CHROMIUM: mwifiex: Add SDIO register debugging
authorPaul Stewart <pstew@chromium.org>
Wed, 14 Nov 2012 02:40:50 +0000 (18:40 -0800)
committerGerrit <chrome-bot@google.com>
Fri, 16 Nov 2012 05:47:16 +0000 (21:47 -0800)
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 <pstew@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/37972

drivers/net/wireless/mwifiex/cmdevt.c
drivers/net/wireless/mwifiex/debugfs.c
drivers/net/wireless/mwifiex/main.c
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/sdio.c
drivers/net/wireless/mwifiex/sdio.h

index 9c85238..a5f9317 100644 (file)
@@ -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);
 }
 
 /*
index 46e34aa..82fd85d 100644 (file)
@@ -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);
 }
 
 /*
index f8aa9da..2da7201 100644 (file)
@@ -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);
 }
 
 /*
index 4fe6617..2ee4c83 100644 (file)
@@ -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 {
index 8ac7558..8306fb2 100644 (file)
@@ -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(&reg_dbg_work))
+               return;
+
+       saved_adapter = adapter;
+       schedule_work(&reg_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(&reg_dbg_work);
        sdio_unregister_driver(&mwifiex_sdio);
 }
 
index ae955af..a4c3a09 100644 (file)
        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 {