1. 程式人生 > >39 解決全志h3 linux核心原始碼裡的關於script.fex的bug

39 解決全志h3 linux核心原始碼裡的關於script.fex的bug

在script.fex裡有關於io口的配置:

    Port:埠+組內序號<功能分配><內部電阻狀態><驅動能力><輸出電平狀態>

    [gpio_para]
    gpio_used       = 1
    ;gpio_num        = 30
    ;gpio_pin_1      = port:PL10<1><default><default><1>
    ;gpio_pin_2      = port:PA15<1><default><default
>
<0> ...

發現在核心原始碼裡並沒有對io口的上下拉功能,驅動電流等級等相關的配置暫存器進行設定,從而讓io口的上下拉功能,驅動電流等功能根本無法應用.

自己寫程式碼來根據script.bin裡的io口相關設定來配置相應的暫存器解決這個問題:

1) 使用核心裡的匯出函式”script_get_pio_list”, 獲取script.bin裡關於主鍵”gpio_para”所有關於gpio的子鍵的配置值.

    script_item_u *list;
    n = script_get_pio_list("gpio_para", &list); //返回值為gpio口的個數

2) 對映關於io口的配置暫存器基地址, 並準備好每組的gpio口的配置暫存器.
這裡寫圖片描述

根據上圖,封裝一個結構體。

#define PIO_BASE  0x01c20800
typedef volatile unsigned int uint32;
typedef struct {
    // gpio口的功能配置暫存器, 每個io口在配置暫存器裡佔用4位(實際用3位)
    uint32 CFGs[4];  //4個功能配置暫存器,每個寄存配置8個io口

    // gpio口的資料暫存器, 每個io口占用一位.
    uint32 DAT;

    // gpio口驅動電流配置暫存器, 每個io口占用2位 
uint32 DRVs[2]; // 兩個暫存器,每個暫存器配置16個io口 // gpio口上下拉配置暫存器, 每個io口占用2位 uint32 PULLs[2]; // 兩個暫存器,每個暫存器配置16個io口 }PIO_t; //表示每組io口都有的配置暫存器 static PIO_t *pios[7]; // 每個元素分別存放PA, PB, PC, PD, PE, PF, PG組io口的暫存器地址 static u8 *vaddr;

並在對映暫存器地址後,分配好每組io口的暫存器地址

    vaddr = ioremap(PIO_BASE, SZ_4K); //對映gpio配置暫存器的基地址
    if (NULL == vaddr)
        return -ENOMEM;

    for (i = 0; i < ARRAY_SIZE(pios); i++) //準備好每組io口的配置暫存器的地址
        pios[i] = (PIO_t *)(vaddr+i*0x24);

3) 再根據從script.bin獲取出來的io口相關配置值來設定暫存器

    for (i = 0; i < n; i++)
    {
        //在核心裡每組io口都算是32個一組,即使硬體上每組都沒有32個io口那麼多.
        //所以list[i].gpio.gpio / 32 即可算出是第幾組, 
        j = list[i].gpio.gpio >> 5;   // pios[j]就是表示相應組的配置寄存結構體
        k = list[i].gpio.gpio & 31;  //算出在組內是第幾個io口

        sel = list[i].gpio.mul_sel; //script.bin裡io功能選擇
        pull = list[i].gpio.pull;   //上下拉功能
        drv = list[i].gpio.drv_level; //驅動電流等級
        level = list[i].gpio.data;  //io口作輸出時,輸出什麼電平


        if (-1 != sel) //功能選擇不是<default>
        {
            pios[j]->CFGs[k>>3] &= ~(0xf << ((k&7)<<2));
            pios[j]->CFGs[k>>3] |= sel << ((k&7)<<2);

            if (1 == sel) //如果是輸出,還需指定輸出的電平
            {
                pios[j]->DAT &= ~(1<<k);
                pios[j]->DAT |= level << k;
            } 
        }
        if (-1 != pull) //上下拉功能不是<default>
        {
            pios[j]->PULLs[k>>4] &= ~(3 << ((k&15)<<1));
            pios[j]->PULLs[k>>4] |= pull << ((k&15)<<1);
        }   

        if (-1 != drv) //驅動電流等級不是<default>
        {
            pios[j]->DRVs[k>>4] &= ~(3 << ((k&15)<<1));
            pios[j]->DRVs[k>>4] |= drv << ((k&15)<<1);
        }
    }

完整程式碼:
h3gpio.c


#include <linux/init.h>
#include <linux/module.h>
#include <mach/sys_config.h>
#include <mach/pinctrl.h>
#include <asm/io.h>
#include <mach/gpio.h>

#define PIO_BASE  0x01c20800
typedef volatile unsigned int uint32;
typedef struct {
    // gpio口的功能配置暫存器, 每個io口在配置暫存器裡佔用4位(實際用3位)
    uint32 CFGs[4];  //4個功能配置暫存器,每個寄存配置8個io口

    // gpio口的資料暫存器, 每個io口占用一位.
    uint32 DAT;

    // gpio口驅動電流配置暫存器, 每個io口占用2位 
    uint32 DRVs[2]; // 兩個暫存器,每個暫存器配置16個io口

    // gpio口上下拉配置暫存器, 每個io口占用2位
    uint32 PULLs[2]; // 兩個暫存器,每個暫存器配置16個io口

}PIO_t; //表示每組io口都有的配置暫存器


static PIO_t  *pios[7]; // 每個元素分別存放PA, PB, PC, PD, PE, PF, PG組io口的暫存器地址
static u8 *vaddr;

static int __init h3gpio_init(void)
{
    script_item_u *list;
    int n, i, j, k;
    int sel, pull, drv, level;

    vaddr = ioremap(PIO_BASE, SZ_4K); //對映gpio配置暫存器的基地址
    if (NULL == vaddr)
        return -ENOMEM;

    for (i = 0; i < ARRAY_SIZE(pios); i++) //準備好每組io口的配置暫存器的地址
        pios[i] = (PIO_t *)(vaddr+i*0x24);

    // pa的配置暫存器0的地址 ==  &pios[0]->CFG0
    n = script_get_pio_list("gpio_para", &list); //返回值為gpio口的個數
    if (n <= 0)
        return -ENODEV;

    for (i = 0; i < n; i++)
    {
        //在核心裡每組io口都算是32個一組,即使硬體上每組都沒有32個io口那麼多.
        //所以list[i].gpio.gpio / 32 即可算出是第幾組, 
        j = list[i].gpio.gpio >> 5;   // pios[j]就是表示相應組的配置寄存結構體
        k = list[i].gpio.gpio & 31;  //算出在組內是第幾個io口

        sel = list[i].gpio.mul_sel; //script.bin裡io功能選擇
        pull = list[i].gpio.pull;   //上下拉功能
        drv = list[i].gpio.drv_level; //驅動電流等級
        level = list[i].gpio.data;  //io口作輸出時,輸出什麼電平

        if (-1 != sel) //功能選擇不是<default>
        {
            pios[j]->CFGs[k>>3] &= ~(0xf << ((k&7)<<2));
            pios[j]->CFGs[k>>3] |= sel << ((k&7)<<2);

            if (1 == sel) //如果是輸出,還需指定輸出的電平
            {
                pios[j]->DAT &= ~(1<<k);
                pios[j]->DAT |= level << k;
            } 
        }
        if (-1 != pull) //上下拉功能不是<default>
        {
            pios[j]->PULLs[k>>4] &= ~(3 << ((k&15)<<1));
            pios[j]->PULLs[k>>4] |= pull << ((k&15)<<1);
        }   

        if (-1 != drv) //驅動電流等級不是<default>
        {
            pios[j]->DRVs[k>>4] &= ~(3 << ((k&15)<<1));
            pios[j]->DRVs[k>>4] |= drv << ((k&15)<<1);
        }
    }

    return 0;
}

static void __exit h3gpio_exit(void)
{
    iounmap(vaddr);
}


module_init(h3gpio_init);
module_exit(h3gpio_exit);

MODULE_LICENSE("GPL");