Lines Matching +full:meson +full:- +full:gxbb +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
33 #include <linux/clk-provider.h>
41 #include <linux/pwm.h>
121 static int meson_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) in meson_pwm_request() argument
123 struct meson_pwm *meson = to_meson_pwm(chip); in meson_pwm_request() local
124 struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm]; in meson_pwm_request()
125 struct device *dev = chip->dev; in meson_pwm_request()
128 err = clk_prepare_enable(channel->clk); in meson_pwm_request()
131 __clk_get_name(channel->clk), err); in meson_pwm_request()
138 static void meson_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) in meson_pwm_free() argument
140 struct meson_pwm *meson = to_meson_pwm(chip); in meson_pwm_free() local
141 struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm]; in meson_pwm_free()
143 clk_disable_unprepare(channel->clk); in meson_pwm_free()
146 static int meson_pwm_calc(struct meson_pwm *meson, struct pwm_device *pwm, in meson_pwm_calc() argument
149 struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm]; in meson_pwm_calc()
154 duty = state->duty_cycle; in meson_pwm_calc()
155 period = state->period; in meson_pwm_calc()
163 if (state->polarity == PWM_POLARITY_INVERSED) in meson_pwm_calc()
164 duty = period - duty; in meson_pwm_calc()
170 fin_freq = clk_round_rate(channel->clk, freq); in meson_pwm_calc()
172 dev_err(meson->chip.dev, "invalid source clock frequency\n"); in meson_pwm_calc()
173 return -EINVAL; in meson_pwm_calc()
176 dev_dbg(meson->chip.dev, "fin_freq: %lu Hz\n", fin_freq); in meson_pwm_calc()
180 dev_err(meson->chip.dev, "unable to get period cnt\n"); in meson_pwm_calc()
181 return -EINVAL; in meson_pwm_calc()
184 dev_dbg(meson->chip.dev, "period=%llu cnt=%u\n", period, cnt); in meson_pwm_calc()
187 channel->hi = cnt; in meson_pwm_calc()
188 channel->lo = 0; in meson_pwm_calc()
190 channel->hi = 0; in meson_pwm_calc()
191 channel->lo = cnt; in meson_pwm_calc()
195 dev_dbg(meson->chip.dev, "duty=%llu duty_cnt=%u\n", duty, duty_cnt); in meson_pwm_calc()
197 channel->hi = duty_cnt; in meson_pwm_calc()
198 channel->lo = cnt - duty_cnt; in meson_pwm_calc()
201 channel->rate = fin_freq; in meson_pwm_calc()
206 static void meson_pwm_enable(struct meson_pwm *meson, struct pwm_device *pwm) in meson_pwm_enable() argument
208 struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm]; in meson_pwm_enable()
214 channel_data = &meson_pwm_per_channel_data[pwm->hwpwm]; in meson_pwm_enable()
216 err = clk_set_rate(channel->clk, channel->rate); in meson_pwm_enable()
218 dev_err(meson->chip.dev, "setting clock rate failed\n"); in meson_pwm_enable()
220 spin_lock_irqsave(&meson->lock, flags); in meson_pwm_enable()
222 value = FIELD_PREP(PWM_HIGH_MASK, channel->hi) | in meson_pwm_enable()
223 FIELD_PREP(PWM_LOW_MASK, channel->lo); in meson_pwm_enable()
224 writel(value, meson->base + channel_data->reg_offset); in meson_pwm_enable()
226 value = readl(meson->base + REG_MISC_AB); in meson_pwm_enable()
227 value |= channel_data->pwm_en_mask; in meson_pwm_enable()
228 writel(value, meson->base + REG_MISC_AB); in meson_pwm_enable()
230 spin_unlock_irqrestore(&meson->lock, flags); in meson_pwm_enable()
233 static void meson_pwm_disable(struct meson_pwm *meson, struct pwm_device *pwm) in meson_pwm_disable() argument
238 spin_lock_irqsave(&meson->lock, flags); in meson_pwm_disable()
240 value = readl(meson->base + REG_MISC_AB); in meson_pwm_disable()
241 value &= ~meson_pwm_per_channel_data[pwm->hwpwm].pwm_en_mask; in meson_pwm_disable()
242 writel(value, meson->base + REG_MISC_AB); in meson_pwm_disable()
244 spin_unlock_irqrestore(&meson->lock, flags); in meson_pwm_disable()
247 static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, in meson_pwm_apply() argument
250 struct meson_pwm *meson = to_meson_pwm(chip); in meson_pwm_apply() local
251 struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm]; in meson_pwm_apply()
254 if (!state->enabled) { in meson_pwm_apply()
255 if (state->polarity == PWM_POLARITY_INVERSED) { in meson_pwm_apply()
268 channel->rate = ULONG_MAX; in meson_pwm_apply()
269 channel->hi = ~0; in meson_pwm_apply()
270 channel->lo = 0; in meson_pwm_apply()
272 meson_pwm_enable(meson, pwm); in meson_pwm_apply()
274 meson_pwm_disable(meson, pwm); in meson_pwm_apply()
277 err = meson_pwm_calc(meson, pwm, state); in meson_pwm_apply()
281 meson_pwm_enable(meson, pwm); in meson_pwm_apply()
287 static u64 meson_pwm_cnt_to_ns(struct pwm_chip *chip, struct pwm_device *pwm, in meson_pwm_cnt_to_ns() argument
290 struct meson_pwm *meson = to_meson_pwm(chip); in meson_pwm_cnt_to_ns() local
295 channel = &meson->channels[pwm->hwpwm]; in meson_pwm_cnt_to_ns()
297 fin_freq = clk_get_rate(channel->clk); in meson_pwm_cnt_to_ns()
304 static int meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, in meson_pwm_get_state() argument
307 struct meson_pwm *meson = to_meson_pwm(chip); in meson_pwm_get_state() local
315 channel = &meson->channels[pwm->hwpwm]; in meson_pwm_get_state()
316 channel_data = &meson_pwm_per_channel_data[pwm->hwpwm]; in meson_pwm_get_state()
318 value = readl(meson->base + REG_MISC_AB); in meson_pwm_get_state()
319 state->enabled = value & channel_data->pwm_en_mask; in meson_pwm_get_state()
321 value = readl(meson->base + channel_data->reg_offset); in meson_pwm_get_state()
322 channel->lo = FIELD_GET(PWM_LOW_MASK, value); in meson_pwm_get_state()
323 channel->hi = FIELD_GET(PWM_HIGH_MASK, value); in meson_pwm_get_state()
325 state->period = meson_pwm_cnt_to_ns(chip, pwm, channel->lo + channel->hi); in meson_pwm_get_state()
326 state->duty_cycle = meson_pwm_cnt_to_ns(chip, pwm, channel->hi); in meson_pwm_get_state()
328 state->polarity = PWM_POLARITY_NORMAL; in meson_pwm_get_state()
350 * Only the 2 first inputs of the GXBB AO PWMs are valid
400 .compatible = "amlogic,meson8b-pwm",
404 .compatible = "amlogic,meson-gxbb-pwm",
408 .compatible = "amlogic,meson-gxbb-ao-pwm",
412 .compatible = "amlogic,meson-axg-ee-pwm",
416 .compatible = "amlogic,meson-axg-ao-pwm",
420 .compatible = "amlogic,meson-g12a-ee-pwm",
424 .compatible = "amlogic,meson-g12a-ao-pwm-ab",
428 .compatible = "amlogic,meson-g12a-ao-pwm-cd",
435 static int meson_pwm_init_channels(struct meson_pwm *meson) in meson_pwm_init_channels() argument
438 struct device *dev = meson->chip.dev; in meson_pwm_init_channels()
443 for (i = 0; i < meson->data->num_parents; i++) { in meson_pwm_init_channels()
444 mux_parent_data[i].index = -1; in meson_pwm_init_channels()
445 mux_parent_data[i].name = meson->data->parent_names[i]; in meson_pwm_init_channels()
448 for (i = 0; i < meson->chip.npwm; i++) { in meson_pwm_init_channels()
449 struct meson_pwm_channel *channel = &meson->channels[i]; in meson_pwm_init_channels()
459 init.num_parents = meson->data->num_parents; in meson_pwm_init_channels()
461 channel->mux.reg = meson->base + REG_MISC_AB; in meson_pwm_init_channels()
462 channel->mux.shift = in meson_pwm_init_channels()
464 channel->mux.mask = MISC_CLK_SEL_MASK; in meson_pwm_init_channels()
465 channel->mux.flags = 0; in meson_pwm_init_channels()
466 channel->mux.lock = &meson->lock; in meson_pwm_init_channels()
467 channel->mux.table = NULL; in meson_pwm_init_channels()
468 channel->mux.hw.init = &init; in meson_pwm_init_channels()
470 err = devm_clk_hw_register(dev, &channel->mux.hw); in meson_pwm_init_channels()
480 div_parent.index = -1; in meson_pwm_init_channels()
481 div_parent.hw = &channel->mux.hw; in meson_pwm_init_channels()
485 channel->div.reg = meson->base + REG_MISC_AB; in meson_pwm_init_channels()
486 channel->div.shift = meson_pwm_per_channel_data[i].clk_div_shift; in meson_pwm_init_channels()
487 channel->div.width = MISC_CLK_DIV_WIDTH; in meson_pwm_init_channels()
488 channel->div.hw.init = &init; in meson_pwm_init_channels()
489 channel->div.flags = 0; in meson_pwm_init_channels()
490 channel->div.lock = &meson->lock; in meson_pwm_init_channels()
492 err = devm_clk_hw_register(dev, &channel->div.hw); in meson_pwm_init_channels()
502 gate_parent.index = -1; in meson_pwm_init_channels()
503 gate_parent.hw = &channel->div.hw; in meson_pwm_init_channels()
507 channel->gate.reg = meson->base + REG_MISC_AB; in meson_pwm_init_channels()
508 channel->gate.bit_idx = meson_pwm_per_channel_data[i].clk_en_shift; in meson_pwm_init_channels()
509 channel->gate.hw.init = &init; in meson_pwm_init_channels()
510 channel->gate.flags = 0; in meson_pwm_init_channels()
511 channel->gate.lock = &meson->lock; in meson_pwm_init_channels()
513 err = devm_clk_hw_register(dev, &channel->gate.hw); in meson_pwm_init_channels()
517 channel->clk = devm_clk_hw_get_clk(dev, &channel->gate.hw, NULL); in meson_pwm_init_channels()
518 if (IS_ERR(channel->clk)) in meson_pwm_init_channels()
519 return dev_err_probe(dev, PTR_ERR(channel->clk), in meson_pwm_init_channels()
528 struct meson_pwm *meson; in meson_pwm_probe() local
531 meson = devm_kzalloc(&pdev->dev, sizeof(*meson), GFP_KERNEL); in meson_pwm_probe()
532 if (!meson) in meson_pwm_probe()
533 return -ENOMEM; in meson_pwm_probe()
535 meson->base = devm_platform_ioremap_resource(pdev, 0); in meson_pwm_probe()
536 if (IS_ERR(meson->base)) in meson_pwm_probe()
537 return PTR_ERR(meson->base); in meson_pwm_probe()
539 spin_lock_init(&meson->lock); in meson_pwm_probe()
540 meson->chip.dev = &pdev->dev; in meson_pwm_probe()
541 meson->chip.ops = &meson_pwm_ops; in meson_pwm_probe()
542 meson->chip.npwm = MESON_NUM_PWMS; in meson_pwm_probe()
544 meson->data = of_device_get_match_data(&pdev->dev); in meson_pwm_probe()
546 err = meson_pwm_init_channels(meson); in meson_pwm_probe()
550 err = devm_pwmchip_add(&pdev->dev, &meson->chip); in meson_pwm_probe()
552 return dev_err_probe(&pdev->dev, err, in meson_pwm_probe()
553 "failed to register PWM chip\n"); in meson_pwm_probe()
560 .name = "meson-pwm",
567 MODULE_DESCRIPTION("Amlogic Meson PWM Generator driver");