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