/*********************************************************************************
* 1.查看代码是在vim下,使用ctags进行的。也可以使用SourceInsight
* 2.为方便查找,使用“------->>>"加数字/字母进行标记,表示后面会进行详解。
* 使用“<<<------"加数字/字母,表示详解或相同的地方,
* 查看时找到相同的标记进行阅读
* 本文主要内容:
* 1.总结
* 2.结构体介绍
* 3.gpio_direction_output等函数的调用过程
* 4.bcm53344初始化过程
*
* Tony Liu, 2015-11-7, Shenzhen
******************************************************************************/
1 总结
1.1 BCM53344共有16个GPIO,是由几个寄存器分开控制的。
1.2 GPIO 0-3来自CMICD,GPIO 4-15来自ChipcommonA的GPIO引脚0-11
1.3 GPIO 8-15与MII或者LED共享,在设置复用功能使,需要硬件进行配置成不同的功能,软件不能配置
参考硬件手册,了解需要将LED_MII_GPIO-SPI_SEL[1:0]进行配置,
1.4 三个重要的结构体
gpio_desc : 内核中提供的公共结构体,内核中会定义一个结构提数组.
每一个GPIO口会对应一个.
gpio_chip : 内核中提供的公共结构体, 内核中会定义一个结构体数组.
每一个GPIO口会对应一个.启动定义了输入输出等各种处理函数
iproc_gpio_chip : 不同的芯片,不同的平台自己定义的结构体
其中记录了芯片的寄存器地址,物理地址映射的虚拟地址等
1.5.内核采用面向对象的思想,定义共有的接口gpio_desc,而gpio_desc中的chip指针指向iproc_gpio_chip中的chip。
这样就可以通过共有接口访问不同芯片的数据。例如,gpio_direction_output/gpio_request等,会使用gpio_desc,
调用其中的gpio_chip,间接调用其中的 direction_input/request函数,如下所示。而request, free,direction_input,
get,direction_output等已经在初始化的时候进行了。例如上面的direction_input指向 iproc_gpiolib_input,
这样就可以访问平台的数据。
初始化函数所做的工作就是将 gpio_desc和 gpio_chip以及平台自己定义的结构提 iproc_gpio_chip结构提联系起来。
并将gpio_chip中的函数指针指向处理函数。将物理地址转化为虚拟地址,保存到iproc_gpio_chip中。
2.结构体介绍
/* 所有芯片都有的结构结构 */
struct gpio_desc {
struct gpio_chip *chip; // -------------------------->>> chip
unsigned long flags;
/* flag symbols are bit numbers */
#define FLAG_REQUESTED 0
#define FLAG_IS_OUT 1
#define FLAG_RESERVED 2
#define FLAG_EXPORT 3 /* protected by sysfs_lock */
#define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */
#define FLAG_TRIG_FALL 5 /* trigger on falling edge */
#define FLAG_TRIG_RISE 6 /* trigger on rising edge */
#define FLAG_ACTIVE_LOW 7 /* sysfs value has active low */
#define FLAG_OPEN_DRAIN 8 /* Gpio is open drain type */
#define FLAG_OPEN_SOURCE 9 /* Gpio is open source type */
#define ID_SHIFT 16 /* add new flags before this one */
#define GPIO_FLAGS_MASK ((1 << ID_SHIFT) - 1)
#define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))
#ifdef CONFIG_DEBUG_FS
const char *label;
#endif
};
/* 通过container_of找到iproc_gpio_chip的首地址,就可以访问iproc_gpio_chip里面的数据了 */
static inline struct iproc_gpio_chip *to_iproc_gpio(struct gpio_chip *gpc)
{
return container_of(gpc, struct iproc_gpio_chip, chip);
}
/* 不同公司芯片自己定义的结构提 */
struct iproc_gpio_chip {
int id;
struct gpio_chip chip; // <<<----------------------- chip
struct iproc_gpio_cfg *config;
void __iomem *ioaddr;
void __iomem *intr_ioaddr;
void __iomem *dmu_ioaddr;
spinlock_t lock;
int irq_base;
struct resource * resource;
int irq;
struct iproc_gpio_irqcfg *irqcfg;
int pin_offset;
};
struct gpio_chip {
const char *label;
struct device *dev;
struct module *owner;
int (*request)(struct gpio_chip *chip,
unsigned offset);
void (*free)(struct gpio_chip *chip,
unsigned offset);
int (*direction_input)(struct gpio_chip *chip,
unsigned offset);
int (*get)(struct gpio_chip *chip,
unsigned offset);
int (*direction_output)(struct gpio_chip *chip,
unsigned offset, int value);
int (*set_debounce)(struct gpio_chip *chip,
unsigned offset, unsigned debounce);
void (*set)(struct gpio_chip *chip,
unsigned offset, int value);
int (*to_irq)(struct gpio_chip *chip,
unsigned offset);
void (*dbg_show)(struct seq_file *s,
struct gpio_chip *chip);
int base;
u16 ngpio;
const char *const *names;
unsigned can_sleep:1;
unsigned exported:1;
#if defined(CONFIG_OF_GPIO)
/*
* If CONFIG_OF is enabled, then all GPIO controllers described in the
* device tree automatically may have an OF translation
*/
struct device_node *of_node;
int of_gpio_n_cells;
int (*of_xlate)(struct gpio_chip *gc,
const struct of_phandle_args *gpiospec, u32 *flags);
#endif
};
3.gpio_direction_output等函数的调用过程
3.1 大致过程
gpio_direction_output {
struct gpio_chip *chip;
gpio_desc *desc = &gpio_desc[gpio];
chip = desc->chip;
chip->direction_output(chip, gpio, value);
} |
|
V
iproc_gpiolib_output(struct gpio_chip *chip,unsigned gpio, int value)
这样就可以调用平台的自己的数据了。
3.2 详解:
int gpio_direction_output(unsigned gpio, int value)
{
unsigned long flags;
struct gpio_chip *chip;
struct gpio_desc *desc = &gpio_desc[gpio];
int status = -EINVAL;
/* Open drain pin should not be driven to 1 */
if (value && test_bit(FLAG_OPEN_DRAIN, &desc->flags))
return gpio_direction_input(gpio);
/* Open source pin should not be driven to 0 */
if (!value && test_bit(FLAG_OPEN_SOURCE, &desc->flags))
return gpio_direction_input(gpio);
spin_lock_irqsave(&gpio_lock, flags);
if (!gpio_is_valid(gpio))
goto fail;
chip = desc->chip;
if (!chip || !chip->set || !chip->direction_output)
goto fail;
gpio -= chip->base;
if (gpio >= chip->ngpio)
goto fail;
status = gpio_ensure_requested(desc, gpio);
if (status < 0)
goto fail;
/* now we know the gpio is valid and chip won't vanish */
spin_unlock_irqrestore(&gpio_lock, flags);
might_sleep_if(chip->can_sleep);
if (status) {
status = chip->request(chip, gpio);
if (status < 0) {
pr_debug("GPIO-%d: chip request fail, %d
",
chip->base + gpio, status);
/* and it's not available to anyone else ...
* gpio_request() is the fully clean solution.
*/
goto lose;
}
}
/* 这里就是不同GPIO对应的函数,
* BCM53344对应那个的接口函数就是iproc_gpiolib_output
* 在iproc_gpiolib_add中指定 */
/* 再通过这个函数调用不同的平台私有的数据 */
status = chip->direction_output(chip, gpio, value);
if (status == 0)
set_bit(FLAG_IS_OUT, &desc->flags);
trace_gpio_value(chip->base + gpio, 0, value);
trace_gpio_direction(chip->base + gpio, 0, status);
lose:
return status;
fail:
spin_unlock_irqrestore(&gpio_lock, flags);
if (status)
pr_debug("%s: gpio-%d status %d
",
__func__, gpio, status);
return status;
}
EXPORT_SYMBOL_GPL(gpio_direction_output);
int iproc_gpiolib_output(struct gpio_chip *chip,
unsigned gpio, int value)
{
/* 找到不同平台自己定义的结构体 */
struct iproc_gpio_chip *ourchip = to_iproc_gpio(chip);
unsigned long flags;
unsigned long val;
unsigned int pin_offset = gpio + ourchip->pin_offset;
unsigned int nBitMask = 1 << pin_offset;
iproc_gpio_lock(ourchip, flags);
val = _iproc_gpio_readl(ourchip, REGOFFSET_GPIO_EN);
/* 置1,输出使能 */
val |= nBitMask;
_iproc_gpio_writel(ourchip, val, REGOFFSET_GPIO_EN);
iproc_gpio_unlock(ourchip, flags);
return 0;
}
4.bcm53344初始化过程
static int __init gpio_init(void)
{
iproc_gpiolib_init();
return 0;
}
int iproc_gpiolib_init(void)
{
/* 根据宏定义的情况直到,iproc_gpios_config
* CONFIFG_MACH_HR
*/
struct iproc_gpio_chip *chip = iproc_gpios_config; // --------------------------->>> 1
int gpn;
int temp_base;
...... (省略)
temp_base = 0;
/* 根据iproc_gpios_config知道for循环值执行一次 */
for (gpn = 0; gpn < ARRAY_SIZE(iproc_gpios_config); gpn++, chip++) {
if (gpn >= MAX_NS_GPIO){
printk("Unavailabe to add gpiolib
");
return -EINVAL;
}
/* gpioA : chip.base = 0
*/
if (chip->chip.base == -EINVAL) {
chip->chip.base = temp_base;
}
/* 虚拟地址映射,设置中断 */
iproc_gpiolib_add(chip); // ------------------------------>>> 2
temp_base = chip->chip.base + chip->chip.ngpio;
}
return 0;
}
// iproc_gpios_config // <<<------------------------------------ 1
根据不同平台确定结构体,我的平台的hurricane2
/*
* 注意下面的注释中说明
* GPIO 0-3来自CMICD
* GPIO 4-15来自ChipcommonA的GPIO引脚0-11
* GPIO 8-15与MII或者LED共享
* 因此base是4,个数使12
*/
所以在配置寄存器的时候就要去找CMICD,ChipcommonA,MII,LED相关的寄存器
/* GPIO 8-15是功能复用引脚,需要硬件进行配置成不同的功能,软件不能配置
* 参考硬件手册,了解需要将LED_MII_GPIO-SPI_SEL[1:0]进行配置,
* 找到对应的引脚,检查硬件是否连接正确,配置为GPIO功能 */
#elif defined(CONFIG_MACH_HR2)
/*
* Chip level GPIO 0-3 from CMICD,
* GPIO 4-15 are from ChipcommonA gpio pin 0 - 11
* where GPIO 8-15 are shared with MII or LED depends on strap pin
* Hence the base is 4 and the number is 12.
*/
//原来的
struct iproc_gpio_chip iproc_gpios_config[] = {
[0] = {
.id = IPROC_GPIO_CCA_ID,
.chip = {
.base = 4,
.label = "GPIOA",
.ngpio = 12,
},
.irq_base = IPROC_GPIO_CCA_IRQ_BASE,
.resource = &iproc_gpio_resources[0],
.irq = IPROC_GPIO_CCA_INT,
.irqcfg = &cca_gpio_irqcfg,
.pin_offset = 0,
},
}
//更改为
struct iproc_gpio_chip iproc_gpios_config[] = {
[0] = {
.id = IPROC_GPIO_CCA_ID,
.chip = {
.base = 0,
.label = "GPIOA",
.ngpio = 4,
},
.resource = &iproc_gpio_resources[0],
.pin_offset = 0,
},
[1] = {
.id = IPROC_GPIO_CCA_ID,
.chip = {
.base = 4,
.label = "GPIOA",
.ngpio = 12,
},
.irq_base = IPROC_GPIO_CCA_IRQ_BASE,
.resource = &iproc_gpio_resources[1],
.irq = IPROC_GPIO_CCA_INT,
.irqcfg = &cca_gpio_irqcfg,
.pin_offset = 0,
},
};
//还需要更改resource里面的地址。
#else
static struct resource iproc_gpio_resources[] = {
[0] = {
.start = IPROC_GPIO_CCA_BASE,
.end = IPROC_GPIO_CCA_BASE + IPROC_GPIO_REG_SIZE - 1,
.flags = IORESOURCE_MEM,
.child = iproc_gpio_cca_config_resource,
},
[1] = {
.start = IPROC_GPIO_CCB_BASE,
.end = IPROC_GPIO_CCB_BASE + IPROC_GPIO_REG_SIZE -1,
.flags = IORESOURCE_MEM,
}
};
//CMIC_GP_DATA_IN 寄存器地址 0x48002000
//CMIC_GP_DATA_OUT 寄存器地址 0x48002004
//CMIC_GP_DATA_EN 寄存器地址 0x48002008
#else
static struct resource iproc_gpio_resources[] = {
[0] = {
.start = 0x48002000,
.end = 0x48002000 + IPROC_GPIO_REG_SIZE - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IPROC_GPIO_CCA_BASE,
.end = IPROC_GPIO_CCA_BASE + IPROC_GPIO_REG_SIZE - 1,
.flags = IORESOURCE_MEM,
.child = iproc_gpio_cca_config_resource,
}
};
//iproc_gpiolib_init(void) 的最后调用 iproc_gpiolib_add(chip);
void __init iproc_gpiolib_add(struct iproc_gpio_chip *chip) // <<<------------------------- 2
{
struct resource *res;
struct gpio_chip *gc = &chip->chip;
int ret, i;
/* 系统定义的宏,用于检查lable,ngpio是否为空,空的话报错,并提示异常 */
BUG_ON(!gc->label);
BUG_ON(!gc->ngpio);
spin_lock_init(&chip->lock);
/* 指定处理函数 */
if (!gc->direction_input) // ----------------------------->>> 3
gc->direction_input = iproc_gpiolib_input;
if (!gc->direction_output)
gc->direction_output = iproc_gpiolib_output;
if (!gc->set)
gc->set = iproc_gpiolib_set;
if (!gc->get)
gc->get = iproc_gpiolib_get;
if (!gc->to_irq)
gc->to_irq = iproc_gpiolib_to_irq;
/* gpiochip_add() prints own failure message on error. */
/* 给每一个gpio分配一个gpio_desc结构体 */
/* gpio_desc是每一款芯片都有的结构体,并且其中的内容相同 */
ret = gpiochip_add(gc); // ------------------------------>>> 4
/* 同一个寄存器控制的GPIO,iproc_gpio指针指向同一个iproc_gpio_chip */
/* iproc_gpio是bcm自己定义的结构体,里面有自己寄存器地址等信息 */
if (ret >= 0)
iproc_gpiolib_track(chip);
printk(KERN_INFO "iproc gpiochip add %s
", gc->label);
/* io remap */
res = chip->resource;
/* 虚拟地址映射,GPIO的基地址开始映射 */
chip->ioaddr = ioremap_nocache(res->start, (res->end - res->start) + 1);
printk(KERN_INFO "%s:ioaddr %p
", gc->label, chip->ioaddr);
chip->intr_ioaddr = NULL;
chip->dmu_ioaddr = NULL;
if(res->child){
for (i=0; i< 2; i++){
/* 虚拟地址映射,CCA的基地址开始映射 */
if (!strcmp("intr", res->child[i].name)){
chip->intr_ioaddr =
ioremap_nocache(res->child[i].start,
(res->child[i].end - res->child[i].start) + 1);
}
if (!strcmp("dmu", res->child[i].name)){
chip->dmu_ioaddr =
ioremap_nocache(res->child[i].start,
(res->child[i].end - res->child[i].start) + 1);
}
}
printk(KERN_INFO "%s:intr_ioaddr %p dmu_ioaddr %p
",
gc->label, chip->intr_ioaddr,chip->dmu_ioaddr);
}
/* 中断 */
if (chip->irq_base) {
for (i = chip->irq_base; i < (chip->irq_base + gc->ngpio); i++) {
irq_set_chip(i, &iproc_gpio_irq_chip);
irq_set_chip_data(i,chip);
irq_set_handler(i, handle_level_irq);
set_irq_flags(i, IRQF_VALID);
}
#if defined(IPROC_GPIO_CCA)
if (chip->id == IPROC_GPIO_CCA_ID ){
unsigned int val;
/* enable the GPIO in CCA interrupt mask */
val = readl(chip->intr_ioaddr + IPROC_CCA_INT_MASK);
val |= IPROC_CCA_INT_F_GPIOINT;
writel(val, chip->intr_ioaddr + IPROC_CCA_INT_MASK);
}
#endif
if (chip->irqcfg) {
struct iproc_gpio_irqcfg *irqcfg = chip->irqcfg;
if (irqcfg->handler) {
ret = request_irq(chip->irq, irqcfg->handler, irqcfg->flags,
gc->label, chip);
if (ret)
printk(KERN_ERR "Unable to request IRQ%d: %d
",
chip->irq, ret);
}
else
printk(KERN_ERR "%s is added without isr!
", chip->chip.label);
}
}
iproc_gpio_dev[dev] = chip;
dev++;
}
int gpiochip_add(struct gpio_chip *chip) // <<<----------------------------- 4
{
unsigned long flags;
int status = 0;
unsigned id;
int base = chip->base;
/* 判断GPIO的序号是否在合理的为内 */
if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - 1))
&& base >= 0) {
status = -EINVAL;
goto fail;
}
spin_lock_irqsave(&gpio_lock, flags);
if (base < 0) {
base = gpiochip_find_base(chip->ngpio);
if (base < 0) {
status = base;
goto unlock;
}
chip->base = base;
}
/* these GPIO numbers must not be managed by another gpio_chip */
for (id = base; id < base + chip->ngpio; id++) {
/* 判断结构体数组原始是否被别的gpio_chip使用,如果有使用,那么status进行标记 */
if (gpio_desc[id].chip != NULL) {
status = -EBUSY;
break;
}
}
/* 如果都没有使用,那么就进行初始化 */
if (status == 0) {
for (id = base; id < base + chip->ngpio; id++) {
/* 都指向统一个chip结构 */
gpio_desc[id].chip = chip;
/* REVISIT: most hardware initializes GPIOs as
* inputs (often with pullups enabled) so power
* usage is minimized. Linux code should set the
* gpio direction first thing; but until it does,
* we may expose the wrong direction in sysfs.
*/
gpio_desc[id].flags = !chip->direction_input
? (1 << FLAG_IS_OUT)
: 0;
}
}
of_gpiochip_add(chip);
unlock:
spin_unlock_irqrestore(&gpio_lock, flags);
if (status)
goto fail;
status = gpiochip_export(chip);
if (status)
goto fail;
pr_debug("gpiochip_add: registered GPIOs %d to %d on device: %s
",
chip->base, chip->base + chip->ngpio - 1,
chip->label ? : "generic");
return 0;
fail:
/* failures here can mean systems won't boot... */
pr_err("gpiochip_add: gpios %d..%d (%s) failed to register
",
chip->base, chip->base + chip->ngpio - 1,
chip->label ? : "generic");
return status;
}
EXPORT_SYMBOL_GPL(gpiochip_add);
// 制定的处理函数举例
int iproc_gpiolib_input(struct gpio_chip *chip, unsigned gpio) // <<<--------------------- 3
{
/* 由于有16个GPIO,并且是由不同的寄存器控制
* 所以GPIO0-GPIO3的iproc_gpio_chip结构体与4-15是不同的 */
struct iproc_gpio_chip *ourchip = to_iproc_gpio(chip);
unsigned long flags;
unsigned int val;
unsigned int pin_offset = gpio + ourchip->pin_offset;
unsigned int nBitMask = 1 << pin_offset;
iproc_gpio_lock(ourchip, flags);
val = _iproc_gpio_readl(ourchip, REGOFFSET_GPIO_EN);
/* 输入使能,清0 */
val &= ~nBitMask;
_iproc_gpio_writel(ourchip, val, REGOFFSET_GPIO_EN);
iproc_gpio_unlock(ourchip, flags);
return 0;
}