brintos

brintos / linux-shallow public Read only

0
0
Text · 10.0 KiB · 4b54068 Raw
419 lines · c
1// SPDX-License-Identifier: GPL-2.0+2//3// arizona-ldo1.c  --  LDO1 supply for Arizona devices4//5// Copyright 2012 Wolfson Microelectronics PLC.6//7// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>8 9#include <linux/module.h>10#include <linux/moduleparam.h>11#include <linux/init.h>12#include <linux/bitops.h>13#include <linux/err.h>14#include <linux/of.h>15#include <linux/gpio/consumer.h>16#include <linux/platform_device.h>17#include <linux/regulator/driver.h>18#include <linux/regulator/machine.h>19#include <linux/regulator/of_regulator.h>20#include <linux/slab.h>21 22#include <linux/regulator/arizona-ldo1.h>23 24#include <linux/mfd/arizona/core.h>25#include <linux/mfd/arizona/pdata.h>26#include <linux/mfd/arizona/registers.h>27 28#include <linux/mfd/madera/core.h>29#include <linux/mfd/madera/pdata.h>30#include <linux/mfd/madera/registers.h>31 32struct arizona_ldo1 {33	struct regulator_dev *regulator;34	struct regmap *regmap;35 36	struct regulator_consumer_supply supply;37	struct regulator_init_data init_data;38 39	struct gpio_desc *ena_gpiod;40};41 42static int arizona_ldo1_hc_set_voltage_sel(struct regulator_dev *rdev,43					   unsigned sel)44{45	struct regmap *regmap = rdev_get_regmap(rdev);46	unsigned int val;47	int ret;48 49	if (sel == rdev->desc->n_voltages - 1)50		val = ARIZONA_LDO1_HI_PWR;51	else52		val = 0;53 54	ret = regmap_update_bits(regmap, ARIZONA_LDO1_CONTROL_2,55				 ARIZONA_LDO1_HI_PWR, val);56	if (ret != 0)57		return ret;58 59	if (val)60		return 0;61 62	return regulator_set_voltage_sel_regmap(rdev, sel);63}64 65static int arizona_ldo1_hc_get_voltage_sel(struct regulator_dev *rdev)66{67	struct regmap *regmap = rdev_get_regmap(rdev);68	unsigned int val;69	int ret;70 71	ret = regmap_read(regmap, ARIZONA_LDO1_CONTROL_2, &val);72	if (ret != 0)73		return ret;74 75	if (val & ARIZONA_LDO1_HI_PWR)76		return rdev->desc->n_voltages - 1;77 78	return regulator_get_voltage_sel_regmap(rdev);79}80 81static const struct regulator_ops arizona_ldo1_hc_ops = {82	.list_voltage = regulator_list_voltage_linear_range,83	.map_voltage = regulator_map_voltage_linear_range,84	.get_voltage_sel = arizona_ldo1_hc_get_voltage_sel,85	.set_voltage_sel = arizona_ldo1_hc_set_voltage_sel,86	.get_bypass = regulator_get_bypass_regmap,87	.set_bypass = regulator_set_bypass_regmap,88};89 90static const struct linear_range arizona_ldo1_hc_ranges[] = {91	REGULATOR_LINEAR_RANGE(900000, 0, 0x6, 50000),92	REGULATOR_LINEAR_RANGE(1800000, 0x7, 0x7, 0),93};94 95static const struct regulator_desc arizona_ldo1_hc = {96	.name = "LDO1",97	.supply_name = "LDOVDD",98	.type = REGULATOR_VOLTAGE,99	.ops = &arizona_ldo1_hc_ops,100 101	.vsel_reg = ARIZONA_LDO1_CONTROL_1,102	.vsel_mask = ARIZONA_LDO1_VSEL_MASK,103	.bypass_reg = ARIZONA_LDO1_CONTROL_1,104	.bypass_mask = ARIZONA_LDO1_BYPASS,105	.linear_ranges = arizona_ldo1_hc_ranges,106	.n_linear_ranges = ARRAY_SIZE(arizona_ldo1_hc_ranges),107	.n_voltages = 8,108	.enable_time = 1500,109	.ramp_delay = 24000,110 111	.owner = THIS_MODULE,112};113 114static const struct regulator_ops arizona_ldo1_ops = {115	.list_voltage = regulator_list_voltage_linear,116	.map_voltage = regulator_map_voltage_linear,117	.get_voltage_sel = regulator_get_voltage_sel_regmap,118	.set_voltage_sel = regulator_set_voltage_sel_regmap,119};120 121static const struct regulator_desc arizona_ldo1 = {122	.name = "LDO1",123	.supply_name = "LDOVDD",124	.type = REGULATOR_VOLTAGE,125	.ops = &arizona_ldo1_ops,126 127	.vsel_reg = ARIZONA_LDO1_CONTROL_1,128	.vsel_mask = ARIZONA_LDO1_VSEL_MASK,129	.min_uV = 900000,130	.uV_step = 25000,131	.n_voltages = 13,132	.enable_time = 500,133	.ramp_delay = 24000,134 135	.owner = THIS_MODULE,136};137 138static const struct regulator_init_data arizona_ldo1_dvfs = {139	.constraints = {140		.min_uV = 1200000,141		.max_uV = 1800000,142		.valid_ops_mask = REGULATOR_CHANGE_STATUS |143				  REGULATOR_CHANGE_VOLTAGE,144	},145	.num_consumer_supplies = 1,146};147 148static const struct regulator_init_data arizona_ldo1_default = {149	.constraints = {150		.valid_ops_mask = REGULATOR_CHANGE_STATUS,151	},152	.num_consumer_supplies = 1,153};154 155static const struct regulator_init_data arizona_ldo1_wm5110 = {156	.constraints = {157		.min_uV = 1175000,158		.max_uV = 1200000,159		.valid_ops_mask = REGULATOR_CHANGE_STATUS |160				  REGULATOR_CHANGE_VOLTAGE,161	},162	.num_consumer_supplies = 1,163};164 165static const struct regulator_desc madera_ldo1 = {166	.name = "LDO1",167	.supply_name = "LDOVDD",168	.type = REGULATOR_VOLTAGE,169	.ops = &arizona_ldo1_ops,170 171	.vsel_reg = MADERA_LDO1_CONTROL_1,172	.vsel_mask = MADERA_LDO1_VSEL_MASK,173	.min_uV = 900000,174	.uV_step = 25000,175	.n_voltages = 13,176	.enable_time = 3000,177 178	.owner = THIS_MODULE,179};180 181static const struct regulator_init_data madera_ldo1_default = {182	.constraints = {183		.min_uV = 1200000,184		.max_uV = 1200000,185		.valid_ops_mask = REGULATOR_CHANGE_STATUS,186	},187	.num_consumer_supplies = 1,188};189 190static int arizona_ldo1_of_get_pdata(struct arizona_ldo1_pdata *pdata,191				     struct regulator_config *config,192				     const struct regulator_desc *desc,193				     bool *external_dcvdd)194{195	struct arizona_ldo1 *ldo1 = config->driver_data;196	struct device_node *np = config->dev->of_node;197	struct device_node *init_node, *dcvdd_node;198	struct regulator_init_data *init_data;199 200	init_node = of_get_child_by_name(np, "ldo1");201	dcvdd_node = of_parse_phandle(np, "DCVDD-supply", 0);202 203	if (init_node) {204		config->of_node = init_node;205 206		init_data = of_get_regulator_init_data(config->dev, init_node,207						       desc);208		if (init_data) {209			init_data->consumer_supplies = &ldo1->supply;210			init_data->num_consumer_supplies = 1;211 212			if (dcvdd_node && dcvdd_node != init_node)213				*external_dcvdd = true;214 215			pdata->init_data = init_data;216		}217	} else if (dcvdd_node) {218		*external_dcvdd = true;219	}220 221	of_node_put(dcvdd_node);222 223	return 0;224}225 226static int arizona_ldo1_common_init(struct platform_device *pdev,227				    struct arizona_ldo1 *ldo1,228				    const struct regulator_desc *desc,229				    struct arizona_ldo1_pdata *pdata,230				    bool *external_dcvdd)231{232	struct device *parent_dev = pdev->dev.parent;233	struct regulator_config config = { };234	int ret;235 236	*external_dcvdd = false;237 238	ldo1->supply.supply = "DCVDD";239	ldo1->init_data.consumer_supplies = &ldo1->supply;240	ldo1->supply.dev_name = dev_name(parent_dev);241 242	config.dev = parent_dev;243	config.driver_data = ldo1;244	config.regmap = ldo1->regmap;245 246	if (IS_ENABLED(CONFIG_OF)) {247		if (!dev_get_platdata(parent_dev)) {248			ret = arizona_ldo1_of_get_pdata(pdata,249							&config, desc,250							external_dcvdd);251			if (ret < 0)252				return ret;253		}254	}255 256	/* We assume that high output = regulator off257	 * Don't use devm, since we need to get against the parent device258	 * so clean up would happen at the wrong time259	 */260	config.ena_gpiod = gpiod_get_optional(parent_dev, "wlf,ldoena",261				GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE);262	if (IS_ERR(config.ena_gpiod))263		return PTR_ERR(config.ena_gpiod);264 265	ldo1->ena_gpiod = config.ena_gpiod;266 267	if (pdata->init_data)268		config.init_data = pdata->init_data;269	else270		config.init_data = &ldo1->init_data;271 272	/*273	 * LDO1 can only be used to supply DCVDD so if it has no274	 * consumers then DCVDD is supplied externally.275	 */276	if (config.init_data->num_consumer_supplies == 0)277		*external_dcvdd = true;278 279	ldo1->regulator = devm_regulator_register(&pdev->dev, desc, &config);280 281	of_node_put(config.of_node);282 283	if (IS_ERR(ldo1->regulator)) {284		ret = PTR_ERR(ldo1->regulator);285		dev_err(&pdev->dev, "Failed to register LDO1 supply: %d\n",286			ret);287		return ret;288	}289 290	platform_set_drvdata(pdev, ldo1);291 292	return 0;293}294 295static int arizona_ldo1_probe(struct platform_device *pdev)296{297	struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);298	struct arizona_ldo1 *ldo1;299	const struct regulator_desc *desc;300	bool external_dcvdd;301	int ret;302 303	ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL);304	if (!ldo1)305		return -ENOMEM;306 307	ldo1->regmap = arizona->regmap;308 309	/*310	 * Since the chip usually supplies itself we provide some311	 * default init_data for it.  This will be overridden with312	 * platform data if provided.313	 */314	switch (arizona->type) {315	case WM5102:316	case WM8997:317	case WM8998:318	case WM1814:319		desc = &arizona_ldo1_hc;320		ldo1->init_data = arizona_ldo1_dvfs;321		break;322	case WM5110:323	case WM8280:324		desc = &arizona_ldo1;325		ldo1->init_data = arizona_ldo1_wm5110;326		break;327	default:328		desc = &arizona_ldo1;329		ldo1->init_data = arizona_ldo1_default;330		break;331	}332 333	ret = arizona_ldo1_common_init(pdev, ldo1, desc,334				       &arizona->pdata.ldo1,335				       &external_dcvdd);336	if (ret == 0)337		arizona->external_dcvdd = external_dcvdd;338 339	return ret;340}341 342static void arizona_ldo1_remove(struct platform_device *pdev)343{344	struct arizona_ldo1 *ldo1 = platform_get_drvdata(pdev);345 346	if (ldo1->ena_gpiod)347		gpiod_put(ldo1->ena_gpiod);348}349 350static int madera_ldo1_probe(struct platform_device *pdev)351{352	struct madera *madera = dev_get_drvdata(pdev->dev.parent);353	struct arizona_ldo1 *ldo1;354	bool external_dcvdd;355	int ret;356 357	ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL);358	if (!ldo1)359		return -ENOMEM;360 361	ldo1->regmap = madera->regmap;362 363	ldo1->init_data = madera_ldo1_default;364 365	ret = arizona_ldo1_common_init(pdev, ldo1, &madera_ldo1,366				       &madera->pdata.ldo1,367				       &external_dcvdd);368	if (ret)369		return ret;370 371	madera->internal_dcvdd = !external_dcvdd;372 373	return 0;374}375 376static struct platform_driver arizona_ldo1_driver = {377	.probe = arizona_ldo1_probe,378	.remove_new = arizona_ldo1_remove,379	.driver		= {380		.name	= "arizona-ldo1",381		.probe_type = PROBE_FORCE_SYNCHRONOUS,382	},383};384 385static struct platform_driver madera_ldo1_driver = {386	.probe = madera_ldo1_probe,387	.remove_new = arizona_ldo1_remove,388	.driver		= {389		.name	= "madera-ldo1",390		.probe_type = PROBE_FORCE_SYNCHRONOUS,391	},392};393 394static struct platform_driver * const madera_ldo1_drivers[] = {395	&arizona_ldo1_driver,396	&madera_ldo1_driver,397};398 399static int __init arizona_ldo1_init(void)400{401	return platform_register_drivers(madera_ldo1_drivers,402					 ARRAY_SIZE(madera_ldo1_drivers));403}404module_init(arizona_ldo1_init);405 406static void __exit madera_ldo1_exit(void)407{408	platform_unregister_drivers(madera_ldo1_drivers,409				    ARRAY_SIZE(madera_ldo1_drivers));410}411module_exit(madera_ldo1_exit);412 413/* Module information */414MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");415MODULE_DESCRIPTION("Arizona LDO1 driver");416MODULE_LICENSE("GPL");417MODULE_ALIAS("platform:arizona-ldo1");418MODULE_ALIAS("platform:madera-ldo1");419