diff options
author | Krishna Gudipati <kgudipat@brocade.com> | 2011-12-22 08:30:19 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-12-23 16:50:58 -0500 |
commit | 7afc5dbde09104b023ce04465ba71aaba0fc4346 (patch) | |
tree | 03109d7dfed9afb5c0e4d1f1c86d307f577f0b6d | |
parent | 72a9730b3f556e18912f3e1b494a7aee7ae3dd91 (diff) |
bna: Add debugfs interface.
Change details:
- Add debugfs support to obtain firmware trace, saved firmware trace on
an IOC crash, driver info and read/write to registers.
- debugfs hierarchy:
bna/pci_dev:<pci_name>
where the pci_name corresponds to the one under /sys/bus/pci/drivers/bna
- Following are the new debugfs entries added:
fwtrc: collect current firmware trace.
fwsave: collect last saved fw trace as a result of firmware crash.
regwr: write one word to chip register
regrd: read one or more words from chip register.
drvinfo: collect the driver information.
Signed-off-by: Krishna Gudipati <kgudipat@brocade.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ethernet/brocade/bna/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/brocade/bna/bfa_cee.c | 35 | ||||
-rw-r--r-- | drivers/net/ethernet/brocade/bna/bfa_cee.h | 4 | ||||
-rw-r--r-- | drivers/net/ethernet/brocade/bna/bfa_ioc.c | 121 | ||||
-rw-r--r-- | drivers/net/ethernet/brocade/bna/bfa_ioc.h | 6 | ||||
-rw-r--r-- | drivers/net/ethernet/brocade/bna/bfi.h | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/brocade/bna/bna_enet.c | 5 | ||||
-rw-r--r-- | drivers/net/ethernet/brocade/bna/bnad.c | 15 | ||||
-rw-r--r-- | drivers/net/ethernet/brocade/bna/bnad.h | 18 | ||||
-rw-r--r-- | drivers/net/ethernet/brocade/bna/bnad_debugfs.c | 623 |
10 files changed, 827 insertions, 4 deletions
diff --git a/drivers/net/ethernet/brocade/bna/Makefile b/drivers/net/ethernet/brocade/bna/Makefile index 74d3abca1960..6027302ae73a 100644 --- a/drivers/net/ethernet/brocade/bna/Makefile +++ b/drivers/net/ethernet/brocade/bna/Makefile | |||
@@ -5,7 +5,7 @@ | |||
5 | 5 | ||
6 | obj-$(CONFIG_BNA) += bna.o | 6 | obj-$(CONFIG_BNA) += bna.o |
7 | 7 | ||
8 | bna-objs := bnad.o bnad_ethtool.o bna_enet.o bna_tx_rx.o | 8 | bna-objs := bnad.o bnad_ethtool.o bnad_debugfs.o bna_enet.o bna_tx_rx.o |
9 | bna-objs += bfa_msgq.o bfa_ioc.o bfa_ioc_ct.o bfa_cee.o | 9 | bna-objs += bfa_msgq.o bfa_ioc.o bfa_ioc_ct.o bfa_cee.o |
10 | bna-objs += cna_fwimg.o | 10 | bna-objs += cna_fwimg.o |
11 | 11 | ||
diff --git a/drivers/net/ethernet/brocade/bna/bfa_cee.c b/drivers/net/ethernet/brocade/bna/bfa_cee.c index 8e627186507c..29f284f79e02 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_cee.c +++ b/drivers/net/ethernet/brocade/bna/bfa_cee.c | |||
@@ -185,6 +185,41 @@ bfa_nw_cee_mem_claim(struct bfa_cee *cee, u8 *dma_kva, u64 dma_pa) | |||
185 | } | 185 | } |
186 | 186 | ||
187 | /** | 187 | /** |
188 | * bfa_cee_get_attr() | ||
189 | * | ||
190 | * @brief Send the request to the f/w to fetch CEE attributes. | ||
191 | * | ||
192 | * @param[in] Pointer to the CEE module data structure. | ||
193 | * | ||
194 | * @return Status | ||
195 | */ | ||
196 | enum bfa_status | ||
197 | bfa_nw_cee_get_attr(struct bfa_cee *cee, struct bfa_cee_attr *attr, | ||
198 | bfa_cee_get_attr_cbfn_t cbfn, void *cbarg) | ||
199 | { | ||
200 | struct bfi_cee_get_req *cmd; | ||
201 | |||
202 | BUG_ON(!((cee != NULL) && (cee->ioc != NULL))); | ||
203 | if (!bfa_nw_ioc_is_operational(cee->ioc)) | ||
204 | return BFA_STATUS_IOC_FAILURE; | ||
205 | |||
206 | if (cee->get_attr_pending == true) | ||
207 | return BFA_STATUS_DEVBUSY; | ||
208 | |||
209 | cee->get_attr_pending = true; | ||
210 | cmd = (struct bfi_cee_get_req *) cee->get_cfg_mb.msg; | ||
211 | cee->attr = attr; | ||
212 | cee->cbfn.get_attr_cbfn = cbfn; | ||
213 | cee->cbfn.get_attr_cbarg = cbarg; | ||
214 | bfi_h2i_set(cmd->mh, BFI_MC_CEE, BFI_CEE_H2I_GET_CFG_REQ, | ||
215 | bfa_ioc_portid(cee->ioc)); | ||
216 | bfa_dma_be_addr_set(cmd->dma_addr, cee->attr_dma.pa); | ||
217 | bfa_nw_ioc_mbox_queue(cee->ioc, &cee->get_cfg_mb, NULL, NULL); | ||
218 | |||
219 | return BFA_STATUS_OK; | ||
220 | } | ||
221 | |||
222 | /** | ||
188 | * bfa_cee_isrs() | 223 | * bfa_cee_isrs() |
189 | * | 224 | * |
190 | * @brief Handles Mail-box interrupts for CEE module. | 225 | * @brief Handles Mail-box interrupts for CEE module. |
diff --git a/drivers/net/ethernet/brocade/bna/bfa_cee.h b/drivers/net/ethernet/brocade/bna/bfa_cee.h index 58d54e98d595..93fde633d6f3 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_cee.h +++ b/drivers/net/ethernet/brocade/bna/bfa_cee.h | |||
@@ -59,5 +59,7 @@ u32 bfa_nw_cee_meminfo(void); | |||
59 | void bfa_nw_cee_mem_claim(struct bfa_cee *cee, u8 *dma_kva, | 59 | void bfa_nw_cee_mem_claim(struct bfa_cee *cee, u8 *dma_kva, |
60 | u64 dma_pa); | 60 | u64 dma_pa); |
61 | void bfa_nw_cee_attach(struct bfa_cee *cee, struct bfa_ioc *ioc, void *dev); | 61 | void bfa_nw_cee_attach(struct bfa_cee *cee, struct bfa_ioc *ioc, void *dev); |
62 | 62 | enum bfa_status bfa_nw_cee_get_attr(struct bfa_cee *cee, | |
63 | struct bfa_cee_attr *attr, | ||
64 | bfa_cee_get_attr_cbfn_t cbfn, void *cbarg); | ||
63 | #endif /* __BFA_CEE_H__ */ | 65 | #endif /* __BFA_CEE_H__ */ |
diff --git a/drivers/net/ethernet/brocade/bna/bfa_ioc.c b/drivers/net/ethernet/brocade/bna/bfa_ioc.c index 1d130445efbc..abfad275b5f3 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_ioc.c +++ b/drivers/net/ethernet/brocade/bna/bfa_ioc.c | |||
@@ -74,6 +74,7 @@ static void bfa_ioc_check_attr_wwns(struct bfa_ioc *ioc); | |||
74 | static void bfa_ioc_event_notify(struct bfa_ioc *, enum bfa_ioc_event); | 74 | static void bfa_ioc_event_notify(struct bfa_ioc *, enum bfa_ioc_event); |
75 | static void bfa_ioc_disable_comp(struct bfa_ioc *ioc); | 75 | static void bfa_ioc_disable_comp(struct bfa_ioc *ioc); |
76 | static void bfa_ioc_lpu_stop(struct bfa_ioc *ioc); | 76 | static void bfa_ioc_lpu_stop(struct bfa_ioc *ioc); |
77 | static void bfa_nw_ioc_debug_save_ftrc(struct bfa_ioc *ioc); | ||
77 | static void bfa_ioc_fail_notify(struct bfa_ioc *ioc); | 78 | static void bfa_ioc_fail_notify(struct bfa_ioc *ioc); |
78 | static void bfa_ioc_pf_enabled(struct bfa_ioc *ioc); | 79 | static void bfa_ioc_pf_enabled(struct bfa_ioc *ioc); |
79 | static void bfa_ioc_pf_disabled(struct bfa_ioc *ioc); | 80 | static void bfa_ioc_pf_disabled(struct bfa_ioc *ioc); |
@@ -997,6 +998,7 @@ bfa_iocpf_sm_disabled(struct bfa_iocpf *iocpf, enum iocpf_event event) | |||
997 | static void | 998 | static void |
998 | bfa_iocpf_sm_initfail_sync_entry(struct bfa_iocpf *iocpf) | 999 | bfa_iocpf_sm_initfail_sync_entry(struct bfa_iocpf *iocpf) |
999 | { | 1000 | { |
1001 | bfa_nw_ioc_debug_save_ftrc(iocpf->ioc); | ||
1000 | bfa_ioc_hw_sem_get(iocpf->ioc); | 1002 | bfa_ioc_hw_sem_get(iocpf->ioc); |
1001 | } | 1003 | } |
1002 | 1004 | ||
@@ -1743,6 +1745,114 @@ bfa_ioc_mbox_flush(struct bfa_ioc *ioc) | |||
1743 | bfa_q_deq(&mod->cmd_q, &cmd); | 1745 | bfa_q_deq(&mod->cmd_q, &cmd); |
1744 | } | 1746 | } |
1745 | 1747 | ||
1748 | /** | ||
1749 | * Read data from SMEM to host through PCI memmap | ||
1750 | * | ||
1751 | * @param[in] ioc memory for IOC | ||
1752 | * @param[in] tbuf app memory to store data from smem | ||
1753 | * @param[in] soff smem offset | ||
1754 | * @param[in] sz size of smem in bytes | ||
1755 | */ | ||
1756 | static int | ||
1757 | bfa_nw_ioc_smem_read(struct bfa_ioc *ioc, void *tbuf, u32 soff, u32 sz) | ||
1758 | { | ||
1759 | u32 pgnum, loff, r32; | ||
1760 | int i, len; | ||
1761 | u32 *buf = tbuf; | ||
1762 | |||
1763 | pgnum = PSS_SMEM_PGNUM(ioc->ioc_regs.smem_pg0, soff); | ||
1764 | loff = PSS_SMEM_PGOFF(soff); | ||
1765 | |||
1766 | /* | ||
1767 | * Hold semaphore to serialize pll init and fwtrc. | ||
1768 | */ | ||
1769 | if (bfa_nw_ioc_sem_get(ioc->ioc_regs.ioc_init_sem_reg) == 0) | ||
1770 | return 1; | ||
1771 | |||
1772 | writel(pgnum, ioc->ioc_regs.host_page_num_fn); | ||
1773 | |||
1774 | len = sz/sizeof(u32); | ||
1775 | for (i = 0; i < len; i++) { | ||
1776 | r32 = swab32(readl((loff) + (ioc->ioc_regs.smem_page_start))); | ||
1777 | buf[i] = be32_to_cpu(r32); | ||
1778 | loff += sizeof(u32); | ||
1779 | |||
1780 | /** | ||
1781 | * handle page offset wrap around | ||
1782 | */ | ||
1783 | loff = PSS_SMEM_PGOFF(loff); | ||
1784 | if (loff == 0) { | ||
1785 | pgnum++; | ||
1786 | writel(pgnum, ioc->ioc_regs.host_page_num_fn); | ||
1787 | } | ||
1788 | } | ||
1789 | |||
1790 | writel(PSS_SMEM_PGNUM(ioc->ioc_regs.smem_pg0, 0), | ||
1791 | ioc->ioc_regs.host_page_num_fn); | ||
1792 | |||
1793 | /* | ||
1794 | * release semaphore | ||
1795 | */ | ||
1796 | readl(ioc->ioc_regs.ioc_init_sem_reg); | ||
1797 | writel(1, ioc->ioc_regs.ioc_init_sem_reg); | ||
1798 | return 0; | ||
1799 | } | ||
1800 | |||
1801 | /** | ||
1802 | * Retrieve saved firmware trace from a prior IOC failure. | ||
1803 | */ | ||
1804 | int | ||
1805 | bfa_nw_ioc_debug_fwtrc(struct bfa_ioc *ioc, void *trcdata, int *trclen) | ||
1806 | { | ||
1807 | u32 loff = BFI_IOC_TRC_OFF + BNA_DBG_FWTRC_LEN * ioc->port_id; | ||
1808 | int tlen, status = 0; | ||
1809 | |||
1810 | tlen = *trclen; | ||
1811 | if (tlen > BNA_DBG_FWTRC_LEN) | ||
1812 | tlen = BNA_DBG_FWTRC_LEN; | ||
1813 | |||
1814 | status = bfa_nw_ioc_smem_read(ioc, trcdata, loff, tlen); | ||
1815 | *trclen = tlen; | ||
1816 | return status; | ||
1817 | } | ||
1818 | |||
1819 | /** | ||
1820 | * Save firmware trace if configured. | ||
1821 | */ | ||
1822 | static void | ||
1823 | bfa_nw_ioc_debug_save_ftrc(struct bfa_ioc *ioc) | ||
1824 | { | ||
1825 | int tlen; | ||
1826 | |||
1827 | if (ioc->dbg_fwsave_once) { | ||
1828 | ioc->dbg_fwsave_once = 0; | ||
1829 | if (ioc->dbg_fwsave_len) { | ||
1830 | tlen = ioc->dbg_fwsave_len; | ||
1831 | bfa_nw_ioc_debug_fwtrc(ioc, ioc->dbg_fwsave, &tlen); | ||
1832 | } | ||
1833 | } | ||
1834 | } | ||
1835 | |||
1836 | /** | ||
1837 | * Retrieve saved firmware trace from a prior IOC failure. | ||
1838 | */ | ||
1839 | int | ||
1840 | bfa_nw_ioc_debug_fwsave(struct bfa_ioc *ioc, void *trcdata, int *trclen) | ||
1841 | { | ||
1842 | int tlen; | ||
1843 | |||
1844 | if (ioc->dbg_fwsave_len == 0) | ||
1845 | return BFA_STATUS_ENOFSAVE; | ||
1846 | |||
1847 | tlen = *trclen; | ||
1848 | if (tlen > ioc->dbg_fwsave_len) | ||
1849 | tlen = ioc->dbg_fwsave_len; | ||
1850 | |||
1851 | memcpy(trcdata, ioc->dbg_fwsave, tlen); | ||
1852 | *trclen = tlen; | ||
1853 | return BFA_STATUS_OK; | ||
1854 | } | ||
1855 | |||
1746 | static void | 1856 | static void |
1747 | bfa_ioc_fail_notify(struct bfa_ioc *ioc) | 1857 | bfa_ioc_fail_notify(struct bfa_ioc *ioc) |
1748 | { | 1858 | { |
@@ -1751,6 +1861,7 @@ bfa_ioc_fail_notify(struct bfa_ioc *ioc) | |||
1751 | */ | 1861 | */ |
1752 | ioc->cbfn->hbfail_cbfn(ioc->bfa); | 1862 | ioc->cbfn->hbfail_cbfn(ioc->bfa); |
1753 | bfa_ioc_event_notify(ioc, BFA_IOC_E_FAILED); | 1863 | bfa_ioc_event_notify(ioc, BFA_IOC_E_FAILED); |
1864 | bfa_nw_ioc_debug_save_ftrc(ioc); | ||
1754 | } | 1865 | } |
1755 | 1866 | ||
1756 | /** | 1867 | /** |
@@ -2058,6 +2169,16 @@ bfa_nw_ioc_disable(struct bfa_ioc *ioc) | |||
2058 | bfa_fsm_send_event(ioc, IOC_E_DISABLE); | 2169 | bfa_fsm_send_event(ioc, IOC_E_DISABLE); |
2059 | } | 2170 | } |
2060 | 2171 | ||
2172 | /** | ||
2173 | * Initialize memory for saving firmware trace. | ||
2174 | */ | ||
2175 | void | ||
2176 | bfa_nw_ioc_debug_memclaim(struct bfa_ioc *ioc, void *dbg_fwsave) | ||
2177 | { | ||
2178 | ioc->dbg_fwsave = dbg_fwsave; | ||
2179 | ioc->dbg_fwsave_len = ioc->iocpf.auto_recover ? BNA_DBG_FWTRC_LEN : 0; | ||
2180 | } | ||
2181 | |||
2061 | static u32 | 2182 | static u32 |
2062 | bfa_ioc_smem_pgnum(struct bfa_ioc *ioc, u32 fmaddr) | 2183 | bfa_ioc_smem_pgnum(struct bfa_ioc *ioc, u32 fmaddr) |
2063 | { | 2184 | { |
diff --git a/drivers/net/ethernet/brocade/bna/bfa_ioc.h b/drivers/net/ethernet/brocade/bna/bfa_ioc.h index fc108c7a735e..3b4460fdc148 100644 --- a/drivers/net/ethernet/brocade/bna/bfa_ioc.h +++ b/drivers/net/ethernet/brocade/bna/bfa_ioc.h | |||
@@ -27,6 +27,8 @@ | |||
27 | #define BFA_IOC_HWSEM_TOV 500 /* msecs */ | 27 | #define BFA_IOC_HWSEM_TOV 500 /* msecs */ |
28 | #define BFA_IOC_HB_TOV 500 /* msecs */ | 28 | #define BFA_IOC_HB_TOV 500 /* msecs */ |
29 | #define BFA_IOC_POLL_TOV 200 /* msecs */ | 29 | #define BFA_IOC_POLL_TOV 200 /* msecs */ |
30 | #define BNA_DBG_FWTRC_LEN (BFI_IOC_TRC_ENTS * BFI_IOC_TRC_ENT_SZ + \ | ||
31 | BFI_IOC_TRC_HDR_SZ) | ||
30 | 32 | ||
31 | /** | 33 | /** |
32 | * PCI device information required by IOC | 34 | * PCI device information required by IOC |
@@ -306,6 +308,7 @@ void bfa_nw_ioc_disable(struct bfa_ioc *ioc); | |||
306 | 308 | ||
307 | void bfa_nw_ioc_error_isr(struct bfa_ioc *ioc); | 309 | void bfa_nw_ioc_error_isr(struct bfa_ioc *ioc); |
308 | bool bfa_nw_ioc_is_disabled(struct bfa_ioc *ioc); | 310 | bool bfa_nw_ioc_is_disabled(struct bfa_ioc *ioc); |
311 | bool bfa_nw_ioc_is_operational(struct bfa_ioc *ioc); | ||
309 | void bfa_nw_ioc_get_attr(struct bfa_ioc *ioc, struct bfa_ioc_attr *ioc_attr); | 312 | void bfa_nw_ioc_get_attr(struct bfa_ioc *ioc, struct bfa_ioc_attr *ioc_attr); |
310 | void bfa_nw_ioc_notify_register(struct bfa_ioc *ioc, | 313 | void bfa_nw_ioc_notify_register(struct bfa_ioc *ioc, |
311 | struct bfa_ioc_notify *notify); | 314 | struct bfa_ioc_notify *notify); |
@@ -317,6 +320,9 @@ void bfa_nw_ioc_fwver_get(struct bfa_ioc *ioc, | |||
317 | bool bfa_nw_ioc_fwver_cmp(struct bfa_ioc *ioc, | 320 | bool bfa_nw_ioc_fwver_cmp(struct bfa_ioc *ioc, |
318 | struct bfi_ioc_image_hdr *fwhdr); | 321 | struct bfi_ioc_image_hdr *fwhdr); |
319 | mac_t bfa_nw_ioc_get_mac(struct bfa_ioc *ioc); | 322 | mac_t bfa_nw_ioc_get_mac(struct bfa_ioc *ioc); |
323 | void bfa_nw_ioc_debug_memclaim(struct bfa_ioc *ioc, void *dbg_fwsave); | ||
324 | int bfa_nw_ioc_debug_fwtrc(struct bfa_ioc *ioc, void *trcdata, int *trclen); | ||
325 | int bfa_nw_ioc_debug_fwsave(struct bfa_ioc *ioc, void *trcdata, int *trclen); | ||
320 | 326 | ||
321 | /* | 327 | /* |
322 | * Timeout APIs | 328 | * Timeout APIs |
diff --git a/drivers/net/ethernet/brocade/bna/bfi.h b/drivers/net/ethernet/brocade/bna/bfi.h index 8230970ad2d3..0d9df695397a 100644 --- a/drivers/net/ethernet/brocade/bna/bfi.h +++ b/drivers/net/ethernet/brocade/bna/bfi.h | |||
@@ -257,6 +257,8 @@ struct bfi_ioc_getattr_reply { | |||
257 | */ | 257 | */ |
258 | #define BFI_IOC_TRC_OFF (0x4b00) | 258 | #define BFI_IOC_TRC_OFF (0x4b00) |
259 | #define BFI_IOC_TRC_ENTS 256 | 259 | #define BFI_IOC_TRC_ENTS 256 |
260 | #define BFI_IOC_TRC_ENT_SZ 16 | ||
261 | #define BFI_IOC_TRC_HDR_SZ 32 | ||
260 | 262 | ||
261 | #define BFI_IOC_FW_SIGNATURE (0xbfadbfad) | 263 | #define BFI_IOC_FW_SIGNATURE (0xbfadbfad) |
262 | #define BFI_IOC_MD5SUM_SZ 4 | 264 | #define BFI_IOC_MD5SUM_SZ 4 |
diff --git a/drivers/net/ethernet/brocade/bna/bna_enet.c b/drivers/net/ethernet/brocade/bna/bna_enet.c index bcfe29676677..9ccc586e3767 100644 --- a/drivers/net/ethernet/brocade/bna/bna_enet.c +++ b/drivers/net/ethernet/brocade/bna/bna_enet.c | |||
@@ -1727,6 +1727,7 @@ bna_ioceth_init(struct bna_ioceth *ioceth, struct bna *bna, | |||
1727 | bfa_nw_ioc_mem_claim(&ioceth->ioc, kva, dma); | 1727 | bfa_nw_ioc_mem_claim(&ioceth->ioc, kva, dma); |
1728 | 1728 | ||
1729 | kva = res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.mdl[0].kva; | 1729 | kva = res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.mdl[0].kva; |
1730 | bfa_nw_ioc_debug_memclaim(&ioceth->ioc, kva); | ||
1730 | 1731 | ||
1731 | /** | 1732 | /** |
1732 | * Attach common modules (Diag, SFP, CEE, Port) and claim respective | 1733 | * Attach common modules (Diag, SFP, CEE, Port) and claim respective |
@@ -1910,8 +1911,8 @@ bna_res_req(struct bna_res_info *res_info) | |||
1910 | /* Virtual memory for retreiving fw_trc */ | 1911 | /* Virtual memory for retreiving fw_trc */ |
1911 | res_info[BNA_RES_MEM_T_FWTRC].res_type = BNA_RES_T_MEM; | 1912 | res_info[BNA_RES_MEM_T_FWTRC].res_type = BNA_RES_T_MEM; |
1912 | res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.mem_type = BNA_MEM_T_KVA; | 1913 | res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.mem_type = BNA_MEM_T_KVA; |
1913 | res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.num = 0; | 1914 | res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.num = 1; |
1914 | res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.len = 0; | 1915 | res_info[BNA_RES_MEM_T_FWTRC].res_u.mem_info.len = BNA_DBG_FWTRC_LEN; |
1915 | 1916 | ||
1916 | /* DMA memory for retreiving stats */ | 1917 | /* DMA memory for retreiving stats */ |
1917 | res_info[BNA_RES_MEM_T_STATS].res_type = BNA_RES_T_MEM; | 1918 | res_info[BNA_RES_MEM_T_STATS].res_type = BNA_RES_T_MEM; |
diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c index 741f2e405006..2eddbaa5db47 100644 --- a/drivers/net/ethernet/brocade/bna/bnad.c +++ b/drivers/net/ethernet/brocade/bna/bnad.c | |||
@@ -44,6 +44,11 @@ static uint bnad_ioc_auto_recover = 1; | |||
44 | module_param(bnad_ioc_auto_recover, uint, 0444); | 44 | module_param(bnad_ioc_auto_recover, uint, 0444); |
45 | MODULE_PARM_DESC(bnad_ioc_auto_recover, "Enable / Disable auto recovery"); | 45 | MODULE_PARM_DESC(bnad_ioc_auto_recover, "Enable / Disable auto recovery"); |
46 | 46 | ||
47 | static uint bna_debugfs_enable = 1; | ||
48 | module_param(bna_debugfs_enable, uint, S_IRUGO | S_IWUSR); | ||
49 | MODULE_PARM_DESC(bna_debugfs_enable, "Enables debugfs feature, default=1," | ||
50 | " Range[false:0|true:1]"); | ||
51 | |||
47 | /* | 52 | /* |
48 | * Global variables | 53 | * Global variables |
49 | */ | 54 | */ |
@@ -3312,6 +3317,10 @@ bnad_pci_probe(struct pci_dev *pdev, | |||
3312 | /* Set link to down state */ | 3317 | /* Set link to down state */ |
3313 | netif_carrier_off(netdev); | 3318 | netif_carrier_off(netdev); |
3314 | 3319 | ||
3320 | /* Setup the debugfs node for this bfad */ | ||
3321 | if (bna_debugfs_enable) | ||
3322 | bnad_debugfs_init(bnad); | ||
3323 | |||
3315 | /* Get resource requirement form bna */ | 3324 | /* Get resource requirement form bna */ |
3316 | spin_lock_irqsave(&bnad->bna_lock, flags); | 3325 | spin_lock_irqsave(&bnad->bna_lock, flags); |
3317 | bna_res_req(&bnad->res_info[0]); | 3326 | bna_res_req(&bnad->res_info[0]); |
@@ -3433,6 +3442,9 @@ disable_ioceth: | |||
3433 | res_free: | 3442 | res_free: |
3434 | bnad_res_free(bnad, &bnad->res_info[0], BNA_RES_T_MAX); | 3443 | bnad_res_free(bnad, &bnad->res_info[0], BNA_RES_T_MAX); |
3435 | drv_uninit: | 3444 | drv_uninit: |
3445 | /* Remove the debugfs node for this bnad */ | ||
3446 | kfree(bnad->regdata); | ||
3447 | bnad_debugfs_uninit(bnad); | ||
3436 | bnad_uninit(bnad); | 3448 | bnad_uninit(bnad); |
3437 | pci_uninit: | 3449 | pci_uninit: |
3438 | bnad_pci_uninit(pdev); | 3450 | bnad_pci_uninit(pdev); |
@@ -3479,6 +3491,9 @@ bnad_pci_remove(struct pci_dev *pdev) | |||
3479 | mutex_unlock(&bnad->conf_mutex); | 3491 | mutex_unlock(&bnad->conf_mutex); |
3480 | bnad_remove_from_list(bnad); | 3492 | bnad_remove_from_list(bnad); |
3481 | bnad_lock_uninit(bnad); | 3493 | bnad_lock_uninit(bnad); |
3494 | /* Remove the debugfs node for this bnad */ | ||
3495 | kfree(bnad->regdata); | ||
3496 | bnad_debugfs_uninit(bnad); | ||
3482 | bnad_uninit(bnad); | 3497 | bnad_uninit(bnad); |
3483 | free_netdev(netdev); | 3498 | free_netdev(netdev); |
3484 | } | 3499 | } |
diff --git a/drivers/net/ethernet/brocade/bna/bnad.h b/drivers/net/ethernet/brocade/bna/bnad.h index 459030c191c5..c975ce672f48 100644 --- a/drivers/net/ethernet/brocade/bna/bnad.h +++ b/drivers/net/ethernet/brocade/bna/bnad.h | |||
@@ -328,6 +328,20 @@ struct bnad { | |||
328 | char adapter_name[BNAD_NAME_LEN]; | 328 | char adapter_name[BNAD_NAME_LEN]; |
329 | char port_name[BNAD_NAME_LEN]; | 329 | char port_name[BNAD_NAME_LEN]; |
330 | char mbox_irq_name[BNAD_NAME_LEN]; | 330 | char mbox_irq_name[BNAD_NAME_LEN]; |
331 | |||
332 | /* debugfs specific data */ | ||
333 | char *regdata; | ||
334 | u32 reglen; | ||
335 | struct dentry *bnad_dentry_files[5]; | ||
336 | struct dentry *port_debugfs_root; | ||
337 | }; | ||
338 | |||
339 | struct bnad_drvinfo { | ||
340 | struct bfa_ioc_attr ioc_attr; | ||
341 | struct bfa_cee_attr cee_attr; | ||
342 | struct bfa_flash_attr flash_attr; | ||
343 | u32 cee_status; | ||
344 | u32 flash_status; | ||
331 | }; | 345 | }; |
332 | 346 | ||
333 | /* | 347 | /* |
@@ -368,6 +382,10 @@ extern void bnad_netdev_qstats_fill(struct bnad *bnad, | |||
368 | extern void bnad_netdev_hwstats_fill(struct bnad *bnad, | 382 | extern void bnad_netdev_hwstats_fill(struct bnad *bnad, |
369 | struct rtnl_link_stats64 *stats); | 383 | struct rtnl_link_stats64 *stats); |
370 | 384 | ||
385 | /* Debugfs */ | ||
386 | void bnad_debugfs_init(struct bnad *bnad); | ||
387 | void bnad_debugfs_uninit(struct bnad *bnad); | ||
388 | |||
371 | /** | 389 | /** |
372 | * MACROS | 390 | * MACROS |
373 | */ | 391 | */ |
diff --git a/drivers/net/ethernet/brocade/bna/bnad_debugfs.c b/drivers/net/ethernet/brocade/bna/bnad_debugfs.c new file mode 100644 index 000000000000..592ad3929f53 --- /dev/null +++ b/drivers/net/ethernet/brocade/bna/bnad_debugfs.c | |||
@@ -0,0 +1,623 @@ | |||
1 | /* | ||
2 | * Linux network driver for Brocade Converged Network Adapter. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of the GNU General Public License (GPL) Version 2 as | ||
6 | * published by the Free Software Foundation | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
11 | * General Public License for more details. | ||
12 | */ | ||
13 | /* | ||
14 | * Copyright (c) 2005-2011 Brocade Communications Systems, Inc. | ||
15 | * All rights reserved | ||
16 | * www.brocade.com | ||
17 | */ | ||
18 | |||
19 | #include <linux/debugfs.h> | ||
20 | #include <linux/module.h> | ||
21 | #include "bnad.h" | ||
22 | |||
23 | /* | ||
24 | * BNA debufs interface | ||
25 | * | ||
26 | * To access the interface, debugfs file system should be mounted | ||
27 | * if not already mounted using: | ||
28 | * mount -t debugfs none /sys/kernel/debug | ||
29 | * | ||
30 | * BNA Hierarchy: | ||
31 | * - bna/pci_dev:<pci_name> | ||
32 | * where the pci_name corresponds to the one under /sys/bus/pci/drivers/bna | ||
33 | * | ||
34 | * Debugging service available per pci_dev: | ||
35 | * fwtrc: To collect current firmware trace. | ||
36 | * fwsave: To collect last saved fw trace as a result of firmware crash. | ||
37 | * regwr: To write one word to chip register | ||
38 | * regrd: To read one or more words from chip register. | ||
39 | */ | ||
40 | |||
41 | struct bnad_debug_info { | ||
42 | char *debug_buffer; | ||
43 | void *i_private; | ||
44 | int buffer_len; | ||
45 | }; | ||
46 | |||
47 | static int | ||
48 | bnad_debugfs_open_fwtrc(struct inode *inode, struct file *file) | ||
49 | { | ||
50 | struct bnad *bnad = inode->i_private; | ||
51 | struct bnad_debug_info *fw_debug; | ||
52 | unsigned long flags; | ||
53 | int rc; | ||
54 | |||
55 | fw_debug = kzalloc(sizeof(struct bnad_debug_info), GFP_KERNEL); | ||
56 | if (!fw_debug) | ||
57 | return -ENOMEM; | ||
58 | |||
59 | fw_debug->buffer_len = BNA_DBG_FWTRC_LEN; | ||
60 | |||
61 | fw_debug->debug_buffer = kzalloc(fw_debug->buffer_len, GFP_KERNEL); | ||
62 | if (!fw_debug->debug_buffer) { | ||
63 | kfree(fw_debug); | ||
64 | fw_debug = NULL; | ||
65 | pr_warn("bna %s: Failed to allocate fwtrc buffer\n", | ||
66 | pci_name(bnad->pcidev)); | ||
67 | return -ENOMEM; | ||
68 | } | ||
69 | |||
70 | spin_lock_irqsave(&bnad->bna_lock, flags); | ||
71 | rc = bfa_nw_ioc_debug_fwtrc(&bnad->bna.ioceth.ioc, | ||
72 | fw_debug->debug_buffer, | ||
73 | &fw_debug->buffer_len); | ||
74 | spin_unlock_irqrestore(&bnad->bna_lock, flags); | ||
75 | if (rc != BFA_STATUS_OK) { | ||
76 | kfree(fw_debug->debug_buffer); | ||
77 | fw_debug->debug_buffer = NULL; | ||
78 | kfree(fw_debug); | ||
79 | fw_debug = NULL; | ||
80 | pr_warn("bnad %s: Failed to collect fwtrc\n", | ||
81 | pci_name(bnad->pcidev)); | ||
82 | return -ENOMEM; | ||
83 | } | ||
84 | |||
85 | file->private_data = fw_debug; | ||
86 | |||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | static int | ||
91 | bnad_debugfs_open_fwsave(struct inode *inode, struct file *file) | ||
92 | { | ||
93 | struct bnad *bnad = inode->i_private; | ||
94 | struct bnad_debug_info *fw_debug; | ||
95 | unsigned long flags; | ||
96 | int rc; | ||
97 | |||
98 | fw_debug = kzalloc(sizeof(struct bnad_debug_info), GFP_KERNEL); | ||
99 | if (!fw_debug) | ||
100 | return -ENOMEM; | ||
101 | |||
102 | fw_debug->buffer_len = BNA_DBG_FWTRC_LEN; | ||
103 | |||
104 | fw_debug->debug_buffer = kzalloc(fw_debug->buffer_len, GFP_KERNEL); | ||
105 | if (!fw_debug->debug_buffer) { | ||
106 | kfree(fw_debug); | ||
107 | fw_debug = NULL; | ||
108 | pr_warn("bna %s: Failed to allocate fwsave buffer\n", | ||
109 | pci_name(bnad->pcidev)); | ||
110 | return -ENOMEM; | ||
111 | } | ||
112 | |||
113 | spin_lock_irqsave(&bnad->bna_lock, flags); | ||
114 | rc = bfa_nw_ioc_debug_fwsave(&bnad->bna.ioceth.ioc, | ||
115 | fw_debug->debug_buffer, | ||
116 | &fw_debug->buffer_len); | ||
117 | spin_unlock_irqrestore(&bnad->bna_lock, flags); | ||
118 | if (rc != BFA_STATUS_OK && rc != BFA_STATUS_ENOFSAVE) { | ||
119 | kfree(fw_debug->debug_buffer); | ||
120 | fw_debug->debug_buffer = NULL; | ||
121 | kfree(fw_debug); | ||
122 | fw_debug = NULL; | ||
123 | pr_warn("bna %s: Failed to collect fwsave\n", | ||
124 | pci_name(bnad->pcidev)); | ||
125 | return -ENOMEM; | ||
126 | } | ||
127 | |||
128 | file->private_data = fw_debug; | ||
129 | |||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | static int | ||
134 | bnad_debugfs_open_reg(struct inode *inode, struct file *file) | ||
135 | { | ||
136 | struct bnad_debug_info *reg_debug; | ||
137 | |||
138 | reg_debug = kzalloc(sizeof(struct bnad_debug_info), GFP_KERNEL); | ||
139 | if (!reg_debug) | ||
140 | return -ENOMEM; | ||
141 | |||
142 | reg_debug->i_private = inode->i_private; | ||
143 | |||
144 | file->private_data = reg_debug; | ||
145 | |||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | static int | ||
150 | bnad_get_debug_drvinfo(struct bnad *bnad, void *buffer, u32 len) | ||
151 | { | ||
152 | struct bnad_drvinfo *drvinfo = (struct bnad_drvinfo *) buffer; | ||
153 | struct bnad_iocmd_comp fcomp; | ||
154 | unsigned long flags = 0; | ||
155 | int ret = BFA_STATUS_FAILED; | ||
156 | |||
157 | /* Get IOC info */ | ||
158 | spin_lock_irqsave(&bnad->bna_lock, flags); | ||
159 | bfa_nw_ioc_get_attr(&bnad->bna.ioceth.ioc, &drvinfo->ioc_attr); | ||
160 | spin_unlock_irqrestore(&bnad->bna_lock, flags); | ||
161 | |||
162 | /* Retrieve CEE related info */ | ||
163 | fcomp.bnad = bnad; | ||
164 | fcomp.comp_status = 0; | ||
165 | init_completion(&fcomp.comp); | ||
166 | spin_lock_irqsave(&bnad->bna_lock, flags); | ||
167 | ret = bfa_nw_cee_get_attr(&bnad->bna.cee, &drvinfo->cee_attr, | ||
168 | bnad_cb_completion, &fcomp); | ||
169 | if (ret != BFA_STATUS_OK) { | ||
170 | spin_unlock_irqrestore(&bnad->bna_lock, flags); | ||
171 | goto out; | ||
172 | } | ||
173 | spin_unlock_irqrestore(&bnad->bna_lock, flags); | ||
174 | wait_for_completion(&fcomp.comp); | ||
175 | drvinfo->cee_status = fcomp.comp_status; | ||
176 | |||
177 | /* Retrieve flash partition info */ | ||
178 | fcomp.comp_status = 0; | ||
179 | init_completion(&fcomp.comp); | ||
180 | spin_lock_irqsave(&bnad->bna_lock, flags); | ||
181 | ret = bfa_nw_flash_get_attr(&bnad->bna.flash, &drvinfo->flash_attr, | ||
182 | bnad_cb_completion, &fcomp); | ||
183 | if (ret != BFA_STATUS_OK) { | ||
184 | spin_unlock_irqrestore(&bnad->bna_lock, flags); | ||
185 | goto out; | ||
186 | } | ||
187 | spin_unlock_irqrestore(&bnad->bna_lock, flags); | ||
188 | wait_for_completion(&fcomp.comp); | ||
189 | drvinfo->flash_status = fcomp.comp_status; | ||
190 | out: | ||
191 | return ret; | ||
192 | } | ||
193 | |||
194 | static int | ||
195 | bnad_debugfs_open_drvinfo(struct inode *inode, struct file *file) | ||
196 | { | ||
197 | struct bnad *bnad = inode->i_private; | ||
198 | struct bnad_debug_info *drv_info; | ||
199 | int rc; | ||
200 | |||
201 | drv_info = kzalloc(sizeof(struct bnad_debug_info), GFP_KERNEL); | ||
202 | if (!drv_info) | ||
203 | return -ENOMEM; | ||
204 | |||
205 | drv_info->buffer_len = sizeof(struct bnad_drvinfo); | ||
206 | |||
207 | drv_info->debug_buffer = kzalloc(drv_info->buffer_len, GFP_KERNEL); | ||
208 | if (!drv_info->debug_buffer) { | ||
209 | kfree(drv_info); | ||
210 | drv_info = NULL; | ||
211 | pr_warn("bna %s: Failed to allocate drv info buffer\n", | ||
212 | pci_name(bnad->pcidev)); | ||
213 | return -ENOMEM; | ||
214 | } | ||
215 | |||
216 | mutex_lock(&bnad->conf_mutex); | ||
217 | rc = bnad_get_debug_drvinfo(bnad, drv_info->debug_buffer, | ||
218 | drv_info->buffer_len); | ||
219 | mutex_unlock(&bnad->conf_mutex); | ||
220 | if (rc != BFA_STATUS_OK) { | ||
221 | kfree(drv_info->debug_buffer); | ||
222 | drv_info->debug_buffer = NULL; | ||
223 | kfree(drv_info); | ||
224 | drv_info = NULL; | ||
225 | pr_warn("bna %s: Failed to collect drvinfo\n", | ||
226 | pci_name(bnad->pcidev)); | ||
227 | return -ENOMEM; | ||
228 | } | ||
229 | |||
230 | file->private_data = drv_info; | ||
231 | |||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | /* Changes the current file position */ | ||
236 | static loff_t | ||
237 | bnad_debugfs_lseek(struct file *file, loff_t offset, int orig) | ||
238 | { | ||
239 | loff_t pos = file->f_pos; | ||
240 | struct bnad_debug_info *debug = file->private_data; | ||
241 | |||
242 | if (!debug) | ||
243 | return -EINVAL; | ||
244 | |||
245 | switch (orig) { | ||
246 | case 0: | ||
247 | file->f_pos = offset; | ||
248 | break; | ||
249 | case 1: | ||
250 | file->f_pos += offset; | ||
251 | break; | ||
252 | case 2: | ||
253 | file->f_pos = debug->buffer_len - offset; | ||
254 | break; | ||
255 | default: | ||
256 | return -EINVAL; | ||
257 | } | ||
258 | |||
259 | if (file->f_pos < 0 || file->f_pos > debug->buffer_len) { | ||
260 | file->f_pos = pos; | ||
261 | return -EINVAL; | ||
262 | } | ||
263 | |||
264 | return file->f_pos; | ||
265 | } | ||
266 | |||
267 | static ssize_t | ||
268 | bnad_debugfs_read(struct file *file, char __user *buf, | ||
269 | size_t nbytes, loff_t *pos) | ||
270 | { | ||
271 | struct bnad_debug_info *debug = file->private_data; | ||
272 | |||
273 | if (!debug || !debug->debug_buffer) | ||
274 | return 0; | ||
275 | |||
276 | return simple_read_from_buffer(buf, nbytes, pos, | ||
277 | debug->debug_buffer, debug->buffer_len); | ||
278 | } | ||
279 | |||
280 | #define BFA_REG_CT_ADDRSZ (0x40000) | ||
281 | #define BFA_REG_CB_ADDRSZ (0x20000) | ||
282 | #define BFA_REG_ADDRSZ(__ioc) \ | ||
283 | ((u32)(bfa_asic_id_ctc(bfa_ioc_devid(__ioc)) ? \ | ||
284 | BFA_REG_CT_ADDRSZ : BFA_REG_CB_ADDRSZ)) | ||
285 | #define BFA_REG_ADDRMSK(__ioc) (BFA_REG_ADDRSZ(__ioc) - 1) | ||
286 | |||
287 | /* | ||
288 | * Function to check if the register offset passed is valid. | ||
289 | */ | ||
290 | static int | ||
291 | bna_reg_offset_check(struct bfa_ioc *ioc, u32 offset, u32 len) | ||
292 | { | ||
293 | u8 area; | ||
294 | |||
295 | /* check [16:15] */ | ||
296 | area = (offset >> 15) & 0x7; | ||
297 | if (area == 0) { | ||
298 | /* PCIe core register */ | ||
299 | if ((offset + (len<<2)) > 0x8000) /* 8k dwords or 32KB */ | ||
300 | return BFA_STATUS_EINVAL; | ||
301 | } else if (area == 0x1) { | ||
302 | /* CB 32 KB memory page */ | ||
303 | if ((offset + (len<<2)) > 0x10000) /* 8k dwords or 32KB */ | ||
304 | return BFA_STATUS_EINVAL; | ||
305 | } else { | ||
306 | /* CB register space 64KB */ | ||
307 | if ((offset + (len<<2)) > BFA_REG_ADDRMSK(ioc)) | ||
308 | return BFA_STATUS_EINVAL; | ||
309 | } | ||
310 | return BFA_STATUS_OK; | ||
311 | } | ||
312 | |||
313 | static ssize_t | ||
314 | bnad_debugfs_read_regrd(struct file *file, char __user *buf, | ||
315 | size_t nbytes, loff_t *pos) | ||
316 | { | ||
317 | struct bnad_debug_info *regrd_debug = file->private_data; | ||
318 | struct bnad *bnad = (struct bnad *)regrd_debug->i_private; | ||
319 | ssize_t rc; | ||
320 | |||
321 | if (!bnad->regdata) | ||
322 | return 0; | ||
323 | |||
324 | rc = simple_read_from_buffer(buf, nbytes, pos, | ||
325 | bnad->regdata, bnad->reglen); | ||
326 | |||
327 | if ((*pos + nbytes) >= bnad->reglen) { | ||
328 | kfree(bnad->regdata); | ||
329 | bnad->regdata = NULL; | ||
330 | bnad->reglen = 0; | ||
331 | } | ||
332 | |||
333 | return rc; | ||
334 | } | ||
335 | |||
336 | static ssize_t | ||
337 | bnad_debugfs_write_regrd(struct file *file, const char __user *buf, | ||
338 | size_t nbytes, loff_t *ppos) | ||
339 | { | ||
340 | struct bnad_debug_info *regrd_debug = file->private_data; | ||
341 | struct bnad *bnad = (struct bnad *)regrd_debug->i_private; | ||
342 | struct bfa_ioc *ioc = &bnad->bna.ioceth.ioc; | ||
343 | int addr, len, rc, i; | ||
344 | u32 *regbuf; | ||
345 | void __iomem *rb, *reg_addr; | ||
346 | unsigned long flags; | ||
347 | void *kern_buf; | ||
348 | |||
349 | /* Allocate memory to store the user space buf */ | ||
350 | kern_buf = kzalloc(nbytes, GFP_KERNEL); | ||
351 | if (!kern_buf) { | ||
352 | pr_warn("bna %s: Failed to allocate user buffer\n", | ||
353 | pci_name(bnad->pcidev)); | ||
354 | return -ENOMEM; | ||
355 | } | ||
356 | |||
357 | if (copy_from_user(kern_buf, (void __user *)buf, nbytes)) { | ||
358 | kfree(kern_buf); | ||
359 | return -ENOMEM; | ||
360 | } | ||
361 | |||
362 | rc = sscanf(kern_buf, "%x:%x", &addr, &len); | ||
363 | if (rc < 2) { | ||
364 | pr_warn("bna %s: Failed to read user buffer\n", | ||
365 | pci_name(bnad->pcidev)); | ||
366 | kfree(kern_buf); | ||
367 | return -EINVAL; | ||
368 | } | ||
369 | |||
370 | kfree(kern_buf); | ||
371 | kfree(bnad->regdata); | ||
372 | bnad->regdata = NULL; | ||
373 | bnad->reglen = 0; | ||
374 | |||
375 | bnad->regdata = kzalloc(len << 2, GFP_KERNEL); | ||
376 | if (!bnad->regdata) { | ||
377 | pr_warn("bna %s: Failed to allocate regrd buffer\n", | ||
378 | pci_name(bnad->pcidev)); | ||
379 | return -ENOMEM; | ||
380 | } | ||
381 | |||
382 | bnad->reglen = len << 2; | ||
383 | rb = bfa_ioc_bar0(ioc); | ||
384 | addr &= BFA_REG_ADDRMSK(ioc); | ||
385 | |||
386 | /* offset and len sanity check */ | ||
387 | rc = bna_reg_offset_check(ioc, addr, len); | ||
388 | if (rc) { | ||
389 | pr_warn("bna %s: Failed reg offset check\n", | ||
390 | pci_name(bnad->pcidev)); | ||
391 | kfree(bnad->regdata); | ||
392 | bnad->regdata = NULL; | ||
393 | bnad->reglen = 0; | ||
394 | return -EINVAL; | ||
395 | } | ||
396 | |||
397 | reg_addr = rb + addr; | ||
398 | regbuf = (u32 *)bnad->regdata; | ||
399 | spin_lock_irqsave(&bnad->bna_lock, flags); | ||
400 | for (i = 0; i < len; i++) { | ||
401 | *regbuf = readl(reg_addr); | ||
402 | regbuf++; | ||
403 | reg_addr += sizeof(u32); | ||
404 | } | ||
405 | spin_unlock_irqrestore(&bnad->bna_lock, flags); | ||
406 | |||
407 | return nbytes; | ||
408 | } | ||
409 | |||
410 | static ssize_t | ||
411 | bnad_debugfs_write_regwr(struct file *file, const char __user *buf, | ||
412 | size_t nbytes, loff_t *ppos) | ||
413 | { | ||
414 | struct bnad_debug_info *debug = file->private_data; | ||
415 | struct bnad *bnad = (struct bnad *)debug->i_private; | ||
416 | struct bfa_ioc *ioc = &bnad->bna.ioceth.ioc; | ||
417 | int addr, val, rc; | ||
418 | void __iomem *reg_addr; | ||
419 | unsigned long flags; | ||
420 | void *kern_buf; | ||
421 | |||
422 | /* Allocate memory to store the user space buf */ | ||
423 | kern_buf = kzalloc(nbytes, GFP_KERNEL); | ||
424 | if (!kern_buf) { | ||
425 | pr_warn("bna %s: Failed to allocate user buffer\n", | ||
426 | pci_name(bnad->pcidev)); | ||
427 | return -ENOMEM; | ||
428 | } | ||
429 | |||
430 | if (copy_from_user(kern_buf, (void __user *)buf, nbytes)) { | ||
431 | kfree(kern_buf); | ||
432 | return -ENOMEM; | ||
433 | } | ||
434 | |||
435 | rc = sscanf(kern_buf, "%x:%x", &addr, &val); | ||
436 | if (rc < 2) { | ||
437 | pr_warn("bna %s: Failed to read user buffer\n", | ||
438 | pci_name(bnad->pcidev)); | ||
439 | kfree(kern_buf); | ||
440 | return -EINVAL; | ||
441 | } | ||
442 | kfree(kern_buf); | ||
443 | |||
444 | addr &= BFA_REG_ADDRMSK(ioc); /* offset only 17 bit and word align */ | ||
445 | |||
446 | /* offset and len sanity check */ | ||
447 | rc = bna_reg_offset_check(ioc, addr, 1); | ||
448 | if (rc) { | ||
449 | pr_warn("bna %s: Failed reg offset check\n", | ||
450 | pci_name(bnad->pcidev)); | ||
451 | return -EINVAL; | ||
452 | } | ||
453 | |||
454 | reg_addr = (bfa_ioc_bar0(ioc)) + addr; | ||
455 | spin_lock_irqsave(&bnad->bna_lock, flags); | ||
456 | writel(val, reg_addr); | ||
457 | spin_unlock_irqrestore(&bnad->bna_lock, flags); | ||
458 | |||
459 | return nbytes; | ||
460 | } | ||
461 | |||
462 | static int | ||
463 | bnad_debugfs_release(struct inode *inode, struct file *file) | ||
464 | { | ||
465 | struct bnad_debug_info *debug = file->private_data; | ||
466 | |||
467 | if (!debug) | ||
468 | return 0; | ||
469 | |||
470 | file->private_data = NULL; | ||
471 | kfree(debug); | ||
472 | return 0; | ||
473 | } | ||
474 | |||
475 | static int | ||
476 | bnad_debugfs_buffer_release(struct inode *inode, struct file *file) | ||
477 | { | ||
478 | struct bnad_debug_info *debug = file->private_data; | ||
479 | |||
480 | if (!debug) | ||
481 | return 0; | ||
482 | |||
483 | kfree(debug->debug_buffer); | ||
484 | |||
485 | file->private_data = NULL; | ||
486 | kfree(debug); | ||
487 | debug = NULL; | ||
488 | return 0; | ||
489 | } | ||
490 | |||
491 | static const struct file_operations bnad_debugfs_op_fwtrc = { | ||
492 | .owner = THIS_MODULE, | ||
493 | .open = bnad_debugfs_open_fwtrc, | ||
494 | .llseek = bnad_debugfs_lseek, | ||
495 | .read = bnad_debugfs_read, | ||
496 | .release = bnad_debugfs_buffer_release, | ||
497 | }; | ||
498 | |||
499 | static const struct file_operations bnad_debugfs_op_fwsave = { | ||
500 | .owner = THIS_MODULE, | ||
501 | .open = bnad_debugfs_open_fwsave, | ||
502 | .llseek = bnad_debugfs_lseek, | ||
503 | .read = bnad_debugfs_read, | ||
504 | .release = bnad_debugfs_buffer_release, | ||
505 | }; | ||
506 | |||
507 | static const struct file_operations bnad_debugfs_op_regrd = { | ||
508 | .owner = THIS_MODULE, | ||
509 | .open = bnad_debugfs_open_reg, | ||
510 | .llseek = bnad_debugfs_lseek, | ||
511 | .read = bnad_debugfs_read_regrd, | ||
512 | .write = bnad_debugfs_write_regrd, | ||
513 | .release = bnad_debugfs_release, | ||
514 | }; | ||
515 | |||
516 | static const struct file_operations bnad_debugfs_op_regwr = { | ||
517 | .owner = THIS_MODULE, | ||
518 | .open = bnad_debugfs_open_reg, | ||
519 | .llseek = bnad_debugfs_lseek, | ||
520 | .write = bnad_debugfs_write_regwr, | ||
521 | .release = bnad_debugfs_release, | ||
522 | }; | ||
523 | |||
524 | static const struct file_operations bnad_debugfs_op_drvinfo = { | ||
525 | .owner = THIS_MODULE, | ||
526 | .open = bnad_debugfs_open_drvinfo, | ||
527 | .llseek = bnad_debugfs_lseek, | ||
528 | .read = bnad_debugfs_read, | ||
529 | .release = bnad_debugfs_buffer_release, | ||
530 | }; | ||
531 | |||
532 | struct bnad_debugfs_entry { | ||
533 | const char *name; | ||
534 | mode_t mode; | ||
535 | const struct file_operations *fops; | ||
536 | }; | ||
537 | |||
538 | static const struct bnad_debugfs_entry bnad_debugfs_files[] = { | ||
539 | { "fwtrc", S_IFREG|S_IRUGO, &bnad_debugfs_op_fwtrc, }, | ||
540 | { "fwsave", S_IFREG|S_IRUGO, &bnad_debugfs_op_fwsave, }, | ||
541 | { "regrd", S_IFREG|S_IRUGO|S_IWUSR, &bnad_debugfs_op_regrd, }, | ||
542 | { "regwr", S_IFREG|S_IWUSR, &bnad_debugfs_op_regwr, }, | ||
543 | { "drvinfo", S_IFREG|S_IRUGO, &bnad_debugfs_op_drvinfo, }, | ||
544 | }; | ||
545 | |||
546 | static struct dentry *bna_debugfs_root; | ||
547 | static atomic_t bna_debugfs_port_count; | ||
548 | |||
549 | /* Initialize debugfs interface for BNA */ | ||
550 | void | ||
551 | bnad_debugfs_init(struct bnad *bnad) | ||
552 | { | ||
553 | const struct bnad_debugfs_entry *file; | ||
554 | char name[64]; | ||
555 | int i; | ||
556 | |||
557 | /* Setup the BNA debugfs root directory*/ | ||
558 | if (!bna_debugfs_root) { | ||
559 | bna_debugfs_root = debugfs_create_dir("bna", NULL); | ||
560 | atomic_set(&bna_debugfs_port_count, 0); | ||
561 | if (!bna_debugfs_root) { | ||
562 | pr_warn("BNA: debugfs root dir creation failed\n"); | ||
563 | return; | ||
564 | } | ||
565 | } | ||
566 | |||
567 | /* Setup the pci_dev debugfs directory for the port */ | ||
568 | snprintf(name, sizeof(name), "pci_dev:%s", pci_name(bnad->pcidev)); | ||
569 | if (!bnad->port_debugfs_root) { | ||
570 | bnad->port_debugfs_root = | ||
571 | debugfs_create_dir(name, bna_debugfs_root); | ||
572 | if (!bnad->port_debugfs_root) { | ||
573 | pr_warn("bna pci_dev %s: root dir creation failed\n", | ||
574 | pci_name(bnad->pcidev)); | ||
575 | return; | ||
576 | } | ||
577 | |||
578 | atomic_inc(&bna_debugfs_port_count); | ||
579 | |||
580 | for (i = 0; i < ARRAY_SIZE(bnad_debugfs_files); i++) { | ||
581 | file = &bnad_debugfs_files[i]; | ||
582 | bnad->bnad_dentry_files[i] = | ||
583 | debugfs_create_file(file->name, | ||
584 | file->mode, | ||
585 | bnad->port_debugfs_root, | ||
586 | bnad, | ||
587 | file->fops); | ||
588 | if (!bnad->bnad_dentry_files[i]) { | ||
589 | pr_warn( | ||
590 | "BNA pci_dev:%s: create %s entry failed\n", | ||
591 | pci_name(bnad->pcidev), file->name); | ||
592 | return; | ||
593 | } | ||
594 | } | ||
595 | } | ||
596 | } | ||
597 | |||
598 | /* Uninitialize debugfs interface for BNA */ | ||
599 | void | ||
600 | bnad_debugfs_uninit(struct bnad *bnad) | ||
601 | { | ||
602 | int i; | ||
603 | |||
604 | for (i = 0; i < ARRAY_SIZE(bnad_debugfs_files); i++) { | ||
605 | if (bnad->bnad_dentry_files[i]) { | ||
606 | debugfs_remove(bnad->bnad_dentry_files[i]); | ||
607 | bnad->bnad_dentry_files[i] = NULL; | ||
608 | } | ||
609 | } | ||
610 | |||
611 | /* Remove the pci_dev debugfs directory for the port */ | ||
612 | if (bnad->port_debugfs_root) { | ||
613 | debugfs_remove(bnad->port_debugfs_root); | ||
614 | bnad->port_debugfs_root = NULL; | ||
615 | atomic_dec(&bna_debugfs_port_count); | ||
616 | } | ||
617 | |||
618 | /* Remove the BNA debugfs root directory */ | ||
619 | if (atomic_read(&bna_debugfs_port_count) == 0) { | ||
620 | debugfs_remove(bna_debugfs_root); | ||
621 | bna_debugfs_root = NULL; | ||
622 | } | ||
623 | } | ||