1 | /* |
2 | * GRLIB AHB APB PNP |
3 | * |
4 | * Copyright (C) 2019 AdaCore |
5 | * |
6 | * Developed by : |
7 | * Frederic Konrad <frederic.konrad@adacore.com> |
8 | * |
9 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License as published by |
11 | * the Free Software Foundation, either version 2 of the License, or |
12 | * (at your option) any later version. |
13 | * |
14 | * This program is distributed in the hope that it will be useful, |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | * GNU General Public License for more details. |
18 | * |
19 | * You should have received a copy of the GNU General Public License along |
20 | * with this program; if not, see <http://www.gnu.org/licenses/>. |
21 | * |
22 | */ |
23 | |
24 | #include "qemu/osdep.h" |
25 | #include "hw/sysbus.h" |
26 | #include "hw/misc/grlib_ahb_apb_pnp.h" |
27 | |
28 | #define GRLIB_PNP_VENDOR_SHIFT (24) |
29 | #define GRLIB_PNP_VENDOR_SIZE (8) |
30 | #define GRLIB_PNP_DEV_SHIFT (12) |
31 | #define GRLIB_PNP_DEV_SIZE (12) |
32 | #define GRLIB_PNP_VER_SHIFT (5) |
33 | #define GRLIB_PNP_VER_SIZE (5) |
34 | #define GRLIB_PNP_IRQ_SHIFT (0) |
35 | #define GRLIB_PNP_IRQ_SIZE (5) |
36 | #define GRLIB_PNP_ADDR_SHIFT (20) |
37 | #define GRLIB_PNP_ADDR_SIZE (12) |
38 | #define GRLIB_PNP_MASK_SHIFT (4) |
39 | #define GRLIB_PNP_MASK_SIZE (12) |
40 | |
41 | #define GRLIB_AHB_DEV_ADDR_SHIFT (20) |
42 | #define GRLIB_AHB_DEV_ADDR_SIZE (12) |
43 | #define GRLIB_AHB_ENTRY_SIZE (0x20) |
44 | #define GRLIB_AHB_MAX_DEV (64) |
45 | #define GRLIB_AHB_SLAVE_OFFSET (0x800) |
46 | |
47 | #define GRLIB_APB_DEV_ADDR_SHIFT (8) |
48 | #define GRLIB_APB_DEV_ADDR_SIZE (12) |
49 | #define GRLIB_APB_ENTRY_SIZE (0x08) |
50 | #define GRLIB_APB_MAX_DEV (512) |
51 | |
52 | #define GRLIB_PNP_MAX_REGS (0x1000) |
53 | |
54 | typedef struct AHBPnp { |
55 | SysBusDevice parent_obj; |
56 | MemoryRegion iomem; |
57 | |
58 | uint32_t regs[GRLIB_PNP_MAX_REGS >> 2]; |
59 | uint8_t master_count; |
60 | uint8_t slave_count; |
61 | } AHBPnp; |
62 | |
63 | void grlib_ahb_pnp_add_entry(AHBPnp *dev, uint32_t address, uint32_t mask, |
64 | uint8_t vendor, uint16_t device, int slave, |
65 | int type) |
66 | { |
67 | unsigned int reg_start; |
68 | |
69 | /* |
70 | * AHB entries look like this: |
71 | * |
72 | * 31 -------- 23 -------- 11 ----- 9 -------- 4 --- 0 |
73 | * | VENDOR ID | DEVICE ID | IRQ ? | VERSION | IRQ | |
74 | * -------------------------------------------------- |
75 | * | USER | |
76 | * -------------------------------------------------- |
77 | * | USER | |
78 | * -------------------------------------------------- |
79 | * | USER | |
80 | * -------------------------------------------------- |
81 | * | USER | |
82 | * -------------------------------------------------- |
83 | * 31 ----------- 20 --- 15 ----------------- 3 ---- 0 |
84 | * | ADDR[31..12] | 00PC | MASK | TYPE | |
85 | * -------------------------------------------------- |
86 | * 31 ----------- 20 --- 15 ----------------- 3 ---- 0 |
87 | * | ADDR[31..12] | 00PC | MASK | TYPE | |
88 | * -------------------------------------------------- |
89 | * 31 ----------- 20 --- 15 ----------------- 3 ---- 0 |
90 | * | ADDR[31..12] | 00PC | MASK | TYPE | |
91 | * -------------------------------------------------- |
92 | * 31 ----------- 20 --- 15 ----------------- 3 ---- 0 |
93 | * | ADDR[31..12] | 00PC | MASK | TYPE | |
94 | * -------------------------------------------------- |
95 | */ |
96 | |
97 | if (slave) { |
98 | assert(dev->slave_count < GRLIB_AHB_MAX_DEV); |
99 | reg_start = (GRLIB_AHB_SLAVE_OFFSET |
100 | + (dev->slave_count * GRLIB_AHB_ENTRY_SIZE)) >> 2; |
101 | dev->slave_count++; |
102 | } else { |
103 | assert(dev->master_count < GRLIB_AHB_MAX_DEV); |
104 | reg_start = (dev->master_count * GRLIB_AHB_ENTRY_SIZE) >> 2; |
105 | dev->master_count++; |
106 | } |
107 | |
108 | dev->regs[reg_start] = deposit32(dev->regs[reg_start], |
109 | GRLIB_PNP_VENDOR_SHIFT, |
110 | GRLIB_PNP_VENDOR_SIZE, |
111 | vendor); |
112 | dev->regs[reg_start] = deposit32(dev->regs[reg_start], |
113 | GRLIB_PNP_DEV_SHIFT, |
114 | GRLIB_PNP_DEV_SIZE, |
115 | device); |
116 | reg_start += 4; |
117 | /* AHB Memory Space */ |
118 | dev->regs[reg_start] = type; |
119 | dev->regs[reg_start] = deposit32(dev->regs[reg_start], |
120 | GRLIB_PNP_ADDR_SHIFT, |
121 | GRLIB_PNP_ADDR_SIZE, |
122 | extract32(address, |
123 | GRLIB_AHB_DEV_ADDR_SHIFT, |
124 | GRLIB_AHB_DEV_ADDR_SIZE)); |
125 | dev->regs[reg_start] = deposit32(dev->regs[reg_start], |
126 | GRLIB_PNP_MASK_SHIFT, |
127 | GRLIB_PNP_MASK_SIZE, |
128 | mask); |
129 | } |
130 | |
131 | static uint64_t grlib_ahb_pnp_read(void *opaque, hwaddr offset, unsigned size) |
132 | { |
133 | AHBPnp *ahb_pnp = GRLIB_AHB_PNP(opaque); |
134 | |
135 | return ahb_pnp->regs[offset >> 2]; |
136 | } |
137 | |
138 | static const MemoryRegionOps grlib_ahb_pnp_ops = { |
139 | .read = grlib_ahb_pnp_read, |
140 | .endianness = DEVICE_BIG_ENDIAN, |
141 | }; |
142 | |
143 | static void grlib_ahb_pnp_realize(DeviceState *dev, Error **errp) |
144 | { |
145 | AHBPnp *ahb_pnp = GRLIB_AHB_PNP(dev); |
146 | SysBusDevice *sbd = SYS_BUS_DEVICE(dev); |
147 | |
148 | memory_region_init_io(&ahb_pnp->iomem, OBJECT(dev), &grlib_ahb_pnp_ops, |
149 | ahb_pnp, TYPE_GRLIB_AHB_PNP, GRLIB_PNP_MAX_REGS); |
150 | sysbus_init_mmio(sbd, &ahb_pnp->iomem); |
151 | } |
152 | |
153 | static void grlib_ahb_pnp_class_init(ObjectClass *klass, void *data) |
154 | { |
155 | DeviceClass *dc = DEVICE_CLASS(klass); |
156 | |
157 | dc->realize = grlib_ahb_pnp_realize; |
158 | } |
159 | |
160 | static const TypeInfo grlib_ahb_pnp_info = { |
161 | .name = TYPE_GRLIB_AHB_PNP, |
162 | .parent = TYPE_SYS_BUS_DEVICE, |
163 | .instance_size = sizeof(AHBPnp), |
164 | .class_init = grlib_ahb_pnp_class_init, |
165 | }; |
166 | |
167 | /* APBPnp */ |
168 | |
169 | typedef struct APBPnp { |
170 | SysBusDevice parent_obj; |
171 | MemoryRegion iomem; |
172 | |
173 | uint32_t regs[GRLIB_PNP_MAX_REGS >> 2]; |
174 | uint32_t entry_count; |
175 | } APBPnp; |
176 | |
177 | void grlib_apb_pnp_add_entry(APBPnp *dev, uint32_t address, uint32_t mask, |
178 | uint8_t vendor, uint16_t device, uint8_t version, |
179 | uint8_t irq, int type) |
180 | { |
181 | unsigned int reg_start; |
182 | |
183 | /* |
184 | * APB entries look like this: |
185 | * |
186 | * 31 -------- 23 -------- 11 ----- 9 ------- 4 --- 0 |
187 | * | VENDOR ID | DEVICE ID | IRQ ? | VERSION | IRQ | |
188 | * |
189 | * 31 ---------- 20 --- 15 ----------------- 3 ---- 0 |
190 | * | ADDR[20..8] | 0000 | MASK | TYPE | |
191 | */ |
192 | |
193 | assert(dev->entry_count < GRLIB_APB_MAX_DEV); |
194 | reg_start = (dev->entry_count * GRLIB_APB_ENTRY_SIZE) >> 2; |
195 | dev->entry_count++; |
196 | |
197 | dev->regs[reg_start] = deposit32(dev->regs[reg_start], |
198 | GRLIB_PNP_VENDOR_SHIFT, |
199 | GRLIB_PNP_VENDOR_SIZE, |
200 | vendor); |
201 | dev->regs[reg_start] = deposit32(dev->regs[reg_start], |
202 | GRLIB_PNP_DEV_SHIFT, |
203 | GRLIB_PNP_DEV_SIZE, |
204 | device); |
205 | dev->regs[reg_start] = deposit32(dev->regs[reg_start], |
206 | GRLIB_PNP_VER_SHIFT, |
207 | GRLIB_PNP_VER_SIZE, |
208 | version); |
209 | dev->regs[reg_start] = deposit32(dev->regs[reg_start], |
210 | GRLIB_PNP_IRQ_SHIFT, |
211 | GRLIB_PNP_IRQ_SIZE, |
212 | irq); |
213 | reg_start += 1; |
214 | dev->regs[reg_start] = type; |
215 | dev->regs[reg_start] = deposit32(dev->regs[reg_start], |
216 | GRLIB_PNP_ADDR_SHIFT, |
217 | GRLIB_PNP_ADDR_SIZE, |
218 | extract32(address, |
219 | GRLIB_APB_DEV_ADDR_SHIFT, |
220 | GRLIB_APB_DEV_ADDR_SIZE)); |
221 | dev->regs[reg_start] = deposit32(dev->regs[reg_start], |
222 | GRLIB_PNP_MASK_SHIFT, |
223 | GRLIB_PNP_MASK_SIZE, |
224 | mask); |
225 | } |
226 | |
227 | static uint64_t grlib_apb_pnp_read(void *opaque, hwaddr offset, unsigned size) |
228 | { |
229 | APBPnp *apb_pnp = GRLIB_APB_PNP(opaque); |
230 | |
231 | return apb_pnp->regs[offset >> 2]; |
232 | } |
233 | |
234 | static const MemoryRegionOps grlib_apb_pnp_ops = { |
235 | .read = grlib_apb_pnp_read, |
236 | .endianness = DEVICE_BIG_ENDIAN, |
237 | }; |
238 | |
239 | static void grlib_apb_pnp_realize(DeviceState *dev, Error **errp) |
240 | { |
241 | APBPnp *apb_pnp = GRLIB_APB_PNP(dev); |
242 | SysBusDevice *sbd = SYS_BUS_DEVICE(dev); |
243 | |
244 | memory_region_init_io(&apb_pnp->iomem, OBJECT(dev), &grlib_apb_pnp_ops, |
245 | apb_pnp, TYPE_GRLIB_APB_PNP, GRLIB_PNP_MAX_REGS); |
246 | sysbus_init_mmio(sbd, &apb_pnp->iomem); |
247 | } |
248 | |
249 | static void grlib_apb_pnp_class_init(ObjectClass *klass, void *data) |
250 | { |
251 | DeviceClass *dc = DEVICE_CLASS(klass); |
252 | |
253 | dc->realize = grlib_apb_pnp_realize; |
254 | } |
255 | |
256 | static const TypeInfo grlib_apb_pnp_info = { |
257 | .name = TYPE_GRLIB_APB_PNP, |
258 | .parent = TYPE_SYS_BUS_DEVICE, |
259 | .instance_size = sizeof(APBPnp), |
260 | .class_init = grlib_apb_pnp_class_init, |
261 | }; |
262 | |
263 | static void grlib_ahb_apb_pnp_register_types(void) |
264 | { |
265 | type_register_static(&grlib_ahb_pnp_info); |
266 | type_register_static(&grlib_apb_pnp_info); |
267 | } |
268 | |
269 | type_init(grlib_ahb_apb_pnp_register_types) |
270 | |