1 /* 2 * QEMU IDE Emulation: MacIO 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/ppc_mac.h> 27 #include <hw/mac_dbdma.h> 28 #include "block.h" 29 #include "block_int.h" 30 #include "sysemu.h" 31 #include "dma.h" 32 33 #include <hw/ide/internal.h> 34 35 /***********************************************************/ 36 /* MacIO based PowerPC IDE */ 37 38 typedef struct MACIOIDEState { 39 IDEBus bus; 40 BlockDriverAIOCB *aiocb; 41 } MACIOIDEState; 42 43 static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) 44 { 45 DBDMA_io *io = opaque; 46 MACIOIDEState *m = io->opaque; 47 IDEState *s = idebus_active_if(&m->bus); 48 49 if (ret < 0) { 50 m->aiocb = NULL; 51 qemu_sglist_destroy(&s->sg); 52 ide_atapi_io_error(s, ret); 53 io->dma_end(opaque); 54 return; 55 } 56 57 if (s->io_buffer_size > 0) { 58 m->aiocb = NULL; 59 qemu_sglist_destroy(&s->sg); 60 61 s->packet_transfer_size -= s->io_buffer_size; 62 63 s->io_buffer_index += s->io_buffer_size; 64 s->lba += s->io_buffer_index >> 11; 65 s->io_buffer_index &= 0x7ff; 66 } 67 68 if (s->packet_transfer_size <= 0) 69 ide_atapi_cmd_ok(s); 70 71 if (io->len == 0) { 72 io->dma_end(opaque); 73 return; 74 } 75 76 /* launch next transfer */ 77 78 s->io_buffer_size = io->len; 79 80 qemu_sglist_init(&s->sg, io->len / TARGET_PAGE_SIZE + 1); 81 qemu_sglist_add(&s->sg, io->addr, io->len); 82 io->addr += io->len; 83 io->len = 0; 84 85 m->aiocb = dma_bdrv_read(s->bs, &s->sg, 86 (int64_t)(s->lba << 2) + (s->io_buffer_index >> 9), 87 pmac_ide_atapi_transfer_cb, io); 88 if (!m->aiocb) { 89 qemu_sglist_destroy(&s->sg); 90 /* Note: media not present is the most likely case */ 91 ide_atapi_cmd_error(s, SENSE_NOT_READY, 92 ASC_MEDIUM_NOT_PRESENT); 93 io->dma_end(opaque); 94 return; 95 } 96 } 97 98 static void pmac_ide_transfer_cb(void *opaque, int ret) 99 { 100 DBDMA_io *io = opaque; 101 MACIOIDEState *m = io->opaque; 102 IDEState *s = idebus_active_if(&m->bus); 103 int n; 104 int64_t sector_num; 105 106 if (ret < 0) { 107 m->aiocb = NULL; 108 qemu_sglist_destroy(&s->sg); 109 ide_dma_error(s); 110 io->dma_end(io); 111 return; 112 } 113 114 sector_num = ide_get_sector(s); 115 if (s->io_buffer_size > 0) { 116 m->aiocb = NULL; 117 qemu_sglist_destroy(&s->sg); 118 n = (s->io_buffer_size + 0x1ff) >> 9; 119 sector_num += n; 120 ide_set_sector(s, sector_num); 121 s->nsector -= n; 122 } 123 124 /* end of transfer ? */ 125 if (s->nsector == 0) { 126 s->status = READY_STAT | SEEK_STAT; 127 ide_set_irq(s->bus); 128 } 129 130 /* end of DMA ? */ 131 132 if (io->len == 0) { 133 io->dma_end(io); 134 return; 135 } 136 137 /* launch next transfer */ 138 139 s->io_buffer_index = 0; 140 s->io_buffer_size = io->len; 141 142 qemu_sglist_init(&s->sg, io->len / TARGET_PAGE_SIZE + 1); 143 qemu_sglist_add(&s->sg, io->addr, io->len); 144 io->addr += io->len; 145 io->len = 0; 146 147 if (s->is_read) 148 m->aiocb = dma_bdrv_read(s->bs, &s->sg, sector_num, 149 pmac_ide_transfer_cb, io); 150 else 151 m->aiocb = dma_bdrv_write(s->bs, &s->sg, sector_num, 152 pmac_ide_transfer_cb, io); 153 if (!m->aiocb) 154 pmac_ide_transfer_cb(io, -1); 155 } 156 157 static void pmac_ide_transfer(DBDMA_io *io) 158 { 159 MACIOIDEState *m = io->opaque; 160 IDEState *s = idebus_active_if(&m->bus); 161 162 s->io_buffer_size = 0; 163 if (s->is_cdrom) { 164 pmac_ide_atapi_transfer_cb(io, 0); 165 return; 166 } 167 168 pmac_ide_transfer_cb(io, 0); 169 } 170 171 static void pmac_ide_flush(DBDMA_io *io) 172 { 173 MACIOIDEState *m = io->opaque; 174 175 if (m->aiocb) 176 qemu_aio_flush(); 177 } 178 179 /* PowerMac IDE memory IO */ 180 static void pmac_ide_writeb (void *opaque, 181 target_phys_addr_t addr, uint32_t val) 182 { 183 MACIOIDEState *d = opaque; 184 185 addr = (addr & 0xFFF) >> 4; 186 switch (addr) { 187 case 1 ... 7: 188 ide_ioport_write(&d->bus, addr, val); 189 break; 190 case 8: 191 case 22: 192 ide_cmd_write(&d->bus, 0, val); 193 break; 194 default: 195 break; 196 } 197 } 198 199 static uint32_t pmac_ide_readb (void *opaque,target_phys_addr_t addr) 200 { 201 uint8_t retval; 202 MACIOIDEState *d = opaque; 203 204 addr = (addr & 0xFFF) >> 4; 205 switch (addr) { 206 case 1 ... 7: 207 retval = ide_ioport_read(&d->bus, addr); 208 break; 209 case 8: 210 case 22: 211 retval = ide_status_read(&d->bus, 0); 212 break; 213 default: 214 retval = 0xFF; 215 break; 216 } 217 return retval; 218 } 219 220 static void pmac_ide_writew (void *opaque, 221 target_phys_addr_t addr, uint32_t val) 222 { 223 MACIOIDEState *d = opaque; 224 225 addr = (addr & 0xFFF) >> 4; 226 #ifdef TARGET_WORDS_BIGENDIAN 227 val = bswap16(val); 228 #endif 229 if (addr == 0) { 230 ide_data_writew(&d->bus, 0, val); 231 } 232 } 233 234 static uint32_t pmac_ide_readw (void *opaque,target_phys_addr_t addr) 235 { 236 uint16_t retval; 237 MACIOIDEState *d = opaque; 238 239 addr = (addr & 0xFFF) >> 4; 240 if (addr == 0) { 241 retval = ide_data_readw(&d->bus, 0); 242 } else { 243 retval = 0xFFFF; 244 } 245 #ifdef TARGET_WORDS_BIGENDIAN 246 retval = bswap16(retval); 247 #endif 248 return retval; 249 } 250 251 static void pmac_ide_writel (void *opaque, 252 target_phys_addr_t addr, uint32_t val) 253 { 254 MACIOIDEState *d = opaque; 255 256 addr = (addr & 0xFFF) >> 4; 257 #ifdef TARGET_WORDS_BIGENDIAN 258 val = bswap32(val); 259 #endif 260 if (addr == 0) { 261 ide_data_writel(&d->bus, 0, val); 262 } 263 } 264 265 static uint32_t pmac_ide_readl (void *opaque,target_phys_addr_t addr) 266 { 267 uint32_t retval; 268 MACIOIDEState *d = opaque; 269 270 addr = (addr & 0xFFF) >> 4; 271 if (addr == 0) { 272 retval = ide_data_readl(&d->bus, 0); 273 } else { 274 retval = 0xFFFFFFFF; 275 } 276 #ifdef TARGET_WORDS_BIGENDIAN 277 retval = bswap32(retval); 278 #endif 279 return retval; 280 } 281 282 static CPUWriteMemoryFunc * const pmac_ide_write[] = { 283 pmac_ide_writeb, 284 pmac_ide_writew, 285 pmac_ide_writel, 286 }; 287 288 static CPUReadMemoryFunc * const pmac_ide_read[] = { 289 pmac_ide_readb, 290 pmac_ide_readw, 291 pmac_ide_readl, 292 }; 293 294 static void pmac_ide_save(QEMUFile *f, void *opaque) 295 { 296 MACIOIDEState *d = opaque; 297 unsigned int i; 298 299 /* per IDE interface data */ 300 idebus_save(f, &d->bus); 301 302 /* per IDE drive data */ 303 for(i = 0; i < 2; i++) { 304 ide_save(f, &d->bus.ifs[i]); 305 } 306 } 307 308 static int pmac_ide_load(QEMUFile *f, void *opaque, int version_id) 309 { 310 MACIOIDEState *d = opaque; 311 unsigned int i; 312 313 if (version_id != 1 && version_id != 3) 314 return -EINVAL; 315 316 /* per IDE interface data */ 317 idebus_load(f, &d->bus, version_id); 318 319 /* per IDE drive data */ 320 for(i = 0; i < 2; i++) { 321 ide_load(f, &d->bus.ifs[i], version_id); 322 } 323 return 0; 324 } 325 326 static void pmac_ide_reset(void *opaque) 327 { 328 MACIOIDEState *d = opaque; 329 330 ide_reset(d->bus.ifs +0); 331 ide_reset(d->bus.ifs +1); 332 } 333 334 /* hd_table must contain 4 block drivers */ 335 /* PowerMac uses memory mapped registers, not I/O. Return the memory 336 I/O index to access the ide. */ 337 int pmac_ide_init (DriveInfo **hd_table, qemu_irq irq, 338 void *dbdma, int channel, qemu_irq dma_irq) 339 { 340 MACIOIDEState *d; 341 int pmac_ide_memory; 342 343 d = qemu_mallocz(sizeof(MACIOIDEState)); 344 ide_init2(&d->bus, hd_table[0], hd_table[1], irq); 345 346 if (dbdma) 347 DBDMA_register_channel(dbdma, channel, dma_irq, pmac_ide_transfer, pmac_ide_flush, d); 348 349 pmac_ide_memory = cpu_register_io_memory(pmac_ide_read, 350 pmac_ide_write, d); 351 register_savevm("ide", 0, 3, pmac_ide_save, pmac_ide_load, d); 352 qemu_register_reset(pmac_ide_reset, d); 353 pmac_ide_reset(d); 354 355 return pmac_ide_memory; 356 } 357