mcp251xfd-dump: add tool to decode chip and driver state of mcp251xfd

It works on dev coredump data generated by the mcp251xfd driver in
case of failures, as well as on regmap based register dumps.

Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
Marc Kleine-Budde
2021-02-13 23:36:43 +01:00
parent e8c90ddff9
commit 66de96d337
13 changed files with 1913 additions and 1 deletions
+863
View File
@@ -0,0 +1,863 @@
// SPDX-License-Identifier: GPL-2.0
//
// Microchip MCP251xFD Family CAN controller debug tool
//
// Copyright (c) 2019, 2020, 2021 Pengutronix,
// Marc Kleine-Budde <kernel@pengutronix.de>
//
#include <linux/kernel.h>
#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,
&regs, 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,
&regs_mcp251xfd, sizeof(regs_mcp251xfd) / sizeof(u32));
if (err)
return;
mcp251xfd_dump_regs(priv, &regs, &regs_mcp251xfd);
mcp251xfd_dump_ram(priv, &regs, &ram);
}