blob: c7e07071c07ace553bb04e66d8edbda4b3bc646d [file] [log] [blame]
Angel Pons1ddb8942020-04-04 18:51:26 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +08002
Kyösti Mälkki13f66502019-03-03 08:01:05 +02003#include <device/mmio.h>
Julius Werner1ed0c8c2014-10-20 13:16:29 -07004#include <assert.h>
Julius Werner9b1f3cc2020-12-30 17:30:12 -08005#include <boot_device.h>
6#include <cbfs.h>
Elyes HAOUASf7a7e652021-02-01 19:39:07 +01007#include <commonlib/region.h>
Julius Wernerfa938c72013-08-29 14:17:36 -07008#include <console/console.h>
Julius Werner1ed0c8c2014-10-20 13:16:29 -07009#include <soc/clk.h>
10#include <soc/gpio.h>
11#include <soc/spi.h>
Elyes HAOUAS7884c222020-07-27 08:12:59 +020012#include <stddef.h>
13#include <stdint.h>
Aaron Durbinc6588c52015-05-15 13:15:34 -050014#include <symbols.h>
Hung-Te Lin7e494052013-01-30 20:02:02 +080015
Hung-Te Lin7e494052013-01-30 20:02:02 +080016#if defined(CONFIG_DEBUG_SPI) && CONFIG_DEBUG_SPI
17# define DEBUG_SPI(x,...) printk(BIOS_DEBUG, "EXYNOS_SPI: " x)
18#else
19# define DEBUG_SPI(x,...)
20#endif
21
22static void exynos_spi_rx_tx(struct exynos_spi *regs, int todo,
23 void *dinp, void const *doutp, int i)
24{
25 int rx_lvl, tx_lvl;
Stefan Reinauer1cf46a72013-02-14 17:06:43 -080026 unsigned int *rxp = (unsigned int *)(dinp + (i * (32 * 1024)));
27 unsigned int out_bytes, in_bytes;
Hung-Te Lin7e494052013-01-30 20:02:02 +080028
Martin Roth4c3ab732013-07-08 16:23:54 -060029 // TODO In current implementation, every read/write must be aligned to
Hung-Te Lin7e494052013-01-30 20:02:02 +080030 // 4 bytes, otherwise you may get timeout or other unexpected results.
Stefan Reinauer08dc3572013-05-14 16:57:50 -070031 ASSERT(todo % 4 == 0);
Hung-Te Lin7e494052013-01-30 20:02:02 +080032
33 out_bytes = in_bytes = todo;
Julius Werner55009af2019-12-02 22:03:27 -080034 setbits32(&regs->ch_cfg, SPI_CH_RST);
35 clrbits32(&regs->ch_cfg, SPI_CH_RST);
Julius Werner2f37bd62015-02-19 14:51:15 -080036 write32(&regs->pkt_cnt, ((todo * 8) / 32) | SPI_PACKET_CNT_EN);
Hung-Te Lin7e494052013-01-30 20:02:02 +080037
38 while (in_bytes) {
39 uint32_t spi_sts;
40 int temp;
41
Julius Werner2f37bd62015-02-19 14:51:15 -080042 spi_sts = read32(&regs->spi_sts);
Hung-Te Lin7e494052013-01-30 20:02:02 +080043 rx_lvl = ((spi_sts >> 15) & 0x7f);
44 tx_lvl = ((spi_sts >> 6) & 0x7f);
45 while (tx_lvl < 32 && out_bytes) {
46 // TODO The "writing" (tx) is not supported now; that's
47 // why we write garbage to keep driving FIFO clock.
48 temp = 0xffffffff;
Julius Werner2f37bd62015-02-19 14:51:15 -080049 write32(&regs->tx_data, temp);
Hung-Te Lin7e494052013-01-30 20:02:02 +080050 out_bytes -= 4;
51 tx_lvl += 4;
52 }
53 while (rx_lvl >= 4 && in_bytes) {
Julius Werner2f37bd62015-02-19 14:51:15 -080054 temp = read32(&regs->rx_data);
Hung-Te Lin7e494052013-01-30 20:02:02 +080055 if (rxp)
56 *rxp++ = temp;
57 in_bytes -= 4;
58 rx_lvl -= 4;
59 }
60 }
61}
62
David Hendricks0b153bd2013-02-09 17:24:17 -080063/* set up SPI channel */
Hung-Te Lin7e494052013-01-30 20:02:02 +080064int exynos_spi_open(struct exynos_spi *regs)
65{
Hung-Te Lin7e494052013-01-30 20:02:02 +080066 /* set the spi1 GPIO */
67
Hung-Te Lin7e494052013-01-30 20:02:02 +080068 /* set pktcnt and enable it */
Julius Werner2f37bd62015-02-19 14:51:15 -080069 write32(&regs->pkt_cnt, 4 | SPI_PACKET_CNT_EN);
Hung-Te Lin7e494052013-01-30 20:02:02 +080070 /* set FB_CLK_SEL */
Julius Werner2f37bd62015-02-19 14:51:15 -080071 write32(&regs->fb_clk, SPI_FB_DELAY_180);
Hung-Te Lin7e494052013-01-30 20:02:02 +080072 /* set CH_WIDTH and BUS_WIDTH as word */
Julius Werner55009af2019-12-02 22:03:27 -080073 setbits32(&regs->mode_cfg,
Hung-Te Lin7e494052013-01-30 20:02:02 +080074 SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
Julius Werner55009af2019-12-02 22:03:27 -080075 clrbits32(&regs->ch_cfg, SPI_CH_CPOL_L); /* CPOL: active high */
Hung-Te Lin7e494052013-01-30 20:02:02 +080076
Martin Roth4c3ab732013-07-08 16:23:54 -060077 /* clear rx and tx channel if set previously */
Julius Werner55009af2019-12-02 22:03:27 -080078 clrbits32(&regs->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON);
Hung-Te Lin7e494052013-01-30 20:02:02 +080079
Julius Werner55009af2019-12-02 22:03:27 -080080 setbits32(&regs->swap_cfg,
81 SPI_RX_SWAP_EN | SPI_RX_BYTE_SWAP | SPI_RX_HWORD_SWAP);
Hung-Te Lin7e494052013-01-30 20:02:02 +080082
83 /* do a soft reset */
Julius Werner55009af2019-12-02 22:03:27 -080084 setbits32(&regs->ch_cfg, SPI_CH_RST);
85 clrbits32(&regs->ch_cfg, SPI_CH_RST);
Hung-Te Lin7e494052013-01-30 20:02:02 +080086
87 /* now set rx and tx channel ON */
Julius Werner55009af2019-12-02 22:03:27 -080088 setbits32(&regs->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON | SPI_CH_HS_EN);
Hung-Te Lin7e494052013-01-30 20:02:02 +080089 return 0;
90}
91
92int exynos_spi_read(struct exynos_spi *regs, void *dest, u32 len, u32 off)
93{
94 int upto, todo;
95 int i;
Julius Werner55009af2019-12-02 22:03:27 -080096 clrbits32(&regs->cs_reg, SPI_SLAVE_SIG_INACT); /* CS low */
Hung-Te Line42030d2013-06-23 08:14:30 +080097
Hung-Te Lin7e494052013-01-30 20:02:02 +080098 /* Send read instruction (0x3h) followed by a 24 bit addr */
Julius Werner2f37bd62015-02-19 14:51:15 -080099 write32(&regs->tx_data, (SF_READ_DATA_CMD << 24) | off);
Hung-Te Lin7e494052013-01-30 20:02:02 +0800100
101 /* waiting for TX done */
Julius Werner2f37bd62015-02-19 14:51:15 -0800102 while (!(read32(&regs->spi_sts) & SPI_ST_TX_DONE));
Hung-Te Lin7e494052013-01-30 20:02:02 +0800103
104 for (upto = 0, i = 0; upto < len; upto += todo, i++) {
105 todo = MIN(len - upto, (1 << 15));
106 exynos_spi_rx_tx(regs, todo, dest, (void *)(off), i);
107 }
108
Julius Werner55009af2019-12-02 22:03:27 -0800109 setbits32(&regs->cs_reg, SPI_SLAVE_SIG_INACT);/* make the CS high */
Hung-Te Lin7e494052013-01-30 20:02:02 +0800110
Hung-Te Line42030d2013-06-23 08:14:30 +0800111 return len;
112}
113
114int exynos_spi_close(struct exynos_spi *regs)
115{
Hung-Te Lin7e494052013-01-30 20:02:02 +0800116 /*
117 * Let put controller mode to BYTE as
118 * SPI driver does not support WORD mode yet
119 */
Julius Werner55009af2019-12-02 22:03:27 -0800120 clrbits32(&regs->mode_cfg,
121 SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
Julius Werner2f37bd62015-02-19 14:51:15 -0800122 write32(&regs->swap_cfg, 0);
Hung-Te Lin7e494052013-01-30 20:02:02 +0800123
Hung-Te Lin7e494052013-01-30 20:02:02 +0800124 /*
125 * Flush spi tx, rx fifos and reset the SPI controller
126 * and clear rx/tx channel
127 */
Julius Werner55009af2019-12-02 22:03:27 -0800128 clrsetbits32(&regs->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST);
129 clrbits32(&regs->ch_cfg, SPI_CH_RST);
130 clrbits32(&regs->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON);
Hung-Te Lin7e494052013-01-30 20:02:02 +0800131 return 0;
132}
133
Aaron Durbinc6588c52015-05-15 13:15:34 -0500134static struct exynos_spi *boot_slave_regs;
Hung-Te Lin7e494052013-01-30 20:02:02 +0800135
Aaron Durbinc6588c52015-05-15 13:15:34 -0500136static ssize_t exynos_spi_readat(const struct region_device *rdev, void *dest,
137 size_t offset, size_t count)
138{
Hung-Te Lin7e494052013-01-30 20:02:02 +0800139 int bytes;
140 DEBUG_SPI("exynos_spi_cbfs_read(%u)\n", count);
Aaron Durbinc6588c52015-05-15 13:15:34 -0500141 exynos_spi_open(boot_slave_regs);
142 bytes = exynos_spi_read(boot_slave_regs, dest, count, offset);
143 exynos_spi_close(boot_slave_regs);
Hung-Te Lin7e494052013-01-30 20:02:02 +0800144 return bytes;
145}
146
Aaron Durbinc6588c52015-05-15 13:15:34 -0500147static void *exynos_spi_map(const struct region_device *rdev,
148 size_t offset, size_t count)
149{
Hung-Te Lin7e494052013-01-30 20:02:02 +0800150 DEBUG_SPI("exynos_spi_cbfs_map\n");
Aaron Durbinc6588c52015-05-15 13:15:34 -0500151 // exynos: spi_rx_tx may work in 4 byte-width-transmission mode and
152 // requires buffer memory address to be aligned.
Hung-Te Lin7e494052013-01-30 20:02:02 +0800153 if (count % 4)
154 count += 4 - (count % 4);
Aaron Durbinc6588c52015-05-15 13:15:34 -0500155 return mmap_helper_rdev_mmap(rdev, offset, count);
Hung-Te Lin7e494052013-01-30 20:02:02 +0800156}
157
Aaron Durbinc6588c52015-05-15 13:15:34 -0500158static const struct region_device_ops exynos_spi_ops = {
159 .mmap = exynos_spi_map,
160 .munmap = mmap_helper_rdev_munmap,
161 .readat = exynos_spi_readat,
162};
163
164static struct mmap_helper_region_device mdev =
Julius Werner9b1f3cc2020-12-30 17:30:12 -0800165 MMAP_HELPER_DEV_INIT(&exynos_spi_ops, 0, CONFIG_ROM_SIZE, &cbfs_cache);
Aaron Durbinc6588c52015-05-15 13:15:34 -0500166
167void exynos_init_spi_boot_device(void)
168{
169 boot_slave_regs = (void *)EXYNOS5_SPI1_BASE;
Hung-Te Lin7e494052013-01-30 20:02:02 +0800170}
171
Aaron Durbinc6588c52015-05-15 13:15:34 -0500172const struct region_device *exynos_spi_boot_device(void)
173{
174 return &mdev.rdev;
Hung-Te Lin7e494052013-01-30 20:02:02 +0800175}