pwm: Changes for v5.6-rc1
This set of changes are mostly cleanups and minor improvements with some new chip support for some drivers. -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEEiOrDCAFJzPfAjcif3SOs138+s6EFAl46zmoZHHRoaWVycnku cmVkaW5nQGdtYWlsLmNvbQAKCRDdI6zXfz6zodGZEACQyt5Es8RbSMws6qmsZXSU XgVi1vCq3Mz51t2DMXp3PCe4Kb7MMTIn8nsFhQ6Q7z888ZCYC6auRTZ5kLNUZvwe guBqUR2+y2RkG0d9fWvf8xan2FqKuh33axX4YljNYI7lj79tyhQvHIC/B4DFes9y FnpvtDsFdVIFJmnDjgynWO5FhGbf7DdO6Mhmj99lQN3yMiUQ6RGmoXXrKoadcbNt vWGr5UqFY72PdVvMQFIA7U3ZeM5n8ocVI6TmvqHhX1ozLyB113yWXZr1hLH4LXzz f14NHygpo/GhB8ch3elxBZcXWPmPyn9tHxd2tYnlQ8whEY5RSD3C/bmVFMruhOLC LkcAmJ4mbk4tOVEoHB23vTcV5oxcVobDqIOj0W9n1pKWsx6Y3J31rIIg2BCPwSB2 o/xtFWafuvX9E90EVVM0H0mZSsxydZLXWOr17wzb1xlVulCpJCzvnhwJ8JXB6a0z 9/az3nGpOa6QB17IkR+sRkjVAnUFJ0CLHURIv+O7O+DBHVuykojdjpACzZcyy7kl 4ZrHhhCKwEbC4gjmtaJTgmQ0L/WGYfOBQKQmBKaQEJWXPUHsyJ4DFJjrIF4INqOe mS9BRvC0nRJ6g3ulOR95sVszMVrXKFUaMN6XqzyWzWPibdDk+1QR4EqU1QZQ/PgR 46RWEV02tG6SK65UzLqZxA== =x0aH -----END PGP SIGNATURE----- Merge tag 'pwm/for-5.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm Pull pwm updates from Thierry Reding: "Mostly cleanups and minor improvements with some new chip support for some drivers" * tag 'pwm/for-5.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm: (37 commits) pwm: Remove set but not set variable 'pwm' pwm: sun4i: Initialize variables before use pwm: stm32: Remove automatic output enable pwm: sun4i: Narrow scope of local variable pwm: bcm2835: Allow building for ARCH_BRCMSTB pwm: imx27: Eliminate error message for defer probe pwm: sun4i: Fix inconsistent IS_ERR and PTR_ERR pwm: sun4i: Move pwm_calculate() out of spin_lock() pwm: omap-dmtimer: Allow compiling with COMPILE_TEST pwm: omap-dmtimer: put_device() after of_find_device_by_node() pwm: omap-dmtimer: Simplify error handling pwm: omap-dmtimer: Remove PWM chip in .remove before making it unfunctional pwm: Implement tracing for .get_state() and .apply_state() pwm: rcar: Document inability to set duty_cycle = 0 pwm: rcar: Drop useless call to pwm_get_state() pwm: Fix minor Kconfig whitespace issues pwm: atmel: Implement .get_state() pwm: atmel: Use register accessors for channels pwm: atmel: Document known weaknesses of both hardware and software pwm: atmel: Replace loop in prescale calculation by ad-hoc calculation ...
This commit is contained in:
commit
4c7d00ccf4
|
@ -3,7 +3,7 @@ Freescale MXS PWM controller
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible: should be "fsl,imx23-pwm"
|
- compatible: should be "fsl,imx23-pwm"
|
||||||
- reg: physical base address and length of the controller's registers
|
- reg: physical base address and length of the controller's registers
|
||||||
- #pwm-cells: should be 2. See pwm.yaml in this directory for a description of
|
- #pwm-cells: should be 3. See pwm.yaml in this directory for a description of
|
||||||
the cells format.
|
the cells format.
|
||||||
- fsl,pwm-number: the number of PWM devices
|
- fsl,pwm-number: the number of PWM devices
|
||||||
|
|
||||||
|
@ -12,6 +12,6 @@ Example:
|
||||||
pwm: pwm@80064000 {
|
pwm: pwm@80064000 {
|
||||||
compatible = "fsl,imx28-pwm", "fsl,imx23-pwm";
|
compatible = "fsl,imx28-pwm", "fsl,imx23-pwm";
|
||||||
reg = <0x80064000 0x2000>;
|
reg = <0x80064000 0x2000>;
|
||||||
#pwm-cells = <2>;
|
#pwm-cells = <3>;
|
||||||
fsl,pwm-number = <8>;
|
fsl,pwm-number = <8>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -100,7 +100,7 @@ config PWM_BCM_KONA
|
||||||
|
|
||||||
config PWM_BCM2835
|
config PWM_BCM2835
|
||||||
tristate "BCM2835 PWM support"
|
tristate "BCM2835 PWM support"
|
||||||
depends on ARCH_BCM2835
|
depends on ARCH_BCM2835 || ARCH_BRCMSTB
|
||||||
help
|
help
|
||||||
PWM framework driver for BCM2835 controller (Raspberry Pi)
|
PWM framework driver for BCM2835 controller (Raspberry Pi)
|
||||||
|
|
||||||
|
@ -328,7 +328,8 @@ config PWM_MXS
|
||||||
|
|
||||||
config PWM_OMAP_DMTIMER
|
config PWM_OMAP_DMTIMER
|
||||||
tristate "OMAP Dual-Mode Timer PWM support"
|
tristate "OMAP Dual-Mode Timer PWM support"
|
||||||
depends on OF && ARCH_OMAP && OMAP_DM_TIMER
|
depends on OF
|
||||||
|
depends on OMAP_DM_TIMER || COMPILE_TEST
|
||||||
help
|
help
|
||||||
Generic PWM framework driver for OMAP Dual-Mode Timer PWM output
|
Generic PWM framework driver for OMAP Dual-Mode Timer PWM output
|
||||||
|
|
||||||
|
@ -490,7 +491,7 @@ config PWM_TEGRA
|
||||||
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-tegra.
|
will be called pwm-tegra.
|
||||||
|
|
||||||
config PWM_TIECAP
|
config PWM_TIECAP
|
||||||
tristate "ECAP PWM support"
|
tristate "ECAP PWM support"
|
||||||
depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_KEYSTONE || ARCH_K3
|
depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_KEYSTONE || ARCH_K3
|
||||||
help
|
help
|
||||||
|
@ -499,7 +500,7 @@ config PWM_TIECAP
|
||||||
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-tiecap.
|
will be called pwm-tiecap.
|
||||||
|
|
||||||
config PWM_TIEHRPWM
|
config PWM_TIEHRPWM
|
||||||
tristate "EHRPWM PWM support"
|
tristate "EHRPWM PWM support"
|
||||||
depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_K3
|
depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_K3
|
||||||
help
|
help
|
||||||
|
|
|
@ -20,6 +20,9 @@
|
||||||
|
|
||||||
#include <dt-bindings/pwm/pwm.h>
|
#include <dt-bindings/pwm/pwm.h>
|
||||||
|
|
||||||
|
#define CREATE_TRACE_POINTS
|
||||||
|
#include <trace/events/pwm.h>
|
||||||
|
|
||||||
#define MAX_PWMS 1024
|
#define MAX_PWMS 1024
|
||||||
|
|
||||||
static DEFINE_MUTEX(pwm_lookup_lock);
|
static DEFINE_MUTEX(pwm_lookup_lock);
|
||||||
|
@ -114,6 +117,11 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pwm->chip->ops->get_state) {
|
||||||
|
pwm->chip->ops->get_state(pwm->chip, pwm, &pwm->state);
|
||||||
|
trace_pwm_get(pwm, &pwm->state);
|
||||||
|
}
|
||||||
|
|
||||||
set_bit(PWMF_REQUESTED, &pwm->flags);
|
set_bit(PWMF_REQUESTED, &pwm->flags);
|
||||||
pwm->label = label;
|
pwm->label = label;
|
||||||
|
|
||||||
|
@ -283,9 +291,6 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
|
||||||
pwm->hwpwm = i;
|
pwm->hwpwm = i;
|
||||||
pwm->state.polarity = polarity;
|
pwm->state.polarity = polarity;
|
||||||
|
|
||||||
if (chip->ops->get_state)
|
|
||||||
chip->ops->get_state(chip, pwm, &pwm->state);
|
|
||||||
|
|
||||||
radix_tree_insert(&pwm_tree, pwm->pwm, pwm);
|
radix_tree_insert(&pwm_tree, pwm->pwm, pwm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -472,6 +477,8 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state)
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
trace_pwm_apply(pwm, state);
|
||||||
|
|
||||||
pwm->state = *state;
|
pwm->state = *state;
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -4,6 +4,19 @@
|
||||||
*
|
*
|
||||||
* Copyright (C) 2013 Atmel Corporation
|
* Copyright (C) 2013 Atmel Corporation
|
||||||
* Bo Shen <voice.shen@atmel.com>
|
* Bo Shen <voice.shen@atmel.com>
|
||||||
|
*
|
||||||
|
* Links to reference manuals for the supported PWM chips can be found in
|
||||||
|
* Documentation/arm/microchip.rst.
|
||||||
|
*
|
||||||
|
* Limitations:
|
||||||
|
* - Periods start with the inactive level.
|
||||||
|
* - Hardware has to be stopped in general to update settings.
|
||||||
|
*
|
||||||
|
* Software bugs/possible improvements:
|
||||||
|
* - When atmel_pwm_apply() is called with state->enabled=false a change in
|
||||||
|
* state->polarity isn't honored.
|
||||||
|
* - Instead of sleeping to wait for a completed period, the interrupt
|
||||||
|
* functionality could be used.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
@ -47,6 +60,8 @@
|
||||||
#define PWMV2_CPRD 0x0C
|
#define PWMV2_CPRD 0x0C
|
||||||
#define PWMV2_CPRDUPD 0x10
|
#define PWMV2_CPRDUPD 0x10
|
||||||
|
|
||||||
|
#define PWM_MAX_PRES 10
|
||||||
|
|
||||||
struct atmel_pwm_registers {
|
struct atmel_pwm_registers {
|
||||||
u8 period;
|
u8 period;
|
||||||
u8 period_upd;
|
u8 period_upd;
|
||||||
|
@ -55,8 +70,7 @@ struct atmel_pwm_registers {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct atmel_pwm_config {
|
struct atmel_pwm_config {
|
||||||
u32 max_period;
|
u32 period_bits;
|
||||||
u32 max_pres;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct atmel_pwm_data {
|
struct atmel_pwm_data {
|
||||||
|
@ -97,7 +111,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;
|
unsigned long base = PWM_CH_REG_OFFSET + ch * PWM_CH_REG_SIZE;
|
||||||
|
|
||||||
return readl_relaxed(chip->base + base + offset);
|
return atmel_pwm_readl(chip, base + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void atmel_pwm_ch_writel(struct atmel_pwm_chip *chip,
|
static inline void atmel_pwm_ch_writel(struct atmel_pwm_chip *chip,
|
||||||
|
@ -106,7 +120,7 @@ static inline void atmel_pwm_ch_writel(struct atmel_pwm_chip *chip,
|
||||||
{
|
{
|
||||||
unsigned long base = PWM_CH_REG_OFFSET + ch * PWM_CH_REG_SIZE;
|
unsigned long base = PWM_CH_REG_OFFSET + ch * PWM_CH_REG_SIZE;
|
||||||
|
|
||||||
writel_relaxed(val, chip->base + base + offset);
|
atmel_pwm_writel(chip, base + offset, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int atmel_pwm_calculate_cprd_and_pres(struct pwm_chip *chip,
|
static int atmel_pwm_calculate_cprd_and_pres(struct pwm_chip *chip,
|
||||||
|
@ -115,17 +129,27 @@ static int atmel_pwm_calculate_cprd_and_pres(struct pwm_chip *chip,
|
||||||
{
|
{
|
||||||
struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
|
struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
|
||||||
unsigned long long cycles = state->period;
|
unsigned long long cycles = state->period;
|
||||||
|
int shift;
|
||||||
|
|
||||||
/* Calculate the period cycles and prescale value */
|
/* Calculate the period cycles and prescale value */
|
||||||
cycles *= clk_get_rate(atmel_pwm->clk);
|
cycles *= clk_get_rate(atmel_pwm->clk);
|
||||||
do_div(cycles, NSEC_PER_SEC);
|
do_div(cycles, NSEC_PER_SEC);
|
||||||
|
|
||||||
for (*pres = 0; cycles > atmel_pwm->data->cfg.max_period; 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 > atmel_pwm->data->cfg.max_pres) {
|
if (shift > PWM_MAX_PRES) {
|
||||||
dev_err(chip->dev, "pres exceeds the maximum value\n");
|
dev_err(chip->dev, "pres exceeds the maximum value\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
} else if (shift > 0) {
|
||||||
|
*pres = shift;
|
||||||
|
cycles >>= *pres;
|
||||||
|
} else {
|
||||||
|
*pres = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
*cprd = cycles;
|
*cprd = cycles;
|
||||||
|
@ -271,8 +295,48 @@ static int atmel_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void atmel_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||||
|
struct pwm_state *state)
|
||||||
|
{
|
||||||
|
struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
|
||||||
|
u32 sr, cmr;
|
||||||
|
|
||||||
|
sr = atmel_pwm_readl(atmel_pwm, PWM_SR);
|
||||||
|
cmr = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR);
|
||||||
|
|
||||||
|
if (sr & (1 << pwm->hwpwm)) {
|
||||||
|
unsigned long rate = clk_get_rate(atmel_pwm->clk);
|
||||||
|
u32 cdty, cprd, pres;
|
||||||
|
u64 tmp;
|
||||||
|
|
||||||
|
pres = cmr & PWM_CMR_CPRE_MSK;
|
||||||
|
|
||||||
|
cprd = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm,
|
||||||
|
atmel_pwm->data->regs.period);
|
||||||
|
tmp = (u64)cprd * NSEC_PER_SEC;
|
||||||
|
tmp <<= pres;
|
||||||
|
state->period = DIV64_U64_ROUND_UP(tmp, rate);
|
||||||
|
|
||||||
|
cdty = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm,
|
||||||
|
atmel_pwm->data->regs.duty);
|
||||||
|
tmp = (u64)cdty * NSEC_PER_SEC;
|
||||||
|
tmp <<= pres;
|
||||||
|
state->duty_cycle = DIV64_U64_ROUND_UP(tmp, rate);
|
||||||
|
|
||||||
|
state->enabled = true;
|
||||||
|
} else {
|
||||||
|
state->enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmr & PWM_CMR_CPOL)
|
||||||
|
state->polarity = PWM_POLARITY_INVERSED;
|
||||||
|
else
|
||||||
|
state->polarity = PWM_POLARITY_NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct pwm_ops atmel_pwm_ops = {
|
static const struct pwm_ops atmel_pwm_ops = {
|
||||||
.apply = atmel_pwm_apply,
|
.apply = atmel_pwm_apply,
|
||||||
|
.get_state = atmel_pwm_get_state,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -285,8 +349,7 @@ static const struct atmel_pwm_data atmel_sam9rl_pwm_data = {
|
||||||
},
|
},
|
||||||
.cfg = {
|
.cfg = {
|
||||||
/* 16 bits to keep period and duty. */
|
/* 16 bits to keep period and duty. */
|
||||||
.max_period = 0xffff,
|
.period_bits = 16,
|
||||||
.max_pres = 10,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -299,8 +362,7 @@ static const struct atmel_pwm_data atmel_sama5_pwm_data = {
|
||||||
},
|
},
|
||||||
.cfg = {
|
.cfg = {
|
||||||
/* 16 bits to keep period and duty. */
|
/* 16 bits to keep period and duty. */
|
||||||
.max_period = 0xffff,
|
.period_bits = 16,
|
||||||
.max_pres = 10,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -313,8 +375,7 @@ static const struct atmel_pwm_data mchp_sam9x60_pwm_data = {
|
||||||
},
|
},
|
||||||
.cfg = {
|
.cfg = {
|
||||||
/* 32 bits to keep period and duty. */
|
/* 32 bits to keep period and duty. */
|
||||||
.max_period = 0xffffffff,
|
.period_bits = 32,
|
||||||
.max_pres = 10,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -25,11 +25,39 @@ struct cros_ec_pwm_device {
|
||||||
struct pwm_chip chip;
|
struct pwm_chip chip;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 *c)
|
static inline struct cros_ec_pwm_device *pwm_to_cros_ec_pwm(struct pwm_chip *c)
|
||||||
{
|
{
|
||||||
return container_of(c, struct cros_ec_pwm_device, chip);
|
return container_of(c, struct cros_ec_pwm_device, chip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int cros_ec_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||||
|
{
|
||||||
|
struct cros_ec_pwm *channel;
|
||||||
|
|
||||||
|
channel = kzalloc(sizeof(*channel), GFP_KERNEL);
|
||||||
|
if (!channel)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
pwm_set_chip_data(pwm, channel);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cros_ec_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||||
|
{
|
||||||
|
struct cros_ec_pwm *channel = pwm_get_chip_data(pwm);
|
||||||
|
|
||||||
|
kfree(channel);
|
||||||
|
}
|
||||||
|
|
||||||
static int cros_ec_pwm_set_duty(struct cros_ec_device *ec, u8 index, u16 duty)
|
static int cros_ec_pwm_set_duty(struct cros_ec_device *ec, u8 index, u16 duty)
|
||||||
{
|
{
|
||||||
struct {
|
struct {
|
||||||
|
@ -96,7 +124,9 @@ static int cros_ec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||||
const struct pwm_state *state)
|
const struct pwm_state *state)
|
||||||
{
|
{
|
||||||
struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip);
|
struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip);
|
||||||
int duty_cycle;
|
struct cros_ec_pwm *channel = pwm_get_chip_data(pwm);
|
||||||
|
u16 duty_cycle;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/* The EC won't let us change the period */
|
/* The EC won't let us change the period */
|
||||||
if (state->period != EC_PWM_MAX_DUTY)
|
if (state->period != EC_PWM_MAX_DUTY)
|
||||||
|
@ -108,13 +138,20 @@ static int cros_ec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||||
*/
|
*/
|
||||||
duty_cycle = state->enabled ? state->duty_cycle : 0;
|
duty_cycle = state->enabled ? state->duty_cycle : 0;
|
||||||
|
|
||||||
return cros_ec_pwm_set_duty(ec_pwm->ec, pwm->hwpwm, duty_cycle);
|
ret = cros_ec_pwm_set_duty(ec_pwm->ec, pwm->hwpwm, duty_cycle);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
channel->duty_cycle = state->duty_cycle;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cros_ec_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
|
static void cros_ec_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||||
struct pwm_state *state)
|
struct pwm_state *state)
|
||||||
{
|
{
|
||||||
struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip);
|
struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip);
|
||||||
|
struct cros_ec_pwm *channel = pwm_get_chip_data(pwm);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = cros_ec_pwm_get_duty(ec_pwm->ec, pwm->hwpwm);
|
ret = cros_ec_pwm_get_duty(ec_pwm->ec, pwm->hwpwm);
|
||||||
|
@ -126,8 +163,19 @@ static void cros_ec_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||||
state->enabled = (ret > 0);
|
state->enabled = (ret > 0);
|
||||||
state->period = EC_PWM_MAX_DUTY;
|
state->period = EC_PWM_MAX_DUTY;
|
||||||
|
|
||||||
/* Note that "disabled" and "duty cycle == 0" are treated the same */
|
/*
|
||||||
state->duty_cycle = ret;
|
* 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct pwm_device *
|
static struct pwm_device *
|
||||||
|
@ -149,6 +197,8 @@ cros_ec_pwm_xlate(struct pwm_chip *pc, const struct of_phandle_args *args)
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct pwm_ops cros_ec_pwm_ops = {
|
static const struct pwm_ops cros_ec_pwm_ops = {
|
||||||
|
.request = cros_ec_pwm_request,
|
||||||
|
.free = cros_ec_pwm_free,
|
||||||
.get_state = cros_ec_pwm_get_state,
|
.get_state = cros_ec_pwm_get_state,
|
||||||
.apply = cros_ec_pwm_apply,
|
.apply = cros_ec_pwm_apply,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
|
|
|
@ -85,6 +85,13 @@ struct pwm_imx27_chip {
|
||||||
struct clk *clk_per;
|
struct clk *clk_per;
|
||||||
void __iomem *mmio_base;
|
void __iomem *mmio_base;
|
||||||
struct pwm_chip chip;
|
struct pwm_chip chip;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The driver cannot read the current duty cycle from the hardware if
|
||||||
|
* the hardware is disabled. Cache the last programmed duty cycle
|
||||||
|
* value to return in that case.
|
||||||
|
*/
|
||||||
|
unsigned int duty_cycle;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_pwm_imx27_chip(chip) container_of(chip, struct pwm_imx27_chip, chip)
|
#define to_pwm_imx27_chip(chip) container_of(chip, struct pwm_imx27_chip, chip)
|
||||||
|
@ -155,14 +162,17 @@ static void pwm_imx27_get_state(struct pwm_chip *chip,
|
||||||
tmp = NSEC_PER_SEC * (u64)(period + 2);
|
tmp = NSEC_PER_SEC * (u64)(period + 2);
|
||||||
state->period = DIV_ROUND_CLOSEST_ULL(tmp, pwm_clk);
|
state->period = DIV_ROUND_CLOSEST_ULL(tmp, pwm_clk);
|
||||||
|
|
||||||
/* PWMSAR can be read only if PWM is enabled */
|
/*
|
||||||
if (state->enabled) {
|
* PWMSAR can be read only if PWM is enabled. If the PWM is disabled,
|
||||||
|
* use the cached value.
|
||||||
|
*/
|
||||||
|
if (state->enabled)
|
||||||
val = readl(imx->mmio_base + MX3_PWMSAR);
|
val = readl(imx->mmio_base + MX3_PWMSAR);
|
||||||
tmp = NSEC_PER_SEC * (u64)(val);
|
else
|
||||||
state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, pwm_clk);
|
val = imx->duty_cycle;
|
||||||
} else {
|
|
||||||
state->duty_cycle = 0;
|
tmp = NSEC_PER_SEC * (u64)(val);
|
||||||
}
|
state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, pwm_clk);
|
||||||
|
|
||||||
if (!state->enabled)
|
if (!state->enabled)
|
||||||
pwm_imx27_clk_disable_unprepare(chip);
|
pwm_imx27_clk_disable_unprepare(chip);
|
||||||
|
@ -220,63 +230,68 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||||
|
|
||||||
pwm_get_state(pwm, &cstate);
|
pwm_get_state(pwm, &cstate);
|
||||||
|
|
||||||
if (state->enabled) {
|
c = clk_get_rate(imx->clk_per);
|
||||||
c = clk_get_rate(imx->clk_per);
|
c *= state->period;
|
||||||
c *= state->period;
|
|
||||||
|
|
||||||
do_div(c, 1000000000);
|
do_div(c, 1000000000);
|
||||||
period_cycles = c;
|
period_cycles = c;
|
||||||
|
|
||||||
prescale = period_cycles / 0x10000 + 1;
|
prescale = period_cycles / 0x10000 + 1;
|
||||||
|
|
||||||
period_cycles /= prescale;
|
period_cycles /= prescale;
|
||||||
c = (unsigned long long)period_cycles * state->duty_cycle;
|
c = (unsigned long long)period_cycles * state->duty_cycle;
|
||||||
do_div(c, state->period);
|
do_div(c, state->period);
|
||||||
duty_cycles = c;
|
duty_cycles = c;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* according to imx pwm RM, the real period value should be
|
* according to imx pwm RM, the real period value should be PERIOD
|
||||||
* PERIOD value in PWMPR plus 2.
|
* value in PWMPR plus 2.
|
||||||
*/
|
*/
|
||||||
if (period_cycles > 2)
|
if (period_cycles > 2)
|
||||||
period_cycles -= 2;
|
period_cycles -= 2;
|
||||||
else
|
else
|
||||||
period_cycles = 0;
|
period_cycles = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Wait for a free FIFO slot if the PWM is already enabled, and
|
* Wait for a free FIFO slot if the PWM is already enabled, and flush
|
||||||
* flush the FIFO if the PWM was disabled and is about to be
|
* the FIFO if the PWM was disabled and is about to be enabled.
|
||||||
* enabled.
|
*/
|
||||||
*/
|
if (cstate.enabled) {
|
||||||
if (cstate.enabled) {
|
pwm_imx27_wait_fifo_slot(chip, pwm);
|
||||||
pwm_imx27_wait_fifo_slot(chip, pwm);
|
} else {
|
||||||
} else {
|
ret = pwm_imx27_clk_prepare_enable(chip);
|
||||||
ret = pwm_imx27_clk_prepare_enable(chip);
|
if (ret)
|
||||||
if (ret)
|
return ret;
|
||||||
return ret;
|
|
||||||
|
|
||||||
pwm_imx27_sw_reset(chip);
|
pwm_imx27_sw_reset(chip);
|
||||||
}
|
|
||||||
|
|
||||||
writel(duty_cycles, imx->mmio_base + MX3_PWMSAR);
|
|
||||||
writel(period_cycles, imx->mmio_base + MX3_PWMPR);
|
|
||||||
|
|
||||||
cr = MX3_PWMCR_PRESCALER_SET(prescale) |
|
|
||||||
MX3_PWMCR_STOPEN | MX3_PWMCR_DOZEN | MX3_PWMCR_WAITEN |
|
|
||||||
FIELD_PREP(MX3_PWMCR_CLKSRC, MX3_PWMCR_CLKSRC_IPG_HIGH) |
|
|
||||||
MX3_PWMCR_DBGEN | MX3_PWMCR_EN;
|
|
||||||
|
|
||||||
if (state->polarity == PWM_POLARITY_INVERSED)
|
|
||||||
cr |= FIELD_PREP(MX3_PWMCR_POUTC,
|
|
||||||
MX3_PWMCR_POUTC_INVERTED);
|
|
||||||
|
|
||||||
writel(cr, imx->mmio_base + MX3_PWMCR);
|
|
||||||
} else if (cstate.enabled) {
|
|
||||||
writel(0, imx->mmio_base + MX3_PWMCR);
|
|
||||||
|
|
||||||
pwm_imx27_clk_disable_unprepare(chip);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writel(duty_cycles, imx->mmio_base + MX3_PWMSAR);
|
||||||
|
writel(period_cycles, imx->mmio_base + MX3_PWMPR);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Store the duty cycle for future reference in cases where the
|
||||||
|
* MX3_PWMSAR register can't be read (i.e. when the PWM is disabled).
|
||||||
|
*/
|
||||||
|
imx->duty_cycle = duty_cycles;
|
||||||
|
|
||||||
|
cr = MX3_PWMCR_PRESCALER_SET(prescale) |
|
||||||
|
MX3_PWMCR_STOPEN | MX3_PWMCR_DOZEN | MX3_PWMCR_WAITEN |
|
||||||
|
FIELD_PREP(MX3_PWMCR_CLKSRC, MX3_PWMCR_CLKSRC_IPG_HIGH) |
|
||||||
|
MX3_PWMCR_DBGEN;
|
||||||
|
|
||||||
|
if (state->polarity == PWM_POLARITY_INVERSED)
|
||||||
|
cr |= FIELD_PREP(MX3_PWMCR_POUTC,
|
||||||
|
MX3_PWMCR_POUTC_INVERTED);
|
||||||
|
|
||||||
|
if (state->enabled)
|
||||||
|
cr |= MX3_PWMCR_EN;
|
||||||
|
|
||||||
|
writel(cr, imx->mmio_base + MX3_PWMCR);
|
||||||
|
|
||||||
|
if (!state->enabled && cstate.enabled)
|
||||||
|
pwm_imx27_clk_disable_unprepare(chip);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,9 +319,13 @@ static int pwm_imx27_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
|
imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
|
||||||
if (IS_ERR(imx->clk_ipg)) {
|
if (IS_ERR(imx->clk_ipg)) {
|
||||||
dev_err(&pdev->dev, "getting ipg clock failed with %ld\n",
|
int ret = PTR_ERR(imx->clk_ipg);
|
||||||
PTR_ERR(imx->clk_ipg));
|
|
||||||
return PTR_ERR(imx->clk_ipg);
|
if (ret != -EPROBE_DEFER)
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"getting ipg clock failed with %d\n",
|
||||||
|
ret);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
imx->clk_per = devm_clk_get(&pdev->dev, "per");
|
imx->clk_per = devm_clk_get(&pdev->dev, "per");
|
||||||
|
|
|
@ -25,12 +25,16 @@
|
||||||
#define PERIOD_PERIOD(p) ((p) & 0xffff)
|
#define PERIOD_PERIOD(p) ((p) & 0xffff)
|
||||||
#define PERIOD_PERIOD_MAX 0x10000
|
#define PERIOD_PERIOD_MAX 0x10000
|
||||||
#define PERIOD_ACTIVE_HIGH (3 << 16)
|
#define PERIOD_ACTIVE_HIGH (3 << 16)
|
||||||
|
#define PERIOD_ACTIVE_LOW (2 << 16)
|
||||||
|
#define PERIOD_INACTIVE_HIGH (3 << 18)
|
||||||
#define PERIOD_INACTIVE_LOW (2 << 18)
|
#define PERIOD_INACTIVE_LOW (2 << 18)
|
||||||
|
#define PERIOD_POLARITY_NORMAL (PERIOD_ACTIVE_HIGH | PERIOD_INACTIVE_LOW)
|
||||||
|
#define PERIOD_POLARITY_INVERSE (PERIOD_ACTIVE_LOW | PERIOD_INACTIVE_HIGH)
|
||||||
#define PERIOD_CDIV(div) (((div) & 0x7) << 20)
|
#define PERIOD_CDIV(div) (((div) & 0x7) << 20)
|
||||||
#define PERIOD_CDIV_MAX 8
|
#define PERIOD_CDIV_MAX 8
|
||||||
|
|
||||||
static const unsigned int cdiv[PERIOD_CDIV_MAX] = {
|
static const u8 cdiv_shift[PERIOD_CDIV_MAX] = {
|
||||||
1, 2, 4, 8, 16, 64, 256, 1024
|
0, 1, 2, 3, 4, 6, 8, 10
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mxs_pwm_chip {
|
struct mxs_pwm_chip {
|
||||||
|
@ -41,19 +45,34 @@ struct mxs_pwm_chip {
|
||||||
|
|
||||||
#define to_mxs_pwm_chip(_chip) container_of(_chip, struct mxs_pwm_chip, chip)
|
#define to_mxs_pwm_chip(_chip) container_of(_chip, struct mxs_pwm_chip, chip)
|
||||||
|
|
||||||
static int mxs_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
static int mxs_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||||
int duty_ns, int period_ns)
|
const struct pwm_state *state)
|
||||||
{
|
{
|
||||||
struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip);
|
struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip);
|
||||||
int ret, div = 0;
|
int ret, div = 0;
|
||||||
unsigned int period_cycles, duty_cycles;
|
unsigned int period_cycles, duty_cycles;
|
||||||
unsigned long rate;
|
unsigned long rate;
|
||||||
unsigned long long c;
|
unsigned long long c;
|
||||||
|
unsigned int pol_bits;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the PWM channel is disabled, make sure to turn on the
|
||||||
|
* clock before calling clk_get_rate() and writing to the
|
||||||
|
* registers. Otherwise, just keep it enabled.
|
||||||
|
*/
|
||||||
|
if (!pwm_is_enabled(pwm)) {
|
||||||
|
ret = clk_prepare_enable(mxs->clk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state->enabled && pwm_is_enabled(pwm))
|
||||||
|
writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + CLR);
|
||||||
|
|
||||||
rate = clk_get_rate(mxs->clk);
|
rate = clk_get_rate(mxs->clk);
|
||||||
while (1) {
|
while (1) {
|
||||||
c = rate / cdiv[div];
|
c = rate >> cdiv_shift[div];
|
||||||
c = c * period_ns;
|
c = c * state->period;
|
||||||
do_div(c, 1000000000);
|
do_div(c, 1000000000);
|
||||||
if (c < PERIOD_PERIOD_MAX)
|
if (c < PERIOD_PERIOD_MAX)
|
||||||
break;
|
break;
|
||||||
|
@ -63,62 +82,40 @@ static int mxs_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||||
}
|
}
|
||||||
|
|
||||||
period_cycles = c;
|
period_cycles = c;
|
||||||
c *= duty_ns;
|
c *= state->duty_cycle;
|
||||||
do_div(c, period_ns);
|
do_div(c, state->period);
|
||||||
duty_cycles = c;
|
duty_cycles = c;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the PWM channel is disabled, make sure to turn on the clock
|
* The data sheet the says registers must be written to in
|
||||||
* before writing the register. Otherwise, keep it enabled.
|
* this order (ACTIVEn, then PERIODn). Also, the new settings
|
||||||
|
* only take effect at the beginning of a new period, avoiding
|
||||||
|
* glitches.
|
||||||
*/
|
*/
|
||||||
if (!pwm_is_enabled(pwm)) {
|
|
||||||
ret = clk_prepare_enable(mxs->clk);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
pol_bits = state->polarity == PWM_POLARITY_NORMAL ?
|
||||||
|
PERIOD_POLARITY_NORMAL : PERIOD_POLARITY_INVERSE;
|
||||||
writel(duty_cycles << 16,
|
writel(duty_cycles << 16,
|
||||||
mxs->base + PWM_ACTIVE0 + pwm->hwpwm * 0x20);
|
mxs->base + PWM_ACTIVE0 + pwm->hwpwm * 0x20);
|
||||||
writel(PERIOD_PERIOD(period_cycles) | PERIOD_ACTIVE_HIGH |
|
writel(PERIOD_PERIOD(period_cycles) | pol_bits | PERIOD_CDIV(div),
|
||||||
PERIOD_INACTIVE_LOW | PERIOD_CDIV(div),
|
mxs->base + PWM_PERIOD0 + pwm->hwpwm * 0x20);
|
||||||
mxs->base + PWM_PERIOD0 + pwm->hwpwm * 0x20);
|
|
||||||
|
|
||||||
/*
|
if (state->enabled) {
|
||||||
* If the PWM is not enabled, turn the clock off again to save power.
|
if (!pwm_is_enabled(pwm)) {
|
||||||
*/
|
/*
|
||||||
if (!pwm_is_enabled(pwm))
|
* The clock was enabled above. Just enable
|
||||||
|
* the channel in the control register.
|
||||||
|
*/
|
||||||
|
writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + SET);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
clk_disable_unprepare(mxs->clk);
|
clk_disable_unprepare(mxs->clk);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mxs_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
|
|
||||||
{
|
|
||||||
struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip);
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = clk_prepare_enable(mxs->clk);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + SET);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mxs_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
|
|
||||||
{
|
|
||||||
struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip);
|
|
||||||
|
|
||||||
writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + CLR);
|
|
||||||
|
|
||||||
clk_disable_unprepare(mxs->clk);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct pwm_ops mxs_pwm_ops = {
|
static const struct pwm_ops mxs_pwm_ops = {
|
||||||
.config = mxs_pwm_config,
|
.apply = mxs_pwm_apply,
|
||||||
.enable = mxs_pwm_enable,
|
|
||||||
.disable = mxs_pwm_disable,
|
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -142,6 +139,8 @@ static int mxs_pwm_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
mxs->chip.dev = &pdev->dev;
|
mxs->chip.dev = &pdev->dev;
|
||||||
mxs->chip.ops = &mxs_pwm_ops;
|
mxs->chip.ops = &mxs_pwm_ops;
|
||||||
|
mxs->chip.of_xlate = of_pwm_xlate_with_flags;
|
||||||
|
mxs->chip.of_pwm_n_cells = 3;
|
||||||
mxs->chip.base = -1;
|
mxs->chip.base = -1;
|
||||||
|
|
||||||
ret = of_property_read_u32(np, "fsl,pwm-number", &mxs->chip.npwm);
|
ret = of_property_read_u32(np, "fsl,pwm-number", &mxs->chip.npwm);
|
||||||
|
|
|
@ -256,7 +256,7 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev)
|
||||||
if (!timer_pdev) {
|
if (!timer_pdev) {
|
||||||
dev_err(&pdev->dev, "Unable to find Timer pdev\n");
|
dev_err(&pdev->dev, "Unable to find Timer pdev\n");
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
goto put;
|
goto err_find_timer_pdev;
|
||||||
}
|
}
|
||||||
|
|
||||||
timer_pdata = dev_get_platdata(&timer_pdev->dev);
|
timer_pdata = dev_get_platdata(&timer_pdev->dev);
|
||||||
|
@ -264,7 +264,7 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev)
|
||||||
dev_dbg(&pdev->dev,
|
dev_dbg(&pdev->dev,
|
||||||
"dmtimer pdata structure NULL, deferring probe\n");
|
"dmtimer pdata structure NULL, deferring probe\n");
|
||||||
ret = -EPROBE_DEFER;
|
ret = -EPROBE_DEFER;
|
||||||
goto put;
|
goto err_platdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
pdata = timer_pdata->timer_ops;
|
pdata = timer_pdata->timer_ops;
|
||||||
|
@ -283,30 +283,25 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev)
|
||||||
!pdata->write_counter) {
|
!pdata->write_counter) {
|
||||||
dev_err(&pdev->dev, "Incomplete dmtimer pdata structure\n");
|
dev_err(&pdev->dev, "Incomplete dmtimer pdata structure\n");
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto put;
|
goto err_platdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!of_get_property(timer, "ti,timer-pwm", NULL)) {
|
if (!of_get_property(timer, "ti,timer-pwm", NULL)) {
|
||||||
dev_err(&pdev->dev, "Missing ti,timer-pwm capability\n");
|
dev_err(&pdev->dev, "Missing ti,timer-pwm capability\n");
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
goto put;
|
goto err_timer_property;
|
||||||
}
|
}
|
||||||
|
|
||||||
dm_timer = pdata->request_by_node(timer);
|
dm_timer = pdata->request_by_node(timer);
|
||||||
if (!dm_timer) {
|
if (!dm_timer) {
|
||||||
ret = -EPROBE_DEFER;
|
ret = -EPROBE_DEFER;
|
||||||
goto put;
|
goto err_request_timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
put:
|
|
||||||
of_node_put(timer);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
omap = devm_kzalloc(&pdev->dev, sizeof(*omap), GFP_KERNEL);
|
omap = devm_kzalloc(&pdev->dev, sizeof(*omap), GFP_KERNEL);
|
||||||
if (!omap) {
|
if (!omap) {
|
||||||
pdata->free(dm_timer);
|
ret = -ENOMEM;
|
||||||
return -ENOMEM;
|
goto err_alloc_omap;
|
||||||
}
|
}
|
||||||
|
|
||||||
omap->pdata = pdata;
|
omap->pdata = pdata;
|
||||||
|
@ -339,27 +334,56 @@ put:
|
||||||
ret = pwmchip_add(&omap->chip);
|
ret = pwmchip_add(&omap->chip);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "failed to register PWM\n");
|
dev_err(&pdev->dev, "failed to register PWM\n");
|
||||||
omap->pdata->free(omap->dm_timer);
|
goto err_pwmchip_add;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
of_node_put(timer);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, omap);
|
platform_set_drvdata(pdev, omap);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_pwmchip_add:
|
||||||
|
|
||||||
|
/*
|
||||||
|
* *omap is allocated using devm_kzalloc,
|
||||||
|
* so no free necessary here
|
||||||
|
*/
|
||||||
|
err_alloc_omap:
|
||||||
|
|
||||||
|
pdata->free(dm_timer);
|
||||||
|
err_request_timer:
|
||||||
|
|
||||||
|
err_timer_property:
|
||||||
|
err_platdata:
|
||||||
|
|
||||||
|
put_device(&timer_pdev->dev);
|
||||||
|
err_find_timer_pdev:
|
||||||
|
|
||||||
|
of_node_put(timer);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pwm_omap_dmtimer_remove(struct platform_device *pdev)
|
static int pwm_omap_dmtimer_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct pwm_omap_dmtimer_chip *omap = platform_get_drvdata(pdev);
|
struct pwm_omap_dmtimer_chip *omap = platform_get_drvdata(pdev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = pwmchip_remove(&omap->chip);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
if (pm_runtime_active(&omap->dm_timer_pdev->dev))
|
if (pm_runtime_active(&omap->dm_timer_pdev->dev))
|
||||||
omap->pdata->stop(omap->dm_timer);
|
omap->pdata->stop(omap->dm_timer);
|
||||||
|
|
||||||
omap->pdata->free(omap->dm_timer);
|
omap->pdata->free(omap->dm_timer);
|
||||||
|
|
||||||
|
put_device(&omap->dm_timer_pdev->dev);
|
||||||
|
|
||||||
mutex_destroy(&omap->mutex);
|
mutex_destroy(&omap->mutex);
|
||||||
|
|
||||||
return pwmchip_remove(&omap->chip);
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id pwm_omap_dmtimer_of_match[] = {
|
static const struct of_device_id pwm_omap_dmtimer_of_match[] = {
|
||||||
|
|
|
@ -159,13 +159,9 @@ static void pca9685_pwm_gpio_set(struct gpio_chip *gpio, unsigned int offset,
|
||||||
static void pca9685_pwm_gpio_free(struct gpio_chip *gpio, unsigned int offset)
|
static void pca9685_pwm_gpio_free(struct gpio_chip *gpio, unsigned int offset)
|
||||||
{
|
{
|
||||||
struct pca9685 *pca = gpiochip_get_data(gpio);
|
struct pca9685 *pca = gpiochip_get_data(gpio);
|
||||||
struct pwm_device *pwm;
|
|
||||||
|
|
||||||
pca9685_pwm_gpio_set(gpio, offset, 0);
|
pca9685_pwm_gpio_set(gpio, offset, 0);
|
||||||
pm_runtime_put(pca->chip.dev);
|
pm_runtime_put(pca->chip.dev);
|
||||||
mutex_lock(&pca->lock);
|
|
||||||
pwm = &pca->chip.pwms[offset];
|
|
||||||
mutex_unlock(&pca->lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pca9685_pwm_gpio_get_direction(struct gpio_chip *chip,
|
static int pca9685_pwm_gpio_get_direction(struct gpio_chip *chip,
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
* R-Car PWM Timer driver
|
* R-Car PWM Timer driver
|
||||||
*
|
*
|
||||||
* Copyright (C) 2015 Renesas Electronics Corporation
|
* Copyright (C) 2015 Renesas Electronics Corporation
|
||||||
|
*
|
||||||
|
* Limitations:
|
||||||
|
* - The hardware cannot generate a 0% duty cycle.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
@ -161,11 +164,9 @@ static int rcar_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||||
const struct pwm_state *state)
|
const struct pwm_state *state)
|
||||||
{
|
{
|
||||||
struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip);
|
struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip);
|
||||||
struct pwm_state cur_state;
|
|
||||||
int div, ret;
|
int div, ret;
|
||||||
|
|
||||||
/* This HW/driver only supports normal polarity */
|
/* This HW/driver only supports normal polarity */
|
||||||
pwm_get_state(pwm, &cur_state);
|
|
||||||
if (state->polarity != PWM_POLARITY_NORMAL)
|
if (state->polarity != PWM_POLARITY_NORMAL)
|
||||||
return -ENOTSUPP;
|
return -ENOTSUPP;
|
||||||
|
|
||||||
|
|
|
@ -377,9 +377,7 @@ static int stm32_pwm_config(struct stm32_pwm *priv, int ch,
|
||||||
else
|
else
|
||||||
regmap_update_bits(priv->regmap, TIM_CCMR2, mask, ccmr);
|
regmap_update_bits(priv->regmap, TIM_CCMR2, mask, ccmr);
|
||||||
|
|
||||||
regmap_update_bits(priv->regmap, TIM_BDTR,
|
regmap_update_bits(priv->regmap, TIM_BDTR, TIM_BDTR_MOE, TIM_BDTR_MOE);
|
||||||
TIM_BDTR_MOE | TIM_BDTR_AOE,
|
|
||||||
TIM_BDTR_MOE | TIM_BDTR_AOE);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,10 @@
|
||||||
* Driver for Allwinner sun4i Pulse Width Modulation Controller
|
* Driver for Allwinner sun4i Pulse Width Modulation Controller
|
||||||
*
|
*
|
||||||
* Copyright (C) 2014 Alexandre Belloni <alexandre.belloni@free-electrons.com>
|
* Copyright (C) 2014 Alexandre Belloni <alexandre.belloni@free-electrons.com>
|
||||||
|
*
|
||||||
|
* Limitations:
|
||||||
|
* - When outputing the source clock directly, the PWM logic will be bypassed
|
||||||
|
* and the currently running period is not guaranteed to be completed
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
|
@ -16,6 +20,7 @@
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pwm.h>
|
#include <linux/pwm.h>
|
||||||
|
#include <linux/reset.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/time.h>
|
#include <linux/time.h>
|
||||||
|
@ -72,12 +77,15 @@ static const u32 prescaler_table[] = {
|
||||||
|
|
||||||
struct sun4i_pwm_data {
|
struct sun4i_pwm_data {
|
||||||
bool has_prescaler_bypass;
|
bool has_prescaler_bypass;
|
||||||
|
bool has_direct_mod_clk_output;
|
||||||
unsigned int npwm;
|
unsigned int npwm;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sun4i_pwm_chip {
|
struct sun4i_pwm_chip {
|
||||||
struct pwm_chip chip;
|
struct pwm_chip chip;
|
||||||
|
struct clk *bus_clk;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
struct reset_control *rst;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
spinlock_t ctrl_lock;
|
spinlock_t ctrl_lock;
|
||||||
const struct sun4i_pwm_data *data;
|
const struct sun4i_pwm_data *data;
|
||||||
|
@ -115,6 +123,20 @@ static void sun4i_pwm_get_state(struct pwm_chip *chip,
|
||||||
|
|
||||||
val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
|
val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PWM chapter in H6 manual has a diagram which explains that if bypass
|
||||||
|
* bit is set, no other setting has any meaning. Even more, experiment
|
||||||
|
* proved that also enable bit is ignored in this case.
|
||||||
|
*/
|
||||||
|
if ((val & BIT_CH(PWM_BYPASS, pwm->hwpwm)) &&
|
||||||
|
sun4i_pwm->data->has_direct_mod_clk_output) {
|
||||||
|
state->period = DIV_ROUND_UP_ULL(NSEC_PER_SEC, clk_rate);
|
||||||
|
state->duty_cycle = DIV_ROUND_UP_ULL(state->period, 2);
|
||||||
|
state->polarity = PWM_POLARITY_NORMAL;
|
||||||
|
state->enabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ((PWM_REG_PRESCAL(val, pwm->hwpwm) == PWM_PRESCAL_MASK) &&
|
if ((PWM_REG_PRESCAL(val, pwm->hwpwm) == PWM_PRESCAL_MASK) &&
|
||||||
sun4i_pwm->data->has_prescaler_bypass)
|
sun4i_pwm->data->has_prescaler_bypass)
|
||||||
prescaler = 1;
|
prescaler = 1;
|
||||||
|
@ -146,13 +168,24 @@ static void sun4i_pwm_get_state(struct pwm_chip *chip,
|
||||||
|
|
||||||
static int sun4i_pwm_calculate(struct sun4i_pwm_chip *sun4i_pwm,
|
static int sun4i_pwm_calculate(struct sun4i_pwm_chip *sun4i_pwm,
|
||||||
const struct pwm_state *state,
|
const struct pwm_state *state,
|
||||||
u32 *dty, u32 *prd, unsigned int *prsclr)
|
u32 *dty, u32 *prd, unsigned int *prsclr,
|
||||||
|
bool *bypass)
|
||||||
{
|
{
|
||||||
u64 clk_rate, div = 0;
|
u64 clk_rate, div = 0;
|
||||||
unsigned int pval, prescaler = 0;
|
unsigned int prescaler = 0;
|
||||||
|
|
||||||
clk_rate = clk_get_rate(sun4i_pwm->clk);
|
clk_rate = clk_get_rate(sun4i_pwm->clk);
|
||||||
|
|
||||||
|
*bypass = sun4i_pwm->data->has_direct_mod_clk_output &&
|
||||||
|
state->enabled &&
|
||||||
|
(state->period * clk_rate >= NSEC_PER_SEC) &&
|
||||||
|
(state->period * clk_rate < 2 * NSEC_PER_SEC) &&
|
||||||
|
(state->duty_cycle * clk_rate * 2 >= NSEC_PER_SEC);
|
||||||
|
|
||||||
|
/* Skip calculation of other parameters if we bypass them */
|
||||||
|
if (*bypass)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (sun4i_pwm->data->has_prescaler_bypass) {
|
if (sun4i_pwm->data->has_prescaler_bypass) {
|
||||||
/* First, test without any prescaler when available */
|
/* First, test without any prescaler when available */
|
||||||
prescaler = PWM_PRESCAL_MASK;
|
prescaler = PWM_PRESCAL_MASK;
|
||||||
|
@ -170,9 +203,11 @@ static int sun4i_pwm_calculate(struct sun4i_pwm_chip *sun4i_pwm,
|
||||||
if (prescaler == 0) {
|
if (prescaler == 0) {
|
||||||
/* Go up from the first divider */
|
/* Go up from the first divider */
|
||||||
for (prescaler = 0; prescaler < PWM_PRESCAL_MASK; prescaler++) {
|
for (prescaler = 0; prescaler < PWM_PRESCAL_MASK; prescaler++) {
|
||||||
if (!prescaler_table[prescaler])
|
unsigned int pval = prescaler_table[prescaler];
|
||||||
|
|
||||||
|
if (!pval)
|
||||||
continue;
|
continue;
|
||||||
pval = prescaler_table[prescaler];
|
|
||||||
div = clk_rate;
|
div = clk_rate;
|
||||||
do_div(div, pval);
|
do_div(div, pval);
|
||||||
div = div * state->period;
|
div = div * state->period;
|
||||||
|
@ -199,10 +234,11 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||||
{
|
{
|
||||||
struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip);
|
struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip);
|
||||||
struct pwm_state cstate;
|
struct pwm_state cstate;
|
||||||
u32 ctrl;
|
u32 ctrl, duty = 0, period = 0, val;
|
||||||
int ret;
|
int ret;
|
||||||
unsigned int delay_us;
|
unsigned int delay_us, prescaler = 0;
|
||||||
unsigned long now;
|
unsigned long now;
|
||||||
|
bool bypass;
|
||||||
|
|
||||||
pwm_get_state(pwm, &cstate);
|
pwm_get_state(pwm, &cstate);
|
||||||
|
|
||||||
|
@ -214,46 +250,52 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = sun4i_pwm_calculate(sun4i_pwm, state, &duty, &period, &prescaler,
|
||||||
|
&bypass);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(chip->dev, "period exceeds the maximum value\n");
|
||||||
|
if (!cstate.enabled)
|
||||||
|
clk_disable_unprepare(sun4i_pwm->clk);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
spin_lock(&sun4i_pwm->ctrl_lock);
|
spin_lock(&sun4i_pwm->ctrl_lock);
|
||||||
ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
|
ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
|
||||||
|
|
||||||
if ((cstate.period != state->period) ||
|
if (sun4i_pwm->data->has_direct_mod_clk_output) {
|
||||||
(cstate.duty_cycle != state->duty_cycle)) {
|
if (bypass) {
|
||||||
u32 period, duty, val;
|
ctrl |= BIT_CH(PWM_BYPASS, pwm->hwpwm);
|
||||||
unsigned int prescaler;
|
/* We can skip other parameter */
|
||||||
|
|
||||||
ret = sun4i_pwm_calculate(sun4i_pwm, state,
|
|
||||||
&duty, &period, &prescaler);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(chip->dev, "period exceeds the maximum value\n");
|
|
||||||
spin_unlock(&sun4i_pwm->ctrl_lock);
|
|
||||||
if (!cstate.enabled)
|
|
||||||
clk_disable_unprepare(sun4i_pwm->clk);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PWM_REG_PRESCAL(ctrl, pwm->hwpwm) != prescaler) {
|
|
||||||
/* Prescaler changed, the clock has to be gated */
|
|
||||||
ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
|
|
||||||
sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG);
|
sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG);
|
||||||
|
spin_unlock(&sun4i_pwm->ctrl_lock);
|
||||||
ctrl &= ~BIT_CH(PWM_PRESCAL_MASK, pwm->hwpwm);
|
return 0;
|
||||||
ctrl |= BIT_CH(prescaler, pwm->hwpwm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val = (duty & PWM_DTY_MASK) | PWM_PRD(period);
|
ctrl &= ~BIT_CH(PWM_BYPASS, pwm->hwpwm);
|
||||||
sun4i_pwm_writel(sun4i_pwm, val, PWM_CH_PRD(pwm->hwpwm));
|
|
||||||
sun4i_pwm->next_period[pwm->hwpwm] = jiffies +
|
|
||||||
usecs_to_jiffies(cstate.period / 1000 + 1);
|
|
||||||
sun4i_pwm->needs_delay[pwm->hwpwm] = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (PWM_REG_PRESCAL(ctrl, pwm->hwpwm) != prescaler) {
|
||||||
|
/* Prescaler changed, the clock has to be gated */
|
||||||
|
ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
|
||||||
|
sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG);
|
||||||
|
|
||||||
|
ctrl &= ~BIT_CH(PWM_PRESCAL_MASK, pwm->hwpwm);
|
||||||
|
ctrl |= BIT_CH(prescaler, pwm->hwpwm);
|
||||||
|
}
|
||||||
|
|
||||||
|
val = (duty & PWM_DTY_MASK) | PWM_PRD(period);
|
||||||
|
sun4i_pwm_writel(sun4i_pwm, val, PWM_CH_PRD(pwm->hwpwm));
|
||||||
|
sun4i_pwm->next_period[pwm->hwpwm] = jiffies +
|
||||||
|
usecs_to_jiffies(cstate.period / 1000 + 1);
|
||||||
|
sun4i_pwm->needs_delay[pwm->hwpwm] = true;
|
||||||
|
|
||||||
if (state->polarity != PWM_POLARITY_NORMAL)
|
if (state->polarity != PWM_POLARITY_NORMAL)
|
||||||
ctrl &= ~BIT_CH(PWM_ACT_STATE, pwm->hwpwm);
|
ctrl &= ~BIT_CH(PWM_ACT_STATE, pwm->hwpwm);
|
||||||
else
|
else
|
||||||
ctrl |= BIT_CH(PWM_ACT_STATE, pwm->hwpwm);
|
ctrl |= BIT_CH(PWM_ACT_STATE, pwm->hwpwm);
|
||||||
|
|
||||||
ctrl |= BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
|
ctrl |= BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
|
||||||
|
|
||||||
if (state->enabled) {
|
if (state->enabled) {
|
||||||
ctrl |= BIT_CH(PWM_EN, pwm->hwpwm);
|
ctrl |= BIT_CH(PWM_EN, pwm->hwpwm);
|
||||||
} else if (!sun4i_pwm->needs_delay[pwm->hwpwm]) {
|
} else if (!sun4i_pwm->needs_delay[pwm->hwpwm]) {
|
||||||
|
@ -319,6 +361,12 @@ static const struct sun4i_pwm_data sun4i_pwm_single_bypass = {
|
||||||
.npwm = 1,
|
.npwm = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct sun4i_pwm_data sun50i_h6_pwm_data = {
|
||||||
|
.has_prescaler_bypass = true,
|
||||||
|
.has_direct_mod_clk_output = true,
|
||||||
|
.npwm = 2,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct of_device_id sun4i_pwm_dt_ids[] = {
|
static const struct of_device_id sun4i_pwm_dt_ids[] = {
|
||||||
{
|
{
|
||||||
.compatible = "allwinner,sun4i-a10-pwm",
|
.compatible = "allwinner,sun4i-a10-pwm",
|
||||||
|
@ -335,6 +383,9 @@ static const struct of_device_id sun4i_pwm_dt_ids[] = {
|
||||||
}, {
|
}, {
|
||||||
.compatible = "allwinner,sun8i-h3-pwm",
|
.compatible = "allwinner,sun8i-h3-pwm",
|
||||||
.data = &sun4i_pwm_single_bypass,
|
.data = &sun4i_pwm_single_bypass,
|
||||||
|
}, {
|
||||||
|
.compatible = "allwinner,sun50i-h6-pwm",
|
||||||
|
.data = &sun50i_h6_pwm_data,
|
||||||
}, {
|
}, {
|
||||||
/* sentinel */
|
/* sentinel */
|
||||||
},
|
},
|
||||||
|
@ -360,9 +411,69 @@ static int sun4i_pwm_probe(struct platform_device *pdev)
|
||||||
if (IS_ERR(pwm->base))
|
if (IS_ERR(pwm->base))
|
||||||
return PTR_ERR(pwm->base);
|
return PTR_ERR(pwm->base);
|
||||||
|
|
||||||
pwm->clk = devm_clk_get(&pdev->dev, NULL);
|
/*
|
||||||
if (IS_ERR(pwm->clk))
|
* All hardware variants need a source clock that is divided and
|
||||||
|
* then feeds the counter that defines the output wave form. In the
|
||||||
|
* device tree this clock is either unnamed or called "mod".
|
||||||
|
* Some variants (e.g. H6) need another clock to access the
|
||||||
|
* hardware registers; this is called "bus".
|
||||||
|
* So we request "mod" first (and ignore the corner case that a
|
||||||
|
* parent provides a "mod" clock while the right one would be the
|
||||||
|
* unnamed one of the PWM device) and if this is not found we fall
|
||||||
|
* back to the first clock of the PWM.
|
||||||
|
*/
|
||||||
|
pwm->clk = devm_clk_get_optional(&pdev->dev, "mod");
|
||||||
|
if (IS_ERR(pwm->clk)) {
|
||||||
|
if (PTR_ERR(pwm->clk) != -EPROBE_DEFER)
|
||||||
|
dev_err(&pdev->dev, "get mod clock failed %pe\n",
|
||||||
|
pwm->clk);
|
||||||
return PTR_ERR(pwm->clk);
|
return PTR_ERR(pwm->clk);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pwm->clk) {
|
||||||
|
pwm->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
|
if (IS_ERR(pwm->clk)) {
|
||||||
|
if (PTR_ERR(pwm->clk) != -EPROBE_DEFER)
|
||||||
|
dev_err(&pdev->dev, "get unnamed clock failed %pe\n",
|
||||||
|
pwm->clk);
|
||||||
|
return PTR_ERR(pwm->clk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pwm->bus_clk = devm_clk_get_optional(&pdev->dev, "bus");
|
||||||
|
if (IS_ERR(pwm->bus_clk)) {
|
||||||
|
if (PTR_ERR(pwm->bus_clk) != -EPROBE_DEFER)
|
||||||
|
dev_err(&pdev->dev, "get bus clock failed %pe\n",
|
||||||
|
pwm->bus_clk);
|
||||||
|
return PTR_ERR(pwm->bus_clk);
|
||||||
|
}
|
||||||
|
|
||||||
|
pwm->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
|
||||||
|
if (IS_ERR(pwm->rst)) {
|
||||||
|
if (PTR_ERR(pwm->rst) != -EPROBE_DEFER)
|
||||||
|
dev_err(&pdev->dev, "get reset failed %pe\n",
|
||||||
|
pwm->rst);
|
||||||
|
return PTR_ERR(pwm->rst);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Deassert reset */
|
||||||
|
ret = reset_control_deassert(pwm->rst);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "cannot deassert reset control: %pe\n",
|
||||||
|
ERR_PTR(ret));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We're keeping the bus clock on for the sake of simplicity.
|
||||||
|
* Actually it only needs to be on for hardware register accesses.
|
||||||
|
*/
|
||||||
|
ret = clk_prepare_enable(pwm->bus_clk);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "cannot prepare and enable bus_clk %pe\n",
|
||||||
|
ERR_PTR(ret));
|
||||||
|
goto err_bus;
|
||||||
|
}
|
||||||
|
|
||||||
pwm->chip.dev = &pdev->dev;
|
pwm->chip.dev = &pdev->dev;
|
||||||
pwm->chip.ops = &sun4i_pwm_ops;
|
pwm->chip.ops = &sun4i_pwm_ops;
|
||||||
|
@ -376,19 +487,34 @@ static int sun4i_pwm_probe(struct platform_device *pdev)
|
||||||
ret = pwmchip_add(&pwm->chip);
|
ret = pwmchip_add(&pwm->chip);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
|
dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
|
||||||
return ret;
|
goto err_pwm_add;
|
||||||
}
|
}
|
||||||
|
|
||||||
platform_set_drvdata(pdev, pwm);
|
platform_set_drvdata(pdev, pwm);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_pwm_add:
|
||||||
|
clk_disable_unprepare(pwm->bus_clk);
|
||||||
|
err_bus:
|
||||||
|
reset_control_assert(pwm->rst);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sun4i_pwm_remove(struct platform_device *pdev)
|
static int sun4i_pwm_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct sun4i_pwm_chip *pwm = platform_get_drvdata(pdev);
|
struct sun4i_pwm_chip *pwm = platform_get_drvdata(pdev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
return pwmchip_remove(&pwm->chip);
|
ret = pwmchip_remove(&pwm->chip);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
clk_disable_unprepare(pwm->bus_clk);
|
||||||
|
reset_control_assert(pwm->rst);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_driver sun4i_pwm_driver = {
|
static struct platform_driver sun4i_pwm_driver = {
|
||||||
|
|
58
include/trace/events/pwm.h
Normal file
58
include/trace/events/pwm.h
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
#undef TRACE_SYSTEM
|
||||||
|
#define TRACE_SYSTEM pwm
|
||||||
|
|
||||||
|
#if !defined(_TRACE_PWM_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||||
|
#define _TRACE_PWM_H
|
||||||
|
|
||||||
|
#include <linux/pwm.h>
|
||||||
|
#include <linux/tracepoint.h>
|
||||||
|
|
||||||
|
DECLARE_EVENT_CLASS(pwm,
|
||||||
|
|
||||||
|
TP_PROTO(struct pwm_device *pwm, const struct pwm_state *state),
|
||||||
|
|
||||||
|
TP_ARGS(pwm, state),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__field(struct pwm_device *, pwm)
|
||||||
|
__field(u64, period)
|
||||||
|
__field(u64, duty_cycle)
|
||||||
|
__field(enum pwm_polarity, polarity)
|
||||||
|
__field(bool, enabled)
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
__entry->pwm = pwm;
|
||||||
|
__entry->period = state->period;
|
||||||
|
__entry->duty_cycle = state->duty_cycle;
|
||||||
|
__entry->polarity = state->polarity;
|
||||||
|
__entry->enabled = state->enabled;
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk("%p: period=%llu duty_cycle=%llu polarity=%d enabled=%d",
|
||||||
|
__entry->pwm, __entry->period, __entry->duty_cycle,
|
||||||
|
__entry->polarity, __entry->enabled)
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_EVENT(pwm, pwm_apply,
|
||||||
|
|
||||||
|
TP_PROTO(struct pwm_device *pwm, const struct pwm_state *state),
|
||||||
|
|
||||||
|
TP_ARGS(pwm, state)
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_EVENT(pwm, pwm_get,
|
||||||
|
|
||||||
|
TP_PROTO(struct pwm_device *pwm, const struct pwm_state *state),
|
||||||
|
|
||||||
|
TP_ARGS(pwm, state)
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif /* _TRACE_PWM_H */
|
||||||
|
|
||||||
|
/* This part must be outside protection */
|
||||||
|
#include <trace/define_trace.h>
|
Loading…
Reference in a new issue