diff options
Diffstat (limited to 'src/soc/intel/quark/spi.c')
-rw-r--r-- | src/soc/intel/quark/spi.c | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/src/soc/intel/quark/spi.c b/src/soc/intel/quark/spi.c new file mode 100644 index 0000000000..1d257a563e --- /dev/null +++ b/src/soc/intel/quark/spi.c @@ -0,0 +1,301 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <arch/io.h> +#include <assert.h> +#include <bootstate.h> +#include <console/console.h> +#include <device/pci_ids.h> +#include <device/pci_def.h> +#include <delay.h> +#include <soc/pci_devs.h> +#include <soc/QuarkNcSocId.h> +#include <soc/spi.h> + +struct spi_context spi_driver_context = { + NULL, + 0, + 0 +}; + +void spi_bios_base(uint32_t bios_base_address) +{ + uint32_t address; + volatile struct flash_ctrlr *ctrlr = spi_driver_context.ctrlr; + + /* Prevent all SPI operations below this address */ + address = 0xff000000 | bios_base_address; + ctrlr->bbar = address; +} + +void spi_controller_lock(void) +{ + volatile struct flash_ctrlr *ctrlr = spi_driver_context.ctrlr; + + /* Prevent BIOS and system from changing the SPI controller setup */ + ctrlr->status |= SPISTS_CLD; +} + +int spi_protection(uint32_t address, uint32_t length) +{ + uint32_t base; + volatile struct flash_ctrlr *ctrlr = spi_driver_context.ctrlr; + int index; + uint32_t limit; + uint32_t protect; + uint32_t value; + + /* Determine the protection range */ + base = address; + limit = address + length - 1; + protect = SPIPBR_WPE | (limit & SPIPBR_PRL) + | ((base >> SPIPBR_PRB_SHIFT) & SPIPBR_PRB); + + /* Walk the list of protected areas */ + for (index = 0; index < ARRAY_SIZE(ctrlr->pbr); index++) { + value = read32(&ctrlr->pbr[index]); + + /* Don't duplicate if the range is already protected */ + if (value == protect) + return 0; + + /* Use the first free register to protect this range */ + if ((value & SPIPBR_WPE) == 0) { + write32(&ctrlr->pbr[index], protect); + return 0; + } + } + + /* No free protection range registers */ + printk(BIOS_ERR, + "Failed to set protection: 0x%08x - 0x%08x, PRRs full!\n", + address, address + length - 1); + return -1; +} + +static int xfer(const struct spi_slave *slave, const void *dout, + size_t bytesout, void *din, size_t bytesin) +{ + struct spi_context *context; + uint16_t control; + volatile struct flash_ctrlr *ctrlr; + uint8_t *data; + int index; + uint8_t opcode; + uint32_t status; + int type; + + /* Locate the context structure */ + context = &spi_driver_context; + ctrlr = context->ctrlr; + + /* Validate the buffer sizes */ + if (bytesin > sizeof(ctrlr->data)) { + printk(BIOS_ERR, "bytesin > %ld\n", sizeof(ctrlr->data)); + goto error; + } + + if (bytesin && (din == NULL)) { + printk(BIOS_ERR, "din is NULL\n"); + goto error; + } + + if (bytesout == 0) { + /* Check for a read operation */ + if (bytesin == 0) { + printk(BIOS_ERR, "bytesout and bytesin == 0\n"); + goto error; + } + + /* Issue the read operation */ + control = context->control; + control |= SPICTL_DC | ((bytesin - 1) << SPICTL_DBCNT_SHIFT); + goto start_cycle; + } + + /* Locate the opcode in the opcode table */ + data = (uint8_t *)dout; + opcode = *data++; + bytesout -= 1; + for (index = 0; index < sizeof(ctrlr->opmenu); index++) + if (opcode == ctrlr->opmenu[index]) + break; + + /* Check for a prefix byte */ + if (index == sizeof(ctrlr->opmenu)) { + for (index = 0; index < sizeof(ctrlr->prefix); index++) + if (opcode == ctrlr->prefix[index]) + break; + + /* Handle the unknown opcode error */ + if (index == sizeof(ctrlr->prefix)) { + printk(BIOS_ERR, "Unknown SPI flash opcode\n"); + goto error; + } + + /* Save the index for the next operation */ + context->prefix = index; + return 0; + } + + /* Get the opcode type */ + type = (ctrlr->type >> (index * 2)) + & (SPITYPE_ADDRESS | SPITYPE_PREFIX); + + /* Determine if the opcode has an address */ + if (type & SPITYPE_ADDRESS) { + if (bytesout < 3) { + printk(BIOS_ERR, "Missing address bytes\n"); + goto error; + } + + /* Use chip select 0 */ + ctrlr->address = (data[0] << 16) + | (data[1] << 8) + | data[2]; + status = ctrlr->address; + data += 3; + bytesout -= 3; + } + + /* Build the control value */ + control = (index << SPICTL_COPTR_SHIFT) + | (context->prefix << SPICTL_SOPTR_SHIFT) + | SPICTL_CG | SPICTL_AR; + if (bytesout) { + memcpy((void *)&ctrlr->data[0], data, bytesout); + control |= SPICTL_DC | ((bytesout - 1) << SPICTL_DBCNT_SHIFT); + } + + /* Save the control value for the read operation request */ + if (!(type & SPITYPE_PREFIX)) { + context->control = control; + return 0; + } + + /* Write operations require a prefix */ + control |= SPICTL_ACS; + +start_cycle: + /* Start the SPI cycle */ + ctrlr->control = control; + status = ctrlr->control; + context->prefix = 0; + + /* Wait for the access to complete */ + while ((status = ctrlr->status) & SPISTS_CIP) + udelay(1); + + /* Clear any errors */ + ctrlr->status = status; + + /* Handle the blocked access error */ + if (status & SPISTS_BA) { + printk(BIOS_ERR, "SPI access blocked!\n"); + return -1; + } + + /* Check for done */ + if (status & SPISTS_CD) { + /* Return any receive data */ + if (bytesin) + memcpy(din, (void *)&ctrlr->data[0], bytesin); + return 0; + } + + /* Handle the timeout error */ + printk(BIOS_ERR, "SPI transaction timeout!\n"); + +error: + context->prefix = 0; + return -1; +} + +void spi_init(void) +{ + uint32_t bios_control; + struct spi_context *context; + volatile struct flash_ctrlr *ctrlr; + device_t dev; + uint32_t rcba; + + /* Determine the base address of the SPI flash controller */ + context = &spi_driver_context; + dev = dev_find_device(PCI_VENDOR_ID_INTEL, LPC_DEVID, NULL); + rcba = pci_read_config32(dev, R_QNC_LPC_RCBA); + if (!(rcba & B_QNC_LPC_RCBA_EN)) { + printk(BIOS_ERR, "RBCA not enabled\n"); + return; + } + rcba &= B_QNC_LPC_RCBA_MASK; + ctrlr = (volatile struct flash_ctrlr *)rcba; + + /* Enable writes to the SPI flash */ + bios_control = pci_read_config32(dev, R_QNC_LPC_BIOS_CNTL); + bios_control |= B_QNC_LPC_BIOS_CNTL_BIOSWE; + pci_write_config32(dev, R_QNC_LPC_BIOS_CNTL, bios_control); + + /* Setup the SPI flash controller */ + context->ctrlr = ctrlr; + ctrlr->opmenu[0] = 0x03; /* Read */ + ctrlr->opmenu[1] = 0x0b; /* Read fast */ + ctrlr->opmenu[2] = 0x05; /* Read status */ + ctrlr->opmenu[3] = 0x9f; /* Read ID */ + ctrlr->opmenu[4] = 0x02; /* Page program */ + ctrlr->opmenu[5] = 0x20; /* Erase 4 KiB */ + ctrlr->opmenu[6] = 0xd8; /* Erase 64 KiB */ + ctrlr->opmenu[7] = 0x01; /* Write status */ + ctrlr->prefix[0] = 0x50; /* Write status enable */ + ctrlr->prefix[1] = 0x06; /* Write enable */ + ctrlr->type = SPITYPE_ADDRESS /* Read */ + | (SPITYPE_ADDRESS << 2) /* Read fast */ + | (0 << 4) /* Read status */ + | (0 << 6) /* Read ID */ + | ((SPITYPE_ADDRESS | SPITYPE_PREFIX) << 8) /* Page program */ + | ((SPITYPE_ADDRESS | SPITYPE_PREFIX) << 10) /* Erase 4 KiB */ + | ((SPITYPE_ADDRESS | SPITYPE_PREFIX) << 12) /* Erase 64 KiB */ + | (SPITYPE_PREFIX << 14); /* Write status */ +} + +static void spi_init_cb(void *unused) +{ + struct spi_flash flash; + + spi_init(); + if (spi_flash_probe(0, 0, &flash)) { + printk(BIOS_DEBUG, "SPI flash failed initialization!\n"); + return; + } + printk(BIOS_DEBUG, "SPI flash successfully initialized\n"); +} + +BOOT_STATE_INIT_ENTRY(BS_DEV_INIT, BS_ON_ENTRY, spi_init_cb, NULL); + +const struct spi_ctrlr spi_driver = { + .xfer = xfer, + .max_xfer_size = 64, + .deduct_cmd_len = false, +}; + +const struct spi_ctrlr_buses spi_ctrlr_bus_map[] = { + { + .ctrlr = &spi_driver, + .bus_start = 0, + .bus_end = 0, + }, +}; + +const size_t spi_ctrlr_bus_map_count = ARRAY_SIZE(spi_ctrlr_bus_map); |