xref: /openbmc/qemu/hw/ppc/pnv_occ.c (revision 135b03cb)
1 /*
2  * QEMU PowerPC PowerNV Emulation of a few OCC related registers
3  *
4  * Copyright (c) 2015-2017, IBM Corporation.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License, version 2, as
8  * published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "qemu/osdep.h"
20 #include "target/ppc/cpu.h"
21 #include "qapi/error.h"
22 #include "qemu/log.h"
23 #include "qemu/module.h"
24 
25 #include "hw/ppc/pnv.h"
26 #include "hw/ppc/pnv_xscom.h"
27 #include "hw/ppc/pnv_occ.h"
28 
29 #define OCB_OCI_OCCMISC         0x4020
30 #define OCB_OCI_OCCMISC_AND     0x4021
31 #define OCB_OCI_OCCMISC_OR      0x4022
32 
33 static void pnv_occ_set_misc(PnvOCC *occ, uint64_t val)
34 {
35     bool irq_state;
36     PnvOCCClass *poc = PNV_OCC_GET_CLASS(occ);
37 
38     val &= 0xffff000000000000ull;
39 
40     occ->occmisc = val;
41     irq_state = !!(val >> 63);
42     pnv_psi_irq_set(occ->psi, poc->psi_irq, irq_state);
43 }
44 
45 static uint64_t pnv_occ_power8_xscom_read(void *opaque, hwaddr addr,
46                                           unsigned size)
47 {
48     PnvOCC *occ = PNV_OCC(opaque);
49     uint32_t offset = addr >> 3;
50     uint64_t val = 0;
51 
52     switch (offset) {
53     case OCB_OCI_OCCMISC:
54         val = occ->occmisc;
55         break;
56     default:
57         qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register: Ox%"
58                       HWADDR_PRIx "\n", addr >> 3);
59     }
60     return val;
61 }
62 
63 static void pnv_occ_power8_xscom_write(void *opaque, hwaddr addr,
64                                        uint64_t val, unsigned size)
65 {
66     PnvOCC *occ = PNV_OCC(opaque);
67     uint32_t offset = addr >> 3;
68 
69     switch (offset) {
70     case OCB_OCI_OCCMISC_AND:
71         pnv_occ_set_misc(occ, occ->occmisc & val);
72         break;
73     case OCB_OCI_OCCMISC_OR:
74         pnv_occ_set_misc(occ, occ->occmisc | val);
75         break;
76     case OCB_OCI_OCCMISC:
77         pnv_occ_set_misc(occ, val);
78         break;
79     default:
80         qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register: Ox%"
81                       HWADDR_PRIx "\n", addr >> 3);
82     }
83 }
84 
85 static const MemoryRegionOps pnv_occ_power8_xscom_ops = {
86     .read = pnv_occ_power8_xscom_read,
87     .write = pnv_occ_power8_xscom_write,
88     .valid.min_access_size = 8,
89     .valid.max_access_size = 8,
90     .impl.min_access_size = 8,
91     .impl.max_access_size = 8,
92     .endianness = DEVICE_BIG_ENDIAN,
93 };
94 
95 static void pnv_occ_power8_class_init(ObjectClass *klass, void *data)
96 {
97     PnvOCCClass *poc = PNV_OCC_CLASS(klass);
98 
99     poc->xscom_size = PNV_XSCOM_OCC_SIZE;
100     poc->xscom_ops = &pnv_occ_power8_xscom_ops;
101     poc->psi_irq = PSIHB_IRQ_OCC;
102 }
103 
104 static const TypeInfo pnv_occ_power8_type_info = {
105     .name          = TYPE_PNV8_OCC,
106     .parent        = TYPE_PNV_OCC,
107     .instance_size = sizeof(PnvOCC),
108     .class_init    = pnv_occ_power8_class_init,
109 };
110 
111 #define P9_OCB_OCI_OCCMISC              0x6080
112 #define P9_OCB_OCI_OCCMISC_CLEAR        0x6081
113 #define P9_OCB_OCI_OCCMISC_OR           0x6082
114 
115 
116 static uint64_t pnv_occ_power9_xscom_read(void *opaque, hwaddr addr,
117                                           unsigned size)
118 {
119     PnvOCC *occ = PNV_OCC(opaque);
120     uint32_t offset = addr >> 3;
121     uint64_t val = 0;
122 
123     switch (offset) {
124     case P9_OCB_OCI_OCCMISC:
125         val = occ->occmisc;
126         break;
127     default:
128         qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register: Ox%"
129                       HWADDR_PRIx "\n", addr >> 3);
130     }
131     return val;
132 }
133 
134 static void pnv_occ_power9_xscom_write(void *opaque, hwaddr addr,
135                                        uint64_t val, unsigned size)
136 {
137     PnvOCC *occ = PNV_OCC(opaque);
138     uint32_t offset = addr >> 3;
139 
140     switch (offset) {
141     case P9_OCB_OCI_OCCMISC_CLEAR:
142         pnv_occ_set_misc(occ, 0);
143         break;
144     case P9_OCB_OCI_OCCMISC_OR:
145         pnv_occ_set_misc(occ, occ->occmisc | val);
146         break;
147     case P9_OCB_OCI_OCCMISC:
148         pnv_occ_set_misc(occ, val);
149        break;
150     default:
151         qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register: Ox%"
152                       HWADDR_PRIx "\n", addr >> 3);
153     }
154 }
155 
156 static const MemoryRegionOps pnv_occ_power9_xscom_ops = {
157     .read = pnv_occ_power9_xscom_read,
158     .write = pnv_occ_power9_xscom_write,
159     .valid.min_access_size = 8,
160     .valid.max_access_size = 8,
161     .impl.min_access_size = 8,
162     .impl.max_access_size = 8,
163     .endianness = DEVICE_BIG_ENDIAN,
164 };
165 
166 static void pnv_occ_power9_class_init(ObjectClass *klass, void *data)
167 {
168     PnvOCCClass *poc = PNV_OCC_CLASS(klass);
169 
170     poc->xscom_size = PNV9_XSCOM_OCC_SIZE;
171     poc->xscom_ops = &pnv_occ_power9_xscom_ops;
172     poc->psi_irq = PSIHB9_IRQ_OCC;
173 }
174 
175 static const TypeInfo pnv_occ_power9_type_info = {
176     .name          = TYPE_PNV9_OCC,
177     .parent        = TYPE_PNV_OCC,
178     .instance_size = sizeof(PnvOCC),
179     .class_init    = pnv_occ_power9_class_init,
180 };
181 
182 static void pnv_occ_realize(DeviceState *dev, Error **errp)
183 {
184     PnvOCC *occ = PNV_OCC(dev);
185     PnvOCCClass *poc = PNV_OCC_GET_CLASS(occ);
186     Object *obj;
187     Error *local_err = NULL;
188 
189     occ->occmisc = 0;
190 
191     obj = object_property_get_link(OBJECT(dev), "psi", &local_err);
192     if (!obj) {
193         error_propagate(errp, local_err);
194         error_prepend(errp, "required link 'psi' not found: ");
195         return;
196     }
197     occ->psi = PNV_PSI(obj);
198 
199     /* XScom region for OCC registers */
200     pnv_xscom_region_init(&occ->xscom_regs, OBJECT(dev), poc->xscom_ops,
201                           occ, "xscom-occ", poc->xscom_size);
202 }
203 
204 static void pnv_occ_class_init(ObjectClass *klass, void *data)
205 {
206     DeviceClass *dc = DEVICE_CLASS(klass);
207 
208     dc->realize = pnv_occ_realize;
209     dc->desc = "PowerNV OCC Controller";
210 }
211 
212 static const TypeInfo pnv_occ_type_info = {
213     .name          = TYPE_PNV_OCC,
214     .parent        = TYPE_DEVICE,
215     .instance_size = sizeof(PnvOCC),
216     .class_init    = pnv_occ_class_init,
217     .class_size    = sizeof(PnvOCCClass),
218     .abstract      = true,
219 };
220 
221 static void pnv_occ_register_types(void)
222 {
223     type_register_static(&pnv_occ_type_info);
224     type_register_static(&pnv_occ_power8_type_info);
225     type_register_static(&pnv_occ_power9_type_info);
226 }
227 
228 type_init(pnv_occ_register_types);
229