39 解決全志h3 linux核心原始碼裡的關於script.fex的bug
阿新 • • 發佈:2019-02-20
在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");