mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-25 08:02:56 +00:00
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
20 changed files with 577 additions and 363 deletions
|
@ -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,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
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
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,
|
||||||
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)
|
||||||
}
|
}
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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 @@
|
||||||
#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 @@
|
||||||
#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 {
|
||||||
|
|
||||||
#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)
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
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);
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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 @@
|
||||||
#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)
|
||||||
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,25 +170,37 @@ 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;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
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 = {
|
||||||
|
|
||||||
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,
|
||||||
{
|
{
|
||||||
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 @@
|
||||||
#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 @@
|
||||||
*
|
*
|
||||||
* 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 @@
|
||||||
#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 @@
|
||||||
#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)
|
||||||
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)
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
|
||||||
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;
|
return 0;
|
||||||
|
|
||||||
err_einval:
|
|
||||||
mutex_unlock(&omap->mutex);
|
|
||||||
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pwm_omap_dmtimer_set_polarity(struct pwm_chip *chip,
|
/**
|
||||||
|
* 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,
|
struct pwm_device *pwm,
|
||||||
enum pwm_polarity polarity)
|
enum pwm_polarity polarity)
|
||||||
{
|
{
|
||||||
struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip);
|
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,
|
omap->pdata->set_pwm(omap->dm_timer,
|
||||||
polarity == PWM_POLARITY_INVERSED,
|
polarity == PWM_POLARITY_INVERSED,
|
||||||
true,
|
true, OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE,
|
||||||
PWM_OMAP_DMTIMER_TRIGGER_OVERFLOW_AND_COMPARE,
|
|
||||||
true);
|
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);
|
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)
|
||||||
!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 @@
|
||||||
#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 @@
|
||||||
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,53 +84,53 @@ static inline struct pca9685 *to_pca(struct pwm_chip *chip)
|
||||||
}
|
}
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_GPIOLIB)
|
#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)
|
static int pca9685_pwm_gpio_request(struct gpio_chip *gpio, unsigned int offset)
|
||||||
{
|
{
|
||||||
struct pca9685 *pca = gpiochip_get_data(gpio);
|
struct pca9685 *pca = gpiochip_get_data(gpio);
|
||||||
struct pwm_device *pwm;
|
|
||||||
|
|
||||||
mutex_lock(&pca->lock);
|
if (pca9685_pwm_test_and_set_inuse(pca, offset))
|
||||||
|
|
||||||
pwm = &pca->chip.pwms[offset];
|
|
||||||
|
|
||||||
if (pwm->flags & (PWMF_REQUESTED | PWMF_EXPORTED)) {
|
|
||||||
mutex_unlock(&pca->lock);
|
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
|
||||||
|
|
||||||
pwm_set_chip_data(pwm, (void *)1);
|
|
||||||
|
|
||||||
mutex_unlock(&pca->lock);
|
|
||||||
pm_runtime_get_sync(pca->chip.dev);
|
pm_runtime_get_sync(pca->chip.dev);
|
||||||
return 0;
|
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)
|
static int pca9685_pwm_gpio_get(struct gpio_chip *gpio, unsigned int offset)
|
||||||
{
|
{
|
||||||
struct pca9685 *pca = gpiochip_get_data(gpio);
|
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);
|
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)
|
||||||
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
|
|
||||||
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,
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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 {
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
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 = {
|
||||||
.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);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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,
|
||||||
|
|
|
@ -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: 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 {
|
||||||
|
|
||||||
struct pwm_args args;
|
struct pwm_args args;
|
||||||
struct pwm_state state;
|
struct pwm_state state;
|
||||||
|
struct pwm_state last;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue