grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] EHCI driver - USB 2.0 support


From: Vladimir 'φ-coder/phcoder' Serbinenko
Subject: Re: [PATCH] EHCI driver - USB 2.0 support
Date: Sat, 25 Jun 2011 22:27:03 +0200
User-agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.17) Gecko/20110606 Iceowl/1.0b2 Icedove/3.1.10

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


Attachment: signature.asc
Description: OpenPGP digital signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]