xref: /qemu/hw/ppc/pnv_lpc.c (revision 0b8fa32f551e863bb548a11394239239270dd3dc)
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