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 + -
显示快捷键?