分类: LINUX
2017-03-10 15:45:21
原文地址:OMAPL138串口驱动(一) 作者:wangbaolin719
一、串口的初始化
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");
}