summaryrefslogtreecommitdiff
path: root/drivers/mfd
diff options
context:
space:
mode:
authorSebastian Reichel <sebastian.reichel@collabora.com>2023-05-04 20:36:12 +0300
committerLee Jones <lee@kernel.org>2023-05-15 18:20:22 +0300
commit210f418f8ace9f056c337f7945e0ae3e242b3389 (patch)
tree2b4d8b57f9abb65ff7e8e182406b6fe5f2da82d2 /drivers/mfd
parent706a4141d40fd98a884824b3d97b19b0ecb7e9a8 (diff)
downloadlinux-210f418f8ace9f056c337f7945e0ae3e242b3389.tar.xz
mfd: rk8xx: Add rk806 support
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. Tested-by: Diederik de Haas <didi.debian@cknow.org> # Rock64, Quartz64 Model A + B Tested-by: Vincent Legoll <vincent.legoll@gmail.com> # Pine64 QuartzPro64 Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> Link: https://lore.kernel.org/r/20230504173618.142075-9-sebastian.reichel@collabora.com Signed-off-by: Lee Jones <lee@kernel.org>
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/Kconfig14
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/rk8xx-core.c69
-rw-r--r--drivers/mfd/rk8xx-spi.c124
4 files changed, 205 insertions, 3 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index de53e6c701fd..d4879cb4e1f6 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1201,6 +1201,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 ba373193e999..4e666ef5b7fc 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -216,6 +216,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 ddf2052c5190..e8fc9e2ab1d0 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,17 @@ static const struct mfd_cell rk805s[] = {
},
};
+static const struct mfd_cell rk806s[] = {
+ { .name = "rk805-pinctrl", .id = PLATFORM_DEVID_AUTO, },
+ { .name = "rk808-regulator", .id = PLATFORM_DEVID_AUTO, },
+ {
+ .name = "rk805-pwrkey",
+ .resources = rk806_pwrkey_resources,
+ .num_resources = ARRAY_SIZE(rk806_pwrkey_resources),
+ .id = PLATFORM_DEVID_AUTO,
+ },
+};
+
static const struct mfd_cell rk808s[] = {
{ .name = "rk808-clkout", .id = PLATFORM_DEVID_NONE, },
{ .name = "rk808-regulator", .id = PLATFORM_DEVID_NONE, },
@@ -123,6 +139,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 },
@@ -273,6 +295,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] = {
@@ -423,6 +466,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,
@@ -549,6 +604,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;
+ int dual_support = 0;
int nr_pre_init_regs;
int nr_cells;
int ret;
@@ -570,6 +626,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 = IRQF_SHARED;
+ break;
case RK808_ID:
rk808->regmap_irq_chip = &rk808_irq_chip;
pre_init_reg = rk808_pre_init_reg;
@@ -601,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, -1,
rk808->regmap_irq_chip, &rk808->irq_data);
if (ret)
return dev_err_probe(dev, ret, "Failed to add irq_chip\n");
@@ -616,8 +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,
- cells, nr_cells, NULL, 0,
+ ret = devm_mfd_add_devices(dev, 0, cells, nr_cells, NULL, 0,
regmap_irq_get_domain(rk808->irq_data));
if (ret)
return dev_err_probe(dev, ret, "failed to add MFD devices\n");
diff --git a/drivers/mfd/rk8xx-spi.c b/drivers/mfd/rk8xx-spi.c
new file mode 100644
index 000000000000..fd137f38c2c4
--- /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");