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

1/*
2 * ADMTEK Adm6996 switch configuration module
3 *
4 * Copyright (C) 2005 Felix Fietkau <nbd@nbd.name>
5 *
6 * Partially based on Broadcom Home Networking Division 10/100 Mbit/s
7 * Ethernet Device Driver (from Montavista 2.4.20_mvl31 Kernel).
8 * Copyright (C) 2004 Broadcom Corporation
9 *
10 * adm_rreg function from adm6996
11 * Copyright (C) 2004 Nikki Chumakov <nikki@gattaca.ru>
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 * 02110-1301, USA.
27 */
28
29#include <linux/module.h>
30#include <linux/init.h>
31#include <linux/if.h>
32#include <linux/if_arp.h>
33#include <linux/sockios.h>
34#include <linux/delay.h>
35#include <asm/uaccess.h>
36
37#include "switch-core.h"
38#include "gpio.h"
39
40#ifdef CONFIG_BCM47XX
41#include <nvram.h>
42#endif
43
44#define DRIVER_NAME "adm6996"
45#define DRIVER_VERSION "0.01"
46
47static int eecs = 0;
48static int eesk = 0;
49static int eedi = 0;
50static int eerc = 0;
51static int force = 0;
52
53MODULE_AUTHOR("Felix Fietkau <openwrt@nbd.name>");
54MODULE_LICENSE("GPL");
55module_param(eecs, int, 0);
56module_param(eesk, int, 0);
57module_param(eedi, int, 0);
58module_param(eerc, int, 0);
59module_param(force, int, 0);
60
61/* Minimum timing constants */
62#define EECK_EDGE_TIME 3 /* 3us - max(adm 2.5us, 93c 1us) */
63#define EEDI_SETUP_TIME 1 /* 1us - max(adm 10ns, 93c 400ns) */
64#define EECS_SETUP_TIME 1 /* 1us - max(adm no, 93c 200ns) */
65
66/* Handy macros for writing fixed length values */
67#define adm_write8(cs, b) { __u8 val = (__u8) (b); adm_write(cs, &val, sizeof(val)*8); }
68#define adm_write16(cs, w) { __u16 val = hton16(w); adm_write(cs, (__u8 *)&val, sizeof(val)*8); }
69#define adm_write32(cs, i) { uint32 val = hton32(i); adm_write(cs, (__u8 *)&val, sizeof(val)*8); }
70
71#define atoi(str) simple_strtoul(((str != NULL) ? str : ""), NULL, 0)
72
73#ifdef CONFIG_BCM47XX
74
75/* Return gpio pin number assigned to the named pin */
76/*
77* Variable should be in format:
78*
79* gpio<N>=pin_name
80*
81* 'def_pin' is returned if there is no such variable found.
82*/
83static unsigned int get_gpiopin(char *pin_name, unsigned int def_pin)
84{
85    char name[] = "gpioXXXX";
86    char val[10];
87    unsigned int pin;
88
89    /* Go thru all possibilities till a match in pin name */
90    for (pin = 0; pin < 16; pin ++) {
91        sprintf(name, "gpio%d", pin);
92        if (nvram_getenv(name, val, sizeof(val)) >= 0) {
93            if (!strcmp(val, pin_name))
94                return pin;
95        }
96    }
97    return def_pin;
98}
99#endif
100
101
102static void adm_write(int cs, char *buf, unsigned int bits)
103{
104    int i, len = (bits + 7) / 8;
105    __u8 mask;
106
107    gpio_out(eecs, (cs ? eecs : 0));
108    udelay(EECK_EDGE_TIME);
109
110    /* Byte assemble from MSB to LSB */
111    for (i = 0; i < len; i++) {
112        /* Bit bang from MSB to LSB */
113        for (mask = 0x80; mask && bits > 0; mask >>= 1, bits --) {
114            /* Clock low */
115            gpio_out(eesk, 0);
116            udelay(EECK_EDGE_TIME);
117
118            /* Output on rising edge */
119            gpio_out(eedi, ((mask & buf[i]) ? eedi : 0));
120            udelay(EEDI_SETUP_TIME);
121
122            /* Clock high */
123            gpio_out(eesk, eesk);
124            udelay(EECK_EDGE_TIME);
125        }
126    }
127
128    /* Clock low */
129    gpio_out(eesk, 0);
130    udelay(EECK_EDGE_TIME);
131
132    if (cs)
133        gpio_out(eecs, 0);
134}
135
136
137static void adm_read(int cs, char *buf, unsigned int bits)
138{
139    int i, len = (bits + 7) / 8;
140    __u8 mask;
141
142    gpio_out(eecs, (cs ? eecs : 0));
143    udelay(EECK_EDGE_TIME);
144
145    /* Byte assemble from MSB to LSB */
146    for (i = 0; i < len; i++) {
147        __u8 byte;
148
149        /* Bit bang from MSB to LSB */
150        for (mask = 0x80, byte = 0; mask && bits > 0; mask >>= 1, bits --) {
151            __u8 gp;
152
153            /* Clock low */
154            gpio_out(eesk, 0);
155            udelay(EECK_EDGE_TIME);
156
157            /* Input on rising edge */
158            gp = gpio_in();
159            if (gp & eedi)
160                byte |= mask;
161
162            /* Clock high */
163            gpio_out(eesk, eesk);
164            udelay(EECK_EDGE_TIME);
165        }
166
167        *buf++ = byte;
168    }
169
170    /* Clock low */
171    gpio_out(eesk, 0);
172    udelay(EECK_EDGE_TIME);
173
174    if (cs)
175        gpio_out(eecs, 0);
176}
177
178
179/* Enable outputs with specified value to the chip */
180static void adm_enout(__u8 pins, __u8 val)
181{
182    /* Prepare GPIO output value */
183    gpio_out(pins, val);
184
185    /* Enable GPIO outputs */
186    gpio_outen(pins, pins);
187    udelay(EECK_EDGE_TIME);
188}
189
190
191/* Disable outputs to the chip */
192static void adm_disout(__u8 pins)
193{
194    /* Disable GPIO outputs */
195    gpio_outen(pins, 0);
196    udelay(EECK_EDGE_TIME);
197}
198
199
200/* Advance clock(s) */
201static void adm_adclk(int clocks)
202{
203    int i;
204    for (i = 0; i < clocks; i++) {
205        /* Clock high */
206        gpio_out(eesk, eesk);
207        udelay(EECK_EDGE_TIME);
208
209        /* Clock low */
210        gpio_out(eesk, 0);
211        udelay(EECK_EDGE_TIME);
212    }
213}
214
215static __u32 adm_rreg(__u8 table, __u8 addr)
216{
217    /* cmd: 01 10 T DD R RRRRRR */
218    __u8 bits[6] = {
219        0xFF, 0xFF, 0xFF, 0xFF,
220        (0x06 << 4) | ((table & 0x01) << 3 | (addr&64)>>6),
221        ((addr&63)<<2)
222    };
223
224    __u8 rbits[4];
225
226    /* Enable GPIO outputs with all pins to 0 */
227    adm_enout((__u8)(eecs | eesk | eedi), 0);
228
229    adm_write(0, bits, 46);
230    adm_disout((__u8)(eedi));
231    adm_adclk(2);
232    adm_read (0, rbits, 32);
233
234    /* Extra clock(s) required per datasheet */
235    adm_adclk(2);
236
237    /* Disable GPIO outputs */
238    adm_disout((__u8)(eecs | eesk));
239
240    if (!table) /* EEPROM has 16-bit registers, but pumps out two registers in one request */
241        return (addr & 0x01 ? (rbits[0]<<8) | rbits[1] : (rbits[2]<<8) | (rbits[3]));
242    else
243        return (rbits[0]<<24) | (rbits[1]<<16) | (rbits[2]<<8) | rbits[3];
244}
245
246
247
248/* Write chip configuration register */
249/* Follow 93c66 timing and chip's min EEPROM timing requirement */
250void
251adm_wreg(__u8 addr, __u16 val)
252{
253    /* cmd(27bits): sb(1) + opc(01) + addr(bbbbbbbb) + data(bbbbbbbbbbbbbbbb) */
254    __u8 bits[4] = {
255        (0x05 << 5) | (addr >> 3),
256        (addr << 5) | (__u8)(val >> 11),
257        (__u8)(val >> 3),
258        (__u8)(val << 5)
259    };
260
261    /* Enable GPIO outputs with all pins to 0 */
262    adm_enout((__u8)(eecs | eesk | eedi), 0);
263
264    /* Write cmd. Total 27 bits */
265    adm_write(1, bits, 27);
266
267    /* Extra clock(s) required per datasheet */
268    adm_adclk(2);
269
270    /* Disable GPIO outputs */
271    adm_disout((__u8)(eecs | eesk | eedi));
272}
273
274
275/* Port configuration registers */
276static int port_conf[] = { 0x01, 0x03, 0x05, 0x07, 0x08, 0x09 };
277
278/* Bits in VLAN port mapping */
279static int vlan_ports[] = { 1 << 0, 1 << 2, 1 << 4, 1 << 6, 1 << 7, 1 << 8 };
280
281static int handle_vlan_port_read(void *driver, char *buf, int nr)
282{
283    int ports, i, c, len = 0;
284
285    if ((nr < 0) || (nr > 15))
286        return 0;
287
288    /* Get VLAN port map */
289    ports = adm_rreg(0, 0x13 + nr);
290
291    for (i = 0; i <= 5; i++) {
292        if (ports & vlan_ports[i]) {
293            c = adm_rreg(0, port_conf[i]);
294
295            len += sprintf(buf + len, "%d", i);
296            if (c & (1 << 4)) {
297                buf[len++] = 't';
298                if (((c & (0xf << 10)) >> 10) == nr)
299                    buf[len++] = '*';
300            } else if (i == 5)
301                buf[len++] = 'u';
302
303            buf[len++] = '\t';
304        }
305    }
306    len += sprintf(buf + len, "\n");
307
308    return len;
309}
310
311static int handle_vlan_port_write(void *driver, char *buf, int nr)
312{
313    int i, cfg, ports;
314    switch_driver *d = (switch_driver *) driver;
315    switch_vlan_config *c = switch_parse_vlan(d, buf);
316
317    if (c == NULL)
318        return -1;
319
320    ports = adm_rreg(0, 0x13 + nr);
321    for (i = 0; i < d->ports; i++) {
322        if (c->port & (1 << i)) {
323            ports |= vlan_ports[i];
324
325            cfg = adm_rreg(0, port_conf[i]);
326
327            /* Tagging */
328            if (c->untag & (1 << i))
329                cfg &= ~(1 << 4);
330            else
331                cfg |= (1 << 4);
332
333            if ((c->untag | c->pvid) & (1 << i)) {
334                cfg = (cfg & ~(0xf << 10)) | (nr << 10);
335            }
336
337            adm_wreg(port_conf[i], (__u16) cfg);
338        } else {
339            ports &= ~(vlan_ports[i]);
340        }
341    }
342    adm_wreg(0x13 + nr, (__u16) ports);
343
344    kfree(c);
345    return 0;
346}
347
348static int handle_port_enable_read(void *driver, char *buf, int nr)
349{
350    return sprintf(buf, "%d\n", ((adm_rreg(0, port_conf[nr]) & (1 << 5)) ? 0 : 1));
351}
352
353static int handle_port_enable_write(void *driver, char *buf, int nr)
354{
355    int reg = adm_rreg(0, port_conf[nr]);
356
357    if (buf[0] == '0')
358        reg |= (1 << 5);
359    else if (buf[0] == '1')
360        reg &= ~(1 << 5);
361    else return -1;
362
363    adm_wreg(port_conf[nr], (__u16) reg);
364    return 0;
365}
366
367static int handle_port_media_read(void *driver, char *buf, int nr)
368{
369    int len;
370    int media = 0;
371    int reg = adm_rreg(0, port_conf[nr]);
372
373    if (reg & (1 << 1))
374        media |= SWITCH_MEDIA_AUTO;
375    if (reg & (1 << 2))
376        media |= SWITCH_MEDIA_100;
377    if (reg & (1 << 3))
378        media |= SWITCH_MEDIA_FD;
379
380    len = switch_print_media(buf, media);
381    return len + sprintf(buf + len, "\n");
382}
383
384static int handle_port_media_write(void *driver, char *buf, int nr)
385{
386    int media = switch_parse_media(buf);
387    int reg = adm_rreg(0, port_conf[nr]);
388
389    if (media < 0)
390        return -1;
391
392    reg &= ~((1 << 1) | (1 << 2) | (1 << 3));
393    if (media & SWITCH_MEDIA_AUTO)
394        reg |= 1 << 1;
395    if (media & SWITCH_MEDIA_100)
396        reg |= 1 << 2;
397    if (media & SWITCH_MEDIA_FD)
398        reg |= 1 << 3;
399
400    adm_wreg(port_conf[nr], reg);
401
402    return 0;
403}
404
405static int handle_vlan_enable_read(void *driver, char *buf, int nr)
406{
407    return sprintf(buf, "%d\n", ((adm_rreg(0, 0x11) & (1 << 5)) ? 1 : 0));
408}
409
410static int handle_vlan_enable_write(void *driver, char *buf, int nr)
411{
412    int reg = adm_rreg(0, 0x11);
413
414    if (buf[0] == '1')
415        reg |= (1 << 5);
416    else if (buf[0] == '0')
417        reg &= ~(1 << 5);
418    else return -1;
419
420    adm_wreg(0x11, (__u16) reg);
421    return 0;
422}
423
424static int handle_reset(void *driver, char *buf, int nr)
425{
426    int i;
427    u32 cfg;
428
429    /*
430     * Reset sequence: RC high->low(100ms)->high(30ms)
431     *
432     * WAR: Certain boards don't have the correct power on
433     * reset logic therefore we must explicitly perform the
434     * sequence in software.
435     */
436    if (eerc) {
437        /* Keep RC high for at least 20ms */
438        adm_enout(eerc, eerc);
439        for (i = 0; i < 20; i ++)
440            udelay(1000);
441        /* Keep RC low for at least 100ms */
442        adm_enout(eerc, 0);
443        for (i = 0; i < 100; i++)
444            udelay(1000);
445        /* Set default configuration */
446        adm_enout((__u8)(eesk | eedi), eesk);
447        /* Keep RC high for at least 30ms */
448        adm_enout(eerc, eerc);
449        for (i = 0; i < 30; i++)
450            udelay(1000);
451        /* Leave RC high and disable GPIO outputs */
452        adm_disout((__u8)(eecs | eesk | eedi));
453
454    }
455
456    /* set up initial configuration for cpu port */
457    cfg = (0x8000 | /* Auto MDIX */
458          (0xf << 10) | /* PVID */
459          (1 << 4) | /* Tagging */
460          0xf); /* full duplex, 100Mbps, auto neg, flow ctrl */
461    adm_wreg(port_conf[5], cfg);
462
463    /* vlan mode select register (0x11): vlan on, mac clone */
464    adm_wreg(0x11, 0xff30);
465
466    return 0;
467}
468
469static int handle_registers(void *driver, char *buf, int nr)
470{
471    int i, len = 0;
472
473    for (i = 0; i <= 0x33; i++) {
474        len += sprintf(buf + len, "0x%02x: 0x%04x\n", i, adm_rreg(0, i));
475    }
476
477    return len;
478}
479
480static int handle_counters(void *driver, char *buf, int nr)
481{
482    int i, len = 0;
483
484    for (i = 0; i <= 0x3c; i++) {
485        len += sprintf(buf + len, "0x%02x: 0x%08x\n", i, adm_rreg(1, i));
486    }
487
488    return len;
489}
490
491static int detect_adm(void)
492{
493    int ret = 0;
494
495#ifdef CONFIG_BCM47XX
496    char buf[20];
497    int boardflags = 0;
498    int boardnum = 0;
499        
500    if (nvram_getenv("boardflags", buf, sizeof(buf)) >= 0)
501        boardflags = simple_strtoul(buf, NULL, 0);
502
503    if (nvram_getenv("boardnum", buf, sizeof(buf)) >= 0)
504        boardnum = simple_strtoul(buf, NULL, 0);
505
506    if ((boardnum == 44) && (boardflags == 0x0388)) { /* Trendware TEW-411BRP+ */
507        ret = 1;
508
509        eecs = get_gpiopin("adm_eecs", 2);
510        eesk = get_gpiopin("adm_eesk", 3);
511        eedi = get_gpiopin("adm_eedi", 4);
512        eerc = get_gpiopin("adm_rc", 5);
513
514    } else if ((boardflags & 0x80) || force) {
515        ret = 1;
516
517        eecs = get_gpiopin("adm_eecs", 2);
518        eesk = get_gpiopin("adm_eesk", 3);
519        eedi = get_gpiopin("adm_eedi", 4);
520        eerc = get_gpiopin("adm_rc", 0);
521
522    } else if (nvram_getenv("boardtype", buf, sizeof(buf)) >= 0) {
523        if (strcmp(buf, "bcm94710dev") == 0) {
524            if (nvram_getenv("boardnum", buf, sizeof(buf)) >= 0) {
525                if (strncmp(buf, "42", 2) == 0) {
526                    /* WRT54G v1.1 hack */
527                    eecs = 2;
528                    eesk = 3;
529                    eedi = 5;
530
531                    ret = 1;
532                }
533            }
534        }
535    }
536
537    if (eecs)
538        eecs = (1 << eecs);
539    if (eesk)
540        eesk = (1 << eesk);
541    if (eedi)
542        eedi = (1 << eedi);
543    if (eerc)
544        eerc = (1 << eerc);
545#else
546    ret = 1;
547#endif
548
549    return ret;
550}
551
552static int __init adm_init(void)
553{
554    switch_config cfg[] = {
555        {"registers", handle_registers, NULL},
556        {"counters", handle_counters, NULL},
557        {"reset", NULL, handle_reset},
558        {"enable_vlan", handle_vlan_enable_read, handle_vlan_enable_write},
559        {NULL, NULL, NULL}
560    };
561    switch_config port[] = {
562        {"enable", handle_port_enable_read, handle_port_enable_write},
563        {"media", handle_port_media_read, handle_port_media_write},
564        {NULL, NULL, NULL}
565    };
566    switch_config vlan[] = {
567        {"ports", handle_vlan_port_read, handle_vlan_port_write},
568        {NULL, NULL, NULL}
569    };
570    switch_driver driver = {
571        name: DRIVER_NAME,
572        version: DRIVER_VERSION,
573        interface: "eth0",
574        ports: 6,
575        cpuport: 5,
576        vlans: 16,
577        driver_handlers: cfg,
578        port_handlers: port,
579        vlan_handlers: vlan,
580    };
581
582    if (!detect_adm())
583        return -ENODEV;
584
585    return switch_register_driver(&driver);
586}
587
588static void __exit adm_exit(void)
589{
590    switch_unregister_driver(DRIVER_NAME);
591}
592
593
594module_init(adm_init);
595module_exit(adm_exit);
596

Archive Download this file



interactive