mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-31 05:54:26 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			1485 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1485 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /******************************************************************************
 | |
|      Copyright (c) 2004, Infineon Technologies.  All rights reserved.
 | |
| 
 | |
|                                No Warranty
 | |
|    Because the program is licensed free of charge, there is no warranty for
 | |
|    the program, to the extent permitted by applicable law.  Except when
 | |
|    otherwise stated in writing the copyright holders and/or other parties
 | |
|    provide the program "as is" without warranty of any kind, either
 | |
|    expressed or implied, including, but not limited to, the implied
 | |
|    warranties of merchantability and fitness for a particular purpose. The
 | |
|    entire risk as to the quality and performance of the program is with
 | |
|    you.  should the program prove defective, you assume the cost of all
 | |
|    necessary servicing, repair or correction.
 | |
| 
 | |
|    In no event unless required by applicable law or agreed to in writing
 | |
|    will any copyright holder, or any other party who may modify and/or
 | |
|    redistribute the program as permitted above, be liable to you for
 | |
|    damages, including any general, special, incidental or consequential
 | |
|    damages arising out of the use or inability to use the program
 | |
|    (including but not limited to loss of data or data being rendered
 | |
|    inaccurate or losses sustained by you or third parties or a failure of
 | |
|    the program to operate with any other programs), even if such holder or
 | |
|    other party has been advised of the possibility of such damages.
 | |
|  ******************************************************************************
 | |
|    Module      : admmod.c
 | |
|    Date        : 2004-09-01
 | |
|    Description : JoeLin
 | |
|    Remarks:
 | |
| 
 | |
|    Revision:
 | |
| 	MarsLin, add to support VLAN
 | |
| 
 | |
|  *****************************************************************************/
 | |
| //000001.joelin 2005/06/02 add"ADM6996_MDC_MDIO_MODE" define, 
 | |
| //		if define ADM6996_MDC_MDIO_MODE==> ADM6996LC and ADM6996I will be in MDIO/MDC(SMI)(16 bit) mode,
 | |
| //		amazon should contrl ADM6996 by MDC/MDIO pin
 | |
| //  		if undef ADM6996_MDC_MDIO_MODE==> ADM6996  will be in EEProm(32 bit) mode,
 | |
| //		amazon should contrl ADM6996 by GPIO15,16,17,18  pin
 | |
| /* 507281:linmars 2005/07/28 support MDIO/EEPROM config mode */
 | |
| /* 509201:linmars remove driver testing codes */
 | |
| 
 | |
| #include <linux/module.h>
 | |
| #include <linux/string.h>
 | |
| #include <linux/proc_fs.h>
 | |
| #include <linux/delay.h>
 | |
| #include <asm/uaccess.h>
 | |
| #include <linux/init.h>
 | |
| #include <linux/ioctl.h>
 | |
| #include <asm/atomic.h>
 | |
| #include <asm-mips/amazon/amazon.h>
 | |
| #include <asm-mips/amazon/adm6996.h>
 | |
| //#include <linux/amazon/adm6996.h>
 | |
| 
 | |
| 
 | |
| unsigned int ifx_sw_conf[ADM_SW_MAX_PORT_NUM+1] = \
 | |
| 	{ADM_SW_PORT0_CONF, ADM_SW_PORT1_CONF, ADM_SW_PORT2_CONF, \
 | |
| 	ADM_SW_PORT3_CONF, ADM_SW_PORT4_CONF, ADM_SW_PORT5_CONF};
 | |
| unsigned int ifx_sw_bits[8] = \
 | |
| 	{0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff};
 | |
| unsigned int ifx_sw_vlan_port[6] = {0, 2, 4, 6, 7, 8};
 | |
| //050613:fchang 
 | |
| /* 507281:linmars start */
 | |
| #ifdef CONFIG_SWITCH_ADM6996_MDIO
 | |
| #define ADM6996_MDC_MDIO_MODE 1 //000001.joelin
 | |
| #else
 | |
| #undef ADM6996_MDC_MDIO_MODE
 | |
| #endif
 | |
| /* 507281:linmars end */
 | |
| #define adm6996i 0
 | |
| #define adm6996lc 1
 | |
| #define adm6996l  2
 | |
| unsigned int adm6996_mode=adm6996i;
 | |
| /*
 | |
|   initialize GPIO pins.
 | |
|   output mode, low
 | |
| */
 | |
| void ifx_gpio_init(void)
 | |
| {
 | |
|  //GPIO16,17,18 direction:output
 | |
|  //GPIO16,17,18 output 0
 | |
|  
 | |
|     AMAZON_SW_REG(AMAZON_GPIO_P1_DIR) |= (GPIO_MDIO|GPIO_MDCS|GPIO_MDC);
 | |
|     AMAZON_SW_REG(AMAZON_GPIO_P1_OUT) =AMAZON_SW_REG(AMAZON_GPIO_P1_IN)& ~(GPIO_MDIO|GPIO_MDCS|GPIO_MDC);
 | |
| 
 | |
| }
 | |
| 
 | |
| /* read one bit from mdio port */
 | |
| int ifx_sw_mdio_readbit(void)
 | |
| {
 | |
|     //int val;
 | |
| 
 | |
|     //val = (AMAZON_SW_REG(GPIO_conf0_REG) & GPIO0_INPUT_MASK) >> 8;
 | |
|     //return val;
 | |
|     //GPIO16
 | |
|     return AMAZON_SW_REG(AMAZON_GPIO_P1_IN)&1;
 | |
| }
 | |
| 
 | |
| /*
 | |
|   MDIO mode selection
 | |
|   1 -> output
 | |
|   0 -> input
 | |
| 
 | |
|   switch input/output mode of GPIO 0
 | |
| */
 | |
| void ifx_mdio_mode(int mode)
 | |
| {
 | |
| //    AMAZON_SW_REG(GPIO_conf0_REG) = mode ? GPIO_ENABLEBITS :
 | |
| //                             ((GPIO_ENABLEBITS | MDIO_INPUT) & ~MDIO_OUTPUT_EN);
 | |
|     mode?(AMAZON_SW_REG(AMAZON_GPIO_P1_DIR)|=GPIO_MDIO):
 | |
|          (AMAZON_SW_REG(AMAZON_GPIO_P1_DIR)&=~GPIO_MDIO);
 | |
|     /*int r=AMAZON_SW_REG(AMAZON_GPIO_P1_DIR);
 | |
|     mode?(r|=GPIO_MDIO):(r&=~GPIO_MDIO);
 | |
|     AMAZON_SW_REG(AMAZON_GPIO_P1_DIR)=r;*/
 | |
| }
 | |
| 
 | |
| void ifx_mdc_hi(void)
 | |
| {
 | |
|     //GPIO_SET_HI(GPIO_MDC);
 | |
|     //AMAZON_SW_REG(AMAZON_GPIO_P1_OUT)|=GPIO_MDC;
 | |
|     /*int r=AMAZON_SW_REG(AMAZON_GPIO_P1_OUT);
 | |
|     r|=GPIO_MDC;
 | |
|     AMAZON_SW_REG(AMAZON_GPIO_P1_OUT)=r;*/
 | |
| 
 | |
|     AMAZON_SW_REG(AMAZON_GPIO_P1_OUT)=AMAZON_SW_REG(AMAZON_GPIO_P1_IN)|GPIO_MDC;
 | |
| }
 | |
| 
 | |
| void ifx_mdio_hi(void)
 | |
| {
 | |
|     //GPIO_SET_HI(GPIO_MDIO);
 | |
|     //AMAZON_SW_REG(AMAZON_GPIO_P1_OUT)|=GPIO_MDIO;
 | |
|     /*int r=AMAZON_SW_REG(AMAZON_GPIO_P1_OUT);
 | |
|     r|=GPIO_MDIO;
 | |
|     AMAZON_SW_REG(AMAZON_GPIO_P1_OUT)=r;*/
 | |
| 
 | |
|     AMAZON_SW_REG(AMAZON_GPIO_P1_OUT)=AMAZON_SW_REG(AMAZON_GPIO_P1_IN)|GPIO_MDIO;
 | |
| }
 | |
| 
 | |
| void ifx_mdcs_hi(void)
 | |
| {
 | |
|     //GPIO_SET_HI(GPIO_MDCS);
 | |
|     //AMAZON_SW_REG(AMAZON_GPIO_P1_OUT)|=GPIO_MDCS;
 | |
|     /*int r=AMAZON_SW_REG(AMAZON_GPIO_P1_OUT);
 | |
|     r|=GPIO_MDCS;
 | |
|     AMAZON_SW_REG(AMAZON_GPIO_P1_OUT)=r;*/
 | |
| 
 | |
|     AMAZON_SW_REG(AMAZON_GPIO_P1_OUT)=AMAZON_SW_REG(AMAZON_GPIO_P1_IN)|GPIO_MDCS;
 | |
| }
 | |
| 
 | |
| void ifx_mdc_lo(void)
 | |
| {
 | |
|     //GPIO_SET_LOW(GPIO_MDC);
 | |
|     //AMAZON_SW_REG(AMAZON_GPIO_P1_OUT)&=~GPIO_MDC;
 | |
|     /*int r=AMAZON_SW_REG(AMAZON_GPIO_P1_OUT);
 | |
|     r&=~GPIO_MDC;
 | |
|     AMAZON_SW_REG(AMAZON_GPIO_P1_OUT)=r;*/
 | |
| 
 | |
|     AMAZON_SW_REG(AMAZON_GPIO_P1_OUT)=AMAZON_SW_REG(AMAZON_GPIO_P1_IN)&(~GPIO_MDC);
 | |
| }
 | |
| 
 | |
| void ifx_mdio_lo(void)
 | |
| {
 | |
|     //GPIO_SET_LOW(GPIO_MDIO);
 | |
|     //AMAZON_SW_REG(AMAZON_GPIO_P1_OUT)&=~GPIO_MDIO;
 | |
|     /*int r=AMAZON_SW_REG(AMAZON_GPIO_P1_OUT);
 | |
|     r&=~GPIO_MDIO;
 | |
|     AMAZON_SW_REG(AMAZON_GPIO_P1_OUT)=r;*/
 | |
| 
 | |
|     AMAZON_SW_REG(AMAZON_GPIO_P1_OUT)=AMAZON_SW_REG(AMAZON_GPIO_P1_IN)&(~GPIO_MDIO);
 | |
| }
 | |
| 
 | |
| void ifx_mdcs_lo(void)
 | |
| {
 | |
|     //GPIO_SET_LOW(GPIO_MDCS);
 | |
|     //AMAZON_SW_REG(AMAZON_GPIO_P1_OUT)&=~GPIO_MDCS;
 | |
|     /*int r=AMAZON_SW_REG(AMAZON_GPIO_P1_OUT);
 | |
|     r&=~GPIO_MDCS;
 | |
|     AMAZON_SW_REG(AMAZON_GPIO_P1_OUT)=r;*/
 | |
|     
 | |
|     AMAZON_SW_REG(AMAZON_GPIO_P1_OUT)=AMAZON_SW_REG(AMAZON_GPIO_P1_IN)&(~GPIO_MDCS);
 | |
| }
 | |
| 
 | |
| /*
 | |
|   mdc pulse
 | |
|   0 -> 1 -> 0
 | |
| */
 | |
| static void ifx_sw_mdc_pulse(void)
 | |
| {
 | |
|     ifx_mdc_lo();
 | |
|     udelay(ADM_SW_MDC_DOWN_DELAY);
 | |
|     ifx_mdc_hi();
 | |
|     udelay(ADM_SW_MDC_UP_DELAY);
 | |
|     ifx_mdc_lo();
 | |
| }
 | |
| 
 | |
| /*
 | |
|   mdc toggle
 | |
|   1 -> 0
 | |
| */
 | |
| static void ifx_sw_mdc_toggle(void)
 | |
| {
 | |
|     ifx_mdc_hi();
 | |
|     udelay(ADM_SW_MDC_UP_DELAY);
 | |
|     ifx_mdc_lo();
 | |
|     udelay(ADM_SW_MDC_DOWN_DELAY);
 | |
| }
 | |
| 
 | |
| /*
 | |
|   enable eeprom write
 | |
|   For ATC 93C66 type EEPROM; accessing ADM6996 internal EEPROM type registers
 | |
| */
 | |
| static void ifx_sw_eeprom_write_enable(void)
 | |
| {
 | |
|     unsigned int op;
 | |
| 
 | |
|     ifx_mdcs_lo();
 | |
|     ifx_mdc_lo();
 | |
|     ifx_mdio_hi();
 | |
|     udelay(ADM_SW_CS_DELAY);
 | |
|     /* enable chip select */
 | |
|     ifx_mdcs_hi();
 | |
|     udelay(ADM_SW_CS_DELAY);
 | |
|     /* start bit */
 | |
|     ifx_mdio_hi();
 | |
|     ifx_sw_mdc_pulse();
 | |
| 
 | |
|     /* eeprom write enable */
 | |
|     op = ADM_SW_BIT_MASK_4;
 | |
|     while (op)
 | |
|     {
 | |
|         if (op & ADM_SW_EEPROM_WRITE_ENABLE)
 | |
|             ifx_mdio_hi();
 | |
|         else
 | |
|             ifx_mdio_lo();
 | |
| 
 | |
|         ifx_sw_mdc_pulse();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     op = ADM_SW_BIT_MASK_1 << (EEPROM_TYPE - 3);
 | |
|     while (op)
 | |
|     {
 | |
|         ifx_mdio_lo();
 | |
|         ifx_sw_mdc_pulse();
 | |
|         op >>= 1;
 | |
|     }
 | |
|     /* disable chip select */
 | |
|     ifx_mdcs_lo();
 | |
|     udelay(ADM_SW_CS_DELAY);
 | |
|     ifx_sw_mdc_pulse();
 | |
| }
 | |
| 
 | |
| /*
 | |
|   disable eeprom write
 | |
| */
 | |
| static void ifx_sw_eeprom_write_disable(void)
 | |
| {
 | |
|     unsigned int op;
 | |
| 
 | |
|     ifx_mdcs_lo();
 | |
|     ifx_mdc_lo();
 | |
|     ifx_mdio_hi();
 | |
|     udelay(ADM_SW_CS_DELAY);
 | |
|     /* enable chip select */
 | |
|     ifx_mdcs_hi();
 | |
|     udelay(ADM_SW_CS_DELAY);
 | |
| 
 | |
|     /* start bit */
 | |
|     ifx_mdio_hi();
 | |
|     ifx_sw_mdc_pulse();
 | |
|     /* eeprom write disable */
 | |
|     op = ADM_SW_BIT_MASK_4;
 | |
|     while (op)
 | |
|     {
 | |
|         if (op & ADM_SW_EEPROM_WRITE_DISABLE)
 | |
|             ifx_mdio_hi();
 | |
|         else
 | |
|             ifx_mdio_lo();
 | |
| 
 | |
|         ifx_sw_mdc_pulse();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     op = ADM_SW_BIT_MASK_1 << (EEPROM_TYPE - 3);
 | |
|     while (op)
 | |
|     {
 | |
|         ifx_mdio_lo();
 | |
| 
 | |
|         ifx_sw_mdc_pulse();
 | |
|         op >>= 1;
 | |
|     }
 | |
|     /* disable chip select */
 | |
|     ifx_mdcs_lo();
 | |
|     udelay(ADM_SW_CS_DELAY);
 | |
|     ifx_sw_mdc_pulse();
 | |
| }
 | |
| 
 | |
| /*
 | |
|   read registers from ADM6996
 | |
|   serial registers start at 0x200 (addr bit 9 = 1b)
 | |
|   EEPROM registers -> 16bits; Serial registers -> 32bits
 | |
| */
 | |
| #ifdef ADM6996_MDC_MDIO_MODE //smi mode//000001.joelin
 | |
| static int ifx_sw_read_adm6996i_smi(unsigned int addr, unsigned int *dat)
 | |
| {
 | |
|    addr=(addr<<16)&0x3ff0000;
 | |
|    AMAZON_SW_REG(AMAZON_SW_MDIO_ACC) =(0xC0000000|addr);
 | |
|    while ((AMAZON_SW_REG(AMAZON_SW_MDIO_ACC))&0x80000000){};
 | |
|    *dat=((AMAZON_SW_REG(AMAZON_SW_MDIO_ACC))&0x0FFFF);
 | |
|     return 0;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static int ifx_sw_read_adm6996i(unsigned int addr, unsigned int *dat)
 | |
| {
 | |
|     unsigned int op;
 | |
| 
 | |
|     ifx_gpio_init();
 | |
| 
 | |
|     ifx_mdcs_hi();
 | |
|     udelay(ADM_SW_CS_DELAY);
 | |
| 
 | |
|     ifx_mdcs_lo();
 | |
|     ifx_mdc_lo();
 | |
|     ifx_mdio_lo();
 | |
| 
 | |
|     udelay(ADM_SW_CS_DELAY);
 | |
| 
 | |
|     /* preamble, 32 bit 1 */
 | |
|     ifx_mdio_hi();
 | |
|     op = ADM_SW_BIT_MASK_32;
 | |
|     while (op)
 | |
|     {
 | |
|         ifx_sw_mdc_pulse();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     /* command start (01b) */
 | |
|     op = ADM_SW_BIT_MASK_2;
 | |
|     while (op)
 | |
|     {
 | |
|         if (op & ADM_SW_SMI_START)
 | |
|             ifx_mdio_hi();
 | |
|         else
 | |
|             ifx_mdio_lo();
 | |
| 
 | |
|         ifx_sw_mdc_pulse();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     /* read command (10b) */
 | |
|     op = ADM_SW_BIT_MASK_2;
 | |
|     while (op)
 | |
|     {
 | |
|         if (op & ADM_SW_SMI_READ)
 | |
|             ifx_mdio_hi();
 | |
|         else
 | |
|             ifx_mdio_lo();
 | |
| 
 | |
|         ifx_sw_mdc_pulse();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     /* send address A9 ~ A0 */
 | |
|     op = ADM_SW_BIT_MASK_10;
 | |
|     while (op)
 | |
|     {
 | |
|         if (op & addr)
 | |
|             ifx_mdio_hi();
 | |
|         else
 | |
|             ifx_mdio_lo();
 | |
| 
 | |
|         ifx_sw_mdc_pulse();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     /* turnaround bits */
 | |
|     op = ADM_SW_BIT_MASK_2;
 | |
|     ifx_mdio_hi();
 | |
|     while (op)
 | |
|     {
 | |
|         ifx_sw_mdc_pulse();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     udelay(ADM_SW_MDC_DOWN_DELAY);
 | |
| 
 | |
|     /* set MDIO pin to input mode */
 | |
|     ifx_mdio_mode(ADM_SW_MDIO_INPUT);
 | |
| 
 | |
|     /* start read data */
 | |
|     *dat = 0;
 | |
| //adm6996i    op = ADM_SW_BIT_MASK_32;
 | |
|     op = ADM_SW_BIT_MASK_16;//adm6996i
 | |
|     while (op)
 | |
|     {
 | |
|         *dat <<= 1;
 | |
|         if (ifx_sw_mdio_readbit()) *dat |= 1;
 | |
|         ifx_sw_mdc_toggle();
 | |
| 
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     /* set MDIO to output mode */
 | |
|     ifx_mdio_mode(ADM_SW_MDIO_OUTPUT);
 | |
| 
 | |
|     /* dummy clock */
 | |
|     op = ADM_SW_BIT_MASK_4;
 | |
|     ifx_mdio_lo();
 | |
|     while(op)
 | |
|     {
 | |
|         ifx_sw_mdc_pulse();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     ifx_mdc_lo();
 | |
|     ifx_mdio_lo();
 | |
|     ifx_mdcs_hi();
 | |
| 
 | |
|     /* EEPROM registers */
 | |
| //adm6996i    if (!(addr & 0x200))
 | |
| //adm6996i    {
 | |
| //adm6996i        if (addr % 2)
 | |
| //adm6996i            *dat >>= 16;
 | |
| //adm6996i        else
 | |
| //adm6996i        *dat &= 0xffff;
 | |
| //adm6996i    }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| //adm6996
 | |
| static int ifx_sw_read_adm6996l(unsigned int addr, unsigned int *dat)
 | |
| {
 | |
|     unsigned int op;
 | |
| 
 | |
|     ifx_gpio_init();
 | |
| 
 | |
|     ifx_mdcs_hi();
 | |
|     udelay(ADM_SW_CS_DELAY);
 | |
| 
 | |
|     ifx_mdcs_lo();
 | |
|     ifx_mdc_lo();
 | |
|     ifx_mdio_lo();
 | |
| 
 | |
|     udelay(ADM_SW_CS_DELAY);
 | |
| 
 | |
|     /* preamble, 32 bit 1 */
 | |
|     ifx_mdio_hi();
 | |
|     op = ADM_SW_BIT_MASK_32;
 | |
|     while (op)
 | |
|     {
 | |
|         ifx_sw_mdc_pulse();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     /* command start (01b) */
 | |
|     op = ADM_SW_BIT_MASK_2;
 | |
|     while (op)
 | |
|     {
 | |
|         if (op & ADM_SW_SMI_START)
 | |
|             ifx_mdio_hi();
 | |
|         else
 | |
|             ifx_mdio_lo();
 | |
| 
 | |
|         ifx_sw_mdc_pulse();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     /* read command (10b) */
 | |
|     op = ADM_SW_BIT_MASK_2;
 | |
|     while (op)
 | |
|     {
 | |
|         if (op & ADM_SW_SMI_READ)
 | |
|             ifx_mdio_hi();
 | |
|         else
 | |
|             ifx_mdio_lo();
 | |
| 
 | |
|         ifx_sw_mdc_pulse();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     /* send address A9 ~ A0 */
 | |
|     op = ADM_SW_BIT_MASK_10;
 | |
|     while (op)
 | |
|     {
 | |
|         if (op & addr)
 | |
|             ifx_mdio_hi();
 | |
|         else
 | |
|             ifx_mdio_lo();
 | |
| 
 | |
|         ifx_sw_mdc_pulse();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     /* turnaround bits */
 | |
|     op = ADM_SW_BIT_MASK_2;
 | |
|     ifx_mdio_hi();
 | |
|     while (op)
 | |
|     {
 | |
|         ifx_sw_mdc_pulse();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     udelay(ADM_SW_MDC_DOWN_DELAY);
 | |
| 
 | |
|     /* set MDIO pin to input mode */
 | |
|     ifx_mdio_mode(ADM_SW_MDIO_INPUT);
 | |
| 
 | |
|     /* start read data */
 | |
|     *dat = 0;
 | |
|     op = ADM_SW_BIT_MASK_32;
 | |
|     while (op)
 | |
|     {
 | |
|         *dat <<= 1;
 | |
|         if (ifx_sw_mdio_readbit()) *dat |= 1;
 | |
|         ifx_sw_mdc_toggle();
 | |
| 
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     /* set MDIO to output mode */
 | |
|     ifx_mdio_mode(ADM_SW_MDIO_OUTPUT);
 | |
| 
 | |
|     /* dummy clock */
 | |
|     op = ADM_SW_BIT_MASK_4;
 | |
|     ifx_mdio_lo();
 | |
|     while(op)
 | |
|     {
 | |
|         ifx_sw_mdc_pulse();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     ifx_mdc_lo();
 | |
|     ifx_mdio_lo();
 | |
|     ifx_mdcs_hi();
 | |
| 
 | |
|     /* EEPROM registers */
 | |
|     if (!(addr & 0x200))
 | |
|     {
 | |
|         if (addr % 2)
 | |
|             *dat >>= 16;
 | |
|         else
 | |
|         *dat &= 0xffff;
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int ifx_sw_read(unsigned int addr, unsigned int *dat)
 | |
| {
 | |
| #ifdef ADM6996_MDC_MDIO_MODE //smi mode ////000001.joelin
 | |
| 	ifx_sw_read_adm6996i_smi(addr,dat);
 | |
| #else	
 | |
| 	if (adm6996_mode==adm6996i) ifx_sw_read_adm6996i(addr,dat);
 | |
| 		else ifx_sw_read_adm6996l(addr,dat);
 | |
| #endif		
 | |
| 	return 0;
 | |
| 	
 | |
| }
 | |
| 
 | |
| /*
 | |
|   write register to ADM6996 eeprom registers
 | |
| */
 | |
| //for adm6996i -start
 | |
| #ifdef ADM6996_MDC_MDIO_MODE //smi mode //000001.joelin
 | |
| static int ifx_sw_write_adm6996i_smi(unsigned int addr, unsigned int dat)
 | |
| {
 | |
|  
 | |
|    AMAZON_SW_REG(AMAZON_SW_MDIO_ACC) = ((addr<<16)&0x3ff0000)|dat|0x80000000;
 | |
|    while ((AMAZON_SW_REG(AMAZON_SW_MDIO_ACC))&0x80000000){};
 | |
|   
 | |
|     return 0;
 | |
|  
 | |
| }
 | |
| #endif //ADM6996_MDC_MDIO_MODE //000001.joelin
 | |
| 
 | |
| static int ifx_sw_write_adm6996i(unsigned int addr, unsigned int dat)
 | |
| {
 | |
|     unsigned int op;
 | |
| 
 | |
|     ifx_gpio_init();
 | |
| 
 | |
|     ifx_mdcs_hi();
 | |
|     udelay(ADM_SW_CS_DELAY);
 | |
| 
 | |
|     ifx_mdcs_lo();
 | |
|     ifx_mdc_lo();
 | |
|     ifx_mdio_lo();
 | |
| 
 | |
|     udelay(ADM_SW_CS_DELAY);
 | |
| 
 | |
|     /* preamble, 32 bit 1 */
 | |
|     ifx_mdio_hi();
 | |
|     op = ADM_SW_BIT_MASK_32;
 | |
|     while (op)
 | |
|     {
 | |
|         ifx_sw_mdc_pulse();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     /* command start (01b) */
 | |
|     op = ADM_SW_BIT_MASK_2;
 | |
|     while (op)
 | |
|     {
 | |
|         if (op & ADM_SW_SMI_START)
 | |
|             ifx_mdio_hi();
 | |
|         else
 | |
|             ifx_mdio_lo();
 | |
| 
 | |
|         ifx_sw_mdc_pulse();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     /* write command (01b) */
 | |
|     op = ADM_SW_BIT_MASK_2;
 | |
|     while (op)
 | |
|     {
 | |
|         if (op & ADM_SW_SMI_WRITE)
 | |
|             ifx_mdio_hi();
 | |
|         else
 | |
|             ifx_mdio_lo();
 | |
| 
 | |
|         ifx_sw_mdc_pulse();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     /* send address A9 ~ A0 */
 | |
|     op = ADM_SW_BIT_MASK_10;
 | |
|     while (op)
 | |
|     {
 | |
|         if (op & addr)
 | |
|             ifx_mdio_hi();
 | |
|         else
 | |
|             ifx_mdio_lo();
 | |
| 
 | |
|         ifx_sw_mdc_pulse();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     /* turnaround bits */
 | |
|     op = ADM_SW_BIT_MASK_2;
 | |
|     ifx_mdio_hi();
 | |
|     while (op)
 | |
|     {
 | |
|         ifx_sw_mdc_pulse();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     udelay(ADM_SW_MDC_DOWN_DELAY);
 | |
| 
 | |
|     /* set MDIO pin to output mode */
 | |
|     ifx_mdio_mode(ADM_SW_MDIO_OUTPUT);
 | |
| 
 | |
|   
 | |
|     /* start write data */
 | |
|     op = ADM_SW_BIT_MASK_16;
 | |
|     while (op)
 | |
|     {
 | |
|         if (op & dat)
 | |
|             ifx_mdio_hi();
 | |
|         else
 | |
|             ifx_mdio_lo();
 | |
| 
 | |
|         ifx_sw_mdc_toggle();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|  //   /* set MDIO to output mode */
 | |
|  //   ifx_mdio_mode(ADM_SW_MDIO_OUTPUT);
 | |
| 
 | |
|     /* dummy clock */
 | |
|     op = ADM_SW_BIT_MASK_4;
 | |
|     ifx_mdio_lo();
 | |
|     while(op)
 | |
|     {
 | |
|         ifx_sw_mdc_pulse();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     ifx_mdc_lo();
 | |
|     ifx_mdio_lo();
 | |
|     ifx_mdcs_hi();
 | |
| 
 | |
|     /* EEPROM registers */
 | |
| //adm6996i    if (!(addr & 0x200))
 | |
| //adm6996i    {
 | |
| //adm6996i        if (addr % 2)
 | |
| //adm6996i            *dat >>= 16;
 | |
| //adm6996i        else
 | |
| //adm6996i        *dat &= 0xffff;
 | |
| //adm6996i    }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| //for adm6996i-end
 | |
| static int ifx_sw_write_adm6996l(unsigned int addr, unsigned int dat)
 | |
| {
 | |
|     unsigned int op;
 | |
| 
 | |
|     ifx_gpio_init();
 | |
| 
 | |
|     /* enable write */
 | |
|     ifx_sw_eeprom_write_enable();
 | |
| 
 | |
|     /* chip select */
 | |
|     ifx_mdcs_hi();
 | |
|     udelay(ADM_SW_CS_DELAY);
 | |
| 
 | |
|     /* issue write command */
 | |
|     /* start bit */
 | |
|     ifx_mdio_hi();
 | |
|     ifx_sw_mdc_pulse();
 | |
| 
 | |
|     /* EEPROM write command */
 | |
|     op = ADM_SW_BIT_MASK_2;
 | |
|     while (op)
 | |
|     {
 | |
|         if (op & ADM_SW_EEPROM_WRITE)
 | |
|             ifx_mdio_hi();
 | |
|         else
 | |
|             ifx_mdio_lo();
 | |
| 
 | |
|         ifx_sw_mdc_pulse();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     /* send address A7 ~ A0 */
 | |
|     op = ADM_SW_BIT_MASK_1 << (EEPROM_TYPE - 1);
 | |
| 
 | |
|     while (op)
 | |
|     {
 | |
|         if (op & addr)
 | |
|             ifx_mdio_hi();
 | |
|         else
 | |
|             ifx_mdio_lo();
 | |
| 
 | |
|         ifx_sw_mdc_toggle();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     /* start write data */
 | |
|     op = ADM_SW_BIT_MASK_16;
 | |
|     while (op)
 | |
|     {
 | |
|         if (op & dat)
 | |
|             ifx_mdio_hi();
 | |
|         else
 | |
|             ifx_mdio_lo();
 | |
| 
 | |
|         ifx_sw_mdc_toggle();
 | |
|         op >>= 1;
 | |
|     }
 | |
| 
 | |
|     /* disable cs & wait 1 clock */
 | |
|     ifx_mdcs_lo();
 | |
|     udelay(ADM_SW_CS_DELAY);
 | |
|     ifx_sw_mdc_toggle();
 | |
| 
 | |
|     ifx_sw_eeprom_write_disable();
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int ifx_sw_write(unsigned int addr, unsigned int dat)
 | |
| {
 | |
| #ifdef ADM6996_MDC_MDIO_MODE //smi mode ////000001.joelin
 | |
| 	ifx_sw_write_adm6996i_smi(addr,dat);
 | |
| #else	//000001.joelin
 | |
| 	if (adm6996_mode==adm6996i) ifx_sw_write_adm6996i(addr,dat);
 | |
| 		else ifx_sw_write_adm6996l(addr,dat);
 | |
| #endif	//000001.joelin
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|   do switch PHY reset
 | |
| */
 | |
| int ifx_sw_reset(void)
 | |
| {
 | |
|     /* reset PHY */
 | |
|     ifx_sw_write(ADM_SW_PHY_RESET, 0);
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* 509201:linmars start */
 | |
| #if 0
 | |
| /*
 | |
|   check port status
 | |
| */
 | |
| int ifx_check_port_status(int port)
 | |
| {
 | |
|     unsigned int val;
 | |
| 
 | |
|     if ((port < 0) || (port > ADM_SW_MAX_PORT_NUM))
 | |
|     {
 | |
|         ifx_printf(("error on port number (%d)!!\n", port));
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     ifx_sw_read(ifx_sw_conf[port], &val);
 | |
|     if (ifx_sw_conf[port]%2) val >>= 16;
 | |
|     /* only 16bits are effective */
 | |
|     val &= 0xFFFF;
 | |
| 
 | |
|     ifx_printf(("Port %d status (%.8x): \n", port, val));
 | |
| 
 | |
|     if (val & ADM_SW_PORT_FLOWCTL)
 | |
|         ifx_printf(("\t802.3x flow control supported!\n"));
 | |
|     else
 | |
|         ifx_printf(("\t802.3x flow control not supported!\n"));
 | |
| 
 | |
|     if (val & ADM_SW_PORT_AN)
 | |
|         ifx_printf(("\tAuto negotiation ON!\n"));
 | |
|     else
 | |
|         ifx_printf(("\tAuto negotiation OFF!\n"));
 | |
| 
 | |
|     if (val & ADM_SW_PORT_100M)
 | |
|         ifx_printf(("\tLink at 100M!\n"));
 | |
|     else
 | |
|         ifx_printf(("\tLink at 10M!\n"));
 | |
| 
 | |
|     if (val & ADM_SW_PORT_FULL)
 | |
|         ifx_printf(("\tFull duplex!\n"));
 | |
|     else
 | |
|         ifx_printf(("\tHalf duplex!\n"));
 | |
| 
 | |
|     if (val & ADM_SW_PORT_DISABLE)
 | |
|         ifx_printf(("\tPort disabled!\n"));
 | |
|     else
 | |
|         ifx_printf(("\tPort enabled!\n"));
 | |
| 
 | |
|     if (val & ADM_SW_PORT_TOS)
 | |
|         ifx_printf(("\tTOS enabled!\n"));
 | |
|     else
 | |
|         ifx_printf(("\tTOS disabled!\n"));
 | |
| 
 | |
|     if (val & ADM_SW_PORT_PPRI)
 | |
|         ifx_printf(("\tPort priority first!\n"));
 | |
|     else
 | |
|         ifx_printf(("\tVLAN or TOS priority first!\n"));
 | |
| 
 | |
|     if (val & ADM_SW_PORT_MDIX)
 | |
|         ifx_printf(("\tAuto MDIX!\n"));
 | |
|     else
 | |
|         ifx_printf(("\tNo auto MDIX\n"));
 | |
| 
 | |
|     ifx_printf(("\tPVID: %d\n", \
 | |
|   	    ((val >> ADM_SW_PORT_PVID_SHIFT)&ifx_sw_bits[ADM_SW_PORT_PVID_BITS])));
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| /*
 | |
|   initialize a VLAN
 | |
|   clear all VLAN bits
 | |
| */
 | |
| int ifx_sw_vlan_init(int vlanid)
 | |
| {
 | |
|     ifx_sw_write(ADM_SW_VLAN0_CONF + vlanid, 0);
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|   add a port to certain vlan
 | |
| */
 | |
| int ifx_sw_vlan_add(int port, int vlanid)
 | |
| {
 | |
|     int reg = 0;
 | |
| 
 | |
|     if ((port < 0) || (port > ADM_SW_MAX_PORT_NUM) || (vlanid < 0) ||
 | |
|         (vlanid > ADM_SW_MAX_VLAN_NUM))
 | |
|     {
 | |
|         ifx_printf(("Port number or VLAN number ERROR!!\n"));
 | |
|         return -1;
 | |
|     }
 | |
|     ifx_sw_read(ADM_SW_VLAN0_CONF + vlanid, ®);
 | |
|     reg |= (1 << ifx_sw_vlan_port[port]);
 | |
|     ifx_sw_write(ADM_SW_VLAN0_CONF + vlanid, reg);
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|   delete a given port from certain vlan
 | |
| */
 | |
| int ifx_sw_vlan_del(int port, int vlanid)
 | |
| {
 | |
|     unsigned int reg = 0;
 | |
| 
 | |
|     if ((port < 0) || (port > ADM_SW_MAX_PORT_NUM) || (vlanid < 0) || (vlanid > ADM_SW_MAX_VLAN_NUM))
 | |
|     {
 | |
|         ifx_printf(("Port number or VLAN number ERROR!!\n"));
 | |
|         return -1;
 | |
|     }
 | |
|     ifx_sw_read(ADM_SW_VLAN0_CONF + vlanid, ®);
 | |
|     reg &= ~(1 << ifx_sw_vlan_port[port]);
 | |
|     ifx_sw_write(ADM_SW_VLAN0_CONF + vlanid, reg);
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|   default VLAN setting
 | |
| 
 | |
|   port 0~3 as untag port and PVID = 1
 | |
|   VLAN1: port 0~3 and port 5 (MII)
 | |
| */
 | |
| static int ifx_sw_init(void)
 | |
| {
 | |
|     ifx_printf(("Setting default ADM6996 registers... \n"));
 | |
| 
 | |
|     /* MAC clone, 802.1q based VLAN */
 | |
|     ifx_sw_write(ADM_SW_VLAN_MODE, 0xff30);
 | |
|     /* auto MDIX, PVID=1, untag */
 | |
|     ifx_sw_write(ADM_SW_PORT0_CONF, 0x840f);
 | |
|     ifx_sw_write(ADM_SW_PORT1_CONF, 0x840f);
 | |
|     ifx_sw_write(ADM_SW_PORT2_CONF, 0x840f);
 | |
|     ifx_sw_write(ADM_SW_PORT3_CONF, 0x840f);
 | |
|     /* auto MDIX, PVID=2, untag */
 | |
|     ifx_sw_write(ADM_SW_PORT5_CONF, 0x880f);
 | |
|     /* port 0~3 & 5 as VLAN1 */
 | |
|     ifx_sw_write(ADM_SW_VLAN0_CONF+1, 0x0155);
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| #endif
 | |
| /* 509201:linmars end */
 | |
| 
 | |
| int adm_open(struct inode *node, struct file *filp)
 | |
| {
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| ssize_t adm_read(struct file *filep, char *buf, size_t count, loff_t *ppos)
 | |
| {
 | |
|     return count;
 | |
| }
 | |
| 
 | |
| ssize_t adm_write(struct file *filep, const char *buf, size_t count, loff_t *ppos)
 | |
| {
 | |
|     return count;
 | |
| }
 | |
| 
 | |
| /* close */
 | |
| int adm_release(struct inode *inode, struct file *filp)
 | |
| {
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* IOCTL function */
 | |
| int adm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long args)
 | |
| {
 | |
|     PREGRW uREGRW;
 | |
|     unsigned int rtval;
 | |
|     unsigned int val;		//6996i
 | |
|     unsigned int control[6] ;	//6996i
 | |
|     unsigned int status[6] ;	//6996i
 | |
|     
 | |
|     PMACENTRY mMACENTRY;//adm6996i
 | |
|     PPROTOCOLFILTER uPROTOCOLFILTER ;///adm6996i
 | |
| 
 | |
|     if (_IOC_TYPE(cmd) != ADM_MAGIC)
 | |
|     {
 | |
|         printk("adm_ioctl: IOC_TYPE(%x) != ADM_MAGIC(%x)! \n", _IOC_TYPE(cmd), ADM_MAGIC);
 | |
|         return (-EINVAL);
 | |
|     }
 | |
| 
 | |
|     if(_IOC_NR(cmd) >= KEY_IOCTL_MAX_KEY)
 | |
|     {
 | |
|         printk(KERN_WARNING "adm_ioctl: IOC_NR(%x) invalid! \n", _IOC_NR(cmd));
 | |
|         return (-EINVAL);
 | |
|     }
 | |
| 
 | |
|     switch (cmd)
 | |
|     {
 | |
|         case ADM_IOCTL_REGRW:
 | |
|         {
 | |
|             uREGRW = (PREGRW)kmalloc(sizeof(REGRW), GFP_KERNEL);
 | |
|             rtval = copy_from_user(uREGRW, (PREGRW)args, sizeof(REGRW));
 | |
|             if (rtval != 0)
 | |
|             {
 | |
|                 printk("ADM_IOCTL_REGRW: copy from user FAILED!! \n");
 | |
|                 return (-EFAULT);
 | |
|             }
 | |
| 
 | |
|             switch(uREGRW->mode)
 | |
|             {
 | |
|                 case REG_READ:
 | |
|                     uREGRW->value = 0x12345678;//inl(uREGRW->addr);
 | |
|                     copy_to_user((PREGRW)args, uREGRW, sizeof(REGRW));
 | |
|                     break;
 | |
|                 case REG_WRITE:
 | |
|                     //outl(uREGRW->value, uREGRW->addr);
 | |
|                     break;
 | |
| 
 | |
|                 default:
 | |
|                     printk("No such Register Read/Write function!! \n");
 | |
|                     return (-EFAULT);
 | |
|             }
 | |
|             kfree(uREGRW);
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         case ADM_SW_IOCTL_REGRW:
 | |
|         {
 | |
|             unsigned int val = 0xff;
 | |
| 
 | |
|             uREGRW = (PREGRW)kmalloc(sizeof(REGRW), GFP_KERNEL);
 | |
|             rtval = copy_from_user(uREGRW, (PREGRW)args, sizeof(REGRW));
 | |
|             if (rtval != 0)
 | |
|             {
 | |
|                 printk("ADM_IOCTL_REGRW: copy from user FAILED!! \n");
 | |
|                 return (-EFAULT);
 | |
|             }
 | |
| 
 | |
|             switch(uREGRW->mode)
 | |
|             {
 | |
|                 case REG_READ:
 | |
|                     ifx_sw_read(uREGRW->addr, &val);
 | |
|                     uREGRW->value = val;
 | |
|                     copy_to_user((PREGRW)args, uREGRW, sizeof(REGRW));
 | |
|                     break;
 | |
| 
 | |
|                 case REG_WRITE:
 | |
|                     ifx_sw_write(uREGRW->addr, uREGRW->value);
 | |
|                     break;
 | |
|                 default:
 | |
|                     printk("No such Register Read/Write function!! \n");
 | |
|                     return (-EFAULT);
 | |
|             }
 | |
|             kfree(uREGRW);
 | |
|             break;
 | |
|         }
 | |
| /* 509201:linmars start */
 | |
| #if 0
 | |
|         case ADM_SW_IOCTL_PORTSTS:
 | |
|             for (rtval = 0; rtval < ADM_SW_MAX_PORT_NUM+1; rtval++)
 | |
|                 ifx_check_port_status(rtval);
 | |
|             break;
 | |
|         case ADM_SW_IOCTL_INIT:
 | |
|             ifx_sw_init();
 | |
|             break;
 | |
| #endif
 | |
| /* 509201:linmars end */
 | |
| //adm6996i
 | |
|         case ADM_SW_IOCTL_MACENTRY_ADD:
 | |
|         case ADM_SW_IOCTL_MACENTRY_DEL:
 | |
|         case ADM_SW_IOCTL_MACENTRY_GET_INIT:
 | |
|         case ADM_SW_IOCTL_MACENTRY_GET_MORE:
 | |
|                 
 | |
| 
 | |
|            mMACENTRY = (PMACENTRY)kmalloc(sizeof(MACENTRY), GFP_KERNEL);
 | |
|             rtval = copy_from_user(mMACENTRY, (PMACENTRY)args, sizeof(MACENTRY));
 | |
|             if (rtval != 0)
 | |
|             {
 | |
|                 printk("ADM_SW_IOCTL_MACENTRY: copy from user FAILED!! \n");
 | |
|                 return (-EFAULT);
 | |
|             }
 | |
|            control[0]=(mMACENTRY->mac_addr[1]<<8)+mMACENTRY->mac_addr[0]     ; 
 | |
|            control[1]=(mMACENTRY->mac_addr[3]<<8)+mMACENTRY->mac_addr[2]      ;         
 | |
|            control[2]=(mMACENTRY->mac_addr[5]<<8)+mMACENTRY->mac_addr[4]     ;
 | |
|            control[3]=(mMACENTRY->fid&0xf)+((mMACENTRY->portmap&0x3f)<<4);
 | |
|            if (((mMACENTRY->info_type)&0x01)) control[4]=(mMACENTRY->ctrl.info_ctrl)+0x1000; //static ,info control
 | |
|            	else	control[4]=((mMACENTRY->ctrl.age_timer)&0xff);//not static ,agetimer
 | |
|          	if (cmd==ADM_SW_IOCTL_MACENTRY_GET_INIT) { 	
 | |
| 	           //initial  the pointer to the first address	
 | |
| 			           val=0x8000;//busy ,status5[15]
 | |
| 			           while(val&0x8000){		//check busy ?
 | |
| 			 	          ifx_sw_read(0x125, &val);
 | |
| 			        	}    
 | |
| 			           control[5]=0x030;//initial the first address	
 | |
| 			           ifx_sw_write(0x11f,control[5]);
 | |
| 			        	       	
 | |
| 			           	
 | |
| 			           val=0x8000;//busy ,status5[15]
 | |
| 			           while(val&0x8000){		//check busy ?
 | |
| 			 	          ifx_sw_read(0x125, &val);
 | |
| 			        	}           	
 | |
| 	           	
 | |
| 	           }	//if (cmd==ADM_SW_IOCTL_MACENTRY_GET_INIT)								
 | |
|            if (cmd==ADM_SW_IOCTL_MACENTRY_ADD) control[5]=0x07;//create a new address
 | |
|            	else if (cmd==ADM_SW_IOCTL_MACENTRY_DEL) control[5]=0x01f;//erased an existed address
 | |
|            	else if ((cmd==ADM_SW_IOCTL_MACENTRY_GET_INIT)||(cmd==ADM_SW_IOCTL_MACENTRY_GET_MORE)) 
 | |
|            		control[5]=0x02c;//search by the mac address field
 | |
|            
 | |
|            val=0x8000;//busy ,status5[15]
 | |
|            while(val&0x8000){		//check busy ?
 | |
|  	          ifx_sw_read(0x125, &val);
 | |
|         	}
 | |
|         	ifx_sw_write(0x11a,control[0]);	
 | |
|         	ifx_sw_write(0x11b,control[1]);	
 | |
|         	ifx_sw_write(0x11c,control[2]);	
 | |
|         	ifx_sw_write(0x11d,control[3]);	
 | |
|         	ifx_sw_write(0x11e,control[4]);	
 | |
|         	ifx_sw_write(0x11f,control[5]);	
 | |
|            val=0x8000;//busy ,status5[15]
 | |
|            while(val&0x8000){		//check busy ?
 | |
|  	          ifx_sw_read(0x125, &val);
 | |
|         	}	
 | |
|            val=((val&0x7000)>>12);//result ,status5[14:12]
 | |
|            mMACENTRY->result=val;
 | |
|    
 | |
|            if (!val) {
 | |
|         		printk(" Command OK!! \n");
 | |
|         		if ((cmd==ADM_SW_IOCTL_MACENTRY_GET_INIT)||(cmd==ADM_SW_IOCTL_MACENTRY_GET_MORE)) {
 | |
| 			           	ifx_sw_read(0x120,&(status[0]));	
 | |
| 			        	ifx_sw_read(0x121,&(status[1]));	
 | |
| 			        	ifx_sw_read(0x122,&(status[2]));	
 | |
| 			        	ifx_sw_read(0x123,&(status[3]));	
 | |
| 			        	ifx_sw_read(0x124,&(status[4]));	
 | |
| 			        	ifx_sw_read(0x125,&(status[5]));	
 | |
| 		
 | |
| 		           		
 | |
| 		        		mMACENTRY->mac_addr[0]=(status[0]&0x00ff)	;
 | |
| 		        		mMACENTRY->mac_addr[1]=(status[0]&0xff00)>>8    ;
 | |
| 		        		mMACENTRY->mac_addr[2]=(status[1]&0x00ff)    ;
 | |
| 		        		mMACENTRY->mac_addr[3]=(status[1]&0xff00)>>8 ;
 | |
| 		        		mMACENTRY->mac_addr[4]=(status[2]&0x00ff)    ;
 | |
| 		        		mMACENTRY->mac_addr[5]=(status[2]&0xff00)>>8 ;
 | |
| 		        		mMACENTRY->fid=(status[3]&0xf);
 | |
| 		        		mMACENTRY->portmap=((status[3]>>4)&0x3f);
 | |
| 		        		if (status[5]&0x2) {//static info_ctrl //status5[1]????
 | |
| 		        			mMACENTRY->ctrl.info_ctrl=(status[4]&0x00ff);
 | |
| 		  				mMACENTRY->info_type=1;
 | |
| 		        				}
 | |
| 		        		else {//not static age_timer
 | |
| 		        			mMACENTRY->ctrl.age_timer=(status[4]&0x00ff);
 | |
| 		  				mMACENTRY->info_type=0;
 | |
| 		        				}
 | |
| //status5[13]????					mMACENTRY->occupy=(status[5]&0x02)>>1;//status5[1]
 | |
| 					mMACENTRY->occupy=(status[5]&0x02000)>>13;//status5[13] ???
 | |
| 					mMACENTRY->bad=(status[5]&0x04)>>2;//status5[2]
 | |
| 				}//if ((cmd==ADM_SW_IOCTL_MACENTRY_GET_INIT)||(cmd==ADM_SW_IOCTL_MACENTRY_GET_MORE)) 
 | |
| 			
 | |
|         	}
 | |
|            else if (val==0x001)  
 | |
|                 printk(" All Entry Used!! \n");
 | |
|             else if (val==0x002) 
 | |
|                 printk("  Entry Not Found!! \n");
 | |
|             else if (val==0x003) 
 | |
|                 printk(" Try Next Entry!! \n");
 | |
|             else if (val==0x005)  
 | |
|                 printk(" Command Error!! \n");   
 | |
|             else   
 | |
|                 printk(" UnKnown Error!! \n");
 | |
|                 
 | |
|             copy_to_user((PMACENTRY)args, mMACENTRY,sizeof(MACENTRY));    
 | |
|                 
 | |
|  	    break;  
 | |
|  
 | |
|         case ADM_SW_IOCTL_FILTER_ADD:
 | |
|         case ADM_SW_IOCTL_FILTER_DEL:
 | |
|         case ADM_SW_IOCTL_FILTER_GET:
 | |
| 
 | |
|             uPROTOCOLFILTER = (PPROTOCOLFILTER)kmalloc(sizeof(PROTOCOLFILTER), GFP_KERNEL);
 | |
|             rtval = copy_from_user(uPROTOCOLFILTER, (PPROTOCOLFILTER)args, sizeof(PROTOCOLFILTER));
 | |
|             if (rtval != 0)
 | |
|             {
 | |
|                 printk("ADM_SW_IOCTL_FILTER_ADD: copy from user FAILED!! \n");
 | |
|                 return (-EFAULT);
 | |
|             }
 | |
|             
 | |
|         	if(cmd==ADM_SW_IOCTL_FILTER_DEL) {	//delete filter
 | |
| 			uPROTOCOLFILTER->ip_p=00;	//delet filter
 | |
| 			uPROTOCOLFILTER->action=00;	//delete filter
 | |
| 		}					//delete filter
 | |
| 
 | |
|             ifx_sw_read(((uPROTOCOLFILTER->protocol_filter_num/2)+0x68), &val);//rx68~rx6b,protocol filter0~7	
 | |
| 
 | |
|             	if (((uPROTOCOLFILTER->protocol_filter_num)%2)==00){	
 | |
|             		if(cmd==ADM_SW_IOCTL_FILTER_GET) uPROTOCOLFILTER->ip_p= val&0x00ff;//get filter ip_p
 | |
|             			else val=(val&0xff00)|(uPROTOCOLFILTER->ip_p);//set filter ip_p
 | |
|         	}
 | |
|         	else {
 | |
|         		if(cmd==ADM_SW_IOCTL_FILTER_GET) uPROTOCOLFILTER->ip_p= (val>>8);//get filter ip_p
 | |
|         			else val=(val&0x00ff)|((uPROTOCOLFILTER->ip_p)<<8);//set filter ip_p
 | |
|         	}	
 | |
|             if(cmd!=ADM_SW_IOCTL_FILTER_GET) ifx_sw_write(((uPROTOCOLFILTER->protocol_filter_num/2)+0x68), val);//write rx68~rx6b,protocol filter0~7	
 | |
|             		
 | |
|             ifx_sw_read(0x95, &val);	//protocol filter action
 | |
|             if(cmd==ADM_SW_IOCTL_FILTER_GET) {
 | |
|             		uPROTOCOLFILTER->action= ((val>>(uPROTOCOLFILTER->protocol_filter_num*2))&0x3);//get filter action
 | |
|             		copy_to_user((PPROTOCOLFILTER)args, uPROTOCOLFILTER, sizeof(PROTOCOLFILTER));
 | |
|             	
 | |
|             	}
 | |
|             	else {
 | |
|             		val=(val&(~(0x03<<(uPROTOCOLFILTER->protocol_filter_num*2))))|(((uPROTOCOLFILTER->action)&0x03)<<(uPROTOCOLFILTER->protocol_filter_num*2));
 | |
|   //          		printk("%d----\n",val);
 | |
|             		ifx_sw_write(0x95, val);	//write protocol filter action		
 | |
|             	}
 | |
|             	
 | |
|             break;
 | |
| //adm6996i  
 | |
| 
 | |
|         /* others */
 | |
|         default:
 | |
|             return -EFAULT;
 | |
|     }
 | |
|     /* end of switch */
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* Santosh: handle IGMP protocol filter ADD/DEL/GET */
 | |
| int adm_process_protocol_filter_request (unsigned int cmd, PPROTOCOLFILTER uPROTOCOLFILTER)
 | |
| {
 | |
|     unsigned int val;		//6996i
 | |
| 
 | |
| 	if(cmd==ADM_SW_IOCTL_FILTER_DEL) {	//delete filter
 | |
| 	uPROTOCOLFILTER->ip_p=00;	//delet filter
 | |
| 	uPROTOCOLFILTER->action=00;	//delete filter
 | |
| 	}					//delete filter
 | |
| 
 | |
|     ifx_sw_read(((uPROTOCOLFILTER->protocol_filter_num/2)+0x68), &val);//rx68~rx6b,protocol filter0~7	
 | |
| 
 | |
|     if (((uPROTOCOLFILTER->protocol_filter_num)%2)==00){	
 | |
|     	if(cmd==ADM_SW_IOCTL_FILTER_GET) uPROTOCOLFILTER->ip_p= val&0x00ff;//get filter ip_p
 | |
|         else val=(val&0xff00)|(uPROTOCOLFILTER->ip_p);//set filter ip_p
 | |
|     }
 | |
|     else {
 | |
|     	if(cmd==ADM_SW_IOCTL_FILTER_GET) uPROTOCOLFILTER->ip_p= (val>>8);//get filter ip_p
 | |
|     	else val=(val&0x00ff)|((uPROTOCOLFILTER->ip_p)<<8);//set filter ip_p
 | |
|     }	
 | |
|     if(cmd!=ADM_SW_IOCTL_FILTER_GET) ifx_sw_write(((uPROTOCOLFILTER->protocol_filter_num/2)+0x68), val);//write rx68~rx6b,protocol filter0~7	
 | |
|             		
 | |
|     	ifx_sw_read(0x95, &val);	//protocol filter action
 | |
|     if(cmd==ADM_SW_IOCTL_FILTER_GET) {
 | |
|        	uPROTOCOLFILTER->action= ((val>>(uPROTOCOLFILTER->protocol_filter_num*2))&0x3);//get filter action
 | |
|     }
 | |
|     else {
 | |
|     	val=(val&(~(0x03<<(uPROTOCOLFILTER->protocol_filter_num*2))))|(((uPROTOCOLFILTER->action)&0x03)<<(uPROTOCOLFILTER->protocol_filter_num*2));
 | |
|         ifx_sw_write(0x95, val);	//write protocol filter action		
 | |
|     }
 | |
|             	
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Santosh: function for MAC ENTRY ADD/DEL/GET */
 | |
| 
 | |
| int adm_process_mac_table_request (unsigned int cmd, PMACENTRY mMACENTRY)
 | |
| {
 | |
|     unsigned int rtval;
 | |
|     unsigned int val;		//6996i
 | |
|     unsigned int control[6] ;	//6996i
 | |
|     unsigned int status[6] ;	//6996i
 | |
| 
 | |
| 	// printk ("adm_process_mac_table_request: enter\n");	
 | |
| 
 | |
|     control[0]=(mMACENTRY->mac_addr[1]<<8)+mMACENTRY->mac_addr[0]     ; 
 | |
|     control[1]=(mMACENTRY->mac_addr[3]<<8)+mMACENTRY->mac_addr[2]      ;         
 | |
|     control[2]=(mMACENTRY->mac_addr[5]<<8)+mMACENTRY->mac_addr[4]     ;
 | |
|     control[3]=(mMACENTRY->fid&0xf)+((mMACENTRY->portmap&0x3f)<<4);
 | |
| 
 | |
|     if (((mMACENTRY->info_type)&0x01)) control[4]=(mMACENTRY->ctrl.info_ctrl)+0x1000; //static ,info control
 | |
|    		else	control[4]=((mMACENTRY->ctrl.age_timer)&0xff);//not static ,agetimer
 | |
|         	if (cmd==ADM_SW_IOCTL_MACENTRY_GET_INIT) { 	
 | |
| 	          //initial  the pointer to the first address	
 | |
| 	           val=0x8000;//busy ,status5[15]
 | |
| 	           while(val&0x8000){		//check busy ?
 | |
| 	           ifx_sw_read(0x125, &val);
 | |
| 	       	}    
 | |
| 	        control[5]=0x030;//initial the first address	
 | |
| 	        ifx_sw_write(0x11f,control[5]);
 | |
| 			        	       	
 | |
| 			           	
 | |
| 			           val=0x8000;//busy ,status5[15]
 | |
| 			           while(val&0x8000){		//check busy ?
 | |
| 			 	          ifx_sw_read(0x125, &val);
 | |
| 			        	}           	
 | |
| 	           	
 | |
| 	           }	//if (cmd==ADM_SW_IOCTL_MACENTRY_GET_INIT)								
 | |
|            if (cmd==ADM_SW_IOCTL_MACENTRY_ADD) control[5]=0x07;//create a new address
 | |
|            	else if (cmd==ADM_SW_IOCTL_MACENTRY_DEL) control[5]=0x01f;//erased an existed address
 | |
|            	else if ((cmd==ADM_SW_IOCTL_MACENTRY_GET_INIT)||(cmd==ADM_SW_IOCTL_MACENTRY_GET_MORE)) 
 | |
|            		control[5]=0x02c;//search by the mac address field
 | |
|            
 | |
|            val=0x8000;//busy ,status5[15]
 | |
|            while(val&0x8000){		//check busy ?
 | |
|  	          ifx_sw_read(0x125, &val);
 | |
|         	}
 | |
|         	ifx_sw_write(0x11a,control[0]);	
 | |
|         	ifx_sw_write(0x11b,control[1]);	
 | |
|         	ifx_sw_write(0x11c,control[2]);	
 | |
|         	ifx_sw_write(0x11d,control[3]);	
 | |
|         	ifx_sw_write(0x11e,control[4]);	
 | |
|         	ifx_sw_write(0x11f,control[5]);	
 | |
|            val=0x8000;//busy ,status5[15]
 | |
|            while(val&0x8000){		//check busy ?
 | |
|  	          ifx_sw_read(0x125, &val);
 | |
|         	}	
 | |
|            val=((val&0x7000)>>12);//result ,status5[14:12]
 | |
|            mMACENTRY->result=val;
 | |
|    
 | |
|            if (!val) {
 | |
|         		printk(" Command OK!! \n");
 | |
|         		if ((cmd==ADM_SW_IOCTL_MACENTRY_GET_INIT)||(cmd==ADM_SW_IOCTL_MACENTRY_GET_MORE)) {
 | |
| 			           	ifx_sw_read(0x120,&(status[0]));	
 | |
| 			        	ifx_sw_read(0x121,&(status[1]));	
 | |
| 			        	ifx_sw_read(0x122,&(status[2]));	
 | |
| 			        	ifx_sw_read(0x123,&(status[3]));	
 | |
| 			        	ifx_sw_read(0x124,&(status[4]));	
 | |
| 			        	ifx_sw_read(0x125,&(status[5]));	
 | |
| 		
 | |
| 		           		
 | |
| 		        		mMACENTRY->mac_addr[0]=(status[0]&0x00ff)	;
 | |
| 		        		mMACENTRY->mac_addr[1]=(status[0]&0xff00)>>8    ;
 | |
| 		        		mMACENTRY->mac_addr[2]=(status[1]&0x00ff)    ;
 | |
| 		        		mMACENTRY->mac_addr[3]=(status[1]&0xff00)>>8 ;
 | |
| 		        		mMACENTRY->mac_addr[4]=(status[2]&0x00ff)    ;
 | |
| 		        		mMACENTRY->mac_addr[5]=(status[2]&0xff00)>>8 ;
 | |
| 		        		mMACENTRY->fid=(status[3]&0xf);
 | |
| 		        		mMACENTRY->portmap=((status[3]>>4)&0x3f);
 | |
| 		        		if (status[5]&0x2) {//static info_ctrl //status5[1]????
 | |
| 		        			mMACENTRY->ctrl.info_ctrl=(status[4]&0x00ff);
 | |
| 		  				mMACENTRY->info_type=1;
 | |
| 		        				}
 | |
| 		        		else {//not static age_timer
 | |
| 		        			mMACENTRY->ctrl.age_timer=(status[4]&0x00ff);
 | |
| 		  				mMACENTRY->info_type=0;
 | |
| 		        				}
 | |
| //status5[13]????					mMACENTRY->occupy=(status[5]&0x02)>>1;//status5[1]
 | |
| 					mMACENTRY->occupy=(status[5]&0x02000)>>13;//status5[13] ???
 | |
| 					mMACENTRY->bad=(status[5]&0x04)>>2;//status5[2]
 | |
| 				}//if ((cmd==ADM_SW_IOCTL_MACENTRY_GET_INIT)||(cmd==ADM_SW_IOCTL_MACENTRY_GET_MORE)) 
 | |
| 			
 | |
|         	}
 | |
|            else if (val==0x001)  
 | |
|                 printk(" All Entry Used!! \n");
 | |
|             else if (val==0x002) 
 | |
|                 printk("  Entry Not Found!! \n");
 | |
|             else if (val==0x003) 
 | |
|                 printk(" Try Next Entry!! \n");
 | |
|             else if (val==0x005)  
 | |
|                 printk(" Command Error!! \n");   
 | |
|             else   
 | |
|                 printk(" UnKnown Error!! \n");
 | |
| 
 | |
| 	// printk ("adm_process_mac_table_request: Exit\n");	
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* Santosh: End of function for MAC ENTRY ADD/DEL*/
 | |
| struct file_operations adm_ops =
 | |
| {
 | |
|     read: adm_read,
 | |
|     write: adm_write,
 | |
|     open: adm_open,
 | |
|     release: adm_release,
 | |
|     ioctl: adm_ioctl
 | |
| };
 | |
| 
 | |
| int adm_proc(char *buf, char **start, off_t offset, int count, int *eof, void *data)
 | |
| {
 | |
|     int len = 0;
 | |
| 
 | |
|     len += sprintf(buf+len, " ************ Registers ************ \n");
 | |
|     *eof = 1;
 | |
|     return len;
 | |
| }
 | |
| 
 | |
| int __init init_adm6996_module(void)
 | |
| {
 | |
|     unsigned int val = 000;
 | |
|     unsigned int val1 = 000;
 | |
| 
 | |
|     printk("Loading ADM6996 driver... \n");
 | |
| 
 | |
|     /* if running on adm5120 */
 | |
|     /* set GPIO 0~2 as adm6996 control pins */
 | |
|     //outl(0x003f3f00, 0x12000028);
 | |
|     /* enable switch port 5 (MII) as RMII mode (5120MAC <-> 6996MAC) */
 | |
|     //outl(0x18a, 0x12000030);
 | |
|     /* group adm5120 port 1 ~ 5 as VLAN0, port 5 & 6(CPU) as VLAN1 */
 | |
|     //outl(0x417e, 0x12000040);
 | |
|     /* end adm5120 fixup */
 | |
| #ifdef ADM6996_MDC_MDIO_MODE //smi mode //000001.joelin
 | |
|     register_chrdev(69, "adm6996", &adm_ops);
 | |
|     AMAZON_SW_REG(AMAZON_SW_MDIO_CFG) = 0x27be;
 | |
|     AMAZON_SW_REG(AMAZON_SW_EPHY) = 0xfc;
 | |
|     adm6996_mode=adm6996i;
 | |
|     ifx_sw_read(0xa0, &val);
 | |
|     ifx_sw_read(0xa1, &val1);
 | |
|     val=((val1&0x0f)<<16)|val;
 | |
|     printk ("\nADM6996 SMI Mode-");
 | |
|     printk ("Chip ID:%5x \n ", val);
 | |
| #else    //000001.joelin
 | |
|  
 | |
|     AMAZON_SW_REG(AMAZON_SW_MDIO_CFG) = 0x2c50;
 | |
|     AMAZON_SW_REG(AMAZON_SW_EPHY) = 0xff;
 | |
| 
 | |
|     AMAZON_SW_REG(AMAZON_GPIO_P1_ALTSEL0) &= ~(GPIO_MDIO|GPIO_MDCS|GPIO_MDC);
 | |
|     AMAZON_SW_REG(AMAZON_GPIO_P1_ALTSEL1) &= ~(GPIO_MDIO|GPIO_MDCS|GPIO_MDC);
 | |
|     AMAZON_SW_REG(AMAZON_GPIO_P1_OD) |= (GPIO_MDIO|GPIO_MDCS|GPIO_MDC);
 | |
|   
 | |
|     ifx_gpio_init();
 | |
|     register_chrdev(69, "adm6996", &adm_ops);
 | |
|     mdelay(100);
 | |
| 
 | |
|     /* create proc entries */
 | |
|     //  create_proc_read_entry("admide", 0, NULL, admide_proc, NULL);
 | |
| 
 | |
| //joelin adm6996i support start
 | |
|     adm6996_mode=adm6996i;
 | |
|     ifx_sw_read(0xa0, &val);
 | |
|     adm6996_mode=adm6996l;
 | |
|     ifx_sw_read(0x200, &val1);
 | |
| //  printk ("\n %0x \n",val1);
 | |
|     if ((val&0xfff0)==0x1020) {
 | |
|         printk ("\n ADM6996I .. \n");
 | |
|         adm6996_mode=adm6996i;	
 | |
|     }
 | |
|     else if ((val1&0xffffff00)==0x71000) {//71010 or 71020
 | |
|         printk ("\n ADM6996LC .. \n");
 | |
|         adm6996_mode=adm6996lc;	
 | |
|     }
 | |
|     else  {
 | |
|         printk ("\n ADM6996L .. \n");
 | |
|         adm6996_mode=adm6996l;	
 | |
|     }
 | |
| #endif //ADM6996_MDC_MDIO_MODE //smi mode //000001.joelin	
 | |
| 
 | |
|     if ((adm6996_mode==adm6996lc)||(adm6996_mode==adm6996i)){
 | |
| #if 0	/* removed by MarsLin */
 | |
|         ifx_sw_write(0x29,0xc000);
 | |
|         ifx_sw_write(0x30,0x0985);
 | |
| #else
 | |
|         ifx_sw_read(0xa0, &val);
 | |
|         if (val == 0x1021) // for both 6996LC and 6996I, only AB version need the patch
 | |
|             ifx_sw_write(0x29, 0x9000);
 | |
|         ifx_sw_write(0x30,0x0985);
 | |
| #endif
 | |
|     }
 | |
| //joelin adm6996i support end
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| void __exit cleanup_adm6996_module(void)
 | |
| {
 | |
|     printk("Free ADM device driver... \n");
 | |
| 
 | |
|     unregister_chrdev(69, "adm6996");
 | |
| 
 | |
|     /* remove proc entries */
 | |
|     //  remove_proc_entry("admide", NULL);
 | |
| }
 | |
| 
 | |
| /* MarsLin, add start */
 | |
| #if defined(CONFIG_IFX_NFEXT_AMAZON_SWITCH_PHYPORT) || defined(CONFIG_IFX_NFEXT_AMAZON_SWITCH_PHYPORT_MODULE)
 | |
|     #define SET_BIT(reg, mask)		reg |= (mask)
 | |
|     #define CLEAR_BIT(reg, mask)	reg &= (~mask)
 | |
|     static int ifx_hw_reset(void)
 | |
|     {
 | |
|         CLEAR_BIT((*AMAZON_GPIO_P0_ALTSEL0),0x2000);
 | |
|         CLEAR_BIT((*AMAZON_GPIO_P0_ALTSEL1),0x2000);
 | |
|         SET_BIT((*AMAZON_GPIO_P0_OD),0x2000);
 | |
|         SET_BIT((*AMAZON_GPIO_P0_DIR), 0x2000);
 | |
| 	CLEAR_BIT((*AMAZON_GPIO_P0_OUT), 0x2000);
 | |
| 	mdelay(500);
 | |
| 	SET_BIT((*AMAZON_GPIO_P0_OUT), 0x2000);
 | |
|         cleanup_adm6996_module();
 | |
|         return init_adm6996_module();
 | |
|     }
 | |
|     int (*adm6996_hw_reset)(void) = ifx_hw_reset;
 | |
|     EXPORT_SYMBOL(adm6996_hw_reset);
 | |
|     EXPORT_SYMBOL(adm6996_mode);
 | |
|     int (*adm6996_sw_read)(unsigned int addr, unsigned int *data) = ifx_sw_read;
 | |
|     EXPORT_SYMBOL(adm6996_sw_read);
 | |
|     int (*adm6996_sw_write)(unsigned int addr, unsigned int data) = ifx_sw_write;
 | |
|     EXPORT_SYMBOL(adm6996_sw_write);
 | |
| #endif
 | |
| /* MarsLin, add end */
 | |
| 
 | |
| /* Santosh: for IGMP proxy/snooping, Begin */
 | |
| EXPORT_SYMBOL (adm_process_mac_table_request);
 | |
| EXPORT_SYMBOL (adm_process_protocol_filter_request);
 | |
| /* Santosh: for IGMP proxy/snooping, End */
 | |
| 	
 | |
| MODULE_DESCRIPTION("ADMtek 6996 Driver");
 | |
| MODULE_AUTHOR("Joe Lin <joe.lin@infineon.com>");
 | |
| MODULE_LICENSE("GPL");
 | |
| 
 | |
| module_init(init_adm6996_module);
 | |
| module_exit(cleanup_adm6996_module);
 | |
| 
 |