summaryrefslogtreecommitdiffstats
path: root/drivers/pwm
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pwm')
-rw-r--r--drivers/pwm/Kconfig10
-rw-r--r--drivers/pwm/Makefile2
-rw-r--r--drivers/pwm/core.c60
-rw-r--r--drivers/pwm/pwm-atmel.c398
-rw-r--r--drivers/pwm/pwm-imx.c80
-rw-r--r--drivers/pwm/pwm-mxs.c26
-rw-r--r--drivers/pwm/pwm-rockchip.c348
-rw-r--r--drivers/pwm/pwm-stm32.c27
-rw-r--r--drivers/pwm/pxa_pwm.c31
9 files changed, 797 insertions, 185 deletions
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index a6e141817b..eb04f92c6f 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
menuconfig PWM
bool "PWM Support"
help
@@ -37,8 +38,15 @@ config PWM_MXS
config PWM_STM32
bool "STM32 PWM Support"
- depends on ARCH_STM32MP || COMPILE_TEST
+ depends on ARCH_STM32 || COMPILE_TEST
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 55558aa42e..4adc083e6c 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -1,6 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_PWM) += core.o
obj-$(CONFIG_PWM_PXA) += pxa_pwm.o
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 aba6c2a709..69724e1a5c 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -35,8 +35,8 @@ struct pwm_device {
unsigned long flags;
#define FLAG_REQUESTED 0
struct list_head node;
- struct device_d *hwdev;
- struct device_d dev;
+ struct device *hwdev;
+ struct device dev;
struct pwm_state params;
struct pwm_args args;
@@ -44,6 +44,15 @@ struct pwm_device {
static LIST_HEAD(pwm_list);
+void pwm_print(void)
+{
+ struct pwm_device *pwm;
+
+ list_for_each_entry(pwm, &pwm_list, node)
+ printf("%s\n", pwm->chip->devname);
+}
+EXPORT_SYMBOL(pwm_print);
+
static struct pwm_device *_find_pwm(const char *devname)
{
struct pwm_device *pwm;
@@ -67,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);
@@ -82,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_d *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)
@@ -106,17 +118,17 @@ int pwmchip_add(struct pwm_chip *chip, struct device_d *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);
@@ -194,7 +206,7 @@ static struct pwm_device *of_node_to_pwm_device(struct device_node *np, int id)
struct pwm_device *pwm;
list_for_each_entry(pwm, &pwm_list, node) {
- if (pwm->hwdev && pwm->hwdev->device_node == np &&
+ if (pwm->hwdev && pwm->hwdev->of_node == np &&
pwm->chip->id == id)
return pwm;
}
@@ -232,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)
@@ -297,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);
@@ -308,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;
@@ -334,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;
}
/*
@@ -356,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);
@@ -372,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 7fa394934a..851676c0dd 100644
--- a/drivers/pwm/pwm-atmel.c
+++ b/drivers/pwm/pwm-atmel.c
@@ -1,18 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for Atmel Pulse Width Modulation Controller
*
* Copyright (C) 2013 Atmel Corporation
* Bo Shen <voice.shen@atmel.com>
* Copyright (C) 2018 Sam Ravnborg <sam@ravnborg.org>
- *
- * Licensed under GPLv2.
*/
#include <of_device.h>
#include <common.h>
#include <driver.h>
#include <module.h>
-#include <printk.h>
+#include <linux/printk.h>
#include <stdio.h>
#include <init.h>
#include <pwm.h>
@@ -56,15 +55,7 @@
#define PWMV2_CPRD 0x0C
#define PWMV2_CPRDUPD 0x10
-/*
- * Max value for duty and period
- *
- * Although the duty and period register is 32 bit,
- * however only the LSB 16 bits are significant.
- */
-#define PWM_MAX_DTY 0xFFFF
-#define PWM_MAX_PRD 0xFFFF
-#define PRD_MAX_PRES 10
+#define PWM_MAX_PRES 10
struct atmel_pwm_registers {
u8 period;
@@ -73,30 +64,53 @@ struct atmel_pwm_registers {
u8 duty_upd;
};
-struct atmel_pwm;
+struct atmel_pwm_config {
+ u32 period_bits;
+};
-struct atmel_pwm_chip {
- struct pwm_chip chip;
- struct atmel_pwm *atmel;
+struct atmel_pwm_data {
+ struct atmel_pwm_registers regs;
+ struct atmel_pwm_config cfg;
};
-struct atmel_pwm {
- struct atmel_pwm_chip atmel_pwm_chip[PWM_CHANNELS];
- const struct atmel_pwm_registers *regs;
+struct atmel_pwm_chip {
+ struct pwm_chip chips[PWM_CHANNELS];
struct clk *clk;
void __iomem *base;
- struct device_d *dev;
+ struct device *dev;
+ const struct atmel_pwm_data *data;
+
+ /*
+ * The hardware supports a mechanism to update a channel's duty cycle at
+ * the end of the currently running period. When such an update is
+ * pending we delay disabling the PWM until the new configuration is
+ * active because otherwise pmw_config(duty_cycle=0); pwm_disable();
+ * might not result in an inactive output.
+ * This bitmask tracks for which channels an update is pending in
+ * hardware.
+ */
+ u32 update_pending;
+
+ /* Protects .update_pending */
+ spinlock_t lock;
};
static inline struct atmel_pwm_chip *to_atmel_pwm_chip(struct pwm_chip *chip)
{
- return container_of(chip, struct atmel_pwm_chip, chip);
+ struct pwm_chip (*chips)[4] = (void *)&chip[-chip->id];
+ return container_of(chips, struct atmel_pwm_chip, chips);
+}
+
+static inline u32 atmel_pwm_readl(struct atmel_pwm_chip *chip,
+ unsigned long offset)
+{
+ return readl_relaxed(chip->base + offset);
}
static inline void atmel_pwm_writel(struct atmel_pwm_chip *chip,
unsigned long offset, unsigned long val)
{
- writel(val, chip->atmel->base + offset);
+ writel_relaxed(val, chip->base + offset);
}
static inline u32 atmel_pwm_ch_readl(struct atmel_pwm_chip *chip,
@@ -104,7 +118,7 @@ static inline u32 atmel_pwm_ch_readl(struct atmel_pwm_chip *chip,
{
unsigned long base = PWM_CH_REG_OFFSET + ch * PWM_CH_REG_SIZE;
- return readl(chip->atmel->base + base + offset);
+ return atmel_pwm_readl(chip, base + offset);
}
static inline void atmel_pwm_ch_writel(struct atmel_pwm_chip *chip,
@@ -113,24 +127,95 @@ static inline void atmel_pwm_ch_writel(struct atmel_pwm_chip *chip,
{
unsigned long base = PWM_CH_REG_OFFSET + ch * PWM_CH_REG_SIZE;
- writel(val, chip->atmel->base + base + offset);
+ atmel_pwm_writel(chip, base + offset, val);
}
-static int atmel_pwm_calculate_cprd_and_pres(struct atmel_pwm_chip *atmel_pwm,
- int period,
+static void atmel_pwm_update_pending(struct atmel_pwm_chip *chip)
+{
+ /*
+ * Each channel that has its bit in ISR set started a new period since
+ * ISR was cleared and so there is no more update pending. Note that
+ * reading ISR clears it, so this needs to handle all channels to not
+ * loose information.
+ */
+ u32 isr = atmel_pwm_readl(chip, PWM_ISR);
+
+ chip->update_pending &= ~isr;
+}
+
+static void atmel_pwm_set_pending(struct atmel_pwm_chip *chip, unsigned int ch)
+{
+ spin_lock(&chip->lock);
+
+ /*
+ * Clear pending flags in hardware because otherwise there might still
+ * be a stale flag in ISR.
+ */
+ atmel_pwm_update_pending(chip);
+
+ chip->update_pending |= (1 << ch);
+
+ spin_unlock(&chip->lock);
+}
+
+static int atmel_pwm_test_pending(struct atmel_pwm_chip *chip, unsigned int ch)
+{
+ int ret = 0;
+
+ spin_lock(&chip->lock);
+
+ if (chip->update_pending & (1 << ch)) {
+ atmel_pwm_update_pending(chip);
+
+ if (chip->update_pending & (1 << ch))
+ ret = 1;
+ }
+
+ spin_unlock(&chip->lock);
+
+ return ret;
+}
+
+static int atmel_pwm_wait_nonpending(struct atmel_pwm_chip *chip, unsigned int ch)
+{
+ unsigned long timeout = get_time_ns() + 2 * HZ;
+ int ret;
+
+ while ((ret = atmel_pwm_test_pending(chip, ch)) &&
+ !is_timeout(get_time_ns(), timeout))
+ udelay(100);
+
+ return ret ? -ETIMEDOUT : 0;
+}
+
+static int atmel_pwm_calculate_cprd_and_pres(struct pwm_chip *chip,
+ unsigned long clkrate,
+ const struct pwm_state *state,
unsigned long *cprd, u32 *pres)
{
- unsigned long long cycles = period;
+ struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
+ unsigned long long cycles = state->period;
+ int shift;
+
/* Calculate the period cycles and prescale value */
- cycles *= clk_get_rate(atmel_pwm->atmel->clk);
+ cycles *= clkrate;
do_div(cycles, NSEC_PER_SEC);
- for (*pres = 0; cycles > PWM_MAX_PRD; cycles >>= 1)
- (*pres)++;
+ /*
+ * The register for the period length is cfg.period_bits bits wide.
+ * So for each bit the number of clock cycles is wider divide the input
+ * clock frequency by two using pres and shift cprd accordingly.
+ */
+ shift = fls(cycles) - atmel_pwm->data->cfg.period_bits;
- if (*pres > PRD_MAX_PRES) {
- dev_err(atmel_pwm->atmel->dev, "pres exceeds the maximum value\n");
+ if (shift > PWM_MAX_PRES) {
+ dev_err(atmel_pwm->dev, "pres exceeds the maximum value\n");
return -EINVAL;
+ } else if (shift > 0) {
+ *pres = shift;
+ cycles >>= *pres;
+ } else {
+ *pres = 0;
}
*cprd = cycles;
@@ -138,137 +223,242 @@ static int atmel_pwm_calculate_cprd_and_pres(struct atmel_pwm_chip *atmel_pwm,
return 0;
}
-static void atmel_pwm_calculate_cdty(int duty, int period,
- unsigned long cprd, unsigned long *cdty)
+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 = duty;
+ unsigned long long cycles = state->duty_cycle;
- cycles *= cprd;
- do_div(cycles, period);
+ cycles *= clkrate;
+ do_div(cycles, NSEC_PER_SEC);
+ cycles >>= pres;
*cdty = cprd - cycles;
}
-static void atmel_pwm_set_cprd_cdty(struct atmel_pwm_chip *atmel_pwm, int ch,
+static void atmel_pwm_update_cdty(struct pwm_chip *chip, unsigned long cdty)
+{
+ struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
+ u32 val;
+
+ if (atmel_pwm->data->regs.duty_upd ==
+ atmel_pwm->data->regs.period_upd) {
+ val = atmel_pwm_ch_readl(atmel_pwm, chip->id, PWM_CMR);
+ val &= ~PWM_CMR_UPD_CDTY;
+ atmel_pwm_ch_writel(atmel_pwm, chip->id, PWM_CMR, val);
+ }
+
+ atmel_pwm_ch_writel(atmel_pwm, chip->id,
+ atmel_pwm->data->regs.duty_upd, cdty);
+ atmel_pwm_set_pending(atmel_pwm, chip->id);
+}
+
+static void atmel_pwm_set_cprd_cdty(struct pwm_chip *chip,
unsigned long cprd, unsigned long cdty)
{
- const struct atmel_pwm_registers *regs = atmel_pwm->atmel->regs;
+ struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
- atmel_pwm_ch_writel(atmel_pwm, ch, regs->duty, cdty);
- atmel_pwm_ch_writel(atmel_pwm, ch, regs->period, cprd);
+ atmel_pwm_ch_writel(atmel_pwm, chip->id,
+ atmel_pwm->data->regs.duty, cdty);
+ atmel_pwm_ch_writel(atmel_pwm, chip->id,
+ atmel_pwm->data->regs.period, cprd);
}
-static int atmel_pwm_config(struct pwm_chip *chip, int duty_ns, int period_ns)
+static void atmel_pwm_disable(struct pwm_chip *chip, bool disable_clk)
{
struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
- unsigned long cprd, cdty;
- u32 pres, val;
- int ret;
- int ch;
+ unsigned long timeout;
- ch = atmel_pwm->chip.id;
- ret = atmel_pwm_calculate_cprd_and_pres(atmel_pwm, period_ns, &cprd, &pres);
- if (ret)
- return ret;
+ atmel_pwm_wait_nonpending(atmel_pwm, chip->id);
- atmel_pwm_calculate_cdty(duty_ns, period_ns, cprd, &cdty);
+ atmel_pwm_writel(atmel_pwm, PWM_DIS, 1 << chip->id);
- /* It is necessary to preserve CPOL, inside CMR */
- val = atmel_pwm_ch_readl(atmel_pwm, ch, PWM_CMR);
- val = (val & ~PWM_CMR_CPRE_MSK) | (pres & PWM_CMR_CPRE_MSK);
- /* Assuming normal polarity */
- val &= ~PWM_CMR_CPOL;
+ /*
+ * Wait for the PWM channel disable operation to be effective before
+ * stopping the clock.
+ */
+ timeout = get_time_ns() + 2 * HZ;
- atmel_pwm_ch_writel(atmel_pwm, ch, PWM_CMR, val);
- atmel_pwm_set_cprd_cdty(atmel_pwm, ch, cprd, cdty);
+ while ((atmel_pwm_readl(atmel_pwm, PWM_SR) & (1 << chip->id)) &&
+ !is_timeout(get_time_ns(), timeout))
+ udelay(100);
- return 0;
+ if (disable_clk)
+ clk_disable(atmel_pwm->clk);
}
-static int atmel_pwm_enable(struct pwm_chip *chip)
+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;
+ unsigned long cprd, cdty;
+ u32 pres, val;
+ int ret;
- atmel_pwm_writel(atmel_pwm, PWM_ENA, 1 << atmel_pwm->chip.id);
- return 0;
-}
+ cstate = chip->state;
-static void atmel_pwm_disable(struct pwm_chip *chip)
-{
- struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
+ if (state->enabled) {
+ unsigned long clkrate = clk_get_rate(atmel_pwm->clk);
+
+ if (cstate.enabled &&
+ cstate.polarity == state->polarity &&
+ 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,
+ atmel_pwm->data->regs.period);
+ pres = cmr & PWM_CMR_CPRE_MSK;
+
+ atmel_pwm_calculate_cdty(state, clkrate, cprd, pres, &cdty);
+ atmel_pwm_update_cdty(chip, cdty);
+ return 0;
+ }
+
+ ret = atmel_pwm_calculate_cprd_and_pres(chip, clkrate, state, &cprd,
+ &pres);
+ if (ret) {
+ dev_err(atmel_pwm->dev,
+ "failed to calculate cprd and prescaler\n");
+ return ret;
+ }
+
+ atmel_pwm_calculate_cdty(state, clkrate, cprd, pres, &cdty);
- atmel_pwm_writel(atmel_pwm, PWM_DIS, 1 << atmel_pwm->chip.id);
+ if (cstate.enabled) {
+ atmel_pwm_disable(chip, false);
+ } else {
+ ret = clk_enable(atmel_pwm->clk);
+ if (ret) {
+ dev_err(atmel_pwm->dev, "failed to enable clock\n");
+ return ret;
+ }
+ }
+
+ /* It is necessary to preserve CPOL, inside CMR */
+ val = atmel_pwm_ch_readl(atmel_pwm, chip->id, PWM_CMR);
+ val = (val & ~PWM_CMR_CPRE_MSK) | (pres & PWM_CMR_CPRE_MSK);
+ if (state->polarity == PWM_POLARITY_NORMAL)
+ val &= ~PWM_CMR_CPOL;
+ else
+ val |= PWM_CMR_CPOL;
+ 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.enabled) {
+ atmel_pwm_disable(chip, true);
+ }
+
+ return 0;
}
-static struct pwm_ops atmel_pwm_ops = {
- .config = atmel_pwm_config,
- .enable = atmel_pwm_enable,
- .disable = atmel_pwm_disable,
+static const struct pwm_ops atmel_pwm_ops = {
+ .apply = atmel_pwm_apply,
};
-static const struct atmel_pwm_registers atmel_pwm_regs_v1 = {
- .period = PWMV1_CPRD,
- .period_upd = PWMV1_CUPD,
- .duty = PWMV1_CDTY,
- .duty_upd = PWMV1_CUPD,
+static const struct atmel_pwm_data atmel_sam9rl_pwm_data = {
+ .regs = {
+ .period = PWMV1_CPRD,
+ .period_upd = PWMV1_CUPD,
+ .duty = PWMV1_CDTY,
+ .duty_upd = PWMV1_CUPD,
+ },
+ .cfg = {
+ /* 16 bits to keep period and duty. */
+ .period_bits = 16,
+ },
+};
+
+static const struct atmel_pwm_data atmel_sama5_pwm_data = {
+ .regs = {
+ .period = PWMV2_CPRD,
+ .period_upd = PWMV2_CPRDUPD,
+ .duty = PWMV2_CDTY,
+ .duty_upd = PWMV2_CDTYUPD,
+ },
+ .cfg = {
+ /* 16 bits to keep period and duty. */
+ .period_bits = 16,
+ },
};
-static const struct atmel_pwm_registers atmel_pwm_regs_v2 = {
- .period = PWMV2_CPRD,
- .period_upd = PWMV2_CPRDUPD,
- .duty = PWMV2_CDTY,
- .duty_upd = PWMV2_CDTYUPD,
+static const struct atmel_pwm_data mchp_sam9x60_pwm_data = {
+ .regs = {
+ .period = PWMV1_CPRD,
+ .period_upd = PWMV1_CUPD,
+ .duty = PWMV1_CDTY,
+ .duty_upd = PWMV1_CUPD,
+ },
+ .cfg = {
+ /* 32 bits to keep period and duty. */
+ .period_bits = 32,
+ },
};
static const struct of_device_id atmel_pwm_dt_ids[] = {
{
.compatible = "atmel,at91sam9rl-pwm",
- .data = &atmel_pwm_regs_v1,
+ .data = &atmel_sam9rl_pwm_data,
}, {
.compatible = "atmel,sama5d3-pwm",
- .data = &atmel_pwm_regs_v2,
+ .data = &atmel_sama5_pwm_data,
}, {
.compatible = "atmel,sama5d2-pwm",
- .data = &atmel_pwm_regs_v2,
+ .data = &atmel_sama5_pwm_data,
+ }, {
+ .compatible = "microchip,sam9x60-pwm",
+ .data = &mchp_sam9x60_pwm_data,
}, {
/* sentinel */
},
};
+MODULE_DEVICE_TABLE(of, atmel_pwm_dt_ids);
+
+static int id = -1;
-static int atmel_pwm_probe(struct device_d *dev)
+static int atmel_pwm_probe(struct device *dev)
{
- const struct atmel_pwm_registers *regs;
- struct atmel_pwm *atmel_pwm;
- struct resource *res;
+ const struct atmel_pwm_data *data;
+ struct atmel_pwm_chip *atmel_pwm;
+ struct resource *iores;
+ const char *alias;
int ret;
int i;
- ret = dev_get_drvdata(dev, (const void **)&regs);
+ ret = dev_get_drvdata(dev, (const void **)&data);
if (ret)
return ret;
atmel_pwm = xzalloc(sizeof(*atmel_pwm));
- atmel_pwm->regs = regs;
+ atmel_pwm->data = data;
atmel_pwm->dev = dev;
atmel_pwm->clk = clk_get(dev, "pwm_clk");
if (IS_ERR(atmel_pwm->clk))
return PTR_ERR(atmel_pwm->clk);
- res = dev_request_mem_resource(dev, 0);
- if (IS_ERR(res))
- return PTR_ERR(res);
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
- atmel_pwm->base = IOMEM(res);
+ atmel_pwm->base = IOMEM(iores->start);
+ alias = of_alias_get(dev->of_node);
+ if (!alias)
+ id++;
for (i = 0; i < PWM_CHANNELS; i++) {
- struct atmel_pwm_chip *chip = &atmel_pwm->atmel_pwm_chip[i];
- chip->chip.ops = &atmel_pwm_ops;
- chip->chip.devname = basprintf("pwm%d", i);
- chip->chip.id = i;
- chip->atmel = atmel_pwm;
-
- ret = pwmchip_add(&chip->chip, dev);
- if (ret < 0) {
- dev_err(dev, "failed to add pwm chip[%d] %d\n", i, ret);
+ struct pwm_chip *chip = &atmel_pwm->chips[i];
+
+ if (alias)
+ chip->devname = basprintf("%sch%u", alias, i + 1);
+ else
+ chip->devname = basprintf("pwm%uch%u", id, i + 1);
+
+ chip->ops = &atmel_pwm_ops;
+ chip->id = i;
+ chip->dev = dev;
+ ret = pwmchip_add(chip);
+ if (ret) {
+ dev_err(dev, "failed to add pwm chip %d\n", ret);
return ret;
}
}
@@ -276,7 +466,7 @@ static int atmel_pwm_probe(struct device_d *dev)
return 0;
}
-static struct driver_d atmel_pwm_driver = {
+static struct driver atmel_pwm_driver = {
.name = "atmel-pwm",
.of_compatible = atmel_pwm_dt_ids,
.probe = atmel_pwm_probe,
diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c
index 6dd6e3eb95..2a75400593 100644
--- a/drivers/pwm/pwm-imx.c
+++ b/drivers/pwm/pwm-imx.c
@@ -1,10 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* simple driver for PWM (Pulse Width Modulator) controller
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* Derived from pxa PWM driver by eric miao <eric.miao@marvell.com>
*/
@@ -39,7 +36,7 @@
#define MX3_PWMCR_EN (1 << 0)
struct imx_chip {
- struct clk *clk_per;
+ struct clk *clk_per, *clk_ipg;
void __iomem *mmio_base;
@@ -96,14 +93,42 @@ static void imx_pwm_set_enable_v1(struct pwm_chip *chip, bool enable)
writel(val, imx->mmio_base + MX1_PWMC);
}
+static int imx_pwm_clk_enable_v2(struct imx_chip *imx)
+{
+ int ret;
+
+ ret = clk_enable(imx->clk_ipg);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(imx->clk_per);
+ if (ret) {
+ clk_disable_unprepare(imx->clk_ipg);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void imx_pwm_clk_disable_v2(struct imx_chip *imx)
+{
+ clk_disable_unprepare(imx->clk_per);
+ clk_disable_unprepare(imx->clk_ipg);
+}
+
static int imx_pwm_config_v2(struct pwm_chip *chip,
int duty_ns, int period_ns)
{
struct imx_chip *imx = to_imx_chip(chip);
unsigned long long c;
unsigned long period_cycles, duty_cycles, prescale;
+ int ret;
u32 cr;
+ ret = imx_pwm_clk_enable_v2(imx);
+ if (ret)
+ return ret;
+
c = clk_get_rate(imx->clk_per);
c = c * period_ns;
do_div(c, 1000000000);
@@ -137,6 +162,9 @@ static int imx_pwm_config_v2(struct pwm_chip *chip,
writel(cr, imx->mmio_base + MX3_PWMCR);
+ if (!chip->state.enabled)
+ imx_pwm_clk_disable_v2(imx);
+
return 0;
}
@@ -144,6 +172,11 @@ static void imx_pwm_set_enable_v2(struct pwm_chip *chip, bool enable)
{
struct imx_chip *imx = to_imx_chip(chip);
u32 val;
+ int ret;
+
+ ret = imx_pwm_clk_enable_v2(imx);
+ if (WARN_ON(ret))
+ return;
val = readl(imx->mmio_base + MX3_PWMCR);
@@ -153,26 +186,31 @@ static void imx_pwm_set_enable_v2(struct pwm_chip *chip, bool enable)
val &= ~MX3_PWMCR_EN;
writel(val, imx->mmio_base + MX3_PWMCR);
+
+ if (!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;
@@ -203,13 +241,14 @@ static struct of_device_id imx_pwm_dt_ids[] = {
{ .compatible = "fsl,imx27-pwm", .data = &imx_pwm_data_v2, },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, imx_pwm_dt_ids);
-static int imx_pwm_probe(struct device_d *dev)
+static int imx_pwm_probe(struct device *dev)
{
struct resource *iores;
const struct imx_pwm_data *data;
struct imx_chip *imx;
- int ret = 0;
+ int ret;
ret = dev_get_drvdata(dev, (const void **)&data);
if (ret)
@@ -217,6 +256,10 @@ static int imx_pwm_probe(struct device_d *dev)
imx = xzalloc(sizeof(*imx));
+ imx->clk_ipg = clk_get_optional(dev, "ipg");
+ if (IS_ERR(imx->clk_ipg))
+ return PTR_ERR(imx->clk_ipg);
+
imx->clk_per = clk_get(dev, "per");
if (IS_ERR(imx->clk_per))
return PTR_ERR(imx->clk_per);
@@ -226,9 +269,10 @@ static int imx_pwm_probe(struct device_d *dev)
return PTR_ERR(iores);
imx->mmio_base = IOMEM(iores->start);
+ imx->chip.dev = dev;
imx->chip.ops = &imx_pwm_ops;
- if (dev->device_node) {
- imx->chip.devname = of_alias_get(dev->device_node);
+ if (dev->of_node) {
+ imx->chip.devname = of_alias_get(dev->of_node);
if (!imx->chip.devname)
imx->chip.devname = basprintf("pwm_%p",
imx->mmio_base);
@@ -239,14 +283,10 @@ static int imx_pwm_probe(struct device_d *dev)
imx->config = data->config;
imx->set_enable = data->set_enable;
- ret = pwmchip_add(&imx->chip, dev);
- if (ret < 0)
- return ret;
-
- return 0;
+ return pwmchip_add(&imx->chip);
}
-static struct driver_d imx_pwm_driver = {
+static struct driver imx_pwm_driver = {
.name = "imx-pwm",
.of_compatible = imx_pwm_dt_ids,
.probe = imx_pwm_probe,
diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c
index 8f77ca07a6..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;
@@ -93,10 +95,10 @@ static struct pwm_ops mxs_pwm_ops = {
.apply = mxs_pwm_apply,
};
-static int mxs_pwm_probe(struct device_d *dev)
+static int mxs_pwm_probe(struct device *dev)
{
struct resource *iores;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct mxs_pwm *mxs;
int ret, i;
uint32_t npwm;
@@ -130,9 +132,10 @@ static int mxs_pwm_probe(struct device_d *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;
@@ -146,8 +149,9 @@ static const struct of_device_id mxs_pwm_dt_ids[] = {
{ .compatible = "fsl,imx23-pwm", },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, mxs_pwm_dt_ids);
-static struct driver_d mxs_pwm_driver = {
+static struct driver mxs_pwm_driver = {
.name = "mxs-pwm",
.of_compatible = mxs_pwm_dt_ids,
.probe = mxs_pwm_probe,
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 061644e4d8..16f80381c2 100644
--- a/drivers/pwm/pwm-stm32.c
+++ b/drivers/pwm/pwm-stm32.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2016 STMicroelectronics 2016
* Copyright (C) 2020 Pengutronix
@@ -14,6 +14,7 @@
#include <io.h>
#include <linux/bitfield.h>
#include <linux/mfd/stm32-timers.h>
+#include <linux/regmap.h>
#include <linux/math64.h>
#include <of.h>
#include <pwm.h>
@@ -197,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;
}
@@ -214,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;
@@ -335,9 +338,9 @@ static int stm32_pwm_detect_channels(struct stm32_pwm *priv)
static int id = -1;
-static int stm32_pwm_probe(struct device_d *dev)
+static int stm32_pwm_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct stm32_timers *ddata = dev->parent->priv;
struct stm32_pwm *priv;
const char *alias;
@@ -362,7 +365,7 @@ static int stm32_pwm_probe(struct device_d *dev)
npwms = stm32_pwm_detect_channels(priv);
- alias = of_alias_get(dev->device_node);
+ alias = of_alias_get(dev->of_node);
if (!alias)
id++;
@@ -376,8 +379,9 @@ static int stm32_pwm_probe(struct device_d *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;
@@ -391,8 +395,9 @@ static const struct of_device_id stm32_pwm_of_match[] = {
{ .compatible = "st,stm32-pwm", },
{ /* sentinel */ },
};
+MODULE_DEVICE_TABLE(of, stm32_pwm_of_match);
-static struct driver_d stm32_pwm_driver = {
+static struct driver stm32_pwm_driver = {
.name = "stm32-pwm",
.probe = stm32_pwm_probe,
.of_compatible = stm32_pwm_of_match,
diff --git a/drivers/pwm/pxa_pwm.c b/drivers/pwm/pxa_pwm.c
index 61c4b8da43..42ccb37b9a 100644
--- a/drivers/pwm/pxa_pwm.c
+++ b/drivers/pwm/pxa_pwm.c
@@ -13,10 +13,10 @@
#include <io.h>
#include <pwm.h>
-#include <mach/hardware.h>
-#include <mach/clock.h>
-#include <mach/pxa-regs.h>
-#include <mach/regs-pwm.h>
+#include <mach/pxa/hardware.h>
+#include <mach/pxa/clock.h>
+#include <mach/pxa/pxa-regs.h>
+#include <mach/pxa/regs-pwm.h>
#include <linux/math64.h>
#include <linux/compiler.h>
@@ -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;
}
@@ -128,7 +130,7 @@ static struct pwm_ops pxa_pwm_ops = {
.apply = pxa_pwm_apply,
};
-static int pxa_pwm_probe(struct device_d *dev)
+static int pxa_pwm_probe(struct device *dev)
{
struct resource *iores;
struct pxa_pwm_chip *chip;
@@ -141,12 +143,13 @@ static int pxa_pwm_probe(struct device_d *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_d pxa_pwm_driver = {
+static struct driver pxa_pwm_driver = {
.name = "pxa_pwm",
.probe = pxa_pwm_probe,
};