ath79: add support for Huawei AP5030DN
[openwrt/openwrt.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
75 READREG(base + QCA955X_OTP_REG_MEM_0 + 4);
76
77 while (!(READREG(base + QCA955X_OTP_REG_STATUS0) &
78 QCA955X_OTP_STATUS0_EFUSE_VALID))
79 ;
80
81 read_data = READREG(base + QCA955X_OTP_REG_STATUS1);
82
83 if (!(read_data & 0x1fff))
84 return 0;
85
86 if (read_data & 0x00001000)
87 otp_value = (read_data & 0xfc0) >> 6;
88 else
89 otp_value = read_data & 0x3f;
90
91 if (otp_value > 31) {
92 otp_per_val = 63 - otp_value;
93 rbias_pos_or_neg = 1;
94 } else {
95 otp_per_val = otp_value;
96 rbias_pos_or_neg = 0;
97 }
98
99 rbias_per = otp_per_val * 15;
100
101 if (rbias_pos_or_neg == 1)
102 res_cal_val = (rbias_per + 34) / 21;
103 else if (rbias_per > 34)
104 res_cal_val = -((rbias_per - 34) / 21);
105 else
106 res_cal_val = (34 - rbias_per) / 21;
107
108 sgmii_res_cal_value = (8 + res_cal_val) & 0xf;
109
110 reversed_sgmii_value = (sgmii_res_cal_value & 8) >> 3;
111 reversed_sgmii_value |= (sgmii_res_cal_value & 4) >> 1;
112 reversed_sgmii_value |= (sgmii_res_cal_value & 2) << 1;
113 reversed_sgmii_value |= (sgmii_res_cal_value & 1) << 3;
114 printf("SGMII cal value = 0x%x\n", reversed_sgmii_value);
115 return reversed_sgmii_value;
116 }
117
118 #define QCA955X_SGMII_SERDES_RES_CALIBRATION BIT(23)
119 #define QCA955X_SGMII_SERDES_RES_CALIBRATION_MASK 0xf
120 #define QCA955X_SGMII_SERDES_RES_CALIBRATION_SHIFT 23
121 #define QCA955X_SGMII_SERDES_LOCK_DETECT_STATUS BIT(15)
122 #define QCA955X_PLL_ETH_SGMII_SERDES_LOCK_DETECT BIT(2)
123 #define QCA955X_PLL_ETH_SGMII_SERDES_PLL_REFCLK BIT(1)
124 #define QCA955X_PLL_ETH_SGMII_SERDES_EN_PLL BIT(0)
125 #define QCA955X_PLL_CLK_CTRL_REG 0x08
126 #define QCA955X_PLL_ETH_XMII_CONTROL_REG 0x28
127 #define QCA955X_PLL_ETH_SGMII_CONTROL_REG 0x48
128 #define QCA955X_PLL_ETH_SGMII_SERDES_REG 0x4c
129
130 static void qca955x_device_reset_clear(unsigned int mask)
131 {
132 unsigned int t, reg;
133
134 reg = KSEG1ADDR(AR71XX_RESET_BASE +
135 QCA955X_RESET_REG_RESET_MODULE);
136
137 t = READREG(reg);
138 WRITEREG(reg, t & ~mask);
139 }
140
141 static void mr18_setup_qca955x_eth_serdes_cal(unsigned int sgmii_value)
142 {
143 unsigned int ethbase, pllbase, t;
144
145 ethbase = KSEG1ADDR(QCA955X_GMAC_BASE);
146 pllbase = KSEG1ADDR(AR71XX_PLL_BASE);
147
148 /* To Check the locking of the SGMII PLL */
149 t = READREG(ethbase + QCA955X_GMAC_REG_SGMII_SERDES);
150 t &= ~(QCA955X_SGMII_SERDES_RES_CALIBRATION_MASK <<
151 QCA955X_SGMII_SERDES_RES_CALIBRATION_SHIFT);
152 t |= (sgmii_value & QCA955X_SGMII_SERDES_RES_CALIBRATION_MASK) <<
153 QCA955X_SGMII_SERDES_RES_CALIBRATION_SHIFT;
154 WRITEREG(ethbase + QCA955X_GMAC_REG_SGMII_SERDES, t);
155
156 WRITEREG(pllbase + QCA955X_PLL_ETH_SGMII_SERDES_REG,
157 QCA955X_PLL_ETH_SGMII_SERDES_LOCK_DETECT |
158 QCA955X_PLL_ETH_SGMII_SERDES_PLL_REFCLK |
159 QCA955X_PLL_ETH_SGMII_SERDES_EN_PLL)
160 ;
161
162 qca955x_device_reset_clear(QCA955X_RESET_SGMII_ANALOG);
163 qca955x_device_reset_clear(QCA955X_RESET_SGMII);
164
165 while (!(READREG(ethbase + QCA955X_GMAC_REG_SGMII_SERDES) &
166 QCA955X_SGMII_SERDES_LOCK_DETECT_STATUS))
167 ;
168 }
169
170 static inline void mr18_init(void)
171 {
172 int res;
173
174 printf("Meraki MR18\n");
175
176 res = mr18_extract_sgmii_res_cal();
177 if (res >= 0)
178 mr18_setup_qca955x_eth_serdes_cal(res);
179
180 }
181 #else
182 static inline void mr18_init(void) { }
183 #endif
184
185 #ifdef CONFIG_BOARD_HUAWEI_AP5030DN
186 static inline void ap5030dn_init(void)
187 {
188 const unsigned int ap5030dn_watchdog_gpio = 15;
189 unsigned int gpiobase, reg;
190
191 gpiobase = KSEG1ADDR(AR71XX_GPIO_BASE);
192
193 printf("Huawei AP5030DN\n");
194
195 reg = READREG(gpiobase + AR71XX_GPIO_REG_OE);
196 WRITEREG(gpiobase + AR71XX_GPIO_REG_OE,
197 reg & ~(1 << ap5030dn_watchdog_gpio));
198
199 /* Set GPIO15 MUX to output CLK_OBS5 (= CPU_CLK/4)
200 * to keep the watchdog happy until wdt-gpio takes over
201 */
202 reg = READREG(gpiobase + AR934X_GPIO_REG_OUT_FUNC3);
203 WRITEREG(gpiobase + AR934X_GPIO_REG_OUT_FUNC3,
204 reg | (QCA955X_GPIO_OUTSEL_CLK_OBS5 << 24));
205 }
206 #else
207 static inline void ap5030dn_init(void) { }
208 #endif
209
210 void board_init(void)
211 {
212 tlwr1043nd_init();
213 mr18_init();
214 ap5030dn_init();
215 }