162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * fixed.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2008 Wolfson Microelectronics PLC.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Copyright (c) 2009 Nokia Corporation
1062306a36Sopenharmony_ci * Roger Quadros <ext-roger.quadros@nokia.com>
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * This is useful for systems with mixed controllable and
1362306a36Sopenharmony_ci * non-controllable regulators, as well as for allowing testing on
1462306a36Sopenharmony_ci * systems with no controllable regulators.
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/err.h>
1862306a36Sopenharmony_ci#include <linux/mutex.h>
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/platform_device.h>
2162306a36Sopenharmony_ci#include <linux/pm_domain.h>
2262306a36Sopenharmony_ci#include <linux/pm_opp.h>
2362306a36Sopenharmony_ci#include <linux/regulator/driver.h>
2462306a36Sopenharmony_ci#include <linux/regulator/fixed.h>
2562306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
2662306a36Sopenharmony_ci#include <linux/slab.h>
2762306a36Sopenharmony_ci#include <linux/of.h>
2862306a36Sopenharmony_ci#include <linux/regulator/of_regulator.h>
2962306a36Sopenharmony_ci#include <linux/regulator/machine.h>
3062306a36Sopenharmony_ci#include <linux/clk.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistruct fixed_voltage_data {
3462306a36Sopenharmony_ci	struct regulator_desc desc;
3562306a36Sopenharmony_ci	struct regulator_dev *dev;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	struct clk *enable_clock;
3862306a36Sopenharmony_ci	unsigned int enable_counter;
3962306a36Sopenharmony_ci	int performance_state;
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistruct fixed_dev_type {
4362306a36Sopenharmony_ci	bool has_enable_clock;
4462306a36Sopenharmony_ci	bool has_performance_state;
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic int reg_clock_enable(struct regulator_dev *rdev)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	struct fixed_voltage_data *priv = rdev_get_drvdata(rdev);
5062306a36Sopenharmony_ci	int ret = 0;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	ret = clk_prepare_enable(priv->enable_clock);
5362306a36Sopenharmony_ci	if (ret)
5462306a36Sopenharmony_ci		return ret;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	priv->enable_counter++;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	return ret;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic int reg_clock_disable(struct regulator_dev *rdev)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct fixed_voltage_data *priv = rdev_get_drvdata(rdev);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	clk_disable_unprepare(priv->enable_clock);
6662306a36Sopenharmony_ci	priv->enable_counter--;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	return 0;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic int reg_domain_enable(struct regulator_dev *rdev)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct fixed_voltage_data *priv = rdev_get_drvdata(rdev);
7462306a36Sopenharmony_ci	struct device *dev = rdev->dev.parent;
7562306a36Sopenharmony_ci	int ret;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	ret = dev_pm_genpd_set_performance_state(dev, priv->performance_state);
7862306a36Sopenharmony_ci	if (ret)
7962306a36Sopenharmony_ci		return ret;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	priv->enable_counter++;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return ret;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic int reg_domain_disable(struct regulator_dev *rdev)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	struct fixed_voltage_data *priv = rdev_get_drvdata(rdev);
8962306a36Sopenharmony_ci	struct device *dev = rdev->dev.parent;
9062306a36Sopenharmony_ci	int ret;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	ret = dev_pm_genpd_set_performance_state(dev, 0);
9362306a36Sopenharmony_ci	if (ret)
9462306a36Sopenharmony_ci		return ret;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	priv->enable_counter--;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return 0;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic int reg_is_enabled(struct regulator_dev *rdev)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct fixed_voltage_data *priv = rdev_get_drvdata(rdev);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	return priv->enable_counter > 0;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/**
11062306a36Sopenharmony_ci * of_get_fixed_voltage_config - extract fixed_voltage_config structure info
11162306a36Sopenharmony_ci * @dev: device requesting for fixed_voltage_config
11262306a36Sopenharmony_ci * @desc: regulator description
11362306a36Sopenharmony_ci *
11462306a36Sopenharmony_ci * Populates fixed_voltage_config structure by extracting data from device
11562306a36Sopenharmony_ci * tree node, returns a pointer to the populated structure of NULL if memory
11662306a36Sopenharmony_ci * alloc fails.
11762306a36Sopenharmony_ci */
11862306a36Sopenharmony_cistatic struct fixed_voltage_config *
11962306a36Sopenharmony_ciof_get_fixed_voltage_config(struct device *dev,
12062306a36Sopenharmony_ci			    const struct regulator_desc *desc)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct fixed_voltage_config *config;
12362306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
12462306a36Sopenharmony_ci	struct regulator_init_data *init_data;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	config = devm_kzalloc(dev, sizeof(struct fixed_voltage_config),
12762306a36Sopenharmony_ci								 GFP_KERNEL);
12862306a36Sopenharmony_ci	if (!config)
12962306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	config->init_data = of_get_regulator_init_data(dev, dev->of_node, desc);
13262306a36Sopenharmony_ci	if (!config->init_data)
13362306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	init_data = config->init_data;
13662306a36Sopenharmony_ci	init_data->constraints.apply_uV = 0;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	config->supply_name = init_data->constraints.name;
13962306a36Sopenharmony_ci	if (init_data->constraints.min_uV == init_data->constraints.max_uV) {
14062306a36Sopenharmony_ci		config->microvolts = init_data->constraints.min_uV;
14162306a36Sopenharmony_ci	} else {
14262306a36Sopenharmony_ci		dev_err(dev,
14362306a36Sopenharmony_ci			 "Fixed regulator specified with variable voltages\n");
14462306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (init_data->constraints.boot_on)
14862306a36Sopenharmony_ci		config->enabled_at_boot = true;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	of_property_read_u32(np, "startup-delay-us", &config->startup_delay);
15162306a36Sopenharmony_ci	of_property_read_u32(np, "off-on-delay-us", &config->off_on_delay);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if (of_property_present(np, "vin-supply"))
15462306a36Sopenharmony_ci		config->input_supply = "vin";
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	return config;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic const struct regulator_ops fixed_voltage_ops = {
16062306a36Sopenharmony_ci};
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic const struct regulator_ops fixed_voltage_clkenabled_ops = {
16362306a36Sopenharmony_ci	.enable = reg_clock_enable,
16462306a36Sopenharmony_ci	.disable = reg_clock_disable,
16562306a36Sopenharmony_ci	.is_enabled = reg_is_enabled,
16662306a36Sopenharmony_ci};
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic const struct regulator_ops fixed_voltage_domain_ops = {
16962306a36Sopenharmony_ci	.enable = reg_domain_enable,
17062306a36Sopenharmony_ci	.disable = reg_domain_disable,
17162306a36Sopenharmony_ci	.is_enabled = reg_is_enabled,
17262306a36Sopenharmony_ci};
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic int reg_fixed_voltage_probe(struct platform_device *pdev)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
17762306a36Sopenharmony_ci	struct fixed_voltage_config *config;
17862306a36Sopenharmony_ci	struct fixed_voltage_data *drvdata;
17962306a36Sopenharmony_ci	const struct fixed_dev_type *drvtype = of_device_get_match_data(dev);
18062306a36Sopenharmony_ci	struct regulator_config cfg = { };
18162306a36Sopenharmony_ci	enum gpiod_flags gflags;
18262306a36Sopenharmony_ci	int ret;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	drvdata = devm_kzalloc(&pdev->dev, sizeof(struct fixed_voltage_data),
18562306a36Sopenharmony_ci			       GFP_KERNEL);
18662306a36Sopenharmony_ci	if (!drvdata)
18762306a36Sopenharmony_ci		return -ENOMEM;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	if (pdev->dev.of_node) {
19062306a36Sopenharmony_ci		config = of_get_fixed_voltage_config(&pdev->dev,
19162306a36Sopenharmony_ci						     &drvdata->desc);
19262306a36Sopenharmony_ci		if (IS_ERR(config))
19362306a36Sopenharmony_ci			return PTR_ERR(config);
19462306a36Sopenharmony_ci	} else {
19562306a36Sopenharmony_ci		config = dev_get_platdata(&pdev->dev);
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	if (!config)
19962306a36Sopenharmony_ci		return -ENOMEM;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	drvdata->desc.name = devm_kstrdup(&pdev->dev,
20262306a36Sopenharmony_ci					  config->supply_name,
20362306a36Sopenharmony_ci					  GFP_KERNEL);
20462306a36Sopenharmony_ci	if (drvdata->desc.name == NULL) {
20562306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to allocate supply name\n");
20662306a36Sopenharmony_ci		return -ENOMEM;
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci	drvdata->desc.type = REGULATOR_VOLTAGE;
20962306a36Sopenharmony_ci	drvdata->desc.owner = THIS_MODULE;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if (drvtype && drvtype->has_enable_clock) {
21262306a36Sopenharmony_ci		drvdata->desc.ops = &fixed_voltage_clkenabled_ops;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci		drvdata->enable_clock = devm_clk_get(dev, NULL);
21562306a36Sopenharmony_ci		if (IS_ERR(drvdata->enable_clock)) {
21662306a36Sopenharmony_ci			dev_err(dev, "Can't get enable-clock from devicetree\n");
21762306a36Sopenharmony_ci			return PTR_ERR(drvdata->enable_clock);
21862306a36Sopenharmony_ci		}
21962306a36Sopenharmony_ci	} else if (drvtype && drvtype->has_performance_state) {
22062306a36Sopenharmony_ci		drvdata->desc.ops = &fixed_voltage_domain_ops;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci		drvdata->performance_state = of_get_required_opp_performance_state(dev->of_node, 0);
22362306a36Sopenharmony_ci		if (drvdata->performance_state < 0) {
22462306a36Sopenharmony_ci			dev_err(dev, "Can't get performance state from devicetree\n");
22562306a36Sopenharmony_ci			return drvdata->performance_state;
22662306a36Sopenharmony_ci		}
22762306a36Sopenharmony_ci	} else {
22862306a36Sopenharmony_ci		drvdata->desc.ops = &fixed_voltage_ops;
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	drvdata->desc.enable_time = config->startup_delay;
23262306a36Sopenharmony_ci	drvdata->desc.off_on_delay = config->off_on_delay;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (config->input_supply) {
23562306a36Sopenharmony_ci		drvdata->desc.supply_name = devm_kstrdup(&pdev->dev,
23662306a36Sopenharmony_ci					    config->input_supply,
23762306a36Sopenharmony_ci					    GFP_KERNEL);
23862306a36Sopenharmony_ci		if (!drvdata->desc.supply_name)
23962306a36Sopenharmony_ci			return -ENOMEM;
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	if (config->microvolts)
24362306a36Sopenharmony_ci		drvdata->desc.n_voltages = 1;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	drvdata->desc.fixed_uV = config->microvolts;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	/*
24862306a36Sopenharmony_ci	 * The signal will be inverted by the GPIO core if flagged so in the
24962306a36Sopenharmony_ci	 * descriptor.
25062306a36Sopenharmony_ci	 */
25162306a36Sopenharmony_ci	if (config->enabled_at_boot)
25262306a36Sopenharmony_ci		gflags = GPIOD_OUT_HIGH;
25362306a36Sopenharmony_ci	else
25462306a36Sopenharmony_ci		gflags = GPIOD_OUT_LOW;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	/*
25762306a36Sopenharmony_ci	 * Some fixed regulators share the enable line between two
25862306a36Sopenharmony_ci	 * regulators which makes it necessary to get a handle on the
25962306a36Sopenharmony_ci	 * same descriptor for two different consumers. This will get
26062306a36Sopenharmony_ci	 * the GPIO descriptor, but only the first call will initialize
26162306a36Sopenharmony_ci	 * it so any flags such as inversion or open drain will only
26262306a36Sopenharmony_ci	 * be set up by the first caller and assumed identical on the
26362306a36Sopenharmony_ci	 * next caller.
26462306a36Sopenharmony_ci	 *
26562306a36Sopenharmony_ci	 * FIXME: find a better way to deal with this.
26662306a36Sopenharmony_ci	 */
26762306a36Sopenharmony_ci	gflags |= GPIOD_FLAGS_BIT_NONEXCLUSIVE;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	/*
27062306a36Sopenharmony_ci	 * Do not use devm* here: the regulator core takes over the
27162306a36Sopenharmony_ci	 * lifecycle management of the GPIO descriptor.
27262306a36Sopenharmony_ci	 */
27362306a36Sopenharmony_ci	cfg.ena_gpiod = gpiod_get_optional(&pdev->dev, NULL, gflags);
27462306a36Sopenharmony_ci	if (IS_ERR(cfg.ena_gpiod))
27562306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, PTR_ERR(cfg.ena_gpiod),
27662306a36Sopenharmony_ci				     "can't get GPIO\n");
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	cfg.dev = &pdev->dev;
27962306a36Sopenharmony_ci	cfg.init_data = config->init_data;
28062306a36Sopenharmony_ci	cfg.driver_data = drvdata;
28162306a36Sopenharmony_ci	cfg.of_node = pdev->dev.of_node;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	drvdata->dev = devm_regulator_register(&pdev->dev, &drvdata->desc,
28462306a36Sopenharmony_ci					       &cfg);
28562306a36Sopenharmony_ci	if (IS_ERR(drvdata->dev)) {
28662306a36Sopenharmony_ci		ret = dev_err_probe(&pdev->dev, PTR_ERR(drvdata->dev),
28762306a36Sopenharmony_ci				    "Failed to register regulator: %ld\n",
28862306a36Sopenharmony_ci				    PTR_ERR(drvdata->dev));
28962306a36Sopenharmony_ci		return ret;
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	platform_set_drvdata(pdev, drvdata);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "%s supplying %duV\n", drvdata->desc.name,
29562306a36Sopenharmony_ci		drvdata->desc.fixed_uV);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	return 0;
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci#if defined(CONFIG_OF)
30162306a36Sopenharmony_cistatic const struct fixed_dev_type fixed_voltage_data = {
30262306a36Sopenharmony_ci	.has_enable_clock = false,
30362306a36Sopenharmony_ci};
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic const struct fixed_dev_type fixed_clkenable_data = {
30662306a36Sopenharmony_ci	.has_enable_clock = true,
30762306a36Sopenharmony_ci};
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic const struct fixed_dev_type fixed_domain_data = {
31062306a36Sopenharmony_ci	.has_performance_state = true,
31162306a36Sopenharmony_ci};
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic const struct of_device_id fixed_of_match[] = {
31462306a36Sopenharmony_ci	{
31562306a36Sopenharmony_ci		.compatible = "regulator-fixed",
31662306a36Sopenharmony_ci		.data = &fixed_voltage_data,
31762306a36Sopenharmony_ci	},
31862306a36Sopenharmony_ci	{
31962306a36Sopenharmony_ci		.compatible = "regulator-fixed-clock",
32062306a36Sopenharmony_ci		.data = &fixed_clkenable_data,
32162306a36Sopenharmony_ci	},
32262306a36Sopenharmony_ci	{
32362306a36Sopenharmony_ci		.compatible = "regulator-fixed-domain",
32462306a36Sopenharmony_ci		.data = &fixed_domain_data,
32562306a36Sopenharmony_ci	},
32662306a36Sopenharmony_ci	{
32762306a36Sopenharmony_ci	},
32862306a36Sopenharmony_ci};
32962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, fixed_of_match);
33062306a36Sopenharmony_ci#endif
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic struct platform_driver regulator_fixed_voltage_driver = {
33362306a36Sopenharmony_ci	.probe		= reg_fixed_voltage_probe,
33462306a36Sopenharmony_ci	.driver		= {
33562306a36Sopenharmony_ci		.name		= "reg-fixed-voltage",
33662306a36Sopenharmony_ci		.probe_type	= PROBE_PREFER_ASYNCHRONOUS,
33762306a36Sopenharmony_ci		.of_match_table = of_match_ptr(fixed_of_match),
33862306a36Sopenharmony_ci	},
33962306a36Sopenharmony_ci};
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic int __init regulator_fixed_voltage_init(void)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	return platform_driver_register(&regulator_fixed_voltage_driver);
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_cisubsys_initcall(regulator_fixed_voltage_init);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic void __exit regulator_fixed_voltage_exit(void)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	platform_driver_unregister(&regulator_fixed_voltage_driver);
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_cimodule_exit(regulator_fixed_voltage_exit);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ciMODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
35462306a36Sopenharmony_ciMODULE_DESCRIPTION("Fixed voltage regulator");
35562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
35662306a36Sopenharmony_ciMODULE_ALIAS("platform:reg-fixed-voltage");
357