新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > S3C2440驅動簡析——串口驅動

        S3C2440驅動簡析——串口驅動

        作者: 時間:2016-11-21 來源:網絡 收藏
        對于驅動的學習停歇了幾乎一周的時間,期間忙于補習Linux應用編程和搜索驅動、內核相關書籍,以便之后更進一步地學習。在之前友善提供的驅動例程里面,涉及的知識面非常有限,需要研究更多的驅動源碼,了解更多的驅動知識,是當務之急。研究別人代碼的同時,當然不忘自己也要動手練習。以下貼出串口驅動程序,并在程序里附上簡要注釋。
        [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. {//本函數選定串口端口和時鐘源
        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);//把設置過的ucon寫回串口控制寄存器
        41. return0;
        42. }
        43. staticints3c2440_serial_getsource(structuart_port*port,
        44. structs3c24xx_uart_clksrc*clk)
        45. {//設置時鐘源和對應預分頻值
        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. {//重設串口
        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);//重新設置寄存器UCON
        95. wr_regl(port,S3C2410_ULCON,cfg->ulcon);//重新設置寄存器ULCON
        96. //resetbothfifos//
        97. wr_regl(port,S3C2410_UFCON,cfg->ufcon|S3C2410_UFCON_RESETBOTH);//重啟fifo
        98. wr_regl(port,S3C2410_UFCON,cfg->ufcon);//重新設定寄存器UFCON
        99. return0;
        100. }
        101. staticstructs3c24xx_uart_infos3c2440_uart_inf={//串口設備環境信息和提供的操作函數
        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={//注冊串口設備
        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);//注銷串口設備
        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://www.104case.com/article/201611/319506.htm

        幾個問題需要我們注意:

        1.設備如何注冊、注銷

        串口驅動被作為一個單獨的模塊被加載進內核,在模塊的加載和卸載函數中,只需注冊和注銷一個platform_driver結構體。

        注冊:

        [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.幾個非常重要的結構體

        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 :提供串口設備環境信息,并提供三個函數的接口

        [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 :設備的信息

        [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 :設備注冊用

        [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.函數的注冊方式

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




        評論


        技術專區

        關閉
        主站蜘蛛池模板: 惠州市| 温州市| 高陵县| 鱼台县| 西盟| 吉木乃县| 皋兰县| 昭觉县| 莲花县| 汉中市| 林口县| 苗栗市| 石狮市| 云南省| 鄂温| 遂昌县| 沅江市| 班玛县| 金秀| 牡丹江市| 上思县| 城口县| 郁南县| 苍溪县| 永福县| 辽源市| 建瓯市| 青海省| 独山县| 金湖县| 如皋市| 綦江县| 大洼县| 离岛区| 沙坪坝区| 巴彦县| 仲巴县| 乌兰察布市| 会理县| 加查县| 克什克腾旗|