Statistics
| Branch: | Revision:

root / hw / spapr_vio.c @ b13ce26d

History | View | Annotate | Download (16.3 kB)

1 4040ab72 David Gibson
/*
2 4040ab72 David Gibson
 * QEMU sPAPR VIO code
3 4040ab72 David Gibson
 *
4 4040ab72 David Gibson
 * Copyright (c) 2010 David Gibson, IBM Corporation <dwg@au1.ibm.com>
5 4040ab72 David Gibson
 * Based on the s390 virtio bus code:
6 4040ab72 David Gibson
 * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
7 4040ab72 David Gibson
 *
8 4040ab72 David Gibson
 * This library is free software; you can redistribute it and/or
9 4040ab72 David Gibson
 * modify it under the terms of the GNU Lesser General Public
10 4040ab72 David Gibson
 * License as published by the Free Software Foundation; either
11 4040ab72 David Gibson
 * version 2 of the License, or (at your option) any later version.
12 4040ab72 David Gibson
 *
13 4040ab72 David Gibson
 * This library is distributed in the hope that it will be useful,
14 4040ab72 David Gibson
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 4040ab72 David Gibson
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 4040ab72 David Gibson
 * Lesser General Public License for more details.
17 4040ab72 David Gibson
 *
18 4040ab72 David Gibson
 * You should have received a copy of the GNU Lesser General Public
19 4040ab72 David Gibson
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
20 4040ab72 David Gibson
 */
21 4040ab72 David Gibson
22 4040ab72 David Gibson
#include "hw.h"
23 4040ab72 David Gibson
#include "sysemu.h"
24 4040ab72 David Gibson
#include "boards.h"
25 4040ab72 David Gibson
#include "monitor.h"
26 4040ab72 David Gibson
#include "loader.h"
27 4040ab72 David Gibson
#include "elf.h"
28 4040ab72 David Gibson
#include "hw/sysbus.h"
29 4040ab72 David Gibson
#include "kvm.h"
30 4040ab72 David Gibson
#include "device_tree.h"
31 b45d63b6 Ben Herrenschmidt
#include "kvm_ppc.h"
32 4040ab72 David Gibson
33 4040ab72 David Gibson
#include "hw/spapr.h"
34 4040ab72 David Gibson
#include "hw/spapr_vio.h"
35 277f9acf Paolo Bonzini
#include "hw/xics.h"
36 4040ab72 David Gibson
37 4040ab72 David Gibson
#ifdef CONFIG_FDT
38 4040ab72 David Gibson
#include <libfdt.h>
39 4040ab72 David Gibson
#endif /* CONFIG_FDT */
40 4040ab72 David Gibson
41 4040ab72 David Gibson
/* #define DEBUG_SPAPR */
42 4040ab72 David Gibson
43 4040ab72 David Gibson
#ifdef DEBUG_SPAPR
44 4040ab72 David Gibson
#define dprintf(fmt, ...) \
45 4040ab72 David Gibson
    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
46 4040ab72 David Gibson
#else
47 4040ab72 David Gibson
#define dprintf(fmt, ...) \
48 4040ab72 David Gibson
    do { } while (0)
49 4040ab72 David Gibson
#endif
50 4040ab72 David Gibson
51 3cb75a7c Paolo Bonzini
static Property spapr_vio_props[] = {
52 a307d594 Alexey Kardashevskiy
    DEFINE_PROP_UINT32("irq", VIOsPAPRDevice, irq, 0), \
53 3cb75a7c Paolo Bonzini
    DEFINE_PROP_END_OF_LIST(),
54 3cb75a7c Paolo Bonzini
};
55 3cb75a7c Paolo Bonzini
56 0d936928 Anthony Liguori
static const TypeInfo spapr_vio_bus_info = {
57 0d936928 Anthony Liguori
    .name = TYPE_SPAPR_VIO_BUS,
58 0d936928 Anthony Liguori
    .parent = TYPE_BUS,
59 0d936928 Anthony Liguori
    .instance_size = sizeof(VIOsPAPRBus),
60 4040ab72 David Gibson
};
61 4040ab72 David Gibson
62 4040ab72 David Gibson
VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg)
63 4040ab72 David Gibson
{
64 0866aca1 Anthony Liguori
    BusChild *kid;
65 4040ab72 David Gibson
    VIOsPAPRDevice *dev = NULL;
66 4040ab72 David Gibson
67 0866aca1 Anthony Liguori
    QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
68 0866aca1 Anthony Liguori
        dev = (VIOsPAPRDevice *)kid->child;
69 4040ab72 David Gibson
        if (dev->reg == reg) {
70 5435352c David Gibson
            return dev;
71 4040ab72 David Gibson
        }
72 4040ab72 David Gibson
    }
73 4040ab72 David Gibson
74 5435352c David Gibson
    return NULL;
75 4040ab72 David Gibson
}
76 4040ab72 David Gibson
77 1e34d859 Michael Ellerman
static char *vio_format_dev_name(VIOsPAPRDevice *dev)
78 1e34d859 Michael Ellerman
{
79 3954d33a Anthony Liguori
    VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
80 1e34d859 Michael Ellerman
    char *name;
81 1e34d859 Michael Ellerman
82 1e34d859 Michael Ellerman
    /* Device tree style name device@reg */
83 3954d33a Anthony Liguori
    if (asprintf(&name, "%s@%x", pc->dt_name, dev->reg) < 0) {
84 1e34d859 Michael Ellerman
        return NULL;
85 1e34d859 Michael Ellerman
    }
86 1e34d859 Michael Ellerman
87 1e34d859 Michael Ellerman
    return name;
88 1e34d859 Michael Ellerman
}
89 1e34d859 Michael Ellerman
90 4040ab72 David Gibson
#ifdef CONFIG_FDT
91 4040ab72 David Gibson
static int vio_make_devnode(VIOsPAPRDevice *dev,
92 4040ab72 David Gibson
                            void *fdt)
93 4040ab72 David Gibson
{
94 3954d33a Anthony Liguori
    VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
95 1e34d859 Michael Ellerman
    int vdevice_off, node_off, ret;
96 1e34d859 Michael Ellerman
    char *dt_name;
97 4040ab72 David Gibson
98 4040ab72 David Gibson
    vdevice_off = fdt_path_offset(fdt, "/vdevice");
99 4040ab72 David Gibson
    if (vdevice_off < 0) {
100 4040ab72 David Gibson
        return vdevice_off;
101 4040ab72 David Gibson
    }
102 4040ab72 David Gibson
103 1e34d859 Michael Ellerman
    dt_name = vio_format_dev_name(dev);
104 1e34d859 Michael Ellerman
    if (!dt_name) {
105 1e34d859 Michael Ellerman
        return -ENOMEM;
106 1e34d859 Michael Ellerman
    }
107 1e34d859 Michael Ellerman
108 1e34d859 Michael Ellerman
    node_off = fdt_add_subnode(fdt, vdevice_off, dt_name);
109 1e34d859 Michael Ellerman
    free(dt_name);
110 4040ab72 David Gibson
    if (node_off < 0) {
111 4040ab72 David Gibson
        return node_off;
112 4040ab72 David Gibson
    }
113 4040ab72 David Gibson
114 4040ab72 David Gibson
    ret = fdt_setprop_cell(fdt, node_off, "reg", dev->reg);
115 4040ab72 David Gibson
    if (ret < 0) {
116 4040ab72 David Gibson
        return ret;
117 4040ab72 David Gibson
    }
118 4040ab72 David Gibson
119 3954d33a Anthony Liguori
    if (pc->dt_type) {
120 4040ab72 David Gibson
        ret = fdt_setprop_string(fdt, node_off, "device_type",
121 3954d33a Anthony Liguori
                                 pc->dt_type);
122 4040ab72 David Gibson
        if (ret < 0) {
123 4040ab72 David Gibson
            return ret;
124 4040ab72 David Gibson
        }
125 4040ab72 David Gibson
    }
126 4040ab72 David Gibson
127 3954d33a Anthony Liguori
    if (pc->dt_compatible) {
128 4040ab72 David Gibson
        ret = fdt_setprop_string(fdt, node_off, "compatible",
129 3954d33a Anthony Liguori
                                 pc->dt_compatible);
130 4040ab72 David Gibson
        if (ret < 0) {
131 4040ab72 David Gibson
            return ret;
132 4040ab72 David Gibson
        }
133 4040ab72 David Gibson
    }
134 4040ab72 David Gibson
135 a307d594 Alexey Kardashevskiy
    if (dev->irq) {
136 a307d594 Alexey Kardashevskiy
        uint32_t ints_prop[] = {cpu_to_be32(dev->irq), 0};
137 00dc738d David Gibson
138 00dc738d David Gibson
        ret = fdt_setprop(fdt, node_off, "interrupts", ints_prop,
139 00dc738d David Gibson
                          sizeof(ints_prop));
140 00dc738d David Gibson
        if (ret < 0) {
141 00dc738d David Gibson
            return ret;
142 00dc738d David Gibson
        }
143 00dc738d David Gibson
    }
144 00dc738d David Gibson
145 5c4cbcf2 Alexey Kardashevskiy
    ret = spapr_tcet_dma_dt(fdt, node_off, "ibm,my-dma-window", dev->dma);
146 ad0ebb91 David Gibson
    if (ret < 0) {
147 ad0ebb91 David Gibson
        return ret;
148 ee86dfee David Gibson
    }
149 ee86dfee David Gibson
150 3954d33a Anthony Liguori
    if (pc->devnode) {
151 3954d33a Anthony Liguori
        ret = (pc->devnode)(dev, fdt, node_off);
152 4040ab72 David Gibson
        if (ret < 0) {
153 4040ab72 David Gibson
            return ret;
154 4040ab72 David Gibson
        }
155 4040ab72 David Gibson
    }
156 4040ab72 David Gibson
157 4040ab72 David Gibson
    return node_off;
158 4040ab72 David Gibson
}
159 4040ab72 David Gibson
#endif /* CONFIG_FDT */
160 4040ab72 David Gibson
161 ee86dfee David Gibson
/*
162 b45d63b6 Ben Herrenschmidt
 * CRQ handling
163 b45d63b6 Ben Herrenschmidt
 */
164 b13ce26d Andreas Färber
static target_ulong h_reg_crq(PowerPCCPU *cpu, sPAPREnvironment *spapr,
165 b45d63b6 Ben Herrenschmidt
                              target_ulong opcode, target_ulong *args)
166 b45d63b6 Ben Herrenschmidt
{
167 b45d63b6 Ben Herrenschmidt
    target_ulong reg = args[0];
168 b45d63b6 Ben Herrenschmidt
    target_ulong queue_addr = args[1];
169 b45d63b6 Ben Herrenschmidt
    target_ulong queue_len = args[2];
170 b45d63b6 Ben Herrenschmidt
    VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
171 b45d63b6 Ben Herrenschmidt
172 b45d63b6 Ben Herrenschmidt
    if (!dev) {
173 d9599c92 David Gibson
        hcall_dprintf("Unit 0x" TARGET_FMT_lx " does not exist\n", reg);
174 b45d63b6 Ben Herrenschmidt
        return H_PARAMETER;
175 b45d63b6 Ben Herrenschmidt
    }
176 b45d63b6 Ben Herrenschmidt
177 b45d63b6 Ben Herrenschmidt
    /* We can't grok a queue size bigger than 256M for now */
178 b45d63b6 Ben Herrenschmidt
    if (queue_len < 0x1000 || queue_len > 0x10000000) {
179 d9599c92 David Gibson
        hcall_dprintf("Queue size too small or too big (0x" TARGET_FMT_lx
180 d9599c92 David Gibson
                      ")\n", queue_len);
181 b45d63b6 Ben Herrenschmidt
        return H_PARAMETER;
182 b45d63b6 Ben Herrenschmidt
    }
183 b45d63b6 Ben Herrenschmidt
184 b45d63b6 Ben Herrenschmidt
    /* Check queue alignment */
185 b45d63b6 Ben Herrenschmidt
    if (queue_addr & 0xfff) {
186 d9599c92 David Gibson
        hcall_dprintf("Queue not aligned (0x" TARGET_FMT_lx ")\n", queue_addr);
187 b45d63b6 Ben Herrenschmidt
        return H_PARAMETER;
188 b45d63b6 Ben Herrenschmidt
    }
189 b45d63b6 Ben Herrenschmidt
190 b45d63b6 Ben Herrenschmidt
    /* Check if device supports CRQs */
191 b45d63b6 Ben Herrenschmidt
    if (!dev->crq.SendFunc) {
192 8e01f355 David Gibson
        hcall_dprintf("Device does not support CRQ\n");
193 b45d63b6 Ben Herrenschmidt
        return H_NOT_FOUND;
194 b45d63b6 Ben Herrenschmidt
    }
195 b45d63b6 Ben Herrenschmidt
196 b45d63b6 Ben Herrenschmidt
    /* Already a queue ? */
197 b45d63b6 Ben Herrenschmidt
    if (dev->crq.qsize) {
198 8e01f355 David Gibson
        hcall_dprintf("CRQ already registered\n");
199 b45d63b6 Ben Herrenschmidt
        return H_RESOURCE;
200 b45d63b6 Ben Herrenschmidt
    }
201 b45d63b6 Ben Herrenschmidt
    dev->crq.qladdr = queue_addr;
202 b45d63b6 Ben Herrenschmidt
    dev->crq.qsize = queue_len;
203 b45d63b6 Ben Herrenschmidt
    dev->crq.qnext = 0;
204 b45d63b6 Ben Herrenschmidt
205 b45d63b6 Ben Herrenschmidt
    dprintf("CRQ for dev 0x" TARGET_FMT_lx " registered at 0x"
206 b45d63b6 Ben Herrenschmidt
            TARGET_FMT_lx "/0x" TARGET_FMT_lx "\n",
207 b45d63b6 Ben Herrenschmidt
            reg, queue_addr, queue_len);
208 b45d63b6 Ben Herrenschmidt
    return H_SUCCESS;
209 b45d63b6 Ben Herrenschmidt
}
210 b45d63b6 Ben Herrenschmidt
211 8e01f355 David Gibson
static target_ulong free_crq(VIOsPAPRDevice *dev)
212 8e01f355 David Gibson
{
213 8e01f355 David Gibson
    dev->crq.qladdr = 0;
214 8e01f355 David Gibson
    dev->crq.qsize = 0;
215 8e01f355 David Gibson
    dev->crq.qnext = 0;
216 8e01f355 David Gibson
217 8e01f355 David Gibson
    dprintf("CRQ for dev 0x%" PRIx32 " freed\n", dev->reg);
218 8e01f355 David Gibson
219 8e01f355 David Gibson
    return H_SUCCESS;
220 8e01f355 David Gibson
}
221 8e01f355 David Gibson
222 b13ce26d Andreas Färber
static target_ulong h_free_crq(PowerPCCPU *cpu, sPAPREnvironment *spapr,
223 b45d63b6 Ben Herrenschmidt
                               target_ulong opcode, target_ulong *args)
224 b45d63b6 Ben Herrenschmidt
{
225 b45d63b6 Ben Herrenschmidt
    target_ulong reg = args[0];
226 b45d63b6 Ben Herrenschmidt
    VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
227 b45d63b6 Ben Herrenschmidt
228 b45d63b6 Ben Herrenschmidt
    if (!dev) {
229 d9599c92 David Gibson
        hcall_dprintf("Unit 0x" TARGET_FMT_lx " does not exist\n", reg);
230 b45d63b6 Ben Herrenschmidt
        return H_PARAMETER;
231 b45d63b6 Ben Herrenschmidt
    }
232 b45d63b6 Ben Herrenschmidt
233 8e01f355 David Gibson
    return free_crq(dev);
234 b45d63b6 Ben Herrenschmidt
}
235 b45d63b6 Ben Herrenschmidt
236 b13ce26d Andreas Färber
static target_ulong h_send_crq(PowerPCCPU *cpu, sPAPREnvironment *spapr,
237 b45d63b6 Ben Herrenschmidt
                               target_ulong opcode, target_ulong *args)
238 b45d63b6 Ben Herrenschmidt
{
239 b45d63b6 Ben Herrenschmidt
    target_ulong reg = args[0];
240 b45d63b6 Ben Herrenschmidt
    target_ulong msg_hi = args[1];
241 b45d63b6 Ben Herrenschmidt
    target_ulong msg_lo = args[2];
242 b45d63b6 Ben Herrenschmidt
    VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
243 b45d63b6 Ben Herrenschmidt
    uint64_t crq_mangle[2];
244 b45d63b6 Ben Herrenschmidt
245 b45d63b6 Ben Herrenschmidt
    if (!dev) {
246 d9599c92 David Gibson
        hcall_dprintf("Unit 0x" TARGET_FMT_lx " does not exist\n", reg);
247 b45d63b6 Ben Herrenschmidt
        return H_PARAMETER;
248 b45d63b6 Ben Herrenschmidt
    }
249 b45d63b6 Ben Herrenschmidt
    crq_mangle[0] = cpu_to_be64(msg_hi);
250 b45d63b6 Ben Herrenschmidt
    crq_mangle[1] = cpu_to_be64(msg_lo);
251 b45d63b6 Ben Herrenschmidt
252 b45d63b6 Ben Herrenschmidt
    if (dev->crq.SendFunc) {
253 b45d63b6 Ben Herrenschmidt
        return dev->crq.SendFunc(dev, (uint8_t *)crq_mangle);
254 b45d63b6 Ben Herrenschmidt
    }
255 b45d63b6 Ben Herrenschmidt
256 b45d63b6 Ben Herrenschmidt
    return H_HARDWARE;
257 b45d63b6 Ben Herrenschmidt
}
258 b45d63b6 Ben Herrenschmidt
259 b13ce26d Andreas Färber
static target_ulong h_enable_crq(PowerPCCPU *cpu, sPAPREnvironment *spapr,
260 b45d63b6 Ben Herrenschmidt
                                 target_ulong opcode, target_ulong *args)
261 b45d63b6 Ben Herrenschmidt
{
262 b45d63b6 Ben Herrenschmidt
    target_ulong reg = args[0];
263 b45d63b6 Ben Herrenschmidt
    VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
264 b45d63b6 Ben Herrenschmidt
265 b45d63b6 Ben Herrenschmidt
    if (!dev) {
266 d9599c92 David Gibson
        hcall_dprintf("Unit 0x" TARGET_FMT_lx " does not exist\n", reg);
267 b45d63b6 Ben Herrenschmidt
        return H_PARAMETER;
268 b45d63b6 Ben Herrenschmidt
    }
269 b45d63b6 Ben Herrenschmidt
270 b45d63b6 Ben Herrenschmidt
    return 0;
271 b45d63b6 Ben Herrenschmidt
}
272 b45d63b6 Ben Herrenschmidt
273 b45d63b6 Ben Herrenschmidt
/* Returns negative error, 0 success, or positive: queue full */
274 b45d63b6 Ben Herrenschmidt
int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq)
275 b45d63b6 Ben Herrenschmidt
{
276 b45d63b6 Ben Herrenschmidt
    int rc;
277 b45d63b6 Ben Herrenschmidt
    uint8_t byte;
278 b45d63b6 Ben Herrenschmidt
279 b45d63b6 Ben Herrenschmidt
    if (!dev->crq.qsize) {
280 b45d63b6 Ben Herrenschmidt
        fprintf(stderr, "spapr_vio_send_creq on uninitialized queue\n");
281 b45d63b6 Ben Herrenschmidt
        return -1;
282 b45d63b6 Ben Herrenschmidt
    }
283 b45d63b6 Ben Herrenschmidt
284 b45d63b6 Ben Herrenschmidt
    /* Maybe do a fast path for KVM just writing to the pages */
285 ad0ebb91 David Gibson
    rc = spapr_vio_dma_read(dev, dev->crq.qladdr + dev->crq.qnext, &byte, 1);
286 b45d63b6 Ben Herrenschmidt
    if (rc) {
287 b45d63b6 Ben Herrenschmidt
        return rc;
288 b45d63b6 Ben Herrenschmidt
    }
289 b45d63b6 Ben Herrenschmidt
    if (byte != 0) {
290 b45d63b6 Ben Herrenschmidt
        return 1;
291 b45d63b6 Ben Herrenschmidt
    }
292 b45d63b6 Ben Herrenschmidt
293 ad0ebb91 David Gibson
    rc = spapr_vio_dma_write(dev, dev->crq.qladdr + dev->crq.qnext + 8,
294 b45d63b6 Ben Herrenschmidt
                             &crq[8], 8);
295 b45d63b6 Ben Herrenschmidt
    if (rc) {
296 b45d63b6 Ben Herrenschmidt
        return rc;
297 b45d63b6 Ben Herrenschmidt
    }
298 b45d63b6 Ben Herrenschmidt
299 b45d63b6 Ben Herrenschmidt
    kvmppc_eieio();
300 b45d63b6 Ben Herrenschmidt
301 ad0ebb91 David Gibson
    rc = spapr_vio_dma_write(dev, dev->crq.qladdr + dev->crq.qnext, crq, 8);
302 b45d63b6 Ben Herrenschmidt
    if (rc) {
303 b45d63b6 Ben Herrenschmidt
        return rc;
304 b45d63b6 Ben Herrenschmidt
    }
305 b45d63b6 Ben Herrenschmidt
306 b45d63b6 Ben Herrenschmidt
    dev->crq.qnext = (dev->crq.qnext + 16) % dev->crq.qsize;
307 b45d63b6 Ben Herrenschmidt
308 b45d63b6 Ben Herrenschmidt
    if (dev->signal_state & 1) {
309 a307d594 Alexey Kardashevskiy
        qemu_irq_pulse(spapr_vio_qirq(dev));
310 b45d63b6 Ben Herrenschmidt
    }
311 b45d63b6 Ben Herrenschmidt
312 b45d63b6 Ben Herrenschmidt
    return 0;
313 b45d63b6 Ben Herrenschmidt
}
314 b45d63b6 Ben Herrenschmidt
315 08942ac1 Ben Herrenschmidt
/* "quiesce" handling */
316 08942ac1 Ben Herrenschmidt
317 08942ac1 Ben Herrenschmidt
static void spapr_vio_quiesce_one(VIOsPAPRDevice *dev)
318 08942ac1 Ben Herrenschmidt
{
319 ad0ebb91 David Gibson
    if (dev->dma) {
320 53724ee5 David Gibson
        spapr_tce_reset(dev->dma);
321 08942ac1 Ben Herrenschmidt
    }
322 4dd96f24 David Gibson
    free_crq(dev);
323 08942ac1 Ben Herrenschmidt
}
324 08942ac1 Ben Herrenschmidt
325 08942ac1 Ben Herrenschmidt
static void rtas_set_tce_bypass(sPAPREnvironment *spapr, uint32_t token,
326 08942ac1 Ben Herrenschmidt
                                uint32_t nargs, target_ulong args,
327 08942ac1 Ben Herrenschmidt
                                uint32_t nret, target_ulong rets)
328 08942ac1 Ben Herrenschmidt
{
329 08942ac1 Ben Herrenschmidt
    VIOsPAPRBus *bus = spapr->vio_bus;
330 08942ac1 Ben Herrenschmidt
    VIOsPAPRDevice *dev;
331 08942ac1 Ben Herrenschmidt
    uint32_t unit, enable;
332 08942ac1 Ben Herrenschmidt
333 08942ac1 Ben Herrenschmidt
    if (nargs != 2) {
334 08942ac1 Ben Herrenschmidt
        rtas_st(rets, 0, -3);
335 08942ac1 Ben Herrenschmidt
        return;
336 08942ac1 Ben Herrenschmidt
    }
337 08942ac1 Ben Herrenschmidt
    unit = rtas_ld(args, 0);
338 08942ac1 Ben Herrenschmidt
    enable = rtas_ld(args, 1);
339 08942ac1 Ben Herrenschmidt
    dev = spapr_vio_find_by_reg(bus, unit);
340 08942ac1 Ben Herrenschmidt
    if (!dev) {
341 08942ac1 Ben Herrenschmidt
        rtas_st(rets, 0, -3);
342 08942ac1 Ben Herrenschmidt
        return;
343 08942ac1 Ben Herrenschmidt
    }
344 ad0ebb91 David Gibson
345 53724ee5 David Gibson
    if (!dev->dma) {
346 53724ee5 David Gibson
        rtas_st(rets, 0, -3);
347 53724ee5 David Gibson
        return;
348 08942ac1 Ben Herrenschmidt
    }
349 08942ac1 Ben Herrenschmidt
350 53724ee5 David Gibson
    spapr_tce_set_bypass(dev->dma, !!enable);
351 53724ee5 David Gibson
352 08942ac1 Ben Herrenschmidt
    rtas_st(rets, 0, 0);
353 08942ac1 Ben Herrenschmidt
}
354 08942ac1 Ben Herrenschmidt
355 08942ac1 Ben Herrenschmidt
static void rtas_quiesce(sPAPREnvironment *spapr, uint32_t token,
356 08942ac1 Ben Herrenschmidt
                         uint32_t nargs, target_ulong args,
357 08942ac1 Ben Herrenschmidt
                         uint32_t nret, target_ulong rets)
358 08942ac1 Ben Herrenschmidt
{
359 08942ac1 Ben Herrenschmidt
    VIOsPAPRBus *bus = spapr->vio_bus;
360 0866aca1 Anthony Liguori
    BusChild *kid;
361 08942ac1 Ben Herrenschmidt
    VIOsPAPRDevice *dev = NULL;
362 08942ac1 Ben Herrenschmidt
363 08942ac1 Ben Herrenschmidt
    if (nargs != 0) {
364 08942ac1 Ben Herrenschmidt
        rtas_st(rets, 0, -3);
365 08942ac1 Ben Herrenschmidt
        return;
366 08942ac1 Ben Herrenschmidt
    }
367 08942ac1 Ben Herrenschmidt
368 0866aca1 Anthony Liguori
    QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
369 0866aca1 Anthony Liguori
        dev = (VIOsPAPRDevice *)kid->child;
370 08942ac1 Ben Herrenschmidt
        spapr_vio_quiesce_one(dev);
371 08942ac1 Ben Herrenschmidt
    }
372 08942ac1 Ben Herrenschmidt
373 08942ac1 Ben Herrenschmidt
    rtas_st(rets, 0, 0);
374 08942ac1 Ben Herrenschmidt
}
375 08942ac1 Ben Herrenschmidt
376 d601fac4 David Gibson
static VIOsPAPRDevice *reg_conflict(VIOsPAPRDevice *dev)
377 9fc380d3 Michael Ellerman
{
378 d601fac4 David Gibson
    VIOsPAPRBus *bus = DO_UPCAST(VIOsPAPRBus, bus, dev->qdev.parent_bus);
379 0866aca1 Anthony Liguori
    BusChild *kid;
380 d601fac4 David Gibson
    VIOsPAPRDevice *other;
381 9fc380d3 Michael Ellerman
382 9fc380d3 Michael Ellerman
    /*
383 d601fac4 David Gibson
     * Check for a device other than the given one which is already
384 d601fac4 David Gibson
     * using the requested address. We have to open code this because
385 d601fac4 David Gibson
     * the given dev might already be in the list.
386 9fc380d3 Michael Ellerman
     */
387 0866aca1 Anthony Liguori
    QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
388 0866aca1 Anthony Liguori
        other = DO_UPCAST(VIOsPAPRDevice, qdev, kid->child);
389 9fc380d3 Michael Ellerman
390 d601fac4 David Gibson
        if (other != dev && other->reg == dev->reg) {
391 d601fac4 David Gibson
            return other;
392 9fc380d3 Michael Ellerman
        }
393 9fc380d3 Michael Ellerman
    }
394 9fc380d3 Michael Ellerman
395 9fc380d3 Michael Ellerman
    return 0;
396 9fc380d3 Michael Ellerman
}
397 9fc380d3 Michael Ellerman
398 b1c7f725 David Gibson
static void spapr_vio_busdev_reset(DeviceState *qdev)
399 8e01f355 David Gibson
{
400 b1c7f725 David Gibson
    VIOsPAPRDevice *dev = DO_UPCAST(VIOsPAPRDevice, qdev, qdev);
401 b1c7f725 David Gibson
    VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
402 8e01f355 David Gibson
403 4dd96f24 David Gibson
    /* Shut down the request queue and TCEs if necessary */
404 4dd96f24 David Gibson
    spapr_vio_quiesce_one(dev);
405 4dd96f24 David Gibson
406 4dd96f24 David Gibson
    dev->signal_state = 0;
407 b1c7f725 David Gibson
408 b1c7f725 David Gibson
    if (pc->reset) {
409 b1c7f725 David Gibson
        pc->reset(dev);
410 b1c7f725 David Gibson
    }
411 8e01f355 David Gibson
}
412 8e01f355 David Gibson
413 d307af79 Anthony Liguori
static int spapr_vio_busdev_init(DeviceState *qdev)
414 4040ab72 David Gibson
{
415 4040ab72 David Gibson
    VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev;
416 3954d33a Anthony Liguori
    VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
417 4040ab72 David Gibson
    char *id;
418 9fc380d3 Michael Ellerman
419 d601fac4 David Gibson
    if (dev->reg != -1) {
420 d601fac4 David Gibson
        /*
421 d601fac4 David Gibson
         * Explicitly assigned address, just verify that no-one else
422 d601fac4 David Gibson
         * is using it.  other mechanism). We have to open code this
423 d601fac4 David Gibson
         * rather than using spapr_vio_find_by_reg() because sdev
424 d601fac4 David Gibson
         * itself is already in the list.
425 d601fac4 David Gibson
         */
426 d601fac4 David Gibson
        VIOsPAPRDevice *other = reg_conflict(dev);
427 d601fac4 David Gibson
428 d601fac4 David Gibson
        if (other) {
429 d601fac4 David Gibson
            fprintf(stderr, "vio: %s and %s devices conflict at address %#x\n",
430 d601fac4 David Gibson
                    object_get_typename(OBJECT(qdev)),
431 d601fac4 David Gibson
                    object_get_typename(OBJECT(&other->qdev)),
432 d601fac4 David Gibson
                    dev->reg);
433 d601fac4 David Gibson
            return -1;
434 d601fac4 David Gibson
        }
435 d601fac4 David Gibson
    } else {
436 d601fac4 David Gibson
        /* Need to assign an address */
437 d601fac4 David Gibson
        VIOsPAPRBus *bus = DO_UPCAST(VIOsPAPRBus, bus, dev->qdev.parent_bus);
438 d601fac4 David Gibson
439 d601fac4 David Gibson
        do {
440 d601fac4 David Gibson
            dev->reg = bus->next_reg++;
441 d601fac4 David Gibson
        } while (reg_conflict(dev));
442 9fc380d3 Michael Ellerman
    }
443 4040ab72 David Gibson
444 1e34d859 Michael Ellerman
    /* Don't overwrite ids assigned on the command line */
445 1e34d859 Michael Ellerman
    if (!dev->qdev.id) {
446 1e34d859 Michael Ellerman
        id = vio_format_dev_name(dev);
447 1e34d859 Michael Ellerman
        if (!id) {
448 1e34d859 Michael Ellerman
            return -1;
449 1e34d859 Michael Ellerman
        }
450 1e34d859 Michael Ellerman
        dev->qdev.id = id;
451 4040ab72 David Gibson
    }
452 4040ab72 David Gibson
453 a307d594 Alexey Kardashevskiy
    dev->irq = spapr_allocate_msi(dev->irq);
454 a307d594 Alexey Kardashevskiy
    if (!dev->irq) {
455 e6c866d4 David Gibson
        return -1;
456 416343b1 Paolo Bonzini
    }
457 4040ab72 David Gibson
458 53724ee5 David Gibson
    if (pc->rtce_window_size) {
459 53724ee5 David Gibson
        uint32_t liobn = SPAPR_VIO_BASE_LIOBN | dev->reg;
460 53724ee5 David Gibson
        dev->dma = spapr_tce_new_dma_context(liobn, pc->rtce_window_size);
461 53724ee5 David Gibson
    }
462 ee86dfee David Gibson
463 3954d33a Anthony Liguori
    return pc->init(dev);
464 4040ab72 David Gibson
}
465 4040ab72 David Gibson
466 b13ce26d Andreas Färber
static target_ulong h_vio_signal(PowerPCCPU *cpu, sPAPREnvironment *spapr,
467 00dc738d David Gibson
                                 target_ulong opcode,
468 00dc738d David Gibson
                                 target_ulong *args)
469 00dc738d David Gibson
{
470 00dc738d David Gibson
    target_ulong reg = args[0];
471 00dc738d David Gibson
    target_ulong mode = args[1];
472 00dc738d David Gibson
    VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg);
473 3954d33a Anthony Liguori
    VIOsPAPRDeviceClass *pc;
474 00dc738d David Gibson
475 00dc738d David Gibson
    if (!dev) {
476 00dc738d David Gibson
        return H_PARAMETER;
477 00dc738d David Gibson
    }
478 00dc738d David Gibson
479 3954d33a Anthony Liguori
    pc = VIO_SPAPR_DEVICE_GET_CLASS(dev);
480 00dc738d David Gibson
481 3954d33a Anthony Liguori
    if (mode & ~pc->signal_mask) {
482 00dc738d David Gibson
        return H_PARAMETER;
483 00dc738d David Gibson
    }
484 00dc738d David Gibson
485 00dc738d David Gibson
    dev->signal_state = mode;
486 00dc738d David Gibson
487 00dc738d David Gibson
    return H_SUCCESS;
488 00dc738d David Gibson
}
489 00dc738d David Gibson
490 4040ab72 David Gibson
VIOsPAPRBus *spapr_vio_bus_init(void)
491 4040ab72 David Gibson
{
492 4040ab72 David Gibson
    VIOsPAPRBus *bus;
493 4040ab72 David Gibson
    BusState *qbus;
494 4040ab72 David Gibson
    DeviceState *dev;
495 4040ab72 David Gibson
496 4040ab72 David Gibson
    /* Create bridge device */
497 4040ab72 David Gibson
    dev = qdev_create(NULL, "spapr-vio-bridge");
498 4040ab72 David Gibson
    qdev_init_nofail(dev);
499 4040ab72 David Gibson
500 4040ab72 David Gibson
    /* Create bus on bridge device */
501 4040ab72 David Gibson
502 0d936928 Anthony Liguori
    qbus = qbus_create(TYPE_SPAPR_VIO_BUS, dev, "spapr-vio");
503 4040ab72 David Gibson
    bus = DO_UPCAST(VIOsPAPRBus, bus, qbus);
504 d601fac4 David Gibson
    bus->next_reg = 0x1000;
505 4040ab72 David Gibson
506 00dc738d David Gibson
    /* hcall-vio */
507 00dc738d David Gibson
    spapr_register_hypercall(H_VIO_SIGNAL, h_vio_signal);
508 00dc738d David Gibson
509 b45d63b6 Ben Herrenschmidt
    /* hcall-crq */
510 b45d63b6 Ben Herrenschmidt
    spapr_register_hypercall(H_REG_CRQ, h_reg_crq);
511 b45d63b6 Ben Herrenschmidt
    spapr_register_hypercall(H_FREE_CRQ, h_free_crq);
512 b45d63b6 Ben Herrenschmidt
    spapr_register_hypercall(H_SEND_CRQ, h_send_crq);
513 b45d63b6 Ben Herrenschmidt
    spapr_register_hypercall(H_ENABLE_CRQ, h_enable_crq);
514 b45d63b6 Ben Herrenschmidt
515 08942ac1 Ben Herrenschmidt
    /* RTAS calls */
516 08942ac1 Ben Herrenschmidt
    spapr_rtas_register("ibm,set-tce-bypass", rtas_set_tce_bypass);
517 08942ac1 Ben Herrenschmidt
    spapr_rtas_register("quiesce", rtas_quiesce);
518 08942ac1 Ben Herrenschmidt
519 4040ab72 David Gibson
    return bus;
520 4040ab72 David Gibson
}
521 4040ab72 David Gibson
522 4040ab72 David Gibson
/* Represents sPAPR hcall VIO devices */
523 4040ab72 David Gibson
524 4040ab72 David Gibson
static int spapr_vio_bridge_init(SysBusDevice *dev)
525 4040ab72 David Gibson
{
526 4040ab72 David Gibson
    /* nothing */
527 4040ab72 David Gibson
    return 0;
528 4040ab72 David Gibson
}
529 4040ab72 David Gibson
530 999e12bb Anthony Liguori
static void spapr_vio_bridge_class_init(ObjectClass *klass, void *data)
531 999e12bb Anthony Liguori
{
532 39bffca2 Anthony Liguori
    DeviceClass *dc = DEVICE_CLASS(klass);
533 39bffca2 Anthony Liguori
    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
534 999e12bb Anthony Liguori
535 39bffca2 Anthony Liguori
    k->init = spapr_vio_bridge_init;
536 39bffca2 Anthony Liguori
    dc->no_user = 1;
537 999e12bb Anthony Liguori
}
538 999e12bb Anthony Liguori
539 39bffca2 Anthony Liguori
static TypeInfo spapr_vio_bridge_info = {
540 39bffca2 Anthony Liguori
    .name          = "spapr-vio-bridge",
541 39bffca2 Anthony Liguori
    .parent        = TYPE_SYS_BUS_DEVICE,
542 39bffca2 Anthony Liguori
    .instance_size = sizeof(SysBusDevice),
543 39bffca2 Anthony Liguori
    .class_init    = spapr_vio_bridge_class_init,
544 4040ab72 David Gibson
};
545 4040ab72 David Gibson
546 39bffca2 Anthony Liguori
static void vio_spapr_device_class_init(ObjectClass *klass, void *data)
547 39bffca2 Anthony Liguori
{
548 39bffca2 Anthony Liguori
    DeviceClass *k = DEVICE_CLASS(klass);
549 39bffca2 Anthony Liguori
    k->init = spapr_vio_busdev_init;
550 b1c7f725 David Gibson
    k->reset = spapr_vio_busdev_reset;
551 0d936928 Anthony Liguori
    k->bus_type = TYPE_SPAPR_VIO_BUS;
552 bce54474 Paolo Bonzini
    k->props = spapr_vio_props;
553 39bffca2 Anthony Liguori
}
554 39bffca2 Anthony Liguori
555 3954d33a Anthony Liguori
static TypeInfo spapr_vio_type_info = {
556 3954d33a Anthony Liguori
    .name = TYPE_VIO_SPAPR_DEVICE,
557 3954d33a Anthony Liguori
    .parent = TYPE_DEVICE,
558 3954d33a Anthony Liguori
    .instance_size = sizeof(VIOsPAPRDevice),
559 3954d33a Anthony Liguori
    .abstract = true,
560 3954d33a Anthony Liguori
    .class_size = sizeof(VIOsPAPRDeviceClass),
561 39bffca2 Anthony Liguori
    .class_init = vio_spapr_device_class_init,
562 3954d33a Anthony Liguori
};
563 3954d33a Anthony Liguori
564 83f7d43a Andreas Färber
static void spapr_vio_register_types(void)
565 4040ab72 David Gibson
{
566 0d936928 Anthony Liguori
    type_register_static(&spapr_vio_bus_info);
567 39bffca2 Anthony Liguori
    type_register_static(&spapr_vio_bridge_info);
568 3954d33a Anthony Liguori
    type_register_static(&spapr_vio_type_info);
569 4040ab72 David Gibson
}
570 4040ab72 David Gibson
571 83f7d43a Andreas Färber
type_init(spapr_vio_register_types)
572 4040ab72 David Gibson
573 4040ab72 David Gibson
#ifdef CONFIG_FDT
574 05c19438 David Gibson
static int compare_reg(const void *p1, const void *p2)
575 05c19438 David Gibson
{
576 05c19438 David Gibson
    VIOsPAPRDevice const *dev1, *dev2;
577 05c19438 David Gibson
578 05c19438 David Gibson
    dev1 = (VIOsPAPRDevice *)*(DeviceState **)p1;
579 05c19438 David Gibson
    dev2 = (VIOsPAPRDevice *)*(DeviceState **)p2;
580 05c19438 David Gibson
581 05c19438 David Gibson
    if (dev1->reg < dev2->reg) {
582 05c19438 David Gibson
        return -1;
583 05c19438 David Gibson
    }
584 05c19438 David Gibson
    if (dev1->reg == dev2->reg) {
585 05c19438 David Gibson
        return 0;
586 05c19438 David Gibson
    }
587 05c19438 David Gibson
588 05c19438 David Gibson
    /* dev1->reg > dev2->reg */
589 05c19438 David Gibson
    return 1;
590 05c19438 David Gibson
}
591 05c19438 David Gibson
592 4040ab72 David Gibson
int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt)
593 4040ab72 David Gibson
{
594 05c19438 David Gibson
    DeviceState *qdev, **qdevs;
595 0866aca1 Anthony Liguori
    BusChild *kid;
596 05c19438 David Gibson
    int i, num, ret = 0;
597 4040ab72 David Gibson
598 05c19438 David Gibson
    /* Count qdevs on the bus list */
599 05c19438 David Gibson
    num = 0;
600 0866aca1 Anthony Liguori
    QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
601 05c19438 David Gibson
        num++;
602 05c19438 David Gibson
    }
603 05c19438 David Gibson
604 05c19438 David Gibson
    /* Copy out into an array of pointers */
605 05c19438 David Gibson
    qdevs = g_malloc(sizeof(qdev) * num);
606 05c19438 David Gibson
    num = 0;
607 0866aca1 Anthony Liguori
    QTAILQ_FOREACH(kid, &bus->bus.children, sibling) {
608 0866aca1 Anthony Liguori
        qdevs[num++] = kid->child;
609 05c19438 David Gibson
    }
610 05c19438 David Gibson
611 05c19438 David Gibson
    /* Sort the array */
612 05c19438 David Gibson
    qsort(qdevs, num, sizeof(qdev), compare_reg);
613 05c19438 David Gibson
614 05c19438 David Gibson
    /* Hack alert. Give the devices to libfdt in reverse order, we happen
615 05c19438 David Gibson
     * to know that will mean they are in forward order in the tree. */
616 05c19438 David Gibson
    for (i = num - 1; i >= 0; i--) {
617 05c19438 David Gibson
        VIOsPAPRDevice *dev = (VIOsPAPRDevice *)(qdevs[i]);
618 4040ab72 David Gibson
619 4040ab72 David Gibson
        ret = vio_make_devnode(dev, fdt);
620 4040ab72 David Gibson
621 4040ab72 David Gibson
        if (ret < 0) {
622 05c19438 David Gibson
            goto out;
623 4040ab72 David Gibson
        }
624 4040ab72 David Gibson
    }
625 4040ab72 David Gibson
626 05c19438 David Gibson
    ret = 0;
627 05c19438 David Gibson
out:
628 05c19438 David Gibson
    free(qdevs);
629 05c19438 David Gibson
630 05c19438 David Gibson
    return ret;
631 4040ab72 David Gibson
}
632 68f3a94c David Gibson
633 68f3a94c David Gibson
int spapr_populate_chosen_stdout(void *fdt, VIOsPAPRBus *bus)
634 68f3a94c David Gibson
{
635 68f3a94c David Gibson
    VIOsPAPRDevice *dev;
636 68f3a94c David Gibson
    char *name, *path;
637 68f3a94c David Gibson
    int ret, offset;
638 68f3a94c David Gibson
639 68f3a94c David Gibson
    dev = spapr_vty_get_default(bus);
640 68f3a94c David Gibson
    if (!dev)
641 68f3a94c David Gibson
        return 0;
642 68f3a94c David Gibson
643 68f3a94c David Gibson
    offset = fdt_path_offset(fdt, "/chosen");
644 68f3a94c David Gibson
    if (offset < 0) {
645 68f3a94c David Gibson
        return offset;
646 68f3a94c David Gibson
    }
647 68f3a94c David Gibson
648 68f3a94c David Gibson
    name = vio_format_dev_name(dev);
649 68f3a94c David Gibson
    if (!name) {
650 68f3a94c David Gibson
        return -ENOMEM;
651 68f3a94c David Gibson
    }
652 68f3a94c David Gibson
653 68f3a94c David Gibson
    if (asprintf(&path, "/vdevice/%s", name) < 0) {
654 68f3a94c David Gibson
        path = NULL;
655 68f3a94c David Gibson
        ret = -ENOMEM;
656 68f3a94c David Gibson
        goto out;
657 68f3a94c David Gibson
    }
658 68f3a94c David Gibson
659 68f3a94c David Gibson
    ret = fdt_setprop_string(fdt, offset, "linux,stdout-path", path);
660 68f3a94c David Gibson
out:
661 68f3a94c David Gibson
    free(name);
662 68f3a94c David Gibson
    free(path);
663 68f3a94c David Gibson
664 68f3a94c David Gibson
    return ret;
665 68f3a94c David Gibson
}
666 4040ab72 David Gibson
#endif /* CONFIG_FDT */