天天看點

【FFH】BearPi-HM Micro 南向研究——GPIO驅動分析【FFH】BearPi-HM Micro 南向研究——GPIO驅動分析

【FFH】BearPi-HM Micro 南向研究——GPIO驅動分析

參考

  1. https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/driver/driver-platform-gpio-develop.md
  2. https://gitee.com/bearpi/bearpi-hm_micro_small/blob/master/applications/BearPi/BearPi-HM_Micro/docs/device-dev/%E7%BC%96%E5%86%99%E4%B8%80%E4%B8%AA%E7%82%B9%E4%BA%AELED%E7%81%AF%E7%A8%8B%E5%BA%8F.md

概述

BearPi-HM Micro開發闆闆載高性能的工業級處理器STM32MP157晶片,為了使STM32MP1的GPIO驅動适配到HDF架構,廠家會對晶片GPIO的驅動與HDF架構的GPIO進行适配,也就是适配層到核心層的實作,然後核心層又會從核心層抽象出一系列接口到接口層,供開發者使用。通過這樣使用者就可以忽略晶片的差異,隻需關注核心層和接口層,就可以實作驅動程式的開發。

在HDF架構中,GPIO的接口适配模式采用無服務模式,用于不需要在使用者态提供API的裝置類型,或者沒有使用者态和核心區分的OS系統,其關聯方式是DevHandle直接指向裝置對象核心态位址。

【FFH】BearPi-HM Micro 南向研究——GPIO驅動分析【FFH】BearPi-HM Micro 南向研究——GPIO驅動分析

晶片管腳号計算規則

分析之前得先了解BearPi-HM Micro的GPIO引腳編号(gpio_num)規則。後面提到的許多函數都會出現gpio_num,看過led驅動例程,查詢引腳圖可以知道led是PA13,然後在代碼中led的引腳編号是led_gpio_num=13,那麼如果是PB13引腳編号應該是多少?

就去研究了一下這個引腳編号是如何得來的,最後得出BearPi-HM_Micro的引腳号的計算方法。其實就是按晶片原理圖的引腳順序一個一個排下去。

【FFH】BearPi-HM Micro 南向研究——GPIO驅動分析【FFH】BearPi-HM Micro 南向研究——GPIO驅動分析

檢視原理圖,可以看到引腳号由P和從A開始的字母,定義為組,以及一個編号組成,即組内偏移,對應的引腳編号為

GPIOA 起始-終止:0 - 15

GPIOB 起始-終止:16 - 31

GPIOC 起始-終止: 32 - 47

GPIO号 = GPIO組索引 * 每組GPIO管腳數(16) + 組内偏移

例如:PG2 = 6*16+2=98 (7是G的字母序,從0開始)

當然具體得看晶片,例如

【FFH】BearPi-HM Micro 南向研究——GPIO驅動分析【FFH】BearPi-HM Micro 南向研究——GPIO驅動分析

如圖引腳号到PI11就結束了,那麼下一個PZ0的引腳号就是接着PI11下一個,也就是140.

總結:引腳号其實是引腳從第一個數下來的編号,我們隻需要知道它的命名規則,就如上面PX都有16個引腳,就可以計算PB,PC對應的起始編号,得到我們想要的gpio_num。

驅動開發步驟

GPIO子產品适配的三個環節是配置屬性檔案,執行個體化驅動入口,以及執行個體化核心層接口函數。GPIO控制器分組管理所有管腳,相關參數會在屬性檔案中有所展現;驅動入口和接口函數的執行個體化環節是廠商驅動接入HDF的核心環節。

1. 配置屬性檔案:

在device_info.hcs檔案中添加GPIO的deviceNode描述。deviceNode資訊與驅動入口注冊相關,器件屬性值與核心層GpioCntlr成員的預設值或限制範圍有密切關系。 本例隻有一個GPIO控制器,如有多個器件資訊,則需要在device_info檔案增加deviceNode資訊,以及在gpio_config檔案中增加對應的器件屬性。

device_gpio :: device {
                device0 :: deviceNode {
                    policy = 0;// 等于0,不需要釋出服務
                    priority = 10;// 驅動啟動優先級
                    permission = 0644;// 驅動建立裝置節點權限
                    moduleName = "HDF_PLATFORM_GPIO";//【必要】用于指定驅動名稱,需要與期望的驅動Entry中的moduleName一緻;
                    serviceName = "HDF_PLATFORM_GPIO";
                    deviceMatchAttr = "st_stm32mp157_gpio";//【必要】用于配置控制器私有資料,要與 gpio_config.hcs 中 
                                                           // 對應控制器保持一緻,其他控制器資訊也在檔案中
                }
            }
           

【可選】添加gpio_config.hcs器件屬性檔案。也就是私有配置屬性。私有配置屬性如何擷取和調用将在後面Stm32GpioReadDrs函數分析。相關配置資訊可以在晶片手冊尋找。

HDF架構在加載驅動的時候,會将對應的配置資訊擷取并儲存在HdfDeviceObject中的property裡面。

root {
    platform {
        gpio_config {
            controller_0x50002000 {
                match_attr = "st_stm32mp157_gpio";//【必要】必須和device_info.hcs中的deviceMatchAttr值一緻
                groupNum = 11;//【必要】GPIO組索引,需要根據裝置情況填寫
                bitNum = 16;//【必要】每組GPIO管腳數 
                gpioRegBase = 0x50002000;//【必要】實體基位址
                gpioRegStep = 0x1000;//【必要】寄存器偏移步進
                irqRegBase = 0x5000D000;//【必要】開啟中斷
                irqRegStep = 0x400; //【必要】共享中斷
            }
        }
    }
}
           

2. 執行個體化驅動入口

驅動開發首先需要執行個體化驅動入口,驅動入口必須為HdfDriverEntry(在 hdf_device_desc.h 中定義)類型的全局變量,且moduleName要和device_info.hcs中保持一緻。HDF架構會将所有加載的驅動的HdfDriverEntry對象首位址彙總,形成一個類似數組的段位址空間,友善上層調用。 一般在加載驅動時HDF會先調用Bind函數,再調用Init函數加載該驅動。當Init調用異常時,HDF架構會調用Release釋放驅動資源并退出。

步驟:

  1. 執行個體化HdfDriverEntry結構體成員。
  2. 調用HDF_INIT将HdfDriverEntry執行個體化對象注冊到HDF架構中。
/* HdfDriverEntry definition */
struct HdfDriverEntry g_GpioDriverEntry = {
    .moduleVersion = 1,
    .moduleName = "HDF_PLATFORM_GPIO",
    .Bind = GpioDriverBind,
    .Init = GpioDriverInit,
    .Release = GpioDriverRelease,
};

/* Init HdfDriverEntry */
HDF_INIT(g_GpioDriverEntry);
           

執行個體化Bind,Init,Release函數

因為gpio采用無服務模式,是以并不需要在Bind函數将服務接口綁定到HDF架構,Init函數主要用于初始化自定義結構體對象,初始化GpioCntlr成員,調用核心層GpioCntlrAdd函數。Release函數用于釋放記憶體和删除控制器,該函數需要在驅動入口結構體中指派給Release 接口,當HDF架構調用Init函數初始化驅動失敗時,可以調用Release釋放驅動資源。所有強制轉換擷取相應對象的操作前提是在Init函數中具備對應指派的操作。

下列為HDF_STATUS相關狀态的說明。

【FFH】BearPi-HM Micro 南向研究——GPIO驅動分析【FFH】BearPi-HM Micro 南向研究——GPIO驅動分析
/* HdfDriverEntry hook function implementations */
static int32_t GpioDriverBind(struct HdfDeviceObject *device)
{   
    (void)device;
    return HDF_SUCCESS;
    
}

static int32_t GpioDriverInit(struct HdfDeviceObject *device)
{
   
    int32_t ret;
    struct Stm32GpioCntlr *stm32gpio = &g_Stm32GpioCntlr;

    dprintf("%s: Enter", __func__);
    if (device == NULL || device->property == NULL) {
        HDF_LOGE("%s: device or property NULL!", __func__);
        return HDF_ERR_INVALID_OBJECT;
    }
    //擷取私有屬性資料
    ret = Stm32GpioReadDrs(stm32gpio, device->property);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: get gpio device resource fail:%d", __func__, ret);
        return ret;
    }
    //引腳無效
    if (stm32gpio->groupNum > GROUP_MAX || stm32gpio->groupNum <= 0 || stm32gpio->bitNum > BIT_MAX ||
        stm32gpio->bitNum <= 0) {
        HDF_LOGE("%s: invalid groupNum:%u or bitNum:%u", __func__, stm32gpio->groupNum,
                 stm32gpio->bitNum);
        return HDF_ERR_INVALID_PARAM;
    }
    //寄存器位址映射
    stm32gpio->regBase = OsalIoRemap(stm32gpio->gpioPhyBase, stm32gpio->groupNum * stm32gpio->gpioRegStep);
    if (stm32gpio->regBase == NULL) {
        HDF_LOGE("%s: err remap phy:0x%x", __func__, stm32gpio->gpioPhyBase);
        return HDF_ERR_IO;
    }
    /* OsalIoRemap: remap registers */
    stm32gpio->exitBase = OsalIoRemap(stm32gpio->irqPhyBase, stm32gpio->iqrRegStep);
    if (stm32gpio->exitBase == NULL) {
        dprintf("%s: OsalIoRemap fail!", __func__);
        return -1;
    }
    //為控制器配置設定記憶體
    ret = InitGpioCntlrMem(stm32gpio);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: err init cntlr mem:%d", __func__, ret);
        OsalIoUnmap((void *)stm32gpio->regBase);
        stm32gpio->regBase = NULL;
        return ret;
    }
    
    stm32gpio->cntlr.count = stm32gpio->groupNum * stm32gpio->bitNum;//計算組起始編号
    stm32gpio->cntlr.priv = (void *)device->property;//私有屬性
    stm32gpio->cntlr.device = device;//綁定裝置
    stm32gpio->cntlr.ops = &g_GpioMethod;//綁定接口函數
    ret = GpioCntlrAdd(&stm32gpio->cntlr);//添加gpio控制器到核心層
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: err add controller: %d", __func__, ret);
        return ret;
    }
    HDF_LOGE("%s: dev service:%s init success!", __func__, HdfDeviceGetServiceName(device));

    return ret;
}

static void GpioDriverRelease(struct HdfDeviceObject *device)
{
    struct GpioCntlr *gpioCntlr = NULL;
    struct Stm32GpioCntlr *stm32gpioGpioCntlr = NULL;

    HDF_LOGD("%s: Enter", __func__);
    if (device == NULL) {
        HDF_LOGE("%s: device is null!", __func__);
        return;
    }

    gpioCntlr = GpioCntlrFromDevice(device);//擷取gpio控制器裝置句柄
    if (gpioCntlr == NULL) {
        HDF_LOGE("%s: no service bound!", __func__);
        return;
    }
    GpioCntlrRemove(gpioCntlr);//移除gpio控制器

    stm32gpioGpioCntlr = (struct Stm32GpioCntlr *)gpioCntlr;
    ReleaseGpioCntlrMem(stm32gpioGpioCntlr);//釋放記憶體
    OsalIoUnmap((void *)stm32gpioGpioCntlr->regBase);
    stm32gpioGpioCntlr->regBase = NULL;
}
           

3.執行個體化GPIO控制器對象:

初始化GpioCntlr成員

GpioCntlr結構體定義如下

struct GpioCntlr {
    struct IDeviceIoService service;//gpio無服務無需執行個體化
    struct HdfDeviceObject *device; //gpio裝置(hcs)
    struct GpioMethod *ops;//gpio操作方式,由stm32mp1_gpio.c實作
    struct DListHead list;
    OsalSpinlock spin;
    uint16_t start;
    uint16_t count;
    struct GpioInfo *ginfos;
    void *priv;//私有屬性指針
};
           

在Init函數已經對GpioCntlr進行了初始化,如下圖,主要步驟就是先建立一個gpio控制器并配置設定記憶體,然後對控制器的成員進行指派,最後調用核心層GpioCntlrAdd添加控制器。

【FFH】BearPi-HM Micro 南向研究——GPIO驅動分析【FFH】BearPi-HM Micro 南向研究——GPIO驅動分析

對于GpioCntlr的執行個體化,主要提幾點:

  1. cntlr.device初始化用于綁定GPIO控制器裝置對象,為HdfDeviceObject類型。
  2. cntlr.priv,也就是我們的私有屬性擷取,上面在配置私有屬性時已經提到私有屬性儲存在HdfDeviceObject中的property裡面,而這一過程在廠家寫的Stm32GpioReadDrs中實作。
【FFH】BearPi-HM Micro 南向研究——GPIO驅動分析【FFH】BearPi-HM Micro 南向研究——GPIO驅動分析
/* 擷取hcs配置資訊到 stm32gpio */
static int32_t Stm32GpioReadDrs(struct Stm32GpioCntlr *stm32gpio, const struct DeviceResourceNode *node)
{
    int32_t ret;
    struct DeviceResourceIface *drsOps = NULL;

    drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);//擷取有關裝置資源配置樹的資訊
    if (drsOps == NULL || drsOps->GetUint32 == NULL) {
        HDF_LOGE("%s: invalid drs ops fail!", __func__);
        return HDF_FAILURE;
    }

    ret = drsOps->GetUint32(node, "gpioRegBase", &stm32gpio->gpioPhyBase, 0);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: read regBase fail!", __func__);
        return ret;
    }

    ret = drsOps->GetUint32(node, "gpioRegStep", &stm32gpio->gpioRegStep, 0);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: read gpioRegStep fail!", __func__);
        return ret;
    }

    ret = drsOps->GetUint16(node, "groupNum", &stm32gpio->groupNum, 0);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: read groupNum fail!", __func__);
        return ret;
    }

    ret = drsOps->GetUint16(node, "bitNum", &stm32gpio->bitNum, 0);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: read bitNum fail!", __func__);
        return ret;
    }

    ret = drsOps->GetUint32(node, "irqRegBase", &stm32gpio->irqPhyBase, 0);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: read regBase fail!", __func__);
        return ret;
    }

    ret = drsOps->GetUint32(node, "irqRegStep", &stm32gpio->iqrRegStep, 0);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%s: read gpioRegStep fail!", __func__);
        return ret;
    }
    return HDF_SUCCESS;
}
           

結構體DeviceResourceIface提供用于擷取有關裝置資源配置樹的資訊的函數。

通過DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE)擷取結構體句柄,然後就可以通過裡面的函數來擷取私有配置資訊。

例如:ret = drsOps->GetUint32(node, "gpioRegBase", &stm32gpio->gpioPhyBase, 0);就是将私有配置中的"gpioRegBase"以uint32的資料類型指派給stm32gpio->gpioPhyBase,node是訓示指向配置樹節點的指針,通過node就可以定位到裝置的屬性樹的root節點,進而讀取到資料。然後最後一個參數是訓示在操作失敗時要填充到值所指向的記憶體。

執行個體化GpioCntlr成員GpioMethod。

上面的初始化還有一個操作stm32gpio->cntlr.ops = &g_GpioMethod;這個就是實作執行個體化GpioCntlr成員GpioMethod。

GpioMethod結構體在drivers/framework/support/platform/include/gpio_core.h中定義,而我們需要執行個體化這些接口函數。gpio_core.c 就是對硬體gpio控制器的抽象,通過gpio控制器可以配置具體的gpio電平、輸入輸出模式、中斷等。

struct GpioMethod {
    /** request exclusive access to an GPIO pin, optional */
    int32_t (*request)(struct GpioCntlr *cntlr, uint16_t local);
    /** release exclusive access to an GPIO pin, optional */
    int32_t (*release)(struct GpioCntlr *cntlr, uint16_t local);
    /** write the level value into a GPIO pin */
    int32_t (*write)(struct GpioCntlr *cntlr, uint16_t local, uint16_t val);
    /** read the level value of a GPIO pin */
    int32_t (*read)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *val);
    /** set the direction for a GPIO pin */
    int32_t (*setDir)(struct GpioCntlr *cntlr, uint16_t local, uint16_t dir);
    /** get the direction of a GPIO pin */
    int32_t (*getDir)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *dir);
    /** get the irq number of a GPIO pin, optional */
    int32_t (*toIrq)(struct GpioCntlr *cntlr, uint16_t local, uint16_t *irq);
    /** set the ISR function for a GPIO pin */
    int32_t (*setIrq)(struct GpioCntlr *cntlr, uint16_t local, uint16_t mode, GpioIrqFunc func, void *arg);
    /** unset the ISR function for a GPIO pin */
    int32_t (*unsetIrq)(struct GpioCntlr *cntlr, uint16_t local);
    /** enable a GPIO pin interrupt */
    int32_t (*enableIrq)(struct GpioCntlr *cntlr, uint16_t local);
    /** disable a GPIO pin interrupt */
    int32_t (*disableIrq)(struct GpioCntlr *cntlr, uint16_t local);
};
           
【FFH】BearPi-HM Micro 南向研究——GPIO驅動分析【FFH】BearPi-HM Micro 南向研究——GPIO驅動分析

回到stm32mp1_gpio.c檔案,廠家寫的GpioMethod結構體定義g_GpioMethod如下,對gpio_core中的函數進行執行個體化。

/* GpioMethod definition */
struct GpioMethod g_GpioMethod = {
    .request = NULL,
    .release = NULL,
    .write = Stm32Mp157GpioWrite,
    .read = Stm32Mp157GpioRead,
    .setDir = Stm32Mp157GpioSetDir,
    .getDir = Stm32Mp157GpioGetDir,
    .toIrq = NULL,
    .setIrq = Stm32Mp157GpioSetIrq,
    .unsetIrq = Stm32Mp157GpioUnsetIrq,
    .enableIrq = Stm32Mp157GpioEnableIrq,
    .disableIrq = Stm32Mp157GpioDisableIrq,
};
           

廠家通過執行個體化上述函數,就可以實作core核心層與适配層的對接,通過調用core裡面的函數就可以間接調用到廠家實作的對不同晶片的适配驅動函數。以上函數都可以在stm32mp1_gpio.c檔案找到。

例如:

int32_t GpioCntlrRead(struct GpioCntlr *cntlr, uint16_t local, uint16_t *val)
{
    if (cntlr == NULL) {
        return HDF_ERR_INVALID_OBJECT;
    }
    if (cntlr->ops == NULL || cntlr->ops->read == NULL) {
        return HDF_ERR_NOT_SUPPORT;
    }
    if (val == NULL) {
        return HDF_ERR_INVALID_PARAM;
    }
    return cntlr->ops->read(cntlr, local, val);//調用控制器的方法
}
           

可以發現core核心層的函數其實就是調用控制器的GpioMethod中的函數,也就是最終會調用到廠家寫的适配驅動函數。

通過适配層綁定後我們就可以調用核心層的函數來操控我們的gpio,而gpio_core的函數在同路徑的gpio.if中又被封裝了一層,簡化了參數,不需要傳入GpioCntlr這個控制器,直接傳入gpio編号就可以進行控制。故如果我們想要實作gpio的一些功能,可以引用gpio_if.h這個頭檔案,在把路徑依賴加上,就可以實作對gpio的操作。

例如:gpio.if.c中的GpioRead函數,對gpio_core.c中的GpioCntlrRead進行封裝,隻需要傳入gpio編号及val的指針位址,就可以實作對gpio的讀取操作。

int32_t GpioRead(uint16_t gpio, uint16_t *val)
{
    return GpioCntlrRead(GpioGetCntlr(gpio), GpioToLocal(gpio), val);
}
           

在LED的例程中也可以發現編寫的驅動代碼對led的gpio管腳操作也是引用了gpio_if.h頭檔案,并且調用其中的函數來操作gpio,想了解gpio的使用可以再仔細檢視代碼看看它是怎麼操作的,gpio的操作流程如下:

【FFH】BearPi-HM Micro 南向研究——GPIO驅動分析【FFH】BearPi-HM Micro 南向研究——GPIO驅動分析

stm32mp1_gpio适配層驅動代碼分析

下面是對廠家寫的适配層的代碼的分析。

先看看stm32mp1_gpio.h檔案廠家自定義的結構體類型

#define GROUP_MAX 13 //最大組數
#define BIT_MAX   16 //一組最大位号   

//描述一個gpio端口 如:GPIOA,GPIOB
struct GpioGroup {
    volatile unsigned char *regBase;    //寄存器映射位址(寄存器位址映射) 
    EXTI_TypeDef *exitBase; //外部中斷基位址
    unsigned int index;     //端口中斷處理函數
    OsalIRQHandle irqFunc;  //中段句柄?
    OsalSpinlock lock;  //自旋鎖
};

//描述一個GPIO控制器,控制所有的GPIO端口
struct Stm32GpioCntlr {
    struct GpioCntlr cntlr;     //gpio核心層控制器
    volatile unsigned char *regBase;    //寄存器映射後得到的位址   
    EXTI_TypeDef *exitBase; //同上
    uint32_t gpioPhyBase;   //gpio寄存器實體基位址
    uint32_t gpioRegStep;    //gpio寄存器偏移步進
    uint32_t irqPhyBase;    //外部中斷寄存器實體基位址
    uint32_t iqrRegStep;    //外部中斷寄存器偏移步進
    uint16_t groupNum;  //gpio組數量
    uint16_t bitNum;    //每組gpio的管腳數量
    struct GpioGroup *groups;    //gpio端口(一個數組)
};

static struct Stm32GpioCntlr g_Stm32GpioCntlr = {
    .groups = NULL,
    .groupNum = GROUP_MAX,
    .bitNum = BIT_MAX,
};
           

stm32mp1_gpio.c驅動函數分析

前面是對組号引腳号處理函數,上面已經分析了gpio_num群組号引腳号的關系,下面這些函數是他們之間的轉換關系。

static inline struct Stm32GpioCntlr *ToStm32GpioCntlr(struct GpioCntlr *cntlr)
{
    return (struct Stm32GpioCntlr *)cntlr;
}
//擷取組号
static inline uint16_t Stm32ToGroupNum(uint16_t gpio)
{
    return (uint16_t)(gpio / g_Stm32GpioCntlr.bitNum);//根據GPIO編号計算組号,即除于每組最大引腳号
}
//擷取位号
static inline uint16_t Stm32ToBitNum(uint16_t gpio)
{
    return (uint16_t)(gpio % g_Stm32GpioCntlr.bitNum);//根據GPIO編号計算位号,餘數即為引腳号
}
//根據組号和位号計算GPIO編号
static inline uint16_t Stm32ToGpioNum(uint16_t group, uint16_t bit)
{
    return (uint16_t)(group * g_Stm32GpioCntlr.bitNum + bit);
}

//根據GPIO編号擷取對應的stm32組号,存在group裡
static int32_t Stm32GetGroupByGpioNum(struct GpioCntlr *cntlr, uint16_t gpio, struct GpioGroup **group)
{
    struct Stm32GpioCntlr *stm32gpio = NULL;
    uint16_t groupIndex = Stm32ToGroupNum(gpio);

    if (cntlr == NULL || cntlr->priv == NULL) {
        HDF_LOGE("%s: cntlr or priv is NULL", __func__);
        return HDF_ERR_INVALID_OBJECT;
    }
    stm32gpio = ToStm32GpioCntlr(cntlr); //GPIOCntlr轉成Stm32GpioCntlr
    if (groupIndex >= stm32gpio->groupNum) {
        HDF_LOGE("%s: err group index:%u", __func__, groupIndex);
        return HDF_ERR_INVALID_PARAM;
    }
    *group = &stm32gpio->groups[groupIndex];//group的映射(可能)
    return HDF_SUCCESS;
}
           

廠家實作的驅動函數功能說明

/*
功能:設定GPIO口方向
參數:*cntlr :GPIO控制器結構體
     gpio:gpio口編号
     dir:方向(IN/OUT)
*/
static int32_t Stm32Mp157GpioSetDir(struct GpioCntlr *cntlr, uint16_t gpio, uint16_t dir);

/* 
功能:擷取GPIO口方向
參數:*cntlr :GPIO控制器結構體
     gpio:gpio口編号
     *dir:存取擷取到的gpio輸入輸出方向
*/ 
static int32_t Stm32Mp157GpioGetDir(struct GpioCntlr *cntlr, uint16_t gpio, uint16_t *dir);

/* 
功能:設定GPIO電平 
參數:*cntlr :GPIO控制器結構體
     gpio:gpio口編号
     val: 電平高低(GPIO_VAL_LOW/GPIO_VAL_HIGH )
*/
static int32_t Stm32Mp157GpioWrite(struct GpioCntlr *cntlr, uint16_t gpio, uint16_t val);

/* 
功能:擷取GPIO電平 
參數:*cntlr :GPIO控制器結構體
     gpio:gpio口編号
     *val: 存取擷取到的電平值
*/  
static int32_t Stm32Mp157GpioRead(struct GpioCntlr *cntlr, uint16_t gpio, uint16_t *val);

/* 
功能:所有gpio的中斷服務函數
參數:irq:gpio中斷号
     data:GpioGroup 産生中斷的gpio所在的分組(端口)
*/
static uint32_t IrqHandleNoShare(uint32_t irq, void *data);

/* 
功能:根據引腳号擷取中斷号
參數:pinNum:E53引腳編号 
*/
static uint32_t  GetGpioIrqNum(uint16_t pinNum);

/* 
功能:注冊gpio管腳的中斷函數 
參數:pinNum:E53引腳編号 
     group:GPIO組
*/
static int32_t GpioRegisterGroupIrqUnsafe(uint16_t pinNum, struct GpioGroup *group);


/* 
功能:初始化gpio中斷寄存器、中斷服務函數
參數:*cntlr :GPIO控制器結構體
     gpio:gpio引腳編号
     func:回調函數參數:*cntlr :GPIO控制器結構體
     gpio:gpio引腳編号
     arg:可選參數
*/
static int32_t Stm32Mp157GpioSetIrq(struct GpioCntlr *cntlr, uint16_t gpio, uint16_t mode, GpioIrqFunc func, void *arg);


/* 
功能:取消設定gpio中斷寄存器、中斷服務函數 
參數:*cntlr :GPIO控制器結構體
     gpio:gpio引腳編号
*/
static int32_t Stm32Mp157GpioUnsetIrq(struct GpioCntlr *cntlr, uint16_t gpio);

/* 
功能:使能GPIO管腳中斷
參數:*cntlr :GPIO控制器結構體
     gpio:gpio引腳編号 
*/
static int32_t Stm32Mp157GpioEnableIrq(struct GpioCntlr *cntlr, uint16_t gpio);

/* 
功能:禁止GPIO管腳中斷 
參數:*cntlr :GPIO控制器結構體
     gpio:gpio引腳編号
*/
static int32_t Stm32Mp157GpioDisableIrq(struct GpioCntlr *cntlr, uint16_t gpio);


           

總結

GPIO的操作函數都是由gpio_if提供,而gpio_if隻是對gpio_core的一層封裝,屏蔽了gpio控制器。gpio_core 中的函數就是調用控制器的各種方法,而廠家需要實作适配層的驅動編寫stm32mp1_gpio,以此來執行個體化核心層驅動。

【FFH】BearPi-HM Micro 南向研究——GPIO驅動分析【FFH】BearPi-HM Micro 南向研究——GPIO驅動分析

通過分析GPIO驅動對HDF架構有了更深入的了解,也了解了不同晶片是如何适配到HDF架構的,對驅動的開發流程也有了很大幫助。後續會嘗試E53序列槽驅動的研究,廠家給的驅動函數并沒有把E53的序列槽驅動加上,研究了挺久也沒驅動起來,希望有成功使用BearPi-HM MicroE53序列槽的能夠交流交流。

以上有些内容屬于個人了解,可能有不對的地方,歡迎交流探讨。

繼續閱讀