// SPDX-License-Identifier: GPL-2.0 // // Microchip MCP251xFD Family CAN controller debug tool // // Copyright (c) 2019, 2020, 2021 Pengutronix, // Marc Kleine-Budde // #include #include "mcp251xfd-dump-userspace.h" struct mcp251xfd_dump_regs_fifo { u32 con; u32 sta; u32 ua; }; struct mcp251xfd_dump_regs_filter { u32 obj; u32 mask; }; struct mcp251xfd_dump_regs { u32 con; u32 nbtcfg; u32 dbtcfg; u32 tdc; u32 tbc; u32 tscon; u32 vec; u32 intf; u32 rxif; u32 txif; u32 rxovif; u32 txatif; u32 txreq; u32 trec; u32 bdiag0; u32 bdiag1; union { struct { u32 tefcon; u32 tefsta; u32 tefua; }; struct mcp251xfd_dump_regs_fifo tef; }; u32 reserved0; union { struct { struct mcp251xfd_dump_regs_fifo txq; struct mcp251xfd_dump_regs_fifo tx_fifo; struct mcp251xfd_dump_regs_fifo rx_fifo; }; struct mcp251xfd_dump_regs_fifo fifo[32]; }; u32 fltcon[8]; struct mcp251xfd_dump_regs_filter filter[32]; }; struct mcp251xfd_dump_ram { u8 ram[MCP251XFD_RAM_SIZE]; }; struct mcp251xfd_dump_regs_mcp251xfd { u32 osc; u32 iocon; u32 crc; u32 ecccon; u32 eccstat; u32 devid; }; #define __dump_bit(val, prefix, bit, desc) \ pr_info("%16s %s\t\t%s\n", __stringify(bit), \ (val) & prefix##_##bit ? "x" : " ", desc) #define __dump_mask(val, prefix, mask, fmt, desc) \ pr_info("%16s = " fmt "\t\t%s\n", \ __stringify(mask), \ FIELD_GET(prefix##_##mask##_MASK, (val)), \ desc) static void mcp251xfd_dump_reg_con(const struct mcp251xfd_priv *priv, u32 val, u16 addr) { pr_info("CON: con(0x%03x)=0x%08x\n", addr, val); __dump_mask(val, MCP251XFD_REG_CON, TXBWS, "0x%02lx", "Transmit Bandwidth Sharing"); __dump_bit(val, MCP251XFD_REG_CON, ABAT, "Abort All Pending Transmissions"); __dump_mask(val, MCP251XFD_REG_CON, REQOP, "0x%02lx", "Request Operation Mode"); __dump_mask(val, MCP251XFD_REG_CON, OPMOD, "0x%02lx", "Operation Mode Status"); __dump_bit(val, MCP251XFD_REG_CON, TXQEN, "Enable Transmit Queue"); __dump_bit(val, MCP251XFD_REG_CON, STEF, "Store in Transmit Event FIFO"); __dump_bit(val, MCP251XFD_REG_CON, SERR2LOM, "Transition to Listen Only Mode on System Error"); __dump_bit(val, MCP251XFD_REG_CON, ESIGM, "Transmit ESI in Gateway Mode"); __dump_bit(val, MCP251XFD_REG_CON, RTXAT, "Restrict Retransmission Attempts"); __dump_bit(val, MCP251XFD_REG_CON, BRSDIS, "Bit Rate Switching Disable"); __dump_bit(val, MCP251XFD_REG_CON, BUSY, "CAN Module is Busy"); __dump_mask(val, MCP251XFD_REG_CON, WFT, "0x%02lx", "Selectable Wake-up Filter Time"); __dump_bit(val, MCP251XFD_REG_CON, WAKFIL, "Enable CAN Bus Line Wake-up Filter"); __dump_bit(val, MCP251XFD_REG_CON, PXEDIS, "Protocol Exception Event Detection Disabled"); __dump_bit(val, MCP251XFD_REG_CON, ISOCRCEN, "Enable ISO CRC in CAN FD Frames"); __dump_mask(val, MCP251XFD_REG_CON, DNCNT, "0x%02lx", "Device Net Filter Bit Number"); } static void mcp251xfd_dump_reg_nbtcfg(const struct mcp251xfd_priv *priv, u32 val, u16 addr) { pr_info("NBTCFG: nbtcfg(0x%03x)=0x%08x\n", addr, val); __dump_mask(val, MCP251XFD_REG_NBTCFG, BRP, "%3lu", "Baud Rate Prescaler"); __dump_mask(val, MCP251XFD_REG_NBTCFG, TSEG1, "%3lu", "Time Segment 1 (Propagation Segment + Phase Segment 1)"); __dump_mask(val, MCP251XFD_REG_NBTCFG, TSEG2, "%3lu", "Time Segment 2 (Phase Segment 2)"); __dump_mask(val, MCP251XFD_REG_NBTCFG, SJW, "%3lu", "Synchronization Jump Width"); } static void mcp251xfd_dump_reg_dbtcfg(const struct mcp251xfd_priv *priv, u32 val, u16 addr) { pr_info("DBTCFG: dbtcfg(0x%03x)=0x%08x\n", addr, val); __dump_mask(val, MCP251XFD_REG_DBTCFG, BRP, "%3lu", "Baud Rate Prescaler"); __dump_mask(val, MCP251XFD_REG_DBTCFG, TSEG1, "%3lu", "Time Segment 1 (Propagation Segment + Phase Segment 1)"); __dump_mask(val, MCP251XFD_REG_DBTCFG, TSEG2, "%3lu", "Time Segment 2 (Phase Segment 2)"); __dump_mask(val, MCP251XFD_REG_DBTCFG, SJW, "%3lu", "Synchronization Jump Width"); } static void mcp251xfd_dump_reg_tdc(const struct mcp251xfd_priv *priv, u32 val, u16 addr) { pr_info("TDC: tdc(0x%03x)=0x%08x\n", addr, val); __dump_bit(val, MCP251XFD_REG_TDC, EDGFLTEN, "Enable Edge Filtering during Bus Integration state"); __dump_bit(val, MCP251XFD_REG_TDC, SID11EN, "Enable 12-Bit SID in CAN FD Base Format Messages"); __dump_mask(val, MCP251XFD_REG_TDC, TDCMOD, "0x%02lx", "Transmitter Delay Compensation Mode"); __dump_mask(val, MCP251XFD_REG_TDC, TDCO, "0x%02lx", "Transmitter Delay Compensation Offset"); __dump_mask(val, MCP251XFD_REG_TDC, TDCV, "0x%02lx", "Transmitter Delay Compensation Value"); } static void mcp251xfd_dump_reg_tbc(const struct mcp251xfd_priv *priv, u32 val, u16 addr) { pr_info("TBC: tbc(0x%03x)=0x%08x\n", addr, val); } static void mcp251xfd_dump_reg_vec(const struct mcp251xfd_priv *priv, u32 val, u16 addr) { u8 rx_code, tx_code, i_code; pr_info("VEC: vec(0x%03x)=0x%08x\n", addr, val); rx_code = FIELD_GET(MCP251XFD_REG_VEC_RXCODE_MASK, val); tx_code = FIELD_GET(MCP251XFD_REG_VEC_TXCODE_MASK, val); i_code = FIELD_GET(MCP251XFD_REG_VEC_ICODE_MASK, val); pr_info("\trxcode: "); if (rx_code == 0x40) pr_cont("No Interrupt"); else if (rx_code < 0x20) pr_cont("FIFO %u", rx_code); else pr_cont("Reserved"); pr_cont(" (0x%02x)\n", rx_code); pr_info("\ttxcode: "); if (tx_code == 0x40) pr_cont("No Interrupt"); else if (tx_code < 0x20) pr_cont("FIFO %u", tx_code); else pr_cont("Reserved"); pr_cont(" (0x%02x)\n", tx_code); pr_info("\ticode: "); if (i_code == 0x4a) pr_cont("Transmit Attempt Interrupt"); else if (i_code == 0x49) pr_cont("Transmit Event FIFO Interrupt"); else if (i_code == 0x48) pr_cont("Invalid Message Occurred"); else if (i_code == 0x47) pr_cont("Operation Mode Changed"); else if (i_code == 0x46) pr_cont("TBC Overflow"); else if (i_code == 0x45) pr_cont("RX/TX MAB Overflow/Underflow"); else if (i_code == 0x44) pr_cont("Address Error Interrupt"); else if (i_code == 0x43) pr_cont("Receive FIFO Overflow Interrupt"); else if (i_code == 0x42) pr_cont("Wake-up Interrupt"); else if (i_code == 0x41) pr_cont("Error Interrupt"); else if (i_code == 0x40) pr_cont("No Interrupt"); else if (i_code < 0x20) pr_cont("FIFO %u", i_code); else pr_cont("Reserved"); pr_cont(" (0x%02x)\n", i_code); } #define __dump_int(val, bit, desc) \ pr_info("\t" __stringify(bit) "\t%s\t%s\t%s\t%s\n", \ (val) & MCP251XFD_REG_INT_##bit##E ? "x" : "", \ (val) & MCP251XFD_REG_INT_##bit##F ? "x" : "", \ FIELD_GET(MCP251XFD_REG_INT_IF_MASK, val) & \ FIELD_GET(MCP251XFD_REG_INT_IE_MASK, val) & \ MCP251XFD_REG_INT_##bit##F ? "x" : "", \ desc) static void mcp251xfd_dump_reg_intf(const struct mcp251xfd_priv *priv, u32 val, u16 addr) { pr_info("INT: intf(0x%03x)=0x%08x\n", addr, val); pr_info("\t\tIE\tIF\tIE & IF\n"); __dump_int(val, IVMI, "Invalid Message Interrupt"); __dump_int(val, WAKI, "Bus Wake Up Interrupt"); __dump_int(val, CERRI, "CAN Bus Error Interrupt"); __dump_int(val, SERRI, "System Error Interrupt"); __dump_int(val, RXOVI, "Receive FIFO Overflow Interrupt"); __dump_int(val, TXATI, "Transmit Attempt Interrupt"); __dump_int(val, SPICRCI, "SPI CRC Error Interrupt"); __dump_int(val, ECCI, "ECC Error Interrupt"); __dump_int(val, TEFI, "Transmit Event FIFO Interrupt"); __dump_int(val, MODI, "Mode Change Interrupt"); __dump_int(val, TBCI, "Time Base Counter Interrupt"); __dump_int(val, RXI, "Receive FIFO Interrupt"); __dump_int(val, TXI, "Transmit FIFO Interrupt"); } #undef __dump_int #define __create_dump_fifo_bitmask(fifo, name, description) \ static void mcp251xfd_dump_reg_##fifo(const struct mcp251xfd_priv *priv, u32 val, u16 addr) \ { \ int i; \ \ pr_info(__stringify(name) ": " __stringify(fifo) "(0x%03x)=0x%08x\n", addr, val); \ pr_info(description ":\n"); \ if (!val) { \ pr_info("\t\t-none-\n"); \ return; \ } \ \ pr_info("\t\t"); \ for (i = 0; i < sizeof(val); i++) { \ if (val & BIT(i)) \ pr_cont("%d ", i); \ } \ \ pr_cont("\n"); \ } __create_dump_fifo_bitmask(rxif, RXIF, "Receive FIFO Interrupt Pending"); __create_dump_fifo_bitmask(rxovif, RXOVIF, "Receive FIFO Overflow Interrupt Pending"); __create_dump_fifo_bitmask(txif, TXIF, "Transmit FIFO Interrupt Pending"); __create_dump_fifo_bitmask(txatif, TXATIF, "Transmit FIFO Attempt Interrupt Pending"); __create_dump_fifo_bitmask(txreq, TXREQ, "Message Send Request"); #undef __create_dump_fifo_bitmask static void mcp251xfd_dump_reg_trec(const struct mcp251xfd_priv *priv, u32 val, u16 addr) { pr_info("TREC: trec(0x%03x)=0x%08x\n", addr, val); __dump_bit(val, MCP251XFD_REG_TREC, TXBO, "Transmitter in Bus Off State"); __dump_bit(val, MCP251XFD_REG_TREC, TXBP, "Transmitter in Error Passive State"); __dump_bit(val, MCP251XFD_REG_TREC, RXBP, "Receiver in Error Passive State"); __dump_bit(val, MCP251XFD_REG_TREC, TXWARN, "Transmitter in Error Warning State"); __dump_bit(val, MCP251XFD_REG_TREC, RXWARN, "Receiver in Error Warning State"); __dump_bit(val, MCP251XFD_REG_TREC, EWARN, "Transmitter or Receiver is in Error Warning State"); __dump_mask(val, MCP251XFD_REG_TREC, TEC, "%3lu", "Transmit Error Counter"); __dump_mask(val, MCP251XFD_REG_TREC, REC, "%3lu", "Receive Error Counter"); } static void mcp251xfd_dump_reg_bdiag0(const struct mcp251xfd_priv *priv, u32 val, u16 addr) { pr_info("BDIAG0: bdiag0(0x%03x)=0x%08x\n", addr, val); __dump_mask(val, MCP251XFD_REG_BDIAG0, DTERRCNT, "%3lu", "Data Bit Rate Transmit Error Counter"); __dump_mask(val, MCP251XFD_REG_BDIAG0, DRERRCNT, "%3lu", "Data Bit Rate Receive Error Counter"); __dump_mask(val, MCP251XFD_REG_BDIAG0, NTERRCNT, "%3lu", "Nominal Bit Rate Transmit Error Counter"); __dump_mask(val, MCP251XFD_REG_BDIAG0, NRERRCNT, "%3lu", "Nominal Bit Rate Receive Error Counter"); } static void mcp251xfd_dump_reg_bdiag1(const struct mcp251xfd_priv *priv, u32 val, u16 addr) { pr_info("BDIAG1: bdiag1(0x%03x)=0x%08x\n", addr, val); __dump_bit(val, MCP251XFD_REG_BDIAG1, DLCMM, "DLC Mismatch"); __dump_bit(val, MCP251XFD_REG_BDIAG1, ESI, "ESI flag of a received CAN FD message was set"); __dump_bit(val, MCP251XFD_REG_BDIAG1, DCRCERR, "Data CRC Error"); __dump_bit(val, MCP251XFD_REG_BDIAG1, DSTUFERR, "Data Bit Stuffing Error"); __dump_bit(val, MCP251XFD_REG_BDIAG1, DFORMERR, "Data Format Error"); __dump_bit(val, MCP251XFD_REG_BDIAG1, DBIT1ERR, "Data BIT1 Error"); __dump_bit(val, MCP251XFD_REG_BDIAG1, DBIT0ERR, "Data BIT0 Error"); __dump_bit(val, MCP251XFD_REG_BDIAG1, TXBOERR, "Device went to bus-off (and auto-recovered)"); __dump_bit(val, MCP251XFD_REG_BDIAG1, NCRCERR, "CRC Error"); __dump_bit(val, MCP251XFD_REG_BDIAG1, NSTUFERR, "Bit Stuffing Error"); __dump_bit(val, MCP251XFD_REG_BDIAG1, NFORMERR, "Format Error"); __dump_bit(val, MCP251XFD_REG_BDIAG1, NACKERR, "Transmitted message was not acknowledged"); __dump_bit(val, MCP251XFD_REG_BDIAG1, NBIT1ERR, "Bit1 Error"); __dump_bit(val, MCP251XFD_REG_BDIAG1, NBIT0ERR, "Bit0 Error"); __dump_mask(val, MCP251XFD_REG_BDIAG1, EFMSGCNT, "%3lu", "Error Free Message Counter"); } static void mcp251xfd_dump_reg_osc(const struct mcp251xfd_priv *priv, u32 val, u16 addr) { pr_info("OSC: osc(0x%03x)=0x%08x\n", addr, val); __dump_bit(val, MCP251XFD_REG_OSC, SCLKRDY, "Synchronized SCLKDIV"); __dump_bit(val, MCP251XFD_REG_OSC, OSCRDY, "Clock Ready"); __dump_bit(val, MCP251XFD_REG_OSC, PLLRDY, "PLL Ready"); __dump_mask(val, MCP251XFD_REG_OSC, CLKODIV, "0x%02lu", "Clock Output Divisor"); __dump_bit(val, MCP251XFD_REG_OSC, SCLKDIV, "System Clock Divisor"); __dump_bit(val, MCP251XFD_REG_OSC, LPMEN, "Low Power Mode (LPM) Enable (MCP2518FD only)"); __dump_bit(val, MCP251XFD_REG_OSC, OSCDIS, "Clock (Oscillator) Disable"); __dump_bit(val, MCP251XFD_REG_OSC, PLLEN, "PLL Enable"); } static void mcp251xfd_dump_reg_tefcon(const struct mcp251xfd_priv *priv, u32 val, u16 addr) { pr_info("TEFCON: tefcon(0x%03x)=0x%08x\n", addr, val); __dump_mask(val, MCP251XFD_REG_TEFCON, FSIZE, "%3lu", "FIFO Size"); __dump_bit(val, MCP251XFD_REG_TEFCON, FRESET, "FIFO Reset"); __dump_bit(val, MCP251XFD_REG_TEFCON, UINC, "Increment Tail"); __dump_bit(val, MCP251XFD_REG_TEFCON, TEFTSEN, "Transmit Event FIFO Time Stamp Enable"); __dump_bit(val, MCP251XFD_REG_TEFCON, TEFOVIE, "Transmit Event FIFO Overflow Interrupt Enable"); __dump_bit(val, MCP251XFD_REG_TEFCON, TEFFIE, "Transmit Event FIFO Full Interrupt Enable"); __dump_bit(val, MCP251XFD_REG_TEFCON, TEFHIE, "Transmit Event FIFO Half Full Interrupt Enable"); __dump_bit(val, MCP251XFD_REG_TEFCON, TEFNEIE, "Transmit Event FIFO Not Empty Interrupt Enable"); } static void mcp251xfd_dump_reg_tefsta(const struct mcp251xfd_priv *priv, u32 val, u16 addr) { pr_info("TEFSTA: tefsta(0x%03x)=0x%08x\n", addr, val); __dump_bit(val, MCP251XFD_REG_TEFSTA, TEFOVIF, "Transmit Event FIFO Overflow Interrupt Flag"); __dump_bit(val, MCP251XFD_REG_TEFSTA, TEFFIF, "Transmit Event FIFO Full Interrupt Flag (0: not full)"); __dump_bit(val, MCP251XFD_REG_TEFSTA, TEFHIF, "Transmit Event FIFO Half Full Interrupt Flag (0: < half full)"); __dump_bit(val, MCP251XFD_REG_TEFSTA, TEFNEIF, "Transmit Event FIFO Not Empty Interrupt Flag (0: empty)"); } static void mcp251xfd_dump_reg_tefua(const struct mcp251xfd_priv *priv, u32 val, u16 addr) { pr_info("TEFUA: tefua(0x%03x)=0x%08x\n", addr, val); } static void mcp251xfd_dump_reg_fifocon(const struct mcp251xfd_priv *priv, u32 val, u16 addr) { pr_info("FIFOCON: fifocon(0x%03x)=0x%08x\n", addr, val); __dump_mask(val, MCP251XFD_REG_FIFOCON, PLSIZE, "%3lu", "Payload Size"); __dump_mask(val, MCP251XFD_REG_FIFOCON, FSIZE, "%3lu", "FIFO Size"); __dump_mask(val, MCP251XFD_REG_FIFOCON, TXAT, "%3lu", "Retransmission Attempts"); __dump_mask(val, MCP251XFD_REG_FIFOCON, TXPRI, "%3lu", "Message Transmit Priority"); __dump_bit(val, MCP251XFD_REG_FIFOCON, FRESET, "FIFO Reset"); __dump_bit(val, MCP251XFD_REG_FIFOCON, TXREQ, "Message Send Request"); __dump_bit(val, MCP251XFD_REG_FIFOCON, UINC, "Increment Head/Tail"); __dump_bit(val, MCP251XFD_REG_FIFOCON, TXEN, "TX/RX FIFO Selection (0: RX, 1: TX)"); __dump_bit(val, MCP251XFD_REG_FIFOCON, RTREN, "Auto RTR Enable"); __dump_bit(val, MCP251XFD_REG_FIFOCON, RXTSEN, "Received Message Time Stamp Enable"); __dump_bit(val, MCP251XFD_REG_FIFOCON, TXATIE, "Transmit Attempts Exhausted Interrupt Enable"); __dump_bit(val, MCP251XFD_REG_FIFOCON, RXOVIE, "Overflow Interrupt Enable"); __dump_bit(val, MCP251XFD_REG_FIFOCON, TFERFFIE, "Transmit/Receive FIFO Empty/Full Interrupt Enable"); __dump_bit(val, MCP251XFD_REG_FIFOCON, TFHRFHIE, "Transmit/Receive FIFO Half Empty/Half Full Interrupt Enable"); __dump_bit(val, MCP251XFD_REG_FIFOCON, TFNRFNIE, "Transmit/Receive FIFO Not Full/Not Empty Interrupt Enable"); } static void mcp251xfd_dump_reg_fifosta(const struct mcp251xfd_priv *priv, u32 val, u16 addr) { pr_info("FIFOSTA: fifosta(0x%03x)=0x%08x\n", addr, val); __dump_mask(val, MCP251XFD_REG_FIFOSTA, FIFOCI, "%3lu", "FIFO Message Index"); __dump_bit(val, MCP251XFD_REG_FIFOSTA, TXABT, "Message Aborted Status (0: completed successfully, 1: aborted)"); __dump_bit(val, MCP251XFD_REG_FIFOSTA, TXLARB, "Message Lost Arbitration Status"); __dump_bit(val, MCP251XFD_REG_FIFOSTA, TXERR, "Error Detected During Transmission"); __dump_bit(val, MCP251XFD_REG_FIFOSTA, TXATIF, "Transmit Attempts Exhausted Interrupt Pending"); __dump_bit(val, MCP251XFD_REG_FIFOSTA, RXOVIF, "Receive FIFO Overflow Interrupt Flag"); __dump_bit(val, MCP251XFD_REG_FIFOSTA, TFERFFIF, "Transmit/Receive FIFO Empty/Full Interrupt Flag"); __dump_bit(val, MCP251XFD_REG_FIFOSTA, TFHRFHIF, "Transmit/Receive FIFO Half Empty/Half Full Interrupt Flag"); __dump_bit(val, MCP251XFD_REG_FIFOSTA, TFNRFNIF, "Transmit/Receive FIFO Not Full/Not Empty Interrupt Flag"); } static void mcp251xfd_dump_reg_fifoua(const struct mcp251xfd_priv *priv, u32 val, u16 addr) { pr_info("FIFOUA: fifoua(0x%03x)=0x%08x\n", addr, val); } #define __dump_call(regs, val) \ do { \ mcp251xfd_dump_reg_##val(priv, (regs)->val, \ (u16)(offsetof(typeof(*(regs)), val) + \ (sizeof(*(regs)) == sizeof(struct mcp251xfd_dump_regs) ? \ 0 : MCP251XFD_REG_OSC))); \ pr_info("\n"); \ } while (0) #define __dump_call_fifo(reg, val) \ do { \ mcp251xfd_dump_reg_##reg(priv, regs->val, (u16)offsetof(typeof(*regs), val)); \ pr_info("\n"); \ } while (0) static void mcp251xfd_dump_regs(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs, const struct mcp251xfd_dump_regs_mcp251xfd *regs_mcp251xfd) { netdev_info(priv->ndev, "-------------------- register dump --------------------\n"); __dump_call(regs, con); __dump_call(regs, nbtcfg); __dump_call(regs, dbtcfg); __dump_call(regs, tdc); __dump_call(regs, tbc); __dump_call(regs, vec); __dump_call(regs, intf); __dump_call(regs, rxif); __dump_call(regs, rxovif); __dump_call(regs, txif); __dump_call(regs, txatif); __dump_call(regs, txreq); __dump_call(regs, trec); __dump_call(regs, bdiag0); __dump_call(regs, bdiag1); __dump_call(regs_mcp251xfd, osc); pr_info("-------------------- TEF --------------------\n"); __dump_call(regs, tefcon); __dump_call(regs, tefsta); __dump_call(regs, tefua); pr_info("-------------------- TX_FIFO --------------------\n"); __dump_call_fifo(fifocon, fifo[MCP251XFD_TX_FIFO].con); __dump_call_fifo(fifosta, fifo[MCP251XFD_TX_FIFO].sta); __dump_call_fifo(fifoua, fifo[MCP251XFD_TX_FIFO].ua); pr_info(" -------------------- RX_FIFO --------------------\n"); __dump_call_fifo(fifocon, fifo[MCP251XFD_RX_FIFO(0)].con); __dump_call_fifo(fifosta, fifo[MCP251XFD_RX_FIFO(0)].sta); __dump_call_fifo(fifoua, fifo[MCP251XFD_RX_FIFO(0)].ua); netdev_info(priv->ndev, "------------------------- end -------------------------\n"); } #undef __dump_call #undef __dump_call_fifo static u8 mcp251xfd_dump_get_fifo_size(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs, u32 fifo_con) { u8 obj_size; obj_size = FIELD_GET(MCP251XFD_REG_FIFOCON_PLSIZE_MASK, fifo_con); switch (obj_size) { case MCP251XFD_REG_FIFOCON_PLSIZE_8: return 8; case MCP251XFD_REG_FIFOCON_PLSIZE_12: return 12; case MCP251XFD_REG_FIFOCON_PLSIZE_16: return 16; case MCP251XFD_REG_FIFOCON_PLSIZE_20: return 20; case MCP251XFD_REG_FIFOCON_PLSIZE_24: return 24; case MCP251XFD_REG_FIFOCON_PLSIZE_32: return 32; case MCP251XFD_REG_FIFOCON_PLSIZE_48: return 48; case MCP251XFD_REG_FIFOCON_PLSIZE_64: return 64; } return 0; } static u8 mcp251xfd_dump_get_fifo_obj_num(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs, u32 fifo_con) { u8 obj_num; obj_num = FIELD_GET(MCP251XFD_REG_FIFOCON_FSIZE_MASK, fifo_con); return obj_num + 1; } static void mcp251xfd_dump_ram_fifo_obj_data(const struct mcp251xfd_priv *priv, const u8 *data, u8 dlc) { int i; u8 len; len = can_dlc2len(get_canfd_dlc(dlc)); if (!len) { pr_info("%16s = -none-\n", "data"); return; } for (i = 0; i < len; i++) { if ((i % 8) == 0) { if (i == 0) pr_info("%16s = %02x", "data", data[i]); else pr_info(" %02x", data[i]); } else if ((i % 4) == 0) { pr_cont(" %02x", data[i]); } else if ((i % 8) == 7) { pr_cont(" %02x\n", data[i]); } else { pr_cont(" %02x", data[i]); } } if (i % 8) pr_cont("\n"); } /* TEF */ static u8 mcp251xfd_dump_get_tef_obj_num(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs) { return mcp251xfd_dump_get_fifo_obj_num(priv, regs, regs->tef.con); } static u8 mcp251xfd_dump_get_tef_tail(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs) { return regs->tefua / sizeof(struct mcp251xfd_hw_tef_obj); } static u16 mcp251xfd_dump_get_tef_obj_rel_addr(const struct mcp251xfd_priv *priv, u8 n) { return sizeof(struct mcp251xfd_hw_tef_obj) * n; } static u16 mcp251xfd_dump_get_tef_obj_addr(const struct mcp251xfd_priv *priv, u8 n) { return mcp251xfd_dump_get_tef_obj_rel_addr(priv, n) + MCP251XFD_RAM_START; } /* TX */ static u8 mcp251xfd_dump_get_tx_obj_size(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs) { return sizeof(struct mcp251xfd_hw_tx_obj_can) - sizeof_field(struct mcp251xfd_hw_tx_obj_can, data) + mcp251xfd_dump_get_fifo_size(priv, regs, regs->tx_fifo.con); } static u8 mcp251xfd_dump_get_tx_obj_num(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs) { return mcp251xfd_dump_get_fifo_obj_num(priv, regs, regs->tx_fifo.con); } static u16 mcp251xfd_dump_get_tx_obj_rel_addr(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs, u8 n) { return mcp251xfd_dump_get_tef_obj_rel_addr(priv, mcp251xfd_dump_get_tef_obj_num(priv, regs)) + mcp251xfd_dump_get_tx_obj_size(priv, regs) * n; } static u16 mcp251xfd_dump_get_tx_obj_addr(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs, u8 n) { return mcp251xfd_dump_get_tx_obj_rel_addr(priv, regs, n) + MCP251XFD_RAM_START; } static u8 mcp251xfd_dump_get_tx_tail(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs) { return (regs->fifo[MCP251XFD_TX_FIFO].ua - mcp251xfd_dump_get_tx_obj_rel_addr(priv, regs, 0)) / mcp251xfd_dump_get_tx_obj_size(priv, regs); } static u8 mcp251xfd_dump_get_tx_head(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs) { return FIELD_GET(MCP251XFD_REG_FIFOSTA_FIFOCI_MASK, regs->fifo[MCP251XFD_TX_FIFO].sta); } /* RX */ static u8 mcp251xfd_dump_get_rx_obj_size(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs) { return sizeof(struct mcp251xfd_hw_rx_obj_can) - sizeof_field(struct mcp251xfd_hw_rx_obj_can, data) + mcp251xfd_dump_get_fifo_size(priv, regs, regs->rx_fifo.con); } static u8 mcp251xfd_dump_get_rx_obj_num(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs) { return mcp251xfd_dump_get_fifo_obj_num(priv, regs, regs->rx_fifo.con); } static u16 mcp251xfd_dump_get_rx_obj_rel_addr(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs, u8 n) { return mcp251xfd_dump_get_tx_obj_rel_addr(priv, regs, mcp251xfd_dump_get_tx_obj_num(priv, regs)) + mcp251xfd_dump_get_rx_obj_size(priv, regs) * n; } static u16 mcp251xfd_dump_get_rx_obj_addr(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs, u8 n) { return mcp251xfd_dump_get_rx_obj_rel_addr(priv, regs, n) + MCP251XFD_RAM_START; } static u8 mcp251xfd_dump_get_rx_tail(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs) { return (regs->fifo[MCP251XFD_RX_FIFO(0)].ua - mcp251xfd_dump_get_rx_obj_rel_addr(priv, regs, 0)) / mcp251xfd_dump_get_rx_obj_size(priv, regs); } static u8 mcp251xfd_dump_get_rx_head(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs) { return FIELD_GET(MCP251XFD_REG_FIFOSTA_FIFOCI_MASK, regs->fifo[MCP251XFD_RX_FIFO(0)].sta); } /* dump TEF */ static void mcp251xfd_dump_ram_tef_obj_one(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs, const struct mcp251xfd_ring *tef, const struct mcp251xfd_hw_tef_obj *hw_tef_obj, u8 n) { pr_info("TEF Object: 0x%02x (0x%03x)%s%s%s%s%s\n", n, mcp251xfd_dump_get_tef_obj_addr(priv, n), mcp251xfd_get_ring_head(tef) == n ? " priv-HEAD" : "", mcp251xfd_dump_get_tef_tail(priv, regs) == n ? " chip-TAIL" : "", mcp251xfd_get_ring_tail(tef) == n ? " priv-TAIL" : "", (mcp251xfd_dump_get_tef_tail(priv, regs) == n ? ((regs->tef.sta & MCP251XFD_REG_TEFSTA_TEFFIF) ? " chip-FIFO-full" : !(regs->tef.sta & MCP251XFD_REG_TEFSTA_TEFNEIF) ? " chip-FIFO-empty" : "") : ("")), (mcp251xfd_get_ring_head(tef) == mcp251xfd_get_ring_tail(tef) && mcp251xfd_get_ring_tail(tef) == n ? (priv->tef->head == priv->tef->tail ? " priv-FIFO-empty" : " priv-FIFO-full") : (""))); pr_info("%16s = 0x%08x\n", "id", hw_tef_obj->id); pr_info("%16s = 0x%08x\n", "flags", hw_tef_obj->flags); pr_info("%16s = 0x%08x\n", "ts", hw_tef_obj->ts); __dump_mask(hw_tef_obj->flags, MCP251XFD_OBJ_FLAGS, SEQ, "0x%06lx", "Sequence"); pr_info("\n"); } static void mcp251xfd_dump_ram_tef_obj(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs, const struct mcp251xfd_dump_ram *ram, const struct mcp251xfd_ring *tef) { int i; pr_info("\nTEF Overview:\n"); pr_info("%16s = 0x%02x 0x%08x\n", "head (p)", mcp251xfd_get_ring_head(tef), tef->head); pr_info("%16s = 0x%02x 0x%02x 0x%08x\n", "tail (c/p)", mcp251xfd_dump_get_tef_tail(priv, regs), mcp251xfd_get_ring_tail(tef), tef->tail); pr_info("\n"); for (i = 0; i < mcp251xfd_dump_get_tef_obj_num(priv, regs); i++) { const struct mcp251xfd_hw_tef_obj *hw_tef_obj; u16 hw_tef_obj_rel_addr; hw_tef_obj_rel_addr = mcp251xfd_dump_get_tef_obj_rel_addr(priv, i); hw_tef_obj = (const struct mcp251xfd_hw_tef_obj *)&ram->ram[hw_tef_obj_rel_addr]; mcp251xfd_dump_ram_tef_obj_one(priv, regs, tef, hw_tef_obj, i); } } /* dump TX */ static void mcp251xfd_dump_ram_tx_obj_one(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs, const struct mcp251xfd_ring *tx, const struct mcp251xfd_hw_tx_obj_canfd *hw_tx_obj, u8 n) { pr_info("TX Object: 0x%02x (0x%03x)%s%s%s%s%s%s\n", n, mcp251xfd_dump_get_tx_obj_addr(priv, regs, n), mcp251xfd_dump_get_tx_head(priv, regs) == n ? " chip-HEAD" : "", mcp251xfd_get_ring_head(tx) == n ? " priv-HEAD" : "", mcp251xfd_dump_get_tx_tail(priv, regs) == n ? " chip-TAIL" : "", mcp251xfd_get_ring_tail(tx) == n ? " priv-TAIL" : "", mcp251xfd_dump_get_tx_tail(priv, regs) == n ? (!(regs->tx_fifo.sta & MCP251XFD_REG_FIFOSTA_TFNRFNIF) ? " chip-FIFO-full" : (regs->tx_fifo.sta & MCP251XFD_REG_FIFOSTA_TFERFFIF) ? " chip-FIFO-empty" : "") : (""), (mcp251xfd_get_ring_head(tx) == mcp251xfd_get_ring_tail(tx) && mcp251xfd_get_ring_tail(tx) == n ? (tx->head == tx->tail ? " priv-FIFO-empty" : " priv-FIFO-full") : (""))); pr_info("%16s = 0x%08x\n", "id", hw_tx_obj->id); pr_info("%16s = 0x%08x\n", "flags", hw_tx_obj->flags); __dump_mask(hw_tx_obj->flags, MCP251XFD_OBJ_FLAGS, SEQ_MCP2517FD, "0x%06lx", "Sequence (MCP2517)"); __dump_mask(hw_tx_obj->flags, MCP251XFD_OBJ_FLAGS, SEQ_MCP2518FD, "0x%06lx", "Sequence (MCP2518)"); mcp251xfd_dump_ram_fifo_obj_data(priv, hw_tx_obj->data, FIELD_GET(MCP251XFD_OBJ_FLAGS_DLC, hw_tx_obj->flags)); pr_info("\n"); } static void mcp251xfd_dump_ram_tx_obj(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs, const struct mcp251xfd_dump_ram *ram, const struct mcp251xfd_ring *tx) { int i; pr_info("\nTX Overview:\n"); pr_info("%16s = 0x%02x 0x%02x 0x%08x\n", "head (c/p)", mcp251xfd_dump_get_tx_head(priv, regs), mcp251xfd_get_ring_head(tx), tx->head); pr_info("%16s = 0x%02x 0x%02x 0x%08x\n", "tail (c/p)", mcp251xfd_dump_get_tx_tail(priv, regs), mcp251xfd_get_ring_tail(tx), tx->tail); pr_info("\n"); for (i = 0; i < mcp251xfd_dump_get_tx_obj_num(priv, regs); i++) { const struct mcp251xfd_hw_tx_obj_canfd *hw_tx_obj; u16 hw_tx_obj_rel_addr; hw_tx_obj_rel_addr = mcp251xfd_dump_get_tx_obj_rel_addr(priv, regs, i); hw_tx_obj = (const struct mcp251xfd_hw_tx_obj_canfd *)&ram->ram[hw_tx_obj_rel_addr]; mcp251xfd_dump_ram_tx_obj_one(priv, regs, tx, hw_tx_obj, i); } } /* dump RX */ static void mcp251xfd_dump_ram_rx_obj_one(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs, const struct mcp251xfd_ring *rx, const struct mcp251xfd_hw_rx_obj_canfd *hw_rx_obj, u8 n) { pr_info("RX Object: 0x%02x (0x%03x)%s%s%s%s%s%s\n", n, mcp251xfd_dump_get_rx_obj_addr(priv, regs, n), mcp251xfd_dump_get_rx_head(priv, regs) == n ? " chip-HEAD" : "", mcp251xfd_get_ring_head(rx) == n ? " priv-HEAD" : "", mcp251xfd_dump_get_rx_tail(priv, regs) == n ? " chip-TAIL" : "", mcp251xfd_get_ring_tail(rx) == n ? " priv-TAIL" : "", mcp251xfd_dump_get_rx_tail(priv, regs) == n ? ((regs->rx_fifo.sta & MCP251XFD_REG_FIFOSTA_TFERFFIF) ? " chip-FIFO-full" : !(regs->rx_fifo.sta & MCP251XFD_REG_FIFOSTA_TFNRFNIF) ? " chip-FIFO-empty" : "") : (""), (mcp251xfd_get_ring_head(rx) == mcp251xfd_get_ring_tail(rx) && mcp251xfd_get_ring_tail(rx) == n ? (priv->rx->head == priv->rx->tail ? " priv-FIFO-empty" : " priv-FIFO-full") : (""))); pr_info("%16s = 0x%08x\n", "id", hw_rx_obj->id); pr_info("%16s = 0x%08x\n", "flags", hw_rx_obj->flags); pr_info("%16s = 0x%08x\n", "ts", hw_rx_obj->ts); mcp251xfd_dump_ram_fifo_obj_data(priv, hw_rx_obj->data, FIELD_GET(MCP251XFD_OBJ_FLAGS_DLC, hw_rx_obj->flags)); pr_info("\n"); } static void mcp251xfd_dump_ram_rx_obj(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs, const struct mcp251xfd_dump_ram *ram, const struct mcp251xfd_ring *rx) { int i; pr_info("\nRX Overview:\n"); pr_info("%16s = 0x%02x 0x%02x 0x%08x\n", "head (c/p)", mcp251xfd_dump_get_rx_head(priv, regs), mcp251xfd_get_ring_head(rx), rx->head); pr_info("%16s = 0x%02x 0x%02x 0x%08x\n", "tail (c/p)", mcp251xfd_dump_get_rx_tail(priv, regs), mcp251xfd_get_ring_tail(rx), rx->tail); pr_info("\n"); for (i = 0; i < mcp251xfd_dump_get_rx_obj_num(priv, regs); i++) { const struct mcp251xfd_hw_rx_obj_canfd *hw_rx_obj; u16 hw_rx_obj_rel_addr; hw_rx_obj_rel_addr = mcp251xfd_dump_get_rx_obj_rel_addr(priv, regs, i); hw_rx_obj = (const struct mcp251xfd_hw_rx_obj_canfd *)&ram->ram[hw_rx_obj_rel_addr]; mcp251xfd_dump_ram_rx_obj_one(priv, regs, rx, hw_rx_obj, i); } } #undef __dump_mask #undef __dump_bit static void mcp251xfd_dump_ram(const struct mcp251xfd_priv *priv, const struct mcp251xfd_dump_regs *regs, const struct mcp251xfd_dump_ram *ram) { netdev_info(priv->ndev, "----------------------- RAM dump ----------------------\n"); mcp251xfd_dump_ram_tef_obj(priv, regs, ram, priv->tef); mcp251xfd_dump_ram_tx_obj(priv, regs, ram, priv->tx); mcp251xfd_dump_ram_rx_obj(priv, regs, ram, priv->rx); netdev_info(priv->ndev, "------------------------- end -------------------------\n"); } void mcp251xfd_dump(struct mcp251xfd_priv *priv) { struct mcp251xfd_dump_regs regs; struct mcp251xfd_dump_ram ram; struct mcp251xfd_dump_regs_mcp251xfd regs_mcp251xfd; int err; BUILD_BUG_ON(sizeof(struct mcp251xfd_dump_regs) != MCP251XFD_REG_FIFOUA(31) - MCP251XFD_REG_CON + 4); err = regmap_bulk_read(priv->map, MCP251XFD_REG_CON, ®s, sizeof(regs) / sizeof(u32)); if (err) return; err = regmap_bulk_read(priv->map, MCP251XFD_RAM_START, &ram, sizeof(ram) / sizeof(u32)); if (err) return; err = regmap_bulk_read(priv->map, MCP251XFD_REG_OSC, ®s_mcp251xfd, sizeof(regs_mcp251xfd) / sizeof(u32)); if (err) return; mcp251xfd_dump_regs(priv, ®s, ®s_mcp251xfd); mcp251xfd_dump_ram(priv, ®s, &ram); }