OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
4 | office | 1 | /* |
2 | * Cisco Meraki MR18 board support |
||
3 | * |
||
4 | * Copyright (C) 2015 Chris Blake <chrisrblake93@gmail.com> |
||
5 | * Copyright (C) 2015 Christian Lamparter <chunkeey@googlemail.com> |
||
6 | * Copyright (C) 2015 Thomas Hebb <tommyhebb@gmail.com> |
||
7 | * |
||
8 | * Based on Cisco Meraki GPL Release r23-20150601 MR18 Device Config |
||
9 | * |
||
10 | * This program is free software; you can redistribute it and/or modify it |
||
11 | * under the terms of the GNU General Public License version 2 as published |
||
12 | * by the Free Software Foundation. |
||
13 | */ |
||
14 | #include <linux/platform_device.h> |
||
15 | #include <linux/ath9k_platform.h> |
||
16 | #include <linux/platform/ar934x_nfc.h> |
||
17 | #include <linux/platform_data/phy-at803x.h> |
||
18 | |||
19 | #include <asm/mach-ath79/ath79.h> |
||
20 | #include <asm/mach-ath79/ar71xx_regs.h> |
||
21 | |||
22 | #include <linux/leds-nu801.h> |
||
23 | #include <linux/pci.h> |
||
24 | |||
25 | #include "common.h" |
||
26 | #include "dev-eth.h" |
||
27 | #include "pci.h" |
||
28 | #include "dev-gpio-buttons.h" |
||
29 | #include "dev-leds-gpio.h" |
||
30 | #include "dev-nfc.h" |
||
31 | #include "dev-wmac.h" |
||
32 | #include "machtypes.h" |
||
33 | |||
34 | #define MR18_GPIO_LED_POWER_WHITE 18 |
||
35 | #define MR18_GPIO_LED_POWER_ORANGE 21 |
||
36 | |||
37 | #define MR18_GPIO_BTN_RESET 17 |
||
38 | #define MR18_KEYS_POLL_INTERVAL 20 /* msecs */ |
||
39 | #define MR18_KEYS_DEBOUNCE_INTERVAL (3 * MR18_KEYS_POLL_INTERVAL) |
||
40 | |||
41 | #define MR18_WAN_PHYADDR 3 |
||
42 | |||
43 | /* used for eth calibration */ |
||
44 | #define MR18_OTP_BASE (AR71XX_APB_BASE + 0x130000) |
||
45 | #define MR18_OTP_SIZE (0x2000) /* just a guess */ |
||
46 | #define MR18_OTP_MEM_0_REG (0x0000) |
||
47 | #define MR18_OTP_INTF2_REG (0x1008) |
||
48 | #define MR18_OTP_STATUS0_REG (0x1018) |
||
49 | #define MR18_OTP_STATUS0_EFUSE_VALID BIT(2) |
||
50 | |||
51 | #define MR18_OTP_STATUS1_REG (0x101c) |
||
52 | #define MR18_OTP_LDO_CTRL_REG (0x1024) |
||
53 | #define MR18_OTP_LDO_STATUS_REG (0x102c) |
||
54 | #define MR18_OTP_LDO_STATUS_POWER_ON BIT(0) |
||
55 | |||
56 | static struct gpio_led MR18_leds_gpio[] __initdata = { |
||
57 | { |
||
58 | .name = "mr18:white:power", |
||
59 | .gpio = MR18_GPIO_LED_POWER_WHITE, |
||
60 | .active_low = 1, |
||
61 | }, { |
||
62 | .name = "mr18:orange:power", |
||
63 | .gpio = MR18_GPIO_LED_POWER_ORANGE, |
||
64 | .active_low = 0, |
||
65 | }, |
||
66 | }; |
||
67 | |||
68 | static struct gpio_keys_button MR18_gpio_keys[] __initdata = { |
||
69 | { |
||
70 | .desc = "reset", |
||
71 | .type = EV_KEY, |
||
72 | .code = KEY_RESTART, |
||
73 | .debounce_interval = MR18_KEYS_DEBOUNCE_INTERVAL, |
||
74 | .gpio = MR18_GPIO_BTN_RESET, |
||
75 | .active_low = 1, |
||
76 | }, |
||
77 | }; |
||
78 | |||
79 | static struct led_nu801_template tricolor_led_template = { |
||
80 | .device_name = "mr18", |
||
81 | .name = "tricolor", |
||
82 | .num_leds = 1, |
||
83 | .cki = 11, |
||
84 | .sdi = 12, |
||
85 | .lei = -1, |
||
86 | .ndelay = 500, |
||
87 | .init_brightness = { |
||
88 | LED_OFF, |
||
89 | LED_OFF, |
||
90 | LED_OFF, |
||
91 | }, |
||
92 | .default_trigger = "none", |
||
93 | .led_colors = { "red", "green", "blue" }, |
||
94 | }; |
||
95 | |||
96 | static struct led_nu801_platform_data tricolor_led_data = { |
||
97 | .num_controllers = 1, |
||
98 | .template = &tricolor_led_template, |
||
99 | }; |
||
100 | |||
101 | static struct platform_device tricolor_leds = { |
||
102 | .name = "leds-nu801", |
||
103 | .id = -1, |
||
104 | .dev.platform_data = &tricolor_led_data, |
||
105 | }; |
||
106 | |||
107 | static int mr18_extract_sgmii_res_cal(void) |
||
108 | { |
||
109 | void __iomem *base; |
||
110 | unsigned int reversed_sgmii_value; |
||
111 | |||
112 | unsigned int otp_value, otp_per_val, rbias_per, read_data; |
||
113 | unsigned int rbias_pos_or_neg; |
||
114 | unsigned int sgmii_res_cal_value; |
||
115 | int res_cal_val; |
||
116 | |||
117 | base = ioremap_nocache(MR18_OTP_BASE, MR18_OTP_SIZE); |
||
118 | if (!base) |
||
119 | return -EIO; |
||
120 | |||
121 | __raw_writel(0x7d, base + MR18_OTP_INTF2_REG); |
||
122 | __raw_writel(0x00, base + MR18_OTP_LDO_CTRL_REG); |
||
123 | |||
124 | while (__raw_readl(base + MR18_OTP_LDO_STATUS_REG) & |
||
125 | MR18_OTP_LDO_STATUS_POWER_ON); |
||
126 | |||
127 | __raw_readl(base + MR18_OTP_MEM_0_REG + 4); |
||
128 | |||
129 | while (!(__raw_readl(base + MR18_OTP_STATUS0_REG) & |
||
130 | MR18_OTP_STATUS0_EFUSE_VALID)); |
||
131 | |||
132 | read_data = __raw_readl(base + MR18_OTP_STATUS1_REG); |
||
133 | |||
134 | iounmap(base); |
||
135 | |||
136 | if (!(read_data & 0x1fff)) |
||
137 | return -ENODEV; |
||
138 | |||
139 | if (read_data & 0x00001000) |
||
140 | otp_value = (read_data & 0xfc0) >> 6; |
||
141 | else |
||
142 | otp_value = read_data & 0x3f; |
||
143 | |||
144 | if (otp_value > 31) { |
||
145 | otp_per_val = 63 - otp_value; |
||
146 | rbias_pos_or_neg = 1; |
||
147 | } else { |
||
148 | otp_per_val = otp_value; |
||
149 | rbias_pos_or_neg = 0; |
||
150 | } |
||
151 | |||
152 | rbias_per = otp_per_val * 15; |
||
153 | |||
154 | if (rbias_pos_or_neg == 1) |
||
155 | res_cal_val = (rbias_per + 34) / 21; |
||
156 | else if (rbias_per > 34) |
||
157 | res_cal_val = -((rbias_per - 34) / 21); |
||
158 | else |
||
159 | res_cal_val = (34 - rbias_per) / 21; |
||
160 | |||
161 | sgmii_res_cal_value = (8 + res_cal_val) & 0xf; |
||
162 | |||
163 | reversed_sgmii_value = (sgmii_res_cal_value & 8) >> 3; |
||
164 | reversed_sgmii_value |= (sgmii_res_cal_value & 4) >> 1; |
||
165 | reversed_sgmii_value |= (sgmii_res_cal_value & 2) << 1; |
||
166 | reversed_sgmii_value |= (sgmii_res_cal_value & 1) << 3; |
||
167 | printk(KERN_INFO "SGMII cal value = 0x%x\n", reversed_sgmii_value); |
||
168 | return reversed_sgmii_value; |
||
169 | } |
||
170 | |||
171 | static void mr18_setup_qca955x_eth_serdes_cal(unsigned int sgmii_value) |
||
172 | { |
||
173 | void __iomem *ethbase, *pllbase; |
||
174 | u32 t; |
||
175 | |||
176 | ethbase = ioremap_nocache(QCA955X_GMAC_BASE, QCA955X_GMAC_SIZE); |
||
177 | pllbase = ioremap_nocache(AR71XX_PLL_BASE, AR71XX_PLL_SIZE); |
||
178 | |||
179 | /* To Check the locking of the SGMII PLL */ |
||
180 | t = __raw_readl(ethbase + QCA955X_GMAC_REG_SGMII_SERDES); |
||
181 | t &= ~(QCA955X_SGMII_SERDES_RES_CALIBRATION_MASK << |
||
182 | QCA955X_SGMII_SERDES_RES_CALIBRATION_SHIFT); |
||
183 | t |= (sgmii_value & QCA955X_SGMII_SERDES_RES_CALIBRATION_MASK) << |
||
184 | QCA955X_SGMII_SERDES_RES_CALIBRATION_SHIFT; |
||
185 | __raw_writel(t, ethbase + QCA955X_GMAC_REG_SGMII_SERDES); |
||
186 | |||
187 | __raw_writel(QCA955X_PLL_ETH_SGMII_SERDES_LOCK_DETECT | |
||
188 | QCA955X_PLL_ETH_SGMII_SERDES_PLL_REFCLK | |
||
189 | QCA955X_PLL_ETH_SGMII_SERDES_EN_PLL, |
||
190 | pllbase + QCA955X_PLL_ETH_SGMII_SERDES_REG); |
||
191 | |||
192 | ath79_device_reset_clear(QCA955X_RESET_SGMII_ANALOG); |
||
193 | ath79_device_reset_clear(QCA955X_RESET_SGMII); |
||
194 | |||
195 | while (!(__raw_readl(ethbase + QCA955X_GMAC_REG_SGMII_SERDES) & |
||
196 | QCA955X_SGMII_SERDES_LOCK_DETECT_STATUS)); |
||
197 | |||
198 | iounmap(ethbase); |
||
199 | iounmap(pllbase); |
||
200 | } |
||
201 | |||
202 | static struct ath9k_platform_data pci_main_wifi_data = { |
||
203 | .led_pin = -1, |
||
204 | }; |
||
205 | static struct ath9k_platform_data pci_scan_wifi_data = { |
||
206 | .led_pin = -1, |
||
207 | }; |
||
208 | |||
209 | static int mr18_dual_pci_plat_dev_init(struct pci_dev *dev) |
||
210 | { |
||
211 | /* The PCIE devices are attached to different busses but they |
||
212 | * both share the same slot number. Checking the PCI_SLOT vals |
||
213 | * does not work. |
||
214 | */ |
||
215 | switch (dev->bus->number) { |
||
216 | case 0: |
||
217 | dev->dev.platform_data = &pci_main_wifi_data; |
||
218 | break; |
||
219 | case 1: |
||
220 | dev->dev.platform_data = &pci_scan_wifi_data; |
||
221 | break; |
||
222 | } |
||
223 | |||
224 | return 0; |
||
225 | } |
||
226 | |||
227 | static void __init mr18_setup(void) |
||
228 | { |
||
229 | int res; |
||
230 | |||
231 | /* NAND */ |
||
232 | ath79_nfc_set_ecc_mode(AR934X_NFC_ECC_SOFT_BCH); |
||
233 | ath79_register_nfc(); |
||
234 | |||
235 | /* even though, the PHY is connected via RGMII, |
||
236 | * the SGMII/SERDES PLLs need to be calibrated and locked. |
||
237 | * Or else, the PHY won't be working for this platfrom. |
||
238 | * |
||
239 | * Figuring this out took such a long time, that we want to |
||
240 | * point this quirk out, before someone wants to remove it. |
||
241 | */ |
||
242 | res = mr18_extract_sgmii_res_cal(); |
||
243 | if (res >= 0) { |
||
244 | /* Setup SoC Eth Config */ |
||
245 | ath79_setup_qca955x_eth_cfg(QCA955X_ETH_CFG_RGMII_EN | |
||
246 | (3 << QCA955X_ETH_CFG_RXD_DELAY_SHIFT) | |
||
247 | (3 << QCA955X_ETH_CFG_RDV_DELAY_SHIFT)); |
||
248 | |||
249 | /* MDIO Interface */ |
||
250 | ath79_register_mdio(0, 0x0); |
||
251 | |||
252 | mr18_setup_qca955x_eth_serdes_cal(res); |
||
253 | |||
254 | /* GMAC0 is connected to an Atheros AR8035-A */ |
||
255 | ath79_init_mac(ath79_eth0_data.mac_addr, NULL, 0); |
||
256 | ath79_eth0_data.mii_bus_dev = &ath79_mdio0_device.dev; |
||
257 | ath79_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_RGMII; |
||
258 | ath79_eth0_data.phy_mask = BIT(MR18_WAN_PHYADDR); |
||
259 | ath79_eth0_pll_data.pll_1000 = 0xa6000000; |
||
260 | ath79_eth0_pll_data.pll_100 = 0xa0000101; |
||
261 | ath79_eth0_pll_data.pll_10 = 0x80001313; |
||
262 | ath79_register_eth(0); |
||
263 | } else { |
||
264 | printk(KERN_ERR "failed to read EFUSE for ethernet cal\n"); |
||
265 | } |
||
266 | |||
267 | /* LEDs and Buttons */ |
||
268 | platform_device_register(&tricolor_leds); |
||
269 | ath79_register_leds_gpio(-1, ARRAY_SIZE(MR18_leds_gpio), |
||
270 | MR18_leds_gpio); |
||
271 | ath79_register_gpio_keys_polled(-1, MR18_KEYS_POLL_INTERVAL, |
||
272 | ARRAY_SIZE(MR18_gpio_keys), |
||
273 | MR18_gpio_keys); |
||
274 | |||
275 | /* Clear RTC reset (Needed by SoC WiFi) */ |
||
276 | ath79_device_reset_clear(QCA955X_RESET_RTC); |
||
277 | |||
278 | /* WiFi */ |
||
279 | ath79_register_wmac_simple(); |
||
280 | |||
281 | pci_main_wifi_data.eeprom_name = "pci_wmac0.eeprom"; |
||
282 | pci_scan_wifi_data.eeprom_name = "pci_wmac1.eeprom"; |
||
283 | ath79_pci_set_plat_dev_init(mr18_dual_pci_plat_dev_init); |
||
284 | ath79_register_pci(); |
||
285 | } |
||
286 | MIPS_MACHINE(ATH79_MACH_MR18, "MR18", "Meraki MR18", mr18_setup); |