1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815 |
- From 8c9e61e7b0f28a66d0f63c07b10fc6617a709010 Mon Sep 17 00:00:00 2001
- From: Patrick Rudolph <patrick.rudolph@9elements.com>
- Date: Sun, 15 Nov 2020 19:59:25 +0100
- Subject: [PATCH 19/22] grub-core/bus/usb: Add xhci support
- Add support for xHCI USB controllers.
- The code is based on seabios implementation, but has been heavily
- modified to match grubs internals.
- Changes done in version 2:
- * Code cleanup
- * Code style fixes
- * Don't leak memory buffers
- * Compile without warnings
- * Add more defines
- * Add more helper functions
- * Don't assume a 1:1 virtual to physical mapping
- * Flush cachelines after writing buffers
- * Don't use hardcoded page size
- * Proper scratchpad register setup
- * xHCI bios ownership handoff
- Changes done in version 3:
- * Fixed a race condition detecting events, which doesn't appear on
- qemu based xHCI controllers
- * Don't accidently disable USB3.0 devices after first command
- * Support arbitrary protocol speed IDs
- * Coding style cleanup
- Tested:
- * Qemu system x86_64
- * virtual USB HID keyboard (usb-kbd)
- * virtual USB HID mass storage (usb-storage)
- * init Supermicro X11SSH-F
- * iKVM HID keyboard
- * USB3 HID mass storage (controller root port)
- * USB HID keyboard
- TODO:
- * Test on more hardware
- * Test on USB3 hubs
- * Support for USB 3.1 and USB 3.2 controllers
- Tested on qemu using coreboot and grub as payload:
- qemu-system-x86_64 -M q35 -bios $firmware -device qemu-xhci,id=xhci -accel kvm -m 1024M \
- -device usb-storage,drive=thumbdrive,bus=xhci.0,port=3 \
- -drive if=none,format=raw,id=thumbdrive,file=ubuntu-20.04.1-desktop-amd64.iso \
- -device usb-kbd,bus=xhci.0
- Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
- Signed-off-by: sylv <sylv@sylv.io>
- ---
- Makefile.am | 2 +-
- grub-core/Makefile.core.def | 7 +
- grub-core/bus/usb/xhci-pci.c | 195 +++
- grub-core/bus/usb/xhci.c | 2496 ++++++++++++++++++++++++++++++++++
- include/grub/usb.h | 4 +
- 5 files changed, 2703 insertions(+), 1 deletion(-)
- create mode 100644 grub-core/bus/usb/xhci-pci.c
- create mode 100644 grub-core/bus/usb/xhci.c
- diff --git a/Makefile.am b/Makefile.am
- index 43635d5ff..65016f856 100644
- --- a/Makefile.am
- +++ b/Makefile.am
- @@ -434,7 +434,7 @@ if COND_i386_coreboot
- FS_PAYLOAD_MODULES ?= $(shell cat grub-core/fs.lst)
- default_payload.elf: grub-mkstandalone grub-mkimage FORCE
- test -f $@ && rm $@ || true
- - pkgdatadir=. ./grub-mkstandalone --grub-mkimage=./grub-mkimage -O i386-coreboot -o $@ --modules='ahci pata ehci uhci ohci usb_keyboard usbms part_msdos ext2 fat at_keyboard part_gpt usbserial_usbdebug cbfs' --install-modules='ls linux search configfile normal cbtime cbls memrw iorw minicmd lsmmap lspci halt reboot hexdump pcidump regexp setpci lsacpi chain test serial multiboot cbmemc linux16 gzio echo help syslinuxcfg xnu $(FS_PAYLOAD_MODULES) password_pbkdf2 $(EXTRA_PAYLOAD_MODULES)' --fonts= --themes= --locales= -d grub-core/ /boot/grub/grub.cfg=$(srcdir)/coreboot.cfg
- + pkgdatadir=. ./grub-mkstandalone --grub-mkimage=./grub-mkimage -O i386-coreboot -o $@ --modules='ahci pata xhci ehci uhci ohci usb_keyboard usbms part_msdos ext2 fat at_keyboard part_gpt usbserial_usbdebug cbfs' --install-modules='ls linux search configfile normal cbtime cbls memrw iorw minicmd lsmmap lspci halt reboot hexdump pcidump regexp setpci lsacpi chain test serial multiboot cbmemc linux16 gzio echo help syslinuxcfg xnu $(FS_PAYLOAD_MODULES) password_pbkdf2 $(EXTRA_PAYLOAD_MODULES)' --fonts= --themes= --locales= -d grub-core/ /boot/grub/grub.cfg=$(srcdir)/coreboot.cfg
- endif
-
- endif
- diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
- index 5c1af8682..9d59acd1e 100644
- --- a/grub-core/Makefile.core.def
- +++ b/grub-core/Makefile.core.def
- @@ -667,6 +667,13 @@ module = {
- enable = arm_coreboot;
- };
-
- +module = {
- + name = xhci;
- + common = bus/usb/xhci.c;
- + pci = bus/usb/xhci-pci.c;
- + enable = pci;
- +};
- +
- module = {
- name = pci;
- common = bus/pci.c;
- diff --git a/grub-core/bus/usb/xhci-pci.c b/grub-core/bus/usb/xhci-pci.c
- new file mode 100644
- index 000000000..a5bd3c97d
- --- /dev/null
- +++ b/grub-core/bus/usb/xhci-pci.c
- @@ -0,0 +1,195 @@
- +/* xhci.c - XHCI Support. */
- +/*
- + * GRUB -- GRand Unified Bootloader
- + * Copyright (C) 2020 9elements Cyber Security
- + *
- + * GRUB is free software: you can redistribute it and/or modify
- + * it under the terms of the GNU General Public License as published by
- + * the Free Software Foundation, either version 3 of the License, or
- + * (at your option) any later version.
- + *
- + * GRUB is distributed in the hope that it will be useful,
- + * but WITHOUT ANY WARRANTY; without even the implied warranty of
- + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + * GNU General Public License for more details.
- + *
- + * You should have received a copy of the GNU General Public License
- + * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
- + */
- +
- +#include <grub/pci.h>
- +#include <grub/cpu/pci.h>
- +#include <grub/cs5536.h>
- +#include <grub/misc.h>
- +#include <grub/mm.h>
- +#include <grub/time.h>
- +#include <grub/usb.h>
- +
- +#define GRUB_XHCI_PCI_SBRN_REG 0x60
- +#define GRUB_XHCI_ADDR_MEM_MASK (~0xff)
- +
- +/* USBLEGSUP bits and related OS OWNED byte offset */
- +enum
- +{
- + GRUB_XHCI_BIOS_OWNED = (1 << 16),
- + GRUB_XHCI_OS_OWNED = (1 << 24)
- +};
- +
- +/* PCI iteration function... */
- +static int
- +grub_xhci_pci_iter (grub_pci_device_t dev, grub_pci_id_t pciid,
- + void *data __attribute__ ((unused)))
- +{
- + volatile grub_uint32_t *regs;
- + grub_uint32_t base, base_h;
- + grub_uint32_t eecp_offset;
- + grub_uint32_t usblegsup = 0;
- + grub_uint64_t maxtime;
- + grub_uint32_t interf;
- + grub_uint32_t subclass;
- + grub_uint32_t class;
- + grub_uint8_t release;
- + grub_uint32_t class_code;
- +
- + grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: begin\n");
- +
- + if (pciid == GRUB_CS5536_PCIID)
- + {
- + grub_dprintf ("xhci", "CS5536 not supported\n");
- + return 0;
- + }
- + else
- + {
- + grub_pci_address_t addr;
- + addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
- + class_code = grub_pci_read (addr) >> 8;
- + interf = class_code & 0xFF;
- + subclass = (class_code >> 8) & 0xFF;
- + class = class_code >> 16;
- +
- + /* If this is not an XHCI controller, just return. */
- + if (class != 0x0c || subclass != 0x03 || interf != 0x30)
- + return 0;
- +
- + grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: class OK\n");
- +
- + /* Check Serial Bus Release Number */
- + addr = grub_pci_make_address (dev, GRUB_XHCI_PCI_SBRN_REG);
- + release = grub_pci_read_byte (addr);
- + if (release != 0x30)
- + {
- + grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: Wrong SBRN: %0x\n",
- + release);
- + return 0;
- + }
- + grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: bus rev. num. OK\n");
- +
- + /* Determine XHCI XHCC registers base address. */
- + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0);
- + base = grub_pci_read (addr);
- + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG1);
- + base_h = grub_pci_read (addr);
- + /* Stop if registers are mapped above 4G - GRUB does not currently
- + * work with registers mapped above 4G */
- + if (((base & GRUB_PCI_ADDR_MEM_TYPE_MASK) != GRUB_PCI_ADDR_MEM_TYPE_32)
- + && (base_h != 0))
- + {
- + grub_dprintf ("xhci",
- + "XHCI grub_xhci_pci_iter: registers above 4G are not supported\n");
- + return 0;
- + }
- + base &= GRUB_PCI_ADDR_MEM_MASK;
- + if (!base)
- + {
- + grub_dprintf ("xhci",
- + "XHCI: XHCI is not mapped\n");
- + return 0;
- + }
- +
- + /* Set bus master - needed for coreboot, VMware, broken BIOSes etc. */
- + addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND);
- + grub_pci_write_word(addr,
- + GRUB_PCI_COMMAND_MEM_ENABLED
- + | GRUB_PCI_COMMAND_BUS_MASTER
- + | grub_pci_read_word(addr));
- +
- + grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: 32-bit XHCI OK\n");
- + }
- +
- + grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: iobase of XHCC: %08x\n",
- + (base & GRUB_XHCI_ADDR_MEM_MASK));
- +
- + regs = grub_pci_device_map_range (dev,
- + (base & GRUB_XHCI_ADDR_MEM_MASK),
- + 0x100);
- +
- + /* Is there EECP ? */
- + eecp_offset = (grub_le_to_cpu32 (regs[2]) >> 8) & 0xff;
- +
- + /* Determine and change ownership. */
- + /* EECP offset valid in HCCPARAMS */
- + /* Ownership can be changed via EECP only */
- + if (pciid != GRUB_CS5536_PCIID && eecp_offset >= 0x40)
- + {
- + grub_pci_address_t pciaddr_eecp;
- + pciaddr_eecp = grub_pci_make_address (dev, eecp_offset);
- +
- + usblegsup = grub_pci_read (pciaddr_eecp);
- + if (usblegsup & GRUB_XHCI_BIOS_OWNED)
- + {
- + grub_boot_time ("Taking ownership of XHCI controller");
- + grub_dprintf ("xhci",
- + "XHCI grub_xhci_pci_iter: XHCI owned by: BIOS\n");
- + /* Ownership change - set OS_OWNED bit */
- + grub_pci_write (pciaddr_eecp, usblegsup | GRUB_XHCI_OS_OWNED);
- + /* Ensure PCI register is written */
- + grub_pci_read (pciaddr_eecp);
- +
- + /* Wait for finish of ownership change, XHCI specification
- + * doesn't say how long it can take... */
- + maxtime = grub_get_time_ms () + 1000;
- + while ((grub_pci_read (pciaddr_eecp) & GRUB_XHCI_BIOS_OWNED)
- + && (grub_get_time_ms () < maxtime));
- + if (grub_pci_read (pciaddr_eecp) & GRUB_XHCI_BIOS_OWNED)
- + {
- + grub_dprintf ("xhci",
- + "XHCI grub_xhci_pci_iter: XHCI change ownership timeout");
- + /* Change ownership in "hard way" - reset BIOS ownership */
- + grub_pci_write (pciaddr_eecp, GRUB_XHCI_OS_OWNED);
- + /* Ensure PCI register is written */
- + grub_pci_read (pciaddr_eecp);
- + }
- + }
- + else if (usblegsup & GRUB_XHCI_OS_OWNED)
- + /* XXX: What to do in this case - nothing ? Can it happen ? */
- + grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: XHCI owned by: OS\n");
- + else
- + {
- + grub_dprintf ("xhci",
- + "XHCI grub_Xhci_pci_iter: XHCI owned by: NONE\n");
- + /* XXX: What to do in this case ? Can it happen ?
- + * Is code below correct ? */
- + /* Ownership change - set OS_OWNED bit */
- + grub_pci_write (pciaddr_eecp, GRUB_XHCI_OS_OWNED);
- + /* Ensure PCI register is written */
- + grub_pci_read (pciaddr_eecp);
- + }
- +
- + /* Disable SMI, just to be sure. */
- + pciaddr_eecp = grub_pci_make_address (dev, eecp_offset + 4);
- + grub_pci_write (pciaddr_eecp, 0);
- + /* Ensure PCI register is written */
- + grub_pci_read (pciaddr_eecp);
- + }
- +
- + grub_dprintf ("xhci", "inithw: XHCI grub_xhci_pci_iter: ownership OK\n");
- +
- + grub_xhci_init_device (regs);
- + return 0;
- +}
- +
- +void
- +grub_xhci_pci_scan (void)
- +{
- + grub_pci_iterate (grub_xhci_pci_iter, NULL);
- +}
- diff --git a/grub-core/bus/usb/xhci.c b/grub-core/bus/usb/xhci.c
- new file mode 100644
- index 000000000..f4591ffb5
- --- /dev/null
- +++ b/grub-core/bus/usb/xhci.c
- @@ -0,0 +1,2496 @@
- +/* xhci.c - XHCI Support. */
- +/*
- + * GRUB -- GRand Unified Bootloader
- + * Copyright (C) 2020 9elements Cyber Security
- + *
- + * GRUB is free software: you can redistribute it and/or modify
- + * it under the terms of the GNU General Public License as published by
- + * the Free Software Foundation, either version 3 of the License, or
- + * (at your option) any later version.
- + *
- + * GRUB is distributed in the hope that it will be useful,
- + * but WITHOUT ANY WARRANTY; without even the implied warranty of
- + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- + * GNU General Public License for more details.
- + *
- + * You should have received a copy of the GNU General Public License
- + * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
- + *
- + * Big parts of this software are inspired by seabios XHCI implementation
- + * Released under LGPLv3. Credits to:
- + *
- + * Copyright (C) 2013 Gerd Hoffmann <kraxel@redhat.com>
- + * Copyright (C) 2014 Kevin O'Connor <kevin@koconnor.net>
- + */
- +
- +#include <grub/dl.h>
- +#include <grub/err.h>
- +#include <grub/mm.h>
- +#include <grub/usb.h>
- +#include <grub/usbtrans.h>
- +#include <grub/misc.h>
- +#include <grub/time.h>
- +#include <grub/loader.h>
- +#include <grub/disk.h>
- +#include <grub/dma.h>
- +#include <grub/cache.h>
- +#include <grub/i386/cpuid.h>
- +
- +GRUB_MOD_LICENSE ("GPLv3+");
- +
- +/* This simple GRUB implementation of XHCI driver */
- +/* Based on the specification
- + * "eXtensible Host Controller Interface for Universal Serial Bus" Revision 1.2
- + */
- +
- +
- +#define xhci_get_field(data, field) \
- + (((data) >> field##_SHIFT) & field##_MASK)
- +#define XHCI_PORTSC_PLS_MASK 0xf
- +#define XHCI_PORTSC_PLS_SHIFT 5
- +#define XHCI_PORTSC_SPEED_MASK 0xf
- +#define XHCI_PORTSC_SPEED_SHIFT 10
- +
- +enum
- +{
- + XHCI_USB_FULLSPEED = 1,
- + XHCI_USB_LOWSPEED,
- + XHCI_USB_HIGHSPEED,
- + XHCI_USB_SUPERSPEED
- +};
- +
- +/* Chapter 5.3 Host Controller Capability Registers */
- +struct grub_xhci_caps {
- + grub_uint8_t caplength;
- + grub_uint8_t reserved_01;
- + grub_uint16_t hciversion;
- + grub_uint32_t hcsparams1;
- + grub_uint32_t hcsparams2;
- + grub_uint32_t hcsparams3;
- + grub_uint32_t hccparams;
- + grub_uint32_t dboff;
- + grub_uint32_t rtsoff;
- + grub_uint32_t hccparams2;
- +} GRUB_PACKED;
- +
- +/* extended capabilities */
- +struct grub_xhci_xcap {
- + grub_uint32_t cap;
- + grub_uint32_t data[];
- +} GRUB_PACKED;
- +
- +#define XHCI_CAP_LEGACY_SUPPORT 1
- +#define XHCI_CAP_SUPPORTED_PROTOCOL 2
- +
- +struct xhci_portmap {
- + grub_uint8_t start;
- + grub_uint8_t count;
- +} GRUB_PACKED;
- +
- +struct grub_xhci_op {
- + grub_uint32_t usbcmd;
- + grub_uint32_t usbsts;
- + grub_uint32_t pagesize;
- + grub_uint32_t reserved_01[2];
- + grub_uint32_t dnctl;
- + grub_uint32_t crcr_low;
- + grub_uint32_t crcr_high;
- + grub_uint32_t reserved_02[4];
- + grub_uint32_t dcbaap_low;
- + grub_uint32_t dcbaap_high;
- + grub_uint32_t config;
- +} GRUB_PACKED;
- +
- +enum
- +{
- + GRUB_XHCI_CMD_RS = (1<<0),
- + GRUB_XHCI_CMD_HCRST = (1<<1),
- + GRUB_XHCI_CMD_INTE = (1<<2),
- + GRUB_XHCI_CMD_HSEE = (1<<3),
- + GRUB_XHCI_CMD_LHCRST = (1<<7),
- + GRUB_XHCI_CMD_CSS = (1<<8),
- + GRUB_XHCI_CMD_CRS = (1<<9),
- + GRUB_XHCI_CMD_EWE = (1<<10),
- + GRUB_XHCI_CMD_EU3S = (1<<11)
- +};
- +
- +enum
- +{
- + GRUB_XHCI_STS_HCH = (1<<0),
- + GRUB_XHCI_STS_HSE = (1<<2),
- + GRUB_XHCI_STS_EINT = (1<<3),
- + GRUB_XHCI_STS_PCD = (1<<4),
- + GRUB_XHCI_STS_SSS = (1<<8),
- + GRUB_XHCI_STS_RSS = (1<<9),
- + GRUB_XHCI_STS_SRE = (1<<10),
- + GRUB_XHCI_STS_CNR = (1<<11),
- + GRUB_XHCI_STS_HCE = (1<<12)
- +};
- +
- +
- +/* Port Registers Offset */
- +#define GRUB_XHCI_PR_OFFSET 0x400
- +/* Interrupter Registers Offset */
- +#define GRUB_XHCI_IR_OFFSET 0x20
- +
- +/* Port Status and Control registers offsets */
- +
- +/* Chapter 6 Data Structures */
- +#define ALIGN_SPBA 64
- +#define ALIGN_DCBAA 64
- +#define ALIGN_CMD_RING_SEG 64
- +#define ALIGN_EVT_RING_SEG 64
- +#define ALIGN_EVT_RING_TABLE 64
- +#define ALIGN_TRB 16
- +#define ALIGN_INCTX 64
- +#define ALIGN_SLOTCTX 32
- +
- +#define BOUNDARY_RING 0x10000
- +
- +enum
- +{
- + GRUB_XHCI_PORTSC_CCS = (1<<0),
- + GRUB_XHCI_PORTSC_PED = (1<<1),
- + GRUB_XHCI_PORTSC_OCA = (1<<3),
- + GRUB_XHCI_PORTSC_PR = (1<<4),
- + GRUB_XHCI_PORTSC_PP = (1<<9),
- + GRUB_XHCI_PORTSC_SPEED_FULL = (1<<10),
- + GRUB_XHCI_PORTSC_SPEED_LOW = (2<<10),
- + GRUB_XHCI_PORTSC_SPEED_HIGH = (3<<10),
- + GRUB_XHCI_PORTSC_SPEED_SUPER = (4<<10),
- + GRUB_XHCI_PORTSC_LWS = (1<<16),
- + GRUB_XHCI_PORTSC_CSC = (1<<17),
- + GRUB_XHCI_PORTSC_PEC = (1<<18),
- + GRUB_XHCI_PORTSC_WRC = (1<<19),
- + GRUB_XHCI_PORTSC_OCC = (1<<20),
- + GRUB_XHCI_PORTSC_PRC = (1<<21),
- + GRUB_XHCI_PORTSC_PLC = (1<<22),
- + GRUB_XHCI_PORTSC_CEC = (1<<23),
- + GRUB_XHCI_PORTSC_CAS = (1<<24),
- + GRUB_XHCI_PORTSC_WCE = (1<<25),
- + GRUB_XHCI_PORTSC_WDE = (1<<26),
- + GRUB_XHCI_PORTSC_WOE = (1<<27),
- + GRUB_XHCI_PORTSC_DR = (1<<30),
- + GRUB_XHCI_PORTSC_WPR = (1<<31)
- +};
- +
- +/* XHCI memory data structs */
- +#define GRUB_XHCI_MAX_ENDPOINTS 32
- +
- +#define GRUB_XHCI_RING_ITEMS 128
- +#define GRUB_XHCI_RING_SIZE (GRUB_XHCI_RING_ITEMS*sizeof(struct grub_xhci_trb))
- +/*
- + * xhci_ring structs are allocated with XHCI_RING_SIZE alignment,
- + * then we can get it from a trb pointer (provided by evt ring).
- + */
- +#define XHCI_RING(_trb) \
- + ((struct grub_xhci_ring*)((grub_uint32_t)(_trb) & ~(GRUB_XHCI_RING_SIZE-1)))
- +
- +/* slot context */
- +struct grub_xhci_slotctx {
- + grub_uint32_t ctx[4];
- + grub_uint32_t reserved_01[4];
- +} GRUB_PACKED;
- +
- +/* endpoint context */
- +struct grub_xhci_epctx {
- + grub_uint32_t ctx[2];
- + grub_uint32_t deq_low;
- + grub_uint32_t deq_high;
- + grub_uint32_t length;
- + grub_uint32_t reserved_01[3];
- +} GRUB_PACKED;
- +
- +/* device context array element */
- +struct grub_xhci_devlist {
- + grub_uint32_t ptr_low;
- + grub_uint32_t ptr_high;
- +} GRUB_PACKED;
- +
- +/* input context */
- +struct grub_xhci_inctx {
- + grub_uint32_t del;
- + grub_uint32_t add;
- + grub_uint32_t reserved_01[6];
- +} GRUB_PACKED;
- +
- +/* transfer block (ring element) */
- +struct grub_xhci_trb {
- + grub_uint32_t ptr_low;
- + grub_uint32_t ptr_high;
- + grub_uint32_t status;
- + grub_uint32_t control;
- +} GRUB_PACKED;
- +
- +#define TRB_C (1<<0)
- +#define TRB_TYPE_SHIFT 10
- +#define TRB_TYPE_MASK 0x3f
- +#define TRB_TYPE(t) (((t) >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK)
- +
- +#define TRB_EV_ED (1<<2)
- +
- +#define TRB_TR_ENT (1<<1)
- +#define TRB_TR_ISP (1<<2)
- +#define TRB_TR_NS (1<<3)
- +#define TRB_TR_CH (1<<4)
- +#define TRB_TR_IOC (1<<5)
- +#define TRB_TR_IDT (1<<6)
- +#define TRB_TR_TBC_SHIFT 7
- +#define TRB_TR_TBC_MASK 0x3
- +#define TRB_TR_BEI (1<<9)
- +#define TRB_TR_TLBPC_SHIFT 16
- +#define TRB_TR_TLBPC_MASK 0xf
- +#define TRB_TR_FRAMEID_SHIFT 20
- +#define TRB_TR_FRAMEID_MASK 0x7ff
- +#define TRB_TR_SIA (1<<31)
- +
- +#define TRB_TR_DIR (1<<16)
- +
- +#define TRB_CR_SLOTID_SHIFT 24
- +#define TRB_CR_SLOTID_MASK 0xff
- +#define TRB_CR_EPID_SHIFT 16
- +#define TRB_CR_EPID_MASK 0x1f
- +
- +#define TRB_CR_BSR (1<<9)
- +#define TRB_CR_DC (1<<9)
- +
- +#define TRB_LK_TC (1<<1)
- +
- +#define TRB_INTR_SHIFT 22
- +#define TRB_INTR_MASK 0x3ff
- +#define TRB_INTR(t) (((t).status >> TRB_INTR_SHIFT) & TRB_INTR_MASK)
- +
- +typedef enum TRBType {
- + TRB_RESERVED = 0,
- + TR_NORMAL,
- + TR_SETUP,
- + TR_DATA,
- + TR_STATUS,
- + TR_ISOCH,
- + TR_LINK,
- + TR_EVDATA,
- + TR_NOOP,
- + CR_ENABLE_SLOT,
- + CR_DISABLE_SLOT,
- + CR_ADDRESS_DEVICE,
- + CR_CONFIGURE_ENDPOINT,
- + CR_EVALUATE_CONTEXT,
- + CR_RESET_ENDPOINT,
- + CR_STOP_ENDPOINT,
- + CR_SET_TR_DEQUEUE,
- + CR_RESET_DEVICE,
- + CR_FORCE_EVENT,
- + CR_NEGOTIATE_BW,
- + CR_SET_LATENCY_TOLERANCE,
- + CR_GET_PORT_BANDWIDTH,
- + CR_FORCE_HEADER,
- + CR_NOOP,
- + ER_TRANSFER = 32,
- + ER_COMMAND_COMPLETE,
- + ER_PORT_STATUS_CHANGE,
- + ER_BANDWIDTH_REQUEST,
- + ER_DOORBELL,
- + ER_HOST_CONTROLLER,
- + ER_DEVICE_NOTIFICATION,
- + ER_MFINDEX_WRAP,
- +} TRBType;
- +
- +typedef enum TRBCCode {
- + CC_INVALID = 0,
- + CC_SUCCESS,
- + CC_DATA_BUFFER_ERROR,
- + CC_BABBLE_DETECTED,
- + CC_USB_TRANSACTION_ERROR,
- + CC_TRB_ERROR,
- + CC_STALL_ERROR,
- + CC_RESOURCE_ERROR,
- + CC_BANDWIDTH_ERROR,
- + CC_NO_SLOTS_ERROR,
- + CC_INVALID_STREAM_TYPE_ERROR,
- + CC_SLOT_NOT_ENABLED_ERROR,
- + CC_EP_NOT_ENABLED_ERROR,
- + CC_SHORT_PACKET,
- + CC_RING_UNDERRUN,
- + CC_RING_OVERRUN,
- + CC_VF_ER_FULL,
- + CC_PARAMETER_ERROR,
- + CC_BANDWIDTH_OVERRUN,
- + CC_CONTEXT_STATE_ERROR,
- + CC_NO_PING_RESPONSE_ERROR,
- + CC_EVENT_RING_FULL_ERROR,
- + CC_INCOMPATIBLE_DEVICE_ERROR,
- + CC_MISSED_SERVICE_ERROR,
- + CC_COMMAND_RING_STOPPED,
- + CC_COMMAND_ABORTED,
- + CC_STOPPED,
- + CC_STOPPED_LENGTH_INVALID,
- + CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR = 29,
- + CC_ISOCH_BUFFER_OVERRUN = 31,
- + CC_EVENT_LOST_ERROR,
- + CC_UNDEFINED_ERROR,
- + CC_INVALID_STREAM_ID_ERROR,
- + CC_SECONDARY_BANDWIDTH_ERROR,
- + CC_SPLIT_TRANSACTION_ERROR
- +} TRBCCode;
- +
- +enum {
- + PLS_U0 = 0,
- + PLS_U1 = 1,
- + PLS_U2 = 2,
- + PLS_U3 = 3,
- + PLS_DISABLED = 4,
- + PLS_RX_DETECT = 5,
- + PLS_INACTIVE = 6,
- + PLS_POLLING = 7,
- + PLS_RECOVERY = 8,
- + PLS_HOT_RESET = 9,
- + PLS_COMPILANCE_MODE = 10,
- + PLS_TEST_MODE = 11,
- + PLS_RESUME = 15,
- +};
- +
- +/* event ring segment */
- +struct grub_xhci_er_seg {
- + grub_uint32_t ptr_low;
- + grub_uint32_t ptr_high;
- + grub_uint32_t size;
- + grub_uint32_t reserved_01;
- +} GRUB_PACKED;
- +
- +struct grub_xhci_ring {
- + struct grub_xhci_trb ring[GRUB_XHCI_RING_ITEMS];
- + struct grub_xhci_trb evt;
- + grub_uint32_t eidx;
- + grub_uint32_t nidx;
- + grub_uint32_t cs;
- +};
- +
- +/* port registers */
- +struct grub_xhci_pr {
- + grub_uint32_t portsc;
- + grub_uint32_t portpmsc;
- + grub_uint32_t portli;
- + grub_uint32_t reserved_01;
- +} GRUB_PACKED;
- +
- +/* doorbell registers */
- +struct grub_xhci_db {
- + grub_uint32_t doorbell;
- +} GRUB_PACKED;
- +
- +/* runtime registers */
- +struct grub_xhci_rts {
- + grub_uint32_t mfindex;
- +} GRUB_PACKED;
- +
- +/* interrupter registers */
- +struct grub_xhci_ir {
- + grub_uint32_t iman;
- + grub_uint32_t imod;
- + grub_uint32_t erstsz;
- + grub_uint32_t reserved_01;
- + grub_uint32_t erstba_low;
- + grub_uint32_t erstba_high;
- + grub_uint32_t erdp_low;
- + grub_uint32_t erdp_high;
- +} GRUB_PACKED;
- +
- +struct grub_xhci_psid {
- + grub_uint8_t id;
- + grub_uint8_t psie;
- + grub_uint16_t psim;
- + grub_uint64_t bitrate;
- + grub_usb_speed_t grub_usb_speed;
- +};
- +
- +struct grub_xhci_psids {
- + grub_uint8_t major;
- + grub_uint8_t minor;
- + struct grub_xhci_psid psids[16];
- +};
- +
- +struct grub_xhci
- +{
- + grub_uint8_t shutdown; /* 1 if preparing shutdown of controller */
- + /* xhci registers */
- + volatile struct grub_xhci_caps *caps; /* Capability registers */
- + volatile struct grub_xhci_op *op; /* Operational registers */
- + volatile struct grub_xhci_pr *pr; /* Port Registers */
- + volatile struct grub_xhci_db *db; /* doorbell */
- + volatile struct grub_xhci_ir *ir; /* Interrupt Registers */
- + /* devinfo */
- + grub_uint32_t xcap;
- + grub_uint32_t ports;
- + grub_uint32_t slots;
- + grub_uint8_t flag64;
- + grub_uint16_t spb;
- + grub_uint32_t pagesize;
- + struct xhci_portmap usb2;
- + struct xhci_portmap usb3;
- + struct grub_xhci_psids *psids;
- + /* xhci data structures */
- + struct grub_pci_dma_chunk *devs_dma;
- + volatile struct grub_xhci_devlist *devs;
- + struct grub_pci_dma_chunk *cmds_dma;
- + volatile struct grub_xhci_ring *cmds;
- + struct grub_pci_dma_chunk *evts_dma;
- + volatile struct grub_xhci_ring *evts;
- + struct grub_pci_dma_chunk *eseg_dma;
- + volatile struct grub_xhci_er_seg *eseg;
- + struct grub_pci_dma_chunk *spba_dma;
- + struct grub_pci_dma_chunk *spad_dma;
- +
- + struct grub_xhci *next;
- +};
- +
- +struct grub_xhci_priv {
- + grub_uint8_t slotid;
- + grub_uint32_t max_packet;
- + struct grub_pci_dma_chunk *enpoint_trbs_dma[32];
- + volatile struct grub_xhci_ring *enpoint_trbs[32];
- + struct grub_pci_dma_chunk *slotctx_dma;
- +};
- +
- +struct grub_xhci_port {
- + grub_uint32_t portsc;
- + grub_uint32_t portpmsc;
- + grub_uint32_t portli;
- + grub_uint32_t reserved_01;
- +};
- +
- +struct grub_xhci_transfer_controller_data {
- + grub_uint32_t transfer_size;
- +};
- +
- +static struct grub_xhci *xhci;
- +
- +/****************************************************************
- + * general access functions
- + ****************************************************************/
- +
- +static inline void
- +grub_xhci_write32(volatile void *addr, grub_uint32_t val) {
- + *(volatile grub_uint32_t *)addr = val;
- +}
- +static inline void
- +grub_xhci_write16(volatile void *addr, grub_uint16_t val) {
- + *(volatile grub_uint16_t *)addr = val;
- +}
- +static inline void
- +grub_xhci_write8(void *addr, grub_uint8_t val) {
- + *(volatile grub_uint8_t *)addr = val;
- +}
- +
- +static inline grub_uint32_t
- +grub_xhci_read32(volatile void *addr) {
- + return grub_le_to_cpu32 (*((volatile grub_uint32_t *)addr));
- +}
- +
- +static inline grub_uint16_t
- +grub_xhci_read16(volatile void *addr) {
- + return grub_le_to_cpu16 (*((volatile grub_uint32_t *)addr));
- +}
- +static inline grub_uint8_t
- +grub_xhci_read8(volatile void *addr) {
- + return (*((volatile grub_uint32_t *)addr));
- +}
- +
- +static inline grub_uint32_t
- +grub_xhci_port_read (struct grub_xhci *x, grub_uint32_t port)
- +{
- + return grub_xhci_read32(&x->pr[port].portsc);
- +}
- +
- +static inline void
- +grub_xhci_port_write (struct grub_xhci *x, grub_uint32_t port,
- + grub_uint32_t and_mask, grub_uint32_t or_mask)
- +{
- + grub_uint32_t reg = grub_xhci_port_read(x, port);
- + reg &= and_mask;
- + reg |= or_mask;
- +
- + grub_xhci_write32(&x->pr[port].portsc, reg);
- +}
- +
- +/****************************************************************
- + * xhci status and support functions
- + ****************************************************************/
- +
- +static grub_uint32_t xhci_get_pagesize(struct grub_xhci *x)
- +{
- + /* Chapter 5.4.3 Page Size Register (PAGESIZE) */
- + for (grub_uint8_t i = 0; i < 16; i++)
- + {
- + if (grub_xhci_read32(&x->op->pagesize) & (1 << i))
- + return 1 << (12 + i);
- + }
- + return 0;
- +}
- +
- +static grub_uint8_t xhci_is_halted(struct grub_xhci *x)
- +{
- + return !!(grub_xhci_read32(&x->op->usbsts) & 1);
- +}
- +
- +/* Just for debugging */
- +static void xhci_check_status(struct grub_xhci *x)
- +{
- + grub_uint32_t reg;
- +
- + reg = grub_xhci_read32(&x->op->usbsts);
- + if (reg & 1)
- + grub_dprintf("xhci", "%s: xHCI halted\n", __func__);
- + if (reg & 2)
- + grub_dprintf("xhci", "%s: Host system error detected\n", __func__);
- + if (reg & (1 << 12))
- + grub_dprintf("xhci", "%s: Internal error detected\n", __func__);
- + reg = grub_xhci_read32(&x->op->crcr_low);
- + if (reg & (1 << 3))
- + grub_dprintf("xhci", "%s: Command ring running\n", __func__);
- +}
- +
- +/* xhci_memalign_dma32 allocates DMA memory satisfying alignment and boundary
- + * requirements without wasting to much memory */
- +static struct grub_pci_dma_chunk *
- +xhci_memalign_dma32(grub_size_t align,
- + grub_size_t size,
- + grub_size_t boundary)
- +{
- + struct grub_pci_dma_chunk *tmp;
- + const grub_uint32_t mask = boundary - 1;
- + grub_uint32_t start, end;
- +
- + /* Allocate some memory and check if it doesn't cross boundary */
- + tmp = grub_memalign_dma32(align, size);
- + start = grub_dma_get_phys(tmp);
- + end = start + size - 1;
- + if ((start & mask) == (end & mask))
- + return tmp;
- + /* Buffer isn't usable, allocate bigger one */
- + grub_dma_free(tmp);
- +
- + return grub_memalign_dma32(boundary, size);
- +}
- +
- +/****************************************************************
- + * helper functions for in context DMA buffer
- + ****************************************************************/
- +
- +static int
- +grub_xhci_inctx_size(struct grub_xhci *x)
- +{
- + const grub_uint8_t cnt = GRUB_XHCI_MAX_ENDPOINTS + 1;
- + return (sizeof(struct grub_xhci_inctx) * cnt) << x->flag64;
- +}
- +
- +static void
- +grub_xhci_inctx_sync_dma_caches(struct grub_xhci *x, struct grub_pci_dma_chunk *inctx)
- +{
- + grub_arch_sync_dma_caches(inctx, grub_xhci_inctx_size(x));
- +}
- +
- +static struct grub_pci_dma_chunk *
- +grub_xhci_alloc_inctx(struct grub_xhci *x, int maxepid,
- + struct grub_usb_device *dev)
- +{
- + int size = grub_xhci_inctx_size(x);
- + struct grub_pci_dma_chunk *dma = xhci_memalign_dma32(ALIGN_INCTX, size,
- + x->pagesize);
- + if (!dma)
- + return NULL;
- +
- + volatile struct grub_xhci_inctx *in = grub_dma_get_virt(dma);
- + grub_memset((void *)in, 0, size);
- +
- + struct grub_xhci_slotctx *slot = (void*)&in[1 << x->flag64];
- + slot->ctx[0] |= maxepid << 27; /* context entries */
- + grub_dprintf("xhci", "%s: speed=%d root_port=%d\n", __func__, dev->speed, dev->root_port);
- + switch (dev->speed) {
- + case GRUB_USB_SPEED_FULL:
- + slot->ctx[0] |= XHCI_USB_FULLSPEED << 20;
- + break;
- + case GRUB_USB_SPEED_HIGH:
- + slot->ctx[0] |= XHCI_USB_HIGHSPEED << 20;
- + break;
- + case GRUB_USB_SPEED_LOW:
- + slot->ctx[0] |= XHCI_USB_LOWSPEED << 20;
- + break;
- + case GRUB_USB_SPEED_SUPER:
- + slot->ctx[0] |= XHCI_USB_SUPERSPEED << 20;
- + break;
- + case GRUB_USB_SPEED_NONE:
- + slot->ctx[0] |= 0 << 20;
- + break;
- + }
- +
- + /* Route is greater zero on devices that are connected to a non root hub */
- + if (dev->route)
- + {
- + /* FIXME: Implement this code for non SuperSpeed hub devices */
- + }
- + slot->ctx[0] |= dev->route;
- + slot->ctx[1] |= (dev->root_port+1) << 16;
- +
- + grub_arch_sync_dma_caches(in, size);
- +
- + return dma;
- +}
- +
- +/****************************************************************
- + * xHCI event processing
- + ****************************************************************/
- +
- +/* Dequeue events on the XHCI event ring generated by the hardware */
- +static void xhci_process_events(struct grub_xhci *x)
- +{
- + volatile struct grub_xhci_ring *evts = x->evts;
- + /* XXX invalidate caches */
- +
- + for (;;) {
- + /* check for event */
- + grub_uint32_t nidx = grub_xhci_read32(&evts->nidx);
- + grub_uint32_t cs = grub_xhci_read32(&evts->cs);
- + volatile struct grub_xhci_trb *etrb = evts->ring + nidx;
- + grub_uint32_t control = grub_xhci_read32(&etrb->control);
- + if ((control & TRB_C) != (cs ? 1 : 0))
- + return;
- +
- + /* process event */
- + grub_uint32_t evt_type = TRB_TYPE(control);
- + grub_uint32_t evt_cc = (grub_xhci_read32(&etrb->status) >> 24) & 0xff;
- +
- + switch (evt_type)
- + {
- + case ER_TRANSFER:
- + case ER_COMMAND_COMPLETE:
- + {
- + struct grub_xhci_trb *rtrb = (void*)grub_xhci_read32(&etrb->ptr_low);
- + struct grub_xhci_ring *ring = XHCI_RING(rtrb);
- + volatile struct grub_xhci_trb *evt = &ring->evt;
- + grub_uint32_t eidx = rtrb - ring->ring + 1;
- + grub_dprintf("xhci", "%s: ring %p [trb %p, evt %p, type %d, eidx %d, cc %d]\n",
- + __func__, ring, rtrb, evt, evt_type, eidx, evt_cc);
- + *evt = *etrb;
- + grub_xhci_write32(&ring->eidx, eidx);
- + break;
- + }
- + case ER_PORT_STATUS_CHANGE:
- + {
- + /* Nothing to do here. grub_xhci_detect_dev will handle it */
- + break;
- + }
- + default:
- + {
- + grub_dprintf("xhci", "%s: unknown event, type %d, cc %d\n",
- + __func__, evt_type, evt_cc);
- + break;
- + }
- + }
- +
- + /* move ring index, notify xhci */
- + nidx++;
- + if (nidx == GRUB_XHCI_RING_ITEMS)
- + {
- + nidx = 0;
- + cs = cs ? 0 : 1;
- + grub_xhci_write32(&evts->cs, cs);
- + }
- + grub_xhci_write32(&evts->nidx, nidx);
- + volatile struct grub_xhci_ir *ir = x->ir;
- + grub_uint32_t erdp = (grub_uint32_t)(evts->ring + nidx);
- + grub_xhci_write32(&ir->erdp_low, erdp);
- + grub_xhci_write32(&ir->erdp_high, 0);
- + }
- +}
- +
- +/****************************************************************
- + * TRB handling
- + ****************************************************************/
- +
- +/* Signal the hardware to process events on a TRB ring */
- +static void xhci_doorbell(struct grub_xhci *x, grub_uint32_t slotid, grub_uint32_t value)
- +{
- + xhci_check_status(x);
- + grub_dprintf("xhci", "%s: slotid %d, epid %d\n", __func__, slotid, value);
- + grub_xhci_write32(&x->db[slotid].doorbell, value);
- +}
- +
- +/* Check if a ring has any pending TRBs */
- +static int xhci_ring_busy(volatile struct grub_xhci_ring *ring)
- +{
- + grub_uint32_t eidx = grub_xhci_read32(&ring->eidx);
- + grub_uint32_t nidx = grub_xhci_read32(&ring->nidx);
- +
- + return (eidx != nidx);
- +}
- +
- +/* Returns free space in ring */
- +static int xhci_ring_free_space(volatile struct grub_xhci_ring *ring)
- +{
- + grub_uint32_t eidx = grub_xhci_read32(&ring->eidx);
- + grub_uint32_t nidx = grub_xhci_read32(&ring->nidx);
- +
- + /* nidx is never 0, so reduce ring buffer size by one */
- + return (eidx > nidx) ? eidx-nidx
- + : (ARRAY_SIZE(ring->ring) - 1) - nidx + eidx;
- +}
- +
- +/* Check if a ring is full */
- +static int xhci_ring_full(volatile struct grub_xhci_ring *ring)
- +{
- + /* Might need to insert one link TRB */
- + return xhci_ring_free_space(ring) <= 1;
- +}
- +
- +/* Check if a ring is almost full */
- +static int xhci_ring_almost_full(volatile struct grub_xhci_ring *ring)
- +{
- + /* Might need to insert one link TRB */
- + return xhci_ring_free_space(ring) <= 2;
- +}
- +
- +/* Wait for a ring to empty (all TRBs processed by hardware) */
- +static int xhci_event_wait(struct grub_xhci *x,
- + volatile struct grub_xhci_ring *ring,
- + grub_uint32_t timeout)
- +{
- + grub_uint32_t end = grub_get_time_ms () + timeout;
- +
- + for (;;)
- + {
- + xhci_check_status(x);
- + xhci_process_events(x);
- + if (!xhci_ring_busy(ring))
- + {
- + grub_uint32_t status = ring->evt.status;
- + return (status >> 24) & 0xff;
- + }
- + if (grub_get_time_ms () > end)
- + {
- + xhci_check_status(x);
- + grub_dprintf("xhci", "%s: Timeout waiting for event\n", __func__);
- + return -1;
- + }
- + }
- +}
- +
- +/* Add a TRB to the given ring, either regular or inline */
- +static void xhci_trb_fill(volatile struct grub_xhci_ring *ring,
- + grub_uint64_t ptr, grub_uint32_t xferlen,
- + grub_uint32_t flags)
- +{
- + volatile struct grub_xhci_trb *dst = &ring->ring[ring->nidx];
- + dst->ptr_low = ptr & 0xffffffff;
- + dst->ptr_high = ptr >> 32;
- + dst->status = xferlen;
- + dst->control = flags | (ring->cs ? TRB_C : 0);
- +
- + grub_arch_sync_dma_caches(dst, sizeof(ring->ring[0]));
- +}
- +
- +/*
- + * Queue a TRB onto a ring.
- + *
- + * The caller must pass a pointer to the data in physical address-space or the
- + * data itself (but no more than 8 bytes) in data_or_addr. Inline data must have
- + * the flag TRB_TR_IDT set.
- + */
- +static void xhci_trb_queue(volatile struct grub_xhci_ring *ring,
- + grub_uint64_t data_or_addr,
- + grub_uint32_t xferlen, grub_uint32_t flags)
- +{
- + grub_dprintf("xhci", "%s: ring %p data %llx len %d flags 0x%x remain 0x%x\n", __func__,
- + ring, data_or_addr, xferlen & 0x1ffff, flags, xferlen >> 17);
- +
- + if (xhci_ring_full(ring))
- + {
- + grub_dprintf("xhci", "%s: ERROR: ring %p is full, discarding TRB\n",
- + __func__, ring);
- + return;
- + }
- +
- + if (ring->nidx >= ARRAY_SIZE(ring->ring) - 1)
- + {
- + /* Reset to command buffer pointer to the first element */
- + xhci_trb_fill(ring, (grub_addr_t)ring->ring, 0, (TR_LINK << 10) | TRB_LK_TC);
- + ring->nidx = 0;
- + ring->cs ^= 1;
- + grub_dprintf("xhci", "%s: ring %p [linked]\n", __func__, ring);
- + }
- +
- + xhci_trb_fill(ring, data_or_addr, xferlen, flags);
- + ring->nidx++;
- + grub_dprintf("xhci", "%s: ring %p [nidx %d, len %d]\n",
- + __func__, ring, ring->nidx, xferlen);
- +}
- +
- +/*
- + * Queue a TRB onto a ring and flush it if necessary.
- + *
- + * The caller must pass a pointer to the data in physical address-space or the
- + * data itself (but no more than 8 bytes) in data_or_addr. Inline data must have
- + * the flag TRB_TR_IDT set.
- + */
- +static int xhci_trb_queue_and_flush(struct grub_xhci *x,
- + grub_uint32_t slotid,
- + grub_uint32_t epid,
- + volatile struct grub_xhci_ring *ring,
- + grub_uint64_t data_or_addr,
- + grub_uint32_t xferlen, grub_uint32_t flags)
- +{
- + grub_uint8_t submit = 0;
- + if (xhci_ring_almost_full(ring))
- + {
- + grub_dprintf("xhci", "%s: almost full e %d n %d\n", __func__, ring->eidx, ring->nidx);
- + flags |= TRB_TR_IOC;
- + submit = 1;
- + }
- + /* Note: xhci_trb_queue might queue on or two elements, if the end of the TRB
- + * has been reached. The caller must account for that when filling the TRB. */
- + xhci_trb_queue(ring, data_or_addr, xferlen, flags);
- + /* Submit if less no free slot is remaining, we might need an additional
- + * one on the next call to this function. */
- + if (submit)
- + {
- + xhci_doorbell(x, slotid, epid);
- + int rc = xhci_event_wait(x, ring, 1000);
- + grub_dprintf("xhci", "%s: xhci_event_wait = %d\n", __func__, rc);
- + return rc;
- + }
- + return 0;
- +}
- +
- +/****************************************************************
- + * xHCI command functions
- + ****************************************************************/
- +
- +/* Submit a command to the xHCI command TRB */
- +static int xhci_cmd_submit(struct grub_xhci *x,
- + struct grub_pci_dma_chunk *inctx_dma,
- + grub_uint32_t flags)
- +{
- + volatile struct grub_xhci_inctx *inctx;
- + /* Don't submit if halted, it will fail */
- + if (xhci_is_halted(x))
- + return -1;
- +
- + if (inctx_dma)
- + {
- + grub_xhci_inctx_sync_dma_caches(x, inctx_dma);
- +
- + inctx = grub_dma_get_virt(inctx_dma);
- +
- + struct grub_xhci_slotctx *slot = (void*)&inctx[1 << x->flag64];
- + grub_uint32_t port = ((slot->ctx[1] >> 16) & 0xff) - 1;
- + grub_uint32_t portsc = grub_xhci_port_read(x, port);
- + if (!(portsc & GRUB_XHCI_PORTSC_CCS))
- + {
- + grub_dprintf("xhci", "%s: root port %d no longer connected\n",
- + __func__, port);
- + return -1;
- + }
- + xhci_trb_queue(x->cmds, grub_dma_get_phys(inctx_dma), 0, flags);
- + }
- + else
- + {
- + xhci_trb_queue(x->cmds, 0, 0, flags);
- + }
- +
- + xhci_doorbell(x, 0, 0);
- + int rc = xhci_event_wait(x, x->cmds, 1000);
- + grub_dprintf("xhci", "%s: xhci_event_wait = %d\n", __func__, rc);
- +
- + return rc;
- +}
- +
- +static int xhci_cmd_enable_slot(struct grub_xhci *x)
- +{
- + grub_uint32_t flags = 0;
- + flags |= (CR_ENABLE_SLOT << 10);
- +
- + grub_dprintf("xhci", "%s:\n", __func__);
- + int cc = xhci_cmd_submit(x, NULL, flags);
- + if (cc != CC_SUCCESS)
- + return -1;
- + grub_dprintf("xhci", "%s: %p\n", __func__, &x->cmds->evt.control);
- + grub_dprintf("xhci", "%s: %x\n", __func__, grub_xhci_read32(&x->cmds->evt.control));
- +
- + return (grub_xhci_read32(&x->cmds->evt.control) >> 24) & 0xff;
- +}
- +
- +static int xhci_cmd_disable_slot(struct grub_xhci *x, grub_uint32_t slotid)
- +{
- + grub_uint32_t flags = 0;
- + flags |= (CR_DISABLE_SLOT << 10);
- + flags |= (slotid << 24);
- +
- + grub_dprintf("xhci", "%s: slotid %d\n", __func__, slotid);
- + return xhci_cmd_submit(x, NULL, flags);
- +}
- +
- +static int xhci_cmd_stop_endpoint(struct grub_xhci *x, grub_uint32_t slotid
- + , grub_uint32_t epid
- + , grub_uint32_t suspend)
- +{
- + grub_uint32_t flags = 0;
- + flags |= (CR_STOP_ENDPOINT << 10);
- + flags |= (epid << 16);
- + flags |= (suspend << 23) ;
- + flags |= (slotid << 24);
- +
- + return xhci_cmd_submit(x, NULL, flags);
- +}
- +
- +static int xhci_cmd_reset_endpoint(struct grub_xhci *x, grub_uint32_t slotid
- + , grub_uint32_t epid
- + , grub_uint32_t preserve)
- +{
- + grub_uint32_t flags = 0;
- + flags |= (preserve << 9);
- + flags |= (CR_RESET_ENDPOINT << 10);
- + flags |= (epid << 16);
- + flags |= (slotid << 24);
- +
- + return xhci_cmd_submit(x, NULL, flags);
- +}
- +
- +static int xhci_cmd_set_dequeue_pointer(struct grub_xhci *x, grub_uint32_t slotid
- + , grub_uint32_t epid
- + , grub_addr_t tr_deque_pointer)
- +{
- + grub_uint32_t flags = 0;
- + flags |= (CR_SET_TR_DEQUEUE << 10);
- + flags |= (epid << 16);
- + flags |= (slotid << 24);
- +
- + xhci_trb_queue(x->cmds, tr_deque_pointer, 0, flags);
- +
- + xhci_doorbell(x, 0, 0);
- + int rc = xhci_event_wait(x, x->cmds, 1000);
- + grub_dprintf("xhci", "%s: xhci_event_wait = %d\n", __func__, rc);
- +
- + return rc;
- +}
- +
- +static int xhci_cmd_address_device(struct grub_xhci *x, grub_uint32_t slotid,
- + struct grub_pci_dma_chunk *inctx_dma)
- +{
- + grub_uint32_t flags = 0;
- + flags |= (CR_ADDRESS_DEVICE << 10);
- + flags |= (slotid << 24);
- +
- + grub_dprintf("xhci", "%s: slotid %d\n", __func__, slotid);
- + return xhci_cmd_submit(x, inctx_dma, flags);
- +}
- +
- +static int xhci_cmd_configure_endpoint(struct grub_xhci *x, grub_uint32_t slotid,
- + struct grub_pci_dma_chunk *inctx_dma)
- +{
- + grub_uint32_t flags = 0;
- + flags |= (CR_CONFIGURE_ENDPOINT << 10);
- + flags |= (slotid << 24);
- +
- + grub_dprintf("xhci", "%s: slotid %d\n", __func__, slotid);
- + return xhci_cmd_submit(x, inctx_dma, flags);
- +}
- +
- +static int xhci_cmd_evaluate_context(struct grub_xhci *x, grub_uint32_t slotid,
- + struct grub_pci_dma_chunk *inctx_dma)
- +{
- + grub_uint32_t flags = 0;
- + flags |= (CR_EVALUATE_CONTEXT << 10);
- + flags |= (slotid << 24);
- +
- + grub_dprintf("xhci", "%s: slotid %d\n", __func__, slotid);
- + return xhci_cmd_submit(x, inctx_dma, flags);
- +}
- +
- +/****************************************************************
- + * xHCI host controller initialization
- + ****************************************************************/
- +
- +static grub_usb_err_t
- +grub_xhci_reset (struct grub_xhci *x)
- +{
- + grub_uint32_t reg;
- + grub_uint32_t end;
- +
- + reg = grub_xhci_read32(&x->op->usbcmd);
- + if (reg & GRUB_XHCI_CMD_RS)
- + {
- + reg &= ~GRUB_XHCI_CMD_RS;
- + grub_xhci_write32(&x->op->usbcmd, reg);
- +
- + end = grub_get_time_ms () + 32;
- + while (grub_xhci_read32(&x->op->usbcmd) & GRUB_XHCI_STS_HCH)
- + {
- + if (grub_get_time_ms () > end)
- + return GRUB_USB_ERR_TIMEOUT;
- +
- + grub_millisleep(1);
- + }
- + }
- +
- + grub_dprintf("xhci", "grub_xhci_reset: resetting HC\n");
- + grub_xhci_write32(&x->op->usbcmd, GRUB_XHCI_CMD_HCRST);
- +
- + /* Wait for device to complete reset and be enabled */
- + end = grub_get_time_ms () + 100;
- + while (grub_xhci_read32(&x->op->usbcmd) & GRUB_XHCI_CMD_HCRST)
- + {
- + if (grub_get_time_ms () > end)
- + return GRUB_USB_ERR_TIMEOUT;
- +
- + grub_millisleep(1);
- + }
- +
- + /* Wait for device to complete reset and be enabled */
- + end = grub_get_time_ms () + 100;
- + while (grub_xhci_read32(&x->op->usbsts) & GRUB_XHCI_STS_CNR)
- + {
- + if (grub_get_time_ms () > end)
- + return GRUB_USB_ERR_TIMEOUT;
- +
- + grub_millisleep(1);
- + }
- +
- + grub_xhci_write32(&x->op->config, x->slots);
- + grub_xhci_write32(&x->op->dcbaap_low, grub_dma_get_phys(x->devs_dma));
- + grub_xhci_write32(&x->op->dcbaap_high, 0);
- + grub_xhci_write32(&x->op->crcr_low, grub_dma_get_phys(x->cmds_dma)| 1);
- + grub_xhci_write32(&x->op->crcr_high, 0);
- + x->cmds->cs = 1;
- +
- + grub_arch_sync_dma_caches(x->cmds, sizeof(*x->cmds));
- +
- + x->eseg->ptr_low = grub_dma_get_phys(x->evts_dma);
- + x->eseg->ptr_high = 0;
- + x->eseg->size = GRUB_XHCI_RING_ITEMS;
- +
- + grub_arch_sync_dma_caches(x->eseg, sizeof(*x->eseg));
- +
- + grub_xhci_write32(&x->ir->erstsz, 1);
- + grub_xhci_write32(&x->ir->erdp_low, grub_dma_get_phys(x->evts_dma));
- + grub_xhci_write32(&x->ir->erdp_high, 0);
- + grub_xhci_write32(&x->ir->erstba_low, grub_dma_get_phys(x->eseg_dma));
- + grub_xhci_write32(&x->ir->erstba_high, 0);
- + x->evts->cs = 1;
- +
- + grub_arch_sync_dma_caches(x->evts, sizeof(*x->eseg));
- +
- + xhci_check_status(x);
- +
- + grub_dprintf ("xhci", "XHCI OP COMMAND: %08x\n",
- + grub_xhci_read32 (&x->op->usbcmd));
- + grub_dprintf ("xhci", "XHCI OP STATUS: %08x\n",
- + grub_xhci_read32 (&x->op->usbsts));
- + grub_dprintf ("xhci", "XHCI OP PAGESIZE: %08x\n",
- + grub_xhci_read32 (&x->op->pagesize));
- + grub_dprintf ("xhci", "XHCI OP DNCTRL: %08x\n",
- + grub_xhci_read32 (&x->op->dnctl));
- + grub_dprintf ("xhci", "XHCI OP CRCR_LOW: %08x\n",
- + grub_xhci_read32 (&x->op->crcr_low));
- + grub_dprintf ("xhci", "XHCI OP CRCR_HIGH: %08x\n",
- + grub_xhci_read32 (&x->op->crcr_high));
- + grub_dprintf ("xhci", "XHCI OP DCBAAP_LOW: %08x\n",
- + grub_xhci_read32 (&x->op->dcbaap_low));
- + grub_dprintf ("xhci", "XHCI OP DCBAAP_HIGH: %08x\n",
- + grub_xhci_read32 (&x->op->dcbaap_high));
- + grub_dprintf ("xhci", "XHCI OP CONFIG: %08x\n",
- + grub_xhci_read32 (&x->op->config));
- + grub_dprintf ("xhci", "XHCI IR ERSTSZ: %08x\n",
- + grub_xhci_read32 (&x->ir->erstsz));
- + grub_dprintf ("xhci", "XHCI IR ERDP: %08x\n",
- + grub_xhci_read32 (&x->ir->erdp_low));
- + grub_dprintf ("xhci", "XHCI IR ERSTBA: %08x\n",
- + grub_xhci_read32 (&x->ir->erstba_low));
- +
- + xhci_check_status(x);
- +
- + return GRUB_USB_ERR_NONE;
- +}
- +
- +static grub_usb_err_t
- +grub_xhci_request_legacy_handoff(volatile struct grub_xhci_xcap *xcap)
- +{
- + grub_uint32_t end;
- +
- + end = grub_get_time_ms () + 10;
- + for (;;)
- + {
- + grub_uint32_t cap = grub_xhci_read32(&xcap->cap);
- + if (cap & (1 << 16))
- + grub_xhci_write32(&xcap->cap, cap | (1 << 24));
- + else
- + break;
- +
- + if (grub_get_time_ms () > end)
- + {
- + grub_dprintf ("xhci","ERROR: %s TIMEOUT\n", __func__);
- + return GRUB_USB_ERR_TIMEOUT;
- + }
- + grub_millisleep(1);
- + }
- + return GRUB_USB_ERR_NONE;
- +}
- +
- +static void
- +grub_xhci_fill_default_speed_mapping(struct grub_xhci_psids *ids)
- +{
- + /* Chapter 7.2.2.1.1 "Default USB Speed ID Mapping" */
- + ids->psids[0].id = 1;
- + ids->psids[0].psie = 2;
- + ids->psids[0].psim = 12;
- + ids->psids[1].id = 2;
- + ids->psids[1].psie = 1;
- + ids->psids[1].psim = 1500;
- + ids->psids[2].id = 3;
- + ids->psids[2].psie = 2;
- + ids->psids[2].psim = 480;
- + ids->psids[3].id = 4;
- + ids->psids[3].psie = 3;
- + ids->psids[3].psim = 5;
- + ids->psids[4].id = 5;
- + ids->psids[4].psie = 3;
- + ids->psids[4].psim = 10;
- + ids->psids[5].id = 6;
- + ids->psids[5].psie = 3;
- + ids->psids[5].psim = 10;
- + ids->psids[6].id = 7;
- + ids->psids[6].psie = 3;
- + ids->psids[6].psim = 20;
- +}
- +
- +static void
- +grub_xhci_calc_speed_mapping(struct grub_xhci_psids *ids)
- +{
- + const grub_uint64_t mult[4] = {1ULL, 1000ULL, 1000000ULL, 1000000000ULL};
- +
- + for (grub_uint8_t i = 0; i < 16; i++)
- + {
- + if (ids->psids[i].id == 0)
- + continue;
- + ids->psids[i].bitrate = mult[ids->psids[i].psie & 3] * (grub_uint64_t)ids->psids[i].psim;
- + if (ids->psids[i].bitrate < 12000000ULL)
- + ids->psids[i].grub_usb_speed = GRUB_USB_SPEED_LOW;
- + else if (ids->psids[i].bitrate < 480000000ULL)
- + ids->psids[i].grub_usb_speed = GRUB_USB_SPEED_FULL;
- + else if (ids->psids[i].bitrate > 1200000000ULL)
- + ids->psids[i].grub_usb_speed = GRUB_USB_SPEED_SUPER;
- + else
- + ids->psids[i].grub_usb_speed = GRUB_USB_SPEED_HIGH;
- + }
- +}
- +
- +
- +/* PCI iteration function... */
- +void
- +grub_xhci_init_device (volatile void *regs)
- +{
- + struct grub_xhci *x;
- + grub_uint32_t hcs1, hcc, reg;
- +
- + /* Allocate memory for the controller and fill basic values. */
- + x = grub_zalloc (sizeof (*x));
- + if (!x)
- + {
- + grub_dprintf("xhci", "Failed to allocate memory\n");
- + return;
- + }
- + x->caps = (volatile struct grub_xhci_caps *) regs;
- + x->op = (volatile struct grub_xhci_op *) (((grub_uint8_t *)regs) +
- + grub_xhci_read8(&x->caps->caplength));
- + x->pr = (volatile struct grub_xhci_pr *) (((grub_uint8_t *)x->op) +
- + GRUB_XHCI_PR_OFFSET);
- + x->db = (volatile struct grub_xhci_db *) (((grub_uint8_t *)regs) +
- + grub_xhci_read32(&x->caps->dboff));
- + x->ir = (volatile struct grub_xhci_ir *) (((grub_uint8_t *)regs) +
- + grub_xhci_read32(&x->caps->rtsoff) + GRUB_XHCI_IR_OFFSET);
- +
- + grub_dprintf ("xhci", "XHCI init: CAPLENGTH: 0x%02x\n",
- + grub_xhci_read8 (&x->caps->caplength));
- + grub_dprintf ("xhci", "XHCI init: HCIVERSION: 0x%04x\n",
- + grub_xhci_read16 (&x->caps->hciversion));
- + grub_dprintf ("xhci", "XHCI init: HCSPARAMS1: 0x%08x\n",
- + grub_xhci_read32 (&x->caps->hcsparams1));
- + grub_dprintf ("xhci", "XHCI init: HCSPARAMS2: 0x%08x\n",
- + grub_xhci_read32 (&x->caps->hcsparams2));
- + grub_dprintf ("xhci", "XHCI init: HCSPARAMS3: 0x%08x\n",
- + grub_xhci_read32 (&x->caps->hcsparams3));
- + grub_dprintf ("xhci", "XHCI init: HCCPARAMS: 0x%08x\n",
- + grub_xhci_read32 (&x->caps->hcsparams3));
- + grub_dprintf ("xhci", "XHCI init: DBOFF: 0x%08x\n",
- + grub_xhci_read32 (&x->caps->dboff));
- + grub_dprintf ("xhci", "XHCI init: RTOFF: 0x%08x\n",
- + grub_xhci_read32 (&x->caps->rtsoff));
- +
- + hcs1 = grub_xhci_read32(&x->caps->hcsparams1);
- + hcc = grub_xhci_read32(&x->caps->hccparams);
- + x->ports = (grub_uint32_t) ((hcs1 >> 24) & 0xff);
- + x->slots = (grub_uint32_t) (hcs1 & 0xff);
- + x->xcap = (grub_uint32_t) ((hcc >> 16) & 0xffff) * sizeof(grub_uint32_t);
- + x->flag64 = (grub_uint8_t) ((hcc & 0x04) ? 1 : 0);
- + grub_dprintf("xhci", "XHCI init: %d ports, %d slots, %d byte contexts\n"
- + , x->ports, x->slots, x->flag64 ? 64 : 32);
- +
- + x->psids = grub_zalloc (sizeof (struct grub_xhci_psids) * x->ports);
- + if (x->xcap)
- + {
- + grub_uint32_t off;
- + volatile grub_uint8_t *addr = (grub_uint8_t *) x->caps + x->xcap;
- + do
- + {
- + volatile struct grub_xhci_xcap *xcap = (void *)addr;
- + grub_uint32_t ports, name, cap = grub_xhci_read32(&xcap->cap);
- + switch (cap & 0xff) {
- + case XHCI_CAP_LEGACY_SUPPORT:
- + {
- + if (grub_xhci_request_legacy_handoff(xcap) != GRUB_USB_ERR_NONE)
- + {
- + grub_dprintf("xhci", "XHCI init: Failed to get xHCI ownership\n");
- + goto fail;
- + }
- + break;
- + }
- + case XHCI_CAP_SUPPORTED_PROTOCOL:
- + {
- + name = grub_xhci_read32(&xcap->data[0]);
- + ports = grub_xhci_read32(&xcap->data[1]);
- + const grub_uint8_t major = (cap >> 24) & 0xff;
- + const grub_uint8_t minor = (cap >> 16) & 0xff;
- + const grub_uint8_t psic = (ports >> 28) & 0xf;
- + const grub_uint8_t count = (ports >> 8) & 0xff;
- + const grub_uint8_t start = (ports >> 0) & 0xff;
- + grub_dprintf("xhci", "XHCI init: protocol %c%c%c%c %x.%02x"
- + ", %d ports (offset %d), def %x, psic %d\n"
- + , (name >> 0) & 0xff
- + , (name >> 8) & 0xff
- + , (name >> 16) & 0xff
- + , (name >> 24) & 0xff
- + , major, minor
- + , count, start
- + , ports >> 16
- + , psic);
- + if (name == 0x20425355 /* "USB " */)
- + {
- + if (major == 2)
- + {
- + x->usb2.start = start;
- + x->usb2.count = count;
- + }
- + else if (major == 3)
- + {
- + x->usb3.start = start;
- + x->usb3.count = count;
- + }
- +
- + for (grub_uint32_t p = start - 1; p < start + count - 1UL; p++)
- + {
- + x->psids[p].major = major;
- + x->psids[p].minor = minor;
- + grub_xhci_fill_default_speed_mapping(&x->psids[p]);
- + for (grub_uint8_t i = 0; i < psic; i++)
- + {
- + grub_uint32_t psid = grub_xhci_read32(&xcap->data[3 + i]);
- + x->psids[p].psids[i].id = (psid >> 0) & 0xf;
- + x->psids[p].psids[i].psie = (psid >> 4) & 0x3;
- + x->psids[p].psids[i].psim = (psid >> 16) & 0xfffff;
- + }
- + grub_xhci_calc_speed_mapping(&x->psids[p]);
- + }
- + }
- +
- + break;
- + }
- + default:
- + {
- + grub_dprintf("xhci", "XHCI extcap 0x%x @ %p\n", cap & 0xff, addr);
- + break;
- + }
- + }
- + off = (cap >> 8) & 0xff;
- + addr += off << 2;
- + }
- + while (off > 0);
- + }
- +
- + x->pagesize = xhci_get_pagesize(x);
- + grub_dprintf("xhci", "XHCI init: Minimum supported page size 0x%x\n",
- + x->pagesize);
- +
- + /* Chapter 6.1 Device Context Base Address Array */
- + x->devs_dma = xhci_memalign_dma32(ALIGN_DCBAA,
- + sizeof(*x->devs) * (x->slots + 1),
- + x->pagesize);
- + if (!x->devs_dma)
- + goto fail;
- + x->devs = grub_dma_get_virt(x->devs_dma);
- + grub_memset((void *)x->devs, 0, sizeof(*x->devs) * (x->slots + 1));
- + grub_arch_sync_dma_caches(x->devs, sizeof(*x->devs) * (x->slots + 1));
- + grub_dprintf ("xhci", "XHCI init: device memory %p (%x)\n",
- + grub_dma_get_virt(x->devs_dma),
- + grub_dma_get_phys(x->devs_dma));
- +
- + /* Chapter 6.5 Event Ring Segment Table */
- + x->eseg_dma = xhci_memalign_dma32(ALIGN_EVT_RING_TABLE, sizeof(*x->eseg), 0);
- + if (!x->eseg_dma)
- + goto fail;
- + x->eseg = grub_dma_get_virt(x->eseg_dma);
- + grub_memset((void *)x->eseg, 0, sizeof(*x->eseg));
- + grub_arch_sync_dma_caches(x->eseg, sizeof(*x->eseg));
- + grub_dprintf ("xhci", "XHCI init: event ring table memory %p (%x)\n",
- + grub_dma_get_virt(x->eseg_dma),
- + grub_dma_get_phys(x->eseg_dma));
- +
- + x->cmds_dma = xhci_memalign_dma32(ALIGN_CMD_RING_SEG, sizeof(*x->cmds),
- + BOUNDARY_RING);
- + if (!x->cmds_dma)
- + goto fail;
- + x->cmds = grub_dma_get_virt(x->cmds_dma);
- + grub_memset((void *)x->cmds, 0, sizeof(*x->cmds));
- + grub_arch_sync_dma_caches(x->cmds, sizeof(*x->cmds));
- + grub_dprintf ("xhci", "XHCI init: command ring memory %p (%x)\n",
- + grub_dma_get_virt(x->cmds_dma),
- + grub_dma_get_phys(x->cmds_dma));
- +
- + x->evts_dma = xhci_memalign_dma32(ALIGN_EVT_RING_SEG, sizeof(*x->evts),
- + BOUNDARY_RING);
- + if (!x->evts_dma)
- + goto fail;
- + x->evts = grub_dma_get_virt(x->evts_dma);
- + grub_memset((void *)x->evts, 0, sizeof(*x->evts));
- + grub_arch_sync_dma_caches(x->evts, sizeof(*x->evts));
- + grub_dprintf ("xhci", "XHCI init: event ring memory %p (%x)\n",
- + grub_dma_get_virt(x->evts_dma),
- + grub_dma_get_phys(x->evts_dma));
- +
- + /* Chapter 4.20 Scratchpad Buffers */
- + reg = grub_xhci_read32(&x->caps->hcsparams2);
- + x->spb = (reg >> 21 & 0x1f) << 5 | reg >> 27;
- + if (x->spb)
- + {
- + volatile grub_uint64_t *spba;
- + grub_dprintf("xhci", "XHCI init: set up %d scratch pad buffers\n",
- + x->spb);
- + x->spba_dma = xhci_memalign_dma32(ALIGN_SPBA, sizeof(*spba) * x->spb,
- + x->pagesize);
- + if (!x->spba_dma)
- + goto fail;
- +
- + x->spad_dma = xhci_memalign_dma32(x->pagesize, x->pagesize * x->spb,
- + x->pagesize);
- + if (!x->spad_dma)
- + {
- + grub_dma_free(x->spba_dma);
- + goto fail;
- + }
- +
- + spba = grub_dma_get_virt(x->spba_dma);
- + for (grub_uint32_t i = 0; i < x->spb; i++)
- + spba[i] = (grub_addr_t)grub_dma_get_phys(x->spad_dma) + (i * x->pagesize);
- + grub_arch_sync_dma_caches(x->spba_dma, sizeof(*spba) * x->spb);
- +
- + x->devs[0].ptr_low = grub_dma_get_phys(x->spba_dma);
- + x->devs[0].ptr_high = 0;
- + grub_arch_sync_dma_caches(x->devs_dma, sizeof(x->devs[0]));
- + grub_dprintf ("xhci", "XHCI init: Allocated %d scratch buffers of size 0x%x\n",
- + x->spb, x->pagesize);
- + }
- +
- + grub_xhci_reset(x);
- +
- + /* Set the running bit */
- + reg = grub_xhci_read32 (&x->op->usbcmd);
- + reg |= GRUB_XHCI_CMD_RS;
- + grub_xhci_write32 (&x->op->usbcmd, reg);
- +
- +
- + /* Link to xhci now that initialisation is successful. */
- + x->next = xhci;
- + xhci = x;
- +
- + return;
- +
- +fail:
- + grub_dprintf ("xhci", "XHCI grub_xhci_pci_iter: FAILED!\n");
- + if (x)
- + {
- + if (x->devs_dma)
- + grub_dma_free (x->devs_dma);
- + if (x->eseg_dma)
- + grub_dma_free (x->eseg_dma);
- + if (x->cmds_dma)
- + grub_dma_free (x->cmds_dma);
- + if (x->evts_dma)
- + grub_dma_free (x->evts_dma);
- + if (x->spad_dma)
- + grub_dma_free (x->spad_dma);
- + if (x->spba_dma)
- + grub_dma_free (x->spba_dma);
- + }
- + grub_free (x);
- +
- + return;
- +}
- +
- +static int
- +grub_xhci_iterate (grub_usb_controller_iterate_hook_t hook, void *hook_data)
- +{
- + struct grub_xhci *x;
- + struct grub_usb_controller dev;
- +
- + for (x = xhci; x; x = x->next)
- + {
- + dev.data = x;
- + if (hook (&dev, hook_data))
- + return 1;
- + }
- +
- + return 0;
- +}
- +
- +/****************************************************************
- + * xHCI maintainance functions
- + ****************************************************************/
- +
- +static grub_usb_err_t
- +grub_xhci_update_hub_portcount (struct grub_xhci *x,
- + grub_usb_transfer_t transfer,
- + grub_uint32_t slotid)
- +{
- + struct grub_pci_dma_chunk *in_dma;
- + volatile struct grub_xhci_slotctx *hdslot;
- + grub_uint32_t epid = 0;
- +
- + if (!transfer || !transfer->dev || !transfer->dev->nports)
- + return GRUB_USB_ERR_NONE;
- +
- + hdslot = grub_dma_phys2virt(x->devs[slotid].ptr_low, x->devs_dma);
- + if ((hdslot->ctx[3] >> 27) == 3)
- + /* Already configured */
- + return 0;
- +
- + grub_dprintf("xhci", "%s: updating hub config to %d ports\n", __func__,
- + transfer->dev->nports);
- +
- + xhci_check_status(x);
- +
- + /* Allocate input context and initialize endpoint info. */
- + in_dma = grub_xhci_alloc_inctx(x, epid, transfer->dev);
- + if (!in_dma)
- + return GRUB_USB_ERR_INTERNAL;
- + volatile struct grub_xhci_inctx *in = grub_dma_get_virt(in_dma);
- +
- + in->add = (1 << epid);
- +
- + struct grub_xhci_epctx *ep = (void*)&in[(epid+1) << x->flag64];
- + ep->ctx[0] |= 1 << 26;
- + ep->ctx[1] |= transfer->dev->nports << 24;
- +
- + int cc = xhci_cmd_configure_endpoint(x, slotid, in_dma);
- + grub_dma_free(in_dma);
- +
- + if (cc != CC_SUCCESS)
- + {
- + grub_dprintf("xhci", "%s: reconf ctl endpoint: failed (cc %d)\n",
- + __func__, cc);
- + return GRUB_USB_ERR_BADDEVICE;
- + }
- +
- + return GRUB_USB_ERR_NONE;
- +}
- +
- +static grub_usb_err_t
- +grub_xhci_update_max_paket_size (struct grub_xhci *x,
- + grub_usb_transfer_t transfer,
- + grub_uint32_t slotid,
- + grub_uint32_t max_packet)
- +{
- + struct grub_pci_dma_chunk *in_dma;
- + grub_uint32_t epid = 1;
- +
- + if (!transfer || !transfer->dev || !max_packet)
- + return GRUB_USB_ERR_NONE;
- +
- + grub_dprintf("xhci", "%s: updating max packet size to 0x%x\n", __func__,
- + max_packet);
- +
- + xhci_check_status(x);
- +
- + /* Allocate input context and initialize endpoint info. */
- + in_dma = grub_xhci_alloc_inctx(x, epid, transfer->dev);
- + if (!in_dma)
- + return GRUB_USB_ERR_INTERNAL;
- + volatile struct grub_xhci_inctx *in = grub_dma_get_virt(in_dma);
- + in->add = (1 << epid);
- +
- + struct grub_xhci_epctx *ep = (void*)&in[(epid+1) << x->flag64];
- + ep->ctx[1] |= max_packet << 16;
- +
- + int cc = xhci_cmd_evaluate_context(x, slotid, in_dma);
- + grub_dma_free(in_dma);
- +
- + if (cc != CC_SUCCESS)
- + {
- + grub_dprintf("xhci", "%s: reconf ctl endpoint: failed (cc %d)\n",
- + __func__, cc);
- + return GRUB_USB_ERR_BADDEVICE;
- + }
- +
- + return GRUB_USB_ERR_NONE;
- +}
- +
- +/****************************************************************
- + * xHCI endpoint enablement functions
- + ****************************************************************/
- +
- +static grub_usb_err_t
- +grub_xhci_prepare_endpoint (struct grub_xhci *x,
- + struct grub_usb_device *dev,
- + grub_uint8_t endpoint,
- + grub_transfer_type_t dir,
- + grub_transaction_type_t type,
- + grub_uint32_t maxpaket,
- + struct grub_xhci_priv *priv)
- +{
- + grub_uint32_t epid;
- + struct grub_pci_dma_chunk *reqs_dma;
- + struct grub_pci_dma_chunk *in_dma;
- + volatile struct grub_xhci_ring *reqs;
- + volatile struct grub_xhci_slotctx *slotctx;
- +
- + if (!x || !priv)
- + return GRUB_USB_ERR_INTERNAL;
- +
- + xhci_check_status(x);
- +
- + if (endpoint == 0)
- + {
- + epid = 1;
- + }
- + else
- + {
- + epid = (endpoint & 0x0f) * 2;
- + epid += (dir == GRUB_USB_TRANSFER_TYPE_IN) ? 1 : 0;
- + }
- + grub_dprintf("xhci", "%s: epid %d\n", __func__, epid);
- +
- + /* Test if already prepared */
- + if (priv->slotid > 0 && priv->enpoint_trbs[epid] != NULL)
- + return GRUB_USB_ERR_NONE;
- +
- + /* Allocate DMA buffer as endpoint cmd TRB */
- + reqs_dma = xhci_memalign_dma32(ALIGN_TRB, sizeof(*reqs),
- + BOUNDARY_RING);
- + if (!reqs_dma)
- + return GRUB_USB_ERR_INTERNAL;
- + reqs = grub_dma_get_virt(reqs_dma);
- + grub_memset((void *)reqs, 0, sizeof(*reqs));
- + reqs->cs = 1;
- +
- + grub_arch_sync_dma_caches(reqs, sizeof(*reqs));
- +
- + /* Allocate input context and initialize endpoint info. */
- + in_dma = grub_xhci_alloc_inctx(x, epid, dev);
- + if (!in_dma)
- + {
- + grub_dma_free(reqs_dma);
- + return GRUB_USB_ERR_INTERNAL;
- + }
- + volatile struct grub_xhci_inctx *in = grub_dma_get_virt(in_dma);
- + in->add = 0x01 | (1 << epid);
- +
- + struct grub_xhci_epctx *ep = (void*)&in[(epid+1) << x->flag64];
- + switch (type)
- + {
- + case GRUB_USB_TRANSACTION_TYPE_CONTROL:
- + ep->ctx[1] |= 0 << 3;
- + break;
- + case GRUB_USB_TRANSACTION_TYPE_BULK:
- + ep->ctx[1] |= 2 << 3;
- + break;
- + }
- + if (dir == GRUB_USB_TRANSFER_TYPE_IN
- + || type== GRUB_USB_TRANSACTION_TYPE_CONTROL)
- + ep->ctx[1] |= 1 << 5;
- + ep->ctx[1] |= maxpaket << 16;
- + ep->deq_low = grub_dma_get_phys(reqs_dma);
- + ep->deq_low |= 1; /* dcs */
- + ep->length = maxpaket;
- +
- + grub_dprintf("xhci", "%s: ring %p, epid %d, max %d\n", __func__,
- + reqs, epid, maxpaket);
- + if (epid == 1 || priv->slotid == 0) {
- + /* Enable slot. */
- + int slotid = xhci_cmd_enable_slot(x);
- + if (slotid < 0)
- + {
- + grub_dprintf("xhci", "%s: enable slot: failed\n", __func__);
- + grub_dma_free(reqs_dma);
- + grub_dma_free(in_dma);
- + return GRUB_USB_ERR_BADDEVICE;
- + }
- + grub_dprintf("xhci", "%s: get slot %d assigned\n", __func__, slotid);
- +
- + grub_uint32_t size = (sizeof(struct grub_xhci_slotctx) * GRUB_XHCI_MAX_ENDPOINTS) << x->flag64;
- +
- + /* Allocate memory for the device specific slot context */
- + priv->slotctx_dma = xhci_memalign_dma32(ALIGN_SLOTCTX, size,
- + x->pagesize);
- + if (!priv->slotctx_dma)
- + {
- + grub_dprintf("xhci", "%s: grub_memalign_dma32 failed\n", __func__);
- + grub_dma_free(reqs_dma);
- + grub_dma_free(in_dma);
- + return GRUB_USB_ERR_INTERNAL;
- + }
- + slotctx = grub_dma_get_virt(priv->slotctx_dma);
- +
- + grub_dprintf("xhci", "%s: enable slot: got slotid %d\n", __func__, slotid);
- + grub_memset((void *)slotctx, 0, size);
- + grub_arch_sync_dma_caches(slotctx, size);
- +
- + x->devs[slotid].ptr_low = grub_dma_get_phys(priv->slotctx_dma);
- + x->devs[slotid].ptr_high = 0;
- + grub_arch_sync_dma_caches(&x->devs[slotid], sizeof(x->devs[0]));
- +
- + /* Send set_address command. */
- + int cc = xhci_cmd_address_device(x, slotid, in_dma);
- + if (cc != CC_SUCCESS)
- + {
- + grub_dprintf("xhci","%s: address device: failed (cc %d)\n", __func__, cc);
- + cc = xhci_cmd_disable_slot(x, slotid);
- + if (cc != CC_SUCCESS) {
- + grub_dprintf("xhci", "%s: disable failed (cc %d)\n", __func__, cc);
- + } else {
- + x->devs[slotid].ptr_low = 0;
- + x->devs[slotid].ptr_high = 0;
- + grub_arch_sync_dma_caches(&x->devs[slotid], sizeof(x->devs[0]));
- + }
- + grub_dma_free(priv->slotctx_dma);
- + grub_dma_free(reqs_dma);
- + grub_dma_free(in_dma);
- + return GRUB_USB_ERR_BADDEVICE;
- + }
- + priv->enpoint_trbs[epid] = reqs;
- + priv->enpoint_trbs_dma[epid] = reqs_dma;
- + priv->slotid = slotid;
- + priv->max_packet = 0;
- + }
- + if (epid != 1)
- + {
- + /* Send configure command. */
- + int cc = xhci_cmd_configure_endpoint(x, priv->slotid, in_dma);
- + if (cc != CC_SUCCESS)
- + {
- + grub_dprintf("xhci", "%s: configure endpoint: failed (cc %d)\n",
- + __func__, cc);
- + grub_dma_free(reqs_dma);
- + grub_dma_free(in_dma);
- + return GRUB_USB_ERR_BADDEVICE;
- + }
- + priv->enpoint_trbs[epid] = reqs;
- + priv->enpoint_trbs_dma[epid] = reqs_dma;
- + }
- +
- + grub_dprintf("xhci", "%s: done\n", __func__);
- + grub_dma_free(in_dma);
- +
- + return GRUB_USB_ERR_NONE;
- +}
- +
- +
- +/****************************************************************
- + * xHCI transfer helper functions
- + ****************************************************************/
- +
- +static grub_usb_err_t
- +grub_xhci_usb_to_grub_err (unsigned char status)
- +{
- + if (status != CC_SUCCESS)
- + grub_dprintf("xhci", "%s: xfer failed (cc %d)\n", __func__, status);
- + else
- + grub_dprintf("xhci", "%s: xfer done (cc %d)\n", __func__, status);
- +
- + if (status == CC_BABBLE_DETECTED)
- + return GRUB_USB_ERR_BABBLE;
- + else if (status == CC_DATA_BUFFER_ERROR)
- + return GRUB_USB_ERR_DATA;
- + else if (status == CC_STALL_ERROR)
- + return GRUB_USB_ERR_STALL;
- + else if (status != CC_SUCCESS)
- + return GRUB_USB_ERR_NAK;
- +
- + return GRUB_USB_ERR_NONE;
- +}
- +
- +static int
- +grub_xhci_transfer_is_zlp(grub_usb_transfer_t transfer, int idx)
- +{
- + if (idx >= transfer->transcnt)
- + return 0;
- +
- + grub_usb_transaction_t tr = &transfer->transactions[idx];
- +
- + return (tr->size == 0) &&
- + ((tr->pid == GRUB_USB_TRANSFER_TYPE_OUT) ||
- + (tr->pid == GRUB_USB_TRANSFER_TYPE_IN));
- +}
- +
- +static int
- +grub_xhci_transfer_is_last(grub_usb_transfer_t transfer, int idx)
- +{
- + return (idx + 1) == transfer->transcnt;
- +}
- +
- +static int
- +grub_xhci_transfer_is_data(grub_usb_transfer_t transfer, int idx)
- +{
- + grub_usb_transaction_t tr;
- +
- + if (idx >= transfer->transcnt)
- + return 0;
- +
- + tr = &transfer->transactions[idx];
- + if (tr->size == 0 ||
- + (tr->pid == GRUB_USB_TRANSFER_TYPE_SETUP))
- + return 0;
- +
- + /* If there's are no DATA pakets before it's a DATA paket */
- + for (int i = idx - 1; i >= 0; i--)
- + {
- + tr = &transfer->transactions[i];
- + if (tr->size > 0 &&
- + ((tr->pid == GRUB_USB_TRANSFER_TYPE_OUT) ||
- + (tr->pid == GRUB_USB_TRANSFER_TYPE_IN)))
- + return 0;
- + }
- + return 1;
- +}
- +
- +static int
- +grub_xhci_transfer_is_in(grub_usb_transfer_t transfer, int idx)
- +{
- + grub_usb_transaction_t tr;
- +
- + if (idx >= transfer->transcnt)
- + return 0;
- +
- + tr = &transfer->transactions[idx];
- +
- + return tr->pid == GRUB_USB_TRANSFER_TYPE_IN;
- +}
- +
- +static int
- +grub_xhci_transfer_is_normal(grub_usb_transfer_t transfer, int idx)
- +{
- + grub_usb_transaction_t tr;
- +
- + if (idx >= transfer->transcnt)
- + return 0;
- +
- + tr = &transfer->transactions[idx];
- + if (tr->size == 0 ||
- + (tr->pid == GRUB_USB_TRANSFER_TYPE_SETUP))
- + return 0;
- +
- + /* If there's at least one DATA paket before it's a normal */
- + for (int i = idx - 1; i >= 0; i--)
- + {
- + tr = &transfer->transactions[i];
- + if (tr->size > 0 &&
- + ((tr->pid == GRUB_USB_TRANSFER_TYPE_OUT) ||
- + (tr->pid == GRUB_USB_TRANSFER_TYPE_IN)))
- + return 1;
- +
- + }
- + return 0;
- +}
- +
- +static int
- +grub_xhci_transfer_next_is_normal(grub_usb_transfer_t transfer, int idx)
- +{
- + return grub_xhci_transfer_is_normal(transfer, idx + 1);
- +}
- +
- +static int
- +grub_xhci_transfer_next_is_in(grub_usb_transfer_t transfer, int idx)
- +{
- + return grub_xhci_transfer_is_in(transfer, idx + 1);
- +}
- +
- +static grub_uint8_t grub_xhci_epid_from_transfer(grub_usb_transfer_t transfer)
- +{
- + grub_uint8_t epid;
- +
- + if (transfer->endpoint == 0) {
- + epid = 1;
- + } else {
- + epid = (transfer->endpoint & 0x0f) * 2;
- + epid += (transfer->dir == GRUB_USB_TRANSFER_TYPE_IN) ? 1 : 0;
- + }
- + return epid;
- +}
- +
- +/****************************************************************
- + * xHCI transfer functions
- + ****************************************************************/
- +
- +static grub_usb_err_t
- +grub_xhci_setup_transfer (grub_usb_controller_t dev,
- + grub_usb_transfer_t transfer)
- +{
- + struct grub_xhci_transfer_controller_data *cdata;
- + struct grub_xhci *x = (struct grub_xhci *) dev->data;
- + grub_uint8_t epid;
- + grub_usb_err_t err;
- + volatile struct grub_xhci_ring *reqs;
- + int rc;
- + struct grub_xhci_priv *priv;
- +
- + xhci_check_status(x);
- +
- + if (!dev || !transfer || !transfer->dev || !transfer->dev->xhci_priv)
- + return GRUB_USB_ERR_INTERNAL;
- +
- + priv = transfer->dev->xhci_priv;
- + err = grub_xhci_prepare_endpoint(x, transfer->dev,
- + transfer->endpoint,
- + transfer->dir,
- + transfer->type,
- + transfer->max,
- + priv);
- +
- + if (err != GRUB_USB_ERR_NONE)
- + return err;
- +
- + epid = grub_xhci_epid_from_transfer(transfer);
- +
- + /* Update the max packet size once descdev.maxsize0 is valid */
- + if (epid == 1 &&
- + (priv->max_packet == 0) &&
- + (transfer->dev->descdev.maxsize0 > 0))
- + {
- + if (transfer->dev->speed == GRUB_USB_SPEED_SUPER)
- + priv->max_packet = 1UL << transfer->dev->descdev.maxsize0;
- + else
- + priv->max_packet = transfer->dev->descdev.maxsize0;
- + err = grub_xhci_update_max_paket_size(x, transfer, priv->slotid, priv->max_packet);
- + if (err != GRUB_USB_ERR_NONE)
- + {
- + grub_dprintf("xhci", "%s: Updating max paket size failed\n", __func__);
- + return err;
- + }
- + }
- + if (epid == 1 &&
- + transfer->dev->descdev.class == 9 &&
- + transfer->dev->nports > 0)
- + {
- + err = grub_xhci_update_hub_portcount(x, transfer, priv->slotid);
- + if (err != GRUB_USB_ERR_NONE)
- + {
- + grub_dprintf("xhci", "%s: Updating max paket size failed\n", __func__);
- + return err;
- + }
- + }
- +
- + /* Allocate private data for the transfer */
- + cdata = grub_zalloc(sizeof(*cdata));
- + if (!cdata)
- + return GRUB_USB_ERR_INTERNAL;
- +
- + reqs = priv->enpoint_trbs[epid];
- +
- + transfer->controller_data = cdata;
- +
- + /* Now queue the transfer onto the TRB */
- + if (transfer->type == GRUB_USB_TRANSACTION_TYPE_CONTROL)
- + {
- + volatile struct grub_usb_packet_setup *setupdata;
- + setupdata = (void *)transfer->transactions[0].data;
- + grub_dprintf("xhci", "%s: CONTROLL TRANS req %d\n", __func__, setupdata->request);
- + grub_dprintf("xhci", "%s: CONTROLL TRANS length %d\n", __func__, setupdata->length);
- +
- + if (setupdata && setupdata->request == GRUB_USB_REQ_SET_ADDRESS)
- + return GRUB_USB_ERR_NONE;
- +
- + if (transfer->transcnt < 2)
- + return GRUB_USB_ERR_INTERNAL;
- +
- + for (int i = 0; i < transfer->transcnt; i++)
- + {
- + grub_uint32_t flags = 0;
- + grub_uint64_t inline_data;
- + grub_usb_transaction_t tr = &transfer->transactions[i];
- +
- + switch (tr->pid)
- + {
- + case GRUB_USB_TRANSFER_TYPE_SETUP:
- + {
- + grub_dprintf("xhci", "%s: SETUP PKG\n", __func__);
- + grub_dprintf("xhci", "%s: transfer->size %d\n", __func__, transfer->size);
- + grub_dprintf("xhci", "%s: tr->size %d SETUP PKG\n", __func__, tr->size);
- +
- + flags |= (TR_SETUP << 10);
- + flags |= TRB_TR_IDT;
- +
- + if (transfer->size > 0)
- + {
- + if (grub_xhci_transfer_next_is_in(transfer, i))
- + flags |= (3 << 16); /* TRT IN */
- + else
- + flags |= (2 << 16); /* TRT OUT */
- + }
- + break;
- + }
- + case GRUB_USB_TRANSFER_TYPE_OUT:
- + {
- + grub_dprintf("xhci", "%s: OUT PKG\n", __func__);
- + cdata->transfer_size += tr->size;
- + break;
- + }
- + case GRUB_USB_TRANSFER_TYPE_IN:
- + {
- + grub_dprintf("xhci", "%s: IN PKG\n", __func__);
- + cdata->transfer_size += tr->size;
- + flags |= TRB_TR_DIR;
- + break;
- + }
- + }
- +
- + if (grub_xhci_transfer_is_normal(transfer, i))
- + flags |= (TR_NORMAL << 10);
- + else if (grub_xhci_transfer_is_data(transfer, i))
- + flags |= (TR_DATA << 10);
- + else if (grub_xhci_transfer_is_zlp(transfer, i))
- + flags |= (TR_STATUS << 10);
- +
- + if (grub_xhci_transfer_next_is_normal(transfer, i))
- + flags |= TRB_TR_CH;
- +
- + if (grub_xhci_transfer_is_last(transfer, i))
- + flags |= TRB_TR_IOC;
- +
- + /* Assume the ring has enough free space for all TRBs */
- + if (flags & TRB_TR_IDT && tr->size <= (int)sizeof(inline_data))
- + {
- + grub_memcpy(&inline_data, (void *)tr->data, tr->size);
- + xhci_trb_queue(reqs, inline_data, tr->size, flags);
- + }
- + else
- + {
- + xhci_trb_queue(reqs, tr->data, tr->size, flags);
- + }
- + }
- + }
- + else if (transfer->type == GRUB_USB_TRANSACTION_TYPE_BULK)
- + {
- + for (int i = 0; i < transfer->transcnt; i++)
- + {
- + grub_uint32_t flags = (TR_NORMAL << 10);
- + grub_usb_transaction_t tr = &transfer->transactions[i];
- + switch (tr->pid)
- + {
- + case GRUB_USB_TRANSFER_TYPE_OUT:
- + {
- + grub_dprintf("xhci", "%s: OUT PKG\n", __func__);
- + cdata->transfer_size += tr->size;
- + break;
- + }
- + case GRUB_USB_TRANSFER_TYPE_IN:
- + {
- + grub_dprintf("xhci", "%s: IN PKG\n", __func__);
- + cdata->transfer_size += tr->size;
- + flags |= TRB_TR_DIR;
- + break;
- + }
- + case GRUB_USB_TRANSFER_TYPE_SETUP:
- + break;
- + }
- + if (grub_xhci_transfer_is_last(transfer, i))
- + flags |= TRB_TR_IOC;
- +
- + /* The ring might be to small, submit while adding new entries */
- + rc = xhci_trb_queue_and_flush(x, priv->slotid, epid,
- + reqs, tr->data, tr->size, flags);
- + if (rc < 0)
- + return GRUB_USB_ERR_TIMEOUT;
- + else if (rc > 1)
- + return grub_xhci_usb_to_grub_err(rc);
- +
- + }
- + }
- + xhci_doorbell(x, priv->slotid, epid);
- +
- + return GRUB_USB_ERR_NONE;
- +}
- +
- +static grub_usb_err_t
- +grub_xhci_check_transfer (grub_usb_controller_t dev,
- + grub_usb_transfer_t transfer, grub_size_t * actual)
- +{
- + grub_uint32_t status;
- + grub_uint32_t remaining;
- + grub_uint8_t epid;
- + volatile struct grub_xhci_ring *reqs;
- + grub_usb_err_t err;
- + int rc;
- +
- + if (!dev->data || !transfer->controller_data || !transfer->dev ||
- + !transfer->dev->xhci_priv)
- + return GRUB_USB_ERR_INTERNAL;
- +
- +
- + struct grub_xhci_priv *priv = transfer->dev->xhci_priv;
- + struct grub_xhci *x = (struct grub_xhci *) dev->data;
- + struct grub_xhci_transfer_controller_data *cdata =
- + transfer->controller_data;
- +
- + xhci_check_status(x);
- + xhci_process_events(x);
- +
- + epid = grub_xhci_epid_from_transfer(transfer);
- +
- + reqs = priv->enpoint_trbs[epid];
- +
- + /* XXX: invalidate caches */
- +
- + /* Get current status from event ring buffer */
- + status = (reqs->evt.status>> 24) & 0xff;
- + remaining = reqs->evt.status & 0xffffff;
- +
- + if (status != CC_STOPPED_LENGTH_INVALID)
- + *actual = cdata->transfer_size - remaining;
- + else
- + *actual = 0;
- +
- + if (xhci_ring_busy(reqs))
- + return GRUB_USB_ERR_WAIT;
- +
- + grub_free(cdata);
- +
- + grub_dprintf("xhci", "%s: xfer done\n", __func__);
- +
- + err = grub_xhci_usb_to_grub_err(status);
- + if (err != GRUB_USB_ERR_NONE)
- + {
- + if (status == CC_STALL_ERROR)
- + {
- + /* Clear the stall by resetting the endpoint */
- + rc = xhci_cmd_reset_endpoint(x, priv->slotid, epid, 1);
- +
- + if (rc < 0)
- + return GRUB_USB_ERR_TIMEOUT;
- +
- + return GRUB_USB_ERR_STALL;
- + }
- + else if (remaining > 0)
- + {
- + return GRUB_USB_ERR_DATA;
- + }
- + }
- +
- + return err;
- +}
- +
- +static grub_usb_err_t
- +grub_xhci_cancel_transfer (grub_usb_controller_t dev,
- + grub_usb_transfer_t transfer)
- +{
- + grub_uint8_t epid;
- + volatile struct grub_xhci_ring *reqs;
- + struct grub_pci_dma_chunk *enpoint_trbs_dma;
- + grub_addr_t deque_pointer;
- + int rc;
- +
- + if (!dev->data || !transfer->controller_data || !transfer->dev ||
- + !transfer->dev->xhci_priv)
- + return GRUB_USB_ERR_INTERNAL;
- +
- + struct grub_xhci *x = (struct grub_xhci *) dev->data;
- + struct grub_xhci_transfer_controller_data *cdata =
- + transfer->controller_data;
- + struct grub_xhci_priv *priv = transfer->dev->xhci_priv;
- +
- + epid = grub_xhci_epid_from_transfer(transfer);
- +
- + enpoint_trbs_dma = priv->enpoint_trbs_dma[epid];
- + reqs = priv->enpoint_trbs[epid];
- +
- + /* Abort current command */
- + rc = xhci_cmd_stop_endpoint(x, priv->slotid, epid, 0);
- + if (rc < 0)
- + return GRUB_USB_ERR_TIMEOUT;
- +
- + /* Reset state */
- + reqs->nidx = 0;
- + reqs->eidx = 0;
- + reqs->cs = 1;
- +
- + grub_arch_sync_dma_caches(reqs, sizeof(*reqs));
- +
- + /* Reset the dequeue pointer to the begging of the TRB */
- + deque_pointer = grub_dma_get_phys(enpoint_trbs_dma);
- + rc = xhci_cmd_set_dequeue_pointer(x, priv->slotid, epid, deque_pointer| 1 );
- + if (rc < 0)
- + return GRUB_USB_ERR_TIMEOUT;
- +
- + reqs->evt.ptr_low = 0;
- + reqs->evt.ptr_high = 0;
- + reqs->evt.control = 0;
- + reqs->evt.status = 0;
- +
- + grub_arch_sync_dma_caches(reqs, sizeof(*reqs));
- +
- + /* Restart ring buffer processing */
- + xhci_doorbell(x, priv->slotid, epid);
- +
- + grub_free (cdata);
- +
- + return GRUB_USB_ERR_NONE;
- +}
- +
- +/****************************************************************
- + * xHCI port status functions
- + ****************************************************************/
- +
- +static int
- +grub_xhci_hubports (grub_usb_controller_t dev)
- +{
- + struct grub_xhci *x = (struct grub_xhci *) dev->data;
- + grub_uint32_t portinfo;
- +
- + portinfo = x->ports;
- + grub_dprintf ("xhci", "root hub ports=%d\n", portinfo);
- + return portinfo;
- +}
- +
- +static grub_usb_err_t
- +grub_xhci_portstatus (grub_usb_controller_t dev,
- + unsigned int port, unsigned int enable)
- +{
- + struct grub_xhci *x = (struct grub_xhci *) dev->data;
- + grub_uint32_t portsc, pls;
- + grub_uint32_t end;
- +
- + portsc = grub_xhci_port_read(x, port);
- + pls = xhci_get_field(portsc, XHCI_PORTSC_PLS);
- +
- + grub_dprintf("xhci", "grub_xhci_portstatus port #%d: 0x%08x,%s%s pls %d enable %d\n",
- + port, portsc,
- + (portsc & GRUB_XHCI_PORTSC_PP) ? " powered," : "",
- + (portsc & GRUB_XHCI_PORTSC_PED) ? " enabled," : "",
- + pls, enable);
- + xhci_check_status(x);
- +
- + if ((enable && (portsc & GRUB_XHCI_PORTSC_PED)) ||
- + (!enable && !(portsc & GRUB_XHCI_PORTSC_PED)))
- + return GRUB_USB_ERR_NONE;
- +
- + if (!enable)
- + {
- + /* Disable port */
- + grub_xhci_port_write(x, port, ~0, GRUB_XHCI_PORTSC_PED);
- + return GRUB_USB_ERR_NONE;
- + }
- +
- + grub_dprintf ("xhci", "portstatus: XHCI STATUS: %08x\n",
- + grub_xhci_read32(&x->op->usbsts));
- + grub_dprintf ("xhci",
- + "portstatus: begin, iobase=%p, port=%d, status=0x%08x\n",
- + x->caps, port, portsc);
- +
- + switch (pls)
- + {
- + case PLS_U0:
- + /* A USB3 port - controller automatically performs reset */
- + break;
- + case PLS_POLLING:
- + /* A USB2 port - perform device reset */
- + grub_xhci_port_write(x, port, ~GRUB_XHCI_PORTSC_PED, GRUB_XHCI_PORTSC_PR);
- + break;
- + default:
- + return GRUB_USB_ERR_NONE;
- + }
- +
- + /* Wait for device to complete reset and be enabled */
- + end = grub_get_time_ms () + 100;
- + for (;;)
- + {
- + portsc = grub_xhci_port_read(x, port);
- + if (!(portsc & GRUB_XHCI_PORTSC_CCS))
- + {
- + /* Device disconnected during reset */
- + grub_dprintf ("xhci","ERROR: %s device disconnected\n", __func__);
- + return GRUB_USB_ERR_BADDEVICE;
- + }
- + if (portsc & GRUB_XHCI_PORTSC_PED)
- + /* Reset complete */
- + break;
- + if (grub_get_time_ms () > end)
- + {
- + grub_dprintf ("xhci","ERROR: %s TIMEOUT\n", __func__);
- + return GRUB_USB_ERR_TIMEOUT;
- + }
- + }
- + xhci_check_status(x);
- +
- + return GRUB_USB_ERR_NONE;
- +}
- +
- +/****************************************************************
- + * xHCI detect device functions
- + ****************************************************************/
- +
- +static grub_usb_speed_t
- +grub_xhci_detect_dev (grub_usb_controller_t dev, int port, int *changed)
- +{
- + struct grub_xhci *x = (struct grub_xhci *) dev->data;
- + grub_uint32_t portsc, speed;
- +
- + *changed = 0;
- + grub_dprintf("xhci", "%s: dev=%p USB%d_%d port %d\n", __func__, dev,
- + x->psids[port-1].major, x->psids[port-1].minor, port);
- +
- + /* On shutdown advertise all ports as disconnected. This will trigger
- + * a gracefull detatch. */
- + if (x->shutdown)
- + {
- + *changed = 1;
- + return GRUB_USB_SPEED_NONE;
- + }
- +
- + /* Don't advertise new devices, connecting will fail if halted */
- + if (xhci_is_halted(x))
- + return GRUB_USB_SPEED_NONE;
- +
- + portsc = grub_xhci_port_read(x, port);
- + speed = xhci_get_field(portsc, XHCI_PORTSC_SPEED);
- + grub_uint8_t pls = xhci_get_field(portsc, XHCI_PORTSC_PLS);
- +
- + grub_dprintf("xhci", "grub_xhci_portstatus port #%d: 0x%08x,%s%s pls %d\n",
- + port, portsc,
- + (portsc & GRUB_XHCI_PORTSC_PP) ? " powered," : "",
- + (portsc & GRUB_XHCI_PORTSC_PED) ? " enabled," : "",
- + pls);
- +
- + /* Connect Status Change bit - it detects change of connection */
- + if (portsc & GRUB_XHCI_PORTSC_CSC)
- + {
- + *changed = 1;
- +
- + grub_xhci_port_write(x, port, ~GRUB_XHCI_PORTSC_PED, GRUB_XHCI_PORTSC_CSC);
- + }
- +
- + if (!(portsc & GRUB_XHCI_PORTSC_CCS))
- + return GRUB_USB_SPEED_NONE;
- +
- + for (grub_uint8_t i = 0; i < 16 && x->psids[port-1].psids[i].id > 0; i++)
- + {
- + if (x->psids[port-1].psids[i].id == speed)
- + {
- + grub_dprintf("xhci", "%s: grub_usb_speed = %d\n", __func__,
- + x->psids[port-1].psids[i].grub_usb_speed );
- + return x->psids[port-1].psids[i].grub_usb_speed;
- + }
- + }
- +
- + return GRUB_USB_SPEED_NONE;
- +}
- +
- +/****************************************************************
- + * xHCI attach/detach functions
- + ****************************************************************/
- +
- +static grub_usb_err_t
- +grub_xhci_attach_dev (grub_usb_controller_t ctrl, grub_usb_device_t dev)
- +{
- + struct grub_xhci *x = (struct grub_xhci *) ctrl->data;
- + grub_usb_err_t err;
- + grub_uint32_t max;
- +
- + grub_dprintf("xhci", "%s: dev=%p\n", __func__, dev);
- +
- + if (!dev || !x)
- + return GRUB_USB_ERR_INTERNAL;
- +
- + dev->xhci_priv = grub_zalloc (sizeof (struct grub_xhci_priv));
- + if (!dev->xhci_priv)
- + return GRUB_USB_ERR_INTERNAL;
- +
- +
- + switch (dev->speed)
- + {
- + case GRUB_USB_SPEED_LOW:
- + {
- + max = 8;
- + break;
- + }
- + case GRUB_USB_SPEED_FULL:
- + case GRUB_USB_SPEED_HIGH:
- + {
- + max = 64;
- + break;
- + }
- + case GRUB_USB_SPEED_SUPER:
- + {
- + max = 512;
- + break;
- + }
- + default:
- + case GRUB_USB_SPEED_NONE:
- + {
- + max = 0;
- + }
- + }
- +
- + /* Assign a slot, assign an address and configure endpoint 0 */
- + err = grub_xhci_prepare_endpoint(x, dev,
- + 0,
- + 0,
- + GRUB_USB_TRANSACTION_TYPE_CONTROL,
- + max,
- + dev->xhci_priv);
- +
- + return err;
- +}
- +
- +static grub_usb_err_t
- +grub_xhci_detach_dev (grub_usb_controller_t ctrl, grub_usb_device_t dev)
- +{
- + struct grub_xhci *x = (struct grub_xhci *) ctrl->data;
- + struct grub_xhci_priv *priv;
- + int cc = CC_SUCCESS;
- +
- + grub_dprintf("xhci", "%s: dev=%p\n", __func__, dev);
- +
- + if (!dev)
- + return GRUB_USB_ERR_INTERNAL;
- +
- + if (dev->xhci_priv)
- + {
- + priv = dev->xhci_priv;
- + /* Stop endpoints and free ring buffer */
- + for (int i = 0; i < GRUB_XHCI_MAX_ENDPOINTS; i++)
- + {
- + if (priv->enpoint_trbs[i] != NULL)
- + {
- + cc = xhci_cmd_stop_endpoint(x, priv->slotid, i, 1);
- + if (cc != CC_SUCCESS)
- + grub_dprintf("xhci", "Failed to disable EP%d on slot %d\n", i,
- + priv->slotid);
- +
- + grub_dprintf("xhci", "grub_dma_free[%d]\n", i);
- +
- + grub_dma_free(priv->enpoint_trbs_dma[i]);
- + priv->enpoint_trbs[i] = NULL;
- + priv->enpoint_trbs_dma[i] = NULL;
- + }
- + }
- +
- + cc = xhci_cmd_disable_slot(x, priv->slotid);
- + if (cc == CC_SUCCESS)
- + {
- + if (priv->slotctx_dma)
- + grub_dma_free(priv->slotctx_dma);
- + x->devs[priv->slotid].ptr_low = 0;
- + x->devs[priv->slotid].ptr_high = 0;
- + grub_arch_sync_dma_caches(&x->devs[priv->slotid], sizeof(x->devs[0]));
- + }
- + else
- + grub_dprintf("xhci", "Failed to disable slot %d\n", priv->slotid);
- +
- + grub_free(dev->xhci_priv);
- + }
- +
- + dev->xhci_priv = NULL;
- +
- + if (cc != CC_SUCCESS)
- + return GRUB_USB_ERR_BADDEVICE;
- + return GRUB_USB_ERR_NONE;
- +}
- +
- +/****************************************************************
- + * xHCI terminate functions
- + ****************************************************************/
- +
- +static void
- +grub_xhci_halt(struct grub_xhci *x)
- +{
- + grub_uint32_t reg;
- +
- + /* Halt the command ring */
- + reg = grub_xhci_read32(&x->op->crcr_low);
- + grub_xhci_write32(&x->op->crcr_low, reg | 4);
- +
- + int rc = xhci_event_wait(x, x->cmds, 100);
- + grub_dprintf("xhci", "%s: xhci_event_wait = %d\n", __func__, rc);
- + if (rc < 0)
- + return;
- +
- + /* Stop the controller */
- + reg = grub_xhci_read32(&x->op->usbcmd);
- + if (reg & GRUB_XHCI_CMD_RS)
- + {
- + reg &= ~GRUB_XHCI_CMD_RS;
- + grub_xhci_write32(&x->op->usbcmd, reg);
- + }
- +
- + return;
- +}
- +
- +static grub_err_t
- +grub_xhci_fini_hw (int noreturn __attribute__ ((unused)))
- +{
- + struct grub_xhci *x;
- +
- + /* We should disable all XHCI HW to prevent any DMA access etc. */
- + for (x = xhci; x; x = x->next)
- + {
- + x->shutdown = 1;
- +
- + /* Gracefully detach active devices */
- + grub_usb_poll_devices(0);
- +
- + /* Check if xHCI is halted and halt it if not */
- + grub_xhci_halt (x);
- +
- + /* Reset xHCI */
- + if (grub_xhci_reset (x) != GRUB_USB_ERR_NONE)
- + return GRUB_ERR_BAD_DEVICE;
- + }
- +
- + return GRUB_ERR_NONE;
- +}
- +
- +static struct grub_usb_controller_dev usb_controller = {
- + .name = "xhci",
- + .iterate = grub_xhci_iterate,
- + .setup_transfer = grub_xhci_setup_transfer,
- + .check_transfer = grub_xhci_check_transfer,
- + .cancel_transfer = grub_xhci_cancel_transfer,
- + .hubports = grub_xhci_hubports,
- + .portstatus = grub_xhci_portstatus,
- + .detect_dev = grub_xhci_detect_dev,
- + .attach_dev = grub_xhci_attach_dev,
- + .detach_dev = grub_xhci_detach_dev,
- + /* estimated max. count of TDs for one bulk transfer */
- + .max_bulk_tds = GRUB_XHCI_RING_ITEMS - 3
- +};
- +
- +GRUB_MOD_INIT (xhci)
- +{
- + grub_stop_disk_firmware ();
- +
- + grub_boot_time ("Initing XHCI hardware");
- + grub_xhci_pci_scan ();
- + grub_boot_time ("Registering XHCI driver");
- + grub_usb_controller_dev_register (&usb_controller);
- + grub_boot_time ("XHCI driver registered");
- +}
- +
- +GRUB_MOD_FINI (xhci)
- +{
- + grub_xhci_fini_hw (0);
- + grub_usb_controller_dev_unregister (&usb_controller);
- +}
- diff --git a/include/grub/usb.h b/include/grub/usb.h
- index 609faf7d0..eb71fa1c7 100644
- --- a/include/grub/usb.h
- +++ b/include/grub/usb.h
- @@ -338,6 +338,10 @@ grub_usb_cancel_transfer (grub_usb_transfer_t trans);
- void
- grub_ehci_init_device (volatile void *regs);
- void
- +grub_xhci_init_device (volatile void *regs);
- +void
- grub_ehci_pci_scan (void);
- +void
- +grub_xhci_pci_scan (void);
-
- #endif /* GRUB_USB_H */
- --
- 2.39.2
|