diff options
author | Yang Zhang <yang.z.zhang@intel.com> | 2015-08-28 09:58:54 +0800 |
---|---|---|
committer | Yang Zhang <yang.z.zhang@intel.com> | 2015-09-01 12:44:00 +0800 |
commit | e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch) | |
tree | 66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/openhackware/src/bloc.c | |
parent | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff) |
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/openhackware/src/bloc.c')
-rw-r--r-- | qemu/roms/openhackware/src/bloc.c | 1258 |
1 files changed, 1258 insertions, 0 deletions
diff --git a/qemu/roms/openhackware/src/bloc.c b/qemu/roms/openhackware/src/bloc.c new file mode 100644 index 000000000..b171ed6e3 --- /dev/null +++ b/qemu/roms/openhackware/src/bloc.c @@ -0,0 +1,1258 @@ +/* + * <bloc.c> + * + * Open Hack'Ware BIOS bloc devices management + * + * Copyright (c) 2004-2005 Jocelyn Mayer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License V2 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdlib.h> +#include <stdio.h> +#include "bios.h" + +#undef DPRINTF +#define DPRINTF(fmt, args...) do { } while (0) + +struct bloc_device_t { + int device; + /* Hardware */ + uint32_t io_base; + int drv; + /* Geometry */ + int heads; + int trks; + int sects; + int seclen; + /* Position */ + int bloc; + int vbloc; + int vpos; + /* Access */ + int (*init)(bloc_device_t *bd, int device); + int (*read_sector)(bloc_device_t *bd, void *buffer, int secnum); + int (*ioctl)(bloc_device_t *bd, int func, void *args); + /* buffer */ + char *buffer; + /* Private data */ + int tmp; + void *private; +#ifdef USE_OPENFIRMWARE + void *OF_private; +#endif + /* Partitions */ + part_t *parts, *bparts; + part_t *boot_part; + int bpartnum; + /* Chain */ + bloc_device_t *next; +}; + +static bloc_device_t *bd_list; + +static int fdc_initialize (bloc_device_t *bd, int device); +static int fdc_read_sector (bloc_device_t *bd, void *buffer, int secnum); + +static int ide_initialize (bloc_device_t *bd, int device); +static int ide_read_sector (bloc_device_t *bd, void *buffer, int secnum); +static int ide_reset (bloc_device_t *bd); + +static int mem_initialize (bloc_device_t *bd, int device); +static int mem_read_sector (bloc_device_t *bd, void *buffer, int secnum); +static int mem_ioctl (bloc_device_t *bd, int func, void *args); + +bloc_device_t *bd_open (int device) +{ + bloc_device_t *bd; + int num; + + bd = bd_get(device); + if (bd != NULL) + return bd; + bd = malloc(sizeof(bloc_device_t)); + if (bd == NULL) + return NULL; + bd->ioctl = NULL; + switch (device) { + case 'a': + num = 0; + bd->init = &fdc_initialize; + bd->read_sector = &fdc_read_sector; + break; + case 'b': + num = 1; + bd->init = &fdc_initialize; + bd->read_sector = &fdc_read_sector; + break; + case 'c': + num = 0; + bd->init = &ide_initialize; + bd->read_sector = &ide_read_sector; + break; + case 'd': + num = 1; + bd->init = &ide_initialize; + bd->read_sector = &ide_read_sector; + break; + case 'e': + num = 2; + bd->init = &ide_initialize; + bd->read_sector = &ide_read_sector; + break; + case 'f': + num = 3; + bd->init = &ide_initialize; + bd->read_sector = &ide_read_sector; + break; + case 'm': + num = 0; + bd->init = &mem_initialize; + bd->read_sector = &mem_read_sector; + bd->ioctl = &mem_ioctl; + break; + default: + return NULL; + } + bd->bloc = -1; + if ((*bd->init)(bd, num) < 0) { + free(bd); + return NULL; + } + bd->buffer = malloc(bd->seclen); + if (bd->buffer == NULL) { + free(bd); + return NULL; + } + bd->device = device; + + return bd; +} + +int bd_seek (bloc_device_t *bd, uint32_t bloc, uint32_t pos) +{ + uint32_t maxbloc; + + maxbloc = bd_maxbloc(bd); + if (bloc > maxbloc) { + DPRINTF("%p bloc: %d maxbloc: %d C: %d H: %d S: %d\n", + bd, bloc, maxbloc, bd->trks, bd->heads, bd->sects); + return -1; + } + bd->vbloc = bloc; + bd->vpos = pos; + DPRINTF("%s: %p %08x %08x %08x %08x %08x\n", __func__, bd, bloc, pos, + bd->bloc, bd->vbloc, bd->vpos); + + return 0; +} + +int bd_read (bloc_device_t *bd, void *buffer, int len) +{ + int clen, total; + + for (total = 0; len > 0; total += clen) { + if (bd->vbloc != bd->bloc) { + /* Do physical seek */ +#if 0 + DPRINTF("Read sector %d\n", bd->vbloc); +#endif + if ((*bd->read_sector)(bd, bd->buffer, bd->vbloc) < 0) { + printf("Error reading bloc %d\n", bd->vbloc); + return -1; + } + bd->bloc = bd->vbloc; + } + clen = bd->seclen - bd->vpos; + if (clen > len) + clen = len; + memcpy(buffer, bd->buffer + bd->vpos, clen); +#if 0 + DPRINTF("%s: %p copy %d bytes (%08x %08x %08x) %08x %08x %08x %08x\n", + __func__, bd, clen, bd->bloc, bd->vbloc, bd->vpos, + ((uint32_t *)buffer)[0], ((uint32_t *)buffer)[1], + ((uint32_t *)buffer)[2], ((uint32_t *)buffer)[3]); +#endif + bd->vpos += clen; + if (bd->vpos == bd->seclen) { + bd->vbloc++; + bd->vpos = 0; + } + buffer += clen; + len -= clen; + } + + return total; +} + +int bd_write (unused bloc_device_t *bd, + unused const void *buffer, unused int len) +{ + return -1; +} + +int bd_ioctl (bloc_device_t *bd, int func, void *args) +{ + if (bd->ioctl == NULL) + return -1; + + return (*bd->ioctl)(bd, func, args); +} + +void bd_close (unused bloc_device_t *bd) +{ +} + +void bd_reset_all(void) +{ + bloc_device_t *bd; + for (bd = bd_list; bd != NULL; bd = bd->next) { + if (bd->init == &ide_initialize) { + /* reset IDE drive because Darwin wants all IDE devices to be reset */ + ide_reset(bd); + } + } +} + +uint32_t bd_seclen (bloc_device_t *bd) +{ + return bd->seclen; +} + +uint32_t bd_maxbloc (bloc_device_t *bd) +{ + return bd_CHS2sect(bd, bd->trks, 0, 1); +} + +/* XXX: to be suppressed */ +void bd_set_boot_part (bloc_device_t *bd, part_t *partition, int partnum) +{ + dprintf("%s: part %p (%p) %d\n", __func__, partition, bd->boot_part, partnum); + if (bd->boot_part == NULL) { + bd->boot_part = partition; + bd->bpartnum = partnum; + } +} + +part_t **_bd_parts (bloc_device_t *bd) +{ + return &bd->parts; +} + +part_t **_bd_bparts (bloc_device_t *bd) +{ + return &bd->bparts; +} + +void bd_set_boot_device (bloc_device_t *bd) +{ +#if defined (USE_OPENFIRMWARE) + OF_blockdev_set_boot_device(bd->OF_private, bd->bpartnum, "\\\\ofwboot"); +#endif +} + +part_t *bd_probe (int boot_device) +{ + char devices[] = { /*'a', 'b',*/ 'c', 'd', 'e', 'f', 'm', '\0', }; + bloc_device_t *bd, **cur; + part_t *boot_part, *tmp; + int i, force_raw; + + boot_part = NULL; + /* Probe bloc devices */ + for (i = 0; devices[i] != '\0'; i++) { + if (devices[i] == 'm' && boot_device != 'm') + break; + bd = bd_open(devices[i]); + if (bd != NULL) { + DPRINTF("New bloc device %c: %p\n", devices[i], bd); + for (cur = &bd_list; *cur != NULL; cur = &(*cur)->next) + continue; + *cur = bd; + } else { + DPRINTF("No bloc device %c\n", devices[i]); + } + } + /* Probe partitions for each bloc device found */ + for (bd = bd_list; bd != NULL; bd = bd->next) { + dprintf("Probe partitions for device %c\n", bd->device); + if (bd->device == 'm') + force_raw = 1; + else + force_raw = 0; + tmp = part_probe(bd, force_raw); + if (boot_device == bd->device) { + boot_part = tmp; + bd_set_boot_device(bd); + } + } + + return boot_part; +} + +bloc_device_t *bd_get (int device) +{ + bloc_device_t *cur; + + for (cur = bd_list; cur != NULL; cur = cur->next) { + if (cur->device == device) { + DPRINTF("%s: found device %c: %p\n", __func__, device, cur); + return cur; + } + } + + return NULL; +} + +void bd_put (unused bloc_device_t *bd) +{ +} + +void bd_sect2CHS (bloc_device_t *bd, uint32_t secnum, + int *cyl, int *head, int *sect) +{ + uint32_t tmp; + + tmp = secnum / bd->sects; + *sect = secnum - (tmp * bd->sects) + 1; + *cyl = tmp / bd->heads; + *head = tmp - (*cyl * bd->heads); +} + +uint32_t bd_CHS2sect (bloc_device_t *bd, + int cyl, int head, int sect) +{ + return (((cyl * bd->heads) + head) * bd->sects) + sect - 1; +} + +/* Floppy driver */ +#define FDC_OUT_BASE (0x03F0) +#define FDC_DOR_PORT (FDC_OUT_BASE + 0x0002) +#define FDC_TAPE_PORT (FDC_OUT_BASE + 0x0003) +#define FDC_MAIN_STATUS (FDC_OUT_BASE + 0x0004) +#define FDC_WRITE_PORT (FDC_OUT_BASE + 0x0005) +#define FDC_READ_PORT (FDC_OUT_BASE + 0x0005) + +static int fdc_read_data (uint8_t *addr, int len) +{ + uint8_t status; + int i; + + for (i = 0; i < len; i++) { + status = inb(FDC_MAIN_STATUS); + if ((status & 0xD0) != 0xD0) { +#if 0 + ERROR("fdc_read_data: read data status != READ_DATA: %0x\n", + status); +#endif + return -1; + } + addr[i] = inb(FDC_READ_PORT); + } + + return 0; +} + +static inline int fdc_write_cmd (uint8_t cmd) +{ + uint8_t status; + + status = inb(FDC_MAIN_STATUS); + if ((status & 0xC0) != 0x80) { +#if 0 + ERROR("fdc_write_cmd: read data status != WRITE_CMD: %0x\n", + status); +#endif + return -1; + } + outb(FDC_WRITE_PORT, cmd); + + return 0; +} + +static int fdc_reset (void) +{ + uint8_t dor; + + dor = inb(FDC_DOR_PORT); + /* Stop motors & enter reset */ + dor &= ~0x34; + outb(FDC_DOR_PORT, dor); + usleep(1000); + /* leave reset state */ + dor |= 0x04; + outb(FDC_DOR_PORT, dor); + usleep(1000); + + return 0; +} + +static int fdc_recalibrate (int drv) +{ + uint8_t data[2]; + + if (drv == 0) + data[0] = 0; + else + data[0] = 1; + if (fdc_write_cmd(0x07) < 0) { + ERROR("fdc_recalibrate != WRITE_CMD\n"); + return -1; + } + if (fdc_write_cmd(data[0]) < 0) { + ERROR("fdc_recalibrate data\n"); + return -1; + } + /* Wait for drive to go out of busy state */ + while ((inb(FDC_MAIN_STATUS) & 0x0F) != 0x00) + continue; + /* Check command status */ + if (fdc_write_cmd(0x08) < 0) { + ERROR("fdc_recalibrate != SENSE_INTERRUPT_STATUS\n"); + return -1; + } + data[0] = inb(FDC_READ_PORT); + data[1] = inb(FDC_READ_PORT); + if (data[0] & 0xD0) { + /* recalibrate / seek failed */ + return -1; + } + /* Status should be WRITE_CMD right now */ + if ((inb(FDC_MAIN_STATUS) & 0xD0) != 0x80) { + ERROR("fdc_recalibrate status\n"); + return -1; + } + + return 0; +} + +static int fdc_start_read (int drv, uint8_t hd, uint8_t trk, uint8_t sect, + int mt) +{ + uint8_t fdc_cmd[9], status; + int i; + + fdc_cmd[0] = 0x66; + if (mt) + fdc_cmd[0] |= 0x80; + fdc_cmd[1] = 0x00; + if (hd) + fdc_cmd[1] |= 0x04; + if (drv) + fdc_cmd[1] |= 0x01; + fdc_cmd[2] = trk; + fdc_cmd[3] = hd; + fdc_cmd[4] = sect; + fdc_cmd[5] = 0x02; + fdc_cmd[6] = 0x12; + fdc_cmd[7] = 0x00; + fdc_cmd[8] = 0x00; + for (i = 0; i < (int)sizeof(fdc_cmd); i++) { + status = inb(FDC_MAIN_STATUS); + if ((status & 0xC0) != 0x80) { + ERROR("fdc_start_read: write command status != WRITE_CMD: %0x\n", + status); + return -1; + } + outb(FDC_WRITE_PORT, fdc_cmd[i]); + } + status = inb(FDC_MAIN_STATUS); + if ((status & 0xD0) != 0xD0) { + ERROR("fdc_read_sector: status != READ_DATA: %0x\n", status); + return -1; + } + + return 0; +} + +/* FDC driver entry points */ +static int fdc_initialize (bloc_device_t *bd, int device) +{ + uint8_t fifo[10]; +#if 0 + uint32_t tape; +#endif + int status; + + if (device > 1) + return -1; + DPRINTF("Init FDC drive %d\n", device); + /* Manage 1.44 MB disks only, for now */ + bd->drv = device; + bd->heads = 2; + bd->trks = 80; + bd->sects = 18; + bd->seclen = 512; + bd->tmp = -1; + fdc_reset(); + /* Dump registers */ + if (fdc_write_cmd(0x0E) < 0) { +#if 0 + ERROR("fdc_reset: DUMP_REGISTER != WRITE_CMD\n"); +#endif + return -1; + } + if (fdc_read_data(fifo, 10) < 0) { + ERROR("fdc_reset: DUMP_REGISTER data\n"); + return -1; + } + /* SPECIFY: be sure we're not in DMA mode */ + if (fdc_write_cmd(0x03) < 0) { + ERROR("fdc_reset: SPECIFY != WRITE_CMD\n"); + return -1; + } + if (fdc_write_cmd(fifo[4]) < 0 || fdc_write_cmd(fifo[5] | 0x01)) { + ERROR("fdc_reset: SPECIFY data\n"); + return -1; + } + /* Status should be WRITE_CMD right now */ + status = inb(FDC_MAIN_STATUS); + if ((status & 0xD0) != 0x80) { + ERROR("fdc_initialise: read data status != WRITE_CMD: %0x\n", + status); + return -1; + } + /* RECALIBRATE */ + if (fdc_recalibrate(device) < 0) { + printf("fd%c: no floppy inserted\n", 'a' + device); + return -1; + } + printf("fd%c initialized\n", 'a' + device); + + return 0; +} + +static int fdc_read_sector (bloc_device_t *bd, void *buffer, int secnum) +{ + int head, cyl, sect; + int need_restart; + +#if DEBUG_BIOS > 1 + printf("Read fdc sector: %d at: %0x\n", secnum, (uint32_t)buffer); + bd_sect2CHS(bd, secnum, &cyl, &head, §); + printf("cur: %d hd: %d trk: %d sect: %d\n", bd->bloc, head, cyl, sect); +#endif + if (secnum != bd->tmp) { + if (fdc_reset() < 0 || fdc_recalibrate(bd->drv) < 0) + return -1; + need_restart = 1; + } else { + need_restart = 0; + } + bd_sect2CHS(bd, secnum, &cyl, &head, §); + if (need_restart == 1 || (head == 0 && sect == 1)) { + if (need_restart == 0) { + /* Read the status */ + uint8_t tmp[7]; + + while (fdc_read_data(tmp, 1) == 0) + continue; + } +#if !defined (DEBUG_BIOS) + printf("."); +#endif + if (fdc_start_read(bd->drv, head, cyl, sect, 1) < 0) + return -1; + bd->bloc = secnum; + bd->tmp = secnum; + } + if (fdc_read_data(buffer, bd->seclen) < 0) + return -1; + bd->tmp++; + + return bd->seclen; +} + +/* SCSI subset */ +/* SPC: primary commands, common to all devices */ +static int spc_inquiry_req (void *buffer, int maxlen) +{ + uint8_t *p; + + p = buffer; + p[0] = 0x12; + /* No page code */ + p[1] = 0x00; + p[2] = 0x00; + p[3] = maxlen >> 8; + p[4] = maxlen; + p[5] = 0x00; + + return 6; +} + +static int spc_inquiry_treat (void *buffer, int len) +{ + const uint8_t *p; + + if (len < 36) + return -1; + p = buffer; + if ((p[0] >> 5) != 0) { + ERROR("Logical unit not ready\n"); + return -1; + } + + return p[0] & 0x1F; +} + +static int spc_test_unit_ready_req (void *buffer) +{ + uint8_t *p; + + p = buffer; + p[0] = 0x00; + p[1] = 0x00; + p[2] = 0x00; + p[3] = 0x00; + p[4] = 0x00; + p[5] = 0x00; + + return 6; +} + +/* MMC: multimedia commands */ +static int mmc_read_capacity_req (void *buffer) +{ + uint8_t *p; + + p = buffer; + p[0] = 0x25; + p[1] = 0x00; + p[2] = 0x00; + p[3] = 0x00; + p[4] = 0x00; + p[5] = 0x00; + p[6] = 0x00; + p[7] = 0x00; + p[8] = 0x00; + p[9] = 0x00; + + return 10; +} + +static int mmc_read_capacity_treat (uint32_t *size, uint32_t *ssize, + const void *buffer, int len) +{ + const uint8_t *p; + + if (len != 8) + return -1; + p = buffer; + /* Only handle CDROM address mode for now */ + *size = ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]) + 1; + *ssize = ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]); + + return 0; +} + +static int mmc_read12_req (void *buffer, uint32_t LBA, uint32_t size) +{ + uint8_t *p; + + p = buffer; + p[0] = 0xA8; + p[1] = 0x00; + p[2] = LBA >> 24; + p[3] = LBA >> 16; + p[4] = LBA >> 8; + p[5] = LBA; + p[6] = size >> 24; + p[7] = size >> 16; + p[8] = size >> 8; + p[9] = size; + p[10] = 0x00; + p[11] = 0x00; + + return 12; +} + +/* IDE disk driver */ +static uint32_t ide_base[2] = { 0x1F0, 0x170, }; +static uint32_t ide_base2[2] = { 0x3F6, 0x376, }; + +typedef struct ide_ops_t { + uint8_t (*port_read)(bloc_device_t *bd, int port); + void (*port_write)(bloc_device_t *bd, int port, uint8_t value); + uint32_t (*data_readl)(bloc_device_t *bd); + void (*data_writel)(bloc_device_t *bd, uint32_t val); + void (*control_write)(bloc_device_t *bd, uint32_t val); + uint32_t base[4]; +#ifdef USE_OPENFIRMWARE + void *OF_private[2]; +#endif +} ide_ops_t; + +/* IDE ISA access */ +static uint8_t ide_isa_port_read (bloc_device_t *bd, int port) +{ + return inb(bd->io_base + port); +} + +static void ide_isa_port_write (bloc_device_t *bd, int port, uint8_t value) +{ + outb(bd->io_base + port, value); +} + +static uint32_t ide_isa_data_readl (bloc_device_t *bd) +{ + return inl(bd->io_base); +} + +static void ide_isa_data_writel (bloc_device_t *bd, uint32_t val) +{ + return outl(bd->io_base, val); +} + +static void ide_isa_control_write (bloc_device_t *bd, uint32_t val) +{ + outb(bd->tmp, val); +} + +static ide_ops_t ide_isa_ops = { + &ide_isa_port_read, + &ide_isa_port_write, + &ide_isa_data_readl, + &ide_isa_data_writel, + &ide_isa_control_write, + { 0, }, +#ifdef USE_OPENFIRMWARE + { NULL, }, +#endif +}; + +static ide_ops_t *ide_pci_ops; + +/* IDE PCI access for pc */ +static uint8_t ide_pci_port_read (bloc_device_t *bd, int port) +{ + uint8_t value; + value = inb(bd->io_base + port); + return value; +} + +static void ide_pci_port_write (bloc_device_t *bd, int port, uint8_t value) +{ + outb(bd->io_base + port, value); +} + +static uint32_t ide_pci_data_readl (bloc_device_t *bd) +{ + return inl(bd->io_base); +} + +static void ide_pci_data_writel (bloc_device_t *bd, uint32_t val) +{ + outl(bd->io_base, val); +} + +static void ide_pci_control_write (bloc_device_t *bd, uint32_t val) +{ + outb(bd->tmp + 2, val); +} + +static ide_ops_t ide_pci_pc_ops = { + &ide_pci_port_read, + &ide_pci_port_write, + &ide_pci_data_readl, + &ide_pci_data_writel, + &ide_pci_control_write, + { 0, }, +#ifdef USE_OPENFIRMWARE + { NULL, }, +#endif +}; + +void ide_pci_pc_register (uint32_t io_base0, uint32_t io_base1, + uint32_t io_base2, uint32_t io_base3, + void *OF_private0, void *OF_private1) +{ + if (ide_pci_ops == NULL) { + ide_pci_ops = malloc(sizeof(ide_ops_t)); + if (ide_pci_ops == NULL) + return; + memcpy(ide_pci_ops, &ide_pci_pc_ops, sizeof(ide_ops_t)); + } + if ((io_base0 != 0 || io_base1 != 0) && + ide_pci_ops->base[0] == 0 && ide_pci_ops->base[2] == 0) { + ide_pci_ops->base[0] = io_base0; + ide_pci_ops->base[2] = io_base1; +#ifdef USE_OPENFIRMWARE + ide_pci_ops->OF_private[0] = OF_private0; +#endif + } + if ((io_base2 != 0 || io_base3 != 0) && + ide_pci_ops->base[1] == 0 && ide_pci_ops->base[3] == 0) { + ide_pci_ops->base[1] = io_base2; + ide_pci_ops->base[3] = io_base3; +#ifdef USE_OPENFIRMWARE + ide_pci_ops->OF_private[1] = OF_private1; +#endif + } +} + +/* IDE PCI access for pmac */ +static uint8_t ide_pmac_port_read (bloc_device_t *bd, int port) +{ + uint32_t addr; + + if (port != 8) + addr = bd->io_base + (port << 4); + else + addr = bd->io_base + 0x160; + eieio(); + + return *((uint8_t *)addr); +} + +static void ide_pmac_port_write (bloc_device_t *bd, int port, uint8_t value) +{ + uint32_t addr; + + if (port != 8) + addr = bd->io_base + (port << 4); + else + addr = bd->io_base + 0x160; + *((uint8_t *)addr) = value; + eieio(); +} + +static uint32_t ide_pmac_data_readl (bloc_device_t *bd) +{ + eieio(); + return ldswap32((uint32_t *)bd->io_base); + // return *((uint32_t *)bd->io_base); +} + +static void ide_pmac_data_writel (bloc_device_t *bd, uint32_t val) +{ + // *((uint32_t *)bd->io_base) = val; + stswap32((uint32_t *)bd->io_base, val); + eieio(); +} + +static void ide_pmac_control_write (bloc_device_t *bd, uint32_t val) +{ + ide_pmac_port_write(bd, 8, val); +} + +static ide_ops_t ide_pmac_ops = { + &ide_pmac_port_read, + &ide_pmac_port_write, + &ide_pmac_data_readl, + &ide_pmac_data_writel, + &ide_pmac_control_write, + { 0, }, +#ifdef USE_OPENFIRMWARE + { NULL, }, +#endif +}; + +void ide_pci_pmac_register (uint32_t io_base0, uint32_t io_base1, + unused void *OF_private) +{ + if (ide_pci_ops == NULL) { + ide_pci_ops = malloc(sizeof(ide_ops_t)); + if (ide_pci_ops == NULL) + return; + memcpy(ide_pci_ops, &ide_pmac_ops, sizeof(ide_ops_t)); + } + if (io_base0 != 0 && ide_pci_ops->base[0] == 0) { + ide_pci_ops->base[0] = io_base0; +#ifdef USE_OPENFIRMWARE + ide_pci_ops->OF_private[0] = OF_private; +#endif + } + if (io_base1 != 0 && ide_pci_ops->base[1] == 0) { + ide_pci_ops->base[1] = io_base1; +#ifdef USE_OPENFIRMWARE + ide_pci_ops->OF_private[1] = OF_private; +#endif + } +} + +static inline uint8_t ide_port_read (bloc_device_t *bd, int port) +{ + ide_ops_t *ops = bd->private; + + return ops->port_read(bd, port); +} + +static inline void ide_port_write (bloc_device_t *bd, int port, uint8_t value) +{ + ide_ops_t *ops = bd->private; + + ops->port_write(bd, port, value); +} + +static inline uint32_t ide_data_readl (bloc_device_t *bd) +{ + ide_ops_t *ops = bd->private; + + return ops->data_readl(bd); +} + +static inline void ide_data_writel (bloc_device_t *bd, uint32_t val) +{ + ide_ops_t *ops = bd->private; + + return ops->data_writel(bd, val); +} + +static inline void ide_control_write (bloc_device_t *bd, uint32_t val) +{ + ide_ops_t *ops = bd->private; + + return ops->control_write(bd, val); +} + +static int ide_reset (bloc_device_t *bd) +{ + int status, is_cdrom, lcyl; + + ide_control_write(bd, 0x04); + status = ide_port_read(bd, 0x07); + if (status != 0x90) { + return -1; + } + ide_control_write(bd, 0x00); + if (bd->drv == 0) + ide_port_write(bd, 0x06, 0xa0); + else + ide_port_write(bd, 0x06, 0xb0); + + lcyl = ide_port_read(bd, 0x04); + switch (lcyl) { + case 0x00: + /* IDE drive */ + is_cdrom = 0; + break; + case 0x14: + /* ATAPI device */ + is_cdrom = 1; + break; + default: + return -1; + } + + return is_cdrom; +} + +static void atapi_pad_req (void *buffer, int len); +static void atapi_make_req (bloc_device_t *bd, uint32_t *buffer, + int maxlen); +static int atapi_read_sector (bloc_device_t *bd, void *buffer, int secnum); + +static int ide_initialize (bloc_device_t *bd, int device) +{ +#ifdef USE_OPENFIRMWARE + void *OF_parent; +#endif + const unsigned char *devname, *devtype, *alias; + uint32_t atapi_buffer[9]; + uint32_t size; + int status, base, is_cdrom, len, i; + + if (device > 1) + base = 1; + else + base = 0; + if (ide_pci_ops != NULL) { + bd->private = ide_pci_ops; + bd->io_base = ide_pci_ops->base[base]; + bd->tmp = ide_pci_ops->base[2 + base]; + if (bd->io_base == 0x00000000 || bd->io_base == 0xFFFFFFFF) { + ERROR("No IDE drive %c\n", device); + return -1; + } + } else { + bd->private = &ide_isa_ops; + bd->io_base = ide_base[base]; + bd->tmp = ide_base2[base]; + } + bd->drv = device & 1; + DPRINTF("Init IDE drive %d-%d (%d)\n", base, bd->drv, device); + is_cdrom = ide_reset(bd); + printf("ide%d: drive %d: ", + (device >> 1), bd->drv); + switch(is_cdrom) { + case 0: + printf("Hard Disk\n"); + devname = "disk"; + devtype = "hd"; + alias = "hd"; + break; + case 1: + printf("CD-ROM\n"); + devname = "cdrom"; + devtype = "cdrom"; + alias = "cd"; + break; + default: + printf("none\n"); + devname = NULL; + devtype = NULL; + alias = NULL; + break; + } + if (is_cdrom < 0) + return -1; +#ifdef USE_OPENFIRMWARE + /* Register disk into OF tree */ + OF_parent = ide_pci_ops->OF_private[base]; + if (OF_parent != NULL) { + bd->OF_private = OF_blockdev_register(OF_parent, bd, devtype, + devname, bd->drv, alias); + } +#endif + /* Select drive */ + if (bd->drv == 0) + ide_port_write(bd, 0x06, 0x40); + else + ide_port_write(bd, 0x06, 0x50); + /* WIN_DEVICE_RESET */ + ide_port_write(bd, 0x07, 0x08); + status = ide_port_read(bd, 0x07); + if (is_cdrom) { + if (status != 0x00) { + ERROR("WIN_DEVICE_RESET : status %0x != 0x00 (is_cdrom: %d)\n", + status, is_cdrom); + return -1; + } + /* TEST_UNIT_READY */ + DPRINTF("TEST_UNIT_READY\n"); + len = spc_test_unit_ready_req(&atapi_buffer); + atapi_pad_req(&atapi_buffer, len); + ide_port_write(bd, 0x07, 0xA0); + status = ide_port_read(bd, 0x07); + if (status != 0x08) { + ERROR("ATAPI TEST_UNIT_READY : status %0x != 0x08\n", status); + /*return -1;*/ /* fails to boot from cdrom? */ + } + for (i = 0; i < 3; i++) { + ide_data_writel(bd, ldswap32(&atapi_buffer[i])); + } + status = ide_port_read(bd, 0x07); + if (status != 0x40) { + ERROR("ATAPI TEST_UNIT_READY : status %0x != 0x40\n", status); + return -1; + } + /* INQUIRY */ + DPRINTF("INQUIRY\n"); + len = spc_inquiry_req(&atapi_buffer, 36); + atapi_pad_req(&atapi_buffer, len); + atapi_make_req(bd, atapi_buffer, 36); + status = ide_port_read(bd, 0x07); + if (status != 0x48) { + ERROR("ATAPI INQUIRY : status %0x != 0x48\n", status); + return -1; + } + for (i = 0; i < 9; i++) + stswap32(&atapi_buffer[i], ide_data_readl(bd)); + if (spc_inquiry_treat(&atapi_buffer, 36) != 0x05) { + ERROR("Only ATAPI CDROMs are handled for now\n"); + return -1; + } + /* READ_CAPACITY */ + DPRINTF("READ_CAPACITY\n"); + len = mmc_read_capacity_req(&atapi_buffer); + atapi_pad_req(&atapi_buffer, len); + atapi_make_req(bd, atapi_buffer, 8); + status = ide_port_read(bd, 0x07); + if (status != 0x48) { + ERROR("ATAPI READ_CAPACITY : status %0x != 0x48\n", status); + return -1; + } + for (i = 0; i < 2; i++) + stswap32(&atapi_buffer[i], ide_data_readl(bd)); + if (mmc_read_capacity_treat(&size, &bd->seclen, + &atapi_buffer, 8) != 0) { + ERROR("Error retrieving ATAPI CDROM capacity\n"); + return -1; + } + bd->read_sector = &atapi_read_sector; + DPRINTF("ATAPI: size=%d ssize=%d\n", size, bd->seclen); + } else { + if (status != 0x41) { + ERROR("WIN_DEVICE_RESET : status %0x != 0x41 (is_cdrom: %d)\n", + status, is_cdrom); + return -1; + } + /* WIN_READ_NATIVE_MAX */ + ide_port_write(bd, 0x07, 0xF8); + status = ide_port_read(bd, 0x07); + if (status != 0x40) { + ERROR("WIN_READ_NATIVE_MAX : status %0x != 0x40\n", status); + return -1; + } + /* Retrieve parameters */ + size = (ide_port_read(bd, 0x06) & ~0xF0) << 24; + size |= ide_port_read(bd, 0x05) << 16; + size |= ide_port_read(bd, 0x04) << 8; + size |= ide_port_read(bd, 0x03); + bd->seclen = 512; + } + bd->heads = 16; + bd->sects = 64; + bd->trks = (size + (16 * 64 - 1)) >> 10; + + return 0; +} + +static void atapi_pad_req (void *buffer, int len) +{ + uint8_t *p; + + p = buffer; + memset(p + len, 0, 12 - len); +} + +static void atapi_make_req (bloc_device_t *bd, uint32_t *buffer, + int maxlen) +{ + int i; + /* select drive */ + if (bd->drv == 0) + ide_port_write(bd, 0x06, 0x40); + else + ide_port_write(bd, 0x06, 0x50); + ide_port_write(bd, 0x04, maxlen & 0xff); + ide_port_write(bd, 0x05, (maxlen >> 8) & 0xff); + ide_port_write(bd, 0x07, 0xA0); + for (i = 0; i < 3; i++) + ide_data_writel(bd, ldswap32(&buffer[i])); +} + +static int atapi_read_sector (bloc_device_t *bd, void *buffer, int secnum) +{ + uint32_t atapi_buffer[4]; + uint8_t *p; + uint32_t status, value; + int i, len; + + len = mmc_read12_req(atapi_buffer, secnum, 1); + atapi_pad_req(&atapi_buffer, len); + atapi_make_req(bd, atapi_buffer, bd->seclen); + status = ide_port_read(bd, 0x07); + if (status != 0x48) { + ERROR("ATAPI READ12 : status %0x != 0x48\n", status); + return -1; + } + p = buffer; + for (i = 0; i < bd->seclen; i += 4) { + value = ide_data_readl(bd); + *p++ = value; + *p++ = value >> 8; + *p++ = value >> 16; + *p++ = value >> 24; + } + status = ide_port_read(bd, 0x07); + if (status != 0x40) { + ERROR("ATAPI READ12 done : status %0x != 0x48\n", status); + return -1; + } + + return 0; +} + +static int ide_read_sector (bloc_device_t *bd, void *buffer, int secnum) +{ + uint32_t value; + uint8_t *p; + int status; + int i; + + bd->drv &= 1; + // printf("ide_read_sector: drv %d secnum %d buf %p\n", bd->drv, secnum, buffer); + /* select drive & set highest bytes */ + if (bd->drv == 0) + ide_port_write(bd, 0x06, 0x40 | (secnum >> 24)); + else + ide_port_write(bd, 0x06, 0x50 | (secnum >> 24)); + /* Set hcyl */ + ide_port_write(bd, 0x05, secnum >> 16); + /* Set lcyl */ + ide_port_write(bd, 0x04, secnum >> 8); + /* Set sect */ + ide_port_write(bd, 0x03, secnum); + /* Read 1 sector */ + ide_port_write(bd, 0x02, 1); + /* WIN_READ */ + ide_port_write(bd, 0x07, 0x20); + status = ide_port_read(bd, 0x07); + // DPRINTF("ide_read_sector: try %d\n", secnum); + if (status != 0x58) { + ERROR("ide_read_sector: %d status %0x != 0x58\n", secnum, status); + return -1; + } + /* Get data */ + p = buffer; + for (i = 0; i < bd->seclen; i += 4) { + value = ide_data_readl(bd); + *p++ = value; + *p++ = value >> 8; + *p++ = value >> 16; + *p++ = value >> 24; + } + status = ide_port_read(bd, 0x07); + if (status != 0x50) { + ERROR("ide_read_sector 6: status %0x != 0x50\n", status); + return -1; + } + + return bd->seclen; +} + +/* Memory image access driver */ +static int mem_initialize (bloc_device_t *bd, int device) +{ + bd->seclen = 512; + bd->private = NULL; + bd->heads = 1; + bd->sects = 1; + bd->trks = 1; + + return device == 'm'; +} + +static int mem_read_sector (bloc_device_t *bd, void *buffer, int secnum) +{ + if (buffer != (char *)bd->private + (bd->seclen * secnum)) { + memmove(buffer, + (char *)bd->private + (bd->seclen * secnum), bd->seclen); + } + + return bd->seclen; +} + +static int mem_ioctl (bloc_device_t *bd, int func, void *args) +{ + uint32_t *u32; + int ret; + + switch (func) { + case MEM_SET_ADDR: + bd->private = args; + ret = 0; + break; + case MEM_SET_SIZE: + u32 = args; + bd->trks = (*u32 + bd->seclen - 1) / bd->seclen + 1; + default: + ret = -1; + break; + } + + return ret; +} |