From ae15dc8ce512d58c7c7757ebfd12c3ca76eb4a08 Mon Sep 17 00:00:00 2001 From: Vic Yang Date: Thu, 19 Jun 2014 14:00:32 -0700 Subject: [PATCH] mec1322: Add SPI master driver This allows us to use the two SPI ports as SPI master. Also, to save CPU time on reading large amount of data, let's add an async interface for SPI transaction. BUG=chrome-os-partner:29805 TEST=Read manufacturer ID from SPI flash with sync/async interface BRANCH=None Change-Id: I427f4215602cccc55c4151f4116226b1e0ccc15e Signed-off-by: Vic Yang Reviewed-on: https://chromium-review.googlesource.com/204719 --- board/mec1322_evb/board.c | 2 + board/mec1322_evb/board.h | 2 + board/mec1322_evb/gpio.inc | 3 + chip/mec1322/build.mk | 1 + chip/mec1322/config_chip.h | 1 + chip/mec1322/registers.h | 11 ++++ chip/mec1322/spi.c | 127 +++++++++++++++++++++++++++++++++++++ include/spi.h | 10 +++ 8 files changed, 157 insertions(+) create mode 100644 chip/mec1322/spi.c diff --git a/board/mec1322_evb/board.c b/board/mec1322_evb/board.c index 877743a97b..38ff53f332 100644 --- a/board/mec1322_evb/board.c +++ b/board/mec1322_evb/board.c @@ -35,6 +35,8 @@ const struct gpio_alt_func gpio_alt_funcs[] = { {GPIO_PORT(12), 0x60, 2, MODULE_KEYBOARD_SCAN, GPIO_KB_INPUT}, {GPIO_PORT(14), 0x14, 3, MODULE_KEYBOARD_SCAN, GPIO_KB_INPUT}, {GPIO_PORT(2), 0x20, 2, MODULE_LPC}, + {GPIO_PORT(12), 0x14, 1, MODULE_SPI}, + {GPIO_PORT(6), 0x10, 1, MODULE_SPI}, }; const int gpio_alt_funcs_count = ARRAY_SIZE(gpio_alt_funcs); diff --git a/board/mec1322_evb/board.h b/board/mec1322_evb/board.h index 7462706cab..eccd28a76e 100644 --- a/board/mec1322_evb/board.h +++ b/board/mec1322_evb/board.h @@ -14,6 +14,8 @@ #define CONFIG_FANS 1 #define CONFIG_ADC #define CONFIG_WAKE_PIN GPIO_S1 +#define CONFIG_SPI_PORT 0 +#define CONFIG_SPI_CS_GPIO GPIO_SHD_CS0 /* Modules we want to exclude */ #undef CONFIG_EEPROM diff --git a/board/mec1322_evb/gpio.inc b/board/mec1322_evb/gpio.inc index 60e2ccf908..b008e90a5c 100644 --- a/board/mec1322_evb/gpio.inc +++ b/board/mec1322_evb/gpio.inc @@ -14,6 +14,9 @@ GPIO(PCH_WAKE_L, PORT(20), 0, GPIO_ODR_HIGH, NULL) /* PCH wake pin */ /* Switch S1 */ GPIO(S1, PORT(6), 3, GPIO_INT_FALLING | GPIO_PULL_UP, NULL) +/* Shared SPI CS */ +GPIO(SHD_CS0, PORT(15), 0, GPIO_ODR_HIGH, NULL) + /* * Signals which aren't implemented on MEC1322 eval board but we'll * emulate anyway, to make it more convenient to debug other code. diff --git a/chip/mec1322/build.mk b/chip/mec1322/build.mk index 33338bf9e1..7b1444dc6e 100644 --- a/chip/mec1322/build.mk +++ b/chip/mec1322/build.mk @@ -21,3 +21,4 @@ chip-$(CONFIG_PWM)+=pwm.o chip-$(CONFIG_WATCHDOG)+=watchdog.o chip-$(HAS_TASK_KEYSCAN)+=keyboard_raw.o chip-$(CONFIG_DMA)+=dma.o +chip-$(CONFIG_SPI)+=spi.o diff --git a/chip/mec1322/config_chip.h b/chip/mec1322/config_chip.h index 20c13c7633..6de25fac3a 100644 --- a/chip/mec1322/config_chip.h +++ b/chip/mec1322/config_chip.h @@ -89,6 +89,7 @@ #define CONFIG_I2C #define CONFIG_LPC #define CONFIG_FPU +#define CONFIG_SPI #define CONFIG_DMA #undef CONFIG_FLASH diff --git a/chip/mec1322/registers.h b/chip/mec1322/registers.h index 8447816f78..2ed60c9db1 100644 --- a/chip/mec1322/registers.h +++ b/chip/mec1322/registers.h @@ -297,6 +297,17 @@ static inline uintptr_t gpio_port_base(int port_id) #define MEC1322_HTIMER_COUNT REG16(MEC1322_HTIMER_BASE + 0x8) +/* SPI */ +#define MEC1322_SPI_BASE(port) (0x40009400 + 0x80 * (port)) +#define MEC1322_SPI_AR(port) REG8(MEC1322_SPI_BASE(port) + 0x00) +#define MEC1322_SPI_CR(port) REG8(MEC1322_SPI_BASE(port) + 0x04) +#define MEC1322_SPI_SR(port) REG8(MEC1322_SPI_BASE(port) + 0x08) +#define MEC1322_SPI_TD(port) REG8(MEC1322_SPI_BASE(port) + 0x0c) +#define MEC1322_SPI_RD(port) REG8(MEC1322_SPI_BASE(port) + 0x10) +#define MEC1322_SPI_CC(port) REG8(MEC1322_SPI_BASE(port) + 0x14) +#define MEC1322_SPI_CG(port) REG8(MEC1322_SPI_BASE(port) + 0x18) + + /* DMA */ #define MEC1322_DMA_BASE 0x40002400 diff --git a/chip/mec1322/spi.c b/chip/mec1322/spi.c new file mode 100644 index 0000000000..713b88744a --- /dev/null +++ b/chip/mec1322/spi.c @@ -0,0 +1,127 @@ +/* Copyright (c) 2014 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. + */ + +/* SPI master module for MEC1322 */ + +#include "common.h" +#include "console.h" +#include "dma.h" +#include "gpio.h" +#include "registers.h" +#include "spi.h" +#include "timer.h" +#include "util.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 + +#define SPI_DMA_CHANNEL (MEC1322_DMAC_SPI0_RX + CONFIG_SPI_PORT * 2) + +static const struct dma_option spi_rx_option = { + SPI_DMA_CHANNEL, (void *)&MEC1322_SPI_RD(0), + MEC1322_DMA_XFER_SIZE(1) +}; + +static int wait_byte(void) +{ + timestamp_t deadline; + + deadline.val = get_time().val + SPI_BYTE_TRANSFER_TIMEOUT_US; + while ((MEC1322_SPI_SR(CONFIG_SPI_PORT) & 0x3) != 0x3) { + if (timestamp_expired(deadline, NULL)) + return EC_ERROR_TIMEOUT; + usleep(SPI_BYTE_TRANSFER_POLL_INTERVAL_US); + } + return EC_SUCCESS; +} + +static int spi_tx(const uint8_t *txdata, int txlen) +{ + int i; + int ret = EC_SUCCESS; + uint8_t dummy __attribute__((unused)) = 0; + + for (i = 0; i < txlen; ++i) { + MEC1322_SPI_TD(CONFIG_SPI_PORT) = txdata[i]; + ret = wait_byte(); + if (ret != EC_SUCCESS) + return ret; + dummy = MEC1322_SPI_RD(CONFIG_SPI_PORT); + } + + return ret; +} + +int spi_transaction_async(const uint8_t *txdata, int txlen, + uint8_t *rxdata, int rxlen) +{ + int ret = EC_SUCCESS; + + gpio_set_level(CONFIG_SPI_CS_GPIO, 0); + + /* Disable auto read */ + MEC1322_SPI_CR(CONFIG_SPI_PORT) &= ~(1 << 5); + + ret = spi_tx(txdata, txlen); + if (ret != EC_SUCCESS) + return ret; + + /* Enable auto read */ + MEC1322_SPI_CR(CONFIG_SPI_PORT) |= 1 << 5; + + dma_start_rx(&spi_rx_option, rxlen, rxdata); + MEC1322_SPI_TD(CONFIG_SPI_PORT) = 0; + + return ret; +} + +int spi_transaction_flush(void) +{ + int ret = dma_wait(SPI_DMA_CHANNEL); + + gpio_set_level(CONFIG_SPI_CS_GPIO, 1); + + return ret; +} + +int spi_transaction(const uint8_t *txdata, int txlen, + uint8_t *rxdata, int rxlen) +{ + int ret; + + ret = spi_transaction_async(txdata, txlen, rxdata, rxlen); + if (ret) + return ret; + return spi_transaction_flush(); +} + +int spi_enable(int enable) +{ + if (enable) { + gpio_config_module(MODULE_SPI, 1); + + /* Set enable bit in SPI_AR */ + MEC1322_SPI_AR(CONFIG_SPI_PORT) |= 0x1; + + /* Set SPDIN to 0 -> Full duplex */ + MEC1322_SPI_CR(CONFIG_SPI_PORT) &= ~(0x3 << 2); + + /* Set CLKPOL, TCLKPH, RCLKPH to 0 */ + MEC1322_SPI_CC(CONFIG_SPI_PORT) &= ~0x7; + + /* Set LSBF to 0 -> MSB first */ + MEC1322_SPI_CR(CONFIG_SPI_PORT) &= ~0x1; + } else { + /* Clear enable bit in SPI_AR */ + MEC1322_SPI_AR(CONFIG_SPI_PORT) &= ~0x1; + + gpio_config_module(MODULE_SPI, 0); + } + + return EC_SUCCESS; +} diff --git a/include/spi.h b/include/spi.h index a03e04f134..f8f916213b 100644 --- a/include/spi.h +++ b/include/spi.h @@ -19,6 +19,16 @@ int spi_enable(int enable); int spi_transaction(const uint8_t *txdata, int txlen, uint8_t *rxdata, int rxlen); +/* Similar to spi_transaction(), but hands over to DMA for reading response. + * Must call spi_transaction_flush() after this to make sure the response is + * received. + */ +int spi_transaction_async(const uint8_t *txdata, int txlen, + uint8_t *rxdata, int rxlen); + +/* Wait for async response received */ +int spi_transaction_flush(void); + #ifdef CONFIG_SPI /** * Called when the NSS level changes, signalling the start or end of a SPI