![cinap_lenrek](/assets/img/avatar_default.png)
the following hooks have been added to the ehci Ctlr structore to handle cache coherency (on arm): void* (*tdalloc)(ulong,int,ulong); void* (*dmaalloc)(ulong); void (*dmafree)(void*); void (*dmaflush)(int,void*,ulong); tdalloc() is used to allocate descriptors and the periodic frame schedule array. on arm, this needs to return uncached memory. tdalloc()ed memory is never freed. dmaalloc()/dmafree() is used for io buffers. this can return cached memory when when hardware maintains cache coherency (pc) or dmaflush() is provided to flush/invalidate the cache (zynq), otherwise needs to return uncached memory. dmaflush() is used to flush/invalidate the cache. the first argument tells us if we need to flush (non zero) or invalidate (zero). uncached.h is gone now. this change makes the handling explicit.
357 lines
7.2 KiB
C
357 lines
7.2 KiB
C
/*
|
|
* Kirkwood-specific code for
|
|
* USB Enhanced Host Controller Interface (EHCI) driver
|
|
* High speed USB 2.0.
|
|
*/
|
|
|
|
#include "u.h"
|
|
#include "../port/lib.h"
|
|
#include "mem.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "io.h"
|
|
#include "../port/error.h"
|
|
#include "../port/usb.h"
|
|
#include "usbehci.h"
|
|
|
|
#define WINTARG(ctl) (((ctl) >> 4) & 017)
|
|
#define WINATTR(ctl) (((ctl) >> 8) & 0377)
|
|
#define WIN64KSIZE(ctl) (((ctl) >> 16) + 1)
|
|
|
|
#define SIZETO64KSIZE(size) ((size) / (64*1024) - 1)
|
|
|
|
enum {
|
|
Debug = 0,
|
|
};
|
|
|
|
typedef struct Kwusb Kwusb;
|
|
typedef struct Kwusbtt Kwusbtt;
|
|
typedef struct Usbwin Usbwin;
|
|
|
|
/* kirkwood usb transaction translator registers? (undocumented) */
|
|
struct Kwusbtt { /* at soc.ehci */
|
|
ulong id;
|
|
ulong hwgeneral;
|
|
ulong hwhost;
|
|
ulong hwdevice;
|
|
ulong hwtxbuf;
|
|
ulong hwrxbuf;
|
|
ulong hwtttxbuf;
|
|
ulong hwttrxbuf;
|
|
};
|
|
|
|
/* kirkwood usb bridge & phy registers */
|
|
struct Kwusb { /* at offset 0x300 from soc.ehci */
|
|
ulong bcs; /* bridge ctl & sts */
|
|
uchar _pad0[0x310-0x304];
|
|
|
|
ulong bic; /* bridge intr. cause */
|
|
ulong bim; /* bridge intr. mask */
|
|
ulong _pad1;
|
|
ulong bea; /* bridge error addr. */
|
|
struct Usbwin {
|
|
ulong ctl; /* see Winenable in io.h */
|
|
ulong base;
|
|
ulong _pad2[2];
|
|
} win[4];
|
|
ulong phycfg; /* phy config. */
|
|
uchar _pad3[0x400-0x364];
|
|
|
|
ulong pwrctl; /* power control */
|
|
uchar _pad4[0x410-0x404];
|
|
ulong phypll; /* phy pll control */
|
|
uchar _pad5[0x420-0x414];
|
|
ulong phytxctl; /* phy transmit control */
|
|
uchar _pad6[0x430-0x424];
|
|
ulong phyrxctl; /* phy receive control */
|
|
uchar _pad7[0x440-0x434];
|
|
ulong phyivref; /* phy ivref control */
|
|
};
|
|
|
|
static Ctlr* ctlrs[Nhcis];
|
|
|
|
static void
|
|
addrmapdump(void)
|
|
{
|
|
int i;
|
|
ulong ctl, targ, attr, size64k;
|
|
Kwusb *map;
|
|
Usbwin *win;
|
|
|
|
if (!Debug)
|
|
return;
|
|
map = (Kwusb *)(soc.ehci + 0x300);
|
|
for (i = 0; i < nelem(map->win); i++) {
|
|
win = &map->win[i];
|
|
ctl = win->ctl;
|
|
if (ctl & Winenable) {
|
|
targ = WINTARG(ctl);
|
|
attr = WINATTR(ctl);
|
|
size64k = WIN64KSIZE(ctl);
|
|
print("usbehci: address map window %d: "
|
|
"targ %ld attr %#lux size %,ld addr %#lux\n",
|
|
i, targ, attr, size64k * 64*1024, win->base);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* assumes ctlr is ilocked */
|
|
static void
|
|
ctlrreset(Ctlr *ctlr)
|
|
{
|
|
int i;
|
|
Eopio *opio;
|
|
|
|
opio = ctlr->opio;
|
|
opio->cmd |= Chcreset;
|
|
coherence();
|
|
/* wait for it to come out of reset */
|
|
for(i = 0; i < 100 && opio->cmd & Chcreset; i++)
|
|
delay(1);
|
|
if(i >= 100)
|
|
print("ehci %#p controller reset timed out\n", ctlr->capio);
|
|
/*
|
|
* Marvell errata FE-USB-340 workaround: 1 << 4 magic:
|
|
* disable streaming. Magic 3 (usb host mode) from the linux driver
|
|
* makes it work. Ick.
|
|
*/
|
|
opio->usbmode |= 1 << 4 | 3;
|
|
coherence();
|
|
}
|
|
|
|
/*
|
|
* configure window `win' as 256MB dram with attribute `attr' and
|
|
* base address
|
|
*/
|
|
static void
|
|
setaddrwin(Kwusb *kw, int win, int attr, ulong base)
|
|
{
|
|
kw->win[win].ctl = Winenable | Targdram << 4 | attr << 8 |
|
|
SIZETO64KSIZE(256*MB) << 16;
|
|
kw->win[win].base = base;
|
|
}
|
|
|
|
static void
|
|
ehcireset(Ctlr *ctlr)
|
|
{
|
|
int i, amp, txvdd;
|
|
ulong v;
|
|
Eopio *opio;
|
|
Kwusb *kw;
|
|
|
|
ilock(ctlr);
|
|
dprint("ehci %#p reset\n", ctlr->capio);
|
|
opio = ctlr->opio;
|
|
|
|
kw = (Kwusb *)(soc.ehci + 0x300);
|
|
kw->bic = 0;
|
|
kw->bim = (1<<4) - 1; /* enable all defined intrs */
|
|
ctlrreset(ctlr);
|
|
|
|
/*
|
|
* clear high 32 bits of address signals if it's 64 bits capable.
|
|
* This is probably not needed but it does not hurt and others do it.
|
|
*/
|
|
if((ctlr->capio->capparms & C64) != 0){
|
|
dprint("ehci: 64 bits\n");
|
|
opio->seg = 0;
|
|
}
|
|
|
|
/* requesting more interrupts per µframe may miss interrupts */
|
|
opio->cmd |= Citc8; /* 1 intr. per ms */
|
|
switch(opio->cmd & Cflsmask){
|
|
case Cfls1024:
|
|
ctlr->nframes = 1024;
|
|
break;
|
|
case Cfls512:
|
|
ctlr->nframes = 512;
|
|
break;
|
|
case Cfls256:
|
|
ctlr->nframes = 256;
|
|
break;
|
|
default:
|
|
panic("ehci: unknown fls %ld", opio->cmd & Cflsmask);
|
|
}
|
|
dprint("ehci: %d frames\n", ctlr->nframes);
|
|
|
|
/*
|
|
* set up the USB address map (bridge address decoding)
|
|
*/
|
|
for (i = 0; i < nelem(kw->win); i++)
|
|
kw->win[i].ctl = kw->win[i].base = 0;
|
|
coherence();
|
|
|
|
setaddrwin(kw, 0, Attrcs0, 0);
|
|
setaddrwin(kw, 1, Attrcs1, 256*MB);
|
|
coherence();
|
|
|
|
if (Debug)
|
|
if (kw->bcs & (1 << 4))
|
|
print("usbehci: not swapping bytes\n");
|
|
else
|
|
print("usbehci: swapping bytes\n");
|
|
addrmapdump(); /* verify sanity */
|
|
|
|
kw->pwrctl |= 1 << 0 | 1 << 1; /* Pu | PuPll */
|
|
coherence();
|
|
|
|
/*
|
|
* Marvell guideline GL-USB-160.
|
|
*/
|
|
kw->phypll |= 1 << 21; /* VCOCAL_START: PLL calibration */
|
|
coherence();
|
|
microdelay(100);
|
|
kw->phypll &= ~(1 << 21);
|
|
|
|
v = kw->phytxctl & ~(017 << 27 | 7); /* REG_EXT_FS_RCALL & AMP_2_0 */
|
|
switch (m->socrev) {
|
|
default:
|
|
print("usbehci: bad 6281 soc rev %d\n", m->socrev);
|
|
/* fall through */
|
|
case Socreva0:
|
|
amp = 4;
|
|
txvdd = 1;
|
|
break;
|
|
case Socreva1:
|
|
amp = 3;
|
|
txvdd = 3;
|
|
break;
|
|
}
|
|
/* REG_EXT_FS_RCALL_EN | REG_RCAL_START | AMP_2_0 */
|
|
kw->phytxctl = v | 1 << 26 | 1 << 12 | amp;
|
|
coherence();
|
|
microdelay(100);
|
|
kw->phytxctl &= ~(1 << 12);
|
|
|
|
v = kw->phyrxctl & ~(3 << 2 | 017 << 4); /* LPF_COEF_1_0 & SQ_THRESH_3_0 */
|
|
kw->phyrxctl = v | 1 << 2 | 8 << 4;
|
|
|
|
v = kw->phyivref & ~(3 << 8); /* TXVDD12 */
|
|
kw->phyivref = v | txvdd << 8;
|
|
coherence();
|
|
|
|
ehcirun(ctlr, 0);
|
|
ctlrreset(ctlr);
|
|
|
|
iunlock(ctlr);
|
|
}
|
|
|
|
static void
|
|
setdebug(Hci*, int d)
|
|
{
|
|
ehcidebug = d;
|
|
}
|
|
|
|
static void
|
|
shutdown(Hci *hp)
|
|
{
|
|
Ctlr *ctlr;
|
|
Eopio *opio;
|
|
|
|
ctlr = hp->aux;
|
|
ilock(ctlr);
|
|
ctlrreset(ctlr);
|
|
|
|
delay(100);
|
|
ehcirun(ctlr, 0);
|
|
|
|
opio = ctlr->opio;
|
|
opio->frbase = 0;
|
|
coherence();
|
|
iunlock(ctlr);
|
|
}
|
|
|
|
static void
|
|
findehcis(void) /* actually just use fixed addresses on sheeva */
|
|
{
|
|
int i;
|
|
Ctlr *ctlr;
|
|
static int already = 0;
|
|
|
|
if(already)
|
|
return;
|
|
already = 1;
|
|
|
|
ctlr = smalloc(sizeof(Ctlr));
|
|
/* the sheeva's usb 2.0 otg uses a superset of the ehci registers */
|
|
ctlr->capio = (Ecapio *)(soc.ehci + 0x100);
|
|
ctlr->opio = (Eopio *) (soc.ehci + 0x140);
|
|
dprint("usbehci: port %#p\n", ctlr->capio);
|
|
|
|
for(i = 0; i < Nhcis; i++)
|
|
if(ctlrs[i] == nil){
|
|
ctlrs[i] = ctlr;
|
|
break;
|
|
}
|
|
if(i == Nhcis)
|
|
print("ehci: bug: more than %d controllers\n", Nhcis);
|
|
}
|
|
|
|
static int
|
|
reset(Hci *hp)
|
|
{
|
|
static Lock resetlck;
|
|
int i;
|
|
Ctlr *ctlr;
|
|
Ecapio *capio;
|
|
|
|
ilock(&resetlck);
|
|
findehcis();
|
|
|
|
/*
|
|
* Any adapter matches if no hp->port is supplied,
|
|
* otherwise the ports must match.
|
|
*/
|
|
ctlr = nil;
|
|
for(i = 0; i < Nhcis && ctlrs[i] != nil; i++){
|
|
ctlr = ctlrs[i];
|
|
if(ctlr->active == 0)
|
|
if(hp->port == 0 || hp->port == (uintptr)ctlr->capio){
|
|
ctlr->active = 1;
|
|
break;
|
|
}
|
|
}
|
|
iunlock(&resetlck);
|
|
if(ctlrs[i] == nil || i == Nhcis)
|
|
return -1;
|
|
|
|
hp->aux = ctlr;
|
|
hp->port = (uintptr)ctlr->capio;
|
|
hp->irq = IRQ0usb0;
|
|
hp->tbdf = 0;
|
|
|
|
capio = ctlr->capio;
|
|
hp->nports = capio->parms & Cnports;
|
|
|
|
ddprint("echi: %s, ncc %lud npcc %lud\n",
|
|
capio->parms & 0x10000 ? "leds" : "no leds",
|
|
(capio->parms >> 12) & 0xf, (capio->parms >> 8) & 0xf);
|
|
ddprint("ehci: routing %s, %sport power ctl, %d ports\n",
|
|
capio->parms & 0x40 ? "explicit" : "automatic",
|
|
capio->parms & 0x10 ? "" : "no ", hp->nports);
|
|
|
|
ctlr->tdalloc = ucallocalign;
|
|
ctlr->dmaalloc = ucalloc;
|
|
ctlr->dmafree = ucfree;
|
|
|
|
ehcireset(ctlr);
|
|
ehcimeminit(ctlr);
|
|
|
|
/*
|
|
* Linkage to the generic HCI driver.
|
|
*/
|
|
ehcilinkage(hp);
|
|
hp->shutdown = shutdown;
|
|
hp->debug = setdebug;
|
|
|
|
intrenable(Irqlo, hp->irq, hp->interrupt, hp, hp->type);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
usbehcilink(void)
|
|
{
|
|
addhcitype("ehci", reset);
|
|
}
|