這周主要對pinctrl子系統進行分析,該分析的基本上已經分析完成,唯一沒有細說的估計就是gpio與pinctrl之間的關聯了。本章即是pinctrl子系統分析的最後一章,本章我們主要實現一個虛擬的pinctrl device驅動,以便我們能夠使用pinctrl子系統提供的接口,實現pinctrl device的驅動開發(本章實現的驅動代碼可以在ubuntu18.04系統上正常運行)。
本篇文章的目的如下:
本篇文章涉及的知識點:
本章的主要章節如下:
一、 virt soc pin描述
二、 virt pinctrl dev驅動實現
三、virt board pin描述及pinctrl maps註冊
四、device與pinctrl的綁定
五、gpio與pinctrl子系統相關知識點說明
六、功能驗證
既然pinctrl device是對soc pin controller的驅動程序,因此我們需要定義下我們虛擬的soc引腳定義。
如下圖所示,本virt soc 提供32個pin,每一個pin支持4個可選狀態。提供2個32bit寄存器描述該soc引腳復用信息,因為每個pin支持4個可選狀態,因此使用2bits描述該pin的狀態。因為只是一個虛擬的soc pin描述,因此此處僅定義了32個pin信息。
兩個寄存器分別定義pinmux_reg0、pinmux_reg1,其中pin0使用pinmux_reg0的bit0、bit1描述其狀態:00b表示gpio0;01b iic0_sdat。Pin1使用pinmux_reg0的bit2、bit3描述其狀態:00b表示gpio1;10表示uart0_tx;
該soc可支持32個gpio、3個iic、2個uart、1個spi、2個can、1個nandflash的功能復用,而這些功能中存在著引腳復用。
前面的文章中,已經說明了pinctrl dev的驅動開發流程,此處再次說明一下:
主要包含如下幾個步驟:
Virt pinctrl dev數據結構
我們定義了三個數據結構,分別為struct virt_function_desc、struct virt_group_desc、struct virt_pinctrl,其中struct virt_function_desc是對一個function的描述,struct virt_group_desc是對一個group的描述,而struct virt_pinctrl則描述一個soc pin controller。
struct virt_function_desc
該數據結構描述一個function,包含function名稱、該function所包含的group名稱數組、group的個數、引腳復用的配置參數、引腳復用配置參數的掩碼(針對我們的soc,mask為0x03(佔用2位),而mux_val即為引腳復用配置值,如針對iic function,則其mux_val為0x01)
struct virt_group_desc
該數據結構描述一個group,包含group名稱,該group包含的引腳個數、引腳id數組。
struct virt_pinctrl
該數據結構描述一個soc pin controller,包含:
struct pinctrl_desc類型變量定義
如下是該soc pin controller對應的struct pinctrl_desc類型變量的定義,包含描述該soc pin controller的引腳信息的變量(virt_pins)、引腳復用操作接口(virt_pinmux_ops)、group獲取相關的操作接口(virt_pinctrl_ops),此處我們沒有實現引腳配置的操作接口,感興趣的童鞋可自行實現。
Pinctrl device的註冊
調用pinctrl_register/devm_pinctrl_register接口即完成virt soc controller 驅動的註冊。
如下即為該virt pinctrl dev驅動對應的platform driver probe函數的實現,相對來說比較簡單
在上面我們為該platform device註冊了屬性參數,主要用於讀取引腳復用配置寄存器virt_pinctrl_ptr->pin_mux_reg的信息,定義如下:
上面說明soc pin controller 驅動的實現,下面我們說明virt board pin 描述及pinctrl maps的註冊。由於在ubunt1804上測試,其內核是沒有支持設備樹的,因此我們通過定義struct pinctrl_map數組,並調用pinctrl_register_mappings實現baord相關的pinctrl maps註冊。
因為僅是測試驗證,此處我們僅描述spi0的pinctrl_map(若是正常的驅動,則需要描述本board所需要配置的所有pinctrl_map信息),我們的pinctrl_map,其對應的spi設備名稱為virt_spi.0(spi master設備所對應的platform device的名稱,因為spi master並沒有使用設備與驅動綁定操作,因此此處不能是spi master對應device的名稱)、virt_pinctrl_dev是我們上面定義的virt pinctrl dev對應的struct device類型變量的名稱、spi0_group表示我們選擇的virt soc pin controller的組名稱、spi0_func表示我們選擇的virt soc pin controller的function名稱(對應最上面的引腳狀態定義表格的內容)。
調用pinctrl_regiser_mappings後,則將該pinctrl_map註冊到pinctrl_maps鍊表上。
若內核支持設備樹,則需要在各自外設的的節點中增加針對pinctrl function、pinctrl group的描述即可。如下圖時zynq-zc702的i2c0控制器的節點描述,通過pinctrl-names(描述該function的狀態,包含default、idle、sleep等,在之前的文章中已經說明,需要了解的可查看之前的文章)、pinctrl-0(對應的的function定義)即可描述
在上面我們定義了針對spi0的pinctrl map,那什麼時候才會配置spi0的引腳復用呢?我們在前面的《Linux pinctrl子系統分析之六 設備與pinctrl子系統的bind》文章中已經說明,當spi0對應的platform device、platform driver 匹配成功後,probe時進行設備與pinctrl子系統的綁定,並完成引腳的參數配置、復用配置操作。而在此次測試中,我們使用之前在《spi分析專欄》中實現的虛擬spi控制器驅動,完成虛擬spi控制器對應的platform device、platform driver的註冊及綁定,從而完成針對spi0引腳的復用配置操作(虛擬spi控制器驅動實現就不再此處細說了)。
針對gpio的使用,一般也是需要進行引腳復用配置,如我們在此處定義的引腳狀態表中,這32個引腳既可以作為gpio引腳、也可以作為不同控制器的引腳。而針對gpio控制器而言,和普通的設備引腳復用又有所不同,針對普通的設備而言,若作為設備引腳使用,則這些引腳均被設備使用(如iic0 sda、iic0 scl)。但是針對gpio控制器而言,如我們實現虛擬gpio控制器,其包含32個gpio引腳,但是由於引腳復用的關係,該gpio控制器中可能只有部分引腳可以作為gpio,因此針對gpio的引腳復用配置,pinctrl與gpio子系統做了兼容設置。
在調用gpio_request時,則會調用pinctrl 子系統提供的pin_request操作,通過pin_request確定該引腳是否已被其他模塊使用(gpiochip_generic_request接口或者pinctrl_request_gpio、pinctrl_gpio_requeset)。而針對gpio與pinctrl,存在gpio引腳index與pinctrl pin index的轉換工作,因此定義數據結構描述gpio引腳與pinctrl 引腳的轉換;主要數據結構為struct gpio_pin_range、pinctrl_gpio_range,主要也就是gpio控制器的gpio base、num_gpio、pinctrl pin引腳的base index等信息。只需要在gpio_chip註冊時,將struct gpio_pin_range類型的變量,添加到struct pinctrl_dev的成員變量鍊表gpio_ranges上即可。
本篇文章我們的虛擬gpio控制器驅動(該驅動是在之前《gpio專欄》中實現的,此處不再細述),增加實現了該功能。主要是在虛擬gpio控制器驅動對應platform driver probe中增加針對gpio range的註冊代碼,實現如下:
執行完成以上工作後,即完成soc pinctrl dev、pinctrl map的註冊,而我們的pinctrl device對應的platform device路徑為/sys/devices/platform/virt_pinctrl_dev,我們可以在該目錄下查看引腳復用寄存器的設置值。如下:
執行完成insmod後,查看寄存器的值
已經完成引腳復用的配置。
測試驗證下:
我們註冊的gpio的base index為256,我們會發現能夠設置gpio0(即256),但是不能設置gpio6(262),那是引腳6我們已經用作spi0 clk了。下面我們註銷spi 0 controller:
註銷spi0後,就可以使用gpio6了,那是在spi controller註銷時,會調用pin_free釋放該引腳,因此就可以將pin6作為gpio使用了。
以上就是本章的主要內容,我們實現了一個虛擬的pinctrl device驅動,且藉助虛擬的spi控制器驅動、虛擬的gpio控制器驅動、sysfs的屬性文件,完成了完整的模擬工作。希望對學習pinctrl子系統的童鞋有所幫助。(本篇文章涉及的所有代碼,會放到gitee上,稍後會把連結放出來)