Lines Matching +full:pwm +full:- +full:channels +full:- +full:mask
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>
42 #include <linux/pwm.h>
106 struct meson_pwm_channel channels[MESON_NUM_PWMS]; member
120 static int meson_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) in meson_pwm_request() argument
124 struct device *dev = chip->dev; in meson_pwm_request()
127 channel = pwm_get_chip_data(pwm); in meson_pwm_request()
131 channel = &meson->channels[pwm->hwpwm]; in meson_pwm_request()
133 if (channel->clk_parent) { in meson_pwm_request()
134 err = clk_set_parent(channel->clk, channel->clk_parent); in meson_pwm_request()
137 __clk_get_name(channel->clk_parent), in meson_pwm_request()
138 __clk_get_name(channel->clk), err); in meson_pwm_request()
143 err = clk_prepare_enable(channel->clk); in meson_pwm_request()
146 __clk_get_name(channel->clk), err); in meson_pwm_request()
150 return pwm_set_chip_data(pwm, channel); in meson_pwm_request()
153 static void meson_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) in meson_pwm_free() argument
155 struct meson_pwm_channel *channel = pwm_get_chip_data(pwm); in meson_pwm_free()
158 clk_disable_unprepare(channel->clk); in meson_pwm_free()
161 static int meson_pwm_calc(struct meson_pwm *meson, struct pwm_device *pwm, in meson_pwm_calc() argument
164 struct meson_pwm_channel *channel = pwm_get_chip_data(pwm); in meson_pwm_calc()
168 duty = state->duty_cycle; in meson_pwm_calc()
169 period = state->period; in meson_pwm_calc()
171 if (state->polarity == PWM_POLARITY_INVERSED) in meson_pwm_calc()
172 duty = period - duty; in meson_pwm_calc()
174 fin_freq = clk_get_rate(channel->clk); in meson_pwm_calc()
176 dev_err(meson->chip.dev, "invalid source clock frequency\n"); in meson_pwm_calc()
177 return -EINVAL; in meson_pwm_calc()
180 dev_dbg(meson->chip.dev, "fin_freq: %lu Hz\n", fin_freq); in meson_pwm_calc()
184 dev_err(meson->chip.dev, "unable to get period pre_div\n"); in meson_pwm_calc()
185 return -EINVAL; in meson_pwm_calc()
190 dev_err(meson->chip.dev, "unable to get period cnt\n"); in meson_pwm_calc()
191 return -EINVAL; in meson_pwm_calc()
194 dev_dbg(meson->chip.dev, "period=%u pre_div=%u cnt=%u\n", period, in meson_pwm_calc()
198 channel->pre_div = pre_div; in meson_pwm_calc()
199 channel->hi = cnt; in meson_pwm_calc()
200 channel->lo = 0; in meson_pwm_calc()
202 channel->pre_div = pre_div; in meson_pwm_calc()
203 channel->hi = 0; in meson_pwm_calc()
204 channel->lo = cnt; in meson_pwm_calc()
210 dev_err(meson->chip.dev, "unable to get duty cycle\n"); in meson_pwm_calc()
211 return -EINVAL; in meson_pwm_calc()
214 dev_dbg(meson->chip.dev, "duty=%u pre_div=%u duty_cnt=%u\n", in meson_pwm_calc()
217 channel->pre_div = pre_div; in meson_pwm_calc()
218 channel->hi = duty_cnt; in meson_pwm_calc()
219 channel->lo = cnt - duty_cnt; in meson_pwm_calc()
225 static void meson_pwm_enable(struct meson_pwm *meson, struct pwm_device *pwm) in meson_pwm_enable() argument
227 struct meson_pwm_channel *channel = pwm_get_chip_data(pwm); in meson_pwm_enable()
232 channel_data = &meson_pwm_per_channel_data[pwm->hwpwm]; in meson_pwm_enable()
234 spin_lock_irqsave(&meson->lock, flags); in meson_pwm_enable()
236 value = readl(meson->base + REG_MISC_AB); in meson_pwm_enable()
237 value &= ~(MISC_CLK_DIV_MASK << channel_data->clk_div_shift); in meson_pwm_enable()
238 value |= channel->pre_div << channel_data->clk_div_shift; in meson_pwm_enable()
239 value |= channel_data->clk_en_mask; in meson_pwm_enable()
240 writel(value, meson->base + REG_MISC_AB); in meson_pwm_enable()
242 value = FIELD_PREP(PWM_HIGH_MASK, channel->hi) | in meson_pwm_enable()
243 FIELD_PREP(PWM_LOW_MASK, channel->lo); in meson_pwm_enable()
244 writel(value, meson->base + channel_data->reg_offset); in meson_pwm_enable()
246 value = readl(meson->base + REG_MISC_AB); in meson_pwm_enable()
247 value |= channel_data->pwm_en_mask; in meson_pwm_enable()
248 writel(value, meson->base + REG_MISC_AB); in meson_pwm_enable()
250 spin_unlock_irqrestore(&meson->lock, flags); in meson_pwm_enable()
253 static void meson_pwm_disable(struct meson_pwm *meson, struct pwm_device *pwm) in meson_pwm_disable() argument
258 spin_lock_irqsave(&meson->lock, flags); in meson_pwm_disable()
260 value = readl(meson->base + REG_MISC_AB); in meson_pwm_disable()
261 value &= ~meson_pwm_per_channel_data[pwm->hwpwm].pwm_en_mask; in meson_pwm_disable()
262 writel(value, meson->base + REG_MISC_AB); in meson_pwm_disable()
264 spin_unlock_irqrestore(&meson->lock, flags); in meson_pwm_disable()
267 static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, in meson_pwm_apply() argument
270 struct meson_pwm_channel *channel = pwm_get_chip_data(pwm); in meson_pwm_apply()
275 return -EINVAL; in meson_pwm_apply()
277 if (!state->enabled) { in meson_pwm_apply()
278 if (state->polarity == PWM_POLARITY_INVERSED) { in meson_pwm_apply()
291 channel->pre_div = 0; in meson_pwm_apply()
292 channel->hi = ~0; in meson_pwm_apply()
293 channel->lo = 0; in meson_pwm_apply()
295 meson_pwm_enable(meson, pwm); in meson_pwm_apply()
297 meson_pwm_disable(meson, pwm); in meson_pwm_apply()
300 err = meson_pwm_calc(meson, pwm, state); in meson_pwm_apply()
304 meson_pwm_enable(meson, pwm); in meson_pwm_apply()
311 struct pwm_device *pwm, u32 cnt) in meson_pwm_cnt_to_ns() argument
319 channel = &meson->channels[pwm->hwpwm]; in meson_pwm_cnt_to_ns()
321 fin_freq = clk_get_rate(channel->clk); in meson_pwm_cnt_to_ns()
327 return cnt * fin_ns * (channel->pre_div + 1); in meson_pwm_cnt_to_ns()
330 static void meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, in meson_pwm_get_state() argument
341 channel = &meson->channels[pwm->hwpwm]; in meson_pwm_get_state()
342 channel_data = &meson_pwm_per_channel_data[pwm->hwpwm]; in meson_pwm_get_state()
344 value = readl(meson->base + REG_MISC_AB); in meson_pwm_get_state()
346 tmp = channel_data->pwm_en_mask | channel_data->clk_en_mask; in meson_pwm_get_state()
347 state->enabled = (value & tmp) == tmp; in meson_pwm_get_state()
349 tmp = value >> channel_data->clk_div_shift; in meson_pwm_get_state()
350 channel->pre_div = FIELD_GET(MISC_CLK_DIV_MASK, tmp); in meson_pwm_get_state()
352 value = readl(meson->base + channel_data->reg_offset); in meson_pwm_get_state()
354 channel->lo = FIELD_GET(PWM_LOW_MASK, value); in meson_pwm_get_state()
355 channel->hi = FIELD_GET(PWM_HIGH_MASK, value); in meson_pwm_get_state()
357 if (channel->lo == 0) { in meson_pwm_get_state()
358 state->period = meson_pwm_cnt_to_ns(chip, pwm, channel->hi); in meson_pwm_get_state()
359 state->duty_cycle = state->period; in meson_pwm_get_state()
360 } else if (channel->lo >= channel->hi) { in meson_pwm_get_state()
361 state->period = meson_pwm_cnt_to_ns(chip, pwm, in meson_pwm_get_state()
362 channel->lo + channel->hi); in meson_pwm_get_state()
363 state->duty_cycle = meson_pwm_cnt_to_ns(chip, pwm, in meson_pwm_get_state()
364 channel->hi); in meson_pwm_get_state()
366 state->period = 0; in meson_pwm_get_state()
367 state->duty_cycle = 0; in meson_pwm_get_state()
457 .compatible = "amlogic,meson8b-pwm",
461 .compatible = "amlogic,meson-gxbb-pwm",
465 .compatible = "amlogic,meson-gxbb-ao-pwm",
469 .compatible = "amlogic,meson-axg-ee-pwm",
473 .compatible = "amlogic,meson-axg-ao-pwm",
477 .compatible = "amlogic,meson-g12a-ee-pwm",
481 .compatible = "amlogic,meson-g12a-ao-pwm-ab",
485 .compatible = "amlogic,meson-g12a-ao-pwm-cd",
494 struct device *dev = meson->chip.dev; in meson_pwm_init_channels()
500 for (i = 0; i < meson->chip.npwm; i++) { in meson_pwm_init_channels()
501 struct meson_pwm_channel *channel = &meson->channels[i]; in meson_pwm_init_channels()
508 init.parent_names = meson->data->parent_names; in meson_pwm_init_channels()
509 init.num_parents = meson->data->num_parents; in meson_pwm_init_channels()
511 channel->mux.reg = meson->base + REG_MISC_AB; in meson_pwm_init_channels()
512 channel->mux.shift = in meson_pwm_init_channels()
514 channel->mux.mask = MISC_CLK_SEL_MASK; in meson_pwm_init_channels()
515 channel->mux.flags = 0; in meson_pwm_init_channels()
516 channel->mux.lock = &meson->lock; in meson_pwm_init_channels()
517 channel->mux.table = NULL; in meson_pwm_init_channels()
518 channel->mux.hw.init = &init; in meson_pwm_init_channels()
520 channel->clk = devm_clk_register(dev, &channel->mux.hw); in meson_pwm_init_channels()
521 if (IS_ERR(channel->clk)) { in meson_pwm_init_channels()
522 err = PTR_ERR(channel->clk); in meson_pwm_init_channels()
529 channel->clk_parent = devm_clk_get_optional(dev, name); in meson_pwm_init_channels()
530 if (IS_ERR(channel->clk_parent)) in meson_pwm_init_channels()
531 return PTR_ERR(channel->clk_parent); in meson_pwm_init_channels()
543 meson = devm_kzalloc(&pdev->dev, sizeof(*meson), GFP_KERNEL); in meson_pwm_probe()
545 return -ENOMEM; in meson_pwm_probe()
548 meson->base = devm_ioremap_resource(&pdev->dev, regs); in meson_pwm_probe()
549 if (IS_ERR(meson->base)) in meson_pwm_probe()
550 return PTR_ERR(meson->base); in meson_pwm_probe()
552 spin_lock_init(&meson->lock); in meson_pwm_probe()
553 meson->chip.dev = &pdev->dev; in meson_pwm_probe()
554 meson->chip.ops = &meson_pwm_ops; in meson_pwm_probe()
555 meson->chip.base = -1; in meson_pwm_probe()
556 meson->chip.npwm = MESON_NUM_PWMS; in meson_pwm_probe()
557 meson->chip.of_xlate = of_pwm_xlate_with_flags; in meson_pwm_probe()
558 meson->chip.of_pwm_n_cells = 3; in meson_pwm_probe()
560 meson->data = of_device_get_match_data(&pdev->dev); in meson_pwm_probe()
566 err = pwmchip_add(&meson->chip); in meson_pwm_probe()
568 dev_err(&pdev->dev, "failed to register PWM chip: %d\n", err); in meson_pwm_probe()
581 return pwmchip_remove(&meson->chip); in meson_pwm_remove()
586 .name = "meson-pwm",
594 MODULE_DESCRIPTION("Amlogic Meson PWM Generator driver");