]> rtime.felk.cvut.cz Git - shark/motorek-5200.git/blob - motorek.c
Whitespace
[shark/motorek-5200.git] / motorek.c
1 /*
2  *      Driver for DC motor connected to MPC5200 based boards.
3  *
4  *      The motor is used for education at Czech Technical University
5  *      in Prague.
6  *
7  *      Authors:
8  *      Michal Sojka            <sojkam1@fel.cvut.cz>
9  *
10  *      This program is free software; you can redistribute it and/or
11  *      modify it under the terms of the GNU General Public License
12  *      as published by the Free Software Foundation; either version
13  *      2 of the License, or (at your option) any later version.
14  */
15
16 #include <linux/module.h>
17 #include <linux/platform_device.h>
18 #include <linux/mod_devicetable.h>
19 #include <linux/of.h>
20 #include <linux/of_platform.h>
21 #include <asm/mpc52xx.h>
22 #include <linux/interrupt.h>
23
24 struct motorek {
25         struct mpc52xx_gpt *pwmf, *pwmb, *irca, *ircb;
26         atomic_t pos;
27         int irq;
28         int action;
29         int last_irc_state;
30 };
31
32 static ssize_t show_position(struct device *dev,
33                             struct device_attribute *attr, char *buf)
34 {
35         struct platform_device  *pdev = to_platform_device(dev);
36         struct motorek          *m = platform_get_drvdata(pdev);
37
38         int len = snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&m->pos));
39         return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
40 }
41
42 static ssize_t show_action(struct device *dev,
43                            struct device_attribute *attr, char *buf)
44 {
45         struct platform_device  *pdev = to_platform_device(dev);
46         struct motorek          *m = platform_get_drvdata(pdev);
47
48         int len = snprintf(buf, PAGE_SIZE, "%d\n", m->action);
49         return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
50 }
51
52 static void motorek_action(struct motorek *m, int action_permile);
53 static ssize_t store_action(struct device *dev, struct device_attribute *attr,
54                             const char *buf, size_t count)
55 {
56         struct platform_device  *pdev = to_platform_device(dev);
57         struct motorek          *m = platform_get_drvdata(pdev);
58
59         int a;
60         sscanf(buf, "%d", &a);
61         motorek_action(m, a);
62         return strnlen(buf, PAGE_SIZE);
63 }
64
65 DEVICE_ATTR(position,0444,show_position,NULL);
66 DEVICE_ATTR(action,0644,show_action,store_action);
67
68 #define MPC52xx_GPT_MODE_DISABLED 0
69 #define MPC52xx_GPT_MODE_INCAPT   1
70 #define MPC52xx_GPT_MODE_OUTCMP   2
71 #define MPC52xx_GPT_MODE_PWM      3
72 #define MPC52xx_GPT_MODE_GPIO     4
73
74 #define MPC52xx_GPT_MODE_GPIO_IN   (0<<4)
75 #define MPC52xx_GPT_MODE_GPIO_OUT0 (2<<4)
76 #define MPC52xx_GPT_MODE_GPIO_OUT1 (1<<4)
77
78 #define MPC52xx_GPT_STATUS_PIN (1<<8)
79
80 #define MPC52xx_GPT_PWM_OP        (1<<8) /* PWM polarity */
81
82 /* Base clock 132 MHz
83  * (/proc/device-tree/cpus/PowerPC,5200@0/bus-frequency) */
84 #define PWM_PERIOD 6600         /* = 132 000 kHz / 20 kHz  */
85
86
87 static void pwm_width(struct mpc52xx_gpt *gpt, u16 width)
88 {
89         out_be32(&gpt->pwm, (width<<16) | MPC52xx_GPT_PWM_OP);
90         //printk("pwm: %p=0x%x\n", gpt, width);
91
92 }
93
94 static void __devinit init_pwn(struct mpc52xx_gpt *gpt)
95 {
96         //while (((unsigned)gpt & 0xff) != 0x50) gpt++;
97         out_be32(&gpt->count, (1<<16) | PWM_PERIOD);
98         pwm_width(gpt, 0);
99         out_be32(&gpt->mode, MPC52xx_GPT_MODE_PWM);
100
101         //out_be32(&gpt->pwm, (500<<16) | MPC52xx_GPT_PWM_OP); /* REMOVE ME */
102
103         //out_be32(&gpt->mode, MPC52xx_GPT_MODE_GPIO | (2<<4));
104         //printk("pwm: %p\n", gpt);
105 }
106
107 static void __devinit init_input(struct mpc52xx_gpt *gpt)
108 {
109         out_be32(&gpt->mode,
110                  MPC52xx_GPT_MODE_GPIO |
111                  MPC52xx_GPT_MODE_GPIO_IN);
112 }
113
114 static void  pwm_done(struct mpc52xx_gpt *gpt)
115 {
116         out_be32(&gpt->mode, 0);
117         out_be32(&gpt->count, 0);
118         pwm_width(gpt, 0);
119 }
120
121 static void motorek_action(struct motorek *m, int action_permile)
122 {
123         m->action = action_permile;
124         if (action_permile >= 0) {
125                 pwm_width(m->pwmb, 0);
126                 pwm_width(m->pwmf, +action_permile*PWM_PERIOD/1000);
127         } else {
128                 pwm_width(m->pwmf, 0);
129                 pwm_width(m->pwmb, -action_permile*PWM_PERIOD/1000);
130         }
131 }
132
133 static int __devinit motorek_init(struct motorek *m)
134 {
135         init_pwn(m->pwmf);
136         init_pwn(m->pwmb);
137         init_input(m->irca);
138         init_input(m->ircb);
139         return 0;
140 }
141
142 static int motorek_done(struct motorek *m)
143 {
144         pwm_done(m->pwmf);
145         pwm_done(m->pwmb);
146         return 0;
147 }
148 #define TRANSITION(old, new) (((old) << 2) | new)
149 static const int table[16] = {
150         [TRANSITION(0,1)] = +1,
151         [TRANSITION(1,3)] = +1,
152         [TRANSITION(3,2)] = +1,
153         [TRANSITION(2,0)] = +1,
154
155         [TRANSITION(1,0)] = -1,
156         [TRANSITION(3,1)] = -1,
157         [TRANSITION(2,3)] = -1,
158         [TRANSITION(0,2)] = -1,
159 };
160
161 static irqreturn_t motorek_irq(int irq, void *dev_id)
162 {
163         struct motorek *m = dev_id;
164         int irc_state =
165                 ((in_be32(&m->irca->status) & MPC52xx_GPT_STATUS_PIN) ? 1 : 0) | \
166                 ((in_be32(&m->ircb->status) & MPC52xx_GPT_STATUS_PIN) ? 2 : 0);
167         atomic_add(table[TRANSITION(m->last_irc_state, irc_state)], &m->pos);
168         m->last_irc_state = irc_state;
169         return IRQ_HANDLED;
170 }
171
172 static struct of_device_id mpc5200_gpt_match[] = {
173         { .compatible = "mpc5200-gpt", },
174         { .compatible = "fsl,mpc5200-gpt", },
175         {},
176 };
177
178 struct mpc52xx_gpt __iomem *iomap_gpt_by_phandle_prop(struct device_node *dn, const char *prop)
179 {
180         struct device_node *np;
181         struct mpc52xx_gpt __iomem *gpt = NULL;
182         const phandle *phandle;
183
184         phandle = of_get_property(dn, prop, NULL);
185         if (!phandle)
186                 return NULL;
187
188         np = of_find_node_by_phandle(*phandle);
189         if (!np)
190                 return NULL;
191
192         if (!of_match_node(mpc5200_gpt_match, np)) {
193                 printk(KERN_ERR "property %s doesn't refer to GPT\n", prop);
194                 goto out;
195         }
196
197         gpt = of_iomap(np, 0);
198 out:
199         of_node_put(np);
200         return gpt;
201 }
202
203 static int __devinit motorek_probe(struct of_device* dev,
204                                    const struct of_device_id *match)
205 {
206         struct device_node *dn = dev->node;
207         struct motorek *m;
208         int err;
209
210         m = kzalloc(sizeof(*m), GFP_KERNEL);
211         if (!m)
212                 return -ENOMEM;
213
214         err = -ENODEV;
215
216         m->pwmf = iomap_gpt_by_phandle_prop(dn, "pwmf");
217         if (!m->pwmf)
218                 goto err_free;
219         m->pwmb = iomap_gpt_by_phandle_prop(dn, "pwmb");
220         if (!m->pwmb)
221                 goto err_unmap_pf;
222         m->irca = iomap_gpt_by_phandle_prop(dn, "irca");
223         if (!m->irca)
224                 goto err_unmap_pb;
225         m->ircb = iomap_gpt_by_phandle_prop(dn, "ircb");
226         if (!m->ircb)
227                 goto err_unmap_ia;
228
229         m->irq = irq_of_parse_and_map(dn, 0);
230         if (m->irq == NO_IRQ)
231                 goto err_unmap_ib;
232         err = request_irq(m->irq, motorek_irq, 0, "motorek", m);
233         if (err)
234                 goto err_unmap_ib;
235
236         motorek_init(m);
237
238         platform_set_drvdata(dev, m);
239         err = device_create_file(&dev->dev,&dev_attr_position);
240         if (err)
241                 goto err_irq;
242         err = device_create_file(&dev->dev,&dev_attr_action);
243         if (err)
244                 goto err_irq;
245
246         printk(KERN_NOTICE "Motorek initialized\n");
247
248         return 0;
249
250 err_irq:
251         free_irq(m->irq, m);
252 err_unmap_ib:
253         iounmap(m->pwmb);
254 err_unmap_ia:
255         iounmap(m->pwmf);
256 err_unmap_pb:
257         iounmap(m->pwmb);
258 err_unmap_pf:
259         iounmap(m->pwmf);
260 err_free:
261         kfree(m);
262         return err;
263 }
264
265 static int __devexit motorek_remove(struct of_device* dev)
266 {
267         struct motorek *m;
268
269         printk(KERN_NOTICE "Removing motorek\n");
270         m = platform_get_drvdata(dev);
271         free_irq(m->irq, m);
272         motorek_done(m);
273         kfree(m);
274         return 0;
275 }
276
277 static struct of_device_id motorek_match[] = {
278         { .type = "motorek", },
279         {},
280 };
281 static struct of_platform_driver motorek_driver = {
282         .owner          = THIS_MODULE,
283         .name           = "motorek",
284         .match_table    = motorek_match,
285         .probe          = motorek_probe,
286         .remove         = __devexit_p(motorek_remove),
287 };
288
289
290 static int __init motorek_init_module(void)
291 {
292         int ret;
293         struct of_device *dev;
294         struct device_node *dn;
295
296         for_each_node_by_type(dn, "motorek") {
297                 if (!of_find_device_by_node(dn)) {
298                         dev = of_platform_device_create(dn, NULL, NULL);
299                         if (!dev)
300                                 return -ENOMEM;
301                 }
302         };
303
304         ret = of_register_platform_driver(&motorek_driver);
305         return ret;
306 }
307
308 static void __exit motorek_exit_module(void)
309 {
310         struct of_device *dev;
311         struct device_node *dn;
312
313         for_each_node_by_type(dn, "motorek") {
314                 while ((dev = of_find_device_by_node(dn))) {
315                         of_device_unregister(dev);
316                 }
317         }
318         of_unregister_platform_driver(&motorek_driver);
319 }
320
321 module_init(motorek_init_module);
322 module_exit(motorek_exit_module);
323
324
325 MODULE_LICENSE("GPL v2");
326 MODULE_VERSION("0.1");
327 MODULE_AUTHOR("Michal Sojka <sojkam1@fel.cvut.cz>");