blob: 908e6ca476052ba2c0aee35de0c85b258197dae6 [file] [log] [blame]
Maximilian Brune2ccb8e72024-01-14 21:59:27 +06001/* SPDX-License-Identifier: GPL-2.0-only */
2
3//TODO needs work
4
5#include <device/mmio.h>
6#include <soc/spi.h>
7#include <soc/clock.h>
8#include <soc/addressmap.h>
9#include <delay.h>
10#include <lib.h>
11
12#include "spi_internal.h"
13
14static struct fu740_spi_ctrl *fu740_spi_ctrls[] = {
15 (struct fu740_spi_ctrl *)FU740_QSPI0,
16 (struct fu740_spi_ctrl *)FU740_QSPI1,
17 (struct fu740_spi_ctrl *)FU740_QSPI2
18};
19
20// Wait until SPI is ready for transmission and transmit byte.
21static void fu740_spi_tx(volatile struct fu740_spi_ctrl *spictrl_reg, uint8_t in)
22{
23#if __riscv_atomic
24 int32_t r;
25 do {
26 asm volatile (
27 "amoor.w %0, %2, %1\n"
28 : "=r" (r), "+A" (spictrl_reg->txdata.raw_bits)
29 : "r" (in)
30 );
31 } while (r < 0);
32#else
33 while ((int32_t) spictrl_reg->txdata.raw_bits < 0)
34 ;
35 spictrl_reg->txdata.data = in;
36#endif
37}
38
39// Wait until SPI receive queue has data and read byte.
40static uint8_t fu740_spi_rx(volatile struct fu740_spi_ctrl *spictrl_reg)
41{
42 int32_t out;
43 while ((out = (int32_t) spictrl_reg->rxdata.raw_bits) < 0)
44 ;
45 return (uint8_t) out;
46}
47
48static int fu740_spi_xfer(const struct spi_slave *slave,
49 const void *dout, size_t bytesout,
50 void *din, size_t bytesin)
51{
52 printk(BIOS_DEBUG, "%s 0, bytesin: %zu, bytesout: %zu, din: %p\n", __func__, bytesin, bytesout, din);
53 hexdump(dout, bytesout);
54 struct fu740_spi_ctrl *spictrl_reg = fu740_spi_ctrls[slave->bus];
55 union fu740_spi_reg_fmt fmt;
56 fmt.raw_bits = read32(&spictrl_reg->fmt.raw_bits);
57 if (fmt.proto == FU740_SPI_PROTO_S) {
58 // working in full-duplex mode
59 // receiving data needs to be triggered by sending data
60 while (bytesout || bytesin) {
61 uint8_t in, out = 0;
62 if (bytesout) {
63 out = *(uint8_t *)dout++;
64 bytesout--;
65 }
66 fu740_spi_tx(spictrl_reg, out);
67 in = fu740_spi_rx(spictrl_reg);
68 if (bytesin) {
69 *(uint8_t *)din++ = in;
70 bytesin--;
71 }
72 }
73 } else {
74 // Working in half duplex
75 // send and receive can be done separately
76 if (dout && din)
77 return -1;
78
79 if (dout) {
80 while (bytesout) {
81 fu740_spi_tx(spictrl_reg, *(uint8_t *)dout++);
82 bytesout--;
83 }
84 }
85
86 if (din) {
87 while (bytesin) {
88 *(uint8_t *)din++ = fu740_spi_rx(spictrl_reg);
89 bytesin--;
90 }
91 }
92 }
93 return 0;
94}
95
96static int fu740_spi_claim_bus(const struct spi_slave *slave)
97{
98 struct fu740_spi_ctrl *spictrl = fu740_spi_ctrls[slave->bus];
99 union fu740_spi_reg_csmode csmode;
100 csmode.raw_bits = 0;
101 csmode.mode = FU740_SPI_CSMODE_HOLD;
102 write32(&spictrl->csmode.raw_bits, csmode.raw_bits);
103 return 0;
104}
105
106static void fu740_spi_release_bus(const struct spi_slave *slave)
107{
108 struct fu740_spi_ctrl *spictrl = fu740_spi_ctrls[slave->bus];
109 union fu740_spi_reg_csmode csmode;
110 csmode.raw_bits = 0;
111 csmode.mode = FU740_SPI_CSMODE_OFF;
112 write32(&spictrl->csmode.raw_bits, csmode.raw_bits);
113}
114
115// reset spi flash chip
116static void fu740_spi_reset(volatile struct fu740_spi_ctrl *spictrl_reg)
117{
118 fu740_spi_tx(spictrl_reg, 0x66);
119 fu740_spi_tx(spictrl_reg, 0x99);
120}
121
122// setup the ffmt (SPI flash instruction format) register
123__maybe_unused static int fu740_spi_setup_ffmt(volatile struct fu740_spi_ctrl *spictrl_reg,
124 const struct fu740_spi_config *config)
125{
126 //union fu740_spi_reg_fctrl fctrl;
127 union fu740_spi_reg_ffmt ffmt;
128
129 printk(BIOS_DEBUG, "config->data_proto: %d, config->cmd_code: %d\n",
130 config->ffmt_config.data_proto,
131 config->ffmt_config.cmd_code);
132
133 //TODO test without this here
134 fu740_spi_reset(spictrl_reg);
135
136 ffmt.raw_bits = 0;
137 ffmt.cmd_en = 1; // enable sending of command
138 ffmt.addr_len = 3; // number of address bytes (0-4)
139 ffmt.pad_cnt = 0; // number of dummy cycles TODO maybe not working
140 ffmt.cmd_proto = FU740_SPI_PROTO_S; // protocol for transmitting command
141 ffmt.addr_proto = FU740_SPI_PROTO_S; // protocol for transmitting address and padding
142 ffmt.data_proto = config->ffmt_config.data_proto; // protocol for receiving data bytes
143 ffmt.cmd_code = config->ffmt_config.cmd_code; // value of command byte
144 ffmt.pad_code = 0; // First 8 bits to transmit during dummy cycles
145 write32(&spictrl_reg->ffmt.raw_bits, ffmt.raw_bits);
146
147 return 0;
148}
149
150int fu740_spi_setup(const struct spi_slave *slave)
151{
152 union fu740_spi_reg_csmode csmode;
153 union fu740_spi_reg_fctrl fctrl;
154
155 struct fu740_spi_ctrl *spictrl_reg = fu740_spi_ctrls[slave->bus];
156 struct fu740_spi_config *config = &fu740_spi_configs[slave->bus];
157
158 if ((config->pha > 1)
159 || (config->pol > 1)
160 || (config->fmt_config.protocol > 2)
161 || (config->fmt_config.endianness > 1)
162 || (config->fmt_config.bits_per_frame > 8))
163 return -1;
164
165 write32(&spictrl_reg->sckdiv, fu740_spi_min_clk_divisor(clock_get_pclk(), config->freq));
166
167 /* disable direct memory-mapped spi flash mode */
168 //TODO test if we need to disable it before changing the settings
169 fctrl.raw_bits = 0;
170 fctrl.en = 0;
171 write32(&spictrl_reg->fctrl.raw_bits, fctrl.raw_bits);
172
173 csmode.raw_bits = 0;
174 csmode.mode = FU740_SPI_CSMODE_HOLD;
175 write32(&spictrl_reg->csmode.raw_bits, csmode.raw_bits);
176 char din[10];
177 char dout[10] = { 0x66 };
178 slave->ctrlr->xfer(slave, dout, 1, din, 0);
179 dout[0] = 0x99;
180 slave->ctrlr->xfer(slave, dout, 1, din, 0);
181 int addr = 0x200;
182 dout[0] = 0x03;
183 dout[1] = (addr >> 16) & 0xFF;
184 dout[2] = (addr >> 8) & 0xFF;
185 dout[3] = addr & 0xFF;
186 slave->ctrlr->xfer(slave, dout, 4, din, 10);
187 csmode.mode = FU740_SPI_CSMODE_AUTO;
188 write32(&spictrl_reg->csmode.raw_bits, csmode.raw_bits);
189 din[9] = 0;
190 return 0;
191}
192
193struct spi_ctrlr fu740_spi_ctrlr = {
194 .xfer = fu740_spi_xfer,
195 .setup = fu740_spi_setup,
196 .claim_bus = fu740_spi_claim_bus,
197 .release_bus = fu740_spi_release_bus,
198 .max_xfer_size = SPI_CTRLR_DEFAULT_MAX_XFER_SIZE,
199};
200
201const struct spi_ctrlr_buses spi_ctrlr_bus_map[] = {
202 {
203 .bus_start = 0,
204 .bus_end = 2,
205 .ctrlr = &fu740_spi_ctrlr,
206 }
207};
208
209const size_t spi_ctrlr_bus_map_count = ARRAY_SIZE(spi_ctrlr_bus_map);