- 硬件資源及描寫敘述
寄存器 | 名稱 | 地址 | 在linux中的描寫敘述 ( 2410 和 2440 處理器對內存地址映射關系同樣 ) |
UART 線性控制寄存器(ULCONn) |
ULCON0?
ULCON1? ULCON2? |
0x50000000
0x50004000 0x50008000 |
linux-2.6.31/arch/arm/plat-s3c/include/plat/regs-serial.h
#define S3C2410_PA_UART0 ?????? ? ? (S3C24XX_PA_UART) #define S3C2410_PA_UART1 ????? ? ? (S3C24XX_PA_UART + 0x4000 ) #define S3C2410_PA_UART2 ????? ? ? (S3C24XX_PA_UART + 0x8000 ) #define S3C2443_PA_UART3????? ? ? (S3C24XX_PA_UART + 0xC000 ) linux-2.6.31/arch/arm/plat-s3c/include/plat/map.h#define S3C24XX_VA_UART??? ?? ? ? S3C_VA_UART//靜態映射后的虛擬地址 #define S3C2410_PA_UART??? ?? ? ? ( 0x50000000 )//物理地址 #define S3C24XX_SZ_UART??? ?? SZ_1M#define S3C_UART_OFFSET??? ?? ( 0x4000 ) |
UART 控制寄存器(UCONn) |
UCON0?
UCON1 UCON2 |
0x50000004
0x50004004 0x50008004 |
#define S3C2410_UCON??? ? (0x04) |
UART FIFO 控制寄存器(UFCONn) |
UFCON0
UFCON1 UFCON2 |
0x50000008
0x50004008 0x50008008 |
#define S3C2410_UFCON??? ? (0x08) |
UART MODEM 控制寄存器(UMCONn) |
UMCON0
UMCON1 |
0x5000000C
0x5000400C |
#define S3C2410_UMCON??? ? (0x0C) |
UART 接收發送狀態寄存器(UTRSTATn) |
UTRSTAT0
UTRSTAT1 UTRSTAT2 |
0x50000010
0x50004010 0x50008010 |
#define S3C2410_UTRSTAT??? ? (0x10) |
UART 錯誤狀態寄存器(UERSTATn) |
UERSTAT0
UERSTAT1 UERSTAT2 |
0x50000014
0x50004014 0x50008014 |
#define S3C2410_UERSTAT??? ? (0x14) |
UART FIFO 狀態寄存器(UFSTATn) |
UFSTAT0
UFSTAT1 UFSTAT2 |
0x50000018
0x50004018 0x50008018 |
#define S3C2410_UFSTAT??? ? (0x18) |
UART MODEM 狀態寄存器(UMSTATn) |
UFSTAT0
UFSTAT1 |
0x5000001C
0x5000401C |
#define S3C2410_UMSTAT??? ? (0x1C) |
UART 發送緩存寄存器(UTXHn) |
UTXH0
UTXH1 UTXH2 |
0x50000020(L)
0x50000023(B) 0x50004020(L) 0x50004023(B) 0x50008020(L) 0x50008023(B) |
#define S3C2410_UTXH??? ? (0x20) |
UART 接收緩存寄存器(URXHn) |
URXH0
URXH1 URXH2 |
0x50000024(L)
0x50000027(B) 0x50004024(L) 0x50004027(B) 0x50008024(L) 0x50008027(B) |
#define S3C2410_URXH??? ? (0x24) |
UART 波特率除數寄存器(UBRDIVn) |
UBRDIV0
UBRDIV1 UBRDIV2 |
0x50000028
0x50004028 0x50008028 |
#define S3C2410_UBRDIV??? ? (0x28) |
ULCON | 位 | 描寫敘述 | 在linux中的描寫敘述 linux-2.6.31/arch/arm/plat-s3c/include/plat/regs-serial.h |
Reserved | [7] | ? | ? |
Infrared Mode | [6] |
決定是否使用紅外模式
0 =正常模式操作 1 = 紅外接收發送模式 |
#define S3C2410_LCON_IRM????????? (1<<6) |
Parity Mode | [5:3] |
在UART發送接收操作中定義奇偶碼的生成和檢
驗類型 0xx = No parity 100 = Odd parity 101 = Even parity 110 = Parity forced/checked as 1 111 = Parity forced/checked as 0 |
#define S3C2410_LCON_PNONE??? ? (0x0)
#define S3C2410_LCON_PEVEN??? ? (0x5 << 3) #define S3C2410_LCON_PODD??? ? (0x4 << 3) #define S3C2410_LCON_PMASK??? ? (0x7 << 3) |
Number of Stop Bit | [2] |
定義度搜按個停止位用于幀末信號
0 =每幀一個停止位 1 =每幀兩個停止位 |
#define S3C2410_LCON_STOPB??? ? (1<<2) |
Word Length | [1:0] |
指出發送接收每幀的數據位數
00 = 5-bits 01 = 6-bits 10 = 7-bits 11 = 8-bits |
#define S3C2410_LCON_CS5??? ? (0x0)
#define S3C2410_LCON_CS6??? ? (0x1) #define S3C2410_LCON_CS7??? ? (0x2) #define S3C2410_LCON_CS8??? ? (0x3) #define S3C2410_LCON_CSMASK??? ? (0x3) |
? | |||
UFCON | 位 | 描寫敘述 | 在linux中的描寫敘述 |
Tx FIFO Trigger Level |
[7:6] |
決定發送FIFO的觸發等級
00 = Empty 01 = 16-byte 10 = 32-byte 11 = 48-byte |
#define S3C2440_UFCON_TXTRIG0??? ? (0<<6)
#define S3C2440_UFCON_TXTRIG16??? ? (1<<6) #define S3C2440_UFCON_TXTRIG32??? ? (2<<6) #define S3C2440_UFCON_TXTRIG48??? ? (3<<6) |
Rx FIFO Trigger Level |
[5:4] |
決定接收FIFO的觸發等級
00 = 1-byte 01 = 8-byte 10 = 16-byte 11 = 32-byte |
#define S3C2440_UFCON_RXTRIG1??? ? (0<<4)
#define S3C2440_UFCON_RXTRIG8??? ? (1<<4) #define S3C2440_UFCON_RXTRIG16??? ? (2<<4) #define S3C2440_UFCON_RXTRIG32??? ? (3<<4) |
Reserved | [3] | ? | ? |
Tx FIFO Reset | [2] |
在重置FIFO后自己主動清除
0 = Normal 1= Tx FIFO reset |
#define S3C2410_UFCON_RESETTX??? ? (1<<2) |
Rx FIFO Reset | [1] |
在重置FIFO后自己主動清除
0 = Normal 1= Rx FIFO reset |
#define S3C2410_UFCON_RESETRX??? ? (1<<1)
#define S3C2410_UFCON_RESETBOTH??? ? (3<<1) |
FIFO Enable | [0] |
0 = Disable
1 = Enable |
? |
默認設置為?
?#define S3C2410_UFCON_DEFAULT??? ? (S3C2410_UFCON_FIFOMODE | \ ??? ??? ??? ??? ?? S3C2410_UFCON_TXTRIG0? | \ ??? ??? ??? ??? ?? S3C2410_UFCON_RXTRIG8 ) |
使能FIFO Tx FIFO為空時觸發中斷 Rx FIFO中包括8個字節時觸發中斷 |
||
? | |||
UFSTAT | 位 | 描寫敘述 | 在linux中的表示 |
Reserved | [15] | ? | ? |
Tx FIFO Full
|
[14] |
僅僅要在發送操作中發送FIFO滿,則自己主動置 1。
0 = 0-byte ≤ Tx FIFO data ≤ 63-byte 1 = Full |
#define S3C2440_UFSTAT_TXFULL??? ? (1<<14) |
Tx FIFO Count | [13:8] | 發送FIFO中的數據數量 | #define S3C2440_UFSTAT_TXSHIFT??? ? (8) |
Reserved | [7] | ? | ? |
Rx FIFO Full | [6] |
僅僅要在接收操作中接收FIFO滿,則自己主動置 1。
0 = 0-byte ≤ Rx FIFO data ≤ 63-byte 1 = Full |
#define S3C2440_UFSTAT_RXFULL??? ? (1<<6) |
Rx FIFO Count | [5:0] | 接收FIFO中的數據數量 | #define S3C2440_UFSTAT_RXSHIFT??? ? (0) |
? | ? | ? | ? |
- uart驅動程序概述
serial_core(定義): | samsumg.c (實現):??????????? ??????????????????????????????????????????????????????? |
struct
uart_driver
{
??? struct module??? ??? *owner; ??? const char??? ??? *driver_name; ??? const char??? ??? *dev_name; ??? int??? ??? ??? ?major; ??? int??? ??? ??? ?minor; ??? int??? ??? ??? ?nr; ??? struct console??? ??? *cons; ??? /* ??? ?* these are private; the low level driver should not ??? ?* touch these; they should be initialised to NULL ??? ?*/ ??? struct uart_state??? *state; ??? struct tty_driver??? *tty_driver; }; |
static struct uart_driver
s3c24xx_uart_drv
= {
??? .owner??? ??? = THIS_MODULE, ??? .dev_name??? = "s3c2410_serial",//設備名稱,這個名稱必須和 ??????? //根文件系統中/etc/inittab文件里的串口名稱一致 ??? .nr??? ??? = CONFIG_SERIAL_SAMSUNG_UARTS, ??? .cons??? ??? = S3C24XX_SERIAL_CONSOLE, ??? .driver_name??? = S3C24XX_SERIAL_NAME,//驅動名稱 ??? .major??? ??? = S3C24XX_SERIAL_MAJOR, ??? .minor??? ??? = S3C24XX_SERIAL_MINOR, }; |
??? ??? uart_driver封裝了tty_driver,使底層uart驅動不用關心ttr_driver。一個tty驅動程序必須注冊/注銷 tty_driver,而uart驅動則變為注冊/注銷uart_driver。使用例如以下接口:
int uart_register_driver(struct uart_driver *drv);
void uart_unregister_driver(struct uart_driver *drv);
實際上,uart_register_driver()和uart_unregister_driver()中分別包括了tty_register_driver()和tty_unregister_driver()的操作。
serial_core(定義): | samsumg.h? samsumg.c(實現): |
struct
uart_port
{
??? spinlock_t??? ??? lock;??? ??? ??? /* port lock */ ??? unsigned long??? ??? iobase;??? ??? ??? /* in/out[bwl] */ ??? unsigned char __iomem??? *membase;??? ??? /* read/write[bwl] */ ??? unsigned int??? ??? (*serial_in)(struct uart_port *, int); ??? void??? ??? ??? (*serial_out)(struct uart_port *, int, int); ??? unsigned int??? ??? irq;??? ??? ??? /* irq number */ ??? unsigned int??? ??? uartclk;??? ??? /* base uart clock */ ??? unsigned int??? ??? fifosize;??? ??? /* tx fifo size */ ??? unsigned char??? ??? x_char;??? ??? ??? /* xon/xoff char */ ??? unsigned char??? ??? regshift;??? ??? /* reg offset shift */ ??? unsigned char??? ??? iotype;??? ??? ??? /* io access style */ ??? unsigned char??? ??? unused1; #define UPIO_PORT??? ??? (0) #define UPIO_HUB6??? ??? (1) #define UPIO_MEM??? ??? (2) #define UPIO_MEM32??? ??? (3) #define UPIO_AU??? ??? ??? (4)??? ??? ??? /* Au1x00 type IO */ #define UPIO_TSI??? ??? (5)??? ??? ??? /* Tsi108/109 type IO */ #define UPIO_DWAPB??? ??? (6)??? ??? ??? /* DesignWare APB UART */ #define UPIO_RM9000??? ??? (7)??? ??? ??? /* RM9000 type IO */ ??? unsigned int??? ??? read_status_mask;??? /* driver specific */ ??? unsigned int??? ??? ignore_status_mask;??? /* driver specific */ ??? struct uart_info??? *info;??? ??? ??? /* pointer to parent info */ ??? struct uart_icount??? icount;??? ??? ??? /* statistics */ ??? struct console??? ??? *cons;??? ??? ??? /* struct console, if any */ #if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ) ??? unsigned long??? ??? sysrq;??? ??? ??? /* sysrq timeout */ #endif ??? upf_t??? ??? ??? flags; #define UPF_FOURPORT??? ??? ((__force upf_t) (1 << 1)) #define UPF_SAK??? ??? ??? ((__force upf_t) (1 << 2)) #define UPF_SPD_MASK??? ??? ((__force upf_t) (0x1030)) #define UPF_SPD_HI??? ??? ((__force upf_t) (0x0010)) #define UPF_SPD_VHI??? ??? ((__force upf_t) (0x0020)) #define UPF_SPD_CUST??? ??? ((__force upf_t) (0x0030)) #define UPF_SPD_SHI??? ??? ((__force upf_t) (0x1000)) #define UPF_SPD_WARP??? ??? ((__force upf_t) (0x1010)) #define UPF_SKIP_TEST??? ??? ((__force upf_t) (1 << 6)) #define UPF_AUTO_IRQ??? ??? ((__force upf_t) (1 << 7)) #define UPF_HARDPPS_CD??? ??? ((__force upf_t) (1 << 11)) #define UPF_LOW_LATENCY??? ??? ((__force upf_t) (1 << 13)) #define UPF_BUGGY_UART??? ??? ((__force upf_t) (1 << 14)) #define UPF_NO_TXEN_TEST??? ((__force upf_t) (1 << 15)) #define UPF_MAGIC_MULTIPLIER??? ((__force upf_t) (1 << 16)) #define UPF_CONS_FLOW??? ??? ((__force upf_t) (1 << 23)) #define UPF_SHARE_IRQ??? ??? ((__force upf_t) (1 << 24)) /* The exact UART type is known and should not be probed.? */ #define UPF_FIXED_TYPE??? ??? ((__force upf_t) (1 << 27)) #define UPF_BOOT_AUTOCONF??? ((__force upf_t) (1 << 28)) #define UPF_FIXED_PORT??? ??? ((__force upf_t) (1 << 29)) #define UPF_DEAD??? ??? ((__force upf_t) (1 << 30)) #define UPF_IOREMAP??? ??? ((__force upf_t) (1 << 31)) #define UPF_CHANGE_MASK??? ??? ((__force upf_t) (0x17fff)) #define UPF_USR_MASK??? ??? ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY)) ??? unsigned int??? ??? mctrl;??? ??? ??? /* current modem ctrl settings */ ??? unsigned int??? ??? timeout;??? ??? /* character-based timeout */ ??? unsigned int??? ??? type;??? ??? ??? /* port type */ ??? const struct uart_ops??? *ops; ??? unsigned int??? ??? custom_divisor; ??? unsigned int??? ??? line;??? ??? ??? /* port index */ ??? resource_size_t??? ??? mapbase;??? ??? /* for ioremap */ ??? struct device??? ??? *dev;??? ??? ??? /* parent device */ ??? unsigned char??? ??? hub6;??? ??? ??? /* this should be in the 8250 driver */ ??? unsigned char??? ??? suspended; ??? unsigned char??? ??? unused[2]; ??? void??? ??? ??? *private_data;??? ??? /* generic platform data pointer */ }; |
struct s3c24xx_uart_port {
//
封裝了uart_port
??? unsigned char??? ??? ??? rx_claimed; ??? unsigned char??? ??? ??? tx_claimed; ??? unsigned int??? ??? ??? pm_level; ??? unsigned long??? ??? ??? baudclk_rate; ??? unsigned int??? ??? ??? rx_irq; ??? unsigned int??? ??? ??? tx_irq; ??? struct s3c24xx_uart_info??? *info; ??? struct s3c24xx_uart_clksrc??? *clksrc; ??? struct clk??? ??? ??? *clk; ??? struct clk??? ??? ??? *baudclk; ??? struct uart_port??? ??? port; #ifdef CONFIG_CPU_FREQ ??? struct notifier_block??? ??? freq_transition; #endif }; static struct s3c24xx_uart_port s3c24xx_serial_ports[ CONFIG_SERIAL_SAMSUNG_UARTS ] = { //3 uarts default ??? [0] = { ??? ??? .port = { ??? ??? ??? .lock??? ??? = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock), ??? ??? ??? .iotype??? ??? = UPIO_MEM, ??? ??? ??? .irq??? ??? = IRQ_S3CUART_RX0, ??? ??? ??? .uartclk??? = 0, ??? ??? ??? .fifosize??? = 16, ??? ??? ??? .ops??? ??? = &s3c24xx_serial_ops, ??? ??? ??? .flags??? ??? = UPF_BOOT_AUTOCONF, ??? ??? ??? .line??? ??? = 0, ??? ??? } ??? }, ??? [1] = { ??? ??? .port = { ??? ??? ??? .lock??? ??? = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock), ??? ??? ??? .iotype??? ??? = UPIO_MEM, ??? ??? ??? .irq??? ??? = IRQ_S3CUART_RX1, ??? ??? ??? .uartclk??? = 0, ??? ??? ??? .fifosize??? = 16, ??? ??? ??? .ops??? ??? = &s3c24xx_serial_ops, ??? ??? ??? .flags??? ??? = UPF_BOOT_AUTOCONF, ??? ??? ??? .line??? ??? = 1, ??? ??? } ??? }, #if CONFIG_SERIAL_SAMSUNG_UARTS > 2 ??? [2] = { ??? ??? .port = { ??? ??? ??? .lock??? ??? = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock), ??? ??? ??? .iotype??? ??? = UPIO_MEM, ??? ??? ??? .irq??? ??? = IRQ_S3CUART_RX2, ??? ??? ??? .uartclk??? = 0, ??? ??? ??? .fifosize??? = 16, ??? ??? ??? .ops??? ??? = &s3c24xx_serial_ops, ??? ??? ??? .flags??? ??? = UPF_BOOT_AUTOCONF, ??? ??? ??? .line??? ??? = 2, ??? ??? } ??? }, #endif #if CONFIG_SERIAL_SAMSUNG_UARTS > 3 ??? [3] = { ??? ??? .port = { ??? ??? ??? .lock??? ??? = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock), ??? ??? ??? .iotype??? ??? = UPIO_MEM, ??? ??? ??? .irq??? ??? = IRQ_S3CUART_RX3, ??? ??? ??? .uartclk??? = 0, ??? ??? ??? .fifosize??? = 16, ??? ??? ??? .ops??? ??? = &s3c24xx_serial_ops, ??? ??? ??? .flags??? ??? = UPF_BOOT_AUTOCONF, ??? ??? ??? .line??? ??? = 3, ??? ??? } ??? } #endif }; |
??? ??? uart_port用于描寫敘述一個UARTport(直接相應于一個串口)的I/Oport或I/O內存地址、FIFO大小、port類型等信息。串口核心層提供例如以下函數來加入1個port:
int uart_add_one_port(struct uart_driver *drv, struct uart_port *port);
??? ??? 對上述函數的調用應該發生在uart_register_driver()之后,uart_add_one_port()的一個最重要作用是封裝了 tty_register_device()。
?? ?? ? uart_add_one_port()的“反函數”是uart_remove_one_port(),當中會調用tty_unregister_device(),原型為:
int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port);
serial_core.h(定義): | samsumg.c (實現):??? ??????????????????????????????????????????????????????????????????????????????????????? |
struct
uart_ops
{
??? unsigned int??? (*tx_empty)(struct uart_port *); ??? void??? ??? (*set_mctrl)(struct uart_port *, unsigned int mctrl); ??? unsigned int??? (*get_mctrl)(struct uart_port *); ??? void??? ??? (*stop_tx)(struct uart_port *); ??? void??? ??? (*start_tx)(struct uart_port *); ??? void??? ??? (*send_xchar)(struct uart_port *, char ch); ??? void??? ??? (*stop_rx)(struct uart_port *); ??? void??? ??? (*enable_ms)(struct uart_port *); ??? void??? ??? (*break_ctl)(struct uart_port *, int ctl); ??? int??? ??? (*startup)(struct uart_port *); ??? void??? ??? (*shutdown)(struct uart_port *); ??? void??? ??? (*flush_buffer)(struct uart_port *); ??? void??? ??? (*set_termios)(struct uart_port *, struct ktermios *new, ??? ??? ??? ??? ?????? struct ktermios *old); ??? void??? ??? (*set_ldisc)(struct uart_port *); ??? void??? ??? (*pm)(struct uart_port *, unsigned int state, ??? ??? ??? ????? unsigned int oldstate); ??? int??? ??? (*set_wake)(struct uart_port *, unsigned int state); ??? /* ??? ?* Return a string describing the type of the port ??? ?*/ ??? const char *(*type)(struct uart_port *); ??? /* ??? ?* Release IO and memory resources used by the port. ??? ?* This includes iounmap if necessary. ??? ?*/ ??? void??? ??? (*release_port)(struct uart_port *); ??? /* ??? ?* Request IO and memory resources used by the port. ??? ?* This includes iomapping the port if necessary. ??? ?*/ ??? int??? ??? (*request_port)(struct uart_port *); ??? void??? ??? (*config_port)(struct uart_port *, int); ??? int??? ??? (*verify_port)(struct uart_port *, struct serial_struct *); ??? int??? ??? (*ioctl)(struct uart_port *, unsigned int, unsigned long); #ifdef CONFIG_CONSOLE_POLL ??? void??? (*poll_put_char)(struct uart_port *, unsigned char); ??? int??? ??? (*poll_get_char)(struct uart_port *); #endif }; |
static struct uart_ops s3c24xx_serial_ops = {
??? .pm??? ??? = s3c24xx_serial_pm, ??? .tx_empty??? = s3c24xx_serial_tx_empty, ??? .get_mctrl??? = s3c24xx_serial_get_mctrl, ??? .set_mctrl??? = s3c24xx_serial_set_mctrl, ??? .stop_tx??? = s3c24xx_serial_stop_tx, ??? .start_tx??? = s3c24xx_serial_start_tx, ??? .stop_rx??? = s3c24xx_serial_stop_rx, ??? .enable_ms??? = s3c24xx_serial_enable_ms, ??? .break_ctl??? = s3c24xx_serial_break_ctl, ??? .startup??? = s3c24xx_serial_startup, ??? .shutdown??? = s3c24xx_serial_shutdown, ??? .set_termios??? = s3c24xx_serial_set_termios, ??? .type??? ??? = s3c24xx_serial_type, ??? .release_port??? = s3c24xx_serial_release_port, ??? .request_port??? = s3c24xx_serial_request_port, ??? .config_port??? = s3c24xx_serial_config_port, ??? .verify_port??? = s3c24xx_serial_verify_port, }; |
- 定義uart_driver、uart_ops、uart_port等結構體的實例并在適當的地方依據詳細硬件和驅動的情況初始化它們,當然詳細設備 xxx的驅動能夠將這些結構套在新定義的xxx_uart_driver、xxx_uart_ops、xxx_uart_port之內。
- 在模塊初始化時調用uart_register_driver()和uart_add_one_port()以注冊UART驅動并加入端口,在模塊卸載時調用uart_unregister_driver()和uart_remove_one_port()以注銷UART驅動并移除端口。
- ?依據詳細硬件的datasheet實現uart_ops中的成員函數,這些函數的實現成為UART驅動的主體工作。
- 串口驅動初始化過程
s3c2410_serial_init()→s3c24xx_serial_init()→ platform_driver_register() 的調用導致s3c24xx_serial_probe()被運行,而s3c24xx_serial_probe()函數中會調用 s3c24xx_serial_init_port()初始化UART端口并調用uart_add_one_port()加入端口。
platform_device_driver 參考: Linux Platform Device and Driver ?? Linux driver model ----- platform
回過頭來看s3c24xx_uart_info結構體(s3c24xx_uart_port的成員),是一些針對s3c2440 uart 的信息,在/drivers/serial/s3c2440.c中:
static struct s3c24xx_uart_info s3c2440_uart_inf = {
??? .name??? ??? = "Samsung S3C2440 UART", ??? .type??? ??? = PORT_S3C2440, ??? .fifosize??? = 64, ??? .rx_fifomask??? = S3C2440_UFSTAT_RXMASK, ??? .rx_fifoshift??? = S3C2440_UFSTAT_RXSHIFT, ??? .rx_fifofull??? = S3C2440_UFSTAT_RXFULL, ??? .tx_fifofull??? = S3C2440_UFSTAT_TXFULL, ??? .tx_fifomask??? = S3C2440_UFSTAT_TXMASK, ??? .tx_fifoshift??? = S3C2440_UFSTAT_TXSHIFT, ??? .get_clksrc??? = s3c2440_serial_getsource, ??? .set_clksrc??? = s3c2440_serial_setsource, ??? .reset_port??? = s3c2440_serial_resetport, }; |
-
串口操作函數,uart_ops接口函數
static int
s3c24xx_serial_startup
(struct uart_port *port)
{ ??? struct s3c24xx_uart_port *ourport = to_ourport(port); ??? int ret; ??? dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n", ??? ??? port->mapbase, port->membase); ??? rx_enabled(port) = 1;//接收使能 ??? ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars , 0, ??? ??? ??? ? s3c24xx_serial_portname(port), ourport); //申請接收中斷, s3c24xx_serial_rx_chars是中斷處理函數 ??? if (ret != 0) { ??? ??? printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq); ??? ??? return ret; ??? } ??? ourport->rx_claimed = 1; ??? dbg("requesting tx irq...\n"); ??? tx_enabled(port) = 1;//發送使能 ??? ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0, ??? ??? ??? ? s3c24xx_serial_portname(port), ourport); //申請發送中斷 ??? if (ret) { ??? ??? printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq); ??? ??? goto err; ??? } ??? ourport->tx_claimed = 1; ??? dbg("s3c24xx_serial_startup ok\n"); ??? /* the port reset code should have done the correct ??? ?* register setup for the port controls */ ??? return ret; ?err: ??? s3c24xx_serial_shutdown(port); ??? return ret; } |
s3c24xx_serial_startup()的“反函數”為s3c24xx_serial_shutdown(),其釋放中斷,禁止發送和接收。
static void s3c24xx_serial_shutdown(struct uart_port *port)
{ ??? struct s3c24xx_uart_port *ourport = to_ourport(port); ??? if (ourport->tx_claimed) { ??? ??? free_irq(ourport->tx_irq, ourport); ??? ??? tx_enabled(port) = 0; ??? ??? ourport->tx_claimed = 0; ??? } ??? if (ourport->rx_claimed) { ??? ??? free_irq(ourport->rx_irq, ourport); ??? ??? ourport->rx_claimed = 0; ??? ??? rx_enabled(port) = 0; ??? } } |
tx_empty()成員函數s3c24xx_serial_tx_empty()用于推斷發送緩沖區是否為空,當使能FIFO模式的時候,推斷UFSTATn寄存器,否則推斷UTRSTATn寄存器的對應位。
static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port)
{ ??? struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); ??? unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT); ??? unsigned long ufcon = rd_regl(port, S3C2410_UFCON); ??? if (ufcon & S3C2410_UFCON_FIFOMODE) { ??? ??? if ((ufstat & info->tx_fifomask) != 0 || ??? ??? ??? (ufstat & info->tx_fifofull)) ??? ??? ??? return 0; ??? ??? return 1; ??? } ??? return s3c24xx_serial_txempty_nofifo(port); } static int s3c24xx_serial_txempty_nofifo(struct uart_port *port) { ??? return (rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE); } |
在samsung.h中定義了例如以下操作寄存器的宏:
/* register access controls */ #define portaddr(port, reg) ((port)->membase + (reg)) #define rd_regb(port, reg) (__raw_readb(portaddr(port, reg))) #define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))? //read regester long #define wr_regb(port, reg, val) __raw_writeb(val, portaddr(port, reg)) #define wr_regl(port, reg, val) __raw_writel(val, portaddr(port, reg)) |
start_tx ()成員函數s3c24xx_serial_start_tx()用于啟動發送,而stop_rx()成員函數 s3c24xx_serial_stop_tx()用于停止發送。
static void s3c24xx_serial_start_tx(struct uart_port *port)
{ ??? struct s3c24xx_uart_port *ourport = to_ourport(port); ??? if (!tx_enabled(port)) { ??? ??? if (port->flags & UPF_CONS_FLOW) ??? ??? ??? s3c24xx_serial_rx_disable(port); ??? ??? enable_irq(ourport->tx_irq); ??? ??? tx_enabled(port) = 1; ??? } } |
static void s3c24xx_serial_stop_tx(struct uart_port *port)
{ ??? struct s3c24xx_uart_port *ourport = to_ourport(port); ??? if (tx_enabled(port)) { ??? ??? disable_irq_nosync(ourport->tx_irq); ??? ??? tx_enabled(port) = 0; ??? ??? if (port->flags & UPF_CONS_FLOW) ??? ??? ??? s3c24xx_serial_rx_enable(port); ??? } } |
??? ??? S3C2410 串口驅動uart_ops結構體的set_termios()成員函數用于改變端口的參數設置,包含波特率、字長、停止位、奇偶校驗等,它會依據傳遞給它的port、termios參數成員的值設置S3C2410 UART的ULCONn、UCONn、UMCONn等寄存器。
static void s3c24xx_serial_set_termios(struct uart_port *port,
??? ??? ??? ??? ?????? struct ktermios *termios, ??? ??? ??? ??? ?????? struct ktermios *old) { ??? struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port); ??? struct s3c24xx_uart_port *ourport = to_ourport(port); ??? struct s3c24xx_uart_clksrc *clksrc = NULL; ??? struct clk *clk = NULL; ??? unsigned long flags; ??? unsigned int baud, quot; ??? unsigned int ulcon; ??? unsigned int umcon; ??? unsigned int udivslot = 0; ??? /* ??? ?* We don't support modem control lines. ??? ?*/ ??? termios->c_cflag &= ~(HUPCL | CMSPAR); ??? termios->c_cflag |= CLOCAL; ??? /* ??? ?* Ask the core to calculate the divisor for us.請求內核計算分頻以便產生相應的波特率 ??? ?*/ ??? baud = uart_get_baud_rate(port, termios, old, 0, 115200*8); ??? if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) ??? ??? quot = port->custom_divisor; ??? else ??? ??? quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud); ??? /* check to see if we need? to change clock source 檢查以確定是否須要改變時鐘源 */ ??? if (ourport->clksrc != clksrc || ourport->baudclk != clk) { ??? ??? dbg("selecting clock %p\n", clk); ??? ??? s3c24xx_serial_setsource(port, clksrc); ??? ??? if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) { ??? ??? ??? clk_disable(ourport->baudclk); ??? ??? ??? ourport->baudclk? = NULL; ??? ??? } ??? ??? clk_enable(clk); ??? ??? ourport->clksrc = clksrc; ??? ??? ourport->baudclk = clk; ??? ??? ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0; ??? } ??? if (ourport->info->has_divslot) { ??? ??? unsigned int div = ourport->baudclk_rate / baud; ??? ??? udivslot = udivslot_table[div & 15]; ??? ??? dbg("udivslot = %04x (div %d)\n", udivslot, div & 15); ??? } ??? /* 設置字長 */ ??? switch (termios->c_cflag & CSIZE) { ??? case CS5: ??? ??? dbg("config: 5bits/char\n"); ??? ??? ulcon = S3C2410_LCON_CS5; ??? ??? break; ??? case CS6: ??? ??? dbg("config: 6bits/char\n"); ??? ??? ulcon = S3C2410_LCON_CS6; ??? ??? break; ??? case CS7: ??? ??? dbg("config: 7bits/char\n"); ??? ??? ulcon = S3C2410_LCON_CS7; ??? ??? break; ??? case CS8: ??? default: ??? ??? dbg("config: 8bits/char\n"); ??? ??? ulcon = S3C2410_LCON_CS8; ??? ??? break; ??? } ??? /* preserve original lcon IR settings */ ??? ulcon |= (cfg->ulcon & S3C2410_LCON_IRM); ??? if (termios->c_cflag & CSTOPB) ??? ??? ulcon |= S3C2410_LCON_STOPB; ??? umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0; ??? if (termios->c_cflag & PARENB) { ??? ??? if (termios->c_cflag & PARODD) ??? ??? ??? ulcon |= S3C2410_LCON_PODD; ??? ??? else ??? ??? ??? ulcon |= S3C2410_LCON_PEVEN; ??? } else { ??? ??? ulcon |= S3C2410_LCON_PNONE; ??? } ??? spin_lock_irqsave(&port->lock, flags); ??? dbg("setting ulcon to %08x, brddiv to %d, udivslot %08x\n", ??? ??? ulcon, quot, udivslot); ??? wr_regl(port, S3C2410_ULCON, ulcon); ??? wr_regl(port, S3C2410_UBRDIV, quot); ??? wr_regl(port, S3C2410_UMCON, umcon); ??? if (ourport->info->has_divslot) ??? ??? wr_regl(port, S3C2443_DIVSLOT, udivslot); ??? dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n", ??? ??? rd_regl(port, S3C2410_ULCON), ??? ??? rd_regl(port, S3C2410_UCON), ??? ??? rd_regl(port, S3C2410_UFCON)); ??? /* ??? ?* Update the per-port timeout. ??? ?*/ ??? uart_update_timeout(port, termios->c_cflag, baud); ??? /* ??? ?* Which character status flags are we interested in? ??? ?*/ ??? port->read_status_mask = S3C2410_UERSTAT_OVERRUN; ??? if (termios->c_iflag & INPCK) ??? ??? port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY; ??? /* ??? ?* Which character status flags should we ignore? ??? ?*/ ??? port->ignore_status_mask = 0; ??? if (termios->c_iflag & IGNPAR) ??? ??? port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN; ??? if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR) ??? ??? port->ignore_status_mask |= S3C2410_UERSTAT_FRAME; ??? /* ??? ?* Ignore all characters if CREAD is not set. ??? ?*/ ??? if ((termios->c_cflag & CREAD) == 0) ??? ??? port->ignore_status_mask |= RXSTAT_DUMMY_READ; ??? spin_unlock_irqrestore(&port->lock, flags); } |
- 接收和發送中斷處理函數
??? ??? s3c24xx_serial_rx_chars ()讀取URXHn寄存器以獲得接收到的字符,并調用 uart_insert_char() 將該字符加入了tty設備的flip緩沖區中,當接收到64個字符或者不再能接受到字符后,調用tty_flip_buffer_push()函數向上層“推”tty設備的flip緩沖。
??? ?? s3c24xx_serial_tx_chars()讀取uart_info中環形緩沖區中的字符,寫入調用UTXHn寄存器。
#define S3C2410_UERSTAT_PARITY (0x1000)
static irqreturn_t s3c24xx_serial_rx_chars (int irq, void *dev_id) { ??? struct s3c24xx_uart_port *ourport = dev_id; ??? struct uart_port *port = &ourport->port; ??? struct tty_struct *tty = port->info->port.tty; ??? unsigned int ufcon, ch, flag, ufstat, uerstat; ??? int max_count = 64; ??? while (max_count-- > 0) { ??? ??? ufcon = rd_regl(port, S3C2410_UFCON); ??? ??? ufstat = rd_regl(port, S3C2410_UFSTAT); ??? ??? if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0) ??? ??? ??? break; ??? ??? uerstat = rd_regl(port, S3C2410_UERSTAT); ??? ??? ch = rd_regb(port, S3C2410_URXH); ??? ??? if (port->flags & UPF_CONS_FLOW) { ??? ??? ??? int txe = s3c24xx_serial_txempty_nofifo(port); ??? ??? ??? if (rx_enabled(port)) { ??? ??? ??? ??? if (!txe) { ??? ??? ??? ??? ??? rx_enabled(port) = 0; ??? ??? ??? ??? ??? continue; ??? ??? ??? ??? } ??? ??? ??? } else { ??? ??? ??? ??? if (txe) { ??? ??? ??? ??? ??? ufcon |= S3C2410_UFCON_RESETRX; ??? ??? ??? ??? ??? wr_regl(port, S3C2410_UFCON, ufcon); ??? ??? ??? ??? ??? rx_enabled(port) = 1; ??? ??? ??? ??? ??? goto out; ??? ??? ??? ??? } ??? ??? ??? ??? continue; ??? ??? ??? } ??? ??? } ??? ??? /* insert the character into the buffer */ ??? ??? flag = TTY_NORMAL; ??? ??? port->icount.rx++; ??? ??? if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) { ??? ??? ??? dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n", ??? ??? ??? ??? ch, uerstat); ??? ??? ??? /* check for break */ ??? ??? ??? if (uerstat & S3C2410_UERSTAT_BREAK) { ??? ??? ??? ??? dbg("break!\n"); ??? ??? ??? ??? port->icount.brk++; ??? ??? ??? ??? if (uart_handle_break(port)) ??? ??? ??? ??? ??? goto ignore_char; ??? ??? ??? } ??? ??? ??? if (uerstat & S3C2410_UERSTAT_FRAME) ??? ??? ??? ??? port->icount.frame++; ??? ??? ??? if (uerstat & S3C2410_UERSTAT_OVERRUN) ??? ??? ??? ??? port->icount.overrun++; ??? ??? ??? uerstat &= port->read_status_mask; ??? ??? ??? if (uerstat & S3C2410_UERSTAT_BREAK) ??? ??? ??? ??? flag = TTY_BREAK; ??? ??? ??? else if (uerstat & S3C2410_UERSTAT_PARITY) ??? ??? ??? ??? flag = TTY_PARITY; ??? ??? ??? else if (uerstat & (S3C2410_UERSTAT_FRAME | ??? ??? ??? ??? ??? ??? S3C2410_UERSTAT_OVERRUN)) ??? ??? ??? ??? flag = TTY_FRAME; ??? ??? } ??? ??? if (uart_handle_sysrq_char(port, ch)) ??? ??? ??? goto ignore_char; ??? ??? uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ??? ??? ??? ??? ?ch, flag); ?ignore_char: ??? ??? continue; ??? } ??? tty_flip_buffer_push(tty); ?out: ??? return IRQ_HANDLED; } |
static irqreturn_t s3c24xx_serial_tx_chars (int irq, void *id) { ??? struct s3c24xx_uart_port *ourport = id; ??? struct uart_port *port = &ourport->port; ??? struct circ_buf *xmit = &port->info->xmit; ??? int count = 256; ??? if (port->x_char) { ??? ??? wr_regb(port, S3C2410_UTXH, port->x_char); ??? ??? port->icount.tx++; ??? ??? port->x_char = 0; ??? ??? goto out; ??? } ??? /* if there isnt anything more to transmit, or the uart is now ??? ?* stopped, disable the uart and exit ??? */ ??? if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { ??? ??? s3c24xx_serial_stop_tx(port); ??? ??? goto out; ??? } ??? /* try and drain the buffer... */ ??? while (!uart_circ_empty(xmit) && count-- > 0) { ??? ??? if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull) ??? ??? ??? break; ??? ??? wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]); ??? ??? xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); ??? ??? port->icount.tx++; ??? } ??? if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) ??? ??? uart_write_wakeup(port); ??? if (uart_circ_empty(xmit)) ??? ??? s3c24xx_serial_stop_tx(port); ?out: ??? return IRQ_HANDLED; } |
- 串口測試
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
