| /* |
| * 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. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc. |
| */ |
| |
| |
| #include <usb/usb.h> |
| #include "generic_hub.h" |
| #include "dwc2_private.h" |
| #include "dwc2.h" |
| |
| #include "base/io.h" |
| #include "base/time.h" |
| |
| static int |
| dwc2_rh_port_status_changed(UsbDev *const dev, const int port) |
| { |
| hprt_t hprt; |
| int changed; |
| dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller); |
| |
| hprt.d32 = read32(dwc2->hprt0); |
| changed = hprt.prtconndet; |
| |
| /* Clear connect detect flag */ |
| if (changed) { |
| hprt.d32 &= HPRT_W1C_MASK; |
| hprt.prtconndet = 1; |
| write32(dwc2->hprt0, hprt.d32); |
| } |
| return changed; |
| } |
| |
| static int |
| dwc2_rh_port_connected(UsbDev *const dev, const int port) |
| { |
| hprt_t hprt; |
| dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller); |
| |
| hprt.d32 = read32(dwc2->hprt0); |
| return hprt.prtconnsts; |
| } |
| |
| static int |
| dwc2_rh_port_in_reset(UsbDev *const dev, const int port) |
| { |
| hprt_t hprt; |
| dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller); |
| |
| hprt.d32 = read32(dwc2->hprt0); |
| return hprt.prtrst; |
| } |
| |
| static int |
| dwc2_rh_port_enabled(UsbDev *const dev, const int port) |
| { |
| hprt_t hprt; |
| dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller); |
| |
| hprt.d32 = read32(dwc2->hprt0); |
| return hprt.prtena; |
| } |
| |
| static UsbSpeed |
| dwc2_rh_port_speed(UsbDev *const dev, const int port) |
| { |
| hprt_t hprt; |
| dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller); |
| |
| hprt.d32 = read32(dwc2->hprt0); |
| if (hprt.prtena) { |
| switch (hprt.prtspd) { |
| case PRTSPD_HIGH: |
| return UsbHighSpeed; |
| case PRTSPD_FULL: |
| return UsbFullSpeed; |
| case PRTSPD_LOW: |
| return UsbLowSpeed; |
| } |
| } |
| return -1; |
| } |
| |
| static int |
| dwc2_rh_reset_port(UsbDev *const dev, const int port) |
| { |
| hprt_t hprt; |
| dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller); |
| |
| hprt.d32 = read32(dwc2->hprt0); |
| hprt.d32 &= HPRT_W1C_MASK; |
| hprt.prtrst = 1; |
| write32(dwc2->hprt0, hprt.d32); |
| |
| /* Wait a bit while reset is active. */ |
| mdelay(50); |
| |
| /* Deassert reset. */ |
| hprt.prtrst = 0; |
| write32(dwc2->hprt0, hprt.d32); |
| |
| /* |
| * If reset and speed enum success the DWC2 core will set enable bit |
| * after port reset bit is deasserted |
| */ |
| mdelay(1); |
| hprt.d32 = read32(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 *const dev, const int port) |
| { |
| hprt_t hprt; |
| dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller); |
| |
| /* Power on the port */ |
| hprt.d32 = read32(dwc2->hprt0); |
| hprt.d32 &= HPRT_W1C_MASK; |
| hprt.prtpwr = 1; |
| write32(dwc2->hprt0, hprt.d32); |
| return 0; |
| } |
| |
| static int |
| dwc2_rh_disable_port(UsbDev *const dev, const int port) |
| { |
| hprt_t hprt; |
| dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller); |
| |
| hprt.d32 = read32(dwc2->hprt0); |
| hprt.d32 &= HPRT_W1C_MASK; |
| /* Disable the port*/ |
| hprt.prtena = 1; |
| /* Power off the port */ |
| hprt.prtpwr = 0; |
| write32(dwc2->hprt0, hprt.d32); |
| return 0; |
| } |
| |
| static const GenericHubOps 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 *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 ", |
| read32(dwc2->hprt0), dwc2->hprt0); |
| usb_debug("DWC2: root hub init done\n"); |
| } |