omap_intlat.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 315 行
C
315 行
/* * drivers/misc/omap_intlat.c * * Copyright (C) 2004 Texas Instruments, Inc. * * This package is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */#include <linux/kernel.h>#include <linux/types.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/init.h>#include <linux/device.h>#include <linux/interrupt.h>#include <linux/timer.h>#include <linux/workqueue.h>#include <linux/spinlock.h>#include <linux/time.h>#include <linux/delay.h>#include <asm/hardware.h>#include <asm/io.h>#include <asm/arch/io.h>#include <asm/arch/clocks.h>#define DRIVER_NAME "omap_intlat"static unsigned int rate = 2000;module_param(rate, uint, 0);MODULE_PARM_DESC(rate, "Delay between generated interrupts (in ms, default is 2000).");static unsigned int delay = 20;module_param(delay, uint, 0);MODULE_PARM_DESC(rate, "Delay between starting the timer and the interrupt firing (in ms, default is 20).");MODULE_LICENSE("GPL");MODULE_DESCRIPTION("OMAP Interrupt Latency Measurement Driver") MODULE_AUTHOR("Texas Instruments, Inc.");struct mputimer_regs { u32 tidr; /*timer identification */ u32 filer1; /*reserved */ u32 filer2; /*reserved */ u32 filer3; /*reserved */ u32 ti_ocpcfg; /*ocp configuration */ u32 tistat; /*interrupt status */ u32 tisr; /*Timer status */ u32 tier; /*Timer interrupt enable */ u32 twer; /*Timer wake-up enable */ u32 tclr; /*Timer control */ u32 tcrr; /*Timer counter */ u32 tldr; /*Timer load */ u32 ttgr; /*Timer trigger */ u32 twps; /*Timer write posted status */ u32 tmar; /*Timer match */ u32 tcar; /*Timer capture */ u32 tsicr; /*Timer synchronization interface control */};struct omap_intlat_info { struct device *dev; int rate; /* interrupt generation rate */ int delay; /* time between enabling timer and firing of interrupt */ int divisor; /* clock divisor */ int mhz; /* timer count rate */ u32 *mem_phys; volatile struct mputimer_regs *mem; unsigned long mem_size; int irq; struct work_struct work_start; struct work_struct work_end; struct timer_list timer; unsigned long count_before; unsigned long count_after;};static void omap_intlat_timer(unsigned long arg){ struct omap_intlat_info *info = (struct omap_intlat_info *)arg; schedule_work(&info->work_start);}#if 0static void omap_gptimer_wait(unsigned long baseaddr, unsigned long offset){ int wait = -1; switch (offset) { case TMAR: wait = 4; break; case TTGR: wait = 3; break; case TLDR: wait = 2; break; case TCRR: wait = 1; break; case TCLR: wait = 0; break; } if (wait != -1) while (readl(baseaddr + TWPS) & (1 << wait)) ;}static unsigned long omap_gptimer_read(unsigned long baseaddr, unsigned long offset){ omap_gptimer_wait(baseaddr, offset); return readl(baseaddr + offset);}static void omap_gptimer_write(unsigned long baseaddr, unsigned long offset, unsigned long value){ omap_gptimer_wait(baseaddr, offset); writel(value, baseaddr + offset);}#endifstatic void omap_intlat_work_start(void *arg){ struct omap_intlat_info *info = (struct omap_intlat_info *)arg; /* nonposted mode */ info->mem->tsicr = 0x0; info->mem->tmar = info->delay * (info->mhz * 1000); info->mem->tcrr = 0x0; /* enable match interrupt */ info->mem->tier = (0x1 << 0); /* compare enable, prescalar enable, start timer */ info->mem->tclr = ((0x1 << 6) | (0x1 << 5) | (0x1 << 0)); info->count_before = info->mem->tcrr;}static void omap_intlat_work_end(void *arg){ struct omap_intlat_info *info = (struct omap_intlat_info *)arg; printk(KERN_DEBUG "%s: latency is %lu timer ticks\n", DRIVER_NAME, info->count_after - info->count_before - (info->delay * (info->mhz * 1000))); mod_timer(&info->timer, jiffies + info->rate * HZ / 1000);}static irqreturn_t omap_intlat_irq(int irq, void *dev_id, struct pt_regs *regs){ struct omap_intlat_info *info = (struct omap_intlat_info *)dev_id; info->count_after = info->mem->tcrr;#if 0 if (!info->mem->tisr) { info->count_after = 0; return IRQ_NONE; }#endif /*clear timer interrupt status register */ info->mem->tisr = info->mem->tisr; /*stop timer */ info->mem->tclr = 0; schedule_work(&info->work_end); return IRQ_HANDLED;}static int omap_intlat_probe(struct device *dev){ struct platform_device *pdev = to_platform_device(dev); struct omap_intlat_info *info = NULL; int ret = 0; if (pdev->resource[0].flags != IORESOURCE_MEM || pdev->resource[1].flags != IORESOURCE_IRQ) { printk(KERN_ERR "%s: invalid resource type\n", DRIVER_NAME); return -ENODEV; } if (!request_mem_region(pdev->resource[0].start, pdev->resource[0].end - pdev->resource[0].start + 1, pdev->name)) { dev_dbg(&pdev->dev, "request_mem_region failed\n"); return -EBUSY; } info = kmalloc(sizeof(struct omap_intlat_info), GFP_KERNEL); if (!info) { ret = -ENOMEM; goto out_w_region; } memzero(info, sizeof(struct omap_intlat_info)); info->mem_phys = (u32 *) pdev->resource[0].start; info->mem_size = pdev->resource[0].end - pdev->resource[0].start + 1; /* map the region */ info->mem = (struct mputimer_regs *)ioremap((unsigned long)info->mem_phys, info->mem_size); if (!info->mem) { printk(KERN_ERR "%s: unable to ioremap\n", DRIVER_NAME); ret = -ENOMEM; goto out_w_mem; } info->dev = dev; info->rate = rate; info->delay = delay; info->count_before = 0; info->count_after = 0; info->mem->tclr = 0; info->divisor = 2; info->mhz = ck_get_rate(OMAP_MPUXOR_CK) / info->divisor; INIT_WORK(&info->work_start, omap_intlat_work_start, info); INIT_WORK(&info->work_end, omap_intlat_work_end, info); init_timer(&info->timer); info->timer.function = omap_intlat_timer; info->timer.data = (unsigned long)info; ret = request_irq(pdev->resource[1].start, omap_intlat_irq, 0, DRIVER_NAME, info); if (ret) goto out_w_remap; info->irq = pdev->resource[1].start; dev_set_drvdata(dev, info); mod_timer(&info->timer, jiffies + info->rate * HZ / 1000); goto out; out_w_remap: iounmap((void *)info->mem); out_w_mem: kfree(info); out_w_region: release_mem_region(pdev->resource[0].start, pdev->resource[0].end - pdev->resource[0].start + 1); out: return ret;}static int omap_intlat_remove(struct device *dev){ struct platform_device *pdev = to_platform_device(dev); struct omap_intlat_info *info = dev_get_drvdata(dev); dev_set_drvdata(dev, NULL); if (info) { free_irq(info->irq, info); flush_scheduled_work(); del_timer_sync(&info->timer); iounmap((void *)info->mem); kfree(info); release_mem_region(pdev->resource[0].start, pdev->resource[0].end - pdev->resource[0].start + 1); } return 0;}static struct device_driver omap_intlat_driver = { .name = "omap_intlat", .bus = &platform_bus_type, .probe = omap_intlat_probe, .remove = omap_intlat_remove,};static int __init omap_intlat_init(void){ return driver_register(&omap_intlat_driver);}static void __exit omap_intlat_exit(void){ driver_unregister(&omap_intlat_driver);}module_init(omap_intlat_init);module_exit(omap_intlat_exit);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?