/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define pr_fmt(fmt) "LPC: " fmt #include #include #include #include #include #include #include #include #include #include #include //#define DBG_IRQ(fmt...) prerror(fmt) #define DBG_IRQ(fmt...) do { } while(0) DEFINE_LOG_ENTRY(OPAL_RC_LPC_READ, OPAL_PLATFORM_ERR_EVT, OPAL_LPC, OPAL_MISC_SUBSYSTEM, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_LPC_WRITE, OPAL_PLATFORM_ERR_EVT, OPAL_LPC, OPAL_MISC_SUBSYSTEM, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); DEFINE_LOG_ENTRY(OPAL_RC_LPC_SYNC, OPAL_PLATFORM_ERR_EVT, OPAL_LPC, OPAL_MISC_SUBSYSTEM, OPAL_PREDICTIVE_ERR_GENERAL, OPAL_NA); /* Used exclusively in manufacturing mode */ DEFINE_LOG_ENTRY(OPAL_RC_LPC_SYNC_PERF, OPAL_PLATFORM_ERR_EVT, OPAL_LPC, OPAL_MISC_SUBSYSTEM, OPAL_UNRECOVERABLE_ERR_DEGRADE_PERF, OPAL_NA); #define ECCB_CTL 0 /* b0020 -> b00200 */ #define ECCB_STAT 2 /* b0022 -> b00210 */ #define ECCB_DATA 3 /* b0023 -> b00218 */ #define ECCB_CTL_MAGIC 0xd000000000000000ul #define ECCB_CTL_DATASZ PPC_BITMASK(4,7) #define ECCB_CTL_READ PPC_BIT(15) #define ECCB_CTL_ADDRLEN PPC_BITMASK(23,25) #define ECCB_ADDRLEN_4B 0x4 #define ECCB_CTL_ADDR PPC_BITMASK(32,63) #define ECCB_STAT_PIB_ERR PPC_BITMASK(0,5) #define ECCB_STAT_RD_DATA PPC_BITMASK(6,37) #define ECCB_STAT_BUSY PPC_BIT(44) #define ECCB_STAT_ERRORS1 PPC_BITMASK(45,51) #define ECCB_STAT_OP_DONE PPC_BIT(52) #define ECCB_STAT_ERRORS2 PPC_BITMASK(53,55) #define ECCB_STAT_ERR_MASK (ECCB_STAT_PIB_ERR | \ ECCB_STAT_ERRORS1 | \ ECCB_STAT_ERRORS2) #define ECCB_TIMEOUT 1000000 /* OPB Master LS registers */ #define OPB_MASTER_LS_IRQ_STAT 0x50 #define OPB_MASTER_LS_IRQ_MASK 0x54 #define OPB_MASTER_LS_IRQ_POL 0x58 #define OPB_MASTER_IRQ_LPC 0x00000800 /* LPC HC registers */ #define LPC_HC_FW_SEG_IDSEL 0x24 #define LPC_HC_FW_RD_ACC_SIZE 0x28 #define LPC_HC_FW_RD_1B 0x00000000 #define LPC_HC_FW_RD_2B 0x01000000 #define LPC_HC_FW_RD_4B 0x02000000 #define LPC_HC_FW_RD_16B 0x04000000 #define LPC_HC_FW_RD_128B 0x07000000 #define LPC_HC_IRQSER_CTRL 0x30 #define LPC_HC_IRQSER_EN 0x80000000 #define LPC_HC_IRQSER_QMODE 0x40000000 #define LPC_HC_IRQSER_START_MASK 0x03000000 #define LPC_HC_IRQSER_START_4CLK 0x00000000 #define LPC_HC_IRQSER_START_6CLK 0x01000000 #define LPC_HC_IRQSER_START_8CLK 0x02000000 #define LPC_HC_IRQMASK 0x34 /* same bit defs as LPC_HC_IRQSTAT */ #define LPC_HC_IRQSTAT 0x38 #define LPC_HC_IRQ_SERIRQ0 0x80000000u /* all bits down to ... */ #define LPC_HC_IRQ_SERIRQ16 0x00008000 /* IRQ16=IOCHK#, IRQ2=SMI# */ #define LPC_HC_IRQ_SERIRQ_ALL 0xffff8000 #define LPC_HC_IRQ_LRESET 0x00000400 #define LPC_HC_IRQ_SYNC_ABNORM_ERR 0x00000080 #define LPC_HC_IRQ_SYNC_NORESP_ERR 0x00000040 #define LPC_HC_IRQ_SYNC_NORM_ERR 0x00000020 #define LPC_HC_IRQ_SYNC_TIMEOUT_ERR 0x00000010 #define LPC_HC_IRQ_TARG_TAR_ERR 0x00000008 #define LPC_HC_IRQ_BM_TAR_ERR 0x00000004 #define LPC_HC_IRQ_BM0_REQ 0x00000002 #define LPC_HC_IRQ_BM1_REQ 0x00000001 #define LPC_HC_IRQ_BASE_IRQS ( \ LPC_HC_IRQ_LRESET | \ LPC_HC_IRQ_SYNC_ABNORM_ERR | \ LPC_HC_IRQ_SYNC_NORESP_ERR | \ LPC_HC_IRQ_SYNC_NORM_ERR | \ LPC_HC_IRQ_SYNC_TIMEOUT_ERR | \ LPC_HC_IRQ_TARG_TAR_ERR | \ LPC_HC_IRQ_BM_TAR_ERR) #define LPC_HC_ERROR_ADDRESS 0x40 #define LPC_NUM_SERIRQ 17 struct lpcm { uint32_t chip_id; uint32_t xbase; void *mbase; struct lock lock; uint8_t fw_idsel; uint8_t fw_rdsz; struct list_head clients; bool has_serirq; uint8_t sirq_routes[LPC_NUM_SERIRQ]; uint32_t sirq_rmasks[4]; }; #define LPC_BUS_DEGRADED_PERF_THRESHOLD 5 struct lpc_client_entry { struct list_node node; const struct lpc_client *clt; }; /* Default LPC bus */ static int32_t lpc_default_chip_id = -1; static bool lpc_irqs_ready; /* * These are expected to be the same on all chips and should probably * be read (or configured) dynamically. This is how things are configured * today on Tuletta. */ static uint32_t lpc_io_opb_base = 0xd0010000; static uint32_t lpc_mem_opb_base = 0xe0000000; static uint32_t lpc_fw_opb_base = 0xf0000000; static uint32_t lpc_reg_opb_base = 0xc0012000; static uint32_t opb_master_reg_base = 0xc0010000; static int64_t opb_mmio_write(struct lpcm *lpc, uint32_t addr, uint32_t data, uint32_t sz) { switch (sz) { case 1: out_8(lpc->mbase + addr, data); return OPAL_SUCCESS; case 2: out_be16(lpc->mbase + addr, data); return OPAL_SUCCESS; case 4: out_be32(lpc->mbase + addr, data); return OPAL_SUCCESS; } prerror("LPC: Invalid data size %d\n", sz); return OPAL_PARAMETER; } static int64_t opb_write(struct lpcm *lpc, uint32_t addr, uint32_t data, uint32_t sz) { uint64_t ctl = ECCB_CTL_MAGIC, stat; int64_t rc, tout; uint64_t data_reg; if (lpc->mbase) return opb_mmio_write(lpc, addr, data, sz); switch(sz) { case 1: data_reg = ((uint64_t)data) << 56; break; case 2: data_reg = ((uint64_t)data) << 48; break; case 4: data_reg = ((uint64_t)data) << 32; break; default: prerror("Invalid data size %d\n", sz); return OPAL_PARAMETER; } rc = xscom_write(lpc->chip_id, lpc->xbase + ECCB_DATA, data_reg); if (rc) { log_simple_error(&e_info(OPAL_RC_LPC_WRITE), "LPC: XSCOM write to ECCB DATA error %lld\n", rc); return rc; } ctl = SETFIELD(ECCB_CTL_DATASZ, ctl, sz); ctl = SETFIELD(ECCB_CTL_ADDRLEN, ctl, ECCB_ADDRLEN_4B); ctl = SETFIELD(ECCB_CTL_ADDR, ctl, addr); rc = xscom_write(lpc->chip_id, lpc->xbase + ECCB_CTL, ctl); if (rc) { log_simple_error(&e_info(OPAL_RC_LPC_WRITE), "LPC: XSCOM write to ECCB CTL error %lld\n", rc); return rc; } for (tout = 0; tout < ECCB_TIMEOUT; tout++) { rc = xscom_read(lpc->chip_id, lpc->xbase + ECCB_STAT, &stat); if (rc) { log_simple_error(&e_info(OPAL_RC_LPC_WRITE), "LPC: XSCOM read from ECCB STAT err %lld\n", rc); return rc; } if (stat & ECCB_STAT_OP_DONE) { if (stat & ECCB_STAT_ERR_MASK) { log_simple_error(&e_info(OPAL_RC_LPC_WRITE), "LPC: Error status: 0x%llx\n", stat); return OPAL_HARDWARE; } return OPAL_SUCCESS; } time_wait_nopoll(100); } log_simple_error(&e_info(OPAL_RC_LPC_WRITE), "LPC: Write timeout !\n"); return OPAL_HARDWARE; } static int64_t opb_mmio_read(struct lpcm *lpc, uint32_t addr, uint32_t *data, uint32_t sz) { switch (sz) { case 1: *data = in_8(lpc->mbase + addr); return OPAL_SUCCESS; case 2: *data = in_be16(lpc->mbase + addr); return OPAL_SUCCESS; case 4: *data = in_be32(lpc->mbase + addr); return OPAL_SUCCESS; } prerror("LPC: Invalid data size %d\n", sz); return OPAL_PARAMETER; } static int64_t opb_read(struct lpcm *lpc, uint32_t addr, uint32_t *data, uint32_t sz) { uint64_t ctl = ECCB_CTL_MAGIC | ECCB_CTL_READ, stat; int64_t rc, tout; if (lpc->mbase) return opb_mmio_read(lpc, addr, data, sz); if (sz != 1 && sz != 2 && sz != 4) { prerror("Invalid data size %d\n", sz); return OPAL_PARAMETER; } ctl = SETFIELD(ECCB_CTL_DATASZ, ctl, sz); ctl = SETFIELD(ECCB_CTL_ADDRLEN, ctl, ECCB_ADDRLEN_4B); ctl = SETFIELD(ECCB_CTL_ADDR, ctl, addr); rc = xscom_write(lpc->chip_id, lpc->xbase + ECCB_CTL, ctl); if (rc) { log_simple_error(&e_info(OPAL_RC_LPC_READ), "LPC: XSCOM write to ECCB CTL error %lld\n", rc); return rc; } for (tout = 0; tout < ECCB_TIMEOUT; tout++) { rc = xscom_read(lpc->chip_id, lpc->xbase + ECCB_STAT, &stat); if (rc) { log_simple_error(&e_info(OPAL_RC_LPC_READ), "LPC: XSCOM read from ECCB STAT err %lld\n", rc); return rc; } if (stat & ECCB_STAT_OP_DONE) { uint32_t rdata = GETFIELD(ECCB_STAT_RD_DATA, stat); if (stat & ECCB_STAT_ERR_MASK) { log_simple_error(&e_info(OPAL_RC_LPC_READ), "LPC: Error status: 0x%llx\n", stat); return OPAL_HARDWARE; } switch(sz) { case 1: *data = rdata >> 24; break; case 2: *data = rdata >> 16; break; default: *data = rdata; break; } return 0; } time_wait_nopoll(100); } log_simple_error(&e_info(OPAL_RC_LPC_READ), "LPC: Read timeout !\n"); return OPAL_HARDWARE; } static int64_t lpc_set_fw_idsel(struct lpcm *lpc, uint8_t idsel) { uint32_t val; int64_t rc; if (idsel == lpc->fw_idsel) return OPAL_SUCCESS; if (idsel > 0xf) return OPAL_PARAMETER; rc = opb_read(lpc, lpc_reg_opb_base + LPC_HC_FW_SEG_IDSEL, &val, 4); if (rc) { prerror("Failed to read HC_FW_SEG_IDSEL register !\n"); return rc; } val = (val & 0xfffffff0) | idsel; rc = opb_write(lpc, lpc_reg_opb_base + LPC_HC_FW_SEG_IDSEL, val, 4); if (rc) { prerror("Failed to write HC_FW_SEG_IDSEL register !\n"); return rc; } lpc->fw_idsel = idsel; return OPAL_SUCCESS; } static int64_t lpc_set_fw_rdsz(struct lpcm *lpc, uint8_t rdsz) { uint32_t val; int64_t rc; if (rdsz == lpc->fw_rdsz) return OPAL_SUCCESS; switch(rdsz) { case 1: val = LPC_HC_FW_RD_1B; break; case 2: val = LPC_HC_FW_RD_2B; break; case 4: val = LPC_HC_FW_RD_4B; break; default: /* * The HW supports 16 and 128 via a buffer/cache * but I have never exprimented with it and am not * sure it works the way we expect so let's leave it * at that for now */ return OPAL_PARAMETER; } rc = opb_write(lpc, lpc_reg_opb_base + LPC_HC_FW_RD_ACC_SIZE, val, 4); if (rc) { prerror("Failed to write LPC_HC_FW_RD_ACC_SIZE !\n"); return rc; } lpc->fw_rdsz = rdsz; return OPAL_SUCCESS; } static int64_t lpc_opb_prepare(struct lpcm *lpc, enum OpalLPCAddressType addr_type, uint32_t addr, uint32_t sz, uint32_t *opb_base, bool is_write) { uint32_t top = addr + sz; uint8_t fw_idsel; int64_t rc; /* Address wraparound */ if (top < addr) return OPAL_PARAMETER; /* * Bound check access and get the OPB base address for * the window corresponding to the access type */ switch(addr_type) { case OPAL_LPC_IO: /* IO space is 64K */ if (top > 0x10000) return OPAL_PARAMETER; /* And only supports byte accesses */ if (sz != 1) return OPAL_PARAMETER; *opb_base = lpc_io_opb_base; break; case OPAL_LPC_MEM: /* MEM space is 256M */ if (top > 0x10000000) return OPAL_PARAMETER; /* And only supports byte accesses */ if (sz != 1) return OPAL_PARAMETER; *opb_base = lpc_mem_opb_base; break; case OPAL_LPC_FW: /* * FW space is in segments of 256M controlled * by IDSEL, make sure we don't cross segments */ *opb_base = lpc_fw_opb_base; fw_idsel = (addr >> 28); if (((top - 1) >> 28) != fw_idsel) return OPAL_PARAMETER; /* Set segment */ rc = lpc_set_fw_idsel(lpc, fw_idsel); if (rc) return rc; /* Set read access size */ if (!is_write) { rc = lpc_set_fw_rdsz(lpc, sz); if (rc) return rc; } break; default: return OPAL_PARAMETER; } return OPAL_SUCCESS; } static int64_t __lpc_write(struct lpcm *lpc, enum OpalLPCAddressType addr_type, uint32_t addr, uint32_t data, uint32_t sz) { uint32_t opb_base; int64_t rc; lock(&lpc->lock); /* * Convert to an OPB access and handle LPC HC configuration * for FW accesses (IDSEL) */ rc = lpc_opb_prepare(lpc, addr_type, addr, sz, &opb_base, true); if (rc) goto bail; /* Perform OPB access */ rc = opb_write(lpc, opb_base + addr, data, sz); /* XXX Add LPC error handling/recovery */ bail: unlock(&lpc->lock); return rc; } int64_t lpc_write(enum OpalLPCAddressType addr_type, uint32_t addr, uint32_t data, uint32_t sz) { struct proc_chip *chip; if (lpc_default_chip_id < 0) return OPAL_PARAMETER; chip = get_chip(lpc_default_chip_id); if (!chip || !chip->lpc) return OPAL_PARAMETER; return __lpc_write(chip->lpc, addr_type, addr, data, sz); } /* * The "OPAL" variant add the emulation of 2 and 4 byte accesses using * byte accesses for IO and MEM space in order to be compatible with * existing Linux expectations */ static int64_t opal_lpc_write(uint32_t chip_id, enum OpalLPCAddressType addr_type, uint32_t addr, uint32_t data, uint32_t sz) { struct proc_chip *chip; int64_t rc; chip = get_chip(chip_id); if (!chip || !chip->lpc) return OPAL_PARAMETER; if (addr_type == OPAL_LPC_FW || sz == 1) return __lpc_write(chip->lpc, addr_type, addr, data, sz); while(sz--) { rc = __lpc_write(chip->lpc, addr_type, addr, data & 0xff, 1); if (rc) return rc; addr++; data >>= 8; } return OPAL_SUCCESS; } static int64_t __lpc_read(struct lpcm *lpc, enum OpalLPCAddressType addr_type, uint32_t addr, uint32_t *data, uint32_t sz) { uint32_t opb_base; int64_t rc; lock(&lpc->lock); /* * Convert to an OPB access and handle LPC HC configuration * for FW accesses (IDSEL and read size) */ rc = lpc_opb_prepare(lpc, addr_type, addr, sz, &opb_base, false); if (rc) goto bail; /* Perform OPB access */ rc = opb_read(lpc, opb_base + addr, data, sz); /* XXX Add LPC error handling/recovery */ bail: unlock(&lpc->lock); return rc; } int64_t lpc_read(enum OpalLPCAddressType addr_type, uint32_t addr, uint32_t *data, uint32_t sz) { struct proc_chip *chip; if (lpc_default_chip_id < 0) return OPAL_PARAMETER; chip = get_chip(lpc_default_chip_id); if (!chip || !chip->lpc) return OPAL_PARAMETER; return __lpc_read(chip->lpc, addr_type, addr, data, sz); } /* * The "OPAL" variant add the emulation of 2 and 4 byte accesses using * byte accesses for IO and MEM space in order to be compatible with * existing Linux expectations */ static int64_t opal_lpc_read(uint32_t chip_id, enum OpalLPCAddressType addr_type, uint32_t addr, uint32_t *data, uint32_t sz) { struct proc_chip *chip; int64_t rc; chip = get_chip(chip_id); if (!chip || !chip->lpc) return OPAL_PARAMETER; if (addr_type == OPAL_LPC_FW || sz == 1) return __lpc_read(chip->lpc, addr_type, addr, data, sz); *data = 0; while(sz--) { uint32_t byte; rc = __lpc_read(chip->lpc, addr_type, addr, &byte, 1); if (rc) return rc; *data = *data | (byte << (8 * sz)); addr++; } return OPAL_SUCCESS; } bool lpc_present(void) { return lpc_default_chip_id >= 0; } /* Called with LPC lock held */ static void lpc_setup_serirq(struct lpcm *lpc) { struct lpc_client_entry *ent; uint32_t mask = LPC_HC_IRQ_BASE_IRQS; int rc; if (!lpc_irqs_ready) return; /* Collect serirq enable bits */ list_for_each(&lpc->clients, ent, node) mask |= ent->clt->interrupts & LPC_HC_IRQ_SERIRQ_ALL; rc = opb_write(lpc, lpc_reg_opb_base + LPC_HC_IRQMASK, mask, 4); if (rc) { prerror("Failed to update irq mask\n"); return; } DBG_IRQ("LPC: IRQ mask set to 0x%08x\n", mask); /* Enable the LPC interrupt in the OPB Master */ opb_write(lpc, opb_master_reg_base + OPB_MASTER_LS_IRQ_POL, 0, 4); rc = opb_write(lpc, opb_master_reg_base + OPB_MASTER_LS_IRQ_MASK, OPB_MASTER_IRQ_LPC, 4); if (rc) prerror("Failed to enable IRQs in OPB\n"); /* Check whether we should enable serirq */ if (mask & LPC_HC_IRQ_SERIRQ_ALL) { rc = opb_write(lpc, lpc_reg_opb_base + LPC_HC_IRQSER_CTRL, LPC_HC_IRQSER_EN | LPC_HC_IRQSER_START_4CLK, 4); DBG_IRQ("LPC: SerIRQ enabled\n"); } else { rc = opb_write(lpc, lpc_reg_opb_base + LPC_HC_IRQSER_CTRL, 0, 4); DBG_IRQ("LPC: SerIRQ disabled\n"); } if (rc) prerror("Failed to configure SerIRQ\n"); { u32 val; rc = opb_read(lpc, lpc_reg_opb_base + LPC_HC_IRQMASK, &val, 4); if (rc) prerror("Failed to readback mask"); else DBG_IRQ("LPC: MASK READBACK=%x\n", val); rc = opb_read(lpc, lpc_reg_opb_base + LPC_HC_IRQSER_CTRL, &val, 4); if (rc) prerror("Failed to readback ctrl"); else DBG_IRQ("LPC: CTRL READBACK=%x\n", val); } } static void __lpc_route_serirq(struct lpcm *lpc, uint32_t sirq, uint32_t psi_idx) { uint32_t reg, shift, val; int64_t rc; lpc->sirq_routes[sirq] = psi_idx; /* We may not be ready yet ... */ if (!lpc->has_serirq) return; if (sirq < 14) { reg = 0xc; shift = 4 + (sirq << 1); } else { reg = 0x8; shift = 8 + ((sirq - 14) << 1); } shift = 30-shift; rc = opb_read(lpc, opb_master_reg_base + reg, &val, 4); if (rc) return; val = val & ~(3 << shift); val |= (psi_idx & 3) << shift; opb_write(lpc, opb_master_reg_base + reg, val, 4); } void lpc_route_serirq(uint32_t chip_id, uint32_t sirq, uint32_t psi_idx) { struct proc_chip *chip; struct lpcm *lpc; uint32_t psi_old; if (sirq >= LPC_NUM_SERIRQ) { prerror("LPC[%03x]: Routing request for invalid SerIRQ %d\n", chip_id, sirq); return; } chip = get_chip(chip_id); if (!chip || !chip->lpc) return; lpc = chip->lpc; lock(&lpc->lock); psi_old = lpc->sirq_routes[sirq]; lpc->sirq_rmasks[psi_old] &= ~(LPC_HC_IRQ_SERIRQ0 >> sirq); lpc->sirq_rmasks[psi_idx] |= (LPC_HC_IRQ_SERIRQ0 >> sirq); __lpc_route_serirq(lpc, sirq, psi_idx); unlock(&lpc->lock); } static void lpc_init_interrupts_one(struct proc_chip *chip) { struct lpcm *lpc = chip->lpc; int i, rc; lock(&lpc->lock); /* First mask them all */ rc = opb_write(lpc, lpc_reg_opb_base + LPC_HC_IRQMASK, 0, 4); if (rc) { prerror("LPC: Failed to init interrutps\n"); goto bail; } switch(chip->type) { case PROC_CHIP_P8_MURANO: case PROC_CHIP_P8_VENICE: /* On Murano/Venice, there is no SerIRQ, only enable error * interrupts */ rc = opb_write(lpc, lpc_reg_opb_base + LPC_HC_IRQMASK, LPC_HC_IRQ_BASE_IRQS, 4); if (rc) { prerror("LPC: Failed to set interrupt mask\n"); goto bail; } opb_write(lpc, lpc_reg_opb_base + LPC_HC_IRQSER_CTRL, 0, 4); break; case PROC_CHIP_P8_NAPLES: /* On Naples, we support LPC interrupts, enable them based * on what clients requests. This will setup the mask and * enable processing */ lpc->has_serirq = true; lpc_setup_serirq(lpc); break; case PROC_CHIP_P9_NIMBUS: case PROC_CHIP_P9_CUMULUS: /* On P9, we additionall setup the routing */ lpc->has_serirq = true; for (i = 0; i < LPC_NUM_SERIRQ; i++) { uint32_t pin = lpc->sirq_routes[i]; __lpc_route_serirq(lpc, i, pin); lpc->sirq_rmasks[pin] |= LPC_HC_IRQ_SERIRQ0 >> i; } lpc_setup_serirq(lpc); break; default: ; } bail: unlock(&lpc->lock); } void lpc_init_interrupts(void) { struct proc_chip *chip; lpc_irqs_ready = true; for_each_chip(chip) { if (chip->lpc) lpc_init_interrupts_one(chip); } } static void lpc_dispatch_reset(struct lpcm *lpc) { struct lpc_client_entry *ent; /* XXX We are going to hit this repeatedly while reset is * asserted which might be sub-optimal. We should instead * detect assertion and start a poller that will wait for * de-assertion. We could notify clients of LPC being * on/off rather than just reset */ prerror("LPC: Got LPC reset on chip 0x%x !\n", lpc->chip_id); /* Collect serirq enable bits */ list_for_each(&lpc->clients, ent, node) { if (!ent->clt->reset) continue; unlock(&lpc->lock); ent->clt->reset(lpc->chip_id); lock(&lpc->lock); } /* Reconfigure serial interrupts */ if (lpc->has_serirq) lpc_setup_serirq(lpc); } static void lpc_dispatch_err_irqs(struct lpcm *lpc, uint32_t irqs) { const char *sync_err = "Unknown LPC error"; static int lpc_bus_err_count; struct opal_err_info *info; uint32_t err_addr; int rc; /* Write back to clear error interrupts, we clear SerIRQ later * as they are handled as level interrupts */ rc = opb_write(lpc, lpc_reg_opb_base + LPC_HC_IRQSTAT, LPC_HC_IRQ_BASE_IRQS, 4); if (rc) prerror("Failed to clear IRQ error latches !\n"); if (irqs & LPC_HC_IRQ_LRESET) lpc_dispatch_reset(lpc); if (irqs & LPC_HC_IRQ_SYNC_ABNORM_ERR) sync_err = "Got SYNC abnormal error."; if (irqs & LPC_HC_IRQ_SYNC_NORESP_ERR) sync_err = "Got SYNC no-response error."; if (irqs & LPC_HC_IRQ_SYNC_NORM_ERR) sync_err = "Got SYNC normal error."; if (irqs & LPC_HC_IRQ_SYNC_TIMEOUT_ERR) sync_err = "Got SYNC timeout error."; if (irqs & LPC_HC_IRQ_TARG_TAR_ERR) sync_err = "Got abnormal TAR error."; if (irqs & LPC_HC_IRQ_BM_TAR_ERR) sync_err = "Got bus master TAR error."; rc = opb_read(lpc, lpc_reg_opb_base + LPC_HC_ERROR_ADDRESS, &err_addr, 4); lpc_bus_err_count++; if (manufacturing_mode && (lpc_bus_err_count > LPC_BUS_DEGRADED_PERF_THRESHOLD)) info = &e_info(OPAL_RC_LPC_SYNC_PERF); else info = &e_info(OPAL_RC_LPC_SYNC); if (rc) log_simple_error(info, "LPC[%03x]: %s " "Error reading error address register\n", lpc->chip_id, sync_err); else log_simple_error(info, "LPC[%03x]: %s Error address reg: " "0x%08x\n", lpc->chip_id, sync_err, err_addr); } static void lpc_dispatch_ser_irqs(struct lpcm *lpc, uint32_t irqs, bool clear_latch) { struct lpc_client_entry *ent; uint32_t cirqs; int rc; irqs &= LPC_HC_IRQ_SERIRQ_ALL; /* Collect serirq enable bits */ list_for_each(&lpc->clients, ent, node) { if (!ent->clt->interrupt) continue; cirqs = ent->clt->interrupts & irqs; if (cirqs) { unlock(&lpc->lock); ent->clt->interrupt(lpc->chip_id, cirqs); lock(&lpc->lock); } } /* Our SerIRQ are level sensitive, we clear the latch after * we call the handler. */ if (!clear_latch) return; rc = opb_write(lpc, lpc_reg_opb_base + LPC_HC_IRQSTAT, irqs, 4); if (rc) prerror("Failed to clear SerIRQ latches !\n"); } void lpc_interrupt(uint32_t chip_id) { struct proc_chip *chip = get_chip(chip_id); struct lpcm *lpc; uint32_t irqs, opb_irqs; int rc; /* No initialized LPC controller on that chip */ if (!chip || !chip->lpc) return; lpc = chip->lpc; lock(&lpc->lock); /* Grab OPB Master LS interrupt status */ rc = opb_read(lpc, opb_master_reg_base + OPB_MASTER_LS_IRQ_STAT, &opb_irqs, 4); if (rc) { prerror("Failed to read OPB IRQ state\n"); unlock(&lpc->lock); return; } DBG_IRQ("LPC: OPB IRQ on chip 0x%x, oirqs=0x%08x\n", chip_id, opb_irqs); /* Check if it's an LPC interrupt */ if (!(opb_irqs & OPB_MASTER_IRQ_LPC)) { /* Something we don't support ? Ack it anyway... */ goto bail; } /* Handle the lpc interrupt source (errors etc...) */ rc = opb_read(lpc, lpc_reg_opb_base + LPC_HC_IRQSTAT, &irqs, 4); if (rc) { prerror("Failed to read LPC IRQ state\n"); goto bail; } DBG_IRQ("LPC: LPC IRQ on chip 0x%x, irqs=0x%08x\n", chip_id, irqs); /* Handle error interrupts */ if (irqs & LPC_HC_IRQ_BASE_IRQS) lpc_dispatch_err_irqs(lpc, irqs); /* Handle SerIRQ interrupts */ if (irqs & LPC_HC_IRQ_SERIRQ_ALL) lpc_dispatch_ser_irqs(lpc, irqs, true); bail: /* Ack it at the OPB level */ opb_write(lpc, opb_master_reg_base + OPB_MASTER_LS_IRQ_STAT, opb_irqs, 4); unlock(&lpc->lock); } void lpc_serirq(uint32_t chip_id, uint32_t index) { struct proc_chip *chip = get_chip(chip_id); struct lpcm *lpc; uint32_t irqs, rmask; int rc; /* No initialized LPC controller on that chip */ if (!chip || !chip->lpc) return; lpc = chip->lpc; lock(&lpc->lock); /* Handle the lpc interrupt source (errors etc...) */ rc = opb_read(lpc, lpc_reg_opb_base + LPC_HC_IRQSTAT, &irqs, 4); if (rc) { prerror("LPC: Failed to read LPC IRQ state\n"); goto bail; } rmask = lpc->sirq_rmasks[index]; DBG_IRQ("LPC: IRQ on chip 0x%x, irqs=0x%08x rmask=0x%08x\n", chip_id, irqs, rmask); irqs &= rmask; /* * Handle SerIRQ interrupts. Don't clear the latch, * it will be done in our special EOI callback if * necessary on DD1 */ if (irqs) lpc_dispatch_ser_irqs(lpc, irqs, false); bail: unlock(&lpc->lock); } void lpc_p9_sirq_eoi(uint32_t chip_id, uint32_t index) { struct proc_chip *chip = get_chip(chip_id); struct lpcm *lpc; uint32_t rmask; /* No initialized LPC controller on that chip */ if (!chip || !chip->lpc) return; lpc = chip->lpc; lock(&lpc->lock); rmask = lpc->sirq_rmasks[index]; opb_write(lpc, lpc_reg_opb_base + LPC_HC_IRQSTAT, rmask, 4); unlock(&lpc->lock); } void lpc_all_interrupts(uint32_t chip_id) { struct proc_chip *chip = get_chip(chip_id); struct lpcm *lpc; /* No initialized LPC controller on that chip */ if (!chip || !chip->lpc) return; lpc = chip->lpc; /* Dispatch all */ lock(&lpc->lock); lpc_dispatch_ser_irqs(lpc, LPC_HC_IRQ_SERIRQ_ALL, false); unlock(&lpc->lock); } static void lpc_init_chip_p8(struct dt_node *xn) { uint32_t gcid = dt_get_chip_id(xn); struct proc_chip *chip; struct lpcm *lpc; chip = get_chip(gcid); assert(chip); lpc = zalloc(sizeof(struct lpcm)); assert(lpc); lpc->chip_id = gcid; lpc->xbase = dt_get_address(xn, 0, NULL); lpc->fw_idsel = 0xff; lpc->fw_rdsz = 0xff; list_head_init(&lpc->clients); init_lock(&lpc->lock); if (lpc_default_chip_id < 0 || dt_has_node_property(xn, "primary", NULL)) { lpc_default_chip_id = gcid; } /* Mask all interrupts for now */ opb_write(lpc, lpc_reg_opb_base + LPC_HC_IRQMASK, 0, 4); printf("LPC[%03x]: Initialized, access via XSCOM @0x%x\n", gcid, lpc->xbase); dt_add_property(xn, "interrupt-controller", NULL, 0); dt_add_property_cells(xn, "#interrupt-cells", 1); assert(dt_prop_get_u32(xn, "#address-cells") == 2); chip->lpc = lpc; } static void lpc_parse_interrupt_map(struct lpcm *lpc, struct dt_node *lpc_node) { const u32 *imap; size_t imap_size; imap = dt_prop_get_def_size(lpc_node, "interrupt-map", NULL, &imap_size); if (!imap) return; imap_size >>= 2; if (imap_size % 5) { prerror("LPC[%03x]: Odd format for LPC interrupt-map !\n", lpc->chip_id); return; } while(imap_size >= 5) { uint32_t sirq = be32_to_cpu(imap[2]); uint32_t pirq = be32_to_cpu(imap[4]); if (sirq >= LPC_NUM_SERIRQ) { prerror("LPC[%03x]: LPC irq %d out of range in" " interrupt-map\n", lpc->chip_id, sirq); } else if (pirq < P9_PSI_IRQ_LPC_SIRQ0 || pirq > P9_PSI_IRQ_LPC_SIRQ3) { prerror("LPC[%03x]: PSI irq %d out of range in" " interrupt-map\n", lpc->chip_id, pirq); } else { uint32_t pin = pirq - P9_PSI_IRQ_LPC_SIRQ0; lpc->sirq_routes[sirq] = pin; prlog(PR_INFO, "LPC[%03x]: SerIRQ %d routed to PSI input %d\n", lpc->chip_id, sirq, pin); } imap += 5; imap_size -= 5; } } static void lpc_init_chip_p9(struct dt_node *opb_node) { uint32_t gcid = dt_get_chip_id(opb_node); struct dt_node *lpc_node; struct proc_chip *chip; struct lpcm *lpc; u64 addr; u32 val; chip = get_chip(gcid); assert(chip); /* Grab OPB base address */ addr = dt_prop_get_cell(opb_node, "ranges", 1); addr <<= 32; addr |= dt_prop_get_cell(opb_node, "ranges", 2); /* Find the "lpc" child node */ lpc_node = dt_find_compatible_node(opb_node, NULL, "ibm,power9-lpc"); if (!lpc_node) return; lpc = zalloc(sizeof(struct lpcm)); assert(lpc); lpc->chip_id = gcid; lpc->mbase = (void *)addr; lpc->fw_idsel = 0xff; lpc->fw_rdsz = 0xff; list_head_init(&lpc->clients); init_lock(&lpc->lock); if (lpc_default_chip_id < 0 || dt_has_node_property(opb_node, "primary", NULL)) { lpc_default_chip_id = gcid; } /* Parse interrupt map if any to setup initial routing */ lpc_parse_interrupt_map(lpc, lpc_node); /* Mask all interrupts for now */ opb_write(lpc, lpc_reg_opb_base + LPC_HC_IRQMASK, 0, 4); /* Default with routing to PSI SerIRQ 0, this will be updated * later when interrupts are initialized. */ opb_read(lpc, opb_master_reg_base + 8, &val, 4); val &= 0xff03ffff; opb_write(lpc, opb_master_reg_base + 8, val, 4); opb_read(lpc, opb_master_reg_base + 0xc, &val, 4); val &= 0xf0000000; opb_write(lpc, opb_master_reg_base + 0xc, val, 4); printf("LPC[%03x]: Initialized, access via MMIO @%p\n", gcid, lpc->mbase); chip->lpc = lpc; } void lpc_init(void) { struct dt_node *xn; bool has_lpc = false; /* Look for P9 first as the DT is compatile for both 8 and 9 */ dt_for_each_compatible(dt_root, xn, "ibm,power9-lpcm-opb") { lpc_init_chip_p9(xn); has_lpc = true; } if (!has_lpc) { dt_for_each_compatible(dt_root, xn, "ibm,power8-lpc") { lpc_init_chip_p8(xn); has_lpc = true; } } if (lpc_default_chip_id >= 0) printf("LPC: Default bus on chip 0x%x\n", lpc_default_chip_id); if (has_lpc) { opal_register(OPAL_LPC_WRITE, opal_lpc_write, 5); opal_register(OPAL_LPC_READ, opal_lpc_read, 5); } } void lpc_used_by_console(void) { struct proc_chip *chip; xscom_used_by_console(); for_each_chip(chip) { struct lpcm *lpc = chip->lpc; if (lpc) { lpc->lock.in_con_path = true; lock(&lpc->lock); unlock(&lpc->lock); } } } bool lpc_ok(void) { struct proc_chip *chip; if (lpc_default_chip_id < 0) return false; if (!xscom_ok()) return false; chip = get_chip(lpc_default_chip_id); if (!chip->lpc) return false; return !lock_held_by_me(&chip->lpc->lock); } void lpc_register_client(uint32_t chip_id, const struct lpc_client *clt) { struct lpc_client_entry *ent; struct proc_chip *chip; struct lpcm *lpc; chip = get_chip(chip_id); assert(chip); lpc = chip->lpc; if (!lpc) { prerror("LPC: Attempt to register client on bad chip 0x%x\n", chip_id); return; } ent = malloc(sizeof(*ent)); assert(ent); ent->clt = clt; lock(&lpc->lock); list_add(&lpc->clients, &ent->node); if (lpc->has_serirq) lpc_setup_serirq(lpc); unlock(&lpc->lock); }