S3C2440驅(qū)動簡析——串口驅(qū)動
- // linux/drivers/serial/s3c2440.c
* * Driver for Samsung S3C2440 and S3C2442 SoC onboard UARTs. * * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics * http://armlinux.simtec.co.uk/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - //
- #include
"linux/module.h" - #include
"linux/ioport.h" - #include
"linux/io.h" - #include
"linux/platform_device.h" - #include
"linux/init.h" - #include
"linux/serial_core.h" - #include
"linux/serial.h" - #include
"asm/irq.h" - #include
"mach/hardware.h" - #include
"plat/regs-serial.h" - #include
"mach/regs-gpio.h" - #include
"samsung.h" - static
int s3c2440_serial_setsource(struct uart_port *port, struct s3c24xx_uart_clksrc *clk) - {
//本函數(shù)選定串口端口和時鐘源 unsigned long ucon = rd_regl(port, S3C2410_UCON); //讀取寄存器UCON // todo - proper fclk<>nonfclk switch. // ucon &= ~S3C2440_UCON_CLKMASK; //#define S3C2440_UCON_CLKMASK (3<<10) if (strcmp(clk->name, "uclk") == 0) //選擇時鐘源 ucon |= S3C2440_UCON_UCLK; else if (strcmp(clk->name, "pclk") == 0) ucon |= S3C2440_UCON_PCLK; else if (strcmp(clk->name, "fclk") == 0) ucon |= S3C2440_UCON_FCLK; else { printk(KERN_ERR "unknown clock source %s/n", clk->name); return -EINVAL; } wr_regl(port, S3C2410_UCON, ucon); //把設(shè)置過的ucon寫回串口控制寄存器 return 0; - }
- static
int s3c2440_serial_getsource(struct uart_port *port, struct s3c24xx_uart_clksrc *clk) - {
//設(shè)置時鐘源和對應(yīng)預(yù)分頻值 unsigned long ucon = rd_regl(port, S3C2410_UCON); unsigned long ucon0, ucon1, ucon2; switch (ucon & S3C2440_UCON_CLKMASK) { case S3C2440_UCON_UCLK: clk->divisor = 1; clk->name = "uclk"; break; case S3C2440_UCON_PCLK: case S3C2440_UCON_PCLK2: clk->divisor = 1; clk->name = "pclk"; break; case S3C2440_UCON_FCLK: // the fun of calculating the uart divisors on * the s3c2440 // ucon0 = __raw_readl(S3C24XX_VA_UART0 + S3C2410_UCON); ucon1 = __raw_readl(S3C24XX_VA_UART1 + S3C2410_UCON); ucon2 = __raw_readl(S3C24XX_VA_UART2 + S3C2410_UCON); printk("ucons: lx, lx, lx/n", ucon0, ucon1, ucon2); ucon0 &= S3C2440_UCON0_DIVMASK; ucon1 &= S3C2440_UCON1_DIVMASK; ucon2 &= S3C2440_UCON2_DIVMASK; if (ucon0 != 0) { clk->divisor = ucon0 >> S3C2440_UCON_DIVSHIFT; clk->divisor += 6; } else if (ucon1 != 0) { clk->divisor = ucon1 >> S3C2440_UCON_DIVSHIFT; clk->divisor += 21; } else if (ucon2 != 0) { clk->divisor = ucon2 >> S3C2440_UCON_DIVSHIFT; clk->divisor += 36; } else { // manual calims 44, seems to be 9 // clk->divisor = 9; } clk->name = "fclk"; break; } return 0; - }
- static
int s3c2440_serial_resetport(struct uart_port *port, struct s3c2410_uartcfg *cfg) - {
//重設(shè)串口 unsigned long ucon = rd_regl(port, S3C2410_UCON); dbg("s3c2440_serial_resetport: port=%p (lx), cfg=%p/n", port, port->mapbase, cfg); // ensure we dont change the clock settings... // ucon &= (S3C2440_UCON0_DIVMASK | (3<<10)); wr_regl(port, S3C2410_UCON, ucon | cfg->ucon); //重新設(shè)置寄存器UCON wr_regl(port, S3C2410_ULCON, cfg->ulcon); //重新設(shè)置寄存器ULCON // reset both fifos // wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); //重啟fifo wr_regl(port, S3C2410_UFCON, cfg->ufcon); //重新設(shè)定寄存器UFCON return 0; - }
- static
struct s3c24xx_uart_info s3c2440_uart_inf = { //串口設(shè)備環(huán)境信息和提供的操作函數(shù) .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, - };
- //
device management // - static
int s3c2440_serial_probe(struct platform_device *dev) - {
//完成串口的添加 dbg("s3c2440_serial_probe: dev=%p/n", dev); return s3c24xx_serial_probe(dev, &s3c2440_uart_inf); - }
- static
struct platform_driver s3c2440_serial_driver = { //注冊串口設(shè)備 .probe = s3c2440_serial_probe, .remove = __devexit_p(s3c24xx_serial_remove), .driver = { .name = "s3c2440-uart", .owner = THIS_MODULE, }, - };
- s3c24xx_console_init(&s3c2440_serial_driver,
&s3c2440_uart_inf); - static
int __init s3c2440_serial_init(void) - {
//初始化模塊 return s3c24xx_serial_init(&s3c2440_serial_driver, &s3c2440_uart_inf); - }
- static
void __exit s3c2440_serial_exit(void) - {
//退出模塊 platform_driver_unregister(&s3c2440_serial_driver); //注銷串口設(shè)備 - }
- module_init(s3c2440_serial_init);
- module_exit(s3c2440_serial_exit);
- MODULE_DESCRIPTION("Samsung
S3C2440,S3C2442 SoC Serial port driver"); - MODULE_AUTHOR("Ben
Dooks "); - MODULE_LICENSE("GPL
v2"); - MODULE_ALIAS("platform:s3c2440-uart");
幾個問題需要我們注意:
1.設(shè)備如何注冊、注銷
串口驅(qū)動被作為一個單獨的模塊被加載進內(nèi)核,在模塊的加載和卸載函數(shù)中,只需注冊和注銷一個platform_driver結(jié)構(gòu)體。
注冊:
- static
struct platform_driver s3c2440_serial_driver = { .probe = s3c2440_serial_probe, .remove = __devexit_p(s3c24xx_serial_remove), .driver = { .name = "s3c2440-uart", .owner = THIS_MODULE, }, - };
注銷:
- platform_driver_unregister(&s3c2440_serial_driver);
2.幾個非常重要的結(jié)構(gòu)體
s3c2410_uartcfg :保存ucon ulcon ufcon三個串口寄存器的值
- struct
s3c2410_uartcfg { unsigned char hwport; // hardware port number // unsigned char unused; unsigned short flags; upf_t uart_flags; // default uart flags // unsigned int has_fracval; unsigned long ucon; // value of ucon for port // unsigned long ulcon; // value of ulcon for port // unsigned long ufcon; // value of ufcon for port // struct s3c24xx_uart_clksrc *clocks; unsigned int clocks_size; - };
s3c24xx_uart_info :提供串口設(shè)備環(huán)境信息,并提供三個函數(shù)的接口
- struct
s3c24xx_uart_info { char *name; unsigned int type; unsigned int fifosize; unsigned long rx_fifomask; unsigned long rx_fifoshift; unsigned long rx_fifofull; unsigned long tx_fifomask; unsigned long tx_fifoshift; unsigned long tx_fifofull; // uart port features // unsigned int has_divslot:1; // clock source control // int (*get_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk); int (*set_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk); // uart controls // int (*reset_port)(struct uart_port *, struct s3c2410_uartcfg *); - };
platform_device :設(shè)備的信息
- struct
platform_device { const char * name; int id; struct device dev; u32 num_resources; struct resource * resource; const struct platform_device_id *id_entry; // arch specific additions // struct pdev_archdata archdata; - };
platform_driver :設(shè)備注冊用
- struct
platform_driver { int (*probe)(struct platform_device *); int (*remove)(struct platform_device *); void (*shutdown)(struct platform_device *); int (*suspend)(struct platform_device *, pm_message_t state); int (*resume)(struct platform_device *); struct device_driver driver; const struct platform_device_id *id_table; - };
3.讀寫寄存器的宏定義
(1)讀寄存器
unsigned long ucon = rd_regl(port, S3C2410_UCON);
#define rd_regl(port, reg)
static unsigned char __raw_readb(unsigned int ptr)
{
}
#define portaddr(port, reg)
(2)寫寄存器
wr_regl(port, S3C2410_UCON, ucon);
#define wr_regl(port, reg, val)
#define portaddr(port, reg)
#define __raw_writel(v,p)
4.函數(shù)的注冊方式
評論