bcm53344 gpio驱动分析
/********************************************************************************* * 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\n", 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\n", __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\n"); 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\n", 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 \n", 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\n", 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\n", chip->irq, ret); } else printk(KERN_ERR "%s is added without isr!\n", 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\n", 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\n", 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; }