forked from luck/tmp_suning_uos_patched
pwm: Changes for v5.7-rc1
There's quite a few changes this time around. Most of these are fixes and cleanups, but there's also new chip support for some drivers and a bit of rework. -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEEiOrDCAFJzPfAjcif3SOs138+s6EFAl6PNdoZHHRoaWVycnku cmVkaW5nQGdtYWlsLmNvbQAKCRDdI6zXfz6zofdhD/0SW+PCzkC1mLveOrbbZkDd F5LtzbdEgPvmxu47v17O6kCtHLmofSNzfMkUw8H6Z6aQdnO2wLkHG7CGjnRIroW3 q8X5nzciDvnKintwXLYjWCV/m9FECqUv07LSmvLzgJSMpoxuAb7JKROIc17GtOBv Gw2LSPZTLimeRgYyStOJr6yKiu31KOGwio2JhE3yknMwNOmINO8QpVaV4kJtrb7t lnPhlgwxG5QMyNojLF6iTb+kFu+3L1UskxXT0QUL5mc1NgdGl1xu8qFhFTad+qnZ SoCodT5T+Z0DQonPbsZ9DRm3NDCA8I5Rt6rh6MO9uRMK8tYm77vZqocOIl07AjEI Wa4SxI+7rzyeqfeVo/SnIJceq2WjVIXKb4mUI/7jVfmGmF1upMPNdLrOjSY4+xdG QyNXYsQt53prswUfReZGy6LvZB7Bjx9gT/zk/hljSI7CjiAsp3Zy+ukO5A6wvsCQ 0gKhhTwWVwL6GVvVDUNpnuNI+OxSFjTCHUyGtdgvo6lvakpLth9+IBs644xvGH6d OgHZIPIW5X+Zt003V+7r5lNaNYBSbujOsAIyf0LX+QWICxwLmAIVikyXE0cNXHBM g3jmJHYnnZds9g9XbHhpYhaNgOfF+SZB7kdHXzzAJzkM8stjoR0Gi1EiWbTAb9xw 0+s6eiq9MS2eUGX41PJw/Q== =Ugxz -----END PGP SIGNATURE----- Merge tag 'pwm/for-5.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm Pull pwm updates from Thierry Reding: "There's quite a few changes this time around. Most of these are fixes and cleanups, but there's also new chip support for some drivers and a bit of rework" * tag 'pwm/for-5.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm: (33 commits) pwm: pca9685: Fix PWM/GPIO inter-operation pwm: Make pwm_apply_state_debug() static pwm: meson: Remove redundant assignment to variable fin_freq pwm: jz4740: Allow selection of PWM channels 0 and 1 pwm: jz4740: Obtain regmap from parent node pwm: jz4740: Improve algorithm of clock calculation pwm: jz4740: Use clocks from TCU driver pwm: sun4i: Remove redundant needs_delay pwm: omap-dmtimer: Implement .apply callback pwm: omap-dmtimer: Do not disable PWM before changing period/duty_cycle pwm: omap-dmtimer: Fix PWM enabling sequence pwm: omap-dmtimer: Update description for PWM OMAP DM timer pwm: omap-dmtimer: Drop unused header file pwm: renesas-tpu: Drop confusing registered message pwm: renesas-tpu: Fix late Runtime PM enablement pwm: rcar: Fix late Runtime PM enablement dt-bindings: pwm: renesas-tpu: Document more R-Car Gen2 support pwm: meson: Fix confusing indentation pwm: pca9685: Use gpio core provided macro GPIO_LINE_DIRECTION_OUT pwm: pca9685: Replace CONFIG_PM with __maybe_unused ...
This commit is contained in:
commit
75bdc9293d
|
@ -1,23 +0,0 @@
|
|||
* PWM controlled by ChromeOS EC
|
||||
|
||||
Google's ChromeOS EC PWM is a simple PWM attached to the Embedded Controller
|
||||
(EC) and controlled via a host-command interface.
|
||||
|
||||
An EC PWM node should be only found as a sub-node of the EC node (see
|
||||
Documentation/devicetree/bindings/mfd/cros-ec.txt).
|
||||
|
||||
Required properties:
|
||||
- compatible: Must contain "google,cros-ec-pwm"
|
||||
- #pwm-cells: Should be 1. The cell specifies the PWM index.
|
||||
|
||||
Example:
|
||||
cros-ec@0 {
|
||||
compatible = "google,cros-ec-spi";
|
||||
|
||||
...
|
||||
|
||||
cros_ec_pwm: ec-pwm {
|
||||
compatible = "google,cros-ec-pwm";
|
||||
#pwm-cells = <1>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,40 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/pwm/google,cros-ec-pwm.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: PWM controlled by ChromeOS EC
|
||||
|
||||
maintainers:
|
||||
- Thierry Reding <thierry.reding@gmail.com>
|
||||
- '"Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>'
|
||||
|
||||
description: |
|
||||
Google's ChromeOS EC PWM is a simple PWM attached to the Embedded Controller
|
||||
(EC) and controlled via a host-command interface.
|
||||
An EC PWM node should be only found as a sub-node of the EC node (see
|
||||
Documentation/devicetree/bindings/mfd/cros-ec.txt).
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: google,cros-ec-pwm
|
||||
"#pwm-cells":
|
||||
description: The cell specifies the PWM index.
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- '#pwm-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
cros-ec@0 {
|
||||
compatible = "google,cros-ec-spi";
|
||||
cros_ec_pwm: ec-pwm {
|
||||
compatible = "google,cros-ec-pwm";
|
||||
#pwm-cells = <1>;
|
||||
};
|
||||
};
|
|
@ -9,6 +9,7 @@ Required properties:
|
|||
- "nvidia,tegra132-pwm", "nvidia,tegra20-pwm": for Tegra132
|
||||
- "nvidia,tegra210-pwm", "nvidia,tegra20-pwm": for Tegra210
|
||||
- "nvidia,tegra186-pwm": for Tegra186
|
||||
- "nvidia,tegra194-pwm": for Tegra194
|
||||
- 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
|
||||
the cells format.
|
||||
|
|
|
@ -33,6 +33,15 @@ config PWM_SYSFS
|
|||
bool
|
||||
default y if SYSFS
|
||||
|
||||
config PWM_DEBUG
|
||||
bool "PWM lowlevel drivers additional checks and debug messages"
|
||||
depends on DEBUG_KERNEL
|
||||
help
|
||||
This option enables some additional checks to help lowlevel driver
|
||||
authors to get their callbacks implemented correctly.
|
||||
It is expected to introduce some runtime overhead and diagnostic
|
||||
output to the kernel log, so only enable while working on a driver.
|
||||
|
||||
config PWM_AB8500
|
||||
tristate "AB8500 PWM support"
|
||||
depends on AB8500_CORE && ARCH_U8500
|
||||
|
@ -44,7 +53,8 @@ config PWM_AB8500
|
|||
|
||||
config PWM_ATMEL
|
||||
tristate "Atmel PWM support"
|
||||
depends on ARCH_AT91 && OF
|
||||
depends on OF
|
||||
depends on ARCH_AT91 || COMPILE_TEST
|
||||
help
|
||||
Generic PWM framework driver for Atmel SoC.
|
||||
|
||||
|
@ -100,7 +110,7 @@ config PWM_BCM_KONA
|
|||
|
||||
config PWM_BCM2835
|
||||
tristate "BCM2835 PWM support"
|
||||
depends on ARCH_BCM2835 || ARCH_BRCMSTB
|
||||
depends on ARCH_BCM2835 || ARCH_BRCMSTB || COMPILE_TEST
|
||||
help
|
||||
PWM framework driver for BCM2835 controller (Raspberry Pi)
|
||||
|
||||
|
@ -109,7 +119,7 @@ config PWM_BCM2835
|
|||
|
||||
config PWM_BERLIN
|
||||
tristate "Marvell Berlin PWM support"
|
||||
depends on ARCH_BERLIN
|
||||
depends on ARCH_BERLIN || COMPILE_TEST
|
||||
help
|
||||
PWM framework driver for Marvell Berlin SoCs.
|
||||
|
||||
|
@ -118,7 +128,7 @@ config PWM_BERLIN
|
|||
|
||||
config PWM_BRCMSTB
|
||||
tristate "Broadcom STB PWM support"
|
||||
depends on ARCH_BRCMSTB || BMIPS_GENERIC
|
||||
depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
|
||||
help
|
||||
Generic PWM framework driver for the Broadcom Set-top-Box
|
||||
SoCs (BCM7xxx).
|
||||
|
@ -152,7 +162,7 @@ config PWM_CROS_EC
|
|||
|
||||
config PWM_EP93XX
|
||||
tristate "Cirrus Logic EP93xx PWM support"
|
||||
depends on ARCH_EP93XX
|
||||
depends on ARCH_EP93XX || COMPILE_TEST
|
||||
help
|
||||
Generic PWM framework driver for Cirrus Logic EP93xx.
|
||||
|
||||
|
@ -195,7 +205,7 @@ config PWM_IMG
|
|||
|
||||
config PWM_IMX1
|
||||
tristate "i.MX1 PWM support"
|
||||
depends on ARCH_MXC
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
help
|
||||
Generic PWM framework driver for i.MX1 and i.MX21
|
||||
|
||||
|
@ -204,7 +214,7 @@ config PWM_IMX1
|
|||
|
||||
config PWM_IMX27
|
||||
tristate "i.MX27 PWM support"
|
||||
depends on ARCH_MXC
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
help
|
||||
Generic PWM framework driver for i.MX27 and later i.MX SoCs.
|
||||
|
||||
|
@ -225,6 +235,8 @@ config PWM_IMX_TPM
|
|||
config PWM_JZ4740
|
||||
tristate "Ingenic JZ47xx PWM support"
|
||||
depends on MACH_INGENIC
|
||||
depends on COMMON_CLK
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Generic PWM framework driver for Ingenic JZ47xx based
|
||||
machines.
|
||||
|
@ -244,7 +256,7 @@ config PWM_LP3943
|
|||
|
||||
config PWM_LPC18XX_SCT
|
||||
tristate "LPC18xx/43xx PWM/SCT support"
|
||||
depends on ARCH_LPC18XX
|
||||
depends on ARCH_LPC18XX || COMPILE_TEST
|
||||
help
|
||||
Generic PWM framework driver for NXP LPC18xx PWM/SCT which
|
||||
supports 16 channels.
|
||||
|
@ -256,7 +268,7 @@ config PWM_LPC18XX_SCT
|
|||
|
||||
config PWM_LPC32XX
|
||||
tristate "LPC32XX PWM support"
|
||||
depends on ARCH_LPC32XX
|
||||
depends on ARCH_LPC32XX || COMPILE_TEST
|
||||
help
|
||||
Generic PWM framework driver for LPC32XX. The LPC32XX SOC has two
|
||||
PWM controllers.
|
||||
|
@ -289,7 +301,8 @@ config PWM_LPSS_PLATFORM
|
|||
|
||||
config PWM_MESON
|
||||
tristate "Amlogic Meson PWM driver"
|
||||
depends on ARCH_MESON
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
depends on COMMON_CLK
|
||||
help
|
||||
The platform driver for Amlogic Meson PWM controller.
|
||||
|
||||
|
@ -318,7 +331,8 @@ config PWM_MEDIATEK
|
|||
|
||||
config PWM_MXS
|
||||
tristate "Freescale MXS PWM support"
|
||||
depends on ARCH_MXS && OF
|
||||
depends on OF
|
||||
depends on ARCH_MXS || COMPILE_TEST
|
||||
select STMP_DEVICE
|
||||
help
|
||||
Generic PWM framework driver for Freescale MXS.
|
||||
|
@ -357,7 +371,7 @@ config PWM_PUV3
|
|||
|
||||
config PWM_PXA
|
||||
tristate "PXA PWM support"
|
||||
depends on ARCH_PXA
|
||||
depends on ARCH_PXA || COMPILE_TEST
|
||||
help
|
||||
Generic PWM framework driver for PXA.
|
||||
|
||||
|
@ -388,14 +402,14 @@ config PWM_RENESAS_TPU
|
|||
|
||||
config PWM_ROCKCHIP
|
||||
tristate "Rockchip PWM support"
|
||||
depends on ARCH_ROCKCHIP
|
||||
depends on ARCH_ROCKCHIP || COMPILE_TEST
|
||||
help
|
||||
Generic PWM framework driver for the PWM controller found on
|
||||
Rockchip SoCs.
|
||||
|
||||
config PWM_SAMSUNG
|
||||
tristate "Samsung PWM support"
|
||||
depends on PLAT_SAMSUNG || ARCH_EXYNOS
|
||||
depends on PLAT_SAMSUNG || ARCH_EXYNOS || COMPILE_TEST
|
||||
help
|
||||
Generic PWM framework driver for Samsung.
|
||||
|
||||
|
@ -415,7 +429,7 @@ config PWM_SIFIVE
|
|||
|
||||
config PWM_SPEAR
|
||||
tristate "STMicroelectronics SPEAr PWM support"
|
||||
depends on PLAT_SPEAR
|
||||
depends on PLAT_SPEAR || COMPILE_TEST
|
||||
depends on OF
|
||||
help
|
||||
Generic PWM framework driver for the PWM controller on ST
|
||||
|
@ -437,7 +451,7 @@ config PWM_SPRD
|
|||
|
||||
config PWM_STI
|
||||
tristate "STiH4xx PWM support"
|
||||
depends on ARCH_STI
|
||||
depends on ARCH_STI || COMPILE_TEST
|
||||
depends on OF
|
||||
help
|
||||
Generic PWM framework driver for STiH4xx SoCs.
|
||||
|
@ -447,7 +461,7 @@ config PWM_STI
|
|||
|
||||
config PWM_STM32
|
||||
tristate "STMicroelectronics STM32 PWM"
|
||||
depends on MFD_STM32_TIMERS
|
||||
depends on MFD_STM32_TIMERS || COMPILE_TEST
|
||||
help
|
||||
Generic PWM framework driver for STM32 SoCs.
|
||||
|
||||
|
@ -483,7 +497,7 @@ config PWM_SUN4I
|
|||
|
||||
config PWM_TEGRA
|
||||
tristate "NVIDIA Tegra PWM support"
|
||||
depends on ARCH_TEGRA
|
||||
depends on ARCH_TEGRA || COMPILE_TEST
|
||||
help
|
||||
Generic PWM framework driver for the PWFM controller found on NVIDIA
|
||||
Tegra SoCs.
|
||||
|
@ -493,7 +507,7 @@ config PWM_TEGRA
|
|||
|
||||
config PWM_TIECAP
|
||||
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 || COMPILE_TEST
|
||||
help
|
||||
PWM driver support for the ECAP APWM controller found on TI SOCs
|
||||
|
||||
|
@ -502,7 +516,7 @@ config PWM_TIECAP
|
|||
|
||||
config PWM_TIEHRPWM
|
||||
tristate "EHRPWM PWM support"
|
||||
depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_K3
|
||||
depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_K3 || COMPILE_TEST
|
||||
help
|
||||
PWM driver support for the EHRPWM controller found on TI SOCs
|
||||
|
||||
|
@ -529,7 +543,7 @@ config PWM_TWL_LED
|
|||
|
||||
config PWM_VT8500
|
||||
tristate "vt8500 PWM support"
|
||||
depends on ARCH_VT8500
|
||||
depends on ARCH_VT8500 || COMPILE_TEST
|
||||
help
|
||||
Generic PWM framework driver for vt8500.
|
||||
|
||||
|
@ -538,7 +552,7 @@ config PWM_VT8500
|
|||
|
||||
config PWM_ZX
|
||||
tristate "ZTE ZX PWM support"
|
||||
depends on ARCH_ZX
|
||||
depends on ARCH_ZX || COMPILE_TEST
|
||||
help
|
||||
Generic PWM framework driver for ZTE ZX family SoCs.
|
||||
|
||||
|
|
|
@ -120,6 +120,9 @@ 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);
|
||||
|
||||
if (IS_ENABLED(PWM_DEBUG))
|
||||
pwm->last = pwm->state;
|
||||
}
|
||||
|
||||
set_bit(PWMF_REQUESTED, &pwm->flags);
|
||||
|
@ -232,17 +235,28 @@ void *pwm_get_chip_data(struct pwm_device *pwm)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(pwm_get_chip_data);
|
||||
|
||||
static bool pwm_ops_check(const struct pwm_ops *ops)
|
||||
static bool pwm_ops_check(const struct pwm_chip *chip)
|
||||
{
|
||||
|
||||
const struct pwm_ops *ops = chip->ops;
|
||||
|
||||
/* driver supports legacy, non-atomic operation */
|
||||
if (ops->config && ops->enable && ops->disable)
|
||||
return true;
|
||||
if (ops->config && ops->enable && ops->disable) {
|
||||
if (IS_ENABLED(CONFIG_PWM_DEBUG))
|
||||
dev_warn(chip->dev,
|
||||
"Driver needs updating to atomic API\n");
|
||||
|
||||
/* driver supports atomic operation */
|
||||
if (ops->apply)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
if (!ops->apply)
|
||||
return false;
|
||||
|
||||
if (IS_ENABLED(CONFIG_PWM_DEBUG) && !ops->get_state)
|
||||
dev_warn(chip->dev,
|
||||
"Please implement the .get_state() callback\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -266,7 +280,7 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
|
|||
if (!chip || !chip->dev || !chip->ops || !chip->npwm)
|
||||
return -EINVAL;
|
||||
|
||||
if (!pwm_ops_check(chip->ops))
|
||||
if (!pwm_ops_check(chip))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&pwm_lock);
|
||||
|
@ -450,6 +464,107 @@ void pwm_free(struct pwm_device *pwm)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(pwm_free);
|
||||
|
||||
static void pwm_apply_state_debug(struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
struct pwm_state *last = &pwm->last;
|
||||
struct pwm_chip *chip = pwm->chip;
|
||||
struct pwm_state s1, s2;
|
||||
int err;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_PWM_DEBUG))
|
||||
return;
|
||||
|
||||
/* No reasonable diagnosis possible without .get_state() */
|
||||
if (!chip->ops->get_state)
|
||||
return;
|
||||
|
||||
/*
|
||||
* *state was just applied. Read out the hardware state and do some
|
||||
* checks.
|
||||
*/
|
||||
|
||||
chip->ops->get_state(chip, pwm, &s1);
|
||||
trace_pwm_get(pwm, &s1);
|
||||
|
||||
/*
|
||||
* The lowlevel driver either ignored .polarity (which is a bug) or as
|
||||
* best effort inverted .polarity and fixed .duty_cycle respectively.
|
||||
* Undo this inversion and fixup for further tests.
|
||||
*/
|
||||
if (s1.enabled && s1.polarity != state->polarity) {
|
||||
s2.polarity = state->polarity;
|
||||
s2.duty_cycle = s1.period - s1.duty_cycle;
|
||||
s2.period = s1.period;
|
||||
s2.enabled = s1.enabled;
|
||||
} else {
|
||||
s2 = s1;
|
||||
}
|
||||
|
||||
if (s2.polarity != state->polarity &&
|
||||
state->duty_cycle < state->period)
|
||||
dev_warn(chip->dev, ".apply ignored .polarity\n");
|
||||
|
||||
if (state->enabled &&
|
||||
last->polarity == state->polarity &&
|
||||
last->period > s2.period &&
|
||||
last->period <= state->period)
|
||||
dev_warn(chip->dev,
|
||||
".apply didn't pick the best available period (requested: %u, applied: %u, possible: %u)\n",
|
||||
state->period, s2.period, last->period);
|
||||
|
||||
if (state->enabled && state->period < s2.period)
|
||||
dev_warn(chip->dev,
|
||||
".apply is supposed to round down period (requested: %u, applied: %u)\n",
|
||||
state->period, s2.period);
|
||||
|
||||
if (state->enabled &&
|
||||
last->polarity == state->polarity &&
|
||||
last->period == s2.period &&
|
||||
last->duty_cycle > s2.duty_cycle &&
|
||||
last->duty_cycle <= state->duty_cycle)
|
||||
dev_warn(chip->dev,
|
||||
".apply didn't pick the best available duty cycle (requested: %u/%u, applied: %u/%u, possible: %u/%u)\n",
|
||||
state->duty_cycle, state->period,
|
||||
s2.duty_cycle, s2.period,
|
||||
last->duty_cycle, last->period);
|
||||
|
||||
if (state->enabled && state->duty_cycle < s2.duty_cycle)
|
||||
dev_warn(chip->dev,
|
||||
".apply is supposed to round down duty_cycle (requested: %u/%u, applied: %u/%u)\n",
|
||||
state->duty_cycle, state->period,
|
||||
s2.duty_cycle, s2.period);
|
||||
|
||||
if (!state->enabled && s2.enabled && s2.duty_cycle > 0)
|
||||
dev_warn(chip->dev,
|
||||
"requested disabled, but yielded enabled with duty > 0");
|
||||
|
||||
/* reapply the state that the driver reported being configured. */
|
||||
err = chip->ops->apply(chip, pwm, &s1);
|
||||
if (err) {
|
||||
*last = s1;
|
||||
dev_err(chip->dev, "failed to reapply current setting\n");
|
||||
return;
|
||||
}
|
||||
|
||||
trace_pwm_apply(pwm, &s1);
|
||||
|
||||
chip->ops->get_state(chip, pwm, last);
|
||||
trace_pwm_get(pwm, last);
|
||||
|
||||
/* reapplication of the current state should give an exact match */
|
||||
if (s1.enabled != last->enabled ||
|
||||
s1.polarity != last->polarity ||
|
||||
(s1.enabled && s1.period != last->period) ||
|
||||
(s1.enabled && s1.duty_cycle != last->duty_cycle)) {
|
||||
dev_err(chip->dev,
|
||||
".apply is not idempotent (ena=%d pol=%d %u/%u) -> (ena=%d pol=%d %u/%u)\n",
|
||||
s1.enabled, s1.polarity, s1.duty_cycle, s1.period,
|
||||
last->enabled, last->polarity, last->duty_cycle,
|
||||
last->period);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pwm_apply_state() - atomically apply a new state to a PWM device
|
||||
* @pwm: PWM device
|
||||
|
@ -480,6 +595,12 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state)
|
|||
trace_pwm_apply(pwm, state);
|
||||
|
||||
pwm->state = *state;
|
||||
|
||||
/*
|
||||
* only do this after pwm->state was applied as some
|
||||
* implementations of .get_state depend on this
|
||||
*/
|
||||
pwm_apply_state_debug(pwm, state);
|
||||
} else {
|
||||
/*
|
||||
* FIXME: restore the initial state in case of error.
|
||||
|
|
|
@ -166,6 +166,7 @@ static int bcm2835_pwm_probe(struct platform_device *pdev)
|
|||
|
||||
pc->chip.dev = &pdev->dev;
|
||||
pc->chip.ops = &bcm2835_pwm_ops;
|
||||
pc->chip.base = -1;
|
||||
pc->chip.npwm = 2;
|
||||
pc->chip.of_xlate = of_pwm_xlate_with_flags;
|
||||
pc->chip.of_pwm_n_cells = 3;
|
||||
|
|
|
@ -18,10 +18,8 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -96,9 +95,8 @@ struct pwm_imx27_chip {
|
|||
|
||||
#define to_pwm_imx27_chip(chip) container_of(chip, struct pwm_imx27_chip, chip)
|
||||
|
||||
static int pwm_imx27_clk_prepare_enable(struct pwm_chip *chip)
|
||||
static int pwm_imx27_clk_prepare_enable(struct pwm_imx27_chip *imx)
|
||||
{
|
||||
struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(imx->clk_ipg);
|
||||
|
@ -114,10 +112,8 @@ static int pwm_imx27_clk_prepare_enable(struct pwm_chip *chip)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void pwm_imx27_clk_disable_unprepare(struct pwm_chip *chip)
|
||||
static void pwm_imx27_clk_disable_unprepare(struct pwm_imx27_chip *imx)
|
||||
{
|
||||
struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
|
||||
|
||||
clk_disable_unprepare(imx->clk_per);
|
||||
clk_disable_unprepare(imx->clk_ipg);
|
||||
}
|
||||
|
@ -130,7 +126,7 @@ static void pwm_imx27_get_state(struct pwm_chip *chip,
|
|||
u64 tmp;
|
||||
int ret;
|
||||
|
||||
ret = pwm_imx27_clk_prepare_enable(chip);
|
||||
ret = pwm_imx27_clk_prepare_enable(imx);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
|
@ -174,8 +170,7 @@ static void pwm_imx27_get_state(struct pwm_chip *chip,
|
|||
tmp = NSEC_PER_SEC * (u64)(val);
|
||||
state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, pwm_clk);
|
||||
|
||||
if (!state->enabled)
|
||||
pwm_imx27_clk_disable_unprepare(chip);
|
||||
pwm_imx27_clk_disable_unprepare(imx);
|
||||
}
|
||||
|
||||
static void pwm_imx27_sw_reset(struct pwm_chip *chip)
|
||||
|
@ -259,7 +254,7 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
if (cstate.enabled) {
|
||||
pwm_imx27_wait_fifo_slot(chip, pwm);
|
||||
} else {
|
||||
ret = pwm_imx27_clk_prepare_enable(chip);
|
||||
ret = pwm_imx27_clk_prepare_enable(imx);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -289,8 +284,8 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
|
||||
writel(cr, imx->mmio_base + MX3_PWMCR);
|
||||
|
||||
if (!state->enabled && cstate.enabled)
|
||||
pwm_imx27_clk_disable_unprepare(chip);
|
||||
if (!state->enabled)
|
||||
pwm_imx27_clk_disable_unprepare(imx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -310,6 +305,8 @@ MODULE_DEVICE_TABLE(of, pwm_imx27_dt_ids);
|
|||
static int pwm_imx27_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct pwm_imx27_chip *imx;
|
||||
int ret;
|
||||
u32 pwmcr;
|
||||
|
||||
imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL);
|
||||
if (imx == NULL)
|
||||
|
@ -352,6 +349,15 @@ static int pwm_imx27_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(imx->mmio_base))
|
||||
return PTR_ERR(imx->mmio_base);
|
||||
|
||||
ret = pwm_imx27_clk_prepare_enable(imx);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* keep clks on if pwm is running */
|
||||
pwmcr = readl(imx->mmio_base + MX3_PWMCR);
|
||||
if (!(pwmcr & MX3_PWMCR_EN))
|
||||
pwm_imx27_clk_disable_unprepare(imx);
|
||||
|
||||
return pwmchip_add(&imx->chip);
|
||||
}
|
||||
|
||||
|
@ -361,8 +367,6 @@ static int pwm_imx27_remove(struct platform_device *pdev)
|
|||
|
||||
imx = platform_get_drvdata(pdev);
|
||||
|
||||
pwm_imx27_clk_disable_unprepare(&imx->chip);
|
||||
|
||||
return pwmchip_remove(&imx->chip);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,18 +13,19 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/ingenic-tcu.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pwm.h>
|
||||
|
||||
#include <asm/mach-jz4740/timer.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define NUM_PWM 8
|
||||
|
||||
struct jz4740_pwm_chip {
|
||||
struct pwm_chip chip;
|
||||
struct clk *clk;
|
||||
struct regmap *map;
|
||||
};
|
||||
|
||||
static inline struct jz4740_pwm_chip *to_jz4740(struct pwm_chip *chip)
|
||||
|
@ -32,82 +33,134 @@ static inline struct jz4740_pwm_chip *to_jz4740(struct pwm_chip *chip)
|
|||
return container_of(chip, struct jz4740_pwm_chip, chip);
|
||||
}
|
||||
|
||||
static bool jz4740_pwm_can_use_chn(struct jz4740_pwm_chip *jz,
|
||||
unsigned int channel)
|
||||
{
|
||||
/* Enable all TCU channels for PWM use by default except channels 0/1 */
|
||||
u32 pwm_channels_mask = GENMASK(NUM_PWM - 1, 2);
|
||||
|
||||
device_property_read_u32(jz->chip.dev->parent,
|
||||
"ingenic,pwm-channels-mask",
|
||||
&pwm_channels_mask);
|
||||
|
||||
return !!(pwm_channels_mask & BIT(channel));
|
||||
}
|
||||
|
||||
static int jz4740_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
/*
|
||||
* Timers 0 and 1 are used for system tasks, so they are unavailable
|
||||
* for use as PWMs.
|
||||
*/
|
||||
if (pwm->hwpwm < 2)
|
||||
struct jz4740_pwm_chip *jz = to_jz4740(chip);
|
||||
struct clk *clk;
|
||||
char name[16];
|
||||
int err;
|
||||
|
||||
if (!jz4740_pwm_can_use_chn(jz, pwm->hwpwm))
|
||||
return -EBUSY;
|
||||
|
||||
jz4740_timer_start(pwm->hwpwm);
|
||||
snprintf(name, sizeof(name), "timer%u", pwm->hwpwm);
|
||||
|
||||
clk = clk_get(chip->dev, name);
|
||||
if (IS_ERR(clk)) {
|
||||
if (PTR_ERR(clk) != -EPROBE_DEFER)
|
||||
dev_err(chip->dev, "Failed to get clock: %pe", clk);
|
||||
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
err = clk_prepare_enable(clk);
|
||||
if (err < 0) {
|
||||
clk_put(clk);
|
||||
return err;
|
||||
}
|
||||
|
||||
pwm_set_chip_data(pwm, clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void jz4740_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
jz4740_timer_set_ctrl(pwm->hwpwm, 0);
|
||||
struct clk *clk = pwm_get_chip_data(pwm);
|
||||
|
||||
jz4740_timer_stop(pwm->hwpwm);
|
||||
clk_disable_unprepare(clk);
|
||||
clk_put(clk);
|
||||
}
|
||||
|
||||
static int jz4740_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
uint32_t ctrl = jz4740_timer_get_ctrl(pwm->pwm);
|
||||
struct jz4740_pwm_chip *jz = to_jz4740(chip);
|
||||
|
||||
ctrl |= JZ_TIMER_CTRL_PWM_ENABLE;
|
||||
jz4740_timer_set_ctrl(pwm->hwpwm, ctrl);
|
||||
jz4740_timer_enable(pwm->hwpwm);
|
||||
/* Enable PWM output */
|
||||
regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
|
||||
TCU_TCSR_PWM_EN, TCU_TCSR_PWM_EN);
|
||||
|
||||
/* Start counter */
|
||||
regmap_write(jz->map, TCU_REG_TESR, BIT(pwm->hwpwm));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void jz4740_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
uint32_t ctrl = jz4740_timer_get_ctrl(pwm->hwpwm);
|
||||
struct jz4740_pwm_chip *jz = to_jz4740(chip);
|
||||
|
||||
/*
|
||||
* Set duty > period. This trick allows the TCU channels in TCU2 mode to
|
||||
* properly return to their init level.
|
||||
*/
|
||||
jz4740_timer_set_duty(pwm->hwpwm, 0xffff);
|
||||
jz4740_timer_set_period(pwm->hwpwm, 0x0);
|
||||
regmap_write(jz->map, TCU_REG_TDHRc(pwm->hwpwm), 0xffff);
|
||||
regmap_write(jz->map, TCU_REG_TDFRc(pwm->hwpwm), 0x0);
|
||||
|
||||
/*
|
||||
* Disable PWM output.
|
||||
* In TCU2 mode (channel 1/2 on JZ4750+), this must be done before the
|
||||
* counter is stopped, while in TCU1 mode the order does not matter.
|
||||
*/
|
||||
ctrl &= ~JZ_TIMER_CTRL_PWM_ENABLE;
|
||||
jz4740_timer_set_ctrl(pwm->hwpwm, ctrl);
|
||||
regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
|
||||
TCU_TCSR_PWM_EN, 0);
|
||||
|
||||
/* Stop counter */
|
||||
jz4740_timer_disable(pwm->hwpwm);
|
||||
regmap_write(jz->map, TCU_REG_TECR, BIT(pwm->hwpwm));
|
||||
}
|
||||
|
||||
static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
struct jz4740_pwm_chip *jz4740 = to_jz4740(pwm->chip);
|
||||
unsigned long long tmp;
|
||||
unsigned long long tmp = 0xffffull * NSEC_PER_SEC;
|
||||
struct clk *clk = pwm_get_chip_data(pwm);
|
||||
unsigned long period, duty;
|
||||
unsigned int prescaler = 0;
|
||||
uint16_t ctrl;
|
||||
long rate;
|
||||
int err;
|
||||
|
||||
tmp = (unsigned long long)clk_get_rate(jz4740->clk) * state->period;
|
||||
do_div(tmp, 1000000000);
|
||||
period = tmp;
|
||||
/*
|
||||
* Limit the clock to a maximum rate that still gives us a period value
|
||||
* which fits in 16 bits.
|
||||
*/
|
||||
do_div(tmp, state->period);
|
||||
|
||||
while (period > 0xffff && prescaler < 6) {
|
||||
period >>= 2;
|
||||
++prescaler;
|
||||
/*
|
||||
* /!\ IMPORTANT NOTE:
|
||||
* -------------------
|
||||
* This code relies on the fact that clk_round_rate() will always round
|
||||
* down, which is not a valid assumption given by the clk API, but only
|
||||
* happens to be true with the clk drivers used for Ingenic SoCs.
|
||||
*
|
||||
* Right now, there is no alternative as the clk API does not have a
|
||||
* round-down function (and won't have one for a while), but if it ever
|
||||
* comes to light, a round-down function should be used instead.
|
||||
*/
|
||||
rate = clk_round_rate(clk, tmp);
|
||||
if (rate < 0) {
|
||||
dev_err(chip->dev, "Unable to round rate: %ld", rate);
|
||||
return rate;
|
||||
}
|
||||
|
||||
if (prescaler == 6)
|
||||
return -EINVAL;
|
||||
/* Calculate period value */
|
||||
tmp = (unsigned long long)rate * state->period;
|
||||
do_div(tmp, NSEC_PER_SEC);
|
||||
period = (unsigned long)tmp;
|
||||
|
||||
/* Calculate duty value */
|
||||
tmp = (unsigned long long)period * state->duty_cycle;
|
||||
do_div(tmp, state->period);
|
||||
duty = period - tmp;
|
||||
|
@ -117,25 +170,37 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
|
||||
jz4740_pwm_disable(chip, pwm);
|
||||
|
||||
jz4740_timer_set_count(pwm->hwpwm, 0);
|
||||
jz4740_timer_set_duty(pwm->hwpwm, duty);
|
||||
jz4740_timer_set_period(pwm->hwpwm, period);
|
||||
|
||||
ctrl = JZ_TIMER_CTRL_PRESCALER(prescaler) | JZ_TIMER_CTRL_SRC_EXT |
|
||||
JZ_TIMER_CTRL_PWM_ABBRUPT_SHUTDOWN;
|
||||
|
||||
jz4740_timer_set_ctrl(pwm->hwpwm, ctrl);
|
||||
|
||||
switch (state->polarity) {
|
||||
case PWM_POLARITY_NORMAL:
|
||||
ctrl &= ~JZ_TIMER_CTRL_PWM_ACTIVE_LOW;
|
||||
break;
|
||||
case PWM_POLARITY_INVERSED:
|
||||
ctrl |= JZ_TIMER_CTRL_PWM_ACTIVE_LOW;
|
||||
break;
|
||||
err = clk_set_rate(clk, rate);
|
||||
if (err) {
|
||||
dev_err(chip->dev, "Unable to set rate: %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
jz4740_timer_set_ctrl(pwm->hwpwm, ctrl);
|
||||
/* Reset counter to 0 */
|
||||
regmap_write(jz4740->map, TCU_REG_TCNTc(pwm->hwpwm), 0);
|
||||
|
||||
/* Set duty */
|
||||
regmap_write(jz4740->map, TCU_REG_TDHRc(pwm->hwpwm), duty);
|
||||
|
||||
/* Set period */
|
||||
regmap_write(jz4740->map, TCU_REG_TDFRc(pwm->hwpwm), period);
|
||||
|
||||
/* Set abrupt shutdown */
|
||||
regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
|
||||
TCU_TCSR_PWM_SD, TCU_TCSR_PWM_SD);
|
||||
|
||||
/* Set polarity */
|
||||
switch (state->polarity) {
|
||||
case PWM_POLARITY_NORMAL:
|
||||
regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
|
||||
TCU_TCSR_PWM_INITL_HIGH, 0);
|
||||
break;
|
||||
case PWM_POLARITY_INVERSED:
|
||||
regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
|
||||
TCU_TCSR_PWM_INITL_HIGH,
|
||||
TCU_TCSR_PWM_INITL_HIGH);
|
||||
break;
|
||||
}
|
||||
|
||||
if (state->enabled)
|
||||
jz4740_pwm_enable(chip, pwm);
|
||||
|
@ -152,17 +217,20 @@ static const struct pwm_ops jz4740_pwm_ops = {
|
|||
|
||||
static int jz4740_pwm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct jz4740_pwm_chip *jz4740;
|
||||
|
||||
jz4740 = devm_kzalloc(&pdev->dev, sizeof(*jz4740), GFP_KERNEL);
|
||||
jz4740 = devm_kzalloc(dev, sizeof(*jz4740), GFP_KERNEL);
|
||||
if (!jz4740)
|
||||
return -ENOMEM;
|
||||
|
||||
jz4740->clk = devm_clk_get(&pdev->dev, "ext");
|
||||
if (IS_ERR(jz4740->clk))
|
||||
return PTR_ERR(jz4740->clk);
|
||||
jz4740->map = device_node_to_regmap(dev->parent->of_node);
|
||||
if (IS_ERR(jz4740->map)) {
|
||||
dev_err(dev, "regmap not found: %ld\n", PTR_ERR(jz4740->map));
|
||||
return PTR_ERR(jz4740->map);
|
||||
}
|
||||
|
||||
jz4740->chip.dev = &pdev->dev;
|
||||
jz4740->chip.dev = dev;
|
||||
jz4740->chip.ops = &jz4740_pwm_ops;
|
||||
jz4740->chip.npwm = NUM_PWM;
|
||||
jz4740->chip.base = -1;
|
||||
|
|
|
@ -136,7 +136,7 @@ static int meson_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
|
|||
dev_err(dev, "failed to set parent %s for %s: %d\n",
|
||||
__clk_get_name(channel->clk_parent),
|
||||
__clk_get_name(channel->clk), err);
|
||||
return err;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,7 +163,7 @@ static int meson_pwm_calc(struct meson_pwm *meson, struct pwm_device *pwm,
|
|||
{
|
||||
struct meson_pwm_channel *channel = pwm_get_chip_data(pwm);
|
||||
unsigned int duty, period, pre_div, cnt, duty_cnt;
|
||||
unsigned long fin_freq = -1;
|
||||
unsigned long fin_freq;
|
||||
|
||||
duty = state->duty_cycle;
|
||||
period = state->period;
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
|
@ -10,7 +10,27 @@
|
|||
*
|
||||
* Description:
|
||||
* This file is the core OMAP support for the generic, Linux
|
||||
* PWM driver / controller, using the OMAP's dual-mode timers.
|
||||
* PWM driver / controller, using the OMAP's dual-mode timers
|
||||
* with a timer counter that goes up. When it overflows it gets
|
||||
* reloaded with the load value and the pwm output goes up.
|
||||
* When counter matches with match register, the output goes down.
|
||||
* Reference Manual: http://www.ti.com/lit/ug/spruh73q/spruh73q.pdf
|
||||
*
|
||||
* Limitations:
|
||||
* - When PWM is stopped, timer counter gets stopped immediately. This
|
||||
* doesn't allow the current PWM period to complete and stops abruptly.
|
||||
* - When PWM is running and changing both duty cycle and period,
|
||||
* we cannot prevent in software that the output might produce
|
||||
* a period with mixed settings. Especially when period/duty_cyle
|
||||
* is updated while the pwm pin is high, current pwm period/duty_cycle
|
||||
* can get updated as below based on the current timer counter:
|
||||
* - period for current cycle = current_period + new period
|
||||
* - duty_cycle for current period = current period + new duty_cycle.
|
||||
* - PWM OMAP DM timer cannot change the polarity when pwm is active. When
|
||||
* user requests a change in polarity when in active state:
|
||||
* - PWM is stopped abruptly(without completing the current cycle)
|
||||
* - Polarity is changed
|
||||
* - A fresh cycle is started.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
|
@ -20,8 +40,8 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <clocksource/timer-ti-dm.h>
|
||||
#include <linux/platform_data/dmtimer-omap.h>
|
||||
#include <linux/platform_data/pwm_omap_dmtimer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pwm.h>
|
||||
|
@ -31,10 +51,20 @@
|
|||
#define DM_TIMER_LOAD_MIN 0xfffffffe
|
||||
#define DM_TIMER_MAX 0xffffffff
|
||||
|
||||
/**
|
||||
* struct pwm_omap_dmtimer_chip - Structure representing a pwm chip
|
||||
* corresponding to omap dmtimer.
|
||||
* @chip: PWM chip structure representing PWM controller
|
||||
* @mutex: Mutex to protect pwm apply state
|
||||
* @dm_timer: Pointer to omap dm timer.
|
||||
* @pdata: Pointer to omap dm timer ops.
|
||||
* dm_timer_pdev: Pointer to omap dm timer platform device
|
||||
*/
|
||||
struct pwm_omap_dmtimer_chip {
|
||||
struct pwm_chip chip;
|
||||
/* Mutex to protect pwm apply state */
|
||||
struct mutex mutex;
|
||||
pwm_omap_dmtimer *dm_timer;
|
||||
struct omap_dm_timer *dm_timer;
|
||||
const struct omap_dm_timer_ops *pdata;
|
||||
struct platform_device *dm_timer_pdev;
|
||||
};
|
||||
|
@ -45,11 +75,22 @@ to_pwm_omap_dmtimer_chip(struct pwm_chip *chip)
|
|||
return container_of(chip, struct pwm_omap_dmtimer_chip, chip);
|
||||
}
|
||||
|
||||
/**
|
||||
* pwm_omap_dmtimer_get_clock_cycles() - Get clock cycles in a time frame
|
||||
* @clk_rate: pwm timer clock rate
|
||||
* @ns: time frame in nano seconds.
|
||||
*
|
||||
* Return number of clock cycles in a given period(ins ns).
|
||||
*/
|
||||
static u32 pwm_omap_dmtimer_get_clock_cycles(unsigned long clk_rate, int ns)
|
||||
{
|
||||
return DIV_ROUND_CLOSEST_ULL((u64)clk_rate * ns, NSEC_PER_SEC);
|
||||
}
|
||||
|
||||
/**
|
||||
* pwm_omap_dmtimer_start() - Start the pwm omap dm timer in pwm mode
|
||||
* @omap: Pointer to pwm omap dm timer chip
|
||||
*/
|
||||
static void pwm_omap_dmtimer_start(struct pwm_omap_dmtimer_chip *omap)
|
||||
{
|
||||
/*
|
||||
|
@ -67,28 +108,46 @@ static void pwm_omap_dmtimer_start(struct pwm_omap_dmtimer_chip *omap)
|
|||
omap->pdata->start(omap->dm_timer);
|
||||
}
|
||||
|
||||
static int pwm_omap_dmtimer_enable(struct pwm_chip *chip,
|
||||
struct pwm_device *pwm)
|
||||
/**
|
||||
* pwm_omap_dmtimer_is_enabled() - Detect if the pwm is enabled.
|
||||
* @omap: Pointer to pwm omap dm timer chip
|
||||
*
|
||||
* Return true if pwm is enabled else false.
|
||||
*/
|
||||
static bool pwm_omap_dmtimer_is_enabled(struct pwm_omap_dmtimer_chip *omap)
|
||||
{
|
||||
struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip);
|
||||
u32 status;
|
||||
|
||||
mutex_lock(&omap->mutex);
|
||||
pwm_omap_dmtimer_start(omap);
|
||||
mutex_unlock(&omap->mutex);
|
||||
status = omap->pdata->get_pwm_status(omap->dm_timer);
|
||||
|
||||
return 0;
|
||||
return !!(status & OMAP_TIMER_CTRL_ST);
|
||||
}
|
||||
|
||||
static void pwm_omap_dmtimer_disable(struct pwm_chip *chip,
|
||||
struct pwm_device *pwm)
|
||||
/**
|
||||
* pwm_omap_dmtimer_polarity() - Detect the polarity of pwm.
|
||||
* @omap: Pointer to pwm omap dm timer chip
|
||||
*
|
||||
* Return the polarity of pwm.
|
||||
*/
|
||||
static int pwm_omap_dmtimer_polarity(struct pwm_omap_dmtimer_chip *omap)
|
||||
{
|
||||
struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip);
|
||||
u32 status;
|
||||
|
||||
mutex_lock(&omap->mutex);
|
||||
omap->pdata->stop(omap->dm_timer);
|
||||
mutex_unlock(&omap->mutex);
|
||||
status = omap->pdata->get_pwm_status(omap->dm_timer);
|
||||
|
||||
return !!(status & OMAP_TIMER_CTRL_SCPWM);
|
||||
}
|
||||
|
||||
/**
|
||||
* pwm_omap_dmtimer_config() - Update the configuration of pwm omap dm timer
|
||||
* @chip: Pointer to PWM controller
|
||||
* @pwm: Pointer to PWM channel
|
||||
* @duty_ns: New duty cycle in nano seconds
|
||||
* @period_ns: New period in nano seconds
|
||||
*
|
||||
* Return 0 if successfully changed the period/duty_cycle else appropriate
|
||||
* error.
|
||||
*/
|
||||
static int pwm_omap_dmtimer_config(struct pwm_chip *chip,
|
||||
struct pwm_device *pwm,
|
||||
int duty_ns, int period_ns)
|
||||
|
@ -96,31 +155,26 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip,
|
|||
struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip);
|
||||
u32 period_cycles, duty_cycles;
|
||||
u32 load_value, match_value;
|
||||
struct clk *fclk;
|
||||
unsigned long clk_rate;
|
||||
bool timer_active;
|
||||
struct clk *fclk;
|
||||
|
||||
dev_dbg(chip->dev, "requested duty cycle: %d ns, period: %d ns\n",
|
||||
duty_ns, period_ns);
|
||||
|
||||
mutex_lock(&omap->mutex);
|
||||
if (duty_ns == pwm_get_duty_cycle(pwm) &&
|
||||
period_ns == pwm_get_period(pwm)) {
|
||||
/* No change - don't cause any transients. */
|
||||
mutex_unlock(&omap->mutex);
|
||||
period_ns == pwm_get_period(pwm))
|
||||
return 0;
|
||||
}
|
||||
|
||||
fclk = omap->pdata->get_fclk(omap->dm_timer);
|
||||
if (!fclk) {
|
||||
dev_err(chip->dev, "invalid pmtimer fclk\n");
|
||||
goto err_einval;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
clk_rate = clk_get_rate(fclk);
|
||||
if (!clk_rate) {
|
||||
dev_err(chip->dev, "invalid pmtimer fclk rate\n");
|
||||
goto err_einval;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(chip->dev, "clk rate: %luHz\n", clk_rate);
|
||||
|
@ -148,7 +202,7 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip,
|
|||
dev_info(chip->dev,
|
||||
"period %d ns too short for clock rate %lu Hz\n",
|
||||
period_ns, clk_rate);
|
||||
goto err_einval;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (duty_cycles < 1) {
|
||||
|
@ -174,81 +228,103 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip,
|
|||
load_value = (DM_TIMER_MAX - period_cycles) + 1;
|
||||
match_value = load_value + duty_cycles - 1;
|
||||
|
||||
/*
|
||||
* We MUST stop the associated dual-mode timer before attempting to
|
||||
* write its registers, but calls to omap_dm_timer_start/stop must
|
||||
* be balanced so check if timer is active before calling timer_stop.
|
||||
*/
|
||||
timer_active = pm_runtime_active(&omap->dm_timer_pdev->dev);
|
||||
if (timer_active)
|
||||
omap->pdata->stop(omap->dm_timer);
|
||||
|
||||
omap->pdata->set_load(omap->dm_timer, load_value);
|
||||
omap->pdata->set_match(omap->dm_timer, true, match_value);
|
||||
|
||||
dev_dbg(chip->dev, "load value: %#08x (%d), match value: %#08x (%d)\n",
|
||||
load_value, load_value, match_value, match_value);
|
||||
|
||||
omap->pdata->set_pwm(omap->dm_timer,
|
||||
pwm_get_polarity(pwm) == PWM_POLARITY_INVERSED,
|
||||
true,
|
||||
PWM_OMAP_DMTIMER_TRIGGER_OVERFLOW_AND_COMPARE,
|
||||
true);
|
||||
|
||||
/* If config was called while timer was running it must be reenabled. */
|
||||
if (timer_active)
|
||||
pwm_omap_dmtimer_start(omap);
|
||||
|
||||
mutex_unlock(&omap->mutex);
|
||||
|
||||
return 0;
|
||||
|
||||
err_einval:
|
||||
mutex_unlock(&omap->mutex);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int pwm_omap_dmtimer_set_polarity(struct pwm_chip *chip,
|
||||
struct pwm_device *pwm,
|
||||
enum pwm_polarity polarity)
|
||||
/**
|
||||
* pwm_omap_dmtimer_set_polarity() - Changes the polarity of the pwm dm timer.
|
||||
* @chip: Pointer to PWM controller
|
||||
* @pwm: Pointer to PWM channel
|
||||
* @polarity: New pwm polarity to be set
|
||||
*/
|
||||
static void pwm_omap_dmtimer_set_polarity(struct pwm_chip *chip,
|
||||
struct pwm_device *pwm,
|
||||
enum pwm_polarity polarity)
|
||||
{
|
||||
struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip);
|
||||
bool enabled;
|
||||
|
||||
/* Disable the PWM before changing the polarity. */
|
||||
enabled = pwm_omap_dmtimer_is_enabled(omap);
|
||||
if (enabled)
|
||||
omap->pdata->stop(omap->dm_timer);
|
||||
|
||||
/*
|
||||
* PWM core will not call set_polarity while PWM is enabled so it's
|
||||
* safe to reconfigure the timer here without stopping it first.
|
||||
*/
|
||||
mutex_lock(&omap->mutex);
|
||||
omap->pdata->set_pwm(omap->dm_timer,
|
||||
polarity == PWM_POLARITY_INVERSED,
|
||||
true,
|
||||
PWM_OMAP_DMTIMER_TRIGGER_OVERFLOW_AND_COMPARE,
|
||||
true);
|
||||
polarity == PWM_POLARITY_INVERSED,
|
||||
true, OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE,
|
||||
true);
|
||||
|
||||
if (enabled)
|
||||
pwm_omap_dmtimer_start(omap);
|
||||
}
|
||||
|
||||
/**
|
||||
* pwm_omap_dmtimer_apply() - Changes the state of the pwm omap dm timer.
|
||||
* @chip: Pointer to PWM controller
|
||||
* @pwm: Pointer to PWM channel
|
||||
* @state: New state to apply
|
||||
*
|
||||
* Return 0 if successfully changed the state else appropriate error.
|
||||
*/
|
||||
static int pwm_omap_dmtimer_apply(struct pwm_chip *chip,
|
||||
struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip);
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&omap->mutex);
|
||||
|
||||
if (pwm_omap_dmtimer_is_enabled(omap) && !state->enabled) {
|
||||
omap->pdata->stop(omap->dm_timer);
|
||||
goto unlock_mutex;
|
||||
}
|
||||
|
||||
if (pwm_omap_dmtimer_polarity(omap) != state->polarity)
|
||||
pwm_omap_dmtimer_set_polarity(chip, pwm, state->polarity);
|
||||
|
||||
ret = pwm_omap_dmtimer_config(chip, pwm, state->duty_cycle,
|
||||
state->period);
|
||||
if (ret)
|
||||
goto unlock_mutex;
|
||||
|
||||
if (!pwm_omap_dmtimer_is_enabled(omap) && state->enabled) {
|
||||
omap->pdata->set_pwm(omap->dm_timer,
|
||||
state->polarity == PWM_POLARITY_INVERSED,
|
||||
true,
|
||||
OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE,
|
||||
true);
|
||||
pwm_omap_dmtimer_start(omap);
|
||||
}
|
||||
|
||||
unlock_mutex:
|
||||
mutex_unlock(&omap->mutex);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct pwm_ops pwm_omap_dmtimer_ops = {
|
||||
.enable = pwm_omap_dmtimer_enable,
|
||||
.disable = pwm_omap_dmtimer_disable,
|
||||
.config = pwm_omap_dmtimer_config,
|
||||
.set_polarity = pwm_omap_dmtimer_set_polarity,
|
||||
.apply = pwm_omap_dmtimer_apply,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int pwm_omap_dmtimer_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *timer;
|
||||
struct platform_device *timer_pdev;
|
||||
struct pwm_omap_dmtimer_chip *omap;
|
||||
struct dmtimer_platform_data *timer_pdata;
|
||||
const struct omap_dm_timer_ops *pdata;
|
||||
pwm_omap_dmtimer *dm_timer;
|
||||
u32 v;
|
||||
struct platform_device *timer_pdev;
|
||||
struct pwm_omap_dmtimer_chip *omap;
|
||||
struct omap_dm_timer *dm_timer;
|
||||
struct device_node *timer;
|
||||
int ret = 0;
|
||||
u32 v;
|
||||
|
||||
timer = of_parse_phandle(np, "ti,timers", 0);
|
||||
if (!timer)
|
||||
|
@ -281,6 +357,7 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev)
|
|||
!pdata->set_load ||
|
||||
!pdata->set_match ||
|
||||
!pdata->set_pwm ||
|
||||
!pdata->get_pwm_status ||
|
||||
!pdata->set_prescaler ||
|
||||
!pdata->write_counter) {
|
||||
dev_err(&pdev->dev, "Incomplete dmtimer pdata structure\n");
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/bitmap.h>
|
||||
|
||||
/*
|
||||
* Because the PCA9685 has only one prescaler per chip, changing the period of
|
||||
|
@ -69,11 +70,11 @@
|
|||
struct pca9685 {
|
||||
struct pwm_chip chip;
|
||||
struct regmap *regmap;
|
||||
int duty_ns;
|
||||
int period_ns;
|
||||
#if IS_ENABLED(CONFIG_GPIOLIB)
|
||||
struct mutex lock;
|
||||
struct gpio_chip gpio;
|
||||
DECLARE_BITMAP(pwms_inuse, PCA9685_MAXCHAN + 1);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -83,53 +84,53 @@ static inline struct pca9685 *to_pca(struct pwm_chip *chip)
|
|||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_GPIOLIB)
|
||||
static bool pca9685_pwm_test_and_set_inuse(struct pca9685 *pca, int pwm_idx)
|
||||
{
|
||||
bool is_inuse;
|
||||
|
||||
mutex_lock(&pca->lock);
|
||||
if (pwm_idx >= PCA9685_MAXCHAN) {
|
||||
/*
|
||||
* "all LEDs" channel:
|
||||
* pretend already in use if any of the PWMs are requested
|
||||
*/
|
||||
if (!bitmap_empty(pca->pwms_inuse, PCA9685_MAXCHAN)) {
|
||||
is_inuse = true;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* regular channel:
|
||||
* pretend already in use if the "all LEDs" channel is requested
|
||||
*/
|
||||
if (test_bit(PCA9685_MAXCHAN, pca->pwms_inuse)) {
|
||||
is_inuse = true;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
is_inuse = test_and_set_bit(pwm_idx, pca->pwms_inuse);
|
||||
out:
|
||||
mutex_unlock(&pca->lock);
|
||||
return is_inuse;
|
||||
}
|
||||
|
||||
static void pca9685_pwm_clear_inuse(struct pca9685 *pca, int pwm_idx)
|
||||
{
|
||||
mutex_lock(&pca->lock);
|
||||
clear_bit(pwm_idx, pca->pwms_inuse);
|
||||
mutex_unlock(&pca->lock);
|
||||
}
|
||||
|
||||
static int pca9685_pwm_gpio_request(struct gpio_chip *gpio, unsigned int offset)
|
||||
{
|
||||
struct pca9685 *pca = gpiochip_get_data(gpio);
|
||||
struct pwm_device *pwm;
|
||||
|
||||
mutex_lock(&pca->lock);
|
||||
|
||||
pwm = &pca->chip.pwms[offset];
|
||||
|
||||
if (pwm->flags & (PWMF_REQUESTED | PWMF_EXPORTED)) {
|
||||
mutex_unlock(&pca->lock);
|
||||
if (pca9685_pwm_test_and_set_inuse(pca, offset))
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
pwm_set_chip_data(pwm, (void *)1);
|
||||
|
||||
mutex_unlock(&pca->lock);
|
||||
pm_runtime_get_sync(pca->chip.dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool pca9685_pwm_is_gpio(struct pca9685 *pca, struct pwm_device *pwm)
|
||||
{
|
||||
bool is_gpio = false;
|
||||
|
||||
mutex_lock(&pca->lock);
|
||||
|
||||
if (pwm->hwpwm >= PCA9685_MAXCHAN) {
|
||||
unsigned int i;
|
||||
|
||||
/*
|
||||
* Check if any of the GPIOs are requested and in that case
|
||||
* prevent using the "all LEDs" channel.
|
||||
*/
|
||||
for (i = 0; i < pca->gpio.ngpio; i++)
|
||||
if (gpiochip_is_requested(&pca->gpio, i)) {
|
||||
is_gpio = true;
|
||||
break;
|
||||
}
|
||||
} else if (pwm_get_chip_data(pwm)) {
|
||||
is_gpio = true;
|
||||
}
|
||||
|
||||
mutex_unlock(&pca->lock);
|
||||
return is_gpio;
|
||||
}
|
||||
|
||||
static int pca9685_pwm_gpio_get(struct gpio_chip *gpio, unsigned int offset)
|
||||
{
|
||||
struct pca9685 *pca = gpiochip_get_data(gpio);
|
||||
|
@ -162,13 +163,14 @@ static void pca9685_pwm_gpio_free(struct gpio_chip *gpio, unsigned int offset)
|
|||
|
||||
pca9685_pwm_gpio_set(gpio, offset, 0);
|
||||
pm_runtime_put(pca->chip.dev);
|
||||
pca9685_pwm_clear_inuse(pca, offset);
|
||||
}
|
||||
|
||||
static int pca9685_pwm_gpio_get_direction(struct gpio_chip *chip,
|
||||
unsigned int offset)
|
||||
{
|
||||
/* Always out */
|
||||
return 0;
|
||||
return GPIO_LINE_DIRECTION_OUT;
|
||||
}
|
||||
|
||||
static int pca9685_pwm_gpio_direction_input(struct gpio_chip *gpio,
|
||||
|
@ -213,12 +215,17 @@ static int pca9685_pwm_gpio_probe(struct pca9685 *pca)
|
|||
return devm_gpiochip_add_data(dev, &pca->gpio, pca);
|
||||
}
|
||||
#else
|
||||
static inline bool pca9685_pwm_is_gpio(struct pca9685 *pca,
|
||||
struct pwm_device *pwm)
|
||||
static inline bool pca9685_pwm_test_and_set_inuse(struct pca9685 *pca,
|
||||
int pwm_idx)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void
|
||||
pca9685_pwm_clear_inuse(struct pca9685 *pca, int pwm_idx)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int pca9685_pwm_gpio_probe(struct pca9685 *pca)
|
||||
{
|
||||
return 0;
|
||||
|
@ -272,8 +279,6 @@ static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
}
|
||||
}
|
||||
|
||||
pca->duty_ns = duty_ns;
|
||||
|
||||
if (duty_ns < 1) {
|
||||
if (pwm->hwpwm >= PCA9685_MAXCHAN)
|
||||
reg = PCA9685_ALL_LED_OFF_H;
|
||||
|
@ -402,7 +407,7 @@ static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
|
|||
{
|
||||
struct pca9685 *pca = to_pca(chip);
|
||||
|
||||
if (pca9685_pwm_is_gpio(pca, pwm))
|
||||
if (pca9685_pwm_test_and_set_inuse(pca, pwm->hwpwm))
|
||||
return -EBUSY;
|
||||
pm_runtime_get_sync(chip->dev);
|
||||
|
||||
|
@ -411,8 +416,11 @@ static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
|
|||
|
||||
static void pca9685_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
struct pca9685 *pca = to_pca(chip);
|
||||
|
||||
pca9685_pwm_disable(chip, pwm);
|
||||
pm_runtime_put(chip->dev);
|
||||
pca9685_pwm_clear_inuse(pca, pwm->hwpwm);
|
||||
}
|
||||
|
||||
static const struct pwm_ops pca9685_pwm_ops = {
|
||||
|
@ -449,7 +457,6 @@ static int pca9685_pwm_probe(struct i2c_client *client,
|
|||
ret);
|
||||
return ret;
|
||||
}
|
||||
pca->duty_ns = 0;
|
||||
pca->period_ns = PCA9685_DEFAULT_PERIOD;
|
||||
|
||||
i2c_set_clientdata(client, pca);
|
||||
|
@ -512,8 +519,7 @@ static int pca9685_pwm_remove(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int pca9685_pwm_runtime_suspend(struct device *dev)
|
||||
static int __maybe_unused pca9685_pwm_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct pca9685 *pca = i2c_get_clientdata(client);
|
||||
|
@ -522,7 +528,7 @@ static int pca9685_pwm_runtime_suspend(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pca9685_pwm_runtime_resume(struct device *dev)
|
||||
static int __maybe_unused pca9685_pwm_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct pca9685 *pca = i2c_get_clientdata(client);
|
||||
|
@ -530,7 +536,6 @@ static int pca9685_pwm_runtime_resume(struct device *dev)
|
|||
pca9685_set_sleep_mode(pca, false);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id pca9685_id[] = {
|
||||
{ "pca9685", 0 },
|
||||
|
|
|
@ -229,24 +229,28 @@ static int rcar_pwm_probe(struct platform_device *pdev)
|
|||
rcar_pwm->chip.base = -1;
|
||||
rcar_pwm->chip.npwm = 1;
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
ret = pwmchip_add(&rcar_pwm->chip);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to register PWM chip: %d\n", ret);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_pwm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rcar_pwm_chip *rcar_pwm = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
ret = pwmchip_remove(&rcar_pwm->chip);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return pwmchip_remove(&rcar_pwm->chip);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id rcar_pwm_of_table[] = {
|
||||
|
|
|
@ -415,16 +415,15 @@ static int tpu_probe(struct platform_device *pdev)
|
|||
tpu->chip.base = -1;
|
||||
tpu->chip.npwm = TPU_CHANNEL_MAX;
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
ret = pwmchip_add(&tpu->chip);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to register PWM chip\n");
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "TPU PWM %d registered\n", tpu->pdev->id);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -434,12 +433,10 @@ static int tpu_remove(struct platform_device *pdev)
|
|||
int ret;
|
||||
|
||||
ret = pwmchip_remove(&tpu->chip);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
|
|
@ -90,7 +90,6 @@ struct sun4i_pwm_chip {
|
|||
spinlock_t ctrl_lock;
|
||||
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)
|
||||
|
@ -287,7 +286,6 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
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)
|
||||
ctrl &= ~BIT_CH(PWM_ACT_STATE, pwm->hwpwm);
|
||||
|
@ -298,7 +296,7 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
|
||||
if (state->enabled) {
|
||||
ctrl |= BIT_CH(PWM_EN, pwm->hwpwm);
|
||||
} else if (!sun4i_pwm->needs_delay[pwm->hwpwm]) {
|
||||
} else {
|
||||
ctrl &= ~BIT_CH(PWM_EN, pwm->hwpwm);
|
||||
ctrl &= ~BIT_CH(PWM_CLK_GATING, pwm->hwpwm);
|
||||
}
|
||||
|
@ -310,15 +308,9 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
if (state->enabled)
|
||||
return 0;
|
||||
|
||||
if (!sun4i_pwm->needs_delay[pwm->hwpwm]) {
|
||||
clk_disable_unprepare(sun4i_pwm->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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])) {
|
||||
if (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)
|
||||
|
@ -326,7 +318,6 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
else
|
||||
usleep_range(delay_us, delay_us * 2);
|
||||
}
|
||||
sun4i_pwm->needs_delay[pwm->hwpwm] = false;
|
||||
|
||||
spin_lock(&sun4i_pwm->ctrl_lock);
|
||||
ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
|
||||
|
|
|
@ -282,9 +282,15 @@ static const struct tegra_pwm_soc tegra186_pwm_soc = {
|
|||
.max_frequency = 102000000UL,
|
||||
};
|
||||
|
||||
static const struct tegra_pwm_soc tegra194_pwm_soc = {
|
||||
.num_channels = 1,
|
||||
.max_frequency = 408000000UL,
|
||||
};
|
||||
|
||||
static const struct of_device_id tegra_pwm_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra20-pwm", .data = &tegra20_pwm_soc },
|
||||
{ .compatible = "nvidia,tegra186-pwm", .data = &tegra186_pwm_soc },
|
||||
{ .compatible = "nvidia,tegra194-pwm", .data = &tegra194_pwm_soc },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tegra_pwm_of_match);
|
||||
|
|
|
@ -248,8 +248,7 @@ int omap_dm_timers_active(void);
|
|||
|
||||
/*
|
||||
* The below are inlined to optimize code size for system timers. Other code
|
||||
* should not need these at all, see
|
||||
* include/linux/platform_data/pwm_omap_dmtimer.h
|
||||
* should not need these at all.
|
||||
*/
|
||||
#if defined(CONFIG_ARCH_OMAP1) || defined(CONFIG_ARCH_OMAP2PLUS)
|
||||
static inline u32 __omap_dm_timer_read(struct omap_dm_timer *timer, u32 reg,
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
* include/linux/platform_data/pwm_omap_dmtimer.h
|
||||
*
|
||||
* OMAP Dual-Mode Timer PWM platform data
|
||||
*
|
||||
* Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Tarun Kanti DebBarma <tarun.kanti@ti.com>
|
||||
* Thara Gopinath <thara@ti.com>
|
||||
*
|
||||
* Platform device conversion and hwmod support.
|
||||
*
|
||||
* Copyright (C) 2005 Nokia Corporation
|
||||
* Author: Lauri Leukkunen <lauri.leukkunen@nokia.com>
|
||||
* PWM and clock framework support by Timo Teras.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PWM_OMAP_DMTIMER_PDATA_H
|
||||
#define __PWM_OMAP_DMTIMER_PDATA_H
|
||||
|
||||
/* clock sources */
|
||||
#define PWM_OMAP_DMTIMER_SRC_SYS_CLK 0x00
|
||||
#define PWM_OMAP_DMTIMER_SRC_32_KHZ 0x01
|
||||
#define PWM_OMAP_DMTIMER_SRC_EXT_CLK 0x02
|
||||
|
||||
/* timer interrupt enable bits */
|
||||
#define PWM_OMAP_DMTIMER_INT_CAPTURE (1 << 2)
|
||||
#define PWM_OMAP_DMTIMER_INT_OVERFLOW (1 << 1)
|
||||
#define PWM_OMAP_DMTIMER_INT_MATCH (1 << 0)
|
||||
|
||||
/* trigger types */
|
||||
#define PWM_OMAP_DMTIMER_TRIGGER_NONE 0x00
|
||||
#define PWM_OMAP_DMTIMER_TRIGGER_OVERFLOW 0x01
|
||||
#define PWM_OMAP_DMTIMER_TRIGGER_OVERFLOW_AND_COMPARE 0x02
|
||||
|
||||
struct omap_dm_timer;
|
||||
typedef struct omap_dm_timer pwm_omap_dmtimer;
|
||||
|
||||
struct pwm_omap_dmtimer_pdata {
|
||||
pwm_omap_dmtimer *(*request_by_node)(struct device_node *np);
|
||||
pwm_omap_dmtimer *(*request_specific)(int timer_id);
|
||||
pwm_omap_dmtimer *(*request)(void);
|
||||
|
||||
int (*free)(pwm_omap_dmtimer *timer);
|
||||
|
||||
void (*enable)(pwm_omap_dmtimer *timer);
|
||||
void (*disable)(pwm_omap_dmtimer *timer);
|
||||
|
||||
int (*get_irq)(pwm_omap_dmtimer *timer);
|
||||
int (*set_int_enable)(pwm_omap_dmtimer *timer, unsigned int value);
|
||||
int (*set_int_disable)(pwm_omap_dmtimer *timer, u32 mask);
|
||||
|
||||
struct clk *(*get_fclk)(pwm_omap_dmtimer *timer);
|
||||
|
||||
int (*start)(pwm_omap_dmtimer *timer);
|
||||
int (*stop)(pwm_omap_dmtimer *timer);
|
||||
int (*set_source)(pwm_omap_dmtimer *timer, int source);
|
||||
|
||||
int (*set_load)(pwm_omap_dmtimer *timer, int autoreload,
|
||||
unsigned int value);
|
||||
int (*set_match)(pwm_omap_dmtimer *timer, int enable,
|
||||
unsigned int match);
|
||||
int (*set_pwm)(pwm_omap_dmtimer *timer, int def_on,
|
||||
int toggle, int trigger);
|
||||
int (*set_prescaler)(pwm_omap_dmtimer *timer, int prescaler);
|
||||
|
||||
unsigned int (*read_counter)(pwm_omap_dmtimer *timer);
|
||||
int (*write_counter)(pwm_omap_dmtimer *timer, unsigned int value);
|
||||
unsigned int (*read_status)(pwm_omap_dmtimer *timer);
|
||||
int (*write_status)(pwm_omap_dmtimer *timer, unsigned int value);
|
||||
};
|
||||
|
||||
#endif /* __PWM_OMAP_DMTIMER_PDATA_H */
|
|
@ -71,7 +71,8 @@ struct pwm_state {
|
|||
* @chip: PWM chip providing this PWM device
|
||||
* @chip_data: chip-private data associated with the PWM device
|
||||
* @args: PWM arguments
|
||||
* @state: curent PWM channel state
|
||||
* @state: last applied state
|
||||
* @last: last implemented state (for PWM_DEBUG)
|
||||
*/
|
||||
struct pwm_device {
|
||||
const char *label;
|
||||
|
@ -83,6 +84,7 @@ struct pwm_device {
|
|||
|
||||
struct pwm_args args;
|
||||
struct pwm_state state;
|
||||
struct pwm_state last;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue
Block a user