developer | 13dc3c8 | 2020-10-16 11:38:39 +0800 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* |
| 3 | * Copyright (C) 2016 MediaTek Inc. |
| 4 | * |
| 5 | * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> |
| 6 | */ |
| 7 | |
developer | 13dc3c8 | 2020-10-16 11:38:39 +0800 | [diff] [blame] | 8 | #include <dm/lists.h> |
| 9 | #include <linux/iopoll.h> |
| 10 | |
| 11 | #include "mtu3.h" |
| 12 | #include "mtu3_dr.h" |
| 13 | |
| 14 | void ssusb_set_force_mode(struct ssusb_mtk *ssusb, |
| 15 | enum mtu3_dr_force_mode mode) |
| 16 | { |
| 17 | u32 value; |
| 18 | |
| 19 | value = mtu3_readl(ssusb->ippc_base, SSUSB_U2_CTRL(0)); |
| 20 | switch (mode) { |
| 21 | case MTU3_DR_FORCE_DEVICE: |
| 22 | value |= SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG; |
| 23 | break; |
| 24 | case MTU3_DR_FORCE_HOST: |
| 25 | value |= SSUSB_U2_PORT_FORCE_IDDIG; |
| 26 | value &= ~SSUSB_U2_PORT_RG_IDDIG; |
| 27 | break; |
| 28 | case MTU3_DR_FORCE_NONE: |
| 29 | value &= ~(SSUSB_U2_PORT_FORCE_IDDIG | SSUSB_U2_PORT_RG_IDDIG); |
| 30 | break; |
| 31 | default: |
| 32 | return; |
| 33 | } |
| 34 | mtu3_writel(ssusb->ippc_base, SSUSB_U2_CTRL(0), value); |
| 35 | } |
| 36 | |
| 37 | /* u2-port0 should be powered on and enabled; */ |
| 38 | int ssusb_check_clocks(struct ssusb_mtk *ssusb, u32 ex_clks) |
| 39 | { |
| 40 | void __iomem *ibase = ssusb->ippc_base; |
| 41 | u32 value, check_val; |
| 42 | int ret; |
| 43 | |
| 44 | check_val = ex_clks | SSUSB_SYS125_RST_B_STS | SSUSB_SYSPLL_STABLE | |
| 45 | SSUSB_REF_RST_B_STS; |
| 46 | |
| 47 | ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS1, value, |
| 48 | ((value & check_val) == check_val), 10000); |
| 49 | if (ret) { |
| 50 | dev_err(ssusb->dev, "clks of sts1 are not stable!\n"); |
| 51 | return ret; |
| 52 | } |
| 53 | |
| 54 | ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS2, value, |
| 55 | (value & SSUSB_U2_MAC_SYS_RST_B_STS), 10000); |
| 56 | if (ret) { |
| 57 | dev_err(ssusb->dev, "mac2 clock is not stable\n"); |
| 58 | return ret; |
| 59 | } |
| 60 | |
| 61 | return 0; |
| 62 | } |
| 63 | |
| 64 | int ssusb_phy_setup(struct ssusb_mtk *ssusb) |
| 65 | { |
| 66 | struct udevice *dev = ssusb->dev; |
| 67 | struct phy_bulk *phys = &ssusb->phys; |
| 68 | int ret; |
| 69 | |
| 70 | ret = generic_phy_get_bulk(dev, phys); |
| 71 | if (ret) |
| 72 | return ret; |
| 73 | |
| 74 | ret = generic_phy_init_bulk(phys); |
| 75 | if (ret) |
| 76 | return ret; |
| 77 | |
| 78 | ret = generic_phy_power_on_bulk(phys); |
| 79 | if (ret) |
| 80 | generic_phy_exit_bulk(phys); |
| 81 | |
| 82 | return ret; |
| 83 | } |
| 84 | |
| 85 | void ssusb_phy_shutdown(struct ssusb_mtk *ssusb) |
| 86 | { |
| 87 | generic_phy_power_off_bulk(&ssusb->phys); |
| 88 | generic_phy_exit_bulk(&ssusb->phys); |
| 89 | } |
| 90 | |
| 91 | static int ssusb_rscs_init(struct ssusb_mtk *ssusb) |
| 92 | { |
| 93 | int ret = 0; |
| 94 | |
| 95 | ret = regulator_set_enable(ssusb->vusb33_supply, true); |
| 96 | if (ret < 0 && ret != -ENOSYS) { |
| 97 | dev_err(ssusb->dev, "failed to enable vusb33\n"); |
| 98 | goto vusb33_err; |
| 99 | } |
| 100 | |
| 101 | ret = clk_enable_bulk(&ssusb->clks); |
| 102 | if (ret) |
| 103 | goto clks_err; |
| 104 | |
| 105 | ret = ssusb_phy_setup(ssusb); |
| 106 | if (ret) { |
| 107 | dev_err(ssusb->dev, "failed to setup phy\n"); |
| 108 | goto phy_err; |
| 109 | } |
| 110 | |
| 111 | return 0; |
| 112 | |
| 113 | phy_err: |
| 114 | clk_disable_bulk(&ssusb->clks); |
| 115 | clks_err: |
| 116 | regulator_set_enable(ssusb->vusb33_supply, false); |
| 117 | vusb33_err: |
| 118 | return ret; |
| 119 | } |
| 120 | |
| 121 | static void ssusb_rscs_exit(struct ssusb_mtk *ssusb) |
| 122 | { |
| 123 | clk_disable_bulk(&ssusb->clks); |
| 124 | regulator_set_enable(ssusb->vusb33_supply, false); |
| 125 | ssusb_phy_shutdown(ssusb); |
| 126 | } |
| 127 | |
| 128 | static void ssusb_ip_sw_reset(struct ssusb_mtk *ssusb) |
| 129 | { |
| 130 | /* reset whole ip (xhci & u3d) */ |
| 131 | mtu3_setbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST); |
| 132 | udelay(1); |
| 133 | mtu3_clrbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST); |
| 134 | } |
| 135 | |
| 136 | static int get_ssusb_rscs(struct udevice *dev, struct ssusb_mtk *ssusb) |
| 137 | { |
| 138 | struct udevice *child; |
| 139 | int ret; |
| 140 | |
| 141 | ret = device_get_supply_regulator(dev, "vusb33-supply", |
| 142 | &ssusb->vusb33_supply); |
| 143 | if (ret) /* optional, ignore error */ |
| 144 | dev_warn(dev, "can't get optional vusb33 %d\n", ret); |
| 145 | |
| 146 | ret = device_get_supply_regulator(dev, "vbus-supply", |
| 147 | &ssusb->vbus_supply); |
| 148 | if (ret) /* optional, ignore error */ |
| 149 | dev_warn(dev, "can't get optional vbus regulator %d!\n", ret); |
| 150 | |
| 151 | ret = clk_get_bulk(dev, &ssusb->clks); |
| 152 | if (ret) { |
| 153 | dev_err(dev, "failed to get clocks %d!\n", ret); |
| 154 | return ret; |
| 155 | } |
| 156 | |
| 157 | ssusb->ippc_base = devfdt_remap_addr_name(dev, "ippc"); |
| 158 | if (!ssusb->ippc_base) { |
| 159 | dev_err(dev, "error mapping memory for ippc\n"); |
| 160 | return -ENODEV; |
| 161 | } |
| 162 | |
| 163 | ret = device_find_first_child(dev, &child); |
| 164 | if (ret || !child) { |
| 165 | dev_err(dev, "failed to get child %d!\n", ret); |
| 166 | return ret; |
| 167 | } |
| 168 | |
| 169 | ssusb->mac_base = devfdt_remap_addr_name(child, "mac"); |
| 170 | if (!ssusb->mac_base) { |
| 171 | dev_err(dev, "error mapping memory for mac\n"); |
| 172 | return -ENODEV; |
| 173 | } |
| 174 | |
Simon Glass | a7ece58 | 2020-12-19 10:40:14 -0700 | [diff] [blame] | 175 | ssusb->dr_mode = usb_get_dr_mode(dev_ofnode(child)); |
developer | 13dc3c8 | 2020-10-16 11:38:39 +0800 | [diff] [blame] | 176 | |
| 177 | if (ssusb->dr_mode == USB_DR_MODE_UNKNOWN || |
| 178 | ssusb->dr_mode == USB_DR_MODE_OTG) |
| 179 | ssusb->dr_mode = USB_DR_MODE_PERIPHERAL; |
| 180 | |
| 181 | if (IS_ENABLED(CONFIG_USB_MTU3_GADGET)) |
| 182 | ssusb->dr_mode = USB_DR_MODE_PERIPHERAL; |
| 183 | else if (IS_ENABLED(CONFIG_USB_MTU3_HOST)) |
| 184 | ssusb->dr_mode = USB_DR_MODE_HOST; |
| 185 | |
| 186 | dev_info(dev, "dr_mode: %d, ippc: 0x%p, mac: 0x%p\n", |
| 187 | ssusb->dr_mode, ssusb->ippc_base, ssusb->mac_base); |
| 188 | |
| 189 | return 0; |
| 190 | } |
| 191 | |
| 192 | static int mtu3_probe(struct udevice *dev) |
| 193 | { |
| 194 | struct ssusb_mtk *ssusb = dev_get_priv(dev); |
| 195 | int ret = -ENOMEM; |
| 196 | |
| 197 | ssusb->dev = dev; |
| 198 | |
| 199 | ret = get_ssusb_rscs(dev, ssusb); |
| 200 | if (ret) |
| 201 | return ret; |
| 202 | |
| 203 | ret = ssusb_rscs_init(ssusb); |
| 204 | if (ret) |
| 205 | return ret; |
| 206 | |
| 207 | ssusb_ip_sw_reset(ssusb); |
| 208 | |
| 209 | return 0; |
| 210 | } |
| 211 | |
| 212 | static int mtu3_remove(struct udevice *dev) |
| 213 | { |
| 214 | struct ssusb_mtk *ssusb = dev_to_ssusb(dev); |
| 215 | |
| 216 | ssusb_rscs_exit(ssusb); |
| 217 | return 0; |
| 218 | } |
| 219 | |
| 220 | static const struct udevice_id ssusb_of_match[] = { |
| 221 | {.compatible = "mediatek,ssusb",}, |
| 222 | {}, |
| 223 | }; |
| 224 | |
| 225 | #if CONFIG_IS_ENABLED(DM_USB_GADGET) |
developer | 13dc3c8 | 2020-10-16 11:38:39 +0800 | [diff] [blame] | 226 | static int mtu3_gadget_probe(struct udevice *dev) |
| 227 | { |
| 228 | struct ssusb_mtk *ssusb = dev_to_ssusb(dev->parent); |
| 229 | struct mtu3 *mtu = dev_get_priv(dev); |
| 230 | |
| 231 | mtu->dev = dev; |
| 232 | ssusb->u3d = mtu; |
| 233 | return ssusb_gadget_init(ssusb); |
| 234 | } |
| 235 | |
| 236 | static int mtu3_gadget_remove(struct udevice *dev) |
| 237 | { |
| 238 | struct mtu3 *mtu = dev_get_priv(dev); |
| 239 | |
| 240 | ssusb_gadget_exit(mtu->ssusb); |
| 241 | return 0; |
| 242 | } |
| 243 | |
Marek Vasut | 5972721 | 2024-06-14 02:51:21 +0200 | [diff] [blame] | 244 | static int mtu3_gadget_handle_interrupts(struct udevice *dev) |
| 245 | { |
| 246 | struct mtu3 *mtu = dev_get_priv(dev); |
| 247 | |
| 248 | mtu3_irq(0, mtu); |
| 249 | |
| 250 | return 0; |
| 251 | } |
| 252 | |
| 253 | static const struct usb_gadget_generic_ops mtu3_gadget_ops = { |
| 254 | .handle_interrupts = mtu3_gadget_handle_interrupts, |
| 255 | }; |
| 256 | |
developer | 13dc3c8 | 2020-10-16 11:38:39 +0800 | [diff] [blame] | 257 | U_BOOT_DRIVER(mtu3_peripheral) = { |
| 258 | .name = "mtu3-peripheral", |
| 259 | .id = UCLASS_USB_GADGET_GENERIC, |
| 260 | .of_match = ssusb_of_match, |
Marek Vasut | 5972721 | 2024-06-14 02:51:21 +0200 | [diff] [blame] | 261 | .ops = &mtu3_gadget_ops, |
developer | 13dc3c8 | 2020-10-16 11:38:39 +0800 | [diff] [blame] | 262 | .probe = mtu3_gadget_probe, |
| 263 | .remove = mtu3_gadget_remove, |
Simon Glass | 8a2b47f | 2020-12-03 16:55:17 -0700 | [diff] [blame] | 264 | .priv_auto = sizeof(struct mtu3), |
developer | 13dc3c8 | 2020-10-16 11:38:39 +0800 | [diff] [blame] | 265 | }; |
| 266 | #endif |
| 267 | |
Simon Glass | 1f2440c | 2021-07-10 21:14:29 -0600 | [diff] [blame] | 268 | #if defined(CONFIG_SPL_USB_HOST) || \ |
Simon Glass | 7ec2413 | 2024-09-29 19:49:48 -0600 | [diff] [blame] | 269 | (!defined(CONFIG_XPL_BUILD) && defined(CONFIG_USB_HOST)) |
developer | 13dc3c8 | 2020-10-16 11:38:39 +0800 | [diff] [blame] | 270 | static int mtu3_host_probe(struct udevice *dev) |
| 271 | { |
| 272 | struct ssusb_mtk *ssusb = dev_to_ssusb(dev->parent); |
| 273 | struct mtu3_host *u3h = dev_get_priv(dev); |
| 274 | struct xhci_hcor *hcor; |
| 275 | int rc; |
| 276 | |
| 277 | u3h->dev = dev; |
| 278 | ssusb->u3h = u3h; |
| 279 | rc = ssusb_host_init(ssusb); |
| 280 | if (rc) |
| 281 | return rc; |
| 282 | |
| 283 | u3h->ctrl.quirks = XHCI_MTK_HOST; |
| 284 | hcor = (struct xhci_hcor *)((uintptr_t)u3h->hcd + |
| 285 | HC_LENGTH(xhci_readl(&u3h->hcd->cr_capbase))); |
| 286 | |
| 287 | return xhci_register(dev, u3h->hcd, hcor); |
| 288 | } |
| 289 | |
| 290 | static int mtu3_host_remove(struct udevice *dev) |
| 291 | { |
| 292 | struct mtu3_host *u3h = dev_get_priv(dev); |
| 293 | |
| 294 | xhci_deregister(dev); |
| 295 | ssusb_host_exit(u3h->ssusb); |
| 296 | return 0; |
| 297 | } |
| 298 | |
| 299 | U_BOOT_DRIVER(mtu3_host) = { |
| 300 | .name = "mtu3-host", |
| 301 | .id = UCLASS_USB, |
| 302 | .of_match = ssusb_of_match, |
| 303 | .probe = mtu3_host_probe, |
| 304 | .remove = mtu3_host_remove, |
Simon Glass | 8a2b47f | 2020-12-03 16:55:17 -0700 | [diff] [blame] | 305 | .priv_auto = sizeof(struct mtu3_host), |
developer | 13dc3c8 | 2020-10-16 11:38:39 +0800 | [diff] [blame] | 306 | .ops = &xhci_usb_ops, |
| 307 | .flags = DM_FLAG_ALLOC_PRIV_DMA, |
| 308 | }; |
| 309 | #endif |
| 310 | |
| 311 | static int mtu3_glue_bind(struct udevice *parent) |
| 312 | { |
| 313 | struct udevice *dev; |
| 314 | enum usb_dr_mode dr_mode; |
| 315 | const char *driver; |
| 316 | const char *name; |
| 317 | ofnode node; |
| 318 | int ret; |
| 319 | |
Simon Glass | a7ece58 | 2020-12-19 10:40:14 -0700 | [diff] [blame] | 320 | node = ofnode_by_compatible(dev_ofnode(parent), "mediatek,ssusb"); |
developer | 13dc3c8 | 2020-10-16 11:38:39 +0800 | [diff] [blame] | 321 | if (!ofnode_valid(node)) |
| 322 | return -ENODEV; |
| 323 | |
| 324 | name = ofnode_get_name(node); |
| 325 | dr_mode = usb_get_dr_mode(node); |
| 326 | |
| 327 | switch (dr_mode) { |
| 328 | #if CONFIG_IS_ENABLED(DM_USB_GADGET) |
| 329 | case USB_DR_MODE_PERIPHERAL: |
| 330 | case USB_DR_MODE_OTG: |
| 331 | dev_dbg(parent, "%s: dr_mode: peripheral\n", __func__); |
| 332 | driver = "mtu3-peripheral"; |
| 333 | break; |
| 334 | #endif |
| 335 | |
Simon Glass | 1f2440c | 2021-07-10 21:14:29 -0600 | [diff] [blame] | 336 | #if defined(CONFIG_SPL_USB_HOST) || \ |
Simon Glass | 7ec2413 | 2024-09-29 19:49:48 -0600 | [diff] [blame] | 337 | (!defined(CONFIG_XPL_BUILD) && defined(CONFIG_USB_HOST)) |
developer | 13dc3c8 | 2020-10-16 11:38:39 +0800 | [diff] [blame] | 338 | case USB_DR_MODE_HOST: |
| 339 | dev_dbg(parent, "%s: dr_mode: host\n", __func__); |
| 340 | driver = "mtu3-host"; |
| 341 | break; |
| 342 | #endif |
| 343 | default: |
| 344 | dev_err(parent, "%s: unsupported dr_mode %d\n", |
| 345 | __func__, dr_mode); |
| 346 | return -ENODEV; |
| 347 | }; |
| 348 | |
| 349 | dev_dbg(parent, "%s: node name: %s, driver %s, dr_mode %d\n", |
| 350 | __func__, name, driver, dr_mode); |
| 351 | |
| 352 | ret = device_bind_driver_to_node(parent, driver, name, node, &dev); |
| 353 | if (ret) |
| 354 | dev_err(parent, "%s: not able to bind usb device mode\n", |
| 355 | __func__); |
| 356 | |
| 357 | return ret; |
| 358 | } |
| 359 | |
| 360 | static const struct udevice_id mtu3_of_match[] = { |
| 361 | {.compatible = "mediatek,mtu3",}, |
| 362 | {}, |
| 363 | }; |
| 364 | |
| 365 | U_BOOT_DRIVER(mtu3) = { |
| 366 | .name = "mtu3", |
| 367 | .id = UCLASS_NOP, |
| 368 | .of_match = mtu3_of_match, |
| 369 | .bind = mtu3_glue_bind, |
| 370 | .probe = mtu3_probe, |
| 371 | .remove = mtu3_remove, |
Simon Glass | 8a2b47f | 2020-12-03 16:55:17 -0700 | [diff] [blame] | 372 | .priv_auto = sizeof(struct ssusb_mtk), |
developer | 13dc3c8 | 2020-10-16 11:38:39 +0800 | [diff] [blame] | 373 | }; |