123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- /*
- * 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"
- //#include "uncached.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 = mallocz(sizeof(Ctlr), 1);
- /* 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);
- ehcireset(ctlr);
- ehcimeminit(ctlr);
- /*
- * Linkage to the generic HCI driver.
- */
- ehcilinkage(hp);
- hp->shutdown = shutdown;
- hp->debug = setdebug;
- return 0;
- }
- void
- usbehcilink(void)
- {
- addhcitype("ehci", reset);
- }
|