qemu/hw/ide/cmd646.c
<<
>>
Prefs
   1/*
   2 * QEMU IDE Emulation: PCI cmd646 support.
   3 *
   4 * Copyright (c) 2003 Fabrice Bellard
   5 * Copyright (c) 2006 Openedhand Ltd.
   6 *
   7 * Permission is hereby granted, free of charge, to any person obtaining a copy
   8 * of this software and associated documentation files (the "Software"), to deal
   9 * in the Software without restriction, including without limitation the rights
  10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11 * copies of the Software, and to permit persons to whom the Software is
  12 * furnished to do so, subject to the following conditions:
  13 *
  14 * The above copyright notice and this permission notice shall be included in
  15 * all copies or substantial portions of the Software.
  16 *
  17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23 * THE SOFTWARE.
  24 */
  25#include <hw/hw.h>
  26#include <hw/pc.h>
  27#include <hw/pci.h>
  28#include <hw/isa.h>
  29#include "block.h"
  30#include "sysemu.h"
  31#include "dma.h"
  32
  33#include <hw/ide/pci.h>
  34
  35/* CMD646 specific */
  36#define MRDMODE         0x71
  37#define   MRDMODE_INTR_CH0      0x04
  38#define   MRDMODE_INTR_CH1      0x08
  39#define   MRDMODE_BLK_CH0       0x10
  40#define   MRDMODE_BLK_CH1       0x20
  41#define UDIDETCR0       0x73
  42#define UDIDETCR1       0x7B
  43
  44static void cmd646_update_irq(PCIIDEState *d);
  45
  46static uint64_t cmd646_cmd_read(void *opaque, hwaddr addr,
  47                                unsigned size)
  48{
  49    CMD646BAR *cmd646bar = opaque;
  50
  51    if (addr != 2 || size != 1) {
  52        return ((uint64_t)1 << (size * 8)) - 1;
  53    }
  54    return ide_status_read(cmd646bar->bus, addr + 2);
  55}
  56
  57static void cmd646_cmd_write(void *opaque, hwaddr addr,
  58                             uint64_t data, unsigned size)
  59{
  60    CMD646BAR *cmd646bar = opaque;
  61
  62    if (addr != 2 || size != 1) {
  63        return;
  64    }
  65    ide_cmd_write(cmd646bar->bus, addr + 2, data);
  66}
  67
  68static const MemoryRegionOps cmd646_cmd_ops = {
  69    .read = cmd646_cmd_read,
  70    .write = cmd646_cmd_write,
  71    .endianness = DEVICE_LITTLE_ENDIAN,
  72};
  73
  74static uint64_t cmd646_data_read(void *opaque, hwaddr addr,
  75                                 unsigned size)
  76{
  77    CMD646BAR *cmd646bar = opaque;
  78
  79    if (size == 1) {
  80        return ide_ioport_read(cmd646bar->bus, addr);
  81    } else if (addr == 0) {
  82        if (size == 2) {
  83            return ide_data_readw(cmd646bar->bus, addr);
  84        } else {
  85            return ide_data_readl(cmd646bar->bus, addr);
  86        }
  87    }
  88    return ((uint64_t)1 << (size * 8)) - 1;
  89}
  90
  91static void cmd646_data_write(void *opaque, hwaddr addr,
  92                             uint64_t data, unsigned size)
  93{
  94    CMD646BAR *cmd646bar = opaque;
  95
  96    if (size == 1) {
  97        ide_ioport_write(cmd646bar->bus, addr, data);
  98    } else if (addr == 0) {
  99        if (size == 2) {
 100            ide_data_writew(cmd646bar->bus, addr, data);
 101        } else {
 102            ide_data_writel(cmd646bar->bus, addr, data);
 103        }
 104    }
 105}
 106
 107static const MemoryRegionOps cmd646_data_ops = {
 108    .read = cmd646_data_read,
 109    .write = cmd646_data_write,
 110    .endianness = DEVICE_LITTLE_ENDIAN,
 111};
 112
 113static void setup_cmd646_bar(PCIIDEState *d, int bus_num)
 114{
 115    IDEBus *bus = &d->bus[bus_num];
 116    CMD646BAR *bar = &d->cmd646_bar[bus_num];
 117
 118    bar->bus = bus;
 119    bar->pci_dev = d;
 120    memory_region_init_io(&bar->cmd, &cmd646_cmd_ops, bar, "cmd646-cmd", 4);
 121    memory_region_init_io(&bar->data, &cmd646_data_ops, bar, "cmd646-data", 8);
 122}
 123
 124static uint64_t bmdma_read(void *opaque, hwaddr addr,
 125                           unsigned size)
 126{
 127    BMDMAState *bm = opaque;
 128    PCIIDEState *pci_dev = bm->pci_dev;
 129    uint32_t val;
 130
 131    if (size != 1) {
 132        return ((uint64_t)1 << (size * 8)) - 1;
 133    }
 134
 135    switch(addr & 3) {
 136    case 0:
 137        val = bm->cmd;
 138        break;
 139    case 1:
 140        val = pci_dev->dev.config[MRDMODE];
 141        break;
 142    case 2:
 143        val = bm->status;
 144        break;
 145    case 3:
 146        if (bm == &pci_dev->bmdma[0]) {
 147            val = pci_dev->dev.config[UDIDETCR0];
 148        } else {
 149            val = pci_dev->dev.config[UDIDETCR1];
 150        }
 151        break;
 152    default:
 153        val = 0xff;
 154        break;
 155    }
 156#ifdef DEBUG_IDE
 157    printf("bmdma: readb 0x%02x : 0x%02x\n", addr, val);
 158#endif
 159    return val;
 160}
 161
 162static void bmdma_write(void *opaque, hwaddr addr,
 163                        uint64_t val, unsigned size)
 164{
 165    BMDMAState *bm = opaque;
 166    PCIIDEState *pci_dev = bm->pci_dev;
 167
 168    if (size != 1) {
 169        return;
 170    }
 171
 172#ifdef DEBUG_IDE
 173    printf("bmdma: writeb 0x%02x : 0x%02x\n", addr, val);
 174#endif
 175    switch(addr & 3) {
 176    case 0:
 177        bmdma_cmd_writeb(bm, val);
 178        break;
 179    case 1:
 180        pci_dev->dev.config[MRDMODE] =
 181            (pci_dev->dev.config[MRDMODE] & ~0x30) | (val & 0x30);
 182        cmd646_update_irq(pci_dev);
 183        break;
 184    case 2:
 185        bm->status = (val & 0x60) | (bm->status & 1) | (bm->status & ~val & 0x06);
 186        break;
 187    case 3:
 188        if (bm == &pci_dev->bmdma[0])
 189            pci_dev->dev.config[UDIDETCR0] = val;
 190        else
 191            pci_dev->dev.config[UDIDETCR1] = val;
 192        break;
 193    }
 194}
 195
 196static const MemoryRegionOps cmd646_bmdma_ops = {
 197    .read = bmdma_read,
 198    .write = bmdma_write,
 199};
 200
 201static void bmdma_setup_bar(PCIIDEState *d)
 202{
 203    BMDMAState *bm;
 204    int i;
 205
 206    memory_region_init(&d->bmdma_bar, "cmd646-bmdma", 16);
 207    for(i = 0;i < 2; i++) {
 208        bm = &d->bmdma[i];
 209        memory_region_init_io(&bm->extra_io, &cmd646_bmdma_ops, bm,
 210                              "cmd646-bmdma-bus", 4);
 211        memory_region_add_subregion(&d->bmdma_bar, i * 8, &bm->extra_io);
 212        memory_region_init_io(&bm->addr_ioport, &bmdma_addr_ioport_ops, bm,
 213                              "cmd646-bmdma-ioport", 4);
 214        memory_region_add_subregion(&d->bmdma_bar, i * 8 + 4, &bm->addr_ioport);
 215    }
 216}
 217
 218/* XXX: call it also when the MRDMODE is changed from the PCI config
 219   registers */
 220static void cmd646_update_irq(PCIIDEState *d)
 221{
 222    int pci_level;
 223    pci_level = ((d->dev.config[MRDMODE] & MRDMODE_INTR_CH0) &&
 224                 !(d->dev.config[MRDMODE] & MRDMODE_BLK_CH0)) ||
 225        ((d->dev.config[MRDMODE] & MRDMODE_INTR_CH1) &&
 226         !(d->dev.config[MRDMODE] & MRDMODE_BLK_CH1));
 227    qemu_set_irq(d->dev.irq[0], pci_level);
 228}
 229
 230/* the PCI irq level is the logical OR of the two channels */
 231static void cmd646_set_irq(void *opaque, int channel, int level)
 232{
 233    PCIIDEState *d = opaque;
 234    int irq_mask;
 235
 236    irq_mask = MRDMODE_INTR_CH0 << channel;
 237    if (level)
 238        d->dev.config[MRDMODE] |= irq_mask;
 239    else
 240        d->dev.config[MRDMODE] &= ~irq_mask;
 241    cmd646_update_irq(d);
 242}
 243
 244static void cmd646_reset(void *opaque)
 245{
 246    PCIIDEState *d = opaque;
 247    unsigned int i;
 248
 249    for (i = 0; i < 2; i++) {
 250        ide_bus_reset(&d->bus[i]);
 251    }
 252}
 253
 254/* CMD646 PCI IDE controller */
 255static int pci_cmd646_ide_initfn(PCIDevice *dev)
 256{
 257    PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev);
 258    uint8_t *pci_conf = d->dev.config;
 259    qemu_irq *irq;
 260    int i;
 261
 262    pci_conf[PCI_CLASS_PROG] = 0x8f;
 263
 264    pci_conf[0x51] = 0x04; // enable IDE0
 265    if (d->secondary) {
 266        /* XXX: if not enabled, really disable the seconday IDE controller */
 267        pci_conf[0x51] |= 0x08; /* enable IDE1 */
 268    }
 269
 270    setup_cmd646_bar(d, 0);
 271    setup_cmd646_bar(d, 1);
 272    pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd646_bar[0].data);
 273    pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd646_bar[0].cmd);
 274    pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd646_bar[1].data);
 275    pci_register_bar(dev, 3, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd646_bar[1].cmd);
 276    bmdma_setup_bar(d);
 277    pci_register_bar(dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &d->bmdma_bar);
 278
 279    /* TODO: RST# value should be 0 */
 280    pci_conf[PCI_INTERRUPT_PIN] = 0x01; // interrupt on pin 1
 281
 282    irq = qemu_allocate_irqs(cmd646_set_irq, d, 2);
 283    for (i = 0; i < 2; i++) {
 284        ide_bus_new(&d->bus[i], &d->dev.qdev, i);
 285        ide_init2(&d->bus[i], irq[i]);
 286
 287        bmdma_init(&d->bus[i], &d->bmdma[i], d);
 288        d->bmdma[i].bus = &d->bus[i];
 289        qemu_add_vm_change_state_handler(d->bus[i].dma->ops->restart_cb,
 290                                         &d->bmdma[i].dma);
 291    }
 292
 293    vmstate_register(&dev->qdev, 0, &vmstate_ide_pci, d);
 294    qemu_register_reset(cmd646_reset, d);
 295    return 0;
 296}
 297
 298static void pci_cmd646_ide_exitfn(PCIDevice *dev)
 299{
 300    PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev);
 301    unsigned i;
 302
 303    for (i = 0; i < 2; ++i) {
 304        memory_region_del_subregion(&d->bmdma_bar, &d->bmdma[i].extra_io);
 305        memory_region_destroy(&d->bmdma[i].extra_io);
 306        memory_region_del_subregion(&d->bmdma_bar, &d->bmdma[i].addr_ioport);
 307        memory_region_destroy(&d->bmdma[i].addr_ioport);
 308        memory_region_destroy(&d->cmd646_bar[i].cmd);
 309        memory_region_destroy(&d->cmd646_bar[i].data);
 310    }
 311    memory_region_destroy(&d->bmdma_bar);
 312}
 313
 314void pci_cmd646_ide_init(PCIBus *bus, DriveInfo **hd_table,
 315                         int secondary_ide_enabled)
 316{
 317    PCIDevice *dev;
 318
 319    dev = pci_create(bus, -1, "cmd646-ide");
 320    qdev_prop_set_uint32(&dev->qdev, "secondary", secondary_ide_enabled);
 321    qdev_init_nofail(&dev->qdev);
 322
 323    pci_ide_create_devs(dev, hd_table);
 324}
 325
 326static Property cmd646_ide_properties[] = {
 327    DEFINE_PROP_UINT32("secondary", PCIIDEState, secondary, 0),
 328    DEFINE_PROP_END_OF_LIST(),
 329};
 330
 331static void cmd646_ide_class_init(ObjectClass *klass, void *data)
 332{
 333    DeviceClass *dc = DEVICE_CLASS(klass);
 334    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
 335
 336    k->init = pci_cmd646_ide_initfn;
 337    k->exit = pci_cmd646_ide_exitfn;
 338    k->vendor_id = PCI_VENDOR_ID_CMD;
 339    k->device_id = PCI_DEVICE_ID_CMD_646;
 340    k->revision = 0x07;
 341    k->class_id = PCI_CLASS_STORAGE_IDE;
 342    dc->props = cmd646_ide_properties;
 343}
 344
 345static TypeInfo cmd646_ide_info = {
 346    .name          = "cmd646-ide",
 347    .parent        = TYPE_PCI_DEVICE,
 348    .instance_size = sizeof(PCIIDEState),
 349    .class_init    = cmd646_ide_class_init,
 350};
 351
 352static void cmd646_ide_register_types(void)
 353{
 354    type_register_static(&cmd646_ide_info);
 355}
 356
 357type_init(cmd646_ide_register_types)
 358