From 8b1cf5e87f603a30dbc0e0d13c44c3628d67b2bf Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Mon, 31 May 2010 13:58:18 +0200 Subject: [PATCH 01/16] Dedicated DMA allocation functions. CS5536 OHCI support. --- bus/cs5536.c | 215 +++++++++++++++++++++++++++++++ bus/pci.c | 53 ++++++++ bus/usb/ohci.c | 175 +++++++++++++++++-------- bus/usb/uhci.c | 6 +- bus/usb/usbtrans.c | 91 ++++++++++--- conf/i386.rmk | 6 + conf/mips-yeeloong.rmk | 34 ++++- include/grub/cs5536.h | 190 +++++++++++++++++++++++++++ include/grub/i386/pci.h | 2 +- include/grub/mips/yeeloong/pci.h | 32 +++-- include/grub/pci.h | 26 ++++ include/grub/smbus.h | 70 ++++++++++ include/grub/usbtrans.h | 2 +- 13 files changed, 817 insertions(+), 85 deletions(-) create mode 100644 bus/cs5536.c create mode 100644 include/grub/cs5536.h create mode 100644 include/grub/smbus.h diff --git a/bus/cs5536.c b/bus/cs5536.c new file mode 100644 index 000000000..61b0646b4 --- /dev/null +++ b/bus/cs5536.c @@ -0,0 +1,215 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010 Free Software Foundation, Inc. + * + * 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 . + */ + +#include +#include +#include +#include +#include + +int +grub_cs5536_find (grub_pci_device_t *devp) +{ + int found = 0; + auto int NESTED_FUNC_ATTR hook (grub_pci_device_t dev, + grub_pci_id_t pciid); + + int NESTED_FUNC_ATTR hook (grub_pci_device_t dev, + grub_pci_id_t pciid) + { + if (pciid == GRUB_CS5536_PCIID) + { + *devp = dev; + found = 1; + return 1; + } + return 0; + } + + grub_pci_iterate (hook); + + return found; +} + +grub_uint64_t +grub_cs5536_read_msr (grub_pci_device_t dev, grub_uint32_t addr) +{ + grub_uint64_t ret = 0; + grub_pci_write (grub_pci_make_address (dev, GRUB_CS5536_MSR_MAILBOX_ADDR), + addr); + ret = (grub_uint64_t) + grub_pci_read (grub_pci_make_address (dev, GRUB_CS5536_MSR_MAILBOX_DATA0)); + ret |= (((grub_uint64_t) + grub_pci_read (grub_pci_make_address (dev, + GRUB_CS5536_MSR_MAILBOX_DATA1))) + << 32); + return ret; +} + +void +grub_cs5536_write_msr (grub_pci_device_t dev, grub_uint32_t addr, + grub_uint64_t val) +{ + grub_pci_write (grub_pci_make_address (dev, GRUB_CS5536_MSR_MAILBOX_ADDR), + addr); + grub_pci_write (grub_pci_make_address (dev, GRUB_CS5536_MSR_MAILBOX_DATA0), + val & 0xffffffff); + grub_pci_write (grub_pci_make_address (dev, GRUB_CS5536_MSR_MAILBOX_DATA1), + val >> 32); +} + +grub_err_t +grub_cs5536_smbus_wait (grub_port_t smbbase) +{ + grub_uint64_t start = grub_get_time_ms (); + while (1) + { + grub_uint8_t status; + status = grub_inb (smbbase + GRUB_CS5536_SMB_REG_STATUS); + if (status & GRUB_CS5536_SMB_REG_STATUS_SDAST) + return GRUB_ERR_NONE; + if (status & GRUB_CS5536_SMB_REG_STATUS_BER) + return grub_error (GRUB_ERR_IO, "SM bus error"); + if (status & GRUB_CS5536_SMB_REG_STATUS_NACK) + return grub_error (GRUB_ERR_IO, "NACK received"); + if (grub_get_time_ms () > start + 40) + return grub_error (GRUB_ERR_IO, "SM stalled"); + } + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_cs5536_read_spd_byte (grub_port_t smbbase, grub_uint8_t dev, + grub_uint8_t addr, grub_uint8_t *res) +{ + grub_err_t err; + + /* Send START. */ + grub_outb (grub_inb (smbbase + GRUB_CS5536_SMB_REG_CTRL1) + | GRUB_CS5536_SMB_REG_CTRL1_START, + smbbase + GRUB_CS5536_SMB_REG_CTRL1); + + /* Send device address. */ + err = grub_cs5536_smbus_wait (smbbase); + if (err) + return err; + grub_outb (dev << 1, smbbase + GRUB_CS5536_SMB_REG_DATA); + + /* Send ACK. */ + err = grub_cs5536_smbus_wait (smbbase); + if (err) + return err; + grub_outb (grub_inb (smbbase + GRUB_CS5536_SMB_REG_CTRL1) + | GRUB_CS5536_SMB_REG_CTRL1_ACK, + smbbase + GRUB_CS5536_SMB_REG_CTRL1); + + /* Send byte address. */ + grub_outb (addr, smbbase + GRUB_CS5536_SMB_REG_DATA); + + /* Send START. */ + err = grub_cs5536_smbus_wait (smbbase); + if (err) + return err; + grub_outb (grub_inb (smbbase + GRUB_CS5536_SMB_REG_CTRL1) + | GRUB_CS5536_SMB_REG_CTRL1_START, + smbbase + GRUB_CS5536_SMB_REG_CTRL1); + + /* Send device address. */ + err = grub_cs5536_smbus_wait (smbbase); + if (err) + return err; + grub_outb ((dev << 1) | 1, smbbase + GRUB_CS5536_SMB_REG_DATA); + + /* Send STOP. */ + err = grub_cs5536_smbus_wait (smbbase); + if (err) + return err; + grub_outb (grub_inb (smbbase + GRUB_CS5536_SMB_REG_CTRL1) + | GRUB_CS5536_SMB_REG_CTRL1_STOP, + smbbase + GRUB_CS5536_SMB_REG_CTRL1); + + err = grub_cs5536_smbus_wait (smbbase); + if (err) + return err; + *res = grub_inb (smbbase + GRUB_CS5536_SMB_REG_DATA); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_cs5536_init_smbus (grub_pci_device_t dev, grub_uint16_t divisor, + grub_port_t *smbbase) +{ + grub_uint64_t smbbar; + + smbbar = grub_cs5536_read_msr (dev, GRUB_CS5536_MSR_SMB_BAR); + + /* FIXME */ + if (!(smbbar & GRUB_CS5536_LBAR_ENABLE)) + return grub_error(GRUB_ERR_IO, "SMB controller not enabled\n"); + *smbbase = (smbbar & GRUB_CS5536_LBAR_ADDR_MASK) + GRUB_MACHINE_PCI_IO_BASE; + + if (divisor < 8) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid divisor"); + + /* Disable SMB. */ + grub_outb (0, *smbbase + GRUB_CS5536_SMB_REG_CTRL2); + + /* Disable interrupts. */ + grub_outb (0, *smbbase + GRUB_CS5536_SMB_REG_CTRL1); + + /* Set as master. */ + grub_outb (GRUB_CS5536_SMB_REG_ADDR_MASTER, + *smbbase + GRUB_CS5536_SMB_REG_ADDR); + + /* Launch. */ + grub_outb (((divisor >> 7) & 0xff), *smbbase + GRUB_CS5536_SMB_REG_CTRL3); + grub_outb (((divisor << 1) & 0xfe) | GRUB_CS5536_SMB_REG_CTRL2_ENABLE, + *smbbase + GRUB_CS5536_SMB_REG_CTRL2); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_cs5536_read_spd (grub_port_t smbbase, grub_uint8_t dev, + struct grub_smbus_spd *res) +{ + grub_err_t err; + grub_size_t size; + grub_uint8_t b; + grub_size_t ptr; + + err = grub_cs5536_read_spd_byte (smbbase, dev, 0, &b); + if (err) + return err; + if (b == 0) + return grub_error (GRUB_ERR_IO, "no SPD found"); + size = b; + + ((grub_uint8_t *) res)[0] = b; + for (ptr = 1; ptr < size; ptr++) + { + err = grub_cs5536_read_spd_byte (smbbase, dev, ptr, + &((grub_uint8_t *) res)[ptr]); + if (err) + return err; + } + return GRUB_ERR_NONE; +} + diff --git a/bus/pci.c b/bus/pci.c index a08e53446..3e2126962 100644 --- a/bus/pci.c +++ b/bus/pci.c @@ -19,6 +19,49 @@ #include #include +#include + +#if GRUB_TARGET_SIZEOF_VOID_P == 4 +struct grub_pci_dma_chunk * +grub_memalign_dma32 (grub_size_t align, grub_size_t size) +{ + return grub_memalign (align, size); +} + +void +grub_dma_free (struct grub_pci_dma_chunk *ch) +{ + grub_free (ch); +} +#endif + +#ifdef GRUB_MACHINE_MIPS_YEELOONG +volatile void * +grub_dma_get_virt (struct grub_pci_dma_chunk *ch) +{ + return (void *) ((((grub_uint32_t) ch) & 0x1fffffff) | 0xa0000000); +} + +grub_uint32_t +grub_dma_get_phys (struct grub_pci_dma_chunk *ch) +{ + return (((grub_uint32_t) ch) & 0x1fffffff) | 0x80000000; +} +#else + +volatile void * +grub_dma_get_virt (struct grub_pci_dma_chunk *ch) +{ + return (void *) ch; +} + +grub_uint32_t +grub_dma_get_phys (struct grub_pci_dma_chunk *ch) +{ + return (grub_uint32_t) ch; +} + +#endif grub_pci_address_t grub_pci_make_address (grub_pci_device_t dev, int reg) @@ -48,6 +91,16 @@ grub_pci_iterate (grub_pci_iteratefunc_t hook) if (id >> 16 == 0xFFFF) continue; +#ifdef GRUB_MACHINE_MIPS_YEELOONG + /* Skip ghosts. */ + if (id == GRUB_YEELOONG_OHCI_PCIID + && dev.function == GRUB_YEELOONG_OHCI_GHOST_FUNCTION) + continue; + if (id == GRUB_YEELOONG_EHCI_PCIID + && dev.function == GRUB_YEELOONG_EHCI_GHOST_FUNCTION) + continue; +#endif + if (hook (dev, id)) return; diff --git a/bus/usb/ohci.c b/bus/usb/ohci.c index 6d185bc7f..7294fe537 100644 --- a/bus/usb/ohci.c +++ b/bus/usb/ohci.c @@ -24,8 +24,9 @@ #include #include #include -#include +#include #include +#include struct grub_ohci_hcca { @@ -63,13 +64,15 @@ struct grub_ohci_td grub_uint32_t buffer_end; } __attribute__((packed)); -typedef struct grub_ohci_td *grub_ohci_td_t; -typedef struct grub_ohci_ed *grub_ohci_ed_t; +typedef volatile struct grub_ohci_td *grub_ohci_td_t; +typedef volatile struct grub_ohci_ed *grub_ohci_ed_t; struct grub_ohci { volatile grub_uint32_t *iobase; volatile struct grub_ohci_hcca *hcca; + grub_uint32_t hcca_addr; + struct grub_pci_dma_chunk *hcca_chunk; struct grub_ohci *next; }; @@ -91,10 +94,23 @@ typedef enum GRUB_OHCI_REG_BULKCURR, GRUB_OHCI_REG_DONEHEAD, GRUB_OHCI_REG_FRAME_INTERVAL, + GRUB_OHCI_REG_PERIODIC_START = 16, GRUB_OHCI_REG_RHUBA = 18, GRUB_OHCI_REG_RHUBPORT = 21 } grub_ohci_reg_t; +#define GRUB_OHCI_RHUB_PORT_POWER_MASK 0x300 +#define GRUB_OHCI_RHUB_PORT_ALL_POWERED 0x200 + +#define GRUB_OHCI_REG_FRAME_INTERVAL_FSMPS_MASK 0x8fff0000 +#define GRUB_OHCI_REG_FRAME_INTERVAL_FSMPS_SHIFT 16 +#define GRUB_OHCI_REG_FRAME_INTERVAL_FI_SHIFT 0 + +/* XXX: Is this choice of timings sane? */ +#define GRUB_OHCI_FSMPS 0x2778 +#define GRUB_OHCI_PERIODIC_START 0x257f +#define GRUB_OHCI_FRAME_INTERVAL 0x2edf + static grub_uint32_t grub_ohci_readreg32 (struct grub_ohci *o, grub_ohci_reg_t reg) { @@ -114,51 +130,81 @@ grub_ohci_writereg32 (struct grub_ohci *o, controller. If this is the case, initialize it. */ static int NESTED_FUNC_ATTR grub_ohci_pci_iter (grub_pci_device_t dev, - grub_pci_id_t pciid __attribute__((unused))) + grub_pci_id_t pciid) { - grub_uint32_t class_code; - grub_uint32_t class; - grub_uint32_t subclass; grub_uint32_t interf; grub_uint32_t base; grub_pci_address_t addr; struct grub_ohci *o; grub_uint32_t revision; - grub_uint32_t frame_interval; - - 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 OHCI controller, just return. */ - if (class != 0x0c || subclass != 0x03 || interf != 0x10) - return 0; + int cs5536; /* Determine IO base address. */ - addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0); - base = grub_pci_read (addr); + grub_dprintf ("ohci", "pciid = %x\n", pciid); + + if (pciid == GRUB_CS5536_PCIID) + { + grub_uint64_t basereg; + + cs5536 = 1; + basereg = grub_cs5536_read_msr (dev, GRUB_CS5536_MSR_USB_OHCI_BASE); + if (!(basereg & GRUB_CS5536_MSR_USB_BASE_MEMORY_ENABLE)) + { + /* Shouldn't happen. */ + grub_dprintf ("ohci", "No OHCI address is assigned\n"); + return 0; + } + base = (basereg & GRUB_CS5536_MSR_USB_BASE_ADDR_MASK); + basereg |= GRUB_CS5536_MSR_USB_BASE_BUS_MASTER; + basereg &= ~GRUB_CS5536_MSR_USB_BASE_PME_ENABLED; + basereg &= ~GRUB_CS5536_MSR_USB_BASE_PME_STATUS; + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_USB_OHCI_BASE, basereg); + } + else + { + grub_uint32_t class_code; + grub_uint32_t class; + grub_uint32_t subclass; + + 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 OHCI controller, just return. */ + if (class != 0x0c || subclass != 0x03 || interf != 0x10) + return 0; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0); + base = grub_pci_read (addr); #if 0 - /* Stop if there is no IO space base address defined. */ - if (! (base & 1)) - return 0; + /* Stop if there is no IO space base address defined. */ + if (! (base & 1)) + return 0; #endif + grub_dprintf ("ohci", "class=0x%02x 0x%02x interface 0x%02x\n", + class, subclass, interf); + } + /* Allocate memory for the controller and register it. */ o = grub_malloc (sizeof (*o)); if (! o) return 1; - o->iobase = (grub_uint32_t *) base; + o->iobase = grub_pci_device_map_range (dev, base, 0x100); + + grub_dprintf ("ohci", "base=%p\n", o->iobase); /* Reserve memory for the HCCA. */ - o->hcca = (struct grub_ohci_hcca *) grub_memalign (256, 256); - - grub_dprintf ("ohci", "class=0x%02x 0x%02x interface 0x%02x base=%p\n", - class, subclass, interf, o->iobase); + o->hcca_chunk = grub_memalign_dma32 (256, 256); + if (! o->hcca_chunk) + return 1; + o->hcca = grub_dma_get_virt (o->hcca_chunk); + o->hcca_addr = grub_dma_get_phys (o->hcca_chunk); /* Check if the OHCI revision is actually 1.0 as supported. */ revision = grub_ohci_readreg32 (o, GRUB_OHCI_REG_REVISION); @@ -166,19 +212,27 @@ grub_ohci_pci_iter (grub_pci_device_t dev, if ((revision & 0xFF) != 0x10) goto fail; - /* Backup the frame interval register. */ - frame_interval = grub_ohci_readreg32 (o, GRUB_OHCI_REG_FRAME_INTERVAL); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBA, + (grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA) + & ~GRUB_OHCI_RHUB_PORT_POWER_MASK) + | GRUB_OHCI_RHUB_PORT_ALL_POWERED); /* Suspend the OHCI by issuing a reset. */ grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1); /* XXX: Magic. */ grub_millisleep (1); grub_dprintf ("ohci", "OHCI reset\n"); - /* Restore the frame interval register. */ - grub_ohci_writereg32 (o, GRUB_OHCI_REG_FRAME_INTERVAL, frame_interval); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_FRAME_INTERVAL, + (GRUB_OHCI_FSMPS + << GRUB_OHCI_REG_FRAME_INTERVAL_FSMPS_SHIFT) + | (GRUB_OHCI_FRAME_INTERVAL + << GRUB_OHCI_REG_FRAME_INTERVAL_FI_SHIFT)); + + grub_ohci_writereg32 (o, GRUB_OHCI_REG_PERIODIC_START, + GRUB_OHCI_PERIODIC_START); /* Setup the HCCA. */ - grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, (grub_uint32_t) o->hcca); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, o->hcca_addr); grub_dprintf ("ohci", "OHCI HCCA\n"); /* Enable the OHCI. */ @@ -194,11 +248,13 @@ grub_ohci_pci_iter (grub_pci_device_t dev, return 0; fail: +#ifndef GRUB_MACHINE_MIPS_YEELOONG if (o) grub_free ((void *) o->hcca); +#endif grub_free (o); - return 1; + return 0; } @@ -229,7 +285,7 @@ grub_ohci_iterate (int (*hook) (grub_usb_controller_t dev)) static void grub_ohci_transaction (grub_ohci_td_t td, grub_transfer_type_t type, unsigned int toggle, - grub_size_t size, char *data) + grub_size_t size, grub_uint32_t data) { grub_uint32_t token; grub_uint32_t buffer; @@ -261,7 +317,7 @@ grub_ohci_transaction (grub_ohci_td_t td, token |= toggle << 24; token |= 1 << 25; - buffer = (grub_uint32_t) data; + buffer = data; buffer_end = buffer + size - 1; td->token = grub_cpu_to_le32 (token); @@ -276,7 +332,10 @@ grub_ohci_transfer (grub_usb_controller_t dev, { struct grub_ohci *o = (struct grub_ohci *) dev->data; grub_ohci_ed_t ed; + grub_uint32_t ed_addr; + struct grub_pci_dma_chunk *ed_chunk, *td_list_chunk; grub_ohci_td_t td_list; + grub_uint32_t td_list_addr; grub_uint32_t target; grub_uint32_t td_tail; grub_uint32_t td_head; @@ -286,18 +345,23 @@ grub_ohci_transfer (grub_usb_controller_t dev, int i; /* Allocate an Endpoint Descriptor. */ - ed = grub_memalign (16, sizeof (*ed)); - if (! ed) + ed_chunk = grub_memalign_dma32 (256, sizeof (*ed)); + if (! ed_chunk) return GRUB_USB_ERR_INTERNAL; + ed = grub_dma_get_virt (ed_chunk); + ed_addr = grub_dma_get_phys (ed_chunk); - td_list = grub_memalign (16, sizeof (*td_list) * (transfer->transcnt + 1)); - if (! td_list) + td_list_chunk = grub_memalign_dma32 (256, sizeof (*td_list) + * (transfer->transcnt + 1)); + if (! td_list_chunk) { - grub_free ((void *) ed); + grub_dma_free (ed_chunk); return GRUB_USB_ERR_INTERNAL; } + td_list = grub_dma_get_virt (td_list_chunk); + td_list_addr = grub_dma_get_phys (td_list_chunk); - grub_dprintf ("ohci", "alloc=%p\n", td_list); + grub_dprintf ("ohci", "alloc=%p/0x%x\n", td_list, td_list_addr); /* Setup all Transfer Descriptors. */ for (i = 0; i < transfer->transcnt; i++) @@ -307,7 +371,8 @@ grub_ohci_transfer (grub_usb_controller_t dev, grub_ohci_transaction (&td_list[i], tr->pid, tr->toggle, tr->size, tr->data); - td_list[i].next_td = grub_cpu_to_le32 (&td_list[i + 1]); + td_list[i].next_td = grub_cpu_to_le32 (td_list_addr + + (i + 1) * sizeof (td_list[0])); } /* Setup the Endpoint Descriptor. */ @@ -324,9 +389,9 @@ grub_ohci_transfer (grub_usb_controller_t dev, /* Set the maximum packet size. */ target |= transfer->max << 16; - td_head = (grub_uint32_t) td_list; + td_head = td_list_addr; - td_tail = (grub_uint32_t) &td_list[transfer->transcnt]; + td_tail = td_list_addr + transfer->transcnt * sizeof (*td_list); ed->target = grub_cpu_to_le32 (target); ed->td_head = grub_cpu_to_le32 (td_head); @@ -353,7 +418,7 @@ grub_ohci_transfer (grub_usb_controller_t dev, status &= ~(1 << 2); grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status); - grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, (grub_uint32_t) ed); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, ed_addr); /* Enable the Bulk list. */ control |= 1 << 5; @@ -380,10 +445,9 @@ grub_ohci_transfer (grub_usb_controller_t dev, status &= ~(1 << 1); grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status); - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, - (grub_uint32_t) ed); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, ed_addr); grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD+1, - (grub_uint32_t) ed); + ed_addr); /* Enable the Control list. */ control |= 1 << 4; @@ -424,9 +488,12 @@ grub_ohci_transfer (grub_usb_controller_t dev, { grub_uint8_t errcode; grub_ohci_td_t tderr; + grub_uint32_t td_err_addr; - tderr = (grub_ohci_td_t) grub_ohci_readreg32 (o, - GRUB_OHCI_REG_DONEHEAD); + td_err_addr = grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD); + + tderr = (grub_ohci_td_t) ((char *) td_list + + (td_err_addr - td_list_addr)); errcode = tderr->token >> 28; switch (errcode) @@ -519,8 +586,8 @@ grub_ohci_transfer (grub_usb_controller_t dev, grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status); /* XXX */ - grub_free (td_list); - grub_free (ed); + grub_dma_free (td_list_chunk); + grub_dma_free (ed_chunk); return err; } diff --git a/bus/usb/uhci.c b/bus/usb/uhci.c index 947f2367b..6e47c38ef 100644 --- a/bus/usb/uhci.c +++ b/bus/usb/uhci.c @@ -380,7 +380,7 @@ static grub_uhci_td_t grub_uhci_transaction (struct grub_uhci *u, unsigned int endp, grub_transfer_type_t type, unsigned int addr, unsigned int toggle, grub_size_t size, - char *data) + grub_uint32_t data) { grub_uhci_td_t td; static const unsigned int tf[] = { 0x69, 0xE1, 0x2D }; @@ -398,7 +398,7 @@ grub_uhci_transaction (struct grub_uhci *u, unsigned int endp, } grub_dprintf ("uhci", - "transaction: endp=%d, type=%d, addr=%d, toggle=%d, size=%d data=%p td=%p\n", + "transaction: endp=%d, type=%d, addr=%d, toggle=%d, size=%d data=0x%x td=%p\n", endp, type, addr, toggle, size, data, td); /* Don't point to any TD, just terminate. */ @@ -418,7 +418,7 @@ grub_uhci_transaction (struct grub_uhci *u, unsigned int endp, td->token = ((size << 21) | (toggle << 19) | (endp << 15) | (addr << 8) | tf[type]); - td->buffer = (grub_uint32_t) data; + td->buffer = data; return td; } diff --git a/bus/usb/usbtrans.c b/bus/usb/usbtrans.c index 09e7af83e..f6d3d30f9 100644 --- a/bus/usb/usbtrans.c +++ b/bus/usb/usbtrans.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -29,30 +30,59 @@ grub_usb_control_msg (grub_usb_device_t dev, grub_uint8_t request, grub_uint16_t value, grub_uint16_t index, - grub_size_t size, char *data) + grub_size_t size0, char *data_in) { int i; grub_usb_transfer_t transfer; int datablocks; - struct grub_usb_packet_setup setupdata; + volatile struct grub_usb_packet_setup *setupdata; + grub_uint32_t setupdata_addr; grub_usb_err_t err; unsigned int max; + struct grub_pci_dma_chunk *data_chunk, *setupdata_chunk; + volatile char *data; + grub_uint32_t data_addr; + grub_size_t size = size0; + + /* FIXME: avoid allocation any kind of buffer in a first place. */ + data_chunk = grub_memalign_dma32 (128, size ? : 16); + if (!data_chunk) + return GRUB_USB_ERR_INTERNAL; + data = grub_dma_get_virt (data_chunk); + data_addr = grub_dma_get_phys (data_chunk); + grub_memcpy ((char *) data, data_in, size); grub_dprintf ("usb", "control: reqtype=0x%02x req=0x%02x val=0x%02x idx=0x%02x size=%d\n", reqtype, request, value, index, size); /* Create a transfer. */ - transfer = grub_malloc (sizeof (struct grub_usb_transfer)); + transfer = grub_malloc (sizeof (*transfer)); if (! transfer) - return grub_errno; + { + grub_dma_free (data_chunk); + return grub_errno; + } + + setupdata_chunk = grub_memalign_dma32 (32, sizeof (*setupdata)); + if (! setupdata_chunk) + { + grub_free (transfer); + grub_dma_free (data_chunk); + return grub_errno; + } + + setupdata = grub_dma_get_virt (setupdata_chunk); + setupdata_addr = grub_dma_get_phys (setupdata_chunk); /* Determine the maximum packet size. */ - if (dev->initialized) + if (dev->initialized && dev->descdev.maxsize0) max = dev->descdev.maxsize0; else max = 64; + grub_dprintf ("usb", "transfer = %p, dev = %p\n", transfer, dev); + datablocks = (size + max - 1) / max; /* XXX: Discriminate between different types of control @@ -71,18 +101,20 @@ grub_usb_control_msg (grub_usb_device_t dev, if (! transfer->transactions) { grub_free (transfer); + grub_dma_free (setupdata_chunk); + grub_dma_free (data_chunk); return grub_errno; } /* Build a Setup packet. XXX: Endianness. */ - setupdata.reqtype = reqtype; - setupdata.request = request; - setupdata.value = value; - setupdata.index = index; - setupdata.length = size; - transfer->transactions[0].size = sizeof (setupdata); + setupdata->reqtype = reqtype; + setupdata->request = request; + setupdata->value = value; + setupdata->index = index; + setupdata->length = size; + transfer->transactions[0].size = sizeof (*setupdata); transfer->transactions[0].pid = GRUB_USB_TRANSFER_TYPE_SETUP; - transfer->transactions[0].data = (char *) &setupdata; + transfer->transactions[0].data = setupdata_addr; transfer->transactions[0].toggle = 0; /* Now the data... XXX: Is this the right way to transfer control @@ -99,13 +131,13 @@ grub_usb_control_msg (grub_usb_device_t dev, tr->pid = GRUB_USB_TRANSFER_TYPE_IN; else tr->pid = GRUB_USB_TRANSFER_TYPE_OUT; - tr->data = &data[i * max]; + tr->data = data_addr + i * max; size -= max; } /* End with an empty OUT transaction. */ transfer->transactions[datablocks + 1].size = 0; - transfer->transactions[datablocks + 1].data = NULL; + transfer->transactions[datablocks + 1].data = 0; if (reqtype & 128) transfer->transactions[datablocks + 1].pid = GRUB_USB_TRANSFER_TYPE_OUT; else @@ -117,13 +149,17 @@ grub_usb_control_msg (grub_usb_device_t dev, grub_free (transfer->transactions); grub_free (transfer); + grub_dma_free (data_chunk); + grub_dma_free (setupdata_chunk); + + grub_memcpy (data_in, (char *) data, size0); return err; } static grub_usb_err_t grub_usb_bulk_readwrite (grub_usb_device_t dev, - int endpoint, grub_size_t size, char *data, + int endpoint, grub_size_t size0, char *data_in, grub_transfer_type_t type) { int i; @@ -132,6 +168,19 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev, unsigned int max; grub_usb_err_t err; int toggle = dev->toggle[endpoint]; + volatile char *data; + grub_uint32_t data_addr; + struct grub_pci_dma_chunk *data_chunk; + grub_size_t size = size0; + + /* FIXME: avoid allocation any kind of buffer in a first place. */ + data_chunk = grub_memalign_dma32 (128, size); + if (!data_chunk) + return GRUB_USB_ERR_INTERNAL; + data = grub_dma_get_virt (data_chunk); + data_addr = grub_dma_get_phys (data_chunk); + if (type == GRUB_USB_TRANSFER_TYPE_OUT) + grub_memcpy ((char *) data, data_in, size); /* Use the maximum packet size given in the endpoint descriptor. */ if (dev->initialized) @@ -150,7 +199,10 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev, /* Create a transfer. */ transfer = grub_malloc (sizeof (struct grub_usb_transfer)); if (! transfer) - return grub_errno; + { + grub_dma_free (data_chunk); + return grub_errno; + } datablocks = ((size + max - 1) / max); transfer->transcnt = datablocks; @@ -167,6 +219,7 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev, if (! transfer->transactions) { grub_free (transfer); + grub_dma_free (data_chunk); return grub_errno; } @@ -181,7 +234,7 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev, tr->toggle = toggle; toggle = toggle ? 0 : 1; tr->pid = type; - tr->data = &data[i * max]; + tr->data = data_addr + i * max; size -= tr->size; } @@ -191,6 +244,10 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev, grub_free (transfer->transactions); grub_free (transfer); + grub_dma_free (data_chunk); + + if (type == GRUB_USB_TRANSFER_TYPE_IN) + grub_memcpy (data_in, (char *) data, size0); return err; } diff --git a/conf/i386.rmk b/conf/i386.rmk index 02ce39817..73e818f9e 100644 --- a/conf/i386.rmk +++ b/conf/i386.rmk @@ -56,6 +56,12 @@ pci_mod_SOURCES = bus/pci.c pci_mod_CFLAGS = $(COMMON_CFLAGS) pci_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For cs5536.mod +pkglib_MODULES += cs5536.mod +cs5536_mod_SOURCES = bus/cs5536.c +cs5536_mod_CFLAGS = $(COMMON_CFLAGS) +cs5536_mod_LDFLAGS = $(COMMON_LDFLAGS) + # For lspci.mod pkglib_MODULES += lspci.mod lspci_mod_SOURCES = commands/lspci.c diff --git a/conf/mips-yeeloong.rmk b/conf/mips-yeeloong.rmk index 5ce8ede9d..2468985c5 100644 --- a/conf/mips-yeeloong.rmk +++ b/conf/mips-yeeloong.rmk @@ -4,7 +4,8 @@ target_machine=yeeloong COMMON_CFLAGS += -march=mips3 COMMON_ASFLAGS += -march=mips3 -kernel_img_HEADERS += pci.h bitmap.h video.h gfxterm.h font.h bitmap_scale.h bufio.h +kernel_img_HEADERS += pci.h bitmap.h video.h gfxterm.h font.h \ + bitmap_scale.h bufio.h cs5536.h machine/pci.h include $(srcdir)/conf/mips.mk @@ -26,6 +27,7 @@ kernel_img_SOURCES = kern/$(target_cpu)/startup.S \ video/fb/fbfill.c video/fb/fbutil.c video/bitmap.c \ video/bitmap_scale.c video/sm712.c bus/pci.c bus/bonito.c \ term/gfxterm.c commands/extcmd.c lib/arg.c \ + bus/cs5536.c \ symlist.c kernel_img_CFLAGS = $(COMMON_CFLAGS) -DUSE_ASCII_FAILBACK kernel_img_ASFLAGS = $(COMMON_ASFLAGS) @@ -69,5 +71,35 @@ linux_mod_CFLAGS = $(COMMON_CFLAGS) linux_mod_ASFLAGS = $(COMMON_ASFLAGS) linux_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For usb.mod +pkglib_MODULES += usb.mod +usb_mod_SOURCES = bus/usb/usb.c bus/usb/usbtrans.c bus/usb/usbhub.c +usb_mod_CFLAGS = $(COMMON_CFLAGS) +usb_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For usbtest.mod +pkglib_MODULES += usbtest.mod +usbtest_mod_SOURCES = commands/usbtest.c +usbtest_mod_CFLAGS = $(COMMON_CFLAGS) +usbtest_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For ohci.mod +pkglib_MODULES += ohci.mod +ohci_mod_SOURCES = bus/usb/ohci.c +ohci_mod_CFLAGS = $(COMMON_CFLAGS) +ohci_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For usbms.mod +pkglib_MODULES += usbms.mod +usbms_mod_SOURCES = disk/usbms.c +usbms_mod_CFLAGS = $(COMMON_CFLAGS) +usbms_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For usb_keyboard.mod +pkglib_MODULES += usb_keyboard.mod +usb_keyboard_mod_SOURCES = term/usb_keyboard.c +usb_keyboard_mod_CFLAGS = $(COMMON_CFLAGS) +usb_keyboard_mod_LDFLAGS = $(COMMON_LDFLAGS) + sbin_SCRIPTS += grub-install grub_install_SOURCES = util/grub-install.in diff --git a/include/grub/cs5536.h b/include/grub/cs5536.h new file mode 100644 index 000000000..cd17e11fc --- /dev/null +++ b/include/grub/cs5536.h @@ -0,0 +1,190 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010 Free Software Foundation, Inc. + * + * 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 . + */ + +#ifndef GRUB_CS5536_HEADER +#define GRUB_CS5536_HEADER 1 + +#ifndef ASM_FILE +#include +#include +#include +#endif + +#define GRUB_CS5536_PCIID 0x208f1022 +#define GRUB_CS5536_MSR_MAILBOX_ADDR 0xf4 +#define GRUB_CS5536_MSR_MAILBOX_DATA0 0xf8 +#define GRUB_CS5536_MSR_MAILBOX_DATA1 0xfc +#define GRUB_CS5536_MSR_IRQ_MAP_BAR 0x80000008 +#define GRUB_CS5536_MSR_SMB_BAR 0x8000000b + +#define GRUB_CS5536_SMBUS_REGS_SIZE 8 +#define GRUB_CS5536_GPIO_REGS_SIZE 256 +#define GRUB_CS5536_MFGPT_REGS_SIZE 64 +#define GRUB_CS5536_IRQ_MAP_REGS_SIZE 32 +#define GRUB_CS5536_PM_REGS_SIZE 128 +#define GRUB_CS5536_ACPI_REGS_SIZE 32 + +#define GRUB_CS5536_USB_OPTION_REGS_SIZE 0x1c +#define GRUB_CS5536_USB_OPTION_REG_UOCMUX 1 +#define GRUB_CS5536_USB_OPTION_REG_UOCMUX_PMUX_MASK 0x03 +#define GRUB_CS5536_USB_OPTION_REG_UOCMUX_PMUX_HC 0x02 + +#define GRUB_CS5536_DESTINATION_GLIU 0 +#define GRUB_CS5536_DESTINATION_GLPCI_SB 1 +#define GRUB_CS5536_DESTINATION_USB 2 +#define GRUB_CS5536_DESTINATION_IDE 3 +#define GRUB_CS5536_DESTINATION_DD 4 +#define GRUB_CS5536_DESTINATION_ACC 5 +#define GRUB_CS5536_DESTINATION_GLCP 7 + +#define GRUB_CS5536_P2D_DEST_SHIFT 61 +#define GRUB_CS5536_P2D_LOG_ALIGN 12 +#define GRUB_CS5536_P2D_ALIGN (1 << GRUB_CS5536_P2D_LOG_ALIGN) +#define GRUB_CS5536_P2D_BASE_SHIFT 20 +#define GRUB_CS5536_P2D_MASK_SHIFT 0 + +#define GRUB_CS5536_MSR_GL_IOD_START 0x000100e0 +#define GRUB_CS5536_IOD_DEST_SHIFT 61 +#define GRUB_CS5536_IOD_BASE_SHIFT 20 +#define GRUB_CS5536_IOD_MASK_SHIFT 0 +#define GRUB_CS5536_IOD_ADDR_MASK 0xfffff + +#define GRUB_CS5536_MSR_GPIO_BAR 0x8000000c +#define GRUB_CS5536_MSR_MFGPT_BAR 0x8000000d +#define GRUB_CS5536_MSR_ACPI_BAR 0x8000000e +#define GRUB_CS5536_MSR_PM_BAR 0x8000000f +#define GRUB_CS5536_MSR_DIVIL_LEG_IO 0x80000014 +#define GRUB_CS5536_MSR_DIVIL_LEG_IO_RTC_ENABLE0 0x00000001 +#define GRUB_CS5536_MSR_DIVIL_LEG_IO_RTC_ENABLE1 0x00000002 +#define GRUB_CS5536_MSR_DIVIL_LEG_IO_MODE_X86 0x10000000 +#define GRUB_CS5536_MSR_DIVIL_LEG_IO_F_REMAP 0x04000000 +#define GRUB_CS5536_MSR_DIVIL_IRQ_MAPPER_PRIMARY_MASK 0x80000024 +#define GRUB_CS5536_MSR_DIVIL_IRQ_MAPPER_LPC_MASK 0x80000025 +#define GRUB_CS5536_DIVIL_LPC_INTERRUPTS 0x1002 +#define GRUB_CS5536_MSR_DIVIL_LPC_SERIAL_IRQ_CONTROL 0x8000004e +#define GRUB_CS5536_MSR_DIVIL_LPC_SERIAL_IRQ_CONTROL_ENABLE 0x80 + +#define GRUB_CS5536_MSR_USB_OHCI_BASE 0x40000008 +#define GRUB_CS5536_MSR_USB_EHCI_BASE 0x40000009 +#define GRUB_CS5536_MSR_USB_CONTROLLER_BASE 0x4000000a +#define GRUB_CS5536_MSR_USB_OPTION_CONTROLLER_BASE 0x4000000b +#define GRUB_CS5536_MSR_USB_BASE_ADDR_MASK 0x00ffffff00ULL +#define GRUB_CS5536_MSR_USB_BASE_BUS_MASTER 0x0400000000ULL +#define GRUB_CS5536_MSR_USB_BASE_MEMORY_ENABLE 0x0200000000ULL +#define GRUB_CS5536_MSR_USB_BASE_PME_ENABLED 0x0800000000ULL +#define GRUB_CS5536_MSR_USB_BASE_PME_STATUS 0x1000000000ULL +#define GRUB_CS5536_MSR_USB_EHCI_BASE_FLDJ_SHIFT 40 + +#define GRUB_CS5536_MSR_IDE_IO_BAR 0x60000008 +#define GRUB_CS5536_MSR_IDE_IO_BAR_UNITS 1 +#define GRUB_CS5536_MSR_IDE_IO_BAR_ADDR_MASK 0xfffffff0 +#define GRUB_CS5536_MSR_IDE_CFG 0x60000010 +#define GRUB_CS5536_MSR_IDE_CFG_CHANNEL_ENABLE 2 +#define GRUB_CS5536_MSR_IDE_TIMING 0x60000012 +#define GRUB_CS5536_MSR_IDE_TIMING_PIO0 0x98 +#define GRUB_CS5536_MSR_IDE_TIMING_DRIVE0_SHIFT 24 +#define GRUB_CS5536_MSR_IDE_TIMING_DRIVE1_SHIFT 16 +#define GRUB_CS5536_MSR_IDE_CAS_TIMING 0x60000013 +#define GRUB_CS5536_MSR_IDE_CAS_TIMING_CMD_PIO0 0x99 +#define GRUB_CS5536_MSR_IDE_CAS_TIMING_CMD_SHIFT 24 +#define GRUB_CS5536_MSR_IDE_CAS_TIMING_DRIVE0_SHIFT 6 +#define GRUB_CS5536_MSR_IDE_CAS_TIMING_DRIVE1_SHIFT 4 +#define GRUB_CS5536_MSR_IDE_CAS_TIMING_PIO0 2 + +#define GRUB_CS5536_MSR_GL_PCI_CTRL 0x00000010 +#define GRUB_CS5536_MSR_GL_PCI_CTRL_MEMORY_ENABLE 1 +#define GRUB_CS5536_MSR_GL_PCI_CTRL_IO_ENABLE 2 +#define GRUB_CS5536_MSR_GL_PCI_CTRL_LATENCY_SHIFT 35 +#define GRUB_CS5536_MSR_GL_PCI_CTRL_OUT_THR_SHIFT 60 +#define GRUB_CS5536_MSR_GL_PCI_CTRL_IN_THR_SHIFT 56 + +#define GRUB_CS5536_MSR_GL_REGIONS_START 0x00000020 +#define GRUB_CS5536_MSR_GL_REGIONS_NUM 16 +#define GRUB_CS5536_MSR_GL_REGION_ENABLE 1 +#define GRUB_CS5536_MSR_GL_REGION_IO 0x100000000ULL +#define GRUB_CS5536_MSR_GL_REGION_BASE_MASK 0xfffff000ULL +#define GRUB_CS5536_MSR_GL_REGION_IO_BASE_SHIFT 12 +#define GRUB_CS5536_MSR_GL_REGION_TOP_MASK 0xfffff00000000000ULL +#define GRUB_CS5536_MSR_GL_REGION_IO_TOP_SHIFT 44 + +#define GRUB_CS5536_MSR_GL_P2D_START 0x00010020 + +#define GRUB_CS5536_SMB_REG_DATA 0x0 +#define GRUB_CS5536_SMB_REG_STATUS 0x1 +#define GRUB_CS5536_SMB_REG_STATUS_SDAST (1 << 6) +#define GRUB_CS5536_SMB_REG_STATUS_BER (1 << 5) +#define GRUB_CS5536_SMB_REG_STATUS_NACK (1 << 4) +#define GRUB_CS5536_SMB_REG_CTRL1 0x3 +#define GRUB_CS5536_SMB_REG_CTRL1_START 0x01 +#define GRUB_CS5536_SMB_REG_CTRL1_STOP 0x02 +#define GRUB_CS5536_SMB_REG_CTRL1_ACK 0x10 +#define GRUB_CS5536_SMB_REG_ADDR 0x4 +#define GRUB_CS5536_SMB_REG_ADDR_MASTER 0x0 +#define GRUB_CS5536_SMB_REG_CTRL2 0x5 +#define GRUB_CS5536_SMB_REG_CTRL2_ENABLE 0x1 +#define GRUB_CS5536_SMB_REG_CTRL3 0x6 + +#ifdef ASM_FILE +#define GRUB_ULL(x) x +#else +#define GRUB_ULL(x) x ## ULL +#endif + +#define GRUB_CS5536_LBAR_ADDR_MASK GRUB_ULL (0x000000000000fff8) +#define GRUB_CS5536_LBAR_ENABLE GRUB_ULL (0x0000000100000000) +#define GRUB_CS5536_LBAR_MASK_MASK GRUB_ULL (0x0000f00000000000) +#define GRUB_CS5536_LBAR_TURN_ON (GRUB_CS5536_LBAR_ENABLE | GRUB_CS5536_LBAR_MASK_MASK) + +/* PMON-compatible LBARs. */ +#define GRUB_CS5536_LBAR_GPIO 0xb000 +#define GRUB_CS5536_LBAR_ACC 0xb200 +#define GRUB_CS5536_LBAR_PM 0xb280 +#define GRUB_CS5536_LBAR_MFGPT 0xb300 +#define GRUB_CS5536_LBAR_ACPI 0xb340 +#define GRUB_CS5536_LBAR_IRQ_MAP 0xb360 +#define GRUB_CS5536_LBAR_IDE 0xb380 +#define GRUB_CS5536_LBAR_SMBUS 0xb390 + +#define GRUB_GPIO_SMBUS_PINS ((1 << 14) | (1 << 15)) +#define GRUB_GPIO_REG_OUT_EN 0x4 +#define GRUB_GPIO_REG_OUT_AUX1 0x10 +#define GRUB_GPIO_REG_IN_EN 0x20 +#define GRUB_GPIO_REG_IN_AUX1 0x34 + +#ifndef ASM_FILE +int EXPORT_FUNC (grub_cs5536_find) (grub_pci_device_t *devp); + +grub_uint64_t EXPORT_FUNC (grub_cs5536_read_msr) (grub_pci_device_t dev, + grub_uint32_t addr); +void EXPORT_FUNC (grub_cs5536_write_msr) (grub_pci_device_t dev, + grub_uint32_t addr, + grub_uint64_t val); +grub_err_t grub_cs5536_read_spd_byte (grub_port_t smbbase, grub_uint8_t dev, + grub_uint8_t addr, grub_uint8_t *res); +grub_err_t EXPORT_FUNC (grub_cs5536_read_spd) (grub_port_t smbbase, + grub_uint8_t dev, + struct grub_smbus_spd *res); +grub_err_t grub_cs5536_smbus_wait (grub_port_t smbbase); +grub_err_t EXPORT_FUNC (grub_cs5536_init_smbus) (grub_pci_device_t dev, + grub_uint16_t divisor, + grub_port_t *smbbase); + +void grub_cs5536_init_geode (grub_pci_device_t dev); +#endif + +#endif diff --git a/include/grub/i386/pci.h b/include/grub/i386/pci.h index bab42adb6..795dec58b 100644 --- a/include/grub/i386/pci.h +++ b/include/grub/i386/pci.h @@ -80,7 +80,7 @@ grub_pci_device_map_range (grub_pci_device_t dev __attribute__ ((unused)), static inline void grub_pci_device_unmap_range (grub_pci_device_t dev __attribute__ ((unused)), - void *mem __attribute__ ((unused)), + volatile void *mem __attribute__ ((unused)), grub_size_t size __attribute__ ((unused))) { } diff --git a/include/grub/mips/yeeloong/pci.h b/include/grub/mips/yeeloong/pci.h index c7bd31d4f..199bac048 100644 --- a/include/grub/mips/yeeloong/pci.h +++ b/include/grub/mips/yeeloong/pci.h @@ -19,16 +19,30 @@ #ifndef GRUB_MACHINE_PCI_H #define GRUB_MACHINE_PCI_H 1 +#ifndef ASM_FILE #include #include +#endif + +#define GRUB_YEELOONG_OHCI_PCIID 0x00351033 +#define GRUB_YEELOONG_EHCI_PCIID 0x00e01033 +#define GRUB_YEELOONG_OHCI_GHOST_FUNCTION 4 +#define GRUB_YEELOONG_EHCI_GHOST_FUNCTION 5 #define GRUB_PCI_NUM_BUS 1 #define GRUB_PCI_NUM_DEVICES 16 -#define GRUB_MACHINE_PCI_IO_BASE 0xbfd00000 -#define GRUB_MACHINE_PCI_CONFSPACE 0xbfe80000 -#define GRUB_MACHINE_PCI_CONF_CTRL_REG (*(volatile grub_uint32_t *) 0xbfe00118) +#define GRUB_MACHINE_PCI_IO_BASE 0xbfd00000 +#define GRUB_MACHINE_PCI_CONFSPACE 0xbfe80000 +#define GRUB_MACHINE_PCI_CONTROLLER_HEADER 0xbfe00000 + +#define GRUB_MACHINE_PCI_CONF_CTRL_REG_ADDR 0xbfe00118 + +#ifndef ASM_FILE +#define GRUB_MACHINE_PCI_CONF_CTRL_REG (*(volatile grub_uint32_t *) \ + GRUB_MACHINE_PCI_CONF_CTRL_REG_ADDR) #define GRUB_MACHINE_PCI_IO_CTRL_REG (*(volatile grub_uint32_t *) 0xbfe00110) +#endif #define GRUB_MACHINE_PCI_WIN_MASK_SIZE 6 #define GRUB_MACHINE_PCI_WIN_MASK ((1 << GRUB_MACHINE_PCI_WIN_MASK_SIZE) - 1) @@ -46,6 +60,7 @@ #define GRUB_MACHINE_PCI_WIN2_ADDR 0xb4000000 #define GRUB_MACHINE_PCI_WIN3_ADDR 0xb8000000 +#ifndef ASM_FILE static inline grub_uint32_t grub_pci_read (grub_pci_address_t addr) { @@ -95,11 +110,12 @@ grub_pci_write_byte (grub_pci_address_t addr, grub_uint8_t data) } volatile void * -grub_pci_device_map_range (grub_pci_device_t dev __attribute__ ((unused)), - grub_addr_t base, grub_size_t size); +EXPORT_FUNC (grub_pci_device_map_range) (grub_pci_device_t dev, + grub_addr_t base, grub_size_t size); void -grub_pci_device_unmap_range (grub_pci_device_t dev __attribute__ ((unused)), - volatile void *mem, - grub_size_t size __attribute__ ((unused))); +EXPORT_FUNC (grub_pci_device_unmap_range) (grub_pci_device_t dev, + volatile void *mem, + grub_size_t size); +#endif #endif /* GRUB_MACHINE_PCI_H */ diff --git a/include/grub/pci.h b/include/grub/pci.h index 89bd1034a..e6d6488f0 100644 --- a/include/grub/pci.h +++ b/include/grub/pci.h @@ -19,8 +19,10 @@ #ifndef GRUB_PCI_H #define GRUB_PCI_H 1 +#ifndef ASM_FILE #include #include +#endif #define GRUB_PCI_ADDR_SPACE_MASK 0x01 #define GRUB_PCI_ADDR_SPACE_MEMORY 0x00 @@ -66,6 +68,20 @@ #define GRUB_PCI_REG_MIN_GNT 0x3e #define GRUB_PCI_REG_MAX_LAT 0x3f +#define GRUB_PCI_COMMAND_IO_ENABLED 0x0001 +#define GRUB_PCI_COMMAND_MEM_ENABLED 0x0002 +#define GRUB_PCI_COMMAND_BUS_MASTER 0x0004 +#define GRUB_PCI_COMMAND_PARITY_ERROR 0x0040 +#define GRUB_PCI_COMMAND_SERR_ENABLE 0x0100 + +#define GRUB_PCI_STATUS_CAPABILITIES 0x0010 +#define GRUB_PCI_STATUS_66MHZ_CAPABLE 0x0020 +#define GRUB_PCI_STATUS_FAST_B2B_CAPABLE 0x0080 + +#define GRUB_PCI_STATUS_DEVSEL_TIMING_SHIFT 9 +#define GRUB_PCI_STATUS_DEVSEL_TIMING_MASK 0x0600 + +#ifndef ASM_FILE typedef grub_uint32_t grub_pci_id_t; #ifdef GRUB_MACHINE_EMU @@ -107,4 +123,14 @@ grub_pci_address_t EXPORT_FUNC(grub_pci_make_address) (grub_pci_device_t dev, void EXPORT_FUNC(grub_pci_iterate) (grub_pci_iteratefunc_t hook); +struct grub_pci_dma_chunk; + +struct grub_pci_dma_chunk *EXPORT_FUNC(grub_memalign_dma32) (grub_size_t align, + grub_size_t size); +void EXPORT_FUNC(grub_dma_free) (struct grub_pci_dma_chunk *ch); +volatile void *EXPORT_FUNC(grub_dma_get_virt) (struct grub_pci_dma_chunk *ch); +grub_uint32_t EXPORT_FUNC(grub_dma_get_phys) (struct grub_pci_dma_chunk *ch); + +#endif + #endif /* GRUB_PCI_H */ diff --git a/include/grub/smbus.h b/include/grub/smbus.h new file mode 100644 index 000000000..0b8e6718f --- /dev/null +++ b/include/grub/smbus.h @@ -0,0 +1,70 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010 Free Software Foundation, Inc. + * + * 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 . + */ + +#ifndef GRUB_SMBUS_HEADER +#define GRUB_SMBUS_HEADER 1 + +#define GRUB_SMB_RAM_START_ADDR 0x50 +#define GRUB_SMB_RAM_NUM_MAX 0x08 + +#define GRUB_SMBUS_SPD_MEMORY_TYPE_ADDR 2 +#define GRUB_SMBUS_SPD_MEMORY_TYPE_DDR2 8 +#define GRUB_SMBUS_SPD_MEMORY_NUM_BANKS_ADDR 17 +#define GRUB_SMBUS_SPD_MEMORY_NUM_ROWS_ADDR 3 +#define GRUB_SMBUS_SPD_MEMORY_NUM_COLUMNS_ADDR 4 +#define GRUB_SMBUS_SPD_MEMORY_NUM_OF_RANKS_ADDR 5 +#define GRUB_SMBUS_SPD_MEMORY_NUM_OF_RANKS_MASK 0x7 +#define GRUB_SMBUS_SPD_MEMORY_CAS_LATENCY_ADDR 18 +#define GRUB_SMBUS_SPD_MEMORY_CAS_LATENCY_MIN_VALUE 5 +#define GRUB_SMBUS_SPD_MEMORY_TRAS_ADDR 30 +#define GRUB_SMBUS_SPD_MEMORY_TRTP_ADDR 38 + +#ifndef ASM_FILE + +struct grub_smbus_spd +{ + grub_uint8_t written_size; + grub_uint8_t log_total_flash_size; + grub_uint8_t memory_type; + union + { + grub_uint8_t unknown[253]; + struct { + grub_uint8_t num_rows; + grub_uint8_t num_columns; + grub_uint8_t num_of_ranks; + grub_uint8_t unused1[12]; + grub_uint8_t num_of_banks; + grub_uint8_t unused2[2]; + grub_uint8_t cas_latency; + grub_uint8_t unused3[9]; + grub_uint8_t rank_capacity; + grub_uint8_t unused4[1]; + grub_uint8_t tras; + grub_uint8_t unused5[7]; + grub_uint8_t trtp; + grub_uint8_t unused6[31]; + grub_uint8_t part_number[18]; + grub_uint8_t unused7[165]; + } ddr2; + }; +}; + +#endif + +#endif diff --git a/include/grub/usbtrans.h b/include/grub/usbtrans.h index 7e4a9d7ee..8c739684e 100644 --- a/include/grub/usbtrans.h +++ b/include/grub/usbtrans.h @@ -37,7 +37,7 @@ struct grub_usb_transaction int size; int toggle; grub_transfer_type_t pid; - char *data; + grub_uint32_t data; }; typedef struct grub_usb_transaction *grub_usb_transaction_t; From 7fd08f56ae0d5f76e5f5c7a184414ae8e0e36a91 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Mon, 31 May 2010 14:01:38 +0200 Subject: [PATCH 02/16] Shut down OHCI on booting --- bus/usb/ohci.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/bus/usb/ohci.c b/bus/usb/ohci.c index 7294fe537..29ab5332a 100644 --- a/bus/usb/ohci.c +++ b/bus/usb/ohci.c @@ -654,6 +654,39 @@ grub_ohci_hubports (grub_usb_controller_t dev) return portinfo & 0xFF; } +static grub_err_t +grub_ohci_fini_hw (int noreturn __attribute__ ((unused))) +{ + struct grub_ohci *o; + + for (o = ohci; o; o = o->next) + { + int i, nports = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA) & 0xff; + for (i = 0; i < nports; i++) + grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + i, + GRUB_OHCI_CLEAR_PORT_ENABLE); + + grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_DONEHEAD, 0); + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_ohci_restore_hw (void) +{ + struct grub_ohci *o; + + for (o = ohci; o; o = o->next) + grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, o->hcca_addr); + + return GRUB_ERR_NONE; +} static struct grub_usb_controller_dev usb_controller = @@ -670,9 +703,12 @@ GRUB_MOD_INIT(ohci) { grub_ohci_inithw (); grub_usb_controller_dev_register (&usb_controller); + grub_loader_register_preboot_hook (grub_ohci_fini_hw, grub_ohci_restore_hw, + GRUB_LOADER_PREBOOT_HOOK_PRIO_DISK); } GRUB_MOD_FINI(ohci) { + grub_ohci_fini_hw (0); grub_usb_controller_dev_unregister (&usb_controller); } From 778ff32476c08a69d99e56fbfe9e7494e0c26bdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Nesrsta?= Date: Mon, 31 May 2010 14:11:41 +0200 Subject: [PATCH 03/16] Fix a bunch of problems with USB. --- bus/usb/ohci.c | 342 +++++++++++++++++++++++++++++++--------- bus/usb/uhci.c | 17 +- bus/usb/usb.c | 12 +- bus/usb/usbtrans.c | 5 +- commands/usbtest.c | 2 + disk/scsi.c | 183 +++++++++++++++++++-- disk/usbms.c | 166 ++++++++++++------- include/grub/scsicmd.h | 62 ++++++-- include/grub/usb.h | 9 +- include/grub/usbtrans.h | 6 +- 10 files changed, 637 insertions(+), 167 deletions(-) diff --git a/bus/usb/ohci.c b/bus/usb/ohci.c index 29ab5332a..03768d39e 100644 --- a/bus/usb/ohci.c +++ b/bus/usb/ohci.c @@ -27,6 +27,7 @@ #include #include #include +#include struct grub_ohci_hcca { @@ -96,7 +97,11 @@ typedef enum GRUB_OHCI_REG_FRAME_INTERVAL, GRUB_OHCI_REG_PERIODIC_START = 16, GRUB_OHCI_REG_RHUBA = 18, - GRUB_OHCI_REG_RHUBPORT = 21 + GRUB_OHCI_REG_RHUBPORT = 21, + GRUB_OHCI_REG_LEGACY_CONTROL = 0x100, + GRUB_OHCI_REG_LEGACY_INPUT = 0x104, + GRUB_OHCI_REG_LEGACY_OUTPUT = 0x108, + GRUB_OHCI_REG_LEGACY_STATUS = 0x10c } grub_ohci_reg_t; #define GRUB_OHCI_RHUB_PORT_POWER_MASK 0x300 @@ -195,7 +200,7 @@ grub_ohci_pci_iter (grub_pci_device_t dev, if (! o) return 1; - o->iobase = grub_pci_device_map_range (dev, base, 0x100); + o->iobase = grub_pci_device_map_range (dev, base, 0x800); grub_dprintf ("ohci", "base=%p\n", o->iobase); @@ -212,10 +217,48 @@ grub_ohci_pci_iter (grub_pci_device_t dev, if ((revision & 0xFF) != 0x10) goto fail; - grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBA, - (grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA) - & ~GRUB_OHCI_RHUB_PORT_POWER_MASK) - | GRUB_OHCI_RHUB_PORT_ALL_POWERED); + + { + grub_uint32_t control; + /* Check SMM/BIOS ownership of OHCI (SMM = USB Legacy Support driver for BIOS) */ + control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL); + if ((control & 0x100) != 0) + { + unsigned i; + grub_dprintf("ohci", "OHCI is owned by SMM\n"); + /* Do change of ownership */ + /* Ownership change request */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, (1<<3)); /* XXX: Magic. */ + /* Waiting for SMM deactivation */ + for (i=0; i < 10; i++) + { + if ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) & 0x100) == 0) + { + grub_dprintf("ohci", "Ownership changed normally.\n"); + break; + } + grub_millisleep (100); + } + if (i >= 10) + { + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, + grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) & ~0x100); + grub_dprintf("ohci", "Ownership changing timeout, change forced !\n"); + } + } + else if (((control & 0x100) == 0) && + ((control & 0xc0) != 0)) /* Not owned by SMM nor reset */ + { + grub_dprintf("ohci", "OHCI is owned by BIOS\n"); + /* Do change of ownership - not implemented yet... */ + /* In fact we probably need to do nothing ...? */ + } + else + { + grub_dprintf("ohci", "OHCI is not owned by SMM nor BIOS\n"); + /* We can setup OHCI. */ + } + } /* Suspend the OHCI by issuing a reset. */ grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1); /* XXX: Magic. */ @@ -232,15 +275,58 @@ grub_ohci_pci_iter (grub_pci_device_t dev, GRUB_OHCI_PERIODIC_START); /* Setup the HCCA. */ + o->hcca->donehead = 0; grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, o->hcca_addr); grub_dprintf ("ohci", "OHCI HCCA\n"); + /* Misc. pre-sets. */ + o->hcca->donehead = 0; + grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0); + + /* Check OHCI Legacy Support */ + if ((revision & 0x100) != 0) + { + grub_dprintf ("ohci", "Legacy Support registers detected\n"); + grub_dprintf ("ohci", "Current state of legacy control reg.: 0x%04x\n", + grub_ohci_readreg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL)); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL, + (grub_ohci_readreg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL)) & ~1); + grub_dprintf ("ohci", "OHCI Legacy Support disabled.\n"); + } + /* Enable the OHCI. */ grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, (2 << 6)); grub_dprintf ("ohci", "OHCI enable: 0x%02x\n", (grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) >> 6) & 3); + /* Power on all ports */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBA, + (grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA) + & ~GRUB_OHCI_RHUB_PORT_POWER_MASK) + | GRUB_OHCI_RHUB_PORT_ALL_POWERED); + /* Wait for stable power (100ms) and stable attachment (100ms) */ + /* I.e. minimum wait time should be probably 200ms. */ + /* We assume that device is attached when ohci is loaded. */ + /* Some devices take long time to power-on or indicate attach. */ + /* Here is some experimental value which should probably mostly work. */ + /* Cameras with manual USB mode selection and maybe some other similar + * devices will not work in some cases - they are repowered during + * ownership change and then they are starting slowly and mostly they + * are wanting select proper mode again... + * The same situation can be on computers where BIOS not set-up OHCI + * to be at least powered USB bus (maybe it is Yeelong case...?) + * Possible workaround could be for example some prompt + * for user with confirmation of proper USB device connection. + * Another workaround - "rmmod usbms", "rmmod ohci", proper start + * and configuration of USB device and then "insmod ohci" + * and "insmod usbms". */ + grub_millisleep (500); + /* Link to ohci now that initialisation is successful. */ o->next = ohci; ohci = o; @@ -317,13 +403,29 @@ grub_ohci_transaction (grub_ohci_td_t td, token |= toggle << 24; token |= 1 << 25; + /* Set "Not accessed" error code */ + token |= 15 << 28; + buffer = data; buffer_end = buffer + size - 1; + /* Set correct buffer values in TD if zero transfer occurs */ + if (size) + { + buffer = (grub_uint32_t) data; + buffer_end = buffer + size - 1; + td->buffer = grub_cpu_to_le32 (buffer); + td->buffer_end = grub_cpu_to_le32 (buffer_end); + } + else + { + td->buffer = 0; + td->buffer_end = 0; + } + + /* Set the rest of TD */ td->token = grub_cpu_to_le32 (token); - td->buffer = grub_cpu_to_le32 (buffer); td->next_td = 0; - td->buffer_end = grub_cpu_to_le32 (buffer_end); } static grub_usb_err_t @@ -342,7 +444,9 @@ grub_ohci_transfer (grub_usb_controller_t dev, grub_uint32_t status; grub_uint32_t control; grub_usb_err_t err; - int i; + int i, j; + grub_uint64_t maxtime; + int err_timeout = 0; /* Allocate an Endpoint Descriptor. */ ed_chunk = grub_memalign_dma32 (256, sizeof (*ed)); @@ -375,13 +479,25 @@ grub_ohci_transfer (grub_usb_controller_t dev, + (i + 1) * sizeof (td_list[0])); } + /* The last-1 TD token we should change to enable interrupt when TD finishes. + * As OHCI interrupts are disabled, it does only setting of WDH bit in + * HcInterruptStatus register - and that is what we want to safely detect + * normal end of all transactions. */ + td_list[transfer->transcnt - 1].token &= ~(7 << 21); + + td_list[transfer->transcnt].token = 0; + td_list[transfer->transcnt].buffer = 0; + td_list[transfer->transcnt].buffer_end = 0; + td_list[transfer->transcnt].next_td = + (grub_uint32_t) &td_list[transfer->transcnt]; + /* Setup the Endpoint Descriptor. */ /* Set the device address. */ target = transfer->devaddr; - /* Set the endpoint. */ - target |= transfer->endpoint << 7; + /* Set the endpoint. It should be masked, we need 4 bits only. */ + target |= (transfer->endpoint & 15) << 7; /* Set the device speed. */ target |= (transfer->dev->speed == GRUB_USB_SPEED_LOW) << 13; @@ -400,6 +516,30 @@ grub_ohci_transfer (grub_usb_controller_t dev, grub_dprintf ("ohci", "program OHCI\n"); + /* Disable the Control and Bulk lists. */ + control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL); + control &= ~(3 << 4); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control); + + /* Clear BulkListFilled and ControlListFilled. */ + status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS); + status &= ~(3 << 1); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status); + + /* Now we should wait for start of next frame. Because we are not using + * interrupt, we reset SF bit and wait when it goes to 1. */ + /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2)); + /* Wait for new SOF */ + while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0); + /* Now it should be safe to change CONTROL and BULK lists. */ + + /* This we do for safety's sake - it should be done in previous call + * of grub_ohci_transfer and nobody should change it in meantime... + * It should be done before start of control or bulk OHCI list. */ + o->hcca->donehead = 0; + grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */ + /* Program the OHCI to actually transfer. */ switch (transfer->type) { @@ -407,24 +547,17 @@ grub_ohci_transfer (grub_usb_controller_t dev, { grub_dprintf ("ohci", "add to bulk list\n"); - status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS); - control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL); - - /* Disable the Control and Bulk lists. */ - control &= ~(3 << 4); - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control); - - /* Clear BulkListFilled. */ - status &= ~(1 << 2); - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status); - + /* Set BulkList Head and Current */ grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, ed_addr); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0); /* Enable the Bulk list. */ + control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL); control |= 1 << 5; grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control); /* Set BulkListFilled. */ + status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS); status |= 1 << 2; grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status); @@ -433,21 +566,9 @@ grub_ohci_transfer (grub_usb_controller_t dev, case GRUB_USB_TRANSACTION_TYPE_CONTROL: { - grub_dprintf ("ohci", "add to control list\n"); - status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS); - control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL); - - /* Disable the Control and Bulk lists. */ - control &= ~(3 << 4); - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control); - - /* Clear ControlListFilled. */ - status &= ~(1 << 1); - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status); - + /* Set ControlList Head and Current */ grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, ed_addr); - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD+1, - ed_addr); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0); /* Enable the Control list. */ control |= 1 << 4; @@ -465,36 +586,77 @@ grub_ohci_transfer (grub_usb_controller_t dev, grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL), grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS)); + /* Safety measure to avoid a hang. */ + maxtime = grub_get_time_ms () + 1000; + /* Wait until the transfer is completed or STALLs. */ - while ((ed->td_head & ~0xf) != (ed->td_tail & ~0xf)) + do { grub_cpu_idle (); - grub_dprintf ("ohci", "head=0x%02x tail=0x%02x\n", ed->td_head, ed->td_tail); + /* Detected a HALT. */ + if (grub_le_to_cpu32 (ed->td_head) & 1) + break; + + if ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x2) != 0) + { + if ((grub_le_to_cpu32 (o->hcca->donehead) & ~0xf) + == td_list_addr + (transfer->transcnt - 1) * sizeof (td_list[0])) + break; - /* Detected a STALL. */ - if (ed->td_head & 1) + /* Done Head can be updated on some another place if ED is halted. */ + if (grub_le_to_cpu32 (ed->td_head) & 1) + break; + + /* If there is not HALT in ED, it is not correct, so debug it, reset + * donehead and WDH and continue waiting. */ + grub_dprintf ("ohci", "Incorrect HccaDoneHead=0x%08x\n", + o->hcca->donehead); + o->hcca->donehead = 0; + grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); + continue; + } + /* Timeout ? */ + if (grub_get_time_ms () > maxtime) + { + /* Disable the Control and Bulk lists. */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, + grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) & ~(3 << 4)); + err_timeout = 1; + break; + } + + if ((ed->td_head & ~0xf) == (ed->td_tail & ~0xf)) break; } + while (1); - grub_dprintf ("ohci", "complete\n"); - -/* if (ed->td_head & 1) */ -/* err = GRUB_USB_ERR_STALL; */ -/* else if (ed->td */ - - - if (ed->td_head & 1) + if (err_timeout) + { + err = GRUB_ERR_TIMEOUT; + grub_dprintf("ohci", "Timeout, target=%08x, head=%08x\n\t\ttail=%08x, next=%08x\n", + grub_le_to_cpu32(ed->target), + grub_le_to_cpu32(ed->td_head), + grub_le_to_cpu32(ed->td_tail), + grub_le_to_cpu32(ed->next_ed)); + } + else if (grub_le_to_cpu32 (ed->td_head) & 1) { - grub_uint8_t errcode; - grub_ohci_td_t tderr; grub_uint32_t td_err_addr; + grub_uint8_t errcode; + grub_ohci_td_t tderr = NULL; - td_err_addr = grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD); + td_err_addr = (grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD) & ~0xf); + if (td_err_addr == 0) + /* If DONEHEAD==0 it means that correct address is in HCCA. + * It should be always now! */ + td_err_addr = (grub_le_to_cpu32 (o->hcca->donehead) & ~0xf); tderr = (grub_ohci_td_t) ((char *) td_list + (td_err_addr - td_list_addr)); - errcode = tderr->token >> 28; + + errcode = grub_le_to_cpu32 (tderr->token) >> 28; + grub_dprintf ("ohci", "OHCI errcode=0x%02x\n", errcode); switch (errcode) { @@ -540,11 +702,17 @@ grub_ohci_transfer (grub_usb_controller_t dev, case 8: /* XXX: Data overrun error. */ err = GRUB_USB_ERR_DATA; + j = ((grub_uint32_t)tderr - (grub_uint32_t)td_list) / sizeof (*td_list); + grub_dprintf ("ohci", "Overrun, failed TD address: %p, index: %d\n", tderr, j); break; case 9: /* XXX: Data underrun error. */ err = GRUB_USB_ERR_DATA; + grub_dprintf ("ohci", "Underrun, number of not transferred bytes: %d\n", + 1 + grub_le_to_cpu32 (tderr->buffer_end) - grub_le_to_cpu32 (tderr->buffer)); + j = ((grub_uint32_t)tderr - (grub_uint32_t)td_list) / sizeof (*td_list); + grub_dprintf ("ohci", "Underrun, failed TD address: %p, index: %d\n", tderr, j); break; case 10: @@ -582,43 +750,73 @@ grub_ohci_transfer (grub_usb_controller_t dev, /* Clear BulkListFilled and ControlListFilled. */ status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS); - status &= ~((1 << 2) | (1 << 3)); + status &= ~(3 << 1); grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status); - - /* XXX */ + + /* Set ED to be skipped - for safety */ + ed->target |= grub_cpu_to_le32 (1 << 14); + + /* Now we should wait for start of next frame. + * It is necessary because we will invalidate pointer to ED and it + * can be on OHCI active till SOF! + * Because we are not using interrupt, we reset SF bit and wait when + * it goes to 1. */ + /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2)); + /* Wait for new SOF */ + while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0); + /* Now it should be safe to change CONTROL and BULK lists. */ + + /* Important cleaning. */ + o->hcca->donehead = 0; + grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0); + + grub_dprintf ("ohci", "OHCI finished, freeing, err=0x%02x\n", err); + grub_dma_free (td_list_chunk); grub_dma_free (ed_chunk); return err; } +#define GRUB_OHCI_SET_PORT_ENABLE (1 << 1) +#define GRUB_OHCI_CLEAR_PORT_ENABLE (1 << 0) +#define GRUB_OHCI_SET_PORT_RESET (1 << 4) +#define GRUB_OHCI_SET_PORT_RESET_STATUS_CHANGE (1 << 20) + static grub_err_t grub_ohci_portstatus (grub_usb_controller_t dev, unsigned int port, unsigned int enable) { struct grub_ohci *o = (struct grub_ohci *) dev->data; - grub_uint32_t status; - /* Reset the port. */ - status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port); - status |= (1 << 4); /* XXX: Magic. */ - grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status); - grub_millisleep (100); + grub_dprintf ("ohci", "begin of portstatus=0x%02x\n", + grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port)); - /* End the reset signaling. */ - status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port); - status |= (1 << 20); /* XXX: Magic. */ - grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, + GRUB_OHCI_SET_PORT_RESET); + grub_millisleep (50); /* For root hub should be nominaly 50ms */ + + /* End the reset signaling. */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, + GRUB_OHCI_SET_PORT_RESET_STATUS_CHANGE); grub_millisleep (10); - /* Enable the port. */ - status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port); - status |= (enable << 1); /* XXX: Magic. */ - grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status); - - status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port); - grub_dprintf ("ohci", "portstatus=0x%02x\n", status); + if (enable) + grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, + GRUB_OHCI_SET_PORT_ENABLE); + else + grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, + GRUB_OHCI_CLEAR_PORT_ENABLE); + grub_millisleep (10); + grub_dprintf ("ohci", "end of portstatus=0x%02x\n", + grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port)); + return GRUB_ERR_NONE; } diff --git a/bus/usb/uhci.c b/bus/usb/uhci.c index 6e47c38ef..eb533c926 100644 --- a/bus/usb/uhci.c +++ b/bus/usb/uhci.c @@ -174,14 +174,15 @@ grub_uhci_pci_iter (grub_pci_device_t dev, return 1; u->iobase = base & GRUB_UHCI_IOMASK; - grub_dprintf ("uhci", "class=0x%02x 0x%02x interface 0x%02x base=0x%x\n", - class, subclass, interf, u->iobase); /* Reserve a page for the frame list. */ u->framelist = grub_memalign (4096, 4096); if (! u->framelist) goto fail; + grub_dprintf ("uhci", "class=0x%02x 0x%02x interface 0x%02x base=0x%x framelist=%p\n", + class, subclass, interf, u->iobase, u->framelist); + /* The framelist pointer of UHCI is only 32 bits, make sure this code works on on 64 bits architectures. */ #if GRUB_CPU_SIZEOF_VOID_P == 8 @@ -221,6 +222,9 @@ grub_uhci_pci_iter (grub_pci_device_t dev, } #endif + grub_dprintf ("uhci", "QH=%p, TD=%p\n", + u->qh, u->td); + /* Link all Transfer Descriptors in a list of available Transfer Descriptors. */ for (i = 0; i < 256; i++) @@ -441,6 +445,8 @@ grub_uhci_transfer (grub_usb_controller_t dev, if (! qh) return grub_errno; + grub_dprintf ("uhci", "transfer, iobase:%08x\n", u->iobase); + for (i = 0; i < transfer->transcnt; i++) { grub_usb_transaction_t tr = &transfer->transactions[i]; @@ -548,7 +554,8 @@ grub_uhci_transfer (grub_usb_controller_t dev, fail: - grub_dprintf ("uhci", "transaction failed\n"); + if (err != GRUB_USB_ERR_NONE) + grub_dprintf ("uhci", "transaction failed\n"); /* Place the QH back in the free list and deallocate the associated TDs. */ @@ -583,6 +590,8 @@ grub_uhci_portstatus (grub_usb_controller_t dev, unsigned int status; grub_uint64_t endtime; + grub_dprintf ("uhci", "portstatus, iobase:%08x\n", u->iobase); + grub_dprintf ("uhci", "enable=%d port=%d\n", enable, port); if (port == 0) @@ -631,6 +640,8 @@ grub_uhci_detect_dev (grub_usb_controller_t dev, int port) int reg; unsigned int status; + grub_dprintf ("uhci", "detect_dev, iobase:%08x\n", u->iobase); + if (port == 0) reg = GRUB_UHCI_REG_PORTSC1; else if (port == 1) diff --git a/bus/usb/usb.c b/bus/usb/usb.c index 8289185da..7df148333 100644 --- a/bus/usb/usb.c +++ b/bus/usb/usb.c @@ -107,7 +107,7 @@ grub_usb_set_configuration (grub_usb_device_t dev, int configuration) { int i; - for (i = 0; i < 16; i++) + for (i = 0; i < 256; i++) dev->toggle[i] = 0; return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT @@ -163,6 +163,16 @@ grub_usb_device_initialize (grub_usb_device_t dev) grub_usb_err_t err; int i; + /* First we have to read first 8 bytes only and determine + * max. size of packet */ + dev->descdev.maxsize0 = 0; /* invalidating, for safety only, can be removed if it is sure it is zero here */ + err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_DEVICE, + 0, 8, (char *) &dev->descdev); + if (err) + return err; + + /* Now we have valid value in dev->descdev.maxsize0, + * so we can read whole device descriptor */ err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_DEVICE, 0, sizeof (struct grub_usb_desc_device), (char *) &dev->descdev); diff --git a/bus/usb/usbtrans.c b/bus/usb/usbtrans.c index f6d3d30f9..0bf5ee6de 100644 --- a/bus/usb/usbtrans.c +++ b/bus/usb/usbtrans.c @@ -138,7 +138,7 @@ grub_usb_control_msg (grub_usb_device_t dev, /* End with an empty OUT transaction. */ transfer->transactions[datablocks + 1].size = 0; transfer->transactions[datablocks + 1].data = 0; - if (reqtype & 128) + if ((reqtype & 128) && datablocks) transfer->transactions[datablocks + 1].pid = GRUB_USB_TRANSFER_TYPE_OUT; else transfer->transactions[datablocks + 1].pid = GRUB_USB_TRANSFER_TYPE_IN; @@ -148,6 +148,7 @@ grub_usb_control_msg (grub_usb_device_t dev, err = dev->controller.dev->transfer (&dev->controller, transfer); grub_free (transfer->transactions); + grub_free (transfer); grub_dma_free (data_chunk); grub_dma_free (setupdata_chunk); @@ -207,7 +208,7 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev, datablocks = ((size + max - 1) / max); transfer->transcnt = datablocks; transfer->size = size - 1; - transfer->endpoint = endpoint; + transfer->endpoint = endpoint & 15; transfer->devaddr = dev->addr; transfer->type = GRUB_USB_TRANSACTION_TYPE_BULK; transfer->max = max; diff --git a/commands/usbtest.c b/commands/usbtest.c index 191c4e4df..7d1f85cfd 100644 --- a/commands/usbtest.c +++ b/commands/usbtest.c @@ -148,6 +148,8 @@ usb_iterate (grub_usb_device_t dev) grub_printf ("%s speed device\n", usb_devspeed[dev->speed]); + return 0; + for (i = 0; i < descdev->configcnt; i++) { struct grub_usb_desc_config *config; diff --git a/disk/scsi.c b/disk/scsi.c index eba237287..e8bfb6a3f 100644 --- a/disk/scsi.c +++ b/disk/scsi.c @@ -25,6 +25,7 @@ #include #include #include +#include static grub_scsi_dev_t grub_scsi_dev_list; @@ -50,7 +51,62 @@ grub_scsi_dev_unregister (grub_scsi_dev_t dev) } -/* Determine the the device is removable and the type of the device +/* Check result of previous operation. */ +static grub_err_t +grub_scsi_request_sense (grub_scsi_t scsi) +{ + struct grub_scsi_request_sense rs; + struct grub_scsi_request_sense_data rsd; + grub_err_t err; + + rs.opcode = grub_scsi_cmd_request_sense; + rs.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + rs.reserved1 = 0; + rs.reserved2 = 0; + rs.alloc_length = 0x12; /* XXX: Hardcoded for now */ + rs.control = 0; + grub_memset (rs.pad, 0, sizeof(rs.pad)); + + err = scsi->dev->read (scsi, sizeof (rs), (char *) &rs, + sizeof (rsd), (char *) &rsd); + if (err) + return err; + + return GRUB_ERR_NONE; +} +/* Self commenting... */ +static grub_err_t +grub_scsi_test_unit_ready (grub_scsi_t scsi) +{ + struct grub_scsi_test_unit_ready tur; + grub_err_t err; + grub_err_t err_sense; + + tur.opcode = grub_scsi_cmd_test_unit_ready; + tur.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + tur.reserved1 = 0; + tur.reserved2 = 0; + tur.reserved3 = 0; + tur.control = 0; + grub_memset (tur.pad, 0, sizeof(tur.pad)); + + err = scsi->dev->read (scsi, sizeof (tur), (char *) &tur, + 0, NULL); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; + /* err_sense is ignored for now and Request Sense Data also... */ + + if (err) + return err; + + return GRUB_ERR_NONE; +} + +/* Determine if the device is removable and the type of the device SCSI. */ static grub_err_t grub_scsi_inquiry (grub_scsi_t scsi) @@ -58,15 +114,26 @@ grub_scsi_inquiry (grub_scsi_t scsi) struct grub_scsi_inquiry iq; struct grub_scsi_inquiry_data iqd; grub_err_t err; + grub_err_t err_sense; iq.opcode = grub_scsi_cmd_inquiry; iq.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + iq.page = 0; iq.reserved = 0; iq.alloc_length = 0x24; /* XXX: Hardcoded for now */ - iq.reserved2 = 0; + iq.control = 0; + grub_memset (iq.pad, 0, sizeof(iq.pad)); err = scsi->dev->read (scsi, sizeof (iq), (char *) &iq, sizeof (iqd), (char *) &iqd); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; + /* err_sense is ignored for now and Request Sense Data also... */ + if (err) return err; @@ -83,13 +150,27 @@ grub_scsi_read_capacity (grub_scsi_t scsi) struct grub_scsi_read_capacity rc; struct grub_scsi_read_capacity_data rcd; grub_err_t err; + grub_err_t err_sense; rc.opcode = grub_scsi_cmd_read_capacity; rc.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; - grub_memset (rc.reserved, 0, sizeof (rc.reserved)); - + rc.logical_block_addr = 0; + rc.reserved1 = 0; + rc.reserved2 = 0; + rc.PMI = 0; + rc.control = 0; + rc.pad = 0; + err = scsi->dev->read (scsi, sizeof (rc), (char *) &rc, sizeof (rcd), (char *) &rcd); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; +/* err_sense is ignored for now and Request Sense Data also... */ + if (err) return err; @@ -107,6 +188,8 @@ grub_scsi_read10 (grub_disk_t disk, grub_disk_addr_t sector, { grub_scsi_t scsi; struct grub_scsi_read10 rd; + grub_err_t err; + grub_err_t err_sense; scsi = disk->data; @@ -118,7 +201,16 @@ grub_scsi_read10 (grub_disk_t disk, grub_disk_addr_t sector, rd.reserved2 = 0; rd.pad = 0; - return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf); + err = scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; + /* err_sense is ignored for now and Request Sense Data also... */ + + return err; } /* Send a SCSI request for DISK: read SIZE sectors starting with @@ -129,6 +221,8 @@ grub_scsi_read12 (grub_disk_t disk, grub_disk_addr_t sector, { grub_scsi_t scsi; struct grub_scsi_read12 rd; + grub_err_t err; + grub_err_t err_sense; scsi = disk->data; @@ -139,7 +233,16 @@ grub_scsi_read12 (grub_disk_t disk, grub_disk_addr_t sector, rd.reserved = 0; rd.control = 0; - return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf); + err = scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; + /* err_sense is ignored for now and Request Sense Data also... */ + + return err; } #if 0 @@ -151,6 +254,8 @@ grub_scsi_write10 (grub_disk_t disk, grub_disk_addr_t sector, { grub_scsi_t scsi; struct grub_scsi_write10 wr; + grub_err_t err; + grub_err_t err_sense; scsi = disk->data; @@ -162,7 +267,16 @@ grub_scsi_write10 (grub_disk_t disk, grub_disk_addr_t sector, wr.reserved2 = 0; wr.pad = 0; - return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf); + err = scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; + /* err_sense is ignored for now and Request Sense Data also... */ + + return err; } /* Send a SCSI request for DISK: write the data stored in BUF to SIZE @@ -172,7 +286,9 @@ grub_scsi_write12 (grub_disk_t disk, grub_disk_addr_t sector, grub_size_t size, char *buf) { grub_scsi_t scsi; - struct grub_scsi_write10 wr; + struct grub_scsi_write12 wr; + grub_err_t err; + grub_err_t err_sense; scsi = disk->data; @@ -181,9 +297,18 @@ grub_scsi_write12 (grub_disk_t disk, grub_disk_addr_t sector, wr.lba = grub_cpu_to_be32 (sector); wr.size = grub_cpu_to_be32 (size); wr.reserved = 0; - wr.pad = 0; + wr.control = 0; - return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf); + err = scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; + /* err_sense is ignored for now and Request Sense Data also... */ + + return err; } #endif @@ -235,6 +360,7 @@ grub_scsi_open (const char *name, grub_disk_t disk) grub_err_t err; int len; int lun; + grub_uint64_t maxtime; scsi = grub_malloc (sizeof (*scsi)); if (! scsi) @@ -292,6 +418,31 @@ grub_scsi_open (const char *name, grub_disk_t disk) else disk->has_partitions = 1; + + /* According to USB MS tests specification, issue Test Unit Ready + * until OK */ + maxtime = grub_get_time_ms () + 1000; + do + { + /* Timeout is necessary - for example in case when we have + * universal card reader with more LUNs and we have only + * one card inserted (or none), so only one LUN (or none) + * will be ready - and we want not to hang... */ + if (grub_get_time_ms () > maxtime) + { + err = GRUB_ERR_READ_ERROR; + grub_free (scsi); + grub_dprintf ("scsi", "LUN is not ready - timeout\n"); + return err; + } + err = grub_scsi_test_unit_ready (scsi); + } + while (err == GRUB_ERR_READ_ERROR); + /* Reset grub_errno ! + * It is set to some error code in loop before... */ + grub_errno = GRUB_ERR_NONE; + + /* Read capacity of media */ err = grub_scsi_read_capacity (scsi); if (err) { @@ -302,12 +453,14 @@ grub_scsi_open (const char *name, grub_disk_t disk) /* SCSI blocks can be something else than 512, although GRUB wants 512 byte blocks. */ - disk->total_sectors = ((scsi->size * scsi->blocksize) - << GRUB_DISK_SECTOR_BITS); + disk->total_sectors = ((grub_uint64_t)scsi->size + * (grub_uint64_t)scsi->blocksize) + >> GRUB_DISK_SECTOR_BITS; - grub_dprintf ("scsi", "capacity=%llu, blksize=%d\n", - (unsigned long long) disk->total_sectors, - scsi->blocksize); + grub_dprintf ("scsi", "blocks=%u, blocksize=%u\n", + scsi->size, scsi->blocksize); + grub_dprintf ("scsi", "Disk total 512 sectors = %llu\n", + disk->total_sectors); return GRUB_ERR_NONE; } diff --git a/disk/usbms.c b/disk/usbms.c index 8554b224f..7b719fb84 100644 --- a/disk/usbms.c +++ b/disk/usbms.c @@ -84,7 +84,8 @@ grub_usbms_finddevs (void) struct grub_usb_desc_device *descdev = &usbdev->descdev; int i; - if (descdev->class != 0 || descdev->subclass || descdev->protocol != 0) + if (descdev->class != 0 || descdev->subclass || descdev->protocol != 0 + || descdev->configcnt == 0) return 0; /* XXX: Just check configuration 0 for now. */ @@ -93,19 +94,31 @@ grub_usbms_finddevs (void) struct grub_usbms_dev *usbms; struct grub_usb_desc_if *interf; int j; - grub_uint8_t luns; + grub_uint8_t luns = 0; + + grub_dprintf ("usbms", "alive\n"); interf = usbdev->config[0].interf[i].descif; /* If this is not a USB Mass Storage device with a supported protocol, just skip it. */ + grub_dprintf ("usbms", "iterate: interf=%d, class=%d, subclass=%d, protocol=%d\n", + i, interf->class, interf->subclass, interf->protocol); + if (interf->class != GRUB_USB_CLASS_MASS_STORAGE - || interf->subclass != GRUB_USBMS_SUBCLASS_BULK + || ( interf->subclass != GRUB_USBMS_SUBCLASS_BULK && + /* Experimental support of RBC, MMC-2, UFI, SFF-8070i devices */ + interf->subclass != GRUB_USBMS_SUBCLASS_RBC && + interf->subclass != GRUB_USBMS_SUBCLASS_MMC2 && + interf->subclass != GRUB_USBMS_SUBCLASS_UFI && + interf->subclass != GRUB_USBMS_SUBCLASS_SFF8070 ) || interf->protocol != GRUB_USBMS_PROTOCOL_BULK) { continue; } + grub_dprintf ("usbms", "alive\n"); + devcnt++; usbms = grub_zalloc (sizeof (struct grub_usbms_dev)); if (! usbms) @@ -114,6 +127,8 @@ grub_usbms_finddevs (void) usbms->dev = usbdev; usbms->interface = i; + grub_dprintf ("usbms", "alive\n"); + /* Iterate over all endpoints of this interface, at least a IN and OUT bulk endpoint are required. */ for (j = 0; j < interf->endpointcnt; j++) @@ -125,14 +140,16 @@ grub_usbms_finddevs (void) { /* Bulk IN endpoint. */ usbms->in = endp; - grub_usb_clear_halt (usbdev, endp->endp_addr & 128); + /* Clear Halt is not possible yet! */ + /* grub_usb_clear_halt (usbdev, endp->endp_addr); */ usbms->in_maxsz = endp->maxpacket; } else if (!(endp->endp_addr & 128) && (endp->attrib & 3) == 2) { /* Bulk OUT endpoint. */ usbms->out = endp; - grub_usb_clear_halt (usbdev, endp->endp_addr & 128); + /* Clear Halt is not possible yet! */ + /* grub_usb_clear_halt (usbdev, endp->endp_addr); */ usbms->out_maxsz = endp->maxpacket; } } @@ -143,51 +160,63 @@ grub_usbms_finddevs (void) return 0; } + grub_dprintf ("usbms", "alive\n"); + + /* XXX: Activate the first configuration. */ + grub_usb_set_configuration (usbdev, 1); + /* Query the amount of LUNs. */ err = grub_usb_control_msg (usbdev, 0xA1, 254, 0, i, 1, (char *) &luns); + if (err) { /* In case of a stall, clear the stall. */ if (err == GRUB_USB_ERR_STALL) { - grub_usb_clear_halt (usbdev, usbms->in->endp_addr & 3); - grub_usb_clear_halt (usbdev, usbms->out->endp_addr & 3); + grub_usb_clear_halt (usbdev, usbms->in->endp_addr); + grub_usb_clear_halt (usbdev, usbms->out->endp_addr); } - /* Just set the amount of LUNs to one. */ grub_errno = GRUB_ERR_NONE; usbms->luns = 1; } else - usbms->luns = luns; + /* luns = 0 means one LUN with ID 0 present ! */ + /* We get from device not number of LUNs but highest + * LUN number. LUNs are numbered from 0, + * i.e. number of LUNs is luns+1 ! */ + usbms->luns = luns + 1; - /* XXX: Check the magic values, does this really make - sense? */ - grub_usb_control_msg (usbdev, (1 << 6) | 1, 255, - 0, i, 0, 0); - - /* XXX: To make Qemu work? */ - if (usbms->luns == 0) - usbms->luns = 1; + grub_dprintf ("usbms", "alive\n"); usbms->next = grub_usbms_dev_list; grub_usbms_dev_list = usbms; - /* XXX: Activate the first configuration. */ - grub_usb_set_configuration (usbdev, 1); - +#if 0 /* All this part should be probably deleted. + * This make trouble on some devices if they are not in + * Phase Error state - and there they should be not in such state... + * Bulk only mass storage reset procedure should be used only + * on place and in time when it is really necessary. */ + /* Reset recovery procedure */ /* Bulk-Only Mass Storage Reset, after the reset commands will be accepted. */ grub_usbms_reset (usbdev, i); + grub_usb_clear_halt (usbdev, usbms->in->endp_addr); + grub_usb_clear_halt (usbdev, usbms->out->endp_addr); +#endif return 0; } + grub_dprintf ("usbms", "alive\n"); return 0; } + grub_dprintf ("usbms", "alive\n"); grub_usb_iterate (usb_iterate); + grub_dprintf ("usbms", "alive\n"); + } @@ -225,6 +254,7 @@ grub_usbms_transfer (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, static grub_uint32_t tag = 0; grub_usb_err_t err = GRUB_USB_ERR_NONE; int retrycnt = 3 + 1; + grub_size_t i; retry: retrycnt--; @@ -237,73 +267,89 @@ grub_usbms_transfer (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, cbw.tag = tag++; cbw.transfer_length = grub_cpu_to_le32 (size); cbw.flags = (!read_write) << GRUB_USBMS_DIRECTION_BIT; - cbw.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + cbw.lun = scsi->lun; /* In USB MS CBW are LUN bits on another place than in SCSI CDB, both should be set correctly. */ cbw.length = cmdsize; grub_memcpy (cbw.cbwcb, cmd, cmdsize); + + /* Debug print of CBW content. */ + grub_dprintf ("usb", "CBW: sign=0x%08x tag=0x%08x len=0x%08x\n", + cbw.signature, cbw.tag, cbw.transfer_length); + grub_dprintf ("usb", "CBW: flags=0x%02x lun=0x%02x CB_len=0x%02x\n", + cbw.flags, cbw.lun, cbw.length); + grub_dprintf ("usb", "CBW: cmd:\n %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + cbw.cbwcb[ 0], cbw.cbwcb[ 1], cbw.cbwcb[ 2], cbw.cbwcb[ 3], + cbw.cbwcb[ 4], cbw.cbwcb[ 5], cbw.cbwcb[ 6], cbw.cbwcb[ 7], + cbw.cbwcb[ 8], cbw.cbwcb[ 9], cbw.cbwcb[10], cbw.cbwcb[11], + cbw.cbwcb[12], cbw.cbwcb[13], cbw.cbwcb[14], cbw.cbwcb[15]); - /* Write the request. */ - err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr & 15, + /* Write the request. + * XXX: Error recovery is maybe still not fully correct. */ + err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr, sizeof (cbw), (char *) &cbw); if (err) { if (err == GRUB_USB_ERR_STALL) { + grub_usb_clear_halt (dev->dev, dev->in->endp_addr); grub_usb_clear_halt (dev->dev, dev->out->endp_addr); goto retry; } return grub_error (GRUB_ERR_IO, "USB Mass Storage request failed"); } - /* Read/write the data. */ - if (read_write == 0) + /* Read/write the data, (maybe) according to specification. */ + if (size && (read_write == 0)) { - err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr & 15, size, buf); - grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL); - if (err) - { - if (err == GRUB_USB_ERR_STALL) - { - grub_usb_clear_halt (dev->dev, dev->in->endp_addr); - goto retry; - } - return grub_error (GRUB_ERR_READ_ERROR, - "can't read from USB Mass Storage device"); - } + err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr, size, buf); + grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL); + if (err) goto CheckCSW; + /* Debug print of received data. */ + grub_dprintf ("usb", "buf:\n"); + if (size <= 64) + for (i=0; idev, dev->in->endp_addr & 15, size, buf); + err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr, size, buf); grub_dprintf ("usb", "write: %d %d\n", err, GRUB_USB_ERR_STALL); - if (err) - { - if (err == GRUB_USB_ERR_STALL) - { - grub_usb_clear_halt (dev->dev, dev->out->endp_addr); - goto retry; - } - return grub_error (GRUB_ERR_WRITE_ERROR, - "can't write to USB Mass Storage device"); - } + grub_dprintf ("usb", "buf:\n"); + /* Debug print of sent data. */ + if (size <= 256) + for (i=0; idev, dev->in->endp_addr & 15, - sizeof (status), (char *) &status); + /* Read the status - (maybe) according to specification. */ +CheckCSW: + err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr, + sizeof (status), (char *) &status); if (err) { - if (err == GRUB_USB_ERR_STALL) - { - grub_usb_clear_halt (dev->dev, dev->in->endp_addr); + grub_usb_clear_halt (dev->dev, dev->in->endp_addr); + err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr, + sizeof (status), (char *) &status); + if (err) + { /* Bulk-only reset device. */ + grub_usbms_reset (dev->dev, dev->interface); + grub_usb_clear_halt (dev->dev, dev->in->endp_addr); + grub_usb_clear_halt (dev->dev, dev->out->endp_addr); goto retry; - } - return grub_error (GRUB_ERR_READ_ERROR, - "can't read status from USB Mass Storage device"); + } } - /* XXX: Magic and check this code. */ + /* Debug print of CSW content. */ + grub_dprintf ("usb", "CSW: sign=0x%08x tag=0x%08x resid=0x%08x\n", + status.signature, status.tag, status.residue); + grub_dprintf ("usb", "CSW: status=0x%02x\n", status.status); + + /* If phase error, do bulk-only reset device. */ if (status.status == 2) { - /* XXX: Phase error, reset device. */ grub_usbms_reset (dev->dev, dev->interface); grub_usb_clear_halt (dev->dev, dev->in->endp_addr); grub_usb_clear_halt (dev->dev, dev->out->endp_addr); diff --git a/include/grub/scsicmd.h b/include/grub/scsicmd.h index 40f237a17..f9a7cb200 100644 --- a/include/grub/scsicmd.h +++ b/include/grub/scsicmd.h @@ -25,14 +25,26 @@ #define GRUB_SCSI_REMOVABLE_BIT 7 #define GRUB_SCSI_LUN_SHIFT 5 +struct grub_scsi_test_unit_ready +{ + grub_uint8_t opcode; + grub_uint8_t lun; /* 7-5 LUN, 4-0 reserved */ + grub_uint8_t reserved1; + grub_uint8_t reserved2; + grub_uint8_t reserved3; + grub_uint8_t control; + grub_uint8_t pad[6]; /* To be ATAPI compatible */ +} __attribute__((packed)); + struct grub_scsi_inquiry { grub_uint8_t opcode; - grub_uint8_t lun; - grub_uint16_t reserved; - grub_uint16_t alloc_length; - grub_uint8_t reserved2; - grub_uint8_t pad[5]; + grub_uint8_t lun; /* 7-5 LUN, 4-1 reserved, 0 EVPD */ + grub_uint8_t page; /* page code if EVPD=1 */ + grub_uint8_t reserved; + grub_uint8_t alloc_length; + grub_uint8_t control; + grub_uint8_t pad[6]; /* To be ATAPI compatible */ } __attribute__((packed)); struct grub_scsi_inquiry_data @@ -47,12 +59,42 @@ struct grub_scsi_inquiry_data char prodrev[4]; } __attribute__((packed)); +struct grub_scsi_request_sense +{ + grub_uint8_t opcode; + grub_uint8_t lun; /* 7-5 LUN, 4-0 reserved */ + grub_uint8_t reserved1; + grub_uint8_t reserved2; + grub_uint8_t alloc_length; + grub_uint8_t control; + grub_uint8_t pad[6]; /* To be ATAPI compatible */ +} __attribute__((packed)); + +struct grub_scsi_request_sense_data +{ + grub_uint8_t error_code; /* 7 Valid, 6-0 Err. code */ + grub_uint8_t segment_number; + grub_uint8_t sense_key; /*7 FileMark, 6 EndOfMedia, 5 ILI, 4-0 sense key */ + grub_uint32_t information; + grub_uint8_t additional_sense_length; + grub_uint32_t cmd_specific_info; + grub_uint8_t additional_sense_code; + grub_uint8_t additional_sense_code_qualifier; + grub_uint8_t field_replaceable_unit_code; + grub_uint8_t sense_key_specific[3]; + /* there can be additional sense field */ +} __attribute__((packed)); + struct grub_scsi_read_capacity { grub_uint8_t opcode; - grub_uint8_t lun; - grub_uint8_t reserved[8]; - grub_uint8_t pad[2]; + grub_uint8_t lun; /* 7-5 LUN, 4-1 reserved, 0 reserved */ + grub_uint32_t logical_block_addr; /* only if PMI=1 */ + grub_uint8_t reserved1; + grub_uint8_t reserved2; + grub_uint8_t PMI; + grub_uint8_t control; + grub_uint16_t pad; /* To be ATAPI compatible */ } __attribute__((packed)); struct grub_scsi_read_capacity_data @@ -106,11 +148,13 @@ struct grub_scsi_write12 typedef enum { grub_scsi_cmd_inquiry = 0x12, + grub_scsi_cmd_test_unit_ready = 0x00, grub_scsi_cmd_read_capacity = 0x25, grub_scsi_cmd_read10 = 0x28, grub_scsi_cmd_write10 = 0x2a, grub_scsi_cmd_read12 = 0xa8, - grub_scsi_cmd_write12 = 0xaa + grub_scsi_cmd_write12 = 0xaa, + grub_scsi_cmd_request_sense = 0x03 } grub_scsi_cmd_t; typedef enum diff --git a/include/grub/usb.h b/include/grub/usb.h index dc90e7879..cbbc39303 100644 --- a/include/grub/usb.h +++ b/include/grub/usb.h @@ -156,7 +156,7 @@ struct grub_usb_device int initialized; /* Data toggle values (used for bulk transfers only). */ - int toggle[16]; + int toggle[256]; /* Device-specific data. */ void *data; @@ -184,7 +184,12 @@ typedef enum typedef enum { - GRUB_USBMS_SUBCLASS_BULK = 0x06 + GRUB_USBMS_SUBCLASS_BULK = 0x06, + /* Experimental support for non-pure SCSI devices */ + GRUB_USBMS_SUBCLASS_RBC = 0x01, + GRUB_USBMS_SUBCLASS_MMC2 = 0x02, + GRUB_USBMS_SUBCLASS_UFI = 0x04, + GRUB_USBMS_SUBCLASS_SFF8070 = 0x05 } grub_usbms_subclass_t; typedef enum diff --git a/include/grub/usbtrans.h b/include/grub/usbtrans.h index 8c739684e..cd818e1fc 100644 --- a/include/grub/usbtrans.h +++ b/include/grub/usbtrans.h @@ -86,9 +86,9 @@ typedef struct grub_usb_transfer *grub_usb_transfer_t; #define GRUB_USB_REQ_HUB_GET_PORT_STATUS 0x00 -#define GRUB_USB_FEATURE_ENDP_HALT 0x01 -#define GRUB_USB_FEATURE_DEV_REMOTE_WU 0x02 -#define GRUB_USB_FEATURE_TEST_MODE 0x04 +#define GRUB_USB_FEATURE_ENDP_HALT 0x00 +#define GRUB_USB_FEATURE_DEV_REMOTE_WU 0x01 +#define GRUB_USB_FEATURE_TEST_MODE 0x02 #define GRUB_USB_HUB_STATUS_CONNECTED (1 << 0) #define GRUB_USB_HUB_STATUS_LOWSPEED (1 << 9) From 7d4873c26a80e0d05c0952aa45f4bda59e1e3f86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Nesrsta?= Date: Tue, 1 Jun 2010 02:10:19 +0200 Subject: [PATCH 04/16] =?UTF-8?q?Merge=20Ale=C5=A1'=20latest=20patch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bus/usb/ohci.c | 156 ++++++++++++++++++++++++++++++---------- bus/usb/uhci.c | 19 +++-- bus/usb/usbtrans.c | 8 +++ disk/scsi.c | 33 ++++++++- disk/usbms.c | 40 ++++++++--- include/grub/usb.h | 3 +- include/grub/usbtrans.h | 3 + term/usb_keyboard.c | 1 - 8 files changed, 206 insertions(+), 57 deletions(-) diff --git a/bus/usb/ohci.c b/bus/usb/ohci.c index 03768d39e..ac6f61ee8 100644 --- a/bus/usb/ohci.c +++ b/bus/usb/ohci.c @@ -116,6 +116,11 @@ typedef enum #define GRUB_OHCI_PERIODIC_START 0x257f #define GRUB_OHCI_FRAME_INTERVAL 0x2edf +#define GRUB_OHCI_SET_PORT_ENABLE (1 << 1) +#define GRUB_OHCI_CLEAR_PORT_ENABLE (1 << 0) +#define GRUB_OHCI_SET_PORT_RESET (1 << 4) +#define GRUB_OHCI_SET_PORT_RESET_STATUS_CHANGE (1 << 20) + static grub_uint32_t grub_ohci_readreg32 (struct grub_ohci *o, grub_ohci_reg_t reg) { @@ -281,7 +286,7 @@ grub_ohci_pci_iter (grub_pci_device_t dev, /* Misc. pre-sets. */ o->hcca->donehead = 0; - grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, 0x7f); /* Clears everything */ grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0); grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0); grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0); @@ -396,8 +401,10 @@ grub_ohci_transaction (grub_ohci_td_t td, break; } +#if 0 /* Always generate interrupt */ /* Generate no interrupts. */ token |= 7 << 21; +#endif /* Set the token. */ token |= toggle << 24; @@ -444,9 +451,12 @@ grub_ohci_transfer (grub_usb_controller_t dev, grub_uint32_t status; grub_uint32_t control; grub_usb_err_t err; - int i, j; + int i; grub_uint64_t maxtime; int err_timeout = 0; + int err_unrec = 0; + grub_uint32_t intstatus; + grub_uint32_t tderr_addr = 0; /* Allocate an Endpoint Descriptor. */ ed_chunk = grub_memalign_dma32 (256, sizeof (*ed)); @@ -479,11 +489,13 @@ grub_ohci_transfer (grub_usb_controller_t dev, + (i + 1) * sizeof (td_list[0])); } +#if 0 /* Better will be enable interrupt on all TDs. */ /* The last-1 TD token we should change to enable interrupt when TD finishes. * As OHCI interrupts are disabled, it does only setting of WDH bit in * HcInterruptStatus register - and that is what we want to safely detect * normal end of all transactions. */ td_list[transfer->transcnt - 1].token &= ~(7 << 21); +#endif td_list[transfer->transcnt].token = 0; td_list[transfer->transcnt].buffer = 0; @@ -582,9 +594,11 @@ grub_ohci_transfer (grub_usb_controller_t dev, } grub_dprintf ("ohci", "wait for completion\n"); - grub_dprintf ("ohci", "control=0x%02x status=0x%02x\n", + grub_dprintf ("ohci", + "begin: control=0x%02x status=0x%02x\n\t\t intstatus=0x%02x\n", grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL), - grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS)); + grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS), + grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS)); /* Safety measure to avoid a hang. */ maxtime = grub_get_time_ms () + 1000; @@ -592,30 +606,34 @@ grub_ohci_transfer (grub_usb_controller_t dev, /* Wait until the transfer is completed or STALLs. */ do { - grub_cpu_idle (); + /* Check transfer status */ + intstatus = grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS); + if ((intstatus & 0x2) != 0) + { + grub_dprintf ("ohci", "Current HccaDoneHead=0x%08x\n", + o->hcca->donehead); + /* Remember last successful TD */ + tderr_addr = grub_le_to_cpu32 (o->hcca->donehead) & ~0xf; + /* Reset DoneHead */ + o->hcca->donehead = 0; + grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); + /* if TD is last, finish */ + if (tderr_addr == td_list_addr + + sizeof (td_list[0]) * (transfer->transcnt - 1)) + break; + continue; + } + + if ((intstatus & 0x10) != 0) + { /* Unrecoverable error - only reset can help...! */ + err_unrec = 1; + break; + } /* Detected a HALT. */ if (grub_le_to_cpu32 (ed->td_head) & 1) break; - - if ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x2) != 0) - { - if ((grub_le_to_cpu32 (o->hcca->donehead) & ~0xf) - == td_list_addr + (transfer->transcnt - 1) * sizeof (td_list[0])) - break; - /* Done Head can be updated on some another place if ED is halted. */ - if (grub_le_to_cpu32 (ed->td_head) & 1) - break; - - /* If there is not HALT in ED, it is not correct, so debug it, reset - * donehead and WDH and continue waiting. */ - grub_dprintf ("ohci", "Incorrect HccaDoneHead=0x%08x\n", - o->hcca->donehead); - o->hcca->donehead = 0; - grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); - continue; - } /* Timeout ? */ if (grub_get_time_ms () > maxtime) { @@ -628,9 +646,45 @@ grub_ohci_transfer (grub_usb_controller_t dev, if ((ed->td_head & ~0xf) == (ed->td_tail & ~0xf)) break; + + grub_cpu_idle (); } while (1); + grub_dprintf ("ohci", "end: control=0x%02x status=0x%02x\n\t\t intstatus=0x%02x\n", + grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL), + grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS), + grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS)); + + if (!tderr_addr) + { + /* It means that something wrong happened, + * it could be: + * - timeout and no TD processed + * - some or unrecoverable error and no TD processed + * - something unexpected... :-( */ + /* Try look into DONEHEAD reg., but there should be also zero */ + grub_dprintf("ohci", "HCCA DoneHead is zero, something is bad!\n"); + tderr_addr = grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD) & ~0xf; + } + + /* Remember last processed transaction (TD) - it is necessary for + * proper setting of toggle bit in next transaction. */ + transfer->last_trans = (tderr_addr - td_list_addr) / sizeof (*td_list); + + /* Check correct value in last_trans */ + /* It could happen if timeout happens and no TD was retired */ + if (transfer->last_trans >= transfer->transcnt || !tderr_addr) + { + grub_dprintf("ohci", "tder==0 or out of TDs range!\n"); + grub_dprintf("ohci", "tderr_addr=0x%x, td_list=%p,\n\t\t last_trans=%d, transcnt=%d\n", + tderr_addr, td_list, transfer->last_trans, transfer->transcnt); + /* We should set something valid... */ + transfer->last_trans = -1; /* Probably no TD done */ + tderr_addr = td_list_addr; + } + + /* In case of timeout do not detect error from TD */ if (err_timeout) { err = GRUB_ERR_TIMEOUT; @@ -640,20 +694,25 @@ grub_ohci_transfer (grub_usb_controller_t dev, grub_le_to_cpu32(ed->td_tail), grub_le_to_cpu32(ed->next_ed)); } + /* In case of unrecoverable error do not detect error from TD */ + else if (err_unrec) + { + err = GRUB_USB_ERR_UNRECOVERABLE; + grub_dprintf("ohci", + "Unrecoverable error, target=%08x, head=%08x\n" + "\t\ttail=%08x, next=%08x\n", + grub_le_to_cpu32(ed->target), + grub_le_to_cpu32(ed->td_head), + grub_le_to_cpu32(ed->td_tail), + grub_le_to_cpu32(ed->next_ed)); + } else if (grub_le_to_cpu32 (ed->td_head) & 1) { - grub_uint32_t td_err_addr; grub_uint8_t errcode; grub_ohci_td_t tderr = NULL; - td_err_addr = (grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD) & ~0xf); - if (td_err_addr == 0) - /* If DONEHEAD==0 it means that correct address is in HCCA. - * It should be always now! */ - td_err_addr = (grub_le_to_cpu32 (o->hcca->donehead) & ~0xf); - tderr = (grub_ohci_td_t) ((char *) td_list - + (td_err_addr - td_list_addr)); + + (tderr_addr - td_list_addr)); errcode = grub_le_to_cpu32 (tderr->token) >> 28; grub_dprintf ("ohci", "OHCI errcode=0x%02x\n", errcode); @@ -702,17 +761,17 @@ grub_ohci_transfer (grub_usb_controller_t dev, case 8: /* XXX: Data overrun error. */ err = GRUB_USB_ERR_DATA; - j = ((grub_uint32_t)tderr - (grub_uint32_t)td_list) / sizeof (*td_list); - grub_dprintf ("ohci", "Overrun, failed TD address: %p, index: %d\n", tderr, j); + grub_dprintf ("ohci", "Overrun, failed TD address: %p, index: %d\n", + tderr, transfer->last_trans); break; case 9: /* XXX: Data underrun error. */ err = GRUB_USB_ERR_DATA; + grub_dprintf ("ohci", "Underrun, failed TD address: %p, index: %d\n", + tderr, transfer->last_trans); grub_dprintf ("ohci", "Underrun, number of not transferred bytes: %d\n", 1 + grub_le_to_cpu32 (tderr->buffer_end) - grub_le_to_cpu32 (tderr->buffer)); - j = ((grub_uint32_t)tderr - (grub_uint32_t)td_list) / sizeof (*td_list); - grub_dprintf ("ohci", "Underrun, failed TD address: %p, index: %d\n", tderr, j); break; case 10: @@ -764,7 +823,8 @@ grub_ohci_transfer (grub_usb_controller_t dev, /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */ grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2)); /* Wait for new SOF */ - while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0); + while (((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0) + && !err_unrec); /* Now it should be safe to change CONTROL and BULK lists. */ /* Important cleaning. */ @@ -774,7 +834,29 @@ grub_ohci_transfer (grub_usb_controller_t dev, grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0); grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0); grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0); - + + if (err_unrec) + { + /* Do OHCI reset in case of unrecoverable error - maybe we will need + * do more - re-enumerate bus etc. (?) */ + + /* Suspend the OHCI by issuing a reset. */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1); /* XXX: Magic. */ + grub_millisleep (1); + grub_dprintf ("ohci", "Unrecoverable error - OHCI reset\n"); + + /* Misc. resets. */ + o->hcca->donehead = 0; + grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, 0x7f); /* Clears everything */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0); + + /* Enable the OHCI. */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, (2 << 6)); + } + grub_dprintf ("ohci", "OHCI finished, freeing, err=0x%02x\n", err); grub_dma_free (td_list_chunk); diff --git a/bus/usb/uhci.c b/bus/usb/uhci.c index eb533c926..1510f98e8 100644 --- a/bus/usb/uhci.c +++ b/bus/usb/uhci.c @@ -332,13 +332,20 @@ grub_free_td (struct grub_uhci *u, grub_uhci_td_t td) } static void -grub_free_queue (struct grub_uhci *u, grub_uhci_td_t td) +grub_free_queue (struct grub_uhci *u, grub_uhci_td_t td, + grub_usb_transfer_t transfer) { - /* Free the TDs in this queue. */ - while (td) + int i; /* Index of TD in transfer */ + + /* Free the TDs in this queue and set last_trans. */ + for (i=0; td; i++) { grub_uhci_td_t tdprev; + /* Check state of TD and possibly set last_trans */ + if (transfer && (td->linkptr & 1)) + transfer->last_trans = i; + /* Unlink the queue. */ tdprev = td; td = (grub_uhci_td_t) td->linkptr2; @@ -461,7 +468,7 @@ grub_uhci_transfer (grub_usb_controller_t dev, td_prev->linkptr = 1; if (td_first) - grub_free_queue (u, td_first); + grub_free_queue (u, td_first, NULL); return GRUB_USB_ERR_INTERNAL; } @@ -560,7 +567,7 @@ grub_uhci_transfer (grub_usb_controller_t dev, /* Place the QH back in the free list and deallocate the associated TDs. */ qh->elinkptr = 1; - grub_free_queue (u, td_first); + grub_free_queue (u, td_first, transfer); return err; } @@ -609,7 +616,7 @@ grub_uhci_portstatus (grub_usb_controller_t dev, grub_uhci_writereg16 (u, reg, enable << 9); /* Wait for the reset to complete. XXX: How long exactly? */ - grub_millisleep (10); + grub_millisleep (50); /* For root hub should be nominaly 50ms */ status = grub_uhci_readreg16 (u, reg); grub_uhci_writereg16 (u, reg, status & ~(1 << 9)); grub_dprintf ("uhci", "reset completed\n"); diff --git a/bus/usb/usbtrans.c b/bus/usb/usbtrans.c index 0bf5ee6de..b3ee167a1 100644 --- a/bus/usb/usbtrans.c +++ b/bus/usb/usbtrans.c @@ -213,6 +213,7 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev, transfer->type = GRUB_USB_TRANSACTION_TYPE_BULK; transfer->max = max; transfer->dev = dev; + transfer->last_trans = -1; /* Reset index of last processed transaction (TD) */ /* Allocate an array of transfer data structures. */ transfer->transactions = grub_malloc (transfer->transcnt @@ -240,6 +241,13 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev, } err = dev->controller.dev->transfer (&dev->controller, transfer); + /* We must remember proper toggle value even if some transactions + * were not processed - correct value should be inversion of last + * processed transaction (TD). */ + if (transfer->last_trans >= 0) + toggle = transfer->transactions[transfer->last_trans].toggle ? 0 : 1; + else + toggle = dev->toggle[endpoint]; /* Nothing done, take original */ grub_dprintf ("usb", "toggle=%d\n", toggle); dev->toggle[endpoint] = toggle; diff --git a/disk/scsi.c b/disk/scsi.c index e8bfb6a3f..dffabc26e 100644 --- a/disk/scsi.c +++ b/disk/scsi.c @@ -421,7 +421,7 @@ grub_scsi_open (const char *name, grub_disk_t disk) /* According to USB MS tests specification, issue Test Unit Ready * until OK */ - maxtime = grub_get_time_ms () + 1000; + maxtime = grub_get_time_ms () + 5000; /* It is safer value */ do { /* Timeout is necessary - for example in case when we have @@ -519,6 +519,37 @@ grub_scsi_read (grub_disk_t disk, grub_disk_addr_t sector, /* XXX: Never reached. */ return GRUB_ERR_NONE; + +#if 0 /* Workaround - it works - but very slowly, from some reason + * unknown to me (specially on OHCI). Do not use it. */ + /* Split transfer requests to device sector size because */ + /* some devices are not able to transfer more than 512-1024 bytes */ + grub_err_t err = GRUB_ERR_NONE; + + for ( ; size; size--) + { + /* Depending on the type, select a read function. */ + switch (scsi->devtype) + { + case grub_scsi_devtype_direct: + err = grub_scsi_read10 (disk, sector, 1, buf); + break; + + case grub_scsi_devtype_cdrom: + err = grub_scsi_read12 (disk, sector, 1, buf); + break; + + default: /* This should not happen */ + return GRUB_ERR_READ_ERROR; + } + if (err) + return err; + sector++; + buf += scsi->blocksize; + } + + return err; +#endif } static grub_err_t diff --git a/disk/usbms.c b/disk/usbms.c index 7b719fb84..a49b30e7e 100644 --- a/disk/usbms.c +++ b/disk/usbms.c @@ -253,6 +253,7 @@ grub_usbms_transfer (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, struct grub_usbms_csw status; static grub_uint32_t tag = 0; grub_usb_err_t err = GRUB_USB_ERR_NONE; + grub_usb_err_t errCSW = GRUB_USB_ERR_NONE; int retrycnt = 3 + 1; grub_size_t i; @@ -290,9 +291,8 @@ grub_usbms_transfer (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, { if (err == GRUB_USB_ERR_STALL) { - grub_usb_clear_halt (dev->dev, dev->in->endp_addr); grub_usb_clear_halt (dev->dev, dev->out->endp_addr); - goto retry; + goto CheckCSW; } return grub_error (GRUB_ERR_IO, "USB Mass Storage request failed"); } @@ -302,7 +302,12 @@ grub_usbms_transfer (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, { err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr, size, buf); grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL); - if (err) goto CheckCSW; + if (err) + { + if (err == GRUB_USB_ERR_STALL) + grub_usb_clear_halt (dev->dev, dev->in->endp_addr); + goto CheckCSW; + } /* Debug print of received data. */ grub_dprintf ("usb", "buf:\n"); if (size <= 64) @@ -316,6 +321,12 @@ grub_usbms_transfer (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr, size, buf); grub_dprintf ("usb", "write: %d %d\n", err, GRUB_USB_ERR_STALL); grub_dprintf ("usb", "buf:\n"); + if (err) + { + if (err == GRUB_USB_ERR_STALL) + grub_usb_clear_halt (dev->dev, dev->out->endp_addr); + goto CheckCSW; + } /* Debug print of sent data. */ if (size <= 256) for (i=0; idev, dev->in->endp_addr, + errCSW = grub_usb_bulk_read (dev->dev, dev->in->endp_addr, sizeof (status), (char *) &status); - if (err) + if (errCSW) { grub_usb_clear_halt (dev->dev, dev->in->endp_addr); - err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr, + errCSW = grub_usb_bulk_read (dev->dev, dev->in->endp_addr, sizeof (status), (char *) &status); - if (err) + if (errCSW) { /* Bulk-only reset device. */ + grub_dprintf ("usb", "Bulk-only reset device - errCSW\n"); grub_usbms_reset (dev->dev, dev->interface); grub_usb_clear_halt (dev->dev, dev->in->endp_addr); grub_usb_clear_halt (dev->dev, dev->out->endp_addr); @@ -347,9 +359,11 @@ CheckCSW: status.signature, status.tag, status.residue); grub_dprintf ("usb", "CSW: status=0x%02x\n", status.status); - /* If phase error, do bulk-only reset device. */ - if (status.status == 2) - { + /* If phase error or not valid signature, do bulk-only reset device. */ + if ((status.status == 2) || + (status.signature != grub_cpu_to_le32(0x53425355))) + { /* Bulk-only reset device. */ + grub_dprintf ("usb", "Bulk-only reset device - bad status\n"); grub_usbms_reset (dev->dev, dev->interface); grub_usb_clear_halt (dev->dev, dev->in->endp_addr); grub_usb_clear_halt (dev->dev, dev->out->endp_addr); @@ -357,9 +371,13 @@ CheckCSW: goto retry; } - if (status.status) + /* If "command failed" status or data transfer failed -> error */ + if ((status.status || err) && !read_write) return grub_error (GRUB_ERR_READ_ERROR, "error communication with USB Mass Storage device"); + else if ((status.status || err) && read_write) + return grub_error (GRUB_ERR_WRITE_ERROR, + "error communication with USB Mass Storage device"); return GRUB_ERR_NONE; } diff --git a/include/grub/usb.h b/include/grub/usb.h index cbbc39303..64dc78d61 100644 --- a/include/grub/usb.h +++ b/include/grub/usb.h @@ -35,7 +35,8 @@ typedef enum GRUB_USB_ERR_NAK, GRUB_USB_ERR_BABBLE, GRUB_USB_ERR_TIMEOUT, - GRUB_USB_ERR_BITSTUFF + GRUB_USB_ERR_BITSTUFF, + GRUB_USB_ERR_UNRECOVERABLE } grub_usb_err_t; typedef enum diff --git a/include/grub/usbtrans.h b/include/grub/usbtrans.h index cd818e1fc..40fc0dd5f 100644 --- a/include/grub/usbtrans.h +++ b/include/grub/usbtrans.h @@ -58,6 +58,9 @@ struct grub_usb_transfer struct grub_usb_device *dev; struct grub_usb_transaction *transactions; + + int last_trans; + /* Index of last processed transaction in OHCI/UHCI driver. */ }; typedef struct grub_usb_transfer *grub_usb_transfer_t; diff --git a/term/usb_keyboard.c b/term/usb_keyboard.c index 5d76c5e02..69d5709b6 100644 --- a/term/usb_keyboard.c +++ b/term/usb_keyboard.c @@ -18,7 +18,6 @@ */ #include -#include #include #include #include From 390b0676c482abcc47c70a530b2bf4edb10f7479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ale=C5=A1=20Nesrsta?= Date: Wed, 2 Jun 2010 02:04:45 +0200 Subject: [PATCH 05/16] Use dev->descdev.maxsize0 even if dev->initialized isn't set --- bus/usb/usbtrans.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bus/usb/usbtrans.c b/bus/usb/usbtrans.c index b3ee167a1..e1b9097e6 100644 --- a/bus/usb/usbtrans.c +++ b/bus/usb/usbtrans.c @@ -76,7 +76,7 @@ grub_usb_control_msg (grub_usb_device_t dev, setupdata_addr = grub_dma_get_phys (setupdata_chunk); /* Determine the maximum packet size. */ - if (dev->initialized && dev->descdev.maxsize0) + if (dev->descdev.maxsize0) max = dev->descdev.maxsize0; else max = 64; From a18c78347304d00b521556ccc76d4c8564d417b0 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 2 Jun 2010 04:51:35 +0200 Subject: [PATCH 06/16] Fewstylistic fixes --- bus/usb/ohci.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/bus/usb/ohci.c b/bus/usb/ohci.c index ac6f61ee8..de0a4e7ac 100644 --- a/bus/usb/ohci.c +++ b/bus/usb/ohci.c @@ -594,10 +594,10 @@ grub_ohci_transfer (grub_usb_controller_t dev, } grub_dprintf ("ohci", "wait for completion\n"); - grub_dprintf ("ohci", - "begin: control=0x%02x status=0x%02x\n\t\t intstatus=0x%02x\n", + grub_dprintf ("ohci", "begin: control=0x%02x status=0x%02x\n", grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL), - grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS), + grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS)); + grub_dprintf ("ohci","intstatus=0x%02x\n", grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS)); /* Safety measure to avoid a hang. */ @@ -621,7 +621,7 @@ grub_ohci_transfer (grub_usb_controller_t dev, if (tderr_addr == td_list_addr + sizeof (td_list[0]) * (transfer->transcnt - 1)) break; - continue; + continue; } if ((intstatus & 0x10) != 0) @@ -651,9 +651,10 @@ grub_ohci_transfer (grub_usb_controller_t dev, } while (1); - grub_dprintf ("ohci", "end: control=0x%02x status=0x%02x\n\t\t intstatus=0x%02x\n", + grub_dprintf ("ohci", "end: control=0x%02x status=0x%02x\n", grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL), - grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS), + grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS)); + grub_dprintf ("ohci", "intstatus=0x%02x\n", grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS)); if (!tderr_addr) @@ -688,21 +689,22 @@ grub_ohci_transfer (grub_usb_controller_t dev, if (err_timeout) { err = GRUB_ERR_TIMEOUT; - grub_dprintf("ohci", "Timeout, target=%08x, head=%08x\n\t\ttail=%08x, next=%08x\n", - grub_le_to_cpu32(ed->target), - grub_le_to_cpu32(ed->td_head), - grub_le_to_cpu32(ed->td_tail), - grub_le_to_cpu32(ed->next_ed)); + grub_dprintf("ohci", "Timeout, target=%08x, head=%08x\n", + grub_le_to_cpu32(ed->target), + grub_le_to_cpu32(ed->td_head)); + grub_dprintf("ohci", "tail=%08x, next=%08x\n", + grub_le_to_cpu32(ed->td_tail), + grub_le_to_cpu32(ed->next_ed)); } /* In case of unrecoverable error do not detect error from TD */ else if (err_unrec) { err = GRUB_USB_ERR_UNRECOVERABLE; grub_dprintf("ohci", - "Unrecoverable error, target=%08x, head=%08x\n" - "\t\ttail=%08x, next=%08x\n", + "Unrecoverable error, target=%08x, head=%08x\n", grub_le_to_cpu32(ed->target), - grub_le_to_cpu32(ed->td_head), + grub_le_to_cpu32(ed->td_head)); + grub_dprintf("ohci", "tail=%08x, next=%08x\n", grub_le_to_cpu32(ed->td_tail), grub_le_to_cpu32(ed->next_ed)); } From 2325800310ce4ac9a5088f5c3fca68e44155e145 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 2 Jun 2010 04:52:35 +0200 Subject: [PATCH 07/16] Fix last_trans determination --- bus/usb/ohci.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/bus/usb/ohci.c b/bus/usb/ohci.c index de0a4e7ac..4d66df0bc 100644 --- a/bus/usb/ohci.c +++ b/bus/usb/ohci.c @@ -671,15 +671,21 @@ grub_ohci_transfer (grub_usb_controller_t dev, /* Remember last processed transaction (TD) - it is necessary for * proper setting of toggle bit in next transaction. */ - transfer->last_trans = (tderr_addr - td_list_addr) / sizeof (*td_list); - + transfer->last_trans = ((tderr_addr - td_list_addr) / sizeof (*td_list)); + grub_dprintf("ohci", "tderr_addr=0x%x, td_list_addr=0x%x,\n", + tderr_addr, td_list_addr); + + if ((ed->td_head & ~0xf) == (ed->td_tail & ~0xf)) + transfer->last_trans = transfer->transcnt - 1; + /* Check correct value in last_trans */ /* It could happen if timeout happens and no TD was retired */ if (transfer->last_trans >= transfer->transcnt || !tderr_addr) { - grub_dprintf("ohci", "tder==0 or out of TDs range!\n"); - grub_dprintf("ohci", "tderr_addr=0x%x, td_list=%p,\n\t\t last_trans=%d, transcnt=%d\n", - tderr_addr, td_list, transfer->last_trans, transfer->transcnt); + grub_dprintf("ohci", "tderr==0 or out of TDs range!\n"); + grub_dprintf("ohci", "last_trans=%d, transcnt=%d\n", + transfer->last_trans, transfer->transcnt); + /* We should set something valid... */ transfer->last_trans = -1; /* Probably no TD done */ tderr_addr = td_list_addr; @@ -713,6 +719,8 @@ grub_ohci_transfer (grub_usb_controller_t dev, grub_uint8_t errcode; grub_ohci_td_t tderr = NULL; + transfer->last_trans--; + tderr = (grub_ohci_td_t) ((char *) td_list + (tderr_addr - td_list_addr)); From f1cb760c99f77795f1c91f11f8157d6984f4593f Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 2 Jun 2010 04:52:52 +0200 Subject: [PATCH 08/16] Disable unused list --- bus/usb/ohci.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/bus/usb/ohci.c b/bus/usb/ohci.c index 4d66df0bc..fd378cc8d 100644 --- a/bus/usb/ohci.c +++ b/bus/usb/ohci.c @@ -563,9 +563,14 @@ grub_ohci_transfer (grub_usb_controller_t dev, grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, ed_addr); grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0); +#define GRUB_OHCI_REG_CONTROL_BULK_ENABLE (1 << 5) +#define GRUB_OHCI_REG_CONTROL_CONTROL_ENABLE (1 << 4) + /* Enable the Bulk list. */ control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL); - control |= 1 << 5; + control |= GRUB_OHCI_REG_CONTROL_BULK_ENABLE; + control &= ~GRUB_OHCI_REG_CONTROL_CONTROL_ENABLE; + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control); /* Set BulkListFilled. */ @@ -578,12 +583,15 @@ grub_ohci_transfer (grub_usb_controller_t dev, case GRUB_USB_TRANSACTION_TYPE_CONTROL: { + grub_dprintf ("ohci", "add to control list\n"); + /* Set ControlList Head and Current */ grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, ed_addr); grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0); /* Enable the Control list. */ - control |= 1 << 4; + control |= GRUB_OHCI_REG_CONTROL_CONTROL_ENABLE; + control &= ~GRUB_OHCI_REG_CONTROL_BULK_ENABLE; grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control); /* Set ControlListFilled. */ From d4955444976a90377d0ff9d8140bb906c2c36f46 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 2 Jun 2010 05:00:54 +0200 Subject: [PATCH 09/16] Shut down PHCI controller harder --- bus/usb/ohci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bus/usb/ohci.c b/bus/usb/ohci.c index fd378cc8d..a42d9fee1 100644 --- a/bus/usb/ohci.c +++ b/bus/usb/ohci.c @@ -970,7 +970,10 @@ grub_ohci_fini_hw (int noreturn __attribute__ ((unused))) grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0); grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0); grub_ohci_writereg32 (o, GRUB_OHCI_REG_DONEHEAD, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, 0); + grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1); } + grub_millisleep (10); return GRUB_ERR_NONE; } From 11dccbfd4d2ed5e91e5f4f767161a0095ef01025 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 2 Jun 2010 16:45:43 +0200 Subject: [PATCH 10/16] Sort scsi_cmd_t. --- include/grub/scsicmd.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/grub/scsicmd.h b/include/grub/scsicmd.h index f9a7cb200..9cc5afe72 100644 --- a/include/grub/scsicmd.h +++ b/include/grub/scsicmd.h @@ -147,14 +147,14 @@ struct grub_scsi_write12 typedef enum { - grub_scsi_cmd_inquiry = 0x12, grub_scsi_cmd_test_unit_ready = 0x00, + grub_scsi_cmd_request_sense = 0x03, + grub_scsi_cmd_inquiry = 0x12, grub_scsi_cmd_read_capacity = 0x25, grub_scsi_cmd_read10 = 0x28, grub_scsi_cmd_write10 = 0x2a, grub_scsi_cmd_read12 = 0xa8, grub_scsi_cmd_write12 = 0xaa, - grub_scsi_cmd_request_sense = 0x03 } grub_scsi_cmd_t; typedef enum From f0e6cceb813e626d313ae6b15e03683b82b322ca Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 2 Jun 2010 16:46:37 +0200 Subject: [PATCH 11/16] Remove leftover early debug exit. --- commands/usbtest.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/commands/usbtest.c b/commands/usbtest.c index 7d1f85cfd..191c4e4df 100644 --- a/commands/usbtest.c +++ b/commands/usbtest.c @@ -148,8 +148,6 @@ usb_iterate (grub_usb_device_t dev) grub_printf ("%s speed device\n", usb_devspeed[dev->speed]); - return 0; - for (i = 0; i < descdev->configcnt; i++) { struct grub_usb_desc_config *config; From 8bd3a1fb3027c7e05be06d5145bd8d16ee1bcbeb Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 2 Jun 2010 16:46:55 +0200 Subject: [PATCH 12/16] Add necessarry cast --- disk/scsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/disk/scsi.c b/disk/scsi.c index dffabc26e..5d3e50966 100644 --- a/disk/scsi.c +++ b/disk/scsi.c @@ -460,7 +460,7 @@ grub_scsi_open (const char *name, grub_disk_t disk) grub_dprintf ("scsi", "blocks=%u, blocksize=%u\n", scsi->size, scsi->blocksize); grub_dprintf ("scsi", "Disk total 512 sectors = %llu\n", - disk->total_sectors); + (unsigned long long) disk->total_sectors); return GRUB_ERR_NONE; } From f133a8a74b31c085e68831f76a7e78a8c49629da Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 2 Jun 2010 16:48:10 +0200 Subject: [PATCH 13/16] Enable USB on 64-bit (still won't work with >= 4GiB memory). --- bus/pci.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bus/pci.c b/bus/pci.c index 3e2126962..1635e889b 100644 --- a/bus/pci.c +++ b/bus/pci.c @@ -21,7 +21,8 @@ #include #include -#if GRUB_TARGET_SIZEOF_VOID_P == 4 +/* FIXME: correctly support 64-bit architectures. */ +/* #if GRUB_TARGET_SIZEOF_VOID_P == 4 */ struct grub_pci_dma_chunk * grub_memalign_dma32 (grub_size_t align, grub_size_t size) { @@ -33,7 +34,7 @@ grub_dma_free (struct grub_pci_dma_chunk *ch) { grub_free (ch); } -#endif +/* #endif */ #ifdef GRUB_MACHINE_MIPS_YEELOONG volatile void * From 5d538b8b4a17de54cda284ee1ba081afbef8f978 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 2 Jun 2010 16:49:22 +0200 Subject: [PATCH 14/16] Use memset --- bus/usb/usb.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bus/usb/usb.c b/bus/usb/usb.c index 7df148333..c872e9276 100644 --- a/bus/usb/usb.c +++ b/bus/usb/usb.c @@ -105,10 +105,7 @@ grub_usb_clear_halt (grub_usb_device_t dev, int endpoint) grub_usb_err_t grub_usb_set_configuration (grub_usb_device_t dev, int configuration) { - int i; - - for (i = 0; i < 256; i++) - dev->toggle[i] = 0; + grub_memset (dev->toggle, 0, sizeof (dev->toggle)); return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT | GRUB_USB_REQTYPE_STANDARD From 7db51e329b7e8f0cdeaea60b2fd866105406d9c4 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 2 Jun 2010 16:51:54 +0200 Subject: [PATCH 15/16] Remove double defines and use dma_free --- bus/usb/ohci.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/bus/usb/ohci.c b/bus/usb/ohci.c index a42d9fee1..956a27292 100644 --- a/bus/usb/ohci.c +++ b/bus/usb/ohci.c @@ -339,10 +339,8 @@ grub_ohci_pci_iter (grub_pci_device_t dev, return 0; fail: -#ifndef GRUB_MACHINE_MIPS_YEELOONG if (o) - grub_free ((void *) o->hcca); -#endif + grub_dma_free (o->hcca_chunk); grub_free (o); return 0; @@ -883,11 +881,6 @@ grub_ohci_transfer (grub_usb_controller_t dev, return err; } -#define GRUB_OHCI_SET_PORT_ENABLE (1 << 1) -#define GRUB_OHCI_CLEAR_PORT_ENABLE (1 << 0) -#define GRUB_OHCI_SET_PORT_RESET (1 << 4) -#define GRUB_OHCI_SET_PORT_RESET_STATUS_CHANGE (1 << 20) - static grub_err_t grub_ohci_portstatus (grub_usb_controller_t dev, unsigned int port, unsigned int enable) From 49396b4f61726411e4d7bfaefbf2f62e93763102 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 2 Jun 2010 16:52:16 +0200 Subject: [PATCH 16/16] Add ChangeLog --- ChangeLog | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/ChangeLog b/ChangeLog index 5072a6026..a693e4385 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,122 @@ +2010-06-02 Aleš Nesrsta + + Finally make USB usable. + + * bus/usb/ohci.c (grub_ohci_reg_t): Add missing values. + (GRUB_OHCI_RHUB_PORT_POWER_MASK): New macro. + (GRUB_OHCI_RHUB_PORT_ALL_POWERED): Likewise. + (GRUB_OHCI_REG_FRAME_INTERVAL_FSMPS_MASK): Likewise. + (GRUB_OHCI_REG_FRAME_INTERVAL_FSMPS_SHIFT): Likewise. + (GRUB_OHCI_REG_FRAME_INTERVAL_FI_SHIFT): Likewise. + (GRUB_OHCI_FSMPS): Likewise. + (GRUB_OHCI_PERIODIC_START): Likewise. + (GRUB_OHCI_FRAME_INTERVAL): Likewise. + (GRUB_OHCI_SET_PORT_ENABLE): Likewise. + (GRUB_OHCI_CLEAR_PORT_ENABLE): Likewise. + (GRUB_OHCI_SET_PORT_RESET): Likewise. + (GRUB_OHCI_SET_PORT_RESET_STATUS_CHANGE): Likewise. + * bus/usb/ohci.c (grub_ohci_pci_iter): Various important fixups. + (grub_ohci_transaction): Likewise. + (grub_ohci_transfer): Improve condition detection algorithms. + Handle toggle property. Program the transactions correctly. + Improve error handling. Various important fixups. + (grub_ohci_portstatus): Put register writes in right order. + * bus/usb/uhci.c (grub_free_queue): Compute last_trans. + (grub_uhci_transfer): Don't show "failed" message on success. + * bus/usb/usb.c (grub_usb_set_configuration): Zero-fill whole "toggle" + array. + (grub_usb_device_initialize): Read first 8 bytes of descriptor to + determine its size. + * bus/usb/usbtrans.c (grub_usb_control_msg): Use descdev.maxsize0 even + before initialization is completed. Use IN direction for empty + transfers. Use last_trans and compute toggle. + * include/grub/usbtrans.h (grub_usb_transfer): New field last_trans. + (GRUB_USB_FEATURE_ENDP_HALT): Correct the value. + (GRUB_USB_FEATURE_DEV_REMOTE_WU): Likewise. + (GRUB_USB_FEATURE_TEST_MODE): Likewise. + * include/grub/usb.h (grub_usb_err_t): New value GRUB_USB_ERR_UNRECOVERABLE. + (grub_usb_device): Increase toggle to 256. + (grub_usbms_subclass_t): New values GRUB_USBMS_SUBCLASS_RBC, + GRUB_USBMS_SUBCLASS_MMC2, GRUB_USBMS_SUBCLASS_UFI and + GRUB_USBMS_SUBCLASS_SFF8070. + * include/grub/scsicmd.h (grub_scsi_test_unit_ready): New structure. + (grub_scsi_inquiry): New member page and alloc_length. + (grub_scsi_request_sense): New structure. + (grub_scsi_request_sense_data): Likewise. + (grub_scsi_read_capacity): New fields logical_block_addr, PMI and + control. + * disk/scsi.c (grub_scsi_request_sense): New function. + (grub_scsi_test_unit_ready): Likewise. + (grub_scsi_inquiry): Fill new fields. + (grub_scsi_read_capacity): Likewise. + (grub_scsi_read10): Add request sense at the end. + (grub_scsi_read12): Likewise. + (grub_scsi_write10): Likewise. + (grub_scsi_write12): Likewise. + (grub_scsi_open): Add Test Unit Ready. + * disk/usbms.c (grub_usbms_finddevs): Check configcnt. + Support additional subclasses. Con't clear halt yet. Activate the + proper config. Calculate LUNs correctly. + (grub_usbms_transfer): Various important fixups. + +2010-06-02 Vladimir Serbinenko + + * bus/pci.c (grub_pci_iterate) [GRUB_MACHINE_MIPS_YEELOONG]: Skip ghosts. + * bus/usb/ohci.c (grub_ohci_portstatus): Handle R/WC correctly. + (grub_ohci_fini_hw): New function. + (grub_ohci_restore_hw): Likewise. + (GRUB_MOD_INIT(ohci)): Register preboot hook. + (GRUB_MOD_FINI(ohci)): Shutdown OHCI. + * term/usb_keyboard.c: Remove include of grub/machine/console.h. + +2010-06-02 Vladimir Serbinenko + + Dedicated DMA allocations. + + * bus/pci.c (grub_memalign_dma32): New function + (grub_dma_free): Likewise. + (grub_dma_get_virt): Likewise. + (grub_dma_get_phys): Likewise. + * bus/usb/ohci.c (grub_ohci): New members hcca_addr and hcca_chunk. + (grub_ohci_pci_iter): Use dma32_alloc. + (grub_ohci_transfer): Likewise. + * bus/usb/usbtrans.c (grub_usb_control_msg): Likewise. + (grub_usb_bulk_readwrite): Likewise. + * include/grub/pci.h: Add declarations. + +2010-06-02 Vladimir Serbinenko + + CS5536 support. + + * bus/cs5536.c: New file. + * bus/usb/ohci.c (grub_ohci_pci_iter): Check for CS5536. + * conf/i386.rmk (pkglib_MODULES): Add cs5536.mod. + (cs5536_mod_SOURCES): New variable. + (cs5536_mod_CFLAGS): Likewise. + (cs5536_mod_LDFLAGS): Likewise. + * conf/mips-yeeloong.rmk (kernel_img_HEADERS): Add cs5536.h and + machine/pci.h. + (kernel_img_SOURCES): Add bus/cs5536.c. + (pkglib_MODULES): Add usb.mod, usbtest.mod, ohci.mod, usbms.mod and + usb_keyboard.mod. + (usb_mod_SOURCES): New variable. + (usb_mod_CFLAGS): New variable. + (usb_mod_LDFLAGS): New variable. + (usbtest_mod_SOURCES): New variable. + (usbtest_mod_CFLAGS): New variable. + (usbtest_mod_LDFLAGS): New variable. + (ohci_mod_SOURCES): New variable. + (ohci_mod_CFLAGS): New variable. + (ohci_mod_LDFLAGS): New variable. + (usbms_mod_SOURCES): New variable. + (usbms_mod_CFLAGS): New variable. + (usbms_mod_LDFLAGS): New variable. + (usb_keyboard_mod_SOURCES): New variable. + (usb_keyboard_mod_CFLAGS): New variable. + (usb_keyboard_mod_LDFLAGS): New variable. + * include/grub/smbus.h: New file. + * include/grub/cs5536.h: New file. + 2010-05-28 Colin Watson * configure.ac: AC_PROG_LEX sets LEX to ":" if lex is missing, not