diff --git a/.gitignore b/.gitignore index fb2edb5..568f070 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ GNUmakefile.in /config/m4/ltsugar.m4 /config/m4/ltversion.m4 /config/m4/lt~obsolete.m4 +/mcp251xfd/.dirstamp /asc2log /bcmserver @@ -47,6 +48,7 @@ GNUmakefile.in /j1939sr /log2asc /log2long +/mcp251xfd-dump /slcan_attach /slcand /slcanpty diff --git a/CMakeLists.txt b/CMakeLists.txt index 33e0b0b..cc13c37 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,6 +65,13 @@ set(PROGRAMS slcanpty ) +add_executable(mcp251xfd-dump + mcp251xfd/mcp251xfd-dev-coredump.c + mcp251xfd/mcp251xfd-dump.c + mcp251xfd/mcp251xfd-main.c + mcp251xfd/mcp251xfd-regmap.c +) + if(NOT ANDROID) list(APPEND PROGRAMS ${PROGRAMS_J1939}) diff --git a/GNUmakefile.am b/GNUmakefile.am index 10f8584..c70f1c8 100644 --- a/GNUmakefile.am +++ b/GNUmakefile.am @@ -24,6 +24,7 @@ noinst_HEADERS = \ include/linux/can/raw.h \ include/linux/can/vxcan.h \ include/linux/errqueue.h \ + include/linux/kernel.h \ include/linux/net_tstamp.h \ include/linux/netlink.h @@ -38,6 +39,15 @@ libcan_la_SOURCES = \ libj1939_la_SOURCES = \ libj1939.c +mcp251xfd_dump_SOURCES = \ + mcp251xfd/mcp251xfd-dev-coredump.c \ + mcp251xfd/mcp251xfd-dump-userspace.h \ + mcp251xfd/mcp251xfd-dump.c \ + mcp251xfd/mcp251xfd-dump.h \ + mcp251xfd/mcp251xfd-main.c \ + mcp251xfd/mcp251xfd-regmap.c \ + mcp251xfd/mcp251xfd.h + bin_PROGRAMS = \ asc2log \ bcmserver \ @@ -65,6 +75,7 @@ bin_PROGRAMS = \ j1939sr \ log2asc \ log2long \ + mcp251xfd-dump \ slcan_attach \ slcand \ slcanpty \ diff --git a/Makefile b/Makefile index 2810d9e..1bd9e88 100644 --- a/Makefile +++ b/Makefile @@ -96,6 +96,7 @@ PROGRAMS := \ cansniffer \ log2asc \ log2long \ + mcp251xfd-dump \ slcanpty all: $(PROGRAMS) @@ -142,3 +143,6 @@ j1939spy: j1939spy.o libj1939.o j1939sr: j1939sr.o libj1939.o testj1939: testj1939.o libj1939.o canbusload: canbusload.o canframelen.o + +mcp251xfd-dump: mcp251xfd/mcp251xfd-dev-coredump.o mcp251xfd/mcp251xfd-dump.o mcp251xfd/mcp251xfd-main.o mcp251xfd/mcp251xfd-regmap.o + $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@ diff --git a/configure.ac b/configure.ac index 2d4993b..f44a5e2 100644 --- a/configure.ac +++ b/configure.ac @@ -19,7 +19,7 @@ CFLAGS="${CFLAGS} -Wall" AC_PROG_CC LT_INIT(win32-dll) -AM_INIT_AUTOMAKE([foreign no-exeext dist-bzip2]) +AM_INIT_AUTOMAKE([foreign no-exeext dist-bzip2 subdir-objects]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) # diff --git a/include/linux/kernel.h b/include/linux/kernel.h new file mode 100644 index 0000000..b7cbdb3 --- /dev/null +++ b/include/linux/kernel.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _LINUX_KERNEL_H +#define _LINUX_KERNEL_H + +#include +#include +#include +#include + +#include + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint32_t __le32; + +struct mcp251xfd_mem; + +struct regmap { + struct mcp251xfd_mem *mem; +}; + +#define pr_info(...) fprintf(stdout, ## __VA_ARGS__) +#define pr_cont(...) fprintf(stdout, ## __VA_ARGS__) +#define netdev_info(ndev, ...) fprintf(stdout, ## __VA_ARGS__) +#define BUILD_BUG_ON(...) + +#define BITS_PER_LONG (sizeof(long) * 8) + +#define ____cacheline_aligned + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +int regmap_bulk_read(struct regmap *map, unsigned int reg, + void *val, size_t val_count); + +#define SZ_2K 0x00000800 + +#define __packed __attribute__((__packed__)) + +#define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER)) + +#define BIT(nr) (UL(1) << (nr)) + +#define __stringify_1(x...) #x +#define __stringify(x...) __stringify_1(x) + +#ifdef __ASSEMBLY__ +#define _AC(X,Y) X +#define _AT(T,X) X +#else +#define __AC(X,Y) (X##Y) +#define _AC(X,Y) __AC(X,Y) +#define _AT(T,X) ((T)(X)) +#endif + +#define _UL(x) (_AC(x, UL)) +#define _ULL(x) (_AC(x, ULL)) + +#define UL(x) (_UL(x)) +#define ULL(x) (_ULL(x)) + +#define GENMASK(h, l) \ + (((~UL(0)) - (UL(1) << (l)) + 1) & \ + (~UL(0) >> (BITS_PER_LONG - 1 - (h)))) + +#define __bf_shf(x) (__builtin_ffsll(x) - 1) + +#define FIELD_PREP(_mask, _val) \ + ({ \ + ((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask); \ + }) + +#define FIELD_GET(_mask, _reg) \ + ({ \ + (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \ + }) + +#define min_t(type, x, y) ({ \ + type __min1 = (x); \ + type __min2 = (y); \ + __min1 < __min2 ? __min1 : __min2; }) + +#define get_canfd_dlc(i) (min_t(__u8, (i), CANFD_MAX_DLC)) + +static const u8 dlc2len[] = {0, 1, 2, 3, 4, 5, 6, 7, + 8, 12, 16, 20, 24, 32, 48, 64}; + +/* get data length from can_dlc with sanitized can_dlc */ +static inline u8 can_dlc2len(u8 can_dlc) +{ + return dlc2len[can_dlc & 0x0F]; +} + +#endif /* _LINUX_KERNEL_H */ diff --git a/mcp251xfd/mcp251xfd-dev-coredump.c b/mcp251xfd/mcp251xfd-dev-coredump.c new file mode 100644 index 0000000..5874d24 --- /dev/null +++ b/mcp251xfd/mcp251xfd-dev-coredump.c @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Microchip MCP251xFD Family CAN controller debug tool +// +// Copyright (c) 2020, 2021 Pengutronix, +// Marc Kleine-Budde +// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "mcp251xfd.h" +#include "mcp251xfd-dump-userspace.h" + +#define pr_err(fmt, args...) fprintf(stderr, fmt, ##args) +#define pr_no(fmt, args...) while (0) { fprintf(stdout, fmt, ##args); } + +#ifdef DEBUG +#define pr_debug(fmt, args...) pr_err(fmt, ##args) +#else +#define pr_debug(fmt, args...) pr_no(fmt, ##args) +#endif + + +struct mcp251xfd_dump_iter { + const void *start; + const struct mcp251xfd_dump_object_header *hdr; + const void *object_start; + const void *object_end; +}; + +static __attribute__((__unused__)) const char * +get_object_type_str(enum mcp251xfd_dump_object_type object_type) +{ + switch (object_type) { + case MCP251XFD_DUMP_OBJECT_TYPE_REG: + return "reg"; + case MCP251XFD_DUMP_OBJECT_TYPE_TEF: + return "tef"; + case MCP251XFD_DUMP_OBJECT_TYPE_RX: + return "rx"; + case MCP251XFD_DUMP_OBJECT_TYPE_TX: + return "tx"; + case MCP251XFD_DUMP_OBJECT_TYPE_END: + return "end"; + default: + return ""; + } +} + +static __attribute__((__unused__)) const char * +get_ring_key_str(enum mcp251xfd_dump_object_ring_key key) +{ + switch (key) { + case MCP251XFD_DUMP_OBJECT_RING_KEY_HEAD: + return "head"; + case MCP251XFD_DUMP_OBJECT_RING_KEY_TAIL: + return "tail"; + case MCP251XFD_DUMP_OBJECT_RING_KEY_BASE: + return "base"; + case MCP251XFD_DUMP_OBJECT_RING_KEY_NR: + return "nr"; + case MCP251XFD_DUMP_OBJECT_RING_KEY_FIFO_NR: + return "fifo-nr"; + case MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_NUM: + return "obj-num"; + case MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_SIZE: + return "obj-size"; + default: + return ""; + } +} + +static int +do_dev_coredump_read_reg(const struct mcp251xfd_priv *priv, + const struct mcp251xfd_dump_iter *iter, + struct mcp251xfd_mem *mem) +{ + const struct mcp251xfd_dump_object_reg *object; + + for (object = iter->object_start; + (void *)(object + 1) <= iter->object_end; + object++) { + uint32_t reg, val; + + reg = le32toh(object->reg); + val = le32toh(object->val); + + pr_debug("%s: object=0x%04zx reg=0x%04x - val=0x%08x\n", + __func__, + (void *)object - iter->start, + reg, val); + + if (reg > ARRAY_SIZE(mem->buf)) + return -EINVAL; + + *(uint32_t *)(mem->buf + reg) = val; + } + + return 0; +} + +static int +do_dev_coredump_read_ring(const struct mcp251xfd_priv *priv, + const struct mcp251xfd_dump_iter *iter, + struct mcp251xfd_ring *ring) +{ + const struct mcp251xfd_dump_object_reg *object; + + for (object = iter->object_start; + (void *)(object + 1) <= iter->object_end; + object++) { + enum mcp251xfd_dump_object_ring_key key; + uint32_t val; + + key = le32toh(object->reg); + val = le32toh(object->val); + + pr_debug("%s: reg=0x%04zx key=0x%02x: %8s - val=0x%08x\n", + __func__, + (void *)object - iter->start, + key, get_ring_key_str(key), val); + + switch (key) { + case MCP251XFD_DUMP_OBJECT_RING_KEY_HEAD: + ring->head = val; + break; + case MCP251XFD_DUMP_OBJECT_RING_KEY_TAIL: + ring->tail = val; + break; + case MCP251XFD_DUMP_OBJECT_RING_KEY_BASE: + ring->base = val; + break; + case MCP251XFD_DUMP_OBJECT_RING_KEY_NR: + ring->nr = val; + break; + case MCP251XFD_DUMP_OBJECT_RING_KEY_FIFO_NR: + ring->fifo_nr = val; + break; + case MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_NUM: + ring->obj_num = val; + break; + case MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_SIZE: + ring->obj_size = val; + break; + default: + return -EINVAL; + } + } + + return 0; +} + +static int +do_dev_coredump_read(struct mcp251xfd_priv *priv, + struct mcp251xfd_mem *mem, + const void *dump, size_t dump_len) +{ + struct mcp251xfd_dump_iter iter[] = { + { + .start = dump, + .hdr = dump, + }, + }; + + while ((void *)(iter->hdr + 1) <= iter->start + dump_len && + le32toh(iter->hdr->magic) == MCP251XFD_DUMP_MAGIC) { + const struct mcp251xfd_dump_object_header *hdr = iter->hdr; + enum mcp251xfd_dump_object_type object_type; + size_t object_offset, object_len; + int err; + + object_type = le32toh(hdr->type); + object_offset = le32toh(hdr->offset); + object_len = le32toh(hdr->len); + + if (object_offset + object_len > dump_len) + return -EFAULT; + + iter->object_start = iter->start + object_offset; + iter->object_end = iter->object_start + object_len; + + pr_debug("%s: hdr=0x%04zx type=0x%08x: %8s - offset=0x%04zx len=0x%04zx end=0x%04zx\n", + __func__, + (void *)iter->hdr - iter->start, + object_type, get_object_type_str(object_type), + object_offset, object_len, object_offset + object_len); + + switch (object_type) { + case MCP251XFD_DUMP_OBJECT_TYPE_REG: + err = do_dev_coredump_read_reg(priv, iter, mem); + break; + case MCP251XFD_DUMP_OBJECT_TYPE_TEF: + err = do_dev_coredump_read_ring(priv, iter, priv->tef); + break; + case MCP251XFD_DUMP_OBJECT_TYPE_RX: + err = do_dev_coredump_read_ring(priv, iter, priv->rx); + break; + case MCP251XFD_DUMP_OBJECT_TYPE_TX: + err = do_dev_coredump_read_ring(priv, iter, priv->tx); + break; + case MCP251XFD_DUMP_OBJECT_TYPE_END: + return 0; + default: + return -EINVAL; + } + + if (err) + return err; + + iter->hdr++; + } + + return -EINVAL; +} + +int mcp251xfd_dev_coredump_read(struct mcp251xfd_priv *priv, + struct mcp251xfd_mem *mem, + const char *dump_path) +{ + struct stat statbuf; + size_t dump_len; + void *dump; + int fd, err; + + fd = open(dump_path, O_RDONLY); + if (fd < 0) + return -errno; + + err = fstat(fd, &statbuf); + if (err < 0) { + err = -errno; + goto out_close; + } + dump_len = statbuf.st_size; + + dump = mmap(NULL, dump_len, PROT_READ, MAP_SHARED, fd, 0x0); + if (dump == MAP_FAILED) { + err = -errno; + goto out_close; + } + + err = do_dev_coredump_read(priv, mem, dump, dump_len); + + munmap(dump, dump_len); + out_close: + close(fd); + return err; +} diff --git a/mcp251xfd/mcp251xfd-dump-userspace.h b/mcp251xfd/mcp251xfd-dump-userspace.h new file mode 100644 index 0000000..890821e --- /dev/null +++ b/mcp251xfd/mcp251xfd-dump-userspace.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Microchip MCP251xFD Family CAN controller debug tool + * + * Copyright (c) 2019, 2020 Pengutronix, + * Marc Kleine-Budde + */ + +#ifndef _MCP251XFD_DUMP_USERSPACE_H +#define _MCP251XFD_DUMP_USERSPACE_H + +#include "mcp251xfd.h" +#include "mcp251xfd-dump.h" + +#define MCP251XFD_TX_FIFO 1 +#define MCP251XFD_RX_FIFO(x) (MCP251XFD_TX_FIFO + 1 + (x)) + +struct mcp251xfd_mem { + char buf[0x1000]; +}; + +struct mcp251xfd_ring { + unsigned int head; + unsigned int tail; + + u16 base; + u8 nr; + u8 fifo_nr; + u8 obj_num; + u8 obj_size; +}; + +struct mcp251xfd_priv { + struct regmap *map; + + struct mcp251xfd_ring tef[1]; + struct mcp251xfd_ring tx[1]; + struct mcp251xfd_ring rx[1]; + + u8 rx_ring_num; +}; + +static inline u8 mcp251xfd_get_ring_head(const struct mcp251xfd_ring *ring) +{ + return ring->head & (ring->obj_num - 1); +} + +static inline u8 mcp251xfd_get_ring_tail(const struct mcp251xfd_ring *ring) +{ + return ring->tail & (ring->obj_num - 1); +} + +void mcp251xfd_dump(struct mcp251xfd_priv *priv); +int mcp251xfd_dev_coredump_read(struct mcp251xfd_priv *priv, + struct mcp251xfd_mem *mem, + const char *file_path); +int mcp251xfd_regmap_read(struct mcp251xfd_priv *priv, + struct mcp251xfd_mem *mem, + const char *file_path); + +#endif diff --git a/mcp251xfd/mcp251xfd-dump.c b/mcp251xfd/mcp251xfd-dump.c new file mode 100644 index 0000000..c496340 --- /dev/null +++ b/mcp251xfd/mcp251xfd-dump.c @@ -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 +// + +#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); +} diff --git a/mcp251xfd/mcp251xfd-dump.h b/mcp251xfd/mcp251xfd-dump.h new file mode 100644 index 0000000..e7560b0 --- /dev/null +++ b/mcp251xfd/mcp251xfd-dump.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * mcp251xfd - Microchip MCP251xFD Family CAN controller driver + * + * Copyright (c) 2019, 2020, 2021 Pengutronix, + * Marc Kleine-Budde + */ + +#ifndef _MCP251XFD_DUMP_H +#define _MCP251XFD_DUMP_H + +#define MCP251XFD_DUMP_MAGIC 0x1825434d + +enum mcp251xfd_dump_object_type { + MCP251XFD_DUMP_OBJECT_TYPE_REG, + MCP251XFD_DUMP_OBJECT_TYPE_TEF, + MCP251XFD_DUMP_OBJECT_TYPE_RX, + MCP251XFD_DUMP_OBJECT_TYPE_TX, + MCP251XFD_DUMP_OBJECT_TYPE_END = -1, +}; + +enum mcp251xfd_dump_object_ring_key { + MCP251XFD_DUMP_OBJECT_RING_KEY_HEAD, + MCP251XFD_DUMP_OBJECT_RING_KEY_TAIL, + MCP251XFD_DUMP_OBJECT_RING_KEY_BASE, + MCP251XFD_DUMP_OBJECT_RING_KEY_NR, + MCP251XFD_DUMP_OBJECT_RING_KEY_FIFO_NR, + MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_NUM, + MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_SIZE, + __MCP251XFD_DUMP_OBJECT_RING_KEY_MAX, +}; + +struct mcp251xfd_dump_object_header { + __le32 magic; + __le32 type; + __le32 offset; + __le32 len; +}; + +struct mcp251xfd_dump_object_reg { + __le32 reg; + __le32 val; +}; + +#endif diff --git a/mcp251xfd/mcp251xfd-main.c b/mcp251xfd/mcp251xfd-main.c new file mode 100644 index 0000000..c5a10e0 --- /dev/null +++ b/mcp251xfd/mcp251xfd-main.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Microchip MCP251xFD Family CAN controller debug tool +// +// Copyright (c) 2020, 2021 Pengutronix, +// Marc Kleine-Budde +// + +#include +#include +#include +#include + +#include + +#include "mcp251xfd-dump-userspace.h" + +static void print_usage(char *prg) +{ + fprintf(stderr, + "%s - decode chip and driver state of mcp251xfd.\n" + "\n" + "Usage: %s [options] \n" + "\n" + " path to dev coredump file\n" + " ('/var/log/devcoredump-19700101-234200.dump')\n" + " path to regmap register file\n" + " ('/sys/kernel/debug/regmap/spi1.0-crc/registers')\n" + " shortcut to regmap register file\n" + " ('spi0.0')\n" + "\n" + "Options:\n" + " -h, --help this help\n" + "\n", + prg, prg); +} + +int regmap_bulk_read(struct regmap *map, unsigned int reg, + void *val, size_t val_count) +{ + memcpy(val, map->mem->buf + reg, + val_count * sizeof(uint32_t)); + + return 0; +} + +int main(int argc, char *argv[]) +{ + struct mcp251xfd_mem mem = { }; + struct regmap map = { + .mem = &mem, + }; + struct mcp251xfd_priv priv = { + .map = &map, + }; + const char *file_path; + int opt, err; + + struct option long_options[] = { + { "help", no_argument, 0, 'h' }, + { 0, 0, 0, 0 }, + }; + + while ((opt = getopt_long(argc, argv, "ei:pq::rvh", long_options, NULL)) != -1) { + switch (opt) { + case 'h': + print_usage(basename(argv[0])); + exit(EXIT_SUCCESS); + break; + + default: + print_usage(basename(argv[0])); + exit(EXIT_FAILURE); + break; + } + } + + file_path = argv[optind]; + + if (!file_path) { + print_usage(basename(argv[0])); + exit(EXIT_FAILURE); + } + + err = mcp251xfd_dev_coredump_read(&priv, &mem, file_path); + if (err) + err = mcp251xfd_regmap_read(&priv, &mem, file_path); + if (err) { + fprintf(stderr, "Unable to read file: '%s'\n", file_path); + exit(EXIT_FAILURE); + } + + mcp251xfd_dump(&priv); + + exit(EXIT_SUCCESS); +} diff --git a/mcp251xfd/mcp251xfd-regmap.c b/mcp251xfd/mcp251xfd-regmap.c new file mode 100644 index 0000000..61105f0 --- /dev/null +++ b/mcp251xfd/mcp251xfd-regmap.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Microchip MCP251xFD Family CAN controller debug tool +// +// Copyright (c) 2020 Pengutronix, +// Marc Kleine-Budde +// + +#include +#include +#include +#include +#include +#include + +#include + +#include "mcp251xfd.h" +#include "mcp251xfd-dump-userspace.h" + +static int +do_mcp251xfd_regmap_read(struct mcp251xfd_priv *priv, + struct mcp251xfd_mem *mem, + const char *file_path) +{ + FILE *reg_file; + uint16_t reg; + uint32_t val; + + reg_file = fopen(file_path, "r"); + if (!reg_file) + return -errno; + + while (fscanf(reg_file, "%hx: %x\n", ®, &val) == 2) { + if (reg >= ARRAY_SIZE(mem->buf)) + return -EINVAL; + + *(uint32_t *)(mem->buf + reg) = val; + } + + fclose(reg_file); + + return 0; +} + +int mcp251xfd_regmap_read(struct mcp251xfd_priv *priv, + struct mcp251xfd_mem *mem, + const char *file_path) +{ + char *tmp; + int err; + + err = do_mcp251xfd_regmap_read(priv, mem, file_path); + if (!err) + return 0; + + /* maybe it's something like "spi0.0" */ + tmp = strchr(file_path, '/'); + if (tmp) + return -ENOENT; + + /* first try literally */ + err = asprintf(&tmp, "/sys/kernel/debug/regmap/%s/registers", file_path); + if (err == -1) + return -errno; + + err = do_mcp251xfd_regmap_read(priv, mem, tmp); + free (tmp); + if (!err) + return 0; + + /* then add "-crc" */ + err = asprintf(&tmp, "/sys/kernel/debug/regmap/%s-crc/registers", file_path); + if (err == -1) + return -errno; + + err = do_mcp251xfd_regmap_read(priv, mem, tmp); + free (tmp); + + return err; +} diff --git a/mcp251xfd/mcp251xfd.h b/mcp251xfd/mcp251xfd.h new file mode 100644 index 0000000..42b9621 --- /dev/null +++ b/mcp251xfd/mcp251xfd.h @@ -0,0 +1,389 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * mcp251xfd - Microchip MCP251xFD Family CAN controller driver + * + * Copyright (c) 2019, 2020 Pengutronix, + * Marc Kleine-Budde + * Copyright (c) 2019 Martin Sperl + */ + +#ifndef _MCP251XFD_H +#define _MCP251XFD_H + +/* MPC251x registers */ + +/* CAN FD Controller Module SFR */ +#define MCP251XFD_REG_CON 0x00 +#define MCP251XFD_REG_CON_TXBWS_MASK GENMASK(31, 28) +#define MCP251XFD_REG_CON_ABAT BIT(27) +#define MCP251XFD_REG_CON_REQOP_MASK GENMASK(26, 24) +#define MCP251XFD_REG_CON_MODE_MIXED 0 +#define MCP251XFD_REG_CON_MODE_SLEEP 1 +#define MCP251XFD_REG_CON_MODE_INT_LOOPBACK 2 +#define MCP251XFD_REG_CON_MODE_LISTENONLY 3 +#define MCP251XFD_REG_CON_MODE_CONFIG 4 +#define MCP251XFD_REG_CON_MODE_EXT_LOOPBACK 5 +#define MCP251XFD_REG_CON_MODE_CAN2_0 6 +#define MCP251XFD_REG_CON_MODE_RESTRICTED 7 +#define MCP251XFD_REG_CON_OPMOD_MASK GENMASK(23, 21) +#define MCP251XFD_REG_CON_TXQEN BIT(20) +#define MCP251XFD_REG_CON_STEF BIT(19) +#define MCP251XFD_REG_CON_SERR2LOM BIT(18) +#define MCP251XFD_REG_CON_ESIGM BIT(17) +#define MCP251XFD_REG_CON_RTXAT BIT(16) +#define MCP251XFD_REG_CON_BRSDIS BIT(12) +#define MCP251XFD_REG_CON_BUSY BIT(11) +#define MCP251XFD_REG_CON_WFT_MASK GENMASK(10, 9) +#define MCP251XFD_REG_CON_WFT_T00FILTER 0x0 +#define MCP251XFD_REG_CON_WFT_T01FILTER 0x1 +#define MCP251XFD_REG_CON_WFT_T10FILTER 0x2 +#define MCP251XFD_REG_CON_WFT_T11FILTER 0x3 +#define MCP251XFD_REG_CON_WAKFIL BIT(8) +#define MCP251XFD_REG_CON_PXEDIS BIT(6) +#define MCP251XFD_REG_CON_ISOCRCEN BIT(5) +#define MCP251XFD_REG_CON_DNCNT_MASK GENMASK(4, 0) + +#define MCP251XFD_REG_NBTCFG 0x04 +#define MCP251XFD_REG_NBTCFG_BRP_MASK GENMASK(31, 24) +#define MCP251XFD_REG_NBTCFG_TSEG1_MASK GENMASK(23, 16) +#define MCP251XFD_REG_NBTCFG_TSEG2_MASK GENMASK(14, 8) +#define MCP251XFD_REG_NBTCFG_SJW_MASK GENMASK(6, 0) + +#define MCP251XFD_REG_DBTCFG 0x08 +#define MCP251XFD_REG_DBTCFG_BRP_MASK GENMASK(31, 24) +#define MCP251XFD_REG_DBTCFG_TSEG1_MASK GENMASK(20, 16) +#define MCP251XFD_REG_DBTCFG_TSEG2_MASK GENMASK(11, 8) +#define MCP251XFD_REG_DBTCFG_SJW_MASK GENMASK(3, 0) + +#define MCP251XFD_REG_TDC 0x0c +#define MCP251XFD_REG_TDC_EDGFLTEN BIT(25) +#define MCP251XFD_REG_TDC_SID11EN BIT(24) +#define MCP251XFD_REG_TDC_TDCMOD_MASK GENMASK(17, 16) +#define MCP251XFD_REG_TDC_TDCMOD_AUTO 2 +#define MCP251XFD_REG_TDC_TDCMOD_MANUAL 1 +#define MCP251XFD_REG_TDC_TDCMOD_DISABLED 0 +#define MCP251XFD_REG_TDC_TDCO_MASK GENMASK(14, 8) +#define MCP251XFD_REG_TDC_TDCV_MASK GENMASK(5, 0) + +#define MCP251XFD_REG_TBC 0x10 + +#define MCP251XFD_REG_TSCON 0x14 +#define MCP251XFD_REG_TSCON_TSRES BIT(18) +#define MCP251XFD_REG_TSCON_TSEOF BIT(17) +#define MCP251XFD_REG_TSCON_TBCEN BIT(16) +#define MCP251XFD_REG_TSCON_TBCPRE_MASK GENMASK(9, 0) + +#define MCP251XFD_REG_VEC 0x18 +#define MCP251XFD_REG_VEC_RXCODE_MASK GENMASK(30, 24) +#define MCP251XFD_REG_VEC_TXCODE_MASK GENMASK(22, 16) +#define MCP251XFD_REG_VEC_FILHIT_MASK GENMASK(12, 8) +#define MCP251XFD_REG_VEC_ICODE_MASK GENMASK(6, 0) + +#define MCP251XFD_REG_INT 0x1c +#define MCP251XFD_REG_INT_IF_MASK GENMASK(15, 0) +#define MCP251XFD_REG_INT_IE_MASK GENMASK(31, 16) +#define MCP251XFD_REG_INT_IVMIE BIT(31) +#define MCP251XFD_REG_INT_WAKIE BIT(30) +#define MCP251XFD_REG_INT_CERRIE BIT(29) +#define MCP251XFD_REG_INT_SERRIE BIT(28) +#define MCP251XFD_REG_INT_RXOVIE BIT(27) +#define MCP251XFD_REG_INT_TXATIE BIT(26) +#define MCP251XFD_REG_INT_SPICRCIE BIT(25) +#define MCP251XFD_REG_INT_ECCIE BIT(24) +#define MCP251XFD_REG_INT_TEFIE BIT(20) +#define MCP251XFD_REG_INT_MODIE BIT(19) +#define MCP251XFD_REG_INT_TBCIE BIT(18) +#define MCP251XFD_REG_INT_RXIE BIT(17) +#define MCP251XFD_REG_INT_TXIE BIT(16) +#define MCP251XFD_REG_INT_IVMIF BIT(15) +#define MCP251XFD_REG_INT_WAKIF BIT(14) +#define MCP251XFD_REG_INT_CERRIF BIT(13) +#define MCP251XFD_REG_INT_SERRIF BIT(12) +#define MCP251XFD_REG_INT_RXOVIF BIT(11) +#define MCP251XFD_REG_INT_TXATIF BIT(10) +#define MCP251XFD_REG_INT_SPICRCIF BIT(9) +#define MCP251XFD_REG_INT_ECCIF BIT(8) +#define MCP251XFD_REG_INT_TEFIF BIT(4) +#define MCP251XFD_REG_INT_MODIF BIT(3) +#define MCP251XFD_REG_INT_TBCIF BIT(2) +#define MCP251XFD_REG_INT_RXIF BIT(1) +#define MCP251XFD_REG_INT_TXIF BIT(0) +/* These IRQ flags must be cleared by SW in the CAN_INT register */ +#define MCP251XFD_REG_INT_IF_CLEARABLE_MASK \ + (MCP251XFD_REG_INT_IVMIF | MCP251XFD_REG_INT_WAKIF | \ + MCP251XFD_REG_INT_CERRIF | MCP251XFD_REG_INT_SERRIF | \ + MCP251XFD_REG_INT_MODIF) + +#define MCP251XFD_REG_RXIF 0x20 +#define MCP251XFD_REG_TXIF 0x24 +#define MCP251XFD_REG_RXOVIF 0x28 +#define MCP251XFD_REG_TXATIF 0x2c +#define MCP251XFD_REG_TXREQ 0x30 + +#define MCP251XFD_REG_TREC 0x34 +#define MCP251XFD_REG_TREC_TXBO BIT(21) +#define MCP251XFD_REG_TREC_TXBP BIT(20) +#define MCP251XFD_REG_TREC_RXBP BIT(19) +#define MCP251XFD_REG_TREC_TXWARN BIT(18) +#define MCP251XFD_REG_TREC_RXWARN BIT(17) +#define MCP251XFD_REG_TREC_EWARN BIT(16) +#define MCP251XFD_REG_TREC_TEC_MASK GENMASK(15, 8) +#define MCP251XFD_REG_TREC_REC_MASK GENMASK(7, 0) + +#define MCP251XFD_REG_BDIAG0 0x38 +#define MCP251XFD_REG_BDIAG0_DTERRCNT_MASK GENMASK(31, 24) +#define MCP251XFD_REG_BDIAG0_DRERRCNT_MASK GENMASK(23, 16) +#define MCP251XFD_REG_BDIAG0_NTERRCNT_MASK GENMASK(15, 8) +#define MCP251XFD_REG_BDIAG0_NRERRCNT_MASK GENMASK(7, 0) + +#define MCP251XFD_REG_BDIAG1 0x3c +#define MCP251XFD_REG_BDIAG1_DLCMM BIT(31) +#define MCP251XFD_REG_BDIAG1_ESI BIT(30) +#define MCP251XFD_REG_BDIAG1_DCRCERR BIT(29) +#define MCP251XFD_REG_BDIAG1_DSTUFERR BIT(28) +#define MCP251XFD_REG_BDIAG1_DFORMERR BIT(27) +#define MCP251XFD_REG_BDIAG1_DBIT1ERR BIT(25) +#define MCP251XFD_REG_BDIAG1_DBIT0ERR BIT(24) +#define MCP251XFD_REG_BDIAG1_TXBOERR BIT(23) +#define MCP251XFD_REG_BDIAG1_NCRCERR BIT(21) +#define MCP251XFD_REG_BDIAG1_NSTUFERR BIT(20) +#define MCP251XFD_REG_BDIAG1_NFORMERR BIT(19) +#define MCP251XFD_REG_BDIAG1_NACKERR BIT(18) +#define MCP251XFD_REG_BDIAG1_NBIT1ERR BIT(17) +#define MCP251XFD_REG_BDIAG1_NBIT0ERR BIT(16) +#define MCP251XFD_REG_BDIAG1_BERR_MASK \ + (MCP251XFD_REG_BDIAG1_DLCMM | MCP251XFD_REG_BDIAG1_ESI | \ + MCP251XFD_REG_BDIAG1_DCRCERR | MCP251XFD_REG_BDIAG1_DSTUFERR | \ + MCP251XFD_REG_BDIAG1_DFORMERR | MCP251XFD_REG_BDIAG1_DBIT1ERR | \ + MCP251XFD_REG_BDIAG1_DBIT0ERR | MCP251XFD_REG_BDIAG1_TXBOERR | \ + MCP251XFD_REG_BDIAG1_NCRCERR | MCP251XFD_REG_BDIAG1_NSTUFERR | \ + MCP251XFD_REG_BDIAG1_NFORMERR | MCP251XFD_REG_BDIAG1_NACKERR | \ + MCP251XFD_REG_BDIAG1_NBIT1ERR | MCP251XFD_REG_BDIAG1_NBIT0ERR) +#define MCP251XFD_REG_BDIAG1_EFMSGCNT_MASK GENMASK(15, 0) + +#define MCP251XFD_REG_TEFCON 0x40 +#define MCP251XFD_REG_TEFCON_FSIZE_MASK GENMASK(28, 24) +#define MCP251XFD_REG_TEFCON_FRESET BIT(10) +#define MCP251XFD_REG_TEFCON_UINC BIT(8) +#define MCP251XFD_REG_TEFCON_TEFTSEN BIT(5) +#define MCP251XFD_REG_TEFCON_TEFOVIE BIT(3) +#define MCP251XFD_REG_TEFCON_TEFFIE BIT(2) +#define MCP251XFD_REG_TEFCON_TEFHIE BIT(1) +#define MCP251XFD_REG_TEFCON_TEFNEIE BIT(0) + +#define MCP251XFD_REG_TEFSTA 0x44 +#define MCP251XFD_REG_TEFSTA_TEFOVIF BIT(3) +#define MCP251XFD_REG_TEFSTA_TEFFIF BIT(2) +#define MCP251XFD_REG_TEFSTA_TEFHIF BIT(1) +#define MCP251XFD_REG_TEFSTA_TEFNEIF BIT(0) + +#define MCP251XFD_REG_TEFUA 0x48 + +#define MCP251XFD_REG_TXQCON 0x50 +#define MCP251XFD_REG_TXQCON_PLSIZE_MASK GENMASK(31, 29) +#define MCP251XFD_REG_TXQCON_PLSIZE_8 0 +#define MCP251XFD_REG_TXQCON_PLSIZE_12 1 +#define MCP251XFD_REG_TXQCON_PLSIZE_16 2 +#define MCP251XFD_REG_TXQCON_PLSIZE_20 3 +#define MCP251XFD_REG_TXQCON_PLSIZE_24 4 +#define MCP251XFD_REG_TXQCON_PLSIZE_32 5 +#define MCP251XFD_REG_TXQCON_PLSIZE_48 6 +#define MCP251XFD_REG_TXQCON_PLSIZE_64 7 +#define MCP251XFD_REG_TXQCON_FSIZE_MASK GENMASK(28, 24) +#define MCP251XFD_REG_TXQCON_TXAT_UNLIMITED 3 +#define MCP251XFD_REG_TXQCON_TXAT_THREE_SHOT 1 +#define MCP251XFD_REG_TXQCON_TXAT_ONE_SHOT 0 +#define MCP251XFD_REG_TXQCON_TXAT_MASK GENMASK(22, 21) +#define MCP251XFD_REG_TXQCON_TXPRI_MASK GENMASK(20, 16) +#define MCP251XFD_REG_TXQCON_FRESET BIT(10) +#define MCP251XFD_REG_TXQCON_TXREQ BIT(9) +#define MCP251XFD_REG_TXQCON_UINC BIT(8) +#define MCP251XFD_REG_TXQCON_TXEN BIT(7) +#define MCP251XFD_REG_TXQCON_TXATIE BIT(4) +#define MCP251XFD_REG_TXQCON_TXQEIE BIT(2) +#define MCP251XFD_REG_TXQCON_TXQNIE BIT(0) + +#define MCP251XFD_REG_TXQSTA 0x54 +#define MCP251XFD_REG_TXQSTA_TXQCI_MASK GENMASK(12, 8) +#define MCP251XFD_REG_TXQSTA_TXABT BIT(7) +#define MCP251XFD_REG_TXQSTA_TXLARB BIT(6) +#define MCP251XFD_REG_TXQSTA_TXERR BIT(5) +#define MCP251XFD_REG_TXQSTA_TXATIF BIT(4) +#define MCP251XFD_REG_TXQSTA_TXQEIF BIT(2) +#define MCP251XFD_REG_TXQSTA_TXQNIF BIT(0) + +#define MCP251XFD_REG_TXQUA 0x58 + +#define MCP251XFD_REG_FIFOCON(x) (0x50 + 0xc * (x)) +#define MCP251XFD_REG_FIFOCON_PLSIZE_MASK GENMASK(31, 29) +#define MCP251XFD_REG_FIFOCON_PLSIZE_8 0 +#define MCP251XFD_REG_FIFOCON_PLSIZE_12 1 +#define MCP251XFD_REG_FIFOCON_PLSIZE_16 2 +#define MCP251XFD_REG_FIFOCON_PLSIZE_20 3 +#define MCP251XFD_REG_FIFOCON_PLSIZE_24 4 +#define MCP251XFD_REG_FIFOCON_PLSIZE_32 5 +#define MCP251XFD_REG_FIFOCON_PLSIZE_48 6 +#define MCP251XFD_REG_FIFOCON_PLSIZE_64 7 +#define MCP251XFD_REG_FIFOCON_FSIZE_MASK GENMASK(28, 24) +#define MCP251XFD_REG_FIFOCON_TXAT_MASK GENMASK(22, 21) +#define MCP251XFD_REG_FIFOCON_TXAT_ONE_SHOT 0 +#define MCP251XFD_REG_FIFOCON_TXAT_THREE_SHOT 1 +#define MCP251XFD_REG_FIFOCON_TXAT_UNLIMITED 3 +#define MCP251XFD_REG_FIFOCON_TXPRI_MASK GENMASK(20, 16) +#define MCP251XFD_REG_FIFOCON_FRESET BIT(10) +#define MCP251XFD_REG_FIFOCON_TXREQ BIT(9) +#define MCP251XFD_REG_FIFOCON_UINC BIT(8) +#define MCP251XFD_REG_FIFOCON_TXEN BIT(7) +#define MCP251XFD_REG_FIFOCON_RTREN BIT(6) +#define MCP251XFD_REG_FIFOCON_RXTSEN BIT(5) +#define MCP251XFD_REG_FIFOCON_TXATIE BIT(4) +#define MCP251XFD_REG_FIFOCON_RXOVIE BIT(3) +#define MCP251XFD_REG_FIFOCON_TFERFFIE BIT(2) +#define MCP251XFD_REG_FIFOCON_TFHRFHIE BIT(1) +#define MCP251XFD_REG_FIFOCON_TFNRFNIE BIT(0) + +#define MCP251XFD_REG_FIFOSTA(x) (0x54 + 0xc * (x)) +#define MCP251XFD_REG_FIFOSTA_FIFOCI_MASK GENMASK(12, 8) +#define MCP251XFD_REG_FIFOSTA_TXABT BIT(7) +#define MCP251XFD_REG_FIFOSTA_TXLARB BIT(6) +#define MCP251XFD_REG_FIFOSTA_TXERR BIT(5) +#define MCP251XFD_REG_FIFOSTA_TXATIF BIT(4) +#define MCP251XFD_REG_FIFOSTA_RXOVIF BIT(3) +#define MCP251XFD_REG_FIFOSTA_TFERFFIF BIT(2) +#define MCP251XFD_REG_FIFOSTA_TFHRFHIF BIT(1) +#define MCP251XFD_REG_FIFOSTA_TFNRFNIF BIT(0) + +#define MCP251XFD_REG_FIFOUA(x) (0x58 + 0xc * (x)) + +#define MCP251XFD_REG_FLTCON(x) (0x1d0 + 0x4 * (x)) +#define MCP251XFD_REG_FLTCON_FLTEN3 BIT(31) +#define MCP251XFD_REG_FLTCON_F3BP_MASK GENMASK(28, 24) +#define MCP251XFD_REG_FLTCON_FLTEN2 BIT(23) +#define MCP251XFD_REG_FLTCON_F2BP_MASK GENMASK(20, 16) +#define MCP251XFD_REG_FLTCON_FLTEN1 BIT(15) +#define MCP251XFD_REG_FLTCON_F1BP_MASK GENMASK(12, 8) +#define MCP251XFD_REG_FLTCON_FLTEN0 BIT(7) +#define MCP251XFD_REG_FLTCON_F0BP_MASK GENMASK(4, 0) +#define MCP251XFD_REG_FLTCON_FLTEN(x) (BIT(7) << 8 * ((x) & 0x3)) +#define MCP251XFD_REG_FLTCON_FLT_MASK(x) (GENMASK(7, 0) << (8 * ((x) & 0x3))) +#define MCP251XFD_REG_FLTCON_FBP(x, fifo) ((fifo) << 8 * ((x) & 0x3)) + +#define MCP251XFD_REG_FLTOBJ(x) (0x1f0 + 0x8 * (x)) +#define MCP251XFD_REG_FLTOBJ_EXIDE BIT(30) +#define MCP251XFD_REG_FLTOBJ_SID11 BIT(29) +#define MCP251XFD_REG_FLTOBJ_EID_MASK GENMASK(28, 11) +#define MCP251XFD_REG_FLTOBJ_SID_MASK GENMASK(10, 0) + +#define MCP251XFD_REG_FLTMASK(x) (0x1f4 + 0x8 * (x)) +#define MCP251XFD_REG_MASK_MIDE BIT(30) +#define MCP251XFD_REG_MASK_MSID11 BIT(29) +#define MCP251XFD_REG_MASK_MEID_MASK GENMASK(28, 11) +#define MCP251XFD_REG_MASK_MSID_MASK GENMASK(10, 0) + +/* RAM */ +#define MCP251XFD_RAM_START 0x400 +#define MCP251XFD_RAM_SIZE SZ_2K + +/* Message Object */ +#define MCP251XFD_OBJ_ID_SID11 BIT(29) +#define MCP251XFD_OBJ_ID_EID_MASK GENMASK(28, 11) +#define MCP251XFD_OBJ_ID_SID_MASK GENMASK(10, 0) +#define MCP251XFD_OBJ_FLAGS_SEQ_MCP2518FD_MASK GENMASK(31, 9) +#define MCP251XFD_OBJ_FLAGS_SEQ_MCP2517FD_MASK GENMASK(15, 9) +#define MCP251XFD_OBJ_FLAGS_SEQ_MASK MCP251XFD_OBJ_FLAGS_SEQ_MCP2518FD_MASK +#define MCP251XFD_OBJ_FLAGS_ESI BIT(8) +#define MCP251XFD_OBJ_FLAGS_FDF BIT(7) +#define MCP251XFD_OBJ_FLAGS_BRS BIT(6) +#define MCP251XFD_OBJ_FLAGS_RTR BIT(5) +#define MCP251XFD_OBJ_FLAGS_IDE BIT(4) +#define MCP251XFD_OBJ_FLAGS_DLC GENMASK(3, 0) + +#define MCP251XFD_REG_FRAME_EFF_SID_MASK GENMASK(28, 18) +#define MCP251XFD_REG_FRAME_EFF_EID_MASK GENMASK(17, 0) + +/* MCP2517/18FD SFR */ +#define MCP251XFD_REG_OSC 0xe00 +#define MCP251XFD_REG_OSC_SCLKRDY BIT(12) +#define MCP251XFD_REG_OSC_OSCRDY BIT(10) +#define MCP251XFD_REG_OSC_PLLRDY BIT(8) +#define MCP251XFD_REG_OSC_CLKODIV_10 3 +#define MCP251XFD_REG_OSC_CLKODIV_4 2 +#define MCP251XFD_REG_OSC_CLKODIV_2 1 +#define MCP251XFD_REG_OSC_CLKODIV_1 0 +#define MCP251XFD_REG_OSC_CLKODIV_MASK GENMASK(6, 5) +#define MCP251XFD_REG_OSC_SCLKDIV BIT(4) +#define MCP251XFD_REG_OSC_LPMEN BIT(3) /* MCP2518FD only */ +#define MCP251XFD_REG_OSC_OSCDIS BIT(2) +#define MCP251XFD_REG_OSC_PLLEN BIT(0) + +#define MCP251XFD_REG_IOCON 0xe04 +#define MCP251XFD_REG_IOCON_INTOD BIT(30) +#define MCP251XFD_REG_IOCON_SOF BIT(29) +#define MCP251XFD_REG_IOCON_TXCANOD BIT(28) +#define MCP251XFD_REG_IOCON_PM1 BIT(25) +#define MCP251XFD_REG_IOCON_PM0 BIT(24) +#define MCP251XFD_REG_IOCON_GPIO1 BIT(17) +#define MCP251XFD_REG_IOCON_GPIO0 BIT(16) +#define MCP251XFD_REG_IOCON_LAT1 BIT(9) +#define MCP251XFD_REG_IOCON_LAT0 BIT(8) +#define MCP251XFD_REG_IOCON_XSTBYEN BIT(6) +#define MCP251XFD_REG_IOCON_TRIS1 BIT(1) +#define MCP251XFD_REG_IOCON_TRIS0 BIT(0) + +#define MCP251XFD_REG_CRC 0xe08 +#define MCP251XFD_REG_CRC_FERRIE BIT(25) +#define MCP251XFD_REG_CRC_CRCERRIE BIT(24) +#define MCP251XFD_REG_CRC_FERRIF BIT(17) +#define MCP251XFD_REG_CRC_CRCERRIF BIT(16) +#define MCP251XFD_REG_CRC_IF_MASK GENMASK(17, 16) +#define MCP251XFD_REG_CRC_MASK GENMASK(15, 0) + +#define MCP251XFD_REG_ECCCON 0xe0c +#define MCP251XFD_REG_ECCCON_PARITY_MASK GENMASK(14, 8) +#define MCP251XFD_REG_ECCCON_DEDIE BIT(2) +#define MCP251XFD_REG_ECCCON_SECIE BIT(1) +#define MCP251XFD_REG_ECCCON_ECCEN BIT(0) + +#define MCP251XFD_REG_ECCSTAT 0xe10 +#define MCP251XFD_REG_ECCSTAT_ERRADDR_MASK GENMASK(27, 16) +#define MCP251XFD_REG_ECCSTAT_IF_MASK GENMASK(2, 1) +#define MCP251XFD_REG_ECCSTAT_DEDIF BIT(2) +#define MCP251XFD_REG_ECCSTAT_SECIF BIT(1) + +#define MCP251XFD_REG_DEVID 0xe14 /* MCP2518FD only */ +#define MCP251XFD_REG_DEVID_ID_MASK GENMASK(7, 4) +#define MCP251XFD_REG_DEVID_REV_MASK GENMASK(3, 0) + +struct mcp251xfd_hw_tef_obj { + u32 id; + u32 flags; + u32 ts; +}; + +struct mcp251xfd_hw_tx_obj_can { + u32 id; + u32 flags; + u8 data[sizeof_field(struct can_frame, data)]; +}; + +struct mcp251xfd_hw_tx_obj_canfd { + u32 id; + u32 flags; + u8 data[sizeof_field(struct canfd_frame, data)]; +}; + +struct mcp251xfd_hw_rx_obj_can { + u32 id; + u32 flags; + u32 ts; + u8 data[sizeof_field(struct can_frame, data)]; +}; + +struct mcp251xfd_hw_rx_obj_canfd { + u32 id; + u32 flags; + u32 ts; + u8 data[sizeof_field(struct canfd_frame, data)]; +}; + +#endif