]>
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" | |
21 | #include "sysemu/sysemu.h" | |
22 | #include "qapi/error.h" | |
23 | #include "qemu/log.h" | |
24 | #include "hw/ppc/xics.h" | |
25 | ||
26 | #define ICP_XIRR_POLL 0 /* 1 byte (CPRR) or 4 bytes */ | |
27 | #define ICP_XIRR 4 /* 1 byte (CPRR) or 4 bytes */ | |
28 | #define ICP_MFRR 12 /* 1 byte access only */ | |
29 | ||
30 | #define ICP_LINKA 16 /* unused */ | |
31 | #define ICP_LINKB 20 /* unused */ | |
32 | #define ICP_LINKC 24 /* unused */ | |
33 | ||
34 | static uint64_t pnv_icp_read(void *opaque, hwaddr addr, unsigned width) | |
35 | { | |
36 | ICPState *icp = ICP(opaque); | |
37 | PnvICPState *picp = PNV_ICP(opaque); | |
38 | bool byte0 = (width == 1 && (addr & 0x3) == 0); | |
39 | uint64_t val = 0xffffffff; | |
40 | ||
41 | switch (addr & 0xffc) { | |
42 | case ICP_XIRR_POLL: | |
43 | val = icp_ipoll(icp, NULL); | |
44 | if (byte0) { | |
45 | val >>= 24; | |
46 | } else if (width != 4) { | |
47 | goto bad_access; | |
48 | } | |
49 | break; | |
50 | case ICP_XIRR: | |
51 | if (byte0) { | |
52 | val = icp_ipoll(icp, NULL) >> 24; | |
53 | } else if (width == 4) { | |
54 | val = icp_accept(icp); | |
55 | } else { | |
56 | goto bad_access; | |
57 | } | |
58 | break; | |
59 | case ICP_MFRR: | |
60 | if (byte0) { | |
61 | val = icp->mfrr; | |
62 | } else { | |
63 | goto bad_access; | |
64 | } | |
65 | break; | |
66 | case ICP_LINKA: | |
67 | if (width == 4) { | |
68 | val = picp->links[0]; | |
69 | } else { | |
70 | goto bad_access; | |
71 | } | |
72 | break; | |
73 | case ICP_LINKB: | |
74 | if (width == 4) { | |
75 | val = picp->links[1]; | |
76 | } else { | |
77 | goto bad_access; | |
78 | } | |
79 | break; | |
80 | case ICP_LINKC: | |
81 | if (width == 4) { | |
82 | val = picp->links[2]; | |
83 | } else { | |
84 | goto bad_access; | |
85 | } | |
86 | break; | |
87 | default: | |
88 | bad_access: | |
89 | qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%" | |
90 | HWADDR_PRIx"/%d\n", addr, width); | |
91 | } | |
92 | ||
93 | return val; | |
94 | } | |
95 | ||
96 | static void pnv_icp_write(void *opaque, hwaddr addr, uint64_t val, | |
97 | unsigned width) | |
98 | { | |
99 | ICPState *icp = ICP(opaque); | |
100 | PnvICPState *picp = PNV_ICP(opaque); | |
101 | bool byte0 = (width == 1 && (addr & 0x3) == 0); | |
102 | ||
103 | switch (addr & 0xffc) { | |
104 | case ICP_XIRR: | |
105 | if (byte0) { | |
106 | icp_set_cppr(icp, val); | |
107 | } else if (width == 4) { | |
108 | icp_eoi(icp, val); | |
109 | } else { | |
110 | goto bad_access; | |
111 | } | |
112 | break; | |
113 | case ICP_MFRR: | |
114 | if (byte0) { | |
115 | icp_set_mfrr(icp, val); | |
116 | } else { | |
117 | goto bad_access; | |
118 | } | |
119 | break; | |
120 | case ICP_LINKA: | |
121 | if (width == 4) { | |
122 | picp->links[0] = val; | |
123 | } else { | |
124 | goto bad_access; | |
125 | } | |
126 | break; | |
127 | case ICP_LINKB: | |
128 | if (width == 4) { | |
129 | picp->links[1] = val; | |
130 | } else { | |
131 | goto bad_access; | |
132 | } | |
133 | break; | |
134 | case ICP_LINKC: | |
135 | if (width == 4) { | |
136 | picp->links[2] = val; | |
137 | } else { | |
138 | goto bad_access; | |
139 | } | |
140 | break; | |
141 | default: | |
142 | bad_access: | |
143 | qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%" | |
144 | HWADDR_PRIx"/%d\n", addr, width); | |
145 | } | |
146 | } | |
147 | ||
148 | static const MemoryRegionOps pnv_icp_ops = { | |
149 | .read = pnv_icp_read, | |
150 | .write = pnv_icp_write, | |
151 | .endianness = DEVICE_BIG_ENDIAN, | |
152 | .valid = { | |
153 | .min_access_size = 1, | |
154 | .max_access_size = 4, | |
155 | }, | |
156 | .impl = { | |
157 | .min_access_size = 1, | |
158 | .max_access_size = 4, | |
159 | }, | |
160 | }; | |
161 | ||
162 | static void pnv_icp_realize(DeviceState *dev, Error **errp) | |
163 | { | |
164 | PnvICPState *icp = PNV_ICP(dev); | |
165 | ||
166 | memory_region_init_io(&icp->mmio, OBJECT(dev), &pnv_icp_ops, | |
167 | icp, "icp-thread", 0x1000); | |
168 | } | |
169 | ||
170 | static void pnv_icp_class_init(ObjectClass *klass, void *data) | |
171 | { | |
172 | DeviceClass *dc = DEVICE_CLASS(klass); | |
173 | ICPStateClass *icpc = ICP_CLASS(klass); | |
174 | ||
175 | icpc->realize = pnv_icp_realize; | |
176 | dc->desc = "PowerNV ICP"; | |
177 | } | |
178 | ||
179 | static const TypeInfo pnv_icp_info = { | |
180 | .name = TYPE_PNV_ICP, | |
181 | .parent = TYPE_ICP, | |
182 | .instance_size = sizeof(PnvICPState), | |
183 | .class_init = pnv_icp_class_init, | |
184 | .class_size = sizeof(ICPStateClass), | |
185 | }; | |
186 | ||
187 | static void pnv_icp_register_types(void) | |
188 | { | |
189 | type_register_static(&pnv_icp_info); | |
190 | } | |
191 | ||
192 | type_init(pnv_icp_register_types) |