2010-09-25 19:01:13 +02:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Copyright (C) 2010 coresystems GmbH
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2013-03-25 23:56:08 +01:00
|
|
|
//#define USB_DEBUG
|
|
|
|
|
2020-09-02 05:37:49 +02:00
|
|
|
#include <inttypes.h>
|
2010-09-25 19:01:13 +02:00
|
|
|
#include <libpayload.h>
|
2014-08-25 08:07:43 +02:00
|
|
|
#include <arch/barrier.h>
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
#include <arch/cache.h>
|
2010-09-25 19:01:13 +02:00
|
|
|
#include "ehci.h"
|
|
|
|
#include "ehci_private.h"
|
|
|
|
|
2011-04-14 21:52:04 +02:00
|
|
|
static void dump_td(u32 addr)
|
|
|
|
{
|
2010-09-25 19:01:13 +02:00
|
|
|
qtd_t *td = phys_to_virt(addr);
|
2012-12-19 09:32:32 +01:00
|
|
|
usb_debug("+---------------------------------------------------+\n");
|
|
|
|
if (((td->token & (3UL << 8)) >> 8) == 2)
|
|
|
|
usb_debug("|..[SETUP]..........................................|\n");
|
|
|
|
else if (((td->token & (3UL << 8)) >> 8) == 1)
|
|
|
|
usb_debug("|..[IN].............................................|\n");
|
|
|
|
else if (((td->token & (3UL << 8)) >> 8) == 0)
|
|
|
|
usb_debug("|..[OUT]............................................|\n");
|
|
|
|
else
|
|
|
|
usb_debug("|..[]...............................................|\n");
|
2020-09-02 05:37:49 +02:00
|
|
|
usb_debug("|:|============ EHCI TD at [0x%08"PRIx32"] ==========|:|\n", addr);
|
|
|
|
usb_debug("|:| ERRORS = [%"PRId32"] | TOKEN = [0x%08"PRIx32"] | |:|\n",
|
2012-12-19 09:32:32 +01:00
|
|
|
3 - ((td->token & QTD_CERR_MASK) >> QTD_CERR_SHIFT), td->token);
|
|
|
|
usb_debug("|:+-----------------------------------------------+:|\n");
|
2020-09-02 05:37:49 +02:00
|
|
|
usb_debug("|:| Next qTD [0x%08"PRIx32"] |:|\n", td->next_qtd);
|
2012-12-19 09:32:32 +01:00
|
|
|
usb_debug("|:+-----------------------------------------------+:|\n");
|
2020-09-02 05:37:49 +02:00
|
|
|
usb_debug("|:| Alt. Next qTD [0x%08"PRIx32"] |:|\n", td->alt_next_qtd);
|
2012-12-19 09:32:32 +01:00
|
|
|
usb_debug("|:+-----------------------------------------------+:|\n");
|
2020-09-02 05:37:49 +02:00
|
|
|
usb_debug("|:| | Bytes to Transfer |[%05"PRId32"] |:|\n", (td->token & QTD_TOTAL_LEN_MASK) >> 16);
|
2012-12-19 09:32:32 +01:00
|
|
|
usb_debug("|:| | PID CODE: | [%ld] |:|\n", (td->token & (3UL << 8)) >> 8);
|
|
|
|
usb_debug("|:| | Interrupt On Complete (IOC) | [%ld] |:|\n", (td->token & (1UL << 15)) >> 15);
|
|
|
|
usb_debug("|:| | Status Active | [%ld] |:|\n", (td->token & (1UL << 7)) >> 7);
|
|
|
|
usb_debug("|:| | Status Halted | [%ld] |:|\n", (td->token & (1UL << 6)) >> 6);
|
|
|
|
usb_debug("|:| TOKEN | Status Data Buffer Error | [%ld] |:|\n", (td->token & (1UL << 5)) >> 5);
|
|
|
|
usb_debug("|:| | Status Babble detected | [%ld] |:|\n", (td->token & (1UL << 4)) >> 4);
|
|
|
|
usb_debug("|:| | Status Transaction Error | [%ld] |:|\n", (td->token & (1UL << 3)) >> 3);
|
|
|
|
usb_debug("|:| | Status Missed Micro Frame | [%ld] |:|\n", (td->token & (1UL << 2)) >> 2);
|
|
|
|
usb_debug("|:| | Split Transaction State | [%ld] |:|\n", (td->token & (1UL << 1)) >> 1);
|
|
|
|
usb_debug("|:| | Ping State | [%ld] |:|\n", td->token & 1UL);
|
|
|
|
usb_debug("|:|-----------------------------------------------|:|\n");
|
|
|
|
usb_debug("|...................................................|\n");
|
|
|
|
usb_debug("+---------------------------------------------------+\n");
|
|
|
|
}
|
|
|
|
|
2013-05-03 01:16:41 +02:00
|
|
|
#if 0 && defined(USB_DEBUG)
|
2012-12-19 09:32:32 +01:00
|
|
|
static void dump_qh(ehci_qh_t *cur)
|
|
|
|
{
|
|
|
|
qtd_t *tmp_qtd = NULL;
|
|
|
|
usb_debug("+===================================================+\n");
|
|
|
|
usb_debug("| ############# EHCI QH at [0x%08lx] ########### |\n", virt_to_phys(cur));
|
|
|
|
usb_debug("+---------------------------------------------------+\n");
|
2020-02-15 09:27:11 +01:00
|
|
|
usb_debug("| Horizontal Link Pointer [0x%08lx] |\n", cur->horiz_link_ptr);
|
2012-12-19 09:32:32 +01:00
|
|
|
usb_debug("+------------------[ 0x%08lx ]-------------------+\n", cur->epchar);
|
|
|
|
usb_debug("| | Maximum Packet Length | [%04ld] |\n", ((cur->epchar & (0x7ffUL << 16)) >> 16));
|
|
|
|
usb_debug("| | Device Address | [%ld] |\n", cur->epchar & 0x7F);
|
|
|
|
usb_debug("| | Inactivate on Next Transaction | [%ld] |\n", ((cur->epchar & (1UL << 7)) >> 7));
|
|
|
|
usb_debug("| | Endpoint Number | [%ld] |\n", ((cur->epchar & (0xFUL << 8)) >> 8));
|
|
|
|
usb_debug("| EPCHAR | Endpoint Speed | [%ld] |\n", ((cur->epchar & (3UL << 12)) >> 12));
|
|
|
|
usb_debug("| | Data Toggle Control | [%ld] |\n", ((cur->epchar & (1UL << 14)) >> 14));
|
|
|
|
usb_debug("| | Head of Reclamation List Flag | [%ld] |\n", ((cur->epchar & (1UL << 15)) >> 15));
|
|
|
|
usb_debug("| | Control Endpoint Flag | [%ld] |\n", ((cur->epchar & (1UL << 27)) >> 27));
|
|
|
|
usb_debug("| | Nak Count Reload | [%ld] |\n", ((cur->epchar & (0xFUL << 28)) >> 28));
|
|
|
|
if (((cur->epchar & (1UL << QH_NON_HS_CTRL_EP_SHIFT)) >> QH_NON_HS_CTRL_EP_SHIFT) == 1) { /* Split transaction */
|
|
|
|
usb_debug("+--------+---------[ 0x%08lx ]----------+--------+\n", cur->epcaps);
|
|
|
|
usb_debug("| | Hub Port | [%ld] |\n", ((cur->epcaps & (0x7FUL << 23)) >> 23)); /* [29:23] */
|
|
|
|
usb_debug("| | Hub Address | [%ld] |\n", ((cur->epcaps & (0x7FUL << 16)) >> 16)); /* [22:16] */
|
|
|
|
}
|
|
|
|
usb_debug("+---------------------------------------------------+\n");
|
|
|
|
usb_debug("| Current QTD [0x%08lx] |\n", cur->current_td_ptr);
|
|
|
|
|
|
|
|
if (!((cur->horiz_link_ptr == 0) && (cur->epchar == 0))) {
|
|
|
|
/* Dump overlay QTD for this QH */
|
|
|
|
usb_debug("+---------------------------------------------------+\n");
|
|
|
|
usb_debug("|::::::::::::::::::: QTD OVERLAY :::::::::::::::::::|\n");
|
|
|
|
dump_td(virt_to_phys((void *)&(cur->td)));
|
|
|
|
/* Dump all TD tree for this QH */
|
|
|
|
tmp_qtd = (qtd_t *)phys_to_virt((cur->td.next_qtd & ~0x1FUL));
|
|
|
|
if (tmp_qtd != NULL)
|
|
|
|
usb_debug("|:::::::::::::::::: EHCI QTD CHAIN :::::::::::::::::|\n");
|
|
|
|
while (tmp_qtd != NULL)
|
|
|
|
{
|
|
|
|
dump_td(virt_to_phys(tmp_qtd));
|
|
|
|
tmp_qtd = (qtd_t *)phys_to_virt((tmp_qtd->next_qtd & ~0x1FUL));
|
|
|
|
}
|
|
|
|
usb_debug("|:::::::::::::::: EOF EHCI QTD CHAIN :::::::::::::::|\n");
|
|
|
|
usb_debug("+---------------------------------------------------+\n");
|
|
|
|
} else {
|
|
|
|
usb_debug("+---------------------------------------------------+\n");
|
|
|
|
}
|
2010-09-25 19:01:13 +02:00
|
|
|
}
|
2013-03-25 23:56:08 +01:00
|
|
|
#endif
|
2010-09-25 19:01:13 +02:00
|
|
|
|
2022-08-10 08:59:18 +02:00
|
|
|
static void ehci_start(hci_t *controller)
|
2010-09-25 19:01:13 +02:00
|
|
|
{
|
2011-11-24 13:19:57 +01:00
|
|
|
EHCI_INST(controller)->operation->usbcmd |= HC_OP_RS;
|
2010-09-25 19:01:13 +02:00
|
|
|
}
|
|
|
|
|
2022-08-10 08:59:18 +02:00
|
|
|
static void ehci_stop(hci_t *controller)
|
2010-09-25 19:01:13 +02:00
|
|
|
{
|
2011-11-24 13:19:57 +01:00
|
|
|
EHCI_INST(controller)->operation->usbcmd &= ~HC_OP_RS;
|
2010-09-25 19:01:13 +02:00
|
|
|
}
|
|
|
|
|
2022-08-10 08:59:18 +02:00
|
|
|
static void ehci_reset(hci_t *controller)
|
2010-09-25 19:01:13 +02:00
|
|
|
{
|
2012-11-09 14:06:05 +01:00
|
|
|
short count = 0;
|
|
|
|
ehci_stop(controller);
|
2020-02-15 09:27:11 +01:00
|
|
|
/* wait 10 ms just to be sure */
|
2012-11-09 14:06:05 +01:00
|
|
|
mdelay(10);
|
|
|
|
if (EHCI_INST(controller)->operation->usbsts & HC_OP_HC_HALTED) {
|
|
|
|
EHCI_INST(controller)->operation->usbcmd = HC_OP_HC_RESET;
|
|
|
|
/* wait 100 ms */
|
|
|
|
for (count = 0; count < 10; count++) {
|
|
|
|
mdelay(10);
|
|
|
|
if (!(EHCI_INST(controller)->operation->usbcmd & HC_OP_HC_RESET)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
usb_debug("ehci_reset(): reset failed!\n");
|
2010-09-25 19:01:13 +02:00
|
|
|
}
|
|
|
|
|
2022-08-10 08:59:18 +02:00
|
|
|
static void ehci_reinit(hci_t *controller)
|
2012-11-12 16:20:32 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2012-05-25 10:09:13 +02:00
|
|
|
static int ehci_set_periodic_schedule(ehci_t *ehcic, int enable)
|
|
|
|
{
|
|
|
|
/* Set periodic schedule status. */
|
|
|
|
if (enable)
|
|
|
|
ehcic->operation->usbcmd |= HC_OP_PERIODIC_SCHED_EN;
|
|
|
|
else
|
|
|
|
ehcic->operation->usbcmd &= ~HC_OP_PERIODIC_SCHED_EN;
|
|
|
|
/* Wait for the controller to accept periodic schedule status.
|
|
|
|
* This shouldn't take too long, but we should timeout nevertheless.
|
|
|
|
*/
|
|
|
|
enable = enable ? HC_OP_PERIODIC_SCHED_STAT : 0;
|
2012-11-21 11:16:13 +01:00
|
|
|
int timeout = 100000; /* time out after 100ms */
|
2012-05-25 10:09:13 +02:00
|
|
|
while (((ehcic->operation->usbsts & HC_OP_PERIODIC_SCHED_STAT) != enable)
|
|
|
|
&& timeout--)
|
2012-11-21 11:16:13 +01:00
|
|
|
udelay(1);
|
2012-05-25 10:09:13 +02:00
|
|
|
if (timeout < 0) {
|
2012-11-01 23:44:10 +01:00
|
|
|
usb_debug("ehci periodic schedule status change timed out.\n");
|
2012-05-25 10:09:13 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-08-10 08:59:18 +02:00
|
|
|
static void ehci_shutdown(hci_t *controller)
|
2010-09-25 19:01:13 +02:00
|
|
|
{
|
2013-03-24 19:40:01 +01:00
|
|
|
detach_controller(controller);
|
2014-04-08 21:54:25 +02:00
|
|
|
|
2012-05-25 10:09:13 +02:00
|
|
|
/* Make sure periodic schedule is disabled */
|
|
|
|
ehci_set_periodic_schedule(EHCI_INST(controller), 0);
|
2012-06-14 13:27:39 +02:00
|
|
|
|
2014-04-08 21:54:25 +02:00
|
|
|
/* Give all ports back to companion controller */
|
2011-11-04 11:57:46 +01:00
|
|
|
EHCI_INST(controller)->operation->configflag = 0;
|
2012-11-12 15:35:44 +01:00
|
|
|
|
2014-04-08 21:54:25 +02:00
|
|
|
/* Free all dynamic allocations */
|
|
|
|
free(EHCI_INST(controller)->dma_buffer);
|
|
|
|
free(phys_to_virt(EHCI_INST(controller)->operation->periodiclistbase));
|
|
|
|
free((void *)EHCI_INST(controller)->dummy_qh);
|
2012-11-12 15:35:44 +01:00
|
|
|
free(EHCI_INST(controller));
|
|
|
|
free(controller);
|
2010-09-25 19:01:13 +02:00
|
|
|
}
|
|
|
|
|
2022-08-10 08:59:18 +02:00
|
|
|
enum { EHCI_OUT = 0, EHCI_IN = 1, EHCI_SETUP = 2 };
|
2010-09-25 19:01:13 +02:00
|
|
|
|
2011-11-24 13:19:57 +01:00
|
|
|
/* returns handled bytes. assumes that the fields it writes are empty on entry */
|
|
|
|
static int fill_td(qtd_t *td, void* data, int datalen)
|
2011-04-14 21:52:04 +02:00
|
|
|
{
|
2010-09-25 19:01:13 +02:00
|
|
|
u32 total_len = 0;
|
2011-11-24 13:19:57 +01:00
|
|
|
u32 page_no = 0;
|
2010-09-25 19:01:13 +02:00
|
|
|
|
|
|
|
u32 start = virt_to_phys(data);
|
|
|
|
u32 page = start & ~4095;
|
|
|
|
u32 offset = start & 4095;
|
|
|
|
u32 page_len = 4096 - offset;
|
|
|
|
|
2011-11-24 13:19:57 +01:00
|
|
|
td->token |= 0 << QTD_CPAGE_SHIFT;
|
|
|
|
td->bufptrs[page_no++] = start;
|
2010-09-25 19:01:13 +02:00
|
|
|
|
|
|
|
if (datalen <= page_len) {
|
|
|
|
total_len = datalen;
|
|
|
|
} else {
|
|
|
|
datalen -= page_len;
|
|
|
|
total_len += page_len;
|
|
|
|
|
2011-11-24 13:19:57 +01:00
|
|
|
while (page_no < 5) {
|
2020-02-15 09:27:11 +01:00
|
|
|
/* we have a contiguous mapping between virtual and physical memory */
|
2010-09-25 19:01:13 +02:00
|
|
|
page += 4096;
|
|
|
|
|
2011-11-24 13:19:57 +01:00
|
|
|
td->bufptrs[page_no++] = page;
|
2010-09-25 19:01:13 +02:00
|
|
|
if (datalen <= 4096) {
|
|
|
|
total_len += datalen;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
datalen -= 4096;
|
|
|
|
total_len += 4096;
|
2013-01-12 01:25:52 +01:00
|
|
|
|
|
|
|
/* end TD at a packet boundary if transfer not complete */
|
|
|
|
if (page_no == 5)
|
|
|
|
total_len &= ~511;
|
2011-11-24 13:19:57 +01:00
|
|
|
}
|
2010-09-25 19:01:13 +02:00
|
|
|
}
|
2011-11-24 13:19:57 +01:00
|
|
|
td->token |= total_len << QTD_TOTAL_LEN_SHIFT;
|
2010-09-25 19:01:13 +02:00
|
|
|
return total_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free up data structures */
|
2011-11-24 13:19:57 +01:00
|
|
|
static void free_qh_and_tds(ehci_qh_t *qh, qtd_t *cur)
|
2011-04-14 21:52:04 +02:00
|
|
|
{
|
2010-09-25 19:01:13 +02:00
|
|
|
qtd_t *next;
|
|
|
|
while (cur) {
|
|
|
|
next = (qtd_t*)phys_to_virt(cur->next_qtd & ~31);
|
2012-11-23 11:58:57 +01:00
|
|
|
free((void *)cur);
|
2010-09-25 19:01:13 +02:00
|
|
|
cur = next;
|
|
|
|
}
|
2012-11-23 11:58:57 +01:00
|
|
|
free((void *)qh);
|
2010-09-25 19:01:13 +02:00
|
|
|
}
|
|
|
|
|
2019-06-22 00:39:59 +02:00
|
|
|
#define EHCI_SLEEP_TIME_US 50
|
|
|
|
|
2011-11-24 13:19:57 +01:00
|
|
|
static int wait_for_tds(qtd_t *head)
|
2011-04-14 21:52:04 +02:00
|
|
|
{
|
2013-02-21 22:41:40 +01:00
|
|
|
/* returns the amount of bytes *not* transmitted, or -1 for error */
|
2010-09-25 19:01:13 +02:00
|
|
|
int result = 0;
|
|
|
|
qtd_t *cur = head;
|
|
|
|
while (1) {
|
|
|
|
if (0) dump_td(virt_to_phys(cur));
|
2012-05-21 14:53:43 +02:00
|
|
|
|
|
|
|
/* wait for results */
|
2019-06-22 00:39:59 +02:00
|
|
|
int timeout = USB_MAX_PROCESSING_TIME_US / EHCI_SLEEP_TIME_US;
|
2012-05-21 14:53:43 +02:00
|
|
|
while ((cur->token & QTD_ACTIVE) && !(cur->token & QTD_HALTED)
|
|
|
|
&& timeout--)
|
2019-06-22 00:39:59 +02:00
|
|
|
udelay(EHCI_SLEEP_TIME_US);
|
2012-05-21 14:53:43 +02:00
|
|
|
if (timeout < 0) {
|
2012-12-11 21:08:07 +01:00
|
|
|
usb_debug("Error: ehci: queue transfer "
|
2012-05-21 14:53:43 +02:00
|
|
|
"processing timed out.\n");
|
2013-02-21 22:41:40 +01:00
|
|
|
return -1;
|
2012-05-21 14:53:43 +02:00
|
|
|
}
|
2011-11-24 13:19:57 +01:00
|
|
|
if (cur->token & QTD_HALTED) {
|
2012-11-21 16:22:26 +01:00
|
|
|
usb_debug("ERROR with packet\n");
|
2010-09-25 19:01:13 +02:00
|
|
|
dump_td(virt_to_phys(cur));
|
2012-11-01 23:44:10 +01:00
|
|
|
usb_debug("-----------------\n");
|
2013-02-21 22:41:40 +01:00
|
|
|
return -1;
|
2010-09-25 19:01:13 +02:00
|
|
|
}
|
2013-02-21 22:41:40 +01:00
|
|
|
result += (cur->token & QTD_TOTAL_LEN_MASK)
|
|
|
|
>> QTD_TOTAL_LEN_SHIFT;
|
2010-09-25 19:01:13 +02:00
|
|
|
if (cur->next_qtd & 1) {
|
2013-02-21 22:41:40 +01:00
|
|
|
break;
|
2010-09-25 19:01:13 +02:00
|
|
|
}
|
2020-09-02 05:37:49 +02:00
|
|
|
if (0)
|
|
|
|
dump_td(virt_to_phys(cur));
|
2010-09-25 19:01:13 +02:00
|
|
|
/* helps debugging the TD chain */
|
2020-09-02 05:37:49 +02:00
|
|
|
if (0)
|
|
|
|
usb_debug("\nmoving from %p to %p\n", cur, phys_to_virt(cur->next_qtd));
|
2010-09-25 19:01:13 +02:00
|
|
|
cur = phys_to_virt(cur->next_qtd);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2012-05-21 14:53:43 +02:00
|
|
|
static int ehci_set_async_schedule(ehci_t *ehcic, int enable)
|
|
|
|
{
|
2014-08-25 08:07:43 +02:00
|
|
|
|
|
|
|
/* Memory barrier to ensure that all memory accesses before we set the
|
|
|
|
* async schedule are complete. It was observed especially in the case of
|
2020-02-24 13:26:04 +01:00
|
|
|
* arm64, that netboot and USB stuff resulted in lots of errors possibly
|
2014-08-25 08:07:43 +02:00
|
|
|
* due to CPU reordering. Hence, enforcing strict CPU ordering.
|
|
|
|
*/
|
|
|
|
mb();
|
|
|
|
|
2012-05-21 14:53:43 +02:00
|
|
|
/* Set async schedule status. */
|
|
|
|
if (enable)
|
|
|
|
ehcic->operation->usbcmd |= HC_OP_ASYNC_SCHED_EN;
|
|
|
|
else
|
|
|
|
ehcic->operation->usbcmd &= ~HC_OP_ASYNC_SCHED_EN;
|
|
|
|
/* Wait for the controller to accept async schedule status.
|
|
|
|
* This shouldn't take too long, but we should timeout nevertheless.
|
|
|
|
*/
|
|
|
|
enable = enable ? HC_OP_ASYNC_SCHED_STAT : 0;
|
|
|
|
int timeout = 100; /* time out after 100ms */
|
|
|
|
while (((ehcic->operation->usbsts & HC_OP_ASYNC_SCHED_STAT) != enable)
|
|
|
|
&& timeout--)
|
|
|
|
mdelay(1);
|
|
|
|
if (timeout < 0) {
|
2012-11-01 23:44:10 +01:00
|
|
|
usb_debug("ehci async schedule status change timed out.\n");
|
2012-05-21 14:53:43 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ehci_process_async_schedule(
|
|
|
|
ehci_t *ehcic, ehci_qh_t *qhead, qtd_t *head)
|
|
|
|
{
|
|
|
|
int result;
|
|
|
|
|
|
|
|
/* make sure async schedule is disabled */
|
2013-02-21 22:41:40 +01:00
|
|
|
if (ehci_set_async_schedule(ehcic, 0)) return -1;
|
2012-05-21 14:53:43 +02:00
|
|
|
|
|
|
|
/* hook up QH */
|
|
|
|
ehcic->operation->asynclistaddr = virt_to_phys(qhead);
|
|
|
|
|
|
|
|
/* start async schedule */
|
2013-02-21 22:41:40 +01:00
|
|
|
if (ehci_set_async_schedule(ehcic, 1)) return -1;
|
2012-05-21 14:53:43 +02:00
|
|
|
|
|
|
|
/* wait for result */
|
|
|
|
result = wait_for_tds(head);
|
|
|
|
|
|
|
|
/* disable async schedule */
|
|
|
|
ehci_set_async_schedule(ehcic, 0);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-08-10 08:59:18 +02:00
|
|
|
static int ehci_bulk(endpoint_t *ep, int size, u8 *src, int finalize)
|
2010-09-25 19:01:13 +02:00
|
|
|
{
|
|
|
|
int result = 0;
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
u8 *end = src + size;
|
2013-02-21 22:41:40 +01:00
|
|
|
int remaining = size;
|
2010-09-25 19:01:13 +02:00
|
|
|
int endp = ep->endpoint & 0xf;
|
2018-04-26 10:11:10 +02:00
|
|
|
int pid = (ep->direction == IN)?EHCI_IN:EHCI_OUT;
|
2010-09-25 19:01:13 +02:00
|
|
|
|
2012-05-23 09:21:54 +02:00
|
|
|
int hubaddr = 0, hubport = 0;
|
|
|
|
if (ep->dev->speed < 2) {
|
|
|
|
/* we need a split transaction */
|
|
|
|
if (closest_usb2_hub(ep->dev, &hubaddr, &hubport))
|
2013-02-21 22:41:40 +01:00
|
|
|
return -1;
|
2012-05-23 09:21:54 +02:00
|
|
|
}
|
|
|
|
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
if (!dma_coherent(src)) {
|
|
|
|
end = EHCI_INST(ep->dev->controller)->dma_buffer + size;
|
|
|
|
if (size > DMA_SIZE) {
|
|
|
|
usb_debug("EHCI bulk transfer too large for DMA buffer: %d\n", size);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (pid == EHCI_OUT)
|
|
|
|
memcpy(end - size, src, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
ehci_qh_t *qh = dma_memalign(64, sizeof(ehci_qh_t));
|
|
|
|
qtd_t *head = dma_memalign(64, sizeof(qtd_t));
|
2010-09-25 19:01:13 +02:00
|
|
|
qtd_t *cur = head;
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
if (!qh || !head)
|
|
|
|
goto oom;
|
2010-09-25 19:01:13 +02:00
|
|
|
while (1) {
|
2012-11-23 11:58:57 +01:00
|
|
|
memset((void *)cur, 0, sizeof(qtd_t));
|
2011-11-24 13:19:57 +01:00
|
|
|
cur->token = QTD_ACTIVE |
|
|
|
|
(pid << QTD_PID_SHIFT) |
|
|
|
|
(0 << QTD_CERR_SHIFT);
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
remaining -= fill_td(cur, end - remaining, remaining);
|
2010-09-25 19:01:13 +02:00
|
|
|
|
2011-11-24 13:19:57 +01:00
|
|
|
cur->alt_next_qtd = QTD_TERMINATE;
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
if (remaining <= 0) {
|
2011-11-24 13:19:57 +01:00
|
|
|
cur->next_qtd = virt_to_phys(0) | QTD_TERMINATE;
|
2010-09-25 19:01:13 +02:00
|
|
|
break;
|
|
|
|
} else {
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
qtd_t *next = dma_memalign(64, sizeof(qtd_t));
|
|
|
|
if (!next)
|
|
|
|
goto oom;
|
2010-09-25 19:01:13 +02:00
|
|
|
cur->next_qtd = virt_to_phys(next);
|
|
|
|
cur = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* create QH */
|
2012-11-23 11:58:57 +01:00
|
|
|
memset((void *)qh, 0, sizeof(ehci_qh_t));
|
2011-11-24 13:19:57 +01:00
|
|
|
qh->horiz_link_ptr = virt_to_phys(qh) | QH_QH;
|
|
|
|
qh->epchar = ep->dev->address |
|
|
|
|
(endp << QH_EP_SHIFT) |
|
|
|
|
(ep->dev->speed << QH_EPS_SHIFT) |
|
|
|
|
(0 << QH_DTC_SHIFT) |
|
|
|
|
(1 << QH_RECLAIM_HEAD_SHIFT) |
|
|
|
|
(ep->maxpacketsize << QH_MPS_SHIFT) |
|
|
|
|
(0 << QH_NAK_CNT_SHIFT);
|
2012-05-23 09:21:54 +02:00
|
|
|
qh->epcaps = (3 << QH_PIPE_MULTIPLIER_SHIFT) |
|
|
|
|
(hubport << QH_PORT_NUMBER_SHIFT) |
|
|
|
|
(hubaddr << QH_HUB_ADDRESS_SHIFT);
|
2010-09-25 19:01:13 +02:00
|
|
|
|
|
|
|
qh->td.next_qtd = virt_to_phys(head);
|
2011-11-24 13:19:57 +01:00
|
|
|
qh->td.token |= (ep->toggle?QTD_TOGGLE_DATA1:0);
|
|
|
|
head->token |= (ep->toggle?QTD_TOGGLE_DATA1:0);
|
2010-09-25 19:01:13 +02:00
|
|
|
|
2012-05-21 14:53:43 +02:00
|
|
|
result = ehci_process_async_schedule(
|
|
|
|
EHCI_INST(ep->dev->controller), qh, head);
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
if (result >= 0) {
|
2013-02-21 22:41:40 +01:00
|
|
|
result = size - result;
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
if (pid == EHCI_IN && end != src + size)
|
|
|
|
memcpy(src, end - size, result);
|
|
|
|
}
|
2010-09-25 19:01:13 +02:00
|
|
|
|
2011-11-24 13:19:57 +01:00
|
|
|
ep->toggle = (cur->token & QTD_TOGGLE_MASK) >> QTD_TOGGLE_SHIFT;
|
2010-09-25 19:01:13 +02:00
|
|
|
|
|
|
|
free_qh_and_tds(qh, head);
|
2013-02-21 22:41:40 +01:00
|
|
|
|
2010-09-25 19:01:13 +02:00
|
|
|
return result;
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
|
|
|
|
oom:
|
|
|
|
usb_debug("Not enough DMA memory for EHCI control structures!\n");
|
|
|
|
free_qh_and_tds(qh, head);
|
|
|
|
return -1;
|
2010-09-25 19:01:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* FIXME: Handle control transfers as 3 QHs, so the 2nd stage can be >0x4000 bytes */
|
2022-08-10 08:59:18 +02:00
|
|
|
static int ehci_control(usbdev_t *dev, direction_t dir, int drlen, void *setup,
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
int dalen, u8 *src)
|
2010-09-25 19:01:13 +02:00
|
|
|
{
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
u8 *data = src;
|
|
|
|
u8 *devreq = setup;
|
2010-09-25 19:01:13 +02:00
|
|
|
int endp = 0; // this is control. always 0 (for now)
|
|
|
|
int toggle = 0;
|
|
|
|
int mlen = dev->endpoints[0].maxpacketsize;
|
|
|
|
int result = 0;
|
|
|
|
|
2012-05-23 09:21:54 +02:00
|
|
|
int hubaddr = 0, hubport = 0, non_hs_ctrl_ep = 0;
|
|
|
|
if (dev->speed < 2) {
|
|
|
|
/* we need a split transaction */
|
|
|
|
if (closest_usb2_hub(dev, &hubaddr, &hubport))
|
2013-02-21 22:41:40 +01:00
|
|
|
return -1;
|
2012-05-23 09:21:54 +02:00
|
|
|
non_hs_ctrl_ep = 1;
|
|
|
|
}
|
|
|
|
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
if (!dma_coherent(setup)) {
|
|
|
|
devreq = EHCI_INST(dev->controller)->dma_buffer;
|
|
|
|
memcpy(devreq, setup, drlen);
|
|
|
|
}
|
|
|
|
if (dalen > 0 && !dma_coherent(src)) {
|
|
|
|
data = EHCI_INST(dev->controller)->dma_buffer + drlen;
|
|
|
|
if (drlen + dalen > DMA_SIZE) {
|
|
|
|
usb_debug("EHCI control transfer too large for DMA buffer: %d\n", drlen + dalen);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (dir == OUT)
|
|
|
|
memcpy(data, src, dalen);
|
|
|
|
}
|
|
|
|
|
2010-09-25 19:01:13 +02:00
|
|
|
/* create qTDs */
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
qtd_t *head = dma_memalign(64, sizeof(qtd_t));
|
|
|
|
ehci_qh_t *qh = dma_memalign(64, sizeof(ehci_qh_t));
|
2010-09-25 19:01:13 +02:00
|
|
|
qtd_t *cur = head;
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
if (!qh || !head)
|
|
|
|
goto oom;
|
2012-11-23 11:58:57 +01:00
|
|
|
memset((void *)cur, 0, sizeof(qtd_t));
|
2011-11-24 13:19:57 +01:00
|
|
|
cur->token = QTD_ACTIVE |
|
|
|
|
(toggle?QTD_TOGGLE_DATA1:0) |
|
|
|
|
(EHCI_SETUP << QTD_PID_SHIFT) |
|
|
|
|
(3 << QTD_CERR_SHIFT);
|
2010-09-25 19:01:13 +02:00
|
|
|
if (fill_td(cur, devreq, drlen) != drlen) {
|
2012-12-11 21:08:07 +01:00
|
|
|
usb_debug("ERROR: couldn't send the entire device request\n");
|
2010-09-25 19:01:13 +02:00
|
|
|
}
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
qtd_t *next = dma_memalign(64, sizeof(qtd_t));
|
2010-09-25 19:01:13 +02:00
|
|
|
cur->next_qtd = virt_to_phys(next);
|
2011-11-24 13:19:57 +01:00
|
|
|
cur->alt_next_qtd = QTD_TERMINATE;
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
if (!next)
|
|
|
|
goto oom;
|
2010-09-25 19:01:13 +02:00
|
|
|
|
|
|
|
/* FIXME: We're limited to 16-20K (depending on alignment) for payload for now.
|
|
|
|
* Figure out, how toggle can be set sensibly in this scenario */
|
|
|
|
if (dalen > 0) {
|
|
|
|
toggle ^= 1;
|
|
|
|
cur = next;
|
2012-11-23 11:58:57 +01:00
|
|
|
memset((void *)cur, 0, sizeof(qtd_t));
|
2011-11-24 13:19:57 +01:00
|
|
|
cur->token = QTD_ACTIVE |
|
|
|
|
(toggle?QTD_TOGGLE_DATA1:0) |
|
|
|
|
(((dir == OUT)?EHCI_OUT:EHCI_IN) << QTD_PID_SHIFT) |
|
|
|
|
(3 << QTD_CERR_SHIFT);
|
2010-09-25 19:01:13 +02:00
|
|
|
if (fill_td(cur, data, dalen) != dalen) {
|
2012-12-11 21:08:07 +01:00
|
|
|
usb_debug("ERROR: couldn't send the entire control payload\n");
|
2010-09-25 19:01:13 +02:00
|
|
|
}
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
next = dma_memalign(64, sizeof(qtd_t));
|
|
|
|
if (!next)
|
|
|
|
goto oom;
|
2010-09-25 19:01:13 +02:00
|
|
|
cur->next_qtd = virt_to_phys(next);
|
2011-11-24 13:19:57 +01:00
|
|
|
cur->alt_next_qtd = QTD_TERMINATE;
|
2010-09-25 19:01:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
toggle = 1;
|
|
|
|
cur = next;
|
2012-11-23 11:58:57 +01:00
|
|
|
memset((void *)cur, 0, sizeof(qtd_t));
|
2011-11-24 13:19:57 +01:00
|
|
|
cur->token = QTD_ACTIVE |
|
|
|
|
(toggle?QTD_TOGGLE_DATA1:QTD_TOGGLE_DATA0) |
|
|
|
|
((dir == OUT)?EHCI_IN:EHCI_OUT) << QTD_PID_SHIFT |
|
|
|
|
(0 << QTD_CERR_SHIFT);
|
2010-09-25 19:01:13 +02:00
|
|
|
fill_td(cur, NULL, 0);
|
2011-11-24 13:19:57 +01:00
|
|
|
cur->next_qtd = virt_to_phys(0) | QTD_TERMINATE;
|
|
|
|
cur->alt_next_qtd = QTD_TERMINATE;
|
2010-09-25 19:01:13 +02:00
|
|
|
|
|
|
|
/* create QH */
|
2012-11-23 11:58:57 +01:00
|
|
|
memset((void *)qh, 0, sizeof(ehci_qh_t));
|
2011-11-24 13:19:57 +01:00
|
|
|
qh->horiz_link_ptr = virt_to_phys(qh) | QH_QH;
|
|
|
|
qh->epchar = dev->address |
|
|
|
|
(endp << QH_EP_SHIFT) |
|
|
|
|
(dev->speed << QH_EPS_SHIFT) |
|
|
|
|
(1 << QH_DTC_SHIFT) | /* ctrl transfers are special: take toggle bit from TD */
|
|
|
|
(1 << QH_RECLAIM_HEAD_SHIFT) |
|
|
|
|
(mlen << QH_MPS_SHIFT) |
|
2012-05-23 09:21:54 +02:00
|
|
|
(non_hs_ctrl_ep << QH_NON_HS_CTRL_EP_SHIFT) |
|
2011-11-24 13:19:57 +01:00
|
|
|
(0 << QH_NAK_CNT_SHIFT);
|
2012-05-23 09:21:54 +02:00
|
|
|
qh->epcaps = (3 << QH_PIPE_MULTIPLIER_SHIFT) |
|
|
|
|
(hubport << QH_PORT_NUMBER_SHIFT) |
|
|
|
|
(hubaddr << QH_HUB_ADDRESS_SHIFT);
|
2010-09-25 19:01:13 +02:00
|
|
|
qh->td.next_qtd = virt_to_phys(head);
|
|
|
|
|
2012-05-21 14:53:43 +02:00
|
|
|
result = ehci_process_async_schedule(
|
|
|
|
EHCI_INST(dev->controller), qh, head);
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
if (result >= 0) {
|
2013-02-21 22:41:40 +01:00
|
|
|
result = dalen - result;
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
if (dir == IN && data != src)
|
|
|
|
memcpy(src, data, result);
|
|
|
|
}
|
2010-09-25 19:01:13 +02:00
|
|
|
|
|
|
|
free_qh_and_tds(qh, head);
|
|
|
|
return result;
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
|
|
|
|
oom:
|
|
|
|
usb_debug("Not enough DMA memory for EHCI control structures!\n");
|
|
|
|
free_qh_and_tds(qh, head);
|
|
|
|
return -1;
|
2010-09-25 19:01:13 +02:00
|
|
|
}
|
|
|
|
|
2012-05-25 10:09:13 +02:00
|
|
|
typedef struct _intr_qtd_t intr_qtd_t;
|
|
|
|
|
|
|
|
struct _intr_qtd_t {
|
|
|
|
volatile qtd_t td;
|
|
|
|
u8 *data;
|
|
|
|
intr_qtd_t *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
volatile ehci_qh_t qh;
|
|
|
|
intr_qtd_t *head;
|
|
|
|
intr_qtd_t *tail;
|
2012-06-14 13:08:36 +02:00
|
|
|
intr_qtd_t *spare;
|
2012-05-25 10:09:13 +02:00
|
|
|
u8 *data;
|
|
|
|
endpoint_t *endp;
|
|
|
|
int reqsize;
|
|
|
|
} intr_queue_t;
|
|
|
|
|
|
|
|
static void fill_intr_queue_td(
|
|
|
|
intr_queue_t *const intrq,
|
|
|
|
intr_qtd_t *const intr_qtd,
|
|
|
|
u8 *const data)
|
|
|
|
{
|
|
|
|
const int pid = (intrq->endp->direction == IN) ? EHCI_IN
|
|
|
|
: (intrq->endp->direction == OUT) ? EHCI_OUT
|
|
|
|
: EHCI_SETUP;
|
|
|
|
const int cerr = (intrq->endp->dev->speed < 2) ? 1 : 0;
|
|
|
|
|
|
|
|
memset(intr_qtd, 0, sizeof(*intr_qtd));
|
|
|
|
intr_qtd->td.next_qtd = QTD_TERMINATE;
|
|
|
|
intr_qtd->td.alt_next_qtd = QTD_TERMINATE;
|
|
|
|
intr_qtd->td.token = QTD_ACTIVE |
|
|
|
|
(pid << QTD_PID_SHIFT) |
|
|
|
|
(cerr << QTD_CERR_SHIFT) |
|
|
|
|
((intrq->endp->toggle & 1) << QTD_TOGGLE_SHIFT);
|
|
|
|
fill_td(&intr_qtd->td, data, intrq->reqsize);
|
|
|
|
intr_qtd->data = data;
|
|
|
|
intr_qtd->next = NULL;
|
|
|
|
|
|
|
|
intrq->endp->toggle ^= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ehci_destroy_intr_queue(endpoint_t *const, void *const);
|
|
|
|
|
|
|
|
static void *ehci_create_intr_queue(
|
|
|
|
endpoint_t *const ep,
|
|
|
|
const int reqsize,
|
|
|
|
int reqcount,
|
|
|
|
const int reqtiming)
|
2010-09-25 19:01:13 +02:00
|
|
|
{
|
2012-05-25 10:09:13 +02:00
|
|
|
int i;
|
|
|
|
|
|
|
|
if ((reqsize > (4 * 4096 + 1)) || /* the maximum for arbitrary aligned
|
|
|
|
data in five 4096 byte pages */
|
|
|
|
(reqtiming > 1024))
|
|
|
|
return NULL;
|
|
|
|
if (reqcount < 2) /* we need at least 2:
|
|
|
|
one for processing, one for the hc to advance to */
|
|
|
|
reqcount = 2;
|
|
|
|
|
|
|
|
int hubaddr = 0, hubport = 0;
|
|
|
|
if (ep->dev->speed < 2) {
|
|
|
|
/* we need a split transaction */
|
|
|
|
if (closest_usb2_hub(ep->dev, &hubaddr, &hubport))
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-09-16 10:39:08 +02:00
|
|
|
intr_queue_t *const intrq = (intr_queue_t *)dma_memalign(64,
|
|
|
|
sizeof(intr_queue_t));
|
2012-06-14 13:08:36 +02:00
|
|
|
/*
|
|
|
|
* reqcount data chunks
|
|
|
|
* plus one more spare, which we'll leave out of queue
|
|
|
|
*/
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
u8 *data = (u8 *)dma_malloc(reqsize * (reqcount + 1));
|
2012-05-25 10:09:13 +02:00
|
|
|
if (!intrq || !data)
|
|
|
|
fatal("Not enough memory to create USB interrupt queue.\n");
|
|
|
|
intrq->data = data;
|
|
|
|
intrq->endp = ep;
|
|
|
|
intrq->reqsize = reqsize;
|
|
|
|
|
|
|
|
/* create #reqcount transfer descriptors (qTDs) */
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
intrq->head = (intr_qtd_t *)dma_memalign(64, sizeof(intr_qtd_t));
|
2019-03-27 18:30:20 +01:00
|
|
|
if (!intrq->head)
|
|
|
|
fatal("Not enough DMA memory to create #reqcount TD.\n");
|
2012-05-25 10:09:13 +02:00
|
|
|
intr_qtd_t *cur_td = intrq->head;
|
|
|
|
for (i = 0; i < reqcount; ++i) {
|
|
|
|
fill_intr_queue_td(intrq, cur_td, data);
|
|
|
|
data += reqsize;
|
|
|
|
if (i < reqcount - 1) {
|
|
|
|
/* create one more qTD */
|
|
|
|
intr_qtd_t *const next_td =
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
(intr_qtd_t *)dma_memalign(64, sizeof(intr_qtd_t));
|
2019-03-27 18:30:20 +01:00
|
|
|
if (!next_td)
|
|
|
|
fatal("Not enough DMA memory to create TD.\n");
|
2012-05-25 10:09:13 +02:00
|
|
|
cur_td->td.next_qtd = virt_to_phys(&next_td->td);
|
|
|
|
cur_td->next = next_td;
|
|
|
|
cur_td = next_td;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
intrq->tail = cur_td;
|
|
|
|
|
2012-06-14 13:08:36 +02:00
|
|
|
/* create spare qTD */
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
intrq->spare = (intr_qtd_t *)dma_memalign(64, sizeof(intr_qtd_t));
|
2019-03-27 18:30:20 +01:00
|
|
|
if (!intrq->spare)
|
|
|
|
fatal("Not enough DMA memory to create spare qTD.\n");
|
libpayload: EHCI: Fix transaction error for interrupt transfer
Data toggle should be running like 0, 1, 0, 1, ...
In the failed case (where a low-speed USB keyboard or km232 device
is installed), data toggle will be running as 0, 1, 0, 1, ..., 1, 1.
Therefore causing Halted or Transaction Error bit to be set in qTD
Status field.
BUG=None
BRANCH=None
TEST=Tested on nyan_kitty platform, firmware-kitty-5771.61.B branch.
Attached USB keyboard or km232 device to root-hub port (same side as
SD card slot).
Made sure no transaction error after doing interrupt transfer.
Change-Id: I576f3c583dae4c279a6e0e8ffdfce5abe463277d
Signed-off-by: Patrick Georgi <pgeorgi@chromium.org>
Original-Commit-Id: 64b0428aaab869e20f6720669e953acf82ecb846
Original-Change-Id: Ic2c0f95cff2ae6e314967b0b82231a962255f1a7
Original-Signed-off-by: Jim Lin <jilin@nvidia.com>
Original-Reviewed-on: https://chromium-review.googlesource.com/233857
Original-Reviewed-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: http://review.coreboot.org/8747
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2014-12-08 12:30:04 +01:00
|
|
|
intrq->spare->data = data;
|
2012-06-14 13:08:36 +02:00
|
|
|
|
2012-05-25 10:09:13 +02:00
|
|
|
/* initialize QH */
|
|
|
|
const int endp = ep->endpoint & 0xf;
|
2012-11-23 11:58:57 +01:00
|
|
|
memset((void *)&intrq->qh, 0, sizeof(intrq->qh));
|
2012-05-25 10:09:13 +02:00
|
|
|
intrq->qh.horiz_link_ptr = PS_TERMINATE;
|
|
|
|
intrq->qh.epchar = ep->dev->address |
|
|
|
|
(endp << QH_EP_SHIFT) |
|
|
|
|
(ep->dev->speed << QH_EPS_SHIFT) |
|
|
|
|
(1 << QH_DTC_SHIFT) |
|
|
|
|
(0 << QH_RECLAIM_HEAD_SHIFT) |
|
|
|
|
(ep->maxpacketsize << QH_MPS_SHIFT) |
|
|
|
|
(0 << QH_NAK_CNT_SHIFT);
|
|
|
|
intrq->qh.epcaps = (1 << QH_PIPE_MULTIPLIER_SHIFT) |
|
|
|
|
(hubport << QH_PORT_NUMBER_SHIFT) |
|
|
|
|
(hubaddr << QH_HUB_ADDRESS_SHIFT) |
|
|
|
|
(0xfe << QH_UFRAME_CMASK_SHIFT) |
|
|
|
|
1 /* uFrame S-mask */;
|
|
|
|
intrq->qh.td.next_qtd = virt_to_phys(&intrq->head->td);
|
|
|
|
|
|
|
|
/* insert QH into periodic schedule */
|
|
|
|
int nothing_placed = 1;
|
|
|
|
u32 *const ps = (u32 *)phys_to_virt(EHCI_INST(ep->dev->controller)
|
|
|
|
->operation->periodiclistbase);
|
2012-06-14 13:27:39 +02:00
|
|
|
const u32 dummy_ptr = virt_to_phys(EHCI_INST(
|
|
|
|
ep->dev->controller)->dummy_qh) | PS_TYPE_QH;
|
2012-05-25 10:09:13 +02:00
|
|
|
for (i = 0; i < 1024; i += reqtiming) {
|
|
|
|
/* advance to the next free position */
|
2012-06-14 13:27:39 +02:00
|
|
|
while ((i < 1024) && (ps[i] != dummy_ptr)) ++i;
|
2012-05-25 10:09:13 +02:00
|
|
|
if (i < 1024) {
|
|
|
|
ps[i] = virt_to_phys(&intrq->qh) | PS_TYPE_QH;
|
|
|
|
nothing_placed = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (nothing_placed) {
|
2012-12-11 21:08:07 +01:00
|
|
|
usb_debug("Error: Failed to place ehci interrupt queue head "
|
2012-05-25 10:09:13 +02:00
|
|
|
"into periodic schedule: no space left\n");
|
|
|
|
ehci_destroy_intr_queue(ep, intrq);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return intrq;
|
2010-09-25 19:01:13 +02:00
|
|
|
}
|
|
|
|
|
2012-05-25 10:09:13 +02:00
|
|
|
static void ehci_destroy_intr_queue(endpoint_t *const ep, void *const queue)
|
2010-09-25 19:01:13 +02:00
|
|
|
{
|
2012-05-25 10:09:13 +02:00
|
|
|
intr_queue_t *const intrq = (intr_queue_t *)queue;
|
|
|
|
|
|
|
|
/* remove QH from periodic schedule */
|
|
|
|
int i;
|
|
|
|
u32 *const ps = (u32 *)phys_to_virt(EHCI_INST(
|
|
|
|
ep->dev->controller)->operation->periodiclistbase);
|
2012-06-14 13:27:39 +02:00
|
|
|
const u32 dummy_ptr = virt_to_phys(EHCI_INST(
|
|
|
|
ep->dev->controller)->dummy_qh) | PS_TYPE_QH;
|
2012-05-25 10:09:13 +02:00
|
|
|
for (i = 0; i < 1024; ++i) {
|
|
|
|
if ((ps[i] & PS_PTR_MASK) == virt_to_phys(&intrq->qh))
|
2012-06-14 13:27:39 +02:00
|
|
|
ps[i] = dummy_ptr;
|
2012-05-25 10:09:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* wait 1ms for frame to end */
|
|
|
|
mdelay(1);
|
|
|
|
|
|
|
|
while (intrq->head) {
|
|
|
|
/* disable qTD and destroy list */
|
|
|
|
intrq->head->td.next_qtd = QTD_TERMINATE;
|
|
|
|
intrq->head->td.token &= ~QTD_ACTIVE;
|
|
|
|
|
|
|
|
/* save and advance head ptr */
|
|
|
|
intr_qtd_t *const to_free = intrq->head;
|
|
|
|
intrq->head = intrq->head->next;
|
|
|
|
|
|
|
|
/* free current interrupt qTD */
|
|
|
|
free(to_free);
|
|
|
|
}
|
2012-06-14 13:08:36 +02:00
|
|
|
free(intrq->spare);
|
2012-05-25 10:09:13 +02:00
|
|
|
free(intrq->data);
|
|
|
|
free(intrq);
|
2010-09-25 19:01:13 +02:00
|
|
|
}
|
|
|
|
|
2012-05-25 10:09:13 +02:00
|
|
|
static u8 *ehci_poll_intr_queue(void *const queue)
|
2010-09-25 19:01:13 +02:00
|
|
|
{
|
2012-05-25 10:09:13 +02:00
|
|
|
intr_queue_t *const intrq = (intr_queue_t *)queue;
|
|
|
|
|
|
|
|
u8 *ret = NULL;
|
|
|
|
|
|
|
|
/* process if head qTD is inactive AND QH has been moved forward */
|
2012-06-14 13:08:36 +02:00
|
|
|
if (!(intrq->head->td.token & QTD_ACTIVE)) {
|
2012-05-25 10:09:13 +02:00
|
|
|
if (!(intrq->head->td.token & QTD_STATUS_MASK))
|
|
|
|
ret = intrq->head->data;
|
|
|
|
else
|
2012-11-01 23:44:10 +01:00
|
|
|
usb_debug("ehci_poll_intr_queue: transfer failed, "
|
2012-05-25 10:09:13 +02:00
|
|
|
"status == 0x%02x\n",
|
|
|
|
intrq->head->td.token & QTD_STATUS_MASK);
|
|
|
|
|
2012-06-14 13:08:36 +02:00
|
|
|
/* insert spare qTD at the end and advance our tail ptr */
|
|
|
|
fill_intr_queue_td(intrq, intrq->spare, intrq->spare->data);
|
|
|
|
intrq->tail->td.next_qtd = virt_to_phys(&intrq->spare->td);
|
|
|
|
intrq->tail->next = intrq->spare;
|
2012-05-25 10:09:13 +02:00
|
|
|
intrq->tail = intrq->tail->next;
|
2012-06-14 13:08:36 +02:00
|
|
|
|
|
|
|
/* reuse executed qTD as spare one and advance our head ptr */
|
|
|
|
intrq->spare = intrq->head;
|
|
|
|
intrq->head = intrq->head->next;
|
|
|
|
}
|
|
|
|
/* reset queue if we fully processed it after underrun */
|
2012-11-22 11:14:03 +01:00
|
|
|
else if ((intrq->qh.td.next_qtd & QTD_TERMINATE) &&
|
|
|
|
/* to prevent race conditions:
|
|
|
|
not our head and not active */
|
|
|
|
(intrq->qh.current_td_ptr !=
|
|
|
|
virt_to_phys(&intrq->head->td)) &&
|
|
|
|
!(intrq->qh.td.token & QTD_ACTIVE)) {
|
2012-11-01 23:44:10 +01:00
|
|
|
usb_debug("resetting underrun ehci interrupt queue.\n");
|
2012-11-22 11:14:03 +01:00
|
|
|
intrq->qh.current_td_ptr = 0;
|
2012-11-23 11:58:57 +01:00
|
|
|
memset((void *)&intrq->qh.td, 0, sizeof(intrq->qh.td));
|
2012-06-14 13:08:36 +02:00
|
|
|
intrq->qh.td.next_qtd = virt_to_phys(&intrq->head->td);
|
2012-05-25 10:09:13 +02:00
|
|
|
}
|
|
|
|
return ret;
|
2010-09-25 19:01:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
hci_t *
|
2022-08-10 08:59:18 +02:00
|
|
|
ehci_init(unsigned long physical_bar)
|
2010-09-25 19:01:13 +02:00
|
|
|
{
|
|
|
|
int i;
|
2022-08-10 08:59:18 +02:00
|
|
|
hci_t *controller = new_controller();
|
|
|
|
controller->instance = xzalloc(sizeof(ehci_t));
|
2017-02-27 18:56:06 +01:00
|
|
|
controller->reg_base = (uintptr_t)physical_bar;
|
2012-06-28 06:30:15 +02:00
|
|
|
controller->type = EHCI;
|
2010-09-25 19:01:13 +02:00
|
|
|
controller->start = ehci_start;
|
|
|
|
controller->stop = ehci_stop;
|
|
|
|
controller->reset = ehci_reset;
|
2012-11-12 16:20:32 +01:00
|
|
|
controller->init = ehci_reinit;
|
2010-09-25 19:01:13 +02:00
|
|
|
controller->shutdown = ehci_shutdown;
|
|
|
|
controller->bulk = ehci_bulk;
|
|
|
|
controller->control = ehci_control;
|
2013-05-24 15:48:56 +02:00
|
|
|
controller->set_address = generic_set_address;
|
|
|
|
controller->finish_device_config = NULL;
|
|
|
|
controller->destroy_device = NULL;
|
2010-09-25 19:01:13 +02:00
|
|
|
controller->create_intr_queue = ehci_create_intr_queue;
|
|
|
|
controller->destroy_intr_queue = ehci_destroy_intr_queue;
|
|
|
|
controller->poll_intr_queue = ehci_poll_intr_queue;
|
2022-08-10 08:59:18 +02:00
|
|
|
init_device_entry(controller, 0);
|
2010-09-25 19:01:13 +02:00
|
|
|
|
2015-05-12 10:44:02 +02:00
|
|
|
EHCI_INST(controller)->capabilities = phys_to_virt(physical_bar);
|
|
|
|
EHCI_INST(controller)->operation = (hc_op_t *)(phys_to_virt(physical_bar) + EHCI_INST(controller)->capabilities->caplength);
|
2010-09-25 19:01:13 +02:00
|
|
|
|
2012-10-26 02:38:43 +02:00
|
|
|
/* Set the high address word (aka segment) if controller is 64-bit */
|
|
|
|
if (EHCI_INST(controller)->capabilities->hccparams & 1)
|
|
|
|
EHCI_INST(controller)->operation->ctrldssegment = 0;
|
|
|
|
|
2010-09-25 19:01:13 +02:00
|
|
|
/* Enable operation of controller */
|
|
|
|
controller->start(controller);
|
|
|
|
|
|
|
|
/* take over all ports. USB1 should be blind now */
|
|
|
|
EHCI_INST(controller)->operation->configflag = 1;
|
|
|
|
|
2012-05-25 10:09:13 +02:00
|
|
|
/* Initialize periodic frame list */
|
|
|
|
/* 1024 32-bit pointers, 4kb aligned */
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
u32 *const periodic_list = (u32 *)dma_memalign(4096, 1024 * sizeof(u32));
|
2012-05-25 10:09:13 +02:00
|
|
|
if (!periodic_list)
|
|
|
|
fatal("Not enough memory creating EHCI periodic frame list.\n");
|
2012-06-14 13:27:39 +02:00
|
|
|
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
if (dma_initialized()) {
|
|
|
|
EHCI_INST(controller)->dma_buffer = dma_memalign(4096, DMA_SIZE);
|
|
|
|
if (!EHCI_INST(controller)->dma_buffer)
|
|
|
|
fatal("Not enough DMA memory for EHCI bounce buffer.\n");
|
|
|
|
}
|
|
|
|
|
2012-06-14 13:27:39 +02:00
|
|
|
/*
|
|
|
|
* Insert dummy QH in periodic frame list
|
|
|
|
* This helps with broken host controllers
|
|
|
|
* and doesn't violate the standard.
|
|
|
|
*/
|
libpayload: Make EHCI driver cache-aware
This patch makes the EHCI driver work on ARM platforms which usually do
not support automatic cache snooping. It uses the new DMA memory
mechanism (which needs to be correctly set up in the Coreboot mainboard
code) to allocate all EHCI-internal communication structures in
cache-coherent memory, and cleans/invalidates the externally supplied
transfer buffers in Bulk and Control functions with explicit calls as
necessary.
Old-Change-Id: Ie8a62545d905b7a4fdd2a56b9405774be69779e5
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/167339
(cherry picked from commit 322338934add36a5372ffe7d2a45e61a4fdd4a54)
libpayload: ehci: Cache management is hard, let's go copying...
It turns out that my previous commit to make the EHCI stack cache aware
on ARM devices wasn't quite correct, and the problem is actually much
trickier than I thought. After having some fun with more weird transfer
problems that appear/disappear based on stack alignment, this is my
current worst-case threat model that any cache managing implementation
would need to handle correctly:
Some upper layer calls ehci_bulk() with a transfer buffer on its stack.
Due to stack alignment, it happens to start just at the top of a cache
line, so up to 64 - 4 bytes of ehci_bulk's stack will share that line.
ehci_bulk() calls dcache_clean() and initializes the USB transfer.
Between that point and the call to dcache_invalidate() at the end of
ehci_bulk(), any access to the stack variables in that cache line (even
a speculative prefetch) will refetch the line into the cache. Afterwards
any other access to a random memory location that just happens to get
aliased to the same cache line may evict it again, causing the processor
to write out stale data to the transfer buffer and possibly overwrite
data that has already been received over USB.
In short, any dcache_clean/dcache_invalidate-based implementation that
preserves correctness while allowing any arbitrary (non cache-aligned)
memory location as a transfer buffer is presumed to be impossible.
Instead, this patch causes all transfer data to be copied to/from a
cache-coherent bounce buffer. It will still transfer directly if the
supplied buffer is already cache-coherent, which can be used by callers
to optimize their transfers (and is true by default on x86).
Old-Change-Id: I112908410bdbc8ca028d44f2f5d388c529f8057f
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/169231
Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
(cherry picked from commit 702dc50f1d56fe206442079fa443437f4336daed)
Squashed the initial commit and a follow up fix.
Change-Id: Idf7e5aa855b4f0221f82fa380a76049f273e4c88
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6633
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
2013-08-28 21:29:28 +02:00
|
|
|
EHCI_INST(controller)->dummy_qh = (ehci_qh_t *)dma_memalign(64, sizeof(ehci_qh_t));
|
2019-03-27 18:30:20 +01:00
|
|
|
if (!EHCI_INST(controller)->dummy_qh)
|
|
|
|
fatal("Not enough DMA memory for EHCI dummy TD.\n");
|
2012-11-23 11:58:57 +01:00
|
|
|
memset((void *)EHCI_INST(controller)->dummy_qh, 0,
|
2012-06-14 13:27:39 +02:00
|
|
|
sizeof(*EHCI_INST(controller)->dummy_qh));
|
|
|
|
EHCI_INST(controller)->dummy_qh->horiz_link_ptr = QH_TERMINATE;
|
2013-08-23 01:24:09 +02:00
|
|
|
EHCI_INST(controller)->dummy_qh->td.next_qtd = QH_TERMINATE;
|
|
|
|
EHCI_INST(controller)->dummy_qh->td.alt_next_qtd = QH_TERMINATE;
|
2012-05-25 10:09:13 +02:00
|
|
|
for (i = 0; i < 1024; ++i)
|
2019-03-27 18:30:20 +01:00
|
|
|
periodic_list[i] =
|
|
|
|
virt_to_phys(EHCI_INST(controller)->dummy_qh)
|
|
|
|
| PS_TYPE_QH;
|
2012-05-25 10:09:13 +02:00
|
|
|
|
|
|
|
/* Make sure periodic schedule is disabled */
|
|
|
|
ehci_set_periodic_schedule(EHCI_INST(controller), 0);
|
|
|
|
/* Set periodic frame list pointer */
|
|
|
|
EHCI_INST(controller)->operation->periodiclistbase =
|
|
|
|
virt_to_phys(periodic_list);
|
|
|
|
/* Enable use of periodic schedule */
|
|
|
|
ehci_set_periodic_schedule(EHCI_INST(controller), 1);
|
|
|
|
|
2010-09-25 19:01:13 +02:00
|
|
|
/* TODO lots of stuff missing */
|
|
|
|
|
|
|
|
controller->devices[0]->controller = controller;
|
|
|
|
controller->devices[0]->init = ehci_rh_init;
|
2022-08-10 08:59:18 +02:00
|
|
|
controller->devices[0]->init(controller->devices[0]);
|
2010-09-25 19:01:13 +02:00
|
|
|
|
|
|
|
return controller;
|
|
|
|
}
|
2013-05-03 01:16:41 +02:00
|
|
|
|
2019-03-06 01:55:15 +01:00
|
|
|
#if CONFIG(LP_USB_PCI)
|
2013-05-03 01:16:41 +02:00
|
|
|
hci_t *
|
2022-08-10 08:59:18 +02:00
|
|
|
ehci_pci_init(pcidev_t addr)
|
2013-05-03 01:16:41 +02:00
|
|
|
{
|
|
|
|
hci_t *controller;
|
|
|
|
u32 reg_base;
|
|
|
|
|
2020-04-27 05:34:05 +02:00
|
|
|
u16 pci_command = pci_read_config16(addr, PCI_COMMAND);
|
2022-08-10 08:59:18 +02:00
|
|
|
pci_command = (pci_command | PCI_COMMAND_MEMORY) & ~PCI_COMMAND_IO;
|
2020-04-27 05:34:05 +02:00
|
|
|
pci_write_config16(addr, PCI_COMMAND, pci_command);
|
2013-05-03 01:16:41 +02:00
|
|
|
|
2022-08-10 08:59:18 +02:00
|
|
|
reg_base = pci_read_config32(addr, USBBASE);
|
2013-05-03 01:16:41 +02:00
|
|
|
|
|
|
|
/* default value for frame length adjust */
|
|
|
|
pci_write_config8(addr, FLADJ, FLADJ_framelength(60000));
|
|
|
|
|
2014-07-07 16:33:59 +02:00
|
|
|
controller = ehci_init((unsigned long)reg_base);
|
2013-05-03 01:16:41 +02:00
|
|
|
|
2017-02-25 12:32:06 +01:00
|
|
|
if (controller)
|
|
|
|
controller->pcidev = addr;
|
|
|
|
|
2013-05-03 01:16:41 +02:00
|
|
|
return controller;
|
|
|
|
}
|
|
|
|
#endif
|