Commit | Line | Data |
---|---|---|
a2308698 HS |
1 | /* |
2 | * PWM framework driver for Cirrus Logic EP93xx | |
3 | * | |
4 | * Copyright (c) 2009 Matthieu Crapet <mcrapet@gmail.com> | |
5 | * Copyright (c) 2009, 2013 H Hartley Sweeten <hsweeten@visionengravers.com> | |
6 | * | |
7 | * EP9301/02 have only one channel: | |
8 | * platform device ep93xx-pwm.1 - PWMOUT1 (EGPIO14) | |
9 | * | |
10 | * EP9307 has only one channel: | |
11 | * platform device ep93xx-pwm.0 - PWMOUT | |
12 | * | |
13 | * EP9312/15 have two channels: | |
14 | * platform device ep93xx-pwm.0 - PWMOUT | |
15 | * platform device ep93xx-pwm.1 - PWMOUT1 (EGPIO14) | |
16 | * | |
17 | * This program is free software; you can redistribute it and/or | |
18 | * modify it under the terms of the GNU General Public License | |
19 | * as published by the Free Software Foundation; either version | |
20 | * 2 of the License, or (at your option) any later version. | |
21 | * | |
22 | * This program is distributed in the hope that it will be useful, | |
23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
25 | * GNU General Public License for more details. | |
26 | */ | |
27 | ||
28 | #include <linux/module.h> | |
29 | #include <linux/platform_device.h> | |
30 | #include <linux/slab.h> | |
31 | #include <linux/clk.h> | |
32 | #include <linux/err.h> | |
33 | #include <linux/io.h> | |
34 | #include <linux/pwm.h> | |
35 | ||
36 | #include <asm/div64.h> | |
37 | ||
67e38f57 | 38 | #include <linux/soc/cirrus/ep93xx.h> /* for ep93xx_pwm_{acquire,release}_gpio() */ |
a2308698 HS |
39 | |
40 | #define EP93XX_PWMx_TERM_COUNT 0x00 | |
41 | #define EP93XX_PWMx_DUTY_CYCLE 0x04 | |
42 | #define EP93XX_PWMx_ENABLE 0x08 | |
43 | #define EP93XX_PWMx_INVERT 0x0c | |
44 | ||
45 | struct ep93xx_pwm { | |
46 | void __iomem *base; | |
47 | struct clk *clk; | |
48 | struct pwm_chip chip; | |
49 | }; | |
50 | ||
51 | static inline struct ep93xx_pwm *to_ep93xx_pwm(struct pwm_chip *chip) | |
52 | { | |
53 | return container_of(chip, struct ep93xx_pwm, chip); | |
54 | } | |
55 | ||
56 | static int ep93xx_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) | |
57 | { | |
58 | struct platform_device *pdev = to_platform_device(chip->dev); | |
59 | ||
60 | return ep93xx_pwm_acquire_gpio(pdev); | |
61 | } | |
62 | ||
63 | static void ep93xx_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) | |
64 | { | |
65 | struct platform_device *pdev = to_platform_device(chip->dev); | |
66 | ||
67 | ep93xx_pwm_release_gpio(pdev); | |
68 | } | |
69 | ||
70 | static int ep93xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |
71 | int duty_ns, int period_ns) | |
72 | { | |
73 | struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); | |
74 | void __iomem *base = ep93xx_pwm->base; | |
75 | unsigned long long c; | |
76 | unsigned long period_cycles; | |
77 | unsigned long duty_cycles; | |
78 | unsigned long term; | |
79 | int ret = 0; | |
80 | ||
81 | /* | |
82 | * The clock needs to be enabled to access the PWM registers. | |
83 | * Configuration can be changed at any time. | |
84 | */ | |
5c31252c | 85 | if (!pwm_is_enabled(pwm)) { |
a2308698 HS |
86 | ret = clk_enable(ep93xx_pwm->clk); |
87 | if (ret) | |
88 | return ret; | |
89 | } | |
90 | ||
91 | c = clk_get_rate(ep93xx_pwm->clk); | |
92 | c *= period_ns; | |
93 | do_div(c, 1000000000); | |
94 | period_cycles = c; | |
95 | ||
96 | c = period_cycles; | |
97 | c *= duty_ns; | |
98 | do_div(c, period_ns); | |
99 | duty_cycles = c; | |
100 | ||
101 | if (period_cycles < 0x10000 && duty_cycles < 0x10000) { | |
102 | term = readw(base + EP93XX_PWMx_TERM_COUNT); | |
103 | ||
104 | /* Order is important if PWM is running */ | |
105 | if (period_cycles > term) { | |
106 | writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT); | |
107 | writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE); | |
108 | } else { | |
109 | writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE); | |
110 | writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT); | |
111 | } | |
112 | } else { | |
113 | ret = -EINVAL; | |
114 | } | |
115 | ||
5c31252c | 116 | if (!pwm_is_enabled(pwm)) |
a2308698 HS |
117 | clk_disable(ep93xx_pwm->clk); |
118 | ||
119 | return ret; | |
120 | } | |
121 | ||
122 | static int ep93xx_pwm_polarity(struct pwm_chip *chip, struct pwm_device *pwm, | |
123 | enum pwm_polarity polarity) | |
124 | { | |
125 | struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); | |
126 | int ret; | |
127 | ||
128 | /* | |
129 | * The clock needs to be enabled to access the PWM registers. | |
130 | * Polarity can only be changed when the PWM is disabled. | |
131 | */ | |
132 | ret = clk_enable(ep93xx_pwm->clk); | |
133 | if (ret) | |
134 | return ret; | |
135 | ||
136 | if (polarity == PWM_POLARITY_INVERSED) | |
137 | writew(0x1, ep93xx_pwm->base + EP93XX_PWMx_INVERT); | |
138 | else | |
139 | writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_INVERT); | |
140 | ||
141 | clk_disable(ep93xx_pwm->clk); | |
142 | ||
143 | return 0; | |
144 | } | |
145 | ||
146 | static int ep93xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | |
147 | { | |
148 | struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); | |
149 | int ret; | |
150 | ||
151 | ret = clk_enable(ep93xx_pwm->clk); | |
152 | if (ret) | |
153 | return ret; | |
154 | ||
155 | writew(0x1, ep93xx_pwm->base + EP93XX_PWMx_ENABLE); | |
156 | ||
157 | return 0; | |
158 | } | |
159 | ||
160 | static void ep93xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | |
161 | { | |
162 | struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); | |
163 | ||
164 | writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_ENABLE); | |
165 | clk_disable(ep93xx_pwm->clk); | |
166 | } | |
167 | ||
168 | static const struct pwm_ops ep93xx_pwm_ops = { | |
169 | .request = ep93xx_pwm_request, | |
170 | .free = ep93xx_pwm_free, | |
171 | .config = ep93xx_pwm_config, | |
172 | .set_polarity = ep93xx_pwm_polarity, | |
173 | .enable = ep93xx_pwm_enable, | |
174 | .disable = ep93xx_pwm_disable, | |
175 | .owner = THIS_MODULE, | |
176 | }; | |
177 | ||
178 | static int ep93xx_pwm_probe(struct platform_device *pdev) | |
179 | { | |
180 | struct ep93xx_pwm *ep93xx_pwm; | |
181 | struct resource *res; | |
182 | int ret; | |
183 | ||
184 | ep93xx_pwm = devm_kzalloc(&pdev->dev, sizeof(*ep93xx_pwm), GFP_KERNEL); | |
185 | if (!ep93xx_pwm) | |
186 | return -ENOMEM; | |
187 | ||
188 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
189 | ep93xx_pwm->base = devm_ioremap_resource(&pdev->dev, res); | |
190 | if (IS_ERR(ep93xx_pwm->base)) | |
191 | return PTR_ERR(ep93xx_pwm->base); | |
192 | ||
193 | ep93xx_pwm->clk = devm_clk_get(&pdev->dev, "pwm_clk"); | |
194 | if (IS_ERR(ep93xx_pwm->clk)) | |
195 | return PTR_ERR(ep93xx_pwm->clk); | |
196 | ||
197 | ep93xx_pwm->chip.dev = &pdev->dev; | |
198 | ep93xx_pwm->chip.ops = &ep93xx_pwm_ops; | |
199 | ep93xx_pwm->chip.base = -1; | |
200 | ep93xx_pwm->chip.npwm = 1; | |
201 | ||
202 | ret = pwmchip_add(&ep93xx_pwm->chip); | |
203 | if (ret < 0) | |
204 | return ret; | |
205 | ||
206 | platform_set_drvdata(pdev, ep93xx_pwm); | |
207 | return 0; | |
208 | } | |
209 | ||
210 | static int ep93xx_pwm_remove(struct platform_device *pdev) | |
211 | { | |
212 | struct ep93xx_pwm *ep93xx_pwm = platform_get_drvdata(pdev); | |
213 | ||
214 | return pwmchip_remove(&ep93xx_pwm->chip); | |
215 | } | |
216 | ||
217 | static struct platform_driver ep93xx_pwm_driver = { | |
218 | .driver = { | |
219 | .name = "ep93xx-pwm", | |
220 | }, | |
221 | .probe = ep93xx_pwm_probe, | |
222 | .remove = ep93xx_pwm_remove, | |
223 | }; | |
224 | module_platform_driver(ep93xx_pwm_driver); | |
225 | ||
226 | MODULE_DESCRIPTION("Cirrus Logic EP93xx PWM driver"); | |
5eabf82e JH |
227 | MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>"); |
228 | MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>"); | |
a2308698 HS |
229 | MODULE_ALIAS("platform:ep93xx-pwm"); |
230 | MODULE_LICENSE("GPL"); |