aboutsummaryrefslogtreecommitdiff
path: root/src/soc/sifive/fu540/spi_internal.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/soc/sifive/fu540/spi_internal.h')
-rw-r--r--src/soc/sifive/fu540/spi_internal.h242
1 files changed, 242 insertions, 0 deletions
diff --git a/src/soc/sifive/fu540/spi_internal.h b/src/soc/sifive/fu540/spi_internal.h
new file mode 100644
index 0000000000..97094c1d8c
--- /dev/null
+++ b/src/soc/sifive/fu540/spi_internal.h
@@ -0,0 +1,242 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2018 SiFive, 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.
+ */
+#ifndef __SOC_SIFIVE_HIFIVE_U_SPI_INTERNAL_H__
+#define __SOC_SIFIVE_HIFIVE_U_SPI_INTERNAL_H__
+
+#include <stdint.h>
+
+#define _ASSERT_SIZEOF(type, size) _Static_assert( \
+ sizeof(type) == (size), \
+ #type " must be " #size " bytes wide")
+
+#define FU540_SPI_CSMODE_AUTO 0
+#define FU540_SPI_CSMODE_HOLD 2
+#define FU540_SPI_CSMODE_OFF 3
+
+typedef union {
+ struct {
+ uint32_t pha : 1;
+ uint32_t pol : 1;
+ uint32_t reserved : 30;
+ };
+ uint32_t raw_bits;
+} spi_reg_sckmode;
+_ASSERT_SIZEOF(spi_reg_sckmode, 4);
+
+
+typedef union {
+ struct {
+ uint32_t mode : 2;
+ uint32_t reserved : 30;
+ };
+ uint32_t raw_bits;
+} spi_reg_csmode;
+_ASSERT_SIZEOF(spi_reg_csmode, 4);
+
+
+typedef union {
+ struct {
+ uint32_t cssck : 8;
+ uint32_t reserved0 : 8;
+ uint32_t sckcs : 8;
+ uint32_t reserved1 : 8;
+ };
+ uint32_t raw_bits;
+} spi_reg_delay0;
+_ASSERT_SIZEOF(spi_reg_delay0, 4);
+
+
+typedef union {
+ struct {
+ uint32_t intercs : 8;
+ uint32_t reserved0 : 8;
+ uint32_t interxfr : 8;
+ uint32_t reserved1 : 8;
+ };
+ uint32_t raw_bits;
+} spi_reg_delay1;
+_ASSERT_SIZEOF(spi_reg_delay1, 4);
+
+
+typedef union {
+ struct {
+ uint32_t proto : 2;
+ uint32_t endian : 1;
+ uint32_t dir : 1;
+ uint32_t reserved0 : 12;
+ uint32_t len : 4;
+ uint32_t reserved1 : 12;
+ };
+ uint32_t raw_bits;
+} spi_reg_fmt;
+_ASSERT_SIZEOF(spi_reg_fmt, 4);
+
+
+typedef union {
+ struct {
+ uint32_t data : 8;
+ uint32_t reserved : 23;
+ uint32_t full : 1;
+ };
+ uint32_t raw_bits;
+} spi_reg_txdata;
+_ASSERT_SIZEOF(spi_reg_txdata, 4);
+
+
+typedef union {
+ struct {
+ uint32_t data : 8;
+ uint32_t reserved : 23;
+ uint32_t empty : 1;
+ };
+ uint32_t raw_bits;
+} spi_reg_rxdata;
+_ASSERT_SIZEOF(spi_reg_rxdata, 4);
+
+
+typedef union {
+ struct {
+ uint32_t txmark : 3;
+ uint32_t reserved : 29;
+ };
+ uint32_t raw_bits;
+} spi_reg_txmark;
+_ASSERT_SIZEOF(spi_reg_txmark, 4);
+
+
+typedef union {
+ struct {
+ uint32_t rxmark : 3;
+ uint32_t reserved : 29;
+ };
+ uint32_t raw_bits;
+} spi_reg_rxmark;
+_ASSERT_SIZEOF(spi_reg_rxmark, 4);
+
+
+typedef union {
+ struct {
+ uint32_t en : 1;
+ uint32_t reserved : 31;
+ };
+ uint32_t raw_bits;
+} spi_reg_fctrl;
+_ASSERT_SIZEOF(spi_reg_fctrl, 4);
+
+
+typedef union {
+ struct {
+ uint32_t cmd_en : 1;
+ uint32_t addr_len : 3;
+ uint32_t pad_cnt : 4;
+ uint32_t command_proto : 2;
+ uint32_t addr_proto : 2;
+ uint32_t data_proto : 2;
+ uint32_t reserved : 2;
+ uint32_t command_code : 8;
+ uint32_t pad_code : 8;
+ };
+ uint32_t raw_bits;
+} spi_reg_ffmt;
+_ASSERT_SIZEOF(spi_reg_ffmt, 4);
+
+
+typedef union {
+ struct {
+ uint32_t txwm : 1;
+ uint32_t rxwm : 1;
+ uint32_t reserved : 30;
+ };
+ uint32_t raw_bits;
+} spi_reg_ie;
+typedef spi_reg_ie spi_reg_ip;
+_ASSERT_SIZEOF(spi_reg_ie, 4);
+_ASSERT_SIZEOF(spi_reg_ip, 4);
+
+#undef _ASSERT_SIZEOF
+
+
+/**
+ * SPI control register memory map.
+ *
+ * All functions take a pointer to a SPI device's control registers.
+ */
+struct spi_ctrl {
+ uint32_t sckdiv;
+ spi_reg_sckmode sckmode;
+ uint32_t reserved08;
+ uint32_t reserved0c;
+
+ uint32_t csid;
+ uint32_t csdef;
+ spi_reg_csmode csmode;
+ uint32_t reserved1c;
+
+ uint32_t reserved20;
+ uint32_t reserved24;
+ spi_reg_delay0 delay0;
+ spi_reg_delay1 delay1;
+
+ uint32_t reserved30;
+ uint32_t reserved34;
+ uint32_t reserved38;
+ uint32_t reserved3c;
+
+ spi_reg_fmt fmt;
+ uint32_t reserved44;
+ spi_reg_txdata txdata;
+ spi_reg_rxdata rxdata;
+
+ spi_reg_txmark txmark;
+ spi_reg_rxmark rxmark;
+ uint32_t reserved58;
+ uint32_t reserved5c;
+
+ spi_reg_fctrl fctrl;
+ spi_reg_ffmt ffmt;
+ uint32_t reserved68;
+ uint32_t reserved6c;
+
+ spi_reg_ie ie;
+ spi_reg_ip ip;
+};
+
+/**
+ * Get smallest clock divisor that divides input_khz to a quotient less than or
+ * equal to max_target_khz;
+ */
+static inline unsigned int
+spi_min_clk_divisor(unsigned int input_khz, unsigned int max_target_khz)
+{
+ // f_sck = f_in / (2 * (div + 1)) => div = (f_in / (2*f_sck)) - 1
+ //
+ // The nearest integer solution for div requires rounding up as to not
+ // exceed max_target_khz.
+ //
+ // div = ceil(f_in / (2*f_sck)) - 1
+ // = floor((f_in - 1 + 2*f_sck) / (2*f_sck)) - 1
+ //
+ // This should not overflow as long as (f_in - 1 + 2*f_sck) does not
+ // exceed 2^32 - 1, which is unlikely since we represent frequencies
+ // in kHz.
+ unsigned int quotient =
+ (input_khz + 2 * max_target_khz - 1) / (2 * max_target_khz);
+ // Avoid underflow
+ if (quotient == 0)
+ return 0;
+ return quotient - 1;
+}
+
+#endif /* __SOC_SIFIVE_HIFIVE_U_SPI_INTERNAL_H__ */