ath79: add Cisco Meraki MR18
[openwrt/staging/chunkeey.git] / target / linux / ath79 / image / lzma-loader / src / board.c
1 /*
2 * LZMA compressed kernel loader for Atheros AR7XXX/AR9XXX based boards
3 *
4 * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 as published
8 * by the Free Software Foundation.
9 */
10
11 #include <stddef.h>
12 #include "config.h"
13 #include "printf.h"
14 #include "ar71xx_regs.h"
15
16 #define READREG(r) *(volatile unsigned int *)(r)
17 #define WRITEREG(r,v) *(volatile unsigned int *)(r) = v
18
19 #define KSEG1ADDR(_x) (((_x) & 0x1fffffff) | 0xa0000000)
20
21 #define UART_BASE 0xb8020000
22
23 #define UART_TX 0
24 #define UART_LSR 5
25
26 #define UART_LSR_THRE 0x20
27
28 #define UART_READ(r) READREG(UART_BASE + 4 * (r))
29 #define UART_WRITE(r,v) WRITEREG(UART_BASE + 4 * (r), (v))
30
31 void board_putc(int ch)
32 {
33 while (((UART_READ(UART_LSR)) & UART_LSR_THRE) == 0);
34 UART_WRITE(UART_TX, ch);
35 while (((UART_READ(UART_LSR)) & UART_LSR_THRE) == 0);
36 }
37
38 #ifdef CONFIG_BOARD_TL_WR1043ND_V1
39 static void tlwr1043nd_init(void)
40 {
41 unsigned int reg = KSEG1ADDR(AR71XX_RESET_BASE);
42 unsigned int t;
43
44 t = READREG(reg + AR913X_RESET_REG_RESET_MODULE);
45 t |= AR71XX_RESET_GE0_PHY;
46 WRITEREG(reg + AR913X_RESET_REG_RESET_MODULE, t);
47 /* flush write */
48 t = READREG(reg + AR913X_RESET_REG_RESET_MODULE);
49 }
50 #else
51 static inline void tlwr1043nd_init(void) {}
52 #endif
53
54 #ifdef CONFIG_BOARD_MERAKI_MR18
55
56 static int mr18_extract_sgmii_res_cal(void)
57 {
58 unsigned int base;
59 unsigned int reversed_sgmii_value;
60
61 unsigned int otp_value, otp_per_val, rbias_per, read_data;
62 unsigned int rbias_pos_or_neg;
63 unsigned int sgmii_res_cal_value;
64 int res_cal_val;
65
66 base = KSEG1ADDR(QCA955X_OTP_BASE);
67
68 WRITEREG(base + QCA955X_OTP_REG_INTF2, 0x7d);
69 WRITEREG(base + QCA955X_OTP_REG_LDO_CTRL, 0x00);
70
71 while (READREG(base + QCA955X_OTP_REG_LDO_STATUS) &
72 QCA955X_OTP_LDO_STATUS_POWER_ON);
73
74 READREG(base + QCA955X_OTP_REG_MEM_0 + 4);
75
76 while (!(READREG(base + QCA955X_OTP_REG_STATUS0) &
77 QCA955X_OTP_STATUS0_EFUSE_VALID));
78
79 read_data = READREG(base + QCA955X_OTP_REG_STATUS1);
80
81 if (!(read_data & 0x1fff))
82 return 0;
83
84 if (read_data & 0x00001000)
85 otp_value = (read_data & 0xfc0) >> 6;
86 else
87 otp_value = read_data & 0x3f;
88
89 if (otp_value > 31) {
90 otp_per_val = 63 - otp_value;
91 rbias_pos_or_neg = 1;
92 } else {
93 otp_per_val = otp_value;
94 rbias_pos_or_neg = 0;
95 }
96
97 rbias_per = otp_per_val * 15;
98
99 if (rbias_pos_or_neg == 1)
100 res_cal_val = (rbias_per + 34) / 21;
101 else if (rbias_per > 34)
102 res_cal_val = -((rbias_per - 34) / 21);
103 else
104 res_cal_val = (34 - rbias_per) / 21;
105
106 sgmii_res_cal_value = (8 + res_cal_val) & 0xf;
107
108 reversed_sgmii_value = (sgmii_res_cal_value & 8) >> 3;
109 reversed_sgmii_value |= (sgmii_res_cal_value & 4) >> 1;
110 reversed_sgmii_value |= (sgmii_res_cal_value & 2) << 1;
111 reversed_sgmii_value |= (sgmii_res_cal_value & 1) << 3;
112 printf("SGMII cal value = 0x%x\n", reversed_sgmii_value);
113 return reversed_sgmii_value;
114 }
115
116 #define QCA955X_SGMII_SERDES_RES_CALIBRATION BIT(23)
117 #define QCA955X_SGMII_SERDES_RES_CALIBRATION_MASK 0xf
118 #define QCA955X_SGMII_SERDES_RES_CALIBRATION_SHIFT 23
119 #define QCA955X_SGMII_SERDES_LOCK_DETECT_STATUS BIT(15)
120 #define QCA955X_PLL_ETH_SGMII_SERDES_LOCK_DETECT BIT(2)
121 #define QCA955X_PLL_ETH_SGMII_SERDES_PLL_REFCLK BIT(1)
122 #define QCA955X_PLL_ETH_SGMII_SERDES_EN_PLL BIT(0)
123 #define QCA955X_PLL_CLK_CTRL_REG 0x08
124 #define QCA955X_PLL_ETH_XMII_CONTROL_REG 0x28
125 #define QCA955X_PLL_ETH_SGMII_CONTROL_REG 0x48
126 #define QCA955X_PLL_ETH_SGMII_SERDES_REG 0x4c
127
128 static void qca955x_device_reset_clear(unsigned int mask)
129 {
130 unsigned int t, reg;
131
132 reg = KSEG1ADDR(AR71XX_RESET_BASE +
133 QCA955X_RESET_REG_RESET_MODULE);
134
135 t = READREG(reg);
136 WRITEREG(reg, t & ~mask);
137 }
138
139 static void mr18_setup_qca955x_eth_serdes_cal(unsigned int sgmii_value)
140 {
141 unsigned int ethbase, pllbase, t;
142
143 ethbase = KSEG1ADDR(QCA955X_GMAC_BASE);
144 pllbase = KSEG1ADDR(AR71XX_PLL_BASE);
145
146 /* To Check the locking of the SGMII PLL */
147 t = READREG(ethbase + QCA955X_GMAC_REG_SGMII_SERDES);
148 t &= ~(QCA955X_SGMII_SERDES_RES_CALIBRATION_MASK <<
149 QCA955X_SGMII_SERDES_RES_CALIBRATION_SHIFT);
150 t |= (sgmii_value & QCA955X_SGMII_SERDES_RES_CALIBRATION_MASK) <<
151 QCA955X_SGMII_SERDES_RES_CALIBRATION_SHIFT;
152 WRITEREG(ethbase + QCA955X_GMAC_REG_SGMII_SERDES, t);
153
154 WRITEREG(pllbase + QCA955X_PLL_ETH_SGMII_SERDES_REG,
155 QCA955X_PLL_ETH_SGMII_SERDES_LOCK_DETECT |
156 QCA955X_PLL_ETH_SGMII_SERDES_PLL_REFCLK |
157 QCA955X_PLL_ETH_SGMII_SERDES_EN_PLL);
158
159 qca955x_device_reset_clear(QCA955X_RESET_SGMII_ANALOG);
160 qca955x_device_reset_clear(QCA955X_RESET_SGMII);
161
162 while (!(READREG(ethbase + QCA955X_GMAC_REG_SGMII_SERDES) &
163 QCA955X_SGMII_SERDES_LOCK_DETECT_STATUS));
164 }
165
166 static inline void mr18_init(void)
167 {
168 int res;
169
170 printf("Meraki MR18\n");
171
172 res = mr18_extract_sgmii_res_cal();
173 if (res >= 0)
174 mr18_setup_qca955x_eth_serdes_cal(res);
175
176 }
177 #else
178 static inline void mr18_init(void) { }
179 #endif
180
181 void board_init(void)
182 {
183 tlwr1043nd_init();
184 mr18_init();
185 }