cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

tcan4x5x-core.c (11104B)


      1// SPDX-License-Identifier: GPL-2.0
      2// SPI to CAN driver for the Texas Instruments TCAN4x5x
      3// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
      4
      5#include "tcan4x5x.h"
      6
      7#define TCAN4X5X_EXT_CLK_DEF 40000000
      8
      9#define TCAN4X5X_DEV_ID0 0x00
     10#define TCAN4X5X_DEV_ID1 0x04
     11#define TCAN4X5X_REV 0x08
     12#define TCAN4X5X_STATUS 0x0C
     13#define TCAN4X5X_ERROR_STATUS 0x10
     14#define TCAN4X5X_CONTROL 0x14
     15
     16#define TCAN4X5X_CONFIG 0x800
     17#define TCAN4X5X_TS_PRESCALE 0x804
     18#define TCAN4X5X_TEST_REG 0x808
     19#define TCAN4X5X_INT_FLAGS 0x820
     20#define TCAN4X5X_MCAN_INT_REG 0x824
     21#define TCAN4X5X_INT_EN 0x830
     22
     23/* Interrupt bits */
     24#define TCAN4X5X_CANBUSTERMOPEN_INT_EN BIT(30)
     25#define TCAN4X5X_CANHCANL_INT_EN BIT(29)
     26#define TCAN4X5X_CANHBAT_INT_EN BIT(28)
     27#define TCAN4X5X_CANLGND_INT_EN BIT(27)
     28#define TCAN4X5X_CANBUSOPEN_INT_EN BIT(26)
     29#define TCAN4X5X_CANBUSGND_INT_EN BIT(25)
     30#define TCAN4X5X_CANBUSBAT_INT_EN BIT(24)
     31#define TCAN4X5X_UVSUP_INT_EN BIT(22)
     32#define TCAN4X5X_UVIO_INT_EN BIT(21)
     33#define TCAN4X5X_TSD_INT_EN BIT(19)
     34#define TCAN4X5X_ECCERR_INT_EN BIT(16)
     35#define TCAN4X5X_CANINT_INT_EN BIT(15)
     36#define TCAN4X5X_LWU_INT_EN BIT(14)
     37#define TCAN4X5X_CANSLNT_INT_EN BIT(10)
     38#define TCAN4X5X_CANDOM_INT_EN BIT(8)
     39#define TCAN4X5X_CANBUS_ERR_INT_EN BIT(5)
     40#define TCAN4X5X_BUS_FAULT BIT(4)
     41#define TCAN4X5X_MCAN_INT BIT(1)
     42#define TCAN4X5X_ENABLE_TCAN_INT \
     43	(TCAN4X5X_MCAN_INT | TCAN4X5X_BUS_FAULT | \
     44	 TCAN4X5X_CANBUS_ERR_INT_EN | TCAN4X5X_CANINT_INT_EN)
     45
     46/* MCAN Interrupt bits */
     47#define TCAN4X5X_MCAN_IR_ARA BIT(29)
     48#define TCAN4X5X_MCAN_IR_PED BIT(28)
     49#define TCAN4X5X_MCAN_IR_PEA BIT(27)
     50#define TCAN4X5X_MCAN_IR_WD BIT(26)
     51#define TCAN4X5X_MCAN_IR_BO BIT(25)
     52#define TCAN4X5X_MCAN_IR_EW BIT(24)
     53#define TCAN4X5X_MCAN_IR_EP BIT(23)
     54#define TCAN4X5X_MCAN_IR_ELO BIT(22)
     55#define TCAN4X5X_MCAN_IR_BEU BIT(21)
     56#define TCAN4X5X_MCAN_IR_BEC BIT(20)
     57#define TCAN4X5X_MCAN_IR_DRX BIT(19)
     58#define TCAN4X5X_MCAN_IR_TOO BIT(18)
     59#define TCAN4X5X_MCAN_IR_MRAF BIT(17)
     60#define TCAN4X5X_MCAN_IR_TSW BIT(16)
     61#define TCAN4X5X_MCAN_IR_TEFL BIT(15)
     62#define TCAN4X5X_MCAN_IR_TEFF BIT(14)
     63#define TCAN4X5X_MCAN_IR_TEFW BIT(13)
     64#define TCAN4X5X_MCAN_IR_TEFN BIT(12)
     65#define TCAN4X5X_MCAN_IR_TFE BIT(11)
     66#define TCAN4X5X_MCAN_IR_TCF BIT(10)
     67#define TCAN4X5X_MCAN_IR_TC BIT(9)
     68#define TCAN4X5X_MCAN_IR_HPM BIT(8)
     69#define TCAN4X5X_MCAN_IR_RF1L BIT(7)
     70#define TCAN4X5X_MCAN_IR_RF1F BIT(6)
     71#define TCAN4X5X_MCAN_IR_RF1W BIT(5)
     72#define TCAN4X5X_MCAN_IR_RF1N BIT(4)
     73#define TCAN4X5X_MCAN_IR_RF0L BIT(3)
     74#define TCAN4X5X_MCAN_IR_RF0F BIT(2)
     75#define TCAN4X5X_MCAN_IR_RF0W BIT(1)
     76#define TCAN4X5X_MCAN_IR_RF0N BIT(0)
     77#define TCAN4X5X_ENABLE_MCAN_INT \
     78	(TCAN4X5X_MCAN_IR_TC | TCAN4X5X_MCAN_IR_RF0N | \
     79	 TCAN4X5X_MCAN_IR_RF1N | TCAN4X5X_MCAN_IR_RF0F | \
     80	 TCAN4X5X_MCAN_IR_RF1F)
     81
     82#define TCAN4X5X_MRAM_START 0x8000
     83#define TCAN4X5X_MCAN_OFFSET 0x1000
     84
     85#define TCAN4X5X_CLEAR_ALL_INT 0xffffffff
     86#define TCAN4X5X_SET_ALL_INT 0xffffffff
     87
     88#define TCAN4X5X_MODE_SEL_MASK (BIT(7) | BIT(6))
     89#define TCAN4X5X_MODE_SLEEP 0x00
     90#define TCAN4X5X_MODE_STANDBY BIT(6)
     91#define TCAN4X5X_MODE_NORMAL BIT(7)
     92
     93#define TCAN4X5X_DISABLE_WAKE_MSK	(BIT(31) | BIT(30))
     94#define TCAN4X5X_DISABLE_INH_MSK	BIT(9)
     95
     96#define TCAN4X5X_SW_RESET BIT(2)
     97
     98#define TCAN4X5X_MCAN_CONFIGURED BIT(5)
     99#define TCAN4X5X_WATCHDOG_EN BIT(3)
    100#define TCAN4X5X_WD_60_MS_TIMER 0
    101#define TCAN4X5X_WD_600_MS_TIMER BIT(28)
    102#define TCAN4X5X_WD_3_S_TIMER BIT(29)
    103#define TCAN4X5X_WD_6_S_TIMER (BIT(28) | BIT(29))
    104
    105static inline struct tcan4x5x_priv *cdev_to_priv(struct m_can_classdev *cdev)
    106{
    107	return container_of(cdev, struct tcan4x5x_priv, cdev);
    108}
    109
    110static void tcan4x5x_check_wake(struct tcan4x5x_priv *priv)
    111{
    112	int wake_state = 0;
    113
    114	if (priv->device_state_gpio)
    115		wake_state = gpiod_get_value(priv->device_state_gpio);
    116
    117	if (priv->device_wake_gpio && wake_state) {
    118		gpiod_set_value(priv->device_wake_gpio, 0);
    119		usleep_range(5, 50);
    120		gpiod_set_value(priv->device_wake_gpio, 1);
    121	}
    122}
    123
    124static int tcan4x5x_reset(struct tcan4x5x_priv *priv)
    125{
    126	int ret = 0;
    127
    128	if (priv->reset_gpio) {
    129		gpiod_set_value(priv->reset_gpio, 1);
    130
    131		/* tpulse_width minimum 30us */
    132		usleep_range(30, 100);
    133		gpiod_set_value(priv->reset_gpio, 0);
    134	} else {
    135		ret = regmap_write(priv->regmap, TCAN4X5X_CONFIG,
    136				   TCAN4X5X_SW_RESET);
    137		if (ret)
    138			return ret;
    139	}
    140
    141	usleep_range(700, 1000);
    142
    143	return ret;
    144}
    145
    146static u32 tcan4x5x_read_reg(struct m_can_classdev *cdev, int reg)
    147{
    148	struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
    149	u32 val;
    150
    151	regmap_read(priv->regmap, TCAN4X5X_MCAN_OFFSET + reg, &val);
    152
    153	return val;
    154}
    155
    156static int tcan4x5x_read_fifo(struct m_can_classdev *cdev, int addr_offset,
    157			      void *val, size_t val_count)
    158{
    159	struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
    160
    161	return regmap_bulk_read(priv->regmap, TCAN4X5X_MRAM_START + addr_offset, val, val_count);
    162}
    163
    164static int tcan4x5x_write_reg(struct m_can_classdev *cdev, int reg, int val)
    165{
    166	struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
    167
    168	return regmap_write(priv->regmap, TCAN4X5X_MCAN_OFFSET + reg, val);
    169}
    170
    171static int tcan4x5x_write_fifo(struct m_can_classdev *cdev,
    172			       int addr_offset, const void *val, size_t val_count)
    173{
    174	struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
    175
    176	return regmap_bulk_write(priv->regmap, TCAN4X5X_MRAM_START + addr_offset, val, val_count);
    177}
    178
    179static int tcan4x5x_power_enable(struct regulator *reg, int enable)
    180{
    181	if (IS_ERR_OR_NULL(reg))
    182		return 0;
    183
    184	if (enable)
    185		return regulator_enable(reg);
    186	else
    187		return regulator_disable(reg);
    188}
    189
    190static int tcan4x5x_write_tcan_reg(struct m_can_classdev *cdev,
    191				   int reg, int val)
    192{
    193	struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
    194
    195	return regmap_write(priv->regmap, reg, val);
    196}
    197
    198static int tcan4x5x_clear_interrupts(struct m_can_classdev *cdev)
    199{
    200	int ret;
    201
    202	ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_STATUS,
    203				      TCAN4X5X_CLEAR_ALL_INT);
    204	if (ret)
    205		return ret;
    206
    207	ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_MCAN_INT_REG,
    208				      TCAN4X5X_ENABLE_MCAN_INT);
    209	if (ret)
    210		return ret;
    211
    212	ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_INT_FLAGS,
    213				      TCAN4X5X_CLEAR_ALL_INT);
    214	if (ret)
    215		return ret;
    216
    217	return tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_ERROR_STATUS,
    218				       TCAN4X5X_CLEAR_ALL_INT);
    219}
    220
    221static int tcan4x5x_init(struct m_can_classdev *cdev)
    222{
    223	struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev);
    224	int ret;
    225
    226	tcan4x5x_check_wake(tcan4x5x);
    227
    228	ret = tcan4x5x_clear_interrupts(cdev);
    229	if (ret)
    230		return ret;
    231
    232	ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_INT_EN,
    233				      TCAN4X5X_ENABLE_TCAN_INT);
    234	if (ret)
    235		return ret;
    236
    237	/* Zero out the MCAN buffers */
    238	ret = m_can_init_ram(cdev);
    239	if (ret)
    240		return ret;
    241
    242	ret = regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
    243				 TCAN4X5X_MODE_SEL_MASK, TCAN4X5X_MODE_NORMAL);
    244	if (ret)
    245		return ret;
    246
    247	return ret;
    248}
    249
    250static int tcan4x5x_disable_wake(struct m_can_classdev *cdev)
    251{
    252	struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev);
    253
    254	return regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
    255				  TCAN4X5X_DISABLE_WAKE_MSK, 0x00);
    256}
    257
    258static int tcan4x5x_disable_state(struct m_can_classdev *cdev)
    259{
    260	struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev);
    261
    262	return regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
    263				  TCAN4X5X_DISABLE_INH_MSK, 0x01);
    264}
    265
    266static int tcan4x5x_get_gpios(struct m_can_classdev *cdev)
    267{
    268	struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev);
    269	int ret;
    270
    271	tcan4x5x->device_wake_gpio = devm_gpiod_get(cdev->dev, "device-wake",
    272						    GPIOD_OUT_HIGH);
    273	if (IS_ERR(tcan4x5x->device_wake_gpio)) {
    274		if (PTR_ERR(tcan4x5x->device_wake_gpio) == -EPROBE_DEFER)
    275			return -EPROBE_DEFER;
    276
    277		tcan4x5x_disable_wake(cdev);
    278	}
    279
    280	tcan4x5x->reset_gpio = devm_gpiod_get_optional(cdev->dev, "reset",
    281						       GPIOD_OUT_LOW);
    282	if (IS_ERR(tcan4x5x->reset_gpio))
    283		tcan4x5x->reset_gpio = NULL;
    284
    285	ret = tcan4x5x_reset(tcan4x5x);
    286	if (ret)
    287		return ret;
    288
    289	tcan4x5x->device_state_gpio = devm_gpiod_get_optional(cdev->dev,
    290							      "device-state",
    291							      GPIOD_IN);
    292	if (IS_ERR(tcan4x5x->device_state_gpio)) {
    293		tcan4x5x->device_state_gpio = NULL;
    294		tcan4x5x_disable_state(cdev);
    295	}
    296
    297	return 0;
    298}
    299
    300static struct m_can_ops tcan4x5x_ops = {
    301	.init = tcan4x5x_init,
    302	.read_reg = tcan4x5x_read_reg,
    303	.write_reg = tcan4x5x_write_reg,
    304	.write_fifo = tcan4x5x_write_fifo,
    305	.read_fifo = tcan4x5x_read_fifo,
    306	.clear_interrupts = tcan4x5x_clear_interrupts,
    307};
    308
    309static int tcan4x5x_can_probe(struct spi_device *spi)
    310{
    311	struct tcan4x5x_priv *priv;
    312	struct m_can_classdev *mcan_class;
    313	int freq, ret;
    314
    315	mcan_class = m_can_class_allocate_dev(&spi->dev,
    316					      sizeof(struct tcan4x5x_priv));
    317	if (!mcan_class)
    318		return -ENOMEM;
    319
    320	priv = cdev_to_priv(mcan_class);
    321
    322	priv->power = devm_regulator_get_optional(&spi->dev, "vsup");
    323	if (PTR_ERR(priv->power) == -EPROBE_DEFER) {
    324		ret = -EPROBE_DEFER;
    325		goto out_m_can_class_free_dev;
    326	} else {
    327		priv->power = NULL;
    328	}
    329
    330	m_can_class_get_clocks(mcan_class);
    331	if (IS_ERR(mcan_class->cclk)) {
    332		dev_err(&spi->dev, "no CAN clock source defined\n");
    333		freq = TCAN4X5X_EXT_CLK_DEF;
    334	} else {
    335		freq = clk_get_rate(mcan_class->cclk);
    336	}
    337
    338	/* Sanity check */
    339	if (freq < 20000000 || freq > TCAN4X5X_EXT_CLK_DEF) {
    340		ret = -ERANGE;
    341		goto out_m_can_class_free_dev;
    342	}
    343
    344	priv->spi = spi;
    345
    346	mcan_class->pm_clock_support = 0;
    347	mcan_class->can.clock.freq = freq;
    348	mcan_class->dev = &spi->dev;
    349	mcan_class->ops = &tcan4x5x_ops;
    350	mcan_class->is_peripheral = true;
    351	mcan_class->net->irq = spi->irq;
    352
    353	spi_set_drvdata(spi, priv);
    354
    355	/* Configure the SPI bus */
    356	spi->bits_per_word = 8;
    357	ret = spi_setup(spi);
    358	if (ret)
    359		goto out_m_can_class_free_dev;
    360
    361	ret = tcan4x5x_regmap_init(priv);
    362	if (ret)
    363		goto out_m_can_class_free_dev;
    364
    365	ret = tcan4x5x_power_enable(priv->power, 1);
    366	if (ret)
    367		goto out_m_can_class_free_dev;
    368
    369	ret = tcan4x5x_get_gpios(mcan_class);
    370	if (ret)
    371		goto out_power;
    372
    373	ret = tcan4x5x_init(mcan_class);
    374	if (ret)
    375		goto out_power;
    376
    377	ret = m_can_class_register(mcan_class);
    378	if (ret)
    379		goto out_power;
    380
    381	netdev_info(mcan_class->net, "TCAN4X5X successfully initialized.\n");
    382	return 0;
    383
    384out_power:
    385	tcan4x5x_power_enable(priv->power, 0);
    386 out_m_can_class_free_dev:
    387	m_can_class_free_dev(mcan_class->net);
    388	return ret;
    389}
    390
    391static void tcan4x5x_can_remove(struct spi_device *spi)
    392{
    393	struct tcan4x5x_priv *priv = spi_get_drvdata(spi);
    394
    395	m_can_class_unregister(&priv->cdev);
    396
    397	tcan4x5x_power_enable(priv->power, 0);
    398
    399	m_can_class_free_dev(priv->cdev.net);
    400}
    401
    402static const struct of_device_id tcan4x5x_of_match[] = {
    403	{
    404		.compatible = "ti,tcan4x5x",
    405	}, {
    406		/* sentinel */
    407	},
    408};
    409MODULE_DEVICE_TABLE(of, tcan4x5x_of_match);
    410
    411static const struct spi_device_id tcan4x5x_id_table[] = {
    412	{
    413		.name = "tcan4x5x",
    414	}, {
    415		/* sentinel */
    416	},
    417};
    418MODULE_DEVICE_TABLE(spi, tcan4x5x_id_table);
    419
    420static struct spi_driver tcan4x5x_can_driver = {
    421	.driver = {
    422		.name = KBUILD_MODNAME,
    423		.of_match_table = tcan4x5x_of_match,
    424		.pm = NULL,
    425	},
    426	.id_table = tcan4x5x_id_table,
    427	.probe = tcan4x5x_can_probe,
    428	.remove = tcan4x5x_can_remove,
    429};
    430module_spi_driver(tcan4x5x_can_driver);
    431
    432MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
    433MODULE_DESCRIPTION("Texas Instruments TCAN4x5x CAN driver");
    434MODULE_LICENSE("GPL v2");