pwm: Changes for v4.20-rc1
This series contains a number of improvements to existing drivers, such as LPSS. Some drivers, such as renesas-tpu and rcar get support for more SoC generations. To round things off this fixes an issue with the sysfs interface. -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEEiOrDCAFJzPfAjcif3SOs138+s6EFAlvcT08ZHHRoaWVycnku cmVkaW5nQGdtYWlsLmNvbQAKCRDdI6zXfz6zoRZ4D/9JewpxTGItNlmgRbrKU2vG IN5B13z/bEUQo3DS0bsqPqso6kma++Y2JJSs6/qABcjvvigAl9/HTzsP1ooJngmg 3VfEXG6r7q6dA+ckqtIyti/VQ57mLmbSGjp8Y5BfA57hfrbTcKEWSjt5JynJodcR byOAMazpA+T28Vh4b1ickdztQ92vxe8NrHLHWXzWTePbSuFCeTQ+RjYaKQk3VPsk kIajkvR2cufxiTY/pBoqJrheNt3W2QY36Lzx8UbVK3vftzFBsBzwepzvO2RAk/D2 lvdGMhOPlDkk8KNaKftbtYct43pAzNfgxjfvRTJP5/+1Sr50FJtQDHYDKEm1qOsn Z2BZ0rCuAYlu6XITSjYaEfyyrrTpd+Gl7WhSUYnu1Esbv33xD530ALU5MzaU0YD/ yuJCqi5ddojJ2/3uOssDuA3cYdoZnQCzODKWWnH8JjGKas9s8KdbPp+7r9Inb8OX uVZM4L3k1ZtAyo/JgtwTK+/fog1ulDI1+L3oNgdFNzCS5Kv67AsZyA9SczI8itwn qdP0ZhQi5ZfmcuadynNy5qAtsZQk9w/i/fij/Rd/PoQOAzRymQJExeVDaDSd50qd A0jlw3Tw/WDY3YQpnW/555YH4z480smHAvO9tDZFINMD26gy3x+GaFAlUZKLLggp Oc39LyR4I6Z6rpPHCf1A0Q== =AECG -----END PGP SIGNATURE----- Merge tag 'pwm/for-4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm Pull pwm updates from Thierry Reding: "This series contains a number of improvements to existing drivers, such as LPSS. Some drivers, such as renesas-tpu and rcar get support for more SoC generations. To round things off this fixes an issue with the sysfs interface" * tag 'pwm/for-4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm: pwm: lpss: Only set update bit if we are actually changing the settings pwm: lpss: Force runtime-resume on suspend on Cherry Trail pwm: Enable TI ECAP driver for ARCH_K3 dt-bindings: pwm: tiecap: Add TI AM654 SoC specific compatible dt-bindings: pwm: rcar: Add r8a774a1 support pwm: Send a uevent on the pwmchip device upon channel sysfs (un)export Revert "pwm: Set class for exported channels in sysfs" dt-bindings: pwm: renesas-tpu: Document r8a7744 support dt-bindings: pwm: rcar: Add r8a7744 support dt-bindings: pwm: renesas: tpu: Document R8A779{7|8}0 bindings dt-bindings: pwm: renesas: pwm-rcar: Document R8A779{7|8}0 bindings dt-bindings: pwm: renesas: tpu: Fix "compatible" prop description pwm: Use SPDX identifier for Renesas drivers pwm: lpss: Add get_state callback pwm: lpss: Release runtime-pm reference from the driver's remove callback pwm: lpss: Check PWM powerstate after resume on Cherry Trail devices pwm: lpss: Move struct pwm_lpss_chip definition to the header file pwm: lpss: Add ACPI HID for second PWM controller on Cherry Trail devices ACPI / PM: Export acpi_device_get_power() for use by modular build drivers pwm: tegra: Remove gratuituous blank line
This commit is contained in:
commit
fcc37f76a9
|
@ -7,6 +7,7 @@ Required properties:
|
|||
for da850 - compatible = "ti,da850-ecap", "ti,am3352-ecap", "ti,am33xx-ecap";
|
||||
for dra746 - compatible = "ti,dra746-ecap", "ti,am3352-ecap";
|
||||
for 66ak2g - compatible = "ti,k2g-ecap", "ti,am3352-ecap";
|
||||
for am654 - compatible = "ti,am654-ecap", "ti,am3352-ecap";
|
||||
- #pwm-cells: should be 3. See pwm.txt in this directory for a description of
|
||||
the cells format. The PWM channel index ranges from 0 to 4. The only third
|
||||
cell flag supported by this binding is PWM_POLARITY_INVERTED.
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
Required Properties:
|
||||
- compatible: should be "renesas,pwm-rcar" and one of the following.
|
||||
- "renesas,pwm-r8a7743": for RZ/G1M
|
||||
- "renesas,pwm-r8a7744": for RZ/G1N
|
||||
- "renesas,pwm-r8a7745": for RZ/G1E
|
||||
- "renesas,pwm-r8a774a1": for RZ/G2M
|
||||
- "renesas,pwm-r8a7778": for R-Car M1A
|
||||
- "renesas,pwm-r8a7779": for R-Car H1
|
||||
- "renesas,pwm-r8a7790": for R-Car H2
|
||||
|
@ -12,6 +14,8 @@ Required Properties:
|
|||
- "renesas,pwm-r8a7795": for R-Car H3
|
||||
- "renesas,pwm-r8a7796": for R-Car M3-W
|
||||
- "renesas,pwm-r8a77965": for R-Car M3-N
|
||||
- "renesas,pwm-r8a77970": for R-Car V3M
|
||||
- "renesas,pwm-r8a77980": for R-Car V3H
|
||||
- "renesas,pwm-r8a77990": for R-Car E3
|
||||
- "renesas,pwm-r8a77995": for R-Car D3
|
||||
- reg: base address and length of the registers block for the PWM.
|
||||
|
|
|
@ -2,13 +2,19 @@
|
|||
|
||||
Required Properties:
|
||||
|
||||
- compatible: should be one of the following.
|
||||
- compatible: must contain one or more of the following:
|
||||
- "renesas,tpu-r8a73a4": for R8A73A4 (R-Mobile APE6) compatible PWM controller.
|
||||
- "renesas,tpu-r8a7740": for R8A7740 (R-Mobile A1) compatible PWM controller.
|
||||
- "renesas,tpu-r8a7743": for R8A7743 (RZ/G1M) compatible PWM controller.
|
||||
- "renesas,tpu-r8a7744": for R8A7744 (RZ/G1N) compatible PWM controller.
|
||||
- "renesas,tpu-r8a7745": for R8A7745 (RZ/G1E) compatible PWM controller.
|
||||
- "renesas,tpu-r8a7790": for R8A7790 (R-Car H2) compatible PWM controller.
|
||||
- "renesas,tpu": for generic R-Car and RZ/G1 TPU PWM controller.
|
||||
- "renesas,tpu-r8a77970": for R8A77970 (R-Car V3M) compatible PWM
|
||||
controller.
|
||||
- "renesas,tpu-r8a77980": for R8A77980 (R-Car V3H) compatible PWM
|
||||
controller.
|
||||
- "renesas,tpu": for the generic TPU PWM controller; this is a fallback for
|
||||
the entries listed above.
|
||||
|
||||
- reg: Base address and length of each memory resource used by the PWM
|
||||
controller hardware module.
|
||||
|
|
|
@ -126,6 +126,7 @@ int acpi_device_get_power(struct acpi_device *device, int *state)
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(acpi_device_get_power);
|
||||
|
||||
static int acpi_dev_pm_explicit_set(struct acpi_device *adev, int state)
|
||||
{
|
||||
|
|
|
@ -447,10 +447,9 @@ config PWM_TEGRA
|
|||
|
||||
config PWM_TIECAP
|
||||
tristate "ECAP PWM support"
|
||||
depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_KEYSTONE
|
||||
depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_KEYSTONE || ARCH_K3
|
||||
help
|
||||
PWM driver support for the ECAP APWM controller found on AM33XX
|
||||
TI SOC
|
||||
PWM driver support for the ECAP APWM controller found on TI SOCs
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called pwm-tiecap.
|
||||
|
|
|
@ -30,6 +30,7 @@ static const struct pwm_lpss_boardinfo pwm_lpss_bsw_info = {
|
|||
.clk_rate = 19200000,
|
||||
.npwm = 1,
|
||||
.base_unit_bits = 16,
|
||||
.other_devices_aml_touches_pwm_regs = true,
|
||||
};
|
||||
|
||||
/* Broxton */
|
||||
|
@ -60,6 +61,7 @@ static int pwm_lpss_probe_platform(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, lpwm);
|
||||
|
||||
dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_SMART_PREPARE);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
|
@ -74,13 +76,29 @@ static int pwm_lpss_remove_platform(struct platform_device *pdev)
|
|||
return pwm_lpss_remove(lpwm);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(pwm_lpss_platform_pm_ops,
|
||||
pwm_lpss_suspend,
|
||||
pwm_lpss_resume);
|
||||
static int pwm_lpss_prepare(struct device *dev)
|
||||
{
|
||||
struct pwm_lpss_chip *lpwm = dev_get_drvdata(dev);
|
||||
|
||||
/*
|
||||
* If other device's AML code touches the PWM regs on suspend/resume
|
||||
* force runtime-resume the PWM controller to allow this.
|
||||
*/
|
||||
if (lpwm->info->other_devices_aml_touches_pwm_regs)
|
||||
return 0; /* Force runtime-resume */
|
||||
|
||||
return 1; /* If runtime-suspended leave as is */
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops pwm_lpss_platform_pm_ops = {
|
||||
.prepare = pwm_lpss_prepare,
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pwm_lpss_suspend, pwm_lpss_resume)
|
||||
};
|
||||
|
||||
static const struct acpi_device_id pwm_lpss_acpi_match[] = {
|
||||
{ "80860F09", (unsigned long)&pwm_lpss_byt_info },
|
||||
{ "80862288", (unsigned long)&pwm_lpss_bsw_info },
|
||||
{ "80862289", (unsigned long)&pwm_lpss_bsw_info },
|
||||
{ "80865AC8", (unsigned long)&pwm_lpss_bxt_info },
|
||||
{ },
|
||||
};
|
||||
|
|
|
@ -32,15 +32,6 @@
|
|||
/* Size of each PWM register space if multiple */
|
||||
#define PWM_SIZE 0x400
|
||||
|
||||
#define MAX_PWMS 4
|
||||
|
||||
struct pwm_lpss_chip {
|
||||
struct pwm_chip chip;
|
||||
void __iomem *regs;
|
||||
const struct pwm_lpss_boardinfo *info;
|
||||
u32 saved_ctrl[MAX_PWMS];
|
||||
};
|
||||
|
||||
static inline struct pwm_lpss_chip *to_lpwm(struct pwm_chip *chip)
|
||||
{
|
||||
return container_of(chip, struct pwm_lpss_chip, chip);
|
||||
|
@ -97,7 +88,7 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm,
|
|||
unsigned long long on_time_div;
|
||||
unsigned long c = lpwm->info->clk_rate, base_unit_range;
|
||||
unsigned long long base_unit, freq = NSEC_PER_SEC;
|
||||
u32 ctrl;
|
||||
u32 orig_ctrl, ctrl;
|
||||
|
||||
do_div(freq, period_ns);
|
||||
|
||||
|
@ -114,13 +105,17 @@ static void pwm_lpss_prepare(struct pwm_lpss_chip *lpwm, struct pwm_device *pwm,
|
|||
do_div(on_time_div, period_ns);
|
||||
on_time_div = 255ULL - on_time_div;
|
||||
|
||||
ctrl = pwm_lpss_read(pwm);
|
||||
orig_ctrl = ctrl = pwm_lpss_read(pwm);
|
||||
ctrl &= ~PWM_ON_TIME_DIV_MASK;
|
||||
ctrl &= ~(base_unit_range << PWM_BASE_UNIT_SHIFT);
|
||||
base_unit &= base_unit_range;
|
||||
ctrl |= (u32) base_unit << PWM_BASE_UNIT_SHIFT;
|
||||
ctrl |= on_time_div;
|
||||
pwm_lpss_write(pwm, ctrl);
|
||||
|
||||
if (orig_ctrl != ctrl) {
|
||||
pwm_lpss_write(pwm, ctrl);
|
||||
pwm_lpss_write(pwm, ctrl | PWM_SW_UPDATE);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void pwm_lpss_cond_enable(struct pwm_device *pwm, bool cond)
|
||||
|
@ -144,7 +139,6 @@ static int pwm_lpss_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
return ret;
|
||||
}
|
||||
pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period);
|
||||
pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_SW_UPDATE);
|
||||
pwm_lpss_cond_enable(pwm, lpwm->info->bypass == false);
|
||||
ret = pwm_lpss_wait_for_update(pwm);
|
||||
if (ret) {
|
||||
|
@ -157,7 +151,6 @@ static int pwm_lpss_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
if (ret)
|
||||
return ret;
|
||||
pwm_lpss_prepare(lpwm, pwm, state->duty_cycle, state->period);
|
||||
pwm_lpss_write(pwm, pwm_lpss_read(pwm) | PWM_SW_UPDATE);
|
||||
return pwm_lpss_wait_for_update(pwm);
|
||||
}
|
||||
} else if (pwm_is_enabled(pwm)) {
|
||||
|
@ -168,8 +161,42 @@ static int pwm_lpss_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* This function gets called once from pwmchip_add to get the initial state */
|
||||
static void pwm_lpss_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
struct pwm_state *state)
|
||||
{
|
||||
struct pwm_lpss_chip *lpwm = to_lpwm(chip);
|
||||
unsigned long base_unit_range;
|
||||
unsigned long long base_unit, freq, on_time_div;
|
||||
u32 ctrl;
|
||||
|
||||
base_unit_range = BIT(lpwm->info->base_unit_bits);
|
||||
|
||||
ctrl = pwm_lpss_read(pwm);
|
||||
on_time_div = 255 - (ctrl & PWM_ON_TIME_DIV_MASK);
|
||||
base_unit = (ctrl >> PWM_BASE_UNIT_SHIFT) & (base_unit_range - 1);
|
||||
|
||||
freq = base_unit * lpwm->info->clk_rate;
|
||||
do_div(freq, base_unit_range);
|
||||
if (freq == 0)
|
||||
state->period = NSEC_PER_SEC;
|
||||
else
|
||||
state->period = NSEC_PER_SEC / (unsigned long)freq;
|
||||
|
||||
on_time_div *= state->period;
|
||||
do_div(on_time_div, 255);
|
||||
state->duty_cycle = on_time_div;
|
||||
|
||||
state->polarity = PWM_POLARITY_NORMAL;
|
||||
state->enabled = !!(ctrl & PWM_ENABLE);
|
||||
|
||||
if (state->enabled)
|
||||
pm_runtime_get(chip->dev);
|
||||
}
|
||||
|
||||
static const struct pwm_ops pwm_lpss_ops = {
|
||||
.apply = pwm_lpss_apply,
|
||||
.get_state = pwm_lpss_get_state,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
@ -214,6 +241,12 @@ EXPORT_SYMBOL_GPL(pwm_lpss_probe);
|
|||
|
||||
int pwm_lpss_remove(struct pwm_lpss_chip *lpwm)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < lpwm->info->npwm; i++) {
|
||||
if (pwm_is_enabled(&lpwm->chip.pwms[i]))
|
||||
pm_runtime_put(lpwm->chip.dev);
|
||||
}
|
||||
return pwmchip_remove(&lpwm->chip);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pwm_lpss_remove);
|
||||
|
|
|
@ -16,13 +16,25 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/pwm.h>
|
||||
|
||||
struct pwm_lpss_chip;
|
||||
#define MAX_PWMS 4
|
||||
|
||||
struct pwm_lpss_chip {
|
||||
struct pwm_chip chip;
|
||||
void __iomem *regs;
|
||||
const struct pwm_lpss_boardinfo *info;
|
||||
u32 saved_ctrl[MAX_PWMS];
|
||||
};
|
||||
|
||||
struct pwm_lpss_boardinfo {
|
||||
unsigned long clk_rate;
|
||||
unsigned int npwm;
|
||||
unsigned long base_unit_bits;
|
||||
bool bypass;
|
||||
/*
|
||||
* On some devices the _PS0/_PS3 AML code of the GPU (GFX0) device
|
||||
* messes with the PWM0 controllers state,
|
||||
*/
|
||||
bool other_devices_aml_touches_pwm_regs;
|
||||
};
|
||||
|
||||
struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r,
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* R-Car PWM Timer driver
|
||||
*
|
||||
* Copyright (C) 2015 Renesas Electronics Corporation
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* R-Mobile TPU PWM driver
|
||||
*
|
||||
* Copyright (C) 2012 Renesas Solutions Corp.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
|
|
|
@ -300,7 +300,6 @@ static const struct of_device_id tegra_pwm_of_match[] = {
|
|||
{ .compatible = "nvidia,tegra186-pwm", .data = &tegra186_pwm_soc },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, tegra_pwm_of_match);
|
||||
|
||||
static const struct dev_pm_ops tegra_pwm_pm_ops = {
|
||||
|
|
|
@ -249,6 +249,7 @@ static void pwm_export_release(struct device *child)
|
|||
static int pwm_export_child(struct device *parent, struct pwm_device *pwm)
|
||||
{
|
||||
struct pwm_export *export;
|
||||
char *pwm_prop[2];
|
||||
int ret;
|
||||
|
||||
if (test_and_set_bit(PWMF_EXPORTED, &pwm->flags))
|
||||
|
@ -263,7 +264,6 @@ static int pwm_export_child(struct device *parent, struct pwm_device *pwm)
|
|||
export->pwm = pwm;
|
||||
mutex_init(&export->lock);
|
||||
|
||||
export->child.class = parent->class;
|
||||
export->child.release = pwm_export_release;
|
||||
export->child.parent = parent;
|
||||
export->child.devt = MKDEV(0, 0);
|
||||
|
@ -277,6 +277,10 @@ static int pwm_export_child(struct device *parent, struct pwm_device *pwm)
|
|||
export = NULL;
|
||||
return ret;
|
||||
}
|
||||
pwm_prop[0] = kasprintf(GFP_KERNEL, "EXPORT=pwm%u", pwm->hwpwm);
|
||||
pwm_prop[1] = NULL;
|
||||
kobject_uevent_env(&parent->kobj, KOBJ_CHANGE, pwm_prop);
|
||||
kfree(pwm_prop[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -289,6 +293,7 @@ static int pwm_unexport_match(struct device *child, void *data)
|
|||
static int pwm_unexport_child(struct device *parent, struct pwm_device *pwm)
|
||||
{
|
||||
struct device *child;
|
||||
char *pwm_prop[2];
|
||||
|
||||
if (!test_and_clear_bit(PWMF_EXPORTED, &pwm->flags))
|
||||
return -ENODEV;
|
||||
|
@ -297,6 +302,11 @@ static int pwm_unexport_child(struct device *parent, struct pwm_device *pwm)
|
|||
if (!child)
|
||||
return -ENODEV;
|
||||
|
||||
pwm_prop[0] = kasprintf(GFP_KERNEL, "UNEXPORT=pwm%u", pwm->hwpwm);
|
||||
pwm_prop[1] = NULL;
|
||||
kobject_uevent_env(&parent->kobj, KOBJ_CHANGE, pwm_prop);
|
||||
kfree(pwm_prop[0]);
|
||||
|
||||
/* for device_find_child() */
|
||||
put_device(child);
|
||||
device_unregister(child);
|
||||
|
|
Loading…
Reference in a new issue