技术文摘

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子系统架构关系图如下:

驱动架构介绍.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文件中的程序,此即为上图中的最下部分--驱动程序,首先从模块初始化开始,程序如下:

1.       static struct platform_driver gsc3280adc_driver = {

2.           .driver    = {

3.               .name    = "adc-core",

4.               .owner    = THIS_MODULE,

5.           },

6.           .probe    = gsc3280_adc_probe,

7.           .remove    = __devexit_p(gsc3280_adc_remove),

8.           .suspend    = gsc3280_adc_suspend,

9.           .resume    = gsc3280_adc_resume,

10.      };

11.       

12.      static int __init gsc3280_adc_init(void)

13.      {

14.          int ret = 0;

15.          

16.          ret = platform_driver_register(&gsc3280adc_driver);

17.          if (ret != 0)

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

19.          return ret;

20.      }

21.      static void __exit gsc3280_adc_exit(void)

22.      {

23.          platform_driver_unregister(&gsc3280adc_driver);

24.      }

25.      module_init(gsc3280_adc_init);

26.      module_exit(gsc3280_adc_exit);

27.       

28.      MODULE_AUTHOR("Davied<apple_guet@126.com>");

29.      MODULE_DESCRIPTION("gsc3280 spi0 adc Driver");

30.      MODULE_LICENSE("GPL");

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


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







2.2、探测函数

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

1.       static int __devinit gsc3280_adc_probe(struct platform_device *pdev)

2.       {

3.           int ret = 0, size = 0;

4.           unsigned long rate = 0;

5.           struct gsc_adc_dev *adc;

6.           struct resource *mem, *ioarea;

7.        

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

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

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

11.          if (adc == NULL) {

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

13.              return -ENOMEM;

14.          }

15.          mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 

16.          if (!mem) {

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

18.              ret = -EINVAL;

19.              goto err_alloc;

20.          }

21.          size = resource_size(mem);

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

23.          if (!ioarea) {

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

25.              ret = -EBUSY;

26.              goto err_alloc; 

27.          } 

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

29.          if (!adc->regs) {

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

31.              ret = -ENOMEM;

32.              goto err_mem;

33.          }

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

35.       

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

37.          if (IS_ERR(adc->clk)) {

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

39.              ret = PTR_ERR(adc->clk);

40.              goto err_map;

41.          }

42.          rate = clk_get_rate(adc->clk);

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

44.          clk_enable(adc->clk);

45.          ret = adc_sysctl(adc);

46.          if (ret != 0)

47.              goto err_map;

48.          

49.          spin_lock_init(&adc->lock);

50.          INIT_LIST_HEAD(&adc->device_entry);

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

52.       

53.          mutex_lock(&gsc3280_adc_list_lock);

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

55.          mutex_unlock(&gsc3280_adc_list_lock);

56.       

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

58.          if (IS_ERR(adc->adc_dev)) {

59.              ret = PTR_ERR(adc->adc_dev);

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

61.              goto err_clk;

62.          }

63.       

64.          platform_set_drvdata(pdev, adc);

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

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

67.          return 0;

68.       

69.       err_clk:

70.          clk_disable(adc->clk);

71.          clk_put(adc->clk);

72.       err_map:

73.          iounmap(adc->regs);

74.       err_mem:

75.          release_mem_region(mem->start, size);

76.          mem = NULL;

77.       err_alloc:

78.          kfree(adc);

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

80.          return ret;

81.      }


说明:

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,在第二篇文章中会多次使用此函数,具体定义如下:

1.       static const struct adc_class_ops gsc3280_adc_ops = {

2.           .convert = gsc3280AdcCon,

3.       };

这里只定义一个转换函数gsc3280AdcCon,就是底层驱动程序提供的对spi0操作的函数,上层调用的AD转换动作,最终都是由此程序来完成的。具体程序如下:
1.       //return 0:date valid, other:date error
2.       static int gsc3280AdcCon(unsigned short cmd)
3.       {
4.           struct gsc_adc_dev *adc;

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

6.        

7.           //DBG("gscAdcCon\n");

8.           mutex_lock(&gsc3280_adc_list_lock);

9.           list_for_each_entry(adc, &gsc3280_adc_list, device_entry) {

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

11.                  status = 0;

12.                  break;

13.              }

14.          }

15.          mutex_unlock(&gsc3280_adc_list_lock);

16.          if (status != 0) {

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

18.              return -5;

19.          }

20.       

21.          adc->cmd = cmd;

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

23.          if (ret < 0) {

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

25.              return ret;

26.          }

27.          ret = readSpiDate(adc);

28.          if (ret < 0) {

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

30.              return ret;

31.          }

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

33.              DBG("cmd error\n");

34.              return CMD_ERR;

35.          }

36.      again:

37.          adc->cmd = CMD_GSC_ADC_NOP;

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

39.          if (ret < 0) {

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

41.              return ret;

42.          }

43.          ret = readSpiDate(adc);

44.          if (ret < 0) {

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

46.              return ret;

47.          }

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

49.              goto again;

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

51.              adc->result &= 0x0fff;

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

53.              return adc->result;

54.          } else {

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

56.              return RESULT_ERR;

57.          }

58.      }


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

1.       //ret: 1:busy, 0:free

2.       static int getSpiState(struct gsc_adc_dev *adc)

3.       {

4.           unsigned int time_cnt = 0;

5.        

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

7.               if (time_cnt++ > MAX_WAIT_CNT) {

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

9.                   return SPI_BUSY;

10.              }

11.          }

12.          return 0;

13.      }

14.       

15.      static int writeSpiDate(struct gsc_adc_dev *adc)

16.      {

17.          int cnt = 0, stat = 0;

18.          

19.          stat = getSpiState(adc);

20.          if (stat != 0) {

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

22.              return stat;

23.          }

24.          //spi0 fifo can write, transmit fifo empty

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

26.              if (cnt++ > MAX_WAIT_CNT) {

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

28.                  return WRITE_DATE_ERR;

29.              }

30.          }

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

32.          return 0;

33.      }

34.       

35.      /* prepare to read data from adc */

36.      static int readSpiDate(struct gsc_adc_dev *adc)

37.      {

38.          int cnt= 0, stat = 0;

39.          

40.          stat = getSpiState(adc);

41.          if (stat < 0) {

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

43.              return stat;

44.          }

45.          //spi0 fifo receive not empty

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

47.              if (cnt++ > MAX_WAIT_CNT) {

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

49.                  return READ_DATE_ERR;

50.              }

51.          }

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

53.          return 0;

54.      }








2.3、移除函数--gsc3280_adc_remove

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

1.     static int __devexit gsc3280_adc_remove(struct platform_device *pdev)

2.     {

3.         struct gsc_adc_dev *adc = platform_get_drvdata(pdev);

4.      

5.         iounmap(adc->regs);

6.         clk_disable(adc->clk);

7.         clk_put(adc->clk);

8.         adc_device_unregister(adc->adc_dev);

9.         kfree(adc);

10.        return 0;

11.    }







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

adc-core.c是gsc3280_adc.c的上面一层,提供了ADC子系统的一些公共函数,让各个ADC驱动注册集成到linux内核中,向驱动程序提供了注册/注销接口。首先还是先看下模块初始化和退出函数。

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


1.     static int __init gsc_adc_init(void)

2.     {

3.         adc_class = class_create(THIS_MODULE, "adc");

4.         if (IS_ERR(adc_class)) {

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

6.             return PTR_ERR(adc_class);

7.         }

8.         //adc_class->suspend = adcSuspend;

9.         //adc_class->resume = adcResume;

10.        adc_dev_init();

11.        adc_sysfs_init(adc_class);

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

13.        return 0;

14.    }

15.    static void __exit gsc_adc_exit(void)

16.    {

17.        adc_dev_exit();

18.        class_destroy(adc_class);

19.        idr_destroy(&adc_idr);

20.    }

21.    subsys_initcall(gsc_adc_init);

22.    module_exit(gsc_adc_exit);


说明:

1、首先建立了一个设备类,在GSC3280的ADC子系统驱动模型(三)----class的使用中介绍。

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

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

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

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







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

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

1.       struct class *adc_class;

2.       static DEFINE_IDR(adc_idr);

3.       static DEFINE_MUTEX(adc_idr_lock);

4.        

5.       static void adc_device_release(struct device *dev)

6.       {

7.           struct adc_core_dev *adc = to_adc_device(dev);

8.           

9.           mutex_lock(&adc_idr_lock);

10.          idr_remove(&adc_idr, adc->id);

11.          mutex_unlock(&adc_idr_lock);

12.          kfree(adc);

13.      }

14.       

15.      /**

16.       * adc_device_register - register w/ ADC class

17.       * @dev: the device to register

18.       *

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

20.       * longer needed.

21.       *

22.       * Returns the pointer to the new struct class device.

23.       */

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

25.                                              const struct adc_class_ops *ops,

26.                                              struct module *owner)

27.      {

28.          struct adc_core_dev *adc;

29.          int id, err;

30.       

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

32.              err = -ENOMEM;

33.              goto exit;

34.          }

35.          mutex_lock(&adc_idr_lock);

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

37.          mutex_unlock(&adc_idr_lock);

38.          if (err < 0)

39.              goto exit;

40.          id = id & MAX_ID_MASK;

41.       

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

43.          if (adc == NULL) {

44.              err = -ENOMEM;

45.              goto exit_idr;

46.          }

47.       

48.          adc->id = id;

49.          adc->ops = ops;

50.          adc->owner = owner;

51.          adc->dev.parent = dev;

52.          adc->dev.class = adc_class;

53.          adc->dev.release = adc_device_release;

54.       

55.          mutex_init(&adc->ops_lock);

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

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

58.       

59.          adc_dev_prepare(adc);

60.       

61.          err = device_register(&adc->dev);

62.          if (err) {

63.              put_device(&adc->dev);

64.              goto exit_kfree;

65.          }

66.       

67.      #ifdef CONFIG_TOUCHSCREEN_GSC3280

68.          err = adc_ts_add_dev(adc);

69.          if (err < 0)

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

71.      #endif

72.       

73.          adc_dev_add_device(adc);

74.          adc_sysfs_add_device(adc);

75.          adc_proc_add_device(adc);

76.       

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

78.          return adc;

79.       

80.      exit_kfree:

81.          kfree(adc);

82.      exit_idr:

83.          mutex_lock(&adc_idr_lock);

84.          idr_remove(&adc_idr, id);

85.          mutex_unlock(&adc_idr_lock);

86.      exit:

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

88.          return ERR_PTR(err);

89.      }

90.      EXPORT_SYMBOL_GPL(adc_device_register);

91.       

92.      /**

93.       * adc_device_unregister - removes the previously registered ADC class device

94.       *

95.       * @adc: the ADC class device to destroy

96.       */

97.      void adc_device_unregister(struct adc_core_dev *adc)

98.      {

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

100.            mutex_lock(&adc->ops_lock);

101.            adc_sysfs_del_device(adc);

102.            adc_dev_del_device(adc);

103.            adc_proc_del_device(adc);

104.            device_unregister(&adc->dev);

105.            adc->ops = NULL;

106.            mutex_unlock(&adc->ops_lock);

107.            put_device(&adc->dev);

108.        }

109.    }

110.    EXPORT_SYMBOL_GPL(adc_device_unregister);


说明:

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

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

3、注册device,在GSC3280的ADC子系统驱动模型(三)----class的使用中介绍。

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

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

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







四、Kconfig和Makefile编写

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

4.1、Kconfig

Kconfig程序如下:

1.       #

2.       # Sensor device configuration

3.       # add by hdw,in order to use adc

4.       #

5.        

6.       menuconfig SPI0_ADC

7.           bool "Adc Hardware support"

8.           help

9.            GSC3280 Adc Hardware support.

10.       

11.       

12.      if SPI0_ADC

13.       

14.      config GSC_SPI0_ADC_CORE

15.          tristate "support adc core"

16.          default SPI0_ADC

17.          help

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

19.           adc core.

20.       

21.      config GSC_ADC_CORE_DEBUG

22.          bool "adc core debugging messages"

23.          depends on GSC_SPI0_ADC_CORE

24.          help

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

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

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

28.       

29.      comment "ADC interfaces"

30.       

31.      config ADC_INTF_SYSFS

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

33.          depends on SYSFS

34.          default SPI0_ADC

35.          help

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

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

38.       

39.           If unsure, say Y.

40.       

41.      config ADC_SYS_DEBUG

42.          bool "adc sys debugging messages"

43.          depends on ADC_INTF_SYSFS

44.          help

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

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

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

48.       

49.      config ADC_INTF_PROC

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

51.          depends on PROC_FS

52.          default SPI0_ADC

53.          help

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

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

56.           through that API.

57.       

58.           If unsure, say Y.

59.       

60.      config ADC_PROC_DEBUG

61.          bool "adc proc debugging messages"

62.          depends on ADC_INTF_PROC

63.          help

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

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

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

67.       

68.      config ADC_INTF_DEV

69.          boolean "/dev/adcN (character devices)"

70.          default SPI0_ADC

71.          help

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

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

74.           /dev/adcN.

75.       

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

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

78.           expected by "hwclock" and some other programs. Recent

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

80.       

81.           If unsure, say Y.

82.       

83.      config ADC_DEV_DEBUG

84.          bool "adc dev debugging messages"

85.          depends on ADC_INTF_DEV

86.          help

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

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

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

90.       

91.       

92.      comment "ADC devices"

93.       

94.      config GSC3280_ADC

95.          tristate "support gsc3280 adc"

96.          depends on GSC_SPI0_ADC_CORE

97.          default SPI0_ADC

98.          help

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

100.         adc convert and touchscreen.

101.     

102.    config GSC3280_ADC_DEBUG

103.        bool "GSC3280 adc debugging messages"

104.        depends on GSC3280_ADC

105.        help

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

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

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

109.         

110.     

111.    endif


在上一层的Kconfig,也就是Linux内核源码目录下的/driver/spi下的Kconfig增加如下内容:
1.       # add by hdw
2.       comment "gsc3280 spi0 adc"
3.       source drivers/spi/adc/Kconfig

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







4.2、Makefile

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

1.       #

2.       # Makefile for the i2c bus drivers.

3.       #

4.        

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

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

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

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

9.        

10.      obj-$(CONFIG_GSC3280_ADC)            += gsc3280_adc.o

11.       

12.      ccflags-$(CONFIG_GSC_ADC_DEV_DEBUG) += -DGSC3280_ADC_DEV_DEBUG


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

1.       #add by hdw

2.       obj-y                += adc/




原文参见:http://blog.chinaunix.net/uid-25445243-id-4010249.html