From 11846b45bd90d27fa7b07d329cef2a128504a231 Mon Sep 17 00:00:00 2001 From: Guochun Huang Date: Fri, 30 Oct 2020 15:48:23 +0800 Subject: [PATCH] regulator: DIO5632: add regulator driver for DIO5632 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The DIO5632 is designed to support general positive/negative driven applications. The DIO5632 is primarily intended to supplying TFT LCD displays, can be used for any application that requires positive and negative supplies, ranging from ±4V to ±6V and current up to 80mA. DIO5632 regulator driver supports to enable/disable and set voltage on its output. Signed-off-by: Guochun Huang Change-Id: I0746197fff9368e6c42cb6777a658ea0900e25bd --- .../bindings/regulator/dio5632-regulator.txt | 39 +++ drivers/regulator/Kconfig | 8 + drivers/regulator/Makefile | 1 + drivers/regulator/dio5632-regulator.c | 239 ++++++++++++++++++ 4 files changed, 287 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/dio5632-regulator.txt create mode 100644 drivers/regulator/dio5632-regulator.c diff --git a/Documentation/devicetree/bindings/regulator/dio5632-regulator.txt b/Documentation/devicetree/bindings/regulator/dio5632-regulator.txt new file mode 100644 index 000000000000..d83d98a40514 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/dio5632-regulator.txt @@ -0,0 +1,39 @@ +DIO5632 regulators + +Required properties: +- compatible: "DIO5632" +- reg: I2C slave address + +Optional Subnode: +Device supports two regulators OUTP and OUTN. A sub node within the + device node describe the properties of these regulators. The sub-node + names must be as follows: + -For regulator outp, the sub node name should be "outp". + -For regulator outn, the sub node name should be "outn". + +-enable-gpios:(active high, output) Regulators are controlled by the input pins. + If it is connected to GPIO through host system then provide the + gpio number as per gpio.txt. + +Each regulator is defined using the standard binding for regulators. + +Example: + + dio5632@3e { + compatible = "DIO5632"; + reg = <0x3e>; + + outp { + regulator-name = "outp"; + regulator-boot-on; + regulator-always-on; + enable-gpios = <&gpio0 RK_PB7 GPIO_ACTIVE_HIGH>; + }; + + outn { + regulator-name = "outn"; + regulator-boot-on; + regulator-always-on; + enable-gpios = <&gpio0 RK_PC0 GPIO_ACTIVE_HIGH>; + }; + }; diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 1039a4a2975b..93a8ad290f49 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1048,5 +1048,13 @@ config REGULATOR_XZ3216 help Support the voltage and current regulators of the XZ321X series of DCDC devices. +config REGULATOR_DIO5632 + tristate "DIO5632 Dual Output Power regulators" + depends on I2C && GPIOLIB + select REGMAP_I2C + help + This driver supports DIO5632 single inductor - dual output + power supply specifcally designed for display panels. + endif diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 6adb3be5faaf..657ad8567026 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -133,5 +133,6 @@ obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o obj-$(CONFIG_REGULATOR_XZ3216) += xz3216.o +obj-$(CONFIG_REGULATOR_DIO5632) += dio5632-regulator.o ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG diff --git a/drivers/regulator/dio5632-regulator.c b/drivers/regulator/dio5632-regulator.c new file mode 100644 index 000000000000..d2183185e3fb --- /dev/null +++ b/drivers/regulator/dio5632-regulator.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020 Rockchip Electronics Co. Ltd. + * + * Author: Guochun Huang + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DIO5632_REG_VPOS 0x00 +#define DIO5632_REG_VNEG 0x01 +#define DIO5632_REG_APPS_DISP_DISN 0x03 +#define DIO5632_REG_CONTROL 0x0FF + +#define DIO5632_VOUT_MASK 0x1F +#define DIO5632_VOUT_N_VOLTAGE 0x15 +#define DIO5632_VOUT_VMIN 4000000 +#define DIO5632_VOUT_VMAX 6000000 +#define DIO5632_VOUT_STEP 100000 + +#define DIO5632_REG_DIS_VPOS BIT(1) +#define DIO5632_REG_DIS_VNEG BIT(0) + +#define DIO5632_REGULATOR_ID_VPOS 0 +#define DIO5632_REGULATOR_ID_VNEG 1 +#define DIO5632_MAX_REGULATORS 2 + +#define DIO5632_ACT_DIS_TIME_SLACK 1000 + +struct DIO5632_reg_pdata { + struct gpio_desc *en_gpiod; + int ena_gpio_state; +}; + +struct DIO5632_regulator { + struct device *dev; + struct DIO5632_reg_pdata reg_pdata[DIO5632_MAX_REGULATORS]; +}; + +static int DIO5632_regulator_enable(struct regulator_dev *rdev) +{ + struct DIO5632_regulator *dio = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + struct DIO5632_reg_pdata *rpdata = &dio->reg_pdata[id]; + int ret; + + if (!IS_ERR(rpdata->en_gpiod)) { + gpiod_set_value_cansleep(rpdata->en_gpiod, 1); + rpdata->ena_gpio_state = 1; + } + + /* Hardware automatically enable discharge bit in enable */ + if (rdev->constraints->active_discharge == + REGULATOR_ACTIVE_DISCHARGE_DISABLE) { + ret = regulator_set_active_discharge_regmap(rdev, false); + if (ret < 0) { + dev_err(dio->dev, "Failed to disable active discharge: %d\n", + ret); + return ret; + } + } + + return 0; +} + +static int DIO5632_regulator_disable(struct regulator_dev *rdev) +{ + struct DIO5632_regulator *dio = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + struct DIO5632_reg_pdata *rpdata = &dio->reg_pdata[id]; + + if (!IS_ERR(rpdata->en_gpiod)) { + gpiod_set_value_cansleep(rpdata->en_gpiod, 0); + rpdata->ena_gpio_state = 0; + } + + return 0; +} + +static int DIO5632_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct DIO5632_regulator *dio = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + struct DIO5632_reg_pdata *rpdata = &dio->reg_pdata[id]; + + if (!IS_ERR(rpdata->en_gpiod)) + return rpdata->ena_gpio_state; + + return 1; +} + +static const struct regulator_ops DIO5632_regulator_ops = { + .enable = DIO5632_regulator_enable, + .disable = DIO5632_regulator_disable, + .is_enabled = DIO5632_regulator_is_enabled, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_active_discharge = regulator_set_active_discharge_regmap, +}; + +static int DIO5632_of_parse_cb(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + struct DIO5632_regulator *dio = config->driver_data; + struct DIO5632_reg_pdata *rpdata = &dio->reg_pdata[desc->id]; + int ret; + + rpdata->en_gpiod = devm_fwnode_get_index_gpiod_from_child(dio->dev, + "enable", 0, &np->fwnode, + GPIOD_OUT_HIGH, "enable"); + if (IS_ERR_OR_NULL(rpdata->en_gpiod)) { + ret = PTR_ERR(rpdata->en_gpiod); + + /* Ignore the error other than probe defer */ + if (ret == -EPROBE_DEFER) + return ret; + return 0; + } + + return 0; +} + +#define DIO5632_REGULATOR_DESC(_id, _name) \ + [DIO5632_REGULATOR_ID_##_id] = { \ + .name = "DIO5632-"#_name, \ + .supply_name = "vin", \ + .id = DIO5632_REGULATOR_ID_##_id, \ + .of_match = of_match_ptr(#_name), \ + .of_parse_cb = DIO5632_of_parse_cb, \ + .ops = &DIO5632_regulator_ops, \ + .n_voltages = DIO5632_VOUT_N_VOLTAGE, \ + .min_uV = DIO5632_VOUT_VMIN, \ + .uV_step = DIO5632_VOUT_STEP, \ + .enable_time = 500, \ + .vsel_mask = DIO5632_VOUT_MASK, \ + .vsel_reg = DIO5632_REG_##_id, \ + .active_discharge_off = 0, \ + .active_discharge_on = DIO5632_REG_DIS_##_id, \ + .active_discharge_mask = DIO5632_REG_DIS_##_id, \ + .active_discharge_reg = DIO5632_REG_APPS_DISP_DISN, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + } + +static const struct regulator_desc dio_regs_desc[DIO5632_MAX_REGULATORS] = { + DIO5632_REGULATOR_DESC(VPOS, outp), + DIO5632_REGULATOR_DESC(VNEG, outn), +}; + +static const struct regmap_range DIO5632_no_reg_ranges[] = { + regmap_reg_range(DIO5632_REG_APPS_DISP_DISN + 1, + DIO5632_REG_CONTROL - 1), +}; + +static const struct regmap_access_table DIO5632_no_reg_table = { + .no_ranges = DIO5632_no_reg_ranges, + .n_no_ranges = ARRAY_SIZE(DIO5632_no_reg_ranges), +}; + +static const struct regmap_config DIO5632_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = DIO5632_REG_CONTROL, + .cache_type = REGCACHE_NONE, + .rd_table = &DIO5632_no_reg_table, + .wr_table = &DIO5632_no_reg_table, +}; + +static int DIO5632_probe(struct i2c_client *client, + const struct i2c_device_id *client_id) +{ + struct device *dev = &client->dev; + struct DIO5632_regulator *dio; + struct regulator_dev *rdev; + struct regmap *rmap; + struct regulator_config config = { }; + int id; + int ret; + + dio = devm_kzalloc(dev, sizeof(*dio), GFP_KERNEL); + if (!dio) + return -ENOMEM; + + rmap = devm_regmap_init_i2c(client, &DIO5632_regmap_config); + if (IS_ERR(rmap)) { + ret = PTR_ERR(rmap); + dev_err(dev, "regmap init failed: %d\n", ret); + return ret; + } + + i2c_set_clientdata(client, dio); + dio->dev = dev; + + for (id = 0; id < DIO5632_MAX_REGULATORS; ++id) { + config.regmap = rmap; + config.dev = dev; + config.driver_data = dio; + + rdev = devm_regulator_register(dev, &dio_regs_desc[id], + &config); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); + dev_err(dev, "regulator %s register failed: %d\n", + dio_regs_desc[id].name, ret); + return ret; + } + } + return 0; +} + +static const struct i2c_device_id DIO5632_id[] = { + {.name = "DIO5632",}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, DIO5632_id); + +static struct i2c_driver DIO5632_i2c_driver = { + .driver = { + .name = "DIO5632", + }, + .probe = DIO5632_probe, + .id_table = DIO5632_id, +}; + +module_i2c_driver(DIO5632_i2c_driver); + +MODULE_DESCRIPTION("DIO5632 regulator driver"); +MODULE_AUTHOR("Guochun Huang "); +MODULE_LICENSE("GPL v2");