diff options
author | Alistair Popple <alistair@popple.id.au> | 2017-01-12 19:18:11 +1100 |
---|---|---|
committer | Alistair Popple <alistair@popple.id.au> | 2017-03-30 15:37:41 +1100 |
commit | 3c8aee3e1d38a470451929c9b3499b85928cb241 (patch) | |
tree | ef7a1fc1a5b6eaee3dc8d63e85bc02faf4d44294 /libpdbg/adu.c | |
parent | f045f14437ef63e10f57580978dc9ca3e0256007 (diff) | |
download | pdbg-3c8aee3e1d38a470451929c9b3499b85928cb241.tar.gz pdbg-3c8aee3e1d38a470451929c9b3499b85928cb241.zip |
Add support for power9 putmem/getmem
Signed-off-by: Alistair Popple <alistair@popple.id.au>
Diffstat (limited to 'libpdbg/adu.c')
-rw-r--r-- | libpdbg/adu.c | 406 |
1 files changed, 251 insertions, 155 deletions
diff --git a/libpdbg/adu.c b/libpdbg/adu.c index 4f2e9cb..f2f6649 100644 --- a/libpdbg/adu.c +++ b/libpdbg/adu.c @@ -20,15 +20,19 @@ #include "operations.h" #include "bitutils.h" -enum adu_retcode {ADU_DONE, ADU_RETRY, ADU_ERR}; - -/* ADU SCOM Register Definitions */ -#define ALTD_CONTROL_REG 0x0 -#define ALTD_CMD_REG 0x1 +/* P8 ADU SCOM Register Definitions */ +#define P8_ALTD_CONTROL_REG 0x0 +#define P8_ALTD_CMD_REG 0x1 #define P8_ALTD_STATUS_REG 0x2 #define P8_ALTD_DATA_REG 0x3 -/* ALTD_CMD_REG fields */ +/* P9 ADU SCOM Register Definitions */ +#define P9_ALTD_CONTROL_REG 0x0 +#define P9_ALTD_CMD_REG 0x1 +#define P9_ALTD_STATUS_REG 0x3 +#define P9_ALTD_DATA_REG 0x4 + +/* Common ALTD_CMD_REG fields */ #define FBC_ALTD_START_OP PPC_BIT(2) #define FBC_ALTD_CLEAR_STATUS PPC_BIT(3) #define FBC_ALTD_RESET_AD_PCB PPC_BIT(4) @@ -37,6 +41,15 @@ enum adu_retcode {ADU_DONE, ADU_RETRY, ADU_ERR}; #define FBC_ALTD_DROP_PRIORITY PPC_BITMASK(20, 21) #define FBC_LOCKED PPC_BIT(11) +/* P9_ALTD_CMD_REG fields */ +#define P9_TTYPE_TREAD PPC_BIT(5) +#define P9_TTYPE_TWRITE 0 +#define P9_FBC_ALTD_TTYPE PPC_BITMASK(25, 31) +#define P9_TTYPE_DMA_PARTIAL_READ 0b000110 +#define P9_TTYPE_DMA_PARTIAL_WRITE 0b100110 +#define P9_FBC_ALTD_TSIZE PPC_BITMASK(32, 39) +#define P9_FBC_ALTD_ADDRESS PPC_BITMASK(8, 63) + #define DROP_PRIORITY_LOW 0ULL #define DROP_PRIORITY_MEDIUM 1ULL #define DROP_PRIORITY_HIGH 2ULL @@ -46,36 +59,98 @@ enum adu_retcode {ADU_DONE, ADU_RETRY, ADU_ERR}; #define SCOPE_SYSTEM 2 #define SCOPE_REMOTE 3 -/* ALTD_CONTROL_REG fields */ -#define FBC_ALTD_TTYPE PPC_BITMASK(0, 5) -#define TTYPE_TREAD PPC_BIT(6) -#define TTYPE_TWRITE 0 -#define FBC_ALTD_TSIZE PPC_BITMASK(7, 13) -#define FBC_ALTD_ADDRESS PPC_BITMASK(14, 63) - -#define TTYPE_CI_PARTIAL_WRITE 0b110111 -#define TTYPE_CI_PARTIAL_OOO_WRITE 0b110110 -#define TTYPE_DMA_PARTIAL_WRITE 0b100110 -#define TTYPE_CI_PARTIAL_READ 0b110100 -#define TTYPE_DMA_PARTIAL_READ 0b110101 -#define TTYPE_PBOPERATION 0b111111 - -/* P8_ALTD_STATUS_REG fields */ +/* P8_ALTD_CONTROL_REG fields */ +#define P8_FBC_ALTD_TTYPE PPC_BITMASK(0, 5) +#define P8_TTYPE_TREAD PPC_BIT(6) +#define P8_TTYPE_TWRITE 0 +#define P8_FBC_ALTD_TSIZE PPC_BITMASK(7, 13) +#define P8_FBC_ALTD_ADDRESS PPC_BITMASK(14, 63) + +#define P8_TTYPE_CI_PARTIAL_WRITE 0b110111 +#define P8_TTYPE_CI_PARTIAL_OOO_WRITE 0b110110 +#define P8_TTYPE_DMA_PARTIAL_WRITE 0b100110 +#define P8_TTYPE_CI_PARTIAL_READ 0b110100 +#define P8_TTYPE_DMA_PARTIAL_READ 0b110101 +#define P8_TTYPE_PBOPERATION 0b111111 + +/* P8/P9_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) +int adu_getmem(struct target *adu_target, uint64_t start_addr, uint8_t *output, uint64_t size) +{ + struct adu *adu; + int rc = 0; + uint64_t addr; + + assert(!strcmp(adu_target->class, "adu")); + adu = target_to_adu(adu_target); + + /* We read data in 8-byte aligned chunks */ + for (addr = 8*(start_addr / 8); addr < start_addr + size; addr += 8) { + uint64_t data; + + if (adu->getmem(adu, addr, &data)) + return -1; + + /* ADU returns data in big-endian form in the register */ + data = __builtin_bswap64(data); + + if (addr < start_addr) { + memcpy(output, ((uint8_t *) &data) + (start_addr - addr), 8 - (start_addr - addr)); + output += 8 - (start_addr - addr); + } else if (addr + 8 > start_addr + size) { + memcpy(output, &data, start_addr + size - addr); + } else { + memcpy(output, &data, 8); + output += 8; + } + } + + return rc; +} + +int adu_putmem(struct target *adu_target, uint64_t start_addr, uint8_t *input, uint64_t size) +{ + struct adu *adu; + int rc = 0, tsize; + uint64_t addr, data, end_addr; + + assert(!strcmp(adu_target->class, "adu")); + adu = target_to_adu(adu_target); + end_addr = start_addr + size; + for (addr = start_addr; addr < end_addr; addr += tsize, input += tsize) { + if ((addr % 8) || (addr + 8 > end_addr)) { + /* If the address is not 64-bit aligned we + * copy in a byte at a time until it is. */ + tsize = 1; + + /* Copy the input data in with correct alignment */ + data = ((uint64_t) *input) << 8*(8 - (addr % 8) - 1); + } else { + tsize = 8; + memcpy(&data, input, sizeof(data)); + data = __builtin_bswap64(data); + } + + adu->putmem(adu, addr, data, tsize); + } + + return rc; +} + static int adu_lock(struct adu *adu) { uint64_t val; - CHECK_ERR(pib_read(&adu->target, ALTD_CMD_REG, &val)); + CHECK_ERR(pib_read(&adu->target, P8_ALTD_CMD_REG, &val)); if (val & FBC_LOCKED) PR_INFO("ADU already locked! Ignoring.\n"); val |= FBC_LOCKED; - CHECK_ERR(pib_write(&adu->target, ALTD_CMD_REG, val)); + CHECK_ERR(pib_write(&adu->target, P8_ALTD_CMD_REG, val)); return 0; } @@ -84,7 +159,7 @@ static int adu_unlock(struct adu *adu) { uint64_t val; - CHECK_ERR(pib_read(&adu->target, ALTD_CMD_REG, &val)); + CHECK_ERR(pib_read(&adu->target, P8_ALTD_CMD_REG, &val)); if (!(val & FBC_LOCKED)) { PR_INFO("ADU already unlocked!\n"); @@ -92,7 +167,7 @@ static int adu_unlock(struct adu *adu) } val &= ~FBC_LOCKED; - CHECK_ERR(pib_write(&adu->target, ALTD_CMD_REG, val)); + CHECK_ERR(pib_write(&adu->target, P8_ALTD_CMD_REG, val)); return 0; } @@ -101,33 +176,38 @@ static int adu_reset(struct adu *adu) { uint64_t val; - CHECK_ERR(pib_read(&adu->target, ALTD_CMD_REG, &val)); + CHECK_ERR(pib_read(&adu->target, P8_ALTD_CMD_REG, &val)); val |= FBC_ALTD_CLEAR_STATUS | FBC_ALTD_RESET_AD_PCB; - CHECK_ERR(pib_write(&adu->target, ALTD_CMD_REG, val)); + CHECK_ERR(pib_write(&adu->target, P8_ALTD_CMD_REG, val)); return 0; } -static uint64_t p8_get_ctrl_reg(uint64_t ctrl_reg, uint64_t addr) +static int p8_adu_getmem(struct adu *adu, uint64_t addr, uint64_t *data) { - 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; -} + uint64_t ctrl_reg, cmd_reg, val; -static uint64_t p8_get_cmd_reg(uint64_t cmd_reg, uint64_t addr) -{ + CHECK_ERR(adu_lock(adu)); + + ctrl_reg = P8_TTYPE_TREAD; + ctrl_reg = SETFIELD(P8_FBC_ALTD_TTYPE, ctrl_reg, P8_TTYPE_DMA_PARTIAL_READ); + ctrl_reg = SETFIELD(P8_FBC_ALTD_TSIZE, ctrl_reg, 8); + + CHECK_ERR(pib_read(&adu->target, P8_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); - return cmd_reg; -} -static enum adu_retcode p8_wait_completion(struct adu *adu) -{ - uint64_t val; +retry: + /* Clear status bits */ + CHECK_ERR(adu_reset(adu)); + + /* Set the address */ + ctrl_reg = SETFIELD(P8_FBC_ALTD_ADDRESS, ctrl_reg, addr); + CHECK_ERR(pib_write(&adu->target, P8_ALTD_CONTROL_REG, ctrl_reg)); + + /* Start the command */ + CHECK_ERR(pib_write(&adu->target, P8_ALTD_CMD_REG, cmd_reg)); /* Wait for completion */ do { @@ -138,73 +218,67 @@ static enum adu_retcode p8_wait_completion(struct adu *adu) !(val & FBC_ALTD_DATA_DONE)) { /* PBINIT_MISSING is expected occasionally so just retry */ if (val & FBC_ALTD_PBINIT_MISSING) - return ADU_RETRY; + goto retry; else { - PR_ERROR("ALTD_STATUS_REG = 0x%016llx\n", val); - return ADU_ERR; + PR_ERROR("Unable to read memory. " \ + "ALTD_STATUS_REG = 0x%016llx\n", val); + return -1; } } - return ADU_DONE; -} + /* Read data */ + CHECK_ERR(pib_read(&adu->target, P8_ALTD_DATA_REG, data)); -static int p8_adu_getmem(struct adu *adu, uint64_t addr) -{ + adu_unlock(adu); + + return 0; } -/* 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 p8_adu_putmem(struct adu *adu, uint64_t addr, uint64_t data, int size) { int rc = 0; - uint64_t addr, ctrl_reg, cmd_reg, val; + uint64_t cmd_reg, ctrl_reg, val; - /* We read data in 8-byte aligned chunks */ - for (addr = 8*(start_addr / 8); addr < start_addr + size; addr += 8) { - uint64_t data; + CHECK_ERR(adu_lock(adu)); - retry: - /* Clear status bits */ - CHECK_ERR(adu_reset(adu)); + ctrl_reg = P8_TTYPE_TWRITE; + ctrl_reg = SETFIELD(P8_FBC_ALTD_TTYPE, ctrl_reg, P8_TTYPE_DMA_PARTIAL_WRITE); + ctrl_reg = SETFIELD(P8_FBC_ALTD_TSIZE, ctrl_reg, size); - /* Set the address */ - ctrl_reg = adu->_get_ctrl_reg(ctrl_reg, addr); - cmd_reg = adu->_get_cmd_reg(cmd_reg, addr); + CHECK_ERR(pib_read(&adu->target, P8_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); - CHECK_ERR(pib_write(&adu->target, ALTD_CONTROL_REG, ctrl_reg)); + /* Clear status bits */ + CHECK_ERR(adu_reset(adu)); - /* Start the command */ - CHECK_ERR(pib_write(&adu->target, ALTD_CMD_REG, cmd_reg)); + /* Set the address */ + ctrl_reg = SETFIELD(P8_FBC_ALTD_ADDRESS, ctrl_reg, addr); - /* Wait for completion */ - 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; - } +retry: + CHECK_ERR(pib_write(&adu->target, P8_ALTD_CONTROL_REG, ctrl_reg)); - /* Read data */ - CHECK_ERR(pib_read(&adu->target, P8_ALTD_DATA_REG, &data)); + /* Write the data */ + CHECK_ERR(pib_write(&adu->target, P8_ALTD_DATA_REG, data)); - /* ADU returns data in big-endian form in the register */ - data = __builtin_bswap64(data); + /* Start the command */ + CHECK_ERR(pib_write(&adu->target, P8_ALTD_CMD_REG, cmd_reg)); - if (addr < start_addr) { - memcpy(output, ((uint8_t *) &data) + (start_addr - addr), 8 - (start_addr - addr)); - output += 8 - (start_addr - addr); - } else if (addr + 8 > start_addr + size) { - memcpy(output, &data, start_addr + size - addr); - } else { - memcpy(output, &data, 8); - output += 8; + /* 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) + goto retry; + else { + PR_ERROR("Unable to write memory. " \ + "P8_ALTD_STATUS_REG = 0x%016llx\n", val); + rc = -1; } } @@ -213,73 +287,100 @@ static int _adu_getmem(struct adu *adu, uint64_t start_addr, uint8_t *output, ui return rc; } -int p8_adu_putmem(struct adu *adu, uint64_t start_addr, uint8_t *input, uint64_t size) +static int p9_adu_getmem(struct adu *adu, uint64_t addr, uint64_t *data) { - int rc = 0, tsize; - uint64_t addr, cmd_reg, ctrl_reg, val, data, end_addr; + uint64_t ctrl_reg, cmd_reg, val; - CHECK_ERR(adu_lock(adu)); + cmd_reg = P9_TTYPE_TREAD; + cmd_reg = SETFIELD(P9_FBC_ALTD_TTYPE, cmd_reg, P9_TTYPE_DMA_PARTIAL_READ); - ctrl_reg = TTYPE_TWRITE; - ctrl_reg = SETFIELD(FBC_ALTD_TTYPE, ctrl_reg, TTYPE_DMA_PARTIAL_WRITE); + /* For a read size is apparently always 0 */ + cmd_reg = SETFIELD(P9_FBC_ALTD_TSIZE, cmd_reg, 0); + cmd_reg |= FBC_ALTD_START_OP; + cmd_reg = SETFIELD(FBC_ALTD_SCOPE, cmd_reg, SCOPE_REMOTE); + cmd_reg = SETFIELD(FBC_ALTD_DROP_PRIORITY, cmd_reg, DROP_PRIORITY_LOW); - 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); +retry: + /* Clear status bits */ + CHECK_ERR(adu_reset(adu)); - end_addr = start_addr + size; - for (addr = start_addr; addr < end_addr; addr += tsize, input += tsize) { - if ((addr % 8) || (addr + 8 > end_addr)) { - /* If the address is not 64-bit aligned we - * copy in a byte at a time until it is. */ - tsize = 1; + /* Set the address */ + ctrl_reg = SETFIELD(P9_FBC_ALTD_ADDRESS, 0, addr); + CHECK_ERR(pib_write(&adu->target, P9_ALTD_CONTROL_REG, ctrl_reg)); - /* Copy the input data in with correct alignment */ - data = ((uint64_t) *input) << 8*(8 - (addr % 8) - 1); - } else { - tsize = 8; - memcpy(&data, input, sizeof(data)); - data = __builtin_bswap64(data); - } - ctrl_reg = SETFIELD(FBC_ALTD_TSIZE, ctrl_reg, tsize); - - retry: - /* Clear status bits */ - CHECK_ERR(adu_reset(adu)); - - /* Set the address */ - ctrl_reg = SETFIELD(FBC_ALTD_ADDRESS, ctrl_reg, addr); - CHECK_ERR(pib_write(&adu->target, ALTD_CONTROL_REG, ctrl_reg)); - - /* Write the data */ - CHECK_ERR(pib_write(&adu->target, P8_ALTD_DATA_REG, data)); - - /* Start the command */ - CHECK_ERR(pib_write(&adu->target, ALTD_CMD_REG, cmd_reg)); - - /* 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) - goto retry; - else { - PR_ERROR("Unable to write memory. " \ - "P8_ALTD_STATUS_REG = 0x%016llx\n", val); - rc = -1; - break; - } + /* Start the command */ + CHECK_ERR(pib_write(&adu->target, P9_ALTD_CMD_REG, cmd_reg)); + + /* Wait for completion */ + do { + CHECK_ERR(pib_read(&adu->target, P9_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); + return -1; } } - adu_unlock(adu); + /* Read data */ + CHECK_ERR(pib_read(&adu->target, P9_ALTD_DATA_REG, data)); - return rc; + return 0; +} + +static int p9_adu_putmem(struct adu *adu, uint64_t addr, uint64_t data, int size) +{ + uint64_t ctrl_reg, cmd_reg, val; + + /* Format to tsize. This is the "secondary encode" and is + shifted left on for writes. */ + size <<= 1; + + cmd_reg = P9_TTYPE_TWRITE; + cmd_reg = SETFIELD(P9_FBC_ALTD_TTYPE, cmd_reg, P9_TTYPE_DMA_PARTIAL_WRITE); + cmd_reg = SETFIELD(P9_FBC_ALTD_TSIZE, cmd_reg, size); + cmd_reg |= FBC_ALTD_START_OP; + cmd_reg = SETFIELD(FBC_ALTD_SCOPE, cmd_reg, SCOPE_REMOTE); + cmd_reg = SETFIELD(FBC_ALTD_DROP_PRIORITY, cmd_reg, DROP_PRIORITY_LOW); + + /* Clear status bits */ + CHECK_ERR(adu_reset(adu)); + + /* Set the address */ + ctrl_reg = SETFIELD(P9_FBC_ALTD_ADDRESS, 0, addr); +retry: + CHECK_ERR(pib_write(&adu->target, P9_ALTD_CONTROL_REG, ctrl_reg)); + + /* Write the data */ + CHECK_ERR(pib_write(&adu->target, P9_ALTD_DATA_REG, data)); + + /* Start the command */ + CHECK_ERR(pib_write(&adu->target, P9_ALTD_CMD_REG, cmd_reg)); + + /* Wait for completion */ + do { + CHECK_ERR(pib_read(&adu->target, P9_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); + return -1; + } + } + + return 0; } struct adu p8_adu = { @@ -288,23 +389,18 @@ struct adu p8_adu = { .compatible = "ibm,power8-adu", .class = "adu", }, - .getmem = _adu_getmem, + .getmem = p8_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, + .getmem = p9_adu_getmem, + .putmem = p9_adu_putmem, }; DECLARE_HW_UNIT(p9_adu); |