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, \
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.
MWIFIEX_DFS_ADD_FILE(getlog);
MWIFIEX_DFS_ADD_FILE(regrdwr);
MWIFIEX_DFS_ADD_FILE(rdeeprom);
+ MWIFIEX_DFS_ADD_FILE(sdio_reg_dbg_trigger);
}
/*
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,
.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,
};
/*
/* Set the flag as user is removing this module. */
user_rmmod = 1;
+ cancel_work_sync(®_dbg_work);
sdio_unregister_driver(&mwifiex_sdio);
}