summaryrefslogtreecommitdiff
path: root/chip/mchp/gpspi.c
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/mchp/gpspi.c
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/mchp/gpspi.c')
-rw-r--r--chip/mchp/gpspi.c267
1 files changed, 267 insertions, 0 deletions
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;
+}
+