#include <linux/module.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/delay.h> #include <bspchip.h> #include "rtk_serdes.h" #define CHECK_INTERVAL 1000 /*1000 msecs*/ unsigned int DEBUG_LEVEL = 0; struct timer_list timer_serdes_link; static void check_serdes_status(void); static int rtk_serdes_nway_status(void) { unsigned int isNwayOn = 0; DBG_PRK("Check SERDES NWAY status\n"); REG32(SYSREG_SDS_TOP1) = 0x08020000; mdelay(10); REG32(SYSREG_SDS_TOP0) = 0xc0020000; mdelay(10); mb(); REG32(SYSREG_SDS_TOP0) = 0xd0020000; mdelay(10); mb(); REG32(SYSREG_SDS_TOP0) = 0xc0020000; mdelay(10); isNwayOn = (REG32(SYSREG_SDS_TOP1) >> 8)& 0xf; return isNwayOn; } static void rtk_serdes_disable_nway(void) { DBG_PRK("Disable SERDES NWAY\n"); REG32(SYSREG_SDS_TOP1) = 0x080271e0; mdelay(10); REG32(SYSREG_SDS_TOP0) = 0xc0020001; mdelay(10); mb(); REG32(SYSREG_SDS_TOP0) = 0xd0020001; mdelay(10); mb(); REG32(SYSREG_SDS_TOP0) = 0xc0020001; mdelay(10); return; } static void rtk_serdes_enable_nway(void) { DBG_PRK("enable SERDES NWAY\n"); REG32(SYSREG_SDS_TOP1) = 0x080270e0; mdelay(10); REG32(SYSREG_SDS_TOP0) = 0xc0020001; mdelay(10); mb(); REG32(SYSREG_SDS_TOP0) = 0xd0020001; mdelay(10); mb(); REG32(SYSREG_SDS_TOP0) = 0xc0020001; mdelay(10); return; } static int rtk_serdes_check_link(void) { unsigned int status=0; REG32(SYSREG_SDS_TOP1) = 0x083d0000; mdelay(10); REG32(SYSREG_SDS_TOP0) = 0xc0020000; mdelay(10); mb(); REG32(SYSREG_SDS_TOP0) = 0xd0020000; mdelay(10); mb(); REG32(SYSREG_SDS_TOP0) = 0xc0020000; mdelay(10); status = REG32(SYSREG_SDS_TOP1) & 0xfff; return status; } static int serdes_write( struct file *filp, const char *buff,unsigned long len, void *data ) { char tmpbuf[512]; char *strptr; char *cmdptr; char *sg; unsigned int status = 0; if (buff && !copy_from_user(tmpbuf, buff, len)) { tmpbuf[len] = '\0'; strptr=tmpbuf; if(strlen(strptr)==0) { goto errout; } cmdptr = strsep(&strptr," "); if (cmdptr==NULL) { goto errout; } /*parse command*/ if(strncmp(cmdptr, "check",5) == 0) { sg = strsep(&strptr," "); if(strncmp(sg, "link",4)==0){ printk("Check SERDES link status !!\n"); status = rtk_serdes_check_link(); if(status == 0x111){ printk("SERDES Link!\n"); } else { printk("SERDES Link failed, status = 0x%X\n",status); } } else if(strncmp(sg, "pinmux",5)==0){ printk("Check SERDES registers!!\n"); printk("REG(0xb8000100) = 0x%08X\n",REG32(0xb8000100)); printk("bit[31] = %x. Serdes %s\nbit[30] = %x. Select port %s\n" , (REG32(0xb8000100)&(1<<31))>>31, (REG32(0xb8000100)&(1<<31))? "enable":"disable" , (REG32(0xb8000100)&(1<<30))>>30, (REG32(0xb8000100)&(1<<30))? "4":"5"); } else if(strncmp(sg, "nway",4)==0){ printk("Check SERDES NWAY status\n"); status = rtk_serdes_nway_status(); if( status == 0) printk("SERDES NWAY is enabled, auto mode\n"); else printk("SERDES NWAY is disabled, force mode\n"); } } if(strncmp(cmdptr, "dbg_level",9) == 0) { sg = strsep(&strptr," "); if(strncmp(sg, "1",1)==0){ DEBUG_LEVEL = 1; } else if(strncmp(sg, "0",1)==0){ DEBUG_LEVEL = 0; } } if(strncmp(cmdptr, "auto",4) == 0) { sg = strsep(&strptr," "); if(strncmp(sg, "1",1)==0){/*enable auto mode*/ DBG_PRK("Enable SERDES NWAY auto detection !!\n"); setup_timer(&timer_serdes_link, check_serdes_status, 0); mod_timer(&timer_serdes_link, jiffies + msecs_to_jiffies(CHECK_INTERVAL)); } else if(strncmp(sg, "0",1)==0){/*disable auto mode*/ if(timer_pending(&timer_serdes_link)) { /*Delete timer*/ DBG_PRK("Disable SERDES auto detection, set SERDES in force mode\n"); del_timer(&timer_serdes_link); rtk_serdes_disable_nway(); } } } } return len; errout: printk("error input\n"); return len; } static int serdes_read(struct seq_file *seq, void *offset) { seq_printf(seq, "Usage:\n"); seq_printf(seq, "Disable/Enable auto detect: #echo auto 0/1 > /proc/realtek/serdes\n"); seq_printf(seq, "Check link status: #echo check link > /proc/realtek/serdes\n"); seq_printf(seq, "Check SERDES port: #echo check pinmux > /proc/realtek/serdes\n"); seq_printf(seq, "Check SERDES NWAY: #echo check nway > /proc/realtek/serdes\n"); seq_printf(seq, "Disable/Enable debug: #echo dbg_level 0/1 > /proc/realtek/serdes\n"); return 0; } static ssize_t write_proc_serdes(struct file *file, const char __user * userbuf, size_t count, loff_t * off) { return serdes_write(file, userbuf, count, NULL); } static int read_proc_open_serdes(struct inode *inode, struct file *file) { return(single_open(file, serdes_read, NULL)); } static const struct file_operations serdes_ops = { .open = read_proc_open_serdes, .read = seq_read, .write = write_proc_serdes, .llseek = seq_lseek, .release = single_release, }; extern struct proc_dir_entry *realtek_proc; static struct proc_dir_entry *serdes_status; static unsigned int try_times = 0; static void check_serdes_status(void) { unsigned int status = 0; status = rtk_serdes_check_link(); if(status == 0x111) { try_times = 0; DBG_PRK("SERDES successfully link-up, reset try_times\n"); } else if(status == 0x101) { if(try_times >= 3) { DBG_PRK("SERDES NWAY may not sync with link-partner in auto mode, switch to force mode!\n"); if(rtk_serdes_nway_status() == 0) { rtk_serdes_disable_nway(); try_times = 0; } } try_times++; } else if(status == 0x100 || status == 0x001 || status == 0x011) { DBG_PRK("SERDES may not correctly configure, status:0x%03x\n", status); try_times = 0; } else if(status == 0x000) { rtk_serdes_enable_nway(); DBG_PRK("SERDES is out of connection, reset NWAY to auto mode\n"); try_times = 0; } mod_timer(&timer_serdes_link, jiffies + msecs_to_jiffies(CHECK_INTERVAL)); return; } static int __init rtk_serdes_init(void) { int ret = -1; serdes_status = proc_create_data("serdes", 0644, realtek_proc, &serdes_ops, NULL); if(serdes_status == NULL) { printk("can't create proc entry for serdes_status\n"); goto ERROR; } if((REG32(0xb8000100)>>31)&0x1){/*Set switch port4 in force mode*/ REG32(0xbb804114) = 0x52f7003f; } return 0; ERROR: return ret; } static void __exit rtk_serdes_exit(void) { remove_proc_entry("SERDES",realtek_proc); return; } module_init(rtk_serdes_init); module_exit(rtk_serdes_exit); MODULE_DESCRIPTION("Realtek SERDES Module"); MODULE_AUTHOR("Memphis <memphis.chung@realtek.com>"); MODULE_LICENSE("GPL");