您的位置:首页 > 运维架构 > Linux

基于Linux-2.6.32.2在mini2440驱动分析一:串口驱动

2012-11-16 09:27 471 查看
基于Linux-2.6.32.2在mini2440驱动分析一:串口驱动
 
串口驱动文件位于: linux-2.6.32.2/drivers/serial/s3c2440.c,省去非重点部分分析。

 

#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)

{

 unsigned long ucon = rd_regl(port, S3C2410_UCON);

 /* todo - proper fclk<>nonfclk switch. */

 ucon &= ~S3C2440_UCON_CLKMASK;

 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);

 return 0;

}

static int s3c2440_serial_getsource(struct uart_port *port,

        struct s3c24xx_uart_clksrc *clk)

{

 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: %08lx, %08lx, %08lx\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)

{

 unsigned long ucon = rd_regl(port, S3C2410_UCON);

 dbg("s3c2440_serial_resetport: port=%p (%08lx), cfg=%p\n",

     port, port->mapbase, cfg);

 /* ensure we don't change the clock settings... */

 ucon &= (S3C2440_UCON0_DIVMASK | (3<<10));

 wr_regl(port, S3C2410_UCON,  ucon | cfg->ucon);

 wr_regl(port, S3C2410_ULCON, cfg->ulcon);

 /* reset both fifos */

 wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);

 wr_regl(port, S3C2410_UFCON, cfg->ufcon);

 return 0;

}

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,

};

/* 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 = {   //这个应该就是类似于file_operation结构体

 .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);

}

module_init(s3c2440_serial_init);

module_exit(s3c2440_serial_exit);

MODULE_DESCRIPTION("Samsung S3C2440,S3C2442 SoC Serial port driver");

MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");

MODULE_LICENSE("GPL v2");

MODULE_ALIAS("platform:s3c2440-uart");

 

 

 

 

 

辅助理解:

1、s3c24xx_serial_init(struct platform_driver *drv, struct s3c24xx_uart_info *info) 函数

int s3c24xx_serial_init(struct platform_driver *drv,

   struct s3c24xx_uart_info *info)

{

 dbg("s3c24xx_serial_init(%p,%p)\n", drv, info);

#ifdef CONFIG_PM

 drv->suspend = s3c24xx_serial_suspend;

 drv->resume = s3c24xx_serial_resume;

#endif

 return platform_driver_register(drv);

}

2、struct platform_driver 结构体

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;

 struct platform_device_id *id_table;

};

 

以下部分涉及S3C2440芯片手册部分内容,附上宏的头文件

/* arch/arm/mach-s3c2410/include/mach/regs-serial.h

 *

 *  From linux/include/asm-arm/hardware/serial_s3c2410.h

 *

 *  Internal header file for Samsung S3C2410 serial ports (UART0-2)*/

#ifndef __ASM_ARM_REGS_SERIAL_H

#define __ASM_ARM_REGS_SERIAL_H

#define S3C24XX_VA_UART0      (S3C_VA_UART)

#define S3C24XX_VA_UART1      (S3C_VA_UART + 0x4000 )

#define S3C24XX_VA_UART2      (S3C_VA_UART + 0x8000 )

#define S3C24XX_VA_UART3      (S3C_VA_UART + 0xC000 )

#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 )

#define S3C2410_URXH   (0x24)

#define S3C2410_UTXH   (0x20)

#define S3C2410_ULCON   (0x00)

#define S3C2410_UCON   (0x04)

#define S3C2410_UFCON   (0x08)

#define S3C2410_UMCON   (0x0C)

#define S3C2410_UBRDIV   (0x28)

#define S3C2410_UTRSTAT   (0x10)

#define S3C2410_UERSTAT   (0x14)

#define S3C2410_UFSTAT   (0x18)

#define S3C2410_UMSTAT   (0x1C)

#define S3C2410_LCON_CFGMASK   ((0xF<<3)|(0x3))

#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)

#define S3C2410_LCON_PNONE   (0x0)

#define S3C2410_LCON_PEVEN   (0x5 << 3)

#define S3C2410_LCON_PODD   (0x4 << 3)

#define S3C2410_LCON_PMASK   (0x7 << 3)

#define S3C2410_LCON_STOPB   (1<<2)

#define S3C2410_LCON_IRM          (1<<6)

#define S3C2440_UCON_CLKMASK   (3<<10)
#define S3C2440_UCON_PCLK   (0<<10)
#define S3C2440_UCON_UCLK   (1<<10)
#define S3C2440_UCON_PCLK2   (2<<10)
#define S3C2440_UCON_FCLK   (3<<10)

#define S3C2440_UCON2_FCLK_EN   (1<<15)

#define S3C2440_UCON0_DIVMASK   (15 << 12)

#define S3C2440_UCON1_DIVMASK   (15 << 12)

#define S3C2440_UCON2_DIVMASK   (7 << 12)

#define S3C2440_UCON_DIVSHIFT   (12)

#define S3C2410_UCON_UCLK   (1<<10)

#define S3C2410_UCON_SBREAK   (1<<4)

#define S3C2410_UCON_TXILEVEL   (1<<9)

#define S3C2410_UCON_RXILEVEL   (1<<8)

#define S3C2410_UCON_TXIRQMODE   (1<<2)

#define S3C2410_UCON_RXIRQMODE   (1<<0)

#define S3C2410_UCON_RXFIFO_TOI   (1<<7)

#define S3C2443_UCON_RXERR_IRQEN  (1<<6)

#define S3C2443_UCON_LOOPBACK   (1<<5)

#define S3C2410_UCON_DEFAULT   (S3C2410_UCON_TXILEVEL  | \

       S3C2410_UCON_RXILEVEL  | \

       S3C2410_UCON_TXIRQMODE | \

       S3C2410_UCON_RXIRQMODE | \

       S3C2410_UCON_RXFIFO_TOI)

#define S3C2410_UFCON_FIFOMODE   (1<<0)

#define S3C2410_UFCON_TXTRIG0   (0<<6)

#define S3C2410_UFCON_RXTRIG8   (1<<4)

#define S3C2410_UFCON_RXTRIG12   (2<<4)

/* S3C2440 FIFO trigger levels */
#define S3C2440_UFCON_RXTRIG1   (0<<4)

#define S3C2440_UFCON_RXTRIG8   (1<<4)

#define S3C2440_UFCON_RXTRIG16   (2<<4)

#define S3C2440_UFCON_RXTRIG32   (3<<4)

#define S3C2440_UFCON_TXTRIG0   (0<<6)

#define S3C2440_UFCON_TXTRIG16   (1<<6)

#define S3C2440_UFCON_TXTRIG32   (2<<6)

#define S3C2440_UFCON_TXTRIG48   (3<<6)

#define S3C2410_UFCON_RESETBOTH   (3<<1)

#define S3C2410_UFCON_RESETTX   (1<<2)

#define S3C2410_UFCON_RESETRX   (1<<1)

#define S3C2410_UFCON_DEFAULT   (S3C2410_UFCON_FIFOMODE | \

       S3C2410_UFCON_TXTRIG0  | \

       S3C2410_UFCON_RXTRIG8 )

#define S3C2410_UMCOM_AFC   (1<<4)

#define S3C2410_UMCOM_RTS_LOW   (1<<0)

#define S3C2410_UFSTAT_TXFULL   (1<<9)

#define S3C2410_UFSTAT_RXFULL   (1<<8)

#define S3C2410_UFSTAT_TXMASK   (15<<4)

#define S3C2410_UFSTAT_TXSHIFT   (4)

#define S3C2410_UFSTAT_RXMASK   (15<<0)

#define S3C2410_UFSTAT_RXSHIFT   (0)

/* UFSTAT S3C2443 same as S3C2440 */
#define S3C2440_UFSTAT_TXFULL   (1<<14)

#define S3C2440_UFSTAT_RXFULL   (1<<6)

#define S3C2440_UFSTAT_TXSHIFT   (8)

#define S3C2440_UFSTAT_RXSHIFT   (0)

#define S3C2440_UFSTAT_TXMASK   (63<<8)

#define S3C2440_UFSTAT_RXMASK   (63)

#define S3C2410_UTRSTAT_TXE   (1<<2)

#define S3C2410_UTRSTAT_TXFE   (1<<1)

#define S3C2410_UTRSTAT_RXDR   (1<<0)

#define S3C2410_UERSTAT_OVERRUN   (1<<0)

#define S3C2410_UERSTAT_FRAME   (1<<2)

#define S3C2410_UERSTAT_BREAK   (1<<3)

#define S3C2443_UERSTAT_PARITY   (1<<1)

#define S3C2410_UERSTAT_ANY   (S3C2410_UERSTAT_OVERRUN | \

       S3C2410_UERSTAT_FRAME | \

       S3C2410_UERSTAT_BREAK)

#define S3C2410_UMSTAT_CTS   (1<<0)

#define S3C2410_UMSTAT_DeltaCTS   (1<<2)

#ifndef __ASSEMBLY__

/* struct s3c24xx_uart_clksrc

 *

 * this structure defines a named clock source that can be used for the

 * uart, so that the best clock can be selected for the requested baud

 * rate.

 *

 * min_baud and max_baud define the range of baud-rates this clock is

 * acceptable for, if they are both zero, it is assumed any baud rate that

 * can be generated from this clock will be used.

 *

 * divisor gives the divisor from the clock to the one seen by the uart

*/

struct s3c24xx_uart_clksrc {

 const char *name;

 unsigned int  divisor;

 unsigned int  min_baud;

 unsigned int  max_baud;

};

/* configuration structure for per-machine configurations for the

 * serial port

 *

 * the pointer is setup by the machine specific initialisation from the

 * arch/arm/mach-s3c2410/ directory.

*/

struct s3c2410_uartcfg {

 unsigned char    hwport;  /* hardware port number */

 unsigned char    unused;

 unsigned short    flags;

 upf_t     uart_flags;  /* default uart flags */

 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_devs

 *

 * this is exported from the core as we cannot use driver_register(),

 * or platform_add_device() before the console_initcall()

*/

extern struct platform_device *s3c24xx_uart_devs[4];

#endif /* __ASSEMBLY__ */

#endif /* __ASM_ARM_REGS_SERIAL_H */

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐