diff options
Diffstat (limited to 'drivers/usb/mtu3/mtu3_host.c')
-rw-r--r-- | drivers/usb/mtu3/mtu3_host.c | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c new file mode 100644 index 0000000000..8001fc2d9b --- /dev/null +++ b/drivers/usb/mtu3/mtu3_host.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mtu3_dr.c - dual role switch and host glue layer + * + * Copyright (C) 2016 MediaTek Inc. + * + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> + */ + +#include <dm/lists.h> +#include <linux/iopoll.h> + +#include "mtu3.h" +#include "mtu3_dr.h" + +static void host_ports_num_get(struct mtu3_host *u3h) +{ + u32 xhci_cap; + + xhci_cap = mtu3_readl(u3h->ippc_base, U3D_SSUSB_IP_XHCI_CAP); + u3h->u2_ports = SSUSB_IP_XHCI_U2_PORT_NUM(xhci_cap); + u3h->u3_ports = SSUSB_IP_XHCI_U3_PORT_NUM(xhci_cap); + + dev_dbg(u3h->dev, "host - u2_ports:%d, u3_ports:%d\n", + u3h->u2_ports, u3h->u3_ports); +} + +/* only configure ports will be used later */ +static int ssusb_host_enable(struct mtu3_host *u3h) +{ + void __iomem *ibase = u3h->ippc_base; + int num_u3p = u3h->u3_ports; + int num_u2p = u3h->u2_ports; + int u3_ports_disabed; + u32 check_clk; + u32 value; + int i; + + /* power on host ip */ + mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN); + + /* power on and enable u3 ports except skipped ones */ + u3_ports_disabed = 0; + for (i = 0; i < num_u3p; i++) { + if ((0x1 << i) & u3h->u3p_dis_msk) { + u3_ports_disabed++; + continue; + } + + value = mtu3_readl(ibase, SSUSB_U3_CTRL(i)); + value &= ~(SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS); + value |= SSUSB_U3_PORT_HOST_SEL; + mtu3_writel(ibase, SSUSB_U3_CTRL(i), value); + } + + /* power on and enable all u2 ports */ + for (i = 0; i < num_u2p; i++) { + value = mtu3_readl(ibase, SSUSB_U2_CTRL(i)); + value &= ~(SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS); + value |= SSUSB_U2_PORT_HOST_SEL; + mtu3_writel(ibase, SSUSB_U2_CTRL(i), value); + } + + check_clk = SSUSB_XHCI_RST_B_STS; + if (num_u3p > u3_ports_disabed) + check_clk = SSUSB_U3_MAC_RST_B_STS; + + return ssusb_check_clocks(u3h->ssusb, check_clk); +} + +static void ssusb_host_disable(struct mtu3_host *u3h) +{ + void __iomem *ibase = u3h->ippc_base; + int num_u3p = u3h->u3_ports; + int num_u2p = u3h->u2_ports; + u32 value; + int i; + + /* power down and disable u3 ports except skipped ones */ + for (i = 0; i < num_u3p; i++) { + if ((0x1 << i) & u3h->u3p_dis_msk) + continue; + + value = mtu3_readl(ibase, SSUSB_U3_CTRL(i)); + value |= SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS; + mtu3_writel(ibase, SSUSB_U3_CTRL(i), value); + } + + /* power down and disable all u2 ports */ + for (i = 0; i < num_u2p; i++) { + value = mtu3_readl(ibase, SSUSB_U2_CTRL(i)); + value |= SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS; + mtu3_writel(ibase, SSUSB_U2_CTRL(i), value); + } + + /* power down host ip */ + mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN); +} + +/* + * If host supports multiple ports, the VBUSes(5V) of ports except port0 + * which supports OTG are better to be enabled by default in DTS. + * Because the host driver will keep link with devices attached when system + * enters suspend mode, so no need to control VBUSes after initialization. + */ +int ssusb_host_init(struct ssusb_mtk *ssusb) +{ + struct mtu3_host *u3h = ssusb->u3h; + struct udevice *dev = u3h->dev; + int ret; + + u3h->ssusb = ssusb; + u3h->hcd = ssusb->mac_base; + u3h->ippc_base = ssusb->ippc_base; + + /* optional property, ignore the error */ + dev_read_u32(dev, "mediatek,u3p-dis-msk", &u3h->u3p_dis_msk); + + host_ports_num_get(u3h); + ret = ssusb_host_enable(u3h); + if (ret) + return ret; + + ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST); + + ret = regulator_set_enable(ssusb->vbus_supply, true); + if (ret < 0 && ret != -ENOSYS) { + dev_err(dev, "failed to enable vbus %d!\n", ret); + return ret; + } + + dev_info(dev, "%s done...\n", __func__); + + return 0; +} + +void ssusb_host_exit(struct ssusb_mtk *ssusb) +{ + regulator_set_enable(ssusb->vbus_supply, false); + ssusb_host_disable(ssusb->u3h); +} |