一、芯片和驅(qū)動架構(gòu)總體介紹 | ||||
1.1、芯片介紹 | ||||
本文使用的芯片為GSC3280,根據(jù)芯片手冊的介紹,ADC 與觸摸屏控制器通過SPI 接口掛在GSC3280的SPI0總線上,支持4線電阻式觸摸屏或當(dāng)ADC輸入使用。GSC3280片上集成的ADC與觸摸屏控制器主要有以下特性: 1、通過標(biāo)準(zhǔn)SPI接口傳輸命令和數(shù)據(jù); 2、最大分辨率為12位; 3、輸入最大的SPI 時(shí)鐘是6MHz,對應(yīng)最大采樣率為120Ksps; 4、支持4線電阻式觸摸屏; 5、可以當(dāng)4路ADC輸入; 6、支持觸摸屏觸筆中斷; 7、支持低功耗模式; | ||||
1.2、驅(qū)動架構(gòu)介紹 | ||||
本文所設(shè)計(jì)的ADC子系統(tǒng)架構(gòu)關(guān)系圖如下: | ||||
由于AD轉(zhuǎn)換使用的是SPI0,所以此處在Linux內(nèi)核源碼目錄的/driver/spi目錄下建立一個(gè)adc目錄,根據(jù)上圖,建立了adc-core.c、adc-dev.c、adc-sysfs.c、adc-proc.c、adc.h和 gsc3280_adc.c等文件?,F(xiàn)在先簡單說下各個(gè)文件的功能: gsc3280_adc.c:是最底層的直接和硬件打交道的驅(qū)動文件,將在(二)中講述。 adc-core.c:gsc3280_adc.c的上面一層,提供了ADC子系統(tǒng)的一些公共函數(shù),讓各個(gè)ADC驅(qū)動注冊集成到linux內(nèi)核中,向驅(qū)動程序提供了注冊/注銷接口。將在第二篇文章中講述。 adc-dev.c:adc-core.c再往上就到了adc-dev.c,adc-dev.c最終生成了/dev/adc設(shè)備節(jié)點(diǎn),上層的應(yīng)用程序就是通過操作此文件來進(jìn)行相關(guān)的讀取AD轉(zhuǎn)換值等操作的。定義了基本的設(shè)備文件操作函數(shù),用戶程序與ADC驅(qū)動的接口函數(shù),這里定義了每個(gè)ioctl命令需要調(diào)用的函數(shù),還有open,read等。將在第二篇文章中講述。 adc-proc.c:與proc文件系統(tǒng)有關(guān),提供通過proc文件系統(tǒng)操作ADC。將在第二篇文章中講述。 adc-sysfs.c:與sysfs有關(guān),提供通過sys文件系統(tǒng)操作ADC。將在第二篇文章中講述。 adc.h、adc-core.h:定義了與ADC有關(guān)的數(shù)據(jù)結(jié)構(gòu),變量和函數(shù)聲明等。在使用時(shí)講述。 | ||||
二、驅(qū)動程序gsc3280_adc.c | ||||
2.1、模塊初始化 | ||||
本部分講述gsc3280_adc.c文件中的程序,此即為上圖中的最下部分--驅(qū)動程序,首先從模塊初始化開始,程序如下: 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、探測函數(shù) | ||||
接下來看下平臺驅(qū)動的探測函數(shù),程序如下: 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、首先申請驅(qū)動結(jié)構(gòu)體內(nèi)存。 2、對資源的申請和映射,包括IO內(nèi)存等。 3、使能spi0時(shí)鐘。 4、配置系統(tǒng)控制寄存器--ret = adc_sysctl(adc)。 5、驅(qū)動結(jié)構(gòu)體成員初始化。 6、adc子系統(tǒng)注冊,此處尤為重要,將在(三)中講述。 7、通過gsc3280_adc_list_lock和gsc3280_adc_list,使得在其他函數(shù)中也能找到1中定義的驅(qū)動結(jié)構(gòu)體內(nèi)存。 8、在紅色部分程序中,注冊了文件操作函數(shù)集gsc3280_adc_ops,在第二篇文章中會多次使用此函數(shù),具體定義如下: 1. static const struct adc_class_ops gsc3280_adc_ops = { 2. .convert = gsc3280AdcCon, 3. }; 這里只定義一個(gè)轉(zhuǎn)換函數(shù)gsc3280AdcCon,就是底層驅(qū)動程序提供的對spi0操作的函數(shù),上層調(diào)用的AD轉(zhuǎn)換動作,最終都是由此程序來完成的。具體程序如下: 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讀寫數(shù)據(jù)函數(shù)如下: 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、移除函數(shù)--gsc3280_adc_remove | ||||
移除函數(shù)就是探測函數(shù)的相反過程,程序如下: 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子系統(tǒng)核心(adc-core.c) | ||||
adc-core.c是gsc3280_adc.c的上面一層,提供了ADC子系統(tǒng)的一些公共函數(shù),讓各個(gè)ADC驅(qū)動注冊集成到linux內(nèi)核中,向驅(qū)動程序提供了注冊/注銷接口。首先還是先看下模塊初始化和退出函數(shù)。 | ||||
3.1、模塊初始化和退出函數(shù) | ||||
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、首先建立了一個(gè)設(shè)備類,在《GSC3280的ADC子系統(tǒng)驅(qū)動模型(三)----class的使用》中介紹。 2、對ADC子系統(tǒng)中的dev進(jìn)行初始化,第二篇文章講述。 3、對ADC子系統(tǒng)中的sysfs進(jìn)行初始化,第二篇文章講述。 4、注意:此處的初始化宏使用的是subsys_initcall,優(yōu)先級高于module_init(),即subsys_initcall先于module_init()執(zhí)行。 5、退出函數(shù)就是初始化函數(shù)的相反過程。 | ||||
3.2、ADC子系統(tǒng)注冊和注銷函數(shù) | ||||
現(xiàn)在就來看下2.2中涉及到的ADC子系統(tǒng)注冊函數(shù)。程序如下: 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機(jī)制獲取id。 2、申請結(jié)構(gòu)體內(nèi)存,初始化成員變量。 3、注冊device,在《GSC3280的ADC子系統(tǒng)驅(qū)動模型(三)----class的使用》中介紹。 4、增加dev、proc和sysfs設(shè)備,第二篇文章中講述。 5、注銷函數(shù)是注冊函數(shù)的相反過程。 6、釋放函數(shù)就是移除idr,釋放結(jié)構(gòu)體內(nèi)存。 | ||||
四、Kconfig和Makefile編寫 | ||||
首先我們在Linux內(nèi)核源碼目錄下的/driver/spi/adc目錄下創(chuàng)建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內(nèi)核源碼目錄下的/driver/spi下的Kconfig增加如下內(nèi)容: 這樣在配置選項(xiàng)中,就可以配置ADC子系統(tǒng)的內(nèi)容了。 | ||||
4.2、Makefile | ||||
類似Kconfig,在Linux內(nèi)核源碼目錄/driver/spi/adc目錄下創(chuàng)建Makefile文件,具體內(nèi)容如下: 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內(nèi)核源碼目錄下的/driver/spi下的Makefile增加如下內(nèi)容 1. #add by hdw 2. obj-y += adc/ | ||||
原文參見:http://blog.chinaunix.net/uid-25445243-id-4010249.html | ||||