Merge patch series "can: mcp251xfd: add gpio functionality"
Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com> says: The mcp251xfd allows two pins to be configured as GPIOs. This series adds support for this feature. The GPIO functionality is controlled with the IOCON register which has an erratum. Patch 1 from https://lore.kernel.org/linux-can/20240429-mcp251xfd-runtime_pm-v1-3-c26a93a66544@pengutronix.de/ Patch 2 refactor of no-crc functions to prepare workaround for non-crc writes Patch 3 is the fix/workaround for the aforementioned erratum Patch 4 only configure pin1 for rx-int Patch 5 adds the gpio support Patch 6 updates dt-binding https://lore.kernel.org/all/20240806-industrious-augmented-crane-44239a-mkl@pengutronix.de/ Link: https://patch.msgid.link/20251001091006.4003841-1-viken.dadhaniya@oss.qualcomm.com Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
commit
2d0938702c
|
|
@ -49,6 +49,11 @@ properties:
|
|||
Must be half or less of "clocks" frequency.
|
||||
maximum: 20000000
|
||||
|
||||
gpio-controller: true
|
||||
|
||||
"#gpio-cells":
|
||||
const: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ config CAN_MCP251XFD
|
|||
select CAN_RX_OFFLOAD
|
||||
select REGMAP
|
||||
select WANT_DEV_COREDUMP
|
||||
select GPIOLIB
|
||||
help
|
||||
Driver for the Microchip MCP251XFD SPI FD-CAN controller
|
||||
family.
|
||||
|
|
|
|||
|
|
@ -608,23 +608,21 @@ static int mcp251xfd_set_bittiming(const struct mcp251xfd_priv *priv)
|
|||
|
||||
static int mcp251xfd_chip_rx_int_enable(const struct mcp251xfd_priv *priv)
|
||||
{
|
||||
u32 val;
|
||||
u32 val, mask;
|
||||
|
||||
if (!priv->rx_int)
|
||||
return 0;
|
||||
|
||||
/* Configure GPIOs:
|
||||
* - PIN0: GPIO Input
|
||||
* - PIN1: GPIO Input/RX Interrupt
|
||||
/* Configure PIN1 as RX Interrupt:
|
||||
*
|
||||
* PIN1 must be Input, otherwise there is a glitch on the
|
||||
* rx-INT line. It happens between setting the PIN as output
|
||||
* (in the first byte of the SPI transfer) and configuring the
|
||||
* PIN as interrupt (in the last byte of the SPI transfer).
|
||||
*/
|
||||
val = MCP251XFD_REG_IOCON_PM0 | MCP251XFD_REG_IOCON_TRIS1 |
|
||||
MCP251XFD_REG_IOCON_TRIS0;
|
||||
return regmap_write(priv->map_reg, MCP251XFD_REG_IOCON, val);
|
||||
val = MCP251XFD_REG_IOCON_TRIS(1);
|
||||
mask = MCP251XFD_REG_IOCON_TRIS(1) | MCP251XFD_REG_IOCON_PM(1);
|
||||
return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON, mask, val);
|
||||
}
|
||||
|
||||
static int mcp251xfd_chip_rx_int_disable(const struct mcp251xfd_priv *priv)
|
||||
|
|
@ -634,13 +632,9 @@ static int mcp251xfd_chip_rx_int_disable(const struct mcp251xfd_priv *priv)
|
|||
if (!priv->rx_int)
|
||||
return 0;
|
||||
|
||||
/* Configure GPIOs:
|
||||
* - PIN0: GPIO Input
|
||||
* - PIN1: GPIO Input
|
||||
*/
|
||||
val = MCP251XFD_REG_IOCON_PM1 | MCP251XFD_REG_IOCON_PM0 |
|
||||
MCP251XFD_REG_IOCON_TRIS1 | MCP251XFD_REG_IOCON_TRIS0;
|
||||
return regmap_write(priv->map_reg, MCP251XFD_REG_IOCON, val);
|
||||
/* Configure PIN1 as GPIO Input */
|
||||
val = MCP251XFD_REG_IOCON_PM(1) | MCP251XFD_REG_IOCON_TRIS(1);
|
||||
return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON, val, val);
|
||||
}
|
||||
|
||||
static int mcp251xfd_chip_ecc_init(struct mcp251xfd_priv *priv)
|
||||
|
|
@ -767,21 +761,13 @@ static void mcp251xfd_chip_stop(struct mcp251xfd_priv *priv,
|
|||
mcp251xfd_chip_interrupts_disable(priv);
|
||||
mcp251xfd_chip_rx_int_disable(priv);
|
||||
mcp251xfd_timestamp_stop(priv);
|
||||
mcp251xfd_chip_sleep(priv);
|
||||
mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_CONFIG);
|
||||
}
|
||||
|
||||
static int mcp251xfd_chip_start(struct mcp251xfd_priv *priv)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = mcp251xfd_chip_softreset(priv);
|
||||
if (err)
|
||||
goto out_chip_stop;
|
||||
|
||||
err = mcp251xfd_chip_clock_init(priv);
|
||||
if (err)
|
||||
goto out_chip_stop;
|
||||
|
||||
err = mcp251xfd_chip_timestamp_init(priv);
|
||||
if (err)
|
||||
goto out_chip_stop;
|
||||
|
|
@ -1625,8 +1611,11 @@ static int mcp251xfd_open(struct net_device *ndev)
|
|||
return err;
|
||||
|
||||
err = pm_runtime_resume_and_get(ndev->dev.parent);
|
||||
if (err)
|
||||
if (err) {
|
||||
if (err == -ETIMEDOUT || err == -ENODEV)
|
||||
pm_runtime_set_suspended(ndev->dev.parent);
|
||||
goto out_close_candev;
|
||||
}
|
||||
|
||||
err = mcp251xfd_ring_alloc(priv);
|
||||
if (err)
|
||||
|
|
@ -1808,6 +1797,160 @@ static int mcp251xfd_register_check_rx_int(struct mcp251xfd_priv *priv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const char * const mcp251xfd_gpio_names[] = { "GPIO0", "GPIO1" };
|
||||
|
||||
static int mcp251xfd_gpio_request(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
|
||||
u32 pin_mask = MCP251XFD_REG_IOCON_PM(offset);
|
||||
int ret;
|
||||
|
||||
if (priv->rx_int && offset == 1) {
|
||||
netdev_err(priv->ndev, "Can't use GPIO 1 with RX-INT!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = pm_runtime_resume_and_get(priv->ndev->dev.parent);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON, pin_mask, pin_mask);
|
||||
}
|
||||
|
||||
static void mcp251xfd_gpio_free(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
|
||||
|
||||
pm_runtime_put(priv->ndev->dev.parent);
|
||||
}
|
||||
|
||||
static int mcp251xfd_gpio_get_direction(struct gpio_chip *chip,
|
||||
unsigned int offset)
|
||||
{
|
||||
struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
|
||||
u32 mask = MCP251XFD_REG_IOCON_TRIS(offset);
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(priv->map_reg, MCP251XFD_REG_IOCON, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (mask & val)
|
||||
return GPIO_LINE_DIRECTION_IN;
|
||||
|
||||
return GPIO_LINE_DIRECTION_OUT;
|
||||
}
|
||||
|
||||
static int mcp251xfd_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
|
||||
u32 mask = MCP251XFD_REG_IOCON_GPIO(offset);
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(priv->map_reg, MCP251XFD_REG_IOCON, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return !!(mask & val);
|
||||
}
|
||||
|
||||
static int mcp251xfd_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
|
||||
unsigned long *bit)
|
||||
{
|
||||
struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(priv->map_reg, MCP251XFD_REG_IOCON, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*bit = FIELD_GET(MCP251XFD_REG_IOCON_GPIO_MASK, val) & *mask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcp251xfd_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned int offset, int value)
|
||||
{
|
||||
struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
|
||||
u32 dir_mask = MCP251XFD_REG_IOCON_TRIS(offset);
|
||||
u32 val_mask = MCP251XFD_REG_IOCON_LAT(offset);
|
||||
u32 val;
|
||||
|
||||
if (value)
|
||||
val = val_mask;
|
||||
else
|
||||
val = 0;
|
||||
|
||||
return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON,
|
||||
dir_mask | val_mask, val);
|
||||
}
|
||||
|
||||
static int mcp251xfd_gpio_direction_input(struct gpio_chip *chip,
|
||||
unsigned int offset)
|
||||
{
|
||||
struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
|
||||
u32 dir_mask = MCP251XFD_REG_IOCON_TRIS(offset);
|
||||
|
||||
return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON, dir_mask, dir_mask);
|
||||
}
|
||||
|
||||
static int mcp251xfd_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
|
||||
{
|
||||
struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
|
||||
u32 val_mask = MCP251XFD_REG_IOCON_LAT(offset);
|
||||
u32 val;
|
||||
|
||||
if (value)
|
||||
val = val_mask;
|
||||
else
|
||||
val = 0;
|
||||
|
||||
return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON, val_mask, val);
|
||||
}
|
||||
|
||||
static int mcp251xfd_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask,
|
||||
unsigned long *bits)
|
||||
{
|
||||
struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
|
||||
u32 val;
|
||||
|
||||
val = FIELD_PREP(MCP251XFD_REG_IOCON_LAT_MASK, *bits);
|
||||
|
||||
return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON,
|
||||
MCP251XFD_REG_IOCON_LAT_MASK, val);
|
||||
}
|
||||
|
||||
static int mcp251fdx_gpio_setup(struct mcp251xfd_priv *priv)
|
||||
{
|
||||
struct gpio_chip *gc = &priv->gc;
|
||||
|
||||
if (!device_property_present(&priv->spi->dev, "gpio-controller"))
|
||||
return 0;
|
||||
|
||||
gc->label = dev_name(&priv->spi->dev);
|
||||
gc->parent = &priv->spi->dev;
|
||||
gc->owner = THIS_MODULE;
|
||||
gc->request = mcp251xfd_gpio_request;
|
||||
gc->free = mcp251xfd_gpio_free;
|
||||
gc->get_direction = mcp251xfd_gpio_get_direction;
|
||||
gc->direction_output = mcp251xfd_gpio_direction_output;
|
||||
gc->direction_input = mcp251xfd_gpio_direction_input;
|
||||
gc->get = mcp251xfd_gpio_get;
|
||||
gc->get_multiple = mcp251xfd_gpio_get_multiple;
|
||||
gc->set = mcp251xfd_gpio_set;
|
||||
gc->set_multiple = mcp251xfd_gpio_set_multiple;
|
||||
gc->base = -1;
|
||||
gc->can_sleep = true;
|
||||
gc->ngpio = ARRAY_SIZE(mcp251xfd_gpio_names);
|
||||
gc->names = mcp251xfd_gpio_names;
|
||||
|
||||
return devm_gpiochip_add_data(&priv->spi->dev, gc, priv);
|
||||
}
|
||||
|
||||
static int
|
||||
mcp251xfd_register_get_dev_id(const struct mcp251xfd_priv *priv, u32 *dev_id,
|
||||
u32 *effective_speed_hz_slow,
|
||||
|
|
@ -1907,53 +2050,59 @@ static int mcp251xfd_register(struct mcp251xfd_priv *priv)
|
|||
struct net_device *ndev = priv->ndev;
|
||||
int err;
|
||||
|
||||
mcp251xfd_register_quirks(priv);
|
||||
|
||||
err = mcp251xfd_clks_and_vdd_enable(priv);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = mcp251xfd_chip_softreset(priv);
|
||||
if (err == -ENODEV)
|
||||
goto out_clks_and_vdd_disable;
|
||||
if (err)
|
||||
goto out_chip_sleep;
|
||||
|
||||
err = mcp251xfd_chip_clock_init(priv);
|
||||
if (err == -ENODEV)
|
||||
goto out_clks_and_vdd_disable;
|
||||
if (err)
|
||||
goto out_chip_sleep;
|
||||
|
||||
pm_runtime_get_noresume(ndev->dev.parent);
|
||||
err = pm_runtime_set_active(ndev->dev.parent);
|
||||
if (err)
|
||||
goto out_runtime_put_noidle;
|
||||
pm_runtime_enable(ndev->dev.parent);
|
||||
|
||||
mcp251xfd_register_quirks(priv);
|
||||
|
||||
err = mcp251xfd_chip_softreset(priv);
|
||||
if (err == -ENODEV)
|
||||
goto out_runtime_disable;
|
||||
if (err)
|
||||
goto out_chip_sleep;
|
||||
|
||||
err = mcp251xfd_chip_clock_init(priv);
|
||||
if (err == -ENODEV)
|
||||
goto out_runtime_disable;
|
||||
if (err)
|
||||
goto out_chip_sleep;
|
||||
|
||||
err = mcp251xfd_register_chip_detect(priv);
|
||||
if (err)
|
||||
goto out_chip_sleep;
|
||||
goto out_runtime_disable;
|
||||
|
||||
err = mcp251xfd_register_check_rx_int(priv);
|
||||
if (err)
|
||||
goto out_chip_sleep;
|
||||
goto out_runtime_disable;
|
||||
|
||||
mcp251xfd_ethtool_init(priv);
|
||||
|
||||
err = mcp251fdx_gpio_setup(priv);
|
||||
if (err) {
|
||||
dev_err_probe(&priv->spi->dev, err, "Failed to register gpio-controller.\n");
|
||||
goto out_runtime_disable;
|
||||
}
|
||||
|
||||
err = register_candev(ndev);
|
||||
if (err)
|
||||
goto out_chip_sleep;
|
||||
goto out_runtime_disable;
|
||||
|
||||
err = mcp251xfd_register_done(priv);
|
||||
if (err)
|
||||
goto out_unregister_candev;
|
||||
|
||||
/* Put controller into sleep mode and let pm_runtime_put()
|
||||
* disable the clocks and vdd. If CONFIG_PM is not enabled,
|
||||
* the clocks and vdd will stay powered.
|
||||
/* Put controller into Config mode and let pm_runtime_put()
|
||||
* put in sleep mode, disable the clocks and vdd. If CONFIG_PM
|
||||
* is not enabled, the clocks and vdd will stay powered.
|
||||
*/
|
||||
err = mcp251xfd_chip_sleep(priv);
|
||||
err = mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_CONFIG);
|
||||
if (err)
|
||||
goto out_unregister_candev;
|
||||
|
||||
|
|
@ -1963,12 +2112,13 @@ static int mcp251xfd_register(struct mcp251xfd_priv *priv)
|
|||
|
||||
out_unregister_candev:
|
||||
unregister_candev(ndev);
|
||||
out_chip_sleep:
|
||||
mcp251xfd_chip_sleep(priv);
|
||||
out_runtime_disable:
|
||||
pm_runtime_disable(ndev->dev.parent);
|
||||
out_runtime_put_noidle:
|
||||
pm_runtime_put_noidle(ndev->dev.parent);
|
||||
out_chip_sleep:
|
||||
mcp251xfd_chip_sleep(priv);
|
||||
out_clks_and_vdd_disable:
|
||||
mcp251xfd_clks_and_vdd_disable(priv);
|
||||
|
||||
return err;
|
||||
|
|
@ -1980,10 +2130,12 @@ static inline void mcp251xfd_unregister(struct mcp251xfd_priv *priv)
|
|||
|
||||
unregister_candev(ndev);
|
||||
|
||||
if (pm_runtime_enabled(ndev->dev.parent))
|
||||
if (pm_runtime_enabled(ndev->dev.parent)) {
|
||||
pm_runtime_disable(ndev->dev.parent);
|
||||
else
|
||||
} else {
|
||||
mcp251xfd_chip_sleep(priv);
|
||||
mcp251xfd_clks_and_vdd_disable(priv);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct of_device_id mcp251xfd_of_match[] = {
|
||||
|
|
@ -2206,16 +2358,41 @@ static void mcp251xfd_remove(struct spi_device *spi)
|
|||
|
||||
static int __maybe_unused mcp251xfd_runtime_suspend(struct device *device)
|
||||
{
|
||||
const struct mcp251xfd_priv *priv = dev_get_drvdata(device);
|
||||
struct mcp251xfd_priv *priv = dev_get_drvdata(device);
|
||||
|
||||
mcp251xfd_chip_sleep(priv);
|
||||
return mcp251xfd_clks_and_vdd_disable(priv);
|
||||
}
|
||||
|
||||
static int __maybe_unused mcp251xfd_runtime_resume(struct device *device)
|
||||
{
|
||||
const struct mcp251xfd_priv *priv = dev_get_drvdata(device);
|
||||
struct mcp251xfd_priv *priv = dev_get_drvdata(device);
|
||||
int err;
|
||||
|
||||
return mcp251xfd_clks_and_vdd_enable(priv);
|
||||
err = mcp251xfd_clks_and_vdd_enable(priv);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = mcp251xfd_chip_softreset(priv);
|
||||
if (err == -ENODEV)
|
||||
goto out_clks_and_vdd_disable;
|
||||
if (err)
|
||||
goto out_chip_sleep;
|
||||
|
||||
err = mcp251xfd_chip_clock_init(priv);
|
||||
if (err == -ENODEV)
|
||||
goto out_clks_and_vdd_disable;
|
||||
if (err)
|
||||
goto out_chip_sleep;
|
||||
|
||||
return 0;
|
||||
|
||||
out_chip_sleep:
|
||||
mcp251xfd_chip_sleep(priv);
|
||||
out_clks_and_vdd_disable:
|
||||
mcp251xfd_clks_and_vdd_disable(priv);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops mcp251xfd_pm_ops = {
|
||||
|
|
|
|||
|
|
@ -13,17 +13,9 @@
|
|||
static const struct regmap_config mcp251xfd_regmap_crc;
|
||||
|
||||
static int
|
||||
mcp251xfd_regmap_nocrc_write(void *context, const void *data, size_t count)
|
||||
{
|
||||
struct spi_device *spi = context;
|
||||
|
||||
return spi_write(spi, data, count);
|
||||
}
|
||||
|
||||
static int
|
||||
mcp251xfd_regmap_nocrc_gather_write(void *context,
|
||||
const void *reg, size_t reg_len,
|
||||
const void *val, size_t val_len)
|
||||
_mcp251xfd_regmap_nocrc_gather_write(void *context,
|
||||
const void *reg, size_t reg_len,
|
||||
const void *val, size_t val_len)
|
||||
{
|
||||
struct spi_device *spi = context;
|
||||
struct mcp251xfd_priv *priv = spi_get_drvdata(spi);
|
||||
|
|
@ -47,6 +39,54 @@ mcp251xfd_regmap_nocrc_gather_write(void *context,
|
|||
return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
|
||||
}
|
||||
|
||||
static int
|
||||
mcp251xfd_regmap_nocrc_gather_write(void *context,
|
||||
const void *reg_p, size_t reg_len,
|
||||
const void *val, size_t val_len)
|
||||
{
|
||||
const u16 byte_exclude = MCP251XFD_REG_IOCON +
|
||||
mcp251xfd_first_byte_set(MCP251XFD_REG_IOCON_GPIO_MASK);
|
||||
u16 reg = be16_to_cpu(*(__be16 *)reg_p) & MCP251XFD_SPI_ADDRESS_MASK;
|
||||
int ret;
|
||||
|
||||
/* Never write to bits 16..23 of IOCON register to avoid clearing of LAT0/LAT1
|
||||
*
|
||||
* According to MCP2518FD Errata DS80000789E 5 writing IOCON register using one
|
||||
* SPI write command clears LAT0/LAT1.
|
||||
*
|
||||
* Errata Fix/Work Around suggests to write registers with single byte
|
||||
* write instructions. However, it seems that the byte at 0xe06(IOCON[23:16])
|
||||
* is for read-only access and writing to it causes the clearing of LAT0/LAT1.
|
||||
*/
|
||||
if (reg <= byte_exclude && reg + val_len > byte_exclude) {
|
||||
size_t len = byte_exclude - reg;
|
||||
|
||||
/* Write up to 0xe05 */
|
||||
ret = _mcp251xfd_regmap_nocrc_gather_write(context, reg_p, reg_len, val, len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Write from 0xe07 on */
|
||||
reg += len + 1;
|
||||
reg = (__force unsigned short)cpu_to_be16(MCP251XFD_SPI_INSTRUCTION_WRITE | reg);
|
||||
return _mcp251xfd_regmap_nocrc_gather_write(context, ®, reg_len,
|
||||
val + len + 1,
|
||||
val_len - len - 1);
|
||||
}
|
||||
|
||||
return _mcp251xfd_regmap_nocrc_gather_write(context, reg_p, reg_len,
|
||||
val, val_len);
|
||||
}
|
||||
|
||||
static int
|
||||
mcp251xfd_regmap_nocrc_write(void *context, const void *data, size_t count)
|
||||
{
|
||||
const size_t data_offset = sizeof(__be16);
|
||||
|
||||
return mcp251xfd_regmap_nocrc_gather_write(context, data, data_offset,
|
||||
data + data_offset, count - data_offset);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
mcp251xfd_update_bits_read_reg(const struct mcp251xfd_priv *priv,
|
||||
unsigned int reg)
|
||||
|
|
@ -64,6 +104,7 @@ mcp251xfd_update_bits_read_reg(const struct mcp251xfd_priv *priv,
|
|||
case MCP251XFD_REG_CON:
|
||||
case MCP251XFD_REG_OSC:
|
||||
case MCP251XFD_REG_ECCCON:
|
||||
case MCP251XFD_REG_IOCON:
|
||||
return true;
|
||||
default:
|
||||
mcp251xfd_for_each_rx_ring(priv, ring, n) {
|
||||
|
|
@ -139,10 +180,9 @@ mcp251xfd_regmap_nocrc_update_bits(void *context, unsigned int reg,
|
|||
tmp_le32 = orig_le32 & ~mask_le32;
|
||||
tmp_le32 |= val_le32 & mask_le32;
|
||||
|
||||
mcp251xfd_spi_cmd_write_nocrc(&buf_tx->cmd, reg + first_byte);
|
||||
memcpy(buf_tx->data, &tmp_le32, len);
|
||||
|
||||
return spi_write(spi, buf_tx, sizeof(buf_tx->cmd) + len);
|
||||
reg += first_byte;
|
||||
mcp251xfd_spi_cmd_write_nocrc(&buf_tx->cmd, reg);
|
||||
return mcp251xfd_regmap_nocrc_gather_write(context, &buf_tx->cmd, 2, &tmp_le32, len);
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
@ -196,9 +236,9 @@ mcp251xfd_regmap_nocrc_read(void *context,
|
|||
}
|
||||
|
||||
static int
|
||||
mcp251xfd_regmap_crc_gather_write(void *context,
|
||||
const void *reg_p, size_t reg_len,
|
||||
const void *val, size_t val_len)
|
||||
_mcp251xfd_regmap_crc_gather_write(void *context,
|
||||
const void *reg_p, size_t reg_len,
|
||||
const void *val, size_t val_len)
|
||||
{
|
||||
struct spi_device *spi = context;
|
||||
struct mcp251xfd_priv *priv = spi_get_drvdata(spi);
|
||||
|
|
@ -229,6 +269,44 @@ mcp251xfd_regmap_crc_gather_write(void *context,
|
|||
return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
|
||||
}
|
||||
|
||||
static int
|
||||
mcp251xfd_regmap_crc_gather_write(void *context,
|
||||
const void *reg_p, size_t reg_len,
|
||||
const void *val, size_t val_len)
|
||||
{
|
||||
const u16 byte_exclude = MCP251XFD_REG_IOCON +
|
||||
mcp251xfd_first_byte_set(MCP251XFD_REG_IOCON_GPIO_MASK);
|
||||
u16 reg = *(u16 *)reg_p;
|
||||
int ret;
|
||||
|
||||
/* Never write to bits 16..23 of IOCON register to avoid clearing of LAT0/LAT1
|
||||
*
|
||||
* According to MCP2518FD Errata DS80000789E 5 writing IOCON register using one
|
||||
* SPI write command clears LAT0/LAT1.
|
||||
*
|
||||
* Errata Fix/Work Around suggests to write registers with single byte
|
||||
* write instructions. However, it seems that the byte at 0xe06(IOCON[23:16])
|
||||
* is for read-only access and writing to it causes the clearing of LAT0/LAT1.
|
||||
*/
|
||||
if (reg <= byte_exclude && reg + val_len > byte_exclude) {
|
||||
size_t len = byte_exclude - reg;
|
||||
|
||||
/* Write up to 0xe05 */
|
||||
ret = _mcp251xfd_regmap_crc_gather_write(context, ®, reg_len, val, len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Write from 0xe07 on */
|
||||
reg += len + 1;
|
||||
return _mcp251xfd_regmap_crc_gather_write(context, ®, reg_len,
|
||||
val + len + 1,
|
||||
val_len - len - 1);
|
||||
}
|
||||
|
||||
return _mcp251xfd_regmap_crc_gather_write(context, reg_p, reg_len,
|
||||
val, val_len);
|
||||
}
|
||||
|
||||
static int
|
||||
mcp251xfd_regmap_crc_write(void *context,
|
||||
const void *data, size_t count)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/can/dev.h>
|
||||
#include <linux/can/rx-offload.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/regmap.h>
|
||||
|
|
@ -335,13 +336,19 @@
|
|||
#define MCP251XFD_REG_IOCON_TXCANOD BIT(28)
|
||||
#define MCP251XFD_REG_IOCON_PM1 BIT(25)
|
||||
#define MCP251XFD_REG_IOCON_PM0 BIT(24)
|
||||
#define MCP251XFD_REG_IOCON_PM(n) (MCP251XFD_REG_IOCON_PM0 << (n))
|
||||
#define MCP251XFD_REG_IOCON_GPIO1 BIT(17)
|
||||
#define MCP251XFD_REG_IOCON_GPIO0 BIT(16)
|
||||
#define MCP251XFD_REG_IOCON_GPIO(n) (MCP251XFD_REG_IOCON_GPIO0 << (n))
|
||||
#define MCP251XFD_REG_IOCON_GPIO_MASK GENMASK(17, 16)
|
||||
#define MCP251XFD_REG_IOCON_LAT1 BIT(9)
|
||||
#define MCP251XFD_REG_IOCON_LAT0 BIT(8)
|
||||
#define MCP251XFD_REG_IOCON_LAT(n) (MCP251XFD_REG_IOCON_LAT0 << (n))
|
||||
#define MCP251XFD_REG_IOCON_LAT_MASK GENMASK(9, 8)
|
||||
#define MCP251XFD_REG_IOCON_XSTBYEN BIT(6)
|
||||
#define MCP251XFD_REG_IOCON_TRIS1 BIT(1)
|
||||
#define MCP251XFD_REG_IOCON_TRIS0 BIT(0)
|
||||
#define MCP251XFD_REG_IOCON_TRIS(n) (MCP251XFD_REG_IOCON_TRIS0 << (n))
|
||||
|
||||
#define MCP251XFD_REG_CRC 0xe08
|
||||
#define MCP251XFD_REG_CRC_FERRIE BIT(25)
|
||||
|
|
@ -670,6 +677,7 @@ struct mcp251xfd_priv {
|
|||
|
||||
struct mcp251xfd_devtype_data devtype_data;
|
||||
struct can_berr_counter bec;
|
||||
struct gpio_chip gc;
|
||||
};
|
||||
|
||||
#define MCP251XFD_IS(_model) \
|
||||
|
|
|
|||
Loading…
Reference in New Issue