From: Paul Stewart Date: Wed, 14 Nov 2012 02:40:50 +0000 (-0800) Subject: CHROMIUM: mwifiex: Add SDIO register debugging X-Git-Url: http://git.cascardo.eti.br/?a=commitdiff_plain;h=4fd53d14d4acc3d76461ebd55a83fe664b93f22b;p=cascardo%2Flinux.git CHROMIUM: mwifiex: Add SDIO register debugging This patch retrieves firmware/hardware status upon tx watchdog timeout or command timeout. Hopefully with this debug patch we can get some clues on what is causing the timeout issue. BUG=chrome-os-partner:15129 TEST=Boot, connect to network Change-Id: I058fe07746e6a1ccd8debde0c0d3d8debd700502 Signed-off-by: Paul Stewart Reviewed-on: https://gerrit.chromium.org/gerrit/37972 --- diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index 9c85238fd19c..a5f9317df248 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -868,6 +868,9 @@ mwifiex_cmd_timeout_func(unsigned long function_context) } if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) mwifiex_init_fw_complete(adapter); + + if (adapter->if_ops.reg_dbg) + adapter->if_ops.reg_dbg(adapter); } /* diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c index 46e34aa65d1c..82fd85de55cd 100644 --- a/drivers/net/wireless/mwifiex/debugfs.c +++ b/drivers/net/wireless/mwifiex/debugfs.c @@ -659,6 +659,31 @@ done: return ret; } +/* + * Proc sdio register dump trigger read handler. + * + * This function is called when the 'sdio_reg_dbg_trigger' file is opened + * for reading + * + * This function can be used to trigger an SDIO register debug register dump. + * Note that this will cause the register dump to appear in printks, rather + * than than being read out of the debugfs file itself. + */ +static ssize_t +mwifiex_sdio_reg_dbg_trigger_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = + (struct mwifiex_private *) file->private_data; + + if (!priv->adapter->if_ops.reg_dbg) + return -EIO; + + priv->adapter->if_ops.reg_dbg(priv->adapter); + return 0; +} + + #define MWIFIEX_DFS_ADD_FILE(name) do { \ if (!debugfs_create_file(#name, 0644, priv->dfs_dev_dir, \ @@ -691,6 +716,7 @@ MWIFIEX_DFS_FILE_READ_OPS(debug); MWIFIEX_DFS_FILE_READ_OPS(getlog); MWIFIEX_DFS_FILE_OPS(regrdwr); MWIFIEX_DFS_FILE_OPS(rdeeprom); +MWIFIEX_DFS_FILE_READ_OPS(sdio_reg_dbg_trigger); /* * This function creates the debug FS directory structure and the files. @@ -712,6 +738,7 @@ mwifiex_dev_debugfs_init(struct mwifiex_private *priv) MWIFIEX_DFS_ADD_FILE(getlog); MWIFIEX_DFS_ADD_FILE(regrdwr); MWIFIEX_DFS_ADD_FILE(rdeeprom); + MWIFIEX_DFS_ADD_FILE(sdio_reg_dbg_trigger); } /* diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index f8aa9daa3797..2da72018e607 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -567,6 +567,9 @@ mwifiex_tx_timeout(struct net_device *dev) jiffies, priv->bss_type, priv->bss_num); mwifiex_set_trans_start(dev); priv->num_tx_timeout++; + + if (priv->adapter->if_ops.reg_dbg) + priv->adapter->if_ops.reg_dbg(priv->adapter); } /* diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 4fe6617941ef..2ee4c83ab8f7 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -556,6 +556,7 @@ struct mwifiex_if_ops { void (*cleanup_mpa_buf) (struct mwifiex_adapter *); int (*cmdrsp_complete) (struct mwifiex_adapter *, struct sk_buff *); int (*event_complete) (struct mwifiex_adapter *, struct sk_buff *); + void (*reg_dbg) (struct mwifiex_adapter *); }; struct mwifiex_adapter { diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 8ac7558d7c80..8306fb21e381 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -1742,6 +1742,104 @@ mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port) port, card->mp_data_port_mask); } +/* + * This function reads and displays SDIO registers for debugging. + */ +static struct mwifiex_adapter *saved_adapter; +static void mwifiex_sdio_reg_dbg_worker(struct work_struct *work) +{ + struct mwifiex_adapter *adapter = saved_adapter; + struct sdio_mmc_card *card = adapter->card; + struct sdio_func *func = card->func; + struct sdio_reg_dbg cccr = { + "CCCR", 0, 0, 10, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + }; + struct sdio_reg_dbg fn1 = { + "FN1", 1, 0, 10, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + }; + struct sdio_reg_dbg fn1_status = { + "FN1 Status", 1, 0, 5, { + HOST_INT_STATUS_REG, + CARD_STATUS_REG, + HOST_INTERRUPT_MASK_REG, + CARD_INTERRUPT_STATUS_REG, + CARD_INTERRUPT_RSR_REG, + }, + }; + struct sdio_reg_dbg fn1_scratch = { + "FN1 Scratch", 1, 100, 11, { + CARD_FW_STATUS0_REG, + CARD_FW_STATUS0_REG + 1, + CARD_FW_STATUS0_REG + 2, + CARD_FW_STATUS0_REG + 3, + CARD_FW_STATUS0_REG + 4, + CARD_FW_STATUS0_REG + 5, + CARD_FW_STATUS0_REG + 6, + CARD_FW_STATUS0_REG + 7, + CARD_FW_STATUS0_REG + 8, + CARD_FW_STATUS0_REG + 9, + CARD_FW_STATUS0_REG + 10, + }, + }; + struct sdio_reg_dbg *regs[] = { + &cccr, + &fn1, + &fn1_status, + &fn1_scratch, + /* read fn1_scratch again for any changes */ + &fn1_scratch, + NULL + }; + int n, i, ret = 0; + u32 reg; + + sdio_claim_host(func); + + for (n = 0; regs[n]; n++) { + char buf[REG_DBG_MAX_NUM*5+1]; + char *p = buf; + + mdelay(regs[n]->delay); + + memset(buf, 0, sizeof(buf)); + for (i = 0; i < regs[n]->num_regs; i++) + p += sprintf(p, "%04X ", regs[n]->reg[i]); + p += sprintf(p, "\n"); + + dev_info(adapter->dev, "%s: %s", regs[n]->name, buf); + + memset(buf, 0, sizeof(buf)); + p = buf; + for (i = 0; i < regs[n]->num_regs; i++) { + reg = regs[n]->reg[i]; + regs[n]->val[i] = regs[n]->fn ? + sdio_readb(func, reg, &ret) : + sdio_f0_readb(func, reg, &ret); + if (!ret) + p += sprintf(p, "%04X ", regs[n]->val[i]); + else + p += sprintf(p, "ERR "); + } + p += sprintf(p, "\n"); + + dev_info(adapter->dev, "%s: %s", regs[n]->name, buf); + } + + sdio_release_host(func); +} +static DECLARE_WORK(reg_dbg_work, mwifiex_sdio_reg_dbg_worker); + +static void mwifiex_sdio_reg_dbg(struct mwifiex_adapter *adapter) +{ + if (work_pending(®_dbg_work)) + return; + + saved_adapter = adapter; + schedule_work(®_dbg_work); +} + static struct mwifiex_if_ops sdio_ops = { .init_if = mwifiex_init_sdio, .cleanup_if = mwifiex_cleanup_sdio, @@ -1760,6 +1858,7 @@ static struct mwifiex_if_ops sdio_ops = { .cleanup_mpa_buf = mwifiex_cleanup_mpa_buf, .cmdrsp_complete = mwifiex_sdio_cmdrsp_complete, .event_complete = mwifiex_sdio_event_complete, + .reg_dbg = mwifiex_sdio_reg_dbg, }; /* @@ -1797,6 +1896,7 @@ mwifiex_sdio_cleanup_module(void) /* Set the flag as user is removing this module. */ user_rmmod = 1; + cancel_work_sync(®_dbg_work); sdio_unregister_driver(&mwifiex_sdio); } diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h index ae955af6f13c..a4c3a09beb32 100644 --- a/drivers/net/wireless/mwifiex/sdio.h +++ b/drivers/net/wireless/mwifiex/sdio.h @@ -252,6 +252,15 @@ a->mpa_rx.start_port = 0; \ } while (0) +#define REG_DBG_MAX_NUM 16 +struct sdio_reg_dbg { + char name[REG_DBG_MAX_NUM]; + u8 fn; + int delay; + int num_regs; + u32 reg[REG_DBG_MAX_NUM]; + u8 val[REG_DBG_MAX_NUM]; +}; /* data structure for SDIO MPA TX */ struct mwifiex_sdio_mpa_tx {