From: Gregor Herburger Date: Wed, 1 Oct 2025 09:10:03 +0000 (+0530) Subject: can: mcp251xfd: add workaround for errata 5 X-Git-Tag: v6.19-rc1~170^2~177^2~1^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c902835fc6eb7e13dbffc404210eed59293425c8;p=thirdparty%2Fkernel%2Flinux.git can: mcp251xfd: add workaround for errata 5 According to Errata DS80000789E 5 writing IOCON register using one SPI write command clears LAT0/LAT1. Errata Fix/Work Around suggests to write registers with single byte write instructions. However, it seems that every write to the second byte causes the overwrite of LAT0/LAT1. Never write byte 2 of IOCON register to avoid clearing of LAT0/LAT1. Signed-off-by: Gregor Herburger Tested-by: Viken Dadhaniya Signed-off-by: Viken Dadhaniya Reviewed-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20251001091006.4003841-4-viken.dadhaniya@oss.qualcomm.com [mkl: add missing MCP251XFD_REG_IOCON_GPIO_MASK] Signed-off-by: Marc Kleine-Budde --- diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c index e61cbd209955b..70d5ff0ae7acb 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c @@ -13,9 +13,9 @@ static const struct regmap_config mcp251xfd_regmap_crc; static int -mcp251xfd_regmap_nocrc_gather_write(void *context, - const void *reg, size_t reg_len, - const void *val, size_t val_len) +_mcp251xfd_regmap_nocrc_gather_write(void *context, + const void *reg, size_t reg_len, + const void *val, size_t val_len) { struct spi_device *spi = context; struct mcp251xfd_priv *priv = spi_get_drvdata(spi); @@ -39,6 +39,45 @@ mcp251xfd_regmap_nocrc_gather_write(void *context, return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer)); } +static int +mcp251xfd_regmap_nocrc_gather_write(void *context, + const void *reg_p, size_t reg_len, + const void *val, size_t val_len) +{ + const u16 byte_exclude = MCP251XFD_REG_IOCON + + mcp251xfd_first_byte_set(MCP251XFD_REG_IOCON_GPIO_MASK); + u16 reg = be16_to_cpu(*(__be16 *)reg_p) & MCP251XFD_SPI_ADDRESS_MASK; + int ret; + + /* Never write to bits 16..23 of IOCON register to avoid clearing of LAT0/LAT1 + * + * According to MCP2518FD Errata DS80000789E 5 writing IOCON register using one + * SPI write command clears LAT0/LAT1. + * + * Errata Fix/Work Around suggests to write registers with single byte + * write instructions. However, it seems that the byte at 0xe06(IOCON[23:16]) + * is for read-only access and writing to it causes the clearing of LAT0/LAT1. + */ + if (reg <= byte_exclude && reg + val_len > byte_exclude) { + size_t len = byte_exclude - reg; + + /* Write up to 0xe05 */ + ret = _mcp251xfd_regmap_nocrc_gather_write(context, reg_p, reg_len, val, len); + if (ret) + return ret; + + /* Write from 0xe07 on */ + reg += len + 1; + reg = (__force unsigned short)cpu_to_be16(MCP251XFD_SPI_INSTRUCTION_WRITE | reg); + return _mcp251xfd_regmap_nocrc_gather_write(context, ®, reg_len, + val + len + 1, + val_len - len - 1); + } + + return _mcp251xfd_regmap_nocrc_gather_write(context, reg_p, reg_len, + val, val_len); +} + static int mcp251xfd_regmap_nocrc_write(void *context, const void *data, size_t count) { @@ -197,9 +236,9 @@ mcp251xfd_regmap_nocrc_read(void *context, } static int -mcp251xfd_regmap_crc_gather_write(void *context, - const void *reg_p, size_t reg_len, - const void *val, size_t val_len) +_mcp251xfd_regmap_crc_gather_write(void *context, + const void *reg_p, size_t reg_len, + const void *val, size_t val_len) { struct spi_device *spi = context; struct mcp251xfd_priv *priv = spi_get_drvdata(spi); @@ -230,6 +269,44 @@ mcp251xfd_regmap_crc_gather_write(void *context, return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer)); } +static int +mcp251xfd_regmap_crc_gather_write(void *context, + const void *reg_p, size_t reg_len, + const void *val, size_t val_len) +{ + const u16 byte_exclude = MCP251XFD_REG_IOCON + + mcp251xfd_first_byte_set(MCP251XFD_REG_IOCON_GPIO_MASK); + u16 reg = *(u16 *)reg_p; + int ret; + + /* Never write to bits 16..23 of IOCON register to avoid clearing of LAT0/LAT1 + * + * According to MCP2518FD Errata DS80000789E 5 writing IOCON register using one + * SPI write command clears LAT0/LAT1. + * + * Errata Fix/Work Around suggests to write registers with single byte + * write instructions. However, it seems that the byte at 0xe06(IOCON[23:16]) + * is for read-only access and writing to it causes the clearing of LAT0/LAT1. + */ + if (reg <= byte_exclude && reg + val_len > byte_exclude) { + size_t len = byte_exclude - reg; + + /* Write up to 0xe05 */ + ret = _mcp251xfd_regmap_crc_gather_write(context, ®, reg_len, val, len); + if (ret) + return ret; + + /* Write from 0xe07 on */ + reg += len + 1; + return _mcp251xfd_regmap_crc_gather_write(context, ®, reg_len, + val + len + 1, + val_len - len - 1); + } + + return _mcp251xfd_regmap_crc_gather_write(context, reg_p, reg_len, + val, val_len); +} + static int mcp251xfd_regmap_crc_write(void *context, const void *data, size_t count) diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h index dcbbd2b2fae82..e63034fd59471 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h @@ -337,6 +337,7 @@ #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_GPIO_MASK GENMASK(17, 16) #define MCP251XFD_REG_IOCON_LAT1 BIT(9) #define MCP251XFD_REG_IOCON_LAT0 BIT(8) #define MCP251XFD_REG_IOCON_XSTBYEN BIT(6)