Linux GPIO驅動 - 硬件及硬件抽象層

在很多的SOC芯片裏,GPIO硬件接口都是和其它硬件接口共用Pin腳,然後通過特定的寄存器去設置Pin使用時的類型。比如目前手上的項目使用的是Marvell的SOC芯片,該芯片上有50個多功能Pin腳(MPP:Multi-Purpose Pins),每個Pin都可以配置成不同的功能。SOC提供了7個32位的寄存器用來配置MPP Pin的類型,寄存器的每4位對應一個Pin,一個寄存器控制可以8個Pin.當MPP Pin被設置成GPIO Pin後,就可以按照GPIO Pin的方式來使用該Pin腳,比如input/output和high/low的設置。

硬件抽象層負責把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通用層實現。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章