Commit cece6460 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'pwm/for-5.2-rc1' of...

Merge tag 'pwm/for-5.2-rc1' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm

Pull pwm updates from Thierry Reding:
 "Nothing out of the ordinary this cycle.

  The bulk of this is a collection of fixes for existing drivers and
  some cleanups. There's one new driver for i.MX SoCs and addition of
  support for some new variants to existing drivers"

* tag 'pwm/for-5.2-rc1' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm:
  pwm: meson: Add clock source configuration for Meson G12A
  dt-bindings: pwm: Update bindings for the Meson G12A Family
  pwm: samsung: Don't uses devm_*() functions in ->request()
  pwm: Clear chip_data in pwm_put()
  pwm: Add i.MX TPM PWM driver support
  dt-bindings: pwm: Add i.MX TPM PWM binding
  pwm: imx27: Use devm_platform_ioremap_resource() to simplify code
  pwm: meson: Use the spin-lock only to protect register modifications
  pwm: meson: Don't disable PWM when setting duty repeatedly
  pwm: meson: Consider 128 a valid pre-divider
  pwm: sysfs: fix typo "its" -> "it's"
  pwm: tiehrpwm: Enable compilation for ARCH_K3
  dt-bindings: pwm: tiehrpwm: Add TI AM654 SoC specific compatible
  pwm: tiehrpwm: Update shadow register for disabling PWMs
  pwm: img: Turn final 'else if' into 'else' in img_pwm_config
  pwm: Fix deadlock warning when removing PWM device
parents 15500c0a f41efceb
Freescale i.MX TPM PWM controller
Required properties:
- compatible : Should be "fsl,imx7ulp-pwm".
- reg: Physical base address and length of the controller's registers.
- #pwm-cells: Should be 3. See pwm.txt in this directory for a description of the cells format.
- clocks : The clock provided by the SoC to drive the PWM.
- interrupts: The interrupt for the PWM controller.
Note: The TPM counter and period counter are shared between multiple channels, so all channels
should use same period setting.
Example:
tpm4: pwm@40250000 {
compatible = "fsl,imx7ulp-pwm";
reg = <0x40250000 0x1000>;
assigned-clocks = <&pcc2 IMX7ULP_CLK_LPTPM4>;
assigned-clock-parents = <&scg1 IMX7ULP_CLK_SOSC_BUS_CLK>;
clocks = <&pcc2 IMX7ULP_CLK_LPTPM4>;
#pwm-cells = <3>;
};
...@@ -7,6 +7,9 @@ Required properties: ...@@ -7,6 +7,9 @@ Required properties:
or "amlogic,meson-gxbb-ao-pwm" or "amlogic,meson-gxbb-ao-pwm"
or "amlogic,meson-axg-ee-pwm" or "amlogic,meson-axg-ee-pwm"
or "amlogic,meson-axg-ao-pwm" or "amlogic,meson-axg-ao-pwm"
or "amlogic,meson-g12a-ee-pwm"
or "amlogic,meson-g12a-ao-pwm-ab"
or "amlogic,meson-g12a-ao-pwm-cd"
- #pwm-cells: Should be 3. See pwm.txt in this directory for a description of - #pwm-cells: Should be 3. See pwm.txt in this directory for a description of
the cells format. the cells format.
......
...@@ -4,6 +4,7 @@ Required properties: ...@@ -4,6 +4,7 @@ Required properties:
- compatible: Must be "ti,<soc>-ehrpwm". - compatible: Must be "ti,<soc>-ehrpwm".
for am33xx - compatible = "ti,am3352-ehrpwm", "ti,am33xx-ehrpwm"; for am33xx - compatible = "ti,am3352-ehrpwm", "ti,am33xx-ehrpwm";
for am4372 - compatible = "ti,am4372-ehrpwm", "ti-am3352-ehrpwm", "ti,am33xx-ehrpwm"; for am4372 - compatible = "ti,am4372-ehrpwm", "ti-am3352-ehrpwm", "ti,am33xx-ehrpwm";
for am654 - compatible = "ti,am654-ehrpwm", "ti-am3352-ehrpwm";
for da850 - compatible = "ti,da850-ehrpwm", "ti-am3352-ehrpwm", "ti,am33xx-ehrpwm"; for da850 - compatible = "ti,da850-ehrpwm", "ti-am3352-ehrpwm", "ti,am33xx-ehrpwm";
for dra746 - compatible = "ti,dra746-ehrpwm", "ti-am3352-ehrpwm"; for dra746 - compatible = "ti,dra746-ehrpwm", "ti-am3352-ehrpwm";
- #pwm-cells: should be 3. See pwm.txt in this directory for a description of - #pwm-cells: should be 3. See pwm.txt in this directory for a description of
......
...@@ -210,6 +210,17 @@ config PWM_IMX27 ...@@ -210,6 +210,17 @@ config PWM_IMX27
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called pwm-imx27. will be called pwm-imx27.
config PWM_IMX_TPM
tristate "i.MX TPM PWM support"
depends on ARCH_MXC || COMPILE_TEST
depends on HAVE_CLK && HAS_IOMEM
help
Generic PWM framework driver for i.MX7ULP TPM module, TPM's full
name is Low Power Timer/Pulse Width Modulation Module.
To compile this driver as a module, choose M here: the module
will be called pwm-imx-tpm.
config PWM_JZ4740 config PWM_JZ4740
tristate "Ingenic JZ47xx PWM support" tristate "Ingenic JZ47xx PWM support"
depends on MACH_INGENIC depends on MACH_INGENIC
...@@ -467,10 +478,9 @@ config PWM_TIECAP ...@@ -467,10 +478,9 @@ config PWM_TIECAP
config PWM_TIEHRPWM config PWM_TIEHRPWM
tristate "EHRPWM PWM support" tristate "EHRPWM PWM support"
depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_K3
help help
PWM driver support for the EHRPWM controller found on AM33XX PWM driver support for the EHRPWM controller found on TI SOCs
TI SOC
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called pwm-tiehrpwm. will be called pwm-tiehrpwm.
......
...@@ -19,6 +19,7 @@ obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o ...@@ -19,6 +19,7 @@ obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o
obj-$(CONFIG_PWM_IMG) += pwm-img.o obj-$(CONFIG_PWM_IMG) += pwm-img.o
obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o
obj-$(CONFIG_PWM_IMX27) += pwm-imx27.o obj-$(CONFIG_PWM_IMX27) += pwm-imx27.o
obj-$(CONFIG_PWM_IMX_TPM) += pwm-imx-tpm.o
obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o
obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o
obj-$(CONFIG_PWM_LPC18XX_SCT) += pwm-lpc18xx-sct.o obj-$(CONFIG_PWM_LPC18XX_SCT) += pwm-lpc18xx-sct.o
......
...@@ -311,10 +311,12 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip, ...@@ -311,10 +311,12 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
if (IS_ENABLED(CONFIG_OF)) if (IS_ENABLED(CONFIG_OF))
of_pwmchip_add(chip); of_pwmchip_add(chip);
pwmchip_sysfs_export(chip);
out: out:
mutex_unlock(&pwm_lock); mutex_unlock(&pwm_lock);
if (!ret)
pwmchip_sysfs_export(chip);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(pwmchip_add_with_polarity); EXPORT_SYMBOL_GPL(pwmchip_add_with_polarity);
...@@ -348,7 +350,7 @@ int pwmchip_remove(struct pwm_chip *chip) ...@@ -348,7 +350,7 @@ int pwmchip_remove(struct pwm_chip *chip)
unsigned int i; unsigned int i;
int ret = 0; int ret = 0;
pwmchip_sysfs_unexport_children(chip); pwmchip_sysfs_unexport(chip);
mutex_lock(&pwm_lock); mutex_lock(&pwm_lock);
...@@ -368,8 +370,6 @@ int pwmchip_remove(struct pwm_chip *chip) ...@@ -368,8 +370,6 @@ int pwmchip_remove(struct pwm_chip *chip)
free_pwms(chip); free_pwms(chip);
pwmchip_sysfs_unexport(chip);
out: out:
mutex_unlock(&pwm_lock); mutex_unlock(&pwm_lock);
return ret; return ret;
...@@ -877,6 +877,7 @@ void pwm_put(struct pwm_device *pwm) ...@@ -877,6 +877,7 @@ void pwm_put(struct pwm_device *pwm)
if (pwm->chip->ops->free) if (pwm->chip->ops->free)
pwm->chip->ops->free(pwm->chip, pwm); pwm->chip->ops->free(pwm->chip, pwm);
pwm_set_chip_data(pwm, NULL);
pwm->label = NULL; pwm->label = NULL;
module_put(pwm->chip->ops->owner); module_put(pwm->chip->ops->owner);
......
...@@ -84,7 +84,6 @@ static void berlin_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) ...@@ -84,7 +84,6 @@ static void berlin_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
{ {
struct berlin_pwm_channel *channel = pwm_get_chip_data(pwm); struct berlin_pwm_channel *channel = pwm_get_chip_data(pwm);
pwm_set_chip_data(pwm, NULL);
kfree(channel); kfree(channel);
} }
......
...@@ -123,7 +123,7 @@ static int img_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -123,7 +123,7 @@ static int img_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
} else if (mul <= max_timebase * 512) { } else if (mul <= max_timebase * 512) {
div = PWM_CTRL_CFG_SUB_DIV0_DIV1; div = PWM_CTRL_CFG_SUB_DIV0_DIV1;
timebase = DIV_ROUND_UP(mul, 512); timebase = DIV_ROUND_UP(mul, 512);
} else if (mul > max_timebase * 512) { } else {
dev_err(chip->dev, dev_err(chip->dev,
"failed to configure timebase steps/divider value\n"); "failed to configure timebase steps/divider value\n");
return -EINVAL; return -EINVAL;
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2018-2019 NXP.
*
* Limitations:
* - The TPM counter and period counter are shared between
* multiple channels, so all channels should use same period
* settings.
* - Changes to polarity cannot be latched at the time of the
* next period start.
* - Changing period and duty cycle together isn't atomic,
* with the wrong timing it might happen that a period is
* produced with old duty cycle but new period settings.
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/log2.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#define PWM_IMX_TPM_PARAM 0x4
#define PWM_IMX_TPM_GLOBAL 0x8
#define PWM_IMX_TPM_SC 0x10
#define PWM_IMX_TPM_CNT 0x14
#define PWM_IMX_TPM_MOD 0x18
#define PWM_IMX_TPM_CnSC(n) (0x20 + (n) * 0x8)
#define PWM_IMX_TPM_CnV(n) (0x24 + (n) * 0x8)
#define PWM_IMX_TPM_PARAM_CHAN GENMASK(7, 0)
#define PWM_IMX_TPM_SC_PS GENMASK(2, 0)
#define PWM_IMX_TPM_SC_CMOD GENMASK(4, 3)
#define PWM_IMX_TPM_SC_CMOD_INC_EVERY_CLK FIELD_PREP(PWM_IMX_TPM_SC_CMOD, 1)
#define PWM_IMX_TPM_SC_CPWMS BIT(5)
#define PWM_IMX_TPM_CnSC_CHF BIT(7)
#define PWM_IMX_TPM_CnSC_MSB BIT(5)
#define PWM_IMX_TPM_CnSC_MSA BIT(4)
/*
* The reference manual describes this field as two separate bits. The
* semantic of the two bits isn't orthogonal though, so they are treated
* together as a 2-bit field here.
*/
#define PWM_IMX_TPM_CnSC_ELS GENMASK(3, 2)
#define PWM_IMX_TPM_CnSC_ELS_INVERSED FIELD_PREP(PWM_IMX_TPM_CnSC_ELS, 1)
#define PWM_IMX_TPM_CnSC_ELS_NORMAL FIELD_PREP(PWM_IMX_TPM_CnSC_ELS, 2)
#define PWM_IMX_TPM_MOD_WIDTH 16
#define PWM_IMX_TPM_MOD_MOD GENMASK(PWM_IMX_TPM_MOD_WIDTH - 1, 0)
struct imx_tpm_pwm_chip {
struct pwm_chip chip;
struct clk *clk;
void __iomem *base;
struct mutex lock;
u32 user_count;
u32 enable_count;
u32 real_period;
};
struct imx_tpm_pwm_param {
u8 prescale;
u32 mod;
u32 val;
};
static inline struct imx_tpm_pwm_chip *
to_imx_tpm_pwm_chip(struct pwm_chip *chip)
{
return container_of(chip, struct imx_tpm_pwm_chip, chip);
}
/*
* This function determines for a given pwm_state *state that a consumer
* might request the pwm_state *real_state that eventually is implemented
* by the hardware and the necessary register values (in *p) to achieve
* this.
*/
static int pwm_imx_tpm_round_state(struct pwm_chip *chip,
struct imx_tpm_pwm_param *p,
struct pwm_state *real_state,
struct pwm_state *state)
{
struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip);
u32 rate, prescale, period_count, clock_unit;
u64 tmp;
rate = clk_get_rate(tpm->clk);
tmp = (u64)state->period * rate;
clock_unit = DIV_ROUND_CLOSEST_ULL(tmp, NSEC_PER_SEC);
if (clock_unit <= PWM_IMX_TPM_MOD_MOD)
prescale = 0;
else
prescale = ilog2(clock_unit) + 1 - PWM_IMX_TPM_MOD_WIDTH;
if ((!FIELD_FIT(PWM_IMX_TPM_SC_PS, prescale)))
return -ERANGE;
p->prescale = prescale;
period_count = (clock_unit + ((1 << prescale) >> 1)) >> prescale;
p->mod = period_count;
/* calculate real period HW can support */
tmp = (u64)period_count << prescale;
tmp *= NSEC_PER_SEC;
real_state->period = DIV_ROUND_CLOSEST_ULL(tmp, rate);
/*
* if eventually the PWM output is inactive, either
* duty cycle is 0 or status is disabled, need to
* make sure the output pin is inactive.
*/
if (!state->enabled)
real_state->duty_cycle = 0;
else
real_state->duty_cycle = state->duty_cycle;
tmp = (u64)p->mod * real_state->duty_cycle;
p->val = DIV_ROUND_CLOSEST_ULL(tmp, real_state->period);
real_state->polarity = state->polarity;
real_state->enabled = state->enabled;
return 0;
}
static void pwm_imx_tpm_get_state(struct pwm_chip *chip,
struct pwm_device *pwm,
struct pwm_state *state)
{
struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip);
u32 rate, val, prescale;
u64 tmp;
/* get period */
state->period = tpm->real_period;
/* get duty cycle */
rate = clk_get_rate(tpm->clk);
val = readl(tpm->base + PWM_IMX_TPM_SC);
prescale = FIELD_GET(PWM_IMX_TPM_SC_PS, val);
tmp = readl(tpm->base + PWM_IMX_TPM_CnV(pwm->hwpwm));
tmp = (tmp << prescale) * NSEC_PER_SEC;
state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, rate);
/* get polarity */
val = readl(tpm->base + PWM_IMX_TPM_CnSC(pwm->hwpwm));
if ((val & PWM_IMX_TPM_CnSC_ELS) == PWM_IMX_TPM_CnSC_ELS_INVERSED)
state->polarity = PWM_POLARITY_INVERSED;
else
/*
* Assume reserved values (2b00 and 2b11) to yield
* normal polarity.
*/
state->polarity = PWM_POLARITY_NORMAL;
/* get channel status */
state->enabled = FIELD_GET(PWM_IMX_TPM_CnSC_ELS, val) ? true : false;
}
/* this function is supposed to be called with mutex hold */
static int pwm_imx_tpm_apply_hw(struct pwm_chip *chip,
struct imx_tpm_pwm_param *p,
struct pwm_state *state,
struct pwm_device *pwm)
{
struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip);
bool period_update = false;
bool duty_update = false;
u32 val, cmod, cur_prescale;
unsigned long timeout;
struct pwm_state c;
if (state->period != tpm->real_period) {
/*
* TPM counter is shared by multiple channels, so
* prescale and period can NOT be modified when
* there are multiple channels in use with different
* period settings.
*/
if (tpm->user_count > 1)
return -EBUSY;
val = readl(tpm->base + PWM_IMX_TPM_SC);
cmod = FIELD_GET(PWM_IMX_TPM_SC_CMOD, val);
cur_prescale = FIELD_GET(PWM_IMX_TPM_SC_PS, val);
if (cmod && cur_prescale != p->prescale)
return -EBUSY;
/* set TPM counter prescale */
val &= ~PWM_IMX_TPM_SC_PS;
val |= FIELD_PREP(PWM_IMX_TPM_SC_PS, p->prescale);
writel(val, tpm->base + PWM_IMX_TPM_SC);
/*
* set period count:
* if the PWM is disabled (CMOD[1:0] = 2b00), then MOD register
* is updated when MOD register is written.
*
* if the PWM is enabled (CMOD[1:0] ≠ 2b00), the period length
* is latched into hardware when the next period starts.
*/
writel(p->mod, tpm->base + PWM_IMX_TPM_MOD);
tpm->real_period = state->period;
period_update = true;
}
pwm_imx_tpm_get_state(chip, pwm, &c);
/* polarity is NOT allowed to be changed if PWM is active */
if (c.enabled && c.polarity != state->polarity)
return -EBUSY;
if (state->duty_cycle != c.duty_cycle) {
/*
* set channel value:
* if the PWM is disabled (CMOD[1:0] = 2b00), then CnV register
* is updated when CnV register is written.
*
* if the PWM is enabled (CMOD[1:0] ≠ 2b00), the duty length
* is latched into hardware when the next period starts.
*/
writel(p->val, tpm->base + PWM_IMX_TPM_CnV(pwm->hwpwm));
duty_update = true;
}
/* make sure MOD & CnV registers are updated */
if (period_update || duty_update) {
timeout = jiffies + msecs_to_jiffies(tpm->real_period /
NSEC_PER_MSEC + 1);
while (readl(tpm->base + PWM_IMX_TPM_MOD) != p->mod
|| readl(tpm->base + PWM_IMX_TPM_CnV(pwm->hwpwm))
!= p->val) {
if (time_after(jiffies, timeout))
return -ETIME;
cpu_relax();
}
}
/*
* polarity settings will enabled/disable output status
* immediately, so if the channel is disabled, need to
* make sure MSA/MSB/ELS are set to 0 which means channel
* disabled.
*/
val = readl(tpm->base + PWM_IMX_TPM_CnSC(pwm->hwpwm));
val &= ~(PWM_IMX_TPM_CnSC_ELS | PWM_IMX_TPM_CnSC_MSA |
PWM_IMX_TPM_CnSC_MSB);
if (state->enabled) {
/*
* set polarity (for edge-aligned PWM modes)
*
* ELS[1:0] = 2b10 yields normal polarity behaviour,
* ELS[1:0] = 2b01 yields inversed polarity.
* The other values are reserved.
*/
val |= PWM_IMX_TPM_CnSC_MSB;
val |= (state->polarity == PWM_POLARITY_NORMAL) ?
PWM_IMX_TPM_CnSC_ELS_NORMAL :
PWM_IMX_TPM_CnSC_ELS_INVERSED;
}
writel(val, tpm->base + PWM_IMX_TPM_CnSC(pwm->hwpwm));
/* control the counter status */
if (state->enabled != c.enabled) {
val = readl(tpm->base + PWM_IMX_TPM_SC);
if (state->enabled) {
if (++tpm->enable_count == 1)
val |= PWM_IMX_TPM_SC_CMOD_INC_EVERY_CLK;
} else {
if (--tpm->enable_count == 0)
val &= ~PWM_IMX_TPM_SC_CMOD;
}
writel(val, tpm->base + PWM_IMX_TPM_SC);
}
return 0;
}
static int pwm_imx_tpm_apply(struct pwm_chip *chip,
struct pwm_device *pwm,
struct pwm_state *state)
{
struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip);
struct imx_tpm_pwm_param param;
struct pwm_state real_state;
int ret;
ret = pwm_imx_tpm_round_state(chip, &param, &real_state, state);
if (ret)
return ret;
mutex_lock(&tpm->lock);
ret = pwm_imx_tpm_apply_hw(chip, &param, &real_state, pwm);
mutex_unlock(&tpm->lock);
return ret;
}
static int pwm_imx_tpm_request(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip);
mutex_lock(&tpm->lock);
tpm->user_count++;
mutex_unlock(&tpm->lock);
return 0;
}
static void pwm_imx_tpm_free(struct pwm_chip *chip, struct pwm_device *pwm)
{
struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip);
mutex_lock(&tpm->lock);
tpm->user_count--;
mutex_unlock(&tpm->lock);
}
static const struct pwm_ops imx_tpm_pwm_ops = {
.request = pwm_imx_tpm_request,
.free = pwm_imx_tpm_free,
.get_state = pwm_imx_tpm_get_state,
.apply = pwm_imx_tpm_apply,
.owner = THIS_MODULE,
};
static int pwm_imx_tpm_probe(struct platform_device *pdev)
{
struct imx_tpm_pwm_chip *tpm;
int ret;
u32 val;
tpm = devm_kzalloc(&pdev->dev, sizeof(*tpm), GFP_KERNEL);
if (!tpm)
return -ENOMEM;
platform_set_drvdata(pdev, tpm);
tpm->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(tpm->base))
return PTR_ERR(tpm->base);
tpm->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(tpm->clk)) {
ret = PTR_ERR(tpm->clk);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev,
"failed to get PWM clock: %d\n", ret);
return ret;
}
ret = clk_prepare_enable(tpm->clk);
if (ret) {
dev_err(&pdev->dev,
"failed to prepare or enable clock: %d\n", ret);
return ret;
}
tpm->chip.dev = &pdev->dev;
tpm->chip.ops = &imx_tpm_pwm_ops;
tpm->chip.base = -1;
tpm->chip.of_xlate = of_pwm_xlate_with_flags;
tpm->chip.of_pwm_n_cells = 3;
/* get number of channels */
val = readl(tpm->base + PWM_IMX_TPM_PARAM);
tpm->chip.npwm = FIELD_GET(PWM_IMX_TPM_PARAM_CHAN, val);
mutex_init(&tpm->lock);
ret = pwmchip_add(&tpm->chip);
if (ret) {
dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
clk_disable_unprepare(tpm->clk);
}
return ret;
}
static int pwm_imx_tpm_remove(struct platform_device *pdev)
{
struct imx_tpm_pwm_chip *tpm = platform_get_drvdata(pdev);
int ret = pwmchip_remove(&tpm->chip);
clk_disable_unprepare(tpm->clk);
return ret;
}
static int __maybe_unused pwm_imx_tpm_suspend(struct device *dev)
{
struct imx_tpm_pwm_chip *tpm = dev_get_drvdata(dev);
if (tpm->enable_count > 0)
return -EBUSY;
clk_disable_unprepare(tpm->clk);
return 0;
}
static int __maybe_unused 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);
if (ret)
dev_err(dev,
"failed to prepare or enable clock: %d\n",
ret);
return ret;
}
static SIMPLE_DEV_PM_OPS(imx_tpm_pwm_pm,
pwm_imx_tpm_suspend, pwm_imx_tpm_resume);
static const struct of_device_id imx_tpm_pwm_dt_ids[] = {
{ .compatible = "fsl,imx7ulp-pwm", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_tpm_pwm_dt_ids);
static struct platform_driver imx_tpm_pwm_driver = {
.driver = {
.name = "imx7ulp-tpm-pwm",
.of_match_table = imx_tpm_pwm_dt_ids,
.pm = &imx_tpm_pwm_pm,
},
.probe = pwm_imx_tpm_probe,
.remove = pwm_imx_tpm_remove,
};
module_platform_driver(imx_tpm_pwm_driver);
MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
MODULE_DESCRIPTION("i.MX TPM PWM Driver");
MODULE_LICENSE("GPL v2");
...@@ -291,7 +291,6 @@ MODULE_DEVICE_TABLE(of, pwm_imx27_dt_ids); ...@@ -291,7 +291,6 @@ MODULE_DEVICE_TABLE(of, pwm_imx27_dt_ids);
static int pwm_imx27_probe(struct platform_device *pdev) static int pwm_imx27_probe(struct platform_device *pdev)
{ {
struct pwm_imx27_chip *imx; struct pwm_imx27_chip *imx;
struct resource *r;
imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL); imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL);
if (imx == NULL) if (imx == NULL)
...@@ -326,8 +325,7 @@ static int pwm_imx27_probe(struct platform_device *pdev) ...@@ -326,8 +325,7 @@ static int pwm_imx27_probe(struct platform_device *pdev)
imx->chip.of_xlate = of_pwm_xlate_with_flags; imx->chip.of_xlate = of_pwm_xlate_with_flags;
imx->chip.of_pwm_n_cells = 3; imx->chip.of_pwm_n_cells = 3;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0); imx->mmio_base = devm_platform_ioremap_resource(pdev, 0);
imx->mmio_base = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(imx->mmio_base)) if (IS_ERR(imx->mmio_base))
return PTR_ERR(imx->mmio_base); return PTR_ERR(imx->mmio_base);
......
...@@ -111,6 +111,10 @@ struct meson_pwm { ...@@ -111,6 +111,10 @@ struct meson_pwm {
const struct meson_pwm_data *data; const struct meson_pwm_data *data;
void __iomem *base; void __iomem *base;
u8 inverter_mask; u8 inverter_mask;
/*
* Protects register (write) access to the REG_MISC_AB register
* that is shared between the two PWMs.
*/
spinlock_t lock; spinlock_t lock;
}; };
...@@ -184,7 +188,7 @@ static int meson_pwm_calc(struct meson_pwm *meson, ...@@ -184,7 +188,7 @@ static int meson_pwm_calc(struct meson_pwm *meson,
do_div(fin_ps, fin_freq); do_div(fin_ps, fin_freq);
/* Calc pre_div with the period */ /* Calc pre_div with the period */
for (pre_div = 0; pre_div < MISC_CLK_DIV_MASK; pre_div++) { for (pre_div = 0; pre_div <= MISC_CLK_DIV_MASK; pre_div++) {
cnt = DIV_ROUND_CLOSEST_ULL((u64)period * 1000, cnt = DIV_ROUND_CLOSEST_ULL((u64)period * 1000,
fin_ps * (pre_div + 1)); fin_ps * (pre_div + 1));
dev_dbg(meson->chip.dev, "fin_ps=%llu pre_div=%u cnt=%u\n", dev_dbg(meson->chip.dev, "fin_ps=%llu pre_div=%u cnt=%u\n",
...@@ -193,7 +197,7 @@ static int meson_pwm_calc(struct meson_pwm *meson, ...@@ -193,7 +197,7 @@ static int meson_pwm_calc(struct meson_pwm *meson,
break; break;
} }
if (pre_div == MISC_CLK_DIV_MASK) { if (pre_div > MISC_CLK_DIV_MASK) {
dev_err(meson->chip.dev, "unable to get period pre_div\n"); dev_err(meson->chip.dev, "unable to get period pre_div\n");
return -EINVAL; return -EINVAL;
} }
...@@ -235,6 +239,7 @@ static void meson_pwm_enable(struct meson_pwm *meson, ...@@ -235,6 +239,7 @@ static void meson_pwm_enable(struct meson_pwm *meson,
{ {
u32 value, clk_shift, clk_enable, enable; u32 value, clk_shift, clk_enable, enable;
unsigned int offset; unsigned int offset;
unsigned long flags;
switch (id) { switch (id) {
case 0: case 0:
...@@ -255,6 +260,8 @@ static void meson_pwm_enable(struct meson_pwm *meson, ...@@ -255,6 +260,8 @@ static void meson_pwm_enable(struct meson_pwm *meson,
return; return;
} }
spin_lock_irqsave(&meson->lock, flags);
value = readl(meson->base + REG_MISC_AB); value = readl(meson->base + REG_MISC_AB);
value &= ~(MISC_CLK_DIV_MASK << clk_shift); value &= ~(MISC_CLK_DIV_MASK << clk_shift);
value |= channel->pre_div << clk_shift; value |= channel->pre_div << clk_shift;
...@@ -267,11 +274,14 @@ static void meson_pwm_enable(struct meson_pwm *meson, ...@@ -267,11 +274,14 @@ static void meson_pwm_enable(struct meson_pwm *meson,
value = readl(meson->base + REG_MISC_AB); value = readl(meson->base + REG_MISC_AB);
value |= enable; value |= enable;
writel(value, meson->base + REG_MISC_AB); writel(value, meson->base + REG_MISC_AB);
spin_unlock_irqrestore(&meson->lock, flags);
} }
static void meson_pwm_disable(struct meson_pwm *meson, unsigned int id) static void meson_pwm_disable(struct meson_pwm *meson, unsigned int id)
{ {
u32 value, enable; u32 value, enable;
unsigned long flags;
switch (id) { switch (id) {
case 0: case 0:
...@@ -286,9 +296,13 @@ static void meson_pwm_disable(struct meson_pwm *meson, unsigned int id) ...@@ -286,9 +296,13 @@ static void meson_pwm_disable(struct meson_pwm *meson, unsigned int id)
return; return;
} }
spin_lock_irqsave(&meson->lock, flags);
value = readl(meson->base + REG_MISC_AB); value = readl(meson->base + REG_MISC_AB);
value &= ~enable; value &= ~enable;
writel(value, meson->base + REG_MISC_AB); writel(value, meson->base + REG_MISC_AB);
spin_unlock_irqrestore(&meson->lock, flags);
} }
static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
...@@ -296,29 +310,21 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -296,29 +310,21 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
{ {
struct meson_pwm_channel *channel = pwm_get_chip_data(pwm); struct meson_pwm_channel *channel = pwm_get_chip_data(pwm);
struct meson_pwm *meson = to_meson_pwm(chip); struct meson_pwm *meson = to_meson_pwm(chip);
unsigned long flags;
int err = 0; int err = 0;
if (!state) if (!state)
return -EINVAL; return -EINVAL;
spin_lock_irqsave(&meson->lock, flags);
if (!state->enabled) { if (!state->enabled) {
meson_pwm_disable(meson, pwm->hwpwm); meson_pwm_disable(meson, pwm->hwpwm);
channel->state.enabled = false; channel->state.enabled = false;
goto unlock; return 0;
} }
if (state->period != channel->state.period || if (state->period != channel->state.period ||
state->duty_cycle != channel->state.duty_cycle || state->duty_cycle != channel->state.duty_cycle ||
state->polarity != channel->state.polarity) { state->polarity != channel->state.polarity) {
if (channel->state.enabled) {
meson_pwm_disable(meson, pwm->hwpwm);
channel->state.enabled = false;
}
if (state->polarity != channel->state.polarity) { if (state->polarity != channel->state.polarity) {
if (state->polarity == PWM_POLARITY_NORMAL) if (state->polarity == PWM_POLARITY_NORMAL)
meson->inverter_mask |= BIT(pwm->hwpwm); meson->inverter_mask |= BIT(pwm->hwpwm);
...@@ -329,7 +335,7 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -329,7 +335,7 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
err = meson_pwm_calc(meson, channel, pwm->hwpwm, err = meson_pwm_calc(meson, channel, pwm->hwpwm,
state->duty_cycle, state->period); state->duty_cycle, state->period);
if (err < 0) if (err < 0)
goto unlock; return err;
channel->state.polarity = state->polarity; channel->state.polarity = state->polarity;
channel->state.period = state->period; channel->state.period = state->period;
...@@ -341,9 +347,7 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -341,9 +347,7 @@ static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
channel->state.enabled = true; channel->state.enabled = true;
} }
unlock: return 0;
spin_unlock_irqrestore(&meson->lock, flags);
return err;
} }
static void meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, static void meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
...@@ -429,6 +433,24 @@ static const struct meson_pwm_data pwm_axg_ao_data = { ...@@ -429,6 +433,24 @@ static const struct meson_pwm_data pwm_axg_ao_data = {
.num_parents = ARRAY_SIZE(pwm_axg_ao_parent_names), .num_parents = ARRAY_SIZE(pwm_axg_ao_parent_names),
}; };
static const char * const pwm_g12a_ao_cd_parent_names[] = {
"aoclk81", "xtal",
};
static const struct meson_pwm_data pwm_g12a_ao_cd_data = {
.parent_names = pwm_g12a_ao_cd_parent_names,
.num_parents = ARRAY_SIZE(pwm_g12a_ao_cd_parent_names),
};
static const char * const pwm_g12a_ee_parent_names[] = {
"xtal", "hdmi_pll", "fclk_div4", "fclk_div3"
};
static const struct meson_pwm_data pwm_g12a_ee_data = {
.parent_names = pwm_g12a_ee_parent_names,
.num_parents = ARRAY_SIZE(pwm_g12a_ee_parent_names),
};
static const struct of_device_id meson_pwm_matches[] = { static const struct of_device_id meson_pwm_matches[] = {
{ {
.compatible = "amlogic,meson8b-pwm", .compatible = "amlogic,meson8b-pwm",
...@@ -450,6 +472,18 @@ static const struct of_device_id meson_pwm_matches[] = { ...@@ -450,6 +472,18 @@ static const struct of_device_id meson_pwm_matches[] = {
.compatible = "amlogic,meson-axg-ao-pwm", .compatible = "amlogic,meson-axg-ao-pwm",
.data = &pwm_axg_ao_data .data = &pwm_axg_ao_data
}, },
{
.compatible = "amlogic,meson-g12a-ee-pwm",
.data = &pwm_g12a_ee_data
},
{
.compatible = "amlogic,meson-g12a-ao-pwm-ab",
.data = &pwm_axg_ao_data
},
{
.compatible = "amlogic,meson-g12a-ao-pwm-cd",
.data = &pwm_g12a_ao_cd_data
},
{}, {},
}; };
MODULE_DEVICE_TABLE(of, meson_pwm_matches); MODULE_DEVICE_TABLE(of, meson_pwm_matches);
......
...@@ -176,7 +176,6 @@ static void pca9685_pwm_gpio_free(struct gpio_chip *gpio, unsigned int offset) ...@@ -176,7 +176,6 @@ static void pca9685_pwm_gpio_free(struct gpio_chip *gpio, unsigned int offset)
pm_runtime_put(pca->chip.dev); pm_runtime_put(pca->chip.dev);
mutex_lock(&pca->lock); mutex_lock(&pca->lock);
pwm = &pca->chip.pwms[offset]; pwm = &pca->chip.pwms[offset];
pwm_set_chip_data(pwm, NULL);
mutex_unlock(&pca->lock); mutex_unlock(&pca->lock);
} }
......
...@@ -226,7 +226,7 @@ static int pwm_samsung_request(struct pwm_chip *chip, struct pwm_device *pwm) ...@@ -226,7 +226,7 @@ static int pwm_samsung_request(struct pwm_chip *chip, struct pwm_device *pwm)
return -EINVAL; return -EINVAL;
} }
our_chan = devm_kzalloc(chip->dev, sizeof(*our_chan), GFP_KERNEL); our_chan = kzalloc(sizeof(*our_chan), GFP_KERNEL);
if (!our_chan) if (!our_chan)
return -ENOMEM; return -ENOMEM;
...@@ -237,8 +237,7 @@ static int pwm_samsung_request(struct pwm_chip *chip, struct pwm_device *pwm) ...@@ -237,8 +237,7 @@ static int pwm_samsung_request(struct pwm_chip *chip, struct pwm_device *pwm)
static void pwm_samsung_free(struct pwm_chip *chip, struct pwm_device *pwm) static void pwm_samsung_free(struct pwm_chip *chip, struct pwm_device *pwm)
{ {
devm_kfree(chip->dev, pwm_get_chip_data(pwm)); kfree(pwm_get_chip_data(pwm));
pwm_set_chip_data(pwm, NULL);
} }
static int pwm_samsung_enable(struct pwm_chip *chip, struct pwm_device *pwm) static int pwm_samsung_enable(struct pwm_chip *chip, struct pwm_device *pwm)
......
...@@ -382,6 +382,8 @@ static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) ...@@ -382,6 +382,8 @@ static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
} }
/* Update shadow register first before modifying active register */ /* Update shadow register first before modifying active register */
ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK,
AQSFRC_RLDCSF_ZRO);
ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val);
/* /*
* Changes to immediate action on Action Qualifier. This puts * Changes to immediate action on Action Qualifier. This puts
......
...@@ -398,7 +398,7 @@ void pwmchip_sysfs_export(struct pwm_chip *chip) ...@@ -398,7 +398,7 @@ void pwmchip_sysfs_export(struct pwm_chip *chip)
/* /*
* If device_create() fails the pwm_chip is still usable by * If device_create() fails the pwm_chip is still usable by
* the kernel its just not exported. * the kernel it's just not exported.
*/ */
parent = device_create(&pwm_class, chip->dev, MKDEV(0, 0), chip, parent = device_create(&pwm_class, chip->dev, MKDEV(0, 0), chip,
"pwmchip%d", chip->base); "pwmchip%d", chip->base);
...@@ -409,19 +409,6 @@ void pwmchip_sysfs_export(struct pwm_chip *chip) ...@@ -409,19 +409,6 @@ void pwmchip_sysfs_export(struct pwm_chip *chip)
} }
void pwmchip_sysfs_unexport(struct pwm_chip *chip) void pwmchip_sysfs_unexport(struct pwm_chip *chip)
{
struct device *parent;
parent = class_find_device(&pwm_class, NULL, chip,
pwmchip_sysfs_match);
if (parent) {
/* for class_find_device() */
put_device(parent);
device_unregister(parent);
}
}
void pwmchip_sysfs_unexport_children(struct pwm_chip *chip)
{ {
struct device *parent; struct device *parent;
unsigned int i; unsigned int i;
...@@ -439,6 +426,7 @@ void pwmchip_sysfs_unexport_children(struct pwm_chip *chip) ...@@ -439,6 +426,7 @@ void pwmchip_sysfs_unexport_children(struct pwm_chip *chip)
} }
put_device(parent); put_device(parent);
device_unregister(parent);
} }
static int __init pwm_sysfs_init(void) static int __init pwm_sysfs_init(void)
......
...@@ -596,7 +596,6 @@ static inline void pwm_remove_table(struct pwm_lookup *table, size_t num) ...@@ -596,7 +596,6 @@ static inline void pwm_remove_table(struct pwm_lookup *table, size_t num)
#ifdef CONFIG_PWM_SYSFS #ifdef CONFIG_PWM_SYSFS
void pwmchip_sysfs_export(struct pwm_chip *chip); void pwmchip_sysfs_export(struct pwm_chip *chip);
void pwmchip_sysfs_unexport(struct pwm_chip *chip); void pwmchip_sysfs_unexport(struct pwm_chip *chip);
void pwmchip_sysfs_unexport_children(struct pwm_chip *chip);
#else #else
static inline void pwmchip_sysfs_export(struct pwm_chip *chip) static inline void pwmchip_sysfs_export(struct pwm_chip *chip)
{ {
...@@ -605,10 +604,6 @@ static inline void pwmchip_sysfs_export(struct pwm_chip *chip) ...@@ -605,10 +604,6 @@ static inline void pwmchip_sysfs_export(struct pwm_chip *chip)
static inline void pwmchip_sysfs_unexport(struct pwm_chip *chip) static inline void pwmchip_sysfs_unexport(struct pwm_chip *chip)
{ {
} }
static inline void pwmchip_sysfs_unexport_children(struct pwm_chip *chip)
{
}
#endif /* CONFIG_PWM_SYSFS */ #endif /* CONFIG_PWM_SYSFS */
#endif /* __LINUX_PWM_H */ #endif /* __LINUX_PWM_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment