⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 adm_hw.c

📁 Ethernet switch driver for adm6996L
💻 C
字号:
/* *       adm6996.c  -- ADM6996L linux interface driver. *	Copyright (c) 2004 Nikki Chumakov (nikki@gattaca.ru) */#include <linux/config.h>#include <linux/module.h>#include <linux/types.h>#include <linux/version.h>#include <linux/errno.h>#include <linux/proc_fs.h>#include <asm/system.h>#include <asm/io.h>#include <asm/segment.h>#include <asm/bitops.h>#include <asm/uaccess.h>#include "adm6996.h"#include <typedefs.h>#include <osl.h>#include <sbutils.h>#include <bcmutils.h>#include <bcmendian.h>#if 0#define GPIO0 0#define GPIO1 1#define GPIO2 2#define GPIO3 3#define GPIO4 4#define GPIO5 5#define GPIO6 6#define GPIO7 7#define GPIO8 8#define B_RESET   1<<GPIO0#define B_ECS   1<<GPIO2#define B_ECK   1<<GPIO3#define B_EDO   1<<GPIO4#define B_EDI   1<<GPIO5#define EEDO_PIN  4#define EECS_PIN  2#define EECK_PIN  3#define EEDI_PIN  5#define RESET_PIN 0#endif// #include <et_dbg.h>/* Minimum timing constants */#define EECK_EDGE_TIME	3	/* 3us - max(adm 2.5us, 93c 1us) */#define EEDI_SETUP_TIME	1	/* 1us - max(adm 10ns, 93c 400ns) */#define EECS_SETUP_TIME	1	/* 1us - max(adm no, 93c 200ns) *//* Forward declarations */adm_info_t *adm_attach(void *sbh); // , char *vars);void adm_detach(adm_info_t *adm);void adm_enable_device(adm_info_t *adm, char *vars);int adm_config_vlan(adm_info_t *adm, char *vars);extern uint32 sb_gpioouten(void *sbh, uint32 mask, uint32 val);extern uint32 sb_gpioout(void *sbh, uint32 mask, uint32 val);extern uint32 sb_gpioin(void *sbh);extern uint32 sb_gpiointmask(void *sbh, uint32 mask, uint32 val);#define OUTENMASK B_RESET|B_ECS|B_ECK|B_EDI#define CFGMASK   B_ECS|B_ECK|B_EDI#define BIT(x)  (1 << (x))#define ASSERT(exp)   do {} while (0)#if 0voidconf_gpio(int x){  ASSERT(sbh);  /* Enable all of output pins */  sb_gpioouten(sbh, OUTENMASK, OUTENMASK);  /* We don't want the B_RESET pin changed, unless   * it tries to set the B_RESET pin.   */  if (x & B_RESET)    sb_gpioout(sbh, OUTENMASK, x);  else    sb_gpioout(sbh, CFGMASK, x);}voidgpio_line_set(int x, unsigned int value){  ASSERT(sbh);  if (value == 1)    sb_gpioout(sbh, BIT(x), BIT(x));  else if (value == 0)    sb_gpioout(sbh, BIT(x), 0);}voidgpio_line_get(int x, int *value){  ASSERT(sbh);  *value = (sb_gpioin(sbh) >> x) & 0x1;}voidgpio_line_config_in(int x){  ASSERT(sbh);  sb_gpioouten(sbh, BIT(x), 0);  sb_gpiointmask(sbh, BIT(x), BIT(x));}voidgpio_line_config_out(int x){  ASSERT(sbh);  sb_gpiointmask(sbh, BIT(x), 0);  sb_gpioouten(sbh, BIT(x), BIT(x));}voidgpio_line_config_out_all(void){  ASSERT(sbh);  sb_gpioouten(sbh, OUTENMASK, OUTENMASK);}inline static void SerialDelay(int count){	udelay(count);}static void InitSerialInterface(void){	gpio_line_set(EECK_PIN, 0);	gpio_line_set(EEDI_PIN, 0);}#endif/* Return gpio number assigned to the named pin *//** Variable should be in format:**	gpio<N>=pin_name** 'def_gpio' is returned if there is no such variable is found.*/#define NUMGPIO	16static uint8adm_get_gpio(char *vars, char *pin_name, uint8 def_gpio){	char name[] = "gpioXXXXXXXX";	char *val;	uint8 i;	/* Go thru all possibilities till a match in pin name */	for (i = 0; i < NUMGPIO; i ++) {		sprintf(name, "gpio%d", i);		val = getvar(vars, name);		if (val && !strcmp(val, pin_name))			return (1 << i);	}	return def_gpio;}/* Allocate private resource */adm_info_t *adm_attach(void *sbh) // , char *vars){	adm_info_t *adm;	/* Allocate private data */	if (!(adm = MALLOC(sizeof(adm_info_t)))) {		printk("adm_attach: out of memory");		return NULL;	}	bzero((char *) adm, sizeof(adm_info_t));	adm->sbh = sbh;	/* Init GPIO mapping. Default GPIO: 2, 3, 4 */	adm->eecs = (1 << 2);	adm->eesk = (1 << 3);	adm->eedi = (1 << 5);#if 0	/* nvram overrides */	adm->eecs = adm_get_gpio(vars, "adm_eecs", adm->eecs);	adm->eesk = adm_get_gpio(vars, "adm_eesk", adm->eesk);	adm->eedi = adm_get_gpio(vars, "adm_eedi", adm->eedi);#endif	return adm;}/* Release private resource */voidadm_detach(adm_info_t *adm){	/* Free private data */	MFREE(adm, sizeof(adm_info_t));}/** The following local functions provide chip access control. The * general rules in writing these supporting routines are:**   1. EECS should be kept low after each routine.*   2. EESK should be kept low after each routine.*//* Enable outputs with specified value to the chip */static voidadm_enout(adm_info_t *adm, uint8 pins, uint8 val){	/* Prepare GPIO output value */	sb_gpioout(adm->sbh, pins, val);	/* Enable GPIO outputs */	sb_gpioouten(adm->sbh, pins, pins);	OSL_DELAY(EECK_EDGE_TIME);}/* Disable outputs to the chip */static voidadm_disout(adm_info_t *adm, uint8 pins){	/* Disable GPIO outputs */	sb_gpioouten(adm->sbh, pins, 0);	OSL_DELAY(EECK_EDGE_TIME);}/* Advance clock(s) */static voidadm_adclk(adm_info_t *adm, int clocks){	int i;	for (i = 0; i < clocks; i ++) {		/* Clock high */		sb_gpioout(adm->sbh, adm->eesk, adm->eesk);		OSL_DELAY(EECK_EDGE_TIME);		/* Clock low */		sb_gpioout(adm->sbh, adm->eesk, 0);		OSL_DELAY(EECK_EDGE_TIME);	}}	/* Read a bit stream to the chip */static voidadm_read(adm_info_t *adm, uint8 cs, uint8 *rbuf, uint rbits){	uint i, len = (rbits + 7) / 8;	uint8 mask;	if (cs)		sb_gpioout(adm->sbh, adm->eecs, adm->eecs);		/* read sequence */	/* Byte assemble from MSB to LSB */	for (i = 0; i < len; i++) {		uint8 byte;		/* Bit bang from MSB to LSB */		for (mask = 0x80, byte = 0; mask && rbits > 0; mask >>= 1, rbits --) {			uint8 gp;						/* Clock low */			sb_gpioout(adm->sbh, adm->eesk, 0);			OSL_DELAY(EECK_EDGE_TIME);			gp = sb_gpioin(adm->sbh);						if (gp & adm->eedi)				byte |= mask;							/* Clock high */			sb_gpioout(adm->sbh, adm->eesk, adm->eesk);			OSL_DELAY(EECK_EDGE_TIME);		}		*rbuf++ = byte;	}	/* Clock low */	sb_gpioout(adm->sbh, adm->eesk, 0);	OSL_DELAY(EECK_EDGE_TIME);	/* CS low */	if (cs)		sb_gpioout(adm->sbh, adm->eecs, 0);}/* Write a bit stream to the chip */static voidadm_write(adm_info_t *adm, uint8 cs, uint8 *buf, uint bits){	uint i, len = (bits + 7) / 8;	uint8 mask;	/* CS high/low */	if (cs)		sb_gpioout(adm->sbh, adm->eecs, adm->eecs);	else		sb_gpioout(adm->sbh, adm->eecs, 0);	OSL_DELAY(EECK_EDGE_TIME);	/* Byte assemble from MSB to LSB */	for (i = 0; i < len; i++) {		/* Bit bang from MSB to LSB */		for (mask = 0x80; mask && bits > 0; mask >>= 1, bits --) {			/* Clock low */			sb_gpioout(adm->sbh, adm->eesk, 0);			OSL_DELAY(EECK_EDGE_TIME);			/* Output on rising edge */			if (mask & buf[i])				sb_gpioout(adm->sbh, adm->eedi, adm->eedi);			else				sb_gpioout(adm->sbh, adm->eedi, 0);			OSL_DELAY(EEDI_SETUP_TIME);					/* Clock high */			sb_gpioout(adm->sbh, adm->eesk, adm->eesk);			OSL_DELAY(EECK_EDGE_TIME);		}	}	/* Clock low */	sb_gpioout(adm->sbh, adm->eesk, 0);	OSL_DELAY(EECK_EDGE_TIME);	/* CS low */	if (cs)		sb_gpioout(adm->sbh, adm->eecs, 0);}voidadm_rreg(adm_info_t *adm, uint8 domain, uint8 addr, uint32* val){	/* cmd(27bits): sb(1) + opc(01) + addr(bbbbbbbb) + data(bbbbbbbbbbbbbbbb) */	/* cmd: 01 10 T DD R RRRRRR */	uint8 bits[6] = {		0xFF, 0xFF, 0xFF, 0xFF, 		(0x06 << 4) | ((domain&1)<<3 | ((0&3)<<1) | (addr&64)>>6),		((addr&63)<<2)	};	uint8 rbits[4];	// printk ("adm_rreg: enabling gpio output.. (sbh=%p)\n", adm->sbh);	// printk("adm_rreg: addr %02x domain %01x (%02X%02X)\n", 	//	addr, domain, bits[4], bits[5]);	// printk ("enabling gpio output.. (sbh=%p)\n", adm->sbh);	/* Enable GPIO outputs with all pins to 0 */	adm_enout(adm, (uint8)(adm->eecs | adm->eesk | adm->eedi), 0);	// printk ("adm write 46...\n");	adm_write(adm, 0, bits, 46);	adm_disout (adm, (uint8)(adm->eedi));	adm_adclk(adm, 2);	adm_read (adm, 0, rbits, 32);	/* Extra clock(s) required per datasheet */	adm_adclk(adm, 2);	/* Disable GPIO outputs */	adm_disout(adm, (uint8)(adm->eecs | adm->eesk));	*val = (rbits[0]<<24) | (rbits[1]<<16) | (rbits[2]<<8) | rbits[3];}/* Handy macros for writing fixed length values */#define adm_write8(adm, cs, b) { uint8 val = (uint8) (b); adm_write(adm, cs, &val, sizeof(val)*8); }#define adm_write16(adm, cs, w) { uint16 val = hton16(w); adm_write(adm, cs, (uint8 *)&val, sizeof(val)*8); }#define adm_write32(adm, cs, i) { uint32 val = hton32(i); adm_write(adm, cs, (uint8 *)&val, sizeof(val)*8); }/* Write chip configuration register *//* Follow 93c66 timing and chip's min EEPROM timing requirement */voidadm_wreg(adm_info_t *adm, uint8 addr, uint16 val){	/* cmd(27bits): sb(1) + opc(01) + addr(bbbbbbbb) + data(bbbbbbbbbbbbbbbb) */	uint8 bits[4] = {		(0x05 << 5) | (addr >> 3),		(addr << 5) | (uint8)(val >> 11),		(uint8)(val >> 3),		(uint8)(val << 5)	};	printk("ADM6996: adm_wreg: addr %02x val %04x (%02X%02X%02X%02X)\n", 		addr, val, bits[0], bits[1], bits[2], bits[3]);	/* Enable GPIO outputs with all pins to 0 */	adm_enout(adm, (uint8)(adm->eecs | adm->eesk | adm->eedi), 0);	/* Write cmd. Total 27 bits */	adm_write(adm, 1, bits, 27);	/* Extra clock(s) required per datasheet */	adm_adclk(adm, 2);	/* Disable GPIO outputs */	adm_disout(adm, (uint8)(adm->eecs | adm->eesk | adm->eedi));}/* Copy each token in wordlist delimited by space into word */static int _strspn(const char * p, const char * s){	int i,j;	for (i=0;p[i];i++) {		for (j=0;s[j];j++) {			if (s[j] == p[i]) break;		}		if (!s[j]) break;	}	return(i);}static int _strcspn(char *p, char *s){	int i,j;	for (i=0;p[i];i++) {		for (j=0;s[j];j++) {			if (s[j] == p[i]) break;		}		if (s[j]) break;	}	return(i);}#define foreach(word, wordlist, next) \	for (next = &wordlist[_strspn(wordlist, " ")], \	     strncpy(word, next, sizeof(word)), \	     word[_strcspn(word, " ")] = '\0', \	     word[sizeof(word) - 1] = '\0', \	     next = strchr(next, ' '); \	     strlen(word); \	     next = next ? &next[_strspn(next, " ")] : "", \	     strncpy(word, next, sizeof(word)), \	     word[_strcspn(word, " ")] = '\0', \	     word[sizeof(word) - 1] = '\0', \	     next = strchr(next, ' '))/* Maximum vlan groups */#define MAX_VLAN_GROUPS	16/* Port configuration */typedef struct {	uint8 addr;	/* port configuration register */	uint16 vlan;	/* vlan port mapping */	uint8 tagged;	/* output tagging */	uint8 cpu;	/* cpu port? 1 - yes, 0 - no */	uint16 pvid;	/* cpu port pvid */} port_cfg;/* Vlan port map */typedef struct {	uint8 addr;	/* vlan port map register */} vlan_cfg;/* Configure the chip based on nvram settings */intadm_config_vlan(adm_info_t *adm, char *vars){	port_cfg port_cfg_tab[] = {		{1, 1<<0, 0, 0, -1},		{3, 1<<2, 0, 0, -1},		{5, 1<<4, 0, 0, -1},		{7, 1<<6, 0, 0, -1},		{8, 1<<7, 0, 0, -1},#if defined(PMON) || defined(_CFE_)		{9, 1<<8, 0, 1, -1}	/* no output tagging for pmon/cfe */#else	/* #if defined(PMON) || defined(CFE) */		{9, 1<<8, 1, 1, -1}	/* output tagging for linux... */#endif	/* #if defined(PMON) || defined(CFE) */	};	vlan_cfg vlan_cfg_tab[] = {		{0x13},		{0x14},		{0x15},		{0x16},		{0x17},		{0x18},		{0x19},		{0x1a},		{0x1b},		{0x1c},		{0x1d},		{0x1e},		{0x1f},		{0x20},		{0x21},		{0x22}	};	uint16 i;	/* vlan mode select register (0x11): vlan on, mac clone */	adm_wreg(adm, 0x11, 0xff30);	/* vlan port group: port configuration, vlan port map */	for (i = 0; i < MAX_VLAN_GROUPS; i ++) {		char port[16], *next;		char vlan_group[16];		uint16 vlan_map = 0;		char *ports;		/* get nvram port settings */		sprintf(vlan_group, "vlan%dports", i);		ports = getvar(vars, vlan_group);		if (!ports)			continue;		/*		* port configuration register (0x01, 0x03, 0x05, 0x07, 0x08, 0x09): 		*   input/output tagging, pvid, auto mdix, auto negotiation, ...		* cpu port needs special handing to support pmon/cfe/linux...		*/		foreach (port, ports, next) {			int port_num = bcm_atoi(port);			uint16 port_cfg;						/* make sure port # is within the range */			if (port_num >= sizeof(port_cfg_tab) / sizeof(port_cfg_tab[0])) {				printk("port number %d is out of range\n", port_num);				continue;			}						/* build vlan port map */			vlan_map |= port_cfg_tab[port_num].vlan;						/* cpu port needs special care */			if (port_cfg_tab[port_num].cpu) {				/* check if the port is marked */				if (strchr(port, '*'))					port_cfg_tab[port_num].pvid = i;				/* will be done later */				continue;			}						/* configure port */			port_cfg = 0x8000 |	/* auto mdix */				(i << 10) | 	/* pvid */				0x000f;		/* full duplex, 100Mbps, auto neg, flow ctrl */			adm_wreg(adm, port_cfg_tab[port_num].addr, port_cfg);		}		/* vlan port map register (0x13 - 0x22) */		adm_wreg(adm, vlan_cfg_tab[i].addr, vlan_map);	}		/* cpu port config: auto mdix, pvid, output tagging, ... */	for (i = 0; i < sizeof(port_cfg_tab)/sizeof(port_cfg_tab[0]); i ++) {		uint16 tagged, pvid;		uint16 port_cfg;				/* cpu port only */		if (port_cfg_tab[i].cpu == 0 || port_cfg_tab[i].pvid == 0xffff)			continue;				/* configure port */		tagged = port_cfg_tab[i].tagged ? 1 : 0;		pvid = port_cfg_tab[i].pvid;		port_cfg = 0x8000 |	/* auto mdix */			(pvid << 10) | 	/* pvid */			(tagged << 4) |	/* output tagging */			0x000f;		/* full duplex, 100Mbps, auto neg, flow ctrl */		adm_wreg(adm, port_cfg_tab[i].addr, port_cfg);	}		return 0;}/** Enable the chip with preset default configuration:**  TP Auto MDIX (EESK/GPIO = 1)*  Single Color LED (EEDI/GPIO = 0)*  EEPROM Disable (H/W pull down)*/voidadm_enable_device(adm_info_t *adm, char *vars){	uint8 rc;	int i;		/* Check nvram override existance */	if ((rc = adm_get_gpio(vars, "adm_rc", 0xff)) == 0xff)		return;		/*	* Reset sequence: RC high->low(100ms)->high(30ms)	*	* WAR: Certain boards don't have the correct power on 	* reset logic therefore we must explicitly perform the	* sequece in software.	*/	/* Keep RC high for at least 20ms */	adm_enout(adm, rc, rc);	for (i = 0; i < 20; i ++)		OSL_DELAY(1000);	/* Keep RC low for at least 100ms */	adm_enout(adm, rc, 0);	for (i = 0; i < 100; i++)		OSL_DELAY(1000);	/* Set default configuration */	adm_enout(adm, (uint8)(adm->eesk | adm->eedi), adm->eesk);	/* Keep RC high for at least 30ms */	adm_enout(adm, rc, rc);	for (i = 0; i < 30; i++)		OSL_DELAY(1000);	/* Leave RC high and disable GPIO outputs */	adm_disout(adm, (uint8)(adm->eecs | adm->eesk | adm->eedi));}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -