1a3980bf5SBenjamin Herrenschmidt /* 2a3980bf5SBenjamin Herrenschmidt * QEMU PowerPC PowerNV LPC controller 3a3980bf5SBenjamin Herrenschmidt * 4a3980bf5SBenjamin Herrenschmidt * Copyright (c) 2016, IBM Corporation. 5a3980bf5SBenjamin Herrenschmidt * 6a3980bf5SBenjamin Herrenschmidt * This library is free software; you can redistribute it and/or 7a3980bf5SBenjamin Herrenschmidt * modify it under the terms of the GNU Lesser General Public 8a3980bf5SBenjamin Herrenschmidt * License as published by the Free Software Foundation; either 9a3980bf5SBenjamin Herrenschmidt * version 2 of the License, or (at your option) any later version. 10a3980bf5SBenjamin Herrenschmidt * 11a3980bf5SBenjamin Herrenschmidt * This library is distributed in the hope that it will be useful, 12a3980bf5SBenjamin Herrenschmidt * but WITHOUT ANY WARRANTY; without even the implied warranty of 13a3980bf5SBenjamin Herrenschmidt * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14a3980bf5SBenjamin Herrenschmidt * Lesser General Public License for more details. 15a3980bf5SBenjamin Herrenschmidt * 16a3980bf5SBenjamin Herrenschmidt * You should have received a copy of the GNU Lesser General Public 17a3980bf5SBenjamin Herrenschmidt * License along with this library; if not, see <http://www.gnu.org/licenses/>. 18a3980bf5SBenjamin Herrenschmidt */ 19a3980bf5SBenjamin Herrenschmidt 20a3980bf5SBenjamin Herrenschmidt #include "qemu/osdep.h" 21a3980bf5SBenjamin Herrenschmidt #include "sysemu/sysemu.h" 22fcf5ef2aSThomas Huth #include "target/ppc/cpu.h" 23a3980bf5SBenjamin Herrenschmidt #include "qapi/error.h" 24a3980bf5SBenjamin Herrenschmidt #include "qemu/log.h" 25*0b8fa32fSMarkus Armbruster #include "qemu/module.h" 2604026890SCédric Le Goater #include "hw/isa/isa.h" 27a3980bf5SBenjamin Herrenschmidt 28a3980bf5SBenjamin Herrenschmidt #include "hw/ppc/pnv.h" 29ec575aa0SCédric Le Goater #include "hw/ppc/pnv_lpc.h" 30ec575aa0SCédric Le Goater #include "hw/ppc/pnv_xscom.h" 31a3980bf5SBenjamin Herrenschmidt #include "hw/ppc/fdt.h" 32a3980bf5SBenjamin Herrenschmidt 33a3980bf5SBenjamin Herrenschmidt #include <libfdt.h> 34a3980bf5SBenjamin Herrenschmidt 35a3980bf5SBenjamin Herrenschmidt enum { 36a3980bf5SBenjamin Herrenschmidt ECCB_CTL = 0, 37a3980bf5SBenjamin Herrenschmidt ECCB_RESET = 1, 38a3980bf5SBenjamin Herrenschmidt ECCB_STAT = 2, 39a3980bf5SBenjamin Herrenschmidt ECCB_DATA = 3, 40a3980bf5SBenjamin Herrenschmidt }; 41a3980bf5SBenjamin Herrenschmidt 42a3980bf5SBenjamin Herrenschmidt /* OPB Master LS registers */ 438207b906SCédric Le Goater #define OPB_MASTER_LS_ROUTE0 0x8 448207b906SCédric Le Goater #define OPB_MASTER_LS_ROUTE1 0xC 45a3980bf5SBenjamin Herrenschmidt #define OPB_MASTER_LS_IRQ_STAT 0x50 46a3980bf5SBenjamin Herrenschmidt #define OPB_MASTER_IRQ_LPC 0x00000800 47a3980bf5SBenjamin Herrenschmidt #define OPB_MASTER_LS_IRQ_MASK 0x54 48a3980bf5SBenjamin Herrenschmidt #define OPB_MASTER_LS_IRQ_POL 0x58 49a3980bf5SBenjamin Herrenschmidt #define OPB_MASTER_LS_IRQ_INPUT 0x5c 50a3980bf5SBenjamin Herrenschmidt 51a3980bf5SBenjamin Herrenschmidt /* LPC HC registers */ 52a3980bf5SBenjamin Herrenschmidt #define LPC_HC_FW_SEG_IDSEL 0x24 53a3980bf5SBenjamin Herrenschmidt #define LPC_HC_FW_RD_ACC_SIZE 0x28 54a3980bf5SBenjamin Herrenschmidt #define LPC_HC_FW_RD_1B 0x00000000 55a3980bf5SBenjamin Herrenschmidt #define LPC_HC_FW_RD_2B 0x01000000 56a3980bf5SBenjamin Herrenschmidt #define LPC_HC_FW_RD_4B 0x02000000 57a3980bf5SBenjamin Herrenschmidt #define LPC_HC_FW_RD_16B 0x04000000 58a3980bf5SBenjamin Herrenschmidt #define LPC_HC_FW_RD_128B 0x07000000 59a3980bf5SBenjamin Herrenschmidt #define LPC_HC_IRQSER_CTRL 0x30 60a3980bf5SBenjamin Herrenschmidt #define LPC_HC_IRQSER_EN 0x80000000 61a3980bf5SBenjamin Herrenschmidt #define LPC_HC_IRQSER_QMODE 0x40000000 62a3980bf5SBenjamin Herrenschmidt #define LPC_HC_IRQSER_START_MASK 0x03000000 63a3980bf5SBenjamin Herrenschmidt #define LPC_HC_IRQSER_START_4CLK 0x00000000 64a3980bf5SBenjamin Herrenschmidt #define LPC_HC_IRQSER_START_6CLK 0x01000000 65a3980bf5SBenjamin Herrenschmidt #define LPC_HC_IRQSER_START_8CLK 0x02000000 66a3980bf5SBenjamin Herrenschmidt #define LPC_HC_IRQMASK 0x34 /* same bit defs as LPC_HC_IRQSTAT */ 67a3980bf5SBenjamin Herrenschmidt #define LPC_HC_IRQSTAT 0x38 68a3980bf5SBenjamin Herrenschmidt #define LPC_HC_IRQ_SERIRQ0 0x80000000 /* all bits down to ... */ 69a3980bf5SBenjamin Herrenschmidt #define LPC_HC_IRQ_SERIRQ16 0x00008000 /* IRQ16=IOCHK#, IRQ2=SMI# */ 70a3980bf5SBenjamin Herrenschmidt #define LPC_HC_IRQ_SERIRQ_ALL 0xffff8000 71a3980bf5SBenjamin Herrenschmidt #define LPC_HC_IRQ_LRESET 0x00000400 72a3980bf5SBenjamin Herrenschmidt #define LPC_HC_IRQ_SYNC_ABNORM_ERR 0x00000080 73a3980bf5SBenjamin Herrenschmidt #define LPC_HC_IRQ_SYNC_NORESP_ERR 0x00000040 74a3980bf5SBenjamin Herrenschmidt #define LPC_HC_IRQ_SYNC_NORM_ERR 0x00000020 75a3980bf5SBenjamin Herrenschmidt #define LPC_HC_IRQ_SYNC_TIMEOUT_ERR 0x00000010 76a3980bf5SBenjamin Herrenschmidt #define LPC_HC_IRQ_SYNC_TARG_TAR_ERR 0x00000008 77a3980bf5SBenjamin Herrenschmidt #define LPC_HC_IRQ_SYNC_BM_TAR_ERR 0x00000004 78a3980bf5SBenjamin Herrenschmidt #define LPC_HC_IRQ_SYNC_BM0_REQ 0x00000002 79a3980bf5SBenjamin Herrenschmidt #define LPC_HC_IRQ_SYNC_BM1_REQ 0x00000001 80a3980bf5SBenjamin Herrenschmidt #define LPC_HC_ERROR_ADDRESS 0x40 81a3980bf5SBenjamin Herrenschmidt 82a3980bf5SBenjamin Herrenschmidt #define LPC_OPB_SIZE 0x100000000ull 83a3980bf5SBenjamin Herrenschmidt 84a3980bf5SBenjamin Herrenschmidt #define ISA_IO_SIZE 0x00010000 85a3980bf5SBenjamin Herrenschmidt #define ISA_MEM_SIZE 0x10000000 86d61c2857SCédric Le Goater #define ISA_FW_SIZE 0x10000000 87a3980bf5SBenjamin Herrenschmidt #define LPC_IO_OPB_ADDR 0xd0010000 88a3980bf5SBenjamin Herrenschmidt #define LPC_IO_OPB_SIZE 0x00010000 89a3980bf5SBenjamin Herrenschmidt #define LPC_MEM_OPB_ADDR 0xe0010000 90a3980bf5SBenjamin Herrenschmidt #define LPC_MEM_OPB_SIZE 0x10000000 91a3980bf5SBenjamin Herrenschmidt #define LPC_FW_OPB_ADDR 0xf0000000 92a3980bf5SBenjamin Herrenschmidt #define LPC_FW_OPB_SIZE 0x10000000 93a3980bf5SBenjamin Herrenschmidt 94a3980bf5SBenjamin Herrenschmidt #define LPC_OPB_REGS_OPB_ADDR 0xc0010000 956f89f48eSCédric Le Goater #define LPC_OPB_REGS_OPB_SIZE 0x00000060 966f89f48eSCédric Le Goater #define LPC_OPB_REGS_OPBA_ADDR 0xc0011000 976f89f48eSCédric Le Goater #define LPC_OPB_REGS_OPBA_SIZE 0x00000008 98a3980bf5SBenjamin Herrenschmidt #define LPC_HC_REGS_OPB_ADDR 0xc0012000 996f89f48eSCédric Le Goater #define LPC_HC_REGS_OPB_SIZE 0x00000100 100a3980bf5SBenjamin Herrenschmidt 101b168a138SCédric Le Goater static int pnv_lpc_dt_xscom(PnvXScomInterface *dev, void *fdt, int xscom_offset) 102a3980bf5SBenjamin Herrenschmidt { 103a3980bf5SBenjamin Herrenschmidt const char compat[] = "ibm,power8-lpc\0ibm,lpc"; 104a3980bf5SBenjamin Herrenschmidt char *name; 105a3980bf5SBenjamin Herrenschmidt int offset; 106a3980bf5SBenjamin Herrenschmidt uint32_t lpc_pcba = PNV_XSCOM_LPC_BASE; 107a3980bf5SBenjamin Herrenschmidt uint32_t reg[] = { 108a3980bf5SBenjamin Herrenschmidt cpu_to_be32(lpc_pcba), 109a3980bf5SBenjamin Herrenschmidt cpu_to_be32(PNV_XSCOM_LPC_SIZE) 110a3980bf5SBenjamin Herrenschmidt }; 111a3980bf5SBenjamin Herrenschmidt 112a3980bf5SBenjamin Herrenschmidt name = g_strdup_printf("isa@%x", lpc_pcba); 113a3980bf5SBenjamin Herrenschmidt offset = fdt_add_subnode(fdt, xscom_offset, name); 114a3980bf5SBenjamin Herrenschmidt _FDT(offset); 115a3980bf5SBenjamin Herrenschmidt g_free(name); 116a3980bf5SBenjamin Herrenschmidt 117a3980bf5SBenjamin Herrenschmidt _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); 118a3980bf5SBenjamin Herrenschmidt _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2))); 119a3980bf5SBenjamin Herrenschmidt _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1))); 120a3980bf5SBenjamin Herrenschmidt _FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat)))); 121a3980bf5SBenjamin Herrenschmidt return 0; 122a3980bf5SBenjamin Herrenschmidt } 123a3980bf5SBenjamin Herrenschmidt 12415376c66SCédric Le Goater /* POWER9 only */ 12515376c66SCédric Le Goater int pnv_dt_lpc(PnvChip *chip, void *fdt, int root_offset) 12615376c66SCédric Le Goater { 12715376c66SCédric Le Goater const char compat[] = "ibm,power9-lpcm-opb\0simple-bus"; 12815376c66SCédric Le Goater const char lpc_compat[] = "ibm,power9-lpc\0ibm,lpc"; 12915376c66SCédric Le Goater char *name; 13015376c66SCédric Le Goater int offset, lpcm_offset; 13115376c66SCédric Le Goater uint64_t lpcm_addr = PNV9_LPCM_BASE(chip); 13215376c66SCédric Le Goater uint32_t opb_ranges[8] = { 0, 13315376c66SCédric Le Goater cpu_to_be32(lpcm_addr >> 32), 13415376c66SCédric Le Goater cpu_to_be32((uint32_t)lpcm_addr), 13515376c66SCédric Le Goater cpu_to_be32(PNV9_LPCM_SIZE / 2), 13615376c66SCédric Le Goater cpu_to_be32(PNV9_LPCM_SIZE / 2), 13715376c66SCédric Le Goater cpu_to_be32(lpcm_addr >> 32), 13815376c66SCédric Le Goater cpu_to_be32(PNV9_LPCM_SIZE / 2), 13915376c66SCédric Le Goater cpu_to_be32(PNV9_LPCM_SIZE / 2), 14015376c66SCédric Le Goater }; 14115376c66SCédric Le Goater uint32_t opb_reg[4] = { cpu_to_be32(lpcm_addr >> 32), 14215376c66SCédric Le Goater cpu_to_be32((uint32_t)lpcm_addr), 14315376c66SCédric Le Goater cpu_to_be32(PNV9_LPCM_SIZE >> 32), 14415376c66SCédric Le Goater cpu_to_be32((uint32_t)PNV9_LPCM_SIZE), 14515376c66SCédric Le Goater }; 14615376c66SCédric Le Goater uint32_t reg[2]; 14715376c66SCédric Le Goater 14815376c66SCédric Le Goater /* 14915376c66SCédric Le Goater * OPB bus 15015376c66SCédric Le Goater */ 15115376c66SCédric Le Goater name = g_strdup_printf("lpcm-opb@%"PRIx64, lpcm_addr); 15215376c66SCédric Le Goater lpcm_offset = fdt_add_subnode(fdt, root_offset, name); 15315376c66SCédric Le Goater _FDT(lpcm_offset); 15415376c66SCédric Le Goater g_free(name); 15515376c66SCédric Le Goater 15615376c66SCédric Le Goater _FDT((fdt_setprop(fdt, lpcm_offset, "reg", opb_reg, sizeof(opb_reg)))); 15715376c66SCédric Le Goater _FDT((fdt_setprop_cell(fdt, lpcm_offset, "#address-cells", 1))); 15815376c66SCédric Le Goater _FDT((fdt_setprop_cell(fdt, lpcm_offset, "#size-cells", 1))); 15915376c66SCédric Le Goater _FDT((fdt_setprop(fdt, lpcm_offset, "compatible", compat, sizeof(compat)))); 16015376c66SCédric Le Goater _FDT((fdt_setprop_cell(fdt, lpcm_offset, "ibm,chip-id", chip->chip_id))); 16115376c66SCédric Le Goater _FDT((fdt_setprop(fdt, lpcm_offset, "ranges", opb_ranges, 16215376c66SCédric Le Goater sizeof(opb_ranges)))); 16315376c66SCédric Le Goater 16415376c66SCédric Le Goater /* 16515376c66SCédric Le Goater * OPB Master registers 16615376c66SCédric Le Goater */ 16715376c66SCédric Le Goater name = g_strdup_printf("opb-master@%x", LPC_OPB_REGS_OPB_ADDR); 16815376c66SCédric Le Goater offset = fdt_add_subnode(fdt, lpcm_offset, name); 16915376c66SCédric Le Goater _FDT(offset); 17015376c66SCédric Le Goater g_free(name); 17115376c66SCédric Le Goater 17215376c66SCédric Le Goater reg[0] = cpu_to_be32(LPC_OPB_REGS_OPB_ADDR); 17315376c66SCédric Le Goater reg[1] = cpu_to_be32(LPC_OPB_REGS_OPB_SIZE); 17415376c66SCédric Le Goater _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); 17515376c66SCédric Le Goater _FDT((fdt_setprop_string(fdt, offset, "compatible", 17615376c66SCédric Le Goater "ibm,power9-lpcm-opb-master"))); 17715376c66SCédric Le Goater 17815376c66SCédric Le Goater /* 17915376c66SCédric Le Goater * OPB arbitrer registers 18015376c66SCédric Le Goater */ 18115376c66SCédric Le Goater name = g_strdup_printf("opb-arbitrer@%x", LPC_OPB_REGS_OPBA_ADDR); 18215376c66SCédric Le Goater offset = fdt_add_subnode(fdt, lpcm_offset, name); 18315376c66SCédric Le Goater _FDT(offset); 18415376c66SCédric Le Goater g_free(name); 18515376c66SCédric Le Goater 18615376c66SCédric Le Goater reg[0] = cpu_to_be32(LPC_OPB_REGS_OPBA_ADDR); 18715376c66SCédric Le Goater reg[1] = cpu_to_be32(LPC_OPB_REGS_OPBA_SIZE); 18815376c66SCédric Le Goater _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); 18915376c66SCédric Le Goater _FDT((fdt_setprop_string(fdt, offset, "compatible", 19015376c66SCédric Le Goater "ibm,power9-lpcm-opb-arbiter"))); 19115376c66SCédric Le Goater 19215376c66SCédric Le Goater /* 19315376c66SCédric Le Goater * LPC Host Controller registers 19415376c66SCédric Le Goater */ 19515376c66SCédric Le Goater name = g_strdup_printf("lpc-controller@%x", LPC_HC_REGS_OPB_ADDR); 19615376c66SCédric Le Goater offset = fdt_add_subnode(fdt, lpcm_offset, name); 19715376c66SCédric Le Goater _FDT(offset); 19815376c66SCédric Le Goater g_free(name); 19915376c66SCédric Le Goater 20015376c66SCédric Le Goater reg[0] = cpu_to_be32(LPC_HC_REGS_OPB_ADDR); 20115376c66SCédric Le Goater reg[1] = cpu_to_be32(LPC_HC_REGS_OPB_SIZE); 20215376c66SCédric Le Goater _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); 20315376c66SCédric Le Goater _FDT((fdt_setprop_string(fdt, offset, "compatible", 20415376c66SCédric Le Goater "ibm,power9-lpc-controller"))); 20515376c66SCédric Le Goater 20615376c66SCédric Le Goater name = g_strdup_printf("lpc@0"); 20715376c66SCédric Le Goater offset = fdt_add_subnode(fdt, lpcm_offset, name); 20815376c66SCédric Le Goater _FDT(offset); 20915376c66SCédric Le Goater g_free(name); 21015376c66SCédric Le Goater _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2))); 21115376c66SCédric Le Goater _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1))); 21215376c66SCédric Le Goater _FDT((fdt_setprop(fdt, offset, "compatible", lpc_compat, 21315376c66SCédric Le Goater sizeof(lpc_compat)))); 21415376c66SCédric Le Goater 21515376c66SCédric Le Goater return 0; 21615376c66SCédric Le Goater } 21715376c66SCédric Le Goater 218a3980bf5SBenjamin Herrenschmidt /* 219a3980bf5SBenjamin Herrenschmidt * These read/write handlers of the OPB address space should be common 220a3980bf5SBenjamin Herrenschmidt * with the P9 LPC Controller which uses direct MMIOs. 221a3980bf5SBenjamin Herrenschmidt * 222a3980bf5SBenjamin Herrenschmidt * TODO: rework to use address_space_stq() and address_space_ldq() 223a3980bf5SBenjamin Herrenschmidt * instead. 224a3980bf5SBenjamin Herrenschmidt */ 225a3980bf5SBenjamin Herrenschmidt static bool opb_read(PnvLpcController *lpc, uint32_t addr, uint8_t *data, 226a3980bf5SBenjamin Herrenschmidt int sz) 227a3980bf5SBenjamin Herrenschmidt { 228a3980bf5SBenjamin Herrenschmidt /* XXX Handle access size limits and FW read caching here */ 2294a4ff4c5SLaurent Vivier return !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, 230a3980bf5SBenjamin Herrenschmidt data, sz, false); 231a3980bf5SBenjamin Herrenschmidt } 232a3980bf5SBenjamin Herrenschmidt 233a3980bf5SBenjamin Herrenschmidt static bool opb_write(PnvLpcController *lpc, uint32_t addr, uint8_t *data, 234a3980bf5SBenjamin Herrenschmidt int sz) 235a3980bf5SBenjamin Herrenschmidt { 236a3980bf5SBenjamin Herrenschmidt /* XXX Handle access size limits here */ 2374a4ff4c5SLaurent Vivier return !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, 238a3980bf5SBenjamin Herrenschmidt data, sz, true); 239a3980bf5SBenjamin Herrenschmidt } 240a3980bf5SBenjamin Herrenschmidt 241a6a444a8SCédric Le Goater #define ECCB_CTL_READ PPC_BIT(15) 242a3980bf5SBenjamin Herrenschmidt #define ECCB_CTL_SZ_LSH (63 - 7) 243a6a444a8SCédric Le Goater #define ECCB_CTL_SZ_MASK PPC_BITMASK(4, 7) 244a6a444a8SCédric Le Goater #define ECCB_CTL_ADDR_MASK PPC_BITMASK(32, 63) 245a3980bf5SBenjamin Herrenschmidt 246a6a444a8SCédric Le Goater #define ECCB_STAT_OP_DONE PPC_BIT(52) 247a6a444a8SCédric Le Goater #define ECCB_STAT_OP_ERR PPC_BIT(52) 248a3980bf5SBenjamin Herrenschmidt #define ECCB_STAT_RD_DATA_LSH (63 - 37) 249a3980bf5SBenjamin Herrenschmidt #define ECCB_STAT_RD_DATA_MASK (0xffffffff << ECCB_STAT_RD_DATA_LSH) 250a3980bf5SBenjamin Herrenschmidt 251a3980bf5SBenjamin Herrenschmidt static void pnv_lpc_do_eccb(PnvLpcController *lpc, uint64_t cmd) 252a3980bf5SBenjamin Herrenschmidt { 253a3980bf5SBenjamin Herrenschmidt /* XXX Check for magic bits at the top, addr size etc... */ 254a3980bf5SBenjamin Herrenschmidt unsigned int sz = (cmd & ECCB_CTL_SZ_MASK) >> ECCB_CTL_SZ_LSH; 255a3980bf5SBenjamin Herrenschmidt uint32_t opb_addr = cmd & ECCB_CTL_ADDR_MASK; 256d07945e7SPrasad J Pandit uint8_t data[8]; 257a3980bf5SBenjamin Herrenschmidt bool success; 258a3980bf5SBenjamin Herrenschmidt 259d07945e7SPrasad J Pandit if (sz > sizeof(data)) { 260d07945e7SPrasad J Pandit qemu_log_mask(LOG_GUEST_ERROR, 261d07945e7SPrasad J Pandit "ECCB: invalid operation at @0x%08x size %d\n", opb_addr, sz); 262d07945e7SPrasad J Pandit return; 263d07945e7SPrasad J Pandit } 264d07945e7SPrasad J Pandit 265a3980bf5SBenjamin Herrenschmidt if (cmd & ECCB_CTL_READ) { 266a3980bf5SBenjamin Herrenschmidt success = opb_read(lpc, opb_addr, data, sz); 267a3980bf5SBenjamin Herrenschmidt if (success) { 268a3980bf5SBenjamin Herrenschmidt lpc->eccb_stat_reg = ECCB_STAT_OP_DONE | 269a3980bf5SBenjamin Herrenschmidt (((uint64_t)data[0]) << 24 | 270a3980bf5SBenjamin Herrenschmidt ((uint64_t)data[1]) << 16 | 271a3980bf5SBenjamin Herrenschmidt ((uint64_t)data[2]) << 8 | 272a3980bf5SBenjamin Herrenschmidt ((uint64_t)data[3])) << ECCB_STAT_RD_DATA_LSH; 273a3980bf5SBenjamin Herrenschmidt } else { 274a3980bf5SBenjamin Herrenschmidt lpc->eccb_stat_reg = ECCB_STAT_OP_DONE | 275a3980bf5SBenjamin Herrenschmidt (0xffffffffull << ECCB_STAT_RD_DATA_LSH); 276a3980bf5SBenjamin Herrenschmidt } 277a3980bf5SBenjamin Herrenschmidt } else { 278a3980bf5SBenjamin Herrenschmidt data[0] = lpc->eccb_data_reg >> 24; 279a3980bf5SBenjamin Herrenschmidt data[1] = lpc->eccb_data_reg >> 16; 280a3980bf5SBenjamin Herrenschmidt data[2] = lpc->eccb_data_reg >> 8; 281a3980bf5SBenjamin Herrenschmidt data[3] = lpc->eccb_data_reg; 282a3980bf5SBenjamin Herrenschmidt 283a3980bf5SBenjamin Herrenschmidt success = opb_write(lpc, opb_addr, data, sz); 284a3980bf5SBenjamin Herrenschmidt lpc->eccb_stat_reg = ECCB_STAT_OP_DONE; 285a3980bf5SBenjamin Herrenschmidt } 286a3980bf5SBenjamin Herrenschmidt /* XXX Which error bit (if any) to signal OPB error ? */ 287a3980bf5SBenjamin Herrenschmidt } 288a3980bf5SBenjamin Herrenschmidt 289a3980bf5SBenjamin Herrenschmidt static uint64_t pnv_lpc_xscom_read(void *opaque, hwaddr addr, unsigned size) 290a3980bf5SBenjamin Herrenschmidt { 291a3980bf5SBenjamin Herrenschmidt PnvLpcController *lpc = PNV_LPC(opaque); 292a3980bf5SBenjamin Herrenschmidt uint32_t offset = addr >> 3; 293a3980bf5SBenjamin Herrenschmidt uint64_t val = 0; 294a3980bf5SBenjamin Herrenschmidt 295a3980bf5SBenjamin Herrenschmidt switch (offset & 3) { 296a3980bf5SBenjamin Herrenschmidt case ECCB_CTL: 297a3980bf5SBenjamin Herrenschmidt case ECCB_RESET: 298a3980bf5SBenjamin Herrenschmidt val = 0; 299a3980bf5SBenjamin Herrenschmidt break; 300a3980bf5SBenjamin Herrenschmidt case ECCB_STAT: 301a3980bf5SBenjamin Herrenschmidt val = lpc->eccb_stat_reg; 302a3980bf5SBenjamin Herrenschmidt lpc->eccb_stat_reg = 0; 303a3980bf5SBenjamin Herrenschmidt break; 304a3980bf5SBenjamin Herrenschmidt case ECCB_DATA: 305a3980bf5SBenjamin Herrenschmidt val = ((uint64_t)lpc->eccb_data_reg) << 32; 306a3980bf5SBenjamin Herrenschmidt break; 307a3980bf5SBenjamin Herrenschmidt } 308a3980bf5SBenjamin Herrenschmidt return val; 309a3980bf5SBenjamin Herrenschmidt } 310a3980bf5SBenjamin Herrenschmidt 311a3980bf5SBenjamin Herrenschmidt static void pnv_lpc_xscom_write(void *opaque, hwaddr addr, 312a3980bf5SBenjamin Herrenschmidt uint64_t val, unsigned size) 313a3980bf5SBenjamin Herrenschmidt { 314a3980bf5SBenjamin Herrenschmidt PnvLpcController *lpc = PNV_LPC(opaque); 315a3980bf5SBenjamin Herrenschmidt uint32_t offset = addr >> 3; 316a3980bf5SBenjamin Herrenschmidt 317a3980bf5SBenjamin Herrenschmidt switch (offset & 3) { 318a3980bf5SBenjamin Herrenschmidt case ECCB_CTL: 319a3980bf5SBenjamin Herrenschmidt pnv_lpc_do_eccb(lpc, val); 320a3980bf5SBenjamin Herrenschmidt break; 321a3980bf5SBenjamin Herrenschmidt case ECCB_RESET: 322a3980bf5SBenjamin Herrenschmidt /* XXXX */ 323a3980bf5SBenjamin Herrenschmidt break; 324a3980bf5SBenjamin Herrenschmidt case ECCB_STAT: 325a3980bf5SBenjamin Herrenschmidt break; 326a3980bf5SBenjamin Herrenschmidt case ECCB_DATA: 327a3980bf5SBenjamin Herrenschmidt lpc->eccb_data_reg = val >> 32; 328a3980bf5SBenjamin Herrenschmidt break; 329a3980bf5SBenjamin Herrenschmidt } 330a3980bf5SBenjamin Herrenschmidt } 331a3980bf5SBenjamin Herrenschmidt 332a3980bf5SBenjamin Herrenschmidt static const MemoryRegionOps pnv_lpc_xscom_ops = { 333a3980bf5SBenjamin Herrenschmidt .read = pnv_lpc_xscom_read, 334a3980bf5SBenjamin Herrenschmidt .write = pnv_lpc_xscom_write, 335a3980bf5SBenjamin Herrenschmidt .valid.min_access_size = 8, 336a3980bf5SBenjamin Herrenschmidt .valid.max_access_size = 8, 337a3980bf5SBenjamin Herrenschmidt .impl.min_access_size = 8, 338a3980bf5SBenjamin Herrenschmidt .impl.max_access_size = 8, 339a3980bf5SBenjamin Herrenschmidt .endianness = DEVICE_BIG_ENDIAN, 340a3980bf5SBenjamin Herrenschmidt }; 341a3980bf5SBenjamin Herrenschmidt 34215376c66SCédric Le Goater static uint64_t pnv_lpc_mmio_read(void *opaque, hwaddr addr, unsigned size) 34315376c66SCédric Le Goater { 34415376c66SCédric Le Goater PnvLpcController *lpc = PNV_LPC(opaque); 34515376c66SCédric Le Goater uint64_t val = 0; 34615376c66SCédric Le Goater uint32_t opb_addr = addr & ECCB_CTL_ADDR_MASK; 34715376c66SCédric Le Goater MemTxResult result; 34815376c66SCédric Le Goater 34915376c66SCédric Le Goater switch (size) { 35015376c66SCédric Le Goater case 4: 35115376c66SCédric Le Goater val = address_space_ldl(&lpc->opb_as, opb_addr, MEMTXATTRS_UNSPECIFIED, 35215376c66SCédric Le Goater &result); 35315376c66SCédric Le Goater break; 35415376c66SCédric Le Goater case 1: 35515376c66SCédric Le Goater val = address_space_ldub(&lpc->opb_as, opb_addr, MEMTXATTRS_UNSPECIFIED, 35615376c66SCédric Le Goater &result); 35715376c66SCédric Le Goater break; 35815376c66SCédric Le Goater default: 35915376c66SCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "OPB read failed at @0x%" 36015376c66SCédric Le Goater HWADDR_PRIx " invalid size %d\n", addr, size); 36115376c66SCédric Le Goater return 0; 36215376c66SCédric Le Goater } 36315376c66SCédric Le Goater 36415376c66SCédric Le Goater if (result != MEMTX_OK) { 36515376c66SCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "OPB read failed at @0x%" 36615376c66SCédric Le Goater HWADDR_PRIx "\n", addr); 36715376c66SCédric Le Goater } 36815376c66SCédric Le Goater 36915376c66SCédric Le Goater return val; 37015376c66SCédric Le Goater } 37115376c66SCédric Le Goater 37215376c66SCédric Le Goater static void pnv_lpc_mmio_write(void *opaque, hwaddr addr, 37315376c66SCédric Le Goater uint64_t val, unsigned size) 37415376c66SCédric Le Goater { 37515376c66SCédric Le Goater PnvLpcController *lpc = PNV_LPC(opaque); 37615376c66SCédric Le Goater uint32_t opb_addr = addr & ECCB_CTL_ADDR_MASK; 37715376c66SCédric Le Goater MemTxResult result; 37815376c66SCédric Le Goater 37915376c66SCédric Le Goater switch (size) { 38015376c66SCédric Le Goater case 4: 38115376c66SCédric Le Goater address_space_stl(&lpc->opb_as, opb_addr, val, MEMTXATTRS_UNSPECIFIED, 38215376c66SCédric Le Goater &result); 38315376c66SCédric Le Goater break; 38415376c66SCédric Le Goater case 1: 38515376c66SCédric Le Goater address_space_stb(&lpc->opb_as, opb_addr, val, MEMTXATTRS_UNSPECIFIED, 38615376c66SCédric Le Goater &result); 38715376c66SCédric Le Goater break; 38815376c66SCédric Le Goater default: 38915376c66SCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "OPB write failed at @0x%" 39015376c66SCédric Le Goater HWADDR_PRIx " invalid size %d\n", addr, size); 39115376c66SCédric Le Goater return; 39215376c66SCédric Le Goater } 39315376c66SCédric Le Goater 39415376c66SCédric Le Goater if (result != MEMTX_OK) { 39515376c66SCédric Le Goater qemu_log_mask(LOG_GUEST_ERROR, "OPB write failed at @0x%" 39615376c66SCédric Le Goater HWADDR_PRIx "\n", addr); 39715376c66SCédric Le Goater } 39815376c66SCédric Le Goater } 39915376c66SCédric Le Goater 40015376c66SCédric Le Goater static const MemoryRegionOps pnv_lpc_mmio_ops = { 40115376c66SCédric Le Goater .read = pnv_lpc_mmio_read, 40215376c66SCédric Le Goater .write = pnv_lpc_mmio_write, 40315376c66SCédric Le Goater .impl = { 40415376c66SCédric Le Goater .min_access_size = 1, 40515376c66SCédric Le Goater .max_access_size = 4, 40615376c66SCédric Le Goater }, 40715376c66SCédric Le Goater .endianness = DEVICE_BIG_ENDIAN, 40815376c66SCédric Le Goater }; 40915376c66SCédric Le Goater 4104d1df88bSBenjamin Herrenschmidt static void pnv_lpc_eval_irqs(PnvLpcController *lpc) 4114d1df88bSBenjamin Herrenschmidt { 4124d1df88bSBenjamin Herrenschmidt bool lpc_to_opb_irq = false; 41382514be2SCédric Le Goater PnvLpcClass *plc = PNV_LPC_GET_CLASS(lpc); 4144d1df88bSBenjamin Herrenschmidt 4154d1df88bSBenjamin Herrenschmidt /* Update LPC controller to OPB line */ 4164d1df88bSBenjamin Herrenschmidt if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) { 4174d1df88bSBenjamin Herrenschmidt uint32_t irqs; 4184d1df88bSBenjamin Herrenschmidt 4194d1df88bSBenjamin Herrenschmidt irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask; 4204d1df88bSBenjamin Herrenschmidt lpc_to_opb_irq = (irqs != 0); 4214d1df88bSBenjamin Herrenschmidt } 4224d1df88bSBenjamin Herrenschmidt 4234d1df88bSBenjamin Herrenschmidt /* We don't honor the polarity register, it's pointless and unused 4244d1df88bSBenjamin Herrenschmidt * anyway 4254d1df88bSBenjamin Herrenschmidt */ 4264d1df88bSBenjamin Herrenschmidt if (lpc_to_opb_irq) { 4274d1df88bSBenjamin Herrenschmidt lpc->opb_irq_input |= OPB_MASTER_IRQ_LPC; 4284d1df88bSBenjamin Herrenschmidt } else { 4294d1df88bSBenjamin Herrenschmidt lpc->opb_irq_input &= ~OPB_MASTER_IRQ_LPC; 4304d1df88bSBenjamin Herrenschmidt } 4314d1df88bSBenjamin Herrenschmidt 4324d1df88bSBenjamin Herrenschmidt /* Update OPB internal latch */ 4334d1df88bSBenjamin Herrenschmidt lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask; 4344d1df88bSBenjamin Herrenschmidt 4354d1df88bSBenjamin Herrenschmidt /* Reflect the interrupt */ 43682514be2SCédric Le Goater pnv_psi_irq_set(lpc->psi, plc->psi_irq, lpc->opb_irq_stat != 0); 4374d1df88bSBenjamin Herrenschmidt } 4384d1df88bSBenjamin Herrenschmidt 439a3980bf5SBenjamin Herrenschmidt static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size) 440a3980bf5SBenjamin Herrenschmidt { 441a3980bf5SBenjamin Herrenschmidt PnvLpcController *lpc = opaque; 442a3980bf5SBenjamin Herrenschmidt uint64_t val = 0xfffffffffffffffful; 443a3980bf5SBenjamin Herrenschmidt 444a3980bf5SBenjamin Herrenschmidt switch (addr) { 445a3980bf5SBenjamin Herrenschmidt case LPC_HC_FW_SEG_IDSEL: 446a3980bf5SBenjamin Herrenschmidt val = lpc->lpc_hc_fw_seg_idsel; 447a3980bf5SBenjamin Herrenschmidt break; 448a3980bf5SBenjamin Herrenschmidt case LPC_HC_FW_RD_ACC_SIZE: 449a3980bf5SBenjamin Herrenschmidt val = lpc->lpc_hc_fw_rd_acc_size; 450a3980bf5SBenjamin Herrenschmidt break; 451a3980bf5SBenjamin Herrenschmidt case LPC_HC_IRQSER_CTRL: 452a3980bf5SBenjamin Herrenschmidt val = lpc->lpc_hc_irqser_ctrl; 453a3980bf5SBenjamin Herrenschmidt break; 454a3980bf5SBenjamin Herrenschmidt case LPC_HC_IRQMASK: 455a3980bf5SBenjamin Herrenschmidt val = lpc->lpc_hc_irqmask; 456a3980bf5SBenjamin Herrenschmidt break; 457a3980bf5SBenjamin Herrenschmidt case LPC_HC_IRQSTAT: 458a3980bf5SBenjamin Herrenschmidt val = lpc->lpc_hc_irqstat; 459a3980bf5SBenjamin Herrenschmidt break; 460a3980bf5SBenjamin Herrenschmidt case LPC_HC_ERROR_ADDRESS: 461a3980bf5SBenjamin Herrenschmidt val = lpc->lpc_hc_error_addr; 462a3980bf5SBenjamin Herrenschmidt break; 463a3980bf5SBenjamin Herrenschmidt default: 464cdbaf8cdSCédric Le Goater qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented register: 0x%" 465a3980bf5SBenjamin Herrenschmidt HWADDR_PRIx "\n", addr); 466a3980bf5SBenjamin Herrenschmidt } 467a3980bf5SBenjamin Herrenschmidt return val; 468a3980bf5SBenjamin Herrenschmidt } 469a3980bf5SBenjamin Herrenschmidt 470a3980bf5SBenjamin Herrenschmidt static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val, 471a3980bf5SBenjamin Herrenschmidt unsigned size) 472a3980bf5SBenjamin Herrenschmidt { 473a3980bf5SBenjamin Herrenschmidt PnvLpcController *lpc = opaque; 474a3980bf5SBenjamin Herrenschmidt 475a3980bf5SBenjamin Herrenschmidt /* XXX Filter out reserved bits */ 476a3980bf5SBenjamin Herrenschmidt 477a3980bf5SBenjamin Herrenschmidt switch (addr) { 478a3980bf5SBenjamin Herrenschmidt case LPC_HC_FW_SEG_IDSEL: 479a3980bf5SBenjamin Herrenschmidt /* XXX Actually figure out how that works as this impact 480a3980bf5SBenjamin Herrenschmidt * memory regions/aliases 481a3980bf5SBenjamin Herrenschmidt */ 482a3980bf5SBenjamin Herrenschmidt lpc->lpc_hc_fw_seg_idsel = val; 483a3980bf5SBenjamin Herrenschmidt break; 484a3980bf5SBenjamin Herrenschmidt case LPC_HC_FW_RD_ACC_SIZE: 485a3980bf5SBenjamin Herrenschmidt lpc->lpc_hc_fw_rd_acc_size = val; 486a3980bf5SBenjamin Herrenschmidt break; 487a3980bf5SBenjamin Herrenschmidt case LPC_HC_IRQSER_CTRL: 488a3980bf5SBenjamin Herrenschmidt lpc->lpc_hc_irqser_ctrl = val; 4894d1df88bSBenjamin Herrenschmidt pnv_lpc_eval_irqs(lpc); 490a3980bf5SBenjamin Herrenschmidt break; 491a3980bf5SBenjamin Herrenschmidt case LPC_HC_IRQMASK: 492a3980bf5SBenjamin Herrenschmidt lpc->lpc_hc_irqmask = val; 4934d1df88bSBenjamin Herrenschmidt pnv_lpc_eval_irqs(lpc); 494a3980bf5SBenjamin Herrenschmidt break; 495a3980bf5SBenjamin Herrenschmidt case LPC_HC_IRQSTAT: 496a3980bf5SBenjamin Herrenschmidt lpc->lpc_hc_irqstat &= ~val; 4974d1df88bSBenjamin Herrenschmidt pnv_lpc_eval_irqs(lpc); 498a3980bf5SBenjamin Herrenschmidt break; 499a3980bf5SBenjamin Herrenschmidt case LPC_HC_ERROR_ADDRESS: 500a3980bf5SBenjamin Herrenschmidt break; 501a3980bf5SBenjamin Herrenschmidt default: 502cdbaf8cdSCédric Le Goater qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented register: 0x%" 503a3980bf5SBenjamin Herrenschmidt HWADDR_PRIx "\n", addr); 504a3980bf5SBenjamin Herrenschmidt } 505a3980bf5SBenjamin Herrenschmidt } 506a3980bf5SBenjamin Herrenschmidt 507a3980bf5SBenjamin Herrenschmidt static const MemoryRegionOps lpc_hc_ops = { 508a3980bf5SBenjamin Herrenschmidt .read = lpc_hc_read, 509a3980bf5SBenjamin Herrenschmidt .write = lpc_hc_write, 510a3980bf5SBenjamin Herrenschmidt .endianness = DEVICE_BIG_ENDIAN, 511a3980bf5SBenjamin Herrenschmidt .valid = { 512a3980bf5SBenjamin Herrenschmidt .min_access_size = 4, 513a3980bf5SBenjamin Herrenschmidt .max_access_size = 4, 514a3980bf5SBenjamin Herrenschmidt }, 515a3980bf5SBenjamin Herrenschmidt .impl = { 516a3980bf5SBenjamin Herrenschmidt .min_access_size = 4, 517a3980bf5SBenjamin Herrenschmidt .max_access_size = 4, 518a3980bf5SBenjamin Herrenschmidt }, 519a3980bf5SBenjamin Herrenschmidt }; 520a3980bf5SBenjamin Herrenschmidt 521a3980bf5SBenjamin Herrenschmidt static uint64_t opb_master_read(void *opaque, hwaddr addr, unsigned size) 522a3980bf5SBenjamin Herrenschmidt { 523a3980bf5SBenjamin Herrenschmidt PnvLpcController *lpc = opaque; 524a3980bf5SBenjamin Herrenschmidt uint64_t val = 0xfffffffffffffffful; 525a3980bf5SBenjamin Herrenschmidt 526a3980bf5SBenjamin Herrenschmidt switch (addr) { 5278207b906SCédric Le Goater case OPB_MASTER_LS_ROUTE0: /* TODO */ 5288207b906SCédric Le Goater val = lpc->opb_irq_route0; 5298207b906SCédric Le Goater break; 5308207b906SCédric Le Goater case OPB_MASTER_LS_ROUTE1: /* TODO */ 5318207b906SCédric Le Goater val = lpc->opb_irq_route1; 5328207b906SCédric Le Goater break; 533a3980bf5SBenjamin Herrenschmidt case OPB_MASTER_LS_IRQ_STAT: 534a3980bf5SBenjamin Herrenschmidt val = lpc->opb_irq_stat; 535a3980bf5SBenjamin Herrenschmidt break; 536a3980bf5SBenjamin Herrenschmidt case OPB_MASTER_LS_IRQ_MASK: 537a3980bf5SBenjamin Herrenschmidt val = lpc->opb_irq_mask; 538a3980bf5SBenjamin Herrenschmidt break; 539a3980bf5SBenjamin Herrenschmidt case OPB_MASTER_LS_IRQ_POL: 540a3980bf5SBenjamin Herrenschmidt val = lpc->opb_irq_pol; 541a3980bf5SBenjamin Herrenschmidt break; 542a3980bf5SBenjamin Herrenschmidt case OPB_MASTER_LS_IRQ_INPUT: 543a3980bf5SBenjamin Herrenschmidt val = lpc->opb_irq_input; 544a3980bf5SBenjamin Herrenschmidt break; 545a3980bf5SBenjamin Herrenschmidt default: 546cdbaf8cdSCédric Le Goater qemu_log_mask(LOG_UNIMP, "OPBM: read on unimplemented register: 0x%" 547a3980bf5SBenjamin Herrenschmidt HWADDR_PRIx "\n", addr); 548a3980bf5SBenjamin Herrenschmidt } 549a3980bf5SBenjamin Herrenschmidt 550a3980bf5SBenjamin Herrenschmidt return val; 551a3980bf5SBenjamin Herrenschmidt } 552a3980bf5SBenjamin Herrenschmidt 553a3980bf5SBenjamin Herrenschmidt static void opb_master_write(void *opaque, hwaddr addr, 554a3980bf5SBenjamin Herrenschmidt uint64_t val, unsigned size) 555a3980bf5SBenjamin Herrenschmidt { 556a3980bf5SBenjamin Herrenschmidt PnvLpcController *lpc = opaque; 557a3980bf5SBenjamin Herrenschmidt 558a3980bf5SBenjamin Herrenschmidt switch (addr) { 5598207b906SCédric Le Goater case OPB_MASTER_LS_ROUTE0: /* TODO */ 5608207b906SCédric Le Goater lpc->opb_irq_route0 = val; 5618207b906SCédric Le Goater break; 5628207b906SCédric Le Goater case OPB_MASTER_LS_ROUTE1: /* TODO */ 5638207b906SCédric Le Goater lpc->opb_irq_route1 = val; 5648207b906SCédric Le Goater break; 565a3980bf5SBenjamin Herrenschmidt case OPB_MASTER_LS_IRQ_STAT: 566a3980bf5SBenjamin Herrenschmidt lpc->opb_irq_stat &= ~val; 5674d1df88bSBenjamin Herrenschmidt pnv_lpc_eval_irqs(lpc); 568a3980bf5SBenjamin Herrenschmidt break; 569a3980bf5SBenjamin Herrenschmidt case OPB_MASTER_LS_IRQ_MASK: 570a3980bf5SBenjamin Herrenschmidt lpc->opb_irq_mask = val; 5714d1df88bSBenjamin Herrenschmidt pnv_lpc_eval_irqs(lpc); 572a3980bf5SBenjamin Herrenschmidt break; 573a3980bf5SBenjamin Herrenschmidt case OPB_MASTER_LS_IRQ_POL: 574a3980bf5SBenjamin Herrenschmidt lpc->opb_irq_pol = val; 5754d1df88bSBenjamin Herrenschmidt pnv_lpc_eval_irqs(lpc); 576a3980bf5SBenjamin Herrenschmidt break; 577a3980bf5SBenjamin Herrenschmidt case OPB_MASTER_LS_IRQ_INPUT: 578a3980bf5SBenjamin Herrenschmidt /* Read only */ 579a3980bf5SBenjamin Herrenschmidt break; 580a3980bf5SBenjamin Herrenschmidt default: 581cdbaf8cdSCédric Le Goater qemu_log_mask(LOG_UNIMP, "OPBM: write on unimplemented register: 0x%" 582cdbaf8cdSCédric Le Goater HWADDR_PRIx " val=0x%08"PRIx64"\n", addr, val); 583a3980bf5SBenjamin Herrenschmidt } 584a3980bf5SBenjamin Herrenschmidt } 585a3980bf5SBenjamin Herrenschmidt 586a3980bf5SBenjamin Herrenschmidt static const MemoryRegionOps opb_master_ops = { 587a3980bf5SBenjamin Herrenschmidt .read = opb_master_read, 588a3980bf5SBenjamin Herrenschmidt .write = opb_master_write, 589a3980bf5SBenjamin Herrenschmidt .endianness = DEVICE_BIG_ENDIAN, 590a3980bf5SBenjamin Herrenschmidt .valid = { 591a3980bf5SBenjamin Herrenschmidt .min_access_size = 4, 592a3980bf5SBenjamin Herrenschmidt .max_access_size = 4, 593a3980bf5SBenjamin Herrenschmidt }, 594a3980bf5SBenjamin Herrenschmidt .impl = { 595a3980bf5SBenjamin Herrenschmidt .min_access_size = 4, 596a3980bf5SBenjamin Herrenschmidt .max_access_size = 4, 597a3980bf5SBenjamin Herrenschmidt }, 598a3980bf5SBenjamin Herrenschmidt }; 599a3980bf5SBenjamin Herrenschmidt 60082514be2SCédric Le Goater static void pnv_lpc_power8_realize(DeviceState *dev, Error **errp) 60182514be2SCédric Le Goater { 60282514be2SCédric Le Goater PnvLpcController *lpc = PNV_LPC(dev); 60382514be2SCédric Le Goater PnvLpcClass *plc = PNV_LPC_GET_CLASS(dev); 60482514be2SCédric Le Goater Error *local_err = NULL; 60582514be2SCédric Le Goater 60682514be2SCédric Le Goater plc->parent_realize(dev, &local_err); 60782514be2SCédric Le Goater if (local_err) { 60882514be2SCédric Le Goater error_propagate(errp, local_err); 60982514be2SCédric Le Goater return; 61082514be2SCédric Le Goater } 61182514be2SCédric Le Goater 61282514be2SCédric Le Goater /* P8 uses a XSCOM region for LPC registers */ 61382514be2SCédric Le Goater pnv_xscom_region_init(&lpc->xscom_regs, OBJECT(lpc), 61482514be2SCédric Le Goater &pnv_lpc_xscom_ops, lpc, "xscom-lpc", 61582514be2SCédric Le Goater PNV_XSCOM_LPC_SIZE); 61682514be2SCédric Le Goater } 61782514be2SCédric Le Goater 61882514be2SCédric Le Goater static void pnv_lpc_power8_class_init(ObjectClass *klass, void *data) 61982514be2SCédric Le Goater { 62082514be2SCédric Le Goater DeviceClass *dc = DEVICE_CLASS(klass); 62182514be2SCédric Le Goater PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); 62282514be2SCédric Le Goater PnvLpcClass *plc = PNV_LPC_CLASS(klass); 62382514be2SCédric Le Goater 62482514be2SCédric Le Goater dc->desc = "PowerNV LPC Controller POWER8"; 62582514be2SCédric Le Goater 62682514be2SCédric Le Goater xdc->dt_xscom = pnv_lpc_dt_xscom; 62782514be2SCédric Le Goater 62882514be2SCédric Le Goater plc->psi_irq = PSIHB_IRQ_LPC_I2C; 62982514be2SCédric Le Goater 63082514be2SCédric Le Goater device_class_set_parent_realize(dc, pnv_lpc_power8_realize, 63182514be2SCédric Le Goater &plc->parent_realize); 63282514be2SCédric Le Goater } 63382514be2SCédric Le Goater 63482514be2SCédric Le Goater static const TypeInfo pnv_lpc_power8_info = { 63582514be2SCédric Le Goater .name = TYPE_PNV8_LPC, 63682514be2SCédric Le Goater .parent = TYPE_PNV_LPC, 63782514be2SCédric Le Goater .instance_size = sizeof(PnvLpcController), 63882514be2SCédric Le Goater .class_init = pnv_lpc_power8_class_init, 63982514be2SCédric Le Goater .interfaces = (InterfaceInfo[]) { 64082514be2SCédric Le Goater { TYPE_PNV_XSCOM_INTERFACE }, 64182514be2SCédric Le Goater { } 64282514be2SCédric Le Goater } 64382514be2SCédric Le Goater }; 64482514be2SCédric Le Goater 64515376c66SCédric Le Goater static void pnv_lpc_power9_realize(DeviceState *dev, Error **errp) 64615376c66SCédric Le Goater { 64715376c66SCédric Le Goater PnvLpcController *lpc = PNV_LPC(dev); 64815376c66SCédric Le Goater PnvLpcClass *plc = PNV_LPC_GET_CLASS(dev); 64915376c66SCédric Le Goater Error *local_err = NULL; 65015376c66SCédric Le Goater 65115376c66SCédric Le Goater plc->parent_realize(dev, &local_err); 65215376c66SCédric Le Goater if (local_err) { 65315376c66SCédric Le Goater error_propagate(errp, local_err); 65415376c66SCédric Le Goater return; 65515376c66SCédric Le Goater } 65615376c66SCédric Le Goater 65715376c66SCédric Le Goater /* P9 uses a MMIO region */ 65815376c66SCédric Le Goater memory_region_init_io(&lpc->xscom_regs, OBJECT(lpc), &pnv_lpc_mmio_ops, 65915376c66SCédric Le Goater lpc, "lpcm", PNV9_LPCM_SIZE); 66015376c66SCédric Le Goater } 66115376c66SCédric Le Goater 66215376c66SCédric Le Goater static void pnv_lpc_power9_class_init(ObjectClass *klass, void *data) 66315376c66SCédric Le Goater { 66415376c66SCédric Le Goater DeviceClass *dc = DEVICE_CLASS(klass); 66515376c66SCédric Le Goater PnvLpcClass *plc = PNV_LPC_CLASS(klass); 66615376c66SCédric Le Goater 66715376c66SCédric Le Goater dc->desc = "PowerNV LPC Controller POWER9"; 66815376c66SCédric Le Goater 66915376c66SCédric Le Goater plc->psi_irq = PSIHB9_IRQ_LPCHC; 67015376c66SCédric Le Goater 67115376c66SCédric Le Goater device_class_set_parent_realize(dc, pnv_lpc_power9_realize, 67215376c66SCédric Le Goater &plc->parent_realize); 67315376c66SCédric Le Goater } 67415376c66SCédric Le Goater 67515376c66SCédric Le Goater static const TypeInfo pnv_lpc_power9_info = { 67615376c66SCédric Le Goater .name = TYPE_PNV9_LPC, 67715376c66SCédric Le Goater .parent = TYPE_PNV_LPC, 67815376c66SCédric Le Goater .instance_size = sizeof(PnvLpcController), 67915376c66SCédric Le Goater .class_init = pnv_lpc_power9_class_init, 68015376c66SCédric Le Goater }; 68115376c66SCédric Le Goater 682a3980bf5SBenjamin Herrenschmidt static void pnv_lpc_realize(DeviceState *dev, Error **errp) 683a3980bf5SBenjamin Herrenschmidt { 684a3980bf5SBenjamin Herrenschmidt PnvLpcController *lpc = PNV_LPC(dev); 6854d1df88bSBenjamin Herrenschmidt Object *obj; 68682514be2SCédric Le Goater Error *local_err = NULL; 68782514be2SCédric Le Goater 68882514be2SCédric Le Goater obj = object_property_get_link(OBJECT(dev), "psi", &local_err); 68982514be2SCédric Le Goater if (!obj) { 69082514be2SCédric Le Goater error_propagate(errp, local_err); 69182514be2SCédric Le Goater error_prepend(errp, "required link 'psi' not found: "); 69282514be2SCédric Le Goater return; 69382514be2SCédric Le Goater } 69482514be2SCédric Le Goater /* The LPC controller needs PSI to generate interrupts */ 69582514be2SCédric Le Goater lpc->psi = PNV_PSI(obj); 696a3980bf5SBenjamin Herrenschmidt 697a3980bf5SBenjamin Herrenschmidt /* Reg inits */ 698a3980bf5SBenjamin Herrenschmidt lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B; 699a3980bf5SBenjamin Herrenschmidt 700a3980bf5SBenjamin Herrenschmidt /* Create address space and backing MR for the OPB bus */ 701a3980bf5SBenjamin Herrenschmidt memory_region_init(&lpc->opb_mr, OBJECT(dev), "lpc-opb", 0x100000000ull); 702a3980bf5SBenjamin Herrenschmidt address_space_init(&lpc->opb_as, &lpc->opb_mr, "lpc-opb"); 703a3980bf5SBenjamin Herrenschmidt 704a3980bf5SBenjamin Herrenschmidt /* Create ISA IO and Mem space regions which are the root of 705a3980bf5SBenjamin Herrenschmidt * the ISA bus (ie, ISA address spaces). We don't create a 706a3980bf5SBenjamin Herrenschmidt * separate one for FW which we alias to memory. 707a3980bf5SBenjamin Herrenschmidt */ 708a3980bf5SBenjamin Herrenschmidt memory_region_init(&lpc->isa_io, OBJECT(dev), "isa-io", ISA_IO_SIZE); 709a3980bf5SBenjamin Herrenschmidt memory_region_init(&lpc->isa_mem, OBJECT(dev), "isa-mem", ISA_MEM_SIZE); 710d61c2857SCédric Le Goater memory_region_init(&lpc->isa_fw, OBJECT(dev), "isa-fw", ISA_FW_SIZE); 711a3980bf5SBenjamin Herrenschmidt 712a3980bf5SBenjamin Herrenschmidt /* Create windows from the OPB space to the ISA space */ 713a3980bf5SBenjamin Herrenschmidt memory_region_init_alias(&lpc->opb_isa_io, OBJECT(dev), "lpc-isa-io", 714a3980bf5SBenjamin Herrenschmidt &lpc->isa_io, 0, LPC_IO_OPB_SIZE); 715a3980bf5SBenjamin Herrenschmidt memory_region_add_subregion(&lpc->opb_mr, LPC_IO_OPB_ADDR, 716a3980bf5SBenjamin Herrenschmidt &lpc->opb_isa_io); 717a3980bf5SBenjamin Herrenschmidt memory_region_init_alias(&lpc->opb_isa_mem, OBJECT(dev), "lpc-isa-mem", 718a3980bf5SBenjamin Herrenschmidt &lpc->isa_mem, 0, LPC_MEM_OPB_SIZE); 719a3980bf5SBenjamin Herrenschmidt memory_region_add_subregion(&lpc->opb_mr, LPC_MEM_OPB_ADDR, 720a3980bf5SBenjamin Herrenschmidt &lpc->opb_isa_mem); 721a3980bf5SBenjamin Herrenschmidt memory_region_init_alias(&lpc->opb_isa_fw, OBJECT(dev), "lpc-isa-fw", 722d61c2857SCédric Le Goater &lpc->isa_fw, 0, LPC_FW_OPB_SIZE); 723a3980bf5SBenjamin Herrenschmidt memory_region_add_subregion(&lpc->opb_mr, LPC_FW_OPB_ADDR, 724a3980bf5SBenjamin Herrenschmidt &lpc->opb_isa_fw); 725a3980bf5SBenjamin Herrenschmidt 726a3980bf5SBenjamin Herrenschmidt /* Create MMIO regions for LPC HC and OPB registers */ 727a3980bf5SBenjamin Herrenschmidt memory_region_init_io(&lpc->opb_master_regs, OBJECT(dev), &opb_master_ops, 728a3980bf5SBenjamin Herrenschmidt lpc, "lpc-opb-master", LPC_OPB_REGS_OPB_SIZE); 729a3980bf5SBenjamin Herrenschmidt memory_region_add_subregion(&lpc->opb_mr, LPC_OPB_REGS_OPB_ADDR, 730a3980bf5SBenjamin Herrenschmidt &lpc->opb_master_regs); 731a3980bf5SBenjamin Herrenschmidt memory_region_init_io(&lpc->lpc_hc_regs, OBJECT(dev), &lpc_hc_ops, lpc, 732a3980bf5SBenjamin Herrenschmidt "lpc-hc", LPC_HC_REGS_OPB_SIZE); 733a3980bf5SBenjamin Herrenschmidt memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR, 734a3980bf5SBenjamin Herrenschmidt &lpc->lpc_hc_regs); 735a3980bf5SBenjamin Herrenschmidt } 736a3980bf5SBenjamin Herrenschmidt 737a3980bf5SBenjamin Herrenschmidt static void pnv_lpc_class_init(ObjectClass *klass, void *data) 738a3980bf5SBenjamin Herrenschmidt { 739a3980bf5SBenjamin Herrenschmidt DeviceClass *dc = DEVICE_CLASS(klass); 740a3980bf5SBenjamin Herrenschmidt 741a3980bf5SBenjamin Herrenschmidt dc->realize = pnv_lpc_realize; 74282514be2SCédric Le Goater dc->desc = "PowerNV LPC Controller"; 743a3980bf5SBenjamin Herrenschmidt } 744a3980bf5SBenjamin Herrenschmidt 745a3980bf5SBenjamin Herrenschmidt static const TypeInfo pnv_lpc_info = { 746a3980bf5SBenjamin Herrenschmidt .name = TYPE_PNV_LPC, 747a3980bf5SBenjamin Herrenschmidt .parent = TYPE_DEVICE, 748a3980bf5SBenjamin Herrenschmidt .class_init = pnv_lpc_class_init, 74982514be2SCédric Le Goater .class_size = sizeof(PnvLpcClass), 75082514be2SCédric Le Goater .abstract = true, 751a3980bf5SBenjamin Herrenschmidt }; 752a3980bf5SBenjamin Herrenschmidt 753a3980bf5SBenjamin Herrenschmidt static void pnv_lpc_register_types(void) 754a3980bf5SBenjamin Herrenschmidt { 755a3980bf5SBenjamin Herrenschmidt type_register_static(&pnv_lpc_info); 75682514be2SCédric Le Goater type_register_static(&pnv_lpc_power8_info); 75715376c66SCédric Le Goater type_register_static(&pnv_lpc_power9_info); 758a3980bf5SBenjamin Herrenschmidt } 759a3980bf5SBenjamin Herrenschmidt 760a3980bf5SBenjamin Herrenschmidt type_init(pnv_lpc_register_types) 7614d1df88bSBenjamin Herrenschmidt 7624d1df88bSBenjamin Herrenschmidt /* If we don't use the built-in LPC interrupt deserializer, we need 7634d1df88bSBenjamin Herrenschmidt * to provide a set of qirqs for the ISA bus or things will go bad. 7644d1df88bSBenjamin Herrenschmidt * 7654d1df88bSBenjamin Herrenschmidt * Most machines using pre-Naples chips (without said deserializer) 7664d1df88bSBenjamin Herrenschmidt * have a CPLD that will collect the SerIRQ and shoot them as a 7674d1df88bSBenjamin Herrenschmidt * single level interrupt to the P8 chip. So let's setup a hook 7684d1df88bSBenjamin Herrenschmidt * for doing just that. 7694d1df88bSBenjamin Herrenschmidt */ 7704d1df88bSBenjamin Herrenschmidt static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level) 7714d1df88bSBenjamin Herrenschmidt { 772b168a138SCédric Le Goater PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); 7734d1df88bSBenjamin Herrenschmidt uint32_t old_state = pnv->cpld_irqstate; 7744d1df88bSBenjamin Herrenschmidt PnvLpcController *lpc = PNV_LPC(opaque); 7754d1df88bSBenjamin Herrenschmidt 7764d1df88bSBenjamin Herrenschmidt if (level) { 7774d1df88bSBenjamin Herrenschmidt pnv->cpld_irqstate |= 1u << n; 7784d1df88bSBenjamin Herrenschmidt } else { 7794d1df88bSBenjamin Herrenschmidt pnv->cpld_irqstate &= ~(1u << n); 7804d1df88bSBenjamin Herrenschmidt } 7814d1df88bSBenjamin Herrenschmidt 7824d1df88bSBenjamin Herrenschmidt if (pnv->cpld_irqstate != old_state) { 7834d1df88bSBenjamin Herrenschmidt pnv_psi_irq_set(lpc->psi, PSIHB_IRQ_EXTERNAL, pnv->cpld_irqstate != 0); 7844d1df88bSBenjamin Herrenschmidt } 7854d1df88bSBenjamin Herrenschmidt } 7864d1df88bSBenjamin Herrenschmidt 7874d1df88bSBenjamin Herrenschmidt static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level) 7884d1df88bSBenjamin Herrenschmidt { 7894d1df88bSBenjamin Herrenschmidt PnvLpcController *lpc = PNV_LPC(opaque); 7904d1df88bSBenjamin Herrenschmidt 7914d1df88bSBenjamin Herrenschmidt /* The Naples HW latches the 1 levels, clearing is done by SW */ 7924d1df88bSBenjamin Herrenschmidt if (level) { 7934d1df88bSBenjamin Herrenschmidt lpc->lpc_hc_irqstat |= LPC_HC_IRQ_SERIRQ0 >> n; 7944d1df88bSBenjamin Herrenschmidt pnv_lpc_eval_irqs(lpc); 7954d1df88bSBenjamin Herrenschmidt } 7964d1df88bSBenjamin Herrenschmidt } 7974d1df88bSBenjamin Herrenschmidt 79804026890SCédric Le Goater ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp) 7994d1df88bSBenjamin Herrenschmidt { 80004026890SCédric Le Goater Error *local_err = NULL; 80104026890SCédric Le Goater ISABus *isa_bus; 80204026890SCédric Le Goater qemu_irq *irqs; 80304026890SCédric Le Goater qemu_irq_handler handler; 80404026890SCédric Le Goater 80504026890SCédric Le Goater /* let isa_bus_new() create its own bridge on SysBus otherwise 80604026890SCédric Le Goater * devices speficied on the command line won't find the bus and 80704026890SCédric Le Goater * will fail to create. 80804026890SCédric Le Goater */ 80904026890SCédric Le Goater isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io, &local_err); 81004026890SCédric Le Goater if (local_err) { 81104026890SCédric Le Goater error_propagate(errp, local_err); 81204026890SCédric Le Goater return NULL; 81304026890SCédric Le Goater } 81404026890SCédric Le Goater 8154d1df88bSBenjamin Herrenschmidt /* Not all variants have a working serial irq decoder. If not, 8164d1df88bSBenjamin Herrenschmidt * handling of LPC interrupts becomes a platform issue (some 8174d1df88bSBenjamin Herrenschmidt * platforms have a CPLD to do it). 8184d1df88bSBenjamin Herrenschmidt */ 81904026890SCédric Le Goater if (use_cpld) { 82004026890SCédric Le Goater handler = pnv_lpc_isa_irq_handler_cpld; 8214d1df88bSBenjamin Herrenschmidt } else { 82204026890SCédric Le Goater handler = pnv_lpc_isa_irq_handler; 8234d1df88bSBenjamin Herrenschmidt } 82404026890SCédric Le Goater 82504026890SCédric Le Goater irqs = qemu_allocate_irqs(handler, lpc, ISA_NUM_IRQS); 82604026890SCédric Le Goater 82704026890SCédric Le Goater isa_bus_irqs(isa_bus, irqs); 82804026890SCédric Le Goater return isa_bus; 8294d1df88bSBenjamin Herrenschmidt } 830