diff mbox series

[net-next,15/19] can: tcan4x5x: rework SPI access

Message ID 20210107094900.173046-16-mkl@pengutronix.de (mailing list archive)
State Accepted
Delegated to: Netdev Maintainers
Headers show
Series [net-next,01/19] can: tcan4x5x: replace DEVICE_NAME by KBUILD_MODNAME | expand

Checks

Context Check Description
netdev/cover_letter success Pull request
netdev/fixes_present success Link
netdev/patch_count warning Series longer than 15 patches
netdev/tree_selection success Clearly marked for net-next
netdev/subject_prefix success Link
netdev/cc_maintainers warning 1 maintainers not CCed: wg@grandegger.com
netdev/source_inline success Was 0 now: 0
netdev/verify_signedoff success Link
netdev/module_param success Was 0 now: 0
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/verify_fixes success Link
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 174 lines checked
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/header_inline success Link
netdev/stable success Stable not CCed

Commit Message

Marc Kleine-Budde Jan. 7, 2021, 9:48 a.m. UTC
This patch reworks the SPI access and fixes several probems:
- tcan4x5x_regmap_gather_write(), tcan4x5x_regmap_read():
  Do not place variable "addr" on stack and use it as buffer for SPI
  transfer. Buffers for SPI transfers must be allocated from DMA save
  memory.
- tcan4x5x_regmap_gather_write(), tcan4x5x_regmap_read():
  Halfe number of SPI transfers by using a single buffer + memcpy().
  This improves the performance, especially on SPI controllers, which
  use interrupt based transfers.
- Use "8" bits per word, not "32". This makes it possible to use this
  driver on SoCs like the Raspberry Pi, which SPI host controller
  drivers only support 8 bits per word.

Note: this breaks half duplex only controllers. Support for them will be
re-added in the next patch.

Reviewed-by: Dan Murphy <dmurphy@ti.com>
Tested-by: Sean Nyekjaer <sean@geanix.com>
Link: https://lore.kernel.org/r/20201215231746.1132907-16-mkl@pengutronix.de
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
---
 drivers/net/can/m_can/tcan4x5x-core.c   |  2 +-
 drivers/net/can/m_can/tcan4x5x-regmap.c | 87 +++++++++++++++++--------
 drivers/net/can/m_can/tcan4x5x.h        | 23 +++++++
 3 files changed, 84 insertions(+), 28 deletions(-)

Comments

Jakub Kicinski Jan. 7, 2021, 7 p.m. UTC | #1
On Thu,  7 Jan 2021 10:48:56 +0100 Marc Kleine-Budde wrote:
> +struct __packed tcan4x5x_map_buf {
> +	struct tcan4x5x_buf_cmd cmd;
> +	u8 data[256 * sizeof(u32)];
> +} ____cacheline_aligned;

Interesting attribute combo, I must say.
Jakub Kicinski Jan. 7, 2021, 7:06 p.m. UTC | #2
On Thu, 7 Jan 2021 11:00:35 -0800 Jakub Kicinski wrote:
> On Thu,  7 Jan 2021 10:48:56 +0100 Marc Kleine-Budde wrote:
> > +struct __packed tcan4x5x_map_buf {
> > +	struct tcan4x5x_buf_cmd cmd;
> > +	u8 data[256 * sizeof(u32)];
> > +} ____cacheline_aligned;  
> 
> Interesting attribute combo, I must say.

Looking at the rest of the patch I don't really see a reason for
__packed.  Perhaps it can be dropped?
Marc Kleine-Budde Jan. 7, 2021, 9:17 p.m. UTC | #3
On 1/7/21 8:06 PM, Jakub Kicinski wrote:
> On Thu, 7 Jan 2021 11:00:35 -0800 Jakub Kicinski wrote:
>> On Thu,  7 Jan 2021 10:48:56 +0100 Marc Kleine-Budde wrote:
>>> +struct __packed tcan4x5x_map_buf {
>>> +	struct tcan4x5x_buf_cmd cmd;
>>> +	u8 data[256 * sizeof(u32)];
>>> +} ____cacheline_aligned;  
>>
>> Interesting attribute combo, I must say.
> 
> Looking at the rest of the patch I don't really see a reason for
> __packed.  Perhaps it can be dropped?

It's the stream of bytes send via SPI to the chip. Here are both structs for
reference:

> +struct __packed tcan4x5x_buf_cmd { 
> +	u8 cmd; 
> +	__be16 addr; 
> +	u8 len; 
> +}; 

This has to be packed, as I assume the compiler would add some space after the
"u8 cmd" to align the __be16 naturally.

> +struct __packed tcan4x5x_map_buf { 
> +	struct tcan4x5x_buf_cmd cmd; 
> +	u8 data[256 * sizeof(u32)]; 
> +} ____cacheline_aligned; 

Due to the packing of the struct tcan4x5x_buf_cmd it should have a length of 4
bytes. Without __packed, will the "u8 data" come directly after the cmd?

Marc
Marc Kleine-Budde Jan. 7, 2021, 9:19 p.m. UTC | #4
On 1/7/21 8:00 PM, Jakub Kicinski wrote:
> On Thu,  7 Jan 2021 10:48:56 +0100 Marc Kleine-Budde wrote:
>> +struct __packed tcan4x5x_map_buf {
>> +	struct tcan4x5x_buf_cmd cmd;
>> +	u8 data[256 * sizeof(u32)];
>> +} ____cacheline_aligned;
> 
> Interesting attribute combo, I must say.

__packed as it's the byte stream send to the chip via SPI.

____cacheline_aligned, as it might be subject to DMA mapping in the SPI host
driver. An alternative would be to allocate these with separate kmalloc().

regards,
Marc
Jakub Kicinski Jan. 7, 2021, 10:38 p.m. UTC | #5
On Thu, 7 Jan 2021 22:17:15 +0100 Marc Kleine-Budde wrote:
> > +struct __packed tcan4x5x_buf_cmd { 
> > +	u8 cmd; 
> > +	__be16 addr; 
> > +	u8 len; 
> > +};   
> 
> This has to be packed, as I assume the compiler would add some space after the
> "u8 cmd" to align the __be16 naturally.

Ack, packing this one makes sense.

> > +struct __packed tcan4x5x_map_buf { 
> > +	struct tcan4x5x_buf_cmd cmd; 
> > +	u8 data[256 * sizeof(u32)]; 
> > +} ____cacheline_aligned;   
> 
> Due to the packing of the struct tcan4x5x_buf_cmd it should have a length of 4
> bytes. Without __packed, will the "u8 data" come directly after the cmd?

Yup, u8 with no alignment attribute will follow the previous
field with no holes.
Ahmad Fatoum Jan. 8, 2021, 10:07 a.m. UTC | #6
On 07.01.21 23:38, Jakub Kicinski wrote:
> On Thu, 7 Jan 2021 22:17:15 +0100 Marc Kleine-Budde wrote:
>>> +struct __packed tcan4x5x_buf_cmd { 
>>> +	u8 cmd; 
>>> +	__be16 addr; 
>>> +	u8 len; 
>>> +};   
>>
>> This has to be packed, as I assume the compiler would add some space after the
>> "u8 cmd" to align the __be16 naturally.
> 
> Ack, packing this one makes sense.
> 
>>> +struct __packed tcan4x5x_map_buf { 
>>> +	struct tcan4x5x_buf_cmd cmd; 
>>> +	u8 data[256 * sizeof(u32)]; 
>>> +} ____cacheline_aligned;   
>>
>> Due to the packing of the struct tcan4x5x_buf_cmd it should have a length of 4
>> bytes. Without __packed, will the "u8 data" come directly after the cmd?
> 
> Yup, u8 with no alignment attribute will follow the previous
> field with no holes.

__packed has a documentation benefit though. It documents that the author
considers the current layout to be the only correct one. (and thus extra
care should be taken when modifying it).

> 
>
Jakub Kicinski Jan. 8, 2021, 4:32 p.m. UTC | #7
On Fri, 8 Jan 2021 11:07:26 +0100 Ahmad Fatoum wrote:
> >>> +struct __packed tcan4x5x_map_buf { 
> >>> +	struct tcan4x5x_buf_cmd cmd; 
> >>> +	u8 data[256 * sizeof(u32)]; 
> >>> +} ____cacheline_aligned;     
> >>
> >> Due to the packing of the struct tcan4x5x_buf_cmd it should have a length of 4
> >> bytes. Without __packed, will the "u8 data" come directly after the cmd?  
> > 
> > Yup, u8 with no alignment attribute will follow the previous
> > field with no holes.  
> 
> __packed has a documentation benefit though. It documents that the author
> considers the current layout to be the only correct one. (and thus extra
> care should be taken when modifying it).

____cacheline_aligned adds a big architecture dependent padding at the
end of this struct, so the size of this structure is architecture
dependent. Besides using packed forced the compiler to use byte by byte
loads on architectures without unaligned access, so __packed is not
free.
David Laight Jan. 8, 2021, 9:53 p.m. UTC | #8
From: Marc Kleine-Budde
> Sent: 07 January 2021 21:17
> 
> On 1/7/21 8:06 PM, Jakub Kicinski wrote:
> > On Thu, 7 Jan 2021 11:00:35 -0800 Jakub Kicinski wrote:
> >> On Thu,  7 Jan 2021 10:48:56 +0100 Marc Kleine-Budde wrote:
> >>> +struct __packed tcan4x5x_map_buf {
> >>> +	struct tcan4x5x_buf_cmd cmd;
> >>> +	u8 data[256 * sizeof(u32)];
> >>> +} ____cacheline_aligned;
> >>
> >> Interesting attribute combo, I must say.
> >
> > Looking at the rest of the patch I don't really see a reason for
> > __packed.  Perhaps it can be dropped?
> 
> It's the stream of bytes send via SPI to the chip. Here are both structs for
> reference:
> 
> > +struct __packed tcan4x5x_buf_cmd {
> > +	u8 cmd;
> > +	__be16 addr;
> > +	u8 len;
> > +};
> 
> This has to be packed, as I assume the compiler would add some space after the
> "u8 cmd" to align the __be16 naturally.

Why not generate a series of 32bit words to be sent over the SPI bus.
Slightly less faffing in the code.
Then have a #define (or inline function) to merge the cmd+addr+len
into a single 32bit word.

Also if the length is in 32bit units, then the data[] field
ought to be u32[].

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
Ahmad Fatoum Jan. 11, 2021, 3:35 p.m. UTC | #9
Hello Jakub,

On 08.01.21 17:32, Jakub Kicinski wrote:
> On Fri, 8 Jan 2021 11:07:26 +0100 Ahmad Fatoum wrote:
>>>>> +struct __packed tcan4x5x_map_buf { 
>>>>> +	struct tcan4x5x_buf_cmd cmd; 
>>>>> +	u8 data[256 * sizeof(u32)]; 
>>>>> +} ____cacheline_aligned;     
>>>>
>>>> Due to the packing of the struct tcan4x5x_buf_cmd it should have a length of 4
>>>> bytes. Without __packed, will the "u8 data" come directly after the cmd?  
>>>
>>> Yup, u8 with no alignment attribute will follow the previous
>>> field with no holes.  
>>
>> __packed has a documentation benefit though. It documents that the author
>> considers the current layout to be the only correct one. (and thus extra
>> care should be taken when modifying it).
> 
> ____cacheline_aligned adds a big architecture dependent padding at the
> end of this struct, so the size of this structure is architecture
> dependent. Besides using packed forced the compiler to use byte by byte
> loads on architectures without unaligned access, so __packed is not
> free.

https://godbolt.org/z/j68x8n

seems to indicate that explicit alignment "overrules" packed's implicit
alignment of 1 as
 there isn't any byte-by-byte access generated for a struct
that is both packed and cacheline aligned. packed only structs are accessed
byte-by-byte however.

Did I get something wrong in my testcase?

I compiled with ARM gcc 8.2  -mno-unaligned-access -fno-strict-aliasing -O2

Cheers,
Ahmad
Jakub Kicinski Jan. 11, 2021, 6:03 p.m. UTC | #10
On Mon, 11 Jan 2021 16:35:30 +0100 Ahmad Fatoum wrote:
> Hello Jakub,
> 
> On 08.01.21 17:32, Jakub Kicinski wrote:
> > On Fri, 8 Jan 2021 11:07:26 +0100 Ahmad Fatoum wrote:  
> >>>>> +struct __packed tcan4x5x_map_buf { 
> >>>>> +	struct tcan4x5x_buf_cmd cmd; 
> >>>>> +	u8 data[256 * sizeof(u32)]; 
> >>>>> +} ____cacheline_aligned;       
> >>>>
> >>>> Due to the packing of the struct tcan4x5x_buf_cmd it should have a length of 4
> >>>> bytes. Without __packed, will the "u8 data" come directly after the cmd?    
> >>>
> >>> Yup, u8 with no alignment attribute will follow the previous
> >>> field with no holes.    
> >>
> >> __packed has a documentation benefit though. It documents that the author
> >> considers the current layout to be the only correct one. (and thus extra
> >> care should be taken when modifying it).  
> > 
> > ____cacheline_aligned adds a big architecture dependent padding at the
> > end of this struct, so the size of this structure is architecture
> > dependent. Besides using packed forced the compiler to use byte by byte
> > loads on architectures without unaligned access, so __packed is not
> > free.  
> 
> https://godbolt.org/z/j68x8n
> 
> seems to indicate that explicit alignment "overrules" packed's implicit
> alignment of 1 as
>  there isn't any byte-by-byte access generated for a struct
> that is both packed and cacheline aligned. packed only structs are accessed
> byte-by-byte however.
> 
> Did I get something wrong in my testcase?
> 
> I compiled with ARM gcc 8.2  -mno-unaligned-access -fno-strict-aliasing -O2

I see, that's why I said combining ____cacheline_aligned with __packed
looks very confusing. Good to know which one takes precedence.
That doesn't change my recommendation to remove __packed, though, let's
not leave readers of this code scratching their heads.
Marc Kleine-Budde Jan. 12, 2021, 3:25 p.m. UTC | #11
On 1/7/21 11:38 PM, Jakub Kicinski wrote:
> On Thu, 7 Jan 2021 22:17:15 +0100 Marc Kleine-Budde wrote:
>>> +struct __packed tcan4x5x_buf_cmd { 
>>> +	u8 cmd; 
>>> +	__be16 addr; 
>>> +	u8 len; 
>>> +};   
>>
>> This has to be packed, as I assume the compiler would add some space after the
>> "u8 cmd" to align the __be16 naturally.
> 
> Ack, packing this one makes sense.
> 
>>> +struct __packed tcan4x5x_map_buf { 
>>> +	struct tcan4x5x_buf_cmd cmd; 
>>> +	u8 data[256 * sizeof(u32)]; 
>>> +} ____cacheline_aligned;   
>>
>> Due to the packing of the struct tcan4x5x_buf_cmd it should have a length of 4
>> bytes. Without __packed, will the "u8 data" come directly after the cmd?
> 
> Yup, u8 with no alignment attribute will follow the previous
> field with no holes.

Ack, without __packed, there's no diff in the objdump on arm.

Marc
Marc Kleine-Budde Jan. 12, 2021, 3:36 p.m. UTC | #12
On 1/8/21 10:53 PM, David Laight wrote:
>> On 1/7/21 8:06 PM, Jakub Kicinski wrote:
>>> On Thu, 7 Jan 2021 11:00:35 -0800 Jakub Kicinski wrote:
>>>> On Thu,  7 Jan 2021 10:48:56 +0100 Marc Kleine-Budde wrote:
>>>>> +struct __packed tcan4x5x_map_buf {
>>>>> +	struct tcan4x5x_buf_cmd cmd;
>>>>> +	u8 data[256 * sizeof(u32)];
>>>>> +} ____cacheline_aligned;
>>>>
>>>> Interesting attribute combo, I must say.
>>>
>>> Looking at the rest of the patch I don't really see a reason for
>>> __packed.  Perhaps it can be dropped?
>>
>> It's the stream of bytes send via SPI to the chip. Here are both structs for
>> reference:
>>
>>> +struct __packed tcan4x5x_buf_cmd {
>>> +	u8 cmd;
>>> +	__be16 addr;
>>> +	u8 len;
>>> +};
>>
>> This has to be packed, as I assume the compiler would add some space after the
>> "u8 cmd" to align the __be16 naturally.
> 
> Why not generate a series of 32bit words to be sent over the SPI bus.
> Slightly less faffing in the code.
> Then have a #define (or inline function) to merge the cmd+addr+len
> into a single 32bit word.

The driver uses regmap. With proper configuration regmap already formats the
"cmd" and the "addr" in correct byte order into the first 3 bytes of the "reg_buf".

As regmap doesn't support generating a length parameter, I use the above struct
tcan4x5x_buf_cmd to set the length.

> Also if the length is in 32bit units, then the data[] field
> ought to be u32[].

In the regmap callback the data is passed with a void pointer and a length (in
bytes), so copying this to a "u8 data[]" felt more natural to me.

regards,
Marc
diff mbox series

Patch

diff --git a/drivers/net/can/m_can/tcan4x5x-core.c b/drivers/net/can/m_can/tcan4x5x-core.c
index 739b8f89a335..d37843a74663 100644
--- a/drivers/net/can/m_can/tcan4x5x-core.c
+++ b/drivers/net/can/m_can/tcan4x5x-core.c
@@ -380,7 +380,7 @@  static int tcan4x5x_can_probe(struct spi_device *spi)
 	spi_set_drvdata(spi, priv);
 
 	/* Configure the SPI bus */
-	spi->bits_per_word = 32;
+	spi->bits_per_word = 8;
 	ret = spi_setup(spi);
 	if (ret)
 		goto out_m_can_class_free_dev;
diff --git a/drivers/net/can/m_can/tcan4x5x-regmap.c b/drivers/net/can/m_can/tcan4x5x-regmap.c
index 5ea162578619..660e9d87dffb 100644
--- a/drivers/net/can/m_can/tcan4x5x-regmap.c
+++ b/drivers/net/can/m_can/tcan4x5x-regmap.c
@@ -9,48 +9,76 @@ 
 
 #include "tcan4x5x.h"
 
-#define TCAN4X5X_WRITE_CMD (0x61 << 24)
-#define TCAN4X5X_READ_CMD (0x41 << 24)
+#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)
+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 spi_message m;
-	u32 addr;
-	struct spi_transfer t[2] = {
-		{ .tx_buf = &addr, .len = reg_len, .cs_change = 0,},
-		{ .tx_buf = val, .len = val_len, },
+	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,
+		},
 	};
 
-	addr = TCAN4X5X_WRITE_CMD | (*((u16 *)reg) << 8) | val_len >> 2;
+	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);
 
-	spi_message_init(&m);
-	spi_message_add_tail(&t[0], &m);
-	spi_message_add_tail(&t[1], &m);
-
-	return spi_sync(spi, &m);
+	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(u32),
-					    data + sizeof(u32),
-					    count - sizeof(u32));
+	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, size_t reg_size,
-				void *val, size_t val_size)
+				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[] = {
+		{
+			.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);
+
+	xfer[0].rx_buf = buf_rx;
+	xfer[0].len = sizeof(buf_tx->cmd) + val_len;
 
-	u32 addr = TCAN4X5X_READ_CMD | (*((u16 *)reg) << 8) | val_size >> 2;
+	if (TCAN4X5X_SANITIZE_SPI)
+		memset(buf_tx->data, 0x0, val_len);
 
-	return spi_write_then_read(spi, &addr, reg_size, (u32 *)val, val_size);
+	err = spi_sync(spi, &msg);
+	if (err)
+		return err;
+
+	memcpy(val_buf, buf_rx->data, val_len);
+
+	return 0;
 }
 
 static const struct regmap_range tcan4x5x_reg_table_yes_range[] = {
@@ -66,21 +94,26 @@  static const struct regmap_access_table tcan4x5x_reg_table = {
 };
 
 static const struct regmap_config tcan4x5x_regmap = {
-	.reg_bits = 32,
+	.reg_bits = 24,
 	.reg_stride = 4,
+	.pad_bits = 8,
 	.val_bits = 32,
 	.wr_table = &tcan4x5x_reg_table,
 	.rd_table = &tcan4x5x_reg_table,
-	.cache_type = REGCACHE_NONE,
 	.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_NATIVE,
-	.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
+	.reg_format_endian_default = REGMAP_ENDIAN_BIG,
+	.val_format_endian_default = REGMAP_ENDIAN_BIG,
 	.max_raw_read = 256,
 	.max_raw_write = 256,
 };
diff --git a/drivers/net/can/m_can/tcan4x5x.h b/drivers/net/can/m_can/tcan4x5x.h
index e5bdd91b8005..7bf264f8e81f 100644
--- a/drivers/net/can/m_can/tcan4x5x.h
+++ b/drivers/net/can/m_can/tcan4x5x.h
@@ -17,6 +17,19 @@ 
 
 #include "m_can.h"
 
+#define TCAN4X5X_SANITIZE_SPI 1
+
+struct __packed tcan4x5x_buf_cmd {
+	u8 cmd;
+	__be16 addr;
+	u8 len;
+};
+
+struct __packed tcan4x5x_map_buf {
+	struct tcan4x5x_buf_cmd cmd;
+	u8 data[256 * sizeof(u32)];
+} ____cacheline_aligned;
+
 struct tcan4x5x_priv {
 	struct m_can_classdev cdev;
 
@@ -27,8 +40,18 @@  struct tcan4x5x_priv {
 	struct gpio_desc *device_wake_gpio;
 	struct gpio_desc *device_state_gpio;
 	struct regulator *power;
+
+	struct tcan4x5x_map_buf map_buf_rx;
+	struct tcan4x5x_map_buf map_buf_tx;
 };
 
+static inline void
+tcan4x5x_spi_cmd_set_len(struct tcan4x5x_buf_cmd *cmd, u8 len)
+{
+	/* number of u32 */
+	cmd->len = len >> 2;
+}
+
 int tcan4x5x_regmap_init(struct tcan4x5x_priv *priv);
 
 #endif