毅哥 发表于 2013-12-28 11:01:08

cubieboard的uboot的GPIO的驱动详解

转发自我的博客:http://blog.sina.com.cn/s/blog_b5020b670101ft49.html

uboot的GPIO相当简单,其就是三层结构。分别为: 1、顶层接口层,其只定义了通用的接口,并不负责实现,实现是我们具体根据具体的芯片来实现的。
2、中间接口实现层,用具体的板子的GPIO来实现顶层的接口
3、 底层具体芯片GPIO的实现层 。

现在具体分析:
顶层接口层

int gpio_request(unsigned gpio, const char *label); //申请GPIO资源
int gpio_free(unsigned gpio); //释放申请的GPIO资源
int gpio_direction_input(unsigned gpio); //设置GPIO为输入模式
int gpio_direction_output(unsigned gpio, int value); //设置GPIO为输出模式
int gpio_get_value(unsigned gpio); //得到GPIO的值
int gpio_set_value(unsigned gpio, int value);//设置GPIO的值
说明:unsigned gpio为逻辑号,虽然和实际的物理GPIO地址有一定的关系,但并不是实际的物理GPIO地址。

中间接口实现层:
用具体的芯片的GPIO来实现其顶层接口

int gpio_request(unsigned gpio, const char *label)
{
return 0;
}

int gpio_free(unsigned gpio)
{
return 0;
}

int gpio_direction_input(unsigned gpio)
{
sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_INPUT);

return sunxi_gpio_input(gpio);
}

int gpio_direction_output(unsigned gpio, int value)
{
sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_OUTPUT);

return sunxi_gpio_output(gpio, value);
}

int gpio_get_value(unsigned gpio)
{
return sunxi_gpio_input(gpio);
}

int gpio_set_value(unsigned gpio, int value)
{
return sunxi_gpio_output(gpio, value);
}

底层具体芯片GPIO的实现层:
在实现的时候,其用了一个小技巧,其目的是把GPIO的物理寄存器放到结构体里面来,从而把物理的地址操作转换为数据结构的操作。
其实现如下:
把SUNXI_PIO_BASE 强制转换为sunxi_gpio_reg *指针来实现。
#define SUNXI_PIO_BASE 0x01c20800



struct sunxi_gpio {
u32 cfg;
u32 dat;
u32 drv;
u32 pull;
};


struct sunxi_gpio_int {
u32 cfg;
u32 ctl;
u32 sta;
u32 deb;
};

struct sunxi_gpio_reg {
struct sunxi_gpio gpio_bank;
u8 res;
struct sunxi_gpio_int gpio_int;
};


我们实现具体的芯片的GPIO的操作的思想是:
使用逻辑符号unsigned gpio,通过SUNXI_PIO_BASE 强制转换为sunxi_gpio_reg *指针的指针来操作相关寄存器。

但是逻辑符号unsigned gpio要通过SUNXI_PIO_BASE 强制转换为sunxi_gpio_reg *指针的指针来操作相关寄存器,必须要解决一个问题,即如何在众多的寄存器的中,找到指定的那个寄存器,并且在该寄存器上找到指定的那些相关位。
即gpio---->bank------>bank中的offset

这个映射关系和具体的芯片有关。
这里只讨论全志的a10芯片。




后面的写不了了,想看完整版的请看我的博客:http://blog.sina.com.cn/s/blog_b5020b670101ft49.html




醉月 发表于 2014-1-4 22:13:46

LZ你好!我今天也在看全志A20 的gpio驱动。
【linux-3.4-cb2】
目录: driver/gpio/gpio-sunxi.c

------------------------
在这个驱动中,我看它使用的是platform平台设备相关编程。
-------------------------
由于是新手,其中有个疑点,还希望LZ能帮忙解答下。
--------------------------
首先,贴下代码:static int __init sunxi_gpio_init(void)
{
        int err = 0;
        err = platform_device_register(&sunxi_gpio_device);
        if (err)
                goto exit;

        return platform_driver_register(&sunxi_gpio_driver);

exit:
        return err;
}
subsys_initcall(sunxi_gpio_init);------------------------static int __devinit sunxi_gpio_probe(struct platform_device *pdev)
{
        int i;
        int err = 0;
        int names_size = 0;
        int gpio_used = 0;
        int gpio_num = 0;
        struct sunxi_gpio_data *gpio_i = NULL;
        struct sunxi_gpio_data *gpio_data = NULL;
        struct sunxi_gpio_chip *sunxi_chip = NULL;
        char **pnames = NULL;

        /* parse script.bin for section
           gpio_used/gpio_num/gpio_pin_x */

        pr_info("sunxi_gpio driver init ver %s\n", SUNXI_GPIO_VER);

       
        err = script_parser_fetch("gpio_para", "gpio_used", &gpio_used,
                                        sizeof(gpio_used)/sizeof(int));

        if (err) {
                /* Not error - just info */
                pr_info("%s can't find script.bin '' 'gpio_used'\n",
                        __func__);
                return err;
        }



        if (!gpio_used) {
                pr_info("%s gpio_used is false. Skip gpio initialization\n",
                        __func__);
                err = 0;
                return err;
        }


        err = script_parser_fetch("gpio_para", "gpio_num", &gpio_num,
                                        sizeof(gpio_num)/sizeof(int));
        if (err) {
                pr_err("%s script_parser_fetch '' 'gpio_num' err\n",
                        __func__);
                return err;
        }

        if (!gpio_num) {
                pr_info("%s gpio_num is none. Skip gpio initialization\n",
                        __func__);
                err = 0;
                return err;
        }

       

        /* Allocate memory for sunxi_gpio_chip + data/names array */
        sunxi_chip = kzalloc(sizeof(struct sunxi_gpio_chip) +
                                sizeof(struct sunxi_gpio_data) * gpio_num,
                                GFP_KERNEL);

       
        gpio_data = (void *)sunxi_chip + sizeof(struct sunxi_gpio_chip);

       

        /* Allocate memory for variable array of fixed size strings */
        /* in one chunk. This is to avoid 1+gpio_num kzalloc calls */
        names_size = sizeof(*pnames) * gpio_num +
                     sizeof(char) * MAX_GPIO_NAMELEN * gpio_num;


        pnames = kzalloc(names_size, GFP_KERNEL);

       
        for (i = 0; i < gpio_num; i++) {
                pnames = (void *)pnames + sizeof(*pnames) * gpio_num +
                                i * MAX_GPIO_NAMELEN;
        }

        if ((!pnames) || (!sunxi_chip)) {
                pr_err("%s kzalloc failed\n", __func__);
                err = -ENOMEM;
                goto exit;
        }

        /* Parse gpio_para/pin script data */
        gpio_i = gpio_data;
       
        for (i = 0; i < gpio_num; i++) {

                sprintf(gpio_i->pin_name, "gpio_pin_%d", i+1);
                err = script_parser_fetch("gpio_para", gpio_i->pin_name,
                                        (int *)&gpio_i->info,
                                        sizeof(script_gpio_set_t));

                if (err) {
                        pr_err("%s script_parser_fetch '' '%s' err\n",
                                __func__, gpio_i->pin_name);
                        break;
                }

                gpio_i++;
        }
       
/*   i/o的物理地址映射到核心虚拟地址空间内 */
        sunxi_chip->gaddr = ioremap(PIO_BASE_ADDRESS, PIO_RANGE_SIZE);

       
        if (!sunxi_chip->gaddr) {
                pr_err("Can't request gpio registers memory\n");
                err = -EIO;
                goto unmap;
        }

        sunxi_chip->dev                = &pdev->dev;
        sunxi_chip->data        = gpio_data;
        sunxi_chip->chip        = template_chip;
        sunxi_chip->chip.ngpio        = gpio_num;
        sunxi_chip->chip.dev        = &pdev->dev;
        sunxi_chip->chip.label        = "A1X_GPIO";
        sunxi_chip->chip.base        = 1;
        sunxi_chip->chip.names        = (const char *const *)pnames;
        sunxi_chip->irq_base        = -1;

        /* configure EINTs for the detected SoC */
        sunxi_gpio_eint_probe();

        /* This needs additional system irq numbers (NR_IRQ=NR_IRQ+EINT_NUM) */
        if (EINT_NUM > 0) {
                sunxi_chip->irq_base = irq_alloc_descs(-1, 0, EINT_NUM, 0);
                if (sunxi_chip->irq_base < 0) {
                        pr_err("Couldn't allocate virq numbers. GPIO irq support disabled\n");
                        err = sunxi_chip->irq_base;
                }
        } else
                pr_info("GPIO irq support disabled in this platform\n");

/*自旋锁初始化*/
        spin_lock_init(&sunxi_chip->irq_lock);

/*irq init*/
        sunxi_gpio_irq_init(sunxi_chip);

/*request irq*/
        if (sunxi_chip->irq_base >= 0) {
                err = request_irq(GPIO_IRQ_NO, sunxi_gpio_irq_handler,
                                  IRQF_SHARED, "sunxi-gpio", sunxi_chip);
                if (err) {
                        pr_err("Can't request irq %d\n", GPIO_IRQ_NO);
                        goto irqchip;
                }
        }

/* register a gpio_chip*/
        err = gpiochip_add(&sunxi_chip->chip);

        if (err < 0)
                goto irqhdl;
/*save gpioprivate data,we cat get these data like this : platform_get_drvdata()*/
        platform_set_drvdata(pdev, sunxi_chip);
        return 0;

irqhdl:
        if (sunxi_chip->irq_base >= 0)
                free_irq(GPIO_IRQ_NO, sunxi_chip);
irqchip:
        sunxi_gpio_irq_remove(sunxi_chip);
        if (sunxi_chip->irq_base >= 0)
                irq_free_descs(sunxi_chip->irq_base, EINT_NUM);
unmap:
        iounmap(sunxi_chip->gaddr);
exit:
        kfree(sunxi_chip);
        kfree(pnames);

        return err;
}
在这里,我只是追到了一些资源的申请和注册,在/sys/class/gpio-sw/目录下可以使用open()wrte() read() ioctl() 这些函数来读取和操作GPIO的数据,但是,我在该驱动中并没发现这些函数的底层实现,这是个什么缘由呢?

谢谢!
页: [1]
查看完整版本: cubieboard的uboot的GPIO的驱动详解