Previously printf()'s were used to show USB messages which results in lots of USB information being shown when it isn't needed. This will now use the usb_debug() printing funtion that already exists in usb.h. Change-Id: I2199814de3327417417eb2e26a660f4a5557cb9f Signed-off-by: Dave Frodin <dave.frodin@se-eng.com> Reviewed-on: http://review.coreboot.org/2044 Tested-by: build bot (Jenkins) Reviewed-by: Anton Kochkov <anton.kochkov@gmail.com>
		
			
				
	
	
		
			270 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			270 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * This file is part of the libpayload project.
 | 
						|
 *
 | 
						|
 * Copyright (C) 2010 Patrick Georgi
 | 
						|
 *
 | 
						|
 * Redistribution and use in source and binary forms, with or without
 | 
						|
 * modification, are permitted provided that the following conditions
 | 
						|
 * are met:
 | 
						|
 * 1. Redistributions of source code must retain the above copyright
 | 
						|
 *    notice, this list of conditions and the following disclaimer.
 | 
						|
 * 2. Redistributions in binary form must reproduce the above copyright
 | 
						|
 *    notice, this list of conditions and the following disclaimer in the
 | 
						|
 *    documentation and/or other materials provided with the distribution.
 | 
						|
 * 3. The name of the author may not be used to endorse or promote products
 | 
						|
 *    derived from this software without specific prior written permission.
 | 
						|
 *
 | 
						|
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 | 
						|
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
						|
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
						|
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 | 
						|
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
						|
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
						|
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
						|
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
						|
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
						|
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
						|
 * SUCH DAMAGE.
 | 
						|
 */
 | 
						|
 | 
						|
#define USB_DEBUG
 | 
						|
 | 
						|
#include <arch/virtual.h>
 | 
						|
#include "xhci.h"
 | 
						|
#include "xhci_private.h"
 | 
						|
 | 
						|
static void xhci_start (hci_t *controller);
 | 
						|
static void xhci_stop (hci_t *controller);
 | 
						|
static void xhci_reset (hci_t *controller);
 | 
						|
static void xhci_shutdown (hci_t *controller);
 | 
						|
static int xhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize);
 | 
						|
static int xhci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq,
 | 
						|
			 int dalen, u8 *data);
 | 
						|
static void* xhci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming);
 | 
						|
static void xhci_destroy_intr_queue (endpoint_t *ep, void *queue);
 | 
						|
static u8* xhci_poll_intr_queue (void *queue);
 | 
						|
 | 
						|
static void
 | 
						|
xhci_reset (hci_t *controller)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
xhci_reinit (hci_t *controller)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
hci_t *
 | 
						|
xhci_init (pcidev_t addr)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
 | 
						|
	hci_t *controller = new_controller ();
 | 
						|
 | 
						|
	if (!controller)
 | 
						|
		fatal("Could not create USB controller instance.\n");
 | 
						|
 | 
						|
	controller->instance = malloc (sizeof (xhci_t));
 | 
						|
	if(!controller->instance)
 | 
						|
		fatal("Not enough memory creating USB controller instance.\n");
 | 
						|
 | 
						|
	controller->type = XHCI;
 | 
						|
 | 
						|
	controller->start = xhci_start;
 | 
						|
	controller->stop = xhci_stop;
 | 
						|
	controller->reset = xhci_reset;
 | 
						|
	controller->init = xhci_reinit;
 | 
						|
	controller->shutdown = xhci_shutdown;
 | 
						|
	controller->bulk = xhci_bulk;
 | 
						|
	controller->control = xhci_control;
 | 
						|
	controller->create_intr_queue = xhci_create_intr_queue;
 | 
						|
	controller->destroy_intr_queue = xhci_destroy_intr_queue;
 | 
						|
	controller->poll_intr_queue = xhci_poll_intr_queue;
 | 
						|
	for (i = 0; i < 128; i++) {
 | 
						|
		controller->devices[i] = 0;
 | 
						|
	}
 | 
						|
	init_device_entry (controller, 0);
 | 
						|
	XHCI_INST (controller)->roothub = controller->devices[0];
 | 
						|
 | 
						|
	controller->bus_address = addr;
 | 
						|
	controller->reg_base = (u32)phys_to_virt(pci_read_config32 (controller->bus_address, 0x10) & ~0xf);
 | 
						|
	//controller->reg_base = pci_read_config32 (controller->bus_address, 0x14) & ~0xf;
 | 
						|
	if (pci_read_config32 (controller->bus_address, 0x14) > 0) {
 | 
						|
		fatal("We don't do 64bit addressing.\n");
 | 
						|
	}
 | 
						|
	usb_debug("regbase: %lx\n", controller->reg_base);
 | 
						|
 | 
						|
	XHCI_INST (controller)->capreg = (void*)controller->reg_base;
 | 
						|
	XHCI_INST (controller)->opreg = (void*)(controller->reg_base + XHCI_INST (controller)->capreg->caplength);
 | 
						|
	XHCI_INST (controller)->hcrreg = (void*)(controller->reg_base + XHCI_INST (controller)->capreg->rtsoff);
 | 
						|
	XHCI_INST (controller)->dbreg = (void*)(controller->reg_base + XHCI_INST (controller)->capreg->dboff);
 | 
						|
	usb_debug("caplen: %lx\nrtsoff: %lx\ndboff: %lx\n", XHCI_INST (controller)->capreg->caplength, XHCI_INST (controller)->capreg->rtsoff, XHCI_INST (controller)->capreg->dboff);
 | 
						|
	usb_debug("caplength: %x\n", XHCI_INST (controller)->capreg->caplength);
 | 
						|
	usb_debug("hciversion: %x.%x\n", XHCI_INST (controller)->capreg->hciver_hi, XHCI_INST (controller)->capreg->hciver_lo);
 | 
						|
	if ((XHCI_INST (controller)->capreg->hciversion < 0x96) || (XHCI_INST (controller)->capreg->hciversion > 0x100)) {
 | 
						|
		fatal("Unsupported xHCI version\n");
 | 
						|
	}
 | 
						|
	usb_debug("maxslots: %x\n", XHCI_INST (controller)->capreg->MaxSlots);
 | 
						|
	usb_debug("maxports: %x\n", XHCI_INST (controller)->capreg->MaxPorts);
 | 
						|
	int pagesize = XHCI_INST (controller)->opreg->pagesize << 12;
 | 
						|
	usb_debug("pagesize: %x\n", pagesize);
 | 
						|
 | 
						|
	XHCI_INST (controller)->dcbaa = memalign(64, (XHCI_INST (controller)->capreg->MaxSlots+1)*sizeof(devctxp_t));
 | 
						|
	memset((void*)XHCI_INST (controller)->dcbaa, 0, (XHCI_INST (controller)->capreg->MaxSlots+1)*sizeof(devctxp_t));
 | 
						|
 | 
						|
	usb_debug("max scratchpad bufs: %x\n", XHCI_INST (controller)->capreg->Max_Scratchpad_Bufs);
 | 
						|
	if (XHCI_INST (controller)->capreg->Max_Scratchpad_Bufs > 0) {
 | 
						|
		XHCI_INST (controller)->dcbaa->ptr = memalign(64, XHCI_INST (controller)->capreg->Max_Scratchpad_Bufs * 8);
 | 
						|
	}
 | 
						|
 | 
						|
	XHCI_INST (controller)->opreg->dcbaap_lo = virt_to_phys(XHCI_INST (controller)->dcbaa);
 | 
						|
	XHCI_INST (controller)->opreg->dcbaap_hi = 0;
 | 
						|
 | 
						|
	usb_debug("waiting for controller to be ready - ");
 | 
						|
	while ((XHCI_INST (controller)->opreg->usbsts & USBSTS_CNR) != 0) mdelay(1);
 | 
						|
	usb_debug("ok.\n");
 | 
						|
 | 
						|
	usb_debug("ERST Max: %lx -> %lx entries\n", XHCI_INST (controller)->capreg->ERST_Max, 1<<(XHCI_INST (controller)->capreg->ERST_Max));
 | 
						|
 | 
						|
	// enable all available slots
 | 
						|
	XHCI_INST (controller)->opreg->config = XHCI_INST (controller)->capreg->MaxSlots & CONFIG_MASK_MaxSlotsEn;
 | 
						|
 | 
						|
	XHCI_INST (controller)->cmd_ring = memalign(64, 16*sizeof(trb_t)); /* TODO: make sure not to cross 64k page boundary */
 | 
						|
	memset((void*)XHCI_INST (controller)->cmd_ring, 0, 16*sizeof(trb_t));
 | 
						|
 | 
						|
	XHCI_INST (controller)->ev_ring = memalign(64, 16*sizeof(trb_t)); /* TODO: make sure not to cross 64k page boundary */
 | 
						|
	memset((void*)XHCI_INST (controller)->ev_ring, 0, 16*sizeof(trb_t));
 | 
						|
 | 
						|
	XHCI_INST (controller)->ev_ring_table = memalign(64, sizeof(erst_entry_t));
 | 
						|
	memset((void*)XHCI_INST (controller)->ev_ring_table, 0, sizeof(erst_entry_t));
 | 
						|
	XHCI_INST (controller)->ev_ring_table[0].seg_base_lo = virt_to_phys(XHCI_INST (controller)->ev_ring);
 | 
						|
	XHCI_INST (controller)->ev_ring_table[0].seg_base_hi = 0;
 | 
						|
	XHCI_INST (controller)->ev_ring_table[0].seg_size = 16;
 | 
						|
 | 
						|
	// init command ring
 | 
						|
	XHCI_INST (controller)->opreg->crcr_lo = virt_to_phys(XHCI_INST (controller)->cmd_ring) | CRCR_RCS;
 | 
						|
	XHCI_INST (controller)->opreg->crcr_hi = 0;
 | 
						|
	XHCI_INST (controller)->cmd_ccs = 1;
 | 
						|
	XHCI_INST (controller)->ev_ccs = 1;
 | 
						|
 | 
						|
	// init primary interrupter
 | 
						|
	XHCI_INST (controller)->hcrreg->intrrs[0].erstsz = 1;
 | 
						|
	XHCI_INST (controller)->hcrreg->intrrs[0].erdp_lo = virt_to_phys(XHCI_INST (controller)->ev_ring);
 | 
						|
	XHCI_INST (controller)->hcrreg->intrrs[0].erdp_hi = 0;
 | 
						|
	XHCI_INST (controller)->hcrreg->intrrs[0].erstba_lo = virt_to_phys(XHCI_INST (controller)->ev_ring_table);
 | 
						|
	XHCI_INST (controller)->hcrreg->intrrs[0].erstba_hi = 0;
 | 
						|
 | 
						|
	XHCI_INST (controller)->opreg->usbcmd |= USBCMD_RS; /* start USB controller */
 | 
						|
	XHCI_INST (controller)->dbreg[0] = 0; // and tell controller to consume commands
 | 
						|
 | 
						|
	/* TODO: TEST */
 | 
						|
	// setup noop command
 | 
						|
	trb_t *cmd = &XHCI_INST (controller)->cmd_ring[0];
 | 
						|
	((u32*)cmd)[3] = 1-XHCI_INST (controller)->cmd_ccs; // disable command descriptor
 | 
						|
	((u32*)cmd)[0] = 0;
 | 
						|
	((u32*)cmd)[1] = 0;
 | 
						|
	((u32*)cmd)[2] = 0;
 | 
						|
	cmd->cmd_No_Op.TRB_Type = TRB_CMD_NOOP;
 | 
						|
 | 
						|
	// ring the HC doorbell
 | 
						|
	usb_debug("Posting command at %lx\n", virt_to_phys(cmd));
 | 
						|
	cmd->cmd_No_Op.C = XHCI_INST (controller)->cmd_ccs; // enable command
 | 
						|
	XHCI_INST (controller)->dbreg[0] = 0; // and tell controller to consume commands
 | 
						|
 | 
						|
	// wait for result in event ring
 | 
						|
	trb_t *ev = &XHCI_INST (controller)->ev_ring[0];
 | 
						|
	trb_t *ev1 = &XHCI_INST (controller)->ev_ring[1];
 | 
						|
	while (ev->event_cmd_cmpl.C != XHCI_INST (controller)->ev_ccs) {
 | 
						|
		usb_debug("CRCR: %lx, USBSTS: %lx\n",  XHCI_INST (controller)->opreg->crcr_lo, XHCI_INST (controller)->opreg->usbsts);
 | 
						|
		usb_debug("ev0.C %x, ev1.C %x\n", ev->event_cmd_cmpl.C, ev1->event_cmd_cmpl.C);
 | 
						|
		mdelay(100);
 | 
						|
	}
 | 
						|
	usb_debug("command ring is %srunning\n", (XHCI_INST (controller)->opreg->crcr_lo & CRCR_CRR)?"":"not ");
 | 
						|
	switch (ev->event_cmd_cmpl.TRB_Type) {
 | 
						|
		case TRB_EV_CMD_CMPL:
 | 
						|
			usb_debug("Completed command TRB at %lx. Code: %d\n",
 | 
						|
				ev->event_cmd_cmpl.Cmd_TRB_Pointer_lo, ev->event_cmd_cmpl.Completion_Code);
 | 
						|
			break;
 | 
						|
		case TRB_EV_PORTSC:
 | 
						|
			usb_debug("Port Status Change Event. Completion Code: %d\n Port: %d. Ignoring.\n",
 | 
						|
				ev->event_cmd_cmpl.Completion_Code, ev->event_portsc.Port);
 | 
						|
			// we ignore the event as we look for the PORTSC registers instead, at a time when it suits _us_
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			usb_debug("Unknown event: %d, Completion Code: %d\n", ev->event_cmd_cmpl.TRB_Type, ev->event_cmd_cmpl.Completion_Code);
 | 
						|
			break;
 | 
						|
	}
 | 
						|
	usb_debug("CRCR: %lx, USBSTS: %lx\n",  XHCI_INST (controller)->opreg->crcr_lo, XHCI_INST (controller)->opreg->usbsts);
 | 
						|
	usb_debug("ev0.C %x, ev1.C %x, ev1.CC %d\n", ev->event_cmd_cmpl.C, ev1->event_cmd_cmpl.C, ev1->event_cmd_cmpl.Completion_Code);
 | 
						|
 | 
						|
	controller->devices[0]->controller = controller;
 | 
						|
	controller->devices[0]->init = xhci_rh_init;
 | 
						|
	controller->devices[0]->init (controller->devices[0]);
 | 
						|
 | 
						|
	return controller;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
xhci_shutdown (hci_t *controller)
 | 
						|
{
 | 
						|
	if (controller == 0)
 | 
						|
		return;
 | 
						|
	detach_controller (controller);
 | 
						|
	XHCI_INST (controller)->roothub->destroy (XHCI_INST (controller)->
 | 
						|
						  roothub);
 | 
						|
	/* TODO: stop hardware, kill data structures */
 | 
						|
	free (XHCI_INST (controller));
 | 
						|
	free (controller);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
xhci_start (hci_t *controller)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
xhci_stop (hci_t *controller)
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
xhci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen,
 | 
						|
	      unsigned char *data)
 | 
						|
{
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
/* finalize == 1: if data is of packet aligned size, add a zero length packet */
 | 
						|
static int
 | 
						|
xhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize)
 | 
						|
{
 | 
						|
	int maxpsize = ep->maxpacketsize;
 | 
						|
	if (maxpsize == 0)
 | 
						|
		fatal("MaxPacketSize == 0!!!");
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
/* create and hook-up an intr queue into device schedule */
 | 
						|
static void*
 | 
						|
xhci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming)
 | 
						|
{
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/* remove queue from device schedule, dropping all data that came in */
 | 
						|
static void
 | 
						|
xhci_destroy_intr_queue (endpoint_t *ep, void *q_)
 | 
						|
{
 | 
						|
	//free(q);
 | 
						|
}
 | 
						|
 | 
						|
/* read one intr-packet from queue, if available. extend the queue for new input.
 | 
						|
   return NULL if nothing new available.
 | 
						|
   Recommended use: while (data=poll_intr_queue(q)) process(data);
 | 
						|
 */
 | 
						|
static u8*
 | 
						|
xhci_poll_intr_queue (void *q_)
 | 
						|
{
 | 
						|
	return NULL;
 | 
						|
}
 |