1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#include "qemu/osdep.h"
21#include "qapi/error.h"
22#include "sysemu/sysemu.h"
23#include "qemu/log.h"
24#include "qemu/module.h"
25#include "hw/ppc/xics.h"
26
27#define ICP_XIRR_POLL 0
28#define ICP_XIRR 4
29#define ICP_MFRR 12
30
31#define ICP_LINKA 16
32#define ICP_LINKB 20
33#define ICP_LINKC 24
34
35static 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:
89bad_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
97static 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:
143bad_access:
144 qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%"
145 HWADDR_PRIx"/%d\n", addr, width);
146 }
147}
148
149static 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
163static void pnv_icp_realize(DeviceState *dev, Error **errp)
164{
165 ICPState *icp = ICP(dev);
166 PnvICPState *pnv_icp = PNV_ICP(icp);
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 }
175
176 memory_region_init_io(&pnv_icp->mmio, OBJECT(icp), &pnv_icp_ops,
177 icp, "icp-thread", 0x1000);
178}
179
180static void pnv_icp_class_init(ObjectClass *klass, void *data)
181{
182 DeviceClass *dc = DEVICE_CLASS(klass);
183 ICPStateClass *icpc = ICP_CLASS(klass);
184
185 device_class_set_parent_realize(dc, pnv_icp_realize,
186 &icpc->parent_realize);
187 dc->desc = "PowerNV ICP";
188}
189
190static 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
198static void pnv_icp_register_types(void)
199{
200 type_register_static(&pnv_icp_info);
201}
202
203type_init(pnv_icp_register_types)
204