diff options
Diffstat (limited to 'drivers/net/can/m_can')
-rw-r--r-- | drivers/net/can/m_can/Makefile | 4 | ||||
-rw-r--r-- | drivers/net/can/m_can/m_can.c | 8 | ||||
-rw-r--r-- | drivers/net/can/m_can/tcan4x5x-core.c (renamed from drivers/net/can/m_can/tcan4x5x.c) | 122 | ||||
-rw-r--r-- | drivers/net/can/m_can/tcan4x5x-regmap.c | 135 | ||||
-rw-r--r-- | drivers/net/can/m_can/tcan4x5x.h | 57 |
5 files changed, 213 insertions, 113 deletions
diff --git a/drivers/net/can/m_can/Makefile b/drivers/net/can/m_can/Makefile index ef7963ff2006..d717bbc9e033 100644 --- a/drivers/net/can/m_can/Makefile +++ b/drivers/net/can/m_can/Makefile @@ -7,3 +7,7 @@ obj-$(CONFIG_CAN_M_CAN) += m_can.o obj-$(CONFIG_CAN_M_CAN_PCI) += m_can_pci.o obj-$(CONFIG_CAN_M_CAN_PLATFORM) += m_can_platform.o obj-$(CONFIG_CAN_M_CAN_TCAN4X5X) += tcan4x5x.o + +tcan4x5x-objs := +tcan4x5x-objs += tcan4x5x-core.o +tcan4x5x-objs += tcan4x5x-regmap.o diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index da551fd0f502..3752520a7d4b 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -930,7 +930,7 @@ static void m_can_echo_tx_event(struct net_device *dev) (fgi << TXEFA_EFAI_SHIFT))); /* update stats */ - stats->tx_bytes += can_get_echo_skb(dev, msg_mark); + stats->tx_bytes += can_get_echo_skb(dev, msg_mark, NULL); stats->tx_packets++; } } @@ -972,7 +972,7 @@ static irqreturn_t m_can_isr(int irq, void *dev_id) if (cdev->version == 30) { if (ir & IR_TC) { /* Transmission Complete Interrupt*/ - stats->tx_bytes += can_get_echo_skb(dev, 0); + stats->tx_bytes += can_get_echo_skb(dev, 0, NULL); stats->tx_packets++; can_led_event(dev, CAN_LED_EVENT_TX); netif_wake_queue(dev); @@ -1483,7 +1483,7 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) M_CAN_FIFO_DATA(i / 4), *(u32 *)(cf->data + i)); - can_put_echo_skb(skb, dev, 0); + can_put_echo_skb(skb, dev, 0, 0); if (cdev->can.ctrlmode & CAN_CTRLMODE_FD) { cccr = m_can_read(cdev, M_CAN_CCCR); @@ -1554,7 +1554,7 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev) /* Push loopback echo. * Will be looped back on TX interrupt based on message marker */ - can_put_echo_skb(skb, dev, putidx); + can_put_echo_skb(skb, dev, putidx, 0); /* Enable TX FIFO element to start transfer */ m_can_write(cdev, M_CAN_TXBAR, (1 << putidx)); diff --git a/drivers/net/can/m_can/tcan4x5x.c b/drivers/net/can/m_can/tcan4x5x-core.c index 970f0e9d19bf..b7caec769ddb 100644 --- a/drivers/net/can/m_can/tcan4x5x.c +++ b/drivers/net/can/m_can/tcan4x5x-core.c @@ -2,15 +2,8 @@ // SPI to CAN driver for the Texas Instruments TCAN4x5x // Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/ -#include <linux/regmap.h> -#include <linux/spi/spi.h> +#include "tcan4x5x.h" -#include <linux/regulator/consumer.h> -#include <linux/gpio/consumer.h> - -#include "m_can.h" - -#define DEVICE_NAME "tcan4x5x" #define TCAN4X5X_EXT_CLK_DEF 40000000 #define TCAN4X5X_DEV_ID0 0x00 @@ -88,14 +81,10 @@ #define TCAN4X5X_MRAM_START 0x8000 #define TCAN4X5X_MCAN_OFFSET 0x1000 -#define TCAN4X5X_MAX_REGISTER 0x8fff #define TCAN4X5X_CLEAR_ALL_INT 0xffffffff #define TCAN4X5X_SET_ALL_INT 0xffffffff -#define TCAN4X5X_WRITE_CMD (0x61 << 24) -#define TCAN4X5X_READ_CMD (0x41 << 24) - #define TCAN4X5X_MODE_SEL_MASK (BIT(7) | BIT(6)) #define TCAN4X5X_MODE_SLEEP 0x00 #define TCAN4X5X_MODE_STANDBY BIT(6) @@ -113,18 +102,6 @@ #define TCAN4X5X_WD_3_S_TIMER BIT(29) #define TCAN4X5X_WD_6_S_TIMER (BIT(28) | BIT(29)) -struct tcan4x5x_priv { - struct m_can_classdev cdev; - - struct regmap *regmap; - struct spi_device *spi; - - struct gpio_desc *reset_gpio; - struct gpio_desc *device_wake_gpio; - struct gpio_desc *device_state_gpio; - struct regulator *power; -}; - static inline struct tcan4x5x_priv *cdev_to_priv(struct m_can_classdev *cdev) { return container_of(cdev, struct tcan4x5x_priv, cdev); @@ -167,72 +144,6 @@ static int tcan4x5x_reset(struct tcan4x5x_priv *priv) return ret; } -static int regmap_spi_gather_write(void *context, const void *reg, - size_t reg_len, const void *val, - size_t val_len) -{ - struct device *dev = context; - struct spi_device *spi = to_spi_device(dev); - 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, }, - }; - - addr = TCAN4X5X_WRITE_CMD | (*((u16 *)reg) << 8) | val_len >> 2; - - spi_message_init(&m); - spi_message_add_tail(&t[0], &m); - spi_message_add_tail(&t[1], &m); - - return spi_sync(spi, &m); -} - -static int tcan4x5x_regmap_write(void *context, const void *data, size_t count) -{ - u16 *reg = (u16 *)(data); - const u32 *val = data + 4; - - return regmap_spi_gather_write(context, reg, 4, val, count - 4); -} - -static int regmap_spi_async_write(void *context, - const void *reg, size_t reg_len, - const void *val, size_t val_len, - struct regmap_async *a) -{ - return -ENOTSUPP; -} - -static struct regmap_async *regmap_spi_async_alloc(void) -{ - return NULL; -} - -static int tcan4x5x_regmap_read(void *context, - const void *reg, size_t reg_size, - void *val, size_t val_size) -{ - struct device *dev = context; - struct spi_device *spi = to_spi_device(dev); - - u32 addr = TCAN4X5X_READ_CMD | (*((u16 *)reg) << 8) | val_size >> 2; - - return spi_write_then_read(spi, &addr, reg_size, (u32 *)val, val_size); -} - -static struct regmap_bus tcan4x5x_bus = { - .write = tcan4x5x_regmap_write, - .gather_write = regmap_spi_gather_write, - .async_write = regmap_spi_async_write, - .async_alloc = regmap_spi_async_alloc, - .read = tcan4x5x_regmap_read, - .read_flag_mask = 0x00, - .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, - .val_format_endian_default = REGMAP_ENDIAN_NATIVE, -}; - static u32 tcan4x5x_read_reg(struct m_can_classdev *cdev, int reg) { struct tcan4x5x_priv *priv = cdev_to_priv(cdev); @@ -387,13 +298,6 @@ static int tcan4x5x_get_gpios(struct m_can_classdev *cdev) return 0; } -static const struct regmap_config tcan4x5x_regmap = { - .reg_bits = 32, - .val_bits = 32, - .cache_type = REGCACHE_NONE, - .max_register = TCAN4X5X_MAX_REGISTER, -}; - static struct m_can_ops tcan4x5x_ops = { .init = tcan4x5x_init, .read_reg = tcan4x5x_read_reg, @@ -450,17 +354,14 @@ 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; - priv->regmap = devm_regmap_init(&spi->dev, &tcan4x5x_bus, - &spi->dev, &tcan4x5x_regmap); - if (IS_ERR(priv->regmap)) { - ret = PTR_ERR(priv->regmap); + ret = tcan4x5x_regmap_init(priv); + if (ret) goto out_m_can_class_free_dev; - } ret = tcan4x5x_power_enable(priv->power, 1); if (ret) @@ -502,23 +403,26 @@ static int tcan4x5x_can_remove(struct spi_device *spi) } static const struct of_device_id tcan4x5x_of_match[] = { - { .compatible = "ti,tcan4x5x", }, - { } + { + .compatible = "ti,tcan4x5x", + }, { + /* sentinel */ + }, }; MODULE_DEVICE_TABLE(of, tcan4x5x_of_match); static const struct spi_device_id tcan4x5x_id_table[] = { { - .name = "tcan4x5x", - .driver_data = 0, + .name = "tcan4x5x", + }, { + /* sentinel */ }, - { } }; MODULE_DEVICE_TABLE(spi, tcan4x5x_id_table); static struct spi_driver tcan4x5x_can_driver = { .driver = { - .name = DEVICE_NAME, + .name = KBUILD_MODNAME, .of_match_table = tcan4x5x_of_match, .pm = NULL, }, diff --git a/drivers/net/can/m_can/tcan4x5x-regmap.c b/drivers/net/can/m_can/tcan4x5x-regmap.c new file mode 100644 index 000000000000..ca80dbaf7a3f --- /dev/null +++ b/drivers/net/can/m_can/tcan4x5x-regmap.c @@ -0,0 +1,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); +} diff --git a/drivers/net/can/m_can/tcan4x5x.h b/drivers/net/can/m_can/tcan4x5x.h new file mode 100644 index 000000000000..c66da829b795 --- /dev/null +++ b/drivers/net/can/m_can/tcan4x5x.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * tcan4x5x - Texas Instruments TCAN4x5x Family CAN controller driver + * + * Copyright (c) 2020 Pengutronix, + * Marc Kleine-Budde <kernel@pengutronix.de> + */ + +#ifndef _TCAN4X5X_H +#define _TCAN4X5X_H + +#include <linux/gpio/consumer.h> +#include <linux/regmap.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> + +#include "m_can.h" + +#define TCAN4X5X_SANITIZE_SPI 1 + +struct __packed tcan4x5x_buf_cmd { + u8 cmd; + __be16 addr; + u8 len; +}; + +struct tcan4x5x_map_buf { + struct tcan4x5x_buf_cmd cmd; + u8 data[256 * sizeof(u32)]; +} ____cacheline_aligned; + +struct tcan4x5x_priv { + struct m_can_classdev cdev; + + struct regmap *regmap; + struct spi_device *spi; + + struct gpio_desc *reset_gpio; + 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 |