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; }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM