本文以imx6ul SoC为例。
涉及目录及文件:
dts: linux-4.14.141\arch\arm\boot\dts\imx6ul.dtsi
controller: drivers\usb\chipidea
phy: drivers\usb\phy
几点说明:
1)4.13 kernel
2)4.13内核中全部是usb2.0的PHY驱动,没有usb3.0的驱动。也许usb3.0的PHY都不需要初始化,直接就可以使用,如femtoPHY。
3)imx6ul的usb controller和phy都集成在SoC中
1. dts配置
\linux-4.14.141\arch\arm\boot\dts\imx6ul.dtsi
usbotg1: usb@02184000 {
compatible = "fsl,imx6ul-usb", "fsl,imx27-usb";
reg = <0x02184000 0x200>;
interrupts = ;
clocks = <&clks IMX6UL_CLK_USBOH3>;
fsl,usbphy = <&usbphy1>;
fsl,usbmisc = <&usbmisc 0>;
fsl,anatop = <&anatop>;
ahb-burst-config = <0x0>;
tx-burst-size-dword = <0x10>;
rx-burst-size-dword = <0x10>;
status = "disabled";
};
usbphy1: usbphy@020c9000 {
compatible = "fsl,imx6ul-usbphy", "fsl,imx23-usbphy";
reg = <0x020c9000 0x1000>;
interrupts = ;
clocks = <&clks IMX6UL_CLK_USBPHY1>;
phy-3p0-supply = <®_3p0>;
fsl,anatop = <&anatop>;
};
2. usb controller
drivers/usb/chipidea/ci_hdrc_imx.c
static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
{ .compatible = "fsl,imx23-usb", .data = &imx23_usb_data},
{ .compatible = "fsl,imx28-usb", .data = &imx28_usb_data},
......
{ .compatible = "fsl,imx6ul-usb", .data = &imx6ul_usb_data},
{ .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data},
{ /* sentinel */ }
};
static struct platform_driver ci_hdrc_imx_driver = {
.probe = ci_hdrc_imx_probe,
.remove = ci_hdrc_imx_remove,
.shutdown = ci_hdrc_imx_shutdown,
.driver = {
.name = "imx_usb",
.of_match_table = ci_hdrc_imx_dt_ids,
.pm = &ci_hdrc_imx_pm_ops,
},
};
drivers/usb/chipidea/core.c
static struct platform_driver ci_hdrc_driver = {
.probe = ci_hdrc_probe,
.remove = ci_hdrc_remove,
.driver = {
.name = "ci_hdrc",
.pm = &ci_pm_ops,
},
};
2.1 首先根据"fsl,imx6ul-usb"进行dts match,执行ci_hdrc_imx_probe。在此函数中:
1)首先获得devm_usb_get_phy_by_phandle("fsl,usbphy") 获得phy,"fsl,usbphy"在dts中有定义
2)然后调用ci_hdrc_add_device添加platform device ("ci_hdrc"),下一步会match 相应的platform driver
函数流程如下:
chipidea/ci_hdrc_imx.c: ci_hdrc_imx_probe -> devm_usb_get_phy_by_phandle | ci_hdrc_add_device -> chipidea/core.c: platform_device_add("ci_hdrc")
2.2 初始化PHY
1)上述添加platform device ("ci_hdrc")后,会match到相应的platform driver,然后执行ci_hdrc_probe
2)在ci_hdrc_probe中初始化usb phy
函数流程如下:
chipidea/core.c: ci_hdrc_probe -> ci_usb_phy_init -> phy.h: usb_phy_init -> (phy->init)
3. usb PHY
drivers/usb/phy/phy-mxs-usb.c
在该文件中主要实现PHY的相关操作函数,然后被controller调用进行初始化。
1)根据"fsl,imx6ul-usbphy"进行dts match,match到后执行mxs_phy_probe
static const struct of_device_id mxs_phy_dt_ids[] = {
{ .compatible = "fsl,imx6sx-usbphy", .data = &imx6sx_phy_data, },
{ .compatible = "fsl,imx6sl-usbphy", .data = &imx6sl_phy_data, },
{ .compatible = "fsl,imx6q-usbphy", .data = &imx6q_phy_data, },
{ .compatible = "fsl,imx23-usbphy", .data = &imx23_phy_data, },
{ .compatible = "fsl,vf610-usbphy", .data = &vf610_phy_data, },
{ .compatible = "fsl,imx6ul-usbphy", .data = &imx6ul_phy_data, },
{ /* sentinel */ }
};
2)在mxs_phy_probe中先设置phy的相关操作函数
mxs_phy->phy.io_priv = base;
mxs_phy->phy.dev = &pdev->dev;
mxs_phy->phy.label = DRIVER_NAME;
mxs_phy->phy.init = mxs_phy_init;
mxs_phy->phy.shutdown = mxs_phy_shutdown;
mxs_phy->phy.set_suspend = mxs_phy_suspend;
mxs_phy->phy.notify_connect = mxs_phy_on_connect;
mxs_phy->phy.notify_disconnect = mxs_phy_on_disconnect;
mxs_phy->phy.type = USB_PHY_TYPE_USB2;
mxs_phy->phy.set_wakeup = mxs_phy_set_wakeup;
3)最后通过usb_add_phy_dev(&mxs_phy->phy)添加usb PHY,被host或device controller driver使用。
4. register
4.1 usb phy
1)usb phy register base address
从上面可以得出usb phy及地址是020c_9000,这也符合dts中的地址定义
2)在phy-mxs-usb.c中定义的寄存器地址偏移
#define HW_USBPHY_PWD 0x00
#define HW_USBPHY_TX 0x10
#define HW_USBPHY_CTRL 0x30
#define HW_USBPHY_CTRL_SET 0x34
#define HW_USBPHY_CTRL_CLR 0x38
#define HW_USBPHY_DEBUG_SET 0x54
#define HW_USBPHY_DEBUG_CLR 0x58
#define HW_USBPHY_IP 0x90
#define HW_USBPHY_IP_SET 0x94
#define HW_USBPHY_IP_CLR 0x98
4.2 usb controller
1)usb controller register base address
从上面可以得出usb phy及地址是0218_4000,这也符合dts中的地址定义
2)在chipidea/ci.h中定义的寄存器地址偏移
#define ID_ID 0x0
#define ID_HWGENERAL 0x4
#define ID_HWHOST 0x8
#define ID_HWDEVICE 0xc
#define ID_HWTXBUF 0x10
#define ID_HWRXBUF 0x14
#define ID_SBUSCFG 0x90