summaryrefslogtreecommitdiff
path: root/src/soc/sifive/fu540/spi.c
diff options
context:
space:
mode:
authorXiang Wang <wxjstz@126.com>2019-05-29 15:39:48 +0800
committerPatrick Rudolph <siro@das-labor.org>2019-08-12 08:35:17 +0000
commitf4e158337683640220e3662fd6ca135bd3bad31b (patch)
treeecf48ac9a68ffe399fd64b40986cc53754470651 /src/soc/sifive/fu540/spi.c
parent8adaffcbed6372970f34b85177bd42bb508d03e2 (diff)
downloadcoreboot-f4e158337683640220e3662fd6ca135bd3bad31b.tar.xz
soc/sifive/fu540: add code for spi and map flash to memory spaces
SiFive's ZSBL has initialized flash, but only 16MB of space is available. 1. add code for spi 2. add code to map flash to memory spaces Change-Id: I106688c65ac7dd70be7479dc4691797b700682d9 Signed-off-by: Xiang Wang <merle@hardenedlinux.org> Reviewed-on: https://review.coreboot.org/c/coreboot/+/33055 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Patrick Rudolph <siro@das-labor.org>
Diffstat (limited to 'src/soc/sifive/fu540/spi.c')
-rw-r--r--src/soc/sifive/fu540/spi.c254
1 files changed, 254 insertions, 0 deletions
diff --git a/src/soc/sifive/fu540/spi.c b/src/soc/sifive/fu540/spi.c
new file mode 100644
index 0000000000..6bf1700767
--- /dev/null
+++ b/src/soc/sifive/fu540/spi.c
@@ -0,0 +1,254 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2018 SiFive, Inc
+ * Copyright (C) 2019 HardenedLinux
+ *
+ * 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/mmio.h>
+#include <soc/spi.h>
+#include <soc/clock.h>
+#include <soc/addressmap.h>
+
+#include "spi_internal.h"
+
+static struct spi_ctrl *spictrls[] = {
+ (struct spi_ctrl *)FU540_QSPI0,
+ (struct spi_ctrl *)FU540_QSPI1,
+ (struct spi_ctrl *)FU540_QSPI2
+};
+
+
+/**
+ * Wait until SPI is ready for transmission and transmit byte.
+ */
+static void spi_tx(volatile struct spi_ctrl *spictrl, uint8_t in)
+{
+#if __riscv_atomic
+ int32_t r;
+ do {
+ asm volatile (
+ "amoor.w %0, %2, %1\n"
+ : "=r" (r), "+A" (spictrl->txdata.raw_bits)
+ : "r" (in)
+ );
+ } while (r < 0);
+#else
+ while ((int32_t) spictrl->txdata.raw_bits < 0)
+ ;
+ spictrl->txdata.data = in;
+#endif
+}
+
+
+/**
+ * Wait until SPI receive queue has data and read byte.
+ */
+static uint8_t spi_rx(volatile struct spi_ctrl *spictrl)
+{
+ int32_t out;
+ while ((out = (int32_t) spictrl->rxdata.raw_bits) < 0)
+ ;
+ return (uint8_t) out;
+}
+
+static int spi_xfer_(const struct spi_slave *slave,
+ const void *dout, size_t bytesout,
+ void *din, size_t bytesin)
+{
+ struct spi_ctrl *spictrl = spictrls[slave->bus];
+ spi_reg_fmt fmt;
+ fmt.raw_bits = read32(&spictrl->fmt.raw_bits);
+ if (fmt.proto == FU540_SPI_PROTO_S) {
+ /* working in full-duplex mode
+ * receiving data needs to be triggered by sending data */
+ while (bytesout || bytesin) {
+ uint8_t in, out = 0;
+ if (bytesout) {
+ out = *(uint8_t *)dout++;
+ bytesout--;
+ }
+ spi_tx(spictrl, out);
+ in = spi_rx(spictrl);
+ if (bytesin) {
+ *(uint8_t *)din++ = in;
+ bytesin--;
+ }
+ }
+ } else {
+ /* Working in half duplex
+ * send and receive can be done separately */
+ if (dout && din)
+ return -1;
+
+ if (dout) {
+ while (bytesout) {
+ spi_tx(spictrl, *(uint8_t *)dout++);
+ bytesout--;
+ }
+ }
+
+ if (din) {
+ while (bytesin) {
+ *(uint8_t *)din++ = spi_rx(spictrl);
+ bytesin--;
+ }
+ }
+ }
+ return 0;
+}
+
+static int spi_setup_(const struct spi_slave *slave)
+{
+ spi_reg_sckmode sckmode;
+ spi_reg_csmode csmode;
+ spi_reg_fmt fmt;
+
+ if ((slave->bus > 2) || (slave->cs != 0))
+ return -1;
+
+ struct spi_ctrl *spictrl = spictrls[slave->bus];
+
+ write32(&spictrl->sckdiv, spi_min_clk_divisor(clock_get_tlclk_khz(),
+ 10000));
+
+ sckmode.raw_bits = 0;
+ sckmode.pha = FU540_SPI_PHA_LOW;
+ sckmode.pol = FU540_SPI_POL_LEADING;
+ write32(&spictrl->sckmode.raw_bits, sckmode.raw_bits);
+
+ csmode.raw_bits = 0;
+ csmode.mode = FU540_SPI_CSMODE_AUTO;
+ write32(&spictrl->csmode.raw_bits, csmode.raw_bits);
+
+ fmt.raw_bits = 0;
+ fmt.proto = FU540_SPI_PROTO_S;
+ fmt.endian = FU540_SPI_ENDIAN_BIG;
+ fmt.dir = 1;
+ fmt.len = 8;
+ write32(&spictrl->fmt.raw_bits, fmt.raw_bits);
+
+ return 0;
+}
+
+struct spi_ctrlr fu540_spi_ctrlr = {
+ .xfer = spi_xfer_,
+ .setup = spi_setup_,
+};
+
+const struct spi_ctrlr_buses spi_ctrlr_bus_map[] = {
+ {
+ .bus_start = 0,
+ .bus_end = 2,
+ .ctrlr = &fu540_spi_ctrlr,
+ }
+};
+
+const size_t spi_ctrlr_bus_map_count = ARRAY_SIZE(spi_ctrlr_bus_map);
+
+int fu540_spi_setup(unsigned int bus, unsigned int cs,
+ struct spi_slave *slave,
+ struct fu540_spi_config *config)
+{
+ spi_reg_sckmode sckmode;
+ spi_reg_csmode csmode;
+ spi_reg_fmt fmt;
+
+ if ((bus > 2) || (cs != 0))
+ return -1;
+
+ if ((config->pha > 1)
+ || (config->pol > 1)
+ || (config->protocol > 2)
+ || (config->endianness > 1)
+ || (config->bits_per_frame > 8))
+ return -1;
+
+ slave->bus = bus;
+ slave->cs = cs;
+ slave->ctrlr = &fu540_spi_ctrlr;
+
+ struct spi_ctrl *spictrl = spictrls[slave->bus];
+
+ write32(&spictrl->sckdiv, spi_min_clk_divisor(clock_get_tlclk_khz(),
+ config->freq / 1000));
+
+ sckmode.raw_bits = 0;
+ sckmode.pha = config->pha;
+ sckmode.pol = config->pol;
+ write32(&spictrl->sckmode.raw_bits, sckmode.raw_bits);
+
+ csmode.raw_bits = 0;
+ csmode.mode = FU540_SPI_CSMODE_AUTO;
+ write32(&spictrl->csmode.raw_bits, csmode.raw_bits);
+
+ fmt.raw_bits = 0;
+ fmt.proto = config->protocol;
+ fmt.endian = config->endianness;
+ fmt.dir = 1;
+ fmt.len = config->bits_per_frame;
+ write32(&spictrl->fmt.raw_bits, fmt.raw_bits);
+
+ return 0;
+}
+
+int fu540_spi_mmap(
+ const struct spi_slave *slave,
+ const struct fu540_spi_mmap_config *config)
+{
+ spi_reg_fctrl fctrl;
+ spi_reg_ffmt ffmt;
+
+ if (slave->bus > 2)
+ return -1;
+
+ if ((config->cmd_en > 1)
+ || (config->addr_len > 4)
+ || (config->pad_cnt > 15)
+ || (config->cmd_proto > 2)
+ || (config->addr_proto > 2)
+ || (config->data_proto > 2)
+ || (config->cmd_code > 255)
+ || (config->pad_code > 255))
+ return -1;
+
+ struct spi_ctrl *spictrl = spictrls[slave->bus];
+
+ /* disable direct memory-mapped spi flash mode */
+ fctrl.raw_bits = 0;
+ fctrl.en = 0;
+ write32(&spictrl->fctrl.raw_bits, fctrl.raw_bits);
+
+ /* reset spi flash chip */
+ spi_tx(spictrl, 0x66);
+ spi_tx(spictrl, 0x99);
+
+ /* Pass the information of the flash read operation to the spi
+ * controller */
+ ffmt.raw_bits = 0;
+ ffmt.cmd_en = config->cmd_en;
+ ffmt.addr_len = config->addr_len;
+ ffmt.pad_cnt = config->pad_cnt;
+ ffmt.command_proto = config->cmd_proto;
+ ffmt.addr_proto = config->addr_proto;
+ ffmt.data_proto = config->data_proto;
+ ffmt.command_code = config->cmd_code;
+ ffmt.pad_code = config->pad_code;
+ write32(&spictrl->ffmt.raw_bits, ffmt.raw_bits);
+
+ /* enable direct memory-mapped spi flash mode */
+ fctrl.raw_bits = 0;
+ fctrl.en = 1;
+ write32(&spictrl->fctrl.raw_bits, fctrl.raw_bits);
+
+ return 0;
+}