diff options
author | Alistair Popple <alistair@popple.id.au> | 2016-12-19 21:07:51 +1100 |
---|---|---|
committer | Alistair Popple <alistair@popple.id.au> | 2017-03-30 15:37:41 +1100 |
commit | f045f14437ef63e10f57580978dc9ca3e0256007 (patch) | |
tree | 86c0bdfe2fd6a54d5517ee5313e89765cd801a08 /libpdbg | |
parent | 72bf33c09f06ae63cd9f8d05e412b64622b340d1 (diff) | |
download | pdbg-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.c | 179 | ||||
-rw-r--r-- | libpdbg/bmcfsi.c | 130 | ||||
-rw-r--r-- | libpdbg/cfam.c | 191 | ||||
-rw-r--r-- | libpdbg/chip.c | 242 | ||||
-rw-r--r-- | libpdbg/compiler.h | 53 | ||||
-rw-r--r-- | libpdbg/device.c | 943 | ||||
-rw-r--r-- | libpdbg/device.h | 251 | ||||
-rw-r--r-- | libpdbg/fakepib.c | 38 | ||||
-rw-r--r-- | libpdbg/i2c.c | 41 | ||||
-rw-r--r-- | libpdbg/kernel.c | 87 | ||||
-rw-r--r-- | libpdbg/operations.h | 49 | ||||
-rw-r--r-- | libpdbg/target.c | 278 | ||||
-rw-r--r-- | libpdbg/target.h | 120 |
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 |