cmd646.c 10.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*
 * QEMU IDE Emulation: PCI cmd646 support.
 *
 * Copyright (c) 2003 Fabrice Bellard
 * Copyright (c) 2006 Openedhand Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include <hw/hw.h>
#include <hw/pc.h>
#include <hw/pci.h>
#include <hw/isa.h>
#include "block.h"
#include "sysemu.h"
#include "dma.h"

#include <hw/ide/pci.h>

/* CMD646 specific */
#define MRDMODE		0x71
#define   MRDMODE_INTR_CH0	0x04
#define   MRDMODE_INTR_CH1	0x08
#define   MRDMODE_BLK_CH0	0x10
#define   MRDMODE_BLK_CH1	0x20
#define UDIDETCR0	0x73
#define UDIDETCR1	0x7B

static void cmd646_update_irq(PCIIDEState *d);

Avi Kivity's avatar
Avi Kivity committed
46
47
static uint64_t cmd646_cmd_read(void *opaque, target_phys_addr_t addr,
                                unsigned size)
48
{
Avi Kivity's avatar
Avi Kivity committed
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
    CMD646BAR *cmd646bar = opaque;

    if (addr != 2 || size != 1) {
        return ((uint64_t)1 << (size * 8)) - 1;
    }
    return ide_status_read(cmd646bar->bus, addr + 2);
}

static void cmd646_cmd_write(void *opaque, target_phys_addr_t addr,
                             uint64_t data, unsigned size)
{
    CMD646BAR *cmd646bar = opaque;

    if (addr != 2 || size != 1) {
        return;
    }
    ide_cmd_write(cmd646bar->bus, addr + 2, data);
}

68
static const MemoryRegionOps cmd646_cmd_ops = {
Avi Kivity's avatar
Avi Kivity committed
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
    .read = cmd646_cmd_read,
    .write = cmd646_cmd_write,
    .endianness = DEVICE_LITTLE_ENDIAN,
};

static uint64_t cmd646_data_read(void *opaque, target_phys_addr_t addr,
                                 unsigned size)
{
    CMD646BAR *cmd646bar = opaque;

    if (size == 1) {
        return ide_ioport_read(cmd646bar->bus, addr);
    } else if (addr == 0) {
        if (size == 2) {
            return ide_data_readw(cmd646bar->bus, addr);
84
        } else {
Avi Kivity's avatar
Avi Kivity committed
85
            return ide_data_readl(cmd646bar->bus, addr);
86
87
        }
    }
Avi Kivity's avatar
Avi Kivity committed
88
    return ((uint64_t)1 << (size * 8)) - 1;
89
90
}

Avi Kivity's avatar
Avi Kivity committed
91
92
static void cmd646_data_write(void *opaque, target_phys_addr_t addr,
                             uint64_t data, unsigned size)
93
{
Avi Kivity's avatar
Avi Kivity committed
94
95
96
    CMD646BAR *cmd646bar = opaque;

    if (size == 1) {
Blue Swirl's avatar
Blue Swirl committed
97
        ide_ioport_write(cmd646bar->bus, addr, data);
Avi Kivity's avatar
Avi Kivity committed
98
99
    } else if (addr == 0) {
        if (size == 2) {
Blue Swirl's avatar
Blue Swirl committed
100
            ide_data_writew(cmd646bar->bus, addr, data);
Avi Kivity's avatar
Avi Kivity committed
101
        } else {
Blue Swirl's avatar
Blue Swirl committed
102
            ide_data_writel(cmd646bar->bus, addr, data);
Avi Kivity's avatar
Avi Kivity committed
103
104
105
106
        }
    }
}

107
static const MemoryRegionOps cmd646_data_ops = {
Avi Kivity's avatar
Avi Kivity committed
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
    .read = cmd646_data_read,
    .write = cmd646_data_write,
    .endianness = DEVICE_LITTLE_ENDIAN,
};

static void setup_cmd646_bar(PCIIDEState *d, int bus_num)
{
    IDEBus *bus = &d->bus[bus_num];
    CMD646BAR *bar = &d->cmd646_bar[bus_num];

    bar->bus = bus;
    bar->pci_dev = d;
    memory_region_init_io(&bar->cmd, &cmd646_cmd_ops, bar, "cmd646-cmd", 4);
    memory_region_init_io(&bar->data, &cmd646_data_ops, bar, "cmd646-data", 8);
}

static uint64_t bmdma_read(void *opaque, target_phys_addr_t addr,
                           unsigned size)
{
    BMDMAState *bm = opaque;
    PCIIDEState *pci_dev = bm->pci_dev;
129
130
    uint32_t val;

Avi Kivity's avatar
Avi Kivity committed
131
132
133
134
    if (size != 1) {
        return ((uint64_t)1 << (size * 8)) - 1;
    }

135
136
137
138
139
    switch(addr & 3) {
    case 0:
        val = bm->cmd;
        break;
    case 1:
140
        val = pci_dev->dev.config[MRDMODE];
141
142
143
144
145
        break;
    case 2:
        val = bm->status;
        break;
    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
152
153
154
155
156
        }
        break;
    default:
        val = 0xff;
        break;
    }
#ifdef DEBUG_IDE
157
    printf("bmdma: readb 0x%02" TARGET_PRIxPHYS " : 0x%02x\n", addr, val);
158
159
160
161
#endif
    return val;
}

Avi Kivity's avatar
Avi Kivity committed
162
163
static void bmdma_write(void *opaque, target_phys_addr_t addr,
                        uint64_t val, unsigned size)
164
{
Avi Kivity's avatar
Avi Kivity committed
165
166
    BMDMAState *bm = opaque;
    PCIIDEState *pci_dev = bm->pci_dev;
167

Avi Kivity's avatar
Avi Kivity committed
168
169
170
    if (size != 1) {
        return;
    }
171

172
#ifdef DEBUG_IDE
173
174
    printf("bmdma: writeb 0x%02" TARGET_PRIxPHYS " : 0x%02" PRIx64 "\n",
           addr, val);
175
176
#endif
    switch(addr & 3) {
177
    case 0:
Avi Kivity's avatar
Avi Kivity committed
178
        bmdma_cmd_writeb(bm, val);
179
        break;
180
    case 1:
181
182
183
        pci_dev->dev.config[MRDMODE] =
            (pci_dev->dev.config[MRDMODE] & ~0x30) | (val & 0x30);
        cmd646_update_irq(pci_dev);
184
185
186
187
188
        break;
    case 2:
        bm->status = (val & 0x60) | (bm->status & 1) | (bm->status & ~val & 0x06);
        break;
    case 3:
189
        if (bm == &pci_dev->bmdma[0])
190
191
192
            pci_dev->dev.config[UDIDETCR0] = val;
        else
            pci_dev->dev.config[UDIDETCR1] = val;
193
194
195
196
        break;
    }
}

197
static const MemoryRegionOps cmd646_bmdma_ops = {
Avi Kivity's avatar
Avi Kivity committed
198
199
200
    .read = bmdma_read,
    .write = bmdma_write,
};
201

Avi Kivity's avatar
Avi Kivity committed
202
static void bmdma_setup_bar(PCIIDEState *d)
203
{
Avi Kivity's avatar
Avi Kivity committed
204
    BMDMAState *bm;
205
206
    int i;

Avi Kivity's avatar
Avi Kivity committed
207
    memory_region_init(&d->bmdma_bar, "cmd646-bmdma", 16);
208
    for(i = 0;i < 2; i++) {
Avi Kivity's avatar
Avi Kivity committed
209
210
211
212
213
214
215
        bm = &d->bmdma[i];
        memory_region_init_io(&bm->extra_io, &cmd646_bmdma_ops, bm,
                              "cmd646-bmdma-bus", 4);
        memory_region_add_subregion(&d->bmdma_bar, i * 8, &bm->extra_io);
        memory_region_init_io(&bm->addr_ioport, &bmdma_addr_ioport_ops, bm,
                              "cmd646-bmdma-ioport", 4);
        memory_region_add_subregion(&d->bmdma_bar, i * 8 + 4, &bm->addr_ioport);
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
    }
}

/* XXX: call it also when the MRDMODE is changed from the PCI config
   registers */
static void cmd646_update_irq(PCIIDEState *d)
{
    int pci_level;
    pci_level = ((d->dev.config[MRDMODE] & MRDMODE_INTR_CH0) &&
                 !(d->dev.config[MRDMODE] & MRDMODE_BLK_CH0)) ||
        ((d->dev.config[MRDMODE] & MRDMODE_INTR_CH1) &&
         !(d->dev.config[MRDMODE] & MRDMODE_BLK_CH1));
    qemu_set_irq(d->dev.irq[0], pci_level);
}

/* the PCI irq level is the logical OR of the two channels */
static void cmd646_set_irq(void *opaque, int channel, int level)
{
    PCIIDEState *d = opaque;
    int irq_mask;

    irq_mask = MRDMODE_INTR_CH0 << channel;
    if (level)
        d->dev.config[MRDMODE] |= irq_mask;
    else
        d->dev.config[MRDMODE] &= ~irq_mask;
    cmd646_update_irq(d);
}

static void cmd646_reset(void *opaque)
{
    PCIIDEState *d = opaque;
    unsigned int i;

Blue Swirl's avatar
Blue Swirl committed
250
251
252
    for (i = 0; i < 2; i++) {
        ide_bus_reset(&d->bus[i]);
    }
253
254
255
256
257
258
259
260
}

/* CMD646 PCI IDE controller */
static int pci_cmd646_ide_initfn(PCIDevice *dev)
{
    PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev);
    uint8_t *pci_conf = d->dev.config;
    qemu_irq *irq;
261
    int i;
262

263
    pci_conf[PCI_CLASS_PROG] = 0x8f;
264
265
266
267
268
269
270

    pci_conf[0x51] = 0x04; // enable IDE0
    if (d->secondary) {
        /* XXX: if not enabled, really disable the seconday IDE controller */
        pci_conf[0x51] |= 0x08; /* enable IDE1 */
    }

Avi Kivity's avatar
Avi Kivity committed
271
272
    setup_cmd646_bar(d, 0);
    setup_cmd646_bar(d, 1);
273
274
275
276
    pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd646_bar[0].data);
    pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd646_bar[0].cmd);
    pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd646_bar[1].data);
    pci_register_bar(dev, 3, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd646_bar[1].cmd);
Avi Kivity's avatar
Avi Kivity committed
277
    bmdma_setup_bar(d);
278
    pci_register_bar(dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &d->bmdma_bar);
279

280
281
    /* TODO: RST# value should be 0 */
    pci_conf[PCI_INTERRUPT_PIN] = 0x01; // interrupt on pin 1
282
283

    irq = qemu_allocate_irqs(cmd646_set_irq, d, 2);
284
285
286
287
    for (i = 0; i < 2; i++) {
        ide_bus_new(&d->bus[i], &d->dev.qdev, i);
        ide_init2(&d->bus[i], irq[i]);

Avi Kivity's avatar
Avi Kivity committed
288
        bmdma_init(&d->bus[i], &d->bmdma[i], d);
Kevin Wolf's avatar
Kevin Wolf committed
289
        d->bmdma[i].bus = &d->bus[i];
290
        qemu_add_vm_change_state_handler(d->bus[i].dma->ops->restart_cb,
Kevin Wolf's avatar
Kevin Wolf committed
291
                                         &d->bmdma[i].dma);
292
    }
293

294
    vmstate_register(&dev->qdev, 0, &vmstate_ide_pci, d);
295
296
297
298
    qemu_register_reset(cmd646_reset, d);
    return 0;
}

299
static void pci_cmd646_ide_exitfn(PCIDevice *dev)
Avi Kivity's avatar
Avi Kivity committed
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
{
    PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev);
    unsigned i;

    for (i = 0; i < 2; ++i) {
        memory_region_del_subregion(&d->bmdma_bar, &d->bmdma[i].extra_io);
        memory_region_destroy(&d->bmdma[i].extra_io);
        memory_region_del_subregion(&d->bmdma_bar, &d->bmdma[i].addr_ioport);
        memory_region_destroy(&d->bmdma[i].addr_ioport);
        memory_region_destroy(&d->cmd646_bar[i].cmd);
        memory_region_destroy(&d->cmd646_bar[i].data);
    }
    memory_region_destroy(&d->bmdma_bar);
}

315
316
317
318
319
void pci_cmd646_ide_init(PCIBus *bus, DriveInfo **hd_table,
                         int secondary_ide_enabled)
{
    PCIDevice *dev;

320
    dev = pci_create(bus, -1, "cmd646-ide");
321
322
323
324
325
326
    qdev_prop_set_uint32(&dev->qdev, "secondary", secondary_ide_enabled);
    qdev_init_nofail(&dev->qdev);

    pci_ide_create_devs(dev, hd_table);
}

327
328
329
330
331
332
333
static Property cmd646_ide_properties[] = {
    DEFINE_PROP_UINT32("secondary", PCIIDEState, secondary, 0),
    DEFINE_PROP_END_OF_LIST(),
};

static void cmd646_ide_class_init(ObjectClass *klass, void *data)
{
334
    DeviceClass *dc = DEVICE_CLASS(klass);
335
336
337
338
339
340
341
342
    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);

    k->init = pci_cmd646_ide_initfn;
    k->exit = pci_cmd646_ide_exitfn;
    k->vendor_id = PCI_VENDOR_ID_CMD;
    k->device_id = PCI_DEVICE_ID_CMD_646;
    k->revision = 0x07;
    k->class_id = PCI_CLASS_STORAGE_IDE;
343
    dc->props = cmd646_ide_properties;
344
345
}

346
347
348
349
350
static TypeInfo cmd646_ide_info = {
    .name          = "cmd646-ide",
    .parent        = TYPE_PCI_DEVICE,
    .instance_size = sizeof(PCIIDEState),
    .class_init    = cmd646_ide_class_init,
351
352
};

Andreas Färber's avatar
Andreas Färber committed
353
static void cmd646_ide_register_types(void)
354
{
355
    type_register_static(&cmd646_ide_info);
356
}
Andreas Färber's avatar
Andreas Färber committed
357
358

type_init(cmd646_ide_register_types)