Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Marc Kleine-Budde | 3043 | 99.67% | 11 | 84.62% |
Thomas Kopp | 10 | 0.33% | 2 | 15.38% |
Total | 3053 | 13 |
// SPDX-License-Identifier: GPL-2.0 // // mcp251xfd - Microchip MCP251xFD Family CAN controller driver // // Copyright (c) 2019, 2020, 2021 Pengutronix, // Marc Kleine-Budde <kernel@pengutronix.de> // #include "mcp251xfd.h" #include <asm/unaligned.h> static const struct regmap_config mcp251xfd_regmap_crc; static int mcp251xfd_regmap_nocrc_write(void *context, const void *data, size_t count) { struct spi_device *spi = context; return spi_write(spi, data, count); } static int 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); struct mcp251xfd_map_buf_nocrc *buf_tx = priv->map_buf_nocrc_tx; struct spi_transfer xfer[] = { { .tx_buf = buf_tx, .len = sizeof(buf_tx->cmd) + val_len, }, }; BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16)); if (IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY) && reg_len != sizeof(buf_tx->cmd.cmd)) return -EINVAL; memcpy(&buf_tx->cmd, reg, sizeof(buf_tx->cmd)); memcpy(buf_tx->data, val, val_len); return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer)); } static inline bool mcp251xfd_update_bits_read_reg(const struct mcp251xfd_priv *priv, unsigned int reg) { struct mcp251xfd_rx_ring *ring; int n; switch (reg) { case MCP251XFD_REG_INT: case MCP251XFD_REG_TEFCON: case MCP251XFD_REG_FLTCON(0): case MCP251XFD_REG_ECCSTAT: case MCP251XFD_REG_CRC: return false; case MCP251XFD_REG_CON: case MCP251XFD_REG_OSC: case MCP251XFD_REG_ECCCON: return true; default: mcp251xfd_for_each_rx_ring(priv, ring, n) { if (reg == MCP251XFD_REG_FIFOCON(ring->fifo_nr)) return false; if (reg == MCP251XFD_REG_FIFOSTA(ring->fifo_nr)) return true; } WARN(1, "Status of reg 0x%04x unknown.\n", reg); } return true; } static int mcp251xfd_regmap_nocrc_update_bits(void *context, unsigned int reg, unsigned int mask, unsigned int val) { struct spi_device *spi = context; struct mcp251xfd_priv *priv = spi_get_drvdata(spi); struct mcp251xfd_map_buf_nocrc *buf_rx = priv->map_buf_nocrc_rx; struct mcp251xfd_map_buf_nocrc *buf_tx = priv->map_buf_nocrc_tx; __le32 orig_le32 = 0, mask_le32, val_le32, tmp_le32; u8 first_byte, last_byte, len; int err; BUILD_BUG_ON(sizeof(buf_rx->cmd) != sizeof(__be16)); BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16)); if (IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY) && mask == 0) return -EINVAL; first_byte = mcp251xfd_first_byte_set(mask); last_byte = mcp251xfd_last_byte_set(mask); len = last_byte - first_byte + 1; if (mcp251xfd_update_bits_read_reg(priv, reg)) { struct spi_transfer xfer[2] = { }; struct spi_message msg; spi_message_init(&msg); spi_message_add_tail(&xfer[0], &msg); if (priv->devtype_data.quirks & MCP251XFD_QUIRK_HALF_DUPLEX) { xfer[0].tx_buf = buf_tx; xfer[0].len = sizeof(buf_tx->cmd); xfer[1].rx_buf = buf_rx->data; xfer[1].len = len; spi_message_add_tail(&xfer[1], &msg); } else { xfer[0].tx_buf = buf_tx; xfer[0].rx_buf = buf_rx; xfer[0].len = sizeof(buf_tx->cmd) + len; if (MCP251XFD_SANITIZE_SPI) memset(buf_tx->data, 0x0, len); } mcp251xfd_spi_cmd_read_nocrc(&buf_tx->cmd, reg + first_byte); err = spi_sync(spi, &msg); if (err) return err; memcpy(&orig_le32, buf_rx->data, len); } mask_le32 = cpu_to_le32(mask >> BITS_PER_BYTE * first_byte); val_le32 = cpu_to_le32(val >> BITS_PER_BYTE * first_byte); tmp_le32 = orig_le32 & ~mask_le32; tmp_le32 |= val_le32 & mask_le32; mcp251xfd_spi_cmd_write_nocrc(&buf_tx->cmd, reg + first_byte); memcpy(buf_tx->data, &tmp_le32, len); return spi_write(spi, buf_tx, sizeof(buf_tx->cmd) + len); } static int mcp251xfd_regmap_nocrc_read(void *context, const void *reg, size_t reg_len, void *val_buf, size_t val_len) { struct spi_device *spi = context; struct mcp251xfd_priv *priv = spi_get_drvdata(spi); struct mcp251xfd_map_buf_nocrc *buf_rx = priv->map_buf_nocrc_rx; struct mcp251xfd_map_buf_nocrc *buf_tx = priv->map_buf_nocrc_tx; struct spi_transfer xfer[2] = { }; struct spi_message msg; int err; BUILD_BUG_ON(sizeof(buf_rx->cmd) != sizeof(__be16)); BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16)); if (IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY) && reg_len != sizeof(buf_tx->cmd.cmd)) return -EINVAL; spi_message_init(&msg); spi_message_add_tail(&xfer[0], &msg); if (priv->devtype_data.quirks & MCP251XFD_QUIRK_HALF_DUPLEX) { xfer[0].tx_buf = reg; xfer[0].len = sizeof(buf_tx->cmd); xfer[1].rx_buf = val_buf; xfer[1].len = val_len; spi_message_add_tail(&xfer[1], &msg); } else { xfer[0].tx_buf = buf_tx; xfer[0].rx_buf = buf_rx; xfer[0].len = sizeof(buf_tx->cmd) + val_len; memcpy(&buf_tx->cmd, reg, sizeof(buf_tx->cmd)); if (MCP251XFD_SANITIZE_SPI) memset(buf_tx->data, 0x0, val_len); } err = spi_sync(spi, &msg); if (err) return err; if (!(priv->devtype_data.quirks & MCP251XFD_QUIRK_HALF_DUPLEX)) memcpy(val_buf, buf_rx->data, val_len); return 0; } static int 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); struct mcp251xfd_map_buf_crc *buf_tx = priv->map_buf_crc_tx; struct spi_transfer xfer[] = { { .tx_buf = buf_tx, .len = sizeof(buf_tx->cmd) + val_len + sizeof(buf_tx->crc), }, }; u16 reg = *(u16 *)reg_p; u16 crc; BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16) + sizeof(u8)); if (IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY) && reg_len != sizeof(buf_tx->cmd.cmd) + mcp251xfd_regmap_crc.pad_bits / BITS_PER_BYTE) return -EINVAL; mcp251xfd_spi_cmd_write_crc(&buf_tx->cmd, reg, val_len); memcpy(buf_tx->data, val, val_len); crc = mcp251xfd_crc16_compute(buf_tx, sizeof(buf_tx->cmd) + val_len); put_unaligned_be16(crc, buf_tx->data + val_len); return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer)); } static int mcp251xfd_regmap_crc_write(void *context, const void *data, size_t count) { const size_t data_offset = sizeof(__be16) + mcp251xfd_regmap_crc.pad_bits / BITS_PER_BYTE; return mcp251xfd_regmap_crc_gather_write(context, data, data_offset, data + data_offset, count - data_offset); } static int mcp251xfd_regmap_crc_read_check_crc(const struct mcp251xfd_map_buf_crc * const buf_rx, const struct mcp251xfd_map_buf_crc * const buf_tx, unsigned int data_len) { u16 crc_received, crc_calculated; crc_received = get_unaligned_be16(buf_rx->data + data_len); crc_calculated = mcp251xfd_crc16_compute2(&buf_tx->cmd, sizeof(buf_tx->cmd), buf_rx->data, data_len); if (crc_received != crc_calculated) return -EBADMSG; return 0; } static int mcp251xfd_regmap_crc_read_one(struct mcp251xfd_priv *priv, struct spi_message *msg, unsigned int data_len) { const struct mcp251xfd_map_buf_crc *buf_rx = priv->map_buf_crc_rx; const struct mcp251xfd_map_buf_crc *buf_tx = priv->map_buf_crc_tx; int err; BUILD_BUG_ON(sizeof(buf_rx->cmd) != sizeof(__be16) + sizeof(u8)); BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16) + sizeof(u8)); err = spi_sync(priv->spi, msg); if (err) return err; return mcp251xfd_regmap_crc_read_check_crc(buf_rx, buf_tx, data_len); } static int mcp251xfd_regmap_crc_read(void *context, const void *reg_p, size_t reg_len, void *val_buf, size_t val_len) { struct spi_device *spi = context; struct mcp251xfd_priv *priv = spi_get_drvdata(spi); struct mcp251xfd_map_buf_crc *buf_rx = priv->map_buf_crc_rx; struct mcp251xfd_map_buf_crc *buf_tx = priv->map_buf_crc_tx; struct spi_transfer xfer[2] = { }; struct spi_message msg; u16 reg = *(u16 *)reg_p; int i, err; BUILD_BUG_ON(sizeof(buf_rx->cmd) != sizeof(__be16) + sizeof(u8)); BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16) + sizeof(u8)); if (IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY) && reg_len != sizeof(buf_tx->cmd.cmd) + mcp251xfd_regmap_crc.pad_bits / BITS_PER_BYTE) return -EINVAL; spi_message_init(&msg); spi_message_add_tail(&xfer[0], &msg); if (priv->devtype_data.quirks & MCP251XFD_QUIRK_HALF_DUPLEX) { xfer[0].tx_buf = buf_tx; xfer[0].len = sizeof(buf_tx->cmd); xfer[1].rx_buf = buf_rx->data; xfer[1].len = val_len + sizeof(buf_tx->crc); spi_message_add_tail(&xfer[1], &msg); } else { xfer[0].tx_buf = buf_tx; xfer[0].rx_buf = buf_rx; xfer[0].len = sizeof(buf_tx->cmd) + val_len + sizeof(buf_tx->crc); if (MCP251XFD_SANITIZE_SPI) memset(buf_tx->data, 0x0, val_len + sizeof(buf_tx->crc)); } mcp251xfd_spi_cmd_read_crc(&buf_tx->cmd, reg, val_len); for (i = 0; i < MCP251XFD_READ_CRC_RETRIES_MAX; i++) { err = mcp251xfd_regmap_crc_read_one(priv, &msg, val_len); if (!err) goto out; if (err != -EBADMSG) return err; /* MCP251XFD_REG_TBC is the time base counter * register. It increments once per SYS clock tick, * which is 20 or 40 MHz. * * Observation on the mcp2518fd shows that if the * lowest byte (which is transferred first on the SPI * bus) of that register is 0x00 or 0x80 the * calculated CRC doesn't always match the transferred * one. On the mcp2517fd this problem is not limited * to the first byte being 0x00 or 0x80. * * If the highest bit in the lowest byte is flipped * the transferred CRC matches the calculated one. We * assume for now the CRC operates on the correct * data. */ if (reg == MCP251XFD_REG_TBC && ((buf_rx->data[0] & 0xf8) == 0x0 || (buf_rx->data[0] & 0xf8) == 0x80)) { /* Flip highest bit in lowest byte of le32 */ buf_rx->data[0] ^= 0x80; /* re-check CRC */ err = mcp251xfd_regmap_crc_read_check_crc(buf_rx, buf_tx, val_len); if (!err) { /* If CRC is now correct, assume * flipped data is OK. */ goto out; } } /* MCP251XFD_REG_OSC is the first ever reg we read from. * * The chip may be in deep sleep and this SPI transfer * (i.e. the assertion of the CS) will wake the chip * up. This takes about 3ms. The CRC of this transfer * is wrong. * * Or there isn't a chip at all, in this case the CRC * will be wrong, too. * * In both cases ignore the CRC and copy the read data * to the caller. It will take care of both cases. * */ if (reg == MCP251XFD_REG_OSC && val_len == sizeof(__le32)) { err = 0; goto out; } netdev_info(priv->ndev, "CRC read error at address 0x%04x (length=%zd, data=%*ph, CRC=0x%04x) retrying.\n", reg, val_len, (int)val_len, buf_rx->data, get_unaligned_be16(buf_rx->data + val_len)); } if (err) { netdev_err(priv->ndev, "CRC read error at address 0x%04x (length=%zd, data=%*ph, CRC=0x%04x).\n", reg, val_len, (int)val_len, buf_rx->data, get_unaligned_be16(buf_rx->data + val_len)); return err; } out: memcpy(val_buf, buf_rx->data, val_len); return 0; } static const struct regmap_range mcp251xfd_reg_table_yes_range[] = { regmap_reg_range(0x000, 0x2ec), /* CAN FD Controller Module SFR */ regmap_reg_range(0x400, 0xbfc), /* RAM */ regmap_reg_range(0xe00, 0xe14), /* MCP2517/18FD SFR */ }; static const struct regmap_access_table mcp251xfd_reg_table = { .yes_ranges = mcp251xfd_reg_table_yes_range, .n_yes_ranges = ARRAY_SIZE(mcp251xfd_reg_table_yes_range), }; static const struct regmap_config mcp251xfd_regmap_nocrc = { .name = "nocrc", .reg_bits = 16, .reg_stride = 4, .pad_bits = 0, .val_bits = 32, .max_register = 0xffc, .wr_table = &mcp251xfd_reg_table, .rd_table = &mcp251xfd_reg_table, .cache_type = REGCACHE_NONE, .read_flag_mask = (__force unsigned long) cpu_to_be16(MCP251XFD_SPI_INSTRUCTION_READ), .write_flag_mask = (__force unsigned long) cpu_to_be16(MCP251XFD_SPI_INSTRUCTION_WRITE), }; static const struct regmap_bus mcp251xfd_bus_nocrc = { .write = mcp251xfd_regmap_nocrc_write, .gather_write = mcp251xfd_regmap_nocrc_gather_write, .reg_update_bits = mcp251xfd_regmap_nocrc_update_bits, .read = mcp251xfd_regmap_nocrc_read, .reg_format_endian_default = REGMAP_ENDIAN_BIG, .val_format_endian_default = REGMAP_ENDIAN_LITTLE, .max_raw_read = sizeof_field(struct mcp251xfd_map_buf_nocrc, data), .max_raw_write = sizeof_field(struct mcp251xfd_map_buf_nocrc, data), }; static const struct regmap_config mcp251xfd_regmap_crc = { .name = "crc", .reg_bits = 16, .reg_stride = 4, .pad_bits = 16, /* keep data bits aligned */ .val_bits = 32, .max_register = 0xffc, .wr_table = &mcp251xfd_reg_table, .rd_table = &mcp251xfd_reg_table, .cache_type = REGCACHE_NONE, }; static const struct regmap_bus mcp251xfd_bus_crc = { .write = mcp251xfd_regmap_crc_write, .gather_write = mcp251xfd_regmap_crc_gather_write, .read = mcp251xfd_regmap_crc_read, .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, .val_format_endian_default = REGMAP_ENDIAN_LITTLE, .max_raw_read = sizeof_field(struct mcp251xfd_map_buf_crc, data), .max_raw_write = sizeof_field(struct mcp251xfd_map_buf_crc, data), }; static inline bool mcp251xfd_regmap_use_nocrc(struct mcp251xfd_priv *priv) { return (!(priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_REG)) || (!(priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_RX)); } static inline bool mcp251xfd_regmap_use_crc(struct mcp251xfd_priv *priv) { return (priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_REG) || (priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_RX); } static int mcp251xfd_regmap_init_nocrc(struct mcp251xfd_priv *priv) { if (!priv->map_nocrc) { struct regmap *map; map = devm_regmap_init(&priv->spi->dev, &mcp251xfd_bus_nocrc, priv->spi, &mcp251xfd_regmap_nocrc); if (IS_ERR(map)) return PTR_ERR(map); priv->map_nocrc = map; } if (!priv->map_buf_nocrc_rx) { priv->map_buf_nocrc_rx = devm_kzalloc(&priv->spi->dev, sizeof(*priv->map_buf_nocrc_rx), GFP_KERNEL); if (!priv->map_buf_nocrc_rx) return -ENOMEM; } if (!priv->map_buf_nocrc_tx) { priv->map_buf_nocrc_tx = devm_kzalloc(&priv->spi->dev, sizeof(*priv->map_buf_nocrc_tx), GFP_KERNEL); if (!priv->map_buf_nocrc_tx) return -ENOMEM; } if (!(priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_REG)) priv->map_reg = priv->map_nocrc; if (!(priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_RX)) priv->map_rx = priv->map_nocrc; return 0; } static void mcp251xfd_regmap_destroy_nocrc(struct mcp251xfd_priv *priv) { if (priv->map_buf_nocrc_rx) { devm_kfree(&priv->spi->dev, priv->map_buf_nocrc_rx); priv->map_buf_nocrc_rx = NULL; } if (priv->map_buf_nocrc_tx) { devm_kfree(&priv->spi->dev, priv->map_buf_nocrc_tx); priv->map_buf_nocrc_tx = NULL; } } static int mcp251xfd_regmap_init_crc(struct mcp251xfd_priv *priv) { if (!priv->map_crc) { struct regmap *map; map = devm_regmap_init(&priv->spi->dev, &mcp251xfd_bus_crc, priv->spi, &mcp251xfd_regmap_crc); if (IS_ERR(map)) return PTR_ERR(map); priv->map_crc = map; } if (!priv->map_buf_crc_rx) { priv->map_buf_crc_rx = devm_kzalloc(&priv->spi->dev, sizeof(*priv->map_buf_crc_rx), GFP_KERNEL); if (!priv->map_buf_crc_rx) return -ENOMEM; } if (!priv->map_buf_crc_tx) { priv->map_buf_crc_tx = devm_kzalloc(&priv->spi->dev, sizeof(*priv->map_buf_crc_tx), GFP_KERNEL); if (!priv->map_buf_crc_tx) return -ENOMEM; } if (priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_REG) priv->map_reg = priv->map_crc; if (priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_RX) priv->map_rx = priv->map_crc; return 0; } static void mcp251xfd_regmap_destroy_crc(struct mcp251xfd_priv *priv) { if (priv->map_buf_crc_rx) { devm_kfree(&priv->spi->dev, priv->map_buf_crc_rx); priv->map_buf_crc_rx = NULL; } if (priv->map_buf_crc_tx) { devm_kfree(&priv->spi->dev, priv->map_buf_crc_tx); priv->map_buf_crc_tx = NULL; } } int mcp251xfd_regmap_init(struct mcp251xfd_priv *priv) { int err; if (mcp251xfd_regmap_use_nocrc(priv)) { err = mcp251xfd_regmap_init_nocrc(priv); if (err) return err; } else { mcp251xfd_regmap_destroy_nocrc(priv); } if (mcp251xfd_regmap_use_crc(priv)) { err = mcp251xfd_regmap_init_crc(priv); if (err) return err; } else { mcp251xfd_regmap_destroy_crc(priv); } return 0; }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1