Commit 38f7d2da authored by Linus Torvalds's avatar Linus Torvalds

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

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

Pull pwm updates from Thierry Reding:
 "This release cycle's changes include mostly updates and cleanups to
  existing drivers along with a few cleanups to the core, documentation
  and device tree bindings"

* tag 'pwm/for-4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm:
  pwm: cros-ec: Fix transposed param settings
  pwm: meson: Improve PWM calculation precision
  dt-bindings: pwm: meson: Add compatible for gxbb ao PWMs
  pwm: meson: Add compatible for the gxbb ao PWMs
  pwm: sun4i: Drop legacy callbacks
  pwm: sun4i: Switch to atomic PWM
  pwm: sun4i: Improve hardware read out
  pwm: hibvt: Constify hibvt_pwm_ops
  pwm: Silently error out on EPROBE_DEFER
  pwm: Standardize document format
  pwm: bfin: Remove unneeded error message
  dt-bindings: pwm: Update STM32 timers clock names
  dt-bindings: pwm: Add R-Car M3-W device tree bindings
  pwm: tegra: Set maximum pwm clock source per SoC tapeout
parents dc087d1e 5ec8c48a
...@@ -2,7 +2,9 @@ Amlogic Meson PWM Controller ...@@ -2,7 +2,9 @@ Amlogic Meson PWM Controller
============================ ============================
Required properties: Required properties:
- compatible: Shall contain "amlogic,meson8b-pwm" or "amlogic,meson-gxbb-pwm". - compatible: Shall contain "amlogic,meson8b-pwm"
or "amlogic,meson-gxbb-pwm"
or "amlogic,meson-gxbb-ao-pwm"
- #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.
......
...@@ -24,7 +24,7 @@ Example: ...@@ -24,7 +24,7 @@ Example:
compatible = "st,stm32-timers"; compatible = "st,stm32-timers";
reg = <0x40010000 0x400>; reg = <0x40010000 0x400>;
clocks = <&rcc 0 160>; clocks = <&rcc 0 160>;
clock-names = "clk_int"; clock-names = "int";
pwm { pwm {
compatible = "st,stm32-pwm"; compatible = "st,stm32-pwm";
......
...@@ -8,6 +8,7 @@ Required Properties: ...@@ -8,6 +8,7 @@ Required Properties:
- "renesas,pwm-r8a7791": for R-Car M2-W - "renesas,pwm-r8a7791": for R-Car M2-W
- "renesas,pwm-r8a7794": for R-Car E2 - "renesas,pwm-r8a7794": for R-Car E2
- "renesas,pwm-r8a7795": for R-Car H3 - "renesas,pwm-r8a7795": for R-Car H3
- "renesas,pwm-r8a7796": for R-Car M3-W
- reg: base address and length of the registers block for the PWM. - reg: base address and length of the registers block for the PWM.
- #pwm-cells: should be 2. See pwm.txt in this directory for a description of - #pwm-cells: should be 2. See pwm.txt in this directory for a description of
the cells format. the cells format.
......
======================================
Pulse Width Modulation (PWM) interface Pulse Width Modulation (PWM) interface
======================================
This provides an overview about the Linux PWM interface This provides an overview about the Linux PWM interface
...@@ -16,7 +18,7 @@ Users of the legacy PWM API use unique IDs to refer to PWM devices. ...@@ -16,7 +18,7 @@ Users of the legacy PWM API use unique IDs to refer to PWM devices.
Instead of referring to a PWM device via its unique ID, board setup code Instead of referring to a PWM device via its unique ID, board setup code
should instead register a static mapping that can be used to match PWM should instead register a static mapping that can be used to match PWM
consumers to providers, as given in the following example: consumers to providers, as given in the following example::
static struct pwm_lookup board_pwm_lookup[] = { static struct pwm_lookup board_pwm_lookup[] = {
PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight", NULL, PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight", NULL,
...@@ -40,9 +42,9 @@ New users should use the pwm_get() function and pass to it the consumer ...@@ -40,9 +42,9 @@ New users should use the pwm_get() function and pass to it the consumer
device or a consumer name. pwm_put() is used to free the PWM device. Managed device or a consumer name. pwm_put() is used to free the PWM device. Managed
variants of these functions, devm_pwm_get() and devm_pwm_put(), also exist. variants of these functions, devm_pwm_get() and devm_pwm_put(), also exist.
After being requested, a PWM has to be configured using: After being requested, a PWM has to be configured using::
int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state); int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state);
This API controls both the PWM period/duty_cycle config and the This API controls both the PWM period/duty_cycle config and the
enable/disable state. enable/disable state.
...@@ -72,11 +74,14 @@ interface is provided to use the PWMs from userspace. It is exposed at ...@@ -72,11 +74,14 @@ interface is provided to use the PWMs from userspace. It is exposed at
pwmchipN, where N is the base of the PWM chip. Inside the directory you pwmchipN, where N is the base of the PWM chip. Inside the directory you
will find: will find:
npwm - The number of PWM channels this chip supports (read-only). npwm
The number of PWM channels this chip supports (read-only).
export - Exports a PWM channel for use with sysfs (write-only). export
Exports a PWM channel for use with sysfs (write-only).
unexport - Unexports a PWM channel from sysfs (write-only). unexport
Unexports a PWM channel from sysfs (write-only).
The PWM channels are numbered using a per-chip index from 0 to npwm-1. The PWM channels are numbered using a per-chip index from 0 to npwm-1.
...@@ -84,21 +89,26 @@ When a PWM channel is exported a pwmX directory will be created in the ...@@ -84,21 +89,26 @@ When a PWM channel is exported a pwmX directory will be created in the
pwmchipN directory it is associated with, where X is the number of the pwmchipN directory it is associated with, where X is the number of the
channel that was exported. The following properties will then be available: channel that was exported. The following properties will then be available:
period - The total period of the PWM signal (read/write). period
Value is in nanoseconds and is the sum of the active and inactive The total period of the PWM signal (read/write).
time of the PWM. Value is in nanoseconds and is the sum of the active and inactive
time of the PWM.
duty_cycle - The active time of the PWM signal (read/write). duty_cycle
Value is in nanoseconds and must be less than the period. The active time of the PWM signal (read/write).
Value is in nanoseconds and must be less than the period.
polarity - Changes the polarity of the PWM signal (read/write). polarity
Writes to this property only work if the PWM chip supports changing Changes the polarity of the PWM signal (read/write).
the polarity. The polarity can only be changed if the PWM is not Writes to this property only work if the PWM chip supports changing
enabled. Value is the string "normal" or "inversed". the polarity. The polarity can only be changed if the PWM is not
enabled. Value is the string "normal" or "inversed".
enable - Enable/disable the PWM signal (read/write). enable
0 - disabled Enable/disable the PWM signal (read/write).
1 - enabled
- 0 - disabled
- 1 - enabled
Implementing a PWM driver Implementing a PWM driver
------------------------- -------------------------
......
...@@ -678,7 +678,9 @@ struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id) ...@@ -678,7 +678,9 @@ struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id)
pc = of_node_to_pwmchip(args.np); pc = of_node_to_pwmchip(args.np);
if (IS_ERR(pc)) { if (IS_ERR(pc)) {
pr_err("%s(): PWM chip not found\n", __func__); if (PTR_ERR(pc) != -EPROBE_DEFER)
pr_err("%s(): PWM chip not found\n", __func__);
pwm = ERR_CAST(pc); pwm = ERR_CAST(pc);
goto put; goto put;
} }
......
...@@ -118,10 +118,8 @@ static int bfin_pwm_probe(struct platform_device *pdev) ...@@ -118,10 +118,8 @@ static int bfin_pwm_probe(struct platform_device *pdev)
int ret; int ret;
pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
if (!pwm) { if (!pwm)
dev_err(&pdev->dev, "failed to allocate memory\n");
return -ENOMEM; return -ENOMEM;
}
platform_set_drvdata(pdev, pwm); platform_set_drvdata(pdev, pwm);
......
...@@ -75,8 +75,8 @@ static int __cros_ec_pwm_get_duty(struct cros_ec_device *ec, u8 index, ...@@ -75,8 +75,8 @@ static int __cros_ec_pwm_get_duty(struct cros_ec_device *ec, u8 index,
msg->version = 0; msg->version = 0;
msg->command = EC_CMD_PWM_GET_DUTY; msg->command = EC_CMD_PWM_GET_DUTY;
msg->insize = sizeof(*params); msg->insize = sizeof(*resp);
msg->outsize = sizeof(*resp); msg->outsize = sizeof(*params);
params->pwm_type = EC_PWM_TYPE_GENERIC; params->pwm_type = EC_PWM_TYPE_GENERIC;
params->index = index; params->index = index;
......
...@@ -165,7 +165,7 @@ static int hibvt_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -165,7 +165,7 @@ static int hibvt_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
return 0; return 0;
} }
static struct pwm_ops hibvt_pwm_ops = { static const struct pwm_ops hibvt_pwm_ops = {
.get_state = hibvt_pwm_get_state, .get_state = hibvt_pwm_get_state,
.apply = hibvt_pwm_apply, .apply = hibvt_pwm_apply,
......
...@@ -103,6 +103,7 @@ struct meson_pwm_channel { ...@@ -103,6 +103,7 @@ struct meson_pwm_channel {
struct meson_pwm_data { struct meson_pwm_data {
const char * const *parent_names; const char * const *parent_names;
unsigned int num_parents;
}; };
struct meson_pwm { struct meson_pwm {
...@@ -162,7 +163,8 @@ static int meson_pwm_calc(struct meson_pwm *meson, ...@@ -162,7 +163,8 @@ static int meson_pwm_calc(struct meson_pwm *meson,
unsigned int duty, unsigned int period) unsigned int duty, unsigned int period)
{ {
unsigned int pre_div, cnt, duty_cnt; unsigned int pre_div, cnt, duty_cnt;
unsigned long fin_freq = -1, fin_ns; unsigned long fin_freq = -1;
u64 fin_ps;
if (~(meson->inverter_mask >> id) & 0x1) if (~(meson->inverter_mask >> id) & 0x1)
duty = period - duty; duty = period - duty;
...@@ -178,13 +180,15 @@ static int meson_pwm_calc(struct meson_pwm *meson, ...@@ -178,13 +180,15 @@ static int meson_pwm_calc(struct meson_pwm *meson,
} }
dev_dbg(meson->chip.dev, "fin_freq: %lu Hz\n", fin_freq); dev_dbg(meson->chip.dev, "fin_freq: %lu Hz\n", fin_freq);
fin_ns = NSEC_PER_SEC / fin_freq; fin_ps = (u64)NSEC_PER_SEC * 1000;
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(period, fin_ns * (pre_div + 1)); cnt = DIV_ROUND_CLOSEST_ULL((u64)period * 1000,
dev_dbg(meson->chip.dev, "fin_ns=%lu pre_div=%u cnt=%u\n", fin_ps * (pre_div + 1));
fin_ns, pre_div, cnt); dev_dbg(meson->chip.dev, "fin_ps=%llu pre_div=%u cnt=%u\n",
fin_ps, pre_div, cnt);
if (cnt <= 0xffff) if (cnt <= 0xffff)
break; break;
} }
...@@ -207,7 +211,8 @@ static int meson_pwm_calc(struct meson_pwm *meson, ...@@ -207,7 +211,8 @@ static int meson_pwm_calc(struct meson_pwm *meson,
channel->lo = cnt; channel->lo = cnt;
} else { } else {
/* Then check is we can have the duty with the same pre_div */ /* Then check is we can have the duty with the same pre_div */
duty_cnt = DIV_ROUND_CLOSEST(duty, fin_ns * (pre_div + 1)); duty_cnt = DIV_ROUND_CLOSEST_ULL((u64)duty * 1000,
fin_ps * (pre_div + 1));
if (duty_cnt > 0xffff) { if (duty_cnt > 0xffff) {
dev_err(meson->chip.dev, "unable to get duty cycle\n"); dev_err(meson->chip.dev, "unable to get duty cycle\n");
return -EINVAL; return -EINVAL;
...@@ -381,6 +386,7 @@ static const char * const pwm_meson8b_parent_names[] = { ...@@ -381,6 +386,7 @@ static const char * const pwm_meson8b_parent_names[] = {
static const struct meson_pwm_data pwm_meson8b_data = { static const struct meson_pwm_data pwm_meson8b_data = {
.parent_names = pwm_meson8b_parent_names, .parent_names = pwm_meson8b_parent_names,
.num_parents = ARRAY_SIZE(pwm_meson8b_parent_names),
}; };
static const char * const pwm_gxbb_parent_names[] = { static const char * const pwm_gxbb_parent_names[] = {
...@@ -389,11 +395,35 @@ static const char * const pwm_gxbb_parent_names[] = { ...@@ -389,11 +395,35 @@ static const char * const pwm_gxbb_parent_names[] = {
static const struct meson_pwm_data pwm_gxbb_data = { static const struct meson_pwm_data pwm_gxbb_data = {
.parent_names = pwm_gxbb_parent_names, .parent_names = pwm_gxbb_parent_names,
.num_parents = ARRAY_SIZE(pwm_gxbb_parent_names),
};
/*
* Only the 2 first inputs of the GXBB AO PWMs are valid
* The last 2 are grounded
*/
static const char * const pwm_gxbb_ao_parent_names[] = {
"xtal", "clk81"
};
static const struct meson_pwm_data pwm_gxbb_ao_data = {
.parent_names = pwm_gxbb_ao_parent_names,
.num_parents = ARRAY_SIZE(pwm_gxbb_ao_parent_names),
}; };
static const struct of_device_id meson_pwm_matches[] = { static const struct of_device_id meson_pwm_matches[] = {
{ .compatible = "amlogic,meson8b-pwm", .data = &pwm_meson8b_data }, {
{ .compatible = "amlogic,meson-gxbb-pwm", .data = &pwm_gxbb_data }, .compatible = "amlogic,meson8b-pwm",
.data = &pwm_meson8b_data
},
{
.compatible = "amlogic,meson-gxbb-pwm",
.data = &pwm_gxbb_data
},
{
.compatible = "amlogic,meson-gxbb-ao-pwm",
.data = &pwm_gxbb_ao_data
},
{}, {},
}; };
MODULE_DEVICE_TABLE(of, meson_pwm_matches); MODULE_DEVICE_TABLE(of, meson_pwm_matches);
...@@ -417,7 +447,7 @@ static int meson_pwm_init_channels(struct meson_pwm *meson, ...@@ -417,7 +447,7 @@ static int meson_pwm_init_channels(struct meson_pwm *meson,
init.ops = &clk_mux_ops; init.ops = &clk_mux_ops;
init.flags = CLK_IS_BASIC; init.flags = CLK_IS_BASIC;
init.parent_names = meson->data->parent_names; init.parent_names = meson->data->parent_names;
init.num_parents = 1 << MISC_CLK_SEL_WIDTH; init.num_parents = meson->data->num_parents;
channel->mux.reg = meson->base + REG_MISC_AB; channel->mux.reg = meson->base + REG_MISC_AB;
channel->mux.shift = mux_reg_shifts[i]; channel->mux.shift = mux_reg_shifts[i];
......
...@@ -8,8 +8,10 @@ ...@@ -8,8 +8,10 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/jiffies.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
...@@ -44,6 +46,10 @@ ...@@ -44,6 +46,10 @@
#define PWM_DTY_MASK GENMASK(15, 0) #define PWM_DTY_MASK GENMASK(15, 0)
#define PWM_REG_PRD(reg) ((((reg) >> 16) & PWM_PRD_MASK) + 1)
#define PWM_REG_DTY(reg) ((reg) & PWM_DTY_MASK)
#define PWM_REG_PRESCAL(reg, chan) (((reg) >> ((chan) * PWMCH_OFFSET)) & PWM_PRESCAL_MASK)
#define BIT_CH(bit, chan) ((bit) << ((chan) * PWMCH_OFFSET)) #define BIT_CH(bit, chan) ((bit) << ((chan) * PWMCH_OFFSET))
static const u32 prescaler_table[] = { static const u32 prescaler_table[] = {
...@@ -77,6 +83,8 @@ struct sun4i_pwm_chip { ...@@ -77,6 +83,8 @@ struct sun4i_pwm_chip {
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;
unsigned long next_period[2];
bool needs_delay[2];
}; };
static inline struct sun4i_pwm_chip *to_sun4i_pwm_chip(struct pwm_chip *chip) static inline struct sun4i_pwm_chip *to_sun4i_pwm_chip(struct pwm_chip *chip)
...@@ -96,26 +104,65 @@ static inline void sun4i_pwm_writel(struct sun4i_pwm_chip *chip, ...@@ -96,26 +104,65 @@ static inline void sun4i_pwm_writel(struct sun4i_pwm_chip *chip,
writel(val, chip->base + offset); writel(val, chip->base + offset);
} }
static int sun4i_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, static void sun4i_pwm_get_state(struct pwm_chip *chip,
int duty_ns, int period_ns) struct pwm_device *pwm,
struct pwm_state *state)
{ {
struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip);
u32 prd, dty, val, clk_gate; u64 clk_rate, tmp;
u32 val;
unsigned int prescaler;
clk_rate = clk_get_rate(sun4i_pwm->clk);
val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
if ((val == PWM_PRESCAL_MASK) && sun4i_pwm->data->has_prescaler_bypass)
prescaler = 1;
else
prescaler = prescaler_table[PWM_REG_PRESCAL(val, pwm->hwpwm)];
if (prescaler == 0)
return;
if (val & BIT_CH(PWM_ACT_STATE, pwm->hwpwm))
state->polarity = PWM_POLARITY_NORMAL;
else
state->polarity = PWM_POLARITY_INVERSED;
if (val & BIT_CH(PWM_CLK_GATING | PWM_EN, pwm->hwpwm))
state->enabled = true;
else
state->enabled = false;
val = sun4i_pwm_readl(sun4i_pwm, PWM_CH_PRD(pwm->hwpwm));
tmp = prescaler * NSEC_PER_SEC * PWM_REG_DTY(val);
state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate);
tmp = prescaler * NSEC_PER_SEC * PWM_REG_PRD(val);
state->period = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate);
}
static int sun4i_pwm_calculate(struct sun4i_pwm_chip *sun4i_pwm,
struct pwm_state *state,
u32 *dty, u32 *prd, unsigned int *prsclr)
{
u64 clk_rate, div = 0; u64 clk_rate, div = 0;
unsigned int prescaler = 0; unsigned int pval, prescaler = 0;
int err;
clk_rate = clk_get_rate(sun4i_pwm->clk); clk_rate = clk_get_rate(sun4i_pwm->clk);
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;
pval = 1;
/* /*
* When not using any prescaler, the clock period in nanoseconds * When not using any prescaler, the clock period in nanoseconds
* is not an integer so round it half up instead of * is not an integer so round it half up instead of
* truncating to get less surprising values. * truncating to get less surprising values.
*/ */
div = clk_rate * period_ns + NSEC_PER_SEC / 2; div = clk_rate * state->period + NSEC_PER_SEC / 2;
do_div(div, NSEC_PER_SEC); do_div(div, NSEC_PER_SEC);
if (div - 1 > PWM_PRD_MASK) if (div - 1 > PWM_PRD_MASK)
prescaler = 0; prescaler = 0;
...@@ -126,137 +173,141 @@ static int sun4i_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -126,137 +173,141 @@ static int sun4i_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
for (prescaler = 0; prescaler < PWM_PRESCAL_MASK; prescaler++) { for (prescaler = 0; prescaler < PWM_PRESCAL_MASK; prescaler++) {
if (!prescaler_table[prescaler]) if (!prescaler_table[prescaler])
continue; continue;
pval = prescaler_table[prescaler];
div = clk_rate; div = clk_rate;
do_div(div, prescaler_table[prescaler]); do_div(div, pval);
div = div * period_ns; div = div * state->period;
do_div(div, NSEC_PER_SEC); do_div(div, NSEC_PER_SEC);
if (div - 1 <= PWM_PRD_MASK) if (div - 1 <= PWM_PRD_MASK)
break; break;
} }
if (div - 1 > PWM_PRD_MASK) { if (div - 1 > PWM_PRD_MASK)
dev_err(chip->dev, "period exceeds the maximum value\n");
return -EINVAL; return -EINVAL;
}
}
prd = div;
div *= duty_ns;
do_div(div, period_ns);
dty = div;
err = clk_prepare_enable(sun4i_pwm->clk);
if (err) {
dev_err(chip->dev, "failed to enable PWM clock\n");
return err;
}
spin_lock(&sun4i_pwm->ctrl_lock);
val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
if (sun4i_pwm->data->has_rdy && (val & PWM_RDY(pwm->hwpwm))) {
spin_unlock(&sun4i_pwm->ctrl_lock);
clk_disable_unprepare(sun4i_pwm->clk);
return -EBUSY;
}
clk_gate = val & BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
if (clk_gate) {
val &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG);
} }
val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); *prd = div;
val &= ~BIT_CH(PWM_PRESCAL_MASK, pwm->hwpwm); div *= state->duty_cycle;
val |= BIT_CH(prescaler, pwm->hwpwm); do_div(div, state->period);
sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG); *dty = div;
*prsclr = prescaler;
val = (dty & PWM_DTY_MASK) | PWM_PRD(prd);
sun4i_pwm_writel(sun4i_pwm, val, PWM_CH_PRD(pwm->hwpwm));
if (clk_gate) { div = (u64)pval * NSEC_PER_SEC * *prd;
val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); state->period = DIV_ROUND_CLOSEST_ULL(div, clk_rate);
val |= clk_gate;
sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG);
}
spin_unlock(&sun4i_pwm->ctrl_lock); div = (u64)pval * NSEC_PER_SEC * *dty;
clk_disable_unprepare(sun4i_pwm->clk); state->duty_cycle = DIV_ROUND_CLOSEST_ULL(div, clk_rate);
return 0; return 0;
} }
static int sun4i_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
enum pwm_polarity polarity) struct pwm_state *state)
{ {
struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip);
u32 val; struct pwm_state cstate;
u32 ctrl;
int ret; int ret;
unsigned int delay_us;
unsigned long now;
ret = clk_prepare_enable(sun4i_pwm->clk); pwm_get_state(pwm, &cstate);
if (ret) {
dev_err(chip->dev, "failed to enable PWM clock\n"); if (!cstate.enabled) {
return ret; ret = clk_prepare_enable(sun4i_pwm->clk);
if (ret) {
dev_err(chip->dev, "failed to enable PWM clock\n");
return ret;
}
} }
spin_lock(&sun4i_pwm->ctrl_lock); spin_lock(&sun4i_pwm->ctrl_lock);
val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
if (polarity != PWM_POLARITY_NORMAL) if ((cstate.period != state->period) ||
val &= ~BIT_CH(PWM_ACT_STATE, pwm->hwpwm); (cstate.duty_cycle != state->duty_cycle)) {
else u32 period, duty, val;
val |= BIT_CH(PWM_ACT_STATE, pwm->hwpwm); unsigned int prescaler;
sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG); 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;
}
spin_unlock(&sun4i_pwm->ctrl_lock); if (PWM_REG_PRESCAL(ctrl, pwm->hwpwm) != prescaler) {
clk_disable_unprepare(sun4i_pwm->clk); /* 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);
return 0; ctrl &= ~BIT_CH(PWM_PRESCAL_MASK, pwm->hwpwm);
} ctrl |= BIT_CH(prescaler, pwm->hwpwm);
}
static int sun4i_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) val = (duty & PWM_DTY_MASK) | PWM_PRD(period);
{ sun4i_pwm_writel(sun4i_pwm, val, PWM_CH_PRD(pwm->hwpwm));
struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); sun4i_pwm->next_period[pwm->hwpwm] = jiffies +
u32 val; usecs_to_jiffies(cstate.period / 1000 + 1);
int ret; sun4i_pwm->needs_delay[pwm->hwpwm] = true;
}
ret = clk_prepare_enable(sun4i_pwm->clk); if (state->polarity != PWM_POLARITY_NORMAL)
if (ret) { ctrl &= ~BIT_CH(PWM_ACT_STATE, pwm->hwpwm);
dev_err(chip->dev, "failed to enable PWM clock\n"); else
return ret; ctrl |= BIT_CH(PWM_ACT_STATE, pwm->hwpwm);
ctrl |= BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
if (state->enabled) {
ctrl |= BIT_CH(PWM_EN, pwm->hwpwm);
} else if (!sun4i_pwm->needs_delay[pwm->hwpwm]) {
ctrl &= ~BIT_CH(PWM_EN, pwm->hwpwm);
ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
} }
spin_lock(&sun4i_pwm->ctrl_lock); sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG);
val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
val |= BIT_CH(PWM_EN, pwm->hwpwm);
val |= BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG);
spin_unlock(&sun4i_pwm->ctrl_lock); spin_unlock(&sun4i_pwm->ctrl_lock);
return 0; if (state->enabled)
} return 0;
static void sun4i_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) if (!sun4i_pwm->needs_delay[pwm->hwpwm]) {
{ clk_disable_unprepare(sun4i_pwm->clk);
struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip); return 0;
u32 val; }
/* We need a full period to elapse before disabling the channel. */
now = jiffies;
if (sun4i_pwm->needs_delay[pwm->hwpwm] &&
time_before(now, sun4i_pwm->next_period[pwm->hwpwm])) {
delay_us = jiffies_to_usecs(sun4i_pwm->next_period[pwm->hwpwm] -
now);
if ((delay_us / 500) > MAX_UDELAY_MS)
msleep(delay_us / 1000 + 1);
else
usleep_range(delay_us, delay_us * 2);
}
sun4i_pwm->needs_delay[pwm->hwpwm] = false;
spin_lock(&sun4i_pwm->ctrl_lock); spin_lock(&sun4i_pwm->ctrl_lock);
val = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
val &= ~BIT_CH(PWM_EN, pwm->hwpwm); ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
val &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm); ctrl &= ~BIT_CH(PWM_EN, pwm->hwpwm);
sun4i_pwm_writel(sun4i_pwm, val, PWM_CTRL_REG); sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG);
spin_unlock(&sun4i_pwm->ctrl_lock); spin_unlock(&sun4i_pwm->ctrl_lock);
clk_disable_unprepare(sun4i_pwm->clk); clk_disable_unprepare(sun4i_pwm->clk);
return 0;
} }
static const struct pwm_ops sun4i_pwm_ops = { static const struct pwm_ops sun4i_pwm_ops = {
.config = sun4i_pwm_config, .apply = sun4i_pwm_apply,
.set_polarity = sun4i_pwm_set_polarity, .get_state = sun4i_pwm_get_state,
.enable = sun4i_pwm_enable,
.disable = sun4i_pwm_disable,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
...@@ -316,8 +367,7 @@ static int sun4i_pwm_probe(struct platform_device *pdev) ...@@ -316,8 +367,7 @@ static int sun4i_pwm_probe(struct platform_device *pdev)
{ {
struct sun4i_pwm_chip *pwm; struct sun4i_pwm_chip *pwm;
struct resource *res; struct resource *res;
u32 val; int ret;
int i, ret;
const struct of_device_id *match; const struct of_device_id *match;
match = of_match_device(sun4i_pwm_dt_ids, &pdev->dev); match = of_match_device(sun4i_pwm_dt_ids, &pdev->dev);
...@@ -353,24 +403,7 @@ static int sun4i_pwm_probe(struct platform_device *pdev) ...@@ -353,24 +403,7 @@ static int sun4i_pwm_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, pwm); platform_set_drvdata(pdev, pwm);
ret = clk_prepare_enable(pwm->clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable PWM clock\n");
goto clk_error;
}
val = sun4i_pwm_readl(pwm, PWM_CTRL_REG);
for (i = 0; i < pwm->chip.npwm; i++)
if (!(val & BIT_CH(PWM_ACT_STATE, i)))
pwm_set_polarity(&pwm->chip.pwms[i],
PWM_POLARITY_INVERSED);
clk_disable_unprepare(pwm->clk);
return 0; return 0;
clk_error:
pwmchip_remove(&pwm->chip);
return ret;
} }
static int sun4i_pwm_remove(struct platform_device *pdev) static int sun4i_pwm_remove(struct platform_device *pdev)
......
...@@ -41,6 +41,9 @@ ...@@ -41,6 +41,9 @@
struct tegra_pwm_soc { struct tegra_pwm_soc {
unsigned int num_channels; unsigned int num_channels;
/* Maximum IP frequency for given SoCs */
unsigned long max_frequency;
}; };
struct tegra_pwm_chip { struct tegra_pwm_chip {
...@@ -201,7 +204,18 @@ static int tegra_pwm_probe(struct platform_device *pdev) ...@@ -201,7 +204,18 @@ static int tegra_pwm_probe(struct platform_device *pdev)
if (IS_ERR(pwm->clk)) if (IS_ERR(pwm->clk))
return PTR_ERR(pwm->clk); return PTR_ERR(pwm->clk);
/* Read PWM clock rate from source */ /* Set maximum frequency of the IP */
ret = clk_set_rate(pwm->clk, pwm->soc->max_frequency);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to set max frequency: %d\n", ret);
return ret;
}
/*
* The requested and configured frequency may differ due to
* clock register resolutions. Get the configured frequency
* so that PWM period can be calculated more accurately.
*/
pwm->clk_rate = clk_get_rate(pwm->clk); pwm->clk_rate = clk_get_rate(pwm->clk);
pwm->rst = devm_reset_control_get(&pdev->dev, "pwm"); pwm->rst = devm_reset_control_get(&pdev->dev, "pwm");
...@@ -273,10 +287,12 @@ static int tegra_pwm_resume(struct device *dev) ...@@ -273,10 +287,12 @@ static int tegra_pwm_resume(struct device *dev)
static const struct tegra_pwm_soc tegra20_pwm_soc = { static const struct tegra_pwm_soc tegra20_pwm_soc = {
.num_channels = 4, .num_channels = 4,
.max_frequency = 48000000UL,
}; };
static const struct tegra_pwm_soc tegra186_pwm_soc = { static const struct tegra_pwm_soc tegra186_pwm_soc = {
.num_channels = 1, .num_channels = 1,
.max_frequency = 102000000UL,
}; };
static const struct of_device_id tegra_pwm_of_match[] = { static const struct of_device_id tegra_pwm_of_match[] = {
......
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