summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuilin Hao <rlhao@marvell.com>2015-11-10 00:12:08 -0800
committerPatrick Georgi <pgeorgi@google.com>2016-02-04 11:30:21 +0100
commit5b429ac2b26cdd487841ef6c45d03ffe7b048f04 (patch)
treecd86c1a00118d36be05be31c660406466ac75173
parent2c8b0b137382c969d791c734dca7c1a7a03b07ca (diff)
downloadcoreboot-5b429ac2b26cdd487841ef6c45d03ffe7b048f04.tar.xz
soc/marvell/armada38x: Add spi driver for armada38x
Port spi driver from uboot to coreboot BUG=chrome-os-partner:47462 TEST=None BRANCH=tot Change-Id: I747be7001f4cfb8eec33e8e5bdef3fe5bb0eb2ca Signed-off-by: Patrick Georgi <pgeorgi@chromium.org> Original-Commit-Id: 9fbc5c2feb6ffacb54ed94e5c7b94b38be2b2ded Original-Change-Id: Ibea9a050ac8bdab6ce4eeb07accde53aeadade5f Original-Signed-off-by: Ruilin Hao <rlhao@marvell.com> Original-Reviewed-on: https://chromium-review.googlesource.com/313340 Original-Commit-Ready: Kan Yan <kyan@google.com> Original-Tested-by: Kan Yan <kyan@google.com> Original-Reviewed-by: Furquan Shaikh <furquan@chromium.org> Original-Reviewed-by: Kan Yan <kyan@google.com> Original-Reviewed-by: Yuji Sasaki <sasakiy@chromium.org> Reviewed-on: https://review.coreboot.org/13111 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
-rw-r--r--src/soc/marvell/armada38x/Makefile.inc4
-rw-r--r--src/soc/marvell/armada38x/spi.c488
2 files changed, 492 insertions, 0 deletions
diff --git a/src/soc/marvell/armada38x/Makefile.inc b/src/soc/marvell/armada38x/Makefile.inc
index 416ad74d87..18f5491d53 100644
--- a/src/soc/marvell/armada38x/Makefile.inc
+++ b/src/soc/marvell/armada38x/Makefile.inc
@@ -2,6 +2,7 @@ ifeq ($(CONFIG_SOC_MARVELL_ARMADA38X),y)
bootblock-y += bootblock.c
bootblock-y += bootblock_asm.S
+bootblock-y += spi.c
bootblock-y += monotonic_timer.c
ifeq ($(CONFIG_BOOTBLOCK_CONSOLE),y)
bootblock-$(CONFIG_DRIVERS_UART) += uart.c
@@ -9,11 +10,14 @@ endif
verstage-$(CONFIG_DRIVERS_UART) += uart.c
verstage-y += monotonic_timer.c
+verstage-y += spi.c
+romstage-y += spi.c
romstage-y += cbmem.c
romstage-y += monotonic_timer.c
romstage-$(CONFIG_DRIVERS_UART) += uart.c
+ramstage-y += spi.c
ramstage-y += cbmem.c
ramstage-y += monotonic_timer.c
ramstage-y += soc.c
diff --git a/src/soc/marvell/armada38x/spi.c b/src/soc/marvell/armada38x/spi.c
new file mode 100644
index 0000000000..ed5d519dd0
--- /dev/null
+++ b/src/soc/marvell/armada38x/spi.c
@@ -0,0 +1,488 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2015 Marvell Inc.
+ *
+ * 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.
+ */
+
+#include <arch/io.h>
+#include <delay.h>
+#include <spi_flash.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <console/console.h>
+#include <soc/common.h>
+
+/******************************************************************************
+base type define
+*******************************************************************************/
+#define MV_SPI_REG_READ mrvl_reg_read
+#define MV_SPI_REG_WRITE mrvl_reg_write
+#define MV_SPI_REG_BIT_SET mrvl_reg_bit_set
+#define MV_SPI_REG_BIT_RESET mrvl_reg_bit_reset
+
+#define MV_SPI_REGS_OFFSET(unit) (0x10600 + (unit * 0x80))
+#define MV_SPI_REGS_BASE(unit) (MV_SPI_REGS_OFFSET(unit))
+#define MV_SPI_IF_CONFIG_REG(spi_id) (MV_SPI_REGS_BASE(spi_id) + 0x04)
+#define MV_SPI_SPR_OFFSET 0
+#define MV_SPI_SPR_MASK (0xF << MV_SPI_SPR_OFFSET)
+#define MV_SPI_SPPR_0_OFFSET 4
+#define MV_SPI_SPPR_0_MASK (0x1 << MV_SPI_SPPR_0_OFFSET)
+#define MV_SPI_SPPR_HI_OFFSET 6
+#define MV_SPI_SPPR_HI_MASK (0x3 << MV_SPI_SPPR_HI_OFFSET)
+
+#define MV_SPI_BYTE_LENGTH_OFFSET 5 /* bit 5 */
+#define MV_SPI_BYTE_LENGTH_MASK (0x1 << MV_SPI_BYTE_LENGTH_OFFSET)
+
+#define MV_SPI_IF_CTRL_REG(spi_id) (MV_SPI_REGS_BASE(spi_id) + 0x00)
+#define MV_SPI_CS_ENABLE_OFFSET 0 /* bit 0 */
+#define MV_SPI_CS_ENABLE_MASK (0x1 << MV_SPI_CS_ENABLE_OFFSET)
+
+#define MV_SPI_CS_NUM_OFFSET 2
+#define MV_SPI_CS_NUM_MASK (0x7 << MV_SPI_CS_NUM_OFFSET)
+#define MV_SPI_CPOL_OFFSET 11
+#define MV_SPI_CPOL_MASK (0x1 << MV_SPI_CPOL_OFFSET)
+#define MV_SPI_CPHA_OFFSET 12
+#define MV_SPI_CPHA_MASK (0x1 << MV_SPI_CPHA_OFFSET)
+#define MV_SPI_TXLSBF_OFFSET 13
+#define MV_SPI_TXLSBF_MASK (0x1 << MV_SPI_TXLSBF_OFFSET)
+#define MV_SPI_RXLSBF_OFFSET 14
+#define MV_SPI_RXLSBF_MASK (0x1 << MV_SPI_RXLSBF_OFFSET)
+
+/* SPI transfer flags */
+#define SPI_XFER_BEGIN 0x01
+#define SPI_XFER_END 0x02
+
+#define MV_SPI_INT_CAUSE_REG(spi_id) (MV_SPI_REGS_BASE(spi_id) + 0x10)
+#define MV_SPI_DATA_OUT_REG(spi_id) (MV_SPI_REGS_BASE(spi_id) + 0x08)
+#define MV_SPI_WAIT_RDY_MAX_LOOP 100000
+#define MV_SPI_DATA_IN_REG(spi_id) (MV_SPI_REGS_BASE(spi_id) + 0x0c)
+
+#define MV_SPI_TMNG_PARAMS_REG(spi_id) (MV_SPI_REGS_BASE(spi_id) + 0x18)
+#define MV_SPI_TMISO_SAMPLE_OFFSET 6
+#define MV_SPI_TMISO_SAMPLE_MASK (0x3 << MV_SPI_TMISO_SAMPLE_OFFSET)
+
+#define CONFIG_ENV_SPI_MAX_HZ 50000000
+#define CONFIG_SF_DEFAULT_SPEED CONFIG_ENV_SPI_MAX_HZ
+
+#define CMD_READ_ARRAY_FAST 0x0b
+
+/******************************************************************************
+base type define end
+*******************************************************************************/
+
+/******************************************************************************
+struct define
+*******************************************************************************/
+typedef enum {
+ SPI_TYPE_FLASH = 0,
+ SPI_TYPE_SLIC_ZARLINK_SILABS,
+ SPI_TYPE_SLIC_LANTIQ,
+ SPI_TYPE_SLIC_ZSI,
+ SPI_TYPE_SLIC_ISI
+} MV_SPI_TYPE;
+
+typedef struct {
+ unsigned short ctrl_model;
+ unsigned int tclk;
+} MV_SPI_HAL_DATA;
+
+typedef struct {
+ int clock_pol_low;
+ enum { SPI_CLK_HALF_CYC, SPI_CLK_BEGIN_CYC } clock_phase;
+ int tx_msb_first;
+ int rx_msb_first;
+} MV_SPI_IF_PARAMS;
+
+typedef struct {
+ /* Does this device support 16 bits access */
+ int en16_bit;
+ /* should we assert / disassert CS for each byte we read / write */
+ int byte_cs_asrt;
+ int clock_pol_low;
+ unsigned int baud_rate;
+ unsigned int clk_phase;
+} MV_SPI_TYPE_INFO;
+
+/******************************************************************************
+struct define end
+*******************************************************************************/
+
+/******************************************************************************
+param define
+*******************************************************************************/
+static MV_SPI_HAL_DATA spi_hal_data;
+static MV_SPI_TYPE_INFO *curr_spi_info = NULL;
+static MV_SPI_TYPE_INFO spi_types[] = { {.en16_bit = MV_TRUE,
+ .clock_pol_low = MV_TRUE,
+ .byte_cs_asrt = MV_FALSE,
+ .baud_rate = (20 << 20), /* 20_m */
+ .clk_phase = SPI_CLK_BEGIN_CYC},
+ {.en16_bit = MV_FALSE,
+ .clock_pol_low = MV_TRUE,
+ .byte_cs_asrt = MV_TRUE,
+ .baud_rate = 0x00800000,
+ .clk_phase = SPI_CLK_BEGIN_CYC},
+ {.en16_bit = MV_FALSE,
+ .clock_pol_low = MV_TRUE,
+ .byte_cs_asrt = MV_FALSE,
+ .baud_rate = 0x00800000,
+ .clk_phase = SPI_CLK_BEGIN_CYC},
+ {.en16_bit = MV_FALSE,
+ .clock_pol_low = MV_TRUE,
+ .byte_cs_asrt = MV_TRUE,
+ .baud_rate = 0x00800000,
+ .clk_phase = SPI_CLK_HALF_CYC},
+ {.en16_bit = MV_FALSE,
+ .clock_pol_low = MV_FALSE,
+ .byte_cs_asrt = MV_TRUE,
+ .baud_rate = 0x00200000,
+ .clk_phase = SPI_CLK_HALF_CYC} };
+
+/******************************************************************************
+param define end
+*******************************************************************************/
+
+static struct spi_slave s_spi;
+
+static int mv_spi_baud_rate_set(unsigned char spi_id,
+ unsigned int serial_baud_rate);
+static void mv_spi_cs_deassert(unsigned char spi_id);
+static int mv_spi_cs_set(unsigned char spi_id, unsigned char cs_id);
+static int mv_spi_if_config_set(unsigned char spi_id,
+ MV_SPI_IF_PARAMS *if_params);
+static int mv_spi_params_set(unsigned char spi_id,
+ unsigned char cs_id,
+ MV_SPI_TYPE type);
+static int mv_spi_init(unsigned char spi_id,
+ unsigned char cs_id,
+ unsigned int serial_baud_rate,
+ MV_SPI_HAL_DATA *hal_data);
+static int mv_spi_sys_init(unsigned char spi_id,
+ unsigned char cs_id,
+ unsigned int serial_baud_rate);
+static void mv_spi_cs_assert(unsigned char spi_id);
+static int mv_spi_8bit_data_tx_rx(unsigned char spi_id,
+ unsigned char tx_data,
+ unsigned char *p_rx_data);
+
+int mv_spi_baud_rate_set(unsigned char spi_id, unsigned int serial_baud_rate)
+{
+ unsigned int spr, sppr;
+ unsigned int divider;
+ unsigned int best_spr = 0, best_sppr = 0;
+ unsigned char exact_match = 0;
+ unsigned int min_baud_offset = 0xFFFFFFFF;
+ unsigned int cpu_clk = spi_hal_data.tclk; /*mv_cpu_pclk_get();*/
+ unsigned int temp_reg;
+
+ assert(cpu_clk != serial_baud_rate);
+ /* Find the best prescale configuration - less or equal */
+ for (spr = 1; spr <= 15; spr++) {
+ for (sppr = 0; sppr <= 7; sppr++) {
+ divider = spr * (1 << sppr);
+ /* check for higher - irrelevant */
+ if ((cpu_clk / divider) > serial_baud_rate)
+ continue;
+
+ /* check for exact fit */
+ if ((cpu_clk / divider) == serial_baud_rate) {
+ best_spr = spr;
+ best_sppr = sppr;
+ exact_match = 1;
+ break;
+ }
+
+ /* check if this is better than the previous one */
+ if ((serial_baud_rate - (cpu_clk / divider)) <
+ min_baud_offset) {
+ min_baud_offset =
+ serial_baud_rate - cpu_clk / divider;
+ best_spr = spr;
+ best_sppr = sppr;
+ }
+ }
+
+ if (exact_match == 1)
+ break;
+ }
+
+ if (best_spr == 0) {
+ printk(BIOS_INFO, "%s ERROR: SPI baud rate prescale error!\n",
+ __func__);
+ return MV_OUT_OF_RANGE;
+ }
+
+ /* configure the Prescale */
+ temp_reg = MV_SPI_REG_READ(MV_SPI_IF_CONFIG_REG(spi_id)) &
+ ~(MV_SPI_SPR_MASK | MV_SPI_SPPR_0_MASK | MV_SPI_SPPR_HI_MASK);
+ temp_reg |= ((best_spr << MV_SPI_SPR_OFFSET) |
+ ((best_sppr & 0x1) << MV_SPI_SPPR_0_OFFSET) |
+ ((best_sppr >> 1) << MV_SPI_SPPR_HI_OFFSET));
+ MV_SPI_REG_WRITE(MV_SPI_IF_CONFIG_REG(spi_id), temp_reg);
+
+ return MV_OK;
+}
+
+void mv_spi_cs_deassert(unsigned char spi_id)
+{
+ MV_SPI_REG_BIT_RESET(MV_SPI_IF_CTRL_REG(spi_id), MV_SPI_CS_ENABLE_MASK);
+}
+
+int mv_spi_cs_set(unsigned char spi_id, unsigned char cs_id)
+{
+ unsigned int ctrl_reg;
+ static unsigned char last_cs_id = 0xFF;
+ static unsigned char last_spi_id = 0xFF;
+
+ if (cs_id > 7)
+ return MV_BAD_PARAM;
+
+ if ((last_spi_id == spi_id) && (last_cs_id == cs_id))
+ return MV_OK;
+
+ ctrl_reg = MV_SPI_REG_READ(MV_SPI_IF_CTRL_REG(spi_id));
+ ctrl_reg &= ~MV_SPI_CS_NUM_MASK;
+ ctrl_reg |= (cs_id << MV_SPI_CS_NUM_OFFSET);
+ MV_SPI_REG_WRITE(MV_SPI_IF_CTRL_REG(spi_id), ctrl_reg);
+
+ last_spi_id = spi_id;
+ last_cs_id = cs_id;
+
+ return MV_OK;
+}
+
+int mv_spi_if_config_set(unsigned char spi_id, MV_SPI_IF_PARAMS *if_params)
+{
+ unsigned int ctrl_reg;
+
+ ctrl_reg = MV_SPI_REG_READ(MV_SPI_IF_CONFIG_REG(spi_id));
+
+ /* Set Clock Polarity */
+ ctrl_reg &= ~(MV_SPI_CPOL_MASK | MV_SPI_CPHA_MASK | MV_SPI_TXLSBF_MASK |
+ MV_SPI_RXLSBF_MASK);
+ if (if_params->clock_pol_low)
+ ctrl_reg |= MV_SPI_CPOL_MASK;
+
+ if (if_params->clock_phase == SPI_CLK_BEGIN_CYC)
+ ctrl_reg |= MV_SPI_CPHA_MASK;
+
+ if (if_params->tx_msb_first)
+ ctrl_reg |= MV_SPI_TXLSBF_MASK;
+
+ if (if_params->rx_msb_first)
+ ctrl_reg |= MV_SPI_RXLSBF_MASK;
+
+ MV_SPI_REG_WRITE(MV_SPI_IF_CONFIG_REG(spi_id), ctrl_reg);
+
+ return MV_OK;
+}
+
+int mv_spi_params_set(unsigned char spi_id,
+ unsigned char cs_id,
+ MV_SPI_TYPE type)
+{
+ MV_SPI_IF_PARAMS if_params;
+
+ if (MV_OK != mv_spi_cs_set(spi_id, cs_id)) {
+ printk(BIOS_INFO, "Error, setting SPI CS failed\n");
+ return MV_ERROR;
+ }
+
+ if (curr_spi_info != (&(spi_types[type]))) {
+ curr_spi_info = &(spi_types[type]);
+ mv_spi_baud_rate_set(spi_id, curr_spi_info->baud_rate);
+
+ if_params.clock_pol_low = curr_spi_info->clock_pol_low;
+ if_params.clock_phase = curr_spi_info->clk_phase;
+ if_params.tx_msb_first = MV_FALSE;
+ if_params.rx_msb_first = MV_FALSE;
+ mv_spi_if_config_set(spi_id, &if_params);
+ }
+
+ return MV_OK;
+}
+
+int mv_spi_init(unsigned char spi_id,
+ unsigned char cs_id,
+ unsigned int serial_baud_rate,
+ MV_SPI_HAL_DATA *hal_data)
+{
+ int ret;
+ unsigned int timing_reg;
+
+ spi_hal_data.ctrl_model = hal_data->ctrl_model;
+ spi_hal_data.tclk = hal_data->tclk;
+
+ /* Set the serial clock */
+ ret = mv_spi_baud_rate_set(spi_id, serial_baud_rate);
+ if (ret != MV_OK)
+ return ret;
+
+ /* Configure the default SPI mode to be 8bit */
+ MV_SPI_REG_BIT_RESET(MV_SPI_IF_CONFIG_REG(spi_id),
+ MV_SPI_BYTE_LENGTH_MASK);
+
+ timing_reg = MV_SPI_REG_READ(MV_SPI_TMNG_PARAMS_REG(spi_id));
+ timing_reg &= ~MV_SPI_TMISO_SAMPLE_MASK;
+ timing_reg |= (0x2) << MV_SPI_TMISO_SAMPLE_OFFSET;
+ MV_SPI_REG_WRITE(MV_SPI_TMNG_PARAMS_REG(spi_id), timing_reg);
+
+ /* Verify that the CS is deasserted */
+ mv_spi_cs_deassert(spi_id);
+
+ mv_spi_params_set(spi_id, cs_id, SPI_TYPE_FLASH);
+
+ return MV_OK;
+}
+
+int mv_spi_sys_init(unsigned char spi_id,
+ unsigned char cs_id,
+ unsigned int serial_baud_rate)
+{
+ MV_SPI_HAL_DATA hal_data;
+
+ hal_data.ctrl_model = MV_6810_DEV_ID;
+ hal_data.tclk = MV_BOARD_TCLK_250MHZ;
+
+ return mv_spi_init(spi_id, cs_id, serial_baud_rate, &hal_data);
+}
+
+void mv_spi_cs_assert(unsigned char spi_id)
+{
+ MV_SPI_REG_BIT_SET(MV_SPI_IF_CTRL_REG(spi_id), MV_SPI_CS_ENABLE_MASK);
+}
+
+int mv_spi_8bit_data_tx_rx(unsigned char spi_id,
+ unsigned char tx_data,
+ unsigned char *p_rx_data)
+{
+ unsigned int i;
+ int ready = MV_FALSE;
+
+ if (curr_spi_info->byte_cs_asrt)
+ mv_spi_cs_assert(spi_id);
+
+ /* First clear the bit in the interrupt cause register */
+ MV_SPI_REG_WRITE(MV_SPI_INT_CAUSE_REG(spi_id), 0x0);
+
+ /* Transmit data */
+ MV_SPI_REG_WRITE(MV_SPI_DATA_OUT_REG(spi_id), tx_data);
+
+ /* wait with timeout for memory ready */
+ for (i = 0; i < MV_SPI_WAIT_RDY_MAX_LOOP; i++) {
+ if (MV_SPI_REG_READ(MV_SPI_INT_CAUSE_REG(spi_id))) {
+ ready = MV_TRUE;
+ break;
+ }
+ }
+
+ if (!ready) {
+ if (curr_spi_info->byte_cs_asrt) {
+ mv_spi_cs_deassert(spi_id);
+ /* WA to compansate Zarlink SLIC CS off time */
+ udelay(4);
+ }
+ return MV_TIMEOUT;
+ }
+
+ /* check that the RX data is needed */
+ if (p_rx_data)
+ *p_rx_data = MV_SPI_REG_READ(MV_SPI_DATA_IN_REG(spi_id));
+
+ if (curr_spi_info->byte_cs_asrt) {
+ mv_spi_cs_deassert(spi_id);
+ /* WA to compansate Zarlink SLIC CS off time */
+ udelay(4);
+ }
+
+ return MV_OK;
+}
+
+static int mrvl_spi_xfer(struct spi_slave *slave,
+ unsigned int bitlen,
+ const void *dout,
+ void *din)
+{
+ int ret;
+ unsigned char *pdout = (unsigned char *)dout;
+ unsigned char *pdin = (unsigned char *)din;
+ int tmp_bitlen = bitlen;
+ unsigned char tmp_dout = 0;
+
+ /* Verify that the SPI mode is in 8bit mode */
+ MV_SPI_REG_BIT_RESET(MV_SPI_IF_CONFIG_REG(slave->bus),
+ MV_SPI_BYTE_LENGTH_MASK);
+
+ while (tmp_bitlen > 0) {
+ if (pdout)
+ tmp_dout = (*pdout) & 0xff;
+
+ /* Transmitted and wait for the transfer to be completed */
+ ret = mv_spi_8bit_data_tx_rx(slave->bus, tmp_dout, pdin);
+ if (ret != MV_OK)
+ return ret;
+
+ /* increment the pointers */
+ if (pdin)
+ pdin++;
+ if (pdout)
+ pdout++;
+
+ tmp_bitlen -= 8;
+ }
+ return 0;
+}
+
+struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs)
+{
+ struct spi_slave *slave = &s_spi;
+
+ slave->bus = bus;
+ slave->cs = cs;
+ mv_spi_sys_init(bus, cs, CONFIG_SF_DEFAULT_SPEED);
+ return slave;
+}
+
+int spi_claim_bus(struct spi_slave *slave)
+{
+ mv_spi_cs_set(slave->bus, slave->cs);
+ mv_spi_cs_assert(slave->bus);
+ return 0;
+}
+
+void spi_release_bus(struct spi_slave *slave)
+{
+ mv_spi_cs_deassert(slave->bus);
+}
+
+unsigned int spi_crop_chunk(unsigned int cmd_len, unsigned int buf_len)
+{
+ return buf_len;
+}
+
+int spi_xfer(struct spi_slave *slave,
+ const void *dout,
+ unsigned out_bytes,
+ void *din,
+ unsigned in_bytes)
+{
+ int ret = -1;
+
+ if (out_bytes)
+ ret = mrvl_spi_xfer(slave, out_bytes * 8, dout, din);
+ else if (in_bytes)
+ ret = mrvl_spi_xfer(slave, in_bytes * 8, dout, din);
+ else
+ die("Unexpected condition in spi_xfer\n");
+ return ret;
+}