新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > S3C2440驅(qū)動簡析——串口驅(qū)動

S3C2440驅(qū)動簡析——串口驅(qū)動

作者: 時間:2016-11-21 來源:網(wǎng)絡(luò) 收藏
對于驅(qū)動的學習停歇了幾乎一周的時間,期間忙于補習Linux應(yīng)用編程和搜索驅(qū)動、內(nèi)核相關(guān)書籍,以便之后更進一步地學習。在之前友善提供的驅(qū)動例程里面,涉及的知識面非常有限,需要研究更多的驅(qū)動源碼,了解更多的驅(qū)動知識,是當務(wù)之急。研究別人代碼的同時,當然不忘自己也要動手練習。以下貼出串口驅(qū)動程序,并在程序里附上簡要注釋。
[c-sharp]view plaincopy
  1. // linux/drivers/serial/s3c2440.c
  2. *
  3. *DriverforSamsungS3C2440andS3C2442SoConboardUARTs.
  4. *
  5. *BenDooks,Copyright(c)2003-2005,2008SimtecElectronics
  6. *http://armlinux.simtec.co.uk/
  7. *
  8. *Thisprogramisfreesoftware;youcanredistributeitand/ormodify
  9. *itunderthetermsoftheGNUGeneralPublicLicenseversion2as
  10. *publishedbytheFreeSoftwareFoundation.
  11. //
  12. #include"linux/module.h"
  13. #include"linux/ioport.h"
  14. #include"linux/io.h"
  15. #include"linux/platform_device.h"
  16. #include"linux/init.h"
  17. #include"linux/serial_core.h"
  18. #include"linux/serial.h"
  19. #include"asm/irq.h"
  20. #include"mach/hardware.h"
  21. #include"plat/regs-serial.h"
  22. #include"mach/regs-gpio.h"
  23. #include"samsung.h"
  24. staticints3c2440_serial_setsource(structuart_port*port,
  25. structs3c24xx_uart_clksrc*clk)
  26. {//本函數(shù)選定串口端口和時鐘源
  27. unsignedlongucon=rd_regl(port,S3C2410_UCON);//讀取寄存器UCON
  28. // todo-properfclk<>nonfclkswitch.//
  29. ucon&=~S3C2440_UCON_CLKMASK;//#defineS3C2440_UCON_CLKMASK(3<<10)
  30. if(strcmp(clk->name,"uclk")==0)//選擇時鐘源
  31. ucon|=S3C2440_UCON_UCLK;
  32. elseif(strcmp(clk->name,"pclk")==0)
  33. ucon|=S3C2440_UCON_PCLK;
  34. elseif(strcmp(clk->name,"fclk")==0)
  35. ucon|=S3C2440_UCON_FCLK;
  36. else{
  37. printk(KERN_ERR"unknownclocksource%s/n",clk->name);
  38. return-EINVAL;
  39. }
  40. wr_regl(port,S3C2410_UCON,ucon);//把設(shè)置過的ucon寫回串口控制寄存器
  41. return0;
  42. }
  43. staticints3c2440_serial_getsource(structuart_port*port,
  44. structs3c24xx_uart_clksrc*clk)
  45. {//設(shè)置時鐘源和對應(yīng)預(yù)分頻值
  46. unsignedlongucon=rd_regl(port,S3C2410_UCON);
  47. unsignedlongucon0,ucon1,ucon2;
  48. switch(ucon&S3C2440_UCON_CLKMASK){
  49. caseS3C2440_UCON_UCLK:
  50. clk->divisor=1;
  51. clk->name="uclk";
  52. break;
  53. caseS3C2440_UCON_PCLK:
  54. caseS3C2440_UCON_PCLK2:
  55. clk->divisor=1;
  56. clk->name="pclk";
  57. break;
  58. caseS3C2440_UCON_FCLK:
  59. //thefunofcalculatingtheuartdivisorson
  60. *thes3c2440//
  61. ucon0=__raw_readl(S3C24XX_VA_UART0+S3C2410_UCON);
  62. ucon1=__raw_readl(S3C24XX_VA_UART1+S3C2410_UCON);
  63. ucon2=__raw_readl(S3C24XX_VA_UART2+S3C2410_UCON);
  64. printk("ucons:lx,lx,lx/n",ucon0,ucon1,ucon2);
  65. ucon0&=S3C2440_UCON0_DIVMASK;
  66. ucon1&=S3C2440_UCON1_DIVMASK;
  67. ucon2&=S3C2440_UCON2_DIVMASK;
  68. if(ucon0!=0){
  69. clk->divisor=ucon0>>S3C2440_UCON_DIVSHIFT;
  70. clk->divisor+=6;
  71. }elseif(ucon1!=0){
  72. clk->divisor=ucon1>>S3C2440_UCON_DIVSHIFT;
  73. clk->divisor+=21;
  74. }elseif(ucon2!=0){
  75. clk->divisor=ucon2>>S3C2440_UCON_DIVSHIFT;
  76. clk->divisor+=36;
  77. }else{
  78. //manualcalims44,seemstobe9//
  79. clk->divisor=9;
  80. }
  81. clk->name="fclk";
  82. break;
  83. }
  84. return0;
  85. }
  86. staticints3c2440_serial_resetport(structuart_port*port,
  87. structs3c2410_uartcfg*cfg)
  88. {//重設(shè)串口
  89. unsignedlongucon=rd_regl(port,S3C2410_UCON);
  90. dbg("s3c2440_serial_resetport:port=%p(lx),cfg=%p/n",
  91. port,port->mapbase,cfg);
  92. //ensurewedontchangetheclocksettings...//
  93. ucon&=(S3C2440_UCON0_DIVMASK|(3<<10));
  94. wr_regl(port,S3C2410_UCON,ucon|cfg->ucon);//重新設(shè)置寄存器UCON
  95. wr_regl(port,S3C2410_ULCON,cfg->ulcon);//重新設(shè)置寄存器ULCON
  96. //resetbothfifos//
  97. wr_regl(port,S3C2410_UFCON,cfg->ufcon|S3C2410_UFCON_RESETBOTH);//重啟fifo
  98. wr_regl(port,S3C2410_UFCON,cfg->ufcon);//重新設(shè)定寄存器UFCON
  99. return0;
  100. }
  101. staticstructs3c24xx_uart_infos3c2440_uart_inf={//串口設(shè)備環(huán)境信息和提供的操作函數(shù)
  102. .name="SamsungS3C2440UART",
  103. .type=PORT_S3C2440,
  104. .fifosize=64,
  105. .rx_fifomask=S3C2440_UFSTAT_RXMASK,
  106. .rx_fifoshift=S3C2440_UFSTAT_RXSHIFT,
  107. .rx_fifofull=S3C2440_UFSTAT_RXFULL,
  108. .tx_fifofull=S3C2440_UFSTAT_TXFULL,
  109. .tx_fifomask=S3C2440_UFSTAT_TXMASK,
  110. .tx_fifoshift=S3C2440_UFSTAT_TXSHIFT,
  111. .get_clksrc=s3c2440_serial_getsource,
  112. .set_clksrc=s3c2440_serial_setsource,
  113. .reset_port=s3c2440_serial_resetport,
  114. };
  115. //devicemanagement//
  116. staticints3c2440_serial_probe(structplatform_device*dev)
  117. {//完成串口的添加
  118. dbg("s3c2440_serial_probe:dev=%p/n",dev);
  119. returns3c24xx_serial_probe(dev,&s3c2440_uart_inf);
  120. }
  121. staticstructplatform_drivers3c2440_serial_driver={//注冊串口設(shè)備
  122. .probe=s3c2440_serial_probe,
  123. .remove=__devexit_p(s3c24xx_serial_remove),
  124. .driver={
  125. .name="s3c2440-uart",
  126. .owner=THIS_MODULE,
  127. },
  128. };
  129. s3c24xx_console_init(&s3c2440_serial_driver,&s3c2440_uart_inf);
  130. staticint__inits3c2440_serial_init(void)
  131. {//初始化模塊
  132. returns3c24xx_serial_init(&s3c2440_serial_driver,&s3c2440_uart_inf);
  133. }
  134. staticvoid__exits3c2440_serial_exit(void)
  135. {//退出模塊
  136. platform_driver_unregister(&s3c2440_serial_driver);//注銷串口設(shè)備
  137. }
  138. module_init(s3c2440_serial_init);
  139. module_exit(s3c2440_serial_exit);
  140. MODULE_DESCRIPTION("SamsungS3C2440,S3C2442SoCSerialportdriver");
  141. MODULE_AUTHOR("BenDooks");
  142. MODULE_LICENSE("GPLv2");
  143. MODULE_ALIAS("platform:s3c2440-uart");

本文引用地址:http://m.butianyuan.cn/article/201611/319506.htm

幾個問題需要我們注意:

1.設(shè)備如何注冊、注銷

串口驅(qū)動被作為一個單獨的模塊被加載進內(nèi)核,在模塊的加載和卸載函數(shù)中,只需注冊和注銷一個platform_driver結(jié)構(gòu)體。

注冊:

[c-sharp]view plaincopy
  1. staticstructplatform_drivers3c2440_serial_driver={
  2. .probe=s3c2440_serial_probe,
  3. .remove=__devexit_p(s3c24xx_serial_remove),
  4. .driver={
  5. .name="s3c2440-uart",
  6. .owner=THIS_MODULE,
  7. },
  8. };

注銷:

[c-sharp]view plaincopy
  1. platform_driver_unregister(&s3c2440_serial_driver);

2.幾個非常重要的結(jié)構(gòu)體

s3c2410_uartcfg :保存ucon ulcon ufcon三個串口寄存器的值

[c-sharp]view plaincopy
  1. structs3c2410_uartcfg{
  2. unsignedcharhwport;//hardwareportnumber//
  3. unsignedcharunused;
  4. unsignedshortflags;
  5. upf_tuart_flags; //defaultuartflags //
  6. unsignedinthas_fracval;
  7. unsignedlongucon;//valueofuconforport//
  8. unsignedlongulcon;//valueofulconforport//
  9. unsignedlongufcon;//valueofufconforport//
  10. structs3c24xx_uart_clksrc*clocks;
  11. unsignedintclocks_size;
  12. };

s3c24xx_uart_info :提供串口設(shè)備環(huán)境信息,并提供三個函數(shù)的接口

[c-sharp]view plaincopy
  1. structs3c24xx_uart_info{
  2. char*name;
  3. unsignedinttype;
  4. unsignedintfifosize;
  5. unsignedlongrx_fifomask;
  6. unsignedlongrx_fifoshift;
  7. unsignedlongrx_fifofull;
  8. unsignedlongtx_fifomask;
  9. unsignedlongtx_fifoshift;
  10. unsignedlongtx_fifofull;
  11. //uartportfeatures//
  12. unsignedinthas_divslot:1;
  13. //clocksourcecontrol//
  14. int(*get_clksrc)(structuart_port*,structs3c24xx_uart_clksrc*clk);
  15. int(*set_clksrc)(structuart_port*,structs3c24xx_uart_clksrc*clk);
  16. //uartcontrols//
  17. int(*reset_port)(structuart_port*,structs3c2410_uartcfg*);
  18. };

platform_device :設(shè)備的信息

[c-sharp]view plaincopy
  1. structplatform_device{
  2. constchar*name;
  3. intid;
  4. structdevicedev;
  5. u32num_resources;
  6. structresource*resource;
  7. conststructplatform_device_id*id_entry;
  8. //archspecificadditions//
  9. structpdev_archdataarchdata;
  10. };

platform_driver :設(shè)備注冊用

[c-sharp]view plaincopy
  1. structplatform_driver{
  2. int(*probe)(structplatform_device*);
  3. int(*remove)(structplatform_device*);
  4. void(*shutdown)(structplatform_device*);
  5. int(*suspend)(structplatform_device*,pm_message_tstate);
  6. int(*resume)(structplatform_device*);
  7. structdevice_driverdriver;
  8. conststructplatform_device_id*id_table;
  9. };

3.讀寫寄存器的宏定義

(1)讀寄存器

unsigned long ucon = rd_regl(port, S3C2410_UCON);

#define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))

static unsigned char __raw_readb(unsigned int ptr)

{

return *((volatile unsigned char *)ptr);

}

#define portaddr(port, reg) ((port)->membase + (reg))

(2)寫寄存器

wr_regl(port, S3C2410_UCON, ucon);

#define wr_regl(port, reg, val) __raw_writel(val, portaddr(port, reg))

#define portaddr(port, reg) ((port)->membase + (reg))

#define __raw_writel(v,p)(*(unsigned long *)(p) = (v))

4.函數(shù)的注冊方式

細心的朋友可能會發(fā)現(xiàn),我們之前一直使用的是傳統(tǒng)的 device driver 機制(通過 driver_register 函數(shù)進行注冊)本串口所使用的是一個設(shè)備用 Platform_device 表示,驅(qū)動用 Platform_driver 進行注冊的機制。而后者是在內(nèi)核2.6版本所提出來的新事物,其優(yōu)勢在于platform機制將設(shè)備本身的資源注冊進內(nèi)核,由內(nèi)核統(tǒng)一管理,在驅(qū)動程序中使用這些資源時通過 platform device 提供的標準接口進行申請并使用。這樣提高了驅(qū)動和資源管理的獨立性,并且擁有較好的可移植性和安全性(這些標準接口是安全的)。關(guān)于這兩種機制更深入的分析,請看以下鏈接:http://blog.csdn.net/jarvis_xian/archive/2011/05/23/6440649.aspx




評論


技術(shù)專區(qū)

關(guān)閉