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