Skip to content

Commit e5a06dc

Browse files
btanghethierryreding
authored andcommitted
pwm: Add BCM2835 PWM driver
Add PWM driver for Broadcom BCM2835 processor (Raspberry Pi) Signed-off-by: Bart Tanghe <[email protected]> Acked-by: Stephen Warren <[email protected]> Signed-off-by: Thierry Reding <[email protected]>
1 parent cac7f24 commit e5a06dc

File tree

4 files changed

+245
-0
lines changed

4 files changed

+245
-0
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
BCM2835 PWM controller (Raspberry Pi controller)
2+
3+
Required properties:
4+
- compatible: should be "brcm,bcm2835-pwm"
5+
- reg: physical base address and length of the controller's registers
6+
- clock: This clock defines the base clock frequency of the PWM hardware
7+
system, the period and the duty_cycle of the PWM signal is a multiple of
8+
the base period.
9+
- #pwm-cells: Should be 2. See pwm.txt in this directory for a description of
10+
the cells format.
11+
12+
Examples:
13+
14+
pwm@2020c000 {
15+
compatible = "brcm,bcm2835-pwm";
16+
reg = <0x2020c000 0x28>;
17+
clocks = <&clk_pwm>;
18+
#pwm-cells = <2>;
19+
};
20+
21+
clocks {
22+
....
23+
clk_pwm: pwm {
24+
compatible = "fixed-clock";
25+
reg = <3>;
26+
#clock-cells = <0>;
27+
clock-frequency = <9200000>;
28+
};
29+
....
30+
};

drivers/pwm/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,15 @@ config PWM_BCM_KONA
7171
To compile this driver as a module, choose M here: the module
7272
will be called pwm-bcm-kona.
7373

74+
config PWM_BCM2835
75+
tristate "BCM2835 PWM support"
76+
depends on ARCH_BCM2835
77+
help
78+
PWM framework driver for BCM2835 controller (Raspberry Pi)
79+
80+
To compile this driver as a module, choose M here: the module
81+
will be called pwm-bcm2835.
82+
7483
config PWM_BFIN
7584
tristate "Blackfin PWM support"
7685
depends on BFIN_GPTIMERS

drivers/pwm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o
44
obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o
55
obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o
66
obj-$(CONFIG_PWM_BCM_KONA) += pwm-bcm-kona.o
7+
obj-$(CONFIG_PWM_BCM2835) += pwm-bcm2835.o
78
obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o
89
obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o
910
obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o

drivers/pwm/pwm-bcm2835.c

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
/*
2+
* Copyright 2014 Bart Tanghe <[email protected]>
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; version 2.
7+
*/
8+
9+
#include <linux/clk.h>
10+
#include <linux/err.h>
11+
#include <linux/io.h>
12+
#include <linux/module.h>
13+
#include <linux/of.h>
14+
#include <linux/platform_device.h>
15+
#include <linux/pwm.h>
16+
17+
#define PWM_CONTROL 0x000
18+
#define PWM_CONTROL_SHIFT(x) ((x) * 8)
19+
#define PWM_CONTROL_MASK 0xff
20+
#define PWM_MODE 0x80 /* set timer in PWM mode */
21+
#define PWM_ENABLE (1 << 0)
22+
#define PWM_POLARITY (1 << 4)
23+
24+
#define PERIOD(x) (((x) * 0x10) + 0x10)
25+
#define DUTY(x) (((x) * 0x10) + 0x14)
26+
27+
#define MIN_PERIOD 108 /* 9.2 MHz max. PWM clock */
28+
29+
struct bcm2835_pwm {
30+
struct pwm_chip chip;
31+
struct device *dev;
32+
unsigned long scaler;
33+
void __iomem *base;
34+
struct clk *clk;
35+
};
36+
37+
static inline struct bcm2835_pwm *to_bcm2835_pwm(struct pwm_chip *chip)
38+
{
39+
return container_of(chip, struct bcm2835_pwm, chip);
40+
}
41+
42+
static int bcm2835_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
43+
{
44+
struct bcm2835_pwm *pc = to_bcm2835_pwm(chip);
45+
u32 value;
46+
47+
value = readl(pc->base + PWM_CONTROL);
48+
value &= ~(PWM_CONTROL_MASK << PWM_CONTROL_SHIFT(pwm->hwpwm));
49+
value |= (PWM_MODE << PWM_CONTROL_SHIFT(pwm->hwpwm));
50+
writel(value, pc->base + PWM_CONTROL);
51+
52+
return 0;
53+
}
54+
55+
static void bcm2835_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
56+
{
57+
struct bcm2835_pwm *pc = to_bcm2835_pwm(chip);
58+
u32 value;
59+
60+
value = readl(pc->base + PWM_CONTROL);
61+
value &= ~(PWM_CONTROL_MASK << PWM_CONTROL_SHIFT(pwm->hwpwm));
62+
writel(value, pc->base + PWM_CONTROL);
63+
}
64+
65+
static int bcm2835_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
66+
int duty_ns, int period_ns)
67+
{
68+
struct bcm2835_pwm *pc = to_bcm2835_pwm(chip);
69+
70+
if (period_ns <= MIN_PERIOD) {
71+
dev_err(pc->dev, "period %d not supported, minimum %d\n",
72+
period_ns, MIN_PERIOD);
73+
return -EINVAL;
74+
}
75+
76+
writel(duty_ns / pc->scaler, pc->base + DUTY(pwm->hwpwm));
77+
writel(period_ns / pc->scaler, pc->base + PERIOD(pwm->hwpwm));
78+
79+
return 0;
80+
}
81+
82+
static int bcm2835_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
83+
{
84+
struct bcm2835_pwm *pc = to_bcm2835_pwm(chip);
85+
u32 value;
86+
87+
value = readl(pc->base + PWM_CONTROL);
88+
value |= PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm);
89+
writel(value, pc->base + PWM_CONTROL);
90+
91+
return 0;
92+
}
93+
94+
static void bcm2835_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
95+
{
96+
struct bcm2835_pwm *pc = to_bcm2835_pwm(chip);
97+
u32 value;
98+
99+
value = readl(pc->base + PWM_CONTROL);
100+
value &= ~(PWM_ENABLE << PWM_CONTROL_SHIFT(pwm->hwpwm));
101+
writel(value, pc->base + PWM_CONTROL);
102+
}
103+
104+
static int bcm2835_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm,
105+
enum pwm_polarity polarity)
106+
{
107+
struct bcm2835_pwm *pc = to_bcm2835_pwm(chip);
108+
u32 value;
109+
110+
value = readl(pc->base + PWM_CONTROL);
111+
112+
if (polarity == PWM_POLARITY_NORMAL)
113+
value &= ~(PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm));
114+
else
115+
value |= PWM_POLARITY << PWM_CONTROL_SHIFT(pwm->hwpwm);
116+
117+
writel(value, pc->base + PWM_CONTROL);
118+
119+
return 0;
120+
}
121+
122+
static const struct pwm_ops bcm2835_pwm_ops = {
123+
.request = bcm2835_pwm_request,
124+
.free = bcm2835_pwm_free,
125+
.config = bcm2835_pwm_config,
126+
.enable = bcm2835_pwm_enable,
127+
.disable = bcm2835_pwm_disable,
128+
.set_polarity = bcm2835_set_polarity,
129+
.owner = THIS_MODULE,
130+
};
131+
132+
static int bcm2835_pwm_probe(struct platform_device *pdev)
133+
{
134+
struct bcm2835_pwm *pc;
135+
struct resource *res;
136+
int ret;
137+
138+
pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
139+
if (!pc)
140+
return -ENOMEM;
141+
142+
pc->dev = &pdev->dev;
143+
144+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
145+
pc->base = devm_ioremap_resource(&pdev->dev, res);
146+
if (IS_ERR(pc->base))
147+
return PTR_ERR(pc->base);
148+
149+
pc->clk = devm_clk_get(&pdev->dev, NULL);
150+
if (IS_ERR(pc->clk)) {
151+
dev_err(&pdev->dev, "clock not found: %ld\n", PTR_ERR(pc->clk));
152+
return PTR_ERR(pc->clk);
153+
}
154+
155+
ret = clk_prepare_enable(pc->clk);
156+
if (ret)
157+
return ret;
158+
159+
pc->scaler = NSEC_PER_SEC / clk_get_rate(pc->clk);
160+
161+
pc->chip.dev = &pdev->dev;
162+
pc->chip.ops = &bcm2835_pwm_ops;
163+
pc->chip.npwm = 2;
164+
165+
platform_set_drvdata(pdev, pc);
166+
167+
ret = pwmchip_add(&pc->chip);
168+
if (ret < 0)
169+
goto add_fail;
170+
171+
return 0;
172+
173+
add_fail:
174+
clk_disable_unprepare(pc->clk);
175+
return ret;
176+
}
177+
178+
static int bcm2835_pwm_remove(struct platform_device *pdev)
179+
{
180+
struct bcm2835_pwm *pc = platform_get_drvdata(pdev);
181+
182+
clk_disable_unprepare(pc->clk);
183+
184+
return pwmchip_remove(&pc->chip);
185+
}
186+
187+
static const struct of_device_id bcm2835_pwm_of_match[] = {
188+
{ .compatible = "brcm,bcm2835-pwm", },
189+
{ /* sentinel */ }
190+
};
191+
MODULE_DEVICE_TABLE(of, bcm2835_pwm_of_match);
192+
193+
static struct platform_driver bcm2835_pwm_driver = {
194+
.driver = {
195+
.name = "bcm2835-pwm",
196+
.of_match_table = bcm2835_pwm_of_match,
197+
},
198+
.probe = bcm2835_pwm_probe,
199+
.remove = bcm2835_pwm_remove,
200+
};
201+
module_platform_driver(bcm2835_pwm_driver);
202+
203+
MODULE_AUTHOR("Bart Tanghe <[email protected]");
204+
MODULE_DESCRIPTION("Broadcom BCM2835 PWM driver");
205+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)