diff options
author | Bart Tanghe <bart.tanghe@thomasmore.be> | 2014-10-08 06:14:32 -0400 |
---|---|---|
committer | Thierry Reding <thierry.reding@gmail.com> | 2014-11-17 06:20:13 -0500 |
commit | e5a06dc5ac1f686d11b11488a88a63ab12e079cb (patch) | |
tree | 3a6c350a173bb47522c1ea15b553951392d04dea | |
parent | cac7f2429872d3733dc3f9915857b1691da2eb2f (diff) |
pwm: Add BCM2835 PWM driver
Add PWM driver for Broadcom BCM2835 processor (Raspberry Pi)
Signed-off-by: Bart Tanghe <bart.tanghe@thomasmore.be>
Acked-by: Stephen Warren <swarren@wwwdotorg.org>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
-rw-r--r-- | Documentation/devicetree/bindings/pwm/pwm-bcm2835.txt | 30 | ||||
-rw-r--r-- | drivers/pwm/Kconfig | 9 | ||||
-rw-r--r-- | drivers/pwm/Makefile | 1 | ||||
-rw-r--r-- | drivers/pwm/pwm-bcm2835.c | 205 |
4 files changed, 245 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/pwm/pwm-bcm2835.txt b/Documentation/devicetree/bindings/pwm/pwm-bcm2835.txt new file mode 100644 index 000000000000..fb6fb31bc4c4 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-bcm2835.txt | |||
@@ -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 | }; | ||
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index ef2dd2e4754b..ddabe3983549 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig | |||
@@ -71,6 +71,15 @@ config PWM_BCM_KONA | |||
71 | To compile this driver as a module, choose M here: the module | 71 | To compile this driver as a module, choose M here: the module |
72 | will be called pwm-bcm-kona. | 72 | will be called pwm-bcm-kona. |
73 | 73 | ||
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 | |||
74 | config PWM_BFIN | 83 | config PWM_BFIN |
75 | tristate "Blackfin PWM support" | 84 | tristate "Blackfin PWM support" |
76 | depends on BFIN_GPTIMERS | 85 | depends on BFIN_GPTIMERS |
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index c458606c3755..88be33bbfdf6 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile | |||
@@ -4,6 +4,7 @@ obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o | |||
4 | obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o | 4 | obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o |
5 | obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o | 5 | obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o |
6 | obj-$(CONFIG_PWM_BCM_KONA) += pwm-bcm-kona.o | 6 | obj-$(CONFIG_PWM_BCM_KONA) += pwm-bcm-kona.o |
7 | obj-$(CONFIG_PWM_BCM2835) += pwm-bcm2835.o | ||
7 | obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o | 8 | obj-$(CONFIG_PWM_BFIN) += pwm-bfin.o |
8 | obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o | 9 | obj-$(CONFIG_PWM_CLPS711X) += pwm-clps711x.o |
9 | obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o | 10 | obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o |
diff --git a/drivers/pwm/pwm-bcm2835.c b/drivers/pwm/pwm-bcm2835.c new file mode 100644 index 000000000000..b4c7f956b6fa --- /dev/null +++ b/drivers/pwm/pwm-bcm2835.c | |||
@@ -0,0 +1,205 @@ | |||
1 | /* | ||
2 | * Copyright 2014 Bart Tanghe <bart.tanghe@thomasmore.be> | ||
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 <bart.tanghe@thomasmore.be"); | ||
204 | MODULE_DESCRIPTION("Broadcom BCM2835 PWM driver"); | ||
205 | MODULE_LICENSE("GPL v2"); | ||