Skip to content

Commit 8f2c06b

Browse files
WhatAmISupposedToPutHeremarcan
authored andcommitted
pwm: Add Apple PWM controller
Adds the Apple PWM controller driver. Signed-off-by: Sasha Finkelstein <[email protected]>
1 parent 63d0303 commit 8f2c06b

3 files changed

Lines changed: 141 additions & 0 deletions

File tree

drivers/pwm/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,18 @@ config PWM_AB8500
5151
To compile this driver as a module, choose M here: the module
5252
will be called pwm-ab8500.
5353

54+
config PWM_APPLE
55+
tristate "Apple SoC PWM support"
56+
depends on ARCH_APPLE || COMPILE_TEST
57+
help
58+
Generic PWM framework driver for PWM controller present on
59+
Apple SoCs
60+
61+
Say Y here if you have an ARM Apple laptop, otherwise say N
62+
63+
To compile this driver as a module, choose M here: the module
64+
will be called pwm-apple.
65+
5466
config PWM_ATMEL
5567
tristate "Atmel PWM support"
5668
depends on ARCH_AT91 || COMPILE_TEST

drivers/pwm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
obj-$(CONFIG_PWM) += core.o
33
obj-$(CONFIG_PWM_SYSFS) += sysfs.o
44
obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o
5+
obj-$(CONFIG_PWM_APPLE) += pwm-apple.o
56
obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o
67
obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM) += pwm-atmel-hlcdc.o
78
obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o

drivers/pwm/pwm-apple.c

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// SPDX-License-Identifier: GPL-2.0 OR MIT
2+
/*
3+
* Driver for the Apple SoC PWM controller
4+
*
5+
* Copyright The Asahi Linux Contributors
6+
*/
7+
8+
#include <linux/module.h>
9+
#include <linux/of.h>
10+
#include <linux/of_device.h>
11+
#include <linux/platform_device.h>
12+
#include <linux/pwm.h>
13+
#include <linux/io.h>
14+
#include <linux/clk.h>
15+
#include <linux/pm_runtime.h>
16+
#include <linux/math64.h>
17+
18+
#define PWM_CONTROL 0x00
19+
#define PWM_ON_CYCLES 0x1c
20+
#define PWM_OFF_CYCLES 0x18
21+
22+
#define CTRL_ENABLE BIT(0)
23+
#define CTRL_MODE BIT(2)
24+
#define CTRL_UPDATE BIT(5)
25+
#define CTRL_TRIGGER BIT(9)
26+
#define CTRL_INVERT BIT(10)
27+
#define CTRL_OUTPUT_ENABLE BIT(14)
28+
29+
struct apple_pwm {
30+
struct pwm_chip chip;
31+
void __iomem *base;
32+
u64 clkrate;
33+
};
34+
35+
static int apple_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
36+
const struct pwm_state *state)
37+
{
38+
struct apple_pwm *fpwm;
39+
u64 on_cycles, off_cycles;
40+
41+
fpwm = container_of(chip, struct apple_pwm, chip);
42+
if (state->enabled) {
43+
on_cycles = mul_u64_u64_div_u64(fpwm->clkrate,
44+
state->duty_cycle, NSEC_PER_SEC);
45+
off_cycles = mul_u64_u64_div_u64(fpwm->clkrate,
46+
state->period, NSEC_PER_SEC) - on_cycles;
47+
writel(on_cycles, fpwm->base + PWM_ON_CYCLES);
48+
writel(off_cycles, fpwm->base + PWM_OFF_CYCLES);
49+
writel(CTRL_ENABLE | CTRL_OUTPUT_ENABLE | CTRL_UPDATE,
50+
fpwm->base + PWM_CONTROL);
51+
} else {
52+
writel(0, fpwm->base + PWM_CONTROL);
53+
}
54+
return 0;
55+
}
56+
57+
static int apple_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
58+
struct pwm_state *state)
59+
{
60+
struct apple_pwm *fpwm;
61+
u32 on_cycles, off_cycles, ctrl;
62+
63+
fpwm = container_of(chip, struct apple_pwm, chip);
64+
65+
ctrl = readl(fpwm->base + PWM_CONTROL);
66+
on_cycles = readl(fpwm->base + PWM_ON_CYCLES);
67+
off_cycles = readl(fpwm->base + PWM_OFF_CYCLES);
68+
69+
state->enabled = (ctrl & CTRL_ENABLE) && (ctrl & CTRL_OUTPUT_ENABLE);
70+
state->polarity = PWM_POLARITY_NORMAL;
71+
state->duty_cycle = div_u64(on_cycles, fpwm->clkrate) * NSEC_PER_SEC;
72+
state->period = div_u64(off_cycles + on_cycles, fpwm->clkrate) * NSEC_PER_SEC;
73+
74+
return 0;
75+
}
76+
77+
static const struct pwm_ops apple_pwm_ops = {
78+
.apply = apple_pwm_apply,
79+
.get_state = apple_pwm_get_state,
80+
.owner = THIS_MODULE,
81+
};
82+
83+
static int apple_pwm_probe(struct platform_device *pdev)
84+
{
85+
struct apple_pwm *pwm;
86+
struct clk *clk;
87+
int ret;
88+
89+
pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL);
90+
if (!pwm)
91+
return -ENOMEM;
92+
93+
pwm->base = devm_platform_ioremap_resource(pdev, 0);
94+
if (IS_ERR(pwm->base))
95+
return PTR_ERR(pwm->base);
96+
97+
platform_set_drvdata(pdev, pwm);
98+
99+
clk = devm_clk_get_enabled(&pdev->dev, NULL);
100+
if (IS_ERR(clk))
101+
return PTR_ERR(clk);
102+
103+
pwm->clkrate = clk_get_rate(clk);
104+
pwm->chip.dev = &pdev->dev;
105+
pwm->chip.npwm = 1;
106+
pwm->chip.ops = &apple_pwm_ops;
107+
108+
ret = devm_pwmchip_add(&pdev->dev, &pwm->chip);
109+
return ret;
110+
}
111+
112+
static const struct of_device_id apple_pwm_of_match[] = {
113+
{ .compatible = "apple,s5l-fpwm" },
114+
{}
115+
};
116+
MODULE_DEVICE_TABLE(of, apple_pwm_of_match);
117+
118+
static struct platform_driver apple_pwm_driver = {
119+
.probe = apple_pwm_probe,
120+
.driver = {
121+
.name = "apple-pwm",
122+
.of_match_table = apple_pwm_of_match,
123+
},
124+
};
125+
module_platform_driver(apple_pwm_driver);
126+
127+
MODULE_DESCRIPTION("Apple SoC PWM driver");
128+
MODULE_LICENSE("Dual MIT/GPL");

0 commit comments

Comments
 (0)