/*********************************************************************************
* 1.查看代码是在vim下,使用ctags进行的。也可以使用SourceInsight
* 2.为方便查找,使用“------->>>"加数字/字母进行标记,表示后面会进行详解。
* 使用“<<<------"加数字/字母,表示详解或相同的地方,
* 查看时找到相同的标记进行阅读
 *    本文主要内容:
 *             1.总结
 *         2.结构体介绍
 *         3.gpio_direction_output等函数的调用过程
 *         4.bcm53344初始化过程
*          
*                   Tony Liu, 2015-11-7, Shenzhen
******************************************************************************/
总结
  1.1 BCM53344共有16个GPIO,是由几个寄存器分开控制的。
  1.2 GPIO -3来自CMICD,GPIO -15来自ChipcommonA的GPIO引脚0-
  1.3 GPIO -15与MII或者LED共享,在设置复用功能使,需要硬件进行配置成不同的功能,软件不能配置
    参考硬件手册,了解需要将LED_MII_GPIO-SPI_SEL[:]进行配置,
  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中。 .结构体介绍
/* 所有芯片都有的结构结构 */
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:;
unsigned exported:; #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
};
.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 < )
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 < ) {
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 == )
set_bit(FLAG_IS_OUT, &desc->flags);
trace_gpio_value(chip->base + gpio, , value);
trace_gpio_direction(chip->base + gpio, , 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 = << 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 ;
} .bcm53344初始化过程
static int __init gpio_init(void)
{
iproc_gpiolib_init(); return ;
} 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 = ;
/* 根据iproc_gpios_config知道for循环值执行一次 */
for (gpn = ; 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 ;
} // 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[] = {
[] = {
.id = IPROC_GPIO_CCA_ID,
.chip = {
.base = ,
.label = "GPIOA",
.ngpio = ,
},
.irq_base = IPROC_GPIO_CCA_IRQ_BASE,
.resource = &iproc_gpio_resources[],
.irq = IPROC_GPIO_CCA_INT,
.irqcfg = &cca_gpio_irqcfg,
.pin_offset = ,
},
}
//更改为
struct iproc_gpio_chip iproc_gpios_config[] = {
[] = {
.id = IPROC_GPIO_CCA_ID,
.chip = {
.base = ,
.label = "GPIOA",
.ngpio = ,
},
.resource = &iproc_gpio_resources[],
.pin_offset = ,
},
[] = {
.id = IPROC_GPIO_CCA_ID,
.chip = {
.base = ,
.label = "GPIOA",
.ngpio = ,
},
.irq_base = IPROC_GPIO_CCA_IRQ_BASE,
.resource = &iproc_gpio_resources[],
.irq = IPROC_GPIO_CCA_INT,
.irqcfg = &cca_gpio_irqcfg,
.pin_offset = ,
},
};
//还需要更改resource里面的地址。
#else
static struct resource iproc_gpio_resources[] = {
[] = {
.start = IPROC_GPIO_CCA_BASE,
.end = IPROC_GPIO_CCA_BASE + IPROC_GPIO_REG_SIZE - ,
.flags = IORESOURCE_MEM,
.child = iproc_gpio_cca_config_resource,
},
[] = {
.start = IPROC_GPIO_CCB_BASE,
.end = IPROC_GPIO_CCB_BASE + IPROC_GPIO_REG_SIZE -,
.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[] = {
[] = {
.start = 0x48002000,
.end = 0x48002000 + IPROC_GPIO_REG_SIZE - ,
.flags = IORESOURCE_MEM,
},
[] = { .start = IPROC_GPIO_CCA_BASE,
.end = IPROC_GPIO_CCA_BASE + IPROC_GPIO_REG_SIZE - ,
.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 >= )
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) + );
printk(KERN_INFO "%s:ioaddr %p \n", gc->label, chip->ioaddr);
chip->intr_ioaddr = NULL;
chip->dmu_ioaddr = NULL;
if(res->child){
for (i=; i< ; 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) + );
}
if (!strcmp("dmu", res->child[i].name)){
chip->dmu_ioaddr =
ioremap_nocache(res->child[i].start,
(res->child[i].end - res->child[i].start) + );
}
}
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 = ;
unsigned id;
int base = chip->base;
/* 判断GPIO的序号是否在合理的为内 */
if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - ))
&& base >= ) {
status = -EINVAL;
goto fail;
} spin_lock_irqsave(&gpio_lock, flags); if (base < ) {
base = gpiochip_find_base(chip->ngpio);
if (base < ) {
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 == ) {
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
? ( << FLAG_IS_OUT)
: ;
}
} 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 - ,
chip->label ? : "generic"); return ;
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 - ,
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 = << 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 ;
}

bcm53344 gpio驱动分析的更多相关文章

  1. uboot的GPIO驱动分析--基于全志的A10芯片【转】

    本文转载自:http://blog.csdn.net/lw2011cg/article/details/68954707 uboot的GPIO驱动分析--基于全志的A10芯片 转载至:http://b ...

  2. Linux下GPIO驱动(三) ----gpio_desc()的分析

    上篇最后提出的疑问是结构体gpio_chip中的成员函数set等是怎么实现的,在回答之前先介绍下gpio_desc这个结构体. 如上图所示,右上方部分为GPIO驱动对其它驱动提供的GPIO操作接口,其 ...

  3. linux内核SPI总线驱动分析(一)(转)

    linux内核SPI总线驱动分析(一)(转) 下面有两个大的模块: 一个是SPI总线驱动的分析            (研究了具体实现的过程) 另一个是SPI总线驱动的编写(不用研究具体的实现过程) ...

  4. 调试exynos4412—ARM嵌入式Linux—LEDS/GPIO驱动之一

    /** ****************************************************************************** * @author    暴走的小 ...

  5. linux i2c驱动架构-dm368 i2c驱动分析

      linux i2c驱动架构-dm368 i2c驱动分析   在阅读本文最好先熟悉一种i2c设备的驱动程序,并且浏览一下i2c-core.c以及芯片提供商的提供的i2c总线驱动(i2c-davinc ...

  6. Linux SD/MMC/SDIO驱动分析_转

    转自:Linux SD/MMC/SDIO驱动分析    https://www.cnblogs.com/cslunatic/p/3678045.html#3053341 一.SD/MMC/SDIO概念 ...

  7. linux驱动基础系列--Linux mmc sd sdio驱动分析

    前言 主要是想对Linux mmc子系统(包含mmc sd sdio)驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如平台驱动.块设备驱动.设备模型等也不进行详细说明原 ...

  8. linux驱动学习(八) i2c驱动架构(史上最全) davinc dm368 i2c驱动分析【转】

    转自:http://blog.csdn.net/ghostyu/article/details/8094049 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] 预备知识 lin ...

  9. 很好的linux下GPIO驱动详解文章

    原文地址  http://blog.csdn.net/llxmedici/article/details/6282372 打算跟着友善之臂的<mini2440 linux移植开发指南>来做 ...

随机推荐

  1. Python hex() 函数

    描述 hex() 函数用于将10进制整数转换成16进制整数. 语法 hex 语法: hex(x) 参数说明: x -- 10进制整数 返回值 返回16进制整数. 实例 以下实例展示了 hex 的使用方 ...

  2. 阿里云-DRDS(转)

    分库分表 DRDS 在后端将数据量较大的数据表水平拆分到后端的每个 RDS 数据库中,这些拆分到RDS中的数据库被称为分库,分库中的表称为分表.DRDS 由每个分库负责每一份数据的读写操作,从而有效的 ...

  3. keras在win7下环境搭建

    无gpu安装过程:一.卸载之前版本.  把之前单独安装的Python等统统卸载掉.学python的时候直接安装了python2.7,先把他卸载掉,因为Anaconda里边包含了python.二.安装A ...

  4. 将linux下的rm命令改造成mv到指定的目录下

    rm是Linux下文件删除的命令,它是Linux下非常强大却又非常危险的一条命令,特别是rm -rf有时候强大到让你欲哭无泪,当你想清除当前目录下的所有文件和目录时,很简单#rm -rf ./*这没什 ...

  5. Java web中listener、 filter、servlet 加载顺序

    真正的加载顺序为:context-param -> listener -> filter -> servlet 加载顺序与它们在 web.xml 文件中的先后顺序无关.即不会因为 f ...

  6. testbench常用语句 很详细相当实用

    内容 与可综合Verilog代码所不同的是,testbench Verilog是在计算机主机上的仿真器中执行的.testbench Verilog的许多构造与C语言相似,我们可在代码中包括复杂的语言结 ...

  7. receiver type *** for instance message is a forward declaration

    转自:http://stackoverflow.com/questions/8815200/receiver-type-for-instance-message-is-a-forward-declar ...

  8. asp.net用三层实现多条件检索

    众所周知,三层将项目分为界面层,业务逻辑层和数据訪问层(以最主要的三层为例) 相同都知道,多条件检索事实上就是依据用户选择的条件项,然后来拼sql语句 那么.既然要依据用户选择的条件项来拼sql语句, ...

  9. 告诉你38个MySQL数据库的小技巧

    无论是运维.开发.测试,还是架构师,数据库技术是一个必备加薪神器,那么,一直说学习数据库.学MySQL,到底是要学习它的哪些东西呢? 1.如何快速掌握MySQL? 培养兴趣 兴趣是最好的老师,不论学习 ...

  10. Java、MySQL - 前补0的方法

    前补0的格式化方式在业务系统中经常使用,记录下此api. Java: public static void main(String[] args) { // 0002 System.out.print ...