开发板:tiny4412SDK + S702 + 4GB Flash
要移植的内核版本:Linux-4.4.0 (支持device tree)
u-boot版本:友善之臂自带的 U-Boot 2010.12
busybox版本:busybox 1.25
目标:
在第一篇文章中,学习了在设备树中增加GPIO资源,在代码中转为对应的中断,本文目标学习在设备树中直接使用中断资源,实现ADC采集底板上滑动变阻器的电压。
原理图:
设备树参考:
adc: adc@126C0000 { compatible = "samsung,exynos-adc-v1"; reg = <0x126C0000 0x100>; interrupt-parent = <&combiner>; interrupts = <10 3>; clocks = <&clock CLK_TSADC>; clock-names = "adc"; #io-channel-cells = <1>; io-channel-ranges; samsung,syscon-phandle = <&pmu_system_controller>; status = "disabled"; };
interrupt combiner 见芯片手册第10章,Interrupt combiner combines several interrupt sources as a group. Several interrupt requests in a group make a group interrupt request and a single request signal。一组中断源公用一个中断请求信号,2440也有类似例子。以ADC为例,它位于组INITG10的number3
samsung,exynos4210-combiner.txtRequired properties:- compatible: should be "samsung,exynos4210-combiner".- interrupt-controller: Identifies the node as an interrupt controller.- * First Cell: Combiner Group Number. * Second Cell: Interrupt number within the group.也就是: interrupt-parent = <&combiner>; interrupts = <10 3>;
设备树:
adc_demo@126C0000{ compatible = "tiny4412,adc_demo"; reg = <0x126C 0x20>; clocks = <&clock CLK_TSADC>; clock-names = "timers"; interrupt-parent = <&combiner>; interrupts = <10 3>;};
在代码中,我们仍可以向平台设备一样,使用
参考:Exynos_adc.c (drivers\iio\adc) 19412 2016/12/17 irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "no irq resource?\n"); return irq; } ... ret = request_irq(info->irq, exynos_adc_isr, 0, dev_name(&pdev->dev), info);
代码:
#include <linux/module.h>#include <linux/kernel.h>#include <linux/cdev.h>#include <linux/device.h>#include <linux/platform_device.h>#include <linux/gpio.h>#include <linux/of.h>#include <linux/of_gpio.h>#include <linux/fs.h>#include <asm/uaccess.h>#include <linux/bitops.h>#include <linux/clk.h>#include <linux/export.h>#include <linux/err.h>#include <linux/io.h>#include <linux/slab.h>#include <linux/spinlock.h>#include <linux/time.h>#include <linux/sched.h>#include <linux/wait.h>#include <linux/interrupt.h>DECLARE_WAIT_QUEUE_HEAD(wait);static int major;static struct cdev adc_cdev;static struct class *cls;struct ADC_BASE{ unsigned int ADCCON; unsigned int temp0; unsigned int ADCDLY; unsigned int ADCDAT; unsigned int temp1; unsigned int temp2; unsigned int CLRINTADC; unsigned int ADCMUX; };volatile static struct ADC_BASE *adc_base = NULL;static int adc_open(struct inode *inode, struct file *file){ printk("adc_open\n"); return 0;}static int adc_release(struct inode *inode, struct file *file){ printk("adc_exit\n"); return 0;}static ssize_t adc_read(struct file *filp, char __user *buf, size_t count, loff_t *off){ int data = 0, ret = 0; printk("adc_read\n"); adc_base->ADCMUX = 0x00; adc_base->ADCCON = (1 << 16 | 1 << 14 | 99 << 6 | 1 << 0); wait_event_interruptible(wait, ((adc_base->ADCCON >> 15) & 0x01)); data = adc_base->ADCDAT & 0xfff; ret = copy_to_user(buf, &data, count); printk("copy_to_user %x\n", data); if (ret < 0) { printk("copy_to_user error\n"); return -EFAULT; } return count;}static struct file_operations adc_fops ={ .owner = THIS_MODULE, .open = adc_open, .read = adc_read, .release = adc_release,};static irqreturn_t adc_demo_isr(int irq, void *dev_id){ printk("enter irq now to wake up\n"); wake_up(&wait); adc_base->CLRINTADC = 1; return IRQ_HANDLED;}struct clk *base_clk;int irq;static int adc_probe(struct platform_device *pdev){ dev_t devid; struct device *dev = &pdev->dev; struct resource *res = NULL; int ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { printk("platform_get_resource error\n"); return -EINVAL; } base_clk = devm_clk_get(&pdev->dev, "timers"); if (IS_ERR(base_clk)) { dev_err(dev, "failed to get timer base clk\n"); return PTR_ERR(base_clk); } ret = clk_prepare_enable(base_clk); if (ret < 0) { dev_err(dev, "failed to enable base clock\n"); return -EINVAL; } printk("res: %x\n", (unsigned int)res->start); adc_base = devm_ioremap_resource(&pdev->dev, res); if (adc_base == NULL) { printk("devm_ioremap_resource error\n"); goto err_clk; } printk("adc_base: %x\n", (unsigned int)adc_base); irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "no irq resource?\n"); goto err_clk; } ret = request_irq(irq, adc_demo_isr, 0, "adc", NULL); if (ret < 0) { dev_err(dev, "failed to request_irq\n"); goto err_clk; } if (alloc_chrdev_region(&devid, 0, 1, "adc") < 0) { printk("%s ERROR\n", __func__); goto err_req_irq; } major = MAJOR(devid); cdev_init(&adc_cdev, &adc_fops); cdev_add(&adc_cdev, devid, 1); cls = class_create(THIS_MODULE, "myadc"); device_create(cls, NULL, MKDEV(major, 0), NULL, "adc"); return 0;err_req_irq: free_irq(irq, NULL);err_clk: clk_disable(base_clk); clk_unprepare(base_clk); return -EINVAL;}static int adc_remove(struct platform_device *pdev){ printk("enter %s\n", __func__); device_destroy(cls, MKDEV(major, 0)); class_destroy(cls); cdev_del(&adc_cdev); unregister_chrdev_region(MKDEV(major, 0), 1); clk_disable(base_clk); clk_unprepare(base_clk); free_irq(irq, NULL); printk("%s enter.\n", __func__); return 0;}static const struct of_device_id adc_dt_ids[] ={ { .compatible = "tiny4412,adc_demo", }, {},};MODULE_DEVICE_TABLE(of, adc_dt_ids);static struct platform_driver adc_driver ={ .driver = { .name = "adc_demo", .of_match_table = of_match_ptr(adc_dt_ids), }, .probe = adc_probe, .remove = adc_remove,};static int adc_init(void){ int ret; printk("enter %s\n", __func__); ret = platform_driver_register(&adc_driver); if (ret) { printk(KERN_ERR "adc demo: probe faiadc: %d\n", ret); } return ret;}static void adc_exit(void){ printk("enter %s\n", __func__); platform_driver_unregister(&adc_driver);}module_init(adc_init);module_exit(adc_exit);MODULE_LICENSE("GPL");
0 0