.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Intel Low Power Subsystem PWM controller driver |
---|
3 | 4 | * |
---|
.. | .. |
---|
7 | 8 | * Author: Chang Rebecca Swee Fun <rebecca.swee.fun.chang@intel.com> |
---|
8 | 9 | * Author: Chew Chiau Ee <chiau.ee.chew@intel.com> |
---|
9 | 10 | * Author: Alan Cox <alan@linux.intel.com> |
---|
10 | | - * |
---|
11 | | - * This program is free software; you can redistribute it and/or modify |
---|
12 | | - * it under the terms of the GNU General Public License version 2 as |
---|
13 | | - * published by the Free Software Foundation. |
---|
14 | 11 | */ |
---|
15 | 12 | |
---|
16 | 13 | #include <linux/delay.h> |
---|
.. | .. |
---|
31 | 28 | |
---|
32 | 29 | /* Size of each PWM register space if multiple */ |
---|
33 | 30 | #define PWM_SIZE 0x400 |
---|
34 | | - |
---|
35 | | -#define MAX_PWMS 4 |
---|
36 | | - |
---|
37 | | -struct pwm_lpss_chip { |
---|
38 | | - struct pwm_chip chip; |
---|
39 | | - void __iomem *regs; |
---|
40 | | - const struct pwm_lpss_boardinfo *info; |
---|
41 | | - u32 saved_ctrl[MAX_PWMS]; |
---|
42 | | -}; |
---|
43 | 31 | |
---|
44 | 32 | static inline struct pwm_lpss_chip *to_lpwm(struct pwm_chip *chip) |
---|
45 | 33 | { |
---|
.. | .. |
---|
97 | 85 | unsigned long long on_time_div; |
---|
98 | 86 | unsigned long c = lpwm->info->clk_rate, base_unit_range; |
---|
99 | 87 | unsigned long long base_unit, freq = NSEC_PER_SEC; |
---|
100 | | - u32 orig_ctrl, ctrl; |
---|
| 88 | + u32 ctrl; |
---|
101 | 89 | |
---|
102 | 90 | do_div(freq, period_ns); |
---|
103 | 91 | |
---|
.. | .. |
---|
116 | 104 | do_div(on_time_div, period_ns); |
---|
117 | 105 | on_time_div = 255ULL - on_time_div; |
---|
118 | 106 | |
---|
119 | | - orig_ctrl = ctrl = pwm_lpss_read(pwm); |
---|
| 107 | + ctrl = pwm_lpss_read(pwm); |
---|
120 | 108 | ctrl &= ~PWM_ON_TIME_DIV_MASK; |
---|
121 | 109 | ctrl &= ~((base_unit_range - 1) << PWM_BASE_UNIT_SHIFT); |
---|
122 | 110 | ctrl |= (u32) base_unit << PWM_BASE_UNIT_SHIFT; |
---|
123 | 111 | ctrl |= on_time_div; |
---|
124 | 112 | |
---|
125 | | - if (orig_ctrl != ctrl) { |
---|
126 | | - pwm_lpss_write(pwm, ctrl); |
---|
127 | | - pwm_lpss_write(pwm, ctrl | PWM_SW_UPDATE); |
---|
128 | | - } |
---|
| 113 | + pwm_lpss_write(pwm, ctrl); |
---|
| 114 | + pwm_lpss_write(pwm, ctrl | PWM_SW_UPDATE); |
---|
129 | 115 | } |
---|
130 | 116 | |
---|
131 | 117 | static inline void pwm_lpss_cond_enable(struct pwm_device *pwm, bool cond) |
---|
.. | .. |
---|
134 | 120 | pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_ENABLE); |
---|
135 | 121 | } |
---|
136 | 122 | |
---|
| 123 | +static int pwm_lpss_prepare_enable(struct pwm_lpss_chip *lpwm, |
---|
| 124 | + struct pwm_device *pwm, |
---|
| 125 | + const struct pwm_state *state) |
---|
| 126 | +{ |
---|
| 127 | + int ret; |
---|
| 128 | + |
---|
| 129 | + ret = pwm_lpss_is_updating(pwm); |
---|
| 130 | + if (ret) |
---|
| 131 | + return ret; |
---|
| 132 | + |
---|
| 133 | + pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period); |
---|
| 134 | + pwm_lpss_cond_enable(pwm, lpwm->info->bypass == false); |
---|
| 135 | + ret = pwm_lpss_wait_for_update(pwm); |
---|
| 136 | + if (ret) |
---|
| 137 | + return ret; |
---|
| 138 | + |
---|
| 139 | + pwm_lpss_cond_enable(pwm, lpwm->info->bypass == true); |
---|
| 140 | + return 0; |
---|
| 141 | +} |
---|
| 142 | + |
---|
137 | 143 | static int pwm_lpss_apply(struct pwm_chip *chip, struct pwm_device *pwm, |
---|
138 | | - struct pwm_state *state) |
---|
| 144 | + const struct pwm_state *state) |
---|
139 | 145 | { |
---|
140 | 146 | struct pwm_lpss_chip *lpwm = to_lpwm(chip); |
---|
141 | | - int ret; |
---|
| 147 | + int ret = 0; |
---|
142 | 148 | |
---|
143 | 149 | if (state->enabled) { |
---|
144 | 150 | if (!pwm_is_enabled(pwm)) { |
---|
145 | 151 | pm_runtime_get_sync(chip->dev); |
---|
146 | | - ret = pwm_lpss_is_updating(pwm); |
---|
147 | | - if (ret) { |
---|
148 | | - pm_runtime_put(chip->dev); |
---|
149 | | - return ret; |
---|
150 | | - } |
---|
151 | | - pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period); |
---|
152 | | - pwm_lpss_cond_enable(pwm, lpwm->info->bypass == false); |
---|
153 | | - ret = pwm_lpss_wait_for_update(pwm); |
---|
154 | | - if (ret) { |
---|
155 | | - pm_runtime_put(chip->dev); |
---|
156 | | - return ret; |
---|
157 | | - } |
---|
158 | | - pwm_lpss_cond_enable(pwm, lpwm->info->bypass == true); |
---|
159 | | - } else { |
---|
160 | | - ret = pwm_lpss_is_updating(pwm); |
---|
| 152 | + ret = pwm_lpss_prepare_enable(lpwm, pwm, state); |
---|
161 | 153 | if (ret) |
---|
162 | | - return ret; |
---|
163 | | - pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period); |
---|
164 | | - return pwm_lpss_wait_for_update(pwm); |
---|
| 154 | + pm_runtime_put(chip->dev); |
---|
| 155 | + } else { |
---|
| 156 | + ret = pwm_lpss_prepare_enable(lpwm, pwm, state); |
---|
165 | 157 | } |
---|
166 | 158 | } else if (pwm_is_enabled(pwm)) { |
---|
167 | 159 | pwm_lpss_write(pwm, pwm_lpss_read(pwm) & ~PWM_ENABLE); |
---|
168 | 160 | pm_runtime_put(chip->dev); |
---|
169 | 161 | } |
---|
170 | 162 | |
---|
171 | | - return 0; |
---|
| 163 | + return ret; |
---|
| 164 | +} |
---|
| 165 | + |
---|
| 166 | +static void pwm_lpss_get_state(struct pwm_chip *chip, struct pwm_device *pwm, |
---|
| 167 | + struct pwm_state *state) |
---|
| 168 | +{ |
---|
| 169 | + struct pwm_lpss_chip *lpwm = to_lpwm(chip); |
---|
| 170 | + unsigned long base_unit_range; |
---|
| 171 | + unsigned long long base_unit, freq, on_time_div; |
---|
| 172 | + u32 ctrl; |
---|
| 173 | + |
---|
| 174 | + pm_runtime_get_sync(chip->dev); |
---|
| 175 | + |
---|
| 176 | + base_unit_range = BIT(lpwm->info->base_unit_bits); |
---|
| 177 | + |
---|
| 178 | + ctrl = pwm_lpss_read(pwm); |
---|
| 179 | + on_time_div = 255 - (ctrl & PWM_ON_TIME_DIV_MASK); |
---|
| 180 | + base_unit = (ctrl >> PWM_BASE_UNIT_SHIFT) & (base_unit_range - 1); |
---|
| 181 | + |
---|
| 182 | + freq = base_unit * lpwm->info->clk_rate; |
---|
| 183 | + do_div(freq, base_unit_range); |
---|
| 184 | + if (freq == 0) |
---|
| 185 | + state->period = NSEC_PER_SEC; |
---|
| 186 | + else |
---|
| 187 | + state->period = NSEC_PER_SEC / (unsigned long)freq; |
---|
| 188 | + |
---|
| 189 | + on_time_div *= state->period; |
---|
| 190 | + do_div(on_time_div, 255); |
---|
| 191 | + state->duty_cycle = on_time_div; |
---|
| 192 | + |
---|
| 193 | + state->polarity = PWM_POLARITY_NORMAL; |
---|
| 194 | + state->enabled = !!(ctrl & PWM_ENABLE); |
---|
| 195 | + |
---|
| 196 | + pm_runtime_put(chip->dev); |
---|
172 | 197 | } |
---|
173 | 198 | |
---|
174 | 199 | static const struct pwm_ops pwm_lpss_ops = { |
---|
175 | 200 | .apply = pwm_lpss_apply, |
---|
| 201 | + .get_state = pwm_lpss_get_state, |
---|
176 | 202 | .owner = THIS_MODULE, |
---|
177 | 203 | }; |
---|
178 | 204 | |
---|
.. | .. |
---|
181 | 207 | { |
---|
182 | 208 | struct pwm_lpss_chip *lpwm; |
---|
183 | 209 | unsigned long c; |
---|
184 | | - int ret; |
---|
| 210 | + int i, ret; |
---|
| 211 | + u32 ctrl; |
---|
185 | 212 | |
---|
186 | 213 | if (WARN_ON(info->npwm > MAX_PWMS)) |
---|
187 | 214 | return ERR_PTR(-ENODEV); |
---|
.. | .. |
---|
211 | 238 | return ERR_PTR(ret); |
---|
212 | 239 | } |
---|
213 | 240 | |
---|
| 241 | + for (i = 0; i < lpwm->info->npwm; i++) { |
---|
| 242 | + ctrl = pwm_lpss_read(&lpwm->chip.pwms[i]); |
---|
| 243 | + if (ctrl & PWM_ENABLE) |
---|
| 244 | + pm_runtime_get(dev); |
---|
| 245 | + } |
---|
| 246 | + |
---|
214 | 247 | return lpwm; |
---|
215 | 248 | } |
---|
216 | 249 | EXPORT_SYMBOL_GPL(pwm_lpss_probe); |
---|
.. | .. |
---|
226 | 259 | return pwmchip_remove(&lpwm->chip); |
---|
227 | 260 | } |
---|
228 | 261 | EXPORT_SYMBOL_GPL(pwm_lpss_remove); |
---|
229 | | - |
---|
230 | | -int pwm_lpss_suspend(struct device *dev) |
---|
231 | | -{ |
---|
232 | | - struct pwm_lpss_chip *lpwm = dev_get_drvdata(dev); |
---|
233 | | - int i; |
---|
234 | | - |
---|
235 | | - for (i = 0; i < lpwm->info->npwm; i++) |
---|
236 | | - lpwm->saved_ctrl[i] = readl(lpwm->regs + i * PWM_SIZE + PWM); |
---|
237 | | - |
---|
238 | | - return 0; |
---|
239 | | -} |
---|
240 | | -EXPORT_SYMBOL_GPL(pwm_lpss_suspend); |
---|
241 | | - |
---|
242 | | -int pwm_lpss_resume(struct device *dev) |
---|
243 | | -{ |
---|
244 | | - struct pwm_lpss_chip *lpwm = dev_get_drvdata(dev); |
---|
245 | | - int i; |
---|
246 | | - |
---|
247 | | - for (i = 0; i < lpwm->info->npwm; i++) |
---|
248 | | - writel(lpwm->saved_ctrl[i], lpwm->regs + i * PWM_SIZE + PWM); |
---|
249 | | - |
---|
250 | | - return 0; |
---|
251 | | -} |
---|
252 | | -EXPORT_SYMBOL_GPL(pwm_lpss_resume); |
---|
253 | 262 | |
---|
254 | 263 | MODULE_DESCRIPTION("PWM driver for Intel LPSS"); |
---|
255 | 264 | MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>"); |
---|