summaryrefslogtreecommitdiffstats
path: root/libpdbg
diff options
context:
space:
mode:
authorAlistair Popple <alistair@popple.id.au>2016-12-19 21:07:51 +1100
committerAlistair Popple <alistair@popple.id.au>2017-03-30 15:37:41 +1100
commitf045f14437ef63e10f57580978dc9ca3e0256007 (patch)
tree86c0bdfe2fd6a54d5517ee5313e89765cd801a08 /libpdbg
parent72bf33c09f06ae63cd9f8d05e412b64622b340d1 (diff)
downloadpdbg-f045f14437ef63e10f57580978dc9ca3e0256007.tar.gz
pdbg-f045f14437ef63e10f57580978dc9ca3e0256007.zip
Clean-up target configuration in preparation for adding P9 support
The previous implementation of targeting was hardcoded, cumbersome and difficult to reconfigure for different chip types. This moves to a method of configuring targets using device-tree which is much easier to maintain and allows methods to be added to support operations like getmem on POWER9. Signed-off-by: Alistair Popple <alistair@popple.id.au>
Diffstat (limited to 'libpdbg')
-rw-r--r--libpdbg/adu.c179
-rw-r--r--libpdbg/bmcfsi.c130
-rw-r--r--libpdbg/cfam.c191
-rw-r--r--libpdbg/chip.c242
-rw-r--r--libpdbg/compiler.h53
-rw-r--r--libpdbg/device.c943
-rw-r--r--libpdbg/device.h251
-rw-r--r--libpdbg/fakepib.c38
-rw-r--r--libpdbg/i2c.c41
-rw-r--r--libpdbg/kernel.c87
-rw-r--r--libpdbg/operations.h49
-rw-r--r--libpdbg/target.c278
-rw-r--r--libpdbg/target.h120
13 files changed, 2076 insertions, 526 deletions
diff --git a/libpdbg/adu.c b/libpdbg/adu.c
index 466924d..4f2e9cb 100644
--- a/libpdbg/adu.c
+++ b/libpdbg/adu.c
@@ -20,11 +20,13 @@
#include "operations.h"
#include "bitutils.h"
+enum adu_retcode {ADU_DONE, ADU_RETRY, ADU_ERR};
+
/* ADU SCOM Register Definitions */
-#define ALTD_CONTROL_REG 0x2020000
-#define ALTD_CMD_REG 0x2020001
-#define ALTD_STATUS_REG 0x2020002
-#define ALTD_DATA_REG 0x2020003
+#define ALTD_CONTROL_REG 0x0
+#define ALTD_CMD_REG 0x1
+#define P8_ALTD_STATUS_REG 0x2
+#define P8_ALTD_DATA_REG 0x3
/* ALTD_CMD_REG fields */
#define FBC_ALTD_START_OP PPC_BIT(2)
@@ -58,31 +60,31 @@
#define TTYPE_DMA_PARTIAL_READ 0b110101
#define TTYPE_PBOPERATION 0b111111
-/* ALTD_STATUS_REG fields */
+/* P8_ALTD_STATUS_REG fields */
#define FBC_ALTD_ADDR_DONE PPC_BIT(2)
#define FBC_ALTD_DATA_DONE PPC_BIT(3)
#define FBC_ALTD_PBINIT_MISSING PPC_BIT(18)
-static int adu_lock(struct target *target)
+static int adu_lock(struct adu *adu)
{
uint64_t val;
- CHECK_ERR(read_target(target, ALTD_CMD_REG, &val));
+ CHECK_ERR(pib_read(&adu->target, ALTD_CMD_REG, &val));
if (val & FBC_LOCKED)
PR_INFO("ADU already locked! Ignoring.\n");
val |= FBC_LOCKED;
- CHECK_ERR(write_target(target, ALTD_CMD_REG, val));
+ CHECK_ERR(pib_write(&adu->target, ALTD_CMD_REG, val));
return 0;
}
-static int adu_unlock(struct target *target)
+static int adu_unlock(struct adu *adu)
{
uint64_t val;
- CHECK_ERR(read_target(target, ALTD_CMD_REG, &val));
+ CHECK_ERR(pib_read(&adu->target, ALTD_CMD_REG, &val));
if (!(val & FBC_LOCKED)) {
PR_INFO("ADU already unlocked!\n");
@@ -90,43 +92,72 @@ static int adu_unlock(struct target *target)
}
val &= ~FBC_LOCKED;
- CHECK_ERR(write_target(target, ALTD_CMD_REG, val));
+ CHECK_ERR(pib_write(&adu->target, ALTD_CMD_REG, val));
return 0;
}
-static int adu_reset(struct target *target)
+static int adu_reset(struct adu *adu)
{
uint64_t val;
- CHECK_ERR(read_target(target, ALTD_CMD_REG, &val));
+ CHECK_ERR(pib_read(&adu->target, ALTD_CMD_REG, &val));
val |= FBC_ALTD_CLEAR_STATUS | FBC_ALTD_RESET_AD_PCB;
- CHECK_ERR(write_target(target, ALTD_CMD_REG, val));
+ CHECK_ERR(pib_write(&adu->target, ALTD_CMD_REG, val));
return 0;
}
-/* Return size bytes of memory in *output. *output must point to an
- * array large enough to hold size bytes. */
-int adu_getmem(struct target *target, uint64_t start_addr, uint8_t *output, uint64_t size)
+static uint64_t p8_get_ctrl_reg(uint64_t ctrl_reg, uint64_t addr)
{
- int rc = 0;
- uint64_t addr, cmd_reg, ctrl_reg, val;
-
- /* P9 ADU is not currently supported */
- if (target->chip_type == CHIP_P9)
- return -1;
-
- CHECK_ERR(adu_lock(target));
-
- ctrl_reg = TTYPE_TREAD;
+ ctrl_reg |= TTYPE_TREAD;
ctrl_reg = SETFIELD(FBC_ALTD_TTYPE, ctrl_reg, TTYPE_DMA_PARTIAL_READ);
ctrl_reg = SETFIELD(FBC_ALTD_TSIZE, ctrl_reg, 8);
+ ctrl_reg = SETFIELD(FBC_ALTD_ADDRESS, ctrl_reg, addr);
+ return ctrl_reg;
+}
- CHECK_ERR(read_target(target, ALTD_CMD_REG, &cmd_reg));
+static uint64_t p8_get_cmd_reg(uint64_t cmd_reg, uint64_t addr)
+{
cmd_reg |= FBC_ALTD_START_OP;
cmd_reg = SETFIELD(FBC_ALTD_SCOPE, cmd_reg, SCOPE_SYSTEM);
cmd_reg = SETFIELD(FBC_ALTD_DROP_PRIORITY, cmd_reg, DROP_PRIORITY_MEDIUM);
+ return cmd_reg;
+}
+
+static enum adu_retcode p8_wait_completion(struct adu *adu)
+{
+ uint64_t val;
+
+ /* Wait for completion */
+ do {
+ CHECK_ERR(pib_read(&adu->target, P8_ALTD_STATUS_REG, &val));
+ } while (!val);
+
+ if( !(val & FBC_ALTD_ADDR_DONE) ||
+ !(val & FBC_ALTD_DATA_DONE)) {
+ /* PBINIT_MISSING is expected occasionally so just retry */
+ if (val & FBC_ALTD_PBINIT_MISSING)
+ return ADU_RETRY;
+ else {
+ PR_ERROR("ALTD_STATUS_REG = 0x%016llx\n", val);
+ return ADU_ERR;
+ }
+ }
+
+ return ADU_DONE;
+}
+
+static int p8_adu_getmem(struct adu *adu, uint64_t addr)
+{
+}
+
+/* Return size bytes of memory in *output. *output must point to an
+ * array large enough to hold size bytes. */
+static int _adu_getmem(struct adu *adu, uint64_t start_addr, uint8_t *output, uint64_t size)
+{
+ int rc = 0;
+ uint64_t addr, ctrl_reg, cmd_reg, val;
/* We read data in 8-byte aligned chunks */
for (addr = 8*(start_addr / 8); addr < start_addr + size; addr += 8) {
@@ -134,35 +165,34 @@ int adu_getmem(struct target *target, uint64_t start_addr, uint8_t *output, uint
retry:
/* Clear status bits */
- CHECK_ERR(adu_reset(target));
+ CHECK_ERR(adu_reset(adu));
/* Set the address */
- ctrl_reg = SETFIELD(FBC_ALTD_ADDRESS, ctrl_reg, addr);
- CHECK_ERR(write_target(target, ALTD_CONTROL_REG, ctrl_reg));
+ ctrl_reg = adu->_get_ctrl_reg(ctrl_reg, addr);
+ cmd_reg = adu->_get_cmd_reg(cmd_reg, addr);
+
+ CHECK_ERR(pib_write(&adu->target, ALTD_CONTROL_REG, ctrl_reg));
/* Start the command */
- CHECK_ERR(write_target(target, ALTD_CMD_REG, cmd_reg));
+ CHECK_ERR(pib_write(&adu->target, ALTD_CMD_REG, cmd_reg));
/* Wait for completion */
- do {
- CHECK_ERR(read_target(target, ALTD_STATUS_REG, &val));
- } while (!val);
-
- if( !(val & FBC_ALTD_ADDR_DONE) ||
- !(val & FBC_ALTD_DATA_DONE)) {
- /* PBINIT_MISSING is expected occasionally so just retry */
- if (val & FBC_ALTD_PBINIT_MISSING)
- goto retry;
- else {
- PR_ERROR("Unable to read memory. " \
- "ALTD_STATUS_REG = 0x%016llx\n", val);
- rc = -1;
- break;
- }
+ switch (adu->_wait_completion(adu)) {
+ case ADU_DONE:
+ break;
+ case ADU_RETRY:
+ goto retry;
+ break;
+ case ADU_ERR:
+ /* Fall through - other return codes also indicate error */
+ default:
+ PR_ERROR("Unable to read memory\n");
+ rc = ADU_ERR;
+ break;
}
/* Read data */
- CHECK_ERR(read_target(target, ALTD_DATA_REG, &data));
+ CHECK_ERR(pib_read(&adu->target, P8_ALTD_DATA_REG, &data));
/* ADU returns data in big-endian form in the register */
data = __builtin_bswap64(data);
@@ -178,26 +208,22 @@ int adu_getmem(struct target *target, uint64_t start_addr, uint8_t *output, uint
}
}
- adu_unlock(target);
+ adu_unlock(adu);
return rc;
}
-int adu_putmem(struct target *target, uint64_t start_addr, uint8_t *input, uint64_t size)
+int p8_adu_putmem(struct adu *adu, uint64_t start_addr, uint8_t *input, uint64_t size)
{
int rc = 0, tsize;
uint64_t addr, cmd_reg, ctrl_reg, val, data, end_addr;
- /* P9 ADU is not currently supported */
- if (target->chip_type == CHIP_P9)
- return -1;
-
- CHECK_ERR(adu_lock(target));
+ CHECK_ERR(adu_lock(adu));
ctrl_reg = TTYPE_TWRITE;
ctrl_reg = SETFIELD(FBC_ALTD_TTYPE, ctrl_reg, TTYPE_DMA_PARTIAL_WRITE);
- CHECK_ERR(read_target(target, ALTD_CMD_REG, &cmd_reg));
+ CHECK_ERR(pib_read(&adu->target, ALTD_CMD_REG, &cmd_reg));
cmd_reg |= FBC_ALTD_START_OP;
cmd_reg = SETFIELD(FBC_ALTD_SCOPE, cmd_reg, SCOPE_SYSTEM);
cmd_reg = SETFIELD(FBC_ALTD_DROP_PRIORITY, cmd_reg, DROP_PRIORITY_MEDIUM);
@@ -220,21 +246,21 @@ int adu_putmem(struct target *target, uint64_t start_addr, uint8_t *input, uint6
retry:
/* Clear status bits */
- CHECK_ERR(adu_reset(target));
+ CHECK_ERR(adu_reset(adu));
/* Set the address */
ctrl_reg = SETFIELD(FBC_ALTD_ADDRESS, ctrl_reg, addr);
- CHECK_ERR(write_target(target, ALTD_CONTROL_REG, ctrl_reg));
+ CHECK_ERR(pib_write(&adu->target, ALTD_CONTROL_REG, ctrl_reg));
/* Write the data */
- CHECK_ERR(write_target(target, ALTD_DATA_REG, data));
+ CHECK_ERR(pib_write(&adu->target, P8_ALTD_DATA_REG, data));
/* Start the command */
- CHECK_ERR(write_target(target, ALTD_CMD_REG, cmd_reg));
+ CHECK_ERR(pib_write(&adu->target, ALTD_CMD_REG, cmd_reg));
/* Wait for completion */
do {
- CHECK_ERR(read_target(target, ALTD_STATUS_REG, &val));
+ CHECK_ERR(pib_read(&adu->target, P8_ALTD_STATUS_REG, &val));
} while (!val);
if( !(val & FBC_ALTD_ADDR_DONE) ||
@@ -244,14 +270,41 @@ int adu_putmem(struct target *target, uint64_t start_addr, uint8_t *input, uint6
goto retry;
else {
PR_ERROR("Unable to write memory. " \
- "ALTD_STATUS_REG = 0x%016llx\n", val);
+ "P8_ALTD_STATUS_REG = 0x%016llx\n", val);
rc = -1;
break;
}
}
}
- adu_unlock(target);
+ adu_unlock(adu);
return rc;
}
+
+struct adu p8_adu = {
+ .target = {
+ .name = "POWER8 ADU",
+ .compatible = "ibm,power8-adu",
+ .class = "adu",
+ },
+ .getmem = _adu_getmem,
+ .putmem = p8_adu_putmem,
+ ._get_ctrl_reg = p8_get_ctrl_reg,
+ ._get_cmd_reg = p8_get_cmd_reg,
+ ._wait_completion = p8_wait_completion,
+};
+DECLARE_HW_UNIT(p8_adu);
+
+
+
+struct adu p9_adu = {
+ .target = {
+ .name = "POWER9 ADU",
+ .compatible = "ibm,power9-adu",
+ .class = "adu",
+ },
+// .getmem = p9_adu_getmem,
+// .putmem = p9_adu_putmem,
+};
+DECLARE_HW_UNIT(p9_adu);
diff --git a/libpdbg/bmcfsi.c b/libpdbg/bmcfsi.c
index a1f13a2..0687895 100644
--- a/libpdbg/bmcfsi.c
+++ b/libpdbg/bmcfsi.c
@@ -26,6 +26,7 @@
#include "bitutils.h"
#include "operations.h"
+#include "device.h"
#include "target.h"
#define GPIO_BASE 0x1e780000
@@ -50,54 +51,13 @@ enum gpio {
GPIO_CRONUS_SEL = 4,
};
-/* POWER8 GPIO mappings */
-struct gpio_pin p8_gpio_pins[] = {
- {0, 4}, /* FSI_CLK = A4*/
- {0, 5}, /* FSI_DAT = A5 */
- {0x20, 30}, /* FSI_DAT_EN = H6 */
- {0, 24}, /* FSI_ENABLE = D0 */
- {0, 6}, /* CRONUS_SEL = A6 */
-};
-/* Clock delay in a for loop, determined by trial and error with
- * -O2 */
-#define P8_CLOCK_DELAY 3
-
-/* POWER9 Witherspoon mappings */
-struct gpio_pin p9w_gpio_pins[] = {
- {0x1e0, 16}, /* FSI_CLK = AA0 */
- {0x20, 0}, /* FSI_DAT = E0 */
- {0x80, 10}, /* FSI_DAT_EN = R2 */
- {0, 24}, /* FSI_ENABLE = D0 */
- {0, 6}, /* CRONUS_SEL = A6 */
-};
-#define P9W_CLOCK_DELAY 20
-
-/* POWER9 Romulus mappings */
-struct gpio_pin p9r_gpio_pins[] = {
- {0x1e0, 16}, /* FSI_CLK = AA0 */
- {0x1e0, 18}, /* FSI_DAT = AA2 */
- {0x80, 10}, /* FSI_DAT_EN = R2 */
- {0, 24}, /* FSI_ENABLE = D0 */
- {0, 6}, /* CRONUS_SEL = A6 */
-};
-#define P9R_CLOCK_DELAY 20
-
-struct gpio_pin p9z_gpio_pins[] = {
- {0, 19}, /* FSI_CLK = C3 */
- {0, 18}, /* FSI_DAT = C2 */
- {0x78, 22}, /* FSI_DAT_EN = O6 */
- {0, 24}, /* FSI_ENABLE = D0 */
- {0x78, 30}, /* CRONUS_SEL = P6 */
-};
-#define P9Z_CLOCK_DELAY 20
-
/* Pointer to the GPIO pins to use for this system */
-static struct gpio_pin *gpio_pins;
#define FSI_CLK &gpio_pins[GPIO_FSI_CLK]
#define FSI_DAT &gpio_pins[GPIO_FSI_DAT]
#define FSI_DAT_EN &gpio_pins[GPIO_FSI_DAT_EN]
#define FSI_ENABLE &gpio_pins[GPIO_FSI_ENABLE]
#define CRONUS_SEL &gpio_pins[GPIO_CRONUS_SEL]
+static struct gpio_pin gpio_pins[GPIO_CRONUS_SEL + 1];
/* FSI result symbols */
enum fsi_result {
@@ -132,7 +92,7 @@ static int clock_delay = 0;
static void *gpio_reg = NULL;
static int mem_fd = 0;
-static void fsi_reset(struct target *target);
+static void fsi_reset(struct fsi *fsi);
static uint32_t readl(void *addr)
{
@@ -380,15 +340,12 @@ static enum fsi_result fsi_d_poll_wait(uint8_t slave_id, uint64_t *resp, int len
return rc;
}
-static int fsi_getcfam(struct target *target, uint64_t addr, uint64_t *value)
+static int fsi_getcfam(struct fsi *fsi, uint32_t addr, uint32_t *value)
{
uint64_t seq;
uint64_t resp;
enum fsi_result rc;
- /* This must be the last target in a chain */
- assert(!target->next);
-
/* Format of the read sequence is:
* 6666555555555544444444443333333333222222222211111111110000000000
* 3210987654321098765432109876543210987654321098765432109876543210
@@ -421,15 +378,12 @@ static int fsi_getcfam(struct target *target, uint64_t addr, uint64_t *value)
return rc;
}
-static int fsi_putcfam(struct target *target, uint64_t addr, uint64_t data)
+static int fsi_putcfam(struct fsi *fsi, uint32_t addr, uint32_t data)
{
uint64_t seq;
uint64_t resp;
enum fsi_result rc;
- /* This must be the last target in a chain */
- assert(!target->next);
-
/* Format of the sequence is:
* 6666555555555544444444443333333333222222222211111111110000000000
* 3210987654321098765432109876543210987654321098765432109876543210
@@ -461,22 +415,19 @@ static int fsi_putcfam(struct target *target, uint64_t addr, uint64_t data)
return rc;
}
-static void fsi_reset(struct target *target)
+static void fsi_reset(struct fsi *fsi)
{
- uint64_t val64, old_base = target->base;
-
- target->base = 0;
+ uint32_t val;
fsi_break();
/* Clear own id on the master CFAM to access hMFSI ports */
- fsi_getcfam(target, 0x800, &val64);
- val64 &= ~(PPC_BIT32(6) | PPC_BIT32(7));
- fsi_putcfam(target, 0x800, val64);
- target->base = old_base;
+ fsi_getcfam(fsi, 0x800, &val);
+ val &= ~(PPC_BIT32(6) | PPC_BIT32(7));
+ fsi_putcfam(fsi, 0x800, val);
}
-static void fsi_destroy(struct target *target)
+void fsi_destroy(struct target *target)
{
set_direction_out(FSI_CLK);
set_direction_out(FSI_DAT);
@@ -493,8 +444,9 @@ static void fsi_destroy(struct target *target)
write_gpio(CRONUS_SEL, 0);
}
-int fsi_target_init(struct target *target, const char *name, enum fsi_system_type type, struct target *next)
+int bmcfsi_probe(struct target *target)
{
+ struct fsi *fsi = target_to_fsi(target);
uint64_t value;
if (!mem_fd) {
@@ -506,27 +458,17 @@ int fsi_target_init(struct target *target, const char *name, enum fsi_system_typ
}
if (!gpio_reg) {
- switch (type) {
- case FSI_SYSTEM_P8:
- gpio_pins = p8_gpio_pins;
- clock_delay = P8_CLOCK_DELAY;
- break;
- case FSI_SYSTEM_P9W:
- gpio_pins = p9w_gpio_pins;
- clock_delay = P9W_CLOCK_DELAY;
- break;
- case FSI_SYSTEM_P9R:
- gpio_pins = p9r_gpio_pins;
- clock_delay = P9R_CLOCK_DELAY;
- break;
- case FSI_SYSTEM_P9Z:
- gpio_pins = p9z_gpio_pins;
- clock_delay = P9Z_CLOCK_DELAY;
- break;
- default:
- PR_ERROR("Unrecognized system type specified\n");
- exit(-1);
- }
+ gpio_pins[GPIO_FSI_CLK].offset = dt_prop_get_u32_index(target->dn, "fsi_clk", 0);
+ gpio_pins[GPIO_FSI_CLK].bit = dt_prop_get_u32_index(target->dn, "fsi_clk", 1);
+ gpio_pins[GPIO_FSI_DAT].offset = dt_prop_get_u32_index(target->dn, "fsi_dat", 0);
+ gpio_pins[GPIO_FSI_DAT].bit = dt_prop_get_u32_index(target->dn, "fsi_dat", 1);
+ gpio_pins[GPIO_FSI_DAT_EN].offset = dt_prop_get_u32_index(target->dn, "fsi_dat_en", 0);
+ gpio_pins[GPIO_FSI_DAT_EN].bit = dt_prop_get_u32_index(target->dn, "fsi_dat_en", 1);
+ gpio_pins[GPIO_FSI_ENABLE].offset = dt_prop_get_u32_index(target->dn, "fsi_enable", 0);
+ gpio_pins[GPIO_FSI_ENABLE].bit = dt_prop_get_u32_index(target->dn, "fsi_enable", 1);
+ gpio_pins[GPIO_CRONUS_SEL].offset = dt_prop_get_u32_index(target->dn, "cronus_sel", 0);
+ gpio_pins[GPIO_CRONUS_SEL].bit = dt_prop_get_u32_index(target->dn, "cronus_sel", 1);
+ clock_delay = dt_prop_get_u32(target->dn, "clock_delay");
/* We only have to do this init once per backend */
gpio_reg = mmap(NULL, getpagesize(),
@@ -543,17 +485,21 @@ int fsi_target_init(struct target *target, const char *name, enum fsi_system_typ
write_gpio(FSI_ENABLE, 1);
write_gpio(CRONUS_SEL, 1);
- fsi_reset(target);
+ fsi_reset(fsi);
}
- /* No cascaded devices after this one. */
- assert(next == NULL);
- target_init(target, name, 0, fsi_getcfam, fsi_putcfam, fsi_destroy,
- next);
-
- /* Read chip id */
- CHECK_ERR(read_target(target, 0xc09, &value));
- target->chip_type = get_chip_type(value);
-
return 0;
}
+
+struct fsi bmcfsi = {
+ .target = {
+ .name = "BMC GPIO bit-banging FSI master",
+ .compatible = "ibm,bmcfsi",
+ .class = "fsi",
+ .probe = bmcfsi_probe,
+
+ },
+ .read = fsi_getcfam,
+ .write = fsi_putcfam,
+};
+DECLARE_HW_UNIT(bmcfsi);
diff --git a/libpdbg/cfam.c b/libpdbg/cfam.c
index 15000b8..69bab80 100644
--- a/libpdbg/cfam.c
+++ b/libpdbg/cfam.c
@@ -22,6 +22,9 @@
#include "bitutils.h"
#include "operations.h"
+#undef PR_DEBUG
+#define PR_DEBUG(...)
+
#define FSI_DATA0_REG 0x0
#define FSI_DATA1_REG 0x1
#define FSI_CMD_REG 0x2
@@ -76,44 +79,48 @@
/* We try up to 1.2ms for an OPB access */
#define MFSI_OPB_MAX_TRIES 1200
-static int fsi2pib_getscom(struct target *target, uint64_t addr, uint64_t *value)
+static int fsi2pib_getscom(struct pib *pib, uint64_t addr, uint64_t *value)
{
- uint64_t result;
+ uint32_t result;
usleep(FSI2PIB_RELAX);
/* Get scom works by putting the address in FSI_CMD_REG and
* reading the result from FST_DATA[01]_REG. */
- CHECK_ERR(write_next_target(target, FSI_RESET_REG, FSI_RESET_CMD));
- CHECK_ERR(write_next_target(target, FSI_CMD_REG, addr));
- CHECK_ERR(read_next_target(target, FSI_DATA0_REG, &result));
- *value = result << 32;
- CHECK_ERR(read_next_target(target, FSI_DATA1_REG, &result));
+ CHECK_ERR(fsi_write(&pib->target, FSI_RESET_REG, FSI_RESET_CMD));
+ CHECK_ERR(fsi_write(&pib->target, FSI_CMD_REG, addr));
+ CHECK_ERR(fsi_read(&pib->target, FSI_DATA0_REG, &result));
+ *value = ((uint64_t) result) << 32;
+ CHECK_ERR(fsi_read(&pib->target, FSI_DATA1_REG, &result));
*value |= result;
return 0;
}
-static int fsi2pib_putscom(struct target *target, uint64_t addr, uint64_t value)
+static int fsi2pib_putscom(struct pib *pib, uint64_t addr, uint64_t value)
{
usleep(FSI2PIB_RELAX);
- CHECK_ERR(write_next_target(target, FSI_RESET_REG, FSI_RESET_CMD));
- CHECK_ERR(write_next_target(target, FSI_DATA0_REG, (value >> 32) & 0xffffffff));
- CHECK_ERR(write_next_target(target, FSI_DATA1_REG, value & 0xffffffff));
- CHECK_ERR(write_next_target(target, FSI_CMD_REG, FSI_CMD_REG_WRITE | addr));
+ CHECK_ERR(fsi_write(&pib->target, FSI_RESET_REG, FSI_RESET_CMD));
+ CHECK_ERR(fsi_write(&pib->target, FSI_DATA0_REG, (value >> 32) & 0xffffffff));
+ CHECK_ERR(fsi_write(&pib->target, FSI_DATA1_REG, value & 0xffffffff));
+ CHECK_ERR(fsi_write(&pib->target, FSI_CMD_REG, FSI_CMD_REG_WRITE | addr));
return 0;
}
-int fsi2pib_target_init(struct target *target, const char *name, uint64_t base, struct target *next)
-{
- target_init(target, name, base, fsi2pib_getscom, fsi2pib_putscom, NULL, next);
-
- return 0;
-}
-
-static uint64_t opb_poll(struct target *target, uint64_t *read_data)
+struct pib fsi_pib = {
+ .target = {
+ .name = "POWER FSI2PIB",
+ .compatible = "ibm,fsi-pib",
+ .class = "pib",
+ },
+ .read = fsi2pib_getscom,
+ .write = fsi2pib_putscom,
+};
+DECLARE_HW_UNIT(fsi_pib);
+
+static uint64_t opb_poll(struct opb *opb, uint32_t *read_data)
{
unsigned long retries = MFSI_OPB_MAX_TRIES;
uint64_t sval;
@@ -123,7 +130,7 @@ static uint64_t opb_poll(struct target *target, uint64_t *read_data)
/* We try again every 10us for a bit more than 1ms */
for (;;) {
/* Read OPB status register */
- rc = read_next_target(target, PIB2OPB_REG_STAT, &sval);
+ rc = pib_read(&opb->target, PIB2OPB_REG_STAT, &sval);
if (rc) {
/* Do something here ? */
PR_ERROR("XSCOM error %lld read OPB STAT\n", rc);
@@ -151,8 +158,8 @@ static uint64_t opb_poll(struct target *target, uint64_t *read_data)
* probing the system.
*/
if (stat & OPB_STAT_ANY_ERR) {
- write_next_target(target, PIB2OPB_REG_RESET, PPC_BIT(0));
- write_next_target(target, PIB2OPB_REG_STAT, PPC_BIT(0));
+ pib_write(&opb->target, PIB2OPB_REG_RESET, PPC_BIT(0));
+ pib_write(&opb->target, PIB2OPB_REG_STAT, PPC_BIT(0));
PR_DEBUG("OPB Error. Status 0x%08x\n", stat);
rc = -1;
} else if (read_data) {
@@ -166,7 +173,7 @@ static uint64_t opb_poll(struct target *target, uint64_t *read_data)
return rc;
}
-static int opb_read(struct target *target, uint64_t addr, uint64_t *data)
+static int p8_opb_read(struct opb *opb, uint32_t addr, uint32_t *data)
{
uint64_t opb_cmd = OPB_CMD_READ | OPB_CMD_32BIT;
int64_t rc;
@@ -179,18 +186,17 @@ static int opb_read(struct target *target, uint64_t addr, uint64_t *data)
opb_cmd |= addr;
opb_cmd <<= 32;
- PR_DEBUG("MFSI_OPB_READ: Writing 0x%16llx to XSCOM %llx\n",
- opb_cmd, target->base);
+ PR_DEBUG("MFSI_OPB_READ: Writing 0x%16llx\n", opb_cmd);
- rc = write_next_target(target, PIB2OPB_REG_CMD, opb_cmd);
+ rc = pib_write(&opb->target, PIB2OPB_REG_CMD, opb_cmd);
if (rc) {
PR_ERROR("XSCOM error %lld writing OPB CMD\n", rc);
return OPB_ERR_XSCOM_ERR;
}
- return opb_poll(target, data);
+ return opb_poll(opb, data);
}
-static int opb_write(struct target *target, uint64_t addr, uint64_t data)
+static int p8_opb_write(struct opb *opb, uint32_t addr, uint32_t data)
{
uint64_t opb_cmd = OPB_CMD_WRITE | OPB_CMD_32BIT;
int64_t rc;
@@ -203,27 +209,26 @@ static int opb_write(struct target *target, uint64_t addr, uint64_t data)
opb_cmd <<= 32;
opb_cmd |= data;
- PR_DEBUG("MFSI_OPB_WRITE: Writing 0x%16llx to XSCOM %llx\n",
- opb_cmd, target->base);
+ PR_DEBUG("MFSI_OPB_WRITE: Writing 0x%16llx\n", opb_cmd);
- rc = write_next_target(target, PIB2OPB_REG_CMD, opb_cmd);
+ rc = pib_write(&opb->target, PIB2OPB_REG_CMD, opb_cmd);
if (rc) {
PR_ERROR("XSCOM error %lld writing OPB CMD\n", rc);
return OPB_ERR_XSCOM_ERR;
}
- return opb_poll(target, NULL);
+ return opb_poll(opb, NULL);
}
-int opb_target_init(struct target *target, const char *name, uint64_t base, struct target *next)
-{
- target_init(target, name, base, opb_read, opb_write, NULL, next);
-
- /* Clear any outstanding errors */
- write_next_target(target, PIB2OPB_REG_RESET, PPC_BIT(0));
- write_next_target(target, PIB2OPB_REG_STAT, PPC_BIT(0));
-
- return 0;
-}
+struct opb p8_opb = {
+ .target = {
+ .name = "POWER8 OPB",
+ .compatible = "ibm,power8-opb",
+ .class = "opb",
+ },
+ .read = p8_opb_read,
+ .write = p8_opb_write,
+};
+DECLARE_HW_UNIT(p8_opb);
enum chip_type get_chip_type(uint64_t chip_id)
{
@@ -239,36 +244,90 @@ enum chip_type get_chip_type(uint64_t chip_id)
}
}
-int mfsi_target_init(struct target *target, const char *name, uint64_t base, struct target *next)
+static int p8_hmfsi_read(struct fsi *fsi, uint32_t addr, uint32_t *data)
+{
+ return opb_read(&fsi->target, addr, data);
+}
+
+static int p8_hmfsi_write(struct fsi *fsi, uint32_t addr, uint32_t data)
{
- target_init(target, name, base, NULL, NULL, NULL, next);
+ return opb_write(&fsi->target, addr, data);
+}
+
+static int p8_hmfsi_probe(struct target *target)
+{
+ struct fsi *fsi = target_to_fsi(target);
+ uint32_t value;
+ int rc;
+
+ if ((rc = opb_read(&fsi->target, 0xc09, &value)))
+ return rc;
+
+ fsi->chip_type = get_chip_type(value);
+
+ PR_DEBUG("Found chip type %x\n", fsi->chip_type);
+ if (fsi->chip_type == CHIP_UNKNOWN)
+ return -1;
return 0;
}
-#define HMFSI_STRIDE 0x80000
-int hmfsi_target_probe(struct target *cfam, struct target *targets, int max_target_count)
+struct fsi p8_opb_hmfsi = {
+ .target = {
+ .name = "POWER8 OPB attached hMFSI",
+ .compatible = "ibm,power8-opb-hmfsi",
+ .class = "fsi",
+ .probe = p8_hmfsi_probe,
+ },
+ .read = p8_hmfsi_read,
+ .write = p8_hmfsi_write,
+};
+DECLARE_HW_UNIT(p8_opb_hmfsi);
+
+static int cfam_hmfsi_read(struct fsi *fsi, uint32_t addr, uint32_t *data)
{
- struct target *target = targets;
- uint64_t value;
- int target_count = 0, i;
-
- for (i = 0; i < 8 && i < max_target_count; i++) {
- mfsi_target_init(target, "MFSI Port", 0x80000 + i * HMFSI_STRIDE, cfam);
- if (read_target(target, 0xc09, &value)) {
- target_del(target);
- continue;
- }
+ struct target *parent_fsi = fsi->target.dn->parent->target;
- target->chip_type = get_chip_type(value);
- if (target->chip_type == CHIP_UNKNOWN) {
- target_del(target);
- continue;
- }
+ addr += dt_get_address(fsi->target.dn, 0, NULL);
- target++;
- target_count++;
- }
+ return fsi_read(parent_fsi, addr, data);
+}
+
+static int cfam_hmfsi_write(struct fsi *fsi, uint32_t addr, uint32_t data)
+{
+ struct target *parent_fsi = fsi->target.dn->parent->target;
+
+ addr += dt_get_address(fsi->target.dn, 0, NULL);
- return target_count;
+ return fsi_write(parent_fsi, addr, data);
}
+
+static int cfam_hmfsi_probe(struct target *target)
+{
+ struct fsi *fsi = target_to_fsi(target);
+ uint32_t value;
+ int rc;
+
+ if ((rc = fsi_read(&fsi->target, 0xc09, &value)))
+ return rc;
+
+ fsi->chip_type = get_chip_type(value);
+
+ PR_DEBUG("Found chip type %x\n", fsi->chip_type);
+ if (fsi->chip_type == CHIP_UNKNOWN)
+ return -1;
+
+ return 0;
+}
+
+struct fsi cfam_hmfsi = {
+ .target = {
+ .name = "CFAM hMFSI Port",
+ .compatible = "ibm,fsi-hmfsi",
+ .class = "fsi",
+ .probe = cfam_hmfsi_probe,
+ },
+ .read = cfam_hmfsi_read,
+ .write = cfam_hmfsi_write,
+};
+DECLARE_HW_UNIT(cfam_hmfsi);
diff --git a/libpdbg/chip.c b/libpdbg/chip.c
index 8abc131..cde11a3 100644
--- a/libpdbg/chip.c
+++ b/libpdbg/chip.c
@@ -20,6 +20,7 @@
#include <ccan/array_size/array_size.h>
#include <unistd.h>
+#include "target.h"
#include "operations.h"
#include "bitutils.h"
@@ -68,7 +69,7 @@
#define SPECIAL_WKUP_DONE PPC_BIT(31)
/* How long (in us) to wait for a special wakeup to complete */
-#define SPECIAL_WKUP_TIMEOUT 1000
+#define SPECIAL_WKUP_TIMEOUT 10
/* Opcodes */
#define MTNIA_OPCODE 0x00000002UL
@@ -135,21 +136,22 @@ static uint64_t ld(uint64_t rt, uint64_t ds, uint64_t ra)
return LD_OPCODE | (rt << 21) | (ra << 16) | (ds << 2);
}
-static int assert_special_wakeup(struct target *chip)
+static int assert_special_wakeup(struct chiplet *chip)
{
int i = 0;
uint64_t gp0;
/* Assert special wakeup to prevent low power states */
- CHECK_ERR(write_target(chip, PMSPCWKUPFSP_REG, FSP_SPECIAL_WAKEUP));
+ CHECK_ERR(pib_write(&chip->target, PMSPCWKUPFSP_REG, FSP_SPECIAL_WAKEUP));
/* Poll for completion */
do {
usleep(1);
- CHECK_ERR(read_target(chip, EX_PM_GP0_REG, &gp0));
+ CHECK_ERR(pib_read(&chip->target, EX_PM_GP0_REG, &gp0));
if (i++ > SPECIAL_WKUP_TIMEOUT) {
- PR_ERROR("Timeout waiting for special wakeup\n");
+ PR_ERROR("Timeout waiting for special wakeup on %s@0x%08lx\n", chip->target.name,
+ dt_get_address(chip->target.dn, 0, NULL));
return -1;
}
} while (!(gp0 & SPECIAL_WKUP_DONE));
@@ -157,41 +159,41 @@ static int assert_special_wakeup(struct target *chip)
return 0;
}
-static int deassert_special_wakeup(struct target *chip)
+static int deassert_special_wakeup(struct chiplet *chip)
{
/* Assert special wakeup to prevent low power states */
- CHECK_ERR(write_target(chip, PMSPCWKUPFSP_REG, 0));
+ CHECK_ERR(pib_write(&chip->target, PMSPCWKUPFSP_REG, 0));
return 0;
}
-uint64_t chiplet_thread_status(struct target *thread)
+uint64_t thread_status(struct thread *thread)
{
return thread->status;
}
-static uint64_t get_thread_status(struct target *thread)
+static uint64_t get_thread_status(struct thread *thread)
{
uint64_t val, mode_reg, thread_status = thread->status;
/* Need to activete debug mode to get complete status */
- CHECK_ERR(read_target(thread, RAS_MODE_REG, &mode_reg));
+ CHECK_ERR(pib_read(&thread->target, RAS_MODE_REG, &mode_reg));
mode_reg |= MR_THREAD_IN_DEBUG;
- CHECK_ERR(write_target(thread, RAS_MODE_REG, mode_reg));
+ CHECK_ERR(pib_write(&thread->target, RAS_MODE_REG, mode_reg));
/* Read status */
- CHECK_ERR(read_target(thread, RAS_STATUS_REG, &val));
+ CHECK_ERR(pib_read(&thread->target, RAS_STATUS_REG, &val));
thread_status = SETFIELD(THREAD_STATUS_ACTIVE, thread_status, !!(val & RAS_STATUS_THREAD_ACTIVE));
thread_status = SETFIELD(THREAD_STATUS_QUIESCE, thread_status, !!(val & RAS_STATUS_TS_QUIESCE));
/* Read POW status */
- CHECK_ERR(read_target(thread, POW_STATUS_REG, &val));
+ CHECK_ERR(pib_read(&thread->target, POW_STATUS_REG, &val));
thread_status = SETFIELD(THREAD_STATUS_STATE, thread_status, GETFIELD(PMC_POW_STATE, val));
/* Clear debug mode */
mode_reg &= ~MR_THREAD_IN_DEBUG;
- CHECK_ERR(write_target(thread, RAS_MODE_REG, mode_reg));
+ CHECK_ERR(pib_write(&thread->target, RAS_MODE_REG, mode_reg));
return thread_status;
}
@@ -199,48 +201,48 @@ static uint64_t get_thread_status(struct target *thread)
/*
* Single step the thread count instructions.
*/
-int ram_step_thread(struct target *thread, int count)
+int ram_step_thread(struct thread *thread, int count)
{
int i;
uint64_t ras_mode, ras_status;
/* Activate single-step mode */
- CHECK_ERR(read_target(thread, RAS_MODE_REG, &ras_mode));
+ CHECK_ERR(pib_read(&thread->target, RAS_MODE_REG, &ras_mode));
ras_mode |= MR_DO_SINGLE_MODE;
- CHECK_ERR(write_target(thread, RAS_MODE_REG, ras_mode));
+ CHECK_ERR(pib_write(&thread->target, RAS_MODE_REG, ras_mode));
/* Step the core */
for (i = 0; i < count; i++) {
- CHECK_ERR(write_target(thread, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_STEP));
+ CHECK_ERR(pib_write(&thread->target, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_STEP));
/* Wait for step to complete */
do {
- CHECK_ERR(read_target(thread, RAS_STATUS_REG, &ras_status));
+ CHECK_ERR(pib_read(&thread->target, RAS_STATUS_REG, &ras_status));
} while (!(ras_status & RAS_STATUS_INST_COMPLETE));
}
/* Deactivate single-step mode */
ras_mode &= ~MR_DO_SINGLE_MODE;
- CHECK_ERR(write_target(thread, RAS_MODE_REG, ras_mode));
+ CHECK_ERR(pib_write(&thread->target, RAS_MODE_REG, ras_mode));
return 0;
}
-int ram_stop_thread(struct target *thread)
+int ram_stop_thread(struct thread *thread)
{
- int i = 0, thread_id = (thread->base >> 4) & 0x7;
+ int i = 0;
uint64_t val;
- struct target *chip = thread->next;
+ struct chiplet *chip = target_to_chiplet(thread->target.dn->parent->target);
do {
/* Quiese active thread */
- CHECK_ERR(write_target(thread, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_STOP));
+ CHECK_ERR(pib_write(&thread->target, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_STOP));
/* Wait for thread to quiese */
- CHECK_ERR(read_target(chip, RAS_STATUS_REG, &val));
+ CHECK_ERR(pib_read(&chip->target, RAS_STATUS_REG, &val));
if (i++ > RAS_STATUS_TIMEOUT) {
PR_ERROR("Unable to quiesce thread %d (0x%016llx).\n",
- thread->index, val);
+ thread->id, val);
PR_ERROR("Continuing anyway.\n");
if (val & PPC_BIT(48)) {
PR_ERROR("Unable to continue\n");
@@ -258,27 +260,42 @@ int ram_stop_thread(struct target *thread)
/* Make the threads RAM thread active */
- CHECK_ERR(read_target(chip, THREAD_ACTIVE_REG, &val));
- val |= PPC_BIT(8) >> thread_id;
- CHECK_ERR(write_target(chip, THREAD_ACTIVE_REG, val));
+ CHECK_ERR(pib_read(&chip->target, THREAD_ACTIVE_REG, &val));
+ val |= PPC_BIT(8) >> thread->id;
+ CHECK_ERR(pib_write(&chip->target, THREAD_ACTIVE_REG, val));
return 0;
}
-int ram_start_thread(struct target *thread)
+int ram_start_thread(struct thread *thread)
{
uint64_t val;
- struct target *chip = thread->next;
- int thread_id = (thread->base >> 4) & 0x7;
+ struct chiplet *chip = target_to_chiplet(thread->target.dn->parent->target);
/* Activate thread */
- CHECK_ERR(write_target(thread, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_START));
+ CHECK_ERR(pib_write(&thread->target, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_START));
/* Restore thread active */
- CHECK_ERR(read_target(chip, THREAD_ACTIVE_REG, &val));
- val &= ~(PPC_BIT(8) >> thread_id);
- val |= PPC_BIT(thread_id);
- CHECK_ERR(write_target(chip, THREAD_ACTIVE_REG, val));
+ CHECK_ERR(pib_read(&chip->target, THREAD_ACTIVE_REG, &val));
+ val &= ~(PPC_BIT(8) >> thread->id);
+ val |= PPC_BIT(thread->id);
+ CHECK_ERR(pib_write(&chip->target, THREAD_ACTIVE_REG, val));
+
+ return 0;
+}
+
+/* We can only ram a thread if all the threads on the core/chip are
+ * quiesced */
+int ram_status(struct chiplet *chip)
+{
+ struct dt_node *dn;
+
+ dt_for_each_compatible(chip->target.dn, dn, "ibm,power8-thread") {
+ struct thread *thread;
+ thread = target_to_thread(dn->target);
+ if (!(get_thread_status(thread) & THREAD_STATUS_QUIESCE))
+ return -1;
+ }
return 0;
}
@@ -291,26 +308,32 @@ int ram_start_thread(struct target *thread)
* data. Note that only register r0 is saved and restored so opcodes
* must not touch other registers.
*/
-static int ram_instructions(struct target *thread, uint64_t *opcodes,
+static int ram_instructions(struct thread *thread, uint64_t *opcodes,
uint64_t *results, int len, unsigned int lpar)
{
uint64_t ram_mode, val, opcode, r0 = 0, r1 = 0;
- struct target *chiplet = thread->next;
- int thread_id = (thread->base >> 4) & 0x7;
+ struct chiplet *chip = target_to_chiplet(thread->target.dn->parent->target);
int i;
int exception = 0;
+ /* Check to see if the parent chip is in a state that can RAM instructions */
+ if (ram_status(chip))
+ return 1;
+
+ if (!(thread_status(thread) & THREAD_STATUS_ACTIVE))
+ return 2;
+
/* Activate RAM mode */
- CHECK_ERR(read_target(chiplet, RAM_MODE_REG, &ram_mode));
+ CHECK_ERR(pib_read(&chip->target, RAM_MODE_REG, &ram_mode));
ram_mode |= RAM_MODE_ENABLE;
- CHECK_ERR(write_target(chiplet, RAM_MODE_REG, ram_mode));
+ CHECK_ERR(pib_write(&chip->target, RAM_MODE_REG, ram_mode));
/* Setup SPRC to use SPRD */
val = SPR_MODE_SPRC_WR_EN;
val = SETFIELD(SPR_MODE_SPRC_SEL, val, 1 << (3 - lpar));
- val = SETFIELD(SPR_MODE_SPRC_T_SEL, val, 1 << (7 - thread_id));
- CHECK_ERR(write_target(chiplet, SPR_MODE_REG, val));
- CHECK_ERR(write_target(chiplet, L0_SCOM_SPRC_REG, SCOM_SPRC_SCRATCH_SPR));
+ val = SETFIELD(SPR_MODE_SPRC_T_SEL, val, 1 << (7 - thread->id));
+ CHECK_ERR(pib_write(&chip->target, SPR_MODE_REG, val));
+ CHECK_ERR(pib_write(&chip->target, L0_SCOM_SPRC_REG, SCOM_SPRC_SCRATCH_SPR));
/* RAM instructions */
for (i = -2; i < len + 2; i++) {
@@ -320,26 +343,26 @@ static int ram_instructions(struct target *thread, uint64_t *opcodes,
/* Save r0 (assumes opcodes don't touch other registers) */
opcode = mtspr(277, 0);
else if (i < len) {
- CHECK_ERR(write_target(chiplet, SCR0_REG, results[i]));
+ CHECK_ERR(pib_write(&chip->target, SCR0_REG, results[i]));
opcode = opcodes[i];
} else if (i == len) {
/* Restore r0 */
- CHECK_ERR(write_target(chiplet, SCR0_REG, r0));
+ CHECK_ERR(pib_write(&chip->target, SCR0_REG, r0));
opcode = mfspr(0, 277);
} else if (i == len + 1) {
/* Restore r1 */
- CHECK_ERR(write_target(chiplet, SCR0_REG, r1));
+ CHECK_ERR(pib_write(&chip->target, SCR0_REG, r1));
opcode = mfspr(0, 277);
}
/* ram instruction */
- val = SETFIELD(RAM_THREAD_SELECT, 0ULL, thread_id);
+ val = SETFIELD(RAM_THREAD_SELECT, 0ULL, thread->id);
val = SETFIELD(RAM_INSTR, val, opcode);
- CHECK_ERR(write_target(chiplet, RAM_CTRL_REG, val));
+ CHECK_ERR(pib_write(&chip->target, RAM_CTRL_REG, val));
/* wait for completion */
do {
- CHECK_ERR(read_target(chiplet, RAM_STATUS_REG, &val));
+ CHECK_ERR(pib_read(&chip->target, RAM_STATUS_REG, &val));
} while (!((val & PPC_BIT(1)) || ((val & PPC_BIT(2)) && (val & PPC_BIT(3)))));
if (!(val & PPC_BIT(1))) {
@@ -353,7 +376,7 @@ static int ram_instructions(struct target *thread, uint64_t *opcodes,
}
/* Save the results */
- CHECK_ERR(read_target(chiplet, SCR0_REG, &val));
+ CHECK_ERR(pib_read(&chip->target, SCR0_REG, &val));
if (i == -2)
r1 = val;
else if (i == -1)
@@ -364,7 +387,7 @@ static int ram_instructions(struct target *thread, uint64_t *opcodes,
/* Disable RAM mode */
ram_mode &= ~RAM_MODE_ENABLE;
- CHECK_ERR(write_target(chiplet, RAM_MODE_REG, ram_mode));
+ CHECK_ERR(pib_write(&chip->target, RAM_MODE_REG, ram_mode));
return exception;
}
@@ -372,7 +395,7 @@ static int ram_instructions(struct target *thread, uint64_t *opcodes,
/*
* Get gpr value. Chip must be stopped.
*/
-int ram_getgpr(struct target *thread, int gpr, uint64_t *value)
+int ram_getgpr(struct thread *thread, int gpr, uint64_t *value)
{
uint64_t opcodes[] = {mtspr(277, gpr)};
uint64_t results[] = {0};
@@ -382,7 +405,7 @@ int ram_getgpr(struct target *thread, int gpr, uint64_t *value)
return 0;
}
-int ram_putgpr(struct target *thread, int gpr, uint64_t value)
+int ram_putgpr(struct thread *thread, int gpr, uint64_t value)
{
uint64_t opcodes[] = {mfspr(gpr, 277)};
uint64_t results[] = {value};
@@ -392,7 +415,7 @@ int ram_putgpr(struct target *thread, int gpr, uint64_t value)
return 0;
}
-int ram_getnia(struct target *thread, uint64_t *value)
+int ram_getnia(struct thread *thread, uint64_t *value)
{
uint64_t opcodes[] = {mfnia(0), mtspr(277, 0)};
uint64_t results[] = {0, 0};
@@ -402,7 +425,7 @@ int ram_getnia(struct target *thread, uint64_t *value)
return 0;
}
-int ram_putnia(struct target *thread, uint64_t value)
+int ram_putnia(struct thread *thread, uint64_t value)
{
uint64_t opcodes[] = {mfspr(0, 277), mtnia(0)};
uint64_t results[] = {value, 0};
@@ -411,7 +434,7 @@ int ram_putnia(struct target *thread, uint64_t value)
return 0;
}
-int ram_getspr(struct target *thread, int spr, uint64_t *value)
+int ram_getspr(struct thread *thread, int spr, uint64_t *value)
{
uint64_t opcodes[] = {mfspr(0, spr), mtspr(277, 0)};
uint64_t results[] = {0, 0};
@@ -421,7 +444,7 @@ int ram_getspr(struct target *thread, int spr, uint64_t *value)
return 0;
}
-int ram_putspr(struct target *thread, int spr, uint64_t value)
+int ram_putspr(struct thread *thread, int spr, uint64_t value)
{
uint64_t opcodes[] = {mfspr(0, 277), mtspr(spr, 0)};
uint64_t results[] = {value, 0};
@@ -430,7 +453,7 @@ int ram_putspr(struct target *thread, int spr, uint64_t value)
return 0;
}
-int ram_getmsr(struct target *thread, uint64_t *value)
+int ram_getmsr(struct thread *thread, uint64_t *value)
{
uint64_t opcodes[] = {mfmsr(0), mtspr(277, 0)};
uint64_t results[] = {0, 0};
@@ -440,7 +463,7 @@ int ram_getmsr(struct target *thread, uint64_t *value)
return 0;
}
-int ram_putmsr(struct target *thread, uint64_t value)
+int ram_putmsr(struct thread *thread, uint64_t value)
{
uint64_t opcodes[] = {mfspr(0, 277), mtmsr(0)};
uint64_t results[] = {value, 0};
@@ -449,7 +472,7 @@ int ram_putmsr(struct target *thread, uint64_t value)
return 0;
}
-int ram_getmem(struct target *thread, uint64_t addr, uint64_t *value)
+int ram_getmem(struct thread *thread, uint64_t addr, uint64_t *value)
{
uint64_t opcodes[] = {mfspr(0, 277), mfspr(1, 277), ld(0, 0, 1), mtspr(277, 0)};
uint64_t results[] = {0xdeaddeaddeaddead, addr, 0, 0};
@@ -459,81 +482,54 @@ int ram_getmem(struct target *thread, uint64_t addr, uint64_t *value)
return 0;
}
-int thread_target_init(struct target *thread, const char *name, uint64_t thread_id, struct target *next)
-{
- target_init(thread, name, 0x13000 | (thread_id << 4), NULL, NULL, NULL, next);
- thread->status = get_thread_status(thread);
-
- /* Threads always exist, although they may not be in a useful state for most operations */
- return 0;
-}
-
/*
* Initialise all viable threads for ramming on the given chiplet.
*/
-int thread_target_probe(struct target *chiplet, struct target *targets, int max_target_count)
+static int p8_thread_probe(struct target *target)
{
- struct target *thread = targets;
- int i, count = 0;
+ struct thread *thread = target_to_thread(target);
- for (i = 0; i < THREADS_PER_CORE && i < max_target_count; i++) {
- thread_target_init(thread, "Thread", i, chiplet);
- thread++;
- count++;
- }
+ thread->id = (dt_get_address(target->dn, 0, NULL) >> 4) & 0xf;
+ thread->status = get_thread_status(thread);
- return count;
+ return 0;
}
-int chiplet_target_init(struct target *target, const char *name, uint64_t chip_id, struct target *next)
+struct thread p8_thread = {
+ .target = {
+ .name = "POWER8 Thread",
+ .compatible = "ibm,power8-thread",
+ .class = "thread",
+ .probe = p8_thread_probe,
+ },
+};
+DECLARE_HW_UNIT(p8_thread);
+
+static int p8_chiplet_probe(struct target *target)
{
- uint64_t value, base;
-
- base = 0x10000000 | (chip_id << 24);
-
- target_init(target, name, base, NULL, NULL, NULL, next);
+ uint64_t value;
+ struct chiplet *chiplet = target_to_chiplet(target);
+ int i, count = 0, rc = 0;
/* Work out if this chip is actually present */
- if (read_target(target, SCOM_EX_GP3, &value)) {
+ if (pib_read(target, SCOM_EX_GP3, &value)) {
PR_DEBUG("Error reading chip GP3 register\n");
return -1;
}
- /* Return 0 is a chip is actually present. We still leave the
- target initialised even if it isn't present as a user may
- want to continue anyway. */
- return -!GETFIELD(PPC_BIT(0), value);
-}
-
-/* Initialises all possible chiplets on the given processor
- * target. *targets should point to pre-allocated memory with enough
- * free space for the maximum number of targets. Returns the number of
- * chips found. */
-int chiplet_target_probe(struct target *processor, struct target *targets, int max_target_count)
-{
- struct target *target = targets;
- int i, count = 0, rc = 0;
-
- /* P9 chiplets are not currently supported */
- if (processor->chip_type == CHIP_P9)
- return 0;
-
- for (i = 0; i <= 0xf && i < max_target_count; i++) {
- if (i == 0 || i == 7 || i == 8 || i == 0xf)
- /* 0, 7, 8 & 0xf are reserved */
- continue;
-
- if (!(rc = chiplet_target_init(target, "Chiplet", i, processor))) {
- assert_special_wakeup(target);
- target++;
- count++;
- } else
- target_del(target);
- }
-
- if (rc)
- /* The last target is invalid, zero it out */
- memset(target, 0, sizeof(*target));
+ if (!GETFIELD(PPC_BIT(0), value))
+ return -1;
- return count;
+ assert_special_wakeup(chiplet);
+ return 0;
}
+
+struct chiplet p8_chiplet = {
+ .target = {
+ .name = "POWER8 Chiplet",
+ .compatible = "ibm,power8-core",
+ .class = "chiplet",
+ .probe = p8_chiplet_probe,
+ },
+};
+DECLARE_HW_UNIT(p8_chiplet);
diff --git a/libpdbg/compiler.h b/libpdbg/compiler.h
new file mode 100644
index 0000000..35bf165
--- /dev/null
+++ b/libpdbg/compiler.h
@@ -0,0 +1,53 @@
+/* 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.
+ */
+
+#ifndef __COMPILER_H
+#define __COMPILER_H
+
+#ifndef __ASSEMBLY__
+
+#include <stddef.h>
+
+/* Macros for various compiler bits and pieces */
+#define __packed __attribute__((packed))
+#define __align(x) __attribute__((__aligned__(x)))
+#define __unused __attribute__((unused))
+#define __used __attribute__((used))
+#define __section(x) __attribute__((__section__(x)))
+#define __noreturn __attribute__((noreturn))
+/* not __const as this has a different meaning (const) */
+#define __attrconst __attribute__((const))
+#define __warn_unused_result __attribute__((warn_unused_result))
+
+#if 0 /* Provided by gcc stddef.h */
+#define offsetof(type,m) __builtin_offsetof(type,m)
+#endif
+
+#define __nomcount __attribute__((no_instrument_function))
+
+/* Compiler barrier */
+static inline void barrier(void)
+{
+ asm volatile("" : : : "memory");
+}
+
+#endif /* __ASSEMBLY__ */
+
+/* Stringification macro */
+#define __tostr(x) #x
+#define tostr(x) __tostr(x)
+
+#endif /* __COMPILER_H */
diff --git a/libpdbg/device.c b/libpdbg/device.c
new file mode 100644
index 0000000..725d656
--- /dev/null
+++ b/libpdbg/device.c
@@ -0,0 +1,943 @@
+/* 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.
+ */
+
+#include "device.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <libfdt/libfdt.h>
+#include <libfdt/libfdt_internal.h>
+#include <ccan/str/str.h>
+#include <ccan/endian/endian.h>
+
+#undef PR_DEBUG
+#define PR_DEBUG(...)
+
+#define zalloc(size) calloc(1, size)
+#define prerror printf
+#define is_rodata(p) false
+
+/* Used to give unique handles. */
+u32 last_phandle = 0;
+
+struct dt_node *dt_root;
+struct dt_node *dt_chosen;
+
+static const char *take_name(const char *name)
+{
+ if (!is_rodata(name) && !(name = strdup(name))) {
+ prerror("Failed to allocate copy of name");
+ abort();
+ }
+ return name;
+}
+
+static void free_name(const char *name)
+{
+ if (!is_rodata(name))
+ free((char *)name);
+}
+
+static struct dt_node *new_node(const char *name)
+{
+ struct dt_node *node = malloc(sizeof *node);
+ if (!node) {
+ prerror("Failed to allocate node\n");
+ abort();
+ }
+
+ node->name = take_name(name);
+ node->parent = NULL;
+ list_head_init(&node->properties);
+ list_head_init(&node->children);
+ /* FIXME: locking? */
+ node->phandle = ++last_phandle;
+ return node;
+}
+
+struct dt_node *dt_new_root(const char *name)
+{
+ return new_node(name);
+}
+
+static const char *get_unitname(const struct dt_node *node)
+{
+ const char *c = strchr(node->name, '@');
+
+ if (!c)
+ return NULL;
+
+ return c + 1;
+}
+
+int dt_cmp_subnodes(const struct dt_node *a, const struct dt_node *b)
+{
+ const char *a_unit = get_unitname(a);
+ const char *b_unit = get_unitname(b);
+
+ ptrdiff_t basenamelen = a_unit - a->name;
+
+ /* sort hex unit addresses by number */
+ if (a_unit && b_unit && !strncmp(a->name, b->name, basenamelen)) {
+ unsigned long long a_num, b_num;
+ char *a_end, *b_end;
+
+ a_num = strtoul(a_unit, &a_end, 16);
+ b_num = strtoul(b_unit, &b_end, 16);
+
+ /* only compare if the unit addr parsed correctly */
+ if (*a_end == 0 && *b_end == 0)
+ return (a_num > b_num) - (a_num < b_num);
+ }
+
+ return strcmp(a->name, b->name);
+}
+
+bool dt_attach_root(struct dt_node *parent, struct dt_node *root)
+{
+ struct dt_node *node;
+
+ assert(!root->parent);
+
+ if (list_empty(&parent->children)) {
+ list_add(&parent->children, &root->list);
+ root->parent = parent;
+
+ return true;
+ }
+
+ dt_for_each_child(parent, node) {
+ int cmp = dt_cmp_subnodes(node, root);
+
+ /* Look for duplicates */
+ if (cmp == 0) {
+ prerror("DT: %s failed, duplicate %s\n",
+ __func__, root->name);
+ return false;
+ }
+
+ /* insert before the first node that's larger
+ * the the node we're inserting */
+ if (cmp > 0)
+ break;
+ }
+
+ list_add_before(&parent->children, &root->list, &node->list);
+ root->parent = parent;
+
+ return true;
+}
+
+static inline void dt_destroy(struct dt_node *dn)
+{
+ if (!dn)
+ return;
+
+ free_name(dn->name);
+ free(dn);
+}
+
+struct dt_node *dt_new(struct dt_node *parent, const char *name)
+{
+ struct dt_node *new;
+ assert(parent);
+
+ new = new_node(name);
+ if (!dt_attach_root(parent, new)) {
+ dt_destroy(new);
+ return NULL;
+ }
+ return new;
+}
+
+struct dt_node *dt_new_addr(struct dt_node *parent, const char *name,
+ uint64_t addr)
+{
+ char *lname;
+ struct dt_node *new;
+ size_t len;
+
+ assert(parent);
+ len = strlen(name) + STR_MAX_CHARS(addr) + 2;
+ lname = malloc(len);
+ if (!lname)
+ return NULL;
+ snprintf(lname, len, "%s@%llx", name, (long long)addr);
+ new = new_node(lname);
+ free(lname);
+ if (!dt_attach_root(parent, new)) {
+ dt_destroy(new);
+ return NULL;
+ }
+ return new;
+}
+
+struct dt_node *dt_new_2addr(struct dt_node *parent, const char *name,
+ uint64_t addr0, uint64_t addr1)
+{
+ char *lname;
+ struct dt_node *new;
+ size_t len;
+ assert(parent);
+
+ len = strlen(name) + 2*STR_MAX_CHARS(addr0) + 3;
+ lname = malloc(len);
+ if (!lname)
+ return NULL;
+ snprintf(lname, len, "%s@%llx,%llx",
+ name, (long long)addr0, (long long)addr1);
+ new = new_node(lname);
+ free(lname);
+ if (!dt_attach_root(parent, new)) {
+ dt_destroy(new);
+ return NULL;
+ }
+ return new;
+}
+
+static struct dt_node *__dt_copy(struct dt_node *node, struct dt_node *parent,
+ bool root)
+{
+ struct dt_property *prop, *new_prop;
+ struct dt_node *new_node, *child;
+
+ new_node = dt_new(parent, node->name);
+ if (!new_node)
+ return NULL;
+
+ list_for_each(&node->properties, prop, list) {
+ new_prop = dt_add_property(new_node, prop->name, prop->prop,
+ prop->len);
+ if (!new_prop)
+ goto fail;
+ }
+
+ list_for_each(&node->children, child, list) {
+ child = __dt_copy(child, new_node, false);
+ if (!child)
+ goto fail;
+ }
+
+ return new_node;
+
+fail:
+ /* dt_free will recurse for us, so only free when we unwind to the
+ * top-level failure */
+ if (root)
+ dt_free(new_node);
+ return NULL;
+}
+
+struct dt_node *dt_copy(struct dt_node *node, struct dt_node *parent)
+{
+ return __dt_copy(node, parent, true);
+}
+
+char *dt_get_path(const struct dt_node *node)
+{
+ unsigned int len = 0;
+ const struct dt_node *n;
+ char *path, *p;
+
+ /* Dealing with NULL is for test/debug purposes */
+ if (!node)
+ return strdup("<NULL>");
+
+ for (n = node; n; n = n->parent) {
+ len += strlen(n->name);
+ if (n->parent || n == node)
+ len++;
+ }
+ path = zalloc(len + 1);
+ assert(path);
+ p = path + len;
+ for (n = node; n; n = n->parent) {
+ len = strlen(n->name);
+ p -= len;
+ memcpy(p, n->name, len);
+ if (n->parent || n == node)
+ *(--p) = '/';
+ }
+ assert(p == path);
+
+ return p;
+}
+
+static const char *__dt_path_split(const char *p,
+ const char **namep, unsigned int *namel,
+ const char **addrp, unsigned int *addrl)
+{
+ const char *at, *sl;
+
+ *namel = *addrl = 0;
+
+ /* Skip initial '/' */
+ while (*p == '/')
+ p++;
+
+ /* Check empty path */
+ if (*p == 0)
+ return p;
+
+ at = strchr(p, '@');
+ sl = strchr(p, '/');
+ if (sl == NULL)
+ sl = p + strlen(p);
+ if (sl < at)
+ at = NULL;
+ if (at) {
+ *addrp = at + 1;
+ *addrl = sl - at - 1;
+ }
+ *namep = p;
+ *namel = at ? (at - p) : (sl - p);
+
+ return sl;
+}
+
+struct dt_node *dt_find_by_path(struct dt_node *root, const char *path)
+{
+ struct dt_node *n;
+ const char *pn, *pa, *p = path, *nn, *na;
+ unsigned int pnl, pal, nnl, nal;
+ bool match;
+
+ /* Walk path components */
+ while (*p) {
+ /* Extract next path component */
+ p = __dt_path_split(p, &pn, &pnl, &pa, &pal);
+ if (pnl == 0 && pal == 0)
+ break;
+
+ /* Compare with each child node */
+ match = false;
+ list_for_each(&root->children, n, list) {
+ match = true;
+ __dt_path_split(n->name, &nn, &nnl, &na, &nal);
+ if (pnl && (pnl != nnl || strncmp(pn, nn, pnl)))
+ match = false;
+ if (pal && (pal != nal || strncmp(pa, na, pal)))
+ match = false;
+ if (match) {
+ root = n;
+ break;
+ }
+ }
+
+ /* No child match */
+ if (!match)
+ return NULL;
+ }
+ return root;
+}
+
+struct dt_node *dt_find_by_name(struct dt_node *root, const char *name)
+{
+ struct dt_node *child, *match;
+
+ list_for_each(&root->children, child, list) {
+ if (!strcmp(child->name, name))
+ return child;
+
+ match = dt_find_by_name(child, name);
+ if (match)
+ return match;
+ }
+
+ return NULL;
+}
+
+struct dt_node *dt_find_by_phandle(struct dt_node *root, u32 phandle)
+{
+ struct dt_node *node;
+
+ dt_for_each_node(root, node)
+ if (node->phandle == phandle)
+ return node;
+ return NULL;
+}
+
+static struct dt_property *new_property(struct dt_node *node,
+ const char *name, size_t size)
+{
+ struct dt_property *p = malloc(sizeof(*p) + size);
+ char *path;
+
+ if (!p) {
+ path = dt_get_path(node);
+ prerror("Failed to allocate property \"%s\" for %s of %zu bytes\n",
+ name, path, size);
+ free(path);
+ abort();
+ }
+ if (dt_find_property(node, name)) {
+ path = dt_get_path(node);
+ prerror("Duplicate property \"%s\" in node %s\n",
+ name, path);
+ free(path);
+ abort();
+
+ }
+
+ p->name = take_name(name);
+ p->len = size;
+ list_add_tail(&node->properties, &p->list);
+ return p;
+}
+
+struct dt_property *dt_add_property(struct dt_node *node,
+ const char *name,
+ const void *val, size_t size)
+{
+ struct dt_property *p;
+
+ /*
+ * Filter out phandle properties, we re-generate them
+ * when flattening
+ */
+ if (strcmp(name, "linux,phandle") == 0 ||
+ strcmp(name, "phandle") == 0) {
+ assert(size == 4);
+ node->phandle = *(const u32 *)val;
+ if (node->phandle >= last_phandle)
+ last_phandle = node->phandle;
+ return NULL;
+ }
+
+ p = new_property(node, name, size);
+ if (size)
+ memcpy(p->prop, val, size);
+ return p;
+}
+
+void dt_resize_property(struct dt_property **prop, size_t len)
+{
+ size_t new_len = sizeof(**prop) + len;
+
+ *prop = realloc(*prop, new_len);
+
+ /* Fix up linked lists in case we moved. (note: not an empty list). */
+ (*prop)->list.next->prev = &(*prop)->list;
+ (*prop)->list.prev->next = &(*prop)->list;
+}
+
+struct dt_property *dt_add_property_string(struct dt_node *node,
+ const char *name,
+ const char *value)
+{
+ return dt_add_property(node, name, value, strlen(value)+1);
+}
+
+struct dt_property *dt_add_property_nstr(struct dt_node *node,
+ const char *name,
+ const char *value, unsigned int vlen)
+{
+ struct dt_property *p;
+ char *tmp = zalloc(vlen + 1);
+
+ if (!tmp)
+ return NULL;
+
+ strncpy(tmp, value, vlen);
+ p = dt_add_property(node, name, tmp, strlen(tmp)+1);
+ free(tmp);
+
+ return p;
+}
+
+struct dt_property *__dt_add_property_cells(struct dt_node *node,
+ const char *name,
+ int count, ...)
+{
+ struct dt_property *p;
+ u32 *val;
+ unsigned int i;
+ va_list args;
+
+ p = new_property(node, name, count * sizeof(u32));
+ val = (u32 *)p->prop;
+ va_start(args, count);
+ for (i = 0; i < count; i++)
+ val[i] = cpu_to_fdt32(va_arg(args, u32));
+ va_end(args);
+ return p;
+}
+
+struct dt_property *__dt_add_property_u64s(struct dt_node *node,
+ const char *name,
+ int count, ...)
+{
+ struct dt_property *p;
+ u64 *val;
+ unsigned int i;
+ va_list args;
+
+ p = new_property(node, name, count * sizeof(u64));
+ val = (u64 *)p->prop;
+ va_start(args, count);
+ for (i = 0; i < count; i++)
+ val[i] = cpu_to_fdt64(va_arg(args, u64));
+ va_end(args);
+ return p;
+}
+
+struct dt_property *__dt_add_property_strings(struct dt_node *node,
+ const char *name,
+ int count, ...)
+{
+ struct dt_property *p;
+ unsigned int i, size;
+ va_list args;
+ const char *sstr;
+ char *s;
+
+ va_start(args, count);
+ for (i = size = 0; i < count; i++) {
+ sstr = va_arg(args, const char *);
+ if (sstr)
+ size += strlen(sstr) + 1;
+ }
+ va_end(args);
+ if (!size)
+ size = 1;
+ p = new_property(node, name, size);
+ s = (char *)p->prop;
+ *s = 0;
+ va_start(args, count);
+ for (i = 0; i < count; i++) {
+ sstr = va_arg(args, const char *);
+ if (sstr) {
+ strcpy(s, sstr);
+ s = s + strlen(sstr) + 1;
+ }
+ }
+ va_end(args);
+ return p;
+}
+
+void dt_del_property(struct dt_node *node, struct dt_property *prop)
+{
+ list_del_from(&node->properties, &prop->list);
+ free_name(prop->name);
+ free(prop);
+}
+
+u32 dt_property_get_cell(const struct dt_property *prop, u32 index)
+{
+ assert(prop->len >= (index+1)*sizeof(u32));
+ /* Always aligned, so this works. */
+ return fdt32_to_cpu(((const u32 *)prop->prop)[index]);
+}
+
+/* First child of this node. */
+struct dt_node *dt_first(const struct dt_node *root)
+{
+ return list_top(&root->children, struct dt_node, list);
+}
+
+/* Return next node, or NULL. */
+struct dt_node *dt_next(const struct dt_node *root,
+ const struct dt_node *prev)
+{
+ /* Children? */
+ if (!list_empty(&prev->children))
+ return dt_first(prev);
+
+ do {
+ /* More siblings? */
+ if (prev->list.next != &prev->parent->children.n)
+ return list_entry(prev->list.next, struct dt_node,list);
+
+ /* No more siblings, move up to parent. */
+ prev = prev->parent;
+ } while (prev != root);
+
+ return NULL;
+}
+
+struct dt_property *__dt_find_property(struct dt_node *node, const char *name)
+{
+ struct dt_property *i;
+
+ list_for_each(&node->properties, i, list)
+ if (strcmp(i->name, name) == 0)
+ return i;
+ return NULL;
+}
+
+struct dt_property *dt_find_property(const struct dt_node *node,
+ const char *name)
+{
+ struct dt_property *i;
+
+ list_for_each(&node->properties, i, list)
+ if (strcmp(i->name, name) == 0)
+ return i;
+ return NULL;
+}
+
+void dt_check_del_prop(struct dt_node *node, const char *name)
+{
+ struct dt_property *p;
+
+ p = __dt_find_property(node, name);
+ if (p)
+ dt_del_property(node, p);
+}
+const struct dt_property *dt_require_property(const struct dt_node *node,
+ const char *name, int wanted_len)
+{
+ const struct dt_property *p = dt_find_property(node, name);
+
+ if (!p) {
+ const char *path = dt_get_path(node);
+
+ prerror("DT: Missing required property %s/%s\n",
+ path, name);
+ assert(false);
+ }
+ if (wanted_len >= 0 && p->len != wanted_len) {
+ const char *path = dt_get_path(node);
+
+ prerror("DT: Unexpected property length %s/%s\n",
+ path, name);
+ prerror("DT: Expected len: %d got len: %zu\n",
+ wanted_len, p->len);
+ assert(false);
+ }
+
+ return p;
+}
+
+bool dt_has_node_property(const struct dt_node *node,
+ const char *name, const char *val)
+{
+ const struct dt_property *p = dt_find_property(node, name);
+
+ if (!p)
+ return false;
+ if (!val)
+ return true;
+
+ return p->len == strlen(val) + 1 && memcmp(p->prop, val, p->len) == 0;
+}
+
+bool dt_prop_find_string(const struct dt_property *p, const char *s)
+{
+ const char *c, *end;
+
+ if (!p)
+ return false;
+ c = p->prop;
+ end = c + p->len;
+
+ while(c < end) {
+ if (!strcasecmp(s, c))
+ return true;
+ c += strlen(c) + 1;
+ }
+ return false;
+}
+
+bool dt_node_is_compatible(const struct dt_node *node, const char *compat)
+{
+ const struct dt_property *p = dt_find_property(node, "compatible");
+
+ return dt_prop_find_string(p, compat);
+}
+
+struct dt_node *dt_find_compatible_node(struct dt_node *root,
+ struct dt_node *prev,
+ const char *compat)
+{
+ struct dt_node *node;
+
+ node = prev ? dt_next(root, prev) : root;
+ for (; node; node = dt_next(root, node))
+ if (dt_node_is_compatible(node, compat))
+ return node;
+ return NULL;
+}
+
+u64 dt_prop_get_u64(const struct dt_node *node, const char *prop)
+{
+ const struct dt_property *p = dt_require_property(node, prop, 8);
+
+ return ((u64)dt_property_get_cell(p, 0) << 32)
+ | dt_property_get_cell(p, 1);
+}
+
+u64 dt_prop_get_u64_def(const struct dt_node *node, const char *prop, u64 def)
+{
+ const struct dt_property *p = dt_find_property(node, prop);
+
+ if (!p)
+ return def;
+
+ return ((u64)dt_property_get_cell(p, 0) << 32)
+ | dt_property_get_cell(p, 1);
+}
+
+u32 dt_prop_get_u32(const struct dt_node *node, const char *prop)
+{
+ const struct dt_property *p = dt_require_property(node, prop, 4);
+
+ return dt_property_get_cell(p, 0);
+}
+
+u32 dt_prop_get_u32_def(const struct dt_node *node, const char *prop, u32 def)
+{
+ const struct dt_property *p = dt_find_property(node, prop);
+
+ if (!p)
+ return def;
+
+ return dt_property_get_cell(p, 0);
+}
+
+u32 dt_prop_get_u32_index(const struct dt_node *node, const char *prop, u32 index)
+{
+ const struct dt_property *p = dt_require_property(node, prop, -1);
+
+ return dt_property_get_cell(p, index);
+}
+
+const void *dt_prop_get(const struct dt_node *node, const char *prop)
+{
+ const struct dt_property *p = dt_require_property(node, prop, -1);
+
+ return p->prop;
+}
+
+const void *dt_prop_get_def(const struct dt_node *node, const char *prop,
+ void *def)
+{
+ const struct dt_property *p = dt_find_property(node, prop);
+
+ return p ? p->prop : def;
+}
+
+const void *dt_prop_get_def_size(const struct dt_node *node, const char *prop,
+ void *def, size_t *len)
+{
+ const struct dt_property *p = dt_find_property(node, prop);
+ *len = 0;
+ if (p)
+ *len = p->len;
+
+ return p ? p->prop : def;
+}
+
+u32 dt_prop_get_cell(const struct dt_node *node, const char *prop, u32 cell)
+{
+ const struct dt_property *p = dt_require_property(node, prop, -1);
+
+ return dt_property_get_cell(p, cell);
+}
+
+u32 dt_prop_get_cell_def(const struct dt_node *node, const char *prop,
+ u32 cell, u32 def)
+{
+ const struct dt_property *p = dt_find_property(node, prop);
+
+ if (!p)
+ return def;
+
+ return dt_property_get_cell(p, cell);
+}
+
+void dt_free(struct dt_node *node)
+{
+ struct dt_node *child;
+ struct dt_property *p;
+
+ while ((child = list_top(&node->children, struct dt_node, list)))
+ dt_free(child);
+
+ while ((p = list_pop(&node->properties, struct dt_property, list))) {
+ free_name(p->name);
+ free(p);
+ }
+
+ if (node->parent)
+ list_del_from(&node->parent->children, &node->list);
+ dt_destroy(node);
+}
+
+int dt_expand_node(struct dt_node *node, const void *fdt, int fdt_node)
+{
+ const struct fdt_property *prop;
+ int offset, nextoffset, err;
+ struct dt_node *child;
+ const char *name;
+ uint32_t tag;
+
+ if (((err = fdt_check_header(fdt)) != 0)
+ || ((err = _fdt_check_node_offset(fdt, fdt_node)) < 0)) {
+ prerror("FDT: Error %d parsing node 0x%x\n", err, fdt_node);
+ return -1;
+ }
+
+ nextoffset = err;
+ do {
+ offset = nextoffset;
+
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+ switch (tag) {
+ case FDT_PROP:
+ prop = _fdt_offset_ptr(fdt, offset);
+ name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
+ dt_add_property(node, name, prop->data,
+ fdt32_to_cpu(prop->len));
+ break;
+ case FDT_BEGIN_NODE:
+ name = fdt_get_name(fdt, offset, NULL);
+ child = dt_new_root(name);
+ assert(child);
+ nextoffset = dt_expand_node(child, fdt, offset);
+
+ /*
+ * This may fail in case of duplicate, keep it
+ * going for now, we may ultimately want to
+ * assert
+ */
+ (void)dt_attach_root(node, child);
+ break;
+ case FDT_END:
+ return -1;
+ }
+ } while (tag != FDT_END_NODE);
+
+ return nextoffset;
+}
+
+void dt_expand(const void *fdt)
+{
+ PR_DEBUG("FDT: Parsing fdt @%p\n", fdt);
+
+ if (dt_expand_node(dt_root, fdt, 0) < 0)
+ abort();
+}
+
+u64 dt_get_number(const void *pdata, unsigned int cells)
+{
+ const u32 *p = pdata;
+ u64 ret = 0;
+
+ while(cells--)
+ ret = (ret << 32) | be32_to_cpu(*(p++));
+ return ret;
+}
+
+u32 dt_n_address_cells(const struct dt_node *node)
+{
+ if (!node->parent)
+ return 0;
+ return dt_prop_get_u32_def(node->parent, "#address-cells", 2);
+}
+
+u32 dt_n_size_cells(const struct dt_node *node)
+{
+ if (!node->parent)
+ return 0;
+ return dt_prop_get_u32_def(node->parent, "#size-cells", 1);
+}
+
+u64 dt_get_address(const struct dt_node *node, unsigned int index,
+ u64 *out_size)
+{
+ const struct dt_property *p;
+ u32 na = dt_n_address_cells(node);
+ u32 ns = dt_n_size_cells(node);
+ u32 pos, n;
+
+ p = dt_require_property(node, "reg", -1);
+ n = (na + ns) * sizeof(u32);
+ pos = n * index;
+ assert((pos + n) <= p->len);
+ if (out_size)
+ *out_size = dt_get_number(p->prop + pos + na * sizeof(u32), ns);
+ return dt_get_number(p->prop + pos, na);
+}
+
+static u32 __dt_get_chip_id(const struct dt_node *node)
+{
+ const struct dt_property *prop;
+
+ for (; node; node = node->parent) {
+ prop = dt_find_property(node, "ibm,chip-id");
+ if (prop)
+ return dt_property_get_cell(prop, 0);
+ }
+ return 0xffffffff;
+}
+
+u32 dt_get_chip_id(const struct dt_node *node)
+{
+ u32 id = __dt_get_chip_id(node);
+ assert(id != 0xffffffff);
+ return id;
+}
+
+struct dt_node *dt_find_compatible_node_on_chip(struct dt_node *root,
+ struct dt_node *prev,
+ const char *compat,
+ uint32_t chip_id)
+{
+ struct dt_node *node;
+
+ node = prev ? dt_next(root, prev) : root;
+ for (; node; node = dt_next(root, node)) {
+ u32 cid = __dt_get_chip_id(node);
+ if (cid == chip_id &&
+ dt_node_is_compatible(node, compat))
+ return node;
+ }
+ return NULL;
+}
+
+unsigned int dt_count_addresses(const struct dt_node *node)
+{
+ const struct dt_property *p;
+ u32 na = dt_n_address_cells(node);
+ u32 ns = dt_n_size_cells(node);
+ u32 n;
+
+ p = dt_require_property(node, "reg", -1);
+ n = (na + ns) * sizeof(u32);
+
+ if (n == 0)
+ return 0;
+
+ return p->len / n;
+}
+
+u64 dt_translate_address(const struct dt_node *node, unsigned int index,
+ u64 *out_size)
+{
+ /* XXX TODO */
+ return dt_get_address(node, index, out_size);
+}
+
+bool dt_node_is_enabled(struct dt_node *node)
+{
+ const struct dt_property *p = dt_find_property(node, "status");
+
+ if (!p)
+ return true;
+
+ return p->len > 1 && p->prop[0] == 'o' && p->prop[1] == 'k';
+}
diff --git a/libpdbg/device.h b/libpdbg/device.h
new file mode 100644
index 0000000..3fcf5c3
--- /dev/null
+++ b/libpdbg/device.h
@@ -0,0 +1,251 @@
+/* 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.
+ */
+
+#ifndef __DEVICE_H
+#define __DEVICE_H
+#include <ccan/list/list.h>
+#include <ccan/short_types/short_types.h>
+#include "compiler.h"
+
+/* Any property or node with this prefix will not be passed to the kernel. */
+#define DT_PRIVATE "skiboot,"
+
+/*
+ * An in-memory representation of a node in the device tree.
+ *
+ * This is trivially flattened into an fdt.
+ *
+ * Note that the add_* routines will make a copy of the name if it's not
+ * a read-only string (ie. usually a string literal).
+ */
+struct dt_property {
+ struct list_node list;
+ const char *name;
+ size_t len;
+ char prop[/* len */];
+};
+
+struct dt_node {
+ const char *name;
+ struct list_node list;
+ struct list_head properties;
+ struct list_head children;
+ struct dt_node *parent;
+ u32 phandle;
+ struct target *target;
+};
+
+/* This is shared with device_tree.c .. make it static when
+ * the latter is gone (hopefully soon)
+ */
+extern u32 last_phandle;
+
+extern struct dt_node *dt_root;
+extern struct dt_node *dt_chosen;
+
+/* Create a root node: ie. a parentless one. */
+struct dt_node *dt_new_root(const char *name);
+
+/* Graft a root node into this tree. */
+bool dt_attach_root(struct dt_node *parent, struct dt_node *root);
+
+/* Add a child node. */
+struct dt_node *dt_new(struct dt_node *parent, const char *name);
+struct dt_node *dt_new_addr(struct dt_node *parent, const char *name,
+ uint64_t unit_addr);
+struct dt_node *dt_new_2addr(struct dt_node *parent, const char *name,
+ uint64_t unit_addr0, uint64_t unit_addr1);
+
+/* Copy node to new parent, including properties and subnodes */
+struct dt_node *dt_copy(struct dt_node *node, struct dt_node *parent);
+
+/* Add a property node, various forms. */
+struct dt_property *dt_add_property(struct dt_node *node,
+ const char *name,
+ const void *val, size_t size);
+struct dt_property *dt_add_property_string(struct dt_node *node,
+ const char *name,
+ const char *value);
+struct dt_property *dt_add_property_nstr(struct dt_node *node,
+ const char *name,
+ const char *value, unsigned int vlen);
+
+/* Given out enough GCC extensions, we will achieve enlightenment! */
+#define dt_add_property_strings(node, name, ...) \
+ __dt_add_property_strings((node), ((name)), \
+ sizeof((const char *[]) { __VA_ARGS__ })/sizeof(const char *), \
+ __VA_ARGS__)
+
+struct dt_property *__dt_add_property_strings(struct dt_node *node,
+ const char *name,
+ int count, ...);
+
+/* Given out enough GCC extensions, we will achieve enlightenment! */
+#define dt_add_property_cells(node, name, ...) \
+ __dt_add_property_cells((node), ((name)), \
+ sizeof((u32[]) { __VA_ARGS__ })/sizeof(u32), \
+ __VA_ARGS__)
+
+struct dt_property *__dt_add_property_cells(struct dt_node *node,
+ const char *name,
+ int count, ...);
+
+#define dt_add_property_u64s(node, name, ...) \
+ __dt_add_property_u64s((node), ((name)), \
+ sizeof((u64[]) { __VA_ARGS__ })/sizeof(u64), \
+ __VA_ARGS__)
+
+struct dt_property *__dt_add_property_u64s(struct dt_node *node,
+ const char *name,
+ int count, ...);
+
+static inline struct dt_property *dt_add_property_u64(struct dt_node *node,
+ const char *name, u64 val)
+{
+ return dt_add_property_cells(node, name, (u32)(val >> 32), (u32)val);
+}
+
+void dt_del_property(struct dt_node *node, struct dt_property *prop);
+
+void dt_check_del_prop(struct dt_node *node, const char *name);
+
+/* Warning: moves *prop! */
+void dt_resize_property(struct dt_property **prop, size_t len);
+
+u32 dt_property_get_cell(const struct dt_property *prop, u32 index);
+
+/* First child of this node. */
+struct dt_node *dt_first(const struct dt_node *root);
+
+/* Return next node, or NULL. */
+struct dt_node *dt_next(const struct dt_node *root, const struct dt_node *prev);
+
+/* Iterate nodes */
+#define dt_for_each_node(root, node) \
+ for (node = dt_first(root); node; node = dt_next(root, node))
+
+#define dt_for_each_child(parent, node) \
+ list_for_each(&parent->children, node, list)
+
+/* Find a string in a string list */
+bool dt_prop_find_string(const struct dt_property *p, const char *s);
+
+/* Check a compatible property */
+bool dt_node_is_compatible(const struct dt_node *node, const char *compat);
+
+/* Find a node based on compatible property */
+struct dt_node *dt_find_compatible_node(struct dt_node *root,
+ struct dt_node *prev,
+ const char *compat);
+
+#define dt_for_each_compatible(root, node, compat) \
+ for (node = NULL; \
+ (node = dt_find_compatible_node(root, node, compat)) != NULL;)
+
+struct dt_node *dt_find_compatible_node_on_chip(struct dt_node *root,
+ struct dt_node *prev,
+ const char *compat,
+ uint32_t chip_id);
+
+#define dt_for_each_compatible_on_chip(root, node, compat, chip_id) \
+ for (node = NULL; \
+ (node = dt_find_compatible_node_on_chip(root, node,\
+ compat, chip_id)) != NULL;)
+/* Check status property */
+bool dt_node_is_enabled(struct dt_node *node);
+
+/* Build the full path for a node. Return a new block of memory, caller
+ * shall free() it
+ */
+char *dt_get_path(const struct dt_node *node);
+
+/* Find a node by path */
+struct dt_node *dt_find_by_path(struct dt_node *root, const char *path);
+
+/* Find a child node by name */
+struct dt_node *dt_find_by_name(struct dt_node *root, const char *name);
+
+/* Find a node by phandle */
+struct dt_node *dt_find_by_phandle(struct dt_node *root, u32 phandle);
+
+/* Find a property by name. */
+struct dt_property *dt_find_property(const struct dt_node *node,\
+ const char *name);
+const struct dt_property *dt_require_property(const struct dt_node *node,
+ const char *name, int wanted_len);
+
+/* non-const variant */
+struct dt_property *__dt_find_property(struct dt_node *node, const char *name);
+
+/* Find a property by name, check if it's the same as val. */
+bool dt_has_node_property(const struct dt_node *node,
+ const char *name, const char *val);
+
+/* Free a node (and any children). */
+void dt_free(struct dt_node *node);
+
+/* Parse an initial fdt */
+void dt_expand(const void *fdt);
+int dt_expand_node(struct dt_node *node, const void *fdt, int fdt_node) __warn_unused_result;
+
+/* Simplified accessors */
+u64 dt_prop_get_u64(const struct dt_node *node, const char *prop);
+u64 dt_prop_get_u64_def(const struct dt_node *node, const char *prop, u64 def);
+u32 dt_prop_get_u32(const struct dt_node *node, const char *prop);
+u32 dt_prop_get_u32_def(const struct dt_node *node, const char *prop, u32 def);
+u32 dt_prop_get_u32_index(const struct dt_node *node, const char *prop, u32 index);
+const void *dt_prop_get(const struct dt_node *node, const char *prop);
+const void *dt_prop_get_def(const struct dt_node *node, const char *prop,
+ void *def);
+const void *dt_prop_get_def_size(const struct dt_node *node, const char *prop,
+ void *def, size_t *len);
+u32 dt_prop_get_cell(const struct dt_node *node, const char *prop, u32 cell);
+u32 dt_prop_get_cell_def(const struct dt_node *node, const char *prop, u32 cell, u32 def);
+
+/* Parsing helpers */
+u32 dt_n_address_cells(const struct dt_node *node);
+u32 dt_n_size_cells(const struct dt_node *node);
+u64 dt_get_number(const void *pdata, unsigned int cells);
+
+/* Find an ibm,chip-id property in this node; if not found, walk up the parent
+ * nodes. Returns -1 if no chip-id property exists. */
+u32 dt_get_chip_id(const struct dt_node *node);
+
+/* Address accessors ("reg" properties parsing). No translation,
+ * only support "simple" address forms (1 or 2 cells). Asserts
+ * if address doesn't exist
+ */
+u64 dt_get_address(const struct dt_node *node, unsigned int index,
+ u64 *out_size);
+
+/* Count "reg" property entries */
+unsigned int dt_count_addresses(const struct dt_node *node);
+
+/* Address translation
+ *
+ * WARNING: Current implementation is simplified and will not
+ * handle complex address formats with address space indicators
+ * nor will it handle "ranges" translations yet... (XX TODO)
+ */
+u64 dt_translate_address(const struct dt_node *node, unsigned int index,
+ u64 *out_size);
+
+/* compare function used to sort child nodes by name when added to the
+ * tree. This is mainly here for testing.
+ */
+int dt_cmp_subnodes(const struct dt_node *a, const struct dt_node *b);
+
+#endif /* __DEVICE_H */
diff --git a/libpdbg/fakepib.c b/libpdbg/fakepib.c
new file mode 100644
index 0000000..22ea3e7
--- /dev/null
+++ b/libpdbg/fakepib.c
@@ -0,0 +1,38 @@
+/* Copyright 2016 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.
+ */
+#include "operations.h"
+
+static int fake_read(struct pib *pib, uint64_t addr, uint64_t *value)
+{
+ *value = 0xdeadbeef;
+ return 0;
+}
+
+static int fake_write(struct pib *pib, uint64_t addr, uint64_t value)
+{
+ return 0;
+}
+
+struct pib fake_pib = {
+ .target = {
+ .name = "Fake PIB",
+ .compatible = "ibm,fake-fsi",
+ .class = "fsi",
+ },
+ .read = fake_read,
+ .write = fake_write,
+};
+DECLARE_HW_UNIT(fake_pib);
diff --git a/libpdbg/i2c.c b/libpdbg/i2c.c
index a75056c..b2a913d 100644
--- a/libpdbg/i2c.c
+++ b/libpdbg/i2c.c
@@ -58,9 +58,9 @@ static int i2c_set_scom_addr(struct i2c_data *i2c_data, uint32_t addr)
return 0;
}
-static int i2c_getscom(struct target *target, uint64_t addr, uint64_t *value)
+static int i2c_getscom(struct pib *pib, uint64_t addr, uint64_t *value)
{
- struct i2c_data *i2c_data = target->priv;
+ struct i2c_data *i2c_data = pib->priv;
uint64_t data;
CHECK_ERR(i2c_set_scom_addr(i2c_data, addr));
@@ -75,9 +75,9 @@ static int i2c_getscom(struct target *target, uint64_t addr, uint64_t *value)
return 0;
}
-static int i2c_putscom(struct target *target, uint64_t addr, uint64_t value)
+static int i2c_putscom(struct pib *pib, uint64_t addr, uint64_t value)
{
- struct i2c_data *i2c_data = target->priv;
+ struct i2c_data *i2c_data = pib->priv;
uint8_t data[12];
/* Setup scom address */
@@ -106,21 +106,27 @@ static int i2c_putscom(struct target *target, uint64_t addr, uint64_t value)
return 0;
}
-static void i2c_destroy(struct target *target)
+static void i2c_destroy(struct pib *pib)
{
- struct i2c_data *i2c_data = target->priv;
+ struct i2c_data *i2c_data = pib->priv;
close(i2c_data->fd);
- free(target->priv);
+ free(i2c_data);
}
/*
* Initialise a i2c backend on the given bus at the given bus address.
*/
-int i2c_target_init(struct target *target, const char *name, struct target *next,
- const char *bus, int addr)
+int i2c_target_probe(struct target *target)
{
+ struct pib *pib = target_to_pib(target);
struct i2c_data *i2c_data;
+ const char *bus;
+ int addr;
+
+ bus = "/dev/i2c4";
+ addr = dt_get_address(pib->target.dn, 0, NULL);
+ assert(addr);
i2c_data = malloc(sizeof(*i2c_data));
if (!i2c_data)
@@ -136,10 +142,19 @@ int i2c_target_init(struct target *target, const char *name, struct target *next
if (i2c_set_addr(i2c_data->fd, addr) < 0)
return -1;
- target_init(target, name, addr, i2c_getscom, i2c_putscom, i2c_destroy,
- next);
- target->priv = i2c_data;
- target->chip_type = CHIP_P8;
+ pib->priv = i2c_data;
return 0;
}
+
+struct pib p8_i2c_pib = {
+ .target = {
+ .name = "POWER8 I2C Slave",
+ .compatible = "ibm,power8-i2c-slave",
+ .class = "pib",
+ .probe = i2c_target_probe,
+ },
+ .read = i2c_getscom,
+ .write = i2c_putscom,
+};
+DECLARE_HW_UNIT(p8_i2c_pib);
diff --git a/libpdbg/kernel.c b/libpdbg/kernel.c
index 559af08..6f16310 100644
--- a/libpdbg/kernel.c
+++ b/libpdbg/kernel.c
@@ -31,52 +31,10 @@
#define FSI_SCAN_PATH "/sys/devices/platform/fsi-master/scan"
#define FSI_CFAM_PATH "/sys/devices/platform/fsi-master/slave@00:00/raw"
-#define FSI_SCOM_PATH "/sys/devices/platform/fsi-master/slave@00:00/"
int fsi_fd;
-int scom_fd;
-static int kernel_putscom(struct target *target, uint64_t addr, uint64_t value)
-{
- int rc;
-
- rc = lseek(scom_fd, addr, SEEK_SET);
- if (rc < 0) {
- warn("Failed to seek %s", FSI_SCOM_PATH);
- return errno;
- }
-
- rc = write(scom_fd, &value, sizeof(value));
- if (rc < 0) {
- warn("Failed to write to 0x%016llx", addr);
- return errno;
- }
-
- return 0;
-
-}
-
-static int kernel_getscom(struct target *target, uint64_t addr, uint64_t *value)
-{
- int rc;
-
- rc = lseek(fsi_fd, addr, SEEK_SET);
- if (rc < 0) {
- warn("Failed to seek %s", FSI_SCOM_PATH);
- return errno;
- }
-
- rc = read(fsi_fd, value, sizeof(*value));
- if (rc < 0) {
- warn("Failed to read from 0x%016llx", addr);
- return errno;
- }
-
- return 0;
-
-}
-
-static int kernel_fsi_getcfam(struct target *target, uint64_t addr64, uint64_t *value)
+static int kernel_fsi_getcfam(struct fsi *fsi, uint32_t addr64, uint32_t *value)
{
int rc;
uint32_t addr = (addr64 & 0x7ffc00) | ((addr64 & 0x3ff) << 2);
@@ -100,7 +58,7 @@ static int kernel_fsi_getcfam(struct target *target, uint64_t addr64, uint64_t *
return 0;
}
-static int kernel_fsi_putcfam(struct target *target, uint64_t addr64, uint64_t data)
+static int kernel_fsi_putcfam(struct fsi *fsi, uint32_t addr64, uint32_t data)
{
int rc;
uint32_t addr = (addr64 & 0x7ffc00) | ((addr64 & 0x3ff) << 2);
@@ -141,9 +99,9 @@ static void kernel_fsi_scan_devices(void)
close(fd);
}
-int kernel_fsi_target_init(struct target *target, const char *name,
- struct target *next)
+int kernel_fsi_probe(struct target *target)
{
+ struct fsi *fsi = target_to_fsi(target);
uint64_t value;
if (!fsi_fd) {
@@ -153,36 +111,31 @@ int kernel_fsi_target_init(struct target *target, const char *name,
/* Open first raw device */
fsi_fd = open(FSI_CFAM_PATH, O_RDWR | O_SYNC);
if (fsi_fd >= 0)
- goto found;
+ return 0;
tries--;
/* Scan */
kernel_fsi_scan_devices();
sleep(1);
}
- if (fsi_fd < 0)
+ if (fsi_fd < 0) {
err(errno, "Unable to open %s", FSI_CFAM_PATH);
+ return -1;
+ }
}
-found:
- /* No cascaded devices after this one. */
- assert(next == NULL);
- target_init(target, name, 0, kernel_fsi_getcfam, kernel_fsi_putcfam,
- kernel_fsi_destroy, next);
-
- /* Read chip id */
- CHECK_ERR(read_target(target, 0xc09, &value));
- target->chip_type = get_chip_type(value);
- return 0;
+ return -1;
}
-int kernel_fsi2pib_target_init(struct target *target, const char *name,
- uint64_t base, struct target *next)
-{
- target_init(target, name, base, kernel_getscom, kernel_putscom, NULL,
- next);
-
- return 0;
-
-}
+struct fsi kernel_fsi = {
+ .target = {
+ .name = "Kernel based FSI master",
+ .compatible = "ibm,kernel-fsi",
+ .class = "fsi",
+ .probe = kernel_fsi_probe,
+ },
+ .read = kernel_fsi_getcfam,
+ .write = kernel_fsi_putcfam,
+};
+DECLARE_HW_UNIT(kernel_fsi);
diff --git a/libpdbg/operations.h b/libpdbg/operations.h
index 76a1ca8..4175af3 100644
--- a/libpdbg/operations.h
+++ b/libpdbg/operations.h
@@ -21,17 +21,10 @@
/* Error codes */
#define EFSI 1
-#define PR_DEBUG(x, args...) \
- //fprintf(stderr, x, ##args)
-#define PR_INFO(x, args...) \
- fprintf(stderr, x, ##args)
-#define PR_ERROR(x, args...) \
- fprintf(stderr, "%s: " x, __FUNCTION__, ##args)
-
#define CHECK_ERR(x) do { \
if (x) { \
PR_DEBUG("%s: %d\n", __FUNCTION__, __LINE__); \
- return -EFSI; \
+ return x; \
} \
} while(0)
@@ -52,37 +45,25 @@ int adu_putmem(struct target *target, uint64_t start_addr, uint8_t *input, uint6
#define THREAD_STATUS_SLEEP PPC_BITMASK(61, 62)
#define THREAD_STATUS_QUIESCE PPC_BIT(60)
-int ram_getgpr(struct target *thread, int gpr, uint64_t *value);
-int ram_putgpr(struct target *thread, int gpr, uint64_t value);
-int ram_getnia(struct target *thread, uint64_t *value);
-int ram_putnia(struct target *thread, uint64_t value);
-int ram_getspr(struct target *thread, int spr, uint64_t *value);
-int ram_putspr(struct target *thread, int spr, uint64_t value);
-int ram_getmsr(struct target *thread, uint64_t *value);
-int ram_putmsr(struct target *thread, uint64_t value);
-int ram_getmem(struct target *thread, uint64_t addr, uint64_t *value);
-uint64_t chiplet_thread_status(struct target *thread);
-int ram_stop_thread(struct target *thread);
-int ram_step_thread(struct target *thread, int count);
-int ram_start_thread(struct target *thread);
+int ram_getgpr(struct thread *thread, int gpr, uint64_t *value);
+int ram_putgpr(struct thread *thread, int gpr, uint64_t value);
+int ram_getnia(struct thread *thread, uint64_t *value);
+int ram_putnia(struct thread *thread, uint64_t value);
+int ram_getspr(struct thread *thread, int spr, uint64_t *value);
+int ram_putspr(struct thread *thread, int spr, uint64_t value);
+int ram_getmsr(struct thread *thread, uint64_t *value);
+int ram_putmsr(struct thread *thread, uint64_t value);
+int ram_getmem(struct thread *thread, uint64_t addr, uint64_t *value);
+uint64_t thread_status(struct thread *thread);
+int ram_stop_thread(struct thread *thread);
+int ram_step_thread(struct thread *thread, int count);
+int ram_start_thread(struct thread *thread);
+void fsi_destroy(struct target *target);
/* GDB server functionality */
int gdbserver_start(uint16_t port);
enum fsi_system_type {FSI_SYSTEM_P8, FSI_SYSTEM_P9W, FSI_SYSTEM_P9R, FSI_SYSTEM_P9Z};
-int fsi_target_init(struct target *target, const char *name, enum fsi_system_type tpye, struct target *next);
-int fsi_target_probe(struct target *targets, int max_target_count);
-int i2c_target_init(struct target *target, const char *name, struct target *next,
- const char *bus, int addr);
-int kernel_fsi_target_init(struct target *target, const char *name, struct target *next);
-int fsi2pib_target_init(struct target *target, const char *name, uint64_t base, struct target *next);
-int kernel_fsi2pib_target_init(struct target *target, const char *name, uint64_t base, struct target *next);
-int opb_target_init(struct target *target, const char *name, uint64_t base, struct target *next);
enum chip_type get_chip_type(uint64_t chip_id);
-int thread_target_init(struct target *thread, const char *name, uint64_t thread_id, struct target *next);
-int thread_target_probe(struct target *chiplet, struct target *targets, int max_target_count);
-int chiplet_target_init(struct target *target, const char *name, uint64_t chip_id, struct target *next);
-int chiplet_target_probe(struct target *processor, struct target *targets, int max_target_count);
-int hmfsi_target_probe(struct target *cfam, struct target *targets, int max_target_count);
#endif
diff --git a/libpdbg/target.c b/libpdbg/target.c
index a006f49..4235f4b 100644
--- a/libpdbg/target.c
+++ b/libpdbg/target.c
@@ -2,70 +2,266 @@
#include <stdint.h>
#include <assert.h>
#include <ccan/list/list.h>
+#include <libfdt/libfdt.h>
#include "target.h"
+#include "device.h"
-void target_init(struct target *target, const char *name, uint64_t base,
- target_read read, target_write write,
- target_destroy destroy, struct target *next)
+#undef PR_DEBUG
+#define PR_DEBUG(...)
+
+struct list_head empty_list = LIST_HEAD_INIT(empty_list);
+struct list_head target_classes = LIST_HEAD_INIT(target_classes);
+
+/* Work out the address to access based on the current target and
+ * final class name */
+static struct dt_node *get_class_target_addr(struct dt_node *dn, const char *name, uint64_t *addr)
+{
+ /* Check class */
+ while (strcmp(dn->target->class, name)) {
+ /* Keep walking the tree translating addresses */
+ *addr += dt_get_address(dn, 0, NULL);
+ dn = dn->parent;
+
+ /* The should always be a parent. If there isn't it
+ * means we traversed up the whole device tree and
+ * didn't find a parent matching the given class. */
+ assert(dn);
+ assert(dn->target);
+ }
+
+ return dn;
+}
+
+int pib_read(struct target *pib_dt, uint64_t addr, uint64_t *data)
+{
+ struct pib *pib;
+ struct dt_node *dn = pib_dt->dn;
+
+ dn = get_class_target_addr(dn, "pib", &addr);
+ pib_dt = dn->target;
+ pib = target_to_pib(pib_dt);
+ return pib->read(pib, addr, data);
+}
+
+int pib_write(struct target *pib_dt, uint64_t addr, uint64_t data)
+{
+ struct pib *pib;
+ struct dt_node *dn = pib_dt->dn;
+
+ dn = get_class_target_addr(dn, "pib", &addr);
+ pib_dt = dn->target;
+ pib = target_to_pib(pib_dt);
+ return pib->write(pib, addr, data);
+}
+
+int opb_read(struct target *opb_dt, uint32_t addr, uint32_t *data)
{
- target->name = name;
- target->index = -1;
- target->base = base;
- target->read = read;
- target->write = write;
- target->destroy = destroy;
- target->next = next;
- target->status = 0;
+ struct opb *opb;
+ struct dt_node *dn = opb_dt->dn;
+ uint64_t addr64 = addr;
- list_head_init(&target->children);
+ dn = get_class_target_addr(dn, "opb", &addr64);
+ opb_dt = dn->target;
+ opb = target_to_opb(opb_dt);
+ return opb->read(opb, addr64, data);
+}
+
+int opb_write(struct target *opb_dt, uint32_t addr, uint32_t data)
+{
+ struct opb *opb;
+ struct dt_node *dn = opb_dt->dn;
+ uint64_t addr64 = addr;
+
+ dn = get_class_target_addr(dn, "opb", &addr64);
+ opb_dt = dn->target;
+ opb = target_to_opb(opb_dt);
+
+ return opb->write(opb, addr64, data);
+}
+
+int fsi_read(struct target *fsi_dt, uint32_t addr, uint32_t *data)
+{
+ struct fsi *fsi;
+ struct dt_node *dn = fsi_dt->dn;
+ uint64_t addr64 = addr;
+
+ dn = get_class_target_addr(dn, "fsi", &addr64);
+ fsi_dt = dn->target;
+ fsi = target_to_fsi(fsi_dt);
+ return fsi->read(fsi, addr64, data);
+}
+
+int fsi_write(struct target *fsi_dt, uint32_t addr, uint32_t data)
+{
+ struct fsi *fsi;
+ struct dt_node *dn = fsi_dt->dn;
+ uint64_t addr64 = addr;
+
+ dn = get_class_target_addr(dn, "fsi", &addr64);
+ fsi_dt = dn->target;
+ fsi = target_to_fsi(fsi_dt);
+
+ return fsi->write(fsi, addr64, data);
+}
- if (next) {
- target->chip_type = target->next->chip_type;
- list_add_tail(&next->children, &target->link);
+int adu_getmem(struct target *adu_target, uint64_t addr, uint8_t *output, uint64_t size)
+{
+ struct adu *adu;
+
+ assert(!strcmp(adu_target->class, "adu"));
+ adu = target_to_adu(adu_target);
+ return adu->getmem(adu, addr, output, size);
+}
+
+int adu_putmem(struct target *adu_target, uint64_t start_addr, uint8_t *input, uint64_t size)
+{
+ struct adu *adu;
+
+ assert(!strcmp(adu_target->class, "adu"));
+ adu = target_to_adu(adu_target);
+ return adu->putmem(adu, start_addr, input, size);
+}
+
+/* Finds the given class. Returns NULL if not found. */
+struct target_class *find_target_class(const char *name)
+{
+ struct target_class *target_class;
+
+ list_for_each(&target_classes, target_class, class_head_link)
+ if (!strcmp(target_class->name, name))
+ return target_class;
+
+ return NULL;
+}
+
+/* Same as above but dies with an assert if the target class doesn't
+ * exist */
+struct target_class *require_target_class(const char *name)
+{
+ struct target_class *target_class;
+
+ target_class = find_target_class(name);
+ if (!target_class) {
+ PR_ERROR("Couldn't find class %s\n", name);
+ assert(0);
}
+ return target_class;
}
-void target_del(struct target *target)
+/* Returns the existing class or allocates space for a new one */
+static struct target_class *get_target_class(const char *name)
{
- if (target->destroy)
- target->destroy(target);
+ struct target_class *target_class;
- /* We don't recursively destroy things yet */
- assert(list_empty(&target->children));
- if (target->next)
- list_del(&target->link);
+ if ((target_class = find_target_class(name)))
+ return target_class;
+
+ /* Need to allocate a new class */
+ PR_DEBUG("Allocating %s target class\n", name);
+ target_class = calloc(1, sizeof(*target_class));
+ assert(target_class);
+ target_class->name = strdup(name);
+ list_head_init(&target_class->targets);
+ list_add(&target_classes, &target_class->class_head_link);
+ return target_class;
}
-int read_target(struct target *target, uint64_t addr, uint64_t *value)
+extern struct hw_unit_info *__start_hw_units;
+extern struct hw_init_info *__stop_hw_units;
+struct hw_unit_info *find_compatible_target(const char *compat)
{
-// printf("Target %s read 0x%0llx\n", target->name, addr);
+ struct hw_unit_info **p;
+ struct target *target, *tmp;
+
+ for (p = &__start_hw_units; p < &__stop_hw_units; p++) {
+ target = (*p)->hw_unit + (*p)->struct_target_offset;
+ if (!strcmp(target->compatible, compat))
+ return *p;
+ }
- if (target->read)
- return target->read(target, addr, value);
- else
- /* If there is no read method fall through to the next one */
- return read_next_target(target, addr, value);
+ return NULL;
}
-int read_next_target(struct target *target, uint64_t addr, uint64_t *value)
+void targets_init(void *fdt)
{
- assert(target->next);
- return read_target(target->next, target->base + addr, value);
+ struct dt_node *dn;
+ const struct dt_property *p;
+ struct target_class *target_class;
+ struct hw_unit_info *hw_unit_info;
+ void *new_hw_unit;
+ struct target *new_target;
+
+ dt_root = dt_new_root("");
+ dt_expand(fdt);
+
+ /* Now we need to walk the device-tree, assign struct targets
+ * to each of the nodes and add them to the appropriate target
+ * classes */
+ dt_for_each_node(dt_root, dn) {
+ p = dt_require_property(dn, "compatible", -1);
+ hw_unit_info = find_compatible_target(p->prop);
+ if (hw_unit_info) {
+ /* We need to allocate a new target */
+ new_hw_unit = malloc(hw_unit_info->size);
+ assert(new_hw_unit);
+ memcpy(new_hw_unit, hw_unit_info->hw_unit, hw_unit_info->size);
+ new_target = new_hw_unit + hw_unit_info->struct_target_offset;
+ new_target->dn = dn;
+ dn->target = new_target;
+ target_class = get_target_class(new_target->class);
+ list_add(&target_class->targets, &new_target->class_link);
+ PR_DEBUG("Found target %s for %s\n", new_target->name, dn->name);
+ } else
+ PR_DEBUG("No target found for %s\n", dn->name);
+ }
}
-int write_target(struct target *target, uint64_t addr, uint64_t value)
+/* Disable a node and all it's children */
+static void disable_node(struct dt_node *dn)
{
-// printf("Target %s write 0x%0llx\n", target->name, addr);
+ struct dt_node *next;
+ struct dt_property *p;
- if (target->write)
- return target->write(target, addr, value);
- else
- return write_next_target(target, addr, value);
+ p = dt_find_property(dn, "status");
+ if (p)
+ dt_del_property(dn, p);
+
+ dt_add_property_string(dn, "status", "disabled");
+ dt_for_each_child(dn, next)
+ disable_node(next);
+}
+
+static void _target_probe(struct dt_node *dn)
+{
+ int rc;
+ struct dt_node *next;
+ struct dt_property *p;
+
+ PR_DEBUG("Probe %s - ", dn->name);
+ if (!dn->target) {
+ PR_DEBUG("target not found\n");
+ return;
+ }
+
+ p = dt_find_property(dn, "status");
+ if ((p && !strcmp(p->prop, "disabled")) || (dn->target->probe && (rc = dn->target->probe(dn->target)))) {
+ if (rc)
+ PR_DEBUG("not found\n");
+ else
+ PR_DEBUG("disabled\n");
+
+ disable_node(dn);
+ } else {
+ PR_DEBUG("success\n");
+ dt_for_each_child(dn, next)
+ _target_probe(next);
+ }
}
-int write_next_target(struct target *target, uint64_t addr, uint64_t value)
+/* We walk the tree root down disabling targets which might/should
+ * exist but don't */
+void target_probe(void)
{
- assert(target->next);
- return write_target(target->next, target->base + addr, value);
+ _target_probe(dt_first(dt_root));
}
diff --git a/libpdbg/target.h b/libpdbg/target.h
index 9e73e3b..9f83b40 100644
--- a/libpdbg/target.h
+++ b/libpdbg/target.h
@@ -18,43 +18,109 @@
#include <stdint.h>
#include <ccan/list/list.h>
+#include <ccan/container_of/container_of.h>
+#include "compiler.h"
+#include "device.h"
-struct target;
-typedef int (*target_read)(struct target *target, uint64_t addr, uint64_t *value);
-typedef int (*target_write)(struct target *target, uint64_t addr, uint64_t value);
-typedef void (*target_destroy)(struct target *target);
+#define PR_DEBUG(x, args...) \
+ fprintf(stderr, x, ##args)
+#define PR_INFO(x, args...) \
+ fprintf(stderr, x, ##args)
+#define PR_ERROR(x, args...) \
+ fprintf(stderr, "%s: " x, __FUNCTION__, ##args)
enum chip_type {CHIP_UNKNOWN, CHIP_P8, CHIP_P8NV, CHIP_P9};
+struct target_class {
+ char *name;
+ struct list_head targets;
+ struct list_node class_head_link;
+};
+
struct target {
- const char *name;
- int index;
- uint64_t base;
- target_read read;
- target_write write;
- target_destroy destroy;
- enum chip_type chip_type;
- struct target *next;
- struct list_node link;
- struct list_head children;
+ char *name;
+ char *compatible;
+ char *class;
+ int (*probe)(struct target *target);
+ struct dt_node *dn;
struct list_node class_link;
+};
+
+struct target_class *find_target_class(const char *name);
+struct target_class *require_target_class(const char *name);
+
+extern struct list_head empty_list;
+#define for_each_class_target(class_name, target) \
+ list_for_each((find_target_class(class_name) ? &require_target_class(class_name)->targets : &empty_list), target, class_link)
+
+struct hw_unit_info {
+ void *hw_unit;
+ size_t size;
+ size_t struct_target_offset;
+};
+
+/* We can't pack the structs themselves directly into a special
+ * section because there doesn't seem to be any standard way of doing
+ * that due to alignment rules. So instead we pack pointers into a
+ * special section. */
+#define DECLARE_HW_UNIT(name) \
+ const struct hw_unit_info __used name ##_hw_unit = \
+ { .hw_unit = &name, .size = sizeof(name), .struct_target_offset = container_off(typeof(name), target) }; \
+ const struct hw_unit_info __used __section("hw_units") *name ##_hw_unit_p = &name ##_hw_unit
+
+struct adu {
+ struct target target;
+ int (*getmem)(struct adu *, uint64_t, uint8_t *, uint64_t);
+ int (*putmem)(struct adu *, uint64_t, uint8_t *, uint64_t);
+ uint64_t (*_get_ctrl_reg)(uint64_t, uint64_t);
+ uint64_t (*_get_cmd_reg)(uint64_t, uint64_t);
+ uint64_t (*_wait_completion)(struct adu *);
+};
+#define target_to_adu(x) container_of(x, struct adu, target)
+
+struct pib {
+ struct target target;
+ int (*read)(struct pib *, uint64_t, uint64_t *);
+ int (*write)(struct pib *, uint64_t, uint64_t);
void *priv;
- uint64_t status;
};
+#define target_to_pib(x) container_of(x, struct pib, target)
-struct target_class {
- const char *name;
- struct list_head targets;
+struct opb {
+ struct target target;
+ int (*read)(struct opb *, uint32_t, uint32_t *);
+ int (*write)(struct opb *, uint32_t, uint32_t);
+};
+#define target_to_opb(x) container_of(x, struct opb, target)
+
+struct fsi {
+ struct target target;
+ int (*read)(struct fsi *, uint32_t, uint32_t *);
+ int (*write)(struct fsi *, uint32_t, uint32_t);
+ enum chip_type chip_type;
};
+#define target_to_fsi(x) container_of(x, struct fsi, target)
+
+struct chiplet {
+ struct target target;
+};
+#define target_to_chiplet(x) container_of(x, struct chiplet, target)
+
+struct thread {
+ struct target target;
+ uint64_t status;
+ int id;
+};
+#define target_to_thread(x) container_of(x, struct thread, target)
+
+void targets_init(void *fdt);
+void target_probe(void);
-int read_target(struct target *target, uint64_t addr, uint64_t *value);
-int read_next_target(struct target *target, uint64_t addr, uint64_t *value);
-int write_target(struct target *target, uint64_t addr, uint64_t value);
-int write_next_target(struct target *target, uint64_t addr, uint64_t value);
-void target_init(struct target *target, const char *name, uint64_t base,
- target_read read, target_write write,
- target_destroy destroy, struct target *next);
-void target_class_add(struct target_class *class, struct target *target, int index);
-void target_del(struct target *target);
+int pib_read(struct target *pib_dt, uint64_t addr, uint64_t *data);
+int pib_write(struct target *pib_dt, uint64_t addr, uint64_t data);
+int opb_read(struct target *opb_dt, uint32_t addr, uint32_t *data);
+int opb_write(struct target *opb_dt, uint32_t addr, uint32_t data);
+int fsi_read(struct target *fsi_dt, uint32_t addr, uint32_t *data);
+int fsi_write(struct target *fsi_dt, uint32_t addr, uint32_t data);
#endif
OpenPOWER on IntegriCloud