coreboot-libre-fam15h-rdimm/3rdparty/chromeec/chip/mchp/spi.c

298 lines
5.9 KiB
C

/* 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;
}