aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/regulator
diff options
context:
space:
mode:
authorMatt Sealey <matt@genesi-usa.com>2013-01-21 12:38:40 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2013-01-26 22:22:21 -0500
commitccf3ed782a6e65e0355b36424e88d2d8b5f46e89 (patch)
tree1b65a8f734ef449fd5f95c7bfb397cf4796d73d9 /drivers/regulator
parent949db153b6466c6f7cad5a427ecea94985927311 (diff)
regulator: mc13892-regulator: correct/refine handling of the SWxHI bit
MC13892 PMIC supports a "HI" bit for 3 of it's 4 buck switcher outputs, which enables a higher set of voltage ranges. Despite a comment in the code ('sw regulators need special care due to the "hi" bit'), it actually does not take special care since it does not modify it's use of the selector table index when this bit is set, giving us very odd behavior when setting a high voltage on supported switchers or listing current voltages. Net effect is in best case the kernel and sysfs report lower voltages than are actually set in hardware (1300mV instead of 1800mV for example) and in the worst case setting a voltage (e.g. 1800mV) will cause an undervoltage condition (e.g. 1300mV). Correct the behavior, taking into account SW1 doesn't support the HI bit, and as such we need to ignore it. While we are modifying these functions, fix and optimize the following; * set_voltage_sel callback was using .reg instead of .vsel_reg - since they were set to the same value it actually didn't break anything but it would be semantically incorrect to use .reg in this case. We now use .vsel_reg and be consistent. * vsel_shift is always 0 for every SWx regulator, and constantly shifting and masking off the bottom few bits is time consuming and makes the code very hard to read - optimize this out. * get_voltage_sel uses the variable "val" and set_voltage_sel uses the variable "selector" (and reg_value). Introduce the variable "selector" to get_voltage_sel such that it makes more sense and allow some leaner code in light of the modifications in this patch. Add better exposure to the debug print so the register value AND the selector are printed as this will adequately show the HI bit in the register. * correct a comment in probe which is doing a version check. Magic values are awful but for once instance, a comment does just as good a job as something symbolic. Signed-off-by: Matt Sealey <matt@genesi-usa.com> Tested-by: Steev Klimaszewski <steev@genesi-usa.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'drivers/regulator')
-rw-r--r--drivers/regulator/mc13892-regulator.c72
1 files changed, 56 insertions, 16 deletions
diff --git a/drivers/regulator/mc13892-regulator.c b/drivers/regulator/mc13892-regulator.c
index 0d84b1f33199..5dd8aaa5cfb8 100644
--- a/drivers/regulator/mc13892-regulator.c
+++ b/drivers/regulator/mc13892-regulator.c
@@ -164,6 +164,14 @@ static const unsigned int mc13892_sw1[] = {
164 1350000, 1375000 164 1350000, 1375000
165}; 165};
166 166
167/*
168 * Note: this table is used to derive SWxVSEL by index into
169 * the array. Offset the values by the index of 1100000uV
170 * to get the actual register value for that voltage selector
171 * if the HI bit is to be set as well.
172 */
173#define MC13892_SWxHI_SEL_OFFSET 20
174
167static const unsigned int mc13892_sw[] = { 175static const unsigned int mc13892_sw[] = {
168 600000, 625000, 650000, 675000, 700000, 725000, 176 600000, 625000, 650000, 675000, 700000, 725000,
169 750000, 775000, 800000, 825000, 850000, 875000, 177 750000, 775000, 800000, 825000, 850000, 875000,
@@ -239,7 +247,6 @@ static const unsigned int mc13892_pwgtdrv[] = {
239}; 247};
240 248
241static struct regulator_ops mc13892_gpo_regulator_ops; 249static struct regulator_ops mc13892_gpo_regulator_ops;
242/* sw regulators need special care due to the "hi bit" */
243static struct regulator_ops mc13892_sw_regulator_ops; 250static struct regulator_ops mc13892_sw_regulator_ops;
244 251
245 252
@@ -396,7 +403,7 @@ static int mc13892_sw_regulator_get_voltage_sel(struct regulator_dev *rdev)
396{ 403{
397 struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev); 404 struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
398 int ret, id = rdev_get_id(rdev); 405 int ret, id = rdev_get_id(rdev);
399 unsigned int val; 406 unsigned int val, selector;
400 407
401 dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id); 408 dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
402 409
@@ -407,12 +414,28 @@ static int mc13892_sw_regulator_get_voltage_sel(struct regulator_dev *rdev)
407 if (ret) 414 if (ret)
408 return ret; 415 return ret;
409 416
410 val = (val & mc13892_regulators[id].vsel_mask) 417 /*
411 >> mc13892_regulators[id].vsel_shift; 418 * Figure out if the HI bit is set inside the switcher mode register
419 * since this means the selector value we return is at a different
420 * offset into the selector table.
421 *
422 * According to the MC13892 documentation note 59 (Table 47) the SW1
423 * buck switcher does not support output range programming therefore
424 * the HI bit must always remain 0. So do not do anything strange if
425 * our register is MC13892_SWITCHERS0.
426 */
427
428 selector = val & mc13892_regulators[id].vsel_mask;
429
430 if ((mc13892_regulators[id].vsel_reg != MC13892_SWITCHERS0) &&
431 (val & MC13892_SWITCHERS0_SWxHI)) {
432 selector += MC13892_SWxHI_SEL_OFFSET;
433 }
412 434
413 dev_dbg(rdev_get_dev(rdev), "%s id: %d val: %d\n", __func__, id, val); 435 dev_dbg(rdev_get_dev(rdev), "%s id: %d val: 0x%08x selector: %d\n",
436 __func__, id, val, selector);
414 437
415 return val; 438 return selector;
416} 439}
417 440
418static int mc13892_sw_regulator_set_voltage_sel(struct regulator_dev *rdev, 441static int mc13892_sw_regulator_set_voltage_sel(struct regulator_dev *rdev,
@@ -425,18 +448,35 @@ static int mc13892_sw_regulator_set_voltage_sel(struct regulator_dev *rdev,
425 448
426 volt = rdev->desc->volt_table[selector]; 449 volt = rdev->desc->volt_table[selector];
427 mask = mc13892_regulators[id].vsel_mask; 450 mask = mc13892_regulators[id].vsel_mask;
428 reg_value = selector << mc13892_regulators[id].vsel_shift; 451 reg_value = selector;
429 452
430 if (volt > 1375000) { 453 /*
431 mask |= MC13892_SWITCHERS0_SWxHI; 454 * Don't mess with the HI bit or support HI voltage offsets for SW1.
432 reg_value |= MC13892_SWITCHERS0_SWxHI; 455 *
433 } else if (volt < 1100000) { 456 * Since the get_voltage_sel callback has given a fudged value for
434 mask |= MC13892_SWITCHERS0_SWxHI; 457 * the selector offset, we need to back out that offset if HI is
435 reg_value &= ~MC13892_SWITCHERS0_SWxHI; 458 * to be set so we write the correct value to the register.
459 *
460 * The HI bit addition and selector offset handling COULD be more
461 * complicated by shifting and masking off the voltage selector part
462 * of the register then logical OR it back in, but since the selector
463 * is at bits 4:0 there is very little point. This makes the whole
464 * thing more readable and we do far less work.
465 */
466
467 if (mc13892_regulators[id].vsel_reg != MC13892_SWITCHERS0) {
468 if (volt > 1375000) {
469 reg_value -= MC13892_SWxHI_SEL_OFFSET;
470 reg_value |= MC13892_SWITCHERS0_SWxHI;
471 mask |= MC13892_SWITCHERS0_SWxHI;
472 } else if (volt < 1100000) {
473 reg_value &= ~MC13892_SWITCHERS0_SWxHI;
474 mask |= MC13892_SWITCHERS0_SWxHI;
475 }
436 } 476 }
437 477
438 mc13xxx_lock(priv->mc13xxx); 478 mc13xxx_lock(priv->mc13xxx);
439 ret = mc13xxx_reg_rmw(priv->mc13xxx, mc13892_regulators[id].reg, mask, 479 ret = mc13xxx_reg_rmw(priv->mc13xxx, mc13892_regulators[id].vsel_reg, mask,
440 reg_value); 480 reg_value);
441 mc13xxx_unlock(priv->mc13xxx); 481 mc13xxx_unlock(priv->mc13xxx);
442 482
@@ -520,7 +560,7 @@ static int mc13892_regulator_probe(struct platform_device *pdev)
520 if (ret) 560 if (ret)
521 goto err_unlock; 561 goto err_unlock;
522 562
523 /* enable switch auto mode */ 563 /* enable switch auto mode (on 2.0A silicon only) */
524 if ((val & 0x0000FFFF) == 0x45d0) { 564 if ((val & 0x0000FFFF) == 0x45d0) {
525 ret = mc13xxx_reg_rmw(mc13892, MC13892_SWITCHERS4, 565 ret = mc13xxx_reg_rmw(mc13892, MC13892_SWITCHERS4,
526 MC13892_SWITCHERS4_SW1MODE_M | 566 MC13892_SWITCHERS4_SW1MODE_M |