dm365管脚复用配置浅析之davinci_cfg_reg调用
内核版本:linux-2.6.32.17-psp03.01.01.39,leopardboard dm365开发板带的sdk包里面的内核
davinci_cfg_reg()函数用来配置dm365的管脚复用功能,调用时直接使用davinci_cfg_reg(index)即可,
其中index是对应的复用功能。它被定义在初始化数组中。要了解davinci_cfg_reg的原理,理解管脚复用表比较关键,下面就详细介绍:
注:以下所有文件都是在内核arch/arm/mach-davince/下,
本文以dm365的I2C管脚复用为例。
1、davinci_cfg_reg在头文件kernel/arch/arm/mach-davince/include/mach/mux.h中包含如下
#ifdefCONFIG_DAVINCI_MUX
/* setup pinmuxing */
extern intdavinci_cfg_reg(unsigned long reg_cfg);
#else
/* boot loaderdoes it all (no warnings from CONFIG_DAVINCI_MUX_WARNINGS) */
static inlineint davinci_cfg_reg(unsigned long reg_cfg) { return 0; }
#endif
2、davinci_cfg_reg被定义于kernel/arch/arm/mach-davince/mux.c中,如下:
/** Setsthe DAVINCI MUX register based on the table*/
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; //传入全局结构体,在dm365.c初始化
void __iomem *base = soc_info->pinmux_base;//获得复用寄存器的基地址0x014c0000
unsigned long flags;
const struct mux_config *cfg;//配置信息结构体,定义于mux.h
unsigned int reg_orig = 0, reg = 0;
unsigned int mask, warn = 0;
if(!soc_info->pinmux_pins)
BUG();
if (index >= soc_info->pinmux_pins_num) {
printk(KERN_ERR "Invalid pin muxindex: %lu (%lu)\n",
index, soc_info->pinmux_pins_num);
dump_stack();
return -ENODEV;
}
cfg = &soc_info->pinmux_pins[index];
if(cfg->name == NULL) {
printk(KERN_ERR "No entry for thespecified index\n");
return -ENODEV;
}
/*Update the mux register in question */
if (cfg->mask) {
unsigned tmp1, tmp2;
spin_lock_irqsave(&mux_spin_lock,flags);
reg_orig = __raw_readl(base +cfg->mux_reg);
mask = (cfg->mask <<cfg->mask_offset);
tmp1 = reg_orig & mask;
reg = reg_orig & ~mask;
tmp2 = (cfg->mode <<cfg->mask_offset);
reg |= tmp2;
if (tmp1 != tmp2)
warn = 1;
__raw_writel(reg, base +cfg->mux_reg);
spin_unlock_irqrestore(&mux_spin_lock,flags);
}
if (warn) {
#ifdefCONFIG_DAVINCI_MUX_WARNINGS
printk(KERN_WARNING"MUX: initialized %s\n", cfg->name);
#endif
}
#ifdefCONFIG_DAVINCI_MUX_DEBUG
if (cfg->debug || warn) {
printk(KERN_WARNING"MUX: Setting register %s\n", cfg->name);
printk(KERN_WARNING " %s(0x%08x) = 0x%08x -> 0x%08x\n",
cfg->mux_reg_name, cfg->mux_reg,reg_orig, reg);
}
#endif
return 0;
}
EXPORT_SYMBOL(davinci_cfg_reg);
3、现在将详细介绍davinci_cfg_reg的执行步骤:
1)、添加锁,
2)、struct davinci_soc_info *soc_info= &davinci_soc_info;
davinci_soc_info是一个比较全局结构体,在dm365.c中被初始化,内容如下
(这里有一个疑问:就是没有弄清楚在什么地方把davinci_soc_info和davinci_soc_info_dm365关联起来的,有知道的朋友,请说明一下)
static structdavinci_soc_info davinci_soc_info_dm365 = {
.io_desc =dm365_io_desc,
.io_desc_num = ARRAY_SIZE(dm365_io_desc),
.jtag_id_base = IO_ADDRESS(0x01c40028),
.ids =dm365_ids,
.ids_num =ARRAY_SIZE(dm365_ids),
.cpu_clks =dm365_clks,
.psc_bases =dm365_psc_bases,
.psc_bases_num = ARRAY_SIZE(dm365_psc_bases), //psc基地址
.pinmux_base = IO_ADDRESS(DAVINCI_SYSTEM_MODULE_BASE),
//在include/mach/hardware.h中定义
// #define DAVINCI_SYSTEM_MODULE_BASE 0x01C40000
.pinmux_pins = dm365_pins, //dm365所有的管脚复用表,接下来要分析的,在dm365.c中
.pinmux_pins_num = ARRAY_SIZE(dm365_pins),//复用数量
.intc_base =IO_ADDRESS(DAVINCI_ARM_INTC_BASE),
.intc_type =DAVINCI_INTC_TYPE_AINTC,
.intc_irq_prios = dm365_default_priorities,
.intc_irq_num = DAVINCI_N_AINTC_IRQ,
.timer_info =&dm365_timer_info,
.gpio_base =IO_ADDRESS(DAVINCI_GPIO_BASE),
.gpio_num =104,
.gpio_irq =IRQ_DM365_GPIO0,
.gpio_unbanked = 8, /* really 16... skip muxed GPIOs */
.serial_dev =&dm365_serial_device,
.emac_pdata =&dm365_emac_pdata,
.sram_dma =0x00010000,
.sram_len =SZ_32K,
};
接着,我们看以下dm365_pins结构
staticconst struct mux_config dm365_pins[] = {
#ifdefCONFIG_DAVINCI_MUX
MUX_CFG(DM365, MMCSD0, 0,24, 1, 0, false)
MUX_CFG(DM365, SD1_CLK, 0, 16,3, 1, false)
MUX_CFG(DM365, SD1_CMD, 4, 30,3, 1, false)
MUX_CFG(DM365, SD1_DATA3, 4, 28,3, 1, false)
MUX_CFG(DM365, SD1_DATA2, 4, 26,3, 1, false)
MUX_CFG(DM365, SD1_DATA1, 4, 24,3, 1, false)
MUX_CFG(DM365, SD1_DATA0, 4, 22,3, 1, false)
MUX_CFG(DM365, I2C_SDA, 3, 23,3, 2, false) //I2C的SDA管脚
MUX_CFG(DM365, I2C_SCL, 3, 21,3, 2, false) //I2C的SCL管脚
MUX_CFG(DM365, AEMIF_AR, 2, 0,3, 1, false)
……..省略其他的一些内容
#endif
};
这个结构体列出了dm365的管脚复用情况,
接着看一下MUX_CFG这个宏定义是怎么样的,定义在mux.h中
#defineMUX_CFG(soc, desc, muxreg, mode_offset, mode_mask, mux_mode, dbg)\
[soc##_##desc]= { \
.name = #desc, \
.debug = dbg, \
.mux_reg_name ="PINMUX"#muxreg, \
.mux_reg =PINMUX##muxreg, \
.mask_offset =mode_offset, \
.mask = mode_mask, \
.mode = mux_mode, \
},
这个宏就是把(DM365, I2C_SDA, 3, 23,3, 2, false)这样的信息转化成mux_configd的格式,其中mux_config定义在include/mach/mux.h里。如下:
structmux_config {
const char *name;//复用管脚的名字,一般以要复用的功能命名
const char *mux_reg_name;//复用寄存器的名字
const unsigned char mux_reg;//复用的配置寄存器
const unsigned char mask_offset; //偏移量,指寄存器的第几位,比如I2C_SDA是通过
//PINMUX3的24和23位设置的,则偏移量为23,
const unsigned char mask;
const unsigned char mode;
//mask和mode的含义。比如要把PINMUX3的23和24为设置成自己想要的值,假设//mask= 3,mode=2,需要这样(reg_old_value& (~(3<<23))) | (2<<23)
// reg_old_value是寄存器原来的值
bool debug;//调试标志
};
结合这两个结构体和宏可以得出
MUX_CFG(DM365, I2C_SDA, 3, 23,3, 2, false)
[DM365_I2C_SDA] = { .name = I2C_SDA,
.debug = false,
.mux_reg_name = “PINMUX3”,
.mux_reg = PINMUX3,
.mask_offset = 23,
.mask = 3,
.mode = 2,
},
3)、好了,回到davinci_cfg_reg函数的分析。首先检测是否已经定义了管脚复用信息
if(!soc_info->pinmux_pins) 这个因为在前面讲的davinci_soc_info初始化的时候已经有了。所以
这一步会继续执行下去
4)、if(index >= soc_info->pinmux_pins_num)检测我们输入的变量值是否超出系统已经定义的
pinmux_pins管脚复用信息表中已有的所有复用之和,一般情况下,index变量是从一个枚 举列表中选择,后面会详细说明index的来由和定义情况。
5)、cfg =&soc_info->pinmux_pins[index];传入需要配置的复用管脚信息,如复用寄存器,偏移量,
复用功能等,这个其实就是第2)步介绍到的mux_config结构体,执行这一步之后cfg的信息如下
cfg->name = I2C_SDA,
cfg->debug = false,
cfg->mux_reg_name =“PINMUX3”,
cfg->mux_reg = PINMUX3,
cfg->mask_offset = 23,
cfg->mask = 3,
cfg->mode = 2,
其中PINMUXn在dm365.c中定义:它们的基地址在davinci_soc_info_dm365的
.pinmux_base = IO_ADDRESS(DAVINCI_SYSTEM_MODULE_BASE),中已经被定义为0x01c40000
#definePINMUX0 0x00
#definePINMUX1 0x04
#definePINMUX2 0x08
#definePINMUX3 0x0c
#definePINMUX4 0x10
6)、接着到了配置寄存器的具体操作:
if (cfg->mask) { //位移量肯定大于0,除非没有被定义
unsigned tmp1, tmp2;
spin_lock_irqsave(&mux_spin_lock,flags);//添加锁
reg_orig = __raw_readl(base +cfg->mux_reg);//首先读出寄存器原来的配置
mask = (cfg->mask <<cfg->mask_offset); //相当于3<<23
tmp1 = reg_orig & mask;//把寄存器的24和23位置保留,其余位置0,是为了找出原来的
//配置,以便接下来与新配置对比,检测是不是有不一样,
reg = reg_orig & ~mask; 把寄存器的24和23位置0,其余位不改变。
tmp2 = (cfg->mode <<cfg->mask_offset);//把需要配置的模式值以为到24和23.
reg |= tmp2; //改变寄存器的值,此时已经加入了自己的配置信息
if (tmp1 != tmp2)//如果配置前后不一样,输出警告信息,说明寄存器复用情况有变。
warn = 1;
__raw_writel(reg, base +cfg->mux_reg);//写寄存器,最终把自己想要的状态设置进去。
spin_unlock_irqrestore(&mux_spin_lock,flags);//解锁。
}
7)、后面的是一些打印信息。
4、接着说说davinci_cfg_reg(index)里面的index。在include/mach/mux.h中有如下的枚举结构定义
enumdavinci_dm365_index {
/* MMC/SD 0 */
DM365_MMCSD0,
/* MMC/SD 1 */
DM365_SD1_CLK,
DM365_SD1_CMD,
DM365_SD1_DATA3,
DM365_SD1_DATA2,
DM365_SD1_DATA1,
DM365_SD1_DATA0,
/* I2C */
DM365_I2C_SDA,
DM365_I2C_SCL,
…省略很多内容
);
这里的枚举类型和staticconst struct mux_config dm365_pins[] 中的内容一一对应,注意这个对应是位置一一对应,比如DM365_I2C_SDA在枚举中排在第八的位置,那么在dm365_pins[]中的位置也要在第八,否则调用的时候会匹配失误得不到自己想要的结果。
5、最后,说说如何调用davinci_cfg_reg,首先需要包含头文件,以上说到的和其他比如寄存器的读写函数
等的头文件。接着在枚举类型中加入信息,然后在dm365_pins[]对应位置添加配置信息。
6)、以上内容是本人看代码时的一点见解,不确定是否正确,有错误欢迎指正,enjoy~~~~