Skip to content

Commit

Permalink
Merge tag 'pwm/for-6.3-rc1' of git://git.kernel.org/pub/scm/linux/ker…
Browse files Browse the repository at this point in the history
…nel/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
  • Loading branch information
Linus Torvalds committed Feb 28, 2023
2 parents b07ce43 + cf70d01 commit e492250
Show file tree
Hide file tree
Showing 10 changed files with 302 additions and 90 deletions.
93 changes: 93 additions & 0 deletions Documentation/devicetree/bindings/pwm/mediatek,mt2712-pwm.yaml
Original file line number Diff line number Diff line change
@@ -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";
};
52 changes: 0 additions & 52 deletions Documentation/devicetree/bindings/pwm/pwm-mediatek.txt

This file was deleted.

68 changes: 68 additions & 0 deletions Documentation/devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.yaml
Original file line number Diff line number Diff line change
@@ -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";
};
112 changes: 103 additions & 9 deletions drivers/pwm/pwm-ab8500.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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,
};

Expand Down
Loading

0 comments on commit e492250

Please sign in to comment.