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