From 54ce73a1f5c7ddecc7579c136dbac9c2c201b621 Mon Sep 17 00:00:00 2001 From: Carl-Daniel Hailfinger Date: Tue, 3 May 2011 21:49:41 +0000 Subject: Revert MMIO space writes on shutdown as needed Reversible MMIO space writes now use rmmio_write*(). Reversible PCI MMIO space writes now use pci_rmmio_write*(). If a MMIO value needs to be queued for restore without writing it, use rmmio_val*(). MMIO space writes which are one-shot (e.g. communication with some chip) should continue to use the permanent mmio_write* variants. Corresponding to flashrom svn r1292. Signed-off-by: Carl-Daniel Hailfinger David tested it successfully on some NM10/ICH7 platforms which switch between SPI and LPC targets (x86 BIOS ROM vs. EC firmware ROM). Acked-by: David Hendricks --- hwaccess.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ichspi.c | 50 +++++++++++++++++++--------- nicintel_spi.c | 8 +++++ programmer.h | 12 +++++++ satamv.c | 6 ++-- 5 files changed, 157 insertions(+), 20 deletions(-) diff --git a/hwaccess.c b/hwaccess.c index bbb91a6..3c4f07a 100644 --- a/hwaccess.c +++ b/hwaccess.c @@ -184,3 +184,104 @@ uint32_t mmio_le_readl(void *addr) { return le_to_cpu32(mmio_readl(addr)); } + +enum mmio_write_type { + mmio_write_type_b, + mmio_write_type_w, + mmio_write_type_l, +}; + +struct undo_mmio_write_data { + void *addr; + int reg; + enum mmio_write_type type; + union { + uint8_t bdata; + uint16_t wdata; + uint32_t ldata; + }; +}; + +void undo_mmio_write(void *p) +{ + struct undo_mmio_write_data *data = p; + msg_pdbg("Restoring MMIO space at %p\n", data->addr); + switch (data->type) { + case mmio_write_type_b: + mmio_writeb(data->bdata, data->addr); + break; + case mmio_write_type_w: + mmio_writew(data->wdata, data->addr); + break; + case mmio_write_type_l: + mmio_writel(data->ldata, data->addr); + break; + } + /* p was allocated in register_undo_mmio_write. */ + free(p); +} + +#define register_undo_mmio_write(a, c) \ +{ \ + struct undo_mmio_write_data *undo_mmio_write_data; \ + undo_mmio_write_data = malloc(sizeof(struct undo_mmio_write_data)); \ + undo_mmio_write_data->addr = a; \ + undo_mmio_write_data->type = mmio_write_type_##c; \ + undo_mmio_write_data->c##data = mmio_read##c(a); \ + register_shutdown(undo_mmio_write, undo_mmio_write_data); \ +} + +#define register_undo_mmio_writeb(a) register_undo_mmio_write(a, b) +#define register_undo_mmio_writew(a) register_undo_mmio_write(a, w) +#define register_undo_mmio_writel(a) register_undo_mmio_write(a, l) + +void rmmio_writeb(uint8_t val, void *addr) +{ + register_undo_mmio_writeb(addr); + mmio_writeb(val, addr); +} + +void rmmio_writew(uint16_t val, void *addr) +{ + register_undo_mmio_writew(addr); + mmio_writew(val, addr); +} + +void rmmio_writel(uint32_t val, void *addr) +{ + register_undo_mmio_writel(addr); + mmio_writel(val, addr); +} + +void rmmio_le_writeb(uint8_t val, void *addr) +{ + register_undo_mmio_writeb(addr); + mmio_le_writeb(val, addr); +} + +void rmmio_le_writew(uint16_t val, void *addr) +{ + register_undo_mmio_writew(addr); + mmio_le_writew(val, addr); +} + +void rmmio_le_writel(uint32_t val, void *addr) +{ + register_undo_mmio_writel(addr); + mmio_le_writel(val, addr); +} + +void rmmio_valb(void *addr) +{ + register_undo_mmio_writeb(addr); +} + +void rmmio_valw(void *addr) +{ + register_undo_mmio_writew(addr); +} + +void rmmio_vall(void *addr) +{ + register_undo_mmio_writel(addr); +} diff --git a/ichspi.c b/ichspi.c index 78c5422..f2814d1 100644 --- a/ichspi.c +++ b/ichspi.c @@ -163,7 +163,7 @@ static uint16_t REGREAD8(int X) static int find_opcode(OPCODES *op, uint8_t opcode); static int find_preop(OPCODES *op, uint8_t preop); static int generate_opcodes(OPCODES * op); -static int program_opcodes(OPCODES * op); +static int program_opcodes(OPCODES *op, int enable_undo); static int run_opcode(OPCODE op, uint32_t offset, uint8_t datalength, uint8_t * data); @@ -269,7 +269,7 @@ static int reprogram_opcode_on_the_fly(uint8_t opcode, unsigned int writecnt, un int oppos=2; // use original JEDEC_BE_D8 offset curopcodes->opcode[oppos].opcode = opcode; curopcodes->opcode[oppos].spi_type = spi_type; - program_opcodes(curopcodes); + program_opcodes(curopcodes, 0); oppos = find_opcode(curopcodes, opcode); msg_pdbg ("on-the-fly OPCODE (0x%02X) re-programmed, op-pos=%d\n", opcode, oppos); return oppos; @@ -357,7 +357,7 @@ static int generate_opcodes(OPCODES * op) return 0; } -int program_opcodes(OPCODES * op) +static int program_opcodes(OPCODES *op, int enable_undo) { uint8_t a; uint16_t preop, optype; @@ -391,16 +391,30 @@ int program_opcodes(OPCODES * op) switch (spi_controller) { case SPI_CONTROLLER_ICH7: case SPI_CONTROLLER_VIA: - REGWRITE16(ICH7_REG_PREOP, preop); - REGWRITE16(ICH7_REG_OPTYPE, optype); - REGWRITE32(ICH7_REG_OPMENU, opmenu[0]); - REGWRITE32(ICH7_REG_OPMENU + 4, opmenu[1]); + /* Register undo only for enable_undo=1, i.e. first call. */ + if (enable_undo) { + rmmio_valw(ich_spibar + ICH7_REG_PREOP); + rmmio_valw(ich_spibar + ICH7_REG_OPTYPE); + rmmio_vall(ich_spibar + ICH7_REG_OPMENU); + rmmio_vall(ich_spibar + ICH7_REG_OPMENU + 4); + } + mmio_writew(preop, ich_spibar + ICH7_REG_PREOP); + mmio_writew(optype, ich_spibar + ICH7_REG_OPTYPE); + mmio_writel(opmenu[0], ich_spibar + ICH7_REG_OPMENU); + mmio_writel(opmenu[1], ich_spibar + ICH7_REG_OPMENU + 4); break; case SPI_CONTROLLER_ICH9: - REGWRITE16(ICH9_REG_PREOP, preop); - REGWRITE16(ICH9_REG_OPTYPE, optype); - REGWRITE32(ICH9_REG_OPMENU, opmenu[0]); - REGWRITE32(ICH9_REG_OPMENU + 4, opmenu[1]); + /* Register undo only for enable_undo=1, i.e. first call. */ + if (enable_undo) { + rmmio_valw(ich_spibar + ICH9_REG_PREOP); + rmmio_valw(ich_spibar + ICH9_REG_OPTYPE); + rmmio_vall(ich_spibar + ICH9_REG_OPMENU); + rmmio_vall(ich_spibar + ICH9_REG_OPMENU + 4); + } + mmio_writew(preop, ich_spibar + ICH9_REG_PREOP); + mmio_writew(optype, ich_spibar + ICH9_REG_OPTYPE); + mmio_writel(opmenu[0], ich_spibar + ICH9_REG_OPMENU); + mmio_writel(opmenu[1], ich_spibar + ICH9_REG_OPMENU + 4); break; default: msg_perr("%s: unsupported chipset\n", __func__); @@ -426,9 +440,11 @@ void ich_set_bbar(uint32_t minaddr) msg_pdbg("Reserved bits in BBAR not zero: 0x%04x", ichspi_bbar); ichspi_bbar |= minaddr; - mmio_writel(ichspi_bbar, ich_spibar + 0x50); + rmmio_writel(ichspi_bbar, ich_spibar + 0x50); ichspi_bbar = mmio_readl(ich_spibar + 0x50); - /* We don't have any option except complaining. */ + /* We don't have any option except complaining. And if the write + * failed, the restore will fail as well, so no problem there. + */ if (ichspi_bbar != minaddr) msg_perr("Setting BBAR failed!\n"); break; @@ -438,9 +454,11 @@ void ich_set_bbar(uint32_t minaddr) msg_pdbg("Reserved bits in BBAR not zero: 0x%04x", ichspi_bbar); ichspi_bbar |= minaddr; - mmio_writel(ichspi_bbar, ich_spibar + 0xA0); + rmmio_writel(ichspi_bbar, ich_spibar + 0xA0); ichspi_bbar = mmio_readl(ich_spibar + 0xA0); - /* We don't have any option except complaining. */ + /* We don't have any option except complaining. And if the write + * failed, the restore will fail as well, so no problem there. + */ if (ichspi_bbar != minaddr) msg_perr("Setting BBAR failed!\n"); break; @@ -470,7 +488,7 @@ static int ich_init_opcodes(void) } else { msg_pdbg("Programming OPCODES... "); curopcodes_done = &O_ST_M25P; - rc = program_opcodes(curopcodes_done); + rc = program_opcodes(curopcodes_done, 1); /* Technically not part of opcode init, but it allows opcodes * to run without transaction errors by setting the lowest * allowed address to zero. diff --git a/nicintel_spi.c b/nicintel_spi.c index 3882e81..28d332e 100644 --- a/nicintel_spi.c +++ b/nicintel_spi.c @@ -148,6 +148,11 @@ int nicintel_spi_init(void) nicintel_spibar = physmap("Intel Gigabit NIC w/ SPI flash", io_base_addr, 4096); + /* Automatic restore of EECD on shutdown is not possible because EECD + * does not only contain FLASH_WRITES_DISABLED|FLASH_WRITES_ENABLED, + * but other bits with side effects as well. Those other bits must be + * left untouched. + */ tmp = pci_mmio_readl(nicintel_spibar + EECD); tmp &= ~FLASH_WRITES_DISABLED; tmp |= FLASH_WRITES_ENABLED; @@ -167,6 +172,9 @@ int nicintel_spi_shutdown(void) { uint32_t tmp; + /* Disable writes manually. See the comment about EECD in + * nicintel_spi_init() for details. + */ tmp = pci_mmio_readl(nicintel_spibar + EECD); tmp &= ~FLASH_WRITES_ENABLED; tmp |= FLASH_WRITES_DISABLED; diff --git a/programmer.h b/programmer.h index 7698ef0..cd5d584 100644 --- a/programmer.h +++ b/programmer.h @@ -316,6 +316,18 @@ uint32_t mmio_le_readl(void *addr); #define pci_mmio_readb mmio_le_readb #define pci_mmio_readw mmio_le_readw #define pci_mmio_readl mmio_le_readl +void rmmio_writeb(uint8_t val, void *addr); +void rmmio_writew(uint16_t val, void *addr); +void rmmio_writel(uint32_t val, void *addr); +void rmmio_le_writeb(uint8_t val, void *addr); +void rmmio_le_writew(uint16_t val, void *addr); +void rmmio_le_writel(uint32_t val, void *addr); +#define pci_rmmio_writeb rmmio_le_writeb +#define pci_rmmio_writew rmmio_le_writew +#define pci_rmmio_writel rmmio_le_writel +void rmmio_valb(void *addr); +void rmmio_valw(void *addr); +void rmmio_vall(void *addr); /* programmer.c */ int noop_shutdown(void); diff --git a/satamv.c b/satamv.c index 2e5af6c..0c0dace 100644 --- a/satamv.c +++ b/satamv.c @@ -102,8 +102,7 @@ int satamv_init(void) msg_pspew("BAR2Sz=0x%01x\n", (tmp >> 19) & 0x7); tmp &= 0xffffffc0; tmp |= 0x0000001f; - /* FIXME: This needs to be an auto-reversible write. */ - pci_mmio_writel(tmp, mv_bar + PCI_BAR2_CONTROL); + pci_rmmio_writel(tmp, mv_bar + PCI_BAR2_CONTROL); /* Enable flash: GPIO Port Control Register 0x104f0 */ tmp = pci_mmio_readl(mv_bar + GPIO_PORT_CONTROL); @@ -114,8 +113,7 @@ int satamv_init(void) "values!\n"); tmp &= 0xfffffffc; tmp |= 0x2; - /* FIXME: This needs to be an auto-reversible write. */ - pci_mmio_writel(tmp, mv_bar + GPIO_PORT_CONTROL); + pci_rmmio_writel(tmp, mv_bar + GPIO_PORT_CONTROL); /* Get I/O BAR location. */ tmp = pci_read_long(pcidev_dev, PCI_BASE_ADDRESS_2) & -- cgit v1.1