diff options
Diffstat (limited to 'drivers/regulator/pwm-regulator.c')
-rw-r--r-- | drivers/regulator/pwm-regulator.c | 162 |
1 files changed, 118 insertions, 44 deletions
diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c index 666bc3bb52ef..c24524242da2 100644 --- a/drivers/regulator/pwm-regulator.c +++ b/drivers/regulator/pwm-regulator.c @@ -22,6 +22,12 @@ #include <linux/pwm.h> #include <linux/gpio/consumer.h> +struct pwm_continuous_reg_data { + unsigned int min_uV_dutycycle; + unsigned int max_uV_dutycycle; + unsigned int dutycycle_unit; +}; + struct pwm_regulator_data { /* Shared */ struct pwm_device *pwm; @@ -29,6 +35,9 @@ struct pwm_regulator_data { /* Voltage table */ struct pwm_voltages *duty_cycle_table; + /* Continuous mode info */ + struct pwm_continuous_reg_data continuous; + /* regulator descriptor */ struct regulator_desc desc; @@ -37,9 +46,6 @@ struct pwm_regulator_data { int state; - /* Continuous voltage */ - int volt_uV; - /* Enable GPIO */ struct gpio_desc *enb_gpio; }; @@ -52,10 +58,31 @@ struct pwm_voltages { /** * Voltage table call-backs */ +static void pwm_regulator_init_state(struct regulator_dev *rdev) +{ + struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); + struct pwm_state pwm_state; + unsigned int dutycycle; + int i; + + pwm_get_state(drvdata->pwm, &pwm_state); + dutycycle = pwm_get_relative_duty_cycle(&pwm_state, 100); + + for (i = 0; i < rdev->desc->n_voltages; i++) { + if (dutycycle == drvdata->duty_cycle_table[i].dutycycle) { + drvdata->state = i; + return; + } + } +} + static int pwm_regulator_get_voltage_sel(struct regulator_dev *rdev) { struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); + if (drvdata->state < 0) + pwm_regulator_init_state(rdev); + return drvdata->state; } @@ -63,16 +90,14 @@ static int pwm_regulator_set_voltage_sel(struct regulator_dev *rdev, unsigned selector) { struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); - struct pwm_args pargs; - int dutycycle; + struct pwm_state pstate; int ret; - pwm_get_args(drvdata->pwm, &pargs); - - dutycycle = (pargs.period * - drvdata->duty_cycle_table[selector].dutycycle) / 100; + pwm_init_state(drvdata->pwm, &pstate); + pwm_set_relative_duty_cycle(&pstate, + drvdata->duty_cycle_table[selector].dutycycle, 100); - ret = pwm_config(drvdata->pwm, dutycycle, pargs.period); + ret = pwm_apply_state(drvdata->pwm, &pstate); if (ret) { dev_err(&rdev->dev, "Failed to configure PWM: %d\n", ret); return ret; @@ -129,57 +154,90 @@ static int pwm_regulator_is_enabled(struct regulator_dev *dev) static int pwm_regulator_get_voltage(struct regulator_dev *rdev) { struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); + unsigned int min_uV_duty = drvdata->continuous.min_uV_dutycycle; + unsigned int max_uV_duty = drvdata->continuous.max_uV_dutycycle; + unsigned int duty_unit = drvdata->continuous.dutycycle_unit; + int min_uV = rdev->constraints->min_uV; + int max_uV = rdev->constraints->max_uV; + int diff_uV = max_uV - min_uV; + struct pwm_state pstate; + unsigned int diff_duty; + unsigned int voltage; + + pwm_get_state(drvdata->pwm, &pstate); + + voltage = pwm_get_relative_duty_cycle(&pstate, duty_unit); + + /* + * The dutycycle for min_uV might be greater than the one for max_uV. + * This is happening when the user needs an inversed polarity, but the + * PWM device does not support inversing it in hardware. + */ + if (max_uV_duty < min_uV_duty) { + voltage = min_uV_duty - voltage; + diff_duty = min_uV_duty - max_uV_duty; + } else { + voltage = voltage - min_uV_duty; + diff_duty = max_uV_duty - min_uV_duty; + } - return drvdata->volt_uV; + voltage = DIV_ROUND_CLOSEST_ULL((u64)voltage * diff_uV, diff_duty); + + return voltage + min_uV; } static int pwm_regulator_set_voltage(struct regulator_dev *rdev, - int min_uV, int max_uV, - unsigned *selector) + int req_min_uV, int req_max_uV, + unsigned int *selector) { struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); + unsigned int min_uV_duty = drvdata->continuous.min_uV_dutycycle; + unsigned int max_uV_duty = drvdata->continuous.max_uV_dutycycle; + unsigned int duty_unit = drvdata->continuous.dutycycle_unit; unsigned int ramp_delay = rdev->constraints->ramp_delay; - struct pwm_args pargs; - unsigned int req_diff = min_uV - rdev->constraints->min_uV; - unsigned int diff; - unsigned int duty_pulse; - u64 req_period; - u32 rem; + int min_uV = rdev->constraints->min_uV; + int max_uV = rdev->constraints->max_uV; + int diff_uV = max_uV - min_uV; + struct pwm_state pstate; int old_uV = pwm_regulator_get_voltage(rdev); + unsigned int diff_duty; + unsigned int dutycycle; int ret; - pwm_get_args(drvdata->pwm, &pargs); - diff = rdev->constraints->max_uV - rdev->constraints->min_uV; + pwm_init_state(drvdata->pwm, &pstate); - /* First try to find out if we get the iduty cycle time which is - * factor of PWM period time. If (request_diff_to_min * pwm_period) - * is perfect divided by voltage_range_diff then it is possible to - * get duty cycle time which is factor of PWM period. This will help - * to get output voltage nearer to requested value as there is no - * calculation loss. + /* + * The dutycycle for min_uV might be greater than the one for max_uV. + * This is happening when the user needs an inversed polarity, but the + * PWM device does not support inversing it in hardware. */ - req_period = req_diff * pargs.period; - div_u64_rem(req_period, diff, &rem); - if (!rem) { - do_div(req_period, diff); - duty_pulse = (unsigned int)req_period; - } else { - duty_pulse = (pargs.period / 100) * ((req_diff * 100) / diff); - } + if (max_uV_duty < min_uV_duty) + diff_duty = min_uV_duty - max_uV_duty; + else + diff_duty = max_uV_duty - min_uV_duty; + + dutycycle = DIV_ROUND_CLOSEST_ULL((u64)(req_min_uV - min_uV) * + diff_duty, + diff_uV); + + if (max_uV_duty < min_uV_duty) + dutycycle = min_uV_duty - dutycycle; + else + dutycycle = min_uV_duty + dutycycle; + + pwm_set_relative_duty_cycle(&pstate, dutycycle, duty_unit); - ret = pwm_config(drvdata->pwm, duty_pulse, pargs.period); + ret = pwm_apply_state(drvdata->pwm, &pstate); if (ret) { dev_err(&rdev->dev, "Failed to configure PWM: %d\n", ret); return ret; } - drvdata->volt_uV = min_uV; - if ((ramp_delay == 0) || !pwm_regulator_is_enabled(rdev)) return 0; /* Ramp delay is in uV/uS. Adjust to uS and delay */ - ramp_delay = DIV_ROUND_UP(abs(min_uV - old_uV), ramp_delay); + ramp_delay = DIV_ROUND_UP(abs(req_min_uV - old_uV), ramp_delay); usleep_range(ramp_delay, ramp_delay + DIV_ROUND_UP(ramp_delay, 10)); return 0; @@ -239,6 +297,7 @@ static int pwm_regulator_init_table(struct platform_device *pdev, return ret; } + drvdata->state = -EINVAL; drvdata->duty_cycle_table = duty_cycle_table; memcpy(&drvdata->ops, &pwm_regulator_voltage_table_ops, sizeof(drvdata->ops)); @@ -251,11 +310,28 @@ static int pwm_regulator_init_table(struct platform_device *pdev, static int pwm_regulator_init_continuous(struct platform_device *pdev, struct pwm_regulator_data *drvdata) { + u32 dutycycle_range[2] = { 0, 100 }; + u32 dutycycle_unit = 100; + memcpy(&drvdata->ops, &pwm_regulator_voltage_continuous_ops, sizeof(drvdata->ops)); drvdata->desc.ops = &drvdata->ops; drvdata->desc.continuous_voltage_range = true; + of_property_read_u32_array(pdev->dev.of_node, + "pwm-dutycycle-range", + dutycycle_range, 2); + of_property_read_u32(pdev->dev.of_node, "pwm-dutycycle-unit", + &dutycycle_unit); + + if (dutycycle_range[0] > dutycycle_unit || + dutycycle_range[1] > dutycycle_unit) + return -EINVAL; + + drvdata->continuous.dutycycle_unit = dutycycle_unit; + drvdata->continuous.min_uV_dutycycle = dutycycle_range[0]; + drvdata->continuous.max_uV_dutycycle = dutycycle_range[1]; + return 0; } @@ -316,11 +392,9 @@ static int pwm_regulator_probe(struct platform_device *pdev) return ret; } - /* - * FIXME: pwm_apply_args() should be removed when switching to the - * atomic PWM API. - */ - pwm_apply_args(drvdata->pwm); + ret = pwm_adjust_config(drvdata->pwm); + if (ret) + return ret; regulator = devm_regulator_register(&pdev->dev, &drvdata->desc, &config); |