Chunfeng Yun | e09b88c | 2020-10-16 11:38:39 +0800 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* |
| 3 | * mtu3_dr.c - dual role switch and host glue layer |
| 4 | * |
| 5 | * Copyright (C) 2016 MediaTek Inc. |
| 6 | * |
| 7 | * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> |
| 8 | */ |
| 9 | |
| 10 | #include <dm/lists.h> |
| 11 | #include <linux/iopoll.h> |
| 12 | |
| 13 | #include "mtu3.h" |
| 14 | #include "mtu3_dr.h" |
| 15 | |
| 16 | static void host_ports_num_get(struct mtu3_host *u3h) |
| 17 | { |
| 18 | u32 xhci_cap; |
| 19 | |
| 20 | xhci_cap = mtu3_readl(u3h->ippc_base, U3D_SSUSB_IP_XHCI_CAP); |
| 21 | u3h->u2_ports = SSUSB_IP_XHCI_U2_PORT_NUM(xhci_cap); |
| 22 | u3h->u3_ports = SSUSB_IP_XHCI_U3_PORT_NUM(xhci_cap); |
| 23 | |
| 24 | dev_dbg(u3h->dev, "host - u2_ports:%d, u3_ports:%d\n", |
| 25 | u3h->u2_ports, u3h->u3_ports); |
| 26 | } |
| 27 | |
| 28 | /* only configure ports will be used later */ |
| 29 | static int ssusb_host_enable(struct mtu3_host *u3h) |
| 30 | { |
| 31 | void __iomem *ibase = u3h->ippc_base; |
| 32 | int num_u3p = u3h->u3_ports; |
| 33 | int num_u2p = u3h->u2_ports; |
| 34 | int u3_ports_disabed; |
| 35 | u32 check_clk; |
| 36 | u32 value; |
| 37 | int i; |
| 38 | |
| 39 | /* power on host ip */ |
| 40 | mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN); |
| 41 | |
| 42 | /* power on and enable u3 ports except skipped ones */ |
| 43 | u3_ports_disabed = 0; |
| 44 | for (i = 0; i < num_u3p; i++) { |
| 45 | if ((0x1 << i) & u3h->u3p_dis_msk) { |
| 46 | u3_ports_disabed++; |
| 47 | continue; |
| 48 | } |
| 49 | |
| 50 | value = mtu3_readl(ibase, SSUSB_U3_CTRL(i)); |
| 51 | value &= ~(SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS); |
| 52 | value |= SSUSB_U3_PORT_HOST_SEL; |
| 53 | mtu3_writel(ibase, SSUSB_U3_CTRL(i), value); |
| 54 | } |
| 55 | |
| 56 | /* power on and enable all u2 ports */ |
| 57 | for (i = 0; i < num_u2p; i++) { |
| 58 | value = mtu3_readl(ibase, SSUSB_U2_CTRL(i)); |
| 59 | value &= ~(SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS); |
| 60 | value |= SSUSB_U2_PORT_HOST_SEL; |
| 61 | mtu3_writel(ibase, SSUSB_U2_CTRL(i), value); |
| 62 | } |
| 63 | |
| 64 | check_clk = SSUSB_XHCI_RST_B_STS; |
| 65 | if (num_u3p > u3_ports_disabed) |
| 66 | check_clk = SSUSB_U3_MAC_RST_B_STS; |
| 67 | |
| 68 | return ssusb_check_clocks(u3h->ssusb, check_clk); |
| 69 | } |
| 70 | |
| 71 | static void ssusb_host_disable(struct mtu3_host *u3h) |
| 72 | { |
| 73 | void __iomem *ibase = u3h->ippc_base; |
| 74 | int num_u3p = u3h->u3_ports; |
| 75 | int num_u2p = u3h->u2_ports; |
| 76 | u32 value; |
| 77 | int i; |
| 78 | |
| 79 | /* power down and disable u3 ports except skipped ones */ |
| 80 | for (i = 0; i < num_u3p; i++) { |
| 81 | if ((0x1 << i) & u3h->u3p_dis_msk) |
| 82 | continue; |
| 83 | |
| 84 | value = mtu3_readl(ibase, SSUSB_U3_CTRL(i)); |
| 85 | value |= SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS; |
| 86 | mtu3_writel(ibase, SSUSB_U3_CTRL(i), value); |
| 87 | } |
| 88 | |
| 89 | /* power down and disable all u2 ports */ |
| 90 | for (i = 0; i < num_u2p; i++) { |
| 91 | value = mtu3_readl(ibase, SSUSB_U2_CTRL(i)); |
| 92 | value |= SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS; |
| 93 | mtu3_writel(ibase, SSUSB_U2_CTRL(i), value); |
| 94 | } |
| 95 | |
| 96 | /* power down host ip */ |
| 97 | mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN); |
| 98 | } |
| 99 | |
| 100 | /* |
| 101 | * If host supports multiple ports, the VBUSes(5V) of ports except port0 |
| 102 | * which supports OTG are better to be enabled by default in DTS. |
| 103 | * Because the host driver will keep link with devices attached when system |
| 104 | * enters suspend mode, so no need to control VBUSes after initialization. |
| 105 | */ |
| 106 | int ssusb_host_init(struct ssusb_mtk *ssusb) |
| 107 | { |
| 108 | struct mtu3_host *u3h = ssusb->u3h; |
| 109 | struct udevice *dev = u3h->dev; |
| 110 | int ret; |
| 111 | |
| 112 | u3h->ssusb = ssusb; |
| 113 | u3h->hcd = ssusb->mac_base; |
| 114 | u3h->ippc_base = ssusb->ippc_base; |
| 115 | |
| 116 | /* optional property, ignore the error */ |
| 117 | dev_read_u32(dev, "mediatek,u3p-dis-msk", &u3h->u3p_dis_msk); |
| 118 | |
| 119 | host_ports_num_get(u3h); |
| 120 | ret = ssusb_host_enable(u3h); |
| 121 | if (ret) |
| 122 | return ret; |
| 123 | |
| 124 | ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST); |
| 125 | |
| 126 | ret = regulator_set_enable(ssusb->vbus_supply, true); |
| 127 | if (ret < 0 && ret != -ENOSYS) { |
| 128 | dev_err(dev, "failed to enable vbus %d!\n", ret); |
| 129 | return ret; |
| 130 | } |
| 131 | |
| 132 | dev_info(dev, "%s done...\n", __func__); |
| 133 | |
| 134 | return 0; |
| 135 | } |
| 136 | |
| 137 | void ssusb_host_exit(struct ssusb_mtk *ssusb) |
| 138 | { |
| 139 | regulator_set_enable(ssusb->vbus_supply, false); |
| 140 | ssusb_host_disable(ssusb->u3h); |
| 141 | } |