diff options
author | Gabe Black <gabeblack@chromium.org> | 2011-09-01 14:42:00 -0700 |
---|---|---|
committer | Gabe Black (Do Not Use) <gabeblack@google.com> | 2011-09-02 17:21:09 -0700 |
commit | 173acafe06125f4d3af1970e2f758888244ca09f (patch) | |
tree | a0ac13338ecc29a93227f2286169c786d3574e37 /drivers/spi | |
parent | 65a41aca3048ad26abcf28b7793bb52b6aaa12b0 (diff) |
Modify the ICH driver to support ICH9 in software sequencing mode
This change modifies the existing ICH SPI controller driver so that it
continues to support the ICH7 controller, but now also the ICH9 controller.
This implementation uses software sequencing mode for two reasons. First, it's
very similar to the only mode of the ICH7 and makes supporting both controllers
much easier. Second, it's much more flexible and actually fits into the u-boot
driver API.
BUG=chrome-os-partner:5829
TEST=With this and other changes, built and vbooted on x86-alex and stumpy.
Signed-off-by: Gabe Black <gabeblack@google.com>
Change-Id: Ifafd5febab8c905760c7e58eca82ee8a383efe36
Reviewed-on: http://gerrit.chromium.org/gerrit/7163
Reviewed-by: Mike Frysinger <vapier@chromium.org>
Tested-by: Gabe Black (Do Not Use) <gabeblack@google.com>
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/ich.c | 260 |
1 files changed, 203 insertions, 57 deletions
diff --git a/drivers/spi/ich.c b/drivers/spi/ich.c index 580b24b2942..0edc5da7267 100644 --- a/drivers/spi/ich.c +++ b/drivers/spi/ich.c @@ -44,25 +44,99 @@ typedef struct ich7_spi_regs { uint32_t bbar; uint16_t preop; uint16_t optype; - uint64_t opmenu; + uint8_t opmenu[8]; } __attribute__((packed)) ich7_spi_regs; -static ich7_spi_regs *ich7_spi = NULL; +typedef struct ich9_spi_regs { + uint32_t bfpr; + uint16_t hsfs; + uint16_t hsfc; + uint32_t faddr; + uint32_t _reserved0; + uint32_t fdata[16]; + uint32_t frap; + uint32_t freg[5]; + uint32_t _reserved1[3]; + uint32_t pr[5]; + uint32_t _reserved2[2]; + uint8_t ssfs; + uint8_t ssfc[3]; + uint16_t preop; + uint16_t optype; + uint8_t opmenu[8]; + uint32_t bbar; + uint8_t _reserved3[12]; + uint32_t fdoc; + uint32_t fdod; + uint8_t _reserved4[8]; + uint32_t afc; + uint32_t lvscc; + uint32_t uvscc; + uint8_t _reserved5[4]; + uint32_t fpb; + uint8_t _reserved6[28]; + uint32_t srdl; + uint32_t srdc; + uint32_t srd; +} __attribute__((packed)) ich9_spi_regs; + +typedef struct ich_spi_controller { + int locked; + + uint8_t *opmenu; + int menubytes; + uint16_t *optype; + uint32_t *addr; + uint8_t *data; + unsigned databytes; + uint8_t *status; + uint16_t *control; + uint32_t *bbar; +} ich_spi_controller; + +static ich_spi_controller cntlr; enum { SPIS_SCIP = 0x0001, SPIS_GRANT = 0x0002, SPIS_CDS = 0x0004, SPIS_FCERR = 0x0008, + SSFS_AEL = 0x0010, SPIS_LOCK = 0x8000, - SPIS_RESERVED_MASK = 0x7ff0 + SPIS_RESERVED_MASK = 0x7ff0, + SSFS_RESERVED_MASK = 0x7fe2 +}; + +enum { + SPIC_SCGO = 0x000002, + SPIC_ACS = 0x000004, + SPIC_SPOP = 0x000008, + SPIC_DBC = 0x003f00, + SPIC_DS = 0x004000, + SPIC_SME = 0x008000, + SSFC_SCF_MASK = 0x070000, + SSFC_RESERVED = 0xf80000 }; enum { - SPIC_SCGO = 0x0002, - SPIC_ACS = 0x0004, - SPIC_SPOP = 0x0008, - SPIC_DS = 0x4000 + HSFS_FDONE = 0x0001, + HSFS_FCERR = 0x0002, + HSFS_AEL = 0x0004, + HSFS_BERASE_MASK = 0x0018, + HSFS_BERASE_SHIFT = 3, + HSFS_SCIP = 0x0020, + HSFS_FDOPSS = 0x2000, + HSFS_FDV = 0x4000, + HSFS_FLOCKDN = 0x8000 +}; + +enum { + HSFC_FGO = 0x0001, + HSFC_FCYCLE_MASK = 0x0006, + HSFC_FCYCLE_SHIFT = 1, + HSFC_FDBC_MASK = 0x3f00, + HSFC_FDBC_SHIFT = 8, + HSFC_FSMIE = 0x8000 }; enum { @@ -108,9 +182,9 @@ static void ich_set_bbar(uint32_t minaddr) uint32_t ichspi_bbar; minaddr &= bbar_mask; - ichspi_bbar = readl(&ich7_spi->bbar) & ~bbar_mask; + ichspi_bbar = readl(cntlr.bbar) & ~bbar_mask; ichspi_bbar |= minaddr; - writel(ichspi_bbar, &ich7_spi->bbar); + writel(ichspi_bbar, cntlr.bbar); } int spi_cs_is_valid(unsigned int bus, unsigned int cs) @@ -130,9 +204,10 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, } memset(slave, 0, sizeof(*slave)); + slave->bus = bus; slave->cs = cs; - return (struct spi_slave *)slave; + return slave; } void spi_free_slave(struct spi_slave *_slave) @@ -141,26 +216,98 @@ void spi_free_slave(struct spi_slave *_slave) free(slave); } +static inline int spi_is_cougarpoint_lpc(uint16_t device_id) +{ + return device_id >= PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MIN && + device_id <= PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MAX; +}; + void spi_init(void) { - const uint16_t spibar_offset = 0x3020; + int ich_version = 0; + uint8_t *rcrb; /* Root Complex Register Block */ uint32_t rcba; /* Root Complex Base Address */ uint8_t bios_cntl; + pci_dev_t dev; - pci_dev_t dev = pci_find_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_TGP_LPC, 0); - /* The docs say it should be at device 31, function 0. */ - if (PCI_DEV(dev) != 31 || PCI_FUNC(dev) != 0) { - puts("ICH SPI: LPC bridge not where it's supposed to be\n"); + int bus; + int last_bus = pci_last_busno(); + + if (last_bus == -1) { + puts("No PCI busses?\n"); + return; + } + + for (bus = 0; bus <= last_bus; bus++) { + uint32_t ids; + uint16_t vendor_id, device_id; + + dev = PCI_BDF(bus, 31, 0); + pci_read_config_dword(dev, 0, &ids); + vendor_id = ids; + device_id = (ids >> 16); + + if (vendor_id != PCI_VENDOR_ID_INTEL) + continue; + + if (device_id == PCI_DEVICE_ID_INTEL_TGP_LPC) { + ich_version = 7; + break; + } else if (spi_is_cougarpoint_lpc(device_id)) { + ich_version = 9; + break; + } + } + + if (!ich_version) { + puts("ICH SPI: No ICH found.\n"); return; } + pci_read_config_dword(dev, 0xf0, &rcba); /* Bits 31-14 are the base address, 13-1 are reserved, 0 is enable. */ rcrb = (uint8_t *)(rcba & 0xffffc000); - ich7_spi = (ich7_spi_regs *)(rcrb + spibar_offset); + switch (ich_version) { + case 7: + { + const uint16_t ich7_spibar_offset = 0x3020; + ich7_spi_regs *ich7_spi = + (ich7_spi_regs *)(rcrb + ich7_spibar_offset); + + ichspi_lock = readw(&ich7_spi->spis) & SPIS_LOCK; + cntlr.opmenu = ich7_spi->opmenu; + cntlr.menubytes = sizeof(ich7_spi->opmenu); + cntlr.optype = &ich7_spi->optype; + cntlr.addr = &ich7_spi->spia; + cntlr.data = (uint8_t *)ich7_spi->spid; + cntlr.databytes = sizeof(ich7_spi->spid); + cntlr.status = (uint8_t *)&ich7_spi->spis; + cntlr.control = &ich7_spi->spic; + cntlr.bbar = &ich7_spi->bbar; + break; + } + case 9: + { + const uint16_t ich9_spibar_offset = 0x3800; + ich9_spi_regs *ich9_spi = + (ich9_spi_regs *)(rcrb + ich9_spibar_offset); + ichspi_lock = readw(&ich9_spi->hsfs) & HSFS_FLOCKDN; + cntlr.opmenu = ich9_spi->opmenu; + cntlr.menubytes = sizeof(ich9_spi->opmenu); + cntlr.optype = &ich9_spi->optype; + cntlr.addr = &ich9_spi->faddr; + cntlr.data = (uint8_t *)ich9_spi->fdata; + cntlr.databytes = sizeof(ich9_spi->fdata); + cntlr.status = &ich9_spi->ssfs; + cntlr.control = (uint16_t *)ich9_spi->ssfc; + cntlr.bbar = &ich9_spi->bbar; + break; + } + default: + printf("ICH SPI: Unrecognized ICH version %d.\n", ich_version); + } - ichspi_lock = readw(&ich7_spi->spis) & SPIS_LOCK; ich_set_bbar(0); /* Disable the BIOS write protect so write commands are allowed. */ @@ -232,36 +379,36 @@ static void spi_setup_type(spi_transaction *trans) static int spi_setup_opcode(spi_transaction *trans) { uint16_t optypes; - uint8_t opmenu[8]; + uint8_t opmenu[cntlr.menubytes]; trans->opcode = trans->out[0]; spi_use_out(trans, 1); if (!ichspi_lock) { /* The lock is off, so just use index 0. */ - writeb(trans->opcode, &ich7_spi->opmenu); - optypes = readw(&ich7_spi->optype); + writeb(trans->opcode, cntlr.opmenu); + optypes = readw(cntlr.optype); optypes = (optypes & 0xfffc) | (trans->type & 0x3); - writew(optypes, &ich7_spi->optype); + writew(optypes, cntlr.optype); return 0; } else { /* The lock is on. See if what we need is on the menu. */ uint8_t optype; uint16_t opcode_index; - read_reg(&ich7_spi->opmenu, &opmenu, sizeof(opmenu)); - for (opcode_index = 0; opcode_index < ARRAY_SIZE(opmenu); + read_reg(cntlr.opmenu, opmenu, sizeof(opmenu)); + for (opcode_index = 0; opcode_index < cntlr.menubytes; opcode_index++) { if (opmenu[opcode_index] == trans->opcode) break; } - if (opcode_index == ARRAY_SIZE(opmenu)) { + if (opcode_index == cntlr.menubytes) { printf("ICH SPI: Opcode %x not found\n", trans->opcode); return -1; } - optypes = readw(&ich7_spi->optype); + optypes = readw(cntlr.optype); optype = (optypes >> (opcode_index * 2)) & 0x3; if (trans->type == SPI_OPCODE_TYPE_WRITE_NO_ADDRESS && optype == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS && @@ -298,12 +445,12 @@ static int spi_setup_offset(spi_transaction *trans) } } -int spi_xfer(struct spi_slave *slave, const void *dout, unsigned int bitsout, - void *din, unsigned int bitsin) +int spi_xfer(struct spi_slave *slave, const void *dout, + unsigned int bitsout, void *din, unsigned int bitsin) { int timeout; - uint16_t spis, spic; + uint16_t status, control; int16_t opcode_index; int with_address; @@ -331,7 +478,7 @@ int spi_xfer(struct spi_slave *slave, const void *dout, unsigned int bitsout, /* 60 ms are 9.6 million cycles at 16 MHz. */ timeout = 100 * 60; - while ((readw(&ich7_spi->spis) & SPIS_SCIP) && --timeout) + while ((readb(cntlr.status) & SPIS_SCIP) && --timeout) udelay(10); if (!timeout) { puts("ICH SPI: SCIP never cleared\n"); @@ -345,75 +492,74 @@ int spi_xfer(struct spi_slave *slave, const void *dout, unsigned int bitsout, return 1; /* - * Read or write up to 64 bytes at a time until everything has been - * sent. + * Read or write up to databytes bytes at a time until everything has + * been sent. */ while (trans.bytesout || trans.bytesin) { - const unsigned maxdata = 64; uint32_t data_length; /* SPI addresses are 24 bit only */ - writel(trans.offset & 0x00FFFFFF, &ich7_spi->spia); + writel(trans.offset & 0x00FFFFFF, cntlr.addr); if (trans.bytesout) - data_length = min(trans.bytesout, maxdata); + data_length = min(trans.bytesout, cntlr.databytes); else - data_length = min(trans.bytesin, maxdata); + data_length = min(trans.bytesin, cntlr.databytes); /* Program data into FDATA0 to N */ if (trans.bytesout) { - write_reg(trans.out, ich7_spi->spid, data_length); + write_reg(trans.out, cntlr.data, data_length); spi_use_out(&trans, data_length); if (with_address) trans.offset += data_length; } - /* Assemble SPIS */ - spis = readw(&ich7_spi->spis); + /* Assemble the status register */ + status = readb(cntlr.status); /* keep reserved bits */ - spis &= SPIS_RESERVED_MASK; + status &= SPIS_RESERVED_MASK; /* clear error status registers */ - spis |= (SPIS_CDS | SPIS_FCERR); - writew(spis, &ich7_spi->spis); + status |= (SPIS_CDS | SPIS_FCERR); + writeb(status, cntlr.status); - /* Assemble SPIC */ - spic = (opcode_index & 0x07) << 4; + /* Assemble the control register */ + control = (opcode_index & 0x07) << 4; if (data_length != 0) { - spic |= SPIC_DS; - spic |= (data_length - 1) << 8; + control |= SPIC_DS; + control |= (data_length - 1) << 8; } timeout = 100 * 60; /* Start */ - spic |= SPIC_SCGO; + control |= SPIC_SCGO; /* write it */ - writew(spic, &ich7_spi->spic); + writew(control, cntlr.control); /* Wait for Cycle Done Status or Flash Cycle Error. */ - spis = readw(&ich7_spi->spis); - while (((spis & (SPIS_CDS | SPIS_FCERR)) == 0) && --timeout) { + status = readb(cntlr.status); + while (((status & (SPIS_CDS | SPIS_FCERR)) == 0) && --timeout) { udelay(10); - spis = readw(&ich7_spi->spis); + status = readb(cntlr.status); } if (!timeout) { - printf("timeout, ICH7_REG_SPIS=0x%04x\n", - readw(&ich7_spi->spis)); + printf("timeout, status = 0x%04x\n", + readb(cntlr.status)); return 1; } - if (spis & SPIS_FCERR) { + if (status & SPIS_FCERR) { puts("ICH SPI: Transaction error\n"); /* keep reserved bits */ - spis &= SPIS_RESERVED_MASK; - writew(spis | SPIS_FCERR, &ich7_spi->spis); + status &= SPIS_RESERVED_MASK; + writeb(status | SPIS_FCERR, cntlr.status); return 1; } if (trans.bytesin) { - read_reg(ich7_spi->spid, trans.in, data_length); + read_reg(cntlr.data, trans.in, data_length); spi_use_in(&trans, data_length); if (with_address) trans.offset += data_length; |