Lines Matching +full:fu540 +full:- +full:c000 +full:- +full:pwm
1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2017-2018 SiFive
4 * For SiFive's PWM IP block documentation please refer Chapter 14 of
5 * Reference Manual : https://static.dev.sifive.com/FU540-C000-v1.0.pdf
7 * PWM output inversion: According to the SiFive Reference manual
12 * hard-tied to 0 (XNOR), which effectively inverts the comparison so that
21 * **active-high** PWM interface.
25 * - When changing both duty cycle and period, we cannot prevent in
28 * - The hardware cannot generate a 0% duty cycle.
29 * - The hardware generates only inverted output.
36 #include <linux/pwm.h>
77 static int pwm_sifive_request(struct pwm_chip *chip, struct pwm_device *pwm) in pwm_sifive_request() argument
81 mutex_lock(&ddata->lock); in pwm_sifive_request()
82 ddata->user_count++; in pwm_sifive_request()
83 mutex_unlock(&ddata->lock); in pwm_sifive_request()
88 static void pwm_sifive_free(struct pwm_chip *chip, struct pwm_device *pwm) in pwm_sifive_free() argument
92 mutex_lock(&ddata->lock); in pwm_sifive_free()
93 ddata->user_count--; in pwm_sifive_free()
94 mutex_unlock(&ddata->lock); in pwm_sifive_free()
97 /* Called holding ddata->lock */
106 * The PWM unit is used with pwmzerocmp=0, so the only way to modify the in pwm_sifive_update_clock()
112 scale_pow = div64_ul(ddata->approx_period * (u64)rate, NSEC_PER_SEC); in pwm_sifive_update_clock()
113 scale = clamp(ilog2(scale_pow) - PWM_SIFIVE_CMPWIDTH, 0, 0xf); in pwm_sifive_update_clock()
117 writel(val, ddata->regs + PWM_SIFIVE_PWMCFG); in pwm_sifive_update_clock()
121 ddata->real_period = DIV_ROUND_UP_ULL(num, rate); in pwm_sifive_update_clock()
122 dev_dbg(ddata->parent, in pwm_sifive_update_clock()
123 "New real_period = %u ns\n", ddata->real_period); in pwm_sifive_update_clock()
126 static int pwm_sifive_get_state(struct pwm_chip *chip, struct pwm_device *pwm, in pwm_sifive_get_state() argument
132 inactive = readl(ddata->regs + PWM_SIFIVE_PWMCMP(pwm->hwpwm)); in pwm_sifive_get_state()
134 * PWM hardware uses 'inactive' counts in pwmcmp, so invert to get actual duty. in pwm_sifive_get_state()
135 * Here, 'inactive' is the low time and we compute duty as max_count - inactive. in pwm_sifive_get_state()
137 duty = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - inactive; in pwm_sifive_get_state()
139 state->enabled = duty > 0; in pwm_sifive_get_state()
141 val = readl(ddata->regs + PWM_SIFIVE_PWMCFG); in pwm_sifive_get_state()
143 state->enabled = false; in pwm_sifive_get_state()
145 state->period = ddata->real_period; in pwm_sifive_get_state()
146 state->duty_cycle = DIV_ROUND_UP_ULL((u64)duty * ddata->real_period, in pwm_sifive_get_state()
148 state->polarity = PWM_POLARITY_NORMAL; in pwm_sifive_get_state()
153 static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm, in pwm_sifive_apply() argument
165 if (state->polarity != PWM_POLARITY_NORMAL) in pwm_sifive_apply()
166 return -EINVAL; in pwm_sifive_apply()
168 cur_state = pwm->state; in pwm_sifive_apply()
171 duty_cycle = state->duty_cycle; in pwm_sifive_apply()
172 if (!state->enabled) in pwm_sifive_apply()
183 do_div(frac, state->period); in pwm_sifive_apply()
185 frac = min(frac, (u64)(1U << PWM_SIFIVE_CMPWIDTH) - 1); in pwm_sifive_apply()
187 inactive = (1U << PWM_SIFIVE_CMPWIDTH) - 1 - frac; in pwm_sifive_apply()
189 mutex_lock(&ddata->lock); in pwm_sifive_apply()
190 if (state->period != ddata->approx_period) { in pwm_sifive_apply()
193 * However if ddate->approx_period == 0 this is the first time we set in pwm_sifive_apply()
197 if (ddata->user_count != 1 && ddata->approx_period) { in pwm_sifive_apply()
198 mutex_unlock(&ddata->lock); in pwm_sifive_apply()
199 return -EBUSY; in pwm_sifive_apply()
201 ddata->approx_period = state->period; in pwm_sifive_apply()
202 pwm_sifive_update_clock(ddata, clk_get_rate(ddata->clk)); in pwm_sifive_apply()
204 mutex_unlock(&ddata->lock); in pwm_sifive_apply()
207 * If the PWM is enabled the clk is already on. So only enable it in pwm_sifive_apply()
209 * the PWM state. in pwm_sifive_apply()
212 ret = clk_enable(ddata->clk); in pwm_sifive_apply()
219 writel(inactive, ddata->regs + PWM_SIFIVE_PWMCMP(pwm->hwpwm)); in pwm_sifive_apply()
221 if (!state->enabled) in pwm_sifive_apply()
222 clk_disable(ddata->clk); in pwm_sifive_apply()
242 mutex_lock(&ddata->lock); in pwm_sifive_clock_notifier()
243 pwm_sifive_update_clock(ddata, ndata->new_rate); in pwm_sifive_clock_notifier()
244 mutex_unlock(&ddata->lock); in pwm_sifive_clock_notifier()
252 struct device *dev = &pdev->dev; in pwm_sifive_probe()
264 ddata->parent = dev; in pwm_sifive_probe()
265 mutex_init(&ddata->lock); in pwm_sifive_probe()
266 chip->ops = &pwm_sifive_ops; in pwm_sifive_probe()
268 ddata->regs = devm_platform_ioremap_resource(pdev, 0); in pwm_sifive_probe()
269 if (IS_ERR(ddata->regs)) in pwm_sifive_probe()
270 return PTR_ERR(ddata->regs); in pwm_sifive_probe()
272 ddata->clk = devm_clk_get_prepared(dev, NULL); in pwm_sifive_probe()
273 if (IS_ERR(ddata->clk)) in pwm_sifive_probe()
274 return dev_err_probe(dev, PTR_ERR(ddata->clk), in pwm_sifive_probe()
277 ret = clk_enable(ddata->clk); in pwm_sifive_probe()
279 dev_err(dev, "failed to enable clock for pwm: %d\n", ret); in pwm_sifive_probe()
283 val = readl(ddata->regs + PWM_SIFIVE_PWMCFG); in pwm_sifive_probe()
287 for (i = 0; i < chip->npwm; ++i) { in pwm_sifive_probe()
288 val = readl(ddata->regs + PWM_SIFIVE_PWMCMP(i)); in pwm_sifive_probe()
294 /* The clk should be on once for each running PWM. */ in pwm_sifive_probe()
298 ret = clk_enable(ddata->clk); in pwm_sifive_probe()
306 clk_disable(ddata->clk); in pwm_sifive_probe()
311 ddata->notifier.notifier_call = pwm_sifive_clock_notifier; in pwm_sifive_probe()
312 ret = clk_notifier_register(ddata->clk, &ddata->notifier); in pwm_sifive_probe()
320 dev_err(dev, "cannot register PWM: %d\n", ret); in pwm_sifive_probe()
325 dev_dbg(dev, "SiFive PWM chip registered %d PWMs\n", chip->npwm); in pwm_sifive_probe()
330 clk_notifier_unregister(ddata->clk, &ddata->notifier); in pwm_sifive_probe()
333 clk_disable(ddata->clk); in pwm_sifive_probe()
334 --enabled_clks; in pwm_sifive_probe()
344 struct pwm_device *pwm; in pwm_sifive_remove() local
348 clk_notifier_unregister(ddata->clk, &ddata->notifier); in pwm_sifive_remove()
350 for (ch = 0; ch < chip->npwm; ch++) { in pwm_sifive_remove()
351 pwm = &chip->pwms[ch]; in pwm_sifive_remove()
352 if (pwm->state.enabled) in pwm_sifive_remove()
353 clk_disable(ddata->clk); in pwm_sifive_remove()
367 .name = "pwm-sifive",
373 MODULE_DESCRIPTION("SiFive PWM driver");