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