]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/misc/sti/sti.c
Merge branch 'omap-fixes'
[linux-2.6-omap-h63xx.git] / drivers / misc / sti / sti.c
1 /*
2  * Support functions for OMAP STI/XTI (Serial Tracing Interface)
3  *
4  * Copyright (C) 2004, 2005, 2006 Nokia Corporation
5  * Written by: Paul Mundt <paul.mundt@nokia.com>
6  *
7  * STI initialization code and channel handling
8  * from Juha Yrjölä <juha.yrjola@nokia.com>.
9  *
10  * XTI initialization
11  * from Roman Tereshonkov <roman.tereshonkov@nokia.com>.
12  *
13  * This file is subject to the terms and conditions of the GNU General Public
14  * License.  See the file "COPYING" in the main directory of this archive
15  * for more details.
16  */
17 #include <linux/init.h>
18 #include <linux/err.h>
19 #include <linux/module.h>
20 #include <linux/kernel.h>
21 #include <linux/spinlock.h>
22 #include <linux/interrupt.h>
23 #include <linux/platform_device.h>
24 #include <linux/clk.h>
25 #include <mach/sti.h>
26 #include <asm/byteorder.h>
27
28 static struct clk *sti_ck;
29 void __iomem *sti_base, *sti_channel_base;
30 static unsigned long sti_kern_mask = STIEn;
31 static unsigned long sti_irq_mask = STI_IRQSTATUS_MASK;
32 static DEFINE_SPINLOCK(sti_lock);
33
34 static struct sti_irqdesc {
35         irqreturn_t (*func)(unsigned long);
36         unsigned long data;
37 } ____cacheline_aligned sti_irq_desc[STI_NR_IRQS];
38
39 void sti_channel_write_trace(int len, int id, void *data, unsigned int channel)
40 {
41         const u8 *tpntr = data;
42
43         sti_channel_writeb(id, channel);
44
45         if (cpu_is_omap16xx())
46                 /* Check u32 boundary */
47                 if (!((u32)data & (STI_PERCHANNEL_SIZE - 1)) &&
48                      (len >= STI_PERCHANNEL_SIZE)) {
49                         const u32 *asrc = data;
50
51                         do {
52                                 sti_channel_writel(cpu_to_be32(*asrc++),
53                                                    channel);
54                                 len -= STI_PERCHANNEL_SIZE;
55                         } while (len >= STI_PERCHANNEL_SIZE);
56
57                         tpntr = (const u8 *)asrc;
58                 }
59
60         while (len--)
61                 sti_channel_writeb(*tpntr++, channel);
62
63         sti_channel_flush(channel);
64 }
65 EXPORT_SYMBOL(sti_channel_write_trace);
66
67 void sti_enable_irq(unsigned int id)
68 {
69         spin_lock_irq(&sti_lock);
70         sti_writel(1 << id, STI_IRQSETEN);
71         spin_unlock_irq(&sti_lock);
72 }
73 EXPORT_SYMBOL(sti_enable_irq);
74
75 void sti_disable_irq(unsigned int id)
76 {
77         spin_lock_irq(&sti_lock);
78
79         if (cpu_is_omap16xx())
80                 sti_writel(1 << id, STI_IRQCLREN);
81         else if (cpu_is_omap24xx())
82                 sti_writel(sti_readl(STI_IRQSETEN) & ~(1 << id), STI_IRQSETEN);
83         else
84                 BUG();
85
86         spin_unlock_irq(&sti_lock);
87 }
88 EXPORT_SYMBOL(sti_disable_irq);
89
90 void sti_ack_irq(unsigned int id)
91 {
92         /* Even though the clear state is 0, we have to write 1 to clear */
93         sti_writel(1 << id, STI_IRQSTATUS);
94 }
95 EXPORT_SYMBOL(sti_ack_irq);
96
97 int sti_request_irq(unsigned int irq, void *handler, unsigned long arg)
98 {
99         struct sti_irqdesc *desc;
100
101         if (unlikely(!handler || irq > STI_NR_IRQS))
102                 return -EINVAL;
103
104         desc = sti_irq_desc + irq;
105         if (unlikely(desc->func)) {
106                 printk(KERN_WARNING "STI: Attempting to request in-use IRQ "
107                                     "%d, consider fixing your code..\n", irq);
108                 return -EBUSY;
109         }
110
111         desc->func = handler;
112         desc->data = arg;
113
114         sti_enable_irq(irq);
115         return 0;
116 }
117 EXPORT_SYMBOL(sti_request_irq);
118
119 void sti_free_irq(unsigned int irq)
120 {
121         struct sti_irqdesc *desc = sti_irq_desc + irq;
122
123         if (unlikely(irq > STI_NR_IRQS))
124                 return;
125
126         sti_disable_irq(irq);
127
128         desc->func = NULL;
129         desc->data = 0;
130 }
131 EXPORT_SYMBOL(sti_free_irq);
132
133 /*
134  * This is a bit heavy, so normally we would defer this to a tasklet.
135  * Unfortunately tasklets are too slow for the RX FIFO interrupt (and
136  * possibly some others), so we just do the irqdesc walking here.
137  */
138 static irqreturn_t sti_interrupt(int irq, void *dev_id)
139 {
140         int ret = IRQ_NONE;
141         u16 status;
142         int i;
143
144         status = sti_readl(STI_IRQSTATUS) & sti_irq_mask;
145
146         for (i = 0; status; i++) {
147                 struct sti_irqdesc *desc = sti_irq_desc + i;
148                 u16 id = 1 << i;
149
150                 if (!(status & id))
151                         continue;
152
153                 if (likely(desc && desc->func))
154                         ret |= desc->func(desc->data);
155                 if (unlikely(ret == IRQ_NONE)) {
156                         printk("STI: spurious interrupt (id %d)\n", id);
157                         sti_disable_irq(i);
158                         sti_ack_irq(i);
159                         ret = IRQ_HANDLED;
160                 }
161
162                 status &= ~id;
163         }
164
165         return IRQ_RETVAL(ret);
166 }
167
168 static void omap_sti_reset(void)
169 {
170         int i;
171
172         /* Reset STI module */
173         sti_writel(0x02, STI_SYSCONFIG);
174
175         /* Wait a while for the STI module to complete its reset */
176         for (i = 0; i < 10000; i++)
177                 if (sti_readl(STI_SYSSTATUS) & 1)
178                         break;
179 }
180
181 static int __init sti_init(void)
182 {
183         char buf[64];
184         int i;
185
186         if (cpu_is_omap16xx()) {
187                 /* Release ARM Rhea buses peripherals enable */
188                 sti_writel(sti_readl(ARM_RSTCT2) | 0x0001, ARM_RSTCT2);
189
190                 /* Enable TC1_CK (functional clock) */
191                 sti_ck = clk_get(NULL, "tc1_ck");
192         } else if (cpu_is_omap24xx())
193                 /* Enable emulation tools clock */
194                 sti_ck = clk_get(NULL, "emul_ck");
195
196         if (IS_ERR(sti_ck))
197                 return PTR_ERR(sti_ck);
198
199         clk_enable(sti_ck);
200
201         /* Reset STI module */
202         omap_sti_reset();
203
204         /* Enable STI */
205         sti_trace_enable(MPUCmdEn);
206
207         /* Change to custom serial protocol */
208         sti_writel(0x01, STI_SERIAL_CFG);
209
210         /* Set STI clock control register to normal mode */
211         sti_writel(0x00, STI_CLK_CTRL);
212
213         i = sti_readl(STI_REVISION);
214         snprintf(buf, sizeof(buf), "OMAP STI support loaded (HW v%u.%u)\n",
215                 (i >> 4) & 0x0f, i & 0x0f);
216         printk(KERN_INFO "%s", buf);
217
218         sti_channel_write_trace(strlen(buf), 0xc3, buf, 239);
219
220         return 0;
221 }
222
223 static void sti_exit(void)
224 {
225         u32 tmp;
226
227         /*
228          * This should have already been done by reset, but we switch off
229          * STI entirely just for added sanity..
230          */
231         tmp = sti_readl(STI_ER);
232         tmp &= ~STIEn;
233         sti_writel(tmp, STI_ER);
234
235         clk_disable(sti_ck);
236         clk_put(sti_ck);
237 }
238
239 static void __sti_trace_enable(int event)
240 {
241         u32 tmp;
242
243         tmp = sti_readl(STI_ER);
244         tmp |= sti_kern_mask | event;
245         sti_writel(tmp, STI_ER);
246 }
247
248 int sti_trace_enable(int event)
249 {
250         spin_lock_irq(&sti_lock);
251         sti_kern_mask |= event;
252         __sti_trace_enable(event);
253         spin_unlock_irq(&sti_lock);
254
255         return 0;
256 }
257 EXPORT_SYMBOL(sti_trace_enable);
258
259 static void __sti_trace_disable(int event)
260 {
261         u32 tmp;
262
263         tmp = sti_readl(STI_DR);
264
265         if (cpu_is_omap16xx()) {
266                 tmp |= event;
267                 tmp &= ~sti_kern_mask;
268         } else if (cpu_is_omap24xx()) {
269                 tmp &= ~event;
270                 tmp |= sti_kern_mask;
271         } else
272                 BUG();
273
274         sti_writel(tmp, STI_DR);
275 }
276
277 void sti_trace_disable(int event)
278 {
279         spin_lock_irq(&sti_lock);
280         sti_kern_mask &= ~event;
281         __sti_trace_disable(event);
282         spin_unlock_irq(&sti_lock);
283 }
284 EXPORT_SYMBOL(sti_trace_disable);
285
286 static ssize_t
287 sti_trace_show(struct device *dev, struct device_attribute *attr, char *buf)
288 {
289         return sprintf(buf, "0x%08lx\n", sti_readl(STI_ER));
290 }
291
292 static ssize_t
293 sti_trace_store(struct device *dev, struct device_attribute *attr,
294                 const char *buf, size_t count)
295 {
296         int evt = simple_strtoul(buf, NULL, 0);
297         int mask = ~evt;
298
299         spin_lock_irq(&sti_lock);
300         __sti_trace_disable(mask);
301         __sti_trace_enable(evt);
302         spin_unlock_irq(&sti_lock);
303
304         return count;
305 }
306 static DEVICE_ATTR(trace, S_IRUGO | S_IWUSR, sti_trace_show, sti_trace_store);
307
308 static ssize_t
309 sti_imask_show(struct device *dev, struct device_attribute *attr, char *buf)
310 {
311         return sprintf(buf, "0x%04lx\n", sti_irq_mask);
312 }
313
314 static ssize_t
315 sti_imask_store(struct device *dev, struct device_attribute *attr,
316                 const char *buf, size_t count)
317 {
318         spin_lock_irq(&sti_lock);
319         sti_irq_mask = simple_strtoul(buf, NULL, 0);
320         spin_unlock_irq(&sti_lock);
321
322         return count;
323 }
324 static DEVICE_ATTR(imask, S_IRUGO | S_IWUSR, sti_imask_show, sti_imask_store);
325
326 static int __devinit sti_probe(struct platform_device *pdev)
327 {
328         struct resource *res, *cres;
329         unsigned int size;
330         int ret;
331
332         if (pdev->num_resources != 3) {
333                 dev_err(&pdev->dev, "invalid number of resources: %d\n",
334                         pdev->num_resources);
335                 return -ENODEV;
336         }
337
338         /* STI base */
339         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
340         if (unlikely(!res)) {
341                 dev_err(&pdev->dev, "invalid mem resource\n");
342                 return -ENODEV;
343         }
344
345         /* Channel base */
346         cres = platform_get_resource(pdev, IORESOURCE_MEM, 1);
347         if (unlikely(!cres)) {
348                 dev_err(&pdev->dev, "invalid channel mem resource\n");
349                 return -ENODEV;
350         }
351
352         ret = device_create_file(&pdev->dev, &dev_attr_trace);
353         if (unlikely(ret != 0))
354                 return ret;
355
356         ret = device_create_file(&pdev->dev, &dev_attr_imask);
357         if (unlikely(ret != 0))
358                 goto err;
359
360         size = res->end - res->start + 1;
361         sti_base = ioremap(res->start, size);
362         if (!sti_base) {
363                 ret = -ENOMEM;
364                 goto err_badremap;
365         }
366
367         size = cres->end - cres->start + 1;
368         sti_channel_base = ioremap(cres->start, size);
369         if (!sti_channel_base) {
370                 iounmap(sti_base);
371                 ret = -ENOMEM;
372                 goto err_badremap;
373         }
374
375         ret = request_irq(platform_get_irq(pdev, 0), sti_interrupt,
376                           IRQF_DISABLED, "sti", NULL);
377         if (unlikely(ret != 0))
378                 goto err_badirq;
379
380         return sti_init();
381
382 err_badirq:
383         iounmap(sti_channel_base);
384         iounmap(sti_base);
385 err_badremap:
386         device_remove_file(&pdev->dev, &dev_attr_imask);
387 err:
388         device_remove_file(&pdev->dev, &dev_attr_trace);
389
390         return ret;
391 }
392
393 static int __devexit sti_remove(struct platform_device *pdev)
394 {
395         unsigned int irq = platform_get_irq(pdev, 0);
396
397         iounmap(sti_channel_base);
398         iounmap(sti_base);
399
400         device_remove_file(&pdev->dev, &dev_attr_trace);
401         device_remove_file(&pdev->dev, &dev_attr_imask);
402         free_irq(irq, NULL);
403         sti_exit();
404
405         return 0;
406 }
407
408 static struct platform_driver sti_driver = {
409         .probe          = sti_probe,
410         .remove         = __devexit_p(sti_remove),
411         .driver         = {
412                 .name   = "sti",
413                 .owner  = THIS_MODULE,
414         },
415 };
416
417 static int __init sti_module_init(void)
418 {
419         return platform_driver_register(&sti_driver);
420 }
421
422 static void __exit sti_module_exit(void)
423 {
424         platform_driver_unregister(&sti_driver);
425 }
426 subsys_initcall(sti_module_init);
427 module_exit(sti_module_exit);
428
429 MODULE_AUTHOR("Paul Mundt, Juha Yrjölä, Roman Tereshonkov");
430 MODULE_LICENSE("GPL");