summaryrefslogtreecommitdiff
path: root/chip
diff options
context:
space:
mode:
authorScott Worley <scott.worley@microchip.corp-partner.google.com>2017-12-21 14:20:09 -0500
committerchrome-bot <chrome-bot@chromium.org>2017-12-28 14:50:30 -0800
commit0a6a7be572cf74af531f922ad84b767749e852a7 (patch)
treec4cc627bf43b26a83da9890133f17c34bf4aca62 /chip
parentbc8fda9e0f4737be3fc635832d4099a9e3b5451a (diff)
downloadchrome-ec-0a6a7be572cf74af531f922ad84b767749e852a7.tar.gz
ec_chip_mchp: Add SPI files
Add Microchip MEC17xx family SPI master controllers and flash files. SPI implements public interface wrapper for QMSPI and GPSPI. MEC17xx family uses QMSPI for loading EC firmware. GPSPI is for general use (sensor). BRANCH=none BUG= TEST=Review only. Change-Id: I23001e254dd437caa0917949f4dd2eb903f1adb1 Signed-off-by: Scott Worley <scott.worley@microchip.corp-partner.google.com>
Diffstat (limited to 'chip')
-rw-r--r--chip/mchp/config_flash_layout.h103
-rw-r--r--chip/mchp/flash.c278
-rw-r--r--chip/mchp/gpspi.c267
-rw-r--r--chip/mchp/gpspi_chip.h35
-rw-r--r--chip/mchp/qmspi.c700
-rw-r--r--chip/mchp/qmspi_chip.h67
-rw-r--r--chip/mchp/spi.c297
-rw-r--r--chip/mchp/spi_chip.h60
8 files changed, 1807 insertions, 0 deletions
diff --git a/chip/mchp/config_flash_layout.h b/chip/mchp/config_flash_layout.h
new file mode 100644
index 0000000000..56b69f1d32
--- /dev/null
+++ b/chip/mchp/config_flash_layout.h
@@ -0,0 +1,103 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __CROS_EC_CONFIG_FLASH_LAYOUT_H
+#define __CROS_EC_CONFIG_FLASH_LAYOUT_H
+
+/*
+ * mec17xx flash layout:
+ * - Non memory-mapped, external SPI.
+ * - RW image at the beginning of writable region.
+ * - Bootloader at the beginning of protected region, followed by RO image.
+ * - Loader + (RO | RW) loaded into program memory.
+ */
+
+/* Non-memmapped, external SPI */
+#define CONFIG_EXTERNAL_STORAGE
+#undef CONFIG_MAPPED_STORAGE
+#undef CONFIG_FLASH_PSTATE
+#define CONFIG_SPI_FLASH
+
+/*
+ * MEC17xx BootROM uses two 4-byte TAG's at SPI offset 0x0 and 0x04.
+ * One valid TAG must be present.
+ * TAG's point to a Header which must be located on a 256 byte
+ * boundary anywhere in the flash (24-bit addressing).
+ * Locate BootROM load Header + LFW + EC_RO at start of second
+ * 4KB sector (offset 0x1000).
+ * Locate BootROM load Header + EC_RW at start of second half of
+ * SPI flash.
+ * LFW size is 4KB
+ * EC_RO and EC_RW padded sizes from the build are 188KB each.
+ * Storage size is 1/2 flash size.
+ */
+#define CONFIG_EC_PROTECTED_STORAGE_OFF 0
+/* Lower 256KB of flash is protected region */
+#define CONFIG_EC_PROTECTED_STORAGE_SIZE 0x40000
+/* Writable storage for EC_RW starts at 256KB */
+#define CONFIG_EC_WRITABLE_STORAGE_OFF 0x40000
+/* Writeable storage is 256KB */
+#define CONFIG_EC_WRITABLE_STORAGE_SIZE 0x40000
+
+
+/* Loader resides at the beginning of program memory */
+#define CONFIG_LOADER_MEM_OFF 0
+#define CONFIG_LOADER_SIZE 0x1000
+
+/* Write protect Loader and RO Image */
+#define CONFIG_WP_STORAGE_OFF CONFIG_EC_PROTECTED_STORAGE_OFF
+/*
+ * Write protect LFW + EC_RO
+ */
+#define CONFIG_WP_STORAGE_SIZE CONFIG_EC_PROTECTED_STORAGE_SIZE
+
+/*
+ * RO / RW images follow the loader in program memory. Either RO or RW
+ * image will be loaded -- both cannot be loaded at the same time.
+ */
+#define CONFIG_RO_MEM_OFF (CONFIG_LOADER_MEM_OFF + \
+ CONFIG_LOADER_SIZE)
+/*
+ * MEC1701H Code + Data SRAM size = 256KB
+ * Allocate 32KB for data leaving 192KB for Code.
+ * The loader is resident in first 4KB of Code SRAM.
+ * EC_RO/RW can load 192 - 4 = 188 KB maximum.
+ * !!! This size MUST be a multiple of flash erase block size.
+ * defined by CONFIG_FLASH_ERASE_SIZE in chip/config_chip.h !!!
+ */
+#define CONFIG_RO_SIZE (188 * 1024)
+#define CONFIG_RW_MEM_OFF CONFIG_RO_MEM_OFF
+#define CONFIG_RW_SIZE CONFIG_RO_SIZE
+
+/*
+ * WP region consists of first half of SPI containing TAGs at beginning
+ * of SPI flash and header + binary(LFW+EC_RO) at offset 0x1000.
+ * Two 4-byte TAG's exist at offset 0 and 4 in the SPI flash device.
+ * We only use first TAG pointing to LFW + EC_RO.
+ * Header size is 128 bytes. Firmware binary is located immediately
+ * after the header.
+ * Second half of SPI flash contains:
+ * Header(128 bytes) + EC_RW
+ */
+#define CONFIG_BOOT_HEADER_STORAGE_OFF 0x1000
+#define CONFIG_RW_BOOT_HEADER_STORAGE_OFF 0x0
+#define CONFIG_BOOT_HEADER_STORAGE_SIZE 0x80
+
+/* Loader / lfw image immediately follows the boot header on SPI */
+#define CONFIG_LOADER_STORAGE_OFF (CONFIG_BOOT_HEADER_STORAGE_OFF + \
+ CONFIG_BOOT_HEADER_STORAGE_SIZE)
+
+/* RO image immediately follows the loader image */
+#define CONFIG_RO_STORAGE_OFF (CONFIG_LOADER_STORAGE_OFF + \
+ CONFIG_LOADER_SIZE)
+
+/*
+ * Header + RW image starts at offset 0 of second half of SPI.
+ */
+#define CONFIG_RW_STORAGE_OFF (CONFIG_RW_BOOT_HEADER_STORAGE_OFF + \
+ CONFIG_BOOT_HEADER_STORAGE_SIZE)
+
+
+#endif /* __CROS_EC_CONFIG_FLASH_LAYOUT_H */
diff --git a/chip/mchp/flash.c b/chip/mchp/flash.c
new file mode 100644
index 0000000000..ca198476b8
--- /dev/null
+++ b/chip/mchp/flash.c
@@ -0,0 +1,278 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "common.h"
+#include "console.h"
+#include "flash.h"
+#include "host_command.h"
+#include "shared_mem.h"
+#include "spi.h"
+#include "spi_flash.h"
+#include "system.h"
+#include "util.h"
+#include "hooks.h"
+#include "tfdp_chip.h"
+
+#define PAGE_SIZE 256
+
+#define FLASH_SYSJUMP_TAG 0x5750 /* "WP" - Write Protect */
+#define FLASH_HOOK_VERSION 1
+
+static int entire_flash_locked;
+
+/* The previous write protect state before sys jump */
+
+struct flash_wp_state {
+ int entire_flash_locked;
+};
+
+/**
+ * Read from physical flash.
+ *
+ * @param offset Flash offset to write.
+ * @param size Number of bytes to write.
+ * @param data Destination buffer for data.
+ */
+int flash_physical_read(int offset, int size, char *data)
+{
+ trace13(0, FLASH, 0,
+ "flash_phys_read: offset=0x%08X size=0x%08X dataptr=0x%08X",
+ offset, size, (uint32_t)data);
+ return spi_flash_read(data, offset, size);
+}
+
+/**
+ * Write to physical flash.
+ *
+ * Offset and size must be a multiple of CONFIG_FLASH_WRITE_SIZE.
+ *
+ * @param offset Flash offset to write.
+ * @param size Number of bytes to write.
+ * @param data Data to write to flash. Must be 32-bit aligned.
+ */
+int flash_physical_write(int offset, int size, const char *data)
+{
+ int ret = EC_SUCCESS;
+ int i, write_size;
+
+ trace13(0, FLASH, 0,
+ "flash_phys_write: offset=0x%08X size=0x%08X dataptr=0x%08X",
+ offset, size, (uint32_t)data);
+
+ if (entire_flash_locked)
+ return EC_ERROR_ACCESS_DENIED;
+
+ /* Fail if offset, size, and data aren't at least word-aligned */
+ if ((offset | size | (uint32_t)(uintptr_t)data) & 3)
+ return EC_ERROR_INVAL;
+
+ for (i = 0; i < size; i += write_size) {
+ write_size = MIN((size - i), SPI_FLASH_MAX_WRITE_SIZE);
+ ret = spi_flash_write(offset + i,
+ write_size,
+ (uint8_t *)data + i);
+ if (ret != EC_SUCCESS)
+ break;
+ }
+ return ret;
+}
+
+/**
+ * Erase physical flash.
+ *
+ * Offset and size must be a multiple of CONFIG_FLASH_ERASE_SIZE.
+ *
+ * @param offset Flash offset to erase.
+ * @param size Number of bytes to erase.
+ */
+int flash_physical_erase(int offset, int size)
+{
+ int ret;
+
+ if (entire_flash_locked)
+ return EC_ERROR_ACCESS_DENIED;
+
+ trace12(0, FLASH, 0,
+ "flash_phys_erase: offset=0x%08X size=0x%08X",
+ offset, size);
+ ret = spi_flash_erase(offset, size);
+ return ret;
+}
+
+/**
+ * Read physical write protect setting for a flash bank.
+ *
+ * @param bank Bank index to check.
+ * @return non-zero if bank is protected until reboot.
+ */
+int flash_physical_get_protect(int bank)
+{
+ return spi_flash_check_protect(bank * CONFIG_FLASH_BANK_SIZE,
+ CONFIG_FLASH_BANK_SIZE);
+}
+
+/**
+ * Protect flash now.
+ *
+ * This is always successful, and only emulates "now" protection
+ *
+ * @param all Protect all (=1) or just read-only
+ * @return non-zero if error.
+ */
+int flash_physical_protect_now(int all)
+{
+ if (all)
+ entire_flash_locked = 1;
+
+ /*
+ * RO "now" protection is not currently implemented. If needed, it
+ * can be added by splitting the entire_flash_locked variable into
+ * and RO and RW vars, and setting + checking the appropriate var
+ * as required.
+ */
+ return EC_SUCCESS;
+}
+
+/**
+ * Return flash protect state flags from the physical layer.
+ *
+ * This should only be called by flash_get_protect().
+ *
+ * Uses the EC_FLASH_PROTECT_* flags from ec_commands.h
+ */
+uint32_t flash_physical_get_protect_flags(void)
+{
+ uint32_t flags = 0;
+
+ if (spi_flash_check_protect(CONFIG_WP_STORAGE_OFF,
+ CONFIG_WP_STORAGE_SIZE)) {
+ flags |= EC_FLASH_PROTECT_RO_AT_BOOT | EC_FLASH_PROTECT_RO_NOW;
+ }
+
+ if (entire_flash_locked)
+ flags |= EC_FLASH_PROTECT_ALL_NOW;
+
+ return flags;
+}
+
+/**
+ * Return the valid flash protect flags.
+ *
+ * @return A combination of EC_FLASH_PROTECT_* flags from ec_commands.h
+ */
+uint32_t flash_physical_get_valid_flags(void)
+{
+ return EC_FLASH_PROTECT_RO_AT_BOOT |
+ EC_FLASH_PROTECT_RO_NOW |
+ EC_FLASH_PROTECT_ALL_NOW;
+}
+
+/**
+ * Return the writable flash protect flags.
+ *
+ * @param cur_flags The current flash protect flags.
+ * @return A combination of EC_FLASH_PROTECT_* flags from ec_commands.h
+ */
+uint32_t flash_physical_get_writable_flags(uint32_t cur_flags)
+{
+ uint32_t ret = 0;
+ enum spi_flash_wp wp_status = SPI_WP_NONE;
+
+ wp_status = spi_flash_check_wp();
+
+ if (wp_status == SPI_WP_NONE || (wp_status == SPI_WP_HARDWARE &&
+ !(cur_flags & EC_FLASH_PROTECT_GPIO_ASSERTED)))
+ ret = EC_FLASH_PROTECT_RO_AT_BOOT | EC_FLASH_PROTECT_RO_NOW;
+
+ if (!entire_flash_locked)
+ ret |= EC_FLASH_PROTECT_ALL_NOW;
+
+ return ret;
+}
+
+/**
+ * Enable write protect for the specified range.
+ *
+ * Once write protect is enabled, it will stay enabled until HW PIN is
+ * de-asserted and SRP register is unset.
+ *
+ * However, this implementation treats FLASH_WP_ALL as FLASH_WP_RO but
+ * tries to remember if "all" region is protected.
+ *
+ * @param range The range to protect.
+ * @return EC_SUCCESS, or nonzero if error.
+ */
+int flash_physical_protect_at_boot(uint32_t new_flags)
+{
+ int offset, size, ret;
+ enum spi_flash_wp flashwp = SPI_WP_NONE;
+
+ if ((new_flags & (EC_FLASH_PROTECT_RO_AT_BOOT |
+ EC_FLASH_PROTECT_ALL_AT_BOOT)) == 0) {
+ /* Clear protection */
+ offset = size = 0;
+ flashwp = SPI_WP_NONE;
+ } else {
+ if (new_flags & EC_FLASH_PROTECT_ALL_AT_BOOT)
+ entire_flash_locked = 1;
+
+ offset = CONFIG_WP_STORAGE_OFF;
+ size = CONFIG_WP_STORAGE_SIZE;
+ flashwp = SPI_WP_HARDWARE;
+ }
+
+ ret = spi_flash_set_protect(offset, size);
+ if (ret == EC_SUCCESS)
+ ret = spi_flash_set_wp(flashwp);
+ return ret;
+}
+
+/**
+ * Initialize the module.
+ *
+ * Applies at-boot protection settings if necessary.
+ */
+int flash_pre_init(void)
+{
+ flash_physical_restore_state();
+ return EC_SUCCESS;
+}
+
+int flash_physical_restore_state(void)
+{
+ uint32_t reset_flags = system_get_reset_flags();
+ int version, size;
+ const struct flash_wp_state *prev;
+
+ /*
+ * If we have already jumped between images, an earlier image
+ * could have applied write protection. Nothing additional needs
+ * to be done.
+ */
+ if (reset_flags & RESET_FLAG_SYSJUMP) {
+ prev = (const struct flash_wp_state *)system_get_jump_tag(
+ FLASH_SYSJUMP_TAG, &version, &size);
+ if (prev && version == FLASH_HOOK_VERSION &&
+ size == sizeof(*prev))
+ entire_flash_locked = prev->entire_flash_locked;
+ return 1;
+ }
+
+ return 0;
+}
+
+/************************************************************************/
+/* Hooks */
+
+static void flash_preserve_state(void)
+{
+ struct flash_wp_state state;
+
+ state.entire_flash_locked = entire_flash_locked;
+
+ system_add_jump_tag(FLASH_SYSJUMP_TAG, FLASH_HOOK_VERSION,
+ sizeof(state), &state);
+}
+DECLARE_HOOK(HOOK_SYSJUMP, flash_preserve_state, HOOK_PRIO_DEFAULT);
diff --git a/chip/mchp/gpspi.c b/chip/mchp/gpspi.c
new file mode 100644
index 0000000000..1cb2b4fbcb
--- /dev/null
+++ b/chip/mchp/gpspi.c
@@ -0,0 +1,267 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* General Purpose SPI master module for MCHP MEC */
+
+#include "common.h"
+#include "console.h"
+#include "dma.h"
+#include "gpio.h"
+#include "registers.h"
+#include "spi.h"
+#include "timer.h"
+#include "util.h"
+#include "hooks.h"
+#include "task.h"
+#include "spi_chip.h"
+#include "gpspi_chip.h"
+#include "tfdp_chip.h"
+
+#define CPUTS(outstr) cputs(CC_SPI, outstr)
+#define CPRINTS(format, args...) cprints(CC_SPI, format, ## args)
+
+#define SPI_BYTE_TRANSFER_TIMEOUT_US (3 * MSEC)
+/* One byte at 12 MHz full duplex = 0.67 us */
+#define SPI_BYTE_TRANSFER_POLL_INTERVAL_US 20
+
+/*
+ * GP-SPI
+ */
+
+/**
+ * Return zero based GPSPI controller index given hardware port.
+ * @param hw_port b[7:4]==1 (GPSPI), b[3:0]=0(GPSPI0), 1(GPSPI1)
+ * @return 0(GPSPI0) or 1(GPSPI1)
+ */
+static uint8_t gpspi_port_to_ctrl_id(uint8_t hw_port)
+{
+ return (hw_port & 0x01);
+}
+
+static int gpspi_wait_byte(const int ctrl)
+{
+ timestamp_t deadline;
+
+ deadline.val = get_time().val + SPI_BYTE_TRANSFER_TIMEOUT_US;
+ while ((MCHP_SPI_SR(ctrl) & 0x3) != 0x3) {
+ if (timestamp_expired(deadline, NULL))
+ return EC_ERROR_TIMEOUT;
+ usleep(SPI_BYTE_TRANSFER_POLL_INTERVAL_US);
+ }
+ return EC_SUCCESS;
+}
+
+/* NOTE: auto-read must be disabled before calling this routine! */
+static void gpspi_rx_fifo_clean(const int ctrl)
+{
+ uint8_t dummy = 0;
+
+ /* If ACTIVE and/or RXFF then clean it */
+ if ((MCHP_SPI_SR(ctrl) & 0x4) == 0x4)
+ dummy += MCHP_SPI_RD(ctrl);
+
+ if ((MCHP_SPI_SR(ctrl) & 0x2) == 0x2)
+ dummy += MCHP_SPI_RD(ctrl);
+}
+/*
+ * NOTE: auto-read must be disabled before calling this routine!
+ */
+#ifndef CONFIG_MCHP_GPSPI_TX_DMA
+static int gpspi_tx(const int ctrl, const uint8_t *txdata, int txlen)
+{
+ int i;
+ int ret;
+ uint8_t dummy = 0;
+
+ gpspi_rx_fifo_clean(ctrl);
+
+ ret = EC_SUCCESS;
+ for (i = 0; i < txlen; ++i) {
+ MCHP_SPI_TD(ctrl) = txdata[i];
+ ret = gpspi_wait_byte(ctrl);
+ if (ret != EC_SUCCESS)
+ break;
+ dummy += MCHP_SPI_RD(ctrl);
+ }
+
+ return ret;
+}
+#endif
+
+int gpspi_transaction_async(const struct spi_device_t *spi_device,
+ const uint8_t *txdata, int txlen,
+ uint8_t *rxdata, int rxlen)
+{
+ int hw_port, ctrl;
+ int ret = EC_SUCCESS;
+ int cs_asserted = 0;
+ const struct dma_option *opdma;
+#ifdef CONFIG_MCHP_GPSPI_TX_DMA
+ dma_chan_t *chan;
+#endif
+ if (spi_device == NULL)
+ return EC_ERROR_PARAM1;
+
+ hw_port = spi_device->port;
+
+ ctrl = gpspi_port_to_ctrl_id(hw_port);
+
+ /* Disable auto read */
+ MCHP_SPI_CR(ctrl) &= ~(1 << 5);
+
+ if ((txdata != NULL) && (txdata != 0)) {
+#ifdef CONFIG_MCHP_GPSPI_TX_DMA
+ opdma = spi_dma_option(spi_device, SPI_DMA_OPTION_WR);
+ if (opdma == NULL)
+ return EC_ERROR_INVAL;
+
+ gpspi_rx_fifo_clean(ctrl);
+
+ dma_prepare_tx(opdma, txlen, txdata);
+
+ chan = dma_get_channel(opdma->channel);
+
+ gpio_set_level(spi_device->gpio_cs, 0);
+ cs_asserted = 1;
+
+ dma_go(chan);
+ ret = dma_wait(opdma->channel);
+ if (ret == EC_SUCCESS)
+ ret = gpspi_wait_byte(ctrl);
+
+ dma_disable(opdma->channel);
+ dma_clear_isr(opdma->channel);
+
+ gpspi_rx_fifo_clean(ctrl);
+#else
+ gpio_set_level(spi_device->gpio_cs, 0);
+ cs_asserted = 1;
+
+ ret = gpspi_tx(ctrl, txdata, txlen);
+#endif
+ }
+
+ if (ret == EC_SUCCESS)
+ if ((rxlen != 0) && (rxdata != NULL)) {
+ ret = EC_ERROR_INVAL;
+ opdma = spi_dma_option(spi_device, SPI_DMA_OPTION_RD);
+ if (opdma != NULL) {
+ if (!cs_asserted)
+ gpio_set_level(spi_device->gpio_cs, 0);
+ /* Enable auto read */
+ MCHP_SPI_CR(ctrl) |= 1 << 5;
+ dma_start_rx(opdma, rxlen, rxdata);
+ MCHP_SPI_TD(ctrl) = 0;
+ ret = EC_SUCCESS;
+ }
+ }
+
+ return ret;
+}
+
+int gpspi_transaction_flush(const struct spi_device_t *spi_device)
+{
+ int ctrl, hw_port;
+ int ret;
+ enum dma_channel chan;
+ const struct dma_option *opdma;
+ timestamp_t deadline;
+
+ if (spi_device == NULL)
+ return EC_ERROR_PARAM1;
+
+ hw_port = spi_device->port;
+ ctrl = gpspi_port_to_ctrl_id(hw_port);
+ opdma = spi_dma_option(spi_device, SPI_DMA_OPTION_RD);
+ chan = opdma->channel;
+
+ ret = dma_wait(chan);
+
+ /* Disable auto read */
+ MCHP_SPI_CR(ctrl) &= ~(1 << 5);
+
+ deadline.val = get_time().val + SPI_BYTE_TRANSFER_TIMEOUT_US;
+ /* Wait for FIFO empty SPISR_TXBE */
+ while ((MCHP_SPI_SR(ctrl) & 0x01) != 0x1) {
+ if (timestamp_expired(deadline, NULL)) {
+ ret = EC_ERROR_TIMEOUT;
+ break;
+ }
+ usleep(SPI_BYTE_TRANSFER_POLL_INTERVAL_US);
+ }
+
+ dma_disable(chan);
+ dma_clear_isr(chan);
+ if (MCHP_SPI_SR(ctrl) & 0x2)
+ hw_port = MCHP_SPI_RD(ctrl);
+
+ gpio_set_level(spi_device->gpio_cs, 1);
+
+ return ret;
+}
+
+int gpspi_transaction_wait(const struct spi_device_t *spi_device)
+{
+ const struct dma_option *opdma;
+
+ opdma = spi_dma_option(spi_device, SPI_DMA_OPTION_RD);
+
+ return dma_wait(opdma->channel);
+}
+
+/**
+ * Enable GPSPI controller and MODULE_SPI_MASTER pins
+ *
+ * @param hw_port b[7:4]=1 b[3:0]=0(GPSPI0), 1(GPSPI1)
+ * @param enable
+ * @return EC_SUCCESS or EC_ERROR_INVAL if port is unrecognized
+ * @note called from mec1701/spi.c
+ *
+ */
+int gpspi_enable(int hw_port, int enable)
+{
+ uint32_t ctrl;
+
+ if ((hw_port != GPSPI0_PORT) && (hw_port != GPSPI1_PORT))
+ return EC_ERROR_INVAL;
+
+ gpio_config_module(MODULE_SPI_MASTER, (enable > 0));
+
+ ctrl = (uint32_t)hw_port & 0x0f;
+
+ if (enable) {
+
+ if (ctrl)
+ MCHP_PCR_SLP_DIS_DEV(MCHP_PCR_GPSPI1);
+ else
+ MCHP_PCR_SLP_DIS_DEV(MCHP_PCR_GPSPI0);
+
+ /* Set enable bit in SPI_AR */
+ MCHP_SPI_AR(ctrl) |= 0x1;
+
+ /* Set SPDIN to 0 -> Full duplex */
+ MCHP_SPI_CR(ctrl) &= ~(0x3 << 2);
+
+ /* Set CLKPOL, TCLKPH, RCLKPH to 0 */
+ MCHP_SPI_CC(ctrl) &= ~0x7;
+
+ /* Set LSBF to 0 -> MSB first */
+ MCHP_SPI_CR(ctrl) &= ~0x1;
+ } else {
+ /* soft reset */
+ MCHP_SPI_CR(ctrl) |= (1u << 4);
+
+ /* Clear enable bit in SPI_AR */
+ MCHP_SPI_AR(ctrl) &= ~0x1;
+
+ if (ctrl)
+ MCHP_PCR_SLP_EN_DEV(MCHP_PCR_GPSPI1);
+ else
+ MCHP_PCR_SLP_EN_DEV(MCHP_PCR_GPSPI0);
+ }
+
+ return EC_SUCCESS;
+}
+
diff --git a/chip/mchp/gpspi_chip.h b/chip/mchp/gpspi_chip.h
new file mode 100644
index 0000000000..529a727e36
--- /dev/null
+++ b/chip/mchp/gpspi_chip.h
@@ -0,0 +1,35 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Register map for MCHP MEC processor
+ */
+/** @file gpspi_chip.h
+ *MCHP MEC General Purpose SPI Master
+ */
+/** @defgroup MCHP MEC gpspi
+ */
+
+#ifndef _GPSPI_CHIP_H
+#define _GPSPI_CHIP_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+/* struct spi_device_t */
+#include "spi.h"
+
+int gpspi_transaction_flush(const struct spi_device_t *spi_device);
+
+int gpspi_transaction_wait(const struct spi_device_t *spi_device);
+
+int gpspi_transaction_async(const struct spi_device_t *spi_device,
+ const uint8_t *txdata, int txlen,
+ uint8_t *rxdata, int rxlen);
+
+int gpspi_enable(int port, int enable);
+
+#endif /* #ifndef _GPSPI_CHIP_H */
+/** @}
+ */
+
diff --git a/chip/mchp/qmspi.c b/chip/mchp/qmspi.c
new file mode 100644
index 0000000000..1a5f9576b8
--- /dev/null
+++ b/chip/mchp/qmspi.c
@@ -0,0 +1,700 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* QMSPI master module for MCHP MEC family */
+
+#include "common.h"
+#include "console.h"
+#include "dma.h"
+#include "gpio.h"
+#include "registers.h"
+#include "spi.h"
+#include "timer.h"
+#include "util.h"
+#include "hooks.h"
+#include "task.h"
+#include "dma_chip.h"
+#include "spi_chip.h"
+#include "qmspi_chip.h"
+#include "tfdp_chip.h"
+
+#define CPUTS(outstr) cputs(CC_SPI, outstr)
+#define CPRINTS(format, args...) cprints(CC_SPI, format, ## args)
+
+#define QMSPI_TRANSFER_TIMEOUT (100 * MSEC)
+#define QMSPI_BYTE_TRANSFER_TIMEOUT_US (3 * MSEC)
+#define QMSPI_BYTE_TRANSFER_POLL_INTERVAL_US 20
+
+
+
+#ifndef CONFIG_MCHP_QMSPI_TX_DMA
+#ifdef LFW
+/*
+ * MCHP 32-bit timer 0 configured for 1us count down mode and no
+ * interrupt in the LFW environment. Don't need to sleep CPU in LFW.
+ */
+static int qmspi_wait(uint32_t mask, uint32_t mval)
+{
+ uint32_t t1, t2, td;
+
+ t1 = MCHP_TMR32_CNT(0);
+
+ while ((MCHP_QMSPI0_STS & mask) != mval) {
+ t2 = MCHP_TMR32_CNT(0);
+ if (t1 >= t2)
+ td = t1 - t2;
+ else
+ td = t1 + (0xfffffffful - t2);
+ if (td > QMSPI_BYTE_TRANSFER_TIMEOUT_US)
+ return EC_ERROR_TIMEOUT;
+ }
+ return EC_SUCCESS;
+}
+#else
+/*
+ * This version uses the full EC_RO/RW timer infrastructure and it needs
+ * a timer ISR to handle timer underflow. Without the ISR we observe false
+ * timeouts when debugging with JTAG.
+ * QMSPI_BYTE_TRANSFER_TIMEOUT_US currently 3ms
+ * QMSPI_BYTE_TRANSFER_POLL_INTERVAL_US currently 100 us
+ */
+
+static int qmspi_wait(uint32_t mask, uint32_t mval)
+{
+ timestamp_t deadline;
+
+ deadline.val = get_time().val + (QMSPI_BYTE_TRANSFER_TIMEOUT_US);
+
+ while ((MCHP_QMSPI0_STS & mask) != mval) {
+ if (timestamp_expired(deadline, NULL))
+ return EC_ERROR_TIMEOUT;
+
+ usleep(QMSPI_BYTE_TRANSFER_POLL_INTERVAL_US);
+ }
+ return EC_SUCCESS;
+}
+#endif /* #ifdef LFW */
+#endif /* #ifndef CONFIG_MCHP_QMSPI_TX_DMA */
+
+/*
+ * Wait for QMSPI read using DMA to finish.
+ * DMA subsystem has 100 ms timeout
+ */
+int qmspi_transaction_wait(const struct spi_device_t *spi_device)
+{
+ const struct dma_option *opdma;
+
+ opdma = spi_dma_option(spi_device, SPI_DMA_OPTION_RD);
+ if (opdma != NULL)
+ return dma_wait(opdma->channel);
+
+ return EC_ERROR_INVAL;
+}
+
+/*
+ * Create QMSPI transmit data descriptor not using DMA.
+ * Transmit on MOSI pin (single/full-duplex) from TX FIFO.
+ * TX FIFO filled by CPU.
+ * Caller will apply close and last flags if applicable.
+ */
+#ifndef CONFIG_MCHP_QMSPI_TX_DMA
+static uint32_t qmspi_build_tx_descr(uint32_t ntx, uint32_t ndid)
+{
+ uint32_t d;
+
+ d = MCHP_QMSPI_C_1X + MCHP_QMSPI_C_TX_DATA;
+ d |= ((ndid & 0x0F) << MCHP_QMSPI_C_NEXT_DESCR_BITPOS);
+
+ if (ntx <= MCHP_QMSPI_C_MAX_UNITS)
+ d |= MCHP_QMSPI_C_XFRU_1B;
+ else {
+ if ((ntx & 0x0f) == 0) {
+ ntx >>= 4;
+ d |= MCHP_QMSPI_C_XFRU_16B;
+ } else if ((ntx & 0x03) == 0) {
+ ntx >>= 2;
+ d |= MCHP_QMSPI_C_XFRU_4B;
+ } else
+ d |= MCHP_QMSPI_C_XFRU_1B;
+
+ if (ntx > MCHP_QMSPI_C_MAX_UNITS)
+ return 0; /* overflow unit count field */
+ }
+
+ d |= (ntx << MCHP_QMSPI_C_NUM_UNITS_BITPOS);
+
+ return d;
+}
+
+/*
+ * Create QMSPI receive data descriptor using DMA.
+ * Receive data on MISO pin (single/full-duplex) and store in QMSPI
+ * RX FIFO. QMSPI triggers DMA channel to read from RX FIFO and write
+ * to memory. Return value is an uint64_t where low 32-bit word is the
+ * descriptor and upper 32-bit word is DMA channel unit length with
+ * value (1, 2, or 4).
+ * Caller will apply close and last flags if applicable.
+ */
+static uint64_t qmspi_build_rx_descr(uint32_t raddr,
+ uint32_t nrx, uint32_t ndid)
+{
+ uint32_t d, dmau, na;
+ uint64_t u;
+
+ d = MCHP_QMSPI_C_1X + MCHP_QMSPI_C_RX_EN;
+ d |= ((ndid & 0x0F) << MCHP_QMSPI_C_NEXT_DESCR_BITPOS);
+
+ dmau = 1;
+ na = (raddr | nrx) & 0x03;
+ if (na == 0) {
+ d |= MCHP_QMSPI_C_RX_DMA_4B;
+ dmau <<= 2;
+ } else if (na == 0x02) {
+ d |= MCHP_QMSPI_C_RX_DMA_2B;
+ dmau <<= 1;
+ } else {
+ d |= MCHP_QMSPI_C_RX_DMA_1B;
+ }
+
+ if ((nrx & 0x0f) == 0) {
+ nrx >>= 4;
+ d |= MCHP_QMSPI_C_XFRU_16B;
+ } else if ((nrx & 0x03) == 0) {
+ nrx >>= 2;
+ d |= MCHP_QMSPI_C_XFRU_4B;
+ } else {
+ d |= MCHP_QMSPI_C_XFRU_1B;
+ }
+
+ u = 0;
+ if (nrx <= MCHP_QMSPI_C_MAX_UNITS) {
+ d |= (nrx << MCHP_QMSPI_C_NUM_UNITS_BITPOS);
+ u = dmau;
+ u <<= 32;
+ u |= d;
+ }
+
+ return u;
+}
+#endif
+
+#ifdef CONFIG_MCHP_QMSPI_TX_DMA
+
+#define QMSPI_ERR_ANY 0x80
+#define QMSPI_ERR_BAD_PTR 0x81
+#define QMSPI_ERR_OUT_OF_DESCR 0x85
+
+/*
+ * bits[1:0] of word
+ * 1 -> 0
+ * 2 -> 1
+ * 4 -> 2
+ */
+static uint32_t qmspi_pins_encoding(uint8_t npins)
+{
+ return (uint32_t)(npins >> 1) & 0x03;
+}
+
+/*
+ * Clear status, FIFO's, and all descriptors.
+ * Enable descriptor mode.
+ */
+static void qmspi_descr_mode_ready(void)
+{
+ int i;
+
+ MCHP_QMSPI0_CTRL = 0;
+ MCHP_QMSPI0_IEN = 0;
+ MCHP_QMSPI0_EXE = MCHP_QMSPI_EXE_CLR_FIFOS;
+ MCHP_QMSPI0_STS = 0xfffffffful;
+ MCHP_QMSPI0_CTRL = MCHP_QMSPI_C_DESCR_MODE_EN;
+ /* clear all descriptors */
+ for (i = 0; i < MCHP_QMSPI_MAX_DESCR; i++)
+ MCHP_QMSPI0_DESCR(i) = 0;
+}
+
+/*
+ * helper
+ * did = zero based index of start descriptor
+ * descr = descriptor configuration
+ * nb = number of bytes to transfer
+ * Return index of last descriptor allocated or 0xffff
+ * if out of descriptors.
+ * Algorithm:
+ * If requested number of bytes will fit in one descriptor then
+ * configure descriptor for QMSPI byte units and return.
+ * Otherwise allocate multiple descriptor using QMSPI 16-byte mode
+ * and remaining < 16 bytes in byte unit descriptor until all bytes
+ * exhausted or out of descriptors error.
+ */
+static uint32_t qmspi_descr_alloc(uint32_t did,
+ uint32_t descr, uint32_t nb)
+{
+ uint32_t nu;
+
+ while (nb) {
+ if (did >= MCHP_QMSPI_MAX_DESCR)
+ return 0xffff;
+
+ descr &= ~(MCHP_QMSPI_C_NUM_UNITS_MASK +
+ MCHP_QMSPI_C_XFRU_MASK);
+
+ if (nb < (MCHP_QMSPI_C_MAX_UNITS + 1)) {
+ descr |= MCHP_QMSPI_C_XFRU_1B;
+ descr += (nb << MCHP_QMSPI_C_NUM_UNITS_BITPOS);
+ nb = 0;
+ } else {
+ descr |= MCHP_QMSPI_C_XFRU_16B;
+ nu = (nb >> 4) & MCHP_QMSPI_C_NUM_UNITS_MASK0;
+ descr += (nu << MCHP_QMSPI_C_NUM_UNITS_BITPOS);
+ nb -= (nu << 4);
+ }
+
+ descr |= ((did+1) << MCHP_QMSPI_C_NEXT_DESCR_BITPOS);
+ MCHP_QMSPI0_DESCR(did) = descr;
+ if (nb)
+ did++;
+ }
+
+ return did;
+}
+
+/*
+ * Build one or more descriptors for command/data transmit.
+ * cfg b[7:0] = start descriptor index
+ * cfg b[15:8] = number of pins for transmit.
+ * If bytes to transmit will fit in TX FIFO then fill TX FIFO and build
+ * one descriptor.
+ * Otherwise build one or more descriptors to fill TX FIFO using DMA
+ * channel and configure the DMA channel for memory to device transfer.
+ */
+static uint32_t qmspi_xmit_data_descr(const struct dma_option *opdma,
+ uint32_t cfg,
+ const uint8_t *data,
+ uint32_t ndata)
+{
+ uint32_t d, d2, did, dma_cfg;
+
+ did = cfg & 0x0f;
+ d = qmspi_pins_encoding((cfg >> 8) & 0x07);
+
+ if (ndata <= MCHP_QMSPI_TX_FIFO_LEN) {
+ d2 = d + (ndata << MCHP_QMSPI_C_NUM_UNITS_BITPOS) +
+ MCHP_QMSPI_C_XFRU_1B + MCHP_QMSPI_C_TX_DATA;
+ d2 += ((did + 1) << MCHP_QMSPI_C_NEXT_DESCR_BITPOS);
+ MCHP_QMSPI0_DESCR(did) = d2;
+ while (ndata--)
+ MCHP_QMSPI0_TX_FIFO8 = *data++;
+ } else { // TX DMA
+ if (((uint32_t)data | ndata) & 0x03) {
+ dma_cfg = 1;
+ d |= (MCHP_QMSPI_C_TX_DATA +
+ MCHP_QMSPI_C_TX_DMA_1B);
+ } else {
+ dma_cfg = 4;
+ d |= (MCHP_QMSPI_C_TX_DATA +
+ MCHP_QMSPI_C_TX_DMA_4B);
+ }
+ did = qmspi_descr_alloc(did, d, ndata);
+ if (did == 0xffff)
+ return QMSPI_ERR_OUT_OF_DESCR;
+
+ dma_clr_chan(opdma->channel);
+ dma_cfg_buffers(opdma->channel, data, ndata,
+ (void *)MCHP_QMSPI0_TX_FIFO_ADDR);
+ dma_cfg_xfr(opdma->channel, dma_cfg,
+ MCHP_DMA_QMSPI0_TX_REQ_ID,
+ (DMA_FLAG_M2D + DMA_FLAG_INCR_MEM));
+ dma_run(opdma->channel);
+ }
+
+ return did;
+}
+
+/*
+ * QMSPI0 Start
+ * flags
+ * b[0] = 1 de-assert chip select when done
+ * b[1] = 1 enable QMSPI interrupts
+ * b[2] = 1 start
+ */
+void qmspi_cfg_irq_start(uint8_t flags)
+{
+ MCHP_INT_DISABLE(MCHP_QMSPI_GIRQ) = MCHP_QMSPI_GIRQ_BIT;
+ MCHP_INT_SOURCE(MCHP_QMSPI_GIRQ) = MCHP_QMSPI_GIRQ_BIT;
+ MCHP_QMSPI0_IEN = 0;
+
+ if (flags & (1u << 1)) {
+ MCHP_QMSPI0_IEN = (MCHP_QMSPI_STS_DONE +
+ MCHP_QMSPI_STS_PROG_ERR);
+ MCHP_INT_ENABLE(MCHP_QMSPI_GIRQ) = MCHP_QMSPI_GIRQ_BIT;
+ }
+
+ if (flags & (1u << 2))
+ MCHP_QMSPI0_EXE = MCHP_QMSPI_EXE_START;
+}
+
+/*
+ * QMSPI transmit and/or receive
+ * np_flags
+ * b[7:0] = flags
+ * b[0] = close(de-assert chip select when done)
+ * b[1] = enable Done and ProgError interrupt
+ * b[2] = start
+ * b[15:8] = number of tx pins
+ * b[24:16] = number of rx pins
+ *
+ * returns last descriptor 0 <= index < MCHP_QMSPI_MAX_DESCR
+ * or error (bit[7]==1)
+ */
+uint8_t qmspi_xfr(const struct spi_device_t *spi_device,
+ uint32_t np_flags,
+ const uint8_t *txdata, uint32_t ntx,
+ uint8_t *rxdata, uint32_t nrx)
+{
+ uint32_t d, did, dma_cfg;
+ const struct dma_option *opdma;
+
+ qmspi_descr_mode_ready();
+
+ did = 0;
+ if (ntx) {
+ if (txdata == NULL)
+ return QMSPI_ERR_BAD_PTR;
+
+ opdma = spi_dma_option(spi_device, SPI_DMA_OPTION_WR);
+
+ d = qmspi_pins_encoding((np_flags >> 8) & 0xff);
+ dma_cfg = (np_flags & 0xFF00) + did;
+ did = qmspi_xmit_data_descr(opdma, dma_cfg, txdata, ntx);
+ if (did & QMSPI_ERR_ANY)
+ return (uint8_t)(did & 0xff);
+
+ if (nrx)
+ did++; /* point to next descriptor */
+ }
+
+ if (nrx) {
+ if (rxdata == NULL)
+ return QMSPI_ERR_BAD_PTR;
+
+ if (did >= MCHP_QMSPI_MAX_DESCR)
+ return QMSPI_ERR_OUT_OF_DESCR;
+
+ d = qmspi_pins_encoding((np_flags >> 16) & 0xff);
+ /* compute DMA units: 1 or 4 */
+ if (((uint32_t)rxdata | nrx) & 0x03) {
+ dma_cfg = 1;
+ d |= (MCHP_QMSPI_C_RX_EN + MCHP_QMSPI_C_RX_DMA_1B);
+ } else {
+ dma_cfg = 4;
+ d |= (MCHP_QMSPI_C_RX_EN + MCHP_QMSPI_C_RX_DMA_4B);
+ }
+ did = qmspi_descr_alloc(did, d, nrx);
+ if (did & QMSPI_ERR_ANY)
+ return (uint8_t)(did & 0xff);
+
+ opdma = spi_dma_option(spi_device, SPI_DMA_OPTION_RD);
+ dma_clr_chan(opdma->channel);
+ dma_cfg_buffers(opdma->channel, rxdata, nrx,
+ (void *)MCHP_QMSPI0_RX_FIFO_ADDR);
+ dma_cfg_xfr(opdma->channel, dma_cfg,
+ MCHP_DMA_QMSPI0_RX_REQ_ID,
+ (DMA_FLAG_D2M + DMA_FLAG_INCR_MEM));
+ dma_run(opdma->channel);
+ }
+
+ if (ntx || nrx) {
+ d = MCHP_QMSPI0_DESCR(did);
+ d |= MCHP_QMSPI_C_DESCR_LAST;
+ if (np_flags & 0x01)
+ d |= MCHP_QMSPI_C_CLOSE;
+ MCHP_QMSPI0_DESCR(did) = d;
+ qmspi_cfg_irq_start(np_flags & 0xFF);
+ }
+
+ return (uint8_t)(did & 0xFF);
+}
+#endif /* #ifdef CONFIG_MCHP_QMSPI_TX_DMA */
+
+/*
+ * QMSPI controller must control chip select therefore this routine
+ * configures QMSPI to assert SPI CS# and de-assert when done.
+ * Transmit using QMSPI TX FIFO only when tx data fits in TX FIFO else
+ * use TX DMA.
+ * Transmit and receive will allocate as many QMSPI descriptors as
+ * needed for data size. This could result in an error if the maximum
+ * number of descriptors is exceeded.
+ * Descriptors are limited to 0x7FFF units where unit size is 1, 4, or
+ * 16 bytes. Code determines unit size based upon number of bytes and
+ * alignment of data buffer.
+ * DMA channel will move data in units of 1 or 4 bytes also based upon
+ * the number of data bytes and buffer alignment.
+ * The most efficient transfers are those where TX and RX buffers are
+ * aligned >= 4 bytes and the number of bytes is a multiple of 4.
+ * NOTE on SPI flash commands:
+ * This routine does NOT handle SPI flash commands requiring
+ * dummy clocks or special mode bytes. Dummy clocks and special mode
+ * bytes require additional descriptors. For example the flash read
+ * dual command (0x3B):
+ * 1. First descriptor transmits 4 bytes (opcode + 24-bit address) on
+ * one pin (IO0).
+ * 2. Second descriptor set for 2 IO pins, 2 bytes, TX disabled. When
+ * this descriptor is executed QMSPI will tri-state IO0 & IO1 and
+ * output 8 clocks (dual mode 4 clocks per byte). The SPI flash may
+ * turn on its output drivers on the first dummy clock.
+ * 3. Third descriptor set for 2 IO pins, read data using DMA. Unit
+ * size and DMA unit size based on number of bytes to read and
+ * alignment of destination buffer.
+ * The common SPI API will be required to supply more information about
+ * SPI flash read commands. A further complication is some larger SPI
+ * flash devices support a 4-byte address mode. 4-byte address mode can
+ * be implemented as separate command code or a configuration bit in
+ * the SPI flash that changes the default 24-bit address command to
+ * require a 32-bit address.
+ * 0x03 is 1-1-1
+ * 0x3B is 1-1-2 with 8 dummy clocks
+ * 0x6B is 1-1-4 with 8 dummy clocks
+ * 0xBB is 1-2-2 with 4 dummy clocks
+ * Number of IO pins for command
+ * Number of IO pins for address
+ * Number of IO pins for data
+ * Number of bit/bytes for address (3 or 4)
+ * Number of dummy clocks after address phase
+ */
+#ifdef CONFIG_MCHP_QMSPI_TX_DMA
+int qmspi_transaction_async(const struct spi_device_t *spi_device,
+ const uint8_t *txdata, int txlen,
+ uint8_t *rxdata, int rxlen)
+{
+ uint32_t np_flags, ntx, nrx;
+ int ret;
+ uint8_t rc;
+
+ ntx = 0;
+ if (txlen >= 0)
+ ntx = (uint32_t)txlen;
+
+ nrx = 0;
+ if (rxlen >= 0)
+ nrx = (uint32_t)rxlen;
+
+ np_flags = 0x010105; /* b[0]=1 close on done, b[2]=1 start */
+ rc = qmspi_xfr(spi_device, np_flags,
+ txdata, ntx,
+ rxdata, nrx);
+
+ if (rc & QMSPI_ERR_ANY)
+ return EC_ERROR_INVAL;
+
+ ret = EC_SUCCESS;
+ return ret;
+}
+#else
+/*
+ * Transmit using CPU and QMSPI TX FIFO(no DMA).
+ * Receive using DMA as above.
+ */
+int qmspi_transaction_async(const struct spi_device_t *spi_device,
+ const uint8_t *txdata, int txlen,
+ uint8_t *rxdata, int rxlen)
+{
+ const struct dma_option *opdma;
+ uint32_t d, did, dmau;
+ uint64_t u;
+
+ if (spi_device == NULL)
+ return EC_ERROR_PARAM1;
+
+ /* soft reset the controller */
+ MCHP_QMSPI0_MODE_ACT_SRST = MCHP_QMSPI_M_SOFT_RESET;
+ d = spi_device->div;
+ d <<= MCHP_QMSPI_M_CLKDIV_BITPOS;
+ d += (MCHP_QMSPI_M_ACTIVATE + MCHP_QMSPI_M_SPI_MODE0);
+ MCHP_QMSPI0_MODE = d;
+ MCHP_QMSPI0_CTRL = MCHP_QMSPI_C_DESCR_MODE_EN;
+
+ d = did = 0;
+
+ if (txlen > 0) {
+ if (txdata == NULL)
+ return EC_ERROR_PARAM2;
+
+ d = qmspi_build_tx_descr((uint32_t)txlen, 1);
+ if (d == 0) /* txlen too large */
+ return EC_ERROR_OVERFLOW;
+
+ MCHP_QMSPI0_DESCR(did) = d;
+ }
+
+ if (rxlen > 0) {
+ if (rxdata == NULL)
+ return EC_ERROR_PARAM4;
+
+ u = qmspi_build_rx_descr((uint32_t)rxdata,
+ (uint32_t)rxlen, 2);
+
+ d = (uint32_t)u;
+ dmau = u >> 32;
+
+ if (txlen > 0)
+ did++;
+ MCHP_QMSPI0_DESCR(did) = d;
+
+ opdma = spi_dma_option(spi_device, SPI_DMA_OPTION_RD);
+ dma_xfr_start_rx(opdma, dmau, (uint32_t)rxlen, rxdata);
+ }
+
+ MCHP_QMSPI0_DESCR(did) |= (MCHP_QMSPI_C_CLOSE +
+ MCHP_QMSPI_C_DESCR_LAST);
+
+ MCHP_QMSPI0_EXE = MCHP_QMSPI_EXE_START;
+
+ while (txlen--) {
+ if (MCHP_QMSPI0_STS & MCHP_QMSPI_STS_TX_BUFF_FULL) {
+ if (qmspi_wait(MCHP_QMSPI_STS_TX_BUFF_EMPTY,
+ MCHP_QMSPI_STS_TX_BUFF_EMPTY) !=
+ EC_SUCCESS) {
+ MCHP_QMSPI0_EXE = MCHP_QMSPI_EXE_STOP;
+ return EC_ERROR_TIMEOUT;
+ }
+ } else
+ MCHP_QMSPI0_TX_FIFO8 = *txdata++;
+ }
+
+ return EC_SUCCESS;
+}
+#endif /* #ifdef CONFIG_MCHP_QMSPI_TX_DMA */
+
+/*
+ * Wait for QMSPI descriptor mode transfer to finish.
+ * QMSPI is configured to perform a complete transaction.
+ * Assert CS#
+ * optional transmit
+ * CPU keeps filling TX FIFO until all bytes are transmitted.
+ * optional receive
+ * QMSPI is configured to read rxlen bytes and uses a DMA channel
+ * to move data from its RX FIFO to memory.
+ * De-assert CS#
+ * This routine can be called with QMSPI hardware in four states:
+ * 1. Transmit only and QMSPI has finished (empty TX FIFO) by the time
+ * this routine is called. QMSPI.Status transfer done status will be
+ * set and QMSPI HW has de-asserted SPI CS#.
+ * 2. Transmit only and QMSPI TX FIFO is still transmitting.
+ * QMSPI transfer done status is not asserted and CS# is still
+ * asserted. QMSPI HW will de-assert CS# when done or firmware
+ * manually stops QMSPI.
+ * 3. Receive was enabled and DMA channel is moving data from
+ * QMSPI RX FIFO to memory. QMSPI.Status transfer done and DMA done
+ * status bits are not set. QMSPI SPI CS# will stay asserted until
+ * transaction finishes or firmware manually stops QMSPI.
+ * 4. Receive was enabled and DMA channel is finished. QMSPI RX FIFO
+ * should be empty and DMA channel is done. QMSPI.Status transfer
+ * done and DMA done status bits will be set. QMSPI HW has de-asserted
+ * SPI CS#.
+ * We are using QMSPI in descriptor mode. The definition of QMSPI.Status
+ * transfer complete bit in this mode is: complete will be set to 1 only
+ * when the last buffer completes its transfer.
+ * TX only sets complete when transfer unit count is matched and all units
+ * have been clocked out of the TX FIFO.
+ * RX DMA transfer complete will be set when the last transfer unit
+ * is out of the RX FIFO but DMA may not be complete until it finishes
+ * moving the transfer unit to memory.
+ * If TX only spin on QMSPI.Status Transfer_Complete bit.
+ * If RX used spin on QMsPI.Status Transfer_Complete and DMA_Complete.
+ * Search descriptors looking for RX DMA enabled.
+ * If RX DMA is enabled add DMA complete flag to status mask.
+ * Spin while QMSPI.Status & mask != mask or timeout.
+ * If timeout force QMSPI to stop and exit spin loop.
+ * if DMA was enabled disable DMA channel.
+ * Clear QMSPI.Status and FIFO's
+ */
+int qmspi_transaction_flush(const struct spi_device_t *spi_device)
+{
+ int ret;
+ uint32_t qsts, mask;
+ const struct dma_option *opdma;
+ timestamp_t deadline;
+
+ if (spi_device == NULL)
+ return EC_ERROR_PARAM1;
+
+ mask = MCHP_QMSPI_STS_DONE;
+
+ ret = EC_SUCCESS;
+ deadline.val = get_time().val + QMSPI_TRANSFER_TIMEOUT;
+
+ qsts = MCHP_QMSPI0_STS;
+ while ((qsts & mask) != mask) {
+ if (timestamp_expired(deadline, NULL)) {
+ MCHP_QMSPI0_EXE = MCHP_QMSPI_EXE_STOP;
+ ret = EC_ERROR_TIMEOUT;
+ break;
+ }
+ usleep(QMSPI_BYTE_TRANSFER_POLL_INTERVAL_US);
+ qsts = MCHP_QMSPI0_STS;
+ }
+
+ /* clear transmit DMA channel */
+ opdma = spi_dma_option(spi_device, SPI_DMA_OPTION_WR);
+ if (opdma == NULL)
+ return EC_ERROR_INVAL;
+
+ dma_disable(opdma->channel);
+ dma_clear_isr(opdma->channel);
+
+ /* clear receive DMA channel */
+ opdma = spi_dma_option(spi_device, SPI_DMA_OPTION_RD);
+ if (opdma == NULL)
+ return EC_ERROR_INVAL;
+
+ dma_disable(opdma->channel);
+ dma_clear_isr(opdma->channel);
+
+ /* clear QMSPI FIFO's */
+ MCHP_QMSPI0_EXE = MCHP_QMSPI_EXE_CLR_FIFOS;
+ MCHP_QMSPI0_STS = 0xffffffff;
+
+ return ret;
+}
+
+/**
+ * Enable QMSPI controller and MODULE_SPI_FLASH pins.
+ *
+ * @param hw_port b[3:0]=0 and b[7:4]=0
+ * @param enable
+ * @return EC_SUCCESS or EC_ERROR_INVAL if port is unrecognized
+ * @note called by spi_enable in mec1701/spi.c
+ *
+ */
+int qmspi_enable(int hw_port, int enable)
+{
+ uint8_t dummy __attribute__((unused)) = 0;
+
+ trace2(0, QMSPI, 0, "qmspi_enable: port = %d enable = %d",
+ hw_port, enable);
+
+ if (hw_port != QMSPI0_PORT)
+ return EC_ERROR_INVAL;
+
+ gpio_config_module(MODULE_SPI_FLASH, (enable > 0));
+
+ if (enable) {
+ MCHP_PCR_SLP_DIS_DEV(MCHP_PCR_QMSPI);
+ MCHP_QMSPI0_MODE_ACT_SRST = MCHP_QMSPI_M_SOFT_RESET;
+ dummy = MCHP_QMSPI0_MODE_ACT_SRST;
+ MCHP_QMSPI0_MODE = (MCHP_QMSPI_M_ACTIVATE +
+ MCHP_QMSPI_M_SPI_MODE0 +
+ MCHP_QMSPI_M_CLKDIV_12M);
+ } else {
+ MCHP_QMSPI0_MODE_ACT_SRST = MCHP_QMSPI_M_SOFT_RESET;
+ dummy = MCHP_QMSPI0_MODE_ACT_SRST;
+ MCHP_QMSPI0_MODE_ACT_SRST = 0;
+ MCHP_PCR_SLP_EN_DEV(MCHP_PCR_QMSPI);
+ }
+
+ return EC_SUCCESS;
+}
+
diff --git a/chip/mchp/qmspi_chip.h b/chip/mchp/qmspi_chip.h
new file mode 100644
index 0000000000..1a1d764267
--- /dev/null
+++ b/chip/mchp/qmspi_chip.h
@@ -0,0 +1,67 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Register map for MEC17xx processor
+ */
+/** @file qmspi_chip.h
+ *MEC17xx Quad SPI Master
+ */
+/** @defgroup MCHP MEC qmspi
+ */
+
+#ifndef _QMSPI_CHIP_H
+#define _QMSPI_CHIP_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+/* struct spi_device_t */
+#include "spi.h"
+
+
+int qmspi_transaction_flush(const struct spi_device_t *spi_device);
+
+int qmspi_transaction_wait(const struct spi_device_t *spi_device);
+
+int qmspi_transaction_sync(const struct spi_device_t *spi_device,
+ const uint8_t *txdata, int txlen,
+ uint8_t *rxdata, int rxlen);
+
+int qmspi_transaction_async(const struct spi_device_t *spi_device,
+ const uint8_t *txdata, int txlen,
+ uint8_t *rxdata, int rxlen);
+
+int qmspi_enable(int port, int enable);
+
+/*
+ * QMSPI0 Start
+ * flags
+ * b[0] = ignored
+ * b[1] = 1 enable QMSPI interrupts
+ * b[2] = 1 start
+ */
+void qmspi_cfg_irq_start(uint8_t flags);
+
+/*
+ * QMSPI transmit and/or receive
+ * np_flags
+ * b[7:0] = flags
+ * b[0] = close(de-assert chip select when done)
+ * b[1] = enable Done and ProgError interrupt
+ * b[2] = start
+ * b[15:8] = number of tx pins
+ * b[24:16] = number of rx pins
+ *
+ * returns last descriptor 0 <= index < MCHP_QMSPI_MAX_DESCR
+ * or error (bit[7]==1)
+ */
+uint8_t qmspi_xfr(const struct spi_device_t *spi_device,
+ uint32_t np_flags,
+ const uint8_t *txdata, uint32_t ntx,
+ uint8_t *rxdata, uint32_t nrx);
+
+#endif /* #ifndef _QMSPI_CHIP_H */
+/** @}
+ */
+
diff --git a/chip/mchp/spi.c b/chip/mchp/spi.c
new file mode 100644
index 0000000000..2d5a7e9a3f
--- /dev/null
+++ b/chip/mchp/spi.c
@@ -0,0 +1,297 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* QMSPI master module for MCHP MEC family */
+
+#include "common.h"
+#include "console.h"
+#include "dma.h"
+#include "gpio.h"
+#include "registers.h"
+#include "spi.h"
+#include "timer.h"
+#include "util.h"
+#include "hooks.h"
+#include "task.h"
+#include "spi_chip.h"
+#include "qmspi_chip.h"
+#if defined(CONFIG_MCHP_GPSPI) && !defined(LFW)
+#include "gpspi_chip.h"
+#endif
+#include "tfdp_chip.h"
+
+#define CPUTS(outstr) cputs(CC_SPI, outstr)
+#define CPRINTS(format, args...) cprints(CC_SPI, format, ## args)
+
+#define SPI_BYTE_TRANSFER_TIMEOUT_US (3 * MSEC)
+#define SPI_BYTE_TRANSFER_POLL_INTERVAL_US 100
+
+
+
+static const struct dma_option spi_rx_option[] = {
+ {
+ MCHP_DMAC_QMSPI0_RX,
+ (void *)(MCHP_QMSPI0_RX_FIFO_ADDR),
+ MCHP_DMA_XFER_SIZE(1) + MCHP_DMA_INC_MEM
+ },
+#if defined(CONFIG_MCHP_GPSPI) && !defined(LFW)
+#if CONFIG_MCHP_GPSPI & 0x01
+ {
+ MCHP_DMAC_SPI0_RX,
+ (void *)&MCHP_SPI_RD(0),
+ MCHP_DMA_XFER_SIZE(1) + MCHP_DMA_INC_MEM
+ },
+#endif
+#if CONFIG_MCHP_GPSPI & 0x02
+ {
+ MCHP_DMAC_SPI1_RX,
+ (void *)&MCHP_SPI_RD(1),
+ MCHP_DMA_XFER_SIZE(1) + MCHP_DMA_INC_MEM
+ },
+#endif
+#endif
+};
+
+static const struct dma_option spi_tx_option[] = {
+ {
+ MCHP_DMAC_QMSPI0_TX,
+ (void *)(MCHP_QMSPI0_TX_FIFO_ADDR),
+ MCHP_DMA_XFER_SIZE(1) + MCHP_DMA_INC_MEM
+ },
+#if defined(CONFIG_MCHP_GPSPI) && !defined(LFW)
+#if CONFIG_MCHP_GPSPI & 0x01
+ {
+ MCHP_DMAC_SPI0_TX,
+ (void *)&MCHP_SPI_TD(0),
+ MCHP_DMA_XFER_SIZE(1) + MCHP_DMA_INC_MEM
+ },
+#endif
+#if CONFIG_MCHP_GPSPI & 0x02
+ {
+ MCHP_DMAC_SPI1_TX,
+ (void *)&MCHP_SPI_TD(1),
+ MCHP_DMA_XFER_SIZE(1) + MCHP_DMA_INC_MEM
+ },
+#endif
+#endif
+};
+
+/* only regular image needs mutex, LFW does not have scheduling */
+#ifndef LFW
+static struct mutex spi_mutex[ARRAY_SIZE(spi_rx_option)];
+
+/*
+ * Acquire mutex for specified SPI controller/port.
+ * Note if mutex is owned by another task this routine
+ * will block until mutex is released.
+ */
+static void spi_mutex_lock(uint8_t hw_port)
+{
+ uint32_t n;
+
+ n = 0;
+#ifdef CONFIG_MCHP_GPSPI
+ if (hw_port & 0xF0) {
+#if (CONFIG_MCHP_GPSPI & 0x03) == 0x03
+ n = (hw_port & 0x0F) + 1;
+#else
+ n = 1;
+#endif
+ }
+#endif
+ mutex_lock(&spi_mutex[n]);
+}
+
+/*
+ * Release mutex for specified SPI controller/port.
+ */
+static void spi_mutex_unlock(uint8_t hw_port)
+{
+ uint32_t n;
+
+ n = 0;
+#ifdef CONFIG_MCHP_GPSPI
+ if (hw_port & 0xF0) {
+#if (CONFIG_MCHP_GPSPI & 0x03) == 0x03
+ n = (hw_port & 0x0F) + 1;
+#else
+ n = 1;
+#endif
+ }
+#endif
+ mutex_unlock(&spi_mutex[n]);
+}
+#endif /* #ifndef LFW */
+
+/*
+ * Public SPI interface
+ */
+
+const void *spi_dma_option(const struct spi_device_t *spi_device,
+ int is_tx)
+{
+ uint32_t n;
+
+ if (spi_device == NULL)
+ return NULL;
+
+ n = 0;
+#if defined(CONFIG_MCHP_GPSPI) && !defined(LFW)
+ if (spi_device->port & 0xF0) {
+#if (CONFIG_MCHP_GPSPI & 0x03) == 0x03
+ n = (spi_device->port & 0x0F) + 1;
+#else
+ n = 1;
+#endif
+ }
+#endif
+
+ if (is_tx)
+ return &spi_tx_option[n];
+ else
+ return &spi_rx_option[n];
+}
+
+int spi_transaction_async(const struct spi_device_t *spi_device,
+ const uint8_t *txdata, int txlen,
+ uint8_t *rxdata, int rxlen)
+{
+ int rc;
+
+ if (spi_device == NULL)
+ return EC_ERROR_INVAL;
+
+ switch (spi_device->port) {
+#if defined(CONFIG_MCHP_GPSPI) && !defined(LFW)
+ case GPSPI0_PORT:
+ case GPSPI1_PORT:
+ rc = gpspi_transaction_async(spi_device, txdata,
+ txlen, rxdata, rxlen);
+ break;
+#endif
+ case QMSPI0_PORT:
+ rc = qmspi_transaction_async(spi_device, txdata,
+ txlen, rxdata, rxlen);
+ break;
+ default:
+ rc = EC_ERROR_INVAL;
+ }
+
+ return rc;
+}
+
+int spi_transaction_flush(const struct spi_device_t *spi_device)
+{
+ int rc;
+
+ if (spi_device == NULL)
+ return EC_ERROR_INVAL;
+
+ switch (spi_device->port) {
+#if defined(CONFIG_MCHP_GPSPI) && !defined(LFW)
+ case GPSPI0_PORT:
+ case GPSPI1_PORT:
+ rc = gpspi_transaction_flush(spi_device);
+ break;
+#endif
+ case QMSPI0_PORT:
+ rc = qmspi_transaction_flush(spi_device);
+ break;
+ default:
+ rc = EC_ERROR_INVAL;
+ }
+
+ return rc;
+}
+
+/* Wait for async response received but do not de-assert chip select */
+int spi_transaction_wait(const struct spi_device_t *spi_device)
+{
+ int rc;
+
+ if (spi_device == NULL)
+ return EC_ERROR_INVAL;
+
+ switch (spi_device->port) {
+#if defined(CONFIG_MCHP_GPSPI) && !defined(LFW)
+#ifndef LFW
+ case GPSPI0_PORT:
+ case GPSPI1_PORT:
+ rc = gpspi_transaction_wait(spi_device);
+ break;
+#endif
+#endif
+ case QMSPI0_PORT:
+ rc = qmspi_transaction_wait(spi_device);
+ break;
+ default:
+ rc = EC_ERROR_INVAL;
+ }
+
+ return rc;
+}
+
+/*
+ * called from common/spi_flash.c
+ * For tranfers reading less than the size of QMSPI RX FIFO call
+ * a routine where reads use FIFO only no DMA.
+ * GP-SPI only has a one byte RX FIFO but small data transfers will be OK
+ * without the overhead of DMA setup.
+ */
+int spi_transaction(const struct spi_device_t *spi_device,
+ const uint8_t *txdata, int txlen,
+ uint8_t *rxdata, int rxlen)
+{
+ int rc;
+
+ if (spi_device == NULL)
+ return EC_ERROR_PARAM1;
+
+#ifndef LFW
+ spi_mutex_lock(spi_device->port);
+#endif
+
+ rc = spi_transaction_async(spi_device, txdata, txlen, rxdata, rxlen);
+ if (rc == EC_SUCCESS)
+ rc = spi_transaction_flush(spi_device);
+
+#ifndef LFW
+ spi_mutex_unlock(spi_device->port);
+#endif
+
+ return rc;
+}
+
+/**
+ * Enable SPI port and associated controller
+ *
+ * @param port Zero based index into spi_device an array of
+ * struct spi_device_t
+ * @param enable
+ * @return EC_SUCCESS or EC_ERROR_INVAL if port is unrecognized
+ * @note called from common/spi_flash.c
+ *
+ * spi_devices[].port is defined as
+ * bits[3:0] = controller instance
+ * bits[7:4] = controller family 0 = QMSPI, 1 = GPSPI
+ */
+int spi_enable(int port, int enable)
+{
+ int rc;
+ uint8_t hw_port;
+
+ rc = EC_ERROR_INVAL;
+ if (port < spi_devices_used) {
+ hw_port = spi_devices[port].port;
+ if ((hw_port & 0xF0) == QMSPI_CLASS)
+ rc = qmspi_enable(hw_port, enable);
+#if defined(CONFIG_MCHP_GPSPI) && !defined(LFW)
+ if ((hw_port & 0xF0) == GPSPI_CLASS)
+ rc = gpspi_enable(hw_port, enable);
+#endif
+ }
+
+ return rc;
+}
diff --git a/chip/mchp/spi_chip.h b/chip/mchp/spi_chip.h
new file mode 100644
index 0000000000..7d273e5155
--- /dev/null
+++ b/chip/mchp/spi_chip.h
@@ -0,0 +1,60 @@
+/* Copyright 2017 The Chromium OS Authors. All rights reserved
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Register map for MCHP MEC processor
+ */
+/** @file qmpis_chip.h
+ *MCHP MEC Quad SPI Master
+ */
+/** @defgroup MCHP MEC qmspi
+ */
+
+#ifndef _SPI_CHIP_H
+#define _SPI_CHIP_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+/* struct spi_device_t */
+#include "spi.h"
+
+#define SPI_DMA_OPTION_RD 0
+#define SPI_DMA_OPTION_WR 1
+
+/*
+ * bits[3:0] = controller instance
+ * bits[7:4] = controller family
+ * 0 = QMSPI, 1 = GPSPI
+ */
+#define QMSPI0_PORT 0x00
+#define GPSPI0_PORT 0x10
+#define GPSPI1_PORT 0x11
+
+
+#define QMSPI_CLASS0 0
+#define GPSPI_CLASS0 1
+
+#define QMSPI_CLASS (0 << 4)
+#define GPSPI_CLASS (1 << 4)
+
+#define QMSPI_CTRL0 0
+#define GPSPI_CTRL0 0
+#define GPSPI_CTRL1 1
+
+/*
+ * Encode zero based controller class and instance values
+ * in port value of spi_device_t.
+ */
+#define SPI_CTRL_ID(c, i) (((c & 0xf) << 4) + (i & 0xf))
+
+/*
+ * helper to return pointer to QMSPI or GPSPI struct dma_option
+ */
+const void *spi_dma_option(const struct spi_device_t *spi_device,
+ int is_tx);
+
+#endif /* #ifndef _QMSPI_CHIP_H */
+/** @}
+ */
+