diff options
Diffstat (limited to 'drivers/mfd/rk8xx-spi.c')
-rw-r--r-- | drivers/mfd/rk8xx-spi.c | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/drivers/mfd/rk8xx-spi.c b/drivers/mfd/rk8xx-spi.c new file mode 100644 index 0000000000..fd137f38c2 --- /dev/null +++ b/drivers/mfd/rk8xx-spi.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip RK806 Core (SPI) driver + * + * Copyright (c) 2021 Rockchip Electronics Co., Ltd. + * Copyright (c) 2023 Collabora Ltd. + * + * Author: Xu Shengfei <xsf@rock-chips.com> + * Author: Sebastian Reichel <sebastian.reichel@collabora.com> + */ + +#include <linux/interrupt.h> +#include <linux/mfd/core.h> +#include <linux/mfd/rk808.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#define RK806_ADDR_SIZE 2 +#define RK806_CMD_WITH_SIZE(CMD, VALUE_BYTES) \ + (RK806_CMD_##CMD | RK806_CMD_CRC_DIS | (VALUE_BYTES - 1)) + +static const struct regmap_range rk806_volatile_ranges[] = { + regmap_reg_range(RK806_POWER_EN0, RK806_POWER_EN5), + regmap_reg_range(RK806_DVS_START_CTRL, RK806_INT_MSK1), +}; + +static const struct regmap_access_table rk806_volatile_table = { + .yes_ranges = rk806_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(rk806_volatile_ranges), +}; + +static const struct regmap_config rk806_regmap_config_spi = { + .reg_bits = 16, + .val_bits = 8, + .max_register = RK806_BUCK_RSERVE_REG5, + .cache_type = REGCACHE_RBTREE, + .volatile_table = &rk806_volatile_table, +}; + +static int rk806_spi_bus_write(void *context, const void *vdata, size_t count) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + struct spi_transfer xfer[2] = { 0 }; + /* data and thus count includes the register address */ + size_t val_size = count - RK806_ADDR_SIZE; + char cmd; + + if (val_size < 1 || val_size > (RK806_CMD_LEN_MSK + 1)) + return -EINVAL; + + cmd = RK806_CMD_WITH_SIZE(WRITE, val_size); + + xfer[0].tx_buf = &cmd; + xfer[0].len = sizeof(cmd); + xfer[1].tx_buf = vdata; + xfer[1].len = count; + + return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer)); +} + +static int rk806_spi_bus_read(void *context, const void *vreg, size_t reg_size, + void *val, size_t val_size) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + char txbuf[3] = { 0 }; + + if (reg_size != RK806_ADDR_SIZE || + val_size < 1 || val_size > (RK806_CMD_LEN_MSK + 1)) + return -EINVAL; + + /* TX buffer contains command byte followed by two address bytes */ + txbuf[0] = RK806_CMD_WITH_SIZE(READ, val_size); + memcpy(txbuf+1, vreg, reg_size); + + return spi_write_then_read(spi, txbuf, sizeof(txbuf), val, val_size); +} + +static const struct regmap_bus rk806_regmap_bus_spi = { + .write = rk806_spi_bus_write, + .read = rk806_spi_bus_read, + .reg_format_endian_default = REGMAP_ENDIAN_LITTLE, +}; + +static int rk8xx_spi_probe(struct spi_device *spi) +{ + struct regmap *regmap; + + regmap = devm_regmap_init(&spi->dev, &rk806_regmap_bus_spi, + &spi->dev, &rk806_regmap_config_spi); + if (IS_ERR(regmap)) + return dev_err_probe(&spi->dev, PTR_ERR(regmap), + "Failed to init regmap\n"); + + return rk8xx_probe(&spi->dev, RK806_ID, spi->irq, regmap); +} + +static const struct of_device_id rk8xx_spi_of_match[] = { + { .compatible = "rockchip,rk806", }, + { } +}; +MODULE_DEVICE_TABLE(of, rk8xx_spi_of_match); + +static const struct spi_device_id rk8xx_spi_id_table[] = { + { "rk806", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, rk8xx_spi_id_table); + +static struct spi_driver rk8xx_spi_driver = { + .driver = { + .name = "rk8xx-spi", + .of_match_table = rk8xx_spi_of_match, + }, + .probe = rk8xx_spi_probe, + .id_table = rk8xx_spi_id_table, +}; +module_spi_driver(rk8xx_spi_driver); + +MODULE_AUTHOR("Xu Shengfei <xsf@rock-chips.com>"); +MODULE_DESCRIPTION("RK8xx SPI PMIC driver"); +MODULE_LICENSE("GPL"); |