diff options
-rw-r--r-- | MAINTAINERS | 3 | ||||
-rw-r--r-- | arch/arm/dts/mt7629-rfb.dts | 8 | ||||
-rw-r--r-- | arch/arm/dts/mt7629.dtsi | 41 | ||||
-rw-r--r-- | arch/sandbox/dts/test.dts | 29 | ||||
-rw-r--r-- | doc/device-tree-bindings/phy/phy-mtk-tphy.txt | 78 | ||||
-rw-r--r-- | doc/device-tree-bindings/usb/mediatek,mtk-xhci.txt | 40 | ||||
-rw-r--r-- | drivers/core/ofnode.c | 11 | ||||
-rw-r--r-- | drivers/core/read.c | 5 | ||||
-rw-r--r-- | drivers/phy/phy-mtk-tphy.c | 316 | ||||
-rw-r--r-- | drivers/phy/phy-uclass.c | 97 | ||||
-rw-r--r-- | drivers/usb/dwc3/core.c | 87 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-generic.c | 7 | ||||
-rw-r--r-- | drivers/usb/gadget/dwc2_udc_otg.c | 93 | ||||
-rw-r--r-- | drivers/usb/host/Kconfig | 6 | ||||
-rw-r--r-- | drivers/usb/host/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/host/xhci-dwc3.c | 7 | ||||
-rw-r--r-- | drivers/usb/host/xhci-mtk.c | 303 | ||||
-rw-r--r-- | drivers/usb/host/xhci.c | 10 | ||||
-rw-r--r-- | include/dm/ofnode.h | 8 | ||||
-rw-r--r-- | include/dm/read.h | 13 | ||||
-rw-r--r-- | include/dwc3-uboot.h | 11 | ||||
-rw-r--r-- | include/generic-phy.h | 92 | ||||
-rw-r--r-- | include/usb/xhci.h | 3 | ||||
-rw-r--r-- | test/dm/ofnode.c | 21 | ||||
-rw-r--r-- | test/dm/phy.c | 33 |
25 files changed, 1135 insertions, 188 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 66f0b07263..ec59ce8b88 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -244,9 +244,12 @@ S: Maintained F: arch/arm/mach-mediatek/ F: arch/arm/include/asm/arch-mediatek/ F: board/mediatek/ +F: doc/device-tree-bindings/phy/phy-mtk-* +F: doc/device-tree-bindings/usb/mediatek,* F: doc/README.mediatek F: drivers/clk/mediatek/ F: drivers/mmc/mtk-sd.c +F: drivers/phy/phy-mtk-* F: drivers/pinctrl/mediatek/ F: drivers/power/domain/mtk-power-domain.c F: drivers/ram/mediatek/ diff --git a/arch/arm/dts/mt7629-rfb.dts b/arch/arm/dts/mt7629-rfb.dts index 687fe1c029..bf84f76344 100644 --- a/arch/arm/dts/mt7629-rfb.dts +++ b/arch/arm/dts/mt7629-rfb.dts @@ -82,6 +82,14 @@ status = "okay"; }; +&xhci { + status = "okay"; +}; + +&u3phy { + status = "okay"; +}; + &watchdog { pinctrl-names = "default"; pinctrl-0 = <&watchdog_pins>; diff --git a/arch/arm/dts/mt7629.dtsi b/arch/arm/dts/mt7629.dtsi index 644d2da4a8..6850e0058d 100644 --- a/arch/arm/dts/mt7629.dtsi +++ b/arch/arm/dts/mt7629.dtsi @@ -11,6 +11,7 @@ #include <dt-bindings/interrupt-controller/arm-gic.h> #include <dt-bindings/power/mt7629-power.h> #include <dt-bindings/reset/mt7629-reset.h> +#include <dt-bindings/phy/phy.h> #include "skeleton.dtsi" / { @@ -222,6 +223,46 @@ #size-cells = <0>; }; + ssusbsys: ssusbsys@1a000000 { + compatible = "mediatek,mt7629-ssusbsys", "syscon"; + reg = <0x1a000000 0x1000>; + #clock-cells = <1>; + }; + + xhci: usb@1a0c0000 { + compatible = "mediatek,mt7629-xhci", "mediatek,mtk-xhci"; + reg = <0x1a0c0000 0x1000>, <0x1a0c3e00 0x0100>; + reg-names = "mac", "ippc"; + power-domains = <&scpsys MT7629_POWER_DOMAIN_HIF1>; + clocks = <&ssusbsys CLK_SSUSB_SYS_EN>, + <&ssusbsys CLK_SSUSB_REF_EN>, + <&ssusbsys CLK_SSUSB_MCU_EN>, + <&ssusbsys CLK_SSUSB_DMA_EN>; + clock-names = "sys_ck", "ref_ck", "mcu_ck", "dma_ck"; + phys = <&u2port0 PHY_TYPE_USB2>, <&u3port0 PHY_TYPE_USB3>; + status = "disabled"; + }; + + u3phy: usb-phy@1a0c4000 { + compatible = "mediatek,mt7629-tphy", "mediatek,generic-tphy-v2"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x1a0c4000 0x1000>; + status = "disabled"; + + u2port0: usb-phy@0 { + reg = <0x0 0x0700>; + #phy-cells = <1>; + clocks = <&ssusbsys CLK_SSUSB_U2_PHY_EN>; + clock-names = "ref"; + }; + + u3port0: usb-phy@700 { + reg = <0x0700 0x0700>; + #phy-cells = <1>; + }; + }; + ethsys: syscon@1b000000 { compatible = "mediatek,mt7629-ethsys", "syscon"; reg = <0x1b000000 0x1000>; diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 4bccfbe6e1..15cd2330a3 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -159,12 +159,23 @@ broken; }; + phy_provider2: gen_phy@2 { + compatible = "sandbox,phy"; + #phy-cells = <0>; + }; + gen_phy_user: gen_phy_user { compatible = "simple-bus"; phys = <&phy_provider0 0>, <&phy_provider0 1>, <&phy_provider1>; phy-names = "phy1", "phy2", "phy3"; }; + gen_phy_user1: gen_phy_user1 { + compatible = "simple-bus"; + phys = <&phy_provider0 0>, <&phy_provider2>; + phy-names = "phy1", "phy2"; + }; + some-bus { #address-cells = <1>; #size-cells = <0>; @@ -218,6 +229,24 @@ compatible = "denx,u-boot-fdt-test1"; }; + i-test { + compatible = "mediatek,u-boot-fdt-test"; + #address-cells = <1>; + #size-cells = <0>; + + subnode@0 { + reg = <0>; + }; + + subnode@1 { + reg = <1>; + }; + + subnode@2 { + reg = <2>; + }; + }; + devres-test { compatible = "denx,u-boot-devres-test"; }; diff --git a/doc/device-tree-bindings/phy/phy-mtk-tphy.txt b/doc/device-tree-bindings/phy/phy-mtk-tphy.txt index 037c5a4be5..8cd23d8c0b 100644 --- a/doc/device-tree-bindings/phy/phy-mtk-tphy.txt +++ b/doc/device-tree-bindings/phy/phy-mtk-tphy.txt @@ -7,10 +7,17 @@ controllers on MediaTek SoCs, such as, USB2.0, USB3.0, PCIe, and SATA. Required properties (controller (parent) node): - compatible : should be one of "mediatek,generic-tphy-v1" - - clocks : (deprecated, use port's clocks instead) a list of phandle + - clock-specifier pairs, one for each entry in clock-names - - clock-names : (deprecated, use port's one instead) must contain - "u3phya_ref": for reference clock of usb3.0 analog phy. + "mediatek,generic-tphy-v2" + +- #address-cells: the number of cells used to represent physical + base addresses. +- #size-cells: the number of cells used to represent the size of an address. +- ranges: the address mapping relationship to the parent, defined with + - empty value: if optional 'reg' is used. + - non-empty value: if optional 'reg' is not used. should set + the child's base address to 0, the physical address + within parent's address space, and the length of + the address map. Required nodes : a sub-node is required for each port the controller provides. Address range information including the usual @@ -27,12 +34,6 @@ Optional properties (controller (parent) node): Required properties (port (child) node): - reg : address and length of the register set for the port. -- clocks : a list of phandle + clock-specifier pairs, one for each - entry in clock-names -- clock-names : must contain - "ref": 48M reference clock for HighSpeed analog phy; and 26M - reference clock for SuperSpeed analog phy, sometimes is - 24M, 25M or 27M, depended on platform. - #phy-cells : should be 1 (See second example) cell after port phandle is phy type from: - PHY_TYPE_USB2 @@ -40,6 +41,17 @@ Required properties (port (child) node): - PHY_TYPE_PCIE - PHY_TYPE_SATA +Optional properties (port (child) node): +- clocks : a list of phandle + clock-specifier pairs, one for each + entry in clock-names +- clock-names : may contain + "ref": 48M reference clock for HighSpeed (digital) phy; and 26M + reference clock for SuperSpeed (digital) phy, sometimes is + 24M, 25M or 27M, depended on platform. + "da_ref": the reference clock of analog phy, used if the clocks + of analog and digital phys are separated, otherwise uses + "ref" clock only if needed. + Example: u3phy2: usb-phy@1a244000 { @@ -84,3 +96,49 @@ usb30: usb@11270000 { phy-names = "usb2-0", "usb3-0"; ... }; + +Layout differences of banks between TPHY V1 and V2 +------------------------------------------------------------- +IP V1: +port offset bank +shared 0x0000 SPLLC + 0x0100 FMREG +u2 port0 0x0800 U2PHY_COM +u3 port0 0x0900 U3PHYD + 0x0a00 U3PHYD_BANK2 + 0x0b00 U3PHYA + 0x0c00 U3PHYA_DA +u2 port1 0x1000 U2PHY_COM +u3 port1 0x1100 U3PHYD + 0x1200 U3PHYD_BANK2 + 0x1300 U3PHYA + 0x1400 U3PHYA_DA +u2 port2 0x1800 U2PHY_COM + ... + +IP V2: +port offset bank +u2 port0 0x0000 MISC + 0x0100 FMREG + 0x0300 U2PHY_COM +u3 port0 0x0700 SPLLC + 0x0800 CHIP + 0x0900 U3PHYD + 0x0a00 U3PHYD_BANK2 + 0x0b00 U3PHYA + 0x0c00 U3PHYA_DA +u2 port1 0x1000 MISC + 0x1100 FMREG + 0x1300 U2PHY_COM +u3 port1 0x1700 SPLLC + 0x1800 CHIP + 0x1900 U3PHYD + 0x1a00 U3PHYD_BANK2 + 0x1b00 U3PHYA + 0x1c00 U3PHYA_DA +u2 port2 0x2000 MISC + ... + + SPLLC shared by u3 ports and FMREG shared by u2 ports on +TPHY V1 are put back into each port; a new bank MISC for +u2 ports and CHIP for u3 ports are added on TPHY V2. diff --git a/doc/device-tree-bindings/usb/mediatek,mtk-xhci.txt b/doc/device-tree-bindings/usb/mediatek,mtk-xhci.txt new file mode 100644 index 0000000000..0447468a2d --- /dev/null +++ b/doc/device-tree-bindings/usb/mediatek,mtk-xhci.txt @@ -0,0 +1,40 @@ +MediaTek xHCI + +The device node for USB3 host controller on MediaTek SoCs. + +Required properties: + - compatible : should be "mediatek,mtk-xhci" + - reg : specifies physical base address and size of the registers + - reg-names: should be "mac" for xHCI MAC and "ippc" for IP port control + - power-domains : a phandle to USB power domain node to control USB's + MTCMOS + - vusb33-supply : regulator of USB avdd3.3v + + - clocks : a list of phandle + clock-specifier pairs, one for each + entry in clock-names + - clock-names : must contain + "sys_ck": controller clock used by normal mode, + the following ones are optional: + "ref_ck": reference clock used by low power mode etc, + "mcu_ck": mcu_bus clock for register access, + "dma_ck": dma_bus clock for data transfer by DMA, + "xhci_ck": controller clock + + - phys : list of all the USB PHYs on this HCD + - phy-names: name specifier for the USB PHY + +Optional properties: + - vbus-supply : reference to the VBUS regulator; + +Example: +xhci: usb@1a0c0000 { + compatible = "mediatek,mt7629-xhci", "mediatek,mtk-xhci"; + reg = <0x1a0c0000 0x1000>, <0x1a0c3e00 0x0100>; + reg-names = "mac", "ippc"; + power-domains = <&scpsys MT7629_POWER_DOMAIN_HIF1>; + clocks = <&ssusbsys CLK_SSUSB_SYS_EN>, <&ssusbsys CLK_SSUSB_REF_EN>, + <&ssusbsys CLK_SSUSB_MCU_EN>, <&ssusbsys CLK_SSUSB_DMA_EN>; + clock-names = "sys_ck", "ref_ck", "mcu_ck", "dma_ck"; + phys = <&u2port0 PHY_TYPE_USB2>, <&u3port0 PHY_TYPE_USB3>; + status = "disabled"; +}; diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c index 20871a6815..e3c42dae5c 100644 --- a/drivers/core/ofnode.c +++ b/drivers/core/ofnode.c @@ -474,6 +474,17 @@ ofnode ofnode_get_chosen_node(const char *name) return ofnode_path(prop); } +int ofnode_get_child_count(ofnode parent) +{ + ofnode child; + int num = 0; + + ofnode_for_each_subnode(child, parent) + num++; + + return num; +} + static int decode_timing_property(ofnode node, const char *name, struct timing_entry *result) { diff --git a/drivers/core/read.c b/drivers/core/read.c index 47b8e03446..3d421f7a69 100644 --- a/drivers/core/read.c +++ b/drivers/core/read.c @@ -352,3 +352,8 @@ fdt_addr_t dev_read_addr_pci(const struct udevice *dev) return addr; } + +int dev_get_child_count(const struct udevice *dev) +{ + return ofnode_get_child_count(dev_ofnode(dev)); +} diff --git a/drivers/phy/phy-mtk-tphy.c b/drivers/phy/phy-mtk-tphy.c index bd089b7a43..81525a48b7 100644 --- a/drivers/phy/phy-mtk-tphy.c +++ b/drivers/phy/phy-mtk-tphy.c @@ -20,11 +20,77 @@ /* version V1 sub-banks offset base address */ /* banks shared by multiple phys */ #define SSUSB_SIFSLV_V1_SPLLC 0x000 /* shared by u3 phys */ +#define SSUSB_SIFSLV_V1_U2FREQ 0x100 /* shared by u2 phys */ #define SSUSB_SIFSLV_V1_CHIP 0x300 /* shared by u3 phys */ +/* u2 phy bank */ +#define SSUSB_SIFSLV_V1_U2PHY_COM 0x000 /* u3/pcie/sata phy banks */ #define SSUSB_SIFSLV_V1_U3PHYD 0x000 #define SSUSB_SIFSLV_V1_U3PHYA 0x200 +/* version V2 sub-banks offset base address */ +/* u2 phy banks */ +#define SSUSB_SIFSLV_V2_MISC 0x000 +#define SSUSB_SIFSLV_V2_U2FREQ 0x100 +#define SSUSB_SIFSLV_V2_U2PHY_COM 0x300 +/* u3/pcie/sata phy banks */ +#define SSUSB_SIFSLV_V2_SPLLC 0x000 +#define SSUSB_SIFSLV_V2_CHIP 0x100 +#define SSUSB_SIFSLV_V2_U3PHYD 0x200 +#define SSUSB_SIFSLV_V2_U3PHYA 0x400 + +#define U3P_USBPHYACR0 0x000 +#define PA0_RG_U2PLL_FORCE_ON BIT(15) +#define PA0_RG_USB20_INTR_EN BIT(5) + +#define U3P_USBPHYACR5 0x014 +#define PA5_RG_U2_HSTX_SRCAL_EN BIT(15) +#define PA5_RG_U2_HSTX_SRCTRL GENMASK(14, 12) +#define PA5_RG_U2_HSTX_SRCTRL_VAL(x) ((0x7 & (x)) << 12) +#define PA5_RG_U2_HS_100U_U3_EN BIT(11) + +#define U3P_USBPHYACR6 0x018 +#define PA6_RG_U2_BC11_SW_EN BIT(23) +#define PA6_RG_U2_OTG_VBUSCMP_EN BIT(20) +#define PA6_RG_U2_SQTH GENMASK(3, 0) +#define PA6_RG_U2_SQTH_VAL(x) (0xf & (x)) + +#define U3P_U2PHYACR4 0x020 +#define P2C_RG_USB20_GPIO_CTL BIT(9) +#define P2C_USB20_GPIO_MODE BIT(8) +#define P2C_U2_GPIO_CTR_MSK \ + (P2C_RG_USB20_GPIO_CTL | P2C_USB20_GPIO_MODE) + +#define U3P_U2PHYDTM0 0x068 +#define P2C_FORCE_UART_EN BIT(26) +#define P2C_FORCE_DATAIN BIT(23) +#define P2C_FORCE_DM_PULLDOWN BIT(21) +#define P2C_FORCE_DP_PULLDOWN BIT(20) +#define P2C_FORCE_XCVRSEL BIT(19) +#define P2C_FORCE_SUSPENDM BIT(18) +#define P2C_FORCE_TERMSEL BIT(17) +#define P2C_RG_DATAIN GENMASK(13, 10) +#define P2C_RG_DATAIN_VAL(x) ((0xf & (x)) << 10) +#define P2C_RG_DMPULLDOWN BIT(7) +#define P2C_RG_DPPULLDOWN BIT(6) +#define P2C_RG_XCVRSEL GENMASK(5, 4) +#define P2C_RG_XCVRSEL_VAL(x) ((0x3 & (x)) << 4) +#define P2C_RG_SUSPENDM BIT(3) +#define P2C_RG_TERMSEL BIT(2) +#define P2C_DTM0_PART_MASK \ + (P2C_FORCE_DATAIN | P2C_FORCE_DM_PULLDOWN | \ + P2C_FORCE_DP_PULLDOWN | P2C_FORCE_XCVRSEL | \ + P2C_FORCE_TERMSEL | P2C_RG_DMPULLDOWN | \ + P2C_RG_DPPULLDOWN | P2C_RG_TERMSEL) + +#define U3P_U2PHYDTM1 0x06C +#define P2C_RG_UART_EN BIT(16) +#define P2C_FORCE_IDDIG BIT(9) +#define P2C_RG_VBUSVALID BIT(5) +#define P2C_RG_SESSEND BIT(4) +#define P2C_RG_AVALID BIT(2) +#define P2C_RG_IDDIG BIT(1) + #define U3P_U3_CHIP_GPIO_CTLD 0x0c #define P3C_REG_IP_SW_RST BIT(31) #define P3C_MCU_BUS_CK_GATE_EN BIT(30) @@ -42,6 +108,14 @@ #define P3A_RG_CLKDRV_AMP GENMASK(31, 29) #define P3A_RG_CLKDRV_AMP_VAL(x) ((0x7 & (x)) << 29) +#define U3P_U3_PHYA_REG6 0x018 +#define P3A_RG_TX_EIDLE_CM GENMASK(31, 28) +#define P3A_RG_TX_EIDLE_CM_VAL(x) ((0xf & (x)) << 28) + +#define U3P_U3_PHYA_REG9 0x024 +#define P3A_RG_RX_DAC_MUX GENMASK(5, 1) +#define P3A_RG_RX_DAC_MUX_VAL(x) ((0x1f & (x)) << 1) + #define U3P_U3_PHYA_DA_REG0 0x100 #define P3A_RG_XTAL_EXT_PE2H GENMASK(17, 16) #define P3A_RG_XTAL_EXT_PE2H_VAL(x) ((0x3 & (x)) << 16) @@ -77,6 +151,16 @@ #define P3A_RG_PLL_DELTA_PE2H GENMASK(15, 0) #define P3A_RG_PLL_DELTA_PE2H_VAL(x) (0xffff & (x)) +#define U3P_U3_PHYD_LFPS1 0x00c +#define P3D_RG_FWAKE_TH GENMASK(21, 16) +#define P3D_RG_FWAKE_TH_VAL(x) ((0x3f & (x)) << 16) + +#define U3P_U3_PHYD_CDR1 0x05c +#define P3D_RG_CDR_BIR_LTD1 GENMASK(28, 24) +#define P3D_RG_CDR_BIR_LTD1_VAL(x) ((0x1f & (x)) << 24) +#define P3D_RG_CDR_BIR_LTD0 GENMASK(12, 8) +#define P3D_RG_CDR_BIR_LTD0_VAL(x) ((0x1f & (x)) << 8) + #define U3P_U3_PHYD_RXDET1 0x128 #define P3D_RG_RXDET_STB2_SET GENMASK(17, 9) #define P3D_RG_RXDET_STB2_SET_VAL(x) ((0x1ff & (x)) << 9) @@ -85,6 +169,21 @@ #define P3D_RG_RXDET_STB2_SET_P3 GENMASK(8, 0) #define P3D_RG_RXDET_STB2_SET_P3_VAL(x) (0x1ff & (x)) +#define U3P_SPLLC_XTALCTL3 0x018 +#define XC3_RG_U3_XTAL_RX_PWD BIT(9) +#define XC3_RG_U3_FRC_XTAL_RX_PWD BIT(8) + +enum mtk_phy_version { + MTK_TPHY_V1 = 1, + MTK_TPHY_V2, +}; + +struct u2phy_banks { + void __iomem *misc; + void __iomem *fmreg; + void __iomem *com; +}; + struct u3phy_banks { void __iomem *spllc; void __iomem *chip; @@ -95,26 +194,136 @@ struct u3phy_banks { struct mtk_phy_instance { void __iomem *port_base; const struct device_node *np; + union { + struct u2phy_banks u2_banks; + struct u3phy_banks u3_banks; + }; - struct u3phy_banks u3_banks; - - /* reference clock of anolog phy */ - struct clk ref_clk; + struct clk ref_clk; /* reference clock of (digital) phy */ + struct clk da_ref_clk; /* reference clock of analog phy */ u32 index; - u8 type; + u32 type; }; struct mtk_tphy { + struct udevice *dev; void __iomem *sif_base; + enum mtk_phy_version version; struct mtk_phy_instance **phys; int nphys; }; +static void u2_phy_instance_init(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) +{ + struct u2phy_banks *u2_banks = &instance->u2_banks; + + /* switch to USB function, and enable usb pll */ + clrsetbits_le32(u2_banks->com + U3P_U2PHYDTM0, + P2C_FORCE_UART_EN | P2C_FORCE_SUSPENDM, + P2C_RG_XCVRSEL_VAL(1) | P2C_RG_DATAIN_VAL(0)); + + clrbits_le32(u2_banks->com + U3P_U2PHYDTM1, P2C_RG_UART_EN); + setbits_le32(u2_banks->com + U3P_USBPHYACR0, PA0_RG_USB20_INTR_EN); + + /* disable switch 100uA current to SSUSB */ + clrbits_le32(u2_banks->com + U3P_USBPHYACR5, PA5_RG_U2_HS_100U_U3_EN); + + clrbits_le32(u2_banks->com + U3P_U2PHYACR4, P2C_U2_GPIO_CTR_MSK); + + /* DP/DM BC1.1 path Disable */ + clrsetbits_le32(u2_banks->com + U3P_USBPHYACR6, + PA6_RG_U2_BC11_SW_EN | PA6_RG_U2_SQTH, + PA6_RG_U2_SQTH_VAL(2)); + + /* set HS slew rate */ + clrsetbits_le32(u2_banks->com + U3P_USBPHYACR5, + PA5_RG_U2_HSTX_SRCTRL, PA5_RG_U2_HSTX_SRCTRL_VAL(4)); + + dev_dbg(tphy->dev, "%s(%d)\n", __func__, instance->index); +} + +static void u2_phy_instance_power_on(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) +{ + struct u2phy_banks *u2_banks = &instance->u2_banks; + + clrbits_le32(u2_banks->com + U3P_U2PHYDTM0, + P2C_RG_XCVRSEL | P2C_RG_DATAIN | P2C_DTM0_PART_MASK); + + /* OTG Enable */ + setbits_le32(u2_banks->com + U3P_USBPHYACR6, + PA6_RG_U2_OTG_VBUSCMP_EN); + + clrsetbits_le32(u2_banks->com + U3P_U2PHYDTM1, + P2C_RG_SESSEND, P2C_RG_VBUSVALID | P2C_RG_AVALID); + + dev_dbg(tphy->dev, "%s(%d)\n", __func__, instance->index); +} + +static void u2_phy_instance_power_off(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) +{ + struct u2phy_banks *u2_banks = &instance->u2_banks; + + clrbits_le32(u2_banks->com + U3P_U2PHYDTM0, + P2C_RG_XCVRSEL | P2C_RG_DATAIN); + + /* OTG Disable */ + clrbits_le32(u2_banks->com + U3P_USBPHYACR6, + PA6_RG_U2_OTG_VBUSCMP_EN); + + clrsetbits_le32(u2_banks->com + U3P_U2PHYDTM1, + P2C_RG_VBUSVALID | P2C_RG_AVALID, P2C_RG_SESSEND); + + dev_dbg(tphy->dev, "%s(%d)\n", __func__, instance->index); +} + +static void u3_phy_instance_init(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) +{ + struct u3phy_banks *u3_banks = &instance->u3_banks; + + /* gating PCIe Analog XTAL clock */ + setbits_le32(u3_banks->spllc + U3P_SPLLC_XTALCTL3, + XC3_RG_U3_XTAL_RX_PWD | XC3_RG_U3_FRC_XTAL_RX_PWD); + + /* gating XSQ */ + clrsetbits_le32(u3_banks->phya + U3P_U3_PHYA_DA_REG0, + P3A_RG_XTAL_EXT_EN_U3, P3A_RG_XTAL_EXT_EN_U3_VAL(2)); + + clrsetbits_le32(u3_banks->phya + U3P_U3_PHYA_REG9, + P3A_RG_RX_DAC_MUX, P3A_RG_RX_DAC_MUX_VAL(4)); + + clrsetbits_le32(u3_banks->phya + U3P_U3_PHYA_REG6, + P3A_RG_TX_EIDLE_CM, P3A_RG_TX_EIDLE_CM_VAL(0xe)); + + clrsetbits_le32(u3_banks->phyd + U3P_U3_PHYD_CDR1, + P3D_RG_CDR_BIR_LTD0 | P3D_RG_CDR_BIR_LTD1, + P3D_RG_CDR_BIR_LTD0_VAL(0xc) | + P3D_RG_CDR_BIR_LTD1_VAL(0x3)); + + clrsetbits_le32(u3_banks->phyd + U3P_U3_PHYD_LFPS1, + P3D_RG_FWAKE_TH, P3D_RG_FWAKE_TH_VAL(0x34)); + + clrsetbits_le32(u3_banks->phyd + U3P_U3_PHYD_RXDET1, + P3D_RG_RXDET_STB2_SET, P3D_RG_RXDET_STB2_SET_VAL(0x10)); + + clrsetbits_le32(u3_banks->phyd + U3P_U3_PHYD_RXDET2, + P3D_RG_RXDET_STB2_SET_P3, + P3D_RG_RXDET_STB2_SET_P3_VAL(0x10)); + + dev_dbg(tphy->dev, "%s(%d)\n", __func__, instance->index); +} + static void pcie_phy_instance_init(struct mtk_tphy *tphy, struct mtk_phy_instance *instance) { struct u3phy_banks *u3_banks = &instance->u3_banks; + if (tphy->version != MTK_TPHY_V1) + return; + clrsetbits_le32(u3_banks->phya + U3P_U3_PHYA_DA_REG0, P3A_RG_XTAL_EXT_PE1H | P3A_RG_XTAL_EXT_PE2H, P3A_RG_XTAL_EXT_PE1H_VAL(0x2) | @@ -187,9 +396,16 @@ static void pcie_phy_instance_power_off(struct mtk_tphy *tphy, static void phy_v1_banks_init(struct mtk_tphy *tphy, struct mtk_phy_instance *instance) { + struct u2phy_banks *u2_banks = &instance->u2_banks; struct u3phy_banks *u3_banks = &instance->u3_banks; switch (instance->type) { + case PHY_TYPE_USB2: + u2_banks->misc = NULL; + u2_banks->fmreg = tphy->sif_base + SSUSB_SIFSLV_V1_U2FREQ; + u2_banks->com = instance->port_base + SSUSB_SIFSLV_V1_U2PHY_COM; + break; + case PHY_TYPE_USB3: case PHY_TYPE_PCIE: u3_banks->spllc = tphy->sif_base + SSUSB_SIFSLV_V1_SPLLC; u3_banks->chip = tphy->sif_base + SSUSB_SIFSLV_V1_CHIP; @@ -197,6 +413,32 @@ static void phy_v1_banks_init(struct mtk_tphy *tphy, u3_banks->phya = instance->port_base + SSUSB_SIFSLV_V1_U3PHYA; break; default: + dev_err(tphy->dev, "incompatible PHY type\n"); + return; + } +} + +static void phy_v2_banks_init(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance) +{ + struct u2phy_banks *u2_banks = &instance->u2_banks; + struct u3phy_banks *u3_banks = &instance->u3_banks; + + switch (instance->type) { + case PHY_TYPE_USB2: + u2_banks->misc = instance->port_base + SSUSB_SIFSLV_V2_MISC; + u2_banks->fmreg = instance->port_base + SSUSB_SIFSLV_V2_U2FREQ; + u2_banks->com = instance->port_base + SSUSB_SIFSLV_V2_U2PHY_COM; + break; + case PHY_TYPE_USB3: + case PHY_TYPE_PCIE: + u3_banks->spllc = instance->port_base + SSUSB_SIFSLV_V2_SPLLC; + u3_banks->chip = instance->port_base + SSUSB_SIFSLV_V2_CHIP; + u3_banks->phyd = instance->port_base + SSUSB_SIFSLV_V2_U3PHYD; + u3_banks->phya = instance->port_base + SSUSB_SIFSLV_V2_U3PHYA; + break; + default: + dev_err(tphy->dev, "incompatible PHY type\n"); return; } } @@ -208,14 +450,30 @@ static int mtk_phy_init(struct phy *phy) int ret; ret = clk_enable(&instance->ref_clk); - if (ret) + if (ret < 0) { + dev_err(tphy->dev, "failed to enable ref_clk\n"); + return ret; + } + + ret = clk_enable(&instance->da_ref_clk); + if (ret < 0) { + dev_err(tphy->dev, "failed to enable da_ref_clk %d\n", ret); + clk_disable(&instance->ref_clk); return ret; + } switch (instance->type) { + case PHY_TYPE_USB2: + u2_phy_instance_init(tphy, instance); + break; + case PHY_TYPE_USB3: + u3_phy_instance_init(tphy, instance); + break; case PHY_TYPE_PCIE: pcie_phy_instance_init(tphy, instance); break; default: + dev_err(tphy->dev, "incompatible PHY type\n"); return -EINVAL; } @@ -227,7 +485,10 @@ static int mtk_phy_power_on(struct phy *phy) struct mtk_tphy *tphy = dev_get_priv(phy->dev); struct mtk_phy_instance *instance = tphy->phys[phy->id]; - pcie_phy_instance_power_on(tphy, instance); + if (instance->type == PHY_TYPE_USB2) + u2_phy_instance_power_on(tphy, instance); + else if (instance->type == PHY_TYPE_PCIE) + pcie_phy_instance_power_on(tphy, instance); return 0; } @@ -237,7 +498,10 @@ static int mtk_phy_power_off(struct phy *phy) struct mtk_tphy *tphy = dev_get_priv(phy->dev); struct mtk_phy_instance *instance = tphy->phys[phy->id]; - pcie_phy_instance_power_off(tphy, instance); + if (instance->type == PHY_TYPE_USB2) + u2_phy_instance_power_off(tphy, instance); + else if (instance->type == PHY_TYPE_PCIE) + pcie_phy_instance_power_off(tphy, instance); return 0; } @@ -247,6 +511,7 @@ static int mtk_phy_exit(struct phy *phy) struct mtk_tphy *tphy = dev_get_priv(phy->dev); struct mtk_phy_instance *instance = tphy->phys[phy->id]; + clk_disable(&instance->da_ref_clk); clk_disable(&instance->ref_clk); return 0; @@ -285,13 +550,19 @@ static int mtk_phy_xlate(struct phy *phy, instance->type = args->args[1]; if (!(instance->type == PHY_TYPE_USB2 || instance->type == PHY_TYPE_USB3 || - instance->type == PHY_TYPE_PCIE || - instance->type == PHY_TYPE_SATA)) { + instance->type == PHY_TYPE_PCIE)) { dev_err(phy->dev, "unsupported device type\n"); return -EINVAL; } - phy_v1_banks_init(tphy, instance); + if (tphy->version == MTK_TPHY_V1) { + phy_v1_banks_init(tphy, instance); + } else if (tphy->version == MTK_TPHY_V2) { + phy_v2_banks_init(tphy, instance); + } else { + dev_err(phy->dev, "phy version is not supported\n"); + return -EINVAL; + } return 0; } @@ -310,17 +581,22 @@ static int mtk_tphy_probe(struct udevice *dev) ofnode subnode; int index = 0; - dev_for_each_subnode(subnode, dev) - tphy->nphys++; + tphy->nphys = dev_get_child_count(dev); tphy->phys = devm_kcalloc(dev, tphy->nphys, sizeof(*tphy->phys), GFP_KERNEL); if (!tphy->phys) return -ENOMEM; - tphy->sif_base = dev_read_addr_ptr(dev); - if (!tphy->sif_base) - return -ENOENT; + tphy->dev = dev; + tphy->version = dev_get_driver_data(dev); + + /* v1 has shared banks */ + if (tphy->version == MTK_TPHY_V1) { + tphy->sif_base = dev_read_addr_ptr(dev); + if (!tphy->sif_base) + return -ENOENT; + } dev_for_each_subnode(subnode, dev) { struct mtk_phy_instance *instance; @@ -345,13 +621,19 @@ static int mtk_tphy_probe(struct udevice *dev) &instance->ref_clk); if (err) return err; + + err = clk_get_optional_nodev(subnode, "da_ref", + &instance->da_ref_clk); + if (err) + return err; } return 0; } static const struct udevice_id mtk_tphy_id_table[] = { - { .compatible = "mediatek,generic-tphy-v1", }, + { .compatible = "mediatek,generic-tphy-v1", .data = MTK_TPHY_V1, }, + { .compatible = "mediatek,generic-tphy-v2", .data = MTK_TPHY_V2, }, { } }; diff --git a/drivers/phy/phy-uclass.c b/drivers/phy/phy-uclass.c index e463b0b400..6ab78448af 100644 --- a/drivers/phy/phy-uclass.c +++ b/drivers/phy/phy-uclass.c @@ -6,6 +6,7 @@ #include <common.h> #include <dm.h> +#include <dm/devres.h> #include <generic-phy.h> static inline struct phy_ops *phy_dev_ops(struct udevice *dev) @@ -167,6 +168,102 @@ int generic_phy_power_off(struct phy *phy) return ops->power_off ? ops->power_off(phy) : 0; } +int generic_phy_get_bulk(struct udevice *dev, struct phy_bulk *bulk) +{ + int i, ret, count; + + bulk->count = 0; + + /* Return if no phy declared */ + if (!dev_read_prop(dev, "phys", NULL)) + return 0; + + count = dev_count_phandle_with_args(dev, "phys", "#phy-cells"); + if (count < 1) + return count; + + bulk->phys = devm_kcalloc(dev, count, sizeof(struct phy), GFP_KERNEL); + if (!bulk->phys) + return -ENOMEM; + + for (i = 0; i < count; i++) { + ret = generic_phy_get_by_index(dev, i, &bulk->phys[i]); + if (ret) { + pr_err("Failed to get PHY%d for %s\n", i, dev->name); + return ret; + } + bulk->count++; + } + + return 0; +} + +int generic_phy_init_bulk(struct phy_bulk *bulk) +{ + struct phy *phys = bulk->phys; + int i, ret; + + for (i = 0; i < bulk->count; i++) { + ret = generic_phy_init(&phys[i]); + if (ret) { + pr_err("Can't init PHY%d\n", i); + goto phys_init_err; + } + } + + return 0; + +phys_init_err: + for (; i > 0; i--) + generic_phy_exit(&phys[i - 1]); + + return ret; +} + +int generic_phy_exit_bulk(struct phy_bulk *bulk) +{ + struct phy *phys = bulk->phys; + int i, ret = 0; + + for (i = 0; i < bulk->count; i++) + ret |= generic_phy_exit(&phys[i]); + + return ret; +} + +int generic_phy_power_on_bulk(struct phy_bulk *bulk) +{ + struct phy *phys = bulk->phys; + int i, ret; + + for (i = 0; i < bulk->count; i++) { + ret = generic_phy_power_on(&phys[i]); + if (ret) { + pr_err("Can't power on PHY%d\n", i); + goto phys_poweron_err; + } + } + + return 0; + +phys_poweron_err: + for (; i > 0; i--) + generic_phy_power_off(&phys[i - 1]); + + return ret; +} + +int generic_phy_power_off_bulk(struct phy_bulk *bulk) +{ + struct phy *phys = bulk->phys; + int i, ret = 0; + + for (i = 0; i < bulk->count; i++) + ret |= generic_phy_power_off(&phys[i]); + + return ret; +} + UCLASS_DRIVER(phy) = { .id = UCLASS_PHY, .name = "phy", diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 4ec3f6df6a..a8982bdc09 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -838,87 +838,32 @@ MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver"); #if CONFIG_IS_ENABLED(PHY) && CONFIG_IS_ENABLED(DM_USB) -int dwc3_setup_phy(struct udevice *dev, struct phy **array, int *num_phys) +int dwc3_setup_phy(struct udevice *dev, struct phy_bulk *phys) { - int i, ret, count; - struct phy *usb_phys; - - /* Return if no phy declared */ - if (!dev_read_prop(dev, "phys", NULL)) - return 0; - count = dev_count_phandle_with_args(dev, "phys", "#phy-cells"); - if (count <= 0) - return count; - - usb_phys = devm_kcalloc(dev, count, sizeof(struct phy), - GFP_KERNEL); - if (!usb_phys) - return -ENOMEM; - - for (i = 0; i < count; i++) { - ret = generic_phy_get_by_index(dev, i, &usb_phys[i]); - if (ret && ret != -ENOENT) { - pr_err("Failed to get USB PHY%d for %s\n", - i, dev->name); - return ret; - } - } - - for (i = 0; i < count; i++) { - ret = generic_phy_init(&usb_phys[i]); - if (ret) { - pr_err("Can't init USB PHY%d for %s\n", - i, dev->name); - goto phys_init_err; - } - } - - for (i = 0; i < count; i++) { - ret = generic_phy_power_on(&usb_phys[i]); - if (ret) { - pr_err("Can't power USB PHY%d for %s\n", - i, dev->name); - goto phys_poweron_err; - } - } - - *array = usb_phys; - *num_phys = count; - return 0; - -phys_poweron_err: - for (i = count - 1; i >= 0; i--) - generic_phy_power_off(&usb_phys[i]); + int ret; - for (i = 0; i < count; i++) - generic_phy_exit(&usb_phys[i]); + ret = generic_phy_get_bulk(dev, phys); + if (ret) + return ret; - return ret; + ret = generic_phy_init_bulk(phys); + if (ret) + return ret; -phys_init_err: - for (; i >= 0; i--) - generic_phy_exit(&usb_phys[i]); + ret = generic_phy_power_on_bulk(phys); + if (ret) + generic_phy_exit_bulk(phys); return ret; } -int dwc3_shutdown_phy(struct udevice *dev, struct phy *usb_phys, int num_phys) +int dwc3_shutdown_phy(struct udevice *dev, struct phy_bulk *phys) { - int i, ret; - - for (i = 0; i < num_phys; i++) { - if (!generic_phy_valid(&usb_phys[i])) - continue; - - ret = generic_phy_power_off(&usb_phys[i]); - ret |= generic_phy_exit(&usb_phys[i]); - if (ret) { - pr_err("Can't shutdown USB PHY%d for %s\n", - i, dev->name); - } - } + int ret; - return 0; + ret = generic_phy_power_off_bulk(phys); + ret |= generic_phy_exit_bulk(phys); + return ret; } #endif diff --git a/drivers/usb/dwc3/dwc3-generic.c b/drivers/usb/dwc3/dwc3-generic.c index febcfc0f54..eabd53a36d 100644 --- a/drivers/usb/dwc3/dwc3-generic.c +++ b/drivers/usb/dwc3/dwc3-generic.c @@ -33,8 +33,7 @@ struct dwc3_generic_plat { struct dwc3_generic_priv { void *base; struct dwc3 dwc3; - struct phy *phys; - int num_phys; + struct phy_bulk phys; }; struct dwc3_generic_host_priv { @@ -56,7 +55,7 @@ static int dwc3_generic_probe(struct udevice *dev, dwc3_of_parse(dwc3); #endif - rc = dwc3_setup_phy(dev, &priv->phys, &priv->num_phys); + rc = dwc3_setup_phy(dev, &priv->phys); if (rc) return rc; @@ -79,7 +78,7 @@ static int dwc3_generic_remove(struct udevice *dev, struct dwc3 *dwc3 = &priv->dwc3; dwc3_remove(dwc3); - dwc3_shutdown_phy(dev, priv->phys, priv->num_phys); + dwc3_shutdown_phy(dev, &priv->phys); unmap_physmem(dwc3->regs, MAP_NOCACHE); return 0; diff --git a/drivers/usb/gadget/dwc2_udc_otg.c b/drivers/usb/gadget/dwc2_udc_otg.c index b9c814cf73..9f21af2923 100644 --- a/drivers/usb/gadget/dwc2_udc_otg.c +++ b/drivers/usb/gadget/dwc2_udc_otg.c @@ -943,8 +943,7 @@ int usb_gadget_handle_interrupts(int index) struct dwc2_priv_data { struct clk_bulk clks; struct reset_ctl_bulk resets; - struct phy *phys; - int num_phys; + struct phy_bulk phys; struct udevice *usb33d_supply; }; @@ -953,87 +952,29 @@ int dm_usb_gadget_handle_interrupts(struct udevice *dev) return dwc2_udc_handle_interrupt(); } -int dwc2_phy_setup(struct udevice *dev, struct phy **array, int *num_phys) +static int dwc2_phy_setup(struct udevice *dev, struct phy_bulk *phys) { - int i, ret, count; - struct phy *usb_phys; - - /* Return if no phy declared */ - if (!dev_read_prop(dev, "phys", NULL)) - return 0; - - count = dev_count_phandle_with_args(dev, "phys", "#phy-cells"); - if (count <= 0) - return count; - - usb_phys = devm_kcalloc(dev, count, sizeof(struct phy), - GFP_KERNEL); - if (!usb_phys) - return -ENOMEM; - - for (i = 0; i < count; i++) { - ret = generic_phy_get_by_index(dev, i, &usb_phys[i]); - if (ret && ret != -ENOENT) { - dev_err(dev, "Failed to get USB PHY%d for %s\n", - i, dev->name); - return ret; - } - } - - for (i = 0; i < count; i++) { - ret = generic_phy_init(&usb_phys[i]); - if (ret) { - dev_err(dev, "Can't init USB PHY%d for %s\n", - i, dev->name); - goto phys_init_err; - } - } - - for (i = 0; i < count; i++) { - ret = generic_phy_power_on(&usb_phys[i]); - if (ret) { - dev_err(dev, "Can't power USB PHY%d for %s\n", - i, dev->name); - goto phys_poweron_err; - } - } - - *array = usb_phys; - *num_phys = count; - - return 0; - -phys_poweron_err: - for (i = count - 1; i >= 0; i--) - generic_phy_power_off(&usb_phys[i]); + int ret; - for (i = 0; i < count; i++) - generic_phy_exit(&usb_phys[i]); + ret = generic_phy_get_bulk(dev, phys); + if (ret) + return ret; - return ret; + ret = generic_phy_init_bulk(phys); + if (ret) + return ret; -phys_init_err: - for (; i >= 0; i--) - generic_phy_exit(&usb_phys[i]); + ret = generic_phy_power_on_bulk(phys); + if (ret) + generic_phy_exit_bulk(phys); return ret; } -void dwc2_phy_shutdown(struct udevice *dev, struct phy *usb_phys, int num_phys) +static void dwc2_phy_shutdown(struct udevice *dev, struct phy_bulk *phys) { - int i, ret; - - for (i = 0; i < num_phys; i++) { - if (!generic_phy_valid(&usb_phys[i])) - continue; - - ret = generic_phy_power_off(&usb_phys[i]); - ret |= generic_phy_exit(&usb_phys[i]); - if (ret) { - dev_err(dev, "Can't shutdown USB PHY%d for %s\n", - i, dev->name); - } - } + generic_phy_power_off_bulk(phys); + generic_phy_exit_bulk(phys); } static int dwc2_udc_otg_ofdata_to_platdata(struct udevice *dev) @@ -1158,7 +1099,7 @@ static int dwc2_udc_otg_probe(struct udevice *dev) if (ret) return ret; - ret = dwc2_phy_setup(dev, &priv->phys, &priv->num_phys); + ret = dwc2_phy_setup(dev, &priv->phys); if (ret) return ret; @@ -1208,7 +1149,7 @@ static int dwc2_udc_otg_remove(struct udevice *dev) clk_release_bulk(&priv->clks); - dwc2_phy_shutdown(dev, priv->phys, priv->num_phys); + dwc2_phy_shutdown(dev, &priv->phys); return dm_scan_fdt_dev(dev); } diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 94ac969058..2f381dc958 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -30,6 +30,12 @@ config USB_XHCI_DWC3_OF_SIMPLE Support USB2/3 functionality in simple SoC integrations with USB controller based on the DesignWare USB3 IP Core. +config USB_XHCI_MTK + bool "Support for MediaTek on-chip xHCI USB controller" + depends on ARCH_MEDIATEK + help + Enables support for the on-chip xHCI controller on MediaTek SoCs. + config USB_XHCI_MVEBU bool "MVEBU USB 3.0 support" default y diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index b62f346094..e8e3b17e42 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_USB_XHCI_DWC3_OF_SIMPLE) += dwc3-of-simple.o obj-$(CONFIG_USB_XHCI_ROCKCHIP) += xhci-rockchip.o obj-$(CONFIG_USB_XHCI_EXYNOS) += xhci-exynos5.o obj-$(CONFIG_USB_XHCI_FSL) += xhci-fsl.o +obj-$(CONFIG_USB_XHCI_MTK) += xhci-mtk.o obj-$(CONFIG_USB_XHCI_MVEBU) += xhci-mvebu.o obj-$(CONFIG_USB_XHCI_OMAP) += xhci-omap.o obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o diff --git a/drivers/usb/host/xhci-dwc3.c b/drivers/usb/host/xhci-dwc3.c index 9fcfa39d4b..563db1a426 100644 --- a/drivers/usb/host/xhci-dwc3.c +++ b/drivers/usb/host/xhci-dwc3.c @@ -19,8 +19,7 @@ #include <linux/usb/otg.h> struct xhci_dwc3_platdata { - struct phy *usb_phys; - int num_phys; + struct phy_bulk *usb_phys; }; void dwc3_set_mode(struct dwc3 *dwc3_reg, u32 mode) @@ -125,7 +124,7 @@ static int xhci_dwc3_probe(struct udevice *dev) hcor = (struct xhci_hcor *)((uintptr_t)hccr + HC_LENGTH(xhci_readl(&(hccr)->cr_capbase))); - ret = dwc3_setup_phy(dev, &plat->usb_phys, &plat->num_phys); + ret = dwc3_setup_phy(dev, plat->usb_phys); if (ret && (ret != -ENOTSUPP)) return ret; @@ -168,7 +167,7 @@ static int xhci_dwc3_remove(struct udevice *dev) { struct xhci_dwc3_platdata *plat = dev_get_platdata(dev); - dwc3_shutdown_phy(dev, plat->usb_phys, plat->num_phys); + dwc3_shutdown_phy(dev, plat->usb_phys); return xhci_deregister(dev); } diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c new file mode 100644 index 0000000000..8ff71854fc --- /dev/null +++ b/drivers/usb/host/xhci-mtk.c @@ -0,0 +1,303 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2019 MediaTek, Inc. + * Authors: Chunfeng Yun <chunfeng.yun@mediatek.com> + */ + +#include <clk.h> +#include <common.h> +#include <dm.h> +#include <dm/devres.h> +#include <generic-phy.h> +#include <malloc.h> +#include <usb.h> +#include <linux/errno.h> +#include <linux/compat.h> +#include <power/regulator.h> +#include <linux/iopoll.h> +#include <usb/xhci.h> + +/* IPPC (IP Port Control) registers */ +#define IPPC_IP_PW_CTRL0 0x00 +#define CTRL0_IP_SW_RST BIT(0) + +#define IPPC_IP_PW_CTRL1 0x04 +#define CTRL1_IP_HOST_PDN BIT(0) + +#define IPPC_IP_PW_STS1 0x10 +#define STS1_IP_SLEEP_STS BIT(30) +#define STS1_U3_MAC_RST BIT(16) +#define STS1_XHCI_RST BIT(11) +#define STS1_SYS125_RST BIT(10) +#define STS1_REF_RST BIT(8) +#define STS1_SYSPLL_STABLE BIT(0) + +#define IPPC_IP_XHCI_CAP 0x24 +#define CAP_U3_PORT_NUM(p) ((p) & 0xff) +#define CAP_U2_PORT_NUM(p) (((p) >> 8) & 0xff) + +#define IPPC_U3_CTRL_0P 0x30 +#define CTRL_U3_PORT_HOST_SEL BIT(2) +#define CTRL_U3_PORT_PDN BIT(1) +#define CTRL_U3_PORT_DIS BIT(0) + +#define IPPC_U2_CTRL_0P 0x50 +#define CTRL_U2_PORT_HOST_SEL BIT(2) +#define CTRL_U2_PORT_PDN BIT(1) +#define CTRL_U2_PORT_DIS BIT(0) + +#define IPPC_U3_CTRL(p) (IPPC_U3_CTRL_0P + ((p) * 0x08)) +#define IPPC_U2_CTRL(p) (IPPC_U2_CTRL_0P + ((p) * 0x08)) + +struct mtk_xhci { + struct xhci_ctrl ctrl; /* Needs to come first in this struct! */ + struct xhci_hccr *hcd; + void __iomem *ippc; + struct udevice *dev; + struct udevice *vusb33_supply; + struct udevice *vbus_supply; + struct clk_bulk clks; + struct phy_bulk phys; + int num_u2ports; + int num_u3ports; +}; + +static int xhci_mtk_host_enable(struct mtk_xhci *mtk) +{ + u32 value; + u32 check_val; + int ret; + int i; + + /* power on host ip */ + clrbits_le32(mtk->ippc + IPPC_IP_PW_CTRL1, CTRL1_IP_HOST_PDN); + + /* power on and enable all u3 ports */ + for (i = 0; i < mtk->num_u3ports; i++) { + clrsetbits_le32(mtk->ippc + IPPC_U3_CTRL(i), + CTRL_U3_PORT_PDN | CTRL_U3_PORT_DIS, + CTRL_U3_PORT_HOST_SEL); + } + + /* power on and enable all u2 ports */ + for (i = 0; i < mtk->num_u2ports; i++) { + clrsetbits_le32(mtk->ippc + IPPC_U2_CTRL(i), + CTRL_U2_PORT_PDN | CTRL_U2_PORT_DIS, + CTRL_U2_PORT_HOST_SEL); + } + + /* + * wait for clocks to be stable, and clock domains reset to + * be inactive after power on and enable ports + */ + check_val = STS1_SYSPLL_STABLE | STS1_REF_RST | + STS1_SYS125_RST | STS1_XHCI_RST; + + if (mtk->num_u3ports) + check_val |= STS1_U3_MAC_RST; + + ret = readl_poll_timeout(mtk->ippc + IPPC_IP_PW_STS1, value, + (check_val == (value & check_val)), 20000); + if (ret) + dev_err(mtk->dev, "clocks are not stable 0x%x!\n", value); + + return ret; +} + +static int xhci_mtk_host_disable(struct mtk_xhci *mtk) +{ + int i; + + /* power down all u3 ports */ + for (i = 0; i < mtk->num_u3ports; i++) + setbits_le32(mtk->ippc + IPPC_U3_CTRL(i), CTRL_U3_PORT_PDN); + + /* power down all u2 ports */ + for (i = 0; i < mtk->num_u2ports; i++) + setbits_le32(mtk->ippc + IPPC_U2_CTRL(i), CTRL_U2_PORT_PDN); + + /* power down host ip */ + setbits_le32(mtk->ippc + IPPC_IP_PW_CTRL1, CTRL1_IP_HOST_PDN); + + return 0; +} + +static int xhci_mtk_ssusb_init(struct mtk_xhci *mtk) +{ + u32 value; + + /* reset whole ip */ + setbits_le32(mtk->ippc + IPPC_IP_PW_CTRL0, CTRL0_IP_SW_RST); + udelay(1); + clrbits_le32(mtk->ippc + IPPC_IP_PW_CTRL0, CTRL0_IP_SW_RST); + + value = readl(mtk->ippc + IPPC_IP_XHCI_CAP); + mtk->num_u3ports = CAP_U3_PORT_NUM(value); + mtk->num_u2ports = CAP_U2_PORT_NUM(value); + dev_info(mtk->dev, "u2p:%d, u3p:%d\n", + mtk->num_u2ports, mtk->num_u3ports); + + return xhci_mtk_host_enable(mtk); +} + +static int xhci_mtk_ofdata_get(struct mtk_xhci *mtk) +{ + struct udevice *dev = mtk->dev; + int ret = 0; + + mtk->hcd = devfdt_remap_addr_name(dev, "mac"); + if (!mtk->hcd) { + dev_err(dev, "failed to get xHCI base address\n"); + return -ENXIO; + } + + mtk->ippc = devfdt_remap_addr_name(dev, "ippc"); + if (!mtk->ippc) { + dev_err(dev, "failed to get IPPC base address\n"); + return -ENXIO; + } + + dev_info(dev, "hcd: 0x%p, ippc: 0x%p\n", mtk->hcd, mtk->ippc); + + ret = clk_get_bulk(dev, &mtk->clks); + if (ret) { + dev_err(dev, "failed to get clocks %d!\n", ret); + return ret; + } + + ret = device_get_supply_regulator(dev, "vusb33-supply", + &mtk->vusb33_supply); + if (ret) + debug("can't get vusb33 regulator %d!\n", ret); + + ret = device_get_supply_regulator(dev, "vbus-supply", + &mtk->vbus_supply); + if (ret) + debug("can't get vbus regulator %d!\n", ret); + + return 0; +} + +static int xhci_mtk_ldos_enable(struct mtk_xhci *mtk) +{ + int ret; + + ret = regulator_set_enable(mtk->vusb33_supply, true); + if (ret < 0 && ret != -ENOSYS) { + dev_err(mtk->dev, "failed to enable vusb33 %d!\n", ret); + return ret; + } + + ret = regulator_set_enable(mtk->vbus_supply, true); + if (ret < 0 && ret != -ENOSYS) { + dev_err(mtk->dev, "failed to enable vbus %d!\n", ret); + regulator_set_enable(mtk->vusb33_supply, false); + return ret; + } + + return 0; +} + +static void xhci_mtk_ldos_disable(struct mtk_xhci *mtk) +{ + regulator_set_enable(mtk->vbus_supply, false); + regulator_set_enable(mtk->vusb33_supply, false); +} + +static int xhci_mtk_phy_setup(struct mtk_xhci *mtk) +{ + struct udevice *dev = mtk->dev; + struct phy_bulk *phys = &mtk->phys; + int ret; + + ret = generic_phy_get_bulk(dev, phys); + if (ret) + return ret; + + ret = generic_phy_init_bulk(phys); + if (ret) + return ret; + + ret = generic_phy_power_on_bulk(phys); + if (ret) + generic_phy_exit_bulk(phys); + + return ret; +} + +static void xhci_mtk_phy_shutdown(struct mtk_xhci *mtk) +{ + generic_phy_power_off_bulk(&mtk->phys); + generic_phy_exit_bulk(&mtk->phys); +} + +static int xhci_mtk_probe(struct udevice *dev) +{ + struct mtk_xhci *mtk = dev_get_priv(dev); + struct xhci_hcor *hcor; + int ret; + + mtk->dev = dev; + ret = xhci_mtk_ofdata_get(mtk); + if (ret) + return ret; + + ret = xhci_mtk_ldos_enable(mtk); + if (ret) + goto ldos_err; + + ret = clk_enable_bulk(&mtk->clks); + if (ret) + goto clks_err; + + ret = xhci_mtk_phy_setup(mtk); + if (ret) + goto phys_err; + + ret = xhci_mtk_ssusb_init(mtk); + if (ret) + goto ssusb_init_err; + + hcor = (struct xhci_hcor *)((uintptr_t)mtk->hcd + + HC_LENGTH(xhci_readl(&mtk->hcd->cr_capbase))); + + return xhci_register(dev, mtk->hcd, hcor); + +ssusb_init_err: + xhci_mtk_phy_shutdown(mtk); +phys_err: + clk_disable_bulk(&mtk->clks); +clks_err: + xhci_mtk_ldos_disable(mtk); +ldos_err: + return ret; +} + +static int xhci_mtk_remove(struct udevice *dev) +{ + struct mtk_xhci *mtk = dev_get_priv(dev); + + xhci_deregister(dev); + xhci_mtk_host_disable(mtk); + xhci_mtk_ldos_disable(mtk); + clk_disable_bulk(&mtk->clks); + + return 0; +} + +static const struct udevice_id xhci_mtk_ids[] = { + { .compatible = "mediatek,mtk-xhci" }, + { } +}; + +U_BOOT_DRIVER(usb_xhci) = { + .name = "xhci-mtk", + .id = UCLASS_USB, + .of_match = xhci_mtk_ids, + .probe = xhci_mtk_probe, + .remove = xhci_mtk_remove, + .ops = &xhci_usb_ops, + .bind = dm_scan_fdt_dev, + .priv_auto_alloc_size = sizeof(struct mtk_xhci), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 40dee2e6d9..c370eb6394 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -610,6 +610,16 @@ static int xhci_set_configuration(struct usb_device *udev) ep_ctx[ep_index]->tx_info = cpu_to_le32(EP_MAX_ESIT_PAYLOAD_LO(max_esit_payload) | EP_AVG_TRB_LENGTH(avg_trb_len)); + + /* + * The MediaTek xHCI defines some extra SW parameters which + * are put into reserved DWs in Slot and Endpoint Contexts + * for synchronous endpoints. + */ + if (IS_ENABLED(CONFIG_USB_XHCI_MTK)) { + ep_ctx[ep_index]->reserved[0] = + cpu_to_le32(EP_BPKTS(1) | EP_BBM(1)); + } } return xhci_configure_endpoints(udev, false); diff --git a/include/dm/ofnode.h b/include/dm/ofnode.h index 618fc10390..a0d3df7786 100644 --- a/include/dm/ofnode.h +++ b/include/dm/ofnode.h @@ -880,6 +880,14 @@ ofnode ofnode_by_prop_value(ofnode from, const char *propname, node = ofnode_next_subnode(node)) /** + * ofnode_get_child_count() - get the child count of a ofnode + * + * @node: valid node to get its child count + * @return the number of subnodes + */ +int ofnode_get_child_count(ofnode parent); + +/** * ofnode_translate_address() - Translate a device-tree address * * Translate an address from the device-tree into a CPU physical address. This diff --git a/include/dm/read.h b/include/dm/read.h index 03c15b8550..b952551d55 100644 --- a/include/dm/read.h +++ b/include/dm/read.h @@ -669,6 +669,14 @@ u64 dev_translate_dma_address(const struct udevice *dev, */ int dev_read_alias_highest_id(const char *stem); +/** + * dev_get_child_count() - get the child count of a device + * + * @dev: device to use for interation (struct udevice *) + * @return the count of child subnode + */ +int dev_get_child_count(const struct udevice *dev); + #else /* CONFIG_DM_DEV_READ_INLINE is enabled */ static inline int dev_read_u32(const struct udevice *dev, @@ -978,6 +986,11 @@ static inline int dev_read_alias_highest_id(const char *stem) return fdtdec_get_alias_highest_id(gd->fdt_blob, stem); } +static inline int dev_get_child_count(const struct udevice *dev) +{ + return ofnode_get_child_count(dev_ofnode(dev)); +} + #endif /* CONFIG_DM_DEV_READ_INLINE */ /** diff --git a/include/dwc3-uboot.h b/include/dwc3-uboot.h index 3c9e204cf0..ce835fd1b2 100644 --- a/include/dwc3-uboot.h +++ b/include/dwc3-uboot.h @@ -9,6 +9,7 @@ #ifndef __DWC3_UBOOT_H_ #define __DWC3_UBOOT_H_ +#include <generic-phy.h> #include <linux/usb/otg.h> #include <linux/usb/phy.h> @@ -43,17 +44,15 @@ void dwc3_uboot_handle_interrupt(int index); struct phy; #if CONFIG_IS_ENABLED(PHY) && CONFIG_IS_ENABLED(DM_USB) -int dwc3_setup_phy(struct udevice *dev, struct phy **array, int *num_phys); -int dwc3_shutdown_phy(struct udevice *dev, struct phy *usb_phys, int num_phys); +int dwc3_setup_phy(struct udevice *dev, struct phy_bulk *phys); +int dwc3_shutdown_phy(struct udevice *dev, struct phy_bulk *phys); #else -static inline int dwc3_setup_phy(struct udevice *dev, struct phy **array, - int *num_phys) +static inline int dwc3_setup_phy(struct udevice *dev, struct phy_bulk *phys) { return -ENOTSUPP; } -static inline int dwc3_shutdown_phy(struct udevice *dev, struct phy *usb_phys, - int num_phys) +static inline int dwc3_shutdown_phy(struct udevice *dev, struct phy_bulk *phys) { return -ENOTSUPP; } diff --git a/include/generic-phy.h b/include/generic-phy.h index 73537025c2..55629ae0b4 100644 --- a/include/generic-phy.h +++ b/include/generic-phy.h @@ -124,6 +124,23 @@ struct phy_ops { int (*power_off)(struct phy *phy); }; +/** + * struct phy_bulk - A handle to (allowing control of) a bulk of phys. + * + * Consumers provide storage for the phy bulk. The content of the structure is + * managed solely by the phy API. A phy bulk struct is initialized + * by "get"ing the phy bulk struct. + * The phy bulk struct is passed to all other bulk phy APIs to apply + * the API to all the phy in the bulk struct. + * + * @phys: An array of phy handles. + * @count: The number of phy handles in the phys array. + */ +struct phy_bulk { + struct phy *phys; + unsigned int count; +}; + #ifdef CONFIG_PHY /** @@ -250,6 +267,55 @@ int generic_phy_get_by_node(ofnode node, int index, struct phy *phy); int generic_phy_get_by_name(struct udevice *user, const char *phy_name, struct phy *phy); +/** + * generic_phy_get_bulk - Get all phys of a device. + * + * This looks up and gets all phys of the consumer device; each device is + * assumed to have n phys associated with it somehow, and this function finds + * and gets all of them in a separate structure. + * + * @dev: The consumer device. + * @bulk A pointer to a phy bulk struct to initialize. + * @return 0 if OK, or a negative error code. + */ +int generic_phy_get_bulk(struct udevice *dev, struct phy_bulk *bulk); + +/** + * generic_phy_init_bulk() - Initialize all phys in a phy bulk struct. + * + * @bulk: A phy bulk struct that was previously successfully requested + * by generic_phy_get_bulk(). + * @return 0 if OK, or negative error code. + */ +int generic_phy_init_bulk(struct phy_bulk *bulk); + +/** + * generic_phy_exit_bulk() - de-initialize all phys in a phy bulk struct. + * + * @bulk: A phy bulk struct that was previously successfully requested + * by generic_phy_get_bulk(). + * @return 0 if OK, or negative error code. + */ +int generic_phy_exit_bulk(struct phy_bulk *bulk); + +/** + * generic_phy_power_on_bulk() - Power on all phys in a phy bulk struct. + * + * @bulk: A phy bulk struct that was previously successfully requested + * by generic_phy_get_bulk(). + * @return 0 if OK, or negative error code. + */ +int generic_phy_power_on_bulk(struct phy_bulk *bulk); + +/** + * generic_phy_power_off_bulk() - Power off all phys in a phy bulk struct. + * + * @bulk: A phy bulk struct that was previously successfully requested + * by generic_phy_get_bulk(). + * @return 0 if OK, or negative error code. + */ +int generic_phy_power_off_bulk(struct phy_bulk *bulk); + #else /* CONFIG_PHY */ static inline int generic_phy_init(struct phy *phy) @@ -289,6 +355,32 @@ static inline int generic_phy_get_by_name(struct udevice *user, const char *phy_ return 0; } +static inline int +generic_phy_get_bulk(struct udevice *dev, struct phy_bulk *bulk) +{ + return 0; +} + +static inline int generic_phy_init_bulk(struct phy_bulk *bulk) +{ + return 0; +} + +static inline int generic_phy_exit_bulk(struct phy_bulk *bulk) +{ + return 0; +} + +static inline int generic_phy_power_on_bulk(struct phy_bulk *bulk) +{ + return 0; +} + +static inline int generic_phy_power_off_bulk(struct phy_bulk *bulk) +{ + return 0; +} + #endif /* CONFIG_PHY */ /** diff --git a/include/usb/xhci.h b/include/usb/xhci.h index 6017504488..20e4a21066 100644 --- a/include/usb/xhci.h +++ b/include/usb/xhci.h @@ -670,6 +670,9 @@ struct xhci_ep_ctx { /* deq bitmasks */ #define EP_CTX_CYCLE_MASK (1 << 0) +/* reserved[0] bitmasks, MediaTek xHCI used */ +#define EP_BPKTS(p) (((p) & 0x7f) << 0) +#define EP_BBM(p) (((p) & 0x1) << 11) /** * struct xhci_input_control_context diff --git a/test/dm/ofnode.c b/test/dm/ofnode.c index 1c49eaf38b..07d5c7d7a6 100644 --- a/test/dm/ofnode.c +++ b/test/dm/ofnode.c @@ -113,3 +113,24 @@ static int dm_test_ofnode_read_chosen(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_ofnode_read_chosen, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +static int dm_test_ofnode_get_child_count(struct unit_test_state *uts) +{ + ofnode node, child_node; + u32 val; + + node = ofnode_path("/i-test"); + ut_assert(ofnode_valid(node)); + + val = ofnode_get_child_count(node); + ut_asserteq(3, val); + + child_node = ofnode_first_subnode(node); + ut_assert(ofnode_valid(child_node)); + val = ofnode_get_child_count(child_node); + ut_asserteq(0, val); + + return 0; +} +DM_TEST(dm_test_ofnode_get_child_count, + DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); diff --git a/test/dm/phy.c b/test/dm/phy.c index 21d92194b9..92455d94af 100644 --- a/test/dm/phy.c +++ b/test/dm/phy.c @@ -110,3 +110,36 @@ static int dm_test_phy_ops(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_phy_ops, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +static int dm_test_phy_bulk(struct unit_test_state *uts) +{ + struct phy_bulk phys; + struct udevice *parent; + + /* test normal operations */ + ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS, + "gen_phy_user1", &parent)); + + ut_assertok(generic_phy_get_bulk(parent, &phys)); + ut_asserteq(2, phys.count); + + ut_asserteq(0, generic_phy_init_bulk(&phys)); + ut_asserteq(0, generic_phy_power_on_bulk(&phys)); + ut_asserteq(0, generic_phy_power_off_bulk(&phys)); + ut_asserteq(0, generic_phy_exit_bulk(&phys)); + + /* has a known problem phy */ + ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS, + "gen_phy_user", &parent)); + + ut_assertok(generic_phy_get_bulk(parent, &phys)); + ut_asserteq(3, phys.count); + + ut_asserteq(0, generic_phy_init_bulk(&phys)); + ut_asserteq(-EIO, generic_phy_power_on_bulk(&phys)); + ut_asserteq(-EIO, generic_phy_power_off_bulk(&phys)); + ut_asserteq(0, generic_phy_exit_bulk(&phys)); + + return 0; +} +DM_TEST(dm_test_phy_bulk, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); |