Skip to content

Commit

Permalink
mfd: rk8xx: Add rk806 support
Browse files Browse the repository at this point in the history
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 <[email protected]> # Rock64, Quartz64 Model A + B
Tested-by: Vincent Legoll <[email protected]> # Pine64 QuartzPro64
Signed-off-by: Sebastian Reichel <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Lee Jones <[email protected]>
  • Loading branch information
sre authored and lag-linaro committed May 15, 2023
1 parent 706a414 commit 210f418
Show file tree
Hide file tree
Showing 5 changed files with 614 additions and 3 deletions.
14 changes: 14 additions & 0 deletions drivers/mfd/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions drivers/mfd/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
69 changes: 66 additions & 3 deletions drivers/mfd/rk8xx-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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, },
Expand Down Expand Up @@ -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 },
Expand Down Expand Up @@ -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] = {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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");
Expand All @@ -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");
Expand Down
124 changes: 124 additions & 0 deletions drivers/mfd/rk8xx-spi.c
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
* Author: Sebastian Reichel <[email protected]>
*/

#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 <[email protected]>");
MODULE_DESCRIPTION("RK8xx SPI PMIC driver");
MODULE_LICENSE("GPL");
Loading

0 comments on commit 210f418

Please sign in to comment.