硬件抽象層負責把GPIO控制器註冊到GPIO通用層,通過調用gpiochip_add()把表示GPIO控制器的gpio_chip註冊到gpiolib中。所以在註冊gpio_chip之前,要對該結構體進行初始化。在mv_gpio.c中可以看到,label用來標識GPIO控制器,direction_input/direction_output用來設置GPIO Pin的方向,get/set用來讀寫GPIO Pin的值,而request用來獲取一個空閒的GPIO Pin,base和ngpio分別標識該控制器的起始Pin和Pin的總數量。
static struct gpio_chip mv_gpiochip = {
.label
= "mv_gpio",
.direction_input= mv_gpio_direction_input,
.get = mv_gpio_get_value,
.direction_output= mv_gpio_direction_output,
.set = mv_gpio_set_value,
.request
= mv_gpio_request,
.base = 0,
.ngpio
= MV_GPP_MAX_PINS,
.can_sleep
= 0,
};
mv_gpio_direction_input用來設置GPIO Pin爲input模式,函數的調用路徑爲mv_gpio_direction_input()->_set_direction()->mvGppTypeSet()->gppRegSet()。在_set_directon裏面,會先對要設置的Pin Num做一些處理,因爲總共有50個GPIO Pin可用,所以需要2組32位的寄存器來控制,所以pin>>5就是算出該Pin是屬於第一組,還是屬於第二組。mask的值就是算出該Pin屬於寄存器的第幾個bit。然後通過mvGppTypeSet函數裏面的gppRegSet把對應的值寫到特定的寄存器中。
static inline void __set_direction(unsigned pin, int input)
{
u32 grp = pin >> 5;
u32 mask = (1 << (pin & 0x1F));
if (input)
mvGppTypeSet(grp, mask, MV_GPP_IN & mask);
else
mvGppTypeSet(grp, mask, MV_GPP_OUT & mask);
}
mv_gpio_direction_output用來設置GPIO Pin爲output模式,因爲GPIO Pin在output模式下可以設置該Pin腳的值,所以該函數在設置Pin腳方向的同時可以設置輸出的值。函數調用路徑爲mv_gpio_direction_output->_set_level()->mvGppValueSet()->_set_direction()->mvGppTypeSet()。從函數執行的路徑可以看出,是先設置GPIO的值,然後再設置GPIO的方向。這個順序比較重要,因爲在不知道默認值得情況下,先設置GPIO爲輸出模式,默認的值可能會引起異常,在之前的項目上就碰到過這種問題。
static void __set_level(unsigned pin, int high)
{
u32 grp = pin >> 5;
u32 mask = (1 << (pin & 0x1F));
if (high)
mvGppValueSet (grp, mask, mask);
else
mvGppValueSet (grp, mask, 0);
}
mv_gpio_set_value用來設置GPIO輸出的值,所以該函數直接調用_set_level()把值寫到GPIO的輸出寄存器裏面。而對於mv_gpio_get_value,讀取的值分兩種情況,即輸出模式的值和輸入模式的值。對於輸出模式,直接讀取對應寄存器的值就可以了。而對於輸入模式,由輸入寄存器和一個極性寄存器共同表示該Pin腳的值,當極性寄存器對應的Pin腳位爲1時,該Pin實際的值跟輸入寄存器裏面的值相反,當極性寄存器對應的Pin腳位爲0時,該Pin實際的值與輸入寄存器裏面的值一致。
static int mv_gpio_get_value(struct gpio_chip *chip, unsigned pin)
{
u32 val;
u32 grp = pin >> 5;
u32 mask = (1 << (pin & 0x1F));
if (MV_REG_READ(GPP_DATA_OUT_EN_REG(grp)) & mask)
val = mvGppValueGet(grp, mask) ^ mvGppPolarityGet(grp, mask);
else
val = MV_REG_READ(GPP_DATA_OUT_REG(grp));
return (val >> (pin & 31)) & 1;
}
mv_gpio_request用來申請一個空閒的GPIO Pin,在該項目的平臺上直接返回一個0值,具體的申請功能由gpio通用層實現。