ucb1x00-core.c

来自「linux 内核源代码」· C语言 代码 · 共 664 行 · 第 1/2 页

C
664
字号
 *	 -ENOENT if devid does not match */int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid){	struct ucb1x00_irq *irq;	int ret;	if (idx >= 16)		goto bad;	irq = ucb->irq_handler + idx;	ret = -ENOENT;	spin_lock_irq(&ucb->lock);	if (irq->devid == devid) {		ucb->irq_ris_enbl &= ~(1 << idx);		ucb->irq_fal_enbl &= ~(1 << idx);		ucb1x00_enable(ucb);		ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl);		ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl);		ucb1x00_disable(ucb);		irq->fn = NULL;		irq->devid = NULL;		ret = 0;	}	spin_unlock_irq(&ucb->lock);	return ret;bad:	printk(KERN_ERR "Freeing bad UCB1x00 irq %d\n", idx);	return -EINVAL;}static int ucb1x00_add_dev(struct ucb1x00 *ucb, struct ucb1x00_driver *drv){	struct ucb1x00_dev *dev;	int ret = -ENOMEM;	dev = kmalloc(sizeof(struct ucb1x00_dev), GFP_KERNEL);	if (dev) {		dev->ucb = ucb;		dev->drv = drv;		ret = drv->add(dev);		if (ret == 0) {			list_add(&dev->dev_node, &ucb->devs);			list_add(&dev->drv_node, &drv->devs);		} else {			kfree(dev);		}	}	return ret;}static void ucb1x00_remove_dev(struct ucb1x00_dev *dev){	dev->drv->remove(dev);	list_del(&dev->dev_node);	list_del(&dev->drv_node);	kfree(dev);}/* * Try to probe our interrupt, rather than relying on lots of * hard-coded machine dependencies.  For reference, the expected * IRQ mappings are: * *  	Machine		Default IRQ *	adsbitsy	IRQ_GPCIN4 *	cerf		IRQ_GPIO_UCB1200_IRQ *	flexanet	IRQ_GPIO_GUI *	freebird	IRQ_GPIO_FREEBIRD_UCB1300_IRQ *	graphicsclient	ADS_EXT_IRQ(8) *	graphicsmaster	ADS_EXT_IRQ(8) *	lart		LART_IRQ_UCB1200 *	omnimeter	IRQ_GPIO23 *	pfs168		IRQ_GPIO_UCB1300_IRQ *	simpad		IRQ_GPIO_UCB1300_IRQ *	shannon		SHANNON_IRQ_GPIO_IRQ_CODEC *	yopy		IRQ_GPIO_UCB1200_IRQ */static int ucb1x00_detect_irq(struct ucb1x00 *ucb){	unsigned long mask;	mask = probe_irq_on();	if (!mask) {		probe_irq_off(mask);		return NO_IRQ;	}	/*	 * Enable the ADC interrupt.	 */	ucb1x00_reg_write(ucb, UCB_IE_RIS, UCB_IE_ADC);	ucb1x00_reg_write(ucb, UCB_IE_FAL, UCB_IE_ADC);	ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0xffff);	ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0);	/*	 * Cause an ADC interrupt.	 */	ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA);	ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START);	/*	 * Wait for the conversion to complete.	 */	while ((ucb1x00_reg_read(ucb, UCB_ADC_DATA) & UCB_ADC_DAT_VAL) == 0);	ucb1x00_reg_write(ucb, UCB_ADC_CR, 0);	/*	 * Disable and clear interrupt.	 */	ucb1x00_reg_write(ucb, UCB_IE_RIS, 0);	ucb1x00_reg_write(ucb, UCB_IE_FAL, 0);	ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0xffff);	ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0);	/*	 * Read triggered interrupt.	 */	return probe_irq_off(mask);}static void ucb1x00_release(struct class_device *dev){	struct ucb1x00 *ucb = classdev_to_ucb1x00(dev);	kfree(ucb);}static struct class ucb1x00_class = {	.name		= "ucb1x00",	.release	= ucb1x00_release,};static int ucb1x00_probe(struct mcp *mcp){	struct ucb1x00 *ucb;	struct ucb1x00_driver *drv;	unsigned int id;	int ret = -ENODEV;	mcp_enable(mcp);	id = mcp_reg_read(mcp, UCB_ID);	if (id != UCB_ID_1200 && id != UCB_ID_1300 && id != UCB_ID_TC35143) {		printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id);		goto err_disable;	}	ucb = kzalloc(sizeof(struct ucb1x00), GFP_KERNEL);	ret = -ENOMEM;	if (!ucb)		goto err_disable;	ucb->cdev.class = &ucb1x00_class;	ucb->cdev.dev = &mcp->attached_device;	strlcpy(ucb->cdev.class_id, "ucb1x00", sizeof(ucb->cdev.class_id));	spin_lock_init(&ucb->lock);	spin_lock_init(&ucb->io_lock);	sema_init(&ucb->adc_sem, 1);	ucb->id  = id;	ucb->mcp = mcp;	ucb->irq = ucb1x00_detect_irq(ucb);	if (ucb->irq == NO_IRQ) {		printk(KERN_ERR "UCB1x00: IRQ probe failed\n");		ret = -ENODEV;		goto err_free;	}	ret = request_irq(ucb->irq, ucb1x00_irq, IRQF_TRIGGER_RISING,			  "UCB1x00", ucb);	if (ret) {		printk(KERN_ERR "ucb1x00: unable to grab irq%d: %d\n",			ucb->irq, ret);		goto err_free;	}	mcp_set_drvdata(mcp, ucb);	ret = class_device_register(&ucb->cdev);	if (ret)		goto err_irq;	INIT_LIST_HEAD(&ucb->devs);	mutex_lock(&ucb1x00_mutex);	list_add(&ucb->node, &ucb1x00_devices);	list_for_each_entry(drv, &ucb1x00_drivers, node) {		ucb1x00_add_dev(ucb, drv);	}	mutex_unlock(&ucb1x00_mutex);	goto out; err_irq:	free_irq(ucb->irq, ucb); err_free:	kfree(ucb); err_disable:	mcp_disable(mcp); out:	return ret;}static void ucb1x00_remove(struct mcp *mcp){	struct ucb1x00 *ucb = mcp_get_drvdata(mcp);	struct list_head *l, *n;	mutex_lock(&ucb1x00_mutex);	list_del(&ucb->node);	list_for_each_safe(l, n, &ucb->devs) {		struct ucb1x00_dev *dev = list_entry(l, struct ucb1x00_dev, dev_node);		ucb1x00_remove_dev(dev);	}	mutex_unlock(&ucb1x00_mutex);	free_irq(ucb->irq, ucb);	class_device_unregister(&ucb->cdev);}int ucb1x00_register_driver(struct ucb1x00_driver *drv){	struct ucb1x00 *ucb;	INIT_LIST_HEAD(&drv->devs);	mutex_lock(&ucb1x00_mutex);	list_add(&drv->node, &ucb1x00_drivers);	list_for_each_entry(ucb, &ucb1x00_devices, node) {		ucb1x00_add_dev(ucb, drv);	}	mutex_unlock(&ucb1x00_mutex);	return 0;}void ucb1x00_unregister_driver(struct ucb1x00_driver *drv){	struct list_head *n, *l;	mutex_lock(&ucb1x00_mutex);	list_del(&drv->node);	list_for_each_safe(l, n, &drv->devs) {		struct ucb1x00_dev *dev = list_entry(l, struct ucb1x00_dev, drv_node);		ucb1x00_remove_dev(dev);	}	mutex_unlock(&ucb1x00_mutex);}static int ucb1x00_suspend(struct mcp *mcp, pm_message_t state){	struct ucb1x00 *ucb = mcp_get_drvdata(mcp);	struct ucb1x00_dev *dev;	mutex_lock(&ucb1x00_mutex);	list_for_each_entry(dev, &ucb->devs, dev_node) {		if (dev->drv->suspend)			dev->drv->suspend(dev, state);	}	mutex_unlock(&ucb1x00_mutex);	return 0;}static int ucb1x00_resume(struct mcp *mcp){	struct ucb1x00 *ucb = mcp_get_drvdata(mcp);	struct ucb1x00_dev *dev;	mutex_lock(&ucb1x00_mutex);	list_for_each_entry(dev, &ucb->devs, dev_node) {		if (dev->drv->resume)			dev->drv->resume(dev);	}	mutex_unlock(&ucb1x00_mutex);	return 0;}static struct mcp_driver ucb1x00_driver = {	.drv		= {		.name	= "ucb1x00",	},	.probe		= ucb1x00_probe,	.remove		= ucb1x00_remove,	.suspend	= ucb1x00_suspend,	.resume		= ucb1x00_resume,};static int __init ucb1x00_init(void){	int ret = class_register(&ucb1x00_class);	if (ret == 0) {		ret = mcp_driver_register(&ucb1x00_driver);		if (ret)			class_unregister(&ucb1x00_class);	}	return ret;}static void __exit ucb1x00_exit(void){	mcp_driver_unregister(&ucb1x00_driver);	class_unregister(&ucb1x00_class);}module_init(ucb1x00_init);module_exit(ucb1x00_exit);EXPORT_SYMBOL(ucb1x00_io_set_dir);EXPORT_SYMBOL(ucb1x00_io_write);EXPORT_SYMBOL(ucb1x00_io_read);EXPORT_SYMBOL(ucb1x00_adc_enable);EXPORT_SYMBOL(ucb1x00_adc_read);EXPORT_SYMBOL(ucb1x00_adc_disable);EXPORT_SYMBOL(ucb1x00_hook_irq);EXPORT_SYMBOL(ucb1x00_free_irq);EXPORT_SYMBOL(ucb1x00_enable_irq);EXPORT_SYMBOL(ucb1x00_disable_irq);EXPORT_SYMBOL(ucb1x00_register_driver);EXPORT_SYMBOL(ucb1x00_unregister_driver);MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");MODULE_DESCRIPTION("UCB1x00 core driver");MODULE_LICENSE("GPL");

⌨️ 快捷键说明

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