Skip to content

Commit

Permalink
Merge tag 'pwm/for-5.6-rc1' of git://git.kernel.org/pub/scm/linux/ker…
Browse files Browse the repository at this point in the history
…nel/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
  ...
  • Loading branch information
torvalds committed Feb 5, 2020
2 parents 18ea671 + 9871abf commit 4c7d00c
Show file tree
Hide file tree
Showing 13 changed files with 539 additions and 199 deletions.
4 changes: 2 additions & 2 deletions Documentation/devicetree/bindings/pwm/mxs-pwm.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Freescale MXS PWM controller
Required properties:
- compatible: should be "fsl,imx23-pwm"
- 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.
- fsl,pwm-number: the number of PWM devices

Expand All @@ -12,6 +12,6 @@ Example:
pwm: pwm@80064000 {
compatible = "fsl,imx28-pwm", "fsl,imx23-pwm";
reg = <0x80064000 0x2000>;
#pwm-cells = <2>;
#pwm-cells = <3>;
fsl,pwm-number = <8>;
};
9 changes: 5 additions & 4 deletions drivers/pwm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ config PWM_BCM_KONA

config PWM_BCM2835
tristate "BCM2835 PWM support"
depends on ARCH_BCM2835
depends on ARCH_BCM2835 || ARCH_BRCMSTB
help
PWM framework driver for BCM2835 controller (Raspberry Pi)

Expand Down Expand Up @@ -328,7 +328,8 @@ config PWM_MXS

config PWM_OMAP_DMTIMER
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
Generic PWM framework driver for OMAP Dual-Mode Timer PWM output

Expand Down Expand Up @@ -490,7 +491,7 @@ config PWM_TEGRA
To compile this driver as a module, choose M here: the module
will be called pwm-tegra.

config PWM_TIECAP
config PWM_TIECAP
tristate "ECAP PWM support"
depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_KEYSTONE || ARCH_K3
help
Expand All @@ -499,7 +500,7 @@ config PWM_TIECAP
To compile this driver as a module, choose M here: the module
will be called pwm-tiecap.

config PWM_TIEHRPWM
config PWM_TIEHRPWM
tristate "EHRPWM PWM support"
depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_K3
help
Expand Down
13 changes: 10 additions & 3 deletions drivers/pwm/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@

#include <dt-bindings/pwm/pwm.h>

#define CREATE_TRACE_POINTS
#include <trace/events/pwm.h>

#define MAX_PWMS 1024

static DEFINE_MUTEX(pwm_lookup_lock);
Expand Down Expand Up @@ -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);
pwm->label = label;

Expand Down Expand Up @@ -283,9 +291,6 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
pwm->hwpwm = i;
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);
}

Expand Down Expand Up @@ -472,6 +477,8 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state)
if (err)
return err;

trace_pwm_apply(pwm, state);

pwm->state = *state;
} else {
/*
Expand Down
87 changes: 74 additions & 13 deletions drivers/pwm/pwm-atmel.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@
*
* Copyright (C) 2013 Atmel Corporation
* Bo Shen <[email protected]>
*
* 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>
Expand Down Expand Up @@ -47,6 +60,8 @@
#define PWMV2_CPRD 0x0C
#define PWMV2_CPRDUPD 0x10

#define PWM_MAX_PRES 10

struct atmel_pwm_registers {
u8 period;
u8 period_upd;
Expand All @@ -55,8 +70,7 @@ struct atmel_pwm_registers {
};

struct atmel_pwm_config {
u32 max_period;
u32 max_pres;
u32 period_bits;
};

struct atmel_pwm_data {
Expand Down Expand Up @@ -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;

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,
Expand All @@ -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;

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,
Expand All @@ -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);
unsigned long long cycles = state->period;
int shift;

/* Calculate the period cycles and prescale value */
cycles *= clk_get_rate(atmel_pwm->clk);
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");
return -EINVAL;
} else if (shift > 0) {
*pres = shift;
cycles >>= *pres;
} else {
*pres = 0;
}

*cprd = cycles;
Expand Down Expand Up @@ -271,8 +295,48 @@ static int atmel_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
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 = {
.apply = atmel_pwm_apply,
.get_state = atmel_pwm_get_state,
.owner = THIS_MODULE,
};

Expand All @@ -285,8 +349,7 @@ static const struct atmel_pwm_data atmel_sam9rl_pwm_data = {
},
.cfg = {
/* 16 bits to keep period and duty. */
.max_period = 0xffff,
.max_pres = 10,
.period_bits = 16,
},
};

Expand All @@ -299,8 +362,7 @@ static const struct atmel_pwm_data atmel_sama5_pwm_data = {
},
.cfg = {
/* 16 bits to keep period and duty. */
.max_period = 0xffff,
.max_pres = 10,
.period_bits = 16,
},
};

Expand All @@ -313,8 +375,7 @@ static const struct atmel_pwm_data mchp_sam9x60_pwm_data = {
},
.cfg = {
/* 32 bits to keep period and duty. */
.max_period = 0xffffffff,
.max_pres = 10,
.period_bits = 32,
},
};

Expand Down
58 changes: 54 additions & 4 deletions drivers/pwm/pwm-cros-ec.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,39 @@ struct cros_ec_pwm_device {
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)
{
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)
{
struct {
Expand Down Expand Up @@ -96,7 +124,9 @@ 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);
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 */
if (state->period != EC_PWM_MAX_DUTY)
Expand All @@ -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;

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,
struct pwm_state *state)
{
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;

ret = cros_ec_pwm_get_duty(ec_pwm->ec, pwm->hwpwm);
Expand All @@ -126,8 +163,19 @@ static void cros_ec_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
state->enabled = (ret > 0);
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 *
Expand All @@ -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 = {
.request = cros_ec_pwm_request,
.free = cros_ec_pwm_free,
.get_state = cros_ec_pwm_get_state,
.apply = cros_ec_pwm_apply,
.owner = THIS_MODULE,
Expand Down
Loading

0 comments on commit 4c7d00c

Please sign in to comment.