Hi, On Fri, Jan 20, 2023 at 04:59:21PM +0000, Lee Jones wrote: > On Mon, 09 Jan 2023, Sebastian Reichel wrote: > > > Add support for SPI connected rk806, which is used by the RK3588 > > evaluation boards. The PMIC is advertised to support I2C and SPI, > > but the evaluation boards all use SPI. Thus only SPI support is > > added here. > > > > Signed-off-by: Sebastian Reichel <sebastian.reichel@xxxxxxxxxxxxx> > > --- > > drivers/mfd/Kconfig | 14 ++ > > drivers/mfd/Makefile | 1 + > > drivers/mfd/rk8xx-core.c | 67 ++++++- > > drivers/mfd/rk8xx-spi.c | 122 ++++++++++++ > > include/linux/mfd/rk808.h | 409 ++++++++++++++++++++++++++++++++++++++ > > 5 files changed, 611 insertions(+), 2 deletions(-) > > create mode 100644 drivers/mfd/rk8xx-spi.c > > > > diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig > > index 692e38283bda..13582ea5cb44 100644 > > --- a/drivers/mfd/Kconfig > > +++ b/drivers/mfd/Kconfig > > @@ -1222,6 +1222,20 @@ config MFD_RK8XX_I2C > > through I2C interface. The device supports multiple sub-devices > > including interrupts, RTC, LDO & DCDC regulators, and onkey. > > > > +config MFD_RK8XX_SPI > > + tristate "Rockchip RK806 Power Management Chip" > > + depends on SPI && OF > > + select MFD_CORE > > + select REGMAP_SPI > > + select REGMAP_IRQ > > + select MFD_RK8XX > > + help > > + If you say yes here you get support for the RK806 Power Management > > + chip. > > + This driver provides common support for accessing the device > > + through an SPI interface. The device supports multiple sub-devices > > + including interrupts, LDO & DCDC regulators, and power on-key. > > + > > config MFD_RN5T618 > > tristate "Ricoh RN5T567/618 PMIC" > > depends on I2C > > diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile > > index f65ef1bd0810..a88f27cd837b 100644 > > --- a/drivers/mfd/Makefile > > +++ b/drivers/mfd/Makefile > > @@ -222,6 +222,7 @@ obj-$(CONFIG_MFD_NTXEC) += ntxec.o > > obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o > > obj-$(CONFIG_MFD_RK8XX) += rk8xx-core.o > > obj-$(CONFIG_MFD_RK8XX_I2C) += rk8xx-i2c.o > > +obj-$(CONFIG_MFD_RK8XX_SPI) += rk8xx-spi.o > > obj-$(CONFIG_MFD_RN5T618) += rn5t618.o > > obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o > > obj-$(CONFIG_MFD_SYSCON) += syscon.o > > diff --git a/drivers/mfd/rk8xx-core.c b/drivers/mfd/rk8xx-core.c > > index c52f5fa1a4da..289f4c1f30c2 100644 > > --- a/drivers/mfd/rk8xx-core.c > > +++ b/drivers/mfd/rk8xx-core.c > > @@ -37,6 +37,11 @@ static const struct resource rk805_key_resources[] = { > > DEFINE_RES_IRQ(RK805_IRQ_PWRON_FALL), > > }; > > > > +static struct resource rk806_pwrkey_resources[] = { > > + DEFINE_RES_IRQ(RK806_IRQ_PWRON_FALL), > > + DEFINE_RES_IRQ(RK806_IRQ_PWRON_RISE), > > +}; > > + > > static const struct resource rk817_pwrkey_resources[] = { > > DEFINE_RES_IRQ(RK817_IRQ_PWRON_RISE), > > DEFINE_RES_IRQ(RK817_IRQ_PWRON_FALL), > > @@ -64,6 +69,16 @@ static const struct mfd_cell rk805s[] = { > > }, > > }; > > > > +static const struct mfd_cell rk806s[] = { > > + { .name = "rk805-pinctrl", }, > > + { .name = "rk808-regulator", }, > > + { > > + .name = "rk805-pwrkey", > > + .num_resources = ARRAY_SIZE(rk806_pwrkey_resources), > > + .resources = &rk806_pwrkey_resources[0], > > My OCD-sense is tingling. > > Could you please add the resources *before* the num_resources please? > > Also: '.resources = rk806_pwrkey_resources' is fine Ack. > > > + }, > > +}; > > + > > static const struct mfd_cell rk808s[] = { > > { .name = "rk808-clkout", .id = PLATFORM_DEVID_NONE, }, > > { .name = "rk808-regulator", .id = PLATFORM_DEVID_NONE, }, > > @@ -122,6 +137,12 @@ static const struct rk808_reg_data rk805_pre_init_reg[] = { > > {RK805_THERMAL_REG, TEMP_HOTDIE_MSK, TEMP115C}, > > }; > > > > +static const struct rk808_reg_data rk806_pre_init_reg[] = { > > + { RK806_GPIO_INT_CONFIG, RK806_INT_POL_MSK, RK806_INT_POL_L }, > > + { RK806_SYS_CFG3, RK806_SLAVE_RESTART_FUN_MSK, RK806_SLAVE_RESTART_FUN_EN }, > > + { RK806_SYS_OPTION, RK806_SYS_ENB2_2M_MSK, RK806_SYS_ENB2_2M_EN }, > > +}; > > + > > static const struct rk808_reg_data rk808_pre_init_reg[] = { > > { RK808_BUCK3_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_150MA }, > > { RK808_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_200MA }, > > @@ -272,6 +293,27 @@ static const struct regmap_irq rk805_irqs[] = { > > }, > > }; > > > > +static const struct regmap_irq rk806_irqs[] = { > > + /* INT_STS0 IRQs */ > > + REGMAP_IRQ_REG(RK806_IRQ_PWRON_FALL, 0, RK806_INT_STS_PWRON_FALL), > > + REGMAP_IRQ_REG(RK806_IRQ_PWRON_RISE, 0, RK806_INT_STS_PWRON_RISE), > > + REGMAP_IRQ_REG(RK806_IRQ_PWRON, 0, RK806_INT_STS_PWRON), > > + REGMAP_IRQ_REG(RK806_IRQ_PWRON_LP, 0, RK806_INT_STS_PWRON_LP), > > + REGMAP_IRQ_REG(RK806_IRQ_HOTDIE, 0, RK806_INT_STS_HOTDIE), > > + REGMAP_IRQ_REG(RK806_IRQ_VDC_RISE, 0, RK806_INT_STS_VDC_RISE), > > + REGMAP_IRQ_REG(RK806_IRQ_VDC_FALL, 0, RK806_INT_STS_VDC_FALL), > > + REGMAP_IRQ_REG(RK806_IRQ_VB_LO, 0, RK806_INT_STS_VB_LO), > > + /* INT_STS1 IRQs */ > > + REGMAP_IRQ_REG(RK806_IRQ_REV0, 1, RK806_INT_STS_REV0), > > + REGMAP_IRQ_REG(RK806_IRQ_REV1, 1, RK806_INT_STS_REV1), > > + REGMAP_IRQ_REG(RK806_IRQ_REV2, 1, RK806_INT_STS_REV2), > > + REGMAP_IRQ_REG(RK806_IRQ_CRC_ERROR, 1, RK806_INT_STS_CRC_ERROR), > > + REGMAP_IRQ_REG(RK806_IRQ_SLP3_GPIO, 1, RK806_INT_STS_SLP3_GPIO), > > + REGMAP_IRQ_REG(RK806_IRQ_SLP2_GPIO, 1, RK806_INT_STS_SLP2_GPIO), > > + REGMAP_IRQ_REG(RK806_IRQ_SLP1_GPIO, 1, RK806_INT_STS_SLP1_GPIO), > > + REGMAP_IRQ_REG(RK806_IRQ_WDT, 1, RK806_INT_STS_WDT), > > +}; > > + > > static const struct regmap_irq rk808_irqs[] = { > > /* INT_STS */ > > [RK808_IRQ_VOUT_LO] = { > > @@ -422,6 +464,18 @@ static struct regmap_irq_chip rk805_irq_chip = { > > .init_ack_masked = true, > > }; > > > > +static struct regmap_irq_chip rk806_irq_chip = { > > + .name = "rk806", > > + .irqs = rk806_irqs, > > + .num_irqs = ARRAY_SIZE(rk806_irqs), > > + .num_regs = 2, > > + .irq_reg_stride = 2, > > + .mask_base = RK806_INT_MSK0, > > + .status_base = RK806_INT_STS0, > > + .ack_base = RK806_INT_STS0, > > + .init_ack_masked = true, > > +}; > > + > > static const struct regmap_irq_chip rk808_irq_chip = { > > .name = "rk808", > > .irqs = rk808_irqs, > > @@ -548,6 +602,7 @@ int rk8xx_probe(struct device *dev, int variant, unsigned int irq, struct regmap > > struct rk808 *rk808; > > const struct rk808_reg_data *pre_init_reg; > > const struct mfd_cell *cells; > > + bool dual_support = false; > > int nr_pre_init_regs; > > int nr_cells; > > int ret; > > @@ -569,6 +624,14 @@ int rk8xx_probe(struct device *dev, int variant, unsigned int irq, struct regmap > > cells = rk805s; > > nr_cells = ARRAY_SIZE(rk805s); > > break; > > + case RK806_ID: > > + rk808->regmap_irq_chip = &rk806_irq_chip; > > + pre_init_reg = rk806_pre_init_reg; > > + nr_pre_init_regs = ARRAY_SIZE(rk806_pre_init_reg); > > + cells = rk806s; > > + nr_cells = ARRAY_SIZE(rk806s); > > + dual_support = true; > > + break; > > case RK808_ID: > > rk808->regmap_irq_chip = &rk808_irq_chip; > > pre_init_reg = rk808_pre_init_reg; > > @@ -602,7 +665,7 @@ int rk8xx_probe(struct device *dev, int variant, unsigned int irq, struct regmap > > return dev_err_probe(dev, -EINVAL, "No interrupt support, no core IRQ\n"); > > > > ret = devm_regmap_add_irq_chip(dev, rk808->regmap, irq, > > - IRQF_ONESHOT, -1, > > + IRQF_ONESHOT | (dual_support ? IRQF_SHARED : 0), -1, > > Why not 'dual_support = IRQF_SHARED', then | it regardless? Ack > > > rk808->regmap_irq_chip, &rk808->irq_data); > > if (ret) > > return dev_err_probe(dev, ret, "Failed to add irq_chip\n"); > > @@ -617,7 +680,7 @@ int rk8xx_probe(struct device *dev, int variant, unsigned int irq, struct regmap > > pre_init_reg[i].addr); > > } > > > > - ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, > > + ret = devm_mfd_add_devices(dev, dual_support ? PLATFORM_DEVID_AUTO : PLATFORM_DEVID_NONE, > > Any reason why you can't use AUTO all the time? That would change device names (and thus sysfs paths) for already supported devices. DEVID_AUTO appends a ".<number>" suffix compared to DEVID_NONE. > > cells, nr_cells, NULL, 0, > > regmap_irq_get_domain(rk808->irq_data)); > > if (ret) > > diff --git a/drivers/mfd/rk8xx-spi.c b/drivers/mfd/rk8xx-spi.c > > new file mode 100644 > > index 000000000000..3fbcaaaa453a > > --- /dev/null > > +++ b/drivers/mfd/rk8xx-spi.c > > @@ -0,0 +1,122 @@ > > +// 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@xxxxxxxxxxxxxx> > > + * Author: Sebastian Reichel <sebastian.reichel@xxxxxxxxxxxxx> > > + */ > > + > > +#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_CMD 0 > > +#define RK806_REG_ADDR_L 1 > > +#define RK806_REG_ADDR_H 2 > > + > > +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 = 8, > > + .val_bits = 8, > > + .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); > > + const char *data = vdata; > > + char buffer[3] = { 0 }; > > + struct spi_transfer xfer[2] = { 0 }; > > + > > + buffer[RK806_CMD] = RK806_CMD_WRITE | (count - 2); > > Comment or define please. > > > + buffer[RK806_REG_ADDR_L] = data[0]; > > As above. > > > + buffer[RK806_REG_ADDR_H] = RK806_REG_H; > > + > > + xfer[0].tx_buf = buffer; > > + xfer[0].len = sizeof(buffer); > > + xfer[1].tx_buf = data+1; > > And here. > > > + xfer[1].len = count-1; > > Here too. I don't follow. What part do you need comments/defines for? I did add the RK806_CMD/RK806_REG_ADDR_L/RK806_REG_ADDR_H when you asked for defines last times, so obviously you meant something else :) > Nit: spaces around the '+' and '-' please. Ack. > > + 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); > > + const char *reg = vreg; > > + char txbuf[3] = { 0 }; > > + > > + if (reg_size != sizeof(char) || val_size < 1) > > + return -EINVAL; > > + > > + txbuf[RK806_CMD] = RK806_CMD_READ | (val_size - 1); > > + txbuf[RK806_REG_ADDR_L] = *reg; > > + txbuf[RK806_REG_ADDR_H] = RK806_REG_H; > > What's this for? Isn't it already 0? Yes. I guess I could remove the '= { 0 }' init considering I explicitly do a full init of the buffer. OTOH the compiler will optimize this anyways. -- Sebastian > > + 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_NATIVE, > > + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, > > +}; > > + > > +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 initialize register map\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@xxxxxxxxxxxxxx>"); > > +MODULE_DESCRIPTION("RK8xx SPI PMIC driver"); > > +MODULE_LICENSE("GPL v2"); > > diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h > > -- > Lee Jones [李琼斯] > > -- > To unsubscribe, send mail to kernel-unsubscribe@xxxxxxxxxxxxxxxxxxxxx.
Attachment:
signature.asc
Description: PGP signature