Skip to content

Commit 6c5126c

Browse files
ukleinekUwe Kleine-König
authored andcommitted
pwm: Provide new consumer API functions for waveforms
Provide API functions for consumers to work with waveforms. Note that one relevant difference between pwm_get_state() and pwm_get_waveform*() is that the latter yields the actually configured hardware state, while the former yields the last state passed to pwm_apply*() and so doesn't account for hardware specific rounding. Signed-off-by: Uwe Kleine-König <[email protected]> Tested-by: Trevor Gamblin <[email protected]> Link: https://lore.kernel.org/r/6c97d27682853f603e18e9196043886dd671845d.1726819463.git.u.kleine-koenig@baylibre.com Signed-off-by: Uwe Kleine-König <[email protected]>
1 parent 17e40c2 commit 6c5126c

File tree

2 files changed

+266
-1
lines changed

2 files changed

+266
-1
lines changed

drivers/pwm/core.c

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,30 @@ static void pwmchip_unlock(struct pwm_chip *chip)
4949

5050
DEFINE_GUARD(pwmchip, struct pwm_chip *, pwmchip_lock(_T), pwmchip_unlock(_T))
5151

52+
static bool pwm_wf_valid(const struct pwm_waveform *wf)
53+
{
54+
/*
55+
* For now restrict waveforms to period_length_ns <= S64_MAX to provide
56+
* some space for future extensions. One possibility is to simplify
57+
* representing waveforms with inverted polarity using negative values
58+
* somehow.
59+
*/
60+
if (wf->period_length_ns > S64_MAX)
61+
return false;
62+
63+
if (wf->duty_length_ns > wf->period_length_ns)
64+
return false;
65+
66+
/*
67+
* .duty_offset_ns is supposed to be smaller than .period_length_ns, apart
68+
* from the corner case .duty_offset_ns == 0 && .period_length_ns == 0.
69+
*/
70+
if (wf->duty_offset_ns && wf->duty_offset_ns >= wf->period_length_ns)
71+
return false;
72+
73+
return true;
74+
}
75+
5276
static void pwm_wf2state(const struct pwm_waveform *wf, struct pwm_state *state)
5377
{
5478
if (wf->period_length_ns) {
@@ -95,6 +119,29 @@ static void pwm_state2wf(const struct pwm_state *state, struct pwm_waveform *wf)
95119
}
96120
}
97121

122+
static int pwmwfcmp(const struct pwm_waveform *a, const struct pwm_waveform *b)
123+
{
124+
if (a->period_length_ns > b->period_length_ns)
125+
return 1;
126+
127+
if (a->period_length_ns < b->period_length_ns)
128+
return -1;
129+
130+
if (a->duty_length_ns > b->duty_length_ns)
131+
return 1;
132+
133+
if (a->duty_length_ns < b->duty_length_ns)
134+
return -1;
135+
136+
if (a->duty_offset_ns > b->duty_offset_ns)
137+
return 1;
138+
139+
if (a->duty_offset_ns < b->duty_offset_ns)
140+
return -1;
141+
142+
return 0;
143+
}
144+
98145
static bool pwm_check_rounding(const struct pwm_waveform *wf,
99146
const struct pwm_waveform *wf_rounded)
100147
{
@@ -145,6 +192,220 @@ static int __pwm_write_waveform(struct pwm_chip *chip, struct pwm_device *pwm, c
145192

146193
#define WFHWSIZE 20
147194

195+
/**
196+
* pwm_round_waveform_might_sleep - Query hardware capabilities
197+
* Cannot be used in atomic context.
198+
* @pwm: PWM device
199+
* @wf: waveform to round and output parameter
200+
*
201+
* Typically a given waveform cannot be implemented exactly by hardware, e.g.
202+
* because hardware only supports coarse period resolution or no duty_offset.
203+
* This function returns the actually implemented waveform if you pass wf to
204+
* pwm_set_waveform_might_sleep now.
205+
*
206+
* Note however that the world doesn't stop turning when you call it, so when
207+
* doing
208+
*
209+
* pwm_round_waveform_might_sleep(mypwm, &wf);
210+
* pwm_set_waveform_might_sleep(mypwm, &wf, true);
211+
*
212+
* the latter might fail, e.g. because an input clock changed its rate between
213+
* these two calls and the waveform determined by
214+
* pwm_round_waveform_might_sleep() cannot be implemented any more.
215+
*
216+
* Returns 0 on success, 1 if there is no valid hardware configuration matching
217+
* the input waveform under the PWM rounding rules or a negative errno.
218+
*/
219+
int pwm_round_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf)
220+
{
221+
struct pwm_chip *chip = pwm->chip;
222+
const struct pwm_ops *ops = chip->ops;
223+
struct pwm_waveform wf_req = *wf;
224+
char wfhw[WFHWSIZE];
225+
int ret_tohw, ret_fromhw;
226+
227+
BUG_ON(WFHWSIZE < ops->sizeof_wfhw);
228+
229+
if (!pwm_wf_valid(wf))
230+
return -EINVAL;
231+
232+
guard(pwmchip)(chip);
233+
234+
if (!chip->operational)
235+
return -ENODEV;
236+
237+
ret_tohw = __pwm_round_waveform_tohw(chip, pwm, wf, wfhw);
238+
if (ret_tohw < 0)
239+
return ret_tohw;
240+
241+
if (IS_ENABLED(CONFIG_PWM_DEBUG) && ret_tohw > 1)
242+
dev_err(&chip->dev, "Unexpected return value from __pwm_round_waveform_tohw: requested %llu/%llu [+%llu], return value %d\n",
243+
wf_req.duty_length_ns, wf_req.period_length_ns, wf_req.duty_offset_ns, ret_tohw);
244+
245+
ret_fromhw = __pwm_round_waveform_fromhw(chip, pwm, wfhw, wf);
246+
if (ret_fromhw < 0)
247+
return ret_fromhw;
248+
249+
if (IS_ENABLED(CONFIG_PWM_DEBUG) && ret_fromhw > 0)
250+
dev_err(&chip->dev, "Unexpected return value from __pwm_round_waveform_fromhw: requested %llu/%llu [+%llu], return value %d\n",
251+
wf_req.duty_length_ns, wf_req.period_length_ns, wf_req.duty_offset_ns, ret_tohw);
252+
253+
if (IS_ENABLED(CONFIG_PWM_DEBUG) &&
254+
ret_tohw == 0 && !pwm_check_rounding(&wf_req, wf))
255+
dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu]\n",
256+
wf_req.duty_length_ns, wf_req.period_length_ns, wf_req.duty_offset_ns,
257+
wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns);
258+
259+
return ret_tohw;
260+
}
261+
EXPORT_SYMBOL_GPL(pwm_round_waveform_might_sleep);
262+
263+
/**
264+
* pwm_get_waveform_might_sleep - Query hardware about current configuration
265+
* Cannot be used in atomic context.
266+
* @pwm: PWM device
267+
* @wf: output parameter
268+
*
269+
* Stores the current configuration of the PWM in @wf. Note this is the
270+
* equivalent of pwm_get_state_hw() (and not pwm_get_state()) for pwm_waveform.
271+
*/
272+
int pwm_get_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf)
273+
{
274+
struct pwm_chip *chip = pwm->chip;
275+
const struct pwm_ops *ops = chip->ops;
276+
char wfhw[WFHWSIZE];
277+
int err;
278+
279+
BUG_ON(WFHWSIZE < ops->sizeof_wfhw);
280+
281+
guard(pwmchip)(chip);
282+
283+
if (!chip->operational)
284+
return -ENODEV;
285+
286+
err = __pwm_read_waveform(chip, pwm, &wfhw);
287+
if (err)
288+
return err;
289+
290+
return __pwm_round_waveform_fromhw(chip, pwm, &wfhw, wf);
291+
}
292+
EXPORT_SYMBOL_GPL(pwm_get_waveform_might_sleep);
293+
294+
/* Called with the pwmchip lock held */
295+
static int __pwm_set_waveform(struct pwm_device *pwm,
296+
const struct pwm_waveform *wf,
297+
bool exact)
298+
{
299+
struct pwm_chip *chip = pwm->chip;
300+
const struct pwm_ops *ops = chip->ops;
301+
char wfhw[WFHWSIZE];
302+
struct pwm_waveform wf_rounded;
303+
int err;
304+
305+
BUG_ON(WFHWSIZE < ops->sizeof_wfhw);
306+
307+
if (!pwm_wf_valid(wf))
308+
return -EINVAL;
309+
310+
err = __pwm_round_waveform_tohw(chip, pwm, wf, &wfhw);
311+
if (err)
312+
return err;
313+
314+
if ((IS_ENABLED(CONFIG_PWM_DEBUG) || exact) && wf->period_length_ns) {
315+
err = __pwm_round_waveform_fromhw(chip, pwm, &wfhw, &wf_rounded);
316+
if (err)
317+
return err;
318+
319+
if (IS_ENABLED(CONFIG_PWM_DEBUG) && !pwm_check_rounding(wf, &wf_rounded))
320+
dev_err(&chip->dev, "Wrong rounding: requested %llu/%llu [+%llu], result %llu/%llu [+%llu]\n",
321+
wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns,
322+
wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns);
323+
324+
if (exact && pwmwfcmp(wf, &wf_rounded)) {
325+
dev_dbg(&chip->dev, "Requested no rounding, but %llu/%llu [+%llu] -> %llu/%llu [+%llu]\n",
326+
wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns,
327+
wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns);
328+
329+
return 1;
330+
}
331+
}
332+
333+
err = __pwm_write_waveform(chip, pwm, &wfhw);
334+
if (err)
335+
return err;
336+
337+
/* update .state */
338+
pwm_wf2state(wf, &pwm->state);
339+
340+
if (IS_ENABLED(CONFIG_PWM_DEBUG) && ops->read_waveform && wf->period_length_ns) {
341+
struct pwm_waveform wf_set;
342+
343+
err = __pwm_read_waveform(chip, pwm, &wfhw);
344+
if (err)
345+
/* maybe ignore? */
346+
return err;
347+
348+
err = __pwm_round_waveform_fromhw(chip, pwm, &wfhw, &wf_set);
349+
if (err)
350+
/* maybe ignore? */
351+
return err;
352+
353+
if (pwmwfcmp(&wf_set, &wf_rounded) != 0)
354+
dev_err(&chip->dev,
355+
"Unexpected setting: requested %llu/%llu [+%llu], expected %llu/%llu [+%llu], set %llu/%llu [+%llu]\n",
356+
wf->duty_length_ns, wf->period_length_ns, wf->duty_offset_ns,
357+
wf_rounded.duty_length_ns, wf_rounded.period_length_ns, wf_rounded.duty_offset_ns,
358+
wf_set.duty_length_ns, wf_set.period_length_ns, wf_set.duty_offset_ns);
359+
}
360+
return 0;
361+
}
362+
363+
/**
364+
* pwm_set_waveform_might_sleep - Apply a new waveform
365+
* Cannot be used in atomic context.
366+
* @pwm: PWM device
367+
* @wf: The waveform to apply
368+
* @exact: If true no rounding is allowed
369+
*
370+
* Typically a requested waveform cannot be implemented exactly, e.g. because
371+
* you requested .period_length_ns = 100 ns, but the hardware can only set
372+
* periods that are a multiple of 8.5 ns. With that hardware passing exact =
373+
* true results in pwm_set_waveform_might_sleep() failing and returning 1. If
374+
* exact = false you get a period of 93.5 ns (i.e. the biggest period not bigger
375+
* than the requested value).
376+
* Note that even with exact = true, some rounding by less than 1 is
377+
* possible/needed. In the above example requesting .period_length_ns = 94 and
378+
* exact = true, you get the hardware configured with period = 93.5 ns.
379+
*/
380+
int pwm_set_waveform_might_sleep(struct pwm_device *pwm,
381+
const struct pwm_waveform *wf, bool exact)
382+
{
383+
struct pwm_chip *chip = pwm->chip;
384+
int err;
385+
386+
might_sleep();
387+
388+
guard(pwmchip)(chip);
389+
390+
if (!chip->operational)
391+
return -ENODEV;
392+
393+
if (IS_ENABLED(CONFIG_PWM_DEBUG) && chip->atomic) {
394+
/*
395+
* Catch any drivers that have been marked as atomic but
396+
* that will sleep anyway.
397+
*/
398+
non_block_start();
399+
err = __pwm_set_waveform(pwm, wf, exact);
400+
non_block_end();
401+
} else {
402+
err = __pwm_set_waveform(pwm, wf, exact);
403+
}
404+
405+
return err;
406+
}
407+
EXPORT_SYMBOL_GPL(pwm_set_waveform_might_sleep);
408+
148409
static void pwm_apply_debug(struct pwm_device *pwm,
149410
const struct pwm_state *state)
150411
{

include/linux/pwm.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,11 @@ static inline void pwmchip_set_drvdata(struct pwm_chip *chip, void *data)
358358
}
359359

360360
#if IS_ENABLED(CONFIG_PWM)
361-
/* PWM user APIs */
361+
362+
/* PWM consumer APIs */
363+
int pwm_round_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf);
364+
int pwm_get_waveform_might_sleep(struct pwm_device *pwm, struct pwm_waveform *wf);
365+
int pwm_set_waveform_might_sleep(struct pwm_device *pwm, const struct pwm_waveform *wf, bool exact);
362366
int pwm_apply_might_sleep(struct pwm_device *pwm, const struct pwm_state *state);
363367
int pwm_apply_atomic(struct pwm_device *pwm, const struct pwm_state *state);
364368
int pwm_adjust_config(struct pwm_device *pwm);

0 commit comments

Comments
 (0)