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 "mtu3.h" 11 #include "mtu3_dr.h" 12 #include "mtu3_debug.h" 13 14 #define USB2_PORT 2 15 #define USB3_PORT 3 16 17 enum mtu3_vbus_id_state { 18 MTU3_ID_FLOAT = 1, 19 MTU3_ID_GROUND, 20 MTU3_VBUS_OFF, 21 MTU3_VBUS_VALID, 22 }; 23 24 static char *mailbox_state_string(enum mtu3_vbus_id_state state) 25 { 26 switch (state) { 27 case MTU3_ID_FLOAT: 28 return "ID_FLOAT"; 29 case MTU3_ID_GROUND: 30 return "ID_GROUND"; 31 case MTU3_VBUS_OFF: 32 return "VBUS_OFF"; 33 case MTU3_VBUS_VALID: 34 return "VBUS_VALID"; 35 default: 36 return "UNKNOWN"; 37 } 38 } 39 40 static void toggle_opstate(struct ssusb_mtk *ssusb) 41 { 42 if (!ssusb->otg_switch.is_u3_drd) { 43 mtu3_setbits(ssusb->mac_base, U3D_DEVICE_CONTROL, DC_SESSION); 44 mtu3_setbits(ssusb->mac_base, U3D_POWER_MANAGEMENT, SOFT_CONN); 45 } 46 } 47 48 /* only port0 supports dual-role mode */ 49 static int ssusb_port0_switch(struct ssusb_mtk *ssusb, 50 int version, bool tohost) 51 { 52 void __iomem *ibase = ssusb->ippc_base; 53 u32 value; 54 55 dev_dbg(ssusb->dev, "%s (switch u%d port0 to %s)\n", __func__, 56 version, tohost ? "host" : "device"); 57 58 if (version == USB2_PORT) { 59 /* 1. power off and disable u2 port0 */ 60 value = mtu3_readl(ibase, SSUSB_U2_CTRL(0)); 61 value |= SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS; 62 mtu3_writel(ibase, SSUSB_U2_CTRL(0), value); 63 64 /* 2. power on, enable u2 port0 and select its mode */ 65 value = mtu3_readl(ibase, SSUSB_U2_CTRL(0)); 66 value &= ~(SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS); 67 value = tohost ? (value | SSUSB_U2_PORT_HOST_SEL) : 68 (value & (~SSUSB_U2_PORT_HOST_SEL)); 69 mtu3_writel(ibase, SSUSB_U2_CTRL(0), value); 70 } else { 71 /* 1. power off and disable u3 port0 */ 72 value = mtu3_readl(ibase, SSUSB_U3_CTRL(0)); 73 value |= SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS; 74 mtu3_writel(ibase, SSUSB_U3_CTRL(0), value); 75 76 /* 2. power on, enable u3 port0 and select its mode */ 77 value = mtu3_readl(ibase, SSUSB_U3_CTRL(0)); 78 value &= ~(SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS); 79 value = tohost ? (value | SSUSB_U3_PORT_HOST_SEL) : 80 (value & (~SSUSB_U3_PORT_HOST_SEL)); 81 mtu3_writel(ibase, SSUSB_U3_CTRL(0), value); 82 } 83 84 return 0; 85 } 86 87 static void switch_port_to_host(struct ssusb_mtk *ssusb) 88 { 89 u32 check_clk = 0; 90 91 dev_dbg(ssusb->dev, "%s\n", __func__); 92 93 ssusb_port0_switch(ssusb, USB2_PORT, true); 94 95 if (ssusb->otg_switch.is_u3_drd) { 96 ssusb_port0_switch(ssusb, USB3_PORT, true); 97 check_clk = SSUSB_U3_MAC_RST_B_STS; 98 } 99 100 ssusb_check_clocks(ssusb, check_clk); 101 102 /* after all clocks are stable */ 103 toggle_opstate(ssusb); 104 } 105 106 static void switch_port_to_device(struct ssusb_mtk *ssusb) 107 { 108 u32 check_clk = 0; 109 110 dev_dbg(ssusb->dev, "%s\n", __func__); 111 112 ssusb_port0_switch(ssusb, USB2_PORT, false); 113 114 if (ssusb->otg_switch.is_u3_drd) { 115 ssusb_port0_switch(ssusb, USB3_PORT, false); 116 check_clk = SSUSB_U3_MAC_RST_B_STS; 117 } 118 119 ssusb_check_clocks(ssusb, check_clk); 120 } 121 122 int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on) 123 { 124 struct ssusb_mtk *ssusb = 125 container_of(otg_sx, struct ssusb_mtk, otg_switch); 126 struct regulator *vbus = otg_sx->vbus; 127 int ret; 128 129 /* vbus is optional */ 130 if (!vbus) 131 return 0; 132 133 dev_dbg(ssusb->dev, "%s: turn %s\n", __func__, is_on ? "on" : "off"); 134 135 if (is_on) { 136 ret = regulator_enable(vbus); 137 if (ret) { 138 dev_err(ssusb->dev, "vbus regulator enable failed\n"); 139 return ret; 140 } 141 } else { 142 regulator_disable(vbus); 143 } 144 145 return 0; 146 } 147 148 /* 149 * switch to host: -> MTU3_VBUS_OFF --> MTU3_ID_GROUND 150 * switch to device: -> MTU3_ID_FLOAT --> MTU3_VBUS_VALID 151 */ 152 static void ssusb_set_mailbox(struct otg_switch_mtk *otg_sx, 153 enum mtu3_vbus_id_state status) 154 { 155 struct ssusb_mtk *ssusb = 156 container_of(otg_sx, struct ssusb_mtk, otg_switch); 157 struct mtu3 *mtu = ssusb->u3d; 158 159 dev_dbg(ssusb->dev, "mailbox %s\n", mailbox_state_string(status)); 160 mtu3_dbg_trace(ssusb->dev, "mailbox %s", mailbox_state_string(status)); 161 162 switch (status) { 163 case MTU3_ID_GROUND: 164 switch_port_to_host(ssusb); 165 ssusb_set_vbus(otg_sx, 1); 166 ssusb->is_host = true; 167 break; 168 case MTU3_ID_FLOAT: 169 ssusb->is_host = false; 170 ssusb_set_vbus(otg_sx, 0); 171 switch_port_to_device(ssusb); 172 break; 173 case MTU3_VBUS_OFF: 174 mtu3_stop(mtu); 175 pm_relax(ssusb->dev); 176 break; 177 case MTU3_VBUS_VALID: 178 /* avoid suspend when works as device */ 179 pm_stay_awake(ssusb->dev); 180 mtu3_start(mtu); 181 break; 182 default: 183 dev_err(ssusb->dev, "invalid state\n"); 184 } 185 } 186 187 static void ssusb_id_work(struct work_struct *work) 188 { 189 struct otg_switch_mtk *otg_sx = 190 container_of(work, struct otg_switch_mtk, id_work); 191 192 if (otg_sx->id_event) 193 ssusb_set_mailbox(otg_sx, MTU3_ID_GROUND); 194 else 195 ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT); 196 } 197 198 static void ssusb_vbus_work(struct work_struct *work) 199 { 200 struct otg_switch_mtk *otg_sx = 201 container_of(work, struct otg_switch_mtk, vbus_work); 202 203 if (otg_sx->vbus_event) 204 ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID); 205 else 206 ssusb_set_mailbox(otg_sx, MTU3_VBUS_OFF); 207 } 208 209 /* 210 * @ssusb_id_notifier is called in atomic context, but @ssusb_set_mailbox 211 * may sleep, so use work queue here 212 */ 213 static int ssusb_id_notifier(struct notifier_block *nb, 214 unsigned long event, void *ptr) 215 { 216 struct otg_switch_mtk *otg_sx = 217 container_of(nb, struct otg_switch_mtk, id_nb); 218 219 otg_sx->id_event = event; 220 schedule_work(&otg_sx->id_work); 221 222 return NOTIFY_DONE; 223 } 224 225 static int ssusb_vbus_notifier(struct notifier_block *nb, 226 unsigned long event, void *ptr) 227 { 228 struct otg_switch_mtk *otg_sx = 229 container_of(nb, struct otg_switch_mtk, vbus_nb); 230 231 otg_sx->vbus_event = event; 232 schedule_work(&otg_sx->vbus_work); 233 234 return NOTIFY_DONE; 235 } 236 237 static int ssusb_extcon_register(struct otg_switch_mtk *otg_sx) 238 { 239 struct ssusb_mtk *ssusb = 240 container_of(otg_sx, struct ssusb_mtk, otg_switch); 241 struct extcon_dev *edev = otg_sx->edev; 242 int ret; 243 244 /* extcon is optional */ 245 if (!edev) 246 return 0; 247 248 otg_sx->vbus_nb.notifier_call = ssusb_vbus_notifier; 249 ret = devm_extcon_register_notifier(ssusb->dev, edev, EXTCON_USB, 250 &otg_sx->vbus_nb); 251 if (ret < 0) { 252 dev_err(ssusb->dev, "failed to register notifier for USB\n"); 253 return ret; 254 } 255 256 otg_sx->id_nb.notifier_call = ssusb_id_notifier; 257 ret = devm_extcon_register_notifier(ssusb->dev, edev, EXTCON_USB_HOST, 258 &otg_sx->id_nb); 259 if (ret < 0) { 260 dev_err(ssusb->dev, "failed to register notifier for USB-HOST\n"); 261 return ret; 262 } 263 264 dev_dbg(ssusb->dev, "EXTCON_USB: %d, EXTCON_USB_HOST: %d\n", 265 extcon_get_state(edev, EXTCON_USB), 266 extcon_get_state(edev, EXTCON_USB_HOST)); 267 268 /* default as host, switch to device mode if needed */ 269 if (extcon_get_state(edev, EXTCON_USB_HOST) == false) 270 ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT); 271 if (extcon_get_state(edev, EXTCON_USB) == true) 272 ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID); 273 274 return 0; 275 } 276 277 /* 278 * We provide an interface via debugfs to switch between host and device modes 279 * depending on user input. 280 * This is useful in special cases, such as uses TYPE-A receptacle but also 281 * wants to support dual-role mode. 282 */ 283 void ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host) 284 { 285 struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; 286 287 if (to_host) { 288 ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_HOST); 289 ssusb_set_mailbox(otg_sx, MTU3_VBUS_OFF); 290 ssusb_set_mailbox(otg_sx, MTU3_ID_GROUND); 291 } else { 292 ssusb_set_force_mode(ssusb, MTU3_DR_FORCE_DEVICE); 293 ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT); 294 ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID); 295 } 296 } 297 298 void ssusb_set_force_mode(struct ssusb_mtk *ssusb, 299 enum mtu3_dr_force_mode mode) 300 { 301 u32 value; 302 303 value = mtu3_readl(ssusb->ippc_base, SSUSB_U2_CTRL(0)); 304 switch (mode) { 305 case MTU3_DR_FORCE_DEVICE: 306 value |= SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG; 307 break; 308 case MTU3_DR_FORCE_HOST: 309 value |= SSUSB_U2_PORT_FORCE_IDDIG; 310 value &= ~SSUSB_U2_PORT_RG_IDDIG; 311 break; 312 case MTU3_DR_FORCE_NONE: 313 value &= ~(SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG); 314 break; 315 default: 316 return; 317 } 318 mtu3_writel(ssusb->ippc_base, SSUSB_U2_CTRL(0), value); 319 } 320 321 int ssusb_otg_switch_init(struct ssusb_mtk *ssusb) 322 { 323 struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; 324 int ret = 0; 325 326 INIT_WORK(&otg_sx->id_work, ssusb_id_work); 327 INIT_WORK(&otg_sx->vbus_work, ssusb_vbus_work); 328 329 if (otg_sx->manual_drd_enabled) 330 ssusb_dr_debugfs_init(ssusb); 331 else 332 ret = ssusb_extcon_register(otg_sx); 333 334 return ret; 335 } 336 337 void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb) 338 { 339 struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; 340 341 cancel_work_sync(&otg_sx->id_work); 342 cancel_work_sync(&otg_sx->vbus_work); 343 } 344