日期:2014-05-16 浏览次数:21390 次
基于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