From: "Vladimir 'φ-coder/phcoder' Serbinenko" <phcoder@gmail.com>
To: The development of GNU GRUB <grub-devel@gnu.org>
Subject: Re: [PATCH] EHCI driver - USB 2.0 support
Date: Sat, 25 Jun 2011 22:27:03 +0200 [thread overview]
Message-ID: <4E064497.50101@gmail.com> (raw)
In-Reply-To: <1309029199.2532.35.camel@pracovna>
[-- Attachment #1: Type: text/plain, Size: 9541 bytes --]
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 #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 294 bytes --]
prev parent reply other threads:[~2011-06-25 20:27 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-06-25 19:13 [PATCH] EHCI driver - USB 2.0 support Aleš Nesrsta
2011-06-25 19:51 ` Szymon Janc
2011-06-26 20:37 ` Aleš Nesrsta
2011-07-21 21:59 ` Vladimir 'φ-coder/phcoder' Serbinenko
2011-08-27 16:42 ` Aleš Nesrsta
2011-09-30 15:37 ` Vladimir 'φ-coder/phcoder' Serbinenko
2011-10-01 8:15 ` Aleš Nesrsta
2011-10-01 18:05 ` Vladimir 'φ-coder/phcoder' Serbinenko
2011-10-01 20:04 ` Vladimir 'φ-coder/phcoder' Serbinenko
2011-10-01 22:03 ` [PATCH] EHCI driver - USB 2.0 support - alignment Aleš Nesrsta
2011-10-01 19:01 ` [PATCH] EHCI driver - USB 2.0 support - addendum Aleš Nesrsta
2011-11-03 9:25 ` [PATCH] EHCI driver - USB 2.0 support Philipp Hahn
2011-11-04 20:56 ` Aleš Nesrsta
[not found] ` <4E40A417.6000309@163.com>
[not found] ` <1312926878.2943.128.camel@pracovna>
2011-08-19 2:58 ` [Resolved] Grub2 can not detect usb disk Cui Lei
2011-06-25 20:27 ` Vladimir 'φ-coder/phcoder' Serbinenko [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4E064497.50101@gmail.com \
--to=phcoder@gmail.com \
--cc=grub-devel@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.