我这里用的是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驱动分析相关推荐

  1. S5PV210 camera 驱动分析(android)

    一.Camera 工作原理介绍 1. 结构 . 一般来说,camera 主要是由 lens 和 sensor IC 两部分组成,其中有的 sensor IC 集成 了 DSP,有的没有集成,但也需要外 ...

  2. mini2440驱动分析之ADC

    mini2440驱动分析之ADC 1 . ADC_DEV 结构 [plain] view plaincopy typedef struct { wait_queue_head_t wait; int  ...

  3. S3C6410开发板adc驱动代码分析及测试代码分析

    在本文中,我们对S3C6410开发板adc驱动代码的实现过程进行分析,然后通过一个实例对adc功能进行测试.在本文的资源中包含了设备驱动的源码和测试的源码. 一.设备驱动源码分析 adc的设备驱动主要 ...

  4. android 触摸屏驱动分析,Android 触摸屏驱动代码分析(ADC 类型触摸屏 CPU:s3c

    Android 2.1 farsight version for s5pc100 File Name: s3c-ts.c 1           简介 1.1          本例基于s5pc100 ...

  5. 【转】android电池(四):电池 电量计(MAX17040)驱动分析篇

    关键词:android 电池  电量计  MAX17040 任务初始化宏 power_supply 平台信息: 内核:linux2.6/linux3.0 系统:android/android4.0  ...

  6. linux 网卡驱动分析,LINUX_网卡驱动分析

    LINUX_网卡驱动分析 (36页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 19.9 积分 Linux DM9000网卡驱动程序完全分析说明仁 本文分 ...

  7. mini2440驱动分析之触摸屏

    mini2440驱动分析之触摸屏 mini2440触摸屏驱动对应的文件为mini2440_ts.c,他是作为输入设备注册到内核的,功能实现是通过输入子系统来完成的,现在分析触摸屏的实现.以后再分析输入 ...

  8. android电池(五):电池 充电IC(PM2301)驱动分析篇

    android电池(五):电池 充电IC(PM2301)驱动分析篇 关键词:android 电池  电量计  PL2301任务初始化宏 power_supply 中断线程化 平台信息: 内核:linu ...

  9. Linux spi驱动分析(四)----SPI设备驱动(W25Q32BV)

    一.W25Q32BV芯片简介 W25X是一系列SPI接口Flash芯片的简称,它采用SPI接口和CPU通信,本文使用的W25Q32BV容量为32M,具体特性如下: 1.1.基本特性 该芯片最大支持10 ...

最新文章

  1. SWAGAN:基于样式的小波驱动生成模型
  2. 【每日一包0029】merge-descriptors
  3. 安全开发之碰撞检测与伤害计算逻辑
  4. border-image图片边框
  5. java 中文域名转码_转换java方法
  6. mysql分组查询n条记录
  7. Android Espresso
  8. 7月25日训练赛签到题HDU1257
  9. Mac Unable to save settings: Failed to save settings. Please restart IntelliJ IDEA
  10. python使用临时文件
  11. 学习iOS从object-c开始-语法(一)
  12. 需求分析中常用的图形工具
  13. 花瓣图片采集工具研究
  14. 关于页面自动提交两次的问题(360浏览器)
  15. Boss直聘怎么写出优秀的简历?
  16. 微波雷达人体感应器,即时存在感知方案,智能家居人体感应交互
  17. matlab实现TE/TM偏振布拉格反射镜
  18. “开放”Android恐怕难敌Apple iOS
  19. leetcode--最长回文串(C语言)
  20. 欧拉计划(鱼C论坛)@20161111

热门文章

  1. 汇集全球21位医生的经验和智慧,总结出最实用的专业建议,这些都是最值得你牢记的健康秘密~
  2. MySQL unique啥意思_mysql中的unique
  3. 如何获取L2股票行情数据接口连接量化交易?
  4. 不同域里相同ip地址_不同的人可能具有相同的公共IP地址吗?
  5. 以太坊源码阅读5——POW源码分析
  6. unity3D 中的空气墙可以进行缩放吗
  7. 女性七大瘦身方法速减“孕妇肚”,还你小蛮腰
  8. 史上最全,用 60+ VSCode 插件,打造最强编辑器
  9. Android4.0 TP多点变单点,单点变多点问题
  10. 敏捷实用工具系列之一:5Why分析法