Merge git://git.denx.de/u-boot-usb
authorTom Rini <trini@konsulko.com>
Tue, 5 May 2020 18:53:51 +0000 (14:53 -0400)
committerTom Rini <trini@konsulko.com>
Tue, 5 May 2020 18:53:51 +0000 (14:53 -0400)
- MediaTek USB host support

25 files changed:
MAINTAINERS
arch/arm/dts/mt7629-rfb.dts
arch/arm/dts/mt7629.dtsi
arch/sandbox/dts/test.dts
doc/device-tree-bindings/phy/phy-mtk-tphy.txt
doc/device-tree-bindings/usb/mediatek,mtk-xhci.txt [new file with mode: 0644]
drivers/core/ofnode.c
drivers/core/read.c
drivers/phy/phy-mtk-tphy.c
drivers/phy/phy-uclass.c
drivers/usb/dwc3/core.c
drivers/usb/dwc3/dwc3-generic.c
drivers/usb/gadget/dwc2_udc_otg.c
drivers/usb/host/Kconfig
drivers/usb/host/Makefile
drivers/usb/host/xhci-dwc3.c
drivers/usb/host/xhci-mtk.c [new file with mode: 0644]
drivers/usb/host/xhci.c
include/dm/ofnode.h
include/dm/read.h
include/dwc3-uboot.h
include/generic-phy.h
include/usb/xhci.h
test/dm/ofnode.c
test/dm/phy.c

index 66f0b07..ec59ce8 100644 (file)
@@ -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/
index 687fe1c..bf84f76 100644 (file)
        status = "okay";
 };
 
+&xhci {
+       status = "okay";
+};
+
+&u3phy {
+       status = "okay";
+};
+
 &watchdog {
        pinctrl-names = "default";
        pinctrl-0 = <&watchdog_pins>;
index 644d2da..6850e00 100644 (file)
@@ -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"
 
 / {
                #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>;
index 4bccfbe..15cd233 100644 (file)
                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>;
                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";
        };
index 037c5a4..8cd23d8 100644 (file)
@@ -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 (file)
index 0000000..0447468
--- /dev/null
@@ -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";
+};
index 20871a6..e3c42da 100644 (file)
@@ -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)
 {
index 47b8e03..3d421f7 100644 (file)
@@ -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));
+}
index bd089b7..81525a4 100644 (file)
 /* 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)
 #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)
 #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)
 #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, },
        { }
 };
 
index e463b0b..6ab7844 100644 (file)
@@ -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",
index 4ec3f6d..a8982bd 100644 (file)
@@ -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
 
index febcfc0..eabd53a 100644 (file)
@@ -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;
index b9c814c..9f21af2 100644 (file)
@@ -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);
 }
index 94ac969..2f381dc 100644 (file)
@@ -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
index b62f346..e8e3b17 100644 (file)
@@ -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
index 9fcfa39..563db1a 100644 (file)
@@ -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 (file)
index 0000000..8ff7185
--- /dev/null
@@ -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,
+};
index 40dee2e..c370eb6 100644 (file)
@@ -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);
index 618fc10..a0d3df7 100644 (file)
@@ -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
index 03c15b8..b952551 100644 (file)
@@ -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 */
 
 /**
index 3c9e204..ce835fd 100644 (file)
@@ -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;
 }
index 7353702..55629ae 100644 (file)
@@ -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 */
 
 /**
index 6017504..20e4a21 100644 (file)
@@ -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
index 1c49eaf..07d5c7d 100644 (file)
@@ -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);
index 21d9219..92455d9 100644 (file)
@@ -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);