diff options
Diffstat (limited to 'roms/seabios/src/hw')
56 files changed, 15883 insertions, 0 deletions
diff --git a/roms/seabios/src/hw/ahci.c b/roms/seabios/src/hw/ahci.c new file mode 100644 index 000000000..687cc7d8d --- /dev/null +++ b/roms/seabios/src/hw/ahci.c @@ -0,0 +1,640 @@ +// Low level AHCI disk access +// +// Copyright (C) 2010 Gerd Hoffmann <kraxel@redhat.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "ahci.h" // CDB_CMD_READ_10 +#include "ata.h" // ATA_CB_STAT +#include "biosvar.h" // GET_GLOBAL +#include "blockcmd.h" // CDB_CMD_READ_10 +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_CLASS_STORAGE_OTHER +#include "pci_regs.h" // PCI_INTERRUPT_LINE +#include "stacks.h" // yield +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // timer_calc +#include "x86.h" // inb + +#define AHCI_REQUEST_TIMEOUT 32000 // 32 seconds max for IDE ops +#define AHCI_RESET_TIMEOUT 500 // 500 miliseconds +#define AHCI_LINK_TIMEOUT 10 // 10 miliseconds + +// prepare sata command fis +static void sata_prep_simple(struct sata_cmd_fis *fis, u8 command) +{ + memset_fl(fis, 0, sizeof(*fis)); + fis->command = command; +} + +static void sata_prep_readwrite(struct sata_cmd_fis *fis, + struct disk_op_s *op, int iswrite) +{ + u64 lba = op->lba; + u8 command; + + memset_fl(fis, 0, sizeof(*fis)); + + if (op->count >= (1<<8) || lba + op->count >= (1<<28)) { + fis->sector_count2 = op->count >> 8; + fis->lba_low2 = lba >> 24; + fis->lba_mid2 = lba >> 32; + fis->lba_high2 = lba >> 40; + lba &= 0xffffff; + command = (iswrite ? ATA_CMD_WRITE_DMA_EXT + : ATA_CMD_READ_DMA_EXT); + } else { + command = (iswrite ? ATA_CMD_WRITE_DMA + : ATA_CMD_READ_DMA); + } + fis->feature = 1; /* dma */ + fis->command = command; + fis->sector_count = op->count; + fis->lba_low = lba; + fis->lba_mid = lba >> 8; + fis->lba_high = lba >> 16; + fis->device = ((lba >> 24) & 0xf) | ATA_CB_DH_LBA; +} + +static void sata_prep_atapi(struct sata_cmd_fis *fis, u16 blocksize) +{ + memset_fl(fis, 0, sizeof(*fis)); + fis->command = ATA_CMD_PACKET; + fis->feature = 1; /* dma */ + fis->lba_mid = blocksize; + fis->lba_high = blocksize >> 8; +} + +// ahci register access helpers +static u32 ahci_ctrl_readl(struct ahci_ctrl_s *ctrl, u32 reg) +{ + u32 addr = ctrl->iobase + reg; + return readl((void*)addr); +} + +static void ahci_ctrl_writel(struct ahci_ctrl_s *ctrl, u32 reg, u32 val) +{ + u32 addr = ctrl->iobase + reg; + writel((void*)addr, val); +} + +static u32 ahci_port_to_ctrl(u32 pnr, u32 port_reg) +{ + u32 ctrl_reg = 0x100; + ctrl_reg += pnr * 0x80; + ctrl_reg += port_reg; + return ctrl_reg; +} + +static u32 ahci_port_readl(struct ahci_ctrl_s *ctrl, u32 pnr, u32 reg) +{ + u32 ctrl_reg = ahci_port_to_ctrl(pnr, reg); + return ahci_ctrl_readl(ctrl, ctrl_reg); +} + +static void ahci_port_writel(struct ahci_ctrl_s *ctrl, u32 pnr, u32 reg, u32 val) +{ + u32 ctrl_reg = ahci_port_to_ctrl(pnr, reg); + ahci_ctrl_writel(ctrl, ctrl_reg, val); +} + +// submit ahci command + wait for result +static int ahci_command(struct ahci_port_s *port_gf, int iswrite, int isatapi, + void *buffer, u32 bsize) +{ + u32 val, status, success, flags, intbits, error; + struct ahci_ctrl_s *ctrl = port_gf->ctrl; + struct ahci_cmd_s *cmd = port_gf->cmd; + struct ahci_fis_s *fis = port_gf->fis; + struct ahci_list_s *list = port_gf->list; + u32 pnr = port_gf->pnr; + + cmd->fis.reg = 0x27; + cmd->fis.pmp_type = 1 << 7; /* cmd fis */ + cmd->prdt[0].base = (u32)buffer; + cmd->prdt[0].baseu = 0; + cmd->prdt[0].flags = bsize-1; + + flags = ((1 << 16) | /* one prd entry */ + (iswrite ? (1 << 6) : 0) | + (isatapi ? (1 << 5) : 0) | + (5 << 0)); /* fis length (dwords) */ + list[0].flags = flags; + list[0].bytes = 0; + list[0].base = (u32)(cmd); + list[0].baseu = 0; + + dprintf(8, "AHCI/%d: send cmd ...\n", pnr); + intbits = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT); + if (intbits) + ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, intbits); + ahci_port_writel(ctrl, pnr, PORT_SCR_ACT, 1); + ahci_port_writel(ctrl, pnr, PORT_CMD_ISSUE, 1); + + u32 end = timer_calc(AHCI_REQUEST_TIMEOUT); + do { + for (;;) { + intbits = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT); + if (intbits) { + ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, intbits); + if (intbits & 0x02) { + status = GET_LOWFLAT(fis->psfis[2]); + error = GET_LOWFLAT(fis->psfis[3]); + break; + } + if (intbits & 0x01) { + status = GET_LOWFLAT(fis->rfis[2]); + error = GET_LOWFLAT(fis->rfis[3]); + break; + } + } + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } + dprintf(8, "AHCI/%d: ... intbits 0x%x, status 0x%x ...\n", + pnr, intbits, status); + } while (status & ATA_CB_STAT_BSY); + + success = (0x00 == (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DF | + ATA_CB_STAT_ERR)) && + ATA_CB_STAT_RDY == (status & (ATA_CB_STAT_RDY))); + if (success) { + dprintf(8, "AHCI/%d: ... finished, status 0x%x, OK\n", pnr, + status); + } else { + dprintf(2, "AHCI/%d: ... finished, status 0x%x, ERROR 0x%x\n", pnr, + status, error); + + // non-queued error recovery (AHCI 1.3 section 6.2.2.1) + // Clears PxCMD.ST to 0 to reset the PxCI register + val = ahci_port_readl(ctrl, pnr, PORT_CMD); + ahci_port_writel(ctrl, pnr, PORT_CMD, val & ~PORT_CMD_START); + + // waits for PxCMD.CR to clear to 0 + while (1) { + val = ahci_port_readl(ctrl, pnr, PORT_CMD); + if ((val & PORT_CMD_LIST_ON) == 0) + break; + yield(); + } + + // Clears any error bits in PxSERR to enable capturing new errors + val = ahci_port_readl(ctrl, pnr, PORT_SCR_ERR); + ahci_port_writel(ctrl, pnr, PORT_SCR_ERR, val); + + // Clears status bits in PxIS as appropriate + val = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT); + ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, val); + + // If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to 1, issue + // a COMRESET to the device to put it in an idle state + val = ahci_port_readl(ctrl, pnr, PORT_TFDATA); + if (val & (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ)) { + dprintf(2, "AHCI/%d: issue comreset\n", pnr); + val = ahci_port_readl(ctrl, pnr, PORT_SCR_CTL); + // set Device Detection Initialization (DET) to 1 for 1 ms for comreset + ahci_port_writel(ctrl, pnr, PORT_SCR_CTL, val | 1); + mdelay (1); + ahci_port_writel(ctrl, pnr, PORT_SCR_CTL, val); + } + + // Sets PxCMD.ST to 1 to enable issuing new commands + val = ahci_port_readl(ctrl, pnr, PORT_CMD); + ahci_port_writel(ctrl, pnr, PORT_CMD, val | PORT_CMD_START); + } + return success ? 0 : -1; +} + +#define CDROM_CDB_SIZE 12 + +int ahci_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + if (! CONFIG_AHCI) + return 0; + + struct ahci_port_s *port_gf = container_of( + op->drive_gf, struct ahci_port_s, drive); + struct ahci_cmd_s *cmd = port_gf->cmd; + u8 *atapi = cdbcmd; + int i, rc; + + sata_prep_atapi(&cmd->fis, blocksize); + for (i = 0; i < CDROM_CDB_SIZE; i++) { + cmd->atapi[i] = atapi[i]; + } + rc = ahci_command(port_gf, 0, 1, op->buf_fl, + op->count * blocksize); + if (rc < 0) + return DISK_RET_EBADTRACK; + return DISK_RET_SUCCESS; +} + +// read/write count blocks from a harddrive, op->buf_fl must be word aligned +static int +ahci_disk_readwrite_aligned(struct disk_op_s *op, int iswrite) +{ + struct ahci_port_s *port_gf = container_of( + op->drive_gf, struct ahci_port_s, drive); + struct ahci_cmd_s *cmd = port_gf->cmd; + int rc; + + sata_prep_readwrite(&cmd->fis, op, iswrite); + rc = ahci_command(port_gf, iswrite, 0, op->buf_fl, + op->count * DISK_SECTOR_SIZE); + dprintf(8, "ahci disk %s, lba %6x, count %3x, buf %p, rc %d\n", + iswrite ? "write" : "read", (u32)op->lba, op->count, op->buf_fl, rc); + if (rc < 0) + return DISK_RET_EBADTRACK; + return DISK_RET_SUCCESS; +} + +// read/write count blocks from a harddrive. +static int +ahci_disk_readwrite(struct disk_op_s *op, int iswrite) +{ + // if caller's buffer is word aligned, use it directly + if (((u32) op->buf_fl & 1) == 0) + return ahci_disk_readwrite_aligned(op, iswrite); + + // Use a word aligned buffer for AHCI I/O + int rc; + struct disk_op_s localop = *op; + u8 *alignedbuf_fl = bounce_buf_fl; + u8 *position = op->buf_fl; + + localop.buf_fl = alignedbuf_fl; + localop.count = 1; + + if (iswrite) { + u16 block; + for (block = 0; block < op->count; block++) { + memcpy_fl (alignedbuf_fl, position, DISK_SECTOR_SIZE); + rc = ahci_disk_readwrite_aligned (&localop, 1); + if (rc) + return rc; + position += DISK_SECTOR_SIZE; + localop.lba++; + } + } else { // read + u16 block; + for (block = 0; block < op->count; block++) { + rc = ahci_disk_readwrite_aligned (&localop, 0); + if (rc) + return rc; + memcpy_fl (position, alignedbuf_fl, DISK_SECTOR_SIZE); + position += DISK_SECTOR_SIZE; + localop.lba++; + } + } + return DISK_RET_SUCCESS; +} + +// command demuxer +int VISIBLE32FLAT +process_ahci_op(struct disk_op_s *op) +{ + if (!CONFIG_AHCI) + return 0; + switch (op->command) { + case CMD_READ: + return ahci_disk_readwrite(op, 0); + case CMD_WRITE: + return ahci_disk_readwrite(op, 1); + case CMD_FORMAT: + case CMD_RESET: + case CMD_ISREADY: + case CMD_VERIFY: + case CMD_SEEK: + return DISK_RET_SUCCESS; + default: + dprintf(1, "AHCI: unknown disk command %d\n", op->command); + op->count = 0; + return DISK_RET_EPARAM; + } +} + +static void +ahci_port_reset(struct ahci_ctrl_s *ctrl, u32 pnr) +{ + u32 val; + + /* disable FIS + CMD */ + u32 end = timer_calc(AHCI_RESET_TIMEOUT); + for (;;) { + val = ahci_port_readl(ctrl, pnr, PORT_CMD); + if (!(val & (PORT_CMD_FIS_RX | PORT_CMD_START | + PORT_CMD_FIS_ON | PORT_CMD_LIST_ON))) + break; + val &= ~(PORT_CMD_FIS_RX | PORT_CMD_START); + ahci_port_writel(ctrl, pnr, PORT_CMD, val); + if (timer_check(end)) { + warn_timeout(); + break; + } + yield(); + } + + /* disable + clear IRQs */ + ahci_port_writel(ctrl, pnr, PORT_IRQ_MASK, 0); + val = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT); + if (val) + ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, val); +} + +static struct ahci_port_s* +ahci_port_alloc(struct ahci_ctrl_s *ctrl, u32 pnr) +{ + struct ahci_port_s *port = malloc_tmp(sizeof(*port)); + + if (!port) { + warn_noalloc(); + return NULL; + } + port->pnr = pnr; + port->ctrl = ctrl; + port->list = memalign_tmp(1024, 1024); + port->fis = memalign_tmp(256, 256); + port->cmd = memalign_tmp(256, 256); + if (port->list == NULL || port->fis == NULL || port->cmd == NULL) { + warn_noalloc(); + return NULL; + } + memset(port->list, 0, 1024); + memset(port->fis, 0, 256); + memset(port->cmd, 0, 256); + + ahci_port_writel(ctrl, pnr, PORT_LST_ADDR, (u32)port->list); + ahci_port_writel(ctrl, pnr, PORT_FIS_ADDR, (u32)port->fis); + return port; +} + +static void ahci_port_release(struct ahci_port_s *port) +{ + ahci_port_reset(port->ctrl, port->pnr); + free(port->list); + free(port->fis); + free(port->cmd); + free(port); +} + +static struct ahci_port_s* ahci_port_realloc(struct ahci_port_s *port) +{ + struct ahci_port_s *tmp; + u32 cmd; + + tmp = malloc_fseg(sizeof(*port)); + if (!tmp) { + warn_noalloc(); + ahci_port_release(port); + return NULL; + } + *tmp = *port; + free(port); + port = tmp; + + ahci_port_reset(port->ctrl, port->pnr); + + free(port->list); + free(port->fis); + free(port->cmd); + port->list = memalign_high(1024, 1024); + port->fis = memalign_high(256, 256); + port->cmd = memalign_high(256, 256); + + ahci_port_writel(port->ctrl, port->pnr, PORT_LST_ADDR, (u32)port->list); + ahci_port_writel(port->ctrl, port->pnr, PORT_FIS_ADDR, (u32)port->fis); + + cmd = ahci_port_readl(port->ctrl, port->pnr, PORT_CMD); + cmd |= (PORT_CMD_FIS_RX|PORT_CMD_START); + ahci_port_writel(port->ctrl, port->pnr, PORT_CMD, cmd); + + return port; +} + +#define MAXMODEL 40 + +/* See ahci spec chapter 10.1 "Software Initialization of HBA" */ +static int ahci_port_setup(struct ahci_port_s *port) +{ + struct ahci_ctrl_s *ctrl = port->ctrl; + u32 pnr = port->pnr; + char model[MAXMODEL+1]; + u16 buffer[256]; + u32 cmd, stat, err, tf; + int rc; + + /* enable FIS recv */ + cmd = ahci_port_readl(ctrl, pnr, PORT_CMD); + cmd |= PORT_CMD_FIS_RX; + ahci_port_writel(ctrl, pnr, PORT_CMD, cmd); + + /* spin up */ + cmd |= PORT_CMD_SPIN_UP; + ahci_port_writel(ctrl, pnr, PORT_CMD, cmd); + u32 end = timer_calc(AHCI_LINK_TIMEOUT); + for (;;) { + stat = ahci_port_readl(ctrl, pnr, PORT_SCR_STAT); + if ((stat & 0x07) == 0x03) { + dprintf(2, "AHCI/%d: link up\n", port->pnr); + break; + } + if (timer_check(end)) { + dprintf(2, "AHCI/%d: link down\n", port->pnr); + return -1; + } + yield(); + } + + /* clear error status */ + err = ahci_port_readl(ctrl, pnr, PORT_SCR_ERR); + if (err) + ahci_port_writel(ctrl, pnr, PORT_SCR_ERR, err); + + /* wait for device becoming ready */ + end = timer_calc(AHCI_REQUEST_TIMEOUT); + for (;;) { + tf = ahci_port_readl(ctrl, pnr, PORT_TFDATA); + if (!(tf & (ATA_CB_STAT_BSY | + ATA_CB_STAT_DRQ))) + break; + if (timer_check(end)) { + warn_timeout(); + dprintf(1, "AHCI/%d: device not ready (tf 0x%x)\n", port->pnr, tf); + return -1; + } + yield(); + } + + /* start device */ + cmd |= PORT_CMD_START; + ahci_port_writel(ctrl, pnr, PORT_CMD, cmd); + + sata_prep_simple(&port->cmd->fis, ATA_CMD_IDENTIFY_PACKET_DEVICE); + rc = ahci_command(port, 0, 0, buffer, sizeof(buffer)); + if (rc == 0) { + port->atapi = 1; + } else { + port->atapi = 0; + sata_prep_simple(&port->cmd->fis, ATA_CMD_IDENTIFY_DEVICE); + rc = ahci_command(port, 0, 0, buffer, sizeof(buffer)); + if (rc < 0) + return -1; + } + + port->drive.cntl_id = pnr; + port->drive.removable = (buffer[0] & 0x80) ? 1 : 0; + + if (!port->atapi) { + // found disk (ata) + port->drive.type = DTYPE_AHCI; + port->drive.blksize = DISK_SECTOR_SIZE; + port->drive.pchs.cylinder = buffer[1]; + port->drive.pchs.head = buffer[3]; + port->drive.pchs.sector = buffer[6]; + + u64 sectors; + if (buffer[83] & (1 << 10)) // word 83 - lba48 support + sectors = *(u64*)&buffer[100]; // word 100-103 + else + sectors = *(u32*)&buffer[60]; // word 60 and word 61 + port->drive.sectors = sectors; + u64 adjsize = sectors >> 11; + char adjprefix = 'M'; + if (adjsize >= (1 << 16)) { + adjsize >>= 10; + adjprefix = 'G'; + } + port->desc = znprintf(MAXDESCSIZE + , "AHCI/%d: %s ATA-%d Hard-Disk (%u %ciBytes)" + , port->pnr + , ata_extract_model(model, MAXMODEL, buffer) + , ata_extract_version(buffer) + , (u32)adjsize, adjprefix); + port->prio = bootprio_find_ata_device(ctrl->pci_tmp, pnr, 0); + } else { + // found cdrom (atapi) + port->drive.type = DTYPE_AHCI_ATAPI; + port->drive.blksize = CDROM_SECTOR_SIZE; + port->drive.sectors = (u64)-1; + u8 iscd = ((buffer[0] >> 8) & 0x1f) == 0x05; + if (!iscd) { + dprintf(1, "AHCI/%d: atapi device isn't a cdrom\n", port->pnr); + return -1; + } + port->desc = znprintf(MAXDESCSIZE + , "DVD/CD [AHCI/%d: %s ATAPI-%d DVD/CD]" + , port->pnr + , ata_extract_model(model, MAXMODEL, buffer) + , ata_extract_version(buffer)); + port->prio = bootprio_find_ata_device(ctrl->pci_tmp, pnr, 0); + } + return 0; +} + +// Detect any drives attached to a given controller. +static void +ahci_port_detect(void *data) +{ + struct ahci_port_s *port = data; + int rc; + + dprintf(2, "AHCI/%d: probing\n", port->pnr); + ahci_port_reset(port->ctrl, port->pnr); + rc = ahci_port_setup(port); + if (rc < 0) + ahci_port_release(port); + else { + port = ahci_port_realloc(port); + if (port == NULL) + return; + dprintf(1, "AHCI/%d: registering: \"%s\"\n", port->pnr, port->desc); + if (!port->atapi) { + // Register with bcv system. + boot_add_hd(&port->drive, port->desc, port->prio); + } else { + // fill cdidmap + boot_add_cd(&port->drive, port->desc, port->prio); + } + } +} + +// Initialize an ata controller and detect its drives. +static void +ahci_controller_setup(struct pci_device *pci) +{ + struct ahci_ctrl_s *ctrl = malloc_fseg(sizeof(*ctrl)); + struct ahci_port_s *port; + u16 bdf = pci->bdf; + u32 val, pnr, max; + + if (!ctrl) { + warn_noalloc(); + return; + } + + if (create_bounce_buf() < 0) { + warn_noalloc(); + free(ctrl); + return; + } + + ctrl->pci_tmp = pci; + ctrl->pci_bdf = bdf; + ctrl->iobase = pci_config_readl(bdf, PCI_BASE_ADDRESS_5); + ctrl->irq = pci_config_readb(bdf, PCI_INTERRUPT_LINE); + dprintf(1, "AHCI controller at %02x.%x, iobase %x, irq %d\n", + bdf >> 3, bdf & 7, ctrl->iobase, ctrl->irq); + + pci_config_maskw(bdf, PCI_COMMAND, 0, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + val = ahci_ctrl_readl(ctrl, HOST_CTL); + ahci_ctrl_writel(ctrl, HOST_CTL, val | HOST_CTL_AHCI_EN); + + ctrl->caps = ahci_ctrl_readl(ctrl, HOST_CAP); + ctrl->ports = ahci_ctrl_readl(ctrl, HOST_PORTS_IMPL); + dprintf(2, "AHCI: cap 0x%x, ports_impl 0x%x\n", + ctrl->caps, ctrl->ports); + + max = ctrl->caps & 0x1f; + for (pnr = 0; pnr <= max; pnr++) { + if (!(ctrl->ports & (1 << pnr))) + continue; + port = ahci_port_alloc(ctrl, pnr); + if (port == NULL) + continue; + run_thread(ahci_port_detect, port); + } +} + +// Locate and init ahci controllers. +static void +ahci_scan(void) +{ + // Scan PCI bus for ATA adapters + struct pci_device *pci; + foreachpci(pci) { + if (pci->class != PCI_CLASS_STORAGE_SATA) + continue; + if (pci->prog_if != 1 /* AHCI rev 1 */) + continue; + ahci_controller_setup(pci); + } +} + +void +ahci_setup(void) +{ + ASSERT32FLAT(); + if (!CONFIG_AHCI) + return; + + dprintf(3, "init ahci\n"); + ahci_scan(); +} diff --git a/roms/seabios/src/hw/ahci.h b/roms/seabios/src/hw/ahci.h new file mode 100644 index 000000000..c8c755a3f --- /dev/null +++ b/roms/seabios/src/hw/ahci.h @@ -0,0 +1,202 @@ +#ifndef __AHCI_H +#define __AHCI_H + +#include "block.h" // struct drive_s +#include "types.h" // u32 + +struct sata_cmd_fis { + u8 reg; + u8 pmp_type; + u8 command; + u8 feature; + + u8 lba_low; + u8 lba_mid; + u8 lba_high; + u8 device; + + u8 lba_low2; + u8 lba_mid2; + u8 lba_high2; + u8 feature2; + + u8 sector_count; + u8 sector_count2; + u8 res_1; + u8 control; + + u8 res_2[64 - 16]; +}; + +struct ahci_ctrl_s { + struct pci_device *pci_tmp; + u16 pci_bdf; + u8 irq; + u32 iobase; + u32 caps; + u32 ports; +}; + +struct ahci_cmd_s { + struct sata_cmd_fis fis; + u8 atapi[0x20]; + u8 res[0x20]; + struct { + u32 base; + u32 baseu; + u32 res; + u32 flags; + } prdt[]; +}; + +/* command list */ +struct ahci_list_s { + u32 flags; + u32 bytes; + u32 base; + u32 baseu; + u32 res[4]; +}; + +struct ahci_fis_s { + u8 dsfis[0x1c]; /* dma setup */ + u8 res_1[0x04]; + u8 psfis[0x14]; /* pio setup */ + u8 res_2[0x0c]; + u8 rfis[0x14]; /* d2h register */ + u8 res_3[0x04]; + u8 sdbfis[0x08]; /* set device bits */ + u8 ufis[0x40]; /* unknown */ + u8 res_4[0x60]; +}; + +struct ahci_port_s { + struct drive_s drive; + struct ahci_ctrl_s *ctrl; + struct ahci_list_s *list; + struct ahci_fis_s *fis; + struct ahci_cmd_s *cmd; + u32 pnr; + u32 atapi; + char *desc; + int prio; +}; + +void ahci_setup(void); +int process_ahci_op(struct disk_op_s *op); +int ahci_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); + +#define AHCI_IRQ_ON_SG (1 << 31) +#define AHCI_CMD_ATAPI (1 << 5) +#define AHCI_CMD_WRITE (1 << 6) +#define AHCI_CMD_PREFETCH (1 << 7) +#define AHCI_CMD_RESET (1 << 8) +#define AHCI_CMD_CLR_BUSY (1 << 10) + +#define RX_FIS_D2H_REG 0x40 /* offset of D2H Register FIS data */ +#define RX_FIS_SDB 0x58 /* offset of SDB FIS data */ +#define RX_FIS_UNK 0x60 /* offset of Unknown FIS data */ + +/* global controller registers */ +#define HOST_CAP 0x00 /* host capabilities */ +#define HOST_CTL 0x04 /* global host control */ +#define HOST_IRQ_STAT 0x08 /* interrupt status */ +#define HOST_PORTS_IMPL 0x0c /* bitmap of implemented ports */ +#define HOST_VERSION 0x10 /* AHCI spec. version compliancy */ + +/* HOST_CTL bits */ +#define HOST_CTL_RESET (1 << 0) /* reset controller; self-clear */ +#define HOST_CTL_IRQ_EN (1 << 1) /* global IRQ enable */ +#define HOST_CTL_AHCI_EN (1 << 31) /* AHCI enabled */ + +/* HOST_CAP bits */ +#define HOST_CAP_SSC (1 << 14) /* Slumber capable */ +#define HOST_CAP_AHCI (1 << 18) /* AHCI only */ +#define HOST_CAP_CLO (1 << 24) /* Command List Override support */ +#define HOST_CAP_SSS (1 << 27) /* Staggered Spin-up */ +#define HOST_CAP_NCQ (1 << 30) /* Native Command Queueing */ +#define HOST_CAP_64 (1 << 31) /* PCI DAC (64-bit DMA) support */ + +/* registers for each SATA port */ +#define PORT_LST_ADDR 0x00 /* command list DMA addr */ +#define PORT_LST_ADDR_HI 0x04 /* command list DMA addr hi */ +#define PORT_FIS_ADDR 0x08 /* FIS rx buf addr */ +#define PORT_FIS_ADDR_HI 0x0c /* FIS rx buf addr hi */ +#define PORT_IRQ_STAT 0x10 /* interrupt status */ +#define PORT_IRQ_MASK 0x14 /* interrupt enable/disable mask */ +#define PORT_CMD 0x18 /* port command */ +#define PORT_TFDATA 0x20 /* taskfile data */ +#define PORT_SIG 0x24 /* device TF signature */ +#define PORT_SCR_STAT 0x28 /* SATA phy register: SStatus */ +#define PORT_SCR_CTL 0x2c /* SATA phy register: SControl */ +#define PORT_SCR_ERR 0x30 /* SATA phy register: SError */ +#define PORT_SCR_ACT 0x34 /* SATA phy register: SActive */ +#define PORT_CMD_ISSUE 0x38 /* command issue */ +#define PORT_RESERVED 0x3c /* reserved */ + +/* PORT_IRQ_{STAT,MASK} bits */ +#define PORT_IRQ_COLD_PRES (1 << 31) /* cold presence detect */ +#define PORT_IRQ_TF_ERR (1 << 30) /* task file error */ +#define PORT_IRQ_HBUS_ERR (1 << 29) /* host bus fatal error */ +#define PORT_IRQ_HBUS_DATA_ERR (1 << 28) /* host bus data error */ +#define PORT_IRQ_IF_ERR (1 << 27) /* interface fatal error */ +#define PORT_IRQ_IF_NONFATAL (1 << 26) /* interface non-fatal error */ +#define PORT_IRQ_OVERFLOW (1 << 24) /* xfer exhausted available S/G */ +#define PORT_IRQ_BAD_PMP (1 << 23) /* incorrect port multiplier */ + +#define PORT_IRQ_PHYRDY (1 << 22) /* PhyRdy changed */ +#define PORT_IRQ_DEV_ILCK (1 << 7) /* device interlock */ +#define PORT_IRQ_CONNECT (1 << 6) /* port connect change status */ +#define PORT_IRQ_SG_DONE (1 << 5) /* descriptor processed */ +#define PORT_IRQ_UNK_FIS (1 << 4) /* unknown FIS rx'd */ +#define PORT_IRQ_SDB_FIS (1 << 3) /* Set Device Bits FIS rx'd */ +#define PORT_IRQ_DMAS_FIS (1 << 2) /* DMA Setup FIS rx'd */ +#define PORT_IRQ_PIOS_FIS (1 << 1) /* PIO Setup FIS rx'd */ +#define PORT_IRQ_D2H_REG_FIS (1 << 0) /* D2H Register FIS rx'd */ + +#define PORT_IRQ_FREEZE (PORT_IRQ_HBUS_ERR | PORT_IRQ_IF_ERR | \ + PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY | \ + PORT_IRQ_UNK_FIS) +#define PORT_IRQ_ERROR (PORT_IRQ_FREEZE | PORT_IRQ_TF_ERR | \ + PORT_IRQ_HBUS_DATA_ERR) +#define DEF_PORT_IRQ (PORT_IRQ_ERROR | PORT_IRQ_SG_DONE | \ + PORT_IRQ_SDB_FIS | PORT_IRQ_DMAS_FIS | \ + PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS) + +/* PORT_CMD bits */ +#define PORT_CMD_ATAPI (1 << 24) /* Device is ATAPI */ +#define PORT_CMD_LIST_ON (1 << 15) /* cmd list DMA engine running */ +#define PORT_CMD_FIS_ON (1 << 14) /* FIS DMA engine running */ +#define PORT_CMD_FIS_RX (1 << 4) /* Enable FIS receive DMA engine */ +#define PORT_CMD_CLO (1 << 3) /* Command list override */ +#define PORT_CMD_POWER_ON (1 << 2) /* Power up device */ +#define PORT_CMD_SPIN_UP (1 << 1) /* Spin up device */ +#define PORT_CMD_START (1 << 0) /* Enable port DMA engine */ + +#define PORT_CMD_ICC_MASK (0xf << 28) /* i/f ICC state mask */ +#define PORT_CMD_ICC_ACTIVE (0x1 << 28) /* Put i/f in active state */ +#define PORT_CMD_ICC_PARTIAL (0x2 << 28) /* Put i/f in partial state */ +#define PORT_CMD_ICC_SLUMBER (0x6 << 28) /* Put i/f in slumber state */ + +#define PORT_IRQ_STAT_DHRS (1 << 0) /* Device to Host Register FIS */ +#define PORT_IRQ_STAT_PSS (1 << 1) /* PIO Setup FIS */ +#define PORT_IRQ_STAT_DSS (1 << 2) /* DMA Setup FIS */ +#define PORT_IRQ_STAT_SDBS (1 << 3) /* Set Device Bits */ +#define PORT_IRQ_STAT_UFS (1 << 4) /* Unknown FIS */ +#define PORT_IRQ_STAT_DPS (1 << 5) /* Descriptor Processed */ +#define PORT_IRQ_STAT_PCS (1 << 6) /* Port Connect Change Status */ +#define PORT_IRQ_STAT_DMPS (1 << 7) /* Device Mechanical Presence + Status */ +#define PORT_IRQ_STAT_PRCS (1 << 22) /* File Ready Status */ +#define PORT_IRQ_STAT_IPMS (1 << 23) /* Incorrect Port Multiplier + Status */ +#define PORT_IRQ_STAT_OFS (1 << 24) /* Overflow Status */ +#define PORT_IRQ_STAT_INFS (1 << 26) /* Interface Non-Fatal Error + Status */ +#define PORT_IRQ_STAT_IFS (1 << 27) /* Interface Fatal Error */ +#define PORT_IRQ_STAT_HBDS (1 << 28) /* Host Bus Data Error Status */ +#define PORT_IRQ_STAT_HBFS (1 << 29) /* Host Bus Fatal Error Status */ +#define PORT_IRQ_STAT_TFES (1 << 30) /* Task File Error Status */ +#define PORT_IRQ_STAT_CPDS (1 << 31) /* Code Port Detect Status */ + +#endif // ahci.h diff --git a/roms/seabios/src/hw/ata.c b/roms/seabios/src/hw/ata.c new file mode 100644 index 000000000..de2a9192e --- /dev/null +++ b/roms/seabios/src/hw/ata.c @@ -0,0 +1,1045 @@ +// Low level ATA disk access +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "ata.h" // ATA_CB_STAT +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // struct drive_s +#include "blockcmd.h" // CDB_CMD_READ_10 +#include "byteorder.h" // be16_to_cpu +#include "malloc.h" // malloc_fseg +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_CLASS_STORAGE_OTHER +#include "pci_regs.h" // PCI_INTERRUPT_LINE +#include "pic.h" // enable_hwirq +#include "stacks.h" // yield +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // timer_calc +#include "x86.h" // inb + +#define IDE_TIMEOUT 32000 //32 seconds max for IDE ops + + +/**************************************************************** + * Helper functions + ****************************************************************/ + +// Wait for the specified ide state +static inline int +await_ide(u8 mask, u8 flags, u16 base, u16 timeout) +{ + u32 end = timer_calc(timeout); + for (;;) { + u8 status = inb(base+ATA_CB_STAT); + if ((status & mask) == flags) + return status; + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } +} + +// Wait for the device to be not-busy. +static int +await_not_bsy(u16 base) +{ + return await_ide(ATA_CB_STAT_BSY, 0, base, IDE_TIMEOUT); +} + +// Wait for the device to be ready. +static int +await_rdy(u16 base) +{ + return await_ide(ATA_CB_STAT_RDY, ATA_CB_STAT_RDY, base, IDE_TIMEOUT); +} + +// Wait for ide state - pauses for one ata cycle first. +static inline int +pause_await_not_bsy(u16 iobase1, u16 iobase2) +{ + // Wait one PIO transfer cycle. + inb(iobase2 + ATA_CB_ASTAT); + + return await_not_bsy(iobase1); +} + +// Wait for ide state - pause for 400ns first. +static inline int +ndelay_await_not_bsy(u16 iobase1) +{ + ndelay(400); + return await_not_bsy(iobase1); +} + +// Reset a drive +static void +ata_reset(struct atadrive_s *adrive_gf) +{ + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u8 slave = GET_GLOBALFLAT(adrive_gf->slave); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); + + dprintf(6, "ata_reset drive=%p\n", &adrive_gf->drive); + // Pulse SRST + outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST, iobase2+ATA_CB_DC); + udelay(5); + outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2+ATA_CB_DC); + msleep(2); + + // wait for device to become not busy. + int status = await_not_bsy(iobase1); + if (status < 0) + goto done; + if (slave) { + // Change device. + u32 end = timer_calc(IDE_TIMEOUT); + for (;;) { + outb(ATA_CB_DH_DEV1, iobase1 + ATA_CB_DH); + status = ndelay_await_not_bsy(iobase1); + if (status < 0) + goto done; + if (inb(iobase1 + ATA_CB_DH) == ATA_CB_DH_DEV1) + break; + // Change drive request failed to take effect - retry. + if (timer_check(end)) { + warn_timeout(); + goto done; + } + } + } else { + // QEMU doesn't reset dh on reset, so set it explicitly. + outb(ATA_CB_DH_DEV0, iobase1 + ATA_CB_DH); + } + + // On a user-reset request, wait for RDY if it is an ATA device. + u8 type=GET_GLOBALFLAT(adrive_gf->drive.type); + if (type == DTYPE_ATA) + status = await_rdy(iobase1); + +done: + // Enable interrupts + outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC); + + dprintf(6, "ata_reset exit status=%x\n", status); +} + +// Check for drive RDY for 16bit interface command. +static int +isready(struct atadrive_s *adrive_gf) +{ + // Read the status from controller + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + u8 status = inb(iobase1 + ATA_CB_STAT); + if ((status & (ATA_CB_STAT_BSY|ATA_CB_STAT_RDY)) == ATA_CB_STAT_RDY) + return DISK_RET_SUCCESS; + return DISK_RET_ENOTREADY; +} + + +/**************************************************************** + * ATA send command + ****************************************************************/ + +struct ata_pio_command { + u8 feature; + u8 sector_count; + u8 lba_low; + u8 lba_mid; + u8 lba_high; + u8 device; + u8 command; + + u8 feature2; + u8 sector_count2; + u8 lba_low2; + u8 lba_mid2; + u8 lba_high2; +}; + +// Send an ata command to the drive. +static int +send_cmd(struct atadrive_s *adrive_gf, struct ata_pio_command *cmd) +{ + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u8 slave = GET_GLOBALFLAT(adrive_gf->slave); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + + // Select device + int status = await_not_bsy(iobase1); + if (status < 0) + return status; + u8 newdh = ((cmd->device & ~ATA_CB_DH_DEV1) + | (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0)); + u8 olddh = inb(iobase1 + ATA_CB_DH); + outb(newdh, iobase1 + ATA_CB_DH); + if ((olddh ^ newdh) & (1<<4)) { + // Was a device change - wait for device to become not busy. + status = ndelay_await_not_bsy(iobase1); + if (status < 0) + return status; + } + + // Check for ATA_CMD_(READ|WRITE)_(SECTORS|DMA)_EXT commands. + if ((cmd->command & ~0x11) == ATA_CMD_READ_SECTORS_EXT) { + outb(cmd->feature2, iobase1 + ATA_CB_FR); + outb(cmd->sector_count2, iobase1 + ATA_CB_SC); + outb(cmd->lba_low2, iobase1 + ATA_CB_SN); + outb(cmd->lba_mid2, iobase1 + ATA_CB_CL); + outb(cmd->lba_high2, iobase1 + ATA_CB_CH); + } + outb(cmd->feature, iobase1 + ATA_CB_FR); + outb(cmd->sector_count, iobase1 + ATA_CB_SC); + outb(cmd->lba_low, iobase1 + ATA_CB_SN); + outb(cmd->lba_mid, iobase1 + ATA_CB_CL); + outb(cmd->lba_high, iobase1 + ATA_CB_CH); + outb(cmd->command, iobase1 + ATA_CB_CMD); + + return 0; +} + +// Wait for data after calling 'send_cmd'. +static int +ata_wait_data(u16 iobase1) +{ + int status = ndelay_await_not_bsy(iobase1); + if (status < 0) + return status; + + if (status & ATA_CB_STAT_ERR) { + dprintf(6, "send_cmd : read error (status=%02x err=%02x)\n" + , status, inb(iobase1 + ATA_CB_ERR)); + return -4; + } + if (!(status & ATA_CB_STAT_DRQ)) { + dprintf(6, "send_cmd : DRQ not set (status %02x)\n", status); + return -5; + } + + return 0; +} + +// Send an ata command that does not transfer any further data. +int +ata_cmd_nondata(struct atadrive_s *adrive_gf, struct ata_pio_command *cmd) +{ + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); + + // Disable interrupts + outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2 + ATA_CB_DC); + + int ret = send_cmd(adrive_gf, cmd); + if (ret) + goto fail; + ret = ndelay_await_not_bsy(iobase1); + if (ret < 0) + goto fail; + + if (ret & ATA_CB_STAT_ERR) { + dprintf(6, "nondata cmd : read error (status=%02x err=%02x)\n" + , ret, inb(iobase1 + ATA_CB_ERR)); + ret = -4; + goto fail; + } + if (ret & ATA_CB_STAT_DRQ) { + dprintf(6, "nondata cmd : DRQ set (status %02x)\n", ret); + ret = -5; + goto fail; + } + +fail: + // Enable interrupts + outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC); + + return ret; +} + + +/**************************************************************** + * ATA PIO transfers + ****************************************************************/ + +// Transfer 'op->count' blocks (of 'blocksize' bytes) to/from drive +// 'op->drive_gf'. +static int +ata_pio_transfer(struct disk_op_s *op, int iswrite, int blocksize) +{ + dprintf(16, "ata_pio_transfer id=%p write=%d count=%d bs=%d buf=%p\n" + , op->drive_gf, iswrite, op->count, blocksize, op->buf_fl); + + struct atadrive_s *adrive_gf = container_of( + op->drive_gf, struct atadrive_s, drive); + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); + int count = op->count; + void *buf_fl = op->buf_fl; + int status; + for (;;) { + if (iswrite) { + // Write data to controller + dprintf(16, "Write sector id=%p dest=%p\n", op->drive_gf, buf_fl); + if (CONFIG_ATA_PIO32) + outsl_fl(iobase1, buf_fl, blocksize / 4); + else + outsw_fl(iobase1, buf_fl, blocksize / 2); + } else { + // Read data from controller + dprintf(16, "Read sector id=%p dest=%p\n", op->drive_gf, buf_fl); + if (CONFIG_ATA_PIO32) + insl_fl(iobase1, buf_fl, blocksize / 4); + else + insw_fl(iobase1, buf_fl, blocksize / 2); + } + buf_fl += blocksize; + + status = pause_await_not_bsy(iobase1, iobase2); + if (status < 0) { + // Error + op->count -= count; + return status; + } + + count--; + if (!count) + break; + status &= (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR); + if (status != ATA_CB_STAT_DRQ) { + dprintf(6, "ata_pio_transfer : more sectors left (status %02x)\n" + , status); + op->count -= count; + return -6; + } + } + + status &= (ATA_CB_STAT_BSY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ + | ATA_CB_STAT_ERR); + if (!iswrite) + status &= ~ATA_CB_STAT_DF; + if (status != 0) { + dprintf(6, "ata_pio_transfer : no sectors left (status %02x)\n", status); + return -7; + } + + return 0; +} + + +/**************************************************************** + * ATA DMA transfers + ****************************************************************/ + +#define BM_CMD 0 +#define BM_CMD_MEMWRITE 0x08 +#define BM_CMD_START 0x01 +#define BM_STATUS 2 +#define BM_STATUS_IRQ 0x04 +#define BM_STATUS_ERROR 0x02 +#define BM_STATUS_ACTIVE 0x01 +#define BM_TABLE 4 + +struct sff_dma_prd { + u32 buf_fl; + u32 count; +}; + +// Check if DMA available and setup transfer if so. +static int +ata_try_dma(struct disk_op_s *op, int iswrite, int blocksize) +{ + ASSERT16(); + if (! CONFIG_ATA_DMA) + return -1; + u32 dest = (u32)op->buf_fl; + if (dest & 1) + // Need minimum alignment of 1. + return -1; + struct atadrive_s *adrive_gf = container_of( + op->drive_gf, struct atadrive_s, drive); + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u16 iomaster = GET_GLOBALFLAT(chan_gf->iomaster); + if (! iomaster) + return -1; + u32 bytes = op->count * blocksize; + if (! bytes) + return -1; + + // Build PRD dma structure. + struct sff_dma_prd *dma = MAKE_FLATPTR(SEG_LOW, ExtraStack); + struct sff_dma_prd *origdma = dma; + while (bytes) { + if (dma >= &origdma[16]) + // Too many descriptors.. + return -1; + u32 count = bytes; + u32 max = 0x10000 - (dest & 0xffff); + if (count > max) + count = max; + + SET_LOWFLAT(dma->buf_fl, dest); + bytes -= count; + if (!bytes) + // Last descriptor. + count |= 1<<31; + dprintf(16, "dma@%p: %08x %08x\n", dma, dest, count); + dest += count; + SET_LOWFLAT(dma->count, count); + dma++; + } + + // Program bus-master controller. + outl((u32)origdma, iomaster + BM_TABLE); + u8 oldcmd = inb(iomaster + BM_CMD) & ~(BM_CMD_MEMWRITE|BM_CMD_START); + outb(oldcmd | (iswrite ? 0x00 : BM_CMD_MEMWRITE), iomaster + BM_CMD); + outb(BM_STATUS_ERROR|BM_STATUS_IRQ, iomaster + BM_STATUS); + + return 0; +} + +// Transfer data using DMA. +static int +ata_dma_transfer(struct disk_op_s *op) +{ + if (! CONFIG_ATA_DMA) + return -1; + dprintf(16, "ata_dma_transfer id=%p buf=%p\n", op->drive_gf, op->buf_fl); + + struct atadrive_s *adrive_gf = container_of( + op->drive_gf, struct atadrive_s, drive); + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u16 iomaster = GET_GLOBALFLAT(chan_gf->iomaster); + + // Start bus-master controller. + u8 oldcmd = inb(iomaster + BM_CMD); + outb(oldcmd | BM_CMD_START, iomaster + BM_CMD); + + u32 end = timer_calc(IDE_TIMEOUT); + u8 status; + for (;;) { + status = inb(iomaster + BM_STATUS); + if (status & BM_STATUS_IRQ) + break; + // Transfer in progress + if (timer_check(end)) { + // Timeout. + warn_timeout(); + break; + } + yield(); + } + outb(oldcmd & ~BM_CMD_START, iomaster + BM_CMD); + + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); + int idestatus = pause_await_not_bsy(iobase1, iobase2); + + if ((status & (BM_STATUS_IRQ|BM_STATUS_ACTIVE)) == BM_STATUS_IRQ + && idestatus >= 0x00 + && (idestatus & (ATA_CB_STAT_BSY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ + | ATA_CB_STAT_ERR)) == 0x00) + // Success. + return 0; + + dprintf(6, "IDE DMA error (dma=%x ide=%x/%x/%x)\n", status, idestatus + , inb(iobase2 + ATA_CB_ASTAT), inb(iobase1 + ATA_CB_ERR)); + op->count = 0; + return -1; +} + + +/**************************************************************** + * ATA hard drive functions + ****************************************************************/ + +// Transfer data to harddrive using PIO protocol. +static int +ata_pio_cmd_data(struct disk_op_s *op, int iswrite, struct ata_pio_command *cmd) +{ + struct atadrive_s *adrive_gf = container_of( + op->drive_gf, struct atadrive_s, drive); + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); + + // Disable interrupts + outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2 + ATA_CB_DC); + + int ret = send_cmd(adrive_gf, cmd); + if (ret) + goto fail; + ret = ata_wait_data(iobase1); + if (ret) + goto fail; + ret = ata_pio_transfer(op, iswrite, DISK_SECTOR_SIZE); + +fail: + // Enable interrupts + outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC); + return ret; +} + +// Transfer data to harddrive using DMA protocol. +static int +ata_dma_cmd_data(struct disk_op_s *op, struct ata_pio_command *cmd) +{ + if (! CONFIG_ATA_DMA) + return -1; + struct atadrive_s *adrive_gf = container_of( + op->drive_gf, struct atadrive_s, drive); + int ret = send_cmd(adrive_gf, cmd); + if (ret) + return ret; + return ata_dma_transfer(op); +} + +// Read/write count blocks from a harddrive. +static int +ata_readwrite(struct disk_op_s *op, int iswrite) +{ + u64 lba = op->lba; + + int usepio = ata_try_dma(op, iswrite, DISK_SECTOR_SIZE); + + struct ata_pio_command cmd; + memset(&cmd, 0, sizeof(cmd)); + + if (op->count >= (1<<8) || lba + op->count >= (1<<28)) { + cmd.sector_count2 = op->count >> 8; + cmd.lba_low2 = lba >> 24; + cmd.lba_mid2 = lba >> 32; + cmd.lba_high2 = lba >> 40; + lba &= 0xffffff; + + if (usepio) + cmd.command = (iswrite ? ATA_CMD_WRITE_SECTORS_EXT + : ATA_CMD_READ_SECTORS_EXT); + else + cmd.command = (iswrite ? ATA_CMD_WRITE_DMA_EXT + : ATA_CMD_READ_DMA_EXT); + } else { + if (usepio) + cmd.command = (iswrite ? ATA_CMD_WRITE_SECTORS + : ATA_CMD_READ_SECTORS); + else + cmd.command = (iswrite ? ATA_CMD_WRITE_DMA + : ATA_CMD_READ_DMA); + } + + cmd.sector_count = op->count; + cmd.lba_low = lba; + cmd.lba_mid = lba >> 8; + cmd.lba_high = lba >> 16; + cmd.device = ((lba >> 24) & 0xf) | ATA_CB_DH_LBA; + + int ret; + if (usepio) + ret = ata_pio_cmd_data(op, iswrite, &cmd); + else + ret = ata_dma_cmd_data(op, &cmd); + if (ret) + return DISK_RET_EBADTRACK; + return DISK_RET_SUCCESS; +} + +// 16bit command demuxer for ATA harddrives. +int +process_ata_op(struct disk_op_s *op) +{ + if (!CONFIG_ATA) + return 0; + + struct atadrive_s *adrive_gf = container_of( + op->drive_gf, struct atadrive_s, drive); + switch (op->command) { + case CMD_READ: + return ata_readwrite(op, 0); + case CMD_WRITE: + return ata_readwrite(op, 1); + case CMD_RESET: + ata_reset(adrive_gf); + return DISK_RET_SUCCESS; + case CMD_ISREADY: + return isready(adrive_gf); + case CMD_FORMAT: + case CMD_VERIFY: + case CMD_SEEK: + return DISK_RET_SUCCESS; + default: + op->count = 0; + return DISK_RET_EPARAM; + } +} + + +/**************************************************************** + * ATAPI functions + ****************************************************************/ + +#define CDROM_CDB_SIZE 12 + +// Low-level atapi command transmit function. +int +atapi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + if (! CONFIG_ATA) + return 0; + + struct atadrive_s *adrive_gf = container_of( + op->drive_gf, struct atadrive_s, drive); + struct ata_channel_s *chan_gf = GET_GLOBALFLAT(adrive_gf->chan_gf); + u16 iobase1 = GET_GLOBALFLAT(chan_gf->iobase1); + u16 iobase2 = GET_GLOBALFLAT(chan_gf->iobase2); + + struct ata_pio_command cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.lba_mid = blocksize; + cmd.lba_high = blocksize >> 8; + cmd.command = ATA_CMD_PACKET; + + // Disable interrupts + outb(ATA_CB_DC_HD15 | ATA_CB_DC_NIEN, iobase2 + ATA_CB_DC); + + int ret = send_cmd(adrive_gf, &cmd); + if (ret) + goto fail; + ret = ata_wait_data(iobase1); + if (ret) + goto fail; + + // Send command to device + outsw_fl(iobase1, MAKE_FLATPTR(GET_SEG(SS), cdbcmd), CDROM_CDB_SIZE / 2); + + int status = pause_await_not_bsy(iobase1, iobase2); + if (status < 0) { + ret = status; + goto fail; + } + + if (status & ATA_CB_STAT_ERR) { + u8 err = inb(iobase1 + ATA_CB_ERR); + // skip "Not Ready" + if (err != 0x20) + dprintf(6, "send_atapi_cmd : read error (status=%02x err=%02x)\n" + , status, err); + ret = -2; + goto fail; + } + if (blocksize) { + if (!(status & ATA_CB_STAT_DRQ)) { + dprintf(6, "send_atapi_cmd : DRQ not set (status %02x)\n", status); + ret = -3; + goto fail; + } + + ret = ata_pio_transfer(op, 0, blocksize); + } + +fail: + // Enable interrupts + outb(ATA_CB_DC_HD15, iobase2+ATA_CB_DC); + if (ret) + return DISK_RET_EBADTRACK; + return DISK_RET_SUCCESS; +} + + +/**************************************************************** + * ATA detect and init + ****************************************************************/ + +// Send an identify device or identify device packet command. +static int +send_ata_identity(struct atadrive_s *adrive, u16 *buffer, int command) +{ + memset(buffer, 0, DISK_SECTOR_SIZE); + + struct disk_op_s dop; + memset(&dop, 0, sizeof(dop)); + dop.drive_gf = &adrive->drive; + dop.count = 1; + dop.lba = 1; + dop.buf_fl = MAKE_FLATPTR(GET_SEG(SS), buffer); + + struct ata_pio_command cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = command; + + return ata_pio_cmd_data(&dop, 0, &cmd); +} + +// Extract the ATA/ATAPI version info. +int +ata_extract_version(u16 *buffer) +{ + // Extract ATA/ATAPI version. + u16 ataversion = buffer[80]; + u8 version; + for (version=15; version>0; version--) + if (ataversion & (1<<version)) + break; + return version; +} + +#define MAXMODEL 40 + +// Extract the ATA/ATAPI model info. +char * +ata_extract_model(char *model, u32 size, u16 *buffer) +{ + // Read model name + int i; + for (i=0; i<size/2; i++) + *(u16*)&model[i*2] = be16_to_cpu(buffer[27+i]); + model[size] = 0x00; + nullTrailingSpace(model); + return model; +} + +// Common init code between ata and atapi +static struct atadrive_s * +init_atadrive(struct atadrive_s *dummy, u16 *buffer) +{ + struct atadrive_s *adrive = malloc_fseg(sizeof(*adrive)); + if (!adrive) { + warn_noalloc(); + return NULL; + } + memset(adrive, 0, sizeof(*adrive)); + adrive->chan_gf = dummy->chan_gf; + adrive->slave = dummy->slave; + adrive->drive.cntl_id = adrive->chan_gf->chanid * 2 + dummy->slave; + adrive->drive.removable = (buffer[0] & 0x80) ? 1 : 0; + return adrive; +} + +// Detect if the given drive is an atapi - initialize it if so. +static struct atadrive_s * +init_drive_atapi(struct atadrive_s *dummy, u16 *buffer) +{ + // Send an IDENTIFY_DEVICE_PACKET command to device + int ret = send_ata_identity(dummy, buffer, ATA_CMD_IDENTIFY_PACKET_DEVICE); + if (ret) + return NULL; + + // Success - setup as ATAPI. + struct atadrive_s *adrive = init_atadrive(dummy, buffer); + if (!adrive) + return NULL; + adrive->drive.type = DTYPE_ATA_ATAPI; + adrive->drive.blksize = CDROM_SECTOR_SIZE; + adrive->drive.sectors = (u64)-1; + u8 iscd = ((buffer[0] >> 8) & 0x1f) == 0x05; + char model[MAXMODEL+1]; + char *desc = znprintf(MAXDESCSIZE + , "DVD/CD [ata%d-%d: %s ATAPI-%d %s]" + , adrive->chan_gf->chanid, adrive->slave + , ata_extract_model(model, MAXMODEL, buffer) + , ata_extract_version(buffer) + , (iscd ? "DVD/CD" : "Device")); + dprintf(1, "%s\n", desc); + + // fill cdidmap + if (iscd) { + int prio = bootprio_find_ata_device(adrive->chan_gf->pci_tmp, + adrive->chan_gf->chanid, + adrive->slave); + boot_add_cd(&adrive->drive, desc, prio); + } + + return adrive; +} + +// Detect if the given drive is a regular ata drive - initialize it if so. +static struct atadrive_s * +init_drive_ata(struct atadrive_s *dummy, u16 *buffer) +{ + // Send an IDENTIFY_DEVICE command to device + int ret = send_ata_identity(dummy, buffer, ATA_CMD_IDENTIFY_DEVICE); + if (ret) + return NULL; + + // Success - setup as ATA. + struct atadrive_s *adrive = init_atadrive(dummy, buffer); + if (!adrive) + return NULL; + adrive->drive.type = DTYPE_ATA; + adrive->drive.blksize = DISK_SECTOR_SIZE; + + adrive->drive.pchs.cylinder = buffer[1]; + adrive->drive.pchs.head = buffer[3]; + adrive->drive.pchs.sector = buffer[6]; + + u64 sectors; + if (buffer[83] & (1 << 10)) // word 83 - lba48 support + sectors = *(u64*)&buffer[100]; // word 100-103 + else + sectors = *(u32*)&buffer[60]; // word 60 and word 61 + adrive->drive.sectors = sectors; + u64 adjsize = sectors >> 11; + char adjprefix = 'M'; + if (adjsize >= (1 << 16)) { + adjsize >>= 10; + adjprefix = 'G'; + } + char model[MAXMODEL+1]; + char *desc = znprintf(MAXDESCSIZE + , "ata%d-%d: %s ATA-%d Hard-Disk (%u %ciBytes)" + , adrive->chan_gf->chanid, adrive->slave + , ata_extract_model(model, MAXMODEL, buffer) + , ata_extract_version(buffer) + , (u32)adjsize, adjprefix); + dprintf(1, "%s\n", desc); + + int prio = bootprio_find_ata_device(adrive->chan_gf->pci_tmp, + adrive->chan_gf->chanid, + adrive->slave); + // Register with bcv system. + boot_add_hd(&adrive->drive, desc, prio); + + return adrive; +} + +static u32 SpinupEnd; + +// Wait for non-busy status and check for "floating bus" condition. +static int +powerup_await_non_bsy(u16 base) +{ + u8 orstatus = 0; + u8 status; + for (;;) { + status = inb(base+ATA_CB_STAT); + if (!(status & ATA_CB_STAT_BSY)) + break; + orstatus |= status; + if (orstatus == 0xff) { + dprintf(4, "powerup IDE floating\n"); + return orstatus; + } + if (timer_check(SpinupEnd)) { + warn_timeout(); + return -1; + } + yield(); + } + dprintf(6, "powerup iobase=%x st=%x\n", base, status); + return status; +} + +// Detect any drives attached to a given controller. +static void +ata_detect(void *data) +{ + struct ata_channel_s *chan_gf = data; + struct atadrive_s dummy; + memset(&dummy, 0, sizeof(dummy)); + dummy.chan_gf = chan_gf; + // Device detection + int didreset = 0; + u8 slave; + for (slave=0; slave<=1; slave++) { + // Wait for not-bsy. + u16 iobase1 = chan_gf->iobase1; + int status = powerup_await_non_bsy(iobase1); + if (status < 0) + continue; + u8 newdh = slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0; + outb(newdh, iobase1+ATA_CB_DH); + ndelay(400); + status = powerup_await_non_bsy(iobase1); + if (status < 0) + continue; + + // Check if ioport registers look valid. + outb(newdh, iobase1+ATA_CB_DH); + u8 dh = inb(iobase1+ATA_CB_DH); + outb(0x55, iobase1+ATA_CB_SC); + outb(0xaa, iobase1+ATA_CB_SN); + u8 sc = inb(iobase1+ATA_CB_SC); + u8 sn = inb(iobase1+ATA_CB_SN); + dprintf(6, "ata_detect ata%d-%d: sc=%x sn=%x dh=%x\n" + , chan_gf->chanid, slave, sc, sn, dh); + if (sc != 0x55 || sn != 0xaa || dh != newdh) + continue; + + // Prepare new drive. + dummy.slave = slave; + + // reset the channel + if (!didreset) { + ata_reset(&dummy); + didreset = 1; + } + + // check for ATAPI + u16 buffer[256]; + struct atadrive_s *adrive = init_drive_atapi(&dummy, buffer); + if (!adrive) { + // Didn't find an ATAPI drive - look for ATA drive. + u8 st = inb(iobase1+ATA_CB_STAT); + if (!st) + // Status not set - can't be a valid drive. + continue; + + // Wait for RDY. + int ret = await_rdy(iobase1); + if (ret < 0) + continue; + + // check for ATA. + adrive = init_drive_ata(&dummy, buffer); + if (!adrive) + // No ATA drive found + continue; + } + + u16 resetresult = buffer[93]; + dprintf(6, "ata_detect resetresult=%04x\n", resetresult); + if (!slave && (resetresult & 0xdf61) == 0x4041) + // resetresult looks valid and device 0 is responding to + // device 1 requests - device 1 must not be present - skip + // detection. + break; + } +} + +// Initialize an ata controller and detect its drives. +static void +init_controller(struct pci_device *pci, int irq + , u32 port1, u32 port2, u32 master) +{ + static int chanid = 0; + struct ata_channel_s *chan_gf = malloc_fseg(sizeof(*chan_gf)); + if (!chan_gf) { + warn_noalloc(); + return; + } + chan_gf->chanid = chanid++; + chan_gf->irq = irq; + chan_gf->pci_bdf = pci ? pci->bdf : -1; + chan_gf->pci_tmp = pci; + chan_gf->iobase1 = port1; + chan_gf->iobase2 = port2; + chan_gf->iomaster = master; + dprintf(1, "ATA controller %d at %x/%x/%x (irq %d dev %x)\n" + , chanid, port1, port2, master, irq, chan_gf->pci_bdf); + run_thread(ata_detect, chan_gf); +} + +#define IRQ_ATA1 14 +#define IRQ_ATA2 15 + +// Handle controllers on an ATA PCI device. +static void +init_pciata(struct pci_device *pci, u8 prog_if) +{ + pci->have_driver = 1; + u16 bdf = pci->bdf; + u8 pciirq = pci_config_readb(bdf, PCI_INTERRUPT_LINE); + int master = 0; + if (CONFIG_ATA_DMA && prog_if & 0x80) { + // Check for bus-mastering. + u32 bar = pci_config_readl(bdf, PCI_BASE_ADDRESS_4); + if (bar & PCI_BASE_ADDRESS_SPACE_IO) { + master = bar & PCI_BASE_ADDRESS_IO_MASK; + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + } + } + + u32 port1, port2, irq; + if (prog_if & 1) { + port1 = (pci_config_readl(bdf, PCI_BASE_ADDRESS_0) + & PCI_BASE_ADDRESS_IO_MASK); + port2 = (pci_config_readl(bdf, PCI_BASE_ADDRESS_1) + & PCI_BASE_ADDRESS_IO_MASK); + irq = pciirq; + } else { + port1 = PORT_ATA1_CMD_BASE; + port2 = PORT_ATA1_CTRL_BASE; + irq = IRQ_ATA1; + } + init_controller(pci, irq, port1, port2, master); + + if (prog_if & 4) { + port1 = (pci_config_readl(bdf, PCI_BASE_ADDRESS_2) + & PCI_BASE_ADDRESS_IO_MASK); + port2 = (pci_config_readl(bdf, PCI_BASE_ADDRESS_3) + & PCI_BASE_ADDRESS_IO_MASK); + irq = pciirq; + } else { + port1 = PORT_ATA2_CMD_BASE; + port2 = PORT_ATA2_CTRL_BASE; + irq = IRQ_ATA2; + } + init_controller(pci, irq, port1, port2, master ? master + 8 : 0); +} + +static void +found_genericata(struct pci_device *pci, void *arg) +{ + init_pciata(pci, pci->prog_if); +} + +static void +found_compatibleahci(struct pci_device *pci, void *arg) +{ + if (CONFIG_AHCI) + // Already handled directly via native ahci interface. + return; + init_pciata(pci, 0x8f); +} + +static const struct pci_device_id pci_ata_tbl[] = { + PCI_DEVICE_CLASS(PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE + , found_genericata), + PCI_DEVICE(PCI_VENDOR_ID_ATI, 0x4391, found_compatibleahci), + PCI_DEVICE_END, +}; + +// Locate and init ata controllers. +static void +ata_scan(void) +{ + if (CONFIG_QEMU && hlist_empty(&PCIDevices)) { + // No PCI devices found - probably a QEMU "-M isapc" machine. + // Try using ISA ports for ATA controllers. + init_controller(NULL, IRQ_ATA1 + , PORT_ATA1_CMD_BASE, PORT_ATA1_CTRL_BASE, 0); + init_controller(NULL, IRQ_ATA2 + , PORT_ATA2_CMD_BASE, PORT_ATA2_CTRL_BASE, 0); + return; + } + + // Scan PCI bus for ATA adapters + struct pci_device *pci; + foreachpci(pci) { + pci_init_device(pci_ata_tbl, pci, NULL); + } +} + +void +ata_setup(void) +{ + ASSERT32FLAT(); + if (!CONFIG_ATA) + return; + + dprintf(3, "init hard drives\n"); + + SpinupEnd = timer_calc(IDE_TIMEOUT); + ata_scan(); + + SET_BDA(disk_control_byte, 0xc0); + + enable_hwirq(14, FUNC16(entry_76)); +} diff --git a/roms/seabios/src/hw/ata.h b/roms/seabios/src/hw/ata.h new file mode 100644 index 000000000..c73892bbe --- /dev/null +++ b/roms/seabios/src/hw/ata.h @@ -0,0 +1,158 @@ +#ifndef __ATA_H +#define __ATA_H + +#include "block.h" // struct drive_s +#include "config.h" // CONFIG_MAX_ATA_INTERFACES +#include "types.h" // u8 + +struct ata_channel_s { + u16 iobase1; + u16 iobase2; + u16 iomaster; + u8 irq; + u8 chanid; + int pci_bdf; + struct pci_device *pci_tmp; +}; + +struct atadrive_s { + struct drive_s drive; + struct ata_channel_s *chan_gf; + u8 slave; +}; + +// ata.c +char *ata_extract_model(char *model, u32 size, u16 *buffer); +int ata_extract_version(u16 *buffer); +int cdrom_read(struct disk_op_s *op); +int atapi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +void ata_setup(void); +int process_ata_op(struct disk_op_s *op); + +#define PORT_ATA2_CMD_BASE 0x0170 +#define PORT_ATA1_CMD_BASE 0x01f0 +#define PORT_ATA2_CTRL_BASE 0x0374 +#define PORT_ATA1_CTRL_BASE 0x03f4 + +// Global defines -- ATA register and register bits. +// command block & control block regs +#define ATA_CB_DATA 0 // data reg in/out pio_base_addr1+0 +#define ATA_CB_ERR 1 // error in pio_base_addr1+1 +#define ATA_CB_FR 1 // feature reg out pio_base_addr1+1 +#define ATA_CB_SC 2 // sector count in/out pio_base_addr1+2 +#define ATA_CB_SN 3 // sector number in/out pio_base_addr1+3 +#define ATA_CB_CL 4 // cylinder low in/out pio_base_addr1+4 +#define ATA_CB_CH 5 // cylinder high in/out pio_base_addr1+5 +#define ATA_CB_DH 6 // device head in/out pio_base_addr1+6 +#define ATA_CB_STAT 7 // primary status in pio_base_addr1+7 +#define ATA_CB_CMD 7 // command out pio_base_addr1+7 + +#define ATA_CB_ASTAT 2 // alternate status in pio_base_addr2+2 +#define ATA_CB_DC 2 // device control out pio_base_addr2+2 +#define ATA_CB_DA 3 // device address in pio_base_addr2+3 + +#define ATA_CB_ER_ICRC 0x80 // ATA Ultra DMA bad CRC +#define ATA_CB_ER_BBK 0x80 // ATA bad block +#define ATA_CB_ER_UNC 0x40 // ATA uncorrected error +#define ATA_CB_ER_MC 0x20 // ATA media change +#define ATA_CB_ER_IDNF 0x10 // ATA id not found +#define ATA_CB_ER_MCR 0x08 // ATA media change request +#define ATA_CB_ER_ABRT 0x04 // ATA command aborted +#define ATA_CB_ER_NTK0 0x02 // ATA track 0 not found +#define ATA_CB_ER_NDAM 0x01 // ATA address mark not found + +#define ATA_CB_ER_P_SNSKEY 0xf0 // ATAPI sense key (mask) +#define ATA_CB_ER_P_MCR 0x08 // ATAPI Media Change Request +#define ATA_CB_ER_P_ABRT 0x04 // ATAPI command abort +#define ATA_CB_ER_P_EOM 0x02 // ATAPI End of Media +#define ATA_CB_ER_P_ILI 0x01 // ATAPI Illegal Length Indication + +// ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC) +#define ATA_CB_SC_P_TAG 0xf8 // ATAPI tag (mask) +#define ATA_CB_SC_P_REL 0x04 // ATAPI release +#define ATA_CB_SC_P_IO 0x02 // ATAPI I/O +#define ATA_CB_SC_P_CD 0x01 // ATAPI C/D + +// bits 7-4 of the device/head (CB_DH) reg +#define ATA_CB_DH_DEV0 0xa0 // select device 0 +#define ATA_CB_DH_DEV1 0xb0 // select device 1 +#define ATA_CB_DH_LBA 0x40 // use LBA + +// status reg (CB_STAT and CB_ASTAT) bits +#define ATA_CB_STAT_BSY 0x80 // busy +#define ATA_CB_STAT_RDY 0x40 // ready +#define ATA_CB_STAT_DF 0x20 // device fault +#define ATA_CB_STAT_WFT 0x20 // write fault (old name) +#define ATA_CB_STAT_SKC 0x10 // seek complete +#define ATA_CB_STAT_SERV 0x10 // service +#define ATA_CB_STAT_DRQ 0x08 // data request +#define ATA_CB_STAT_CORR 0x04 // corrected +#define ATA_CB_STAT_IDX 0x02 // index +#define ATA_CB_STAT_ERR 0x01 // error (ATA) +#define ATA_CB_STAT_CHK 0x01 // check (ATAPI) + +// device control reg (CB_DC) bits +#define ATA_CB_DC_HD15 0x08 // bit should always be set to one +#define ATA_CB_DC_SRST 0x04 // soft reset +#define ATA_CB_DC_NIEN 0x02 // disable interrupts + +// Most mandtory and optional ATA commands (from ATA-3), +#define ATA_CMD_NOP 0x00 +#define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03 +#define ATA_CMD_DEVICE_RESET 0x08 +#define ATA_CMD_RECALIBRATE 0x10 +#define ATA_CMD_READ_SECTORS 0x20 +#define ATA_CMD_READ_SECTORS_EXT 0x24 +#define ATA_CMD_READ_DMA_EXT 0x25 +#define ATA_CMD_READ_DMA_QUEUED_EXT 0x26 +#define ATA_CMD_READ_NATIVE_MAX_ADDRESS_EXT 0x27 +#define ATA_CMD_READ_MULTIPLE_EXT 0x29 +#define ATA_CMD_READ_LOG_EXT 0x2F +#define ATA_CMD_WRITE_SECTORS 0x30 +#define ATA_CMD_WRITE_SECTORS_EXT 0x34 +#define ATA_CMD_WRITE_DMA_EXT 0x35 +#define ATA_CMD_WRITE_DMA_QUEUED_EXT 0x36 +#define ATA_CMD_SET_MAX_ADDRESS_EXT 0x37 +#define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38 +#define ATA_CMD_WRITE_MULTIPLE_EXT 0x39 +#define ATA_CMD_WRITE_VERIFY 0x3C +#define ATA_CMD_WRITE_LOG_EXT 0x3F +#define ATA_CMD_READ_VERIFY_SECTORS 0x40 +#define ATA_CMD_READ_VERIFY_SECTORS_EXT 0x42 +#define ATA_CMD_FORMAT_TRACK 0x50 +#define ATA_CMD_SEEK 0x70 +#define ATA_CMD_CFA_TRANSLATE_SECTOR 0x87 +#define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90 +#define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91 +#define ATA_CMD_STANDBY_IMMEDIATE2 0x94 +#define ATA_CMD_IDLE_IMMEDIATE2 0x95 +#define ATA_CMD_STANDBY2 0x96 +#define ATA_CMD_IDLE2 0x97 +#define ATA_CMD_CHECK_POWER_MODE2 0x98 +#define ATA_CMD_SLEEP2 0x99 +#define ATA_CMD_PACKET 0xA0 +#define ATA_CMD_IDENTIFY_PACKET_DEVICE 0xA1 +#define ATA_CMD_CFA_ERASE_SECTORS 0xC0 +#define ATA_CMD_READ_MULTIPLE 0xC4 +#define ATA_CMD_WRITE_MULTIPLE 0xC5 +#define ATA_CMD_SET_MULTIPLE_MODE 0xC6 +#define ATA_CMD_READ_DMA_QUEUED 0xC7 +#define ATA_CMD_READ_DMA 0xC8 +#define ATA_CMD_WRITE_DMA 0xCA +#define ATA_CMD_WRITE_DMA_QUEUED 0xCC +#define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD +#define ATA_CMD_STANDBY_IMMEDIATE 0xE0 +#define ATA_CMD_IDLE_IMMEDIATE 0xE1 +#define ATA_CMD_STANDBY 0xE2 +#define ATA_CMD_IDLE 0xE3 +#define ATA_CMD_READ_BUFFER 0xE4 +#define ATA_CMD_CHECK_POWER_MODE 0xE5 +#define ATA_CMD_SLEEP 0xE6 +#define ATA_CMD_FLUSH_CACHE 0xE7 +#define ATA_CMD_WRITE_BUFFER 0xE8 +#define ATA_CMD_IDENTIFY_DEVICE 0xEC +#define ATA_CMD_SET_FEATURES 0xEF +#define ATA_CMD_READ_NATIVE_MAX_ADDRESS 0xF8 +#define ATA_CMD_SET_MAX 0xF9 + +#endif // ata.h diff --git a/roms/seabios/src/hw/blockcmd.c b/roms/seabios/src/hw/blockcmd.c new file mode 100644 index 000000000..96950f2d7 --- /dev/null +++ b/roms/seabios/src/hw/blockcmd.c @@ -0,0 +1,284 @@ +// Support for several common scsi like command data block requests +// +// Copyright (C) 2010 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "ahci.h" // atapi_cmd_data +#include "ata.h" // atapi_cmd_data +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // struct disk_op_s +#include "blockcmd.h" // struct cdb_request_sense +#include "byteorder.h" // be32_to_cpu +#include "esp-scsi.h" // esp_scsi_cmd_data +#include "lsi-scsi.h" // lsi_scsi_cmd_data +#include "megasas.h" // megasas_cmd_data +#include "pvscsi.h" // pvscsi_cmd_data +#include "output.h" // dprintf +#include "std/disk.h" // DISK_RET_EPARAM +#include "string.h" // memset +#include "usb-msc.h" // usb_cmd_data +#include "usb-uas.h" // usb_cmd_data +#include "util.h" // timer_calc +#include "virtio-scsi.h" // virtio_scsi_cmd_data + +// Route command to low-level handler. +static int +cdb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + u8 type = GET_GLOBALFLAT(op->drive_gf->type); + switch (type) { + case DTYPE_ATA_ATAPI: + return atapi_cmd_data(op, cdbcmd, blocksize); + case DTYPE_USB: + return usb_cmd_data(op, cdbcmd, blocksize); + case DTYPE_UAS: + return uas_cmd_data(op, cdbcmd, blocksize); + case DTYPE_VIRTIO_SCSI: + return virtio_scsi_cmd_data(op, cdbcmd, blocksize); + case DTYPE_LSI_SCSI: + return lsi_scsi_cmd_data(op, cdbcmd, blocksize); + case DTYPE_ESP_SCSI: + return esp_scsi_cmd_data(op, cdbcmd, blocksize); + case DTYPE_MEGASAS: + return megasas_cmd_data(op, cdbcmd, blocksize); + case DTYPE_PVSCSI: + return pvscsi_cmd_data(op, cdbcmd, blocksize); + case DTYPE_AHCI_ATAPI: + if (!MODESEGMENT) + return ahci_cmd_data(op, cdbcmd, blocksize); + default: + op->count = 0; + return DISK_RET_EPARAM; + } +} + +// Determine if the command is a request to pull data from the device +int +cdb_is_read(u8 *cdbcmd, u16 blocksize) +{ + return blocksize && cdbcmd[0] != CDB_CMD_WRITE_10; +} + +int +scsi_is_ready(struct disk_op_s *op) +{ + dprintf(6, "scsi_is_ready (drive=%p)\n", op->drive_gf); + + /* Retry TEST UNIT READY for 5 seconds unless MEDIUM NOT PRESENT is + * reported by the device. If the device reports "IN PROGRESS", + * 30 seconds is added. */ + int in_progress = 0; + u32 end = timer_calc(5000); + for (;;) { + if (timer_check(end)) { + dprintf(1, "test unit ready failed\n"); + return -1; + } + + int ret = cdb_test_unit_ready(op); + if (!ret) + // Success + break; + + struct cdbres_request_sense sense; + ret = cdb_get_sense(op, &sense); + if (ret) + // Error - retry. + continue; + + // Sense succeeded. + if (sense.asc == 0x3a) { /* MEDIUM NOT PRESENT */ + dprintf(1, "Device reports MEDIUM NOT PRESENT\n"); + return -1; + } + + if (sense.asc == 0x04 && sense.ascq == 0x01 && !in_progress) { + /* IN PROGRESS OF BECOMING READY */ + printf("Waiting for device to detect medium... "); + /* Allow 30 seconds more */ + end = timer_calc(30000); + in_progress = 1; + } + } + return 0; +} + +// Validate drive, find block size / sector count, and register drive. +int +scsi_drive_setup(struct drive_s *drive, const char *s, int prio) +{ + struct disk_op_s dop; + memset(&dop, 0, sizeof(dop)); + dop.drive_gf = drive; + struct cdbres_inquiry data; + int ret = cdb_get_inquiry(&dop, &data); + if (ret) + return ret; + char vendor[sizeof(data.vendor)+1], product[sizeof(data.product)+1]; + char rev[sizeof(data.rev)+1]; + strtcpy(vendor, data.vendor, sizeof(vendor)); + nullTrailingSpace(vendor); + strtcpy(product, data.product, sizeof(product)); + nullTrailingSpace(product); + strtcpy(rev, data.rev, sizeof(rev)); + nullTrailingSpace(rev); + int pdt = data.pdt & 0x1f; + int removable = !!(data.removable & 0x80); + dprintf(1, "%s vendor='%s' product='%s' rev='%s' type=%d removable=%d\n" + , s, vendor, product, rev, pdt, removable); + drive->removable = removable; + + if (pdt == SCSI_TYPE_CDROM) { + drive->blksize = CDROM_SECTOR_SIZE; + drive->sectors = (u64)-1; + + char *desc = znprintf(MAXDESCSIZE, "DVD/CD [%s Drive %s %s %s]" + , s, vendor, product, rev); + boot_add_cd(drive, desc, prio); + return 0; + } + + ret = scsi_is_ready(&dop); + if (ret) { + dprintf(1, "scsi_is_ready returned %d\n", ret); + return ret; + } + + struct cdbres_read_capacity capdata; + ret = cdb_read_capacity(&dop, &capdata); + if (ret) + return ret; + + // READ CAPACITY returns the address of the last block. + // We do not bother with READ CAPACITY(16) because BIOS does not support + // 64-bit LBA anyway. + drive->blksize = be32_to_cpu(capdata.blksize); + if (drive->blksize != DISK_SECTOR_SIZE) { + dprintf(1, "%s: unsupported block size %d\n", s, drive->blksize); + return -1; + } + drive->sectors = (u64)be32_to_cpu(capdata.sectors) + 1; + dprintf(1, "%s blksize=%d sectors=%d\n" + , s, drive->blksize, (unsigned)drive->sectors); + + // We do not recover from USB stalls, so try to be safe and avoid + // sending the command if the (obsolete, but still provided by QEMU) + // fixed disk geometry page may not be supported. + // + // We could also send the command only to small disks (e.g. <504MiB) + // but some old USB keys only support a very small subset of SCSI which + // does not even include the MODE SENSE command! + // + if (CONFIG_QEMU_HARDWARE && memcmp(vendor, "QEMU", 5) == 0) { + struct cdbres_mode_sense_geom geomdata; + ret = cdb_mode_sense_geom(&dop, &geomdata); + if (ret == 0) { + u32 cylinders; + cylinders = geomdata.cyl[0] << 16; + cylinders |= geomdata.cyl[1] << 8; + cylinders |= geomdata.cyl[2]; + if (cylinders && geomdata.heads && + drive->sectors <= 0xFFFFFFFFULL && + ((u32)drive->sectors % (geomdata.heads * cylinders) == 0)) { + drive->pchs.cylinder = cylinders; + drive->pchs.head = geomdata.heads; + drive->pchs.sector = (u32)drive->sectors / (geomdata.heads * cylinders); + } + } + } + + char *desc = znprintf(MAXDESCSIZE, "%s Drive %s %s %s" + , s, vendor, product, rev); + boot_add_hd(drive, desc, prio); + return 0; +} + +int +cdb_get_inquiry(struct disk_op_s *op, struct cdbres_inquiry *data) +{ + struct cdb_request_sense cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_INQUIRY; + cmd.length = sizeof(*data); + op->count = 1; + op->buf_fl = data; + return cdb_cmd_data(op, &cmd, sizeof(*data)); +} + +// Request SENSE +int +cdb_get_sense(struct disk_op_s *op, struct cdbres_request_sense *data) +{ + struct cdb_request_sense cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_REQUEST_SENSE; + cmd.length = sizeof(*data); + op->count = 1; + op->buf_fl = data; + return cdb_cmd_data(op, &cmd, sizeof(*data)); +} + +// Test unit ready +int +cdb_test_unit_ready(struct disk_op_s *op) +{ + struct cdb_request_sense cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_TEST_UNIT_READY; + op->count = 0; + op->buf_fl = NULL; + return cdb_cmd_data(op, &cmd, 0); +} + +// Request capacity +int +cdb_read_capacity(struct disk_op_s *op, struct cdbres_read_capacity *data) +{ + struct cdb_read_capacity cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_READ_CAPACITY; + op->count = 1; + op->buf_fl = data; + return cdb_cmd_data(op, &cmd, sizeof(*data)); +} + +// Mode sense, geometry page. +int +cdb_mode_sense_geom(struct disk_op_s *op, struct cdbres_mode_sense_geom *data) +{ + struct cdb_mode_sense cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_MODE_SENSE; + cmd.flags = 8; /* DBD */ + cmd.page = MODE_PAGE_HD_GEOMETRY; + cmd.count = cpu_to_be16(sizeof(*data)); + op->count = 1; + op->buf_fl = data; + return cdb_cmd_data(op, &cmd, sizeof(*data)); +} + +// Read sectors. +int +cdb_read(struct disk_op_s *op) +{ + struct cdb_rwdata_10 cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_READ_10; + cmd.lba = cpu_to_be32(op->lba); + cmd.count = cpu_to_be16(op->count); + return cdb_cmd_data(op, &cmd, GET_GLOBALFLAT(op->drive_gf->blksize)); +} + +// Write sectors. +int +cdb_write(struct disk_op_s *op) +{ + struct cdb_rwdata_10 cmd; + memset(&cmd, 0, sizeof(cmd)); + cmd.command = CDB_CMD_WRITE_10; + cmd.lba = cpu_to_be32(op->lba); + cmd.count = cpu_to_be16(op->count); + return cdb_cmd_data(op, &cmd, GET_GLOBALFLAT(op->drive_gf->blksize)); +} diff --git a/roms/seabios/src/hw/blockcmd.h b/roms/seabios/src/hw/blockcmd.h new file mode 100644 index 000000000..8bacfcfb4 --- /dev/null +++ b/roms/seabios/src/hw/blockcmd.h @@ -0,0 +1,118 @@ +// Definitions for SCSI style command data blocks. +#ifndef __BLOCKCMD_H +#define __BLOCKCMD_H + +#include "types.h" // u8 + +#define CDB_CMD_READ_10 0x28 +#define CDB_CMD_VERIFY_10 0x2f +#define CDB_CMD_WRITE_10 0x2a + +struct cdb_rwdata_10 { + u8 command; + u8 flags; + u32 lba; + u8 resreved_06; + u16 count; + u8 reserved_09; + u8 pad[6]; +} PACKED; + +#define CDB_CMD_READ_CAPACITY 0x25 + +struct cdb_read_capacity { + u8 command; + u8 flags; + u8 resreved_02[8]; + u8 pad[6]; +} PACKED; + +struct cdbres_read_capacity { + u32 sectors; + u32 blksize; +} PACKED; + +#define CDB_CMD_TEST_UNIT_READY 0x00 +#define CDB_CMD_INQUIRY 0x12 +#define CDB_CMD_REQUEST_SENSE 0x03 + +struct cdb_request_sense { + u8 command; + u8 flags; + u16 reserved_02; + u8 length; + u8 reserved_05; + u8 pad[10]; +} PACKED; + +struct cdbres_request_sense { + u8 errcode; + u8 segment; + u8 flags; + u32 info; + u8 additional; + u32 specific; + u8 asc; + u8 ascq; + u32 reserved_0e; +} PACKED; + +#define SCSI_TYPE_DISK 0x00 +#define SCSI_TYPE_CDROM 0x05 + +struct cdbres_inquiry { + u8 pdt; + u8 removable; + u8 reserved_02[2]; + u8 additional; + u8 reserved_05[3]; + char vendor[8]; + char product[16]; + char rev[4]; +} PACKED; + +#define CDB_CMD_MODE_SENSE 0x5A +#define MODE_PAGE_HD_GEOMETRY 0x04 + +struct cdb_mode_sense { + u8 command; + u8 flags; + u8 page; + u32 reserved_03; + u16 count; + u8 reserved_09; + u8 pad[6]; +} PACKED; + +struct cdbres_mode_sense_geom { + u8 unused_00[3]; + u8 read_only; + u32 unused_04; + u8 page; + u8 length; + u8 cyl[3]; + u8 heads; + u8 precomp[3]; + u8 reduced[3]; + u16 step_rate; + u8 landing[3]; + u16 rpm; +} PACKED; + +// blockcmd.c +int cdb_is_read(u8 *cdbcmd, u16 blocksize); +struct disk_op_s; +int cdb_get_inquiry(struct disk_op_s *op, struct cdbres_inquiry *data); +int cdb_get_sense(struct disk_op_s *op, struct cdbres_request_sense *data); +int cdb_test_unit_ready(struct disk_op_s *op); +int cdb_read_capacity(struct disk_op_s *op, struct cdbres_read_capacity *data); +int cdb_mode_sense_geom(struct disk_op_s *op, struct cdbres_mode_sense_geom *data); +int cdb_inquiry(struct disk_op_s *op, struct cdbres_inquiry *data); +int cdb_read(struct disk_op_s *op); +int cdb_write(struct disk_op_s *op); + +int scsi_is_ready(struct disk_op_s *op); +struct drive_s; +int scsi_drive_setup(struct drive_s *drive, const char *s, int prio); + +#endif // blockcmd.h diff --git a/roms/seabios/src/hw/dma.c b/roms/seabios/src/hw/dma.c new file mode 100644 index 000000000..20c9fbb76 --- /dev/null +++ b/roms/seabios/src/hw/dma.c @@ -0,0 +1,67 @@ +// Code to support legacy Intel 8237 DMA chip. +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "util.h" // dma_setup +#include "x86.h" // outb + +#define PORT_DMA_ADDR_2 0x0004 +#define PORT_DMA_CNT_2 0x0005 +#define PORT_DMA1_MASK_REG 0x000a +#define PORT_DMA1_MODE_REG 0x000b +#define PORT_DMA1_CLEAR_FF_REG 0x000c +#define PORT_DMA1_MASTER_CLEAR 0x000d +#define PORT_DMA_PAGE_2 0x0081 +#define PORT_DMA2_MASK_REG 0x00d4 +#define PORT_DMA2_MODE_REG 0x00d6 +#define PORT_DMA2_MASTER_CLEAR 0x00da + +// Setup the DMA controller for a floppy transfer. +int +dma_floppy(u32 addr, int count, int isWrite) +{ + // check for 64K boundary overrun + u16 end = count - 1; + u32 last_addr = addr + end; + if ((addr >> 16) != (last_addr >> 16)) + return -1; + + u8 mode_register = 0x46; // single mode, increment, autoinit disable, + if (isWrite) + mode_register = 0x4a; + + outb(0x06, PORT_DMA1_MASK_REG); + outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop + outb(addr, PORT_DMA_ADDR_2); + outb(addr>>8, PORT_DMA_ADDR_2); + outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop + outb(end, PORT_DMA_CNT_2); + outb(end>>8, PORT_DMA_CNT_2); + + // port 0b: DMA-1 Mode Register + // transfer type=write, channel 2 + outb(mode_register, PORT_DMA1_MODE_REG); + + // port 81: DMA-1 Page Register, channel 2 + outb(addr>>16, PORT_DMA_PAGE_2); + + outb(0x02, PORT_DMA1_MASK_REG); // unmask channel 2 + + return 0; +} + +// Reset DMA controller +void +dma_setup(void) +{ + // first reset the DMA controllers + outb(0, PORT_DMA1_MASTER_CLEAR); + outb(0, PORT_DMA2_MASTER_CLEAR); + + // then initialize the DMA controllers + outb(0xc0, PORT_DMA2_MODE_REG); + outb(0x00, PORT_DMA2_MASK_REG); +} diff --git a/roms/seabios/src/hw/esp-scsi.c b/roms/seabios/src/hw/esp-scsi.c new file mode 100644 index 000000000..982c1d548 --- /dev/null +++ b/roms/seabios/src/hw/esp-scsi.c @@ -0,0 +1,238 @@ +// AMD PCscsi boot support. +// +// Copyright (C) 2012 Red Hat Inc. +// +// Authors: +// Paolo Bonzini <pbonzini@redhat.com> +// +// based on lsi-scsi.c which is written by: +// Gerd Hoffman <kraxel@redhat.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // struct drive_s +#include "blockcmd.h" // scsi_drive_setup +#include "config.h" // CONFIG_* +#include "fw/paravirt.h" // runningOnQEMU +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_DEVICE_ID +#include "pci_regs.h" // PCI_VENDOR_ID +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // usleep + +#define ESP_TCLO 0x00 +#define ESP_TCMID 0x04 +#define ESP_FIFO 0x08 +#define ESP_CMD 0x0c +#define ESP_WBUSID 0x10 +#define ESP_TCHI 0x38 + +#define ESP_RSTAT 0x10 +#define ESP_RINTR 0x14 +#define ESP_RFLAGS 0x1c + +#define ESP_DMA_CMD 0x40 +#define ESP_DMA_STC 0x44 +#define ESP_DMA_SPA 0x48 +#define ESP_DMA_WBC 0x4c +#define ESP_DMA_WAC 0x50 +#define ESP_DMA_STAT 0x54 +#define ESP_DMA_SMDLA 0x58 +#define ESP_DMA_WMAC 0x58c + +#define ESP_CMD_DMA 0x80 +#define ESP_CMD_RESET 0x02 +#define ESP_CMD_TI 0x10 +#define ESP_CMD_ICCS 0x11 +#define ESP_CMD_SELATN 0x42 + +#define ESP_STAT_DI 0x01 +#define ESP_STAT_CD 0x02 +#define ESP_STAT_MSG 0x04 +#define ESP_STAT_TC 0x10 + +#define ESP_INTR_DC 0x20 + +struct esp_lun_s { + struct drive_s drive; + struct pci_device *pci; + u32 iobase; + u8 target; + u8 lun; +}; + +static void +esp_scsi_dma(u32 iobase, u32 buf, u32 len, int read) +{ + outb(len & 0xff, iobase + ESP_TCLO); + outb((len >> 8) & 0xff, iobase + ESP_TCMID); + outb((len >> 16) & 0xff, iobase + ESP_TCHI); + outl(buf, iobase + ESP_DMA_SPA); + outl(len, iobase + ESP_DMA_STC); + outb(read ? 0x83 : 0x03, iobase + ESP_DMA_CMD); +} + +static int +esp_scsi_cmd(struct esp_lun_s *llun_gf, struct disk_op_s *op, + u8 *cdbcmd, u16 target, u16 lun, u16 blocksize) +{ + u32 iobase = GET_GLOBALFLAT(llun_gf->iobase); + int i, state; + u8 status; + + outb(target, iobase + ESP_WBUSID); + + /* + * We need to pass the LUN at the beginning of the command, and the FIFO + * is only 16 bytes, so we cannot support 16-byte CDBs. The alternative + * would be to use DMA for the 17-byte command too, which is quite + * overkill. + */ + outb(lun, iobase + ESP_FIFO); + cdbcmd[1] &= 0x1f; + cdbcmd[1] |= lun << 5; + for (i = 0; i < 12; i++) + outb(cdbcmd[i], iobase + ESP_FIFO); + outb(ESP_CMD_SELATN, iobase + ESP_CMD); + + for (state = 0;;) { + u8 stat = inb(iobase + ESP_RSTAT); + + /* Detect disconnected device. */ + if (state == 0 && (inb(iobase + ESP_RINTR) & ESP_INTR_DC)) { + return DISK_RET_ENOTREADY; + } + + /* HBA reads command, clears CD, sets TC -> do DMA if needed. */ + if (state == 0 && (stat & ESP_STAT_TC)) { + state++; + if (op->count && blocksize) { + /* Data phase. */ + u32 count = (u32)op->count * blocksize; + esp_scsi_dma(iobase, (u32)op->buf_fl, count, + cdb_is_read(cdbcmd, blocksize)); + outb(ESP_CMD_TI | ESP_CMD_DMA, iobase + ESP_CMD); + continue; + } + } + + /* At end of DMA TC is set again -> complete command. */ + if (state == 1 && (stat & ESP_STAT_TC)) { + state++; + outb(ESP_CMD_ICCS, iobase + ESP_CMD); + continue; + } + + /* Finally read data from the message in phase. */ + if (state == 2 && (stat & ESP_STAT_MSG)) { + state++; + status = inb(iobase + ESP_FIFO); + inb(iobase + ESP_FIFO); + break; + } + usleep(5); + } + + if (status == 0) { + return DISK_RET_SUCCESS; + } + + return DISK_RET_EBADTRACK; +} + +int +esp_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + if (!CONFIG_ESP_SCSI) + return DISK_RET_EBADTRACK; + + struct esp_lun_s *llun_gf = + container_of(op->drive_gf, struct esp_lun_s, drive); + + return esp_scsi_cmd(llun_gf, op, cdbcmd, + GET_GLOBALFLAT(llun_gf->target), + GET_GLOBALFLAT(llun_gf->lun), + blocksize); +} + +static int +esp_scsi_add_lun(struct pci_device *pci, u32 iobase, u8 target, u8 lun) +{ + struct esp_lun_s *llun = malloc_fseg(sizeof(*llun)); + if (!llun) { + warn_noalloc(); + return -1; + } + memset(llun, 0, sizeof(*llun)); + llun->drive.type = DTYPE_ESP_SCSI; + llun->drive.cntl_id = pci->bdf; + llun->pci = pci; + llun->target = target; + llun->lun = lun; + llun->iobase = iobase; + + char *name = znprintf(16, "esp %02x:%02x.%x %d:%d", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), + pci_bdf_to_fn(pci->bdf), target, lun); + int prio = bootprio_find_scsi_device(pci, target, lun); + int ret = scsi_drive_setup(&llun->drive, name, prio); + free(name); + if (ret) + goto fail; + return 0; + +fail: + free(llun); + return -1; +} + +static void +esp_scsi_scan_target(struct pci_device *pci, u32 iobase, u8 target) +{ + esp_scsi_add_lun(pci, iobase, target, 0); +} + +static void +init_esp_scsi(struct pci_device *pci) +{ + u16 bdf = pci->bdf; + u32 iobase = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0) + & PCI_BASE_ADDRESS_IO_MASK; + + dprintf(1, "found esp at %02x:%02x.%x, io @ %x\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), + pci_bdf_to_fn(bdf), iobase); + + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + + // reset + outb(ESP_CMD_RESET, iobase + ESP_CMD); + + int i; + for (i = 0; i <= 7; i++) + esp_scsi_scan_target(pci, iobase, i); + + return; +} + +void +esp_scsi_setup(void) +{ + ASSERT32FLAT(); + if (!CONFIG_ESP_SCSI || !runningOnQEMU()) + return; + + dprintf(3, "init esp\n"); + + struct pci_device *pci; + foreachpci(pci) { + if (pci->vendor != PCI_VENDOR_ID_AMD + || pci->device != PCI_DEVICE_ID_AMD_SCSI) + continue; + init_esp_scsi(pci); + } +} diff --git a/roms/seabios/src/hw/esp-scsi.h b/roms/seabios/src/hw/esp-scsi.h new file mode 100644 index 000000000..dc555f395 --- /dev/null +++ b/roms/seabios/src/hw/esp-scsi.h @@ -0,0 +1,8 @@ +#ifndef __ESP_SCSI_H +#define __ESP_SCSI_H + +struct disk_op_s; +int esp_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +void esp_scsi_setup(void); + +#endif /* __ESP_SCSI_H */ diff --git a/roms/seabios/src/hw/floppy.c b/roms/seabios/src/hw/floppy.c new file mode 100644 index 000000000..b8482031c --- /dev/null +++ b/roms/seabios/src/hw/floppy.c @@ -0,0 +1,689 @@ +// 16bit code to access floppy drives. +// +// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // SET_BDA +#include "block.h" // struct drive_s +#include "bregs.h" // struct bregs +#include "config.h" // CONFIG_FLOPPY +#include "malloc.h" // malloc_fseg +#include "output.h" // dprintf +#include "pci.h" // pci_to_bdf +#include "pci_ids.h" // PCI_CLASS_BRIDGE_ISA +#include "pic.h" // pic_eoi1 +#include "romfile.h" // romfile_loadint +#include "rtc.h" // rtc_read +#include "stacks.h" // yield +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // timer_calc + +#define PORT_FD_BASE 0x03f0 +#define PORT_FD_DOR 0x03f2 +#define PORT_FD_STATUS 0x03f4 +#define PORT_FD_DATA 0x03f5 +#define PORT_FD_DIR 0x03f7 + +#define FLOPPY_SIZE_CODE 0x02 // 512 byte sectors +#define FLOPPY_DATALEN 0xff // Not used - because size code is 0x02 +#define FLOPPY_MOTOR_TICKS 37 // ~2 seconds +#define FLOPPY_FILLBYTE 0xf6 +#define FLOPPY_GAPLEN 0x1B +#define FLOPPY_FORMAT_GAPLEN 0x6c +#define FLOPPY_PIO_TIMEOUT 1000 + +// New diskette parameter table adding 3 parameters from IBM +// Since no provisions are made for multiple drive types, most +// values in this table are ignored. I set parameters for 1.44M +// floppy here +struct floppy_ext_dbt_s diskette_param_table2 VARFSEG = { + .dbt = { + .specify1 = 0xAF, // step rate 12ms, head unload 240ms + .specify2 = 0x02, // head load time 4ms, DMA used + .shutoff_ticks = FLOPPY_MOTOR_TICKS, // ~2 seconds + .bps_code = FLOPPY_SIZE_CODE, + .sectors = 18, + .interblock_len = FLOPPY_GAPLEN, + .data_len = FLOPPY_DATALEN, + .gap_len = FLOPPY_FORMAT_GAPLEN, + .fill_byte = FLOPPY_FILLBYTE, + .settle_time = 0x0F, // 15ms + .startup_time = 0x08, // 1 second + }, + .max_track = 79, // maximum track + .data_rate = 0, // data transfer rate + .drive_type = 4, // drive type in cmos +}; + +struct floppy_dbt_s diskette_param_table VAR16FIXED(0xefc7); + +struct floppyinfo_s { + struct chs_s chs; + u8 floppy_size; + u8 data_rate; +}; + +#define FLOPPY_SIZE_525 0x01 +#define FLOPPY_SIZE_350 0x02 + +#define FLOPPY_RATE_500K 0x00 +#define FLOPPY_RATE_300K 0x01 +#define FLOPPY_RATE_250K 0x02 +#define FLOPPY_RATE_1M 0x03 + +struct floppyinfo_s FloppyInfo[] VARFSEG = { + // Unknown + { {0, 0, 0}, 0x00, 0x00}, + // 1 - 360KB, 5.25" - 2 heads, 40 tracks, 9 sectors + { {2, 40, 9}, FLOPPY_SIZE_525, FLOPPY_RATE_300K}, + // 2 - 1.2MB, 5.25" - 2 heads, 80 tracks, 15 sectors + { {2, 80, 15}, FLOPPY_SIZE_525, FLOPPY_RATE_500K}, + // 3 - 720KB, 3.5" - 2 heads, 80 tracks, 9 sectors + { {2, 80, 9}, FLOPPY_SIZE_350, FLOPPY_RATE_250K}, + // 4 - 1.44MB, 3.5" - 2 heads, 80 tracks, 18 sectors + { {2, 80, 18}, FLOPPY_SIZE_350, FLOPPY_RATE_500K}, + // 5 - 2.88MB, 3.5" - 2 heads, 80 tracks, 36 sectors + { {2, 80, 36}, FLOPPY_SIZE_350, FLOPPY_RATE_1M}, + // 6 - 160k, 5.25" - 1 heads, 40 tracks, 8 sectors + { {1, 40, 8}, FLOPPY_SIZE_525, FLOPPY_RATE_250K}, + // 7 - 180k, 5.25" - 1 heads, 40 tracks, 9 sectors + { {1, 40, 9}, FLOPPY_SIZE_525, FLOPPY_RATE_300K}, + // 8 - 320k, 5.25" - 2 heads, 40 tracks, 8 sectors + { {2, 40, 8}, FLOPPY_SIZE_525, FLOPPY_RATE_250K}, +}; + +struct drive_s * +init_floppy(int floppyid, int ftype) +{ + if (ftype <= 0 || ftype >= ARRAY_SIZE(FloppyInfo)) { + dprintf(1, "Bad floppy type %d\n", ftype); + return NULL; + } + + struct drive_s *drive = malloc_fseg(sizeof(*drive)); + if (!drive) { + warn_noalloc(); + return NULL; + } + memset(drive, 0, sizeof(*drive)); + drive->cntl_id = floppyid; + drive->type = DTYPE_FLOPPY; + drive->blksize = DISK_SECTOR_SIZE; + drive->floppy_type = ftype; + drive->sectors = (u64)-1; + + memcpy(&drive->lchs, &FloppyInfo[ftype].chs + , sizeof(FloppyInfo[ftype].chs)); + return drive; +} + +static void +addFloppy(int floppyid, int ftype) +{ + struct drive_s *drive = init_floppy(floppyid, ftype); + if (!drive) + return; + char *desc = znprintf(MAXDESCSIZE, "Floppy [drive %c]", 'A' + floppyid); + struct pci_device *pci = pci_find_class(PCI_CLASS_BRIDGE_ISA); /* isa-to-pci bridge */ + int prio = bootprio_find_fdc_device(pci, PORT_FD_BASE, floppyid); + boot_add_floppy(drive, desc, prio); +} + +void +floppy_setup(void) +{ + memcpy(&diskette_param_table, &diskette_param_table2 + , sizeof(diskette_param_table)); + SET_IVT(0x1E, SEGOFF(SEG_BIOS + , (u32)&diskette_param_table2 - BUILD_BIOS_ADDR)); + + if (! CONFIG_FLOPPY) + return; + dprintf(3, "init floppy drives\n"); + + if (CONFIG_QEMU) { + u8 type = rtc_read(CMOS_FLOPPY_DRIVE_TYPE); + if (type & 0xf0) + addFloppy(0, type >> 4); + if (type & 0x0f) + addFloppy(1, type & 0x0f); + } else { + u8 type = romfile_loadint("etc/floppy0", 0); + if (type) + addFloppy(0, type); + type = romfile_loadint("etc/floppy1", 0); + if (type) + addFloppy(1, type); + } + + enable_hwirq(6, FUNC16(entry_0e)); +} + +// Find a floppy type that matches a given image size. +int +find_floppy_type(u32 size) +{ + int i; + for (i=1; i<ARRAY_SIZE(FloppyInfo); i++) { + struct chs_s *c = &FloppyInfo[i].chs; + if (c->cylinder * c->head * c->sector * DISK_SECTOR_SIZE == size) + return i; + } + return -1; +} + + +/**************************************************************** + * Low-level floppy IO + ****************************************************************/ + +u8 FloppyDOR VARLOW; + +static inline void +floppy_dor_write(u8 val) +{ + outb(val, PORT_FD_DOR); + SET_LOW(FloppyDOR, val); +} + +static void +floppy_disable_controller(void) +{ + dprintf(2, "Floppy_disable_controller\n"); + floppy_dor_write(0x00); +} + +static int +floppy_wait_irq(void) +{ + u8 frs = GET_BDA(floppy_recalibration_status); + SET_BDA(floppy_recalibration_status, frs & ~FRS_IRQ); + for (;;) { + if (!GET_BDA(floppy_motor_counter)) { + warn_timeout(); + floppy_disable_controller(); + return DISK_RET_ETIMEOUT; + } + frs = GET_BDA(floppy_recalibration_status); + if (frs & FRS_IRQ) + break; + // Could use yield_toirq() here, but that causes issues on + // bochs, so use yield() instead. + yield(); + } + + SET_BDA(floppy_recalibration_status, frs & ~FRS_IRQ); + return DISK_RET_SUCCESS; +} + +// Floppy commands +#define FCF_WAITIRQ 0x10000 +#define FC_CHECKIRQ (0x08 | (0<<8) | (2<<12)) +#define FC_SEEK (0x0f | (2<<8) | (0<<12) | FCF_WAITIRQ) +#define FC_RECALIBRATE (0x07 | (1<<8) | (0<<12) | FCF_WAITIRQ) +#define FC_READID (0x4a | (1<<8) | (7<<12) | FCF_WAITIRQ) +#define FC_READ (0xe6 | (8<<8) | (7<<12) | FCF_WAITIRQ) +#define FC_WRITE (0xc5 | (8<<8) | (7<<12) | FCF_WAITIRQ) +#define FC_FORMAT (0x4d | (5<<8) | (7<<12) | FCF_WAITIRQ) + +// Send the specified command and it's parameters to the floppy controller. +static int +floppy_pio(int command, u8 *param) +{ + dprintf(9, "Floppy pio command %x\n", command); + // Send command and parameters to controller. + u32 end = timer_calc(FLOPPY_PIO_TIMEOUT); + int send = (command >> 8) & 0xf; + int i = 0; + for (;;) { + u8 sts = inb(PORT_FD_STATUS); + if (!(sts & 0x80)) { + if (timer_check(end)) { + warn_timeout(); + floppy_disable_controller(); + return DISK_RET_ETIMEOUT; + } + continue; + } + if (sts & 0x40) { + floppy_disable_controller(); + return DISK_RET_ECONTROLLER; + } + if (i == 0) + outb(command & 0xff, PORT_FD_DATA); + else + outb(param[i-1], PORT_FD_DATA); + if (i++ >= send) + break; + } + + // Wait for command to complete. + if (command & FCF_WAITIRQ) { + int ret = floppy_wait_irq(); + if (ret) + return ret; + } + + // Read response from controller. + end = timer_calc(FLOPPY_PIO_TIMEOUT); + int receive = (command >> 12) & 0xf; + i = 0; + for (;;) { + u8 sts = inb(PORT_FD_STATUS); + if (!(sts & 0x80)) { + if (timer_check(end)) { + warn_timeout(); + floppy_disable_controller(); + return DISK_RET_ETIMEOUT; + } + continue; + } + if (i >= receive) { + if (sts & 0x40) { + floppy_disable_controller(); + return DISK_RET_ECONTROLLER; + } + break; + } + if (!(sts & 0x40)) { + floppy_disable_controller(); + return DISK_RET_ECONTROLLER; + } + param[i++] = inb(PORT_FD_DATA); + } + + return DISK_RET_SUCCESS; +} + +static int +floppy_enable_controller(void) +{ + dprintf(2, "Floppy_enable_controller\n"); + SET_BDA(floppy_motor_counter, FLOPPY_MOTOR_TICKS); + floppy_dor_write(0x00); + floppy_dor_write(0x0c); + int ret = floppy_wait_irq(); + if (ret) + return ret; + + u8 param[2]; + return floppy_pio(FC_CHECKIRQ, param); +} + +// Activate a drive and send a command to it. +static int +floppy_drive_pio(u8 floppyid, int command, u8 *param) +{ + // Enable controller if it isn't running. + if (!(GET_LOW(FloppyDOR) & 0x04)) { + int ret = floppy_enable_controller(); + if (ret) + return ret; + } + + // reset the disk motor timeout value of INT 08 + SET_BDA(floppy_motor_counter, FLOPPY_MOTOR_TICKS); + + // Turn on motor of selected drive, DMA & int enabled, normal operation + floppy_dor_write((floppyid ? 0x20 : 0x10) | 0x0c | floppyid); + + // Send command. + int ret = floppy_pio(command, param); + if (ret) + return ret; + + // Check IRQ command is needed after irq commands with no results + if ((command & FCF_WAITIRQ) && ((command >> 12) & 0xf) == 0) + return floppy_pio(FC_CHECKIRQ, param); + return DISK_RET_SUCCESS; +} + + +/**************************************************************** + * Floppy media sense and seeking + ****************************************************************/ + +static int +floppy_drive_recal(u8 floppyid) +{ + dprintf(2, "Floppy_drive_recal %d\n", floppyid); + // send Recalibrate command to controller + u8 param[2]; + param[0] = floppyid; + int ret = floppy_drive_pio(floppyid, FC_RECALIBRATE, param); + if (ret) + return ret; + + u8 frs = GET_BDA(floppy_recalibration_status); + SET_BDA(floppy_recalibration_status, frs | (1<<floppyid)); + SET_BDA(floppy_track[floppyid], 0); + return DISK_RET_SUCCESS; +} + +static int +floppy_drive_readid(u8 floppyid, u8 data_rate, u8 head) +{ + // Set data rate. + outb(data_rate, PORT_FD_DIR); + + // send Read Sector Id command + u8 param[7]; + param[0] = (head << 2) | floppyid; // HD DR1 DR2 + int ret = floppy_drive_pio(floppyid, FC_READID, param); + if (ret) + return ret; + if (param[0] & 0xc0) + return -1; + return 0; +} + +static int +floppy_media_sense(struct drive_s *drive_gf) +{ + u8 ftype = GET_GLOBALFLAT(drive_gf->floppy_type), stype = ftype; + u8 floppyid = GET_GLOBALFLAT(drive_gf->cntl_id); + + u8 data_rate = GET_GLOBAL(FloppyInfo[stype].data_rate); + int ret = floppy_drive_readid(floppyid, data_rate, 0); + if (ret) { + // Attempt media sense. + for (stype=1; ; stype++) { + if (stype >= ARRAY_SIZE(FloppyInfo)) + return DISK_RET_EMEDIA; + if (stype==ftype + || (GET_GLOBAL(FloppyInfo[stype].floppy_size) + != GET_GLOBAL(FloppyInfo[ftype].floppy_size)) + || (GET_GLOBAL(FloppyInfo[stype].chs.head) + > GET_GLOBAL(FloppyInfo[ftype].chs.head)) + || (GET_GLOBAL(FloppyInfo[stype].chs.cylinder) + > GET_GLOBAL(FloppyInfo[ftype].chs.cylinder)) + || (GET_GLOBAL(FloppyInfo[stype].chs.sector) + > GET_GLOBAL(FloppyInfo[ftype].chs.sector))) + continue; + data_rate = GET_GLOBAL(FloppyInfo[stype].data_rate); + ret = floppy_drive_readid(floppyid, data_rate, 0); + if (!ret) + break; + } + } + dprintf(2, "Floppy_media_sense on drive %d found rate %d\n" + , floppyid, data_rate); + + u8 old_data_rate = GET_BDA(floppy_media_state[floppyid]) >> 6; + SET_BDA(floppy_last_data_rate, (old_data_rate<<2) | (data_rate<<6)); + u8 media = (stype == 1 ? 0x04 : (stype == 2 ? 0x05 : 0x07)); + u8 fms = (data_rate<<6) | FMS_MEDIA_DRIVE_ESTABLISHED | media; + if (GET_GLOBAL(FloppyInfo[stype].chs.cylinder) + < GET_GLOBAL(FloppyInfo[ftype].chs.cylinder)) + fms |= FMS_DOUBLE_STEPPING; + SET_BDA(floppy_media_state[floppyid], fms); + + return DISK_RET_SUCCESS; +} + +// Prepare a floppy for a data transfer. +static int +floppy_prep(struct drive_s *drive_gf, u8 cylinder) +{ + u8 floppyid = GET_GLOBALFLAT(drive_gf->cntl_id); + if (!(GET_BDA(floppy_recalibration_status) & (1<<floppyid)) || + !(GET_BDA(floppy_media_state[floppyid]) & FMS_MEDIA_DRIVE_ESTABLISHED)) { + // Recalibrate drive. + int ret = floppy_drive_recal(floppyid); + if (ret) + return ret; + + // Sense media. + ret = floppy_media_sense(drive_gf); + if (ret) + return ret; + } + + // Seek to cylinder if needed. + u8 lastcyl = GET_BDA(floppy_track[floppyid]); + if (cylinder != lastcyl) { + u8 param[2]; + param[0] = floppyid; + param[1] = cylinder; + int ret = floppy_drive_pio(floppyid, FC_SEEK, param); + if (ret) + return ret; + SET_BDA(floppy_track[floppyid], cylinder); + } + + return DISK_RET_SUCCESS; +} + + +/**************************************************************** + * Floppy DMA transfer + ****************************************************************/ + +// Perform a floppy transfer command (setup DMA and issue PIO). +static int +floppy_dma_cmd(struct disk_op_s *op, int count, int command, u8 *param) +{ + // Setup DMA controller + int isWrite = command != FC_READ; + int ret = dma_floppy((u32)op->buf_fl, count, isWrite); + if (ret) + return DISK_RET_EBOUNDARY; + + // Invoke floppy controller + u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id); + ret = floppy_drive_pio(floppyid, command, param); + if (ret) + return ret; + + // Populate floppy_return_status in BDA + int i; + for (i=0; i<7; i++) + SET_BDA(floppy_return_status[i], param[i]); + + if (param[0] & 0xc0) { + if (param[1] & 0x02) + return DISK_RET_EWRITEPROTECT; + dprintf(1, "floppy error: %02x %02x %02x %02x %02x %02x %02x\n" + , param[0], param[1], param[2], param[3] + , param[4], param[5], param[6]); + return DISK_RET_ECONTROLLER; + } + + return DISK_RET_SUCCESS; +} + + +/**************************************************************** + * Floppy handlers + ****************************************************************/ + +static struct chs_s +lba2chs(struct disk_op_s *op) +{ + struct chs_s res = { }; + + u32 tmp = op->lba; + u16 nls = GET_GLOBALFLAT(op->drive_gf->lchs.sector); + res.sector = (tmp % nls) + 1; + + tmp /= nls; + u16 nlh = GET_GLOBALFLAT(op->drive_gf->lchs.head); + res.head = tmp % nlh; + + tmp /= nlh; + res.cylinder = tmp; + + return res; +} + +// diskette controller reset +static int +floppy_reset(struct disk_op_s *op) +{ + SET_BDA(floppy_recalibration_status, 0); + SET_BDA(floppy_media_state[0], 0); + SET_BDA(floppy_media_state[1], 0); + SET_BDA(floppy_track[0], 0); + SET_BDA(floppy_track[1], 0); + SET_BDA(floppy_last_data_rate, 0); + floppy_disable_controller(); + return floppy_enable_controller(); +} + +// Read Diskette Sectors +static int +floppy_read(struct disk_op_s *op) +{ + struct chs_s chs = lba2chs(op); + int res = floppy_prep(op->drive_gf, chs.cylinder); + if (res) + goto fail; + + // send read-normal-data command to controller + u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id); + u8 param[8]; + param[0] = (chs.head << 2) | floppyid; // HD DR1 DR2 + param[1] = chs.cylinder; + param[2] = chs.head; + param[3] = chs.sector; + param[4] = FLOPPY_SIZE_CODE; + param[5] = chs.sector + op->count - 1; // last sector to read on track + param[6] = FLOPPY_GAPLEN; + param[7] = FLOPPY_DATALEN; + res = floppy_dma_cmd(op, op->count * DISK_SECTOR_SIZE, FC_READ, param); + if (res) + goto fail; + return DISK_RET_SUCCESS; +fail: + op->count = 0; // no sectors read + return res; +} + +// Write Diskette Sectors +static int +floppy_write(struct disk_op_s *op) +{ + struct chs_s chs = lba2chs(op); + int res = floppy_prep(op->drive_gf, chs.cylinder); + if (res) + goto fail; + + // send write-normal-data command to controller + u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id); + u8 param[8]; + param[0] = (chs.head << 2) | floppyid; // HD DR1 DR2 + param[1] = chs.cylinder; + param[2] = chs.head; + param[3] = chs.sector; + param[4] = FLOPPY_SIZE_CODE; + param[5] = chs.sector + op->count - 1; // last sector to write on track + param[6] = FLOPPY_GAPLEN; + param[7] = FLOPPY_DATALEN; + res = floppy_dma_cmd(op, op->count * DISK_SECTOR_SIZE, FC_WRITE, param); + if (res) + goto fail; + return DISK_RET_SUCCESS; +fail: + op->count = 0; // no sectors read + return res; +} + +// Verify Diskette Sectors +static int +floppy_verify(struct disk_op_s *op) +{ + struct chs_s chs = lba2chs(op); + int res = floppy_prep(op->drive_gf, chs.cylinder); + if (res) + goto fail; + + // This command isn't implemented - just return success. + return DISK_RET_SUCCESS; +fail: + op->count = 0; // no sectors read + return res; +} + +// format diskette track +static int +floppy_format(struct disk_op_s *op) +{ + struct chs_s chs = lba2chs(op); + int res = floppy_prep(op->drive_gf, chs.cylinder); + if (res) + return res; + + // send format-track command to controller + u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id); + u8 param[7]; + param[0] = (chs.head << 2) | floppyid; // HD DR1 DR2 + param[1] = FLOPPY_SIZE_CODE; + param[2] = op->count; // number of sectors per track + param[3] = FLOPPY_FORMAT_GAPLEN; + param[4] = FLOPPY_FILLBYTE; + return floppy_dma_cmd(op, op->count * 4, FC_FORMAT, param); +} + +int +process_floppy_op(struct disk_op_s *op) +{ + if (!CONFIG_FLOPPY) + return 0; + + switch (op->command) { + case CMD_RESET: + return floppy_reset(op); + case CMD_READ: + return floppy_read(op); + case CMD_WRITE: + return floppy_write(op); + case CMD_VERIFY: + return floppy_verify(op); + case CMD_FORMAT: + return floppy_format(op); + default: + op->count = 0; + return DISK_RET_EPARAM; + } +} + + +/**************************************************************** + * HW irqs + ****************************************************************/ + +// INT 0Eh Diskette Hardware ISR Entry Point +void VISIBLE16 +handle_0e(void) +{ + if (! CONFIG_FLOPPY) + return; + debug_isr(DEBUG_ISR_0e); + + // diskette interrupt has occurred + u8 frs = GET_BDA(floppy_recalibration_status); + SET_BDA(floppy_recalibration_status, frs | FRS_IRQ); + + pic_eoi1(); +} + +// Called from int08 handler. +void +floppy_tick(void) +{ + if (! CONFIG_FLOPPY) + return; + + // time to turn off drive(s)? + u8 fcount = GET_BDA(floppy_motor_counter); + if (fcount) { + fcount--; + SET_BDA(floppy_motor_counter, fcount); + if (fcount == 0) + // turn motor(s) off + floppy_dor_write(GET_LOW(FloppyDOR) & ~0xf0); + } +} diff --git a/roms/seabios/src/hw/lsi-scsi.c b/roms/seabios/src/hw/lsi-scsi.c new file mode 100644 index 000000000..b1d6bbf4b --- /dev/null +++ b/roms/seabios/src/hw/lsi-scsi.c @@ -0,0 +1,217 @@ +// (qemu-emulated) lsi53c895a boot support. +// +// Copyright (C) 2012 Red Hat Inc. +// +// Authors: +// Gerd Hoffmann <kraxel@redhat.com> +// +// based on virtio-scsi.c which is written by: +// Paolo Bonzini <pbonzini@redhat.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // struct drive_s +#include "blockcmd.h" // scsi_drive_setup +#include "config.h" // CONFIG_* +#include "fw/paravirt.h" // runningOnQEMU +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_DEVICE_ID_VIRTIO_BLK +#include "pci_regs.h" // PCI_VENDOR_ID +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // usleep + +#define LSI_REG_DSTAT 0x0c +#define LSI_REG_ISTAT0 0x14 +#define LSI_REG_DSP0 0x2c +#define LSI_REG_DSP1 0x2d +#define LSI_REG_DSP2 0x2e +#define LSI_REG_DSP3 0x2f +#define LSI_REG_SIST0 0x42 +#define LSI_REG_SIST1 0x43 + +#define LSI_ISTAT0_DIP 0x01 +#define LSI_ISTAT0_SIP 0x02 +#define LSI_ISTAT0_INTF 0x04 +#define LSI_ISTAT0_CON 0x08 +#define LSI_ISTAT0_SEM 0x10 +#define LSI_ISTAT0_SIGP 0x20 +#define LSI_ISTAT0_SRST 0x40 +#define LSI_ISTAT0_ABRT 0x80 + +struct lsi_lun_s { + struct drive_s drive; + struct pci_device *pci; + u32 iobase; + u8 target; + u8 lun; +}; + +static int +lsi_scsi_cmd(struct lsi_lun_s *llun_gf, struct disk_op_s *op, + void *cdbcmd, u16 target, u16 lun, u16 blocksize) +{ + u32 iobase = GET_GLOBALFLAT(llun_gf->iobase); + u32 dma = ((cdb_is_read(cdbcmd, blocksize) ? 0x01000000 : 0x00000000) | + (op->count * blocksize)); + u8 msgout[] = { + 0x80 | lun, // select lun + 0x08, + }; + u8 status = 0xff; + u8 msgin_tmp[2]; + u8 msgin = 0xff; + + u32 script[] = { + /* select target, send scsi command */ + 0x40000000 | target << 16, // select target + 0x00000000, + 0x06000001, // msgout + (u32)MAKE_FLATPTR(GET_SEG(SS), &msgout), + 0x02000010, // scsi command + (u32)MAKE_FLATPTR(GET_SEG(SS), cdbcmd), + + /* handle disconnect */ + 0x87820000, // phase == msgin ? + 0x00000018, + 0x07000002, // msgin + (u32)MAKE_FLATPTR(GET_SEG(SS), &msgin_tmp), + 0x50000000, // re-select + 0x00000000, + 0x07000002, // msgin + (u32)MAKE_FLATPTR(GET_SEG(SS), &msgin_tmp), + + /* dma data, get status, raise irq */ + dma, // dma data + (u32)op->buf_fl, + 0x03000001, // status + (u32)MAKE_FLATPTR(GET_SEG(SS), &status), + 0x07000001, // msgin + (u32)MAKE_FLATPTR(GET_SEG(SS), &msgin), + 0x98080000, // dma irq + 0x00000000, + }; + u32 dsp = (u32)MAKE_FLATPTR(GET_SEG(SS), &script); + + outb(dsp & 0xff, iobase + LSI_REG_DSP0); + outb((dsp >> 8) & 0xff, iobase + LSI_REG_DSP1); + outb((dsp >> 16) & 0xff, iobase + LSI_REG_DSP2); + outb((dsp >> 24) & 0xff, iobase + LSI_REG_DSP3); + + for (;;) { + u8 dstat = inb(iobase + LSI_REG_DSTAT); + u8 sist0 = inb(iobase + LSI_REG_SIST0); + u8 sist1 = inb(iobase + LSI_REG_SIST1); + if (sist0 || sist1) { + goto fail; + } + if (dstat & 0x04) { + break; + } + usleep(5); + } + + if (msgin == 0 && status == 0) { + return DISK_RET_SUCCESS; + } + +fail: + return DISK_RET_EBADTRACK; +} + +int +lsi_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + if (!CONFIG_LSI_SCSI) + return DISK_RET_EBADTRACK; + + struct lsi_lun_s *llun_gf = + container_of(op->drive_gf, struct lsi_lun_s, drive); + + return lsi_scsi_cmd(llun_gf, op, cdbcmd, + GET_GLOBALFLAT(llun_gf->target), + GET_GLOBALFLAT(llun_gf->lun), + blocksize); +} + +static int +lsi_scsi_add_lun(struct pci_device *pci, u32 iobase, u8 target, u8 lun) +{ + struct lsi_lun_s *llun = malloc_fseg(sizeof(*llun)); + if (!llun) { + warn_noalloc(); + return -1; + } + memset(llun, 0, sizeof(*llun)); + llun->drive.type = DTYPE_LSI_SCSI; + llun->drive.cntl_id = pci->bdf; + llun->pci = pci; + llun->target = target; + llun->lun = lun; + llun->iobase = iobase; + + char *name = znprintf(16, "lsi %02x:%02x.%x %d:%d", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), + pci_bdf_to_fn(pci->bdf), target, lun); + int prio = bootprio_find_scsi_device(pci, target, lun); + int ret = scsi_drive_setup(&llun->drive, name, prio); + free(name); + if (ret) + goto fail; + return 0; + +fail: + free(llun); + return -1; +} + +static void +lsi_scsi_scan_target(struct pci_device *pci, u32 iobase, u8 target) +{ + /* TODO: send REPORT LUNS. For now, only LUN 0 is recognized. */ + lsi_scsi_add_lun(pci, iobase, target, 0); +} + +static void +init_lsi_scsi(struct pci_device *pci) +{ + u16 bdf = pci->bdf; + u32 iobase = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0) + & PCI_BASE_ADDRESS_IO_MASK; + + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + + dprintf(1, "found lsi53c895a at %02x:%02x.%x, io @ %x\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), + pci_bdf_to_fn(bdf), iobase); + + // reset + outb(LSI_ISTAT0_SRST, iobase + LSI_REG_ISTAT0); + + int i; + for (i = 0; i < 7; i++) + lsi_scsi_scan_target(pci, iobase, i); + + return; +} + +void +lsi_scsi_setup(void) +{ + ASSERT32FLAT(); + if (!CONFIG_LSI_SCSI || !runningOnQEMU()) + return; + + dprintf(3, "init lsi53c895a\n"); + + struct pci_device *pci; + foreachpci(pci) { + if (pci->vendor != PCI_VENDOR_ID_LSI_LOGIC + || pci->device != PCI_DEVICE_ID_LSI_53C895A) + continue; + init_lsi_scsi(pci); + } +} diff --git a/roms/seabios/src/hw/lsi-scsi.h b/roms/seabios/src/hw/lsi-scsi.h new file mode 100644 index 000000000..9c5a9b212 --- /dev/null +++ b/roms/seabios/src/hw/lsi-scsi.h @@ -0,0 +1,8 @@ +#ifndef __LSI_SCSI_H +#define __LSI_SCSI_H + +struct disk_op_s; +int lsi_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +void lsi_scsi_setup(void); + +#endif /* __LSI_SCSI_H */ diff --git a/roms/seabios/src/hw/megasas.c b/roms/seabios/src/hw/megasas.c new file mode 100644 index 000000000..a5dc14fcf --- /dev/null +++ b/roms/seabios/src/hw/megasas.c @@ -0,0 +1,400 @@ +// MegaRAID SAS boot support. +// +// Copyright (C) 2012 Hannes Reinecke, SUSE Linux Products GmbH +// +// Authors: +// Hannes Reinecke <hare@suse.de> +// +// based on virtio-scsi.c which is written by: +// Paolo Bonzini <pbonzini@redhat.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // struct drive_s +#include "blockcmd.h" // scsi_drive_setup +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_DEVICE_ID_XXX +#include "pci_regs.h" // PCI_VENDOR_ID +#include "stacks.h" // yield +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // timer_calc + +#define MFI_DB 0x0 // Doorbell +#define MFI_OMSG0 0x18 // Outbound message 0 +#define MFI_IDB 0x20 // Inbound doorbell +#define MFI_ODB 0x2c // Outbound doorbell +#define MFI_IQP 0x40 // Inbound queue port +#define MFI_OSP0 0xb0 // Outbound scratch pad0 +#define MFI_IQPL 0xc0 // Inbound queue port (low bytes) +#define MFI_IQPH 0xc4 // Inbound queue port (high bytes) + +#define MFI_STATE_MASK 0xf0000000 +#define MFI_STATE_WAIT_HANDSHAKE 0x60000000 +#define MFI_STATE_BOOT_MESSAGE_PENDING 0x90000000 +#define MFI_STATE_READY 0xb0000000 +#define MFI_STATE_OPERATIONAL 0xc0000000 +#define MFI_STATE_FAULT 0xf0000000 + +/* MFI Commands */ +typedef enum { + MFI_CMD_INIT = 0x00, + MFI_CMD_LD_READ, + MFI_CMD_LD_WRITE, + MFI_CMD_LD_SCSI_IO, + MFI_CMD_PD_SCSI_IO, + MFI_CMD_DCMD, + MFI_CMD_ABORT, + MFI_CMD_SMP, + MFI_CMD_STP +} mfi_cmd_t; + +struct megasas_cmd_frame { + u8 cmd; /*00h */ + u8 sense_len; /*01h */ + u8 cmd_status; /*02h */ + u8 scsi_status; /*03h */ + + u8 target_id; /*04h */ + u8 lun; /*05h */ + u8 cdb_len; /*06h */ + u8 sge_count; /*07h */ + + u32 context; /*08h */ + u32 context_64; /*0Ch */ + + u16 flags; /*10h */ + u16 timeout; /*12h */ + u32 data_xfer_len; /*14h */ + + union { + struct { + u32 opcode; /*18h */ + u8 mbox[12]; /*1Ch */ + u32 sgl_addr; /*28h */ + u32 sgl_len; /*32h */ + u32 pad; /*34h */ + } dcmd; + struct { + u32 sense_buf_lo; /*18h */ + u32 sense_buf_hi; /*1Ch */ + u8 cdb[16]; /*20h */ + u32 sgl_addr; /*30h */ + u32 sgl_len; /*34h */ + } pthru; + struct { + u8 pad[22]; /*18h */ + } gen; + }; +} __attribute__ ((packed)); + +struct mfi_ld_list_s { + u32 count; + u32 reserved_0; + struct { + u8 target; + u8 lun; + u16 seq; + u8 state; + u8 reserved_1[3]; + u64 size; + } lds[64]; +} __attribute__ ((packed)); + +#define MEGASAS_POLL_TIMEOUT 60000 // 60 seconds polling timeout + +struct megasas_lun_s { + struct drive_s drive; + struct megasas_cmd_frame *frame; + u32 iobase; + u16 pci_id; + u8 target; + u8 lun; +}; + +static int megasas_fire_cmd(u16 pci_id, u32 ioaddr, + struct megasas_cmd_frame *frame) +{ + u32 frame_addr = (u32)frame; + int frame_count = 1; + u8 cmd_state; + + dprintf(2, "Frame 0x%x\n", frame_addr); + if (pci_id == PCI_DEVICE_ID_LSI_SAS2004 || + pci_id == PCI_DEVICE_ID_LSI_SAS2008) { + outl(0, ioaddr + MFI_IQPH); + outl(frame_addr | frame_count << 1 | 1, ioaddr + MFI_IQPL); + } else if (pci_id == PCI_DEVICE_ID_DELL_PERC5 || + pci_id == PCI_DEVICE_ID_LSI_SAS1064R || + pci_id == PCI_DEVICE_ID_LSI_VERDE_ZCR) { + outl(frame_addr >> 3 | frame_count, ioaddr + MFI_IQP); + } else { + outl(frame_addr | frame_count << 1 | 1, ioaddr + MFI_IQP); + } + + u32 end = timer_calc(MEGASAS_POLL_TIMEOUT); + do { + for (;;) { + cmd_state = GET_LOWFLAT(frame->cmd_status); + if (cmd_state != 0xff) + break; + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } + } while (cmd_state == 0xff); + + if (cmd_state == 0 || cmd_state == 0x2d) + return 0; + dprintf(1, "ERROR: Frame 0x%x, status 0x%x\n", frame_addr, cmd_state); + return -1; +} + +int +megasas_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + struct megasas_lun_s *mlun_gf = + container_of(op->drive_gf, struct megasas_lun_s, drive); + u8 *cdb = cdbcmd; + struct megasas_cmd_frame *frame = GET_GLOBALFLAT(mlun_gf->frame); + u16 pci_id = GET_GLOBALFLAT(mlun_gf->pci_id); + int i; + + if (!CONFIG_MEGASAS) + return DISK_RET_EBADTRACK; + + memset_fl(frame, 0, sizeof(*frame)); + SET_LOWFLAT(frame->cmd, MFI_CMD_LD_SCSI_IO); + SET_LOWFLAT(frame->cmd_status, 0xFF); + SET_LOWFLAT(frame->target_id, GET_GLOBALFLAT(mlun_gf->target)); + SET_LOWFLAT(frame->lun, GET_GLOBALFLAT(mlun_gf->lun)); + SET_LOWFLAT(frame->flags, 0x0001); + SET_LOWFLAT(frame->data_xfer_len, op->count * blocksize); + SET_LOWFLAT(frame->cdb_len, 16); + + for (i = 0; i < 16; i++) { + SET_LOWFLAT(frame->pthru.cdb[i], cdb[i]); + } + dprintf(2, "pthru cmd 0x%x count %d bs %d\n", + cdb[0], op->count, blocksize); + + if (op->count) { + SET_LOWFLAT(frame->pthru.sgl_addr, (u32)op->buf_fl); + SET_LOWFLAT(frame->pthru.sgl_len, op->count * blocksize); + SET_LOWFLAT(frame->sge_count, 1); + } + SET_LOWFLAT(frame->context, (u32)frame); + + if (megasas_fire_cmd(pci_id, GET_GLOBALFLAT(mlun_gf->iobase), frame) == 0) + return DISK_RET_SUCCESS; + + dprintf(2, "pthru cmd 0x%x failed\n", cdb[0]); + return DISK_RET_EBADTRACK; +} + +static int +megasas_add_lun(struct pci_device *pci, u32 iobase, u8 target, u8 lun) +{ + struct megasas_lun_s *mlun = malloc_fseg(sizeof(*mlun)); + char *name; + int prio, ret = 0; + + if (!mlun) { + warn_noalloc(); + return -1; + } + memset(mlun, 0, sizeof(*mlun)); + mlun->drive.type = DTYPE_MEGASAS; + mlun->drive.cntl_id = pci->bdf; + mlun->pci_id = pci->device; + mlun->target = target; + mlun->lun = lun; + mlun->iobase = iobase; + mlun->frame = memalign_low(256, sizeof(struct megasas_cmd_frame)); + if (!mlun->frame) { + warn_noalloc(); + free(mlun); + return -1; + } + name = znprintf(36, "MegaRAID SAS (PCI %02x:%02x.%x) LD %d:%d", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), + pci_bdf_to_fn(pci->bdf), target, lun); + prio = bootprio_find_scsi_device(pci, target, lun); + ret = scsi_drive_setup(&mlun->drive, name, prio); + free(name); + if (ret) { + free(mlun->frame); + free(mlun); + ret = -1; + } + + return ret; +} + +static void megasas_scan_target(struct pci_device *pci, u32 iobase) +{ + struct mfi_ld_list_s ld_list; + struct megasas_cmd_frame *frame = memalign_tmp(256, sizeof(*frame)); + int i; + + memset(&ld_list, 0, sizeof(ld_list)); + memset_fl(frame, 0, sizeof(*frame)); + + frame->cmd = MFI_CMD_DCMD; + frame->cmd_status = 0xFF; + frame->sge_count = 1; + frame->flags = 0x0011; + frame->data_xfer_len = sizeof(ld_list); + frame->dcmd.opcode = 0x03010000; + frame->dcmd.sgl_addr = (u32)MAKE_FLATPTR(GET_SEG(SS), &ld_list); + frame->dcmd.sgl_len = sizeof(ld_list); + frame->context = (u32)frame; + + if (megasas_fire_cmd(pci->device, iobase, frame) == 0) { + dprintf(2, "%d LD found\n", ld_list.count); + for (i = 0; i < ld_list.count; i++) { + dprintf(2, "LD %d:%d state 0x%x\n", + ld_list.lds[i].target, ld_list.lds[i].lun, + ld_list.lds[i].state); + if (ld_list.lds[i].state != 0) { + megasas_add_lun(pci, iobase, + ld_list.lds[i].target, ld_list.lds[i].lun); + } + } + } +} + +static int megasas_transition_to_ready(struct pci_device *pci, u32 ioaddr) +{ + u32 fw_state = 0, new_state, mfi_flags = 0; + + if (pci->device == PCI_DEVICE_ID_LSI_SAS1064R || + pci->device == PCI_DEVICE_ID_DELL_PERC5) + new_state = inl(ioaddr + MFI_OMSG0) & MFI_STATE_MASK; + else + new_state = inl(ioaddr + MFI_OSP0) & MFI_STATE_MASK; + + while (fw_state != new_state) { + switch (new_state) { + case MFI_STATE_FAULT: + dprintf(1, "ERROR: fw in fault state\n"); + return -1; + break; + case MFI_STATE_WAIT_HANDSHAKE: + mfi_flags = 0x08; + /* fallthrough */ + case MFI_STATE_BOOT_MESSAGE_PENDING: + mfi_flags |= 0x10; + if (pci->device == PCI_DEVICE_ID_LSI_SAS2004 || + pci->device == PCI_DEVICE_ID_LSI_SAS2008 || + pci->device == PCI_DEVICE_ID_LSI_SAS2208 || + pci->device == PCI_DEVICE_ID_LSI_SAS3108) { + outl(ioaddr + MFI_DB, mfi_flags); + } else { + outl(ioaddr + MFI_IDB, mfi_flags); + } + break; + case MFI_STATE_OPERATIONAL: + mfi_flags = 0x07; + if (pci->device == PCI_DEVICE_ID_LSI_SAS2004 || + pci->device == PCI_DEVICE_ID_LSI_SAS2008 || + pci->device == PCI_DEVICE_ID_LSI_SAS2208 || + pci->device == PCI_DEVICE_ID_LSI_SAS3108) { + outl(ioaddr + MFI_DB, mfi_flags); + if (pci->device == PCI_DEVICE_ID_LSI_SAS2208 || + pci->device == PCI_DEVICE_ID_LSI_SAS3108) { + int j = 0; + u32 doorbell; + + while (j < MEGASAS_POLL_TIMEOUT) { + doorbell = inl(ioaddr + MFI_DB) & 1; + if (!doorbell) + break; + msleep(20); + j++; + } + } + } else { + outw(ioaddr + MFI_IDB, mfi_flags); + } + break; + case MFI_STATE_READY: + dprintf(2, "MegaRAID SAS fw ready\n"); + return 0; + } + // The current state should not last longer than poll timeout + u32 end = timer_calc(MEGASAS_POLL_TIMEOUT); + for (;;) { + if (timer_check(end)) { + break; + } + yield(); + fw_state = new_state; + if (pci->device == PCI_DEVICE_ID_LSI_SAS1064R || + pci->device == PCI_DEVICE_ID_DELL_PERC5) + new_state = inl(ioaddr + MFI_OMSG0) & MFI_STATE_MASK; + else + new_state = inl(ioaddr + MFI_OSP0) & MFI_STATE_MASK; + if (new_state != fw_state) { + break; + } + } + } + dprintf(1, "ERROR: fw in state %x\n", new_state & MFI_STATE_MASK); + return -1; +} + +static void +init_megasas(struct pci_device *pci) +{ + u16 bdf = pci->bdf; + u32 iobase = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_2) + & PCI_BASE_ADDRESS_IO_MASK; + + dprintf(1, "found MegaRAID SAS at %02x:%02x.%x, io @ %x\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), + pci_bdf_to_fn(bdf), iobase); + + pci_config_maskw(pci->bdf, PCI_COMMAND, 0, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + // reset + if (megasas_transition_to_ready(pci, iobase) == 0) + megasas_scan_target(pci, iobase); + + return; +} + +void +megasas_setup(void) +{ + ASSERT32FLAT(); + if (!CONFIG_MEGASAS) + return; + + dprintf(3, "init megasas\n"); + + struct pci_device *pci; + foreachpci(pci) { + if (pci->vendor != PCI_VENDOR_ID_LSI_LOGIC && + pci->vendor != PCI_VENDOR_ID_DELL) + continue; + if (pci->device == PCI_DEVICE_ID_LSI_SAS1064R || + pci->device == PCI_DEVICE_ID_LSI_SAS1078 || + pci->device == PCI_DEVICE_ID_LSI_SAS1078DE || + pci->device == PCI_DEVICE_ID_LSI_SAS2108 || + pci->device == PCI_DEVICE_ID_LSI_SAS2108E || + pci->device == PCI_DEVICE_ID_LSI_SAS2004 || + pci->device == PCI_DEVICE_ID_LSI_SAS2008 || + pci->device == PCI_DEVICE_ID_LSI_VERDE_ZCR || + pci->device == PCI_DEVICE_ID_DELL_PERC5 || + pci->device == PCI_DEVICE_ID_LSI_SAS2208 || + pci->device == PCI_DEVICE_ID_LSI_SAS3108) + init_megasas(pci); + } +} diff --git a/roms/seabios/src/hw/megasas.h b/roms/seabios/src/hw/megasas.h new file mode 100644 index 000000000..124042e1c --- /dev/null +++ b/roms/seabios/src/hw/megasas.h @@ -0,0 +1,8 @@ +#ifndef __MEGASAS_H +#define __MEGASAS_H + +struct disk_op_s; +int megasas_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +void megasas_setup(void); + +#endif /* __MEGASAS_H */ diff --git a/roms/seabios/src/hw/pci.c b/roms/seabios/src/hw/pci.c new file mode 100644 index 000000000..6c9aa81a7 --- /dev/null +++ b/roms/seabios/src/hw/pci.c @@ -0,0 +1,282 @@ +// PCI config space access functions. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_* +#include "farptr.h" // MAKE_FLATPTR +#include "malloc.h" // malloc_tmp +#include "output.h" // dprintf +#include "pci.h" // pci_config_writel +#include "pci_ids.h" // PCI_CLASS_DISPLAY_VGA +#include "pci_regs.h" // PCI_VENDOR_ID +#include "romfile.h" // romfile_loadint +#include "stacks.h" // call32 +#include "string.h" // memset +#include "util.h" // udelay +#include "x86.h" // readl + +void pci_config_writel(u16 bdf, u32 addr, u32 val) +{ + outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD); + outl(val, PORT_PCI_DATA); +} + +void pci_config_writew(u16 bdf, u32 addr, u16 val) +{ + outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD); + outw(val, PORT_PCI_DATA + (addr & 2)); +} + +void pci_config_writeb(u16 bdf, u32 addr, u8 val) +{ + outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD); + outb(val, PORT_PCI_DATA + (addr & 3)); +} + +u32 pci_config_readl(u16 bdf, u32 addr) +{ + outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD); + return inl(PORT_PCI_DATA); +} + +u16 pci_config_readw(u16 bdf, u32 addr) +{ + outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD); + return inw(PORT_PCI_DATA + (addr & 2)); +} + +u8 pci_config_readb(u16 bdf, u32 addr) +{ + outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD); + return inb(PORT_PCI_DATA + (addr & 3)); +} + +void +pci_config_maskw(u16 bdf, u32 addr, u16 off, u16 on) +{ + u16 val = pci_config_readw(bdf, addr); + val = (val & ~off) | on; + pci_config_writew(bdf, addr, val); +} + +// Helper function for foreachbdf() macro - return next device +int +pci_next(int bdf, int bus) +{ + if (pci_bdf_to_fn(bdf) == 0 + && (pci_config_readb(bdf, PCI_HEADER_TYPE) & 0x80) == 0) + // Last found device wasn't a multi-function device - skip to + // the next device. + bdf += 8; + else + bdf += 1; + + for (;;) { + if (pci_bdf_to_bus(bdf) != bus) + return -1; + + u16 v = pci_config_readw(bdf, PCI_VENDOR_ID); + if (v != 0x0000 && v != 0xffff) + // Device is present. + return bdf; + + if (pci_bdf_to_fn(bdf) == 0) + bdf += 8; + else + bdf += 1; + } +} + +struct hlist_head PCIDevices VARVERIFY32INIT; +int MaxPCIBus VARFSEG; + +// Check if PCI is available at all +int +pci_probe_host(void) +{ + outl(0x80000000, PORT_PCI_CMD); + if (inl(PORT_PCI_CMD) != 0x80000000) { + dprintf(1, "Detected non-PCI system\n"); + return -1; + } + return 0; +} + +// Find all PCI devices and populate PCIDevices linked list. +void +pci_probe_devices(void) +{ + dprintf(3, "PCI probe\n"); + struct pci_device *busdevs[256]; + memset(busdevs, 0, sizeof(busdevs)); + struct hlist_node **pprev = &PCIDevices.first; + int extraroots = romfile_loadint("etc/extra-pci-roots", 0); + int bus = -1, lastbus = 0, rootbuses = 0, count=0; + while (bus < 0xff && (bus < MaxPCIBus || rootbuses < extraroots)) { + bus++; + int bdf; + foreachbdf(bdf, bus) { + // Create new pci_device struct and add to list. + struct pci_device *dev = malloc_tmp(sizeof(*dev)); + if (!dev) { + warn_noalloc(); + return; + } + memset(dev, 0, sizeof(*dev)); + hlist_add(&dev->node, pprev); + pprev = &dev->node.next; + count++; + + // Find parent device. + int rootbus; + struct pci_device *parent = busdevs[bus]; + if (!parent) { + if (bus != lastbus) + rootbuses++; + lastbus = bus; + rootbus = rootbuses; + if (bus > MaxPCIBus) + MaxPCIBus = bus; + } else { + rootbus = parent->rootbus; + } + + // Populate pci_device info. + dev->bdf = bdf; + dev->parent = parent; + dev->rootbus = rootbus; + u32 vendev = pci_config_readl(bdf, PCI_VENDOR_ID); + dev->vendor = vendev & 0xffff; + dev->device = vendev >> 16; + u32 classrev = pci_config_readl(bdf, PCI_CLASS_REVISION); + dev->class = classrev >> 16; + dev->prog_if = classrev >> 8; + dev->revision = classrev & 0xff; + dev->header_type = pci_config_readb(bdf, PCI_HEADER_TYPE); + u8 v = dev->header_type & 0x7f; + if (v == PCI_HEADER_TYPE_BRIDGE || v == PCI_HEADER_TYPE_CARDBUS) { + u8 secbus = pci_config_readb(bdf, PCI_SECONDARY_BUS); + dev->secondary_bus = secbus; + if (secbus > bus && !busdevs[secbus]) + busdevs[secbus] = dev; + if (secbus > MaxPCIBus) + MaxPCIBus = secbus; + } + dprintf(4, "PCI device %02x:%02x.%x (vd=%04x:%04x c=%04x)\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf) + , pci_bdf_to_fn(bdf) + , dev->vendor, dev->device, dev->class); + } + } + dprintf(1, "Found %d PCI devices (max PCI bus is %02x)\n", count, MaxPCIBus); +} + +// Search for a device with the specified vendor and device ids. +struct pci_device * +pci_find_device(u16 vendid, u16 devid) +{ + struct pci_device *pci; + foreachpci(pci) { + if (pci->vendor == vendid && pci->device == devid) + return pci; + } + return NULL; +} + +// Search for a device with the specified class id. +struct pci_device * +pci_find_class(u16 classid) +{ + struct pci_device *pci; + foreachpci(pci) { + if (pci->class == classid) + return pci; + } + return NULL; +} + +int pci_init_device(const struct pci_device_id *ids + , struct pci_device *pci, void *arg) +{ + while (ids->vendid || ids->class_mask) { + if ((ids->vendid == PCI_ANY_ID || ids->vendid == pci->vendor) && + (ids->devid == PCI_ANY_ID || ids->devid == pci->device) && + !((ids->class ^ pci->class) & ids->class_mask)) { + if (ids->func) + ids->func(pci, arg); + return 0; + } + ids++; + } + return -1; +} + +struct pci_device * +pci_find_init_device(const struct pci_device_id *ids, void *arg) +{ + struct pci_device *pci; + foreachpci(pci) { + if (pci_init_device(ids, pci, arg) == 0) + return pci; + } + return NULL; +} + +void +pci_reboot(void) +{ + u8 v = inb(PORT_PCI_REBOOT) & ~6; + outb(v|2, PORT_PCI_REBOOT); /* Request hard reset */ + udelay(50); + outb(v|6, PORT_PCI_REBOOT); /* Actually do the reset */ + udelay(50); +} + +// helper functions to access pci mmio bars from real mode + +u32 VISIBLE32FLAT +pci_readl_32(u32 addr) +{ + dprintf(9, "32: pci read : %x\n", addr); + return readl((void*)addr); +} + +u32 pci_readl(u32 addr) +{ + if (MODESEGMENT) { + dprintf(9, "16: pci read : %x\n", addr); + extern void _cfunc32flat_pci_readl_32(u32 addr); + return call32(_cfunc32flat_pci_readl_32, addr, -1); + } else { + return pci_readl_32(addr); + } +} + +struct reg32 { + u32 addr; + u32 data; +}; + +void VISIBLE32FLAT +pci_writel_32(struct reg32 *reg32) +{ + dprintf(9, "32: pci write: %x, %x (%p)\n", reg32->addr, reg32->data, reg32); + writel((void*)(reg32->addr), reg32->data); +} + +void pci_writel(u32 addr, u32 val) +{ + struct reg32 reg32 = { .addr = addr, .data = val }; + if (MODESEGMENT) { + dprintf(9, "16: pci write: %x, %x (%x:%p)\n", + reg32.addr, reg32.data, GET_SEG(SS), ®32); + void *flatptr = MAKE_FLATPTR(GET_SEG(SS), ®32); + extern void _cfunc32flat_pci_writel_32(struct reg32 *reg32); + call32(_cfunc32flat_pci_writel_32, (u32)flatptr, -1); + } else { + pci_writel_32(®32); + } +} diff --git a/roms/seabios/src/hw/pci.h b/roms/seabios/src/hw/pci.h new file mode 100644 index 000000000..9c7351d47 --- /dev/null +++ b/roms/seabios/src/hw/pci.h @@ -0,0 +1,125 @@ +#ifndef __PCI_H +#define __PCI_H + +#include "types.h" // u32 +#include "list.h" // hlist_node + +#define PORT_PCI_CMD 0x0cf8 +#define PORT_PCI_REBOOT 0x0cf9 +#define PORT_PCI_DATA 0x0cfc + +#define PCI_ROM_SLOT 6 +#define PCI_NUM_REGIONS 7 +#define PCI_BRIDGE_NUM_REGIONS 2 + +static inline u8 pci_bdf_to_bus(u16 bdf) { + return bdf >> 8; +} +static inline u8 pci_bdf_to_devfn(u16 bdf) { + return bdf & 0xff; +} +static inline u16 pci_bdf_to_busdev(u16 bdf) { + return bdf & ~0x07; +} +static inline u8 pci_bdf_to_dev(u16 bdf) { + return (bdf >> 3) & 0x1f; +} +static inline u8 pci_bdf_to_fn(u16 bdf) { + return bdf & 0x07; +} +static inline u16 pci_to_bdf(int bus, int dev, int fn) { + return (bus<<8) | (dev<<3) | fn; +} +static inline u16 pci_bus_devfn_to_bdf(int bus, u16 devfn) { + return (bus << 8) | devfn; +} + +void pci_config_writel(u16 bdf, u32 addr, u32 val); +void pci_config_writew(u16 bdf, u32 addr, u16 val); +void pci_config_writeb(u16 bdf, u32 addr, u8 val); +u32 pci_config_readl(u16 bdf, u32 addr); +u16 pci_config_readw(u16 bdf, u32 addr); +u8 pci_config_readb(u16 bdf, u32 addr); +void pci_config_maskw(u16 bdf, u32 addr, u16 off, u16 on); + +struct pci_device *pci_find_device(u16 vendid, u16 devid); +struct pci_device *pci_find_class(u16 classid); + +struct pci_device { + u16 bdf; + u8 rootbus; + struct hlist_node node; + struct pci_device *parent; + + // Configuration space device information + u16 vendor, device; + u16 class; + u8 prog_if, revision; + u8 header_type; + u8 secondary_bus; + + // Local information on device. + int have_driver; +}; +extern u64 pcimem_start, pcimem_end; +extern u64 pcimem64_start, pcimem64_end; +extern struct hlist_head PCIDevices; +extern int MaxPCIBus; +int pci_probe_host(void); +void pci_probe_devices(void); +static inline u32 pci_classprog(struct pci_device *pci) { + return (pci->class << 8) | pci->prog_if; +} + +#define foreachpci(PCI) \ + hlist_for_each_entry(PCI, &PCIDevices, node) + +int pci_next(int bdf, int bus); +#define foreachbdf(BDF, BUS) \ + for (BDF=pci_next(pci_bus_devfn_to_bdf((BUS), 0)-1, (BUS)) \ + ; BDF >= 0 \ + ; BDF=pci_next(BDF, (BUS))) + +#define PCI_ANY_ID (~0) +struct pci_device_id { + u32 vendid; + u32 devid; + u32 class; + u32 class_mask; + void (*func)(struct pci_device *pci, void *arg); +}; + +#define PCI_DEVICE(vendor_id, device_id, init_func) \ + { \ + .vendid = (vendor_id), \ + .devid = (device_id), \ + .class = PCI_ANY_ID, \ + .class_mask = 0, \ + .func = (init_func) \ + } + +#define PCI_DEVICE_CLASS(vendor_id, device_id, class_code, init_func) \ + { \ + .vendid = (vendor_id), \ + .devid = (device_id), \ + .class = (class_code), \ + .class_mask = ~0, \ + .func = (init_func) \ + } + +#define PCI_DEVICE_END \ + { \ + .vendid = 0, \ + } + +int pci_init_device(const struct pci_device_id *ids + , struct pci_device *pci, void *arg); +struct pci_device *pci_find_init_device(const struct pci_device_id *ids + , void *arg); +void pci_reboot(void); + +// helper functions to access pci mmio bars from real mode +u32 pci_readl(u32 addr); +void pci_writel(u32 addr, u32 val); + +#endif diff --git a/roms/seabios/src/hw/pci_ids.h b/roms/seabios/src/hw/pci_ids.h new file mode 100644 index 000000000..1cd4f7269 --- /dev/null +++ b/roms/seabios/src/hw/pci_ids.h @@ -0,0 +1,2623 @@ +/* + * PCI Class, Vendor and Device IDs + * + * Please keep sorted. + */ + +/* Device classes and subclasses */ + +#define PCI_CLASS_NOT_DEFINED 0x0000 +#define PCI_CLASS_NOT_DEFINED_VGA 0x0001 + +#define PCI_BASE_CLASS_STORAGE 0x01 +#define PCI_CLASS_STORAGE_SCSI 0x0100 +#define PCI_CLASS_STORAGE_IDE 0x0101 +#define PCI_CLASS_STORAGE_FLOPPY 0x0102 +#define PCI_CLASS_STORAGE_IPI 0x0103 +#define PCI_CLASS_STORAGE_RAID 0x0104 +#define PCI_CLASS_STORAGE_SATA 0x0106 +#define PCI_CLASS_STORAGE_SATA_AHCI 0x010601 +#define PCI_CLASS_STORAGE_SAS 0x0107 +#define PCI_CLASS_STORAGE_OTHER 0x0180 + +#define PCI_BASE_CLASS_NETWORK 0x02 +#define PCI_CLASS_NETWORK_ETHERNET 0x0200 +#define PCI_CLASS_NETWORK_TOKEN_RING 0x0201 +#define PCI_CLASS_NETWORK_FDDI 0x0202 +#define PCI_CLASS_NETWORK_ATM 0x0203 +#define PCI_CLASS_NETWORK_OTHER 0x0280 + +#define PCI_BASE_CLASS_DISPLAY 0x03 +#define PCI_CLASS_DISPLAY_VGA 0x0300 +#define PCI_CLASS_DISPLAY_XGA 0x0301 +#define PCI_CLASS_DISPLAY_3D 0x0302 +#define PCI_CLASS_DISPLAY_OTHER 0x0380 + +#define PCI_BASE_CLASS_MULTIMEDIA 0x04 +#define PCI_CLASS_MULTIMEDIA_VIDEO 0x0400 +#define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401 +#define PCI_CLASS_MULTIMEDIA_PHONE 0x0402 +#define PCI_CLASS_MULTIMEDIA_OTHER 0x0480 + +#define PCI_BASE_CLASS_MEMORY 0x05 +#define PCI_CLASS_MEMORY_RAM 0x0500 +#define PCI_CLASS_MEMORY_FLASH 0x0501 +#define PCI_CLASS_MEMORY_OTHER 0x0580 + +#define PCI_BASE_CLASS_BRIDGE 0x06 +#define PCI_CLASS_BRIDGE_HOST 0x0600 +#define PCI_CLASS_BRIDGE_ISA 0x0601 +#define PCI_CLASS_BRIDGE_EISA 0x0602 +#define PCI_CLASS_BRIDGE_MC 0x0603 +#define PCI_CLASS_BRIDGE_PCI 0x0604 +#define PCI_CLASS_BRIDGE_PCMCIA 0x0605 +#define PCI_CLASS_BRIDGE_NUBUS 0x0606 +#define PCI_CLASS_BRIDGE_CARDBUS 0x0607 +#define PCI_CLASS_BRIDGE_RACEWAY 0x0608 +#define PCI_CLASS_BRIDGE_OTHER 0x0680 + +#define PCI_BASE_CLASS_COMMUNICATION 0x07 +#define PCI_CLASS_COMMUNICATION_SERIAL 0x0700 +#define PCI_CLASS_COMMUNICATION_PARALLEL 0x0701 +#define PCI_CLASS_COMMUNICATION_MULTISERIAL 0x0702 +#define PCI_CLASS_COMMUNICATION_MODEM 0x0703 +#define PCI_CLASS_COMMUNICATION_OTHER 0x0780 + +#define PCI_BASE_CLASS_SYSTEM 0x08 +#define PCI_CLASS_SYSTEM_PIC 0x0800 +#define PCI_CLASS_SYSTEM_PIC_IOAPIC 0x080010 +#define PCI_CLASS_SYSTEM_PIC_IOXAPIC 0x080020 +#define PCI_CLASS_SYSTEM_DMA 0x0801 +#define PCI_CLASS_SYSTEM_TIMER 0x0802 +#define PCI_CLASS_SYSTEM_RTC 0x0803 +#define PCI_CLASS_SYSTEM_PCI_HOTPLUG 0x0804 +#define PCI_CLASS_SYSTEM_SDHCI 0x0805 +#define PCI_CLASS_SYSTEM_OTHER 0x0880 + +#define PCI_BASE_CLASS_INPUT 0x09 +#define PCI_CLASS_INPUT_KEYBOARD 0x0900 +#define PCI_CLASS_INPUT_PEN 0x0901 +#define PCI_CLASS_INPUT_MOUSE 0x0902 +#define PCI_CLASS_INPUT_SCANNER 0x0903 +#define PCI_CLASS_INPUT_GAMEPORT 0x0904 +#define PCI_CLASS_INPUT_OTHER 0x0980 + +#define PCI_BASE_CLASS_DOCKING 0x0a +#define PCI_CLASS_DOCKING_GENERIC 0x0a00 +#define PCI_CLASS_DOCKING_OTHER 0x0a80 + +#define PCI_BASE_CLASS_PROCESSOR 0x0b +#define PCI_CLASS_PROCESSOR_386 0x0b00 +#define PCI_CLASS_PROCESSOR_486 0x0b01 +#define PCI_CLASS_PROCESSOR_PENTIUM 0x0b02 +#define PCI_CLASS_PROCESSOR_ALPHA 0x0b10 +#define PCI_CLASS_PROCESSOR_POWERPC 0x0b20 +#define PCI_CLASS_PROCESSOR_MIPS 0x0b30 +#define PCI_CLASS_PROCESSOR_CO 0x0b40 + +#define PCI_BASE_CLASS_SERIAL 0x0c +#define PCI_CLASS_SERIAL_FIREWIRE 0x0c00 +#define PCI_CLASS_SERIAL_FIREWIRE_OHCI 0x0c0010 +#define PCI_CLASS_SERIAL_ACCESS 0x0c01 +#define PCI_CLASS_SERIAL_SSA 0x0c02 +#define PCI_CLASS_SERIAL_USB 0x0c03 +#define PCI_CLASS_SERIAL_USB_UHCI 0x0c0300 +#define PCI_CLASS_SERIAL_USB_OHCI 0x0c0310 +#define PCI_CLASS_SERIAL_USB_EHCI 0x0c0320 +#define PCI_CLASS_SERIAL_USB_XHCI 0x0c0330 +#define PCI_CLASS_SERIAL_FIBER 0x0c04 +#define PCI_CLASS_SERIAL_SMBUS 0x0c05 + +#define PCI_BASE_CLASS_WIRELESS 0x0d +#define PCI_CLASS_WIRELESS_RF_CONTROLLER 0x0d10 +#define PCI_CLASS_WIRELESS_WHCI 0x0d1010 + +#define PCI_BASE_CLASS_INTELLIGENT 0x0e +#define PCI_CLASS_INTELLIGENT_I2O 0x0e00 + +#define PCI_BASE_CLASS_SATELLITE 0x0f +#define PCI_CLASS_SATELLITE_TV 0x0f00 +#define PCI_CLASS_SATELLITE_AUDIO 0x0f01 +#define PCI_CLASS_SATELLITE_VOICE 0x0f03 +#define PCI_CLASS_SATELLITE_DATA 0x0f04 + +#define PCI_BASE_CLASS_CRYPT 0x10 +#define PCI_CLASS_CRYPT_NETWORK 0x1000 +#define PCI_CLASS_CRYPT_ENTERTAINMENT 0x1001 +#define PCI_CLASS_CRYPT_OTHER 0x1080 + +#define PCI_BASE_CLASS_SIGNAL_PROCESSING 0x11 +#define PCI_CLASS_SP_DPIO 0x1100 +#define PCI_CLASS_SP_OTHER 0x1180 + +#define PCI_CLASS_OTHERS 0xff + +/* Vendors and devices. Sort key: vendor first, device next. */ + +#define PCI_VENDOR_ID_TTTECH 0x0357 +#define PCI_DEVICE_ID_TTTECH_MC322 0x000a + +#define PCI_VENDOR_ID_DYNALINK 0x0675 +#define PCI_DEVICE_ID_DYNALINK_IS64PH 0x1702 + +#define PCI_VENDOR_ID_BERKOM 0x0871 +#define PCI_DEVICE_ID_BERKOM_A1T 0xffa1 +#define PCI_DEVICE_ID_BERKOM_T_CONCEPT 0xffa2 +#define PCI_DEVICE_ID_BERKOM_A4T 0xffa4 +#define PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO 0xffa8 + +#define PCI_VENDOR_ID_COMPAQ 0x0e11 +#define PCI_DEVICE_ID_COMPAQ_TOKENRING 0x0508 +#define PCI_DEVICE_ID_COMPAQ_TACHYON 0xa0fc +#define PCI_DEVICE_ID_COMPAQ_SMART2P 0xae10 +#define PCI_DEVICE_ID_COMPAQ_NETEL100 0xae32 +#define PCI_DEVICE_ID_COMPAQ_NETEL10 0xae34 +#define PCI_DEVICE_ID_COMPAQ_TRIFLEX_IDE 0xae33 +#define PCI_DEVICE_ID_COMPAQ_NETFLEX3I 0xae35 +#define PCI_DEVICE_ID_COMPAQ_NETEL100D 0xae40 +#define PCI_DEVICE_ID_COMPAQ_NETEL100PI 0xae43 +#define PCI_DEVICE_ID_COMPAQ_NETEL100I 0xb011 +#define PCI_DEVICE_ID_COMPAQ_CISS 0xb060 +#define PCI_DEVICE_ID_COMPAQ_CISSB 0xb178 +#define PCI_DEVICE_ID_COMPAQ_CISSC 0x46 +#define PCI_DEVICE_ID_COMPAQ_THUNDER 0xf130 +#define PCI_DEVICE_ID_COMPAQ_NETFLEX3B 0xf150 + +#define PCI_VENDOR_ID_NCR 0x1000 +#define PCI_VENDOR_ID_LSI_LOGIC 0x1000 +#define PCI_DEVICE_ID_NCR_53C810 0x0001 +#define PCI_DEVICE_ID_NCR_53C820 0x0002 +#define PCI_DEVICE_ID_NCR_53C825 0x0003 +#define PCI_DEVICE_ID_NCR_53C815 0x0004 +#define PCI_DEVICE_ID_LSI_53C810AP 0x0005 +#define PCI_DEVICE_ID_NCR_53C860 0x0006 +#define PCI_DEVICE_ID_LSI_53C1510 0x000a +#define PCI_DEVICE_ID_NCR_53C896 0x000b +#define PCI_DEVICE_ID_NCR_53C895 0x000c +#define PCI_DEVICE_ID_NCR_53C885 0x000d +#define PCI_DEVICE_ID_NCR_53C875 0x000f +#define PCI_DEVICE_ID_NCR_53C1510 0x0010 +#define PCI_DEVICE_ID_LSI_53C895A 0x0012 +#define PCI_DEVICE_ID_LSI_53C875A 0x0013 +#define PCI_DEVICE_ID_LSI_53C1010_33 0x0020 +#define PCI_DEVICE_ID_LSI_53C1010_66 0x0021 +#define PCI_DEVICE_ID_LSI_53C1030 0x0030 +#define PCI_DEVICE_ID_LSI_1030_53C1035 0x0032 +#define PCI_DEVICE_ID_LSI_53C1035 0x0040 +#define PCI_DEVICE_ID_NCR_53C875J 0x008f +#define PCI_DEVICE_ID_LSI_FC909 0x0621 +#define PCI_DEVICE_ID_LSI_FC929 0x0622 +#define PCI_DEVICE_ID_LSI_FC929_LAN 0x0623 +#define PCI_DEVICE_ID_LSI_FC919 0x0624 +#define PCI_DEVICE_ID_LSI_FC919_LAN 0x0625 +#define PCI_DEVICE_ID_LSI_FC929X 0x0626 +#define PCI_DEVICE_ID_LSI_FC939X 0x0642 +#define PCI_DEVICE_ID_LSI_FC949X 0x0640 +#define PCI_DEVICE_ID_LSI_FC949ES 0x0646 +#define PCI_DEVICE_ID_LSI_FC919X 0x0628 +#define PCI_DEVICE_ID_NCR_YELLOWFIN 0x0701 +#define PCI_DEVICE_ID_LSI_61C102 0x0901 +#define PCI_DEVICE_ID_LSI_63C815 0x1000 +#define PCI_DEVICE_ID_LSI_SAS1064 0x0050 +#define PCI_DEVICE_ID_LSI_SAS1064R 0x0411 +#define PCI_DEVICE_ID_LSI_VERDE_ZCR 0x0413 +#define PCI_DEVICE_ID_LSI_SAS1066 0x005E +#define PCI_DEVICE_ID_LSI_SAS1068 0x0054 +#define PCI_DEVICE_ID_LSI_SAS1064A 0x005C +#define PCI_DEVICE_ID_LSI_SAS1064E 0x0056 +#define PCI_DEVICE_ID_LSI_SAS1066E 0x005A +#define PCI_DEVICE_ID_LSI_SAS1068E 0x0058 +#define PCI_DEVICE_ID_LSI_SAS1078 0x0060 +#define PCI_DEVICE_ID_LSI_SAS1078DE 0x007C +#define PCI_DEVICE_ID_LSI_SAS2108E 0x0078 +#define PCI_DEVICE_ID_LSI_SAS2108 0x0079 +#define PCI_DEVICE_ID_LSI_SAS2208 0x005B +#define PCI_DEVICE_ID_LSI_SAS3108 0x005D +#define PCI_DEVICE_ID_LSI_SAS2004 0x0071 +#define PCI_DEVICE_ID_LSI_SAS2008 0x0073 + +#define PCI_VENDOR_ID_ATI 0x1002 +/* Mach64 */ +#define PCI_DEVICE_ID_ATI_68800 0x4158 +#define PCI_DEVICE_ID_ATI_215CT222 0x4354 +#define PCI_DEVICE_ID_ATI_210888CX 0x4358 +#define PCI_DEVICE_ID_ATI_215ET222 0x4554 +/* Mach64 / Rage */ +#define PCI_DEVICE_ID_ATI_215GB 0x4742 +#define PCI_DEVICE_ID_ATI_215GD 0x4744 +#define PCI_DEVICE_ID_ATI_215GI 0x4749 +#define PCI_DEVICE_ID_ATI_215GP 0x4750 +#define PCI_DEVICE_ID_ATI_215GQ 0x4751 +#define PCI_DEVICE_ID_ATI_215XL 0x4752 +#define PCI_DEVICE_ID_ATI_215GT 0x4754 +#define PCI_DEVICE_ID_ATI_215GTB 0x4755 +#define PCI_DEVICE_ID_ATI_215_IV 0x4756 +#define PCI_DEVICE_ID_ATI_215_IW 0x4757 +#define PCI_DEVICE_ID_ATI_215_IZ 0x475A +#define PCI_DEVICE_ID_ATI_210888GX 0x4758 +#define PCI_DEVICE_ID_ATI_215_LB 0x4c42 +#define PCI_DEVICE_ID_ATI_215_LD 0x4c44 +#define PCI_DEVICE_ID_ATI_215_LG 0x4c47 +#define PCI_DEVICE_ID_ATI_215_LI 0x4c49 +#define PCI_DEVICE_ID_ATI_215_LM 0x4c4D +#define PCI_DEVICE_ID_ATI_215_LN 0x4c4E +#define PCI_DEVICE_ID_ATI_215_LR 0x4c52 +#define PCI_DEVICE_ID_ATI_215_LS 0x4c53 +#define PCI_DEVICE_ID_ATI_264_LT 0x4c54 +/* Mach64 VT */ +#define PCI_DEVICE_ID_ATI_264VT 0x5654 +#define PCI_DEVICE_ID_ATI_264VU 0x5655 +#define PCI_DEVICE_ID_ATI_264VV 0x5656 +/* Rage128 GL */ +#define PCI_DEVICE_ID_ATI_RAGE128_RE 0x5245 +#define PCI_DEVICE_ID_ATI_RAGE128_RF 0x5246 +#define PCI_DEVICE_ID_ATI_RAGE128_RG 0x5247 +/* Rage128 VR */ +#define PCI_DEVICE_ID_ATI_RAGE128_RK 0x524b +#define PCI_DEVICE_ID_ATI_RAGE128_RL 0x524c +#define PCI_DEVICE_ID_ATI_RAGE128_SE 0x5345 +#define PCI_DEVICE_ID_ATI_RAGE128_SF 0x5346 +#define PCI_DEVICE_ID_ATI_RAGE128_SG 0x5347 +#define PCI_DEVICE_ID_ATI_RAGE128_SH 0x5348 +#define PCI_DEVICE_ID_ATI_RAGE128_SK 0x534b +#define PCI_DEVICE_ID_ATI_RAGE128_SL 0x534c +#define PCI_DEVICE_ID_ATI_RAGE128_SM 0x534d +#define PCI_DEVICE_ID_ATI_RAGE128_SN 0x534e +/* Rage128 Ultra */ +#define PCI_DEVICE_ID_ATI_RAGE128_TF 0x5446 +#define PCI_DEVICE_ID_ATI_RAGE128_TL 0x544c +#define PCI_DEVICE_ID_ATI_RAGE128_TR 0x5452 +#define PCI_DEVICE_ID_ATI_RAGE128_TS 0x5453 +#define PCI_DEVICE_ID_ATI_RAGE128_TT 0x5454 +#define PCI_DEVICE_ID_ATI_RAGE128_TU 0x5455 +/* Rage128 M3 */ +#define PCI_DEVICE_ID_ATI_RAGE128_LE 0x4c45 +#define PCI_DEVICE_ID_ATI_RAGE128_LF 0x4c46 +/* Rage128 M4 */ +#define PCI_DEVICE_ID_ATI_RAGE128_MF 0x4d46 +#define PCI_DEVICE_ID_ATI_RAGE128_ML 0x4d4c +/* Rage128 Pro GL */ +#define PCI_DEVICE_ID_ATI_RAGE128_PA 0x5041 +#define PCI_DEVICE_ID_ATI_RAGE128_PB 0x5042 +#define PCI_DEVICE_ID_ATI_RAGE128_PC 0x5043 +#define PCI_DEVICE_ID_ATI_RAGE128_PD 0x5044 +#define PCI_DEVICE_ID_ATI_RAGE128_PE 0x5045 +#define PCI_DEVICE_ID_ATI_RAGE128_PF 0x5046 +/* Rage128 Pro VR */ +#define PCI_DEVICE_ID_ATI_RAGE128_PG 0x5047 +#define PCI_DEVICE_ID_ATI_RAGE128_PH 0x5048 +#define PCI_DEVICE_ID_ATI_RAGE128_PI 0x5049 +#define PCI_DEVICE_ID_ATI_RAGE128_PJ 0x504A +#define PCI_DEVICE_ID_ATI_RAGE128_PK 0x504B +#define PCI_DEVICE_ID_ATI_RAGE128_PL 0x504C +#define PCI_DEVICE_ID_ATI_RAGE128_PM 0x504D +#define PCI_DEVICE_ID_ATI_RAGE128_PN 0x504E +#define PCI_DEVICE_ID_ATI_RAGE128_PO 0x504F +#define PCI_DEVICE_ID_ATI_RAGE128_PP 0x5050 +#define PCI_DEVICE_ID_ATI_RAGE128_PQ 0x5051 +#define PCI_DEVICE_ID_ATI_RAGE128_PR 0x5052 +#define PCI_DEVICE_ID_ATI_RAGE128_PS 0x5053 +#define PCI_DEVICE_ID_ATI_RAGE128_PT 0x5054 +#define PCI_DEVICE_ID_ATI_RAGE128_PU 0x5055 +#define PCI_DEVICE_ID_ATI_RAGE128_PV 0x5056 +#define PCI_DEVICE_ID_ATI_RAGE128_PW 0x5057 +#define PCI_DEVICE_ID_ATI_RAGE128_PX 0x5058 +/* Rage128 M4 */ +/* Radeon R100 */ +#define PCI_DEVICE_ID_ATI_RADEON_QD 0x5144 +#define PCI_DEVICE_ID_ATI_RADEON_QE 0x5145 +#define PCI_DEVICE_ID_ATI_RADEON_QF 0x5146 +#define PCI_DEVICE_ID_ATI_RADEON_QG 0x5147 +/* Radeon RV100 (VE) */ +#define PCI_DEVICE_ID_ATI_RADEON_QY 0x5159 +#define PCI_DEVICE_ID_ATI_RADEON_QZ 0x515a +/* Radeon R200 (8500) */ +#define PCI_DEVICE_ID_ATI_RADEON_QL 0x514c +#define PCI_DEVICE_ID_ATI_RADEON_QN 0x514e +#define PCI_DEVICE_ID_ATI_RADEON_QO 0x514f +#define PCI_DEVICE_ID_ATI_RADEON_Ql 0x516c +#define PCI_DEVICE_ID_ATI_RADEON_BB 0x4242 +/* Radeon R200 (9100) */ +#define PCI_DEVICE_ID_ATI_RADEON_QM 0x514d +/* Radeon RV200 (7500) */ +#define PCI_DEVICE_ID_ATI_RADEON_QW 0x5157 +#define PCI_DEVICE_ID_ATI_RADEON_QX 0x5158 +/* Radeon NV-100 */ +/* Radeon RV250 (9000) */ +#define PCI_DEVICE_ID_ATI_RADEON_Id 0x4964 +#define PCI_DEVICE_ID_ATI_RADEON_Ie 0x4965 +#define PCI_DEVICE_ID_ATI_RADEON_If 0x4966 +#define PCI_DEVICE_ID_ATI_RADEON_Ig 0x4967 +/* Radeon RV280 (9200) */ +#define PCI_DEVICE_ID_ATI_RADEON_Ya 0x5961 +#define PCI_DEVICE_ID_ATI_RADEON_Yd 0x5964 +/* Radeon R300 (9500) */ +/* Radeon R300 (9700) */ +#define PCI_DEVICE_ID_ATI_RADEON_ND 0x4e44 +#define PCI_DEVICE_ID_ATI_RADEON_NE 0x4e45 +#define PCI_DEVICE_ID_ATI_RADEON_NF 0x4e46 +#define PCI_DEVICE_ID_ATI_RADEON_NG 0x4e47 +/* Radeon R350 (9800) */ +/* Radeon RV350 (9600) */ +/* Radeon M6 */ +#define PCI_DEVICE_ID_ATI_RADEON_LY 0x4c59 +#define PCI_DEVICE_ID_ATI_RADEON_LZ 0x4c5a +/* Radeon M7 */ +#define PCI_DEVICE_ID_ATI_RADEON_LW 0x4c57 +#define PCI_DEVICE_ID_ATI_RADEON_LX 0x4c58 +/* Radeon M9 */ +#define PCI_DEVICE_ID_ATI_RADEON_Ld 0x4c64 +#define PCI_DEVICE_ID_ATI_RADEON_Le 0x4c65 +#define PCI_DEVICE_ID_ATI_RADEON_Lf 0x4c66 +#define PCI_DEVICE_ID_ATI_RADEON_Lg 0x4c67 +/* Radeon */ +/* RadeonIGP */ +#define PCI_DEVICE_ID_ATI_RS100 0xcab0 +#define PCI_DEVICE_ID_ATI_RS200 0xcab2 +#define PCI_DEVICE_ID_ATI_RS200_B 0xcbb2 +#define PCI_DEVICE_ID_ATI_RS250 0xcab3 +#define PCI_DEVICE_ID_ATI_RS300_100 0x5830 +#define PCI_DEVICE_ID_ATI_RS300_133 0x5831 +#define PCI_DEVICE_ID_ATI_RS300_166 0x5832 +#define PCI_DEVICE_ID_ATI_RS300_200 0x5833 +#define PCI_DEVICE_ID_ATI_RS350_100 0x7830 +#define PCI_DEVICE_ID_ATI_RS350_133 0x7831 +#define PCI_DEVICE_ID_ATI_RS350_166 0x7832 +#define PCI_DEVICE_ID_ATI_RS350_200 0x7833 +#define PCI_DEVICE_ID_ATI_RS400_100 0x5a30 +#define PCI_DEVICE_ID_ATI_RS400_133 0x5a31 +#define PCI_DEVICE_ID_ATI_RS400_166 0x5a32 +#define PCI_DEVICE_ID_ATI_RS400_200 0x5a33 +#define PCI_DEVICE_ID_ATI_RS480 0x5950 +/* ATI IXP Chipset */ +#define PCI_DEVICE_ID_ATI_IXP200_IDE 0x4349 +#define PCI_DEVICE_ID_ATI_IXP200_SMBUS 0x4353 +#define PCI_DEVICE_ID_ATI_IXP300_SMBUS 0x4363 +#define PCI_DEVICE_ID_ATI_IXP300_IDE 0x4369 +#define PCI_DEVICE_ID_ATI_IXP300_SATA 0x436e +#define PCI_DEVICE_ID_ATI_IXP400_SMBUS 0x4372 +#define PCI_DEVICE_ID_ATI_IXP400_IDE 0x4376 +#define PCI_DEVICE_ID_ATI_IXP400_SATA 0x4379 +#define PCI_DEVICE_ID_ATI_IXP400_SATA2 0x437a +#define PCI_DEVICE_ID_ATI_IXP600_SATA 0x4380 +#define PCI_DEVICE_ID_ATI_SBX00_SMBUS 0x4385 +#define PCI_DEVICE_ID_ATI_IXP600_IDE 0x438c +#define PCI_DEVICE_ID_ATI_IXP700_SATA 0x4390 +#define PCI_DEVICE_ID_ATI_IXP700_IDE 0x439c + +#define PCI_VENDOR_ID_VLSI 0x1004 +#define PCI_DEVICE_ID_VLSI_82C592 0x0005 +#define PCI_DEVICE_ID_VLSI_82C593 0x0006 +#define PCI_DEVICE_ID_VLSI_82C594 0x0007 +#define PCI_DEVICE_ID_VLSI_82C597 0x0009 +#define PCI_DEVICE_ID_VLSI_82C541 0x000c +#define PCI_DEVICE_ID_VLSI_82C543 0x000d +#define PCI_DEVICE_ID_VLSI_82C532 0x0101 +#define PCI_DEVICE_ID_VLSI_82C534 0x0102 +#define PCI_DEVICE_ID_VLSI_82C535 0x0104 +#define PCI_DEVICE_ID_VLSI_82C147 0x0105 +#define PCI_DEVICE_ID_VLSI_VAS96011 0x0702 + +#define PCI_VENDOR_ID_ADL 0x1005 +#define PCI_DEVICE_ID_ADL_2301 0x2301 + +#define PCI_VENDOR_ID_NS 0x100b +#define PCI_DEVICE_ID_NS_87415 0x0002 +#define PCI_DEVICE_ID_NS_87560_LIO 0x000e +#define PCI_DEVICE_ID_NS_87560_USB 0x0012 +#define PCI_DEVICE_ID_NS_83815 0x0020 +#define PCI_DEVICE_ID_NS_83820 0x0022 +#define PCI_DEVICE_ID_NS_CS5535_ISA 0x002b +#define PCI_DEVICE_ID_NS_CS5535_IDE 0x002d +#define PCI_DEVICE_ID_NS_CS5535_AUDIO 0x002e +#define PCI_DEVICE_ID_NS_CS5535_USB 0x002f +#define PCI_DEVICE_ID_NS_GX_VIDEO 0x0030 +#define PCI_DEVICE_ID_NS_SATURN 0x0035 +#define PCI_DEVICE_ID_NS_SCx200_BRIDGE 0x0500 +#define PCI_DEVICE_ID_NS_SCx200_SMI 0x0501 +#define PCI_DEVICE_ID_NS_SCx200_IDE 0x0502 +#define PCI_DEVICE_ID_NS_SCx200_AUDIO 0x0503 +#define PCI_DEVICE_ID_NS_SCx200_VIDEO 0x0504 +#define PCI_DEVICE_ID_NS_SCx200_XBUS 0x0505 +#define PCI_DEVICE_ID_NS_SC1100_BRIDGE 0x0510 +#define PCI_DEVICE_ID_NS_SC1100_SMI 0x0511 +#define PCI_DEVICE_ID_NS_SC1100_XBUS 0x0515 +#define PCI_DEVICE_ID_NS_87410 0xd001 + +#define PCI_DEVICE_ID_NS_GX_HOST_BRIDGE 0x0028 + +#define PCI_VENDOR_ID_TSENG 0x100c +#define PCI_DEVICE_ID_TSENG_W32P_2 0x3202 +#define PCI_DEVICE_ID_TSENG_W32P_b 0x3205 +#define PCI_DEVICE_ID_TSENG_W32P_c 0x3206 +#define PCI_DEVICE_ID_TSENG_W32P_d 0x3207 +#define PCI_DEVICE_ID_TSENG_ET6000 0x3208 + +#define PCI_VENDOR_ID_WEITEK 0x100e +#define PCI_DEVICE_ID_WEITEK_P9000 0x9001 +#define PCI_DEVICE_ID_WEITEK_P9100 0x9100 + +#define PCI_VENDOR_ID_DEC 0x1011 +#define PCI_DEVICE_ID_DEC_BRD 0x0001 +#define PCI_DEVICE_ID_DEC_TULIP 0x0002 +#define PCI_DEVICE_ID_DEC_TGA 0x0004 +#define PCI_DEVICE_ID_DEC_TULIP_FAST 0x0009 +#define PCI_DEVICE_ID_DEC_TGA2 0x000D +#define PCI_DEVICE_ID_DEC_FDDI 0x000F +#define PCI_DEVICE_ID_DEC_TULIP_PLUS 0x0014 +#define PCI_DEVICE_ID_DEC_21142 0x0019 +#define PCI_DEVICE_ID_DEC_21052 0x0021 +#define PCI_DEVICE_ID_DEC_21150 0x0022 +#define PCI_DEVICE_ID_DEC_21152 0x0024 +#define PCI_DEVICE_ID_DEC_21153 0x0025 +#define PCI_DEVICE_ID_DEC_21154 0x0026 +#define PCI_DEVICE_ID_DEC_21285 0x1065 +#define PCI_DEVICE_ID_COMPAQ_42XX 0x0046 + +#define PCI_VENDOR_ID_CIRRUS 0x1013 +#define PCI_DEVICE_ID_CIRRUS_7548 0x0038 +#define PCI_DEVICE_ID_CIRRUS_5430 0x00a0 +#define PCI_DEVICE_ID_CIRRUS_5434_4 0x00a4 +#define PCI_DEVICE_ID_CIRRUS_5434_8 0x00a8 +#define PCI_DEVICE_ID_CIRRUS_5436 0x00ac +#define PCI_DEVICE_ID_CIRRUS_5446 0x00b8 +#define PCI_DEVICE_ID_CIRRUS_5480 0x00bc +#define PCI_DEVICE_ID_CIRRUS_5462 0x00d0 +#define PCI_DEVICE_ID_CIRRUS_5464 0x00d4 +#define PCI_DEVICE_ID_CIRRUS_5465 0x00d6 +#define PCI_DEVICE_ID_CIRRUS_6729 0x1100 +#define PCI_DEVICE_ID_CIRRUS_6832 0x1110 +#define PCI_DEVICE_ID_CIRRUS_7543 0x1202 +#define PCI_DEVICE_ID_CIRRUS_4610 0x6001 +#define PCI_DEVICE_ID_CIRRUS_4612 0x6003 +#define PCI_DEVICE_ID_CIRRUS_4615 0x6004 + +#define PCI_VENDOR_ID_IBM 0x1014 +#define PCI_DEVICE_ID_IBM_TR 0x0018 +#define PCI_DEVICE_ID_IBM_TR_WAKE 0x003e +#define PCI_DEVICE_ID_IBM_CPC710_PCI64 0x00fc +#define PCI_DEVICE_ID_IBM_SNIPE 0x0180 +#define PCI_DEVICE_ID_IBM_CITRINE 0x028C +#define PCI_DEVICE_ID_IBM_GEMSTONE 0xB166 +#define PCI_DEVICE_ID_IBM_OBSIDIAN 0x02BD +#define PCI_DEVICE_ID_IBM_ICOM_DEV_ID_1 0x0031 +#define PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2 0x0219 +#define PCI_DEVICE_ID_IBM_ICOM_V2_TWO_PORTS_RVX 0x021A +#define PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM 0x0251 +#define PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM_PCIE 0x0361 +#define PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL 0x252 + +#define PCI_VENDOR_ID_UNISYS 0x1018 +#define PCI_DEVICE_ID_UNISYS_DMA_DIRECTOR 0x001C + +#define PCI_VENDOR_ID_COMPEX2 0x101a /* pci.ids says "AT&T GIS (NCR)" */ +#define PCI_DEVICE_ID_COMPEX2_100VG 0x0005 + +#define PCI_VENDOR_ID_WD 0x101c +#define PCI_DEVICE_ID_WD_90C 0xc24a + +#define PCI_VENDOR_ID_AMI 0x101e +#define PCI_DEVICE_ID_AMI_MEGARAID3 0x1960 +#define PCI_DEVICE_ID_AMI_MEGARAID 0x9010 +#define PCI_DEVICE_ID_AMI_MEGARAID2 0x9060 + +#define PCI_VENDOR_ID_AMD 0x1022 +#define PCI_DEVICE_ID_AMD_K8_NB 0x1100 +#define PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP 0x1101 +#define PCI_DEVICE_ID_AMD_K8_NB_MEMCTL 0x1102 +#define PCI_DEVICE_ID_AMD_K8_NB_MISC 0x1103 +#define PCI_DEVICE_ID_AMD_10H_NB_HT 0x1200 +#define PCI_DEVICE_ID_AMD_10H_NB_MAP 0x1201 +#define PCI_DEVICE_ID_AMD_10H_NB_DRAM 0x1202 +#define PCI_DEVICE_ID_AMD_10H_NB_MISC 0x1203 +#define PCI_DEVICE_ID_AMD_10H_NB_LINK 0x1204 +#define PCI_DEVICE_ID_AMD_11H_NB_HT 0x1300 +#define PCI_DEVICE_ID_AMD_11H_NB_MAP 0x1301 +#define PCI_DEVICE_ID_AMD_11H_NB_DRAM 0x1302 +#define PCI_DEVICE_ID_AMD_11H_NB_MISC 0x1303 +#define PCI_DEVICE_ID_AMD_11H_NB_LINK 0x1304 +#define PCI_DEVICE_ID_AMD_LANCE 0x2000 +#define PCI_DEVICE_ID_AMD_LANCE_HOME 0x2001 +#define PCI_DEVICE_ID_AMD_SCSI 0x2020 +#define PCI_DEVICE_ID_AMD_SERENADE 0x36c0 +#define PCI_DEVICE_ID_AMD_FE_GATE_7006 0x7006 +#define PCI_DEVICE_ID_AMD_FE_GATE_7007 0x7007 +#define PCI_DEVICE_ID_AMD_FE_GATE_700C 0x700C +#define PCI_DEVICE_ID_AMD_FE_GATE_700E 0x700E +#define PCI_DEVICE_ID_AMD_COBRA_7401 0x7401 +#define PCI_DEVICE_ID_AMD_VIPER_7409 0x7409 +#define PCI_DEVICE_ID_AMD_VIPER_740B 0x740B +#define PCI_DEVICE_ID_AMD_VIPER_7410 0x7410 +#define PCI_DEVICE_ID_AMD_VIPER_7411 0x7411 +#define PCI_DEVICE_ID_AMD_VIPER_7413 0x7413 +#define PCI_DEVICE_ID_AMD_VIPER_7440 0x7440 +#define PCI_DEVICE_ID_AMD_OPUS_7441 0x7441 +#define PCI_DEVICE_ID_AMD_OPUS_7443 0x7443 +#define PCI_DEVICE_ID_AMD_VIPER_7443 0x7443 +#define PCI_DEVICE_ID_AMD_OPUS_7445 0x7445 +#define PCI_DEVICE_ID_AMD_8111_LPC 0x7468 +#define PCI_DEVICE_ID_AMD_8111_IDE 0x7469 +#define PCI_DEVICE_ID_AMD_8111_SMBUS2 0x746a +#define PCI_DEVICE_ID_AMD_8111_SMBUS 0x746b +#define PCI_DEVICE_ID_AMD_8111_AUDIO 0x746d +#define PCI_DEVICE_ID_AMD_8151_0 0x7454 +#define PCI_DEVICE_ID_AMD_8131_BRIDGE 0x7450 +#define PCI_DEVICE_ID_AMD_8131_APIC 0x7451 +#define PCI_DEVICE_ID_AMD_8132_BRIDGE 0x7458 +#define PCI_DEVICE_ID_AMD_CS5536_ISA 0x2090 +#define PCI_DEVICE_ID_AMD_CS5536_FLASH 0x2091 +#define PCI_DEVICE_ID_AMD_CS5536_AUDIO 0x2093 +#define PCI_DEVICE_ID_AMD_CS5536_OHC 0x2094 +#define PCI_DEVICE_ID_AMD_CS5536_EHC 0x2095 +#define PCI_DEVICE_ID_AMD_CS5536_UDC 0x2096 +#define PCI_DEVICE_ID_AMD_CS5536_UOC 0x2097 +#define PCI_DEVICE_ID_AMD_CS5536_IDE 0x209A + +#define PCI_DEVICE_ID_AMD_LX_VIDEO 0x2081 +#define PCI_DEVICE_ID_AMD_LX_AES 0x2082 + +#define PCI_VENDOR_ID_TRIDENT 0x1023 +#define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX 0x2000 +#define PCI_DEVICE_ID_TRIDENT_4DWAVE_NX 0x2001 +#define PCI_DEVICE_ID_TRIDENT_9320 0x9320 +#define PCI_DEVICE_ID_TRIDENT_9388 0x9388 +#define PCI_DEVICE_ID_TRIDENT_9397 0x9397 +#define PCI_DEVICE_ID_TRIDENT_939A 0x939A +#define PCI_DEVICE_ID_TRIDENT_9520 0x9520 +#define PCI_DEVICE_ID_TRIDENT_9525 0x9525 +#define PCI_DEVICE_ID_TRIDENT_9420 0x9420 +#define PCI_DEVICE_ID_TRIDENT_9440 0x9440 +#define PCI_DEVICE_ID_TRIDENT_9660 0x9660 +#define PCI_DEVICE_ID_TRIDENT_9750 0x9750 +#define PCI_DEVICE_ID_TRIDENT_9850 0x9850 +#define PCI_DEVICE_ID_TRIDENT_9880 0x9880 +#define PCI_DEVICE_ID_TRIDENT_8400 0x8400 +#define PCI_DEVICE_ID_TRIDENT_8420 0x8420 +#define PCI_DEVICE_ID_TRIDENT_8500 0x8500 + +#define PCI_VENDOR_ID_AI 0x1025 +#define PCI_DEVICE_ID_AI_M1435 0x1435 + +#define PCI_VENDOR_ID_DELL 0x1028 +#define PCI_DEVICE_ID_DELL_RACIII 0x0008 +#define PCI_DEVICE_ID_DELL_RAC4 0x0012 +#define PCI_DEVICE_ID_DELL_PERC5 0x0015 + +#define PCI_VENDOR_ID_MATROX 0x102B +#define PCI_DEVICE_ID_MATROX_MGA_2 0x0518 +#define PCI_DEVICE_ID_MATROX_MIL 0x0519 +#define PCI_DEVICE_ID_MATROX_MYS 0x051A +#define PCI_DEVICE_ID_MATROX_MIL_2 0x051b +#define PCI_DEVICE_ID_MATROX_MYS_AGP 0x051e +#define PCI_DEVICE_ID_MATROX_MIL_2_AGP 0x051f +#define PCI_DEVICE_ID_MATROX_MGA_IMP 0x0d10 +#define PCI_DEVICE_ID_MATROX_G100_MM 0x1000 +#define PCI_DEVICE_ID_MATROX_G100_AGP 0x1001 +#define PCI_DEVICE_ID_MATROX_G200_PCI 0x0520 +#define PCI_DEVICE_ID_MATROX_G200_AGP 0x0521 +#define PCI_DEVICE_ID_MATROX_G400 0x0525 +#define PCI_DEVICE_ID_MATROX_G200EV_PCI 0x0530 +#define PCI_DEVICE_ID_MATROX_G550 0x2527 +#define PCI_DEVICE_ID_MATROX_VIA 0x4536 + +#define PCI_VENDOR_ID_CT 0x102c +#define PCI_DEVICE_ID_CT_69000 0x00c0 +#define PCI_DEVICE_ID_CT_65545 0x00d8 +#define PCI_DEVICE_ID_CT_65548 0x00dc +#define PCI_DEVICE_ID_CT_65550 0x00e0 +#define PCI_DEVICE_ID_CT_65554 0x00e4 +#define PCI_DEVICE_ID_CT_65555 0x00e5 + +#define PCI_VENDOR_ID_MIRO 0x1031 +#define PCI_DEVICE_ID_MIRO_36050 0x5601 +#define PCI_DEVICE_ID_MIRO_DC10PLUS 0x7efe +#define PCI_DEVICE_ID_MIRO_DC30PLUS 0xd801 + +#define PCI_VENDOR_ID_NEC 0x1033 +#define PCI_DEVICE_ID_NEC_CBUS_1 0x0001 /* PCI-Cbus Bridge */ +#define PCI_DEVICE_ID_NEC_LOCAL 0x0002 /* Local Bridge */ +#define PCI_DEVICE_ID_NEC_ATM 0x0003 /* ATM LAN Controller */ +#define PCI_DEVICE_ID_NEC_R4000 0x0004 /* R4000 Bridge */ +#define PCI_DEVICE_ID_NEC_486 0x0005 /* 486 Like Peripheral Bus Bridge */ +#define PCI_DEVICE_ID_NEC_ACCEL_1 0x0006 /* Graphic Accelerator */ +#define PCI_DEVICE_ID_NEC_UXBUS 0x0007 /* UX-Bus Bridge */ +#define PCI_DEVICE_ID_NEC_ACCEL_2 0x0008 /* Graphic Accelerator */ +#define PCI_DEVICE_ID_NEC_GRAPH 0x0009 /* PCI-CoreGraph Bridge */ +#define PCI_DEVICE_ID_NEC_VL 0x0016 /* PCI-VL Bridge */ +#define PCI_DEVICE_ID_NEC_STARALPHA2 0x002c /* STAR ALPHA2 */ +#define PCI_DEVICE_ID_NEC_CBUS_2 0x002d /* PCI-Cbus Bridge */ +#define PCI_DEVICE_ID_NEC_USB 0x0035 /* PCI-USB Host */ +#define PCI_DEVICE_ID_NEC_CBUS_3 0x003b +#define PCI_DEVICE_ID_NEC_NAPCCARD 0x003e +#define PCI_DEVICE_ID_NEC_PCX2 0x0046 /* PowerVR */ +#define PCI_DEVICE_ID_NEC_VRC5476 0x009b +#define PCI_DEVICE_ID_NEC_VRC4173 0x00a5 +#define PCI_DEVICE_ID_NEC_VRC5477_AC97 0x00a6 +#define PCI_DEVICE_ID_NEC_PC9821CS01 0x800c /* PC-9821-CS01 */ +#define PCI_DEVICE_ID_NEC_PC9821NRB06 0x800d /* PC-9821NR-B06 */ + +#define PCI_VENDOR_ID_FD 0x1036 +#define PCI_DEVICE_ID_FD_36C70 0x0000 + +#define PCI_VENDOR_ID_SI 0x1039 +#define PCI_DEVICE_ID_SI_5591_AGP 0x0001 +#define PCI_DEVICE_ID_SI_6202 0x0002 +#define PCI_DEVICE_ID_SI_503 0x0008 +#define PCI_DEVICE_ID_SI_ACPI 0x0009 +#define PCI_DEVICE_ID_SI_SMBUS 0x0016 +#define PCI_DEVICE_ID_SI_LPC 0x0018 +#define PCI_DEVICE_ID_SI_5597_VGA 0x0200 +#define PCI_DEVICE_ID_SI_6205 0x0205 +#define PCI_DEVICE_ID_SI_501 0x0406 +#define PCI_DEVICE_ID_SI_496 0x0496 +#define PCI_DEVICE_ID_SI_300 0x0300 +#define PCI_DEVICE_ID_SI_315H 0x0310 +#define PCI_DEVICE_ID_SI_315 0x0315 +#define PCI_DEVICE_ID_SI_315PRO 0x0325 +#define PCI_DEVICE_ID_SI_530 0x0530 +#define PCI_DEVICE_ID_SI_540 0x0540 +#define PCI_DEVICE_ID_SI_550 0x0550 +#define PCI_DEVICE_ID_SI_540_VGA 0x5300 +#define PCI_DEVICE_ID_SI_550_VGA 0x5315 +#define PCI_DEVICE_ID_SI_620 0x0620 +#define PCI_DEVICE_ID_SI_630 0x0630 +#define PCI_DEVICE_ID_SI_633 0x0633 +#define PCI_DEVICE_ID_SI_635 0x0635 +#define PCI_DEVICE_ID_SI_640 0x0640 +#define PCI_DEVICE_ID_SI_645 0x0645 +#define PCI_DEVICE_ID_SI_646 0x0646 +#define PCI_DEVICE_ID_SI_648 0x0648 +#define PCI_DEVICE_ID_SI_650 0x0650 +#define PCI_DEVICE_ID_SI_651 0x0651 +#define PCI_DEVICE_ID_SI_655 0x0655 +#define PCI_DEVICE_ID_SI_661 0x0661 +#define PCI_DEVICE_ID_SI_730 0x0730 +#define PCI_DEVICE_ID_SI_733 0x0733 +#define PCI_DEVICE_ID_SI_630_VGA 0x6300 +#define PCI_DEVICE_ID_SI_735 0x0735 +#define PCI_DEVICE_ID_SI_740 0x0740 +#define PCI_DEVICE_ID_SI_741 0x0741 +#define PCI_DEVICE_ID_SI_745 0x0745 +#define PCI_DEVICE_ID_SI_746 0x0746 +#define PCI_DEVICE_ID_SI_755 0x0755 +#define PCI_DEVICE_ID_SI_760 0x0760 +#define PCI_DEVICE_ID_SI_900 0x0900 +#define PCI_DEVICE_ID_SI_961 0x0961 +#define PCI_DEVICE_ID_SI_962 0x0962 +#define PCI_DEVICE_ID_SI_963 0x0963 +#define PCI_DEVICE_ID_SI_965 0x0965 +#define PCI_DEVICE_ID_SI_966 0x0966 +#define PCI_DEVICE_ID_SI_968 0x0968 +#define PCI_DEVICE_ID_SI_1180 0x1180 +#define PCI_DEVICE_ID_SI_5511 0x5511 +#define PCI_DEVICE_ID_SI_5513 0x5513 +#define PCI_DEVICE_ID_SI_5517 0x5517 +#define PCI_DEVICE_ID_SI_5518 0x5518 +#define PCI_DEVICE_ID_SI_5571 0x5571 +#define PCI_DEVICE_ID_SI_5581 0x5581 +#define PCI_DEVICE_ID_SI_5582 0x5582 +#define PCI_DEVICE_ID_SI_5591 0x5591 +#define PCI_DEVICE_ID_SI_5596 0x5596 +#define PCI_DEVICE_ID_SI_5597 0x5597 +#define PCI_DEVICE_ID_SI_5598 0x5598 +#define PCI_DEVICE_ID_SI_5600 0x5600 +#define PCI_DEVICE_ID_SI_7012 0x7012 +#define PCI_DEVICE_ID_SI_7013 0x7013 +#define PCI_DEVICE_ID_SI_7016 0x7016 +#define PCI_DEVICE_ID_SI_7018 0x7018 + +#define PCI_VENDOR_ID_HP 0x103c +#define PCI_DEVICE_ID_HP_VISUALIZE_EG 0x1005 +#define PCI_DEVICE_ID_HP_VISUALIZE_FX6 0x1006 +#define PCI_DEVICE_ID_HP_VISUALIZE_FX4 0x1008 +#define PCI_DEVICE_ID_HP_VISUALIZE_FX2 0x100a +#define PCI_DEVICE_ID_HP_TACHYON 0x1028 +#define PCI_DEVICE_ID_HP_TACHLITE 0x1029 +#define PCI_DEVICE_ID_HP_J2585A 0x1030 +#define PCI_DEVICE_ID_HP_J2585B 0x1031 +#define PCI_DEVICE_ID_HP_J2973A 0x1040 +#define PCI_DEVICE_ID_HP_J2970A 0x1042 +#define PCI_DEVICE_ID_HP_DIVA 0x1048 +#define PCI_DEVICE_ID_HP_DIVA_TOSCA1 0x1049 +#define PCI_DEVICE_ID_HP_DIVA_TOSCA2 0x104A +#define PCI_DEVICE_ID_HP_DIVA_MAESTRO 0x104B +#define PCI_DEVICE_ID_HP_REO_IOC 0x10f1 +#define PCI_DEVICE_ID_HP_VISUALIZE_FXE 0x108b +#define PCI_DEVICE_ID_HP_DIVA_HALFDOME 0x1223 +#define PCI_DEVICE_ID_HP_DIVA_KEYSTONE 0x1226 +#define PCI_DEVICE_ID_HP_DIVA_POWERBAR 0x1227 +#define PCI_DEVICE_ID_HP_ZX1_IOC 0x122a +#define PCI_DEVICE_ID_HP_PCIX_LBA 0x122e +#define PCI_DEVICE_ID_HP_SX1000_IOC 0x127c +#define PCI_DEVICE_ID_HP_DIVA_EVEREST 0x1282 +#define PCI_DEVICE_ID_HP_DIVA_AUX 0x1290 +#define PCI_DEVICE_ID_HP_DIVA_RMP3 0x1301 +#define PCI_DEVICE_ID_HP_DIVA_HURRICANE 0x132a +#define PCI_DEVICE_ID_HP_CISSA 0x3220 +#define PCI_DEVICE_ID_HP_CISSC 0x3230 +#define PCI_DEVICE_ID_HP_CISSD 0x3238 +#define PCI_DEVICE_ID_HP_CISSE 0x323a +#define PCI_DEVICE_ID_HP_ZX2_IOC 0x4031 + +#define PCI_VENDOR_ID_PCTECH 0x1042 +#define PCI_DEVICE_ID_PCTECH_RZ1000 0x1000 +#define PCI_DEVICE_ID_PCTECH_RZ1001 0x1001 +#define PCI_DEVICE_ID_PCTECH_SAMURAI_IDE 0x3020 + +#define PCI_VENDOR_ID_ASUSTEK 0x1043 +#define PCI_DEVICE_ID_ASUSTEK_0675 0x0675 + +#define PCI_VENDOR_ID_DPT 0x1044 +#define PCI_DEVICE_ID_DPT 0xa400 + +#define PCI_VENDOR_ID_OPTI 0x1045 +#define PCI_DEVICE_ID_OPTI_82C558 0xc558 +#define PCI_DEVICE_ID_OPTI_82C621 0xc621 +#define PCI_DEVICE_ID_OPTI_82C700 0xc700 +#define PCI_DEVICE_ID_OPTI_82C825 0xd568 + +#define PCI_VENDOR_ID_ELSA 0x1048 +#define PCI_DEVICE_ID_ELSA_MICROLINK 0x1000 +#define PCI_DEVICE_ID_ELSA_QS3000 0x3000 + +#define PCI_VENDOR_ID_BUSLOGIC 0x104B +#define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC 0x0140 +#define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER 0x1040 +#define PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT 0x8130 + +#define PCI_VENDOR_ID_TI 0x104c +#define PCI_DEVICE_ID_TI_TVP4020 0x3d07 +#define PCI_DEVICE_ID_TI_4450 0x8011 +#define PCI_DEVICE_ID_TI_TSB43AB22 0x8023 +#define PCI_DEVICE_ID_TI_XX21_XX11 0x8031 +#define PCI_DEVICE_ID_TI_XX21_XX11_FM 0x8033 +#define PCI_DEVICE_ID_TI_XX21_XX11_SD 0x8034 +#define PCI_DEVICE_ID_TI_X515 0x8036 +#define PCI_DEVICE_ID_TI_XX12 0x8039 +#define PCI_DEVICE_ID_TI_XX12_FM 0x803b +#define PCI_DEVICE_ID_TI_1130 0xac12 +#define PCI_DEVICE_ID_TI_1031 0xac13 +#define PCI_DEVICE_ID_TI_1131 0xac15 +#define PCI_DEVICE_ID_TI_1250 0xac16 +#define PCI_DEVICE_ID_TI_1220 0xac17 +#define PCI_DEVICE_ID_TI_1221 0xac19 +#define PCI_DEVICE_ID_TI_1210 0xac1a +#define PCI_DEVICE_ID_TI_1450 0xac1b +#define PCI_DEVICE_ID_TI_1225 0xac1c +#define PCI_DEVICE_ID_TI_1251A 0xac1d +#define PCI_DEVICE_ID_TI_1211 0xac1e +#define PCI_DEVICE_ID_TI_1251B 0xac1f +#define PCI_DEVICE_ID_TI_4410 0xac41 +#define PCI_DEVICE_ID_TI_4451 0xac42 +#define PCI_DEVICE_ID_TI_4510 0xac44 +#define PCI_DEVICE_ID_TI_4520 0xac46 +#define PCI_DEVICE_ID_TI_7510 0xac47 +#define PCI_DEVICE_ID_TI_7610 0xac48 +#define PCI_DEVICE_ID_TI_7410 0xac49 +#define PCI_DEVICE_ID_TI_1410 0xac50 +#define PCI_DEVICE_ID_TI_1420 0xac51 +#define PCI_DEVICE_ID_TI_1451A 0xac52 +#define PCI_DEVICE_ID_TI_1620 0xac54 +#define PCI_DEVICE_ID_TI_1520 0xac55 +#define PCI_DEVICE_ID_TI_1510 0xac56 +#define PCI_DEVICE_ID_TI_X620 0xac8d +#define PCI_DEVICE_ID_TI_X420 0xac8e +#define PCI_DEVICE_ID_TI_XX20_FM 0xac8f + +#define PCI_VENDOR_ID_SONY 0x104d + +/* Winbond have two vendor IDs! See 0x10ad as well */ +#define PCI_VENDOR_ID_WINBOND2 0x1050 +#define PCI_DEVICE_ID_WINBOND2_89C940F 0x5a5a +#define PCI_DEVICE_ID_WINBOND2_6692 0x6692 + +#define PCI_VENDOR_ID_ANIGMA 0x1051 +#define PCI_DEVICE_ID_ANIGMA_MC145575 0x0100 + +#define PCI_VENDOR_ID_EFAR 0x1055 +#define PCI_DEVICE_ID_EFAR_SLC90E66_1 0x9130 +#define PCI_DEVICE_ID_EFAR_SLC90E66_3 0x9463 + +#define PCI_VENDOR_ID_MOTOROLA 0x1057 +#define PCI_DEVICE_ID_MOTOROLA_MPC105 0x0001 +#define PCI_DEVICE_ID_MOTOROLA_MPC106 0x0002 +#define PCI_DEVICE_ID_MOTOROLA_MPC107 0x0004 +#define PCI_DEVICE_ID_MOTOROLA_RAVEN 0x4801 +#define PCI_DEVICE_ID_MOTOROLA_FALCON 0x4802 +#define PCI_DEVICE_ID_MOTOROLA_HAWK 0x4803 +#define PCI_DEVICE_ID_MOTOROLA_HARRIER 0x480b +#define PCI_DEVICE_ID_MOTOROLA_MPC5200 0x5803 +#define PCI_DEVICE_ID_MOTOROLA_MPC5200B 0x5809 + +#define PCI_VENDOR_ID_PROMISE 0x105a +#define PCI_DEVICE_ID_PROMISE_20265 0x0d30 +#define PCI_DEVICE_ID_PROMISE_20267 0x4d30 +#define PCI_DEVICE_ID_PROMISE_20246 0x4d33 +#define PCI_DEVICE_ID_PROMISE_20262 0x4d38 +#define PCI_DEVICE_ID_PROMISE_20263 0x0D38 +#define PCI_DEVICE_ID_PROMISE_20268 0x4d68 +#define PCI_DEVICE_ID_PROMISE_20269 0x4d69 +#define PCI_DEVICE_ID_PROMISE_20270 0x6268 +#define PCI_DEVICE_ID_PROMISE_20271 0x6269 +#define PCI_DEVICE_ID_PROMISE_20275 0x1275 +#define PCI_DEVICE_ID_PROMISE_20276 0x5275 +#define PCI_DEVICE_ID_PROMISE_20277 0x7275 + +#define PCI_VENDOR_ID_UMC 0x1060 +#define PCI_DEVICE_ID_UMC_UM8673F 0x0101 +#define PCI_DEVICE_ID_UMC_UM8886BF 0x673a +#define PCI_DEVICE_ID_UMC_UM8886A 0x886a + +#define PCI_VENDOR_ID_PICOPOWER 0x1066 +#define PCI_DEVICE_ID_PICOPOWER_PT86C523 0x0002 +#define PCI_DEVICE_ID_PICOPOWER_PT86C523BBP 0x8002 + +#define PCI_VENDOR_ID_MYLEX 0x1069 +#define PCI_DEVICE_ID_MYLEX_DAC960_P 0x0001 +#define PCI_DEVICE_ID_MYLEX_DAC960_PD 0x0002 +#define PCI_DEVICE_ID_MYLEX_DAC960_PG 0x0010 +#define PCI_DEVICE_ID_MYLEX_DAC960_LA 0x0020 +#define PCI_DEVICE_ID_MYLEX_DAC960_LP 0x0050 +#define PCI_DEVICE_ID_MYLEX_DAC960_BA 0xBA56 +#define PCI_DEVICE_ID_MYLEX_DAC960_GEM 0xB166 + +#define PCI_VENDOR_ID_APPLE 0x106b +#define PCI_DEVICE_ID_APPLE_BANDIT 0x0001 +#define PCI_DEVICE_ID_APPLE_HYDRA 0x000e +#define PCI_DEVICE_ID_APPLE_UNI_N_FW 0x0018 +#define PCI_DEVICE_ID_APPLE_UNI_N_AGP 0x0020 +#define PCI_DEVICE_ID_APPLE_UNI_N_GMAC 0x0021 +#define PCI_DEVICE_ID_APPLE_UNI_N_GMACP 0x0024 +#define PCI_DEVICE_ID_APPLE_UNI_N_AGP_P 0x0027 +#define PCI_DEVICE_ID_APPLE_UNI_N_AGP15 0x002d +#define PCI_DEVICE_ID_APPLE_UNI_N_PCI15 0x002e +#define PCI_DEVICE_ID_APPLE_UNI_N_GMAC2 0x0032 +#define PCI_DEVICE_ID_APPLE_UNI_N_ATA 0x0033 +#define PCI_DEVICE_ID_APPLE_UNI_N_AGP2 0x0034 +#define PCI_DEVICE_ID_APPLE_IPID_ATA100 0x003b +#define PCI_DEVICE_ID_APPLE_K2_ATA100 0x0043 +#define PCI_DEVICE_ID_APPLE_U3_AGP 0x004b +#define PCI_DEVICE_ID_APPLE_K2_GMAC 0x004c +#define PCI_DEVICE_ID_APPLE_SH_ATA 0x0050 +#define PCI_DEVICE_ID_APPLE_SH_SUNGEM 0x0051 +#define PCI_DEVICE_ID_APPLE_U3L_AGP 0x0058 +#define PCI_DEVICE_ID_APPLE_U3H_AGP 0x0059 +#define PCI_DEVICE_ID_APPLE_IPID2_AGP 0x0066 +#define PCI_DEVICE_ID_APPLE_IPID2_ATA 0x0069 +#define PCI_DEVICE_ID_APPLE_IPID2_FW 0x006a +#define PCI_DEVICE_ID_APPLE_IPID2_GMAC 0x006b +#define PCI_DEVICE_ID_APPLE_TIGON3 0x1645 + +#define PCI_VENDOR_ID_YAMAHA 0x1073 +#define PCI_DEVICE_ID_YAMAHA_724 0x0004 +#define PCI_DEVICE_ID_YAMAHA_724F 0x000d +#define PCI_DEVICE_ID_YAMAHA_740 0x000a +#define PCI_DEVICE_ID_YAMAHA_740C 0x000c +#define PCI_DEVICE_ID_YAMAHA_744 0x0010 +#define PCI_DEVICE_ID_YAMAHA_754 0x0012 + +#define PCI_VENDOR_ID_QLOGIC 0x1077 +#define PCI_DEVICE_ID_QLOGIC_ISP10160 0x1016 +#define PCI_DEVICE_ID_QLOGIC_ISP1020 0x1020 +#define PCI_DEVICE_ID_QLOGIC_ISP1080 0x1080 +#define PCI_DEVICE_ID_QLOGIC_ISP12160 0x1216 +#define PCI_DEVICE_ID_QLOGIC_ISP1240 0x1240 +#define PCI_DEVICE_ID_QLOGIC_ISP1280 0x1280 +#define PCI_DEVICE_ID_QLOGIC_ISP2100 0x2100 +#define PCI_DEVICE_ID_QLOGIC_ISP2200 0x2200 +#define PCI_DEVICE_ID_QLOGIC_ISP2300 0x2300 +#define PCI_DEVICE_ID_QLOGIC_ISP2312 0x2312 +#define PCI_DEVICE_ID_QLOGIC_ISP2322 0x2322 +#define PCI_DEVICE_ID_QLOGIC_ISP6312 0x6312 +#define PCI_DEVICE_ID_QLOGIC_ISP6322 0x6322 +#define PCI_DEVICE_ID_QLOGIC_ISP2422 0x2422 +#define PCI_DEVICE_ID_QLOGIC_ISP2432 0x2432 +#define PCI_DEVICE_ID_QLOGIC_ISP2512 0x2512 +#define PCI_DEVICE_ID_QLOGIC_ISP2522 0x2522 +#define PCI_DEVICE_ID_QLOGIC_ISP5422 0x5422 +#define PCI_DEVICE_ID_QLOGIC_ISP5432 0x5432 + +#define PCI_VENDOR_ID_CYRIX 0x1078 +#define PCI_DEVICE_ID_CYRIX_5510 0x0000 +#define PCI_DEVICE_ID_CYRIX_PCI_MASTER 0x0001 +#define PCI_DEVICE_ID_CYRIX_5520 0x0002 +#define PCI_DEVICE_ID_CYRIX_5530_LEGACY 0x0100 +#define PCI_DEVICE_ID_CYRIX_5530_IDE 0x0102 +#define PCI_DEVICE_ID_CYRIX_5530_AUDIO 0x0103 +#define PCI_DEVICE_ID_CYRIX_5530_VIDEO 0x0104 + +#define PCI_VENDOR_ID_CONTAQ 0x1080 +#define PCI_DEVICE_ID_CONTAQ_82C693 0xc693 + +#define PCI_VENDOR_ID_OLICOM 0x108d +#define PCI_DEVICE_ID_OLICOM_OC2325 0x0012 +#define PCI_DEVICE_ID_OLICOM_OC2183 0x0013 +#define PCI_DEVICE_ID_OLICOM_OC2326 0x0014 + +#define PCI_VENDOR_ID_SUN 0x108e +#define PCI_DEVICE_ID_SUN_EBUS 0x1000 +#define PCI_DEVICE_ID_SUN_HAPPYMEAL 0x1001 +#define PCI_DEVICE_ID_SUN_RIO_EBUS 0x1100 +#define PCI_DEVICE_ID_SUN_RIO_GEM 0x1101 +#define PCI_DEVICE_ID_SUN_RIO_1394 0x1102 +#define PCI_DEVICE_ID_SUN_RIO_USB 0x1103 +#define PCI_DEVICE_ID_SUN_GEM 0x2bad +#define PCI_DEVICE_ID_SUN_SIMBA 0x5000 +#define PCI_DEVICE_ID_SUN_PBM 0x8000 +#define PCI_DEVICE_ID_SUN_SCHIZO 0x8001 +#define PCI_DEVICE_ID_SUN_SABRE 0xa000 +#define PCI_DEVICE_ID_SUN_HUMMINGBIRD 0xa001 +#define PCI_DEVICE_ID_SUN_TOMATILLO 0xa801 +#define PCI_DEVICE_ID_SUN_CASSINI 0xabba + +#define PCI_VENDOR_ID_CMD 0x1095 +#define PCI_DEVICE_ID_CMD_643 0x0643 +#define PCI_DEVICE_ID_CMD_646 0x0646 +#define PCI_DEVICE_ID_CMD_648 0x0648 +#define PCI_DEVICE_ID_CMD_649 0x0649 + +#define PCI_DEVICE_ID_SII_680 0x0680 +#define PCI_DEVICE_ID_SII_3112 0x3112 +#define PCI_DEVICE_ID_SII_1210SA 0x0240 + +#define PCI_VENDOR_ID_BROOKTREE 0x109e +#define PCI_DEVICE_ID_BROOKTREE_878 0x0878 +#define PCI_DEVICE_ID_BROOKTREE_879 0x0879 + +#define PCI_VENDOR_ID_SGI 0x10a9 +#define PCI_DEVICE_ID_SGI_IOC3 0x0003 +#define PCI_DEVICE_ID_SGI_LITHIUM 0x1002 +#define PCI_DEVICE_ID_SGI_IOC4 0x100a + +#define PCI_VENDOR_ID_WINBOND 0x10ad +#define PCI_DEVICE_ID_WINBOND_82C105 0x0105 +#define PCI_DEVICE_ID_WINBOND_83C553 0x0565 + +#define PCI_VENDOR_ID_PLX 0x10b5 +#define PCI_DEVICE_ID_PLX_R685 0x1030 +#define PCI_DEVICE_ID_PLX_ROMULUS 0x106a +#define PCI_DEVICE_ID_PLX_SPCOM800 0x1076 +#define PCI_DEVICE_ID_PLX_1077 0x1077 +#define PCI_DEVICE_ID_PLX_SPCOM200 0x1103 +#define PCI_DEVICE_ID_PLX_DJINN_ITOO 0x1151 +#define PCI_DEVICE_ID_PLX_R753 0x1152 +#define PCI_DEVICE_ID_PLX_OLITEC 0x1187 +#define PCI_DEVICE_ID_PLX_PCI200SYN 0x3196 +#define PCI_DEVICE_ID_PLX_9030 0x9030 +#define PCI_DEVICE_ID_PLX_9050 0x9050 +#define PCI_DEVICE_ID_PLX_9080 0x9080 +#define PCI_DEVICE_ID_PLX_GTEK_SERIAL2 0xa001 + +#define PCI_VENDOR_ID_MADGE 0x10b6 +#define PCI_DEVICE_ID_MADGE_MK2 0x0002 + +#define PCI_VENDOR_ID_3COM 0x10b7 +#define PCI_DEVICE_ID_3COM_3C985 0x0001 +#define PCI_DEVICE_ID_3COM_3C940 0x1700 +#define PCI_DEVICE_ID_3COM_3C339 0x3390 +#define PCI_DEVICE_ID_3COM_3C359 0x3590 +#define PCI_DEVICE_ID_3COM_3C940B 0x80eb +#define PCI_DEVICE_ID_3COM_3CR990 0x9900 +#define PCI_DEVICE_ID_3COM_3CR990_TX_95 0x9902 +#define PCI_DEVICE_ID_3COM_3CR990_TX_97 0x9903 +#define PCI_DEVICE_ID_3COM_3CR990B 0x9904 +#define PCI_DEVICE_ID_3COM_3CR990_FX 0x9905 +#define PCI_DEVICE_ID_3COM_3CR990SVR95 0x9908 +#define PCI_DEVICE_ID_3COM_3CR990SVR97 0x9909 +#define PCI_DEVICE_ID_3COM_3CR990SVR 0x990a + +#define PCI_VENDOR_ID_AL 0x10b9 +#define PCI_DEVICE_ID_AL_M1533 0x1533 +#define PCI_DEVICE_ID_AL_M1535 0x1535 +#define PCI_DEVICE_ID_AL_M1541 0x1541 +#define PCI_DEVICE_ID_AL_M1563 0x1563 +#define PCI_DEVICE_ID_AL_M1621 0x1621 +#define PCI_DEVICE_ID_AL_M1631 0x1631 +#define PCI_DEVICE_ID_AL_M1632 0x1632 +#define PCI_DEVICE_ID_AL_M1641 0x1641 +#define PCI_DEVICE_ID_AL_M1644 0x1644 +#define PCI_DEVICE_ID_AL_M1647 0x1647 +#define PCI_DEVICE_ID_AL_M1651 0x1651 +#define PCI_DEVICE_ID_AL_M1671 0x1671 +#define PCI_DEVICE_ID_AL_M1681 0x1681 +#define PCI_DEVICE_ID_AL_M1683 0x1683 +#define PCI_DEVICE_ID_AL_M1689 0x1689 +#define PCI_DEVICE_ID_AL_M5219 0x5219 +#define PCI_DEVICE_ID_AL_M5228 0x5228 +#define PCI_DEVICE_ID_AL_M5229 0x5229 +#define PCI_DEVICE_ID_AL_M5451 0x5451 +#define PCI_DEVICE_ID_AL_M7101 0x7101 + +#define PCI_VENDOR_ID_NEOMAGIC 0x10c8 +#define PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO 0x8005 +#define PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO 0x8006 +#define PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO 0x8016 + +#define PCI_VENDOR_ID_TCONRAD 0x10da +#define PCI_DEVICE_ID_TCONRAD_TOKENRING 0x0508 + +#define PCI_VENDOR_ID_NVIDIA 0x10de +#define PCI_DEVICE_ID_NVIDIA_TNT 0x0020 +#define PCI_DEVICE_ID_NVIDIA_TNT2 0x0028 +#define PCI_DEVICE_ID_NVIDIA_UTNT2 0x0029 +#define PCI_DEVICE_ID_NVIDIA_TNT_UNKNOWN 0x002a +#define PCI_DEVICE_ID_NVIDIA_VTNT2 0x002C +#define PCI_DEVICE_ID_NVIDIA_UVTNT2 0x002D +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SMBUS 0x0034 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE 0x0035 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA 0x0036 +#define PCI_DEVICE_ID_NVIDIA_NVENET_10 0x0037 +#define PCI_DEVICE_ID_NVIDIA_NVENET_11 0x0038 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2 0x003e +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_ULTRA 0x0040 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800 0x0041 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_LE 0x0042 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_GT 0x0045 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_4000 0x004E +#define PCI_DEVICE_ID_NVIDIA_NFORCE4_SMBUS 0x0052 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE 0x0053 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA 0x0054 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2 0x0055 +#define PCI_DEVICE_ID_NVIDIA_NVENET_8 0x0056 +#define PCI_DEVICE_ID_NVIDIA_NVENET_9 0x0057 +#define PCI_DEVICE_ID_NVIDIA_CK804_AUDIO 0x0059 +#define PCI_DEVICE_ID_NVIDIA_CK804_PCIE 0x005d +#define PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS 0x0064 +#define PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE 0x0065 +#define PCI_DEVICE_ID_NVIDIA_NVENET_2 0x0066 +#define PCI_DEVICE_ID_NVIDIA_MCP2_MODEM 0x0069 +#define PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO 0x006a +#define PCI_DEVICE_ID_NVIDIA_NFORCE2S_SMBUS 0x0084 +#define PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE 0x0085 +#define PCI_DEVICE_ID_NVIDIA_NVENET_4 0x0086 +#define PCI_DEVICE_ID_NVIDIA_MCP2S_MODEM 0x0089 +#define PCI_DEVICE_ID_NVIDIA_CK8_AUDIO 0x008a +#define PCI_DEVICE_ID_NVIDIA_NVENET_5 0x008c +#define PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA 0x008e +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_7800_GT 0x0090 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_7800_GTX 0x0091 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_7800 0x0098 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_7800_GTX 0x0099 +#define PCI_DEVICE_ID_NVIDIA_ITNT2 0x00A0 +#define PCI_DEVICE_ID_GEFORCE_6800A 0x00c1 +#define PCI_DEVICE_ID_GEFORCE_6800A_LE 0x00c2 +#define PCI_DEVICE_ID_GEFORCE_GO_6800 0x00c8 +#define PCI_DEVICE_ID_GEFORCE_GO_6800_ULTRA 0x00c9 +#define PCI_DEVICE_ID_QUADRO_FX_GO1400 0x00cc +#define PCI_DEVICE_ID_QUADRO_FX_1400 0x00ce +#define PCI_DEVICE_ID_NVIDIA_NFORCE3 0x00d1 +#define PCI_DEVICE_ID_NVIDIA_NFORCE3_SMBUS 0x00d4 +#define PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE 0x00d5 +#define PCI_DEVICE_ID_NVIDIA_NVENET_3 0x00d6 +#define PCI_DEVICE_ID_NVIDIA_MCP3_MODEM 0x00d9 +#define PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO 0x00da +#define PCI_DEVICE_ID_NVIDIA_NVENET_7 0x00df +#define PCI_DEVICE_ID_NVIDIA_NFORCE3S 0x00e1 +#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA 0x00e3 +#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SMBUS 0x00e4 +#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE 0x00e5 +#define PCI_DEVICE_ID_NVIDIA_NVENET_6 0x00e6 +#define PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO 0x00ea +#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2 0x00ee +#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6800_ALT1 0x00f0 +#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6600_ALT1 0x00f1 +#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6600_ALT2 0x00f2 +#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6200_ALT1 0x00f3 +#define PCIE_DEVICE_ID_NVIDIA_GEFORCE_6800_GT 0x00f9 +#define PCIE_DEVICE_ID_NVIDIA_QUADRO_NVS280 0x00fd +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_SDR 0x0100 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_DDR 0x0101 +#define PCI_DEVICE_ID_NVIDIA_QUADRO 0x0103 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX 0x0110 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX2 0x0111 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GO 0x0112 +#define PCI_DEVICE_ID_NVIDIA_QUADRO2_MXR 0x0113 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6600_GT 0x0140 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6600 0x0141 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6610_XL 0x0145 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_540 0x014E +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6200 0x014F +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS 0x0150 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS2 0x0151 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_ULTRA 0x0152 +#define PCI_DEVICE_ID_NVIDIA_QUADRO2_PRO 0x0153 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6200_TURBOCACHE 0x0161 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6200 0x0164 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6250 0x0166 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6200_1 0x0167 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6250_1 0x0168 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_460 0x0170 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440 0x0171 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_420 0x0172 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440_SE 0x0173 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO 0x0174 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO 0x0175 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO_M32 0x0176 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_460_GO 0x0177 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_500XGL 0x0178 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO_M64 0x0179 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_200 0x017A +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_550XGL 0x017B +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_500_GOGL 0x017C +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_410_GO_M16 0x017D +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440_8X 0x0181 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440SE_8X 0x0182 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_420_8X 0x0183 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_4000 0x0185 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_448_GO 0x0186 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_488_GO 0x0187 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_580_XGL 0x0188 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_MAC 0x0189 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_280_NVS 0x018A +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_380_XGL 0x018B +#define PCI_DEVICE_ID_NVIDIA_IGEFORCE2 0x01a0 +#define PCI_DEVICE_ID_NVIDIA_NFORCE 0x01a4 +#define PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO 0x01b1 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_SMBUS 0x01b4 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_IDE 0x01bc +#define PCI_DEVICE_ID_NVIDIA_MCP1_MODEM 0x01c1 +#define PCI_DEVICE_ID_NVIDIA_NVENET_1 0x01c3 +#define PCI_DEVICE_ID_NVIDIA_NFORCE2 0x01e0 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE3 0x0200 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE3_1 0x0201 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE3_2 0x0202 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_DDC 0x0203 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B 0x0211 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B_LE 0x0212 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B_GT 0x0215 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4600 0x0250 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4400 0x0251 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4200 0x0253 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_900XGL 0x0258 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_750XGL 0x0259 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_700XGL 0x025B +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS 0x0264 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE 0x0265 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA 0x0266 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2 0x0267 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS 0x0368 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE 0x036E +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA 0x037E +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2 0x037F +#define PCI_DEVICE_ID_NVIDIA_NVENET_12 0x0268 +#define PCI_DEVICE_ID_NVIDIA_NVENET_13 0x0269 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800 0x0280 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800_8X 0x0281 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800SE 0x0282 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE4_4200_GO 0x0286 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_980_XGL 0x0288 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_780_XGL 0x0289 +#define PCI_DEVICE_ID_NVIDIA_QUADRO4_700_GOGL 0x028C +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5800_ULTRA 0x0301 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5800 0x0302 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_2000 0x0308 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_1000 0x0309 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600_ULTRA 0x0311 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600 0x0312 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600SE 0x0314 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5600 0x031A +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5650 0x031B +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_GO700 0x031C +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200 0x0320 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200_ULTRA 0x0321 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200_1 0x0322 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200SE 0x0323 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5200 0x0324 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5250 0x0325 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5500 0x0326 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5100 0x0327 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5250_32 0x0328 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO_5200 0x0329 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_NVS_280_PCI 0x032A +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_500 0x032B +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5300 0x032C +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5100 0x032D +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900_ULTRA 0x0330 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900 0x0331 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900XT 0x0332 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5950_ULTRA 0x0333 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900ZT 0x0334 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_3000 0x0338 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_700 0x033F +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700_ULTRA 0x0341 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700 0x0342 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700LE 0x0343 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700VE 0x0344 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5700_1 0x0347 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5700_2 0x0348 +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_GO1000 0x034C +#define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_1100 0x034E +#define PCI_DEVICE_ID_NVIDIA_NVENET_14 0x0372 +#define PCI_DEVICE_ID_NVIDIA_NVENET_15 0x0373 +#define PCI_DEVICE_ID_NVIDIA_NVENET_16 0x03E5 +#define PCI_DEVICE_ID_NVIDIA_NVENET_17 0x03E6 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA 0x03E7 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SMBUS 0x03EB +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE 0x03EC +#define PCI_DEVICE_ID_NVIDIA_NVENET_18 0x03EE +#define PCI_DEVICE_ID_NVIDIA_NVENET_19 0x03EF +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA2 0x03F6 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA3 0x03F7 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_SMBUS 0x0446 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_IDE 0x0448 +#define PCI_DEVICE_ID_NVIDIA_NVENET_20 0x0450 +#define PCI_DEVICE_ID_NVIDIA_NVENET_21 0x0451 +#define PCI_DEVICE_ID_NVIDIA_NVENET_22 0x0452 +#define PCI_DEVICE_ID_NVIDIA_NVENET_23 0x0453 +#define PCI_DEVICE_ID_NVIDIA_NVENET_24 0x054C +#define PCI_DEVICE_ID_NVIDIA_NVENET_25 0x054D +#define PCI_DEVICE_ID_NVIDIA_NVENET_26 0x054E +#define PCI_DEVICE_ID_NVIDIA_NVENET_27 0x054F +#define PCI_DEVICE_ID_NVIDIA_NVENET_28 0x07DC +#define PCI_DEVICE_ID_NVIDIA_NVENET_29 0x07DD +#define PCI_DEVICE_ID_NVIDIA_NVENET_30 0x07DE +#define PCI_DEVICE_ID_NVIDIA_NVENET_31 0x07DF +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_IDE 0x0560 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_IDE 0x056C +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP77_IDE 0x0759 +#define PCI_DEVICE_ID_NVIDIA_NVENET_32 0x0760 +#define PCI_DEVICE_ID_NVIDIA_NVENET_33 0x0761 +#define PCI_DEVICE_ID_NVIDIA_NVENET_34 0x0762 +#define PCI_DEVICE_ID_NVIDIA_NVENET_35 0x0763 +#define PCI_DEVICE_ID_NVIDIA_NVENET_36 0x0AB0 +#define PCI_DEVICE_ID_NVIDIA_NVENET_37 0x0AB1 +#define PCI_DEVICE_ID_NVIDIA_NVENET_38 0x0AB2 +#define PCI_DEVICE_ID_NVIDIA_NVENET_39 0x0AB3 + +#define PCI_VENDOR_ID_IMS 0x10e0 +#define PCI_DEVICE_ID_IMS_TT128 0x9128 +#define PCI_DEVICE_ID_IMS_TT3D 0x9135 + +#define PCI_VENDOR_ID_INTERG 0x10ea +#define PCI_DEVICE_ID_INTERG_1682 0x1682 +#define PCI_DEVICE_ID_INTERG_2000 0x2000 +#define PCI_DEVICE_ID_INTERG_2010 0x2010 +#define PCI_DEVICE_ID_INTERG_5000 0x5000 +#define PCI_DEVICE_ID_INTERG_5050 0x5050 + +#define PCI_VENDOR_ID_REALTEK 0x10ec +#define PCI_DEVICE_ID_REALTEK_8139 0x8139 + +#define PCI_VENDOR_ID_XILINX 0x10ee +#define PCI_DEVICE_ID_RME_DIGI96 0x3fc0 +#define PCI_DEVICE_ID_RME_DIGI96_8 0x3fc1 +#define PCI_DEVICE_ID_RME_DIGI96_8_PRO 0x3fc2 +#define PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST 0x3fc3 +#define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP 0x3fc5 +#define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI 0x3fc6 + +#define PCI_VENDOR_ID_INIT 0x1101 + +#define PCI_VENDOR_ID_CREATIVE 0x1102 /* duplicate: ECTIVA */ +#define PCI_DEVICE_ID_CREATIVE_EMU10K1 0x0002 + +#define PCI_VENDOR_ID_ECTIVA 0x1102 /* duplicate: CREATIVE */ +#define PCI_DEVICE_ID_ECTIVA_EV1938 0x8938 + +#define PCI_VENDOR_ID_TTI 0x1103 +#define PCI_DEVICE_ID_TTI_HPT343 0x0003 +#define PCI_DEVICE_ID_TTI_HPT366 0x0004 +#define PCI_DEVICE_ID_TTI_HPT372 0x0005 +#define PCI_DEVICE_ID_TTI_HPT302 0x0006 +#define PCI_DEVICE_ID_TTI_HPT371 0x0007 +#define PCI_DEVICE_ID_TTI_HPT374 0x0008 +#define PCI_DEVICE_ID_TTI_HPT372N 0x0009 /* apparently a 372N variant? */ + +#define PCI_VENDOR_ID_VIA 0x1106 +#define PCI_DEVICE_ID_VIA_8763_0 0x0198 +#define PCI_DEVICE_ID_VIA_8380_0 0x0204 +#define PCI_DEVICE_ID_VIA_3238_0 0x0238 +#define PCI_DEVICE_ID_VIA_PT880 0x0258 +#define PCI_DEVICE_ID_VIA_PT880ULTRA 0x0308 +#define PCI_DEVICE_ID_VIA_PX8X0_0 0x0259 +#define PCI_DEVICE_ID_VIA_3269_0 0x0269 +#define PCI_DEVICE_ID_VIA_K8T800PRO_0 0x0282 +#define PCI_DEVICE_ID_VIA_3296_0 0x0296 +#define PCI_DEVICE_ID_VIA_8363_0 0x0305 +#define PCI_DEVICE_ID_VIA_P4M800CE 0x0314 +#define PCI_DEVICE_ID_VIA_P4M890 0x0327 +#define PCI_DEVICE_ID_VIA_VT3324 0x0324 +#define PCI_DEVICE_ID_VIA_VT3336 0x0336 +#define PCI_DEVICE_ID_VIA_VT3351 0x0351 +#define PCI_DEVICE_ID_VIA_VT3364 0x0364 +#define PCI_DEVICE_ID_VIA_8371_0 0x0391 +#define PCI_DEVICE_ID_VIA_8501_0 0x0501 +#define PCI_DEVICE_ID_VIA_82C561 0x0561 +#define PCI_DEVICE_ID_VIA_82C586_1 0x0571 +#define PCI_DEVICE_ID_VIA_82C576 0x0576 +#define PCI_DEVICE_ID_VIA_82C586_0 0x0586 +#define PCI_DEVICE_ID_VIA_82C596 0x0596 +#define PCI_DEVICE_ID_VIA_82C597_0 0x0597 +#define PCI_DEVICE_ID_VIA_82C598_0 0x0598 +#define PCI_DEVICE_ID_VIA_8601_0 0x0601 +#define PCI_DEVICE_ID_VIA_8605_0 0x0605 +#define PCI_DEVICE_ID_VIA_82C686 0x0686 +#define PCI_DEVICE_ID_VIA_82C691_0 0x0691 +#define PCI_DEVICE_ID_VIA_82C576_1 0x1571 +#define PCI_DEVICE_ID_VIA_82C586_2 0x3038 +#define PCI_DEVICE_ID_VIA_82C586_3 0x3040 +#define PCI_DEVICE_ID_VIA_82C596_3 0x3050 +#define PCI_DEVICE_ID_VIA_82C596B_3 0x3051 +#define PCI_DEVICE_ID_VIA_82C686_4 0x3057 +#define PCI_DEVICE_ID_VIA_82C686_5 0x3058 +#define PCI_DEVICE_ID_VIA_8233_5 0x3059 +#define PCI_DEVICE_ID_VIA_8233_0 0x3074 +#define PCI_DEVICE_ID_VIA_8633_0 0x3091 +#define PCI_DEVICE_ID_VIA_8367_0 0x3099 +#define PCI_DEVICE_ID_VIA_8653_0 0x3101 +#define PCI_DEVICE_ID_VIA_8622 0x3102 +#define PCI_DEVICE_ID_VIA_8235_USB_2 0x3104 +#define PCI_DEVICE_ID_VIA_8233C_0 0x3109 +#define PCI_DEVICE_ID_VIA_8361 0x3112 +#define PCI_DEVICE_ID_VIA_XM266 0x3116 +#define PCI_DEVICE_ID_VIA_612X 0x3119 +#define PCI_DEVICE_ID_VIA_862X_0 0x3123 +#define PCI_DEVICE_ID_VIA_8753_0 0x3128 +#define PCI_DEVICE_ID_VIA_8233A 0x3147 +#define PCI_DEVICE_ID_VIA_8703_51_0 0x3148 +#define PCI_DEVICE_ID_VIA_8237_SATA 0x3149 +#define PCI_DEVICE_ID_VIA_XN266 0x3156 +#define PCI_DEVICE_ID_VIA_6410 0x3164 +#define PCI_DEVICE_ID_VIA_8754C_0 0x3168 +#define PCI_DEVICE_ID_VIA_8235 0x3177 +#define PCI_DEVICE_ID_VIA_8385_0 0x3188 +#define PCI_DEVICE_ID_VIA_8377_0 0x3189 +#define PCI_DEVICE_ID_VIA_8378_0 0x3205 +#define PCI_DEVICE_ID_VIA_8783_0 0x3208 +#define PCI_DEVICE_ID_VIA_8237 0x3227 +#define PCI_DEVICE_ID_VIA_8251 0x3287 +#define PCI_DEVICE_ID_VIA_8237A 0x3337 +#define PCI_DEVICE_ID_VIA_8237S 0x3372 +#define PCI_DEVICE_ID_VIA_SATA_EIDE 0x5324 +#define PCI_DEVICE_ID_VIA_8231 0x8231 +#define PCI_DEVICE_ID_VIA_8231_4 0x8235 +#define PCI_DEVICE_ID_VIA_8365_1 0x8305 +#define PCI_DEVICE_ID_VIA_CX700 0x8324 +#define PCI_DEVICE_ID_VIA_CX700_IDE 0x0581 +#define PCI_DEVICE_ID_VIA_VX800 0x8353 +#define PCI_DEVICE_ID_VIA_8371_1 0x8391 +#define PCI_DEVICE_ID_VIA_82C598_1 0x8598 +#define PCI_DEVICE_ID_VIA_838X_1 0xB188 +#define PCI_DEVICE_ID_VIA_83_87XX_1 0xB198 + +#define PCI_VENDOR_ID_SIEMENS 0x110A +#define PCI_DEVICE_ID_SIEMENS_DSCC4 0x2102 + +#define PCI_VENDOR_ID_VORTEX 0x1119 +#define PCI_DEVICE_ID_VORTEX_GDT60x0 0x0000 +#define PCI_DEVICE_ID_VORTEX_GDT6000B 0x0001 +#define PCI_DEVICE_ID_VORTEX_GDT6x10 0x0002 +#define PCI_DEVICE_ID_VORTEX_GDT6x20 0x0003 +#define PCI_DEVICE_ID_VORTEX_GDT6530 0x0004 +#define PCI_DEVICE_ID_VORTEX_GDT6550 0x0005 +#define PCI_DEVICE_ID_VORTEX_GDT6x17 0x0006 +#define PCI_DEVICE_ID_VORTEX_GDT6x27 0x0007 +#define PCI_DEVICE_ID_VORTEX_GDT6537 0x0008 +#define PCI_DEVICE_ID_VORTEX_GDT6557 0x0009 +#define PCI_DEVICE_ID_VORTEX_GDT6x15 0x000a +#define PCI_DEVICE_ID_VORTEX_GDT6x25 0x000b +#define PCI_DEVICE_ID_VORTEX_GDT6535 0x000c +#define PCI_DEVICE_ID_VORTEX_GDT6555 0x000d +#define PCI_DEVICE_ID_VORTEX_GDT6x17RP 0x0100 +#define PCI_DEVICE_ID_VORTEX_GDT6x27RP 0x0101 +#define PCI_DEVICE_ID_VORTEX_GDT6537RP 0x0102 +#define PCI_DEVICE_ID_VORTEX_GDT6557RP 0x0103 +#define PCI_DEVICE_ID_VORTEX_GDT6x11RP 0x0104 +#define PCI_DEVICE_ID_VORTEX_GDT6x21RP 0x0105 + +#define PCI_VENDOR_ID_EF 0x111a +#define PCI_DEVICE_ID_EF_ATM_FPGA 0x0000 +#define PCI_DEVICE_ID_EF_ATM_ASIC 0x0002 +#define PCI_DEVICE_ID_EF_ATM_LANAI2 0x0003 +#define PCI_DEVICE_ID_EF_ATM_LANAIHB 0x0005 + +#define PCI_VENDOR_ID_IDT 0x111d +#define PCI_DEVICE_ID_IDT_IDT77201 0x0001 + +#define PCI_VENDOR_ID_FORE 0x1127 +#define PCI_DEVICE_ID_FORE_PCA200E 0x0300 + +#define PCI_VENDOR_ID_PHILIPS 0x1131 +#define PCI_DEVICE_ID_PHILIPS_SAA7146 0x7146 +#define PCI_DEVICE_ID_PHILIPS_SAA9730 0x9730 + +#define PCI_VENDOR_ID_EICON 0x1133 +#define PCI_DEVICE_ID_EICON_DIVA20 0xe002 +#define PCI_DEVICE_ID_EICON_DIVA20_U 0xe004 +#define PCI_DEVICE_ID_EICON_DIVA201 0xe005 +#define PCI_DEVICE_ID_EICON_DIVA202 0xe00b +#define PCI_DEVICE_ID_EICON_MAESTRA 0xe010 +#define PCI_DEVICE_ID_EICON_MAESTRAQ 0xe012 +#define PCI_DEVICE_ID_EICON_MAESTRAQ_U 0xe013 +#define PCI_DEVICE_ID_EICON_MAESTRAP 0xe014 + +#define PCI_VENDOR_ID_CISCO 0x1137 + +#define PCI_VENDOR_ID_ZIATECH 0x1138 +#define PCI_DEVICE_ID_ZIATECH_5550_HC 0x5550 + + +#define PCI_VENDOR_ID_SYSKONNECT 0x1148 +#define PCI_DEVICE_ID_SYSKONNECT_TR 0x4200 +#define PCI_DEVICE_ID_SYSKONNECT_GE 0x4300 +#define PCI_DEVICE_ID_SYSKONNECT_YU 0x4320 +#define PCI_DEVICE_ID_SYSKONNECT_9DXX 0x4400 +#define PCI_DEVICE_ID_SYSKONNECT_9MXX 0x4500 + +#define PCI_VENDOR_ID_DIGI 0x114f +#define PCI_DEVICE_ID_DIGI_DF_M_IOM2_E 0x0070 +#define PCI_DEVICE_ID_DIGI_DF_M_E 0x0071 +#define PCI_DEVICE_ID_DIGI_DF_M_IOM2_A 0x0072 +#define PCI_DEVICE_ID_DIGI_DF_M_A 0x0073 +#define PCI_DEVICE_ID_NEO_2DB9 0x00C8 +#define PCI_DEVICE_ID_NEO_2DB9PRI 0x00C9 +#define PCI_DEVICE_ID_NEO_2RJ45 0x00CA +#define PCI_DEVICE_ID_NEO_2RJ45PRI 0x00CB +#define PCIE_DEVICE_ID_NEO_4_IBM 0x00F4 + +#define PCI_VENDOR_ID_XIRCOM 0x115d +#define PCI_DEVICE_ID_XIRCOM_RBM56G 0x0101 +#define PCI_DEVICE_ID_XIRCOM_X3201_MDM 0x0103 + +#define PCI_VENDOR_ID_SERVERWORKS 0x1166 +#define PCI_DEVICE_ID_SERVERWORKS_HE 0x0008 +#define PCI_DEVICE_ID_SERVERWORKS_LE 0x0009 +#define PCI_DEVICE_ID_SERVERWORKS_GCNB_LE 0x0017 +#define PCI_DEVICE_ID_SERVERWORKS_HT1000_PXB 0x0036 +#define PCI_DEVICE_ID_SERVERWORKS_EPB 0x0103 +#define PCI_DEVICE_ID_SERVERWORKS_HT2000_PCIE 0x0132 +#define PCI_DEVICE_ID_SERVERWORKS_OSB4 0x0200 +#define PCI_DEVICE_ID_SERVERWORKS_CSB5 0x0201 +#define PCI_DEVICE_ID_SERVERWORKS_CSB6 0x0203 +#define PCI_DEVICE_ID_SERVERWORKS_HT1000SB 0x0205 +#define PCI_DEVICE_ID_SERVERWORKS_OSB4IDE 0x0211 +#define PCI_DEVICE_ID_SERVERWORKS_CSB5IDE 0x0212 +#define PCI_DEVICE_ID_SERVERWORKS_CSB6IDE 0x0213 +#define PCI_DEVICE_ID_SERVERWORKS_HT1000IDE 0x0214 +#define PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2 0x0217 +#define PCI_DEVICE_ID_SERVERWORKS_CSB6LPC 0x0227 + +#define PCI_VENDOR_ID_SBE 0x1176 +#define PCI_DEVICE_ID_SBE_WANXL100 0x0301 +#define PCI_DEVICE_ID_SBE_WANXL200 0x0302 +#define PCI_DEVICE_ID_SBE_WANXL400 0x0104 + +#define PCI_VENDOR_ID_TOSHIBA 0x1179 +#define PCI_DEVICE_ID_TOSHIBA_PICCOLO 0x0102 +#define PCI_DEVICE_ID_TOSHIBA_PICCOLO_1 0x0103 +#define PCI_DEVICE_ID_TOSHIBA_PICCOLO_2 0x0105 +#define PCI_DEVICE_ID_TOSHIBA_TOPIC95 0x060a +#define PCI_DEVICE_ID_TOSHIBA_TOPIC97 0x060f +#define PCI_DEVICE_ID_TOSHIBA_TOPIC100 0x0617 + +#define PCI_VENDOR_ID_TOSHIBA_2 0x102f +#define PCI_DEVICE_ID_TOSHIBA_TC35815CF 0x0030 +#define PCI_DEVICE_ID_TOSHIBA_TC35815_NWU 0x0031 +#define PCI_DEVICE_ID_TOSHIBA_TC35815_TX4939 0x0032 +#define PCI_DEVICE_ID_TOSHIBA_TC86C001_IDE 0x0105 +#define PCI_DEVICE_ID_TOSHIBA_TC86C001_MISC 0x0108 +#define PCI_DEVICE_ID_TOSHIBA_SPIDER_NET 0x01b3 + +#define PCI_VENDOR_ID_ATTO 0x117c + +#define PCI_VENDOR_ID_RICOH 0x1180 +#define PCI_DEVICE_ID_RICOH_RL5C465 0x0465 +#define PCI_DEVICE_ID_RICOH_RL5C466 0x0466 +#define PCI_DEVICE_ID_RICOH_RL5C475 0x0475 +#define PCI_DEVICE_ID_RICOH_RL5C476 0x0476 +#define PCI_DEVICE_ID_RICOH_RL5C478 0x0478 +#define PCI_DEVICE_ID_RICOH_R5C822 0x0822 +#define PCI_DEVICE_ID_RICOH_R5C832 0x0832 +#define PCI_DEVICE_ID_RICOH_R5C843 0x0843 + +#define PCI_VENDOR_ID_DLINK 0x1186 +#define PCI_DEVICE_ID_DLINK_DGE510T 0x4c00 + +#define PCI_VENDOR_ID_ARTOP 0x1191 +#define PCI_DEVICE_ID_ARTOP_ATP850UF 0x0005 +#define PCI_DEVICE_ID_ARTOP_ATP860 0x0006 +#define PCI_DEVICE_ID_ARTOP_ATP860R 0x0007 +#define PCI_DEVICE_ID_ARTOP_ATP865 0x0008 +#define PCI_DEVICE_ID_ARTOP_ATP865R 0x0009 +#define PCI_DEVICE_ID_ARTOP_AEC7610 0x8002 +#define PCI_DEVICE_ID_ARTOP_AEC7612UW 0x8010 +#define PCI_DEVICE_ID_ARTOP_AEC7612U 0x8020 +#define PCI_DEVICE_ID_ARTOP_AEC7612S 0x8030 +#define PCI_DEVICE_ID_ARTOP_AEC7612D 0x8040 +#define PCI_DEVICE_ID_ARTOP_AEC7612SUW 0x8050 +#define PCI_DEVICE_ID_ARTOP_8060 0x8060 + +#define PCI_VENDOR_ID_ZEITNET 0x1193 +#define PCI_DEVICE_ID_ZEITNET_1221 0x0001 +#define PCI_DEVICE_ID_ZEITNET_1225 0x0002 + +#define PCI_VENDOR_ID_FUJITSU_ME 0x119e +#define PCI_DEVICE_ID_FUJITSU_FS155 0x0001 +#define PCI_DEVICE_ID_FUJITSU_FS50 0x0003 + +#define PCI_SUBVENDOR_ID_KEYSPAN 0x11a9 +#define PCI_SUBDEVICE_ID_KEYSPAN_SX2 0x5334 + +#define PCI_VENDOR_ID_MARVELL 0x11ab +#define PCI_DEVICE_ID_MARVELL_GT64111 0x4146 +#define PCI_DEVICE_ID_MARVELL_GT64260 0x6430 +#define PCI_DEVICE_ID_MARVELL_MV64360 0x6460 +#define PCI_DEVICE_ID_MARVELL_MV64460 0x6480 +#define PCI_DEVICE_ID_MARVELL_88ALP01_NAND 0x4100 +#define PCI_DEVICE_ID_MARVELL_88ALP01_SD 0x4101 +#define PCI_DEVICE_ID_MARVELL_88ALP01_CCIC 0x4102 + +#define PCI_VENDOR_ID_V3 0x11b0 +#define PCI_DEVICE_ID_V3_V960 0x0001 +#define PCI_DEVICE_ID_V3_V351 0x0002 + +#define PCI_VENDOR_ID_ATT 0x11c1 +#define PCI_DEVICE_ID_ATT_VENUS_MODEM 0x480 + +#define PCI_VENDOR_ID_SPECIALIX 0x11cb +#define PCI_DEVICE_ID_SPECIALIX_IO8 0x2000 +#define PCI_DEVICE_ID_SPECIALIX_RIO 0x8000 +#define PCI_SUBDEVICE_ID_SPECIALIX_SPEED4 0xa004 + +#define PCI_VENDOR_ID_ANALOG_DEVICES 0x11d4 +#define PCI_DEVICE_ID_AD1889JS 0x1889 + +#define PCI_DEVICE_ID_SEGA_BBA 0x1234 + +#define PCI_VENDOR_ID_ZORAN 0x11de +#define PCI_DEVICE_ID_ZORAN_36057 0x6057 +#define PCI_DEVICE_ID_ZORAN_36120 0x6120 + +#define PCI_VENDOR_ID_COMPEX 0x11f6 +#define PCI_DEVICE_ID_COMPEX_ENET100VG4 0x0112 + +#define PCI_VENDOR_ID_RP 0x11fe +#define PCI_DEVICE_ID_RP32INTF 0x0001 +#define PCI_DEVICE_ID_RP8INTF 0x0002 +#define PCI_DEVICE_ID_RP16INTF 0x0003 +#define PCI_DEVICE_ID_RP4QUAD 0x0004 +#define PCI_DEVICE_ID_RP8OCTA 0x0005 +#define PCI_DEVICE_ID_RP8J 0x0006 +#define PCI_DEVICE_ID_RP4J 0x0007 +#define PCI_DEVICE_ID_RP8SNI 0x0008 +#define PCI_DEVICE_ID_RP16SNI 0x0009 +#define PCI_DEVICE_ID_RPP4 0x000A +#define PCI_DEVICE_ID_RPP8 0x000B +#define PCI_DEVICE_ID_RP4M 0x000D +#define PCI_DEVICE_ID_RP2_232 0x000E +#define PCI_DEVICE_ID_RP2_422 0x000F +#define PCI_DEVICE_ID_URP32INTF 0x0801 +#define PCI_DEVICE_ID_URP8INTF 0x0802 +#define PCI_DEVICE_ID_URP16INTF 0x0803 +#define PCI_DEVICE_ID_URP8OCTA 0x0805 +#define PCI_DEVICE_ID_UPCI_RM3_8PORT 0x080C +#define PCI_DEVICE_ID_UPCI_RM3_4PORT 0x080D +#define PCI_DEVICE_ID_CRP16INTF 0x0903 + +#define PCI_VENDOR_ID_CYCLADES 0x120e +#define PCI_DEVICE_ID_CYCLOM_Y_Lo 0x0100 +#define PCI_DEVICE_ID_CYCLOM_Y_Hi 0x0101 +#define PCI_DEVICE_ID_CYCLOM_4Y_Lo 0x0102 +#define PCI_DEVICE_ID_CYCLOM_4Y_Hi 0x0103 +#define PCI_DEVICE_ID_CYCLOM_8Y_Lo 0x0104 +#define PCI_DEVICE_ID_CYCLOM_8Y_Hi 0x0105 +#define PCI_DEVICE_ID_CYCLOM_Z_Lo 0x0200 +#define PCI_DEVICE_ID_CYCLOM_Z_Hi 0x0201 +#define PCI_DEVICE_ID_PC300_RX_2 0x0300 +#define PCI_DEVICE_ID_PC300_RX_1 0x0301 +#define PCI_DEVICE_ID_PC300_TE_2 0x0310 +#define PCI_DEVICE_ID_PC300_TE_1 0x0311 +#define PCI_DEVICE_ID_PC300_TE_M_2 0x0320 +#define PCI_DEVICE_ID_PC300_TE_M_1 0x0321 + +#define PCI_VENDOR_ID_ESSENTIAL 0x120f +#define PCI_DEVICE_ID_ESSENTIAL_ROADRUNNER 0x0001 + +#define PCI_VENDOR_ID_O2 0x1217 +#define PCI_DEVICE_ID_O2_6729 0x6729 +#define PCI_DEVICE_ID_O2_6730 0x673a +#define PCI_DEVICE_ID_O2_6832 0x6832 +#define PCI_DEVICE_ID_O2_6836 0x6836 + +#define PCI_VENDOR_ID_3DFX 0x121a +#define PCI_DEVICE_ID_3DFX_VOODOO 0x0001 +#define PCI_DEVICE_ID_3DFX_VOODOO2 0x0002 +#define PCI_DEVICE_ID_3DFX_BANSHEE 0x0003 +#define PCI_DEVICE_ID_3DFX_VOODOO3 0x0005 +#define PCI_DEVICE_ID_3DFX_VOODOO5 0x0009 + +#define PCI_VENDOR_ID_AVM 0x1244 +#define PCI_DEVICE_ID_AVM_B1 0x0700 +#define PCI_DEVICE_ID_AVM_C4 0x0800 +#define PCI_DEVICE_ID_AVM_A1 0x0a00 +#define PCI_DEVICE_ID_AVM_A1_V2 0x0e00 +#define PCI_DEVICE_ID_AVM_C2 0x1100 +#define PCI_DEVICE_ID_AVM_T1 0x1200 + +#define PCI_VENDOR_ID_STALLION 0x124d + +/* Allied Telesyn */ +#define PCI_VENDOR_ID_AT 0x1259 +#define PCI_SUBDEVICE_ID_AT_2700FX 0x2701 +#define PCI_SUBDEVICE_ID_AT_2701FX 0x2703 + +#define PCI_VENDOR_ID_ESS 0x125d +#define PCI_DEVICE_ID_ESS_ESS1968 0x1968 +#define PCI_DEVICE_ID_ESS_ESS1978 0x1978 +#define PCI_DEVICE_ID_ESS_ALLEGRO_1 0x1988 +#define PCI_DEVICE_ID_ESS_ALLEGRO 0x1989 +#define PCI_DEVICE_ID_ESS_CANYON3D_2LE 0x1990 +#define PCI_DEVICE_ID_ESS_CANYON3D_2 0x1992 +#define PCI_DEVICE_ID_ESS_MAESTRO3 0x1998 +#define PCI_DEVICE_ID_ESS_MAESTRO3_1 0x1999 +#define PCI_DEVICE_ID_ESS_MAESTRO3_HW 0x199a +#define PCI_DEVICE_ID_ESS_MAESTRO3_2 0x199b + +#define PCI_VENDOR_ID_SATSAGEM 0x1267 +#define PCI_DEVICE_ID_SATSAGEM_NICCY 0x1016 + +#define PCI_VENDOR_ID_ENSONIQ 0x1274 +#define PCI_DEVICE_ID_ENSONIQ_CT5880 0x5880 +#define PCI_DEVICE_ID_ENSONIQ_ES1370 0x5000 +#define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371 + +#define PCI_VENDOR_ID_TRANSMETA 0x1279 +#define PCI_DEVICE_ID_EFFICEON 0x0060 + +#define PCI_VENDOR_ID_ROCKWELL 0x127A + +#define PCI_VENDOR_ID_ITE 0x1283 +#define PCI_DEVICE_ID_ITE_8211 0x8211 +#define PCI_DEVICE_ID_ITE_8212 0x8212 +#define PCI_DEVICE_ID_ITE_8213 0x8213 +#define PCI_DEVICE_ID_ITE_8152 0x8152 +#define PCI_DEVICE_ID_ITE_8872 0x8872 +#define PCI_DEVICE_ID_ITE_IT8330G_0 0xe886 + +/* formerly Platform Tech */ +#define PCI_DEVICE_ID_ESS_ESS0100 0x0100 + +#define PCI_VENDOR_ID_ALTEON 0x12ae + +#define PCI_SUBVENDOR_ID_CONNECT_TECH 0x12c4 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232 0x0001 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232 0x0002 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232 0x0003 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485 0x0004 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4 0x0005 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485 0x0006 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2 0x0007 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485 0x0008 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6 0x0009 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1 0x000A +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1 0x000B +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_20MHZ 0x000C +#define PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_PTM 0x000D +#define PCI_SUBDEVICE_ID_CONNECT_TECH_NT960PCI 0x0100 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_2 0x0201 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_4 0x0202 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_232 0x0300 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_232 0x0301 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_232 0x0302 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_1_1 0x0310 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_2 0x0311 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4 0x0312 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2 0x0320 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4 0x0321 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8 0x0322 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_485 0x0330 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_485 0x0331 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_485 0x0332 + +#define PCI_VENDOR_ID_NVIDIA_SGS 0x12d2 +#define PCI_DEVICE_ID_NVIDIA_SGS_RIVA128 0x0018 + +#define PCI_SUBVENDOR_ID_CHASE_PCIFAST 0x12E0 +#define PCI_SUBDEVICE_ID_CHASE_PCIFAST4 0x0031 +#define PCI_SUBDEVICE_ID_CHASE_PCIFAST8 0x0021 +#define PCI_SUBDEVICE_ID_CHASE_PCIFAST16 0x0011 +#define PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC 0x0041 +#define PCI_SUBVENDOR_ID_CHASE_PCIRAS 0x124D +#define PCI_SUBDEVICE_ID_CHASE_PCIRAS4 0xF001 +#define PCI_SUBDEVICE_ID_CHASE_PCIRAS8 0xF010 + +#define PCI_VENDOR_ID_AUREAL 0x12eb +#define PCI_DEVICE_ID_AUREAL_VORTEX_1 0x0001 +#define PCI_DEVICE_ID_AUREAL_VORTEX_2 0x0002 +#define PCI_DEVICE_ID_AUREAL_ADVANTAGE 0x0003 + +#define PCI_VENDOR_ID_ELECTRONICDESIGNGMBH 0x12f8 +#define PCI_DEVICE_ID_LML_33R10 0x8a02 + +#define PCI_VENDOR_ID_ESDGMBH 0x12fe +#define PCI_DEVICE_ID_ESDGMBH_CPCIASIO4 0x0111 + +#define PCI_VENDOR_ID_SIIG 0x131f +#define PCI_SUBVENDOR_ID_SIIG 0x131f +#define PCI_DEVICE_ID_SIIG_1S_10x_550 0x1000 +#define PCI_DEVICE_ID_SIIG_1S_10x_650 0x1001 +#define PCI_DEVICE_ID_SIIG_1S_10x_850 0x1002 +#define PCI_DEVICE_ID_SIIG_1S1P_10x_550 0x1010 +#define PCI_DEVICE_ID_SIIG_1S1P_10x_650 0x1011 +#define PCI_DEVICE_ID_SIIG_1S1P_10x_850 0x1012 +#define PCI_DEVICE_ID_SIIG_1P_10x 0x1020 +#define PCI_DEVICE_ID_SIIG_2P_10x 0x1021 +#define PCI_DEVICE_ID_SIIG_2S_10x_550 0x1030 +#define PCI_DEVICE_ID_SIIG_2S_10x_650 0x1031 +#define PCI_DEVICE_ID_SIIG_2S_10x_850 0x1032 +#define PCI_DEVICE_ID_SIIG_2S1P_10x_550 0x1034 +#define PCI_DEVICE_ID_SIIG_2S1P_10x_650 0x1035 +#define PCI_DEVICE_ID_SIIG_2S1P_10x_850 0x1036 +#define PCI_DEVICE_ID_SIIG_4S_10x_550 0x1050 +#define PCI_DEVICE_ID_SIIG_4S_10x_650 0x1051 +#define PCI_DEVICE_ID_SIIG_4S_10x_850 0x1052 +#define PCI_DEVICE_ID_SIIG_1S_20x_550 0x2000 +#define PCI_DEVICE_ID_SIIG_1S_20x_650 0x2001 +#define PCI_DEVICE_ID_SIIG_1S_20x_850 0x2002 +#define PCI_DEVICE_ID_SIIG_1P_20x 0x2020 +#define PCI_DEVICE_ID_SIIG_2P_20x 0x2021 +#define PCI_DEVICE_ID_SIIG_2S_20x_550 0x2030 +#define PCI_DEVICE_ID_SIIG_2S_20x_650 0x2031 +#define PCI_DEVICE_ID_SIIG_2S_20x_850 0x2032 +#define PCI_DEVICE_ID_SIIG_2P1S_20x_550 0x2040 +#define PCI_DEVICE_ID_SIIG_2P1S_20x_650 0x2041 +#define PCI_DEVICE_ID_SIIG_2P1S_20x_850 0x2042 +#define PCI_DEVICE_ID_SIIG_1S1P_20x_550 0x2010 +#define PCI_DEVICE_ID_SIIG_1S1P_20x_650 0x2011 +#define PCI_DEVICE_ID_SIIG_1S1P_20x_850 0x2012 +#define PCI_DEVICE_ID_SIIG_4S_20x_550 0x2050 +#define PCI_DEVICE_ID_SIIG_4S_20x_650 0x2051 +#define PCI_DEVICE_ID_SIIG_4S_20x_850 0x2052 +#define PCI_DEVICE_ID_SIIG_2S1P_20x_550 0x2060 +#define PCI_DEVICE_ID_SIIG_2S1P_20x_650 0x2061 +#define PCI_DEVICE_ID_SIIG_2S1P_20x_850 0x2062 +#define PCI_DEVICE_ID_SIIG_8S_20x_550 0x2080 +#define PCI_DEVICE_ID_SIIG_8S_20x_650 0x2081 +#define PCI_DEVICE_ID_SIIG_8S_20x_850 0x2082 +#define PCI_SUBDEVICE_ID_SIIG_QUARTET_SERIAL 0x2050 + +#define PCI_VENDOR_ID_RADISYS 0x1331 + +#define PCI_VENDOR_ID_MICRO_MEMORY 0x1332 +#define PCI_DEVICE_ID_MICRO_MEMORY_5415CN 0x5415 +#define PCI_DEVICE_ID_MICRO_MEMORY_5425CN 0x5425 +#define PCI_DEVICE_ID_MICRO_MEMORY_6155 0x6155 + +#define PCI_VENDOR_ID_DOMEX 0x134a +#define PCI_DEVICE_ID_DOMEX_DMX3191D 0x0001 + +#define PCI_VENDOR_ID_INTASHIELD 0x135a +#define PCI_DEVICE_ID_INTASHIELD_IS200 0x0d80 +#define PCI_DEVICE_ID_INTASHIELD_IS400 0x0dc0 + +#define PCI_VENDOR_ID_QUATECH 0x135C +#define PCI_DEVICE_ID_QUATECH_QSC100 0x0010 +#define PCI_DEVICE_ID_QUATECH_DSC100 0x0020 +#define PCI_DEVICE_ID_QUATECH_ESC100D 0x0050 +#define PCI_DEVICE_ID_QUATECH_ESC100M 0x0060 +#define PCI_DEVICE_ID_QUATECH_SPPXP_100 0x0278 + +#define PCI_VENDOR_ID_SEALEVEL 0x135e +#define PCI_DEVICE_ID_SEALEVEL_U530 0x7101 +#define PCI_DEVICE_ID_SEALEVEL_UCOMM2 0x7201 +#define PCI_DEVICE_ID_SEALEVEL_UCOMM422 0x7402 +#define PCI_DEVICE_ID_SEALEVEL_UCOMM232 0x7202 +#define PCI_DEVICE_ID_SEALEVEL_COMM4 0x7401 +#define PCI_DEVICE_ID_SEALEVEL_COMM8 0x7801 +#define PCI_DEVICE_ID_SEALEVEL_UCOMM8 0x7804 + +#define PCI_VENDOR_ID_HYPERCOPE 0x1365 +#define PCI_DEVICE_ID_HYPERCOPE_PLX 0x9050 +#define PCI_SUBDEVICE_ID_HYPERCOPE_OLD_ERGO 0x0104 +#define PCI_SUBDEVICE_ID_HYPERCOPE_ERGO 0x0106 +#define PCI_SUBDEVICE_ID_HYPERCOPE_METRO 0x0107 +#define PCI_SUBDEVICE_ID_HYPERCOPE_CHAMP2 0x0108 + +#define PCI_VENDOR_ID_KAWASAKI 0x136b +#define PCI_DEVICE_ID_MCHIP_KL5A72002 0xff01 + +#define PCI_VENDOR_ID_CNET 0x1371 +#define PCI_DEVICE_ID_CNET_GIGACARD 0x434e + +#define PCI_VENDOR_ID_LMC 0x1376 +#define PCI_DEVICE_ID_LMC_HSSI 0x0003 +#define PCI_DEVICE_ID_LMC_DS3 0x0004 +#define PCI_DEVICE_ID_LMC_SSI 0x0005 +#define PCI_DEVICE_ID_LMC_T1 0x0006 + +#define PCI_VENDOR_ID_NETGEAR 0x1385 +#define PCI_DEVICE_ID_NETGEAR_GA620 0x620a + +#define PCI_VENDOR_ID_APPLICOM 0x1389 +#define PCI_DEVICE_ID_APPLICOM_PCIGENERIC 0x0001 +#define PCI_DEVICE_ID_APPLICOM_PCI2000IBS_CAN 0x0002 +#define PCI_DEVICE_ID_APPLICOM_PCI2000PFB 0x0003 + +#define PCI_VENDOR_ID_MOXA 0x1393 +#define PCI_DEVICE_ID_MOXA_RC7000 0x0001 +#define PCI_DEVICE_ID_MOXA_CP102 0x1020 +#define PCI_DEVICE_ID_MOXA_CP102UL 0x1021 +#define PCI_DEVICE_ID_MOXA_CP102U 0x1022 +#define PCI_DEVICE_ID_MOXA_C104 0x1040 +#define PCI_DEVICE_ID_MOXA_CP104U 0x1041 +#define PCI_DEVICE_ID_MOXA_CP104JU 0x1042 +#define PCI_DEVICE_ID_MOXA_CP104EL 0x1043 +#define PCI_DEVICE_ID_MOXA_CT114 0x1140 +#define PCI_DEVICE_ID_MOXA_CP114 0x1141 +#define PCI_DEVICE_ID_MOXA_CP118U 0x1180 +#define PCI_DEVICE_ID_MOXA_CP118EL 0x1181 +#define PCI_DEVICE_ID_MOXA_CP132 0x1320 +#define PCI_DEVICE_ID_MOXA_CP132U 0x1321 +#define PCI_DEVICE_ID_MOXA_CP134U 0x1340 +#define PCI_DEVICE_ID_MOXA_C168 0x1680 +#define PCI_DEVICE_ID_MOXA_CP168U 0x1681 +#define PCI_DEVICE_ID_MOXA_CP168EL 0x1682 +#define PCI_DEVICE_ID_MOXA_CP204J 0x2040 +#define PCI_DEVICE_ID_MOXA_C218 0x2180 +#define PCI_DEVICE_ID_MOXA_C320 0x3200 + +#define PCI_VENDOR_ID_CCD 0x1397 +#define PCI_DEVICE_ID_CCD_HFC4S 0x08B4 +#define PCI_SUBDEVICE_ID_CCD_PMX2S 0x1234 +#define PCI_DEVICE_ID_CCD_HFC8S 0x16B8 +#define PCI_DEVICE_ID_CCD_2BD0 0x2bd0 +#define PCI_DEVICE_ID_CCD_HFCE1 0x30B1 +#define PCI_SUBDEVICE_ID_CCD_SPD4S 0x3136 +#define PCI_SUBDEVICE_ID_CCD_SPDE1 0x3137 +#define PCI_DEVICE_ID_CCD_B000 0xb000 +#define PCI_DEVICE_ID_CCD_B006 0xb006 +#define PCI_DEVICE_ID_CCD_B007 0xb007 +#define PCI_DEVICE_ID_CCD_B008 0xb008 +#define PCI_DEVICE_ID_CCD_B009 0xb009 +#define PCI_DEVICE_ID_CCD_B00A 0xb00a +#define PCI_DEVICE_ID_CCD_B00B 0xb00b +#define PCI_DEVICE_ID_CCD_B00C 0xb00c +#define PCI_DEVICE_ID_CCD_B100 0xb100 +#define PCI_SUBDEVICE_ID_CCD_IOB4ST 0xB520 +#define PCI_SUBDEVICE_ID_CCD_IOB8STR 0xB521 +#define PCI_SUBDEVICE_ID_CCD_IOB8ST 0xB522 +#define PCI_SUBDEVICE_ID_CCD_IOB1E1 0xB523 +#define PCI_SUBDEVICE_ID_CCD_SWYX4S 0xB540 +#define PCI_SUBDEVICE_ID_CCD_JH4S20 0xB550 +#define PCI_SUBDEVICE_ID_CCD_IOB8ST_1 0xB552 +#define PCI_SUBDEVICE_ID_CCD_BN4S 0xB560 +#define PCI_SUBDEVICE_ID_CCD_BN8S 0xB562 +#define PCI_SUBDEVICE_ID_CCD_BNE1 0xB563 +#define PCI_SUBDEVICE_ID_CCD_BNE1D 0xB564 +#define PCI_SUBDEVICE_ID_CCD_BNE1DP 0xB565 +#define PCI_SUBDEVICE_ID_CCD_BN2S 0xB566 +#define PCI_SUBDEVICE_ID_CCD_BN1SM 0xB567 +#define PCI_SUBDEVICE_ID_CCD_BN4SM 0xB568 +#define PCI_SUBDEVICE_ID_CCD_BN2SM 0xB569 +#define PCI_SUBDEVICE_ID_CCD_BNE1M 0xB56A +#define PCI_SUBDEVICE_ID_CCD_BN8SP 0xB56B +#define PCI_SUBDEVICE_ID_CCD_HFC4S 0xB620 +#define PCI_SUBDEVICE_ID_CCD_HFC8S 0xB622 +#define PCI_DEVICE_ID_CCD_B700 0xb700 +#define PCI_DEVICE_ID_CCD_B701 0xb701 +#define PCI_SUBDEVICE_ID_CCD_HFCE1 0xC523 +#define PCI_SUBDEVICE_ID_CCD_OV2S 0xE884 +#define PCI_SUBDEVICE_ID_CCD_OV4S 0xE888 +#define PCI_SUBDEVICE_ID_CCD_OV8S 0xE998 + +#define PCI_VENDOR_ID_EXAR 0x13a8 +#define PCI_DEVICE_ID_EXAR_XR17C152 0x0152 +#define PCI_DEVICE_ID_EXAR_XR17C154 0x0154 +#define PCI_DEVICE_ID_EXAR_XR17C158 0x0158 + +#define PCI_VENDOR_ID_MICROGATE 0x13c0 +#define PCI_DEVICE_ID_MICROGATE_USC 0x0010 +#define PCI_DEVICE_ID_MICROGATE_SCA 0x0030 + +#define PCI_VENDOR_ID_3WARE 0x13C1 +#define PCI_DEVICE_ID_3WARE_1000 0x1000 +#define PCI_DEVICE_ID_3WARE_7000 0x1001 +#define PCI_DEVICE_ID_3WARE_9000 0x1002 + +#define PCI_VENDOR_ID_IOMEGA 0x13ca +#define PCI_DEVICE_ID_IOMEGA_BUZ 0x4231 + +#define PCI_VENDOR_ID_ABOCOM 0x13D1 +#define PCI_DEVICE_ID_ABOCOM_2BD1 0x2BD1 + +#define PCI_VENDOR_ID_SUNDANCE 0x13f0 + +#define PCI_VENDOR_ID_CMEDIA 0x13f6 +#define PCI_DEVICE_ID_CMEDIA_CM8338A 0x0100 +#define PCI_DEVICE_ID_CMEDIA_CM8338B 0x0101 +#define PCI_DEVICE_ID_CMEDIA_CM8738 0x0111 +#define PCI_DEVICE_ID_CMEDIA_CM8738B 0x0112 + +#define PCI_VENDOR_ID_LAVA 0x1407 +#define PCI_DEVICE_ID_LAVA_DSERIAL 0x0100 /* 2x 16550 */ +#define PCI_DEVICE_ID_LAVA_QUATRO_A 0x0101 /* 2x 16550, half of 4 port */ +#define PCI_DEVICE_ID_LAVA_QUATRO_B 0x0102 /* 2x 16550, half of 4 port */ +#define PCI_DEVICE_ID_LAVA_OCTO_A 0x0180 /* 4x 16550A, half of 8 port */ +#define PCI_DEVICE_ID_LAVA_OCTO_B 0x0181 /* 4x 16550A, half of 8 port */ +#define PCI_DEVICE_ID_LAVA_PORT_PLUS 0x0200 /* 2x 16650 */ +#define PCI_DEVICE_ID_LAVA_QUAD_A 0x0201 /* 2x 16650, half of 4 port */ +#define PCI_DEVICE_ID_LAVA_QUAD_B 0x0202 /* 2x 16650, half of 4 port */ +#define PCI_DEVICE_ID_LAVA_SSERIAL 0x0500 /* 1x 16550 */ +#define PCI_DEVICE_ID_LAVA_PORT_650 0x0600 /* 1x 16650 */ +#define PCI_DEVICE_ID_LAVA_PARALLEL 0x8000 +#define PCI_DEVICE_ID_LAVA_DUAL_PAR_A 0x8002 /* The Lava Dual Parallel is */ +#define PCI_DEVICE_ID_LAVA_DUAL_PAR_B 0x8003 /* two PCI devices on a card */ +#define PCI_DEVICE_ID_LAVA_BOCA_IOPPAR 0x8800 + +#define PCI_VENDOR_ID_TIMEDIA 0x1409 +#define PCI_DEVICE_ID_TIMEDIA_1889 0x7168 + +#define PCI_VENDOR_ID_ICE 0x1412 +#define PCI_DEVICE_ID_ICE_1712 0x1712 +#define PCI_DEVICE_ID_VT1724 0x1724 + +#define PCI_VENDOR_ID_OXSEMI 0x1415 +#define PCI_DEVICE_ID_OXSEMI_12PCI840 0x8403 +#define PCI_DEVICE_ID_OXSEMI_PCIe840 0xC000 +#define PCI_DEVICE_ID_OXSEMI_PCIe840_G 0xC004 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_0 0xC100 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_0_G 0xC104 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_1 0xC110 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_1_G 0xC114 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_1_U 0xC118 +#define PCI_DEVICE_ID_OXSEMI_PCIe952_1_GU 0xC11C +#define PCI_DEVICE_ID_OXSEMI_16PCI954 0x9501 +#define PCI_DEVICE_ID_OXSEMI_16PCI95N 0x9511 +#define PCI_DEVICE_ID_OXSEMI_16PCI954PP 0x9513 +#define PCI_DEVICE_ID_OXSEMI_16PCI952 0x9521 +#define PCI_DEVICE_ID_OXSEMI_16PCI952PP 0x9523 + +#define PCI_VENDOR_ID_CHELSIO 0x1425 + +#define PCI_VENDOR_ID_SAMSUNG 0x144d + +#define PCI_VENDOR_ID_MYRICOM 0x14c1 + +#define PCI_VENDOR_ID_TITAN 0x14D2 +#define PCI_DEVICE_ID_TITAN_010L 0x8001 +#define PCI_DEVICE_ID_TITAN_100L 0x8010 +#define PCI_DEVICE_ID_TITAN_110L 0x8011 +#define PCI_DEVICE_ID_TITAN_200L 0x8020 +#define PCI_DEVICE_ID_TITAN_210L 0x8021 +#define PCI_DEVICE_ID_TITAN_400L 0x8040 +#define PCI_DEVICE_ID_TITAN_800L 0x8080 +#define PCI_DEVICE_ID_TITAN_100 0xA001 +#define PCI_DEVICE_ID_TITAN_200 0xA005 +#define PCI_DEVICE_ID_TITAN_400 0xA003 +#define PCI_DEVICE_ID_TITAN_800B 0xA004 + +#define PCI_VENDOR_ID_PANACOM 0x14d4 +#define PCI_DEVICE_ID_PANACOM_QUADMODEM 0x0400 +#define PCI_DEVICE_ID_PANACOM_DUALMODEM 0x0402 + +#define PCI_VENDOR_ID_SIPACKETS 0x14d9 +#define PCI_DEVICE_ID_SP1011 0x0010 + +#define PCI_VENDOR_ID_AFAVLAB 0x14db +#define PCI_DEVICE_ID_AFAVLAB_P028 0x2180 +#define PCI_DEVICE_ID_AFAVLAB_P030 0x2182 +#define PCI_SUBDEVICE_ID_AFAVLAB_P061 0x2150 + +#define PCI_VENDOR_ID_BROADCOM 0x14e4 +#define PCI_DEVICE_ID_TIGON3_5752 0x1600 +#define PCI_DEVICE_ID_TIGON3_5752M 0x1601 +#define PCI_DEVICE_ID_NX2_5709 0x1639 +#define PCI_DEVICE_ID_NX2_5709S 0x163a +#define PCI_DEVICE_ID_TIGON3_5700 0x1644 +#define PCI_DEVICE_ID_TIGON3_5701 0x1645 +#define PCI_DEVICE_ID_TIGON3_5702 0x1646 +#define PCI_DEVICE_ID_TIGON3_5703 0x1647 +#define PCI_DEVICE_ID_TIGON3_5704 0x1648 +#define PCI_DEVICE_ID_TIGON3_5704S_2 0x1649 +#define PCI_DEVICE_ID_NX2_5706 0x164a +#define PCI_DEVICE_ID_NX2_5708 0x164c +#define PCI_DEVICE_ID_TIGON3_5702FE 0x164d +#define PCI_DEVICE_ID_NX2_57710 0x164e +#define PCI_DEVICE_ID_NX2_57711 0x164f +#define PCI_DEVICE_ID_NX2_57711E 0x1650 +#define PCI_DEVICE_ID_TIGON3_5705 0x1653 +#define PCI_DEVICE_ID_TIGON3_5705_2 0x1654 +#define PCI_DEVICE_ID_TIGON3_5720 0x1658 +#define PCI_DEVICE_ID_TIGON3_5721 0x1659 +#define PCI_DEVICE_ID_TIGON3_5722 0x165a +#define PCI_DEVICE_ID_TIGON3_5723 0x165b +#define PCI_DEVICE_ID_TIGON3_5705M 0x165d +#define PCI_DEVICE_ID_TIGON3_5705M_2 0x165e +#define PCI_DEVICE_ID_TIGON3_5714 0x1668 +#define PCI_DEVICE_ID_TIGON3_5714S 0x1669 +#define PCI_DEVICE_ID_TIGON3_5780 0x166a +#define PCI_DEVICE_ID_TIGON3_5780S 0x166b +#define PCI_DEVICE_ID_TIGON3_5705F 0x166e +#define PCI_DEVICE_ID_TIGON3_5754M 0x1672 +#define PCI_DEVICE_ID_TIGON3_5755M 0x1673 +#define PCI_DEVICE_ID_TIGON3_5756 0x1674 +#define PCI_DEVICE_ID_TIGON3_5750 0x1676 +#define PCI_DEVICE_ID_TIGON3_5751 0x1677 +#define PCI_DEVICE_ID_TIGON3_5715 0x1678 +#define PCI_DEVICE_ID_TIGON3_5715S 0x1679 +#define PCI_DEVICE_ID_TIGON3_5754 0x167a +#define PCI_DEVICE_ID_TIGON3_5755 0x167b +#define PCI_DEVICE_ID_TIGON3_5750M 0x167c +#define PCI_DEVICE_ID_TIGON3_5751M 0x167d +#define PCI_DEVICE_ID_TIGON3_5751F 0x167e +#define PCI_DEVICE_ID_TIGON3_5787F 0x167f +#define PCI_DEVICE_ID_TIGON3_5761E 0x1680 +#define PCI_DEVICE_ID_TIGON3_5761 0x1681 +#define PCI_DEVICE_ID_TIGON3_5764 0x1684 +#define PCI_DEVICE_ID_TIGON3_5787M 0x1693 +#define PCI_DEVICE_ID_TIGON3_5782 0x1696 +#define PCI_DEVICE_ID_TIGON3_5784 0x1698 +#define PCI_DEVICE_ID_TIGON3_5785 0x1699 +#define PCI_DEVICE_ID_TIGON3_5786 0x169a +#define PCI_DEVICE_ID_TIGON3_5787 0x169b +#define PCI_DEVICE_ID_TIGON3_5788 0x169c +#define PCI_DEVICE_ID_TIGON3_5789 0x169d +#define PCI_DEVICE_ID_TIGON3_5702X 0x16a6 +#define PCI_DEVICE_ID_TIGON3_5703X 0x16a7 +#define PCI_DEVICE_ID_TIGON3_5704S 0x16a8 +#define PCI_DEVICE_ID_NX2_5706S 0x16aa +#define PCI_DEVICE_ID_NX2_5708S 0x16ac +#define PCI_DEVICE_ID_TIGON3_5702A3 0x16c6 +#define PCI_DEVICE_ID_TIGON3_5703A3 0x16c7 +#define PCI_DEVICE_ID_TIGON3_5781 0x16dd +#define PCI_DEVICE_ID_TIGON3_5753 0x16f7 +#define PCI_DEVICE_ID_TIGON3_5753M 0x16fd +#define PCI_DEVICE_ID_TIGON3_5753F 0x16fe +#define PCI_DEVICE_ID_TIGON3_5901 0x170d +#define PCI_DEVICE_ID_BCM4401B1 0x170c +#define PCI_DEVICE_ID_TIGON3_5901_2 0x170e +#define PCI_DEVICE_ID_TIGON3_5906 0x1712 +#define PCI_DEVICE_ID_TIGON3_5906M 0x1713 +#define PCI_DEVICE_ID_BCM4401 0x4401 +#define PCI_DEVICE_ID_BCM4401B0 0x4402 + +#define PCI_VENDOR_ID_TOPIC 0x151f +#define PCI_DEVICE_ID_TOPIC_TP560 0x0000 + +#define PCI_VENDOR_ID_MAINPINE 0x1522 +#define PCI_DEVICE_ID_MAINPINE_PBRIDGE 0x0100 +#define PCI_VENDOR_ID_ENE 0x1524 +#define PCI_DEVICE_ID_ENE_CB712_SD 0x0550 +#define PCI_DEVICE_ID_ENE_CB712_SD_2 0x0551 +#define PCI_DEVICE_ID_ENE_CB714_SD 0x0750 +#define PCI_DEVICE_ID_ENE_CB714_SD_2 0x0751 +#define PCI_DEVICE_ID_ENE_1211 0x1211 +#define PCI_DEVICE_ID_ENE_1225 0x1225 +#define PCI_DEVICE_ID_ENE_1410 0x1410 +#define PCI_DEVICE_ID_ENE_710 0x1411 +#define PCI_DEVICE_ID_ENE_712 0x1412 +#define PCI_DEVICE_ID_ENE_1420 0x1420 +#define PCI_DEVICE_ID_ENE_720 0x1421 +#define PCI_DEVICE_ID_ENE_722 0x1422 + +#define PCI_SUBVENDOR_ID_PERLE 0x155f +#define PCI_SUBDEVICE_ID_PCI_RAS4 0xf001 +#define PCI_SUBDEVICE_ID_PCI_RAS8 0xf010 + +#define PCI_VENDOR_ID_SYBA 0x1592 +#define PCI_DEVICE_ID_SYBA_2P_EPP 0x0782 +#define PCI_DEVICE_ID_SYBA_1P_ECP 0x0783 + +#define PCI_VENDOR_ID_MORETON 0x15aa +#define PCI_DEVICE_ID_RASTEL_2PORT 0x2000 + +#define PCI_VENDOR_ID_ZOLTRIX 0x15b0 +#define PCI_DEVICE_ID_ZOLTRIX_2BD0 0x2bd0 + +#define PCI_VENDOR_ID_MELLANOX 0x15b3 +#define PCI_DEVICE_ID_MELLANOX_TAVOR 0x5a44 +#define PCI_DEVICE_ID_MELLANOX_TAVOR_BRIDGE 0x5a46 +#define PCI_DEVICE_ID_MELLANOX_ARBEL_COMPAT 0x6278 +#define PCI_DEVICE_ID_MELLANOX_ARBEL 0x6282 +#define PCI_DEVICE_ID_MELLANOX_SINAI_OLD 0x5e8c +#define PCI_DEVICE_ID_MELLANOX_SINAI 0x6274 + +#define PCI_VENDOR_ID_QUICKNET 0x15e2 +#define PCI_DEVICE_ID_QUICKNET_XJ 0x0500 + +/* + * ADDI-DATA GmbH communication cards <info@addi-data.com> + */ +#define PCI_VENDOR_ID_ADDIDATA_OLD 0x10E8 +#define PCI_VENDOR_ID_ADDIDATA 0x15B8 +#define PCI_DEVICE_ID_ADDIDATA_APCI7500 0x7000 +#define PCI_DEVICE_ID_ADDIDATA_APCI7420 0x7001 +#define PCI_DEVICE_ID_ADDIDATA_APCI7300 0x7002 +#define PCI_DEVICE_ID_ADDIDATA_APCI7800 0x818E +#define PCI_DEVICE_ID_ADDIDATA_APCI7500_2 0x7009 +#define PCI_DEVICE_ID_ADDIDATA_APCI7420_2 0x700A +#define PCI_DEVICE_ID_ADDIDATA_APCI7300_2 0x700B +#define PCI_DEVICE_ID_ADDIDATA_APCI7500_3 0x700C +#define PCI_DEVICE_ID_ADDIDATA_APCI7420_3 0x700D +#define PCI_DEVICE_ID_ADDIDATA_APCI7300_3 0x700E +#define PCI_DEVICE_ID_ADDIDATA_APCI7800_3 0x700F + +#define PCI_VENDOR_ID_PDC 0x15e9 + +#define PCI_VENDOR_ID_FARSITE 0x1619 +#define PCI_DEVICE_ID_FARSITE_T2P 0x0400 +#define PCI_DEVICE_ID_FARSITE_T4P 0x0440 +#define PCI_DEVICE_ID_FARSITE_T1U 0x0610 +#define PCI_DEVICE_ID_FARSITE_T2U 0x0620 +#define PCI_DEVICE_ID_FARSITE_T4U 0x0640 +#define PCI_DEVICE_ID_FARSITE_TE1 0x1610 +#define PCI_DEVICE_ID_FARSITE_TE1C 0x1612 + +#define PCI_VENDOR_ID_ARIMA 0x161f + +#define PCI_VENDOR_ID_BROCADE 0x1657 + +#define PCI_VENDOR_ID_SIBYTE 0x166d +#define PCI_DEVICE_ID_BCM1250_PCI 0x0001 +#define PCI_DEVICE_ID_BCM1250_HT 0x0002 + +#define PCI_VENDOR_ID_ATHEROS 0x168c + +#define PCI_VENDOR_ID_NETCELL 0x169c +#define PCI_DEVICE_ID_REVOLUTION 0x0044 + +#define PCI_VENDOR_ID_CENATEK 0x16CA +#define PCI_DEVICE_ID_CENATEK_IDE 0x0001 + +#define PCI_VENDOR_ID_VITESSE 0x1725 +#define PCI_DEVICE_ID_VITESSE_VSC7174 0x7174 + +#define PCI_VENDOR_ID_LINKSYS 0x1737 +#define PCI_DEVICE_ID_LINKSYS_EG1064 0x1064 + +#define PCI_VENDOR_ID_ALTIMA 0x173b +#define PCI_DEVICE_ID_ALTIMA_AC1000 0x03e8 +#define PCI_DEVICE_ID_ALTIMA_AC1001 0x03e9 +#define PCI_DEVICE_ID_ALTIMA_AC9100 0x03ea +#define PCI_DEVICE_ID_ALTIMA_AC1003 0x03eb + +#define PCI_VENDOR_ID_BELKIN 0x1799 +#define PCI_DEVICE_ID_BELKIN_F5D7010V7 0x701f + +#define PCI_VENDOR_ID_RDC 0x17f3 +#define PCI_DEVICE_ID_RDC_R6020 0x6020 +#define PCI_DEVICE_ID_RDC_R6030 0x6030 +#define PCI_DEVICE_ID_RDC_R6040 0x6040 +#define PCI_DEVICE_ID_RDC_R6060 0x6060 +#define PCI_DEVICE_ID_RDC_R6061 0x6061 + +#define PCI_VENDOR_ID_LENOVO 0x17aa + +#define PCI_VENDOR_ID_ARECA 0x17d3 +#define PCI_DEVICE_ID_ARECA_1110 0x1110 +#define PCI_DEVICE_ID_ARECA_1120 0x1120 +#define PCI_DEVICE_ID_ARECA_1130 0x1130 +#define PCI_DEVICE_ID_ARECA_1160 0x1160 +#define PCI_DEVICE_ID_ARECA_1170 0x1170 +#define PCI_DEVICE_ID_ARECA_1200 0x1200 +#define PCI_DEVICE_ID_ARECA_1201 0x1201 +#define PCI_DEVICE_ID_ARECA_1202 0x1202 +#define PCI_DEVICE_ID_ARECA_1210 0x1210 +#define PCI_DEVICE_ID_ARECA_1220 0x1220 +#define PCI_DEVICE_ID_ARECA_1230 0x1230 +#define PCI_DEVICE_ID_ARECA_1260 0x1260 +#define PCI_DEVICE_ID_ARECA_1270 0x1270 +#define PCI_DEVICE_ID_ARECA_1280 0x1280 +#define PCI_DEVICE_ID_ARECA_1380 0x1380 +#define PCI_DEVICE_ID_ARECA_1381 0x1381 +#define PCI_DEVICE_ID_ARECA_1680 0x1680 +#define PCI_DEVICE_ID_ARECA_1681 0x1681 + +#define PCI_VENDOR_ID_S2IO 0x17d5 +#define PCI_DEVICE_ID_S2IO_WIN 0x5731 +#define PCI_DEVICE_ID_S2IO_UNI 0x5831 +#define PCI_DEVICE_ID_HERC_WIN 0x5732 +#define PCI_DEVICE_ID_HERC_UNI 0x5832 + +#define PCI_VENDOR_ID_SITECOM 0x182d +#define PCI_DEVICE_ID_SITECOM_DC105V2 0x3069 + +#define PCI_VENDOR_ID_TOPSPIN 0x1867 + +#define PCI_VENDOR_ID_TDI 0x192E +#define PCI_DEVICE_ID_TDI_EHCI 0x0101 + +#define PCI_VENDOR_ID_FREESCALE 0x1957 +#define PCI_DEVICE_ID_MPC8548E 0x0012 +#define PCI_DEVICE_ID_MPC8548 0x0013 +#define PCI_DEVICE_ID_MPC8543E 0x0014 +#define PCI_DEVICE_ID_MPC8543 0x0015 +#define PCI_DEVICE_ID_MPC8547E 0x0018 +#define PCI_DEVICE_ID_MPC8545E 0x0019 +#define PCI_DEVICE_ID_MPC8545 0x001a +#define PCI_DEVICE_ID_MPC8568E 0x0020 +#define PCI_DEVICE_ID_MPC8568 0x0021 +#define PCI_DEVICE_ID_MPC8567E 0x0022 +#define PCI_DEVICE_ID_MPC8567 0x0023 +#define PCI_DEVICE_ID_MPC8533E 0x0030 +#define PCI_DEVICE_ID_MPC8533 0x0031 +#define PCI_DEVICE_ID_MPC8544E 0x0032 +#define PCI_DEVICE_ID_MPC8544 0x0033 +#define PCI_DEVICE_ID_MPC8572E 0x0040 +#define PCI_DEVICE_ID_MPC8572 0x0041 +#define PCI_DEVICE_ID_MPC8536E 0x0050 +#define PCI_DEVICE_ID_MPC8536 0x0051 +#define PCI_DEVICE_ID_MPC8641 0x7010 +#define PCI_DEVICE_ID_MPC8641D 0x7011 +#define PCI_DEVICE_ID_MPC8610 0x7018 + +#define PCI_VENDOR_ID_PASEMI 0x1959 + +#define PCI_VENDOR_ID_ATTANSIC 0x1969 +#define PCI_DEVICE_ID_ATTANSIC_L1 0x1048 +#define PCI_DEVICE_ID_ATTANSIC_L2 0x2048 + +#define PCI_VENDOR_ID_JMICRON 0x197B +#define PCI_DEVICE_ID_JMICRON_JMB360 0x2360 +#define PCI_DEVICE_ID_JMICRON_JMB361 0x2361 +#define PCI_DEVICE_ID_JMICRON_JMB363 0x2363 +#define PCI_DEVICE_ID_JMICRON_JMB365 0x2365 +#define PCI_DEVICE_ID_JMICRON_JMB366 0x2366 +#define PCI_DEVICE_ID_JMICRON_JMB368 0x2368 +#define PCI_DEVICE_ID_JMICRON_JMB38X_SD 0x2381 +#define PCI_DEVICE_ID_JMICRON_JMB38X_MMC 0x2382 +#define PCI_DEVICE_ID_JMICRON_JMB38X_MS 0x2383 + +#define PCI_VENDOR_ID_KORENIX 0x1982 +#define PCI_DEVICE_ID_KORENIX_JETCARDF0 0x1600 +#define PCI_DEVICE_ID_KORENIX_JETCARDF1 0x16ff + +#define PCI_VENDOR_ID_TEKRAM 0x1de1 +#define PCI_DEVICE_ID_TEKRAM_DC290 0xdc29 + +#define PCI_VENDOR_ID_TEHUTI 0x1fc9 +#define PCI_DEVICE_ID_TEHUTI_3009 0x3009 +#define PCI_DEVICE_ID_TEHUTI_3010 0x3010 +#define PCI_DEVICE_ID_TEHUTI_3014 0x3014 + +#define PCI_VENDOR_ID_HINT 0x3388 +#define PCI_DEVICE_ID_HINT_VXPROII_IDE 0x8013 + +#define PCI_VENDOR_ID_3DLABS 0x3d3d +#define PCI_DEVICE_ID_3DLABS_PERMEDIA2 0x0007 +#define PCI_DEVICE_ID_3DLABS_PERMEDIA2V 0x0009 + +#define PCI_VENDOR_ID_NETXEN 0x4040 +#define PCI_DEVICE_ID_NX2031_10GXSR 0x0001 +#define PCI_DEVICE_ID_NX2031_10GCX4 0x0002 +#define PCI_DEVICE_ID_NX2031_4GCU 0x0003 +#define PCI_DEVICE_ID_NX2031_IMEZ 0x0004 +#define PCI_DEVICE_ID_NX2031_HMEZ 0x0005 +#define PCI_DEVICE_ID_NX2031_XG_MGMT 0x0024 +#define PCI_DEVICE_ID_NX2031_XG_MGMT2 0x0025 +#define PCI_DEVICE_ID_NX3031 0x0100 + +#define PCI_VENDOR_ID_AKS 0x416c +#define PCI_DEVICE_ID_AKS_ALADDINCARD 0x0100 + +#define PCI_VENDOR_ID_S3 0x5333 +#define PCI_DEVICE_ID_S3_TRIO 0x8811 +#define PCI_DEVICE_ID_S3_868 0x8880 +#define PCI_DEVICE_ID_S3_968 0x88f0 +#define PCI_DEVICE_ID_S3_SAVAGE4 0x8a25 +#define PCI_DEVICE_ID_S3_PROSAVAGE8 0x8d04 +#define PCI_DEVICE_ID_S3_SONICVIBES 0xca00 + +#define PCI_VENDOR_ID_DUNORD 0x5544 +#define PCI_DEVICE_ID_DUNORD_I3000 0x0001 + +#define PCI_VENDOR_ID_DCI 0x6666 +#define PCI_DEVICE_ID_DCI_PCCOM4 0x0001 +#define PCI_DEVICE_ID_DCI_PCCOM8 0x0002 +#define PCI_DEVICE_ID_DCI_PCCOM2 0x0004 + +#define PCI_VENDOR_ID_INTEL 0x8086 +#define PCI_DEVICE_ID_INTEL_EESSC 0x0008 +#define PCI_DEVICE_ID_INTEL_PXHD_0 0x0320 +#define PCI_DEVICE_ID_INTEL_PXHD_1 0x0321 +#define PCI_DEVICE_ID_INTEL_PXH_0 0x0329 +#define PCI_DEVICE_ID_INTEL_PXH_1 0x032A +#define PCI_DEVICE_ID_INTEL_PXHV 0x032C +#define PCI_DEVICE_ID_INTEL_82375 0x0482 +#define PCI_DEVICE_ID_INTEL_82424 0x0483 +#define PCI_DEVICE_ID_INTEL_82378 0x0484 +#define PCI_DEVICE_ID_INTEL_I960 0x0960 +#define PCI_DEVICE_ID_INTEL_I960RM 0x0962 +#define PCI_DEVICE_ID_INTEL_82815_MC 0x1130 +#define PCI_DEVICE_ID_INTEL_82815_CGC 0x1132 +#define PCI_DEVICE_ID_INTEL_82092AA_0 0x1221 +#define PCI_DEVICE_ID_INTEL_7505_0 0x2550 +#define PCI_DEVICE_ID_INTEL_7205_0 0x255d +#define PCI_DEVICE_ID_INTEL_82437 0x122d +#define PCI_DEVICE_ID_INTEL_82371FB_0 0x122e +#define PCI_DEVICE_ID_INTEL_82371FB_1 0x1230 +#define PCI_DEVICE_ID_INTEL_82371MX 0x1234 +#define PCI_DEVICE_ID_INTEL_82441 0x1237 +#define PCI_DEVICE_ID_INTEL_82380FB 0x124b +#define PCI_DEVICE_ID_INTEL_82439 0x1250 +#define PCI_DEVICE_ID_INTEL_80960_RP 0x1960 +#define PCI_DEVICE_ID_INTEL_82840_HB 0x1a21 +#define PCI_DEVICE_ID_INTEL_82845_HB 0x1a30 +#define PCI_DEVICE_ID_INTEL_IOAT 0x1a38 +#define PCI_DEVICE_ID_INTEL_82801AA_0 0x2410 +#define PCI_DEVICE_ID_INTEL_82801AA_1 0x2411 +#define PCI_DEVICE_ID_INTEL_82801AA_3 0x2413 +#define PCI_DEVICE_ID_INTEL_82801AA_5 0x2415 +#define PCI_DEVICE_ID_INTEL_82801AA_6 0x2416 +#define PCI_DEVICE_ID_INTEL_82801AA_8 0x2418 +#define PCI_DEVICE_ID_INTEL_82801AB_0 0x2420 +#define PCI_DEVICE_ID_INTEL_82801AB_1 0x2421 +#define PCI_DEVICE_ID_INTEL_82801AB_3 0x2423 +#define PCI_DEVICE_ID_INTEL_82801AB_5 0x2425 +#define PCI_DEVICE_ID_INTEL_82801AB_6 0x2426 +#define PCI_DEVICE_ID_INTEL_82801AB_8 0x2428 +#define PCI_DEVICE_ID_INTEL_82801BA_0 0x2440 +#define PCI_DEVICE_ID_INTEL_82801BA_2 0x2443 +#define PCI_DEVICE_ID_INTEL_82801BA_4 0x2445 +#define PCI_DEVICE_ID_INTEL_82801BA_6 0x2448 +#define PCI_DEVICE_ID_INTEL_82801BA_8 0x244a +#define PCI_DEVICE_ID_INTEL_82801BA_9 0x244b +#define PCI_DEVICE_ID_INTEL_82801BA_10 0x244c +#define PCI_DEVICE_ID_INTEL_82801BA_11 0x244e +#define PCI_DEVICE_ID_INTEL_82801E_0 0x2450 +#define PCI_DEVICE_ID_INTEL_82801E_11 0x245b +#define PCI_DEVICE_ID_INTEL_82801CA_0 0x2480 +#define PCI_DEVICE_ID_INTEL_82801CA_3 0x2483 +#define PCI_DEVICE_ID_INTEL_82801CA_5 0x2485 +#define PCI_DEVICE_ID_INTEL_82801CA_6 0x2486 +#define PCI_DEVICE_ID_INTEL_82801CA_10 0x248a +#define PCI_DEVICE_ID_INTEL_82801CA_11 0x248b +#define PCI_DEVICE_ID_INTEL_82801CA_12 0x248c +#define PCI_DEVICE_ID_INTEL_82801DB_0 0x24c0 +#define PCI_DEVICE_ID_INTEL_82801DB_1 0x24c1 +#define PCI_DEVICE_ID_INTEL_82801DB_3 0x24c3 +#define PCI_DEVICE_ID_INTEL_82801DB_5 0x24c5 +#define PCI_DEVICE_ID_INTEL_82801DB_6 0x24c6 +#define PCI_DEVICE_ID_INTEL_82801DB_9 0x24c9 +#define PCI_DEVICE_ID_INTEL_82801DB_10 0x24ca +#define PCI_DEVICE_ID_INTEL_82801DB_11 0x24cb +#define PCI_DEVICE_ID_INTEL_82801DB_12 0x24cc +#define PCI_DEVICE_ID_INTEL_82801EB_0 0x24d0 +#define PCI_DEVICE_ID_INTEL_82801EB_1 0x24d1 +#define PCI_DEVICE_ID_INTEL_82801EB_3 0x24d3 +#define PCI_DEVICE_ID_INTEL_82801EB_5 0x24d5 +#define PCI_DEVICE_ID_INTEL_82801EB_6 0x24d6 +#define PCI_DEVICE_ID_INTEL_82801EB_11 0x24db +#define PCI_DEVICE_ID_INTEL_82801EB_12 0x24dc +#define PCI_DEVICE_ID_INTEL_82801EB_13 0x24dd +#define PCI_DEVICE_ID_INTEL_ESB_1 0x25a1 +#define PCI_DEVICE_ID_INTEL_ESB_2 0x25a2 +#define PCI_DEVICE_ID_INTEL_ESB_4 0x25a4 +#define PCI_DEVICE_ID_INTEL_ESB_5 0x25a6 +#define PCI_DEVICE_ID_INTEL_ESB_9 0x25ab +#define PCI_DEVICE_ID_INTEL_82820_HB 0x2500 +#define PCI_DEVICE_ID_INTEL_82820_UP_HB 0x2501 +#define PCI_DEVICE_ID_INTEL_82850_HB 0x2530 +#define PCI_DEVICE_ID_INTEL_82860_HB 0x2531 +#define PCI_DEVICE_ID_INTEL_E7501_MCH 0x254c +#define PCI_DEVICE_ID_INTEL_82845G_HB 0x2560 +#define PCI_DEVICE_ID_INTEL_82845G_IG 0x2562 +#define PCI_DEVICE_ID_INTEL_82865_HB 0x2570 +#define PCI_DEVICE_ID_INTEL_82865_IG 0x2572 +#define PCI_DEVICE_ID_INTEL_82875_HB 0x2578 +#define PCI_DEVICE_ID_INTEL_82915G_HB 0x2580 +#define PCI_DEVICE_ID_INTEL_82915G_IG 0x2582 +#define PCI_DEVICE_ID_INTEL_82915GM_HB 0x2590 +#define PCI_DEVICE_ID_INTEL_82915GM_IG 0x2592 +#define PCI_DEVICE_ID_INTEL_5000_ERR 0x25F0 +#define PCI_DEVICE_ID_INTEL_5000_FBD0 0x25F5 +#define PCI_DEVICE_ID_INTEL_5000_FBD1 0x25F6 +#define PCI_DEVICE_ID_INTEL_82945G_HB 0x2770 +#define PCI_DEVICE_ID_INTEL_82945G_IG 0x2772 +#define PCI_DEVICE_ID_INTEL_3000_HB 0x2778 +#define PCI_DEVICE_ID_INTEL_82945GM_HB 0x27A0 +#define PCI_DEVICE_ID_INTEL_82945GM_IG 0x27A2 +#define PCI_DEVICE_ID_INTEL_ICH6_0 0x2640 +#define PCI_DEVICE_ID_INTEL_ICH6_1 0x2641 +#define PCI_DEVICE_ID_INTEL_ICH6_2 0x2642 +#define PCI_DEVICE_ID_INTEL_ICH6_16 0x266a +#define PCI_DEVICE_ID_INTEL_ICH6_17 0x266d +#define PCI_DEVICE_ID_INTEL_ICH6_18 0x266e +#define PCI_DEVICE_ID_INTEL_ICH6_19 0x266f +#define PCI_DEVICE_ID_INTEL_ESB2_0 0x2670 +#define PCI_DEVICE_ID_INTEL_ESB2_14 0x2698 +#define PCI_DEVICE_ID_INTEL_ESB2_17 0x269b +#define PCI_DEVICE_ID_INTEL_ESB2_18 0x269e +#define PCI_DEVICE_ID_INTEL_ICH7_0 0x27b8 +#define PCI_DEVICE_ID_INTEL_ICH7_1 0x27b9 +#define PCI_DEVICE_ID_INTEL_ICH7_30 0x27b0 +#define PCI_DEVICE_ID_INTEL_ICH7_31 0x27bd +#define PCI_DEVICE_ID_INTEL_ICH7_17 0x27da +#define PCI_DEVICE_ID_INTEL_ICH7_19 0x27dd +#define PCI_DEVICE_ID_INTEL_ICH7_20 0x27de +#define PCI_DEVICE_ID_INTEL_ICH7_21 0x27df +#define PCI_DEVICE_ID_INTEL_ICH8_0 0x2810 +#define PCI_DEVICE_ID_INTEL_ICH8_1 0x2811 +#define PCI_DEVICE_ID_INTEL_ICH8_2 0x2812 +#define PCI_DEVICE_ID_INTEL_ICH8_3 0x2814 +#define PCI_DEVICE_ID_INTEL_ICH8_4 0x2815 +#define PCI_DEVICE_ID_INTEL_ICH8_5 0x283e +#define PCI_DEVICE_ID_INTEL_ICH8_6 0x2850 +#define PCI_DEVICE_ID_INTEL_ICH9_0 0x2910 +#define PCI_DEVICE_ID_INTEL_ICH9_1 0x2917 +#define PCI_DEVICE_ID_INTEL_ICH9_2 0x2912 +#define PCI_DEVICE_ID_INTEL_ICH9_3 0x2913 +#define PCI_DEVICE_ID_INTEL_ICH9_4 0x2914 +#define PCI_DEVICE_ID_INTEL_ICH9_5 0x2919 +#define PCI_DEVICE_ID_INTEL_ICH9_6 0x2930 +#define PCI_DEVICE_ID_INTEL_ICH9_7 0x2916 +#define PCI_DEVICE_ID_INTEL_ICH9_8 0x2918 +#define PCI_DEVICE_ID_INTEL_82855PM_HB 0x3340 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG4 0x3429 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG5 0x342a +#define PCI_DEVICE_ID_INTEL_IOAT_TBG6 0x342b +#define PCI_DEVICE_ID_INTEL_IOAT_TBG7 0x342c +#define PCI_DEVICE_ID_INTEL_IOAT_TBG0 0x3430 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG1 0x3431 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG2 0x3432 +#define PCI_DEVICE_ID_INTEL_IOAT_TBG3 0x3433 +#define PCI_DEVICE_ID_INTEL_82830_HB 0x3575 +#define PCI_DEVICE_ID_INTEL_82830_CGC 0x3577 +#define PCI_DEVICE_ID_INTEL_82855GM_HB 0x3580 +#define PCI_DEVICE_ID_INTEL_82855GM_IG 0x3582 +#define PCI_DEVICE_ID_INTEL_E7520_MCH 0x3590 +#define PCI_DEVICE_ID_INTEL_E7320_MCH 0x3592 +#define PCI_DEVICE_ID_INTEL_MCH_PA 0x3595 +#define PCI_DEVICE_ID_INTEL_MCH_PA1 0x3596 +#define PCI_DEVICE_ID_INTEL_MCH_PB 0x3597 +#define PCI_DEVICE_ID_INTEL_MCH_PB1 0x3598 +#define PCI_DEVICE_ID_INTEL_MCH_PC 0x3599 +#define PCI_DEVICE_ID_INTEL_MCH_PC1 0x359a +#define PCI_DEVICE_ID_INTEL_E7525_MCH 0x359e +#define PCI_DEVICE_ID_INTEL_IOAT_CNB 0x360b +#define PCI_DEVICE_ID_INTEL_FBD_CNB 0x360c +#define PCI_DEVICE_ID_INTEL_ICH10_0 0x3a14 +#define PCI_DEVICE_ID_INTEL_ICH10_1 0x3a16 +#define PCI_DEVICE_ID_INTEL_ICH10_2 0x3a18 +#define PCI_DEVICE_ID_INTEL_ICH10_3 0x3a1a +#define PCI_DEVICE_ID_INTEL_ICH10_4 0x3a30 +#define PCI_DEVICE_ID_INTEL_ICH10_5 0x3a60 +#define PCI_DEVICE_ID_INTEL_PCH_LPC_MIN 0x3b00 +#define PCI_DEVICE_ID_INTEL_PCH_LPC_MAX 0x3b1f +#define PCI_DEVICE_ID_INTEL_PCH_SMBUS 0x3b30 +#define PCI_DEVICE_ID_INTEL_IOAT_SNB 0x402f +#define PCI_DEVICE_ID_INTEL_5100_16 0x65f0 +#define PCI_DEVICE_ID_INTEL_5100_21 0x65f5 +#define PCI_DEVICE_ID_INTEL_5100_22 0x65f6 +#define PCI_DEVICE_ID_INTEL_5400_ERR 0x4030 +#define PCI_DEVICE_ID_INTEL_5400_FBD0 0x4035 +#define PCI_DEVICE_ID_INTEL_5400_FBD1 0x4036 +#define PCI_DEVICE_ID_INTEL_IOAT_SCNB 0x65ff +#define PCI_DEVICE_ID_INTEL_TOLAPAI_0 0x5031 +#define PCI_DEVICE_ID_INTEL_TOLAPAI_1 0x5032 +#define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000 +#define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010 +#define PCI_DEVICE_ID_INTEL_82371SB_2 0x7020 +#define PCI_DEVICE_ID_INTEL_82437VX 0x7030 +#define PCI_DEVICE_ID_INTEL_82439TX 0x7100 +#define PCI_DEVICE_ID_INTEL_82371AB_0 0x7110 +#define PCI_DEVICE_ID_INTEL_82371AB 0x7111 +#define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112 +#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 +#define PCI_DEVICE_ID_INTEL_82810_MC1 0x7120 +#define PCI_DEVICE_ID_INTEL_82810_IG1 0x7121 +#define PCI_DEVICE_ID_INTEL_82810_MC3 0x7122 +#define PCI_DEVICE_ID_INTEL_82810_IG3 0x7123 +#define PCI_DEVICE_ID_INTEL_82810E_MC 0x7124 +#define PCI_DEVICE_ID_INTEL_82810E_IG 0x7125 +#define PCI_DEVICE_ID_INTEL_82443LX_0 0x7180 +#define PCI_DEVICE_ID_INTEL_82443LX_1 0x7181 +#define PCI_DEVICE_ID_INTEL_82443BX_0 0x7190 +#define PCI_DEVICE_ID_INTEL_82443BX_1 0x7191 +#define PCI_DEVICE_ID_INTEL_82443BX_2 0x7192 +#define PCI_DEVICE_ID_INTEL_440MX 0x7195 +#define PCI_DEVICE_ID_INTEL_440MX_6 0x7196 +#define PCI_DEVICE_ID_INTEL_82443MX_0 0x7198 +#define PCI_DEVICE_ID_INTEL_82443MX_1 0x7199 +#define PCI_DEVICE_ID_INTEL_82443MX_3 0x719b +#define PCI_DEVICE_ID_INTEL_82443GX_0 0x71a0 +#define PCI_DEVICE_ID_INTEL_82443GX_2 0x71a2 +#define PCI_DEVICE_ID_INTEL_82372FB_1 0x7601 +#define PCI_DEVICE_ID_INTEL_SCH_LPC 0x8119 +#define PCI_DEVICE_ID_INTEL_SCH_IDE 0x811a +#define PCI_DEVICE_ID_INTEL_82454GX 0x84c4 +#define PCI_DEVICE_ID_INTEL_82450GX 0x84c5 +#define PCI_DEVICE_ID_INTEL_82451NX 0x84ca +#define PCI_DEVICE_ID_INTEL_82454NX 0x84cb +#define PCI_DEVICE_ID_INTEL_84460GX 0x84ea +#define PCI_DEVICE_ID_INTEL_IXP4XX 0x8500 +#define PCI_DEVICE_ID_INTEL_IXP2800 0x9004 +#define PCI_DEVICE_ID_INTEL_S21152BB 0xb152 + +#define PCI_VENDOR_ID_SCALEMP 0x8686 +#define PCI_DEVICE_ID_SCALEMP_VSMP_CTL 0x1010 + +#define PCI_VENDOR_ID_COMPUTONE 0x8e0e +#define PCI_DEVICE_ID_COMPUTONE_IP2EX 0x0291 +#define PCI_DEVICE_ID_COMPUTONE_PG 0x0302 +#define PCI_SUBVENDOR_ID_COMPUTONE 0x8e0e +#define PCI_SUBDEVICE_ID_COMPUTONE_PG4 0x0001 +#define PCI_SUBDEVICE_ID_COMPUTONE_PG8 0x0002 +#define PCI_SUBDEVICE_ID_COMPUTONE_PG6 0x0003 + +#define PCI_VENDOR_ID_KTI 0x8e2e + +#define PCI_VENDOR_ID_ADAPTEC 0x9004 +#define PCI_DEVICE_ID_ADAPTEC_7810 0x1078 +#define PCI_DEVICE_ID_ADAPTEC_7821 0x2178 +#define PCI_DEVICE_ID_ADAPTEC_38602 0x3860 +#define PCI_DEVICE_ID_ADAPTEC_7850 0x5078 +#define PCI_DEVICE_ID_ADAPTEC_7855 0x5578 +#define PCI_DEVICE_ID_ADAPTEC_3860 0x6038 +#define PCI_DEVICE_ID_ADAPTEC_1480A 0x6075 +#define PCI_DEVICE_ID_ADAPTEC_7860 0x6078 +#define PCI_DEVICE_ID_ADAPTEC_7861 0x6178 +#define PCI_DEVICE_ID_ADAPTEC_7870 0x7078 +#define PCI_DEVICE_ID_ADAPTEC_7871 0x7178 +#define PCI_DEVICE_ID_ADAPTEC_7872 0x7278 +#define PCI_DEVICE_ID_ADAPTEC_7873 0x7378 +#define PCI_DEVICE_ID_ADAPTEC_7874 0x7478 +#define PCI_DEVICE_ID_ADAPTEC_7895 0x7895 +#define PCI_DEVICE_ID_ADAPTEC_7880 0x8078 +#define PCI_DEVICE_ID_ADAPTEC_7881 0x8178 +#define PCI_DEVICE_ID_ADAPTEC_7882 0x8278 +#define PCI_DEVICE_ID_ADAPTEC_7883 0x8378 +#define PCI_DEVICE_ID_ADAPTEC_7884 0x8478 +#define PCI_DEVICE_ID_ADAPTEC_7885 0x8578 +#define PCI_DEVICE_ID_ADAPTEC_7886 0x8678 +#define PCI_DEVICE_ID_ADAPTEC_7887 0x8778 +#define PCI_DEVICE_ID_ADAPTEC_7888 0x8878 + +#define PCI_VENDOR_ID_ADAPTEC2 0x9005 +#define PCI_DEVICE_ID_ADAPTEC2_2940U2 0x0010 +#define PCI_DEVICE_ID_ADAPTEC2_2930U2 0x0011 +#define PCI_DEVICE_ID_ADAPTEC2_7890B 0x0013 +#define PCI_DEVICE_ID_ADAPTEC2_7890 0x001f +#define PCI_DEVICE_ID_ADAPTEC2_3940U2 0x0050 +#define PCI_DEVICE_ID_ADAPTEC2_3950U2D 0x0051 +#define PCI_DEVICE_ID_ADAPTEC2_7896 0x005f +#define PCI_DEVICE_ID_ADAPTEC2_7892A 0x0080 +#define PCI_DEVICE_ID_ADAPTEC2_7892B 0x0081 +#define PCI_DEVICE_ID_ADAPTEC2_7892D 0x0083 +#define PCI_DEVICE_ID_ADAPTEC2_7892P 0x008f +#define PCI_DEVICE_ID_ADAPTEC2_7899A 0x00c0 +#define PCI_DEVICE_ID_ADAPTEC2_7899B 0x00c1 +#define PCI_DEVICE_ID_ADAPTEC2_7899D 0x00c3 +#define PCI_DEVICE_ID_ADAPTEC2_7899P 0x00cf +#define PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN 0x0500 +#define PCI_DEVICE_ID_ADAPTEC2_SCAMP 0x0503 + +#define PCI_VENDOR_ID_HOLTEK 0x9412 +#define PCI_DEVICE_ID_HOLTEK_6565 0x6565 + +#define PCI_VENDOR_ID_NETMOS 0x9710 +#define PCI_DEVICE_ID_NETMOS_9705 0x9705 +#define PCI_DEVICE_ID_NETMOS_9715 0x9715 +#define PCI_DEVICE_ID_NETMOS_9735 0x9735 +#define PCI_DEVICE_ID_NETMOS_9745 0x9745 +#define PCI_DEVICE_ID_NETMOS_9755 0x9755 +#define PCI_DEVICE_ID_NETMOS_9805 0x9805 +#define PCI_DEVICE_ID_NETMOS_9815 0x9815 +#define PCI_DEVICE_ID_NETMOS_9835 0x9835 +#define PCI_DEVICE_ID_NETMOS_9845 0x9845 +#define PCI_DEVICE_ID_NETMOS_9855 0x9855 + +#define PCI_VENDOR_ID_3COM_2 0xa727 + +#define PCI_VENDOR_ID_DIGIUM 0xd161 +#define PCI_DEVICE_ID_DIGIUM_HFC4S 0xb410 + +#define PCI_SUBVENDOR_ID_EXSYS 0xd84d +#define PCI_SUBDEVICE_ID_EXSYS_4014 0x4014 +#define PCI_SUBDEVICE_ID_EXSYS_4055 0x4055 + +#define PCI_VENDOR_ID_TIGERJET 0xe159 +#define PCI_DEVICE_ID_TIGERJET_300 0x0001 +#define PCI_DEVICE_ID_TIGERJET_100 0x0002 + +#define PCI_VENDOR_ID_XILINX_RME 0xea60 +#define PCI_DEVICE_ID_RME_DIGI32 0x9896 +#define PCI_DEVICE_ID_RME_DIGI32_PRO 0x9897 +#define PCI_DEVICE_ID_RME_DIGI32_8 0x9898 + +#define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4 +#define PCI_DEVICE_ID_VIRTIO_BLK 0x1001 +#define PCI_DEVICE_ID_VIRTIO_SCSI 0x1004 + +#define PCI_VENDOR_ID_VMWARE 0x15ad +#define PCI_DEVICE_ID_VMWARE_PVSCSI 0x07C0 diff --git a/roms/seabios/src/hw/pci_regs.h b/roms/seabios/src/hw/pci_regs.h new file mode 100644 index 000000000..e5effd47e --- /dev/null +++ b/roms/seabios/src/hw/pci_regs.h @@ -0,0 +1,556 @@ +/* + * pci_regs.h + * + * PCI standard defines + * Copyright 1994, Drew Eckhardt + * Copyright 1997--1999 Martin Mares <mj@ucw.cz> + * + * For more information, please consult the following manuals (look at + * http://www.pcisig.com/ for how to get them): + * + * PCI BIOS Specification + * PCI Local Bus Specification + * PCI to PCI Bridge Specification + * PCI System Design Guide + * + * For hypertransport information, please consult the following manuals + * from http://www.hypertransport.org + * + * The Hypertransport I/O Link Specification + */ + +#ifndef LINUX_PCI_REGS_H +#define LINUX_PCI_REGS_H + +/* + * Under PCI, each device has 256 bytes of configuration address space, + * of which the first 64 bytes are standardized as follows: + */ +#define PCI_VENDOR_ID 0x00 /* 16 bits */ +#define PCI_DEVICE_ID 0x02 /* 16 bits */ +#define PCI_COMMAND 0x04 /* 16 bits */ +#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */ +#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */ +#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */ +#define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */ +#define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */ +#define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */ +#define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */ +#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */ +#define PCI_COMMAND_SERR 0x100 /* Enable SERR */ +#define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */ +#define PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */ + +#define PCI_STATUS 0x06 /* 16 bits */ +#define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */ +#define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */ +#define PCI_STATUS_UDF 0x40 /* Support User Definable Features [obsolete] */ +#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */ +#define PCI_STATUS_PARITY 0x100 /* Detected parity error */ +#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */ +#define PCI_STATUS_DEVSEL_FAST 0x000 +#define PCI_STATUS_DEVSEL_MEDIUM 0x200 +#define PCI_STATUS_DEVSEL_SLOW 0x400 +#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */ +#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */ +#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */ +#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */ +#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */ + +#define PCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8 revision */ +#define PCI_REVISION_ID 0x08 /* Revision ID */ +#define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */ +#define PCI_CLASS_DEVICE 0x0a /* Device class */ + +#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */ +#define PCI_LATENCY_TIMER 0x0d /* 8 bits */ +#define PCI_HEADER_TYPE 0x0e /* 8 bits */ +#define PCI_HEADER_TYPE_NORMAL 0 +#define PCI_HEADER_TYPE_BRIDGE 1 +#define PCI_HEADER_TYPE_CARDBUS 2 + +#define PCI_BIST 0x0f /* 8 bits */ +#define PCI_BIST_CODE_MASK 0x0f /* Return result */ +#define PCI_BIST_START 0x40 /* 1 to start BIST, 2 secs or less */ +#define PCI_BIST_CAPABLE 0x80 /* 1 if BIST capable */ + +/* + * Base addresses specify locations in memory or I/O space. + * Decoded size can be determined by writing a value of + * 0xffffffff to the register, and reading it back. Only + * 1 bits are decoded. + */ +#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */ +#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits [htype 0,1 only] */ +#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits [htype 0 only] */ +#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */ +#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */ +#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */ +#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */ +#define PCI_BASE_ADDRESS_SPACE_IO 0x01 +#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00 +#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06 +#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */ +#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */ +#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */ +#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */ +#define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL) +#define PCI_BASE_ADDRESS_IO_MASK (~0x03UL) +/* bit 1 is reserved if address_space = 1 */ + +/* Header type 0 (normal devices) */ +#define PCI_CARDBUS_CIS 0x28 +#define PCI_SUBSYSTEM_VENDOR_ID 0x2c +#define PCI_SUBSYSTEM_ID 0x2e +#define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */ +#define PCI_ROM_ADDRESS_ENABLE 0x01 +#define PCI_ROM_ADDRESS_MASK (~0x7ffUL) + +#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */ + +/* 0x35-0x3b are reserved */ +#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */ +#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */ +#define PCI_MIN_GNT 0x3e /* 8 bits */ +#define PCI_MAX_LAT 0x3f /* 8 bits */ + +/* Header type 1 (PCI-to-PCI bridges) */ +#define PCI_PRIMARY_BUS 0x18 /* Primary bus number */ +#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */ +#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */ +#define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */ +#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */ +#define PCI_IO_LIMIT 0x1d +#define PCI_IO_RANGE_TYPE_MASK 0x0fUL /* I/O bridging type */ +#define PCI_IO_RANGE_TYPE_16 0x00 +#define PCI_IO_RANGE_TYPE_32 0x01 +#define PCI_IO_RANGE_MASK (~0x0fUL) +#define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */ +#define PCI_MEMORY_BASE 0x20 /* Memory range behind */ +#define PCI_MEMORY_LIMIT 0x22 +#define PCI_MEMORY_RANGE_TYPE_MASK 0x0fUL +#define PCI_MEMORY_RANGE_MASK (~0x0fUL) +#define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */ +#define PCI_PREF_MEMORY_LIMIT 0x26 +#define PCI_PREF_RANGE_TYPE_MASK 0x0fUL +#define PCI_PREF_RANGE_TYPE_32 0x00 +#define PCI_PREF_RANGE_TYPE_64 0x01 +#define PCI_PREF_RANGE_MASK (~0x0fUL) +#define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */ +#define PCI_PREF_LIMIT_UPPER32 0x2c +#define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */ +#define PCI_IO_LIMIT_UPPER16 0x32 +/* 0x34 same as for htype 0 */ +/* 0x35-0x3b is reserved */ +#define PCI_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */ +/* 0x3c-0x3d are same as for htype 0 */ +#define PCI_BRIDGE_CONTROL 0x3e +#define PCI_BRIDGE_CTL_PARITY 0x01 /* Enable parity detection on secondary interface */ +#define PCI_BRIDGE_CTL_SERR 0x02 /* The same for SERR forwarding */ +#define PCI_BRIDGE_CTL_ISA 0x04 /* Enable ISA mode */ +#define PCI_BRIDGE_CTL_VGA 0x08 /* Forward VGA addresses */ +#define PCI_BRIDGE_CTL_MASTER_ABORT 0x20 /* Report master aborts */ +#define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */ +#define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */ + +/* Header type 2 (CardBus bridges) */ +#define PCI_CB_CAPABILITY_LIST 0x14 +/* 0x15 reserved */ +#define PCI_CB_SEC_STATUS 0x16 /* Secondary status */ +#define PCI_CB_PRIMARY_BUS 0x18 /* PCI bus number */ +#define PCI_CB_CARD_BUS 0x19 /* CardBus bus number */ +#define PCI_CB_SUBORDINATE_BUS 0x1a /* Subordinate bus number */ +#define PCI_CB_LATENCY_TIMER 0x1b /* CardBus latency timer */ +#define PCI_CB_MEMORY_BASE_0 0x1c +#define PCI_CB_MEMORY_LIMIT_0 0x20 +#define PCI_CB_MEMORY_BASE_1 0x24 +#define PCI_CB_MEMORY_LIMIT_1 0x28 +#define PCI_CB_IO_BASE_0 0x2c +#define PCI_CB_IO_BASE_0_HI 0x2e +#define PCI_CB_IO_LIMIT_0 0x30 +#define PCI_CB_IO_LIMIT_0_HI 0x32 +#define PCI_CB_IO_BASE_1 0x34 +#define PCI_CB_IO_BASE_1_HI 0x36 +#define PCI_CB_IO_LIMIT_1 0x38 +#define PCI_CB_IO_LIMIT_1_HI 0x3a +#define PCI_CB_IO_RANGE_MASK (~0x03UL) +/* 0x3c-0x3d are same as for htype 0 */ +#define PCI_CB_BRIDGE_CONTROL 0x3e +#define PCI_CB_BRIDGE_CTL_PARITY 0x01 /* Similar to standard bridge control register */ +#define PCI_CB_BRIDGE_CTL_SERR 0x02 +#define PCI_CB_BRIDGE_CTL_ISA 0x04 +#define PCI_CB_BRIDGE_CTL_VGA 0x08 +#define PCI_CB_BRIDGE_CTL_MASTER_ABORT 0x20 +#define PCI_CB_BRIDGE_CTL_CB_RESET 0x40 /* CardBus reset */ +#define PCI_CB_BRIDGE_CTL_16BIT_INT 0x80 /* Enable interrupt for 16-bit cards */ +#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 0x100 /* Prefetch enable for both memory regions */ +#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM1 0x200 +#define PCI_CB_BRIDGE_CTL_POST_WRITES 0x400 +#define PCI_CB_SUBSYSTEM_VENDOR_ID 0x40 +#define PCI_CB_SUBSYSTEM_ID 0x42 +#define PCI_CB_LEGACY_MODE_BASE 0x44 /* 16-bit PC Card legacy mode base address (ExCa) */ +/* 0x48-0x7f reserved */ + +/* Capability lists */ + +#define PCI_CAP_LIST_ID 0 /* Capability ID */ +#define PCI_CAP_ID_PM 0x01 /* Power Management */ +#define PCI_CAP_ID_AGP 0x02 /* Accelerated Graphics Port */ +#define PCI_CAP_ID_VPD 0x03 /* Vital Product Data */ +#define PCI_CAP_ID_SLOTID 0x04 /* Slot Identification */ +#define PCI_CAP_ID_MSI 0x05 /* Message Signalled Interrupts */ +#define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */ +#define PCI_CAP_ID_PCIX 0x07 /* PCI-X */ +#define PCI_CAP_ID_HT 0x08 /* HyperTransport */ +#define PCI_CAP_ID_VNDR 0x09 /* Vendor specific */ +#define PCI_CAP_ID_DBG 0x0A /* Debug port */ +#define PCI_CAP_ID_CCRC 0x0B /* CompactPCI Central Resource Control */ +#define PCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */ +#define PCI_CAP_ID_SSVID 0x0D /* Bridge subsystem vendor/device ID */ +#define PCI_CAP_ID_AGP3 0x0E /* AGP Target PCI-PCI bridge */ +#define PCI_CAP_ID_EXP 0x10 /* PCI Express */ +#define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ +#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ +#define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */ +#define PCI_CAP_SIZEOF 4 + +/* Power Management Registers */ + +#define PCI_PM_PMC 2 /* PM Capabilities Register */ +#define PCI_PM_CAP_VER_MASK 0x0007 /* Version */ +#define PCI_PM_CAP_PME_CLOCK 0x0008 /* PME clock required */ +#define PCI_PM_CAP_RESERVED 0x0010 /* Reserved field */ +#define PCI_PM_CAP_DSI 0x0020 /* Device specific initialization */ +#define PCI_PM_CAP_AUX_POWER 0x01C0 /* Auxilliary power support mask */ +#define PCI_PM_CAP_D1 0x0200 /* D1 power state support */ +#define PCI_PM_CAP_D2 0x0400 /* D2 power state support */ +#define PCI_PM_CAP_PME 0x0800 /* PME pin supported */ +#define PCI_PM_CAP_PME_MASK 0xF800 /* PME Mask of all supported states */ +#define PCI_PM_CAP_PME_D0 0x0800 /* PME# from D0 */ +#define PCI_PM_CAP_PME_D1 0x1000 /* PME# from D1 */ +#define PCI_PM_CAP_PME_D2 0x2000 /* PME# from D2 */ +#define PCI_PM_CAP_PME_D3 0x4000 /* PME# from D3 (hot) */ +#define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */ +#define PCI_PM_CAP_PME_SHIFT 11 /* Start of the PME Mask in PMC */ +#define PCI_PM_CTRL 4 /* PM control and status register */ +#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */ +#define PCI_PM_CTRL_NO_SOFT_RESET 0x0004 /* No reset for D3hot->D0 */ +#define PCI_PM_CTRL_PME_ENABLE 0x0100 /* PME pin enable */ +#define PCI_PM_CTRL_DATA_SEL_MASK 0x1e00 /* Data select (??) */ +#define PCI_PM_CTRL_DATA_SCALE_MASK 0x6000 /* Data scale (??) */ +#define PCI_PM_CTRL_PME_STATUS 0x8000 /* PME pin status */ +#define PCI_PM_PPB_EXTENSIONS 6 /* PPB support extensions (??) */ +#define PCI_PM_PPB_B2_B3 0x40 /* Stop clock when in D3hot (??) */ +#define PCI_PM_BPCC_ENABLE 0x80 /* Bus power/clock control enable (??) */ +#define PCI_PM_DATA_REGISTER 7 /* (??) */ +#define PCI_PM_SIZEOF 8 + +/* AGP registers */ + +#define PCI_AGP_VERSION 2 /* BCD version number */ +#define PCI_AGP_RFU 3 /* Rest of capability flags */ +#define PCI_AGP_STATUS 4 /* Status register */ +#define PCI_AGP_STATUS_RQ_MASK 0xff000000 /* Maximum number of requests - 1 */ +#define PCI_AGP_STATUS_SBA 0x0200 /* Sideband addressing supported */ +#define PCI_AGP_STATUS_64BIT 0x0020 /* 64-bit addressing supported */ +#define PCI_AGP_STATUS_FW 0x0010 /* FW transfers supported */ +#define PCI_AGP_STATUS_RATE4 0x0004 /* 4x transfer rate supported */ +#define PCI_AGP_STATUS_RATE2 0x0002 /* 2x transfer rate supported */ +#define PCI_AGP_STATUS_RATE1 0x0001 /* 1x transfer rate supported */ +#define PCI_AGP_COMMAND 8 /* Control register */ +#define PCI_AGP_COMMAND_RQ_MASK 0xff000000 /* Master: Maximum number of requests */ +#define PCI_AGP_COMMAND_SBA 0x0200 /* Sideband addressing enabled */ +#define PCI_AGP_COMMAND_AGP 0x0100 /* Allow processing of AGP transactions */ +#define PCI_AGP_COMMAND_64BIT 0x0020 /* Allow processing of 64-bit addresses */ +#define PCI_AGP_COMMAND_FW 0x0010 /* Force FW transfers */ +#define PCI_AGP_COMMAND_RATE4 0x0004 /* Use 4x rate */ +#define PCI_AGP_COMMAND_RATE2 0x0002 /* Use 2x rate */ +#define PCI_AGP_COMMAND_RATE1 0x0001 /* Use 1x rate */ +#define PCI_AGP_SIZEOF 12 + +/* Vital Product Data */ + +#define PCI_VPD_ADDR 2 /* Address to access (15 bits!) */ +#define PCI_VPD_ADDR_MASK 0x7fff /* Address mask */ +#define PCI_VPD_ADDR_F 0x8000 /* Write 0, 1 indicates completion */ +#define PCI_VPD_DATA 4 /* 32-bits of data returned here */ + +/* Slot Identification */ + +#define PCI_SID_ESR 2 /* Expansion Slot Register */ +#define PCI_SID_ESR_NSLOTS 0x1f /* Number of expansion slots available */ +#define PCI_SID_ESR_FIC 0x20 /* First In Chassis Flag */ +#define PCI_SID_CHASSIS_NR 3 /* Chassis Number */ + +/* Message Signalled Interrupts registers */ + +#define PCI_MSI_FLAGS 2 /* Various flags */ +#define PCI_MSI_FLAGS_64BIT 0x80 /* 64-bit addresses allowed */ +#define PCI_MSI_FLAGS_QSIZE 0x70 /* Message queue size configured */ +#define PCI_MSI_FLAGS_QMASK 0x0e /* Maximum queue size available */ +#define PCI_MSI_FLAGS_ENABLE 0x01 /* MSI feature enabled */ +#define PCI_MSI_FLAGS_MASKBIT 0x100 /* 64-bit mask bits allowed */ +#define PCI_MSI_RFU 3 /* Rest of capability flags */ +#define PCI_MSI_ADDRESS_LO 4 /* Lower 32 bits */ +#define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */ +#define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */ +#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */ +#define PCI_MSI_MASK_BIT 16 /* Mask bits register */ + +/* MSI-X registers (these are at offset PCI_MSIX_FLAGS) */ +#define PCI_MSIX_FLAGS 2 +#define PCI_MSIX_FLAGS_QSIZE 0x7FF +#define PCI_MSIX_FLAGS_ENABLE (1 << 15) +#define PCI_MSIX_FLAGS_MASKALL (1 << 14) +#define PCI_MSIX_FLAGS_BIRMASK (7 << 0) +#define PCI_MSIX_FLAGS_BITMASK (1 << 0) + +/* CompactPCI Hotswap Register */ + +#define PCI_CHSWP_CSR 2 /* Control and Status Register */ +#define PCI_CHSWP_DHA 0x01 /* Device Hiding Arm */ +#define PCI_CHSWP_EIM 0x02 /* ENUM# Signal Mask */ +#define PCI_CHSWP_PIE 0x04 /* Pending Insert or Extract */ +#define PCI_CHSWP_LOO 0x08 /* LED On / Off */ +#define PCI_CHSWP_PI 0x30 /* Programming Interface */ +#define PCI_CHSWP_EXT 0x40 /* ENUM# status - extraction */ +#define PCI_CHSWP_INS 0x80 /* ENUM# status - insertion */ + +/* PCI-X registers */ + +#define PCI_X_CMD 2 /* Modes & Features */ +#define PCI_X_CMD_DPERR_E 0x0001 /* Data Parity Error Recovery Enable */ +#define PCI_X_CMD_ERO 0x0002 /* Enable Relaxed Ordering */ +#define PCI_X_CMD_READ_512 0x0000 /* 512 byte maximum read byte count */ +#define PCI_X_CMD_READ_1K 0x0004 /* 1Kbyte maximum read byte count */ +#define PCI_X_CMD_READ_2K 0x0008 /* 2Kbyte maximum read byte count */ +#define PCI_X_CMD_READ_4K 0x000c /* 4Kbyte maximum read byte count */ +#define PCI_X_CMD_MAX_READ 0x000c /* Max Memory Read Byte Count */ + /* Max # of outstanding split transactions */ +#define PCI_X_CMD_SPLIT_1 0x0000 /* Max 1 */ +#define PCI_X_CMD_SPLIT_2 0x0010 /* Max 2 */ +#define PCI_X_CMD_SPLIT_3 0x0020 /* Max 3 */ +#define PCI_X_CMD_SPLIT_4 0x0030 /* Max 4 */ +#define PCI_X_CMD_SPLIT_8 0x0040 /* Max 8 */ +#define PCI_X_CMD_SPLIT_12 0x0050 /* Max 12 */ +#define PCI_X_CMD_SPLIT_16 0x0060 /* Max 16 */ +#define PCI_X_CMD_SPLIT_32 0x0070 /* Max 32 */ +#define PCI_X_CMD_MAX_SPLIT 0x0070 /* Max Outstanding Split Transactions */ +#define PCI_X_CMD_VERSION(x) (((x) >> 12) & 3) /* Version */ +#define PCI_X_STATUS 4 /* PCI-X capabilities */ +#define PCI_X_STATUS_DEVFN 0x000000ff /* A copy of devfn */ +#define PCI_X_STATUS_BUS 0x0000ff00 /* A copy of bus nr */ +#define PCI_X_STATUS_64BIT 0x00010000 /* 64-bit device */ +#define PCI_X_STATUS_133MHZ 0x00020000 /* 133 MHz capable */ +#define PCI_X_STATUS_SPL_DISC 0x00040000 /* Split Completion Discarded */ +#define PCI_X_STATUS_UNX_SPL 0x00080000 /* Unexpected Split Completion */ +#define PCI_X_STATUS_COMPLEX 0x00100000 /* Device Complexity */ +#define PCI_X_STATUS_MAX_READ 0x00600000 /* Designed Max Memory Read Count */ +#define PCI_X_STATUS_MAX_SPLIT 0x03800000 /* Designed Max Outstanding Split Transactions */ +#define PCI_X_STATUS_MAX_CUM 0x1c000000 /* Designed Max Cumulative Read Size */ +#define PCI_X_STATUS_SPL_ERR 0x20000000 /* Rcvd Split Completion Error Msg */ +#define PCI_X_STATUS_266MHZ 0x40000000 /* 266 MHz capable */ +#define PCI_X_STATUS_533MHZ 0x80000000 /* 533 MHz capable */ + +/* PCI Express capability registers */ + +#define PCI_EXP_FLAGS 2 /* Capabilities register */ +#define PCI_EXP_FLAGS_VERS 0x000f /* Capability version */ +#define PCI_EXP_FLAGS_TYPE 0x00f0 /* Device/Port type */ +#define PCI_EXP_TYPE_ENDPOINT 0x0 /* Express Endpoint */ +#define PCI_EXP_TYPE_LEG_END 0x1 /* Legacy Endpoint */ +#define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */ +#define PCI_EXP_TYPE_UPSTREAM 0x5 /* Upstream Port */ +#define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */ +#define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCI/PCI-X Bridge */ +#define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */ +#define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */ +#define PCI_EXP_DEVCAP 4 /* Device capabilities */ +#define PCI_EXP_DEVCAP_PAYLOAD 0x07 /* Max_Payload_Size */ +#define PCI_EXP_DEVCAP_PHANTOM 0x18 /* Phantom functions */ +#define PCI_EXP_DEVCAP_EXT_TAG 0x20 /* Extended tags */ +#define PCI_EXP_DEVCAP_L0S 0x1c0 /* L0s Acceptable Latency */ +#define PCI_EXP_DEVCAP_L1 0xe00 /* L1 Acceptable Latency */ +#define PCI_EXP_DEVCAP_ATN_BUT 0x1000 /* Attention Button Present */ +#define PCI_EXP_DEVCAP_ATN_IND 0x2000 /* Attention Indicator Present */ +#define PCI_EXP_DEVCAP_PWR_IND 0x4000 /* Power Indicator Present */ +#define PCI_EXP_DEVCAP_RBER 0x8000 /* Role-Based Error Reporting */ +#define PCI_EXP_DEVCAP_PWR_VAL 0x3fc0000 /* Slot Power Limit Value */ +#define PCI_EXP_DEVCAP_PWR_SCL 0xc000000 /* Slot Power Limit Scale */ +#define PCI_EXP_DEVCAP_FLR 0x10000000 /* Function Level Reset */ +#define PCI_EXP_DEVCTL 8 /* Device Control */ +#define PCI_EXP_DEVCTL_CERE 0x0001 /* Correctable Error Reporting En. */ +#define PCI_EXP_DEVCTL_NFERE 0x0002 /* Non-Fatal Error Reporting Enable */ +#define PCI_EXP_DEVCTL_FERE 0x0004 /* Fatal Error Reporting Enable */ +#define PCI_EXP_DEVCTL_URRE 0x0008 /* Unsupported Request Reporting En. */ +#define PCI_EXP_DEVCTL_RELAX_EN 0x0010 /* Enable relaxed ordering */ +#define PCI_EXP_DEVCTL_PAYLOAD 0x00e0 /* Max_Payload_Size */ +#define PCI_EXP_DEVCTL_EXT_TAG 0x0100 /* Extended Tag Field Enable */ +#define PCI_EXP_DEVCTL_PHANTOM 0x0200 /* Phantom Functions Enable */ +#define PCI_EXP_DEVCTL_AUX_PME 0x0400 /* Auxiliary Power PM Enable */ +#define PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800 /* Enable No Snoop */ +#define PCI_EXP_DEVCTL_READRQ 0x7000 /* Max_Read_Request_Size */ +#define PCI_EXP_DEVCTL_BCR_FLR 0x8000 /* Bridge Configuration Retry / FLR */ +#define PCI_EXP_DEVSTA 10 /* Device Status */ +#define PCI_EXP_DEVSTA_CED 0x01 /* Correctable Error Detected */ +#define PCI_EXP_DEVSTA_NFED 0x02 /* Non-Fatal Error Detected */ +#define PCI_EXP_DEVSTA_FED 0x04 /* Fatal Error Detected */ +#define PCI_EXP_DEVSTA_URD 0x08 /* Unsupported Request Detected */ +#define PCI_EXP_DEVSTA_AUXPD 0x10 /* AUX Power Detected */ +#define PCI_EXP_DEVSTA_TRPND 0x20 /* Transactions Pending */ +#define PCI_EXP_LNKCAP 12 /* Link Capabilities */ +#define PCI_EXP_LNKCAP_ASPMS 0xc00 /* ASPM Support */ +#define PCI_EXP_LNKCAP_L0SEL 0x7000 /* L0s Exit Latency */ +#define PCI_EXP_LNKCAP_L1EL 0x38000 /* L1 Exit Latency */ +#define PCI_EXP_LNKCAP_CLKPM 0x40000 /* L1 Clock Power Management */ +#define PCI_EXP_LNKCTL 16 /* Link Control */ +#define PCI_EXP_LNKCTL_RL 0x20 /* Retrain Link */ +#define PCI_EXP_LNKCTL_CCC 0x40 /* Common Clock COnfiguration */ +#define PCI_EXP_LNKCTL_CLKREQ_EN 0x100 /* Enable clkreq */ +#define PCI_EXP_LNKSTA 18 /* Link Status */ +#define PCI_EXP_LNKSTA_LT 0x800 /* Link Training */ +#define PCI_EXP_LNKSTA_SLC 0x1000 /* Slot Clock Configuration */ +#define PCI_EXP_SLTCAP 20 /* Slot Capabilities */ +#define PCI_EXP_SLTCTL 24 /* Slot Control */ +#define PCI_EXP_SLTSTA 26 /* Slot Status */ +#define PCI_EXP_RTCTL 28 /* Root Control */ +#define PCI_EXP_RTCTL_SECEE 0x01 /* System Error on Correctable Error */ +#define PCI_EXP_RTCTL_SENFEE 0x02 /* System Error on Non-Fatal Error */ +#define PCI_EXP_RTCTL_SEFEE 0x04 /* System Error on Fatal Error */ +#define PCI_EXP_RTCTL_PMEIE 0x08 /* PME Interrupt Enable */ +#define PCI_EXP_RTCTL_CRSSVE 0x10 /* CRS Software Visibility Enable */ +#define PCI_EXP_RTCAP 30 /* Root Capabilities */ +#define PCI_EXP_RTSTA 32 /* Root Status */ +#define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */ +#define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */ +#define PCI_EXP_DEVCTL2 40 /* Device Control 2 */ +#define PCI_EXP_DEVCTL2_ARI 0x20 /* Alternative Routing-ID */ + +/* Extended Capabilities (PCI-X 2.0 and Express) */ +#define PCI_EXT_CAP_ID(header) (header & 0x0000ffff) +#define PCI_EXT_CAP_VER(header) ((header >> 16) & 0xf) +#define PCI_EXT_CAP_NEXT(header) ((header >> 20) & 0xffc) + +#define PCI_EXT_CAP_ID_ERR 1 +#define PCI_EXT_CAP_ID_VC 2 +#define PCI_EXT_CAP_ID_DSN 3 +#define PCI_EXT_CAP_ID_PWR 4 +#define PCI_EXT_CAP_ID_ARI 14 + +/* Advanced Error Reporting */ +#define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */ +#define PCI_ERR_UNC_TRAIN 0x00000001 /* Training */ +#define PCI_ERR_UNC_DLP 0x00000010 /* Data Link Protocol */ +#define PCI_ERR_UNC_POISON_TLP 0x00001000 /* Poisoned TLP */ +#define PCI_ERR_UNC_FCP 0x00002000 /* Flow Control Protocol */ +#define PCI_ERR_UNC_COMP_TIME 0x00004000 /* Completion Timeout */ +#define PCI_ERR_UNC_COMP_ABORT 0x00008000 /* Completer Abort */ +#define PCI_ERR_UNC_UNX_COMP 0x00010000 /* Unexpected Completion */ +#define PCI_ERR_UNC_RX_OVER 0x00020000 /* Receiver Overflow */ +#define PCI_ERR_UNC_MALF_TLP 0x00040000 /* Malformed TLP */ +#define PCI_ERR_UNC_ECRC 0x00080000 /* ECRC Error Status */ +#define PCI_ERR_UNC_UNSUP 0x00100000 /* Unsupported Request */ +#define PCI_ERR_UNCOR_MASK 8 /* Uncorrectable Error Mask */ + /* Same bits as above */ +#define PCI_ERR_UNCOR_SEVER 12 /* Uncorrectable Error Severity */ + /* Same bits as above */ +#define PCI_ERR_COR_STATUS 16 /* Correctable Error Status */ +#define PCI_ERR_COR_RCVR 0x00000001 /* Receiver Error Status */ +#define PCI_ERR_COR_BAD_TLP 0x00000040 /* Bad TLP Status */ +#define PCI_ERR_COR_BAD_DLLP 0x00000080 /* Bad DLLP Status */ +#define PCI_ERR_COR_REP_ROLL 0x00000100 /* REPLAY_NUM Rollover */ +#define PCI_ERR_COR_REP_TIMER 0x00001000 /* Replay Timer Timeout */ +#define PCI_ERR_COR_MASK 20 /* Correctable Error Mask */ + /* Same bits as above */ +#define PCI_ERR_CAP 24 /* Advanced Error Capabilities */ +#define PCI_ERR_CAP_FEP(x) ((x) & 31) /* First Error Pointer */ +#define PCI_ERR_CAP_ECRC_GENC 0x00000020 /* ECRC Generation Capable */ +#define PCI_ERR_CAP_ECRC_GENE 0x00000040 /* ECRC Generation Enable */ +#define PCI_ERR_CAP_ECRC_CHKC 0x00000080 /* ECRC Check Capable */ +#define PCI_ERR_CAP_ECRC_CHKE 0x00000100 /* ECRC Check Enable */ +#define PCI_ERR_HEADER_LOG 28 /* Header Log Register (16 bytes) */ +#define PCI_ERR_ROOT_COMMAND 44 /* Root Error Command */ +/* Correctable Err Reporting Enable */ +#define PCI_ERR_ROOT_CMD_COR_EN 0x00000001 +/* Non-fatal Err Reporting Enable */ +#define PCI_ERR_ROOT_CMD_NONFATAL_EN 0x00000002 +/* Fatal Err Reporting Enable */ +#define PCI_ERR_ROOT_CMD_FATAL_EN 0x00000004 +#define PCI_ERR_ROOT_STATUS 48 +#define PCI_ERR_ROOT_COR_RCV 0x00000001 /* ERR_COR Received */ +/* Multi ERR_COR Received */ +#define PCI_ERR_ROOT_MULTI_COR_RCV 0x00000002 +/* ERR_FATAL/NONFATAL Recevied */ +#define PCI_ERR_ROOT_UNCOR_RCV 0x00000004 +/* Multi ERR_FATAL/NONFATAL Recevied */ +#define PCI_ERR_ROOT_MULTI_UNCOR_RCV 0x00000008 +#define PCI_ERR_ROOT_FIRST_FATAL 0x00000010 /* First Fatal */ +#define PCI_ERR_ROOT_NONFATAL_RCV 0x00000020 /* Non-Fatal Received */ +#define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Received */ +#define PCI_ERR_ROOT_COR_SRC 52 +#define PCI_ERR_ROOT_SRC 54 + +/* Virtual Channel */ +#define PCI_VC_PORT_REG1 4 +#define PCI_VC_PORT_REG2 8 +#define PCI_VC_PORT_CTRL 12 +#define PCI_VC_PORT_STATUS 14 +#define PCI_VC_RES_CAP 16 +#define PCI_VC_RES_CTRL 20 +#define PCI_VC_RES_STATUS 26 + +/* Power Budgeting */ +#define PCI_PWR_DSR 4 /* Data Select Register */ +#define PCI_PWR_DATA 8 /* Data Register */ +#define PCI_PWR_DATA_BASE(x) ((x) & 0xff) /* Base Power */ +#define PCI_PWR_DATA_SCALE(x) (((x) >> 8) & 3) /* Data Scale */ +#define PCI_PWR_DATA_PM_SUB(x) (((x) >> 10) & 7) /* PM Sub State */ +#define PCI_PWR_DATA_PM_STATE(x) (((x) >> 13) & 3) /* PM State */ +#define PCI_PWR_DATA_TYPE(x) (((x) >> 15) & 7) /* Type */ +#define PCI_PWR_DATA_RAIL(x) (((x) >> 18) & 7) /* Power Rail */ +#define PCI_PWR_CAP 12 /* Capability */ +#define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */ + +/* + * Hypertransport sub capability types + * + * Unfortunately there are both 3 bit and 5 bit capability types defined + * in the HT spec, catering for that is a little messy. You probably don't + * want to use these directly, just use pci_find_ht_capability() and it + * will do the right thing for you. + */ +#define HT_3BIT_CAP_MASK 0xE0 +#define HT_CAPTYPE_SLAVE 0x00 /* Slave/Primary link configuration */ +#define HT_CAPTYPE_HOST 0x20 /* Host/Secondary link configuration */ + +#define HT_5BIT_CAP_MASK 0xF8 +#define HT_CAPTYPE_IRQ 0x80 /* IRQ Configuration */ +#define HT_CAPTYPE_REMAPPING_40 0xA0 /* 40 bit address remapping */ +#define HT_CAPTYPE_REMAPPING_64 0xA2 /* 64 bit address remapping */ +#define HT_CAPTYPE_UNITID_CLUMP 0x90 /* Unit ID clumping */ +#define HT_CAPTYPE_EXTCONF 0x98 /* Extended Configuration Space Access */ +#define HT_CAPTYPE_MSI_MAPPING 0xA8 /* MSI Mapping Capability */ +#define HT_MSI_FLAGS 0x02 /* Offset to flags */ +#define HT_MSI_FLAGS_ENABLE 0x1 /* Mapping enable */ +#define HT_MSI_FLAGS_FIXED 0x2 /* Fixed mapping only */ +#define HT_MSI_FIXED_ADDR 0x00000000FEE00000ULL /* Fixed addr */ +#define HT_MSI_ADDR_LO 0x04 /* Offset to low addr bits */ +#define HT_MSI_ADDR_LO_MASK 0xFFF00000 /* Low address bit mask */ +#define HT_MSI_ADDR_HI 0x08 /* Offset to high addr bits */ +#define HT_CAPTYPE_DIRECT_ROUTE 0xB0 /* Direct routing configuration */ +#define HT_CAPTYPE_VCSET 0xB8 /* Virtual Channel configuration */ +#define HT_CAPTYPE_ERROR_RETRY 0xC0 /* Retry on error configuration */ +#define HT_CAPTYPE_GEN3 0xD0 /* Generation 3 hypertransport configuration */ +#define HT_CAPTYPE_PM 0xE0 /* Hypertransport powermanagement configuration */ + +/* Alternative Routing-ID Interpretation */ +#define PCI_ARI_CAP 0x04 /* ARI Capability Register */ +#define PCI_ARI_CAP_MFVC 0x0001 /* MFVC Function Groups Capability */ +#define PCI_ARI_CAP_ACS 0x0002 /* ACS Function Groups Capability */ +#define PCI_ARI_CAP_NFN(x) (((x) >> 8) & 0xff) /* Next Function Number */ +#define PCI_ARI_CTRL 0x06 /* ARI Control Register */ +#define PCI_ARI_CTRL_MFVC 0x0001 /* MFVC Function Groups Enable */ +#define PCI_ARI_CTRL_ACS 0x0002 /* ACS Function Groups Enable */ +#define PCI_ARI_CTRL_FG(x) (((x) >> 4) & 7) /* Function Group */ + +#endif /* LINUX_PCI_REGS_H */ diff --git a/roms/seabios/src/hw/pic.c b/roms/seabios/src/hw/pic.c new file mode 100644 index 000000000..6ff696765 --- /dev/null +++ b/roms/seabios/src/hw/pic.c @@ -0,0 +1,101 @@ +// Helpers for working with i8259 interrupt controller. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // SET_IVT +#include "config.h" // CONFIG_* +#include "output.h" // dprintf +#include "pic.h" // pic_* + +u16 +pic_irqmask_read(void) +{ + return inb(PORT_PIC1_DATA) | (inb(PORT_PIC2_DATA) << 8); +} + +void +pic_irqmask_write(u16 mask) +{ + outb(mask, PORT_PIC1_DATA); + outb(mask >> 8, PORT_PIC2_DATA); +} + +void +pic_irqmask_mask(u16 off, u16 on) +{ + u8 pic1off = off, pic1on = on, pic2off = off>>8, pic2on = on>>8; + outb((inb(PORT_PIC1_DATA) & ~pic1off) | pic1on, PORT_PIC1_DATA); + outb((inb(PORT_PIC2_DATA) & ~pic2off) | pic2on, PORT_PIC2_DATA); +} + +void +pic_reset(u8 irq0, u8 irq8) +{ + // Send ICW1 (select OCW1 + will send ICW4) + outb(0x11, PORT_PIC1_CMD); + outb(0x11, PORT_PIC2_CMD); + // Send ICW2 (base irqs: 0x08-0x0f for irq0-7, 0x70-0x77 for irq8-15) + outb(irq0, PORT_PIC1_DATA); + outb(irq8, PORT_PIC2_DATA); + // Send ICW3 (cascaded pic ids) + outb(0x04, PORT_PIC1_DATA); + outb(0x02, PORT_PIC2_DATA); + // Send ICW4 (enable 8086 mode) + outb(0x01, PORT_PIC1_DATA); + outb(0x01, PORT_PIC2_DATA); + // Mask all irqs (except cascaded PIC2 irq) + pic_irqmask_write(PIC_IRQMASK_DEFAULT); +} + +void +pic_setup(void) +{ + dprintf(3, "init pic\n"); + pic_reset(BIOS_HWIRQ0_VECTOR, BIOS_HWIRQ8_VECTOR); +} + +void +enable_hwirq(int hwirq, struct segoff_s func) +{ + pic_irqmask_mask(1 << hwirq, 0); + int vector; + if (hwirq < 8) + vector = BIOS_HWIRQ0_VECTOR + hwirq; + else + vector = BIOS_HWIRQ8_VECTOR + hwirq - 8; + SET_IVT(vector, func); +} + +static u8 +pic_isr1_read(void) +{ + // 0x0b == select OCW1 + read ISR + outb(0x0b, PORT_PIC1_CMD); + return inb(PORT_PIC1_CMD); +} + +static u8 +pic_isr2_read(void) +{ + // 0x0b == select OCW1 + read ISR + outb(0x0b, PORT_PIC2_CMD); + return inb(PORT_PIC2_CMD); +} + +// Handler for otherwise unused hardware irqs. +void VISIBLE16 +handle_hwpic1(struct bregs *regs) +{ + dprintf(DEBUG_ISR_hwpic1, "handle_hwpic1 irq=%x\n", pic_isr1_read()); + pic_eoi1(); +} + +void VISIBLE16 +handle_hwpic2(struct bregs *regs) +{ + dprintf(DEBUG_ISR_hwpic2, "handle_hwpic2 irq=%x\n", pic_isr2_read()); + pic_eoi2(); +} diff --git a/roms/seabios/src/hw/pic.h b/roms/seabios/src/hw/pic.h new file mode 100644 index 000000000..6947b6e81 --- /dev/null +++ b/roms/seabios/src/hw/pic.h @@ -0,0 +1,56 @@ +// Helpers for working with i8259 interrupt controller. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. +#ifndef __PIC_H +#define __PIC_H + +#include "x86.h" // outb + +#define PORT_PIC1_CMD 0x0020 +#define PORT_PIC1_DATA 0x0021 +#define PORT_PIC2_CMD 0x00a0 +#define PORT_PIC2_DATA 0x00a1 + +// PORT_PIC1 bitdefs +#define PIC1_IRQ0 (1<<0) +#define PIC1_IRQ1 (1<<1) +#define PIC1_IRQ2 (1<<2) +#define PIC1_IRQ5 (1<<5) +#define PIC1_IRQ6 (1<<6) +// PORT_PIC2 bitdefs +#define PIC2_IRQ8 (1<<8) +#define PIC2_IRQ12 (1<<12) +#define PIC2_IRQ13 (1<<13) +#define PIC2_IRQ14 (1<<14) + +#define PIC_IRQMASK_DEFAULT ((u16)~PIC1_IRQ2) + +#define BIOS_HWIRQ0_VECTOR 0x08 +#define BIOS_HWIRQ8_VECTOR 0x70 + +static inline void +pic_eoi1(void) +{ + // Send eoi (select OCW2 + eoi) + outb(0x20, PORT_PIC1_CMD); +} + +static inline void +pic_eoi2(void) +{ + // Send eoi (select OCW2 + eoi) + outb(0x20, PORT_PIC2_CMD); + pic_eoi1(); +} + +u16 pic_irqmask_read(void); +void pic_irqmask_write(u16 mask); +void pic_irqmask_mask(u16 off, u16 on); +void pic_reset(u8 irq0, u8 irq8); +void pic_setup(void); +void enable_hwirq(int hwirq, struct segoff_s func); + +#endif // pic.h diff --git a/roms/seabios/src/hw/ps2port.c b/roms/seabios/src/hw/ps2port.c new file mode 100644 index 000000000..04995c881 --- /dev/null +++ b/roms/seabios/src/hw/ps2port.c @@ -0,0 +1,507 @@ +// Support for handling the PS/2 mouse/keyboard ports. +// +// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> +// Several ideas taken from code Copyright (c) 1999-2004 Vojtech Pavlik +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_LOW +#include "output.h" // dprintf +#include "pic.h" // pic_eoi1 +#include "ps2port.h" // ps2_kbd_command +#include "romfile.h" // romfile_loadint +#include "stacks.h" // yield +#include "util.h" // udelay +#include "x86.h" // inb + + +/**************************************************************** + * Low level i8042 commands. + ****************************************************************/ + +// Timeout value. +#define I8042_CTL_TIMEOUT 10000 + +#define I8042_BUFFER_SIZE 16 + +static int +i8042_wait_read(void) +{ + dprintf(7, "i8042_wait_read\n"); + int i; + for (i=0; i<I8042_CTL_TIMEOUT; i++) { + u8 status = inb(PORT_PS2_STATUS); + if (status & I8042_STR_OBF) + return 0; + udelay(50); + } + warn_timeout(); + return -1; +} + +static int +i8042_wait_write(void) +{ + dprintf(7, "i8042_wait_write\n"); + int i; + for (i=0; i<I8042_CTL_TIMEOUT; i++) { + u8 status = inb(PORT_PS2_STATUS); + if (! (status & I8042_STR_IBF)) + return 0; + udelay(50); + } + warn_timeout(); + return -1; +} + +static int +i8042_flush(void) +{ + dprintf(7, "i8042_flush\n"); + int i; + for (i=0; i<I8042_BUFFER_SIZE; i++) { + u8 status = inb(PORT_PS2_STATUS); + if (! (status & I8042_STR_OBF)) + return 0; + udelay(50); + u8 data = inb(PORT_PS2_DATA); + dprintf(7, "i8042 flushed %x (status=%x)\n", data, status); + } + + warn_timeout(); + return -1; +} + +static int +__i8042_command(int command, u8 *param) +{ + int receive = (command >> 8) & 0xf; + int send = (command >> 12) & 0xf; + + // Send the command. + int ret = i8042_wait_write(); + if (ret) + return ret; + outb(command, PORT_PS2_STATUS); + + // Send parameters (if any). + int i; + for (i = 0; i < send; i++) { + ret = i8042_wait_write(); + if (ret) + return ret; + outb(param[i], PORT_PS2_DATA); + } + + // Receive parameters (if any). + for (i = 0; i < receive; i++) { + ret = i8042_wait_read(); + if (ret) + return ret; + param[i] = inb(PORT_PS2_DATA); + dprintf(7, "i8042 param=%x\n", param[i]); + } + + return 0; +} + +static int +i8042_command(int command, u8 *param) +{ + dprintf(7, "i8042_command cmd=%x\n", command); + int ret = __i8042_command(command, param); + if (ret) + dprintf(2, "i8042 command %x failed\n", command); + return ret; +} + +static int +i8042_kbd_write(u8 c) +{ + dprintf(7, "i8042_kbd_write c=%d\n", c); + int ret = i8042_wait_write(); + if (! ret) + outb(c, PORT_PS2_DATA); + return ret; +} + +static int +i8042_aux_write(u8 c) +{ + return i8042_command(I8042_CMD_AUX_SEND, &c); +} + +void +i8042_reboot(void) +{ + if (! CONFIG_PS2PORT) + return; + int i; + for (i=0; i<10; i++) { + i8042_wait_write(); + udelay(50); + outb(0xfe, PORT_PS2_STATUS); /* pulse reset low */ + udelay(50); + } +} + + +/**************************************************************** + * Device commands. + ****************************************************************/ + +#define PS2_RET_ACK 0xfa +#define PS2_RET_NAK 0xfe + +static int +ps2_recvbyte(int aux, int needack, int timeout) +{ + u32 end = timer_calc(timeout); + for (;;) { + u8 status = inb(PORT_PS2_STATUS); + if (status & I8042_STR_OBF) { + u8 data = inb(PORT_PS2_DATA); + dprintf(7, "ps2 read %x\n", data); + + if (!!(status & I8042_STR_AUXDATA) == aux) { + if (!needack) + return data; + if (data == PS2_RET_ACK) + return data; + if (data == PS2_RET_NAK) { + dprintf(1, "Got ps2 nak (status=%x)\n", status); + return data; + } + } + + // This data not part of command - just discard it. + dprintf(1, "Discarding ps2 data %02x (status=%02x)\n", data, status); + } + + if (timer_check(end)) { + // Don't warn on second byte of a reset + if (timeout > 100) + warn_timeout(); + return -1; + } + yield(); + } +} + +static int +ps2_sendbyte(int aux, u8 command, int timeout) +{ + dprintf(7, "ps2_sendbyte aux=%d cmd=%x\n", aux, command); + int ret; + if (aux) + ret = i8042_aux_write(command); + else + ret = i8042_kbd_write(command); + if (ret) + return ret; + + // Read ack. + ret = ps2_recvbyte(aux, 1, timeout); + if (ret < 0) + return ret; + if (ret != PS2_RET_ACK) + return -1; + + return 0; +} + +u8 Ps2ctr VARLOW; + +static int +__ps2_command(int aux, int command, u8 *param) +{ + int ret2; + int receive = (command >> 8) & 0xf; + int send = (command >> 12) & 0xf; + + // Disable interrupts and keyboard/mouse. + u8 ps2ctr = GET_LOW(Ps2ctr); + u8 newctr = ((ps2ctr | I8042_CTR_AUXDIS | I8042_CTR_KBDDIS) + & ~(I8042_CTR_KBDINT|I8042_CTR_AUXINT)); + dprintf(6, "i8042 ctr old=%x new=%x\n", ps2ctr, newctr); + int ret = i8042_command(I8042_CMD_CTL_WCTR, &newctr); + if (ret) + return ret; + + // Flush any interrupts already pending. + yield(); + + // Enable port command is being sent to. + if (aux) + newctr &= ~I8042_CTR_AUXDIS; + else + newctr &= ~I8042_CTR_KBDDIS; + ret = i8042_command(I8042_CMD_CTL_WCTR, &newctr); + if (ret) + goto fail; + + if (command == ATKBD_CMD_RESET_BAT) { + // Reset is special wrt timeouts and bytes received. + + // Send command. + ret = ps2_sendbyte(aux, command, 1000); + if (ret) + goto fail; + + // Receive parameters. + ret = ps2_recvbyte(aux, 0, 4000); + if (ret < 0) + goto fail; + param[0] = ret; + ret = ps2_recvbyte(aux, 0, 100); + if (ret < 0) + // Some devices only respond with one byte on reset. + ret = 0; + param[1] = ret; + } else if (command == ATKBD_CMD_GETID) { + // Getid is special wrt bytes received. + + // Send command. + ret = ps2_sendbyte(aux, command, 200); + if (ret) + goto fail; + + // Receive parameters. + ret = ps2_recvbyte(aux, 0, 500); + if (ret < 0) + goto fail; + param[0] = ret; + if (ret == 0xab || ret == 0xac || ret == 0x2b || ret == 0x5d + || ret == 0x60 || ret == 0x47) { + // These ids (keyboards) return two bytes. + ret = ps2_recvbyte(aux, 0, 500); + if (ret < 0) + goto fail; + param[1] = ret; + } else { + param[1] = 0; + } + } else { + // Send command. + ret = ps2_sendbyte(aux, command, 200); + if (ret) + goto fail; + + // Send parameters (if any). + int i; + for (i = 0; i < send; i++) { + ret = ps2_sendbyte(aux, param[i], 200); + if (ret) + goto fail; + } + + // Receive parameters (if any). + for (i = 0; i < receive; i++) { + ret = ps2_recvbyte(aux, 0, 500); + if (ret < 0) + goto fail; + param[i] = ret; + } + } + + ret = 0; + +fail: + // Restore interrupts and keyboard/mouse. + ret2 = i8042_command(I8042_CMD_CTL_WCTR, &ps2ctr); + if (ret2) + return ret2; + + return ret; +} + +static int +ps2_command(int aux, int command, u8 *param) +{ + dprintf(7, "ps2_command aux=%d cmd=%x\n", aux, command); + int ret = __ps2_command(aux, command, param); + if (ret) + dprintf(2, "ps2 command %x failed (aux=%d)\n", command, aux); + return ret; +} + +int +ps2_kbd_command(int command, u8 *param) +{ + if (! CONFIG_PS2PORT) + return -1; + return ps2_command(0, command, param); +} + +int +ps2_mouse_command(int command, u8 *param) +{ + if (! CONFIG_PS2PORT) + return -1; + + // Update ps2ctr for mouse enable/disable. + if (command == PSMOUSE_CMD_ENABLE || command == PSMOUSE_CMD_DISABLE) { + u8 ps2ctr = GET_LOW(Ps2ctr); + if (command == PSMOUSE_CMD_ENABLE) + ps2ctr = (ps2ctr | I8042_CTR_AUXINT) & ~I8042_CTR_AUXDIS; + else + ps2ctr = (ps2ctr | I8042_CTR_AUXDIS) & ~I8042_CTR_AUXINT; + SET_LOW(Ps2ctr, ps2ctr); + } + + return ps2_command(1, command, param); +} + + +/**************************************************************** + * IRQ handlers + ****************************************************************/ + +// INT74h : PS/2 mouse hardware interrupt +void VISIBLE16 +handle_74(void) +{ + if (! CONFIG_PS2PORT) + return; + + debug_isr(DEBUG_ISR_74); + + u8 v = inb(PORT_PS2_STATUS); + if ((v & (I8042_STR_OBF|I8042_STR_AUXDATA)) + != (I8042_STR_OBF|I8042_STR_AUXDATA)) { + dprintf(1, "ps2 mouse irq but no mouse data.\n"); + goto done; + } + v = inb(PORT_PS2_DATA); + + if (!(GET_LOW(Ps2ctr) & I8042_CTR_AUXINT)) + // Interrupts not enabled. + goto done; + + process_mouse(v); + +done: + pic_eoi2(); +} + +// INT09h : Keyboard Hardware Service Entry Point +void VISIBLE16 +handle_09(void) +{ + if (! CONFIG_PS2PORT) + return; + + debug_isr(DEBUG_ISR_09); + + // read key from keyboard controller + u8 v = inb(PORT_PS2_STATUS); + if (v & I8042_STR_AUXDATA) { + dprintf(1, "ps2 keyboard irq but found mouse data?!\n"); + goto done; + } + v = inb(PORT_PS2_DATA); + + if (!(GET_LOW(Ps2ctr) & I8042_CTR_KBDINT)) + // Interrupts not enabled. + goto done; + + process_key(v); + + // Some old programs expect ISR to turn keyboard back on. + i8042_command(I8042_CMD_KBD_ENABLE, NULL); + +done: + pic_eoi1(); +} + + +/**************************************************************** + * Setup + ****************************************************************/ + +static void +ps2_keyboard_setup(void *data) +{ + /* flush incoming keys */ + int ret = i8042_flush(); + if (ret) + return; + + // Controller self-test. + u8 param[2]; + ret = i8042_command(I8042_CMD_CTL_TEST, param); + if (ret) + return; + if (param[0] != 0x55) { + dprintf(1, "i8042 self test failed (got %x not 0x55)\n", param[0]); + return; + } + + // Controller keyboard test. + ret = i8042_command(I8042_CMD_KBD_TEST, param); + if (ret) + return; + if (param[0] != 0x00) { + dprintf(1, "i8042 keyboard test failed (got %x not 0x00)\n", param[0]); + return; + } + + // Disable keyboard and mouse events. + SET_LOW(Ps2ctr, I8042_CTR_KBDDIS | I8042_CTR_AUXDIS); + + + /* ------------------- keyboard side ------------------------*/ + /* reset keyboard and self test (keyboard side) */ + int spinupdelay = romfile_loadint("etc/ps2-keyboard-spinup", 0); + u32 end = timer_calc(spinupdelay); + for (;;) { + ret = ps2_kbd_command(ATKBD_CMD_RESET_BAT, param); + if (!ret) + break; + if (timer_check(end)) { + if (spinupdelay) + warn_timeout(); + return; + } + yield(); + } + if (param[0] != 0xaa) { + dprintf(1, "keyboard self test failed (got %x not 0xaa)\n", param[0]); + return; + } + + /* Disable keyboard */ + ret = ps2_kbd_command(ATKBD_CMD_RESET_DIS, NULL); + if (ret) + return; + + // Set scancode command (mode 2) + param[0] = 0x02; + ret = ps2_kbd_command(ATKBD_CMD_SSCANSET, param); + if (ret) + return; + + // Keyboard Mode: disable mouse, scan code convert, enable kbd IRQ + SET_LOW(Ps2ctr, I8042_CTR_AUXDIS | I8042_CTR_XLATE | I8042_CTR_KBDINT); + + /* Enable keyboard */ + ret = ps2_kbd_command(ATKBD_CMD_ENABLE, NULL); + if (ret) + return; + + dprintf(1, "PS2 keyboard initialized\n"); +} + +void +ps2port_setup(void) +{ + ASSERT32FLAT(); + if (! CONFIG_PS2PORT) + return; + dprintf(3, "init ps2port\n"); + + enable_hwirq(1, FUNC16(entry_09)); + enable_hwirq(12, FUNC16(entry_74)); + + run_thread(ps2_keyboard_setup, NULL); +} diff --git a/roms/seabios/src/hw/ps2port.h b/roms/seabios/src/hw/ps2port.h new file mode 100644 index 000000000..2e6f25a53 --- /dev/null +++ b/roms/seabios/src/hw/ps2port.h @@ -0,0 +1,75 @@ +// Basic ps2 port (keyboard/mouse) command handling. +#ifndef __PS2PORT_H +#define __PS2PORT_H + +#define PORT_PS2_DATA 0x0060 +#define PORT_PS2_CTRLB 0x0061 +#define PORT_PS2_STATUS 0x0064 +#define PORT_A20 0x0092 + +// PORT_A20 bitdefs +#define A20_ENABLE_BIT 0x02 + +// Standard commands. +#define I8042_CMD_CTL_RCTR 0x0120 +#define I8042_CMD_CTL_WCTR 0x1060 +#define I8042_CMD_CTL_TEST 0x01aa + +#define I8042_CMD_KBD_TEST 0x01ab +#define I8042_CMD_KBD_DISABLE 0x00ad +#define I8042_CMD_KBD_ENABLE 0x00ae + +#define I8042_CMD_AUX_DISABLE 0x00a7 +#define I8042_CMD_AUX_ENABLE 0x00a8 +#define I8042_CMD_AUX_SEND 0x10d4 + +// Keyboard commands +#define ATKBD_CMD_SETLEDS 0x10ed +#define ATKBD_CMD_SSCANSET 0x10f0 +#define ATKBD_CMD_GETID 0x02f2 +#define ATKBD_CMD_ENABLE 0x00f4 +#define ATKBD_CMD_RESET_DIS 0x00f5 +#define ATKBD_CMD_RESET_BAT 0x02ff + +// Mouse commands +#define PSMOUSE_CMD_SETSCALE11 0x00e6 +#define PSMOUSE_CMD_SETSCALE21 0x00e7 +#define PSMOUSE_CMD_SETRES 0x10e8 +#define PSMOUSE_CMD_GETINFO 0x03e9 +#define PSMOUSE_CMD_GETID 0x02f2 +#define PSMOUSE_CMD_SETRATE 0x10f3 +#define PSMOUSE_CMD_ENABLE 0x00f4 +#define PSMOUSE_CMD_DISABLE 0x00f5 +#define PSMOUSE_CMD_RESET_BAT 0x02ff + +// Status register bits. +#define I8042_STR_PARITY 0x80 +#define I8042_STR_TIMEOUT 0x40 +#define I8042_STR_AUXDATA 0x20 +#define I8042_STR_KEYLOCK 0x10 +#define I8042_STR_CMDDAT 0x08 +#define I8042_STR_MUXERR 0x04 +#define I8042_STR_IBF 0x02 +#define I8042_STR_OBF 0x01 + +// Control register bits. +#define I8042_CTR_KBDINT 0x01 +#define I8042_CTR_AUXINT 0x02 +#define I8042_CTR_IGNKEYLOCK 0x08 +#define I8042_CTR_KBDDIS 0x10 +#define I8042_CTR_AUXDIS 0x20 +#define I8042_CTR_XLATE 0x40 + +#ifndef __ASSEMBLY__ + +#include "types.h" // u8 + +// functions +void i8042_reboot(void); +int ps2_kbd_command(int command, u8 *param); +int ps2_mouse_command(int command, u8 *param); +void ps2port_setup(void); + +#endif // !__ASSEMBLY__ + +#endif // ps2port.h diff --git a/roms/seabios/src/hw/pvscsi.c b/roms/seabios/src/hw/pvscsi.c new file mode 100644 index 000000000..6911230cb --- /dev/null +++ b/roms/seabios/src/hw/pvscsi.c @@ -0,0 +1,364 @@ +// QEMU VMWARE Paravirtualized SCSI boot support. +// +// Copyright (c) 2013 Ravello Systems LTD (http://ravellosystems.com) +// +// Authors: +// Evgeny Budilovsky <evgeny.budilovsky@ravellosystems.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // struct drive_s +#include "blockcmd.h" // scsi_drive_setup +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_DEVICE_ID_VMWARE_PVSCSI +#include "pci_regs.h" // PCI_VENDOR_ID +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // usleep +#include "pvscsi.h" +#include "virtio-ring.h" // PAGE_SHIFT, virt_to_phys + +#define MASK(n) ((1 << (n)) - 1) + +#define SIMPLE_QUEUE_TAG 0x20 + +#define PVSCSI_INTR_CMPL_0 (1 << 0) +#define PVSCSI_INTR_CMPL_1 (1 << 1) +#define PVSCSI_INTR_CMPL_MASK MASK(2) + +#define PVSCSI_INTR_MSG_0 (1 << 2) +#define PVSCSI_INTR_MSG_1 (1 << 3) +#define PVSCSI_INTR_MSG_MASK (MASK(2) << 2) +#define PVSCSI_INTR_ALL_SUPPORTED MASK(4) + +#define PVSCSI_FLAG_CMD_WITH_SG_LIST (1 << 0) +#define PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB (1 << 1) +#define PVSCSI_FLAG_CMD_DIR_NONE (1 << 2) +#define PVSCSI_FLAG_CMD_DIR_TOHOST (1 << 3) +#define PVSCSI_FLAG_CMD_DIR_TODEVICE (1 << 4) + +enum PVSCSIRegOffset { + PVSCSI_REG_OFFSET_COMMAND = 0x0, + PVSCSI_REG_OFFSET_COMMAND_DATA = 0x4, + PVSCSI_REG_OFFSET_COMMAND_STATUS = 0x8, + PVSCSI_REG_OFFSET_LAST_STS_0 = 0x100, + PVSCSI_REG_OFFSET_LAST_STS_1 = 0x104, + PVSCSI_REG_OFFSET_LAST_STS_2 = 0x108, + PVSCSI_REG_OFFSET_LAST_STS_3 = 0x10c, + PVSCSI_REG_OFFSET_INTR_STATUS = 0x100c, + PVSCSI_REG_OFFSET_INTR_MASK = 0x2010, + PVSCSI_REG_OFFSET_KICK_NON_RW_IO = 0x3014, + PVSCSI_REG_OFFSET_DEBUG = 0x3018, + PVSCSI_REG_OFFSET_KICK_RW_IO = 0x4018, +}; + +enum PVSCSICommands { + PVSCSI_CMD_FIRST = 0, + PVSCSI_CMD_ADAPTER_RESET = 1, + PVSCSI_CMD_ISSUE_SCSI = 2, + PVSCSI_CMD_SETUP_RINGS = 3, + PVSCSI_CMD_RESET_BUS = 4, + PVSCSI_CMD_RESET_DEVICE = 5, + PVSCSI_CMD_ABORT_CMD = 6, + PVSCSI_CMD_CONFIG = 7, + PVSCSI_CMD_SETUP_MSG_RING = 8, + PVSCSI_CMD_DEVICE_UNPLUG = 9, + PVSCSI_CMD_LAST = 10 +}; + +#define PVSCSI_SETUP_RINGS_MAX_NUM_PAGES 32 +struct PVSCSICmdDescSetupRings { + u32 reqRingNumPages; + u32 cmpRingNumPages; + u64 ringsStatePPN; + u64 reqRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES]; + u64 cmpRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES]; +} PACKED; + +struct PVSCSIRingCmpDesc { + u64 context; + u64 dataLen; + u32 senseLen; + u16 hostStatus; + u16 scsiStatus; + u32 pad[2]; +} PACKED; + +struct PVSCSIRingsState { + u32 reqProdIdx; + u32 reqConsIdx; + u32 reqNumEntriesLog2; + + u32 cmpProdIdx; + u32 cmpConsIdx; + u32 cmpNumEntriesLog2; + + u8 pad[104]; + + u32 msgProdIdx; + u32 msgConsIdx; + u32 msgNumEntriesLog2; +} PACKED; + +struct PVSCSIRingReqDesc { + u64 context; + u64 dataAddr; + u64 dataLen; + u64 senseAddr; + u32 senseLen; + u32 flags; + u8 cdb[16]; + u8 cdbLen; + u8 lun[8]; + u8 tag; + u8 bus; + u8 target; + u8 vcpuHint; + u8 unused[59]; +} PACKED; + +struct pvscsi_ring_dsc_s { + struct PVSCSIRingsState *ring_state; + struct PVSCSIRingReqDesc *ring_reqs; + struct PVSCSIRingCmpDesc *ring_cmps; +}; + +struct pvscsi_lun_s { + struct drive_s drive; + struct pci_device *pci; + u32 iobase; + u8 target; + u8 lun; + struct pvscsi_ring_dsc_s *ring_dsc; +}; + +static void +pvscsi_write_cmd_desc(u32 iobase, u32 cmd, const void *desc, size_t len) +{ + const u32 *ptr = desc; + size_t i; + + len /= sizeof(*ptr); + pci_writel(iobase + PVSCSI_REG_OFFSET_COMMAND, cmd); + for (i = 0; i < len; i++) + pci_writel(iobase + PVSCSI_REG_OFFSET_COMMAND_DATA, ptr[i]); +} + +static void +pvscsi_kick_rw_io(u32 iobase) +{ + pci_writel(iobase + PVSCSI_REG_OFFSET_KICK_RW_IO, 0); +} + +static void +pvscsi_wait_intr_cmpl(u32 iobase) +{ + while (!(pci_readl(iobase + PVSCSI_REG_OFFSET_INTR_STATUS) & PVSCSI_INTR_CMPL_MASK)) + usleep(5); + pci_writel(iobase + PVSCSI_REG_OFFSET_INTR_STATUS, PVSCSI_INTR_CMPL_MASK); + +} + +static void +pvscsi_init_rings(u32 iobase, struct pvscsi_ring_dsc_s **ring_dsc) +{ + struct PVSCSICmdDescSetupRings cmd = {0,}; + + struct pvscsi_ring_dsc_s *dsc = memalign_low(sizeof(*dsc), PAGE_SIZE); + if (!dsc) { + warn_noalloc(); + return; + } + + dsc->ring_state = + (struct PVSCSIRingsState *)memalign_low(PAGE_SIZE, PAGE_SIZE); + dsc->ring_reqs = + (struct PVSCSIRingReqDesc *)memalign_low(PAGE_SIZE, PAGE_SIZE); + dsc->ring_cmps = + (struct PVSCSIRingCmpDesc *)memalign_low(PAGE_SIZE, PAGE_SIZE); + if (!dsc->ring_state || !dsc->ring_reqs || !dsc->ring_cmps) { + warn_noalloc(); + return; + } + memset(dsc->ring_state, 0, PAGE_SIZE); + memset(dsc->ring_reqs, 0, PAGE_SIZE); + memset(dsc->ring_cmps, 0, PAGE_SIZE); + + cmd.reqRingNumPages = 1; + cmd.cmpRingNumPages = 1; + cmd.ringsStatePPN = virt_to_phys(dsc->ring_state) >> PAGE_SHIFT; + cmd.reqRingPPNs[0] = virt_to_phys(dsc->ring_reqs) >> PAGE_SHIFT; + cmd.cmpRingPPNs[0] = virt_to_phys(dsc->ring_cmps) >> PAGE_SHIFT; + + pvscsi_write_cmd_desc(iobase, PVSCSI_CMD_SETUP_RINGS, + &cmd, sizeof(cmd)); + *ring_dsc = dsc; +} + +static void pvscsi_fill_req(struct PVSCSIRingsState *s, + struct PVSCSIRingReqDesc *req, + u16 target, u16 lun, void *cdbcmd, u16 blocksize, + struct disk_op_s *op) +{ + SET_LOWFLAT(req->bus, 0); + SET_LOWFLAT(req->target, target); + memset(LOWFLAT2LOW(&req->lun[0]), 0, sizeof(req->lun)); + SET_LOWFLAT(req->lun[1], lun); + SET_LOWFLAT(req->senseLen, 0); + SET_LOWFLAT(req->senseAddr, 0); + SET_LOWFLAT(req->cdbLen, 16); + SET_LOWFLAT(req->vcpuHint, 0); + memcpy(LOWFLAT2LOW(&req->cdb[0]), cdbcmd, 16); + SET_LOWFLAT(req->tag, SIMPLE_QUEUE_TAG); + SET_LOWFLAT(req->flags, + cdb_is_read(cdbcmd, blocksize) ? + PVSCSI_FLAG_CMD_DIR_TOHOST : PVSCSI_FLAG_CMD_DIR_TODEVICE); + + SET_LOWFLAT(req->dataLen, op->count * blocksize); + SET_LOWFLAT(req->dataAddr, (u32)op->buf_fl); + SET_LOWFLAT(s->reqProdIdx, GET_LOWFLAT(s->reqProdIdx) + 1); + +} + +static u32 +pvscsi_get_rsp(struct PVSCSIRingsState *s, + struct PVSCSIRingCmpDesc *rsp) +{ + u32 status = GET_LOWFLAT(rsp->hostStatus); + SET_LOWFLAT(s->cmpConsIdx, GET_LOWFLAT(s->cmpConsIdx)+1); + return status; +} + +static int +pvscsi_cmd(struct pvscsi_lun_s *plun_gf, struct disk_op_s *op, + void *cdbcmd, u16 target, u16 lun, u16 blocksize) +{ + struct pvscsi_ring_dsc_s *ring_dsc = GET_GLOBALFLAT(plun_gf->ring_dsc); + struct PVSCSIRingsState *s = GET_LOWFLAT(ring_dsc->ring_state); + u32 req_entries = GET_LOWFLAT(s->reqNumEntriesLog2); + u32 cmp_entries = GET_LOWFLAT(s->cmpNumEntriesLog2); + struct PVSCSIRingReqDesc *req; + struct PVSCSIRingCmpDesc *rsp; + u32 status; + + if (GET_LOWFLAT(s->reqProdIdx) - GET_LOWFLAT(s->cmpConsIdx) >= 1 << req_entries) { + dprintf(1, "pvscsi: ring full: reqProdIdx=%d cmpConsIdx=%d\n", + GET_LOWFLAT(s->reqProdIdx), GET_LOWFLAT(s->cmpConsIdx)); + return DISK_RET_EBADTRACK; + } + + req = GET_LOWFLAT(ring_dsc->ring_reqs) + (GET_LOWFLAT(s->reqProdIdx) & MASK(req_entries)); + pvscsi_fill_req(s, req, target, lun, cdbcmd, blocksize, op); + + pvscsi_kick_rw_io(GET_GLOBALFLAT(plun_gf->iobase)); + pvscsi_wait_intr_cmpl(GET_GLOBALFLAT(plun_gf->iobase)); + + rsp = GET_LOWFLAT(ring_dsc->ring_cmps) + (GET_LOWFLAT(s->cmpConsIdx) & MASK(cmp_entries)); + status = pvscsi_get_rsp(s, rsp); + + return status == 0 ? DISK_RET_SUCCESS : DISK_RET_EBADTRACK; +} + +int +pvscsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + if (!CONFIG_PVSCSI) + return DISK_RET_EBADTRACK; + + struct pvscsi_lun_s *plun_gf = + container_of(op->drive_gf, struct pvscsi_lun_s, drive); + + return pvscsi_cmd(plun_gf, op, cdbcmd, + GET_GLOBALFLAT(plun_gf->target), + GET_GLOBALFLAT(plun_gf->lun), + blocksize); + +} + +static int +pvscsi_add_lun(struct pci_device *pci, u32 iobase, + struct pvscsi_ring_dsc_s *ring_dsc, u8 target, u8 lun) +{ + struct pvscsi_lun_s *plun = malloc_fseg(sizeof(*plun)); + if (!plun) { + warn_noalloc(); + return -1; + } + memset(plun, 0, sizeof(*plun)); + plun->drive.type = DTYPE_PVSCSI; + plun->drive.cntl_id = pci->bdf; + plun->pci = pci; + plun->target = target; + plun->lun = lun; + plun->iobase = iobase; + plun->ring_dsc = ring_dsc; + + char *name = znprintf(16, "pvscsi %02x:%02x.%x %d:%d", + pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf), + pci_bdf_to_fn(pci->bdf), target, lun); + int prio = bootprio_find_scsi_device(pci, target, lun); + int ret = scsi_drive_setup(&plun->drive, name, prio); + free(name); + if (ret) + goto fail; + return 0; + +fail: + free(plun); + return -1; +} + +static void +pvscsi_scan_target(struct pci_device *pci, u32 iobase, + struct pvscsi_ring_dsc_s *ring_dsc, u8 target) +{ + /* TODO: send REPORT LUNS. For now, only LUN 0 is recognized. */ + pvscsi_add_lun(pci, iobase, ring_dsc, target, 0); +} + +static void +init_pvscsi(struct pci_device *pci) +{ + struct pvscsi_ring_dsc_s *ring_dsc = NULL; + int i; + u16 bdf = pci->bdf; + u32 iobase = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0) + & PCI_BASE_ADDRESS_MEM_MASK; + + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + + dprintf(1, "found pvscsi at %02x:%02x.%x, io @ %x\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), + pci_bdf_to_fn(bdf), iobase); + + pvscsi_write_cmd_desc(iobase, PVSCSI_CMD_ADAPTER_RESET, NULL, 0); + + pvscsi_init_rings(iobase, &ring_dsc); + for (i = 0; i < 7; i++) + pvscsi_scan_target(pci, iobase, ring_dsc, i); + + return; + +} + +void +pvscsi_setup(void) +{ + ASSERT32FLAT(); + if (! CONFIG_PVSCSI) + return; + + dprintf(3, "init pvscsi\n"); + + struct pci_device *pci; + foreachpci(pci) { + if (pci->vendor != PCI_VENDOR_ID_VMWARE + || pci->device != PCI_DEVICE_ID_VMWARE_PVSCSI) + continue; + init_pvscsi(pci); + } +} diff --git a/roms/seabios/src/hw/pvscsi.h b/roms/seabios/src/hw/pvscsi.h new file mode 100644 index 000000000..fde9f0b98 --- /dev/null +++ b/roms/seabios/src/hw/pvscsi.h @@ -0,0 +1,8 @@ +#ifndef _PVSCSI_H_ +#define _PVSCSI_H_ + +struct disk_op_s; +int pvscsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +void pvscsi_setup(void); + +#endif /* _PVSCSI_H_ */ diff --git a/roms/seabios/src/hw/ramdisk.c b/roms/seabios/src/hw/ramdisk.c new file mode 100644 index 000000000..81aed50cc --- /dev/null +++ b/roms/seabios/src/hw/ramdisk.c @@ -0,0 +1,112 @@ +// Code for emulating a drive via high-memory accesses. +// +// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // struct drive_s +#include "bregs.h" // struct bregs +#include "malloc.h" // malloc_fseg +#include "memmap.h" // add_e820 +#include "output.h" // dprintf +#include "romfile.h" // romfile_findprefix +#include "stacks.h" // call16_int +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // process_ramdisk_op + +void +ramdisk_setup(void) +{ + if (!CONFIG_FLASH_FLOPPY) + return; + + // Find image. + struct romfile_s *file = romfile_findprefix("floppyimg/", NULL); + if (!file) + return; + const char *filename = file->name; + u32 size = file->size; + dprintf(3, "Found floppy file %s of size %d\n", filename, size); + int ftype = find_floppy_type(size); + if (ftype < 0) { + dprintf(3, "No floppy type found for ramdisk size\n"); + return; + } + + // Allocate ram for image. + void *pos = memalign_tmphigh(PAGE_SIZE, size); + if (!pos) { + warn_noalloc(); + return; + } + add_e820((u32)pos, size, E820_RESERVED); + + // Copy image into ram. + int ret = file->copy(file, pos, size); + if (ret < 0) + return; + + // Setup driver. + struct drive_s *drive = init_floppy((u32)pos, ftype); + if (!drive) + return; + drive->type = DTYPE_RAMDISK; + dprintf(1, "Mapping CBFS floppy %s to addr %p\n", filename, pos); + char *desc = znprintf(MAXDESCSIZE, "Ramdisk [%s]", &filename[10]); + boot_add_floppy(drive, desc, bootprio_find_named_rom(filename, 0)); +} + +static int +ramdisk_copy(struct disk_op_s *op, int iswrite) +{ + u32 offset = GET_GLOBALFLAT(op->drive_gf->cntl_id); + offset += (u32)op->lba * DISK_SECTOR_SIZE; + u64 opd = GDT_DATA | GDT_LIMIT(0xfffff) | GDT_BASE((u32)op->buf_fl); + u64 ramd = GDT_DATA | GDT_LIMIT(0xfffff) | GDT_BASE(offset); + + u64 gdt[6]; + if (iswrite) { + gdt[2] = opd; + gdt[3] = ramd; + } else { + gdt[2] = ramd; + gdt[3] = opd; + } + + // Call int 1587 to copy data. + struct bregs br; + memset(&br, 0, sizeof(br)); + br.flags = F_CF|F_IF; + br.ah = 0x87; + br.es = GET_SEG(SS); + br.si = (u32)gdt; + br.cx = op->count * DISK_SECTOR_SIZE / 2; + call16_int(0x15, &br); + + if (br.flags & F_CF) + return DISK_RET_EBADTRACK; + return DISK_RET_SUCCESS; +} + +int +process_ramdisk_op(struct disk_op_s *op) +{ + if (!CONFIG_FLASH_FLOPPY) + return 0; + + switch (op->command) { + case CMD_READ: + return ramdisk_copy(op, 0); + case CMD_WRITE: + return ramdisk_copy(op, 1); + case CMD_VERIFY: + case CMD_FORMAT: + case CMD_RESET: + return DISK_RET_SUCCESS; + default: + op->count = 0; + return DISK_RET_EPARAM; + } +} diff --git a/roms/seabios/src/hw/rtc.c b/roms/seabios/src/hw/rtc.c new file mode 100644 index 000000000..628d5429f --- /dev/null +++ b/roms/seabios/src/hw/rtc.c @@ -0,0 +1,93 @@ +// Support for MC146818 Real Time Clock chip. +// +// Copyright (C) 2008-2013 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2002 MandrakeSoft S.A. +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_LOW +#include "rtc.h" // rtc_read +#include "stacks.h" // yield +#include "util.h" // timer_calc +#include "x86.h" // inb + +u8 +rtc_read(u8 index) +{ + index |= NMI_DISABLE_BIT; + outb(index, PORT_CMOS_INDEX); + return inb(PORT_CMOS_DATA); +} + +void +rtc_write(u8 index, u8 val) +{ + index |= NMI_DISABLE_BIT; + outb(index, PORT_CMOS_INDEX); + outb(val, PORT_CMOS_DATA); +} + +void +rtc_mask(u8 index, u8 off, u8 on) +{ + outb(index, PORT_CMOS_INDEX); + u8 val = inb(PORT_CMOS_DATA); + outb((val & ~off) | on, PORT_CMOS_DATA); +} + +int +rtc_updating(void) +{ + // This function checks to see if the update-in-progress bit + // is set in CMOS Status Register A. If not, it returns 0. + // If it is set, it tries to wait until there is a transition + // to 0, and will return 0 if such a transition occurs. A -1 + // is returned only after timing out. The maximum period + // that this bit should be set is constrained to (1984+244) + // useconds, but we wait for longer just to be sure. + + if ((rtc_read(CMOS_STATUS_A) & RTC_A_UIP) == 0) + return 0; + u32 end = timer_calc(15); + for (;;) { + if ((rtc_read(CMOS_STATUS_A) & RTC_A_UIP) == 0) + return 0; + if (timer_check(end)) + // update-in-progress never transitioned to 0 + return -1; + yield(); + } +} + +void +rtc_setup(void) +{ + rtc_write(CMOS_STATUS_A, 0x26); // 32,768Khz src, 976.5625us updates + rtc_mask(CMOS_STATUS_B, ~RTC_B_DSE, RTC_B_24HR); + rtc_read(CMOS_STATUS_C); + rtc_read(CMOS_STATUS_D); +} + +int RTCusers VARLOW; + +void +rtc_use(void) +{ + int count = GET_LOW(RTCusers); + SET_LOW(RTCusers, count+1); + if (count) + return; + // Turn on the Periodic Interrupt timer + rtc_mask(CMOS_STATUS_B, 0, RTC_B_PIE); +} + +void +rtc_release(void) +{ + int count = GET_LOW(RTCusers); + SET_LOW(RTCusers, count-1); + if (count != 1) + return; + // Clear the Periodic Interrupt. + rtc_mask(CMOS_STATUS_B, RTC_B_PIE, 0); +} diff --git a/roms/seabios/src/hw/rtc.h b/roms/seabios/src/hw/rtc.h new file mode 100644 index 000000000..252e73a41 --- /dev/null +++ b/roms/seabios/src/hw/rtc.h @@ -0,0 +1,75 @@ +#ifndef __RTC_H +#define __RTC_H + +#define PORT_CMOS_INDEX 0x0070 +#define PORT_CMOS_DATA 0x0071 + +// PORT_CMOS_INDEX nmi disable bit +#define NMI_DISABLE_BIT 0x80 + +// Standard BIOS RTC chip entries +#define CMOS_RTC_SECONDS 0x00 +#define CMOS_RTC_SECONDS_ALARM 0x01 +#define CMOS_RTC_MINUTES 0x02 +#define CMOS_RTC_MINUTES_ALARM 0x03 +#define CMOS_RTC_HOURS 0x04 +#define CMOS_RTC_HOURS_ALARM 0x05 +#define CMOS_RTC_DAY_WEEK 0x06 +#define CMOS_RTC_DAY_MONTH 0x07 +#define CMOS_RTC_MONTH 0x08 +#define CMOS_RTC_YEAR 0x09 +#define CMOS_STATUS_A 0x0a +#define CMOS_STATUS_B 0x0b +#define CMOS_STATUS_C 0x0c +#define CMOS_STATUS_D 0x0d +#define CMOS_RESET_CODE 0x0f + +// QEMU cmos config fields. DO NOT ADD MORE. (All new content should +// be passed via the fw_cfg "file" interface.) +#define CMOS_FLOPPY_DRIVE_TYPE 0x10 +#define CMOS_DISK_DATA 0x12 +#define CMOS_EQUIPMENT_INFO 0x14 +#define CMOS_DISK_DRIVE1_TYPE 0x19 +#define CMOS_DISK_DRIVE2_TYPE 0x1a +#define CMOS_DISK_DRIVE1_CYL 0x1b +#define CMOS_DISK_DRIVE2_CYL 0x24 +#define CMOS_MEM_EXTMEM_LOW 0x30 +#define CMOS_MEM_EXTMEM_HIGH 0x31 +#define CMOS_CENTURY 0x32 +#define CMOS_MEM_EXTMEM2_LOW 0x34 +#define CMOS_MEM_EXTMEM2_HIGH 0x35 +#define CMOS_BIOS_BOOTFLAG1 0x38 +#define CMOS_BIOS_DISKTRANSFLAG 0x39 +#define CMOS_BIOS_BOOTFLAG2 0x3d +#define CMOS_MEM_HIGHMEM_LOW 0x5b +#define CMOS_MEM_HIGHMEM_MID 0x5c +#define CMOS_MEM_HIGHMEM_HIGH 0x5d +#define CMOS_BIOS_SMP_COUNT 0x5f + +// RTC register flags +#define RTC_A_UIP 0x80 + +#define RTC_B_SET 0x80 +#define RTC_B_PIE 0x40 +#define RTC_B_AIE 0x20 +#define RTC_B_UIE 0x10 +#define RTC_B_BIN 0x04 +#define RTC_B_24HR 0x02 +#define RTC_B_DSE 0x01 + +#ifndef __ASSEMBLY__ + +#include "types.h" // u8 + +// rtc.c +u8 rtc_read(u8 index); +void rtc_write(u8 index, u8 val); +void rtc_mask(u8 index, u8 off, u8 on); +int rtc_updating(void); +void rtc_setup(void); +void rtc_use(void); +void rtc_release(void); + +#endif // !__ASSEMBLY__ + +#endif // rtc.h diff --git a/roms/seabios/src/hw/serialio.c b/roms/seabios/src/hw/serialio.c new file mode 100644 index 000000000..6486fc086 --- /dev/null +++ b/roms/seabios/src/hw/serialio.c @@ -0,0 +1,89 @@ +// Low-level serial (and serial-like) device access. +// +// Copyright (C) 2008-1013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_DEBUG_SERIAL +#include "fw/paravirt.h" // RunningOnQEMU +#include "output.h" // dprintf +#include "serialio.h" // serial_debug_preinit +#include "x86.h" // outb + + +/**************************************************************** + * Serial port debug output + ****************************************************************/ + +#define DEBUG_TIMEOUT 100000 + +// Setup the debug serial port for output. +void +serial_debug_preinit(void) +{ + if (!CONFIG_DEBUG_SERIAL) + return; + // setup for serial logging: 8N1 + u8 oldparam, newparam = 0x03; + oldparam = inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LCR); + outb(newparam, CONFIG_DEBUG_SERIAL_PORT+SEROFF_LCR); + // Disable irqs + u8 oldier, newier = 0; + oldier = inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_IER); + outb(newier, CONFIG_DEBUG_SERIAL_PORT+SEROFF_IER); + + if (oldparam != newparam || oldier != newier) + dprintf(1, "Changing serial settings was %x/%x now %x/%x\n" + , oldparam, oldier, newparam, newier); +} + +// Write a character to the serial port. +static void +serial_debug(char c) +{ + if (!CONFIG_DEBUG_SERIAL) + return; + int timeout = DEBUG_TIMEOUT; + while ((inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LSR) & 0x20) != 0x20) + if (!timeout--) + // Ran out of time. + return; + outb(c, CONFIG_DEBUG_SERIAL_PORT+SEROFF_DATA); +} + +void +serial_debug_putc(char c) +{ + if (c == '\n') + serial_debug('\r'); + serial_debug(c); +} + +// Make sure all serial port writes have been completely sent. +void +serial_debug_flush(void) +{ + if (!CONFIG_DEBUG_SERIAL) + return; + int timeout = DEBUG_TIMEOUT; + while ((inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LSR) & 0x60) != 0x60) + if (!timeout--) + // Ran out of time. + return; +} + + +/**************************************************************** + * QEMU debug port + ****************************************************************/ + +u16 DebugOutputPort VARFSEG = 0x402; + +// Write a character to the special debugging port. +void +qemu_debug_putc(char c) +{ + if (CONFIG_DEBUG_IO && runningOnQEMU()) + // Send character to debug port. + outb(c, GET_GLOBAL(DebugOutputPort)); +} diff --git a/roms/seabios/src/hw/serialio.h b/roms/seabios/src/hw/serialio.h new file mode 100644 index 000000000..88296fe7f --- /dev/null +++ b/roms/seabios/src/hw/serialio.h @@ -0,0 +1,29 @@ +#ifndef __SERIALIO_H +#define __SERIALIO_H + +#include "types.h" // u16 + +#define PORT_LPT2 0x0278 +#define PORT_SERIAL4 0x02e8 +#define PORT_SERIAL2 0x02f8 +#define PORT_LPT1 0x0378 +#define PORT_SERIAL3 0x03e8 +#define PORT_SERIAL1 0x03f8 + +// Serial port offsets +#define SEROFF_DATA 0 +#define SEROFF_DLL 0 +#define SEROFF_IER 1 +#define SEROFF_DLH 1 +#define SEROFF_IIR 2 +#define SEROFF_LCR 3 +#define SEROFF_LSR 5 +#define SEROFF_MSR 6 + +void serial_debug_preinit(void); +void serial_debug_putc(char c); +void serial_debug_flush(void); +extern u16 DebugOutputPort; +void qemu_debug_putc(char c); + +#endif // serialio.h diff --git a/roms/seabios/src/hw/timer.c b/roms/seabios/src/hw/timer.c new file mode 100644 index 000000000..2832dece1 --- /dev/null +++ b/roms/seabios/src/hw/timer.c @@ -0,0 +1,257 @@ +// Internal timer and Intel 8253 Programmable Interrupt Timer (PIT) support. +// +// Copyright (C) 2008-2013 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_LOW +#include "config.h" // CONFIG_* +#include "output.h" // dprintf +#include "ps2port.h" // PORT_PS2_CTRLB +#include "stacks.h" // yield +#include "util.h" // timer_setup +#include "x86.h" // cpuid + +#define PORT_PIT_COUNTER0 0x0040 +#define PORT_PIT_COUNTER1 0x0041 +#define PORT_PIT_COUNTER2 0x0042 +#define PORT_PIT_MODE 0x0043 + +// Bits for PORT_PIT_MODE +#define PM_SEL_TIMER0 (0<<6) +#define PM_SEL_TIMER1 (1<<6) +#define PM_SEL_TIMER2 (2<<6) +#define PM_SEL_READBACK (3<<6) +#define PM_ACCESS_LATCH (0<<4) +#define PM_ACCESS_LOBYTE (1<<4) +#define PM_ACCESS_HIBYTE (2<<4) +#define PM_ACCESS_WORD (3<<4) +#define PM_MODE0 (0<<1) +#define PM_MODE1 (1<<1) +#define PM_MODE2 (2<<1) +#define PM_MODE3 (3<<1) +#define PM_MODE4 (4<<1) +#define PM_MODE5 (5<<1) +#define PM_CNT_BINARY (0<<0) +#define PM_CNT_BCD (1<<0) +#define PM_READ_COUNTER0 (1<<1) +#define PM_READ_COUNTER1 (1<<2) +#define PM_READ_COUNTER2 (1<<3) +#define PM_READ_STATUSVALUE (0<<4) +#define PM_READ_VALUE (1<<4) +#define PM_READ_STATUS (2<<4) + +// Bits for PORT_PS2_CTRLB +#define PPCB_T2GATE (1<<0) +#define PPCB_SPKR (1<<1) +#define PPCB_T2OUT (1<<5) + +#define PMTIMER_HZ 3579545 // Underlying Hz of the PM Timer +#define PMTIMER_TO_PIT 3 // Ratio of pmtimer rate to pit rate + +u32 TimerKHz VARFSEG; +u16 TimerPort VARFSEG; +u8 ShiftTSC VARFSEG; + + +/**************************************************************** + * Internal timer setup + ****************************************************************/ + +#define CALIBRATE_COUNT 0x800 // Approx 1.7ms + +// Calibrate the CPU time-stamp-counter +static void +tsctimer_setup(void) +{ + // Setup "timer2" + u8 orig = inb(PORT_PS2_CTRLB); + outb((orig & ~PPCB_SPKR) | PPCB_T2GATE, PORT_PS2_CTRLB); + /* binary, mode 0, LSB/MSB, Ch 2 */ + outb(PM_SEL_TIMER2|PM_ACCESS_WORD|PM_MODE0|PM_CNT_BINARY, PORT_PIT_MODE); + /* LSB of ticks */ + outb(CALIBRATE_COUNT & 0xFF, PORT_PIT_COUNTER2); + /* MSB of ticks */ + outb(CALIBRATE_COUNT >> 8, PORT_PIT_COUNTER2); + + u64 start = rdtscll(); + while ((inb(PORT_PS2_CTRLB) & PPCB_T2OUT) == 0) + ; + u64 end = rdtscll(); + + // Restore PORT_PS2_CTRLB + outb(orig, PORT_PS2_CTRLB); + + // Store calibrated cpu khz. + u64 diff = end - start; + dprintf(6, "tsc calibrate start=%u end=%u diff=%u\n" + , (u32)start, (u32)end, (u32)diff); + u64 t = DIV_ROUND_UP(diff * PMTIMER_HZ, CALIBRATE_COUNT); + while (t >= (1<<24)) { + ShiftTSC++; + t = (t + 1) >> 1; + } + TimerKHz = DIV_ROUND_UP((u32)t, 1000 * PMTIMER_TO_PIT); + + dprintf(1, "CPU Mhz=%u\n", (TimerKHz << ShiftTSC) / 1000); +} + +// Setup internal timers. +void +timer_setup(void) +{ + if (CONFIG_PMTIMER && TimerPort) { + dprintf(3, "pmtimer already configured; will not calibrate TSC\n"); + return; + } + + u32 eax, ebx, ecx, edx, cpuid_features = 0; + cpuid(0, &eax, &ebx, &ecx, &edx); + if (eax > 0) + cpuid(1, &eax, &ebx, &ecx, &cpuid_features); + + if (!(cpuid_features & CPUID_TSC)) { + TimerPort = PORT_PIT_COUNTER0; + TimerKHz = DIV_ROUND_UP(PMTIMER_HZ, 1000 * PMTIMER_TO_PIT); + dprintf(3, "386/486 class CPU. Using TSC emulation\n"); + return; + } + + tsctimer_setup(); +} + +void +pmtimer_setup(u16 ioport) +{ + if (!CONFIG_PMTIMER) + return; + dprintf(1, "Using pmtimer, ioport 0x%x\n", ioport); + TimerPort = ioport; + TimerKHz = DIV_ROUND_UP(PMTIMER_HZ, 1000); +} + + +/**************************************************************** + * Internal timer reading + ****************************************************************/ + +u32 TimerLast VARLOW; + +// Add extra high bits to timers that have less than 32bits of precision. +static u32 +timer_adjust_bits(u32 value, u32 validbits) +{ + u32 last = GET_LOW(TimerLast); + value = (last & ~validbits) | (value & validbits); + if (value < last) + value += validbits + 1; + SET_LOW(TimerLast, value); + return value; +} + +// Sample the current timer value. +static u32 +timer_read(void) +{ + u16 port = GET_GLOBAL(TimerPort); + if (!port) + // Read from CPU TSC + return rdtscll() >> GET_GLOBAL(ShiftTSC); + if (CONFIG_PMTIMER && port != PORT_PIT_COUNTER0) + // Read from PMTIMER + return timer_adjust_bits(inl(port), 0xffffff); + // Read from PIT. + outb(PM_SEL_READBACK | PM_READ_VALUE | PM_READ_COUNTER0, PORT_PIT_MODE); + u16 v = inb(PORT_PIT_COUNTER0) | (inb(PORT_PIT_COUNTER0) << 8); + return timer_adjust_bits(v, 0xffff); +} + +// Check if the current time is past a previously calculated end time. +int +timer_check(u32 end) +{ + return (s32)(timer_read() - end) > 0; +} + +static void +timer_delay(u32 diff) +{ + u32 start = timer_read(); + u32 end = start + diff; + while (!timer_check(end)) + cpu_relax(); +} + +static void +timer_sleep(u32 diff) +{ + u32 start = timer_read(); + u32 end = start + diff; + while (!timer_check(end)) + yield(); +} + +void ndelay(u32 count) { + timer_delay(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000000)); +} +void udelay(u32 count) { + timer_delay(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000)); +} +void mdelay(u32 count) { + timer_delay(count * GET_GLOBAL(TimerKHz)); +} + +void nsleep(u32 count) { + timer_sleep(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000000)); +} +void usleep(u32 count) { + timer_sleep(DIV_ROUND_UP(count * GET_GLOBAL(TimerKHz), 1000)); +} +void msleep(u32 count) { + timer_sleep(count * GET_GLOBAL(TimerKHz)); +} + +// Return the TSC value that is 'msecs' time in the future. +u32 +timer_calc(u32 msecs) +{ + return timer_read() + (GET_GLOBAL(TimerKHz) * msecs); +} +u32 +timer_calc_usec(u32 usecs) +{ + return timer_read() + DIV_ROUND_UP(GET_GLOBAL(TimerKHz) * usecs, 1000); +} + + +/**************************************************************** + * PIT setup + ****************************************************************/ + +#define PIT_TICK_INTERVAL 65536 // Default interval for 18.2Hz timer + +// Return the number of milliseconds in 'ticks' number of timer irqs. +u32 +ticks_to_ms(u32 ticks) +{ + u32 t = PIT_TICK_INTERVAL * 1000 * PMTIMER_TO_PIT * ticks; + return DIV_ROUND_UP(t, PMTIMER_HZ); +} + +// Return the number of timer irqs in 'ms' number of milliseconds. +u32 +ticks_from_ms(u32 ms) +{ + u32 t = DIV_ROUND_UP((u64)ms * PMTIMER_HZ, PIT_TICK_INTERVAL); + return DIV_ROUND_UP(t, 1000 * PMTIMER_TO_PIT); +} + +void +pit_setup(void) +{ + // timer0: binary count, 16bit count, mode 2 + outb(PM_SEL_TIMER0|PM_ACCESS_WORD|PM_MODE2|PM_CNT_BINARY, PORT_PIT_MODE); + // maximum count of 0000H = 18.2Hz + outb(0x0, PORT_PIT_COUNTER0); + outb(0x0, PORT_PIT_COUNTER0); +} diff --git a/roms/seabios/src/hw/usb-ehci.c b/roms/seabios/src/hw/usb-ehci.c new file mode 100644 index 000000000..b495d6cd6 --- /dev/null +++ b/roms/seabios/src/hw/usb-ehci.c @@ -0,0 +1,726 @@ +// Code for handling EHCI USB controllers. +// +// Copyright (C) 2010 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_LOWFLAT +#include "config.h" // CONFIG_* +#include "output.h" // dprintf +#include "malloc.h" // free +#include "pci.h" // pci_bdf_to_bus +#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_UHCI +#include "pci_regs.h" // PCI_BASE_ADDRESS_0 +#include "string.h" // memset +#include "usb.h" // struct usb_s +#include "usb-ehci.h" // struct ehci_qh +#include "usb-ohci.h" // ohci_setup +#include "usb-uhci.h" // uhci_setup +#include "util.h" // msleep +#include "x86.h" // readl + +struct usb_ehci_s { + struct usb_s usb; + struct ehci_caps *caps; + struct ehci_regs *regs; + struct ehci_qh *async_qh; + struct pci_device *companion[8]; + int checkports; + int legacycount; +}; + +struct ehci_pipe { + struct ehci_qh qh; + struct ehci_qtd *next_td, *tds; + void *data; + struct usb_pipe pipe; +}; + + +/**************************************************************** + * Root hub + ****************************************************************/ + +#define EHCI_TIME_POSTPOWER 20 +#define EHCI_TIME_POSTRESET 2 + +// Check if need companion controllers for full/low speed devices +static void +ehci_note_port(struct usb_ehci_s *cntl) +{ + if (--cntl->checkports) + // Ports still being detected. + return; + if (! cntl->legacycount) + // No full/low speed devices found. + return; + // Start companion controllers. + int i; + for (i=0; i<ARRAY_SIZE(cntl->companion); i++) { + struct pci_device *pci = cntl->companion[i]; + if (!pci) + break; + + // ohci/uhci_setup call pci_config_X - don't run from irq handler. + wait_preempt(); + + if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_UHCI) + uhci_setup(pci, cntl->usb.busid + i); + else if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_OHCI) + ohci_setup(pci, cntl->usb.busid + i); + } +} + +// Check if device attached to port +static int +ehci_hub_detect(struct usbhub_s *hub, u32 port) +{ + struct usb_ehci_s *cntl = container_of(hub->cntl, struct usb_ehci_s, usb); + u32 *portreg = &cntl->regs->portsc[port]; + u32 portsc = readl(portreg); + + // Power up port. + if (!(portsc & PORT_POWER)) { + portsc |= PORT_POWER; + writel(portreg, portsc); + msleep(EHCI_TIME_POSTPOWER); + } else { + // Port is already powered up, but we don't know how long it + // has been powered up, so wait the 20ms. + msleep(EHCI_TIME_POSTPOWER); + } + portsc = readl(portreg); + + if (!(portsc & PORT_CONNECT)) + // No device present + goto doneearly; + + if ((portsc & PORT_LINESTATUS_MASK) == PORT_LINESTATUS_KSTATE) { + // low speed device + cntl->legacycount++; + writel(portreg, portsc | PORT_OWNER); + goto doneearly; + } + + // XXX - if just powered up, need to wait for USB_TIME_ATTDB? + + // Begin reset on port + portsc = (portsc & ~PORT_PE) | PORT_RESET; + writel(portreg, portsc); + msleep(USB_TIME_DRSTR); + return 0; + +doneearly: + ehci_note_port(cntl); + return -1; +} + +// Reset device on port +static int +ehci_hub_reset(struct usbhub_s *hub, u32 port) +{ + struct usb_ehci_s *cntl = container_of(hub->cntl, struct usb_ehci_s, usb); + u32 *portreg = &cntl->regs->portsc[port]; + u32 portsc = readl(portreg); + + // Finish reset on port + portsc &= ~PORT_RESET; + writel(portreg, portsc); + msleep(EHCI_TIME_POSTRESET); + + int rv = -1; + portsc = readl(portreg); + if (!(portsc & PORT_CONNECT)) + // No longer connected + goto resetfail; + if (!(portsc & PORT_PE)) { + // full speed device + cntl->legacycount++; + writel(portreg, portsc | PORT_OWNER); + goto resetfail; + } + + rv = USB_HIGHSPEED; +resetfail: + ehci_note_port(cntl); + return rv; +} + +// Disable port +static void +ehci_hub_disconnect(struct usbhub_s *hub, u32 port) +{ + struct usb_ehci_s *cntl = container_of(hub->cntl, struct usb_ehci_s, usb); + u32 *portreg = &cntl->regs->portsc[port]; + u32 portsc = readl(portreg); + writel(portreg, portsc & ~PORT_PE); +} + +static struct usbhub_op_s ehci_HubOp = { + .detect = ehci_hub_detect, + .reset = ehci_hub_reset, + .disconnect = ehci_hub_disconnect, +}; + +// Find any devices connected to the root hub. +static int +check_ehci_ports(struct usb_ehci_s *cntl) +{ + ASSERT32FLAT(); + struct usbhub_s hub; + memset(&hub, 0, sizeof(hub)); + hub.cntl = &cntl->usb; + hub.portcount = cntl->checkports; + hub.op = &ehci_HubOp; + usb_enumerate(&hub); + return hub.devcount; +} + + +/**************************************************************** + * Setup + ****************************************************************/ + +// Wait for next USB async frame to start - for ensuring safe memory release. +static void +ehci_waittick(struct usb_ehci_s *cntl) +{ + if (MODE16) { + msleep(10); + return; + } + // Wait for access to "doorbell" + barrier(); + u32 cmd, sts; + u32 end = timer_calc(100); + for (;;) { + sts = readl(&cntl->regs->usbsts); + if (!(sts & STS_IAA)) { + cmd = readl(&cntl->regs->usbcmd); + if (!(cmd & CMD_IAAD)) + break; + } + if (timer_check(end)) { + warn_timeout(); + return; + } + yield(); + } + // Ring "doorbell" + writel(&cntl->regs->usbcmd, cmd | CMD_IAAD); + // Wait for completion + for (;;) { + sts = readl(&cntl->regs->usbsts); + if (sts & STS_IAA) + break; + if (timer_check(end)) { + warn_timeout(); + return; + } + yield(); + } + // Ack completion + writel(&cntl->regs->usbsts, STS_IAA); +} + +static void +ehci_free_pipes(struct usb_ehci_s *cntl) +{ + dprintf(7, "ehci_free_pipes %p\n", cntl); + + struct ehci_qh *start = cntl->async_qh; + struct ehci_qh *pos = start; + for (;;) { + struct ehci_qh *next = (void*)(pos->next & ~EHCI_PTR_BITS); + if (next == start) + break; + struct ehci_pipe *pipe = container_of(next, struct ehci_pipe, qh); + if (pipe->pipe.cntl != &cntl->usb) + pos->next = next->next; + else + pos = next; + } + ehci_waittick(cntl); + for (;;) { + struct usb_pipe *usbpipe = cntl->usb.freelist; + if (!usbpipe) + break; + cntl->usb.freelist = usbpipe->freenext; + struct ehci_pipe *pipe = container_of(usbpipe, struct ehci_pipe, pipe); + free(pipe); + } +} + +static void +configure_ehci(void *data) +{ + struct usb_ehci_s *cntl = data; + + // Allocate ram for schedule storage + struct ehci_framelist *fl = memalign_high(sizeof(*fl), sizeof(*fl)); + struct ehci_qh *intr_qh = memalign_high(EHCI_QH_ALIGN, sizeof(*intr_qh)); + struct ehci_qh *async_qh = memalign_high(EHCI_QH_ALIGN, sizeof(*async_qh)); + if (!fl || !intr_qh || !async_qh) { + warn_noalloc(); + goto fail; + } + + // XXX - check for halted? + + // Reset the HC + u32 cmd = readl(&cntl->regs->usbcmd); + writel(&cntl->regs->usbcmd, (cmd & ~(CMD_ASE | CMD_PSE)) | CMD_HCRESET); + u32 end = timer_calc(250); + for (;;) { + cmd = readl(&cntl->regs->usbcmd); + if (!(cmd & CMD_HCRESET)) + break; + if (timer_check(end)) { + warn_timeout(); + goto fail; + } + yield(); + } + + // Disable interrupts (just to be safe). + writel(&cntl->regs->usbintr, 0); + + // Set schedule to point to primary intr queue head + memset(intr_qh, 0, sizeof(*intr_qh)); + intr_qh->next = EHCI_PTR_TERM; + intr_qh->info2 = (0x01 << QH_SMASK_SHIFT); + intr_qh->token = QTD_STS_HALT; + intr_qh->qtd_next = intr_qh->alt_next = EHCI_PTR_TERM; + int i; + for (i=0; i<ARRAY_SIZE(fl->links); i++) + fl->links[i] = (u32)intr_qh | EHCI_PTR_QH; + writel(&cntl->regs->periodiclistbase, (u32)fl); + + // Set async list to point to primary async queue head + memset(async_qh, 0, sizeof(*async_qh)); + async_qh->next = (u32)async_qh | EHCI_PTR_QH; + async_qh->info1 = QH_HEAD; + async_qh->token = QTD_STS_HALT; + async_qh->qtd_next = async_qh->alt_next = EHCI_PTR_TERM; + cntl->async_qh = async_qh; + writel(&cntl->regs->asynclistbase, (u32)async_qh); + + // Enable queues + writel(&cntl->regs->usbcmd, cmd | CMD_ASE | CMD_PSE | CMD_RUN); + + // Set default of high speed for root hub. + writel(&cntl->regs->configflag, 1); + cntl->checkports = readl(&cntl->caps->hcsparams) & HCS_N_PORTS_MASK; + + // Find devices + int count = check_ehci_ports(cntl); + ehci_free_pipes(cntl); + if (count) + // Success + return; + + // No devices found - shutdown and free controller. + writel(&cntl->regs->usbcmd, cmd & ~CMD_RUN); + msleep(4); // 2ms to stop reading memory - XXX +fail: + free(fl); + free(intr_qh); + free(async_qh); + free(cntl); +} + +int +ehci_setup(struct pci_device *pci, int busid, struct pci_device *comppci) +{ + if (! CONFIG_USB_EHCI) + return -1; + + u16 bdf = pci->bdf; + u32 baseaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0); + struct ehci_caps *caps = (void*)(baseaddr & PCI_BASE_ADDRESS_MEM_MASK); + u32 hcc_params = readl(&caps->hccparams); + + struct usb_ehci_s *cntl = malloc_tmphigh(sizeof(*cntl)); + if (!cntl) { + warn_noalloc(); + return -1; + } + memset(cntl, 0, sizeof(*cntl)); + cntl->usb.busid = busid; + cntl->usb.pci = pci; + cntl->usb.type = USB_TYPE_EHCI; + cntl->caps = caps; + cntl->regs = (void*)caps + readb(&caps->caplength); + if (hcc_params & HCC_64BIT_ADDR) + cntl->regs->ctrldssegment = 0; + + dprintf(1, "EHCI init on dev %02x:%02x.%x (regs=%p)\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf) + , pci_bdf_to_fn(bdf), cntl->regs); + + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + + // XXX - check for and disable SMM control? + + // Find companion controllers. + int count = 0; + for (;;) { + if (!comppci || comppci == pci) + break; + if (pci_classprog(comppci) == PCI_CLASS_SERIAL_USB_UHCI) + cntl->companion[count++] = comppci; + else if (pci_classprog(comppci) == PCI_CLASS_SERIAL_USB_OHCI) + cntl->companion[count++] = comppci; + comppci = container_of(comppci->node.next, struct pci_device, node); + } + + run_thread(configure_ehci, cntl); + return 0; +} + + +/**************************************************************** + * End point communication + ****************************************************************/ + +// Setup fields in qh +static void +ehci_desc2pipe(struct ehci_pipe *pipe, struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + usb_desc2pipe(&pipe->pipe, usbdev, epdesc); + + pipe->qh.info1 = ((pipe->pipe.maxpacket << QH_MAXPACKET_SHIFT) + | (pipe->pipe.speed << QH_SPEED_SHIFT) + | (pipe->pipe.ep << QH_EP_SHIFT) + | (pipe->pipe.devaddr << QH_DEVADDR_SHIFT)); + + pipe->qh.info2 = (1 << QH_MULT_SHIFT); + struct usbdevice_s *hubdev = usbdev->hub->usbdev; + if (hubdev) { + struct ehci_pipe *hpipe = container_of( + hubdev->defpipe, struct ehci_pipe, pipe); + if (hpipe->pipe.speed == USB_HIGHSPEED) + pipe->qh.info2 |= ((usbdev->port << QH_HUBPORT_SHIFT) + | (hpipe->pipe.devaddr << QH_HUBADDR_SHIFT)); + else + pipe->qh.info2 = hpipe->qh.info2; + } + + u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + if (eptype == USB_ENDPOINT_XFER_CONTROL) + pipe->qh.info1 |= ((pipe->pipe.speed != USB_HIGHSPEED ? QH_CONTROL : 0) + | QH_TOGGLECONTROL); + else if (eptype == USB_ENDPOINT_XFER_INT) + pipe->qh.info2 |= (0x01 << QH_SMASK_SHIFT) | (0x1c << QH_CMASK_SHIFT); +} + +static struct usb_pipe * +ehci_alloc_intr_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + struct usb_ehci_s *cntl = container_of( + usbdev->hub->cntl, struct usb_ehci_s, usb); + int frameexp = usb_getFrameExp(usbdev, epdesc); + dprintf(7, "ehci_alloc_intr_pipe %p %d\n", &cntl->usb, frameexp); + + if (frameexp > 10) + frameexp = 10; + int maxpacket = epdesc->wMaxPacketSize; + // Determine number of entries needed for 2 timer ticks. + int ms = 1<<frameexp; + int count = DIV_ROUND_UP(ticks_to_ms(2), ms); + struct ehci_pipe *pipe = memalign_low(EHCI_QH_ALIGN, sizeof(*pipe)); + struct ehci_qtd *tds = memalign_low(EHCI_QTD_ALIGN, sizeof(*tds) * count); + void *data = malloc_low(maxpacket * count); + if (!pipe || !tds || !data) { + warn_noalloc(); + goto fail; + } + memset(pipe, 0, sizeof(*pipe)); + ehci_desc2pipe(pipe, usbdev, epdesc); + pipe->next_td = pipe->tds = tds; + pipe->data = data; + pipe->qh.qtd_next = (u32)tds; + + int i; + for (i=0; i<count; i++) { + struct ehci_qtd *td = &tds[i]; + td->qtd_next = (i==count-1 ? (u32)tds : (u32)&td[1]); + td->alt_next = EHCI_PTR_TERM; + td->token = (ehci_explen(maxpacket) | QTD_STS_ACTIVE + | QTD_PID_IN | ehci_maxerr(3)); + td->buf[0] = (u32)data + maxpacket * i; + } + + // Add to interrupt schedule. + struct ehci_framelist *fl = (void*)readl(&cntl->regs->periodiclistbase); + if (frameexp == 0) { + // Add to existing interrupt entry. + struct ehci_qh *intr_qh = (void*)(fl->links[0] & ~EHCI_PTR_BITS); + pipe->qh.next = intr_qh->next; + barrier(); + intr_qh->next = (u32)&pipe->qh | EHCI_PTR_QH; + } else { + int startpos = 1<<(frameexp-1); + pipe->qh.next = fl->links[startpos]; + barrier(); + for (i=startpos; i<ARRAY_SIZE(fl->links); i+=ms) + fl->links[i] = (u32)&pipe->qh | EHCI_PTR_QH; + } + + return &pipe->pipe; +fail: + free(pipe); + free(tds); + free(data); + return NULL; +} + +struct usb_pipe * +ehci_alloc_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + if (! CONFIG_USB_EHCI) + return NULL; + u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + if (eptype == USB_ENDPOINT_XFER_INT) + return ehci_alloc_intr_pipe(usbdev, epdesc); + struct usb_ehci_s *cntl = container_of( + usbdev->hub->cntl, struct usb_ehci_s, usb); + dprintf(7, "ehci_alloc_async_pipe %p %d\n", &cntl->usb, eptype); + + struct usb_pipe *usbpipe = usb_getFreePipe(&cntl->usb, eptype); + if (usbpipe) { + // Use previously allocated pipe. + struct ehci_pipe *pipe = container_of(usbpipe, struct ehci_pipe, pipe); + ehci_desc2pipe(pipe, usbdev, epdesc); + return usbpipe; + } + + // Allocate a new queue head. + struct ehci_pipe *pipe; + if (eptype == USB_ENDPOINT_XFER_CONTROL) + pipe = memalign_tmphigh(EHCI_QH_ALIGN, sizeof(*pipe)); + else + pipe = memalign_low(EHCI_QH_ALIGN, sizeof(*pipe)); + if (!pipe) { + warn_noalloc(); + return NULL; + } + memset(pipe, 0, sizeof(*pipe)); + ehci_desc2pipe(pipe, usbdev, epdesc); + pipe->qh.qtd_next = pipe->qh.alt_next = EHCI_PTR_TERM; + + // Add queue head to controller list. + struct ehci_qh *async_qh = cntl->async_qh; + pipe->qh.next = async_qh->next; + barrier(); + async_qh->next = (u32)&pipe->qh | EHCI_PTR_QH; + return &pipe->pipe; +} + +static void +ehci_reset_pipe(struct ehci_pipe *pipe) +{ + SET_LOWFLAT(pipe->qh.qtd_next, EHCI_PTR_TERM); + SET_LOWFLAT(pipe->qh.alt_next, EHCI_PTR_TERM); + barrier(); + SET_LOWFLAT(pipe->qh.token, GET_LOWFLAT(pipe->qh.token) & QTD_TOGGLE); +} + +static int +ehci_wait_td(struct ehci_pipe *pipe, struct ehci_qtd *td, int timeout) +{ + u32 end = timer_calc(timeout); + u32 status; + for (;;) { + status = td->token; + if (!(status & QTD_STS_ACTIVE)) + break; + if (timer_check(end)) { + u32 cur = GET_LOWFLAT(pipe->qh.current); + u32 tok = GET_LOWFLAT(pipe->qh.token); + u32 next = GET_LOWFLAT(pipe->qh.qtd_next); + warn_timeout(); + dprintf(1, "ehci pipe=%p cur=%08x tok=%08x next=%x td=%p status=%x\n" + , pipe, cur, tok, next, td, status); + ehci_reset_pipe(pipe); + struct usb_ehci_s *cntl = container_of( + GET_LOWFLAT(pipe->pipe.cntl), struct usb_ehci_s, usb); + ehci_waittick(cntl); + return -1; + } + yield(); + } + if (status & QTD_STS_HALT) { + dprintf(1, "ehci_wait_td error - status=%x\n", status); + ehci_reset_pipe(pipe); + return -2; + } + return 0; +} + +static int +fillTDbuffer(struct ehci_qtd *td, u16 maxpacket, const void *buf, int bytes) +{ + u32 dest = (u32)buf; + u32 *pos = td->buf; + while (bytes) { + if (pos >= &td->buf[ARRAY_SIZE(td->buf)]) + // More data than can transfer in a single qtd - only use + // full packets to prevent a babble error. + return ALIGN_DOWN(dest - (u32)buf, maxpacket); + u32 count = bytes; + u32 max = 0x1000 - (dest & 0xfff); + if (count > max) + count = max; + *pos = dest; + bytes -= count; + dest += count; + pos++; + } + return dest - (u32)buf; +} + +int +ehci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize + , void *data, int datasize) +{ + ASSERT32FLAT(); + if (! CONFIG_USB_EHCI) + return -1; + dprintf(5, "ehci_control %p (dir=%d cmd=%d data=%d)\n" + , p, dir, cmdsize, datasize); + if (datasize > 4*4096 || cmdsize > 4*4096) { + // XXX - should support larger sizes. + warn_noalloc(); + return -1; + } + struct ehci_pipe *pipe = container_of(p, struct ehci_pipe, pipe); + + // Setup transfer descriptors + struct ehci_qtd *tds = memalign_tmphigh(EHCI_QTD_ALIGN, sizeof(*tds) * 3); + if (!tds) { + warn_noalloc(); + return -1; + } + memset(tds, 0, sizeof(*tds) * 3); + struct ehci_qtd *td = tds; + + td->qtd_next = (u32)&td[1]; + td->alt_next = EHCI_PTR_TERM; + td->token = (ehci_explen(cmdsize) | QTD_STS_ACTIVE + | QTD_PID_SETUP | ehci_maxerr(3)); + u16 maxpacket = pipe->pipe.maxpacket; + fillTDbuffer(td, maxpacket, cmd, cmdsize); + td++; + + if (datasize) { + td->qtd_next = (u32)&td[1]; + td->alt_next = EHCI_PTR_TERM; + td->token = (QTD_TOGGLE | ehci_explen(datasize) | QTD_STS_ACTIVE + | (dir ? QTD_PID_IN : QTD_PID_OUT) | ehci_maxerr(3)); + fillTDbuffer(td, maxpacket, data, datasize); + td++; + } + + td->qtd_next = EHCI_PTR_TERM; + td->alt_next = EHCI_PTR_TERM; + td->token = (QTD_TOGGLE | QTD_STS_ACTIVE + | (dir ? QTD_PID_OUT : QTD_PID_IN) | ehci_maxerr(3)); + + // Transfer data + barrier(); + pipe->qh.qtd_next = (u32)tds; + int i, ret=0; + for (i=0; i<3; i++) { + struct ehci_qtd *td = &tds[i]; + ret = ehci_wait_td(pipe, td, 500); + if (ret) + break; + } + free(tds); + return ret; +} + +#define STACKQTDS 4 + +int +ehci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize) +{ + if (! CONFIG_USB_EHCI) + return -1; + struct ehci_pipe *pipe = container_of(p, struct ehci_pipe, pipe); + dprintf(7, "ehci_send_bulk qh=%p dir=%d data=%p size=%d\n" + , &pipe->qh, dir, data, datasize); + + // Allocate 4 tds on stack (with required alignment) + u8 tdsbuf[sizeof(struct ehci_qtd) * STACKQTDS + EHCI_QTD_ALIGN - 1]; + struct ehci_qtd *tds = (void*)ALIGN((u32)tdsbuf, EHCI_QTD_ALIGN); + memset(tds, 0, sizeof(*tds) * STACKQTDS); + barrier(); + SET_LOWFLAT(pipe->qh.qtd_next, (u32)MAKE_FLATPTR(GET_SEG(SS), tds)); + + u16 maxpacket = GET_LOWFLAT(pipe->pipe.maxpacket); + int tdpos = 0; + while (datasize) { + struct ehci_qtd *td = &tds[tdpos++ % STACKQTDS]; + int ret = ehci_wait_td(pipe, td, 5000); + if (ret) + return -1; + + struct ehci_qtd *nexttd_fl = MAKE_FLATPTR(GET_SEG(SS) + , &tds[tdpos % STACKQTDS]); + + int transfer = fillTDbuffer(td, maxpacket, data, datasize); + td->qtd_next = (transfer==datasize ? EHCI_PTR_TERM : (u32)nexttd_fl); + td->alt_next = EHCI_PTR_TERM; + barrier(); + td->token = (ehci_explen(transfer) | QTD_STS_ACTIVE + | (dir ? QTD_PID_IN : QTD_PID_OUT) | ehci_maxerr(3)); + + data += transfer; + datasize -= transfer; + } + int i; + for (i=0; i<STACKQTDS; i++) { + struct ehci_qtd *td = &tds[tdpos++ % STACKQTDS]; + int ret = ehci_wait_td(pipe, td, 5000); + if (ret) + return -1; + } + + return 0; +} + +int +ehci_poll_intr(struct usb_pipe *p, void *data) +{ + ASSERT16(); + if (! CONFIG_USB_EHCI) + return -1; + struct ehci_pipe *pipe = container_of(p, struct ehci_pipe, pipe); + struct ehci_qtd *td = GET_LOWFLAT(pipe->next_td); + u32 token = GET_LOWFLAT(td->token); + if (token & QTD_STS_ACTIVE) + // No intrs found. + return -1; + // XXX - check for errors. + + // Copy data. + int maxpacket = GET_LOWFLAT(pipe->pipe.maxpacket); + int pos = td - GET_LOWFLAT(pipe->tds); + void *tddata = GET_LOWFLAT(pipe->data) + maxpacket * pos; + memcpy_far(GET_SEG(SS), data, SEG_LOW, LOWFLAT2LOW(tddata), maxpacket); + + // Reenable this td. + struct ehci_qtd *next = (void*)(GET_LOWFLAT(td->qtd_next) & ~EHCI_PTR_BITS); + SET_LOWFLAT(pipe->next_td, next); + SET_LOWFLAT(td->buf[0], (u32)tddata); + barrier(); + SET_LOWFLAT(td->token, (ehci_explen(maxpacket) | QTD_STS_ACTIVE + | QTD_PID_IN | ehci_maxerr(3))); + + return 0; +} diff --git a/roms/seabios/src/hw/usb-ehci.h b/roms/seabios/src/hw/usb-ehci.h new file mode 100644 index 000000000..5672033a0 --- /dev/null +++ b/roms/seabios/src/hw/usb-ehci.h @@ -0,0 +1,176 @@ +#ifndef __USB_EHCI_H +#define __USB_EHCI_H + +// usb-ehci.c +int ehci_setup(struct pci_device *pci, int busid, struct pci_device *comppci); +struct usbdevice_s; +struct usb_endpoint_descriptor; +struct usb_pipe *ehci_alloc_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc); +struct usb_pipe; +int ehci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize + , void *data, int datasize); +int ehci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize); +int ehci_poll_intr(struct usb_pipe *p, void *data); + + +/**************************************************************** + * ehci structs and flags + ****************************************************************/ + +struct ehci_caps { + u8 caplength; + u8 reserved_01; + u16 hciversion; + u32 hcsparams; + u32 hccparams; + u64 portroute; +} PACKED; + +#define HCC_64BIT_ADDR 1 + +#define HCS_N_PORTS_MASK 0xf + +struct ehci_regs { + u32 usbcmd; + u32 usbsts; + u32 usbintr; + u32 frindex; + u32 ctrldssegment; + u32 periodiclistbase; + u32 asynclistbase; + u32 reserved[9]; + u32 configflag; + u32 portsc[0]; +} PACKED; + +#define CMD_PARK (1<<11) +#define CMD_PARK_CNT(c) (((c)>>8)&3) +#define CMD_LRESET (1<<7) +#define CMD_IAAD (1<<6) +#define CMD_ASE (1<<5) +#define CMD_PSE (1<<4) +#define CMD_HCRESET (1<<1) +#define CMD_RUN (1<<0) + +#define STS_ASS (1<<15) +#define STS_PSS (1<<14) +#define STS_RECL (1<<13) +#define STS_HALT (1<<12) +#define STS_IAA (1<<5) +#define STS_FATAL (1<<4) +#define STS_FLR (1<<3) +#define STS_PCD (1<<2) +#define STS_ERR (1<<1) +#define STS_INT (1<<0) + +#define FLAG_CF (1<<0) + +#define PORT_WKOC_E (1<<22) +#define PORT_WKDISC_E (1<<21) +#define PORT_WKCONN_E (1<<20) +#define PORT_TEST_PKT (0x4<<16) +#define PORT_LED_OFF (0<<14) +#define PORT_LED_AMBER (1<<14) +#define PORT_LED_GREEN (2<<14) +#define PORT_LED_MASK (3<<14) +#define PORT_OWNER (1<<13) +#define PORT_POWER (1<<12) +#define PORT_LINESTATUS_MASK (3<<10) +#define PORT_LINESTATUS_KSTATE (1<<10) +#define PORT_RESET (1<<8) +#define PORT_SUSPEND (1<<7) +#define PORT_RESUME (1<<6) +#define PORT_OCC (1<<5) +#define PORT_OC (1<<4) +#define PORT_PEC (1<<3) +#define PORT_PE (1<<2) +#define PORT_CSC (1<<1) +#define PORT_CONNECT (1<<0) +#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) + + +#define EHCI_QH_ALIGN 128 // Can't span a 4K boundary, so increase from 32 + +struct ehci_qh { + u32 next; + u32 info1; + u32 info2; + u32 current; + + u32 qtd_next; + u32 alt_next; + u32 token; + u32 buf[5]; + u32 buf_hi[5]; +} PACKED; + +#define QH_CONTROL (1 << 27) +#define QH_MAXPACKET_SHIFT 16 +#define QH_MAXPACKET_MASK (0x7ff << QH_MAXPACKET_SHIFT) +#define QH_HEAD (1 << 15) +#define QH_TOGGLECONTROL (1 << 14) +#define QH_SPEED_SHIFT 12 +#define QH_SPEED_MASK (0x3 << QH_SPEED_SHIFT) +#define QH_EP_SHIFT 8 +#define QH_EP_MASK (0xf << QH_EP_SHIFT) +#define QH_DEVADDR_SHIFT 0 +#define QH_DEVADDR_MASK (0x7f << QH_DEVADDR_SHIFT) + +#define QH_SMASK_SHIFT 0 +#define QH_SMASK_MASK (0xff << QH_SMASK_SHIFT) +#define QH_CMASK_SHIFT 8 +#define QH_CMASK_MASK (0xff << QH_CMASK_SHIFT) +#define QH_HUBADDR_SHIFT 16 +#define QH_HUBADDR_MASK (0x7f << QH_HUBADDR_SHIFT) +#define QH_HUBPORT_SHIFT 23 +#define QH_HUBPORT_MASK (0x7f << QH_HUBPORT_SHIFT) +#define QH_MULT_SHIFT 30 +#define QH_MULT_MASK (0x3 << QH_MULT_SHIFT) + +#define EHCI_PTR_BITS 0x001F +#define EHCI_PTR_TERM 0x0001 +#define EHCI_PTR_QH 0x0002 + + +#define EHCI_QTD_ALIGN 64 // Can't span a 4K boundary, so increase from 32 + +struct ehci_qtd { + u32 qtd_next; + u32 alt_next; + u32 token; + u32 buf[5]; + u32 buf_hi[5]; + /* keep struct size a multiple of 64 bytes, as we're allocating + arrays. Without this padding, the second qtd could have the + wrong alignment. */ +} PACKED __aligned(EHCI_QTD_ALIGN); + +#define QTD_TOGGLE (1 << 31) +#define QTD_LENGTH_SHIFT 16 +#define QTD_LENGTH_MASK (0x7fff << QTD_LENGTH_SHIFT) +#define QTD_CERR_SHIFT 10 +#define QTD_CERR_MASK (0x3 << QTD_CERR_SHIFT) +#define QTD_IOC (1 << 15) +#define QTD_PID_OUT (0x0 << 8) +#define QTD_PID_IN (0x1 << 8) +#define QTD_PID_SETUP (0x2 << 8) +#define QTD_STS_ACTIVE (1 << 7) +#define QTD_STS_HALT (1 << 6) +#define QTD_STS_DBE (1 << 5) +#define QTD_STS_BABBLE (1 << 4) +#define QTD_STS_XACT (1 << 3) +#define QTD_STS_MMF (1 << 2) +#define QTD_STS_STS (1 << 1) +#define QTD_STS_PING (1 << 0) + +#define ehci_explen(len) (((len) << QTD_LENGTH_SHIFT) & QTD_LENGTH_MASK) + +#define ehci_maxerr(err) (((err) << QTD_CERR_SHIFT) & QTD_CERR_MASK) + + +struct ehci_framelist { + u32 links[1024]; +} PACKED; + +#endif // usb-ehci.h diff --git a/roms/seabios/src/hw/usb-hid.c b/roms/seabios/src/hw/usb-hid.c new file mode 100644 index 000000000..e94aa1679 --- /dev/null +++ b/roms/seabios/src/hw/usb-hid.c @@ -0,0 +1,432 @@ +// Code for handling USB Human Interface Devices (HID). +// +// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "config.h" // CONFIG_* +#include "output.h" // dprintf +#include "ps2port.h" // ATKBD_CMD_GETID +#include "usb.h" // usb_ctrlrequest +#include "usb-hid.h" // usb_keyboard_setup +#include "util.h" // process_key + +struct usb_pipe *keyboard_pipe VARFSEG; +struct usb_pipe *mouse_pipe VARFSEG; + + +/**************************************************************** + * Setup + ****************************************************************/ + +// Send USB HID protocol message. +static int +set_protocol(struct usb_pipe *pipe, u16 val) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + req.bRequest = HID_REQ_SET_PROTOCOL; + req.wValue = val; + req.wIndex = 0; + req.wLength = 0; + return send_default_control(pipe, &req, NULL); +} + +// Send USB HID SetIdle request. +static int +set_idle(struct usb_pipe *pipe, int ms) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + req.bRequest = HID_REQ_SET_IDLE; + req.wValue = (ms/4)<<8; + req.wIndex = 0; + req.wLength = 0; + return send_default_control(pipe, &req, NULL); +} + +#define KEYREPEATWAITMS 500 +#define KEYREPEATMS 33 + +static int +usb_kbd_setup(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + if (! CONFIG_USB_KEYBOARD) + return -1; + if (keyboard_pipe) + // XXX - this enables the first found keyboard (could be random) + return -1; + + if (epdesc->wMaxPacketSize != 8) + return -1; + + // Enable "boot" protocol. + int ret = set_protocol(usbdev->defpipe, 0); + if (ret) + return -1; + // Periodically send reports to enable key repeat. + ret = set_idle(usbdev->defpipe, KEYREPEATMS); + if (ret) + return -1; + + keyboard_pipe = usb_alloc_pipe(usbdev, epdesc); + if (!keyboard_pipe) + return -1; + + dprintf(1, "USB keyboard initialized\n"); + return 0; +} + +static int +usb_mouse_setup(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + if (! CONFIG_USB_MOUSE) + return -1; + if (mouse_pipe) + // XXX - this enables the first found mouse (could be random) + return -1; + + if (epdesc->wMaxPacketSize < 3 || epdesc->wMaxPacketSize > 8) + return -1; + + // Enable "boot" protocol. + int ret = set_protocol(usbdev->defpipe, 0); + if (ret) + return -1; + + mouse_pipe = usb_alloc_pipe(usbdev, epdesc); + if (!mouse_pipe) + return -1; + + dprintf(1, "USB mouse initialized\n"); + return 0; +} + +// Initialize a found USB HID device (if applicable). +int +usb_hid_setup(struct usbdevice_s *usbdev) +{ + if (! CONFIG_USB_KEYBOARD || ! CONFIG_USB_MOUSE) + return -1; + dprintf(2, "usb_hid_setup %p\n", usbdev->defpipe); + + struct usb_interface_descriptor *iface = usbdev->iface; + if (iface->bInterfaceSubClass != USB_INTERFACE_SUBCLASS_BOOT) + // Doesn't support boot protocol. + return -1; + + // Find intr in endpoint. + struct usb_endpoint_descriptor *epdesc = findEndPointDesc( + usbdev, USB_ENDPOINT_XFER_INT, USB_DIR_IN); + if (!epdesc) { + dprintf(1, "No usb hid intr in?\n"); + return -1; + } + + if (iface->bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD) + return usb_kbd_setup(usbdev, epdesc); + if (iface->bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE) + return usb_mouse_setup(usbdev, epdesc); + return -1; +} + + +/**************************************************************** + * Keyboard events + ****************************************************************/ + +// Mapping from USB key id to ps2 key sequence. +static u16 KeyToScanCode[] VAR16 = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x001e, 0x0030, 0x002e, 0x0020, + 0x0012, 0x0021, 0x0022, 0x0023, 0x0017, 0x0024, 0x0025, 0x0026, + 0x0032, 0x0031, 0x0018, 0x0019, 0x0010, 0x0013, 0x001f, 0x0014, + 0x0016, 0x002f, 0x0011, 0x002d, 0x0015, 0x002c, 0x0002, 0x0003, + 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, + 0x001c, 0x0001, 0x000e, 0x000f, 0x0039, 0x000c, 0x000d, 0x001a, + 0x001b, 0x002b, 0x0000, 0x0027, 0x0028, 0x0029, 0x0033, 0x0034, + 0x0035, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, + 0x0041, 0x0042, 0x0043, 0x0044, 0x0057, 0x0058, 0xe037, 0x0046, + 0xe11d, 0xe052, 0xe047, 0xe049, 0xe053, 0xe04f, 0xe051, 0xe04d, + 0xe04b, 0xe050, 0xe048, 0x0045, 0xe035, 0x0037, 0x004a, 0x004e, + 0xe01c, 0x004f, 0x0050, 0x0051, 0x004b, 0x004c, 0x004d, 0x0047, + 0x0048, 0x0049, 0x0052, 0x0053 +}; + +// Mapping from USB modifier id to ps2 key sequence. +static u16 ModifierToScanCode[] VAR16 = { + //lcntl, lshift, lalt, lgui, rcntl, rshift, ralt, rgui + 0x001d, 0x002a, 0x0038, 0xe05b, 0xe01d, 0x0036, 0xe038, 0xe05c +}; + +#define RELEASEBIT 0x80 + +// Format of USB keyboard event data +struct keyevent { + u8 modifiers; + u8 reserved; + u8 keys[6]; +}; + +// Translate data from KeyToScanCode[] to calls to process_key(). +static void +prockeys(u16 keys) +{ + if (keys > 0xff) { + u8 key = keys>>8; + if (key == 0xe1) { + // Pause key + process_key(0xe1); + process_key(0x1d | (keys & RELEASEBIT)); + process_key(0x45 | (keys & RELEASEBIT)); + return; + } + process_key(key); + } + process_key(keys); +} + +// Handle a USB key press/release event. +static void +procscankey(u8 key, u8 flags) +{ + if (key >= ARRAY_SIZE(KeyToScanCode)) + return; + u16 keys = GET_GLOBAL(KeyToScanCode[key]); + if (keys) + prockeys(keys | flags); +} + +// Handle a USB modifier press/release event. +static void +procmodkey(u8 mods, u8 flags) +{ + int i; + for (i=0; mods; i++) + if (mods & (1<<i)) { + // Modifier key change. + prockeys(GET_GLOBAL(ModifierToScanCode[i]) | flags); + mods &= ~(1<<i); + } +} + +struct usbkeyinfo { + union { + struct { + u8 modifiers; + u8 repeatcount; + u8 keys[6]; + }; + u64 data; + }; +}; +struct usbkeyinfo LastUSBkey VARLOW; + +// Process USB keyboard data. +static void +handle_key(struct keyevent *data) +{ + dprintf(9, "Got key %x %x\n", data->modifiers, data->keys[0]); + + // Load old keys. + struct usbkeyinfo old; + old.data = GET_LOW(LastUSBkey.data); + + // Check for keys no longer pressed. + int addpos = 0; + int i; + for (i=0; i<ARRAY_SIZE(old.keys); i++) { + u8 key = old.keys[i]; + if (!key) + break; + int j; + for (j=0;; j++) { + if (j>=ARRAY_SIZE(data->keys)) { + // Key released. + procscankey(key, RELEASEBIT); + if (i+1 >= ARRAY_SIZE(old.keys) || !old.keys[i+1]) + // Last pressed key released - disable repeat. + old.repeatcount = 0xff; + break; + } + if (data->keys[j] == key) { + // Key still pressed. + data->keys[j] = 0; + old.keys[addpos++] = key; + break; + } + } + } + procmodkey(old.modifiers & ~data->modifiers, RELEASEBIT); + + // Process new keys + procmodkey(data->modifiers & ~old.modifiers, 0); + old.modifiers = data->modifiers; + for (i=0; i<ARRAY_SIZE(data->keys); i++) { + u8 key = data->keys[i]; + if (!key) + continue; + // New key pressed. + procscankey(key, 0); + old.keys[addpos++] = key; + old.repeatcount = KEYREPEATWAITMS / KEYREPEATMS + 1; + } + if (addpos < ARRAY_SIZE(old.keys)) + old.keys[addpos] = 0; + + // Check for key repeat event. + if (addpos) { + if (!old.repeatcount) + procscankey(old.keys[addpos-1], 0); + else if (old.repeatcount != 0xff) + old.repeatcount--; + } + + // Update old keys + SET_LOW(LastUSBkey.data, old.data); +} + +// Check if a USB keyboard event is pending and process it if so. +static void +usb_check_key(void) +{ + if (! CONFIG_USB_KEYBOARD) + return; + struct usb_pipe *pipe = GET_GLOBAL(keyboard_pipe); + if (!pipe) + return; + + for (;;) { + struct keyevent data; + int ret = usb_poll_intr(pipe, &data); + if (ret) + break; + handle_key(&data); + } +} + +// Test if USB keyboard is active. +inline int +usb_kbd_active(void) +{ + if (! CONFIG_USB_KEYBOARD) + return 0; + return GET_GLOBAL(keyboard_pipe) != NULL; +} + +// Handle a ps2 style keyboard command. +inline int +usb_kbd_command(int command, u8 *param) +{ + if (! CONFIG_USB_KEYBOARD) + return -1; + dprintf(9, "usb keyboard cmd=%x\n", command); + switch (command) { + case ATKBD_CMD_GETID: + // Return the id of a standard AT keyboard. + param[0] = 0xab; + param[1] = 0x83; + return 0; + default: + return -1; + } +} + + +/**************************************************************** + * Mouse events + ****************************************************************/ + +// Format of USB mouse event data +struct mouseevent { + u8 buttons; + u8 x, y; + u8 reserved[5]; +}; + +// Process USB mouse data. +static void +handle_mouse(struct mouseevent *data) +{ + dprintf(9, "Got mouse b=%x x=%x y=%x\n", data->buttons, data->x, data->y); + + s8 x = data->x, y = -data->y; + u8 flag = ((data->buttons & 0x7) | (1<<3) + | (x & 0x80 ? (1<<4) : 0) | (y & 0x80 ? (1<<5) : 0)); + process_mouse(flag); + process_mouse(x); + process_mouse(y); +} + +// Check if a USB mouse event is pending and process it if so. +static void +usb_check_mouse(void) +{ + if (! CONFIG_USB_MOUSE) + return; + struct usb_pipe *pipe = GET_GLOBAL(mouse_pipe); + if (!pipe) + return; + + for (;;) { + struct mouseevent data; + int ret = usb_poll_intr(pipe, &data); + if (ret) + break; + handle_mouse(&data); + } +} + +// Test if USB mouse is active. +inline int +usb_mouse_active(void) +{ + if (! CONFIG_USB_MOUSE) + return 0; + return GET_GLOBAL(mouse_pipe) != NULL; +} + +// Handle a ps2 style mouse command. +inline int +usb_mouse_command(int command, u8 *param) +{ + if (! CONFIG_USB_MOUSE) + return -1; + dprintf(9, "usb mouse cmd=%x\n", command); + switch (command) { + case PSMOUSE_CMD_ENABLE: + case PSMOUSE_CMD_DISABLE: + case PSMOUSE_CMD_SETSCALE11: + return 0; + case PSMOUSE_CMD_SETSCALE21: + case PSMOUSE_CMD_SETRATE: + case PSMOUSE_CMD_SETRES: + // XXX + return 0; + case PSMOUSE_CMD_RESET_BAT: + case PSMOUSE_CMD_GETID: + // Return the id of a standard AT mouse. + param[0] = 0xaa; + param[1] = 0x00; + return 0; + + case PSMOUSE_CMD_GETINFO: + param[0] = 0x00; + param[1] = 4; + param[2] = 100; + return 0; + + default: + return -1; + } +} + +// Check for USB events pending - called periodically from timer interrupt. +void +usb_check_event(void) +{ + usb_check_key(); + usb_check_mouse(); +} diff --git a/roms/seabios/src/hw/usb-hid.h b/roms/seabios/src/hw/usb-hid.h new file mode 100644 index 000000000..ef34e7963 --- /dev/null +++ b/roms/seabios/src/hw/usb-hid.h @@ -0,0 +1,29 @@ +#ifndef __USB_HID_H +#define __USB_HID_H + +// usb-hid.c +struct usbdevice_s; +int usb_hid_setup(struct usbdevice_s *usbdev); +inline int usb_kbd_active(void); +inline int usb_kbd_command(int command, u8 *param); +inline int usb_mouse_active(void); +inline int usb_mouse_command(int command, u8 *param); +void usb_check_event(void); + + +/**************************************************************** + * hid flags + ****************************************************************/ + +#define USB_INTERFACE_SUBCLASS_BOOT 1 +#define USB_INTERFACE_PROTOCOL_KEYBOARD 1 +#define USB_INTERFACE_PROTOCOL_MOUSE 2 + +#define HID_REQ_GET_REPORT 0x01 +#define HID_REQ_GET_IDLE 0x02 +#define HID_REQ_GET_PROTOCOL 0x03 +#define HID_REQ_SET_REPORT 0x09 +#define HID_REQ_SET_IDLE 0x0A +#define HID_REQ_SET_PROTOCOL 0x0B + +#endif // ush-hid.h diff --git a/roms/seabios/src/hw/usb-hub.c b/roms/seabios/src/hw/usb-hub.c new file mode 100644 index 000000000..477518b33 --- /dev/null +++ b/roms/seabios/src/hw/usb-hub.c @@ -0,0 +1,187 @@ +// Code for handling standard USB hubs. +// +// Copyright (C) 2010 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_USB_HUB +#include "output.h" // dprintf +#include "string.h" // memset +#include "usb.h" // struct usb_s +#include "usb-hub.h" // struct usb_hub_descriptor +#include "util.h" // timer_calc + +static int +get_hub_desc(struct usb_pipe *pipe, struct usb_hub_descriptor *desc) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE; + req.bRequest = USB_REQ_GET_DESCRIPTOR; + req.wValue = USB_DT_HUB<<8; + req.wIndex = 0; + req.wLength = sizeof(*desc); + return send_default_control(pipe, &req, desc); +} + +static int +set_port_feature(struct usbhub_s *hub, int port, int feature) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER; + req.bRequest = USB_REQ_SET_FEATURE; + req.wValue = feature; + req.wIndex = port + 1; + req.wLength = 0; + mutex_lock(&hub->lock); + int ret = send_default_control(hub->usbdev->defpipe, &req, NULL); + mutex_unlock(&hub->lock); + return ret; +} + +static int +clear_port_feature(struct usbhub_s *hub, int port, int feature) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_OTHER; + req.bRequest = USB_REQ_CLEAR_FEATURE; + req.wValue = feature; + req.wIndex = port + 1; + req.wLength = 0; + mutex_lock(&hub->lock); + int ret = send_default_control(hub->usbdev->defpipe, &req, NULL); + mutex_unlock(&hub->lock); + return ret; +} + +static int +get_port_status(struct usbhub_s *hub, int port, struct usb_port_status *sts) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_OTHER; + req.bRequest = USB_REQ_GET_STATUS; + req.wValue = 0; + req.wIndex = port + 1; + req.wLength = sizeof(*sts); + mutex_lock(&hub->lock); + int ret = send_default_control(hub->usbdev->defpipe, &req, sts); + mutex_unlock(&hub->lock); + return ret; +} + +// Check if device attached to port +static int +usb_hub_detect(struct usbhub_s *hub, u32 port) +{ + // Turn on power to port. + int ret = set_port_feature(hub, port, USB_PORT_FEAT_POWER); + if (ret) + goto fail; + + // Wait for port power to stabilize. + msleep(hub->powerwait); + + // Check periodically for a device connect. + struct usb_port_status sts; + u32 end = timer_calc(USB_TIME_SIGATT); + for (;;) { + ret = get_port_status(hub, port, &sts); + if (ret) + goto fail; + if (sts.wPortStatus & USB_PORT_STAT_CONNECTION) + // Device connected. + break; + if (timer_check(end)) + // No device found. + return -1; + msleep(5); + } + + // XXX - wait USB_TIME_ATTDB time? + + return 0; + +fail: + dprintf(1, "Failure on hub port %d detect\n", port); + return -1; +} + +// Disable port +static void +usb_hub_disconnect(struct usbhub_s *hub, u32 port) +{ + int ret = clear_port_feature(hub, port, USB_PORT_FEAT_ENABLE); + if (ret) + dprintf(1, "Failure on hub port %d disconnect\n", port); +} + +// Reset device on port +static int +usb_hub_reset(struct usbhub_s *hub, u32 port) +{ + int ret = set_port_feature(hub, port, USB_PORT_FEAT_RESET); + if (ret) + goto fail; + + // Wait for reset to complete. + struct usb_port_status sts; + u32 end = timer_calc(USB_TIME_DRST * 2); + for (;;) { + ret = get_port_status(hub, port, &sts); + if (ret) + goto fail; + if (!(sts.wPortStatus & USB_PORT_STAT_RESET)) + break; + if (timer_check(end)) { + warn_timeout(); + goto fail; + } + msleep(5); + } + + // Reset complete. + if (!(sts.wPortStatus & USB_PORT_STAT_CONNECTION)) + // Device no longer present + return -1; + + return ((sts.wPortStatus & USB_PORT_STAT_SPEED_MASK) + >> USB_PORT_STAT_SPEED_SHIFT); + +fail: + dprintf(1, "Failure on hub port %d reset\n", port); + usb_hub_disconnect(hub, port); + return -1; +} + +static struct usbhub_op_s HubOp = { + .detect = usb_hub_detect, + .reset = usb_hub_reset, + .disconnect = usb_hub_disconnect, +}; + +// Configure a usb hub and then find devices connected to it. +int +usb_hub_setup(struct usbdevice_s *usbdev) +{ + ASSERT32FLAT(); + if (!CONFIG_USB_HUB) + return -1; + + struct usb_hub_descriptor desc; + int ret = get_hub_desc(usbdev->defpipe, &desc); + if (ret) + return ret; + + struct usbhub_s hub; + memset(&hub, 0, sizeof(hub)); + hub.usbdev = usbdev; + hub.cntl = usbdev->defpipe->cntl; + hub.powerwait = desc.bPwrOn2PwrGood * 2; + hub.portcount = desc.bNbrPorts; + hub.op = &HubOp; + usb_enumerate(&hub); + + dprintf(1, "Initialized USB HUB (%d ports used)\n", hub.devcount); + if (hub.devcount) + return 0; + return -1; +} diff --git a/roms/seabios/src/hw/usb-hub.h b/roms/seabios/src/hw/usb-hub.h new file mode 100644 index 000000000..5b099474d --- /dev/null +++ b/roms/seabios/src/hw/usb-hub.h @@ -0,0 +1,60 @@ +#ifndef __USB_HUB_H +#define __USB_HUB_H + +// usb-hub.c +struct usbdevice_s; +int usb_hub_setup(struct usbdevice_s *usbdev); + + +/**************************************************************** + * hub flags + ****************************************************************/ + +#define USB_DT_HUB (USB_TYPE_CLASS | 0x09) + +struct usb_hub_descriptor { + u8 bDescLength; + u8 bDescriptorType; + u8 bNbrPorts; + u16 wHubCharacteristics; + u8 bPwrOn2PwrGood; + u8 bHubContrCurrent; + // Variable length fields for DeviceRemovable[], PortPwrCtrlMask[] follow. +} PACKED; + +#define USB_PORT_FEAT_CONNECTION 0 +#define USB_PORT_FEAT_ENABLE 1 +#define USB_PORT_FEAT_SUSPEND 2 +#define USB_PORT_FEAT_OVER_CURRENT 3 +#define USB_PORT_FEAT_RESET 4 +#define USB_PORT_FEAT_POWER 8 +#define USB_PORT_FEAT_LOWSPEED 9 +#define USB_PORT_FEAT_C_CONNECTION 16 +#define USB_PORT_FEAT_C_ENABLE 17 +#define USB_PORT_FEAT_C_SUSPEND 18 +#define USB_PORT_FEAT_C_OVER_CURRENT 19 +#define USB_PORT_FEAT_C_RESET 20 +#define USB_PORT_FEAT_TEST 21 +#define USB_PORT_FEAT_INDICATOR 22 +#define USB_PORT_FEAT_C_PORT_L1 23 + +struct usb_port_status { + u16 wPortStatus; + u16 wPortChange; +} PACKED; + +#define USB_PORT_STAT_CONNECTION 0x0001 +#define USB_PORT_STAT_ENABLE 0x0002 +#define USB_PORT_STAT_SUSPEND 0x0004 +#define USB_PORT_STAT_OVERCURRENT 0x0008 +#define USB_PORT_STAT_RESET 0x0010 +#define USB_PORT_STAT_L1 0x0020 +#define USB_PORT_STAT_POWER 0x0100 +#define USB_PORT_STAT_SPEED_SHIFT 9 +#define USB_PORT_STAT_SPEED_MASK (0x3 << USB_PORT_STAT_SPEED_SHIFT) +#define USB_PORT_STAT_LOW_SPEED 0x0200 +#define USB_PORT_STAT_HIGH_SPEED 0x0400 +#define USB_PORT_STAT_TEST 0x0800 +#define USB_PORT_STAT_INDICATOR 0x1000 + +#endif // ush-hid.h diff --git a/roms/seabios/src/hw/usb-msc.c b/roms/seabios/src/hw/usb-msc.c new file mode 100644 index 000000000..7e2e440a2 --- /dev/null +++ b/roms/seabios/src/hw/usb-msc.c @@ -0,0 +1,218 @@ +// Code for handling USB Mass Storage Controller devices. +// +// Copyright (C) 2010 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // DTYPE_USB +#include "blockcmd.h" // cdb_read +#include "config.h" // CONFIG_USB_MSC +#include "malloc.h" // free +#include "output.h" // dprintf +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "usb.h" // struct usb_s +#include "usb-msc.h" // usb_msc_setup +#include "util.h" // bootprio_find_usb + +struct usbdrive_s { + struct drive_s drive; + struct usb_pipe *bulkin, *bulkout; + int lun; +}; + + +/**************************************************************** + * Bulk-only drive command processing + ****************************************************************/ + +#define USB_CDB_SIZE 12 + +#define CBW_SIGNATURE 0x43425355 // USBC + +struct cbw_s { + u32 dCBWSignature; + u32 dCBWTag; + u32 dCBWDataTransferLength; + u8 bmCBWFlags; + u8 bCBWLUN; + u8 bCBWCBLength; + u8 CBWCB[16]; +} PACKED; + +#define CSW_SIGNATURE 0x53425355 // USBS + +struct csw_s { + u32 dCSWSignature; + u32 dCSWTag; + u32 dCSWDataResidue; + u8 bCSWStatus; +} PACKED; + +static int +usb_msc_send(struct usbdrive_s *udrive_gf, int dir, void *buf, u32 bytes) +{ + struct usb_pipe *pipe; + if (dir == USB_DIR_OUT) + pipe = GET_GLOBALFLAT(udrive_gf->bulkout); + else + pipe = GET_GLOBALFLAT(udrive_gf->bulkin); + return usb_send_bulk(pipe, dir, buf, bytes); +} + +// Low-level usb command transmit function. +int +usb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + if (!CONFIG_USB_MSC) + return 0; + + dprintf(16, "usb_cmd_data id=%p write=%d count=%d bs=%d buf=%p\n" + , op->drive_gf, 0, op->count, blocksize, op->buf_fl); + struct usbdrive_s *udrive_gf = container_of( + op->drive_gf, struct usbdrive_s, drive); + + // Setup command block wrapper. + u32 bytes = blocksize * op->count; + struct cbw_s cbw; + memset(&cbw, 0, sizeof(cbw)); + memcpy(cbw.CBWCB, cdbcmd, USB_CDB_SIZE); + cbw.dCBWSignature = CBW_SIGNATURE; + cbw.dCBWTag = 999; // XXX + cbw.dCBWDataTransferLength = bytes; + cbw.bmCBWFlags = cdb_is_read(cdbcmd, blocksize) ? USB_DIR_IN : USB_DIR_OUT; + cbw.bCBWLUN = GET_GLOBALFLAT(udrive_gf->lun); + cbw.bCBWCBLength = USB_CDB_SIZE; + + // Transfer cbw to device. + int ret = usb_msc_send(udrive_gf, USB_DIR_OUT + , MAKE_FLATPTR(GET_SEG(SS), &cbw), sizeof(cbw)); + if (ret) + goto fail; + + // Transfer data to/from device. + if (bytes) { + ret = usb_msc_send(udrive_gf, cbw.bmCBWFlags, op->buf_fl, bytes); + if (ret) + goto fail; + } + + // Transfer csw info. + struct csw_s csw; + ret = usb_msc_send(udrive_gf, USB_DIR_IN + , MAKE_FLATPTR(GET_SEG(SS), &csw), sizeof(csw)); + if (ret) + goto fail; + + if (!csw.bCSWStatus) + return DISK_RET_SUCCESS; + if (csw.bCSWStatus == 2) + goto fail; + + if (blocksize) + op->count -= csw.dCSWDataResidue / blocksize; + return DISK_RET_EBADTRACK; + +fail: + // XXX - reset connection + dprintf(1, "USB transmission failed\n"); + op->count = 0; + return DISK_RET_EBADTRACK; +} + +static int +usb_msc_maxlun(struct usb_pipe *pipe) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + req.bRequest = 0xfe; + req.wValue = 0; + req.wIndex = 0; + req.wLength = 1; + unsigned char maxlun; + int ret = send_default_control(pipe, &req, &maxlun); + if (ret) + return 0; + return maxlun; +} + +static int +usb_msc_lun_setup(struct usb_pipe *inpipe, struct usb_pipe *outpipe, + struct usbdevice_s *usbdev, int lun) +{ + // Allocate drive structure. + struct usbdrive_s *drive = malloc_fseg(sizeof(*drive)); + if (!drive) { + warn_noalloc(); + return -1; + } + memset(drive, 0, sizeof(*drive)); + drive->drive.type = DTYPE_USB; + drive->bulkin = inpipe; + drive->bulkout = outpipe; + drive->lun = lun; + + int prio = bootprio_find_usb(usbdev, lun); + int ret = scsi_drive_setup(&drive->drive, "USB MSC", prio); + if (ret) { + dprintf(1, "Unable to configure USB MSC drive.\n"); + free(drive); + return -1; + } + return 0; +} + +/**************************************************************** + * Setup + ****************************************************************/ + +// Configure a usb msc device. +int +usb_msc_setup(struct usbdevice_s *usbdev) +{ + if (!CONFIG_USB_MSC) + return -1; + + // Verify right kind of device + struct usb_interface_descriptor *iface = usbdev->iface; + if ((iface->bInterfaceSubClass != US_SC_SCSI && + iface->bInterfaceSubClass != US_SC_ATAPI_8070 && + iface->bInterfaceSubClass != US_SC_ATAPI_8020) + || iface->bInterfaceProtocol != US_PR_BULK) { + dprintf(1, "Unsupported MSC USB device (subclass=%02x proto=%02x)\n" + , iface->bInterfaceSubClass, iface->bInterfaceProtocol); + return -1; + } + + // Find bulk in and bulk out endpoints. + struct usb_pipe *inpipe = NULL, *outpipe = NULL; + struct usb_endpoint_descriptor *indesc = findEndPointDesc( + usbdev, USB_ENDPOINT_XFER_BULK, USB_DIR_IN); + struct usb_endpoint_descriptor *outdesc = findEndPointDesc( + usbdev, USB_ENDPOINT_XFER_BULK, USB_DIR_OUT); + if (!indesc || !outdesc) + goto fail; + inpipe = usb_alloc_pipe(usbdev, indesc); + outpipe = usb_alloc_pipe(usbdev, outdesc); + if (!inpipe || !outpipe) + goto fail; + + int maxlun = usb_msc_maxlun(usbdev->defpipe); + int lun, pipesused = 0; + for (lun = 0; lun < maxlun + 1; lun++) { + int ret = usb_msc_lun_setup(inpipe, outpipe, usbdev, lun); + if (!ret) + pipesused = 1; + } + + if (!pipesused) + goto fail; + + return 0; +fail: + dprintf(1, "Unable to configure USB MSC device.\n"); + free_pipe(inpipe); + free_pipe(outpipe); + return -1; +} diff --git a/roms/seabios/src/hw/usb-msc.h b/roms/seabios/src/hw/usb-msc.h new file mode 100644 index 000000000..c40d75556 --- /dev/null +++ b/roms/seabios/src/hw/usb-msc.h @@ -0,0 +1,10 @@ +#ifndef __USB_MSC_H +#define __USB_MSC_H + +// usb-msc.c +struct disk_op_s; +int usb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +struct usbdevice_s; +int usb_msc_setup(struct usbdevice_s *usbdev); + +#endif // ush-msc.h diff --git a/roms/seabios/src/hw/usb-ohci.c b/roms/seabios/src/hw/usb-ohci.c new file mode 100644 index 000000000..313e3fd44 --- /dev/null +++ b/roms/seabios/src/hw/usb-ohci.c @@ -0,0 +1,538 @@ +// Code for handling OHCI USB controllers. +// +// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_LOWFLAT +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // pci_bdf_to_bus +#include "pci_regs.h" // PCI_BASE_ADDRESS_0 +#include "string.h" // memset +#include "usb.h" // struct usb_s +#include "usb-ohci.h" // struct ohci_hcca +#include "util.h" // msleep +#include "x86.h" // readl + +#define FIT (1 << 31) + +struct usb_ohci_s { + struct usb_s usb; + struct ohci_regs *regs; +}; + +struct ohci_pipe { + struct ohci_ed ed; + struct usb_pipe pipe; + void *data; + int count; + struct ohci_td *tds; +}; + + +/**************************************************************** + * Root hub + ****************************************************************/ + +// Check if device attached to port +static int +ohci_hub_detect(struct usbhub_s *hub, u32 port) +{ + struct usb_ohci_s *cntl = container_of(hub->cntl, struct usb_ohci_s, usb); + u32 sts = readl(&cntl->regs->roothub_portstatus[port]); + if (!(sts & RH_PS_CCS)) + // No device. + return -1; + + // XXX - need to wait for USB_TIME_ATTDB if just powered up? + + return 0; +} + +// Disable port +static void +ohci_hub_disconnect(struct usbhub_s *hub, u32 port) +{ + struct usb_ohci_s *cntl = container_of(hub->cntl, struct usb_ohci_s, usb); + writel(&cntl->regs->roothub_portstatus[port], RH_PS_CCS|RH_PS_LSDA); +} + +// Reset device on port +static int +ohci_hub_reset(struct usbhub_s *hub, u32 port) +{ + struct usb_ohci_s *cntl = container_of(hub->cntl, struct usb_ohci_s, usb); + writel(&cntl->regs->roothub_portstatus[port], RH_PS_PRS); + u32 sts; + u32 end = timer_calc(USB_TIME_DRSTR * 2); + for (;;) { + sts = readl(&cntl->regs->roothub_portstatus[port]); + if (!(sts & RH_PS_PRS)) + // XXX - need to ensure USB_TIME_DRSTR time in reset? + break; + if (timer_check(end)) { + // Timeout. + warn_timeout(); + ohci_hub_disconnect(hub, port); + return -1; + } + yield(); + } + + if ((sts & (RH_PS_CCS|RH_PS_PES)) != (RH_PS_CCS|RH_PS_PES)) + // Device no longer present + return -1; + + return !!(sts & RH_PS_LSDA); +} + +static struct usbhub_op_s ohci_HubOp = { + .detect = ohci_hub_detect, + .reset = ohci_hub_reset, + .disconnect = ohci_hub_disconnect, +}; + +// Find any devices connected to the root hub. +static int +check_ohci_ports(struct usb_ohci_s *cntl) +{ + ASSERT32FLAT(); + // Turn on power for all devices on roothub. + u32 rha = readl(&cntl->regs->roothub_a); + rha &= ~(RH_A_PSM | RH_A_OCPM); + writel(&cntl->regs->roothub_status, RH_HS_LPSC); + writel(&cntl->regs->roothub_b, RH_B_PPCM); + msleep((rha >> 24) * 2); + // XXX - need to sleep for USB_TIME_SIGATT if just powered up? + + struct usbhub_s hub; + memset(&hub, 0, sizeof(hub)); + hub.cntl = &cntl->usb; + hub.portcount = rha & RH_A_NDP; + hub.op = &ohci_HubOp; + usb_enumerate(&hub); + return hub.devcount; +} + + +/**************************************************************** + * Setup + ****************************************************************/ + +// Wait for next USB frame to start - for ensuring safe memory release. +static void +ohci_waittick(struct usb_ohci_s *cntl) +{ + barrier(); + struct ohci_hcca *hcca = (void*)cntl->regs->hcca; + u32 startframe = hcca->frame_no; + u32 end = timer_calc(1000 * 5); + for (;;) { + if (hcca->frame_no != startframe) + break; + if (timer_check(end)) { + warn_timeout(); + return; + } + yield(); + } +} + +static void +ohci_free_pipes(struct usb_ohci_s *cntl) +{ + dprintf(7, "ohci_free_pipes %p\n", cntl); + + u32 creg = readl(&cntl->regs->control); + if (creg & (OHCI_CTRL_CLE|OHCI_CTRL_BLE)) { + writel(&cntl->regs->control, creg & ~(OHCI_CTRL_CLE|OHCI_CTRL_BLE)); + ohci_waittick(cntl); + } + + u32 *pos = &cntl->regs->ed_controlhead; + for (;;) { + struct ohci_ed *next = (void*)*pos; + if (!next) + break; + struct ohci_pipe *pipe = container_of(next, struct ohci_pipe, ed); + if (pipe->pipe.cntl != &cntl->usb) { + *pos = next->hwNextED; + free(pipe); + } else { + pos = &next->hwNextED; + } + } + + writel(&cntl->regs->ed_controlcurrent, 0); + writel(&cntl->regs->ed_bulkcurrent, 0); + writel(&cntl->regs->control, creg); + cntl->usb.freelist = NULL; +} + +static int +start_ohci(struct usb_ohci_s *cntl, struct ohci_hcca *hcca) +{ + u32 oldfminterval = readl(&cntl->regs->fminterval); + u32 oldrwc = readl(&cntl->regs->control) & OHCI_CTRL_RWC; + + // XXX - check if already running? + + // Do reset + writel(&cntl->regs->control, OHCI_USB_RESET | oldrwc); + readl(&cntl->regs->control); // flush writes + msleep(USB_TIME_DRSTR); + + // Do software init (min 10us, max 2ms) + u32 end = timer_calc_usec(10); + writel(&cntl->regs->cmdstatus, OHCI_HCR); + for (;;) { + u32 status = readl(&cntl->regs->cmdstatus); + if (! status & OHCI_HCR) + break; + if (timer_check(end)) { + warn_timeout(); + return -1; + } + } + + // Init memory + writel(&cntl->regs->ed_controlhead, 0); + writel(&cntl->regs->ed_bulkhead, 0); + writel(&cntl->regs->hcca, (u32)hcca); + + // Init fminterval + u32 fi = oldfminterval & 0x3fff; + writel(&cntl->regs->fminterval + , (((oldfminterval & FIT) ^ FIT) + | fi | (((6 * (fi - 210)) / 7) << 16))); + writel(&cntl->regs->periodicstart, ((9 * fi) / 10) & 0x3fff); + readl(&cntl->regs->control); // flush writes + + // XXX - verify that fminterval was setup correctly. + + // Go into operational state + writel(&cntl->regs->control + , (OHCI_CTRL_CBSR | OHCI_CTRL_CLE | OHCI_CTRL_PLE + | OHCI_USB_OPER | oldrwc)); + readl(&cntl->regs->control); // flush writes + + return 0; +} + +static void +stop_ohci(struct usb_ohci_s *cntl) +{ + u32 oldrwc = readl(&cntl->regs->control) & OHCI_CTRL_RWC; + writel(&cntl->regs->control, oldrwc); + readl(&cntl->regs->control); // flush writes +} + +static void +configure_ohci(void *data) +{ + struct usb_ohci_s *cntl = data; + + // Allocate memory + struct ohci_hcca *hcca = memalign_high(256, sizeof(*hcca)); + struct ohci_ed *intr_ed = malloc_high(sizeof(*intr_ed)); + if (!hcca || !intr_ed) { + warn_noalloc(); + goto free; + } + memset(hcca, 0, sizeof(*hcca)); + memset(intr_ed, 0, sizeof(*intr_ed)); + intr_ed->hwINFO = ED_SKIP; + int i; + for (i=0; i<ARRAY_SIZE(hcca->int_table); i++) + hcca->int_table[i] = (u32)intr_ed; + + int ret = start_ohci(cntl, hcca); + if (ret) + goto err; + + int count = check_ohci_ports(cntl); + ohci_free_pipes(cntl); + if (! count) + goto err; + return; + +err: + stop_ohci(cntl); +free: + free(hcca); + free(intr_ed); +} + +void +ohci_setup(struct pci_device *pci, int busid) +{ + if (! CONFIG_USB_OHCI) + return; + struct usb_ohci_s *cntl = malloc_tmphigh(sizeof(*cntl)); + if (!cntl) { + warn_noalloc(); + return; + } + memset(cntl, 0, sizeof(*cntl)); + cntl->usb.busid = busid; + cntl->usb.pci = pci; + cntl->usb.type = USB_TYPE_OHCI; + + u16 bdf = pci->bdf; + u32 baseaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0); + cntl->regs = (void*)(baseaddr & PCI_BASE_ADDRESS_MEM_MASK); + + dprintf(1, "OHCI init on dev %02x:%02x.%x (regs=%p)\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf) + , pci_bdf_to_fn(bdf), cntl->regs); + + // Enable bus mastering and memory access. + pci_config_maskw(bdf, PCI_COMMAND + , 0, PCI_COMMAND_MASTER|PCI_COMMAND_MEMORY); + + // XXX - check for and disable SMM control? + + // Disable interrupts + writel(&cntl->regs->intrdisable, ~0); + writel(&cntl->regs->intrstatus, ~0); + + run_thread(configure_ohci, cntl); +} + + +/**************************************************************** + * End point communication + ****************************************************************/ + +// Setup fields in ed +static void +ohci_desc2pipe(struct ohci_pipe *pipe, struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + usb_desc2pipe(&pipe->pipe, usbdev, epdesc); + pipe->ed.hwINFO = (ED_SKIP | usbdev->devaddr | (pipe->pipe.ep << 7) + | (epdesc->wMaxPacketSize << 16) + | (usbdev->speed ? ED_LOWSPEED : 0)); +} + +static struct usb_pipe * +ohci_alloc_intr_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + struct usb_ohci_s *cntl = container_of( + usbdev->hub->cntl, struct usb_ohci_s, usb); + int frameexp = usb_getFrameExp(usbdev, epdesc); + dprintf(7, "ohci_alloc_intr_pipe %p %d\n", &cntl->usb, frameexp); + + if (frameexp > 5) + frameexp = 5; + int maxpacket = epdesc->wMaxPacketSize; + // Determine number of entries needed for 2 timer ticks. + int ms = 1<<frameexp; + int count = DIV_ROUND_UP(ticks_to_ms(2), ms) + 1; + struct ohci_pipe *pipe = malloc_low(sizeof(*pipe)); + struct ohci_td *tds = malloc_low(sizeof(*tds) * count); + void *data = malloc_low(maxpacket * count); + if (!pipe || !tds || !data) + goto err; + memset(pipe, 0, sizeof(*pipe)); + ohci_desc2pipe(pipe, usbdev, epdesc); + pipe->ed.hwINFO &= ~ED_SKIP; + pipe->data = data; + pipe->count = count; + pipe->tds = tds; + + struct ohci_ed *ed = &pipe->ed; + ed->hwHeadP = (u32)&tds[0]; + ed->hwTailP = (u32)&tds[count-1]; + + int i; + for (i=0; i<count-1; i++) { + tds[i].hwINFO = TD_DP_IN | TD_T_TOGGLE | TD_CC; + tds[i].hwCBP = (u32)data + maxpacket * i; + tds[i].hwNextTD = (u32)&tds[i+1]; + tds[i].hwBE = tds[i].hwCBP + maxpacket - 1; + } + + // Add to interrupt schedule. + struct ohci_hcca *hcca = (void*)cntl->regs->hcca; + if (frameexp == 0) { + // Add to existing interrupt entry. + struct ohci_ed *intr_ed = (void*)hcca->int_table[0]; + ed->hwNextED = intr_ed->hwNextED; + barrier(); + intr_ed->hwNextED = (u32)ed; + } else { + int startpos = 1<<(frameexp-1); + ed->hwNextED = hcca->int_table[startpos]; + barrier(); + for (i=startpos; i<ARRAY_SIZE(hcca->int_table); i+=ms) + hcca->int_table[i] = (u32)ed; + } + + return &pipe->pipe; + +err: + free(pipe); + free(tds); + free(data); + return NULL; +} + +struct usb_pipe * +ohci_alloc_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + if (! CONFIG_USB_OHCI) + return NULL; + u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + if (eptype == USB_ENDPOINT_XFER_INT) + return ohci_alloc_intr_pipe(usbdev, epdesc); + if (eptype != USB_ENDPOINT_XFER_CONTROL) { + dprintf(1, "OHCI Bulk transfers not supported.\n"); + return NULL; + } + struct usb_ohci_s *cntl = container_of( + usbdev->hub->cntl, struct usb_ohci_s, usb); + dprintf(7, "ohci_alloc_async_pipe %p\n", &cntl->usb); + + struct usb_pipe *usbpipe = usb_getFreePipe(&cntl->usb, eptype); + if (usbpipe) { + // Use previously allocated pipe. + struct ohci_pipe *pipe = container_of(usbpipe, struct ohci_pipe, pipe); + ohci_desc2pipe(pipe, usbdev, epdesc); + return usbpipe; + } + + // Allocate a new queue head. + struct ohci_pipe *pipe = malloc_tmphigh(sizeof(*pipe)); + if (!pipe) { + warn_noalloc(); + return NULL; + } + memset(pipe, 0, sizeof(*pipe)); + ohci_desc2pipe(pipe, usbdev, epdesc); + + // Add queue head to controller list. + pipe->ed.hwNextED = cntl->regs->ed_controlhead; + barrier(); + cntl->regs->ed_controlhead = (u32)&pipe->ed; + return &pipe->pipe; +} + +static int +wait_ed(struct ohci_ed *ed) +{ + // XXX - 500ms just a guess + u32 end = timer_calc(500); + for (;;) { + if (ed->hwHeadP == ed->hwTailP) + return 0; + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } +} + +int +ohci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize + , void *data, int datasize) +{ + if (! CONFIG_USB_OHCI) + return -1; + dprintf(5, "ohci_control %p\n", p); + if (datasize > 4096) { + // XXX - should support larger sizes. + warn_noalloc(); + return -1; + } + struct ohci_pipe *pipe = container_of(p, struct ohci_pipe, pipe); + struct usb_ohci_s *cntl = container_of( + pipe->pipe.cntl, struct usb_ohci_s, usb); + + // Setup transfer descriptors + struct ohci_td *tds = malloc_tmphigh(sizeof(*tds) * 3); + if (!tds) { + warn_noalloc(); + return -1; + } + struct ohci_td *td = tds; + td->hwINFO = TD_DP_SETUP | TD_T_DATA0 | TD_CC; + td->hwCBP = (u32)cmd; + td->hwNextTD = (u32)&td[1]; + td->hwBE = (u32)cmd + cmdsize - 1; + td++; + if (datasize) { + td->hwINFO = (dir ? TD_DP_IN : TD_DP_OUT) | TD_T_DATA1 | TD_CC; + td->hwCBP = (u32)data; + td->hwNextTD = (u32)&td[1]; + td->hwBE = (u32)data + datasize - 1; + td++; + } + td->hwINFO = (dir ? TD_DP_OUT : TD_DP_IN) | TD_T_DATA1 | TD_CC; + td->hwCBP = 0; + td->hwNextTD = (u32)&td[1]; + td->hwBE = 0; + td++; + + // Transfer data + pipe->ed.hwHeadP = (u32)tds; + pipe->ed.hwTailP = (u32)td; + barrier(); + pipe->ed.hwINFO &= ~ED_SKIP; + writel(&cntl->regs->cmdstatus, OHCI_CLF); + + int ret = wait_ed(&pipe->ed); + pipe->ed.hwINFO |= ED_SKIP; + if (ret) + ohci_waittick(cntl); + free(tds); + return ret; +} + +int +ohci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize) +{ + return -1; +} + +int +ohci_poll_intr(struct usb_pipe *p, void *data) +{ + ASSERT16(); + if (! CONFIG_USB_OHCI) + return -1; + + struct ohci_pipe *pipe = container_of(p, struct ohci_pipe, pipe); + struct ohci_td *tds = GET_LOWFLAT(pipe->tds); + struct ohci_td *head = (void*)(GET_LOWFLAT(pipe->ed.hwHeadP) & ~(ED_C|ED_H)); + struct ohci_td *tail = (void*)GET_LOWFLAT(pipe->ed.hwTailP); + int count = GET_LOWFLAT(pipe->count); + int pos = (tail - tds + 1) % count; + struct ohci_td *next = &tds[pos]; + if (head == next) + // No intrs found. + return -1; + // XXX - check for errors. + + // Copy data. + int maxpacket = GET_LOWFLAT(pipe->pipe.maxpacket); + void *pipedata = GET_LOWFLAT((pipe->data)); + void *intrdata = pipedata + maxpacket * pos; + memcpy_far(GET_SEG(SS), data, SEG_LOW, LOWFLAT2LOW(intrdata), maxpacket); + + // Reenable this td. + SET_LOWFLAT(tail->hwINFO, TD_DP_IN | TD_T_TOGGLE | TD_CC); + intrdata = pipedata + maxpacket * (tail-tds); + SET_LOWFLAT(tail->hwCBP, (u32)intrdata); + SET_LOWFLAT(tail->hwNextTD, (u32)next); + SET_LOWFLAT(tail->hwBE, (u32)intrdata + maxpacket - 1); + barrier(); + SET_LOWFLAT(pipe->ed.hwTailP, (u32)next); + + return 0; +} diff --git a/roms/seabios/src/hw/usb-ohci.h b/roms/seabios/src/hw/usb-ohci.h new file mode 100644 index 000000000..ad0ffece5 --- /dev/null +++ b/roms/seabios/src/hw/usb-ohci.h @@ -0,0 +1,143 @@ +#ifndef __USB_OHCI_H +#define __USB_OHCI_H + +// usb-ohci.c +void ohci_setup(struct pci_device *pci, int busid); +struct usbdevice_s; +struct usb_endpoint_descriptor; +struct usb_pipe *ohci_alloc_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc); +struct usb_pipe; +int ohci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize + , void *data, int datasize); +int ohci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize); +int ohci_poll_intr(struct usb_pipe *p, void *data); + + +/**************************************************************** + * ohci structs and flags + ****************************************************************/ + +struct ohci_ed { + u32 hwINFO; + u32 hwTailP; + u32 hwHeadP; + u32 hwNextED; +} PACKED; + +#define ED_ISO (1 << 15) +#define ED_SKIP (1 << 14) +#define ED_LOWSPEED (1 << 13) +#define ED_OUT (0x01 << 11) +#define ED_IN (0x02 << 11) + +#define ED_C (0x02) +#define ED_H (0x01) + +struct ohci_td { + u32 hwINFO; + u32 hwCBP; + u32 hwNextTD; + u32 hwBE; +} PACKED; + +#define TD_CC 0xf0000000 +#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) +#define TD_DI 0x00E00000 + +#define TD_DONE 0x00020000 +#define TD_ISO 0x00010000 + +#define TD_EC 0x0C000000 +#define TD_T 0x03000000 +#define TD_T_DATA0 0x02000000 +#define TD_T_DATA1 0x03000000 +#define TD_T_TOGGLE 0x00000000 +#define TD_DP 0x00180000 +#define TD_DP_SETUP 0x00000000 +#define TD_DP_IN 0x00100000 +#define TD_DP_OUT 0x00080000 + +#define TD_R 0x00040000 + +struct ohci_hcca { + u32 int_table[32]; + u32 frame_no; + u32 done_head; + u8 reserved[120]; +} PACKED; + +struct ohci_regs { + u32 revision; + u32 control; + u32 cmdstatus; + u32 intrstatus; + u32 intrenable; + u32 intrdisable; + + u32 hcca; + u32 ed_periodcurrent; + u32 ed_controlhead; + u32 ed_controlcurrent; + u32 ed_bulkhead; + u32 ed_bulkcurrent; + u32 donehead; + + u32 fminterval; + u32 fmremaining; + u32 fmnumber; + u32 periodicstart; + u32 lsthresh; + + u32 roothub_a; + u32 roothub_b; + u32 roothub_status; + u32 roothub_portstatus[15]; +} PACKED; + +#define OHCI_CTRL_CBSR (3 << 0) +#define OHCI_CTRL_PLE (1 << 2) +#define OHCI_CTRL_CLE (1 << 4) +#define OHCI_CTRL_BLE (1 << 5) +#define OHCI_CTRL_HCFS (3 << 6) +# define OHCI_USB_RESET (0 << 6) +# define OHCI_USB_OPER (2 << 6) +#define OHCI_CTRL_RWC (1 << 9) + +#define OHCI_HCR (1 << 0) +#define OHCI_CLF (1 << 1) + +#define OHCI_INTR_MIE (1 << 31) + +#define RH_PS_CCS 0x00000001 +#define RH_PS_PES 0x00000002 +#define RH_PS_PSS 0x00000004 +#define RH_PS_POCI 0x00000008 +#define RH_PS_PRS 0x00000010 +#define RH_PS_PPS 0x00000100 +#define RH_PS_LSDA 0x00000200 +#define RH_PS_CSC 0x00010000 +#define RH_PS_PESC 0x00020000 +#define RH_PS_PSSC 0x00040000 +#define RH_PS_OCIC 0x00080000 +#define RH_PS_PRSC 0x00100000 + +#define RH_HS_LPS 0x00000001 +#define RH_HS_OCI 0x00000002 +#define RH_HS_DRWE 0x00008000 +#define RH_HS_LPSC 0x00010000 +#define RH_HS_OCIC 0x00020000 +#define RH_HS_CRWE 0x80000000 + +#define RH_B_DR 0x0000ffff +#define RH_B_PPCM 0xffff0000 + +#define RH_A_NDP (0xff << 0) +#define RH_A_PSM (1 << 8) +#define RH_A_NPS (1 << 9) +#define RH_A_DT (1 << 10) +#define RH_A_OCPM (1 << 11) +#define RH_A_NOCP (1 << 12) +#define RH_A_POTPGT (0xff << 24) + +#endif // usb-ohci.h diff --git a/roms/seabios/src/hw/usb-uas.c b/roms/seabios/src/hw/usb-uas.c new file mode 100644 index 000000000..33657acbc --- /dev/null +++ b/roms/seabios/src/hw/usb-uas.c @@ -0,0 +1,271 @@ +// Code for handling usb attached scsi devices. +// +// only usb 2.0 for now. +// +// once we have xhci driver with usb 3.0 support this must +// be updated to use usb3 streams so booting from usb3 +// devices actually works. +// +// Authors: +// Gerd Hoffmann <kraxel@redhat.com> +// +// based on usb-msc.c which is written by: +// Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // DTYPE_USB +#include "blockcmd.h" // cdb_read +#include "config.h" // CONFIG_USB_UAS +#include "malloc.h" // free +#include "output.h" // dprintf +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "usb.h" // struct usb_s +#include "usb-uas.h" // usb_uas_init +#include "util.h" // bootprio_find_usb + +#define UAS_UI_COMMAND 0x01 +#define UAS_UI_SENSE 0x03 +#define UAS_UI_RESPONSE 0x04 +#define UAS_UI_TASK_MGMT 0x05 +#define UAS_UI_READ_READY 0x06 +#define UAS_UI_WRITE_READY 0x07 + +#define UAS_PIPE_ID_COMMAND 0x01 +#define UAS_PIPE_ID_STATUS 0x02 +#define UAS_PIPE_ID_DATA_IN 0x03 +#define UAS_PIPE_ID_DATA_OUT 0x04 + +typedef struct { + u8 id; + u8 reserved; + u16 tag; +} PACKED uas_ui_header; + +typedef struct { + u8 prio_taskattr; /* 6:3 priority, 2:0 task attribute */ + u8 reserved_1; + u8 add_cdb_length; /* 7:2 additional adb length (dwords) */ + u8 reserved_2; + u8 lun[8]; + u8 cdb[16]; + u8 add_cdb[]; +} PACKED uas_ui_command; + +typedef struct { + u16 status_qualifier; + u8 status; + u8 reserved[7]; + u16 sense_length; + u8 sense_data[18]; +} PACKED uas_ui_sense; + +typedef struct { + u16 add_response_info; + u8 response_code; +} PACKED uas_ui_response; + +typedef struct { + u8 function; + u8 reserved; + u16 task_tag; + u8 lun[8]; +} PACKED uas_ui_task_mgmt; + +typedef struct { + uas_ui_header hdr; + union { + uas_ui_command command; + uas_ui_sense sense; + uas_ui_task_mgmt task; + uas_ui_response response; + }; +} PACKED uas_ui; + +struct uasdrive_s { + struct drive_s drive; + struct usb_pipe *command, *status, *data_in, *data_out; + int lun; +}; + +int +uas_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + if (!CONFIG_USB_UAS) + return DISK_RET_EBADTRACK; + + struct uasdrive_s *drive_gf = container_of( + op->drive_gf, struct uasdrive_s, drive); + + uas_ui ui; + memset(&ui, 0, sizeof(ui)); + ui.hdr.id = UAS_UI_COMMAND; + ui.hdr.tag = 0xdead; + ui.command.lun[1] = GET_GLOBALFLAT(drive_gf->lun); + memcpy(ui.command.cdb, cdbcmd, sizeof(ui.command.cdb)); + int ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->command), + USB_DIR_OUT, MAKE_FLATPTR(GET_SEG(SS), &ui), + sizeof(ui.hdr) + sizeof(ui.command)); + if (ret) { + dprintf(1, "uas: command send fail"); + goto fail; + } + + memset(&ui, 0xff, sizeof(ui)); + ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->status), + USB_DIR_IN, MAKE_FLATPTR(GET_SEG(SS), &ui), sizeof(ui)); + if (ret) { + dprintf(1, "uas: status recv fail"); + goto fail; + } + + switch (ui.hdr.id) { + case UAS_UI_SENSE: + goto have_sense; + case UAS_UI_READ_READY: + ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->data_in), + USB_DIR_IN, op->buf_fl, op->count * blocksize); + if (ret) { + dprintf(1, "uas: data read fail"); + goto fail; + } + break; + case UAS_UI_WRITE_READY: + ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->data_out), + USB_DIR_OUT, op->buf_fl, op->count * blocksize); + if (ret) { + dprintf(1, "uas: data write fail"); + goto fail; + } + break; + default: + dprintf(1, "uas: unknown status ui id %d", ui.hdr.id); + goto fail; + } + + memset(&ui, 0xff, sizeof(ui)); + ret = usb_send_bulk(GET_GLOBALFLAT(drive_gf->status), + USB_DIR_IN, MAKE_FLATPTR(GET_SEG(SS), &ui), sizeof(ui)); + if (ret) { + dprintf(1, "uas: status recv fail"); + goto fail; + } + if (ui.hdr.id != UAS_UI_SENSE) { + dprintf(1, "uas: expected sense ui, got ui id %d", ui.hdr.id); + goto fail; + } + +have_sense: + if (ui.sense.status == 0) { + return DISK_RET_SUCCESS; + } + +fail: + return DISK_RET_EBADTRACK; +} + +static int +uas_lun_setup(struct usbdevice_s *usbdev, + struct usb_pipe *command, struct usb_pipe *status, + struct usb_pipe *data_in, struct usb_pipe *data_out, + int lun) +{ + // Allocate drive structure. + struct uasdrive_s *drive = malloc_fseg(sizeof(*drive)); + if (!drive) { + warn_noalloc(); + return -1; + } + memset(drive, 0, sizeof(*drive)); + drive->drive.type = DTYPE_UAS; + drive->command = command; + drive->status = status; + drive->data_in = data_in; + drive->data_out = data_out; + drive->lun = lun; + + int prio = bootprio_find_usb(usbdev, lun); + int ret = scsi_drive_setup(&drive->drive, "USB UAS", prio); + if (ret) { + free(drive); + return -1; + } + return 0; +} + +int +usb_uas_setup(struct usbdevice_s *usbdev) +{ + if (!CONFIG_USB_UAS) + return -1; + + // Verify right kind of device + struct usb_interface_descriptor *iface = usbdev->iface; + if (iface->bInterfaceSubClass != US_SC_SCSI || + iface->bInterfaceProtocol != US_PR_UAS) { + dprintf(1, "Unsupported UAS device (subclass=%02x proto=%02x)\n" + , iface->bInterfaceSubClass, iface->bInterfaceProtocol); + return -1; + } + + /* find & allocate pipes */ + struct usb_endpoint_descriptor *ep = NULL; + struct usb_pipe *command = NULL; + struct usb_pipe *status = NULL; + struct usb_pipe *data_in = NULL; + struct usb_pipe *data_out = NULL; + u8 *desc = (u8*)iface; + while (desc) { + desc += desc[0]; + switch (desc[1]) { + case USB_DT_ENDPOINT: + ep = (void*)desc; + break; + case USB_DT_ENDPOINT_COMPANION: + /* No support (yet) for usb3 streams */ + dprintf(1, "Superspeed UAS devices not supported (yet)\n"); + goto fail; + case 0x24: + switch (desc[2]) { + case UAS_PIPE_ID_COMMAND: + command = usb_alloc_pipe(usbdev, ep); + break; + case UAS_PIPE_ID_STATUS: + status = usb_alloc_pipe(usbdev, ep); + break; + case UAS_PIPE_ID_DATA_IN: + data_in = usb_alloc_pipe(usbdev, ep); + break; + case UAS_PIPE_ID_DATA_OUT: + data_out = usb_alloc_pipe(usbdev, ep); + break; + default: + goto fail; + } + break; + default: + desc = NULL; + break; + } + } + if (!command || !status || !data_in || !data_out) + goto fail; + + /* TODO: send REPORT LUNS. For now, only LUN 0 is recognized. */ + int ret = uas_lun_setup(usbdev, command, status, data_in, data_out, 0); + if (ret < 0) { + dprintf(1, "Unable to configure UAS drive.\n"); + goto fail; + } + + return 0; + +fail: + free_pipe(command); + free_pipe(status); + free_pipe(data_in); + free_pipe(data_out); + return -1; +} diff --git a/roms/seabios/src/hw/usb-uas.h b/roms/seabios/src/hw/usb-uas.h new file mode 100644 index 000000000..ad91c5f60 --- /dev/null +++ b/roms/seabios/src/hw/usb-uas.h @@ -0,0 +1,9 @@ +#ifndef __USB_UAS_H +#define __USB_UAS_H + +struct disk_op_s; +int uas_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +struct usbdevice_s; +int usb_uas_setup(struct usbdevice_s *usbdev); + +#endif /* __USB_UAS_H */ diff --git a/roms/seabios/src/hw/usb-uhci.c b/roms/seabios/src/hw/usb-uhci.c new file mode 100644 index 000000000..03eb5e1a4 --- /dev/null +++ b/roms/seabios/src/hw/usb-uhci.c @@ -0,0 +1,581 @@ +// Code for handling UHCI USB controllers. +// +// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_LOWFLAT +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // pci_bdf_to_bus +#include "pci_regs.h" // PCI_BASE_ADDRESS_4 +#include "string.h" // memset +#include "usb.h" // struct usb_s +#include "usb-uhci.h" // USBLEGSUP +#include "util.h" // msleep +#include "x86.h" // outw + +struct usb_uhci_s { + struct usb_s usb; + u16 iobase; + struct uhci_qh *control_qh; + struct uhci_framelist *framelist; +}; + +struct uhci_pipe { + struct uhci_qh qh; + struct uhci_td *next_td; + struct usb_pipe pipe; + u16 iobase; + u8 toggle; +}; + + +/**************************************************************** + * Root hub + ****************************************************************/ + +// Check if device attached to a given port +static int +uhci_hub_detect(struct usbhub_s *hub, u32 port) +{ + struct usb_uhci_s *cntl = container_of(hub->cntl, struct usb_uhci_s, usb); + u16 ioport = cntl->iobase + USBPORTSC1 + port * 2; + + u16 status = inw(ioport); + if (!(status & USBPORTSC_CCS)) + // No device + return -1; + + // XXX - if just powered up, need to wait for USB_TIME_ATTDB? + + // Begin reset on port + outw(USBPORTSC_PR, ioport); + msleep(USB_TIME_DRSTR); + return 0; +} + +// Reset device on port +static int +uhci_hub_reset(struct usbhub_s *hub, u32 port) +{ + struct usb_uhci_s *cntl = container_of(hub->cntl, struct usb_uhci_s, usb); + u16 ioport = cntl->iobase + USBPORTSC1 + port * 2; + + // Finish reset on port + outw(0, ioport); + udelay(6); // 64 high-speed bit times + u16 status = inw(ioport); + if (!(status & USBPORTSC_CCS)) + // No longer connected + return -1; + outw(USBPORTSC_PE, ioport); + return !!(status & USBPORTSC_LSDA); +} + +// Disable port +static void +uhci_hub_disconnect(struct usbhub_s *hub, u32 port) +{ + struct usb_uhci_s *cntl = container_of(hub->cntl, struct usb_uhci_s, usb); + u16 ioport = cntl->iobase + USBPORTSC1 + port * 2; + outw(0, ioport); +} + +static struct usbhub_op_s uhci_HubOp = { + .detect = uhci_hub_detect, + .reset = uhci_hub_reset, + .disconnect = uhci_hub_disconnect, +}; + +// Find any devices connected to the root hub. +static int +check_uhci_ports(struct usb_uhci_s *cntl) +{ + ASSERT32FLAT(); + struct usbhub_s hub; + memset(&hub, 0, sizeof(hub)); + hub.cntl = &cntl->usb; + hub.portcount = 2; + hub.op = &uhci_HubOp; + usb_enumerate(&hub); + return hub.devcount; +} + + +/**************************************************************** + * Setup + ****************************************************************/ + +// Wait for next USB frame to start - for ensuring safe memory release. +static void +uhci_waittick(u16 iobase) +{ + barrier(); + u16 startframe = inw(iobase + USBFRNUM); + u32 end = timer_calc(1000 * 5); + for (;;) { + if (inw(iobase + USBFRNUM) != startframe) + break; + if (timer_check(end)) { + warn_timeout(); + return; + } + yield(); + } +} + +static void +uhci_free_pipes(struct usb_uhci_s *cntl) +{ + dprintf(7, "uhci_free_pipes %p\n", cntl); + + struct uhci_qh *pos = (void*)(cntl->framelist->links[0] & ~UHCI_PTR_BITS); + for (;;) { + u32 link = pos->link; + if (link == UHCI_PTR_TERM) + break; + struct uhci_qh *next = (void*)(link & ~UHCI_PTR_BITS); + struct uhci_pipe *pipe = container_of(next, struct uhci_pipe, qh); + if (pipe->pipe.cntl != &cntl->usb) + pos->link = next->link; + else + pos = next; + } + uhci_waittick(cntl->iobase); + for (;;) { + struct usb_pipe *usbpipe = cntl->usb.freelist; + if (!usbpipe) + break; + cntl->usb.freelist = usbpipe->freenext; + struct uhci_pipe *pipe = container_of(usbpipe, struct uhci_pipe, pipe); + free(pipe); + } +} + +static void +reset_uhci(struct usb_uhci_s *cntl, u16 bdf) +{ + // XXX - don't reset if not needed. + + // Reset PIRQ and SMI + pci_config_writew(bdf, USBLEGSUP, USBLEGSUP_RWC); + + // Reset the HC + outw(USBCMD_HCRESET, cntl->iobase + USBCMD); + udelay(5); + + // Disable interrupts and commands (just to be safe). + outw(0, cntl->iobase + USBINTR); + outw(0, cntl->iobase + USBCMD); +} + +static void +configure_uhci(void *data) +{ + struct usb_uhci_s *cntl = data; + + // Allocate ram for schedule storage + struct uhci_td *term_td = malloc_high(sizeof(*term_td)); + struct uhci_framelist *fl = memalign_high(sizeof(*fl), sizeof(*fl)); + struct uhci_pipe *intr_pipe = malloc_high(sizeof(*intr_pipe)); + struct uhci_pipe *term_pipe = malloc_high(sizeof(*term_pipe)); + if (!term_td || !fl || !intr_pipe || !term_pipe) { + warn_noalloc(); + goto fail; + } + + // Work around for PIIX errata + memset(term_td, 0, sizeof(*term_td)); + term_td->link = UHCI_PTR_TERM; + term_td->token = (uhci_explen(0) | (0x7f << TD_TOKEN_DEVADDR_SHIFT) + | USB_PID_IN); + memset(term_pipe, 0, sizeof(*term_pipe)); + term_pipe->qh.element = (u32)term_td; + term_pipe->qh.link = UHCI_PTR_TERM; + term_pipe->pipe.cntl = &cntl->usb; + + // Set schedule to point to primary intr queue head + memset(intr_pipe, 0, sizeof(*intr_pipe)); + intr_pipe->qh.element = UHCI_PTR_TERM; + intr_pipe->qh.link = (u32)&term_pipe->qh | UHCI_PTR_QH; + intr_pipe->pipe.cntl = &cntl->usb; + int i; + for (i=0; i<ARRAY_SIZE(fl->links); i++) + fl->links[i] = (u32)&intr_pipe->qh | UHCI_PTR_QH; + cntl->framelist = fl; + cntl->control_qh = &intr_pipe->qh; + barrier(); + + // Set the frame length to the default: 1 ms exactly + outb(USBSOF_DEFAULT, cntl->iobase + USBSOF); + + // Store the frame list base address + outl((u32)fl->links, cntl->iobase + USBFLBASEADD); + + // Set the current frame number + outw(0, cntl->iobase + USBFRNUM); + + // Mark as configured and running with a 64-byte max packet. + outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, cntl->iobase + USBCMD); + + // Find devices + int count = check_uhci_ports(cntl); + uhci_free_pipes(cntl); + if (count) + // Success + return; + + // No devices found - shutdown and free controller. + outw(0, cntl->iobase + USBCMD); +fail: + free(term_td); + free(fl); + free(intr_pipe); + free(term_pipe); + free(cntl); +} + +void +uhci_setup(struct pci_device *pci, int busid) +{ + if (! CONFIG_USB_UHCI) + return; + u16 bdf = pci->bdf; + struct usb_uhci_s *cntl = malloc_tmphigh(sizeof(*cntl)); + if (!cntl) { + warn_noalloc(); + return; + } + memset(cntl, 0, sizeof(*cntl)); + cntl->usb.busid = busid; + cntl->usb.pci = pci; + cntl->usb.type = USB_TYPE_UHCI; + cntl->iobase = (pci_config_readl(bdf, PCI_BASE_ADDRESS_4) + & PCI_BASE_ADDRESS_IO_MASK); + + dprintf(1, "UHCI init on dev %02x:%02x.%x (io=%x)\n" + , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf) + , pci_bdf_to_fn(bdf), cntl->iobase); + + pci_config_maskw(bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + + reset_uhci(cntl, bdf); + + run_thread(configure_uhci, cntl); +} + + +/**************************************************************** + * End point communication + ****************************************************************/ + +static struct usb_pipe * +uhci_alloc_intr_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + struct usb_uhci_s *cntl = container_of( + usbdev->hub->cntl, struct usb_uhci_s, usb); + int frameexp = usb_getFrameExp(usbdev, epdesc); + dprintf(7, "uhci_alloc_intr_pipe %p %d\n", &cntl->usb, frameexp); + + if (frameexp > 10) + frameexp = 10; + int maxpacket = epdesc->wMaxPacketSize; + // Determine number of entries needed for 2 timer ticks. + int ms = 1<<frameexp; + int count = DIV_ROUND_UP(ticks_to_ms(2), ms); + count = ALIGN(count, 2); + struct uhci_pipe *pipe = malloc_low(sizeof(*pipe)); + struct uhci_td *tds = malloc_low(sizeof(*tds) * count); + void *data = malloc_low(maxpacket * count); + if (!pipe || !tds || !data) { + warn_noalloc(); + goto fail; + } + memset(pipe, 0, sizeof(*pipe)); + usb_desc2pipe(&pipe->pipe, usbdev, epdesc); + int lowspeed = pipe->pipe.speed; + int devaddr = pipe->pipe.devaddr | (pipe->pipe.ep << 7); + pipe->qh.element = (u32)tds; + pipe->next_td = &tds[0]; + pipe->iobase = cntl->iobase; + + int toggle = 0; + int i; + for (i=0; i<count; i++) { + tds[i].link = (i==count-1 ? (u32)&tds[0] : (u32)&tds[i+1]); + tds[i].status = (uhci_maxerr(3) | (lowspeed ? TD_CTRL_LS : 0) + | TD_CTRL_ACTIVE); + tds[i].token = (uhci_explen(maxpacket) | toggle + | (devaddr << TD_TOKEN_DEVADDR_SHIFT) + | USB_PID_IN); + tds[i].buffer = data + maxpacket * i; + toggle ^= TD_TOKEN_TOGGLE; + } + + // Add to interrupt schedule. + struct uhci_framelist *fl = cntl->framelist; + if (frameexp == 0) { + // Add to existing interrupt entry. + struct uhci_qh *intr_qh = (void*)(fl->links[0] & ~UHCI_PTR_BITS); + pipe->qh.link = intr_qh->link; + barrier(); + intr_qh->link = (u32)&pipe->qh | UHCI_PTR_QH; + if (cntl->control_qh == intr_qh) + cntl->control_qh = &pipe->qh; + } else { + int startpos = 1<<(frameexp-1); + pipe->qh.link = fl->links[startpos]; + barrier(); + for (i=startpos; i<ARRAY_SIZE(fl->links); i+=ms) + fl->links[i] = (u32)&pipe->qh | UHCI_PTR_QH; + } + + return &pipe->pipe; +fail: + free(pipe); + free(tds); + free(data); + return NULL; +} + +struct usb_pipe * +uhci_alloc_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + if (! CONFIG_USB_UHCI) + return NULL; + u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + if (eptype == USB_ENDPOINT_XFER_INT) + return uhci_alloc_intr_pipe(usbdev, epdesc); + struct usb_uhci_s *cntl = container_of( + usbdev->hub->cntl, struct usb_uhci_s, usb); + dprintf(7, "uhci_alloc_async_pipe %p %d\n", &cntl->usb, eptype); + + struct usb_pipe *usbpipe = usb_getFreePipe(&cntl->usb, eptype); + if (usbpipe) { + // Use previously allocated pipe. + usb_desc2pipe(usbpipe, usbdev, epdesc); + return usbpipe; + } + + // Allocate a new queue head. + struct uhci_pipe *pipe; + if (eptype == USB_ENDPOINT_XFER_CONTROL) + pipe = malloc_tmphigh(sizeof(*pipe)); + else + pipe = malloc_low(sizeof(*pipe)); + if (!pipe) { + warn_noalloc(); + return NULL; + } + memset(pipe, 0, sizeof(*pipe)); + usb_desc2pipe(&pipe->pipe, usbdev, epdesc); + pipe->qh.element = UHCI_PTR_TERM; + pipe->iobase = cntl->iobase; + + // Add queue head to controller list. + struct uhci_qh *control_qh = cntl->control_qh; + pipe->qh.link = control_qh->link; + barrier(); + control_qh->link = (u32)&pipe->qh | UHCI_PTR_QH; + if (eptype == USB_ENDPOINT_XFER_CONTROL) + cntl->control_qh = &pipe->qh; + return &pipe->pipe; +} + +static int +wait_pipe(struct uhci_pipe *pipe, int timeout) +{ + u32 end = timer_calc(timeout); + for (;;) { + u32 el_link = GET_LOWFLAT(pipe->qh.element); + if (el_link & UHCI_PTR_TERM) + return 0; + if (timer_check(end)) { + warn_timeout(); + u16 iobase = GET_LOWFLAT(pipe->iobase); + struct uhci_td *td = (void*)(el_link & ~UHCI_PTR_BITS); + dprintf(1, "Timeout on wait_pipe %p (td=%p s=%x c=%x/%x)\n" + , pipe, (void*)el_link, GET_LOWFLAT(td->status) + , inw(iobase + USBCMD) + , inw(iobase + USBSTS)); + SET_LOWFLAT(pipe->qh.element, UHCI_PTR_TERM); + uhci_waittick(iobase); + return -1; + } + yield(); + } +} + +static int +wait_td(struct uhci_td *td) +{ + u32 end = timer_calc(5000); // XXX - lookup real time. + u32 status; + for (;;) { + status = td->status; + if (!(status & TD_CTRL_ACTIVE)) + break; + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } + if (status & TD_CTRL_ANY_ERROR) { + dprintf(1, "wait_td error - status=%x\n", status); + return -2; + } + return 0; +} + +int +uhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize + , void *data, int datasize) +{ + ASSERT32FLAT(); + if (! CONFIG_USB_UHCI) + return -1; + dprintf(5, "uhci_control %p\n", p); + struct uhci_pipe *pipe = container_of(p, struct uhci_pipe, pipe); + + int maxpacket = pipe->pipe.maxpacket; + int lowspeed = pipe->pipe.speed; + int devaddr = pipe->pipe.devaddr | (pipe->pipe.ep << 7); + + // Setup transfer descriptors + int count = 2 + DIV_ROUND_UP(datasize, maxpacket); + struct uhci_td *tds = malloc_tmphigh(sizeof(*tds) * count); + if (!tds) { + warn_noalloc(); + return -1; + } + + tds[0].link = (u32)&tds[1] | UHCI_PTR_DEPTH; + tds[0].status = (uhci_maxerr(3) | (lowspeed ? TD_CTRL_LS : 0) + | TD_CTRL_ACTIVE); + tds[0].token = (uhci_explen(cmdsize) | (devaddr << TD_TOKEN_DEVADDR_SHIFT) + | USB_PID_SETUP); + tds[0].buffer = (void*)cmd; + int toggle = TD_TOKEN_TOGGLE; + int i; + for (i=1; i<count-1; i++) { + tds[i].link = (u32)&tds[i+1] | UHCI_PTR_DEPTH; + tds[i].status = (uhci_maxerr(3) | (lowspeed ? TD_CTRL_LS : 0) + | TD_CTRL_ACTIVE); + int len = (i == count-2 ? (datasize - (i-1)*maxpacket) : maxpacket); + tds[i].token = (uhci_explen(len) | toggle + | (devaddr << TD_TOKEN_DEVADDR_SHIFT) + | (dir ? USB_PID_IN : USB_PID_OUT)); + tds[i].buffer = data + (i-1) * maxpacket; + toggle ^= TD_TOKEN_TOGGLE; + } + tds[i].link = UHCI_PTR_TERM; + tds[i].status = (uhci_maxerr(0) | (lowspeed ? TD_CTRL_LS : 0) + | TD_CTRL_ACTIVE); + tds[i].token = (uhci_explen(0) | TD_TOKEN_TOGGLE + | (devaddr << TD_TOKEN_DEVADDR_SHIFT) + | (dir ? USB_PID_OUT : USB_PID_IN)); + tds[i].buffer = 0; + + // Transfer data + barrier(); + pipe->qh.element = (u32)&tds[0]; + int ret = wait_pipe(pipe, 500); + free(tds); + return ret; +} + +#define STACKTDS 4 +#define TDALIGN 16 + +int +uhci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize) +{ + if (! CONFIG_USB_UHCI) + return -1; + struct uhci_pipe *pipe = container_of(p, struct uhci_pipe, pipe); + dprintf(7, "uhci_send_bulk qh=%p dir=%d data=%p size=%d\n" + , &pipe->qh, dir, data, datasize); + int maxpacket = GET_LOWFLAT(pipe->pipe.maxpacket); + int lowspeed = GET_LOWFLAT(pipe->pipe.speed); + int devaddr = (GET_LOWFLAT(pipe->pipe.devaddr) + | (GET_LOWFLAT(pipe->pipe.ep) << 7)); + int toggle = GET_LOWFLAT(pipe->toggle) ? TD_TOKEN_TOGGLE : 0; + + // Allocate 4 tds on stack (16byte aligned) + u8 tdsbuf[sizeof(struct uhci_td) * STACKTDS + TDALIGN - 1]; + struct uhci_td *tds = (void*)ALIGN((u32)tdsbuf, TDALIGN); + memset(tds, 0, sizeof(*tds) * STACKTDS); + + // Enable tds + barrier(); + SET_LOWFLAT(pipe->qh.element, (u32)MAKE_FLATPTR(GET_SEG(SS), tds)); + + int tdpos = 0; + while (datasize) { + struct uhci_td *td = &tds[tdpos++ % STACKTDS]; + int ret = wait_td(td); + if (ret) + goto fail; + + int transfer = datasize; + if (transfer > maxpacket) + transfer = maxpacket; + struct uhci_td *nexttd_fl = MAKE_FLATPTR(GET_SEG(SS) + , &tds[tdpos % STACKTDS]); + td->link = (transfer==datasize ? UHCI_PTR_TERM : (u32)nexttd_fl); + td->token = (uhci_explen(transfer) | toggle + | (devaddr << TD_TOKEN_DEVADDR_SHIFT) + | (dir ? USB_PID_IN : USB_PID_OUT)); + td->buffer = data; + barrier(); + td->status = (uhci_maxerr(3) | (lowspeed ? TD_CTRL_LS : 0) + | TD_CTRL_ACTIVE); + toggle ^= TD_TOKEN_TOGGLE; + + data += transfer; + datasize -= transfer; + } + SET_LOWFLAT(pipe->toggle, !!toggle); + return wait_pipe(pipe, 5000); +fail: + dprintf(1, "uhci_send_bulk failed\n"); + SET_LOWFLAT(pipe->qh.element, UHCI_PTR_TERM); + uhci_waittick(GET_LOWFLAT(pipe->iobase)); + return -1; +} + +int +uhci_poll_intr(struct usb_pipe *p, void *data) +{ + ASSERT16(); + if (! CONFIG_USB_UHCI) + return -1; + + struct uhci_pipe *pipe = container_of(p, struct uhci_pipe, pipe); + struct uhci_td *td = GET_LOWFLAT(pipe->next_td); + u32 status = GET_LOWFLAT(td->status); + u32 token = GET_LOWFLAT(td->token); + if (status & TD_CTRL_ACTIVE) + // No intrs found. + return -1; + // XXX - check for errors. + + // Copy data. + void *tddata = GET_LOWFLAT(td->buffer); + memcpy_far(GET_SEG(SS), data, SEG_LOW, LOWFLAT2LOW(tddata) + , uhci_expected_length(token)); + + // Reenable this td. + struct uhci_td *next = (void*)(GET_LOWFLAT(td->link) & ~UHCI_PTR_BITS); + SET_LOWFLAT(pipe->next_td, next); + barrier(); + SET_LOWFLAT(td->status, (uhci_maxerr(0) | (status & TD_CTRL_LS) + | TD_CTRL_ACTIVE)); + + return 0; +} diff --git a/roms/seabios/src/hw/usb-uhci.h b/roms/seabios/src/hw/usb-uhci.h new file mode 100644 index 000000000..b83c48711 --- /dev/null +++ b/roms/seabios/src/hw/usb-uhci.h @@ -0,0 +1,128 @@ +#ifndef __USB_UHCI_H +#define __USB_UHCI_H + +// usb-uhci.c +void uhci_setup(struct pci_device *pci, int busid); +struct usbdevice_s; +struct usb_endpoint_descriptor; +struct usb_pipe *uhci_alloc_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc); +struct usb_pipe; +int uhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize + , void *data, int datasize); +int uhci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize); +int uhci_poll_intr(struct usb_pipe *p, void *data); + + +/**************************************************************** + * uhci structs and flags + ****************************************************************/ + +/* USB port status and control registers */ +#define USBPORTSC1 16 +#define USBPORTSC2 18 +#define USBPORTSC_CCS 0x0001 /* Current Connect Status + * ("device present") */ +#define USBPORTSC_CSC 0x0002 /* Connect Status Change */ +#define USBPORTSC_PE 0x0004 /* Port Enable */ +#define USBPORTSC_PEC 0x0008 /* Port Enable Change */ +#define USBPORTSC_DPLUS 0x0010 /* D+ high (line status) */ +#define USBPORTSC_DMINUS 0x0020 /* D- high (line status) */ +#define USBPORTSC_RD 0x0040 /* Resume Detect */ +#define USBPORTSC_RES1 0x0080 /* reserved, always 1 */ +#define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */ +#define USBPORTSC_PR 0x0200 /* Port Reset */ + +/* Legacy support register */ +#define USBLEGSUP 0xc0 +#define USBLEGSUP_RWC 0x8f00 /* the R/WC bits */ + +/* Command register */ +#define USBCMD 0 +#define USBCMD_RS 0x0001 /* Run/Stop */ +#define USBCMD_HCRESET 0x0002 /* Host reset */ +#define USBCMD_GRESET 0x0004 /* Global reset */ +#define USBCMD_EGSM 0x0008 /* Global Suspend Mode */ +#define USBCMD_FGR 0x0010 /* Force Global Resume */ +#define USBCMD_SWDBG 0x0020 /* SW Debug mode */ +#define USBCMD_CF 0x0040 /* Config Flag (sw only) */ +#define USBCMD_MAXP 0x0080 /* Max Packet (0 = 32, 1 = 64) */ + +/* Status register */ +#define USBSTS 2 +#define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */ +#define USBSTS_ERROR 0x0002 /* Interrupt due to error */ +#define USBSTS_RD 0x0004 /* Resume Detect */ +#define USBSTS_HSE 0x0008 /* Host System Error: PCI problems */ +#define USBSTS_HCPE 0x0010 /* Host Controller Process Error: + * the schedule is buggy */ +#define USBSTS_HCH 0x0020 /* HC Halted */ + +/* Interrupt enable register */ +#define USBINTR 4 +#define USBINTR_TIMEOUT 0x0001 /* Timeout/CRC error enable */ +#define USBINTR_RESUME 0x0002 /* Resume interrupt enable */ +#define USBINTR_IOC 0x0004 /* Interrupt On Complete enable */ +#define USBINTR_SP 0x0008 /* Short packet interrupt enable */ + +#define USBFRNUM 6 +#define USBFLBASEADD 8 +#define USBSOF 12 +#define USBSOF_DEFAULT 64 /* Frame length is exactly 1 ms */ + +struct uhci_framelist { + u32 links[1024]; +} PACKED; + +#define TD_CTRL_SPD (1 << 29) /* Short Packet Detect */ +#define TD_CTRL_C_ERR_MASK (3 << 27) /* Error Counter bits */ +#define TD_CTRL_C_ERR_SHIFT 27 +#define TD_CTRL_LS (1 << 26) /* Low Speed Device */ +#define TD_CTRL_IOS (1 << 25) /* Isochronous Select */ +#define TD_CTRL_IOC (1 << 24) /* Interrupt on Complete */ +#define TD_CTRL_ACTIVE (1 << 23) /* TD Active */ +#define TD_CTRL_STALLED (1 << 22) /* TD Stalled */ +#define TD_CTRL_DBUFERR (1 << 21) /* Data Buffer Error */ +#define TD_CTRL_BABBLE (1 << 20) /* Babble Detected */ +#define TD_CTRL_NAK (1 << 19) /* NAK Received */ +#define TD_CTRL_CRCTIMEO (1 << 18) /* CRC/Time Out Error */ +#define TD_CTRL_BITSTUFF (1 << 17) /* Bit Stuff Error */ +#define TD_CTRL_ACTLEN_MASK 0x7FF /* actual length, encoded as n - 1 */ + +#define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \ + TD_CTRL_BABBLE | TD_CTRL_CRCTIMEO | \ + TD_CTRL_BITSTUFF) +#define uhci_maxerr(err) ((err) << TD_CTRL_C_ERR_SHIFT) + +#define TD_TOKEN_DEVADDR_SHIFT 8 +#define TD_TOKEN_TOGGLE_SHIFT 19 +#define TD_TOKEN_TOGGLE (1 << 19) +#define TD_TOKEN_EXPLEN_SHIFT 21 +#define TD_TOKEN_EXPLEN_MASK 0x7FF /* expected length, encoded as n-1 */ +#define TD_TOKEN_PID_MASK 0xFF + +#define uhci_explen(len) ((((len) - 1) & TD_TOKEN_EXPLEN_MASK) << \ + TD_TOKEN_EXPLEN_SHIFT) + +#define uhci_expected_length(token) ((((token) >> TD_TOKEN_EXPLEN_SHIFT) + \ + 1) & TD_TOKEN_EXPLEN_MASK) + +struct uhci_td { + u32 link; + u32 status; + u32 token; + void *buffer; +} PACKED; + +struct uhci_qh { + u32 link; + u32 element; +} PACKED; + +#define UHCI_PTR_BITS 0x000F +#define UHCI_PTR_TERM 0x0001 +#define UHCI_PTR_QH 0x0002 +#define UHCI_PTR_DEPTH 0x0004 +#define UHCI_PTR_BREADTH 0x0000 + +#endif // usb-uhci.h diff --git a/roms/seabios/src/hw/usb-xhci.c b/roms/seabios/src/hw/usb-xhci.c new file mode 100644 index 000000000..66ce3c473 --- /dev/null +++ b/roms/seabios/src/hw/usb-xhci.c @@ -0,0 +1,1129 @@ +#include "config.h" // CONFIG_* +#include "output.h" // dprintf +#include "string.h" // memcpy_fl +#include "util.h" // timer_calc +#include "x86.h" // readl +#include "malloc.h" // memalign_low +#include "pci.h" // pci_bdf_to_bus +#include "pci_regs.h" // PCI_BASE_ADDRESS_0 +#include "usb.h" // struct usb_s +#include "usb-xhci.h" // struct ehci_qh +#include "biosvar.h" // GET_LOWFLAT + +// -------------------------------------------------------------- +// configuration + +#define XHCI_RING_ITEMS 16 +#define XHCI_RING_SIZE (XHCI_RING_ITEMS*sizeof(struct xhci_trb)) + +/* + * xhci_ring structs are allocated with XHCI_RING_SIZE alignment, + * then we can get it from a trb pointer (provided by evt ring). + */ +#define XHCI_RING(_trb) \ + ((struct xhci_ring*)((u32)(_trb) & ~(XHCI_RING_SIZE-1))) + +// -------------------------------------------------------------- +// bit definitions + +#define XHCI_CMD_RS (1<<0) +#define XHCI_CMD_HCRST (1<<1) +#define XHCI_CMD_INTE (1<<2) +#define XHCI_CMD_HSEE (1<<3) +#define XHCI_CMD_LHCRST (1<<7) +#define XHCI_CMD_CSS (1<<8) +#define XHCI_CMD_CRS (1<<9) +#define XHCI_CMD_EWE (1<<10) +#define XHCI_CMD_EU3S (1<<11) + +#define XHCI_STS_HCH (1<<0) +#define XHCI_STS_HSE (1<<2) +#define XHCI_STS_EINT (1<<3) +#define XHCI_STS_PCD (1<<4) +#define XHCI_STS_SSS (1<<8) +#define XHCI_STS_RSS (1<<9) +#define XHCI_STS_SRE (1<<10) +#define XHCI_STS_CNR (1<<11) +#define XHCI_STS_HCE (1<<12) + +#define XHCI_PORTSC_CCS (1<<0) +#define XHCI_PORTSC_PED (1<<1) +#define XHCI_PORTSC_OCA (1<<3) +#define XHCI_PORTSC_PR (1<<4) +#define XHCI_PORTSC_PLS_SHIFT 5 +#define XHCI_PORTSC_PLS_MASK 0xf +#define XHCI_PORTSC_PP (1<<9) +#define XHCI_PORTSC_SPEED_SHIFT 10 +#define XHCI_PORTSC_SPEED_MASK 0xf +#define XHCI_PORTSC_SPEED_FULL (1<<10) +#define XHCI_PORTSC_SPEED_LOW (2<<10) +#define XHCI_PORTSC_SPEED_HIGH (3<<10) +#define XHCI_PORTSC_SPEED_SUPER (4<<10) +#define XHCI_PORTSC_PIC_SHIFT 14 +#define XHCI_PORTSC_PIC_MASK 0x3 +#define XHCI_PORTSC_LWS (1<<16) +#define XHCI_PORTSC_CSC (1<<17) +#define XHCI_PORTSC_PEC (1<<18) +#define XHCI_PORTSC_WRC (1<<19) +#define XHCI_PORTSC_OCC (1<<20) +#define XHCI_PORTSC_PRC (1<<21) +#define XHCI_PORTSC_PLC (1<<22) +#define XHCI_PORTSC_CEC (1<<23) +#define XHCI_PORTSC_CAS (1<<24) +#define XHCI_PORTSC_WCE (1<<25) +#define XHCI_PORTSC_WDE (1<<26) +#define XHCI_PORTSC_WOE (1<<27) +#define XHCI_PORTSC_DR (1<<30) +#define XHCI_PORTSC_WPR (1<<31) + +#define TRB_C (1<<0) +#define TRB_TYPE_SHIFT 10 +#define TRB_TYPE_MASK 0x3f +#define TRB_TYPE(t) (((t) >> TRB_TYPE_SHIFT) & TRB_TYPE_MASK) + +#define TRB_EV_ED (1<<2) + +#define TRB_TR_ENT (1<<1) +#define TRB_TR_ISP (1<<2) +#define TRB_TR_NS (1<<3) +#define TRB_TR_CH (1<<4) +#define TRB_TR_IOC (1<<5) +#define TRB_TR_IDT (1<<6) +#define TRB_TR_TBC_SHIFT 7 +#define TRB_TR_TBC_MASK 0x3 +#define TRB_TR_BEI (1<<9) +#define TRB_TR_TLBPC_SHIFT 16 +#define TRB_TR_TLBPC_MASK 0xf +#define TRB_TR_FRAMEID_SHIFT 20 +#define TRB_TR_FRAMEID_MASK 0x7ff +#define TRB_TR_SIA (1<<31) + +#define TRB_TR_DIR (1<<16) + +#define TRB_CR_SLOTID_SHIFT 24 +#define TRB_CR_SLOTID_MASK 0xff +#define TRB_CR_EPID_SHIFT 16 +#define TRB_CR_EPID_MASK 0x1f + +#define TRB_CR_BSR (1<<9) +#define TRB_CR_DC (1<<9) + +#define TRB_LK_TC (1<<1) + +#define TRB_INTR_SHIFT 22 +#define TRB_INTR_MASK 0x3ff +#define TRB_INTR(t) (((t).status >> TRB_INTR_SHIFT) & TRB_INTR_MASK) + +typedef enum TRBType { + TRB_RESERVED = 0, + TR_NORMAL, + TR_SETUP, + TR_DATA, + TR_STATUS, + TR_ISOCH, + TR_LINK, + TR_EVDATA, + TR_NOOP, + CR_ENABLE_SLOT, + CR_DISABLE_SLOT, + CR_ADDRESS_DEVICE, + CR_CONFIGURE_ENDPOINT, + CR_EVALUATE_CONTEXT, + CR_RESET_ENDPOINT, + CR_STOP_ENDPOINT, + CR_SET_TR_DEQUEUE, + CR_RESET_DEVICE, + CR_FORCE_EVENT, + CR_NEGOTIATE_BW, + CR_SET_LATENCY_TOLERANCE, + CR_GET_PORT_BANDWIDTH, + CR_FORCE_HEADER, + CR_NOOP, + ER_TRANSFER = 32, + ER_COMMAND_COMPLETE, + ER_PORT_STATUS_CHANGE, + ER_BANDWIDTH_REQUEST, + ER_DOORBELL, + ER_HOST_CONTROLLER, + ER_DEVICE_NOTIFICATION, + ER_MFINDEX_WRAP, +} TRBType; + +typedef enum TRBCCode { + CC_INVALID = 0, + CC_SUCCESS, + CC_DATA_BUFFER_ERROR, + CC_BABBLE_DETECTED, + CC_USB_TRANSACTION_ERROR, + CC_TRB_ERROR, + CC_STALL_ERROR, + CC_RESOURCE_ERROR, + CC_BANDWIDTH_ERROR, + CC_NO_SLOTS_ERROR, + CC_INVALID_STREAM_TYPE_ERROR, + CC_SLOT_NOT_ENABLED_ERROR, + CC_EP_NOT_ENABLED_ERROR, + CC_SHORT_PACKET, + CC_RING_UNDERRUN, + CC_RING_OVERRUN, + CC_VF_ER_FULL, + CC_PARAMETER_ERROR, + CC_BANDWIDTH_OVERRUN, + CC_CONTEXT_STATE_ERROR, + CC_NO_PING_RESPONSE_ERROR, + CC_EVENT_RING_FULL_ERROR, + CC_INCOMPATIBLE_DEVICE_ERROR, + CC_MISSED_SERVICE_ERROR, + CC_COMMAND_RING_STOPPED, + CC_COMMAND_ABORTED, + CC_STOPPED, + CC_STOPPED_LENGTH_INVALID, + CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR = 29, + CC_ISOCH_BUFFER_OVERRUN = 31, + CC_EVENT_LOST_ERROR, + CC_UNDEFINED_ERROR, + CC_INVALID_STREAM_ID_ERROR, + CC_SECONDARY_BANDWIDTH_ERROR, + CC_SPLIT_TRANSACTION_ERROR +} TRBCCode; + +enum { + PLS_U0 = 0, + PLS_U1 = 1, + PLS_U2 = 2, + PLS_U3 = 3, + PLS_DISABLED = 4, + PLS_RX_DETECT = 5, + PLS_INACTIVE = 6, + PLS_POLLING = 7, + PLS_RECOVERY = 8, + PLS_HOT_RESET = 9, + PLS_COMPILANCE_MODE = 10, + PLS_TEST_MODE = 11, + PLS_RESUME = 15, +}; + +#define xhci_get_field(data, field) \ + (((data) >> field##_SHIFT) & field##_MASK) + +// -------------------------------------------------------------- +// state structs + +struct xhci_ring { + struct xhci_trb ring[XHCI_RING_ITEMS]; + struct xhci_trb evt; + u32 eidx; + u32 nidx; + u32 cs; + struct mutex_s lock; +}; + +struct usb_xhci_s { + struct usb_s usb; + struct usbhub_s hub; + + /* devinfo */ + u32 baseaddr; + u32 xcap; + u32 ports; + u32 slots; + + /* xhci registers */ + struct xhci_caps *caps; + struct xhci_op *op; + struct xhci_pr *pr; + struct xhci_ir *ir; + struct xhci_db *db; + + /* xhci data structures */ + struct xhci_devlist *devs; + struct xhci_ring *cmds; + struct xhci_ring *evts; + struct xhci_er_seg *eseg; + + /* usb devices */ + struct hlist_head list; +}; + +struct xhci_device { + struct xhci_devctx devctx; + struct xhci_inctx inctx; + + struct usbdevice_s *usbdev; + struct usb_xhci_s *xhci; + u32 slotid; + struct hlist_node next; +}; + +struct xhci_pipe { + struct xhci_ring reqs; + + struct usb_pipe pipe; + struct xhci_device *dev; + u32 epid; + void *buf; + int bufused; +}; + +// -------------------------------------------------------------- +// tables + +static const char *speed_name[16] = { + [ 0 ] = " - ", + [ 1 ] = "Full", + [ 2 ] = "Low", + [ 3 ] = "High", + [ 4 ] = "Super", +}; + +static const int speed_from_xhci[16] = { + [ 0 ] = -1, + [ 1 ] = USB_FULLSPEED, + [ 2 ] = USB_LOWSPEED, + [ 3 ] = USB_HIGHSPEED, + [ 4 ] = USB_SUPERSPEED, + [ 5 ... 15 ] = -1, +}; + +static const int speed_to_xhci[] = { + [ USB_FULLSPEED ] = 1, + [ USB_LOWSPEED ] = 2, + [ USB_HIGHSPEED ] = 3, + [ USB_SUPERSPEED ] = 4, +}; + +static const int speed_to_ctlsize[] = { + [ USB_FULLSPEED ] = 8, + [ USB_LOWSPEED ] = 8, + [ USB_HIGHSPEED ] = 64, + [ USB_SUPERSPEED ] = 256, +}; + +static const int eptype_to_xhci_in[] = { + [ USB_ENDPOINT_XFER_CONTROL] = 4, + [ USB_ENDPOINT_XFER_ISOC ] = 5, + [ USB_ENDPOINT_XFER_BULK ] = 6, + [ USB_ENDPOINT_XFER_INT ] = 7, +}; + +static const int eptype_to_xhci_out[] = { + [ USB_ENDPOINT_XFER_CONTROL] = 4, + [ USB_ENDPOINT_XFER_ISOC ] = 1, + [ USB_ENDPOINT_XFER_BULK ] = 2, + [ USB_ENDPOINT_XFER_INT ] = 3, +}; + +// -------------------------------------------------------------- +// internal functions, 16bit + 32bit + +static void xhci_doorbell(struct usb_xhci_s *xhci, u32 slotid, u32 value) +{ + struct xhci_db *db = GET_LOWFLAT(xhci->db); + u32 addr = (u32)(&db[slotid].doorbell); + pci_writel(addr, value); +} + +static void xhci_process_events(struct usb_xhci_s *xhci) +{ + struct xhci_ring *evts = GET_LOWFLAT(xhci->evts); + + for (;;) { + /* check for event */ + u32 nidx = GET_LOWFLAT(evts->nidx); + u32 cs = GET_LOWFLAT(evts->cs); + struct xhci_trb *etrb = evts->ring + nidx; + u32 control = GET_LOWFLAT(etrb->control); + if ((control & TRB_C) != (cs ? 1 : 0)) + return; + + /* process event */ + u32 evt_type = TRB_TYPE(control); + u32 evt_cc = (GET_LOWFLAT(etrb->status) >> 24) & 0xff; + switch (evt_type) { + case ER_TRANSFER: + case ER_COMMAND_COMPLETE: + { + struct xhci_trb *rtrb = (void*)GET_LOWFLAT(etrb->ptr_low); + struct xhci_ring *ring = XHCI_RING(rtrb); + struct xhci_trb *evt = &ring->evt; + u32 eidx = rtrb - ring->ring + 1; + dprintf(5, "%s: ring %p [trb %p, evt %p, type %d, eidx %d, cc %d]\n", + __func__, ring, rtrb, evt, evt_type, eidx, evt_cc); + memcpy_fl(evt, etrb, sizeof(*etrb)); + SET_LOWFLAT(ring->eidx, eidx); + break; + } + case ER_PORT_STATUS_CHANGE: + { + u32 portid = (GET_LOWFLAT(etrb->ptr_low) >> 24) & 0xff; + dprintf(3, "%s: status change port #%d\n", + __func__, portid); + break; + } + default: + dprintf(1, "%s: unknown event, type %d, cc %d\n", + __func__, evt_type, evt_cc); + break; + } + + /* move ring index, notify xhci */ + nidx++; + if (nidx == XHCI_RING_ITEMS) { + nidx = 0; + cs = cs ? 0 : 1; + SET_LOWFLAT(evts->cs, cs); + } + SET_LOWFLAT(evts->nidx, nidx); + struct xhci_ir *ir = GET_LOWFLAT(xhci->ir); + u32 addr = (u32)(&ir->erdp_low); + u32 erdp = (u32)(evts->ring + nidx); + pci_writel(addr, erdp); + } +} + +static int xhci_ring_busy(struct xhci_ring *ring) +{ + u32 eidx = GET_LOWFLAT(ring->eidx); + u32 nidx = GET_LOWFLAT(ring->nidx); + return (eidx != nidx); +} + +static int xhci_event_wait(struct usb_xhci_s *xhci, + struct xhci_ring *ring, + u32 timeout) +{ + u32 end = timer_calc(timeout); + + for (;;) { + xhci_process_events(xhci); + if (!xhci_ring_busy(ring)) { + u32 status = GET_LOWFLAT(ring->evt.status); + return (status >> 24) & 0xff; + } + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } +} + +static void xhci_trb_queue(struct xhci_ring *ring, + struct xhci_trb *trb) +{ + u32 nidx = GET_LOWFLAT(ring->nidx); + u32 cs = GET_LOWFLAT(ring->cs); + struct xhci_trb *dst; + u32 control; + + if (nidx == XHCI_RING_ITEMS-1) { + dst = ring->ring + nidx; + control = (TR_LINK << 10); // trb type + control |= TRB_LK_TC; + control |= (cs ? TRB_C : 0); + SET_LOWFLAT(dst->ptr_low, (u32)&ring[0]); + SET_LOWFLAT(dst->ptr_high, 0); + SET_LOWFLAT(dst->status, 0); + SET_LOWFLAT(dst->control, control); + nidx = 0; + cs = cs ? 0 : 1; + SET_LOWFLAT(ring->nidx, nidx); + SET_LOWFLAT(ring->cs, cs); + + dprintf(5, "%s: ring %p [linked]\n", __func__, ring); + } + + dst = ring->ring + nidx; + control = GET_LOWFLAT(trb->control) | (cs ? TRB_C : 0); + + SET_LOWFLAT(dst->ptr_low, GET_LOWFLAT(trb->ptr_low)); + SET_LOWFLAT(dst->ptr_high, GET_LOWFLAT(trb->ptr_high)); + SET_LOWFLAT(dst->status, GET_LOWFLAT(trb->status)); + SET_LOWFLAT(dst->control, control); + nidx++; + SET_LOWFLAT(ring->nidx, nidx); + + dprintf(5, "%s: ring %p [nidx %d, len %d]\n", + __func__, ring, nidx, + GET_LOWFLAT(trb->status) & 0xffff); +} + +static void xhci_xfer_queue(struct xhci_pipe *pipe, + struct xhci_trb *trb) +{ + xhci_trb_queue(&pipe->reqs, trb); +} + +static void xhci_xfer_kick(struct xhci_pipe *pipe) +{ + struct xhci_device *dev = GET_LOWFLAT(pipe->dev); + struct usb_xhci_s *xhci = GET_LOWFLAT(dev->xhci); + u32 slotid = GET_LOWFLAT(dev->slotid); + u32 epid = GET_LOWFLAT(pipe->epid); + + dprintf(5, "%s: ring %p, slotid %d, epid %d\n", + __func__, &pipe->reqs, slotid, epid); + xhci_doorbell(xhci, slotid, epid); +} + +static void xhci_xfer_normal(struct xhci_pipe *pipe, + void *data, int datalen) +{ + struct xhci_trb trb; + + memset(&trb, 0, sizeof(trb)); + trb.ptr_low = (u32)data; + trb.status = datalen; + trb.control |= (TR_NORMAL << 10); // trb type + trb.control |= TRB_TR_IOC; + + xhci_xfer_queue(pipe, MAKE_FLATPTR(GET_SEG(SS), &trb)); + xhci_xfer_kick(pipe); +} + +// -------------------------------------------------------------- +// internal functions, pure 32bit + +static int wait_bit(u32 *reg, u32 mask, int value, u32 timeout) +{ + ASSERT32FLAT(); + u32 end = timer_calc(timeout); + + while ((readl(reg) & mask) != value) { + if (timer_check(end)) { + warn_timeout(); + return -1; + } + yield(); + } + return 0; +} + +static int xhci_cmd_submit(struct usb_xhci_s *xhci, + struct xhci_trb *cmd) +{ + ASSERT32FLAT(); + int rc; + + mutex_lock(&xhci->cmds->lock); + xhci_trb_queue(xhci->cmds, cmd); + xhci_doorbell(xhci, 0, 0); + rc = xhci_event_wait(xhci, xhci->cmds, 1000); + mutex_unlock(&xhci->cmds->lock); + return rc; +} + +static int xhci_cmd_enable_slot(struct usb_xhci_s *xhci) +{ + ASSERT32FLAT(); + struct xhci_trb cmd = { + .ptr_low = 0, + .ptr_high = 0, + .status = 0, + .control = (CR_ENABLE_SLOT << 10) + }; + dprintf(3, "%s:\n", __func__); + int cc = xhci_cmd_submit(xhci, &cmd); + if (cc != CC_SUCCESS) + return -1; + return (xhci->cmds->evt.control >> 24) & 0xff; +} + +static int xhci_cmd_disable_slot(struct xhci_device *dev) +{ + ASSERT32FLAT(); + struct xhci_trb cmd = { + .ptr_low = 0, + .ptr_high = 0, + .status = 0, + .control = (dev->slotid << 24) | (CR_DISABLE_SLOT << 10) + }; + dprintf(3, "%s: slotid %d\n", __func__, dev->slotid); + return xhci_cmd_submit(dev->xhci, &cmd); +} + +static int xhci_cmd_address_device(struct xhci_device *dev) +{ + ASSERT32FLAT(); + struct xhci_trb cmd = { + .ptr_low = (u32)&dev->inctx, + .ptr_high = 0, + .status = 0, + .control = (dev->slotid << 24) | (CR_ADDRESS_DEVICE << 10) + }; + dprintf(3, "%s: slotid %d\n", __func__, dev->slotid); + return xhci_cmd_submit(dev->xhci, &cmd); +} + +static int xhci_cmd_configure_endpoint(struct xhci_device *dev) +{ + ASSERT32FLAT(); + struct xhci_trb cmd = { + .ptr_low = (u32)&dev->inctx, + .ptr_high = 0, + .status = 0, + .control = (dev->slotid << 24) | (CR_CONFIGURE_ENDPOINT << 10) + }; + dprintf(3, "%s: slotid %d, add 0x%x, del 0x%x\n", __func__, + dev->slotid, dev->inctx.add, dev->inctx.del); + return xhci_cmd_submit(dev->xhci, &cmd); +} + +static int xhci_cmd_evaluate_context(struct xhci_device *dev) +{ + ASSERT32FLAT(); + struct xhci_trb cmd = { + .ptr_low = (u32)&dev->inctx, + .ptr_high = 0, + .status = 0, + .control = (dev->slotid << 24) | (CR_EVALUATE_CONTEXT << 10) + }; + dprintf(3, "%s: slotid %d, add 0x%x, del 0x%x\n", __func__, + dev->slotid, dev->inctx.add, dev->inctx.del); + return xhci_cmd_submit(dev->xhci, &cmd); +} + +static void xhci_xfer_setup(struct xhci_pipe *pipe, + const struct usb_ctrlrequest *req, + int dir, int datalen) +{ + ASSERT32FLAT(); + struct xhci_trb trb; + + memset(&trb, 0, sizeof(trb)); + trb.ptr_low |= req->bRequestType; + trb.ptr_low |= (req->bRequest) << 8; + trb.ptr_low |= (req->wValue) << 16; + trb.ptr_high |= req->wIndex; + trb.ptr_high |= (req->wLength) << 16; + trb.status |= 8; // length + trb.control |= (TR_SETUP << 10); // trb type + trb.control |= TRB_TR_IDT; + if (datalen) + trb.control |= (dir ? 3 : 2) << 16; // transfer type + xhci_xfer_queue(pipe, &trb); +} + +static void xhci_xfer_data(struct xhci_pipe *pipe, + int dir, void *data, int datalen) +{ + ASSERT32FLAT(); + struct xhci_trb trb; + + memset(&trb, 0, sizeof(trb)); + trb.ptr_low = (u32)data; + trb.status = datalen; + trb.control |= (TR_DATA << 10); // trb type + if (dir) + trb.control |= (1 << 16); + xhci_xfer_queue(pipe, &trb); +} + +static void xhci_xfer_status(struct xhci_pipe *pipe, int dir) +{ + ASSERT32FLAT(); + struct xhci_trb trb; + + memset(&trb, 0, sizeof(trb)); + trb.control |= (TR_STATUS << 10); // trb type + trb.control |= TRB_TR_IOC; + if (dir) + trb.control |= (1 << 16); + + xhci_xfer_queue(pipe, &trb); + xhci_xfer_kick(pipe); +} + +static struct xhci_device *xhci_find_alloc_device(struct usb_xhci_s *xhci, + struct usbdevice_s *usbdev) +{ + ASSERT32FLAT(); + struct xhci_device *dev; + + hlist_for_each_entry(dev, &xhci->list, next) { + if (dev->usbdev == usbdev) { + return dev; + } + } + + dev = memalign_low(64, sizeof(*dev)); + if (!dev) { + warn_noalloc(); + return NULL; + } + memset(dev, 0, sizeof(*dev)); + dev->usbdev = usbdev; + dev->xhci = xhci; + hlist_add_head(&dev->next, &xhci->list); + return dev; +} + +static void +configure_xhci(void *data) +{ + ASSERT32FLAT(); + struct usb_xhci_s *xhci = data; + u32 reg; + + xhci->devs = memalign_high(64, sizeof(*xhci->devs) * (xhci->slots + 1)); + xhci->eseg = memalign_high(64, sizeof(*xhci->eseg)); + xhci->cmds = memalign_high(XHCI_RING_SIZE, sizeof(*xhci->cmds)); + xhci->evts = memalign_low(XHCI_RING_SIZE, sizeof(*xhci->evts)); + if (!xhci->devs || !xhci->cmds || !xhci->evts || !xhci->eseg) { + warn_noalloc(); + goto fail; + } + memset(xhci->devs, 0, sizeof(*xhci->devs) * (xhci->slots + 1)); + memset(xhci->cmds, 0, sizeof(*xhci->cmds)); + memset(xhci->evts, 0, sizeof(*xhci->evts)); + memset(xhci->eseg, 0, sizeof(*xhci->eseg)); + + reg = readl(&xhci->op->usbcmd); + if (reg & XHCI_CMD_RS) { + reg &= ~XHCI_CMD_RS; + writel(&xhci->op->usbcmd, reg); + if (wait_bit(&xhci->op->usbsts, XHCI_STS_HCH, XHCI_STS_HCH, 32) != 0) + goto fail; + } + + dprintf(3, "%s: resetting\n", __func__); + writel(&xhci->op->usbcmd, XHCI_CMD_HCRST); + if (wait_bit(&xhci->op->usbcmd, XHCI_CMD_HCRST, 0, 100) != 0) + goto fail; + if (wait_bit(&xhci->op->usbsts, XHCI_STS_CNR, 0, 100) != 0) + goto fail; + + writel(&xhci->op->config, xhci->slots); + writel(&xhci->op->dcbaap_low, (u32)xhci->devs); + writel(&xhci->op->dcbaap_high, 0); + writel(&xhci->op->crcr_low, (u32)xhci->cmds | 1); + writel(&xhci->op->crcr_high, 0); + xhci->cmds->cs = 1; + + xhci->eseg->ptr_low = (u32)xhci->evts; + xhci->eseg->ptr_high = 0; + xhci->eseg->size = XHCI_RING_ITEMS; + writel(&xhci->ir->erstsz, 1); + writel(&xhci->ir->erdp_low, (u32)xhci->evts); + writel(&xhci->ir->erdp_high, 0); + writel(&xhci->ir->erstba_low, (u32)xhci->eseg); + writel(&xhci->ir->erstba_high, 0); + xhci->evts->cs = 1; + + reg = readl(&xhci->op->usbcmd); + reg |= XHCI_CMD_RS; + writel(&xhci->op->usbcmd, reg); + + // FIXME: try find a more elegant way than a fixed delay + mdelay(100); + + usb_enumerate(&xhci->hub); + if (xhci->hub.devcount) + return; + + // No devices found - shutdown and free controller. + dprintf(1, "XHCI no devices found\n"); + reg = readl(&xhci->op->usbcmd); + reg &= ~XHCI_CMD_RS; + writel(&xhci->op->usbcmd, reg); + wait_bit(&xhci->op->usbsts, XHCI_STS_HCH, XHCI_STS_HCH, 32); + +fail: + free(xhci->eseg); + free(xhci->evts); + free(xhci->cmds); + free(xhci->devs); + free(xhci); +} + +// -------------------------------------------------------------- +// xhci root hub + +// Check if device attached to port +static void +xhci_print_port_state(int loglevel, const char *prefix, u32 port, u32 portsc) +{ + ASSERT32FLAT(); + u32 pls = xhci_get_field(portsc, XHCI_PORTSC_PLS); + u32 speed = xhci_get_field(portsc, XHCI_PORTSC_SPEED); + + dprintf(loglevel, "%s port #%d: 0x%08x,%s%s pls %d, speed %d [%s]\n", + prefix, port + 1, portsc, + (portsc & XHCI_PORTSC_PP) ? " powered," : "", + (portsc & XHCI_PORTSC_PED) ? " enabled," : "", + pls, speed, speed_name[speed]); +} + +static int +xhci_hub_detect(struct usbhub_s *hub, u32 port) +{ + ASSERT32FLAT(); + struct usb_xhci_s *xhci = container_of(hub->cntl, struct usb_xhci_s, usb); + u32 portsc = readl(&xhci->pr[port].portsc); + + xhci_print_port_state(3, __func__, port, portsc); + switch (xhci_get_field(portsc, XHCI_PORTSC_PLS)) { + case PLS_U0: + case PLS_POLLING: + return 0; + default: + return -1; + } +} + +// Reset device on port +static int +xhci_hub_reset(struct usbhub_s *hub, u32 port) +{ + ASSERT32FLAT(); + struct usb_xhci_s *xhci = container_of(hub->cntl, struct usb_xhci_s, usb); + u32 portsc = readl(&xhci->pr[port].portsc); + int rc; + + switch (xhci_get_field(portsc, XHCI_PORTSC_PLS)) { + case PLS_U0: + rc = speed_from_xhci[xhci_get_field(portsc, XHCI_PORTSC_SPEED)]; + break; + case PLS_POLLING: + xhci_print_port_state(3, __func__, port, portsc); + portsc |= XHCI_PORTSC_PR; + writel(&xhci->pr[port].portsc, portsc); + if (wait_bit(&xhci->pr[port].portsc, XHCI_PORTSC_PED, XHCI_PORTSC_PED, 100) != 0) + return -1; + portsc = readl(&xhci->pr[port].portsc); + rc = speed_from_xhci[xhci_get_field(portsc, XHCI_PORTSC_SPEED)]; + break; + default: + rc = -1; + break; + } + + xhci_print_port_state(1, "XHCI", port, portsc); + return rc; +} + +static void +xhci_hub_disconnect(struct usbhub_s *hub, u32 port) +{ + ASSERT32FLAT(); + struct usb_xhci_s *xhci = container_of(hub->cntl, struct usb_xhci_s, usb); + struct xhci_device *dev; + + hlist_for_each_entry(dev, &xhci->list, next) { + if (dev->usbdev->hub == hub && + dev->usbdev->port == port && + dev->slotid != 0) { + xhci_cmd_disable_slot(dev); + hlist_del(&dev->next); + return; + } + } +} + +static struct usbhub_op_s xhci_hub_ops = { + .detect = xhci_hub_detect, + .reset = xhci_hub_reset, + .disconnect = xhci_hub_disconnect, +}; + +// -------------------------------------------------------------- +// external interface + +struct usb_pipe * +xhci_alloc_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + ASSERT32FLAT(); + if (!CONFIG_USB_XHCI) + return NULL; + u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + struct usb_xhci_s *xhci = container_of( + usbdev->hub->cntl, struct usb_xhci_s, usb); + struct xhci_pipe *pipe; + u32 epid; + + if (epdesc->bEndpointAddress == 0) { + epid = 1; + } else { + epid = (epdesc->bEndpointAddress & 0x0f) * 2; + epid += (epdesc->bEndpointAddress & USB_DIR_IN) ? 1 : 0; + } + + if (eptype == USB_ENDPOINT_XFER_CONTROL) + pipe = memalign_high(XHCI_RING_SIZE, sizeof(*pipe)); + else + pipe = memalign_low(XHCI_RING_SIZE, sizeof(*pipe)); + if (!pipe) { + warn_noalloc(); + return NULL; + } + memset(pipe, 0, sizeof(*pipe)); + + usb_desc2pipe(&pipe->pipe, usbdev, epdesc); + pipe->dev = xhci_find_alloc_device(xhci, usbdev); + if (!pipe->dev) { + free(pipe); + return NULL; + } + pipe->epid = epid; + pipe->reqs.cs = 1; + if (eptype == USB_ENDPOINT_XFER_INT) + pipe->buf = malloc_low(pipe->pipe.maxpacket); + + dprintf(3, "%s: usbdev %p, ring %p, slotid %d, epid %d\n", __func__, + usbdev, &pipe->reqs, pipe->dev->slotid, pipe->epid); + if (pipe->epid > 1 && pipe->dev->slotid) { + struct xhci_inctx *in = &pipe->dev->inctx; + in->add = (1 << pipe->epid) | 1; + in->del = 0; + + in->slot.ctx[0] |= (31 << 27); // context entries + + int e = pipe->epid-1; + in->ep[e].ctx[1] |= (eptype << 3); + if (epdesc->bEndpointAddress & USB_DIR_IN) + in->ep[e].ctx[1] |= (1 << 5); + in->ep[e].ctx[1] |= (pipe->pipe.maxpacket << 16); + in->ep[e].deq_low = (u32)&pipe->reqs.ring[0]; + in->ep[e].deq_low |= 1; // dcs + in->ep[e].deq_high = 0; + in->ep[e].length = pipe->pipe.maxpacket; + + int cc = xhci_cmd_configure_endpoint(pipe->dev); + if (cc != CC_SUCCESS) { + dprintf(1, "%s: configure endpoint: failed (cc %d)\n", __func__, cc); + free(pipe); + return NULL; + } + } + + return &pipe->pipe; +} + +struct usb_pipe * +xhci_update_pipe(struct usbdevice_s *usbdev, struct usb_pipe *upipe + , struct usb_endpoint_descriptor *epdesc) +{ + ASSERT32FLAT(); + if (!CONFIG_USB_XHCI) + return NULL; + u8 eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + struct xhci_pipe *pipe = container_of(upipe, struct xhci_pipe, pipe); + dprintf(3, "%s: usbdev %p, ring %p, slotid %d, epid %d\n", __func__, + usbdev, &pipe->reqs, pipe->dev->slotid, pipe->epid); + if (eptype == USB_ENDPOINT_XFER_CONTROL && + pipe->pipe.maxpacket != epdesc->wMaxPacketSize) { + dprintf(1, "%s: reconf ctl endpoint pkt size: %d -> %d\n", + __func__, pipe->pipe.maxpacket, epdesc->wMaxPacketSize); + pipe->pipe.maxpacket = epdesc->wMaxPacketSize; + struct xhci_inctx *in = &pipe->dev->inctx; + in->add = (1 << 1); + in->del = 0; + in->ep[0].ctx[1] &= 0xffff; + in->ep[0].ctx[1] |= (pipe->pipe.maxpacket << 16); + int cc = xhci_cmd_evaluate_context(pipe->dev); + if (cc != CC_SUCCESS) { + dprintf(1, "%s: reconf ctl endpoint: failed (cc %d)\n", + __func__, cc); + } + } + return upipe; +} + +int +xhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize + , void *data, int datalen) +{ + ASSERT32FLAT(); + if (!CONFIG_USB_XHCI) + return -1; + const struct usb_ctrlrequest *req = cmd; + struct xhci_pipe *pipe = container_of(p, struct xhci_pipe, pipe); + struct usb_xhci_s *xhci = pipe->dev->xhci; + int cc; + + if (req->bRequest == USB_REQ_SET_ADDRESS) { + int slotid = xhci_cmd_enable_slot(xhci); + if (slotid < 0) { + dprintf(1, "%s: enable slot: failed\n", __func__); + return -1; + } + dprintf(3, "%s: enable slot: got slotid %d\n", __func__, slotid); + pipe->dev->slotid = slotid; + xhci->devs[slotid].ptr_low = (u32)&pipe->dev->devctx; + xhci->devs[slotid].ptr_high = 0; + + struct usbdevice_s *usbdev = pipe->dev->usbdev; + u32 route = 0; + while (usbdev->hub->usbdev) { + route <<= 4; + route |= (usbdev->port+1) & 0xf; + usbdev = usbdev->hub->usbdev; + } + dprintf(3, "%s: root port %d, route 0x%x\n", + __func__, usbdev->port+1, route); + + struct xhci_inctx *in = &pipe->dev->inctx; + in->add = 0x03; + in->slot.ctx[0] |= (1 << 27); // context entries + in->slot.ctx[0] |= speed_to_xhci[pipe->dev->usbdev->speed] << 20; + in->slot.ctx[0] |= route; + in->slot.ctx[1] |= (usbdev->port+1) << 16; + /* TODO ctx0: hub bit */ + /* TODO ctx1: hub ports */ + + in->ep[0].ctx[0] |= (3 << 16); // interval: 1ms + in->ep[0].ctx[1] |= (4 << 3); // control pipe + in->ep[0].ctx[1] |= (speed_to_ctlsize[pipe->dev->usbdev->speed] << 16); + + in->ep[0].deq_low = (u32)&pipe->reqs.ring[0]; + in->ep[0].deq_low |= 1; // dcs + in->ep[0].deq_high = 0; + in->ep[0].length = 8; + + cc = xhci_cmd_address_device(pipe->dev); + if (cc != CC_SUCCESS) { + dprintf(1, "%s: address device: failed (cc %d)\n", __func__, cc); + return -1; + } + return 0; + } + + xhci_xfer_setup(pipe, req, dir, datalen); + if (datalen) + xhci_xfer_data(pipe, dir, data, datalen); + xhci_xfer_status(pipe, dir); + + cc = xhci_event_wait(xhci, &pipe->reqs, 1000); + if (cc != CC_SUCCESS) { + dprintf(1, "%s: control xfer failed (cc %d)\n", __func__, cc); + return -1; + } + + return 0; +} + +int +xhci_send_bulk(struct usb_pipe *p, int dir, void *data, int datalen) +{ + if (!CONFIG_USB_XHCI) + return -1; + + struct xhci_pipe *pipe = container_of(p, struct xhci_pipe, pipe); + struct xhci_device *dev = GET_LOWFLAT(pipe->dev); + struct usb_xhci_s *xhci = GET_LOWFLAT(dev->xhci); + + xhci_xfer_normal(pipe, data, datalen); + int cc = xhci_event_wait(xhci, &pipe->reqs, 1000); + if (cc != CC_SUCCESS) { + dprintf(1, "%s: bulk xfer failed (cc %d)\n", __func__, cc); + return -1; + } + return 0; +} + +int +xhci_poll_intr(struct usb_pipe *p, void *data) +{ + if (!CONFIG_USB_XHCI) + return -1; + + struct xhci_pipe *pipe = container_of(p, struct xhci_pipe, pipe); + struct xhci_device *dev = GET_LOWFLAT(pipe->dev); + struct usb_xhci_s *xhci = GET_LOWFLAT(dev->xhci); + u32 len = GET_LOWFLAT(pipe->pipe.maxpacket); + void *buf = GET_LOWFLAT(pipe->buf); + int bufused = GET_LOWFLAT(pipe->bufused); + + if (!bufused) { + xhci_xfer_normal(pipe, buf, len); + bufused = 1; + SET_LOWFLAT(pipe->bufused, bufused); + return -1; + } + + xhci_process_events(xhci); + if (xhci_ring_busy(&pipe->reqs)) + return -1; + dprintf(5, "%s: st %x ct %x [ %p <= %p / %d ]\n", __func__, + GET_LOWFLAT(pipe->reqs.evt.status), + GET_LOWFLAT(pipe->reqs.evt.control), + MAKE_FLATPTR(GET_SEG(SS), data), buf, len); + memcpy_fl(MAKE_FLATPTR(GET_SEG(SS), data), buf, len); + xhci_xfer_normal(pipe, buf, len); + return 0; +} + +int +xhci_setup(struct pci_device *pci, int busid) +{ + ASSERT32FLAT(); + if (!CONFIG_USB_XHCI) + return -1; + + struct usb_xhci_s *xhci = malloc_low(sizeof(*xhci)); + if (!xhci) { + warn_noalloc(); + return -1; + } + memset(xhci, 0, sizeof(*xhci)); + + xhci->baseaddr = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0) + & PCI_BASE_ADDRESS_MEM_MASK; + xhci->caps = (void*)(xhci->baseaddr); + xhci->op = (void*)(xhci->baseaddr + readb(&xhci->caps->caplength)); + xhci->pr = (void*)(xhci->baseaddr + readb(&xhci->caps->caplength) + 0x400); + xhci->db = (void*)(xhci->baseaddr + readl(&xhci->caps->dboff)); + xhci->ir = (void*)(xhci->baseaddr + readl(&xhci->caps->rtsoff) + 0x20); + + u32 hcs1 = readl(&xhci->caps->hcsparams1); + u32 hcc = readl(&xhci->caps->hccparams); + xhci->ports = (hcs1 >> 24) & 0xff; + xhci->slots = hcs1 & 0xff; + xhci->xcap = ((hcc >> 16) & 0xffff) << 2; + + xhci->usb.busid = busid; + xhci->usb.pci = pci; + xhci->usb.type = USB_TYPE_XHCI; + xhci->hub.cntl = &xhci->usb; + xhci->hub.portcount = xhci->ports; + xhci->hub.op = &xhci_hub_ops; + + dprintf(1, "XHCI init on dev %02x:%02x.%x: regs @ %p, %d ports, %d slots\n" + , pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf) + , pci_bdf_to_fn(pci->bdf), xhci->caps + , xhci->ports, xhci->slots); + + if (xhci->xcap) { + u32 off, addr = xhci->baseaddr + xhci->xcap; + do { + struct xhci_xcap *xcap = (void*)addr; + u32 ports, name, cap = readl(&xcap->cap); + switch (cap & 0xff) { + case 0x02: + name = readl(&xcap->data[0]); + ports = readl(&xcap->data[1]); + dprintf(1, "XHCI protocol %c%c%c%c %x.%02x, %d ports (offset %d)\n" + , (name >> 0) & 0xff + , (name >> 8) & 0xff + , (name >> 16) & 0xff + , (name >> 24) & 0xff + , (cap >> 24) & 0xff + , (cap >> 16) & 0xff + , (ports >> 8) & 0xff + , (ports >> 0) & 0xff); + break; + default: + dprintf(1, "XHCI extcap 0x%x @ %x\n", cap & 0xff, addr); + break; + } + off = (cap >> 8) & 0xff; + addr += off << 2; + } while (off > 0); + } + + pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); + + run_thread(configure_xhci, xhci); + return 0; +} diff --git a/roms/seabios/src/hw/usb-xhci.h b/roms/seabios/src/hw/usb-xhci.h new file mode 100644 index 000000000..64ee82c25 --- /dev/null +++ b/roms/seabios/src/hw/usb-xhci.h @@ -0,0 +1,144 @@ +#ifndef __USB_XHCI_H +#define __USB_XHCI_H + +struct usbdevice_s; +struct usb_endpoint_descriptor; +struct usb_pipe; + +// -------------------------------------------------------------- + +// usb-xhci.c +int xhci_setup(struct pci_device *pci, int busid); +struct usb_pipe *xhci_alloc_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc); +struct usb_pipe *xhci_update_pipe(struct usbdevice_s *usbdev + , struct usb_pipe *pipe + , struct usb_endpoint_descriptor *epdesc); +int xhci_control(struct usb_pipe *p, int dir, const void *cmd, int cmdsize + , void *data, int datasize); +int xhci_send_bulk(struct usb_pipe *p, int dir, void *data, int datasize); +int xhci_poll_intr(struct usb_pipe *p, void *data); + +// -------------------------------------------------------------- +// register interface + +// capabilities +struct xhci_caps { + u8 caplength; + u8 reserved_01; + u16 hciversion; + u32 hcsparams1; + u32 hcsparams2; + u32 hcsparams3; + u32 hccparams; + u32 dboff; + u32 rtsoff; +} PACKED; + +// extended capabilities +struct xhci_xcap { + u32 cap; + u32 data[]; +} PACKED; + +// operational registers +struct xhci_op { + u32 usbcmd; + u32 usbsts; + u32 pagesize; + u32 reserved_01[2]; + u32 dnctl; + u32 crcr_low; + u32 crcr_high; + u32 reserved_02[4]; + u32 dcbaap_low; + u32 dcbaap_high; + u32 config; +} PACKED; + +// port registers +struct xhci_pr { + u32 portsc; + u32 portpmsc; + u32 portli; + u32 reserved_01; +} PACKED; + +// doorbell registers +struct xhci_db { + u32 doorbell; +} PACKED; + +// runtime registers +struct xhci_rts { + u32 mfindex; +} PACKED; + +// interrupter registers +struct xhci_ir { + u32 iman; + u32 imod; + u32 erstsz; + u32 reserved_01; + u32 erstba_low; + u32 erstba_high; + u32 erdp_low; + u32 erdp_high; +} PACKED; + +// -------------------------------------------------------------- +// memory data structs + +// slot context +struct xhci_slotctx { + u32 ctx[4]; + u32 reserved_01[4]; +} PACKED; + +// endpoint context +struct xhci_epctx { + u32 ctx[2]; + u32 deq_low; + u32 deq_high; + u32 length; + u32 reserved_01[3]; +} PACKED; + +// device context +struct xhci_devctx { + struct xhci_slotctx slot; + struct xhci_epctx ep[31]; +} PACKED; + +// device context array element +struct xhci_devlist { + u32 ptr_low; + u32 ptr_high; +} PACKED; + +// input context +struct xhci_inctx { + u32 del; + u32 add; + u32 reserved_01[6]; + struct xhci_slotctx slot; + struct xhci_epctx ep[31]; +} PACKED; + +// transfer block (ring element) +struct xhci_trb { + u32 ptr_low; + u32 ptr_high; + u32 status; + u32 control; +} PACKED; + +// event ring segment +struct xhci_er_seg { + u32 ptr_low; + u32 ptr_high; + u32 size; + u32 reserved_01; +} PACKED; + +#endif // usb-xhci.h diff --git a/roms/seabios/src/hw/usb.c b/roms/seabios/src/hw/usb.c new file mode 100644 index 000000000..8fe741f4a --- /dev/null +++ b/roms/seabios/src/hw/usb.c @@ -0,0 +1,490 @@ +// Main code for handling USB controllers and devices. +// +// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBAL +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_UHCI +#include "pci_regs.h" // PCI_CLASS_REVISION +#include "string.h" // memset +#include "usb.h" // struct usb_s +#include "usb-ehci.h" // ehci_setup +#include "usb-xhci.h" // xhci_setup +#include "usb-hid.h" // usb_keyboard_setup +#include "usb-hub.h" // usb_hub_setup +#include "usb-msc.h" // usb_msc_setup +#include "usb-ohci.h" // ohci_setup +#include "usb-uas.h" // usb_uas_setup +#include "usb-uhci.h" // uhci_setup +#include "util.h" // msleep +#include "x86.h" // __fls + + +/**************************************************************** + * Controller function wrappers + ****************************************************************/ + +// Allocate an async pipe (control or bulk). +struct usb_pipe * +usb_alloc_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + switch (usbdev->hub->cntl->type) { + default: + case USB_TYPE_UHCI: + return uhci_alloc_pipe(usbdev, epdesc); + case USB_TYPE_OHCI: + return ohci_alloc_pipe(usbdev, epdesc); + case USB_TYPE_EHCI: + return ehci_alloc_pipe(usbdev, epdesc); + case USB_TYPE_XHCI: + return xhci_alloc_pipe(usbdev, epdesc); + } +} + +// Update an pipe (used for control only) +struct usb_pipe * +usb_update_pipe(struct usbdevice_s *usbdev, struct usb_pipe *pipe + , struct usb_endpoint_descriptor *epdesc) +{ + switch (usbdev->hub->cntl->type) { + case USB_TYPE_XHCI: + return xhci_update_pipe(usbdev, pipe, epdesc); + default: + free_pipe(pipe); + return usb_alloc_pipe(usbdev, epdesc); + } +} + +// Send a message on a control pipe using the default control descriptor. +static int +send_control(struct usb_pipe *pipe, int dir, const void *cmd, int cmdsize + , void *data, int datasize) +{ + ASSERT32FLAT(); + switch (pipe->type) { + default: + case USB_TYPE_UHCI: + return uhci_control(pipe, dir, cmd, cmdsize, data, datasize); + case USB_TYPE_OHCI: + return ohci_control(pipe, dir, cmd, cmdsize, data, datasize); + case USB_TYPE_EHCI: + return ehci_control(pipe, dir, cmd, cmdsize, data, datasize); + case USB_TYPE_XHCI: + return xhci_control(pipe, dir, cmd, cmdsize, data, datasize); + } +} + +int +usb_send_bulk(struct usb_pipe *pipe_fl, int dir, void *data, int datasize) +{ + switch (GET_LOWFLAT(pipe_fl->type)) { + default: + case USB_TYPE_UHCI: + return uhci_send_bulk(pipe_fl, dir, data, datasize); + case USB_TYPE_OHCI: + return ohci_send_bulk(pipe_fl, dir, data, datasize); + case USB_TYPE_EHCI: + return ehci_send_bulk(pipe_fl, dir, data, datasize); + case USB_TYPE_XHCI: + return xhci_send_bulk(pipe_fl, dir, data, datasize); + } +} + +int +usb_poll_intr(struct usb_pipe *pipe_fl, void *data) +{ + switch (GET_LOWFLAT(pipe_fl->type)) { + default: + case USB_TYPE_UHCI: + return uhci_poll_intr(pipe_fl, data); + case USB_TYPE_OHCI: + return ohci_poll_intr(pipe_fl, data); + case USB_TYPE_EHCI: + return ehci_poll_intr(pipe_fl, data); + case USB_TYPE_XHCI: + return xhci_poll_intr(pipe_fl, data); + } +} + + +/**************************************************************** + * Helper functions + ****************************************************************/ + +// Send a message to the default control pipe of a device. +int +send_default_control(struct usb_pipe *pipe, const struct usb_ctrlrequest *req + , void *data) +{ + return send_control(pipe, req->bRequestType & USB_DIR_IN + , req, sizeof(*req), data, req->wLength); +} + +// Free an allocated control or bulk pipe. +void +free_pipe(struct usb_pipe *pipe) +{ + ASSERT32FLAT(); + if (!pipe) + return; + // Add to controller's free list. + struct usb_s *cntl = pipe->cntl; + pipe->freenext = cntl->freelist; + cntl->freelist = pipe; +} + +// Check for an available pipe on the freelist. +struct usb_pipe * +usb_getFreePipe(struct usb_s *cntl, u8 eptype) +{ + struct usb_pipe **pfree = &cntl->freelist; + for (;;) { + struct usb_pipe *pipe = *pfree; + if (!pipe) + return NULL; + if (pipe->eptype == eptype) { + *pfree = pipe->freenext; + return pipe; + } + pfree = &pipe->freenext; + } +} + +// Fill "pipe" endpoint info from an endpoint descriptor. +void +usb_desc2pipe(struct usb_pipe *pipe, struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + pipe->cntl = usbdev->hub->cntl; + pipe->type = usbdev->hub->cntl->type; + pipe->ep = epdesc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + pipe->devaddr = usbdev->devaddr; + pipe->speed = usbdev->speed; + pipe->maxpacket = epdesc->wMaxPacketSize; + pipe->eptype = epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; +} + +// Find the exponential period of the requested interrupt end point. +int +usb_getFrameExp(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc) +{ + int period = epdesc->bInterval; + if (usbdev->speed != USB_HIGHSPEED) + return (period <= 0) ? 0 : __fls(period); + return (period <= 4) ? 0 : period - 4; +} + +// Find the first endpoing of a given type in an interface description. +struct usb_endpoint_descriptor * +findEndPointDesc(struct usbdevice_s *usbdev, int type, int dir) +{ + struct usb_endpoint_descriptor *epdesc = (void*)&usbdev->iface[1]; + for (;;) { + if ((void*)epdesc >= (void*)usbdev->iface + usbdev->imax + || epdesc->bDescriptorType == USB_DT_INTERFACE) { + return NULL; + } + if (epdesc->bDescriptorType == USB_DT_ENDPOINT + && (epdesc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == dir + && (epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == type) + return epdesc; + epdesc = (void*)epdesc + epdesc->bLength; + } +} + +// Get the first 8 bytes of the device descriptor. +static int +get_device_info8(struct usb_pipe *pipe, struct usb_device_descriptor *dinfo) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE; + req.bRequest = USB_REQ_GET_DESCRIPTOR; + req.wValue = USB_DT_DEVICE<<8; + req.wIndex = 0; + req.wLength = 8; + return send_default_control(pipe, &req, dinfo); +} + +static struct usb_config_descriptor * +get_device_config(struct usb_pipe *pipe) +{ + struct usb_config_descriptor cfg; + + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE; + req.bRequest = USB_REQ_GET_DESCRIPTOR; + req.wValue = USB_DT_CONFIG<<8; + req.wIndex = 0; + req.wLength = sizeof(cfg); + int ret = send_default_control(pipe, &req, &cfg); + if (ret) + return NULL; + + void *config = malloc_tmphigh(cfg.wTotalLength); + if (!config) + return NULL; + req.wLength = cfg.wTotalLength; + ret = send_default_control(pipe, &req, config); + if (ret) + return NULL; + //hexdump(config, cfg.wTotalLength); + return config; +} + +static int +set_configuration(struct usb_pipe *pipe, u16 val) +{ + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE; + req.bRequest = USB_REQ_SET_CONFIGURATION; + req.wValue = val; + req.wIndex = 0; + req.wLength = 0; + return send_default_control(pipe, &req, NULL); +} + + +/**************************************************************** + * Initialization and enumeration + ****************************************************************/ + +// Assign an address to a device in the default state on the given +// controller. +static int +usb_set_address(struct usbdevice_s *usbdev) +{ + ASSERT32FLAT(); + struct usb_s *cntl = usbdev->hub->cntl; + dprintf(3, "set_address %p\n", cntl); + if (cntl->maxaddr >= USB_MAXADDR) + return -1; + + // Create a pipe for the default address. + struct usb_endpoint_descriptor epdesc = { + .wMaxPacketSize = 8, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + }; + usbdev->defpipe = usb_alloc_pipe(usbdev, &epdesc); + if (!usbdev->defpipe) + return -1; + + msleep(USB_TIME_RSTRCY); + + // Send set_address command. + struct usb_ctrlrequest req; + req.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE; + req.bRequest = USB_REQ_SET_ADDRESS; + req.wValue = cntl->maxaddr + 1; + req.wIndex = 0; + req.wLength = 0; + int ret = send_default_control(usbdev->defpipe, &req, NULL); + if (ret) { + free_pipe(usbdev->defpipe); + return -1; + } + + msleep(USB_TIME_SETADDR_RECOVERY); + + cntl->maxaddr++; + usbdev->devaddr = cntl->maxaddr; + usbdev->defpipe = usb_update_pipe(usbdev, usbdev->defpipe, &epdesc); + if (!usbdev->defpipe) + return -1; + return 0; +} + +// Called for every found device - see if a driver is available for +// this device and do setup if so. +static int +configure_usb_device(struct usbdevice_s *usbdev) +{ + ASSERT32FLAT(); + dprintf(3, "config_usb: %p\n", usbdev->defpipe); + + // Set the max packet size for endpoint 0 of this device. + struct usb_device_descriptor dinfo; + int ret = get_device_info8(usbdev->defpipe, &dinfo); + if (ret) + return 0; + dprintf(3, "device rev=%04x cls=%02x sub=%02x proto=%02x size=%02x\n" + , dinfo.bcdUSB, dinfo.bDeviceClass, dinfo.bDeviceSubClass + , dinfo.bDeviceProtocol, dinfo.bMaxPacketSize0); + if (dinfo.bMaxPacketSize0 < 8 || dinfo.bMaxPacketSize0 > 64) + return 0; + struct usb_endpoint_descriptor epdesc = { + .wMaxPacketSize = dinfo.bMaxPacketSize0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + }; + usbdev->defpipe = usb_update_pipe(usbdev, usbdev->defpipe, &epdesc); + if (!usbdev->defpipe) + return -1; + + // Get configuration + struct usb_config_descriptor *config = get_device_config(usbdev->defpipe); + if (!config) + return 0; + + // Determine if a driver exists for this device - only look at the + // first interface of the first configuration. + struct usb_interface_descriptor *iface = (void*)(&config[1]); + if (iface->bInterfaceClass != USB_CLASS_HID + && iface->bInterfaceClass != USB_CLASS_MASS_STORAGE + && iface->bInterfaceClass != USB_CLASS_HUB) + // Not a supported device. + goto fail; + + // Set the configuration. + ret = set_configuration(usbdev->defpipe, config->bConfigurationValue); + if (ret) + goto fail; + + // Configure driver. + usbdev->config = config; + usbdev->iface = iface; + usbdev->imax = (void*)config + config->wTotalLength - (void*)iface; + if (iface->bInterfaceClass == USB_CLASS_HUB) + ret = usb_hub_setup(usbdev); + else if (iface->bInterfaceClass == USB_CLASS_MASS_STORAGE) { + if (iface->bInterfaceProtocol == US_PR_BULK) + ret = usb_msc_setup(usbdev); + if (iface->bInterfaceProtocol == US_PR_UAS) + ret = usb_uas_setup(usbdev); + } else + ret = usb_hid_setup(usbdev); + if (ret) + goto fail; + + free(config); + return 1; +fail: + free(config); + return 0; +} + +static void +usb_hub_port_setup(void *data) +{ + struct usbdevice_s *usbdev = data; + struct usbhub_s *hub = usbdev->hub; + u32 port = usbdev->port; + + // Detect if device present (and possibly start reset) + int ret = hub->op->detect(hub, port); + if (ret) + // No device present + goto done; + + // Reset port and determine device speed + mutex_lock(&hub->cntl->resetlock); + ret = hub->op->reset(hub, port); + if (ret < 0) + // Reset failed + goto resetfail; + usbdev->speed = ret; + + // Set address of port + ret = usb_set_address(usbdev); + if (ret) { + hub->op->disconnect(hub, port); + goto resetfail; + } + mutex_unlock(&hub->cntl->resetlock); + + // Configure the device + int count = configure_usb_device(usbdev); + free_pipe(usbdev->defpipe); + if (!count) + hub->op->disconnect(hub, port); + hub->devcount += count; +done: + hub->threads--; + free(usbdev); + return; + +resetfail: + mutex_unlock(&hub->cntl->resetlock); + goto done; +} + +void +usb_enumerate(struct usbhub_s *hub) +{ + u32 portcount = hub->portcount; + hub->threads = portcount; + + // Launch a thread for every port. + int i; + for (i=0; i<portcount; i++) { + struct usbdevice_s *usbdev = malloc_tmphigh(sizeof(*usbdev)); + if (!usbdev) { + warn_noalloc(); + continue; + } + memset(usbdev, 0, sizeof(*usbdev)); + usbdev->hub = hub; + usbdev->port = i; + run_thread(usb_hub_port_setup, usbdev); + } + + // Wait for threads to complete. + while (hub->threads) + yield(); +} + +void +usb_setup(void) +{ + ASSERT32FLAT(); + if (! CONFIG_USB) + return; + + dprintf(3, "init usb\n"); + + // Look for USB controllers + int count = 0; + struct pci_device *pci, *ehcipci = NULL; + foreachpci(pci) { + if (pci->class != PCI_CLASS_SERIAL_USB) + continue; + + if (!ehcipci || pci->bdf >= ehcipci->bdf) { + // Check to see if this device has an ehci controller + int found = 0; + ehcipci = pci; + for (;;) { + if (pci_classprog(ehcipci) == PCI_CLASS_SERIAL_USB_EHCI) { + // Found an ehci controller. + int ret = ehci_setup(ehcipci, count++, pci); + if (ret) + // Error + break; + count += found; + pci = ehcipci; + break; + } + if (ehcipci->class == PCI_CLASS_SERIAL_USB) + found++; + ehcipci = container_of_or_null( + ehcipci->node.next, struct pci_device, node); + if (!ehcipci || (pci_bdf_to_busdev(ehcipci->bdf) + != pci_bdf_to_busdev(pci->bdf))) + // No ehci controller found. + break; + } + } + + if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_UHCI) + uhci_setup(pci, count++); + else if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_OHCI) + ohci_setup(pci, count++); + else if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_XHCI) + xhci_setup(pci, count++); + } +} diff --git a/roms/seabios/src/hw/usb.h b/roms/seabios/src/hw/usb.h new file mode 100644 index 000000000..22173fb37 --- /dev/null +++ b/roms/seabios/src/hw/usb.h @@ -0,0 +1,244 @@ +// USB functions and data. +#ifndef __USB_H +#define __USB_H + +#include "stacks.h" // struct mutex_s + +// Information on a USB end point. +struct usb_pipe { + union { + struct usb_s *cntl; + struct usb_pipe *freenext; + }; + u8 type; + u8 ep; + u8 devaddr; + u8 speed; + u16 maxpacket; + u8 eptype; +}; + +// Common information for usb devices. +struct usbdevice_s { + struct usbhub_s *hub; + struct usb_pipe *defpipe; + u32 port; + struct usb_config_descriptor *config; + struct usb_interface_descriptor *iface; + int imax; + u8 speed; + u8 devaddr; +}; + +// Common information for usb controllers. +struct usb_s { + struct usb_pipe *freelist; + struct mutex_s resetlock; + struct pci_device *pci; + int busid; + u8 type; + u8 maxaddr; +}; + +// Information for enumerating USB hubs +struct usbhub_s { + struct usbhub_op_s *op; + struct usbdevice_s *usbdev; + struct usb_s *cntl; + struct mutex_s lock; + u32 powerwait; + u32 port; + u32 threads; + u32 portcount; + u32 devcount; +}; + +// Hub callback (32bit) info +struct usbhub_op_s { + int (*detect)(struct usbhub_s *hub, u32 port); + int (*reset)(struct usbhub_s *hub, u32 port); + void (*disconnect)(struct usbhub_s *hub, u32 port); +}; + +#define USB_TYPE_UHCI 1 +#define USB_TYPE_OHCI 2 +#define USB_TYPE_EHCI 3 +#define USB_TYPE_XHCI 4 + +#define USB_FULLSPEED 0 +#define USB_LOWSPEED 1 +#define USB_HIGHSPEED 2 +#define USB_SUPERSPEED 3 + +#define USB_MAXADDR 127 + + +/**************************************************************** + * usb structs and flags + ****************************************************************/ + +// USB mandated timings (in ms) +#define USB_TIME_SIGATT 100 +#define USB_TIME_ATTDB 100 +#define USB_TIME_DRST 10 +#define USB_TIME_DRSTR 50 +#define USB_TIME_RSTRCY 10 + +#define USB_TIME_SETADDR_RECOVERY 2 + +#define USB_PID_OUT 0xe1 +#define USB_PID_IN 0x69 +#define USB_PID_SETUP 0x2d + +#define USB_DIR_OUT 0 /* to device */ +#define USB_DIR_IN 0x80 /* to host */ + +#define USB_TYPE_MASK (0x03 << 5) +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) + +#define USB_RECIP_MASK 0x1f +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 + +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +struct usb_ctrlrequest { + u8 bRequestType; + u8 bRequest; + u16 wValue; + u16 wIndex; + u16 wLength; +} PACKED; + +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 +#define USB_DT_DEVICE_QUALIFIER 0x06 +#define USB_DT_OTHER_SPEED_CONFIG 0x07 +#define USB_DT_ENDPOINT_COMPANION 0x30 + +struct usb_device_descriptor { + u8 bLength; + u8 bDescriptorType; + + u16 bcdUSB; + u8 bDeviceClass; + u8 bDeviceSubClass; + u8 bDeviceProtocol; + u8 bMaxPacketSize0; + u16 idVendor; + u16 idProduct; + u16 bcdDevice; + u8 iManufacturer; + u8 iProduct; + u8 iSerialNumber; + u8 bNumConfigurations; +} PACKED; + +#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ +#define USB_CLASS_AUDIO 1 +#define USB_CLASS_COMM 2 +#define USB_CLASS_HID 3 +#define USB_CLASS_PHYSICAL 5 +#define USB_CLASS_STILL_IMAGE 6 +#define USB_CLASS_PRINTER 7 +#define USB_CLASS_MASS_STORAGE 8 +#define USB_CLASS_HUB 9 + +struct usb_config_descriptor { + u8 bLength; + u8 bDescriptorType; + + u16 wTotalLength; + u8 bNumInterfaces; + u8 bConfigurationValue; + u8 iConfiguration; + u8 bmAttributes; + u8 bMaxPower; +} PACKED; + +struct usb_interface_descriptor { + u8 bLength; + u8 bDescriptorType; + + u8 bInterfaceNumber; + u8 bAlternateSetting; + u8 bNumEndpoints; + u8 bInterfaceClass; + u8 bInterfaceSubClass; + u8 bInterfaceProtocol; + u8 iInterface; +} PACKED; + +struct usb_endpoint_descriptor { + u8 bLength; + u8 bDescriptorType; + + u8 bEndpointAddress; + u8 bmAttributes; + u16 wMaxPacketSize; + u8 bInterval; +} PACKED; + +#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */ +#define USB_ENDPOINT_DIR_MASK 0x80 + +#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */ +#define USB_ENDPOINT_XFER_CONTROL 0 +#define USB_ENDPOINT_XFER_ISOC 1 +#define USB_ENDPOINT_XFER_BULK 2 +#define USB_ENDPOINT_XFER_INT 3 +#define USB_ENDPOINT_MAX_ADJUSTABLE 0x80 + + +/**************************************************************** + * usb mass storage flags + ****************************************************************/ + +#define US_SC_ATAPI_8020 0x02 +#define US_SC_ATAPI_8070 0x05 +#define US_SC_SCSI 0x06 + +#define US_PR_BULK 0x50 /* bulk-only transport */ +#define US_PR_UAS 0x62 /* usb attached scsi */ + +/**************************************************************** + * function defs + ****************************************************************/ + +// usb.c +struct usb_pipe *usb_alloc_pipe(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc); +int usb_send_bulk(struct usb_pipe *pipe, int dir, void *data, int datasize); +int usb_poll_intr(struct usb_pipe *pipe, void *data); +int send_default_control(struct usb_pipe *pipe, const struct usb_ctrlrequest *req + , void *data); +void free_pipe(struct usb_pipe *pipe); +struct usb_pipe *usb_getFreePipe(struct usb_s *cntl, u8 eptype); +void usb_desc2pipe(struct usb_pipe *pipe, struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc); +int usb_getFrameExp(struct usbdevice_s *usbdev + , struct usb_endpoint_descriptor *epdesc); +struct usb_endpoint_descriptor *findEndPointDesc(struct usbdevice_s *usbdev + , int type, int dir); +void usb_enumerate(struct usbhub_s *hub); +void usb_setup(void); + +#endif // usb.h diff --git a/roms/seabios/src/hw/virtio-blk.c b/roms/seabios/src/hw/virtio-blk.c new file mode 100644 index 000000000..0290d671c --- /dev/null +++ b/roms/seabios/src/hw/virtio-blk.c @@ -0,0 +1,177 @@ +// Virtio block boot support. +// +// Copyright (C) 2010 Red Hat Inc. +// +// Authors: +// Gleb Natapov <gnatapov@redhat.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "config.h" // CONFIG_* +#include "block.h" // struct drive_s +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_DEVICE_ID_VIRTIO_BLK +#include "pci_regs.h" // PCI_VENDOR_ID +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // usleep +#include "virtio-pci.h" +#include "virtio-ring.h" +#include "virtio-blk.h" + +struct virtiodrive_s { + struct drive_s drive; + struct vring_virtqueue *vq; + u16 ioaddr; +}; + +static int +virtio_blk_op(struct disk_op_s *op, int write) +{ + struct virtiodrive_s *vdrive_gf = + container_of(op->drive_gf, struct virtiodrive_s, drive); + struct vring_virtqueue *vq = GET_GLOBALFLAT(vdrive_gf->vq); + struct virtio_blk_outhdr hdr = { + .type = write ? VIRTIO_BLK_T_OUT : VIRTIO_BLK_T_IN, + .ioprio = 0, + .sector = op->lba, + }; + u8 status = VIRTIO_BLK_S_UNSUPP; + struct vring_list sg[] = { + { + .addr = MAKE_FLATPTR(GET_SEG(SS), &hdr), + .length = sizeof(hdr), + }, + { + .addr = op->buf_fl, + .length = GET_GLOBALFLAT(vdrive_gf->drive.blksize) * op->count, + }, + { + .addr = MAKE_FLATPTR(GET_SEG(SS), &status), + .length = sizeof(status), + }, + }; + + /* Add to virtqueue and kick host */ + if (write) + vring_add_buf(vq, sg, 2, 1, 0, 0); + else + vring_add_buf(vq, sg, 1, 2, 0, 0); + vring_kick(GET_GLOBALFLAT(vdrive_gf->ioaddr), vq, 1); + + /* Wait for reply */ + while (!vring_more_used(vq)) + usleep(5); + + /* Reclaim virtqueue element */ + vring_get_buf(vq, NULL); + + /* Clear interrupt status register. Avoid leaving interrupts stuck if + * VRING_AVAIL_F_NO_INTERRUPT was ignored and interrupts were raised. + */ + vp_get_isr(GET_GLOBALFLAT(vdrive_gf->ioaddr)); + + return status == VIRTIO_BLK_S_OK ? DISK_RET_SUCCESS : DISK_RET_EBADTRACK; +} + +int +process_virtio_blk_op(struct disk_op_s *op) +{ + if (! CONFIG_VIRTIO_BLK) + return 0; + switch (op->command) { + case CMD_READ: + return virtio_blk_op(op, 0); + case CMD_WRITE: + return virtio_blk_op(op, 1); + case CMD_FORMAT: + case CMD_RESET: + case CMD_ISREADY: + case CMD_VERIFY: + case CMD_SEEK: + return DISK_RET_SUCCESS; + default: + op->count = 0; + return DISK_RET_EPARAM; + } +} + +static void +init_virtio_blk(struct pci_device *pci) +{ + u16 bdf = pci->bdf; + dprintf(1, "found virtio-blk at %x:%x\n", pci_bdf_to_bus(bdf), + pci_bdf_to_dev(bdf)); + struct virtiodrive_s *vdrive = malloc_fseg(sizeof(*vdrive)); + if (!vdrive) { + warn_noalloc(); + return; + } + memset(vdrive, 0, sizeof(*vdrive)); + vdrive->drive.type = DTYPE_VIRTIO_BLK; + vdrive->drive.cntl_id = bdf; + + u16 ioaddr = vp_init_simple(bdf); + vdrive->ioaddr = ioaddr; + if (vp_find_vq(ioaddr, 0, &vdrive->vq) < 0 ) { + dprintf(1, "fail to find vq for virtio-blk %x:%x\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)); + goto fail; + } + + struct virtio_blk_config cfg; + vp_get(ioaddr, 0, &cfg, sizeof(cfg)); + + u32 f = vp_get_features(ioaddr); + vdrive->drive.blksize = (f & (1 << VIRTIO_BLK_F_BLK_SIZE)) ? + cfg.blk_size : DISK_SECTOR_SIZE; + + vdrive->drive.sectors = cfg.capacity; + dprintf(3, "virtio-blk %x:%x blksize=%d sectors=%u\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), + vdrive->drive.blksize, (u32)vdrive->drive.sectors); + + if (vdrive->drive.blksize != DISK_SECTOR_SIZE) { + dprintf(1, "virtio-blk %x:%x block size %d is unsupported\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), + vdrive->drive.blksize); + goto fail; + } + + vdrive->drive.pchs.cylinder = cfg.cylinders; + vdrive->drive.pchs.head = cfg.heads; + vdrive->drive.pchs.sector = cfg.sectors; + char *desc = znprintf(MAXDESCSIZE, "Virtio disk PCI:%x:%x", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)); + + boot_add_hd(&vdrive->drive, desc, bootprio_find_pci_device(pci)); + + vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE | + VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK); + return; + +fail: + free(vdrive->vq); + free(vdrive); +} + +void +virtio_blk_setup(void) +{ + ASSERT32FLAT(); + if (! CONFIG_VIRTIO_BLK) + return; + + dprintf(3, "init virtio-blk\n"); + + struct pci_device *pci; + foreachpci(pci) { + if (pci->vendor != PCI_VENDOR_ID_REDHAT_QUMRANET + || pci->device != PCI_DEVICE_ID_VIRTIO_BLK) + continue; + init_virtio_blk(pci); + } +} diff --git a/roms/seabios/src/hw/virtio-blk.h b/roms/seabios/src/hw/virtio-blk.h new file mode 100644 index 000000000..b233c744b --- /dev/null +++ b/roms/seabios/src/hw/virtio-blk.h @@ -0,0 +1,43 @@ +#ifndef _VIRTIO_BLK_H +#define _VIRTIO_BLK_H + +struct virtio_blk_config +{ + u64 capacity; + u32 size_max; + u32 seg_max; + u16 cylinders; + u8 heads; + u8 sectors; + u32 blk_size; + u8 physical_block_exp; + u8 alignment_offset; + u16 min_io_size; + u32 opt_io_size; +} __attribute__((packed)); + +#define VIRTIO_BLK_F_BLK_SIZE 6 + +/* These two define direction. */ +#define VIRTIO_BLK_T_IN 0 +#define VIRTIO_BLK_T_OUT 1 + +/* This is the first element of the read scatter-gather list. */ +struct virtio_blk_outhdr { + /* VIRTIO_BLK_T* */ + u32 type; + /* io priority. */ + u32 ioprio; + /* Sector (ie. 512 byte offset) */ + u64 sector; +}; + +#define VIRTIO_BLK_S_OK 0 +#define VIRTIO_BLK_S_IOERR 1 +#define VIRTIO_BLK_S_UNSUPP 2 + +struct disk_op_s; +int process_virtio_blk_op(struct disk_op_s *op); +void virtio_blk_setup(void); + +#endif /* _VIRTIO_BLK_H */ diff --git a/roms/seabios/src/hw/virtio-pci.c b/roms/seabios/src/hw/virtio-pci.c new file mode 100644 index 000000000..a38250485 --- /dev/null +++ b/roms/seabios/src/hw/virtio-pci.c @@ -0,0 +1,96 @@ +/* virtio-pci.c - pci interface for virtio interface + * + * (c) Copyright 2008 Bull S.A.S. + * + * Author: Laurent Vivier <Laurent.Vivier@bull.net> + * + * some parts from Linux Virtio PCI driver + * + * Copyright IBM Corp. 2007 + * Authors: Anthony Liguori <aliguori@us.ibm.com> + * + * Adopted for Seabios: Gleb Natapov <gleb@redhat.com> + * + * This work is licensed under the terms of the GNU LGPLv3 + * See the COPYING file in the top-level directory. + */ + +#include "config.h" // CONFIG_DEBUG_LEVEL +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // pci_config_readl +#include "pci_regs.h" // PCI_BASE_ADDRESS_0 +#include "string.h" // memset +#include "virtio-pci.h" +#include "virtio-ring.h" + +int vp_find_vq(unsigned int ioaddr, int queue_index, + struct vring_virtqueue **p_vq) +{ + u16 num; + + ASSERT32FLAT(); + struct vring_virtqueue *vq = *p_vq = memalign_low(PAGE_SIZE, sizeof(*vq)); + if (!vq) { + warn_noalloc(); + goto fail; + } + memset(vq, 0, sizeof(*vq)); + + /* select the queue */ + + outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_SEL); + + /* check if the queue is available */ + + num = inw(ioaddr + VIRTIO_PCI_QUEUE_NUM); + if (!num) { + dprintf(1, "ERROR: queue size is 0\n"); + goto fail; + } + + if (num > MAX_QUEUE_NUM) { + dprintf(1, "ERROR: queue size %d > %d\n", num, MAX_QUEUE_NUM); + goto fail; + } + + /* check if the queue is already active */ + + if (inl(ioaddr + VIRTIO_PCI_QUEUE_PFN)) { + dprintf(1, "ERROR: queue already active\n"); + goto fail; + } + + vq->queue_index = queue_index; + + /* initialize the queue */ + + struct vring * vr = &vq->vring; + vring_init(vr, num, (unsigned char*)&vq->queue); + + /* activate the queue + * + * NOTE: vr->desc is initialized by vring_init() + */ + + outl((unsigned long)virt_to_phys(vr->desc) >> PAGE_SHIFT, + ioaddr + VIRTIO_PCI_QUEUE_PFN); + + return num; + +fail: + free(vq); + *p_vq = NULL; + return -1; +} + +u16 vp_init_simple(u16 bdf) +{ + u16 ioaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0) & + PCI_BASE_ADDRESS_IO_MASK; + + vp_reset(ioaddr); + vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE | + VIRTIO_CONFIG_S_DRIVER ); + return ioaddr; +} diff --git a/roms/seabios/src/hw/virtio-pci.h b/roms/seabios/src/hw/virtio-pci.h new file mode 100644 index 000000000..bc04b039e --- /dev/null +++ b/roms/seabios/src/hw/virtio-pci.h @@ -0,0 +1,105 @@ +#ifndef _VIRTIO_PCI_H +#define _VIRTIO_PCI_H + +#include "x86.h" // inl + +/* A 32-bit r/o bitmask of the features supported by the host */ +#define VIRTIO_PCI_HOST_FEATURES 0 + +/* A 32-bit r/w bitmask of features activated by the guest */ +#define VIRTIO_PCI_GUEST_FEATURES 4 + +/* A 32-bit r/w PFN for the currently selected queue */ +#define VIRTIO_PCI_QUEUE_PFN 8 + +/* A 16-bit r/o queue size for the currently selected queue */ +#define VIRTIO_PCI_QUEUE_NUM 12 + +/* A 16-bit r/w queue selector */ +#define VIRTIO_PCI_QUEUE_SEL 14 + +/* A 16-bit r/w queue notifier */ +#define VIRTIO_PCI_QUEUE_NOTIFY 16 + +/* An 8-bit device status register. */ +#define VIRTIO_PCI_STATUS 18 + +/* An 8-bit r/o interrupt status register. Reading the value will return the + * current contents of the ISR and will also clear it. This is effectively + * a read-and-acknowledge. */ +#define VIRTIO_PCI_ISR 19 + +/* The bit of the ISR which indicates a device configuration change. */ +#define VIRTIO_PCI_ISR_CONFIG 0x2 + +/* The remaining space is defined by each driver as the per-driver + * configuration space */ +#define VIRTIO_PCI_CONFIG 20 + +/* Virtio ABI version, this must match exactly */ +#define VIRTIO_PCI_ABI_VERSION 0 + +static inline u32 vp_get_features(unsigned int ioaddr) +{ + return inl(ioaddr + VIRTIO_PCI_HOST_FEATURES); +} + +static inline void vp_set_features(unsigned int ioaddr, u32 features) +{ + outl(features, ioaddr + VIRTIO_PCI_GUEST_FEATURES); +} + +static inline void vp_get(unsigned int ioaddr, unsigned offset, + void *buf, unsigned len) +{ + u8 *ptr = buf; + unsigned i; + + for (i = 0; i < len; i++) + ptr[i] = inb(ioaddr + VIRTIO_PCI_CONFIG + offset + i); +} + +static inline u8 vp_get_status(unsigned int ioaddr) +{ + return inb(ioaddr + VIRTIO_PCI_STATUS); +} + +static inline void vp_set_status(unsigned int ioaddr, u8 status) +{ + if (status == 0) /* reset */ + return; + outb(status, ioaddr + VIRTIO_PCI_STATUS); +} + +static inline u8 vp_get_isr(unsigned int ioaddr) +{ + return inb(ioaddr + VIRTIO_PCI_ISR); +} + +static inline void vp_reset(unsigned int ioaddr) +{ + outb(0, ioaddr + VIRTIO_PCI_STATUS); + (void)inb(ioaddr + VIRTIO_PCI_ISR); +} + +static inline void vp_notify(unsigned int ioaddr, int queue_index) +{ + outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_NOTIFY); +} + +static inline void vp_del_vq(unsigned int ioaddr, int queue_index) +{ + /* select the queue */ + + outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_SEL); + + /* deactivate the queue */ + + outl(0, ioaddr + VIRTIO_PCI_QUEUE_PFN); +} + +struct vring_virtqueue; +u16 vp_init_simple(u16 bdf); +int vp_find_vq(unsigned int ioaddr, int queue_index, + struct vring_virtqueue **p_vq); +#endif /* _VIRTIO_PCI_H_ */ diff --git a/roms/seabios/src/hw/virtio-ring.c b/roms/seabios/src/hw/virtio-ring.c new file mode 100644 index 000000000..97e0b3487 --- /dev/null +++ b/roms/seabios/src/hw/virtio-ring.c @@ -0,0 +1,149 @@ +/* virtio-pci.c - virtio ring management + * + * (c) Copyright 2008 Bull S.A.S. + * + * Author: Laurent Vivier <Laurent.Vivier@bull.net> + * + * some parts from Linux Virtio Ring + * + * Copyright Rusty Russell IBM Corporation 2007 + * + * Adopted for Seabios: Gleb Natapov <gleb@redhat.com> + * + * This work is licensed under the terms of the GNU LGPLv3 + * See the COPYING file in the top-level directory. + * + * + */ + +#include "biosvar.h" // GET_GLOBAL +#include "output.h" // panic +#include "virtio-ring.h" +#include "virtio-pci.h" + +#define BUG() do { \ + panic("BUG: failure at %d/%s()!\n", __LINE__, __func__); \ + } while (0) +#define BUG_ON(condition) do { if (condition) BUG(); } while (0) + +/* + * vring_more_used + * + * is there some used buffers ? + * + */ + +int vring_more_used(struct vring_virtqueue *vq) +{ + struct vring_used *used = GET_LOWFLAT(vq->vring.used); + int more = GET_LOWFLAT(vq->last_used_idx) != GET_LOWFLAT(used->idx); + /* Make sure ring reads are done after idx read above. */ + smp_rmb(); + return more; +} + +/* + * vring_free + * + * put at the begin of the free list the current desc[head] + */ + +void vring_detach(struct vring_virtqueue *vq, unsigned int head) +{ + struct vring *vr = &vq->vring; + struct vring_desc *desc = GET_LOWFLAT(vr->desc); + unsigned int i; + + /* find end of given descriptor */ + + i = head; + while (GET_LOWFLAT(desc[i].flags) & VRING_DESC_F_NEXT) + i = GET_LOWFLAT(desc[i].next); + + /* link it with free list and point to it */ + + SET_LOWFLAT(desc[i].next, GET_LOWFLAT(vq->free_head)); + SET_LOWFLAT(vq->free_head, head); +} + +/* + * vring_get_buf + * + * get a buffer from the used list + * + */ + +int vring_get_buf(struct vring_virtqueue *vq, unsigned int *len) +{ + struct vring *vr = &vq->vring; + struct vring_used_elem *elem; + struct vring_used *used = GET_LOWFLAT(vq->vring.used); + u32 id; + int ret; + +// BUG_ON(!vring_more_used(vq)); + + elem = &used->ring[GET_LOWFLAT(vq->last_used_idx) % GET_LOWFLAT(vr->num)]; + id = GET_LOWFLAT(elem->id); + if (len != NULL) + *len = GET_LOWFLAT(elem->len); + + ret = GET_LOWFLAT(vq->vdata[id]); + + vring_detach(vq, id); + + SET_LOWFLAT(vq->last_used_idx, GET_LOWFLAT(vq->last_used_idx) + 1); + + return ret; +} + +void vring_add_buf(struct vring_virtqueue *vq, + struct vring_list list[], + unsigned int out, unsigned int in, + int index, int num_added) +{ + struct vring *vr = &vq->vring; + int i, av, head, prev; + struct vring_desc *desc = GET_LOWFLAT(vr->desc); + struct vring_avail *avail = GET_LOWFLAT(vr->avail); + + BUG_ON(out + in == 0); + + prev = 0; + head = GET_LOWFLAT(vq->free_head); + for (i = head; out; i = GET_LOWFLAT(desc[i].next), out--) { + SET_LOWFLAT(desc[i].flags, VRING_DESC_F_NEXT); + SET_LOWFLAT(desc[i].addr, (u64)virt_to_phys(list->addr)); + SET_LOWFLAT(desc[i].len, list->length); + prev = i; + list++; + } + for ( ; in; i = GET_LOWFLAT(desc[i].next), in--) { + SET_LOWFLAT(desc[i].flags, VRING_DESC_F_NEXT|VRING_DESC_F_WRITE); + SET_LOWFLAT(desc[i].addr, (u64)virt_to_phys(list->addr)); + SET_LOWFLAT(desc[i].len, list->length); + prev = i; + list++; + } + SET_LOWFLAT(desc[prev].flags, + GET_LOWFLAT(desc[prev].flags) & ~VRING_DESC_F_NEXT); + + SET_LOWFLAT(vq->free_head, i); + + SET_LOWFLAT(vq->vdata[head], index); + + av = (GET_LOWFLAT(avail->idx) + num_added) % GET_LOWFLAT(vr->num); + SET_LOWFLAT(avail->ring[av], head); +} + +void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int num_added) +{ + struct vring *vr = &vq->vring; + struct vring_avail *avail = GET_LOWFLAT(vr->avail); + + /* Make sure idx update is done after ring write. */ + smp_wmb(); + SET_LOWFLAT(avail->idx, GET_LOWFLAT(avail->idx) + num_added); + + vp_notify(ioaddr, GET_LOWFLAT(vq->queue_index)); +} diff --git a/roms/seabios/src/hw/virtio-ring.h b/roms/seabios/src/hw/virtio-ring.h new file mode 100644 index 000000000..b7a7aafb2 --- /dev/null +++ b/roms/seabios/src/hw/virtio-ring.h @@ -0,0 +1,131 @@ +#ifndef _VIRTIO_RING_H +#define _VIRTIO_RING_H + +#include "types.h" // u64 +#include "memmap.h" // PAGE_SIZE + +#define PAGE_SHIFT 12 +#define PAGE_MASK (PAGE_SIZE-1) + +#define virt_to_phys(v) (unsigned long)(v) +#define phys_to_virt(p) (void*)(p) +/* Compiler barrier is enough as an x86 CPU does not reorder reads or writes */ +#define smp_rmb() barrier() +#define smp_wmb() barrier() + +/* Status byte for guest to report progress, and synchronize features. */ +/* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */ +#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 +/* We have found a driver for the device. */ +#define VIRTIO_CONFIG_S_DRIVER 2 +/* Driver has used its parts of the config, and is happy */ +#define VIRTIO_CONFIG_S_DRIVER_OK 4 +/* We've given up on this device. */ +#define VIRTIO_CONFIG_S_FAILED 0x80 + +#define MAX_QUEUE_NUM (128) + +#define VRING_DESC_F_NEXT 1 +#define VRING_DESC_F_WRITE 2 + +#define VRING_AVAIL_F_NO_INTERRUPT 1 + +#define VRING_USED_F_NO_NOTIFY 1 + +struct vring_desc +{ + u64 addr; + u32 len; + u16 flags; + u16 next; +}; + +struct vring_avail +{ + u16 flags; + u16 idx; + u16 ring[0]; +}; + +struct vring_used_elem +{ + u32 id; + u32 len; +}; + +struct vring_used +{ + u16 flags; + u16 idx; + struct vring_used_elem ring[]; +}; + +struct vring { + unsigned int num; + struct vring_desc *desc; + struct vring_avail *avail; + struct vring_used *used; +}; + +#define vring_size(num) \ + (((((sizeof(struct vring_desc) * num) + \ + (sizeof(struct vring_avail) + sizeof(u16) * num)) \ + + PAGE_MASK) & ~PAGE_MASK) + \ + (sizeof(struct vring_used) + sizeof(struct vring_used_elem) * num)) + +typedef unsigned char virtio_queue_t[vring_size(MAX_QUEUE_NUM)]; + +struct vring_virtqueue { + virtio_queue_t queue; + struct vring vring; + u16 free_head; + u16 last_used_idx; + u16 vdata[MAX_QUEUE_NUM]; + /* PCI */ + int queue_index; +}; + +struct vring_list { + char *addr; + unsigned int length; +}; + +static inline void vring_init(struct vring *vr, + unsigned int num, unsigned char *queue) +{ + unsigned int i; + unsigned long pa; + + ASSERT32FLAT(); + vr->num = num; + + /* physical address of desc must be page aligned */ + + pa = virt_to_phys(queue); + pa = (pa + PAGE_MASK) & ~PAGE_MASK; + vr->desc = phys_to_virt(pa); + + vr->avail = (struct vring_avail *)&vr->desc[num]; + /* disable interrupts */ + vr->avail->flags |= VRING_AVAIL_F_NO_INTERRUPT; + + /* physical address of used must be page aligned */ + + pa = virt_to_phys(&vr->avail->ring[num]); + pa = (pa + PAGE_MASK) & ~PAGE_MASK; + vr->used = phys_to_virt(pa); + + for (i = 0; i < num - 1; i++) + vr->desc[i].next = i + 1; + vr->desc[i].next = 0; +} + +int vring_more_used(struct vring_virtqueue *vq); +void vring_detach(struct vring_virtqueue *vq, unsigned int head); +int vring_get_buf(struct vring_virtqueue *vq, unsigned int *len); +void vring_add_buf(struct vring_virtqueue *vq, struct vring_list list[], + unsigned int out, unsigned int in, + int index, int num_added); +void vring_kick(unsigned int ioaddr, struct vring_virtqueue *vq, int num_added); + +#endif /* _VIRTIO_RING_H_ */ diff --git a/roms/seabios/src/hw/virtio-scsi.c b/roms/seabios/src/hw/virtio-scsi.c new file mode 100644 index 000000000..4b4ec7bb0 --- /dev/null +++ b/roms/seabios/src/hw/virtio-scsi.c @@ -0,0 +1,186 @@ +// Virtio SCSI boot support. +// +// Copyright (C) 2012 Red Hat Inc. +// +// Authors: +// Paolo Bonzini <pbonzini@redhat.com> +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_GLOBALFLAT +#include "block.h" // struct drive_s +#include "blockcmd.h" // scsi_drive_setup +#include "config.h" // CONFIG_* +#include "malloc.h" // free +#include "output.h" // dprintf +#include "pci.h" // foreachpci +#include "pci_ids.h" // PCI_DEVICE_ID_VIRTIO_BLK +#include "pci_regs.h" // PCI_VENDOR_ID +#include "std/disk.h" // DISK_RET_SUCCESS +#include "string.h" // memset +#include "util.h" // usleep +#include "virtio-pci.h" +#include "virtio-ring.h" +#include "virtio-scsi.h" + +struct virtio_lun_s { + struct drive_s drive; + struct pci_device *pci; + struct vring_virtqueue *vq; + u16 ioaddr; + u16 target; + u16 lun; +}; + +static int +virtio_scsi_cmd(u16 ioaddr, struct vring_virtqueue *vq, struct disk_op_s *op, + void *cdbcmd, u16 target, u16 lun, u16 blocksize) +{ + struct virtio_scsi_req_cmd req; + struct virtio_scsi_resp_cmd resp; + struct vring_list sg[3]; + + memset(&req, 0, sizeof(req)); + req.lun[0] = 1; + req.lun[1] = target; + req.lun[2] = (lun >> 8) | 0x40; + req.lun[3] = (lun & 0xff); + memcpy(req.cdb, cdbcmd, 16); + + u32 len = op->count * blocksize; + int datain = cdb_is_read(cdbcmd, blocksize); + int in_num = (datain ? 2 : 1); + int out_num = (len ? 3 : 2) - in_num; + + sg[0].addr = MAKE_FLATPTR(GET_SEG(SS), &req); + sg[0].length = sizeof(req); + + sg[out_num].addr = MAKE_FLATPTR(GET_SEG(SS), &resp); + sg[out_num].length = sizeof(resp); + + if (len) { + int data_idx = (datain ? 2 : 1); + sg[data_idx].addr = op->buf_fl; + sg[data_idx].length = len; + } + + /* Add to virtqueue and kick host */ + vring_add_buf(vq, sg, out_num, in_num, 0, 0); + vring_kick(ioaddr, vq, 1); + + /* Wait for reply */ + while (!vring_more_used(vq)) + usleep(5); + + /* Reclaim virtqueue element */ + vring_get_buf(vq, NULL); + + /* Clear interrupt status register. Avoid leaving interrupts stuck if + * VRING_AVAIL_F_NO_INTERRUPT was ignored and interrupts were raised. + */ + vp_get_isr(ioaddr); + + if (resp.response == VIRTIO_SCSI_S_OK && resp.status == 0) { + return DISK_RET_SUCCESS; + } + return DISK_RET_EBADTRACK; +} + +int +virtio_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) +{ + struct virtio_lun_s *vlun_gf = + container_of(op->drive_gf, struct virtio_lun_s, drive); + + return virtio_scsi_cmd(GET_GLOBALFLAT(vlun_gf->ioaddr), + GET_GLOBALFLAT(vlun_gf->vq), op, cdbcmd, + GET_GLOBALFLAT(vlun_gf->target), + GET_GLOBALFLAT(vlun_gf->lun), + blocksize); +} + +static int +virtio_scsi_add_lun(struct pci_device *pci, u16 ioaddr, + struct vring_virtqueue *vq, u16 target, u16 lun) +{ + struct virtio_lun_s *vlun = malloc_fseg(sizeof(*vlun)); + if (!vlun) { + warn_noalloc(); + return -1; + } + memset(vlun, 0, sizeof(*vlun)); + vlun->drive.type = DTYPE_VIRTIO_SCSI; + vlun->drive.cntl_id = pci->bdf; + vlun->pci = pci; + vlun->ioaddr = ioaddr; + vlun->vq = vq; + vlun->target = target; + vlun->lun = lun; + + int prio = bootprio_find_scsi_device(pci, target, lun); + int ret = scsi_drive_setup(&vlun->drive, "virtio-scsi", prio); + if (ret) + goto fail; + return 0; + +fail: + free(vlun); + return -1; +} + +static int +virtio_scsi_scan_target(struct pci_device *pci, u16 ioaddr, + struct vring_virtqueue *vq, u16 target) +{ + /* TODO: send REPORT LUNS. For now, only LUN 0 is recognized. */ + int ret = virtio_scsi_add_lun(pci, ioaddr, vq, target, 0); + return ret < 0 ? 0 : 1; +} + +static void +init_virtio_scsi(struct pci_device *pci) +{ + u16 bdf = pci->bdf; + dprintf(1, "found virtio-scsi at %x:%x\n", pci_bdf_to_bus(bdf), + pci_bdf_to_dev(bdf)); + struct vring_virtqueue *vq = NULL; + u16 ioaddr = vp_init_simple(bdf); + if (vp_find_vq(ioaddr, 2, &vq) < 0 ) { + dprintf(1, "fail to find vq for virtio-scsi %x:%x\n", + pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)); + goto fail; + } + + vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE | + VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK); + + int i, tot; + for (tot = 0, i = 0; i < 256; i++) + tot += virtio_scsi_scan_target(pci, ioaddr, vq, i); + + if (!tot) + goto fail; + + return; + +fail: + free(vq); +} + +void +virtio_scsi_setup(void) +{ + ASSERT32FLAT(); + if (! CONFIG_VIRTIO_SCSI) + return; + + dprintf(3, "init virtio-scsi\n"); + + struct pci_device *pci; + foreachpci(pci) { + if (pci->vendor != PCI_VENDOR_ID_REDHAT_QUMRANET + || pci->device != PCI_DEVICE_ID_VIRTIO_SCSI) + continue; + init_virtio_scsi(pci); + } +} diff --git a/roms/seabios/src/hw/virtio-scsi.h b/roms/seabios/src/hw/virtio-scsi.h new file mode 100644 index 000000000..96c3701d2 --- /dev/null +++ b/roms/seabios/src/hw/virtio-scsi.h @@ -0,0 +1,47 @@ +#ifndef _VIRTIO_SCSI_H +#define _VIRTIO_SCSI_H + +#define VIRTIO_SCSI_CDB_SIZE 32 +#define VIRTIO_SCSI_SENSE_SIZE 96 + +struct virtio_scsi_config +{ + u32 num_queues; + u32 seg_max; + u32 max_sectors; + u32 cmd_per_lun; + u32 event_info_size; + u32 sense_size; + u32 cdb_size; + u16 max_channel; + u16 max_target; + u32 max_lun; +} __attribute__((packed)); + +/* This is the first element of the "out" scatter-gather list. */ +struct virtio_scsi_req_cmd { + u8 lun[8]; + u64 id; + u8 task_attr; + u8 prio; + u8 crn; + char cdb[VIRTIO_SCSI_CDB_SIZE]; +} __attribute__((packed)); + +/* This is the first element of the "in" scatter-gather list. */ +struct virtio_scsi_resp_cmd { + u32 sense_len; + u32 residual; + u16 status_qualifier; + u8 status; + u8 response; + u8 sense[VIRTIO_SCSI_SENSE_SIZE]; +} __attribute__((packed)); + +#define VIRTIO_SCSI_S_OK 0 + +struct disk_op_s; +int virtio_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize); +void virtio_scsi_setup(void); + +#endif /* _VIRTIO_SCSI_H */ |