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