diff options
author | Haoran.Wang <elven.wang@nxp.com> | 2018-02-05 16:26:29 +0800 |
---|---|---|
committer | Jason Liu <jason.hui.liu@nxp.com> | 2019-02-12 10:31:35 +0800 |
commit | 2ec0f8ff9df2eff5c3548320a6e8b6bd403d359b (patch) | |
tree | 6e466110ee41cd8c7a85703688dcda5ade49e37d /drivers | |
parent | 5c587c56dc75877328e348c2fc45bf6a111b54c7 (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/Kconfig | 8 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 1 | ||||
-rw-r--r-- | drivers/mfd/bd71837.c | 308 | ||||
-rw-r--r-- | drivers/regulator/Kconfig | 6 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 1 | ||||
-rw-r--r-- | drivers/regulator/bd71837-regulator.c | 1075 | ||||
-rw-r--r-- | drivers/regulator/userspace-consumer.c | 53 | ||||
-rw-r--r-- | drivers/regulator/virtual.c | 38 |
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", ®, &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", ®_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), }, }; |