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/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>
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);
35 static struct sti_irqdesc {
36 irqreturn_t (*func)(unsigned long);
38 } ____cacheline_aligned sti_irq_desc[STI_NR_IRQS];
40 void sti_channel_write_trace(int len, int id, void *data, unsigned int channel)
42 const u8 *tpntr = data;
44 sti_channel_writeb(id, channel);
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;
53 sti_channel_writel(cpu_to_be32(*asrc++),
55 len -= STI_PERCHANNEL_SIZE;
56 } while (len >= STI_PERCHANNEL_SIZE);
58 tpntr = (const u8 *)asrc;
62 sti_channel_writeb(*tpntr++, channel);
64 sti_channel_flush(channel);
66 EXPORT_SYMBOL(sti_channel_write_trace);
68 void sti_enable_irq(unsigned int id)
70 spin_lock_irq(&sti_lock);
71 sti_writel(1 << id, STI_IRQSETEN);
72 spin_unlock_irq(&sti_lock);
74 EXPORT_SYMBOL(sti_enable_irq);
76 void sti_disable_irq(unsigned int id)
78 spin_lock_irq(&sti_lock);
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);
87 spin_unlock_irq(&sti_lock);
89 EXPORT_SYMBOL(sti_disable_irq);
91 void sti_ack_irq(unsigned int id)
93 /* Even though the clear state is 0, we have to write 1 to clear */
94 sti_writel(1 << id, STI_IRQSTATUS);
96 EXPORT_SYMBOL(sti_ack_irq);
98 int sti_request_irq(unsigned int irq, void *handler, unsigned long arg)
100 struct sti_irqdesc *desc;
102 if (unlikely(!handler || irq > STI_NR_IRQS))
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);
112 desc->func = handler;
118 EXPORT_SYMBOL(sti_request_irq);
120 void sti_free_irq(unsigned int irq)
122 struct sti_irqdesc *desc = sti_irq_desc + irq;
124 if (unlikely(irq > STI_NR_IRQS))
127 sti_disable_irq(irq);
132 EXPORT_SYMBOL(sti_free_irq);
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.
139 static irqreturn_t sti_interrupt(int irq, void *dev_id, struct pt_regs *regs)
145 status = sti_readl(STI_IRQSTATUS) & sti_irq_mask;
147 for (i = 0; status; i++) {
148 struct sti_irqdesc *desc = sti_irq_desc + i;
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);
166 return IRQ_RETVAL(ret);
169 static void omap_sti_reset(void)
173 /* Reset STI module */
174 sti_writel(0x02, STI_SYSCONFIG);
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)
182 static int __init sti_init(void)
187 if (cpu_is_omap16xx()) {
188 /* Release ARM Rhea buses peripherals enable */
189 sti_writel(sti_readl(ARM_RSTCT2) | 0x0001, ARM_RSTCT2);
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");
198 return PTR_ERR(sti_ck);
202 /* Reset STI module */
206 sti_trace_enable(MPUCmdEn);
208 /* Change to custom serial protocol */
209 sti_writel(0x01, STI_SERIAL_CFG);
211 /* Set STI clock control register to normal mode */
212 sti_writel(0x00, STI_CLK_CTRL);
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);
219 sti_channel_write_trace(strlen(buf), 0xc3, buf, 239);
224 static void sti_exit(void)
229 * This should have already been done by reset, but we switch off
230 * STI entirely just for added sanity..
232 tmp = sti_readl(STI_ER);
234 sti_writel(tmp, STI_ER);
240 static void __sti_trace_enable(int event)
244 tmp = sti_readl(STI_ER);
245 tmp |= sti_kern_mask | event;
246 sti_writel(tmp, STI_ER);
249 int sti_trace_enable(int event)
251 spin_lock_irq(&sti_lock);
252 sti_kern_mask |= event;
253 __sti_trace_enable(event);
254 spin_unlock_irq(&sti_lock);
258 EXPORT_SYMBOL(sti_trace_enable);
260 static void __sti_trace_disable(int event)
264 tmp = sti_readl(STI_DR);
266 if (cpu_is_omap16xx()) {
268 tmp &= ~sti_kern_mask;
269 } else if (cpu_is_omap24xx()) {
271 tmp |= sti_kern_mask;
275 sti_writel(tmp, STI_DR);
278 void sti_trace_disable(int event)
280 spin_lock_irq(&sti_lock);
281 sti_kern_mask &= ~event;
282 __sti_trace_disable(event);
283 spin_unlock_irq(&sti_lock);
285 EXPORT_SYMBOL(sti_trace_disable);
288 sti_trace_show(struct device *dev, struct device_attribute *attr, char *buf)
290 return sprintf(buf, "0x%08lx\n", sti_readl(STI_ER));
294 sti_trace_store(struct device *dev, struct device_attribute *attr,
295 const char *buf, size_t count)
297 int evt = simple_strtoul(buf, NULL, 0);
300 spin_lock_irq(&sti_lock);
301 __sti_trace_disable(mask);
302 __sti_trace_enable(evt);
303 spin_unlock_irq(&sti_lock);
307 static DEVICE_ATTR(trace, S_IRUGO | S_IWUSR, sti_trace_show, sti_trace_store);
310 sti_imask_show(struct device *dev, struct device_attribute *attr, char *buf)
312 return sprintf(buf, "0x%04lx\n", sti_irq_mask);
316 sti_imask_store(struct device *dev, struct device_attribute *attr,
317 const char *buf, size_t count)
319 spin_lock_irq(&sti_lock);
320 sti_irq_mask = simple_strtoul(buf, NULL, 0);
321 spin_unlock_irq(&sti_lock);
325 static DEVICE_ATTR(imask, S_IRUGO | S_IWUSR, sti_imask_show, sti_imask_store);
327 static int __devinit sti_probe(struct platform_device *pdev)
329 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 sti_base = res->start;
363 * OMAP 16xx keeps channels in a relatively sane location,
364 * whereas 24xx maps them much further out, and so they must be
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;
372 sti_channel_base = (unsigned long)ioremap(cres->start, size);
373 if (unlikely(!sti_channel_base)) {
379 ret = request_irq(platform_get_irq(pdev, 0), sti_interrupt,
380 SA_INTERRUPT, "sti", NULL);
381 if (unlikely(ret != 0))
387 iounmap((void *)sti_channel_base);
389 device_remove_file(&pdev->dev, &dev_attr_imask);
391 device_remove_file(&pdev->dev, &dev_attr_trace);
396 static int __devexit sti_remove(struct platform_device *pdev)
398 unsigned int irq = platform_get_irq(pdev, 0);
400 if (cpu_is_omap24xx())
401 iounmap((void *)sti_channel_base);
403 device_remove_file(&pdev->dev, &dev_attr_trace);
404 device_remove_file(&pdev->dev, &dev_attr_imask);
411 static struct platform_driver sti_driver = {
413 .remove = __devexit_p(sti_remove),
416 .owner = THIS_MODULE,
420 static int __init sti_module_init(void)
422 return platform_driver_register(&sti_driver);
425 static void __exit sti_module_exit(void)
427 platform_driver_unregister(&sti_driver);
429 subsys_initcall(sti_module_init);
430 module_exit(sti_module_exit);
432 MODULE_AUTHOR("Paul Mundt, Juha Yrjölä, Roman Tereshonkov");
433 MODULE_LICENSE("GPL");