xref: /openbmc/qemu/hw/intc/xics_pnv.c (revision 8f3cd250)
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 "sysemu/sysemu.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     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