broadcom-diag: support for the WNDR3700v3
[openwrt/svn-archive/archive.git] / package / broadcom-diag / src / diag.c
index 090fc5e3d59c499a8514b2f96a91ac6aeb664773..ccbaee2d264c501f8b7844fb6743a126156ecdf4 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/netlink.h>
 #include <linux/kobject.h>
 #include <net/sock.h>
+#include <bcm47xx_board.h>
 extern u64 uevent_next_seqnum(void);
 
 #include "gpio.h"
@@ -63,7 +64,9 @@ enum {
        WRT600NV11,
        WRT610N,
        WRT610NV2,
+       E1000V1,
        E3000V1,
+       E3200V1,
 
        /* ASUS */
        WLHDD,
@@ -119,6 +122,8 @@ enum {
        WGT634U,
        WNR834BV1,
        WNR834BV2,
+       WNDR3400V1,
+       WNDR3700V3,
 
        /* Trendware */
        TEW411BRPP,
@@ -156,9 +161,9 @@ static void __init bcm4780_init(void) {
 
                /* Enables GPIO 3 that controls HDD and led power on ASUS WL-700gE */
                printk(MODULE_NAME ": Spinning up HDD and enabling leds\n");
-               gpio_outen(pin, pin);
-               gpio_control(pin, 0);
-               gpio_out(pin, pin);
+               bcm47xx_gpio_outen(pin, pin);
+               bcm47xx_gpio_control(pin, 0);
+               bcm47xx_gpio_out(pin, pin);
 
                /* Wait 5s, so the HDD can spin up */
                set_current_state(TASK_INTERRUPTIBLE);
@@ -168,14 +173,14 @@ static void __init bcm4780_init(void) {
 static void __init NetCenter_init(void) {
                /* unset pin 6 (+12V) */
                int pin = 1 << 6;
-               gpio_outen(pin, pin);
-               gpio_control(pin, 0);
-               gpio_out(pin, pin);
+               bcm47xx_gpio_outen(pin, pin);
+               bcm47xx_gpio_control(pin, 0);
+               bcm47xx_gpio_out(pin, pin);
                /* unset pin 1 (turn off red led, blue will light alone if +5V comes up) */
                pin = 1 << 1;
-               gpio_outen(pin, pin);
-               gpio_control(pin, 0);
-               gpio_out(pin, pin);
+               bcm47xx_gpio_outen(pin, pin);
+               bcm47xx_gpio_control(pin, 0);
+               bcm47xx_gpio_out(pin, pin);
                /* unset pin 3 (+5V) and wait 5 seconds (harddisk spin up) */
                bcm4780_init();
 }
@@ -184,9 +189,9 @@ static void __init bcm57xx_init(void) {
        int pin = 1 << 2;
 
        /* FIXME: switch comes up, but port mappings/vlans not right */
-       gpio_outen(pin, pin);
-       gpio_control(pin, 0);
-       gpio_out(pin, pin);
+       bcm47xx_gpio_outen(pin, pin);
+       bcm47xx_gpio_control(pin, 0);
+       bcm47xx_gpio_out(pin, pin);
 }
 
 static struct platform_t __initdata platforms[] = {
@@ -421,6 +426,25 @@ static struct platform_t __initdata platforms[] = {
                        { .name = "wlan",       .gpio = 1 << 1, .polarity = NORMAL },   // Wireless LED
                },
        },
+       /* same hardware as WRT160NV3 and Cisco Valet M10V1, but different board detection, combine? */
+       [E1000V1] = {
+               .name           = "Linksys E1000 V1",
+               .buttons        = {
+                       { .name = "reset",      .gpio = 1 << 6 },
+                       { .name = "wps",        .gpio = 1 << 5 }, /* nvram get gpio5=wps_button */
+               },
+               .leds           = {
+                       /** turns on leds for all ethernet ports (wan too)
+                        *  this also disconnects some, or maybe all, ethernet ports 
+                        *  1: leds work normally
+                        *  0: all lit all the time */
+                       /* nvram get gpio3=robo_reset */
+                       { .name = "wlan",       .gpio = 1 << 0, .polarity = NORMAL },
+                       { .name = "power",      .gpio = 1 << 1, .polarity = NORMAL },
+                       { .name = "ses_blue",   .gpio = 1 << 4, .polarity = REVERSE }, /* nvram get gpio4=wps_led */
+                       { .name = "ses_orange", .gpio = 1 << 2, .polarity = REVERSE }, /* nvram get gpio2=wps_status_led */
+               },
+       },
        [E3000V1] = {
                .name           = "Linksys E3000 V1",
                .buttons        = {
@@ -435,6 +459,18 @@ static struct platform_t __initdata platforms[] = {
                        { .name = "wlan",       .gpio = 1 << 1, .polarity = NORMAL },   // Wireless LED
                },
        },
+       [E3200V1] = {
+               .name           = "Linksys E3200 V1",
+               .buttons        = {
+                       /* { .name = "switch",  .gpio = 1 << 4 },*/     /* nvram get gpio4=robo_reset */
+                       { .name = "reset",      .gpio = 1 << 5 },       /* nvram get reset_gpio=5 */
+                       { .name = "wps",        .gpio = 1 << 8 },       /* nvram get gpio8=wps_button */
+                       /* { .name = "wombo",   .gpio = 1 << 23 },*/    /* nvram get gpio23=wombo_reset - wireless on motherboard */
+               },
+               .leds   = {
+                       { .name = "power",      .gpio = 1 << 3, .polarity = REVERSE },  /* Power LED */
+               },
+       },
        /* Asus */
        [WLHDD] = {
                .name           = "ASUS WL-HDD",
@@ -876,6 +912,44 @@ static struct platform_t __initdata platforms[] = {
                        { .name = "connected",  .gpio = 1 << 7, .polarity = NORMAL },
                },
        },
+       [WNDR3400V1] = {
+               .name           = "Netgear WNDR3400 V1",
+               .buttons        = {
+                       /* nvram get gpio5=robo_reset */
+                       { .name = "reset",      .gpio = 1 << 4 },
+                       { .name = "wps",        .gpio = 1 << 6 },
+                       { .name = "wlan",       .gpio = 1 << 8 },
+               },
+               .leds           = {
+                       { .name = "wlan",       .gpio = 0 << 0, .polarity = NORMAL },
+                       { .name = "connected",  .gpio = 1 << 0, .polarity = NORMAL },
+                       { .name = "power",      .gpio = 1 << 3, .polarity = NORMAL },
+                       { .name = "diag",       .gpio = 1 << 7, .polarity = NORMAL },
+                       { .name = "usb",        .gpio = 1 << 2, .polarity = REVERSE },
+               },
+       },
+       [WNDR3700V3] = {
+               .name           = "Netgear WNDR3700 V3",
+               .buttons        = {
+                       /* { .name = "usb",     .gpio = 1 << 1 }, */ /* this button doesn't seem to exist. */
+                       { .name = "wlan",       .gpio = 1 << 2 },
+                       { .name = "reset",      .gpio = 1 << 3 },
+                       { .name = "wps",        .gpio = 1 << 4 },
+                       /* { .name = "switch",  .gpio = 1 << 5 },*/     /* nvram get gpio5=robo_reset */
+               },
+               .leds           = {
+                       { .name = "power",      .gpio = (1 << 0) | GPIO_TYPE_SHIFT, .polarity = REVERSE },
+                       { .name = "diag",       .gpio = (1 << 1) | GPIO_TYPE_SHIFT, .polarity = REVERSE },
+                       /* WAN LED doesn't respond to GPIO control. The switch is probably driving it.
+                        * { .name = "wan",     .gpio = (1 << 2) | GPIO_TYPE_SHIFT, .polarity = REVERSE },
+                        */
+                       { .name = "wlan2g",     .gpio = (1 << 3) | GPIO_TYPE_SHIFT, .polarity = REVERSE },
+                       { .name = "wlan5g",     .gpio = (1 << 4) | GPIO_TYPE_SHIFT, .polarity = REVERSE },
+                       { .name = "usb",        .gpio = (1 << 5) | GPIO_TYPE_SHIFT, .polarity = REVERSE },
+                       { .name = "wps",        .gpio = (1 << 6) | GPIO_TYPE_SHIFT, .polarity = REVERSE },
+                       { .name = "wlan",       .gpio = (1 << 7) | GPIO_TYPE_SHIFT, .polarity = REVERSE },
+               },
+       },
        /* Trendware */
        [TEW411BRPP] = {
                .name           = "Trendware TEW411BRP+",
@@ -1018,85 +1092,13 @@ static struct platform_t __initdata platforms[] = {
        },
 };
 
-static struct platform_t __init *platform_detect(void)
+static struct platform_t __init *platform_detect_legacy(void)
 {
        char *boardnum, *boardtype, *buf;
 
        if (strcmp(getvar("nvram_type"), "cfe") == 0)
                return &platforms[WGT634U];
 
-       /* Look for a model identifier */
-
-       /* Based on "model_name" */
-       if ((buf = nvram_get("model_name"))) {
-               if (!strcmp(buf, "DIR-130"))
-                       return &platforms[DIR130];
-               if (!strcmp(buf, "DIR-330"))
-                       return &platforms[DIR330];
-       }
-
-       /* Based on "wsc_modelname */
-       if ((buf = nvram_get("wsc_modelname"))) {
-               if (!strcmp(buf, "WRT610N"))
-                       return &platforms[WRT610N];
-       }
-
-       /* Based on "model_no" */
-       if ((buf = nvram_get("model_no"))) {
-               if (startswith(buf,"WL700")) /* WL700* */
-                       return &platforms[WL700GE];
-       }
-
-       /* Based on "hardware_version" */
-       if ((buf = nvram_get("hardware_version"))) {
-               if (startswith(buf,"WL500GPV2-")) /* WL500GPV2-* */
-                       return &platforms[WL500GPV2];
-               if (startswith(buf,"WL520GC-")) /* WL520GU-* */
-                       return &platforms[WL520GC];
-               if (startswith(buf,"WL520GU-")) /* WL520GU-* */
-                       return &platforms[WL520GU];
-               if (startswith(buf,"WL330GE-")) /* WL330GE-* */
-                       return &platforms[WL330GE];
-               if (startswith(buf,"RT-N16-")) /* RT-N16-* */
-                       return &platforms[RTN16];
-               if (startswith(buf,"F7D4301")) /* F7D4301* */
-                       return &platforms[BELKIN_F7D4301];
-       }
-
-       /* Based on "ModelId" */
-       if ((buf = nvram_get("ModelId"))) {
-               if (!strcmp(buf, "WR850GP"))
-                       return &platforms[WR850GP];
-               if (!strcmp(buf, "WR850G"))
-                       return &platforms[WR850GV2V3];
-               if (!strcmp(buf, "WX-5565") && !strcmp(getvar("boardtype"),"bcm94710ap"))
-                       return &platforms[TM2300]; /* Dell TrueMobile 2300 */
-               if (startswith(buf,"WE800G")) /* WE800G* */
-                       return &platforms[WE800G];
-       }
-
-       /* Buffalo */
-       if ((buf = (nvram_get("melco_id") ?: nvram_get("buffalo_id")))) {
-               /* Buffalo hardware, check id for specific hardware matches */
-               if (!strcmp(buf, "29bb0332"))
-                       return &platforms[WBR2_G54];
-               if (!strcmp(buf, "29129"))
-                       return &platforms[WLA2_G54L];
-               if (!strcmp(buf, "30189"))
-                       return &platforms[WHR_HP_G54];
-               if (!strcmp(buf, "32093"))
-                       return &platforms[WHR_G125];
-               if (!strcmp(buf, "30182"))
-                       return &platforms[WHR_G54S];
-               if (!strcmp(buf, "290441dd"))
-                       return &platforms[WHR2_A54G54];
-               if (!strcmp(buf, "31120"))
-                       return &platforms[WZR_G300N];
-               if (!strcmp(buf, "30083"))
-                       return &platforms[WZR_RS_G54];
-               if (!strcmp(buf, "30103"))
-                       return &platforms[WZR_RS_G54HP];
-       }
 
        /* no easy model number, attempt to guess */
        boardnum = getvar("boardnum");
@@ -1110,52 +1112,18 @@ static struct platform_t __init *platform_detect(void)
                        return &platforms[WRT600N];
        }
 
-       /*
-        * Normally, these would go inside the "CFE based - newer hardware" block below; however, during early init, the
-        * "pmon_ver" variable is not available on the E3000v1 (and probably the WRT610Nv2 also).  Until this is figured out,
-        * these will need to remain here in order for platform detection to work.
-        */
-       if (!strcmp(boardnum, "42")) { /* Linksys */
-               if (!strcmp(boardtype, "0x04cf") && !strcmp(getvar("boot_hw_model"), "E300") && !strcmp(getvar("boot_hw_ver"), "1.0"))
-                       return &platforms[E3000V1];
-
-               if (!strcmp(boardtype, "0x04cf") && !strcmp(getvar("boot_hw_model"), "WRT610N") && !strcmp(getvar("boot_hw_ver"), "2.0"))
-                       return &platforms[WRT610NV2];
-       }
-
        if (startswith(getvar("pmon_ver"), "CFE")) {
                /* CFE based - newer hardware */
                if (!strcmp(boardnum, "42")) { /* Linksys */
-                       if (!strcmp(boardtype, "0x478") && !strcmp(getvar("boot_hw_model"), "WRT300N") && !strcmp(getvar("boot_hw_ver"), "1.1"))
-                               return &platforms[WRT300NV11];
-
                        if (!strcmp(boardtype, "0x478") && !strcmp(getvar("cardbus"), "1"))
                                return &platforms[WRT350N];
 
                        if (!strcmp(boardtype, "0x0101") && !strcmp(getvar("boot_ver"), "v3.6"))
                                return &platforms[WRT54G3G];
 
-                       if (!strcmp(boardtype, "0x042f") && !strcmp(getvar("model_name"), "WRT54G3GV2-VF"))
-                               return &platforms[WRT54G3GV2_VF];
-
                        if (!strcmp(getvar("et1phyaddr"),"5") && !strcmp(getvar("et1mdcport"), "1"))
                                return &platforms[WRTSL54GS];
 
-                       if (!strcmp(boardtype, "0x0472")) {
-                               if(!strcmp(getvar("boot_hw_model"), "WRT150N")) {
-                                       if(!strcmp(getvar("boot_hw_ver"), "1"))
-                                               return &platforms[WRT150NV1];
-                                       else if(!strcmp(getvar("boot_hw_ver"), "1.1"))
-                                               return &platforms[WRT150NV11];
-                               }
-                               else if(!strcmp(getvar("boot_hw_model"), "WRT160N")) {
-                                       if(!strcmp(getvar("boot_hw_ver"), "1.0"))
-                                               return &platforms[WRT160NV1];
-                                       else if(!strcmp(getvar("boot_hw_ver"), "3.0"))
-                                               return &platforms[WRT160NV3];
-                               }
-                       }
-
                        /* default to WRT54G */
                        return &platforms[WRT54G];
                }
@@ -1172,9 +1140,7 @@ static struct platform_t __init *platform_detect(void)
                }
 
                if (!strcmp(boardnum, "45")) { /* ASUS */
-                       if (!strcmp(boardtype,"0x042f"))
-                               return &platforms[WL500GP];
-                       else if (!strcmp(boardtype,"0x0472"))
+                       if (!strcmp(boardtype,"0x0472"))
                                return &platforms[WL500W];
                        else if (!strcmp(boardtype,"0x467"))
                                return &platforms[WL320GE];
@@ -1283,6 +1249,138 @@ static struct platform_t __init *platform_detect(void)
        return NULL;
 }
 
+static struct platform_t __init *platform_detect(void)
+{
+       enum bcm47xx_board board;
+       const char *board_name;
+
+
+       board = bcm47xx_board_get();
+       board_name = bcm47xx_board_get_name();
+       if (board != BCM47XX_BOARD_UNKNOWN && board != BCM47XX_BOARD_NON)
+               printk(MODULE_NAME ": kernel found a \"%s\"\n", board_name);
+
+       switch(board) {
+       case BCM47XX_BOARD_ASUS_RTN16:
+               return &platforms[RTN16];
+       case BCM47XX_BOARD_ASUS_WL330GE:
+               return &platforms[WL330GE];
+       case BCM47XX_BOARD_ASUS_WL500GPV1:
+               return &platforms[WL500GP];
+       case BCM47XX_BOARD_ASUS_WL500GPV2:
+               return &platforms[WL500GPV2];
+       case BCM47XX_BOARD_ASUS_WL520GC:
+               return &platforms[WL520GC];
+       case BCM47XX_BOARD_ASUS_WL520GU:
+               return &platforms[WL520GU];
+       case BCM47XX_BOARD_ASUS_WL700GE:
+               return &platforms[WL700GE];
+       case BCM47XX_BOARD_BELKIN_F7D4301:
+               return &platforms[BELKIN_F7D4301];
+       case BCM47XX_BOARD_BUFFALO_WBR2_G54:
+               return &platforms[WBR2_G54];
+       case BCM47XX_BOARD_BUFFALO_WHR2_A54G54:
+               return &platforms[WHR2_A54G54];
+       case BCM47XX_BOARD_BUFFALO_WHR_G125:
+               return &platforms[WHR_G125];
+       case BCM47XX_BOARD_BUFFALO_WHR_G54S:
+               return &platforms[WHR_G54S];
+       case BCM47XX_BOARD_BUFFALO_WHR_HP_G54:
+               return &platforms[WHR_HP_G54];
+       case BCM47XX_BOARD_BUFFALO_WLA2_G54L:
+               return &platforms[WLA2_G54L];
+       case BCM47XX_BOARD_BUFFALO_WZR_G300N:
+               return &platforms[WZR_G300N];
+       case BCM47XX_BOARD_BUFFALO_WZR_RS_G54:
+               return &platforms[WZR_RS_G54];
+       case BCM47XX_BOARD_BUFFALO_WZR_RS_G54HP:
+               return &platforms[WZR_RS_G54HP];
+       case BCM47XX_BOARD_DELL_TM2300:
+               return &platforms[TM2300];
+       case BCM47XX_BOARD_DLINK_DIR130:
+               return &platforms[DIR130];
+       case BCM47XX_BOARD_DLINK_DIR330:
+               return &platforms[DIR330];
+       case BCM47XX_BOARD_LINKSYS_E1000V1:
+               return &platforms[E1000V1];
+       case BCM47XX_BOARD_LINKSYS_E3000V1:
+               return &platforms[E3000V1];
+       case BCM47XX_BOARD_LINKSYS_E3200V1:
+               return &platforms[E3200V1];
+       case BCM47XX_BOARD_LINKSYS_WRT150NV1:
+               return &platforms[WRT150NV1];
+       case BCM47XX_BOARD_LINKSYS_WRT150NV11:
+               return &platforms[WRT150NV11];
+       case BCM47XX_BOARD_LINKSYS_WRT160NV1:
+               return &platforms[WRT160NV1];
+       case BCM47XX_BOARD_LINKSYS_WRT160NV3:
+               return &platforms[WRT160NV3];
+       case BCM47XX_BOARD_LINKSYS_WRT300NV11:
+               return &platforms[WRT300NV11];
+       case BCM47XX_BOARD_LINKSYS_WRT54G3GV2:
+               return &platforms[WRT54G3GV2_VF];
+       case BCM47XX_BOARD_LINKSYS_WRT610NV1:
+               return &platforms[WRT610N];
+       case BCM47XX_BOARD_LINKSYS_WRT610NV2:
+               return &platforms[WRT610NV2];
+       case BCM47XX_BOARD_MOTOROLA_WE800G:
+               return &platforms[WE800G];
+       case BCM47XX_BOARD_MOTOROLA_WR850GP:
+               return &platforms[WR850GP];
+       case BCM47XX_BOARD_MOTOROLA_WR850GV2V3:
+               return &platforms[WR850GV2V3];
+       case BCM47XX_BOARD_NETGEAR_WNDR3400V1:
+               return &platforms[WNDR3400V1];
+       case BCM47XX_BOARD_NETGEAR_WNDR3700V3:
+               return &platforms[WNDR3700V3];
+       case BCM47XX_BOARD_UNKNOWN:
+       case BCM47XX_BOARD_NON:
+               printk(MODULE_NAME ": unknown board found, try legacy detect\n");
+               printk(MODULE_NAME ": please open a ticket at https://dev.openwrt.org and attach the complete nvram\n");
+               return platform_detect_legacy();
+       default:
+               printk(MODULE_NAME ": board was detected as \"%s\", but not gpio configuration available\n", board_name);
+               printk(MODULE_NAME ": now trying legacy detect\n");
+               return platform_detect_legacy();
+       }
+}
+
+static inline void ssb_maskset32(struct ssb_device *dev,
+                                 u16 offset, u32 mask, u32 set)
+{
+       ssb_write32(dev, offset, (ssb_read32(dev, offset) & mask) | set);
+}
+
+static void gpio_set_irqenable(int enabled, irqreturn_t (*handler)(int, void *))
+{
+       int irq;
+
+       irq = gpio_to_irq(0);
+       if (irq == -EINVAL) return;
+       
+       if (enabled) {
+               if (request_irq(irq, handler, IRQF_SHARED, "gpio", handler))
+                       return;
+       } else {
+               free_irq(irq, handler);
+       }
+
+       switch (bcm47xx_bus_type) {
+#ifdef CONFIG_BCM47XX_SSB
+       case BCM47XX_BUS_TYPE_SSB:
+               if (bcm47xx_bus.ssb.chipco.dev)
+                       ssb_maskset32(bcm47xx_bus.ssb.chipco.dev, SSB_CHIPCO_IRQMASK, ~SSB_CHIPCO_IRQ_GPIO, (enabled ? SSB_CHIPCO_IRQ_GPIO : 0));
+               break;
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+       case BCM47XX_BUS_TYPE_BCMA:
+               if (bcm47xx_bus.bcma.bus.drv_cc.core)
+                       bcma_maskset32(bcm47xx_bus.bcma.bus.drv_cc.core, BCMA_CC_IRQMASK, ~BCMA_CC_IRQ_GPIO, (enabled ? BCMA_CC_IRQ_GPIO : 0));
+               break;
+#endif
+       }
+}
+
 static void register_buttons(struct button_t *b)
 {
        for (; b->name; b++)
@@ -1290,18 +1388,18 @@ static void register_buttons(struct button_t *b)
 
        platform.button_mask &= ~gpiomask;
 
-       gpio_outen(platform.button_mask, 0);
-       gpio_control(platform.button_mask, 0);
-       platform.button_polarity = gpio_in() & platform.button_mask;
-       gpio_intpolarity(platform.button_mask, platform.button_polarity);
-       gpio_setintmask(platform.button_mask, platform.button_mask);
+       bcm47xx_gpio_outen(platform.button_mask, 0);
+       bcm47xx_gpio_control(platform.button_mask, 0);
+       platform.button_polarity = bcm47xx_gpio_in(~0) & platform.button_mask;
+       bcm47xx_gpio_polarity(platform.button_mask, platform.button_polarity);
+       bcm47xx_gpio_intmask(platform.button_mask, platform.button_mask);
 
        gpio_set_irqenable(1, button_handler);
 }
 
 static void unregister_buttons(struct button_t *b)
 {
-       gpio_setintmask(platform.button_mask, 0);
+       bcm47xx_gpio_intmask(platform.button_mask, 0);
 
        gpio_set_irqenable(0, button_handler);
 }
@@ -1361,12 +1459,12 @@ static irqreturn_t button_handler(int irq, void *dev_id)
        struct button_t *b;
        u32 in, changed;
 
-       in = gpio_in() & platform.button_mask;
-       gpio_intpolarity(platform.button_mask, in);
+       in = bcm47xx_gpio_in(~0) & platform.button_mask;
+       bcm47xx_gpio_polarity(platform.button_mask, in);
        changed = platform.button_polarity ^ in;
        platform.button_polarity = in;
 
-       changed &= ~gpio_outen(0, 0);
+       changed &= ~bcm47xx_gpio_outen(0, 0);
 
        for (b = platform.buttons; b->name; b++) {
                struct event_t *event;
@@ -1403,13 +1501,23 @@ static void register_leds(struct led_t *l)
                if (l->gpio & gpiomask)
                        continue;
 
-               if (l->gpio & GPIO_TYPE_EXTIF) {
+               switch (l->gpio & GPIO_TYPE_MASK) {
+               case GPIO_TYPE_EXTIF:
                        l->state = 0;
                        set_led_extif(l);
-               } else {
+                       break;
+               case GPIO_TYPE_SHIFT:
+                       mask |= (SHIFTREG_DATA | SHIFTREG_CLK);
+                       oe_mask |= (SHIFTREG_DATA | SHIFTREG_CLK);
+                       l->state = (l->polarity != NORMAL);
+                       set_led_shift(l);
+                       break;
+               case GPIO_TYPE_NORMAL:
+               default:
                        if (l->polarity != INPUT) oe_mask |= l->gpio;
                        mask |= l->gpio;
                        val |= (l->polarity == NORMAL)?0:l->gpio;
+                       break;
                }
 
                if (l->polarity == INPUT) continue;
@@ -1422,10 +1530,10 @@ static void register_leds(struct led_t *l)
                }
        }
 
-       gpio_outen(mask, oe_mask);
-       gpio_control(mask, 0);
-       gpio_out(mask, val);
-       gpio_setintmask(mask, 0);
+       bcm47xx_gpio_outen(mask, oe_mask);
+       bcm47xx_gpio_control(mask, 0);
+       bcm47xx_gpio_out(mask, val);
+       bcm47xx_gpio_intmask(mask, 0);
 }
 
 static void unregister_leds(struct led_t *l)
@@ -1438,33 +1546,85 @@ static void unregister_leds(struct led_t *l)
 
 static void set_led_extif(struct led_t *led)
 {
-       gpio_set_extif(led->gpio, led->state);
+       volatile u8 *addr = (volatile u8 *) KSEG1ADDR(EXTIF_UART) + (led->gpio & ~GPIO_TYPE_MASK);
+       if (led->state)
+               *addr = 0xFF;
+       else
+               *addr;
+}
+
+/*
+ * This should be extended to allow the platform to specify the pins and width
+ * of the shift register. They're hardcoded for now because only the WNDR3700v3
+ * uses it.
+ */
+static void shiftreg_output(unsigned int val)
+{
+       unsigned int mask;
+
+       bcm47xx_gpio_out(SHIFTREG_DATA, SHIFTREG_DATA); /* init off, pull high */
+       bcm47xx_gpio_out(SHIFTREG_CLK, 0); /* init reset */
+
+       /* shift 8 times */
+       for(mask = 1 << (SHIFTREG_MAX_BITS-1); mask; mask >>= 1)
+       {
+               bcm47xx_gpio_out(SHIFTREG_DATA, (val & mask) ? SHIFTREG_DATA : 0);
+               bcm47xx_gpio_out(SHIFTREG_CLK, SHIFTREG_CLK); /* pull high to trigger */
+               bcm47xx_gpio_out(SHIFTREG_CLK, 0); /* reset to low */
+       }
+}
+
+static void set_led_shift(struct led_t *led)
+{
+       static u32      shiftreg = 0;
+       u32                     old = shiftreg;
+       u32                     pin = (led->gpio & ~GPIO_TYPE_MASK);
+
+       if (led->state) {
+               shiftreg |= pin;
+       } else {
+               shiftreg &= ~pin;
+       }
+
+       /* Clock the bits out. */
+       if (shiftreg != old) {
+               shiftreg_output(shiftreg);
+       }
 }
 
+
 static void led_flash(unsigned long dummy) {
        struct led_t *l;
        u32 mask = 0;
        u8 extif_blink = 0;
 
        for (l = platform.leds; l->name; l++) {
-               if (l->flash) {
-                       if (l->gpio & GPIO_TYPE_EXTIF) {
-                               extif_blink = 1;
-                               l->state = !l->state;
-                               set_led_extif(l);
-                       } else {
-                               mask |= l->gpio;
-                       }
+               if (!l->flash) continue;
+               switch (l->gpio & GPIO_TYPE_MASK) {
+               case GPIO_TYPE_EXTIF:
+                       extif_blink = 1;
+                       l->state = !l->state;
+                       set_led_extif(l);
+                       break;
+               case GPIO_TYPE_SHIFT:
+                       extif_blink = 1;
+                       l->state = !l->state;
+                       set_led_shift(l);
+                       break;
+               case GPIO_TYPE_NORMAL:
+               default:
+                       mask |= l->gpio;
+                       break;
                }
        }
 
        mask &= ~gpiomask;
        if (mask) {
-               u32 val = ~gpio_in();
+               u32 val = ~bcm47xx_gpio_in(~0);
 
-               gpio_outen(mask, mask);
-               gpio_control(mask, 0);
-               gpio_out(mask, val);
+               bcm47xx_gpio_outen(mask, mask);
+               bcm47xx_gpio_control(mask, 0);
+               bcm47xx_gpio_out(mask, val);
        }
        if (mask || extif_blink) {
                mod_timer(&led_timer, jiffies + FLASH_TIME);
@@ -1485,16 +1645,14 @@ static ssize_t diag_proc_read(struct file *file, char *buf, size_t count, loff_t
                switch (handler->type) {
                        case PROC_LED: {
                                struct led_t * led = (struct led_t *) handler->ptr;
+                               u8 p = (led->polarity == NORMAL ? 0 : 1);
                                if (led->flash) {
                                        len = sprintf(page, "f\n");
+                               } else if ((led->gpio & GPIO_TYPE_MASK) != GPIO_TYPE_NORMAL) {
+                                       len = sprintf(page, "%d\n", ((led->state ^ p) ? 1 : 0));
                                } else {
-                                       if (led->gpio & GPIO_TYPE_EXTIF) {
-                                               len = sprintf(page, "%d\n", led->state);
-                                       } else {
-                                               u32 in = (gpio_in() & led->gpio ? 1 : 0);
-                                               u8 p = (led->polarity == NORMAL ? 0 : 1);
-                                               len = sprintf(page, "%d\n", ((in ^ p) ? 1 : 0));
-                                       }
+                                       u32 in = (bcm47xx_gpio_in(~0) & led->gpio ? 1 : 0);
+                                       len = sprintf(page, "%d\n", ((in ^ p) ? 1 : 0));
                                }
                                break;
                        }
@@ -1551,13 +1709,16 @@ static ssize_t diag_proc_write(struct file *file, const char *buf, size_t count,
                                        led_flash(0);
                                } else {
                                        led->flash = 0;
-                                       if (led->gpio & GPIO_TYPE_EXTIF) {
+                                       if ((led->gpio & GPIO_TYPE_MASK) == GPIO_TYPE_EXTIF) {
                                                led->state = p ^ ((page[0] == '1') ? 1 : 0);
                                                set_led_extif(led);
+                                       } else if ((led->gpio & GPIO_TYPE_MASK) == GPIO_TYPE_SHIFT) {
+                                               led->state = p ^ ((page[0] == '1') ? 1 : 0);
+                                               set_led_shift(led);
                                        } else {
-                                               gpio_outen(led->gpio, led->gpio);
-                                               gpio_control(led->gpio, 0);
-                                               gpio_out(led->gpio, ((p ^ (page[0] == '1')) ? led->gpio : 0));
+                                               bcm47xx_gpio_outen(led->gpio, led->gpio);
+                                               bcm47xx_gpio_control(led->gpio, 0);
+                                               bcm47xx_gpio_out(led->gpio, ((p ^ (page[0] == '1')) ? led->gpio : 0));
                                        }
                                }
                                break;