[coreboot-gerrit] New patch to review for coreboot: 94dc2a2 IMGTEC SPI controller driver

Patrick Georgi (pgeorgi@google.com) gerrit at coreboot.org
Thu Mar 26 14:45:24 CET 2015


Patrick Georgi (pgeorgi at google.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/9049

-gerrit

commit 94dc2a25966654ede5a3793ba936960bf3f13506
Author: Ionela Voinescu <ionela.voinescu at imgtec.com>
Date:   Tue Sep 9 20:18:55 2014 +0100

    IMGTEC SPI controller driver
    
    The Serial Peripheral Flash Interface (SPFI) block allows
    communication with various devices over the SPI bus.
    
    It uses a configurable transaction interface and it clocks
    the bus according to the configured command, address, gap (aka
    dummy) and data lengths.
    
    This controller requires the SPI_ATOMIC_SEQUENCING flag set
    (write and read done in the same transaction) as it cannot
    directly control CS and will assert/de-assert CS at the
    beginning/end of a transaction itself.
    
    Note that the size of any transfer cannot be greater than
    64KB - 1, as this is configured in a 16-bit field.
    
    The SOC has 2 SPFI interfaces each of them providing 5 slave select
    lines. SPFI 0 supports single and dual modes, SPFI 1 supports
    single, dual and quad modes.
    
    For SPFI interface 0:
     - The block needs the system PLL and the following top level
       SPI clock registers to be set:
       - CR_cr_top_spi0clkinternal_CTRL[2:0] with division value
       - CR_MIPS_CLOCK_GATE[19]: bit cr_top_SPI0CLKOUT_MIPS set
       - CR_cr_top_SPI0CLKOUT_CTRL[6:0] with division value
     - The following MFIO configuration paramters are also required:
       Signal name		Pad name        MFIO mode
       spim0_d0_txd		MFIO_MIPS_10	0
       spim0_d1_rxd		MFIO_MIPS_9	0
       spim0_mclk		MFIO_MIPS_8	0
       spim0_cs0		MFIO_MIPS_2	1
       spim0_cs1		MFIO_MIPS_1	1
       spim0_cs2		MFIO_MIPS_55	1
    			MFIO_MIPS_28	1
       spim0_cs3		MFIO_MIPS_56	1
    			MFIO_MIPS_29	1
       spim0_cs4		MFIO_MIPS_57	1
    			MFIO_MIMPS_30	1
    
    For SPFI interface 1:
     - The block needs the system PLL and the following top level
       SPI clock registers to be set:
       - CR_cr_top_spi1clkinternal_CTRL[2:0] with division value
       - CR_MIPS_CLOCK_GATE[20]: bit cr_top_SPI1CLKOUT_MIPS set
       - CR_cr_top_SPI1CLKOUT_CTRL[6:0] with division value
     - The following MFIO configuration paramters are also required:
       Signal name		Pad name	MFIO mode
       spim1_d0_txd		MFIO_MIPS_5	0
       spim1_d1_rxd		MFIO_MIPS_4	0
       spim1_mclk		MFIO_MIPS_3	0
       spim1_d2		MFIO_MIPS_6	0
       spim1_d3		MFIO_MIPS_7	0
       spim1_cs0		MFIO_MIPS_0	0
       spim1_cs1		MFIO_MIPS_1	0
       			MFIO_MIPS_58	1
       spim1_cs2		MFIO_MIPS_2	0
       			MFIO_MIPS_55	2
       			MFIO_MIPS_31	1
       spim1_cs3		MFIO_MIPS_56	2
       spim1_cs4		MFIO_MIPS_57	2
    
    BUG=chrome-os-partner:31438, chrome-os-partner:32441
    TEST=Tested as bare-metal driver on Pistachio FPGA
    
    Change-Id: I3b3e4475976e6fba58cef93b12d997ec5cb26341
    Signed-off-by: Patrick Georgi <pgeorgi at chromium.org>
    Original-Commit-Id: 621849942e27f7d6cf2c8ade7f2c4d18d2318b91
    Original-Change-Id: Ib257eb6236bd2895281175871b4ab979660f1239
    Original-Signed-off-by: Ionela Voinescu <ionela.voinescu at imgtec.com>
    Original-Reviewed-on: https://chromium-review.googlesource.com/217320
    Original-Reviewed-by: Vadim Bendebury <vbendeb at chromium.org>
---
 src/soc/imgtec/pistachio/Kconfig      |   1 +
 src/soc/imgtec/pistachio/Makefile.inc |   3 +-
 src/soc/imgtec/pistachio/cpu.h        |  24 ++
 src/soc/imgtec/pistachio/spi.c        | 524 +++++++++++++++++++++++++++++++++-
 src/soc/imgtec/pistachio/spi.h        | 358 +++++++++++++++++++++++
 5 files changed, 899 insertions(+), 11 deletions(-)

diff --git a/src/soc/imgtec/pistachio/Kconfig b/src/soc/imgtec/pistachio/Kconfig
index 1acfe39..83408bd 100644
--- a/src/soc/imgtec/pistachio/Kconfig
+++ b/src/soc/imgtec/pistachio/Kconfig
@@ -26,6 +26,7 @@ config CPU_IMGTEC_PISTACHIO
 	select HAVE_MONOTONIC_TIMER
 	select HAVE_UART_MEMORY_MAPPED
 	select HAVE_UART_SPECIAL
+	select SPI_ATOMIC_SEQUENCING
 	bool
 
 if CPU_IMGTEC_PISTACHIO
diff --git a/src/soc/imgtec/pistachio/Makefile.inc b/src/soc/imgtec/pistachio/Makefile.inc
index 2660848..e695abd 100644
--- a/src/soc/imgtec/pistachio/Makefile.inc
+++ b/src/soc/imgtec/pistachio/Makefile.inc
@@ -19,7 +19,7 @@
 # MA 02110-1301 USA
 #
 
-# We enable CBFS_SPI_WRAPPER for Danuibe targets.
+# We enable CBFS_SPI_WRAPPER for Pistachio targets.
 bootblock-y += spi.c
 romstage-y += spi.c
 ramstage-y += spi.c
@@ -39,6 +39,7 @@ ramstage-y += timestamp.c
 
 romstage-y += cbmem.c
 romstage-y += romstage.c
+romstage-y += monotonic_timer.c
 romstage-y += timestamp.c
 
 # Generate the actual coreboot bootblock code
diff --git a/src/soc/imgtec/pistachio/cpu.h b/src/soc/imgtec/pistachio/cpu.h
new file mode 100644
index 0000000..c61c58d
--- /dev/null
+++ b/src/soc/imgtec/pistachio/cpu.h
@@ -0,0 +1,24 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2014 Imagination Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of
+ * the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __SOC_IMGTEC_DANUBE_CPU_H__
+#define __SOC_IMGTEC_DANUBE_CPU_H__
+
+#define IMG_SPIM0_BASE_ADDRESS	0xB8100F00
+#define IMG_SPIM1_BASE_ADDRESS	0xB8101000
+
+#endif
diff --git a/src/soc/imgtec/pistachio/spi.c b/src/soc/imgtec/pistachio/spi.c
index 95bf827..85ee0d7 100644
--- a/src/soc/imgtec/pistachio/spi.c
+++ b/src/soc/imgtec/pistachio/spi.c
@@ -1,9 +1,11 @@
 /*
- * Copyright (C) 2014 Google, Inc.
+ * This file is part of the coreboot project.
  *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
+ * Copyright (C) 2014 Imagination Technologies
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -11,25 +13,527 @@
  * GNU General Public License for more details.
  */
 
-#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <timer.h>
 #include <spi-generic.h>
+#include <spi_flash.h>
+#include "cpu.h"
+#include "spi.h"
+
+#if !CONFIG_SPI_ATOMIC_SEQUENCING
+#error "Unsupported SPI driver API"
+#endif
+
+struct img_spi_slave {
+	struct spi_slave slave;
+	/* SPIM instance device parameters */
+	struct spim_device_parameters device_parameters;
+	/* SPIM instance base address */
+	u32 base;
+	/* Boolean property that is TRUE if API has been initialised */
+	int initialised;
+};
+
+/* Allocate memory for the maximum number of devices */
+static struct
+img_spi_slave img_spi_slaves[SPIM_NUM_BLOCKS*SPIM_NUM_PORTS_PER_BLOCK];
+
+/*
+ * Wait for the bit at the shift position to be set in reg
+ * If the bit is not set in SPI_TIMEOUT_VALUE_US return with error
+ */
+static int wait_status(u32 reg, u32 shift)
+{
+	struct stopwatch sw;
+
+	stopwatch_init_usecs_expire(&sw, SPI_TIMEOUT_VALUE_US);
+	while (!(read32(reg) & (1 << shift))) {
+		if (stopwatch_expired(&sw))
+			return -SPIM_TIMEOUT;
+	}
+	return SPIM_OK;
+}
+
+/* Transmitter function. Fills TX FIFO with data before enabling SPIM */
+static int transmitdata(struct spi_slave *slave, u8 *buffer, u32 size)
+{
+	u32 blocksize, base, write_data;
+	int ret;
+
+	base = container_of(slave, struct img_spi_slave, slave)->base;
+	while (size) {
+		/* Wait until FIFO empty */
+		write32(base + SPFI_INT_CLEAR_REG_OFFSET, SPFI_SDE_MASK);
+		ret = wait_status(base + SPFI_INT_STATUS_REG_OFFSET,
+					SPFI_SDE_SHIFT);
+		if (ret)
+			return ret;
+
+		/*
+		 * Write to FIFO in blocks of 16 words (64 bytes)
+		 * Do 32bit writes first.
+		 */
+		blocksize = SPIM_MAX_BLOCK_BYTES;
+		while ((size >= sizeof(u32)) && blocksize) {
+			memcpy(&write_data, buffer, sizeof(u32));
+			write32(base + SPFI_SEND_LONG_REG_OFFSET, write_data);
+			buffer += sizeof(u32);
+			size -= sizeof(u32);
+			blocksize -= sizeof(u32);
+		}
+		while (size && blocksize) {
+			write32(base + SPFI_SEND_BYTE_REG_OFFSET, *buffer);
+			buffer++;
+			size--;
+			blocksize--;
+		}
+	}
+	return SPIM_OK;
+}
+
+/* Receiver function */
+static int receivedata(struct spi_slave *slave, u8 *buffer, u32 size)
+{
+	u32 read_data, base;
+	int ret;
+
+	base = container_of(slave, struct img_spi_slave, slave)->base;
+	/*
+	 * Do 32bit reads first. Clear status GDEX32BIT here so that the first
+	 * status reg. read gets the actual bit state
+	 */
+	write32(base + SPFI_INT_CLEAR_REG_OFFSET, SPFI_GDEX32BIT_MASK);
+	while (size >= sizeof(u32)) {
+		ret = wait_status(base + SPFI_INT_STATUS_REG_OFFSET,
+					SPFI_GDEX32BIT_SHIFT);
+		if (ret)
+			return ret;
+		read_data = read32(base + SPFI_GET_LONG_REG_OFFSET);
+		memcpy(buffer, &read_data, sizeof(u32));
+		buffer += sizeof(u32);
+		size -= sizeof(u32);
+		/* Clear interrupt status on GDEX32BITL */
+		write32(base + SPFI_INT_CLEAR_REG_OFFSET, SPFI_GDEX32BIT_MASK);
+	}
+
+	/*
+	 * Do the remaining 8bit reads. Clear status GDEX8BIT here so that
+	 * the first status reg. read gets the actual bit state
+	 */
+	write32(base + SPFI_INT_CLEAR_REG_OFFSET, SPFI_GDEX8BIT_MASK);
+	while (size) {
+		ret = wait_status(base + SPFI_INT_STATUS_REG_OFFSET,
+					SPFI_GDEX8BIT_SHIFT);
+		if (ret)
+			return ret;
+		*buffer = read32(base + SPFI_GET_BYTE_REG_OFFSET);
+		buffer++;
+		size--;
+		/* Clear interrupt status on SPFI_GDEX8BIT */
+		write32(base + SPFI_INT_CLEAR_REG_OFFSET, SPFI_GDEX8BIT_MASK);
+	}
+	return SPIM_OK;
+}
+
+/* Sets port parameters in port state register. */
+static void  setparams(struct spi_slave *slave, u32 port,
+			struct spim_device_parameters *params)
+{
+	u32 spim_parameters, port_state, base;
+
+	spim_parameters = 0;
+
+	base = container_of(slave, struct img_spi_slave, slave)->base;
+	port_state = read32(base + SPFI_PORT_STATE_REG_OFFSET);
+	port_state &= ~((SPIM_PORT0_MASK>>port)|SPFI_PORT_SELECT_MASK);
+	port_state |= params->cs_idle_level<<(SPIM_CS0_IDLE_SHIFT-port);
+	port_state |=
+		params->data_idle_level<<(SPIM_DATA0_IDLE_SHIFT-port);
+
+	/* Clock idle level and phase */
+	switch (params->spi_mode) {
+	case SPIM_MODE_0:
+		break;
+	case SPIM_MODE_1:
+		port_state |= (1 << (SPIM_CLOCK0_PHASE_SHIFT - port));
+		break;
+	case SPIM_MODE_2:
+		port_state |= (1 << (SPIM_CLOCK0_IDLE_SHIFT - port));
+		break;
+	case SPIM_MODE_3:
+		port_state |= (1 << (SPIM_CLOCK0_IDLE_SHIFT - port)) |
+				 (1 << (SPIM_CLOCK0_PHASE_SHIFT - port));
+		break;
+	}
+	/* Set port state register */
+	write32(base + SPFI_PORT_STATE_REG_OFFSET, port_state);
+
+	/* Set up values to be written to device parameter register */
+	spim_parameters |= params->bitrate << SPIM_CLK_DIVIDE_SHIFT;
+	spim_parameters |= params->cs_setup << SPIM_CS_SETUP_SHIFT;
+	spim_parameters |= params->cs_hold << SPIM_CS_HOLD_SHIFT;
+	spim_parameters |= params->cs_delay << SPIM_CS_DELAY_SHIFT;
+
+	write32(base + SPFI_PORT_0_PARAM_REG_OFFSET + 4 * port,
+			spim_parameters);
+}
+
+/* Sets up transaction register */
+static u32 transaction_reg_setup(struct spim_buffer *first,
+					struct spim_buffer *second)
+{
+	u32 reg = 0;
+
+	/* 2nd transfer exists? */
+	if (second) {
+		/*
+		 * If second transfer exists, it's a "command followed by data"
+		 * type of transfer and first transfer is defined by
+		 * CMD_LENGTH, ADDR_LENGTH, DUMMY_LENGTH... fields of
+		 * transaction register
+		 */
+		reg = spi_write_reg_field(reg, SPFI_CMD_LENGTH, 1);
+		reg = spi_write_reg_field(reg, SPFI_ADDR_LENGTH,
+						first->size - 1);
+		reg = spi_write_reg_field(reg, SPFI_DUMMY_LENGTH, 0);
+		/* Set data size (size of the second transfer) */
+		reg = spi_write_reg_field(reg, SPFI_TSIZE, second->size);
+	} else {
+		/* Set data size, in this case size of the 1st transfer */
+		reg = spi_write_reg_field(reg, SPFI_TSIZE, first->size);
+	}
+	return reg;
+}
+
+/* Sets up control register */
+static u32 control_reg_setup(struct spim_buffer *first,
+				struct spim_buffer *second)
+{
+	u32 reg;
+
+	/* Enable SPFI */
+	reg = SPFI_EN_MASK;
+	reg |= first->inter_byte_delay ? SPIM_BYTE_DELAY_MASK : 0;
 
+	/* Set up the transfer mode */
+	reg = spi_write_reg_field(reg, SPFI_TRNSFR_MODE_DQ, SPIM_CMD_MODE_0);
+	reg = spi_write_reg_field(reg, SPFI_TRNSFR_MODE, SPIM_DMODE_SINGLE);
+
+	if (second) {
+		/* Set TX bit if the 2nd transaction is 'send' */
+		reg = spi_write_reg_field(reg, SPFI_TX_RX,
+						second->isread ? 0 : 1);
+		/*
+		 * Set send/get DMA for both transactions
+		 * (first is always 'send')
+		 */
+		reg = spi_write_reg_field(reg, SPIM_SEND_DMA, 1);
+		if (second->isread)
+			reg = spi_write_reg_field(reg, SPIM_GET_DMA, 1);
+
+	} else {
+		/* Set TX bit if the 1st transaction is 'send' */
+		reg |= first->isread ? 0 : SPFI_TX_RX_MASK;
+		/* Set send/get DMA */
+		reg |= first->isread ? SPIM_GET_DMA_MASK : SPIM_SEND_DMA_MASK;
+	}
+	return reg;
+}
+
+/* Checks the given buffer information */
+static int check_buffers(struct spi_slave *slave, struct spim_buffer *first,
+				struct spim_buffer *second){
+
+	if (!(container_of(slave, struct img_spi_slave, slave)->initialised))
+		return -SPIM_API_NOT_INITIALISED;
+	/*
+	 * First operation must always be defined
+	 * It can be either a read or a write and its size cannot be bigge
+	 * than SPIM_MAX_TANSFER_BYTES = 64KB - 1 (0xFFFF)
+	 */
+	if (!first)
+		return -SPIM_INVALID_READ_WRITE;
+	if (first->size > SPIM_MAX_TRANSFER_BYTES)
+		return -SPIM_INVALID_SIZE;
+	if (first->isread > 1)
+		return -SPIM_INVALID_READ_WRITE;
+	/* Check operation parameters for 'second' */
+	if (second) {
+		/*
+		 * If the second operation is defined it must be a read
+		 * operation and its size must not be bigger than
+		 * SPIM_MAX_TANSFER_BYTES = 64KB - 1 (0xFFFF)
+		 */
+		if (second->size > SPIM_MAX_TRANSFER_BYTES)
+			return -SPIM_INVALID_SIZE;
+		if (!second->isread)
+			return -SPIM_INVALID_READ_WRITE;
+		/*
+		 * If the second operations is defined, the first operation
+		 * must be a write and its size cannot be bigger than
+		 * SPIM_MAX_FLASH_COMMAND_BYTES(8): command size (1) +
+		 * address size (7).
+		 */
+		if (first->isread)
+			return -SPIM_INVALID_READ_WRITE;
+		if (first->size > SPIM_MAX_FLASH_COMMAND_BYTES)
+			return -SPIM_INVALID_SIZE;
+
+	}
+	return SPIM_OK;
+}
+
+/* Checks the set bitrate */
+static int check_bitrate(u32 rate)
+{
+	/* Bitrate must be 1, 2, 4, 8, 16, 32, 64, or 128 */
+	switch (rate) {
+	case 1:
+	case 2:
+	case 4:
+	case 8:
+	case 16:
+	case 32:
+	case 64:
+	case 128:
+		return SPIM_OK;
+	default:
+		return -SPIM_INVALID_BIT_RATE;
+	}
+	return -SPIM_INVALID_BIT_RATE;
+}
+
+/* Checks device parameters for errors */
+static int check_device_params(struct spim_device_parameters *pdev_param)
+{
+	if (pdev_param->spi_mode < SPIM_MODE_0 ||
+		pdev_param->spi_mode > SPIM_MODE_3)
+		return -SPIM_INVALID_SPI_MODE;
+	if (check_bitrate(pdev_param->bitrate) != SPIM_OK)
+		return -SPIM_INVALID_BIT_RATE;
+	if (pdev_param->cs_idle_level > 1)
+		return -SPIM_INVALID_CS_IDLE_LEVEL;
+	if (pdev_param->data_idle_level > 1)
+		return -SPIM_INVALID_DATA_IDLE_LEVEL;
+	return SPIM_OK;
+}
+
+/* Function that carries out read/write operations */
+static int spim_io(struct spi_slave *slave, struct spim_buffer *first,
+			struct spim_buffer *second)
+{
+	u32 reg, base;
+	int i, trans_count, ret;
+	struct spim_buffer *transaction[2];
+
+	base = container_of(slave, struct img_spi_slave, slave)->base;
+
+	ret = check_buffers(slave, first, second);
+	if (ret)
+		return ret;
+
+	/*
+	 * Soft reset peripheral internals, this will terminate any
+	 * pending transactions
+	 */
+	write32(base + SPFI_CONTROL_REG_OFFSET, SPIM_SOFT_RESET_MASK);
+	write32(base + SPFI_CONTROL_REG_OFFSET, 0);
+	/* Port state register */
+	reg = read32(base +  SPFI_PORT_STATE_REG_OFFSET);
+	reg = spi_write_reg_field(reg, SPFI_PORT_SELECT, slave->cs);
+	write32(base + SPFI_PORT_STATE_REG_OFFSET, reg);
+	/* Set transaction register */
+	reg = transaction_reg_setup(first, second);
+	write32(base + SPFI_TRANSACTION_REG_OFFSET, reg);
+	/* Clear status */
+	write32(base + SPFI_INT_CLEAR_REG_OFFSET, 0xffffffff);
+	/* Set control register */
+	reg = control_reg_setup(first, second);
+	write32(base + SPFI_CONTROL_REG_OFFSET, reg);
+	/* First transaction always exists */
+	transaction[0] = first;
+	trans_count = 1;
+	/* Is there a second transaction? */
+	if (second) {
+		transaction[1] = second;
+		trans_count++;
+	}
+	/* Now write/read FIFO's */
+	for (i = 0; i < trans_count; i++) {
+		/* Which transaction to execute, "Send" or "Get"? */
+		if (transaction[i]->isread) {
+			/* Get */
+			ret = receivedata(slave, transaction[i]->buffer,
+					transaction[i]->size);
+			if (ret) {
+				printk(BIOS_ERR,
+					"%s: Error: receive data failed.\n",
+					__func__);
+				return ret;
+			}
+		} else {
+			/* Send */
+			ret = transmitdata(slave, transaction[i]->buffer,
+					transaction[i]->size);
+			if (ret) {
+				printk(BIOS_ERR,
+					"%s: Error: transmit data failed.\n",
+					__func__);
+				return ret;
+			}
+		}
+	}
+
+	/* Wait for end of the transaction */
+	ret = wait_status(base + SPFI_INT_STATUS_REG_OFFSET,
+				SPFI_ALLDONE_SHIFT);
+	if (ret)
+		return ret;
+	/*
+	 * Soft reset peripheral internals, this will terminate any
+	 * pending transactions
+	 */
+	write32(base + SPFI_CONTROL_REG_OFFSET, SPIM_SOFT_RESET_MASK);
+	write32(base + SPFI_CONTROL_REG_OFFSET, 0);
+
+	return SPIM_OK;
+}
+
+/* Initialization, must be called once on start up */
+void spi_init(void)
+{
+	/* Clear everything just in case */
+	memset(img_spi_slaves, 0, sizeof(img_spi_slaves));
+}
+
+/* Set up communications parameters for a SPI slave. */
 struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs)
 {
-	return NULL;
+
+	struct img_spi_slave *img_slave = NULL;
+	struct spi_slave *slave;
+	struct spim_device_parameters *device_parameters;
+	u32 base;
+
+	switch (bus) {
+	case 0:
+		base = IMG_SPIM0_BASE_ADDRESS;
+		break;
+	case 1:
+		base = IMG_SPIM1_BASE_ADDRESS;
+		break;
+	default:
+		printk(BIOS_ERR, "%s: Error: unsupported bus.\n",
+				__func__);
+		return NULL;
+	}
+	if (cs > SPIM_DEVICE4) {
+		printk(BIOS_ERR, "%s: Error: unsupported chipselect.\n",
+				__func__);
+		return NULL;
+	}
+
+	img_slave = img_spi_slaves + bus * SPIM_NUM_PORTS_PER_BLOCK + cs;
+	slave = &(img_slave->slave);
+	device_parameters = &(img_slave->device_parameters);
+
+	img_slave->base = base;
+	slave->bus = bus;
+	slave->cs = cs;
+	slave->rw = SPI_READ_FLAG | SPI_WRITE_FLAG;
+	device_parameters->bitrate = 64;
+	device_parameters->cs_setup = 0;
+	device_parameters->cs_hold = 0;
+	device_parameters->cs_delay = 0;
+	device_parameters->spi_mode = SPIM_MODE_0;
+	device_parameters->cs_idle_level = 1;
+	device_parameters->data_idle_level = 0;
+	img_slave->initialised = IMG_FALSE;
+
+	return slave;
 }
 
+/* Claim the bus and prepare it for communication */
 int spi_claim_bus(struct spi_slave *slave)
 {
-	return 0;
+	int ret;
+	struct img_spi_slave *img_slave;
+
+	if (!slave) {
+		printk(BIOS_ERR, "%s: Error: slave was not set up.\n",
+				__func__);
+		return -SPIM_API_NOT_INITIALISED;
+	}
+	img_slave = container_of(slave, struct img_spi_slave, slave);
+	if (img_slave->initialised)
+		return SPIM_OK;
+	/* Check device parameters */
+	ret = check_device_params(&(img_slave->device_parameters));
+	if (ret) {
+		printk(BIOS_ERR, "%s: Error: incorrect device parameters.\n",
+				__func__);
+		return ret;
+	}
+	/* Set device parameters */
+	setparams(slave, slave->cs, &(img_slave->device_parameters));
+	/* Soft reset peripheral internals */
+	write32(img_slave->base + SPFI_CONTROL_REG_OFFSET,
+		SPIM_SOFT_RESET_MASK);
+	write32(img_slave->base + SPFI_CONTROL_REG_OFFSET, 0);
+	img_slave->initialised = IMG_TRUE;
+	return SPIM_OK;
 }
 
+/* Release the SPI bus */
 void spi_release_bus(struct spi_slave *slave)
 {
+	struct img_spi_slave *img_slave;
+
+	if (!slave) {
+		printk(BIOS_ERR, "%s: Error: slave was not set up.\n",
+				__func__);
+		return;
+	}
+	img_slave = container_of(slave, struct img_spi_slave, slave);
+	img_slave->initialised = IMG_FALSE;
+	/* Soft reset peripheral internals */
+	write32(img_slave->base + SPFI_CONTROL_REG_OFFSET,
+		SPIM_SOFT_RESET_MASK);
+	write32(img_slave->base + SPFI_CONTROL_REG_OFFSET, 0);
 }
 
-int spi_xfer(struct spi_slave *slave, const void *dout,
-	     unsigned out_bytes, void *din, unsigned in_bytes)
+/* SPI transfer */
+int spi_xfer(struct spi_slave *slave, const void *dout, unsigned int bytesout,
+		void *din, unsigned int bytesin)
 {
-	return 0;
+	struct spim_buffer	buff_0;
+	struct spim_buffer	buff_1;
+
+	if (!slave) {
+		printk(BIOS_ERR, "%s: Error: slave was not set up.\n",
+				__func__);
+		return -SPIM_API_NOT_INITIALISED;
+	}
+	if (!dout && !din) {
+		printk(BIOS_ERR, "%s: Error: both buffers are NULL.\n",
+				__func__);
+			return -SPIM_INVALID_TRANSFER_DESC;
+	}
+	/* If we only have a read or a write operation
+	 * the parameters for it will be put in the first buffer
+	 */
+	buff_0.buffer = (dout) ? (void *)dout : (void *)din;
+	buff_0.size = (dout) ? bytesout : bytesin;
+	buff_0.isread	= (dout) ? IMG_FALSE : IMG_TRUE;
+	buff_0.inter_byte_delay = 0;
+
+	if (dout && din) {
+		/* Set up the read buffer to recieve our data */
+		buff_1.buffer = din;
+		buff_1.size = bytesin;
+		buff_1.isread = IMG_TRUE;
+		buff_1.inter_byte_delay = 0;
+	}
+	return spim_io(slave, &buff_0, (dout && din) ? &buff_1 : NULL);
 }
diff --git a/src/soc/imgtec/pistachio/spi.h b/src/soc/imgtec/pistachio/spi.h
new file mode 100644
index 0000000..8b4f06b
--- /dev/null
+++ b/src/soc/imgtec/pistachio/spi.h
@@ -0,0 +1,358 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2014 Imagination Technologies
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef __SOC_IMGTEC_DANUBE_SPI_H__
+#define __SOC_IMGTEC_DANUBE_SPI_H__
+
+#include <arch/types.h>
+#include <arch/io.h>
+
+#define spi_read_reg_field(regval, field)		\
+(							\
+	((field##_MASK) == 0xFFFFFFFF) ?		\
+	(regval) :					\
+	(((regval) & (field##_MASK)) >> (field##_SHIFT))\
+)
+
+#define spi_write_reg_field(regval, field, val)		\
+(							\
+	((field##_MASK) == 0xFFFFFFFF) ?		\
+	(val) :						\
+	(((regval) & ~(field##_MASK)) |			\
+	(((val) << (field##_SHIFT)) & (field##_MASK)))	\
+)
+
+/*
+ * Parameter register
+ * Each of these corresponds to a single port (ie CS line) in the interface
+ * Fields	Name		Description
+ * ======	====		===========
+ * b31:24	CLK_RATE	Bit Clock rate = (24.576 * value / 512) MHz
+ * b23:16	CS_SETUP	Chip Select setup = (40 * value) ns
+ * b15:8	CS_HOLD		Chip Select hold = (40 * value) ns
+ * b7:0		CS_DELAY	Chip Select delay = (40 * value) ns
+ */
+
+#define SPIM_CLK_DIVIDE_MASK     (0xFF000000)
+#define SPIM_CS_SETUP_MASK       (0x00FF0000)
+#define SPIM_CS_HOLD_MASK        (0x0000FF00)
+#define SPIM_CS_DELAY_MASK       (0x000000FF)
+#define SPIM_CS_PARAM_MASK      (SPIM_CS_SETUP_MASK \
+				| SPIM_CS_HOLD_MASK \
+				| SPIM_CS_DELAY_MASK)
+
+#define SPIM_CLK_DIVIDE_SHIFT            (24)
+#define SPIM_CS_SETUP_SHIFT              (16)
+#define SPIM_CS_HOLD_SHIFT                (8)
+#define SPIM_CS_DELAY_SHIFT               (0)
+#define SPIM_CS_PARAM_SHIFT               (0)
+
+/* Control register */
+
+#define SPFI_DRIBBLE_COUNT_MASK  (0x000e0000)
+#define SPFI_MEMORY_IF_MASK      (0x00008000)
+#define SPIM_BYTE_DELAY_MASK     (0x00004000)
+#define SPIM_CS_DEASSERT_MASK    (0x00002000)
+#define SPIM_CONTINUE_MASK       (0x00001000)
+#define SPIM_SOFT_RESET_MASK     (0x00000800)
+#define SPIM_SEND_DMA_MASK       (0x00000400)
+#define SPIM_GET_DMA_MASK        (0x00000200)
+#define SPIM_EDGE_TX_RX_MASK     (0x00000100)
+#define SPFI_TRNSFR_MODE_MASK    (0x000000e0)
+#define SPFI_TRNSFR_MODE_DQ_MASK (0x0000001c)
+#define SPFI_TX_RX_MASK          (0x00000002)
+#define SPFI_EN_MASK             (0x00000001)
+
+#define SPFI_DRIBBLE_COUNT_SHIFT         (17)
+#define SPFI_MEMORY_IF_SHIFT             (15)
+#define SPIM_BYTE_DELAY_SHIFT            (14)
+#define SPIM_CS_DEASSERT_SHIFT           (13)
+#define SPIM_CONTINUE_SHIFT              (12)
+#define SPIM_SOFT_RESET_SHIFT            (11)
+#define SPIM_SEND_DMA_SHIFT              (10)
+#define   SPIM_GET_DMA_SHIFT              (9)
+#define SPIM_EDGE_TX_RX_SHIFT             (8)
+#define SPFI_TRNSFR_MODE_SHIFT            (5)
+#define SPFI_TRNSFR_MODE_DQ_SHIFT         (2)
+#define SPFI_TX_RX_SHIFT                  (1)
+#define SPFI_EN_SHIFT                     (0)
+
+/* Transaction register*/
+
+#define SPFI_TSIZE_MASK           (0xffff0000)
+#define SPFI_CMD_LENGTH_MASK      (0x0000e000)
+#define SPFI_ADDR_LENGTH_MASK     (0x00001c00)
+#define SPFI_DUMMY_LENGTH_MASK    (0x000003e0)
+#define SPFI_PI_LENGTH_MASK       (0x0000001c)
+
+#define SPFI_TSIZE_SHIFT                  (16)
+#define SPFI_CMD_LENGTH_SHIFT             (13)
+#define SPFI_ADDR_LENGTH_SHIFT            (10)
+#define SPFI_DUMMY_LENGTH_SHIFT            (5)
+#define SPFI_PI_LENGTH_SHIFT               (2)
+
+/* Port state register */
+
+#define SPFI_PORT_SELECT_MASK    (0x00700000)
+/* WARNING the following bits are reversed */
+#define SPFI_CLOCK0_IDLE_MASK    (0x000f8000)
+#define SPFI_CLOCK0_PHASE_MASK   (0x00007c00)
+#define SPFI_CS0_IDLE_MASK       (0x000003e0)
+#define SPFI_DATA0_IDLE_MASK     (0x0000001f)
+
+#define SPIM_CLOCK0_IDLE_MASK    (0x000f8000)
+#define SPIM_CLOCK0_PHASE_MASK   (0x00007c00)
+#define SPIM_CS0_IDLE_MASK       (0x000003e0)
+#define SPIM_DATA0_IDLE_MASK     (0x0000001f)
+
+#define SPIM_PORT0_MASK          (0x00084210)
+
+#define SPFI_PORT_SELECT_SHIFT           (20)
+/* WARNING the following bits are reversed, bit 0 is highest */
+#define SPFI_CLOCK0_IDLE_SHIFT           (19)
+#define SPFI_CLOCK0_PHASE_SHIFT          (14)
+#define SPFI_CS0_IDLE_SHIFT               (9)
+#define SPFI_DATA0_IDLE_SHIFT             (4)
+
+#define SPIM_CLOCK0_IDLE_SHIFT           (19)
+#define SPIM_CLOCK0_PHASE_SHIFT          (14)
+#define SPIM_CS0_IDLE_SHIFT               (9)
+#define SPIM_DATA0_IDLE_SHIFT             (4)
+
+
+/*
+ * Interrupt registers
+ * SPFI_GDOF_MASK means Rx buffer full, not an overflow, because clock stalls
+ * SPFI_SDUF_MASK means Tx buffer empty, not an underflow, because clock stalls
+ */
+#define SPFI_IACCESS_MASK        (0x00001000)
+#define SPFI_GDEX8BIT_MASK       (0x00000800)
+#define SPFI_ALLDONE_MASK        (0x00000200)
+#define SPFI_GDFUL_MASK          (0x00000100)
+#define SPFI_GDHF_MASK           (0x00000080)
+#define SPFI_GDEX32BIT_MASK      (0x00000040)
+#define SPFI_GDTRIG_MASK         (0x00000020)
+#define SPFI_SDFUL_MASK          (0x00000008)
+#define SPFI_SDHF_MASK           (0x00000004)
+#define SPFI_SDE_MASK            (0x00000002)
+#define SPFI_SDTRIG_MASK         (0x00000001)
+
+#define SPFI_IACCESS_SHIFT               (12)
+#define SPFI_GDEX8BIT_SHIFT              (11)
+#define SPFI_ALLDONE_SHIFT                (9)
+#define SPFI_GDFUL_SHIFT                  (8)
+#define SPFI_GDHF_SHIFT                   (7)
+#define SPFI_GDEX32BIT_SHIFT              (6)
+#define SPFI_GDTRIG_SHIFT                 (5)
+#define SPFI_SDFUL_SHIFT                  (3)
+#define SPFI_SDHF_SHIFT                   (2)
+#define SPFI_SDE_SHIFT                    (1)
+#define SPFI_SDTRIG_SHIFT                 (0)
+
+
+/* SPFI register block */
+
+#define SPFI_PORT_0_PARAM_REG_OFFSET             (0x00)
+#define SPFI_PORT_1_PARAM_REG_OFFSET             (0x04)
+#define SPFI_PORT_2_PARAM_REG_OFFSET             (0x08)
+#define SPFI_PORT_3_PARAM_REG_OFFSET             (0x0C)
+#define SPFI_PORT_4_PARAM_REG_OFFSET             (0x10)
+#define SPFI_CONTROL_REG_OFFSET                  (0x14)
+#define SPFI_TRANSACTION_REG_OFFSET              (0x18)
+#define SPFI_PORT_STATE_REG_OFFSET               (0x1C)
+
+#define SPFI_SEND_LONG_REG_OFFSET                (0x20)
+#define SPFI_SEND_BYTE_REG_OFFSET                (0x24)
+#define SPFI_GET_LONG_REG_OFFSET                 (0x28)
+#define SPFI_GET_BYTE_REG_OFFSET                 (0x2C)
+
+#define SPFI_INT_STATUS_REG_OFFSET               (0x30)
+#define SPFI_INT_ENABLE_REG_OFFSET               (0x34)
+#define SPFI_INT_CLEAR_REG_OFFSET                (0x38)
+
+#define SPFI_IMMEDIATE_STATUS_REG_OFFSET         (0x3c)
+
+#define SPFI_FLASH_BASE_ADDRESS_REG_OFFSET       (0x48)
+#define SPFI_FLASH_STATUS_REG_OFFSET             (0x4C)
+
+#define IMG_FALSE				0
+#define IMG_TRUE				1
+
+/* Number of SPIM interfaces*/
+#define SPIM_NUM_BLOCKS				2
+/* Number of chip select lines supported by the SPI master port. */
+#define SPIM_NUM_PORTS_PER_BLOCK		(SPIM_DUMMY_CS)
+/* Maximum transfer size (in bytes) for the SPI master port. */
+#define SPIM_MAX_TRANSFER_BYTES			(0xFFFF)
+/* Maximum size of a flash command: command bytes+address_bytes. */
+#define SPIM_MAX_FLASH_COMMAND_BYTES		(0x8)
+/* Write operation to fifo done in blocks of 16 words (64 bytes) */
+#define SPIM_MAX_BLOCK_BYTES			(0x40)
+/* Number of tries until timeout error is returned*/
+#define SPI_TIMEOUT_VALUE_US			500000
+
+/* SPIM initialisation function return value.*/
+enum spim_return {
+	/* Initialisation parameters are valid. */
+	SPIM_OK = 0,
+	/* Mode parameter is invalid. */
+	SPIM_INVALID_SPI_MODE,
+	/* Chip select idle level is invalid. */
+	SPIM_INVALID_CS_IDLE_LEVEL,
+	/* Data idle level is invalid. */
+	SPIM_INVALID_DATA_IDLE_LEVEL,
+	/* Chip select line parameter is invalid. */
+	SPIM_INVALID_CS_LINE,
+	/* Transfer size parameter is invalid. */
+	SPIM_INVALID_SIZE,
+	/* Read/write parameter is invalid. */
+	SPIM_INVALID_READ_WRITE,
+	/* Continue parameter is invalid. */
+	SPIM_INVALID_CONTINUE,
+	/* Invalid block index */
+	SPIM_INVALID_BLOCK_INDEX,
+	/* Extended error values */
+	/* Invalid bit rate */
+	SPIM_INVALID_BIT_RATE,
+	/* Invalid CS hold value */
+	SPIM_INVALID_CS_HOLD_VALUE,
+	/* API function called before API is initialised */
+	SPIM_API_NOT_INITIALISED,
+	/* SPI driver initialisation failed */
+	SPIM_DRIVER_INIT_ERROR,
+	/* Invalid transfer description */
+	SPIM_INVALID_TRANSFER_DESC,
+	/* Timeout */
+	SPIM_TIMEOUT
+
+};
+
+/* This type defines the SPI Mode.*/
+enum spim_mode {
+	/* Mode 0 (clock idle low, data valid on first clock transition). */
+	SPIM_MODE_0 = 0,
+	/* Mode 1 (clock idle low, data valid on second clock transition). */
+	SPIM_MODE_1,
+	/* Mode 2 (clock idle high, data valid on first clock transition). */
+	SPIM_MODE_2,
+	/* Mode 3 (clock idle high, data valid on second clock transition). */
+	SPIM_MODE_3
+
+};
+
+/* This type defines the SPIM device numbers (chip select lines). */
+enum spim_device {
+	/* Device 0 (CS0). */
+	SPIM_DEVICE0 = 0,
+	/* Device 1 (CS1). */
+	SPIM_DEVICE1,
+	/* Device 2 (CS2). */
+	SPIM_DEVICE2,
+	/* Device 3 (CS3). */
+	SPIM_DEVICE3,
+	/* Device 4 (CS4). */
+	SPIM_DEVICE4,
+	/* Dummy chip select. */
+	SPIM_DUMMY_CS
+
+};
+
+/* This structure defines communication parameters for a slave device */
+struct spim_device_parameters {
+	/* Bit rate value.*/
+	unsigned char bitrate;
+	/*
+	 * Chip select set up time.
+	 * Time taken between chip select going active and activity occurring
+	 * on the clock, calculated by dividing the desired set up time in ns
+	 * by the Input clock period. (setup time / Input clock freq)
+	 */
+	unsigned char cs_setup;
+	/*
+	 * Chip select hold time.
+	 * Time after the last clock pulse before chip select goes inactive,
+	 * calculated by dividing the desired hold time in ns by the
+	 * Input clock period (hold time / Input clock freq).
+	 */
+	unsigned char cs_hold;
+	/*
+	 * Chip select delay time (CS minimum inactive time).
+	 * Minimum time after chip select goes inactive before chip select
+	 * can go active again, calculated by dividing the desired delay time
+	 * in ns by the Input clock period (delay time / Input clock freq).
+	 */
+	unsigned char cs_delay;
+	/* SPI Mode. */
+	enum spim_mode spi_mode;
+	/* Chip select idle level (0=low, 1=high, Others=invalid). */
+	unsigned int cs_idle_level;
+	/* Data idle level (0=low, 1=high, Others=invalid). */
+	unsigned int data_idle_level;
+
+};
+
+/* Command transfer mode */
+enum command_mode {
+	/* Command, address, dummy and PI cycles are transferred on sio0 */
+	SPIM_CMD_MODE_0 = 0,
+	/*
+	 * Command and Address is transferred on sio0 port only but dummy
+	 * cycles and PI is transferred on all the interface ports.
+	 */
+	SPIM_CMD_MODE_1,
+	/*
+	 * Command is transferred on sio0 port only but address, dummy
+	 * and PI is transferred on all the interface portS
+	 */
+	SPIM_CMD_MODE_2,
+	/*
+	 * Command, address, dummy and PI bytes are transferred on all
+	 * the interfaces
+	 */
+	SPIM_CMD_MODE_3
+};
+
+/* Data transfer mode */
+enum transfer_mode {
+	/* Transfer data in single mode */
+	SPIM_DMODE_SINGLE = 0,
+	/* Transfer data in dual mode */
+	SPIM_DMODE_DUAL,
+	/* Transfer data in quad mode */
+	SPIM_DMODE_QUAD
+};
+
+/* This structure contains parameters that describe an SPIM operation. */
+struct spim_buffer {
+	/* The buffer to read from or write to. */
+	unsigned char *buffer;
+
+	/* Number of bytes to read/write. Valid range is 0 to 65536 bytes. */
+	unsigned int size;
+
+	/* Read/write select. TRUE for read, FALSE for write, Others-invalid.*/
+	int isread;
+
+	/*
+	 * ByteDelay select.
+	 * Selects whether or not a delay is inserted between bytes.
+	 * 0 - Minimum inter-byte delay
+	 * 1 - Inter-byte delay of (cs_hold/master_clk half period)*master_clk.
+	 */
+	int inter_byte_delay;
+};
+
+#endif /* __SOC_IMGTEC_DANUBE_SPI_H__ */



More information about the coreboot-gerrit mailing list