Benjamin Herrenschmidt | a3980bf | 2016-10-22 11:46:42 +0200 | [diff] [blame] | 1 | /* |
| 2 | * QEMU PowerPC PowerNV LPC controller |
| 3 | * |
| 4 | * Copyright (c) 2016, IBM Corporation. |
| 5 | * |
| 6 | * This library is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU Lesser General Public |
| 8 | * License as published by the Free Software Foundation; either |
| 9 | * version 2 of the License, or (at your option) any later version. |
| 10 | * |
| 11 | * This library is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 14 | * Lesser General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU Lesser General Public |
| 17 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
| 18 | */ |
| 19 | |
| 20 | #include "qemu/osdep.h" |
| 21 | #include "sysemu/sysemu.h" |
Thomas Huth | fcf5ef2 | 2016-10-11 08:56:52 +0200 | [diff] [blame] | 22 | #include "target/ppc/cpu.h" |
Benjamin Herrenschmidt | a3980bf | 2016-10-22 11:46:42 +0200 | [diff] [blame] | 23 | #include "qapi/error.h" |
| 24 | #include "qemu/log.h" |
| 25 | |
Benjamin Herrenschmidt | a3980bf | 2016-10-22 11:46:42 +0200 | [diff] [blame] | 26 | #include "hw/ppc/pnv.h" |
Cédric Le Goater | ec575aa | 2016-11-07 19:03:02 +0100 | [diff] [blame] | 27 | #include "hw/ppc/pnv_lpc.h" |
| 28 | #include "hw/ppc/pnv_xscom.h" |
Benjamin Herrenschmidt | a3980bf | 2016-10-22 11:46:42 +0200 | [diff] [blame] | 29 | #include "hw/ppc/fdt.h" |
| 30 | |
| 31 | #include <libfdt.h> |
| 32 | |
| 33 | enum { |
| 34 | ECCB_CTL = 0, |
| 35 | ECCB_RESET = 1, |
| 36 | ECCB_STAT = 2, |
| 37 | ECCB_DATA = 3, |
| 38 | }; |
| 39 | |
| 40 | /* OPB Master LS registers */ |
| 41 | #define OPB_MASTER_LS_IRQ_STAT 0x50 |
| 42 | #define OPB_MASTER_IRQ_LPC 0x00000800 |
| 43 | #define OPB_MASTER_LS_IRQ_MASK 0x54 |
| 44 | #define OPB_MASTER_LS_IRQ_POL 0x58 |
| 45 | #define OPB_MASTER_LS_IRQ_INPUT 0x5c |
| 46 | |
| 47 | /* LPC HC registers */ |
| 48 | #define LPC_HC_FW_SEG_IDSEL 0x24 |
| 49 | #define LPC_HC_FW_RD_ACC_SIZE 0x28 |
| 50 | #define LPC_HC_FW_RD_1B 0x00000000 |
| 51 | #define LPC_HC_FW_RD_2B 0x01000000 |
| 52 | #define LPC_HC_FW_RD_4B 0x02000000 |
| 53 | #define LPC_HC_FW_RD_16B 0x04000000 |
| 54 | #define LPC_HC_FW_RD_128B 0x07000000 |
| 55 | #define LPC_HC_IRQSER_CTRL 0x30 |
| 56 | #define LPC_HC_IRQSER_EN 0x80000000 |
| 57 | #define LPC_HC_IRQSER_QMODE 0x40000000 |
| 58 | #define LPC_HC_IRQSER_START_MASK 0x03000000 |
| 59 | #define LPC_HC_IRQSER_START_4CLK 0x00000000 |
| 60 | #define LPC_HC_IRQSER_START_6CLK 0x01000000 |
| 61 | #define LPC_HC_IRQSER_START_8CLK 0x02000000 |
| 62 | #define LPC_HC_IRQMASK 0x34 /* same bit defs as LPC_HC_IRQSTAT */ |
| 63 | #define LPC_HC_IRQSTAT 0x38 |
| 64 | #define LPC_HC_IRQ_SERIRQ0 0x80000000 /* all bits down to ... */ |
| 65 | #define LPC_HC_IRQ_SERIRQ16 0x00008000 /* IRQ16=IOCHK#, IRQ2=SMI# */ |
| 66 | #define LPC_HC_IRQ_SERIRQ_ALL 0xffff8000 |
| 67 | #define LPC_HC_IRQ_LRESET 0x00000400 |
| 68 | #define LPC_HC_IRQ_SYNC_ABNORM_ERR 0x00000080 |
| 69 | #define LPC_HC_IRQ_SYNC_NORESP_ERR 0x00000040 |
| 70 | #define LPC_HC_IRQ_SYNC_NORM_ERR 0x00000020 |
| 71 | #define LPC_HC_IRQ_SYNC_TIMEOUT_ERR 0x00000010 |
| 72 | #define LPC_HC_IRQ_SYNC_TARG_TAR_ERR 0x00000008 |
| 73 | #define LPC_HC_IRQ_SYNC_BM_TAR_ERR 0x00000004 |
| 74 | #define LPC_HC_IRQ_SYNC_BM0_REQ 0x00000002 |
| 75 | #define LPC_HC_IRQ_SYNC_BM1_REQ 0x00000001 |
| 76 | #define LPC_HC_ERROR_ADDRESS 0x40 |
| 77 | |
| 78 | #define LPC_OPB_SIZE 0x100000000ull |
| 79 | |
| 80 | #define ISA_IO_SIZE 0x00010000 |
| 81 | #define ISA_MEM_SIZE 0x10000000 |
| 82 | #define LPC_IO_OPB_ADDR 0xd0010000 |
| 83 | #define LPC_IO_OPB_SIZE 0x00010000 |
| 84 | #define LPC_MEM_OPB_ADDR 0xe0010000 |
| 85 | #define LPC_MEM_OPB_SIZE 0x10000000 |
| 86 | #define LPC_FW_OPB_ADDR 0xf0000000 |
| 87 | #define LPC_FW_OPB_SIZE 0x10000000 |
| 88 | |
| 89 | #define LPC_OPB_REGS_OPB_ADDR 0xc0010000 |
| 90 | #define LPC_OPB_REGS_OPB_SIZE 0x00002000 |
| 91 | #define LPC_HC_REGS_OPB_ADDR 0xc0012000 |
| 92 | #define LPC_HC_REGS_OPB_SIZE 0x00001000 |
| 93 | |
| 94 | |
| 95 | /* |
| 96 | * TODO: the "primary" cell should only be added on chip 0. This is |
| 97 | * how skiboot chooses the default LPC controller on multichip |
| 98 | * systems. |
| 99 | * |
| 100 | * It would be easly done if we can change the populate() interface to |
| 101 | * replace the PnvXScomInterface parameter by a PnvChip one |
| 102 | */ |
| 103 | static int pnv_lpc_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset) |
| 104 | { |
| 105 | const char compat[] = "ibm,power8-lpc\0ibm,lpc"; |
| 106 | char *name; |
| 107 | int offset; |
| 108 | uint32_t lpc_pcba = PNV_XSCOM_LPC_BASE; |
| 109 | uint32_t reg[] = { |
| 110 | cpu_to_be32(lpc_pcba), |
| 111 | cpu_to_be32(PNV_XSCOM_LPC_SIZE) |
| 112 | }; |
| 113 | |
| 114 | name = g_strdup_printf("isa@%x", lpc_pcba); |
| 115 | offset = fdt_add_subnode(fdt, xscom_offset, name); |
| 116 | _FDT(offset); |
| 117 | g_free(name); |
| 118 | |
| 119 | _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); |
| 120 | _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2))); |
| 121 | _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1))); |
| 122 | _FDT((fdt_setprop(fdt, offset, "primary", NULL, 0))); |
| 123 | _FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat)))); |
| 124 | return 0; |
| 125 | } |
| 126 | |
| 127 | /* |
| 128 | * These read/write handlers of the OPB address space should be common |
| 129 | * with the P9 LPC Controller which uses direct MMIOs. |
| 130 | * |
| 131 | * TODO: rework to use address_space_stq() and address_space_ldq() |
| 132 | * instead. |
| 133 | */ |
| 134 | static bool opb_read(PnvLpcController *lpc, uint32_t addr, uint8_t *data, |
| 135 | int sz) |
| 136 | { |
| 137 | bool success; |
| 138 | |
| 139 | /* XXX Handle access size limits and FW read caching here */ |
| 140 | success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, |
| 141 | data, sz, false); |
| 142 | |
| 143 | return success; |
| 144 | } |
| 145 | |
| 146 | static bool opb_write(PnvLpcController *lpc, uint32_t addr, uint8_t *data, |
| 147 | int sz) |
| 148 | { |
| 149 | bool success; |
| 150 | |
| 151 | /* XXX Handle access size limits here */ |
| 152 | success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, |
| 153 | data, sz, true); |
| 154 | |
| 155 | return success; |
| 156 | } |
| 157 | |
| 158 | #define ECCB_CTL_READ (1ull << (63 - 15)) |
| 159 | #define ECCB_CTL_SZ_LSH (63 - 7) |
| 160 | #define ECCB_CTL_SZ_MASK (0xfull << ECCB_CTL_SZ_LSH) |
| 161 | #define ECCB_CTL_ADDR_MASK 0xffffffffu; |
| 162 | |
| 163 | #define ECCB_STAT_OP_DONE (1ull << (63 - 52)) |
| 164 | #define ECCB_STAT_OP_ERR (1ull << (63 - 52)) |
| 165 | #define ECCB_STAT_RD_DATA_LSH (63 - 37) |
| 166 | #define ECCB_STAT_RD_DATA_MASK (0xffffffff << ECCB_STAT_RD_DATA_LSH) |
| 167 | |
| 168 | static void pnv_lpc_do_eccb(PnvLpcController *lpc, uint64_t cmd) |
| 169 | { |
| 170 | /* XXX Check for magic bits at the top, addr size etc... */ |
| 171 | unsigned int sz = (cmd & ECCB_CTL_SZ_MASK) >> ECCB_CTL_SZ_LSH; |
| 172 | uint32_t opb_addr = cmd & ECCB_CTL_ADDR_MASK; |
| 173 | uint8_t data[4]; |
| 174 | bool success; |
| 175 | |
| 176 | if (cmd & ECCB_CTL_READ) { |
| 177 | success = opb_read(lpc, opb_addr, data, sz); |
| 178 | if (success) { |
| 179 | lpc->eccb_stat_reg = ECCB_STAT_OP_DONE | |
| 180 | (((uint64_t)data[0]) << 24 | |
| 181 | ((uint64_t)data[1]) << 16 | |
| 182 | ((uint64_t)data[2]) << 8 | |
| 183 | ((uint64_t)data[3])) << ECCB_STAT_RD_DATA_LSH; |
| 184 | } else { |
| 185 | lpc->eccb_stat_reg = ECCB_STAT_OP_DONE | |
| 186 | (0xffffffffull << ECCB_STAT_RD_DATA_LSH); |
| 187 | } |
| 188 | } else { |
| 189 | data[0] = lpc->eccb_data_reg >> 24; |
| 190 | data[1] = lpc->eccb_data_reg >> 16; |
| 191 | data[2] = lpc->eccb_data_reg >> 8; |
| 192 | data[3] = lpc->eccb_data_reg; |
| 193 | |
| 194 | success = opb_write(lpc, opb_addr, data, sz); |
| 195 | lpc->eccb_stat_reg = ECCB_STAT_OP_DONE; |
| 196 | } |
| 197 | /* XXX Which error bit (if any) to signal OPB error ? */ |
| 198 | } |
| 199 | |
| 200 | static uint64_t pnv_lpc_xscom_read(void *opaque, hwaddr addr, unsigned size) |
| 201 | { |
| 202 | PnvLpcController *lpc = PNV_LPC(opaque); |
| 203 | uint32_t offset = addr >> 3; |
| 204 | uint64_t val = 0; |
| 205 | |
| 206 | switch (offset & 3) { |
| 207 | case ECCB_CTL: |
| 208 | case ECCB_RESET: |
| 209 | val = 0; |
| 210 | break; |
| 211 | case ECCB_STAT: |
| 212 | val = lpc->eccb_stat_reg; |
| 213 | lpc->eccb_stat_reg = 0; |
| 214 | break; |
| 215 | case ECCB_DATA: |
| 216 | val = ((uint64_t)lpc->eccb_data_reg) << 32; |
| 217 | break; |
| 218 | } |
| 219 | return val; |
| 220 | } |
| 221 | |
| 222 | static void pnv_lpc_xscom_write(void *opaque, hwaddr addr, |
| 223 | uint64_t val, unsigned size) |
| 224 | { |
| 225 | PnvLpcController *lpc = PNV_LPC(opaque); |
| 226 | uint32_t offset = addr >> 3; |
| 227 | |
| 228 | switch (offset & 3) { |
| 229 | case ECCB_CTL: |
| 230 | pnv_lpc_do_eccb(lpc, val); |
| 231 | break; |
| 232 | case ECCB_RESET: |
| 233 | /* XXXX */ |
| 234 | break; |
| 235 | case ECCB_STAT: |
| 236 | break; |
| 237 | case ECCB_DATA: |
| 238 | lpc->eccb_data_reg = val >> 32; |
| 239 | break; |
| 240 | } |
| 241 | } |
| 242 | |
| 243 | static const MemoryRegionOps pnv_lpc_xscom_ops = { |
| 244 | .read = pnv_lpc_xscom_read, |
| 245 | .write = pnv_lpc_xscom_write, |
| 246 | .valid.min_access_size = 8, |
| 247 | .valid.max_access_size = 8, |
| 248 | .impl.min_access_size = 8, |
| 249 | .impl.max_access_size = 8, |
| 250 | .endianness = DEVICE_BIG_ENDIAN, |
| 251 | }; |
| 252 | |
| 253 | static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size) |
| 254 | { |
| 255 | PnvLpcController *lpc = opaque; |
| 256 | uint64_t val = 0xfffffffffffffffful; |
| 257 | |
| 258 | switch (addr) { |
| 259 | case LPC_HC_FW_SEG_IDSEL: |
| 260 | val = lpc->lpc_hc_fw_seg_idsel; |
| 261 | break; |
| 262 | case LPC_HC_FW_RD_ACC_SIZE: |
| 263 | val = lpc->lpc_hc_fw_rd_acc_size; |
| 264 | break; |
| 265 | case LPC_HC_IRQSER_CTRL: |
| 266 | val = lpc->lpc_hc_irqser_ctrl; |
| 267 | break; |
| 268 | case LPC_HC_IRQMASK: |
| 269 | val = lpc->lpc_hc_irqmask; |
| 270 | break; |
| 271 | case LPC_HC_IRQSTAT: |
| 272 | val = lpc->lpc_hc_irqstat; |
| 273 | break; |
| 274 | case LPC_HC_ERROR_ADDRESS: |
| 275 | val = lpc->lpc_hc_error_addr; |
| 276 | break; |
| 277 | default: |
| 278 | qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented register: Ox%" |
| 279 | HWADDR_PRIx "\n", addr); |
| 280 | } |
| 281 | return val; |
| 282 | } |
| 283 | |
| 284 | static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val, |
| 285 | unsigned size) |
| 286 | { |
| 287 | PnvLpcController *lpc = opaque; |
| 288 | |
| 289 | /* XXX Filter out reserved bits */ |
| 290 | |
| 291 | switch (addr) { |
| 292 | case LPC_HC_FW_SEG_IDSEL: |
| 293 | /* XXX Actually figure out how that works as this impact |
| 294 | * memory regions/aliases |
| 295 | */ |
| 296 | lpc->lpc_hc_fw_seg_idsel = val; |
| 297 | break; |
| 298 | case LPC_HC_FW_RD_ACC_SIZE: |
| 299 | lpc->lpc_hc_fw_rd_acc_size = val; |
| 300 | break; |
| 301 | case LPC_HC_IRQSER_CTRL: |
| 302 | lpc->lpc_hc_irqser_ctrl = val; |
| 303 | break; |
| 304 | case LPC_HC_IRQMASK: |
| 305 | lpc->lpc_hc_irqmask = val; |
| 306 | break; |
| 307 | case LPC_HC_IRQSTAT: |
| 308 | lpc->lpc_hc_irqstat &= ~val; |
| 309 | break; |
| 310 | case LPC_HC_ERROR_ADDRESS: |
| 311 | break; |
| 312 | default: |
| 313 | qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented register: Ox%" |
| 314 | HWADDR_PRIx "\n", addr); |
| 315 | } |
| 316 | } |
| 317 | |
| 318 | static const MemoryRegionOps lpc_hc_ops = { |
| 319 | .read = lpc_hc_read, |
| 320 | .write = lpc_hc_write, |
| 321 | .endianness = DEVICE_BIG_ENDIAN, |
| 322 | .valid = { |
| 323 | .min_access_size = 4, |
| 324 | .max_access_size = 4, |
| 325 | }, |
| 326 | .impl = { |
| 327 | .min_access_size = 4, |
| 328 | .max_access_size = 4, |
| 329 | }, |
| 330 | }; |
| 331 | |
| 332 | static uint64_t opb_master_read(void *opaque, hwaddr addr, unsigned size) |
| 333 | { |
| 334 | PnvLpcController *lpc = opaque; |
| 335 | uint64_t val = 0xfffffffffffffffful; |
| 336 | |
| 337 | switch (addr) { |
| 338 | case OPB_MASTER_LS_IRQ_STAT: |
| 339 | val = lpc->opb_irq_stat; |
| 340 | break; |
| 341 | case OPB_MASTER_LS_IRQ_MASK: |
| 342 | val = lpc->opb_irq_mask; |
| 343 | break; |
| 344 | case OPB_MASTER_LS_IRQ_POL: |
| 345 | val = lpc->opb_irq_pol; |
| 346 | break; |
| 347 | case OPB_MASTER_LS_IRQ_INPUT: |
| 348 | val = lpc->opb_irq_input; |
| 349 | break; |
| 350 | default: |
| 351 | qemu_log_mask(LOG_UNIMP, "OPB MASTER Unimplemented register: Ox%" |
| 352 | HWADDR_PRIx "\n", addr); |
| 353 | } |
| 354 | |
| 355 | return val; |
| 356 | } |
| 357 | |
| 358 | static void opb_master_write(void *opaque, hwaddr addr, |
| 359 | uint64_t val, unsigned size) |
| 360 | { |
| 361 | PnvLpcController *lpc = opaque; |
| 362 | |
| 363 | switch (addr) { |
| 364 | case OPB_MASTER_LS_IRQ_STAT: |
| 365 | lpc->opb_irq_stat &= ~val; |
| 366 | break; |
| 367 | case OPB_MASTER_LS_IRQ_MASK: |
| 368 | /* XXX Filter out reserved bits */ |
| 369 | lpc->opb_irq_mask = val; |
| 370 | break; |
| 371 | case OPB_MASTER_LS_IRQ_POL: |
| 372 | /* XXX Filter out reserved bits */ |
| 373 | lpc->opb_irq_pol = val; |
| 374 | break; |
| 375 | case OPB_MASTER_LS_IRQ_INPUT: |
| 376 | /* Read only */ |
| 377 | break; |
| 378 | default: |
| 379 | qemu_log_mask(LOG_UNIMP, "OPB MASTER Unimplemented register: Ox%" |
| 380 | HWADDR_PRIx "\n", addr); |
| 381 | } |
| 382 | } |
| 383 | |
| 384 | static const MemoryRegionOps opb_master_ops = { |
| 385 | .read = opb_master_read, |
| 386 | .write = opb_master_write, |
| 387 | .endianness = DEVICE_BIG_ENDIAN, |
| 388 | .valid = { |
| 389 | .min_access_size = 4, |
| 390 | .max_access_size = 4, |
| 391 | }, |
| 392 | .impl = { |
| 393 | .min_access_size = 4, |
| 394 | .max_access_size = 4, |
| 395 | }, |
| 396 | }; |
| 397 | |
| 398 | static void pnv_lpc_realize(DeviceState *dev, Error **errp) |
| 399 | { |
| 400 | PnvLpcController *lpc = PNV_LPC(dev); |
| 401 | |
| 402 | /* Reg inits */ |
| 403 | lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B; |
| 404 | |
| 405 | /* Create address space and backing MR for the OPB bus */ |
| 406 | memory_region_init(&lpc->opb_mr, OBJECT(dev), "lpc-opb", 0x100000000ull); |
| 407 | address_space_init(&lpc->opb_as, &lpc->opb_mr, "lpc-opb"); |
| 408 | |
| 409 | /* Create ISA IO and Mem space regions which are the root of |
| 410 | * the ISA bus (ie, ISA address spaces). We don't create a |
| 411 | * separate one for FW which we alias to memory. |
| 412 | */ |
| 413 | memory_region_init(&lpc->isa_io, OBJECT(dev), "isa-io", ISA_IO_SIZE); |
| 414 | memory_region_init(&lpc->isa_mem, OBJECT(dev), "isa-mem", ISA_MEM_SIZE); |
| 415 | |
| 416 | /* Create windows from the OPB space to the ISA space */ |
| 417 | memory_region_init_alias(&lpc->opb_isa_io, OBJECT(dev), "lpc-isa-io", |
| 418 | &lpc->isa_io, 0, LPC_IO_OPB_SIZE); |
| 419 | memory_region_add_subregion(&lpc->opb_mr, LPC_IO_OPB_ADDR, |
| 420 | &lpc->opb_isa_io); |
| 421 | memory_region_init_alias(&lpc->opb_isa_mem, OBJECT(dev), "lpc-isa-mem", |
| 422 | &lpc->isa_mem, 0, LPC_MEM_OPB_SIZE); |
| 423 | memory_region_add_subregion(&lpc->opb_mr, LPC_MEM_OPB_ADDR, |
| 424 | &lpc->opb_isa_mem); |
| 425 | memory_region_init_alias(&lpc->opb_isa_fw, OBJECT(dev), "lpc-isa-fw", |
| 426 | &lpc->isa_mem, 0, LPC_FW_OPB_SIZE); |
| 427 | memory_region_add_subregion(&lpc->opb_mr, LPC_FW_OPB_ADDR, |
| 428 | &lpc->opb_isa_fw); |
| 429 | |
| 430 | /* Create MMIO regions for LPC HC and OPB registers */ |
| 431 | memory_region_init_io(&lpc->opb_master_regs, OBJECT(dev), &opb_master_ops, |
| 432 | lpc, "lpc-opb-master", LPC_OPB_REGS_OPB_SIZE); |
| 433 | memory_region_add_subregion(&lpc->opb_mr, LPC_OPB_REGS_OPB_ADDR, |
| 434 | &lpc->opb_master_regs); |
| 435 | memory_region_init_io(&lpc->lpc_hc_regs, OBJECT(dev), &lpc_hc_ops, lpc, |
| 436 | "lpc-hc", LPC_HC_REGS_OPB_SIZE); |
| 437 | memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR, |
| 438 | &lpc->lpc_hc_regs); |
| 439 | |
| 440 | /* XScom region for LPC registers */ |
| 441 | pnv_xscom_region_init(&lpc->xscom_regs, OBJECT(dev), |
| 442 | &pnv_lpc_xscom_ops, lpc, "xscom-lpc", |
| 443 | PNV_XSCOM_LPC_SIZE); |
| 444 | } |
| 445 | |
| 446 | static void pnv_lpc_class_init(ObjectClass *klass, void *data) |
| 447 | { |
| 448 | DeviceClass *dc = DEVICE_CLASS(klass); |
| 449 | PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); |
| 450 | |
| 451 | xdc->populate = pnv_lpc_populate; |
| 452 | |
| 453 | dc->realize = pnv_lpc_realize; |
| 454 | } |
| 455 | |
| 456 | static const TypeInfo pnv_lpc_info = { |
| 457 | .name = TYPE_PNV_LPC, |
| 458 | .parent = TYPE_DEVICE, |
| 459 | .instance_size = sizeof(PnvLpcController), |
| 460 | .class_init = pnv_lpc_class_init, |
| 461 | .interfaces = (InterfaceInfo[]) { |
| 462 | { TYPE_PNV_XSCOM_INTERFACE }, |
| 463 | { } |
| 464 | } |
| 465 | }; |
| 466 | |
| 467 | static void pnv_lpc_register_types(void) |
| 468 | { |
| 469 | type_register_static(&pnv_lpc_info); |
| 470 | } |
| 471 | |
| 472 | type_init(pnv_lpc_register_types) |