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 <gregor.herburger@ew.tq-group.com>
Tested-by: Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com>
Signed-off-by: Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com>
Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
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 <mkl@pengutronix.de>
This commit is contained in:
Gregor Herburger
2025-10-01 14:40:03 +05:30
committed by Marc Kleine-Budde
parent f5982a679a
commit c902835fc6
2 changed files with 84 additions and 6 deletions

View File

@@ -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, 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, reg_len, val, len);
if (ret)
return ret;
/* Write from 0xe07 on */
reg += len + 1;
return _mcp251xfd_regmap_crc_gather_write(context, &reg, 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)

View File

@@ -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)