summaryrefslogtreecommitdiff
path: root/drivers/pwm
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pwm')
-rw-r--r--drivers/pwm/Kconfig24
-rw-r--r--drivers/pwm/Makefile2
-rw-r--r--drivers/pwm/core.c181
-rw-r--r--drivers/pwm/pwm-atmel-tcb.c113
-rw-r--r--drivers/pwm/pwm-axi-pwmgen.c242
-rw-r--r--drivers/pwm/pwm-cros-ec.c64
-rw-r--r--drivers/pwm/pwm-gpio.c241
-rw-r--r--drivers/pwm/pwm-imx-tpm.c16
-rw-r--r--drivers/pwm/pwm-imx1.c1
-rw-r--r--drivers/pwm/pwm-imx27.c1
-rw-r--r--drivers/pwm/pwm-intel-lgm.c1
-rw-r--r--drivers/pwm/pwm-jz4740.c9
-rw-r--r--drivers/pwm/pwm-lpss-pci.c22
-rw-r--r--drivers/pwm/pwm-lpss-platform.c10
-rw-r--r--drivers/pwm/pwm-mediatek.c1
-rw-r--r--drivers/pwm/pwm-meson.c39
-rw-r--r--drivers/pwm/pwm-pxa.c1
-rw-r--r--drivers/pwm/pwm-samsung.c1
-rw-r--r--drivers/pwm/pwm-spear.c1
-rw-r--r--drivers/pwm/pwm-stm32.c50
-rw-r--r--drivers/pwm/pwm-visconti.c1
-rw-r--r--drivers/pwm/pwm-xilinx.c27
22 files changed, 772 insertions, 276 deletions
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 1dd7921194f5..3e53838990f5 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -94,6 +94,19 @@ config PWM_ATMEL_TCB
To compile this driver as a module, choose M here: the module
will be called pwm-atmel-tcb.
+config PWM_AXI_PWMGEN
+ tristate "Analog Devices AXI PWM generator"
+ depends on MICROBLAZE || NIOS2 || ARCH_ZYNQ || ARCH_ZYNQMP || ARCH_INTEL_SOCFPGA || COMPILE_TEST
+ select REGMAP_MMIO
+ help
+ This enables support for the Analog Devices AXI PWM generator.
+
+ This is a configurable PWM generator with variable pulse width and
+ period.
+
+ To compile this driver as a module, choose M here: the module will be
+ called pwm-axi-pwmgen.
+
config PWM_BCM_IPROC
tristate "iProc PWM support"
depends on ARCH_BCM_IPROC || COMPILE_TEST
@@ -223,6 +236,17 @@ config PWM_FSL_FTM
To compile this driver as a module, choose M here: the module
will be called pwm-fsl-ftm.
+config PWM_GPIO
+ tristate "GPIO PWM support"
+ depends on GPIOLIB
+ depends on HIGH_RES_TIMERS
+ help
+ Generic PWM framework driver for software PWM toggling a GPIO pin
+ from kernel high-resolution timers.
+
+ To compile this driver as a module, choose M here: the module
+ will be called pwm-gpio.
+
config PWM_HIBVT
tristate "HiSilicon BVT PWM support"
depends on ARCH_HISI || COMPILE_TEST
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 90913519f11a..0be4f3e6dd43 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -5,6 +5,7 @@ obj-$(CONFIG_PWM_APPLE) += pwm-apple.o
obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o
obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM) += pwm-atmel-hlcdc.o
obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o
+obj-$(CONFIG_PWM_AXI_PWMGEN) += pwm-axi-pwmgen.o
obj-$(CONFIG_PWM_BCM_IPROC) += pwm-bcm-iproc.o
obj-$(CONFIG_PWM_BCM_KONA) += pwm-bcm-kona.o
obj-$(CONFIG_PWM_BCM2835) += pwm-bcm2835.o
@@ -18,6 +19,7 @@ obj-$(CONFIG_PWM_DWC_CORE) += pwm-dwc-core.o
obj-$(CONFIG_PWM_DWC) += pwm-dwc.o
obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o
obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o
+obj-$(CONFIG_PWM_GPIO) += pwm-gpio.o
obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o
obj-$(CONFIG_PWM_IMG) += pwm-img.o
obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 18574857641e..8acbcf5b6673 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -6,6 +6,8 @@
* Copyright (C) 2011-2012 Avionic Design GmbH
*/
+#define DEFAULT_SYMBOL_NAMESPACE PWM
+
#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/idr.h>
@@ -135,6 +137,25 @@ static void pwm_apply_debug(struct pwm_device *pwm,
}
}
+static bool pwm_state_valid(const struct pwm_state *state)
+{
+ /*
+ * For a disabled state all other state description is irrelevant and
+ * and supposed to be ignored. So also ignore any strange values and
+ * consider the state ok.
+ */
+ if (state->enabled)
+ return true;
+
+ if (!state->period)
+ return false;
+
+ if (state->duty_cycle > state->period)
+ return false;
+
+ return true;
+}
+
/**
* __pwm_apply() - atomically apply a new state to a PWM device
* @pwm: PWM device
@@ -145,9 +166,25 @@ static int __pwm_apply(struct pwm_device *pwm, const struct pwm_state *state)
struct pwm_chip *chip;
int err;
- if (!pwm || !state || !state->period ||
- state->duty_cycle > state->period)
+ if (!pwm || !state)
+ return -EINVAL;
+
+ if (!pwm_state_valid(state)) {
+ /*
+ * Allow to transition from one invalid state to another.
+ * This ensures that you can e.g. change the polarity while
+ * the period is zero. (This happens on stm32 when the hardware
+ * is in its poweron default state.) This greatly simplifies
+ * working with the sysfs API where you can only change one
+ * parameter at a time.
+ */
+ if (!pwm_state_valid(&pwm->state)) {
+ pwm->state = *state;
+ return 0;
+ }
+
return -EINVAL;
+ }
chip = pwm->chip;
@@ -291,19 +328,15 @@ EXPORT_SYMBOL_GPL(pwm_adjust_config);
int pwm_capture(struct pwm_device *pwm, struct pwm_capture *result,
unsigned long timeout)
{
- int err;
-
if (!pwm || !pwm->chip->ops)
return -EINVAL;
if (!pwm->chip->ops->capture)
return -ENOSYS;
- mutex_lock(&pwm_lock);
- err = pwm->chip->ops->capture(pwm->chip, pwm, result, timeout);
- mutex_unlock(&pwm_lock);
+ guard(mutex)(&pwm_lock);
- return err;
+ return pwm->chip->ops->capture(pwm->chip, pwm, result, timeout);
}
EXPORT_SYMBOL_GPL(pwm_capture);
@@ -315,19 +348,15 @@ static struct pwm_chip *pwmchip_find_by_name(const char *name)
if (!name)
return NULL;
- mutex_lock(&pwm_lock);
+ guard(mutex)(&pwm_lock);
idr_for_each_entry_ul(&pwm_chips, chip, tmp, id) {
const char *chip_name = dev_name(pwmchip_parent(chip));
- if (chip_name && strcmp(chip_name, name) == 0) {
- mutex_unlock(&pwm_lock);
+ if (chip_name && strcmp(chip_name, name) == 0)
return chip;
- }
}
- mutex_unlock(&pwm_lock);
-
return NULL;
}
@@ -394,9 +423,9 @@ err_get_device:
* chip. A negative error code is returned if the index is not valid for the
* specified PWM chip or if the PWM device cannot be requested.
*/
-struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
- unsigned int index,
- const char *label)
+static struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
+ unsigned int index,
+ const char *label)
{
struct pwm_device *pwm;
int err;
@@ -404,18 +433,16 @@ struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
if (!chip || index >= chip->npwm)
return ERR_PTR(-EINVAL);
- mutex_lock(&pwm_lock);
+ guard(mutex)(&pwm_lock);
+
pwm = &chip->pwms[index];
err = pwm_device_request(pwm, label);
if (err < 0)
- pwm = ERR_PTR(err);
+ return ERR_PTR(err);
- mutex_unlock(&pwm_lock);
return pwm;
}
-EXPORT_SYMBOL_GPL(pwm_request_from_chip);
-
struct pwm_device *
of_pwm_xlate_with_flags(struct pwm_chip *chip, const struct of_phandle_args *args)
@@ -511,11 +538,11 @@ static ssize_t period_store(struct device *pwm_dev,
if (ret)
return ret;
- mutex_lock(&export->lock);
+ guard(mutex)(&export->lock);
+
pwm_get_state(pwm, &state);
state.period = val;
ret = pwm_apply_might_sleep(pwm, &state);
- mutex_unlock(&export->lock);
return ret ? : size;
}
@@ -546,11 +573,11 @@ static ssize_t duty_cycle_store(struct device *pwm_dev,
if (ret)
return ret;
- mutex_lock(&export->lock);
+ guard(mutex)(&export->lock);
+
pwm_get_state(pwm, &state);
state.duty_cycle = val;
ret = pwm_apply_might_sleep(pwm, &state);
- mutex_unlock(&export->lock);
return ret ? : size;
}
@@ -580,7 +607,7 @@ static ssize_t enable_store(struct device *pwm_dev,
if (ret)
return ret;
- mutex_lock(&export->lock);
+ guard(mutex)(&export->lock);
pwm_get_state(pwm, &state);
@@ -592,14 +619,11 @@ static ssize_t enable_store(struct device *pwm_dev,
state.enabled = true;
break;
default:
- ret = -EINVAL;
- goto unlock;
+ return -EINVAL;
}
ret = pwm_apply_might_sleep(pwm, &state);
-unlock:
- mutex_unlock(&export->lock);
return ret ? : size;
}
@@ -643,11 +667,11 @@ static ssize_t polarity_store(struct device *pwm_dev,
else
return -EINVAL;
- mutex_lock(&export->lock);
+ guard(mutex)(&export->lock);
+
pwm_get_state(pwm, &state);
state.polarity = polarity;
ret = pwm_apply_might_sleep(pwm, &state);
- mutex_unlock(&export->lock);
return ret ? : size;
}
@@ -1102,11 +1126,11 @@ int __pwmchip_add(struct pwm_chip *chip, struct module *owner)
chip->owner = owner;
- mutex_lock(&pwm_lock);
+ guard(mutex)(&pwm_lock);
ret = idr_alloc(&pwm_chips, chip, 0, 0, GFP_KERNEL);
if (ret < 0)
- goto err_idr_alloc;
+ return ret;
chip->id = ret;
@@ -1119,8 +1143,6 @@ int __pwmchip_add(struct pwm_chip *chip, struct module *owner)
if (ret)
goto err_device_add;
- mutex_unlock(&pwm_lock);
-
return 0;
err_device_add:
@@ -1128,9 +1150,6 @@ err_device_add:
of_pwmchip_remove(chip);
idr_remove(&pwm_chips, chip->id);
-err_idr_alloc:
-
- mutex_unlock(&pwm_lock);
return ret;
}
@@ -1149,11 +1168,8 @@ void pwmchip_remove(struct pwm_chip *chip)
if (IS_ENABLED(CONFIG_OF))
of_pwmchip_remove(chip);
- mutex_lock(&pwm_lock);
-
- idr_remove(&pwm_chips, chip->id);
-
- mutex_unlock(&pwm_lock);
+ scoped_guard(mutex, &pwm_lock)
+ idr_remove(&pwm_chips, chip->id);
device_del(&chip->dev);
}
@@ -1209,15 +1225,11 @@ static struct pwm_chip *fwnode_to_pwmchip(struct fwnode_handle *fwnode)
struct pwm_chip *chip;
unsigned long id, tmp;
- mutex_lock(&pwm_lock);
+ guard(mutex)(&pwm_lock);
idr_for_each_entry_ul(&pwm_chips, chip, tmp, id)
- if (pwmchip_parent(chip) && device_match_fwnode(pwmchip_parent(chip), fwnode)) {
- mutex_unlock(&pwm_lock);
+ if (pwmchip_parent(chip) && device_match_fwnode(pwmchip_parent(chip), fwnode))
return chip;
- }
-
- mutex_unlock(&pwm_lock);
return ERR_PTR(-EPROBE_DEFER);
}
@@ -1366,14 +1378,12 @@ static LIST_HEAD(pwm_lookup_list);
*/
void pwm_add_table(struct pwm_lookup *table, size_t num)
{
- mutex_lock(&pwm_lookup_lock);
+ guard(mutex)(&pwm_lookup_lock);
while (num--) {
list_add_tail(&table->list, &pwm_lookup_list);
table++;
}
-
- mutex_unlock(&pwm_lookup_lock);
}
/**
@@ -1383,14 +1393,12 @@ void pwm_add_table(struct pwm_lookup *table, size_t num)
*/
void pwm_remove_table(struct pwm_lookup *table, size_t num)
{
- mutex_lock(&pwm_lookup_lock);
+ guard(mutex)(&pwm_lookup_lock);
while (num--) {
list_del(&table->list);
table++;
}
-
- mutex_unlock(&pwm_lookup_lock);
}
/**
@@ -1451,36 +1459,33 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
* Then we take the most specific entry - with the following order
* of precedence: dev+con > dev only > con only.
*/
- mutex_lock(&pwm_lookup_lock);
-
- list_for_each_entry(p, &pwm_lookup_list, list) {
- match = 0;
+ scoped_guard(mutex, &pwm_lookup_lock)
+ list_for_each_entry(p, &pwm_lookup_list, list) {
+ match = 0;
- if (p->dev_id) {
- if (!dev_id || strcmp(p->dev_id, dev_id))
- continue;
+ if (p->dev_id) {
+ if (!dev_id || strcmp(p->dev_id, dev_id))
+ continue;
- match += 2;
- }
+ match += 2;
+ }
- if (p->con_id) {
- if (!con_id || strcmp(p->con_id, con_id))
- continue;
+ if (p->con_id) {
+ if (!con_id || strcmp(p->con_id, con_id))
+ continue;
- match += 1;
- }
+ match += 1;
+ }
- if (match > best) {
- chosen = p;
+ if (match > best) {
+ chosen = p;
- if (match != 3)
- best = match;
- else
- break;
+ if (match != 3)
+ best = match;
+ else
+ break;
+ }
}
- }
-
- mutex_unlock(&pwm_lookup_lock);
if (!chosen)
return ERR_PTR(-ENODEV);
@@ -1532,11 +1537,11 @@ void pwm_put(struct pwm_device *pwm)
chip = pwm->chip;
- mutex_lock(&pwm_lock);
+ guard(mutex)(&pwm_lock);
if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) {
pr_warn("PWM device already freed\n");
- goto out;
+ return;
}
if (chip->ops->free)
@@ -1547,8 +1552,6 @@ void pwm_put(struct pwm_device *pwm)
put_device(&chip->dev);
module_put(chip->owner);
-out:
- mutex_unlock(&pwm_lock);
}
EXPORT_SYMBOL_GPL(pwm_put);
@@ -1705,9 +1708,17 @@ DEFINE_SEQ_ATTRIBUTE(pwm_debugfs);
static int __init pwm_init(void)
{
+ int ret;
+
+ ret = class_register(&pwm_class);
+ if (ret) {
+ pr_err("Failed to initialize PWM class (%pe)\n", ERR_PTR(ret));
+ return ret;
+ }
+
if (IS_ENABLED(CONFIG_DEBUG_FS))
debugfs_create_file("pwm", 0444, NULL, NULL, &pwm_debugfs_fops);
- return class_register(&pwm_class);
+ return 0;
}
subsys_initcall(pwm_init);
diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c
index 528e54c5999d..f9a9c12cbcdd 100644
--- a/drivers/pwm/pwm-atmel-tcb.c
+++ b/drivers/pwm/pwm-atmel-tcb.c
@@ -81,7 +81,8 @@ static int atmel_tcb_pwm_request(struct pwm_chip *chip,
tcbpwm->period = 0;
tcbpwm->div = 0;
- spin_lock(&tcbpwmc->lock);
+ guard(spinlock)(&tcbpwmc->lock);
+
regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr);
/*
* Get init config from Timer Counter registers if
@@ -107,7 +108,6 @@ static int atmel_tcb_pwm_request(struct pwm_chip *chip,
cmr |= ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO | ATMEL_TC_EEVT_XC0;
regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), cmr);
- spin_unlock(&tcbpwmc->lock);
return 0;
}
@@ -137,7 +137,6 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm,
if (tcbpwm->duty == 0)
polarity = !polarity;
- spin_lock(&tcbpwmc->lock);
regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr);
/* flush old setting and set the new one */
@@ -172,8 +171,6 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm,
ATMEL_TC_SWTRG);
tcbpwmc->bkup.enabled = 0;
}
-
- spin_unlock(&tcbpwmc->lock);
}
static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm,
@@ -194,7 +191,6 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm,
if (tcbpwm->duty == 0)
polarity = !polarity;
- spin_lock(&tcbpwmc->lock);
regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), &cmr);
/* flush old setting and set the new one */
@@ -256,7 +252,6 @@ static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm,
regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CCR),
ATMEL_TC_SWTRG | ATMEL_TC_CLKEN);
tcbpwmc->bkup.enabled = 1;
- spin_unlock(&tcbpwmc->lock);
return 0;
}
@@ -265,7 +260,8 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
{
struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm];
- struct atmel_tcb_pwm_device *atcbpwm = NULL;
+ /* companion PWM sharing register values period and div */
+ struct atmel_tcb_pwm_device *atcbpwm = &tcbpwmc->pwms[pwm->hwpwm ^ 1];
int i = 0;
int slowclk = 0;
unsigned period;
@@ -310,11 +306,6 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
duty = div_u64(duty_ns, min);
period = div_u64(period_ns, min);
- if (pwm->hwpwm == 0)
- atcbpwm = &tcbpwmc->pwms[1];
- else
- atcbpwm = &tcbpwmc->pwms[0];
-
/*
* PWM devices provided by the TCB driver are grouped by 2.
* PWM devices in a given group must be configured with the
@@ -323,8 +314,7 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
* We're checking the period value of the second PWM device
* in this group before applying the new config.
*/
- if ((atcbpwm && atcbpwm->duty > 0 &&
- atcbpwm->duty != atcbpwm->period) &&
+ if ((atcbpwm->duty > 0 && atcbpwm->duty != atcbpwm->period) &&
(atcbpwm->div != i || atcbpwm->period != period)) {
dev_err(pwmchip_parent(chip),
"failed to configure period_ns: PWM group already configured with a different value\n");
@@ -341,9 +331,12 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
static int atmel_tcb_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
const struct pwm_state *state)
{
+ struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
int duty_cycle, period;
int ret;
+ guard(spinlock)(&tcbpwmc->lock);
+
if (!state->enabled) {
atmel_tcb_pwm_disable(chip, pwm, state->polarity);
return 0;
@@ -389,17 +382,17 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev)
{
struct pwm_chip *chip;
const struct of_device_id *match;
- struct atmel_tcb_pwm_chip *tcbpwm;
+ struct atmel_tcb_pwm_chip *tcbpwmc;
const struct atmel_tcb_config *config;
struct device_node *np = pdev->dev.of_node;
char clk_name[] = "t0_clk";
int err;
int channel;
- chip = devm_pwmchip_alloc(&pdev->dev, NPWM, sizeof(*tcbpwm));
+ chip = devm_pwmchip_alloc(&pdev->dev, NPWM, sizeof(*tcbpwmc));
if (IS_ERR(chip))
return PTR_ERR(chip);
- tcbpwm = to_tcb_chip(chip);
+ tcbpwmc = to_tcb_chip(chip);
err = of_property_read_u32(np, "reg", &channel);
if (err < 0) {
@@ -409,20 +402,20 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev)
return err;
}
- tcbpwm->regmap = syscon_node_to_regmap(np->parent);
- if (IS_ERR(tcbpwm->regmap))
- return PTR_ERR(tcbpwm->regmap);
+ tcbpwmc->regmap = syscon_node_to_regmap(np->parent);
+ if (IS_ERR(tcbpwmc->regmap))
+ return PTR_ERR(tcbpwmc->regmap);
- tcbpwm->slow_clk = of_clk_get_by_name(np->parent, "slow_clk");
- if (IS_ERR(tcbpwm->slow_clk))
- return PTR_ERR(tcbpwm->slow_clk);
+ tcbpwmc->slow_clk = of_clk_get_by_name(np->parent, "slow_clk");
+ if (IS_ERR(tcbpwmc->slow_clk))
+ return PTR_ERR(tcbpwmc->slow_clk);
clk_name[1] += channel;
- tcbpwm->clk = of_clk_get_by_name(np->parent, clk_name);
- if (IS_ERR(tcbpwm->clk))
- tcbpwm->clk = of_clk_get_by_name(np->parent, "t0_clk");
- if (IS_ERR(tcbpwm->clk)) {
- err = PTR_ERR(tcbpwm->clk);
+ tcbpwmc->clk = of_clk_get_by_name(np->parent, clk_name);
+ if (IS_ERR(tcbpwmc->clk))
+ tcbpwmc->clk = of_clk_get_by_name(np->parent, "t0_clk");
+ if (IS_ERR(tcbpwmc->clk)) {
+ err = PTR_ERR(tcbpwmc->clk);
goto err_slow_clk;
}
@@ -430,22 +423,22 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev)
config = match->data;
if (config->has_gclk) {
- tcbpwm->gclk = of_clk_get_by_name(np->parent, "gclk");
- if (IS_ERR(tcbpwm->gclk)) {
- err = PTR_ERR(tcbpwm->gclk);
+ tcbpwmc->gclk = of_clk_get_by_name(np->parent, "gclk");
+ if (IS_ERR(tcbpwmc->gclk)) {
+ err = PTR_ERR(tcbpwmc->gclk);
goto err_clk;
}
}
chip->ops = &atmel_tcb_pwm_ops;
- tcbpwm->channel = channel;
- tcbpwm->width = config->counter_width;
+ tcbpwmc->channel = channel;
+ tcbpwmc->width = config->counter_width;
- err = clk_prepare_enable(tcbpwm->slow_clk);
+ err = clk_prepare_enable(tcbpwmc->slow_clk);
if (err)
goto err_gclk;
- spin_lock_init(&tcbpwm->lock);
+ spin_lock_init(&tcbpwmc->lock);
err = pwmchip_add(chip);
if (err < 0)
@@ -456,16 +449,16 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev)
return 0;
err_disable_clk:
- clk_disable_unprepare(tcbpwm->slow_clk);
+ clk_disable_unprepare(tcbpwmc->slow_clk);
err_gclk:
- clk_put(tcbpwm->gclk);
+ clk_put(tcbpwmc->gclk);
err_clk:
- clk_put(tcbpwm->clk);
+ clk_put(tcbpwmc->clk);
err_slow_clk:
- clk_put(tcbpwm->slow_clk);
+ clk_put(tcbpwmc->slow_clk);
return err;
}
@@ -473,14 +466,14 @@ err_slow_clk:
static void atmel_tcb_pwm_remove(struct platform_device *pdev)
{
struct pwm_chip *chip = platform_get_drvdata(pdev);
- struct atmel_tcb_pwm_chip *tcbpwm = to_tcb_chip(chip);
+ struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
pwmchip_remove(chip);
- clk_disable_unprepare(tcbpwm->slow_clk);
- clk_put(tcbpwm->gclk);
- clk_put(tcbpwm->clk);
- clk_put(tcbpwm->slow_clk);
+ clk_disable_unprepare(tcbpwmc->slow_clk);
+ clk_put(tcbpwmc->gclk);
+ clk_put(tcbpwmc->clk);
+ clk_put(tcbpwmc->slow_clk);
}
static const struct of_device_id atmel_tcb_pwm_dt_ids[] = {
@@ -492,14 +485,14 @@ MODULE_DEVICE_TABLE(of, atmel_tcb_pwm_dt_ids);
static int atmel_tcb_pwm_suspend(struct device *dev)
{
struct pwm_chip *chip = dev_get_drvdata(dev);
- struct atmel_tcb_pwm_chip *tcbpwm = to_tcb_chip(chip);
- struct atmel_tcb_channel *chan = &tcbpwm->bkup;
- unsigned int channel = tcbpwm->channel;
+ struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
+ struct atmel_tcb_channel *chan = &tcbpwmc->bkup;
+ unsigned int channel = tcbpwmc->channel;
- regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, CMR), &chan->cmr);
- regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, RA), &chan->ra);
- regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, RB), &chan->rb);
- regmap_read(tcbpwm->regmap, ATMEL_TC_REG(channel, RC), &chan->rc);
+ regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, CMR), &chan->cmr);
+ regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, RA), &chan->ra);
+ regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, RB), &chan->rb);
+ regmap_read(tcbpwmc->regmap, ATMEL_TC_REG(channel, RC), &chan->rc);
return 0;
}
@@ -507,17 +500,17 @@ static int atmel_tcb_pwm_suspend(struct device *dev)
static int atmel_tcb_pwm_resume(struct device *dev)
{
struct pwm_chip *chip = dev_get_drvdata(dev);
- struct atmel_tcb_pwm_chip *tcbpwm = to_tcb_chip(chip);
- struct atmel_tcb_channel *chan = &tcbpwm->bkup;
- unsigned int channel = tcbpwm->channel;
+ struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip);
+ struct atmel_tcb_channel *chan = &tcbpwmc->bkup;
+ unsigned int channel = tcbpwmc->channel;
- regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, CMR), chan->cmr);
- regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, RA), chan->ra);
- regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, RB), chan->rb);
- regmap_write(tcbpwm->regmap, ATMEL_TC_REG(channel, RC), chan->rc);
+ regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, CMR), chan->cmr);
+ regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, RA), chan->ra);
+ regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, RB), chan->rb);
+ regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(channel, RC), chan->rc);
if (chan->enabled)
- regmap_write(tcbpwm->regmap,
+ regmap_write(tcbpwmc->regmap,
ATMEL_TC_CLKEN | ATMEL_TC_SWTRG,
ATMEL_TC_REG(channel, CCR));
diff --git a/drivers/pwm/pwm-axi-pwmgen.c b/drivers/pwm/pwm-axi-pwmgen.c
new file mode 100644
index 000000000000..3ad60edf20a5
--- /dev/null
+++ b/drivers/pwm/pwm-axi-pwmgen.c
@@ -0,0 +1,242 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Analog Devices AXI PWM generator
+ *
+ * Copyright 2024 Analog Devices Inc.
+ * Copyright 2024 Baylibre SAS
+ *
+ * Device docs: https://analogdevicesinc.github.io/hdl/library/axi_pwm_gen/index.html
+ *
+ * Limitations:
+ * - The writes to registers for period and duty are shadowed until
+ * LOAD_CONFIG is written to AXI_PWMGEN_REG_CONFIG, at which point
+ * they take effect.
+ * - Writing LOAD_CONFIG also has the effect of re-synchronizing all
+ * enabled channels, which could cause glitching on other channels. It
+ * is therefore expected that channels are assigned harmonic periods
+ * and all have a single user coordinating this.
+ * - Supports normal polarity. Does not support changing polarity.
+ * - On disable, the PWM output becomes low (inactive).
+ */
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/fpga/adi-axi-common.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define AXI_PWMGEN_REG_CORE_VERSION 0x00
+#define AXI_PWMGEN_REG_ID 0x04
+#define AXI_PWMGEN_REG_SCRATCHPAD 0x08
+#define AXI_PWMGEN_REG_CORE_MAGIC 0x0C
+#define AXI_PWMGEN_REG_CONFIG 0x10
+#define AXI_PWMGEN_REG_NPWM 0x14
+#define AXI_PWMGEN_CHX_PERIOD(ch) (0x40 + (4 * (ch)))
+#define AXI_PWMGEN_CHX_DUTY(ch) (0x80 + (4 * (ch)))
+#define AXI_PWMGEN_CHX_OFFSET(ch) (0xC0 + (4 * (ch)))
+#define AXI_PWMGEN_REG_CORE_MAGIC_VAL 0x601A3471 /* Identification number to test during setup */
+#define AXI_PWMGEN_LOAD_CONFIG BIT(1)
+#define AXI_PWMGEN_REG_CONFIG_RESET BIT(0)
+
+struct axi_pwmgen_ddata {
+ struct regmap *regmap;
+ unsigned long clk_rate_hz;
+};
+
+static const struct regmap_config axi_pwmgen_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = 0xFC,
+};
+
+static int axi_pwmgen_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct axi_pwmgen_ddata *ddata = pwmchip_get_drvdata(chip);
+ unsigned int ch = pwm->hwpwm;
+ struct regmap *regmap = ddata->regmap;
+ u64 period_cnt, duty_cnt;
+ int ret;
+
+ if (state->polarity != PWM_POLARITY_NORMAL)
+ return -EINVAL;
+
+ if (state->enabled) {
+ period_cnt = mul_u64_u64_div_u64(state->period, ddata->clk_rate_hz, NSEC_PER_SEC);
+ if (period_cnt > UINT_MAX)
+ period_cnt = UINT_MAX;
+
+ if (period_cnt == 0)
+ return -EINVAL;
+
+ ret = regmap_write(regmap, AXI_PWMGEN_CHX_PERIOD(ch), period_cnt);
+ if (ret)
+ return ret;
+
+ duty_cnt = mul_u64_u64_div_u64(state->duty_cycle, ddata->clk_rate_hz, NSEC_PER_SEC);
+ if (duty_cnt > UINT_MAX)
+ duty_cnt = UINT_MAX;
+
+ ret = regmap_write(regmap, AXI_PWMGEN_CHX_DUTY(ch), duty_cnt);
+ if (ret)
+ return ret;
+ } else {
+ ret = regmap_write(regmap, AXI_PWMGEN_CHX_PERIOD(ch), 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(regmap, AXI_PWMGEN_CHX_DUTY(ch), 0);
+ if (ret)
+ return ret;
+ }
+
+ return regmap_write(regmap, AXI_PWMGEN_REG_CONFIG, AXI_PWMGEN_LOAD_CONFIG);
+}
+
+static int axi_pwmgen_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct axi_pwmgen_ddata *ddata = pwmchip_get_drvdata(chip);
+ struct regmap *regmap = ddata->regmap;
+ unsigned int ch = pwm->hwpwm;
+ u32 cnt;
+ int ret;
+
+ ret = regmap_read(regmap, AXI_PWMGEN_CHX_PERIOD(ch), &cnt);
+ if (ret)
+ return ret;
+
+ state->enabled = cnt != 0;
+
+ state->period = DIV_ROUND_UP_ULL((u64)cnt * NSEC_PER_SEC, ddata->clk_rate_hz);
+
+ ret = regmap_read(regmap, AXI_PWMGEN_CHX_DUTY(ch), &cnt);
+ if (ret)
+ return ret;
+
+ state->duty_cycle = DIV_ROUND_UP_ULL((u64)cnt * NSEC_PER_SEC, ddata->clk_rate_hz);
+
+ state->polarity = PWM_POLARITY_NORMAL;
+
+ return 0;
+}
+
+static const struct pwm_ops axi_pwmgen_pwm_ops = {
+ .apply = axi_pwmgen_apply,
+ .get_state = axi_pwmgen_get_state,
+};
+
+static int axi_pwmgen_setup(struct regmap *regmap, struct device *dev)
+{
+ int ret;
+ u32 val;
+
+ ret = regmap_read(regmap, AXI_PWMGEN_REG_CORE_MAGIC, &val);
+ if (ret)
+ return ret;
+
+ if (val != AXI_PWMGEN_REG_CORE_MAGIC_VAL)
+ return dev_err_probe(dev, -ENODEV,
+ "failed to read expected value from register: got %08x, expected %08x\n",
+ val, AXI_PWMGEN_REG_CORE_MAGIC_VAL);
+
+ ret = regmap_read(regmap, AXI_PWMGEN_REG_CORE_VERSION, &val);
+ if (ret)
+ return ret;
+
+ if (ADI_AXI_PCORE_VER_MAJOR(val) != 2) {
+ return dev_err_probe(dev, -ENODEV, "Unsupported peripheral version %u.%u.%u\n",
+ ADI_AXI_PCORE_VER_MAJOR(val),
+ ADI_AXI_PCORE_VER_MINOR(val),
+ ADI_AXI_PCORE_VER_PATCH(val));
+ }
+
+ /* Enable the core */
+ ret = regmap_clear_bits(regmap, AXI_PWMGEN_REG_CONFIG, AXI_PWMGEN_REG_CONFIG_RESET);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(regmap, AXI_PWMGEN_REG_NPWM, &val);
+ if (ret)
+ return ret;
+
+ /* Return the number of PWMs */
+ return val;
+}
+
+static int axi_pwmgen_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct regmap *regmap;
+ struct pwm_chip *chip;
+ struct axi_pwmgen_ddata *ddata;
+ struct clk *clk;
+ void __iomem *io_base;
+ int ret;
+
+ io_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(io_base))
+ return PTR_ERR(io_base);
+
+ regmap = devm_regmap_init_mmio(dev, io_base, &axi_pwmgen_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "failed to init register map\n");
+
+ ret = axi_pwmgen_setup(regmap, dev);
+ if (ret < 0)
+ return ret;
+
+ chip = devm_pwmchip_alloc(dev, ret, sizeof(*ddata));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+ ddata = pwmchip_get_drvdata(chip);
+ ddata->regmap = regmap;
+
+ clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk), "failed to get clock\n");
+
+ ret = devm_clk_rate_exclusive_get(dev, clk);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get exclusive rate\n");
+
+ ddata->clk_rate_hz = clk_get_rate(clk);
+ if (!ddata->clk_rate_hz || ddata->clk_rate_hz > NSEC_PER_SEC)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid clock rate: %lu\n", ddata->clk_rate_hz);
+
+ chip->ops = &axi_pwmgen_pwm_ops;
+ chip->atomic = true;
+
+ ret = devm_pwmchip_add(dev, chip);
+ if (ret)
+ return dev_err_probe(dev, ret, "could not add PWM chip\n");
+
+ return 0;
+}
+
+static const struct of_device_id axi_pwmgen_ids[] = {
+ { .compatible = "adi,axi-pwmgen-2.00.a" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, axi_pwmgen_ids);
+
+static struct platform_driver axi_pwmgen_driver = {
+ .driver = {
+ .name = "axi-pwmgen",
+ .of_match_table = axi_pwmgen_ids,
+ },
+ .probe = axi_pwmgen_probe,
+};
+module_platform_driver(axi_pwmgen_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sergiu Cuciurean <sergiu.cuciurean@analog.com>");
+MODULE_AUTHOR("Trevor Gamblin <tgamblin@baylibre.com>");
+MODULE_DESCRIPTION("Driver for the Analog Devices AXI PWM generator");
diff --git a/drivers/pwm/pwm-cros-ec.c b/drivers/pwm/pwm-cros-ec.c
index 606ccfdaf4cc..189301dc395e 100644
--- a/drivers/pwm/pwm-cros-ec.c
+++ b/drivers/pwm/pwm-cros-ec.c
@@ -20,20 +20,10 @@
*
* @ec: Pointer to EC device
* @use_pwm_type: Use PWM types instead of generic channels
- * @channel: array with per-channel data
*/
struct cros_ec_pwm_device {
struct cros_ec_device *ec;
bool use_pwm_type;
- struct cros_ec_pwm *channel;
-};
-
-/**
- * struct cros_ec_pwm - per-PWM driver data
- * @duty_cycle: cached duty cycle
- */
-struct cros_ec_pwm {
- u16 duty_cycle;
};
static inline struct cros_ec_pwm_device *pwm_to_cros_ec_pwm(struct pwm_chip *chip)
@@ -135,7 +125,6 @@ static int cros_ec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
const struct pwm_state *state)
{
struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip);
- struct cros_ec_pwm *channel = &ec_pwm->channel[pwm->hwpwm];
u16 duty_cycle;
int ret;
@@ -156,8 +145,6 @@ static int cros_ec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
if (ret < 0)
return ret;
- channel->duty_cycle = state->duty_cycle;
-
return 0;
}
@@ -165,7 +152,6 @@ static int cros_ec_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_state *state)
{
struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip);
- struct cros_ec_pwm *channel = &ec_pwm->channel[pwm->hwpwm];
int ret;
ret = cros_ec_pwm_get_duty(ec_pwm->ec, ec_pwm->use_pwm_type, pwm->hwpwm);
@@ -175,44 +161,13 @@ static int cros_ec_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
}
state->enabled = (ret > 0);
+ state->duty_cycle = ret;
state->period = EC_PWM_MAX_DUTY;
state->polarity = PWM_POLARITY_NORMAL;
- /*
- * Note that "disabled" and "duty cycle == 0" are treated the same. If
- * the cached duty cycle is not zero, used the cached duty cycle. This
- * ensures that the configured duty cycle is kept across a disable and
- * enable operation and avoids potentially confusing consumers.
- *
- * For the case of the initial hardware readout, channel->duty_cycle
- * will be 0 and the actual duty cycle read from the EC is used.
- */
- if (ret == 0 && channel->duty_cycle > 0)
- state->duty_cycle = channel->duty_cycle;
- else
- state->duty_cycle = ret;
-
return 0;
}
-static struct pwm_device *
-cros_ec_pwm_xlate(struct pwm_chip *chip, const struct of_phandle_args *args)
-{
- struct pwm_device *pwm;
-
- if (args->args[0] >= chip->npwm)
- return ERR_PTR(-EINVAL);
-
- pwm = pwm_request_from_chip(chip, args->args[0], NULL);
- if (IS_ERR(pwm))
- return pwm;
-
- /* The EC won't let us change the period */
- pwm->args.period = EC_PWM_MAX_DUTY;
-
- return pwm;
-}
-
static const struct pwm_ops cros_ec_pwm_ops = {
.get_state = cros_ec_pwm_get_state,
.apply = cros_ec_pwm_apply,
@@ -263,7 +218,7 @@ static int cros_ec_pwm_probe(struct platform_device *pdev)
struct cros_ec_pwm_device *ec_pwm;
struct pwm_chip *chip;
bool use_pwm_type = false;
- unsigned int npwm;
+ unsigned int i, npwm;
int ret;
if (!ec)
@@ -289,12 +244,17 @@ static int cros_ec_pwm_probe(struct platform_device *pdev)
/* PWM chip */
chip->ops = &cros_ec_pwm_ops;
- chip->of_xlate = cros_ec_pwm_xlate;
- ec_pwm->channel = devm_kcalloc(dev, chip->npwm, sizeof(*ec_pwm->channel),
- GFP_KERNEL);
- if (!ec_pwm->channel)
- return -ENOMEM;
+ /*
+ * The device tree binding for this device is special as it only uses a
+ * single cell (for the hwid) and so doesn't provide a default period.
+ * This isn't a big problem though as the hardware only supports a
+ * single period length, it's just a bit ugly to make this fit into the
+ * pwm core abstractions. So initialize the period here, as
+ * of_pwm_xlate_with_flags() won't do that for us.
+ */
+ for (i = 0; i < npwm; ++i)
+ chip->pwms[i].args.period = EC_PWM_MAX_DUTY;
dev_dbg(dev, "Probed %u PWMs\n", chip->npwm);
diff --git a/drivers/pwm/pwm-gpio.c b/drivers/pwm/pwm-gpio.c
new file mode 100644
index 000000000000..9f8884ac7504
--- /dev/null
+++ b/drivers/pwm/pwm-gpio.c
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Generic software PWM for modulating GPIOs
+ *
+ * Copyright (C) 2020 Axis Communications AB
+ * Copyright (C) 2020 Nicola Di Lieto
+ * Copyright (C) 2024 Stefan Wahren
+ * Copyright (C) 2024 Linus Walleij
+ */
+
+#include <linux/cleanup.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/hrtimer.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/pwm.h>
+#include <linux/spinlock.h>
+#include <linux/time.h>
+#include <linux/types.h>
+
+struct pwm_gpio {
+ struct hrtimer gpio_timer;
+ struct gpio_desc *gpio;
+ struct pwm_state state;
+ struct pwm_state next_state;
+
+ /* Protect internal state between pwm_ops and hrtimer */
+ spinlock_t lock;
+
+ bool changing;
+ bool running;
+ bool level;
+};
+
+static void pwm_gpio_round(struct pwm_state *dest, const struct pwm_state *src)
+{
+ u64 dividend;
+ u32 remainder;
+
+ *dest = *src;
+
+ /* Round down to hrtimer resolution */
+ dividend = dest->period;
+ remainder = do_div(dividend, hrtimer_resolution);
+ dest->period -= remainder;
+
+ dividend = dest->duty_cycle;
+ remainder = do_div(dividend, hrtimer_resolution);
+ dest->duty_cycle -= remainder;
+}
+
+static u64 pwm_gpio_toggle(struct pwm_gpio *gpwm, bool level)
+{
+ const struct pwm_state *state = &gpwm->state;
+ bool invert = state->polarity == PWM_POLARITY_INVERSED;
+
+ gpwm->level = level;
+ gpiod_set_value(gpwm->gpio, gpwm->level ^ invert);
+
+ if (!state->duty_cycle || state->duty_cycle == state->period) {
+ gpwm->running = false;
+ return 0;
+ }
+
+ gpwm->running = true;
+ return level ? state->duty_cycle : state->period - state->duty_cycle;
+}
+
+static enum hrtimer_restart pwm_gpio_timer(struct hrtimer *gpio_timer)
+{
+ struct pwm_gpio *gpwm = container_of(gpio_timer, struct pwm_gpio,
+ gpio_timer);
+ u64 next_toggle;
+ bool new_level;
+
+ guard(spinlock_irqsave)(&gpwm->lock);
+
+ /* Apply new state at end of current period */
+ if (!gpwm->level && gpwm->changing) {
+ gpwm->changing = false;
+ gpwm->state = gpwm->next_state;
+ new_level = !!gpwm->state.duty_cycle;
+ } else {
+ new_level = !gpwm->level;
+ }
+
+ next_toggle = pwm_gpio_toggle(gpwm, new_level);
+ if (next_toggle)
+ hrtimer_forward(gpio_timer, hrtimer_get_expires(gpio_timer),
+ ns_to_ktime(next_toggle));
+
+ return next_toggle ? HRTIMER_RESTART : HRTIMER_NORESTART;
+}
+
+static int pwm_gpio_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+ const struct pwm_state *state)
+{
+ struct pwm_gpio *gpwm = pwmchip_get_drvdata(chip);
+ bool invert = state->polarity == PWM_POLARITY_INVERSED;
+
+ if (state->duty_cycle && state->duty_cycle < hrtimer_resolution)
+ return -EINVAL;
+
+ if (state->duty_cycle != state->period &&
+ (state->period - state->duty_cycle < hrtimer_resolution))
+ return -EINVAL;
+
+ if (!state->enabled) {
+ hrtimer_cancel(&gpwm->gpio_timer);
+ } else if (!gpwm->running) {
+ int ret;
+
+ /*
+ * This just enables the output, but pwm_gpio_toggle()
+ * really starts the duty cycle.
+ */
+ ret = gpiod_direction_output(gpwm->gpio, invert);
+ if (ret)
+ return ret;
+ }
+
+ guard(spinlock_irqsave)(&gpwm->lock);
+
+ if (!state->enabled) {
+ pwm_gpio_round(&gpwm->state, state);
+ gpwm->running = false;
+ gpwm->changing = false;
+
+ gpiod_set_value(gpwm->gpio, invert);
+ } else if (gpwm->running) {
+ pwm_gpio_round(&gpwm->next_state, state);
+ gpwm->changing = true;
+ } else {
+ unsigned long next_toggle;
+
+ pwm_gpio_round(&gpwm->state, state);
+ gpwm->changing = false;
+
+ next_toggle = pwm_gpio_toggle(gpwm, !!state->duty_cycle);
+ if (next_toggle)
+ hrtimer_start(&gpwm->gpio_timer, next_toggle,
+ HRTIMER_MODE_REL);
+ }
+
+ return 0;
+}
+
+static int pwm_gpio_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+ struct pwm_state *state)
+{
+ struct pwm_gpio *gpwm = pwmchip_get_drvdata(chip);
+
+ guard(spinlock_irqsave)(&gpwm->lock);
+
+ if (gpwm->changing)
+ *state = gpwm->next_state;
+ else
+ *state = gpwm->state;
+
+ return 0;
+}
+
+static const struct pwm_ops pwm_gpio_ops = {
+ .apply = pwm_gpio_apply,
+ .get_state = pwm_gpio_get_state,
+};
+
+static void pwm_gpio_disable_hrtimer(void *data)
+{
+ struct pwm_gpio *gpwm = data;
+
+ hrtimer_cancel(&gpwm->gpio_timer);
+}
+
+static int pwm_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pwm_chip *chip;
+ struct pwm_gpio *gpwm;
+ int ret;
+
+ chip = devm_pwmchip_alloc(dev, 1, sizeof(*gpwm));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+
+ gpwm = pwmchip_get_drvdata(chip);
+
+ spin_lock_init(&gpwm->lock);
+
+ gpwm->gpio = devm_gpiod_get(dev, NULL, GPIOD_ASIS);
+ if (IS_ERR(gpwm->gpio))
+ return dev_err_probe(dev, PTR_ERR(gpwm->gpio),
+ "%pfw: could not get gpio\n",
+ dev_fwnode(dev));
+
+ if (gpiod_cansleep(gpwm->gpio))
+ return dev_err_probe(dev, -EINVAL,
+ "%pfw: sleeping GPIO not supported\n",
+ dev_fwnode(dev));
+
+ chip->ops = &pwm_gpio_ops;
+ chip->atomic = true;
+
+ hrtimer_init(&gpwm->gpio_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ret = devm_add_action_or_reset(dev, pwm_gpio_disable_hrtimer, gpwm);
+ if (ret)
+ return ret;
+
+ gpwm->gpio_timer.function = pwm_gpio_timer;
+
+ ret = pwmchip_add(chip);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "could not add pwmchip\n");
+
+ return 0;
+}
+
+static const struct of_device_id pwm_gpio_dt_ids[] = {
+ { .compatible = "pwm-gpio" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, pwm_gpio_dt_ids);
+
+static struct platform_driver pwm_gpio_driver = {
+ .driver = {
+ .name = "pwm-gpio",
+ .of_match_table = pwm_gpio_dt_ids,
+ },
+ .probe = pwm_gpio_probe,
+};
+module_platform_driver(pwm_gpio_driver);
+
+MODULE_DESCRIPTION("PWM GPIO driver");
+MODULE_AUTHOR("Vincent Whitchurch");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pwm/pwm-imx-tpm.c b/drivers/pwm/pwm-imx-tpm.c
index c50ddbac43c8..96ea343856f0 100644
--- a/drivers/pwm/pwm-imx-tpm.c
+++ b/drivers/pwm/pwm-imx-tpm.c
@@ -20,6 +20,7 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/slab.h>
@@ -380,6 +381,7 @@ static int pwm_imx_tpm_probe(struct platform_device *pdev)
static int pwm_imx_tpm_suspend(struct device *dev)
{
struct imx_tpm_pwm_chip *tpm = dev_get_drvdata(dev);
+ int ret;
if (tpm->enable_count > 0)
return -EBUSY;
@@ -393,7 +395,11 @@ static int pwm_imx_tpm_suspend(struct device *dev)
clk_disable_unprepare(tpm->clk);
- return 0;
+ ret = pinctrl_pm_select_sleep_state(dev);
+ if (ret)
+ clk_prepare_enable(tpm->clk);
+
+ return ret;
}
static int pwm_imx_tpm_resume(struct device *dev)
@@ -401,9 +407,15 @@ static int pwm_imx_tpm_resume(struct device *dev)
struct imx_tpm_pwm_chip *tpm = dev_get_drvdata(dev);
int ret = 0;
- ret = clk_prepare_enable(tpm->clk);
+ ret = pinctrl_pm_select_default_state(dev);
if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(tpm->clk);
+ if (ret) {
dev_err(dev, "failed to prepare or enable clock: %d\n", ret);
+ pinctrl_pm_select_sleep_state(dev);
+ }
return ret;
}
diff --git a/drivers/pwm/pwm-imx1.c b/drivers/pwm/pwm-imx1.c
index 1d2aae2d278f..d5535d208005 100644
--- a/drivers/pwm/pwm-imx1.c
+++ b/drivers/pwm/pwm-imx1.c
@@ -194,5 +194,6 @@ static struct platform_driver pwm_imx1_driver = {
};
module_platform_driver(pwm_imx1_driver);
+MODULE_DESCRIPTION("i.MX1 and i.MX21 Pulse Width Modulator driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c
index e1412116ef65..9e2bbf5b4a8c 100644
--- a/drivers/pwm/pwm-imx27.c
+++ b/drivers/pwm/pwm-imx27.c
@@ -352,5 +352,6 @@ static struct platform_driver imx_pwm_driver = {
};
module_platform_driver(imx_pwm_driver);
+MODULE_DESCRIPTION("i.MX27 and later i.MX SoCs Pulse Width Modulator driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
diff --git a/drivers/pwm/pwm-intel-lgm.c b/drivers/pwm/pwm-intel-lgm.c
index f9cc7c17c8f0..084c71a0a11b 100644
--- a/drivers/pwm/pwm-intel-lgm.c
+++ b/drivers/pwm/pwm-intel-lgm.c
@@ -230,4 +230,5 @@ static struct platform_driver lgm_pwm_driver = {
};
module_platform_driver(lgm_pwm_driver);
+MODULE_DESCRIPTION("Intel LGM Pulse Width Modulator driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c
index da4bf543d357..6bdb01619380 100644
--- a/drivers/pwm/pwm-jz4740.c
+++ b/drivers/pwm/pwm-jz4740.c
@@ -201,12 +201,11 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
* state instead of its inactive state.
*/
if ((state->polarity == PWM_POLARITY_NORMAL) ^ state->enabled)
- regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
- TCU_TCSR_PWM_INITL_HIGH, 0);
+ regmap_clear_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
+ TCU_TCSR_PWM_INITL_HIGH);
else
- regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
- TCU_TCSR_PWM_INITL_HIGH,
- TCU_TCSR_PWM_INITL_HIGH);
+ regmap_set_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
+ TCU_TCSR_PWM_INITL_HIGH);
if (state->enabled)
jz4740_pwm_enable(chip, pwm);
diff --git a/drivers/pwm/pwm-lpss-pci.c b/drivers/pwm/pwm-lpss-pci.c
index 25045c229520..f7ece2809e6b 100644
--- a/drivers/pwm/pwm-lpss-pci.c
+++ b/drivers/pwm/pwm-lpss-pci.c
@@ -46,25 +46,6 @@ static void pwm_lpss_remove_pci(struct pci_dev *pdev)
pm_runtime_get_sync(&pdev->dev);
}
-static int pwm_lpss_runtime_suspend_pci(struct device *dev)
-{
- /*
- * The PCI core will handle transition to D3 automatically. We only
- * need to provide runtime PM hooks for that to happen.
- */
- return 0;
-}
-
-static int pwm_lpss_runtime_resume_pci(struct device *dev)
-{
- return 0;
-}
-
-static DEFINE_RUNTIME_DEV_PM_OPS(pwm_lpss_pci_pm,
- pwm_lpss_runtime_suspend_pci,
- pwm_lpss_runtime_resume_pci,
- NULL);
-
static const struct pci_device_id pwm_lpss_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x0ac8), (unsigned long)&pwm_lpss_bxt_info},
{ PCI_VDEVICE(INTEL, 0x0f08), (unsigned long)&pwm_lpss_byt_info},
@@ -84,9 +65,6 @@ static struct pci_driver pwm_lpss_driver_pci = {
.id_table = pwm_lpss_pci_ids,
.probe = pwm_lpss_probe_pci,
.remove = pwm_lpss_remove_pci,
- .driver = {
- .pm = pm_ptr(&pwm_lpss_pci_pm),
- },
};
module_pci_driver(pwm_lpss_driver_pci);
diff --git a/drivers/pwm/pwm-lpss-platform.c b/drivers/pwm/pwm-lpss-platform.c
index dbc9f5b17bdc..5130238a4567 100644
--- a/drivers/pwm/pwm-lpss-platform.c
+++ b/drivers/pwm/pwm-lpss-platform.c
@@ -55,14 +55,7 @@ static int pwm_lpss_probe_platform(struct platform_device *pdev)
DPM_FLAG_SMART_SUSPEND);
pm_runtime_set_active(&pdev->dev);
- pm_runtime_enable(&pdev->dev);
-
- return 0;
-}
-
-static void pwm_lpss_remove_platform(struct platform_device *pdev)
-{
- pm_runtime_disable(&pdev->dev);
+ return devm_pm_runtime_enable(&pdev->dev);
}
static const struct acpi_device_id pwm_lpss_acpi_match[] = {
@@ -80,7 +73,6 @@ static struct platform_driver pwm_lpss_driver_platform = {
.acpi_match_table = pwm_lpss_acpi_match,
},
.probe = pwm_lpss_probe_platform,
- .remove_new = pwm_lpss_remove_platform,
};
module_platform_driver(pwm_lpss_driver_platform);
diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c
index 19a87873ad60..01dfa0fab80a 100644
--- a/drivers/pwm/pwm-mediatek.c
+++ b/drivers/pwm/pwm-mediatek.c
@@ -395,4 +395,5 @@ static struct platform_driver pwm_mediatek_driver = {
module_platform_driver(pwm_mediatek_driver);
MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
+MODULE_DESCRIPTION("MediaTek general purpose Pulse Width Modulator driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c
index b2f97dfb01bb..98e6c1533312 100644
--- a/drivers/pwm/pwm-meson.c
+++ b/drivers/pwm/pwm-meson.c
@@ -460,6 +460,37 @@ static int meson_pwm_init_channels_meson8b_v2(struct pwm_chip *chip)
return meson_pwm_init_clocks_meson8b(chip, mux_parent_data);
}
+static void meson_pwm_s4_put_clk(void *data)
+{
+ struct clk *clk = data;
+
+ clk_put(clk);
+}
+
+static int meson_pwm_init_channels_s4(struct pwm_chip *chip)
+{
+ struct device *dev = pwmchip_parent(chip);
+ struct device_node *np = dev->of_node;
+ struct meson_pwm *meson = to_meson_pwm(chip);
+ int i, ret;
+
+ for (i = 0; i < MESON_NUM_PWMS; i++) {
+ meson->channels[i].clk = of_clk_get(np, i);
+ if (IS_ERR(meson->channels[i].clk))
+ return dev_err_probe(dev,
+ PTR_ERR(meson->channels[i].clk),
+ "Failed to get clk\n");
+
+ ret = devm_add_action_or_reset(dev, meson_pwm_s4_put_clk,
+ meson->channels[i].clk);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to add clk_put action\n");
+ }
+
+ return 0;
+}
+
static const struct meson_pwm_data pwm_meson8b_data = {
.parent_names = { "xtal", NULL, "fclk_div4", "fclk_div3" },
.channels_init = meson_pwm_init_channels_meson8b_legacy,
@@ -498,6 +529,10 @@ static const struct meson_pwm_data pwm_meson8_v2_data = {
.channels_init = meson_pwm_init_channels_meson8b_v2,
};
+static const struct meson_pwm_data pwm_s4_data = {
+ .channels_init = meson_pwm_init_channels_s4,
+};
+
static const struct of_device_id meson_pwm_matches[] = {
{
.compatible = "amlogic,meson8-pwm-v2",
@@ -536,6 +571,10 @@ static const struct of_device_id meson_pwm_matches[] = {
.compatible = "amlogic,meson-g12a-ao-pwm-cd",
.data = &pwm_g12a_ao_cd_data
},
+ {
+ .compatible = "amlogic,meson-s4-pwm",
+ .data = &pwm_s4_data
+ },
{},
};
MODULE_DEVICE_TABLE(of, meson_pwm_matches);
diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c
index bb7bb48b2e6d..430bd6a709e9 100644
--- a/drivers/pwm/pwm-pxa.c
+++ b/drivers/pwm/pwm-pxa.c
@@ -208,4 +208,5 @@ static struct platform_driver pwm_driver = {
module_platform_driver(pwm_driver);
+MODULE_DESCRIPTION("PXA Pulse Width Modulator driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c
index efb60c9f0cb3..7adf4f2b1049 100644
--- a/drivers/pwm/pwm-samsung.c
+++ b/drivers/pwm/pwm-samsung.c
@@ -644,6 +644,7 @@ static struct platform_driver pwm_samsung_driver = {
};
module_platform_driver(pwm_samsung_driver);
+MODULE_DESCRIPTION("Samsung Pulse Width Modulator driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tomasz Figa <tomasz.figa@gmail.com>");
MODULE_ALIAS("platform:samsung-pwm");
diff --git a/drivers/pwm/pwm-spear.c b/drivers/pwm/pwm-spear.c
index 6c6f3b38c835..4f372279f313 100644
--- a/drivers/pwm/pwm-spear.c
+++ b/drivers/pwm/pwm-spear.c
@@ -255,6 +255,7 @@ static struct platform_driver spear_pwm_driver = {
module_platform_driver(spear_pwm_driver);
+MODULE_DESCRIPTION("ST Microelectronics SPEAr Pulse Width Modulator driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Shiraz Hashim <shiraz.linux.kernel@gmail.com>");
MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.com>");
diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c
index a2f231d13a9f..fd754a99cf2e 100644
--- a/drivers/pwm/pwm-stm32.c
+++ b/drivers/pwm/pwm-stm32.c
@@ -321,22 +321,30 @@ static int stm32_pwm_config(struct stm32_pwm *priv, unsigned int ch,
* First we need to find the minimal value for prescaler such that
*
* period_ns * clkrate
- * ------------------------------
+ * ------------------------------ < max_arr + 1
* NSEC_PER_SEC * (prescaler + 1)
*
- * isn't bigger than max_arr.
+ * This equation is equivalent to
+ *
+ * period_ns * clkrate
+ * ---------------------------- < prescaler + 1
+ * NSEC_PER_SEC * (max_arr + 1)
+ *
+ * Using integer division and knowing that the right hand side is
+ * integer, this is further equivalent to
+ *
+ * (period_ns * clkrate) // (NSEC_PER_SEC * (max_arr + 1)) ≤ prescaler
*/
prescaler = mul_u64_u64_div_u64(period_ns, clk_get_rate(priv->clk),
- (u64)NSEC_PER_SEC * priv->max_arr);
- if (prescaler > 0)
- prescaler -= 1;
-
+ (u64)NSEC_PER_SEC * ((u64)priv->max_arr + 1));
if (prescaler > MAX_TIM_PSC)
return -EINVAL;
prd = mul_u64_u64_div_u64(period_ns, clk_get_rate(priv->clk),
(u64)NSEC_PER_SEC * (prescaler + 1));
+ if (!prd)
+ return -EINVAL;
/*
* All channels share the same prescaler and counter so when two
@@ -360,7 +368,7 @@ static int stm32_pwm_config(struct stm32_pwm *priv, unsigned int ch,
dty = mul_u64_u64_div_u64(duty_ns, clk_get_rate(priv->clk),
(u64)NSEC_PER_SEC * (prescaler + 1));
- regmap_write(priv->regmap, TIM_CCR1 + 4 * ch, dty);
+ regmap_write(priv->regmap, TIM_CCRx(ch + 1), dty);
/* Configure output mode */
shift = (ch & 0x1) * CCMR_CHANNEL_SHIFT;
@@ -382,9 +390,9 @@ static int stm32_pwm_set_polarity(struct stm32_pwm *priv, unsigned int ch,
{
u32 mask;
- mask = TIM_CCER_CC1P << (ch * 4);
+ mask = TIM_CCER_CCxP(ch + 1);
if (priv->have_complementary_output)
- mask |= TIM_CCER_CC1NP << (ch * 4);
+ mask |= TIM_CCER_CCxNP(ch + 1);
regmap_update_bits(priv->regmap, TIM_CCER, mask,
polarity == PWM_POLARITY_NORMAL ? 0 : mask);
@@ -402,9 +410,9 @@ static int stm32_pwm_enable(struct stm32_pwm *priv, unsigned int ch)
return ret;
/* Enable channel */
- mask = TIM_CCER_CC1E << (ch * 4);
+ mask = TIM_CCER_CCxE(ch + 1);
if (priv->have_complementary_output)
- mask |= TIM_CCER_CC1NE << (ch * 4);
+ mask |= TIM_CCER_CCxNE(ch);
regmap_set_bits(priv->regmap, TIM_CCER, mask);
@@ -422,9 +430,9 @@ static void stm32_pwm_disable(struct stm32_pwm *priv, unsigned int ch)
u32 mask;
/* Disable channel */
- mask = TIM_CCER_CC1E << (ch * 4);
+ mask = TIM_CCER_CCxE(ch + 1);
if (priv->have_complementary_output)
- mask |= TIM_CCER_CC1NE << (ch * 4);
+ mask |= TIM_CCER_CCxNE(ch + 1);
regmap_clear_bits(priv->regmap, TIM_CCER, mask);
@@ -444,8 +452,9 @@ static int stm32_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
enabled = pwm->state.enabled;
- if (enabled && !state->enabled) {
- stm32_pwm_disable(priv, pwm->hwpwm);
+ if (!state->enabled) {
+ if (enabled)
+ stm32_pwm_disable(priv, pwm->hwpwm);
return 0;
}
@@ -493,8 +502,8 @@ static int stm32_pwm_get_state(struct pwm_chip *chip,
if (ret)
goto out;
- state->enabled = ccer & (TIM_CCER_CC1E << (ch * 4));
- state->polarity = (ccer & (TIM_CCER_CC1P << (ch * 4))) ?
+ state->enabled = ccer & TIM_CCER_CCxE(ch + 1);
+ state->polarity = (ccer & TIM_CCER_CCxP(ch + 1)) ?
PWM_POLARITY_INVERSED : PWM_POLARITY_NORMAL;
ret = regmap_read(priv->regmap, TIM_PSC, &psc);
if (ret)
@@ -502,7 +511,7 @@ static int stm32_pwm_get_state(struct pwm_chip *chip,
ret = regmap_read(priv->regmap, TIM_ARR, &arr);
if (ret)
goto out;
- ret = regmap_read(priv->regmap, TIM_CCR1 + 4 * ch, &ccr);
+ ret = regmap_read(priv->regmap, TIM_CCRx(ch + 1), &ccr);
if (ret)
goto out;
@@ -673,7 +682,8 @@ static int stm32_pwm_probe(struct platform_device *pdev)
* .apply() won't overflow.
*/
if (clk_get_rate(priv->clk) > 1000000000)
- return dev_err_probe(dev, -EINVAL, "Failed to lock clock\n");
+ return dev_err_probe(dev, -EINVAL, "Clock freq too high (%lu)\n",
+ clk_get_rate(priv->clk));
chip->ops = &stm32pwm_ops;
@@ -702,7 +712,7 @@ static int stm32_pwm_suspend(struct device *dev)
ccer = active_channels(priv);
for (i = 0; i < chip->npwm; i++) {
- mask = TIM_CCER_CC1E << (i * 4);
+ mask = TIM_CCER_CCxE(i + 1);
if (ccer & mask) {
dev_err(dev, "PWM %u still in use by consumer %s\n",
i, chip->pwms[i].label);
diff --git a/drivers/pwm/pwm-visconti.c b/drivers/pwm/pwm-visconti.c
index 9e55380957be..28fae4979e3f 100644
--- a/drivers/pwm/pwm-visconti.c
+++ b/drivers/pwm/pwm-visconti.c
@@ -170,6 +170,7 @@ static struct platform_driver visconti_pwm_driver = {
};
module_platform_driver(visconti_pwm_driver);
+MODULE_DESCRIPTION("Toshiba Visconti Pulse Width Modulator driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>");
MODULE_ALIAS("platform:pwm-visconti");
diff --git a/drivers/pwm/pwm-xilinx.c b/drivers/pwm/pwm-xilinx.c
index 3a7deebb0d0c..52c241982807 100644
--- a/drivers/pwm/pwm-xilinx.c
+++ b/drivers/pwm/pwm-xilinx.c
@@ -224,7 +224,6 @@ static int xilinx_pwm_probe(struct platform_device *pdev)
if (IS_ERR(chip))
return PTR_ERR(chip);
priv = xilinx_pwm_chip_to_priv(chip);
- platform_set_drvdata(pdev, chip);
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
@@ -263,37 +262,24 @@ static int xilinx_pwm_probe(struct platform_device *pdev)
* alas, such properties are not allowed to be used.
*/
- priv->clk = devm_clk_get(dev, "s_axi_aclk");
+ priv->clk = devm_clk_get_enabled(dev, "s_axi_aclk");
if (IS_ERR(priv->clk))
return dev_err_probe(dev, PTR_ERR(priv->clk),
"Could not get clock\n");
- ret = clk_prepare_enable(priv->clk);
+ ret = devm_clk_rate_exclusive_get(dev, priv->clk);
if (ret)
- return dev_err_probe(dev, ret, "Clock enable failed\n");
- clk_rate_exclusive_get(priv->clk);
+ return dev_err_probe(dev, ret,
+ "Failed to lock clock rate\n");
chip->ops = &xilinx_pwm_ops;
- ret = pwmchip_add(chip);
- if (ret) {
- clk_rate_exclusive_put(priv->clk);
- clk_disable_unprepare(priv->clk);
+ ret = devm_pwmchip_add(dev, chip);
+ if (ret)
return dev_err_probe(dev, ret, "Could not register PWM chip\n");
- }
return 0;
}
-static void xilinx_pwm_remove(struct platform_device *pdev)
-{
- struct pwm_chip *chip = platform_get_drvdata(pdev);
- struct xilinx_timer_priv *priv = xilinx_pwm_chip_to_priv(chip);
-
- pwmchip_remove(chip);
- clk_rate_exclusive_put(priv->clk);
- clk_disable_unprepare(priv->clk);
-}
-
static const struct of_device_id xilinx_pwm_of_match[] = {
{ .compatible = "xlnx,xps-timer-1.00.a", },
{},
@@ -302,7 +288,6 @@ MODULE_DEVICE_TABLE(of, xilinx_pwm_of_match);
static struct platform_driver xilinx_pwm_driver = {
.probe = xilinx_pwm_probe,
- .remove_new = xilinx_pwm_remove,
.driver = {
.name = "xilinx-pwm",
.of_match_table = of_match_ptr(xilinx_pwm_of_match),