1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
// SPDX-License-Identifier: GPL-2.0
//
// tcan4x5x - Texas Instruments TCAN4x5x Family CAN controller driver
//
// Copyright (c) 2020 Pengutronix,
// Marc Kleine-Budde <kernel@pengutronix.de>
// Copyright (c) 2018-2019 Texas Instruments Incorporated
// http://www.ti.com/
#include "tcan4x5x.h"
#define TCAN4X5X_SPI_INSTRUCTION_WRITE (0x61 << 24)
#define TCAN4X5X_SPI_INSTRUCTION_READ (0x41 << 24)
#define TCAN4X5X_MAX_REGISTER 0x87fc
static int tcan4x5x_regmap_gather_write(void *context,
const void *reg, size_t reg_len,
const void *val, size_t val_len)
{
struct spi_device *spi = context;
struct tcan4x5x_priv *priv = spi_get_drvdata(spi);
struct tcan4x5x_map_buf *buf_tx = &priv->map_buf_tx;
struct spi_transfer xfer[] = {
{
.tx_buf = buf_tx,
.len = sizeof(buf_tx->cmd) + val_len,
},
};
memcpy(&buf_tx->cmd, reg, sizeof(buf_tx->cmd.cmd) +
sizeof(buf_tx->cmd.addr));
tcan4x5x_spi_cmd_set_len(&buf_tx->cmd, val_len);
memcpy(buf_tx->data, val, val_len);
return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
}
static int tcan4x5x_regmap_write(void *context, const void *data, size_t count)
{
return tcan4x5x_regmap_gather_write(context, data, sizeof(__be32),
data + sizeof(__be32),
count - sizeof(__be32));
}
static int tcan4x5x_regmap_read(void *context,
const void *reg_buf, size_t reg_len,
void *val_buf, size_t val_len)
{
struct spi_device *spi = context;
struct tcan4x5x_priv *priv = spi_get_drvdata(spi);
struct tcan4x5x_map_buf *buf_rx = &priv->map_buf_rx;
struct tcan4x5x_map_buf *buf_tx = &priv->map_buf_tx;
struct spi_transfer xfer[2] = {
{
.tx_buf = buf_tx,
}
};
struct spi_message msg;
int err;
spi_message_init(&msg);
spi_message_add_tail(&xfer[0], &msg);
memcpy(&buf_tx->cmd, reg_buf, sizeof(buf_tx->cmd.cmd) +
sizeof(buf_tx->cmd.addr));
tcan4x5x_spi_cmd_set_len(&buf_tx->cmd, val_len);
if (spi->controller->flags & SPI_CONTROLLER_HALF_DUPLEX) {
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].rx_buf = buf_rx;
xfer[0].len = sizeof(buf_tx->cmd) + val_len;
if (TCAN4X5X_SANITIZE_SPI)
memset(buf_tx->data, 0x0, val_len);
}
err = spi_sync(spi, &msg);
if (err)
return err;
if (!(spi->controller->flags & SPI_CONTROLLER_HALF_DUPLEX))
memcpy(val_buf, buf_rx->data, val_len);
return 0;
}
static const struct regmap_range tcan4x5x_reg_table_yes_range[] = {
regmap_reg_range(0x0000, 0x002c), /* Device ID and SPI Registers */
regmap_reg_range(0x0800, 0x083c), /* Device configuration registers and Interrupt Flags*/
regmap_reg_range(0x1000, 0x10fc), /* M_CAN */
regmap_reg_range(0x8000, 0x87fc), /* MRAM */
};
static const struct regmap_access_table tcan4x5x_reg_table = {
.yes_ranges = tcan4x5x_reg_table_yes_range,
.n_yes_ranges = ARRAY_SIZE(tcan4x5x_reg_table_yes_range),
};
static const struct regmap_config tcan4x5x_regmap = {
.reg_bits = 24,
.reg_stride = 4,
.pad_bits = 8,
.val_bits = 32,
.wr_table = &tcan4x5x_reg_table,
.rd_table = &tcan4x5x_reg_table,
.max_register = TCAN4X5X_MAX_REGISTER,
.cache_type = REGCACHE_NONE,
.read_flag_mask = (__force unsigned long)
cpu_to_be32(TCAN4X5X_SPI_INSTRUCTION_READ),
.write_flag_mask = (__force unsigned long)
cpu_to_be32(TCAN4X5X_SPI_INSTRUCTION_WRITE),
};
static const struct regmap_bus tcan4x5x_bus = {
.write = tcan4x5x_regmap_write,
.gather_write = tcan4x5x_regmap_gather_write,
.read = tcan4x5x_regmap_read,
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
.val_format_endian_default = REGMAP_ENDIAN_BIG,
.max_raw_read = 256,
.max_raw_write = 256,
};
int tcan4x5x_regmap_init(struct tcan4x5x_priv *priv)
{
priv->regmap = devm_regmap_init(&priv->spi->dev, &tcan4x5x_bus,
priv->spi, &tcan4x5x_regmap);
return PTR_ERR_OR_ZERO(priv->regmap);
}
|