diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 96cdc0e1f7e8..c9e25aa1953b 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -204,6 +204,11 @@ config REGULATOR_BD718XX This driver can also be built as a module. If so, the module will be called bd718x7-regulator. +config REGULATOR_BD7181X + tristate "RoHM BD71815/BD71817 Power Regulator" + depends on MFD_BD7181X + help + This driver supports BD71815/BD71817 voltage regulator chips. config REGULATOR_BD9571MWV tristate "ROHM BD9571MWV Regulators" diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index c58c41a3260c..c609e5606279 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o obj-$(CONFIG_REGULATOR_BD70528) += bd70528-regulator.o obj-$(CONFIG_REGULATOR_BD718XX) += bd718x7-regulator.o +obj-$(CONFIG_REGULATOR_BD7181X) += bd7181x-regulator.o obj-$(CONFIG_REGULATOR_BD9571MWV) += bd9571mwv-regulator.o obj-$(CONFIG_REGULATOR_DA903X) += da903x.o obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o diff --git a/drivers/regulator/bd7181x-regulator.c b/drivers/regulator/bd7181x-regulator.c new file mode 100644 index 000000000000..12501368f945 --- /dev/null +++ b/drivers/regulator/bd7181x-regulator.c @@ -0,0 +1,788 @@ +/* + * @file bd7181x-regulator.c RoHM BD71815/BD71817 regulator driver + * + * Copyright 2014 Embest Technology Co. Ltd. Inc. + * + * @author Tony Luo + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BD7181X_VOL_OFFSET 0 +#define BD7181X_STANDBY_OFFSET 0 +#define BD7181X_DVS_BUCK_NUM 2 +#define BD7181X_DVS_HIGH_LOW 2 + +struct bd7181x_buck_dvs { + int i2c_dvs_enable; + u32 voltage[BD7181X_DVS_HIGH_LOW]; +}; + +struct bd7181x_regulator { + struct regulator_desc desc; + unsigned char stby_reg; + unsigned char stby_mask; +}; + +/** @brief bd7181x regulator type */ +struct bd7181x_pmic { + struct bd7181x_regulator descs[BD7181X_REGULATOR_CNT]; /**< regulator description to system */ + struct bd7181x *mfd; /**< parent device */ + struct device *dev; /**< regulator kernel device */ + struct regulator_dev *rdev[BD7181X_REGULATOR_CNT]; /**< regulator device of system */ + struct bd7181x_buck_dvs buck_dvs[BD7181X_DVS_BUCK_NUM]; /**< buck1/2 dvs */ +}; + +static const int bd7181x_wled_currents[] = { + // 0x00 + 10, 20, 30, 50, + 70, 100, 200, 300, + 500, 700, 1000, 2000, + 3000, 4000, 5000, 6000, + // 0x10 + 7000, 8000, 9000, 10000, + 11000, 12000, 13000, 14000, + 15000, 16000, 17000, 18000, + 19000, 20000, 21000, 22000, + // 0x20 + 23000, 24000, 25000, +}; + +/* + * BUCK1/2 + * 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 bd7181x_buck12_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) +{ + struct bd7181x_pmic *pmic = rdev_get_drvdata(rdev); + struct bd7181x *mfd = pmic->mfd; + int id = rdev->desc->id; + unsigned int ramp_value = BUCK1_RAMPRATE_10P00MV; + + 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, BD7181X_REG_BUCK1_MODE + id*0x1, + BUCK1_RAMPRATE_MASK, ramp_value << 6); +} + +static int bd7181x_led_set_current_limit(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + struct bd7181x_pmic* pmic = rdev_get_drvdata(rdev); + struct bd7181x* mfd = pmic->mfd; + u8 addr; + // int id = rdev_get_id(rdev); + int i; + + addr = BD7181X_REG_LED_DIMM; + + for (i = ARRAY_SIZE(bd7181x_wled_currents) - 1 ; i >= 0; i--) { + if (bd7181x_wled_currents[i] >= min_uA && + bd7181x_wled_currents[i] <= max_uA) + return bd7181x_update_bits(mfd, addr, 0x3F, i); + } + + return -EINVAL; +} + +static int bd7181x_led_get_current_limit(struct regulator_dev *rdev) +{ + struct bd7181x_pmic* pmic = rdev_get_drvdata(rdev); + struct bd7181x* mfd = pmic->mfd; + // int id = rdev_get_id(rdev); + u8 addr; + int r; + + addr = BD7181X_REG_LED_DIMM; + + r = bd7181x_reg_read(mfd, addr); + if (r < 0) { + return r; + } + + r = r & 0x3F; + + return (r < ARRAY_SIZE(bd7181x_wled_currents)) ? + bd7181x_wled_currents[r] : -EINVAL; +} + +static int bd7181x_buck12_get_voltage_sel(struct regulator_dev *rdev) +{ + struct bd7181x_pmic *pmic = rdev_get_drvdata(rdev); + int rid = rdev_get_id(rdev); + struct bd7181x *bd7181x = pmic->mfd; + int ret, val; + u8 regh = BD7181X_REG_BUCK1_VOLT_H + rid*0x2, + regl = BD7181X_REG_BUCK1_VOLT_L + rid*0x2; + + ret = bd7181x_reg_read(bd7181x, regh); + if (ret < 0) { + return ret; + } + val = ret; + if((!(val & BUCK1_STBY_DVS)) && (!(val & BUCK1_DVSSEL))) { + ret = bd7181x_reg_read(bd7181x, regl); + if (ret < 0) { + return ret; + } + val = ret & BUCK1_L_MASK; + } else { + val &= BUCK1_H_MASK; + } + return val; +} + +/* + * For Buck 1/2. + * + */ +static int bd7181x_buck12_set_voltage_sel(struct regulator_dev *rdev, unsigned sel) +{ + struct bd7181x_pmic *pmic = rdev_get_drvdata(rdev); + int rid = rdev_get_id(rdev); + struct bd7181x *bd7181x = pmic->mfd; + int ret, val; + u8 regh = BD7181X_REG_BUCK1_VOLT_H + rid*0x2, + regl = BD7181X_REG_BUCK1_VOLT_L + rid*0x2; + + ret = bd7181x_reg_read(bd7181x, regh); + if (ret < 0) { + return ret; + } + val = ret; + if((!(val & BUCK1_STBY_DVS)) && (!(val & BUCK1_DVSSEL))) { + ret = bd7181x_reg_write(bd7181x, regl, sel & BUCK1_L_MASK); + } else { + val = (val & 0xC0) | (sel & BUCK1_H_MASK); + ret = bd7181x_reg_write(bd7181x, regh, val); + } + + return ret; +} + +static struct regulator_ops bd7181x_ldo_regulator_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +static struct regulator_ops bd7181x_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 bd7181x_buck_regulator_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, + .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 bd7181x_buck12_regulator_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = bd7181x_buck12_set_voltage_sel, + .get_voltage_sel = bd7181x_buck12_get_voltage_sel, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_ramp_delay = bd7181x_buck12_set_ramp_delay, +}; + +static struct regulator_ops bd7181x_led_regulator_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_current_limit = bd7181x_led_set_current_limit, + .get_current_limit = bd7181x_led_get_current_limit, +}; + +#define BD7181X_FIXED_REG(_name, ereg, emsk, voltage) \ + [BD7181X_ ## _name] = { \ + .desc = { \ + .name = #_name, \ + .n_voltages = 1, \ + .ops = &bd7181x_fixed_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = BD7181X_ ## _name, \ + .owner = THIS_MODULE, \ + .min_uV = (voltage), \ + .enable_reg = (ereg), \ + .enable_mask = (emsk), \ + }, \ + } + +#define BD7181X_BUCK_REG(_name, base, ereg, min, max, step) \ + [BD7181X_ ## _name] = { \ + .desc = { \ + .name = #_name,\ + .n_voltages = ((max) - (min)) / (step) + 1, \ + .ops = &bd7181x_buck_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = BD7181X_ ## _name, \ + .owner = THIS_MODULE, \ + .min_uV = (min), \ + .uV_step = (step), \ + .vsel_reg = (base) + BD7181X_VOL_OFFSET, \ + .vsel_mask = 0x3f, \ + .enable_reg = (ereg), \ + .enable_mask = 0x04, \ + }, \ + .stby_reg = (base) + BD7181X_STANDBY_OFFSET, \ + .stby_mask = 0x3f, \ + } + +#define BD7181X_BUCK12_REG(_name, base, ereg, min, max, step) \ + [BD7181X_ ## _name] = { \ + .desc = { \ + .name = #_name,\ + .n_voltages = ((max) - (min)) / (step) + 1, \ + .ops = &bd7181x_buck12_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = BD7181X_ ## _name, \ + .owner = THIS_MODULE, \ + .min_uV = (min), \ + .uV_step = (step), \ + .vsel_reg = (base) + BD7181X_VOL_OFFSET, \ + .vsel_mask = 0x3f, \ + .enable_reg = (ereg), \ + .enable_mask = 0x04, \ + }, \ + .stby_reg = (base) + BD7181X_STANDBY_OFFSET, \ + .stby_mask = 0x3f, \ + } + +#define BD7181X_LED_REG(_name, base, mask, ereg, emsk, voltages) \ + [BD7181X_ ## _name] = { \ + .desc = { \ + .name = #_name, \ + .n_voltages = ARRAY_SIZE(voltages), \ + .ops = &bd7181x_led_regulator_ops, \ + .type = REGULATOR_CURRENT, \ + .id = BD7181X_ ## _name, \ + .owner = THIS_MODULE, \ + .volt_table = voltages, \ + .vsel_reg = (base), \ + .vsel_mask = (mask), \ + .enable_reg = (ereg), \ + .enable_mask = (emsk), \ + }, \ + } + +#define BD7181X_LDO_REG(_name, base, ereg, emsk, min, max, step) \ + [BD7181X_ ## _name] = { \ + .desc = { \ + .name = #_name, \ + .n_voltages = ((max) - (min)) / (step) + 1, \ + .ops = &bd7181x_ldo_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = BD7181X_ ## _name, \ + .owner = THIS_MODULE, \ + .min_uV = (min), \ + .uV_step = (step), \ + .vsel_reg = (base), \ + .vsel_mask = 0x3f, \ + .enable_reg = (ereg), \ + .enable_mask = (emsk), \ + }, \ + .stby_reg = (base), \ + .stby_mask = 0x20, \ + } + +static struct bd7181x_regulator bd7181x_regulators[] = { + BD7181X_BUCK12_REG(BUCK1, BD7181X_REG_BUCK1_VOLT_H, BD7181X_REG_BUCK1_MODE, 800000, 2000000, 25000), + BD7181X_BUCK12_REG(BUCK2, BD7181X_REG_BUCK2_VOLT_H, BD7181X_REG_BUCK2_MODE, 800000, 2000000, 25000), + BD7181X_BUCK_REG(BUCK3, BD7181X_REG_BUCK3_VOLT, BD7181X_REG_BUCK3_MODE, 1200000, 2700000, 50000), + BD7181X_BUCK_REG(BUCK4, BD7181X_REG_BUCK4_VOLT, BD7181X_REG_BUCK4_MODE, 1100000, 1850000, 25000), + BD7181X_BUCK_REG(BUCK5, BD7181X_REG_BUCK5_VOLT, BD7181X_REG_BUCK5_MODE, 1800000, 3300000, 50000), + BD7181X_LDO_REG(LDO1, BD7181X_REG_LDO1_VOLT, BD7181X_REG_LDO_MODE1, 0x40, 800000, 3300000, 50000), + BD7181X_LDO_REG(LDO2, BD7181X_REG_LDO2_VOLT, BD7181X_REG_LDO_MODE2, 0x04, 800000, 3300000, 50000), + BD7181X_LDO_REG(LDO3, BD7181X_REG_LDO3_VOLT, BD7181X_REG_LDO_MODE2, 0x40, 800000, 3300000, 50000), + BD7181X_LDO_REG(LDO4, BD7181X_REG_LDO4_VOLT, BD7181X_REG_LDO_MODE3, 0x04, 800000, 3300000, 50000), + BD7181X_LDO_REG(LDO5, BD7181X_REG_LDO5_VOLT_H,BD7181X_REG_LDO_MODE3,0x40, 800000, 3300000, 50000), + BD7181X_FIXED_REG(LDODVREF, BD7181X_REG_LDO_MODE4, 0x40, 3000000), + BD7181X_FIXED_REG(LDOLPSR, BD7181X_REG_LDO_MODE4, 0x04, 1800000), + BD7181X_LED_REG(WLED, BD7181X_REG_LED_DIMM, 0x3F, BD7181X_REG_LED_CTRL, 0x04, bd7181x_wled_currents), +}; + +#ifdef CONFIG_OF + +static struct of_regulator_match bd7181x_matches[] = { + { .name = "buck1", }, + { .name = "buck2", }, + { .name = "buck3", }, + { .name = "buck4", }, + { .name = "buck5", }, + { .name = "ldo1", }, + { .name = "ldo2", }, + { .name = "ldo3", }, + { .name = "ldo4", }, + { .name = "ldo5", }, + { .name = "dvref", }, + { .name = "lpsr", }, + { .name = "wled", }, +}; + +/**@brief parse bd7181x regulator device tree + * @param pdev platform device of bd7181x regulator + * @param bd7181x_reg_matches return regualtor matches + * @retval 0 parse success + * @retval NULL parse fail + */ +static int bd7181x_parse_dt_reg_data( + struct platform_device *pdev, + struct of_regulator_match **reg_matches) +{ + // struct bd7181x *bd7181x = 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(bd7181x_matches); + matches = bd7181x_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 bd7181x_parse_dt_reg_data( + struct platform_device *pdev, + struct of_regulator_match **reg_matches) +{ + *reg_matches = NULL; + return 0; +} +#endif + +/** @brief out32k mode constants */ +static const char* out32k_modes[] = {"open_drain", "cmos"}; + +/** @brief retrive out32k output mode */ +static ssize_t show_mode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct bd7181x_pmic *pmic = dev_get_drvdata(dev); + int o; + + o = bd7181x_reg_read(pmic->mfd, BD7181X_REG_OUT32K); + o = (o & OUT32K_MODE) != 0; + + return sprintf(buf, "%s\n", out32k_modes[o]); +} + +/** @brief set out32k output mode */ +static ssize_t set_mode(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bd7181x_pmic *pmic = dev_get_drvdata(dev); + int o, r; + + if (strncmp(buf, out32k_modes[0], strlen(out32k_modes[0])) == 0) { + o = 0; + } else { + o = OUT32K_MODE; + } + + r = bd7181x_update_bits(pmic->mfd, BD7181X_REG_OUT32K, OUT32K_MODE, o); + if (r < 0) { + return r; + } + return count; +} + +/** @brief retrive out32k output value */ +static ssize_t show_value(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct bd7181x_pmic *pmic = dev_get_drvdata(dev); + int o; + + o = bd7181x_reg_read(pmic->mfd, BD7181X_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 bd7181x_pmic *pmic = dev_get_drvdata(dev); + int o, r; + + if (sscanf(buf, "%d", &o) < 1) { + return -EINVAL; + } + + if (o != 0) { + o = OUT32K_EN; + } + r = bd7181x_update_bits(pmic->mfd, BD7181X_REG_OUT32K, OUT32K_EN, o); + if (r < 0) { + return r; + } + return count; +} + +/** @brief list all supported modes */ +static ssize_t available_modes(struct device *dev, struct device_attribute *attr, char *buf) +{ + int i, r; + + r = 0; + for (i = 0; i < ARRAY_SIZE(out32k_modes) && r >= 0; i++) { + r += sprintf(buf + r, "%s ", out32k_modes[i]); + } + r += sprintf(buf + r, "\n"); + + return r; +} + +/** @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 retrive dvssel output value */ +static ssize_t show_dvssel(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct bd7181x_pmic *pmic = dev_get_drvdata(dev); + int value, index = 0, i; + + for (i = 0; i < BD7181X_DVS_BUCK_NUM; i++) { + value = bd7181x_reg_read(pmic->mfd, BD7181X_REG_BUCK1_VOLT_H + i*0x2); + if(value < 0) + return value; + value = (value & BUCK1_DVSSEL) != 0; + index += sprintf(buf+index, "BUCK%i: %d\n", i, value); + } + return index; +} + +/** @brief set o output value */ +static ssize_t set_dvssel(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bd7181x_pmic *pmic = dev_get_drvdata(dev); + int devsel1, devsel2, ret; + + if (sscanf(buf, "%d %d", &devsel1, &devsel2) < 1) { + return -EINVAL; + } + + ret = bd7181x_update_bits(pmic->mfd, BD7181X_REG_BUCK1_VOLT_H, BUCK1_DVSSEL, devsel1<<7); + if(ret < 0) + return ret; + ret = bd7181x_update_bits(pmic->mfd, BD7181X_REG_BUCK2_VOLT_H, BUCK2_DVSSEL, devsel2<<7); + if(ret < 0) + return ret; + return count; +} + +static DEVICE_ATTR(out32k_mode, S_IWUSR | S_IRUGO, show_mode, set_mode); +static DEVICE_ATTR(out32k_value, S_IWUSR | S_IRUGO, show_value, set_value); +static DEVICE_ATTR(available_mode, S_IWUSR | S_IRUGO, available_modes, NULL); +static DEVICE_ATTR(available_value, S_IWUSR | S_IRUGO, available_values, NULL); +static DEVICE_ATTR(dvssel, S_IWUSR | S_IRUGO, show_dvssel, set_dvssel); + +/** @brief device sysfs attribute table, about o */ +static struct attribute *gpo_attributes[] = { + &dev_attr_out32k_mode.attr, + &dev_attr_out32k_value.attr, + &dev_attr_available_mode.attr, + &dev_attr_available_value.attr, + &dev_attr_dvssel.attr, + NULL +}; + +static const struct attribute_group gpo_attr_group = { + .attrs = gpo_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_bd7181x_buck_dvs(struct platform_device *pdev, struct bd7181x_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, "bd7181x,pmic-buck1-uses-i2c-dvs", NULL)) { + buck_dvs[0].i2c_dvs_enable = 1; + if (of_property_read_u32_array(pmic_np, + "bd7181x,pmic-buck1-dvs-voltage", + &buck_dvs[0].voltage[0], 2)) { + dev_err(&pdev->dev, "buck1 voltages not specified\n"); + } + } + + if (of_get_property(pmic_np, "bd7181x,pmic-buck2-uses-i2c-dvs", NULL)) { + buck_dvs[1].i2c_dvs_enable = 1; + if (of_property_read_u32_array(pmic_np, + "bd7181x,pmic-buck2-dvs-voltage", + &buck_dvs[1].voltage[0], 2)) { + dev_err(&pdev->dev, "buck2 voltages not specified\n"); + } + } +} +#else +static void of_bd7181x_buck_dvs(struct platform_device *pdev, struct bd7181x_buck_dvs *buck_dvs) +{ + buck_dvs[0].i2c_dvs_enable = 0; + buck_dvs[0].voltage[0] = BUCK1_H_DEFAULT; + buck_dvs[0].voltage[1] = BUCK1_L_DEFAULT; + buck_dvs[1].i2c_dvs_enable = 0; + buck_dvs[1].voltage[0] = BUCK1_H_DEFAULT; + buck_dvs[1].voltage[1] = BUCK1_L_DEFAULT; +} +#endif + +static int bd7181x_buck12_dvs_init(struct bd7181x_pmic *pmic) +{ + struct bd7181x *bd7181x = pmic->mfd; + struct bd7181x_buck_dvs *buck_dvs = &pmic->buck_dvs[0]; + int i, ret, val, selector = 0; + u8 regh, regl; + + for(i = 0; i < BD7181X_DVS_BUCK_NUM; i++, buck_dvs++) { + if (!buck_dvs->i2c_dvs_enable) + continue; + + regh = BD7181X_REG_BUCK1_VOLT_H + i*0x2; + regl = BD7181X_REG_BUCK1_VOLT_L + i*0x2; + val = BUCK1_DVSSEL; + dev_info(pmic->dev, "Buck%d: I2C DVS Enabled !\n", i); + val &= ~BUCK1_STBY_DVS; + dev_info(pmic->dev, "Buck%d: DVS High-Low[%d - %d].\n", i, buck_dvs->voltage[0], buck_dvs->voltage[1]); + selector = regulator_map_voltage_iterate(pmic->rdev[i], buck_dvs->voltage[0], buck_dvs->voltage[0]); + if(selector < 0) { + dev_err(pmic->dev, "%s(): not found selector for voltage [%d]\n", __func__, buck_dvs->voltage[0]); + } else { + ret = bd7181x_reg_write(bd7181x, regh, val | (selector & BUCK1_H_MASK)); + if(ret < 0) + return ret; + } + selector = regulator_map_voltage_iterate(pmic->rdev[i], buck_dvs->voltage[1], buck_dvs->voltage[1]); + if(selector < 0) { + dev_err(pmic->dev, "%s(): not found selector for voltage [%d]\n", __func__, buck_dvs->voltage[1]); + } else { + ret = bd7181x_reg_write(bd7181x, regl, val | (selector & BUCK1_L_MASK)); + if(ret < 0) + return ret; + } + } + return 0; +} + +/**@brief probe bd7181x regulator device + @param pdev bd7181x regulator platform device + @retval 0 success + @retval negative fail +*/ +static int bd7181x_probe(struct platform_device *pdev) +{ + struct bd7181x_pmic *pmic; + struct bd7181x_board *pdata; + struct regulator_config config = {}; + struct bd7181x *bd7181x = dev_get_drvdata(pdev->dev.parent); + struct of_regulator_match *matches = NULL; + int i, err; + + pmic = kzalloc(sizeof(*pmic), GFP_KERNEL); + if (!pmic) { + dev_err(&pdev->dev, "Memory allocation failed for pmic\n"); + return -ENOMEM; + } + + memcpy(pmic->descs, bd7181x_regulators, sizeof(pmic->descs)); + + pmic->dev = &pdev->dev; + pmic->mfd = bd7181x; + platform_set_drvdata(pdev, pmic); + + pdata = dev_get_platdata(bd7181x->dev); + if (!pdata && bd7181x->dev->of_node) { + bd7181x_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_bd7181x_buck_dvs(pdev, &pmic->buck_dvs[0]); + + for (i = 0; i < BD7181X_REGULATOR_CNT; i++) { + struct regulator_init_data *init_data; + struct regulator_desc *desc; + struct regulator_dev *rdev; + + desc = &pmic->descs[i].desc; + desc->name = bd7181x_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 = bd7181x->regmap; + config.of_node = matches[i].of_node; + + rdev = regulator_register(desc, &config); + if (IS_ERR(rdev)) { + dev_err(bd7181x->dev, + "failed to register %s regulator\n", + desc->name); + err = PTR_ERR(rdev); + goto err; + } + pmic->rdev[i] = rdev; + } + + err = sysfs_create_group(&pdev->dev.kobj, &gpo_attr_group); + if (err != 0) { + dev_err(&pdev->dev, "Failed to create attribute group: %d\n", err); + goto err; + } + + /* Init buck12 dvs */ + err = bd7181x_buck12_dvs_init(pmic); + if (err != 0) { + dev_err(&pdev->dev, "Failed to buck12 dvs: %d\n", err); + goto err; + } + + return 0; + +err: + while (--i >= 0) + regulator_unregister(pmic->rdev[i]); + + kfree(pmic); + return err; +} + +/**@brief remove bd7181x regulator device + @param pdev bd7181x regulator platform device + @return 0 +*/ +static int bd7181x_remove(struct platform_device *pdev) +{ + struct bd7181x_pmic *pmic = platform_get_drvdata(pdev); + int i; + + sysfs_remove_group(&pdev->dev.kobj, &gpo_attr_group); + + for (i = 0; i < BD7181X_REGULATOR_CNT; i++) + regulator_unregister(pmic->rdev[i]); + + kfree(pmic); + return 0; +} + +static struct platform_driver bd7181x_driver = { + .driver = { + .name = "bd7181x-pmic", + .owner = THIS_MODULE, + }, + .probe = bd7181x_probe, + .remove = bd7181x_remove, +}; + +/**@brief module initialize function */ +static int __init bd7181x_init(void) +{ + return platform_driver_register(&bd7181x_driver); +} +subsys_initcall(bd7181x_init); + +/**@brief module deinitialize function */ +static void __exit bd7181x_cleanup(void) +{ + platform_driver_unregister(&bd7181x_driver); +} +module_exit(bd7181x_cleanup); + +MODULE_AUTHOR("Tony Luo "); +MODULE_DESCRIPTION("BD71815/BD71817 voltage regulator driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bd7181x-pmic");