1.问题描述
在通过GPIO模拟I2C时序的问题,要用到PG11这个引脚,而这个引脚默认是JTAG0的TDO,所在在加载ko模块时,会现现
# echo 203 > export
[***liuyouhua-debug***] offset: 203 group: 6 numl: 11 num: 3 value: 0x7
Please Check GPIOG11's multi-function = 0x7
注:203是PG11对应的CPU引脚封装号,定义在$(KERN-DIR)/arch/arm/mach-nuc980/include/mach/gpio.h中:
#define NUC980_PG11 (0xC0 + 11)
#define NUC980_PG12 (0xC0 + 12)
#define NUC980_PG13 (0xC0 + 13)
#define NUC980_PG14 (0xC0 + 14)
#define NUC980_PG15 (0xC0 + 15)
2. 问题分析
通过跟踪内核代码发现,PG11没有得到$(KERN-DIR)/drivers/pinctrl-nuc980.c里nuc980_set_mux函数的处理:
/*
* selector = data.nux.func, which is entry number in nuc980_functions,
* and group = data.mux.group, which is entry number in nuc980_pmx_func
* group is not used since some function use different setting between
* different ports. for example UART....
*/
int nuc980_set_mux(struct pinctrl_dev *pctldev, unsigned selector,
unsigned group)
{
unsigned int i, j;
unsigned int reg, offset;
for(i = 0; i < nuc980_pinctrl_groups[group].num_pins; i++) {
j = nuc980_pinctrl_groups[group].pins[i];
offset = (j >> 4) * 8 + ((j & 0x8) ? 4 : 0); //计算j在REG_MFP_GPX定义中的偏移
reg = __raw_readl(REG_MFP_GPA_L + offset);
reg = (reg & ~(0xF << ((j & 0x7) * 4))) | (nuc980_pinctrl_groups[group].func << ((j & 0x7) * 4));
__raw_writel(reg, REG_MFP_GPA_L + offset);
}
return 0;
}
- reg是获取对应的REG_MFP_GPx_x配置参数,如获取PG11是0x77777000
- nuc980_pinctrl_groups[group].func对应nuc980_pinctrl_groups[]中的func
- nuc980_pinctrl_groups数组在$(KERN-DIR)/drivers/pinctrl/pinctrl-nuc980.c定义,其中0x6B对应的PG11的定义:
const struct pinctrl_pin_desc nuc980_pins[] = {
//......
PINCTRL_PIN(0x6B, "PG11"),
PINCTRL_PIN(0x6C, "PG12"),
PINCTRL_PIN(0x6D, "PG13"),
PINCTRL_PIN(0x6E, "PG14"),
PINCTRL_PIN(0x6F, "PG15"),
};
static const unsigned jtag0_pins[] = {0x6B, 0x6C, 0x6D, 0x6E, 0x6F};
static const struct nuc980_pinctrl_group nuc980_pinctrl_groups[] = {
//......
{
.name = "jtag0_grp",
.pins = jtag0_pins,
.num_pins = ARRAY_SIZE(jtag0_pins),
.func = 0x7,
},
//......
};
- group参数对应nuc980_pinctrl_groups数据下标,由于我在$(KERN-DIR)/arch/arm/mach-nuc980/dev.c中关闭了所有引脚PG11的设备注册,所以PG11的nuc980_set_mux并没有得到调用。
- offset = (j >> 4) * 8 + ((j & 0x8) ? 4 : 0);根据j参数出对应的REG_MFP_GPx_L/H,REG_MFP_GPx定义在$(KERN-DIR)/arch/arm/mach-nuc980/include/mach/regs-gcc.h中:
#define REG_MFP_GPA_L (GCR_BA+0x070) /* GPIOA Low Byte Multiple Function Control Register */
#define REG_MFP_GPA_H (GCR_BA+0x074) /* GPIOA High Byte Multiple Function Control Register */
#define REG_MFP_GPB_L (GCR_BA+0x078) /* GPIOB Low Byte Multiple Function Control Register */
#define REG_MFP_GPB_H (GCR_BA+0x07C) /* GPIOB High Byte Multiple Function Control Register */
#define REG_MFP_GPC_L (GCR_BA+0x080) /* GPIOC Low Byte Multiple Function Control Register */
#define REG_MFP_GPC_H (GCR_BA+0x084) /* GPIOC High Byte Multiple Function Control Register */
#define REG_MFP_GPD_L (GCR_BA+0x088) /* GPIOD Low Byte Multiple Function Control Register */
#define REG_MFP_GPD_H (GCR_BA+0x08C) /* GPIOD High Byte Multiple Function Control Register */
#define REG_MFP_GPE_L (GCR_BA+0x090) /* GPIOE Low Byte Multiple Function Control Register */
#define REG_MFP_GPE_H (GCR_BA+0x094) /* GPIOE High Byte Multiple Function Control Register */
#define REG_MFP_GPF_L (GCR_BA+0x098) /* GPIOF Low Byte Multiple Function Control Register */
#define REG_MFP_GPF_H (GCR_BA+0x09C) /* GPIOF High Byte Multiple Function Control Register */
#define REG_MFP_GPG_L (GCR_BA+0x0A0) /* GPIOG Low Byte Multiple Function Control Register */
#define REG_MFP_GPG_H (GCR_BA+0x0A4) /* GPIOG High Byte Multiple Function Control Register */
#define REG_MFP_GPH_L (GCR_BA+0x0A8) /* GPIOH Low Byte Multiple Function Control Register */
#define REG_MFP_GPH_H (GCR_BA+0x0AC) /* GPIOH High Byte Multiple Function Control Register */
#define REG_MFP_GPI_L (GCR_BA+0x0B0) /* GPIOI Low Byte Multiple Function Control Register */
#define REG_MFP_GPI_H (GCR_BA+0x0B4) /* GPIOI High Byte Multiple Function Control Register */
#define REG_MFP_GPJ_L (GCR_BA+0x0B8) /* GPIOJ Low Byte Multiple Function Control Register */
#define REG_DDR_DS_CR (GCR_BA+0x0E0) /* DDR I/O Driving Strength Control Register */
#define REG_PORDISCR (GCR_BA+0x100) /* Power-On-Reset Disable Control Register */
#define REG_ICEDBGCR (GCR_BA+0x104) /* ICE Debug Interface Control Register */
#define REG_WRPRTR (GCR_BA+0x1FC) /* Register Write-Protection Control Register */
实例解析PG11:
- jtag0_pins[]中定义PG11的是0x6B,因此offset = (j >> 4) * 8 + ((j & 0x8) ? 4 : 0)中j的值是0x6B;
- j>>4计算出0x6B所属GPIO组(0x6B对应REG_MFP_GPG组);
- (j >> 4) * 8的8代表8个字节,具体查看《NUC980_Technical_Rev》中System Manager的Register Description段(如下图);
- ((j & 0x8) ? 4 : 0)计算是REG_MFP_GPx_L还是REG_MFP_GPx_H。
3.解决方法
在驱动模块内,自己封装GPIO的set_mux,如:
int nuc980_gpio_set_mux(unsigned int gpio_num)
{
unsigned int reg, offset;
offset = (gpio_num >> 4) * 8 + ((gpio_num & 0x8) ? 4 : 0); //计算j在REG_MFP_GPX定义中的偏移
reg = __raw_readl(REG_MFP_GPA_L + offset);
reg = (reg & ~(0xF << ((gpio_num & 0x7) * 4))) | (0x0 << ((gpio_num & 0x7) * 4));
__raw_writel(reg, REG_MFP_GPA_L + offset);
return 0;
}
- gpio_num参数在$(KERN-DIR)/drivers/pinctrl/pinctrl-nuc980.c中定义:
PINCTRL_PIN(0x6B, "PG11"),
PINCTRL_PIN(0x6C, "PG12"),
PINCTRL_PIN(0x6D, "PG13"),
PINCTRL_PIN(0x6E, "PG14"),
PINCTRL_PIN(0x6F, "PG15"),
- 调用方法:
nuc980_gpio_set_mux(0x6B);