/* * Driver for voltage controller regulators * * Copyright (C) 2017 Google, Inc. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include struct vctrl_voltage_range { int min_uV; int max_uV; }; struct vctrl_voltage_ranges { struct vctrl_voltage_range ctrl; struct vctrl_voltage_range out; }; struct vctrl_voltage_table { int ctrl; int out; int ovp_min_sel; }; struct vctrl_data { struct regulator_dev *rdev; struct regulator_desc desc; struct regulator *ctrl_reg; bool enabled; unsigned int min_slew_down_rate; unsigned int ovp_threshold; struct vctrl_voltage_ranges vrange; struct vctrl_voltage_table *vtable; unsigned int sel; }; static int vctrl_calc_ctrl_voltage(struct vctrl_data *vctrl, int out_uV) { struct vctrl_voltage_range *ctrl = &vctrl->vrange.ctrl; struct vctrl_voltage_range *out = &vctrl->vrange.out; return ctrl->min_uV + DIV_ROUND_CLOSEST_ULL((s64)(out_uV - out->min_uV) * (ctrl->max_uV - ctrl->min_uV), out->max_uV - out->min_uV); } static int vctrl_calc_output_voltage(struct vctrl_data *vctrl, int ctrl_uV) { struct vctrl_voltage_range *ctrl = &vctrl->vrange.ctrl; struct vctrl_voltage_range *out = &vctrl->vrange.out; if (ctrl_uV < 0) { pr_err("vctrl: failed to get control voltage\n"); return ctrl_uV; } if (ctrl_uV < ctrl->min_uV) return out->min_uV; if (ctrl_uV > ctrl->max_uV) return out->max_uV; return out->min_uV + DIV_ROUND_CLOSEST_ULL((s64)(ctrl_uV - ctrl->min_uV) * (out->max_uV - out->min_uV), ctrl->max_uV - ctrl->min_uV); } static int vctrl_get_voltage(struct regulator_dev *rdev) { struct vctrl_data *vctrl = rdev_get_drvdata(rdev); int ctrl_uV = regulator_get_voltage(vctrl->ctrl_reg); return vctrl_calc_output_voltage(vctrl, ctrl_uV); } static int vctrl_set_voltage(struct regulator_dev *rdev, int req_min_uV, int req_max_uV, unsigned int *selector) { struct vctrl_data *vctrl = rdev_get_drvdata(rdev); struct regulator *ctrl_reg = vctrl->ctrl_reg; int orig_ctrl_uV = regulator_get_voltage(ctrl_reg); int uV = vctrl_calc_output_voltage(vctrl, orig_ctrl_uV); int ret; if (req_min_uV >= uV || !vctrl->ovp_threshold) /* voltage rising or no OVP */ return regulator_set_voltage( ctrl_reg, vctrl_calc_ctrl_voltage(vctrl, req_min_uV), vctrl_calc_ctrl_voltage(vctrl, req_max_uV)); while (uV > req_min_uV) { int max_drop_uV = (uV * vctrl->ovp_threshold) / 100; int next_uV; int next_ctrl_uV; int delay; /* Make sure no infinite loop even in crazy cases */ if (max_drop_uV == 0) max_drop_uV = 1; next_uV = max_t(int, req_min_uV, uV - max_drop_uV); next_ctrl_uV = vctrl_calc_ctrl_voltage(vctrl, next_uV); ret = regulator_set_voltage(ctrl_reg, next_ctrl_uV, next_ctrl_uV); if (ret) goto err; delay = DIV_ROUND_UP(uV - next_uV, vctrl->min_slew_down_rate); usleep_range(delay, delay + DIV_ROUND_UP(delay, 10)); uV = next_uV; } return 0; err: /* Try to go back to original voltage */ regulator_set_voltage(ctrl_reg, orig_ctrl_uV, orig_ctrl_uV); return ret; } static int vctrl_get_voltage_sel(struct regulator_dev *rdev) { struct vctrl_data *vctrl = rdev_get_drvdata(rdev); return vctrl->sel; } static int vctrl_set_voltage_sel(struct regulator_dev *rdev, unsigned int selector) { struct vctrl_data *vctrl = rdev_get_drvdata(rdev); struct regulator *ctrl_reg = vctrl->ctrl_reg; unsigned int orig_sel = vctrl->sel; int ret; if (selector >= rdev->desc->n_voltages) return -EINVAL; if (selector >= vctrl->sel || !vctrl->ovp_threshold) { /* voltage rising or no OVP */ ret = regulator_set_voltage(ctrl_reg, vctrl->vtable[selector].ctrl, vctrl->vtable[selector].ctrl); if (!ret) vctrl->sel = selector; return ret; } while (vctrl->sel != selector) { unsigned int next_sel; int delay; if (selector >= vctrl->vtable[vctrl->sel].ovp_min_sel) next_sel = selector; else next_sel = vctrl->vtable[vctrl->sel].ovp_min_sel; ret = regulator_set_voltage(ctrl_reg, vctrl->vtable[next_sel].ctrl, vctrl->vtable[next_sel].ctrl); if (ret) { dev_err(&rdev->dev, "failed to set contro