~ahs3/+junk/cq-qemu

« back to all changes in this revision

Viewing changes to hw/ide/cmd646.c

  • Committer: Al Stone
  • Date: 2012-02-09 01:17:20 UTC
  • Revision ID: albert.stone@canonical.com-20120209011720-tztl7ik3qayz80p4
first commit to bzr for qemu

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
44
static void cmd646_update_irq(PCIIDEState *d);
 
45
 
 
46
static uint64_t cmd646_cmd_read(void *opaque, target_phys_addr_t 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
 
 
57
static void cmd646_cmd_write(void *opaque, target_phys_addr_t 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
 
 
68
static MemoryRegionOps cmd646_cmd_ops = {
 
69
    .read = cmd646_cmd_read,
 
70
    .write = cmd646_cmd_write,
 
71
    .endianness = DEVICE_LITTLE_ENDIAN,
 
72
};
 
73
 
 
74
static uint64_t cmd646_data_read(void *opaque, target_phys_addr_t 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
 
 
91
static void cmd646_data_write(void *opaque, target_phys_addr_t addr,
 
92
                             uint64_t data, unsigned size)
 
93
{
 
94
    CMD646BAR *cmd646bar = opaque;
 
95
 
 
96
    if (size == 1) {
 
97
        return ide_ioport_write(cmd646bar->bus, addr, data);
 
98
    } else if (addr == 0) {
 
99
        if (size == 2) {
 
100
            return ide_data_writew(cmd646bar->bus, addr, data);
 
101
        } else {
 
102
            return ide_data_writel(cmd646bar->bus, addr, data);
 
103
        }
 
104
    }
 
105
}
 
106
 
 
107
static MemoryRegionOps cmd646_data_ops = {
 
108
    .read = cmd646_data_read,
 
109
    .write = cmd646_data_write,
 
110
    .endianness = DEVICE_LITTLE_ENDIAN,
 
111
};
 
112
 
 
113
static 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
 
 
124
static uint64_t bmdma_read(void *opaque, target_phys_addr_t 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
 
 
162
static void bmdma_write(void *opaque, target_phys_addr_t 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
 
 
196
static MemoryRegionOps cmd646_bmdma_ops = {
 
197
    .read = bmdma_read,
 
198
    .write = bmdma_write,
 
199
};
 
200
 
 
201
static 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 */
 
220
static 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 */
 
231
static 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
 
 
244
static 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 */
 
255
static 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
 
 
298
static int 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
    return 0;
 
314
}
 
315
 
 
316
void pci_cmd646_ide_init(PCIBus *bus, DriveInfo **hd_table,
 
317
                         int secondary_ide_enabled)
 
318
{
 
319
    PCIDevice *dev;
 
320
 
 
321
    dev = pci_create(bus, -1, "cmd646-ide");
 
322
    qdev_prop_set_uint32(&dev->qdev, "secondary", secondary_ide_enabled);
 
323
    qdev_init_nofail(&dev->qdev);
 
324
 
 
325
    pci_ide_create_devs(dev, hd_table);
 
326
}
 
327
 
 
328
static PCIDeviceInfo cmd646_ide_info[] = {
 
329
    {
 
330
        .qdev.name    = "cmd646-ide",
 
331
        .qdev.size    = sizeof(PCIIDEState),
 
332
        .init         = pci_cmd646_ide_initfn,
 
333
        .exit         = pci_cmd646_ide_exitfn,
 
334
        .vendor_id    = PCI_VENDOR_ID_CMD,
 
335
        .device_id    = PCI_DEVICE_ID_CMD_646,
 
336
        .revision     = 0x07, // IDE controller revision
 
337
        .class_id     = PCI_CLASS_STORAGE_IDE,
 
338
        .qdev.props   = (Property[]) {
 
339
            DEFINE_PROP_UINT32("secondary", PCIIDEState, secondary, 0),
 
340
            DEFINE_PROP_END_OF_LIST(),
 
341
        },
 
342
    },{
 
343
        /* end of list */
 
344
    }
 
345
};
 
346
 
 
347
static void cmd646_ide_register(void)
 
348
{
 
349
    pci_qdev_register_many(cmd646_ide_info);
 
350
}
 
351
device_init(cmd646_ide_register);