`

dm365管脚复用配置浅析之davinci_cfg_reg调用

 
阅读更多

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_infodavinci_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~~~~

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics