diff options
author | Chen-Yu Tsai <wens@csie.org> | 2016-08-27 03:55:39 -0400 |
---|---|---|
committer | Lee Jones <lee.jones@linaro.org> | 2016-09-13 07:49:45 -0400 |
commit | 2ca342d391e3d8b56ed64626db8cfba8101b7c1d (patch) | |
tree | bcaf1b7dfd41dfe3e1357e4a9127ce4791e260cd /drivers/regulator | |
parent | 29b4817d4018df78086157ea3a55c1d9424a7cfc (diff) |
regulator: axp20x: Support AXP806 variant
The X-Powers AXP806 PMIC has a new set of buck and LDO regulators, and
also a switch. The buck regulators support teaming into multi-phase
groups, with A+B, A+B+C, D+E groupings.
Some registers controlling DCDC converter work settings are at different
offsets. Deal with them as well.
Add support for this new variant.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Acked-by: Mark Brown <broonie@kernel.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Diffstat (limited to 'drivers/regulator')
-rw-r--r-- | drivers/regulator/axp20x-regulator.c | 118 |
1 files changed, 111 insertions, 7 deletions
diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index 6d9ac76a772f..54382ef902c6 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c | |||
@@ -244,16 +244,64 @@ static const struct regulator_desc axp22x_drivevbus_regulator = { | |||
244 | .ops = &axp20x_ops_sw, | 244 | .ops = &axp20x_ops_sw, |
245 | }; | 245 | }; |
246 | 246 | ||
247 | static const struct regulator_linear_range axp809_dcdc4_ranges[] = { | 247 | static const struct regulator_linear_range axp806_dcdca_ranges[] = { |
248 | REGULATOR_LINEAR_RANGE(600000, 0x0, 0x2f, 20000), | 248 | REGULATOR_LINEAR_RANGE(600000, 0x0, 0x32, 10000), |
249 | REGULATOR_LINEAR_RANGE(1800000, 0x30, 0x38, 100000), | 249 | REGULATOR_LINEAR_RANGE(1120000, 0x33, 0x47, 20000), |
250 | }; | 250 | }; |
251 | 251 | ||
252 | static const struct regulator_linear_range axp809_dldo1_ranges[] = { | 252 | static const struct regulator_linear_range axp806_dcdcd_ranges[] = { |
253 | REGULATOR_LINEAR_RANGE(600000, 0x0, 0x2d, 20000), | ||
254 | REGULATOR_LINEAR_RANGE(1600000, 0x2e, 0x3f, 100000), | ||
255 | }; | ||
256 | |||
257 | static const struct regulator_linear_range axp806_cldo2_ranges[] = { | ||
253 | REGULATOR_LINEAR_RANGE(700000, 0x0, 0x1a, 100000), | 258 | REGULATOR_LINEAR_RANGE(700000, 0x0, 0x1a, 100000), |
254 | REGULATOR_LINEAR_RANGE(3400000, 0x1b, 0x1f, 200000), | 259 | REGULATOR_LINEAR_RANGE(3400000, 0x1b, 0x1f, 200000), |
255 | }; | 260 | }; |
256 | 261 | ||
262 | static const struct regulator_desc axp806_regulators[] = { | ||
263 | AXP_DESC_RANGES(AXP806, DCDCA, "dcdca", "vina", axp806_dcdca_ranges, | ||
264 | 72, AXP806_DCDCA_V_CTRL, 0x7f, AXP806_PWR_OUT_CTRL1, | ||
265 | BIT(0)), | ||
266 | AXP_DESC(AXP806, DCDCB, "dcdcb", "vinb", 1000, 2550, 50, | ||
267 | AXP806_DCDCB_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(1)), | ||
268 | AXP_DESC_RANGES(AXP806, DCDCC, "dcdcc", "vinc", axp806_dcdca_ranges, | ||
269 | 72, AXP806_DCDCC_V_CTRL, 0x7f, AXP806_PWR_OUT_CTRL1, | ||
270 | BIT(2)), | ||
271 | AXP_DESC_RANGES(AXP806, DCDCD, "dcdcd", "vind", axp806_dcdcd_ranges, | ||
272 | 64, AXP806_DCDCD_V_CTRL, 0x3f, AXP806_PWR_OUT_CTRL1, | ||
273 | BIT(3)), | ||
274 | AXP_DESC(AXP806, DCDCE, "dcdce", "vine", 1100, 3400, 100, | ||
275 | AXP806_DCDCB_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(4)), | ||
276 | AXP_DESC(AXP806, ALDO1, "aldo1", "aldoin", 700, 3300, 100, | ||
277 | AXP806_ALDO1_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(5)), | ||
278 | AXP_DESC(AXP806, ALDO2, "aldo2", "aldoin", 700, 3400, 100, | ||
279 | AXP806_ALDO2_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(6)), | ||
280 | AXP_DESC(AXP806, ALDO3, "aldo3", "aldoin", 700, 3300, 100, | ||
281 | AXP806_ALDO3_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(7)), | ||
282 | AXP_DESC(AXP806, BLDO1, "bldo1", "bldoin", 700, 1900, 100, | ||
283 | AXP806_BLDO1_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(0)), | ||
284 | AXP_DESC(AXP806, BLDO2, "bldo2", "bldoin", 700, 1900, 100, | ||
285 | AXP806_BLDO2_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(1)), | ||
286 | AXP_DESC(AXP806, BLDO3, "bldo3", "bldoin", 700, 1900, 100, | ||
287 | AXP806_BLDO3_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(2)), | ||
288 | AXP_DESC(AXP806, BLDO4, "bldo4", "bldoin", 700, 1900, 100, | ||
289 | AXP806_BLDO4_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(3)), | ||
290 | AXP_DESC(AXP806, CLDO1, "cldo1", "cldoin", 700, 3300, 100, | ||
291 | AXP806_CLDO1_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2, BIT(4)), | ||
292 | AXP_DESC_RANGES(AXP806, CLDO2, "cldo2", "cldoin", axp806_cldo2_ranges, | ||
293 | 32, AXP806_CLDO2_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2, | ||
294 | BIT(5)), | ||
295 | AXP_DESC(AXP806, CLDO3, "cldo3", "cldoin", 700, 3300, 100, | ||
296 | AXP806_CLDO3_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2, BIT(6)), | ||
297 | AXP_DESC_SW(AXP806, SW, "sw", "swin", AXP806_PWR_OUT_CTRL2, BIT(7)), | ||
298 | }; | ||
299 | |||
300 | static const struct regulator_linear_range axp809_dcdc4_ranges[] = { | ||
301 | REGULATOR_LINEAR_RANGE(600000, 0x0, 0x2f, 20000), | ||
302 | REGULATOR_LINEAR_RANGE(1800000, 0x30, 0x38, 100000), | ||
303 | }; | ||
304 | |||
257 | static const struct regulator_desc axp809_regulators[] = { | 305 | static const struct regulator_desc axp809_regulators[] = { |
258 | AXP_DESC(AXP809, DCDC1, "dcdc1", "vin1", 1600, 3400, 100, | 306 | AXP_DESC(AXP809, DCDC1, "dcdc1", "vin1", 1600, 3400, 100, |
259 | AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(1)), | 307 | AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(1)), |
@@ -278,7 +326,7 @@ static const struct regulator_desc axp809_regulators[] = { | |||
278 | AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(7)), | 326 | AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(7)), |
279 | AXP_DESC(AXP809, ALDO3, "aldo3", "aldoin", 700, 3300, 100, | 327 | AXP_DESC(AXP809, ALDO3, "aldo3", "aldoin", 700, 3300, 100, |
280 | AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)), | 328 | AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)), |
281 | AXP_DESC_RANGES(AXP809, DLDO1, "dldo1", "dldoin", axp809_dldo1_ranges, | 329 | AXP_DESC_RANGES(AXP809, DLDO1, "dldo1", "dldoin", axp806_cldo2_ranges, |
282 | 32, AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, | 330 | 32, AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, |
283 | BIT(3)), | 331 | BIT(3)), |
284 | AXP_DESC(AXP809, DLDO2, "dldo2", "dldoin", 700, 3300, 100, | 332 | AXP_DESC(AXP809, DLDO2, "dldo2", "dldoin", 700, 3300, 100, |
@@ -302,6 +350,7 @@ static const struct regulator_desc axp809_regulators[] = { | |||
302 | static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq) | 350 | static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq) |
303 | { | 351 | { |
304 | struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); | 352 | struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); |
353 | unsigned int reg = AXP20X_DCDC_FREQ; | ||
305 | u32 min, max, def, step; | 354 | u32 min, max, def, step; |
306 | 355 | ||
307 | switch (axp20x->variant) { | 356 | switch (axp20x->variant) { |
@@ -312,6 +361,14 @@ static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq) | |||
312 | def = 1500; | 361 | def = 1500; |
313 | step = 75; | 362 | step = 75; |
314 | break; | 363 | break; |
364 | case AXP806_ID: | ||
365 | /* | ||
366 | * AXP806 DCDC work frequency setting has the same range and | ||
367 | * step as AXP22X, but at a different register. | ||
368 | * Fall through to the check below. | ||
369 | * (See include/linux/mfd/axp20x.h) | ||
370 | */ | ||
371 | reg = AXP806_DCDC_FREQ_CTRL; | ||
315 | case AXP221_ID: | 372 | case AXP221_ID: |
316 | case AXP223_ID: | 373 | case AXP223_ID: |
317 | case AXP809_ID: | 374 | case AXP809_ID: |
@@ -343,7 +400,7 @@ static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq) | |||
343 | 400 | ||
344 | dcdcfreq = (dcdcfreq - min) / step; | 401 | dcdcfreq = (dcdcfreq - min) / step; |
345 | 402 | ||
346 | return regmap_update_bits(axp20x->regmap, AXP20X_DCDC_FREQ, | 403 | return regmap_update_bits(axp20x->regmap, reg, |
347 | AXP20X_FREQ_DCDC_MASK, dcdcfreq); | 404 | AXP20X_FREQ_DCDC_MASK, dcdcfreq); |
348 | } | 405 | } |
349 | 406 | ||
@@ -377,6 +434,7 @@ static int axp20x_regulator_parse_dt(struct platform_device *pdev) | |||
377 | static int axp20x_set_dcdc_workmode(struct regulator_dev *rdev, int id, u32 workmode) | 434 | static int axp20x_set_dcdc_workmode(struct regulator_dev *rdev, int id, u32 workmode) |
378 | { | 435 | { |
379 | struct axp20x_dev *axp20x = rdev_get_drvdata(rdev); | 436 | struct axp20x_dev *axp20x = rdev_get_drvdata(rdev); |
437 | unsigned int reg = AXP20X_DCDC_MODE; | ||
380 | unsigned int mask; | 438 | unsigned int mask; |
381 | 439 | ||
382 | switch (axp20x->variant) { | 440 | switch (axp20x->variant) { |
@@ -392,6 +450,13 @@ static int axp20x_set_dcdc_workmode(struct regulator_dev *rdev, int id, u32 work | |||
392 | workmode <<= ffs(mask) - 1; | 450 | workmode <<= ffs(mask) - 1; |
393 | break; | 451 | break; |
394 | 452 | ||
453 | case AXP806_ID: | ||
454 | reg = AXP806_DCDC_MODE_CTRL2; | ||
455 | /* | ||
456 | * AXP806 DCDC regulator IDs have the same range as AXP22X. | ||
457 | * Fall through to the check below. | ||
458 | * (See include/linux/mfd/axp20x.h) | ||
459 | */ | ||
395 | case AXP221_ID: | 460 | case AXP221_ID: |
396 | case AXP223_ID: | 461 | case AXP223_ID: |
397 | case AXP809_ID: | 462 | case AXP809_ID: |
@@ -408,7 +473,34 @@ static int axp20x_set_dcdc_workmode(struct regulator_dev *rdev, int id, u32 work | |||
408 | return -EINVAL; | 473 | return -EINVAL; |
409 | } | 474 | } |
410 | 475 | ||
411 | return regmap_update_bits(rdev->regmap, AXP20X_DCDC_MODE, mask, workmode); | 476 | return regmap_update_bits(rdev->regmap, reg, mask, workmode); |
477 | } | ||
478 | |||
479 | /* | ||
480 | * This function checks whether a regulator is part of a poly-phase | ||
481 | * output setup based on the registers settings. Returns true if it is. | ||
482 | */ | ||
483 | static bool axp20x_is_polyphase_slave(struct axp20x_dev *axp20x, int id) | ||
484 | { | ||
485 | u32 reg = 0; | ||
486 | |||
487 | /* Only AXP806 has poly-phase outputs */ | ||
488 | if (axp20x->variant != AXP806_ID) | ||
489 | return false; | ||
490 | |||
491 | regmap_read(axp20x->regmap, AXP806_DCDC_MODE_CTRL2, ®); | ||
492 | |||
493 | switch (id) { | ||
494 | case AXP806_DCDCB: | ||
495 | return (((reg & GENMASK(7, 6)) == BIT(6)) || | ||
496 | ((reg & GENMASK(7, 6)) == BIT(7))); | ||
497 | case AXP806_DCDCC: | ||
498 | return ((reg & GENMASK(7, 6)) == BIT(7)); | ||
499 | case AXP806_DCDCE: | ||
500 | return !!(reg & BIT(5)); | ||
501 | } | ||
502 | |||
503 | return false; | ||
412 | } | 504 | } |
413 | 505 | ||
414 | static int axp20x_regulator_probe(struct platform_device *pdev) | 506 | static int axp20x_regulator_probe(struct platform_device *pdev) |
@@ -440,6 +532,10 @@ static int axp20x_regulator_probe(struct platform_device *pdev) | |||
440 | drivevbus = of_property_read_bool(pdev->dev.parent->of_node, | 532 | drivevbus = of_property_read_bool(pdev->dev.parent->of_node, |
441 | "x-powers,drive-vbus-en"); | 533 | "x-powers,drive-vbus-en"); |
442 | break; | 534 | break; |
535 | case AXP806_ID: | ||
536 | regulators = axp806_regulators; | ||
537 | nregulators = AXP806_REG_ID_MAX; | ||
538 | break; | ||
443 | case AXP809_ID: | 539 | case AXP809_ID: |
444 | regulators = axp809_regulators; | 540 | regulators = axp809_regulators; |
445 | nregulators = AXP809_REG_ID_MAX; | 541 | nregulators = AXP809_REG_ID_MAX; |
@@ -458,6 +554,14 @@ static int axp20x_regulator_probe(struct platform_device *pdev) | |||
458 | struct regulator_desc *new_desc; | 554 | struct regulator_desc *new_desc; |
459 | 555 | ||
460 | /* | 556 | /* |
557 | * If this regulator is a slave in a poly-phase setup, | ||
558 | * skip it, as its controls are bound to the master | ||
559 | * regulator and won't work. | ||
560 | */ | ||
561 | if (axp20x_is_polyphase_slave(axp20x, i)) | ||
562 | continue; | ||
563 | |||
564 | /* | ||
461 | * Regulators DC1SW and DC5LDO are connected internally, | 565 | * Regulators DC1SW and DC5LDO are connected internally, |
462 | * so we have to handle their supply names separately. | 566 | * so we have to handle their supply names separately. |
463 | * | 567 | * |