slavio_misc.c 11.7 KB
Newer Older
B
bellard 已提交
1 2
/*
 * QEMU Sparc SLAVIO aux io port emulation
3
 *
B
bellard 已提交
4
 * Copyright (c) 2005 Fabrice Bellard
5
 *
B
bellard 已提交
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 * 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.
 */
P
pbrook 已提交
24 25 26 27
#include "hw.h"
#include "sun4m.h"
#include "sysemu.h"

B
bellard 已提交
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
/* debug misc */
//#define DEBUG_MISC

/*
 * This is the auxio port, chip control and system control part of
 * chip STP2001 (Slave I/O), also produced as NCR89C105. See
 * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
 *
 * This also includes the PMC CPU idle controller.
 */

#ifdef DEBUG_MISC
#define MISC_DPRINTF(fmt, args...) \
do { printf("MISC: " fmt , ##args); } while (0)
#else
#define MISC_DPRINTF(fmt, args...)
#endif

typedef struct MiscState {
P
pbrook 已提交
47
    qemu_irq irq;
B
bellard 已提交
48 49
    uint8_t config;
    uint8_t aux1, aux2;
50 51
    uint8_t diag, mctrl;
    uint32_t sysctrl;
52
    uint16_t leds;
53
    CPUState *env;
B
bellard 已提交
54 55
} MiscState;

B
blueswir1 已提交
56
#define MISC_SIZE 1
57 58
#define SYSCTRL_MAXADDR 3
#define SYSCTRL_SIZE (SYSCTRL_MAXADDR + 1)
B
blueswir1 已提交
59
#define LED_MAXADDR 1
60
#define LED_SIZE (LED_MAXADDR + 1)
B
bellard 已提交
61

62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
#define MISC_MASK 0x0fff0000
#define MISC_LEDS 0x01600000
#define MISC_CFG  0x01800000
#define MISC_DIAG 0x01a00000
#define MISC_MDM  0x01b00000
#define MISC_SYS  0x01f00000

#define AUX2_PWROFF    0x01
#define AUX2_PWRINTCLR 0x02
#define AUX2_PWRFAIL   0x20

#define CFG_PWRINTEN   0x08

#define SYS_RESET      0x01
#define SYS_RESETSTAT  0x02

B
bellard 已提交
78 79 80 81
static void slavio_misc_update_irq(void *opaque)
{
    MiscState *s = opaque;

82
    if ((s->aux2 & AUX2_PWRFAIL) && (s->config & CFG_PWRINTEN)) {
P
pbrook 已提交
83 84
        MISC_DPRINTF("Raise IRQ\n");
        qemu_irq_raise(s->irq);
B
bellard 已提交
85
    } else {
P
pbrook 已提交
86 87
        MISC_DPRINTF("Lower IRQ\n");
        qemu_irq_lower(s->irq);
B
bellard 已提交
88 89 90 91 92 93 94
    }
}

static void slavio_misc_reset(void *opaque)
{
    MiscState *s = opaque;

B
bellard 已提交
95
    // Diagnostic and system control registers not cleared in reset
B
bellard 已提交
96 97 98 99 100 101 102 103
    s->config = s->aux1 = s->aux2 = s->mctrl = 0;
}

void slavio_set_power_fail(void *opaque, int power_failing)
{
    MiscState *s = opaque;

    MISC_DPRINTF("Power fail: %d, config: %d\n", power_failing, s->config);
104 105
    if (power_failing && (s->config & CFG_PWRINTEN)) {
        s->aux2 |= AUX2_PWRFAIL;
B
bellard 已提交
106
    } else {
107
        s->aux2 &= ~AUX2_PWRFAIL;
B
bellard 已提交
108 109 110 111
    }
    slavio_misc_update_irq(s);
}

112 113
static void slavio_misc_mem_writeb(void *opaque, target_phys_addr_t addr,
                                   uint32_t val)
B
bellard 已提交
114 115 116
{
    MiscState *s = opaque;

117 118
    switch (addr & MISC_MASK) {
    case MISC_CFG:
B
blueswir1 已提交
119 120 121 122
        MISC_DPRINTF("Write config %2.2x\n", val & 0xff);
        s->config = val & 0xff;
        slavio_misc_update_irq(s);
        break;
123
    case MISC_DIAG:
B
blueswir1 已提交
124 125 126
        MISC_DPRINTF("Write diag %2.2x\n", val & 0xff);
        s->diag = val & 0xff;
        break;
127
    case MISC_MDM:
B
blueswir1 已提交
128 129 130
        MISC_DPRINTF("Write modem control %2.2x\n", val & 0xff);
        s->mctrl = val & 0xff;
        break;
131
    default:
B
blueswir1 已提交
132
        break;
B
bellard 已提交
133 134 135 136 137 138 139 140
    }
}

static uint32_t slavio_misc_mem_readb(void *opaque, target_phys_addr_t addr)
{
    MiscState *s = opaque;
    uint32_t ret = 0;

141 142
    switch (addr & MISC_MASK) {
    case MISC_CFG:
B
blueswir1 已提交
143 144 145
        ret = s->config;
        MISC_DPRINTF("Read config %2.2x\n", ret);
        break;
146
    case MISC_DIAG:
B
blueswir1 已提交
147 148 149
        ret = s->diag;
        MISC_DPRINTF("Read diag %2.2x\n", ret);
        break;
150
    case MISC_MDM:
B
blueswir1 已提交
151 152 153
        ret = s->mctrl;
        MISC_DPRINTF("Read modem control %2.2x\n", ret);
        break;
154
    default:
B
blueswir1 已提交
155
        break;
B
bellard 已提交
156 157 158 159 160 161
    }
    return ret;
}

static CPUReadMemoryFunc *slavio_misc_mem_read[3] = {
    slavio_misc_mem_readb,
162 163
    NULL,
    NULL,
B
bellard 已提交
164 165 166 167
};

static CPUWriteMemoryFunc *slavio_misc_mem_write[3] = {
    slavio_misc_mem_writeb,
168 169
    NULL,
    NULL,
B
bellard 已提交
170 171
};

172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 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 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
static void slavio_aux1_mem_writeb(void *opaque, target_phys_addr_t addr,
                                   uint32_t val)
{
    MiscState *s = opaque;

    MISC_DPRINTF("Write aux1 %2.2x\n", val & 0xff);
    s->aux1 = val & 0xff;
}

static uint32_t slavio_aux1_mem_readb(void *opaque, target_phys_addr_t addr)
{
    MiscState *s = opaque;
    uint32_t ret = 0;

    ret = s->aux1;
    MISC_DPRINTF("Read aux1 %2.2x\n", ret);

    return ret;
}

static CPUReadMemoryFunc *slavio_aux1_mem_read[3] = {
    slavio_aux1_mem_readb,
    NULL,
    NULL,
};

static CPUWriteMemoryFunc *slavio_aux1_mem_write[3] = {
    slavio_aux1_mem_writeb,
    NULL,
    NULL,
};

static void slavio_aux2_mem_writeb(void *opaque, target_phys_addr_t addr,
                                   uint32_t val)
{
    MiscState *s = opaque;

    val &= AUX2_PWRINTCLR | AUX2_PWROFF;
    MISC_DPRINTF("Write aux2 %2.2x\n", val);
    val |= s->aux2 & AUX2_PWRFAIL;
    if (val & AUX2_PWRINTCLR) // Clear Power Fail int
        val &= AUX2_PWROFF;
    s->aux2 = val;
    if (val & AUX2_PWROFF)
        qemu_system_shutdown_request();
    slavio_misc_update_irq(s);
}

static uint32_t slavio_aux2_mem_readb(void *opaque, target_phys_addr_t addr)
{
    MiscState *s = opaque;
    uint32_t ret = 0;

    ret = s->aux2;
    MISC_DPRINTF("Read aux2 %2.2x\n", ret);

    return ret;
}

static CPUReadMemoryFunc *slavio_aux2_mem_read[3] = {
    slavio_aux2_mem_readb,
    NULL,
    NULL,
};

static CPUWriteMemoryFunc *slavio_aux2_mem_write[3] = {
    slavio_aux2_mem_writeb,
    NULL,
    NULL,
};

static void apc_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
{
    MiscState *s = opaque;

    MISC_DPRINTF("Write power management %2.2x\n", val & 0xff);
    cpu_interrupt(s->env, CPU_INTERRUPT_HALT);
}

static uint32_t apc_mem_readb(void *opaque, target_phys_addr_t addr)
{
    uint32_t ret = 0;

    MISC_DPRINTF("Read power management %2.2x\n", ret);
    return ret;
}

static CPUReadMemoryFunc *apc_mem_read[3] = {
    apc_mem_readb,
    NULL,
    NULL,
};

static CPUWriteMemoryFunc *apc_mem_write[3] = {
    apc_mem_writeb,
    NULL,
    NULL,
};

271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
static uint32_t slavio_sysctrl_mem_readl(void *opaque, target_phys_addr_t addr)
{
    MiscState *s = opaque;
    uint32_t ret = 0, saddr;

    saddr = addr & SYSCTRL_MAXADDR;
    switch (saddr) {
    case 0:
        ret = s->sysctrl;
        break;
    default:
        break;
    }
    MISC_DPRINTF("Read system control reg 0x" TARGET_FMT_plx " = %x\n", addr,
                 ret);
    return ret;
}

static void slavio_sysctrl_mem_writel(void *opaque, target_phys_addr_t addr,
                                      uint32_t val)
{
    MiscState *s = opaque;
    uint32_t saddr;

    saddr = addr & SYSCTRL_MAXADDR;
    MISC_DPRINTF("Write system control reg 0x" TARGET_FMT_plx " =  %x\n", addr,
                 val);
    switch (saddr) {
    case 0:
300 301
        if (val & SYS_RESET) {
            s->sysctrl = SYS_RESETSTAT;
302 303 304 305 306 307 308 309 310
            qemu_system_reset_request();
        }
        break;
    default:
        break;
    }
}

static CPUReadMemoryFunc *slavio_sysctrl_mem_read[3] = {
311 312
    NULL,
    NULL,
313 314 315 316
    slavio_sysctrl_mem_readl,
};

static CPUWriteMemoryFunc *slavio_sysctrl_mem_write[3] = {
317 318
    NULL,
    NULL,
319 320 321
    slavio_sysctrl_mem_writel,
};

322
static uint32_t slavio_led_mem_readw(void *opaque, target_phys_addr_t addr)
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
{
    MiscState *s = opaque;
    uint32_t ret = 0, saddr;

    saddr = addr & LED_MAXADDR;
    switch (saddr) {
    case 0:
        ret = s->leds;
        break;
    default:
        break;
    }
    MISC_DPRINTF("Read diagnostic LED reg 0x" TARGET_FMT_plx " = %x\n", addr,
                 ret);
    return ret;
}

340
static void slavio_led_mem_writew(void *opaque, target_phys_addr_t addr,
341 342 343 344 345 346 347 348 349 350
                                  uint32_t val)
{
    MiscState *s = opaque;
    uint32_t saddr;

    saddr = addr & LED_MAXADDR;
    MISC_DPRINTF("Write diagnostic LED reg 0x" TARGET_FMT_plx " =  %x\n", addr,
                 val);
    switch (saddr) {
    case 0:
B
blueswir1 已提交
351
        s->leds = val;
352 353 354 355 356 357 358
        break;
    default:
        break;
    }
}

static CPUReadMemoryFunc *slavio_led_mem_read[3] = {
359 360 361
    NULL,
    slavio_led_mem_readw,
    NULL,
362 363 364
};

static CPUWriteMemoryFunc *slavio_led_mem_write[3] = {
365 366 367
    NULL,
    slavio_led_mem_writew,
    NULL,
368 369
};

B
bellard 已提交
370 371 372
static void slavio_misc_save(QEMUFile *f, void *opaque)
{
    MiscState *s = opaque;
P
pbrook 已提交
373
    int tmp;
374
    uint8_t tmp8;
B
bellard 已提交
375

P
pbrook 已提交
376 377
    tmp = 0;
    qemu_put_be32s(f, &tmp); /* ignored, was IRQ.  */
B
bellard 已提交
378 379 380 381 382
    qemu_put_8s(f, &s->config);
    qemu_put_8s(f, &s->aux1);
    qemu_put_8s(f, &s->aux2);
    qemu_put_8s(f, &s->diag);
    qemu_put_8s(f, &s->mctrl);
383 384
    tmp8 = s->sysctrl & 0xff;
    qemu_put_8s(f, &tmp8);
B
bellard 已提交
385 386 387 388 389
}

static int slavio_misc_load(QEMUFile *f, void *opaque, int version_id)
{
    MiscState *s = opaque;
P
pbrook 已提交
390
    int tmp;
391
    uint8_t tmp8;
B
bellard 已提交
392 393 394 395

    if (version_id != 1)
        return -EINVAL;

P
pbrook 已提交
396
    qemu_get_be32s(f, &tmp);
B
bellard 已提交
397 398 399 400 401
    qemu_get_8s(f, &s->config);
    qemu_get_8s(f, &s->aux1);
    qemu_get_8s(f, &s->aux2);
    qemu_get_8s(f, &s->diag);
    qemu_get_8s(f, &s->mctrl);
402 403
    qemu_get_8s(f, &tmp8);
    s->sysctrl = (uint32_t)tmp8;
B
bellard 已提交
404 405 406
    return 0;
}

407
void *slavio_misc_init(target_phys_addr_t base, target_phys_addr_t power_base,
408 409 410
                       target_phys_addr_t aux1_base,
                       target_phys_addr_t aux2_base, qemu_irq irq,
                       CPUState *env)
B
bellard 已提交
411
{
412
    int io;
B
bellard 已提交
413 414 415 416 417 418
    MiscState *s;

    s = qemu_mallocz(sizeof(MiscState));
    if (!s)
        return NULL;

419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
    if (base) {
        /* 8 bit registers */
        io = cpu_register_io_memory(0, slavio_misc_mem_read,
                                    slavio_misc_mem_write, s);
        // Slavio control
        cpu_register_physical_memory(base + MISC_CFG, MISC_SIZE, io);
        // Diagnostics
        cpu_register_physical_memory(base + MISC_DIAG, MISC_SIZE, io);
        // Modem control
        cpu_register_physical_memory(base + MISC_MDM, MISC_SIZE, io);

        /* 16 bit registers */
        io = cpu_register_io_memory(0, slavio_led_mem_read,
                                    slavio_led_mem_write, s);
        /* ss600mp diag LEDs */
        cpu_register_physical_memory(base + MISC_LEDS, MISC_SIZE, io);

        /* 32 bit registers */
        io = cpu_register_io_memory(0, slavio_sysctrl_mem_read,
                                    slavio_sysctrl_mem_write, s);
        // System control
        cpu_register_physical_memory(base + MISC_SYS, SYSCTRL_SIZE, io);
    }

    // AUX 1 (Misc System Functions)
    if (aux1_base) {
        io = cpu_register_io_memory(0, slavio_aux1_mem_read,
                                    slavio_aux1_mem_write, s);
        cpu_register_physical_memory(aux1_base, MISC_SIZE, io);
    }

    // AUX 2 (Software Powerdown Control)
    if (aux2_base) {
        io = cpu_register_io_memory(0, slavio_aux2_mem_read,
                                    slavio_aux2_mem_write, s);
        cpu_register_physical_memory(aux2_base, MISC_SIZE, io);
    }

    // Power management (APC) XXX: not a Slavio device
    if (power_base) {
        io = cpu_register_io_memory(0, apc_mem_read, apc_mem_write, s);
        cpu_register_physical_memory(power_base, MISC_SIZE, io);
    }
462

B
bellard 已提交
463
    s->irq = irq;
464
    s->env = env;
B
bellard 已提交
465

466 467
    register_savevm("slavio_misc", base, 1, slavio_misc_save, slavio_misc_load,
                    s);
B
bellard 已提交
468 469
    qemu_register_reset(slavio_misc_reset, s);
    slavio_misc_reset(s);
470

B
bellard 已提交
471 472
    return s;
}