blob: bd24b65f578fc4fd88edfbeff820a8d8a66208dd [file] [log] [blame]
/*
* (C) Copyright 2008, Michael Trimarchi <trimarchimichael@yahoo.it>
*
* Author: Michael Trimarchi <trimarchimichael@yahoo.it>
* This code is based on ehci freescale driver
*
* 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; either version 2 of
* the License, or (at your option) any later version.
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#if defined(CONFIG_CMD_USB)
#include <usb.h>
#include "ctrlEnv/mvCtrlEnvLib.h"
#include "boardEnv/mvBoardEnvLib.h"
#include "ctrlEnv/mvCtrlEnvSpec.h"
#include "ctrlEnv/sys/mvCpuIf.h"
#if defined(CONFIG_USB_XHCI)
#include "drivers/usb/host/xhci.h"
#endif
#if defined(CONFIG_USB_EHCI)
#include "drivers/usb/host/ehci.h"
#endif
#include "mvSysUsbConfig.h"
#if defined (CONFIG_USB_XHCI)
/*********************************************************************************/
/********************** xHCI Stack registration layer **********************/
/*********************************************************************************/
/*
* initialize USB3 Core:
* Set UTMI PHY Selector
*/
static void mv_xhci_core_init(MV_U32 unitId)
{
/* A shared xHCI MAC with USB2/3 requires UTMI Phy selection */
mvCtrlUtmiPhySelectorSet(USB3_UNIT_ID);
}
int xhci_hcd_init(int index, struct xhci_hccr **hccr, struct xhci_hcor **hcor)
{
if (mvCtrlUsb3HostMaxGet() <= 0) {
mvOsPrintf("\n%s: Error: USB 3.0 is not supported on current device\n", __func__);
return -1;
}
mv_xhci_core_init(index);
/* Set operational xHCI register base*/
*hccr = (struct xhci_hccr *)(USB3_REGS_PHYS_BASE(index));
/* Set host controller operation register base */
*hcor = (struct xhci_hcor *)((uint32_t) (*hccr) + XHCI_HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));
debug("marvell-xhci: init hccr %x and hcor %x hc_length %d\n",
(uint32_t)*hccr, (uint32_t)*hcor,
(uint32_t)XHCI_HC_LENGTH(xhci_readl(&(*hccr)->cr_capbase)));
return 0;
}
void xhci_hcd_stop(int index)
{
return;
}
#endif /* CONFIG_USB_XHCI */
#if defined(CONFIG_USB_EHCI)
/*********************************************************************************/
/********************** eHCI Stack registration layer **********************/
/*********************************************************************************/
/* ehci_hcd_init:
* Create the appropriate control structures to manage new EHCI host controller.
*/
int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)
{
/* ALP/A375 USB2 port 0 is a shared MAC with USB2/3, and requires UTMI Phy selection */
if (index == 0)
mvCtrlUtmiPhySelectorSet(USB_UNIT_ID);
*hccr = (struct ehci_hccr *)(MV_USB2_CAPLENGTH_OFFSET(index));
*hcor = (struct ehci_hcor *)((uint32_t) (*hccr) + HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
debug ("Marvell init hccr %x and hcor %x hc_length %d\n",
(uint32_t)hccr, (uint32_t)hcor,
(uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase)));
return 0;
}
/* ehci_hcd_stop:
* Destroy the appropriate control structures corresponding the the EHCI host controller.
*/
int ehci_hcd_stop(int index)
{
return 0;
}
int ehci_usb_alloc_device(struct usb_device *udev)
{
return 0;
}
#endif /* CONFIG_USB_EHCI */
/*********************************************************************************/
/******************** Host Controller stack replacement-layer ********************/
/*********************************************************************************/
/* Stack pointers*/
struct hc_interface {
MV_BOOL interface_supported;
int (*hc_usb_lowlevel_init)(int index, void **controller);
int (*hc_usb_lowlevel_stop)(int index);
int (*hc_submit_int_msg)(struct usb_device *dev, unsigned long pipe, void *buffer,
int length, int interval);
int (*hc_submit_bulk_msg)(struct usb_device *dev, unsigned long pipe, void *buffer,
int length);
int (*hc_submit_control_msg)(struct usb_device *dev, unsigned long pipe, void *buffer,
int length, struct devrequest *setup);
int (*hc_usb_alloc_device)(struct usb_device *udev);
};
/* hc is the Host Controller struct for xHCI/eHCI routine's pointers */
struct hc_interface *hc;
struct hc_interface hc_ehci = {
#if defined(CONFIG_USB_EHCI)
.interface_supported = MV_TRUE,
.hc_usb_lowlevel_init = ehci_usb_lowlevel_init,
.hc_usb_lowlevel_stop = ehci_usb_lowlevel_stop,
.hc_submit_int_msg = ehci_submit_int_msg,
.hc_submit_bulk_msg = ehci_submit_bulk_msg,
.hc_submit_control_msg = ehci_submit_control_msg,
.hc_usb_alloc_device = ehci_usb_alloc_device
#else
.interface_supported = MV_FALSE
#endif
};
struct hc_interface hc_xhci = {
#if defined(CONFIG_USB_XHCI)
.interface_supported = MV_TRUE,
.hc_usb_lowlevel_init = xhci_usb_lowlevel_init,
.hc_usb_lowlevel_stop = xhci_usb_lowlevel_stop,
.hc_submit_int_msg = xhci_submit_int_msg,
.hc_submit_bulk_msg = xhci_submit_bulk_msg,
.hc_submit_control_msg = xhci_submit_control_msg,
.hc_usb_alloc_device = xhci_usb_alloc_device
#else
.interface_supported = MV_FALSE
#endif
};
/*******************************************************************************
* printUsbError - This function used to print error message when USB error dedected.
* called from "usb_lowlevel_init" and "usb_lowlevel_stop" functions.
*
* INPUT:
* MV_BOOL value - MV_TRUE if usb connected on current board ,MV_FALSE otherwise.
*
* OUTPUT:
* prints USB error messages.
*
* RETURN:
* -1
*
*******************************************************************************/
static int printUsbError(MV_BOOL mvBoardIsUsbPortConnected ,int port)
{
int usbType = simple_strtoul(getenv("usbType"), NULL, 10);
int usb3HostNum = mvCtrlUsb3HostMaxGet();
int usb2HostNum = mvCtrlUsbMaxGet();
if(mvBoardIsUsbPortConnected == MV_FALSE)
mvOsPrintf("\nError: requested 'usbActive' (%d) is not connected on current board\n",port);
else {
mvOsPrintf("Error: requested 'usbType' (Type %d) is not supported.", usbType);
if ((usbType == 2 && usb2HostNum < 1) || (usbType ==3 && usb3HostNum < 1))
mvOsPrintf(" (no available USB%d ports on current Soc).\n",usbType);
}
if ((hc_ehci.interface_supported == MV_TRUE && usb2HostNum > 0) ||
(hc_xhci.interface_supported == MV_TRUE && usb3HostNum > 0))
mvOsPrintf("\n\n\t Supported Units:\n");
if (hc_ehci.interface_supported == MV_TRUE && usb2HostNum > 0)
mvOsPrintf("\n\tUSB2.0: %d ports: set usbType = 2 --> EHCI Stack will be used\n", usb2HostNum);
if (hc_xhci.interface_supported == MV_TRUE && usb3HostNum > 0)
mvOsPrintf("\tUSB3.0: %d ports: set usbType = 3 --> xHCI Stack will be used\n", usb3HostNum);
return -1;
}
/* usb_lowlevel_init:
* this routine navigate between currently selected stack (eHCI/xHCI)
* - Read 'usbType' env variable to select requested interface:
* usbType = 2 -- > eHCI
* usbType = 3 -- > xHCI
* - Call usb_lowlevel_init from selected stack
*/
/* mark detected usb port, to ensure proper 'stop'
* process before next detection request */
int currentUsbActive = -1;
int usb_lowlevel_init(int index, void **controller)
{
int usb2HostNum, usb3HostNum, usbType, usbActive = 0;
usbActive = simple_strtoul(getenv("usbActive"), NULL, 10);
usbType = simple_strtoul(getenv("usbType"), NULL, 10);
usb3HostNum = mvCtrlUsb3HostMaxGet();
usb2HostNum = mvCtrlUsbMaxGet();
switch (usbType) {
case 2:
if (hc_ehci.interface_supported == MV_TRUE && usb2HostNum > 0) {
usbActive = mvCtrlUsbMapGet(USB_UNIT_ID, usbActive);
if (mvBoardIsUsbPortConnected(USB_UNIT_ID, usbActive) == MV_FALSE)
return printUsbError(MV_FALSE, usbActive);
hc = &hc_ehci; /* set Host Controller struct for function pointers */
} else
return printUsbError(MV_TRUE, usbActive);
break;
case 3:
if (hc_xhci.interface_supported == MV_TRUE && usb3HostNum > 0) {
/* mvCtrlUsbMapGet used only for legacy devices ALP/A375 */
usbActive = mvCtrlUsbMapGet(USB3_UNIT_ID, usbActive);
if (mvBoardIsUsbPortConnected(USB3_UNIT_ID, usbActive) == MV_FALSE)
return printUsbError(MV_FALSE, usbActive);
if (mvCtrlIsUsbSerDesConnected(usbActive) == MV_FALSE) {
mvOsPrintf("\n'usbActive' warning (%d): Invalid USB3.0 port (no valid SerDes)..", usbActive);
mvOsPrintf("Trying USB2.0 Host via USB3.0\n");
}
#ifdef MV_USB_VBUS_CYCLE
/* VBUS signal is lowered prior to SerDes initialization sequence,
* before initializing and detecting device, set VBUS enabled */
mvBoardUsbVbusSet(usbActive);
#endif
hc = &hc_xhci; /* set Host Controller struct for function pointers */
} else
return printUsbError(MV_TRUE, usbActive);
break;
default:
return printUsbError(MV_TRUE, usbActive);
}
mvOsPrintf("Port (usbActive) : %d\tInterface (usbType = %d) : ", usbActive,
((usbType == 3) ? 3 : 2));
/* Make sure that usbActive never exeeds the configured max controllers count
The CONFIG_USB_MAX_CONTROLLER_COUNT can be changed for different boards */
if (usbActive >= CONFIG_USB_MAX_CONTROLLER_HOST_COUNT) {
mvOsPrintf("usbActive=%d is out of supported range\n",usbActive);
return -1;
}
/* Marvell USB code supports only one active controller (USB0), while the actual host
device is selected by usbActive environment variable */
if (index > 0) {
mvOsPrintf("\nOnly one active controller supported! Skipping USB%d initialization.\n", index);
return -1;
}
currentUsbActive = usbActive;
return hc->hc_usb_lowlevel_init(usbActive, controller);
}
int usb_lowlevel_stop(int index)
{
int usbActive = currentUsbActive, ret;
if (!hc) {
mvOsPrintf("%s: Error: run 'usb reset' to set host controller interface.\n", __func__);
return -1;
}
/* Mark that there is no current usbActive to stop */
currentUsbActive = -1;
ret = hc->hc_usb_lowlevel_stop(usbActive);
hc = NULL;
return ret;
}
int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int length, int interval)
{
if (!hc) {
mvOsPrintf("%s: Error: run 'usb reset' to set host controller interface.\n", __func__);
return -1;
}
return hc->hc_submit_int_msg(dev, pipe, buffer, length, interval);
}
int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int length)
{
if (!hc) {
mvOsPrintf("%s: Error: run 'usb reset' to set host controller interface.\n", __func__);
return -1;
}
return hc->hc_submit_bulk_msg(dev, pipe, buffer, length);
}
int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
int length, struct devrequest *setup)
{
if (!hc) {
mvOsPrintf("%s: Error: run 'usb reset' to set host controller interface.\n", __func__);
return -1;
}
return hc->hc_submit_control_msg(dev, pipe, buffer, length, setup);
}
int usb_alloc_device(struct usb_device *udev)
{
if (!hc) {
mvOsPrintf("%s: Error: run 'usb reset' to set host controller interface.\n", __func__);
return -1;
}
return hc->hc_usb_alloc_device(udev);
}
#endif /* CONFIG_CMD_USB */