Statistics
| Branch: | Revision:

root / hw / slavio_misc.c @ ca02f319

History | View | Annotate | Download (8.7 kB)

1
/*
2
 * QEMU Sparc SLAVIO aux io port emulation
3
 *
4
 * Copyright (c) 2005 Fabrice Bellard
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
 * THE SOFTWARE.
23
 */
24
#include "vl.h"
25
/* debug misc */
26
//#define DEBUG_MISC
27

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

    
36
#ifdef DEBUG_MISC
37
#define MISC_DPRINTF(fmt, args...) \
38
do { printf("MISC: " fmt , ##args); } while (0)
39
#else
40
#define MISC_DPRINTF(fmt, args...)
41
#endif
42

    
43
typedef struct MiscState {
44
    qemu_irq irq;
45
    uint8_t config;
46
    uint8_t aux1, aux2;
47
    uint8_t diag, mctrl;
48
    uint32_t sysctrl;
49
} MiscState;
50

    
51
#define MISC_SIZE 1
52
#define SYSCTRL_MAXADDR 3
53
#define SYSCTRL_SIZE (SYSCTRL_MAXADDR + 1)
54

    
55
static void slavio_misc_update_irq(void *opaque)
56
{
57
    MiscState *s = opaque;
58

    
59
    if ((s->aux2 & 0x4) && (s->config & 0x8)) {
60
        MISC_DPRINTF("Raise IRQ\n");
61
        qemu_irq_raise(s->irq);
62
    } else {
63
        MISC_DPRINTF("Lower IRQ\n");
64
        qemu_irq_lower(s->irq);
65
    }
66
}
67

    
68
static void slavio_misc_reset(void *opaque)
69
{
70
    MiscState *s = opaque;
71

    
72
    // Diagnostic and system control registers not cleared in reset
73
    s->config = s->aux1 = s->aux2 = s->mctrl = 0;
74
}
75

    
76
void slavio_set_power_fail(void *opaque, int power_failing)
77
{
78
    MiscState *s = opaque;
79

    
80
    MISC_DPRINTF("Power fail: %d, config: %d\n", power_failing, s->config);
81
    if (power_failing && (s->config & 0x8)) {
82
        s->aux2 |= 0x4;
83
    } else {
84
        s->aux2 &= ~0x4;
85
    }
86
    slavio_misc_update_irq(s);
87
}
88

    
89
static void slavio_misc_mem_writeb(void *opaque, target_phys_addr_t addr,
90
                                   uint32_t val)
91
{
92
    MiscState *s = opaque;
93

    
94
    switch (addr & 0xfff0000) {
95
    case 0x1800000:
96
        MISC_DPRINTF("Write config %2.2x\n", val & 0xff);
97
        s->config = val & 0xff;
98
        slavio_misc_update_irq(s);
99
        break;
100
    case 0x1900000:
101
        MISC_DPRINTF("Write aux1 %2.2x\n", val & 0xff);
102
        s->aux1 = val & 0xff;
103
        break;
104
    case 0x1910000:
105
        val &= 0x3;
106
        MISC_DPRINTF("Write aux2 %2.2x\n", val);
107
        val |= s->aux2 & 0x4;
108
        if (val & 0x2) // Clear Power Fail int
109
            val &= 0x1;
110
        s->aux2 = val;
111
        if (val & 1)
112
            qemu_system_shutdown_request();
113
        slavio_misc_update_irq(s);
114
        break;
115
    case 0x1a00000:
116
        MISC_DPRINTF("Write diag %2.2x\n", val & 0xff);
117
        s->diag = val & 0xff;
118
        break;
119
    case 0x1b00000:
120
        MISC_DPRINTF("Write modem control %2.2x\n", val & 0xff);
121
        s->mctrl = val & 0xff;
122
        break;
123
    case 0xa000000:
124
        MISC_DPRINTF("Write power management %2.2x\n", val & 0xff);
125
        cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HALT);
126
        break;
127
    }
128
}
129

    
130
static uint32_t slavio_misc_mem_readb(void *opaque, target_phys_addr_t addr)
131
{
132
    MiscState *s = opaque;
133
    uint32_t ret = 0;
134

    
135
    switch (addr & 0xfff0000) {
136
    case 0x1800000:
137
        ret = s->config;
138
        MISC_DPRINTF("Read config %2.2x\n", ret);
139
        break;
140
    case 0x1900000:
141
        ret = s->aux1;
142
        MISC_DPRINTF("Read aux1 %2.2x\n", ret);
143
        break;
144
    case 0x1910000:
145
        ret = s->aux2;
146
        MISC_DPRINTF("Read aux2 %2.2x\n", ret);
147
        break;
148
    case 0x1a00000:
149
        ret = s->diag;
150
        MISC_DPRINTF("Read diag %2.2x\n", ret);
151
        break;
152
    case 0x1b00000:
153
        ret = s->mctrl;
154
        MISC_DPRINTF("Read modem control %2.2x\n", ret);
155
        break;
156
    case 0xa000000:
157
        MISC_DPRINTF("Read power management %2.2x\n", ret);
158
        break;
159
    }
160
    return ret;
161
}
162

    
163
static CPUReadMemoryFunc *slavio_misc_mem_read[3] = {
164
    slavio_misc_mem_readb,
165
    slavio_misc_mem_readb,
166
    slavio_misc_mem_readb,
167
};
168

    
169
static CPUWriteMemoryFunc *slavio_misc_mem_write[3] = {
170
    slavio_misc_mem_writeb,
171
    slavio_misc_mem_writeb,
172
    slavio_misc_mem_writeb,
173
};
174

    
175
static uint32_t slavio_sysctrl_mem_readl(void *opaque, target_phys_addr_t addr)
176
{
177
    MiscState *s = opaque;
178
    uint32_t ret = 0, saddr;
179

    
180
    saddr = addr & SYSCTRL_MAXADDR;
181
    switch (saddr) {
182
    case 0:
183
        ret = s->sysctrl;
184
        break;
185
    default:
186
        break;
187
    }
188
    MISC_DPRINTF("Read system control reg 0x" TARGET_FMT_plx " = %x\n", addr,
189
                 ret);
190
    return ret;
191
}
192

    
193
static void slavio_sysctrl_mem_writel(void *opaque, target_phys_addr_t addr,
194
                                      uint32_t val)
195
{
196
    MiscState *s = opaque;
197
    uint32_t saddr;
198

    
199
    saddr = addr & SYSCTRL_MAXADDR;
200
    MISC_DPRINTF("Write system control reg 0x" TARGET_FMT_plx " =  %x\n", addr,
201
                 val);
202
    switch (saddr) {
203
    case 0:
204
        if (val & 1) {
205
            s->sysctrl = 0x2;
206
            qemu_system_reset_request();
207
        }
208
        break;
209
    default:
210
        break;
211
    }
212
}
213

    
214
static CPUReadMemoryFunc *slavio_sysctrl_mem_read[3] = {
215
    slavio_sysctrl_mem_readl,
216
    slavio_sysctrl_mem_readl,
217
    slavio_sysctrl_mem_readl,
218
};
219

    
220
static CPUWriteMemoryFunc *slavio_sysctrl_mem_write[3] = {
221
    slavio_sysctrl_mem_writel,
222
    slavio_sysctrl_mem_writel,
223
    slavio_sysctrl_mem_writel,
224
};
225

    
226
static void slavio_misc_save(QEMUFile *f, void *opaque)
227
{
228
    MiscState *s = opaque;
229
    int tmp;
230
    uint8_t tmp8;
231

    
232
    tmp = 0;
233
    qemu_put_be32s(f, &tmp); /* ignored, was IRQ.  */
234
    qemu_put_8s(f, &s->config);
235
    qemu_put_8s(f, &s->aux1);
236
    qemu_put_8s(f, &s->aux2);
237
    qemu_put_8s(f, &s->diag);
238
    qemu_put_8s(f, &s->mctrl);
239
    tmp8 = s->sysctrl & 0xff;
240
    qemu_put_8s(f, &tmp8);
241
}
242

    
243
static int slavio_misc_load(QEMUFile *f, void *opaque, int version_id)
244
{
245
    MiscState *s = opaque;
246
    int tmp;
247
    uint8_t tmp8;
248

    
249
    if (version_id != 1)
250
        return -EINVAL;
251

    
252
    qemu_get_be32s(f, &tmp);
253
    qemu_get_8s(f, &s->config);
254
    qemu_get_8s(f, &s->aux1);
255
    qemu_get_8s(f, &s->aux2);
256
    qemu_get_8s(f, &s->diag);
257
    qemu_get_8s(f, &s->mctrl);
258
    qemu_get_8s(f, &tmp8);
259
    s->sysctrl = (uint32_t)tmp8;
260
    return 0;
261
}
262

    
263
void *slavio_misc_init(target_phys_addr_t base, target_phys_addr_t power_base,
264
                       qemu_irq irq)
265
{
266
    int slavio_misc_io_memory;
267
    MiscState *s;
268

    
269
    s = qemu_mallocz(sizeof(MiscState));
270
    if (!s)
271
        return NULL;
272

    
273
    /* 8 bit registers */
274
    slavio_misc_io_memory = cpu_register_io_memory(0, slavio_misc_mem_read,
275
                                                   slavio_misc_mem_write, s);
276
    // Slavio control
277
    cpu_register_physical_memory(base + 0x1800000, MISC_SIZE,
278
                                 slavio_misc_io_memory);
279
    // AUX 1
280
    cpu_register_physical_memory(base + 0x1900000, MISC_SIZE,
281
                                 slavio_misc_io_memory);
282
    // AUX 2
283
    cpu_register_physical_memory(base + 0x1910000, MISC_SIZE,
284
                                 slavio_misc_io_memory);
285
    // Diagnostics
286
    cpu_register_physical_memory(base + 0x1a00000, MISC_SIZE,
287
                                 slavio_misc_io_memory);
288
    // Modem control
289
    cpu_register_physical_memory(base + 0x1b00000, MISC_SIZE,
290
                                 slavio_misc_io_memory);
291
    // Power management
292
    cpu_register_physical_memory(power_base, MISC_SIZE, slavio_misc_io_memory);
293

    
294
    /* 32 bit registers */
295
    slavio_misc_io_memory = cpu_register_io_memory(0, slavio_sysctrl_mem_read,
296
                                                   slavio_sysctrl_mem_write,
297
                                                   s);
298
    // System control
299
    cpu_register_physical_memory(base + 0x1f00000, SYSCTRL_SIZE,
300
                                 slavio_misc_io_memory);
301

    
302
    s->irq = irq;
303

    
304
    register_savevm("slavio_misc", base, 1, slavio_misc_save, slavio_misc_load,
305
                    s);
306
    qemu_register_reset(slavio_misc_reset, s);
307
    slavio_misc_reset(s);
308
    return s;
309
}