drivers/wifi: Remove unnecessary data structure copy
[coreboot2.git] / src / soc / samsung / exynos5250 / spi.c
blobc7e07071c07ace553bb04e66d8edbda4b3bc646d
1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <device/mmio.h>
4 #include <assert.h>
5 #include <boot_device.h>
6 #include <cbfs.h>
7 #include <commonlib/region.h>
8 #include <console/console.h>
9 #include <soc/clk.h>
10 #include <soc/gpio.h>
11 #include <soc/spi.h>
12 #include <stddef.h>
13 #include <stdint.h>
14 #include <symbols.h>
16 #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
22 static void exynos_spi_rx_tx(struct exynos_spi *regs, int todo,
23 void *dinp, void const *doutp, int i)
25 int rx_lvl, tx_lvl;
26 unsigned int *rxp = (unsigned int *)(dinp + (i * (32 * 1024)));
27 unsigned int out_bytes, in_bytes;
29 // TODO In current implementation, every read/write must be aligned to
30 // 4 bytes, otherwise you may get timeout or other unexpected results.
31 ASSERT(todo % 4 == 0);
33 out_bytes = in_bytes = todo;
34 setbits32(&regs->ch_cfg, SPI_CH_RST);
35 clrbits32(&regs->ch_cfg, SPI_CH_RST);
36 write32(&regs->pkt_cnt, ((todo * 8) / 32) | SPI_PACKET_CNT_EN);
38 while (in_bytes) {
39 uint32_t spi_sts;
40 int temp;
42 spi_sts = read32(&regs->spi_sts);
43 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;
49 write32(&regs->tx_data, temp);
50 out_bytes -= 4;
51 tx_lvl += 4;
53 while (rx_lvl >= 4 && in_bytes) {
54 temp = read32(&regs->rx_data);
55 if (rxp)
56 *rxp++ = temp;
57 in_bytes -= 4;
58 rx_lvl -= 4;
63 /* set up SPI channel */
64 int exynos_spi_open(struct exynos_spi *regs)
66 /* set the spi1 GPIO */
68 /* set pktcnt and enable it */
69 write32(&regs->pkt_cnt, 4 | SPI_PACKET_CNT_EN);
70 /* set FB_CLK_SEL */
71 write32(&regs->fb_clk, SPI_FB_DELAY_180);
72 /* set CH_WIDTH and BUS_WIDTH as word */
73 setbits32(&regs->mode_cfg,
74 SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
75 clrbits32(&regs->ch_cfg, SPI_CH_CPOL_L); /* CPOL: active high */
77 /* clear rx and tx channel if set previously */
78 clrbits32(&regs->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON);
80 setbits32(&regs->swap_cfg,
81 SPI_RX_SWAP_EN | SPI_RX_BYTE_SWAP | SPI_RX_HWORD_SWAP);
83 /* do a soft reset */
84 setbits32(&regs->ch_cfg, SPI_CH_RST);
85 clrbits32(&regs->ch_cfg, SPI_CH_RST);
87 /* now set rx and tx channel ON */
88 setbits32(&regs->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON | SPI_CH_HS_EN);
89 return 0;
92 int exynos_spi_read(struct exynos_spi *regs, void *dest, u32 len, u32 off)
94 int upto, todo;
95 int i;
96 clrbits32(&regs->cs_reg, SPI_SLAVE_SIG_INACT); /* CS low */
98 /* Send read instruction (0x3h) followed by a 24 bit addr */
99 write32(&regs->tx_data, (SF_READ_DATA_CMD << 24) | off);
101 /* waiting for TX done */
102 while (!(read32(&regs->spi_sts) & SPI_ST_TX_DONE));
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);
109 setbits32(&regs->cs_reg, SPI_SLAVE_SIG_INACT);/* make the CS high */
111 return len;
114 int exynos_spi_close(struct exynos_spi *regs)
117 * Let put controller mode to BYTE as
118 * SPI driver does not support WORD mode yet
120 clrbits32(&regs->mode_cfg,
121 SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
122 write32(&regs->swap_cfg, 0);
125 * Flush spi tx, rx fifos and reset the SPI controller
126 * and clear rx/tx channel
128 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);
131 return 0;
134 static struct exynos_spi *boot_slave_regs;
136 static ssize_t exynos_spi_readat(const struct region_device *rdev, void *dest,
137 size_t offset, size_t count)
139 int bytes;
140 DEBUG_SPI("exynos_spi_cbfs_read(%u)\n", count);
141 exynos_spi_open(boot_slave_regs);
142 bytes = exynos_spi_read(boot_slave_regs, dest, count, offset);
143 exynos_spi_close(boot_slave_regs);
144 return bytes;
147 static void *exynos_spi_map(const struct region_device *rdev,
148 size_t offset, size_t count)
150 DEBUG_SPI("exynos_spi_cbfs_map\n");
151 // exynos: spi_rx_tx may work in 4 byte-width-transmission mode and
152 // requires buffer memory address to be aligned.
153 if (count % 4)
154 count += 4 - (count % 4);
155 return mmap_helper_rdev_mmap(rdev, offset, count);
158 static const struct region_device_ops exynos_spi_ops = {
159 .mmap = exynos_spi_map,
160 .munmap = mmap_helper_rdev_munmap,
161 .readat = exynos_spi_readat,
164 static struct mmap_helper_region_device mdev =
165 MMAP_HELPER_DEV_INIT(&exynos_spi_ops, 0, CONFIG_ROM_SIZE, &cbfs_cache);
167 void exynos_init_spi_boot_device(void)
169 boot_slave_regs = (void *)EXYNOS5_SPI1_BASE;
172 const struct region_device *exynos_spi_boot_device(void)
174 return &mdev.rdev;