cxgb4: Add support for cim_la entry in debugfs
authorHariprasad Shenai <hariprasad@chelsio.com>
Wed, 7 Jan 2015 03:18:01 +0000 (08:48 +0530)
committerDavid S. Miller <davem@davemloft.net>
Fri, 9 Jan 2015 03:39:10 +0000 (19:39 -0800)
The CIM LA captures the embedded processor’s internal state. Optionally, it can
also trace the flow of data in and out of the embedded processor. Therefore, the
CIM LA output contains detailed information of what code the embedded processor
executed prior to the CIM LA capture.

Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.h
drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
drivers/net/ethernet/chelsio/cxgb4/t4_hw.h
drivers/net/ethernet/chelsio/cxgb4/t4_regs.h

index 73b1f3aea0926fdbe534bce33554c3b516a5bdb8..46cd506120d30f19367c5f4085111e98e589a2d2 100644 (file)
@@ -304,6 +304,8 @@ struct adapter_params {
        struct devlog_params devlog;
        enum pcie_memwin drv_memwin;
 
+       unsigned int cim_la_size;
+
        unsigned int sf_size;             /* serial flash size in bytes */
        unsigned int sf_nsec;             /* # of flash sectors */
        unsigned int sf_fw_start;         /* start of FW image in flash */
@@ -1034,6 +1036,11 @@ int t4_mc_read(struct adapter *adap, int idx, u32 addr, __be32 *data,
               u64 *parity);
 int t4_edc_read(struct adapter *adap, int idx, u32 addr, __be32 *data,
                u64 *parity);
+int t4_cim_read(struct adapter *adap, unsigned int addr, unsigned int n,
+               unsigned int *valp);
+int t4_cim_write(struct adapter *adap, unsigned int addr, unsigned int n,
+                const unsigned int *valp);
+int t4_cim_read_la(struct adapter *adap, u32 *la_buf, unsigned int *wrptr);
 const char *t4_get_port_type_description(enum fw_port_type port_type);
 void t4_get_port_stats(struct adapter *adap, int idx, struct port_stats *p);
 void t4_read_mtu_tbl(struct adapter *adap, u16 *mtus, u8 *mtu_log);
index 1d3d1c590808687fc7c35c3eb8b0982c46ed6f28..0f7b23f15810a4e4748b08951dcb9397f785cc3e 100644 (file)
 #include "cxgb4_debugfs.h"
 #include "l2t.h"
 
-/* Firmware Device Log dump.
- */
+/* generic seq_file support for showing a table of size rows x width. */
+static void *seq_tab_get_idx(struct seq_tab *tb, loff_t pos)
+{
+       pos -= tb->skip_first;
+       return pos >= tb->rows ? NULL : &tb->data[pos * tb->width];
+}
+
+static void *seq_tab_start(struct seq_file *seq, loff_t *pos)
+{
+       struct seq_tab *tb = seq->private;
+
+       if (tb->skip_first && *pos == 0)
+               return SEQ_START_TOKEN;
+
+       return seq_tab_get_idx(tb, *pos);
+}
+
+static void *seq_tab_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+       v = seq_tab_get_idx(seq->private, *pos + 1);
+       if (v)
+               ++*pos;
+       return v;
+}
+
+static void seq_tab_stop(struct seq_file *seq, void *v)
+{
+}
+
+static int seq_tab_show(struct seq_file *seq, void *v)
+{
+       const struct seq_tab *tb = seq->private;
+
+       return tb->show(seq, v, ((char *)v - tb->data) / tb->width);
+}
+
+static const struct seq_operations seq_tab_ops = {
+       .start = seq_tab_start,
+       .next  = seq_tab_next,
+       .stop  = seq_tab_stop,
+       .show  = seq_tab_show
+};
+
+struct seq_tab *seq_open_tab(struct file *f, unsigned int rows,
+                            unsigned int width, unsigned int have_header,
+                            int (*show)(struct seq_file *seq, void *v, int i))
+{
+       struct seq_tab *p;
+
+       p = __seq_open_private(f, &seq_tab_ops, sizeof(*p) + rows * width);
+       if (p) {
+               p->show = show;
+               p->rows = rows;
+               p->width = width;
+               p->skip_first = have_header != 0;
+       }
+       return p;
+}
+
+static int cim_la_show(struct seq_file *seq, void *v, int idx)
+{
+       if (v == SEQ_START_TOKEN)
+               seq_puts(seq, "Status   Data      PC     LS0Stat  LS0Addr "
+                        "            LS0Data\n");
+       else {
+               const u32 *p = v;
+
+               seq_printf(seq,
+                          "  %02x  %x%07x %x%07x %08x %08x %08x%08x%08x%08x\n",
+                          (p[0] >> 4) & 0xff, p[0] & 0xf, p[1] >> 4,
+                          p[1] & 0xf, p[2] >> 4, p[2] & 0xf, p[3], p[4], p[5],
+                          p[6], p[7]);
+       }
+       return 0;
+}
+
+static int cim_la_show_3in1(struct seq_file *seq, void *v, int idx)
+{
+       if (v == SEQ_START_TOKEN) {
+               seq_puts(seq, "Status   Data      PC\n");
+       } else {
+               const u32 *p = v;
+
+               seq_printf(seq, "  %02x   %08x %08x\n", p[5] & 0xff, p[6],
+                          p[7]);
+               seq_printf(seq, "  %02x   %02x%06x %02x%06x\n",
+                          (p[3] >> 8) & 0xff, p[3] & 0xff, p[4] >> 8,
+                          p[4] & 0xff, p[5] >> 8);
+               seq_printf(seq, "  %02x   %x%07x %x%07x\n", (p[0] >> 4) & 0xff,
+                          p[0] & 0xf, p[1] >> 4, p[1] & 0xf, p[2] >> 4);
+       }
+       return 0;
+}
+
+static int cim_la_open(struct inode *inode, struct file *file)
+{
+       int ret;
+       unsigned int cfg;
+       struct seq_tab *p;
+       struct adapter *adap = inode->i_private;
+
+       ret = t4_cim_read(adap, UP_UP_DBG_LA_CFG_A, 1, &cfg);
+       if (ret)
+               return ret;
+
+       p = seq_open_tab(file, adap->params.cim_la_size / 8, 8 * sizeof(u32), 1,
+                        cfg & UPDBGLACAPTPCONLY_F ?
+                        cim_la_show_3in1 : cim_la_show);
+       if (!p)
+               return -ENOMEM;
+
+       ret = t4_cim_read_la(adap, (u32 *)p->data, NULL);
+       if (ret)
+               seq_release_private(inode, file);
+       return ret;
+}
+
+static const struct file_operations cim_la_fops = {
+       .owner   = THIS_MODULE,
+       .open    = cim_la_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = seq_release_private
+};
+
+/* Firmware Device Log dump. */
 static const char * const devlog_level_strings[] = {
        [FW_DEVLOG_LEVEL_EMERG]         = "EMERG",
        [FW_DEVLOG_LEVEL_CRIT]          = "CRIT",
@@ -318,6 +442,7 @@ int t4_setup_debugfs(struct adapter *adap)
        u32 size;
 
        static struct t4_debugfs_entry t4_debugfs_files[] = {
+               { "cim_la", &cim_la_fops, S_IRUSR, 0 },
                { "devlog", &devlog_fops, S_IRUSR, 0 },
                { "l2t", &t4_l2t_fops, S_IRUSR, 0},
        };
index a3d8867efd3d7aff3e59c0cac6c71a7561cd1364..70fcbc93082677626a5cf486e0ee6b17b458a3b7 100644 (file)
@@ -44,6 +44,18 @@ struct t4_debugfs_entry {
        unsigned char data;
 };
 
+struct seq_tab {
+       int (*show)(struct seq_file *seq, void *v, int idx);
+       unsigned int rows;        /* # of entries */
+       unsigned char width;      /* size in bytes of each entry */
+       unsigned char skip_first; /* whether the first line is a header */
+       char data[0];             /* the table data */
+};
+
+struct seq_tab *seq_open_tab(struct file *f, unsigned int rows,
+                            unsigned int width, unsigned int have_header,
+                            int (*show)(struct seq_file *seq, void *v, int i));
+
 int t4_setup_debugfs(struct adapter *adap);
 void add_debugfs_files(struct adapter *adap,
                       struct t4_debugfs_entry *files,
index 3776279337c8fa1e6fcb779b1b319b064157234b..1e30554f2699076e66ee51c9aa512a09d4c2273e 100644 (file)
@@ -4031,6 +4031,7 @@ int t4_prep_adapter(struct adapter *adapter)
                return -EINVAL;
        }
 
+       adapter->params.cim_la_size = CIMLA_SIZE;
        init_cong_ctrl(adapter->params.a_wnd, adapter->params.b_wnd);
 
        /*
@@ -4323,3 +4324,122 @@ int t4_port_init(struct adapter *adap, int mbox, int pf, int vf)
        }
        return 0;
 }
+
+/**
+ *     t4_cim_read - read a block from CIM internal address space
+ *     @adap: the adapter
+ *     @addr: the start address within the CIM address space
+ *     @n: number of words to read
+ *     @valp: where to store the result
+ *
+ *     Reads a block of 4-byte words from the CIM intenal address space.
+ */
+int t4_cim_read(struct adapter *adap, unsigned int addr, unsigned int n,
+               unsigned int *valp)
+{
+       int ret = 0;
+
+       if (t4_read_reg(adap, CIM_HOST_ACC_CTRL_A) & HOSTBUSY_F)
+               return -EBUSY;
+
+       for ( ; !ret && n--; addr += 4) {
+               t4_write_reg(adap, CIM_HOST_ACC_CTRL_A, addr);
+               ret = t4_wait_op_done(adap, CIM_HOST_ACC_CTRL_A, HOSTBUSY_F,
+                                     0, 5, 2);
+               if (!ret)
+                       *valp++ = t4_read_reg(adap, CIM_HOST_ACC_DATA_A);
+       }
+       return ret;
+}
+
+/**
+ *     t4_cim_write - write a block into CIM internal address space
+ *     @adap: the adapter
+ *     @addr: the start address within the CIM address space
+ *     @n: number of words to write
+ *     @valp: set of values to write
+ *
+ *     Writes a block of 4-byte words into the CIM intenal address space.
+ */
+int t4_cim_write(struct adapter *adap, unsigned int addr, unsigned int n,
+                const unsigned int *valp)
+{
+       int ret = 0;
+
+       if (t4_read_reg(adap, CIM_HOST_ACC_CTRL_A) & HOSTBUSY_F)
+               return -EBUSY;
+
+       for ( ; !ret && n--; addr += 4) {
+               t4_write_reg(adap, CIM_HOST_ACC_DATA_A, *valp++);
+               t4_write_reg(adap, CIM_HOST_ACC_CTRL_A, addr | HOSTWRITE_F);
+               ret = t4_wait_op_done(adap, CIM_HOST_ACC_CTRL_A, HOSTBUSY_F,
+                                     0, 5, 2);
+       }
+       return ret;
+}
+
+static int t4_cim_write1(struct adapter *adap, unsigned int addr,
+                        unsigned int val)
+{
+       return t4_cim_write(adap, addr, 1, &val);
+}
+
+/**
+ *     t4_cim_read_la - read CIM LA capture buffer
+ *     @adap: the adapter
+ *     @la_buf: where to store the LA data
+ *     @wrptr: the HW write pointer within the capture buffer
+ *
+ *     Reads the contents of the CIM LA buffer with the most recent entry at
+ *     the end of the returned data and with the entry at @wrptr first.
+ *     We try to leave the LA in the running state we find it in.
+ */
+int t4_cim_read_la(struct adapter *adap, u32 *la_buf, unsigned int *wrptr)
+{
+       int i, ret;
+       unsigned int cfg, val, idx;
+
+       ret = t4_cim_read(adap, UP_UP_DBG_LA_CFG_A, 1, &cfg);
+       if (ret)
+               return ret;
+
+       if (cfg & UPDBGLAEN_F) {        /* LA is running, freeze it */
+               ret = t4_cim_write1(adap, UP_UP_DBG_LA_CFG_A, 0);
+               if (ret)
+                       return ret;
+       }
+
+       ret = t4_cim_read(adap, UP_UP_DBG_LA_CFG_A, 1, &val);
+       if (ret)
+               goto restart;
+
+       idx = UPDBGLAWRPTR_G(val);
+       if (wrptr)
+               *wrptr = idx;
+
+       for (i = 0; i < adap->params.cim_la_size; i++) {
+               ret = t4_cim_write1(adap, UP_UP_DBG_LA_CFG_A,
+                                   UPDBGLARDPTR_V(idx) | UPDBGLARDEN_F);
+               if (ret)
+                       break;
+               ret = t4_cim_read(adap, UP_UP_DBG_LA_CFG_A, 1, &val);
+               if (ret)
+                       break;
+               if (val & UPDBGLARDEN_F) {
+                       ret = -ETIMEDOUT;
+                       break;
+               }
+               ret = t4_cim_read(adap, UP_UP_DBG_LA_DATA_A, 1, &la_buf[i]);
+               if (ret)
+                       break;
+               idx = (idx + 1) & UPDBGLARDPTR_M;
+       }
+restart:
+       if (cfg & UPDBGLAEN_F) {
+               int r = t4_cim_write1(adap, UP_UP_DBG_LA_CFG_A,
+                                     cfg & ~UPDBGLARDEN_F);
+               if (!ret)
+                       ret = r;
+       }
+       return ret;
+}
index 5e5eee6c6b5ea90b7c869181b2cebe891bfef241..bcc925b20088ff31768a03c689b73e15155e6d4b 100644 (file)
@@ -55,6 +55,10 @@ enum {
        WOL_PAT_LEN    = 128,   /* length of WoL patterns */
 };
 
+enum {
+       CIMLA_SIZE     = 2048,  /* # of 32-bit words in CIM LA */
+};
+
 enum {
        SF_PAGE_SIZE = 256,           /* serial flash page size */
        SF_SEC_SIZE = 64 * 1024,      /* serial flash sector size */
index 4077227b5cea9c88d56995c354ec544bb9a5fa9d..3fe6eeb3a55ed31544551ecfee16fc4577d2e6b9 100644 (file)
 #define PL_VF_WHOAMI_A 0x0
 #define PL_VF_REVISION_A 0x8
 
+/* registers for module CIM */
+#define CIM_HOST_ACC_CTRL_A    0x7b50
+#define CIM_HOST_ACC_DATA_A    0x7b54
+#define UP_UP_DBG_LA_CFG_A     0x140
+#define UP_UP_DBG_LA_DATA_A    0x144
+
+#define HOSTBUSY_S     17
+#define HOSTBUSY_V(x)  ((x) << HOSTBUSY_S)
+#define HOSTBUSY_F     HOSTBUSY_V(1U)
+
+#define HOSTWRITE_S    16
+#define HOSTWRITE_V(x) ((x) << HOSTWRITE_S)
+#define HOSTWRITE_F    HOSTWRITE_V(1U)
+
+#define UPDBGLARDEN_S          1
+#define UPDBGLARDEN_V(x)       ((x) << UPDBGLARDEN_S)
+#define UPDBGLARDEN_F          UPDBGLARDEN_V(1U)
+
+#define UPDBGLAEN_S    0
+#define UPDBGLAEN_V(x) ((x) << UPDBGLAEN_S)
+#define UPDBGLAEN_F    UPDBGLAEN_V(1U)
+
+#define UPDBGLARDPTR_S         2
+#define UPDBGLARDPTR_M         0xfffU
+#define UPDBGLARDPTR_V(x)      ((x) << UPDBGLARDPTR_S)
+
+#define UPDBGLAWRPTR_S    16
+#define UPDBGLAWRPTR_M    0xfffU
+#define UPDBGLAWRPTR_G(x) (((x) >> UPDBGLAWRPTR_S) & UPDBGLAWRPTR_M)
+
+#define UPDBGLACAPTPCONLY_S    30
+#define UPDBGLACAPTPCONLY_V(x) ((x) << UPDBGLACAPTPCONLY_S)
+#define UPDBGLACAPTPCONLY_F    UPDBGLACAPTPCONLY_V(1U)
+
 #endif /* __T4_REGS_H */