From 5deb3317cb51ac52de922bb55f8492624018906d Mon Sep 17 00:00:00 2001 From: Roman Yeryomin Date: Thu, 13 Sep 2012 00:40:35 +0300 Subject: Add realtek target files Signed-off-by: Roman Yeryomin --- .../realtek/files/drivers/char/rtl_mdio/Makefile | 2 + .../realtek/files/drivers/char/rtl_mdio/rtl_mdio.c | 1316 ++++++++++++++++++++ .../realtek/files/drivers/char/rtl_mdio/rtl_mdio.h | 309 +++++ 3 files changed, 1627 insertions(+) create mode 100644 target/linux/realtek/files/drivers/char/rtl_mdio/Makefile create mode 100644 target/linux/realtek/files/drivers/char/rtl_mdio/rtl_mdio.c create mode 100644 target/linux/realtek/files/drivers/char/rtl_mdio/rtl_mdio.h (limited to 'target/linux/realtek/files/drivers/char/rtl_mdio') diff --git a/target/linux/realtek/files/drivers/char/rtl_mdio/Makefile b/target/linux/realtek/files/drivers/char/rtl_mdio/Makefile new file mode 100644 index 000000000..18b38e500 --- /dev/null +++ b/target/linux/realtek/files/drivers/char/rtl_mdio/Makefile @@ -0,0 +1,2 @@ + +obj-y += rtl_mdio.o diff --git a/target/linux/realtek/files/drivers/char/rtl_mdio/rtl_mdio.c b/target/linux/realtek/files/drivers/char/rtl_mdio/rtl_mdio.c new file mode 100644 index 000000000..85cc3ffda --- /dev/null +++ b/target/linux/realtek/files/drivers/char/rtl_mdio/rtl_mdio.c @@ -0,0 +1,1316 @@ +/* +################################################################################ +# +# RTL8198 MDIO char driver +# +# Copyright(c) 2010 Realtek Semiconductor Corp. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation; either version 2 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, see . +# +# Author: +# Realtek WiFi AP software team +# No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan +# +################################################################################ +*/ +/*================================================================*/ +/* System Include Files */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bspchip.h" + + +/*================================================================*/ +/* Local Include Files */ + +#include "rtl_mdio.h" + +//#define CONFIG_RTL8197B_B_CUT_PATCH +/*================================================================*/ +/* Global Variables */ + +int cpu_suspend_enabled = 0; + + +/*================================================================*/ +/* Local Variables */ +static struct mdio_priv *dev_priv=NULL; + +#ifdef SIMULATION +static unsigned char data_in[256]; +static int data_in_len = 0, data_in_read_idx = 0; + +static unsigned short data_out; +static int data_out_len = 0, msg_is_fetched = 0; +static unsigned long reg_scr =0, reg_isr=0; +#endif + + + + +/*================================================================*/ +#if 0 +int pre_jiffies=1; + +void* MAX_FUNCT; +unsigned int MAX_STACK=8*1024; +unsigned int PRINT_STACK=2428; +unsigned int COPY_STACK=2428; + +unsigned int MAX_FUNCT_call[40]; + +#define PRINT_INTV (1*60*100) +/* print max sp ever 3min */ +//extern volatile int jiffies; + + +void __attribute__((__no_instrument_function__)) +__cyg_profile_func_enter(void *this_func, void *call_site) +{ + unsigned int sp_addr; + unsigned int sp_size; + +#if 0 + if (this_func == rtk_voip_dsp_init) + { + printk("rtk_voip_dsp_init enter\n"); + } +#endif + + __asm__ __volatile__("ori %0, $29, 0": "=r"(sp_addr) ); + + sp_size = sp_addr & 8191; + + if(MAX_STACK > sp_size) + { + MAX_STACK = sp_size; + MAX_FUNCT = this_func; + + if (COPY_STACK > sp_size) { + COPY_STACK = sp_size; + copy_trace(sp_addr); + } + + + } + + if ( ((int)jiffies - pre_jiffies) > PRINT_INTV ) { + + pre_jiffies = jiffies; + printk("MAXSP,%x,%d.", MAX_FUNCT, MAX_STACK); + + if (PRINT_STACK > MAX_STACK) { + int i; + unsigned long flags; + save_flags(flags); cli(); + PRINT_STACK = MAX_STACK; + printk("\nCall Trace:"); + + for (i=0 ; i<40 ; i++) { + if (0==MAX_FUNCT_call[i]) + break; + printk(" [<%08lx>]", MAX_FUNCT_call[i]); + if ( 4==(i%5)) + printk("\n"); + } + restore_flags(flags); + } + MAX_STACK = 8*1024; + } +} + +void __attribute__((__no_instrument_function__)) +__cyg_profile_func_exit(void *this_func, void *call_site) +{ +#if 0 + if (this_func == rtk_voip_dsp_init) + { + printk("rtk_voip_dsp_init exit\n"); + } +#endif +} + +#include + +void __attribute__((__no_instrument_function__)) copy_trace(unsigned int *sp) +{ + int i; + //int column = 0; + unsigned int *stack; + unsigned long kernel_start, kernel_end; + + extern char _stext, _etext; + + stack = sp ; + i = 0; + + kernel_start = (unsigned long) &_stext; + kernel_end = (unsigned long) &_etext; + + + //printk("\nCall Trace:"); + + while ((unsigned long) stack & (PAGE_SIZE -1)) { + unsigned long addr; + + if (__get_user(addr, stack++)) { + printk(" (Bad stack address)\n"); + break; + } + + /* + * If the address is either in the text segment of the + * kernel, or in the region which contains vmalloc'ed + * memory, it *may* be the address of a calling + * routine; if so, print it so that someone tracing + * down the cause of the crash will be able to figure + * out the call path that was taken. + */ + + if (addr >= kernel_start && addr < kernel_end) { + + MAX_FUNCT_call[i]=addr; + //printk(" [<%08lx>]", addr); + //if (column++ == 5) { + // printk("\n"); + // column = 0; + //} + if (++i > 40) { + //printk(" ..."); + break; + } + } + + + } + + //if (column != 0) + // printk("\n"); + + for ( ; i<40; i++) + MAX_FUNCT_call[i] = 0; +} +#endif +/*================================================================*/ + + + + +/*================================================================*/ +/* Routine Implementations */ + +#ifdef SIMULATION +static unsigned long register_read_dw(int offset) +{ + unsigned long status = 0; + unsigned short wdata = 0; + + if (offset == REG_ISR) { + status = reg_isr; + + if (data_in_len > data_in_read_idx) + status |= IP_NEWMSG; + + if (msg_is_fetched) { + status |= IP_MSGFETCH; + msg_is_fetched = 0; + } + + return status; + } + else if (offset == REG_RCR) { + ASSERT(data_in_len > data_in_read_idx); + memcpy(&wdata, &data_in[data_in_read_idx], 2); + data_in_read_idx += 2; + status |= wdata; + return status; + } + else if (offset == REG_SYSCR) { + return reg_scr; + } + else { + ASSERT(0); + return status; + } +} + +static void register_write_dw(int offset, unsigned long data) +{ + if (offset == REG_SSR) { + unsigned short wData = (unsigned short)data; + + memcpy(&data_out, &wData, 2); + data_out_len = 2; + } + else if (offset == REG_ISR) { + reg_isr &= ~data ; + } +} +#endif // SIMULATION + +#ifdef KDB_MSG +static void inline debugk_out(unsigned char *label, unsigned char *data, int data_length) +{ + int i,j; + int num_blocks; + int block_remainder; + + num_blocks = data_length >> 4; + block_remainder = data_length & 15; + + if (label) + DEBUGK_OUT("%s\n", label); + + if (data==NULL || data_length==0) + return; + + for (i=0; i 0) { + printk("\t"); + for (j=0; jevt_que_head, priv->evt_que_tail, EV_QUE_MAX); + if (size == 0) { + DEBUGK_ERR("Indication queue full, drop event!\n"); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) + if (priv->host_pid != -1) + kill_proc(priv->host_pid, SIGUSR1, 1); +#else + if (priv->host_pid != NULL) + kill_pid(priv->host_pid, SIGUSR1, 1); +#endif + + return 0; + } + + ASSERT(data_len < MDIO_BUFSIZE); + + priv->ind_evt_que[priv->evt_que_head].id = id; + priv->ind_evt_que[priv->evt_que_head].len = data_len; + memcpy(&priv->ind_evt_que[priv->evt_que_head].buf, data, data_len); + priv->evt_que_head = (priv->evt_que_head + 1) & (EV_QUE_MAX - 1); +#if 0 //mark_nfbi , now we use polling instead of singal in HCD +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) + if (priv->host_pid != -1) + kill_proc(priv->host_pid, SIGUSR1, 1); +#else + if (priv->host_pid != NULL) + kill_pid(priv->host_pid, SIGUSR1, 1); +#endif +#endif + return 1; +} + +static int retrieve_evt(struct mdio_priv *priv, unsigned char *out) +{ + int len = 0; + + if (CIRC_CNT(priv->evt_que_head, priv->evt_que_tail, EV_QUE_MAX) > 0) { // more than one evt pending + len = EVT_BUF_OFFSET + priv->ind_evt_que[priv->evt_que_tail].len; + memcpy(out, &priv->ind_evt_que[priv->evt_que_tail], len); + priv->evt_que_tail = (priv->evt_que_tail + 1) & (EV_QUE_MAX - 1); + } + return len; +} + +static void process_rx_cmd(struct mdio_priv *priv, unsigned short data) +{ + if (priv->rx_cmd_time && + (priv->rx_cmd_state == STATE_RX_WAIT_LEN || + priv->rx_cmd_state == STATE_RX_WAIT_DATA) && + (priv->cmd_timeout && TIME_DIFF(jiffies, priv->rx_cmd_time) > priv->cmd_timeout)) { + DEBUGK_ERR("Rx cmd timeout [%ld][0x%x], discard pending cmd!\n", TIME_DIFF(jiffies, priv->rx_cmd_time), data); + RESET_RX_STATE; + return; + } + + if ((data & FIRST_CMD_MASK) && (priv->rx_cmd_state != STATE_RX_INIT)) { + DEBUGK_ERR("Rx Sync bit but not in INIT_STATE [%d][0x%x], discard pending cmd!\n", priv->rx_cmd_state, data); + RESET_RX_STATE; + } + + if (priv->rx_cmd_state == STATE_RX_INIT) { + ASSERT(priv->data_in.len == 0); + + if (!(data & FIRST_CMD_MASK)) { + DEBUGK_ERR("Got invalid rx cmd id [0x%x], discard it!\n", data); + goto invalid_cmd; + } + PUT_IN_DATA(data); // cmd id + priv->rx_cmd_state = STATE_RX_WAIT_LEN; + priv->rx_cmd_time = jiffies; + } + else { // STATE_RX_WAIT_LEN or STATE_RX_WAIT_DATA + // check if first cmd byte is '0' + if (data & 0xff00) { + DEBUGK_ERR("1st byte of rx cmd not zero [%x]!\n", data >> 8); + goto invalid_cmd; + } + + if (priv->rx_cmd_state == STATE_RX_WAIT_LEN) { + PUT_IN_DATA(data); + priv->rx_cmd_state = STATE_RX_WAIT_DATA; + priv->rx_cmd_remain_len = (data + 1)*2; // including checksum + priv->rx_cmd_time = jiffies; + } + else { // in STATE_RX_WAIT_DATA + ASSERT (priv->rx_cmd_remain_len > 0); + + PUT_IN_DATA(data); + priv->rx_cmd_remain_len -= 2; + if (priv->rx_cmd_remain_len <= 0) { // rx last bye, calcuate checksum + if (!is_checksum_ok(priv->data_in.buf, priv->data_in.len)) { + DEBUGK_ERR("Rx cmd cheksum error!\n"); + goto invalid_cmd; + } + priv->data_in.len -= 2; // substract checksum length + + indicate_evt(priv, IND_CMD_EV, priv->data_in.buf, priv->data_in.len); + + RESET_RX_STATE; + } + else + priv->rx_cmd_time = jiffies; + } + } + + return; + +invalid_cmd: + RESET_RX_STATE; +} + +static void transmit_msg(struct mdio_priv *priv) +{ + unsigned short data; + + if (priv->data_out.len <= 0 || priv->tx_status_transmitting_len >= priv->data_out.len) + return; + + memcpy(&data, priv->data_out.buf+priv->tx_status_transmitting_len, 2); + register_write_dw(REG_SSR, (unsigned long)data); + + priv->tx_status_transmitting_len += 2; + + if (priv->tx_status_transmitting_len >= priv->data_out.len) + priv->tx_status_state = STATE_TX_INIT; + else + priv->tx_status_state = STATE_TX_IN_PROGRESS; +} + +#ifdef CONFIG_RTK_VOIP_ETHERNET_DSP_IS_DSP + +static irqreturn_t mdio_interrupt(int irq, void *dev_id) +{ + struct mdio_priv *priv = (struct mdio_priv *)dev_id; + + unsigned long status, data; + + while (1) { + status = register_read_dw(REG_ISR); + if (!status) + break; + + register_write_dw(REG_ISR, status); // clear interrupt + + unsigned long st_dsp_id; + extern int Set_Ethernet_DSP_ID(unsigned char dsp_id); + extern unsigned int Get_Ethernet_DSP_ID(void); + printk("in mdio_interrupt, status = 0x%x\n", status); + + if (status & 0x8)//bit3 + { + data = register_read_dw(REG_SYSCR) & 0xF;// use SYSCR bit 0~3 to decide DSP ID for each DSP. up to 16 DSP. + + if((data>=0) && (data<=3)) + { + Set_Ethernet_DSP_ID((unsigned char)data); + //printk("Set DSP ID to %d\n", data); + + // Singal AP process to chage DSP MAC addr + printk("signal to hcd to change MAC Addr\n"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) + if (priv->host_pid != -1) + kill_proc(priv->host_pid, SIGUSR2, 1); +#else + if (priv->host_pid != -1) + kill_pid(priv->host_pid, SIGUSR2, 1); +#endif + } + else + printk("%s: NOT support dsp_id=%d\n", __FUNCTION__, data); + } + else + { + printk("Get unknown mdio interrupt status = 0x%x\n", status); + } + + if (status & IP_NEWMSG) { + data = register_read_dw(REG_RCR); + process_rx_cmd(priv, (unsigned short)data); + } + if (status & IP_MSGFETCH) { + transmit_msg(priv); + } + if (status & (IP_ISOLATION|IP_ETHMAC|IP_WLANMAC|IP_ETHPHY |IP_WLANPHY|IP_SELMIICLK)) { + data = register_read_dw(REG_SYSCR); + data &= (CR_ISOLATION |CR_ETHMAC|CR_WLANMAC|CR_ETHPHY|CR_WLANPHY|CR_SELMIICLK); + indicate_evt(priv, IND_SYSCTL_EV, (unsigned char *)&data, sizeof(data)); + } +#ifdef CONFIG_RTK_VOIP_ETHERNET_DSP_IS_DSP + if (status & ~(IP_MSGFETCH|IP_NEWMSG|IP_ISOLATION|IP_ETHMAC| + IP_WLANMAC|IP_ETHPHY |IP_WLANPHY |IP_SELMIICLK |IP_CUSTOM3)) { +#else + if (status & ~(IP_MSGFETCH|IP_NEWMSG|IP_ISOLATION|IP_ETHMAC| + IP_WLANMAC|IP_ETHPHY |IP_WLANPHY|IP_SELMIICLK)) { +#endif + DEBUGK_ERR("Got satus=0x%x, not supported yet!\n", (unsigned int)status); + } + } + return IRQ_HANDLED; +} + +#else + +//static void mdio_interrupt(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t mdio_interrupt(int irq, void *dev_id) +{ + struct mdio_priv *priv = (struct mdio_priv *)dev_id; + + unsigned long status, data; + + while (1) { + status = register_read_dw(REG_ISR); + if (!status) + break; + + register_write_dw(REG_ISR, status); // clear interrupt + + if (status & IP_NEWMSG) { + data = register_read_dw(REG_RCR); + process_rx_cmd(priv, (unsigned short)data); + } + if (status & IP_MSGFETCH) { + transmit_msg(priv); + } + if (status & (IP_ISOLATION|IP_ETHMAC|IP_WLANMAC|IP_ETHPHY |IP_WLANPHY|IP_SELMIICLK)) { + data = register_read_dw(REG_SYSCR); + data &= (CR_ISOLATION |CR_ETHMAC|CR_WLANMAC|CR_ETHPHY|CR_WLANPHY|CR_SELMIICLK); + indicate_evt(priv, IND_SYSCTL_EV, (unsigned char *)&data, sizeof(data)); + } + if (status & ~(IP_MSGFETCH|IP_NEWMSG|IP_ISOLATION|IP_ETHMAC| + IP_WLANMAC|IP_ETHPHY |IP_WLANPHY|IP_SELMIICLK)) { + DEBUGK_ERR("Got satus=0x%x, not supported yet!\n", (unsigned int)status); + } + } + return IRQ_HANDLED; +} + +#endif + +void toggle_usb_device_insert_bit(void) +{ + unsigned long flags; + + if (dev_priv == NULL) + return; + spin_lock_irqsave(&dev_priv->reglock, flags); + if (REG32(REG_SYSSR) & SR_USBInsertStatus) + REG32(REG_SYSSR) = REG32(REG_SYSSR) & (~SR_USBInsertStatus); + else + REG32(REG_SYSSR) = REG32(REG_SYSSR) | SR_USBInsertStatus; + spin_unlock_irqrestore(&dev_priv->reglock, flags); + //printk("usb device inserted\n"); +} + +void toggle_usb_device_remove_bit(void) +{ + unsigned long flags; + + if (dev_priv == NULL) + return; + spin_lock_irqsave(&dev_priv->reglock, flags); + if (REG32(REG_SYSSR) & SR_USBRemoveStatus) + REG32(REG_SYSSR) = REG32(REG_SYSSR) & (~SR_USBRemoveStatus); + else + REG32(REG_SYSSR) = REG32(REG_SYSSR) | SR_USBRemoveStatus; + spin_unlock_irqrestore(&dev_priv->reglock, flags); + //printk("usb device removed\n"); +} + +void set_wlanlink_bit(int val) +{ + unsigned long flags; + + if (dev_priv == NULL) + return; + spin_lock_irqsave(&dev_priv->reglock, flags); + if (val) + REG32(REG_SYSSR) = REG32(REG_SYSSR) | SR_WLANLink; + else + REG32(REG_SYSSR) = REG32(REG_SYSSR) & (~SR_WLANLink); + spin_unlock_irqrestore(&dev_priv->reglock, flags); +} + +static void mdio_reg_poll_timer(unsigned long task_priv) +{ + unsigned long flags; + unsigned long reg1, reg2; + struct mdio_priv *priv = (struct mdio_priv *)task_priv; + + if (!priv->poll_timer_up) + return; + + if (priv->reg_BMCR_write != REG32(REG_BMCR)) { + //printk("1. priv->reg_BMCR_write=%x\n", priv->reg_BMCR_write); + reg1 = reg2 = REG32(REG_BMCR); + if ((reg1&0x8000) && (priv->reg_BMCR_write&0x8000)) //check "reset" bit + reg1 &= 0x7fff; + if ((reg1&0x0200) && (priv->reg_BMCR_write&0x0200)) //check "restart auto negotiation" bit + reg1 &= 0xfdff; + + if (priv->force_power_down) + reg1 |= 0x0800; //power down + priv->reg_BMCR_write = reg2; + set_ether_phy_reg(ETH_PORT_NUM, 0, reg1); + if (reg1 & 0x1000) { + if ((priv->reg_ANAR_write & 0x0180) == 0x0000) + set_ether_phy_reg(6, 0, 0x120c); + else + set_ether_phy_reg(6, 0, 0x1208); + } +#ifdef CONFIG_RTL8197B_B_CUT_PATCH + else { + if (reg1 & 0x2000) //100M + set_ether_phy_reg(6, 0, 0x120c); + else //10M + set_ether_phy_reg(6, 0, 0x1208); + } +#endif + //printk("2. priv->reg_BMCR_write=%x\n", priv->reg_BMCR_write); + } + + if (priv->reg_BMCR_read != get_ether_phy_reg(ETH_PORT_NUM, 0)) { + //printk("1. priv->reg_BMCR_read=%x\n", priv->reg_BMCR_read); + priv->reg_BMCR_read = get_ether_phy_reg(ETH_PORT_NUM, 0); + if (priv->reg_BMCR_write & 0x0800) //power down + REG32(REG_BMCR) = priv->reg_BMCR_read; + else + REG32(REG_BMCR) = priv->reg_BMCR_read & 0xf7ff; + //printk("2. priv->reg_BMCR_read=%x\n", priv->reg_BMCR_read); + } + + if (priv->reg_BMSR_read != get_ether_phy_reg(ETH_PORT_NUM, 1)) { + reg1 = get_ether_phy_reg(ETH_PORT_NUM, 1); + if ((priv->reg_BMSR_read ^ reg1) & (~BIT(2))) { + //toggle EthPHYStatusChange bit to generate interrupt + spin_lock_irqsave(&priv->reglock, flags); + if (REG32(REG_SYSSR) & SR_EthPHYStatusChange) + REG32(REG_SYSSR) = REG32(REG_SYSSR) & (~SR_EthPHYStatusChange); + else + REG32(REG_SYSSR) = REG32(REG_SYSSR) | SR_EthPHYStatusChange; + spin_unlock_irqrestore(&priv->reglock, flags); + //printk("EthPhy status changed!\n"); + } + + priv->reg_BMSR_read = reg1; + REG32(REG_BMSR) = priv->reg_BMSR_read; + + if (priv->eth_phy_link_status != ((priv->reg_BMSR_read & BIT(2)) ? 1 : 0)) { + priv->eth_phy_link_status = ((priv->reg_BMSR_read & BIT(2)) ? 1 : 0); + spin_lock_irqsave(&priv->reglock, flags); + if (priv->eth_phy_link_status) + REG32(REG_SYSSR) = REG32(REG_SYSSR) | SR_EthLink; + else + REG32(REG_SYSSR) = REG32(REG_SYSSR) & (~SR_EthLink); + spin_unlock_irqrestore(&priv->reglock, flags); + //DEBUGK_OUT("Ether Link changed [%d]!\n", priv->eth_phy_link_status); + //printk("Ether Link changed [%d]!\n", priv->eth_phy_link_status); + } + } + + if (!(priv->reg_BMCR_write&0x8000)) { + if (priv->reg_ANAR_write != REG32(REG_ANAR)) { + //printk("1. priv->reg_ANAR_write=%x\n", priv->reg_ANAR_write); + priv->reg_ANAR_write = REG32(REG_ANAR); + //printk("2. priv->reg_ANAR_write=%x\n", priv->reg_ANAR_write); + set_ether_phy_reg(ETH_PORT_NUM, 4, priv->reg_ANAR_write); + //printk("3. priv->reg_ANAR_write=%x\n", priv->reg_ANAR_write); + if (priv->reg_BMCR_write&0x1000) { + if ((priv->reg_ANAR_write & 0x0180) == 0x0000) + set_ether_phy_reg(6, 0, 0x120c); + else + set_ether_phy_reg(6, 0, 0x1208); + } + } + } + + if (priv->reg_ANAR_read != get_ether_phy_reg(ETH_PORT_NUM, 4)) { + //printk("1. priv->reg_ANAR_read=%x\n", priv->reg_ANAR_read); + priv->reg_ANAR_read = get_ether_phy_reg(ETH_PORT_NUM, 4); + //printk("2. priv->reg_ANAR_read=%x\n", priv->reg_ANAR_read); + REG32(REG_ANAR) = priv->reg_ANAR_read; + } + + if (priv->reg_ANLPAR_read != get_ether_phy_reg(ETH_PORT_NUM, 5)) { + priv->reg_ANLPAR_read = get_ether_phy_reg(ETH_PORT_NUM, 5); + REG32(REG_ANLPAR) = priv->reg_ANLPAR_read; + } + + mod_timer(&priv->reg_poll_timer, jiffies + priv->phy_reg_poll_time); +} + +static int mdio_open(struct inode *inode, struct file *filp) +{ + filp->private_data = dev_priv; + //MOD_INC_USE_COUNT; + return 0; +} + +static int mdio_close(struct inode *inode, struct file *filp) +{ + //MOD_DEC_USE_COUNT; + return 0; +} + +static ssize_t mdio_read (struct file *filp, char *buf, size_t count, loff_t *offset) +{ + struct mdio_priv *priv = (struct mdio_priv *)filp->private_data; + + if (!buf) + return 0; + + count = retrieve_evt(priv, buf); + + return count; +} + +static ssize_t mdio_write (struct file *filp, const char *buf, size_t count, loff_t *offset) +{ + struct mdio_priv *priv = (struct mdio_priv *)filp->private_data; + unsigned short last_word = 0; + + if (!buf) { + DEBUGK_ERR("buf = NULL!\n"); + goto ret; + } + + if (count > MDIO_BUFSIZE) { + DEBUGK_ERR("write length too big!\n"); + count = -EFAULT; + goto ret; + } + + if (priv->tx_status_state != STATE_TX_INIT) { + DEBUGK_ERR("Transmit status, but not in valid state [%d]. Reset state!\n", priv->tx_status_state); + priv->tx_status_state = STATE_TX_INIT; + } + + if (count %2) { + DEBUGK_ERR("Invalid Tx size [%d]!\n", count); + count = -EFAULT; + goto ret; + } + + if (copy_from_user((void *)priv->data_out.buf, buf, count)) { + DEBUGK_ERR("copy_from_user() error!\n"); + count = -EFAULT; + goto ret; + } + +#ifdef KDB_MSG + debugk_out("write data", priv->data_out.buf, count); +#endif + + last_word = (unsigned short) append_checksum(priv->data_out.buf, count); + memcpy(&priv->data_out.buf[count], &last_word, 2); + + priv->data_out.len = count + sizeof(last_word); + priv->tx_status_transmitting_len = 0; + priv->tx_status_state = STATE_TX_INIT; + + transmit_msg(priv); + +ret: + return count; +} + +static void dump_private_data(void) +{ + //int i; + + printk("cmd_timeout=%d\n", dev_priv->cmd_timeout); + printk("poll_timer_up=%d\n", dev_priv->poll_timer_up); + printk("phy_reg_poll_time=%d\n", dev_priv->phy_reg_poll_time); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) + printk("host_pid=%d\n", dev_priv->host_pid); +#else + printk("host_pid=0x%p\n", dev_priv->host_pid); +#endif + printk("force_power_down=%d\n", dev_priv->force_power_down); + printk("eth_phy_link_status=%d\n", dev_priv->eth_phy_link_status); + printk("\nreg_BMCR_write=0x%04lx\n", dev_priv->reg_BMCR_write); + printk("reg_BMCR_read=0x%04lx\n", dev_priv->reg_BMCR_read); + printk("reg_BMSR_read=0x%04lx\n", dev_priv->reg_BMSR_read); + printk("reg_ANAR_write=0x%04lx\n", dev_priv->reg_ANAR_write); + printk("reg_ANAR_read=0x%04lx\n", dev_priv->reg_ANAR_read); + printk("reg_ANLPAR_read=0x%04lx\n", dev_priv->reg_ANLPAR_read); + + printk("\nevt_que_head=%d\n", dev_priv->evt_que_head); + printk("evt_que_tail=%d\n", dev_priv->evt_que_tail); + printk("evt_que count=%d\n", CIRC_CNT(dev_priv->evt_que_head, dev_priv->evt_que_tail, EV_QUE_MAX)); + printk("evt_que space=%d\n", CIRC_SPACE(dev_priv->evt_que_head, dev_priv->evt_que_tail, EV_QUE_MAX)); + + printk("\ntx_status_state:"); + switch(dev_priv->tx_status_state) { + case STATE_TX_INIT: + printk("STATE_TX_INIT\n"); + break; + case STATE_TX_IN_PROGRESS: + printk("STATE_TX_IN_PROGRESS\n"); + break; + } + printk("tx_status_transmitting_len=%d\n", dev_priv->tx_status_transmitting_len); + + printk("rx_cmd_state:"); + switch(dev_priv->rx_cmd_state) { + case STATE_RX_INIT: + printk("STATE_RX_INIT\n"); + break; + case STATE_RX_WAIT_LEN: + printk("STATE_RX_WAIT_LEN\n"); + break; + case STATE_RX_WAIT_DATA: + printk("STATE_RX_WAIT_DATA\n"); + break; + case STATE_RX_WAIT_DAEMON: + printk("STATE_RX_WAIT_DAEMON\n"); + break; + } + printk("rx_cmd_remain_len=%d\n", dev_priv->rx_cmd_remain_len); + printk("rx_cmd_time=%lx jiffies=%lx (%d sec)\n", dev_priv->rx_cmd_time, jiffies, (int)(jiffies-dev_priv->rx_cmd_time)/HZ); +} + +void mdio_private_command(int type) +{ + switch(type) { + case 0: //stop reg poll timer + dev_priv->poll_timer_up = 0; + break; + case 1: //start reg poll timer + dev_priv->poll_timer_up = 1; + mod_timer(&dev_priv->reg_poll_timer, jiffies + dev_priv->phy_reg_poll_time); + break; + case 2: //force power down: on + dev_priv->force_power_down = 1; + dev_priv->reg_BMCR_write = 0xf0000; //force poll timer to do power down + break; + case 3: //force power down: off + dev_priv->force_power_down = 0; + dev_priv->reg_BMCR_write = 0xf0000; //force poll timer to do power down + break; + case 4: + dump_private_data(); + break; + case 5: //WLAN link up + set_wlanlink_bit(1); + break; + case 6: //WLAN link down + set_wlanlink_bit(0); + break; + } +} + +static int mdio_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + struct mdio_priv *priv = (struct mdio_priv *)filp->private_data; + int val, retval = 0; + unsigned char bval; + struct mdio_mem32_param mem_param; + struct reg_param regparam; + + /* + * extract the type and number bitfields, and don't decode + * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok() + */ + if (_IOC_TYPE(cmd) != MDIO_IOC_MAGIC) return -ENOTTY; + if (_IOC_NR(cmd) > MDIO_IOCTL_MAXNR) return -ENOTTY; + + switch (cmd) { + case MDIO_IOCTL_PRIV_CMD: + retval = get_user(val, (int *)arg); + if (retval == 0) { + mdio_private_command(val); + } + break; + case MDIO_IOCTL_GET_REG: + retval = copy_from_user(®param, (struct reg_param *)arg, sizeof(struct reg_param)); + if (retval == 0) { + //printk("1regparam.addr=%x regparam.val=%x\n", regparam.addr, regparam.val); + regparam.val = get_ether_phy_reg(((regparam.addr>>16)&0xffff), (regparam.addr&0xffff)); + //printk("2regparam.addr=%x regparam.val=%x\n", regparam.addr, regparam.val); + } + retval = copy_to_user((struct reg_param *)arg, ®param, sizeof(struct reg_param)); + break; + case MDIO_IOCTL_SET_REG: + retval = copy_from_user(®param, (struct reg_param *)arg, sizeof(struct reg_param)); + if (retval == 0) { + //printk("regparam.addr=%x regparam.val=%x\n", regparam.addr, regparam.val); + set_ether_phy_reg(((regparam.addr>>16)&0xffff), (regparam.addr&0xffff), regparam.val); + } + break; + case MDIO_IOCTL_SET_HOST_PID: + retval = copy_from_user((void *)&val, (void *)arg, 4); + if (retval == 0) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) + priv->host_pid = val; +#elif LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30) + priv->host_pid = find_pid_ns(val,0); +#else + // TODO : Check in stable 2.6.27 + priv->host_pid = task_pid(find_task_by_vpid(val)); + //priv->host_pid = task_pid(current); +#endif + DEBUGK_OUT("set pid=%d\n", val); + printk("set pid=%d priv->host_pid=%p\n", val, priv->host_pid); + } + spin_lock_irqsave(&priv->reglock, flags); + REG32(REG_SYSSR) = REG32(REG_SYSSR) | SR_AllSoftwareReady; + spin_unlock_irqrestore(&priv->reglock, flags); + break; + case MDIO_IOCTL_SET_CMD_TIMEOUT: + retval = copy_from_user((void *)&bval, (void *)arg, 1); + if (retval == 0) { + priv->cmd_timeout = (int)bval; + DEBUGK_OUT("set cmd_timeout=%d\n", priv->cmd_timeout); + } + break; + case MDIO_IOCTL_SET_PHY_POLL_TIME: + retval = copy_from_user( (void *)&bval, (void *)arg, 1); + if (retval == 0) { + priv->phy_reg_poll_time = (int)bval; + mod_timer(&priv->reg_poll_timer, jiffies + priv->phy_reg_poll_time); + DEBUGK_OUT("set poll_time=%d\n", priv->phy_reg_poll_time); + } + break; + case MDIO_IOCTL_READ_MEM: + retval = copy_from_user( (void *)&mem_param.addr, (void *)arg, 4); + if (retval == 0) { + mem_param.val = READ_MEM32(mem_param.addr); + retval = copy_to_user((void *)arg, (void *)&mem_param.val, 4); + DEBUGK_OUT("read_mem: addr=0x%x, data=0x%x\n", (int)mem_param.addr, (int)mem_param.val); + } + break; + case MDIO_IOCTL_WRITE_MEM: + retval = copy_from_user((void *)&mem_param, (void *)arg, sizeof(mem_param)); + if (retval == 0) { + WRITE_MEM32(mem_param.addr, mem_param.val); + DEBUGK_OUT("write_mem: addr=0x%x, data=0x%x\n", (int)mem_param.addr, (int)mem_param.val); + } + break; + case MDIO_IOCTL_SET_MII_PAUSE: + retval = copy_from_user( (void *)&bval, (void *)arg, 1); + if (retval == 0) { + if (bval == 0) // disable pause + WRITE_MEM32(PCRP0, (~(0x3< 0) { + +fetch_again: + while (data_out_len > 0) { + data_out_len = 0; + size += sprintf(&buf[size], "%04x ", data_out); + } + + msg_is_fetched = 1; + + mdio_interrupt(0, (void *)dev_priv, (struct pt_regs *)NULL); + + if (data_out_len > 0) + goto fetch_again; + + strcat(&buf[size++], "\n"); + } + + return size; +} + +static unsigned short _atoi(char *s, int base) +{ + int k = 0; + + k = 0; + if (base == 10) { + while (*s != '\0' && *s >= '0' && *s <= '9') { + k = 10 * k + (*s - '0'); + s++; + } + } + else { + while (*s != '\0') { + int v; + if ( *s >= '0' && *s <= '9') + v = *s - '0'; + else if ( *s >= 'a' && *s <= 'f') + v = *s - 'a' + 10; + else if ( *s >= 'A' && *s <= 'F') + v = *s - 'A' + 10; + else { + DEBUGK_ERR("error hex format [%x]!\n", *s); + return 0; + } + k = 16 * k + v; + s++; + } + } + return (unsigned short)k; +} + + +static int write_proc(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + char tmp[100]; + int len=count; + unsigned short in_data; + + if (!memcmp(buffer, "cmd: ", 5)) { + buffer += 5; + len -= 5; + data_in_len = 0; + while (len > 0) { + memcpy(tmp, buffer, 4); + tmp[4] = '\0'; + + in_data = _atoi(tmp, 16); + + memcpy(&data_in[data_in_len], &in_data, 2); + data_in_len += 2; + len -= 5; + buffer += 5; + } + + data_in_read_idx = 0; + mdio_interrupt(0, (void *)dev_priv, (struct pt_regs *)NULL); + } + else if (!memcmp(buffer, "scr: ", 5)) { + buffer += 5; + memcpy(tmp, buffer, 4); + tmp[4] = '\0'; + reg_scr = _atoi(tmp, 16); + } + else if (!memcmp(buffer, "isr: ", 5)) { + buffer += 5; + memcpy(tmp, buffer, 4); + tmp[4] = '\0'; + reg_isr = _atoi(tmp, 16); + + mdio_interrupt(0, (void *)dev_priv, (struct pt_regs *)NULL); + } + else { + printk("Invalid cmd!\n"); + } + + return count; +} +#endif + + +static void __exit mdio_exit(void) +{ + //DEBUGK_OUT("%s: major=%d, minor=%d\n", __FUNCTION__, MAJOR(inode->i_rdev), MINOR(inode->i_rdev)); + + REG32(REG_IMR) = 0; + + free_irq(BSP_NFBI_IRQ, dev_priv); + + del_timer_sync(&dev_priv->reg_poll_timer); + + kfree(dev_priv); + + dev_priv = NULL; +} + +static struct file_operations mdio_fops = { + read: mdio_read, + write: mdio_write, + ioctl: mdio_ioctl, + open: mdio_open, + release: mdio_close, +}; + +static int __init mdio_init(void) +{ + struct mdio_priv *priv; + + if (register_chrdev(DRIVER_MAJOR, DRIVER_NAME, &mdio_fops)) { + DEBUGK_ERR(KERN_ERR DRIVER_NAME": unable to get major %d\n", DRIVER_MAJOR); + return -EIO; + } + +#ifdef SIMULATION + struct proc_dir_entry *res; + res = create_proc_entry("mdio_flag", 0, NULL); + if (res) { + res->read_proc = read_proc; + res->write_proc = write_proc; + } + else { + DEBUGK_ERR(KERN_ERR DRIVER_NAME": unable to create /proc/mdio_flag\n"); + return -1; + } +#endif + + printk(KERN_INFO DRIVER_NAME" driver "DRIVER_VER" at %x (Interrupt %d)\n", NFBI_BASE, BSP_NFBI_IRQ); + + DEBUGK_OUT("%s: major=%d, minor=%d\n", __FUNCTION__, MAJOR(inode->i_rdev), MINOR(inode->i_rdev)); + + priv = (struct mdio_priv *)kmalloc(sizeof (struct mdio_priv), GFP_KERNEL); + if(!priv) + return -ENOMEM; + + memset((void *)priv, 0, sizeof (struct mdio_priv)); + priv->reglock = SPIN_LOCK_UNLOCKED; + //if (request_irq(BSP_NFBI_IRQ, mdio_interrupt, SA_INTERRUPT, DRIVER_NAME, (void *)priv)) { + //if (request_irq(BSP_NFBI_IRQ, mdio_interrupt, IRQF_DISABLED, DRIVER_NAME, (void *)priv)) { + if (request_irq(BSP_NFBI_IRQ, mdio_interrupt, 0, DRIVER_NAME, (void *)priv)) { + DEBUGK_ERR(KERN_ERR DRIVER_NAME": IRQ %d is not free.\n", BSP_NFBI_IRQ); + return -1; + } + else + printk("Request BSP_NFBI_IRQ successfully.\n"); + + REG32(REG_IMR) = NEEDED_IRQ_MASK; + +#if 1 + //force the poll time to update the register + priv->reg_BMCR_write = 0xf0000; + priv->reg_BMCR_read = 0xf0000; + priv->reg_BMSR_read = 0xf0000; + priv->reg_ANAR_write = 0xf0000; + priv->reg_ANAR_read = 0xf0000; + priv->reg_ANLPAR_read = 0xf0000; + priv->eth_phy_link_status = -1; +#else + priv->reg_BMCR_write = REG32(REG_BMCR); + set_ether_phy_reg(ETH_PORT_NUM, 0, priv->reg_BMCR_write); + + priv->reg_BMCR_read = get_ether_phy_reg(ETH_PORT_NUM, 0); + REG32(REG_BMCR) = priv->reg_BMCR_read; + + priv->reg_BMSR_read = get_ether_phy_reg(ETH_PORT_NUM, 1); + REG32(REG_BMSR) = priv->reg_BMSR_read; + + priv->reg_ANAR_write = REG32(REG_ANAR); + set_ether_phy_reg(ETH_PORT_NUM, 4, priv->reg_ANAR_write); + //printk("priv->reg_ANAR_write=%x\n", priv->reg_ANAR_write); + + priv->reg_ANAR_read = get_ether_phy_reg(ETH_PORT_NUM, 4); + REG32(REG_ANAR) = priv->reg_ANAR_read; + //printk("priv->reg_ANAR_read=%x\n", priv->reg_ANAR_read); + + priv->reg_ANLPAR_read = get_ether_phy_reg(ETH_PORT_NUM, 5); + REG32(REG_ANLPAR) = priv->reg_ANLPAR_read; + + priv->eth_phy_link_status = ((priv->reg_BMSR_read & BIT(2)) ? 1 : 0); +#endif + + init_timer(&priv->reg_poll_timer); + priv->reg_poll_timer.data = (unsigned long)priv; + priv->reg_poll_timer.function = mdio_reg_poll_timer; + priv->poll_timer_up = 0; + priv->force_power_down = 0; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) + priv->host_pid = -1; +#else + priv->host_pid = NULL; +#endif + dev_priv = priv; + + return 0; +} + + +/*================================================================*/ + +module_init(mdio_init); +module_exit(mdio_exit); + +MODULE_DESCRIPTION("Driver for RTL8197B MDC/MDIO"); +MODULE_LICENSE("none-GPL"); +//EXPORT_NO_SYMBOLS; diff --git a/target/linux/realtek/files/drivers/char/rtl_mdio/rtl_mdio.h b/target/linux/realtek/files/drivers/char/rtl_mdio/rtl_mdio.h new file mode 100644 index 000000000..5def6bd34 --- /dev/null +++ b/target/linux/realtek/files/drivers/char/rtl_mdio/rtl_mdio.h @@ -0,0 +1,309 @@ +/* +################################################################################ +# +# RTL8198 MDIO char driver header +# +# Copyright(c) 2010 Realtek Semiconductor Corp. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation; either version 2 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, see . +# +# Author: +# Realtek WiFi AP software team +# No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan +# +################################################################################ +*/ +/*================================================================*/ + +#ifndef INCLUDE_RTL_MDIO_H +#define INCLUDE_RTL_MDIO_H + + +/*================================================================*/ +/* Compiling Flags */ + +#define KDB_ERR // defined to print out error message +//#define KDB_MSG // defined to print out debug message +//#define SIMULATION // defined to do simuation +#ifndef CONFIG_RTK_VOIP_ETHERNET_DSP_IS_DSP +#define JUMP_CMD // defined to add jump ioctl cmd, for development purpose +#endif + + +/*================================================================*/ +/* Constant Definitions */ + +#define DRIVER_NAME "rtl_mdio" +#define DRIVER_VER "0.2" +#define IO_LEN 0x40 +#define DRIVER_MAJOR 14 +#define MDIO_BUFSIZE 516 //2 + 2 + 2*255 + 2 +#define EV_QUE_MAX 16 +//#define BIT(x) (1 << (x)) + +// RTL8197B IRQ number and Register offset +#define NFBI_BASE 0xb8019000 // NFBI base address +#define REG_RCR (0x00+NFBI_BASE) // Receive Command Register +#define REG_SSR (0x04+NFBI_BASE) // Send Status Register +#define REG_SYSCR (0x08+NFBI_BASE) // System Control Register +#define REG_SYSSR (0x0C+NFBI_BASE) // System Status Register +#define REG_IMR (0x10+NFBI_BASE) // Interrupt Mask Register +#define REG_ISR (0x14+NFBI_BASE) // Interrupt Status Register +#define REG_BMCR (0x20+NFBI_BASE) // Basic Mode Control Register +#define REG_BMSR (0x24+NFBI_BASE) // Basic Mode Status Register +#define REG_ANAR (0x28+NFBI_BASE) // Auto-Negotiation Advertisement Register +#define REG_ANLPAR (0x2C+NFBI_BASE) // Auto-Negotiation Link Partner Ability Register +#define REG_NFBIRR (0x30+NFBI_BASE) // NFBI Reset Control Register + +// bitmask definition for ISR +#define IP_ISOLATION BIT(15) // ISOLATION Interrupt Pending +#define IP_ETHMAC BIT(14) // Enable/disable Ethernet MAC +#define IP_WLANMAC BIT(13) // Enable/disable WLAN MAC +#define IP_ETHPHY BIT(12) // Enable/disable Ethernet PHY +#define IP_WLANPHY BIT(11) // Enable/disable WLAN PHY +#define IP_SELMIICLK BIT(10) // Select MII Clock Speed +#define IP_RSVD9 BIT(9) // Reserved +#define IP_CUSTOM8 BIT(8) // Customized used 8 +#define IP_CUSTOM7 BIT(7) // Customized used 7 +#define IP_CUSTOM6 BIT(6) // Customized used 6 +#define IP_CUSTOM5 BIT(5) // Customized used 5 +#define IP_CUSTOM4 BIT(4) // Customized used 4 +#define IP_CUSTOM3 BIT(3) // Customized used 3 +#define IP_MSGFETCH BIT(2) // Previous msg has been fetched +#define IP_NEWMSG BIT(1) // New msg has come +#define IP_RSVD0 BIT(0) // Reserved + +// bitmask definition for SCR +#define CR_ISOLATION BIT(15) // ISOLATION control bit +#define CR_ETHMAC BIT(14) // Ethernet MAC control bit +#define CR_WLANMAC BIT(13) // WLAN MAC control bit +#define CR_ETHPHY BIT(12) // Ethernet PHY control bi +#define CR_WLANPHY BIT(11) // WLAN PHY control bit +#define CR_SELMIICLK (BIT(10)|BIT(9)) // Select MII Clock Speed control bit +#define CR_CUSTOM8 BIT(8) // Customized used 8 +#define CR_CUSTOM7 BIT(7) // Customized used 7 +#define CR_CUSTOM6 BIT(6) // Customized used 6 +#define CR_CUSTOM5 BIT(5) // Customized used 5 +#define CR_CUSTOM4 BIT(4) // Customized used 4 +#define CR_CUSTOM3 BIT(3) // Customized used 3 +#define CR_CUSTOM2 BIT(2) // Customized used 2 +#define CR_CUSTOM1 BIT(1) // Customized used 1 +#define CR_CUSTOM0 BIT(0) // Customized used 0 + +// bitmask definition for SYSSR +#define SR_CheckSumDone BIT(15) +#define SR_CheckSumOK BIT(14) +#define SR_WLANLink BIT(13) +#define SR_EthLink BIT(12) +#define SR_EthPHYStatusChange BIT(11) +#define SR_AllSoftwareReady BIT(10) +#define SR_USBInsertStatus BIT(7) +#define SR_USBRemoveStatus BIT(6) +#define SR_BootcodeReady BIT(5) + +// rx cmd id bitmask +#define FIRST_CMD_MASK BIT(15) + +// All received interrupt mask +#ifdef CONFIG_RTK_VOIP_ETHERNET_DSP_IS_DSP +#define NEEDED_IRQ_MASK (IP_NEWMSG|IP_MSGFETCH|IP_SELMIICLK|IP_WLANPHY| \ + IP_ETHPHY|IP_WLANMAC|IP_ETHMAC|IP_ISOLATION|IP_CUSTOM3) +#else +#define NEEDED_IRQ_MASK (IP_NEWMSG|IP_MSGFETCH|IP_SELMIICLK|IP_WLANPHY| \ + IP_ETHPHY|IP_WLANMAC|IP_ETHMAC|IP_ISOLATION) +#endif + +/* Ether register base address */ +#define MACCR_BASE 0xbb804000 /* Internal Ether MAC base address */ +#define PCRAM_BASE (MACCR_BASE+0x100) /* Per-port Configuration Register */ +#define PCRP0 (0x004 + PCRAM_BASE) /* Port Configuration Register of Port 0 */ +#define PCRP3 (0x010 + PCRAM_BASE) /* Port Configuration Register of Port 3 */ +#define P0GMIICR (0x04C + PCRAM_BASE) /* Port-0 GMII Configuration Register */ +#define PauseFlowControl 8 /* Bit-shift number of PauseFlowControl */ + +/* MAC control register field definitions */ +#define MDCIOCR (0x004+MACCR_BASE) /* MDC/MDIO Command */ +#define MDCIOSR (0x008+MACCR_BASE) /* MDC/MDIO Status */ +//#define STATUS (1<<31) /* 0: Process Done, 1: In progress */ +//STATUS has been defined in linux-2.6.30/arch/rlx/include/asm/ptrace.h +#define MDCIO_STATUS (1<<31) /* 0: Process Done, 1: In progress */ + +/* MDCIOCR - MDC/MDIO Command */ +#define COMMAND_READ (0<<31) /* 0:Read Access, 1:Write Access */ +#define COMMAND_WRITE (1<<31) /* 0:Read Access, 1:Write Access */ + +#define PHYADD_OFFSET (24) /* PHY Address, said, PHY ID */ +#define REGADD_OFFSET (16) /* PHY Register */ + +#define ETH_PORT_NUM (3) /* Port number of built-in Ether phy */ +#define MII_PORT_NUM (0) /* Port number of NFBI MII */ + +// rx cmd state +enum { + STATE_RX_INIT, + STATE_RX_WAIT_LEN, + STATE_RX_WAIT_DATA, + STATE_RX_WAIT_DAEMON +}; + +// tx status state +enum { + STATE_TX_INIT, + STATE_TX_IN_PROGRESS +}; + +// indication event id +enum { + IND_CMD_EV, + IND_SYSCTL_EV +}; + +// cmd id of write data +enum { + WRITE_MDIO, + SET_CMD_TIMEOUT, + SET_PHY_POLL_TIME, + SET_HOST_PID, +}; + +// cmd id of ioctl +#define MDIO_IOC_MAGIC 'k' + +#define MDIO_IOCTL_SET_HOST_PID _IOW(MDIO_IOC_MAGIC, 0, int) +#define MDIO_IOCTL_SET_CMD_TIMEOUT _IOW(MDIO_IOC_MAGIC, 1, char) +#define MDIO_IOCTL_SET_PHY_POLL_TIME _IOW(MDIO_IOC_MAGIC, 2, char) +#define MDIO_IOCTL_READ_MEM _IOWR(MDIO_IOC_MAGIC, 3, int) +#define MDIO_IOCTL_WRITE_MEM _IOW(MDIO_IOC_MAGIC, 4, struct mdio_mem32_param) +#define MDIO_IOCTL_SET_MII_PAUSE _IOW(MDIO_IOC_MAGIC, 5, char) +#define MDIO_IOCTL_SET_SUSPEND _IOW(MDIO_IOC_MAGIC, 6, char) +#define MDIO_IOCTL_READ_SCR _IOR(MDIO_IOC_MAGIC, 7, int) +#define MDIO_IOCTL_JUMP_ADDR _IOW(MDIO_IOC_MAGIC, 8, int) +#define MDIO_IOCTL_PRIV_CMD _IOW(MDIO_IOC_MAGIC, 9, int) +#define MDIO_IOCTL_GET_REG _IOWR(MDIO_IOC_MAGIC, 10, struct reg_param) +#define MDIO_IOCTL_SET_REG _IOW(MDIO_IOC_MAGIC, 11, struct reg_param) +#define MDIO_IOCTL_SET_MII_CLK _IOW(MDIO_IOC_MAGIC, 12, int) +#define MDIO_IOCTL_SET_ETH_PAUSE _IOW(MDIO_IOC_MAGIC, 13, char) +#ifdef CONFIG_RTK_VOIP_ETHERNET_DSP_IS_DSP +#define MDIO_IOCTL_READ_DSP_ID _IOW(MDIO_IOC_MAGIC, 14, unsigned int) +#define MDIO_IOCTL_MAXNR 14 +#else +#define MDIO_IOCTL_MAXNR 13 +#endif + +#define TIME_DIFF(a, b) ((a >= b)? (a - b):(0xffffffff - b + a + 1)) +#define EVT_BUF_OFFSET ((int)(long *)&(((struct evt_msg *)0)->buf)) +#ifdef REG32 + #undef REG32 +#endif +#define REG32(reg) (*((volatile unsigned long *)(reg))) + +#ifdef WRITE_MEM32 + #undef WRITE_MEM32 + #undef READ_MEM32 +#endif +#define WRITE_MEM32(reg,val) REG32(reg)=val +#define READ_MEM32(reg) REG32(reg) + +#ifndef SIMULATION +#define register_read_dw(offset) (REG32(offset)) +#define register_write_dw(offset, data) (REG32(offset)=data) +#endif + +#define PUT_IN_DATA(data) { \ + memcpy(&priv->data_in.buf[priv->data_in.len], &data, 2); \ + priv->data_in.len += 2; \ +} + +#define RESET_RX_STATE { \ + priv->rx_cmd_state = STATE_RX_INIT; \ + priv->data_in.len = 0; \ + priv->rx_cmd_time = 0; \ +} + +#ifdef KDB_MSG + #define DEBUGK_OUT(fmt, args...) printk("%s_%s: "fmt, DRIVER_NAME, __FUNCTION__, ## args) + + #define ASSERT(expr) \ + if(!(expr)) { \ + printk( "\033[33;41m%s:%d: assert(%s)\033[m\n", \ + __FILE__,__LINE__,#expr); \ + } +#else + #define DEBUGK_OUT(fmt, args...) + + #define ASSERT(expr) +#endif + +#ifdef KDB_ERR + #define DEBUGK_ERR(fmt, args...) printk("%s_%s_ERR: "fmt, DRIVER_NAME, __FUNCTION__, ## args) +#else + #define DEBUGK_ERR(fmt, args...) +#endif + +/*================================================================*/ +/* Structure Definition */ + +struct buf_ar { + int len; + unsigned char buf[MDIO_BUFSIZE]; +}; + +struct evt_msg { + int id; // event id + int len; // length in buf + unsigned char buf[MDIO_BUFSIZE]; +}; + +struct mdio_mem32_param { + unsigned long addr; + unsigned long val; +}; + +struct reg_param { + unsigned long addr; + unsigned long val; +}; + +#ifdef __KERNEL__ +struct mdio_priv { + int cmd_timeout; // in 10ms + int phy_reg_poll_time; // in 10ms +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) + int host_pid; // pid of host-daemon +#else + struct pid *host_pid; +#endif + int poll_timer_up; + int force_power_down; + struct timer_list reg_poll_timer; + unsigned long reg_BMCR_read, reg_BMCR_write; + unsigned long reg_BMSR_read; + unsigned long reg_ANAR_read, reg_ANAR_write; + unsigned long reg_ANLPAR_read; + int eth_phy_link_status; + struct file *filp; + struct buf_ar data_out; + struct buf_ar data_in; + int rx_cmd_state; + unsigned long rx_cmd_time; + int rx_cmd_remain_len; + int tx_status_state; + int tx_status_transmitting_len; + int evt_que_head, evt_que_tail; + struct evt_msg ind_evt_que[EV_QUE_MAX]; + spinlock_t reglock; +}; +#endif + +#endif // INCLUDE_RTL_MDIO_H -- cgit v1.2.3