Commit 75bdc929 authored by Linus Torvalds's avatar Linus Torvalds

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

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
  ...
parents 6900433e 9cc5f232
* 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>;
};
};
# 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: ...@@ -9,6 +9,7 @@ Required properties:
- "nvidia,tegra132-pwm", "nvidia,tegra20-pwm": for Tegra132 - "nvidia,tegra132-pwm", "nvidia,tegra20-pwm": for Tegra132
- "nvidia,tegra210-pwm", "nvidia,tegra20-pwm": for Tegra210 - "nvidia,tegra210-pwm", "nvidia,tegra20-pwm": for Tegra210
- "nvidia,tegra186-pwm": for Tegra186 - "nvidia,tegra186-pwm": for Tegra186
- "nvidia,tegra194-pwm": for Tegra194
- reg: physical base address and length of the controller's registers - reg: physical base address and length of the controller's registers
- #pwm-cells: should be 2. See pwm.yaml in this directory for a description of - #pwm-cells: should be 2. See pwm.yaml in this directory for a description of
the cells format. the cells format.
......
...@@ -33,6 +33,15 @@ config PWM_SYSFS ...@@ -33,6 +33,15 @@ config PWM_SYSFS
bool bool
default y if SYSFS 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 config PWM_AB8500
tristate "AB8500 PWM support" tristate "AB8500 PWM support"
depends on AB8500_CORE && ARCH_U8500 depends on AB8500_CORE && ARCH_U8500
...@@ -44,7 +53,8 @@ config PWM_AB8500 ...@@ -44,7 +53,8 @@ config PWM_AB8500
config PWM_ATMEL config PWM_ATMEL
tristate "Atmel PWM support" tristate "Atmel PWM support"
depends on ARCH_AT91 && OF depends on OF
depends on ARCH_AT91 || COMPILE_TEST
help help
Generic PWM framework driver for Atmel SoC. Generic PWM framework driver for Atmel SoC.
...@@ -100,7 +110,7 @@ config PWM_BCM_KONA ...@@ -100,7 +110,7 @@ config PWM_BCM_KONA
config PWM_BCM2835 config PWM_BCM2835
tristate "BCM2835 PWM support" tristate "BCM2835 PWM support"
depends on ARCH_BCM2835 || ARCH_BRCMSTB depends on ARCH_BCM2835 || ARCH_BRCMSTB || COMPILE_TEST
help help
PWM framework driver for BCM2835 controller (Raspberry Pi) PWM framework driver for BCM2835 controller (Raspberry Pi)
...@@ -109,7 +119,7 @@ config PWM_BCM2835 ...@@ -109,7 +119,7 @@ config PWM_BCM2835
config PWM_BERLIN config PWM_BERLIN
tristate "Marvell Berlin PWM support" tristate "Marvell Berlin PWM support"
depends on ARCH_BERLIN depends on ARCH_BERLIN || COMPILE_TEST
help help
PWM framework driver for Marvell Berlin SoCs. PWM framework driver for Marvell Berlin SoCs.
...@@ -118,7 +128,7 @@ config PWM_BERLIN ...@@ -118,7 +128,7 @@ config PWM_BERLIN
config PWM_BRCMSTB config PWM_BRCMSTB
tristate "Broadcom STB PWM support" tristate "Broadcom STB PWM support"
depends on ARCH_BRCMSTB || BMIPS_GENERIC depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
help help
Generic PWM framework driver for the Broadcom Set-top-Box Generic PWM framework driver for the Broadcom Set-top-Box
SoCs (BCM7xxx). SoCs (BCM7xxx).
...@@ -152,7 +162,7 @@ config PWM_CROS_EC ...@@ -152,7 +162,7 @@ config PWM_CROS_EC
config PWM_EP93XX config PWM_EP93XX
tristate "Cirrus Logic EP93xx PWM support" tristate "Cirrus Logic EP93xx PWM support"
depends on ARCH_EP93XX depends on ARCH_EP93XX || COMPILE_TEST
help help
Generic PWM framework driver for Cirrus Logic EP93xx. Generic PWM framework driver for Cirrus Logic EP93xx.
...@@ -195,7 +205,7 @@ config PWM_IMG ...@@ -195,7 +205,7 @@ config PWM_IMG
config PWM_IMX1 config PWM_IMX1
tristate "i.MX1 PWM support" tristate "i.MX1 PWM support"
depends on ARCH_MXC depends on ARCH_MXC || COMPILE_TEST
help help
Generic PWM framework driver for i.MX1 and i.MX21 Generic PWM framework driver for i.MX1 and i.MX21
...@@ -204,7 +214,7 @@ config PWM_IMX1 ...@@ -204,7 +214,7 @@ config PWM_IMX1
config PWM_IMX27 config PWM_IMX27
tristate "i.MX27 PWM support" tristate "i.MX27 PWM support"
depends on ARCH_MXC depends on ARCH_MXC || COMPILE_TEST
help help
Generic PWM framework driver for i.MX27 and later i.MX SoCs. Generic PWM framework driver for i.MX27 and later i.MX SoCs.
...@@ -225,6 +235,8 @@ config PWM_IMX_TPM ...@@ -225,6 +235,8 @@ config PWM_IMX_TPM
config PWM_JZ4740 config PWM_JZ4740
tristate "Ingenic JZ47xx PWM support" tristate "Ingenic JZ47xx PWM support"
depends on MACH_INGENIC depends on MACH_INGENIC
depends on COMMON_CLK
select MFD_SYSCON
help help
Generic PWM framework driver for Ingenic JZ47xx based Generic PWM framework driver for Ingenic JZ47xx based
machines. machines.
...@@ -244,7 +256,7 @@ config PWM_LP3943 ...@@ -244,7 +256,7 @@ config PWM_LP3943
config PWM_LPC18XX_SCT config PWM_LPC18XX_SCT
tristate "LPC18xx/43xx PWM/SCT support" tristate "LPC18xx/43xx PWM/SCT support"
depends on ARCH_LPC18XX depends on ARCH_LPC18XX || COMPILE_TEST
help help
Generic PWM framework driver for NXP LPC18xx PWM/SCT which Generic PWM framework driver for NXP LPC18xx PWM/SCT which
supports 16 channels. supports 16 channels.
...@@ -256,7 +268,7 @@ config PWM_LPC18XX_SCT ...@@ -256,7 +268,7 @@ config PWM_LPC18XX_SCT
config PWM_LPC32XX config PWM_LPC32XX
tristate "LPC32XX PWM support" tristate "LPC32XX PWM support"
depends on ARCH_LPC32XX depends on ARCH_LPC32XX || COMPILE_TEST
help help
Generic PWM framework driver for LPC32XX. The LPC32XX SOC has two Generic PWM framework driver for LPC32XX. The LPC32XX SOC has two
PWM controllers. PWM controllers.
...@@ -289,7 +301,8 @@ config PWM_LPSS_PLATFORM ...@@ -289,7 +301,8 @@ config PWM_LPSS_PLATFORM
config PWM_MESON config PWM_MESON
tristate "Amlogic Meson PWM driver" tristate "Amlogic Meson PWM driver"
depends on ARCH_MESON depends on ARCH_MESON || COMPILE_TEST
depends on COMMON_CLK
help help
The platform driver for Amlogic Meson PWM controller. The platform driver for Amlogic Meson PWM controller.
...@@ -318,7 +331,8 @@ config PWM_MEDIATEK ...@@ -318,7 +331,8 @@ config PWM_MEDIATEK
config PWM_MXS config PWM_MXS
tristate "Freescale MXS PWM support" tristate "Freescale MXS PWM support"
depends on ARCH_MXS && OF depends on OF
depends on ARCH_MXS || COMPILE_TEST
select STMP_DEVICE select STMP_DEVICE
help help
Generic PWM framework driver for Freescale MXS. Generic PWM framework driver for Freescale MXS.
...@@ -357,7 +371,7 @@ config PWM_PUV3 ...@@ -357,7 +371,7 @@ config PWM_PUV3
config PWM_PXA config PWM_PXA
tristate "PXA PWM support" tristate "PXA PWM support"
depends on ARCH_PXA depends on ARCH_PXA || COMPILE_TEST
help help
Generic PWM framework driver for PXA. Generic PWM framework driver for PXA.
...@@ -388,14 +402,14 @@ config PWM_RENESAS_TPU ...@@ -388,14 +402,14 @@ config PWM_RENESAS_TPU
config PWM_ROCKCHIP config PWM_ROCKCHIP
tristate "Rockchip PWM support" tristate "Rockchip PWM support"
depends on ARCH_ROCKCHIP depends on ARCH_ROCKCHIP || COMPILE_TEST
help help
Generic PWM framework driver for the PWM controller found on Generic PWM framework driver for the PWM controller found on
Rockchip SoCs. Rockchip SoCs.
config PWM_SAMSUNG config PWM_SAMSUNG
tristate "Samsung PWM support" tristate "Samsung PWM support"
depends on PLAT_SAMSUNG || ARCH_EXYNOS depends on PLAT_SAMSUNG || ARCH_EXYNOS || COMPILE_TEST
help help
Generic PWM framework driver for Samsung. Generic PWM framework driver for Samsung.
...@@ -415,7 +429,7 @@ config PWM_SIFIVE ...@@ -415,7 +429,7 @@ config PWM_SIFIVE
config PWM_SPEAR config PWM_SPEAR
tristate "STMicroelectronics SPEAr PWM support" tristate "STMicroelectronics SPEAr PWM support"
depends on PLAT_SPEAR depends on PLAT_SPEAR || COMPILE_TEST
depends on OF depends on OF
help help
Generic PWM framework driver for the PWM controller on ST Generic PWM framework driver for the PWM controller on ST
...@@ -437,7 +451,7 @@ config PWM_SPRD ...@@ -437,7 +451,7 @@ config PWM_SPRD
config PWM_STI config PWM_STI
tristate "STiH4xx PWM support" tristate "STiH4xx PWM support"
depends on ARCH_STI depends on ARCH_STI || COMPILE_TEST
depends on OF depends on OF
help help
Generic PWM framework driver for STiH4xx SoCs. Generic PWM framework driver for STiH4xx SoCs.
...@@ -447,7 +461,7 @@ config PWM_STI ...@@ -447,7 +461,7 @@ config PWM_STI
config PWM_STM32 config PWM_STM32
tristate "STMicroelectronics STM32 PWM" tristate "STMicroelectronics STM32 PWM"
depends on MFD_STM32_TIMERS depends on MFD_STM32_TIMERS || COMPILE_TEST
help help
Generic PWM framework driver for STM32 SoCs. Generic PWM framework driver for STM32 SoCs.
...@@ -483,7 +497,7 @@ config PWM_SUN4I ...@@ -483,7 +497,7 @@ config PWM_SUN4I
config PWM_TEGRA config PWM_TEGRA
tristate "NVIDIA Tegra PWM support" tristate "NVIDIA Tegra PWM support"
depends on ARCH_TEGRA depends on ARCH_TEGRA || COMPILE_TEST
help help
Generic PWM framework driver for the PWFM controller found on NVIDIA Generic PWM framework driver for the PWFM controller found on NVIDIA
Tegra SoCs. Tegra SoCs.
...@@ -493,7 +507,7 @@ config PWM_TEGRA ...@@ -493,7 +507,7 @@ config PWM_TEGRA
config PWM_TIECAP config PWM_TIECAP
tristate "ECAP PWM support" tristate "ECAP PWM support"
depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_KEYSTONE || ARCH_K3 depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST
help help
PWM driver support for the ECAP APWM controller found on TI SOCs PWM driver support for the ECAP APWM controller found on TI SOCs
...@@ -502,7 +516,7 @@ config PWM_TIECAP ...@@ -502,7 +516,7 @@ config PWM_TIECAP
config PWM_TIEHRPWM config PWM_TIEHRPWM
tristate "EHRPWM PWM support" tristate "EHRPWM PWM support"
depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_K3 depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_K3 || COMPILE_TEST
help help
PWM driver support for the EHRPWM controller found on TI SOCs PWM driver support for the EHRPWM controller found on TI SOCs
...@@ -529,7 +543,7 @@ config PWM_TWL_LED ...@@ -529,7 +543,7 @@ config PWM_TWL_LED
config PWM_VT8500 config PWM_VT8500
tristate "vt8500 PWM support" tristate "vt8500 PWM support"
depends on ARCH_VT8500 depends on ARCH_VT8500 || COMPILE_TEST
help help
Generic PWM framework driver for vt8500. Generic PWM framework driver for vt8500.
...@@ -538,7 +552,7 @@ config PWM_VT8500 ...@@ -538,7 +552,7 @@ config PWM_VT8500
config PWM_ZX config PWM_ZX
tristate "ZTE ZX PWM support" tristate "ZTE ZX PWM support"
depends on ARCH_ZX depends on ARCH_ZX || COMPILE_TEST
help help
Generic PWM framework driver for ZTE ZX family SoCs. 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) ...@@ -120,6 +120,9 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label)
if (pwm->chip->ops->get_state) { if (pwm->chip->ops->get_state) {
pwm->chip->ops->get_state(pwm->chip, pwm, &pwm->state); pwm->chip->ops->get_state(pwm->chip, pwm, &pwm->state);
trace_pwm_get(pwm, &pwm->state); trace_pwm_get(pwm, &pwm->state);
if (IS_ENABLED(PWM_DEBUG))
pwm->last = pwm->state;
} }
set_bit(PWMF_REQUESTED, &pwm->flags); set_bit(PWMF_REQUESTED, &pwm->flags);
...@@ -232,17 +235,28 @@ void *pwm_get_chip_data(struct pwm_device *pwm) ...@@ -232,17 +235,28 @@ void *pwm_get_chip_data(struct pwm_device *pwm)
} }
EXPORT_SYMBOL_GPL(pwm_get_chip_data); 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 */ /* driver supports legacy, non-atomic operation */
if (ops->config && ops->enable && ops->disable) if (ops->config && ops->enable && ops->disable) {
return true; 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 true;
}
if (!ops->apply)
return false; 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, ...@@ -266,7 +280,7 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
if (!chip || !chip->dev || !chip->ops || !chip->npwm) if (!chip || !chip->dev || !chip->ops || !chip->npwm)
return -EINVAL; return -EINVAL;
if (!pwm_ops_check(chip->ops)) if (!pwm_ops_check(chip))
return -EINVAL; return -EINVAL;
mutex_lock(&pwm_lock); mutex_lock(&pwm_lock);
...@@ -450,6 +464,107 @@ void pwm_free(struct pwm_device *pwm) ...@@ -450,6 +464,107 @@ void pwm_free(struct pwm_device *pwm)
} }
EXPORT_SYMBOL_GPL(pwm_free); 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_apply_state() - atomically apply a new state to a PWM device
* @pwm: PWM device * @pwm: PWM device
...@@ -480,6 +595,12 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state) ...@@ -480,6 +595,12 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state)
trace_pwm_apply(pwm, state); trace_pwm_apply(pwm, state);
pwm->state = *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 { } else {
/* /*
* FIXME: restore the initial state in case of error. * FIXME: restore the initial state in case of error.
......
...@@ -166,6 +166,7 @@ static int bcm2835_pwm_probe(struct platform_device *pdev) ...@@ -166,6 +166,7 @@ static int bcm2835_pwm_probe(struct platform_device *pdev)
pc->chip.dev = &pdev->dev; pc->chip.dev = &pdev->dev;
pc->chip.ops = &bcm2835_pwm_ops; pc->chip.ops = &bcm2835_pwm_ops;
pc->chip.base = -1;
pc->chip.npwm = 2; pc->chip.npwm = 2;
pc->chip.of_xlate = of_pwm_xlate_with_flags; pc->chip.of_xlate = of_pwm_xlate_with_flags;
pc->chip.of_pwm_n_cells = 3; pc->chip.of_pwm_n_cells = 3;
......
...@@ -18,10 +18,8 @@ ...@@ -18,10 +18,8 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/log2.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pwm.h> #include <linux/pwm.h>
#include <linux/slab.h> #include <linux/slab.h>
......
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pwm.h> #include <linux/pwm.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -96,9 +95,8 @@ struct pwm_imx27_chip { ...@@ -96,9 +95,8 @@ struct pwm_imx27_chip {
#define to_pwm_imx27_chip(chip) container_of(chip, struct pwm_imx27_chip, chip) #define to_pwm_imx27_chip(chip) container_of(chip, struct pwm_imx27_chip, chip)
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; int ret;
ret = clk_prepare_enable(imx->clk_ipg); ret = clk_prepare_enable(imx->clk_ipg);
...@@ -114,10 +112,8 @@ static int pwm_imx27_clk_prepare_enable(struct pwm_chip *chip) ...@@ -114,10 +112,8 @@ static int pwm_imx27_clk_prepare_enable(struct pwm_chip *chip)
return 0; 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_per);
clk_disable_unprepare(imx->clk_ipg); clk_disable_unprepare(imx->clk_ipg);
} }
...@@ -130,7 +126,7 @@ static void pwm_imx27_get_state(struct pwm_chip *chip, ...@@ -130,7 +126,7 @@ static void pwm_imx27_get_state(struct pwm_chip *chip,
u64 tmp; u64 tmp;
int ret; int ret;
ret = pwm_imx27_clk_prepare_enable(chip); ret = pwm_imx27_clk_prepare_enable(imx);
if (ret < 0) if (ret < 0)
return; return;
...@@ -174,8 +170,7 @@ static void pwm_imx27_get_state(struct pwm_chip *chip, ...@@ -174,8 +170,7 @@ static void pwm_imx27_get_state(struct pwm_chip *chip,
tmp = NSEC_PER_SEC * (u64)(val); tmp = NSEC_PER_SEC * (u64)(val);
state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, pwm_clk); state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, pwm_clk);
if (!state->enabled) pwm_imx27_clk_disable_unprepare(imx);
pwm_imx27_clk_disable_unprepare(chip);
} }
static void pwm_imx27_sw_reset(struct pwm_chip *chip) 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, ...@@ -259,7 +254,7 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
if (cstate.enabled) { if (cstate.enabled) {
pwm_imx27_wait_fifo_slot(chip, pwm); pwm_imx27_wait_fifo_slot(chip, pwm);
} else { } else {
ret = pwm_imx27_clk_prepare_enable(chip); ret = pwm_imx27_clk_prepare_enable(imx);
if (ret) if (ret)
return ret; return ret;
...@@ -289,8 +284,8 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -289,8 +284,8 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
writel(cr, imx->mmio_base + MX3_PWMCR); writel(cr, imx->mmio_base + MX3_PWMCR);
if (!state->enabled && cstate.enabled) if (!state->enabled)
pwm_imx27_clk_disable_unprepare(chip); pwm_imx27_clk_disable_unprepare(imx);
return 0; return 0;
} }
...@@ -310,6 +305,8 @@ MODULE_DEVICE_TABLE(of, pwm_imx27_dt_ids); ...@@ -310,6 +305,8 @@ MODULE_DEVICE_TABLE(of, pwm_imx27_dt_ids);
static int pwm_imx27_probe(struct platform_device *pdev) static int pwm_imx27_probe(struct platform_device *pdev)
{ {
struct pwm_imx27_chip *imx; struct pwm_imx27_chip *imx;
int ret;
u32 pwmcr;
imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL); imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL);
if (imx == NULL) if (imx == NULL)
...@@ -352,6 +349,15 @@ static int pwm_imx27_probe(struct platform_device *pdev) ...@@ -352,6 +349,15 @@ static int pwm_imx27_probe(struct platform_device *pdev)
if (IS_ERR(imx->mmio_base)) if (IS_ERR(imx->mmio_base))
return PTR_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); return pwmchip_add(&imx->chip);
} }
...@@ -361,8 +367,6 @@ static int pwm_imx27_remove(struct platform_device *pdev) ...@@ -361,8 +367,6 @@ static int pwm_imx27_remove(struct platform_device *pdev)
imx = platform_get_drvdata(pdev); imx = platform_get_drvdata(pdev);
pwm_imx27_clk_disable_unprepare(&imx->chip);
return pwmchip_remove(&imx->chip); return pwmchip_remove(&imx->chip);
} }
......
...@@ -13,18 +13,19 @@ ...@@ -13,18 +13,19 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/mfd/ingenic-tcu.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pwm.h> #include <linux/pwm.h>
#include <linux/regmap.h>
#include <asm/mach-jz4740/timer.h>
#define NUM_PWM 8 #define NUM_PWM 8
struct jz4740_pwm_chip { struct jz4740_pwm_chip {
struct pwm_chip chip; struct pwm_chip chip;
struct clk *clk; struct regmap *map;
}; };
static inline struct jz4740_pwm_chip *to_jz4740(struct pwm_chip *chip) 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) ...@@ -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); 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) static int jz4740_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
{ {
/* struct jz4740_pwm_chip *jz = to_jz4740(chip);
* Timers 0 and 1 are used for system tasks, so they are unavailable struct clk *clk;
* for use as PWMs. char name[16];
*/ int err;
if (pwm->hwpwm < 2)
if (!jz4740_pwm_can_use_chn(jz, pwm->hwpwm))
return -EBUSY; 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; return 0;
} }
static void jz4740_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) 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) 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; /* Enable PWM output */
jz4740_timer_set_ctrl(pwm->hwpwm, ctrl); regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
jz4740_timer_enable(pwm->hwpwm); TCU_TCSR_PWM_EN, TCU_TCSR_PWM_EN);
/* Start counter */
regmap_write(jz->map, TCU_REG_TESR, BIT(pwm->hwpwm));
return 0; return 0;
} }
static void jz4740_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 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 * Set duty > period. This trick allows the TCU channels in TCU2 mode to
* properly return to their init level. * properly return to their init level.
*/ */
jz4740_timer_set_duty(pwm->hwpwm, 0xffff); regmap_write(jz->map, TCU_REG_TDHRc(pwm->hwpwm), 0xffff);
jz4740_timer_set_period(pwm->hwpwm, 0x0); regmap_write(jz->map, TCU_REG_TDFRc(pwm->hwpwm), 0x0);
/* /*
* Disable PWM output. * Disable PWM output.
* In TCU2 mode (channel 1/2 on JZ4750+), this must be done before the * 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. * counter is stopped, while in TCU1 mode the order does not matter.
*/ */
ctrl &= ~JZ_TIMER_CTRL_PWM_ENABLE; regmap_update_bits(jz->map, TCU_REG_TCSRc(pwm->hwpwm),
jz4740_timer_set_ctrl(pwm->hwpwm, ctrl); TCU_TCSR_PWM_EN, 0);
/* Stop counter */ /* 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, static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
const struct pwm_state *state) const struct pwm_state *state)
{ {
struct jz4740_pwm_chip *jz4740 = to_jz4740(pwm->chip); 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 long period, duty;
unsigned int prescaler = 0; long rate;
uint16_t ctrl; int err;
tmp = (unsigned long long)clk_get_rate(jz4740->clk) * state->period; /*
do_div(tmp, 1000000000); * Limit the clock to a maximum rate that still gives us a period value
period = tmp; * which fits in 16 bits.
*/
do_div(tmp, state->period);
while (period > 0xffff && prescaler < 6) { /*
period >>= 2; * /!\ IMPORTANT NOTE:
++prescaler; * -------------------
* 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) /* Calculate period value */
return -EINVAL; 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; tmp = (unsigned long long)period * state->duty_cycle;
do_div(tmp, state->period); do_div(tmp, state->period);
duty = period - tmp; duty = period - tmp;
...@@ -117,26 +170,38 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -117,26 +170,38 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
jz4740_pwm_disable(chip, pwm); jz4740_pwm_disable(chip, pwm);
jz4740_timer_set_count(pwm->hwpwm, 0); err = clk_set_rate(clk, rate);
jz4740_timer_set_duty(pwm->hwpwm, duty); if (err) {
jz4740_timer_set_period(pwm->hwpwm, period); dev_err(chip->dev, "Unable to set rate: %d", err);
return err;
}
/* Reset counter to 0 */
regmap_write(jz4740->map, TCU_REG_TCNTc(pwm->hwpwm), 0);
ctrl = JZ_TIMER_CTRL_PRESCALER(prescaler) | JZ_TIMER_CTRL_SRC_EXT | /* Set duty */
JZ_TIMER_CTRL_PWM_ABBRUPT_SHUTDOWN; regmap_write(jz4740->map, TCU_REG_TDHRc(pwm->hwpwm), duty);
jz4740_timer_set_ctrl(pwm->hwpwm, ctrl); /* 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) { switch (state->polarity) {
case PWM_POLARITY_NORMAL: case PWM_POLARITY_NORMAL:
ctrl &= ~JZ_TIMER_CTRL_PWM_ACTIVE_LOW; regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
TCU_TCSR_PWM_INITL_HIGH, 0);
break; break;
case PWM_POLARITY_INVERSED: case PWM_POLARITY_INVERSED:
ctrl |= JZ_TIMER_CTRL_PWM_ACTIVE_LOW; regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
TCU_TCSR_PWM_INITL_HIGH,
TCU_TCSR_PWM_INITL_HIGH);
break; break;
} }
jz4740_timer_set_ctrl(pwm->hwpwm, ctrl);
if (state->enabled) if (state->enabled)
jz4740_pwm_enable(chip, pwm); jz4740_pwm_enable(chip, pwm);
...@@ -152,17 +217,20 @@ static const struct pwm_ops jz4740_pwm_ops = { ...@@ -152,17 +217,20 @@ static const struct pwm_ops jz4740_pwm_ops = {
static int jz4740_pwm_probe(struct platform_device *pdev) static int jz4740_pwm_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev;
struct jz4740_pwm_chip *jz4740; struct jz4740_pwm_chip *jz4740;
jz4740 = devm_kzalloc(&pdev->dev, sizeof(*jz4740), GFP_KERNEL); jz4740 = devm_kzalloc(dev, sizeof(*jz4740), GFP_KERNEL);
if (!jz4740) if (!jz4740)
return -ENOMEM; return -ENOMEM;
jz4740->clk = devm_clk_get(&pdev->dev, "ext"); jz4740->map = device_node_to_regmap(dev->parent->of_node);
if (IS_ERR(jz4740->clk)) if (IS_ERR(jz4740->map)) {
return PTR_ERR(jz4740->clk); 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.ops = &jz4740_pwm_ops;
jz4740->chip.npwm = NUM_PWM; jz4740->chip.npwm = NUM_PWM;
jz4740->chip.base = -1; jz4740->chip.base = -1;
......
...@@ -163,7 +163,7 @@ static int meson_pwm_calc(struct meson_pwm *meson, struct pwm_device *pwm, ...@@ -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); struct meson_pwm_channel *channel = pwm_get_chip_data(pwm);
unsigned int duty, period, pre_div, cnt, duty_cnt; unsigned int duty, period, pre_div, cnt, duty_cnt;
unsigned long fin_freq = -1; unsigned long fin_freq;
duty = state->duty_cycle; duty = state->duty_cycle;
period = state->period; period = state->period;
......
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pwm.h> #include <linux/pwm.h>
#include <linux/slab.h> #include <linux/slab.h>
......
...@@ -10,7 +10,27 @@ ...@@ -10,7 +10,27 @@
* *
* Description: * Description:
* This file is the core OMAP support for the generic, Linux * 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> #include <linux/clk.h>
...@@ -20,8 +40,8 @@ ...@@ -20,8 +40,8 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <clocksource/timer-ti-dm.h>
#include <linux/platform_data/dmtimer-omap.h> #include <linux/platform_data/dmtimer-omap.h>
#include <linux/platform_data/pwm_omap_dmtimer.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/pwm.h> #include <linux/pwm.h>
...@@ -31,10 +51,20 @@ ...@@ -31,10 +51,20 @@
#define DM_TIMER_LOAD_MIN 0xfffffffe #define DM_TIMER_LOAD_MIN 0xfffffffe
#define DM_TIMER_MAX 0xffffffff #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_omap_dmtimer_chip {
struct pwm_chip chip; struct pwm_chip chip;
/* Mutex to protect pwm apply state */
struct mutex mutex; struct mutex mutex;
pwm_omap_dmtimer *dm_timer; struct omap_dm_timer *dm_timer;
const struct omap_dm_timer_ops *pdata; const struct omap_dm_timer_ops *pdata;
struct platform_device *dm_timer_pdev; struct platform_device *dm_timer_pdev;
}; };
...@@ -45,11 +75,22 @@ to_pwm_omap_dmtimer_chip(struct pwm_chip *chip) ...@@ -45,11 +75,22 @@ to_pwm_omap_dmtimer_chip(struct pwm_chip *chip)
return container_of(chip, struct pwm_omap_dmtimer_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) 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); 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) 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) ...@@ -67,28 +108,46 @@ static void pwm_omap_dmtimer_start(struct pwm_omap_dmtimer_chip *omap)
omap->pdata->start(omap->dm_timer); 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); status = omap->pdata->get_pwm_status(omap->dm_timer);
pwm_omap_dmtimer_start(omap);
mutex_unlock(&omap->mutex);
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); status = omap->pdata->get_pwm_status(omap->dm_timer);
omap->pdata->stop(omap->dm_timer);
mutex_unlock(&omap->mutex); 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, static int pwm_omap_dmtimer_config(struct pwm_chip *chip,
struct pwm_device *pwm, struct pwm_device *pwm,
int duty_ns, int period_ns) int duty_ns, int period_ns)
...@@ -96,31 +155,26 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip, ...@@ -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); struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip);
u32 period_cycles, duty_cycles; u32 period_cycles, duty_cycles;
u32 load_value, match_value; u32 load_value, match_value;
struct clk *fclk;
unsigned long clk_rate; unsigned long clk_rate;
bool timer_active; struct clk *fclk;
dev_dbg(chip->dev, "requested duty cycle: %d ns, period: %d ns\n", dev_dbg(chip->dev, "requested duty cycle: %d ns, period: %d ns\n",
duty_ns, period_ns); duty_ns, period_ns);
mutex_lock(&omap->mutex);
if (duty_ns == pwm_get_duty_cycle(pwm) && if (duty_ns == pwm_get_duty_cycle(pwm) &&
period_ns == pwm_get_period(pwm)) { period_ns == pwm_get_period(pwm))
/* No change - don't cause any transients. */
mutex_unlock(&omap->mutex);
return 0; return 0;
}
fclk = omap->pdata->get_fclk(omap->dm_timer); fclk = omap->pdata->get_fclk(omap->dm_timer);
if (!fclk) { if (!fclk) {
dev_err(chip->dev, "invalid pmtimer fclk\n"); dev_err(chip->dev, "invalid pmtimer fclk\n");
goto err_einval; return -EINVAL;
} }
clk_rate = clk_get_rate(fclk); clk_rate = clk_get_rate(fclk);
if (!clk_rate) { if (!clk_rate) {
dev_err(chip->dev, "invalid pmtimer fclk rate\n"); dev_err(chip->dev, "invalid pmtimer fclk rate\n");
goto err_einval; return -EINVAL;
} }
dev_dbg(chip->dev, "clk rate: %luHz\n", clk_rate); dev_dbg(chip->dev, "clk rate: %luHz\n", clk_rate);
...@@ -148,7 +202,7 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip, ...@@ -148,7 +202,7 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip,
dev_info(chip->dev, dev_info(chip->dev,
"period %d ns too short for clock rate %lu Hz\n", "period %d ns too short for clock rate %lu Hz\n",
period_ns, clk_rate); period_ns, clk_rate);
goto err_einval; return -EINVAL;
} }
if (duty_cycles < 1) { if (duty_cycles < 1) {
...@@ -174,81 +228,103 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip, ...@@ -174,81 +228,103 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip,
load_value = (DM_TIMER_MAX - period_cycles) + 1; load_value = (DM_TIMER_MAX - period_cycles) + 1;
match_value = load_value + duty_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_load(omap->dm_timer, load_value);
omap->pdata->set_match(omap->dm_timer, true, match_value); omap->pdata->set_match(omap->dm_timer, true, match_value);
dev_dbg(chip->dev, "load value: %#08x (%d), match value: %#08x (%d)\n", dev_dbg(chip->dev, "load value: %#08x (%d), match value: %#08x (%d)\n",
load_value, load_value, match_value, match_value); load_value, load_value, match_value, match_value);
omap->pdata->set_pwm(omap->dm_timer, return 0;
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); /**
* 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;
return 0; /* Disable the PWM before changing the polarity. */
enabled = pwm_omap_dmtimer_is_enabled(omap);
if (enabled)
omap->pdata->stop(omap->dm_timer);
err_einval: omap->pdata->set_pwm(omap->dm_timer,
mutex_unlock(&omap->mutex); polarity == PWM_POLARITY_INVERSED,
true, OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE,
true);
return -EINVAL; if (enabled)
pwm_omap_dmtimer_start(omap);
} }
static int pwm_omap_dmtimer_set_polarity(struct pwm_chip *chip, /**
* 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, struct pwm_device *pwm,
enum pwm_polarity polarity) const struct pwm_state *state)
{ {
struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip); struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip);
int ret = 0;
/*
* 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); 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, omap->pdata->set_pwm(omap->dm_timer,
polarity == PWM_POLARITY_INVERSED, state->polarity == PWM_POLARITY_INVERSED,
true, true,
PWM_OMAP_DMTIMER_TRIGGER_OVERFLOW_AND_COMPARE, OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE,
true); true);
pwm_omap_dmtimer_start(omap);
}
unlock_mutex:
mutex_unlock(&omap->mutex); mutex_unlock(&omap->mutex);
return 0; return ret;
} }
static const struct pwm_ops pwm_omap_dmtimer_ops = { static const struct pwm_ops pwm_omap_dmtimer_ops = {
.enable = pwm_omap_dmtimer_enable, .apply = pwm_omap_dmtimer_apply,
.disable = pwm_omap_dmtimer_disable,
.config = pwm_omap_dmtimer_config,
.set_polarity = pwm_omap_dmtimer_set_polarity,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
static int pwm_omap_dmtimer_probe(struct platform_device *pdev) static int pwm_omap_dmtimer_probe(struct platform_device *pdev)
{ {
struct device_node *np = pdev->dev.of_node; 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; struct dmtimer_platform_data *timer_pdata;
const struct omap_dm_timer_ops *pdata; const struct omap_dm_timer_ops *pdata;
pwm_omap_dmtimer *dm_timer; struct platform_device *timer_pdev;
u32 v; struct pwm_omap_dmtimer_chip *omap;
struct omap_dm_timer *dm_timer;
struct device_node *timer;
int ret = 0; int ret = 0;
u32 v;
timer = of_parse_phandle(np, "ti,timers", 0); timer = of_parse_phandle(np, "ti,timers", 0);
if (!timer) if (!timer)
...@@ -281,6 +357,7 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev) ...@@ -281,6 +357,7 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev)
!pdata->set_load || !pdata->set_load ||
!pdata->set_match || !pdata->set_match ||
!pdata->set_pwm || !pdata->set_pwm ||
!pdata->get_pwm_status ||
!pdata->set_prescaler || !pdata->set_prescaler ||
!pdata->write_counter) { !pdata->write_counter) {
dev_err(&pdev->dev, "Incomplete dmtimer pdata structure\n"); dev_err(&pdev->dev, "Incomplete dmtimer pdata structure\n");
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/bitmap.h>
/* /*
* Because the PCA9685 has only one prescaler per chip, changing the period of * Because the PCA9685 has only one prescaler per chip, changing the period of
...@@ -69,11 +70,11 @@ ...@@ -69,11 +70,11 @@
struct pca9685 { struct pca9685 {
struct pwm_chip chip; struct pwm_chip chip;
struct regmap *regmap; struct regmap *regmap;
int duty_ns;
int period_ns; int period_ns;
#if IS_ENABLED(CONFIG_GPIOLIB) #if IS_ENABLED(CONFIG_GPIOLIB)
struct mutex lock; struct mutex lock;
struct gpio_chip gpio; struct gpio_chip gpio;
DECLARE_BITMAP(pwms_inuse, PCA9685_MAXCHAN + 1);
#endif #endif
}; };
...@@ -83,51 +84,51 @@ static inline struct pca9685 *to_pca(struct pwm_chip *chip) ...@@ -83,51 +84,51 @@ static inline struct pca9685 *to_pca(struct pwm_chip *chip)
} }
#if IS_ENABLED(CONFIG_GPIOLIB) #if IS_ENABLED(CONFIG_GPIOLIB)
static int pca9685_pwm_gpio_request(struct gpio_chip *gpio, unsigned int offset) static bool pca9685_pwm_test_and_set_inuse(struct pca9685 *pca, int pwm_idx)
{ {
struct pca9685 *pca = gpiochip_get_data(gpio); bool is_inuse;
struct pwm_device *pwm;
mutex_lock(&pca->lock); mutex_lock(&pca->lock);
if (pwm_idx >= PCA9685_MAXCHAN) {
pwm = &pca->chip.pwms[offset]; /*
* "all LEDs" channel:
if (pwm->flags & (PWMF_REQUESTED | PWMF_EXPORTED)) { * pretend already in use if any of the PWMs are requested
mutex_unlock(&pca->lock); */
return -EBUSY; if (!bitmap_empty(pca->pwms_inuse, PCA9685_MAXCHAN)) {
is_inuse = true;
goto out;
} }
} else {
pwm_set_chip_data(pwm, (void *)1); /*
* 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); mutex_unlock(&pca->lock);
pm_runtime_get_sync(pca->chip.dev); return is_inuse;
return 0;
} }
static bool pca9685_pwm_is_gpio(struct pca9685 *pca, struct pwm_device *pwm) static void pca9685_pwm_clear_inuse(struct pca9685 *pca, int pwm_idx)
{ {
bool is_gpio = false;
mutex_lock(&pca->lock); mutex_lock(&pca->lock);
clear_bit(pwm_idx, pca->pwms_inuse);
mutex_unlock(&pca->lock);
}
if (pwm->hwpwm >= PCA9685_MAXCHAN) { static int pca9685_pwm_gpio_request(struct gpio_chip *gpio, unsigned int offset)
unsigned int i; {
struct pca9685 *pca = gpiochip_get_data(gpio);
/*
* 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); if (pca9685_pwm_test_and_set_inuse(pca, offset))
return is_gpio; return -EBUSY;
pm_runtime_get_sync(pca->chip.dev);
return 0;
} }
static int pca9685_pwm_gpio_get(struct gpio_chip *gpio, unsigned int offset) static int pca9685_pwm_gpio_get(struct gpio_chip *gpio, unsigned int offset)
...@@ -162,13 +163,14 @@ static void pca9685_pwm_gpio_free(struct gpio_chip *gpio, unsigned int offset) ...@@ -162,13 +163,14 @@ static void pca9685_pwm_gpio_free(struct gpio_chip *gpio, unsigned int offset)
pca9685_pwm_gpio_set(gpio, offset, 0); pca9685_pwm_gpio_set(gpio, offset, 0);
pm_runtime_put(pca->chip.dev); pm_runtime_put(pca->chip.dev);
pca9685_pwm_clear_inuse(pca, offset);
} }
static int pca9685_pwm_gpio_get_direction(struct gpio_chip *chip, static int pca9685_pwm_gpio_get_direction(struct gpio_chip *chip,
unsigned int offset) unsigned int offset)
{ {
/* Always out */ /* Always out */
return 0; return GPIO_LINE_DIRECTION_OUT;
} }
static int pca9685_pwm_gpio_direction_input(struct gpio_chip *gpio, static int pca9685_pwm_gpio_direction_input(struct gpio_chip *gpio,
...@@ -213,12 +215,17 @@ static int pca9685_pwm_gpio_probe(struct pca9685 *pca) ...@@ -213,12 +215,17 @@ static int pca9685_pwm_gpio_probe(struct pca9685 *pca)
return devm_gpiochip_add_data(dev, &pca->gpio, pca); return devm_gpiochip_add_data(dev, &pca->gpio, pca);
} }
#else #else
static inline bool pca9685_pwm_is_gpio(struct pca9685 *pca, static inline bool pca9685_pwm_test_and_set_inuse(struct pca9685 *pca,
struct pwm_device *pwm) int pwm_idx)
{ {
return false; 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) static inline int pca9685_pwm_gpio_probe(struct pca9685 *pca)
{ {
return 0; return 0;
...@@ -272,8 +279,6 @@ static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -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 (duty_ns < 1) {
if (pwm->hwpwm >= PCA9685_MAXCHAN) if (pwm->hwpwm >= PCA9685_MAXCHAN)
reg = PCA9685_ALL_LED_OFF_H; reg = PCA9685_ALL_LED_OFF_H;
...@@ -402,7 +407,7 @@ static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) ...@@ -402,7 +407,7 @@ static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
{ {
struct pca9685 *pca = to_pca(chip); 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; return -EBUSY;
pm_runtime_get_sync(chip->dev); pm_runtime_get_sync(chip->dev);
...@@ -411,8 +416,11 @@ static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) ...@@ -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) static void pca9685_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
{ {
struct pca9685 *pca = to_pca(chip);
pca9685_pwm_disable(chip, pwm); pca9685_pwm_disable(chip, pwm);
pm_runtime_put(chip->dev); pm_runtime_put(chip->dev);
pca9685_pwm_clear_inuse(pca, pwm->hwpwm);
} }
static const struct pwm_ops pca9685_pwm_ops = { static const struct pwm_ops pca9685_pwm_ops = {
...@@ -449,7 +457,6 @@ static int pca9685_pwm_probe(struct i2c_client *client, ...@@ -449,7 +457,6 @@ static int pca9685_pwm_probe(struct i2c_client *client,
ret); ret);
return ret; return ret;
} }
pca->duty_ns = 0;
pca->period_ns = PCA9685_DEFAULT_PERIOD; pca->period_ns = PCA9685_DEFAULT_PERIOD;
i2c_set_clientdata(client, pca); i2c_set_clientdata(client, pca);
...@@ -512,8 +519,7 @@ static int pca9685_pwm_remove(struct i2c_client *client) ...@@ -512,8 +519,7 @@ static int pca9685_pwm_remove(struct i2c_client *client)
return 0; return 0;
} }
#ifdef CONFIG_PM static int __maybe_unused pca9685_pwm_runtime_suspend(struct device *dev)
static int pca9685_pwm_runtime_suspend(struct device *dev)
{ {
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = to_i2c_client(dev);
struct pca9685 *pca = i2c_get_clientdata(client); struct pca9685 *pca = i2c_get_clientdata(client);
...@@ -522,7 +528,7 @@ static int pca9685_pwm_runtime_suspend(struct device *dev) ...@@ -522,7 +528,7 @@ static int pca9685_pwm_runtime_suspend(struct device *dev)
return 0; 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 i2c_client *client = to_i2c_client(dev);
struct pca9685 *pca = i2c_get_clientdata(client); struct pca9685 *pca = i2c_get_clientdata(client);
...@@ -530,7 +536,6 @@ static int pca9685_pwm_runtime_resume(struct device *dev) ...@@ -530,7 +536,6 @@ static int pca9685_pwm_runtime_resume(struct device *dev)
pca9685_set_sleep_mode(pca, false); pca9685_set_sleep_mode(pca, false);
return 0; return 0;
} }
#endif
static const struct i2c_device_id pca9685_id[] = { static const struct i2c_device_id pca9685_id[] = {
{ "pca9685", 0 }, { "pca9685", 0 },
......
...@@ -229,24 +229,28 @@ static int rcar_pwm_probe(struct platform_device *pdev) ...@@ -229,24 +229,28 @@ static int rcar_pwm_probe(struct platform_device *pdev)
rcar_pwm->chip.base = -1; rcar_pwm->chip.base = -1;
rcar_pwm->chip.npwm = 1; rcar_pwm->chip.npwm = 1;
pm_runtime_enable(&pdev->dev);
ret = pwmchip_add(&rcar_pwm->chip); ret = pwmchip_add(&rcar_pwm->chip);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "failed to register PWM chip: %d\n", ret); dev_err(&pdev->dev, "failed to register PWM chip: %d\n", ret);
pm_runtime_disable(&pdev->dev);
return ret; return ret;
} }
pm_runtime_enable(&pdev->dev);
return 0; return 0;
} }
static int rcar_pwm_remove(struct platform_device *pdev) static int rcar_pwm_remove(struct platform_device *pdev)
{ {
struct rcar_pwm_chip *rcar_pwm = platform_get_drvdata(pdev); struct rcar_pwm_chip *rcar_pwm = platform_get_drvdata(pdev);
int ret;
ret = pwmchip_remove(&rcar_pwm->chip);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
return pwmchip_remove(&rcar_pwm->chip); return ret;
} }
static const struct of_device_id rcar_pwm_of_table[] = { static const struct of_device_id rcar_pwm_of_table[] = {
......
...@@ -415,16 +415,15 @@ static int tpu_probe(struct platform_device *pdev) ...@@ -415,16 +415,15 @@ static int tpu_probe(struct platform_device *pdev)
tpu->chip.base = -1; tpu->chip.base = -1;
tpu->chip.npwm = TPU_CHANNEL_MAX; tpu->chip.npwm = TPU_CHANNEL_MAX;
pm_runtime_enable(&pdev->dev);
ret = pwmchip_add(&tpu->chip); ret = pwmchip_add(&tpu->chip);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "failed to register PWM chip\n"); dev_err(&pdev->dev, "failed to register PWM chip\n");
pm_runtime_disable(&pdev->dev);
return ret; return ret;
} }
dev_info(&pdev->dev, "TPU PWM %d registered\n", tpu->pdev->id);
pm_runtime_enable(&pdev->dev);
return 0; return 0;
} }
...@@ -434,12 +433,10 @@ static int tpu_remove(struct platform_device *pdev) ...@@ -434,12 +433,10 @@ static int tpu_remove(struct platform_device *pdev)
int ret; int ret;
ret = pwmchip_remove(&tpu->chip); ret = pwmchip_remove(&tpu->chip);
if (ret)
return ret;
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
return 0; return ret;
} }
#ifdef CONFIG_OF #ifdef CONFIG_OF
......
...@@ -90,7 +90,6 @@ struct sun4i_pwm_chip { ...@@ -90,7 +90,6 @@ struct sun4i_pwm_chip {
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]; 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)
...@@ -287,7 +286,6 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -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_writel(sun4i_pwm, val, PWM_CH_PRD(pwm->hwpwm));
sun4i_pwm->next_period[pwm->hwpwm] = jiffies + sun4i_pwm->next_period[pwm->hwpwm] = jiffies +
usecs_to_jiffies(cstate.period / 1000 + 1); usecs_to_jiffies(cstate.period / 1000 + 1);
sun4i_pwm->needs_delay[pwm->hwpwm] = true;
if (state->polarity != PWM_POLARITY_NORMAL) if (state->polarity != PWM_POLARITY_NORMAL)
ctrl &= ~BIT_CH(PWM_ACT_STATE, pwm->hwpwm); ctrl &= ~BIT_CH(PWM_ACT_STATE, pwm->hwpwm);
...@@ -298,7 +296,7 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -298,7 +296,7 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
if (state->enabled) { if (state->enabled) {
ctrl |= BIT_CH(PWM_EN, pwm->hwpwm); ctrl |= BIT_CH(PWM_EN, pwm->hwpwm);
} else if (!sun4i_pwm->needs_delay[pwm->hwpwm]) { } else {
ctrl &= ~BIT_CH(PWM_EN, pwm->hwpwm); ctrl &= ~BIT_CH(PWM_EN, pwm->hwpwm);
ctrl &= ~BIT_CH(PWM_CLK_GATING, 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, ...@@ -310,15 +308,9 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
if (state->enabled) if (state->enabled)
return 0; 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. */ /* We need a full period to elapse before disabling the channel. */
now = jiffies; now = jiffies;
if (sun4i_pwm->needs_delay[pwm->hwpwm] && if (time_before(now, sun4i_pwm->next_period[pwm->hwpwm])) {
time_before(now, sun4i_pwm->next_period[pwm->hwpwm])) {
delay_us = jiffies_to_usecs(sun4i_pwm->next_period[pwm->hwpwm] - delay_us = jiffies_to_usecs(sun4i_pwm->next_period[pwm->hwpwm] -
now); now);
if ((delay_us / 500) > MAX_UDELAY_MS) if ((delay_us / 500) > MAX_UDELAY_MS)
...@@ -326,7 +318,6 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, ...@@ -326,7 +318,6 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
else else
usleep_range(delay_us, delay_us * 2); 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);
ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG); ctrl = sun4i_pwm_readl(sun4i_pwm, PWM_CTRL_REG);
......
...@@ -282,9 +282,15 @@ static const struct tegra_pwm_soc tegra186_pwm_soc = { ...@@ -282,9 +282,15 @@ static const struct tegra_pwm_soc tegra186_pwm_soc = {
.max_frequency = 102000000UL, .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[] = { static const struct of_device_id tegra_pwm_of_match[] = {
{ .compatible = "nvidia,tegra20-pwm", .data = &tegra20_pwm_soc }, { .compatible = "nvidia,tegra20-pwm", .data = &tegra20_pwm_soc },
{ .compatible = "nvidia,tegra186-pwm", .data = &tegra186_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); MODULE_DEVICE_TABLE(of, tegra_pwm_of_match);
......
...@@ -248,8 +248,7 @@ int omap_dm_timers_active(void); ...@@ -248,8 +248,7 @@ int omap_dm_timers_active(void);
/* /*
* The below are inlined to optimize code size for system timers. Other code * The below are inlined to optimize code size for system timers. Other code
* should not need these at all, see * should not need these at all.
* include/linux/platform_data/pwm_omap_dmtimer.h
*/ */
#if defined(CONFIG_ARCH_OMAP1) || defined(CONFIG_ARCH_OMAP2PLUS) #if defined(CONFIG_ARCH_OMAP1) || defined(CONFIG_ARCH_OMAP2PLUS)
static inline u32 __omap_dm_timer_read(struct omap_dm_timer *timer, u32 reg, static inline u32 __omap_dm_timer_read(struct omap_dm_timer *timer, u32 reg,
......
/*
* 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 { ...@@ -71,7 +71,8 @@ struct pwm_state {
* @chip: PWM chip providing this PWM device * @chip: PWM chip providing this PWM device
* @chip_data: chip-private data associated with the PWM device * @chip_data: chip-private data associated with the PWM device
* @args: PWM arguments * @args: PWM arguments
* @state: curent PWM channel state * @state: last applied state
* @last: last implemented state (for PWM_DEBUG)
*/ */
struct pwm_device { struct pwm_device {
const char *label; const char *label;
...@@ -83,6 +84,7 @@ struct pwm_device { ...@@ -83,6 +84,7 @@ struct pwm_device {
struct pwm_args args; struct pwm_args args;
struct pwm_state state; struct pwm_state state;
struct pwm_state last;
}; };
/** /**
......
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