USB匯流排-Linux核心USB3.0控制器初始化程式碼分析(三)
1.概述
RK33999使用synopsys dwc3的USB3.0控制器IP。早期的初始化需要在兩個模組中進行,一個在rockchip官方提供的驅動中初始化,位於drivers/usb/dwc3/dwc3-rockchip.c檔案中,主要初始化和CPU緊密相關的內容,如時鐘、復位、電源、extcon(用於USB模式切換),另一個在synopsys提供的驅動中初始化,位於drivers/usb/dwc3/core.c檔案中,這部分和USB3.0控制器密切相關,如USB3.0控制器內部暫存器地址、USB3.0的PHY、中斷等。只有兩個模組都初始化完畢,USB3.0控制器才能正常工作。本節只分析USB驅動早期初始化部分。
2.裝置樹
下面是USB3.0控制器的裝置樹節點。最外層的相容屬性為"rockchip,rk3399-dwc3",為rockchip定義的屬性,有時鐘、電源、復位、extcon等。extcon(external connectors)是USB用於狀態通知的驅動,主要用於USB模式切換,當PHY收到中斷及處理完USB狀態後,通過extcon驅動廣播到監聽該事件的所有驅動。使用devm_extcon_register_notifier來註冊監聽usb狀態變化的回撥函式。內層的相容屬性為"snps,dwc3",為synopsys公司定義的屬性,主要和USB控制器及PHY相關。dwc3裝置節點具體屬性資訊可參考Documentation/devicetree/bindings/usb/dwc3.txt文件。
由於usbdrd3_0裝置樹節點是根節點的子節點,且有compatible屬性,因此核心會自動將其轉換為platform_device,然後和對應的驅動進行匹配,而其子節點usbdrd_dwc3_0核心則不會處理,由其父節點usbdrd3_0的驅動處理。
usbdrd3_0: usb0 { compatible = "rockchip,rk3399-dwc3"; clocks = <&cru SCLK_USB3OTG0_REF>, <&cru SCLK_USB3OTG0_SUSPEND>, <&cru ACLK_USB3OTG0>, <&cru ACLK_USB3_GRF>; clock-names = "ref_clk", "suspend_clk", "bus_clk", "grf_clk"; power-domains = <&power RK3399_PD_USB3>; // 電源 resets = <&cru SRST_A_USB3_OTG0>; // 用於復位 reset-names = "usb3-otg"; #address-cells = <2>; #size-cells = <2>; ranges; status = "disabled"; usbdrd_dwc3_0: dwc3@fe800000 { compatible = "snps,dwc3"; reg = <0x0 0xfe800000 0x0 0x100000>; interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH 0>; // 中斷屬性 dr_mode = "otg"; // 模式,預設模式為OTG phys = <&u2phy0_otg>, <&tcphy0_usb3>; phy-names = "usb2-phy", "usb3-phy"; // USB PHY phy_type = "utmi_wide"; /* when set clears the enblslpm in GUSB2PHYCFG, disabling the suspend signal to the PHY */ snps,dis_enblslpm_quirk; /* when set, clear the u2_freeclk_exists in GUSB2PHYCFG, specify that USB2 PHY doesn't provide a free-running PHY clock */ snps,dis-u2-freeclk-exists-quirk; /* when set core will disable USB2 suspend phy */ snps,dis_u2_susphy_quirk; /* when set core will change PHY power from P0 to P1/P2/P3 without delay */ snps,dis-del-phy-power-chg-quirk; /* when set, disable u2mac linestate check during HS transmit */ snps,tx-ipgap-linecheck-dis-quirk; /* when set, need an extraordinary delay to wait for xHC enter the Halted state (i.e. HCH in the USBSTS register is '1') */ snps,xhci-slow-suspend-quirk; /* when set, the xHC use the Evaluate Next TRB(ENT) flag to force the xHC to pre-fetch the next TRB of a TD */ snps,xhci-trb-ent-quirk; /* when set, need warm reset on resume */ snps,usb3-warm-reset-on-resume-quirk; status = "disabled"; }; }; &usbdrd3_0 { status = "okay"; extcon = <&fusb0>; }; &i2c4 { status = "okay"; i2c-scl-rising-time-ns = <160>; i2c-scl-falling-time-ns = <30>; clock-frequency = <400000>; fusb0: fusb30x@22 { compatible = "fairchild,fusb302"; reg = <0x22>; pinctrl-names = "default"; pinctrl-0 = <&fusb0_int>; int-n-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>; vbus-5v-gpios = <&gpio4 26 GPIO_ACTIVE_HIGH>; status = "okay"; }; ...... };
3.初始化驅動分析
初始化驅動分為兩部分,一部分是和CPU相關的初始化,如時鐘、電源等,由rockchip提供的驅動完成,另一部分是USB控制器相關的初始化,如USB控制器暫存器地址、中斷、PHY等,由synopsys官方的驅動完成。這兩部分分開來分析,首先分析rockchip提供的驅動,最後分析synopsys官方的驅動。
3.1.rockchip USB初始化驅動分析
rockchip提供的USB初始化驅動是一個platform_driver,裝置樹匹配的屬性為"rockchip,rk3399-dwc3",入口函式為dwc3_rockchip_probe。下面分析一下入口函式的執行流程。
[drivers/usb/dwc3/dwc3-rockchip.c]
static const struct of_device_id rockchip_dwc3_match[] = {
{ .compatible = "rockchip,rk3399-dwc3" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, rockchip_dwc3_match);
static struct platform_driver dwc3_rockchip_driver = {
.probe = dwc3_rockchip_probe,
.remove = dwc3_rockchip_remove,
.driver = {
.name = "rockchip-dwc3",
.of_match_table = rockchip_dwc3_match,
.pm = DEV_PM_OPS,
},
};
module_platform_driver(dwc3_rockchip_driver);
dwc3_rockchip_probe函式主要的工作如下:
(1)獲取時鐘和使能時鐘
(2)將子節點usbdrd_dwc3_0轉換為platform_device,並儲存子節點對應裝置驅動程式的私有資料結構dwc3結構體指標,若獲取不到dwc3結構體指標,則返回EPROBE_DEFER,核心稍後會再次執行dwc3_rockchip_probe函式。
(3)處理extcon屬性,設定通知回撥函式,裝置的回撥函式為dwc3_rockchip_device_notifier,主機的回撥函式為dwc3_rockchip_host_notifier,回撥函式通過otg_work工作佇列執行
(4)非同步執行dwc3_rockchip_async_probe函式,主要註冊通知回撥、設定電源等工作
dwc3_rockchip_probe
devm_kzalloc // 分配struct dwc3_rockchip結構體記憶體
of_clk_get_parent_count // 獲取裝置樹引用時鐘源的數量
rockchip->num_clocks = count // 儲存時鐘源的數量
devm_kcalloc // 分配num_clocks個struct clk*指標
platform_set_drvdata // 儲存dwc3_rockchip指標
clk = of_clk_get(np, i) // 獲取時鐘
clk_prepare_enable // 使能時鐘
rockchip->clks[i] = clk // 儲存時鐘結構體指標
pm_runtime_set_active // 電源管理相關
pm_runtime_enable
pm_runtime_get_sync
devm_reset_control_get(dev, "usb3-otg") // 獲取用於復位的reset_control
of_get_child_by_name(np, "dwc3") // 獲取子節點dwc3的device_node
/* 遍歷指定節點的所有子節點,將複合要求的轉換為platform_device,即將usbdrd3_0的子節點
usbdrd_dwc3_0轉換為platform_device */
of_platform_populate
// 初始化工作佇列,工作佇列的入口函式為dwc3_rockchip_otg_extcon_evt_work,用於USB模式切換
INIT_WORK(&rockchip->otg_work, dwc3_rockchip_otg_extcon_evt_work)
of_find_device_by_node // 獲取子節點的platform_device指標,即usbdrd_dwc3_0節點
/* 獲取子節點驅動的私有資料指標,即dwc3結構體指標,若獲取不成功,則返回EPROBE_DEFER,
核心會後續再次執行dwc3_rockchip_probe */
rockchip->dwc = platform_get_drvdata
// 若是Host和OTG模式,則獲取主機控制器的struct usb_hcd指標
rockchip->hcd = dev_get_drvdata(&rockchip->dwc->xhci->dev)
// 處理extcon屬性
dwc3_rockchip_get_extcon_dev
// 判斷usbdrd3_0節點中是否有extcon屬性
device_property_read_bool(dev, "extcon")
extcon_get_edev_by_phandle // 通過引用的裝置樹節點獲取struct extcon_dev
// 設定通知的回撥函式
rockchip->device_nb.notifier_call = dwc3_rockchip_device_notifier;
rockchip->host_nb.notifier_call = dwc3_rockchip_host_notifier;
rockchip->edev = edev;
// 非同步執行dwc3_rockchip_async_probe函式,實質上是通過system_unbound_wq工作佇列執行
async_schedule(dwc3_rockchip_async_probe, rockchip)
extcon的回撥函式如下,裝置和主機的回撥函式除了引數不一樣,其他都一樣,都是通過schedule_work排程otg_work工作佇列處理工作任務,工作任務函式為dwc3_rockchip_otg_extcon_evt_work,主要和USB模式切換有關,後面分析extcon驅動時詳細分析。
dwc3_rockchip_device_notifier
// 獲取dwc3_rockchip結構體
rockchip = container_of(nb, struct dwc3_rockchip, device_nb)
// usb不處於suspended狀態時,排程otg_work工作佇列
schedule_work(&rockchip->otg_work)
dwc3_rockchip_host_notifier
// 獲取dwc3_rockchip結構體
rockchip = container_of(nb, struct dwc3_rockchip, host_nb)
// usb不處於suspended狀態時,排程otg_work工作佇列
schedule_work(&rockchip->otg_work)
dwc3_rockchip_async_probe是非同步執行的函式,實質上是通過system_unbound_wq工作佇列執行。主要工作是註冊extcon的通知回撥函式、給USB PHY上電及建立除錯屬性檔案組。
dwc3_rockchip_async_probe
// 獲取裝置是否有"needs-reset-on-resume"屬性,有返回true
device_property_read_bool(dev, "needs-reset-on-resume")
devm_extcon_register_notifier(..., &rockchip->device_nb) // 註冊裝置的extcon通知回撥函式
devm_extcon_register_notifier(..., &rockchip->host_nb) // 註冊主機的extcon通知回撥函式
// 若存在extcon或dr_mode為USB_DR_MODE_OTG,則進行電源相關設定
pm_runtime_set_autosuspend_delay // 設定autosuspend的延遲時間為500毫秒
pm_runtime_allow // 開啟動態電源管理
pm_runtime_suspend // 進入suspend狀態
// 排程otg_work工作佇列,執行函式為dwc3_rockchip_otg_extcon_evt_work
schedule_work(&rockchip->otg_work)
// 若extcon不存在且dr_mode不為USB_DR_MODE_OTG,說明USB控制器不進行模式切換
// 只能是主機模式或裝置模式,
// 設定connected為true,防止USB從PM suspend狀態轉換為resume時復位DWC3控制器,
// 主機模式復位時會導致裝置重新列舉
rockchip->connected = true
// 若裝置沒有設定"needs-reset-on-resume"屬性且CPU為"rockchip,rk3399"且
// dr_mode為USB_DR_MODE_HOST
// RK3399的USB3.0的PHY為Type-C PHY,除了在dwc3_core_init()中上電,還需要在
// 這裡上電,以防止USB裝置連線到DWC3主機控制器後狀態切換為suspend時關閉PHY的電源
phy_power_on(dwc->usb2_generic_phy)
phy_power_on(dwc->usb3_generic_phy)
rockchip->is_phy_on = true
// 建立除錯屬性檔案組
sysfs_create_group(&dev->kobj, &dwc3_rockchip_attr_group)
3.2.synopsys USB初始化驅動分析
3.2.1.資料結構體分析
struct dwc3是USB3.0 OTG控制器的核心資料結構,所有工作都圍繞此資料結構展開。該資料結構的意義如下面的程式碼所示,省略了一些不太重要的內容。
[drivers/usb/dwc3/core.h]
struct dwc3 {
// 端點0的USB控制請求, 即ep0out接收到的setup請求,如Get Descriptor, Set Interface等命令
struct usb_ctrlrequest *ctrl_req;
struct dwc3_trb *ep0_trb; // 端點0控制傳輸的trb
// 使用kzalloc函式分配的, 不是dma_alloc_coherent函式
// Get Status和Set Sel等標準請求需要用到該預分配的buffer作為usb傳輸的負載
void *ep0_bounce; // 端點0的bounce buffer
void *zlp_buf; // request->zero設定時使用
// 主要用於休眠喚醒(Hibernation)功能, 即休眠時儲存控制器的暫存器資訊到記憶體中
void *scratchbuf; // RK3399 dwc3控制器沒有使用
u8 *setup_buf; // 處理標準USB請求時使用
dma_addr_t ctrl_req_addr; // ctrl_req的dma地址
dma_addr_t ep0_trb_addr; // ep0_trb的dma地址
dma_addr_t ep0_bounce_addr; // ep0_bounce的dma的地址
dma_addr_t scratch_addr; // scratchbuf的dma地址
struct dwc3_request ep0_usb_req; // dummy req used while handling STD USB requests
struct device *dev;
struct platform_device *xhci; // USB主機控制器資料結構
struct resource xhci_resources[DWC3_XHCI_RESOURCES_NUM]; // USB主機控制器資源
// 事件buffer,控制器會將傳輸的事件資訊儲存到該緩衝區中,由軟體統一處理
struct dwc3_event_buffer *ev_buf;
struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM]; // 端點資料結構的指標陣列,長度為32
struct usb_gadget gadget; // USB控制器處於裝置模式時使用的資料結構
struct usb_gadget_driver *gadget_driver; // 裝置模式時使用的驅動,由具體匹配的裝置決定
void __iomem *regs; // 暫存器基地址
size_t regs_size; // 暫存器地址長度
enum usb_dr_mode dr_mode; // USB控制器模式列舉型別
u32 fladj; // frame length adjustment
u32 irq_gadget; // USB處於裝置模式時的中斷號
u32 nr_scratch; // scratch buffers的數量,沒有使用
u32 u1u2; // only used on revisions <1.83a for workaround
u32 maximum_speed; // 最大的速度
u32 revision; // USB控制器暫存器中的版本號
enum dwc3_ep0_next ep0_next_event; // hold the next expected event
enum dwc3_ep0_state ep0state; // 端點0的狀態
enum dwc3_link_state link_state; // 連結狀態
u16 isoch_delay; // wValue from Set Isochronous Delay request
u16 u2sel; // parameter from Set SEL request
u16 u2pel; // parameter from Set SEL request
u8 u1sel; // parameter from Set SEL request
u8 u1pel; // parameter from Set SEL request
u8 speed; // device speed (super, high, full, low)
u8 num_out_eps; // 輸出端點的數量
u8 num_in_eps; // 輸入端點的數量
void *mem; // 指向該結構體的起始記憶體地址,dwc3有對齊要求,可能開始的多個位元組記憶體沒有使用
struct dwc3_hwparams hwparams; // 暫存器快取
struct dentry *root;
struct debugfs_regset32 *regset;
u8 test_mode;
u8 test_mode_nr;
u8 lpm_nyet_threshold;
u8 hird_threshold;
u32 grxthrcfg[2];
u32 gtxthrcfg[2];
};
struct dwc3結構體很複雜,內部嵌入了很多重要的資料結構,它們之間的關係可以簡略用下面的圖說明。xhci指向了usb作為主機模式時的資料結構,使用platform_device表示,主機模式的驅動是platform_driver,兩個會通過裝置名稱xhci-hcd匹配,具體在分析主機驅動時說明。
usb作為裝置模式時的資料結構是usb_gadget和gadget_driver;usb_gadget內部包含了usb_udc和usb_gadget_ops資料結構,usb_gadget_ops是usb控制器硬體操作函式集合,指向了dwc3_gadget_ops;gadget_driver是具體裝置的驅動,需要根據匹配裝置的型別決定,在裝置匹配成功時設定。eps[32]是一個指標陣列,儲存了裝置模式時所有端點結構體dwc3_ep的指標,每一個端點都對應一個dwc3_ep資料結構,端點0的操作函式集合指向了dwc3_gadget_ep0_ops,其他端點的操作函式集合指向了dwc3_gadget_ep_ops。
3.2.2.驅動分析
dwc3_rockchip_probe會將裝置樹節點usbdrd_dwc3_0轉換為platform_device,隨後會和dwc3_driver匹配,匹配成功後dwc3_probe函式將會被執行。
[drivers/usb/dwc3/core.c]
static const struct of_device_id of_dwc3_match[] = {
{
.compatible = "snps,dwc3"
},
{
.compatible = "synopsys,dwc3"
},
{ },
};
static struct platform_driver dwc3_driver = {
.probe = dwc3_probe,
.remove = dwc3_remove,
.driver = {
.name = "dwc3",
.of_match_table = of_match_ptr(of_dwc3_match),
.acpi_match_table = ACPI_PTR(dwc3_acpi_match),
.pm = &dwc3_dev_pm_ops,
},
};
module_platform_driver(dwc3_driver);
dwc3_probe完成dwc3 USB3.0控制器初始化的工作。主要內容如下:
(1)分配驅動的資料結構dwc3,並且按16位元組對齊
(2)獲取並處理資源,如暫存器資源、引數、最大速度、dr_mode等其他屬性
(3)分配一致性DMA緩衝區dwc3_event_buffer,DMA將USB控制器事件傳輸到dwc3_event_buffer後由CPU處理
(4)核心初始化和USB模式初始化,後面詳細分析
(5)初始化除錯檔案,具體如下圖所示,使用者可以在使用者空間獲取USB控制器資訊和控制USB控制器
dwc3_probe
mem = devm_kzalloc // 分配dwc3結構體
dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1) // 按16位元組對齊
dwc->mem = mem // 儲存分配的記憶體地址
// 設定64位DMA掩碼,裝置並不一定能在所有記憶體地址上執行DMA操作,在這種情況下,應該設定
// DMA地址掩碼,dma_mask是裝置DMA可以定址的範圍,coherent_dma_mask用於一致性對映的DMA掩碼
dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64))
platform_get_resource // 獲取暫存器地址資源
devm_ioremap_resource // 對映暫存器地址,但不包括xHCI的暫存器,xHCI的暫存器由其驅動處理
lpm_nyet_threshold = 0xff /* default to highest possible threshold */
tx_de_emphasis = 1 /* default to -3.5dB de-emphasis */
/* default to assert utmi_sleep_n and use maximum allowed HIRD
threshold value of 0b1100 */
hird_threshold = 12
usb_get_maximum_speed // 獲取最大速度
usb_get_dr_mode // 獲取dr_mode
...... // 獲取dwc3的一些屬性,可參考裝置樹節點和dwc3.txt文件
// 若maximum_speed為USB_SPEED_UNKNOWN,則設定為USB_SPEED_SUPER
dwc->maximum_speed = USB_SPEED_SUPER;
platform_set_drvdata // 設定platform_device的私有資料
dwc3_cache_hwparams // 讀取dwc3控制器內部暫存器儲存的引數,儲存到dwc3的hwparams
dwc3_core_get_phy // 獲取dwc3 usb控制器的phy
dwc3_alloc_event_buffers // 分配事件緩衝區,長度為DWC3_EVENT_BUFFERS_SIZE=4096
dwc3_alloc_one_event_buffer
devm_kzalloc // 首先分配管理事件緩衝區的dwc3_event_buffer結構體
evt->dwc = dwc // 儲存dwc3結構體指標
evt->length = length // 儲存緩衝區長度
// 分配一致性DMA緩衝區,buf儲存虛擬地址,&evt->dma儲存實體地址
evt->buf = dma_alloc_coherent(dwc->dev, length, &evt->dma, GFP_KERNEL)
dwc3_alloc_scratch_buffers // 分配暫存緩衝區,沒有使用
dwc->scratchbuf = kmalloc_array // 分配nr_scratch * DWC3_SCRATCHBUF_SIZE記憶體
dwc3_core_init // 核心初始化
dwc3_core_init_mode // 根據dr_mode初始化對應的模式,有device、host和otg模式
dwc3_debugfs_init // dwc3除錯屬性相關初始化
debugfs_create_dir // 建立除錯目錄/sys/kernel/debug/fe800000.dwc3(fe900000.dwc3)
kzalloc // 分配debugfs_regset32結構體
dwc->regset->regs = dwc3_regs // 儲存需要dump的暫存器
debugfs_create_regset32("regdump", ...) // 建立可以dump暫存器值的除錯檔案
// 建立可以切換模式的除錯檔案,需要開啟CONFIG_USB_DWC3_DUAL_ROLE選項
debugfs_create_file("mode", ...., &dwc3_mode_fops)
// 建立除錯模式的除錯檔案,需要開啟CONFIG_USB_DWC3_DUAL_ROLE或CONFIG_USB_DWC3_GADGET選項
debugfs_create_file("testmode", ..., &dwc3_testmode_fops)
// 建立link_state除錯檔案,需要開啟CONFIG_USB_DWC3_DUAL_ROLE或CONFIG_USB_DWC3_GADGET選項
debugfs_create_file("link_state", ..., &dwc3_link_state_fops)
// 建立端點除錯檔案,需要開啟CONFIG_USB_DWC3_DUAL_ROLE或CONFIG_USB_DWC3_GADGET選項
dwc3_debugfs_create_endpoint_dirs(dwc, root)
dwc3_debugfs_create_endpoint_dir // 迴圈建立輸入端點除錯檔案
debugfs_create_dir // 建立目錄
dwc3_debugfs_create_endpoint_files // 建立檔案
dwc3_debugfs_create_endpoint_dir // 迴圈建立輸出端點除錯檔案
debugfs_create_dir // 建立目錄
dwc3_debugfs_create_endpoint_files // 建立檔案
dwc3_core_init主要的工作是初始化USB控制器硬體,主要流程如下:
(1)獲取USB控制器IP的版本,便於後續進行不同的配置,USB控制器不同IP版本之間有差別,將Linux核心版本號寫入USB控制器暫存器,以便發現某些版本下的bug
(2)根據dr_mode,選擇是否復位USB控制器
(3)從USB控制器暫存器快取中獲取USB控制器端點數量,此處端點表示的是一組資源
(4)建立scratch_buffers,採用流式DMA對映,RK3399的USB控制器沒有使用該特性
dwc3_core_init dwc3_readl(dwc->regs, DWC3_GSNPSID) // 獲取Global SNPS ID Register中的內容 /* Write Linux Version Code to our GUID register so it's easy to figure out which kernel version a bug was found. */ dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE) dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE) dwc3_soft_reset // 軟體復位USB控制器 // 只有USB控制器處於裝置模式才會復位,處於主機模式或dr_mode為OTG模式 // 且暫存器被配置為主機模式時不復位,由後續的驅動復位 dwc3_readl(dwc->regs, DWC3_GCTL) // 讀取USB的全域性控制暫存器 timeout = jiffies + msecs_to_jiffies(500) // 設定復位等待超時時間為500毫秒 dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST) // 軟體復位,復位成功該位被硬體清除 // 迴圈讀取DWC3_DCTL暫存器,判斷復位是否成功 dwc3_readl(dwc->regs, DWC3_DCTL) time_after(jiffies, timeout) // 判斷超時時間是否到 cpu_relax() // 記憶體屏障 asm volatile("yield" ::: "memory") // yield:表示當前執行的執行緒不重要,可以被切換出去,memory:記憶體屏障 asm volatile("yield" ::: "memory") // aarch64 dwc3_core_soft_reset // 初始化PHY dwc3_phy_setup // Configure USB PHY Interface of DWC3 Core dwc3_core_num_eps // 獲取端點數量 dwc->num_in_eps = DWC3_NUM_IN_EPS(parms) // 輸入端點數量 dwc->num_out_eps = DWC3_NUM_EPS(parms) - dwc->num_in_eps // 輸出端點數量 dwc3_setup_scratch_buffers // 沒有使用 // 流式DMA對映,資料可以雙向移動,即可以傳輸到裝置也可以從裝置傳出,返回實體地址 scratch_addr = dma_map_single(..., DMA_BIDIRECTIONAL); dwc->scratch_addr = scratch_addr // 儲存實體地址 param = lower_32_bits(scratch_addr) // 獲取低32位實體地址 dwc3_send_gadget_generic_command(..., DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO, ...) // 向暫存器寫入低32位地址,作為命令的引數,命令執行的時候會使用 dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param) // 寫入命令,DWC3_DGCMD_CMDACT:啟用命令,設定後開始執行命令 // DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO:Set Scratchpad Buffer Array Address Lo dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT)
USB控制器host模式驅動和device模式驅動是兩套驅動框架,不能通用。因此,dwc3驅動初始化的時候,會根據dr_mode初始化對應的USB模式驅動,dr_mode在裝置樹中指定。設定USB控制器模式驅動的是dwc3_core_init_mode函式,該函式的主要工作如下:
dwc3_core_init_mode // 根據dr_mode初始化對應的驅動 // dwc->dr_mode == USB_DR_MODE_PERIPHERAL dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE) // 設定USB控制器為裝置模式 dwc3_gadget_init // 初始化裝置驅動 // dwc->dr_mode == USB_DR_MODE_HOST dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST) // 設定USB控制器為主機模式 dwc3_host_init // 初始化主機驅動 // dwc->dr_mode == USB_DR_MODE_OTG // 設定USB控制器為OTG模式,處於OTG模式的USB控制器可以為主機也可以為裝置,由extcon負責切換 dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG) dwc3_host_init // 初始化主機驅動 dwc3_gadget_init // 初始化裝置驅動
主機驅動和裝置驅動具體的初始化過程在後面講到主機和裝置的時候具體分析。