S5PV210 ADC驱动分析
我这里用的是mini210开发板,在其内核源码中,采用了Linux设备驱动模型中的platform虚拟总线来管理ADC设备。首先看S5PV210提供的ADC驱动接口。在plat-samsung/dev-adc.c中定义了s3c_device_adc,它是一个platform_device结构体,描述adc这个设备。
/* plat-samsung/dev-adc.c */
static struct resource s3c_adc_resource[] = {[0] = {.start = SAMSUNG_PA_ADC,.end = SAMSUNG_PA_ADC + SZ_256 - 1,.flags = IORESOURCE_MEM,},[1] = {.start = IRQ_TC,.end = IRQ_TC,.flags = IORESOURCE_IRQ,},[2] = {.start = IRQ_ADC,.end = IRQ_ADC,.flags = IORESOURCE_IRQ,},
};
struct platform_device s3c_device_adc = {.name = "samsung-adc",.id = -1,.num_resources = ARRAY_SIZE(s3c_adc_resource),.resource = s3c_adc_resource,
};
从中可以看出,s3c_adc_resource中定义了ADC模数转换器所需要的内存地址空间、中断等资源,然后将其作为成员放入到s3c_device_adc结构体中,接下来需要做的是将s3c_device_adc加入到Platform虚拟总线管理的设备链表中去。
在s5pv210_map_io函数中,首先将s3c_device_adc的name属性设置成了”s5pv210-adc”。
/* mach-s5pv210/cpu.c */
void __init s5pv210_map_io(void)
{...s3c_adc_setname("s5pv210-adc");...
}
/* re-define device name depending on support. */
static inline void s3c_adc_setname(char *name)
{
**#ifdef CONFIG_SAMSUNG_DEV_ADC**s3c_device_adc.name = name;
**#endif**
}
然后,s3c_device_adc结构体指针被添加到mini210_devices[]这个platform_device*结构体指针数组中。
static struct platform_device *mini210_devices[] __initdata = {&s3c_device_adc,...
}
最后,通过platform_add_devices(mini210_devices, ARRAY_SIZE(mini210_devices));
添加s3c_device_adc到platform虚拟总线管理的设备链表中去。
在Linux的设备驱动模型中,总线、设备和驱动这3个结构体非常重要,总线将设备和驱动绑定。系统每注册一个设备的时候,就会寻找与之匹配的驱动;相反的,系统每注册一个驱动的时候,会寻找与之匹配的设备,匹配工作由总线完成。
这里有了ADC的platform device设备,则需要对应的platform driver与其匹配,在plat-samsung/adc.c中,可以找到s3c_adc_init的定义。
static struct platform_device_id s3c_adc_driver_ids[] = {{.name = "s3c24xx-adc",.driver_data = TYPE_S3C24XX,}, {.name = "s3c64xx-adc",.driver_data = TYPE_S3C64XX,}, {.name = "s5pv210-adc",.driver_data = TYPE_S5PV210,},{ }
};
MODULE_DEVICE_TABLE(platform, s3c_adc_driver_ids);static struct platform_driver s3c_adc_driver = {.id_table = s3c_adc_driver_ids,.driver = {.name = "s3c-adc",.owner = THIS_MODULE,},.probe = s3c_adc_probe,.remove = __devexit_p(s3c_adc_remove),.suspend = s3c_adc_suspend,.resume = s3c_adc_resume,
};static int __init adc_init(void)
{int ret;ret = platform_driver_register(&s3c_adc_driver);if (ret)printk(KERN_ERR "%s: failed to add adc driver\n", __func__);return ret;
}arch_initcall(adc_init);
platform_bus_type总线的math函数依靠platform_device的name属性与platform_driver的driver.name或id_table.name是否一致来完成ADC设备与ADC驱动程序的匹配,匹配成功后,则调用platform driver的probe指向的函数,来对设备进行初始化。
s3c_adc_probe()完成了S5PV210的ADC基地址映射、分频值设置、数据位设置等,s3c_adc_remove()函数则实现了相反的功能。
static int s3c_adc_probe(struct platform_device *pdev)
{struct device *dev = &pdev->dev;struct adc_device *adc;struct resource *regs;int ret;unsigned tmp;// 为adc_device结构体分配内存adc = kzalloc(sizeof(struct adc_device), GFP_KERNEL);if (unlikely(adc == NULL)) { ... }// 上锁spin_lock_init(&adc->lock);// 设置adc结构体属性值adc->pdev = pdev;adc->prescale = S3C2410_ADCCON_PRSCVL(49);adc->delay = S3C2410_ADCDLY_DELAY(1000);adc->cputype = platform_get_device_id(adc->pdev)->driver_data;adc->irq = platform_get_irq(pdev, 1); // 获取中断号if (unlikely(adc->irq <= 0)) { ... }ret = request_irq(adc->irq, s3c_adc_irq, 0, dev_name(dev), adc); // 申请中断if (unlikely(ret < 0)) { ... }adc->clk = clk_get(NULL, "adc"); // 获取adc设备时钟if (unlikely(IS_ERR(adc->clk))) { ... }regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); // 获取资源(相对应寄存器的地址)if (unlikely(!regs)) { ... }adc->regs = ioremap(regs->start, resource_size(regs)); // 地址映射if (unlikely(!adc->regs)) { ... }clk_enable(adc->clk); // 使能时钟tmp = adc->prescale | S3C2410_ADCCON_PRSCEN;if ((adc->cputype == TYPE_S3C64XX) || (adc->cputype == TYPE_S5PV210)){/* Enable 12-bit ADC resolution */tmp |= S3C64XX_ADCCON_RESSEL;}tmp |= S3C2410_ADCCON_STDBM;writel(tmp, adc->regs + S3C2410_ADCCON); //设置分频值与数据位writel(adc->delay, adc->regs + S3C2410_ADCDLY); //设置AD采集时间platform_set_drvdata(pdev, adc);adc_dev = adc;return 0;err: ...
}
static int __devexit s3c_adc_remove(struct platform_device *pdev)
{struct adc_device *adc = platform_get_drvdata(pdev);iounmap(adc->regs);free_irq(adc->irq, adc);clk_disable(adc->clk);clk_put(adc->clk);kfree(adc);return 0;
}
而s3c_adc_suspend和s3c_adc_resume函数分别用于系统休眠和唤醒时调用。
#ifdef CONFIG_PM
static int s3c_adc_suspend(struct platform_device *pdev, pm_message_t state)
{struct adc_device *adc = platform_get_drvdata(pdev);unsigned long flags;u32 con;spin_lock_irqsave(&adc->lock, flags);con = readl(adc->regs + S3C2410_ADCCON);con |= S3C2410_ADCCON_STDBM;writel(con, adc->regs + S3C2410_ADCCON);disable_irq(adc->irq);spin_unlock_irqrestore(&adc->lock, flags);clk_disable(adc->clk);return 0;
}static int s3c_adc_resume(struct platform_device *pdev)
{struct adc_device *adc = platform_get_drvdata(pdev);unsigned int tmp = 0;clk_enable(adc->clk);enable_irq(adc->irq);tmp = adc->prescale | S3C2410_ADCCON_PRSCEN;/* Enable 12-bit ADC resolution */if ((adc->cputype == TYPE_S3C64XX) || (adc->cputype == TYPE_S5PV210))tmp |= S3C64XX_ADCCON_RESSEL;writel(tmp, adc->regs + S3C2410_ADCCON);writel(adc->delay, adc->regs + S3C2410_ADCDLY);return 0;
}
此外,在plat-samsung/adc.c中,还定义了一下其它的函数,用来设置adc通道,读取adc值。
struct s3c_adc_client {struct platform_device *pdev;struct list_head pend;wait_queue_head_t *wait;unsigned int nr_samples;int result;unsigned char is_ts;unsigned char channel;void (*select_cb)(struct s3c_adc_client *c, unsigned selected);void (*convert_cb)(struct s3c_adc_client *c,unsigned val1, unsigned val2,unsigned *samples_left);
};static inline void s3c_adc_convert(struct adc_device *adc)
{unsigned con = readl(adc->regs + S3C2410_ADCCON);con |= S3C2410_ADCCON_ENABLE_START;writel(con, adc->regs + S3C2410_ADCCON);
}static inline void s3c_adc_select(struct adc_device *adc,struct s3c_adc_client *client)
{unsigned con = readl(adc->regs + S3C2410_ADCCON);client->select_cb(client, 1);con &= ~S3C2410_ADCCON_MUXMASK;con &= ~S3C2410_ADCCON_STDBM;con &= ~S3C2410_ADCCON_STARTMASK;con |= S3C2410_ADCCON_PRSCEN;if (!client->is_ts) {if (adc->cputype == TYPE_S5PV210)writel(S5PV210_ADCCON_SELMUX(client->channel),adc->regs + S5PV210_ADCMUX);elsecon |= S3C2410_ADCCON_SELMUX(client->channel);}writel(con, adc->regs + S3C2410_ADCCON);
}static void s3c_adc_dbgshow(struct adc_device *adc)
{adc_dbg(adc, "CON=%08x, TSC=%08x, DLY=%08x\n",readl(adc->regs + S3C2410_ADCCON),readl(adc->regs + S3C2410_ADCTSC),readl(adc->regs + S3C2410_ADCDLY));
}static void s3c_adc_try(struct adc_device *adc)
{struct s3c_adc_client *next = adc->ts_pend;unsigned int con = readl(adc->regs + S3C2410_ADCCON);if (!next && !list_empty(&adc_pending)) {next = list_first_entry(&adc_pending,struct s3c_adc_client, pend);list_del(&next->pend);} elseadc->ts_pend = NULL;if (next) {adc_dbg(adc, "new client is %p\n", next);adc->cur = next;s3c_adc_select(adc, next);s3c_adc_convert(adc);s3c_adc_dbgshow(adc);} else {con &= ~S3C2410_ADCCON_PRSCEN;con |= S3C2410_ADCCON_STDBM;writel(con, adc->regs + S3C2410_ADCCON);}
}int s3c_adc_start(struct s3c_adc_client *client,unsigned int channel, unsigned int nr_samples)
{struct adc_device *adc = adc_dev;unsigned long flags;if (!adc) {printk(KERN_ERR "%s: failed to find adc\n", __func__);return -EINVAL;}if (client->is_ts && adc->ts_pend)return -EAGAIN;spin_lock_irqsave(&adc->lock, flags);client->channel = channel;client->nr_samples = nr_samples;if (client->is_ts)adc->ts_pend = client;elselist_add_tail(&client->pend, &adc_pending);if (!adc->cur)s3c_adc_try(adc);spin_unlock_irqrestore(&adc->lock, flags);return 0;
}
EXPORT_SYMBOL_GPL(s3c_adc_start);static void s3c_adc_stop(struct s3c_adc_client *client)
{unsigned long flags;spin_lock_irqsave(&adc_dev->lock, flags);/* We should really check that nothing is in progress. */if (adc_dev->cur == client)adc_dev->cur = NULL;if (adc_dev->ts_pend == client)adc_dev->ts_pend = NULL;else {struct list_head *p, *n;struct s3c_adc_client *tmp;list_for_each_safe(p, n, &adc_pending) {tmp = list_entry(p, struct s3c_adc_client, pend);if (tmp == client)list_del(&tmp->pend);}}if (adc_dev->cur == NULL)s3c_adc_try(adc_dev);spin_unlock_irqrestore(&adc_dev->lock, flags);
}static void s3c_convert_done(struct s3c_adc_client *client,unsigned v, unsigned u, unsigned *left)
{client->result = v;wake_up(client->wait);
}int s3c_adc_read(struct s3c_adc_client *client, unsigned int ch)
{DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake);int ret;unsigned long flags;spin_lock_irqsave(&adc_dev->lock, flags);client->convert_cb = s3c_convert_done;client->wait = &wake;client->result = -1;spin_unlock_irqrestore(&adc_dev->lock, flags);ret = s3c_adc_start(client, ch, 1);if (ret < 0)goto err;ret = wait_event_timeout(wake, client->result >= 0, HZ / 2);if (client->result < 0) {s3c_adc_stop(client);dev_warn(&adc_dev->pdev->dev, "%s: %p is timed out\n",__func__, client);ret = -ETIMEDOUT;goto err;}client->convert_cb = NULL;return client->result;err:return ret;
}
EXPORT_SYMBOL_GPL(s3c_adc_read);static void s3c_adc_default_select(struct s3c_adc_client *client,unsigned select)
{
}struct s3c_adc_client *s3c_adc_register(struct platform_device *pdev,void (*select)(struct s3c_adc_client *client,unsigned int selected),void (*conv)(struct s3c_adc_client *client,unsigned d0, unsigned d1,unsigned *samples_left),unsigned int is_ts)
{struct s3c_adc_client *client;WARN_ON(!pdev);if (!select)select = s3c_adc_default_select;if (!pdev)return ERR_PTR(-EINVAL);client = kzalloc(sizeof(struct s3c_adc_client), GFP_KERNEL);if (!client) {dev_err(&pdev->dev, "no memory for adc client\n");return ERR_PTR(-ENOMEM);}client->pdev = pdev;client->is_ts = is_ts;client->select_cb = select;client->convert_cb = conv;return client;
}
EXPORT_SYMBOL_GPL(s3c_adc_register);void s3c_adc_release(struct s3c_adc_client *client)
{s3c_adc_stop(client);kfree(client);
}
EXPORT_SYMBOL_GPL(s3c_adc_release);static irqreturn_t s3c_adc_irq(int irq, void *pw)
{struct adc_device *adc = pw;struct s3c_adc_client *client = adc->cur;unsigned data0, data1;if (!client) {dev_warn(&adc->pdev->dev, "%s: no adc pending\n", __func__);goto exit;}data0 = readl(adc->regs + S3C2410_ADCDAT0);data1 = readl(adc->regs + S3C2410_ADCDAT1);adc_dbg(adc, "read %d: 0x%04x, 0x%04x\n", client->nr_samples, data0, data1);if (client->nr_samples > 0)client->nr_samples--;if ((adc->cputype == TYPE_S3C64XX) || (adc->cputype == TYPE_S5PV210)) {/* S3C64XX ADC resolution is 12-bit */data0 &= 0xfff;data1 &= 0xfff;} else {data0 &= 0x3ff;data1 &= 0x3ff;}if (client->convert_cb)(client->convert_cb)(client, data0, data1, &client->nr_samples);if (client->nr_samples > 0) {/* fire another conversion for this */client->select_cb(client, 1);s3c_adc_convert(adc);} else {spin_lock(&adc->lock);(client->select_cb)(client, 0);adc->cur = NULL;s3c_adc_try(adc);spin_unlock(&adc->lock);}exit:if ((adc->cputype == TYPE_S3C64XX) || (adc->cputype == TYPE_S5PV210)) {/* Clear ADC interrupt */writel(0, adc->regs + S3C64XX_ADCCLRINT);}return IRQ_HANDLED;
}
实际使用中,以drivers/char/mini210_adc.c文件为例,如下面代码所示,先通过s3c_adc_register()函数申请一个s3c_adc_client结构体变量指针,然后通过ret = s3c_adc_read(adcdev.client, adcdev.channel);
读相应通道的adc值即可。
typedef struct {struct mutex lock;struct s3c_adc_client *client;int channel;
} ADC_DEV;static ADC_DEV adcdev;
...
adcdev.client = s3c_adc_register(dev, NULL, NULL, 0);
...
ret = mutex_lock_interruptible(&adcdev.lock);
if(ret<0){...}
ret = s3c_adc_read(adcdev.client, adcdev.channel);
mutex_unlock(&adcdev.lock);
S5PV210 ADC驱动分析相关推荐
- S5PV210 camera 驱动分析(android)
一.Camera 工作原理介绍 1. 结构 . 一般来说,camera 主要是由 lens 和 sensor IC 两部分组成,其中有的 sensor IC 集成 了 DSP,有的没有集成,但也需要外 ...
- mini2440驱动分析之ADC
mini2440驱动分析之ADC 1 . ADC_DEV 结构 [plain] view plaincopy typedef struct { wait_queue_head_t wait; int ...
- S3C6410开发板adc驱动代码分析及测试代码分析
在本文中,我们对S3C6410开发板adc驱动代码的实现过程进行分析,然后通过一个实例对adc功能进行测试.在本文的资源中包含了设备驱动的源码和测试的源码. 一.设备驱动源码分析 adc的设备驱动主要 ...
- android 触摸屏驱动分析,Android 触摸屏驱动代码分析(ADC 类型触摸屏 CPU:s3c
Android 2.1 farsight version for s5pc100 File Name: s3c-ts.c 1 简介 1.1 本例基于s5pc100 ...
- 【转】android电池(四):电池 电量计(MAX17040)驱动分析篇
关键词:android 电池 电量计 MAX17040 任务初始化宏 power_supply 平台信息: 内核:linux2.6/linux3.0 系统:android/android4.0 ...
- linux 网卡驱动分析,LINUX_网卡驱动分析
LINUX_网卡驱动分析 (36页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 19.9 积分 Linux DM9000网卡驱动程序完全分析说明仁 本文分 ...
- mini2440驱动分析之触摸屏
mini2440驱动分析之触摸屏 mini2440触摸屏驱动对应的文件为mini2440_ts.c,他是作为输入设备注册到内核的,功能实现是通过输入子系统来完成的,现在分析触摸屏的实现.以后再分析输入 ...
- android电池(五):电池 充电IC(PM2301)驱动分析篇
android电池(五):电池 充电IC(PM2301)驱动分析篇 关键词:android 电池 电量计 PL2301任务初始化宏 power_supply 中断线程化 平台信息: 内核:linu ...
- Linux spi驱动分析(四)----SPI设备驱动(W25Q32BV)
一.W25Q32BV芯片简介 W25X是一系列SPI接口Flash芯片的简称,它采用SPI接口和CPU通信,本文使用的W25Q32BV容量为32M,具体特性如下: 1.1.基本特性 该芯片最大支持10 ...
最新文章
- SWAGAN:基于样式的小波驱动生成模型
- 【每日一包0029】merge-descriptors
- 安全开发之碰撞检测与伤害计算逻辑
- border-image图片边框
- java 中文域名转码_转换java方法
- mysql分组查询n条记录
- Android Espresso
- 7月25日训练赛签到题HDU1257
- Mac Unable to save settings: Failed to save settings. Please restart IntelliJ IDEA
- python使用临时文件
- 学习iOS从object-c开始-语法(一)
- 需求分析中常用的图形工具
- 花瓣图片采集工具研究
- 关于页面自动提交两次的问题(360浏览器)
- Boss直聘怎么写出优秀的简历?
- 微波雷达人体感应器,即时存在感知方案,智能家居人体感应交互
- matlab实现TE/TM偏振布拉格反射镜
- “开放”Android恐怕难敌Apple iOS
- leetcode--最长回文串(C语言)
- 欧拉计划(鱼C论坛)@20161111
热门文章
- 汇集全球21位医生的经验和智慧,总结出最实用的专业建议,这些都是最值得你牢记的健康秘密~
- MySQL unique啥意思_mysql中的unique
- 如何获取L2股票行情数据接口连接量化交易?
- 不同域里相同ip地址_不同的人可能具有相同的公共IP地址吗?
- 以太坊源码阅读5——POW源码分析
- unity3D 中的空气墙可以进行缩放吗
- 女性七大瘦身方法速减“孕妇肚”,还你小蛮腰
- 史上最全,用 60+ VSCode 插件,打造最强编辑器
- Android4.0 TP多点变单点,单点变多点问题
- 敏捷实用工具系列之一:5Why分析法