From 796ac7edea839865eb5499edec7356c810bf4fe8 Mon Sep 17 00:00:00 2001 From: "Kevin.xie" Date: Tue, 12 Jul 2022 09:57:05 +0800 Subject: [PATCH] driver: regulator: Add axp15060 pmic regulator driver Add support for the axp15060 pmic. Signed-off-by: Kevin.xie --- arch/riscv/configs/starfive_jh7110_defconfig | 1 + drivers/regulator/Kconfig | 8 + drivers/regulator/Makefile | 1 + drivers/regulator/axp15060-regulator.c | 381 +++++++++++++++++++++++++++ include/linux/regulator/axp15060.h | 47 ++++ 5 files changed, 438 insertions(+) create mode 100644 drivers/regulator/axp15060-regulator.c create mode 100644 include/linux/regulator/axp15060.h diff --git a/arch/riscv/configs/starfive_jh7110_defconfig b/arch/riscv/configs/starfive_jh7110_defconfig index f6b9e80..49f83fd 100644 --- a/arch/riscv/configs/starfive_jh7110_defconfig +++ b/arch/riscv/configs/starfive_jh7110_defconfig @@ -177,6 +177,7 @@ CONFIG_WATCHDOG=y CONFIG_WATCHDOG_SYSFS=y CONFIG_STARFIVE_WATCHDOG=y CONFIG_REGULATOR=y +CONFIG_REGULATOR_AXP15060=y CONFIG_REGULATOR_STARFIVE_JH7110=y # CONFIG_MEDIA_CEC_SUPPORT is not set CONFIG_MEDIA_SUPPORT=y diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index b650eee..0bc8df0 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -178,6 +178,14 @@ config REGULATOR_ATC260X ATC260x PMICs. This will enable support for all the software controllable DCDC/LDO regulators. +config REGULATOR_AXP15060 + tristate "X-POWERS AXP15060 PMIC Regulators" + depends on I2C + select REGMAP_I2C + help + This driver provides support for the voltage regulators on the + AXP15060 PMIC. + config REGULATOR_AXP20X tristate "X-POWERS AXP20X PMIC Regulators" depends on MFD_AXP20X diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 3220029..927a16d 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_REGULATOR_ARM_SCMI) += scmi-regulator.o obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o obj-$(CONFIG_REGULATOR_ATC260X) += atc260x-regulator.o +obj-$(CONFIG_REGULATOR_AXP15060) += axp15060-regulator.o obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o obj-$(CONFIG_REGULATOR_BD71815) += bd71815-regulator.o diff --git a/drivers/regulator/axp15060-regulator.c b/drivers/regulator/axp15060-regulator.c new file mode 100644 index 0000000..e5d2b4c --- /dev/null +++ b/drivers/regulator/axp15060-regulator.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022 Starfive Technology Co., Ltd. + * Author: Kevin Xie + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AXP15060_ON_OFF_CTRL_1 0x10 +#define AXP15060_ON_OFF_CTRL_2 0x11 +#define AXP15060_ON_OFF_CTRL_3 0x12 +#define AXP15060_VOL_CTRL_DCDC_1 0x13 +#define AXP15060_VOL_CTRL_DCDC_2 0x14 +#define AXP15060_VOL_CTRL_DCDC_3 0x15 +#define AXP15060_VOL_CTRL_DCDC_4 0x16 +#define AXP15060_VOL_CTRL_DCDC_5 0x17 +#define AXP15060_VOL_CTRL_DCDC_6 0x18 +#define AXP15060_VOL_CTRL_ALDO_1 0x19 +#define AXP15060_DCDC_MODE_CTRL_1 0x1A +#define AXP15060_DCDC_MODE_CTRL_2 0x1B + +#define AXP15060_OUTPUT_MONITOR_OFF_DISCHARGE 0x1E +#define AXP15060_IRQ_PWROK_VOFF_SETTING 0x1F +#define AXP15060_VOL_CTRL_ALDO_2 0x20 +#define AXP15060_VOL_CTRL_ALDO_3 0x21 +#define AXP15060_VOL_CTRL_ALDO_4 0x22 +#define AXP15060_VOL_CTRL_ALDO_5 0x23 +#define AXP15060_VOL_CTRL_BLDO_1 0x24 +#define AXP15060_VOL_CTRL_BLDO_2 0x25 +#define AXP15060_VOL_CTRL_BLDO_3 0x26 +#define AXP15060_VOL_CTRL_BLDO_4 0x27 +#define AXP15060_VOL_CTRL_BLDO_5 0x28 +#define AXP15060_VOL_CTRL_CLDO_1 0x29 +#define AXP15060_VOL_CTRL_CLDO_2 0x2A +/* CLDO3 voltage ctrl and CLDO3/GPIO1/Wakeup ctrl */ +#define AXP15060_VOL_CTRL_CLDO_3 0x2B +#define AXP15060_CLDO_4_GPIO_2_CTRL 0x2C +#define AXP15060_VOL_CTRL_CLDO_4 0x2D +#define AXP15060_VOL_CTRL_CPUSLDO 0x2E + +#define AXP15060_PWR_WAKEUP_CTRL 0x31 +/* Power disable and power down sequence */ +#define AXP15060_PWR_DISABLE 0x32 + +#define AXP15060_POK_SETTING 0x36 + +#define AXP15060_INTERFACE_MODE_SEL 0x3E + +#define AXP15060_IRQ_ENABLE_1 0x40 +#define AXP15060_IRQ_ENABLE_2 0x41 + +#define AXP15060_IRQ_STATUS_1 0x48 +#define AXP15060_IRQ_STATUS_2 0x49 + +/* AXP15060_ON_OFF_CTRL_1 */ +#define AXP15060_PWR_OUT_DCDC1_MASK BIT(0) +#define AXP15060_PWR_OUT_DCDC2_MASK BIT(1) +#define AXP15060_PWR_OUT_DCDC3_MASK BIT(2) +#define AXP15060_PWR_OUT_DCDC4_MASK BIT(3) +#define AXP15060_PWR_OUT_DCDC5_MASK BIT(4) +#define AXP15060_PWR_OUT_DCDC6_MASK BIT(5) + +/* AXP15060_ON_OFF_CTRL_2 */ +#define AXP15060_PWR_OUT_ALDO1_MASK BIT(0) +#define AXP15060_PWR_OUT_ALDO2_MASK BIT(1) +#define AXP15060_PWR_OUT_ALDO3_MASK BIT(2) +#define AXP15060_PWR_OUT_ALDO4_MASK BIT(3) +#define AXP15060_PWR_OUT_ALDO5_MASK BIT(4) +#define AXP15060_PWR_OUT_BLDO1_MASK BIT(5) +#define AXP15060_PWR_OUT_BLDO2_MASK BIT(6) +#define AXP15060_PWR_OUT_BLDO3_MASK BIT(7) + +/* AXP15060_ON_OFF_CTRL_3 */ +#define AXP15060_PWR_OUT_BLDO4_MASK BIT(0) +#define AXP15060_PWR_OUT_BLDO5_MASK BIT(1) +#define AXP15060_PWR_OUT_CLDO1_MASK BIT(2) +#define AXP15060_PWR_OUT_CLDO2_MASK BIT(3) +#define AXP15060_PWR_OUT_CLDO3_MASK BIT(4) +#define AXP15060_PWR_OUT_CLDO4_MASK BIT(5) +#define AXP15060_PWR_OUT_CPULDO_MASK BIT(6) +#define AXP15060_PWR_OUT_SWITCH_MASK BIT(7) + +#define AXP15060_ALDO1_V_OUT_MASK GENMASK(4, 0) +#define AXP15060_ALDO2_V_OUT_MASK GENMASK(4, 0) +#define AXP15060_ALDO3_V_OUT_MASK GENMASK(4, 0) +#define AXP15060_ALDO4_V_OUT_MASK GENMASK(4, 0) +#define AXP15060_ALDO5_V_OUT_MASK GENMASK(4, 0) + +#define AXP15060_BLDO1_V_OUT_MASK GENMASK(4, 0) +#define AXP15060_BLDO2_V_OUT_MASK GENMASK(4, 0) +#define AXP15060_BLDO3_V_OUT_MASK GENMASK(4, 0) +#define AXP15060_BLDO4_V_OUT_MASK GENMASK(4, 0) +#define AXP15060_BLDO5_V_OUT_MASK GENMASK(4, 0) + +#define AXP15060_CLDO1_V_OUT_MASK GENMASK(4, 0) +#define AXP15060_CLDO2_V_OUT_MASK GENMASK(4, 0) +#define AXP15060_CLDO3_V_OUT_MASK GENMASK(4, 0) +#define AXP15060_CLDO4_V_OUT_MASK GENMASK(5, 0) +#define AXP15060_CPUSLDO_V_OUT_MASK GENMASK(3, 0) + +#define AXP15060_DCDC1_V_OUT_MASK GENMASK(4, 0) +/* DCDC2 bit7 is set by fw, which is different from datasheet desc. */ +#define AXP15060_DCDC2_V_OUT_MASK GENMASK(7, 0) +#define AXP15060_DCDC3_V_OUT_MASK GENMASK(6, 0) +#define AXP15060_DCDC4_V_OUT_MASK GENMASK(6, 0) +#define AXP15060_DCDC5_V_OUT_MASK GENMASK(6, 0) +#define AXP15060_DCDC6_V_OUT_MASK GENMASK(4, 0) + +#define AXP15060_DCDC2_V_OUT_RANGE1_7bit_500mV_START 0x00 +#define AXP15060_DCDC2_V_OUT_RANGE1_7bit_1200mV_END 0x46 +#define AXP15060_DCDC2_V_OUT_RANGE2_7bit_1220mV_START 0x47 +#define AXP15060_DCDC2_V_OUT_RANGE2_7bit_1540mV_END 0x57 +#define AXP15060_DCDC2_V_OUT_RANGE1_500mV_START 0x80 +#define AXP15060_DCDC2_V_OUT_RANGE1_1200mV_END 0xC6 +#define AXP15060_DCDC2_V_OUT_RANGE2_1220mV_START 0xC7 +#define AXP15060_DCDC2_V_OUT_RANGE2_1540mV_END 0xD7 +#define AXP15060_DCDC2_NUM_VOLTAGES \ + (AXP15060_DCDC2_V_OUT_RANGE2_1540mV_END + 1) + +static const struct linear_range axp15060_dcdc2_ranges[] = { + REGULATOR_LINEAR_RANGE(500000, + AXP15060_DCDC2_V_OUT_RANGE1_7bit_500mV_START, + AXP15060_DCDC2_V_OUT_RANGE1_7bit_1200mV_END, + 10000), + REGULATOR_LINEAR_RANGE(1220000, + AXP15060_DCDC2_V_OUT_RANGE2_7bit_1220mV_START, + AXP15060_DCDC2_V_OUT_RANGE2_7bit_1540mV_END, + 20000), + REGULATOR_LINEAR_RANGE(500000, + AXP15060_DCDC2_V_OUT_RANGE1_500mV_START, + AXP15060_DCDC2_V_OUT_RANGE1_1200mV_END, + 10000), + REGULATOR_LINEAR_RANGE(1220000, + AXP15060_DCDC2_V_OUT_RANGE2_1220mV_START, + AXP15060_DCDC2_V_OUT_RANGE2_1540mV_END, + 20000), +}; + +static const struct regmap_range axp15060_writeable_ranges[] = { + regmap_reg_range(AXP15060_ON_OFF_CTRL_1, AXP15060_DCDC_MODE_CTRL_2), + regmap_reg_range(AXP15060_OUTPUT_MONITOR_OFF_DISCHARGE, + AXP15060_VOL_CTRL_CPUSLDO), + regmap_reg_range(AXP15060_PWR_WAKEUP_CTRL, AXP15060_PWR_DISABLE), + regmap_reg_range(AXP15060_POK_SETTING, AXP15060_POK_SETTING), + regmap_reg_range(AXP15060_INTERFACE_MODE_SEL, AXP15060_INTERFACE_MODE_SEL), + regmap_reg_range(AXP15060_IRQ_ENABLE_1, AXP15060_IRQ_ENABLE_2), + regmap_reg_range(AXP15060_IRQ_STATUS_1, AXP15060_IRQ_STATUS_2), +}; + +static const struct regmap_access_table axp15060_writeable_table = { + .yes_ranges = axp15060_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(axp15060_writeable_ranges), +}; + +static const struct regmap_config axp15060_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .wr_table = &axp15060_writeable_table, + .max_register = AXP15060_IRQ_STATUS_2, + .cache_type = REGCACHE_NONE, +}; + +static const struct regulator_ops axp15060_fixed_ops = { + .list_voltage = regulator_list_voltage_linear, +}; + +static const struct regulator_ops axp15060_sw_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +static const struct regulator_ops axp15060_ops = { + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +static const struct regulator_ops axp15060_range_ops = { + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .list_voltage = regulator_list_voltage_linear_range, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +#define AXP15060_DESC_FIXED(_id, _match, _volt) \ + { \ + .name = (_match), \ + .of_match = of_match_ptr(_match), \ + .regulators_node = of_match_ptr("regulators"), \ + .type = REGULATOR_VOLTAGE, \ + .id = AXP15060_ID_##_id, \ + .n_voltages = 1, \ + .owner = THIS_MODULE, \ + .min_uV = (_volt) * 1000, \ + .ops = &axp15060_fixed_ops, \ + } + +#define AXP_DESC_SW(_id, _match, _ereg, _emask) \ + { \ + .name = (_match), \ + .of_match = of_match_ptr(_match), \ + .regulators_node = of_match_ptr("regulators"), \ + .type = REGULATOR_VOLTAGE, \ + .id = AXP15060_ID_##_id, \ + .owner = THIS_MODULE, \ + .enable_reg = (_ereg), \ + .enable_mask = (_emask), \ + .ops = &axp15060_sw_ops, \ + } + +#define AXP15060_DESC(_id, _match, _min, _max, _step, _vreg, \ + _vmask, _ereg, _emask) \ + { \ + .name = (_match), \ + .of_match = of_match_ptr(_match), \ + .regulators_node = of_match_ptr("regulators"), \ + .type = REGULATOR_VOLTAGE, \ + .id = AXP15060_ID_##_id, \ + .n_voltages = (((_max) - (_min)) / (_step) + 1), \ + .owner = THIS_MODULE, \ + .min_uV = (_min) * 1000, \ + .uV_step = (_step) * 1000, \ + .vsel_reg = (_vreg), \ + .vsel_mask = (_vmask), \ + .enable_reg = (_ereg), \ + .enable_mask = (_emask), \ + .ops = &axp15060_ops, \ + } + +#define AXP15060_DESC_RANGES(_id, _match, _ranges, _n_voltages,\ + _vreg, _vmask, _ereg, _emask) \ + { \ + .name = (_match), \ + .of_match = of_match_ptr(_match), \ + .regulators_node = of_match_ptr("regulators"), \ + .type = REGULATOR_VOLTAGE, \ + .id = AXP15060_ID_##_id, \ + .n_voltages = (_n_voltages), \ + .owner = THIS_MODULE, \ + .vsel_reg = (_vreg), \ + .vsel_mask = (_vmask), \ + .enable_reg = (_ereg), \ + .enable_mask = (_emask), \ + .linear_ranges = (_ranges), \ + .n_linear_ranges = ARRAY_SIZE(_ranges), \ + .ops = &axp15060_range_ops, \ + } +/* Only register the regulators that needed to be controlled(onoff/vol) */ +static const struct regulator_desc axp15060_regulators[] = { + AXP15060_DESC(ALDO1, "mipi_0p9", 700, 3300, 100, + AXP15060_VOL_CTRL_ALDO_1, AXP15060_ALDO1_V_OUT_MASK, + AXP15060_ON_OFF_CTRL_2, AXP15060_PWR_OUT_ALDO1_MASK), + + AXP15060_DESC(ALDO3, "hdmi_1p8", 700, 3300, 100, + AXP15060_VOL_CTRL_ALDO_3, AXP15060_ALDO3_V_OUT_MASK, + AXP15060_ON_OFF_CTRL_2, AXP15060_PWR_OUT_ALDO3_MASK), + + AXP15060_DESC(ALDO4, "sdio_vdd", 700, 3300, 100, + AXP15060_VOL_CTRL_ALDO_4, AXP15060_ALDO4_V_OUT_MASK, + AXP15060_ON_OFF_CTRL_2, AXP15060_PWR_OUT_ALDO4_MASK), + + AXP15060_DESC(ALDO5, "hdmi_0p9", 700, 3300, 100, + AXP15060_VOL_CTRL_ALDO_5, AXP15060_ALDO5_V_OUT_MASK, + AXP15060_ON_OFF_CTRL_2, AXP15060_PWR_OUT_ALDO5_MASK), + + AXP15060_DESC_RANGES(DCDC2, "cpu_vdd_0p9", + axp15060_dcdc2_ranges, AXP15060_DCDC2_NUM_VOLTAGES, + AXP15060_VOL_CTRL_DCDC_2, AXP15060_DCDC2_V_OUT_MASK, + AXP15060_ON_OFF_CTRL_1, AXP15060_PWR_OUT_DCDC2_MASK), +}; + +static struct of_regulator_match axp15060_matches[] = { + { .name = "mipi_0p9", }, + { .name = "hdmi_1p8", }, + { .name = "sdio_vdd", }, + { .name = "hdmi_0p9", }, + { .name = "cpu_vdd_0p9", }, +}; + +static int axp15060_i2c_probe(struct i2c_client *i2c) +{ + struct regulator_config config = { }; + struct regulator_dev *rdev; + struct device_node *np, *regulators; + struct regmap *regmap; + int i, ret; + + np = of_node_get(i2c->dev.of_node); + if (!np) + return -EINVAL; + + regulators = of_get_child_by_name(np, "regulators"); + if (!regulators) { + dev_err(&i2c->dev, "Regulators node not found\n"); + return -EINVAL; + } + + i = of_regulator_match(&i2c->dev, regulators, axp15060_matches, + ARRAY_SIZE(axp15060_matches)); + of_node_put(i2c->dev.of_node); + if (i < 0) { + dev_err(&i2c->dev, "Failed to match regulators\n"); + return -EINVAL; + } + + regmap = devm_regmap_init_i2c(i2c, &axp15060_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", ret); + return ret; + } + + for (i = 0; i < AXP15060_MAX_REGULATORS; i++) { + config.dev = &i2c->dev; + config.regmap = regmap; + config.init_data = axp15060_matches[i].init_data; + + rdev = devm_regulator_register(&i2c->dev, + &axp15060_regulators[i], &config); + if (IS_ERR(rdev)) { + dev_err(&i2c->dev, + "Failed to register AXP15060 regulator\n"); + return PTR_ERR(rdev); + } + dev_info(&i2c->dev, "Register %s done! vol range:%d ~ %d mV\n", + rdev->desc->name, + (rdev->constraints->min_uV) / 1000, + (rdev->constraints->max_uV) / 1000); + } + + return 0; +} + +static const struct i2c_device_id axp15060_i2c_id[] = { + {"axp15060_reg", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, axp15060_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id axp15060_dt_ids[] = { + { .compatible = "stf,axp15060-regulator", + .data = &axp15060_i2c_id[0] }, + {}, +}; +MODULE_DEVICE_TABLE(of, axp15060_dt_ids); +#endif + +static struct i2c_driver axp15060_regulator_driver = { + .driver = { + .name = "axp15060-regulator", + .of_match_table = of_match_ptr(axp15060_dt_ids), + }, + .probe_new = axp15060_i2c_probe, + .id_table = axp15060_i2c_id, +}; + +module_i2c_driver(axp15060_regulator_driver); + +MODULE_AUTHOR("Kevin Xie "); +MODULE_DESCRIPTION("Regulator device driver for X-Powers AXP15060"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/regulator/axp15060.h b/include/linux/regulator/axp15060.h new file mode 100644 index 0000000..8456a1a --- /dev/null +++ b/include/linux/regulator/axp15060.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 Starfive Technology Co., Ltd. + * Author: Mason Huo + */ + +#ifndef __LINUX_REGULATOR_AXP15060_H +#define __LINUX_REGULATOR_AXP15060_H + +/* Hide useless or always-on regulators */ +/* +enum axp15060_reg_id { + AXP15060_ID_ALDO1 = 0, + AXP15060_ID_ALDO2, + AXP15060_ID_ALDO3, + AXP15060_ID_ALDO4, + AXP15060_ID_ALDO5, + AXP15060_ID_BLDO1, + AXP15060_ID_BLDO2, + AXP15060_ID_BLDO3, + AXP15060_ID_BLDO4, + AXP15060_ID_BLDO5, + AXP15060_ID_CLDO1, + AXP15060_ID_CLDO2, + AXP15060_ID_CLDO3, + AXP15060_ID_CLDO4, + AXP15060_ID_CPUSLDO, + AXP15060_ID_DCDC1, + AXP15060_ID_DCDC2, + AXP15060_ID_DCDC3, + AXP15060_ID_DCDC4, + AXP15060_ID_DCDC5, + AXP15060_ID_DCDC6, + AXP15060_MAX_REGULATORS, +}; +*/ + +enum axp15060_reg_id { + AXP15060_ID_ALDO1 = 0, + AXP15060_ID_ALDO3, + AXP15060_ID_ALDO4, + AXP15060_ID_ALDO5, + AXP15060_ID_DCDC2, + AXP15060_MAX_REGULATORS, +}; + +#endif /* __LINUX_REGULATOR_AXP15060_H */ -- 2.7.4