blob: 879d04a598ad791e1f9e2c1d809a2f7c7e2f1e9f [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>
Aaron Durbinc6588c52015-05-15 13:15:34 -05005#include <boot_device.h>
Julius Wernerfa938c72013-08-29 14:17:36 -07006#include <console/console.h>
Julius Werner1ed0c8c2014-10-20 13:16:29 -07007#include <soc/clk.h>
8#include <soc/gpio.h>
9#include <soc/spi.h>
Elyes HAOUAS7884c222020-07-27 08:12:59 +020010#include <stddef.h>
11#include <stdint.h>
Aaron Durbinc6588c52015-05-15 13:15:34 -050012#include <symbols.h>
Hung-Te Lin7e494052013-01-30 20:02:02 +080013
Hung-Te Lin7e494052013-01-30 20:02:02 +080014#if defined(CONFIG_DEBUG_SPI) && CONFIG_DEBUG_SPI
15# define DEBUG_SPI(x,...) printk(BIOS_DEBUG, "EXYNOS_SPI: " x)
16#else
17# define DEBUG_SPI(x,...)
18#endif
19
20static void exynos_spi_rx_tx(struct exynos_spi *regs, int todo,
21 void *dinp, void const *doutp, int i)
22{
23 int rx_lvl, tx_lvl;
Stefan Reinauer1cf46a72013-02-14 17:06:43 -080024 unsigned int *rxp = (unsigned int *)(dinp + (i * (32 * 1024)));
25 unsigned int out_bytes, in_bytes;
Hung-Te Lin7e494052013-01-30 20:02:02 +080026
Martin Roth4c3ab732013-07-08 16:23:54 -060027 // TODO In current implementation, every read/write must be aligned to
Hung-Te Lin7e494052013-01-30 20:02:02 +080028 // 4 bytes, otherwise you may get timeout or other unexpected results.
Stefan Reinauer08dc3572013-05-14 16:57:50 -070029 ASSERT(todo % 4 == 0);
Hung-Te Lin7e494052013-01-30 20:02:02 +080030
31 out_bytes = in_bytes = todo;
Julius Werner55009af2019-12-02 22:03:27 -080032 setbits32(&regs->ch_cfg, SPI_CH_RST);
33 clrbits32(&regs->ch_cfg, SPI_CH_RST);
Julius Werner2f37bd62015-02-19 14:51:15 -080034 write32(&regs->pkt_cnt, ((todo * 8) / 32) | SPI_PACKET_CNT_EN);
Hung-Te Lin7e494052013-01-30 20:02:02 +080035
36 while (in_bytes) {
37 uint32_t spi_sts;
38 int temp;
39
Julius Werner2f37bd62015-02-19 14:51:15 -080040 spi_sts = read32(&regs->spi_sts);
Hung-Te Lin7e494052013-01-30 20:02:02 +080041 rx_lvl = ((spi_sts >> 15) & 0x7f);
42 tx_lvl = ((spi_sts >> 6) & 0x7f);
43 while (tx_lvl < 32 && out_bytes) {
44 // TODO The "writing" (tx) is not supported now; that's
45 // why we write garbage to keep driving FIFO clock.
46 temp = 0xffffffff;
Julius Werner2f37bd62015-02-19 14:51:15 -080047 write32(&regs->tx_data, temp);
Hung-Te Lin7e494052013-01-30 20:02:02 +080048 out_bytes -= 4;
49 tx_lvl += 4;
50 }
51 while (rx_lvl >= 4 && in_bytes) {
Julius Werner2f37bd62015-02-19 14:51:15 -080052 temp = read32(&regs->rx_data);
Hung-Te Lin7e494052013-01-30 20:02:02 +080053 if (rxp)
54 *rxp++ = temp;
55 in_bytes -= 4;
56 rx_lvl -= 4;
57 }
58 }
59}
60
David Hendricks0b153bd2013-02-09 17:24:17 -080061/* set up SPI channel */
Hung-Te Lin7e494052013-01-30 20:02:02 +080062int exynos_spi_open(struct exynos_spi *regs)
63{
Hung-Te Lin7e494052013-01-30 20:02:02 +080064 /* set the spi1 GPIO */
65
Hung-Te Lin7e494052013-01-30 20:02:02 +080066 /* set pktcnt and enable it */
Julius Werner2f37bd62015-02-19 14:51:15 -080067 write32(&regs->pkt_cnt, 4 | SPI_PACKET_CNT_EN);
Hung-Te Lin7e494052013-01-30 20:02:02 +080068 /* set FB_CLK_SEL */
Julius Werner2f37bd62015-02-19 14:51:15 -080069 write32(&regs->fb_clk, SPI_FB_DELAY_180);
Hung-Te Lin7e494052013-01-30 20:02:02 +080070 /* set CH_WIDTH and BUS_WIDTH as word */
Julius Werner55009af2019-12-02 22:03:27 -080071 setbits32(&regs->mode_cfg,
Hung-Te Lin7e494052013-01-30 20:02:02 +080072 SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
Julius Werner55009af2019-12-02 22:03:27 -080073 clrbits32(&regs->ch_cfg, SPI_CH_CPOL_L); /* CPOL: active high */
Hung-Te Lin7e494052013-01-30 20:02:02 +080074
Martin Roth4c3ab732013-07-08 16:23:54 -060075 /* clear rx and tx channel if set previously */
Julius Werner55009af2019-12-02 22:03:27 -080076 clrbits32(&regs->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON);
Hung-Te Lin7e494052013-01-30 20:02:02 +080077
Julius Werner55009af2019-12-02 22:03:27 -080078 setbits32(&regs->swap_cfg,
79 SPI_RX_SWAP_EN | SPI_RX_BYTE_SWAP | SPI_RX_HWORD_SWAP);
Hung-Te Lin7e494052013-01-30 20:02:02 +080080
81 /* do a soft reset */
Julius Werner55009af2019-12-02 22:03:27 -080082 setbits32(&regs->ch_cfg, SPI_CH_RST);
83 clrbits32(&regs->ch_cfg, SPI_CH_RST);
Hung-Te Lin7e494052013-01-30 20:02:02 +080084
85 /* now set rx and tx channel ON */
Julius Werner55009af2019-12-02 22:03:27 -080086 setbits32(&regs->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON | SPI_CH_HS_EN);
Hung-Te Lin7e494052013-01-30 20:02:02 +080087 return 0;
88}
89
90int exynos_spi_read(struct exynos_spi *regs, void *dest, u32 len, u32 off)
91{
92 int upto, todo;
93 int i;
Julius Werner55009af2019-12-02 22:03:27 -080094 clrbits32(&regs->cs_reg, SPI_SLAVE_SIG_INACT); /* CS low */
Hung-Te Line42030d2013-06-23 08:14:30 +080095
Hung-Te Lin7e494052013-01-30 20:02:02 +080096 /* Send read instruction (0x3h) followed by a 24 bit addr */
Julius Werner2f37bd62015-02-19 14:51:15 -080097 write32(&regs->tx_data, (SF_READ_DATA_CMD << 24) | off);
Hung-Te Lin7e494052013-01-30 20:02:02 +080098
99 /* waiting for TX done */
Julius Werner2f37bd62015-02-19 14:51:15 -0800100 while (!(read32(&regs->spi_sts) & SPI_ST_TX_DONE));
Hung-Te Lin7e494052013-01-30 20:02:02 +0800101
102 for (upto = 0, i = 0; upto < len; upto += todo, i++) {
103 todo = MIN(len - upto, (1 << 15));
104 exynos_spi_rx_tx(regs, todo, dest, (void *)(off), i);
105 }
106
Julius Werner55009af2019-12-02 22:03:27 -0800107 setbits32(&regs->cs_reg, SPI_SLAVE_SIG_INACT);/* make the CS high */
Hung-Te Lin7e494052013-01-30 20:02:02 +0800108
Hung-Te Line42030d2013-06-23 08:14:30 +0800109 return len;
110}
111
112int exynos_spi_close(struct exynos_spi *regs)
113{
Hung-Te Lin7e494052013-01-30 20:02:02 +0800114 /*
115 * Let put controller mode to BYTE as
116 * SPI driver does not support WORD mode yet
117 */
Julius Werner55009af2019-12-02 22:03:27 -0800118 clrbits32(&regs->mode_cfg,
119 SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
Julius Werner2f37bd62015-02-19 14:51:15 -0800120 write32(&regs->swap_cfg, 0);
Hung-Te Lin7e494052013-01-30 20:02:02 +0800121
Hung-Te Lin7e494052013-01-30 20:02:02 +0800122 /*
123 * Flush spi tx, rx fifos and reset the SPI controller
124 * and clear rx/tx channel
125 */
Julius Werner55009af2019-12-02 22:03:27 -0800126 clrsetbits32(&regs->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST);
127 clrbits32(&regs->ch_cfg, SPI_CH_RST);
128 clrbits32(&regs->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON);
Hung-Te Lin7e494052013-01-30 20:02:02 +0800129 return 0;
130}
131
Aaron Durbinc6588c52015-05-15 13:15:34 -0500132static struct exynos_spi *boot_slave_regs;
Hung-Te Lin7e494052013-01-30 20:02:02 +0800133
Aaron Durbinc6588c52015-05-15 13:15:34 -0500134static ssize_t exynos_spi_readat(const struct region_device *rdev, void *dest,
135 size_t offset, size_t count)
136{
Hung-Te Lin7e494052013-01-30 20:02:02 +0800137 int bytes;
138 DEBUG_SPI("exynos_spi_cbfs_read(%u)\n", count);
Aaron Durbinc6588c52015-05-15 13:15:34 -0500139 exynos_spi_open(boot_slave_regs);
140 bytes = exynos_spi_read(boot_slave_regs, dest, count, offset);
141 exynos_spi_close(boot_slave_regs);
Hung-Te Lin7e494052013-01-30 20:02:02 +0800142 return bytes;
143}
144
Aaron Durbinc6588c52015-05-15 13:15:34 -0500145static void *exynos_spi_map(const struct region_device *rdev,
146 size_t offset, size_t count)
147{
Hung-Te Lin7e494052013-01-30 20:02:02 +0800148 DEBUG_SPI("exynos_spi_cbfs_map\n");
Aaron Durbinc6588c52015-05-15 13:15:34 -0500149 // exynos: spi_rx_tx may work in 4 byte-width-transmission mode and
150 // requires buffer memory address to be aligned.
Hung-Te Lin7e494052013-01-30 20:02:02 +0800151 if (count % 4)
152 count += 4 - (count % 4);
Aaron Durbinc6588c52015-05-15 13:15:34 -0500153 return mmap_helper_rdev_mmap(rdev, offset, count);
Hung-Te Lin7e494052013-01-30 20:02:02 +0800154}
155
Aaron Durbinc6588c52015-05-15 13:15:34 -0500156static const struct region_device_ops exynos_spi_ops = {
157 .mmap = exynos_spi_map,
158 .munmap = mmap_helper_rdev_munmap,
159 .readat = exynos_spi_readat,
160};
161
162static struct mmap_helper_region_device mdev =
163 MMAP_HELPER_REGION_INIT(&exynos_spi_ops, 0, CONFIG_ROM_SIZE);
164
165void exynos_init_spi_boot_device(void)
166{
167 boot_slave_regs = (void *)EXYNOS5_SPI1_BASE;
168
Julius Werner7e0dea62019-02-20 18:39:22 -0800169 mmap_helper_device_init(&mdev, _cbfs_cache, REGION_SIZE(cbfs_cache));
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}