arizona-ldo1.c 8.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * arizona-ldo1.c  --  LDO1 supply for Arizona devices
 *
 * Copyright 2012 Wolfson Microelectronics PLC.
 *
 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
 *
 *  This program is free software; you can redistribute  it and/or modify it
 *  under  the terms of  the GNU General  Public License as published by the
 *  Free Software Foundation;  either version 2 of the  License, or (at your
 *  option) any later version.
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/err.h>
19
#include <linux/of.h>
20
#include <linux/gpio/consumer.h>
21 22 23
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
24
#include <linux/regulator/of_regulator.h>
25 26
#include <linux/slab.h>

27 28
#include <linux/regulator/arizona-ldo1.h>

29 30 31 32 33 34
#include <linux/mfd/arizona/core.h>
#include <linux/mfd/arizona/pdata.h>
#include <linux/mfd/arizona/registers.h>

struct arizona_ldo1 {
	struct regulator_dev *regulator;
35
	struct regmap *regmap;
36 37 38

	struct regulator_consumer_supply supply;
	struct regulator_init_data init_data;
39 40

	struct gpio_desc *ena_gpiod;
41 42
};

43 44 45 46
static int arizona_ldo1_hc_set_voltage_sel(struct regulator_dev *rdev,
					   unsigned sel)
{
	struct arizona_ldo1 *ldo = rdev_get_drvdata(rdev);
47
	struct regmap *regmap = ldo->regmap;
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
	unsigned int val;
	int ret;

	if (sel == rdev->desc->n_voltages - 1)
		val = ARIZONA_LDO1_HI_PWR;
	else
		val = 0;

	ret = regmap_update_bits(regmap, ARIZONA_LDO1_CONTROL_2,
				 ARIZONA_LDO1_HI_PWR, val);
	if (ret != 0)
		return ret;

	if (val)
		return 0;

	val = sel << ARIZONA_LDO1_VSEL_SHIFT;

	return regmap_update_bits(regmap, ARIZONA_LDO1_CONTROL_1,
				  ARIZONA_LDO1_VSEL_MASK, val);
}

static int arizona_ldo1_hc_get_voltage_sel(struct regulator_dev *rdev)
{
	struct arizona_ldo1 *ldo = rdev_get_drvdata(rdev);
73
	struct regmap *regmap = ldo->regmap;
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
	unsigned int val;
	int ret;

	ret = regmap_read(regmap, ARIZONA_LDO1_CONTROL_2, &val);
	if (ret != 0)
		return ret;

	if (val & ARIZONA_LDO1_HI_PWR)
		return rdev->desc->n_voltages - 1;

	ret = regmap_read(regmap, ARIZONA_LDO1_CONTROL_1, &val);
	if (ret != 0)
		return ret;

	return (val & ARIZONA_LDO1_VSEL_MASK) >> ARIZONA_LDO1_VSEL_SHIFT;
}

91
static const struct regulator_ops arizona_ldo1_hc_ops = {
92 93
	.list_voltage = regulator_list_voltage_linear_range,
	.map_voltage = regulator_map_voltage_linear_range,
94 95 96 97 98 99
	.get_voltage_sel = arizona_ldo1_hc_get_voltage_sel,
	.set_voltage_sel = arizona_ldo1_hc_set_voltage_sel,
	.get_bypass = regulator_get_bypass_regmap,
	.set_bypass = regulator_set_bypass_regmap,
};

100 101 102 103 104
static const struct regulator_linear_range arizona_ldo1_hc_ranges[] = {
	REGULATOR_LINEAR_RANGE(900000, 0, 0x6, 50000),
	REGULATOR_LINEAR_RANGE(1800000, 0x7, 0x7, 0),
};

105 106 107 108 109 110 111 112
static const struct regulator_desc arizona_ldo1_hc = {
	.name = "LDO1",
	.supply_name = "LDOVDD",
	.type = REGULATOR_VOLTAGE,
	.ops = &arizona_ldo1_hc_ops,

	.bypass_reg = ARIZONA_LDO1_CONTROL_1,
	.bypass_mask = ARIZONA_LDO1_BYPASS,
113 114
	.linear_ranges = arizona_ldo1_hc_ranges,
	.n_linear_ranges = ARRAY_SIZE(arizona_ldo1_hc_ranges),
115
	.n_voltages = 8,
116
	.enable_time = 1500,
117
	.ramp_delay = 24000,
118 119 120 121

	.owner = THIS_MODULE,
};

122
static const struct regulator_ops arizona_ldo1_ops = {
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
	.list_voltage = regulator_list_voltage_linear,
	.map_voltage = regulator_map_voltage_linear,
	.get_voltage_sel = regulator_get_voltage_sel_regmap,
	.set_voltage_sel = regulator_set_voltage_sel_regmap,
};

static const struct regulator_desc arizona_ldo1 = {
	.name = "LDO1",
	.supply_name = "LDOVDD",
	.type = REGULATOR_VOLTAGE,
	.ops = &arizona_ldo1_ops,

	.vsel_reg = ARIZONA_LDO1_CONTROL_1,
	.vsel_mask = ARIZONA_LDO1_VSEL_MASK,
	.min_uV = 900000,
138 139
	.uV_step = 25000,
	.n_voltages = 13,
140
	.enable_time = 500,
141
	.ramp_delay = 24000,
142 143 144 145

	.owner = THIS_MODULE,
};

146 147 148 149 150 151 152 153 154 155
static const struct regulator_init_data arizona_ldo1_dvfs = {
	.constraints = {
		.min_uV = 1200000,
		.max_uV = 1800000,
		.valid_ops_mask = REGULATOR_CHANGE_STATUS |
				  REGULATOR_CHANGE_VOLTAGE,
	},
	.num_consumer_supplies = 1,
};

156
static const struct regulator_init_data arizona_ldo1_default = {
157 158 159
	.constraints = {
		.valid_ops_mask = REGULATOR_CHANGE_STATUS,
	},
160 161 162
	.num_consumer_supplies = 1,
};

163 164 165 166 167 168 169 170 171 172
static const struct regulator_init_data arizona_ldo1_wm5110 = {
	.constraints = {
		.min_uV = 1175000,
		.max_uV = 1200000,
		.valid_ops_mask = REGULATOR_CHANGE_STATUS |
				  REGULATOR_CHANGE_VOLTAGE,
	},
	.num_consumer_supplies = 1,
};

173
static int arizona_ldo1_of_get_pdata(struct arizona_ldo1_pdata *pdata,
174
				     struct regulator_config *config,
175 176
				     const struct regulator_desc *desc,
				     bool *external_dcvdd)
177
{
178
	struct arizona_ldo1 *ldo1 = config->driver_data;
179
	struct device_node *np = config->dev->of_node;
180 181
	struct device_node *init_node, *dcvdd_node;
	struct regulator_init_data *init_data;
182

183 184
	init_node = of_get_child_by_name(np, "ldo1");
	dcvdd_node = of_parse_phandle(np, "DCVDD-supply", 0);
185 186 187 188

	if (init_node) {
		config->of_node = init_node;

189 190
		init_data = of_get_regulator_init_data(config->dev, init_node,
						       desc);
191 192 193 194 195
		if (init_data) {
			init_data->consumer_supplies = &ldo1->supply;
			init_data->num_consumer_supplies = 1;

			if (dcvdd_node && dcvdd_node != init_node)
196
				*external_dcvdd = true;
197

198
			pdata->init_data = init_data;
199 200
		}
	} else if (dcvdd_node) {
201
		*external_dcvdd = true;
202 203 204 205
	}

	of_node_put(dcvdd_node);

206 207 208
	return 0;
}

209 210 211 212 213
static int arizona_ldo1_common_init(struct platform_device *pdev,
				    struct arizona_ldo1 *ldo1,
				    const struct regulator_desc *desc,
				    struct arizona_ldo1_pdata *pdata,
				    bool *external_dcvdd)
214
{
215
	struct device *parent_dev = pdev->dev.parent;
216 217 218
	struct regulator_config config = { };
	int ret;

219
	*external_dcvdd = false;
220

221
	ldo1->supply.supply = "DCVDD";
222 223
	ldo1->init_data.consumer_supplies = &ldo1->supply;
	ldo1->supply.dev_name = dev_name(parent_dev);
224

225
	config.dev = parent_dev;
226
	config.driver_data = ldo1;
227
	config.regmap = ldo1->regmap;
228 229

	if (IS_ENABLED(CONFIG_OF)) {
230 231
		if (!dev_get_platdata(parent_dev)) {
			ret = arizona_ldo1_of_get_pdata(pdata,
232
							&config, desc,
233
							external_dcvdd);
234 235 236 237 238
			if (ret < 0)
				return ret;
		}
	}

239 240 241 242 243
	/* We assume that high output = regulator off
	 * Don't use devm, since we need to get against the parent device
	 * so clean up would happen at the wrong time
	 */
	config.ena_gpiod = gpiod_get_optional(parent_dev, "wlf,ldoena",
244
				GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE);
245 246
	if (IS_ERR(config.ena_gpiod))
		return PTR_ERR(config.ena_gpiod);
247

248 249
	ldo1->ena_gpiod = config.ena_gpiod;

250 251
	if (pdata->init_data)
		config.init_data = pdata->init_data;
252 253 254
	else
		config.init_data = &ldo1->init_data;

255 256 257 258 259
	/*
	 * LDO1 can only be used to supply DCVDD so if it has no
	 * consumers then DCVDD is supplied externally.
	 */
	if (config.init_data->num_consumer_supplies == 0)
260
		*external_dcvdd = true;
261

262
	ldo1->regulator = devm_regulator_register(&pdev->dev, desc, &config);
263 264 265

	of_node_put(config.of_node);

266 267
	if (IS_ERR(ldo1->regulator)) {
		ret = PTR_ERR(ldo1->regulator);
268
		dev_err(&pdev->dev, "Failed to register LDO1 supply: %d\n",
269 270 271 272 273 274 275 276 277
			ret);
		return ret;
	}

	platform_set_drvdata(pdev, ldo1);

	return 0;
}

278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
static int arizona_ldo1_probe(struct platform_device *pdev)
{
	struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
	struct arizona_ldo1 *ldo1;
	const struct regulator_desc *desc;
	bool external_dcvdd;
	int ret;

	ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL);
	if (!ldo1)
		return -ENOMEM;

	ldo1->regmap = arizona->regmap;

	/*
	 * Since the chip usually supplies itself we provide some
	 * default init_data for it.  This will be overridden with
	 * platform data if provided.
	 */
	switch (arizona->type) {
	case WM5102:
	case WM8997:
	case WM8998:
	case WM1814:
		desc = &arizona_ldo1_hc;
		ldo1->init_data = arizona_ldo1_dvfs;
		break;
	case WM5110:
	case WM8280:
		desc = &arizona_ldo1;
		ldo1->init_data = arizona_ldo1_wm5110;
		break;
	default:
		desc = &arizona_ldo1;
		ldo1->init_data = arizona_ldo1_default;
		break;
	}

	ret = arizona_ldo1_common_init(pdev, ldo1, desc,
				       &arizona->pdata.ldo1,
				       &external_dcvdd);
	if (ret == 0)
		arizona->external_dcvdd = external_dcvdd;

	return ret;
}

325 326 327 328 329 330 331 332 333 334
static int arizona_ldo1_remove(struct platform_device *pdev)
{
	struct arizona_ldo1 *ldo1 = platform_get_drvdata(pdev);

	if (ldo1->ena_gpiod)
		gpiod_put(ldo1->ena_gpiod);

	return 0;
}

335 336
static struct platform_driver arizona_ldo1_driver = {
	.probe = arizona_ldo1_probe,
337
	.remove = arizona_ldo1_remove,
338 339 340 341 342 343 344 345 346 347 348 349
	.driver		= {
		.name	= "arizona-ldo1",
	},
};

module_platform_driver(arizona_ldo1_driver);

/* Module information */
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_DESCRIPTION("Arizona LDO1 driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:arizona-ldo1");