OMAPL138串口驱动(一)

1120阅读 0评论2013-07-23 枫海8深蓝
分类:LINUX

一、串口的初始化
main.c(setup_arch(&command_line);)---->setup.c(paging_init(mdesc);)---->mmu.c(devicemaps_init(mdesc);)---->mmu.c(mdesc->map_io();)
---->\arch\arm\mach-davinci\board-da850-evm.c(.map_io  = da850_evm_map_io,)---->\arch\arm\mach-davinci\da850.c(void __init da850_init(void))

1.在board-da850-evm.c文件末尾有一段宏,约2161行
MACHINE_START(DAVINCI_DA850_EVM, "DaVinci DA850/OMAP-L138/AM18x EVM")

.boot_params  = (DA8XX_DDR_BASE + 0x100),

.map_io          = da850_evm_map_io,

.init_irq   = cp_intc_init,

.timer             = &davinci_timer,

.init_machine  = da850_evm_init,

MACHINE_END
//这里定义了linux的启动地址,IO口复用配置函数da850_evm_map_io,中断初始化函数,板级初始化函数da850_evm_init(),即内核的setup.c和main.c是通过这个宏接受了板级的配置来进行初始化的。
//内核分别调用了和配置了MACHINE_START宏中的函数和参数

2.在board-da850-evm.c文件中的da850_evm_init()函数:
//这个函数主要是对整个板级电路各个模块如uart、spi、vpif、lcd等在内核中的注册和初始化。
//我们只关注函数中对串口的初始化,以及串口IO复用配置
static __init void da850_evm_init(void)
{
 int ret;
 char mask = 0;
 //在\arch\arm\mach-davinci\include\mach\common.h中定义davinci_soc_info结构。
 //而\arch\arm\mach-davinci\common.c中davinci_common_init()对其初始化。
 //davinci_soc_info真正的定义在\arch\arm\mach-davinci\da850.c中,定义达芬奇架构的各类资源的地址等
 /*static struct davinci_soc_info davinci_soc_info_da850 = {
  .io_desc  = da850_io_desc,//IO地址数组
  .io_desc_num  = ARRAY_SIZE(da850_io_desc),//数组大小
  .jtag_id_reg  = DA8XX_SYSCFG0_BASE + DA8XX_JTAG_ID_REG,//调试地址(见\arch\arm\mach-davinci\include\mach\da8xx.h)
  .ids   = da850_ids,
  .ids_num  = ARRAY_SIZE(da850_ids),
  .cpu_clks  = da850_clks,//时钟
  .psc_bases  = da850_psc_bases,
  .psc_bases_num  = ARRAY_SIZE(da850_psc_bases),
  .pinmux_base  = DA8XX_SYSCFG0_BASE + 0x120,//复用管脚配置基地址
  .pinmux_pins  = da850_pins,//所有的复用管脚配置数组
  .pinmux_pins_num = ARRAY_SIZE(da850_pins),//数组大小
  .intc_base  = DA8XX_CP_INTC_BASE,//中断基地址(见\arch\arm\mach-davinci\include\mach\da8xx.h)
  .intc_type  = DAVINCI_INTC_TYPE_CP_INTC,
  .intc_irq_prios  = da850_default_priorities,//缺省中断优先级数组
  .intc_irq_num  = DA850_N_CP_INTC_IRQ,//中断数量 101(/arch/arm/mach-davinci/include/mach/irqs.h)
  .timer_info  = &da850_timer_info,//计时器
  .gpio_type  = GPIO_TYPE_DAVINCI,//GPIO类型
  .gpio_base  = DA8XX_GPIO_BASE,//GPIO基地址(见\arch\arm\mach-davinci\include\mach\da8xx.h)
  .gpio_num  = 144,//GPIO数量
  .gpio_irq  = IRQ_DA8XX_GPIO0,//GPIO中断42 (/arch/arm/mach-davinci/include/mach/irqs.h)
  .serial_dev  = &da8xx_serial_device,串口设备//(见\arch\arm\mach-davinci\devices-da8xx.c)
  .emac_pdata  = &da8xx_emac_pdata,
  .sram_dma  = DA8XX_ARM_RAM_BASE,//RAM基地址(见\arch\arm\mach-davinci\include\mach\da8xx.h)
  .sram_len  = SZ_8K,//RAM大小
  .reset_device  = &da8xx_wdt_device,
 };
 */
 struct davinci_soc_info *soc_info = &davinci_soc_info;

//对网络,I2C等的管脚配置......

//对UART1复用管脚寄存器的配置
 //da850_uart1_pins在da850.c中定义const short da850_uart1_pins[] __initdata = {DA850_UART1_RXD, DA850_UART1_TXD};
 ret = davinci_cfg_reg_list(da850_uart1_pins);//函数定义在/arch/arm/mach-davinci/mux.c
 if (ret)
  pr_warning("da850_evm_init: UART 1 mux setup failed:"" %d\n", ret);
 
 //对UART0复用管脚寄存器的配置
 //da850_uart0_pins在da850.c中定义const short da850_uart0_pins[] __initdata = {DA850_NUART0_CTS, DA850_NUART0_RTS, DA850_UART0_RXD, DA850_UART0_TXD,-1};
 ret = davinci_cfg_reg_list(da850_uart0_pins);//函数定义在/arch/arm/mach-davinci/mux.c
 if (ret)
  pr_warning("da850_evm_init: UART 0 mux setup failed:"" %d\n", ret);
 
 //davinci_uart_config结构定义在/arch/arm/mach-davinci/include/mach/serial.h
 //static struct davinci_uart_config da850_evm_uart_config __initdata = {
 // .enabled_uarts = 0x7,//串口位使能,bit0=1表示UART0使能,bit1=1表示UART1使能,这里表示UART0-UART2使能
 //};
 //此函数使能串口,并将使能串口的物理地址映射为内存地址
 davinci_serial_init(&da850_evm_uart_config);//函数定义在arch/arm/mach-davinci/serial.c

//......
}

//davinci_cfg_reg_list()函数定义在/arch/arm/mach-davinci/mux.c
static void __iomem *pinmux_base;//复用寄存器基地址
//比如配置UART0_RXD的复用
/* [DA850_UART0_RXD] = {
   .name =  UART0_RXD,
   .debug = false,//
   .mux_reg_name = "PINMUX3",//复用配置寄存器名称(PINMUX0~PINMUX19)
   .mux_reg = PINMUX(3),  //即12.在\arch\arm\mach-davinci\include\mach\mux.h中定义,复用管脚寄存器地址,是个相对地址,具体使用时会加上复用寄存器的基地址 #define PINMUX(x)  (4 * (x))
   .mask_offset = 16, //偏移量,指寄存器的第几位,比如UART0_RXD是通过PINMUX3的16-19位设置的,则偏移量为16(偏移量一般为4的倍数)
   .mask = 15, //掩码0x0F
   .mode = 2,
  },
*/
int __init_or_module davinci_cfg_reg(const unsigned long index)
{
 static DEFINE_SPINLOCK(mux_spin_lock);//定义自旋锁
 struct davinci_soc_info *soc_info = &davinci_soc_info;//达芬奇架构的物理地址资源定义数组(见上)
 unsigned long flags;
 const struct mux_config *cfg;
 unsigned int reg_orig = 0, reg = 0;
 unsigned int mask, warn = 0;

if (WARN_ON(!soc_info->pinmux_pins))//soc资源的复用管脚数组定义不为空,否则出错
  return -ENODEV;

if (!pinmux_base) {//映射获得复用寄存器的基地址(内存中)
  pinmux_base = ioremap(soc_info->pinmux_base, SZ_4K);//映射复用管脚的基地址(0x01C1 4120:SYSCFG0寄存器)到内存地址(共4k)
  if (WARN_ON(!pinmux_base))
   return -ENOMEM;
 }

if (index >= soc_info->pinmux_pins_num) {//index大小不能复用管脚的数量
  printk(KERN_ERR "Invalid pin mux index: %lu (%lu)\n",index, soc_info->pinmux_pins_num);
  dump_stack();
  return -ENODEV;
 }
 //mux_config结构体定义在\arch\arm\mach-davinci\include\mach\mux.h
 cfg = &soc_info->pinmux_pins[index];//从复用管脚数组中取出相应的复用管脚配置信息结构体

if (cfg->name == NULL) {//名称不能为空
  printk(KERN_ERR "No entry for the specified index\n");
  return -ENODEV;
 }

if (cfg->mask) {//配置掩码,这里为0x0F
  unsigned tmp1, tmp2;

spin_lock_irqsave(&mux_spin_lock, flags);//上锁
  reg_orig = __raw_readl(pinmux_base + cfg->mux_reg);//读取寄存器原来的值,复用寄存器的地址为pinmux_base+12,即PINMUX3的复用寄存器地址

mask = (cfg->mask << cfg->mask_offset); //相当于15<<16,偏移量,指寄存器的第几位,即0xF0000
  tmp1 = reg_orig & mask;;//即reg_orig&0xF0000,把寄存器16到19位保留,其余位置0,是为了找出原来的配置,以便接下来与新配置对比,检测是不是有不一样,
  reg = reg_orig & ~mask;//把寄存器的16到19位置0,其余位不改变。

tmp2 = (cfg->mode << cfg->mask_offset);//即2<<16,即0x20000,把需要配置的模式值移位到16.
  reg |= tmp2; //改变寄存器的值,此时已经加入了自己的配置信息,即现在PINMUX3寄存器的第17位=1,正好设置为UART0_RXD

if (tmp1 != tmp2)
   warn = 1;

__raw_writel(reg, pinmux_base + cfg->mux_reg);//将配置好的数值重新写回寄存器
  spin_unlock_irqrestore(&mux_spin_lock, flags);//解锁
 }

return 0;
}

int __init_or_module davinci_cfg_reg_list(const short pins[])
{
 int i, error = -EINVAL;

if (pins)//数组地址不为空
  for (i = 0; pins[i] >= 0; i++) {//扫描数组
   error = davinci_cfg_reg(pins[i]);
   if (error)
    break;
  }

return error;
}

//此函数使能串口,并将使能串口的物理地址映射为内存地址
int __init davinci_serial_init(struct davinci_uart_config *info)
{
 int i;
 char name[16];
 struct clk *uart_clk;
 struct davinci_soc_info *soc_info = &davinci_soc_info;(见上)
 struct device *dev = &soc_info->serial_dev->dev;
 struct plat_serial8250_port *p = dev->platform_data;//定义在\arch\arm\mach-davinci\devices-da8xx.c
 /*struct platform_device da8xx_serial_device = {//platform_device就描述了设备对象。
  .name = "serial8250",
  .id = PLAT8250_DEV_PLATFORM,
  .dev = {
   .platform_data = da8xx_serial_pdata,//这个platform_device对象的私有数据指成员向一个plat_serial8250_port类型的数组。在这里该数组描述了三个串口接口的基本信息。
                     //当8250驱动检测到这个platform_device对象后,就分析该对象的私有数据成员指向的那个plat_serial8250_port类型的数组。
                     //然后根据该数组的每个成员描述的信息生成一个串口对象设备。
  },
 };
 
 static struct plat_serial8250_port da8xx_serial_pdata[] = {
  {
   .mapbase = DA8XX_UART0_BASE,//串口接口寄存器物理地址的基地址,#define DAVINCI_UART0_BASE (IO_PHYS + 0x20000)(见/arch/arm/mach-davinci/include/mach/serial.h)
   .irq  = IRQ_DA8XX_UARTINT0,//该串口接口使用的中断号, #define IRQ_DA8XX_UARTINT0  25,(在/arch/arm/mach-davinci/include/mach/irqs.h)
   .flags  = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP | UPF_FIXED_TYPE,
   .type  = PORT_AR7,//串口0类型
   .iotype  = UPIO_MEM,//成员表示该串口接口寄存器的地址类型,8位的内存地址
   .regshift = 2,//在访问该串口接口的某个寄存器时,需把该寄存器的号左移多少位然后加基地址(不管是物理或虚拟地址)才能得能到这个寄存器的址址
  },
  {
   .mapbase = DA8XX_UART1_BASE, //DAVINCI_UART1_BASE (IO_PHYS + 0x20400)
   .irq  = IRQ_DA8XX_UARTINT1, //中断号是53
   .flags  = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST |UPF_IOREMAP | UPF_FIXED_TYPE,
   .type  = PORT_AR7,
   .iotype  = UPIO_MEM,
   .regshift = 2,
  },
  {
   .mapbase = DA8XX_UART2_BASE, //#define DAVINCI_UART2_BASE (IO_PHYS + 0x20800)
   .irq  = IRQ_DA8XX_UARTINT2, //中断号是61
   .flags  = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST |UPF_IOREMAP | UPF_FIXED_TYPE,
   .type  = PORT_AR7,
   .iotype  = UPIO_MEM,
   .regshift = 2,
  },
  {
   .flags = 0,
  },
 };*/

for (i = 0; p->flags; i++, p++) {
  if (!(info->enabled_uarts & (1 << i)))//根据使能标志判断此串口是否再需要往下进行初始化
  {
   continue;
  }
  
  sprintf(name, "uart%d", i);//串口名称,即时钟list链表中的名称
  //clk_get从一个时钟list链表中以字符name名称来查找一个时钟clk结构体并且返回
  //时钟链表在da850.c,根据名称找到时钟源
  /*static struct clk uart0_clk = {
   .name  = "uart0",
   .parent  = &pll0_sysclk2,
   .lpsc  = DA8XX_LPSC0_UART0,
  };

static struct clk uart1_clk = {
   .name  = "uart1",
   .parent  = &pll0_sysclk2,
   .lpsc  = DA8XX_LPSC1_UART1,
   .gpsc  = 1,
   .flags  = DA850_CLK_ASYNC3,
  };*/
  uart_clk = clk_get(dev, name);
  if (IS_ERR(uart_clk)) {
   printk(KERN_ERR "%s:%d: failed to get UART%d clock\n",__func__, __LINE__, i);
   continue;
  }
  //调用clk_enable(),来使能对应的外设时钟源。
  clk_enable(uart_clk);
  p->uartclk = clk_get_rate(uart_clk);//时钟源频率
  p->clk = uart_clk;//时钟源赋值

if (!p->membase && p->mapbase) {
   p->membase = ioremap(p->mapbase, SZ_4K);//把该串口接口寄存器的物理地址映射到虚拟地址空间

if (p->membase)
    p->flags &= ~UPF_IOREMAP;
   else
    pr_err("uart regs ioremap failed\n");
  }
  
  //达芬奇平台的串口需要复位
  if (p->membase && ((p->type != PORT_AR7) ||cpu_is_davinci_da850() ||cpu_is_davinci_da830()))
   davinci_serial_reset(p);
 }

return platform_device_register(soc_info->serial_dev);//向系统注册驱动,过程中在系统寻找注册的设备(根据.name),找到后运行.probe进行初始化。
}
3.在board-da850-evm.c文件中的da850_evm_map_io()函数中直接调用da850_init()函数:
//这个函数主要是对omapl138进行板级资源配置,定义达芬奇架构的各类资源的地址等。
static void __init da850_evm_map_io(void)
{
 da850_init();
}

void __init da850_init(void)//在\arch\arm\mach-davinci\da850.c
{
 unsigned int v;
 //初始化板级资源结构davinci_soc_info,并进行IO映射,使能系统时钟
 davinci_common_init(&davinci_soc_info_da850);//在\arch\arm\mach-davinci\common.c

//DA8XX_SYSCFG0_BASE寄存器映射
 da8xx_syscfg0_base = ioremap(DA8XX_SYSCFG0_BASE, SZ_4K);
 if (WARN(!da8xx_syscfg0_base, "Unable to map syscfg0 module"))
  return;
 
 //DA8XX_SYSCFG1_BASE寄存器映射
 da8xx_syscfg1_base = ioremap(DA8XX_SYSCFG1_BASE, SZ_4K);
 if (WARN(!da8xx_syscfg1_base, "Unable to map syscfg1 module"))
  return;

da850_set_async3_src(1);

/* Unlock writing to PLL0 registers */
 v = __raw_readl(DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP0_REG));
 v &= ~CFGCHIP0_PLL_MASTER_LOCK;
 __raw_writel(v, DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP0_REG));

/* Unlock writing to PLL1 registers */
 v = __raw_readl(DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP3_REG));
 v &= ~CFGCHIP3_PLL1_MASTER_LOCK;
 __raw_writel(v, DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP3_REG));
}

void __init davinci_common_init(struct davinci_soc_info *soc_info)//在 arch\arm\mach-davinci\da850.c 中调用
{
 int ret;

if (!soc_info) {
  ret = -EINVAL;
  goto err;
 }
 //将davinci_soc_info_da850复制给davinci_soc_info
 memcpy(&davinci_soc_info, soc_info, sizeof(struct davinci_soc_info));
 
 //若IO数组已经分配,则创建I/O映射表,属于静态映射
 if (davinci_soc_info.io_desc && (davinci_soc_info.io_desc_num > 0))
  iotable_init(davinci_soc_info.io_desc,davinci_soc_info.io_desc_num);
  //大致流程为:只要定义相应I/O资源的map_desc结构体,并将该结构体传给iotable_init函数执行,就可以创建相应的I/O资源到内核虚拟地址空间的映射表了。
  /*在da850.c中定义了IO资源的map_desc结构体
  static struct map_desc da850_io_desc[] = {
  {
   .virtual = IO_VIRT,
   .pfn  = __phys_to_pfn(IO_PHYS),
   .length  = IO_SIZE,
   .type  = MT_DEVICE
  },
  {
   .virtual = DA8XX_CP_INTC_VIRT,
   .pfn  = __phys_to_pfn(DA8XX_CP_INTC_BASE),
   .length  = DA8XX_CP_INTC_SIZE,
   .type  = MT_DEVICE
  },
  {
   .virtual = SRAM_VIRT,
   .pfn  = __phys_to_pfn(DA8XX_ARM_RAM_BASE),
   .length  = SZ_8K,
   .type  = MT_DEVICE
  },
 };
  */
 /*
  * Normally devicemaps_init() would flush caches and tlb after
  * mdesc->map_io(), but we must also do it here because of the CPU
  * revision check below.
  */
 local_flush_tlb_all();
 flush_cache_all();

if (!davinci_soc_info.reset)
  davinci_soc_info.reset = davinci_watchdog_reset;//设置看门狗复位函数

ret = davinci_init_id(&davinci_soc_info);
 if (ret < 0)
  goto err;

if (davinci_soc_info.cpu_clks) {
  ret = davinci_clk_init(davinci_soc_info.cpu_clks);//初始化系统时钟

if (ret != 0)
   goto err;
 }

return;

err:
 panic("davinci_common_init: SoC Initialization failed\n");
}


上一篇:linux 串口编程
下一篇:总线设备驱动模型----驱动篇