/****************************************************************************** * FILE PURPOSE: Vlynq Linux Device Driver Source ****************************************************************************** * FILE NAME: vlynq_drv.c * * DESCRIPTION: Vlynq Linux Device Driver Source * * REVISION HISTORY: * * Date Description Author *----------------------------------------------------------------------------- * 17 July 2003 Initial Creation Anant Gole * 17 Dec 2003 Updates Sharath Kumar * 12 Oct 2004 Revamped for PF 1.0 VLYNQ Suraj Iyer * * (C) Copyright 2003, Texas Instruments, Inc *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include "pal_vlynq.h" #include "register_access.h" #include extern Uint32 wlan_mem_addr; /* WLAN memory base */ extern Uint32 wlan_reg_addr; /* WLAN register area base */ /* Exporting the symbols for the VLYNQ. */ EXPORT_SYMBOL(PAL_vlynqAddDevice); EXPORT_SYMBOL(PAL_vlynqRemoveDevice); EXPORT_SYMBOL(PAL_vlynqMapRegion); EXPORT_SYMBOL(PAL_vlynqUnMapRegion); EXPORT_SYMBOL(PAL_vlynqGetDevBase); EXPORT_SYMBOL(PAL_vlynqGetLinkStatus); EXPORT_SYMBOL(PAL_vlynqGetNumRoot); EXPORT_SYMBOL(PAL_vlynqGetRoot); EXPORT_SYMBOL(PAL_vlynqGetNext); EXPORT_SYMBOL(PAL_vlynqIsLast); EXPORT_SYMBOL(PAL_vlynqGetChainLength); EXPORT_SYMBOL(PAL_vlynqGetBaseAddr); EXPORT_SYMBOL(PAL_vlynqGetRootAtBase); EXPORT_SYMBOL(PAL_vlynqGetRootVlynq); EXPORT_SYMBOL(PAL_vlynqChainAppend); EXPORT_SYMBOL(PAL_vlynqChainUnAppend); EXPORT_SYMBOL(PAL_vlynqMapIrq); EXPORT_SYMBOL(PAL_vlynqUnMapIrq); EXPORT_SYMBOL(PAL_vlynqAddIsr); EXPORT_SYMBOL(PAL_vlynqRemoveIsr); EXPORT_SYMBOL(PAL_vlynqRootIsr); EXPORT_SYMBOL(PAL_vlynqGetIrqCount); EXPORT_SYMBOL(PAL_vlynqGetForIrq); EXPORT_SYMBOL(PAL_vlynqSetIrqPol); EXPORT_SYMBOL(PAL_vlynqGetIrqPol); EXPORT_SYMBOL(PAL_vlynqSetIrqType); EXPORT_SYMBOL(PAL_vlynqGetIrqType); EXPORT_SYMBOL(PAL_vlynqDisableIrq); EXPORT_SYMBOL(PAL_vlynqEnableIrq); EXPORT_SYMBOL(PAL_vlynqConfigClock); EXPORT_SYMBOL(PAL_vlynqInit); EXPORT_SYMBOL(PAL_vlynqCleanUp); EXPORT_SYMBOL(PAL_vlynqDump); EXPORT_SYMBOL(PAL_vlynqIoctl); EXPORT_SYMBOL(PAL_vlynqDevCbRegister); EXPORT_SYMBOL(PAL_vlynqDevCbUnregister); EXPORT_SYMBOL(PAL_vlynqDevFind); EXPORT_SYMBOL(PAL_vlynqDevGetVlynq); EXPORT_SYMBOL(PAL_vlynqDevFindIrq); EXPORT_SYMBOL(PAL_vlynqDevGetResetBit); EXPORT_SYMBOL(PAL_vlynqDevCreate); EXPORT_SYMBOL(PAL_vlynqDevDestroy); #ifdef CONFIG_WLAN_VLYNQ_TEST extern void InitDemoInterrupt( void ); #endif #define TI_VLYNQ_VERSION "0.4" #define ERRPRINT printk /* Define the max vlynq ports this driver will support. Device name strings are statically added here */ #define MAX_VLYNQ_PORTS 2 #define DAVINCI_VLYNQ_INT_NUM 31 /* The vlynq is conntected to interrupt 31 in the DaVinci interrupt controller */ #define VLYNQ_TNETW1X50_MEM_SIZE 0x00080000 /* Total size of the TNETW1150 memory */ #define VLYNQ_TNETW1X50_REG_SIZE 0x00100000 /* Size of TNETW1150 registers area, MAC+PHY */ static int vlynq_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { return 0; } static struct file_operations vlynq_fops = { owner: THIS_MODULE, ioctl: vlynq_ioctl, }; /* Vlynq device object */ static struct miscdevice vlynq_miscdev [MAX_VLYNQ_PORTS] = { { MISC_DYNAMIC_MINOR , "vlynq0", &vlynq_fops }, { MISC_DYNAMIC_MINOR , "vlynq1", &vlynq_fops }, }; static int vlynq_read_all_vlynq(char *buf, char **start, off_t offset, int count, int *eof, void *data) { return(PAL_vlynqDump(NULL, PAL_VLYNQ_DUMP_ALL_ROOT, buf, count - 80, eof)); } static int vlynq_read_registers(char *buf, char **start, off_t offset, int count, int *eof, void *data) { return(PAL_vlynqDump(data, PAL_VLYNQ_DUMP_ALL_REGS, buf, count - 80, eof)); } static int vlynq_read_raw(char *buf, char **start, off_t offset, int count, int *eof, void *data) { return(PAL_vlynqDump(data, PAL_VLYNQ_DUMP_RAW_DATA, buf, count - 80, eof)); } static int vlynq_read_status_word(char *buf, char **start, off_t offset, int count, int *eof, void *data) { return(PAL_vlynqDump(data, PAL_VLYNQ_DUMP_STS_REG, buf, count - 80, eof)); } static int vlynq_read_control_word(char *buf, char **start, off_t offset, int count, int *eof, void *data) { return(PAL_vlynqDump(data, PAL_VLYNQ_DUMP_CNTL_REG, buf, count - 80, eof)); } static int vlynq_read_tnet_die_id(char *buf, char **start, off_t offset, int count, int *eof, void *data) { unsigned int die_lo, die_hi, len; die_lo = *(unsigned long*)(wlan_reg_addr + 0x88c); die_hi = *(unsigned long*)(wlan_reg_addr + 0x890); len = sprintf(buf, "%x%x", die_hi, die_lo); *eof = 1; return len; } /* Proc read function */ static int vlynq_read_link_proc(char *buf, char **start, off_t offset, int count, int *eof, void *data) { int len = 0; int root, root_count; char *link_msg[] = {" DOWN "," UP "}; root_count = PAL_vlynqGetNumRoot(); for(root = 0; root < root_count; root++) { int hop = 0; int link; PAL_VLYNQ_HND *p_vlynq_hnd; len += sprintf(buf + len, "Vlynq Root 0:\n"); p_vlynq_hnd = PAL_vlynqGetRoot(root); if(!p_vlynq_hnd) continue; do { link = PAL_vlynqGetLinkStatus(p_vlynq_hnd); len += sprintf(buf + len, " Vlynq Bridge %d%d: %s\n", root, hop, link_msg[link]); p_vlynq_hnd = PAL_vlynqGetNext(p_vlynq_hnd); } while(link && !PAL_vlynqIsLast(p_vlynq_hnd)); } return len; } /* Proc function to display driver version */ static int vlynq_read_ver_proc(char *buf, char **start, off_t offset, int count, int *eof, void *data) { int len=0; len += sprintf(buf +len,"\nTI Linux VLYNQ Driver Version %s\n",TI_VLYNQ_VERSION); return len; } struct proc_dir_entry *vlynq_proc_root; /* Wrapper for vlynq ISR */ static irqreturn_t lnx_vlynq_root_isr(int irq, void * arg, struct pt_regs *regs) { PAL_vlynqRootIsr(arg); return IRQ_HANDLED; } extern void Davinci_Vlynq_Hardware_Init(void); int vlynq_init_module(void) { int root_count, root; int instance_count = 1; gpio_control_hw (non_wlan_reset, 1); #ifdef CONFIG_WLAN_VLYNQ_TEST printk("Creating reg_accesss proc entry....\n"); ra_init(); #endif /* Call the vlynq initialization */ printk("********************* VLYNQ Initialization ************************\n"); printk("Starting the DaVinci EVM board vlynq initialization...\n"); /* Iniialize the Morpheus board VLYNQ module */ Davinci_Vlynq_Hardware_Init(); wlan_mem_addr = (unsigned int)ioremap(wlan_mem_addr, VLYNQ_TNETW1X50_MEM_SIZE); wlan_reg_addr = (unsigned int)ioremap(wlan_reg_addr, VLYNQ_TNETW1X50_REG_SIZE); printk("WLAN memory is remapped to address %x \n",wlan_mem_addr); printk("WLAN registers are remapped to address %x \n",wlan_reg_addr); /* If num of configured vlynq ports > supported by driver return error */ if (instance_count > MAX_VLYNQ_PORTS) { ERRPRINT("ERROR: vlynq_init_module(): Max %d supported while configured for %d.\n", MAX_VLYNQ_PORTS, instance_count); return (-1); } vlynq_proc_root = proc_mkdir("bus/vlynq",NULL); if(!vlynq_proc_root) return -ENOMEM; create_proc_read_entry("vlynq_data", 0, vlynq_proc_root, vlynq_read_all_vlynq, NULL); create_proc_read_entry("tnet_die_id", 0, vlynq_proc_root, vlynq_read_tnet_die_id, NULL); root_count = PAL_vlynqGetNumRoot( ); if(!root_count) return (-1); for(root = 0; root < root_count; root++) { PAL_VLYNQ_HND *p_root_vlynq, *p_vlynq_hnd = PAL_vlynqGetRoot(root); char name[50]; int hop = 0, irq; unsigned int base_addr; if(!p_vlynq_hnd) continue; if(!PAL_vlynqGetLinkStatus(p_vlynq_hnd)) continue; if(PAL_vlynqGetBaseAddr(p_vlynq_hnd, &base_addr)) continue; printk("The vlynq controller is located at address %x\n", base_addr); if(base_addr) { irq = DAVINCI_VLYNQ_INT_NUM; printk("The vlynq is using interrupt %d ....\n",irq); } else continue; p_root_vlynq = p_vlynq_hnd; for(; ;p_vlynq_hnd = PAL_vlynqGetNext(p_vlynq_hnd)) { sprintf(name, "vlynq%d%d_regs", root, hop); create_proc_read_entry(name, 0, vlynq_proc_root, vlynq_read_registers, p_vlynq_hnd); sprintf(name, "vlynq%d%d_raw", root, hop); create_proc_read_entry(name, 0, vlynq_proc_root, vlynq_read_raw, p_vlynq_hnd); sprintf(name, "vlynq%d%d_status", root, hop); create_proc_read_entry(name, 0, vlynq_proc_root, vlynq_read_status_word, p_vlynq_hnd); sprintf(name, "vlynq%d%d_control", root, hop); create_proc_read_entry(name, 0, vlynq_proc_root, vlynq_read_control_word, p_vlynq_hnd); hop++; if(PAL_vlynqIsLast(p_vlynq_hnd)) break; } if(misc_register(&vlynq_miscdev[root]) < -1) continue; request_irq(irq, lnx_vlynq_root_isr, 0, vlynq_miscdev[root].name, p_root_vlynq); printk("Installed misc driver %s: it handles vlynq bridge%d with %d hop(s).\n", vlynq_miscdev[root].name, root, hop); #ifdef CONFIG_WLAN_VLYNQ_TEST InitDemoInterrupt(); #endif } /* Creating proc entry for the devices */ create_proc_read_entry("avalanche/vlynq_link", 0, NULL, vlynq_read_link_proc, NULL); create_proc_read_entry("avalanche/vlynq_ver", 0, NULL, vlynq_read_ver_proc, NULL); return 0; } void vlynq_cleanup_module(void) { /* * Note: This is not complete. It is ok for now as this * driver is built into the kernel. */ int unit = 0; for (unit = 0; unit < 1; unit++) { printk("vlynq_cleanup_module(): Unregistring misc device %s\n",vlynq_miscdev[unit].name); misc_deregister(&vlynq_miscdev[unit]); } remove_proc_entry("avalanche/vlynq_link", NULL); remove_proc_entry("avalanche/vlynq_ver", NULL); gpio_control_hw (non_wlan_reset, 1); } module_init(vlynq_init_module); module_exit(vlynq_cleanup_module);