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