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