It took almost a week to learn drivers. During this period, I was busy learning Linux application programming, search driver, and kernel-related books for further study. In the previously friendly driver routines, the knowledge involved is very limited. It is imperative to study more driver source code and learn more about driver knowledge. While studying other people's code, you must also do it yourself. The following is a serial port driver, and a brief note is attached to the program.
/* Linux/Drivers/serial/S3C2440. c <br/> * driver for Samsung S3C2440 and s3c2442 SOC onboard uarts. <br/> * Ben dooks, copyright (c) 2003-simtec electronics <br/> * http://armlinux.simtec.co.uk/<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 T He 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> # In Clude "Samsung. H "</P> <p> static int s3c2440_serial_setsource (struct uart_port * port, <br/> struct s3c24xx_uart_clksrc * CLK) <br/> {// This function selects the serial port and clock source <br/> unsigned long ucon = rd_regl (port, s3c2410_ucon ); // read the ucon register </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) // select the clock source <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); // write the configured ucon back to the serial control register <br/> return 0; <br/>}</P> <p> static int s3c2440_serial_getsource (struct uart_port * port, <br/> struct s3c24xx_uart_clksrc * CLK) <br/> {// set the clock source and pre-division value <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 = _ r Aw_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/> {// reset the serial port <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); // reset the register ucon <br/> wr_regl (port, s3c2410_ulcon, CFG-> ulcon ); // reset the register ulcon </P> <p>/* reset both runtime OS */</P> <p> wr_regl (port, s3c2410_ufcon, CFG-> ufcon | s3c2410_ufcon_resetboth); // restart the FIFO <br/> wr_regl (port, s3c2410_ufcon, CFG-> ufcon ); // reset the register ufcon </P> <p> return 0; <br/>}</P> <p> static struct s3c24xx_uart_info s3c2440_uart_inf = {// The Environment Information of the serial device and the provided operation functions <br/>. name = "Samsung S3C2440 UART", <br/>. type = port_s3c2440, <br/>. required osize = 64, <br/>. rx_policomask = s3c2440_ufstat_rxmask, <br/>. rx_1_oshift = s3c2440_ufstat_rxshift, <br/>. rx_policofull = s3c2440_ufstat_rxfull, <br/>. tx_effecofull = s3c2440_ufstat_txfull, <br/>. tx_effecomask = 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/> {// complete serial port addition <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 ={// register a serial device <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_lele_init (& s3c2440_serial_driver, & s3c2440_uart_inf ); </P> <p> static int _ init s3c2440_serial_init (void) <br/> {// initialization module <br/> return s3c24xx_serial_init (& s3c2440_serial_driver, & s3c2440_uart_inf ); <br/>}</P> <p> static void _ exit s3c2440_serial_exit (void) <br/>{// exit the module <br/> platform_driver_unregister (& s3c2440_serial_driver ); // disconnect the serial port device <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/>
Pay attention to the following issues:
1. How to register and cancel a device
The serial driver is loaded into the kernel as a separate module. In the module's loading and unloading functions, you only need to register and deregister a platform_driver struct.
Registration:
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/> };
Logout:
Platform_driver_unregister (& s3c2440_serial_driver );
2. several very important struct
S3c2410_uartcfg: saves the values of the ucon ulcon ufcon three serial port registers.
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: Provides the environment information of the serial port device and provides three function interfaces.
Struct s3c24xx_uart_info {<br/> char * Name; <br/> unsigned inttype; <br/> unsigned intfifosize; <br/> unsigned longrx_w.omask; <br/> unsigned longrx_w.w.oshift; <br/> unsigned longrx_policofull; <br/> unsigned longtx_1_omask; <br/> unsigned longtx_1_oshift; <br/> unsigned longtx_1_ofull; </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: device information
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: used for device registration
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. macro definition of read/write registers
(1) read registers
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) Write register
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. Function Registration Method
Careful friends may find that we have been using the traditional device driver mechanism (registered through the driver_register function). This serial port uses a device that is represented by platform_device, the driver uses platform_driver for registration. The latter is a new thing proposed in kernel 2.6. Its advantage is that the platform mechanism registers the resources of the device into the kernel and is centrally managed by the kernel, when using these resources in the driver, you can apply for and use the standard interface provided by platform device. This improves the independence of drivers and resource management, and provides good portability and Security (these standard interfaces are secure ). For more in-depth analysis of these two mechanisms, see the following link: http://blog.csdn.net/jarvis_xian/archive/2011/05/23/6440649.aspx