diff options
Diffstat (limited to 'target/linux/ar71xx/files/arch')
-rw-r--r-- | target/linux/ar71xx/files/arch/mips/ath79/mach-wap4410n.c | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/target/linux/ar71xx/files/arch/mips/ath79/mach-wap4410n.c b/target/linux/ar71xx/files/arch/mips/ath79/mach-wap4410n.c new file mode 100644 index 0000000..f2cf071 --- /dev/null +++ b/target/linux/ar71xx/files/arch/mips/ath79/mach-wap4410n.c @@ -0,0 +1,285 @@ +/* + * Cisco WAP4410N board support + * + * Copyright (C) 2014 Caleb James DeLisle <cjd@cjdns.fr> + * Copyright (C) 2015 Ryan A Young <rayoung@utexas.edu> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, or (at your option) any later version. + */ +#include <linux/platform_device.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> +#include <linux/nxp_74hc153.h> + +#include <asm/mach-ath79/ath79.h> + +#include "dev-eth.h" +#include "dev-gpio-buttons.h" +#include "dev-leds-gpio.h" +#include "dev-usb.h" +#include "dev-wmac.h" +#include "machtypes.h" + +/* -------------- begin flash device -------------- */ + +#define FLASH_BASE 0xbf000000 + +/* where the actual art data is within the art partition. */ +#define ART_DATA_OFFSET 0x1000 + +/* If changed, make sure to change image/Makefile too! */ +#define KERN_SIZE 0x190000 + +/* Flash layout: u-boot/include/upgrade.h in cisco's GPL dump */ +#define FLASH_SIZE (0x800000) +#define BOOT_SIZE (0x40000) +#define NVRAM_SIZE (0x10000) +#define ART_SIZE (0x10000) +/* + * Note that this is different from upgrade.h, in which ENV_SIZE is 0x10000. + * This is because the sercomm header is located in the upper end of the root + * section instead of the env section, and it must not be overwritten by rootfs. + */ +#define ENV_SIZE (0x20000) + +#define NODE_INFO_OFFSET (BOOT_SIZE - 0x90) +struct wap4410n_node_info { + /** Serial number written on back of device. */ + char serial_no[16]; + + /** Internal to Sercomm (?), France = { domain: 0x80, country: 0x00 } */ + uint8_t domain; + uint8_t country; + + /** written on the board, eg: 13 */ + uint8_t hw_ver; + + uint8_t zero0[5]; + + /** ASCII numeric digits */ + char wps_pin[8]; + + uint8_t zero1[16]; + + uint8_t mac_addr[6]; + + uint8_t zero2[3]; + + /** 31734572436f4d6d -> "1sErCoMm" does not seem to be checked. */ + char magic_1sErCoMm[8]; + + /** 00010000 (offset 0x41, completely unaligned) */ + uint8_t unknown0[4]; + + /** Used by upslug2 protocol */ + uint8_t hardware_id[32]; + + /** 0000240800008000000000030000200400000008 */ + uint8_t unknown1[20]; + + /** 734572436f4d6d -> "sErCoMm" */ + uint8_t magic_sErCoMm[7]; + + uint8_t zero3[16]; +}; + +#define UPGRADE_INFO_OFFSET 0x7dfff0 +struct wap4410n_upgrade_info { + /** Stock WAP4410: 2408 */ + uint16_t product_id; + + /** Always 8000 */ + uint16_t protocol; + + /** 2007 -> v2.0.7.x */ + uint16_t fw_version; + + /** 90f7 same value as NSLU2 */ + uint16_t unknown0; + + /** bootloader checks this and will "soft brick" if it's not correct. */ + uint8_t eRcOmM[6]; + + uint8_t pad[2]; +}; + +/* + * An instruction in the bootloader which checks that 0x7dfff8 == "eRcOmM" and + * bricks if it's not. + * If this instruction is overwritten with a zero, you get 64k of extra space. + * 2406 0006 1440 000a 8fbc 0020 <-- unpatched + * 2406 0006 0000 0000 8fbc 0020 <-- patched + */ +#define SERCOMM_CHECK_LAST_INSN 0x24060006 +#define SERCOMM_CHECK_INSN_OFFSET 0x19e08 +#define SERCOMM_CHECK_NEXT_INSN 0x8fbc0020 + + +/* Little bit of arithmatic on the flash layout. */ +#define NVRAM_OFFSET BOOT_SIZE +#define KERN_OFFSET (NVRAM_OFFSET + NVRAM_SIZE) +#define ROOT_OFFSET (KERN_OFFSET + KERN_SIZE) +#define ART_OFFSET (FLASH_SIZE - ART_SIZE) +#define ENV_OFFSET (ART_OFFSET - ENV_SIZE) +/* rootfs is whatever is left. */ +#define ROOT_SIZE (ENV_OFFSET - ROOT_OFFSET) + +#define PART(b, s, n, f) { .name = n, .offset = b, .size = s, .mask_flags = f } +static struct mtd_partition wap4410n_flash_partitions[] = { + PART(0x00000000, BOOT_SIZE, "u-boot", MTD_WRITEABLE), + PART(NVRAM_OFFSET, NVRAM_SIZE, "u-boot-env", MTD_WRITEABLE), + PART(KERN_OFFSET, KERN_SIZE, "kernel", 0), + PART(ROOT_OFFSET, ROOT_SIZE, "rootfs", 0), + PART(ENV_OFFSET, ENV_SIZE, "sercomm", MTD_WRITEABLE), + PART(ART_OFFSET, ART_SIZE, "art", MTD_WRITEABLE), + + /* Pseudo-partition over whole upgradable space, used by sysupgrade. */ + PART(KERN_OFFSET, KERN_SIZE + ROOT_SIZE, "firmware", 0) +}; +#undef PART + +static struct physmap_flash_data wap4410n_flash_data = { + .width = 2, + .parts = wap4410n_flash_partitions, + .nr_parts = ARRAY_SIZE(wap4410n_flash_partitions), +}; + +static struct resource wap4410n_flash_resources[] = { + [0] = { + .start = FLASH_BASE, + .end = FLASH_BASE + FLASH_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device wap4410n_flash_device = { + .name = "physmap-flash", + .id = -1, + .resource = wap4410n_flash_resources, + .num_resources = ARRAY_SIZE(wap4410n_flash_resources), + .dev = { .platform_data = &wap4410n_flash_data } +}; + + +static void __init wap4410n_flash_reg(void) +{ + uint32_t *insn = (uint32_t *) (FLASH_BASE + SERCOMM_CHECK_INSN_OFFSET); + int i; + if (SERCOMM_CHECK_LAST_INSN != insn[-1] || + SERCOMM_CHECK_NEXT_INSN != insn[1]) { + printk(KERN_INFO "Unrecognized bootloader, costs 64k storage"); + } else if (insn[0]) { + printk(KERN_INFO "eRcOmM check at %p in uboot, costs 64k storage", + (void *)insn); + } else { + printk(KERN_INFO "eRcOmM check at %p patched, gain 64k storage", + (void *)insn); + wap4410n_flash_partitions[3].size += + wap4410n_flash_partitions[4].size; + wap4410n_flash_data.nr_parts--; + for (i = 4; i < wap4410n_flash_data.nr_parts; i++) { + memcpy(&wap4410n_flash_partitions[i], + &wap4410n_flash_partitions[i + 1], + sizeof(struct mtd_partition)); + } + } + platform_device_register(&wap4410n_flash_device); +} + +/* -------------- end flash device -------------- */ + + +/* -------------------- GPIO -------------------- */ + +#define LED_WIRELESS 0 +#define LED_POWER 1 +#define KEYS_POLL_INTERVAL 20 /* msecs */ +#define KEYS_DEBOUNE_INTERVAL (3 * KEYS_POLL_INTERVAL) + +/* 2 lights are gpio, other 2 are hardwired. */ +static struct gpio_led wap4410n_leds_gpio[] __initdata = { + { + .name = "wrt4410n:green:power", + .gpio = LED_POWER, + .active_low = 1, + }, + { + .name = "wrt4410n:green:wireless", + .gpio = LED_WIRELESS, + .active_low = 1, + }, +}; + +static struct gpio_keys_button wap4410n_gpio_keys[] __initdata = { + { + .desc = "reset", + .type = EV_KEY, + .code = KEY_RESTART, + .debounce_interval = KEYS_DEBOUNE_INTERVAL, + .gpio = 21, + .active_low = 1, + } +}; + +static void __init wap4410n_gpio_reg(void) +{ + ath79_register_gpio_keys_polled( + -1, + KEYS_POLL_INTERVAL, + ARRAY_SIZE(wap4410n_gpio_keys), + wap4410n_gpio_keys + ); + ath79_register_leds_gpio( + -1, + ARRAY_SIZE(wap4410n_leds_gpio), + wap4410n_leds_gpio + ); +} + +/* -------------------- end GPIO -------------------- */ + +/** Never called, just for build time verification. */ +static void wap4410n_build_verify(void) +{ + BUILD_BUG_ON((KERN_SIZE / 0x10000 * 0x10000) != KERN_SIZE); + BUILD_BUG_ON(sizeof(struct wap4410n_upgrade_info) != 16); + BUILD_BUG_ON(sizeof(struct wap4410n_node_info) != 0x90); +} + +static void __init wap4410n_setup(void) +{ + struct wap4410n_node_info *ni = (struct wap4410n_node_info *) + (FLASH_BASE + NODE_INFO_OFFSET); + uint8_t *art = (uint8_t *) + (FLASH_BASE + FLASH_SIZE - ART_SIZE + ART_DATA_OFFSET); + + ath79_init_mac(ath79_eth0_data.mac_addr, ni->mac_addr, 0); + + ath79_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_RGMII; + /* TODO: SPEED_1000 causes a silent failure, testing needed. */ + ath79_eth0_data.speed = SPEED_100; + ath79_eth0_data.duplex = DUPLEX_FULL; + ath79_register_eth(0); + + ath79_register_usb(); + + wap4410n_flash_reg(); + + ath79_register_wmac(art, ni->mac_addr); + + wap4410n_gpio_reg(); + + /* silence compiler warning */ + if (0) + wap4410n_build_verify(); +} + +MIPS_MACHINE( + ATH79_MACH_WAP4410N, + "WAP4410N", + "Linksys WAP4410N", + wap4410n_setup +); |