18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for voltage controller regulators 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Google, Inc. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/delay.h> 98c2ecf20Sopenharmony_ci#include <linux/err.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/of.h> 138c2ecf20Sopenharmony_ci#include <linux/of_device.h> 148c2ecf20Sopenharmony_ci#include <linux/regulator/coupler.h> 158c2ecf20Sopenharmony_ci#include <linux/regulator/driver.h> 168c2ecf20Sopenharmony_ci#include <linux/regulator/of_regulator.h> 178c2ecf20Sopenharmony_ci#include <linux/sort.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "internal.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistruct vctrl_voltage_range { 228c2ecf20Sopenharmony_ci int min_uV; 238c2ecf20Sopenharmony_ci int max_uV; 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct vctrl_voltage_ranges { 278c2ecf20Sopenharmony_ci struct vctrl_voltage_range ctrl; 288c2ecf20Sopenharmony_ci struct vctrl_voltage_range out; 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct vctrl_voltage_table { 328c2ecf20Sopenharmony_ci int ctrl; 338c2ecf20Sopenharmony_ci int out; 348c2ecf20Sopenharmony_ci int ovp_min_sel; 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistruct vctrl_data { 388c2ecf20Sopenharmony_ci struct regulator_dev *rdev; 398c2ecf20Sopenharmony_ci struct regulator_desc desc; 408c2ecf20Sopenharmony_ci bool enabled; 418c2ecf20Sopenharmony_ci unsigned int min_slew_down_rate; 428c2ecf20Sopenharmony_ci unsigned int ovp_threshold; 438c2ecf20Sopenharmony_ci struct vctrl_voltage_ranges vrange; 448c2ecf20Sopenharmony_ci struct vctrl_voltage_table *vtable; 458c2ecf20Sopenharmony_ci unsigned int sel; 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int vctrl_calc_ctrl_voltage(struct vctrl_data *vctrl, int out_uV) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct vctrl_voltage_range *ctrl = &vctrl->vrange.ctrl; 518c2ecf20Sopenharmony_ci struct vctrl_voltage_range *out = &vctrl->vrange.out; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci return ctrl->min_uV + 548c2ecf20Sopenharmony_ci DIV_ROUND_CLOSEST_ULL((s64)(out_uV - out->min_uV) * 558c2ecf20Sopenharmony_ci (ctrl->max_uV - ctrl->min_uV), 568c2ecf20Sopenharmony_ci out->max_uV - out->min_uV); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic int vctrl_calc_output_voltage(struct vctrl_data *vctrl, int ctrl_uV) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci struct vctrl_voltage_range *ctrl = &vctrl->vrange.ctrl; 628c2ecf20Sopenharmony_ci struct vctrl_voltage_range *out = &vctrl->vrange.out; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (ctrl_uV < 0) { 658c2ecf20Sopenharmony_ci pr_err("vctrl: failed to get control voltage\n"); 668c2ecf20Sopenharmony_ci return ctrl_uV; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (ctrl_uV < ctrl->min_uV) 708c2ecf20Sopenharmony_ci return out->min_uV; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (ctrl_uV > ctrl->max_uV) 738c2ecf20Sopenharmony_ci return out->max_uV; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci return out->min_uV + 768c2ecf20Sopenharmony_ci DIV_ROUND_CLOSEST_ULL((s64)(ctrl_uV - ctrl->min_uV) * 778c2ecf20Sopenharmony_ci (out->max_uV - out->min_uV), 788c2ecf20Sopenharmony_ci ctrl->max_uV - ctrl->min_uV); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic int vctrl_get_voltage(struct regulator_dev *rdev) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct vctrl_data *vctrl = rdev_get_drvdata(rdev); 848c2ecf20Sopenharmony_ci int ctrl_uV; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (!rdev->supply) 878c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci ctrl_uV = regulator_get_voltage_rdev(rdev->supply->rdev); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return vctrl_calc_output_voltage(vctrl, ctrl_uV); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int vctrl_set_voltage(struct regulator_dev *rdev, 958c2ecf20Sopenharmony_ci int req_min_uV, int req_max_uV, 968c2ecf20Sopenharmony_ci unsigned int *selector) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct vctrl_data *vctrl = rdev_get_drvdata(rdev); 998c2ecf20Sopenharmony_ci int orig_ctrl_uV; 1008c2ecf20Sopenharmony_ci int uV; 1018c2ecf20Sopenharmony_ci int ret; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (!rdev->supply) 1048c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci orig_ctrl_uV = regulator_get_voltage_rdev(rdev->supply->rdev); 1078c2ecf20Sopenharmony_ci uV = vctrl_calc_output_voltage(vctrl, orig_ctrl_uV); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (req_min_uV >= uV || !vctrl->ovp_threshold) 1108c2ecf20Sopenharmony_ci /* voltage rising or no OVP */ 1118c2ecf20Sopenharmony_ci return regulator_set_voltage_rdev(rdev->supply->rdev, 1128c2ecf20Sopenharmony_ci vctrl_calc_ctrl_voltage(vctrl, req_min_uV), 1138c2ecf20Sopenharmony_ci vctrl_calc_ctrl_voltage(vctrl, req_max_uV), 1148c2ecf20Sopenharmony_ci PM_SUSPEND_ON); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci while (uV > req_min_uV) { 1178c2ecf20Sopenharmony_ci int max_drop_uV = (uV * vctrl->ovp_threshold) / 100; 1188c2ecf20Sopenharmony_ci int next_uV; 1198c2ecf20Sopenharmony_ci int next_ctrl_uV; 1208c2ecf20Sopenharmony_ci int delay; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* Make sure no infinite loop even in crazy cases */ 1238c2ecf20Sopenharmony_ci if (max_drop_uV == 0) 1248c2ecf20Sopenharmony_ci max_drop_uV = 1; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci next_uV = max_t(int, req_min_uV, uV - max_drop_uV); 1278c2ecf20Sopenharmony_ci next_ctrl_uV = vctrl_calc_ctrl_voltage(vctrl, next_uV); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci ret = regulator_set_voltage_rdev(rdev->supply->rdev, 1308c2ecf20Sopenharmony_ci next_ctrl_uV, 1318c2ecf20Sopenharmony_ci next_ctrl_uV, 1328c2ecf20Sopenharmony_ci PM_SUSPEND_ON); 1338c2ecf20Sopenharmony_ci if (ret) 1348c2ecf20Sopenharmony_ci goto err; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci delay = DIV_ROUND_UP(uV - next_uV, vctrl->min_slew_down_rate); 1378c2ecf20Sopenharmony_ci usleep_range(delay, delay + DIV_ROUND_UP(delay, 10)); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci uV = next_uV; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return 0; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cierr: 1458c2ecf20Sopenharmony_ci /* Try to go back to original voltage */ 1468c2ecf20Sopenharmony_ci regulator_set_voltage_rdev(rdev->supply->rdev, orig_ctrl_uV, orig_ctrl_uV, 1478c2ecf20Sopenharmony_ci PM_SUSPEND_ON); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return ret; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic int vctrl_get_voltage_sel(struct regulator_dev *rdev) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct vctrl_data *vctrl = rdev_get_drvdata(rdev); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return vctrl->sel; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int vctrl_set_voltage_sel(struct regulator_dev *rdev, 1608c2ecf20Sopenharmony_ci unsigned int selector) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct vctrl_data *vctrl = rdev_get_drvdata(rdev); 1638c2ecf20Sopenharmony_ci unsigned int orig_sel = vctrl->sel; 1648c2ecf20Sopenharmony_ci int ret; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (!rdev->supply) 1678c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (selector >= rdev->desc->n_voltages) 1708c2ecf20Sopenharmony_ci return -EINVAL; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (selector >= vctrl->sel || !vctrl->ovp_threshold) { 1738c2ecf20Sopenharmony_ci /* voltage rising or no OVP */ 1748c2ecf20Sopenharmony_ci ret = regulator_set_voltage_rdev(rdev->supply->rdev, 1758c2ecf20Sopenharmony_ci vctrl->vtable[selector].ctrl, 1768c2ecf20Sopenharmony_ci vctrl->vtable[selector].ctrl, 1778c2ecf20Sopenharmony_ci PM_SUSPEND_ON); 1788c2ecf20Sopenharmony_ci if (!ret) 1798c2ecf20Sopenharmony_ci vctrl->sel = selector; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return ret; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci while (vctrl->sel != selector) { 1858c2ecf20Sopenharmony_ci unsigned int next_sel; 1868c2ecf20Sopenharmony_ci int delay; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (selector >= vctrl->vtable[vctrl->sel].ovp_min_sel) 1898c2ecf20Sopenharmony_ci next_sel = selector; 1908c2ecf20Sopenharmony_ci else 1918c2ecf20Sopenharmony_ci next_sel = vctrl->vtable[vctrl->sel].ovp_min_sel; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci ret = regulator_set_voltage_rdev(rdev->supply->rdev, 1948c2ecf20Sopenharmony_ci vctrl->vtable[next_sel].ctrl, 1958c2ecf20Sopenharmony_ci vctrl->vtable[next_sel].ctrl, 1968c2ecf20Sopenharmony_ci PM_SUSPEND_ON); 1978c2ecf20Sopenharmony_ci if (ret) { 1988c2ecf20Sopenharmony_ci dev_err(&rdev->dev, 1998c2ecf20Sopenharmony_ci "failed to set control voltage to %duV\n", 2008c2ecf20Sopenharmony_ci vctrl->vtable[next_sel].ctrl); 2018c2ecf20Sopenharmony_ci goto err; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci vctrl->sel = next_sel; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci delay = DIV_ROUND_UP(vctrl->vtable[vctrl->sel].out - 2068c2ecf20Sopenharmony_ci vctrl->vtable[next_sel].out, 2078c2ecf20Sopenharmony_ci vctrl->min_slew_down_rate); 2088c2ecf20Sopenharmony_ci usleep_range(delay, delay + DIV_ROUND_UP(delay, 10)); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci return 0; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cierr: 2148c2ecf20Sopenharmony_ci if (vctrl->sel != orig_sel) { 2158c2ecf20Sopenharmony_ci /* Try to go back to original voltage */ 2168c2ecf20Sopenharmony_ci if (!regulator_set_voltage_rdev(rdev->supply->rdev, 2178c2ecf20Sopenharmony_ci vctrl->vtable[orig_sel].ctrl, 2188c2ecf20Sopenharmony_ci vctrl->vtable[orig_sel].ctrl, 2198c2ecf20Sopenharmony_ci PM_SUSPEND_ON)) 2208c2ecf20Sopenharmony_ci vctrl->sel = orig_sel; 2218c2ecf20Sopenharmony_ci else 2228c2ecf20Sopenharmony_ci dev_warn(&rdev->dev, 2238c2ecf20Sopenharmony_ci "failed to restore original voltage\n"); 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci return ret; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int vctrl_list_voltage(struct regulator_dev *rdev, 2308c2ecf20Sopenharmony_ci unsigned int selector) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci struct vctrl_data *vctrl = rdev_get_drvdata(rdev); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (selector >= rdev->desc->n_voltages) 2358c2ecf20Sopenharmony_ci return -EINVAL; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci return vctrl->vtable[selector].out; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic int vctrl_parse_dt(struct platform_device *pdev, 2418c2ecf20Sopenharmony_ci struct vctrl_data *vctrl) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci int ret; 2448c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 2458c2ecf20Sopenharmony_ci u32 pval; 2468c2ecf20Sopenharmony_ci u32 vrange_ctrl[2]; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "ovp-threshold-percent", &pval); 2498c2ecf20Sopenharmony_ci if (!ret) { 2508c2ecf20Sopenharmony_ci vctrl->ovp_threshold = pval; 2518c2ecf20Sopenharmony_ci if (vctrl->ovp_threshold > 100) { 2528c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 2538c2ecf20Sopenharmony_ci "ovp-threshold-percent (%u) > 100\n", 2548c2ecf20Sopenharmony_ci vctrl->ovp_threshold); 2558c2ecf20Sopenharmony_ci return -EINVAL; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "min-slew-down-rate", &pval); 2608c2ecf20Sopenharmony_ci if (!ret) { 2618c2ecf20Sopenharmony_ci vctrl->min_slew_down_rate = pval; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* We use the value as int and as divider; sanity check */ 2648c2ecf20Sopenharmony_ci if (vctrl->min_slew_down_rate == 0) { 2658c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 2668c2ecf20Sopenharmony_ci "min-slew-down-rate must not be 0\n"); 2678c2ecf20Sopenharmony_ci return -EINVAL; 2688c2ecf20Sopenharmony_ci } else if (vctrl->min_slew_down_rate > INT_MAX) { 2698c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "min-slew-down-rate (%u) too big\n", 2708c2ecf20Sopenharmony_ci vctrl->min_slew_down_rate); 2718c2ecf20Sopenharmony_ci return -EINVAL; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (vctrl->ovp_threshold && !vctrl->min_slew_down_rate) { 2768c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 2778c2ecf20Sopenharmony_ci "ovp-threshold-percent requires min-slew-down-rate\n"); 2788c2ecf20Sopenharmony_ci return -EINVAL; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "regulator-min-microvolt", &pval); 2828c2ecf20Sopenharmony_ci if (ret) { 2838c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 2848c2ecf20Sopenharmony_ci "failed to read regulator-min-microvolt: %d\n", ret); 2858c2ecf20Sopenharmony_ci return ret; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci vctrl->vrange.out.min_uV = pval; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "regulator-max-microvolt", &pval); 2908c2ecf20Sopenharmony_ci if (ret) { 2918c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 2928c2ecf20Sopenharmony_ci "failed to read regulator-max-microvolt: %d\n", ret); 2938c2ecf20Sopenharmony_ci return ret; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci vctrl->vrange.out.max_uV = pval; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci ret = of_property_read_u32_array(np, "ctrl-voltage-range", vrange_ctrl, 2988c2ecf20Sopenharmony_ci 2); 2998c2ecf20Sopenharmony_ci if (ret) { 3008c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to read ctrl-voltage-range: %d\n", 3018c2ecf20Sopenharmony_ci ret); 3028c2ecf20Sopenharmony_ci return ret; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (vrange_ctrl[0] >= vrange_ctrl[1]) { 3068c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "ctrl-voltage-range is invalid: %d-%d\n", 3078c2ecf20Sopenharmony_ci vrange_ctrl[0], vrange_ctrl[1]); 3088c2ecf20Sopenharmony_ci return -EINVAL; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci vctrl->vrange.ctrl.min_uV = vrange_ctrl[0]; 3128c2ecf20Sopenharmony_ci vctrl->vrange.ctrl.max_uV = vrange_ctrl[1]; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return 0; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic int vctrl_cmp_ctrl_uV(const void *a, const void *b) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci const struct vctrl_voltage_table *at = a; 3208c2ecf20Sopenharmony_ci const struct vctrl_voltage_table *bt = b; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return at->ctrl - bt->ctrl; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic int vctrl_init_vtable(struct platform_device *pdev, 3268c2ecf20Sopenharmony_ci struct regulator *ctrl_reg) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci struct vctrl_data *vctrl = platform_get_drvdata(pdev); 3298c2ecf20Sopenharmony_ci struct regulator_desc *rdesc = &vctrl->desc; 3308c2ecf20Sopenharmony_ci struct vctrl_voltage_range *vrange_ctrl = &vctrl->vrange.ctrl; 3318c2ecf20Sopenharmony_ci int n_voltages; 3328c2ecf20Sopenharmony_ci int ctrl_uV; 3338c2ecf20Sopenharmony_ci int i, idx_vt; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci n_voltages = regulator_count_voltages(ctrl_reg); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci rdesc->n_voltages = n_voltages; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* determine number of steps within the range of the vctrl regulator */ 3408c2ecf20Sopenharmony_ci for (i = 0; i < n_voltages; i++) { 3418c2ecf20Sopenharmony_ci ctrl_uV = regulator_list_voltage(ctrl_reg, i); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (ctrl_uV < vrange_ctrl->min_uV || 3448c2ecf20Sopenharmony_ci ctrl_uV > vrange_ctrl->max_uV) 3458c2ecf20Sopenharmony_ci rdesc->n_voltages--; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (rdesc->n_voltages == 0) { 3498c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "invalid configuration\n"); 3508c2ecf20Sopenharmony_ci return -EINVAL; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci vctrl->vtable = devm_kcalloc(&pdev->dev, rdesc->n_voltages, 3548c2ecf20Sopenharmony_ci sizeof(struct vctrl_voltage_table), 3558c2ecf20Sopenharmony_ci GFP_KERNEL); 3568c2ecf20Sopenharmony_ci if (!vctrl->vtable) 3578c2ecf20Sopenharmony_ci return -ENOMEM; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* create mapping control <=> output voltage */ 3608c2ecf20Sopenharmony_ci for (i = 0, idx_vt = 0; i < n_voltages; i++) { 3618c2ecf20Sopenharmony_ci ctrl_uV = regulator_list_voltage(ctrl_reg, i); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (ctrl_uV < vrange_ctrl->min_uV || 3648c2ecf20Sopenharmony_ci ctrl_uV > vrange_ctrl->max_uV) 3658c2ecf20Sopenharmony_ci continue; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci vctrl->vtable[idx_vt].ctrl = ctrl_uV; 3688c2ecf20Sopenharmony_ci vctrl->vtable[idx_vt].out = 3698c2ecf20Sopenharmony_ci vctrl_calc_output_voltage(vctrl, ctrl_uV); 3708c2ecf20Sopenharmony_ci idx_vt++; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* we rely on the table to be ordered by ascending voltage */ 3748c2ecf20Sopenharmony_ci sort(vctrl->vtable, rdesc->n_voltages, 3758c2ecf20Sopenharmony_ci sizeof(struct vctrl_voltage_table), vctrl_cmp_ctrl_uV, 3768c2ecf20Sopenharmony_ci NULL); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* pre-calculate OVP-safe downward transitions */ 3798c2ecf20Sopenharmony_ci for (i = rdesc->n_voltages - 1; i > 0; i--) { 3808c2ecf20Sopenharmony_ci int j; 3818c2ecf20Sopenharmony_ci int ovp_min_uV = (vctrl->vtable[i].out * 3828c2ecf20Sopenharmony_ci (100 - vctrl->ovp_threshold)) / 100; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci for (j = 0; j < i; j++) { 3858c2ecf20Sopenharmony_ci if (vctrl->vtable[j].out >= ovp_min_uV) { 3868c2ecf20Sopenharmony_ci vctrl->vtable[i].ovp_min_sel = j; 3878c2ecf20Sopenharmony_ci break; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (j == i) { 3928c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "switching down from %duV may cause OVP shutdown\n", 3938c2ecf20Sopenharmony_ci vctrl->vtable[i].out); 3948c2ecf20Sopenharmony_ci /* use next lowest voltage */ 3958c2ecf20Sopenharmony_ci vctrl->vtable[i].ovp_min_sel = i - 1; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci return 0; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic int vctrl_enable(struct regulator_dev *rdev) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct vctrl_data *vctrl = rdev_get_drvdata(rdev); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci vctrl->enabled = true; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic int vctrl_disable(struct regulator_dev *rdev) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci struct vctrl_data *vctrl = rdev_get_drvdata(rdev); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci vctrl->enabled = false; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return 0; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic int vctrl_is_enabled(struct regulator_dev *rdev) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci struct vctrl_data *vctrl = rdev_get_drvdata(rdev); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci return vctrl->enabled; 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic const struct regulator_ops vctrl_ops_cont = { 4288c2ecf20Sopenharmony_ci .enable = vctrl_enable, 4298c2ecf20Sopenharmony_ci .disable = vctrl_disable, 4308c2ecf20Sopenharmony_ci .is_enabled = vctrl_is_enabled, 4318c2ecf20Sopenharmony_ci .get_voltage = vctrl_get_voltage, 4328c2ecf20Sopenharmony_ci .set_voltage = vctrl_set_voltage, 4338c2ecf20Sopenharmony_ci}; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic const struct regulator_ops vctrl_ops_non_cont = { 4368c2ecf20Sopenharmony_ci .enable = vctrl_enable, 4378c2ecf20Sopenharmony_ci .disable = vctrl_disable, 4388c2ecf20Sopenharmony_ci .is_enabled = vctrl_is_enabled, 4398c2ecf20Sopenharmony_ci .set_voltage_sel = vctrl_set_voltage_sel, 4408c2ecf20Sopenharmony_ci .get_voltage_sel = vctrl_get_voltage_sel, 4418c2ecf20Sopenharmony_ci .list_voltage = vctrl_list_voltage, 4428c2ecf20Sopenharmony_ci .map_voltage = regulator_map_voltage_iterate, 4438c2ecf20Sopenharmony_ci}; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic int vctrl_probe(struct platform_device *pdev) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 4488c2ecf20Sopenharmony_ci struct vctrl_data *vctrl; 4498c2ecf20Sopenharmony_ci const struct regulator_init_data *init_data; 4508c2ecf20Sopenharmony_ci struct regulator_desc *rdesc; 4518c2ecf20Sopenharmony_ci struct regulator_config cfg = { }; 4528c2ecf20Sopenharmony_ci struct vctrl_voltage_range *vrange_ctrl; 4538c2ecf20Sopenharmony_ci struct regulator *ctrl_reg; 4548c2ecf20Sopenharmony_ci int ctrl_uV; 4558c2ecf20Sopenharmony_ci int ret; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci vctrl = devm_kzalloc(&pdev->dev, sizeof(struct vctrl_data), 4588c2ecf20Sopenharmony_ci GFP_KERNEL); 4598c2ecf20Sopenharmony_ci if (!vctrl) 4608c2ecf20Sopenharmony_ci return -ENOMEM; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, vctrl); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci ret = vctrl_parse_dt(pdev, vctrl); 4658c2ecf20Sopenharmony_ci if (ret) 4668c2ecf20Sopenharmony_ci return ret; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci ctrl_reg = devm_regulator_get(&pdev->dev, "ctrl"); 4698c2ecf20Sopenharmony_ci if (IS_ERR(ctrl_reg)) 4708c2ecf20Sopenharmony_ci return PTR_ERR(ctrl_reg); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci vrange_ctrl = &vctrl->vrange.ctrl; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci rdesc = &vctrl->desc; 4758c2ecf20Sopenharmony_ci rdesc->name = "vctrl"; 4768c2ecf20Sopenharmony_ci rdesc->type = REGULATOR_VOLTAGE; 4778c2ecf20Sopenharmony_ci rdesc->owner = THIS_MODULE; 4788c2ecf20Sopenharmony_ci rdesc->supply_name = "ctrl"; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if ((regulator_get_linear_step(ctrl_reg) == 1) || 4818c2ecf20Sopenharmony_ci (regulator_count_voltages(ctrl_reg) == -EINVAL)) { 4828c2ecf20Sopenharmony_ci rdesc->continuous_voltage_range = true; 4838c2ecf20Sopenharmony_ci rdesc->ops = &vctrl_ops_cont; 4848c2ecf20Sopenharmony_ci } else { 4858c2ecf20Sopenharmony_ci rdesc->ops = &vctrl_ops_non_cont; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci init_data = of_get_regulator_init_data(&pdev->dev, np, rdesc); 4898c2ecf20Sopenharmony_ci if (!init_data) 4908c2ecf20Sopenharmony_ci return -ENOMEM; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci cfg.of_node = np; 4938c2ecf20Sopenharmony_ci cfg.dev = &pdev->dev; 4948c2ecf20Sopenharmony_ci cfg.driver_data = vctrl; 4958c2ecf20Sopenharmony_ci cfg.init_data = init_data; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (!rdesc->continuous_voltage_range) { 4988c2ecf20Sopenharmony_ci ret = vctrl_init_vtable(pdev, ctrl_reg); 4998c2ecf20Sopenharmony_ci if (ret) 5008c2ecf20Sopenharmony_ci return ret; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* Use locked consumer API when not in regulator framework */ 5038c2ecf20Sopenharmony_ci ctrl_uV = regulator_get_voltage(ctrl_reg); 5048c2ecf20Sopenharmony_ci if (ctrl_uV < 0) { 5058c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get control voltage\n"); 5068c2ecf20Sopenharmony_ci return ctrl_uV; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* determine current voltage selector from control voltage */ 5108c2ecf20Sopenharmony_ci if (ctrl_uV < vrange_ctrl->min_uV) { 5118c2ecf20Sopenharmony_ci vctrl->sel = 0; 5128c2ecf20Sopenharmony_ci } else if (ctrl_uV > vrange_ctrl->max_uV) { 5138c2ecf20Sopenharmony_ci vctrl->sel = rdesc->n_voltages - 1; 5148c2ecf20Sopenharmony_ci } else { 5158c2ecf20Sopenharmony_ci int i; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci for (i = 0; i < rdesc->n_voltages; i++) { 5188c2ecf20Sopenharmony_ci if (ctrl_uV == vctrl->vtable[i].ctrl) { 5198c2ecf20Sopenharmony_ci vctrl->sel = i; 5208c2ecf20Sopenharmony_ci break; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* Drop ctrl-supply here in favor of regulator core managed supply */ 5278c2ecf20Sopenharmony_ci devm_regulator_put(ctrl_reg); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci vctrl->rdev = devm_regulator_register(&pdev->dev, rdesc, &cfg); 5308c2ecf20Sopenharmony_ci if (IS_ERR(vctrl->rdev)) { 5318c2ecf20Sopenharmony_ci ret = PTR_ERR(vctrl->rdev); 5328c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register regulator: %d\n", ret); 5338c2ecf20Sopenharmony_ci return ret; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci return 0; 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic const struct of_device_id vctrl_of_match[] = { 5408c2ecf20Sopenharmony_ci { .compatible = "vctrl-regulator", }, 5418c2ecf20Sopenharmony_ci {}, 5428c2ecf20Sopenharmony_ci}; 5438c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, vctrl_of_match); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic struct platform_driver vctrl_driver = { 5468c2ecf20Sopenharmony_ci .probe = vctrl_probe, 5478c2ecf20Sopenharmony_ci .driver = { 5488c2ecf20Sopenharmony_ci .name = "vctrl-regulator", 5498c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(vctrl_of_match), 5508c2ecf20Sopenharmony_ci }, 5518c2ecf20Sopenharmony_ci}; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cimodule_platform_driver(vctrl_driver); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Voltage Controlled Regulator Driver"); 5568c2ecf20Sopenharmony_ciMODULE_AUTHOR("Matthias Kaehlcke <mka@chromium.org>"); 5578c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 558