Skip to content

Commit

Permalink
regulator: s5m8767a: Support AP watchdog reset operation
Browse files Browse the repository at this point in the history
The S5M8767A can't know status of ap reset.
So, After AP watchdog reset, AP can't boot normally.

Problem can be happened like below condition.
- AP Bootable lowest voltage(vdd_arm): 0.9v
- AP DVFS voltage table: 0.8v, 0.9v, 1.0v
- During AP works on lowest voltage(0.8V), watchdog reset is asserted
- AP can't boot, because vdd arm is still 0.8v

Solution
- Basic concept:
  After ap watchdog reset, GPIO configuration is changed by default value
- S5M8767A has function of voltage control with gpio (8 levels with 3 gpios)
- Set bootable voltage on level 0 -> can work with default gpio configuration
- In the probing, Change voltage control level from level 0 to level 1
- Execute normal dvfs operation on level 1
- After watchdog reset, ap gpio is set by default value
- PMIC operation mode is changed by ap reset (level1 -> level0)
- Regardless of previous vdd_arm voltage, AP always can be booted.

Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
  • Loading branch information
Sangbeom Kim authored and Mark Brown committed Jun 19, 2012
1 parent 852abad commit c848bc8
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 44 deletions.
137 changes: 93 additions & 44 deletions drivers/regulator/s5m8767.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ struct s5m8767_info {
u8 buck3_vol[8];
u8 buck4_vol[8];
int buck_gpios[3];
int buck_ds[3];
int buck_gpioindex;
};

Expand Down Expand Up @@ -283,17 +284,17 @@ static int s5m8767_get_voltage_register(struct regulator_dev *rdev, int *_reg)
reg = S5M8767_REG_BUCK1CTRL2;
break;
case S5M8767_BUCK2:
reg = S5M8767_REG_BUCK2DVS1;
reg = S5M8767_REG_BUCK2DVS2;
if (s5m8767->buck2_gpiodvs)
reg += s5m8767->buck_gpioindex;
break;
case S5M8767_BUCK3:
reg = S5M8767_REG_BUCK3DVS1;
reg = S5M8767_REG_BUCK3DVS2;
if (s5m8767->buck3_gpiodvs)
reg += s5m8767->buck_gpioindex;
break;
case S5M8767_BUCK4:
reg = S5M8767_REG_BUCK4DVS1;
reg = S5M8767_REG_BUCK4DVS2;
if (s5m8767->buck4_gpiodvs)
reg += s5m8767->buck_gpioindex;
break;
Expand Down Expand Up @@ -512,7 +513,7 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
struct regulator_config config = { };
struct regulator_dev **rdev;
struct s5m8767_info *s5m8767;
int i, ret, size;
int i, ret, size, buck_init;

if (!pdata) {
dev_err(pdev->dev.parent, "Platform data not supplied\n");
Expand Down Expand Up @@ -563,12 +564,37 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
s5m8767->buck_gpios[0] = pdata->buck_gpios[0];
s5m8767->buck_gpios[1] = pdata->buck_gpios[1];
s5m8767->buck_gpios[2] = pdata->buck_gpios[2];
s5m8767->buck_ds[0] = pdata->buck_ds[0];
s5m8767->buck_ds[1] = pdata->buck_ds[1];
s5m8767->buck_ds[2] = pdata->buck_ds[2];

s5m8767->ramp_delay = pdata->buck_ramp_delay;
s5m8767->buck2_ramp = pdata->buck2_ramp_enable;
s5m8767->buck3_ramp = pdata->buck3_ramp_enable;
s5m8767->buck4_ramp = pdata->buck4_ramp_enable;
s5m8767->opmode = pdata->opmode;

buck_init = s5m8767_convert_voltage_to_sel(&buck_voltage_val2,
pdata->buck2_init,
pdata->buck2_init +
buck_voltage_val2.step);

s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK2DVS2, buck_init);

buck_init = s5m8767_convert_voltage_to_sel(&buck_voltage_val2,
pdata->buck3_init,
pdata->buck3_init +
buck_voltage_val2.step);

s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK3DVS2, buck_init);

buck_init = s5m8767_convert_voltage_to_sel(&buck_voltage_val2,
pdata->buck4_init,
pdata->buck4_init +
buck_voltage_val2.step);

s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK4DVS2, buck_init);

for (i = 0; i < 8; i++) {
if (s5m8767->buck2_gpiodvs) {
s5m8767->buck2_vol[i] =
Expand Down Expand Up @@ -598,48 +624,71 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
}
}

if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs ||
pdata->buck4_gpiodvs) {
if (gpio_is_valid(pdata->buck_gpios[0]) &&
gpio_is_valid(pdata->buck_gpios[1]) &&
gpio_is_valid(pdata->buck_gpios[2])) {
ret = gpio_request(pdata->buck_gpios[0],
"S5M8767 SET1");
if (ret == -EBUSY)
dev_warn(&pdev->dev, "Duplicated gpio request for SET1\n");

ret = gpio_request(pdata->buck_gpios[1],
"S5M8767 SET2");
if (ret == -EBUSY)
dev_warn(&pdev->dev, "Duplicated gpio request for SET2\n");

ret = gpio_request(pdata->buck_gpios[2],
"S5M8767 SET3");
if (ret == -EBUSY)
dev_warn(&pdev->dev, "Duplicated gpio request for SET3\n");
/* SET1 GPIO */
gpio_direction_output(pdata->buck_gpios[0],
(s5m8767->buck_gpioindex >> 2) & 0x1);
/* SET2 GPIO */
gpio_direction_output(pdata->buck_gpios[1],
(s5m8767->buck_gpioindex >> 1) & 0x1);
/* SET3 GPIO */
gpio_direction_output(pdata->buck_gpios[2],
(s5m8767->buck_gpioindex >> 0) & 0x1);
ret = 0;
} else {
dev_err(&pdev->dev, "GPIO NOT VALID\n");
ret = -EINVAL;
return ret;
}
if (gpio_is_valid(pdata->buck_gpios[0]) &&
gpio_is_valid(pdata->buck_gpios[1]) &&
gpio_is_valid(pdata->buck_gpios[2])) {
ret = gpio_request(pdata->buck_gpios[0], "S5M8767 SET1");
if (ret == -EBUSY)
dev_warn(&pdev->dev, "Duplicated gpio request"
" for SET1\n");

ret = gpio_request(pdata->buck_gpios[1], "S5M8767 SET2");
if (ret == -EBUSY)
dev_warn(&pdev->dev, "Duplicated gpio request"
" for SET2\n");

ret = gpio_request(pdata->buck_gpios[2], "S5M8767 SET3");
if (ret == -EBUSY)
dev_warn(&pdev->dev, "Duplicated gpio request"
" for SET3\n");
/* SET1 GPIO */
gpio_direction_output(pdata->buck_gpios[0],
(s5m8767->buck_gpioindex >> 2) & 0x1);
/* SET2 GPIO */
gpio_direction_output(pdata->buck_gpios[1],
(s5m8767->buck_gpioindex >> 1) & 0x1);
/* SET3 GPIO */
gpio_direction_output(pdata->buck_gpios[2],
(s5m8767->buck_gpioindex >> 0) & 0x1);
ret = 0;

} else {
dev_err(&pdev->dev, "GPIO NOT VALID\n");
ret = -EINVAL;
return ret;
}

s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK2CTRL,
(pdata->buck2_gpiodvs) ? (1 << 1) : (0 << 1), 1 << 1);
s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK3CTRL,
(pdata->buck3_gpiodvs) ? (1 << 1) : (0 << 1), 1 << 1);
s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK4CTRL,
(pdata->buck4_gpiodvs) ? (1 << 1) : (0 << 1), 1 << 1);
ret = gpio_request(pdata->buck_ds[0], "S5M8767 DS2");
if (ret == -EBUSY)
dev_warn(&pdev->dev, "Duplicated gpio request for DS2\n");

ret = gpio_request(pdata->buck_ds[1], "S5M8767 DS3");
if (ret == -EBUSY)
dev_warn(&pdev->dev, "Duplicated gpio request for DS3\n");

ret = gpio_request(pdata->buck_ds[2], "S5M8767 DS4");
if (ret == -EBUSY)
dev_warn(&pdev->dev, "Duplicated gpio request for DS4\n");

/* DS2 GPIO */
gpio_direction_output(pdata->buck_ds[0], 0x0);
/* DS3 GPIO */
gpio_direction_output(pdata->buck_ds[1], 0x0);
/* DS4 GPIO */
gpio_direction_output(pdata->buck_ds[2], 0x0);

if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs ||
pdata->buck4_gpiodvs) {
s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK2CTRL,
(pdata->buck2_gpiodvs) ? (1 << 1) : (0 << 1),
1 << 1);
s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK3CTRL,
(pdata->buck3_gpiodvs) ? (1 << 1) : (0 << 1),
1 << 1);
s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK4CTRL,
(pdata->buck4_gpiodvs) ? (1 << 1) : (0 << 1),
1 << 1);
}

/* Initialize GPIO DVS registers */
for (i = 0; i < 8; i++) {
Expand Down
5 changes: 5 additions & 0 deletions include/linux/mfd/s5m87xx/s5m-core.h
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ struct s5m_platform_data {
bool buck_voltage_lock;

int buck_gpios[3];
int buck_ds[3];
int buck2_voltage[8];
bool buck2_gpiodvs;
int buck3_voltage[8];
Expand All @@ -369,6 +370,10 @@ struct s5m_platform_data {
bool buck2_ramp_enable;
bool buck3_ramp_enable;
bool buck4_ramp_enable;

int buck2_init;
int buck3_init;
int buck4_init;
};

#endif /* __LINUX_MFD_S5M_CORE_H */

0 comments on commit c848bc8

Please sign in to comment.