pwm: Changes for v6.3-rc1

This rather small set of changes includes some minor fixes and
 improvements. The AB8500 driver gained support for reading the initial
 hardware state and the Synopsys DesignWare driver received some work to
 prepare for device tree and platform support.
 -----BEGIN PGP SIGNATURE-----
 
 iQJNBAABCAA3FiEEiOrDCAFJzPfAjcif3SOs138+s6EFAmP+DlQZHHRoaWVycnku
 cmVkaW5nQGdtYWlsLmNvbQAKCRDdI6zXfz6zoaNvD/46MIgjveZf/+SltL1MF5Th
 RcudsTk4U5k2MHwVp7UV6UAKq45uu6b88CRWkfKCoN0V9c+JaItMurSI1VPRJf5L
 9rW9axRd3EUSfPqu8WuqpcTocjhILuWIHFr2feGdlRGeEbUmBc1Jsj89MTePmp3n
 BNMIU9QU3or31VAE99GRb0ALrPkTW4fnHqH7dHEUxfrQ2Aaa7CAy5TuWSsS9nWDt
 NaP1MQHHkSr4viUjmDVzcD6trK4C3nDXhtU9GjzHRGuA8FVk0PvsE8JuSpiNmCUj
 1oibqZNwBeVn+BWPabbGQmkWHmBp5GkKtXohcFiibqivIUOx469dgjzHwIA71i8w
 wA3AnIm9Mga9sCDON0d6XzR3yjl6/ZC4igHtUxY6PTy4Gh3sKcXxlu6GSj7SZA4N
 xOCykGxOaGYoiPFtVNWwTfkTApYezp+gi5xLzxk+InGCHFtJpaMwRu+PsBwL311K
 GTxrr4NR49W/4ogE8/epeunh8flmzBSA8cWQJiKRD0q/Xtl0AnrVPDCN1wZHTqqm
 LFivsq13vn0yDNnfwc12whhA1DaG08Mesec4QSemhAMlErs1Oqud77wdBIa9oK5w
 iPe3ienzvvow7Y8Ek4fqBFYY8ACHoyoMt5aINe8ZV7DYXE79HInY/q9RKSvYT+0x
 X4V5ihcowNXt6Kl67PsNBA==
 =PQhV
 -----END PGP SIGNATURE-----

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

Pull pwm updates from Thierry Reding:
 "This rather small set of changes includes some minor fixes and
  improvements.

  The AB8500 driver gained support for reading the initial hardware
  state and the Synopsys DesignWare driver received some work to prepare
  for device tree and platform support"

* tag 'pwm/for-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm:
  pwm: dwc: Use devm_pwmchip_add()
  pwm: dwc: Move memory allocation to own function
  pwm: dwc: Change &pci->dev to dev in probe
  dt-bindings: pwm: Document Synopsys DesignWare snps,pwm-dw-apb-timers-pwm2
  pwm: iqs620a: Replace one remaining instance of regmap_update_bits()
  pwm: ab8500: Implement .get_state()
  pwm: ab8500: Fix calculation of duty and period
  pwm: lp3943: Drop unused i2c include
  dt-bindings: pwm: mediatek: Convert pwm-mediatek to DT schema
  pwm: stm32-lp: fix the check on arr and cmp registers update
  pwm: Move pwm_capture() dummy to restore order
  pwm: sifive: Always let the first pwm_apply_state succeed
This commit is contained in:
Linus Torvalds 2023-02-28 09:12:47 -08:00
commit e492250d52
10 changed files with 302 additions and 90 deletions

View File

@ -0,0 +1,93 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/pwm/mediatek,mt2712-pwm.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MediaTek PWM Controller
maintainers:
- John Crispin <john@phrozen.org>
allOf:
- $ref: pwm.yaml#
properties:
compatible:
oneOf:
- enum:
- mediatek,mt2712-pwm
- mediatek,mt6795-pwm
- mediatek,mt7622-pwm
- mediatek,mt7623-pwm
- mediatek,mt7628-pwm
- mediatek,mt7629-pwm
- mediatek,mt8183-pwm
- mediatek,mt8365-pwm
- mediatek,mt8516-pwm
- items:
- enum:
- mediatek,mt8195-pwm
- const: mediatek,mt8183-pwm
reg:
maxItems: 1
"#pwm-cells":
const: 2
interrupts:
maxItems: 1
clocks:
minItems: 2
maxItems: 10
clock-names:
description:
This controller needs two input clocks for its core and one
clock for each PWM output.
minItems: 2
items:
- const: top
- const: main
- const: pwm1
- const: pwm2
- const: pwm3
- const: pwm4
- const: pwm5
- const: pwm6
- const: pwm7
- const: pwm8
required:
- compatible
- reg
- "#pwm-cells"
- clocks
- clock-names
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/mt2712-clk.h>
#include <dt-bindings/interrupt-controller/irq.h>
pwm0: pwm@11006000 {
compatible = "mediatek,mt2712-pwm";
reg = <0x11006000 0x1000>;
#pwm-cells = <2>;
interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_LOW>;
clocks = <&topckgen CLK_TOP_PWM_SEL>, <&pericfg CLK_PERI_PWM>,
<&pericfg CLK_PERI_PWM0>, <&pericfg CLK_PERI_PWM1>,
<&pericfg CLK_PERI_PWM2>, <&pericfg CLK_PERI_PWM3>,
<&pericfg CLK_PERI_PWM4>, <&pericfg CLK_PERI_PWM5>,
<&pericfg CLK_PERI_PWM6>, <&pericfg CLK_PERI_PWM7>;
clock-names = "top", "main",
"pwm1", "pwm2",
"pwm3", "pwm4",
"pwm5", "pwm6",
"pwm7", "pwm8";
};

View File

@ -1,52 +0,0 @@
MediaTek PWM controller
Required properties:
- compatible: should be "mediatek,<name>-pwm":
- "mediatek,mt2712-pwm": found on mt2712 SoC.
- "mediatek,mt6795-pwm": found on mt6795 SoC.
- "mediatek,mt7622-pwm": found on mt7622 SoC.
- "mediatek,mt7623-pwm": found on mt7623 SoC.
- "mediatek,mt7628-pwm": found on mt7628 SoC.
- "mediatek,mt7629-pwm": found on mt7629 SoC.
- "mediatek,mt8183-pwm": found on mt8183 SoC.
- "mediatek,mt8195-pwm", "mediatek,mt8183-pwm": found on mt8195 SoC.
- "mediatek,mt8365-pwm": found on mt8365 SoC.
- "mediatek,mt8516-pwm": found on mt8516 SoC.
- reg: physical base address and length of the controller's registers.
- #pwm-cells: must be 2. See pwm.yaml in this directory for a description of
the cell format.
- clocks: phandle and clock specifier of the PWM reference clock.
- clock-names: must contain the following, except for MT7628 which
has no clocks
- "top": the top clock generator
- "main": clock used by the PWM core
- "pwm1-3": the three per PWM clocks for mt8365
- "pwm1-8": the eight per PWM clocks for mt2712
- "pwm1-6": the six per PWM clocks for mt7622
- "pwm1-5": the five per PWM clocks for mt7623
- "pwm1" : the PWM1 clock for mt7629
- pinctrl-names: Must contain a "default" entry.
- pinctrl-0: One property must exist for each entry in pinctrl-names.
See pinctrl/pinctrl-bindings.txt for details of the property values.
Optional properties:
- assigned-clocks: Reference to the PWM clock entries.
- assigned-clock-parents: The phandle of the parent clock of PWM clock.
Example:
pwm0: pwm@11006000 {
compatible = "mediatek,mt7623-pwm";
reg = <0 0x11006000 0 0x1000>;
#pwm-cells = <2>;
clocks = <&topckgen CLK_TOP_PWM_SEL>,
<&pericfg CLK_PERI_PWM>,
<&pericfg CLK_PERI_PWM1>,
<&pericfg CLK_PERI_PWM2>,
<&pericfg CLK_PERI_PWM3>,
<&pericfg CLK_PERI_PWM4>,
<&pericfg CLK_PERI_PWM5>;
clock-names = "top", "main", "pwm1", "pwm2",
"pwm3", "pwm4", "pwm5";
pinctrl-names = "default";
pinctrl-0 = <&pwm0_pins>;
};

View File

@ -0,0 +1,68 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright (C) 2022 SiFive, Inc.
%YAML 1.2
---
$id: http://devicetree.org/schemas/pwm/snps,dw-apb-timers-pwm2.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Synopsys DW-APB timers PWM controller
maintainers:
- Ben Dooks <ben.dooks@sifive.com>
description:
This describes the DesignWare APB timers module when used in the PWM
mode. The IP core can be generated with various options which can
control the functionality, the number of PWMs available and other
internal controls the designer requires.
The IP block has a version register so this can be used for detection
instead of having to encode the IP version number in the device tree
comaptible.
allOf:
- $ref: pwm.yaml#
properties:
compatible:
const: snps,dw-apb-timers-pwm2
reg:
maxItems: 1
"#pwm-cells":
const: 3
clocks:
items:
- description: Interface bus clock
- description: PWM reference clock
clock-names:
items:
- const: bus
- const: timer
snps,pwm-number:
$ref: /schemas/types.yaml#/definitions/uint32
description: The number of PWM channels configured for this instance
enum: [1, 2, 3, 4, 5, 6, 7, 8]
required:
- compatible
- reg
- "#pwm-cells"
- clocks
- clock-names
additionalProperties: false
examples:
- |
pwm: pwm@180000 {
compatible = "snps,dw-apb-timers-pwm2";
reg = <0x180000 0x200>;
#pwm-cells = <3>;
clocks = <&bus>, <&timer>;
clock-names = "bus", "timer";
};

View File

@ -3,6 +3,7 @@
* Copyright (C) ST-Ericsson SA 2010
*
* Author: Arun R Murthy <arun.murthy@stericsson.com>
* Datasheet: https://web.archive.org/web/20130614115108/http://www.stericsson.com/developers/CD00291561_UM1031_AB8500_user_manual-rev5_CTDS_public.pdf
*/
#include <linux/err.h>
#include <linux/platform_device.h>
@ -20,6 +21,8 @@
#define AB8500_PWM_OUT_CTRL2_REG 0x61
#define AB8500_PWM_OUT_CTRL7_REG 0x66
#define AB8500_PWM_CLKRATE 9600000
struct ab8500_pwm_chip {
struct pwm_chip chip;
unsigned int hwid;
@ -35,13 +38,60 @@ static int ab8500_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
{
int ret;
u8 reg;
unsigned int higher_val, lower_val;
u8 higher_val, lower_val;
unsigned int duty_steps, div;
struct ab8500_pwm_chip *ab8500 = ab8500_pwm_from_chip(chip);
if (state->polarity != PWM_POLARITY_NORMAL)
return -EINVAL;
if (!state->enabled) {
if (state->enabled) {
/*
* A time quantum is
* q = (32 - FreqPWMOutx[3:0]) / AB8500_PWM_CLKRATE
* The period is always 1024 q, duty_cycle is between 1q and 1024q.
*
* FreqPWMOutx[3:0] | output frequency | output frequency | 1024q = period
* | (from manual) | (1 / 1024q) | = 1 / freq
* -----------------+------------------+------------------+--------------
* b0000 | 293 Hz | 292.968750 Hz | 3413333.33 ns
* b0001 | 302 Hz | 302.419355 Hz | 3306666.66 ns
* b0010 | 312 Hz | 312.500000 Hz | 3200000 ns
* b0011 | 323 Hz | 323.275862 Hz | 3093333.33 ns
* b0100 | 334 Hz | 334.821429 Hz | 2986666.66 ns
* b0101 | 347 Hz | 347.222222 Hz | 2880000 ns
* b0110 | 360 Hz | 360.576923 Hz | 2773333.33 ns
* b0111 | 375 Hz | 375.000000 Hz | 2666666.66 ns
* b1000 | 390 Hz | 390.625000 Hz | 2560000 ns
* b1001 | 407 Hz | 407.608696 Hz | 2453333.33 ns
* b1010 | 426 Hz | 426.136364 Hz | 2346666.66 ns
* b1011 | 446 Hz | 446.428571 Hz | 2240000 ns
* b1100 | 468 Hz | 468.750000 Hz | 2133333.33 ns
* b1101 | 493 Hz | 493.421053 Hz | 2026666.66 ns
* b1110 | 520 Hz | 520.833333 Hz | 1920000 ns
* b1111 | 551 Hz | 551.470588 Hz | 1813333.33 ns
*
*
* AB8500_PWM_CLKRATE is a multiple of 1024, so the division by
* 1024 can be done in this factor without loss of precision.
*/
div = min_t(u64, mul_u64_u64_div_u64(state->period,
AB8500_PWM_CLKRATE >> 10,
NSEC_PER_SEC), 32); /* 32 - FreqPWMOutx[3:0] */
if (div <= 16)
/* requested period < 3413333.33 */
return -EINVAL;
duty_steps = max_t(u64, mul_u64_u64_div_u64(state->duty_cycle,
AB8500_PWM_CLKRATE,
(u64)NSEC_PER_SEC * div), 1024);
}
/*
* The hardware doesn't support duty_steps = 0 explicitly, but emits low
* when disabled.
*/
if (!state->enabled || duty_steps == 0) {
ret = abx500_mask_and_set_register_interruptible(chip->dev,
AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
1 << ab8500->hwid, 0);
@ -53,28 +103,29 @@ static int ab8500_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
}
/*
* get the first 8 bits that are be written to
* The lower 8 bits of duty_steps is written to ...
* AB8500_PWM_OUT_CTRL1_REG[0:7]
*/
lower_val = state->duty_cycle & 0x00FF;
lower_val = (duty_steps - 1) & 0x00ff;
/*
* get bits [9:10] that are to be written to
* AB8500_PWM_OUT_CTRL2_REG[0:1]
* The two remaining high bits to
* AB8500_PWM_OUT_CTRL2_REG[0:1]; together with FreqPWMOutx.
*/
higher_val = ((state->duty_cycle & 0x0300) >> 8);
higher_val = ((duty_steps - 1) & 0x0300) >> 8 | (32 - div) << 4;
reg = AB8500_PWM_OUT_CTRL1_REG + (ab8500->hwid * 2);
ret = abx500_set_register_interruptible(chip->dev, AB8500_MISC,
reg, (u8)lower_val);
reg, lower_val);
if (ret < 0)
return ret;
ret = abx500_set_register_interruptible(chip->dev, AB8500_MISC,
(reg + 1), (u8)higher_val);
(reg + 1), higher_val);
if (ret < 0)
return ret;
/* enable */
ret = abx500_mask_and_set_register_interruptible(chip->dev,
AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
1 << ab8500->hwid, 1 << ab8500->hwid);
@ -85,8 +136,51 @@ static int ab8500_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
return ret;
}
static int ab8500_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
struct pwm_state *state)
{
u8 ctrl7, lower_val, higher_val;
int ret;
struct ab8500_pwm_chip *ab8500 = ab8500_pwm_from_chip(chip);
unsigned int div, duty_steps;
ret = abx500_get_register_interruptible(chip->dev, AB8500_MISC,
AB8500_PWM_OUT_CTRL7_REG,
&ctrl7);
if (ret)
return ret;
state->polarity = PWM_POLARITY_NORMAL;
if (!(ctrl7 & 1 << ab8500->hwid)) {
state->enabled = false;
return 0;
}
ret = abx500_get_register_interruptible(chip->dev, AB8500_MISC,
AB8500_PWM_OUT_CTRL1_REG + (ab8500->hwid * 2),
&lower_val);
if (ret)
return ret;
ret = abx500_get_register_interruptible(chip->dev, AB8500_MISC,
AB8500_PWM_OUT_CTRL2_REG + (ab8500->hwid * 2),
&higher_val);
if (ret)
return ret;
div = 32 - ((higher_val & 0xf0) >> 4);
duty_steps = ((higher_val & 3) << 8 | lower_val) + 1;
state->period = DIV64_U64_ROUND_UP((u64)div << 10, AB8500_PWM_CLKRATE);
state->duty_cycle = DIV64_U64_ROUND_UP((u64)div * duty_steps, AB8500_PWM_CLKRATE);
return 0;
}
static const struct pwm_ops ab8500_pwm_ops = {
.apply = ab8500_pwm_apply,
.get_state = ab8500_pwm_get_state,
.owner = THIS_MODULE,
};

View File

@ -198,20 +198,35 @@ static const struct pwm_ops dwc_pwm_ops = {
.owner = THIS_MODULE,
};
static struct dwc_pwm *dwc_pwm_alloc(struct device *dev)
{
struct dwc_pwm *dwc;
dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL);
if (!dwc)
return NULL;
dwc->chip.dev = dev;
dwc->chip.ops = &dwc_pwm_ops;
dwc->chip.npwm = DWC_TIMERS_TOTAL;
dev_set_drvdata(dev, dwc);
return dwc;
}
static int dwc_pwm_probe(struct pci_dev *pci, const struct pci_device_id *id)
{
struct device *dev = &pci->dev;
struct dwc_pwm *dwc;
int ret;
dwc = devm_kzalloc(&pci->dev, sizeof(*dwc), GFP_KERNEL);
dwc = dwc_pwm_alloc(dev);
if (!dwc)
return -ENOMEM;
ret = pcim_enable_device(pci);
if (ret) {
dev_err(&pci->dev,
"Failed to enable device (%pe)\n", ERR_PTR(ret));
dev_err(dev, "Failed to enable device (%pe)\n", ERR_PTR(ret));
return ret;
}
@ -219,24 +234,17 @@ static int dwc_pwm_probe(struct pci_dev *pci, const struct pci_device_id *id)
ret = pcim_iomap_regions(pci, BIT(0), pci_name(pci));
if (ret) {
dev_err(&pci->dev,
"Failed to iomap PCI BAR (%pe)\n", ERR_PTR(ret));
dev_err(dev, "Failed to iomap PCI BAR (%pe)\n", ERR_PTR(ret));
return ret;
}
dwc->base = pcim_iomap_table(pci)[0];
if (!dwc->base) {
dev_err(&pci->dev, "Base address missing\n");
dev_err(dev, "Base address missing\n");
return -ENOMEM;
}
pci_set_drvdata(pci, dwc);
dwc->chip.dev = dev;
dwc->chip.ops = &dwc_pwm_ops;
dwc->chip.npwm = DWC_TIMERS_TOTAL;
ret = pwmchip_add(&dwc->chip);
ret = devm_pwmchip_add(dev, &dwc->chip);
if (ret)
return ret;
@ -248,12 +256,8 @@ static int dwc_pwm_probe(struct pci_dev *pci, const struct pci_device_id *id)
static void dwc_pwm_remove(struct pci_dev *pci)
{
struct dwc_pwm *dwc = pci_get_drvdata(pci);
pm_runtime_forbid(&pci->dev);
pm_runtime_get_noresume(&pci->dev);
pwmchip_remove(&dwc->chip);
}
#ifdef CONFIG_PM_SLEEP

View File

@ -55,8 +55,8 @@ static int iqs620_pwm_init(struct iqs620_pwm_private *iqs620_pwm,
if (ret)
return ret;
return regmap_update_bits(iqs62x->regmap, IQS620_PWR_SETTINGS,
IQS620_PWR_SETTINGS_PWM_OUT, 0xff);
return regmap_set_bits(iqs62x->regmap, IQS620_PWR_SETTINGS,
IQS620_PWR_SETTINGS_PWM_OUT);
}
static int iqs620_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,

View File

@ -8,7 +8,6 @@
*/
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/mfd/lp3943.h>
#include <linux/module.h>
#include <linux/platform_device.h>

View File

@ -161,7 +161,13 @@ static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
mutex_lock(&ddata->lock);
if (state->period != ddata->approx_period) {
if (ddata->user_count != 1) {
/*
* Don't let a 2nd user change the period underneath the 1st user.
* However if ddate->approx_period == 0 this is the first time we set
* any period, so let whoever gets here first set the period so other
* users who agree on the period won't fail.
*/
if (ddata->user_count != 1 && ddata->approx_period) {
mutex_unlock(&ddata->lock);
return -EBUSY;
}

View File

@ -127,7 +127,7 @@ static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm,
/* ensure CMP & ARR registers are properly written */
ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,
(val & STM32_LPTIM_CMPOK_ARROK),
(val & STM32_LPTIM_CMPOK_ARROK) == STM32_LPTIM_CMPOK_ARROK,
100, 1000);
if (ret) {
dev_err(priv->chip.dev, "ARR/CMP registers write issue\n");

View File

@ -440,13 +440,6 @@ static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
return -EINVAL;
}
static inline int pwm_capture(struct pwm_device *pwm,
struct pwm_capture *result,
unsigned long timeout)
{
return -EINVAL;
}
static inline int pwm_enable(struct pwm_device *pwm)
{
might_sleep();
@ -458,6 +451,13 @@ static inline void pwm_disable(struct pwm_device *pwm)
might_sleep();
}
static inline int pwm_capture(struct pwm_device *pwm,
struct pwm_capture *result,
unsigned long timeout)
{
return -EINVAL;
}
static inline int pwm_set_chip_data(struct pwm_device *pwm, void *data)
{
return -EINVAL;