]>
Commit | Line | Data |
---|---|---|
99285aae CLG |
1 | /* |
2 | * QEMU PowerPC PowerNV Interrupt Control Presenter (ICP) model | |
3 | * | |
4 | * Copyright (c) 2017, IBM Corporation. | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public License | |
8 | * as published by the Free Software Foundation; either version 2 of | |
9 | * the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
20 | #include "qemu/osdep.h" | |
a028dd42 | 21 | #include "qapi/error.h" |
99285aae | 22 | #include "sysemu/sysemu.h" |
99285aae | 23 | #include "qemu/log.h" |
0b8fa32f | 24 | #include "qemu/module.h" |
99285aae CLG |
25 | #include "hw/ppc/xics.h" |
26 | ||
27 | #define ICP_XIRR_POLL 0 /* 1 byte (CPRR) or 4 bytes */ | |
28 | #define ICP_XIRR 4 /* 1 byte (CPRR) or 4 bytes */ | |
29 | #define ICP_MFRR 12 /* 1 byte access only */ | |
30 | ||
31 | #define ICP_LINKA 16 /* unused */ | |
32 | #define ICP_LINKB 20 /* unused */ | |
33 | #define ICP_LINKC 24 /* unused */ | |
34 | ||
35 | static uint64_t pnv_icp_read(void *opaque, hwaddr addr, unsigned width) | |
36 | { | |
37 | ICPState *icp = ICP(opaque); | |
38 | PnvICPState *picp = PNV_ICP(opaque); | |
39 | bool byte0 = (width == 1 && (addr & 0x3) == 0); | |
40 | uint64_t val = 0xffffffff; | |
41 | ||
42 | switch (addr & 0xffc) { | |
43 | case ICP_XIRR_POLL: | |
44 | val = icp_ipoll(icp, NULL); | |
45 | if (byte0) { | |
46 | val >>= 24; | |
47 | } else if (width != 4) { | |
48 | goto bad_access; | |
49 | } | |
50 | break; | |
51 | case ICP_XIRR: | |
52 | if (byte0) { | |
53 | val = icp_ipoll(icp, NULL) >> 24; | |
54 | } else if (width == 4) { | |
55 | val = icp_accept(icp); | |
56 | } else { | |
57 | goto bad_access; | |
58 | } | |
59 | break; | |
60 | case ICP_MFRR: | |
61 | if (byte0) { | |
62 | val = icp->mfrr; | |
63 | } else { | |
64 | goto bad_access; | |
65 | } | |
66 | break; | |
67 | case ICP_LINKA: | |
68 | if (width == 4) { | |
69 | val = picp->links[0]; | |
70 | } else { | |
71 | goto bad_access; | |
72 | } | |
73 | break; | |
74 | case ICP_LINKB: | |
75 | if (width == 4) { | |
76 | val = picp->links[1]; | |
77 | } else { | |
78 | goto bad_access; | |
79 | } | |
80 | break; | |
81 | case ICP_LINKC: | |
82 | if (width == 4) { | |
83 | val = picp->links[2]; | |
84 | } else { | |
85 | goto bad_access; | |
86 | } | |
87 | break; | |
88 | default: | |
89 | bad_access: | |
90 | qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%" | |
91 | HWADDR_PRIx"/%d\n", addr, width); | |
92 | } | |
93 | ||
94 | return val; | |
95 | } | |
96 | ||
97 | static void pnv_icp_write(void *opaque, hwaddr addr, uint64_t val, | |
98 | unsigned width) | |
99 | { | |
100 | ICPState *icp = ICP(opaque); | |
101 | PnvICPState *picp = PNV_ICP(opaque); | |
102 | bool byte0 = (width == 1 && (addr & 0x3) == 0); | |
103 | ||
104 | switch (addr & 0xffc) { | |
105 | case ICP_XIRR: | |
106 | if (byte0) { | |
107 | icp_set_cppr(icp, val); | |
108 | } else if (width == 4) { | |
109 | icp_eoi(icp, val); | |
110 | } else { | |
111 | goto bad_access; | |
112 | } | |
113 | break; | |
114 | case ICP_MFRR: | |
115 | if (byte0) { | |
116 | icp_set_mfrr(icp, val); | |
117 | } else { | |
118 | goto bad_access; | |
119 | } | |
120 | break; | |
121 | case ICP_LINKA: | |
122 | if (width == 4) { | |
123 | picp->links[0] = val; | |
124 | } else { | |
125 | goto bad_access; | |
126 | } | |
127 | break; | |
128 | case ICP_LINKB: | |
129 | if (width == 4) { | |
130 | picp->links[1] = val; | |
131 | } else { | |
132 | goto bad_access; | |
133 | } | |
134 | break; | |
135 | case ICP_LINKC: | |
136 | if (width == 4) { | |
137 | picp->links[2] = val; | |
138 | } else { | |
139 | goto bad_access; | |
140 | } | |
141 | break; | |
142 | default: | |
143 | bad_access: | |
144 | qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%" | |
145 | HWADDR_PRIx"/%d\n", addr, width); | |
146 | } | |
147 | } | |
148 | ||
149 | static const MemoryRegionOps pnv_icp_ops = { | |
150 | .read = pnv_icp_read, | |
151 | .write = pnv_icp_write, | |
152 | .endianness = DEVICE_BIG_ENDIAN, | |
153 | .valid = { | |
154 | .min_access_size = 1, | |
155 | .max_access_size = 4, | |
156 | }, | |
157 | .impl = { | |
158 | .min_access_size = 1, | |
159 | .max_access_size = 4, | |
160 | }, | |
161 | }; | |
162 | ||
a028dd42 | 163 | static void pnv_icp_realize(DeviceState *dev, Error **errp) |
99285aae | 164 | { |
a028dd42 | 165 | ICPState *icp = ICP(dev); |
100f7388 | 166 | PnvICPState *pnv_icp = PNV_ICP(icp); |
a028dd42 CLG |
167 | ICPStateClass *icpc = ICP_GET_CLASS(icp); |
168 | Error *local_err = NULL; | |
169 | ||
170 | icpc->parent_realize(dev, &local_err); | |
171 | if (local_err) { | |
172 | error_propagate(errp, local_err); | |
173 | return; | |
174 | } | |
99285aae | 175 | |
100f7388 | 176 | memory_region_init_io(&pnv_icp->mmio, OBJECT(icp), &pnv_icp_ops, |
99285aae CLG |
177 | icp, "icp-thread", 0x1000); |
178 | } | |
179 | ||
180 | static void pnv_icp_class_init(ObjectClass *klass, void *data) | |
181 | { | |
182 | DeviceClass *dc = DEVICE_CLASS(klass); | |
183 | ICPStateClass *icpc = ICP_CLASS(klass); | |
184 | ||
a028dd42 CLG |
185 | device_class_set_parent_realize(dc, pnv_icp_realize, |
186 | &icpc->parent_realize); | |
99285aae CLG |
187 | dc->desc = "PowerNV ICP"; |
188 | } | |
189 | ||
190 | static const TypeInfo pnv_icp_info = { | |
191 | .name = TYPE_PNV_ICP, | |
192 | .parent = TYPE_ICP, | |
193 | .instance_size = sizeof(PnvICPState), | |
194 | .class_init = pnv_icp_class_init, | |
195 | .class_size = sizeof(ICPStateClass), | |
196 | }; | |
197 | ||
198 | static void pnv_icp_register_types(void) | |
199 | { | |
200 | type_register_static(&pnv_icp_info); | |
201 | } | |
202 | ||
203 | type_init(pnv_icp_register_types) |