diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2024-04-29 08:57:58 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2024-04-29 08:57:58 +0200 |
commit | f5fa3c40f63f57e1ca0dae08773098fea6420068 (patch) | |
tree | ded5766062f6077de95a8d04189e57cd84f34d7a | |
parent | 99ff6bb9d9a3ce450e7f5940b2df568744b54778 (diff) | |
parent | a77bd919a9e07bebce02dd1604a57c6c566471f8 (diff) | |
download | barebox-f5fa3c40f63f57e1ca0dae08773098fea6420068.tar.gz barebox-f5fa3c40f63f57e1ca0dae08773098fea6420068.tar.xz |
Merge branch 'for-next/pwm'
-rw-r--r-- | arch/arm/dts/rk356x.dtsi | 19 | ||||
-rw-r--r-- | commands/pwm.c | 32 | ||||
-rw-r--r-- | drivers/led/led-pwm.c | 8 | ||||
-rw-r--r-- | drivers/pwm/Kconfig | 7 | ||||
-rw-r--r-- | drivers/pwm/Makefile | 1 | ||||
-rw-r--r-- | drivers/pwm/core.c | 45 | ||||
-rw-r--r-- | drivers/pwm/pwm-atmel.c | 21 | ||||
-rw-r--r-- | drivers/pwm/pwm-imx.c | 17 | ||||
-rw-r--r-- | drivers/pwm/pwm-mxs.c | 19 | ||||
-rw-r--r-- | drivers/pwm/pwm-rockchip.c | 348 | ||||
-rw-r--r-- | drivers/pwm/pwm-stm32.c | 15 | ||||
-rw-r--r-- | drivers/pwm/pxa_pwm.c | 19 | ||||
-rw-r--r-- | drivers/sound/pwm-beeper.c | 6 | ||||
-rw-r--r-- | include/pwm.h | 39 |
14 files changed, 502 insertions, 94 deletions
diff --git a/arch/arm/dts/rk356x.dtsi b/arch/arm/dts/rk356x.dtsi index 923e18e7cc..60f231e124 100644 --- a/arch/arm/dts/rk356x.dtsi +++ b/arch/arm/dts/rk356x.dtsi @@ -1,6 +1,25 @@ // SPDX-License-Identifier: (GPL-2.0+ OR MIT) / { + aliases { + pwm0 = &pwm0; + pwm1 = &pwm1; + pwm2 = &pwm2; + pwm3 = &pwm3; + pwm4 = &pwm4; + pwm5 = &pwm5; + pwm6 = &pwm6; + pwm7 = &pwm7; + pwm8 = &pwm8; + pwm9 = &pwm9; + pwm10 = &pwm10; + pwm11 = &pwm11; + pwm12 = &pwm12; + pwm13 = &pwm13; + pwm14 = &pwm14; + pwm15 = &pwm15; + }; + chosen { barebox,bootsource-mmc0 = &sdhci; barebox,bootsource-mmc1 = &sdmmc0; diff --git a/commands/pwm.c b/commands/pwm.c index 5d41fa8ff4..b86f865d55 100644 --- a/commands/pwm.c +++ b/commands/pwm.c @@ -15,10 +15,10 @@ static bool is_equal_state(struct pwm_state *state1, struct pwm_state *state2) { - return (state1->period_ns == state2->period_ns) - && (state1->duty_ns == state2->duty_ns) + return (state1->period == state2->period) + && (state1->duty_cycle == state2->duty_cycle) && (state1->polarity == state2->polarity) - && (state1->p_enable == state2->p_enable); + && (state1->enabled == state2->enabled); } static int do_pwm_cmd(int argc, char *argv[]) @@ -109,12 +109,12 @@ static int do_pwm_cmd(int argc, char *argv[]) /* argc will be at least 3 with a valid devname */ if (verbose || (argc <= 3)) { printf("pwm params for '%s':\n", devname); - printf(" period : %u (ns)\n", state.period_ns); - printf(" duty : %u (ns)\n", state.duty_ns); - printf(" enabled : %d\n", state.p_enable); + printf(" period : %u (ns)\n", state.period); + printf(" duty : %u (ns)\n", state.duty_cycle); + printf(" enabled : %d\n", state.enabled); printf(" polarity : %s\n", state.polarity == 0 ? "Normal" : "Inverted"); - if (state.period_ns) - printf(" freq : %lu (Hz)\n", HZ_FROM_NANOSECONDS(state.period_ns)); + if (state.period) + printf(" freq : %lu (Hz)\n", HZ_FROM_NANOSECONDS(state.period)); else printf(" freq : -\n"); @@ -122,7 +122,7 @@ static int do_pwm_cmd(int argc, char *argv[]) return 0; } - if ((state.period_ns == 0) && (freq < 0) && (period < 0)) { + if ((state.period == 0) && (freq < 0) && (period < 0)) { printf(" need to know some timing info: freq or period\n"); pwm_free(pwm); return COMMAND_ERROR; @@ -135,24 +135,24 @@ static int do_pwm_cmd(int argc, char *argv[]) /* period */ if (freq > 0) { - state.p_enable = true; - state.period_ns = HZ_TO_NANOSECONDS(freq); + state.enabled = true; + state.period = HZ_TO_NANOSECONDS(freq); if (use_default_width && (width < 0)) { width = 50; } } else if (period > 0) { - state.p_enable = true; - state.period_ns = period; + state.enabled = true; + state.period = period; } /* duty */ if (width >= 0) { pwm_set_relative_duty_cycle(&state, width, 100); } else if (duty >= 0) { - state.duty_ns = duty; + state.duty_cycle = duty; } - if (state.duty_ns > state.period_ns) { + if (state.duty_cycle > state.period) { printf(" duty_ns must not be greater than period_ns\n"); } @@ -167,7 +167,7 @@ static int do_pwm_cmd(int argc, char *argv[]) * output (eg if duty => 0) and stopping in one command */ if (stop > 0) { - state.p_enable = false; + state.enabled = false; error = pwm_apply_state(pwm, &state); if (error < 0) printf(" error while stopping: %d\n", error); diff --git a/drivers/led/led-pwm.c b/drivers/led/led-pwm.c index 2ffb72e692..6f4abf97c8 100644 --- a/drivers/led/led-pwm.c +++ b/drivers/led/led-pwm.c @@ -27,14 +27,14 @@ static void led_pwm_set(struct led *led, unsigned int brightness) pwm_get_state(pwmled->pwm, &state); - duty = state.period_ns * brightness; + duty = state.period * brightness; do_div(duty, max); if (pwmled->active_low) - duty = state.period_ns - duty; + duty = state.period - duty; - state.p_enable = true; - state.duty_ns = duty; + state.enabled = true; + state.duty_cycle = duty; pwm_apply_state(pwmled->pwm, &state); } diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 0b12278e80..eb04f92c6f 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -42,4 +42,11 @@ config PWM_STM32 help This enables PWM support for STM32 MCUs and MPUs. +config PWM_ROCKCHIP + tristate "Rockchip PWM support" + depends on ARCH_ROCKCHIP || COMPILE_TEST + help + Generic PWM framework driver for the PWM controller found on + Rockchip SoCs. + endif diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 9c3b10ae31..4adc083e6c 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o obj-$(CONFIG_PWM_IMX) += pwm-imx.o obj-$(CONFIG_PWM_MXS) += pwm-mxs.o obj-$(CONFIG_PWM_STM32) += pwm-stm32.o +obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index a3f27708e6..69724e1a5c 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -76,7 +76,7 @@ static int set_enable(struct param_d *p, void *priv) { struct pwm_device *pwm = priv; - if (pwm->params.p_enable) + if (pwm->params.enabled) pwm_enable(pwm); else pwm_disable(pwm); @@ -91,22 +91,25 @@ static int set_enable(struct param_d *p, void *priv) * register a new pwm. pwm->devname must be initialized, usually * from dev_name(dev) from the hardware driver. */ -int pwmchip_add(struct pwm_chip *chip, struct device *dev) +int pwmchip_add(struct pwm_chip *chip) { struct pwm_device *pwm; struct param_d *p; int ret; + if (!chip->devname) + return -EINVAL; + if (_find_pwm(chip->devname)) return -EBUSY; pwm = xzalloc(sizeof(*pwm)); pwm->chip = chip; - pwm->hwdev = dev; + pwm->hwdev = chip->dev; dev_set_name(&pwm->dev, chip->devname); pwm->dev.id = DEVICE_ID_SINGLE; - pwm->dev.parent = dev; + pwm->dev.parent = chip->dev; ret = register_device(&pwm->dev); if (ret) @@ -115,17 +118,17 @@ int pwmchip_add(struct pwm_chip *chip, struct device *dev) list_add_tail(&pwm->node, &pwm_list); p = dev_add_param_uint32(&pwm->dev, "duty_ns", apply_params, - NULL, &pwm->params.duty_ns, "%u", pwm); + NULL, &pwm->params.duty_cycle, "%u", pwm); if (IS_ERR(p)) return PTR_ERR(p); p = dev_add_param_uint32(&pwm->dev, "period_ns", apply_params, - NULL, &pwm->params.period_ns, "%u", pwm); + NULL, &pwm->params.period, "%u", pwm); if (IS_ERR(p)) return PTR_ERR(p); p = dev_add_param_bool(&pwm->dev, "enable", set_enable, - NULL, &pwm->params.p_enable, pwm); + NULL, &pwm->params.enabled, pwm); if (IS_ERR(p)) return PTR_ERR(p); @@ -241,7 +244,7 @@ struct pwm_device *of_pwm_request(struct device_node *np, const char *con_id) pwm->args.polarity = PWM_POLARITY_NORMAL; if (args.args_count > 2 && args.args[2] & PWM_POLARITY_INVERTED) - pwm->args.polarity = PWM_POLARITY_INVERTED; + pwm->args.polarity = PWM_POLARITY_INVERSED; ret = __pwm_request(pwm); if (ret) @@ -306,9 +309,9 @@ void pwm_init_state(const struct pwm_device *pwm, /* Then fill it with the reference config */ pwm_get_args(pwm, &args); - state->period_ns = args.period_ns; + state->period = args.period_ns; state->polarity = args.polarity; - state->duty_ns = 0; + state->duty_cycle = 0; } EXPORT_SYMBOL_GPL(pwm_init_state); @@ -317,13 +320,13 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state) struct pwm_chip *chip = pwm->chip; int ret = -EINVAL; - if (state->period_ns == 0) + if (state->period == 0) goto err; - if (state->duty_ns > state->period_ns) + if (state->duty_cycle > state->period) goto err; - ret = chip->ops->apply(chip, state); + ret = chip->ops->apply(chip, pwm, state); err: if (ret == 0) chip->state = *state; @@ -343,18 +346,18 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) return -EINVAL; pwm_get_state(pwm, &state); - if (state.duty_ns == duty_ns && state.period_ns == period_ns) + if (state.duty_cycle == duty_ns && state.period == period_ns) return 0; - state.duty_ns = duty_ns; - state.period_ns = period_ns; + state.duty_cycle = duty_ns; + state.period = period_ns; return pwm_apply_state(pwm, &state); } EXPORT_SYMBOL_GPL(pwm_config); unsigned int pwm_get_period(struct pwm_device *pwm) { - return pwm->chip->state.period_ns; + return pwm->chip->state.period; } /* @@ -365,10 +368,10 @@ int pwm_enable(struct pwm_device *pwm) struct pwm_state state; pwm_get_state(pwm, &state); - if (state.p_enable) + if (state.enabled) return 0; - state.p_enable = true; + state.enabled = true; return pwm_apply_state(pwm, &state); } EXPORT_SYMBOL_GPL(pwm_enable); @@ -381,10 +384,10 @@ void pwm_disable(struct pwm_device *pwm) struct pwm_state state; pwm_get_state(pwm, &state); - if (!state.p_enable) + if (!state.enabled) return; - state.p_enable = false; + state.enabled = false; pwm_apply_state(pwm, &state); } EXPORT_SYMBOL_GPL(pwm_disable); diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c index d5e70600ee..851676c0dd 100644 --- a/drivers/pwm/pwm-atmel.c +++ b/drivers/pwm/pwm-atmel.c @@ -194,7 +194,7 @@ static int atmel_pwm_calculate_cprd_and_pres(struct pwm_chip *chip, unsigned long *cprd, u32 *pres) { struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip); - unsigned long long cycles = state->period_ns; + unsigned long long cycles = state->period; int shift; /* Calculate the period cycles and prescale value */ @@ -227,7 +227,7 @@ static void atmel_pwm_calculate_cdty(const struct pwm_state *state, unsigned long clkrate, unsigned long cprd, u32 pres, unsigned long *cdty) { - unsigned long long cycles = state->duty_ns; + unsigned long long cycles = state->duty_cycle; cycles *= clkrate; do_div(cycles, NSEC_PER_SEC); @@ -286,7 +286,9 @@ static void atmel_pwm_disable(struct pwm_chip *chip, bool disable_clk) clk_disable(atmel_pwm->clk); } -static int atmel_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state) +static int atmel_pwm_apply(struct pwm_chip *chip, + struct pwm_device *pwm, + const struct pwm_state *state) { struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip); struct pwm_state cstate; @@ -296,12 +298,12 @@ static int atmel_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state) cstate = chip->state; - if (state->p_enable) { + if (state->enabled) { unsigned long clkrate = clk_get_rate(atmel_pwm->clk); - if (cstate.p_enable && + if (cstate.enabled && cstate.polarity == state->polarity && - cstate.period_ns == state->period_ns) { + cstate.period == state->period) { u32 cmr = atmel_pwm_ch_readl(atmel_pwm, chip->id, PWM_CMR); cprd = atmel_pwm_ch_readl(atmel_pwm, chip->id, @@ -323,7 +325,7 @@ static int atmel_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state) atmel_pwm_calculate_cdty(state, clkrate, cprd, pres, &cdty); - if (cstate.p_enable) { + if (cstate.enabled) { atmel_pwm_disable(chip, false); } else { ret = clk_enable(atmel_pwm->clk); @@ -343,7 +345,7 @@ static int atmel_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state) atmel_pwm_ch_writel(atmel_pwm, chip->id, PWM_CMR, val); atmel_pwm_set_cprd_cdty(chip, cprd, cdty); atmel_pwm_writel(atmel_pwm, PWM_ENA, 1 << chip->id); - } else if (cstate.p_enable) { + } else if (cstate.enabled) { atmel_pwm_disable(chip, true); } @@ -453,7 +455,8 @@ static int atmel_pwm_probe(struct device *dev) chip->ops = &atmel_pwm_ops; chip->id = i; - ret = pwmchip_add(chip, dev); + chip->dev = dev; + ret = pwmchip_add(chip); if (ret) { dev_err(dev, "failed to add pwm chip %d\n", ret); return ret; diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index c9db4aef34..2a75400593 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -162,7 +162,7 @@ static int imx_pwm_config_v2(struct pwm_chip *chip, writel(cr, imx->mmio_base + MX3_PWMCR); - if (!chip->state.p_enable) + if (!chip->state.enabled) imx_pwm_clk_disable_v2(imx); return 0; @@ -191,24 +191,26 @@ static void imx_pwm_set_enable_v2(struct pwm_chip *chip, bool enable) imx_pwm_clk_disable_v2(imx); } -static int imx_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state) +static int imx_pwm_apply(struct pwm_chip *chip, + struct pwm_device *pwm, + const struct pwm_state *state) { struct imx_chip *imx = to_imx_chip(chip); bool enabled; int ret; - enabled = chip->state.p_enable; + enabled = chip->state.enabled; - if (enabled && !state->p_enable) { + if (enabled && !state->enabled) { imx->set_enable(chip, false); return 0; } - ret = imx->config(chip, state->duty_ns, state->period_ns); + ret = imx->config(chip, state->duty_cycle, state->period); if (ret) return ret; - if (!enabled && state->p_enable) + if (!enabled && state->enabled) imx->set_enable(chip, true); return 0; @@ -267,6 +269,7 @@ static int imx_pwm_probe(struct device *dev) return PTR_ERR(iores); imx->mmio_base = IOMEM(iores->start); + imx->chip.dev = dev; imx->chip.ops = &imx_pwm_ops; if (dev->of_node) { imx->chip.devname = of_alias_get(dev->of_node); @@ -280,7 +283,7 @@ static int imx_pwm_probe(struct device *dev) imx->config = data->config; imx->set_enable = data->set_enable; - return pwmchip_add(&imx->chip, dev);; + return pwmchip_add(&imx->chip); } static struct driver imx_pwm_driver = { diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c index 6b89ac192a..3e3efade68 100644 --- a/drivers/pwm/pwm-mxs.c +++ b/drivers/pwm/pwm-mxs.c @@ -44,7 +44,9 @@ struct mxs_pwm { #define to_mxs_pwm_chip(_chip) container_of(_chip, struct mxs_pwm_chip, chip) -static int mxs_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state) +static int mxs_pwm_apply(struct pwm_chip *chip, + struct pwm_device *pwm, + const struct pwm_state *state) { struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip); int div = 0; @@ -53,9 +55,9 @@ static int mxs_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state) unsigned long long c; bool enabled; - enabled = chip->state.p_enable; + enabled = chip->state.enabled; - if (enabled && !state->p_enable) { + if (enabled && !state->enabled) { writel(1 << mxs->chip.id, mxs->mxs->base + PWM_CTRL + CLR); return 0; } @@ -63,7 +65,7 @@ static int mxs_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state) rate = clk_get_rate(mxs->mxs->clk); while (1) { c = rate / cdiv[div]; - c = c * state->period_ns; + c = c * state->period; do_div(c, 1000000000); if (c < PERIOD_PERIOD_MAX) break; @@ -73,8 +75,8 @@ static int mxs_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state) } period_cycles = c; - c *= state->duty_ns; - do_div(c, state->period_ns); + c *= state->duty_cycle; + do_div(c, state->period); duty_cycles = c; writel(duty_cycles << 16, @@ -83,7 +85,7 @@ static int mxs_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state) PERIOD_INACTIVE_LOW | PERIOD_CDIV(div), mxs->mxs->base + PWM_PERIOD0 + mxs->chip.id * 0x20); - if (!enabled && state->p_enable) + if (!enabled && state->enabled) writel(1 << mxs->chip.id, mxs->mxs->base + PWM_CTRL + SET); return 0; @@ -130,9 +132,10 @@ static int mxs_pwm_probe(struct device *dev) mxspwm->chip.ops = &mxs_pwm_ops; mxspwm->chip.devname = basprintf("pwm%d", i); mxspwm->chip.id = i; + mxspwm->chip.dev = dev; mxspwm->mxs = mxs; - ret = pwmchip_add(&mxspwm->chip, dev); + ret = pwmchip_add(&mxspwm->chip); if (ret < 0) { dev_err(dev, "failed to add pwm chip %d\n", ret); return ret; diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c new file mode 100644 index 0000000000..960867c520 --- /dev/null +++ b/drivers/pwm/pwm-rockchip.c @@ -0,0 +1,348 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * PWM driver for Rockchip SoCs + * + * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com> + * Copyright (C) 2014 ROCKCHIP, Inc. + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <module.h> +#include <of.h> +#include <linux/device.h> +#include <pwm.h> +#include <linux/time.h> + +#define PWM_CTRL_TIMER_EN (1 << 0) +#define PWM_CTRL_OUTPUT_EN (1 << 3) + +#define PWM_ENABLE (1 << 0) +#define PWM_CONTINUOUS (1 << 1) +#define PWM_DUTY_POSITIVE (1 << 3) +#define PWM_DUTY_NEGATIVE (0 << 3) +#define PWM_INACTIVE_NEGATIVE (0 << 4) +#define PWM_INACTIVE_POSITIVE (1 << 4) +#define PWM_POLARITY_MASK (PWM_DUTY_POSITIVE | PWM_INACTIVE_POSITIVE) +#define PWM_OUTPUT_LEFT (0 << 5) +#define PWM_LOCK_EN (1 << 6) +#define PWM_LP_DISABLE (0 << 8) + +struct rockchip_pwm_chip { + struct pwm_chip chip; + struct clk *clk; + struct clk *pclk; + const struct rockchip_pwm_data *data; + void __iomem *base; +}; + +struct rockchip_pwm_regs { + unsigned long duty; + unsigned long period; + unsigned long cntr; + unsigned long ctrl; +}; + +struct rockchip_pwm_data { + struct rockchip_pwm_regs regs; + unsigned int prescaler; + bool supports_polarity; + bool supports_lock; + u32 enable_conf; +}; + +static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct rockchip_pwm_chip, chip); +} + +static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + unsigned long period, duty; + u64 clk_rate, div; + u32 ctrl; + + clk_rate = clk_get_rate(pc->clk); + + /* + * Since period and duty cycle registers have a width of 32 + * bits, every possible input period can be obtained using the + * default prescaler value for all practical clock rate values. + */ + div = clk_rate * state->period; + period = DIV_ROUND_CLOSEST_ULL(div, + pc->data->prescaler * NSEC_PER_SEC); + + div = clk_rate * state->duty_cycle; + duty = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC); + + /* + * Lock the period and duty of previous configuration, then + * change the duty and period, that would not be effective. + */ + ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl); + if (pc->data->supports_lock) { + ctrl |= PWM_LOCK_EN; + writel_relaxed(ctrl, pc->base + pc->data->regs.ctrl); + } + + writel(period, pc->base + pc->data->regs.period); + writel(duty, pc->base + pc->data->regs.duty); + + if (pc->data->supports_polarity) { + ctrl &= ~PWM_POLARITY_MASK; + if (state->polarity == PWM_POLARITY_INVERSED) + ctrl |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE; + else + ctrl |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE; + } + + /* + * Unlock and set polarity at the same time, + * the configuration of duty, period and polarity + * would be effective together at next period. + */ + if (pc->data->supports_lock) + ctrl &= ~PWM_LOCK_EN; + + writel(ctrl, pc->base + pc->data->regs.ctrl); +} + +static int rockchip_pwm_enable(struct pwm_chip *chip, + struct pwm_device *pwm, + bool enable) +{ + struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + u32 enable_conf = pc->data->enable_conf; + int ret; + u32 val; + + if (enable) { + ret = clk_enable(pc->clk); + if (ret) + return ret; + } + + val = readl_relaxed(pc->base + pc->data->regs.ctrl); + + if (enable) + val |= enable_conf; + else + val &= ~enable_conf; + + writel_relaxed(val, pc->base + pc->data->regs.ctrl); + + if (!enable) + clk_disable(pc->clk); + + return 0; +} + +static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + struct pwm_state curstate; + bool enabled; + int ret = 0; + + ret = clk_enable(pc->pclk); + if (ret) + return ret; + + ret = clk_enable(pc->clk); + if (ret) + return ret; + + pwm_get_state(pwm, &curstate); + enabled = curstate.enabled; + + if (state->polarity != curstate.polarity && enabled && + !pc->data->supports_lock) { + ret = rockchip_pwm_enable(chip, pwm, false); + if (ret) + goto out; + enabled = false; + } + + rockchip_pwm_config(chip, pwm, state); + if (state->enabled != enabled) { + ret = rockchip_pwm_enable(chip, pwm, state->enabled); + if (ret) + goto out; + } + +out: + clk_disable(pc->clk); + clk_disable(pc->pclk); + + return ret; +} + +static const struct pwm_ops rockchip_pwm_ops = { + .apply = rockchip_pwm_apply, +}; + +static const struct rockchip_pwm_data pwm_data_v1 = { + .regs = { + .duty = 0x04, + .period = 0x08, + .cntr = 0x00, + .ctrl = 0x0c, + }, + .prescaler = 2, + .supports_polarity = false, + .supports_lock = false, + .enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN, +}; + +static const struct rockchip_pwm_data pwm_data_v2 = { + .regs = { + .duty = 0x08, + .period = 0x04, + .cntr = 0x00, + .ctrl = 0x0c, + }, + .prescaler = 1, + .supports_polarity = true, + .supports_lock = false, + .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | + PWM_CONTINUOUS, +}; + +static const struct rockchip_pwm_data pwm_data_vop = { + .regs = { + .duty = 0x08, + .period = 0x04, + .cntr = 0x0c, + .ctrl = 0x00, + }, + .prescaler = 1, + .supports_polarity = true, + .supports_lock = false, + .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | + PWM_CONTINUOUS, +}; + +static const struct rockchip_pwm_data pwm_data_v3 = { + .regs = { + .duty = 0x08, + .period = 0x04, + .cntr = 0x00, + .ctrl = 0x0c, + }, + .prescaler = 1, + .supports_polarity = true, + .supports_lock = true, + .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | + PWM_CONTINUOUS, +}; + +static const struct of_device_id rockchip_pwm_dt_ids[] = { + { .compatible = "rockchip,rk2928-pwm", .data = &pwm_data_v1}, + { .compatible = "rockchip,rk3288-pwm", .data = &pwm_data_v2}, + { .compatible = "rockchip,vop-pwm", .data = &pwm_data_vop}, + { .compatible = "rockchip,rk3328-pwm", .data = &pwm_data_v3}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rockchip_pwm_dt_ids); + +static int rockchip_pwm_probe(struct device *dev) +{ + struct rockchip_pwm_chip *pc; + u32 enable_conf, ctrl; + bool enabled; + int ret, count; + + pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL); + if (!pc) + return -ENOMEM; + + pc->base = dev_platform_ioremap_resource(dev, 0); + if (IS_ERR(pc->base)) + return PTR_ERR(pc->base); + + pc->clk = clk_get(dev, "pwm"); + if (IS_ERR(pc->clk)) { + pc->clk = clk_get(dev, NULL); + if (IS_ERR(pc->clk)) + return dev_err_probe(dev, PTR_ERR(pc->clk), + "Can't get PWM clk\n"); + } + + count = of_count_phandle_with_args(dev->of_node, + "clocks", "#clock-cells"); + if (count == 2) + pc->pclk = clk_get(dev, "pclk"); + else + pc->pclk = pc->clk; + + if (IS_ERR(pc->pclk)) + return dev_err_probe(dev, PTR_ERR(pc->pclk), "Can't get APB clk\n"); + + ret = clk_prepare_enable(pc->clk); + if (ret) + return dev_err_probe(dev, ret, "Can't prepare enable PWM clk\n"); + + ret = clk_prepare_enable(pc->pclk); + if (ret) { + dev_err_probe(dev, ret, "Can't prepare enable APB clk\n"); + goto err_clk; + } + + dev->priv = pc; + + pc->data = device_get_match_data(dev); + pc->chip.dev = dev; + pc->chip.ops = &rockchip_pwm_ops; + + enable_conf = pc->data->enable_conf; + ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl); + enabled = (ctrl & enable_conf) == enable_conf; + + pc->chip.devname = of_alias_get(dev->of_node); + if (!pc->chip.devname) + pc->chip.devname = basprintf("pwm_%p", pc->base); + + ret = pwmchip_add(&pc->chip); + if (ret < 0) { + dev_err_probe(dev, ret, "pwmchip_add() failed\n"); + goto err_pclk; + } + + /* Keep the PWM clk enabled if the PWM appears to be up and running. */ + if (!enabled) + clk_disable(pc->clk); + + clk_disable(pc->pclk); + + return 0; + +err_pclk: + clk_disable_unprepare(pc->pclk); +err_clk: + clk_disable_unprepare(pc->clk); + + return ret; +} + +static void rockchip_pwm_remove(struct device *dev) +{ + struct rockchip_pwm_chip *pc = dev->priv; + + pwmchip_remove(&pc->chip); +} + +static struct driver rockchip_pwm_driver = { + .name = "rockchip-pwm", + .of_match_table = rockchip_pwm_dt_ids, + .probe = rockchip_pwm_probe, + .remove = rockchip_pwm_remove, +}; +device_platform_driver(rockchip_pwm_driver); + +MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>"); +MODULE_DESCRIPTION("Rockchip SoC PWM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index 5c2029ab6a..16f80381c2 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -198,15 +198,17 @@ static void stm32_pwm_disable(struct stm32_pwm *priv, unsigned ch) clk_disable(priv->clk); } -static int stm32_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state) +static int stm32_pwm_apply(struct pwm_chip *chip, + struct pwm_device *pwm, + const struct pwm_state *state) { bool enabled; struct stm32_pwm *priv = to_stm32_pwm_dev(chip); int ret; - enabled = chip->state.p_enable; + enabled = chip->state.enabled; - if (enabled && !state->p_enable) { + if (enabled && !state->enabled) { stm32_pwm_disable(priv, chip->id); return 0; } @@ -215,11 +217,11 @@ static int stm32_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state) stm32_pwm_set_polarity(priv, chip->id, state->polarity); ret = stm32_pwm_config(priv, chip->id, - state->duty_ns, state->period_ns); + state->duty_cycle, state->period); if (ret) return ret; - if (!enabled && state->p_enable) + if (!enabled && state->enabled) ret = stm32_pwm_enable(priv, chip->id); return ret; @@ -377,8 +379,9 @@ static int stm32_pwm_probe(struct device *dev) chip->ops = &stm32pwm_ops; chip->id = i; + chip->dev = dev; - ret = pwmchip_add(chip, dev); + ret = pwmchip_add(chip); if (ret < 0) { dev_err(dev, "failed to add pwm chip %d\n", ret); return ret; diff --git a/drivers/pwm/pxa_pwm.c b/drivers/pwm/pxa_pwm.c index 0ed69d999f..42ccb37b9a 100644 --- a/drivers/pwm/pxa_pwm.c +++ b/drivers/pwm/pxa_pwm.c @@ -77,22 +77,24 @@ static void pxa_pwm_disable(struct pxa_pwm_chip *pxa_pwm) * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE * PWM_CLK_RATE = 13 MHz */ -static int pxa_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state) +static int pxa_pwm_apply(struct pwm_chip *chip, + struct pwm_device *pwm, + const struct pwm_state *state) { unsigned long long c; unsigned long period_cycles, prescale, pv, dc; struct pxa_pwm_chip *pxa_pwm = to_pxa_pwm_chip(chip); bool enabled; - enabled = chip->state.p_enable; + enabled = chip->state.enabled; - if (enabled && !state->p_enable) { + if (enabled && !state->enabled) { pxa_pwm_disable(pxa_pwm); return 0; } c = pxa_get_pwmclk(); - c = c * state->period_ns; + c = c * state->period; do_div(c, 1000000000); period_cycles = c; @@ -104,10 +106,10 @@ static int pxa_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state) if (prescale > 63) return -EINVAL; - if (state->duty_ns == state->period_ns) + if (state->duty_cycle == state->period) dc = PWMDCR_FD; else - dc = (pv + 1) * state->duty_ns / state->period_ns; + dc = (pv + 1) * state->duty_cycle / state->period; /* NOTE: the clock to PWM has to be enabled first * before writing to the registers @@ -116,7 +118,7 @@ static int pxa_pwm_apply(struct pwm_chip *chip, const struct pwm_state *state) writel(dc, pxa_pwm->iobase + PWMDCR); writel(pv, pxa_pwm->iobase + PWMPCR); - if (!enabled && state->p_enable) { + if (!enabled && state->enabled) { pxa_pwm_enable(pxa_pwm); return 0; } @@ -141,9 +143,10 @@ static int pxa_pwm_probe(struct device *dev) return PTR_ERR(iores); chip->iobase = IOMEM(iores->start); chip->id = dev->id; + chip->chip.dev = dev; dev->priv = chip; - return pwmchip_add(&chip->chip, dev); + return pwmchip_add(&chip->chip); } static struct driver pxa_pwm_driver = { diff --git a/drivers/sound/pwm-beeper.c b/drivers/sound/pwm-beeper.c index 21e57d4b07..94b27359c1 100644 --- a/drivers/sound/pwm-beeper.c +++ b/drivers/sound/pwm-beeper.c @@ -31,8 +31,8 @@ static int pwm_beeper_beep(struct sound_card *card, unsigned freq, unsigned dura pwm_get_state(beeper->pwm, &state); - state.p_enable = true; - state.period_ns = HZ_TO_NANOSECONDS(freq); + state.enabled = true; + state.period = HZ_TO_NANOSECONDS(freq); pwm_set_relative_duty_cycle(&state, 50, 100); error = pwm_apply_state(beeper->pwm, &state); @@ -66,7 +66,7 @@ static int pwm_beeper_probe(struct device *dev) /* Sync up PWM state and ensure it is off. */ pwm_init_state(beeper->pwm, &state); - state.p_enable = false; + state.enabled = false; error = pwm_apply_state(beeper->pwm, &state); if (error) { dev_err(dev, "failed to apply initial PWM state: %d\n", diff --git a/include/pwm.h b/include/pwm.h index 4d403fe174..b90ac1de42 100644 --- a/include/pwm.h +++ b/include/pwm.h @@ -8,20 +8,32 @@ struct pwm_device; struct device; -#define PWM_POLARITY_NORMAL 0 +/** + * enum pwm_polarity - polarity of a PWM signal + * @PWM_POLARITY_NORMAL: a high signal for the duration of the duty- + * cycle, followed by a low signal for the remainder of the pulse + * period + * @PWM_POLARITY_INVERSED: a low signal for the duration of the duty- + * cycle, followed by a high signal for the remainder of the pulse + * period + */ +enum pwm_polarity { + PWM_POLARITY_NORMAL, + PWM_POLARITY_INVERSED, +}; /* * struct pwm_state - state of a PWM channel - * @period_ns: PWM period (in nanoseconds) - * @duty_ns: PWM duty cycle (in nanoseconds) + * @period: PWM period (in nanoseconds) + * @duty_cycle: PWM duty cycle (in nanoseconds) * @polarity: PWM polarity - * @p_enable: PWM enabled status + * @enabled: PWM enabled status */ struct pwm_state { - unsigned int period_ns; - unsigned int duty_ns; + unsigned int period; + unsigned int duty_cycle; unsigned int polarity; - unsigned int p_enable; + unsigned int enabled; }; void pwm_print(void); @@ -91,9 +103,9 @@ pwm_set_relative_duty_cycle(struct pwm_state *state, unsigned int duty_cycle, if (!scale || duty_cycle > scale) return -EINVAL; - state->duty_ns = DIV_ROUND_CLOSEST_ULL((u64)duty_cycle * - state->period_ns, - scale); + state->duty_cycle = DIV_ROUND_CLOSEST_ULL((u64)duty_cycle * + state->period, + scale); return 0; } @@ -123,17 +135,20 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state); struct pwm_ops { int (*request)(struct pwm_chip *chip); void (*free)(struct pwm_chip *chip); - int (*apply)(struct pwm_chip *chip, const struct pwm_state *state); + int (*apply)(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state); }; /** * struct pwm_chip - abstract a PWM + * @dev: device providing the PWMs * @id: The id of this pwm * @devname: unique identifier for this pwm * @ops: The callbacks for this PWM * @state: current state of the PWM */ struct pwm_chip { + struct device *dev; int id; const char *devname; const struct pwm_ops *ops; @@ -141,7 +156,7 @@ struct pwm_chip { struct pwm_state state; }; -int pwmchip_add(struct pwm_chip *chip, struct device *dev); +int pwmchip_add(struct pwm_chip *chip); int pwmchip_remove(struct pwm_chip *chip); #endif /* __PWM_H */ |