Root/package/switch/src/switch-robo.c

1/*
2 * Broadcom BCM5325E/536x switch configuration module
3 *
4 * Copyright (C) 2005 Felix Fietkau <nbd@nbd.name>
5 * Copyright (C) 2008 Michael Buesch <mb@bu3sch.de>
6 * Based on 'robocfg' by Oleg I. Vdovikin
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 * 02110-1301, USA.
22 */
23
24#include <linux/module.h>
25#include <linux/init.h>
26#include <linux/if.h>
27#include <linux/if_arp.h>
28#include <linux/sockios.h>
29#include <linux/ethtool.h>
30#include <linux/mii.h>
31#include <linux/delay.h>
32#include <asm/uaccess.h>
33
34#include "switch-core.h"
35#include "etc53xx.h"
36
37#define DRIVER_NAME "bcm53xx"
38#define DRIVER_VERSION "0.02"
39#define PFX "roboswitch: "
40
41#define ROBO_PHY_ADDR 0x1E /* robo switch phy address */
42#define ROBO_PHY_ADDR_TG3 0x01 /* Tigon3 PHY address */
43#define ROBO_PHY_ADDR_BCM63XX 0x00 /* BCM63XX PHY address */
44
45/* MII registers */
46#define REG_MII_PAGE 0x10 /* MII Page register */
47#define REG_MII_ADDR 0x11 /* MII Address register */
48#define REG_MII_DATA0 0x18 /* MII Data register 0 */
49
50#define REG_MII_PAGE_ENABLE 1
51#define REG_MII_ADDR_WRITE 1
52#define REG_MII_ADDR_READ 2
53
54/* Robo device ID register (in ROBO_MGMT_PAGE) */
55#define ROBO_DEVICE_ID 0x30
56#define ROBO_DEVICE_ID_5325 0x25 /* Faked */
57#define ROBO_DEVICE_ID_5395 0x95
58#define ROBO_DEVICE_ID_5397 0x97
59#define ROBO_DEVICE_ID_5398 0x98
60#define ROBO_DEVICE_ID_53115 0x3115
61
62/* Private et.o ioctls */
63#define SIOCGETCPHYRD (SIOCDEVPRIVATE + 9)
64#define SIOCSETCPHYWR (SIOCDEVPRIVATE + 10)
65
66/* Only available on brcm47xx */
67#ifdef BROADCOM
68extern char *nvram_get(const char *name);
69#define getvar(str) (nvram_get(str)?:"")
70#else
71#define getvar(str) ""
72#endif
73
74/* Data structure for a Roboswitch device. */
75struct robo_switch {
76    char *device; /* The device name string (ethX) */
77    u16 devid; /* ROBO_DEVICE_ID_53xx */
78    bool use_et;
79    bool is_5350;
80    u8 phy_addr; /* PHY address of the device */
81    struct ifreq ifr;
82    struct net_device *dev;
83    unsigned char port[6];
84};
85
86/* Currently we can only have one device in the system. */
87static struct robo_switch robo;
88
89
90static int do_ioctl(int cmd, void *buf)
91{
92    mm_segment_t old_fs = get_fs();
93    int ret;
94
95    if (buf != NULL)
96        robo.ifr.ifr_data = (caddr_t) buf;
97
98    set_fs(KERNEL_DS);
99#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)
100    ret = robo.dev->netdev_ops->ndo_do_ioctl(robo.dev, &robo.ifr, cmd);
101#else
102    ret = robo.dev->do_ioctl(robo.dev, &robo.ifr, cmd);
103#endif
104    set_fs(old_fs);
105
106    return ret;
107}
108
109static u16 mdio_read(__u16 phy_id, __u8 reg)
110{
111    if (robo.use_et) {
112        int args[2] = { reg };
113
114        if (phy_id != robo.phy_addr) {
115            printk(KERN_ERR PFX
116                "Access to real 'phy' registers unavaliable.\n"
117                "Upgrade kernel driver.\n");
118
119            return 0xffff;
120        }
121
122
123        if (do_ioctl(SIOCGETCPHYRD, &args) < 0) {
124            printk(KERN_ERR PFX
125                   "[%s:%d] SIOCGETCPHYRD failed!\n", __FILE__, __LINE__);
126            return 0xffff;
127        }
128
129        return args[1];
130    } else {
131        struct mii_ioctl_data *mii = (struct mii_ioctl_data *) &robo.ifr.ifr_data;
132        mii->phy_id = phy_id;
133        mii->reg_num = reg;
134
135        if (do_ioctl(SIOCGMIIREG, NULL) < 0) {
136            printk(KERN_ERR PFX
137                   "[%s:%d] SIOCGMIIREG failed!\n", __FILE__, __LINE__);
138
139            return 0xffff;
140        }
141
142        return mii->val_out;
143    }
144}
145
146static void mdio_write(__u16 phy_id, __u8 reg, __u16 val)
147{
148    if (robo.use_et) {
149        int args[2] = { reg, val };
150
151        if (phy_id != robo.phy_addr) {
152            printk(KERN_ERR PFX
153                "Access to real 'phy' registers unavaliable.\n"
154                "Upgrade kernel driver.\n");
155
156            return;
157        }
158
159        if (do_ioctl(SIOCSETCPHYWR, args) < 0) {
160            printk(KERN_ERR PFX
161                   "[%s:%d] SIOCGETCPHYWR failed!\n", __FILE__, __LINE__);
162            return;
163        }
164    } else {
165        struct mii_ioctl_data *mii = (struct mii_ioctl_data *)&robo.ifr.ifr_data;
166
167        mii->phy_id = phy_id;
168        mii->reg_num = reg;
169        mii->val_in = val;
170
171        if (do_ioctl(SIOCSMIIREG, NULL) < 0) {
172            printk(KERN_ERR PFX
173                   "[%s:%d] SIOCSMIIREG failed!\n", __FILE__, __LINE__);
174            return;
175        }
176    }
177}
178
179static int robo_reg(__u8 page, __u8 reg, __u8 op)
180{
181    int i = 3;
182
183    /* set page number */
184    mdio_write(robo.phy_addr, REG_MII_PAGE,
185        (page << 8) | REG_MII_PAGE_ENABLE);
186
187    /* set register address */
188    mdio_write(robo.phy_addr, REG_MII_ADDR,
189        (reg << 8) | op);
190
191    /* check if operation completed */
192    while (i--) {
193        if ((mdio_read(robo.phy_addr, REG_MII_ADDR) & 3) == 0)
194            return 0;
195    }
196
197    printk(KERN_ERR PFX "[%s:%d] timeout in robo_reg!\n", __FILE__, __LINE__);
198
199    return 0;
200}
201
202/*
203static void robo_read(__u8 page, __u8 reg, __u16 *val, int count)
204{
205    int i;
206
207    robo_reg(page, reg, REG_MII_ADDR_READ);
208
209    for (i = 0; i < count; i++)
210        val[i] = mdio_read(robo.phy_addr, REG_MII_DATA0 + i);
211}
212*/
213
214static __u16 robo_read16(__u8 page, __u8 reg)
215{
216    robo_reg(page, reg, REG_MII_ADDR_READ);
217
218    return mdio_read(robo.phy_addr, REG_MII_DATA0);
219}
220
221static __u32 robo_read32(__u8 page, __u8 reg)
222{
223    robo_reg(page, reg, REG_MII_ADDR_READ);
224
225    return mdio_read(robo.phy_addr, REG_MII_DATA0) +
226        (mdio_read(robo.phy_addr, REG_MII_DATA0 + 1) << 16);
227}
228
229static void robo_write16(__u8 page, __u8 reg, __u16 val16)
230{
231    /* write data */
232    mdio_write(robo.phy_addr, REG_MII_DATA0, val16);
233
234    robo_reg(page, reg, REG_MII_ADDR_WRITE);
235}
236
237static void robo_write32(__u8 page, __u8 reg, __u32 val32)
238{
239    /* write data */
240    mdio_write(robo.phy_addr, REG_MII_DATA0, val32 & 65535);
241    mdio_write(robo.phy_addr, REG_MII_DATA0 + 1, val32 >> 16);
242
243    robo_reg(page, reg, REG_MII_ADDR_WRITE);
244}
245
246/* checks that attached switch is 5325E/5350 */
247static int robo_vlan5350(void)
248{
249    /* set vlan access id to 15 and read it back */
250    __u16 val16 = 15;
251    robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16);
252
253    /* 5365 will refuse this as it does not have this reg */
254    return (robo_read16(ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350) == val16);
255}
256
257static int robo_switch_enable(void)
258{
259    unsigned int i, last_port;
260    u16 val;
261
262    val = robo_read16(ROBO_CTRL_PAGE, ROBO_SWITCH_MODE);
263    if (!(val & (1 << 1))) {
264        /* Unmanaged mode */
265        val &= ~(1 << 0);
266        /* With forwarding */
267        val |= (1 << 1);
268        robo_write16(ROBO_CTRL_PAGE, ROBO_SWITCH_MODE, val);
269        val = robo_read16(ROBO_CTRL_PAGE, ROBO_SWITCH_MODE);
270        if (!(val & (1 << 1))) {
271            printk("Failed to enable switch\n");
272            return -EBUSY;
273        }
274
275        last_port = (robo.devid == ROBO_DEVICE_ID_5398) ?
276                ROBO_PORT6_CTRL : ROBO_PORT3_CTRL;
277        for (i = ROBO_PORT0_CTRL; i < last_port + 1; i++)
278            robo_write16(ROBO_CTRL_PAGE, i, 0);
279    }
280
281    /* WAN port LED, except for Netgear WGT634U */
282    if (strcmp(getvar("nvram_type"), "cfe") != 0)
283        robo_write16(ROBO_CTRL_PAGE, 0x16, 0x1F);
284
285    return 0;
286}
287
288static void robo_switch_reset(void)
289{
290    if ((robo.devid == ROBO_DEVICE_ID_5395) ||
291        (robo.devid == ROBO_DEVICE_ID_5397) ||
292        (robo.devid == ROBO_DEVICE_ID_5398)) {
293        /* Trigger a software reset. */
294        robo_write16(ROBO_CTRL_PAGE, 0x79, 0x83);
295        mdelay(500);
296        robo_write16(ROBO_CTRL_PAGE, 0x79, 0);
297    }
298}
299
300static int robo_probe(char *devname)
301{
302    __u32 phyid;
303    unsigned int i;
304    int err = 1;
305
306    printk(KERN_INFO PFX "Probing device %s: ", devname);
307    strcpy(robo.ifr.ifr_name, devname);
308
309#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
310    if ((robo.dev = dev_get_by_name(devname)) == NULL) {
311#else
312    if ((robo.dev = dev_get_by_name(&init_net, devname)) == NULL) {
313#endif
314        printk("No such device\n");
315        return 1;
316    }
317
318    robo.device = devname;
319    for (i = 0; i < 5; i++)
320        robo.port[i] = i;
321    robo.port[5] = 8;
322
323    /* try access using MII ioctls - get phy address */
324    if (do_ioctl(SIOCGMIIPHY, NULL) < 0) {
325        robo.use_et = 1;
326        robo.phy_addr = ROBO_PHY_ADDR;
327    } else {
328        /* got phy address check for robo address */
329        struct mii_ioctl_data *mii = (struct mii_ioctl_data *) &robo.ifr.ifr_data;
330        if ((mii->phy_id != ROBO_PHY_ADDR) &&
331            (mii->phy_id != ROBO_PHY_ADDR_BCM63XX) &&
332            (mii->phy_id != ROBO_PHY_ADDR_TG3)) {
333            printk("Invalid phy address (%d)\n", mii->phy_id);
334            goto done;
335        }
336        robo.use_et = 0;
337        /* The robo has a fixed PHY address that is different from the
338         * Tigon3 and BCM63xx PHY address. */
339        robo.phy_addr = ROBO_PHY_ADDR;
340    }
341
342    phyid = mdio_read(robo.phy_addr, 0x2) |
343        (mdio_read(robo.phy_addr, 0x3) << 16);
344
345    if (phyid == 0xffffffff || phyid == 0x55210022) {
346        printk("No Robo switch in managed mode found, phy_id = 0x%08x\n", phyid);
347        goto done;
348    }
349
350    /* Get the device ID */
351    for (i = 0; i < 10; i++) {
352        robo.devid = robo_read16(ROBO_MGMT_PAGE, ROBO_DEVICE_ID);
353        if (robo.devid)
354            break;
355        udelay(10);
356    }
357    if (!robo.devid)
358        robo.devid = ROBO_DEVICE_ID_5325; /* Fake it */
359    robo.is_5350 = robo_vlan5350();
360
361    robo_switch_reset();
362    err = robo_switch_enable();
363    if (err)
364        goto done;
365    err = 0;
366
367    printk("found a 5%s%x!%s\n", robo.devid & 0xff00 ? "" : "3", robo.devid,
368        robo.is_5350 ? " It's a 5350." : "");
369
370done:
371    if (err) {
372        dev_put(robo.dev);
373        robo.dev = NULL;
374    }
375    return err;
376}
377
378
379static int handle_vlan_port_read(void *driver, char *buf, int nr)
380{
381    __u16 val16;
382    int len = 0;
383    int j;
384
385    val16 = (nr) /* vlan */ | (0 << 12) /* read */ | (1 << 13) /* enable */;
386
387    if (robo.is_5350) {
388        u32 val32;
389        robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16);
390        /* actual read */
391        val32 = robo_read32(ROBO_VLAN_PAGE, ROBO_VLAN_READ);
392        if ((val32 & (1 << 20)) /* valid */) {
393            for (j = 0; j < 6; j++) {
394                if (val32 & (1 << j)) {
395                    len += sprintf(buf + len, "%d", j);
396                    if (val32 & (1 << (j + 6))) {
397                        if (j == 5) buf[len++] = 'u';
398                    } else {
399                        buf[len++] = 't';
400                        if (robo_read16(ROBO_VLAN_PAGE, ROBO_VLAN_PORT0_DEF_TAG + (j << 1)) == nr)
401                            buf[len++] = '*';
402                    }
403                    buf[len++] = '\t';
404                }
405            }
406            len += sprintf(buf + len, "\n");
407        }
408    } else {
409        robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS, val16);
410        /* actual read */
411        val16 = robo_read16(ROBO_VLAN_PAGE, ROBO_VLAN_READ);
412        if ((val16 & (1 << 14)) /* valid */) {
413            for (j = 0; j < 6; j++) {
414                if (val16 & (1 << j)) {
415                    len += sprintf(buf + len, "%d", j);
416                    if (val16 & (1 << (j + 7))) {
417                        if (j == 5) buf[len++] = 'u';
418                    } else {
419                        buf[len++] = 't';
420                        if (robo_read16(ROBO_VLAN_PAGE, ROBO_VLAN_PORT0_DEF_TAG + (j << 1)) == nr)
421                            buf[len++] = '*';
422                    }
423                    buf[len++] = '\t';
424                }
425            }
426            len += sprintf(buf + len, "\n");
427        }
428    }
429
430    buf[len] = '\0';
431
432    return len;
433}
434
435static int handle_vlan_port_write(void *driver, char *buf, int nr)
436{
437    switch_driver *d = (switch_driver *) driver;
438    switch_vlan_config *c = switch_parse_vlan(d, buf);
439    int j;
440    __u16 val16;
441
442    if (c == NULL)
443        return -EINVAL;
444
445    for (j = 0; j < d->ports; j++) {
446        if ((c->untag | c->pvid) & (1 << j))
447            /* change default vlan tag */
448            robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_PORT0_DEF_TAG + (j << 1), nr);
449    }
450
451    /* write config now */
452
453    if (robo.devid != ROBO_DEVICE_ID_5325) {
454        __u8 regoff = ((robo.devid == ROBO_DEVICE_ID_5395) ||
455            (robo.devid == ROBO_DEVICE_ID_53115)) ? 0x20 : 0;
456
457        robo_write32(ROBO_ARLIO_PAGE, 0x63 + regoff, (c->untag << 9) | c->port);
458        robo_write16(ROBO_ARLIO_PAGE, 0x61 + regoff, nr);
459        robo_write16(ROBO_ARLIO_PAGE, 0x60 + regoff, 1 << 7);
460        return 0;
461    }
462
463    val16 = (nr) /* vlan */ | (1 << 12) /* write */ | (1 << 13) /* enable */;
464    if (robo.is_5350) {
465        robo_write32(ROBO_VLAN_PAGE, ROBO_VLAN_WRITE_5350,
466            (1 << 20) /* valid */ | (c->untag << 6) | c->port);
467        robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS_5350, val16);
468    } else {
469        robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_WRITE,
470            (1 << 14) /* valid */ | (c->untag << 7) | c->port);
471        robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_TABLE_ACCESS, val16);
472    }
473
474    return 0;
475}
476
477#define set_switch(state) \
478    robo_write16(ROBO_CTRL_PAGE, ROBO_SWITCH_MODE, (robo_read16(ROBO_CTRL_PAGE, ROBO_SWITCH_MODE) & ~2) | (state ? 2 : 0));
479
480static int handle_enable_read(void *driver, char *buf, int nr)
481{
482    return sprintf(buf, "%d\n", (((robo_read16(ROBO_CTRL_PAGE, ROBO_SWITCH_MODE) & 2) == 2) ? 1 : 0));
483}
484
485static int handle_enable_write(void *driver, char *buf, int nr)
486{
487    set_switch(buf[0] == '1');
488
489    return 0;
490}
491
492static int handle_enable_vlan_read(void *driver, char *buf, int nr)
493{
494    return sprintf(buf, "%d\n", (((robo_read16(ROBO_VLAN_PAGE, ROBO_VLAN_CTRL0) & (1 << 7)) == (1 << 7)) ? 1 : 0));
495}
496
497static int handle_enable_vlan_write(void *driver, char *buf, int nr)
498{
499    int disable = ((buf[0] != '1') ? 1 : 0);
500
501    robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_CTRL0, disable ? 0 :
502        (1 << 7) /* 802.1Q VLAN */ | (3 << 5) /* mac check and hash */);
503    robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_CTRL1, disable ? 0 :
504        (robo.devid == ROBO_DEVICE_ID_5325 ? (1 << 1) :
505        0) | (1 << 2) | (1 << 3)); /* RSV multicast */
506
507    if (robo.devid != ROBO_DEVICE_ID_5325)
508        return 0;
509
510    robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_CTRL4, disable ? 0 :
511        (1 << 6) /* drop invalid VID frames */);
512    robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_CTRL5, disable ? 0 :
513        (1 << 3) /* drop miss V table frames */);
514
515    return 0;
516}
517
518static int handle_reset(void *driver, char *buf, int nr)
519{
520    switch_driver *d = (switch_driver *) driver;
521    switch_vlan_config *c = switch_parse_vlan(d, buf);
522    int j;
523    __u16 val16;
524
525    if (c == NULL)
526        return -EINVAL;
527
528    /* disable switching */
529    set_switch(0);
530
531    /* reset vlans */
532    for (j = 0; j <= ((robo.is_5350) ? VLAN_ID_MAX5350 : VLAN_ID_MAX); j++) {
533        /* write config now */
534        val16 = (j) /* vlan */ | (1 << 12) /* write */ | (1 << 13) /* enable */;
535        if (robo.is_5350)
536            robo_write32(ROBO_VLAN_PAGE, ROBO_VLAN_WRITE_5350, 0);
537        else
538            robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_WRITE, 0);
539        robo_write16(ROBO_VLAN_PAGE, robo.is_5350 ? ROBO_VLAN_TABLE_ACCESS_5350 :
540                                ROBO_VLAN_TABLE_ACCESS,
541                 val16);
542    }
543
544    /* reset ports to a known good state */
545    for (j = 0; j < d->ports; j++) {
546        robo_write16(ROBO_CTRL_PAGE, robo.port[j], 0x0000);
547        robo_write16(ROBO_VLAN_PAGE, ROBO_VLAN_PORT0_DEF_TAG + (j << 1), 0);
548    }
549
550    /* enable switching */
551    set_switch(1);
552
553    /* enable vlans */
554    handle_enable_vlan_write(driver, "1", 0);
555
556    return 0;
557}
558
559static int __init robo_init(void)
560{
561    int notfound = 1;
562    char *device;
563
564    device = strdup("ethX");
565    for (device[3] = '0'; (device[3] <= '3') && notfound; device[3]++) {
566        if (! switch_device_registered (device))
567            notfound = robo_probe(device);
568    }
569    device[3]--;
570
571    if (notfound) {
572        kfree(device);
573        return -ENODEV;
574    } else {
575        static const switch_config cfg[] = {
576            {
577                .name = "enable",
578                .read = handle_enable_read,
579                .write = handle_enable_write
580            }, {
581                .name = "enable_vlan",
582                .read = handle_enable_vlan_read,
583                .write = handle_enable_vlan_write
584            }, {
585                .name = "reset",
586                .read = NULL,
587                .write = handle_reset
588            }, { NULL, },
589        };
590        static const switch_config vlan[] = {
591            {
592                .name = "ports",
593                .read = handle_vlan_port_read,
594                .write = handle_vlan_port_write
595            }, { NULL, },
596        };
597        switch_driver driver = {
598            .name = DRIVER_NAME,
599            .version = DRIVER_VERSION,
600            .interface = device,
601            .cpuport = 5,
602            .ports = 6,
603            .vlans = 16,
604            .driver_handlers = cfg,
605            .port_handlers = NULL,
606            .vlan_handlers = vlan,
607        };
608        if (robo.devid != ROBO_DEVICE_ID_5325) {
609            driver.ports = 9;
610            driver.cpuport = 8;
611        }
612
613        return switch_register_driver(&driver);
614    }
615}
616
617static void __exit robo_exit(void)
618{
619    switch_unregister_driver(DRIVER_NAME);
620    if (robo.dev)
621        dev_put(robo.dev);
622    kfree(robo.device);
623}
624
625
626MODULE_AUTHOR("Felix Fietkau <openwrt@nbd.name>");
627MODULE_LICENSE("GPL");
628
629module_init(robo_init);
630module_exit(robo_exit);
631

Archive Download this file



interactive