1/*
2 * ADMtek switch setup functions
3 *
4 * Copyright (C) 2013, Broadcom Corporation. All Rights Reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * $Id: etc_adm.c 286404 2011-09-27 19:29:08Z $
19 */
20
21#include <et_cfg.h>
22#include <typedefs.h>
23#include <osl.h>
24#include <bcmutils.h>
25#include <siutils.h>
26#include <bcmendian.h>
27#include <bcmparams.h>
28#include <etc_adm.h>
29#include <et_dbg.h>
30
31/* Private state per ADM switch */
32struct adm_info_s {
33	si_t *sih;			/* SiliconBackplane handle */
34	void *vars;			/* variables handle */
35	uint coreidx;			/* Current core index */
36	uint32 eecs, eesk, eedi;	/* GPIO mapping */
37};
38
39/* Minimum timing constants */
40#define EECK_EDGE_TIME	3	/* 3us - max(adm 2.5us, 93c 1us) */
41#define EEDI_SETUP_TIME	1	/* 1us - max(adm 10ns, 93c 400ns) */
42#define EECS_SETUP_TIME	1	/* 1us - max(adm no, 93c 200ns) */
43
44/* Allocate private resource */
45adm_info_t *
46adm_attach(si_t *sih, char *vars)
47{
48	adm_info_t *adm;
49	int gpio;
50
51	/* Allocate private data */
52	if (!(adm = MALLOC(si_osh(sih), sizeof(adm_info_t)))) {
53		ET_ERROR(("adm_attach: out of memory, malloc %d bytes", MALLOCED(si_osh(sih))));
54		return NULL;
55	}
56	bzero((char *) adm, sizeof(adm_info_t));
57	adm->sih = sih;
58	adm->vars = vars;
59
60	/* Init GPIO mapping. Default GPIO: 2, 3, 4 */
61	gpio = getgpiopin(vars, "adm_eecs", 2);
62	ET_ERROR(("adm_attach: got %d as adm_eecs", gpio));
63	if (gpio == GPIO_PIN_NOTDEFINED) {
64		ET_ERROR(("adm_attach: adm_eecs gpio fail: GPIO 2 in use"));
65		goto error;
66	}
67	adm->eecs = 1 << gpio;
68
69	gpio = getgpiopin(vars, "adm_eesk", 3);
70	ET_ERROR(("adm_attach: got %d as adm_eesk", gpio));
71	if (gpio == GPIO_PIN_NOTDEFINED) {
72		ET_ERROR(("adm_attach: adm_eesk gpio fail: GPIO 3 in use"));
73		goto error;
74	}
75	adm->eesk = 1 << gpio;
76
77	gpio = getgpiopin(vars, "adm_eedi", 4);
78	ET_ERROR(("adm_attach: got %d as adm_eedi", gpio));
79	if (gpio == GPIO_PIN_NOTDEFINED) {
80		ET_ERROR(("adm_attach: adm_eedi gpio fail: GPIO 4 in use"));
81		goto error;
82	}
83	adm->eedi = 1 << gpio;
84
85	return adm;
86
87error:
88	adm_detach(adm);
89	return NULL;
90}
91
92/* Release private resource */
93void
94adm_detach(adm_info_t *adm)
95{
96	/* Free private data */
97	MFREE(si_osh(adm->sih), adm, sizeof(adm_info_t));
98}
99
100/*
101* The following local functions provide chip access control. The
102* general rules in writing these supporting routines are:
103*
104*   1. EECS should be kept low after each routine.
105*   2. EESK should be kept low after each routine.
106*/
107/* Enable register access to the chip */
108static void
109adm_enable(adm_info_t *adm)
110{
111	void *regs;
112
113	/* Save current core index */
114	adm->coreidx = si_coreidx(adm->sih);
115
116	/* Switch to GPIO core for faster access */
117	regs = si_gpiosetcore(adm->sih);
118	ASSERT(regs);
119}
120
121/* Disable register access to the chip */
122static void
123adm_disable(adm_info_t *adm)
124{
125	/* Switch back to original core */
126	si_setcoreidx(adm->sih, adm->coreidx);
127}
128
129/* Enable outputs with specified value to the chip */
130static void
131adm_enout(adm_info_t *adm, uint32 pins, uint val)
132{
133	/* Prepare GPIO output value */
134	si_gpioout(adm->sih, pins, val, GPIO_DRV_PRIORITY);
135	/* Enable GPIO outputs */
136	si_gpioouten(adm->sih, pins, pins, GPIO_DRV_PRIORITY);
137	OSL_DELAY(EECK_EDGE_TIME);
138}
139
140/* Disable outputs to the chip */
141static void
142adm_disout(adm_info_t *adm, uint32 pins)
143{
144	/* Disable GPIO outputs */
145	si_gpioouten(adm->sih, pins, 0, GPIO_DRV_PRIORITY);
146	OSL_DELAY(EECK_EDGE_TIME);
147}
148
149/* Advance clock(s) */
150static void
151adm_adclk(adm_info_t *adm, int clocks)
152{
153	int i;
154	for (i = 0; i < clocks; i ++) {
155		/* Clock high */
156		si_gpioout(adm->sih, adm->eesk, adm->eesk, GPIO_DRV_PRIORITY);
157		OSL_DELAY(EECK_EDGE_TIME);
158		/* Clock low */
159		si_gpioout(adm->sih, adm->eesk, 0, GPIO_DRV_PRIORITY);
160		OSL_DELAY(EECK_EDGE_TIME);
161	}
162}
163
164/* Write a bit stream to the chip */
165static void
166adm_write(adm_info_t *adm, int cs, uint8 *buf, uint bits)
167{
168	uint i, len = (bits + 7) / 8;
169	uint8 mask;
170
171	/* CS high/low */
172	if (cs)
173		si_gpioout(adm->sih, adm->eecs, adm->eecs, GPIO_DRV_PRIORITY);
174	else
175		si_gpioout(adm->sih, adm->eecs, 0, GPIO_DRV_PRIORITY);
176	OSL_DELAY(EECK_EDGE_TIME);
177
178	/* Byte assemble from MSB to LSB */
179	for (i = 0; i < len; i++) {
180		/* Bit bang from MSB to LSB */
181		for (mask = 0x80; mask && bits > 0; mask >>= 1, bits --) {
182			/* Clock low */
183			si_gpioout(adm->sih, adm->eesk, 0, GPIO_DRV_PRIORITY);
184			OSL_DELAY(EECK_EDGE_TIME);
185
186			/* Output on rising edge */
187			if (mask & buf[i])
188				si_gpioout(adm->sih, adm->eedi, adm->eedi, GPIO_DRV_PRIORITY);
189			else
190				si_gpioout(adm->sih, adm->eedi, 0, GPIO_DRV_PRIORITY);
191			OSL_DELAY(EEDI_SETUP_TIME);
192
193			/* Clock high */
194			si_gpioout(adm->sih, adm->eesk, adm->eesk, GPIO_DRV_PRIORITY);
195			OSL_DELAY(EECK_EDGE_TIME);
196		}
197	}
198
199	/* Clock low */
200	si_gpioout(adm->sih, adm->eesk, 0, GPIO_DRV_PRIORITY);
201	OSL_DELAY(EECK_EDGE_TIME);
202
203	/* CS low */
204	if (cs)
205		si_gpioout(adm->sih, adm->eecs, 0, GPIO_DRV_PRIORITY);
206}
207
208/* Handy macros for writing fixed length values */
209#define adm_write8(adm, cs, b) { uint8 val = (uint8) (b); adm_write(adm, cs, &val, sizeof(val)*8); }
210#define adm_write16(adm, cs, w) { uint16 val = hton16(w); \
211		adm_write(adm, cs, (uint8 *)&val, sizeof(val)*8); }
212#define adm_write32(adm, cs, i) { uint32 val = hton32(i); \
213		adm_write(adm, cs, (uint8 *)&val, sizeof(val)*8); }
214
215/* Write chip configuration register */
216/* Follow 93c66 timing and chip's min EEPROM timing requirement */
217static void
218adm_wreg(adm_info_t *adm, uint8 addr, uint16 val)
219{
220	/* cmd(27bits): sb(1) + opc(01) + addr(bbbbbbbb) + data(bbbbbbbbbbbbbbbb) */
221	uint8 bits[4] = {
222		(0x05 << 5) | (addr >> 3),
223		(addr << 5) | (uint8)(val >> 11),
224		(uint8)(val >> 3),
225		(uint8)(val << 5)
226	};
227
228	ET_TRACE(("adm_wreg: addr %02x val %04x (%02X%02X%02X%02X)\n",
229		addr, val, bits[0], bits[1], bits[2], bits[3]));
230
231	/* Enable GPIO outputs with all pins to 0 */
232	adm_enout(adm, adm->eecs | adm->eesk | adm->eedi, 0);
233
234	/* Write cmd. Total 27 bits */
235	adm_write(adm, 1, bits, 27);
236
237	/* Extra clock(s) required per datasheet */
238	adm_adclk(adm, 2);
239
240	/* Disable GPIO outputs */
241	adm_disout(adm, adm->eecs | adm->eesk | adm->eedi);
242}
243
244/* Configure the chip based on nvram settings */
245int
246adm_config_vlan(adm_info_t *adm)
247{
248	/* Port configuration */
249	struct {
250		uint8 addr;	/* port configuration register */
251		uint16 vlan;	/* vlan port mapping */
252		uint8 tagged;	/* output tagging */
253		uint8 cpu;	/* cpu port? 1 - yes, 0 - no */
254		uint16 pvid;	/* cpu port pvid */
255	} port_cfg_tab[] = {
256		{1, 1<<0, 0, 0, -1},
257		{3, 1<<2, 0, 0, -1},
258		{5, 1<<4, 0, 0, -1},
259		{7, 1<<6, 0, 0, -1},
260		{8, 1<<7, 0, 0, -1},
261#if defined(PMON) || defined(_CFE_)
262		{9, 1<<8, 0, 1, -1}	/* no output tagging for pmon/cfe */
263#else	/* #if defined(PMON) || defined(CFE) */
264		{9, 1<<8, 1, 1, -1}	/* output tagging for linux... */
265#endif	/* #if defined(PMON) || defined(CFE) */
266	};
267	/* Vlan ports bitmap */
268	struct {
269		uint8 addr;	/* vlan port map register */
270	} vlan_cfg_tab[] = {
271		{0x13},
272		{0x14},
273		{0x15},
274		{0x16},
275		{0x17},
276		{0x18},
277		{0x19},
278		{0x1a},
279		{0x1b},
280		{0x1c},
281		{0x1d},
282		{0x1e},
283		{0x1f},
284		{0x20},
285		{0x21},
286		{0x22}
287	};
288	uint16 vid, i;
289
290	/* Enable access to the switch */
291	adm_enable(adm);
292
293	/* vlan mode select register (0x11): vlan on, mac clone */
294	adm_wreg(adm, 0x11, 0xff30);
295
296	/* vlan port group: port configuration, vlan port map */
297	/* VLAN ID is equal to vlan number, max 16 vlans */
298	for (vid = 0; vid < 16; vid ++) {
299		char port[] = "XXXX", *ports, *next, *cur;
300		char vlanports[] = "vlanXXXXports";
301		uint16 vlan_map = 0;
302		int port_num, len;
303		uint16 port_cfg;
304
305		/* no members if VLAN id is out of limitation */
306		if (vid > VLAN_MAXVID)
307			goto vlan_setup;
308
309		/* get nvram port settings */
310		sprintf(vlanports, "vlan%dports", vid);
311		ports = getvar(adm->vars, vlanports);
312
313		/* disable this vlan if not defined */
314		if (!ports)
315			goto vlan_setup;
316
317		/*
318		* port configuration register (0x01, 0x03, 0x05, 0x07, 0x08, 0x09):
319		*   input/output tagging, pvid, auto mdix, auto negotiation, ...
320		* cpu port needs special handing to support pmon/cfe/linux...
321		*/
322		for (cur = ports; cur; cur = next) {
323			/* tokenize the port list */
324			while (*cur == ' ')
325				cur ++;
326			next = bcmstrstr(cur, " ");
327			len = next ? next - cur : strlen(cur);
328			if (!len)
329				break;
330			if (len > sizeof(port) - 1)
331				len = sizeof(port) - 1;
332			strncpy(port, cur, len);
333			port[len] = 0;
334
335			/* make sure port # is within the range */
336			port_num = bcm_atoi(port);
337			if (port_num >= sizeof(port_cfg_tab) / sizeof(port_cfg_tab[0])) {
338				ET_ERROR(("port number %d is out of range\n", port_num));
339				continue;
340			}
341
342			/* build vlan port map */
343			vlan_map |= port_cfg_tab[port_num].vlan;
344
345			/* cpu port needs special care */
346			if (port_cfg_tab[port_num].cpu) {
347				/* cpu port's default VLAN is lan! */
348				if (strchr(port, '*'))
349					port_cfg_tab[port_num].pvid = vid;
350				/* will be done later */
351				continue;
352			}
353
354			/* configure port */
355			port_cfg = 0x8000 |	/* auto mdix */
356				(vid << 10) | 	/* pvid */
357				0x000f;		/* full duplex, 100Mbps, auto neg, flow ctrl */
358			adm_wreg(adm, port_cfg_tab[port_num].addr, port_cfg);
359		}
360vlan_setup:
361		/* vlan port map register (0x13 - 0x22) */
362		adm_wreg(adm, vlan_cfg_tab[vid].addr, vlan_map);
363	}
364
365	/* cpu port config: auto mdix, pvid, output tagging, ... */
366	for (i = 0; i < sizeof(port_cfg_tab)/sizeof(port_cfg_tab[0]); i ++) {
367		uint16 tagged, pvid;
368		uint16 port_cfg;
369
370		/* cpu port only */
371		if (port_cfg_tab[i].cpu == 0 || port_cfg_tab[i].pvid == 0xffff)
372			continue;
373
374		/* configure port */
375		tagged = port_cfg_tab[i].tagged ? 1 : 0;
376		pvid = port_cfg_tab[i].pvid;
377		port_cfg = 0x8000 |	/* auto mdix */
378			(pvid << 10) | 	/* pvid */
379			(tagged << 4) |	/* output tagging */
380			0x000f;		/* full duplex, 100Mbps, auto neg, flow ctrl */
381		adm_wreg(adm, port_cfg_tab[i].addr, port_cfg);
382	}
383
384	/* Disable access to the switch */
385	adm_disable(adm);
386
387	return 0;
388}
389
390/*
391* Enable the chip with preset default configuration:
392*
393*  TP Auto MDIX (EESK/GPIO = 1)
394*  Single Color LED (EEDI/GPIO = 0)
395*  EEPROM Disable (H/W pull down)
396*/
397int
398adm_enable_device(adm_info_t *adm)
399{
400	uint32 rc;
401	int i;
402
403	/* Check nvram override existance */
404	if ((rc = getgpiopin(adm->vars, "adm_rc", GPIO_PIN_NOTDEFINED)) == GPIO_PIN_NOTDEFINED)
405		return 0;
406	rc = 1 << rc;
407
408	/* Enable access to the switch */
409	adm_enable(adm);
410	/*
411	* Reset sequence: RC high->low(100ms)->high(30ms)
412	*
413	* WAR: Certain boards don't have the correct power on
414	* reset logic therefore we must explicitly perform the
415	* sequece in software.
416	*/
417	/* Keep RC high for at least 20ms */
418	adm_enout(adm, rc, rc);
419	for (i = 0; i < 20; i ++)
420		OSL_DELAY(1000);
421	/* Keep RC low for at least 100ms */
422	adm_enout(adm, rc, 0);
423	for (i = 0; i < 100; i++)
424		OSL_DELAY(1000);
425	/* Set default configuration */
426	adm_enout(adm, adm->eesk | adm->eedi, adm->eesk);
427	/* Keep RC high for at least 30ms */
428	adm_enout(adm, rc, rc);
429	for (i = 0; i < 30; i++)
430		OSL_DELAY(1000);
431	/* Leave RC high and disable GPIO outputs */
432	adm_disout(adm, adm->eecs | adm->eesk | adm->eedi);
433	/* Disable access to the switch */
434	adm_disable(adm);
435	return 0;
436}
437
438#ifdef BCMDBG
439/* Read a bit stream from the chip */
440static void
441adm_read(adm_info_t *adm, int cs, uint32 pin, uint8 *buf, uint bits)
442{
443	uint i, len = (bits + 7) / 8;
444
445	/* CS high/low */
446	if (cs)
447		si_gpioout(adm->sih, adm->eecs, adm->eecs, GPIO_DRV_PRIORITY);
448	else
449		si_gpioout(adm->sih, adm->eecs, 0, GPIO_DRV_PRIORITY);
450	OSL_DELAY(EECK_EDGE_TIME);
451
452	/* Byte assemble from MSB to LSB */
453	for (i = 0; i < len; i ++) {
454		uint8 mask, byte = 0;
455		/* Bit bang from MSB to LSB */
456		for (mask = 0x80; mask && bits > 0; mask >>= 1, bits --) {
457			/* Clock high */
458			si_gpioout(adm->sih, adm->eesk, adm->eesk, GPIO_DRV_PRIORITY);
459			OSL_DELAY(EECK_EDGE_TIME);
460
461			/* Sample on rising edge */
462			if (si_gpioin(adm->sih) & pin)
463				byte |= mask;
464
465			/* Clock low */
466			si_gpioout(adm->sih, adm->eesk, 0, GPIO_DRV_PRIORITY);
467			OSL_DELAY(EECK_EDGE_TIME);
468		}
469		buf[i] = byte;
470	}
471
472	/* CS low */
473	if (cs)
474		si_gpioout(adm->sih, adm->eecs, 0, GPIO_DRV_PRIORITY);
475}
476
477/* Handy macros for reading fixed length values */
478#define adm_read8(adm, cs, pin, b) { uint8 val; \
479		adm_read(adm, cs, pin, &val, sizeof(val) * 8); *(b) = val; }
480#define adm_read16(adm, cs, pin, w) { uint16 val; \
481		adm_read(adm, cs, pin, (uint8 *)&val, sizeof(val) * 8); *(w) = ntoh16(val); }
482#define adm_read32(adm, cs, pin, i) { uint32 val; \
483		adm_read(adm, cs, pin, (uint8 *)&val, sizeof(val) * 8); *(i) = ntoh32(val); }
484
485/* Read counter/config register. table 0 - config registers, 1 - internal counters */
486/* Follow chip's SMI timing */
487static uint32
488adm_rreg(adm_info_t *adm, int addr, int table)
489{
490	/* Command: preamble(11) + start(01) + opcode(10) + table(b) +
491	* device(00) + register(bbbbbbb)
492	*/
493	uint16 cmd = (3 << 14) | (1 << 12) | (2 << 10) | (table << 9) | (0 << 7) | addr;
494	uint32 data;
495
496	/* Enable GPIO outputs */
497	adm_enout(adm, adm->eecs | adm->eesk | adm->eedi, 0);
498
499	/* Preamble: at lease 32 bit 1s */
500	adm_write32(adm, 0, 0xffffffff);
501
502	/* Command: 2 extra preamble bits plus 14 command bits */
503	adm_write16(adm, 0, cmd);
504
505	/* Z EEDI: the switch will drive it */
506	adm_disout(adm, adm->eedi);
507
508	/* Turn around: 1 bit */
509	adm_adclk(adm, 1);
510
511	/* Register value: 32 bits */
512	adm_read32(adm, 0, adm->eedi, &data);
513
514	/* Idle: at least 1 extra clock */
515	adm_adclk(adm, 2);
516
517	/* Disable GPIO outputs */
518	adm_disout(adm, adm->eecs | adm->eesk);
519
520	return data;
521}
522
523char*
524adm_dump_regs(adm_info_t *adm, char *buf)
525{
526	uint32 v;
527	int i;
528
529	/* enable access to the switch */
530	adm_enable(adm);
531
532	/* dump admtek switch registers */
533	buf += sprintf(buf, "admtek:\n");
534	for (i = 0; i <= 0x2d; i++) {
535		v = adm_rreg(adm, i, 0);
536		buf += sprintf(buf, "%04x ",
537			((i % 2) ? ((v >> 16) & 0xffff) :  (v & 0xffff)));
538		if ((i % 8) == 7)
539			buf += sprintf(buf, "\n");
540	}
541	buf += sprintf(buf, "\n");
542
543	/* disable access to the switch */
544	adm_disable(adm);
545
546	return (buf);
547}
548#endif /* BCMDBG */
549