switch-adm.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. /*
  2. * ADMTEK Adm6996 switch configuration module
  3. *
  4. * Copyright (C) 2005 Felix Fietkau <[email protected]>
  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 <[email protected]>
  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. #include <linux/autoconf.h>
  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. #include "switch-core.h"
  37. #include "gpio.h"
  38. #define DRIVER_NAME "adm6996"
  39. #define DRIVER_VERSION "0.01"
  40. static int eecs = 0;
  41. static int eesk = 0;
  42. static int eedi = 0;
  43. static int eerc = 0;
  44. static int force = 0;
  45. MODULE_AUTHOR("Felix Fietkau <[email protected]>");
  46. MODULE_LICENSE("GPL");
  47. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,52)
  48. module_param(eecs, int, 0);
  49. module_param(eesk, int, 0);
  50. module_param(eedi, int, 0);
  51. module_param(eerc, int, 0);
  52. module_param(force, int, 0);
  53. #else
  54. MODULE_PARM(eecs, "i");
  55. MODULE_PARM(eesk, "i");
  56. MODULE_PARM(eedi, "i");
  57. MODULE_PARM(eerc, "i");
  58. MODULE_PARM(force, "i");
  59. #endif
  60. /* Minimum timing constants */
  61. #define EECK_EDGE_TIME 3 /* 3us - max(adm 2.5us, 93c 1us) */
  62. #define EEDI_SETUP_TIME 1 /* 1us - max(adm 10ns, 93c 400ns) */
  63. #define EECS_SETUP_TIME 1 /* 1us - max(adm no, 93c 200ns) */
  64. /* Handy macros for writing fixed length values */
  65. #define adm_write8(cs, b) { __u8 val = (__u8) (b); adm_write(cs, &val, sizeof(val)*8); }
  66. #define adm_write16(cs, w) { __u16 val = hton16(w); adm_write(cs, (__u8 *)&val, sizeof(val)*8); }
  67. #define adm_write32(cs, i) { uint32 val = hton32(i); adm_write(cs, (__u8 *)&val, sizeof(val)*8); }
  68. #define atoi(str) simple_strtoul(((str != NULL) ? str : ""), NULL, 0)
  69. #ifdef BROADCOM
  70. extern char *nvram_get(char *name);
  71. /* Return gpio pin number assigned to the named pin */
  72. /*
  73. * Variable should be in format:
  74. *
  75. * gpio<N>=pin_name
  76. *
  77. * 'def_pin' is returned if there is no such variable found.
  78. */
  79. static unsigned int get_gpiopin(char *pin_name, unsigned int def_pin)
  80. {
  81. char name[] = "gpioXXXX";
  82. char *val;
  83. unsigned int pin;
  84. /* Go thru all possibilities till a match in pin name */
  85. for (pin = 0; pin < 16; pin ++) {
  86. sprintf(name, "gpio%d", pin);
  87. val = nvram_get(name);
  88. if (val && !strcmp(val, pin_name))
  89. return pin;
  90. }
  91. return def_pin;
  92. }
  93. #endif
  94. static void adm_write(int cs, char *buf, unsigned int bits)
  95. {
  96. int i, len = (bits + 7) / 8;
  97. __u8 mask;
  98. gpio_out(eecs, (cs ? eecs : 0));
  99. udelay(EECK_EDGE_TIME);
  100. /* Byte assemble from MSB to LSB */
  101. for (i = 0; i < len; i++) {
  102. /* Bit bang from MSB to LSB */
  103. for (mask = 0x80; mask && bits > 0; mask >>= 1, bits --) {
  104. /* Clock low */
  105. gpio_out(eesk, 0);
  106. udelay(EECK_EDGE_TIME);
  107. /* Output on rising edge */
  108. gpio_out(eedi, ((mask & buf[i]) ? eedi : 0));
  109. udelay(EEDI_SETUP_TIME);
  110. /* Clock high */
  111. gpio_out(eesk, eesk);
  112. udelay(EECK_EDGE_TIME);
  113. }
  114. }
  115. /* Clock low */
  116. gpio_out(eesk, 0);
  117. udelay(EECK_EDGE_TIME);
  118. if (cs)
  119. gpio_out(eecs, 0);
  120. }
  121. static void adm_read(int cs, char *buf, unsigned int bits)
  122. {
  123. int i, len = (bits + 7) / 8;
  124. __u8 mask;
  125. gpio_out(eecs, (cs ? eecs : 0));
  126. udelay(EECK_EDGE_TIME);
  127. /* Byte assemble from MSB to LSB */
  128. for (i = 0; i < len; i++) {
  129. __u8 byte;
  130. /* Bit bang from MSB to LSB */
  131. for (mask = 0x80, byte = 0; mask && bits > 0; mask >>= 1, bits --) {
  132. __u8 gp;
  133. /* Clock low */
  134. gpio_out(eesk, 0);
  135. udelay(EECK_EDGE_TIME);
  136. /* Input on rising edge */
  137. gp = gpio_in();
  138. if (gp & eedi)
  139. byte |= mask;
  140. /* Clock high */
  141. gpio_out(eesk, eesk);
  142. udelay(EECK_EDGE_TIME);
  143. }
  144. *buf++ = byte;
  145. }
  146. /* Clock low */
  147. gpio_out(eesk, 0);
  148. udelay(EECK_EDGE_TIME);
  149. if (cs)
  150. gpio_out(eecs, 0);
  151. }
  152. /* Enable outputs with specified value to the chip */
  153. static void adm_enout(__u8 pins, __u8 val)
  154. {
  155. /* Prepare GPIO output value */
  156. gpio_out(pins, val);
  157. /* Enable GPIO outputs */
  158. gpio_outen(pins, pins);
  159. udelay(EECK_EDGE_TIME);
  160. }
  161. /* Disable outputs to the chip */
  162. static void adm_disout(__u8 pins)
  163. {
  164. /* Disable GPIO outputs */
  165. gpio_outen(pins, 0);
  166. udelay(EECK_EDGE_TIME);
  167. }
  168. /* Advance clock(s) */
  169. static void adm_adclk(int clocks)
  170. {
  171. int i;
  172. for (i = 0; i < clocks; i++) {
  173. /* Clock high */
  174. gpio_out(eesk, eesk);
  175. udelay(EECK_EDGE_TIME);
  176. /* Clock low */
  177. gpio_out(eesk, 0);
  178. udelay(EECK_EDGE_TIME);
  179. }
  180. }
  181. static __u32 adm_rreg(__u8 table, __u8 addr)
  182. {
  183. /* cmd: 01 10 T DD R RRRRRR */
  184. __u8 bits[6] = {
  185. 0xFF, 0xFF, 0xFF, 0xFF,
  186. (0x06 << 4) | ((table & 0x01) << 3 | (addr&64)>>6),
  187. ((addr&63)<<2)
  188. };
  189. __u8 rbits[4];
  190. /* Enable GPIO outputs with all pins to 0 */
  191. adm_enout((__u8)(eecs | eesk | eedi), 0);
  192. adm_write(0, bits, 46);
  193. adm_disout((__u8)(eedi));
  194. adm_adclk(2);
  195. adm_read (0, rbits, 32);
  196. /* Extra clock(s) required per datasheet */
  197. adm_adclk(2);
  198. /* Disable GPIO outputs */
  199. adm_disout((__u8)(eecs | eesk));
  200. if (!table) /* EEPROM has 16-bit registers, but pumps out two registers in one request */
  201. return (addr & 0x01 ? (rbits[0]<<8) | rbits[1] : (rbits[2]<<8) | (rbits[3]));
  202. else
  203. return (rbits[0]<<24) | (rbits[1]<<16) | (rbits[2]<<8) | rbits[3];
  204. }
  205. /* Write chip configuration register */
  206. /* Follow 93c66 timing and chip's min EEPROM timing requirement */
  207. void
  208. adm_wreg(__u8 addr, __u16 val)
  209. {
  210. /* cmd(27bits): sb(1) + opc(01) + addr(bbbbbbbb) + data(bbbbbbbbbbbbbbbb) */
  211. __u8 bits[4] = {
  212. (0x05 << 5) | (addr >> 3),
  213. (addr << 5) | (__u8)(val >> 11),
  214. (__u8)(val >> 3),
  215. (__u8)(val << 5)
  216. };
  217. /* Enable GPIO outputs with all pins to 0 */
  218. adm_enout((__u8)(eecs | eesk | eedi), 0);
  219. /* Write cmd. Total 27 bits */
  220. adm_write(1, bits, 27);
  221. /* Extra clock(s) required per datasheet */
  222. adm_adclk(2);
  223. /* Disable GPIO outputs */
  224. adm_disout((__u8)(eecs | eesk | eedi));
  225. }
  226. /* Port configuration registers */
  227. static int port_conf[] = { 0x01, 0x03, 0x05, 0x07, 0x08, 0x09 };
  228. /* Bits in VLAN port mapping */
  229. static int vlan_ports[] = { 1 << 0, 1 << 2, 1 << 4, 1 << 6, 1 << 7, 1 << 8 };
  230. static int handle_vlan_port_read(void *driver, char *buf, int nr)
  231. {
  232. int ports, i, c, len = 0;
  233. if ((nr < 0) || (nr > 15))
  234. return 0;
  235. /* Get VLAN port map */
  236. ports = adm_rreg(0, 0x13 + nr);
  237. for (i = 0; i <= 5; i++) {
  238. if (ports & vlan_ports[i]) {
  239. c = adm_rreg(0, port_conf[i]);
  240. len += sprintf(buf + len, "%d", i);
  241. if (c & (1 << 4)) {
  242. buf[len++] = 't';
  243. if (((c & (0xf << 10)) >> 10) == nr)
  244. buf[len++] = '*';
  245. } else if (i == 5)
  246. buf[len++] = 'u';
  247. buf[len++] = '\t';
  248. }
  249. }
  250. len += sprintf(buf + len, "\n");
  251. return len;
  252. }
  253. static int handle_vlan_port_write(void *driver, char *buf, int nr)
  254. {
  255. int i, cfg, ports;
  256. switch_driver *d = (switch_driver *) driver;
  257. switch_vlan_config *c = switch_parse_vlan(d, buf);
  258. if (c == NULL)
  259. return -1;
  260. ports = adm_rreg(0, 0x13 + nr);
  261. for (i = 0; i < d->ports; i++) {
  262. if (c->port & (1 << i)) {
  263. ports |= vlan_ports[i];
  264. cfg = adm_rreg(0, port_conf[i]);
  265. /* Tagging */
  266. if (c->untag & (1 << i))
  267. cfg &= ~(1 << 4);
  268. else
  269. cfg |= (1 << 4);
  270. if ((c->untag | c->pvid) & (1 << i)) {
  271. cfg = (cfg & ~(0xf << 10)) | (nr << 10);
  272. }
  273. adm_wreg(port_conf[i], (__u16) cfg);
  274. } else {
  275. ports &= ~(vlan_ports[i]);
  276. }
  277. }
  278. adm_wreg(0x13 + nr, (__u16) ports);
  279. return 0;
  280. }
  281. static int handle_port_enable_read(void *driver, char *buf, int nr)
  282. {
  283. return sprintf(buf, "%d\n", ((adm_rreg(0, port_conf[nr]) & (1 << 5)) ? 0 : 1));
  284. }
  285. static int handle_port_enable_write(void *driver, char *buf, int nr)
  286. {
  287. int reg = adm_rreg(0, port_conf[nr]);
  288. if (buf[0] == '0')
  289. reg |= (1 << 5);
  290. else if (buf[0] == '1')
  291. reg &= ~(1 << 5);
  292. else return -1;
  293. adm_wreg(port_conf[nr], (__u16) reg);
  294. return 0;
  295. }
  296. static int handle_port_media_read(void *driver, char *buf, int nr)
  297. {
  298. int len;
  299. int media = 0;
  300. int reg = adm_rreg(0, port_conf[nr]);
  301. if (reg & (1 << 1))
  302. media |= SWITCH_MEDIA_AUTO;
  303. if (reg & (1 << 2))
  304. media |= SWITCH_MEDIA_100;
  305. if (reg & (1 << 3))
  306. media |= SWITCH_MEDIA_FD;
  307. len = switch_print_media(buf, media);
  308. return len + sprintf(buf + len, "\n");
  309. }
  310. static int handle_port_media_write(void *driver, char *buf, int nr)
  311. {
  312. int media = switch_parse_media(buf);
  313. int reg = adm_rreg(0, port_conf[nr]);
  314. if (media < 0)
  315. return -1;
  316. reg &= ~((1 << 1) | (1 << 2) | (1 << 3));
  317. if (media & SWITCH_MEDIA_AUTO)
  318. reg |= 1 << 1;
  319. if (media & SWITCH_MEDIA_100)
  320. reg |= 1 << 2;
  321. if (media & SWITCH_MEDIA_FD)
  322. reg |= 1 << 3;
  323. adm_wreg(port_conf[nr], reg);
  324. return 0;
  325. }
  326. static int handle_vlan_enable_read(void *driver, char *buf, int nr)
  327. {
  328. return sprintf(buf, "%d\n", ((adm_rreg(0, 0x11) & (1 << 5)) ? 1 : 0));
  329. }
  330. static int handle_vlan_enable_write(void *driver, char *buf, int nr)
  331. {
  332. int reg = adm_rreg(0, 0x11);
  333. if (buf[0] == '1')
  334. reg |= (1 << 5);
  335. else if (buf[0] == '0')
  336. reg &= ~(1 << 5);
  337. else return -1;
  338. adm_wreg(0x11, (__u16) reg);
  339. return 0;
  340. }
  341. static int handle_reset(void *driver, char *buf, int nr)
  342. {
  343. int i;
  344. u32 cfg;
  345. /*
  346. * Reset sequence: RC high->low(100ms)->high(30ms)
  347. *
  348. * WAR: Certain boards don't have the correct power on
  349. * reset logic therefore we must explicitly perform the
  350. * sequence in software.
  351. */
  352. if (eerc) {
  353. /* Keep RC high for at least 20ms */
  354. adm_enout(eerc, eerc);
  355. for (i = 0; i < 20; i ++)
  356. udelay(1000);
  357. /* Keep RC low for at least 100ms */
  358. adm_enout(eerc, 0);
  359. for (i = 0; i < 100; i++)
  360. udelay(1000);
  361. /* Set default configuration */
  362. adm_enout((__u8)(eesk | eedi), eesk);
  363. /* Keep RC high for at least 30ms */
  364. adm_enout(eerc, eerc);
  365. for (i = 0; i < 30; i++)
  366. udelay(1000);
  367. /* Leave RC high and disable GPIO outputs */
  368. adm_disout((__u8)(eecs | eesk | eedi));
  369. }
  370. /* set up initial configuration for cpu port */
  371. cfg = (0x8000 | /* Auto MDIX */
  372. (0xf << 10) | /* PVID */
  373. (1 << 4) | /* Tagging */
  374. 0xf); /* full duplex, 100Mbps, auto neg, flow ctrl */
  375. adm_wreg(port_conf[5], cfg);
  376. /* vlan mode select register (0x11): vlan on, mac clone */
  377. adm_wreg(0x11, 0xff30);
  378. return 0;
  379. }
  380. static int handle_registers(void *driver, char *buf, int nr)
  381. {
  382. int i, len = 0;
  383. for (i = 0; i <= 0x33; i++) {
  384. len += sprintf(buf + len, "0x%02x: 0x%04x\n", i, adm_rreg(0, i));
  385. }
  386. return len;
  387. }
  388. static int handle_counters(void *driver, char *buf, int nr)
  389. {
  390. int i, len = 0;
  391. for (i = 0; i <= 0x3c; i++) {
  392. len += sprintf(buf + len, "0x%02x: 0x%08x\n", i, adm_rreg(1, i));
  393. }
  394. return len;
  395. }
  396. static int detect_adm(void)
  397. {
  398. int ret = 0;
  399. #ifdef BROADCOM
  400. int boardflags = atoi(nvram_get("boardflags"));
  401. int boardnum = atoi(nvram_get("boardnum"));
  402. if ((boardnum == 44) && (boardflags == 0x0388)) { /* Trendware TEW-411BRP+ */
  403. ret = 1;
  404. eecs = get_gpiopin("adm_eecs", 2);
  405. eesk = get_gpiopin("adm_eesk", 3);
  406. eedi = get_gpiopin("adm_eedi", 4);
  407. eerc = get_gpiopin("adm_rc", 5);
  408. } else if ((boardflags & 0x80) || force) {
  409. ret = 1;
  410. eecs = get_gpiopin("adm_eecs", 2);
  411. eesk = get_gpiopin("adm_eesk", 3);
  412. eedi = get_gpiopin("adm_eedi", 4);
  413. eerc = get_gpiopin("adm_rc", 0);
  414. } else if ((strcmp(nvram_get("boardtype") ?: "", "bcm94710dev") == 0) &&
  415. (strncmp(nvram_get("boardnum") ?: "", "42", 2) == 0)) {
  416. /* WRT54G v1.1 hack */
  417. eecs = 2;
  418. eesk = 3;
  419. eedi = 5;
  420. ret = 1;
  421. }
  422. if (eecs)
  423. eecs = (1 << eecs);
  424. if (eesk)
  425. eesk = (1 << eesk);
  426. if (eedi)
  427. eedi = (1 << eedi);
  428. if (eerc)
  429. eerc = (1 << eerc);
  430. #else
  431. ret = 1;
  432. #endif
  433. return ret;
  434. }
  435. static int __init adm_init(void)
  436. {
  437. switch_config cfg[] = {
  438. {"registers", handle_registers, NULL},
  439. {"counters", handle_counters, NULL},
  440. {"reset", NULL, handle_reset},
  441. {"enable_vlan", handle_vlan_enable_read, handle_vlan_enable_write},
  442. {NULL, NULL, NULL}
  443. };
  444. switch_config port[] = {
  445. {"enable", handle_port_enable_read, handle_port_enable_write},
  446. {"media", handle_port_media_read, handle_port_media_write},
  447. {NULL, NULL, NULL}
  448. };
  449. switch_config vlan[] = {
  450. {"ports", handle_vlan_port_read, handle_vlan_port_write},
  451. {NULL, NULL, NULL}
  452. };
  453. switch_driver driver = {
  454. name: DRIVER_NAME,
  455. version: DRIVER_VERSION,
  456. interface: "eth0",
  457. ports: 6,
  458. cpuport: 5,
  459. vlans: 16,
  460. driver_handlers: cfg,
  461. port_handlers: port,
  462. vlan_handlers: vlan,
  463. };
  464. if (!detect_adm())
  465. return -ENODEV;
  466. return switch_register_driver(&driver);
  467. }
  468. static void __exit adm_exit(void)
  469. {
  470. switch_unregister_driver(DRIVER_NAME);
  471. }
  472. module_init(adm_init);
  473. module_exit(adm_exit);