summaryrefslogtreecommitdiffstats
path: root/target/linux/realtek/files/drivers/char/rtl_mdio
diff options
context:
space:
mode:
authorRoman Yeryomin <roman@advem.lv>2012-09-13 00:40:35 +0300
committerRoman Yeryomin <roman@advem.lv>2012-12-03 00:13:21 +0200
commit5deb3317cb51ac52de922bb55f8492624018906d (patch)
treec2fbe6346699d9bb0f2100490c3029519bb8fde8 /target/linux/realtek/files/drivers/char/rtl_mdio
parent0239d37124f9184b478a42de8a7fa1bc85a6a6fe (diff)
Add realtek target files
Signed-off-by: Roman Yeryomin <roman@advem.lv>
Diffstat (limited to 'target/linux/realtek/files/drivers/char/rtl_mdio')
-rw-r--r--target/linux/realtek/files/drivers/char/rtl_mdio/Makefile2
-rw-r--r--target/linux/realtek/files/drivers/char/rtl_mdio/rtl_mdio.c1316
-rw-r--r--target/linux/realtek/files/drivers/char/rtl_mdio/rtl_mdio.h309
3 files changed, 1627 insertions, 0 deletions
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 <http://www.gnu.org/licenses/>.
+#
+# Author:
+# Realtek WiFi AP software team <cn_sd8@realtek.com>
+# No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan
+#
+################################################################################
+*/
+/*================================================================*/
+/* System Include Files */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/circ_buf.h>
+#include <asm/uaccess.h>
+#include <linux/timer.h>
+#include <linux/proc_fs.h>
+#include <linux/interrupt.h>
+#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 <asm-mips/uaccess.h>
+
+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<num_blocks; i++) {
+ printk("\t");
+ for (j=0; j<16; j++)
+ printk("%02x ", data[j + (i<<4)]);
+ printk("\n");
+ }
+
+ if (block_remainder > 0) {
+ printk("\t");
+ for (j=0; j<block_remainder; j++)
+ printk("%02x ", data[j+(num_blocks<<4)]);
+ printk("\n");
+ }
+}
+#endif // KDB_MSG
+
+unsigned long static get_ether_phy_reg(unsigned long phyId, unsigned long regId)
+{
+ unsigned long status;
+
+ WRITE_MEM32((void *)MDCIOCR, COMMAND_READ | ( phyId << PHYADD_OFFSET ) | ( regId << REGADD_OFFSET ));
+
+ do { status = READ_MEM32( MDCIOSR ); } while ( ( status & MDCIO_STATUS ) != 0 );
+
+ status &= 0xffff;
+ return status;
+}
+
+static void set_ether_phy_reg(unsigned long phyId, unsigned long regId, unsigned long wData)
+{
+ WRITE_MEM32(MDCIOCR, COMMAND_WRITE | ( phyId << PHYADD_OFFSET ) | ( regId << REGADD_OFFSET ) | wData);
+
+ while( ( READ_MEM32( MDCIOSR ) & MDCIO_STATUS ) != 0 ); /* wait until command complete */
+}
+
+static int is_checksum_ok(unsigned char *data, int len)
+{
+ int i;
+ unsigned char sum=0;
+
+ for (i=0; i<len; i++)
+ sum += data[i];
+
+ if (sum == 0)
+ return 1;
+ else
+ return 0;
+}
+
+static unsigned char append_checksum(unsigned char *data, int len)
+{
+ int i;
+ unsigned char sum=0;
+
+ for (i=0; i<len; i++)
+ sum += data[i];
+
+ sum = ~sum + 1;
+ return sum;
+}
+
+static int indicate_evt(struct mdio_priv *priv, int id, unsigned char *data, int data_len)
+{
+ int size;
+
+ size = CIRC_SPACE(priv->evt_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(&regparam, (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, &regparam, sizeof(struct reg_param));
+ break;
+ case MDIO_IOCTL_SET_REG:
+ retval = copy_from_user(&regparam, (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<<PauseFlowControl))&READ_MEM32(PCRP0));
+ else
+ WRITE_MEM32(PCRP0, (3<<PauseFlowControl)|READ_MEM32(PCRP0));
+ DEBUGK_OUT("set mii_pause=%d\n", bval);
+ }
+ break;
+ case MDIO_IOCTL_SET_ETH_PAUSE:
+ retval = copy_from_user( (void *)&bval, (void *)arg, 1);
+ if (retval == 0) {
+ if (bval == 0) // disable pause
+ WRITE_MEM32(PCRP3, (~(0x3<<PauseFlowControl))&READ_MEM32(PCRP3));
+ else
+ WRITE_MEM32(PCRP3, (3<<PauseFlowControl)|READ_MEM32(PCRP3));
+ DEBUGK_OUT("set eth_pause=%d\n", bval);
+ }
+ break;
+ case MDIO_IOCTL_SET_MII_CLK:
+ retval = copy_from_user( (void *)&val, (void *)arg, 4);
+ if (retval == 0) {
+ if (val == 0) { //00: 25MHz at 100M mode
+ WRITE_MEM32(PCRP0, (~(0x3<<20))&READ_MEM32(PCRP0)); //force 100Mbps for port 0
+ WRITE_MEM32(P0GMIICR, (~(1<<22))&READ_MEM32(P0GMIICR)); //Turbo MII off
+ }
+ else if (val == 1) { //01: 2.5MHz at 10M mode
+ WRITE_MEM32(PCRP0, (1<<20)|((~(0x3<<20))&READ_MEM32(PCRP0))); //force 10Mbps for port 0
+ WRITE_MEM32(P0GMIICR, (~(1<<22))&READ_MEM32(P0GMIICR)); //Turbo MII off
+ }
+ else if (val == 2) { //10: 50MHz at Turbo-MII mode
+ WRITE_MEM32(PCRP0, (~(0x3<<20))&READ_MEM32(PCRP0)); //force 100Mbps for port 0
+ WRITE_MEM32(P0GMIICR, (1<<22)|READ_MEM32(P0GMIICR)); //using Turbo MII
+ }
+ //printk("set mii clk=%d\n", val);
+ }
+ break;
+ case MDIO_IOCTL_SET_SUSPEND:
+ retval = copy_from_user( (void *)&bval, (void *)arg, 1);
+ if (retval == 0) {
+ cpu_suspend_enabled = (int) bval;
+ DEBUGK_OUT("set cpu_suspend=%d\n", bval);
+ }
+ break;
+ case MDIO_IOCTL_READ_SCR:
+ val = register_read_dw(REG_SYSCR);
+ retval = copy_to_user((void *)arg, (void *)&val, sizeof(val));
+ DEBUGK_OUT("read_src src=0x%x\n", val);
+ break;
+#ifdef CONFIG_RTK_VOIP_ETHERNET_DSP_IS_DSP
+ case MDIO_IOCTL_READ_DSP_ID:
+ {
+ unsigned int id;
+ extern unsigned int Get_Ethernet_DSP_ID(void);
+ id = Get_Ethernet_DSP_ID();
+ retval = copy_to_user((void *)arg, (void *)&id, sizeof(id));
+ printk("Get DSP ID = %d\n", id);
+ break;
+ }
+#endif
+#ifdef JUMP_CMD
+ case MDIO_IOCTL_JUMP_ADDR:
+ retval = copy_from_user( (void *)&val, (void *)arg, sizeof(val));
+ if (retval == 0) {
+ extern void setup_reboot_addr(unsigned long addr);
+ extern int is_fault;
+
+ DEBUGK_OUT("jump to addr=0x%x\n", val);
+ setup_reboot_addr((unsigned long)val);
+ is_fault = 1; // cause watchdog reset
+ }
+ break;
+#endif
+ default: /* redundant, as cmd was checked against MAXNR */
+ DEBUGK_ERR("Invalid ioctl cmd [0x%x]!\n", cmd);
+ return -ENOTTY;
+ }
+ return retval;
+}
+
+#ifdef SIMULATION
+static int read_proc(char *buf, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int size = 0;
+
+ if (data_out_len > 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 <http://www.gnu.org/licenses/>.
+#
+# Author:
+# Realtek WiFi AP software team <cn_sd8@realtek.com>
+# 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