/***************************************************************************** ** FILE NAME : dwc_otg_attr.c ** PROJECT : USB Host and Device driver ** MODULES : USB Host and Device driver ** SRC VERSION : 2.0 ** DATE : 1/March/2008 ** AUTHOR : Chen, Howard based on Synopsys Original ** DESCRIPTION : The diagnostic interface will provide access to the controller ** for bringing up the hardware and testing. The Linux driver ** attributes feature will be used to provide the Linux Diagnostic ** Interface. These attributes are accessed through sysfs. ** FUNCTIONS : ** COMPILER : gcc ** REFERENCE : ** COPYRIGHT : ** Version Control Section ** ** $Author$ ** $Date$ ** $Revisions$ ** $Log$ Revision history *****************************************************************************/ /*! \file dwc_otg_attr.c \brief This file contains the interface to the Linux device attributes. */ #include #include "ifxusb_version.h" #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) #include #include #include #include #include #include #include #include /* permission constants */ #include #include "dwc_otg_plat.h" #include "dwc_otg_attr.h" #include "dwc_otg_driver.h" #ifdef DWC_IS_DEVICE #include "dwc_otg_pcd.h" #endif #ifdef DWC_IS_HOST #include "dwc_otg_hcd.h" #endif #ifndef REGSIZE #define REGSIZE 0x00001000 #endif /* * MACROs for defining sysfs attribute */ #define DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \ static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \ { \ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); \ uint32_t val; \ val = dwc_read_reg32 (_addr_); \ val = (val & (_mask_)) >> _shift_; \ return sprintf (buf, "%s = 0x%x\n", _string_, val); \ } #define DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \ static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, const char *buf, size_t count) \ { \ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); \ uint32_t set = simple_strtoul(buf, NULL, 16); \ uint32_t clear = set; \ clear = ((~clear) << _shift_) & _mask_; \ set = (set << _shift_) & _mask_; \ dev_dbg(_dev, "Storing Address=0x%08x Set=0x%08x Clear=0x%08x\n", (uint32_t)_addr_, set, clear); \ dwc_modify_reg32(_addr_, clear, set); \ return count; \ } #define DWC_OTG_DEVICE_ATTR_BITFIELD_RW(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \ DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \ DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \ DEVICE_ATTR(_otg_attr_name_,0644,_otg_attr_name_##_show,_otg_attr_name_##_store); #define DWC_OTG_DEVICE_ATTR_BITFIELD_RO(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \ DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \ DEVICE_ATTR(_otg_attr_name_,0444,_otg_attr_name_##_show,NULL); /* * MACROs for defining sysfs attribute for 32-bit registers */ #define DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_addr_,_string_) \ static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \ { \ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); \ uint32_t val; \ val = dwc_read_reg32 (_addr_); \ return sprintf (buf, "%s = 0x%08x\n", _string_, val); \ } #define DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_addr_,_string_) \ static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, const char *buf, size_t count) \ { \ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); \ uint32_t val = simple_strtoul(buf, NULL, 16); \ dev_dbg(_dev, "Storing Address=0x%08x Val=0x%08x\n", (uint32_t)_addr_, val); \ dwc_write_reg32(_addr_, val); \ return count; \ } #define DWC_OTG_DEVICE_ATTR_REG32_RW(_otg_attr_name_,_addr_,_string_) \ DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_addr_,_string_) \ DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_addr_,_string_) \ DEVICE_ATTR(_otg_attr_name_,0644,_otg_attr_name_##_show,_otg_attr_name_##_store); #define DWC_OTG_DEVICE_ATTR_REG32_RO(_otg_attr_name_,_addr_,_string_) \ DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_addr_,_string_) \ DEVICE_ATTR(_otg_attr_name_,0444,_otg_attr_name_##_show,NULL); /* * Functions for Show/Store of Attributes */ /* Show the register offset of the Register Access. */ static ssize_t regoffset_show( struct device *_dev, struct device_attribute *attr, char *buf) { dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); return snprintf(buf, sizeof("0xFFFFFFFF\n")+1,"0x%08x\n", otg_dev->reg_offset); } /* Set the register offset for the next Register Access Read/Write */ static ssize_t regoffset_store( struct device *_dev, struct device_attribute *attr, const char *buf, size_t count ) { dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); uint32_t offset = simple_strtoul(buf, NULL, 16); if (offset < REGSIZE ){ otg_dev->reg_offset = offset; }else{ dev_err( _dev, "invalid offset\n" ); } return count; } DEVICE_ATTR(regoffset, S_IRUGO|S_IWUSR, regoffset_show, regoffset_store); /* Show the value of the register at the offset in the reg_offset attribute.*/ static ssize_t regvalue_show( struct device *_dev, struct device_attribute *attr, char *buf) { dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); uint32_t val; volatile uint32_t *addr; if (otg_dev->reg_offset < REGSIZE && (otg_dev->base)){ /* Calculate the address */ addr = (uint32_t*)(otg_dev->reg_offset + (uint8_t*)otg_dev->base); val = dwc_read_reg32( addr ); return snprintf(buf, sizeof("Reg@0xFFFFFFFF = 0xFFFFFFFF\n")+1, "Reg@0x%06x = 0x%08x\n", otg_dev->reg_offset, val); }else{ dev_err(_dev, "Invalid offset (0x%0x)\n", otg_dev->reg_offset); return sprintf(buf, "invalid offset\n" ); } } /* Store the value in the register at the offset in the reg_offset attribute. */ static ssize_t regvalue_store( struct device *_dev, struct device_attribute *attr, const char *buf, size_t count ) { dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); volatile uint32_t * addr; uint32_t val = simple_strtoul(buf, NULL, 16); if (otg_dev->reg_offset != 0xFFFFFFFF && 0 != otg_dev->base){ /* Calculate the address */ addr = (uint32_t*)(otg_dev->reg_offset + (uint8_t*)otg_dev->base); dwc_write_reg32( addr, val ); }else{ dev_err(_dev, "Invalid Register Offset (0x%08x)\n", otg_dev->reg_offset); } return count; } DEVICE_ATTR(regvalue, S_IRUGO|S_IWUSR, regvalue_show, regvalue_store); /* * Attributes */ DWC_OTG_DEVICE_ATTR_BITFIELD_RO(mode,&(otg_dev->core_if->core_global_regs->gotgctl),(1<<20),20,"Mode"); DWC_OTG_DEVICE_ATTR_REG32_RW(gotgctl,&(otg_dev->core_if->core_global_regs->gotgctl),"GOTGCTL"); DWC_OTG_DEVICE_ATTR_REG32_RW(gusbcfg,&(otg_dev->core_if->core_global_regs->gusbcfg),"GUSBCFG"); DWC_OTG_DEVICE_ATTR_REG32_RW(grxfsiz,&(otg_dev->core_if->core_global_regs->grxfsiz),"GRXFSIZ"); DWC_OTG_DEVICE_ATTR_REG32_RW(gnptxfsiz,&(otg_dev->core_if->core_global_regs->gnptxfsiz),"GNPTXFSIZ"); DWC_OTG_DEVICE_ATTR_REG32_RW(gpvndctl,&(otg_dev->core_if->core_global_regs->gpvndctl),"GPVNDCTL"); DWC_OTG_DEVICE_ATTR_REG32_RW(guid,&(otg_dev->core_if->core_global_regs->guid),"GUID"); DWC_OTG_DEVICE_ATTR_REG32_RO(gsnpsid,&(otg_dev->core_if->core_global_regs->gsnpsid),"GSNPSID"); DWC_OTG_DEVICE_ATTR_REG32_RO(hptxfsiz,&(otg_dev->core_if->core_global_regs->hptxfsiz),"HPTXFSIZ"); #ifdef DWC_IS_DEVICE DWC_OTG_DEVICE_ATTR_BITFIELD_RW(devspeed,&(otg_dev->core_if->dev_if->dev_global_regs->dcfg),0x3,0,"Device Speed"); DWC_OTG_DEVICE_ATTR_BITFIELD_RO(enumspeed,&(otg_dev->core_if->dev_if->dev_global_regs->dsts),0x6,1,"Device Enumeration Speed"); #endif #ifdef DWC_IS_HOST DWC_OTG_DEVICE_ATTR_REG32_RW(hprt0,otg_dev->core_if->host_if->hprt0,"HPRT0"); DWC_OTG_DEVICE_ATTR_BITFIELD_RO(busconnected,otg_dev->core_if->host_if->hprt0,0x01,0,"Bus Connected"); #endif #ifdef DWC_IS_HOST /* Show the Bus Power status */ static ssize_t buspower_show( struct device *_dev, struct device_attribute *attr, char *buf) { dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); hprt0_data_t val; val.d32 = dwc_read_reg32 (otg_dev->core_if->host_if->hprt0); return sprintf (buf, "Bus Power = 0x%x\n", val.b.prtpwr); } /* Set the Bus Power status */ static ssize_t buspower_store( struct device *_dev, struct device_attribute *attr, const char *buf,size_t count ) { uint32_t on = simple_strtoul(buf, NULL, 16); if(on) dwc_otg_vbus_on (); else dwc_otg_vbus_off (); return count; } DEVICE_ATTR(buspower, 0644, buspower_show, buspower_store); /* Show the Bus Suspend status*/ static ssize_t bussuspend_show( struct device *_dev, struct device_attribute *attr, char *buf) { dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); hprt0_data_t val; val.d32 = dwc_read_reg32 (otg_dev->core_if->host_if->hprt0); return sprintf (buf, "Bus Suspend = 0x%x\n", val.b.prtsusp); } /* Set the Bus Suspend status*/ static ssize_t bussuspend_store( struct device *_dev, struct device_attribute *attr, const char *buf, size_t count ) { dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); uint32_t in = simple_strtoul(buf, NULL, 16); uint32_t *addr = (uint32_t *)otg_dev->core_if->host_if->hprt0; hprt0_data_t mem; mem.d32 = dwc_read_reg32(addr); mem.b.prtsusp = in; dev_dbg(_dev, "Storing Address=0x%08x Data=0x%08x\n", (uint32_t)addr, mem.d32); dwc_write_reg32(addr, mem.d32); return count; } DEVICE_ATTR(bussuspend, 0644, bussuspend_show, bussuspend_store); #endif /* Show the status of Remote Wakeup.*/ /* static ssize_t remote_wakeup_show( struct device *_dev, struct device_attribute *attr, char *buf) { #ifdef DWC_IS_DEVICE dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); dctl_data_t val; val.d32 = dwc_read_reg32( &otg_dev->core_if->dev_if->dev_global_regs->dctl); return sprintf( buf, "Remote Wakeup = %d Enabled = %d\n", val.b.rmtwkupsig, otg_dev->pcd->remote_wakeup_enable); #else return sprintf(buf, "Host Only Mode!\n"); #endif } */ /* * Initiate a remote wakeup of the host. The Device control register * Remote Wakeup Signal bit is written if the PCD Remote wakeup enable * flag is set. */ /* static ssize_t remote_wakeup_store( struct device *_dev, struct device_attribute *attr, const char *buf,size_t count ) { #ifdef DWC_IS_DEVICE uint32_t val = simple_strtoul(buf, NULL, 16); dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); if (val&1){ dwc_otg_pcd_remote_wakeup(otg_dev->pcd, 1); }else{ dwc_otg_pcd_remote_wakeup(otg_dev->pcd, 0); } #endif return count; } DEVICE_ATTR(remote_wakeup, S_IRUGO|S_IWUSR, remote_wakeup_show, remote_wakeup_store); */ /* * Dump global registers and either host or device registers (depending on the * current mode of the core). */ static ssize_t regdump_show( struct device *_dev, struct device_attribute *attr, char *buf) { #ifdef DEBUG dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); dwc_otg_dump_global_registers( otg_dev->core_if); #ifdef DWC_IS_HOST dwc_otg_dump_host_registers( otg_dev->core_if); #endif #ifdef DWC_IS_DEVICE dwc_otg_dump_dev_registers( otg_dev->core_if); #endif #endif return sprintf( buf, "Register Dump\n" ); } DEVICE_ATTR(regdump, S_IRUGO|S_IWUSR, regdump_show, 0); /* * Dump the current hcd state. */ static ssize_t hcddump_show( struct device *_dev, struct device_attribute *attr, char *buf) { #ifdef DWC_IS_HOST dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); dwc_otg_hcd_dump_state(otg_dev->hcd); #endif return sprintf( buf, "HCD Dump\n" ); } DEVICE_ATTR(hcddump, S_IRUGO|S_IWUSR, hcddump_show, 0); /* * Dump the average frame remaining at SOF. This can be used to * determine average interrupt latency. Frame remaining is also shown for * start transfer and two additional sample points. */ static ssize_t hcd_frrem_show( struct device *_dev, struct device_attribute *attr, char *buf) { #ifdef DWC_IS_HOST dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); dwc_otg_hcd_dump_frrem(otg_dev->hcd); #endif return sprintf( buf, "HCD Dump Frame Remaining\n" ); } DEVICE_ATTR(hcd_frrem, S_IRUGO|S_IWUSR, hcd_frrem_show, 0); /* * Displays the time required to read the GNPTXFSIZ register many times (the * output shows the number of times the register is read). */ #define RW_REG_COUNT 10000000 #define MSEC_PER_JIFFIE 1000/HZ static ssize_t rd_reg_test_show( struct device *_dev, struct device_attribute *attr, char *buf) { int i; int time; int start_jiffies; dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); printk("HZ %d, MSEC_PER_JIFFIE %d, loops_per_jiffy %lu\n", HZ, MSEC_PER_JIFFIE, loops_per_jiffy); start_jiffies = jiffies; for (i = 0; i < RW_REG_COUNT; i++){ dwc_read_reg32(&otg_dev->core_if->core_global_regs->gnptxfsiz); } time = jiffies - start_jiffies; return sprintf( buf, "Time to read GNPTXFSIZ reg %d times: %d msecs (%d jiffies)\n", RW_REG_COUNT, time * MSEC_PER_JIFFIE, time ); } DEVICE_ATTR(rd_reg_test, S_IRUGO|S_IWUSR, rd_reg_test_show, 0); /* * Displays the time required to write the GNPTXFSIZ register many times (the * output shows the number of times the register is written). */ static ssize_t wr_reg_test_show( struct device *_dev, struct device_attribute *attr, char *buf) { int i; int time; int start_jiffies; dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); uint32_t reg_val; printk("HZ %d, MSEC_PER_JIFFIE %d, loops_per_jiffy %lu\n", HZ, MSEC_PER_JIFFIE, loops_per_jiffy); reg_val = dwc_read_reg32(&otg_dev->core_if->core_global_regs->gnptxfsiz); start_jiffies = jiffies; for (i = 0; i < RW_REG_COUNT; i++){ dwc_write_reg32(&otg_dev->core_if->core_global_regs->gnptxfsiz, reg_val); } time = jiffies - start_jiffies; return sprintf( buf, "Time to write GNPTXFSIZ reg %d times: %d msecs (%d jiffies)\n", RW_REG_COUNT, time * MSEC_PER_JIFFIE, time); } DEVICE_ATTR(wr_reg_test, S_IRUGO|S_IWUSR, wr_reg_test_show, 0); /* * Create the device files */ void dwc_otg_attr_create (struct device *_dev) { int retval; retval = device_create_file(_dev, &dev_attr_regoffset); retval += device_create_file(_dev, &dev_attr_regvalue); retval += device_create_file(_dev, &dev_attr_mode); retval += device_create_file(_dev, &dev_attr_gotgctl); retval += device_create_file(_dev, &dev_attr_gusbcfg); retval += device_create_file(_dev, &dev_attr_grxfsiz); retval += device_create_file(_dev, &dev_attr_gnptxfsiz); retval += device_create_file(_dev, &dev_attr_gpvndctl); retval += device_create_file(_dev, &dev_attr_guid); retval += device_create_file(_dev, &dev_attr_gsnpsid); #ifdef DWC_IS_DEVICE retval += device_create_file(_dev, &dev_attr_devspeed); retval += device_create_file(_dev, &dev_attr_enumspeed); #endif #ifdef DWC_IS_HOST retval += device_create_file(_dev, &dev_attr_busconnected); retval += device_create_file(_dev, &dev_attr_buspower); retval += device_create_file(_dev, &dev_attr_bussuspend); retval += device_create_file(_dev, &dev_attr_hptxfsiz); retval += device_create_file(_dev, &dev_attr_hprt0); retval += device_create_file(_dev, &dev_attr_hcd_frrem); retval += device_create_file(_dev, &dev_attr_hcddump); #endif // retval += device_create_file(_dev, &dev_attr_remote_wakeup); retval += device_create_file(_dev, &dev_attr_regdump); if(retval != 0){ DWC_PRINT("cannot create sysfs device files.\n"); // DWC_PRINT("killing own sysfs device files!\n"); dwc_otg_attr_remove(_dev); } } /* * Remove the device files */ void dwc_otg_attr_remove (struct device *_dev) { device_remove_file(_dev, &dev_attr_regoffset); device_remove_file(_dev, &dev_attr_regvalue); device_remove_file(_dev, &dev_attr_mode); device_remove_file(_dev, &dev_attr_gotgctl); device_remove_file(_dev, &dev_attr_gusbcfg); device_remove_file(_dev, &dev_attr_grxfsiz); device_remove_file(_dev, &dev_attr_gnptxfsiz); device_remove_file(_dev, &dev_attr_gpvndctl); device_remove_file(_dev, &dev_attr_guid); device_remove_file(_dev, &dev_attr_gsnpsid); // device_remove_file(_dev, &dev_attr_remote_wakeup); device_remove_file(_dev, &dev_attr_regdump); #ifdef DWC_IS_DEVICE device_remove_file(_dev, &dev_attr_devspeed); device_remove_file(_dev, &dev_attr_enumspeed); #endif #ifdef DWC_IS_HOST device_remove_file(_dev, &dev_attr_busconnected); device_remove_file(_dev, &dev_attr_buspower); device_remove_file(_dev, &dev_attr_bussuspend); device_remove_file(_dev, &dev_attr_hptxfsiz); device_remove_file(_dev, &dev_attr_hprt0); device_remove_file(_dev, &dev_attr_hcddump); device_remove_file(_dev, &dev_attr_hcd_frrem); #endif } #endif