ar71xx: Add support for Cisco-Linksys WAP4410N
[openwrt/openwrt.git] / target / linux / ar71xx / files / arch / mips / ath79 / mach-wap4410n.c
1 /*
2 * Cisco WAP4410N board support
3 *
4 * Copyright (C) 2014 Caleb James DeLisle <cjd@cjdns.fr>
5 * Copyright (C) 2015 Ryan A Young <rayoung@utexas.edu>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published
9 * by the Free Software Foundation, or (at your option) any later version.
10 */
11 #include <linux/platform_device.h>
12 #include <linux/mtd/mtd.h>
13 #include <linux/mtd/partitions.h>
14 #include <linux/mtd/physmap.h>
15 #include <linux/nxp_74hc153.h>
16
17 #include <asm/mach-ath79/ath79.h>
18
19 #include "dev-eth.h"
20 #include "dev-gpio-buttons.h"
21 #include "dev-leds-gpio.h"
22 #include "dev-usb.h"
23 #include "dev-wmac.h"
24 #include "machtypes.h"
25
26 /* -------------- begin flash device -------------- */
27
28 #define FLASH_BASE 0xbf000000
29
30 /* where the actual art data is within the art partition. */
31 #define ART_DATA_OFFSET 0x1000
32
33 /* If changed, make sure to change image/Makefile too! */
34 #define KERN_SIZE 0x190000
35
36 /* Flash layout: u-boot/include/upgrade.h in cisco's GPL dump */
37 #define FLASH_SIZE (0x800000)
38 #define BOOT_SIZE (0x40000)
39 #define NVRAM_SIZE (0x10000)
40 #define ART_SIZE (0x10000)
41 /*
42 * Note that this is different from upgrade.h, in which ENV_SIZE is 0x10000.
43 * This is because the sercomm header is located in the upper end of the root
44 * section instead of the env section, and it must not be overwritten by rootfs.
45 */
46 #define ENV_SIZE (0x20000)
47
48 #define NODE_INFO_OFFSET (BOOT_SIZE - 0x90)
49 struct wap4410n_node_info {
50 /** Serial number written on back of device. */
51 char serial_no[16];
52
53 /** Internal to Sercomm (?), France = { domain: 0x80, country: 0x00 } */
54 uint8_t domain;
55 uint8_t country;
56
57 /** written on the board, eg: 13 */
58 uint8_t hw_ver;
59
60 uint8_t zero0[5];
61
62 /** ASCII numeric digits */
63 char wps_pin[8];
64
65 uint8_t zero1[16];
66
67 uint8_t mac_addr[6];
68
69 uint8_t zero2[3];
70
71 /** 31734572436f4d6d -> "1sErCoMm" does not seem to be checked. */
72 char magic_1sErCoMm[8];
73
74 /** 00010000 (offset 0x41, completely unaligned) */
75 uint8_t unknown0[4];
76
77 /** Used by upslug2 protocol */
78 uint8_t hardware_id[32];
79
80 /** 0000240800008000000000030000200400000008 */
81 uint8_t unknown1[20];
82
83 /** 734572436f4d6d -> "sErCoMm" */
84 uint8_t magic_sErCoMm[7];
85
86 uint8_t zero3[16];
87 };
88
89 #define UPGRADE_INFO_OFFSET 0x7dfff0
90 struct wap4410n_upgrade_info {
91 /** Stock WAP4410: 2408 */
92 uint16_t product_id;
93
94 /** Always 8000 */
95 uint16_t protocol;
96
97 /** 2007 -> v2.0.7.x */
98 uint16_t fw_version;
99
100 /** 90f7 same value as NSLU2 */
101 uint16_t unknown0;
102
103 /** bootloader checks this and will "soft brick" if it's not correct. */
104 uint8_t eRcOmM[6];
105
106 uint8_t pad[2];
107 };
108
109 /*
110 * An instruction in the bootloader which checks that 0x7dfff8 == "eRcOmM" and
111 * bricks if it's not.
112 * If this instruction is overwritten with a zero, you get 64k of extra space.
113 * 2406 0006 1440 000a 8fbc 0020 <-- unpatched
114 * 2406 0006 0000 0000 8fbc 0020 <-- patched
115 */
116 #define SERCOMM_CHECK_LAST_INSN 0x24060006
117 #define SERCOMM_CHECK_INSN_OFFSET 0x19e08
118 #define SERCOMM_CHECK_NEXT_INSN 0x8fbc0020
119
120
121 /* Little bit of arithmatic on the flash layout. */
122 #define NVRAM_OFFSET BOOT_SIZE
123 #define KERN_OFFSET (NVRAM_OFFSET + NVRAM_SIZE)
124 #define ROOT_OFFSET (KERN_OFFSET + KERN_SIZE)
125 #define ART_OFFSET (FLASH_SIZE - ART_SIZE)
126 #define ENV_OFFSET (ART_OFFSET - ENV_SIZE)
127 /* rootfs is whatever is left. */
128 #define ROOT_SIZE (ENV_OFFSET - ROOT_OFFSET)
129
130 #define PART(b, s, n, f) { .name = n, .offset = b, .size = s, .mask_flags = f }
131 static struct mtd_partition wap4410n_flash_partitions[] = {
132 PART(0x00000000, BOOT_SIZE, "u-boot", MTD_WRITEABLE),
133 PART(NVRAM_OFFSET, NVRAM_SIZE, "u-boot-env", MTD_WRITEABLE),
134 PART(KERN_OFFSET, KERN_SIZE, "kernel", 0),
135 PART(ROOT_OFFSET, ROOT_SIZE, "rootfs", 0),
136 PART(ENV_OFFSET, ENV_SIZE, "sercomm", MTD_WRITEABLE),
137 PART(ART_OFFSET, ART_SIZE, "art", MTD_WRITEABLE),
138
139 /* Pseudo-partition over whole upgradable space, used by sysupgrade. */
140 PART(KERN_OFFSET, KERN_SIZE + ROOT_SIZE, "firmware", 0)
141 };
142 #undef PART
143
144 static struct physmap_flash_data wap4410n_flash_data = {
145 .width = 2,
146 .parts = wap4410n_flash_partitions,
147 .nr_parts = ARRAY_SIZE(wap4410n_flash_partitions),
148 };
149
150 static struct resource wap4410n_flash_resources[] = {
151 [0] = {
152 .start = FLASH_BASE,
153 .end = FLASH_BASE + FLASH_SIZE - 1,
154 .flags = IORESOURCE_MEM,
155 },
156 };
157
158 static struct platform_device wap4410n_flash_device = {
159 .name = "physmap-flash",
160 .id = -1,
161 .resource = wap4410n_flash_resources,
162 .num_resources = ARRAY_SIZE(wap4410n_flash_resources),
163 .dev = { .platform_data = &wap4410n_flash_data }
164 };
165
166
167 static void __init wap4410n_flash_reg(void)
168 {
169 uint32_t *insn = (uint32_t *) (FLASH_BASE + SERCOMM_CHECK_INSN_OFFSET);
170 int i;
171 if (SERCOMM_CHECK_LAST_INSN != insn[-1] ||
172 SERCOMM_CHECK_NEXT_INSN != insn[1]) {
173 printk(KERN_INFO "Unrecognized bootloader, costs 64k storage");
174 } else if (insn[0]) {
175 printk(KERN_INFO "eRcOmM check at %p in uboot, costs 64k storage",
176 (void *)insn);
177 } else {
178 printk(KERN_INFO "eRcOmM check at %p patched, gain 64k storage",
179 (void *)insn);
180 wap4410n_flash_partitions[3].size +=
181 wap4410n_flash_partitions[4].size;
182 wap4410n_flash_data.nr_parts--;
183 for (i = 4; i < wap4410n_flash_data.nr_parts; i++) {
184 memcpy(&wap4410n_flash_partitions[i],
185 &wap4410n_flash_partitions[i + 1],
186 sizeof(struct mtd_partition));
187 }
188 }
189 platform_device_register(&wap4410n_flash_device);
190 }
191
192 /* -------------- end flash device -------------- */
193
194
195 /* -------------------- GPIO -------------------- */
196
197 #define LED_WIRELESS 0
198 #define LED_POWER 1
199 #define KEYS_POLL_INTERVAL 20 /* msecs */
200 #define KEYS_DEBOUNE_INTERVAL (3 * KEYS_POLL_INTERVAL)
201
202 /* 2 lights are gpio, other 2 are hardwired. */
203 static struct gpio_led wap4410n_leds_gpio[] __initdata = {
204 {
205 .name = "wrt4410n:green:power",
206 .gpio = LED_POWER,
207 .active_low = 1,
208 },
209 {
210 .name = "wrt4410n:green:wireless",
211 .gpio = LED_WIRELESS,
212 .active_low = 1,
213 },
214 };
215
216 static struct gpio_keys_button wap4410n_gpio_keys[] __initdata = {
217 {
218 .desc = "reset",
219 .type = EV_KEY,
220 .code = KEY_RESTART,
221 .debounce_interval = KEYS_DEBOUNE_INTERVAL,
222 .gpio = 21,
223 .active_low = 1,
224 }
225 };
226
227 static void __init wap4410n_gpio_reg(void)
228 {
229 ath79_register_gpio_keys_polled(
230 -1,
231 KEYS_POLL_INTERVAL,
232 ARRAY_SIZE(wap4410n_gpio_keys),
233 wap4410n_gpio_keys
234 );
235 ath79_register_leds_gpio(
236 -1,
237 ARRAY_SIZE(wap4410n_leds_gpio),
238 wap4410n_leds_gpio
239 );
240 }
241
242 /* -------------------- end GPIO -------------------- */
243
244 /** Never called, just for build time verification. */
245 static void wap4410n_build_verify(void)
246 {
247 BUILD_BUG_ON((KERN_SIZE / 0x10000 * 0x10000) != KERN_SIZE);
248 BUILD_BUG_ON(sizeof(struct wap4410n_upgrade_info) != 16);
249 BUILD_BUG_ON(sizeof(struct wap4410n_node_info) != 0x90);
250 }
251
252 static void __init wap4410n_setup(void)
253 {
254 struct wap4410n_node_info *ni = (struct wap4410n_node_info *)
255 (FLASH_BASE + NODE_INFO_OFFSET);
256 uint8_t *art = (uint8_t *)
257 (FLASH_BASE + FLASH_SIZE - ART_SIZE + ART_DATA_OFFSET);
258
259 ath79_init_mac(ath79_eth0_data.mac_addr, ni->mac_addr, 0);
260
261 ath79_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_RGMII;
262 /* TODO: SPEED_1000 causes a silent failure, testing needed. */
263 ath79_eth0_data.speed = SPEED_100;
264 ath79_eth0_data.duplex = DUPLEX_FULL;
265 ath79_register_eth(0);
266
267 ath79_register_usb();
268
269 wap4410n_flash_reg();
270
271 ath79_register_wmac(art, ni->mac_addr);
272
273 wap4410n_gpio_reg();
274
275 /* silence compiler warning */
276 if (0)
277 wap4410n_build_verify();
278 }
279
280 MIPS_MACHINE(
281 ATH79_MACH_WAP4410N,
282 "WAP4410N",
283 "Linksys WAP4410N",
284 wap4410n_setup
285 );