S3C2440驅動簡析——串口驅動

來源:互聯網
上載者:User

     對於驅動的學習停歇了幾乎一周的時間,期間忙於補習Linux應用編程和搜尋驅動、核心相關書籍,以便之後更進一步地學習。在之前友善提供的驅動常式裡面,涉及的知識面非常有限,需要研究更多的驅動源碼,瞭解更多的驅動知識,是當務之急。研究別人代碼的同時,當然不忘自己也要動手練習。以下貼出串口驅動程式,並在程式裡附上簡要注釋。

 

/* linux/drivers/serial/s3c2440.c<br /> *<br /> * Driver for Samsung S3C2440 and S3C2442 SoC onboard UARTs.<br /> *<br /> * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics<br /> *http://armlinux.simtec.co.uk/<br /> *<br /> * This program is free software; you can redistribute it and/or modify<br /> * it under the terms of the GNU General Public License version 2 as<br /> * published by the Free Software Foundation.<br />*/</p><p>#include <linux/module.h><br />#include <linux/ioport.h><br />#include <linux/io.h><br />#include <linux/platform_device.h><br />#include <linux/init.h><br />#include <linux/serial_core.h><br />#include <linux/serial.h></p><p>#include <asm/irq.h><br />#include <mach/hardware.h></p><p>#include <plat/regs-serial.h><br />#include <mach/regs-gpio.h></p><p>#include "samsung.h"</p><p>static int s3c2440_serial_setsource(struct uart_port *port,<br /> struct s3c24xx_uart_clksrc *clk)<br />{ //本函數選定串口連接埠和時鐘源<br />unsigned long ucon = rd_regl(port, S3C2410_UCON); //讀取寄存器UCON</p><p>/* todo - proper fclk<>nonfclk switch. */</p><p>ucon &= ~S3C2440_UCON_CLKMASK; //#define S3C2440_UCON_CLKMASK (3<<10)</p><p>if (strcmp(clk->name, "uclk") == 0) //選擇時鐘源<br />ucon |= S3C2440_UCON_UCLK;<br />else if (strcmp(clk->name, "pclk") == 0)<br />ucon |= S3C2440_UCON_PCLK;<br />else if (strcmp(clk->name, "fclk") == 0)<br />ucon |= S3C2440_UCON_FCLK;<br />else {<br />printk(KERN_ERR "unknown clock source %s/n", clk->name);<br />return -EINVAL;<br />}</p><p>wr_regl(port, S3C2410_UCON, ucon); //把設定過的ucon寫回串口控制寄存器<br />return 0;<br />}</p><p>static int s3c2440_serial_getsource(struct uart_port *port,<br /> struct s3c24xx_uart_clksrc *clk)<br />{ //設定時鐘源和對應預分頻值<br />unsigned long ucon = rd_regl(port, S3C2410_UCON);<br />unsigned long ucon0, ucon1, ucon2;</p><p>switch (ucon & S3C2440_UCON_CLKMASK) {<br />case S3C2440_UCON_UCLK:<br />clk->divisor = 1;<br />clk->name = "uclk";<br />break;</p><p>case S3C2440_UCON_PCLK:<br />case S3C2440_UCON_PCLK2:<br />clk->divisor = 1;<br />clk->name = "pclk";<br />break;</p><p>case S3C2440_UCON_FCLK:<br />/* the fun of calculating the uart divisors on<br /> * the s3c2440 */</p><p>ucon0 = __raw_readl(S3C24XX_VA_UART0 + S3C2410_UCON);<br />ucon1 = __raw_readl(S3C24XX_VA_UART1 + S3C2410_UCON);<br />ucon2 = __raw_readl(S3C24XX_VA_UART2 + S3C2410_UCON);</p><p>printk("ucons: %08lx, %08lx, %08lx/n", ucon0, ucon1, ucon2);</p><p>ucon0 &= S3C2440_UCON0_DIVMASK;<br />ucon1 &= S3C2440_UCON1_DIVMASK;<br />ucon2 &= S3C2440_UCON2_DIVMASK;</p><p>if (ucon0 != 0) {<br />clk->divisor = ucon0 >> S3C2440_UCON_DIVSHIFT;<br />clk->divisor += 6;<br />} else if (ucon1 != 0) {<br />clk->divisor = ucon1 >> S3C2440_UCON_DIVSHIFT;<br />clk->divisor += 21;<br />} else if (ucon2 != 0) {<br />clk->divisor = ucon2 >> S3C2440_UCON_DIVSHIFT;<br />clk->divisor += 36;<br />} else {<br />/* manual calims 44, seems to be 9 */<br />clk->divisor = 9;<br />}</p><p>clk->name = "fclk";<br />break;<br />}</p><p>return 0;<br />}</p><p>static int s3c2440_serial_resetport(struct uart_port *port,<br /> struct s3c2410_uartcfg *cfg)<br />{ //重設串口<br />unsigned long ucon = rd_regl(port, S3C2410_UCON);</p><p>dbg("s3c2440_serial_resetport: port=%p (%08lx), cfg=%p/n",<br /> port, port->mapbase, cfg);</p><p>/* ensure we don't change the clock settings... */</p><p>ucon &= (S3C2440_UCON0_DIVMASK | (3<<10)); </p><p>wr_regl(port, S3C2410_UCON, ucon | cfg->ucon); //重新設定寄存器UCON<br />wr_regl(port, S3C2410_ULCON, cfg->ulcon); //重新設定寄存器ULCON</p><p>/* reset both fifos */</p><p>wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); //重啟fifo<br />wr_regl(port, S3C2410_UFCON, cfg->ufcon); //重新設定寄存器UFCON</p><p>return 0;<br />}</p><p>static struct s3c24xx_uart_info s3c2440_uart_inf = { //串口裝置環境資訊和提供的操作函數<br />.name= "Samsung S3C2440 UART",<br />.type= PORT_S3C2440,<br />.fifosize= 64,<br />.rx_fifomask= S3C2440_UFSTAT_RXMASK,<br />.rx_fifoshift= S3C2440_UFSTAT_RXSHIFT,<br />.rx_fifofull= S3C2440_UFSTAT_RXFULL,<br />.tx_fifofull= S3C2440_UFSTAT_TXFULL,<br />.tx_fifomask= S3C2440_UFSTAT_TXMASK,<br />.tx_fifoshift= S3C2440_UFSTAT_TXSHIFT,<br />.get_clksrc= s3c2440_serial_getsource,<br />.set_clksrc= s3c2440_serial_setsource,<br />.reset_port= s3c2440_serial_resetport,<br />};</p><p>/* device management */</p><p>static int s3c2440_serial_probe(struct platform_device *dev)<br />{ //完成串口的添加<br />dbg("s3c2440_serial_probe: dev=%p/n", dev);<br />return s3c24xx_serial_probe(dev, &s3c2440_uart_inf);<br />}</p><p>static struct platform_driver s3c2440_serial_driver = { //註冊串口裝置<br />.probe= s3c2440_serial_probe,<br />.remove= __devexit_p(s3c24xx_serial_remove),<br />.driver= {<br />.name= "s3c2440-uart",<br />.owner= THIS_MODULE,<br />},<br />};</p><p>s3c24xx_console_init(&s3c2440_serial_driver, &s3c2440_uart_inf);</p><p>static int __init s3c2440_serial_init(void)<br />{ //初始化模組<br />return s3c24xx_serial_init(&s3c2440_serial_driver, &s3c2440_uart_inf);<br />}</p><p>static void __exit s3c2440_serial_exit(void)<br />{ //結束模組<br />platform_driver_unregister(&s3c2440_serial_driver); //登出串口裝置<br />}</p><p>module_init(s3c2440_serial_init);<br />module_exit(s3c2440_serial_exit);</p><p>MODULE_DESCRIPTION("Samsung S3C2440,S3C2442 SoC Serial port driver");<br />MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");<br />MODULE_LICENSE("GPL v2");<br />MODULE_ALIAS("platform:s3c2440-uart");<br />

 

 

幾個問題需要我們注意:

1.裝置如何註冊、登出

串口驅動被作為一個單獨的模組被載入進核心,在模組的載入和卸載函數中,只需註冊和登出一個platform_driver結構體。

註冊:

static struct platform_driver s3c2440_serial_driver = {<br />.probe= s3c2440_serial_probe,<br />.remove= __devexit_p(s3c24xx_serial_remove),<br />.driver= {<br />.name= "s3c2440-uart",<br />.owner= THIS_MODULE,<br />},<br />};

 

登出:

platform_driver_unregister(&s3c2440_serial_driver);

 

2.幾個非常重要的結構體

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

struct s3c2410_uartcfg {<br />unsigned char hwport; /* hardware port number */<br />unsigned char unused;<br />unsigned short flags;<br />upf_t uart_flags; /* default uart flags */</p><p>unsigned int has_fracval;</p><p>unsigned long ucon; /* value of ucon for port */<br />unsigned long ulcon; /* value of ulcon for port */<br />unsigned long ufcon; /* value of ufcon for port */</p><p>struct s3c24xx_uart_clksrc *clocks;<br />unsigned int clocks_size;<br />};

 

 

s3c24xx_uart_info :提供串口裝置環境資訊,並提供三個函數的介面

struct s3c24xx_uart_info {<br />char*name;<br />unsigned inttype;<br />unsigned intfifosize;<br />unsigned longrx_fifomask;<br />unsigned longrx_fifoshift;<br />unsigned longrx_fifofull;<br />unsigned longtx_fifomask;<br />unsigned longtx_fifoshift;<br />unsigned longtx_fifofull;</p><p>/* uart port features */</p><p>unsigned inthas_divslot:1;</p><p>/* clock source control */</p><p>int (*get_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk);<br />int (*set_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk);</p><p>/* uart controls */<br />int (*reset_port)(struct uart_port *, struct s3c2410_uartcfg *);<br />};

 

 

platform_device :裝置的資訊

 struct platform_device {<br />const char* name;<br />intid;<br />struct devicedev;<br />u32num_resources;<br />struct resource* resource;</p><p>const struct platform_device_id*id_entry;</p><p>/* arch specific additions */<br />struct pdev_archdataarchdata;<br />};

 

 

platform_driver :裝置註冊用

struct platform_driver {<br />int (*probe)(struct platform_device *);<br />int (*remove)(struct platform_device *);<br />void (*shutdown)(struct platform_device *);<br />int (*suspend)(struct platform_device *, pm_message_t state);<br />int (*resume)(struct platform_device *);<br />struct device_driver driver;<br />const struct platform_device_id *id_table;<br />};

 

 

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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.