Lines Matching +full:clk +full:- +full:pwm
1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
3 * PWM controller driver for Amlogic Meson SoCs.
5 * This PWM is only a set of Gates, Dividers and Counters:
6 * PWM output is achieved by calculating a clock that permits calculating
13 * Setting the duty cycle will disable and re-enable the PWM output.
14 * Disabling the PWM stops the output immediately (without waiting for the
17 * The public S912 (GXM) datasheet contains some documentation for this PWM
19 * https://dl.khadas.com/Hardware/VIM2/Datasheet/S912_Datasheet_V0.220170314publicversion-Wesion.pdf
23 * https://dn.odroid.com/S922X/ODROID-N2/Datasheet/S922X_Public_Datasheet_V0.2.pdf
32 #include <linux/clk.h>
33 #include <linux/clk-provider.h>
41 #include <linux/pwm.h>
108 struct clk *clk; member
134 static int meson_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) in meson_pwm_request() argument
137 struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm]; in meson_pwm_request()
141 err = clk_prepare_enable(channel->clk); in meson_pwm_request()
144 __clk_get_name(channel->clk), err); in meson_pwm_request()
151 static void meson_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) in meson_pwm_free() argument
154 struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm]; in meson_pwm_free()
156 clk_disable_unprepare(channel->clk); in meson_pwm_free()
159 static int meson_pwm_calc(struct pwm_chip *chip, struct pwm_device *pwm, in meson_pwm_calc() argument
163 struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm]; in meson_pwm_calc()
168 duty = state->duty_cycle; in meson_pwm_calc()
169 period = state->period; in meson_pwm_calc()
177 if (state->polarity == PWM_POLARITY_INVERSED && !meson->data->has_polarity) in meson_pwm_calc()
178 duty = period - duty; in meson_pwm_calc()
184 fin_freq = clk_round_rate(channel->clk, freq); in meson_pwm_calc()
188 return fin_freq ? fin_freq : -EINVAL; in meson_pwm_calc()
196 return -EINVAL; in meson_pwm_calc()
202 channel->hi = cnt; in meson_pwm_calc()
203 channel->lo = 0; in meson_pwm_calc()
204 channel->constant = true; in meson_pwm_calc()
206 channel->hi = 0; in meson_pwm_calc()
207 channel->lo = cnt; in meson_pwm_calc()
208 channel->constant = true; in meson_pwm_calc()
214 channel->hi = duty_cnt; in meson_pwm_calc()
215 channel->lo = cnt - duty_cnt; in meson_pwm_calc()
216 channel->constant = false; in meson_pwm_calc()
219 channel->rate = fin_freq; in meson_pwm_calc()
224 static void meson_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) in meson_pwm_enable() argument
227 struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm]; in meson_pwm_enable()
233 channel_data = &meson_pwm_per_channel_data[pwm->hwpwm]; in meson_pwm_enable()
235 err = clk_set_rate(channel->clk, channel->rate); in meson_pwm_enable()
239 spin_lock_irqsave(&meson->lock, flags); in meson_pwm_enable()
241 value = FIELD_PREP(PWM_HIGH_MASK, channel->hi) | in meson_pwm_enable()
242 FIELD_PREP(PWM_LOW_MASK, channel->lo); in meson_pwm_enable()
243 writel(value, meson->base + channel_data->reg_offset); in meson_pwm_enable()
245 value = readl(meson->base + REG_MISC_AB); in meson_pwm_enable()
246 value |= channel_data->pwm_en_mask; in meson_pwm_enable()
248 if (meson->data->has_constant) { in meson_pwm_enable()
249 value &= ~channel_data->const_en_mask; in meson_pwm_enable()
250 if (channel->constant) in meson_pwm_enable()
251 value |= channel_data->const_en_mask; in meson_pwm_enable()
254 if (meson->data->has_polarity) { in meson_pwm_enable()
255 value &= ~channel_data->inv_en_mask; in meson_pwm_enable()
256 if (channel->inverted) in meson_pwm_enable()
257 value |= channel_data->inv_en_mask; in meson_pwm_enable()
260 writel(value, meson->base + REG_MISC_AB); in meson_pwm_enable()
262 spin_unlock_irqrestore(&meson->lock, flags); in meson_pwm_enable()
265 static void meson_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) in meson_pwm_disable() argument
268 struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm]; in meson_pwm_disable()
273 channel_data = &meson_pwm_per_channel_data[pwm->hwpwm]; in meson_pwm_disable()
275 spin_lock_irqsave(&meson->lock, flags); in meson_pwm_disable()
277 value = readl(meson->base + REG_MISC_AB); in meson_pwm_disable()
278 value &= ~channel_data->pwm_en_mask; in meson_pwm_disable()
280 if (meson->data->has_polarity) { in meson_pwm_disable()
281 value &= ~channel_data->inv_en_mask; in meson_pwm_disable()
282 if (channel->inverted) in meson_pwm_disable()
283 value |= channel_data->inv_en_mask; in meson_pwm_disable()
286 writel(value, meson->base + REG_MISC_AB); in meson_pwm_disable()
288 spin_unlock_irqrestore(&meson->lock, flags); in meson_pwm_disable()
291 static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, in meson_pwm_apply() argument
295 struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm]; in meson_pwm_apply()
298 channel->inverted = (state->polarity == PWM_POLARITY_INVERSED); in meson_pwm_apply()
300 if (!state->enabled) { in meson_pwm_apply()
301 if (channel->inverted && !meson->data->has_polarity) { in meson_pwm_apply()
314 channel->rate = ULONG_MAX; in meson_pwm_apply()
315 channel->hi = ~0; in meson_pwm_apply()
316 channel->lo = 0; in meson_pwm_apply()
317 channel->constant = true; in meson_pwm_apply()
319 meson_pwm_enable(chip, pwm); in meson_pwm_apply()
321 meson_pwm_disable(chip, pwm); in meson_pwm_apply()
324 err = meson_pwm_calc(chip, pwm, state); in meson_pwm_apply()
328 meson_pwm_enable(chip, pwm); in meson_pwm_apply()
339 static int meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, in meson_pwm_get_state() argument
348 channel_data = &meson_pwm_per_channel_data[pwm->hwpwm]; in meson_pwm_get_state()
349 fin_freq = clk_get_rate(meson->channels[pwm->hwpwm].clk); in meson_pwm_get_state()
351 value = readl(meson->base + REG_MISC_AB); in meson_pwm_get_state()
352 state->enabled = value & channel_data->pwm_en_mask; in meson_pwm_get_state()
354 if (meson->data->has_polarity && (value & channel_data->inv_en_mask)) in meson_pwm_get_state()
355 state->polarity = PWM_POLARITY_INVERSED; in meson_pwm_get_state()
357 state->polarity = PWM_POLARITY_NORMAL; in meson_pwm_get_state()
359 value = readl(meson->base + channel_data->reg_offset); in meson_pwm_get_state()
363 state->period = meson_pwm_cnt_to_ns(fin_freq, lo + hi); in meson_pwm_get_state()
364 state->duty_cycle = meson_pwm_cnt_to_ns(fin_freq, hi); in meson_pwm_get_state()
386 struct meson_pwm_channel *channel = &meson->channels[i]; in meson_pwm_init_clocks_meson8b()
398 channel->mux.reg = meson->base + REG_MISC_AB; in meson_pwm_init_clocks_meson8b()
399 channel->mux.shift = in meson_pwm_init_clocks_meson8b()
401 channel->mux.mask = MISC_CLK_SEL_MASK; in meson_pwm_init_clocks_meson8b()
402 channel->mux.flags = 0; in meson_pwm_init_clocks_meson8b()
403 channel->mux.lock = &meson->lock; in meson_pwm_init_clocks_meson8b()
404 channel->mux.table = NULL; in meson_pwm_init_clocks_meson8b()
405 channel->mux.hw.init = &init; in meson_pwm_init_clocks_meson8b()
407 err = devm_clk_hw_register(dev, &channel->mux.hw); in meson_pwm_init_clocks_meson8b()
417 div_parent.index = -1; in meson_pwm_init_clocks_meson8b()
418 div_parent.hw = &channel->mux.hw; in meson_pwm_init_clocks_meson8b()
422 channel->div.reg = meson->base + REG_MISC_AB; in meson_pwm_init_clocks_meson8b()
423 channel->div.shift = meson_pwm_per_channel_data[i].clk_div_shift; in meson_pwm_init_clocks_meson8b()
424 channel->div.width = MISC_CLK_DIV_WIDTH; in meson_pwm_init_clocks_meson8b()
425 channel->div.hw.init = &init; in meson_pwm_init_clocks_meson8b()
426 channel->div.flags = 0; in meson_pwm_init_clocks_meson8b()
427 channel->div.lock = &meson->lock; in meson_pwm_init_clocks_meson8b()
429 err = devm_clk_hw_register(dev, &channel->div.hw); in meson_pwm_init_clocks_meson8b()
439 gate_parent.index = -1; in meson_pwm_init_clocks_meson8b()
440 gate_parent.hw = &channel->div.hw; in meson_pwm_init_clocks_meson8b()
444 channel->gate.reg = meson->base + REG_MISC_AB; in meson_pwm_init_clocks_meson8b()
445 channel->gate.bit_idx = meson_pwm_per_channel_data[i].clk_en_shift; in meson_pwm_init_clocks_meson8b()
446 channel->gate.hw.init = &init; in meson_pwm_init_clocks_meson8b()
447 channel->gate.flags = 0; in meson_pwm_init_clocks_meson8b()
448 channel->gate.lock = &meson->lock; in meson_pwm_init_clocks_meson8b()
450 err = devm_clk_hw_register(dev, &channel->gate.hw); in meson_pwm_init_clocks_meson8b()
454 channel->clk = devm_clk_hw_get_clk(dev, &channel->gate.hw, NULL); in meson_pwm_init_clocks_meson8b()
455 if (IS_ERR(channel->clk)) in meson_pwm_init_clocks_meson8b()
456 return dev_err_probe(dev, PTR_ERR(channel->clk), in meson_pwm_init_clocks_meson8b()
473 mux_parent_data[i].index = -1; in meson_pwm_init_channels_meson8b_legacy()
474 mux_parent_data[i].name = meson->data->parent_names[i]; in meson_pwm_init_channels_meson8b_legacy()
490 * Also DT requires clock-names to be explicitly ordered, so there is in meson_pwm_init_channels_meson8b_v2()
501 struct clk *clk = data; in meson_pwm_s4_put_clk() local
503 clk_put(clk); in meson_pwm_s4_put_clk()
509 struct device_node *np = dev->of_node; in meson_pwm_init_channels_s4()
514 meson->channels[i].clk = of_clk_get(np, i); in meson_pwm_init_channels_s4()
515 if (IS_ERR(meson->channels[i].clk)) in meson_pwm_init_channels_s4()
517 PTR_ERR(meson->channels[i].clk), in meson_pwm_init_channels_s4()
518 "Failed to get clk\n"); in meson_pwm_init_channels_s4()
521 meson->channels[i].clk); in meson_pwm_init_channels_s4()
597 .compatible = "amlogic,meson8-pwm-v2",
601 .compatible = "amlogic,meson-axg-pwm-v2",
605 .compatible = "amlogic,meson-g12-pwm-v2",
610 .compatible = "amlogic,meson8b-pwm",
614 .compatible = "amlogic,meson-gxbb-pwm",
618 .compatible = "amlogic,meson-gxbb-ao-pwm",
622 .compatible = "amlogic,meson-axg-ee-pwm",
626 .compatible = "amlogic,meson-axg-ao-pwm",
630 .compatible = "amlogic,meson-g12a-ee-pwm",
634 .compatible = "amlogic,meson-g12a-ao-pwm-ab",
638 .compatible = "amlogic,meson-g12a-ao-pwm-cd",
642 .compatible = "amlogic,meson-s4-pwm",
655 chip = devm_pwmchip_alloc(&pdev->dev, MESON_NUM_PWMS, sizeof(*meson)); in meson_pwm_probe()
660 meson->base = devm_platform_ioremap_resource(pdev, 0); in meson_pwm_probe()
661 if (IS_ERR(meson->base)) in meson_pwm_probe()
662 return PTR_ERR(meson->base); in meson_pwm_probe()
664 spin_lock_init(&meson->lock); in meson_pwm_probe()
665 chip->ops = &meson_pwm_ops; in meson_pwm_probe()
667 meson->data = of_device_get_match_data(&pdev->dev); in meson_pwm_probe()
669 err = meson->data->channels_init(chip); in meson_pwm_probe()
673 err = devm_pwmchip_add(&pdev->dev, chip); in meson_pwm_probe()
675 return dev_err_probe(&pdev->dev, err, in meson_pwm_probe()
676 "failed to register PWM chip\n"); in meson_pwm_probe()
683 .name = "meson-pwm",
690 MODULE_DESCRIPTION("Amlogic Meson PWM Generator driver");