On 25.06.2011 21:13, Aleš Nesrsta wrote: > Hi, > > because I still see no EHCI driver in GRUB for long time, I slowly > prepared myself something what looks to be working... > EHCI driver code is based on UHCI (OHCI) GRUB driver, no other source > code was used (copied). Very good.
> I shortly tested main functions: > - high speed device connected directly to EHCI port - working, OK > - low/full speed device connected directly to EHCI port - not working > but it is OK (it cannot work according to specification) Ir must be rerouted to companion. Some controllers (like in my thinkpad) have no companion. I haven't played with internals to see how it's done. > /* ehci.c - EHCI Support. */ > /* > * GRUB -- GRand Unified Bootloader > * Copyright (C) 2008 Free Software Foundation, Inc. Add 2011 here. > GRUB_MOD_LICENSE ("GPLv3+"); > > /* This simple GRUB implementation of EHCI driver: > * - assumes 32 bits architecture, no IRQ How exactly do you use this? > #define GRUB_EHCI_SPARAMS_PPC (1<<4) /* Power port control */ Please run indent on the file. > #define GRUB_EHCI_PORT_READ(e, port) \ > grub_ehci_oper_read32 ((e), GRUB_EHCI_PORT_STAT_CMD + (port)*4) > Why not make it an inline static function? > #define GRUB_EHCI_PORT_RESBITS(e, port, bits) \ > { grub_ehci_oper_write32 ((e), GRUB_EHCI_PORT_STAT_CMD + (port)*4, \ > GRUB_EHCI_PORT_READ((e), (port)) & GRUB_EHCI_PORT_WMASK & ~(bits)); \ > GRUB_EHCI_PORT_READ((e), (port)); } > ditto > #define GRUB_EHCI_PORT_SETBITS(e, port, bits) \ > { grub_ehci_oper_write32 ((e), GRUB_EHCI_PORT_STAT_CMD + (port)*4, \ > (GRUB_EHCI_PORT_READ((e), (port)) & GRUB_EHCI_PORT_WMASK) | (bits)); \ > GRUB_EHCI_PORT_READ((e), (port)); } > ditto > /* EHCI Queue Element Transfer Descriptor (qTD) */ > /* Align to 32-byte boundaries */ > struct grub_ehci_td > { > /* EHCI HW part */ > grub_uint32_t next_td; /* Pointer to next qTD */ > grub_uint32_t alt_next_td; /* Pointer to alternate next qTD */ > grub_uint32_t token; /* Toggle, Len, Interrupt, Page, Error, PID, Status */ > grub_uint32_t buffer_page[GRUB_EHCI_TD_BUF_PAGES]; /* Buffer pointer (+ > cur. offset in page 0 */ > /* 64-bits part */ > grub_uint32_t buffer_page_high[GRUB_EHCI_TD_BUF_PAGES]; > /* EHCI driver part */ > grub_ehci_td_t link_td; /* pointer to next free/chained TD */ > grub_uint32_t size; > grub_uint32_t pad[1]; /* padding to some multiple of 32 bytes */ > } __attribute__((packed)); > packed isn't necessary here > /* EHCI Queue Head */ > /* Align to 32-byte boundaries */ > /* QH allocation is made in the similar/same way as in OHCI driver, > * because unlninking QH from the Asynchronous list is not so > * trivial as on UHCI (at least it is time consuming) */ > struct grub_ehci_qh > { > /* EHCI HW part */ > grub_uint32_t qh_hptr; /* Horiz. pointer & Terminate */ > grub_uint32_t ep_char; /* EP characteristics */ > grub_uint32_t ep_cap; /* EP capabilities */ > grub_uint32_t td_current; /* current TD link pointer */ > struct grub_ehci_td td_overlay; /* TD overlay area = 64 bytes */ > /* EHCI driver part */ > grub_uint32_t pad[4]; /* padding to some multiple of 32 bytes */ > } __attribute__((packed)); Same here > /* EHCC registers access functions */ > static inline grub_uint32_t > grub_ehci_ehcc_read32 (struct grub_ehci *e, grub_uint32_t addr) > { > return grub_le_to_cpu32 ( > *((grub_uint32_t *)((grub_uint32_t)e->iobase_ehcc + addr))); > } Convert to char * in order to do arithmetics, not grub_uint32_t. Also you need to use volatile attribute in last conversion. Same for following functions > /* Halt if EHCI HC not halted */ > static grub_err_t > grub_ehci_halt (struct grub_ehci *e) > { > grub_uint64_t maxtime; > > if ((grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS) > & GRUB_EHCI_ST_HC_HALTED) == 0 ) /* EHCI is not halted */ > { > /* Halt EHCI */ > grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, > ~GRUB_EHCI_CMD_RUNSTOP > & grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); > /* Ensure command is written */ > grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND); > maxtime = grub_get_time_ms () + 1000; /* Fix: Should be 2ms ! */ > while (((grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS) > & GRUB_EHCI_ST_HC_HALTED) == 0) > && (grub_get_time_ms () < maxtime)); > if ((grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS) > & GRUB_EHCI_ST_HC_HALTED) == 0 ) > return GRUB_ERR_TIMEOUT; > } > > return GRUB_ERR_NONE; > } > > /* EHCI HC reset */ > static grub_err_t > grub_ehci_reset (struct grub_ehci *e) > { > grub_uint64_t maxtime; > > grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, > GRUB_EHCI_CMD_HC_RESET > | grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); > /* Ensure command is written */ > grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND); > /* XXX: How long time could take reset of HC ? */ > maxtime = grub_get_time_ms () + 1000; > while (((grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND) > & GRUB_EHCI_CMD_HC_RESET) != 0) > && (grub_get_time_ms () < maxtime)); > if ((grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND) > & GRUB_EHCI_CMD_HC_RESET) != 0 ) > return GRUB_ERR_TIMEOUT; > > return GRUB_ERR_NONE; > } > > /* PCI iteration function... */ > static int NESTED_FUNC_ATTR > grub_ehci_pci_iter (grub_pci_device_t dev, > grub_pci_id_t pciid __attribute__((unused))) > { > grub_pci_address_t addr; > grub_uint8_t release; > grub_uint32_t class_code; > grub_uint32_t interf; > grub_uint32_t subclass; > grub_uint32_t class; > grub_uint32_t base, base_h; > struct grub_ehci *e; > grub_uint32_t eecp_offset; > grub_uint32_t fp; > int i; > grub_uint32_t usblegsup = 0; > grub_uint64_t maxtime; > grub_uint32_t n_ports; > > grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: begin\n"); > > 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 EHCI controller, just return. */ > if (class != 0x0c || subclass != 0x03 || interf != 0x20) > return 0; > > grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: class OK\n"); > > /* Check Serial Bus Release Number */ > addr = grub_pci_make_address (dev, GRUB_EHCI_PCI_SBRN_REG); > release = grub_pci_read_byte (addr); > if (release != 0x20) > { > grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: Wrong SBRN: %0x\n", > release); > return 0; > } > > grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: bus rev. num. OK\n"); > > /* Determine EHCI EHCC 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 not 32bit address type - this driver does not currently > * work with 64bit - maybe later... */ No need to specifically exclude those. Just zero-pad address. > /* Determine base address of EHCI operational registers */ > e->iobase = (grub_uint32_t *)((grub_uint32_t)e->iobase_ehcc + > (grub_uint32_t) grub_ehci_ehcc_read8 (e, > GRUB_EHCI_EHCC_CAPLEN)); > e->iobase should have volatile attribute > /* Reserve a page for the frame list - it is accurate for max. > * possible size of framelist. But currently it is not used. */ > e->framelist = grub_memalign (4096, 4096); > if (! e->framelist) > goto fail; > /* XXX: The currently allowed EHCI pointers are only 32 bits, > * make sure this code works on on 64 bits architectures. */ That's why you have to use dmaalign32 > /* Determine and change ownership. */ > /* XXX: Really should we handle it ? > * Is BIOS code active when GRUB is loaded and can BIOS properly > * "assist" in change of EHCI ownership ? */ > if (e->pcibase_eecp) /* Ownership can be changed via EECP only */ > { > usblegsup = grub_pci_read (e->pcibase_eecp); > if (usblegsup & GRUB_EHCI_BIOS_OWNED) > { > grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: EHCI owned by: > BIOS\n"); > /* Ownership change - set OS_OWNED bit */ > /* XXX: Is PCI address for grub_pci_write_byte() correct ? */ > grub_pci_write_byte (e->pcibase_eecp + GRUB_EHCI_OS_OWNED_OFF, 1); Arithmetics with PCI address aren't guaranteed to be available or to behave in a sane way. > /* Wait for finish of ownership change, EHCI specification > * doesn't say how long it can take... */ > maxtime = grub_get_time_ms () + 1000; > while ((grub_pci_read (e->pcibase_eecp) & GRUB_EHCI_BIOS_OWNED) > && (grub_get_time_ms () < maxtime)); > if (grub_pci_read (e->pcibase_eecp) & GRUB_EHCI_BIOS_OWNED) > { > grub_error (GRUB_ERR_TIMEOUT, "EHCI grub_ehci_pci_iter:EHCI > change ownership timeout"); In this case you have to take the ownership the hard way. You clean GRUB_EHCI_BIOS_OWNED yourself and disable all SMM interrupts (next EECP field) -- Regards Vladimir 'φ-coder/phcoder' Serbinenko
signature.asc
Description: OpenPGP digital signature
_______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel