19c16fa79SAlberto Garcia /* 29c16fa79SAlberto Garcia * QEMU TEWS TPCI200 IndustryPack carrier emulation 39c16fa79SAlberto Garcia * 49c16fa79SAlberto Garcia * Copyright (C) 2012 Igalia, S.L. 5b996aed5SAlberto Garcia * Author: Alberto Garcia <berto@igalia.com> 69c16fa79SAlberto Garcia * 79c16fa79SAlberto Garcia * This code is licensed under the GNU GPL v2 or (at your option) any 89c16fa79SAlberto Garcia * later version. 99c16fa79SAlberto Garcia */ 109c16fa79SAlberto Garcia 110430891cSPeter Maydell #include "qemu/osdep.h" 12a7174d70SPhilippe Mathieu-Daudé #include "qemu/units.h" 131f9c4cfdSAndreas Färber #include "hw/ipack/ipack.h" 1464552b6bSMarkus Armbruster #include "hw/irq.h" 1583c9f4caSPaolo Bonzini #include "hw/pci/pci.h" 16d6454270SMarkus Armbruster #include "migration/vmstate.h" 179c16fa79SAlberto Garcia #include "qemu/bitops.h" 180b8fa32fSMarkus Armbruster #include "qemu/module.h" 19*db1015e9SEduardo Habkost #include "qom/object.h" 209c16fa79SAlberto Garcia 219c16fa79SAlberto Garcia /* #define DEBUG_TPCI */ 229c16fa79SAlberto Garcia 239c16fa79SAlberto Garcia #ifdef DEBUG_TPCI 249c16fa79SAlberto Garcia #define DPRINTF(fmt, ...) \ 259c16fa79SAlberto Garcia do { fprintf(stderr, "TPCI200: " fmt, ## __VA_ARGS__); } while (0) 269c16fa79SAlberto Garcia #else 279c16fa79SAlberto Garcia #define DPRINTF(fmt, ...) do { } while (0) 289c16fa79SAlberto Garcia #endif 299c16fa79SAlberto Garcia 309c16fa79SAlberto Garcia #define N_MODULES 4 319c16fa79SAlberto Garcia 329c16fa79SAlberto Garcia #define IP_ID_SPACE 2 339c16fa79SAlberto Garcia #define IP_INT_SPACE 3 349c16fa79SAlberto Garcia #define IP_IO_SPACE_ADDR_MASK 0x7F 359c16fa79SAlberto Garcia #define IP_ID_SPACE_ADDR_MASK 0x3F 369c16fa79SAlberto Garcia #define IP_INT_SPACE_ADDR_MASK 0x3F 379c16fa79SAlberto Garcia 389c16fa79SAlberto Garcia #define STATUS_INT(IP, INTNO) BIT((IP) * 2 + (INTNO)) 3909a021fbSStefan Weil #define STATUS_TIME(IP) BIT((IP) + 12) 409c16fa79SAlberto Garcia #define STATUS_ERR_ANY 0xF00 419c16fa79SAlberto Garcia 429c16fa79SAlberto Garcia #define CTRL_CLKRATE BIT(0) 439c16fa79SAlberto Garcia #define CTRL_RECOVER BIT(1) 449c16fa79SAlberto Garcia #define CTRL_TIME_INT BIT(2) 459c16fa79SAlberto Garcia #define CTRL_ERR_INT BIT(3) 469c16fa79SAlberto Garcia #define CTRL_INT_EDGE(INTNO) BIT(4 + (INTNO)) 479c16fa79SAlberto Garcia #define CTRL_INT(INTNO) BIT(6 + (INTNO)) 489c16fa79SAlberto Garcia 499c16fa79SAlberto Garcia #define REG_REV_ID 0x00 509c16fa79SAlberto Garcia #define REG_IP_A_CTRL 0x02 519c16fa79SAlberto Garcia #define REG_IP_B_CTRL 0x04 529c16fa79SAlberto Garcia #define REG_IP_C_CTRL 0x06 539c16fa79SAlberto Garcia #define REG_IP_D_CTRL 0x08 549c16fa79SAlberto Garcia #define REG_RESET 0x0A 559c16fa79SAlberto Garcia #define REG_STATUS 0x0C 569c16fa79SAlberto Garcia #define IP_N_FROM_REG(REG) ((REG) / 2 - 1) 579c16fa79SAlberto Garcia 58*db1015e9SEduardo Habkost struct TPCI200State { 599c16fa79SAlberto Garcia PCIDevice dev; 609c16fa79SAlberto Garcia IPackBus bus; 619c16fa79SAlberto Garcia MemoryRegion mmio; 629c16fa79SAlberto Garcia MemoryRegion io; 639c16fa79SAlberto Garcia MemoryRegion las0; 649c16fa79SAlberto Garcia MemoryRegion las1; 659c16fa79SAlberto Garcia MemoryRegion las2; 669c16fa79SAlberto Garcia MemoryRegion las3; 679c16fa79SAlberto Garcia bool big_endian[3]; 689c16fa79SAlberto Garcia uint8_t ctrl[N_MODULES]; 699c16fa79SAlberto Garcia uint16_t status; 709c16fa79SAlberto Garcia uint8_t int_set; 71*db1015e9SEduardo Habkost }; 72*db1015e9SEduardo Habkost typedef struct TPCI200State TPCI200State; 739c16fa79SAlberto Garcia 749c16fa79SAlberto Garcia #define TYPE_TPCI200 "tpci200" 759c16fa79SAlberto Garcia 769c16fa79SAlberto Garcia #define TPCI200(obj) \ 779c16fa79SAlberto Garcia OBJECT_CHECK(TPCI200State, (obj), TYPE_TPCI200) 789c16fa79SAlberto Garcia 799c16fa79SAlberto Garcia static const uint8_t local_config_regs[] = { 809c16fa79SAlberto Garcia 0x00, 0xFF, 0xFF, 0x0F, 0x00, 0xFC, 0xFF, 0x0F, 0x00, 0x00, 0x00, 819c16fa79SAlberto Garcia 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 829c16fa79SAlberto Garcia 0x00, 0x08, 0x01, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x01, 839c16fa79SAlberto Garcia 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x60, 0x41, 0xD4, 849c16fa79SAlberto Garcia 0xA2, 0x20, 0x41, 0x14, 0xA2, 0x20, 0x41, 0x14, 0xA2, 0x20, 0x01, 859c16fa79SAlberto Garcia 0x14, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x08, 0x01, 0x02, 869c16fa79SAlberto Garcia 0x00, 0x04, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x80, 0x02, 0x41, 879c16fa79SAlberto Garcia 0x00, 0x00, 0x00, 0x00, 0x40, 0x7A, 0x00, 0x52, 0x92, 0x24, 0x02 889c16fa79SAlberto Garcia }; 899c16fa79SAlberto Garcia 909c16fa79SAlberto Garcia static void adjust_addr(bool big_endian, hwaddr *addr, unsigned size) 919c16fa79SAlberto Garcia { 929c16fa79SAlberto Garcia /* During 8 bit access in big endian mode, 939c16fa79SAlberto Garcia odd and even addresses are swapped */ 949c16fa79SAlberto Garcia if (big_endian && size == 1) { 959c16fa79SAlberto Garcia *addr ^= 1; 969c16fa79SAlberto Garcia } 979c16fa79SAlberto Garcia } 989c16fa79SAlberto Garcia 999c16fa79SAlberto Garcia static uint64_t adjust_value(bool big_endian, uint64_t *val, unsigned size) 1009c16fa79SAlberto Garcia { 1019c16fa79SAlberto Garcia /* Local spaces only support 8/16 bit access, 1029c16fa79SAlberto Garcia * so there's no need to care for sizes > 2 */ 1039c16fa79SAlberto Garcia if (big_endian && size == 2) { 1049c16fa79SAlberto Garcia *val = bswap16(*val); 1059c16fa79SAlberto Garcia } 1069c16fa79SAlberto Garcia return *val; 1079c16fa79SAlberto Garcia } 1089c16fa79SAlberto Garcia 1099c16fa79SAlberto Garcia static void tpci200_set_irq(void *opaque, int intno, int level) 1109c16fa79SAlberto Garcia { 1119c16fa79SAlberto Garcia IPackDevice *ip = opaque; 1129c16fa79SAlberto Garcia IPackBus *bus = IPACK_BUS(qdev_get_parent_bus(DEVICE(ip))); 1139c16fa79SAlberto Garcia PCIDevice *pcidev = PCI_DEVICE(BUS(bus)->parent); 1149c16fa79SAlberto Garcia TPCI200State *dev = TPCI200(pcidev); 1159c16fa79SAlberto Garcia unsigned ip_n = ip->slot; 1169c16fa79SAlberto Garcia uint16_t prev_status = dev->status; 1179c16fa79SAlberto Garcia 1189c16fa79SAlberto Garcia assert(ip->slot >= 0 && ip->slot < N_MODULES); 1199c16fa79SAlberto Garcia 1209c16fa79SAlberto Garcia /* The requested interrupt must be enabled in the IP CONTROL 1219c16fa79SAlberto Garcia * register */ 1229c16fa79SAlberto Garcia if (!(dev->ctrl[ip_n] & CTRL_INT(intno))) { 1239c16fa79SAlberto Garcia return; 1249c16fa79SAlberto Garcia } 1259c16fa79SAlberto Garcia 1269c16fa79SAlberto Garcia /* Update the interrupt status in the IP STATUS register */ 1279c16fa79SAlberto Garcia if (level) { 1289c16fa79SAlberto Garcia dev->status |= STATUS_INT(ip_n, intno); 1299c16fa79SAlberto Garcia } else { 1309c16fa79SAlberto Garcia dev->status &= ~STATUS_INT(ip_n, intno); 1319c16fa79SAlberto Garcia } 1329c16fa79SAlberto Garcia 1339c16fa79SAlberto Garcia /* Return if there are no changes */ 1349c16fa79SAlberto Garcia if (dev->status == prev_status) { 1359c16fa79SAlberto Garcia return; 1369c16fa79SAlberto Garcia } 1379c16fa79SAlberto Garcia 1389c16fa79SAlberto Garcia DPRINTF("IP %u INT%u#: %u\n", ip_n, intno, level); 1399c16fa79SAlberto Garcia 1409c16fa79SAlberto Garcia /* Check if the interrupt is edge sensitive */ 1419c16fa79SAlberto Garcia if (dev->ctrl[ip_n] & CTRL_INT_EDGE(intno)) { 1429c16fa79SAlberto Garcia if (level) { 1439e64f8a3SMarcel Apfelbaum pci_set_irq(&dev->dev, !dev->int_set); 1449e64f8a3SMarcel Apfelbaum pci_set_irq(&dev->dev, dev->int_set); 1459c16fa79SAlberto Garcia } 1469c16fa79SAlberto Garcia } else { 1479c16fa79SAlberto Garcia unsigned i, j; 1489c16fa79SAlberto Garcia uint16_t level_status = dev->status; 1499c16fa79SAlberto Garcia 1509c16fa79SAlberto Garcia /* Check if there are any level sensitive interrupts set by 1519c16fa79SAlberto Garcia removing the ones that are edge sensitive from the status 1529c16fa79SAlberto Garcia register */ 1539c16fa79SAlberto Garcia for (i = 0; i < N_MODULES; i++) { 1549c16fa79SAlberto Garcia for (j = 0; j < 2; j++) { 1559c16fa79SAlberto Garcia if (dev->ctrl[i] & CTRL_INT_EDGE(j)) { 1569c16fa79SAlberto Garcia level_status &= ~STATUS_INT(i, j); 1579c16fa79SAlberto Garcia } 1589c16fa79SAlberto Garcia } 1599c16fa79SAlberto Garcia } 1609c16fa79SAlberto Garcia 1619c16fa79SAlberto Garcia if (level_status && !dev->int_set) { 1629e64f8a3SMarcel Apfelbaum pci_irq_assert(&dev->dev); 1639c16fa79SAlberto Garcia dev->int_set = 1; 1649c16fa79SAlberto Garcia } else if (!level_status && dev->int_set) { 1659e64f8a3SMarcel Apfelbaum pci_irq_deassert(&dev->dev); 1669c16fa79SAlberto Garcia dev->int_set = 0; 1679c16fa79SAlberto Garcia } 1689c16fa79SAlberto Garcia } 1699c16fa79SAlberto Garcia } 1709c16fa79SAlberto Garcia 1719c16fa79SAlberto Garcia static uint64_t tpci200_read_cfg(void *opaque, hwaddr addr, unsigned size) 1729c16fa79SAlberto Garcia { 1739c16fa79SAlberto Garcia TPCI200State *s = opaque; 1749c16fa79SAlberto Garcia uint8_t ret = 0; 1759c16fa79SAlberto Garcia if (addr < ARRAY_SIZE(local_config_regs)) { 1769c16fa79SAlberto Garcia ret = local_config_regs[addr]; 1779c16fa79SAlberto Garcia } 1789c16fa79SAlberto Garcia /* Endianness is stored in the first bit of these registers */ 1799c16fa79SAlberto Garcia if ((addr == 0x2b && s->big_endian[0]) || 1809c16fa79SAlberto Garcia (addr == 0x2f && s->big_endian[1]) || 1819c16fa79SAlberto Garcia (addr == 0x33 && s->big_endian[2])) { 1829c16fa79SAlberto Garcia ret |= 1; 1839c16fa79SAlberto Garcia } 1849c16fa79SAlberto Garcia DPRINTF("Read from LCR 0x%x: 0x%x\n", (unsigned) addr, (unsigned) ret); 1859c16fa79SAlberto Garcia return ret; 1869c16fa79SAlberto Garcia } 1879c16fa79SAlberto Garcia 1889c16fa79SAlberto Garcia static void tpci200_write_cfg(void *opaque, hwaddr addr, uint64_t val, 1899c16fa79SAlberto Garcia unsigned size) 1909c16fa79SAlberto Garcia { 1919c16fa79SAlberto Garcia TPCI200State *s = opaque; 1929c16fa79SAlberto Garcia /* Endianness is stored in the first bit of these registers */ 1939c16fa79SAlberto Garcia if (addr == 0x2b || addr == 0x2f || addr == 0x33) { 1949c16fa79SAlberto Garcia unsigned las = (addr - 0x2b) / 4; 1959c16fa79SAlberto Garcia s->big_endian[las] = val & 1; 1969c16fa79SAlberto Garcia DPRINTF("LAS%u big endian mode: %u\n", las, (unsigned) val & 1); 1979c16fa79SAlberto Garcia } else { 1989c16fa79SAlberto Garcia DPRINTF("Write to LCR 0x%x: 0x%x\n", (unsigned) addr, (unsigned) val); 1999c16fa79SAlberto Garcia } 2009c16fa79SAlberto Garcia } 2019c16fa79SAlberto Garcia 2029c16fa79SAlberto Garcia static uint64_t tpci200_read_las0(void *opaque, hwaddr addr, unsigned size) 2039c16fa79SAlberto Garcia { 2049c16fa79SAlberto Garcia TPCI200State *s = opaque; 2059c16fa79SAlberto Garcia uint64_t ret = 0; 2069c16fa79SAlberto Garcia 2079c16fa79SAlberto Garcia switch (addr) { 2089c16fa79SAlberto Garcia 2099c16fa79SAlberto Garcia case REG_REV_ID: 2109c16fa79SAlberto Garcia DPRINTF("Read REVISION ID\n"); /* Current value is 0x00 */ 2119c16fa79SAlberto Garcia break; 2129c16fa79SAlberto Garcia 2139c16fa79SAlberto Garcia case REG_IP_A_CTRL: 2149c16fa79SAlberto Garcia case REG_IP_B_CTRL: 2159c16fa79SAlberto Garcia case REG_IP_C_CTRL: 2169c16fa79SAlberto Garcia case REG_IP_D_CTRL: 2179c16fa79SAlberto Garcia { 2189c16fa79SAlberto Garcia unsigned ip_n = IP_N_FROM_REG(addr); 2199c16fa79SAlberto Garcia ret = s->ctrl[ip_n]; 2209c16fa79SAlberto Garcia DPRINTF("Read IP %c CONTROL: 0x%x\n", 'A' + ip_n, (unsigned) ret); 2219c16fa79SAlberto Garcia } 2229c16fa79SAlberto Garcia break; 2239c16fa79SAlberto Garcia 2249c16fa79SAlberto Garcia case REG_RESET: 2259c16fa79SAlberto Garcia DPRINTF("Read RESET\n"); /* Not implemented */ 2269c16fa79SAlberto Garcia break; 2279c16fa79SAlberto Garcia 2289c16fa79SAlberto Garcia case REG_STATUS: 2299c16fa79SAlberto Garcia ret = s->status; 2309c16fa79SAlberto Garcia DPRINTF("Read STATUS: 0x%x\n", (unsigned) ret); 2319c16fa79SAlberto Garcia break; 2329c16fa79SAlberto Garcia 2339c16fa79SAlberto Garcia /* Reserved */ 2349c16fa79SAlberto Garcia default: 2359c16fa79SAlberto Garcia DPRINTF("Unsupported read from LAS0 0x%x\n", (unsigned) addr); 2369c16fa79SAlberto Garcia break; 2379c16fa79SAlberto Garcia } 2389c16fa79SAlberto Garcia 2399c16fa79SAlberto Garcia return adjust_value(s->big_endian[0], &ret, size); 2409c16fa79SAlberto Garcia } 2419c16fa79SAlberto Garcia 2429c16fa79SAlberto Garcia static void tpci200_write_las0(void *opaque, hwaddr addr, uint64_t val, 2439c16fa79SAlberto Garcia unsigned size) 2449c16fa79SAlberto Garcia { 2459c16fa79SAlberto Garcia TPCI200State *s = opaque; 2469c16fa79SAlberto Garcia 2479c16fa79SAlberto Garcia adjust_value(s->big_endian[0], &val, size); 2489c16fa79SAlberto Garcia 2499c16fa79SAlberto Garcia switch (addr) { 2509c16fa79SAlberto Garcia 2519c16fa79SAlberto Garcia case REG_REV_ID: 2529c16fa79SAlberto Garcia DPRINTF("Write Revision ID: 0x%x\n", (unsigned) val); /* No effect */ 2539c16fa79SAlberto Garcia break; 2549c16fa79SAlberto Garcia 2559c16fa79SAlberto Garcia case REG_IP_A_CTRL: 2569c16fa79SAlberto Garcia case REG_IP_B_CTRL: 2579c16fa79SAlberto Garcia case REG_IP_C_CTRL: 2589c16fa79SAlberto Garcia case REG_IP_D_CTRL: 2599c16fa79SAlberto Garcia { 2609c16fa79SAlberto Garcia unsigned ip_n = IP_N_FROM_REG(addr); 2619c16fa79SAlberto Garcia s->ctrl[ip_n] = val; 2629c16fa79SAlberto Garcia DPRINTF("Write IP %c CONTROL: 0x%x\n", 'A' + ip_n, (unsigned) val); 2639c16fa79SAlberto Garcia } 2649c16fa79SAlberto Garcia break; 2659c16fa79SAlberto Garcia 2669c16fa79SAlberto Garcia case REG_RESET: 2679c16fa79SAlberto Garcia DPRINTF("Write RESET: 0x%x\n", (unsigned) val); /* Not implemented */ 2689c16fa79SAlberto Garcia break; 2699c16fa79SAlberto Garcia 2709c16fa79SAlberto Garcia case REG_STATUS: 2719c16fa79SAlberto Garcia { 2729c16fa79SAlberto Garcia unsigned i; 2739c16fa79SAlberto Garcia 2749c16fa79SAlberto Garcia for (i = 0; i < N_MODULES; i++) { 2759c16fa79SAlberto Garcia IPackDevice *ip = ipack_device_find(&s->bus, i); 2769c16fa79SAlberto Garcia 2779c16fa79SAlberto Garcia if (ip != NULL) { 2789c16fa79SAlberto Garcia if (val & STATUS_INT(i, 0)) { 2799c16fa79SAlberto Garcia DPRINTF("Clear IP %c INT0# status\n", 'A' + i); 2809c16fa79SAlberto Garcia qemu_irq_lower(ip->irq[0]); 2819c16fa79SAlberto Garcia } 2829c16fa79SAlberto Garcia if (val & STATUS_INT(i, 1)) { 2839c16fa79SAlberto Garcia DPRINTF("Clear IP %c INT1# status\n", 'A' + i); 2849c16fa79SAlberto Garcia qemu_irq_lower(ip->irq[1]); 2859c16fa79SAlberto Garcia } 2869c16fa79SAlberto Garcia } 2879c16fa79SAlberto Garcia 28809a021fbSStefan Weil if (val & STATUS_TIME(i)) { 2899c16fa79SAlberto Garcia DPRINTF("Clear IP %c timeout\n", 'A' + i); 29009a021fbSStefan Weil s->status &= ~STATUS_TIME(i); 2919c16fa79SAlberto Garcia } 2929c16fa79SAlberto Garcia } 2939c16fa79SAlberto Garcia 2949c16fa79SAlberto Garcia if (val & STATUS_ERR_ANY) { 2959c16fa79SAlberto Garcia DPRINTF("Unexpected write to STATUS register: 0x%x\n", 2969c16fa79SAlberto Garcia (unsigned) val); 2979c16fa79SAlberto Garcia } 2989c16fa79SAlberto Garcia } 2999c16fa79SAlberto Garcia break; 3009c16fa79SAlberto Garcia 3019c16fa79SAlberto Garcia /* Reserved */ 3029c16fa79SAlberto Garcia default: 3039c16fa79SAlberto Garcia DPRINTF("Unsupported write to LAS0 0x%x: 0x%x\n", 3049c16fa79SAlberto Garcia (unsigned) addr, (unsigned) val); 3059c16fa79SAlberto Garcia break; 3069c16fa79SAlberto Garcia } 3079c16fa79SAlberto Garcia } 3089c16fa79SAlberto Garcia 3099c16fa79SAlberto Garcia static uint64_t tpci200_read_las1(void *opaque, hwaddr addr, unsigned size) 3109c16fa79SAlberto Garcia { 3119c16fa79SAlberto Garcia TPCI200State *s = opaque; 3129c16fa79SAlberto Garcia IPackDevice *ip; 3139c16fa79SAlberto Garcia uint64_t ret = 0; 3149c16fa79SAlberto Garcia unsigned ip_n, space; 3159c16fa79SAlberto Garcia uint8_t offset; 3169c16fa79SAlberto Garcia 3179c16fa79SAlberto Garcia adjust_addr(s->big_endian[1], &addr, size); 3189c16fa79SAlberto Garcia 3199c16fa79SAlberto Garcia /* 3209c16fa79SAlberto Garcia * The address is divided into the IP module number (0-4), the IP 3219c16fa79SAlberto Garcia * address space (I/O, ID, INT) and the offset within that space. 3229c16fa79SAlberto Garcia */ 3239c16fa79SAlberto Garcia ip_n = addr >> 8; 3249c16fa79SAlberto Garcia space = (addr >> 6) & 3; 3259c16fa79SAlberto Garcia ip = ipack_device_find(&s->bus, ip_n); 3269c16fa79SAlberto Garcia 3279c16fa79SAlberto Garcia if (ip == NULL) { 3289c16fa79SAlberto Garcia DPRINTF("Read LAS1: IP module %u not installed\n", ip_n); 3299c16fa79SAlberto Garcia } else { 3309c16fa79SAlberto Garcia IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); 3319c16fa79SAlberto Garcia switch (space) { 3329c16fa79SAlberto Garcia 3339c16fa79SAlberto Garcia case IP_ID_SPACE: 3349c16fa79SAlberto Garcia offset = addr & IP_ID_SPACE_ADDR_MASK; 3359c16fa79SAlberto Garcia if (k->id_read) { 3369c16fa79SAlberto Garcia ret = k->id_read(ip, offset); 3379c16fa79SAlberto Garcia } 3389c16fa79SAlberto Garcia break; 3399c16fa79SAlberto Garcia 3409c16fa79SAlberto Garcia case IP_INT_SPACE: 3419c16fa79SAlberto Garcia offset = addr & IP_INT_SPACE_ADDR_MASK; 3429c16fa79SAlberto Garcia 3439c16fa79SAlberto Garcia /* Read address 0 to ACK IP INT0# and address 2 to ACK IP INT1# */ 3449c16fa79SAlberto Garcia if (offset == 0 || offset == 2) { 3459c16fa79SAlberto Garcia unsigned intno = offset / 2; 3469c16fa79SAlberto Garcia bool int_set = s->status & STATUS_INT(ip_n, intno); 3479c16fa79SAlberto Garcia bool int_edge_sensitive = s->ctrl[ip_n] & CTRL_INT_EDGE(intno); 3489c16fa79SAlberto Garcia if (int_set && !int_edge_sensitive) { 3499c16fa79SAlberto Garcia qemu_irq_lower(ip->irq[intno]); 3509c16fa79SAlberto Garcia } 3519c16fa79SAlberto Garcia } 3529c16fa79SAlberto Garcia 3539c16fa79SAlberto Garcia if (k->int_read) { 3549c16fa79SAlberto Garcia ret = k->int_read(ip, offset); 3559c16fa79SAlberto Garcia } 3569c16fa79SAlberto Garcia break; 3579c16fa79SAlberto Garcia 3589c16fa79SAlberto Garcia default: 3599c16fa79SAlberto Garcia offset = addr & IP_IO_SPACE_ADDR_MASK; 3609c16fa79SAlberto Garcia if (k->io_read) { 3619c16fa79SAlberto Garcia ret = k->io_read(ip, offset); 3629c16fa79SAlberto Garcia } 3639c16fa79SAlberto Garcia break; 3649c16fa79SAlberto Garcia } 3659c16fa79SAlberto Garcia } 3669c16fa79SAlberto Garcia 3679c16fa79SAlberto Garcia return adjust_value(s->big_endian[1], &ret, size); 3689c16fa79SAlberto Garcia } 3699c16fa79SAlberto Garcia 3709c16fa79SAlberto Garcia static void tpci200_write_las1(void *opaque, hwaddr addr, uint64_t val, 3719c16fa79SAlberto Garcia unsigned size) 3729c16fa79SAlberto Garcia { 3739c16fa79SAlberto Garcia TPCI200State *s = opaque; 3749c16fa79SAlberto Garcia IPackDevice *ip; 3759c16fa79SAlberto Garcia unsigned ip_n, space; 3769c16fa79SAlberto Garcia uint8_t offset; 3779c16fa79SAlberto Garcia 3789c16fa79SAlberto Garcia adjust_addr(s->big_endian[1], &addr, size); 3799c16fa79SAlberto Garcia adjust_value(s->big_endian[1], &val, size); 3809c16fa79SAlberto Garcia 3819c16fa79SAlberto Garcia /* 3829c16fa79SAlberto Garcia * The address is divided into the IP module number, the IP 3839c16fa79SAlberto Garcia * address space (I/O, ID, INT) and the offset within that space. 3849c16fa79SAlberto Garcia */ 3859c16fa79SAlberto Garcia ip_n = addr >> 8; 3869c16fa79SAlberto Garcia space = (addr >> 6) & 3; 3879c16fa79SAlberto Garcia ip = ipack_device_find(&s->bus, ip_n); 3889c16fa79SAlberto Garcia 3899c16fa79SAlberto Garcia if (ip == NULL) { 3909c16fa79SAlberto Garcia DPRINTF("Write LAS1: IP module %u not installed\n", ip_n); 3919c16fa79SAlberto Garcia } else { 3929c16fa79SAlberto Garcia IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); 3939c16fa79SAlberto Garcia switch (space) { 3949c16fa79SAlberto Garcia 3959c16fa79SAlberto Garcia case IP_ID_SPACE: 3969c16fa79SAlberto Garcia offset = addr & IP_ID_SPACE_ADDR_MASK; 3979c16fa79SAlberto Garcia if (k->id_write) { 3989c16fa79SAlberto Garcia k->id_write(ip, offset, val); 3999c16fa79SAlberto Garcia } 4009c16fa79SAlberto Garcia break; 4019c16fa79SAlberto Garcia 4029c16fa79SAlberto Garcia case IP_INT_SPACE: 4039c16fa79SAlberto Garcia offset = addr & IP_INT_SPACE_ADDR_MASK; 4049c16fa79SAlberto Garcia if (k->int_write) { 4059c16fa79SAlberto Garcia k->int_write(ip, offset, val); 4069c16fa79SAlberto Garcia } 4079c16fa79SAlberto Garcia break; 4089c16fa79SAlberto Garcia 4099c16fa79SAlberto Garcia default: 4109c16fa79SAlberto Garcia offset = addr & IP_IO_SPACE_ADDR_MASK; 4119c16fa79SAlberto Garcia if (k->io_write) { 4129c16fa79SAlberto Garcia k->io_write(ip, offset, val); 4139c16fa79SAlberto Garcia } 4149c16fa79SAlberto Garcia break; 4159c16fa79SAlberto Garcia } 4169c16fa79SAlberto Garcia } 4179c16fa79SAlberto Garcia } 4189c16fa79SAlberto Garcia 4199c16fa79SAlberto Garcia static uint64_t tpci200_read_las2(void *opaque, hwaddr addr, unsigned size) 4209c16fa79SAlberto Garcia { 4219c16fa79SAlberto Garcia TPCI200State *s = opaque; 4229c16fa79SAlberto Garcia IPackDevice *ip; 4239c16fa79SAlberto Garcia uint64_t ret = 0; 4249c16fa79SAlberto Garcia unsigned ip_n; 4259c16fa79SAlberto Garcia uint32_t offset; 4269c16fa79SAlberto Garcia 4279c16fa79SAlberto Garcia adjust_addr(s->big_endian[2], &addr, size); 4289c16fa79SAlberto Garcia 4299c16fa79SAlberto Garcia /* 4309c16fa79SAlberto Garcia * The address is divided into the IP module number and the offset 4319c16fa79SAlberto Garcia * within the IP module MEM space. 4329c16fa79SAlberto Garcia */ 4339c16fa79SAlberto Garcia ip_n = addr >> 23; 4349c16fa79SAlberto Garcia offset = addr & 0x7fffff; 4359c16fa79SAlberto Garcia ip = ipack_device_find(&s->bus, ip_n); 4369c16fa79SAlberto Garcia 4379c16fa79SAlberto Garcia if (ip == NULL) { 4389c16fa79SAlberto Garcia DPRINTF("Read LAS2: IP module %u not installed\n", ip_n); 4399c16fa79SAlberto Garcia } else { 4409c16fa79SAlberto Garcia IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); 4419c16fa79SAlberto Garcia if (k->mem_read16) { 4429c16fa79SAlberto Garcia ret = k->mem_read16(ip, offset); 4439c16fa79SAlberto Garcia } 4449c16fa79SAlberto Garcia } 4459c16fa79SAlberto Garcia 4469c16fa79SAlberto Garcia return adjust_value(s->big_endian[2], &ret, size); 4479c16fa79SAlberto Garcia } 4489c16fa79SAlberto Garcia 4499c16fa79SAlberto Garcia static void tpci200_write_las2(void *opaque, hwaddr addr, uint64_t val, 4509c16fa79SAlberto Garcia unsigned size) 4519c16fa79SAlberto Garcia { 4529c16fa79SAlberto Garcia TPCI200State *s = opaque; 4539c16fa79SAlberto Garcia IPackDevice *ip; 4549c16fa79SAlberto Garcia unsigned ip_n; 4559c16fa79SAlberto Garcia uint32_t offset; 4569c16fa79SAlberto Garcia 4579c16fa79SAlberto Garcia adjust_addr(s->big_endian[2], &addr, size); 4589c16fa79SAlberto Garcia adjust_value(s->big_endian[2], &val, size); 4599c16fa79SAlberto Garcia 4609c16fa79SAlberto Garcia /* 4619c16fa79SAlberto Garcia * The address is divided into the IP module number and the offset 4629c16fa79SAlberto Garcia * within the IP module MEM space. 4639c16fa79SAlberto Garcia */ 4649c16fa79SAlberto Garcia ip_n = addr >> 23; 4659c16fa79SAlberto Garcia offset = addr & 0x7fffff; 4669c16fa79SAlberto Garcia ip = ipack_device_find(&s->bus, ip_n); 4679c16fa79SAlberto Garcia 4689c16fa79SAlberto Garcia if (ip == NULL) { 4699c16fa79SAlberto Garcia DPRINTF("Write LAS2: IP module %u not installed\n", ip_n); 4709c16fa79SAlberto Garcia } else { 4719c16fa79SAlberto Garcia IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); 4729c16fa79SAlberto Garcia if (k->mem_write16) { 4739c16fa79SAlberto Garcia k->mem_write16(ip, offset, val); 4749c16fa79SAlberto Garcia } 4759c16fa79SAlberto Garcia } 4769c16fa79SAlberto Garcia } 4779c16fa79SAlberto Garcia 4789c16fa79SAlberto Garcia static uint64_t tpci200_read_las3(void *opaque, hwaddr addr, unsigned size) 4799c16fa79SAlberto Garcia { 4809c16fa79SAlberto Garcia TPCI200State *s = opaque; 4819c16fa79SAlberto Garcia IPackDevice *ip; 4829c16fa79SAlberto Garcia uint64_t ret = 0; 4839c16fa79SAlberto Garcia /* 4849c16fa79SAlberto Garcia * The address is divided into the IP module number and the offset 4859c16fa79SAlberto Garcia * within the IP module MEM space. 4869c16fa79SAlberto Garcia */ 4879c16fa79SAlberto Garcia unsigned ip_n = addr >> 22; 4889c16fa79SAlberto Garcia uint32_t offset = addr & 0x3fffff; 4899c16fa79SAlberto Garcia 4909c16fa79SAlberto Garcia ip = ipack_device_find(&s->bus, ip_n); 4919c16fa79SAlberto Garcia 4929c16fa79SAlberto Garcia if (ip == NULL) { 4939c16fa79SAlberto Garcia DPRINTF("Read LAS3: IP module %u not installed\n", ip_n); 4949c16fa79SAlberto Garcia } else { 4959c16fa79SAlberto Garcia IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); 4969c16fa79SAlberto Garcia if (k->mem_read8) { 4979c16fa79SAlberto Garcia ret = k->mem_read8(ip, offset); 4989c16fa79SAlberto Garcia } 4999c16fa79SAlberto Garcia } 5009c16fa79SAlberto Garcia 5019c16fa79SAlberto Garcia return ret; 5029c16fa79SAlberto Garcia } 5039c16fa79SAlberto Garcia 5049c16fa79SAlberto Garcia static void tpci200_write_las3(void *opaque, hwaddr addr, uint64_t val, 5059c16fa79SAlberto Garcia unsigned size) 5069c16fa79SAlberto Garcia { 5079c16fa79SAlberto Garcia TPCI200State *s = opaque; 5089c16fa79SAlberto Garcia IPackDevice *ip; 5099c16fa79SAlberto Garcia /* 5109c16fa79SAlberto Garcia * The address is divided into the IP module number and the offset 5119c16fa79SAlberto Garcia * within the IP module MEM space. 5129c16fa79SAlberto Garcia */ 5139c16fa79SAlberto Garcia unsigned ip_n = addr >> 22; 5149c16fa79SAlberto Garcia uint32_t offset = addr & 0x3fffff; 5159c16fa79SAlberto Garcia 5169c16fa79SAlberto Garcia ip = ipack_device_find(&s->bus, ip_n); 5179c16fa79SAlberto Garcia 5189c16fa79SAlberto Garcia if (ip == NULL) { 5199c16fa79SAlberto Garcia DPRINTF("Write LAS3: IP module %u not installed\n", ip_n); 5209c16fa79SAlberto Garcia } else { 5219c16fa79SAlberto Garcia IPackDeviceClass *k = IPACK_DEVICE_GET_CLASS(ip); 5229c16fa79SAlberto Garcia if (k->mem_write8) { 5239c16fa79SAlberto Garcia k->mem_write8(ip, offset, val); 5249c16fa79SAlberto Garcia } 5259c16fa79SAlberto Garcia } 5269c16fa79SAlberto Garcia } 5279c16fa79SAlberto Garcia 5289c16fa79SAlberto Garcia static const MemoryRegionOps tpci200_cfg_ops = { 5299c16fa79SAlberto Garcia .read = tpci200_read_cfg, 5309c16fa79SAlberto Garcia .write = tpci200_write_cfg, 5319c16fa79SAlberto Garcia .endianness = DEVICE_NATIVE_ENDIAN, 5329c16fa79SAlberto Garcia .valid = { 5339c16fa79SAlberto Garcia .min_access_size = 1, 5349c16fa79SAlberto Garcia .max_access_size = 4 5359c16fa79SAlberto Garcia }, 5369c16fa79SAlberto Garcia .impl = { 5379c16fa79SAlberto Garcia .min_access_size = 1, 5389c16fa79SAlberto Garcia .max_access_size = 1 5399c16fa79SAlberto Garcia } 5409c16fa79SAlberto Garcia }; 5419c16fa79SAlberto Garcia 5429c16fa79SAlberto Garcia static const MemoryRegionOps tpci200_las0_ops = { 5439c16fa79SAlberto Garcia .read = tpci200_read_las0, 5449c16fa79SAlberto Garcia .write = tpci200_write_las0, 5459c16fa79SAlberto Garcia .endianness = DEVICE_NATIVE_ENDIAN, 5469c16fa79SAlberto Garcia .valid = { 5479c16fa79SAlberto Garcia .min_access_size = 2, 5489c16fa79SAlberto Garcia .max_access_size = 2 5499c16fa79SAlberto Garcia } 5509c16fa79SAlberto Garcia }; 5519c16fa79SAlberto Garcia 5529c16fa79SAlberto Garcia static const MemoryRegionOps tpci200_las1_ops = { 5539c16fa79SAlberto Garcia .read = tpci200_read_las1, 5549c16fa79SAlberto Garcia .write = tpci200_write_las1, 5559c16fa79SAlberto Garcia .endianness = DEVICE_NATIVE_ENDIAN, 5569c16fa79SAlberto Garcia .valid = { 5579c16fa79SAlberto Garcia .min_access_size = 1, 5589c16fa79SAlberto Garcia .max_access_size = 2 5599c16fa79SAlberto Garcia } 5609c16fa79SAlberto Garcia }; 5619c16fa79SAlberto Garcia 5629c16fa79SAlberto Garcia static const MemoryRegionOps tpci200_las2_ops = { 5639c16fa79SAlberto Garcia .read = tpci200_read_las2, 5649c16fa79SAlberto Garcia .write = tpci200_write_las2, 5659c16fa79SAlberto Garcia .endianness = DEVICE_NATIVE_ENDIAN, 5669c16fa79SAlberto Garcia .valid = { 5679c16fa79SAlberto Garcia .min_access_size = 1, 5689c16fa79SAlberto Garcia .max_access_size = 2 5699c16fa79SAlberto Garcia } 5709c16fa79SAlberto Garcia }; 5719c16fa79SAlberto Garcia 5729c16fa79SAlberto Garcia static const MemoryRegionOps tpci200_las3_ops = { 5739c16fa79SAlberto Garcia .read = tpci200_read_las3, 5749c16fa79SAlberto Garcia .write = tpci200_write_las3, 5759c16fa79SAlberto Garcia .endianness = DEVICE_NATIVE_ENDIAN, 5769c16fa79SAlberto Garcia .valid = { 5779c16fa79SAlberto Garcia .min_access_size = 1, 5789c16fa79SAlberto Garcia .max_access_size = 1 5799c16fa79SAlberto Garcia } 5809c16fa79SAlberto Garcia }; 5819c16fa79SAlberto Garcia 5829af21dbeSMarkus Armbruster static void tpci200_realize(PCIDevice *pci_dev, Error **errp) 5839c16fa79SAlberto Garcia { 5849c16fa79SAlberto Garcia TPCI200State *s = TPCI200(pci_dev); 5859c16fa79SAlberto Garcia uint8_t *c = s->dev.config; 5869c16fa79SAlberto Garcia 5879c16fa79SAlberto Garcia pci_set_word(c + PCI_COMMAND, 0x0003); 5889c16fa79SAlberto Garcia pci_set_word(c + PCI_STATUS, 0x0280); 5899c16fa79SAlberto Garcia 5909c16fa79SAlberto Garcia pci_set_byte(c + PCI_INTERRUPT_PIN, 0x01); /* Interrupt pin A */ 5919c16fa79SAlberto Garcia 5929c16fa79SAlberto Garcia pci_set_byte(c + PCI_CAPABILITY_LIST, 0x40); 5939c16fa79SAlberto Garcia pci_set_long(c + 0x40, 0x48014801); 5949c16fa79SAlberto Garcia pci_set_long(c + 0x48, 0x00024C06); 5959c16fa79SAlberto Garcia pci_set_long(c + 0x4C, 0x00000003); 5969c16fa79SAlberto Garcia 597300b1fc6SPaolo Bonzini memory_region_init_io(&s->mmio, OBJECT(s), &tpci200_cfg_ops, 5989c16fa79SAlberto Garcia s, "tpci200_mmio", 128); 599300b1fc6SPaolo Bonzini memory_region_init_io(&s->io, OBJECT(s), &tpci200_cfg_ops, 6009c16fa79SAlberto Garcia s, "tpci200_io", 128); 601300b1fc6SPaolo Bonzini memory_region_init_io(&s->las0, OBJECT(s), &tpci200_las0_ops, 6029c16fa79SAlberto Garcia s, "tpci200_las0", 256); 603300b1fc6SPaolo Bonzini memory_region_init_io(&s->las1, OBJECT(s), &tpci200_las1_ops, 6049c16fa79SAlberto Garcia s, "tpci200_las1", 1024); 605300b1fc6SPaolo Bonzini memory_region_init_io(&s->las2, OBJECT(s), &tpci200_las2_ops, 606a7174d70SPhilippe Mathieu-Daudé s, "tpci200_las2", 32 * MiB); 607300b1fc6SPaolo Bonzini memory_region_init_io(&s->las3, OBJECT(s), &tpci200_las3_ops, 608a7174d70SPhilippe Mathieu-Daudé s, "tpci200_las3", 16 * MiB); 6099c16fa79SAlberto Garcia pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio); 6109c16fa79SAlberto Garcia pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io); 6119c16fa79SAlberto Garcia pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las0); 6129c16fa79SAlberto Garcia pci_register_bar(&s->dev, 3, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las1); 6139c16fa79SAlberto Garcia pci_register_bar(&s->dev, 4, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las2); 6149c16fa79SAlberto Garcia pci_register_bar(&s->dev, 5, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las3); 6159c16fa79SAlberto Garcia 61677cbb28aSAndreas Färber ipack_bus_new_inplace(&s->bus, sizeof(s->bus), DEVICE(pci_dev), NULL, 6179c16fa79SAlberto Garcia N_MODULES, tpci200_set_irq); 6189c16fa79SAlberto Garcia } 6199c16fa79SAlberto Garcia 6209c16fa79SAlberto Garcia static const VMStateDescription vmstate_tpci200 = { 6219c16fa79SAlberto Garcia .name = "tpci200", 6229c16fa79SAlberto Garcia .version_id = 1, 6239c16fa79SAlberto Garcia .minimum_version_id = 1, 6249c16fa79SAlberto Garcia .fields = (VMStateField[]) { 6259c16fa79SAlberto Garcia VMSTATE_PCI_DEVICE(dev, TPCI200State), 6269c16fa79SAlberto Garcia VMSTATE_BOOL_ARRAY(big_endian, TPCI200State, 3), 6279c16fa79SAlberto Garcia VMSTATE_UINT8_ARRAY(ctrl, TPCI200State, N_MODULES), 6289c16fa79SAlberto Garcia VMSTATE_UINT16(status, TPCI200State), 6299c16fa79SAlberto Garcia VMSTATE_UINT8(int_set, TPCI200State), 6309c16fa79SAlberto Garcia VMSTATE_END_OF_LIST() 6319c16fa79SAlberto Garcia } 6329c16fa79SAlberto Garcia }; 6339c16fa79SAlberto Garcia 6349c16fa79SAlberto Garcia static void tpci200_class_init(ObjectClass *klass, void *data) 6359c16fa79SAlberto Garcia { 6369c16fa79SAlberto Garcia DeviceClass *dc = DEVICE_CLASS(klass); 6379c16fa79SAlberto Garcia PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); 6389c16fa79SAlberto Garcia 6399af21dbeSMarkus Armbruster k->realize = tpci200_realize; 6409c16fa79SAlberto Garcia k->vendor_id = PCI_VENDOR_ID_TEWS; 6419c16fa79SAlberto Garcia k->device_id = PCI_DEVICE_ID_TEWS_TPCI200; 6429c16fa79SAlberto Garcia k->class_id = PCI_CLASS_BRIDGE_OTHER; 6439c16fa79SAlberto Garcia k->subsystem_vendor_id = PCI_VENDOR_ID_TEWS; 6449c16fa79SAlberto Garcia k->subsystem_id = 0x300A; 645125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_INPUT, dc->categories); 6469c16fa79SAlberto Garcia dc->desc = "TEWS TPCI200 IndustryPack carrier"; 6479c16fa79SAlberto Garcia dc->vmsd = &vmstate_tpci200; 6489c16fa79SAlberto Garcia } 6499c16fa79SAlberto Garcia 6509c16fa79SAlberto Garcia static const TypeInfo tpci200_info = { 6519c16fa79SAlberto Garcia .name = TYPE_TPCI200, 6529c16fa79SAlberto Garcia .parent = TYPE_PCI_DEVICE, 6539c16fa79SAlberto Garcia .instance_size = sizeof(TPCI200State), 6549c16fa79SAlberto Garcia .class_init = tpci200_class_init, 655fd3b02c8SEduardo Habkost .interfaces = (InterfaceInfo[]) { 656fd3b02c8SEduardo Habkost { INTERFACE_CONVENTIONAL_PCI_DEVICE }, 657fd3b02c8SEduardo Habkost { }, 658fd3b02c8SEduardo Habkost }, 6599c16fa79SAlberto Garcia }; 6609c16fa79SAlberto Garcia 6619c16fa79SAlberto Garcia static void tpci200_register_types(void) 6629c16fa79SAlberto Garcia { 6639c16fa79SAlberto Garcia type_register_static(&tpci200_info); 6649c16fa79SAlberto Garcia } 6659c16fa79SAlberto Garcia 6669c16fa79SAlberto Garcia type_init(tpci200_register_types) 667