1 /*
2  * simple driver for PWM (Pulse Width Modulator) controller
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 version 2 as
6  * published by the Free Software Foundation.
7  *
8  * Derived from pxa PWM driver by eric miao <eric.miao@marvell.com>
9  */
10 
11 #include <linux/module.h>
12 #include <linux/kernel.h>
13 #include <linux/platform_device.h>
14 #include <linux/slab.h>
15 #include <linux/err.h>
16 #include <linux/clk.h>
17 #include <linux/io.h>
18 #include <linux/pwm.h>
19 #include <mach/hardware.h>
20 
21 
22 /* i.MX1 and i.MX21 share the same PWM function block: */
23 
24 #define MX1_PWMC    0x00   /* PWM Control Register */
25 #define MX1_PWMS    0x04   /* PWM Sample Register */
26 #define MX1_PWMP    0x08   /* PWM Period Register */
27 
28 
29 /* i.MX27, i.MX31, i.MX35 share the same PWM function block: */
30 
31 #define MX3_PWMCR                 0x00    /* PWM Control Register */
32 #define MX3_PWMSAR                0x0C    /* PWM Sample Register */
33 #define MX3_PWMPR                 0x10    /* PWM Period Register */
34 #define MX3_PWMCR_PRESCALER(x)    (((x - 1) & 0xFFF) << 4)
35 #define MX3_PWMCR_DOZEEN                (1 << 24)
36 #define MX3_PWMCR_WAITEN                (1 << 23)
37 #define MX3_PWMCR_DBGEN			(1 << 22)
38 #define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16)
39 #define MX3_PWMCR_CLKSRC_IPG      (1 << 16)
40 #define MX3_PWMCR_EN              (1 << 0)
41 
42 
43 
44 struct pwm_device {
45 	struct list_head	node;
46 	struct platform_device *pdev;
47 
48 	const char	*label;
49 	struct clk	*clk;
50 
51 	int		clk_enabled;
52 	void __iomem	*mmio_base;
53 
54 	unsigned int	use_count;
55 	unsigned int	pwm_id;
56 };
57 
pwm_config(struct pwm_device * pwm,int duty_ns,int period_ns)58 int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
59 {
60 	if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
61 		return -EINVAL;
62 
63 	if (!(cpu_is_mx1() || cpu_is_mx21())) {
64 		unsigned long long c;
65 		unsigned long period_cycles, duty_cycles, prescale;
66 		u32 cr;
67 
68 		c = clk_get_rate(pwm->clk);
69 		c = c * period_ns;
70 		do_div(c, 1000000000);
71 		period_cycles = c;
72 
73 		prescale = period_cycles / 0x10000 + 1;
74 
75 		period_cycles /= prescale;
76 		c = (unsigned long long)period_cycles * duty_ns;
77 		do_div(c, period_ns);
78 		duty_cycles = c;
79 
80 		/*
81 		 * according to imx pwm RM, the real period value should be
82 		 * PERIOD value in PWMPR plus 2.
83 		 */
84 		if (period_cycles > 2)
85 			period_cycles -= 2;
86 		else
87 			period_cycles = 0;
88 
89 		writel(duty_cycles, pwm->mmio_base + MX3_PWMSAR);
90 		writel(period_cycles, pwm->mmio_base + MX3_PWMPR);
91 
92 		cr = MX3_PWMCR_PRESCALER(prescale) |
93 			MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN |
94 			MX3_PWMCR_DBGEN | MX3_PWMCR_EN;
95 
96 		if (cpu_is_mx25())
97 			cr |= MX3_PWMCR_CLKSRC_IPG;
98 		else
99 			cr |= MX3_PWMCR_CLKSRC_IPG_HIGH;
100 
101 		writel(cr, pwm->mmio_base + MX3_PWMCR);
102 	} else if (cpu_is_mx1() || cpu_is_mx21()) {
103 		/* The PWM subsystem allows for exact frequencies. However,
104 		 * I cannot connect a scope on my device to the PWM line and
105 		 * thus cannot provide the program the PWM controller
106 		 * exactly. Instead, I'm relying on the fact that the
107 		 * Bootloader (u-boot or WinCE+haret) has programmed the PWM
108 		 * function group already. So I'll just modify the PWM sample
109 		 * register to follow the ratio of duty_ns vs. period_ns
110 		 * accordingly.
111 		 *
112 		 * This is good enough for programming the brightness of
113 		 * the LCD backlight.
114 		 *
115 		 * The real implementation would divide PERCLK[0] first by
116 		 * both the prescaler (/1 .. /128) and then by CLKSEL
117 		 * (/2 .. /16).
118 		 */
119 		u32 max = readl(pwm->mmio_base + MX1_PWMP);
120 		u32 p = max * duty_ns / period_ns;
121 		writel(max - p, pwm->mmio_base + MX1_PWMS);
122 	} else {
123 		BUG();
124 	}
125 
126 	return 0;
127 }
128 EXPORT_SYMBOL(pwm_config);
129 
pwm_enable(struct pwm_device * pwm)130 int pwm_enable(struct pwm_device *pwm)
131 {
132 	int rc = 0;
133 
134 	if (!pwm->clk_enabled) {
135 		rc = clk_enable(pwm->clk);
136 		if (!rc)
137 			pwm->clk_enabled = 1;
138 	}
139 	return rc;
140 }
141 EXPORT_SYMBOL(pwm_enable);
142 
pwm_disable(struct pwm_device * pwm)143 void pwm_disable(struct pwm_device *pwm)
144 {
145 	writel(0, pwm->mmio_base + MX3_PWMCR);
146 
147 	if (pwm->clk_enabled) {
148 		clk_disable(pwm->clk);
149 		pwm->clk_enabled = 0;
150 	}
151 }
152 EXPORT_SYMBOL(pwm_disable);
153 
154 static DEFINE_MUTEX(pwm_lock);
155 static LIST_HEAD(pwm_list);
156 
pwm_request(int pwm_id,const char * label)157 struct pwm_device *pwm_request(int pwm_id, const char *label)
158 {
159 	struct pwm_device *pwm;
160 	int found = 0;
161 
162 	mutex_lock(&pwm_lock);
163 
164 	list_for_each_entry(pwm, &pwm_list, node) {
165 		if (pwm->pwm_id == pwm_id) {
166 			found = 1;
167 			break;
168 		}
169 	}
170 
171 	if (found) {
172 		if (pwm->use_count == 0) {
173 			pwm->use_count++;
174 			pwm->label = label;
175 		} else
176 			pwm = ERR_PTR(-EBUSY);
177 	} else
178 		pwm = ERR_PTR(-ENOENT);
179 
180 	mutex_unlock(&pwm_lock);
181 	return pwm;
182 }
183 EXPORT_SYMBOL(pwm_request);
184 
pwm_free(struct pwm_device * pwm)185 void pwm_free(struct pwm_device *pwm)
186 {
187 	mutex_lock(&pwm_lock);
188 
189 	if (pwm->use_count) {
190 		pwm->use_count--;
191 		pwm->label = NULL;
192 	} else
193 		pr_warning("PWM device already freed\n");
194 
195 	mutex_unlock(&pwm_lock);
196 }
197 EXPORT_SYMBOL(pwm_free);
198 
mxc_pwm_probe(struct platform_device * pdev)199 static int __devinit mxc_pwm_probe(struct platform_device *pdev)
200 {
201 	struct pwm_device *pwm;
202 	struct resource *r;
203 	int ret = 0;
204 
205 	pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
206 	if (pwm == NULL) {
207 		dev_err(&pdev->dev, "failed to allocate memory\n");
208 		return -ENOMEM;
209 	}
210 
211 	pwm->clk = clk_get(&pdev->dev, "pwm");
212 
213 	if (IS_ERR(pwm->clk)) {
214 		ret = PTR_ERR(pwm->clk);
215 		goto err_free;
216 	}
217 
218 	pwm->clk_enabled = 0;
219 
220 	pwm->use_count = 0;
221 	pwm->pwm_id = pdev->id;
222 	pwm->pdev = pdev;
223 
224 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
225 	if (r == NULL) {
226 		dev_err(&pdev->dev, "no memory resource defined\n");
227 		ret = -ENODEV;
228 		goto err_free_clk;
229 	}
230 
231 	r = request_mem_region(r->start, resource_size(r), pdev->name);
232 	if (r == NULL) {
233 		dev_err(&pdev->dev, "failed to request memory resource\n");
234 		ret = -EBUSY;
235 		goto err_free_clk;
236 	}
237 
238 	pwm->mmio_base = ioremap(r->start, resource_size(r));
239 	if (pwm->mmio_base == NULL) {
240 		dev_err(&pdev->dev, "failed to ioremap() registers\n");
241 		ret = -ENODEV;
242 		goto err_free_mem;
243 	}
244 
245 	mutex_lock(&pwm_lock);
246 	list_add_tail(&pwm->node, &pwm_list);
247 	mutex_unlock(&pwm_lock);
248 
249 	platform_set_drvdata(pdev, pwm);
250 	return 0;
251 
252 err_free_mem:
253 	release_mem_region(r->start, resource_size(r));
254 err_free_clk:
255 	clk_put(pwm->clk);
256 err_free:
257 	kfree(pwm);
258 	return ret;
259 }
260 
mxc_pwm_remove(struct platform_device * pdev)261 static int __devexit mxc_pwm_remove(struct platform_device *pdev)
262 {
263 	struct pwm_device *pwm;
264 	struct resource *r;
265 
266 	pwm = platform_get_drvdata(pdev);
267 	if (pwm == NULL)
268 		return -ENODEV;
269 
270 	mutex_lock(&pwm_lock);
271 	list_del(&pwm->node);
272 	mutex_unlock(&pwm_lock);
273 
274 	iounmap(pwm->mmio_base);
275 
276 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
277 	release_mem_region(r->start, resource_size(r));
278 
279 	clk_put(pwm->clk);
280 
281 	kfree(pwm);
282 	return 0;
283 }
284 
285 static struct platform_driver mxc_pwm_driver = {
286 	.driver		= {
287 		.name	= "mxc_pwm",
288 	},
289 	.probe		= mxc_pwm_probe,
290 	.remove		= __devexit_p(mxc_pwm_remove),
291 };
292 
mxc_pwm_init(void)293 static int __init mxc_pwm_init(void)
294 {
295 	return platform_driver_register(&mxc_pwm_driver);
296 }
297 arch_initcall(mxc_pwm_init);
298 
mxc_pwm_exit(void)299 static void __exit mxc_pwm_exit(void)
300 {
301 	platform_driver_unregister(&mxc_pwm_driver);
302 }
303 module_exit(mxc_pwm_exit);
304 
305 MODULE_LICENSE("GPL v2");
306 MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
307