xref: /openbmc/qemu/hw/intc/xics_pnv.c (revision 64552b6b)
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 "qemu/module.h"
25 #include "hw/ppc/xics.h"
26 
27 #define ICP_XIRR_POLL    0 /* 1 byte (CPRR) or 4 bytes */
28 #define ICP_XIRR         4 /* 1 byte (CPRR) or 4 bytes */
29 #define ICP_MFRR        12 /* 1 byte access only */
30 
31 #define ICP_LINKA       16 /* unused */
32 #define ICP_LINKB       20 /* unused */
33 #define ICP_LINKC       24 /* unused */
34 
35 static 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:
89 bad_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 
97 static 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:
143 bad_access:
144         qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%"
145                       HWADDR_PRIx"/%d\n", addr, width);
146     }
147 }
148 
149 static 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 
163 static 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 
180 static 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 
190 static 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 
198 static void pnv_icp_register_types(void)
199 {
200     type_register_static(&pnv_icp_info);
201 }
202 
203 type_init(pnv_icp_register_types)
204