.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (C) 2012 - 2014 Allwinner Tech |
---|
3 | 4 | * Pan Nan <pannan@allwinnertech.com> |
---|
4 | 5 | * |
---|
5 | 6 | * Copyright (C) 2014 Maxime Ripard |
---|
6 | 7 | * Maxime Ripard <maxime.ripard@free-electrons.com> |
---|
7 | | - * |
---|
8 | | - * This program is free software; you can redistribute it and/or |
---|
9 | | - * modify it under the terms of the GNU General Public License as |
---|
10 | | - * published by the Free Software Foundation; either version 2 of |
---|
11 | | - * the License, or (at your option) any later version. |
---|
12 | 8 | */ |
---|
13 | 9 | |
---|
| 10 | +#include <linux/bitfield.h> |
---|
14 | 11 | #include <linux/clk.h> |
---|
15 | 12 | #include <linux/delay.h> |
---|
16 | 13 | #include <linux/device.h> |
---|
.. | .. |
---|
62 | 59 | #define SUN6I_FIFO_CTL_TF_RST BIT(31) |
---|
63 | 60 | |
---|
64 | 61 | #define SUN6I_FIFO_STA_REG 0x1c |
---|
65 | | -#define SUN6I_FIFO_STA_RF_CNT_MASK 0x7f |
---|
66 | | -#define SUN6I_FIFO_STA_RF_CNT_BITS 0 |
---|
67 | | -#define SUN6I_FIFO_STA_TF_CNT_MASK 0x7f |
---|
68 | | -#define SUN6I_FIFO_STA_TF_CNT_BITS 16 |
---|
| 62 | +#define SUN6I_FIFO_STA_RF_CNT_MASK GENMASK(7, 0) |
---|
| 63 | +#define SUN6I_FIFO_STA_TF_CNT_MASK GENMASK(23, 16) |
---|
69 | 64 | |
---|
70 | 65 | #define SUN6I_CLK_CTL_REG 0x24 |
---|
71 | 66 | #define SUN6I_CLK_CTL_CDR2_MASK 0xff |
---|
.. | .. |
---|
77 | 72 | #define SUN6I_MAX_XFER_SIZE 0xffffff |
---|
78 | 73 | |
---|
79 | 74 | #define SUN6I_BURST_CNT_REG 0x30 |
---|
80 | | -#define SUN6I_BURST_CNT(cnt) ((cnt) & SUN6I_MAX_XFER_SIZE) |
---|
81 | 75 | |
---|
82 | 76 | #define SUN6I_XMIT_CNT_REG 0x34 |
---|
83 | | -#define SUN6I_XMIT_CNT(cnt) ((cnt) & SUN6I_MAX_XFER_SIZE) |
---|
84 | 77 | |
---|
85 | 78 | #define SUN6I_BURST_CTL_CNT_REG 0x38 |
---|
86 | | -#define SUN6I_BURST_CTL_CNT_STC(cnt) ((cnt) & SUN6I_MAX_XFER_SIZE) |
---|
87 | 79 | |
---|
88 | 80 | #define SUN6I_TXDATA_REG 0x200 |
---|
89 | 81 | #define SUN6I_RXDATA_REG 0x300 |
---|
.. | .. |
---|
113 | 105 | writel(value, sspi->base_addr + reg); |
---|
114 | 106 | } |
---|
115 | 107 | |
---|
| 108 | +static inline u32 sun6i_spi_get_rx_fifo_count(struct sun6i_spi *sspi) |
---|
| 109 | +{ |
---|
| 110 | + u32 reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG); |
---|
| 111 | + |
---|
| 112 | + return FIELD_GET(SUN6I_FIFO_STA_RF_CNT_MASK, reg); |
---|
| 113 | +} |
---|
| 114 | + |
---|
116 | 115 | static inline u32 sun6i_spi_get_tx_fifo_count(struct sun6i_spi *sspi) |
---|
117 | 116 | { |
---|
118 | 117 | u32 reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG); |
---|
119 | 118 | |
---|
120 | | - reg >>= SUN6I_FIFO_STA_TF_CNT_BITS; |
---|
121 | | - |
---|
122 | | - return reg & SUN6I_FIFO_STA_TF_CNT_MASK; |
---|
123 | | -} |
---|
124 | | - |
---|
125 | | -static inline void sun6i_spi_enable_interrupt(struct sun6i_spi *sspi, u32 mask) |
---|
126 | | -{ |
---|
127 | | - u32 reg = sun6i_spi_read(sspi, SUN6I_INT_CTL_REG); |
---|
128 | | - |
---|
129 | | - reg |= mask; |
---|
130 | | - sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, reg); |
---|
| 119 | + return FIELD_GET(SUN6I_FIFO_STA_TF_CNT_MASK, reg); |
---|
131 | 120 | } |
---|
132 | 121 | |
---|
133 | 122 | static inline void sun6i_spi_disable_interrupt(struct sun6i_spi *sspi, u32 mask) |
---|
.. | .. |
---|
138 | 127 | sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, reg); |
---|
139 | 128 | } |
---|
140 | 129 | |
---|
141 | | -static inline void sun6i_spi_drain_fifo(struct sun6i_spi *sspi, int len) |
---|
| 130 | +static inline void sun6i_spi_drain_fifo(struct sun6i_spi *sspi) |
---|
142 | 131 | { |
---|
143 | | - u32 reg, cnt; |
---|
| 132 | + u32 len; |
---|
144 | 133 | u8 byte; |
---|
145 | 134 | |
---|
146 | 135 | /* See how much data is available */ |
---|
147 | | - reg = sun6i_spi_read(sspi, SUN6I_FIFO_STA_REG); |
---|
148 | | - reg &= SUN6I_FIFO_STA_RF_CNT_MASK; |
---|
149 | | - cnt = reg >> SUN6I_FIFO_STA_RF_CNT_BITS; |
---|
150 | | - |
---|
151 | | - if (len > cnt) |
---|
152 | | - len = cnt; |
---|
| 136 | + len = sun6i_spi_get_rx_fifo_count(sspi); |
---|
153 | 137 | |
---|
154 | 138 | while (len--) { |
---|
155 | 139 | byte = readb(sspi->base_addr + SUN6I_RXDATA_REG); |
---|
.. | .. |
---|
158 | 142 | } |
---|
159 | 143 | } |
---|
160 | 144 | |
---|
161 | | -static inline void sun6i_spi_fill_fifo(struct sun6i_spi *sspi, int len) |
---|
| 145 | +static inline void sun6i_spi_fill_fifo(struct sun6i_spi *sspi) |
---|
162 | 146 | { |
---|
163 | 147 | u32 cnt; |
---|
| 148 | + int len; |
---|
164 | 149 | u8 byte; |
---|
165 | 150 | |
---|
166 | 151 | /* See how much data we can fit */ |
---|
167 | 152 | cnt = sspi->fifo_depth - sun6i_spi_get_tx_fifo_count(sspi); |
---|
168 | 153 | |
---|
169 | | - len = min3(len, (int)cnt, sspi->len); |
---|
| 154 | + len = min((int)cnt, sspi->len); |
---|
170 | 155 | |
---|
171 | 156 | while (len--) { |
---|
172 | 157 | byte = sspi->tx_buf ? *sspi->tx_buf++ : 0; |
---|
.. | .. |
---|
205 | 190 | unsigned int mclk_rate, div, div_cdr1, div_cdr2, timeout; |
---|
206 | 191 | unsigned int start, end, tx_time; |
---|
207 | 192 | unsigned int trig_level; |
---|
208 | | - unsigned int tx_len = 0; |
---|
| 193 | + unsigned int tx_len = 0, rx_len = 0; |
---|
209 | 194 | int ret = 0; |
---|
210 | 195 | u32 reg; |
---|
211 | 196 | |
---|
.. | .. |
---|
260 | 245 | * If it's a TX only transfer, we don't want to fill the RX |
---|
261 | 246 | * FIFO with bogus data |
---|
262 | 247 | */ |
---|
263 | | - if (sspi->rx_buf) |
---|
| 248 | + if (sspi->rx_buf) { |
---|
264 | 249 | reg &= ~SUN6I_TFR_CTL_DHB; |
---|
265 | | - else |
---|
| 250 | + rx_len = tfr->len; |
---|
| 251 | + } else { |
---|
266 | 252 | reg |= SUN6I_TFR_CTL_DHB; |
---|
| 253 | + } |
---|
267 | 254 | |
---|
268 | 255 | /* We want to control the chip select manually */ |
---|
269 | 256 | reg |= SUN6I_TFR_CTL_CS_MANUAL; |
---|
.. | .. |
---|
295 | 282 | div_cdr2 = DIV_ROUND_UP(div_cdr1, 2); |
---|
296 | 283 | if (div_cdr2 <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) { |
---|
297 | 284 | reg = SUN6I_CLK_CTL_CDR2(div_cdr2 - 1) | SUN6I_CLK_CTL_DRS; |
---|
| 285 | + tfr->effective_speed_hz = mclk_rate / (2 * div_cdr2); |
---|
298 | 286 | } else { |
---|
299 | 287 | div = min(SUN6I_CLK_CTL_CDR1_MASK, order_base_2(div_cdr1)); |
---|
300 | 288 | reg = SUN6I_CLK_CTL_CDR1(div); |
---|
| 289 | + tfr->effective_speed_hz = mclk_rate / (1 << div); |
---|
301 | 290 | } |
---|
302 | 291 | |
---|
303 | 292 | sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg); |
---|
.. | .. |
---|
311 | 300 | tx_len = tfr->len; |
---|
312 | 301 | |
---|
313 | 302 | /* Setup the counters */ |
---|
314 | | - sun6i_spi_write(sspi, SUN6I_BURST_CNT_REG, SUN6I_BURST_CNT(tfr->len)); |
---|
315 | | - sun6i_spi_write(sspi, SUN6I_XMIT_CNT_REG, SUN6I_XMIT_CNT(tx_len)); |
---|
316 | | - sun6i_spi_write(sspi, SUN6I_BURST_CTL_CNT_REG, |
---|
317 | | - SUN6I_BURST_CTL_CNT_STC(tx_len)); |
---|
| 303 | + sun6i_spi_write(sspi, SUN6I_BURST_CNT_REG, tfr->len); |
---|
| 304 | + sun6i_spi_write(sspi, SUN6I_XMIT_CNT_REG, tx_len); |
---|
| 305 | + sun6i_spi_write(sspi, SUN6I_BURST_CTL_CNT_REG, tx_len); |
---|
318 | 306 | |
---|
319 | 307 | /* Fill the TX FIFO */ |
---|
320 | | - sun6i_spi_fill_fifo(sspi, sspi->fifo_depth); |
---|
| 308 | + sun6i_spi_fill_fifo(sspi); |
---|
321 | 309 | |
---|
322 | 310 | /* Enable the interrupts */ |
---|
323 | | - sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, SUN6I_INT_CTL_TC); |
---|
324 | | - sun6i_spi_enable_interrupt(sspi, SUN6I_INT_CTL_TC | |
---|
325 | | - SUN6I_INT_CTL_RF_RDY); |
---|
| 311 | + reg = SUN6I_INT_CTL_TC; |
---|
| 312 | + |
---|
| 313 | + if (rx_len > sspi->fifo_depth) |
---|
| 314 | + reg |= SUN6I_INT_CTL_RF_RDY; |
---|
326 | 315 | if (tx_len > sspi->fifo_depth) |
---|
327 | | - sun6i_spi_enable_interrupt(sspi, SUN6I_INT_CTL_TF_ERQ); |
---|
| 316 | + reg |= SUN6I_INT_CTL_TF_ERQ; |
---|
| 317 | + |
---|
| 318 | + sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, reg); |
---|
328 | 319 | |
---|
329 | 320 | /* Start the transfer */ |
---|
330 | 321 | reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); |
---|
.. | .. |
---|
341 | 332 | dev_name(&spi->dev), tfr->len, tfr->speed_hz, |
---|
342 | 333 | jiffies_to_msecs(end - start), tx_time); |
---|
343 | 334 | ret = -ETIMEDOUT; |
---|
344 | | - goto out; |
---|
345 | 335 | } |
---|
346 | 336 | |
---|
347 | | -out: |
---|
348 | 337 | sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, 0); |
---|
349 | 338 | |
---|
350 | 339 | return ret; |
---|
.. | .. |
---|
358 | 347 | /* Transfer complete */ |
---|
359 | 348 | if (status & SUN6I_INT_CTL_TC) { |
---|
360 | 349 | sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TC); |
---|
361 | | - sun6i_spi_drain_fifo(sspi, sspi->fifo_depth); |
---|
| 350 | + sun6i_spi_drain_fifo(sspi); |
---|
362 | 351 | complete(&sspi->done); |
---|
363 | 352 | return IRQ_HANDLED; |
---|
364 | 353 | } |
---|
365 | 354 | |
---|
366 | 355 | /* Receive FIFO 3/4 full */ |
---|
367 | 356 | if (status & SUN6I_INT_CTL_RF_RDY) { |
---|
368 | | - sun6i_spi_drain_fifo(sspi, SUN6I_FIFO_DEPTH); |
---|
| 357 | + sun6i_spi_drain_fifo(sspi); |
---|
369 | 358 | /* Only clear the interrupt _after_ draining the FIFO */ |
---|
370 | 359 | sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_RF_RDY); |
---|
371 | 360 | return IRQ_HANDLED; |
---|
.. | .. |
---|
373 | 362 | |
---|
374 | 363 | /* Transmit FIFO 3/4 empty */ |
---|
375 | 364 | if (status & SUN6I_INT_CTL_TF_ERQ) { |
---|
376 | | - sun6i_spi_fill_fifo(sspi, SUN6I_FIFO_DEPTH); |
---|
| 365 | + sun6i_spi_fill_fifo(sspi); |
---|
377 | 366 | |
---|
378 | 367 | if (!sspi->len) |
---|
379 | 368 | /* nothing left to transmit */ |
---|
.. | .. |
---|
441 | 430 | { |
---|
442 | 431 | struct spi_master *master; |
---|
443 | 432 | struct sun6i_spi *sspi; |
---|
444 | | - struct resource *res; |
---|
445 | 433 | int ret = 0, irq; |
---|
446 | 434 | |
---|
447 | 435 | master = spi_alloc_master(&pdev->dev, sizeof(struct sun6i_spi)); |
---|
.. | .. |
---|
453 | 441 | platform_set_drvdata(pdev, master); |
---|
454 | 442 | sspi = spi_master_get_devdata(master); |
---|
455 | 443 | |
---|
456 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
457 | | - sspi->base_addr = devm_ioremap_resource(&pdev->dev, res); |
---|
| 444 | + sspi->base_addr = devm_platform_ioremap_resource(pdev, 0); |
---|
458 | 445 | if (IS_ERR(sspi->base_addr)) { |
---|
459 | 446 | ret = PTR_ERR(sspi->base_addr); |
---|
460 | 447 | goto err_free_master; |
---|
.. | .. |
---|
462 | 449 | |
---|
463 | 450 | irq = platform_get_irq(pdev, 0); |
---|
464 | 451 | if (irq < 0) { |
---|
465 | | - dev_err(&pdev->dev, "No spi IRQ specified\n"); |
---|
466 | 452 | ret = -ENXIO; |
---|
467 | 453 | goto err_free_master; |
---|
468 | 454 | } |
---|
.. | .. |
---|
479 | 465 | |
---|
480 | 466 | master->max_speed_hz = 100 * 1000 * 1000; |
---|
481 | 467 | master->min_speed_hz = 3 * 1000; |
---|
| 468 | + master->use_gpio_descriptors = true; |
---|
482 | 469 | master->set_cs = sun6i_spi_set_cs; |
---|
483 | 470 | master->transfer_one = sun6i_spi_transfer_one; |
---|
484 | 471 | master->num_chipselect = 4; |
---|