aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/ath79/image/lzma-loader/src/board.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/ath79/image/lzma-loader/src/board.c')
-rw-r--r--target/linux/ath79/image/lzma-loader/src/board.c133
1 files changed, 133 insertions, 0 deletions
diff --git a/target/linux/ath79/image/lzma-loader/src/board.c b/target/linux/ath79/image/lzma-loader/src/board.c
index 2f4dd6b1f6..7b1e110ee2 100644
--- a/target/linux/ath79/image/lzma-loader/src/board.c
+++ b/target/linux/ath79/image/lzma-loader/src/board.c
@@ -10,6 +10,7 @@
#include <stddef.h>
#include "config.h"
+#include "printf.h"
#include "ar71xx_regs.h"
#define READREG(r) *(volatile unsigned int *)(r)
@@ -50,7 +51,139 @@ static void tlwr1043nd_init(void)
static inline void tlwr1043nd_init(void) {}
#endif
+#ifdef CONFIG_BOARD_MERAKI_MR18
+
+static int mr18_extract_sgmii_res_cal(void)
+{
+ unsigned int base;
+ unsigned int reversed_sgmii_value;
+
+ unsigned int otp_value, otp_per_val, rbias_per, read_data;
+ unsigned int rbias_pos_or_neg;
+ unsigned int sgmii_res_cal_value;
+ int res_cal_val;
+
+ base = KSEG1ADDR(QCA955X_OTP_BASE);
+
+ WRITEREG(base + QCA955X_OTP_REG_INTF2, 0x7d);
+ WRITEREG(base + QCA955X_OTP_REG_LDO_CTRL, 0x00);
+
+ while (READREG(base + QCA955X_OTP_REG_LDO_STATUS) &
+ QCA955X_OTP_LDO_STATUS_POWER_ON)
+ ;
+
+ READREG(base + QCA955X_OTP_REG_MEM_0 + 4);
+
+ while (!(READREG(base + QCA955X_OTP_REG_STATUS0) &
+ QCA955X_OTP_STATUS0_EFUSE_VALID))
+ ;
+
+ read_data = READREG(base + QCA955X_OTP_REG_STATUS1);
+
+ if (!(read_data & 0x1fff))
+ return 0;
+
+ if (read_data & 0x00001000)
+ otp_value = (read_data & 0xfc0) >> 6;
+ else
+ otp_value = read_data & 0x3f;
+
+ if (otp_value > 31) {
+ otp_per_val = 63 - otp_value;
+ rbias_pos_or_neg = 1;
+ } else {
+ otp_per_val = otp_value;
+ rbias_pos_or_neg = 0;
+ }
+
+ rbias_per = otp_per_val * 15;
+
+ if (rbias_pos_or_neg == 1)
+ res_cal_val = (rbias_per + 34) / 21;
+ else if (rbias_per > 34)
+ res_cal_val = -((rbias_per - 34) / 21);
+ else
+ res_cal_val = (34 - rbias_per) / 21;
+
+ sgmii_res_cal_value = (8 + res_cal_val) & 0xf;
+
+ reversed_sgmii_value = (sgmii_res_cal_value & 8) >> 3;
+ reversed_sgmii_value |= (sgmii_res_cal_value & 4) >> 1;
+ reversed_sgmii_value |= (sgmii_res_cal_value & 2) << 1;
+ reversed_sgmii_value |= (sgmii_res_cal_value & 1) << 3;
+ printf("SGMII cal value = 0x%x\n", reversed_sgmii_value);
+ return reversed_sgmii_value;
+}
+
+#define QCA955X_SGMII_SERDES_RES_CALIBRATION BIT(23)
+#define QCA955X_SGMII_SERDES_RES_CALIBRATION_MASK 0xf
+#define QCA955X_SGMII_SERDES_RES_CALIBRATION_SHIFT 23
+#define QCA955X_SGMII_SERDES_LOCK_DETECT_STATUS BIT(15)
+#define QCA955X_PLL_ETH_SGMII_SERDES_LOCK_DETECT BIT(2)
+#define QCA955X_PLL_ETH_SGMII_SERDES_PLL_REFCLK BIT(1)
+#define QCA955X_PLL_ETH_SGMII_SERDES_EN_PLL BIT(0)
+#define QCA955X_PLL_CLK_CTRL_REG 0x08
+#define QCA955X_PLL_ETH_XMII_CONTROL_REG 0x28
+#define QCA955X_PLL_ETH_SGMII_CONTROL_REG 0x48
+#define QCA955X_PLL_ETH_SGMII_SERDES_REG 0x4c
+
+static void qca955x_device_reset_clear(unsigned int mask)
+{
+ unsigned int t, reg;
+
+ reg = KSEG1ADDR(AR71XX_RESET_BASE +
+ QCA955X_RESET_REG_RESET_MODULE);
+
+ t = READREG(reg);
+ WRITEREG(reg, t & ~mask);
+}
+
+static void mr18_setup_qca955x_eth_serdes_cal(unsigned int sgmii_value)
+{
+ unsigned int ethbase, pllbase, t;
+
+ ethbase = KSEG1ADDR(QCA955X_GMAC_BASE);
+ pllbase = KSEG1ADDR(AR71XX_PLL_BASE);
+
+ /* To Check the locking of the SGMII PLL */
+ t = READREG(ethbase + QCA955X_GMAC_REG_SGMII_SERDES);
+ t &= ~(QCA955X_SGMII_SERDES_RES_CALIBRATION_MASK <<
+ QCA955X_SGMII_SERDES_RES_CALIBRATION_SHIFT);
+ t |= (sgmii_value & QCA955X_SGMII_SERDES_RES_CALIBRATION_MASK) <<
+ QCA955X_SGMII_SERDES_RES_CALIBRATION_SHIFT;
+ WRITEREG(ethbase + QCA955X_GMAC_REG_SGMII_SERDES, t);
+
+ WRITEREG(pllbase + QCA955X_PLL_ETH_SGMII_SERDES_REG,
+ QCA955X_PLL_ETH_SGMII_SERDES_LOCK_DETECT |
+ QCA955X_PLL_ETH_SGMII_SERDES_PLL_REFCLK |
+ QCA955X_PLL_ETH_SGMII_SERDES_EN_PLL)
+ ;
+
+ qca955x_device_reset_clear(QCA955X_RESET_SGMII_ANALOG);
+ qca955x_device_reset_clear(QCA955X_RESET_SGMII);
+
+ while (!(READREG(ethbase + QCA955X_GMAC_REG_SGMII_SERDES) &
+ QCA955X_SGMII_SERDES_LOCK_DETECT_STATUS))
+ ;
+}
+
+static inline void mr18_init(void)
+{
+ int res;
+
+ printf("Meraki MR18\n");
+
+ res = mr18_extract_sgmii_res_cal();
+ if (res >= 0)
+ mr18_setup_qca955x_eth_serdes_cal(res);
+
+}
+#else
+static inline void mr18_init(void) { }
+#endif
+
void board_init(void)
{
tlwr1043nd_init();
+ mr18_init();
}