aboutsummaryrefslogtreecommitdiff
path: root/hw/lpc.c
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2016-07-07 11:49:58 +1000
committerStewart Smith <stewart@linux.vnet.ibm.com>2016-07-08 18:11:37 +1000
commit83242dbdc9608ecc36f72ca7e66959f5ddabdfaa (patch)
treee8b56e00eb20cb1ed93955279f1c852b528dd625 /hw/lpc.c
parentae1e9d6eedf2fd6c07a93619239b277ce7fefc89 (diff)
downloadskiboot-83242dbdc9608ecc36f72ca7e66959f5ddabdfaa.zip
skiboot-83242dbdc9608ecc36f72ca7e66959f5ddabdfaa.tar.gz
skiboot-83242dbdc9608ecc36f72ca7e66959f5ddabdfaa.tar.bz2
lpc: Add basic P9 LPC read/write ops
We still need to review interrupts handling etc... Also update the example device-tree for SIMICS Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Acked-by: Michael Neuling <mikey@neuling.org> Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
Diffstat (limited to 'hw/lpc.c')
-rw-r--r--hw/lpc.c132
1 files changed, 107 insertions, 25 deletions
diff --git a/hw/lpc.c b/hw/lpc.c
index 1e81eeb..5f37ba6 100644
--- a/hw/lpc.c
+++ b/hw/lpc.c
@@ -129,6 +129,24 @@ 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 proc_chip *chip, uint32_t addr, uint32_t data,
+ uint32_t sz)
+{
+ switch (sz) {
+ case 1:
+ out_8(chip->lpc_mbase + addr, data);
+ return OPAL_SUCCESS;
+ case 2:
+ out_be16(chip->lpc_mbase + addr, data);
+ return OPAL_SUCCESS;
+ case 4:
+ out_be32(chip->lpc_mbase + addr, data);
+ return OPAL_SUCCESS;
+ }
+ prerror("LPC: Invalid data size %d\n", sz);
+ return OPAL_PARAMETER;
+}
+
static int64_t opb_write(struct proc_chip *chip, uint32_t addr, uint32_t data,
uint32_t sz)
{
@@ -136,6 +154,9 @@ static int64_t opb_write(struct proc_chip *chip, uint32_t addr, uint32_t data,
int64_t rc, tout;
uint64_t data_reg;
+ if (chip->lpc_mbase)
+ return opb_mmio_write(chip, addr, data, sz);
+
switch(sz) {
case 1:
data_reg = ((uint64_t)data) << 56;
@@ -190,12 +211,33 @@ static int64_t opb_write(struct proc_chip *chip, uint32_t addr, uint32_t data,
return OPAL_HARDWARE;
}
+static int64_t opb_mmio_read(struct proc_chip *chip, uint32_t addr, uint32_t *data,
+ uint32_t sz)
+{
+ switch (sz) {
+ case 1:
+ *data = in_8(chip->lpc_mbase + addr);
+ return OPAL_SUCCESS;
+ case 2:
+ *data = in_be16(chip->lpc_mbase + addr);
+ return OPAL_SUCCESS;
+ case 4:
+ *data = in_be32(chip->lpc_mbase + addr);
+ return OPAL_SUCCESS;
+ }
+ prerror("LPC: Invalid data size %d\n", sz);
+ return OPAL_PARAMETER;
+}
+
static int64_t opb_read(struct proc_chip *chip, uint32_t addr, uint32_t *data,
uint32_t sz)
{
uint64_t ctl = ECCB_CTL_MAGIC | ECCB_CTL_READ, stat;
int64_t rc, tout;
+ if (chip->lpc_mbase)
+ return opb_mmio_read(chip, addr, data, sz);
+
if (sz != 1 && sz != 2 && sz != 4) {
prerror("Invalid data size %d\n", sz);
return OPAL_PARAMETER;
@@ -378,7 +420,7 @@ static int64_t __lpc_write(uint32_t chip_id, enum OpalLPCAddressType addr_type,
uint32_t opb_base;
int64_t rc;
- if (!chip || !chip->lpc_xbase)
+ if (!chip || (!chip->lpc_xbase && !chip->lpc_mbase))
return OPAL_PARAMETER;
lock(&chip->lpc_lock);
@@ -437,7 +479,7 @@ static int64_t __lpc_read(uint32_t chip_id, enum OpalLPCAddressType addr_type,
uint32_t opb_base;
int64_t rc;
- if (!chip || !chip->lpc_xbase)
+ if (!chip || (!chip->lpc_xbase && !chip->lpc_mbase))
return OPAL_PARAMETER;
lock(&chip->lpc_lock);
@@ -697,7 +739,7 @@ void lpc_interrupt(uint32_t chip_id)
int rc;
/* No initialized LPC controller on that chip */
- if (!chip->lpc_xbase)
+ if (!chip || (!chip->lpc_xbase && !chip->lpc_mbase))
return;
lock(&chip->lpc_lock);
@@ -752,35 +794,75 @@ void lpc_all_interrupts(uint32_t chip_id)
unlock(&chip->lpc_lock);
}
-void lpc_init(void)
+static void lpc_init_chip_p8(struct dt_node *xn)
+ {
+ uint32_t gcid = dt_get_chip_id(xn);
+ struct proc_chip *chip;
+
+ chip = get_chip(gcid);
+ assert(chip);
+
+ chip->lpc_xbase = dt_get_address(xn, 0, NULL);
+ chip->lpc_fw_idsel = 0xff;
+ chip->lpc_fw_rdsz = 0xff;
+ init_lock(&chip->lpc_lock);
+
+ if (lpc_default_chip_id < 0 ||
+ dt_has_node_property(xn, "primary", NULL)) {
+ lpc_default_chip_id = chip->id;
+ }
+
+ prlog(PR_NOTICE, "Bus on chip %d, access via XSCOM, PCB_Addr=0x%x\n",
+ chip->id, chip->lpc_xbase);
+
+ lpc_init_interrupts(chip);
+ if (chip->type == PROC_CHIP_P8_NAPLES)
+ dt_add_property(xn, "interrupt-controller", NULL, 0);
+}
+
+static void lpc_init_chip_p9(struct dt_node *opb_node)
{
- struct dt_node *xn;
- bool has_lpc = false;
+ uint32_t gcid = dt_get_chip_id(opb_node);
+ struct proc_chip *chip;
+ u64 addr;
- dt_for_each_compatible(dt_root, xn, "ibm,power8-lpc") {
- uint32_t gcid = dt_get_chip_id(xn);
- struct proc_chip *chip;
+ chip = get_chip(gcid);
+ assert(chip);
- 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);
- chip->lpc_xbase = dt_get_address(xn, 0, NULL);
- chip->lpc_fw_idsel = 0xff;
- chip->lpc_fw_rdsz = 0xff;
- init_lock(&chip->lpc_lock);
+ chip->lpc_mbase = (void *)addr;
+ chip->lpc_fw_idsel = 0xff;
+ chip->lpc_fw_rdsz = 0xff;
+ init_lock(&chip->lpc_lock);
- if (lpc_default_chip_id < 0 ||
- dt_has_node_property(xn, "primary", NULL)) {
- lpc_default_chip_id = chip->id;
- }
+ if (lpc_default_chip_id < 0 ||
+ dt_has_node_property(opb_node, "primary", NULL)) {
+ lpc_default_chip_id = chip->id;
+ }
- prlog(PR_NOTICE, "Bus on chip %d PCB_Addr=0x%x\n",
- chip->id, chip->lpc_xbase);
- has_lpc = true;
+ prlog(PR_NOTICE, "Bus on chip %d, access via MMIO @%p\n",
+ chip->id, chip->lpc_mbase);
+
+ // XXX TODO
+ //lpc_init_interrupts(chip);
+}
- lpc_init_interrupts(chip);
- if (chip->type == PROC_CHIP_P8_NAPLES)
- dt_add_property(xn, "interrupt-controller", NULL, 0);
+void lpc_init(void)
+{
+ struct dt_node *xn;
+ bool has_lpc = false;
+
+ dt_for_each_compatible(dt_root, xn, "ibm,power8-lpc") {
+ lpc_init_chip_p8(xn);
+ has_lpc = true;
+ }
+ dt_for_each_compatible(dt_root, xn, "ibm,power9-lpcm-opb") {
+ lpc_init_chip_p9(xn);
+ has_lpc = true;
}
if (lpc_default_chip_id >= 0)
prlog(PR_NOTICE, "Default bus on chip %d\n",