root / hw / tpci200.c @ 3e3648b2
History | View | Annotate | Download (18.7 kB)
1 |
/*
|
---|---|
2 |
* QEMU TEWS TPCI200 IndustryPack carrier emulation
|
3 |
*
|
4 |
* Copyright (C) 2012 Igalia, S.L.
|
5 |
* Author: Alberto Garcia <agarcia@igalia.com>
|
6 |
*
|
7 |
* This code is licensed under the GNU GPL v2 or (at your option) any
|
8 |
* later version.
|
9 |
*/
|
10 |
|
11 |
#include "ipack.h" |
12 |
#include "pci/pci.h" |
13 |
#include "qemu/bitops.h" |
14 |
#include <stdio.h> |
15 |
|
16 |
/* #define DEBUG_TPCI */
|
17 |
|
18 |
#ifdef DEBUG_TPCI
|
19 |
#define DPRINTF(fmt, ...) \
|
20 |
do { fprintf(stderr, "TPCI200: " fmt, ## __VA_ARGS__); } while (0) |
21 |
#else
|
22 |
#define DPRINTF(fmt, ...) do { } while (0) |
23 |
#endif
|
24 |
|
25 |
#define N_MODULES 4 |
26 |
|
27 |
#define IP_ID_SPACE 2 |
28 |
#define IP_INT_SPACE 3 |
29 |
#define IP_IO_SPACE_ADDR_MASK 0x7F |
30 |
#define IP_ID_SPACE_ADDR_MASK 0x3F |
31 |
#define IP_INT_SPACE_ADDR_MASK 0x3F |
32 |
|
33 |
#define STATUS_INT(IP, INTNO) BIT((IP) * 2 + (INTNO)) |
34 |
#define STATUS_TIME(IP) BIT((IP) + 12) |
35 |
#define STATUS_ERR_ANY 0xF00 |
36 |
|
37 |
#define CTRL_CLKRATE BIT(0) |
38 |
#define CTRL_RECOVER BIT(1) |
39 |
#define CTRL_TIME_INT BIT(2) |
40 |
#define CTRL_ERR_INT BIT(3) |
41 |
#define CTRL_INT_EDGE(INTNO) BIT(4 + (INTNO)) |
42 |
#define CTRL_INT(INTNO) BIT(6 + (INTNO)) |
43 |
|
44 |
#define REG_REV_ID 0x00 |
45 |
#define REG_IP_A_CTRL 0x02 |
46 |
#define REG_IP_B_CTRL 0x04 |
47 |
#define REG_IP_C_CTRL 0x06 |
48 |
#define REG_IP_D_CTRL 0x08 |
49 |
#define REG_RESET 0x0A |
50 |
#define REG_STATUS 0x0C |
51 |
#define IP_N_FROM_REG(REG) ((REG) / 2 - 1) |
52 |
|
53 |
typedef struct { |
54 |
PCIDevice dev; |
55 |
IPackBus bus; |
56 |
MemoryRegion mmio; |
57 |
MemoryRegion io; |
58 |
MemoryRegion las0; |
59 |
MemoryRegion las1; |
60 |
MemoryRegion las2; |
61 |
MemoryRegion las3; |
62 |
bool big_endian[3]; |
63 |
uint8_t ctrl[N_MODULES]; |
64 |
uint16_t status; |
65 |
uint8_t int_set; |
66 |
} TPCI200State; |
67 |
|
68 |
#define TYPE_TPCI200 "tpci200" |
69 |
|
70 |
#define TPCI200(obj) \
|
71 |
OBJECT_CHECK(TPCI200State, (obj), TYPE_TPCI200) |
72 |
|
73 |
static const uint8_t local_config_regs[] = { |
74 |
0x00, 0xFF, 0xFF, 0x0F, 0x00, 0xFC, 0xFF, 0x0F, 0x00, 0x00, 0x00, |
75 |
0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, |
76 |
0x00, 0x08, 0x01, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x01, |
77 |
0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x60, 0x41, 0xD4, |
78 |
0xA2, 0x20, 0x41, 0x14, 0xA2, 0x20, 0x41, 0x14, 0xA2, 0x20, 0x01, |
79 |
0x14, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x08, 0x01, 0x02, |
80 |
0x00, 0x04, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x80, 0x02, 0x41, |
81 |
0x00, 0x00, 0x00, 0x00, 0x40, 0x7A, 0x00, 0x52, 0x92, 0x24, 0x02 |
82 |
}; |
83 |
|
84 |
static void adjust_addr(bool big_endian, hwaddr *addr, unsigned size) |
85 |
{ |
86 |
/* During 8 bit access in big endian mode,
|
87 |
odd and even addresses are swapped */
|
88 |
if (big_endian && size == 1) { |
89 |
*addr ^= 1;
|
90 |
} |
91 |
} |
92 |
|
93 |
static uint64_t adjust_value(bool big_endian, uint64_t *val, unsigned size) |
94 |
{ |
95 |
/* Local spaces only support 8/16 bit access,
|
96 |
* so there's no need to care for sizes > 2 */
|
97 |
if (big_endian && size == 2) { |
98 |
*val = bswap16(*val); |
99 |
} |
100 |
return *val;
|
101 |
} |
102 |
|
103 |
static void tpci200_set_irq(void *opaque, int intno, int level) |
104 |
{ |
105 |
IPackDevice *ip = opaque; |
106 |
IPackBus *bus = IPACK_BUS(qdev_get_parent_bus(DEVICE(ip))); |
107 |
PCIDevice *pcidev = PCI_DEVICE(BUS(bus)->parent); |
108 |
TPCI200State *dev = TPCI200(pcidev); |
109 |
unsigned ip_n = ip->slot;
|
110 |
uint16_t prev_status = dev->status; |
111 |
|
112 |
assert(ip->slot >= 0 && ip->slot < N_MODULES);
|
113 |
|
114 |
/* The requested interrupt must be enabled in the IP CONTROL
|
115 |
* register */
|
116 |
if (!(dev->ctrl[ip_n] & CTRL_INT(intno))) {
|
117 |
return;
|
118 |
} |
119 |
|
120 |
/* Update the interrupt status in the IP STATUS register */
|
121 |
if (level) {
|
122 |
dev->status |= STATUS_INT(ip_n, intno); |
123 |
} else {
|
124 |
dev->status &= ~STATUS_INT(ip_n, intno); |
125 |
} |
126 |
|
127 |
/* Return if there are no changes */
|
128 |
if (dev->status == prev_status) {
|
129 |
return;
|
130 |
} |
131 |
|
132 |
DPRINTF("IP %u INT%u#: %u\n", ip_n, intno, level);
|
133 |
|
134 |
/* Check if the interrupt is edge sensitive */
|
135 |
if (dev->ctrl[ip_n] & CTRL_INT_EDGE(intno)) {
|
136 |
if (level) {
|
137 |
qemu_set_irq(dev->dev.irq[0], !dev->int_set);
|
138 |
qemu_set_irq(dev->dev.irq[0], dev->int_set);
|
139 |
} |
140 |
} else {
|
141 |
unsigned i, j;
|
142 |
uint16_t level_status = dev->status; |
143 |
|
144 |
/* Check if there are any level sensitive interrupts set by
|
145 |
removing the ones that are edge sensitive from the status
|
146 |
register */
|
147 |
for (i = 0; i < N_MODULES; i++) { |
148 |
for (j = 0; j < 2; j++) { |
149 |
if (dev->ctrl[i] & CTRL_INT_EDGE(j)) {
|
150 |
level_status &= ~STATUS_INT(i, j); |
151 |
} |
152 |
} |
153 |
} |
154 |
|
155 |
if (level_status && !dev->int_set) {
|
156 |
qemu_irq_raise(dev->dev.irq[0]);
|
157 |
dev->int_set = 1;
|
158 |
} else if (!level_status && dev->int_set) { |
159 |
qemu_irq_lower(dev->dev.irq[0]);
|
160 |
dev->int_set = 0;
|
161 |
} |
162 |
} |
163 |
} |
164 |
|
165 |
static uint64_t tpci200_read_cfg(void *opaque, hwaddr addr, unsigned size) |
166 |
{ |
167 |
TPCI200State *s = opaque; |
168 |
uint8_t ret = 0;
|
169 |
if (addr < ARRAY_SIZE(local_config_regs)) {
|
170 |
ret = local_config_regs[addr]; |
171 |
} |
172 |
/* Endianness is stored in the first bit of these registers */
|
173 |
if ((addr == 0x2b && s->big_endian[0]) || |
174 |
(addr == 0x2f && s->big_endian[1]) || |
175 |
(addr == 0x33 && s->big_endian[2])) { |
176 |
ret |= 1;
|
177 |
} |
178 |
DPRINTF("Read from LCR 0x%x: 0x%x\n", (unsigned) addr, (unsigned) ret); |
179 |
return ret;
|
180 |
} |
181 |
|
182 |
static void tpci200_write_cfg(void *opaque, hwaddr addr, uint64_t val, |
183 |
unsigned size)
|
184 |
{ |
185 |
TPCI200State *s = opaque; |
186 |
/* Endianness is stored in the first bit of these registers */
|
187 |
if (addr == 0x2b || addr == 0x2f || addr == 0x33) { |
188 |
unsigned las = (addr - 0x2b) / 4; |
189 |
s->big_endian[las] = val & 1;
|
190 |
DPRINTF("LAS%u big endian mode: %u\n", las, (unsigned) val & 1); |
191 |
} else {
|
192 |
DPRINTF("Write to LCR 0x%x: 0x%x\n", (unsigned) addr, (unsigned) val); |
193 |
} |
194 |
} |
195 |
|
196 |
static uint64_t tpci200_read_las0(void *opaque, hwaddr addr, unsigned size) |
197 |
{ |
198 |
TPCI200State *s = opaque; |
199 |
uint64_t ret = 0;
|
200 |
|
201 |
switch (addr) {
|
202 |
|
203 |
case REG_REV_ID:
|
204 |
DPRINTF("Read REVISION ID\n"); /* Current value is 0x00 */ |
205 |
break;
|
206 |
|
207 |
case REG_IP_A_CTRL:
|
208 |
case REG_IP_B_CTRL:
|
209 |
case REG_IP_C_CTRL:
|
210 |
case REG_IP_D_CTRL:
|
211 |
{ |
212 |
unsigned ip_n = IP_N_FROM_REG(addr);
|
213 |
ret = s->ctrl[ip_n]; |
214 |
DPRINTF("Read IP %c CONTROL: 0x%x\n", 'A' + ip_n, (unsigned) ret); |
215 |
} |
216 |
break;
|
217 |
|
218 |
case REG_RESET:
|
219 |
DPRINTF("Read RESET\n"); /* Not implemented */ |
220 |
break;
|
221 |
|
222 |
case REG_STATUS:
|
223 |
ret = s->status; |
224 |
DPRINTF("Read STATUS: 0x%x\n", (unsigned) ret); |
225 |
break;
|
226 |
|
227 |
/* Reserved */
|
228 |
default:
|
229 |
DPRINTF("Unsupported read from LAS0 0x%x\n", (unsigned) addr); |
230 |
break;
|
231 |
} |
232 |
|
233 |
return adjust_value(s->big_endian[0], &ret, size); |
234 |
} |
235 |
|
236 |
static void tpci200_write_las0(void *opaque, hwaddr addr, uint64_t val, |
237 |
unsigned size)
|
238 |
{ |
239 |
TPCI200State *s = opaque; |
240 |
|
241 |
adjust_value(s->big_endian[0], &val, size);
|
242 |
|
243 |
switch (addr) {
|
244 |
|
245 |
case REG_REV_ID:
|
246 |
DPRINTF("Write Revision ID: 0x%x\n", (unsigned) val); /* No effect */ |
247 |
break;
|
248 |
|
249 |
case REG_IP_A_CTRL:
|
250 |
case REG_IP_B_CTRL:
|
251 |
case REG_IP_C_CTRL:
|
252 |
case REG_IP_D_CTRL:
|
253 |
{ |
254 |
unsigned ip_n = IP_N_FROM_REG(addr);
|
255 |
s->ctrl[ip_n] = val; |
256 |
DPRINTF("Write IP %c CONTROL: 0x%x\n", 'A' + ip_n, (unsigned) val); |
257 |
} |
258 |
break;
|
259 |
|
260 |
case REG_RESET:
|
261 |
DPRINTF("Write RESET: 0x%x\n", (unsigned) val); /* Not implemented */ |
262 |
break;
|
263 |
|
264 |
case REG_STATUS:
|
265 |
{ |
266 |
unsigned i;
|
267 |
|
268 |
for (i = 0; i < N_MODULES; i++) { |
269 |
IPackDevice *ip = ipack_device_find(&s->bus, i); |
270 |
|
271 |
if (ip != NULL) { |
272 |
if (val & STATUS_INT(i, 0)) { |
273 |
DPRINTF("Clear IP %c INT0# status\n", 'A' + i); |
274 |
qemu_irq_lower(ip->irq[0]);
|
275 |
} |
276 |
if (val & STATUS_INT(i, 1)) { |
277 |
DPRINTF("Clear IP %c INT1# status\n", 'A' + i); |
278 |
qemu_irq_lower(ip->irq[1]);
|
279 |
} |
280 |
} |
281 |
|
282 |
if (val & STATUS_TIME(i)) {
|
283 |
DPRINTF("Clear IP %c timeout\n", 'A' + i); |
284 |
s->status &= ~STATUS_TIME(i); |
285 |
} |
286 |
} |
287 |
|
288 |
if (val & STATUS_ERR_ANY) {
|
289 |
DPRINTF("Unexpected write to STATUS register: 0x%x\n",
|
290 |
(unsigned) val);
|
291 |
} |
292 |
} |
293 |
break;
|
294 |
|
295 |
/* Reserved */
|
296 |
default:
|
297 |
DPRINTF("Unsupported write to LAS0 0x%x: 0x%x\n",
|
298 |
(unsigned) addr, (unsigned) val); |
299 |
break;
|
300 |
} |
301 |
} |
302 |
|
303 |
static uint64_t tpci200_read_las1(void *opaque, hwaddr addr, unsigned size) |
304 |
{ |
305 |
TPCI200State *s = opaque; |
306 |
IPackDevice *ip; |
307 |
uint64_t ret = 0;
|
308 |
unsigned ip_n, space;
|
309 |
uint8_t offset; |
310 |
|
311 |
adjust_addr(s->big_endian[1], &addr, size);
|
312 |
|
313 |
/*
|
314 |
* The address is divided into the IP module number (0-4), the IP
|
315 |
* address space (I/O, ID, INT) and the offset within that space.
|
316 |
*/
|
317 |
ip_n = addr >> 8;
|
318 |
space = (addr >> 6) & 3; |
319 |
ip = ipack_device_find(&s->bus, ip_n); |
320 |
|
321 |
if (ip == NULL) { |
322 |
DPRINTF("Read LAS1: IP module %u not installed\n", ip_n);
|
323 |
} else {
|
324 |
IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); |
325 |
switch (space) {
|
326 |
|
327 |
case IP_ID_SPACE:
|
328 |
offset = addr & IP_ID_SPACE_ADDR_MASK; |
329 |
if (k->id_read) {
|
330 |
ret = k->id_read(ip, offset); |
331 |
} |
332 |
break;
|
333 |
|
334 |
case IP_INT_SPACE:
|
335 |
offset = addr & IP_INT_SPACE_ADDR_MASK; |
336 |
|
337 |
/* Read address 0 to ACK IP INT0# and address 2 to ACK IP INT1# */
|
338 |
if (offset == 0 || offset == 2) { |
339 |
unsigned intno = offset / 2; |
340 |
bool int_set = s->status & STATUS_INT(ip_n, intno);
|
341 |
bool int_edge_sensitive = s->ctrl[ip_n] & CTRL_INT_EDGE(intno);
|
342 |
if (int_set && !int_edge_sensitive) {
|
343 |
qemu_irq_lower(ip->irq[intno]); |
344 |
} |
345 |
} |
346 |
|
347 |
if (k->int_read) {
|
348 |
ret = k->int_read(ip, offset); |
349 |
} |
350 |
break;
|
351 |
|
352 |
default:
|
353 |
offset = addr & IP_IO_SPACE_ADDR_MASK; |
354 |
if (k->io_read) {
|
355 |
ret = k->io_read(ip, offset); |
356 |
} |
357 |
break;
|
358 |
} |
359 |
} |
360 |
|
361 |
return adjust_value(s->big_endian[1], &ret, size); |
362 |
} |
363 |
|
364 |
static void tpci200_write_las1(void *opaque, hwaddr addr, uint64_t val, |
365 |
unsigned size)
|
366 |
{ |
367 |
TPCI200State *s = opaque; |
368 |
IPackDevice *ip; |
369 |
unsigned ip_n, space;
|
370 |
uint8_t offset; |
371 |
|
372 |
adjust_addr(s->big_endian[1], &addr, size);
|
373 |
adjust_value(s->big_endian[1], &val, size);
|
374 |
|
375 |
/*
|
376 |
* The address is divided into the IP module number, the IP
|
377 |
* address space (I/O, ID, INT) and the offset within that space.
|
378 |
*/
|
379 |
ip_n = addr >> 8;
|
380 |
space = (addr >> 6) & 3; |
381 |
ip = ipack_device_find(&s->bus, ip_n); |
382 |
|
383 |
if (ip == NULL) { |
384 |
DPRINTF("Write LAS1: IP module %u not installed\n", ip_n);
|
385 |
} else {
|
386 |
IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); |
387 |
switch (space) {
|
388 |
|
389 |
case IP_ID_SPACE:
|
390 |
offset = addr & IP_ID_SPACE_ADDR_MASK; |
391 |
if (k->id_write) {
|
392 |
k->id_write(ip, offset, val); |
393 |
} |
394 |
break;
|
395 |
|
396 |
case IP_INT_SPACE:
|
397 |
offset = addr & IP_INT_SPACE_ADDR_MASK; |
398 |
if (k->int_write) {
|
399 |
k->int_write(ip, offset, val); |
400 |
} |
401 |
break;
|
402 |
|
403 |
default:
|
404 |
offset = addr & IP_IO_SPACE_ADDR_MASK; |
405 |
if (k->io_write) {
|
406 |
k->io_write(ip, offset, val); |
407 |
} |
408 |
break;
|
409 |
} |
410 |
} |
411 |
} |
412 |
|
413 |
static uint64_t tpci200_read_las2(void *opaque, hwaddr addr, unsigned size) |
414 |
{ |
415 |
TPCI200State *s = opaque; |
416 |
IPackDevice *ip; |
417 |
uint64_t ret = 0;
|
418 |
unsigned ip_n;
|
419 |
uint32_t offset; |
420 |
|
421 |
adjust_addr(s->big_endian[2], &addr, size);
|
422 |
|
423 |
/*
|
424 |
* The address is divided into the IP module number and the offset
|
425 |
* within the IP module MEM space.
|
426 |
*/
|
427 |
ip_n = addr >> 23;
|
428 |
offset = addr & 0x7fffff;
|
429 |
ip = ipack_device_find(&s->bus, ip_n); |
430 |
|
431 |
if (ip == NULL) { |
432 |
DPRINTF("Read LAS2: IP module %u not installed\n", ip_n);
|
433 |
} else {
|
434 |
IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); |
435 |
if (k->mem_read16) {
|
436 |
ret = k->mem_read16(ip, offset); |
437 |
} |
438 |
} |
439 |
|
440 |
return adjust_value(s->big_endian[2], &ret, size); |
441 |
} |
442 |
|
443 |
static void tpci200_write_las2(void *opaque, hwaddr addr, uint64_t val, |
444 |
unsigned size)
|
445 |
{ |
446 |
TPCI200State *s = opaque; |
447 |
IPackDevice *ip; |
448 |
unsigned ip_n;
|
449 |
uint32_t offset; |
450 |
|
451 |
adjust_addr(s->big_endian[2], &addr, size);
|
452 |
adjust_value(s->big_endian[2], &val, size);
|
453 |
|
454 |
/*
|
455 |
* The address is divided into the IP module number and the offset
|
456 |
* within the IP module MEM space.
|
457 |
*/
|
458 |
ip_n = addr >> 23;
|
459 |
offset = addr & 0x7fffff;
|
460 |
ip = ipack_device_find(&s->bus, ip_n); |
461 |
|
462 |
if (ip == NULL) { |
463 |
DPRINTF("Write LAS2: IP module %u not installed\n", ip_n);
|
464 |
} else {
|
465 |
IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); |
466 |
if (k->mem_write16) {
|
467 |
k->mem_write16(ip, offset, val); |
468 |
} |
469 |
} |
470 |
} |
471 |
|
472 |
static uint64_t tpci200_read_las3(void *opaque, hwaddr addr, unsigned size) |
473 |
{ |
474 |
TPCI200State *s = opaque; |
475 |
IPackDevice *ip; |
476 |
uint64_t ret = 0;
|
477 |
/*
|
478 |
* The address is divided into the IP module number and the offset
|
479 |
* within the IP module MEM space.
|
480 |
*/
|
481 |
unsigned ip_n = addr >> 22; |
482 |
uint32_t offset = addr & 0x3fffff;
|
483 |
|
484 |
ip = ipack_device_find(&s->bus, ip_n); |
485 |
|
486 |
if (ip == NULL) { |
487 |
DPRINTF("Read LAS3: IP module %u not installed\n", ip_n);
|
488 |
} else {
|
489 |
IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); |
490 |
if (k->mem_read8) {
|
491 |
ret = k->mem_read8(ip, offset); |
492 |
} |
493 |
} |
494 |
|
495 |
return ret;
|
496 |
} |
497 |
|
498 |
static void tpci200_write_las3(void *opaque, hwaddr addr, uint64_t val, |
499 |
unsigned size)
|
500 |
{ |
501 |
TPCI200State *s = opaque; |
502 |
IPackDevice *ip; |
503 |
/*
|
504 |
* The address is divided into the IP module number and the offset
|
505 |
* within the IP module MEM space.
|
506 |
*/
|
507 |
unsigned ip_n = addr >> 22; |
508 |
uint32_t offset = addr & 0x3fffff;
|
509 |
|
510 |
ip = ipack_device_find(&s->bus, ip_n); |
511 |
|
512 |
if (ip == NULL) { |
513 |
DPRINTF("Write LAS3: IP module %u not installed\n", ip_n);
|
514 |
} else {
|
515 |
IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); |
516 |
if (k->mem_write8) {
|
517 |
k->mem_write8(ip, offset, val); |
518 |
} |
519 |
} |
520 |
} |
521 |
|
522 |
static const MemoryRegionOps tpci200_cfg_ops = { |
523 |
.read = tpci200_read_cfg, |
524 |
.write = tpci200_write_cfg, |
525 |
.endianness = DEVICE_NATIVE_ENDIAN, |
526 |
.valid = { |
527 |
.min_access_size = 1,
|
528 |
.max_access_size = 4
|
529 |
}, |
530 |
.impl = { |
531 |
.min_access_size = 1,
|
532 |
.max_access_size = 1
|
533 |
} |
534 |
}; |
535 |
|
536 |
static const MemoryRegionOps tpci200_las0_ops = { |
537 |
.read = tpci200_read_las0, |
538 |
.write = tpci200_write_las0, |
539 |
.endianness = DEVICE_NATIVE_ENDIAN, |
540 |
.valid = { |
541 |
.min_access_size = 2,
|
542 |
.max_access_size = 2
|
543 |
} |
544 |
}; |
545 |
|
546 |
static const MemoryRegionOps tpci200_las1_ops = { |
547 |
.read = tpci200_read_las1, |
548 |
.write = tpci200_write_las1, |
549 |
.endianness = DEVICE_NATIVE_ENDIAN, |
550 |
.valid = { |
551 |
.min_access_size = 1,
|
552 |
.max_access_size = 2
|
553 |
} |
554 |
}; |
555 |
|
556 |
static const MemoryRegionOps tpci200_las2_ops = { |
557 |
.read = tpci200_read_las2, |
558 |
.write = tpci200_write_las2, |
559 |
.endianness = DEVICE_NATIVE_ENDIAN, |
560 |
.valid = { |
561 |
.min_access_size = 1,
|
562 |
.max_access_size = 2
|
563 |
} |
564 |
}; |
565 |
|
566 |
static const MemoryRegionOps tpci200_las3_ops = { |
567 |
.read = tpci200_read_las3, |
568 |
.write = tpci200_write_las3, |
569 |
.endianness = DEVICE_NATIVE_ENDIAN, |
570 |
.valid = { |
571 |
.min_access_size = 1,
|
572 |
.max_access_size = 1
|
573 |
} |
574 |
}; |
575 |
|
576 |
static int tpci200_initfn(PCIDevice *pci_dev) |
577 |
{ |
578 |
TPCI200State *s = TPCI200(pci_dev); |
579 |
uint8_t *c = s->dev.config; |
580 |
|
581 |
pci_set_word(c + PCI_COMMAND, 0x0003);
|
582 |
pci_set_word(c + PCI_STATUS, 0x0280);
|
583 |
|
584 |
pci_set_byte(c + PCI_INTERRUPT_PIN, 0x01); /* Interrupt pin A */ |
585 |
|
586 |
pci_set_byte(c + PCI_CAPABILITY_LIST, 0x40);
|
587 |
pci_set_long(c + 0x40, 0x48014801); |
588 |
pci_set_long(c + 0x48, 0x00024C06); |
589 |
pci_set_long(c + 0x4C, 0x00000003); |
590 |
|
591 |
memory_region_init_io(&s->mmio, &tpci200_cfg_ops, |
592 |
s, "tpci200_mmio", 128); |
593 |
memory_region_init_io(&s->io, &tpci200_cfg_ops, |
594 |
s, "tpci200_io", 128); |
595 |
memory_region_init_io(&s->las0, &tpci200_las0_ops, |
596 |
s, "tpci200_las0", 256); |
597 |
memory_region_init_io(&s->las1, &tpci200_las1_ops, |
598 |
s, "tpci200_las1", 1024); |
599 |
memory_region_init_io(&s->las2, &tpci200_las2_ops, |
600 |
s, "tpci200_las2", 1024*1024*32); |
601 |
memory_region_init_io(&s->las3, &tpci200_las3_ops, |
602 |
s, "tpci200_las3", 1024*1024*16); |
603 |
pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio);
|
604 |
pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io);
|
605 |
pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las0);
|
606 |
pci_register_bar(&s->dev, 3, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las1);
|
607 |
pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las2);
|
608 |
pci_register_bar(&s->dev, 5, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las3);
|
609 |
|
610 |
ipack_bus_new_inplace(&s->bus, DEVICE(&s->dev), NULL,
|
611 |
N_MODULES, tpci200_set_irq); |
612 |
|
613 |
return 0; |
614 |
} |
615 |
|
616 |
static void tpci200_exitfn(PCIDevice *pci_dev) |
617 |
{ |
618 |
TPCI200State *s = TPCI200(pci_dev); |
619 |
|
620 |
memory_region_destroy(&s->mmio); |
621 |
memory_region_destroy(&s->io); |
622 |
memory_region_destroy(&s->las0); |
623 |
memory_region_destroy(&s->las1); |
624 |
memory_region_destroy(&s->las2); |
625 |
memory_region_destroy(&s->las3); |
626 |
} |
627 |
|
628 |
static const VMStateDescription vmstate_tpci200 = { |
629 |
.name = "tpci200",
|
630 |
.version_id = 1,
|
631 |
.minimum_version_id = 1,
|
632 |
.minimum_version_id_old = 1,
|
633 |
.fields = (VMStateField[]) { |
634 |
VMSTATE_PCI_DEVICE(dev, TPCI200State), |
635 |
VMSTATE_BOOL_ARRAY(big_endian, TPCI200State, 3),
|
636 |
VMSTATE_UINT8_ARRAY(ctrl, TPCI200State, N_MODULES), |
637 |
VMSTATE_UINT16(status, TPCI200State), |
638 |
VMSTATE_UINT8(int_set, TPCI200State), |
639 |
VMSTATE_END_OF_LIST() |
640 |
} |
641 |
}; |
642 |
|
643 |
static void tpci200_class_init(ObjectClass *klass, void *data) |
644 |
{ |
645 |
DeviceClass *dc = DEVICE_CLASS(klass); |
646 |
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); |
647 |
|
648 |
k->init = tpci200_initfn; |
649 |
k->exit = tpci200_exitfn; |
650 |
k->vendor_id = PCI_VENDOR_ID_TEWS; |
651 |
k->device_id = PCI_DEVICE_ID_TEWS_TPCI200; |
652 |
k->class_id = PCI_CLASS_BRIDGE_OTHER; |
653 |
k->subsystem_vendor_id = PCI_VENDOR_ID_TEWS; |
654 |
k->subsystem_id = 0x300A;
|
655 |
dc->desc = "TEWS TPCI200 IndustryPack carrier";
|
656 |
dc->vmsd = &vmstate_tpci200; |
657 |
} |
658 |
|
659 |
static const TypeInfo tpci200_info = { |
660 |
.name = TYPE_TPCI200, |
661 |
.parent = TYPE_PCI_DEVICE, |
662 |
.instance_size = sizeof(TPCI200State),
|
663 |
.class_init = tpci200_class_init, |
664 |
}; |
665 |
|
666 |
static void tpci200_register_types(void) |
667 |
{ |
668 |
type_register_static(&tpci200_info); |
669 |
} |
670 |
|
671 |
type_init(tpci200_register_types) |