Messages in this thread Patch in this message | | | From | Dzmitry Sankouski <> | Subject | [PATCH 2/6] regulator: s2dos05: add Samsung s2dos05 driver | Date | Wed, 18 Oct 2023 15:30:29 +0300 |
| |
Add driver for s2dos05 regulator. The s2dos05 is a companion power management IC for the smart phones, and provides LDOs[1~4] and BUCKs[1].
Signed-off-by: Dzmitry Sankouski <dsankouski@gmail.com>
---
drivers/regulator/Kconfig | 8 + drivers/regulator/Makefile | 1 + drivers/regulator/s2dos05.c | 601 ++++++++++++++++++++++++++++++ include/linux/regulator/s2dos05.h | 173 +++++++++ 4 files changed, 783 insertions(+) create mode 100644 drivers/regulator/s2dos05.c create mode 100644 include/linux/regulator/s2dos05.h
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 823f8e6e4801..ec18439edc6a 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1232,6 +1232,14 @@ config REGULATOR_RTQ6752 synchronous boost converters for PAVDD, and one synchronous NAVDD buck-boost. This device is suitable for automotive TFT-LCD panel. +config REGULATOR_S2DOS05 + tristate "SLSI S2DOS05 regulator" + depends on I2C + help + This driver provides support for the voltage regulators of the S2DOS05. + The S2DOS05 is a companion power management IC for the smart phones. + The S2DOS05 handles LDO and BUCK control + config REGULATOR_S2MPA01 tristate "Samsung S2MPA01 voltage regulator" depends on MFD_SEC_CORE || COMPILE_TEST diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 15e0d614ff66..b59f729b7af7 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -145,6 +145,7 @@ obj-$(CONFIG_REGULATOR_RT6245) += rt6245-regulator.o obj-$(CONFIG_REGULATOR_RTMV20) += rtmv20-regulator.o obj-$(CONFIG_REGULATOR_RTQ2134) += rtq2134-regulator.o obj-$(CONFIG_REGULATOR_RTQ6752) += rtq6752-regulator.o +obj-$(CONFIG_REGULATOR_S2DOS05) += s2dos05.o obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o diff --git a/drivers/regulator/s2dos05.c b/drivers/regulator/s2dos05.c new file mode 100644 index 000000000000..01b8b9834289 --- /dev/null +++ b/drivers/regulator/s2dos05.c @@ -0,0 +1,601 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * s2dos05.c - Regulator driver for the Samsung s2dos05 + * + * Copyright (C) 2016 Samsung Electronics + * Copyright (C) 2023 Dzmitry Sankouski <dsankouski@gmail.com> + * + */ + +#include <linux/module.h> +#include <linux/bug.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/of_gpio.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/s2dos05.h> +#include <linux/i2c.h> +#include <linux/regulator/of_regulator.h> +#include <linux/debugfs.h> + +struct s2dos05_data { + struct s2dos05_dev *iodev; + int num_regulators; + struct regulator_dev *rdev[S2DOS05_REGULATOR_MAX]; + int opmode[S2DOS05_REGULATOR_MAX]; +}; + +int s2dos05_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest) +{ + struct s2dos05_dev *s2dos05 = i2c_get_clientdata(i2c); + int ret; + + mutex_lock(&s2dos05->i2c_lock); + ret = i2c_smbus_read_byte_data(i2c, reg); + mutex_unlock(&s2dos05->i2c_lock); + if (ret < 0) { + pr_info("%s:%s reg(0x%x), ret(%d)\n", + MFD_DEV_NAME, __func__, reg, ret); + return ret; + } + + ret &= 0xff; + *dest = ret; + return 0; +} +EXPORT_SYMBOL_GPL(s2dos05_read_reg); + +int s2dos05_write_reg(struct i2c_client *i2c, u8 reg, u8 value) +{ + struct s2dos05_dev *s2dos05 = i2c_get_clientdata(i2c); + int ret; + + mutex_lock(&s2dos05->i2c_lock); + ret = i2c_smbus_write_byte_data(i2c, reg, value); + mutex_unlock(&s2dos05->i2c_lock); + if (ret < 0) + pr_info("%s:%s reg(0x%x), ret(%d)\n", + MFD_DEV_NAME, __func__, reg, ret); + + return ret; +} + +int s2dos05_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask) +{ + struct s2dos05_dev *s2dos05 = i2c_get_clientdata(i2c); + int ret; + u8 old_val, new_val; + + mutex_lock(&s2dos05->i2c_lock); + ret = i2c_smbus_read_byte_data(i2c, reg); + if (ret >= 0) { + old_val = ret & 0xff; + new_val = (val & mask) | (old_val & (~mask)); + ret = i2c_smbus_write_byte_data(i2c, reg, new_val); + } + mutex_unlock(&s2dos05->i2c_lock); + return ret; +} + +int s2dos05_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf) +{ + struct s2dos05_dev *s2dos05 = i2c_get_clientdata(i2c); + int ret; + + mutex_lock(&s2dos05->i2c_lock); + ret = i2c_smbus_read_i2c_block_data(i2c, reg, count, buf); + mutex_unlock(&s2dos05->i2c_lock); + if (ret < 0) + return ret; + + return 0; +} + +int s2dos05_read_word(struct i2c_client *i2c, u8 reg) +{ + struct s2dos05_dev *s2dos05 = i2c_get_clientdata(i2c); + int ret; + + mutex_lock(&s2dos05->i2c_lock); + ret = i2c_smbus_read_word_data(i2c, reg); + mutex_unlock(&s2dos05->i2c_lock); + if (ret < 0) + return ret; + + return ret; +} + +int s2dos05_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf) +{ + struct s2dos05_dev *s2dos05 = i2c_get_clientdata(i2c); + int ret; + + mutex_lock(&s2dos05->i2c_lock); + ret = i2c_smbus_write_i2c_block_data(i2c, reg, count, buf); + mutex_unlock(&s2dos05->i2c_lock); + if (ret < 0) + return ret; + + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static int s2dos05_regdump_show(struct seq_file *s, void *unused) +{ + struct s2dos05_dev *s2dos05_dev = s->private; + u8 i, val = 0; + + for (i = S2DOS05_REG_STAT; i <= S2DOS05_REG_IRQ; i++) { + s2dos05_read_reg(s2dos05_dev->i2c, i, &val); + seq_printf(s, "0x%x: 0x%x\n", i, val); + } + + return 0; +} + +static int s2dos05_regdump_open(struct inode *inode, struct file *file) +{ + return single_open(file, s2dos05_regdump_show, inode->i_private); +} + +static const struct file_operations s2dos05_regdump_operations = { + .open = s2dos05_regdump_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void s2dos05_debugfs_init(struct s2dos05_dev *s2dos05) +{ + debugfs_create_file("s2dos05_regdump", 0440, + NULL, s2dos05, &s2dos05_regdump_operations); +} +#endif + +static int s2m_enable(struct regulator_dev *rdev) +{ + struct s2dos05_data *info = rdev_get_drvdata(rdev); + struct i2c_client *i2c = info->iodev->i2c; + + return s2dos05_update_reg(i2c, rdev->desc->enable_reg, + info->opmode[rdev_get_id(rdev)], + rdev->desc->enable_mask); +} + +static int s2m_disable_regmap(struct regulator_dev *rdev) +{ + struct s2dos05_data *info = rdev_get_drvdata(rdev); + struct i2c_client *i2c = info->iodev->i2c; + u8 val; + + if (rdev->desc->enable_is_inverted) + val = rdev->desc->enable_mask; + else + val = 0; + + return s2dos05_update_reg(i2c, rdev->desc->enable_reg, + val, rdev->desc->enable_mask); +} + +static int s2m_is_enabled_regmap(struct regulator_dev *rdev) +{ + struct s2dos05_data *info = rdev_get_drvdata(rdev); + struct i2c_client *i2c = info->iodev->i2c; + int ret; + u8 val; + + ret = s2dos05_read_reg(i2c, rdev->desc->enable_reg, &val); + if (ret < 0) + return ret; + + if (rdev->desc->enable_is_inverted) + return (val & rdev->desc->enable_mask) == 0; + else + return (val & rdev->desc->enable_mask) != 0; +} + +static int s2m_get_voltage_sel_regmap(struct regulator_dev *rdev) +{ + struct s2dos05_data *info = rdev_get_drvdata(rdev); + struct i2c_client *i2c = info->iodev->i2c; + int ret; + u8 val; + + ret = s2dos05_read_reg(i2c, rdev->desc->vsel_reg, &val); + if (ret < 0) + return ret; + + val &= rdev->desc->vsel_mask; + + return val; +} + +static int s2m_set_voltage_sel_regmap(struct regulator_dev *rdev, + unsigned int sel) +{ + struct s2dos05_data *info = rdev_get_drvdata(rdev); + struct i2c_client *i2c = info->iodev->i2c; + int ret; + + ret = s2dos05_update_reg(i2c, rdev->desc->vsel_reg, + sel, rdev->desc->vsel_mask); + if (ret < 0) + goto out; + + if (rdev->desc->apply_bit) + ret = s2dos05_update_reg(i2c, rdev->desc->apply_reg, + rdev->desc->apply_bit, + rdev->desc->apply_bit); + return ret; +out: + pr_warn("%s: failed to set voltage_sel_regmap\n", rdev->desc->name); + return ret; +} + +static int s2m_set_voltage_sel_regmap_buck(struct regulator_dev *rdev, + unsigned int sel) +{ + struct s2dos05_data *info = rdev_get_drvdata(rdev); + struct i2c_client *i2c = info->iodev->i2c; + int ret; + + ret = s2dos05_write_reg(i2c, rdev->desc->vsel_reg, sel); + if (ret < 0) + goto out; + + if (rdev->desc->apply_bit) + ret = s2dos05_update_reg(i2c, rdev->desc->apply_reg, + rdev->desc->apply_bit, + rdev->desc->apply_bit); + return ret; +out: + pr_warn("%s: failed to set voltage_sel_regmap\n", rdev->desc->name); + return ret; +} + +static int s2m_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, + unsigned int new_selector) +{ + int old_volt, new_volt; + + /* sanity check */ + if (!rdev->desc->ops->list_voltage) + return -EINVAL; + + old_volt = rdev->desc->ops->list_voltage(rdev, old_selector); + new_volt = rdev->desc->ops->list_voltage(rdev, new_selector); + + if (old_selector < new_selector) + return DIV_ROUND_UP(new_volt - old_volt, S2DOS05_RAMP_DELAY); + + return 0; +} + +static int s2m_set_active_discharge(struct regulator_dev *rdev, + bool enable) +{ + struct s2dos05_data *info = rdev_get_drvdata(rdev); + struct i2c_client *i2c = info->iodev->i2c; + int ret; + u8 val; + + if (enable) + val = rdev->desc->active_discharge_on; + else + val = rdev->desc->active_discharge_off; + + ret = s2dos05_update_reg(i2c, rdev->desc->active_discharge_reg, + val, rdev->desc->active_discharge_mask); + return ret; +} + +static const struct regulator_ops s2dos05_ldo_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .is_enabled = s2m_is_enabled_regmap, + .enable = s2m_enable, + .disable = s2m_disable_regmap, + .get_voltage_sel = s2m_get_voltage_sel_regmap, + .set_voltage_sel = s2m_set_voltage_sel_regmap, + .set_voltage_time_sel = s2m_set_voltage_time_sel, + .set_active_discharge = s2m_set_active_discharge, +}; + +static const struct regulator_ops s2dos05_buck_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .is_enabled = s2m_is_enabled_regmap, + .enable = s2m_enable, + .disable = s2m_disable_regmap, + .get_voltage_sel = s2m_get_voltage_sel_regmap, + .set_voltage_sel = s2m_set_voltage_sel_regmap_buck, + .set_voltage_time_sel = s2m_set_voltage_time_sel, + .set_active_discharge = s2m_set_active_discharge, +}; + +#define _BUCK(macro) S2DOS05_BUCK##macro +#define _buck_ops(num) s2dos05_buck_ops##num + +#define _LDO(macro) S2DOS05_LDO##macro +#define _REG(ctrl) S2DOS05_REG##ctrl +#define _ldo_ops(num) s2dos05_ldo_ops##num +#define _MASK(macro) S2DOS05_ENABLE_MASK##macro +#define _TIME(macro) S2DOS05_ENABLE_TIME##macro + +#define BUCK_DESC(_name, _id, _ops, m, s, v, e, em, t, a) { \ + .name = _name, \ + .id = _id, \ + .ops = _ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = m, \ + .uV_step = s, \ + .n_voltages = S2DOS05_BUCK_N_VOLTAGES, \ + .vsel_reg = v, \ + .vsel_mask = S2DOS05_BUCK_VSEL_MASK, \ + .enable_reg = e, \ + .enable_mask = em, \ + .enable_time = t, \ + .active_discharge_off = 0, \ + .active_discharge_on = S2DOS05_BUCK_FD_MASK, \ + .active_discharge_reg = a, \ + .active_discharge_mask = S2DOS05_BUCK_FD_MASK \ +} + +#define LDO_DESC(_name, _id, _ops, m, s, v, e, em, t, a) { \ + .name = _name, \ + .id = _id, \ + .ops = _ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = m, \ + .uV_step = s, \ + .n_voltages = S2DOS05_LDO_N_VOLTAGES, \ + .vsel_reg = v, \ + .vsel_mask = S2DOS05_LDO_VSEL_MASK, \ + .enable_reg = e, \ + .enable_mask = em, \ + .enable_time = t, \ + .active_discharge_off = 0, \ + .active_discharge_on = S2DOS05_LDO_FD_MASK, \ + .active_discharge_reg = a, \ + .active_discharge_mask = S2DOS05_LDO_FD_MASK \ +} + +static struct regulator_desc regulators[S2DOS05_REGULATOR_MAX] = { + /* name, id, ops, min_uv, uV_step, vsel_reg, enable_reg */ + LDO_DESC("s2dos05-ldo1", _LDO(1), &_ldo_ops(), _LDO(_MIN1), + _LDO(_STEP1), _REG(_LDO1_CFG), + _REG(_EN), _MASK(_L1), _TIME(_LDO), _REG(_LDO1_CFG)), + LDO_DESC("s2dos05-ldo2", _LDO(2), &_ldo_ops(), _LDO(_MIN1), + _LDO(_STEP1), _REG(_LDO2_CFG), + _REG(_EN), _MASK(_L2), _TIME(_LDO), _REG(_LDO2_CFG)), + LDO_DESC("s2dos05-ldo3", _LDO(3), &_ldo_ops(), _LDO(_MIN2), + _LDO(_STEP1), _REG(_LDO3_CFG), + _REG(_EN), _MASK(_L3), _TIME(_LDO), _REG(_LDO3_CFG)), + LDO_DESC("s2dos05-ldo4", _LDO(4), &_ldo_ops(), _LDO(_MIN2), + _LDO(_STEP1), _REG(_LDO4_CFG), + _REG(_EN), _MASK(_L4), _TIME(_LDO), _REG(_LDO4_CFG)), + BUCK_DESC("s2dos05-buck1", _BUCK(1), &_buck_ops(), _BUCK(_MIN1), + _BUCK(_STEP1), _REG(_BUCK_VOUT), + _REG(_EN), _MASK(_B1), _TIME(_BUCK), _REG(_BUCK_CFG)), +}; + +#ifdef CONFIG_OF +static int s2dos05_pmic_dt_parse_pdata(struct device *dev, + struct s2dos05_platform_data *pdata) +{ + struct device_node *pmic_np, *regulators_np, *reg_np; + struct s2dos05_regulator_data *rdata; + unsigned int i; + + pmic_np = dev->of_node; + if (!pmic_np) { + dev_err(dev, "could not find pmic sub-node\n"); + return -ENODEV; + } + + pdata->dp_irq = of_get_named_gpio(pmic_np, "s2dos05,s2dos05_int", 0); + if (pdata->dp_irq < 0) + pr_err("%s error reading s2dos05_irq = %d\n", + __func__, pdata->dp_irq); + + pdata->wakeup = of_property_read_bool(pmic_np, "s2dos05,wakeup"); + + regulators_np = of_find_node_by_name(pmic_np, "regulators"); + if (!regulators_np) { + dev_err(dev, "could not find regulators sub-node\n"); + return -EINVAL; + } + + /* count the number of regulators to be supported in pmic */ + pdata->num_regulators = 0; + for_each_child_of_node(regulators_np, reg_np) { + pdata->num_regulators++; + } + + rdata = devm_kzalloc(dev, sizeof(*rdata) * + pdata->num_regulators, GFP_KERNEL); + if (!rdata) + return -ENOMEM; + + pdata->regulators = rdata; + for_each_child_of_node(regulators_np, reg_np) { + for (i = 0; i < ARRAY_SIZE(regulators); i++) + if (!of_node_cmp(reg_np->name, + regulators[i].name)) + break; + + if (i == ARRAY_SIZE(regulators)) { + dev_warn(dev, + "don't know how to configure regulator %s\n", + reg_np->name); + continue; + } + + rdata->id = i; + rdata->initdata = of_get_regulator_init_data( + dev, reg_np, + ®ulators[i]); + rdata->reg_node = reg_np; + rdata++; + } + of_node_put(regulators_np); + + return 0; +} +#else +static int s2dos05_pmic_dt_parse_pdata(struct s2dos05_dev *iodev, + struct s2dos05_platform_data *pdata) +{ + return 0; +} +#endif /* CONFIG_OF */ + +static int s2dos05_pmic_probe(struct i2c_client *i2c) +{ + struct s2dos05_dev *iodev; + struct device *dev = &i2c->dev; + struct s2dos05_platform_data *pdata = i2c->dev.platform_data; + struct regulator_config config = { }; + struct s2dos05_data *s2dos05; + int i; + int ret = 0; + + pr_info("%s:%s\n", MFD_DEV_NAME, __func__); + + iodev = kzalloc(sizeof(struct s2dos05_dev), GFP_KERNEL); + if (!iodev) + return -ENOMEM; + + if (i2c->dev.of_node) { + pdata = devm_kzalloc(&i2c->dev, + sizeof(struct s2dos05_platform_data), GFP_KERNEL); + if (!pdata) { + ret = -ENOMEM; + goto err_pdata; + } + ret = s2dos05_pmic_dt_parse_pdata(&i2c->dev, pdata); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to get device of_node\n"); + goto err_dt; + } + + i2c->dev.platform_data = pdata; + } else + pdata = i2c->dev.platform_data; + + iodev->dev = &i2c->dev; + iodev->i2c = i2c; + + if (pdata) { + iodev->pdata = pdata; + iodev->wakeup = pdata->wakeup; + } else { + ret = -EINVAL; + goto err_dt; + } + mutex_init(&iodev->i2c_lock); + i2c_set_clientdata(i2c, iodev); + + s2dos05 = devm_kzalloc(&i2c->dev, sizeof(struct s2dos05_data), + GFP_KERNEL); + if (!s2dos05) { + ret = -EINVAL; + goto err_data; + } + + s2dos05->iodev = iodev; + s2dos05->num_regulators = pdata->num_regulators; + + for (i = 0; i < pdata->num_regulators; i++) { + int id = pdata->regulators[i].id; + + config.dev = &i2c->dev; + config.init_data = pdata->regulators[i].initdata; + config.driver_data = s2dos05; + config.of_node = pdata->regulators[i].reg_node; + s2dos05->opmode[id] = regulators[id].enable_mask; + s2dos05->rdev[i] = regulator_register(dev, ®ulators[id], &config); + if (IS_ERR(s2dos05->rdev[i])) { + ret = PTR_ERR(s2dos05->rdev[i]); + dev_err(&i2c->dev, "regulator init failed for %d\n", + id); + s2dos05->rdev[i] = NULL; + goto err_rdata; + } + } + +#ifdef CONFIG_DEBUG_FS + s2dos05_debugfs_init(iodev); +#endif + + return ret; + +err_rdata: + pr_info("[%s:%d] err:\n", __FILE__, __LINE__); + for (i = 0; i < s2dos05->num_regulators; i++) + if (s2dos05->rdev[i]) + regulator_unregister(s2dos05->rdev[i]); +err_data: + mutex_destroy(&iodev->i2c_lock); + devm_kfree(&i2c->dev, (void *)s2dos05); +err_dt: + devm_kfree(&i2c->dev, (void *)pdata); +err_pdata: + kfree(iodev); + + return ret; +} + +static void s2dos05_pmic_remove(struct i2c_client *i2c) +{ + struct s2dos05_data *s2dos05 = i2c_get_clientdata(i2c); + int i; + + for (i = 0; i < s2dos05->num_regulators; i++) + if (s2dos05->rdev[i]) + regulator_unregister(s2dos05->rdev[i]); +} + +#if defined(CONFIG_OF) +static const struct of_device_id s2dos05_i2c_dt_ids[] = { + { .compatible = "samsung,s2dos05" }, + { }, +}; +MODULE_DEVICE_TABLE(of, s2dos05_i2c_dt_ids); +#endif /* CONFIG_OF */ + +#if defined(CONFIG_OF) +static const struct i2c_device_id s2dos05_pmic_id[] = { + {"s2dos05", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, s2dos05_pmic_id); +#endif /* CONFIG_OF */ + +static struct i2c_driver s2dos05_i2c_driver = { + .driver = { + .name = "s2dos05", +#if defined(CONFIG_OF) + .of_match_table = s2dos05_i2c_dt_ids, +#endif /* CONFIG_OF */ + .suppress_bind_attrs = true, + }, + .probe = s2dos05_pmic_probe, + .remove = s2dos05_pmic_remove, + .id_table = s2dos05_pmic_id, +}; +module_i2c_driver(s2dos05_i2c_driver); + +MODULE_DESCRIPTION("SAMSUNG s2dos05 Regulator Driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/regulator/s2dos05.h b/include/linux/regulator/s2dos05.h new file mode 100644 index 000000000000..e33e86257589 --- /dev/null +++ b/include/linux/regulator/s2dos05.h @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: GPL-2.0+ + * s2dos05.h + * + * Copyright (c) 2016 Samsung Electronics Co., Ltd + * http://www.samsung.com + * + */ + +#ifndef __LINUX_MFD_S2DOS05_H +#define __LINUX_MFD_S2DOS05_H +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define MFD_DEV_NAME "s2dos05" + +/** + * sec_regulator_data - regulator data + * @id: regulator id + * @initdata: regulator init data (constraints, supplies, ...) + */ + +struct s2dos05_dev { + struct device *dev; + struct i2c_client *i2c; /* 0xB2; PMIC, Flash LED */ + struct mutex i2c_lock; + + int type; + u8 rev_num; /* pmic Rev */ + bool wakeup; + int dp_irq; + int adc_mode; + int adc_sync_mode; + u8 adc_en_val; + + struct s2dos05_platform_data *pdata; +}; + +struct s2dos05_regulator_data { + int id; + struct regulator_init_data *initdata; + struct device_node *reg_node; +}; + +struct s2dos05_platform_data { + bool wakeup; + int num_regulators; + struct s2dos05_regulator_data *regulators; + int device_type; + int dp_irq; + + /* adc_mode + * 0 : not use + * 1 : current meter + * 2 : power meter + */ + int adc_mode; + /* 1 : sync mode, 2 : async mode */ + int adc_sync_mode; +}; + +struct s2dos05 { + struct regmap *regmap; +}; + +/* S2DOS05 registers */ +/* Slave Addr : 0xC0 */ +enum S2DOS05_reg { + S2DOS05_REG_DEV_ID, + S2DOS05_REG_TOPSYS_STAT, + S2DOS05_REG_STAT, + S2DOS05_REG_EN, + S2DOS05_REG_LDO1_CFG, + S2DOS05_REG_LDO2_CFG, + S2DOS05_REG_LDO3_CFG, + S2DOS05_REG_LDO4_CFG, + S2DOS05_REG_BUCK_CFG, + S2DOS05_REG_BUCK_VOUT, + S2DOS05_REG_IRQ_MASK = 0x0D, + S2DOS05_REG_SSD_TSD = 0x0E, + S2DOS05_REG_OCL = 0x10, + S2DOS05_REG_IRQ = 0x11 +}; + +/* S2DOS05 regulator ids */ +enum S2DOS05_regulators { + S2DOS05_LDO1, + S2DOS05_LDO2, + S2DOS05_LDO3, + S2DOS05_LDO4, + S2DOS05_BUCK1, + S2DOS05_REG_MAX, +}; + +#define S2DOS05_IRQ_PWRMT_MASK (1 << 5) +#define S2DOS05_IRQ_TSD_MASK (1 << 4) +#define S2DOS05_IRQ_SSD_MASK (1 << 3) +#define S2DOS05_IRQ_SCP_MASK (1 << 2) +#define S2DOS05_IRQ_UVLO_MASK (1 << 1) +#define S2DOS05_IRQ_OCD_MASK (1 << 0) + +#define S2DOS05_BUCK_MIN1 506250 +#define S2DOS05_LDO_MIN1 1500000 +#define S2DOS05_LDO_MIN2 2700000 +#define S2DOS05_BUCK_STEP1 6250 +#define S2DOS05_LDO_STEP1 25000 +#define S2DOS05_LDO_VSEL_MASK 0x7F +#define S2DOS05_LDO_FD_MASK 0x80 +#define S2DOS05_BUCK_VSEL_MASK 0xFF +#define S2DOS05_BUCK_FD_MASK 0x08 + +#define S2DOS05_ENABLE_MASK_L1 (1 << 0) +#define S2DOS05_ENABLE_MASK_L2 (1 << 1) +#define S2DOS05_ENABLE_MASK_L3 (1 << 2) +#define S2DOS05_ENABLE_MASK_L4 (1 << 3) +#define S2DOS05_ENABLE_MASK_B1 (1 << 4) + +#define S2DOS05_RAMP_DELAY 12000 + +#define S2DOS05_ENABLE_TIME_LDO 50 +#define S2DOS05_ENABLE_TIME_BUCK 350 + +#define S2DOS05_ENABLE_SHIFT 0x06 +#define S2DOS05_LDO_N_VOLTAGES (S2DOS05_LDO_VSEL_MASK + 1) +#define S2DOS05_BUCK_N_VOLTAGES (S2DOS05_BUCK_VSEL_MASK + 1) + +#define S2DOS05_PMIC_EN_SHIFT 6 +#define S2DOS05_REGULATOR_MAX (S2DOS05_REG_MAX) + +/* ----------power meter ----------*/ +#define S2DOS05_REG_PWRMT_CTRL1 0x0A +#define S2DOS05_REG_PWRMT_CTRL2 0x0B +#define S2DOS05_REG_PWRMT_DATA 0x0C +#define S2DOS05_REG_IRQ_MASK 0x0D + +#define CURRENT_ELVDD 2450 +#define CURRENT_ELVSS 2450 +#define CURRENT_AVDD 612 +#define CURRENT_BUCK 1220 +#define CURRENT_L1 2000 +#define CURRENT_L2 2000 +#define CURRENT_L3 2000 +#define CURRENT_L4 2000 + +#define POWER_ELVDD 24500 +#define POWER_ELVSS 24500 +#define POWER_AVDD 3060 +#define POWER_BUCK 1525 +#define POWER_L1 5000 +#define POWER_L2 5000 +#define POWER_L3 5000 +#define POWER_L4 5000 + +#define ADC_EN_MASK 0x80 +#define ADC_ASYNCRD_MASK 0x80 +#define ADC_PTR_MASK 0x0F +#define ADC_PGEN_MASK 0x30 +#define CURRENT_MODE 0x00 +#define POWER_MODE 0x10 +#define RAWCURRENT_MODE 0x20 +#define SMPNUM_MASK 0x0F + +#define S2DOS05_MAX_ADC_CHANNEL 8 + +void s2dos05_powermeter_init(struct s2dos05_dev *s2dos05); +void s2dos05_powermeter_deinit(struct s2dos05_dev *s2dos05); + +/* S2DOS05 shared i2c API function */ +int s2dos05_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest); +int s2dos05_write_reg(struct i2c_client *i2c, u8 reg, u8 value); +int s2dos05_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask); + + +#endif /* __LINUX_MFD_S2DOS05_H */ -- 2.39.2
| |