2 * Support functions for OMAP STI/XTI (Serial Tracing Interface)
4 * Copyright (C) 2004, 2005, 2006 Nokia Corporation
5 * Written by: Paul Mundt <paul.mundt@nokia.com>
7 * STI initialization code and channel handling
8 * from Juha Yrjölä <juha.yrjola@nokia.com>.
11 * from Roman Tereshonkov <roman.tereshonkov@nokia.com>.
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
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>
26 #include <asm/byteorder.h>
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);
34 static struct sti_irqdesc {
35 irqreturn_t (*func)(unsigned long);
37 } ____cacheline_aligned sti_irq_desc[STI_NR_IRQS];
39 void sti_channel_write_trace(int len, int id, void *data, unsigned int channel)
41 const u8 *tpntr = data;
43 sti_channel_writeb(id, channel);
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;
52 sti_channel_writel(cpu_to_be32(*asrc++),
54 len -= STI_PERCHANNEL_SIZE;
55 } while (len >= STI_PERCHANNEL_SIZE);
57 tpntr = (const u8 *)asrc;
61 sti_channel_writeb(*tpntr++, channel);
63 sti_channel_flush(channel);
65 EXPORT_SYMBOL(sti_channel_write_trace);
67 void sti_enable_irq(unsigned int id)
69 spin_lock_irq(&sti_lock);
70 sti_writel(1 << id, STI_IRQSETEN);
71 spin_unlock_irq(&sti_lock);
73 EXPORT_SYMBOL(sti_enable_irq);
75 void sti_disable_irq(unsigned int id)
77 spin_lock_irq(&sti_lock);
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);
86 spin_unlock_irq(&sti_lock);
88 EXPORT_SYMBOL(sti_disable_irq);
90 void sti_ack_irq(unsigned int id)
92 /* Even though the clear state is 0, we have to write 1 to clear */
93 sti_writel(1 << id, STI_IRQSTATUS);
95 EXPORT_SYMBOL(sti_ack_irq);
97 int sti_request_irq(unsigned int irq, void *handler, unsigned long arg)
99 struct sti_irqdesc *desc;
101 if (unlikely(!handler || irq > STI_NR_IRQS))
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);
111 desc->func = handler;
117 EXPORT_SYMBOL(sti_request_irq);
119 void sti_free_irq(unsigned int irq)
121 struct sti_irqdesc *desc = sti_irq_desc + irq;
123 if (unlikely(irq > STI_NR_IRQS))
126 sti_disable_irq(irq);
131 EXPORT_SYMBOL(sti_free_irq);
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.
138 static irqreturn_t sti_interrupt(int irq, void *dev_id)
144 status = sti_readl(STI_IRQSTATUS) & sti_irq_mask;
146 for (i = 0; status; i++) {
147 struct sti_irqdesc *desc = sti_irq_desc + i;
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);
165 return IRQ_RETVAL(ret);
168 static void omap_sti_reset(void)
172 /* Reset STI module */
173 sti_writel(0x02, STI_SYSCONFIG);
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)
181 static int __init sti_init(void)
186 if (cpu_is_omap16xx()) {
187 /* Release ARM Rhea buses peripherals enable */
188 sti_writel(sti_readl(ARM_RSTCT2) | 0x0001, ARM_RSTCT2);
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");
197 return PTR_ERR(sti_ck);
201 /* Reset STI module */
205 sti_trace_enable(MPUCmdEn);
207 /* Change to custom serial protocol */
208 sti_writel(0x01, STI_SERIAL_CFG);
210 /* Set STI clock control register to normal mode */
211 sti_writel(0x00, STI_CLK_CTRL);
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);
218 sti_channel_write_trace(strlen(buf), 0xc3, buf, 239);
223 static void sti_exit(void)
228 * This should have already been done by reset, but we switch off
229 * STI entirely just for added sanity..
231 tmp = sti_readl(STI_ER);
233 sti_writel(tmp, STI_ER);
239 static void __sti_trace_enable(int event)
243 tmp = sti_readl(STI_ER);
244 tmp |= sti_kern_mask | event;
245 sti_writel(tmp, STI_ER);
248 int sti_trace_enable(int event)
250 spin_lock_irq(&sti_lock);
251 sti_kern_mask |= event;
252 __sti_trace_enable(event);
253 spin_unlock_irq(&sti_lock);
257 EXPORT_SYMBOL(sti_trace_enable);
259 static void __sti_trace_disable(int event)
263 tmp = sti_readl(STI_DR);
265 if (cpu_is_omap16xx()) {
267 tmp &= ~sti_kern_mask;
268 } else if (cpu_is_omap24xx()) {
270 tmp |= sti_kern_mask;
274 sti_writel(tmp, STI_DR);
277 void sti_trace_disable(int event)
279 spin_lock_irq(&sti_lock);
280 sti_kern_mask &= ~event;
281 __sti_trace_disable(event);
282 spin_unlock_irq(&sti_lock);
284 EXPORT_SYMBOL(sti_trace_disable);
287 sti_trace_show(struct device *dev, struct device_attribute *attr, char *buf)
289 return sprintf(buf, "0x%08lx\n", sti_readl(STI_ER));
293 sti_trace_store(struct device *dev, struct device_attribute *attr,
294 const char *buf, size_t count)
296 int evt = simple_strtoul(buf, NULL, 0);
299 spin_lock_irq(&sti_lock);
300 __sti_trace_disable(mask);
301 __sti_trace_enable(evt);
302 spin_unlock_irq(&sti_lock);
306 static DEVICE_ATTR(trace, S_IRUGO | S_IWUSR, sti_trace_show, sti_trace_store);
309 sti_imask_show(struct device *dev, struct device_attribute *attr, char *buf)
311 return sprintf(buf, "0x%04lx\n", sti_irq_mask);
315 sti_imask_store(struct device *dev, struct device_attribute *attr,
316 const char *buf, size_t count)
318 spin_lock_irq(&sti_lock);
319 sti_irq_mask = simple_strtoul(buf, NULL, 0);
320 spin_unlock_irq(&sti_lock);
324 static DEVICE_ATTR(imask, S_IRUGO | S_IWUSR, sti_imask_show, sti_imask_store);
326 static int __devinit sti_probe(struct platform_device *pdev)
328 struct resource *res, *cres;
332 if (pdev->num_resources != 3) {
333 dev_err(&pdev->dev, "invalid number of resources: %d\n",
334 pdev->num_resources);
339 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
340 if (unlikely(!res)) {
341 dev_err(&pdev->dev, "invalid mem resource\n");
346 cres = platform_get_resource(pdev, IORESOURCE_MEM, 1);
347 if (unlikely(!cres)) {
348 dev_err(&pdev->dev, "invalid channel mem resource\n");
352 ret = device_create_file(&pdev->dev, &dev_attr_trace);
353 if (unlikely(ret != 0))
356 ret = device_create_file(&pdev->dev, &dev_attr_imask);
357 if (unlikely(ret != 0))
360 size = res->end - res->start + 1;
361 sti_base = ioremap(res->start, size);
367 size = cres->end - cres->start + 1;
368 sti_channel_base = ioremap(cres->start, size);
369 if (!sti_channel_base) {
375 ret = request_irq(platform_get_irq(pdev, 0), sti_interrupt,
376 IRQF_DISABLED, "sti", NULL);
377 if (unlikely(ret != 0))
383 iounmap(sti_channel_base);
386 device_remove_file(&pdev->dev, &dev_attr_imask);
388 device_remove_file(&pdev->dev, &dev_attr_trace);
393 static int __devexit sti_remove(struct platform_device *pdev)
395 unsigned int irq = platform_get_irq(pdev, 0);
397 iounmap(sti_channel_base);
400 device_remove_file(&pdev->dev, &dev_attr_trace);
401 device_remove_file(&pdev->dev, &dev_attr_imask);
408 static struct platform_driver sti_driver = {
410 .remove = __devexit_p(sti_remove),
413 .owner = THIS_MODULE,
417 static int __init sti_module_init(void)
419 return platform_driver_register(&sti_driver);
422 static void __exit sti_module_exit(void)
424 platform_driver_unregister(&sti_driver);
426 subsys_initcall(sti_module_init);
427 module_exit(sti_module_exit);
429 MODULE_AUTHOR("Paul Mundt, Juha Yrjölä, Roman Tereshonkov");
430 MODULE_LICENSE("GPL");