diff --git a/Documentation/devicetree/bindings/net/can/tcan4x5x.txt b/Documentation/devicetree/bindings/net/can/tcan4x5x.txt index e3501bfa22e90..170e23f0610db 100644 --- a/Documentation/devicetree/bindings/net/can/tcan4x5x.txt +++ b/Documentation/devicetree/bindings/net/can/tcan4x5x.txt @@ -4,7 +4,10 @@ Texas Instruments TCAN4x5x CAN Controller This file provides device node information for the TCAN4x5x interface contains. Required properties: - - compatible: "ti,tcan4x5x" + - compatible: + "ti,tcan4552", "ti,tcan4x5x" + "ti,tcan4553", "ti,tcan4x5x" or + "ti,tcan4x5x" - reg: 0 - #address-cells: 1 - #size-cells: 0 @@ -21,8 +24,10 @@ Optional properties: - reset-gpios: Hardwired output GPIO. If not defined then software reset. - device-state-gpios: Input GPIO that indicates if the device is in - a sleep state or if the device is active. - - device-wake-gpios: Wake up GPIO to wake up the TCAN device. + a sleep state or if the device is active. Not + available with tcan4552/4553. + - device-wake-gpios: Wake up GPIO to wake up the TCAN device. Not + available with tcan4552/4553. Example: tcan4x5x: tcan4x5x@0 { diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index d204703521d28..16ecc11c7f62a 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -1913,6 +1913,22 @@ static int register_m_can_dev(struct net_device *dev) return register_candev(dev); } +int m_can_check_mram_cfg(struct m_can_classdev *cdev, u32 mram_max_size) +{ + u32 total_size; + + total_size = cdev->mcfg[MRAM_TXB].off - cdev->mcfg[MRAM_SIDF].off + + cdev->mcfg[MRAM_TXB].num * TXB_ELEMENT_SIZE; + if (total_size > mram_max_size) { + dev_err(cdev->dev, "Total size of mram config(%u) exceeds mram(%u)\n", + total_size, mram_max_size); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(m_can_check_mram_cfg); + static void m_can_of_parse_mram(struct m_can_classdev *cdev, const u32 *mram_config_vals) { diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h index c543928c756fc..520e14277dff5 100644 --- a/drivers/net/can/m_can/m_can.h +++ b/drivers/net/can/m_can/m_can.h @@ -103,6 +103,7 @@ int m_can_class_register(struct m_can_classdev *cdev); void m_can_class_unregister(struct m_can_classdev *cdev); int m_can_class_get_clocks(struct m_can_classdev *cdev); int m_can_init_ram(struct m_can_classdev *priv); +int m_can_check_mram_cfg(struct m_can_classdev *cdev, u32 mram_max_size); int m_can_class_suspend(struct device *dev); int m_can_class_resume(struct device *dev); diff --git a/drivers/net/can/m_can/tcan4x5x-core.c b/drivers/net/can/m_can/tcan4x5x-core.c index 2342aa011647c..8a4143809d337 100644 --- a/drivers/net/can/m_can/tcan4x5x-core.c +++ b/drivers/net/can/m_can/tcan4x5x-core.c @@ -6,8 +6,9 @@ #define TCAN4X5X_EXT_CLK_DEF 40000000 -#define TCAN4X5X_DEV_ID0 0x00 -#define TCAN4X5X_DEV_ID1 0x04 +#define TCAN4X5X_DEV_ID1 0x00 +#define TCAN4X5X_DEV_ID1_TCAN 0x4e414354 /* ASCII TCAN */ +#define TCAN4X5X_DEV_ID2 0x04 #define TCAN4X5X_REV 0x08 #define TCAN4X5X_STATUS 0x0C #define TCAN4X5X_ERROR_STATUS_MASK 0x10 @@ -80,6 +81,7 @@ TCAN4X5X_MCAN_IR_RF1F) #define TCAN4X5X_MRAM_START 0x8000 +#define TCAN4X5X_MRAM_SIZE 0x800 #define TCAN4X5X_MCAN_OFFSET 0x1000 #define TCAN4X5X_CLEAR_ALL_INT 0xffffffff @@ -102,6 +104,37 @@ #define TCAN4X5X_WD_3_S_TIMER BIT(29) #define TCAN4X5X_WD_6_S_TIMER (BIT(28) | BIT(29)) +struct tcan4x5x_version_info { + const char *name; + u32 id2_register; + + bool has_wake_pin; + bool has_state_pin; +}; + +enum { + TCAN4552 = 0, + TCAN4553, + TCAN4X5X, +}; + +static const struct tcan4x5x_version_info tcan4x5x_versions[] = { + [TCAN4552] = { + .name = "4552", + .id2_register = 0x32353534, + }, + [TCAN4553] = { + .name = "4553", + .id2_register = 0x32353534, + }, + /* generic version with no id2_register at the end */ + [TCAN4X5X] = { + .name = "generic", + .has_wake_pin = true, + .has_state_pin = true, + }, +}; + static inline struct tcan4x5x_priv *cdev_to_priv(struct m_can_classdev *cdev) { return container_of(cdev, struct tcan4x5x_priv, cdev); @@ -253,18 +286,53 @@ static int tcan4x5x_disable_state(struct m_can_classdev *cdev) TCAN4X5X_DISABLE_INH_MSK, 0x01); } -static int tcan4x5x_get_gpios(struct m_can_classdev *cdev) +static const struct tcan4x5x_version_info +*tcan4x5x_find_version(struct tcan4x5x_priv *priv) +{ + u32 val; + int ret; + + ret = regmap_read(priv->regmap, TCAN4X5X_DEV_ID1, &val); + if (ret) + return ERR_PTR(ret); + + if (val != TCAN4X5X_DEV_ID1_TCAN) { + dev_err(&priv->spi->dev, "Not a tcan device %x\n", val); + return ERR_PTR(-ENODEV); + } + + ret = regmap_read(priv->regmap, TCAN4X5X_DEV_ID2, &val); + if (ret) + return ERR_PTR(ret); + + for (int i = 0; i != ARRAY_SIZE(tcan4x5x_versions); ++i) { + const struct tcan4x5x_version_info *vinfo = &tcan4x5x_versions[i]; + + if (!vinfo->id2_register || val == vinfo->id2_register) { + dev_info(&priv->spi->dev, "Detected TCAN device version %s\n", + vinfo->name); + return vinfo; + } + } + + return &tcan4x5x_versions[TCAN4X5X]; +} + +static int tcan4x5x_get_gpios(struct m_can_classdev *cdev, + const struct tcan4x5x_version_info *version_info) { struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev); int ret; - tcan4x5x->device_wake_gpio = devm_gpiod_get(cdev->dev, "device-wake", - GPIOD_OUT_HIGH); - if (IS_ERR(tcan4x5x->device_wake_gpio)) { - if (PTR_ERR(tcan4x5x->device_wake_gpio) == -EPROBE_DEFER) - return -EPROBE_DEFER; + if (version_info->has_wake_pin) { + tcan4x5x->device_wake_gpio = devm_gpiod_get(cdev->dev, "device-wake", + GPIOD_OUT_HIGH); + if (IS_ERR(tcan4x5x->device_wake_gpio)) { + if (PTR_ERR(tcan4x5x->device_wake_gpio) == -EPROBE_DEFER) + return -EPROBE_DEFER; - tcan4x5x_disable_wake(cdev); + tcan4x5x_disable_wake(cdev); + } } tcan4x5x->reset_gpio = devm_gpiod_get_optional(cdev->dev, "reset", @@ -276,12 +344,14 @@ static int tcan4x5x_get_gpios(struct m_can_classdev *cdev) if (ret) return ret; - tcan4x5x->device_state_gpio = devm_gpiod_get_optional(cdev->dev, - "device-state", - GPIOD_IN); - if (IS_ERR(tcan4x5x->device_state_gpio)) { - tcan4x5x->device_state_gpio = NULL; - tcan4x5x_disable_state(cdev); + if (version_info->has_state_pin) { + tcan4x5x->device_state_gpio = devm_gpiod_get_optional(cdev->dev, + "device-state", + GPIOD_IN); + if (IS_ERR(tcan4x5x->device_state_gpio)) { + tcan4x5x->device_state_gpio = NULL; + tcan4x5x_disable_state(cdev); + } } return 0; @@ -298,6 +368,7 @@ static struct m_can_ops tcan4x5x_ops = { static int tcan4x5x_can_probe(struct spi_device *spi) { + const struct tcan4x5x_version_info *version_info; struct tcan4x5x_priv *priv; struct m_can_classdev *mcan_class; int freq, ret; @@ -307,6 +378,10 @@ static int tcan4x5x_can_probe(struct spi_device *spi) if (!mcan_class) return -ENOMEM; + ret = m_can_check_mram_cfg(mcan_class, TCAN4X5X_MRAM_SIZE); + if (ret) + goto out_m_can_class_free_dev; + priv = cdev_to_priv(mcan_class); priv->power = devm_regulator_get_optional(&spi->dev, "vsup"); @@ -327,6 +402,8 @@ static int tcan4x5x_can_probe(struct spi_device *spi) /* Sanity check */ if (freq < 20000000 || freq > TCAN4X5X_EXT_CLK_DEF) { + dev_err(&spi->dev, "Clock frequency is out of supported range %d\n", + freq); ret = -ERANGE; goto out_m_can_class_free_dev; } @@ -345,28 +422,49 @@ static int tcan4x5x_can_probe(struct spi_device *spi) /* Configure the SPI bus */ spi->bits_per_word = 8; ret = spi_setup(spi); - if (ret) + if (ret) { + dev_err(&spi->dev, "SPI setup failed %pe\n", ERR_PTR(ret)); goto out_m_can_class_free_dev; + } ret = tcan4x5x_regmap_init(priv); - if (ret) + if (ret) { + dev_err(&spi->dev, "regmap init failed %pe\n", ERR_PTR(ret)); goto out_m_can_class_free_dev; + } ret = tcan4x5x_power_enable(priv->power, 1); - if (ret) + if (ret) { + dev_err(&spi->dev, "Enabling regulator failed %pe\n", + ERR_PTR(ret)); goto out_m_can_class_free_dev; + } - ret = tcan4x5x_get_gpios(mcan_class); - if (ret) + version_info = tcan4x5x_find_version(priv); + if (IS_ERR(version_info)) { + ret = PTR_ERR(version_info); + goto out_power; + } + + ret = tcan4x5x_get_gpios(mcan_class, version_info); + if (ret) { + dev_err(&spi->dev, "Getting gpios failed %pe\n", ERR_PTR(ret)); goto out_power; + } ret = tcan4x5x_init(mcan_class); - if (ret) + if (ret) { + dev_err(&spi->dev, "tcan initialization failed %pe\n", + ERR_PTR(ret)); goto out_power; + } ret = m_can_class_register(mcan_class); - if (ret) + if (ret) { + dev_err(&spi->dev, "Failed registering m_can device %pe\n", + ERR_PTR(ret)); goto out_power; + } netdev_info(mcan_class->net, "TCAN4X5X successfully initialized.\n"); return 0; diff --git a/drivers/net/can/m_can/tcan4x5x-regmap.c b/drivers/net/can/m_can/tcan4x5x-regmap.c index 2b218ce04e9f2..fafa6daa67e69 100644 --- a/drivers/net/can/m_can/tcan4x5x-regmap.c +++ b/drivers/net/can/m_can/tcan4x5x-regmap.c @@ -95,7 +95,6 @@ static const struct regmap_range tcan4x5x_reg_table_wr_range[] = { regmap_reg_range(0x000c, 0x0010), /* Device configuration registers and Interrupt Flags*/ regmap_reg_range(0x0800, 0x080c), - regmap_reg_range(0x0814, 0x0814), regmap_reg_range(0x0820, 0x0820), regmap_reg_range(0x0830, 0x0830), /* M_CAN */