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    return 0;
345}
346
347static int handle_port_enable_read(void *driver, char *buf, int nr)
348{
349    return sprintf(buf, "%d\n", ((adm_rreg(0, port_conf[nr]) & (1 << 5)) ? 0 : 1));
350}
351
352static int handle_port_enable_write(void *driver, char *buf, int nr)
353{
354    int reg = adm_rreg(0, port_conf[nr]);
355
356    if (buf[0] == '0')
357        reg |= (1 << 5);
358    else if (buf[0] == '1')
359        reg &= ~(1 << 5);
360    else return -1;
361
362    adm_wreg(port_conf[nr], (__u16) reg);
363    return 0;
364}
365
366static int handle_port_media_read(void *driver, char *buf, int nr)
367{
368    int len;
369    int media = 0;
370    int reg = adm_rreg(0, port_conf[nr]);
371
372    if (reg & (1 << 1))
373        media |= SWITCH_MEDIA_AUTO;
374    if (reg & (1 << 2))
375        media |= SWITCH_MEDIA_100;
376    if (reg & (1 << 3))
377        media |= SWITCH_MEDIA_FD;
378
379    len = switch_print_media(buf, media);
380    return len + sprintf(buf + len, "\n");
381}
382
383static int handle_port_media_write(void *driver, char *buf, int nr)
384{
385    int media = switch_parse_media(buf);
386    int reg = adm_rreg(0, port_conf[nr]);
387
388    if (media < 0)
389        return -1;
390
391    reg &= ~((1 << 1) | (1 << 2) | (1 << 3));
392    if (media & SWITCH_MEDIA_AUTO)
393        reg |= 1 << 1;
394    if (media & SWITCH_MEDIA_100)
395        reg |= 1 << 2;
396    if (media & SWITCH_MEDIA_FD)
397        reg |= 1 << 3;
398
399    adm_wreg(port_conf[nr], reg);
400
401    return 0;
402}
403
404static int handle_vlan_enable_read(void *driver, char *buf, int nr)
405{
406    return sprintf(buf, "%d\n", ((adm_rreg(0, 0x11) & (1 << 5)) ? 1 : 0));
407}
408
409static int handle_vlan_enable_write(void *driver, char *buf, int nr)
410{
411    int reg = adm_rreg(0, 0x11);
412
413    if (buf[0] == '1')
414        reg |= (1 << 5);
415    else if (buf[0] == '0')
416        reg &= ~(1 << 5);
417    else return -1;
418
419    adm_wreg(0x11, (__u16) reg);
420    return 0;
421}
422
423static int handle_reset(void *driver, char *buf, int nr)
424{
425    int i;
426    u32 cfg;
427
428    /*
429     * Reset sequence: RC high->low(100ms)->high(30ms)
430     *
431     * WAR: Certain boards don't have the correct power on
432     * reset logic therefore we must explicitly perform the
433     * sequence in software.
434     */
435    if (eerc) {
436        /* Keep RC high for at least 20ms */
437        adm_enout(eerc, eerc);
438        for (i = 0; i < 20; i ++)
439            udelay(1000);
440        /* Keep RC low for at least 100ms */
441        adm_enout(eerc, 0);
442        for (i = 0; i < 100; i++)
443            udelay(1000);
444        /* Set default configuration */
445        adm_enout((__u8)(eesk | eedi), eesk);
446        /* Keep RC high for at least 30ms */
447        adm_enout(eerc, eerc);
448        for (i = 0; i < 30; i++)
449            udelay(1000);
450        /* Leave RC high and disable GPIO outputs */
451        adm_disout((__u8)(eecs | eesk | eedi));
452
453    }
454
455    /* set up initial configuration for cpu port */
456    cfg = (0x8000 | /* Auto MDIX */
457          (0xf << 10) | /* PVID */
458          (1 << 4) | /* Tagging */
459          0xf); /* full duplex, 100Mbps, auto neg, flow ctrl */
460    adm_wreg(port_conf[5], cfg);
461
462    /* vlan mode select register (0x11): vlan on, mac clone */
463    adm_wreg(0x11, 0xff30);
464
465    return 0;
466}
467
468static int handle_registers(void *driver, char *buf, int nr)
469{
470    int i, len = 0;
471
472    for (i = 0; i <= 0x33; i++) {
473        len += sprintf(buf + len, "0x%02x: 0x%04x\n", i, adm_rreg(0, i));
474    }
475
476    return len;
477}
478
479static int handle_counters(void *driver, char *buf, int nr)
480{
481    int i, len = 0;
482
483    for (i = 0; i <= 0x3c; i++) {
484        len += sprintf(buf + len, "0x%02x: 0x%08x\n", i, adm_rreg(1, i));
485    }
486
487    return len;
488}
489
490static int detect_adm(void)
491{
492    int ret = 0;
493
494#ifdef CONFIG_BCM47XX
495    char buf[20];
496    int boardflags = 0;
497    int boardnum = 0;
498        
499    if (nvram_getenv("boardflags", buf, sizeof(buf)) >= 0)
500        boardflags = simple_strtoul(buf, NULL, 0);
501
502    if (nvram_getenv("boardnum", buf, sizeof(buf)) >= 0)
503        boardnum = simple_strtoul(buf, NULL, 0);
504
505    if ((boardnum == 44) && (boardflags == 0x0388)) { /* Trendware TEW-411BRP+ */
506        ret = 1;
507
508        eecs = get_gpiopin("adm_eecs", 2);
509        eesk = get_gpiopin("adm_eesk", 3);
510        eedi = get_gpiopin("adm_eedi", 4);
511        eerc = get_gpiopin("adm_rc", 5);
512
513    } else if ((boardflags & 0x80) || force) {
514        ret = 1;
515
516        eecs = get_gpiopin("adm_eecs", 2);
517        eesk = get_gpiopin("adm_eesk", 3);
518        eedi = get_gpiopin("adm_eedi", 4);
519        eerc = get_gpiopin("adm_rc", 0);
520
521    } else if (nvram_getenv("boardtype", buf, sizeof(buf)) >= 0) {
522        if (strcmp(buf, "bcm94710dev") == 0) {
523            if (nvram_getenv("boardnum", buf, sizeof(buf)) >= 0) {
524                if (strncmp(buf, "42", 2) == 0) {
525                    /* WRT54G v1.1 hack */
526                    eecs = 2;
527                    eesk = 3;
528                    eedi = 5;
529
530                    ret = 1;
531                }
532            }
533        }
534    }
535
536    if (eecs)
537        eecs = (1 << eecs);
538    if (eesk)
539        eesk = (1 << eesk);
540    if (eedi)
541        eedi = (1 << eedi);
542    if (eerc)
543        eerc = (1 << eerc);
544#else
545    ret = 1;
546#endif
547
548    return ret;
549}
550
551static int __init adm_init(void)
552{
553    switch_config cfg[] = {
554        {"registers", handle_registers, NULL},
555        {"counters", handle_counters, NULL},
556        {"reset", NULL, handle_reset},
557        {"enable_vlan", handle_vlan_enable_read, handle_vlan_enable_write},
558        {NULL, NULL, NULL}
559    };
560    switch_config port[] = {
561        {"enable", handle_port_enable_read, handle_port_enable_write},
562        {"media", handle_port_media_read, handle_port_media_write},
563        {NULL, NULL, NULL}
564    };
565    switch_config vlan[] = {
566        {"ports", handle_vlan_port_read, handle_vlan_port_write},
567        {NULL, NULL, NULL}
568    };
569    switch_driver driver = {
570        name: DRIVER_NAME,
571        version: DRIVER_VERSION,
572        interface: "eth0",
573        ports: 6,
574        cpuport: 5,
575        vlans: 16,
576        driver_handlers: cfg,
577        port_handlers: port,
578        vlan_handlers: vlan,
579    };
580
581    if (!detect_adm())
582        return -ENODEV;
583
584    return switch_register_driver(&driver);
585}
586
587static void __exit adm_exit(void)
588{
589    switch_unregister_driver(DRIVER_NAME);
590}
591
592
593module_init(adm_init);
594module_exit(adm_exit);
595

Archive Download this file



interactive