brintos

brintos / linux-shallow public Read only

0
0
Text · 10.5 KiB · 1cb647e Raw
409 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * fixed.c4 *5 * Copyright 2008 Wolfson Microelectronics PLC.6 *7 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>8 *9 * Copyright (c) 2009 Nokia Corporation10 * Roger Quadros <ext-roger.quadros@nokia.com>11 *12 * This is useful for systems with mixed controllable and13 * non-controllable regulators, as well as for allowing testing on14 * systems with no controllable regulators.15 */16 17#include <linux/err.h>18#include <linux/mutex.h>19#include <linux/module.h>20#include <linux/platform_device.h>21#include <linux/pm_domain.h>22#include <linux/pm_opp.h>23#include <linux/reboot.h>24#include <linux/regulator/driver.h>25#include <linux/regulator/fixed.h>26#include <linux/gpio/consumer.h>27#include <linux/slab.h>28#include <linux/of.h>29#include <linux/regulator/of_regulator.h>30#include <linux/regulator/machine.h>31#include <linux/clk.h>32 33/* Default time in millisecond to wait for emergency shutdown */34#define FV_DEF_EMERG_SHUTDWN_TMO	1035 36struct fixed_voltage_data {37	struct regulator_desc desc;38	struct regulator_dev *dev;39 40	struct clk *enable_clock;41	unsigned int enable_counter;42	int performance_state;43};44 45struct fixed_dev_type {46	bool has_enable_clock;47	bool has_performance_state;48};49 50static int reg_clock_enable(struct regulator_dev *rdev)51{52	struct fixed_voltage_data *priv = rdev_get_drvdata(rdev);53	int ret = 0;54 55	ret = clk_prepare_enable(priv->enable_clock);56	if (ret)57		return ret;58 59	priv->enable_counter++;60 61	return ret;62}63 64static int reg_clock_disable(struct regulator_dev *rdev)65{66	struct fixed_voltage_data *priv = rdev_get_drvdata(rdev);67 68	clk_disable_unprepare(priv->enable_clock);69	priv->enable_counter--;70 71	return 0;72}73 74static int reg_domain_enable(struct regulator_dev *rdev)75{76	struct fixed_voltage_data *priv = rdev_get_drvdata(rdev);77	struct device *dev = rdev->dev.parent;78	int ret;79 80	ret = dev_pm_genpd_set_performance_state(dev, priv->performance_state);81	if (ret)82		return ret;83 84	priv->enable_counter++;85 86	return ret;87}88 89static int reg_domain_disable(struct regulator_dev *rdev)90{91	struct fixed_voltage_data *priv = rdev_get_drvdata(rdev);92	struct device *dev = rdev->dev.parent;93	int ret;94 95	ret = dev_pm_genpd_set_performance_state(dev, 0);96	if (ret)97		return ret;98 99	priv->enable_counter--;100 101	return 0;102}103 104static int reg_is_enabled(struct regulator_dev *rdev)105{106	struct fixed_voltage_data *priv = rdev_get_drvdata(rdev);107 108	return priv->enable_counter > 0;109}110 111static irqreturn_t reg_fixed_under_voltage_irq_handler(int irq, void *data)112{113	struct fixed_voltage_data *priv = data;114	struct regulator_dev *rdev = priv->dev;115 116	regulator_notifier_call_chain(rdev, REGULATOR_EVENT_UNDER_VOLTAGE,117				      NULL);118 119	return IRQ_HANDLED;120}121 122/**123 * reg_fixed_get_irqs - Get and register the optional IRQ for fixed voltage124 *                      regulator.125 * @dev: Pointer to the device structure.126 * @priv: Pointer to fixed_voltage_data structure containing private data.127 *128 * This function tries to get the IRQ from the device firmware node.129 * If it's an optional IRQ and not found, it returns 0.130 * Otherwise, it attempts to request the threaded IRQ.131 *132 * Return: 0 on success, or a negative error number on failure.133 */134static int reg_fixed_get_irqs(struct device *dev,135			      struct fixed_voltage_data *priv)136{137	int ret;138 139	ret = fwnode_irq_get(dev_fwnode(dev), 0);140	/* This is optional IRQ. If not found we will get -EINVAL */141	if (ret == -EINVAL)142		return 0;143	if (ret < 0)144		return dev_err_probe(dev, ret, "Failed to get IRQ\n");145 146	ret = devm_request_threaded_irq(dev, ret, NULL,147					reg_fixed_under_voltage_irq_handler,148					IRQF_ONESHOT, "under-voltage", priv);149	if (ret)150		return dev_err_probe(dev, ret, "Failed to request IRQ\n");151 152	return 0;153}154 155/**156 * of_get_fixed_voltage_config - extract fixed_voltage_config structure info157 * @dev: device requesting for fixed_voltage_config158 * @desc: regulator description159 *160 * Populates fixed_voltage_config structure by extracting data from device161 * tree node.162 *163 * Return: Pointer to a populated &struct fixed_voltage_config or %NULL if164 *	   memory allocation fails.165 */166static struct fixed_voltage_config *167of_get_fixed_voltage_config(struct device *dev,168			    const struct regulator_desc *desc)169{170	struct fixed_voltage_config *config;171	struct device_node *np = dev->of_node;172	struct regulator_init_data *init_data;173 174	config = devm_kzalloc(dev, sizeof(struct fixed_voltage_config),175								 GFP_KERNEL);176	if (!config)177		return ERR_PTR(-ENOMEM);178 179	config->init_data = of_get_regulator_init_data(dev, dev->of_node, desc);180	if (!config->init_data)181		return ERR_PTR(-EINVAL);182 183	init_data = config->init_data;184	init_data->constraints.apply_uV = 0;185 186	config->supply_name = init_data->constraints.name;187	if (init_data->constraints.min_uV == init_data->constraints.max_uV) {188		config->microvolts = init_data->constraints.min_uV;189	} else {190		dev_err(dev,191			 "Fixed regulator specified with variable voltages\n");192		return ERR_PTR(-EINVAL);193	}194 195	if (init_data->constraints.boot_on)196		config->enabled_at_boot = true;197 198	of_property_read_u32(np, "startup-delay-us", &config->startup_delay);199	of_property_read_u32(np, "off-on-delay-us", &config->off_on_delay);200 201	if (of_property_present(np, "vin-supply"))202		config->input_supply = "vin";203 204	return config;205}206 207static const struct regulator_ops fixed_voltage_ops = {208};209 210static const struct regulator_ops fixed_voltage_clkenabled_ops = {211	.enable = reg_clock_enable,212	.disable = reg_clock_disable,213	.is_enabled = reg_is_enabled,214};215 216static const struct regulator_ops fixed_voltage_domain_ops = {217	.enable = reg_domain_enable,218	.disable = reg_domain_disable,219	.is_enabled = reg_is_enabled,220};221 222static int reg_fixed_voltage_probe(struct platform_device *pdev)223{224	struct device *dev = &pdev->dev;225	struct fixed_voltage_config *config;226	struct fixed_voltage_data *drvdata;227	const struct fixed_dev_type *drvtype = of_device_get_match_data(dev);228	struct regulator_config cfg = { };229	enum gpiod_flags gflags;230	int ret;231 232	drvdata = devm_kzalloc(&pdev->dev, sizeof(struct fixed_voltage_data),233			       GFP_KERNEL);234	if (!drvdata)235		return -ENOMEM;236 237	if (pdev->dev.of_node) {238		config = of_get_fixed_voltage_config(&pdev->dev,239						     &drvdata->desc);240		if (IS_ERR(config))241			return PTR_ERR(config);242	} else {243		config = dev_get_platdata(&pdev->dev);244	}245 246	if (!config)247		return -ENOMEM;248 249	drvdata->desc.name = devm_kstrdup(&pdev->dev,250					  config->supply_name,251					  GFP_KERNEL);252	if (drvdata->desc.name == NULL) {253		dev_err(&pdev->dev, "Failed to allocate supply name\n");254		return -ENOMEM;255	}256	drvdata->desc.type = REGULATOR_VOLTAGE;257	drvdata->desc.owner = THIS_MODULE;258 259	if (drvtype && drvtype->has_enable_clock) {260		drvdata->desc.ops = &fixed_voltage_clkenabled_ops;261 262		drvdata->enable_clock = devm_clk_get(dev, NULL);263		if (IS_ERR(drvdata->enable_clock)) {264			dev_err(dev, "Can't get enable-clock from devicetree\n");265			return PTR_ERR(drvdata->enable_clock);266		}267	} else if (drvtype && drvtype->has_performance_state) {268		drvdata->desc.ops = &fixed_voltage_domain_ops;269 270		drvdata->performance_state = of_get_required_opp_performance_state(dev->of_node, 0);271		if (drvdata->performance_state < 0) {272			dev_err(dev, "Can't get performance state from devicetree\n");273			return drvdata->performance_state;274		}275	} else {276		drvdata->desc.ops = &fixed_voltage_ops;277	}278 279	drvdata->desc.enable_time = config->startup_delay;280	drvdata->desc.off_on_delay = config->off_on_delay;281 282	if (config->input_supply) {283		drvdata->desc.supply_name = devm_kstrdup(&pdev->dev,284					    config->input_supply,285					    GFP_KERNEL);286		if (!drvdata->desc.supply_name)287			return -ENOMEM;288	}289 290	if (config->microvolts)291		drvdata->desc.n_voltages = 1;292 293	drvdata->desc.fixed_uV = config->microvolts;294 295	/*296	 * The signal will be inverted by the GPIO core if flagged so in the297	 * descriptor.298	 */299	if (config->enabled_at_boot)300		gflags = GPIOD_OUT_HIGH;301	else302		gflags = GPIOD_OUT_LOW;303 304	/*305	 * Some fixed regulators share the enable line between two306	 * regulators which makes it necessary to get a handle on the307	 * same descriptor for two different consumers. This will get308	 * the GPIO descriptor, but only the first call will initialize309	 * it so any flags such as inversion or open drain will only310	 * be set up by the first caller and assumed identical on the311	 * next caller.312	 *313	 * FIXME: find a better way to deal with this.314	 */315	gflags |= GPIOD_FLAGS_BIT_NONEXCLUSIVE;316 317	/*318	 * Do not use devm* here: the regulator core takes over the319	 * lifecycle management of the GPIO descriptor.320	 */321	cfg.ena_gpiod = gpiod_get_optional(&pdev->dev, NULL, gflags);322	if (IS_ERR(cfg.ena_gpiod))323		return dev_err_probe(&pdev->dev, PTR_ERR(cfg.ena_gpiod),324				     "can't get GPIO\n");325 326	cfg.dev = &pdev->dev;327	cfg.init_data = config->init_data;328	cfg.driver_data = drvdata;329	cfg.of_node = pdev->dev.of_node;330 331	drvdata->dev = devm_regulator_register(&pdev->dev, &drvdata->desc,332					       &cfg);333	if (IS_ERR(drvdata->dev)) {334		ret = dev_err_probe(&pdev->dev, PTR_ERR(drvdata->dev),335				    "Failed to register regulator: %ld\n",336				    PTR_ERR(drvdata->dev));337		return ret;338	}339 340	platform_set_drvdata(pdev, drvdata);341 342	dev_dbg(&pdev->dev, "%s supplying %duV\n", drvdata->desc.name,343		drvdata->desc.fixed_uV);344 345	ret = reg_fixed_get_irqs(dev, drvdata);346	if (ret)347		return ret;348 349	return 0;350}351 352#if defined(CONFIG_OF)353static const struct fixed_dev_type fixed_voltage_data = {354	.has_enable_clock = false,355};356 357static const struct fixed_dev_type fixed_clkenable_data = {358	.has_enable_clock = true,359};360 361static const struct fixed_dev_type fixed_domain_data = {362	.has_performance_state = true,363};364 365static const struct of_device_id fixed_of_match[] = {366	{367		.compatible = "regulator-fixed",368		.data = &fixed_voltage_data,369	},370	{371		.compatible = "regulator-fixed-clock",372		.data = &fixed_clkenable_data,373	},374	{375		.compatible = "regulator-fixed-domain",376		.data = &fixed_domain_data,377	},378	{379	},380};381MODULE_DEVICE_TABLE(of, fixed_of_match);382#endif383 384static struct platform_driver regulator_fixed_voltage_driver = {385	.probe		= reg_fixed_voltage_probe,386	.driver		= {387		.name		= "reg-fixed-voltage",388		.probe_type	= PROBE_PREFER_ASYNCHRONOUS,389		.of_match_table = of_match_ptr(fixed_of_match),390	},391};392 393static int __init regulator_fixed_voltage_init(void)394{395	return platform_driver_register(&regulator_fixed_voltage_driver);396}397subsys_initcall(regulator_fixed_voltage_init);398 399static void __exit regulator_fixed_voltage_exit(void)400{401	platform_driver_unregister(&regulator_fixed_voltage_driver);402}403module_exit(regulator_fixed_voltage_exit);404 405MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");406MODULE_DESCRIPTION("Fixed voltage regulator");407MODULE_LICENSE("GPL");408MODULE_ALIAS("platform:reg-fixed-voltage");409