aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/can/m_can/tcan4x5x-regmap.c
blob: ca80dbaf7a3f5415050fd143125a00ee4aa35397 (plain) (blame)
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 0x8ffc

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);
}