]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
10e8bf88 SR |
2 | /* |
3 | * Copyright (C) 2012 | |
4 | * Altera Corporation <www.altera.com> | |
10e8bf88 SR |
5 | */ |
6 | ||
7 | #include <common.h> | |
8 | #include <dm.h> | |
9 | #include <fdtdec.h> | |
10 | #include <malloc.h> | |
11 | #include <spi.h> | |
1221ce45 | 12 | #include <linux/errno.h> |
10e8bf88 SR |
13 | #include "cadence_qspi.h" |
14 | ||
15 | #define CQSPI_STIG_READ 0 | |
16 | #define CQSPI_STIG_WRITE 1 | |
17 | #define CQSPI_INDIRECT_READ 2 | |
18 | #define CQSPI_INDIRECT_WRITE 3 | |
19 | ||
20 | DECLARE_GLOBAL_DATA_PTR; | |
21 | ||
22 | static int cadence_spi_write_speed(struct udevice *bus, uint hz) | |
23 | { | |
24 | struct cadence_spi_platdata *plat = bus->platdata; | |
25 | struct cadence_spi_priv *priv = dev_get_priv(bus); | |
26 | ||
27 | cadence_qspi_apb_config_baudrate_div(priv->regbase, | |
28 | CONFIG_CQSPI_REF_CLK, hz); | |
29 | ||
30 | /* Reconfigure delay timing if speed is changed. */ | |
31 | cadence_qspi_apb_delay(priv->regbase, CONFIG_CQSPI_REF_CLK, hz, | |
32 | plat->tshsl_ns, plat->tsd2d_ns, | |
33 | plat->tchsh_ns, plat->tslch_ns); | |
34 | ||
35 | return 0; | |
36 | } | |
37 | ||
38 | /* Calibration sequence to determine the read data capture delay register */ | |
98fbd71d | 39 | static int spi_calibration(struct udevice *bus, uint hz) |
10e8bf88 | 40 | { |
10e8bf88 SR |
41 | struct cadence_spi_priv *priv = dev_get_priv(bus); |
42 | void *base = priv->regbase; | |
43 | u8 opcode_rdid = 0x9F; | |
44 | unsigned int idcode = 0, temp = 0; | |
45 | int err = 0, i, range_lo = -1, range_hi = -1; | |
46 | ||
47 | /* start with slowest clock (1 MHz) */ | |
48 | cadence_spi_write_speed(bus, 1000000); | |
49 | ||
50 | /* configure the read data capture delay register to 0 */ | |
51 | cadence_qspi_apb_readdata_capture(base, 1, 0); | |
52 | ||
53 | /* Enable QSPI */ | |
54 | cadence_qspi_apb_controller_enable(base); | |
55 | ||
56 | /* read the ID which will be our golden value */ | |
57 | err = cadence_qspi_apb_command_read(base, 1, &opcode_rdid, | |
58 | 3, (u8 *)&idcode); | |
59 | if (err) { | |
60 | puts("SF: Calibration failed (read)\n"); | |
61 | return err; | |
62 | } | |
63 | ||
64 | /* use back the intended clock and find low range */ | |
98fbd71d | 65 | cadence_spi_write_speed(bus, hz); |
10e8bf88 SR |
66 | for (i = 0; i < CQSPI_READ_CAPTURE_MAX_DELAY; i++) { |
67 | /* Disable QSPI */ | |
68 | cadence_qspi_apb_controller_disable(base); | |
69 | ||
70 | /* reconfigure the read data capture delay register */ | |
71 | cadence_qspi_apb_readdata_capture(base, 1, i); | |
72 | ||
73 | /* Enable back QSPI */ | |
74 | cadence_qspi_apb_controller_enable(base); | |
75 | ||
76 | /* issue a RDID to get the ID value */ | |
77 | err = cadence_qspi_apb_command_read(base, 1, &opcode_rdid, | |
78 | 3, (u8 *)&temp); | |
79 | if (err) { | |
80 | puts("SF: Calibration failed (read)\n"); | |
81 | return err; | |
82 | } | |
83 | ||
84 | /* search for range lo */ | |
85 | if (range_lo == -1 && temp == idcode) { | |
86 | range_lo = i; | |
87 | continue; | |
88 | } | |
89 | ||
90 | /* search for range hi */ | |
91 | if (range_lo != -1 && temp != idcode) { | |
92 | range_hi = i - 1; | |
93 | break; | |
94 | } | |
95 | range_hi = i; | |
96 | } | |
97 | ||
98 | if (range_lo == -1) { | |
99 | puts("SF: Calibration failed (low range)\n"); | |
100 | return err; | |
101 | } | |
102 | ||
103 | /* Disable QSPI for subsequent initialization */ | |
104 | cadence_qspi_apb_controller_disable(base); | |
105 | ||
106 | /* configure the final value for read data capture delay register */ | |
107 | cadence_qspi_apb_readdata_capture(base, 1, (range_hi + range_lo) / 2); | |
108 | debug("SF: Read data capture delay calibrated to %i (%i - %i)\n", | |
109 | (range_hi + range_lo) / 2, range_lo, range_hi); | |
110 | ||
111 | /* just to ensure we do once only when speed or chip select change */ | |
98fbd71d | 112 | priv->qspi_calibrated_hz = hz; |
10e8bf88 SR |
113 | priv->qspi_calibrated_cs = spi_chip_select(bus); |
114 | ||
115 | return 0; | |
116 | } | |
117 | ||
118 | static int cadence_spi_set_speed(struct udevice *bus, uint hz) | |
119 | { | |
120 | struct cadence_spi_platdata *plat = bus->platdata; | |
121 | struct cadence_spi_priv *priv = dev_get_priv(bus); | |
122 | int err; | |
123 | ||
4e609b6c CLS |
124 | if (hz > plat->max_hz) |
125 | hz = plat->max_hz; | |
126 | ||
10e8bf88 SR |
127 | /* Disable QSPI */ |
128 | cadence_qspi_apb_controller_disable(priv->regbase); | |
129 | ||
98fbd71d CLS |
130 | /* |
131 | * Calibration required for different current SCLK speed, requested | |
132 | * SCLK speed or chip select | |
133 | */ | |
134 | if (priv->previous_hz != hz || | |
135 | priv->qspi_calibrated_hz != hz || | |
10e8bf88 | 136 | priv->qspi_calibrated_cs != spi_chip_select(bus)) { |
98fbd71d | 137 | err = spi_calibration(bus, hz); |
10e8bf88 SR |
138 | if (err) |
139 | return err; | |
98fbd71d CLS |
140 | |
141 | /* prevent calibration run when same as previous request */ | |
142 | priv->previous_hz = hz; | |
10e8bf88 SR |
143 | } |
144 | ||
145 | /* Enable QSPI */ | |
146 | cadence_qspi_apb_controller_enable(priv->regbase); | |
147 | ||
148 | debug("%s: speed=%d\n", __func__, hz); | |
149 | ||
150 | return 0; | |
151 | } | |
152 | ||
153 | static int cadence_spi_probe(struct udevice *bus) | |
154 | { | |
155 | struct cadence_spi_platdata *plat = bus->platdata; | |
156 | struct cadence_spi_priv *priv = dev_get_priv(bus); | |
157 | ||
158 | priv->regbase = plat->regbase; | |
159 | priv->ahbbase = plat->ahbbase; | |
160 | ||
161 | if (!priv->qspi_is_init) { | |
162 | cadence_qspi_apb_controller_init(plat); | |
163 | priv->qspi_is_init = 1; | |
164 | } | |
165 | ||
166 | return 0; | |
167 | } | |
168 | ||
169 | static int cadence_spi_set_mode(struct udevice *bus, uint mode) | |
170 | { | |
171 | struct cadence_spi_priv *priv = dev_get_priv(bus); | |
10e8bf88 SR |
172 | |
173 | /* Disable QSPI */ | |
174 | cadence_qspi_apb_controller_disable(priv->regbase); | |
175 | ||
176 | /* Set SPI mode */ | |
7d403f28 | 177 | cadence_qspi_apb_set_clk_mode(priv->regbase, mode); |
10e8bf88 SR |
178 | |
179 | /* Enable QSPI */ | |
180 | cadence_qspi_apb_controller_enable(priv->regbase); | |
181 | ||
182 | return 0; | |
183 | } | |
184 | ||
185 | static int cadence_spi_xfer(struct udevice *dev, unsigned int bitlen, | |
186 | const void *dout, void *din, unsigned long flags) | |
187 | { | |
188 | struct udevice *bus = dev->parent; | |
189 | struct cadence_spi_platdata *plat = bus->platdata; | |
190 | struct cadence_spi_priv *priv = dev_get_priv(bus); | |
2372e14f | 191 | struct dm_spi_slave_platdata *dm_plat = dev_get_parent_platdata(dev); |
10e8bf88 SR |
192 | void *base = priv->regbase; |
193 | u8 *cmd_buf = priv->cmd_buf; | |
194 | size_t data_bytes; | |
195 | int err = 0; | |
196 | u32 mode = CQSPI_STIG_WRITE; | |
197 | ||
198 | if (flags & SPI_XFER_BEGIN) { | |
199 | /* copy command to local buffer */ | |
200 | priv->cmd_len = bitlen / 8; | |
201 | memcpy(cmd_buf, dout, priv->cmd_len); | |
202 | } | |
203 | ||
204 | if (flags == (SPI_XFER_BEGIN | SPI_XFER_END)) { | |
205 | /* if start and end bit are set, the data bytes is 0. */ | |
206 | data_bytes = 0; | |
207 | } else { | |
208 | data_bytes = bitlen / 8; | |
209 | } | |
9bd39dd8 | 210 | debug("%s: len=%zu [bytes]\n", __func__, data_bytes); |
10e8bf88 SR |
211 | |
212 | /* Set Chip select */ | |
213 | cadence_qspi_apb_chipselect(base, spi_chip_select(dev), | |
15a70a5d | 214 | plat->is_decoded_cs); |
10e8bf88 SR |
215 | |
216 | if ((flags & SPI_XFER_END) || (flags == 0)) { | |
217 | if (priv->cmd_len == 0) { | |
218 | printf("QSPI: Error, command is empty.\n"); | |
219 | return -1; | |
220 | } | |
221 | ||
222 | if (din && data_bytes) { | |
223 | /* read */ | |
224 | /* Use STIG if no address. */ | |
225 | if (!CQSPI_IS_ADDR(priv->cmd_len)) | |
226 | mode = CQSPI_STIG_READ; | |
227 | else | |
228 | mode = CQSPI_INDIRECT_READ; | |
229 | } else if (dout && !(flags & SPI_XFER_BEGIN)) { | |
230 | /* write */ | |
231 | if (!CQSPI_IS_ADDR(priv->cmd_len)) | |
232 | mode = CQSPI_STIG_WRITE; | |
233 | else | |
234 | mode = CQSPI_INDIRECT_WRITE; | |
235 | } | |
236 | ||
237 | switch (mode) { | |
238 | case CQSPI_STIG_READ: | |
239 | err = cadence_qspi_apb_command_read( | |
240 | base, priv->cmd_len, cmd_buf, | |
241 | data_bytes, din); | |
242 | ||
243 | break; | |
244 | case CQSPI_STIG_WRITE: | |
245 | err = cadence_qspi_apb_command_write(base, | |
246 | priv->cmd_len, cmd_buf, | |
247 | data_bytes, dout); | |
248 | break; | |
249 | case CQSPI_INDIRECT_READ: | |
250 | err = cadence_qspi_apb_indirect_read_setup(plat, | |
08fe9c29 | 251 | priv->cmd_len, dm_plat->mode, cmd_buf); |
10e8bf88 SR |
252 | if (!err) { |
253 | err = cadence_qspi_apb_indirect_read_execute | |
254 | (plat, data_bytes, din); | |
255 | } | |
256 | break; | |
257 | case CQSPI_INDIRECT_WRITE: | |
258 | err = cadence_qspi_apb_indirect_write_setup | |
259 | (plat, priv->cmd_len, cmd_buf); | |
260 | if (!err) { | |
261 | err = cadence_qspi_apb_indirect_write_execute | |
262 | (plat, data_bytes, dout); | |
263 | } | |
264 | break; | |
265 | default: | |
266 | err = -1; | |
267 | break; | |
268 | } | |
269 | ||
270 | if (flags & SPI_XFER_END) { | |
271 | /* clear command buffer */ | |
272 | memset(cmd_buf, 0, sizeof(priv->cmd_buf)); | |
273 | priv->cmd_len = 0; | |
274 | } | |
275 | } | |
276 | ||
277 | return err; | |
278 | } | |
279 | ||
280 | static int cadence_spi_ofdata_to_platdata(struct udevice *bus) | |
281 | { | |
282 | struct cadence_spi_platdata *plat = bus->platdata; | |
283 | const void *blob = gd->fdt_blob; | |
e160f7d4 | 284 | int node = dev_of_offset(bus); |
10e8bf88 | 285 | int subnode; |
10e8bf88 | 286 | |
6c353674 LFT |
287 | plat->regbase = (void *)devfdt_get_addr_index(bus, 0); |
288 | plat->ahbbase = (void *)devfdt_get_addr_index(bus, 1); | |
15a70a5d JR |
289 | plat->is_decoded_cs = fdtdec_get_bool(blob, node, "cdns,is-decoded-cs"); |
290 | plat->fifo_depth = fdtdec_get_uint(blob, node, "cdns,fifo-depth", 128); | |
291 | plat->fifo_width = fdtdec_get_uint(blob, node, "cdns,fifo-width", 4); | |
292 | plat->trigger_address = fdtdec_get_uint(blob, node, | |
293 | "cdns,trigger-address", 0); | |
10e8bf88 | 294 | |
10e8bf88 SR |
295 | /* All other paramters are embedded in the child node */ |
296 | subnode = fdt_first_subnode(blob, node); | |
1dc7d00f | 297 | if (subnode < 0) { |
10e8bf88 SR |
298 | printf("Error: subnode with SPI flash config missing!\n"); |
299 | return -ENODEV; | |
300 | } | |
301 | ||
040f4ba7 CLS |
302 | /* Use 500 KHz as a suitable default */ |
303 | plat->max_hz = fdtdec_get_uint(blob, subnode, "spi-max-frequency", | |
304 | 500000); | |
305 | ||
10e8bf88 | 306 | /* Read other parameters from DT */ |
15a70a5d JR |
307 | plat->page_size = fdtdec_get_uint(blob, subnode, "page-size", 256); |
308 | plat->block_size = fdtdec_get_uint(blob, subnode, "block-size", 16); | |
309 | plat->tshsl_ns = fdtdec_get_uint(blob, subnode, "cdns,tshsl-ns", 200); | |
310 | plat->tsd2d_ns = fdtdec_get_uint(blob, subnode, "cdns,tsd2d-ns", 255); | |
311 | plat->tchsh_ns = fdtdec_get_uint(blob, subnode, "cdns,tchsh-ns", 20); | |
312 | plat->tslch_ns = fdtdec_get_uint(blob, subnode, "cdns,tslch-ns", 20); | |
10e8bf88 SR |
313 | |
314 | debug("%s: regbase=%p ahbbase=%p max-frequency=%d page-size=%d\n", | |
315 | __func__, plat->regbase, plat->ahbbase, plat->max_hz, | |
316 | plat->page_size); | |
317 | ||
318 | return 0; | |
319 | } | |
320 | ||
321 | static const struct dm_spi_ops cadence_spi_ops = { | |
322 | .xfer = cadence_spi_xfer, | |
323 | .set_speed = cadence_spi_set_speed, | |
324 | .set_mode = cadence_spi_set_mode, | |
325 | /* | |
326 | * cs_info is not needed, since we require all chip selects to be | |
327 | * in the device tree explicitly | |
328 | */ | |
329 | }; | |
330 | ||
331 | static const struct udevice_id cadence_spi_ids[] = { | |
2a3a9993 | 332 | { .compatible = "cdns,qspi-nor" }, |
10e8bf88 SR |
333 | { } |
334 | }; | |
335 | ||
336 | U_BOOT_DRIVER(cadence_spi) = { | |
337 | .name = "cadence_spi", | |
338 | .id = UCLASS_SPI, | |
339 | .of_match = cadence_spi_ids, | |
340 | .ops = &cadence_spi_ops, | |
341 | .ofdata_to_platdata = cadence_spi_ofdata_to_platdata, | |
342 | .platdata_auto_alloc_size = sizeof(struct cadence_spi_platdata), | |
343 | .priv_auto_alloc_size = sizeof(struct cadence_spi_priv), | |
10e8bf88 SR |
344 | .probe = cadence_spi_probe, |
345 | }; |