/* * 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]; /* read in order to flush the write buffer */ 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);