summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2024-04-29 08:57:58 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2024-04-29 08:57:58 +0200
commitf5fa3c40f63f57e1ca0dae08773098fea6420068 (patch)
treeded5766062f6077de95a8d04189e57cd84f34d7a
parent99ff6bb9d9a3ce450e7f5940b2df568744b54778 (diff)
parenta77bd919a9e07bebce02dd1604a57c6c566471f8 (diff)
downloadbarebox-f5fa3c40f63f57e1ca0dae08773098fea6420068.tar.gz
barebox-f5fa3c40f63f57e1ca0dae08773098fea6420068.tar.xz
Merge branch 'for-next/pwm'
-rw-r--r--arch/arm/dts/rk356x.dtsi19
-rw-r--r--commands/pwm.c32
-rw-r--r--drivers/led/led-pwm.c8
-rw-r--r--drivers/pwm/Kconfig7
-rw-r--r--drivers/pwm/Makefile1
-rw-r--r--drivers/pwm/core.c45
-rw-r--r--drivers/pwm/pwm-atmel.c21
-rw-r--r--drivers/pwm/pwm-imx.c17
-rw-r--r--drivers/pwm/pwm-mxs.c19
-rw-r--r--drivers/pwm/pwm-rockchip.c348
-rw-r--r--drivers/pwm/pwm-stm32.c15
-rw-r--r--drivers/pwm/pxa_pwm.c19
-rw-r--r--drivers/sound/pwm-beeper.c6
-rw-r--r--include/pwm.h39
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 */