brintos

brintos / linux-shallow public Read only

0
0
Text · 13.2 KiB · 2796580 Raw
556 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Driver for voltage controller regulators4 *5 * Copyright (C) 2017 Google, Inc.6 */7 8#include <linux/delay.h>9#include <linux/err.h>10#include <linux/init.h>11#include <linux/module.h>12#include <linux/of.h>13#include <linux/platform_device.h>14#include <linux/regulator/coupler.h>15#include <linux/regulator/driver.h>16#include <linux/regulator/of_regulator.h>17#include <linux/sort.h>18 19#include "internal.h"20 21struct vctrl_voltage_range {22	int min_uV;23	int max_uV;24};25 26struct vctrl_voltage_ranges {27	struct vctrl_voltage_range ctrl;28	struct vctrl_voltage_range out;29};30 31struct vctrl_voltage_table {32	int ctrl;33	int out;34	int ovp_min_sel;35};36 37struct vctrl_data {38	struct regulator_dev *rdev;39	struct regulator_desc desc;40	bool enabled;41	unsigned int min_slew_down_rate;42	unsigned int ovp_threshold;43	struct vctrl_voltage_ranges vrange;44	struct vctrl_voltage_table *vtable;45	unsigned int sel;46};47 48static int vctrl_calc_ctrl_voltage(struct vctrl_data *vctrl, int out_uV)49{50	struct vctrl_voltage_range *ctrl = &vctrl->vrange.ctrl;51	struct vctrl_voltage_range *out = &vctrl->vrange.out;52 53	return ctrl->min_uV +54		DIV_ROUND_CLOSEST_ULL((s64)(out_uV - out->min_uV) *55				      (ctrl->max_uV - ctrl->min_uV),56				      out->max_uV - out->min_uV);57}58 59static int vctrl_calc_output_voltage(struct vctrl_data *vctrl, int ctrl_uV)60{61	struct vctrl_voltage_range *ctrl = &vctrl->vrange.ctrl;62	struct vctrl_voltage_range *out = &vctrl->vrange.out;63 64	if (ctrl_uV < 0) {65		pr_err("vctrl: failed to get control voltage\n");66		return ctrl_uV;67	}68 69	if (ctrl_uV < ctrl->min_uV)70		return out->min_uV;71 72	if (ctrl_uV > ctrl->max_uV)73		return out->max_uV;74 75	return out->min_uV +76		DIV_ROUND_CLOSEST_ULL((s64)(ctrl_uV - ctrl->min_uV) *77				      (out->max_uV - out->min_uV),78				      ctrl->max_uV - ctrl->min_uV);79}80 81static int vctrl_get_voltage(struct regulator_dev *rdev)82{83	struct vctrl_data *vctrl = rdev_get_drvdata(rdev);84	int ctrl_uV;85 86	if (!rdev->supply)87		return -EPROBE_DEFER;88 89	ctrl_uV = regulator_get_voltage_rdev(rdev->supply->rdev);90 91	return vctrl_calc_output_voltage(vctrl, ctrl_uV);92}93 94static int vctrl_set_voltage(struct regulator_dev *rdev,95			     int req_min_uV, int req_max_uV,96			     unsigned int *selector)97{98	struct vctrl_data *vctrl = rdev_get_drvdata(rdev);99	int orig_ctrl_uV;100	int uV;101	int ret;102 103	if (!rdev->supply)104		return -EPROBE_DEFER;105 106	orig_ctrl_uV = regulator_get_voltage_rdev(rdev->supply->rdev);107	uV = vctrl_calc_output_voltage(vctrl, orig_ctrl_uV);108 109	if (req_min_uV >= uV || !vctrl->ovp_threshold)110		/* voltage rising or no OVP */111		return regulator_set_voltage_rdev(rdev->supply->rdev,112			vctrl_calc_ctrl_voltage(vctrl, req_min_uV),113			vctrl_calc_ctrl_voltage(vctrl, req_max_uV),114			PM_SUSPEND_ON);115 116	while (uV > req_min_uV) {117		int max_drop_uV = (uV * vctrl->ovp_threshold) / 100;118		int next_uV;119		int next_ctrl_uV;120		int delay;121 122		/* Make sure no infinite loop even in crazy cases */123		if (max_drop_uV == 0)124			max_drop_uV = 1;125 126		next_uV = max_t(int, req_min_uV, uV - max_drop_uV);127		next_ctrl_uV = vctrl_calc_ctrl_voltage(vctrl, next_uV);128 129		ret = regulator_set_voltage_rdev(rdev->supply->rdev,130					    next_ctrl_uV,131					    next_ctrl_uV,132					    PM_SUSPEND_ON);133		if (ret)134			goto err;135 136		delay = DIV_ROUND_UP(uV - next_uV, vctrl->min_slew_down_rate);137		usleep_range(delay, delay + DIV_ROUND_UP(delay, 10));138 139		uV = next_uV;140	}141 142	return 0;143 144err:145	/* Try to go back to original voltage */146	regulator_set_voltage_rdev(rdev->supply->rdev, orig_ctrl_uV, orig_ctrl_uV,147				   PM_SUSPEND_ON);148 149	return ret;150}151 152static int vctrl_get_voltage_sel(struct regulator_dev *rdev)153{154	struct vctrl_data *vctrl = rdev_get_drvdata(rdev);155 156	return vctrl->sel;157}158 159static int vctrl_set_voltage_sel(struct regulator_dev *rdev,160				 unsigned int selector)161{162	struct vctrl_data *vctrl = rdev_get_drvdata(rdev);163	unsigned int orig_sel = vctrl->sel;164	int ret;165 166	if (!rdev->supply)167		return -EPROBE_DEFER;168 169	if (selector >= rdev->desc->n_voltages)170		return -EINVAL;171 172	if (selector >= vctrl->sel || !vctrl->ovp_threshold) {173		/* voltage rising or no OVP */174		ret = regulator_set_voltage_rdev(rdev->supply->rdev,175					    vctrl->vtable[selector].ctrl,176					    vctrl->vtable[selector].ctrl,177					    PM_SUSPEND_ON);178		if (!ret)179			vctrl->sel = selector;180 181		return ret;182	}183 184	while (vctrl->sel != selector) {185		unsigned int next_sel;186		int delay;187 188		next_sel = max_t(unsigned int, selector, vctrl->vtable[vctrl->sel].ovp_min_sel);189 190		ret = regulator_set_voltage_rdev(rdev->supply->rdev,191					    vctrl->vtable[next_sel].ctrl,192					    vctrl->vtable[next_sel].ctrl,193					    PM_SUSPEND_ON);194		if (ret) {195			dev_err(&rdev->dev,196				"failed to set control voltage to %duV\n",197				vctrl->vtable[next_sel].ctrl);198			goto err;199		}200		vctrl->sel = next_sel;201 202		delay = DIV_ROUND_UP(vctrl->vtable[vctrl->sel].out -203				     vctrl->vtable[next_sel].out,204				     vctrl->min_slew_down_rate);205		usleep_range(delay, delay + DIV_ROUND_UP(delay, 10));206	}207 208	return 0;209 210err:211	if (vctrl->sel != orig_sel) {212		/* Try to go back to original voltage */213		if (!regulator_set_voltage_rdev(rdev->supply->rdev,214					   vctrl->vtable[orig_sel].ctrl,215					   vctrl->vtable[orig_sel].ctrl,216					   PM_SUSPEND_ON))217			vctrl->sel = orig_sel;218		else219			dev_warn(&rdev->dev,220				 "failed to restore original voltage\n");221	}222 223	return ret;224}225 226static int vctrl_list_voltage(struct regulator_dev *rdev,227			      unsigned int selector)228{229	struct vctrl_data *vctrl = rdev_get_drvdata(rdev);230 231	if (selector >= rdev->desc->n_voltages)232		return -EINVAL;233 234	return vctrl->vtable[selector].out;235}236 237static int vctrl_parse_dt(struct platform_device *pdev,238			  struct vctrl_data *vctrl)239{240	int ret;241	struct device_node *np = pdev->dev.of_node;242	u32 pval;243	u32 vrange_ctrl[2];244 245	ret = of_property_read_u32(np, "ovp-threshold-percent", &pval);246	if (!ret) {247		vctrl->ovp_threshold = pval;248		if (vctrl->ovp_threshold > 100) {249			dev_err(&pdev->dev,250				"ovp-threshold-percent (%u) > 100\n",251				vctrl->ovp_threshold);252			return -EINVAL;253		}254	}255 256	ret = of_property_read_u32(np, "min-slew-down-rate", &pval);257	if (!ret) {258		vctrl->min_slew_down_rate = pval;259 260		/* We use the value as int and as divider; sanity check */261		if (vctrl->min_slew_down_rate == 0) {262			dev_err(&pdev->dev,263				"min-slew-down-rate must not be 0\n");264			return -EINVAL;265		} else if (vctrl->min_slew_down_rate > INT_MAX) {266			dev_err(&pdev->dev, "min-slew-down-rate (%u) too big\n",267				vctrl->min_slew_down_rate);268			return -EINVAL;269		}270	}271 272	if (vctrl->ovp_threshold && !vctrl->min_slew_down_rate) {273		dev_err(&pdev->dev,274			"ovp-threshold-percent requires min-slew-down-rate\n");275		return -EINVAL;276	}277 278	ret = of_property_read_u32(np, "regulator-min-microvolt", &pval);279	if (ret) {280		dev_err(&pdev->dev,281			"failed to read regulator-min-microvolt: %d\n", ret);282		return ret;283	}284	vctrl->vrange.out.min_uV = pval;285 286	ret = of_property_read_u32(np, "regulator-max-microvolt", &pval);287	if (ret) {288		dev_err(&pdev->dev,289			"failed to read regulator-max-microvolt: %d\n", ret);290		return ret;291	}292	vctrl->vrange.out.max_uV = pval;293 294	ret = of_property_read_u32_array(np, "ctrl-voltage-range", vrange_ctrl,295					 2);296	if (ret) {297		dev_err(&pdev->dev, "failed to read ctrl-voltage-range: %d\n",298			ret);299		return ret;300	}301 302	if (vrange_ctrl[0] >= vrange_ctrl[1]) {303		dev_err(&pdev->dev, "ctrl-voltage-range is invalid: %d-%d\n",304			vrange_ctrl[0], vrange_ctrl[1]);305		return -EINVAL;306	}307 308	vctrl->vrange.ctrl.min_uV = vrange_ctrl[0];309	vctrl->vrange.ctrl.max_uV = vrange_ctrl[1];310 311	return 0;312}313 314static int vctrl_cmp_ctrl_uV(const void *a, const void *b)315{316	const struct vctrl_voltage_table *at = a;317	const struct vctrl_voltage_table *bt = b;318 319	return at->ctrl - bt->ctrl;320}321 322static int vctrl_init_vtable(struct platform_device *pdev,323			     struct regulator *ctrl_reg)324{325	struct vctrl_data *vctrl = platform_get_drvdata(pdev);326	struct regulator_desc *rdesc = &vctrl->desc;327	struct vctrl_voltage_range *vrange_ctrl = &vctrl->vrange.ctrl;328	int n_voltages;329	int ctrl_uV;330	int i, idx_vt;331 332	n_voltages = regulator_count_voltages(ctrl_reg);333 334	rdesc->n_voltages = n_voltages;335 336	/* determine number of steps within the range of the vctrl regulator */337	for (i = 0; i < n_voltages; i++) {338		ctrl_uV = regulator_list_voltage(ctrl_reg, i);339 340		if (ctrl_uV < vrange_ctrl->min_uV ||341		    ctrl_uV > vrange_ctrl->max_uV)342			rdesc->n_voltages--;343	}344 345	if (rdesc->n_voltages == 0) {346		dev_err(&pdev->dev, "invalid configuration\n");347		return -EINVAL;348	}349 350	vctrl->vtable = devm_kcalloc(&pdev->dev, rdesc->n_voltages,351				     sizeof(struct vctrl_voltage_table),352				     GFP_KERNEL);353	if (!vctrl->vtable)354		return -ENOMEM;355 356	/* create mapping control <=> output voltage */357	for (i = 0, idx_vt = 0; i < n_voltages; i++) {358		ctrl_uV = regulator_list_voltage(ctrl_reg, i);359 360		if (ctrl_uV < vrange_ctrl->min_uV ||361		    ctrl_uV > vrange_ctrl->max_uV)362			continue;363 364		vctrl->vtable[idx_vt].ctrl = ctrl_uV;365		vctrl->vtable[idx_vt].out =366			vctrl_calc_output_voltage(vctrl, ctrl_uV);367		idx_vt++;368	}369 370	/* we rely on the table to be ordered by ascending voltage */371	sort(vctrl->vtable, rdesc->n_voltages,372	     sizeof(struct vctrl_voltage_table), vctrl_cmp_ctrl_uV,373	     NULL);374 375	/* pre-calculate OVP-safe downward transitions */376	for (i = rdesc->n_voltages - 1; i > 0; i--) {377		int j;378		int ovp_min_uV = (vctrl->vtable[i].out *379				  (100 - vctrl->ovp_threshold)) / 100;380 381		for (j = 0; j < i; j++) {382			if (vctrl->vtable[j].out >= ovp_min_uV) {383				vctrl->vtable[i].ovp_min_sel = j;384				break;385			}386		}387 388		if (j == i) {389			dev_warn(&pdev->dev, "switching down from %duV may cause OVP shutdown\n",390				vctrl->vtable[i].out);391			/* use next lowest voltage */392			vctrl->vtable[i].ovp_min_sel = i - 1;393		}394	}395 396	return 0;397}398 399static int vctrl_enable(struct regulator_dev *rdev)400{401	struct vctrl_data *vctrl = rdev_get_drvdata(rdev);402 403	vctrl->enabled = true;404 405	return 0;406}407 408static int vctrl_disable(struct regulator_dev *rdev)409{410	struct vctrl_data *vctrl = rdev_get_drvdata(rdev);411 412	vctrl->enabled = false;413 414	return 0;415}416 417static int vctrl_is_enabled(struct regulator_dev *rdev)418{419	struct vctrl_data *vctrl = rdev_get_drvdata(rdev);420 421	return vctrl->enabled;422}423 424static const struct regulator_ops vctrl_ops_cont = {425	.enable		  = vctrl_enable,426	.disable	  = vctrl_disable,427	.is_enabled	  = vctrl_is_enabled,428	.get_voltage	  = vctrl_get_voltage,429	.set_voltage	  = vctrl_set_voltage,430};431 432static const struct regulator_ops vctrl_ops_non_cont = {433	.enable		  = vctrl_enable,434	.disable	  = vctrl_disable,435	.is_enabled	  = vctrl_is_enabled,436	.set_voltage_sel = vctrl_set_voltage_sel,437	.get_voltage_sel = vctrl_get_voltage_sel,438	.list_voltage    = vctrl_list_voltage,439	.map_voltage     = regulator_map_voltage_iterate,440};441 442static int vctrl_probe(struct platform_device *pdev)443{444	struct device_node *np = pdev->dev.of_node;445	struct vctrl_data *vctrl;446	const struct regulator_init_data *init_data;447	struct regulator_desc *rdesc;448	struct regulator_config cfg = { };449	struct vctrl_voltage_range *vrange_ctrl;450	struct regulator *ctrl_reg;451	int ctrl_uV;452	int ret;453 454	vctrl = devm_kzalloc(&pdev->dev, sizeof(struct vctrl_data),455			     GFP_KERNEL);456	if (!vctrl)457		return -ENOMEM;458 459	platform_set_drvdata(pdev, vctrl);460 461	ret = vctrl_parse_dt(pdev, vctrl);462	if (ret)463		return ret;464 465	ctrl_reg = devm_regulator_get(&pdev->dev, "ctrl");466	if (IS_ERR(ctrl_reg))467		return PTR_ERR(ctrl_reg);468 469	vrange_ctrl = &vctrl->vrange.ctrl;470 471	rdesc = &vctrl->desc;472	rdesc->name = "vctrl";473	rdesc->type = REGULATOR_VOLTAGE;474	rdesc->owner = THIS_MODULE;475	rdesc->supply_name = "ctrl";476 477	if ((regulator_get_linear_step(ctrl_reg) == 1) ||478	    (regulator_count_voltages(ctrl_reg) == -EINVAL)) {479		rdesc->continuous_voltage_range = true;480		rdesc->ops = &vctrl_ops_cont;481	} else {482		rdesc->ops = &vctrl_ops_non_cont;483	}484 485	init_data = of_get_regulator_init_data(&pdev->dev, np, rdesc);486	if (!init_data)487		return -ENOMEM;488 489	cfg.of_node = np;490	cfg.dev = &pdev->dev;491	cfg.driver_data = vctrl;492	cfg.init_data = init_data;493 494	if (!rdesc->continuous_voltage_range) {495		ret = vctrl_init_vtable(pdev, ctrl_reg);496		if (ret)497			return ret;498 499		/* Use locked consumer API when not in regulator framework */500		ctrl_uV = regulator_get_voltage(ctrl_reg);501		if (ctrl_uV < 0) {502			dev_err(&pdev->dev, "failed to get control voltage\n");503			return ctrl_uV;504		}505 506		/* determine current voltage selector from control voltage */507		if (ctrl_uV < vrange_ctrl->min_uV) {508			vctrl->sel = 0;509		} else if (ctrl_uV > vrange_ctrl->max_uV) {510			vctrl->sel = rdesc->n_voltages - 1;511		} else {512			int i;513 514			for (i = 0; i < rdesc->n_voltages; i++) {515				if (ctrl_uV == vctrl->vtable[i].ctrl) {516					vctrl->sel = i;517					break;518				}519			}520		}521	}522 523	/* Drop ctrl-supply here in favor of regulator core managed supply */524	devm_regulator_put(ctrl_reg);525 526	vctrl->rdev = devm_regulator_register(&pdev->dev, rdesc, &cfg);527	if (IS_ERR(vctrl->rdev)) {528		ret = PTR_ERR(vctrl->rdev);529		dev_err(&pdev->dev, "failed to register regulator: %d\n", ret);530		return ret;531	}532 533	return 0;534}535 536static const struct of_device_id vctrl_of_match[] = {537	{ .compatible = "vctrl-regulator", },538	{},539};540MODULE_DEVICE_TABLE(of, vctrl_of_match);541 542static struct platform_driver vctrl_driver = {543	.probe		= vctrl_probe,544	.driver		= {545		.name		= "vctrl-regulator",546		.probe_type	= PROBE_PREFER_ASYNCHRONOUS,547		.of_match_table = of_match_ptr(vctrl_of_match),548	},549};550 551module_platform_driver(vctrl_driver);552 553MODULE_DESCRIPTION("Voltage Controlled Regulator Driver");554MODULE_AUTHOR("Matthias Kaehlcke <mka@chromium.org>");555MODULE_LICENSE("GPL v2");556