| From 0311770c0aaf6953290a9189fc90d719dd8f1cae Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 3 Sep 2020 13:23:24 +0200 |
| Subject: pwm: lpss: Add range limit check for the base_unit register value |
| |
| From: Hans de Goede <hdegoede@redhat.com> |
| |
| [ Upstream commit ef9f60daab309558c8bb3e086a9a11ee40bd6061 ] |
| |
| When the user requests a high enough period ns value, then the |
| calculations in pwm_lpss_prepare() might result in a base_unit value of 0. |
| |
| But according to the data-sheet the way the PWM controller works is that |
| each input clock-cycle the base_unit gets added to a N bit counter and |
| that counter overflowing determines the PWM output frequency. Adding 0 |
| to the counter is a no-op. The data-sheet even explicitly states that |
| writing 0 to the base_unit bits will result in the PWM outputting a |
| continuous 0 signal. |
| |
| When the user requestes a low enough period ns value, then the |
| calculations in pwm_lpss_prepare() might result in a base_unit value |
| which is bigger then base_unit_range - 1. Currently the codes for this |
| deals with this by applying a mask: |
| |
| base_unit &= (base_unit_range - 1); |
| |
| But this means that we let the value overflow the range, we throw away the |
| higher bits and store whatever value is left in the lower bits into the |
| register leading to a random output frequency, rather then clamping the |
| output frequency to the highest frequency which the hardware can do. |
| |
| This commit fixes both issues by clamping the base_unit value to be |
| between 1 and (base_unit_range - 1). |
| |
| Fixes: 684309e5043e ("pwm: lpss: Avoid potential overflow of base_unit") |
| Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> |
| Acked-by: Thierry Reding <thierry.reding@gmail.com> |
| Signed-off-by: Hans de Goede <hdegoede@redhat.com> |
| Link: https://patchwork.freedesktop.org/patch/msgid/20200903112337.4113-5-hdegoede@redhat.com |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/pwm/pwm-lpss.c | 3 ++- |
| 1 file changed, 2 insertions(+), 1 deletion(-) |
| |
| diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c |
| index da63c029aa286..69f8be065919e 100644 |
| --- a/drivers/pwm/pwm-lpss.c |
| +++ b/drivers/pwm/pwm-lpss.c |
| @@ -109,6 +109,8 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm, |
| freq *= base_unit_range; |
| |
| base_unit = DIV_ROUND_CLOSEST_ULL(freq, c); |
| + /* base_unit must not be 0 and we also want to avoid overflowing it */ |
| + base_unit = clamp_val(base_unit, 1, base_unit_range - 1); |
| |
| on_time_div = 255ULL * duty_ns; |
| do_div(on_time_div, period_ns); |
| @@ -117,7 +119,6 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm, |
| orig_ctrl = ctrl = pwm_lpss_read(pwm); |
| ctrl &= ~PWM_ON_TIME_DIV_MASK; |
| ctrl &= ~((base_unit_range - 1) << PWM_BASE_UNIT_SHIFT); |
| - base_unit &= (base_unit_range - 1); |
| ctrl |= (u32) base_unit << PWM_BASE_UNIT_SHIFT; |
| ctrl |= on_time_div; |
| |
| -- |
| 2.25.1 |
| |