Commit 5853e133 authored by Vivek Gautam's avatar Vivek Gautam Committed by Marek Vasut
Browse files

USB: xHCI: Add stack support for xHCI



This adds stack layer for eXtensible Host Controller Interface
which facilitates use of USB 3.0 in host mode.

Adapting xHCI host controller driver in linux-kernel
by Sarah Sharp to needs in u-boot.

Initial porting from Linux kernel version 3.4, with following
top commit history of drivers/usb/host/xhci* :
cf84055 xHCI: Cleanup isoc transfer ring when TD length mismatch found

This adds the basic xHCI host controller driver with bare minimum
features:
- Control/Bulk transfer support has been added with required
  infrastructure for necessary xHC data structures.
- Stream protocol hasn't been supported yet.
- No support for quirky devices has been added.
Signed-off-by: default avatarVikas C Sajjan <vikas.sajjan@samsung.com>
Signed-off-by: default avatarJulius Werner <jwerner@chromium.org>
Signed-off-by: default avatarVivek Gautam <gautam.vivek@samsung.com>
Cc: Simon Glass <sjg@chromium.org>
Cc: Minkyu Kang <mk7.kang@samsung.com>
Cc: Dan Murphy <dmurphy@ti.com>
Cc: Marek Vasut <marex@denx.de>
parent e3d7440c
......@@ -854,6 +854,16 @@ void usb_free_device(void)
usb_dev[dev_index].devnum = -1;
}
/*
* XHCI issues Enable Slot command and thereafter
* allocates device contexts. Provide a weak alias
* function for the purpose, so that XHCI overrides it
* and EHCI/OHCI just work out of the box.
*/
__weak int usb_alloc_device(struct usb_device *udev)
{
return 0;
}
/*
* By the time we get here, the device has gotten a new device ID
* and is in the default state. We need to identify the thing and
......@@ -867,6 +877,17 @@ int usb_new_device(struct usb_device *dev)
int tmp;
ALLOC_CACHE_ALIGN_BUFFER(unsigned char, tmpbuf, USB_BUFSIZ);
/*
* Allocate usb 3.0 device context.
* USB 3.0 (xHCI) protocol tries to allocate device slot
* and related data structures first. This call does that.
* Refer to sec 4.3.2 in xHCI spec rev1.0
*/
if (usb_alloc_device(dev)) {
printf("Cannot allocate device context to get SLOT_ID\n");
return -1;
}
/* We still haven't set the Address yet */
addr = dev->devnum;
dev->devnum = 0;
......@@ -897,7 +918,7 @@ int usb_new_device(struct usb_device *dev)
* http://sourceforge.net/mailarchive/forum.php?
* thread_id=5729457&forum_id=5398
*/
struct usb_device_descriptor *desc;
__maybe_unused struct usb_device_descriptor *desc;
int port = -1;
struct usb_device *parent = dev->parent;
unsigned short portstatus;
......@@ -914,6 +935,13 @@ int usb_new_device(struct usb_device *dev)
dev->epmaxpacketin[0] = 64;
dev->epmaxpacketout[0] = 64;
/*
* XHCI needs to issue a Address device command to setup
* proper device context structures, before it can interact
* with the device. So a get_descriptor will fail before any
* of that is done for XHCI unlike EHCI.
*/
#ifndef CONFIG_USB_XHCI
err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, 64);
if (err < 0) {
debug("usb_new_device: usb_get_descriptor() failed\n");
......@@ -926,11 +954,12 @@ int usb_new_device(struct usb_device *dev)
* to differentiate between HUB and DEVICE.
*/
dev->descriptor.bDeviceClass = desc->bDeviceClass;
#endif
/* find the port number we're at */
if (parent) {
int j;
/* find the port number we're at */
for (j = 0; j < parent->maxchild; j++) {
if (parent->children[j] == dev) {
port = j;
......
......@@ -42,6 +42,9 @@ COBJS-$(CONFIG_USB_EHCI_SPEAR) += ehci-spear.o
COBJS-$(CONFIG_USB_EHCI_TEGRA) += ehci-tegra.o
COBJS-$(CONFIG_USB_EHCI_VCT) += ehci-vct.o
# xhci
COBJS-$(CONFIG_USB_XHCI) += xhci.o xhci-mem.o xhci-ring.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
......
/*
* USB HOST XHCI Controller stack
*
* Based on xHCI host controller driver in linux-kernel
* by Sarah Sharp.
*
* Copyright (C) 2008 Intel Corp.
* Author: Sarah Sharp
*
* Copyright (C) 2013 Samsung Electronics Co.Ltd
* Authors: Vivek Gautam <gautam.vivek@samsung.com>
* Vikas Sajjan <vikas.sajjan@samsung.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/byteorder.h>
#include <usb.h>
#include <malloc.h>
#include <asm/cache.h>
#include <asm-generic/errno.h>
#include "xhci.h"
#define CACHELINE_SIZE CONFIG_SYS_CACHELINE_SIZE
/**
* flushes the address passed till the length
*
* @param addr pointer to memory region to be flushed
* @param len the length of the cache line to be flushed
* @return none
*/
void xhci_flush_cache(uint32_t addr, u32 len)
{
BUG_ON((void *)addr == NULL || len == 0);
flush_dcache_range(addr & ~(CACHELINE_SIZE - 1),
ALIGN(addr + len, CACHELINE_SIZE));
}
/**
* invalidates the address passed till the length
*
* @param addr pointer to memory region to be invalidates
* @param len the length of the cache line to be invalidated
* @return none
*/
void xhci_inval_cache(uint32_t addr, u32 len)
{
BUG_ON((void *)addr == NULL || len == 0);
invalidate_dcache_range(addr & ~(CACHELINE_SIZE - 1),
ALIGN(addr + len, CACHELINE_SIZE));
}
/**
* frees the "segment" pointer passed
*
* @param ptr pointer to "segement" to be freed
* @return none
*/
static void xhci_segment_free(struct xhci_segment *seg)
{
free(seg->trbs);
seg->trbs = NULL;
free(seg);
}
/**
* frees the "ring" pointer passed
*
* @param ptr pointer to "ring" to be freed
* @return none
*/
static void xhci_ring_free(struct xhci_ring *ring)
{
struct xhci_segment *seg;
struct xhci_segment *first_seg;
BUG_ON(!ring);
first_seg = ring->first_seg;
seg = first_seg->next;
while (seg != first_seg) {
struct xhci_segment *next = seg->next;
xhci_segment_free(seg);
seg = next;
}
xhci_segment_free(first_seg);
free(ring);
}
/**
* frees the "xhci_container_ctx" pointer passed
*
* @param ptr pointer to "xhci_container_ctx" to be freed
* @return none
*/
static void xhci_free_container_ctx(struct xhci_container_ctx *ctx)
{
free(ctx->bytes);
free(ctx);
}
/**
* frees the virtual devices for "xhci_ctrl" pointer passed
*
* @param ptr pointer to "xhci_ctrl" whose virtual devices are to be freed
* @return none
*/
static void xhci_free_virt_devices(struct xhci_ctrl *ctrl)
{
int i;
int slot_id;
struct xhci_virt_device *virt_dev;
/*
* refactored here to loop through all virt_dev
* Slot ID 0 is reserved
*/
for (slot_id = 0; slot_id < MAX_HC_SLOTS; slot_id++) {
virt_dev = ctrl->devs[slot_id];
if (!virt_dev)
continue;
ctrl->dcbaa->dev_context_ptrs[slot_id] = 0;
for (i = 0; i < 31; ++i)
if (virt_dev->eps[i].ring)
xhci_ring_free(virt_dev->eps[i].ring);
if (virt_dev->in_ctx)
xhci_free_container_ctx(virt_dev->in_ctx);
if (virt_dev->out_ctx)
xhci_free_container_ctx(virt_dev->out_ctx);
free(virt_dev);
/* make sure we are pointing to NULL */
ctrl->devs[slot_id] = NULL;
}
}
/**
* frees all the memory allocated
*
* @param ptr pointer to "xhci_ctrl" to be cleaned up
* @return none
*/
void xhci_cleanup(struct xhci_ctrl *ctrl)
{
xhci_ring_free(ctrl->event_ring);
xhci_ring_free(ctrl->cmd_ring);
xhci_free_virt_devices(ctrl);
free(ctrl->erst.entries);
free(ctrl->dcbaa);
memset(ctrl, '\0', sizeof(struct xhci_ctrl));
}
/**
* Malloc the aligned memory
*
* @param size size of memory to be allocated
* @return allocates the memory and returns the aligned pointer
*/
static void *xhci_malloc(unsigned int size)
{
void *ptr;
size_t cacheline_size = max(XHCI_ALIGNMENT, CACHELINE_SIZE);
ptr = memalign(cacheline_size, ALIGN(size, cacheline_size));
BUG_ON(!ptr);
memset(ptr, '\0', size);
xhci_flush_cache((uint32_t)ptr, size);
return ptr;
}
/**
* Make the prev segment point to the next segment.
* Change the last TRB in the prev segment to be a Link TRB which points to the
* address of the next segment. The caller needs to set any Link TRB
* related flags, such as End TRB, Toggle Cycle, and no snoop.
*
* @param prev pointer to the previous segment
* @param next pointer to the next segment
* @param link_trbs flag to indicate whether to link the trbs or NOT
* @return none
*/
static void xhci_link_segments(struct xhci_segment *prev,
struct xhci_segment *next, bool link_trbs)
{
u32 val;
u64 val_64 = 0;
if (!prev || !next)
return;
prev->next = next;
if (link_trbs) {
val_64 = (uintptr_t)next->trbs;
prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr = val_64;
/*
* Set the last TRB in the segment to
* have a TRB type ID of Link TRB
*/
val = le32_to_cpu(prev->trbs[TRBS_PER_SEGMENT-1].link.control);
val &= ~TRB_TYPE_BITMASK;
val |= (TRB_LINK << TRB_TYPE_SHIFT);
prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val);
}
}
/**
* Initialises the Ring's enqueue,dequeue,enq_seg pointers
*
* @param ring pointer to the RING to be intialised
* @return none
*/
static void xhci_initialize_ring_info(struct xhci_ring *ring)
{
/*
* The ring is empty, so the enqueue pointer == dequeue pointer
*/
ring->enqueue = ring->first_seg->trbs;
ring->enq_seg = ring->first_seg;
ring->dequeue = ring->enqueue;
ring->deq_seg = ring->first_seg;
/*
* The ring is initialized to 0. The producer must write 1 to the
* cycle bit to handover ownership of the TRB, so PCS = 1.
* The consumer must compare CCS to the cycle bit to
* check ownership, so CCS = 1.
*/
ring->cycle_state = 1;
}
/**
* Allocates a generic ring segment from the ring pool, sets the dma address,
* initializes the segment to zero, and sets the private next pointer to NULL.
* Section 4.11.1.1:
* "All components of all Command and Transfer TRBs shall be initialized to '0'"
*
* @param none
* @return pointer to the newly allocated SEGMENT
*/
static struct xhci_segment *xhci_segment_alloc(void)
{
struct xhci_segment *seg;
seg = (struct xhci_segment *)malloc(sizeof(struct xhci_segment));
BUG_ON(!seg);
seg->trbs = (union xhci_trb *)xhci_malloc(SEGMENT_SIZE);
seg->next = NULL;
return seg;
}
/**
* Create a new ring with zero or more segments.
* TODO: current code only uses one-time-allocated single-segment rings
* of 1KB anyway, so we might as well get rid of all the segment and
* linking code (and maybe increase the size a bit, e.g. 4KB).
*
*
* Link each segment together into a ring.
* Set the end flag and the cycle toggle bit on the last segment.
* See section 4.9.2 and figures 15 and 16 of XHCI spec rev1.0.
*
* @param num_segs number of segments in the ring
* @param link_trbs flag to indicate whether to link the trbs or NOT
* @return pointer to the newly created RING
*/
struct xhci_ring *xhci_ring_alloc(unsigned int num_segs, bool link_trbs)
{
struct xhci_ring *ring;
struct xhci_segment *prev;
ring = (struct xhci_ring *)malloc(sizeof(struct xhci_ring));
BUG_ON(!ring);
if (num_segs == 0)
return ring;
ring->first_seg = xhci_segment_alloc();
BUG_ON(!ring->first_seg);
num_segs--;
prev = ring->first_seg;
while (num_segs > 0) {
struct xhci_segment *next;
next = xhci_segment_alloc();
BUG_ON(!next);
xhci_link_segments(prev, next, link_trbs);
prev = next;
num_segs--;
}
xhci_link_segments(prev, ring->first_seg, link_trbs);
if (link_trbs) {
/* See section 4.9.2.1 and 6.4.4.1 */
prev->trbs[TRBS_PER_SEGMENT-1].link.control |=
cpu_to_le32(LINK_TOGGLE);
}
xhci_initialize_ring_info(ring);
return ring;
}
/**
* Allocates the Container context
*
* @param ctrl Host controller data structure
* @param type type of XHCI Container Context
* @return NULL if failed else pointer to the context on success
*/
static struct xhci_container_ctx
*xhci_alloc_container_ctx(struct xhci_ctrl *ctrl, int type)
{
struct xhci_container_ctx *ctx;
ctx = (struct xhci_container_ctx *)
malloc(sizeof(struct xhci_container_ctx));
BUG_ON(!ctx);
BUG_ON((type != XHCI_CTX_TYPE_DEVICE) && (type != XHCI_CTX_TYPE_INPUT));
ctx->type = type;
ctx->size = (MAX_EP_CTX_NUM + 1) *
CTX_SIZE(readl(&ctrl->hccr->cr_hccparams));
if (type == XHCI_CTX_TYPE_INPUT)
ctx->size += CTX_SIZE(readl(&ctrl->hccr->cr_hccparams));
ctx->bytes = (u8 *)xhci_malloc(ctx->size);
return ctx;
}
/**
* Allocating virtual device
*
* @param udev pointer to USB deivce structure
* @return 0 on success else -1 on failure
*/
int xhci_alloc_virt_device(struct usb_device *udev)
{
u64 byte_64 = 0;
unsigned int slot_id = udev->slot_id;
struct xhci_virt_device *virt_dev;
struct xhci_ctrl *ctrl = udev->controller;
/* Slot ID 0 is reserved */
if (ctrl->devs[slot_id]) {
printf("Virt dev for slot[%d] already allocated\n", slot_id);
return -EEXIST;
}
ctrl->devs[slot_id] = (struct xhci_virt_device *)
malloc(sizeof(struct xhci_virt_device));
if (!ctrl->devs[slot_id]) {
puts("Failed to allocate virtual device\n");
return -ENOMEM;
}
memset(ctrl->devs[slot_id], 0, sizeof(struct xhci_virt_device));
virt_dev = ctrl->devs[slot_id];
/* Allocate the (output) device context that will be used in the HC. */
virt_dev->out_ctx = xhci_alloc_container_ctx(ctrl,
XHCI_CTX_TYPE_DEVICE);
if (!virt_dev->out_ctx) {
puts("Failed to allocate out context for virt dev\n");
return -ENOMEM;
}
/* Allocate the (input) device context for address device command */
virt_dev->in_ctx = xhci_alloc_container_ctx(ctrl,
XHCI_CTX_TYPE_INPUT);
if (!virt_dev->in_ctx) {
puts("Failed to allocate in context for virt dev\n");
return -ENOMEM;
}
/* Allocate endpoint 0 ring */
virt_dev->eps[0].ring = xhci_ring_alloc(1, true);
byte_64 = (uintptr_t)(virt_dev->out_ctx->bytes);
/* Point to output device context in dcbaa. */
ctrl->dcbaa->dev_context_ptrs[slot_id] = byte_64;
xhci_flush_cache((uint32_t)&ctrl->dcbaa->dev_context_ptrs[slot_id],
sizeof(__le64));
return 0;
}
/**
* Allocates the necessary data structures
* for XHCI host controller
*
* @param ctrl Host controller data structure
* @param hccr pointer to HOST Controller Control Registers
* @param hcor pointer to HOST Controller Operational Registers
* @return 0 if successful else -1 on failure
*/
int xhci_mem_init(struct xhci_ctrl *ctrl, struct xhci_hccr *hccr,
struct xhci_hcor *hcor)
{
uint64_t val_64;
uint64_t trb_64;
uint32_t val;
unsigned long deq;
int i;
struct xhci_segment *seg;
/* DCBAA initialization */
ctrl->dcbaa = (struct xhci_device_context_array *)
xhci_malloc(sizeof(struct xhci_device_context_array));
if (ctrl->dcbaa == NULL) {
puts("unable to allocate DCBA\n");
return -ENOMEM;
}
val_64 = (uintptr_t)ctrl->dcbaa;
/* Set the pointer in DCBAA register */
xhci_writeq(&hcor->or_dcbaap, val_64);
/* Command ring control pointer register initialization */
ctrl->cmd_ring = xhci_ring_alloc(1, true);
/* Set the address in the Command Ring Control register */
trb_64 = (uintptr_t)ctrl->cmd_ring->first_seg->trbs;
val_64 = xhci_readq(&hcor->or_crcr);
val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) |
(trb_64 & (u64) ~CMD_RING_RSVD_BITS) |
ctrl->cmd_ring->cycle_state;
xhci_writeq(&hcor->or_crcr, val_64);
/* write the address of db register */
val = xhci_readl(&hccr->cr_dboff);
val &= DBOFF_MASK;
ctrl->dba = (struct xhci_doorbell_array *)((char *)hccr + val);
/* write the address of runtime register */
val = xhci_readl(&hccr->cr_rtsoff);
val &= RTSOFF_MASK;
ctrl->run_regs = (struct xhci_run_regs *)((char *)hccr + val);
/* writting the address of ir_set structure */
ctrl->ir_set = &ctrl->run_regs->ir_set[0];
/* Event ring does not maintain link TRB */
ctrl->event_ring = xhci_ring_alloc(ERST_NUM_SEGS, false);
ctrl->erst.entries = (struct xhci_erst_entry *)
xhci_malloc(sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS);
ctrl->erst.num_entries = ERST_NUM_SEGS;
for (val = 0, seg = ctrl->event_ring->first_seg;
val < ERST_NUM_SEGS;
val++) {
trb_64 = 0;
trb_64 = (uintptr_t)seg->trbs;
struct xhci_erst_entry *entry = &ctrl->erst.entries[val];
xhci_writeq(&entry->seg_addr, trb_64);
entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT);
entry->rsvd = 0;
seg = seg->next;
}
xhci_flush_cache((uint32_t)ctrl->erst.entries,
ERST_NUM_SEGS * sizeof(struct xhci_erst_entry));
deq = (unsigned long)ctrl->event_ring->dequeue;
/* Update HC event ring dequeue pointer */
xhci_writeq(&ctrl->ir_set->erst_dequeue,
(u64)deq & (u64)~ERST_PTR_MASK);
/* set ERST count with the number of entries in the segment table */
val = xhci_readl(&ctrl->ir_set->erst_size);
val &= ERST_SIZE_MASK;
val |= ERST_NUM_SEGS;
xhci_writel(&ctrl->ir_set->erst_size, val);
/* this is the event ring segment table pointer */
val_64 = xhci_readq(&ctrl->ir_set->erst_base);
val_64 &= ERST_PTR_MASK;
val_64 |= ((u32)(ctrl->erst.entries) & ~ERST_PTR_MASK);
xhci_writeq(&ctrl->ir_set->erst_base, val_64);
/* initializing the virtual devices to NULL */
for (i = 0; i < MAX_HC_SLOTS; ++i)
ctrl->devs[i] = NULL;
/*
* Just Zero'ing this register completely,
* or some spurious Device Notification Events
* might screw things here.
*/
xhci_writel(&hcor->or_dnctrl, 0x0);
return 0;
}
/**
* Give the input control context for the passed container context
*
* @param ctx pointer to the context
* @return pointer to the Input control context data
*/
struct xhci_input_control_ctx
*xhci_get_input_control_ctx(struct xhci_container_ctx *ctx)
{
BUG_ON(ctx->type != XHCI_CTX_TYPE_INPUT);
return (struct xhci_input_control_ctx *)ctx->bytes;
}
/**
* Give the slot context for the passed container context
*
* @param ctrl Host controller data structure
* @param ctx pointer to the context
* @return pointer to the slot control context data
*/
struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_ctrl *ctrl,
struct xhci_container_ctx *ctx)
{
if (ctx->type == XHCI_CTX_TYPE_DEVICE)
return (struct xhci_slot_ctx *)ctx->bytes;
return (struct xhci_slot_ctx *)
(ctx->bytes + CTX_SIZE(readl(&ctrl->hccr->cr_hccparams)));