qemu/hw/isa/pc87312.c
<<
>>
Prefs
   1/*
   2 * QEMU National Semiconductor PC87312 (Super I/O)
   3 *
   4 * Copyright (c) 2010-2012 Herve Poussineau
   5 * Copyright (c) 2011-2012 Andreas Färber
   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
  26#include "qemu/osdep.h"
  27#include "hw/isa/pc87312.h"
  28#include "qapi/error.h"
  29#include "qemu/error-report.h"
  30#include "sysemu/block-backend.h"
  31#include "sysemu/blockdev.h"
  32#include "sysemu/sysemu.h"
  33#include "chardev/char.h"
  34#include "trace.h"
  35
  36
  37#define REG_FER 0
  38#define REG_FAR 1
  39#define REG_PTR 2
  40
  41#define FER_PARALLEL_EN   0x01
  42#define FER_UART1_EN      0x02
  43#define FER_UART2_EN      0x04
  44#define FER_FDC_EN        0x08
  45#define FER_FDC_4         0x10
  46#define FER_FDC_ADDR      0x20
  47#define FER_IDE_EN        0x40
  48#define FER_IDE_ADDR      0x80
  49
  50#define FAR_PARALLEL_ADDR 0x03
  51#define FAR_UART1_ADDR    0x0C
  52#define FAR_UART2_ADDR    0x30
  53#define FAR_UART_3_4      0xC0
  54
  55#define PTR_POWER_DOWN    0x01
  56#define PTR_CLOCK_DOWN    0x02
  57#define PTR_PWDN          0x04
  58#define PTR_IRQ_5_7       0x08
  59#define PTR_UART1_TEST    0x10
  60#define PTR_UART2_TEST    0x20
  61#define PTR_LOCK_CONF     0x40
  62#define PTR_EPP_MODE      0x80
  63
  64
  65/* Parallel port */
  66
  67static inline bool is_parallel_enabled(PC87312State *s)
  68{
  69    return s->regs[REG_FER] & FER_PARALLEL_EN;
  70}
  71
  72static const uint32_t parallel_base[] = { 0x378, 0x3bc, 0x278, 0x00 };
  73
  74static inline uint32_t get_parallel_iobase(PC87312State *s)
  75{
  76    return parallel_base[s->regs[REG_FAR] & FAR_PARALLEL_ADDR];
  77}
  78
  79static const uint32_t parallel_irq[] = { 5, 7, 5, 0 };
  80
  81static inline uint32_t get_parallel_irq(PC87312State *s)
  82{
  83    int idx;
  84    idx = (s->regs[REG_FAR] & FAR_PARALLEL_ADDR);
  85    if (idx == 0) {
  86        return (s->regs[REG_PTR] & PTR_IRQ_5_7) ? 7 : 5;
  87    } else {
  88        return parallel_irq[idx];
  89    }
  90}
  91
  92
  93/* UARTs */
  94
  95static const uint32_t uart_base[2][4] = {
  96    { 0x3e8, 0x338, 0x2e8, 0x220 },
  97    { 0x2e8, 0x238, 0x2e0, 0x228 }
  98};
  99
 100static inline uint32_t get_uart_iobase(PC87312State *s, int i)
 101{
 102    int idx;
 103    idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3;
 104    if (idx == 0) {
 105        return 0x3f8;
 106    } else if (idx == 1) {
 107        return 0x2f8;
 108    } else {
 109        return uart_base[idx & 1][(s->regs[REG_FAR] & FAR_UART_3_4) >> 6];
 110    }
 111}
 112
 113static inline uint32_t get_uart_irq(PC87312State *s, int i)
 114{
 115    int idx;
 116    idx = (s->regs[REG_FAR] >> (2 * i + 2)) & 0x3;
 117    return (idx & 1) ? 3 : 4;
 118}
 119
 120static inline bool is_uart_enabled(PC87312State *s, int i)
 121{
 122    return s->regs[REG_FER] & (FER_UART1_EN << i);
 123}
 124
 125
 126/* Floppy controller */
 127
 128static inline bool is_fdc_enabled(PC87312State *s)
 129{
 130    return s->regs[REG_FER] & FER_FDC_EN;
 131}
 132
 133static inline uint32_t get_fdc_iobase(PC87312State *s)
 134{
 135    return (s->regs[REG_FER] & FER_FDC_ADDR) ? 0x370 : 0x3f0;
 136}
 137
 138
 139/* IDE controller */
 140
 141static inline bool is_ide_enabled(PC87312State *s)
 142{
 143    return s->regs[REG_FER] & FER_IDE_EN;
 144}
 145
 146static inline uint32_t get_ide_iobase(PC87312State *s)
 147{
 148    return (s->regs[REG_FER] & FER_IDE_ADDR) ? 0x170 : 0x1f0;
 149}
 150
 151
 152static void reconfigure_devices(PC87312State *s)
 153{
 154    error_report("pc87312: unsupported device reconfiguration (%02x %02x %02x)",
 155                 s->regs[REG_FER], s->regs[REG_FAR], s->regs[REG_PTR]);
 156}
 157
 158static void pc87312_soft_reset(PC87312State *s)
 159{
 160    static const uint8_t fer_init[] = {
 161        0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4b, 0x4b,
 162        0x4b, 0x4b, 0x4b, 0x4b, 0x0f, 0x0f, 0x0f, 0x0f,
 163        0x49, 0x49, 0x49, 0x49, 0x07, 0x07, 0x07, 0x07,
 164        0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x08, 0x00,
 165    };
 166    static const uint8_t far_init[] = {
 167        0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x00, 0x01,
 168        0x01, 0x09, 0x08, 0x08, 0x10, 0x11, 0x39, 0x24,
 169        0x00, 0x01, 0x01, 0x00, 0x10, 0x11, 0x39, 0x24,
 170        0x10, 0x11, 0x11, 0x39, 0x24, 0x38, 0x10, 0x10,
 171    };
 172    static const uint8_t ptr_init[] = {
 173        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 174        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 175        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 176        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
 177    };
 178
 179    s->read_id_step = 0;
 180    s->selected_index = REG_FER;
 181
 182    s->regs[REG_FER] = fer_init[s->config & 0x1f];
 183    s->regs[REG_FAR] = far_init[s->config & 0x1f];
 184    s->regs[REG_PTR] = ptr_init[s->config & 0x1f];
 185}
 186
 187static void pc87312_hard_reset(PC87312State *s)
 188{
 189    pc87312_soft_reset(s);
 190}
 191
 192static void pc87312_io_write(void *opaque, hwaddr addr, uint64_t val,
 193                             unsigned int size)
 194{
 195    PC87312State *s = opaque;
 196
 197    trace_pc87312_io_write(addr, val);
 198
 199    if ((addr & 1) == 0) {
 200        /* Index register */
 201        s->read_id_step = 2;
 202        s->selected_index = val;
 203    } else {
 204        /* Data register */
 205        if (s->selected_index < 3) {
 206            s->regs[s->selected_index] = val;
 207            reconfigure_devices(s);
 208        }
 209    }
 210}
 211
 212static uint64_t pc87312_io_read(void *opaque, hwaddr addr, unsigned int size)
 213{
 214    PC87312State *s = opaque;
 215    uint32_t val;
 216
 217    if ((addr & 1) == 0) {
 218        /* Index register */
 219        if (s->read_id_step++ == 0) {
 220            val = 0x88;
 221        } else if (s->read_id_step++ == 1) {
 222            val = 0;
 223        } else {
 224            val = s->selected_index;
 225        }
 226    } else {
 227        /* Data register */
 228        if (s->selected_index < 3) {
 229            val = s->regs[s->selected_index];
 230        } else {
 231            /* Invalid selected index */
 232            val = 0;
 233        }
 234    }
 235
 236    trace_pc87312_io_read(addr, val);
 237    return val;
 238}
 239
 240static const MemoryRegionOps pc87312_io_ops = {
 241    .read  = pc87312_io_read,
 242    .write = pc87312_io_write,
 243    .endianness = DEVICE_LITTLE_ENDIAN,
 244    .valid = {
 245        .min_access_size = 1,
 246        .max_access_size = 1,
 247    },
 248};
 249
 250static int pc87312_post_load(void *opaque, int version_id)
 251{
 252    PC87312State *s = opaque;
 253
 254    reconfigure_devices(s);
 255    return 0;
 256}
 257
 258static void pc87312_reset(DeviceState *d)
 259{
 260    PC87312State *s = PC87312(d);
 261
 262    pc87312_soft_reset(s);
 263}
 264
 265static void pc87312_realize(DeviceState *dev, Error **errp)
 266{
 267    PC87312State *s;
 268    DeviceState *d;
 269    ISADevice *isa;
 270    ISABus *bus;
 271    Chardev *chr;
 272    DriveInfo *drive;
 273    char name[5];
 274    int i;
 275
 276    s = PC87312(dev);
 277    isa = ISA_DEVICE(dev);
 278    bus = isa_bus_from_device(isa);
 279    isa_register_ioport(isa, &s->io, s->iobase);
 280    pc87312_hard_reset(s);
 281
 282    if (is_parallel_enabled(s)) {
 283        /* FIXME use a qdev chardev prop instead of parallel_hds[] */
 284        chr = parallel_hds[0];
 285        if (chr == NULL) {
 286            chr = qemu_chr_new("par0", "null");
 287        }
 288        isa = isa_create(bus, "isa-parallel");
 289        d = DEVICE(isa);
 290        qdev_prop_set_uint32(d, "index", 0);
 291        qdev_prop_set_uint32(d, "iobase", get_parallel_iobase(s));
 292        qdev_prop_set_uint32(d, "irq", get_parallel_irq(s));
 293        qdev_prop_set_chr(d, "chardev", chr);
 294        qdev_init_nofail(d);
 295        s->parallel.dev = isa;
 296        trace_pc87312_info_parallel(get_parallel_iobase(s),
 297                                    get_parallel_irq(s));
 298    }
 299
 300    for (i = 0; i < 2; i++) {
 301        if (is_uart_enabled(s, i)) {
 302            /* FIXME use a qdev chardev prop instead of serial_hds[] */
 303            chr = serial_hds[i];
 304            if (chr == NULL) {
 305                snprintf(name, sizeof(name), "ser%d", i);
 306                chr = qemu_chr_new(name, "null");
 307            }
 308            isa = isa_create(bus, "isa-serial");
 309            d = DEVICE(isa);
 310            qdev_prop_set_uint32(d, "index", i);
 311            qdev_prop_set_uint32(d, "iobase", get_uart_iobase(s, i));
 312            qdev_prop_set_uint32(d, "irq", get_uart_irq(s, i));
 313            qdev_prop_set_chr(d, "chardev", chr);
 314            qdev_init_nofail(d);
 315            s->uart[i].dev = isa;
 316            trace_pc87312_info_serial(i, get_uart_iobase(s, i),
 317                                      get_uart_irq(s, i));
 318        }
 319    }
 320
 321    if (is_fdc_enabled(s)) {
 322        isa = isa_create(bus, "isa-fdc");
 323        d = DEVICE(isa);
 324        qdev_prop_set_uint32(d, "iobase", get_fdc_iobase(s));
 325        qdev_prop_set_uint32(d, "irq", 6);
 326        /* FIXME use a qdev drive property instead of drive_get() */
 327        drive = drive_get(IF_FLOPPY, 0, 0);
 328        if (drive != NULL) {
 329            qdev_prop_set_drive(d, "driveA", blk_by_legacy_dinfo(drive),
 330                                &error_fatal);
 331        }
 332        /* FIXME use a qdev drive property instead of drive_get() */
 333        drive = drive_get(IF_FLOPPY, 0, 1);
 334        if (drive != NULL) {
 335            qdev_prop_set_drive(d, "driveB", blk_by_legacy_dinfo(drive),
 336                                &error_fatal);
 337        }
 338        qdev_init_nofail(d);
 339        s->fdc.dev = isa;
 340        trace_pc87312_info_floppy(get_fdc_iobase(s));
 341    }
 342
 343    if (is_ide_enabled(s)) {
 344        isa = isa_create(bus, "isa-ide");
 345        d = DEVICE(isa);
 346        qdev_prop_set_uint32(d, "iobase", get_ide_iobase(s));
 347        qdev_prop_set_uint32(d, "iobase2", get_ide_iobase(s) + 0x206);
 348        qdev_prop_set_uint32(d, "irq", 14);
 349        qdev_init_nofail(d);
 350        s->ide.dev = isa;
 351        trace_pc87312_info_ide(get_ide_iobase(s));
 352    }
 353}
 354
 355static void pc87312_initfn(Object *obj)
 356{
 357    PC87312State *s = PC87312(obj);
 358
 359    memory_region_init_io(&s->io, obj, &pc87312_io_ops, s, "pc87312", 2);
 360}
 361
 362static const VMStateDescription vmstate_pc87312 = {
 363    .name = "pc87312",
 364    .version_id = 1,
 365    .minimum_version_id = 1,
 366    .post_load = pc87312_post_load,
 367    .fields = (VMStateField[]) {
 368        VMSTATE_UINT8(read_id_step, PC87312State),
 369        VMSTATE_UINT8(selected_index, PC87312State),
 370        VMSTATE_UINT8_ARRAY(regs, PC87312State, 3),
 371        VMSTATE_END_OF_LIST()
 372    }
 373};
 374
 375static Property pc87312_properties[] = {
 376    DEFINE_PROP_UINT32("iobase", PC87312State, iobase, 0x398),
 377    DEFINE_PROP_UINT8("config", PC87312State, config, 1),
 378    DEFINE_PROP_END_OF_LIST()
 379};
 380
 381static void pc87312_class_init(ObjectClass *klass, void *data)
 382{
 383    DeviceClass *dc = DEVICE_CLASS(klass);
 384
 385    dc->realize = pc87312_realize;
 386    dc->reset = pc87312_reset;
 387    dc->vmsd = &vmstate_pc87312;
 388    dc->props = pc87312_properties;
 389}
 390
 391static const TypeInfo pc87312_type_info = {
 392    .name          = TYPE_PC87312,
 393    .parent        = TYPE_ISA_DEVICE,
 394    .instance_size = sizeof(PC87312State),
 395    .instance_init = pc87312_initfn,
 396    .class_init    = pc87312_class_init,
 397};
 398
 399static void pc87312_register_types(void)
 400{
 401    type_register_static(&pc87312_type_info);
 402}
 403
 404type_init(pc87312_register_types)
 405