| /* |
| * Regulators driver for ASR PM813 |
| * |
| * Copyright 2021 ASR Microelectronics (Shanghai) Co., Ltd. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| */ |
| #include <linux/module.h> |
| #include <linux/moduleparam.h> |
| #include <linux/init.h> |
| #include <linux/err.h> |
| #include <linux/regmap.h> |
| #include <linux/regulator/driver.h> |
| #include <linux/regulator/machine.h> |
| #include <linux/mfd/88pm80x.h> |
| #include <linux/mfd/pm813.h> |
| #include <linux/delay.h> |
| #include <linux/io.h> |
| #include <linux/of.h> |
| #include <linux/cputype.h> |
| #include <linux/regulator/of_regulator.h> |
| |
| /* LDO1 with DVC[0..3] */ |
| #define PM813_LDO1_VOUT (0x71) /* VOUT1 */ |
| #define PM813_LDO2_VOUT (0x74) |
| #define PM813_LDO3_VOUT (0x77) |
| #define PM813_LDO4_VOUT (0x7A) |
| #define PM813_LDO5_VOUT (0x7D) |
| #define PM813_LDO6_VOUT (0x80) |
| #define PM813_LDO7_VOUT (0x83) |
| #define PM813_LDO8_VOUT (0x86) |
| #define PM813_LDO9_VOUT (0x89) |
| #define PM813_LDO10_VOUT (0x8C) |
| #define PM813_LDO11_VOUT (0x8F) |
| #define PM813_LDO12_VOUT (0x92) |
| #define PM813_LDO13_VOUT (0x95) |
| |
| /* BUCK1 with DVC[0..3] */ |
| #define PM813_BUCK1 (0x2A) |
| #define PM813_BUCK2 (0x30) |
| #define PM813_BUCK3 (0x40) |
| |
| #define PM813_BUCK1_ENA (0x20) |
| #define PM813_BUCK2_ENA (0x30) |
| #define PM813_BUCK3_ENA (0x40) |
| |
| #define PM813_LDO1_ENA (0x71) /* VOUT1 */ |
| #define PM813_LDO2_ENA (0x74) |
| #define PM813_LDO3_ENA (0x77) |
| #define PM813_LDO4_ENA (0x7A) |
| #define PM813_LDO5_ENA (0x7D) |
| #define PM813_LDO6_ENA (0x80) |
| #define PM813_LDO7_ENA (0x83) |
| #define PM813_LDO8_ENA (0x86) |
| #define PM813_LDO9_ENA (0x89) |
| #define PM813_LDO10_ENA (0x8C) |
| #define PM813_LDO11_ENA (0x8F) |
| #define PM813_LDO12_ENA (0x92) |
| #define PM813_LDO13_ENA (0x95) |
| |
| #define MAX_SLEEP_CURRENT 5000 |
| #define REGULATOR_SLEEP_MODE_DIS 0 |
| #define REGULATOR_SLEEP_MODE_EN 1 |
| |
| /* |
| * Convert voltage value to reg value for BUCK1 |
| */ |
| #define BUCK1_VOL2REG(vol) \ |
| (((vol) > 1587500) \ |
| ? ((((vol)-1600000)/50000) + 0x50) \ |
| : (((vol)-600000)/12500)) |
| |
| #define BUCK1_REG2VOL(reg) \ |
| (((reg) < 0x50) \ |
| ? (reg * 12500 + 600000) \ |
| : ((reg - 0x50) * 50000 + 1600000)) |
| |
| struct pm813_regulator_volt_range { |
| int min_uv; |
| int max_uv; |
| int step_uv; |
| /* the register value for min_uv */ |
| int min_val; |
| }; |
| |
| struct pm813_regulator_info { |
| struct regulator_desc desc;; |
| int max_ua; |
| unsigned int sleep_enable_bit; |
| unsigned int sleep_enable_reg; |
| struct pm813_regulator_volt_range *volt; |
| unsigned int ranges; |
| }; |
| |
| struct pm813_regulators { |
| struct regulator_dev *regulators[PM813_ID_RG_MAX]; |
| struct pm80x_chip *chip; |
| struct regmap *map; |
| }; |
| |
| /* |
| * vreg - the buck regs string. |
| * ereg - the string for the enable register. |
| * ebit - the bit number in the enable register. |
| * amax - the current |
| * Buck has 2 kinds of voltage steps. It is easy to find voltage by ranges, |
| * not the constant voltage table. |
| */ |
| #define PM813_BUCK(vreg, ereg, ebit, amax, sebit, sereg, volt_ranges) \ |
| { \ |
| .desc = { \ |
| .name = #vreg, \ |
| .ops = &pm813_volt_range_ops, \ |
| .type = REGULATOR_VOLTAGE, \ |
| .id = PM813_ID_##vreg, \ |
| .owner = THIS_MODULE, \ |
| .vsel_reg = PM813_##vreg, \ |
| .vsel_mask = 0x7f, \ |
| .enable_reg = PM813_##ereg, \ |
| .enable_mask = 1 << (ebit), \ |
| }, \ |
| .max_ua = (amax), \ |
| .volt = (volt_ranges), \ |
| .ranges = ARRAY_SIZE(volt_ranges), \ |
| .sleep_enable_bit = sebit, \ |
| .sleep_enable_reg = PM813_##sereg, \ |
| } |
| |
| /* |
| * vreg - the LDO regs string |
| * ereg - the string for the enable register. |
| * ebit - the bit number in the enable register. |
| * amax - the current |
| * volt_table - the LDO voltage table |
| * For all the LDOes, there are too many ranges. Using volt_table will be |
| * simpler and faster. |
| */ |
| #define PM813_LDO(vreg, ereg, ebit, amax, sebit, sereg, ldo_volt_table) \ |
| { \ |
| .desc = { \ |
| .name = #vreg, \ |
| .ops = &pm813_volt_table_ops, \ |
| .type = REGULATOR_VOLTAGE, \ |
| .id = PM813_ID_##vreg, \ |
| .owner = THIS_MODULE, \ |
| .n_voltages = ARRAY_SIZE(ldo_volt_table), \ |
| .vsel_reg = PM813_##vreg##_VOUT, \ |
| .vsel_mask = 0x3c, \ |
| .enable_reg = PM813_##ereg, \ |
| .enable_mask = 1 << (ebit), \ |
| .volt_table = ldo_volt_table, \ |
| }, \ |
| .max_ua = (amax), \ |
| .sleep_enable_bit = sebit, \ |
| .sleep_enable_reg = PM813_##sereg, \ |
| } |
| |
| /* Ranges are sorted in ascending order. */ |
| static struct pm813_regulator_volt_range buck1_5_volt_range[] = { |
| {600000, 1587500, 12500, 0x0}, |
| {1600000, 3950000, 50000, 0x50}, |
| }; |
| |
| /* LDO 1~6 have same voltage table. */ |
| static const unsigned int ldo1_4_volt_table[] = { |
| 1200000, 1250000, 1700000, 1800000, 1850000, 1900000, 2500000, 2600000, |
| 2700000, 2750000, 2800000, 2850000, 2900000, 3000000, 3100000, 3300000, |
| }; |
| |
| static const unsigned int ldo5_6_volt_table[] = { |
| 1700000, 1800000, 1900000, 2500000, 2800000, 2900000, 3100000, 3300000, |
| }; |
| |
| static const unsigned int pm813s_ldo_volt_table[] = { |
| 1200000, 1250000, 1600000, 1650000, 1700000, 1750000, 1800000, 1850000, |
| 1900000, 2750000, 2800000, 2850000, 2900000, 3000000, 3100000, 3300000, |
| }; |
| |
| int pm813_set_mode(struct regulator_dev *rdev, unsigned int mode) |
| { |
| struct pm813_regulator_info *info = rdev_get_drvdata(rdev); |
| unsigned int val, sleep_bit, sleep_enable_mask, reg; |
| |
| sleep_bit = info->sleep_enable_bit; |
| sleep_enable_mask = (0x3 << sleep_bit); |
| |
| /* |
| * if mode is REGULATOR_SLEEP_MODE_EN, enable related LDO sleep mode |
| * else disable related LDO sleep mode. |
| */ |
| switch (mode) { |
| case REGULATOR_MODE_IDLE: |
| val = (0x2 << sleep_bit); |
| break; |
| case REGULATOR_MODE_NORMAL: |
| val = (0x3 << sleep_bit); |
| break; |
| default: |
| dev_err(rdev_get_dev(rdev), "No right mode to set!\n"); |
| return -EINVAL; |
| } |
| |
| reg = info->sleep_enable_reg; |
| |
| return regmap_update_bits(rdev->regmap, reg, sleep_enable_mask, val); |
| } |
| |
| static unsigned int pm813_get_optimum_mode(struct regulator_dev *rdev, |
| int input_uV, int output_uV, |
| int output_uA) |
| { |
| struct pm813_regulator_info *info = rdev_get_drvdata(rdev); |
| |
| if (info == NULL) { |
| dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); |
| return REGULATOR_MODE_IDLE; |
| } |
| if (output_uA < 0) { |
| dev_err(rdev_get_dev(rdev), "wrong current input, current < 0!!!\n"); |
| return REGULATOR_MODE_IDLE; |
| } |
| /* |
| * get_optimum_mode be called at enbale/disable_regulator function. |
| * If output_uA is not set it will be 0, |
| * set its defult value to be REGULATOR_MODE_IDLE. |
| */ |
| return (MAX_SLEEP_CURRENT > output_uA) ? REGULATOR_MODE_IDLE : |
| REGULATOR_MODE_NORMAL; |
| } |
| static int pm813_get_current_limit(struct regulator_dev *rdev) |
| { |
| struct pm813_regulator_info *info = rdev_get_drvdata(rdev); |
| |
| return info->max_ua; |
| } |
| |
| static int pm813_set_voltage(struct regulator_dev *rdev, |
| int min_uv, int max_uv, unsigned *selector) |
| { |
| struct pm813_regulator_info *info = rdev_get_drvdata(rdev); |
| struct pm813_regulator_volt_range *range; |
| int i, best_index = -1; |
| |
| if (info->volt == NULL) |
| return -EINVAL; |
| |
| /* |
| * Ranges are sorted in ascending order. So if we found a best_uv |
| * in this range, we can break out. |
| */ |
| for (i = 0; i < info->ranges; i++) { |
| range = &info->volt[i]; |
| |
| if (min_uv <= range->max_uv && max_uv >= range->min_uv) { |
| if (min_uv <= range->min_uv) |
| best_index = 0; |
| else |
| best_index = (min_uv - range->min_uv + |
| range->step_uv - 1) / range->step_uv; |
| break; |
| } |
| } |
| |
| if (best_index == -1) |
| return -EINVAL; |
| |
| *selector = best_index + range->min_val; |
| |
| return regulator_set_voltage_sel_regmap(rdev, *selector); |
| } |
| |
| static int pm813_get_voltage(struct regulator_dev *rdev) |
| { |
| struct pm813_regulator_info *info = rdev_get_drvdata(rdev); |
| struct pm813_regulator_volt_range *range; |
| int i, val, max_val, volt = -EINVAL; |
| |
| if (info->volt == NULL) |
| return -EINVAL; |
| |
| val = regulator_get_voltage_sel_regmap(rdev); |
| if (val < 0) |
| return val; |
| |
| /* get the voltage via the register value */ |
| for (i = 0; i < info->ranges; i++) { |
| range = &info->volt[i]; |
| max_val = (range->max_uv - range->min_uv) / range->step_uv |
| + range->min_val; |
| |
| if (val >= range->min_val && val <= max_val) { |
| volt = (val - range->min_val) * range->step_uv |
| + range->min_uv; |
| break; |
| } |
| } |
| |
| return volt; |
| } |
| |
| static struct regulator_ops pm813_volt_range_ops = { |
| .set_voltage = pm813_set_voltage, |
| .get_voltage = pm813_get_voltage, |
| .enable = regulator_enable_regmap, |
| .disable = regulator_disable_regmap, |
| .is_enabled = regulator_is_enabled_regmap, |
| .get_current_limit = pm813_get_current_limit, |
| .get_optimum_mode = pm813_get_optimum_mode, |
| .set_mode = pm813_set_mode, |
| }; |
| |
| static struct regulator_ops pm813_volt_table_ops = { |
| .list_voltage = regulator_list_voltage_table, |
| .map_voltage = regulator_map_voltage_iterate, |
| .set_voltage_sel = regulator_set_voltage_sel_regmap, |
| .get_voltage_sel = regulator_get_voltage_sel_regmap, |
| .enable = regulator_enable_regmap, |
| .disable = regulator_disable_regmap, |
| .is_enabled = regulator_is_enabled_regmap, |
| .get_current_limit = pm813_get_current_limit, |
| .get_optimum_mode = pm813_get_optimum_mode, |
| .set_mode = pm813_set_mode, |
| }; |
| |
| /* The array is indexed by id(PM813_ID_XXX) */ |
| static struct pm813_regulator_info pm813_regulator_info[] = { |
| PM813_BUCK(BUCK1, BUCK1_ENA, 7, 1600000, 3, BUCK_SLP1, |
| buck1_5_volt_range), |
| PM813_BUCK(BUCK2, BUCK2_ENA, 7, 800000, 3, BUCK_SLP2, |
| buck1_5_volt_range), |
| PM813_BUCK(BUCK3, BUCK3_ENA, 7, 800000, 3, BUCK_SLP3, |
| buck1_5_volt_range), |
| |
| PM813_LDO(LDO1, LDO1_ENA, 6, 200000, 4, LDO_SLP1, |
| ldo1_4_volt_table), |
| PM813_LDO(LDO2, LDO2_ENA, 6, 200000, 4, LDO_SLP2, |
| ldo1_4_volt_table), |
| PM813_LDO(LDO3, LDO3_ENA, 6, 200000, 4, LDO_SLP3, |
| ldo1_4_volt_table), |
| PM813_LDO(LDO4, LDO4_ENA, 6, 200000, 4, LDO_SLP4, |
| ldo1_4_volt_table), |
| PM813_LDO(LDO5, LDO5_ENA, 6, 400000, 4, LDO_SLP5, |
| ldo5_6_volt_table), |
| PM813_LDO(LDO6, LDO6_ENA, 6, 200000, 4, LDO_SLP6, |
| ldo1_4_volt_table), |
| |
| PM813_LDO(LDO7, LDO7_ENA, 6, 200000, 4, LDO_SLP7, |
| ldo1_4_volt_table), |
| PM813_LDO(LDO8, LDO8_ENA, 6, 200000, 4, LDO_SLP8, |
| ldo1_4_volt_table), |
| PM813_LDO(LDO9, LDO9_ENA, 6, 200000, 4, LDO_SLP9, |
| ldo1_4_volt_table), |
| PM813_LDO(LDO10, LDO10_ENA, 6, 200000, 4, LDO_SLP10, |
| ldo1_4_volt_table), |
| PM813_LDO(LDO11, LDO11_ENA, 6, 200000, 4, LDO_SLP11, |
| ldo1_4_volt_table), |
| PM813_LDO(LDO12, LDO12_ENA, 6, 200000, 4, LDO_SLP12, |
| ldo1_4_volt_table), |
| PM813_LDO(LDO13, LDO13_ENA, 6, 400000, 4, LDO_SLP13, |
| ldo5_6_volt_table), |
| |
| |
| }; |
| |
| #define PM813_REGULATOR_OF_MATCH(id) \ |
| { \ |
| .name = "PM813-" #id, \ |
| .driver_data = &pm813_regulator_info[PM813_ID_##id], \ |
| } |
| |
| static struct of_regulator_match pm813_regulator_matches[] = { |
| PM813_REGULATOR_OF_MATCH(BUCK1), |
| PM813_REGULATOR_OF_MATCH(BUCK2), |
| PM813_REGULATOR_OF_MATCH(BUCK3), |
| |
| PM813_REGULATOR_OF_MATCH(LDO1), |
| PM813_REGULATOR_OF_MATCH(LDO2), |
| /* PM813_REGULATOR_OF_MATCH(LDO3), */ |
| PM813_REGULATOR_OF_MATCH(LDO4), |
| PM813_REGULATOR_OF_MATCH(LDO5), |
| PM813_REGULATOR_OF_MATCH(LDO6), |
| PM813_REGULATOR_OF_MATCH(LDO7), |
| /* PM813_REGULATOR_OF_MATCH(LDO8), */ |
| PM813_REGULATOR_OF_MATCH(LDO9), |
| PM813_REGULATOR_OF_MATCH(LDO10), |
| PM813_REGULATOR_OF_MATCH(LDO11), |
| PM813_REGULATOR_OF_MATCH(LDO12), |
| PM813_REGULATOR_OF_MATCH(LDO13), |
| }; |
| |
| static struct of_regulator_match pm813_regulator_matches_1906[] = { |
| PM813_REGULATOR_OF_MATCH(BUCK1), |
| PM813_REGULATOR_OF_MATCH(BUCK2), |
| PM813_REGULATOR_OF_MATCH(BUCK3), |
| |
| PM813_REGULATOR_OF_MATCH(LDO1), |
| PM813_REGULATOR_OF_MATCH(LDO2), |
| /* PM813_REGULATOR_OF_MATCH(LDO3), */ |
| PM813_REGULATOR_OF_MATCH(LDO4), |
| PM813_REGULATOR_OF_MATCH(LDO5), |
| PM813_REGULATOR_OF_MATCH(LDO6), |
| PM813_REGULATOR_OF_MATCH(LDO7), |
| PM813_REGULATOR_OF_MATCH(LDO8), |
| PM813_REGULATOR_OF_MATCH(LDO9), |
| PM813_REGULATOR_OF_MATCH(LDO10), |
| PM813_REGULATOR_OF_MATCH(LDO11), |
| PM813_REGULATOR_OF_MATCH(LDO12), |
| PM813_REGULATOR_OF_MATCH(LDO13), |
| }; |
| |
| static int pm813_regulator_dt_init(struct platform_device *pdev, |
| struct of_regulator_match **regulator_matches, int *range) |
| { |
| struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent); |
| struct device_node *np = pdev->dev.of_node; |
| |
| if (chip->type == CHIP_PM813) { |
| if (cpu_is_asr1906()) { |
| *regulator_matches = pm813_regulator_matches_1906; |
| *range = ARRAY_SIZE(pm813_regulator_matches_1906); |
| } |
| else { |
| *regulator_matches = pm813_regulator_matches; |
| *range = ARRAY_SIZE(pm813_regulator_matches); |
| } |
| } else |
| return -ENODEV; |
| |
| return of_regulator_match(&pdev->dev, np, *regulator_matches, *range); |
| } |
| |
| static int pm813_regulator_probe(struct platform_device *pdev) |
| { |
| struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent); |
| struct pm80x_platform_data *pdata = pdev->dev.parent->platform_data; |
| struct pm813_regulators *pm813_data; |
| struct pm813_regulator_info *info; |
| struct regulator_config config = { }; |
| struct regulator_init_data *init_data; |
| int i, ret, range = 0; |
| struct of_regulator_match *regulator_matches = NULL; |
| |
| if (!pdata || pdata->num_regulators == 0) { |
| if (IS_ENABLED(CONFIG_OF)) { |
| ret = pm813_regulator_dt_init(pdev, ®ulator_matches, |
| &range); |
| if (ret < 0) |
| return ret; |
| } else { |
| return -ENODEV; |
| } |
| } else if (pdata->num_regulators) { |
| /* Check whether num_regulator is valid. */ |
| unsigned int count = 0; |
| for (i = 0; pdata->regulators[i]; i++) |
| count++; |
| if (count != pdata->num_regulators) |
| return -EINVAL; |
| } else { |
| return -EINVAL; |
| } |
| |
| pm813_data = devm_kzalloc(&pdev->dev, sizeof(*pm813_data), |
| GFP_KERNEL); |
| if (!pm813_data) { |
| dev_err(&pdev->dev, "Failed to allocate PM813_regualtors"); |
| return -ENOMEM; |
| } |
| |
| pm813_data->map = chip->subchip->regmap_power; |
| pm813_data->chip = chip; |
| |
| platform_set_drvdata(pdev, pm813_data); |
| |
| for (i = 0; i < range; i++) { |
| if (!pdata || pdata->num_regulators == 0) |
| init_data = regulator_matches->init_data; |
| else |
| init_data = pdata->regulators[i]; |
| if (!init_data) { |
| dev_err(&pdev->dev, "%s not matched!\n", |
| regulator_matches->name); |
| regulator_matches++; |
| continue; |
| } |
| info = regulator_matches->driver_data; |
| config.dev = &pdev->dev; |
| config.init_data = init_data; |
| config.driver_data = info; |
| config.regmap = pm813_data->map; |
| config.of_node = regulator_matches->of_node; |
| |
| pm813_data->regulators[i] = |
| regulator_register(&info->desc, &config); |
| if (IS_ERR(pm813_data->regulators[i])) { |
| ret = PTR_ERR(pm813_data->regulators[i]); |
| dev_err(&pdev->dev, "Failed to register %s\n", |
| info->desc.name); |
| |
| while (--i >= 0 && pm813_data->regulators[i]) |
| regulator_unregister(pm813_data->regulators[i]); |
| |
| return ret; |
| } |
| pm813_data->regulators[i]->constraints->valid_ops_mask |= |
| (REGULATOR_CHANGE_DRMS | REGULATOR_CHANGE_MODE); |
| pm813_data->regulators[i]->constraints->valid_modes_mask |= |
| (REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE); |
| pm813_data->regulators[i]->constraints->input_uV = 1000; |
| |
| regulator_matches++; |
| } |
| |
| return 0; |
| } |
| |
| static int pm813_regulator_remove(struct platform_device *pdev) |
| { |
| struct pm813_regulators *pm813_data = platform_get_drvdata(pdev); |
| int i; |
| |
| for (i = 0; pm813_data->regulators[i] && i < PM813_ID_RG_MAX; i++) |
| regulator_unregister(pm813_data->regulators[i]); |
| |
| return 0; |
| } |
| |
| static struct platform_driver pm813_regulator_driver = { |
| .driver = { |
| .name = "pm813-regulator", |
| .owner = THIS_MODULE, |
| }, |
| .probe = pm813_regulator_probe, |
| .remove = pm813_regulator_remove, |
| }; |
| |
| static int __init pm813_regulator_init(void) |
| { |
| return platform_driver_register(&pm813_regulator_driver); |
| } |
| subsys_initcall(pm813_regulator_init); |
| |
| static void __exit pm813_regulator_exit(void) |
| { |
| platform_driver_unregister(&pm813_regulator_driver); |
| } |
| module_exit(pm813_regulator_exit); |
| |
| MODULE_LICENSE("GPL"); |
| MODULE_DESCRIPTION("Regulator Driver for ASR PM813 PMIC"); |
| MODULE_ALIAS("platform:PM813-regulator"); |