Merge git://git.denx.de/u-boot-usb

- MediaTek USB host support
This commit is contained in:
Tom Rini 2020-05-05 14:53:51 -04:00
commit 1259567ae3
25 changed files with 1135 additions and 188 deletions

View 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/

View file

@ -82,6 +82,14 @@
status = "okay";
};
&xhci {
status = "okay";
};
&u3phy {
status = "okay";
};
&watchdog {
pinctrl-names = "default";
pinctrl-0 = <&watchdog_pins>;

View 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"
/ {
@ -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>;

View file

@ -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";
};

View 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.

View file

@ -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";
};

View 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)
{

View 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));
}

View file

@ -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, },
{ }
};

View 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",

View 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;
int ret;
/* 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;
ret = generic_phy_get_bulk(dev, phys);
if (ret)
return ret;
usb_phys = devm_kcalloc(dev, count, sizeof(struct phy),
GFP_KERNEL);
if (!usb_phys)
return -ENOMEM;
ret = generic_phy_init_bulk(phys);
if (ret)
return ret;
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]);
for (i = 0; i < count; i++)
generic_phy_exit(&usb_phys[i]);
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;
int 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);
}
}
return 0;
ret = generic_phy_power_off_bulk(phys);
ret |= generic_phy_exit_bulk(phys);
return ret;
}
#endif

View 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;

View 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;
int ret;
/* Return if no phy declared */
if (!dev_read_prop(dev, "phys", NULL))
return 0;
ret = generic_phy_get_bulk(dev, phys);
if (ret)
return ret;
count = dev_count_phandle_with_args(dev, "phys", "#phy-cells");
if (count <= 0)
return count;
ret = generic_phy_init_bulk(phys);
if (ret)
return ret;
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]);
for (i = 0; i < count; i++)
generic_phy_exit(&usb_phys[i]);
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);
}

View 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

View 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

View 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);
}

303
drivers/usb/host/xhci-mtk.c Normal file
View file

@ -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,
};

View 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);

View file

@ -879,6 +879,14 @@ ofnode ofnode_by_prop_value(ofnode from, const char *propname,
ofnode_valid(node); \
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
*

View 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 */
/**

View 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;
}

View 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 */
/**

View 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

View 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);

View 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);