hpet.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 980 行 · 第 1/2 页

C
980
字号
		if ((v & Tn_PER_INT_CAP_MASK) == 0) {			err = -ENXIO;			break;		}		devp->hd_flags |= HPET_PERIODIC;		break;	case HPET_DPI:		v = readq(&timer->hpet_config);		if ((v & Tn_PER_INT_CAP_MASK) == 0) {			err = -ENXIO;			break;		}		if (devp->hd_flags & HPET_PERIODIC &&		    readq(&timer->hpet_config) & Tn_TYPE_CNF_MASK) {			v = readq(&timer->hpet_config);			v ^= Tn_TYPE_CNF_MASK;			writeq(v, &timer->hpet_config);		}		devp->hd_flags &= ~HPET_PERIODIC;		break;	case HPET_IRQFREQ:		if (!kernel && (arg > hpet_max_freq) &&		    !capable(CAP_SYS_RESOURCE)) {			err = -EACCES;			break;		}		if (arg & (arg - 1)) {			err = -EINVAL;			break;		}		devp->hd_ireqfreq = hpet_time_div(hpetp->hp_period * arg);	}	return err;}static struct file_operations hpet_fops = {	.owner = THIS_MODULE,	.llseek = no_llseek,	.read = hpet_read,	.poll = hpet_poll,	.ioctl = hpet_ioctl,	.open = hpet_open,	.release = hpet_release,	.fasync = hpet_fasync,	.mmap = hpet_mmap,};EXPORT_SYMBOL(hpet_alloc);EXPORT_SYMBOL(hpet_register);EXPORT_SYMBOL(hpet_unregister);EXPORT_SYMBOL(hpet_control);int hpet_register(struct hpet_task *tp, int periodic){	unsigned int i;	u64 mask;	struct hpet_timer __iomem *timer;	struct hpet_dev *devp;	struct hpets *hpetp;	switch (periodic) {	case 1:		mask = Tn_PER_INT_CAP_MASK;		break;	case 0:		mask = 0;		break;	default:		return -EINVAL;	}	spin_lock_irq(&hpet_task_lock);	spin_lock(&hpet_lock);	for (devp = NULL, hpetp = hpets; hpetp && !devp; hpetp = hpetp->hp_next)		for (timer = hpetp->hp_hpet->hpet_timers, i = 0;		     i < hpetp->hp_ntimer; i++, timer++) {			if ((readq(&timer->hpet_config) & Tn_PER_INT_CAP_MASK)			    != mask)				continue;			devp = &hpetp->hp_dev[i];			if (devp->hd_flags & HPET_OPEN || devp->hd_task) {				devp = NULL;				continue;			}			tp->ht_opaque = devp;			devp->hd_task = tp;			break;		}	spin_unlock(&hpet_lock);	spin_unlock_irq(&hpet_task_lock);	if (tp->ht_opaque)		return 0;	else		return -EBUSY;}static inline int hpet_tpcheck(struct hpet_task *tp){	struct hpet_dev *devp;	struct hpets *hpetp;	devp = tp->ht_opaque;	if (!devp)		return -ENXIO;	for (hpetp = hpets; hpetp; hpetp = hpetp->hp_next)		if (devp >= hpetp->hp_dev		    && devp < (hpetp->hp_dev + hpetp->hp_ntimer)		    && devp->hd_hpet == hpetp->hp_hpet)			return 0;	return -ENXIO;}int hpet_unregister(struct hpet_task *tp){	struct hpet_dev *devp;	struct hpet_timer __iomem *timer;	int err;	if ((err = hpet_tpcheck(tp)))		return err;	spin_lock_irq(&hpet_task_lock);	spin_lock(&hpet_lock);	devp = tp->ht_opaque;	if (devp->hd_task != tp) {		spin_unlock(&hpet_lock);		spin_unlock_irq(&hpet_task_lock);		return -ENXIO;	}	timer = devp->hd_timer;	writeq((readq(&timer->hpet_config) & ~Tn_INT_ENB_CNF_MASK),	       &timer->hpet_config);	devp->hd_flags &= ~(HPET_IE | HPET_PERIODIC);	devp->hd_task = NULL;	spin_unlock(&hpet_lock);	spin_unlock_irq(&hpet_task_lock);	return 0;}int hpet_control(struct hpet_task *tp, unsigned int cmd, unsigned long arg){	struct hpet_dev *devp;	int err;	if ((err = hpet_tpcheck(tp)))		return err;	spin_lock_irq(&hpet_lock);	devp = tp->ht_opaque;	if (devp->hd_task != tp) {		spin_unlock_irq(&hpet_lock);		return -ENXIO;	}	spin_unlock_irq(&hpet_lock);	return hpet_ioctl_common(devp, cmd, arg, 1);}#ifdef	CONFIG_TIME_INTERPOLATIONstatic struct time_interpolator hpet_interpolator = {	.source = TIME_SOURCE_MMIO64,	.shift = 10};#endifstatic ctl_table hpet_table[] = {	{	 .ctl_name = 1,	 .procname = "max-user-freq",	 .data = &hpet_max_freq,	 .maxlen = sizeof(int),	 .mode = 0644,	 .proc_handler = &proc_dointvec,	 },	{.ctl_name = 0}};static ctl_table hpet_root[] = {	{	 .ctl_name = 1,	 .procname = "hpet",	 .maxlen = 0,	 .mode = 0555,	 .child = hpet_table,	 },	{.ctl_name = 0}};static ctl_table dev_root[] = {	{	 .ctl_name = CTL_DEV,	 .procname = "dev",	 .maxlen = 0,	 .mode = 0555,	 .child = hpet_root,	 },	{.ctl_name = 0}};static struct ctl_table_header *sysctl_header;/* * Adjustment for when arming the timer with * initial conditions.  That is, main counter * ticks expired before interrupts are enabled. */#define	TICK_CALIBRATE	(1000UL)static unsigned long __init hpet_calibrate(struct hpets *hpetp){	struct hpet_timer __iomem *timer = NULL;	unsigned long t, m, count, i, flags, start;	struct hpet_dev *devp;	int j;	struct hpet __iomem *hpet;	for (j = 0, devp = hpetp->hp_dev; j < hpetp->hp_ntimer; j++, devp++)		if ((devp->hd_flags & HPET_OPEN) == 0) {			timer = devp->hd_timer;			break;		}	if (!timer)		return 0;	hpet = hpets->hp_hpet;	t = read_counter(&timer->hpet_compare);	i = 0;	count = hpet_time_div(hpetp->hp_period * TICK_CALIBRATE);	local_irq_save(flags);	start = read_counter(&hpet->hpet_mc);	do {		m = read_counter(&hpet->hpet_mc);		write_counter(t + m + hpetp->hp_delta, &timer->hpet_compare);	} while (i++, (m - start) < count);	local_irq_restore(flags);	return (m - start) / i;}int __init hpet_alloc(struct hpet_data *hdp){	u64 cap, mcfg;	struct hpet_dev *devp;	u32 i, ntimer;	struct hpets *hpetp;	size_t siz;	struct hpet __iomem *hpet;	static struct hpets *last __initdata = (struct hpets *)0;	unsigned long ns;	/*	 * hpet_alloc can be called by platform dependent code.	 * if platform dependent code has allocated the hpet	 * ACPI also reports hpet, then we catch it here.	 */	for (hpetp = hpets; hpetp; hpetp = hpetp->hp_next)		if (hpetp->hp_hpet == hdp->hd_address)			return 0;	siz = sizeof(struct hpets) + ((hdp->hd_nirqs - 1) *				      sizeof(struct hpet_dev));	hpetp = kmalloc(siz, GFP_KERNEL);	if (!hpetp)		return -ENOMEM;	memset(hpetp, 0, siz);	hpetp->hp_which = hpet_nhpet++;	hpetp->hp_hpet = hdp->hd_address;	hpetp->hp_ntimer = hdp->hd_nirqs;	for (i = 0; i < hdp->hd_nirqs; i++)		hpetp->hp_dev[i].hd_hdwirq = hdp->hd_irq[i];	hpet = hpetp->hp_hpet;	cap = readq(&hpet->hpet_cap);	ntimer = ((cap & HPET_NUM_TIM_CAP_MASK) >> HPET_NUM_TIM_CAP_SHIFT) + 1;	if (hpetp->hp_ntimer != ntimer) {		printk(KERN_WARNING "hpet: number irqs doesn't agree"		       " with number of timers\n");		kfree(hpetp);		return -ENODEV;	}	if (last)		last->hp_next = hpetp;	else		hpets = hpetp;	last = hpetp;	hpetp->hp_period = (cap & HPET_COUNTER_CLK_PERIOD_MASK) >>	    HPET_COUNTER_CLK_PERIOD_SHIFT;	printk(KERN_INFO "hpet%d: at MMIO 0x%p, IRQ%s",		hpetp->hp_which, hpet, hpetp->hp_ntimer > 1 ? "s" : "");	for (i = 0; i < hpetp->hp_ntimer; i++)		printk("%s %d", i > 0 ? "," : "", hdp->hd_irq[i]);	printk("\n");	ns = hpetp->hp_period;	/* femptoseconds, 10^-15 */	do_div(ns, 1000000);	/* convert to nanoseconds, 10^-9 */	printk(KERN_INFO "hpet%d: %ldns tick, %d %d-bit timers\n",		hpetp->hp_which, ns, hpetp->hp_ntimer,		cap & HPET_COUNTER_SIZE_MASK ? 64 : 32);	mcfg = readq(&hpet->hpet_config);	if ((mcfg & HPET_ENABLE_CNF_MASK) == 0) {		write_counter(0L, &hpet->hpet_mc);		mcfg |= HPET_ENABLE_CNF_MASK;		writeq(mcfg, &hpet->hpet_config);	}	for (i = 0, devp = hpetp->hp_dev; i < hpetp->hp_ntimer;	     i++, hpet_ntimer++, devp++) {		unsigned long v;		struct hpet_timer __iomem *timer;		timer = &hpet->hpet_timers[devp - hpetp->hp_dev];		v = readq(&timer->hpet_config);		devp->hd_hpets = hpetp;		devp->hd_hpet = hpet;		devp->hd_timer = timer;		/*		 * If the timer was reserved by platform code,		 * then make timer unavailable for opens.		 */		if (hdp->hd_state & (1 << i)) {			devp->hd_flags = HPET_OPEN;			continue;		}		init_waitqueue_head(&devp->hd_waitqueue);	}	hpetp->hp_delta = hpet_calibrate(hpetp);	return 0;}static acpi_status __init hpet_resources(struct acpi_resource *res, void *data){	struct hpet_data *hdp;	acpi_status status;	struct acpi_resource_address64 addr;	struct hpets *hpetp;	hdp = data;	status = acpi_resource_to_address64(res, &addr);	if (ACPI_SUCCESS(status)) {		unsigned long size;		size = addr.max_address_range - addr.min_address_range + 1;		hdp->hd_address = ioremap(addr.min_address_range, size);		for (hpetp = hpets; hpetp; hpetp = hpetp->hp_next)			if (hpetp->hp_hpet == hdp->hd_address)				return -EBUSY;	} else if (res->id == ACPI_RSTYPE_EXT_IRQ) {		struct acpi_resource_ext_irq *irqp;		int i;		irqp = &res->data.extended_irq;		if (irqp->number_of_interrupts > 0) {			hdp->hd_nirqs = irqp->number_of_interrupts;			for (i = 0; i < hdp->hd_nirqs; i++)				hdp->hd_irq[i] =				    acpi_register_gsi(irqp->interrupts[i],						      irqp->edge_level,						      irqp->active_high_low);		}	}	return AE_OK;}static int __init hpet_acpi_add(struct acpi_device *device){	acpi_status result;	struct hpet_data data;	memset(&data, 0, sizeof(data));	result =	    acpi_walk_resources(device->handle, METHOD_NAME__CRS,				hpet_resources, &data);	if (ACPI_FAILURE(result))		return -ENODEV;	if (!data.hd_address || !data.hd_nirqs) {		printk("%s: no address or irqs in _CRS\n", __FUNCTION__);		return -ENODEV;	}	return hpet_alloc(&data);}static int __init hpet_acpi_remove(struct acpi_device *device, int type){	return 0;}static struct acpi_driver hpet_acpi_driver = {	.name = "hpet",	.ids = "PNP0103",	.ops = {		.add = hpet_acpi_add,		.remove = hpet_acpi_remove,		},};static struct miscdevice hpet_misc = { HPET_MINOR, "hpet", &hpet_fops };static int __init hpet_init(void){	(void)acpi_bus_register_driver(&hpet_acpi_driver);	if (hpets) {		if (misc_register(&hpet_misc))			return -ENODEV;		sysctl_header = register_sysctl_table(dev_root, 0);#ifdef	CONFIG_TIME_INTERPOLATION		{			struct hpet *hpet;			hpet = hpets->hp_hpet;			hpet_interpolator.addr = &hpets->hp_hpet->hpet_mc;			hpet_interpolator.frequency = hpet_time_div(hpets->hp_period);			hpet_interpolator.drift = hpet_interpolator.frequency *			    HPET_DRIFT / 1000000;			register_time_interpolator(&hpet_interpolator);		}#endif		return 0;	} else		return -ENODEV;}static void __exit hpet_exit(void){	acpi_bus_unregister_driver(&hpet_acpi_driver);	if (hpets)		unregister_sysctl_table(sysctl_header);	return;}module_init(hpet_init);module_exit(hpet_exit);MODULE_AUTHOR("Bob Picco <Robert.Picco@hp.com>");MODULE_LICENSE("GPL");

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?