/* * This file is part of the coreboot project. * * Copyright (C) 2014 Rockchip Electronics * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include "generic_hub.h" #include "dwc2_private.h" #include "dwc2.h" static int dwc2_rh_port_status_changed(usbdev_t *const dev, const int port) { hprt_t hprt; int changed; dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller); hprt.d32 = readl(dwc2->hprt0); changed = hprt.prtconndet; /* Clear connect detect flag */ if (changed) { hprt.d32 &= HPRT_W1C_MASK; hprt.prtconndet = 1; writel(hprt.d32, dwc2->hprt0); } return changed; } static int dwc2_rh_port_connected(usbdev_t *const dev, const int port) { hprt_t hprt; dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller); hprt.d32 = readl(dwc2->hprt0); return hprt.prtconnsts; } static int dwc2_rh_port_in_reset(usbdev_t *const dev, const int port) { hprt_t hprt; dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller); hprt.d32 = readl(dwc2->hprt0); return hprt.prtrst; } static int dwc2_rh_port_enabled(usbdev_t *const dev, const int port) { hprt_t hprt; dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller); hprt.d32 = readl(dwc2->hprt0); return hprt.prtena; } static usb_speed dwc2_rh_port_speed(usbdev_t *const dev, const int port) { hprt_t hprt; dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller); hprt.d32 = readl(dwc2->hprt0); if (hprt.prtena) { switch (hprt.prtspd) { case PRTSPD_HIGH: return HIGH_SPEED; case PRTSPD_FULL: return FULL_SPEED; case PRTSPD_LOW: return LOW_SPEED; } } return -1; } static int dwc2_rh_reset_port(usbdev_t *const dev, const int port) { hprt_t hprt; dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller); hprt.d32 = readl(dwc2->hprt0); hprt.d32 &= HPRT_W1C_MASK; hprt.prtrst = 1; writel(hprt.d32, dwc2->hprt0); /* Wait a bit while reset is active. */ mdelay(50); /* Deassert reset. */ hprt.prtrst = 0; writel(hprt.d32, dwc2->hprt0); /* * If reset and speed enum success the DWC2 core will set enable bit * after port reset bit is deasserted */ mdelay(1); hprt.d32 = readl(dwc2->hprt0); usb_debug("%s reset port ok, hprt = 0x%08x\n", __func__, hprt.d32); if (!hprt.prtena) { usb_debug("%s enable port fail! hprt = 0x%08x\n", __func__, hprt.d32); return -1; } return 0; } static int dwc2_rh_enable_port(usbdev_t *const dev, const int port) { hprt_t hprt; dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller); /* Power on the port */ hprt.d32 = readl(dwc2->hprt0); hprt.d32 &= HPRT_W1C_MASK; hprt.prtpwr = 1; writel(hprt.d32, dwc2->hprt0); return 0; } static int dwc2_rh_disable_port(usbdev_t *const dev, const int port) { hprt_t hprt; dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller); hprt.d32 = readl(dwc2->hprt0); hprt.d32 &= HPRT_W1C_MASK; /* Disable the port*/ hprt.prtena = 1; /* Power off the port */ hprt.prtpwr = 0; writel(hprt.d32, dwc2->hprt0); return 0; } static const generic_hub_ops_t dwc2_rh_ops = { .hub_status_changed = NULL, .port_status_changed = dwc2_rh_port_status_changed, .port_connected = dwc2_rh_port_connected, .port_in_reset = dwc2_rh_port_in_reset, .port_enabled = dwc2_rh_port_enabled, .port_speed = dwc2_rh_port_speed, .enable_port = dwc2_rh_enable_port, .disable_port = dwc2_rh_disable_port, .start_port_reset = NULL, .reset_port = dwc2_rh_reset_port, }; void dwc2_rh_init(usbdev_t *dev) { dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller); /* we can set them here because a root hub _really_ shouldn't appear elsewhere */ dev->address = 0; dev->hub = -1; dev->port = -1; generic_hub_init(dev, 1, &dwc2_rh_ops); usb_debug("dwc2_rh_init HPRT 0x%08x p = %p\n ", readl(dwc2->hprt0), dwc2->hprt0); usb_debug("DWC2: root hub init done\n"); }