19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 29dee7a72SMatthias Kaehlcke /* 39dee7a72SMatthias Kaehlcke * Driver for voltage controller regulators 49dee7a72SMatthias Kaehlcke * 59dee7a72SMatthias Kaehlcke * Copyright (C) 2017 Google, Inc. 69dee7a72SMatthias Kaehlcke */ 79dee7a72SMatthias Kaehlcke 89dee7a72SMatthias Kaehlcke #include <linux/delay.h> 99dee7a72SMatthias Kaehlcke #include <linux/err.h> 109dee7a72SMatthias Kaehlcke #include <linux/init.h> 119dee7a72SMatthias Kaehlcke #include <linux/module.h> 129dee7a72SMatthias Kaehlcke #include <linux/of.h> 13*045a44d4SRob Herring #include <linux/platform_device.h> 14e9153311SEnric Balletbo i Serra #include <linux/regulator/coupler.h> 159dee7a72SMatthias Kaehlcke #include <linux/regulator/driver.h> 169dee7a72SMatthias Kaehlcke #include <linux/regulator/of_regulator.h> 179dee7a72SMatthias Kaehlcke #include <linux/sort.h> 189dee7a72SMatthias Kaehlcke 19e9153311SEnric Balletbo i Serra #include "internal.h" 20e9153311SEnric Balletbo i Serra 219dee7a72SMatthias Kaehlcke struct vctrl_voltage_range { 229dee7a72SMatthias Kaehlcke int min_uV; 239dee7a72SMatthias Kaehlcke int max_uV; 249dee7a72SMatthias Kaehlcke }; 259dee7a72SMatthias Kaehlcke 269dee7a72SMatthias Kaehlcke struct vctrl_voltage_ranges { 279dee7a72SMatthias Kaehlcke struct vctrl_voltage_range ctrl; 289dee7a72SMatthias Kaehlcke struct vctrl_voltage_range out; 299dee7a72SMatthias Kaehlcke }; 309dee7a72SMatthias Kaehlcke 319dee7a72SMatthias Kaehlcke struct vctrl_voltage_table { 329dee7a72SMatthias Kaehlcke int ctrl; 339dee7a72SMatthias Kaehlcke int out; 349dee7a72SMatthias Kaehlcke int ovp_min_sel; 359dee7a72SMatthias Kaehlcke }; 369dee7a72SMatthias Kaehlcke 379dee7a72SMatthias Kaehlcke struct vctrl_data { 389dee7a72SMatthias Kaehlcke struct regulator_dev *rdev; 399dee7a72SMatthias Kaehlcke struct regulator_desc desc; 409dee7a72SMatthias Kaehlcke bool enabled; 419dee7a72SMatthias Kaehlcke unsigned int min_slew_down_rate; 429dee7a72SMatthias Kaehlcke unsigned int ovp_threshold; 439dee7a72SMatthias Kaehlcke struct vctrl_voltage_ranges vrange; 449dee7a72SMatthias Kaehlcke struct vctrl_voltage_table *vtable; 459dee7a72SMatthias Kaehlcke unsigned int sel; 469dee7a72SMatthias Kaehlcke }; 479dee7a72SMatthias Kaehlcke 489dee7a72SMatthias Kaehlcke static int vctrl_calc_ctrl_voltage(struct vctrl_data *vctrl, int out_uV) 499dee7a72SMatthias Kaehlcke { 509dee7a72SMatthias Kaehlcke struct vctrl_voltage_range *ctrl = &vctrl->vrange.ctrl; 519dee7a72SMatthias Kaehlcke struct vctrl_voltage_range *out = &vctrl->vrange.out; 529dee7a72SMatthias Kaehlcke 539dee7a72SMatthias Kaehlcke return ctrl->min_uV + 549dee7a72SMatthias Kaehlcke DIV_ROUND_CLOSEST_ULL((s64)(out_uV - out->min_uV) * 559dee7a72SMatthias Kaehlcke (ctrl->max_uV - ctrl->min_uV), 569dee7a72SMatthias Kaehlcke out->max_uV - out->min_uV); 579dee7a72SMatthias Kaehlcke } 589dee7a72SMatthias Kaehlcke 599dee7a72SMatthias Kaehlcke static int vctrl_calc_output_voltage(struct vctrl_data *vctrl, int ctrl_uV) 609dee7a72SMatthias Kaehlcke { 619dee7a72SMatthias Kaehlcke struct vctrl_voltage_range *ctrl = &vctrl->vrange.ctrl; 629dee7a72SMatthias Kaehlcke struct vctrl_voltage_range *out = &vctrl->vrange.out; 639dee7a72SMatthias Kaehlcke 649dee7a72SMatthias Kaehlcke if (ctrl_uV < 0) { 659dee7a72SMatthias Kaehlcke pr_err("vctrl: failed to get control voltage\n"); 669dee7a72SMatthias Kaehlcke return ctrl_uV; 679dee7a72SMatthias Kaehlcke } 689dee7a72SMatthias Kaehlcke 699dee7a72SMatthias Kaehlcke if (ctrl_uV < ctrl->min_uV) 709dee7a72SMatthias Kaehlcke return out->min_uV; 719dee7a72SMatthias Kaehlcke 729dee7a72SMatthias Kaehlcke if (ctrl_uV > ctrl->max_uV) 739dee7a72SMatthias Kaehlcke return out->max_uV; 749dee7a72SMatthias Kaehlcke 759dee7a72SMatthias Kaehlcke return out->min_uV + 769dee7a72SMatthias Kaehlcke DIV_ROUND_CLOSEST_ULL((s64)(ctrl_uV - ctrl->min_uV) * 779dee7a72SMatthias Kaehlcke (out->max_uV - out->min_uV), 789dee7a72SMatthias Kaehlcke ctrl->max_uV - ctrl->min_uV); 799dee7a72SMatthias Kaehlcke } 809dee7a72SMatthias Kaehlcke 819dee7a72SMatthias Kaehlcke static int vctrl_get_voltage(struct regulator_dev *rdev) 829dee7a72SMatthias Kaehlcke { 839dee7a72SMatthias Kaehlcke struct vctrl_data *vctrl = rdev_get_drvdata(rdev); 8421e39809SChen-Yu Tsai int ctrl_uV; 8521e39809SChen-Yu Tsai 8621e39809SChen-Yu Tsai if (!rdev->supply) 8721e39809SChen-Yu Tsai return -EPROBE_DEFER; 8821e39809SChen-Yu Tsai 8921e39809SChen-Yu Tsai ctrl_uV = regulator_get_voltage_rdev(rdev->supply->rdev); 909dee7a72SMatthias Kaehlcke 919dee7a72SMatthias Kaehlcke return vctrl_calc_output_voltage(vctrl, ctrl_uV); 929dee7a72SMatthias Kaehlcke } 939dee7a72SMatthias Kaehlcke 949dee7a72SMatthias Kaehlcke static int vctrl_set_voltage(struct regulator_dev *rdev, 959dee7a72SMatthias Kaehlcke int req_min_uV, int req_max_uV, 969dee7a72SMatthias Kaehlcke unsigned int *selector) 979dee7a72SMatthias Kaehlcke { 989dee7a72SMatthias Kaehlcke struct vctrl_data *vctrl = rdev_get_drvdata(rdev); 9921e39809SChen-Yu Tsai int orig_ctrl_uV; 10021e39809SChen-Yu Tsai int uV; 1019dee7a72SMatthias Kaehlcke int ret; 1029dee7a72SMatthias Kaehlcke 10321e39809SChen-Yu Tsai if (!rdev->supply) 10421e39809SChen-Yu Tsai return -EPROBE_DEFER; 10521e39809SChen-Yu Tsai 10621e39809SChen-Yu Tsai orig_ctrl_uV = regulator_get_voltage_rdev(rdev->supply->rdev); 10721e39809SChen-Yu Tsai uV = vctrl_calc_output_voltage(vctrl, orig_ctrl_uV); 10821e39809SChen-Yu Tsai 1099dee7a72SMatthias Kaehlcke if (req_min_uV >= uV || !vctrl->ovp_threshold) 1109dee7a72SMatthias Kaehlcke /* voltage rising or no OVP */ 11121e39809SChen-Yu Tsai return regulator_set_voltage_rdev(rdev->supply->rdev, 1129dee7a72SMatthias Kaehlcke vctrl_calc_ctrl_voltage(vctrl, req_min_uV), 113e9153311SEnric Balletbo i Serra vctrl_calc_ctrl_voltage(vctrl, req_max_uV), 114e9153311SEnric Balletbo i Serra PM_SUSPEND_ON); 1159dee7a72SMatthias Kaehlcke 1169dee7a72SMatthias Kaehlcke while (uV > req_min_uV) { 1179dee7a72SMatthias Kaehlcke int max_drop_uV = (uV * vctrl->ovp_threshold) / 100; 1189dee7a72SMatthias Kaehlcke int next_uV; 1199dee7a72SMatthias Kaehlcke int next_ctrl_uV; 1209dee7a72SMatthias Kaehlcke int delay; 1219dee7a72SMatthias Kaehlcke 1229dee7a72SMatthias Kaehlcke /* Make sure no infinite loop even in crazy cases */ 1239dee7a72SMatthias Kaehlcke if (max_drop_uV == 0) 1249dee7a72SMatthias Kaehlcke max_drop_uV = 1; 1259dee7a72SMatthias Kaehlcke 1269dee7a72SMatthias Kaehlcke next_uV = max_t(int, req_min_uV, uV - max_drop_uV); 1279dee7a72SMatthias Kaehlcke next_ctrl_uV = vctrl_calc_ctrl_voltage(vctrl, next_uV); 1289dee7a72SMatthias Kaehlcke 12921e39809SChen-Yu Tsai ret = regulator_set_voltage_rdev(rdev->supply->rdev, 1309dee7a72SMatthias Kaehlcke next_ctrl_uV, 131e9153311SEnric Balletbo i Serra next_ctrl_uV, 132e9153311SEnric Balletbo i Serra PM_SUSPEND_ON); 1339dee7a72SMatthias Kaehlcke if (ret) 1349dee7a72SMatthias Kaehlcke goto err; 1359dee7a72SMatthias Kaehlcke 1369dee7a72SMatthias Kaehlcke delay = DIV_ROUND_UP(uV - next_uV, vctrl->min_slew_down_rate); 1379dee7a72SMatthias Kaehlcke usleep_range(delay, delay + DIV_ROUND_UP(delay, 10)); 1389dee7a72SMatthias Kaehlcke 1399dee7a72SMatthias Kaehlcke uV = next_uV; 1409dee7a72SMatthias Kaehlcke } 1419dee7a72SMatthias Kaehlcke 1429dee7a72SMatthias Kaehlcke return 0; 1439dee7a72SMatthias Kaehlcke 1449dee7a72SMatthias Kaehlcke err: 1459dee7a72SMatthias Kaehlcke /* Try to go back to original voltage */ 14621e39809SChen-Yu Tsai regulator_set_voltage_rdev(rdev->supply->rdev, orig_ctrl_uV, orig_ctrl_uV, 147e9153311SEnric Balletbo i Serra PM_SUSPEND_ON); 1489dee7a72SMatthias Kaehlcke 1499dee7a72SMatthias Kaehlcke return ret; 1509dee7a72SMatthias Kaehlcke } 1519dee7a72SMatthias Kaehlcke 1529dee7a72SMatthias Kaehlcke static int vctrl_get_voltage_sel(struct regulator_dev *rdev) 1539dee7a72SMatthias Kaehlcke { 1549dee7a72SMatthias Kaehlcke struct vctrl_data *vctrl = rdev_get_drvdata(rdev); 1559dee7a72SMatthias Kaehlcke 1569dee7a72SMatthias Kaehlcke return vctrl->sel; 1579dee7a72SMatthias Kaehlcke } 1589dee7a72SMatthias Kaehlcke 1599dee7a72SMatthias Kaehlcke static int vctrl_set_voltage_sel(struct regulator_dev *rdev, 1609dee7a72SMatthias Kaehlcke unsigned int selector) 1619dee7a72SMatthias Kaehlcke { 1629dee7a72SMatthias Kaehlcke struct vctrl_data *vctrl = rdev_get_drvdata(rdev); 1639dee7a72SMatthias Kaehlcke unsigned int orig_sel = vctrl->sel; 1649dee7a72SMatthias Kaehlcke int ret; 1659dee7a72SMatthias Kaehlcke 16621e39809SChen-Yu Tsai if (!rdev->supply) 16721e39809SChen-Yu Tsai return -EPROBE_DEFER; 16821e39809SChen-Yu Tsai 1699dee7a72SMatthias Kaehlcke if (selector >= rdev->desc->n_voltages) 1709dee7a72SMatthias Kaehlcke return -EINVAL; 1719dee7a72SMatthias Kaehlcke 1729dee7a72SMatthias Kaehlcke if (selector >= vctrl->sel || !vctrl->ovp_threshold) { 1739dee7a72SMatthias Kaehlcke /* voltage rising or no OVP */ 17421e39809SChen-Yu Tsai ret = regulator_set_voltage_rdev(rdev->supply->rdev, 1759dee7a72SMatthias Kaehlcke vctrl->vtable[selector].ctrl, 176e9153311SEnric Balletbo i Serra vctrl->vtable[selector].ctrl, 177e9153311SEnric Balletbo i Serra PM_SUSPEND_ON); 1789dee7a72SMatthias Kaehlcke if (!ret) 1799dee7a72SMatthias Kaehlcke vctrl->sel = selector; 1809dee7a72SMatthias Kaehlcke 1819dee7a72SMatthias Kaehlcke return ret; 1829dee7a72SMatthias Kaehlcke } 1839dee7a72SMatthias Kaehlcke 1849dee7a72SMatthias Kaehlcke while (vctrl->sel != selector) { 1859dee7a72SMatthias Kaehlcke unsigned int next_sel; 1869dee7a72SMatthias Kaehlcke int delay; 1879dee7a72SMatthias Kaehlcke 1888a317e00SHaowen Bai next_sel = max_t(unsigned int, selector, vctrl->vtable[vctrl->sel].ovp_min_sel); 1899dee7a72SMatthias Kaehlcke 19021e39809SChen-Yu Tsai ret = regulator_set_voltage_rdev(rdev->supply->rdev, 1919dee7a72SMatthias Kaehlcke vctrl->vtable[next_sel].ctrl, 192e9153311SEnric Balletbo i Serra vctrl->vtable[next_sel].ctrl, 193e9153311SEnric Balletbo i Serra PM_SUSPEND_ON); 1949dee7a72SMatthias Kaehlcke if (ret) { 1959dee7a72SMatthias Kaehlcke dev_err(&rdev->dev, 1969dee7a72SMatthias Kaehlcke "failed to set control voltage to %duV\n", 1979dee7a72SMatthias Kaehlcke vctrl->vtable[next_sel].ctrl); 1989dee7a72SMatthias Kaehlcke goto err; 1999dee7a72SMatthias Kaehlcke } 2009dee7a72SMatthias Kaehlcke vctrl->sel = next_sel; 2019dee7a72SMatthias Kaehlcke 2029dee7a72SMatthias Kaehlcke delay = DIV_ROUND_UP(vctrl->vtable[vctrl->sel].out - 2039dee7a72SMatthias Kaehlcke vctrl->vtable[next_sel].out, 2049dee7a72SMatthias Kaehlcke vctrl->min_slew_down_rate); 2059dee7a72SMatthias Kaehlcke usleep_range(delay, delay + DIV_ROUND_UP(delay, 10)); 2069dee7a72SMatthias Kaehlcke } 2079dee7a72SMatthias Kaehlcke 2089dee7a72SMatthias Kaehlcke return 0; 2099dee7a72SMatthias Kaehlcke 2109dee7a72SMatthias Kaehlcke err: 2119dee7a72SMatthias Kaehlcke if (vctrl->sel != orig_sel) { 2129dee7a72SMatthias Kaehlcke /* Try to go back to original voltage */ 21321e39809SChen-Yu Tsai if (!regulator_set_voltage_rdev(rdev->supply->rdev, 2149dee7a72SMatthias Kaehlcke vctrl->vtable[orig_sel].ctrl, 215e9153311SEnric Balletbo i Serra vctrl->vtable[orig_sel].ctrl, 216e9153311SEnric Balletbo i Serra PM_SUSPEND_ON)) 2179dee7a72SMatthias Kaehlcke vctrl->sel = orig_sel; 2189dee7a72SMatthias Kaehlcke else 2199dee7a72SMatthias Kaehlcke dev_warn(&rdev->dev, 2209dee7a72SMatthias Kaehlcke "failed to restore original voltage\n"); 2219dee7a72SMatthias Kaehlcke } 2229dee7a72SMatthias Kaehlcke 2239dee7a72SMatthias Kaehlcke return ret; 2249dee7a72SMatthias Kaehlcke } 2259dee7a72SMatthias Kaehlcke 2269dee7a72SMatthias Kaehlcke static int vctrl_list_voltage(struct regulator_dev *rdev, 2279dee7a72SMatthias Kaehlcke unsigned int selector) 2289dee7a72SMatthias Kaehlcke { 2299dee7a72SMatthias Kaehlcke struct vctrl_data *vctrl = rdev_get_drvdata(rdev); 2309dee7a72SMatthias Kaehlcke 2319dee7a72SMatthias Kaehlcke if (selector >= rdev->desc->n_voltages) 2329dee7a72SMatthias Kaehlcke return -EINVAL; 2339dee7a72SMatthias Kaehlcke 2349dee7a72SMatthias Kaehlcke return vctrl->vtable[selector].out; 2359dee7a72SMatthias Kaehlcke } 2369dee7a72SMatthias Kaehlcke 2379dee7a72SMatthias Kaehlcke static int vctrl_parse_dt(struct platform_device *pdev, 2389dee7a72SMatthias Kaehlcke struct vctrl_data *vctrl) 2399dee7a72SMatthias Kaehlcke { 2409dee7a72SMatthias Kaehlcke int ret; 2419dee7a72SMatthias Kaehlcke struct device_node *np = pdev->dev.of_node; 2429dee7a72SMatthias Kaehlcke u32 pval; 2439dee7a72SMatthias Kaehlcke u32 vrange_ctrl[2]; 2449dee7a72SMatthias Kaehlcke 2459dee7a72SMatthias Kaehlcke ret = of_property_read_u32(np, "ovp-threshold-percent", &pval); 2469dee7a72SMatthias Kaehlcke if (!ret) { 2479dee7a72SMatthias Kaehlcke vctrl->ovp_threshold = pval; 2489dee7a72SMatthias Kaehlcke if (vctrl->ovp_threshold > 100) { 2499dee7a72SMatthias Kaehlcke dev_err(&pdev->dev, 2509dee7a72SMatthias Kaehlcke "ovp-threshold-percent (%u) > 100\n", 2519dee7a72SMatthias Kaehlcke vctrl->ovp_threshold); 2529dee7a72SMatthias Kaehlcke return -EINVAL; 2539dee7a72SMatthias Kaehlcke } 2549dee7a72SMatthias Kaehlcke } 2559dee7a72SMatthias Kaehlcke 2569dee7a72SMatthias Kaehlcke ret = of_property_read_u32(np, "min-slew-down-rate", &pval); 2579dee7a72SMatthias Kaehlcke if (!ret) { 2589dee7a72SMatthias Kaehlcke vctrl->min_slew_down_rate = pval; 2599dee7a72SMatthias Kaehlcke 2609dee7a72SMatthias Kaehlcke /* We use the value as int and as divider; sanity check */ 2619dee7a72SMatthias Kaehlcke if (vctrl->min_slew_down_rate == 0) { 2629dee7a72SMatthias Kaehlcke dev_err(&pdev->dev, 2639dee7a72SMatthias Kaehlcke "min-slew-down-rate must not be 0\n"); 2649dee7a72SMatthias Kaehlcke return -EINVAL; 2659dee7a72SMatthias Kaehlcke } else if (vctrl->min_slew_down_rate > INT_MAX) { 2669dee7a72SMatthias Kaehlcke dev_err(&pdev->dev, "min-slew-down-rate (%u) too big\n", 2679dee7a72SMatthias Kaehlcke vctrl->min_slew_down_rate); 2689dee7a72SMatthias Kaehlcke return -EINVAL; 2699dee7a72SMatthias Kaehlcke } 2709dee7a72SMatthias Kaehlcke } 2719dee7a72SMatthias Kaehlcke 2729dee7a72SMatthias Kaehlcke if (vctrl->ovp_threshold && !vctrl->min_slew_down_rate) { 2739dee7a72SMatthias Kaehlcke dev_err(&pdev->dev, 2749dee7a72SMatthias Kaehlcke "ovp-threshold-percent requires min-slew-down-rate\n"); 2759dee7a72SMatthias Kaehlcke return -EINVAL; 2769dee7a72SMatthias Kaehlcke } 2779dee7a72SMatthias Kaehlcke 2789dee7a72SMatthias Kaehlcke ret = of_property_read_u32(np, "regulator-min-microvolt", &pval); 2799dee7a72SMatthias Kaehlcke if (ret) { 2809dee7a72SMatthias Kaehlcke dev_err(&pdev->dev, 2819dee7a72SMatthias Kaehlcke "failed to read regulator-min-microvolt: %d\n", ret); 2829dee7a72SMatthias Kaehlcke return ret; 2839dee7a72SMatthias Kaehlcke } 2849dee7a72SMatthias Kaehlcke vctrl->vrange.out.min_uV = pval; 2859dee7a72SMatthias Kaehlcke 2869dee7a72SMatthias Kaehlcke ret = of_property_read_u32(np, "regulator-max-microvolt", &pval); 2879dee7a72SMatthias Kaehlcke if (ret) { 2889dee7a72SMatthias Kaehlcke dev_err(&pdev->dev, 2899dee7a72SMatthias Kaehlcke "failed to read regulator-max-microvolt: %d\n", ret); 2909dee7a72SMatthias Kaehlcke return ret; 2919dee7a72SMatthias Kaehlcke } 2929dee7a72SMatthias Kaehlcke vctrl->vrange.out.max_uV = pval; 2939dee7a72SMatthias Kaehlcke 2949dee7a72SMatthias Kaehlcke ret = of_property_read_u32_array(np, "ctrl-voltage-range", vrange_ctrl, 2959dee7a72SMatthias Kaehlcke 2); 2969dee7a72SMatthias Kaehlcke if (ret) { 2979dee7a72SMatthias Kaehlcke dev_err(&pdev->dev, "failed to read ctrl-voltage-range: %d\n", 2989dee7a72SMatthias Kaehlcke ret); 2999dee7a72SMatthias Kaehlcke return ret; 3009dee7a72SMatthias Kaehlcke } 3019dee7a72SMatthias Kaehlcke 3029dee7a72SMatthias Kaehlcke if (vrange_ctrl[0] >= vrange_ctrl[1]) { 3039dee7a72SMatthias Kaehlcke dev_err(&pdev->dev, "ctrl-voltage-range is invalid: %d-%d\n", 3049dee7a72SMatthias Kaehlcke vrange_ctrl[0], vrange_ctrl[1]); 3059dee7a72SMatthias Kaehlcke return -EINVAL; 3069dee7a72SMatthias Kaehlcke } 3079dee7a72SMatthias Kaehlcke 3089dee7a72SMatthias Kaehlcke vctrl->vrange.ctrl.min_uV = vrange_ctrl[0]; 3099dee7a72SMatthias Kaehlcke vctrl->vrange.ctrl.max_uV = vrange_ctrl[1]; 3109dee7a72SMatthias Kaehlcke 3119dee7a72SMatthias Kaehlcke return 0; 3129dee7a72SMatthias Kaehlcke } 3139dee7a72SMatthias Kaehlcke 3149dee7a72SMatthias Kaehlcke static int vctrl_cmp_ctrl_uV(const void *a, const void *b) 3159dee7a72SMatthias Kaehlcke { 3169dee7a72SMatthias Kaehlcke const struct vctrl_voltage_table *at = a; 3179dee7a72SMatthias Kaehlcke const struct vctrl_voltage_table *bt = b; 3189dee7a72SMatthias Kaehlcke 3199dee7a72SMatthias Kaehlcke return at->ctrl - bt->ctrl; 3209dee7a72SMatthias Kaehlcke } 3219dee7a72SMatthias Kaehlcke 32221e39809SChen-Yu Tsai static int vctrl_init_vtable(struct platform_device *pdev, 32321e39809SChen-Yu Tsai struct regulator *ctrl_reg) 3249dee7a72SMatthias Kaehlcke { 3259dee7a72SMatthias Kaehlcke struct vctrl_data *vctrl = platform_get_drvdata(pdev); 3269dee7a72SMatthias Kaehlcke struct regulator_desc *rdesc = &vctrl->desc; 3279dee7a72SMatthias Kaehlcke struct vctrl_voltage_range *vrange_ctrl = &vctrl->vrange.ctrl; 3289dee7a72SMatthias Kaehlcke int n_voltages; 3299dee7a72SMatthias Kaehlcke int ctrl_uV; 3309dee7a72SMatthias Kaehlcke int i, idx_vt; 3319dee7a72SMatthias Kaehlcke 3329dee7a72SMatthias Kaehlcke n_voltages = regulator_count_voltages(ctrl_reg); 3339dee7a72SMatthias Kaehlcke 3349dee7a72SMatthias Kaehlcke rdesc->n_voltages = n_voltages; 3359dee7a72SMatthias Kaehlcke 3369dee7a72SMatthias Kaehlcke /* determine number of steps within the range of the vctrl regulator */ 3379dee7a72SMatthias Kaehlcke for (i = 0; i < n_voltages; i++) { 3389dee7a72SMatthias Kaehlcke ctrl_uV = regulator_list_voltage(ctrl_reg, i); 3399dee7a72SMatthias Kaehlcke 3409dee7a72SMatthias Kaehlcke if (ctrl_uV < vrange_ctrl->min_uV || 3419e488c0aSAxel Lin ctrl_uV > vrange_ctrl->max_uV) 3429dee7a72SMatthias Kaehlcke rdesc->n_voltages--; 3439dee7a72SMatthias Kaehlcke } 3449dee7a72SMatthias Kaehlcke 3459dee7a72SMatthias Kaehlcke if (rdesc->n_voltages == 0) { 3469dee7a72SMatthias Kaehlcke dev_err(&pdev->dev, "invalid configuration\n"); 3479dee7a72SMatthias Kaehlcke return -EINVAL; 3489dee7a72SMatthias Kaehlcke } 3499dee7a72SMatthias Kaehlcke 350a9bbb453SAxel Lin vctrl->vtable = devm_kcalloc(&pdev->dev, rdesc->n_voltages, 351a9bbb453SAxel Lin sizeof(struct vctrl_voltage_table), 352a9bbb453SAxel Lin GFP_KERNEL); 3539dee7a72SMatthias Kaehlcke if (!vctrl->vtable) 3549dee7a72SMatthias Kaehlcke return -ENOMEM; 3559dee7a72SMatthias Kaehlcke 3569dee7a72SMatthias Kaehlcke /* create mapping control <=> output voltage */ 3579dee7a72SMatthias Kaehlcke for (i = 0, idx_vt = 0; i < n_voltages; i++) { 3589dee7a72SMatthias Kaehlcke ctrl_uV = regulator_list_voltage(ctrl_reg, i); 3599dee7a72SMatthias Kaehlcke 3609dee7a72SMatthias Kaehlcke if (ctrl_uV < vrange_ctrl->min_uV || 3619dee7a72SMatthias Kaehlcke ctrl_uV > vrange_ctrl->max_uV) 3629dee7a72SMatthias Kaehlcke continue; 3639dee7a72SMatthias Kaehlcke 3649dee7a72SMatthias Kaehlcke vctrl->vtable[idx_vt].ctrl = ctrl_uV; 3659dee7a72SMatthias Kaehlcke vctrl->vtable[idx_vt].out = 3669dee7a72SMatthias Kaehlcke vctrl_calc_output_voltage(vctrl, ctrl_uV); 3679dee7a72SMatthias Kaehlcke idx_vt++; 3689dee7a72SMatthias Kaehlcke } 3699dee7a72SMatthias Kaehlcke 3709dee7a72SMatthias Kaehlcke /* we rely on the table to be ordered by ascending voltage */ 3719dee7a72SMatthias Kaehlcke sort(vctrl->vtable, rdesc->n_voltages, 3729dee7a72SMatthias Kaehlcke sizeof(struct vctrl_voltage_table), vctrl_cmp_ctrl_uV, 3739dee7a72SMatthias Kaehlcke NULL); 3749dee7a72SMatthias Kaehlcke 3759dee7a72SMatthias Kaehlcke /* pre-calculate OVP-safe downward transitions */ 376a9bbb453SAxel Lin for (i = rdesc->n_voltages - 1; i > 0; i--) { 3779dee7a72SMatthias Kaehlcke int j; 3789dee7a72SMatthias Kaehlcke int ovp_min_uV = (vctrl->vtable[i].out * 3799dee7a72SMatthias Kaehlcke (100 - vctrl->ovp_threshold)) / 100; 3809dee7a72SMatthias Kaehlcke 3819dee7a72SMatthias Kaehlcke for (j = 0; j < i; j++) { 3829dee7a72SMatthias Kaehlcke if (vctrl->vtable[j].out >= ovp_min_uV) { 3839dee7a72SMatthias Kaehlcke vctrl->vtable[i].ovp_min_sel = j; 3849dee7a72SMatthias Kaehlcke break; 3859dee7a72SMatthias Kaehlcke } 3869dee7a72SMatthias Kaehlcke } 3879dee7a72SMatthias Kaehlcke 3889dee7a72SMatthias Kaehlcke if (j == i) { 3899dee7a72SMatthias Kaehlcke dev_warn(&pdev->dev, "switching down from %duV may cause OVP shutdown\n", 3909dee7a72SMatthias Kaehlcke vctrl->vtable[i].out); 3919dee7a72SMatthias Kaehlcke /* use next lowest voltage */ 3929dee7a72SMatthias Kaehlcke vctrl->vtable[i].ovp_min_sel = i - 1; 3939dee7a72SMatthias Kaehlcke } 3949dee7a72SMatthias Kaehlcke } 3959dee7a72SMatthias Kaehlcke 3969dee7a72SMatthias Kaehlcke return 0; 3979dee7a72SMatthias Kaehlcke } 3989dee7a72SMatthias Kaehlcke 3999dee7a72SMatthias Kaehlcke static int vctrl_enable(struct regulator_dev *rdev) 4009dee7a72SMatthias Kaehlcke { 4019dee7a72SMatthias Kaehlcke struct vctrl_data *vctrl = rdev_get_drvdata(rdev); 4029dee7a72SMatthias Kaehlcke 4039dee7a72SMatthias Kaehlcke vctrl->enabled = true; 4049dee7a72SMatthias Kaehlcke 40521e39809SChen-Yu Tsai return 0; 4069dee7a72SMatthias Kaehlcke } 4079dee7a72SMatthias Kaehlcke 4089dee7a72SMatthias Kaehlcke static int vctrl_disable(struct regulator_dev *rdev) 4099dee7a72SMatthias Kaehlcke { 4109dee7a72SMatthias Kaehlcke struct vctrl_data *vctrl = rdev_get_drvdata(rdev); 4119dee7a72SMatthias Kaehlcke 4129dee7a72SMatthias Kaehlcke vctrl->enabled = false; 4139dee7a72SMatthias Kaehlcke 41421e39809SChen-Yu Tsai return 0; 4159dee7a72SMatthias Kaehlcke } 4169dee7a72SMatthias Kaehlcke 4179dee7a72SMatthias Kaehlcke static int vctrl_is_enabled(struct regulator_dev *rdev) 4189dee7a72SMatthias Kaehlcke { 4199dee7a72SMatthias Kaehlcke struct vctrl_data *vctrl = rdev_get_drvdata(rdev); 4209dee7a72SMatthias Kaehlcke 4219dee7a72SMatthias Kaehlcke return vctrl->enabled; 4229dee7a72SMatthias Kaehlcke } 4239dee7a72SMatthias Kaehlcke 4249dee7a72SMatthias Kaehlcke static const struct regulator_ops vctrl_ops_cont = { 4259dee7a72SMatthias Kaehlcke .enable = vctrl_enable, 4269dee7a72SMatthias Kaehlcke .disable = vctrl_disable, 4279dee7a72SMatthias Kaehlcke .is_enabled = vctrl_is_enabled, 4289dee7a72SMatthias Kaehlcke .get_voltage = vctrl_get_voltage, 4299dee7a72SMatthias Kaehlcke .set_voltage = vctrl_set_voltage, 4309dee7a72SMatthias Kaehlcke }; 4319dee7a72SMatthias Kaehlcke 4329dee7a72SMatthias Kaehlcke static const struct regulator_ops vctrl_ops_non_cont = { 4339dee7a72SMatthias Kaehlcke .enable = vctrl_enable, 4349dee7a72SMatthias Kaehlcke .disable = vctrl_disable, 4359dee7a72SMatthias Kaehlcke .is_enabled = vctrl_is_enabled, 4369dee7a72SMatthias Kaehlcke .set_voltage_sel = vctrl_set_voltage_sel, 4379dee7a72SMatthias Kaehlcke .get_voltage_sel = vctrl_get_voltage_sel, 4389dee7a72SMatthias Kaehlcke .list_voltage = vctrl_list_voltage, 4399dee7a72SMatthias Kaehlcke .map_voltage = regulator_map_voltage_iterate, 4409dee7a72SMatthias Kaehlcke }; 4419dee7a72SMatthias Kaehlcke 4429dee7a72SMatthias Kaehlcke static int vctrl_probe(struct platform_device *pdev) 4439dee7a72SMatthias Kaehlcke { 4449dee7a72SMatthias Kaehlcke struct device_node *np = pdev->dev.of_node; 4459dee7a72SMatthias Kaehlcke struct vctrl_data *vctrl; 4469dee7a72SMatthias Kaehlcke const struct regulator_init_data *init_data; 4479dee7a72SMatthias Kaehlcke struct regulator_desc *rdesc; 4489dee7a72SMatthias Kaehlcke struct regulator_config cfg = { }; 4499dee7a72SMatthias Kaehlcke struct vctrl_voltage_range *vrange_ctrl; 45021e39809SChen-Yu Tsai struct regulator *ctrl_reg; 4519dee7a72SMatthias Kaehlcke int ctrl_uV; 4529dee7a72SMatthias Kaehlcke int ret; 4539dee7a72SMatthias Kaehlcke 4549dee7a72SMatthias Kaehlcke vctrl = devm_kzalloc(&pdev->dev, sizeof(struct vctrl_data), 4559dee7a72SMatthias Kaehlcke GFP_KERNEL); 4569dee7a72SMatthias Kaehlcke if (!vctrl) 4579dee7a72SMatthias Kaehlcke return -ENOMEM; 4589dee7a72SMatthias Kaehlcke 4599dee7a72SMatthias Kaehlcke platform_set_drvdata(pdev, vctrl); 4609dee7a72SMatthias Kaehlcke 4619dee7a72SMatthias Kaehlcke ret = vctrl_parse_dt(pdev, vctrl); 4629dee7a72SMatthias Kaehlcke if (ret) 4639dee7a72SMatthias Kaehlcke return ret; 4649dee7a72SMatthias Kaehlcke 46521e39809SChen-Yu Tsai ctrl_reg = devm_regulator_get(&pdev->dev, "ctrl"); 46621e39809SChen-Yu Tsai if (IS_ERR(ctrl_reg)) 46721e39809SChen-Yu Tsai return PTR_ERR(ctrl_reg); 46821e39809SChen-Yu Tsai 4699dee7a72SMatthias Kaehlcke vrange_ctrl = &vctrl->vrange.ctrl; 4709dee7a72SMatthias Kaehlcke 4719dee7a72SMatthias Kaehlcke rdesc = &vctrl->desc; 4729dee7a72SMatthias Kaehlcke rdesc->name = "vctrl"; 4739dee7a72SMatthias Kaehlcke rdesc->type = REGULATOR_VOLTAGE; 4749dee7a72SMatthias Kaehlcke rdesc->owner = THIS_MODULE; 47521e39809SChen-Yu Tsai rdesc->supply_name = "ctrl"; 4769dee7a72SMatthias Kaehlcke 47721e39809SChen-Yu Tsai if ((regulator_get_linear_step(ctrl_reg) == 1) || 47821e39809SChen-Yu Tsai (regulator_count_voltages(ctrl_reg) == -EINVAL)) { 4799dee7a72SMatthias Kaehlcke rdesc->continuous_voltage_range = true; 4809dee7a72SMatthias Kaehlcke rdesc->ops = &vctrl_ops_cont; 4819dee7a72SMatthias Kaehlcke } else { 4829dee7a72SMatthias Kaehlcke rdesc->ops = &vctrl_ops_non_cont; 4839dee7a72SMatthias Kaehlcke } 4849dee7a72SMatthias Kaehlcke 4859dee7a72SMatthias Kaehlcke init_data = of_get_regulator_init_data(&pdev->dev, np, rdesc); 4869dee7a72SMatthias Kaehlcke if (!init_data) 4879dee7a72SMatthias Kaehlcke return -ENOMEM; 4889dee7a72SMatthias Kaehlcke 4899dee7a72SMatthias Kaehlcke cfg.of_node = np; 4909dee7a72SMatthias Kaehlcke cfg.dev = &pdev->dev; 4919dee7a72SMatthias Kaehlcke cfg.driver_data = vctrl; 4929dee7a72SMatthias Kaehlcke cfg.init_data = init_data; 4939dee7a72SMatthias Kaehlcke 4949dee7a72SMatthias Kaehlcke if (!rdesc->continuous_voltage_range) { 49521e39809SChen-Yu Tsai ret = vctrl_init_vtable(pdev, ctrl_reg); 4969dee7a72SMatthias Kaehlcke if (ret) 4979dee7a72SMatthias Kaehlcke return ret; 4989dee7a72SMatthias Kaehlcke 49998e47570SChen-Yu Tsai /* Use locked consumer API when not in regulator framework */ 50021e39809SChen-Yu Tsai ctrl_uV = regulator_get_voltage(ctrl_reg); 5019dee7a72SMatthias Kaehlcke if (ctrl_uV < 0) { 5029dee7a72SMatthias Kaehlcke dev_err(&pdev->dev, "failed to get control voltage\n"); 5039dee7a72SMatthias Kaehlcke return ctrl_uV; 5049dee7a72SMatthias Kaehlcke } 5059dee7a72SMatthias Kaehlcke 5069dee7a72SMatthias Kaehlcke /* determine current voltage selector from control voltage */ 5079dee7a72SMatthias Kaehlcke if (ctrl_uV < vrange_ctrl->min_uV) { 5089dee7a72SMatthias Kaehlcke vctrl->sel = 0; 5099dee7a72SMatthias Kaehlcke } else if (ctrl_uV > vrange_ctrl->max_uV) { 5109dee7a72SMatthias Kaehlcke vctrl->sel = rdesc->n_voltages - 1; 5119dee7a72SMatthias Kaehlcke } else { 5129dee7a72SMatthias Kaehlcke int i; 5139dee7a72SMatthias Kaehlcke 5149dee7a72SMatthias Kaehlcke for (i = 0; i < rdesc->n_voltages; i++) { 5159dee7a72SMatthias Kaehlcke if (ctrl_uV == vctrl->vtable[i].ctrl) { 5169dee7a72SMatthias Kaehlcke vctrl->sel = i; 5179dee7a72SMatthias Kaehlcke break; 5189dee7a72SMatthias Kaehlcke } 5199dee7a72SMatthias Kaehlcke } 5209dee7a72SMatthias Kaehlcke } 5219dee7a72SMatthias Kaehlcke } 5229dee7a72SMatthias Kaehlcke 52321e39809SChen-Yu Tsai /* Drop ctrl-supply here in favor of regulator core managed supply */ 52421e39809SChen-Yu Tsai devm_regulator_put(ctrl_reg); 52521e39809SChen-Yu Tsai 5269dee7a72SMatthias Kaehlcke vctrl->rdev = devm_regulator_register(&pdev->dev, rdesc, &cfg); 5279dee7a72SMatthias Kaehlcke if (IS_ERR(vctrl->rdev)) { 5289dee7a72SMatthias Kaehlcke ret = PTR_ERR(vctrl->rdev); 5299dee7a72SMatthias Kaehlcke dev_err(&pdev->dev, "failed to register regulator: %d\n", ret); 5309dee7a72SMatthias Kaehlcke return ret; 5319dee7a72SMatthias Kaehlcke } 5329dee7a72SMatthias Kaehlcke 5339dee7a72SMatthias Kaehlcke return 0; 5349dee7a72SMatthias Kaehlcke } 5359dee7a72SMatthias Kaehlcke 5369dee7a72SMatthias Kaehlcke static const struct of_device_id vctrl_of_match[] = { 5379dee7a72SMatthias Kaehlcke { .compatible = "vctrl-regulator", }, 5389dee7a72SMatthias Kaehlcke {}, 5399dee7a72SMatthias Kaehlcke }; 5409dee7a72SMatthias Kaehlcke MODULE_DEVICE_TABLE(of, vctrl_of_match); 5419dee7a72SMatthias Kaehlcke 5429dee7a72SMatthias Kaehlcke static struct platform_driver vctrl_driver = { 5439dee7a72SMatthias Kaehlcke .probe = vctrl_probe, 5449dee7a72SMatthias Kaehlcke .driver = { 5459dee7a72SMatthias Kaehlcke .name = "vctrl-regulator", 546259b93b2SDouglas Anderson .probe_type = PROBE_PREFER_ASYNCHRONOUS, 5479dee7a72SMatthias Kaehlcke .of_match_table = of_match_ptr(vctrl_of_match), 5489dee7a72SMatthias Kaehlcke }, 5499dee7a72SMatthias Kaehlcke }; 5509dee7a72SMatthias Kaehlcke 5519dee7a72SMatthias Kaehlcke module_platform_driver(vctrl_driver); 5529dee7a72SMatthias Kaehlcke 5539dee7a72SMatthias Kaehlcke MODULE_DESCRIPTION("Voltage Controlled Regulator Driver"); 5549dee7a72SMatthias Kaehlcke MODULE_AUTHOR("Matthias Kaehlcke <mka@chromium.org>"); 5559dee7a72SMatthias Kaehlcke MODULE_LICENSE("GPL v2"); 556