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 "qapi/error.h" |
22 | #include "qemu/log.h" |
23 | #include "qemu/module.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 | ICPState *icp = ICP(dev); |
165 | PnvICPState *pnv_icp = PNV_ICP(icp); |
166 | ICPStateClass *icpc = ICP_GET_CLASS(icp); |
167 | Error *local_err = NULL; |
168 | |
169 | icpc->parent_realize(dev, &local_err); |
170 | if (local_err) { |
171 | error_propagate(errp, local_err); |
172 | return; |
173 | } |
174 | |
175 | memory_region_init_io(&pnv_icp->mmio, OBJECT(icp), &pnv_icp_ops, |
176 | icp, "icp-thread" , 0x1000); |
177 | } |
178 | |
179 | static void pnv_icp_class_init(ObjectClass *klass, void *data) |
180 | { |
181 | DeviceClass *dc = DEVICE_CLASS(klass); |
182 | ICPStateClass *icpc = ICP_CLASS(klass); |
183 | |
184 | device_class_set_parent_realize(dc, pnv_icp_realize, |
185 | &icpc->parent_realize); |
186 | dc->desc = "PowerNV ICP" ; |
187 | } |
188 | |
189 | static const TypeInfo pnv_icp_info = { |
190 | .name = TYPE_PNV_ICP, |
191 | .parent = TYPE_ICP, |
192 | .instance_size = sizeof(PnvICPState), |
193 | .class_init = pnv_icp_class_init, |
194 | .class_size = sizeof(ICPStateClass), |
195 | }; |
196 | |
197 | static void pnv_icp_register_types(void) |
198 | { |
199 | type_register_static(&pnv_icp_info); |
200 | } |
201 | |
202 | type_init(pnv_icp_register_types) |
203 | |