gsc3280 linux io中断,GSC3280的ADC子系统驱动模型(一)

一、芯片和驱动架构总体介绍

1.1、芯片介绍

本文使用的芯片为GSC3280,根据芯片手册的介绍,ADC 与触摸屏控制器通过 SPI 接口挂在GSC3280的SPI0总

线上,支持4线电阻式触摸屏或当ADC输入使用。GSC3280片上集成的ADC与触摸屏控制器主要有以下特性:

1、通过标准SPI接口传输命令和数据;

2、最大分辨率为12位;

3、输入最大的SPI 时钟是6MHz,对应最大采样率为120Ksps;

4、支持4线电阻式触摸屏;

5、可以当4路ADC输入;

6、支持触摸屏触笔中断;

7、支持低功耗模式;

1.2、驱动架构介绍

本文所设计的ADC子系统架构关系图如下:

d95085f044acb7ee3568add4a61fe354.png

由于AD转换使用的是SPI0,所以此处在Linux内核源码目录的/driver/spi目录下建立一个adc目录,根据上图,

建立了adc-core.c、adc-dev.c、adc-sysfs.c、adc-proc.c、adc.h和gsc3280_adc.c等文件。现在先简单说下

各个文件的功能:

gsc3280_adc.c:是最底层的直接和硬件打交道的驱动文件,将在(二)中讲述。

adc-core.c:gsc3280_adc.c的上面一层,提供了ADC子系统的一些公共函数,让各个ADC驱动注册集成到

linux内核中,向驱动程序提供了注册/注销接口。将在第二篇文章中讲述。

adc-dev.c:adc-core.c再往上就到了adc-dev.c,adc-dev.c最终生成了/dev/adc设备节点,上层的应用程

序就是通过操作此文件来进行相关的读取AD转换值等操作的。定义了基本的设备文件操作函数,用户程序与ADC驱

动的接口函数,这里定义了每个ioctl命令需要调用的函数,还有open,read等。将在第二篇文章中讲述。

adc-proc.c:与proc文件系统有关,提供通过proc文件系统操作ADC。将在第二篇文章中讲述。

adc-sysfs.c:与sysfs有关,提供通过sys文件系统操作ADC。将在第二篇文章中讲述。

adc.h、adc-core.h:定义了与ADC有关的数据结构,变量和函数声明等。在使用时讲述。

二、驱动程序gsc3280_adc.c

2.1、模块初始化

本部分讲述gsc3280_adc.c文件中的程序,此即为上图中的最下部分--驱动程序,首先从模块初始化开始,程

序如下:

点击(此处)折叠或打开

static struct platform_driver gsc3280adc_driver = {

.driver    = {

.name    = "adc-core",

.owner    = THIS_MODULE,

},

.probe    = gsc3280_adc_probe,

.remove    = __devexit_p(gsc3280_adc_remove),

.suspend    = gsc3280_adc_suspend,

.resume    = gsc3280_adc_resume,

};

static int __init gsc3280_adc_init(void)

{

int ret = 0;

ret = platform_driver_register(&gsc3280adc_driver);

if (ret != 0)

DBG("!!!!!!gsc adc core register error!!!!!!\n");

return ret;

}

static void __exit gsc3280_adc_exit(void)

{

platform_driver_unregister(&gsc3280adc_driver);

}

module_init(gsc3280_adc_init);

module_exit(gsc3280_adc_exit);

MODULE_AUTHOR("Davied");

MODULE_DESCRIPTION("gsc3280 spi0 adc Driver");

MODULE_LICENSE("GPL");

MODULE_ALIAS("platform:gsc3280-spi0 adc");

在这里将设备定义为平台设备,驱动注册函数即为平台驱动的注册。

2.2、探测函数

接下来看下平台驱动的探测函数,程序如下:

点击(此处)折叠或打开

static int __devinit gsc3280_adc_probe(struct platform_device *pdev)

{

int ret = 0, size = 0;

unsigned long rate = 0;

struct gsc_adc_dev *adc;

struct resource *mem, *ioarea;

DBG("############\n");

printk(KERN_INFO "GSC3280 spi0 adc probe start\n");

adc = kzalloc(sizeof(struct gsc_adc_dev), GFP_KERNEL);

if (adc == NULL) {

DBG("failed to allocate adc_core_dev\n");

return -ENOMEM;

}

mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);

if (!mem) {

DBG("no mem resource.\n");

ret = -EINVAL;

goto err_alloc;

}

size = resource_size(mem);

ioarea = request_mem_region(mem->start, size, pdev->name);

if (!ioarea) {

DBG("SPI region already claimed.\n");

ret = -EBUSY;

goto err_alloc;

}

adc->regs = ioremap_nocache(mem->start, resource_size(mem));

if (!adc->regs) {

DBG("SPI region already mapped.\n");

ret = -ENOMEM;

goto err_mem;

}

DBG("probe: mapped spi0 base=%p.\n", adc->regs);

adc->clk = clk_get(NULL, "spi0");

if (IS_ERR(adc->clk)) {

DBG("failed to find watchdog clock source.\n");

ret = PTR_ERR(adc->clk);

goto err_map;

}

rate = clk_get_rate(adc->clk);

DBG("rate is %ld.\n", rate);

clk_enable(adc->clk);

ret = adc_sysctl(adc);

if (ret != 0)

goto err_map;

spin_lock_init(&adc->lock);

INIT_LIST_HEAD(&adc->device_entry);

strlcpy(adc->name, GSC3280_ADC_NAME, sizeof(adc->name));

mutex_lock(&gsc3280_adc_list_lock);

list_add(&adc->device_entry, &gsc3280_adc_list);

mutex_unlock(&gsc3280_adc_list_lock);

adc->adc_dev=adc_device_register(adc->name, &pdev->dev, &gsc3280_adc_ops,THIS_MODULE);

if (IS_ERR(adc->adc_dev)) {

ret = PTR_ERR(adc->adc_dev);

DBG("unable to register the class device\n");

goto err_clk;

}

platform_set_drvdata(pdev, adc);

printk(KERN_INFO "GSC3280 adc probe SUCCESS.\n");

DBG("############\n");

return 0;

err_clk:

clk_disable(adc->clk);

clk_put(adc->clk);

err_map:

iounmap(adc->regs);

err_mem:

release_mem_region(mem->start, size);

mem = NULL;

err_alloc:

kfree(adc);

printk(KERN_INFO "!!!!!!GSC3280 adc probe error!!!!!!\n");

return ret;

}

说明:

1、首先申请驱动结构体内存。

2、对资源的申请和映射,包括IO内存等。

3、使能spi0时钟。

4、配置系统控制寄存器--ret=adc_sysctl(adc)。

5、驱动结构体成员初始化。

6、adc子系统注册,此处尤为重要,将在(三)中讲述。

7、通过gsc3280_adc_list_lock和gsc3280_adc_list,使得在其他函数中也能找到1中定义的驱动结构体内存。

8、在红色部分程序中,注册了文件操作函数集gsc3280_adc_ops,在第二篇文章中会多次使用此函数,具体定

义如下:

点击(此处)折叠或打开

staticconststruct adc_class_ops gsc3280_adc_ops= {

.convert=gsc3280AdcCon,

};

这里只定义一个转换函数gsc3280AdcCon,就是底层驱动程序提供的对spi0操作的函数,上层调用的AD转换动

作,最终都是由此程序来完成的。具体程序如下:

点击(此处)折叠或打开

//return 0:date valid, other:date error

staticint gsc3280AdcCon(unsigned short cmd)

{

struct gsc_adc_dev *adc;

int ret = 0, status = 0/*, cnt = 0*/;

//DBG("gscAdcCon\n");

mutex_lock(&gsc3280_adc_list_lock);

list_for_each_entry(adc, &gsc3280_adc_list, device_entry) {

if(strcmp(adc->name, GSC3280_ADC_NAME) == 0) {

status = 0;

break;

}

}

mutex_unlock(&gsc3280_adc_list_lock);

if (status != 0) {

DBG("get gsc3280 adc struct error\n");

return -5;

}

adc->cmd = cmd;

ret = writeSpiDate(adc);    //send test cmd

if (ret < 0) {

//DBG("cmd = %x\n", adc->cmd);

return ret;

}

ret = readSpiDate(adc);

if (ret < 0) {

//DBG("result = %x\n", adc->result);

return ret;

}

if (adc->result != ((adc->cmd >> 12) | 0x8000)) {

DBG("cmd error\n");

return CMD_ERR;

}

again:

adc->cmd = CMD_GSC_ADC_NOP;

ret = writeSpiDate(adc);    //send nop cmd

if (ret < 0) {

DBG("send nop cmd error\n");

return ret;

}

ret = readSpiDate(adc);

if (ret < 0) {

DBG("in read result = %x\n", adc->result);

return ret;

}

if ((adc->result & 0xf000) == 0xf000)

goto again;

if ((adc->result & 0xf000) == 0) {

adc->result &= 0x0fff;

DBG("get result success, result = %d\n", adc->result);

return adc->result;

} else {

DBG("get adc result error, result = %d\n", adc->result);

return RESULT_ERR;

}

}

使用spi读写数据函数如下:

点击(此处)折叠或打开

//ret: 1:busy, 0:free

static int getSpiState(struct gsc_adc_dev *adc)

{

unsigned int time_cnt = 0;

while (readl(adc->regs + GSC_SPI_SR) & GSC_SPI_SR_BUSY) {

if (time_cnt++ > MAX_WAIT_CNT) {

DBG("spi busy, stat = %x\n", readl(adc->regs + GSC_SPI_SR));

return SPI_BUSY;

}

}

return 0;

}

static int writeSpiDate(struct gsc_adc_dev *adc)

{

int cnt = 0, stat = 0;

stat = getSpiState(adc);

if (stat != 0) {

DBG("in write spi date,spi is busy\n");

return stat;

}

//spi0 fifo can write, transmit fifo empty

while (!(readl(adc->regs + GSC_SPI_SR) & GSC_SPI_SR_TX_NO_FULL)) {

if (cnt++ > MAX_WAIT_CNT) {

DBG("write spi date error, stat = %x\n", readl(adc->regs + GSC_SPI_SR));

return WRITE_DATE_ERR;

}

}

writel(adc->cmd, adc->regs + GSC_SPI_DA_S);

return 0;

}

/* prepare to read data from adc */

static int readSpiDate(struct gsc_adc_dev *adc)

{

int cnt= 0, stat = 0;

stat = getSpiState(adc);

if (stat < 0) {

DBG("in read spi date,spi is busy\n");

return stat;

}

//spi0 fifo receive not empty

while (!(readl(adc->regs + GSC_SPI_SR) & GSC_SPI_SR_RX_N_EMPTY)) {

if (cnt++ > MAX_WAIT_CNT) {

DBG("read spi date error, spi stat = %x\n", readl(adc->regs + GSC_SPI_SR));

return READ_DATE_ERR;

}

}

adc->result = (unsigned short)readl(adc->regs + GSC_SPI_DA_S);

return 0;

}

2.3、移除函数--gsc3280_adc_remove

移除函数就是探测函数的相反过程,程序如下:

点击(此处)折叠或打开

static int __devexit gsc3280_adc_remove(struct platform_device *pdev)

{

struct gsc_adc_dev *adc = platform_get_drvdata(pdev);

iounmap(adc->regs);

clk_disable(adc->clk);

clk_put(adc->clk);

adc_device_unregister(adc->adc_dev);

kfree(adc);

return 0;

}

三、ADC子系统核心(adc-core.c)

adc-core.c是gsc3280_adc.c的上面一层,提供了ADC子系统的一些公共函数,让各个ADC驱动注册集成到

linux内核中,向驱动程序提供了注册/注销接口。首先还是先看下模块初始化和退出函数。

3.1、模块初始化和退出函数

点击(此处)折叠或打开

static int __init gsc_adc_init(void)

{

adc_class = class_create(THIS_MODULE, "adc");

if (IS_ERR(adc_class)) {

printk(KERN_ERR "%s: couldn't create class\n", __FILE__);

return PTR_ERR(adc_class);

}

//adc_class->suspend = adcSuspend;

//adc_class->resume = adcResume;

adc_dev_init();

adc_sysfs_init(adc_class);

writel(0x01, (volatile unsigned int *)0xbc04a0ac);    //enable ts and adc

return 0;

}

static void __exit gsc_adc_exit(void)

{

adc_dev_exit();

class_destroy(adc_class);

idr_destroy(&adc_idr);

}

subsys_initcall(gsc_adc_init);

module_exit(gsc_adc_exit);

说明:

2、对ADC子系统中的dev进行初始化,第二篇文章讲述。

3、对ADC子系统中的sysfs进行初始化,第二篇文章讲述。

4、注意:此处的初始化宏使用的是subsys_initcall,优先级高于module_init(),即subsys_initcall先于module_init()执行。

5、退出函数就是初始化函数的相反过程。

3.2、ADC子系统注册和注销函数

现在就来看下2.2中涉及到的ADC子系统注册函数。程序如下:

点击(此处)折叠或打开

struct class *adc_class;

static DEFINE_IDR(adc_idr);

static DEFINE_MUTEX(adc_idr_lock);

static void adc_device_release(struct device *dev)

{

struct adc_core_dev *adc = to_adc_device(dev);

mutex_lock(&adc_idr_lock);

idr_remove(&adc_idr, adc->id);

mutex_unlock(&adc_idr_lock);

kfree(adc);

}

/**

* adc_device_register - register w/ ADC class

* @dev: the device to register

*

* adc_device_unregister() must be called when the class device is no

* longer needed.

*

* Returns the pointer to the new struct class device.

*/

struct adc_core_dev *adc_device_register(const char *name, struct device *dev,

const struct adc_class_ops *ops,

struct module *owner)

{

struct adc_core_dev *adc;

int id, err;

if (idr_pre_get(&adc_idr, GFP_KERNEL) == 0) {

err = -ENOMEM;

goto exit;

}

mutex_lock(&adc_idr_lock);

err = idr_get_new(&adc_idr, NULL, &id);

mutex_unlock(&adc_idr_lock);

if (err < 0)

goto exit;

id = id & MAX_ID_MASK;

adc= kzalloc(sizeof(struct adc_core_dev), GFP_KERNEL);

if (adc == NULL) {

err = -ENOMEM;

goto exit_idr;

}

adc->id = id;

adc->ops = ops;

adc->owner = owner;

adc->dev.parent = dev;

adc->dev.class = adc_class;

adc->dev.release = adc_device_release;

mutex_init(&adc->ops_lock);

strlcpy(adc->name, name, ADC_CORE_NAME_SIZE);

dev_set_name(&adc->dev, "adc%d", id);

adc_dev_prepare(adc);

err = device_register(&adc->dev);

if (err) {

put_device(&adc->dev);

goto exit_kfree;

}

#ifdef CONFIG_TOUCHSCREEN_GSC3280

err = adc_ts_add_dev(adc);

if (err < 0)

DBG("adc ts add dev error\n");

#endif

adc_dev_add_device(adc);

adc_sysfs_add_device(adc);

adc_proc_add_device(adc);

dev_info(dev, "adc core: registered %s as %s\n", adc->name, dev_name(&adc->dev));

return adc;

exit_kfree:

kfree(adc);

exit_idr:

mutex_lock(&adc_idr_lock);

idr_remove(&adc_idr, id);

mutex_unlock(&adc_idr_lock);

exit:

dev_err(dev, "adc core: unable to register %s, err = %d\n", name, err);

return ERR_PTR(err);

}

EXPORT_SYMBOL_GPL(adc_device_register);

/**

* adc_device_unregister - removes the previously registered ADC class device

*

* @adc: the ADC class device to destroy

*/

void adc_device_unregister(struct adc_core_dev *adc)

{

if (get_device(&adc->dev) != NULL) {

mutex_lock(&adc->ops_lock);

adc_sysfs_del_device(adc);

adc_dev_del_device(adc);

adc_proc_del_device(adc);

device_unregister(&adc->dev);

adc->ops = NULL;

mutex_unlock(&adc->ops_lock);

put_device(&adc->dev);

}

}

EXPORT_SYMBOL_GPL(adc_device_unregister);

说明:

1、首先使用idr机制获取id。

2、申请结构体内存,初始化成员变量。

4、增加dev、proc和sysfs设备,第二篇文章中讲述。

5、注销函数是注册函数的相反过程。

6、释放函数就是移除idr,释放结构体内存。

四、Kconfig和Makefile编写

首先我们在Linux内核源码目录下的/driver/spi/adc目录下创建Kconfig和Makefile文件。

4.1、Kconfig

Kconfig程序如下:

点击(此处)折叠或打开

#

# Sensor device configuration

# add by hdw,in order to use adc

#

menuconfig SPI0_ADC

bool "Adc Hardware support"

help

GSC3280 Adc Hardware support.

if SPI0_ADC

config GSC_SPI0_ADC_CORE

tristate "support adc core"

default SPI0_ADC

help

If you say yes to this option, support will be included gsc3280

adc core.

config GSC_ADC_CORE_DEBUG

bool "adc core debugging messages"

depends on GSC_SPI0_ADC_CORE

help

Say Y here if you want the GSC3280 to produce a bunch of debug

messages to the system log. Select this if you are having a

problem with GSC3280 and want to see more of what is going on.

comment "ADC interfaces"

config ADC_INTF_SYSFS

boolean "/sys/class/adc/adcN (sysfs)"

depends on SYSFS

default SPI0_ADC

help

Say yes here if you want to use your ADCs using sysfs interfaces,

/sys/class/adc/adc0 through /sys/.../adcN.

If unsure, say Y.

config ADC_SYS_DEBUG

bool "adc sys debugging messages"

depends on ADC_INTF_SYSFS

help

Say Y here if you want the adc sysfs dev to produce a bunch of debug

messages to the system log. Select this if you are having a

problem with adc core and want to see more of what is going on.

config ADC_INTF_PROC

boolean "/proc/driver/adc (procfs for adc0)"

depends on PROC_FS

default SPI0_ADC

help

Say yes here if you want to use your first ADC through the proc

interface, /proc/driver/adc. Other ADCs will not be available

through that API.

If unsure, say Y.

config ADC_PROC_DEBUG

bool "adc proc debugging messages"

depends on ADC_INTF_PROC

help

Say Y here if you want the adc proc dev to produce a bunch of debug

messages to the system log. Select this if you are having a

problem with adc core and want to see more of what is going on.

config ADC_INTF_DEV

boolean "/dev/adcN (character devices)"

default SPI0_ADC

help

Say yes here if you want to use your ADCs using the /dev

interfaces, which "udev" sets up as /dev/adc0 through

/dev/adcN.

You may want to set up a symbolic link so one of these

can be accessed as /dev/adc, which is a name

expected by "hwclock" and some other programs. Recent

versions of "udev" are known to set up the symlink for you.

If unsure, say Y.

config ADC_DEV_DEBUG

bool "adc dev debugging messages"

depends on ADC_INTF_DEV

help

Say Y here if you want the adc dev to produce a bunch of debug

messages to the system log. Select this if you are having a

problem with adc core and want to see more of what is going on.

comment "ADC devices"

config GSC3280_ADC

tristate "support gsc3280 adc"

depends on GSC_SPI0_ADC_CORE

default SPI0_ADC

help

If you say yes to this option, support will be included gsc3280

adc convert and touchscreen.

config GSC3280_ADC_DEBUG

bool "GSC3280 adc debugging messages"

depends on GSC3280_ADC

help

Say Y here if you want the GSC3280 adc to produce a bunch of debug

messages to the system log. Select this if you are having a

problem with GSC3280 and want to see more of what is going on.

endif

在上一层的Kconfig,也就是Linux内核源码目录下的/driver/spi下的Kconfig增加如下内容:

点击(此处)折叠或打开

# add by hdw

comment "gsc3280 spi0 adc"

source drivers/spi/adc/Kconfig

这样在配置选项中,就可以配置ADC子系统的内容了。

4.2、Makefile

类似Kconfig,在Linux内核源码目录/driver/spi/adc目录下创建Makefile文件,具体内容如下:

点击(此处)折叠或打开

#

# Makefile for the i2c bus drivers.

#

obj-$(CONFIG_GSC_SPI0_ADC_CORE)        += adc-core.o

obj-$(CONFIG_ADC_INTF_DEV)            += adc-dev.o

obj-$(CONFIG_ADC_INTF_PROC)            += adc-proc.o

obj-$(CONFIG_ADC_INTF_SYSFS)            += adc-sysfs.o

obj-$(CONFIG_GSC3280_ADC)            += gsc3280_adc.o

ccflags-$(CONFIG_GSC_ADC_DEV_DEBUG) += -DGSC3280_ADC_DEV_DEBUG

在上一层的Makefile,也就是Linux内核源码目录下的/driver/spi下的Makefile增加如下内容:

点击(此处)折叠或打开

#add by hdw

obj-y                += adc/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值