/* ========================================================================= * The Synopsys DWC ETHER QOS Software Driver and documentation (hereinafter * "Software") is an unsupported proprietary work of Synopsys, Inc. unless * otherwise expressly agreed to in writing between Synopsys and you. * * The Software IS NOT an item of Licensed Software or Licensed Product under * any End User Software License Agreement or Agreement for Licensed Product * with Synopsys or any supplement thereto. Permission is hereby granted, * free of charge, to any person obtaining a copy of this software annotated * with this license and the Software, to deal in the Software without * restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, subject * to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * ========================================================================= */ #include #include #include "DWC_ETH_QOS_yheader.h" #include "DWC_ETH_QOS_yregacc.h" typedef enum { DWC_DBG_REG_LOW, DWC_DBG_REG_HIGH, DWC_DBG_REG_WR_VAL } dwc_dbg_token_type_t; typedef struct { struct dentry *dir; uint32_t reg_lo, reg_hi; } DWC_ETH_QOS_debug_t; #define DWC_DBG_MAX_ARGS 3 #define DWC_DBG_MAX_BUF_SIZE 8192 static DWC_ETH_QOS_debug_t dwc_dbg_data; static uint32_t dwc_dbg_reg_snprint(DWC_ETH_QOS_debug_t *dwc_dbg, char *buf) { uint32_t reg_lo, reg_hi, value, len = 0, size = DWC_DBG_MAX_BUF_SIZE; reg_lo = dwc_dbg->reg_lo & (~0x3); reg_hi = (dwc_dbg->reg_hi + 0x3) & (~0x3); while(reg_lo <= reg_hi && len < size) { value = DWC_REG_RD(reg_lo); len += snprintf(buf + len, size - len, "REGISTER[0x%08x]\tVALUE[0x%08x]\n", reg_lo, value); reg_lo += 0x4; } return ((len < size)? len : size); } static void dwc_dbg_reg_printk(DWC_ETH_QOS_debug_t *dwc_dbg) { uint32_t reg_lo, reg_hi, value; reg_lo = dwc_dbg->reg_lo & (~0x3); reg_hi = (dwc_dbg->reg_hi + 0x3) & (~0x3); while(reg_lo <= reg_hi) { value = DWC_REG_RD(reg_lo); printk(KERN_INFO "REGISTER[0x%08x]\tVALUE[0x%08x]\n", reg_lo, value); reg_lo += 0x4; } } static void dwc_dbg_reg_write(DWC_ETH_QOS_debug_t *dwc_dbg, uint32_t value) { uint32_t reg_lo, reg_hi; reg_lo = dwc_dbg->reg_lo & (~0x3); reg_hi = (dwc_dbg->reg_hi + 0x3) & (~0x3); DBGPR("[%s] reg_lo(0x%08x) reg_hi(0x%08x)\n", __FUNCTION__, reg_lo, reg_hi); while(reg_lo <= reg_hi) { DWC_REG_WR(reg_lo, value); reg_lo += 0x4; } } /* * Parse arguments: "REG_LOW<-REG_HIGH> " */ static int dwc_dbg_parse_args(char *buf, uint32_t buf_len, DWC_ETH_QOS_debug_t *dwc_dbg, uint32_t *wr_val, bool *wr_val_present) { char *ch = NULL, *cur = NULL; dwc_dbg_token_type_t cur_type = DWC_DBG_REG_LOW; int ret = -EINVAL, argc = 0; bool in_token = false; buf[buf_len-1] = '\0'; DBGPR("--> dwc_dbg_parse_args - Arguments = %s\n", buf); for (ch = buf; argc < DWC_DBG_MAX_ARGS; ch++) { if (*ch == '\0') argc = DWC_DBG_MAX_ARGS; if (in_token) { if (*ch == '\t' || *ch == ' ' || *ch == '\0') { *ch = '\0'; if (cur_type == DWC_DBG_REG_LOW) { dwc_dbg->reg_lo = simple_strtoul(cur, NULL, 16); dwc_dbg->reg_hi = dwc_dbg->reg_lo; DBGPR("reg_lo = 0x%08x\n", dwc_dbg->reg_lo); cur_type = DWC_DBG_REG_WR_VAL; } else if (cur_type == DWC_DBG_REG_HIGH) { dwc_dbg->reg_hi = simple_strtoul(cur, NULL, 16); DBGPR("reg_hi = 0x%08x\n", dwc_dbg->reg_hi); cur_type = DWC_DBG_REG_WR_VAL; } else { *wr_val = simple_strtoul(cur, NULL, 16); *wr_val_present = true; DBGPR("wr_val = 0x%08x\n", *wr_val); } in_token = false; argc++; ret = 0; } else if (*ch == '-') { *ch = '\0'; BUG_ON(cur_type != DWC_DBG_REG_LOW); dwc_dbg->reg_lo = simple_strtoul(cur, NULL, 16); DBGPR("reg_lo = 0x%08x\n", dwc_dbg->reg_lo); cur_type = DWC_DBG_REG_HIGH; in_token = false; argc++; ret = 0; } else if (!isxdigit(*ch) && *ch != 'x') { printk(KERN_ALERT "Invalid parameter(%c)!\n", *ch); ret = -EINVAL; argc = DWC_DBG_MAX_ARGS; } } else if (isxdigit(*ch)) { DBGPR("Starting token(%c)\n", *ch); cur = ch; in_token = true; } else if (*ch != '\t' && *ch != ' ' && *ch != '\0') { printk(KERN_ALERT "Invalid parameter(%c)!\n", *ch); ret = -EINVAL; argc = DWC_DBG_MAX_ARGS; } } DBGPR("<-- dwc_dbg_parse_args\n"); return ret; } static ssize_t dwc_dbg_write(struct file *file, const char __user * userbuf, size_t count, loff_t * ppos) { DWC_ETH_QOS_debug_t *dwc_dbg = (DWC_ETH_QOS_debug_t *)file->private_data; char *buf = NULL; int ret = count; uint32_t wr_val = 0; bool wr_val_present = false; DBGPR("--> dwc_dbg_write - count(%d)\n", count); buf = kmalloc(count, GFP_KERNEL); if (!buf) { ret = -ENOMEM; } else if (copy_from_user(buf, userbuf, count)) { ret = -EFAULT; } else if (dwc_dbg_parse_args(buf, count, dwc_dbg, &wr_val, &wr_val_present)) { ret = -EINVAL; } else if (wr_val_present) { dwc_dbg_reg_write(dwc_dbg, wr_val); } else { dwc_dbg_reg_printk(dwc_dbg); } if(buf) kfree(buf); DBGPR("<-- dwc_dbg_write\n"); return ret; } static ssize_t dwc_dbg_read(struct file *file, char __user * userbuf, size_t count, loff_t * ppos) { DWC_ETH_QOS_debug_t *dwc_dbg = (DWC_ETH_QOS_debug_t *)file->private_data; char *buf = NULL; int ret = 0; uint32_t len = 0; DBGPR("--> dwc_dbg_read\n"); buf = kmalloc(DWC_DBG_MAX_BUF_SIZE, GFP_KERNEL); if (!buf) { ret = -ENOMEM; } else { len = dwc_dbg_reg_snprint(dwc_dbg, buf); ret = simple_read_from_buffer(userbuf, count, ppos, buf, len); kfree(buf); } DBGPR("<-- dwc_dbg_read\n"); return ret; } static const struct file_operations dwc_dbg_fops = { .owner = THIS_MODULE, .open = simple_open, .read = dwc_dbg_read, .write = dwc_dbg_write, }; int create_debug_files(void) { struct dentry *dwc_reg; int ret = 0; DBGPR("--> create_debug_files\n"); memset(&dwc_dbg_data, 0, sizeof(DWC_ETH_QOS_debug_t)); dwc_dbg_data.dir = debugfs_create_dir("DWC_ETH_QOS", NULL); if (dwc_dbg_data.dir == NULL) { printk(KERN_ALERT "error creating directory: DWC_ETH_QOS\n"); ret = -ENODEV; } dwc_reg = debugfs_create_file("registers", 744, dwc_dbg_data.dir, &dwc_dbg_data, &dwc_dbg_fops); if (dwc_reg == NULL) { printk(KERN_INFO "error creating file: registers\n"); remove_debug_files(); ret = -ENODEV; } DBGPR("<-- create_debug_files\n"); return ret; } void remove_debug_files(void) { DBGPR("--> remove_debug_files\n"); debugfs_remove_recursive(dwc_dbg_data.dir); DBGPR("<-- remove_debug_files\n"); }