summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorHaoran.Wang <elven.wang@nxp.com>2018-02-05 16:26:29 +0800
committerJason Liu <jason.hui.liu@nxp.com>2019-02-12 10:31:35 +0800
commit2ec0f8ff9df2eff5c3548320a6e8b6bd403d359b (patch)
tree6e466110ee41cd8c7a85703688dcda5ade49e37d /drivers
parent5c587c56dc75877328e348c2fc45bf6a111b54c7 (diff)
MLK-18205-9 Support BD71837 PMIC chip on i.MX platforms
Signed-off-by: Haoran.Wang <elven.wang@nxp.com> Signed-off-by: Robin Gong <yibin.gong@nxp.com> Signed-off-by: Anson Huang <Anson.Huang@nxp.com> (cherry picked from commit 37f67d291e74a3428310cb5c98f556411042f810)
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mfd/Kconfig8
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/bd71837.c308
-rw-r--r--drivers/regulator/Kconfig6
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/bd71837-regulator.c1075
-rw-r--r--drivers/regulator/userspace-consumer.c53
-rw-r--r--drivers/regulator/virtual.c38
8 files changed, 1490 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 0ef00750353c..997a6172735e 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1810,6 +1810,14 @@ config MFD_STM32_TIMERS
for PWM and IIO Timer. This driver allow to share the
registers between the others drivers.
+config MFD_BD71837
+ bool "BD71837 Power Management chip"
+ depends on I2C=y
+ select MFD_CORE
+ help
+ if you say yes here you get support for the BD71837
+ Power Management chips.
+
menu "Multimedia Capabilities Port drivers"
depends on ARCH_SA1100
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 117939d091f7..c6755df735ba 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -231,3 +231,4 @@ obj-$(CONFIG_MFD_SUN4I_GPADC) += sun4i-gpadc.o
obj-$(CONFIG_MFD_STM32_LPTIMER) += stm32-lptimer.o
obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o
obj-$(CONFIG_MFD_MXS_LRADC) += mxs-lradc.o
+obj-$(CONFIG_MFD_BD71837) += bd71837.o
diff --git a/drivers/mfd/bd71837.c b/drivers/mfd/bd71837.c
new file mode 100644
index 000000000000..1f24d0784bb2
--- /dev/null
+++ b/drivers/mfd/bd71837.c
@@ -0,0 +1,308 @@
+/*
+ * @file bd71837.c -- ROHM BD71837MWV mfd driver
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * @author: cpham2403@gmail.com
+ * Copyright 2017.
+ */
+#define DEBUG
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/gpio.h>
+#include <linux/regmap.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/bd71837.h>
+
+/* Default enable debug message All Level */
+unsigned int bd71837_debug_mask = BD71837_DBG0;
+
+/** @brief bd71837 irq resource */
+static struct resource pmic_resources[] = {
+ // irq# 0
+ {
+ .start = BD71837_IRQ,
+ .end = BD71837_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/** @brief bd71837 multi function cells */
+static struct mfd_cell bd71837_mfd_cells[] = {
+ {
+ .name = "bd71837-pmic",
+ .num_resources = ARRAY_SIZE(pmic_resources),
+ .resources = &pmic_resources[0],
+ },
+};
+
+/** @brief bd71837 irqs */
+static const struct regmap_irq bd71837_irqs[] = {
+ [BD71837_IRQ] = {
+ .mask = BD71837_INT_MASK,
+ .reg_offset = 0,
+ },
+};
+
+/** @brief bd71837 irq chip definition */
+static struct regmap_irq_chip bd71837_irq_chip = {
+ .name = "bd71837",
+ .irqs = bd71837_irqs,
+ .num_irqs = ARRAY_SIZE(bd71837_irqs),
+ .num_regs = 1,
+ .irq_reg_stride = 1,
+ .status_base = BD71837_REG_IRQ,
+ .mask_base = BD71837_REG_MIRQ,
+ .mask_invert = true,
+ // .ack_base = BD71837_REG_IRQ,
+};
+
+/** @brief bd71837 irq initialize
+ * @param bd71837 bd71837 device to init
+ * @param bdinfo platform init data
+ * @retval 0 probe success
+ * @retval negative error number
+ */
+static int bd71837_irq_init(struct bd71837 *bd71837, struct bd71837_board *bdinfo)
+{
+ int irq;
+ int ret = 0;
+
+ if (!bdinfo) {
+ dev_warn(bd71837->dev, "No interrupt support, no pdata\n");
+ return -EINVAL;
+ }
+
+ dev_info(bd71837->dev, "gpio_intr = %d \n", bdinfo->gpio_intr);
+ irq = gpio_to_irq(bdinfo->gpio_intr);
+
+ bd71837->chip_irq = irq;
+ dev_info(bd71837->dev, "chip_irq=%d \n", bd71837->chip_irq);
+ ret = regmap_add_irq_chip(bd71837->regmap, bd71837->chip_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_FALLING, bdinfo->irq_base,
+ &bd71837_irq_chip, &bd71837->irq_data);
+ if (ret < 0) {
+ dev_warn(bd71837->dev, "Failed to add irq_chip %d\n", ret);
+ }
+ return ret;
+}
+
+/** @brief bd71837 irq initialize
+ * @param bd71837 bd71837 device to init
+ * @retval 0 probe success
+ * @retval negative error number
+ */
+static int bd71837_irq_exit(struct bd71837 *bd71837)
+{
+ if (bd71837->chip_irq > 0)
+ regmap_del_irq_chip(bd71837->chip_irq, bd71837->irq_data);
+ return 0;
+}
+
+/** @brief check whether volatile register
+ * @param dev kernel device pointer
+ * @param reg register index
+ */
+static bool is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ // struct bd71837 *bd71837 = dev_get_drvdata(dev);
+
+ /*
+ * Caching all regulator registers.
+ */
+ return true;
+}
+
+/** @brief regmap configures */
+static const struct regmap_config bd71837_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_reg = is_volatile_reg,
+ .max_register = BD71837_MAX_REGISTER - 1,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+#ifdef CONFIG_OF
+static struct of_device_id bd71837_of_match[] = {
+ { .compatible = "rohm,bd71837", .data = (void *)0},
+ { },
+};
+MODULE_DEVICE_TABLE(of, bd71837_of_match);
+
+
+/** @brief parse device tree data of bd71837
+ * @param client client object provided by system
+ * @param chip_id return chip id back to caller
+ * @return board initialize data
+ */
+static struct bd71837_board *bd71837_parse_dt(struct i2c_client *client,
+ int *chip_id)
+{
+ struct device_node *np = client->dev.of_node;
+ struct bd71837_board *board_info;
+ unsigned int prop;
+ const struct of_device_id *match;
+ int r = 0;
+
+ match = of_match_device(bd71837_of_match, &client->dev);
+ if (!match) {
+ dev_err(&client->dev, "Failed to find matching dt id\n");
+ return NULL;
+ }
+
+ chip_id = (int *)match->data;
+
+ board_info = devm_kzalloc(&client->dev, sizeof(*board_info),
+ GFP_KERNEL);
+ if (!board_info) {
+ dev_err(&client->dev, "Failed to allocate pdata\n");
+ return NULL;
+ }
+
+ board_info->gpio_intr = of_get_named_gpio(np, "gpio_intr", 0);
+ if (!gpio_is_valid(board_info->gpio_intr)) {
+ dev_err(&client->dev, "no pmic intr pin available\n");
+ goto err_intr;
+ }
+
+ r = of_property_read_u32(np, "irq_base", &prop);
+ if (!r) {
+ board_info->irq_base = prop;
+ } else {
+ board_info->irq_base = -1;
+ }
+
+ return board_info;
+
+err_intr:
+ devm_kfree(&client->dev, board_info);
+ return NULL;
+}
+#endif
+
+/** @brief probe bd71837 device
+ * @param i2c client object provided by system
+ * @param id chip id
+ * @retval 0 probe success
+ * @retval negative error number
+ */
+static int bd71837_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct bd71837 *bd71837;
+ struct bd71837_board *pmic_plat_data;
+ struct bd71837_board *of_pmic_plat_data = NULL;
+ int chip_id = id->driver_data;
+ int ret = 0;
+
+ pmic_plat_data = dev_get_platdata(&i2c->dev);
+
+ if (!pmic_plat_data && i2c->dev.of_node) {
+ pmic_plat_data = bd71837_parse_dt(i2c, &chip_id);
+ of_pmic_plat_data = pmic_plat_data;
+ }
+
+ if (!pmic_plat_data)
+ return -EINVAL;
+
+ bd71837 = kzalloc(sizeof(struct bd71837), GFP_KERNEL);
+ if (bd71837 == NULL)
+ return -ENOMEM;
+
+ bd71837->of_plat_data = of_pmic_plat_data;
+ i2c_set_clientdata(i2c, bd71837);
+ bd71837->dev = &i2c->dev;
+ bd71837->i2c_client = i2c;
+ bd71837->id = chip_id;
+ mutex_init(&bd71837->io_mutex);
+
+ bd71837->regmap = devm_regmap_init_i2c(i2c, &bd71837_regmap_config);
+ if (IS_ERR(bd71837->regmap)) {
+ ret = PTR_ERR(bd71837->regmap);
+ dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = bd71837_reg_read(bd71837, BD71837_REG_REV);
+ if (ret < 0) {
+ dev_err(bd71837->dev, "%s(): Read BD71837_REG_DEVICE failed!\n", __func__);
+ goto err;
+ }
+ dev_info(bd71837->dev, "Device ID=0x%X\n", ret);
+
+ bd71837_irq_init(bd71837, of_pmic_plat_data);
+
+ ret = mfd_add_devices(bd71837->dev, -1,
+ bd71837_mfd_cells, ARRAY_SIZE(bd71837_mfd_cells),
+ NULL, 0,
+ regmap_irq_get_domain(bd71837->irq_data));
+ if (ret < 0)
+ goto err;
+
+ return ret;
+
+err:
+ mfd_remove_devices(bd71837->dev);
+ kfree(bd71837);
+ return ret;
+}
+
+/** @brief remove bd71837 device
+ * @param i2c client object provided by system
+ * @return 0
+ */
+static int bd71837_i2c_remove(struct i2c_client *i2c)
+{
+ struct bd71837 *bd71837 = i2c_get_clientdata(i2c);
+
+ bd71837_irq_exit(bd71837);
+ mfd_remove_devices(bd71837->dev);
+ kfree(bd71837);
+
+ return 0;
+}
+
+static const struct i2c_device_id bd71837_i2c_id[] = {
+ { "bd71837", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, bd71837_i2c_id);
+
+static struct i2c_driver bd71837_i2c_driver = {
+ .driver = {
+ .name = "bd71837",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(bd71837_of_match),
+ },
+ .probe = bd71837_i2c_probe,
+ .remove = bd71837_i2c_remove,
+ .id_table = bd71837_i2c_id,
+};
+
+static int __init bd71837_i2c_init(void)
+{
+ return i2c_add_driver(&bd71837_i2c_driver);
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(bd71837_i2c_init);
+
+static void __exit bd71837_i2c_exit(void)
+{
+ i2c_del_driver(&bd71837_i2c_driver);
+}
+module_exit(bd71837_i2c_exit);
+
+MODULE_AUTHOR("Cong Pham <cpham2403@gmail.com>");
+MODULE_DESCRIPTION("BD71837 chip multi-function driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 926e3140db68..5361947ea726 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -977,5 +977,11 @@ config REGULATOR_WM8994
This driver provides support for the voltage regulators on the
WM8994 CODEC.
+config REGULATOR_BD71837
+ tristate "RoHM BD71837 Power Regulator"
+ depends on MFD_BD71837
+ help
+ This driver supports BD71837 voltage regulator chips.
+
endif
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 916c69587332..1bddbefbc8e7 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -125,6 +125,7 @@ obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o
obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o
obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o
+obj-$(CONFIG_REGULATOR_BD71837) += bd71837-regulator.o
ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
diff --git a/drivers/regulator/bd71837-regulator.c b/drivers/regulator/bd71837-regulator.c
new file mode 100644
index 000000000000..4b04fb1f221f
--- /dev/null
+++ b/drivers/regulator/bd71837-regulator.c
@@ -0,0 +1,1075 @@
+/*
+ * @file bd71837-regulator.c ROHM BD71837MWV regulator driver
+ *
+ * @author: cpham2403@gmail.com
+ * Copyright 2017.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#define DEBUG
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/mfd/bd71837.h>
+#include <linux/regulator/of_regulator.h>
+
+#define BD71837_DVS_BUCK_NUM 4 /* Buck 1/2/3/4 support DVS */
+#define BD71837_DVS_RUN_IDLE_SUSP 3
+#define BD71837_DVS_RUN_IDLE 2
+#define BD71837_DVS_RUN 1
+
+struct bd71837_buck_dvs {
+ u32 voltage[BD71837_DVS_RUN_IDLE_SUSP];
+};
+
+/** @brief bd71837 regulator type */
+struct bd71837_pmic {
+ struct regulator_desc descs[BD71837_REGULATOR_CNT]; /**< regulator description to system */
+ struct bd71837 *mfd; /**< parent device */
+ struct device *dev; /**< regulator kernel device */
+ struct regulator_dev *rdev[BD71837_REGULATOR_CNT]; /**< regulator device of system */
+ struct bd71837_buck_dvs buck_dvs[BD71837_DVS_BUCK_NUM]; /**< buck1/2 dvs */
+ int reg_index;
+};
+
+/*
+ * BUCK1/2/3/4
+ * BUCK1RAMPRATE[1:0] BUCK1 DVS ramp rate setting
+ * 00: 10.00mV/usec 10mV 1uS
+ * 01: 5.00mV/usec 10mV 2uS
+ * 10: 2.50mV/usec 10mV 4uS
+ * 11: 1.25mV/usec 10mV 8uS
+ */
+static int bd71837_buck1234_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
+{
+ struct bd71837_pmic *pmic = rdev_get_drvdata(rdev);
+ struct bd71837 *mfd = pmic->mfd;
+ int id = rdev->desc->id;
+ unsigned int ramp_value = BUCK1_RAMPRATE_10P00MV;
+
+ dev_dbg(pmic->dev, "Buck[%d] Set Ramp = %d\n", id + 1, ramp_delay);
+ switch (ramp_delay) {
+ case 1 ... 1250:
+ ramp_value = BUCK1_RAMPRATE_1P25MV;
+ break;
+ case 1251 ... 2500:
+ ramp_value = BUCK1_RAMPRATE_2P50MV;
+ break;
+ case 2501 ... 5000:
+ ramp_value = BUCK1_RAMPRATE_5P00MV;
+ break;
+ case 5001 ... 10000:
+ ramp_value = BUCK1_RAMPRATE_10P00MV;
+ break;
+ default:
+ ramp_value = BUCK1_RAMPRATE_10P00MV;
+ dev_err(pmic->dev, "%s: ramp_delay: %d not supported, setting 10000mV//us\n",
+ rdev->desc->name, ramp_delay);
+ }
+
+ return regmap_update_bits(mfd->regmap, BD71837_REG_BUCK1_CTRL + id,
+ BUCK1_RAMPRATE_MASK, ramp_value << 6);
+}
+
+static struct regulator_ops bd71837_ldo_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
+static struct regulator_ops bd71837_fixed_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+static struct regulator_ops bd71837_buck_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .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,
+};
+
+static struct regulator_ops bd71837_buck1234_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .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,
+ .set_ramp_delay = bd71837_buck1234_set_ramp_delay,
+};
+
+/*
+ * BUCK1/2/3/4
+ * 0.70 to 1.30V (10mV step)
+ */
+static const struct regulator_linear_range bd71837_buck1234_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(700000, 0x00, 0x3C, 10000),
+ REGULATOR_LINEAR_RANGE(1300000, 0x3D, 0x3F, 0),
+};
+
+/*
+ * BUCK5
+ * 0.9V to 1.35V ()
+ */
+static const struct regulator_linear_range bd71837_buck5_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(700000, 0x00, 0x03, 100000),
+ REGULATOR_LINEAR_RANGE(1050000, 0x04, 0x05, 50000),
+ REGULATOR_LINEAR_RANGE(1200000, 0x06, 0x07, 150000),
+};
+
+/*
+ * BUCK6
+ * 3.0V to 3.3V (step 100mV)
+ */
+static const struct regulator_linear_range bd71837_buck6_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(3000000, 0x00, 0x03, 100000),
+};
+
+/*
+ * BUCK7
+ * 1.605V to 1.995V ()
+ * 000 = 1.605V
+ * 001 = 1.695V
+ * 010 = 1.755V
+ * 011 = 1.8V (Initial)
+ * 100 = 1.845V
+ * 101 = 1.905V
+ * 110 = 1.95V
+ * 111 = 1.995V
+ */
+static const struct regulator_linear_range bd71837_buck7_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1605000, 0x00, 0x01, 90000),
+ REGULATOR_LINEAR_RANGE(1755000, 0x02, 0x03, 45000),
+ REGULATOR_LINEAR_RANGE(1845000, 0x04, 0x05, 60000),
+ REGULATOR_LINEAR_RANGE(1950000, 0x06, 0x07, 45000),
+};
+
+/*
+ * BUCK8
+ * 0.8V to 1.40V (step 10mV)
+ */
+static const struct regulator_linear_range bd71837_buck8_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(800000, 0x00, 0x3C, 10000),
+ REGULATOR_LINEAR_RANGE(1400000, 0x3D, 0x3F, 0),
+};
+
+/*
+ * LDO1
+ * 3.0 to 3.3V (100mV step)
+ */
+static const struct regulator_linear_range bd71837_ldo1_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(3000000, 0x00, 0x03, 100000),
+};
+
+/*
+ * LDO3
+ * 1.8 to 3.3V (100mV step)
+ */
+static const struct regulator_linear_range bd71837_ldo3_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1800000, 0x00, 0x0F, 100000),
+};
+
+/*
+ * LDO4
+ * 0.9 to 1.8V (100mV step)
+ */
+static const struct regulator_linear_range bd71837_ldo4_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(900000, 0x00, 0x09, 100000),
+ REGULATOR_LINEAR_RANGE(1800000, 0x0A, 0x0F, 0),
+};
+
+/*
+ * LDO5
+ * 1.8 to 3.3V (100mV step)
+ */
+static const struct regulator_linear_range bd71837_ldo5_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1800000, 0x00, 0x0F, 100000),
+};
+
+/*
+ * LDO6
+ * 0.9 to 1.8V (100mV step)
+ */
+static const struct regulator_linear_range bd71837_ldo6_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(900000, 0x00, 0x09, 100000),
+ REGULATOR_LINEAR_RANGE(1800000, 0x0A, 0x0F, 0),
+};
+
+/*
+ * LDO7
+ * 1.8 to 3.3V (100mV step)
+ */
+static const struct regulator_linear_range bd71837_ldo7_voltage_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1800000, 0x00, 0x0F, 100000),
+};
+
+static const struct regulator_desc bd71837_regulators[] = {
+ {
+ .name = "BUCK1",
+ .id = BD71837_BUCK1,
+ .ops = &bd71837_buck1234_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_BUCK1_VOLTAGE_NUM,
+ .linear_ranges = bd71837_buck1234_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_buck1234_voltage_ranges),
+ .vsel_reg = BD71837_REG_BUCK1_VOLT_RUN,
+ .vsel_mask = BUCK1_RUN_MASK,
+ .enable_reg = BD71837_REG_BUCK1_CTRL,
+ .enable_mask = BUCK1_SEL|BUCK1_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "BUCK2",
+ .id = BD71837_BUCK2,
+ .ops = &bd71837_buck1234_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_BUCK2_VOLTAGE_NUM,
+ .linear_ranges = bd71837_buck1234_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_buck1234_voltage_ranges),
+ .vsel_reg = BD71837_REG_BUCK2_VOLT_RUN,
+ .vsel_mask = BUCK2_RUN_MASK,
+ .enable_reg = BD71837_REG_BUCK2_CTRL,
+ .enable_mask = BUCK2_SEL|BUCK2_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "BUCK3",
+ .id = BD71837_BUCK3,
+ .ops = &bd71837_buck1234_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_BUCK3_VOLTAGE_NUM,
+ .linear_ranges = bd71837_buck1234_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_buck1234_voltage_ranges),
+ .vsel_reg = BD71837_REG_BUCK3_VOLT_RUN,
+ .vsel_mask = BUCK3_RUN_MASK,
+ .enable_reg = BD71837_REG_BUCK3_CTRL,
+ .enable_mask = BUCK3_SEL|BUCK3_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "BUCK4",
+ .id = BD71837_BUCK4,
+ .ops = &bd71837_buck1234_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_BUCK4_VOLTAGE_NUM,
+ .linear_ranges = bd71837_buck1234_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_buck1234_voltage_ranges),
+ .vsel_reg = BD71837_REG_BUCK4_VOLT_RUN,
+ .vsel_mask = BUCK4_RUN_MASK,
+ .enable_reg = BD71837_REG_BUCK4_CTRL,
+ .enable_mask = BUCK4_SEL|BUCK4_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "BUCK5",
+ .id = BD71837_BUCK5,
+ .ops = &bd71837_buck_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_BUCK5_VOLTAGE_NUM,
+ .linear_ranges = bd71837_buck5_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_buck5_voltage_ranges),
+ .vsel_reg = BD71837_REG_BUCK5_VOLT,
+ .vsel_mask = BUCK5_MASK,
+ .enable_reg = BD71837_REG_BUCK5_CTRL,
+ .enable_mask = BUCK5_SEL|BUCK5_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "BUCK6",
+ .id = BD71837_BUCK6,
+ .ops = &bd71837_buck_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_BUCK6_VOLTAGE_NUM,
+ .linear_ranges = bd71837_buck6_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_buck6_voltage_ranges),
+ .vsel_reg = BD71837_REG_BUCK6_VOLT,
+ .vsel_mask = BUCK6_MASK,
+ .enable_reg = BD71837_REG_BUCK6_CTRL,
+ .enable_mask = BUCK6_SEL|BUCK6_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "BUCK7",
+ .id = BD71837_BUCK7,
+ .ops = &bd71837_buck_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_BUCK7_VOLTAGE_NUM,
+ .linear_ranges = bd71837_buck7_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_buck7_voltage_ranges),
+ .vsel_reg = BD71837_REG_BUCK7_VOLT,
+ .vsel_mask = BUCK7_MASK,
+ .enable_reg = BD71837_REG_BUCK7_CTRL,
+ .enable_mask = BUCK7_SEL|BUCK7_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "BUCK8",
+ .id = BD71837_BUCK8,
+ .ops = &bd71837_buck_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_BUCK8_VOLTAGE_NUM,
+ .linear_ranges = bd71837_buck8_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_buck8_voltage_ranges),
+ .vsel_reg = BD71837_REG_BUCK8_VOLT,
+ .vsel_mask = BUCK8_MASK,
+ .enable_reg = BD71837_REG_BUCK8_CTRL,
+ .enable_mask = BUCK8_SEL|BUCK8_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "LDO1",
+ .id = BD71837_LDO1,
+ .ops = &bd71837_ldo_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_LDO1_VOLTAGE_NUM,
+ .linear_ranges = bd71837_ldo1_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_ldo1_voltage_ranges),
+ .vsel_reg = BD71837_REG_LDO1_VOLT,
+ .vsel_mask = LDO1_MASK,
+ .enable_reg = BD71837_REG_LDO1_VOLT,
+ .enable_mask = LDO1_SEL|LDO1_EN,
+ .owner = THIS_MODULE,
+ },
+ /*
+ * LDO2 0.9V
+ * Fixed voltage
+ */
+ {
+ .name = "LDO2",
+ .id = BD71837_LDO2,
+ .ops = &bd71837_fixed_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_LDO2_VOLTAGE_NUM,
+ .min_uV = 900000,
+ .enable_reg = BD71837_REG_LDO2_VOLT,
+ .enable_mask = LDO2_SEL|LDO2_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "LDO3",
+ .id = BD71837_LDO3,
+ .ops = &bd71837_ldo_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_LDO3_VOLTAGE_NUM,
+ .linear_ranges = bd71837_ldo3_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_ldo3_voltage_ranges),
+ .vsel_reg = BD71837_REG_LDO3_VOLT,
+ .vsel_mask = LDO3_MASK,
+ .enable_reg = BD71837_REG_LDO3_VOLT,
+ .enable_mask = LDO3_SEL|LDO3_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "LDO4",
+ .id = BD71837_LDO4,
+ .ops = &bd71837_ldo_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_LDO4_VOLTAGE_NUM,
+ .linear_ranges = bd71837_ldo4_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_ldo4_voltage_ranges),
+ .vsel_reg = BD71837_REG_LDO4_VOLT,
+ .vsel_mask = LDO4_MASK,
+ .enable_reg = BD71837_REG_LDO4_VOLT,
+ .enable_mask = LDO4_SEL|LDO4_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "LDO5",
+ .id = BD71837_LDO5,
+ .ops = &bd71837_ldo_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_LDO5_VOLTAGE_NUM,
+ .linear_ranges = bd71837_ldo5_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_ldo5_voltage_ranges),
+ .vsel_reg = BD71837_REG_LDO5_VOLT,
+ .vsel_mask = LDO5_MASK,
+ .enable_reg = BD71837_REG_LDO5_VOLT,
+ .enable_mask = LDO5_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "LDO6",
+ .id = BD71837_LDO6,
+ .ops = &bd71837_ldo_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_LDO6_VOLTAGE_NUM,
+ .linear_ranges = bd71837_ldo6_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_ldo6_voltage_ranges),
+ .vsel_reg = BD71837_REG_LDO6_VOLT,
+ .vsel_mask = LDO6_MASK,
+ .enable_reg = BD71837_REG_LDO6_VOLT,
+ .enable_mask = LDO6_EN,
+ .owner = THIS_MODULE,
+ },
+ {
+ .name = "LDO7",
+ .id = BD71837_LDO7,
+ .ops = &bd71837_ldo_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = BD71837_LDO7_VOLTAGE_NUM,
+ .linear_ranges = bd71837_ldo7_voltage_ranges,
+ .n_linear_ranges = ARRAY_SIZE(bd71837_ldo7_voltage_ranges),
+ .vsel_reg = BD71837_REG_LDO7_VOLT,
+ .vsel_mask = LDO7_MASK,
+ .enable_reg = BD71837_REG_LDO7_VOLT,
+ .enable_mask = LDO7_EN,
+ .owner = THIS_MODULE,
+ },
+};
+
+#ifdef CONFIG_OF
+
+static struct of_regulator_match bd71837_matches[] = {
+ { .name = "buck1", },
+ { .name = "buck2", },
+ { .name = "buck3", },
+ { .name = "buck4", },
+ { .name = "buck5", },
+ { .name = "buck6", },
+ { .name = "buck7", },
+ { .name = "buck8", },
+ { .name = "ldo1", },
+ { .name = "ldo2", },
+ { .name = "ldo3", },
+ { .name = "ldo4", },
+ { .name = "ldo5", },
+ { .name = "ldo6", },
+ { .name = "ldo7", },
+};
+
+/**@brief parse bd71837 regulator device tree
+ * @param pdev platform device of bd71837 regulator
+ * @param bd71837_reg_matches return regualtor matches
+ * @retval 0 parse success
+ * @retval NULL parse fail
+ */
+static int bd71837_parse_dt_reg_data(
+ struct platform_device *pdev,
+ struct of_regulator_match **reg_matches)
+{
+ // struct bd71837 *bd71837 = dev_get_drvdata(pdev->dev.parent);
+ struct device_node *np, *regulators;
+ struct of_regulator_match *matches;
+ int ret, count;
+
+ np = of_node_get(pdev->dev.parent->of_node);
+ regulators = of_find_node_by_name(np, "regulators");
+ if (!regulators) {
+ dev_err(&pdev->dev, "regulator node not found\n");
+ return -EINVAL;
+ }
+
+ count = ARRAY_SIZE(bd71837_matches);
+ matches = bd71837_matches;
+
+ ret = of_regulator_match(&pdev->dev, regulators, matches, count);
+ of_node_put(regulators);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Error parsing regulator init data: %d\n",
+ ret);
+ return ret;
+ }
+
+ *reg_matches = matches;
+
+ return 0;
+}
+#else
+static inline int bd71837_parse_dt_reg_data(
+ struct platform_device *pdev,
+ struct of_regulator_match **reg_matches)
+{
+ *reg_matches = NULL;
+ return 0;
+}
+#endif
+
+/** @brief retrive out32k output value */
+static ssize_t show_value(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct bd71837_pmic *pmic = dev_get_drvdata(dev);
+ int o;
+
+ o = bd71837_reg_read(pmic->mfd, BD71837_REG_OUT32K);
+ o = (o & OUT32K_EN) != 0;
+
+ return sprintf(buf, "%d\n", o);
+}
+
+/** @brief set o output value */
+static ssize_t set_value(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bd71837_pmic *pmic = dev_get_drvdata(dev);
+ int o, r;
+
+ if (sscanf(buf, "%d", &o) < 1) {
+ return -EINVAL;
+ }
+
+ if (o != 0) {
+ o = OUT32K_EN;
+ }
+ r = bd71837_update_bits(pmic->mfd, BD71837_REG_OUT32K, OUT32K_EN, o);
+ if (r < 0) {
+ return r;
+ }
+ return count;
+}
+
+/** @brief list all supported values */
+static ssize_t available_values(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "0 1 \n");
+}
+
+/** @brief directly set raw value to chip register, format: 'register value' */
+static ssize_t bd71837_sysfs_set_registers(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct bd71837_pmic *pmic = dev_get_drvdata(dev);
+ ssize_t ret = 0;
+ unsigned int reg;
+ unsigned int val;
+
+ ret = sscanf(buf, "%x %x", &reg, &val);
+ if (ret < 1) {
+ pmic->reg_index = -1;
+ dev_err(pmic->dev, "registers set: <reg> <value>\n");
+ return count;
+ }
+
+ if (ret == 1 && reg < BD71837_MAX_REGISTER) {
+ pmic->reg_index = reg;
+ dev_info(pmic->dev, "registers set: reg=0x%x\n", reg);
+ return count;
+ }
+
+ if (reg > BD71837_MAX_REGISTER) {
+ dev_err(pmic->dev, "reg=%d out of Max=%d\n", reg, BD71837_MAX_REGISTER);
+ return -EINVAL;
+ }
+ dev_info(pmic->dev, "registers set: reg=0x%x, val=0x%x\n", reg, val);
+ ret = bd71837_reg_write(pmic->mfd, reg, val);
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+/** @brief print value of chip register, format: 'register=value' */
+static ssize_t bd71837_sysfs_print_reg(struct bd71837_pmic *pmic,
+ u8 reg,
+ char *buf)
+{
+ int ret = bd71837_reg_read(pmic->mfd, reg);
+ if (ret < 0)
+ return sprintf(buf, "%#.2x=error %d\n", reg, ret);
+ return sprintf(buf, "[0x%.2X] = %.2X\n", reg, ret);
+}
+
+/** @brief show all raw values of chip register, format per line: 'register=value' */
+static ssize_t bd71837_sysfs_show_registers(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct bd71837_pmic *pmic = dev_get_drvdata(dev);
+ ssize_t ret = 0;
+ int i;
+
+ dev_info(pmic->dev, "register: index[0x%x]\n", pmic->reg_index);
+ if (pmic->reg_index >= 0) {
+ ret += bd71837_sysfs_print_reg(pmic, pmic->reg_index, buf + ret);
+ } else {
+ for (i = 0; i < BD71837_MAX_REGISTER; i++) {
+ ret += bd71837_sysfs_print_reg(pmic, i, buf + ret);
+ }
+ }
+ return ret;
+}
+
+static DEVICE_ATTR(out32k_value, S_IWUSR | S_IRUGO, show_value, set_value);
+static DEVICE_ATTR(available_value, S_IWUSR | S_IRUGO, available_values, NULL);
+static DEVICE_ATTR(registers, S_IWUSR | S_IRUGO,
+ bd71837_sysfs_show_registers, bd71837_sysfs_set_registers);
+
+/** @brief device sysfs attribute table, about o */
+static struct attribute *clk_attributes[] = {
+ &dev_attr_out32k_value.attr,
+ &dev_attr_available_value.attr,
+ &dev_attr_registers.attr,
+ NULL
+};
+
+static const struct attribute_group clk_attr_group = {
+ .attrs = clk_attributes,
+};
+
+/*----------------------------------------------------------------------*/
+#ifdef CONFIG_OF
+/** @brief buck1/2 dvs enable/voltage from device tree
+ * @param pdev platfrom device pointer
+ * @param buck_dvs pointer
+ * @return void
+ */
+static void of_bd71837_buck_dvs(struct platform_device *pdev, struct bd71837_buck_dvs *buck_dvs)
+{
+ struct device_node *pmic_np;
+
+ pmic_np = of_node_get(pdev->dev.parent->of_node);
+ if (!pmic_np) {
+ dev_err(&pdev->dev, "could not find pmic sub-node\n");
+ return;
+ }
+
+ if (of_get_property(pmic_np, "bd71837,pmic-buck1-uses-i2c-dvs", NULL)) {
+ if (of_property_read_u32_array(pmic_np,
+ "bd71837,pmic-buck1-dvs-voltage",
+ &buck_dvs[0].voltage[0], BD71837_DVS_RUN_IDLE_SUSP)) {
+ dev_err(&pdev->dev, "buck1 voltages not specified\n");
+ }
+ }
+
+ if (of_get_property(pmic_np, "bd71837,pmic-buck2-uses-i2c-dvs", NULL)) {
+ if (of_property_read_u32_array(pmic_np,
+ "bd71837,pmic-buck2-dvs-voltage",
+ &buck_dvs[1].voltage[0], BD71837_DVS_RUN_IDLE)) {
+ dev_err(&pdev->dev, "buck2 voltages not specified\n");
+ }
+ }
+
+ if (of_get_property(pmic_np, "bd71837,pmic-buck3-uses-i2c-dvs", NULL)) {
+ if (of_property_read_u32_array(pmic_np,
+ "bd71837,pmic-buck3-dvs-voltage",
+ &buck_dvs[2].voltage[0], BD71837_DVS_RUN)) {
+ dev_err(&pdev->dev, "buck3 voltages not specified\n");
+ }
+ }
+ if (of_get_property(pmic_np, "bd71837,pmic-buck4-uses-i2c-dvs", NULL)) {
+ if (of_property_read_u32_array(pmic_np,
+ "bd71837,pmic-buck4-dvs-voltage",
+ &buck_dvs[3].voltage[0], BD71837_DVS_RUN)) {
+ dev_err(&pdev->dev, "buck4 voltages not specified\n");
+ }
+ }
+}
+#else
+static void of_bd71837_buck_dvs(struct platform_device *pdev, struct bd71837_buck_dvs *buck_dvs)
+{
+ buck_dvs[0].voltage[0] = BUCK1_RUN_DEFAULT;
+ buck_dvs[0].voltage[1] = BUCK1_IDLE_DEFAULT;
+ buck_dvs[0].voltage[2] = BUCK1_SUSP_DEFAULT;
+ buck_dvs[1].voltage[0] = BUCK2_RUN_DEFAULT;
+ buck_dvs[1].voltage[1] = BUCK2_IDLE_DEFAULT;
+ buck_dvs[1].voltage[2] = 0; /* Not supported */
+ buck_dvs[2].voltage[0] = BUCK3_RUN_DEFAULT;
+ buck_dvs[2].voltage[1] = 0; /* Not supported */
+ buck_dvs[2].voltage[2] = 0; /* Not supported */
+ buck_dvs[3].voltage[0] = BUCK4_RUN_DEFAULT;
+ buck_dvs[3].voltage[1] = 0; /* Not supported */
+ buck_dvs[3].voltage[2] = 0; /* Not supported */
+}
+#endif
+
+static int bd71837_buck1234_dvs_init(struct bd71837_pmic *pmic)
+{
+ struct bd71837 *bd71837 = pmic->mfd;
+ struct bd71837_buck_dvs *buck_dvs = &pmic->buck_dvs[0];
+ int i, ret, val, selector = 0;
+ u8 reg_run, reg_idle, reg_susp;
+ u8 reg_run_msk, reg_idle_msk, reg_susp_msk;
+
+ for (i = 0; i < BD71837_DVS_BUCK_NUM; i++, buck_dvs++) {
+ switch (i) {
+ case 0:
+ default:
+ reg_run = BD71837_REG_BUCK1_VOLT_RUN;
+ reg_run_msk = BUCK1_RUN_MASK;
+ reg_idle = BD71837_REG_BUCK1_VOLT_IDLE;
+ reg_idle_msk = BUCK1_IDLE_MASK;
+ reg_susp = BD71837_REG_BUCK1_VOLT_SUSP;
+ reg_susp_msk = BUCK1_SUSP_MASK;
+ break;
+ case 1:
+ reg_run = BD71837_REG_BUCK2_VOLT_RUN;
+ reg_run_msk = BUCK2_RUN_MASK;
+ reg_idle = BD71837_REG_BUCK2_VOLT_IDLE;
+ reg_idle_msk = BUCK2_IDLE_MASK;
+ reg_susp = 0;
+ break;
+ case 2:
+ reg_run = BD71837_REG_BUCK3_VOLT_RUN;
+ reg_run_msk = BUCK3_RUN_MASK;
+ reg_idle = 0;
+ reg_susp = 0;
+ break;
+ case 3:
+ reg_run = BD71837_REG_BUCK4_VOLT_RUN;
+ reg_run_msk = BUCK4_RUN_MASK;
+ reg_idle = 0;
+ reg_susp = 0;
+ break;
+ }
+
+ dev_dbg(pmic->dev, "Buck%d: DVS Run-Idle-Susp[%d - %d - %d].\n", i+1, buck_dvs->voltage[0], buck_dvs->voltage[1], buck_dvs->voltage[2]);
+ if (reg_run > 0) {
+ selector = regulator_map_voltage_iterate(pmic->rdev[i], buck_dvs->voltage[0], buck_dvs->voltage[0]);
+ if (selector < 0) {
+ dev_dbg(pmic->dev, "%s(): not found selector for Run voltage [%d]\n", __func__, buck_dvs->voltage[0]);
+ } else {
+ val = (selector & reg_run_msk);
+ ret = bd71837_reg_write(bd71837, reg_run, val);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ if (reg_idle > 0) {
+ selector = regulator_map_voltage_iterate(pmic->rdev[i], buck_dvs->voltage[1], buck_dvs->voltage[1]);
+ if (selector < 0) {
+ dev_dbg(pmic->dev, "%s(): not found selector for Idle voltage [%d]\n", __func__, buck_dvs->voltage[1]);
+ } else {
+ val = (selector & reg_idle_msk);
+ ret = bd71837_reg_write(bd71837, reg_idle, val);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ if (reg_susp > 0) {
+ selector = regulator_map_voltage_iterate(pmic->rdev[i], buck_dvs->voltage[2], buck_dvs->voltage[2]);
+ if (selector < 0) {
+ dev_dbg(pmic->dev, "%s(): not found selector for Susp voltage [%d]\n", __func__, buck_dvs->voltage[2]);
+ } else {
+ val = (selector & reg_susp_msk);
+ ret = bd71837_reg_write(bd71837, reg_susp, val);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ }
+ return 0;
+}
+
+/**@brief bd71837 pmic interrupt
+ * @param irq system irq
+ * @param pwrsys bd71837 power device of system
+ * @retval IRQ_HANDLED success
+ * @retval IRQ_NONE error
+ */
+static irqreturn_t bd71837_pmic_interrupt(int irq, void *pwrsys)
+{
+ struct device *dev = pwrsys;
+ struct bd71837 *mfd = dev_get_drvdata(dev->parent);
+ // struct bd71837_power *pwr = dev_get_drvdata(dev);
+ int reg;
+
+ bd71837_debug(BD71837_DBG0, "bd71837_pmic_interrupt() in.\n");
+
+ reg = bd71837_reg_read(mfd, BD71837_REG_IRQ);
+ if (reg < 0)
+ return IRQ_NONE;
+
+ if (reg & IRQ_SWRST) {
+ bd71837_debug(BD71837_DBG0, "IRQ_SWRST\n");
+ }
+ if (reg & IRQ_PWRON_S) {
+ bd71837_debug(BD71837_DBG0, "IRQ_PWRON_S\n");
+ }
+ if (reg & IRQ_PWRON_L) {
+ bd71837_debug(BD71837_DBG0, "IRQ_PWRON_L\n");
+ }
+ if (reg & IRQ_PWRON) {
+ bd71837_debug(BD71837_DBG0, "IRQ_PWRON\n");
+ }
+ if (reg & IRQ_WDOG) {
+ bd71837_debug(BD71837_DBG0, "IRQ_WDOG\n");
+ }
+ if (reg & IRQ_ON_REQ) {
+ bd71837_debug(BD71837_DBG0, "IRQ_ON_REQ\n");
+ }
+ if (reg & IRQ_STBY_REQ) {
+ bd71837_debug(BD71837_DBG0, "IRQ_STBY_REQ\n");
+ }
+
+ reg = bd71837_reg_write(mfd, BD71837_REG_IRQ, reg);
+ if (reg < 0)
+ return IRQ_NONE;
+
+ return IRQ_HANDLED;
+}
+
+/**@brief probe bd71837 regulator device
+ @param pdev bd71837 regulator platform device
+ @retval 0 success
+ @retval negative fail
+*/
+static int bd71837_probe(struct platform_device *pdev)
+{
+ struct bd71837_pmic *pmic;
+ struct bd71837_board *pdata;
+ struct regulator_config config = {};
+ struct bd71837 *bd71837 = dev_get_drvdata(pdev->dev.parent);
+ struct of_regulator_match *matches = NULL;
+ int i = 0, err, irq = 0, ret = 0;
+
+ pmic = kzalloc(sizeof(*pmic), GFP_KERNEL);
+ if (!pmic) {
+ dev_err(&pdev->dev, "Memory allocation failed for pmic\n");
+ return -ENOMEM;
+ }
+
+ memcpy(pmic->descs, bd71837_regulators, sizeof(pmic->descs));
+
+ pmic->dev = &pdev->dev;
+ pmic->mfd = bd71837;
+ platform_set_drvdata(pdev, pmic);
+ pdata = dev_get_platdata(bd71837->dev);
+
+ if (!pdata && bd71837->dev->of_node) {
+ bd71837_parse_dt_reg_data(pdev, &matches);
+ if (matches == NULL) {
+ dev_err(&pdev->dev, "Platform data not found\n");
+ return -EINVAL;
+ }
+ }
+
+ /* Get buck dvs parameters */
+ of_bd71837_buck_dvs(pdev, &pmic->buck_dvs[0]);
+
+ /* Register LOCK release */
+ err = bd71837_reg_write(bd71837, BD71837_REG_REGLOCK, 0x0);
+ if (err != 0) {
+ dev_err(&pdev->dev, "Failed to write LOCK register(%d)\n", err);
+ goto err;
+ }
+ /* Enable LDO5 */
+ err = bd71837_set_bits(bd71837, BD71837_REG_LDO5_VOLT, LDO5_EN);
+ if (err != 0) {
+ dev_err(&pdev->dev, "Failed to enable LDO5 register(%d)\n", err);
+ goto err;
+ }
+ /* Enable LDO6 */
+ err = bd71837_set_bits(bd71837, BD71837_REG_LDO6_VOLT, LDO6_EN);
+ if (err != 0) {
+ dev_err(&pdev->dev, "Failed to enable LDO6 register(%d)\n", err);
+ goto err;
+ }
+ /* Enable LDO7 */
+ err = bd71837_set_bits(bd71837, BD71837_REG_LDO7_VOLT, LDO7_EN);
+ if (err != 0) {
+ dev_err(&pdev->dev, "Failed to enable LDO7 register(%d)\n", err);
+ goto err;
+ }
+
+ for (i = 0; i < BD71837_REGULATOR_CNT; i++) {
+ struct regulator_init_data *init_data;
+ struct regulator_desc *desc;
+ struct regulator_dev *rdev;
+
+ desc = &pmic->descs[i];
+ desc->name = bd71837_matches[i].name;
+
+ if (pdata) {
+ init_data = pdata->init_data[i];
+ } else {
+ init_data = matches[i].init_data;
+ }
+
+ config.dev = pmic->dev;
+ config.init_data = init_data;
+ config.driver_data = pmic;
+ config.regmap = bd71837->regmap;
+ config.of_node = matches[i].of_node;
+ dev_info(config.dev, "regulator register name '%s'\n", desc->name);
+
+ rdev = regulator_register(desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(bd71837->dev,
+ "failed to register %s regulator\n",
+ desc->name);
+ err = PTR_ERR(rdev);
+ goto err;
+ }
+ pmic->rdev[i] = rdev;
+ }
+
+ /* Init sysfs registers */
+ pmic->reg_index = -1;
+
+ err = sysfs_create_group(&pdev->dev.kobj, &clk_attr_group);
+ if (err != 0) {
+ dev_err(&pdev->dev, "Failed to create attribute group: %d\n", err);
+ goto err;
+ }
+
+ /* Init Buck1/2/3/4 dvs */
+ err = bd71837_buck1234_dvs_init(pmic);
+ if (err != 0) {
+ dev_err(&pdev->dev, "Failed to buck12 dvs: %d\n", err);
+ goto err;
+ }
+
+ /* Add Interrupt */
+ irq = platform_get_irq(pdev, 0); // get irq number
+ if (irq <= 0) {
+ dev_warn(&pdev->dev, "platform irq error # %d\n", irq);
+ return -ENXIO;
+ }
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ bd71837_pmic_interrupt, IRQF_TRIGGER_LOW | IRQF_EARLY_RESUME,
+ dev_name(&pdev->dev), &pdev->dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "IRQ %d is not free.\n", irq);
+ }
+
+ /* Un-mask IRQ Interrupt */
+ ret = bd71837_reg_write(bd71837, BD71837_REG_MIRQ, 0);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Write Un-mask 'BD71837_REG_MIRQ': failed!\n");
+ ret = -EIO;
+ goto err;
+ }
+
+ return 0;
+
+err:
+ while (--i >= 0)
+ regulator_unregister(pmic->rdev[i]);
+
+ kfree(pmic);
+ return err;
+}
+
+/**@brief remove bd71837 regulator device
+ @param pdev bd71837 regulator platform device
+ @return 0
+*/
+static int __exit bd71837_remove(struct platform_device *pdev)
+{
+ struct bd71837_pmic *pmic = platform_get_drvdata(pdev);
+ int i;
+
+ sysfs_remove_group(&pdev->dev.kobj, &clk_attr_group);
+
+ for (i = 0; i < BD71837_REGULATOR_CNT; i++)
+ regulator_unregister(pmic->rdev[i]);
+
+ kfree(pmic);
+ return 0;
+}
+
+static struct platform_driver bd71837_driver = {
+ .driver = {
+ .name = "bd71837-pmic",
+ .owner = THIS_MODULE,
+ },
+ .probe = bd71837_probe,
+ .remove = bd71837_remove,
+};
+
+/**@brief module initialize function */
+static int __init bd71837_init(void)
+{
+ return platform_driver_register(&bd71837_driver);
+}
+subsys_initcall(bd71837_init);
+
+/**@brief module deinitialize function */
+static void __exit bd71837_cleanup(void)
+{
+ platform_driver_unregister(&bd71837_driver);
+}
+module_exit(bd71837_cleanup);
+
+MODULE_AUTHOR("Cong Pham <cpham2403@gmail.com>");
+MODULE_DESCRIPTION("BD71837 voltage regulator driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bd71837-pmic");
+
+/*-------------------------------------------------------*/
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+
+#define PROCFS_NAME "bd71837"
+#define BD71837_REV "BD71837 Driver: Rev002\n"
+
+#define BD71837_BUF_SIZE 1024
+static char procfs_buffer[BD71837_BUF_SIZE];
+/**
+ * This function is called then the /proc file is read
+ *
+ */
+static int onetime;
+static ssize_t bd71837_proc_read (struct file *file, char __user *buffer, size_t count, loff_t *data)
+{
+ int ret = 0, error = 0;
+ if (onetime == 0) {
+ onetime = 1;
+ memset(procfs_buffer, 0, BD71837_BUF_SIZE);
+ sprintf(procfs_buffer, "%s", BD71837_REV);
+ ret = strlen(procfs_buffer);
+ error = copy_to_user(buffer, procfs_buffer, strlen(procfs_buffer));
+ } else {
+ //Clear for next time
+ onetime = 0;
+ }
+ return (error != 0) ? 0 : ret;
+}
+
+static ssize_t bd71837_proc_write (struct file *file, const char __user *buffer, size_t count, loff_t *data)
+{
+ sscanf(buffer, "0x%x", &bd71837_debug_mask);
+ printk("bd71837: bd71837_debug_mask=0x%08x\n", bd71837_debug_mask);
+ return count;
+}
+
+static const struct file_operations bd71837_proc_fops = {
+ .owner = THIS_MODULE,
+ .read = bd71837_proc_read,
+ .write = bd71837_proc_write,
+};
+
+/**
+ *This function is called when the module is loaded
+ *
+ */
+int bd71837_revision_init(void)
+{
+ struct proc_dir_entry *bd71837_proc_entry;
+
+ /* create the /proc/bd71837 */
+ bd71837_proc_entry = proc_create(PROCFS_NAME, 0644, NULL, &bd71837_proc_fops);
+ if (bd71837_proc_entry == NULL) {
+ printk("Error: Could not initialize /proc/%s\n", PROCFS_NAME);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+module_init(bd71837_revision_init);
+/*-------------------------------------------------------*/
diff --git a/drivers/regulator/userspace-consumer.c b/drivers/regulator/userspace-consumer.c
index 765acc11c9c8..9d6c07976fa5 100644
--- a/drivers/regulator/userspace-consumer.c
+++ b/drivers/regulator/userspace-consumer.c
@@ -22,6 +22,8 @@
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/regulator/userspace-consumer.h>
+#include <linux/of.h>
+#include <linux/regulator/of_regulator.h>
#include <linux/slab.h>
struct userspace_consumer_data {
@@ -105,6 +107,39 @@ static const struct attribute_group attr_group = {
.attrs = attributes,
};
+#if defined(CONFIG_OF)
+static struct regulator_userspace_consumer_data*
+ of_get_uc_config(struct device *dev, struct device_node *np)
+{
+ struct regulator_userspace_consumer_data *ucd;
+ int r;
+
+ ucd = devm_kzalloc(dev, sizeof(struct regulator_userspace_consumer_data)
+ + sizeof(struct regulator_bulk_data),
+ GFP_KERNEL);
+ if (ucd == NULL)
+ return NULL;
+
+ r = of_property_read_string(np, "uc-name", &ucd->name);
+ if (r) {
+ goto err;
+ }
+
+ ucd->num_supplies = 1;
+ ucd->supplies = (struct regulator_bulk_data *)&ucd[1];
+
+ r = of_property_read_string(np, "suck-supply", &ucd->supplies->supply);
+ if (r) {
+ goto err;
+ }
+ return ucd;
+
+err:
+ devm_kfree(dev, ucd);
+ return NULL;
+}
+#endif
+
static int regulator_userspace_consumer_probe(struct platform_device *pdev)
{
struct regulator_userspace_consumer_data *pdata;
@@ -112,6 +147,11 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev)
int ret;
pdata = dev_get_platdata(&pdev->dev);
+#if defined(CONFIG_OF)
+ if (!pdata && pdev->dev.of_node) {
+ pdata = of_get_uc_config(&pdev->dev, pdev->dev.of_node);
+ }
+#endif
if (!pdata)
return -EINVAL;
@@ -151,6 +191,8 @@ static int regulator_userspace_consumer_probe(struct platform_device *pdev)
drvdata->enabled = pdata->init_on;
platform_set_drvdata(pdev, drvdata);
+ dev_info(&pdev->dev, "attached: %s\n", drvdata->name);
+
return 0;
err_enable:
@@ -171,11 +213,22 @@ static int regulator_userspace_consumer_remove(struct platform_device *pdev)
return 0;
}
+#if defined(CONFIG_OF)
+static const struct of_device_id uc_of_match[] = {
+ { .compatible = "userspace_consumer", },
+ {},
+};
+#endif
+
static struct platform_driver regulator_userspace_consumer_driver = {
.probe = regulator_userspace_consumer_probe,
.remove = regulator_userspace_consumer_remove,
.driver = {
.name = "reg-userspace-consumer",
+ .owner = THIS_MODULE,
+#if defined(CONFIG_OF)
+ .of_match_table = of_match_ptr(uc_of_match),
+#endif
},
};
diff --git a/drivers/regulator/virtual.c b/drivers/regulator/virtual.c
index a6f1c7a9914f..115b9b350f76 100644
--- a/drivers/regulator/virtual.c
+++ b/drivers/regulator/virtual.c
@@ -15,8 +15,11 @@
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
+#include <linux/regulator/of_regulator.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/of.h>
+
struct virtual_consumer_data {
struct mutex lock;
@@ -285,9 +288,22 @@ static const struct attribute_group regulator_virtual_attr_group = {
.attrs = regulator_virtual_attributes,
};
+static const char *of_get_virt_regulator_config(struct device *dev, struct device_node *np)
+{
+ const char *reg_id;
+ int r;
+
+ r = of_property_read_string(np, "virtual-supply", &reg_id);
+ if (r) {
+ return NULL;
+ }
+ return reg_id;
+}
+
static int regulator_virtual_probe(struct platform_device *pdev)
{
char *reg_id = dev_get_platdata(&pdev->dev);
+ struct device_node *np = pdev->dev.of_node;
struct virtual_consumer_data *drvdata;
int ret;
@@ -296,6 +312,15 @@ static int regulator_virtual_probe(struct platform_device *pdev)
if (drvdata == NULL)
return -ENOMEM;
+ if (np) {
+ reg_id = (char *)of_get_virt_regulator_config(&pdev->dev, np);
+ }
+
+ if (reg_id == NULL) {
+ dev_err(&pdev->dev, "Fail to get reg_id");
+ return -EINVAL;
+ }
+
mutex_init(&drvdata->lock);
drvdata->regulator = devm_regulator_get(&pdev->dev, reg_id);
@@ -318,6 +343,8 @@ static int regulator_virtual_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, drvdata);
+ dev_info(&pdev->dev, "attached: %s\n", reg_id);
+
return 0;
}
@@ -330,14 +357,25 @@ static int regulator_virtual_remove(struct platform_device *pdev)
if (drvdata->enabled)
regulator_disable(drvdata->regulator);
+ platform_set_drvdata(pdev, NULL);
+
return 0;
}
+#if defined(CONFIG_OF)
+static const struct of_device_id regulator_virtual_of_match[] = {
+ { .compatible = "regulator-virtual", },
+ {},
+};
+#endif
+
static struct platform_driver regulator_virtual_consumer_driver = {
.probe = regulator_virtual_probe,
.remove = regulator_virtual_remove,
.driver = {
.name = "reg-virt-consumer",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(regulator_virtual_of_match),
},
};