Blame view
drivers/spi/spi-atmel.c
45.5 KB
754ce4f29 [PATCH] SPI: atme... |
1 2 3 4 5 6 7 8 9 10 11 |
/* * Driver for Atmel AT32 and AT91 SPI Controllers * * Copyright (C) 2006 Atmel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/kernel.h> |
754ce4f29 [PATCH] SPI: atme... |
12 13 14 15 16 |
#include <linux/clk.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/delay.h> #include <linux/dma-mapping.h> |
1ccc404a7 spi/spi-atmel: ad... |
17 |
#include <linux/dmaengine.h> |
754ce4f29 [PATCH] SPI: atme... |
18 19 20 |
#include <linux/err.h> #include <linux/interrupt.h> #include <linux/spi/spi.h> |
5a0e3ad6a include cleanup: ... |
21 |
#include <linux/slab.h> |
1ccc404a7 spi/spi-atmel: ad... |
22 |
#include <linux/platform_data/dma-atmel.h> |
850a5b670 spi/atmel: add DT... |
23 |
#include <linux/of.h> |
754ce4f29 [PATCH] SPI: atme... |
24 |
|
d4820b749 spi/spi-atmel: de... |
25 26 |
#include <linux/io.h> #include <linux/gpio.h> |
961062007 spi: atmel: use m... |
27 |
#include <linux/of_gpio.h> |
5bdfd491a spi: atmel: adopt... |
28 |
#include <linux/pinctrl/consumer.h> |
ce0c4caf2 spi/atmel: add su... |
29 |
#include <linux/pm_runtime.h> |
bb2d1c36c [PATCH] SPI contr... |
30 |
|
ca632f556 spi: reorganize d... |
31 32 33 34 35 36 37 38 39 40 41 42 43 |
/* SPI register offsets */ #define SPI_CR 0x0000 #define SPI_MR 0x0004 #define SPI_RDR 0x0008 #define SPI_TDR 0x000c #define SPI_SR 0x0010 #define SPI_IER 0x0014 #define SPI_IDR 0x0018 #define SPI_IMR 0x001c #define SPI_CSR0 0x0030 #define SPI_CSR1 0x0034 #define SPI_CSR2 0x0038 #define SPI_CSR3 0x003c |
11f2764fe spi: atmel: add s... |
44 45 |
#define SPI_FMR 0x0040 #define SPI_FLR 0x0044 |
d4820b749 spi/spi-atmel: de... |
46 |
#define SPI_VERSION 0x00fc |
ca632f556 spi: reorganize d... |
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
#define SPI_RPR 0x0100 #define SPI_RCR 0x0104 #define SPI_TPR 0x0108 #define SPI_TCR 0x010c #define SPI_RNPR 0x0110 #define SPI_RNCR 0x0114 #define SPI_TNPR 0x0118 #define SPI_TNCR 0x011c #define SPI_PTCR 0x0120 #define SPI_PTSR 0x0124 /* Bitfields in CR */ #define SPI_SPIEN_OFFSET 0 #define SPI_SPIEN_SIZE 1 #define SPI_SPIDIS_OFFSET 1 #define SPI_SPIDIS_SIZE 1 #define SPI_SWRST_OFFSET 7 #define SPI_SWRST_SIZE 1 #define SPI_LASTXFER_OFFSET 24 #define SPI_LASTXFER_SIZE 1 |
11f2764fe spi: atmel: add s... |
67 68 69 70 71 72 73 74 |
#define SPI_TXFCLR_OFFSET 16 #define SPI_TXFCLR_SIZE 1 #define SPI_RXFCLR_OFFSET 17 #define SPI_RXFCLR_SIZE 1 #define SPI_FIFOEN_OFFSET 30 #define SPI_FIFOEN_SIZE 1 #define SPI_FIFODIS_OFFSET 31 #define SPI_FIFODIS_SIZE 1 |
ca632f556 spi: reorganize d... |
75 76 77 78 79 80 81 82 83 84 85 86 |
/* Bitfields in MR */ #define SPI_MSTR_OFFSET 0 #define SPI_MSTR_SIZE 1 #define SPI_PS_OFFSET 1 #define SPI_PS_SIZE 1 #define SPI_PCSDEC_OFFSET 2 #define SPI_PCSDEC_SIZE 1 #define SPI_FDIV_OFFSET 3 #define SPI_FDIV_SIZE 1 #define SPI_MODFDIS_OFFSET 4 #define SPI_MODFDIS_SIZE 1 |
d4820b749 spi/spi-atmel: de... |
87 88 |
#define SPI_WDRBT_OFFSET 5 #define SPI_WDRBT_SIZE 1 |
ca632f556 spi: reorganize d... |
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
#define SPI_LLB_OFFSET 7 #define SPI_LLB_SIZE 1 #define SPI_PCS_OFFSET 16 #define SPI_PCS_SIZE 4 #define SPI_DLYBCS_OFFSET 24 #define SPI_DLYBCS_SIZE 8 /* Bitfields in RDR */ #define SPI_RD_OFFSET 0 #define SPI_RD_SIZE 16 /* Bitfields in TDR */ #define SPI_TD_OFFSET 0 #define SPI_TD_SIZE 16 /* Bitfields in SR */ #define SPI_RDRF_OFFSET 0 #define SPI_RDRF_SIZE 1 #define SPI_TDRE_OFFSET 1 #define SPI_TDRE_SIZE 1 #define SPI_MODF_OFFSET 2 #define SPI_MODF_SIZE 1 #define SPI_OVRES_OFFSET 3 #define SPI_OVRES_SIZE 1 #define SPI_ENDRX_OFFSET 4 #define SPI_ENDRX_SIZE 1 #define SPI_ENDTX_OFFSET 5 #define SPI_ENDTX_SIZE 1 #define SPI_RXBUFF_OFFSET 6 #define SPI_RXBUFF_SIZE 1 #define SPI_TXBUFE_OFFSET 7 #define SPI_TXBUFE_SIZE 1 #define SPI_NSSR_OFFSET 8 #define SPI_NSSR_SIZE 1 #define SPI_TXEMPTY_OFFSET 9 #define SPI_TXEMPTY_SIZE 1 #define SPI_SPIENS_OFFSET 16 #define SPI_SPIENS_SIZE 1 |
11f2764fe spi: atmel: add s... |
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
#define SPI_TXFEF_OFFSET 24 #define SPI_TXFEF_SIZE 1 #define SPI_TXFFF_OFFSET 25 #define SPI_TXFFF_SIZE 1 #define SPI_TXFTHF_OFFSET 26 #define SPI_TXFTHF_SIZE 1 #define SPI_RXFEF_OFFSET 27 #define SPI_RXFEF_SIZE 1 #define SPI_RXFFF_OFFSET 28 #define SPI_RXFFF_SIZE 1 #define SPI_RXFTHF_OFFSET 29 #define SPI_RXFTHF_SIZE 1 #define SPI_TXFPTEF_OFFSET 30 #define SPI_TXFPTEF_SIZE 1 #define SPI_RXFPTEF_OFFSET 31 #define SPI_RXFPTEF_SIZE 1 |
ca632f556 spi: reorganize d... |
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
/* Bitfields in CSR0 */ #define SPI_CPOL_OFFSET 0 #define SPI_CPOL_SIZE 1 #define SPI_NCPHA_OFFSET 1 #define SPI_NCPHA_SIZE 1 #define SPI_CSAAT_OFFSET 3 #define SPI_CSAAT_SIZE 1 #define SPI_BITS_OFFSET 4 #define SPI_BITS_SIZE 4 #define SPI_SCBR_OFFSET 8 #define SPI_SCBR_SIZE 8 #define SPI_DLYBS_OFFSET 16 #define SPI_DLYBS_SIZE 8 #define SPI_DLYBCT_OFFSET 24 #define SPI_DLYBCT_SIZE 8 /* Bitfields in RCR */ #define SPI_RXCTR_OFFSET 0 #define SPI_RXCTR_SIZE 16 /* Bitfields in TCR */ #define SPI_TXCTR_OFFSET 0 #define SPI_TXCTR_SIZE 16 /* Bitfields in RNCR */ #define SPI_RXNCR_OFFSET 0 #define SPI_RXNCR_SIZE 16 /* Bitfields in TNCR */ #define SPI_TXNCR_OFFSET 0 #define SPI_TXNCR_SIZE 16 /* Bitfields in PTCR */ #define SPI_RXTEN_OFFSET 0 #define SPI_RXTEN_SIZE 1 #define SPI_RXTDIS_OFFSET 1 #define SPI_RXTDIS_SIZE 1 #define SPI_TXTEN_OFFSET 8 #define SPI_TXTEN_SIZE 1 #define SPI_TXTDIS_OFFSET 9 #define SPI_TXTDIS_SIZE 1 |
11f2764fe spi: atmel: add s... |
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
/* Bitfields in FMR */ #define SPI_TXRDYM_OFFSET 0 #define SPI_TXRDYM_SIZE 2 #define SPI_RXRDYM_OFFSET 4 #define SPI_RXRDYM_SIZE 2 #define SPI_TXFTHRES_OFFSET 16 #define SPI_TXFTHRES_SIZE 6 #define SPI_RXFTHRES_OFFSET 24 #define SPI_RXFTHRES_SIZE 6 /* Bitfields in FLR */ #define SPI_TXFL_OFFSET 0 #define SPI_TXFL_SIZE 6 #define SPI_RXFL_OFFSET 16 #define SPI_RXFL_SIZE 6 |
ca632f556 spi: reorganize d... |
200 201 202 203 204 205 206 207 208 209 |
/* Constants for BITS */ #define SPI_BITS_8_BPT 0 #define SPI_BITS_9_BPT 1 #define SPI_BITS_10_BPT 2 #define SPI_BITS_11_BPT 3 #define SPI_BITS_12_BPT 4 #define SPI_BITS_13_BPT 5 #define SPI_BITS_14_BPT 6 #define SPI_BITS_15_BPT 7 #define SPI_BITS_16_BPT 8 |
11f2764fe spi: atmel: add s... |
210 211 212 |
#define SPI_ONE_DATA 0 #define SPI_TWO_DATA 1 #define SPI_FOUR_DATA 2 |
ca632f556 spi: reorganize d... |
213 214 215 216 |
/* Bit manipulation macros */ #define SPI_BIT(name) \ (1 << SPI_##name##_OFFSET) |
a536d7654 spi: atmel: Silen... |
217 |
#define SPI_BF(name, value) \ |
ca632f556 spi: reorganize d... |
218 |
(((value) & ((1 << SPI_##name##_SIZE) - 1)) << SPI_##name##_OFFSET) |
a536d7654 spi: atmel: Silen... |
219 |
#define SPI_BFEXT(name, value) \ |
ca632f556 spi: reorganize d... |
220 |
(((value) >> SPI_##name##_OFFSET) & ((1 << SPI_##name##_SIZE) - 1)) |
a536d7654 spi: atmel: Silen... |
221 222 223 |
#define SPI_BFINS(name, value, old) \ (((old) & ~(((1 << SPI_##name##_SIZE) - 1) << SPI_##name##_OFFSET)) \ | SPI_BF(name, value)) |
ca632f556 spi: reorganize d... |
224 225 |
/* Register access macros */ |
ea467326e spi: atmel: use e... |
226 |
#ifdef CONFIG_AVR32 |
a536d7654 spi: atmel: Silen... |
227 |
#define spi_readl(port, reg) \ |
ca632f556 spi: reorganize d... |
228 |
__raw_readl((port)->regs + SPI_##reg) |
a536d7654 spi: atmel: Silen... |
229 |
#define spi_writel(port, reg, value) \ |
ca632f556 spi: reorganize d... |
230 |
__raw_writel((value), (port)->regs + SPI_##reg) |
11f2764fe spi: atmel: add s... |
231 232 233 234 235 236 237 238 239 240 |
#define spi_readw(port, reg) \ __raw_readw((port)->regs + SPI_##reg) #define spi_writew(port, reg, value) \ __raw_writew((value), (port)->regs + SPI_##reg) #define spi_readb(port, reg) \ __raw_readb((port)->regs + SPI_##reg) #define spi_writeb(port, reg, value) \ __raw_writeb((value), (port)->regs + SPI_##reg) |
ea467326e spi: atmel: use e... |
241 242 243 244 245 |
#else #define spi_readl(port, reg) \ readl_relaxed((port)->regs + SPI_##reg) #define spi_writel(port, reg, value) \ writel_relaxed((value), (port)->regs + SPI_##reg) |
11f2764fe spi: atmel: add s... |
246 247 248 249 250 251 252 253 254 255 |
#define spi_readw(port, reg) \ readw_relaxed((port)->regs + SPI_##reg) #define spi_writew(port, reg, value) \ writew_relaxed((value), (port)->regs + SPI_##reg) #define spi_readb(port, reg) \ readb_relaxed((port)->regs + SPI_##reg) #define spi_writeb(port, reg, value) \ writeb_relaxed((value), (port)->regs + SPI_##reg) |
ea467326e spi: atmel: use e... |
256 |
#endif |
1ccc404a7 spi/spi-atmel: ad... |
257 258 259 260 |
/* use PIO for small transfers, avoiding DMA setup/teardown overhead and * cache operations; better heuristics consider wordsize and bitrate. */ #define DMA_MIN_BYTES 16 |
8090d6d1a spi: atmel: Refac... |
261 |
#define SPI_DMA_TIMEOUT (msecs_to_jiffies(1000)) |
ce0c4caf2 spi/atmel: add su... |
262 |
#define AUTOSUSPEND_TIMEOUT 2000 |
d4820b749 spi/spi-atmel: de... |
263 264 265 266 |
struct atmel_spi_caps { bool is_spi2; bool has_wdrbt; bool has_dma_support; |
7094576cc spi: atmel: fix c... |
267 |
bool has_pdc_support; |
d4820b749 spi/spi-atmel: de... |
268 |
}; |
754ce4f29 [PATCH] SPI: atme... |
269 270 271 272 273 |
/* * The core SPI transfer engine just talks to a register bank to set up * DMA transfers; transfer queue progress is driven by IRQs. The clock * framework provides the base clock, subdivided for each spi_device. |
754ce4f29 [PATCH] SPI: atme... |
274 275 276 |
*/ struct atmel_spi { spinlock_t lock; |
8aad7924b spi/spi-atmel: ad... |
277 |
unsigned long flags; |
754ce4f29 [PATCH] SPI: atme... |
278 |
|
dfab30ee6 spi/spi-atmel: ad... |
279 |
phys_addr_t phybase; |
754ce4f29 [PATCH] SPI: atme... |
280 281 282 283 |
void __iomem *regs; int irq; struct clk *clk; struct platform_device *pdev; |
39fe33f98 spi: atmel: Fix s... |
284 |
unsigned long spi_clk; |
754ce4f29 [PATCH] SPI: atme... |
285 |
|
754ce4f29 [PATCH] SPI: atme... |
286 |
struct spi_transfer *current_transfer; |
0c3b97487 spi: atmel: Make ... |
287 |
int current_remaining_bytes; |
823cd0454 spi/spi-atmel: st... |
288 |
int done_status; |
754ce4f29 [PATCH] SPI: atme... |
289 |
|
8090d6d1a spi: atmel: Refac... |
290 |
struct completion xfer_completion; |
d4820b749 spi/spi-atmel: de... |
291 |
struct atmel_spi_caps caps; |
1ccc404a7 spi/spi-atmel: ad... |
292 293 294 |
bool use_dma; bool use_pdc; |
482030348 spi: atmel: add s... |
295 |
bool use_cs_gpios; |
8090d6d1a spi: atmel: Refac... |
296 297 298 |
bool keep_cs; bool cs_active; |
11f2764fe spi: atmel: add s... |
299 300 |
u32 fifo_size; |
754ce4f29 [PATCH] SPI: atme... |
301 |
}; |
5ee36c989 spi: atmel_spi up... |
302 303 304 305 306 |
/* Controller-specific per-slave state */ struct atmel_spi_device { unsigned int npcs_pin; u32 csr; }; |
7910d9af0 spi: atmel: Use c... |
307 |
#define SPI_MAX_DMA_XFER 65535 /* true for both PDC and DMA */ |
754ce4f29 [PATCH] SPI: atme... |
308 309 310 |
#define INVALID_DMA_ADDRESS 0xffffffff /* |
5bfa26ca1 atmel_spi: clean ... |
311 312 313 314 315 316 |
* Version 2 of the SPI controller has * - CR.LASTXFER * - SPI_MR.DIV32 may become FDIV or must-be-zero (here: always zero) * - SPI_SR.TXEMPTY, SPI_SR.NSSR (and corresponding irqs) * - SPI_CSRx.CSAAT * - SPI_CSRx.SBCR allows faster clocking |
5bfa26ca1 atmel_spi: clean ... |
317 |
*/ |
d4820b749 spi/spi-atmel: de... |
318 |
static bool atmel_spi_is_v2(struct atmel_spi *as) |
5bfa26ca1 atmel_spi: clean ... |
319 |
{ |
d4820b749 spi/spi-atmel: de... |
320 |
return as->caps.is_spi2; |
5bfa26ca1 atmel_spi: clean ... |
321 322 323 |
} /* |
754ce4f29 [PATCH] SPI: atme... |
324 325 |
* Earlier SPI controllers (e.g. on at91rm9200) have a design bug whereby * they assume that spi slave device state will not change on deselect, so |
defbd3b4b atmel_spi: don't ... |
326 327 328 |
* that automagic deselection is OK. ("NPCSx rises if no data is to be * transmitted") Not so! Workaround uses nCSx pins as GPIOs; or newer * controllers have CSAAT and friends. |
754ce4f29 [PATCH] SPI: atme... |
329 |
* |
defbd3b4b atmel_spi: don't ... |
330 331 332 333 334 335 336 337 338 339 340 |
* Since the CSAAT functionality is a bit weird on newer controllers as * well, we use GPIO to control nCSx pins on all controllers, updating * MR.PCS to avoid confusing the controller. Using GPIOs also lets us * support active-high chipselects despite the controller's belief that * only active-low devices/systems exists. * * However, at91rm9200 has a second erratum whereby nCS0 doesn't work * right when driven with GPIO. ("Mode Fault does not allow more than one * Master on Chip Select 0.") No workaround exists for that ... so for * nCS0 on that chip, we (a) don't use the GPIO, (b) can't support CS_HIGH, * and (c) will trigger that first erratum in some cases. |
754ce4f29 [PATCH] SPI: atme... |
341 |
*/ |
defbd3b4b atmel_spi: don't ... |
342 |
static void cs_activate(struct atmel_spi *as, struct spi_device *spi) |
754ce4f29 [PATCH] SPI: atme... |
343 |
{ |
5ee36c989 spi: atmel_spi up... |
344 |
struct atmel_spi_device *asd = spi->controller_state; |
754ce4f29 [PATCH] SPI: atme... |
345 |
unsigned active = spi->mode & SPI_CS_HIGH; |
defbd3b4b atmel_spi: don't ... |
346 |
u32 mr; |
d4820b749 spi/spi-atmel: de... |
347 |
if (atmel_spi_is_v2(as)) { |
97ed465b4 spi/spi-atmel: ad... |
348 349 350 |
spi_writel(as, CSR0 + 4 * spi->chip_select, asd->csr); /* For the low SPI version, there is a issue that PDC transfer * on CS1,2,3 needs SPI_CSR0.BITS config as SPI_CSR1,2,3.BITS |
5ee36c989 spi: atmel_spi up... |
351 352 |
*/ spi_writel(as, CSR0, asd->csr); |
d4820b749 spi/spi-atmel: de... |
353 |
if (as->caps.has_wdrbt) { |
97ed465b4 spi/spi-atmel: ad... |
354 355 356 357 358 |
spi_writel(as, MR, SPI_BF(PCS, ~(0x01 << spi->chip_select)) | SPI_BIT(WDRBT) | SPI_BIT(MODFDIS) | SPI_BIT(MSTR)); |
d4820b749 spi/spi-atmel: de... |
359 |
} else { |
97ed465b4 spi/spi-atmel: ad... |
360 361 362 363 |
spi_writel(as, MR, SPI_BF(PCS, ~(0x01 << spi->chip_select)) | SPI_BIT(MODFDIS) | SPI_BIT(MSTR)); |
d4820b749 spi/spi-atmel: de... |
364 |
} |
1ccc404a7 spi/spi-atmel: ad... |
365 |
|
5ee36c989 spi: atmel_spi up... |
366 |
mr = spi_readl(as, MR); |
482030348 spi: atmel: add s... |
367 368 |
if (as->use_cs_gpios) gpio_set_value(asd->npcs_pin, active); |
5ee36c989 spi: atmel_spi up... |
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 |
} else { u32 cpol = (spi->mode & SPI_CPOL) ? SPI_BIT(CPOL) : 0; int i; u32 csr; /* Make sure clock polarity is correct */ for (i = 0; i < spi->master->num_chipselect; i++) { csr = spi_readl(as, CSR0 + 4 * i); if ((csr ^ cpol) & SPI_BIT(CPOL)) spi_writel(as, CSR0 + 4 * i, csr ^ SPI_BIT(CPOL)); } mr = spi_readl(as, MR); mr = SPI_BFINS(PCS, ~(1 << spi->chip_select), mr); |
482030348 spi: atmel: add s... |
384 |
if (as->use_cs_gpios && spi->chip_select != 0) |
5ee36c989 spi: atmel_spi up... |
385 386 387 |
gpio_set_value(asd->npcs_pin, active); spi_writel(as, MR, mr); } |
defbd3b4b atmel_spi: don't ... |
388 389 390 |
dev_dbg(&spi->dev, "activate %u%s, mr %08x ", |
5ee36c989 spi: atmel_spi up... |
391 |
asd->npcs_pin, active ? " (high)" : "", |
defbd3b4b atmel_spi: don't ... |
392 |
mr); |
754ce4f29 [PATCH] SPI: atme... |
393 |
} |
defbd3b4b atmel_spi: don't ... |
394 |
static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi) |
754ce4f29 [PATCH] SPI: atme... |
395 |
{ |
5ee36c989 spi: atmel_spi up... |
396 |
struct atmel_spi_device *asd = spi->controller_state; |
754ce4f29 [PATCH] SPI: atme... |
397 |
unsigned active = spi->mode & SPI_CS_HIGH; |
defbd3b4b atmel_spi: don't ... |
398 399 400 401 402 403 404 405 406 407 |
u32 mr; /* only deactivate *this* device; sometimes transfers to * another device may be active when this routine is called. */ mr = spi_readl(as, MR); if (~SPI_BFEXT(PCS, mr) & (1 << spi->chip_select)) { mr = SPI_BFINS(PCS, 0xf, mr); spi_writel(as, MR, mr); } |
754ce4f29 [PATCH] SPI: atme... |
408 |
|
defbd3b4b atmel_spi: don't ... |
409 410 |
dev_dbg(&spi->dev, "DEactivate %u%s, mr %08x ", |
5ee36c989 spi: atmel_spi up... |
411 |
asd->npcs_pin, active ? " (low)" : "", |
defbd3b4b atmel_spi: don't ... |
412 |
mr); |
482030348 spi: atmel: add s... |
413 414 415 |
if (!as->use_cs_gpios) spi_writel(as, CR, SPI_BIT(LASTXFER)); else if (atmel_spi_is_v2(as) || spi->chip_select != 0) |
5ee36c989 spi: atmel_spi up... |
416 |
gpio_set_value(asd->npcs_pin, !active); |
754ce4f29 [PATCH] SPI: atme... |
417 |
} |
6c07ef298 spi/atmel: Annota... |
418 |
static void atmel_spi_lock(struct atmel_spi *as) __acquires(&as->lock) |
8aad7924b spi/spi-atmel: ad... |
419 420 421 |
{ spin_lock_irqsave(&as->lock, as->flags); } |
6c07ef298 spi/atmel: Annota... |
422 |
static void atmel_spi_unlock(struct atmel_spi *as) __releases(&as->lock) |
8aad7924b spi/spi-atmel: ad... |
423 424 425 |
{ spin_unlock_irqrestore(&as->lock, as->flags); } |
1ccc404a7 spi/spi-atmel: ad... |
426 427 428 429 430 |
static inline bool atmel_spi_use_dma(struct atmel_spi *as, struct spi_transfer *xfer) { return as->use_dma && xfer->len >= DMA_MIN_BYTES; } |
04242ca4e spi: atmel: Use S... |
431 432 433 434 435 436 437 438 |
static bool atmel_spi_can_dma(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) { struct atmel_spi *as = spi_master_get_devdata(master); return atmel_spi_use_dma(as, xfer); } |
1ccc404a7 spi/spi-atmel: ad... |
439 440 441 442 |
static int atmel_spi_dma_slave_config(struct atmel_spi *as, struct dma_slave_config *slave_config, u8 bits_per_word) { |
768f3d9d8 spi: atmel: remov... |
443 |
struct spi_master *master = platform_get_drvdata(as->pdev); |
1ccc404a7 spi/spi-atmel: ad... |
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 |
int err = 0; if (bits_per_word > 8) { slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; slave_config->src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; } else { slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; slave_config->src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; } slave_config->dst_addr = (dma_addr_t)as->phybase + SPI_TDR; slave_config->src_addr = (dma_addr_t)as->phybase + SPI_RDR; slave_config->src_maxburst = 1; slave_config->dst_maxburst = 1; slave_config->device_fc = false; |
11f2764fe spi: atmel: add s... |
459 460 461 462 463 464 465 466 467 468 469 470 471 472 |
/* * This driver uses fixed peripheral select mode (PS bit set to '0' in * the Mode Register). * So according to the datasheet, when FIFOs are available (and * enabled), the Transmit FIFO operates in Multiple Data Mode. * In this mode, up to 2 data, not 4, can be written into the Transmit * Data Register in a single access. * However, the first data has to be written into the lowest 16 bits and * the second data into the highest 16 bits of the Transmit * Data Register. For 8bit data (the most frequent case), it would * require to rework tx_buf so each data would actualy fit 16 bits. * So we'd rather write only one data at the time. Hence the transmit * path works the same whether FIFOs are available (and enabled) or not. */ |
1ccc404a7 spi/spi-atmel: ad... |
473 |
slave_config->direction = DMA_MEM_TO_DEV; |
768f3d9d8 spi: atmel: remov... |
474 |
if (dmaengine_slave_config(master->dma_tx, slave_config)) { |
1ccc404a7 spi/spi-atmel: ad... |
475 476 477 478 479 |
dev_err(&as->pdev->dev, "failed to configure tx dma channel "); err = -EINVAL; } |
11f2764fe spi: atmel: add s... |
480 481 482 483 484 485 486 487 |
/* * This driver configures the spi controller for master mode (MSTR bit * set to '1' in the Mode Register). * So according to the datasheet, when FIFOs are available (and * enabled), the Receive FIFO operates in Single Data Mode. * So the receive path works the same whether FIFOs are available (and * enabled) or not. */ |
1ccc404a7 spi/spi-atmel: ad... |
488 |
slave_config->direction = DMA_DEV_TO_MEM; |
768f3d9d8 spi: atmel: remov... |
489 |
if (dmaengine_slave_config(master->dma_rx, slave_config)) { |
1ccc404a7 spi/spi-atmel: ad... |
490 491 492 493 494 495 496 497 |
dev_err(&as->pdev->dev, "failed to configure rx dma channel "); err = -EINVAL; } return err; } |
768f3d9d8 spi: atmel: remov... |
498 499 |
static int atmel_spi_configure_dma(struct spi_master *master, struct atmel_spi *as) |
1ccc404a7 spi/spi-atmel: ad... |
500 |
{ |
1ccc404a7 spi/spi-atmel: ad... |
501 |
struct dma_slave_config slave_config; |
2f767a9f6 spi: atmel: conve... |
502 |
struct device *dev = &as->pdev->dev; |
1ccc404a7 spi/spi-atmel: ad... |
503 |
int err; |
2f767a9f6 spi: atmel: conve... |
504 505 506 |
dma_cap_mask_t mask; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); |
1ccc404a7 spi/spi-atmel: ad... |
507 |
|
768f3d9d8 spi: atmel: remov... |
508 509 510 |
master->dma_tx = dma_request_slave_channel_reason(dev, "tx"); if (IS_ERR(master->dma_tx)) { err = PTR_ERR(master->dma_tx); |
5e9af37e4 spi: atmel: intro... |
511 512 513 |
if (err == -EPROBE_DEFER) { dev_warn(dev, "no DMA channel available at the moment "); |
768f3d9d8 spi: atmel: remov... |
514 |
goto error_clear; |
5e9af37e4 spi: atmel: intro... |
515 |
} |
2f767a9f6 spi: atmel: conve... |
516 517 518 519 |
dev_err(dev, "DMA TX channel not available, SPI unable to use DMA "); err = -EBUSY; |
768f3d9d8 spi: atmel: remov... |
520 |
goto error_clear; |
1ccc404a7 spi/spi-atmel: ad... |
521 |
} |
2f767a9f6 spi: atmel: conve... |
522 |
|
5e9af37e4 spi: atmel: intro... |
523 524 525 526 |
/* * No reason to check EPROBE_DEFER here since we have already requested * tx channel. If it fails here, it's for another reason. */ |
768f3d9d8 spi: atmel: remov... |
527 |
master->dma_rx = dma_request_slave_channel(dev, "rx"); |
2f767a9f6 spi: atmel: conve... |
528 |
|
768f3d9d8 spi: atmel: remov... |
529 |
if (!master->dma_rx) { |
2f767a9f6 spi: atmel: conve... |
530 531 532 |
dev_err(dev, "DMA RX channel not available, SPI unable to use DMA "); |
1ccc404a7 spi/spi-atmel: ad... |
533 534 535 536 537 538 539 540 541 542 543 |
err = -EBUSY; goto error; } err = atmel_spi_dma_slave_config(as, &slave_config, 8); if (err) goto error; dev_info(&as->pdev->dev, "Using %s (tx) and %s (rx) for DMA transfers ", |
768f3d9d8 spi: atmel: remov... |
544 545 |
dma_chan_name(master->dma_tx), dma_chan_name(master->dma_rx)); |
1ccc404a7 spi/spi-atmel: ad... |
546 547 |
return 0; error: |
768f3d9d8 spi: atmel: remov... |
548 549 550 551 552 553 |
if (master->dma_rx) dma_release_channel(master->dma_rx); if (!IS_ERR(master->dma_tx)) dma_release_channel(master->dma_tx); error_clear: master->dma_tx = master->dma_rx = NULL; |
1ccc404a7 spi/spi-atmel: ad... |
554 555 |
return err; } |
768f3d9d8 spi: atmel: remov... |
556 |
static void atmel_spi_stop_dma(struct spi_master *master) |
1ccc404a7 spi/spi-atmel: ad... |
557 |
{ |
768f3d9d8 spi: atmel: remov... |
558 559 560 561 |
if (master->dma_rx) dmaengine_terminate_all(master->dma_rx); if (master->dma_tx) dmaengine_terminate_all(master->dma_tx); |
1ccc404a7 spi/spi-atmel: ad... |
562 |
} |
768f3d9d8 spi: atmel: remov... |
563 |
static void atmel_spi_release_dma(struct spi_master *master) |
1ccc404a7 spi/spi-atmel: ad... |
564 |
{ |
768f3d9d8 spi: atmel: remov... |
565 566 567 568 569 570 571 572 |
if (master->dma_rx) { dma_release_channel(master->dma_rx); master->dma_rx = NULL; } if (master->dma_tx) { dma_release_channel(master->dma_tx); master->dma_tx = NULL; } |
1ccc404a7 spi/spi-atmel: ad... |
573 574 575 576 577 578 579 |
} /* This function is called by the DMA driver from tasklet context */ static void dma_callback(void *data) { struct spi_master *master = data; struct atmel_spi *as = spi_master_get_devdata(master); |
8090d6d1a spi: atmel: Refac... |
580 |
complete(&as->xfer_completion); |
1ccc404a7 spi/spi-atmel: ad... |
581 582 583 |
} /* |
11f2764fe spi: atmel: add s... |
584 |
* Next transfer using PIO without FIFO. |
1ccc404a7 spi/spi-atmel: ad... |
585 |
*/ |
11f2764fe spi: atmel: add s... |
586 587 |
static void atmel_spi_next_xfer_single(struct spi_master *master, struct spi_transfer *xfer) |
1ccc404a7 spi/spi-atmel: ad... |
588 589 |
{ struct atmel_spi *as = spi_master_get_devdata(master); |
8090d6d1a spi: atmel: Refac... |
590 |
unsigned long xfer_pos = xfer->len - as->current_remaining_bytes; |
1ccc404a7 spi/spi-atmel: ad... |
591 592 593 |
dev_vdbg(master->dev.parent, "atmel_spi_next_xfer_pio "); |
1ccc404a7 spi/spi-atmel: ad... |
594 595 596 597 598 599 |
/* Make sure data is not remaining in RDR */ spi_readl(as, RDR); while (spi_readl(as, SR) & SPI_BIT(RDRF)) { spi_readl(as, RDR); cpu_relax(); } |
7910d9af0 spi: atmel: Use c... |
600 601 602 603 |
if (xfer->bits_per_word > 8) spi_writel(as, TDR, *(u16 *)(xfer->tx_buf + xfer_pos)); else spi_writel(as, TDR, *(u8 *)(xfer->tx_buf + xfer_pos)); |
1ccc404a7 spi/spi-atmel: ad... |
604 605 |
dev_dbg(master->dev.parent, |
f557c98b1 spi/spi-atmel: BU... |
606 607 608 609 |
" start pio xfer %p: len %u tx %p rx %p bitpw %d ", xfer, xfer->len, xfer->tx_buf, xfer->rx_buf, xfer->bits_per_word); |
1ccc404a7 spi/spi-atmel: ad... |
610 611 612 613 614 615 |
/* Enable relevant interrupts */ spi_writel(as, IER, SPI_BIT(RDRF) | SPI_BIT(OVRES)); } /* |
11f2764fe spi: atmel: add s... |
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 |
* Next transfer using PIO with FIFO. */ static void atmel_spi_next_xfer_fifo(struct spi_master *master, struct spi_transfer *xfer) { struct atmel_spi *as = spi_master_get_devdata(master); u32 current_remaining_data, num_data; u32 offset = xfer->len - as->current_remaining_bytes; const u16 *words = (const u16 *)((u8 *)xfer->tx_buf + offset); const u8 *bytes = (const u8 *)((u8 *)xfer->tx_buf + offset); u16 td0, td1; u32 fifomr; dev_vdbg(master->dev.parent, "atmel_spi_next_xfer_fifo "); /* Compute the number of data to transfer in the current iteration */ current_remaining_data = ((xfer->bits_per_word > 8) ? ((u32)as->current_remaining_bytes >> 1) : (u32)as->current_remaining_bytes); num_data = min(current_remaining_data, as->fifo_size); /* Flush RX and TX FIFOs */ spi_writel(as, CR, SPI_BIT(RXFCLR) | SPI_BIT(TXFCLR)); while (spi_readl(as, FLR)) cpu_relax(); /* Set RX FIFO Threshold to the number of data to transfer */ fifomr = spi_readl(as, FMR); spi_writel(as, FMR, SPI_BFINS(RXFTHRES, num_data, fifomr)); /* Clear FIFO flags in the Status Register, especially RXFTHF */ (void)spi_readl(as, SR); /* Fill TX FIFO */ while (num_data >= 2) { |
7910d9af0 spi: atmel: Use c... |
652 653 654 |
if (xfer->bits_per_word > 8) { td0 = *words++; td1 = *words++; |
11f2764fe spi: atmel: add s... |
655 |
} else { |
7910d9af0 spi: atmel: Use c... |
656 657 |
td0 = *bytes++; td1 = *bytes++; |
11f2764fe spi: atmel: add s... |
658 659 660 661 662 663 664 |
} spi_writel(as, TDR, (td1 << 16) | td0); num_data -= 2; } if (num_data) { |
7910d9af0 spi: atmel: Use c... |
665 666 667 668 |
if (xfer->bits_per_word > 8) td0 = *words++; else td0 = *bytes++; |
11f2764fe spi: atmel: add s... |
669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 |
spi_writew(as, TDR, td0); num_data--; } dev_dbg(master->dev.parent, " start fifo xfer %p: len %u tx %p rx %p bitpw %d ", xfer, xfer->len, xfer->tx_buf, xfer->rx_buf, xfer->bits_per_word); /* * Enable RX FIFO Threshold Flag interrupt to be notified about * transfer completion. */ spi_writel(as, IER, SPI_BIT(RXFTHF) | SPI_BIT(OVRES)); } /* * Next transfer using PIO. */ static void atmel_spi_next_xfer_pio(struct spi_master *master, struct spi_transfer *xfer) { struct atmel_spi *as = spi_master_get_devdata(master); if (as->fifo_size) atmel_spi_next_xfer_fifo(master, xfer); else atmel_spi_next_xfer_single(master, xfer); } /* |
1ccc404a7 spi/spi-atmel: ad... |
702 |
* Submit next transfer for DMA. |
1ccc404a7 spi/spi-atmel: ad... |
703 704 705 706 707 708 |
*/ static int atmel_spi_next_xfer_dma_submit(struct spi_master *master, struct spi_transfer *xfer, u32 *plen) { struct atmel_spi *as = spi_master_get_devdata(master); |
768f3d9d8 spi: atmel: remov... |
709 710 |
struct dma_chan *rxchan = master->dma_rx; struct dma_chan *txchan = master->dma_tx; |
1ccc404a7 spi/spi-atmel: ad... |
711 712 713 714 |
struct dma_async_tx_descriptor *rxdesc; struct dma_async_tx_descriptor *txdesc; struct dma_slave_config slave_config; dma_cookie_t cookie; |
1ccc404a7 spi/spi-atmel: ad... |
715 716 717 718 719 720 721 722 723 724 |
dev_vdbg(master->dev.parent, "atmel_spi_next_xfer_dma_submit "); /* Check that the channels are available */ if (!rxchan || !txchan) return -ENODEV; /* release lock for DMA operations */ atmel_spi_unlock(as); |
04242ca4e spi: atmel: Use S... |
725 |
*plen = xfer->len; |
1ccc404a7 spi/spi-atmel: ad... |
726 |
|
06515f839 spi: atmel: Fix D... |
727 728 |
if (atmel_spi_dma_slave_config(as, &slave_config, xfer->bits_per_word)) |
1ccc404a7 spi/spi-atmel: ad... |
729 730 731 |
goto err_exit; /* Send both scatterlists */ |
04242ca4e spi: atmel: Use S... |
732 733 |
rxdesc = dmaengine_prep_slave_sg(rxchan, xfer->rx_sg.sgl, xfer->rx_sg.nents, |
ef40eb39e spi: atmel: Use d... |
734 735 |
DMA_FROM_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); |
1ccc404a7 spi/spi-atmel: ad... |
736 737 |
if (!rxdesc) goto err_dma; |
04242ca4e spi: atmel: Use S... |
738 739 |
txdesc = dmaengine_prep_slave_sg(txchan, xfer->tx_sg.sgl, xfer->tx_sg.nents, |
ef40eb39e spi: atmel: Use d... |
740 741 |
DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); |
1ccc404a7 spi/spi-atmel: ad... |
742 743 744 745 |
if (!txdesc) goto err_dma; dev_dbg(master->dev.parent, |
2de024b76 spi/atmel: Fix fo... |
746 747 748 749 |
" start dma xfer %p: len %u tx %p/%08llx rx %p/%08llx ", xfer, xfer->len, xfer->tx_buf, (unsigned long long)xfer->tx_dma, xfer->rx_buf, (unsigned long long)xfer->rx_dma); |
1ccc404a7 spi/spi-atmel: ad... |
750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 |
/* Enable relevant interrupts */ spi_writel(as, IER, SPI_BIT(OVRES)); /* Put the callback on the RX transfer only, that should finish last */ rxdesc->callback = dma_callback; rxdesc->callback_param = master; /* Submit and fire RX and TX with TX last so we're ready to read! */ cookie = rxdesc->tx_submit(rxdesc); if (dma_submit_error(cookie)) goto err_dma; cookie = txdesc->tx_submit(txdesc); if (dma_submit_error(cookie)) goto err_dma; rxchan->device->device_issue_pending(rxchan); txchan->device->device_issue_pending(txchan); /* take back lock */ atmel_spi_lock(as); return 0; err_dma: spi_writel(as, IDR, SPI_BIT(OVRES)); |
768f3d9d8 spi: atmel: remov... |
774 |
atmel_spi_stop_dma(master); |
1ccc404a7 spi/spi-atmel: ad... |
775 776 777 778 |
err_exit: atmel_spi_lock(as); return -ENOMEM; } |
154443c72 atmel_spi: chain ... |
779 780 781 782 783 784 |
static void atmel_spi_next_xfer_data(struct spi_master *master, struct spi_transfer *xfer, dma_addr_t *tx_dma, dma_addr_t *rx_dma, u32 *plen) { |
7910d9af0 spi: atmel: Use c... |
785 786 |
*rx_dma = xfer->rx_dma + xfer->len - *plen; *tx_dma = xfer->tx_dma + xfer->len - *plen; |
04242ca4e spi: atmel: Use S... |
787 788 |
if (*plen > master->max_dma_len) *plen = master->max_dma_len; |
154443c72 atmel_spi: chain ... |
789 |
} |
d3b72c7e6 spi: atmel: add s... |
790 791 792 793 794 795 796 797 |
static int atmel_spi_set_xfer_speed(struct atmel_spi *as, struct spi_device *spi, struct spi_transfer *xfer) { u32 scbr, csr; unsigned long bus_hz; /* v1 chips start out at half the peripheral bus speed. */ |
39fe33f98 spi: atmel: Fix s... |
798 |
bus_hz = as->spi_clk; |
d3b72c7e6 spi: atmel: add s... |
799 800 801 802 803 804 805 |
if (!atmel_spi_is_v2(as)) bus_hz /= 2; /* * Calculate the lowest divider that satisfies the * constraint, assuming div32/fdiv/mbz == 0. */ |
e8646580c spi: atmel: Remov... |
806 |
scbr = DIV_ROUND_UP(bus_hz, xfer->speed_hz); |
d3b72c7e6 spi: atmel: add s... |
807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 |
/* * If the resulting divider doesn't fit into the * register bitfield, we can't satisfy the constraint. */ if (scbr >= (1 << SPI_SCBR_SIZE)) { dev_err(&spi->dev, "setup: %d Hz too slow, scbr %u; min %ld Hz ", xfer->speed_hz, scbr, bus_hz/255); return -EINVAL; } if (scbr == 0) { dev_err(&spi->dev, "setup: %d Hz too high, scbr %u; max %ld Hz ", xfer->speed_hz, scbr, bus_hz); return -EINVAL; } csr = spi_readl(as, CSR0 + 4 * spi->chip_select); csr = SPI_BFINS(SCBR, scbr, csr); spi_writel(as, CSR0 + 4 * spi->chip_select, csr); return 0; } |
754ce4f29 [PATCH] SPI: atme... |
832 |
/* |
1ccc404a7 spi/spi-atmel: ad... |
833 |
* Submit next transfer for PDC. |
754ce4f29 [PATCH] SPI: atme... |
834 835 |
* lock is held, spi irq is blocked */ |
1ccc404a7 spi/spi-atmel: ad... |
836 |
static void atmel_spi_pdc_next_xfer(struct spi_master *master, |
8090d6d1a spi: atmel: Refac... |
837 838 |
struct spi_message *msg, struct spi_transfer *xfer) |
754ce4f29 [PATCH] SPI: atme... |
839 840 |
{ struct atmel_spi *as = spi_master_get_devdata(master); |
8090d6d1a spi: atmel: Refac... |
841 |
u32 len; |
754ce4f29 [PATCH] SPI: atme... |
842 |
dma_addr_t tx_dma, rx_dma; |
8090d6d1a spi: atmel: Refac... |
843 |
spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS)); |
754ce4f29 [PATCH] SPI: atme... |
844 |
|
8090d6d1a spi: atmel: Refac... |
845 846 847 |
len = as->current_remaining_bytes; atmel_spi_next_xfer_data(master, xfer, &tx_dma, &rx_dma, &len); as->current_remaining_bytes -= len; |
754ce4f29 [PATCH] SPI: atme... |
848 |
|
8090d6d1a spi: atmel: Refac... |
849 850 |
spi_writel(as, RPR, rx_dma); spi_writel(as, TPR, tx_dma); |
754ce4f29 [PATCH] SPI: atme... |
851 |
|
8090d6d1a spi: atmel: Refac... |
852 853 854 855 |
if (msg->spi->bits_per_word > 8) len >>= 1; spi_writel(as, RCR, len); spi_writel(as, TCR, len); |
754ce4f29 [PATCH] SPI: atme... |
856 |
|
8090d6d1a spi: atmel: Refac... |
857 858 859 860 861 862 |
dev_dbg(&msg->spi->dev, " start xfer %p: len %u tx %p/%08llx rx %p/%08llx ", xfer, xfer->len, xfer->tx_buf, (unsigned long long)xfer->tx_dma, xfer->rx_buf, (unsigned long long)xfer->rx_dma); |
dc329442b atmel_spi: fix ha... |
863 |
|
8090d6d1a spi: atmel: Refac... |
864 865 |
if (as->current_remaining_bytes) { len = as->current_remaining_bytes; |
154443c72 atmel_spi: chain ... |
866 |
atmel_spi_next_xfer_data(master, xfer, &tx_dma, &rx_dma, &len); |
8090d6d1a spi: atmel: Refac... |
867 |
as->current_remaining_bytes -= len; |
754ce4f29 [PATCH] SPI: atme... |
868 |
|
154443c72 atmel_spi: chain ... |
869 870 |
spi_writel(as, RNPR, rx_dma); spi_writel(as, TNPR, tx_dma); |
754ce4f29 [PATCH] SPI: atme... |
871 |
|
154443c72 atmel_spi: chain ... |
872 873 874 875 |
if (msg->spi->bits_per_word > 8) len >>= 1; spi_writel(as, RNCR, len); spi_writel(as, TNCR, len); |
8bacb2190 atmel_spi: fix dm... |
876 877 |
dev_dbg(&msg->spi->dev, |
2de024b76 spi/atmel: Fix fo... |
878 879 880 881 882 |
" next xfer %p: len %u tx %p/%08llx rx %p/%08llx ", xfer, xfer->len, xfer->tx_buf, (unsigned long long)xfer->tx_dma, xfer->rx_buf, (unsigned long long)xfer->rx_dma); |
154443c72 atmel_spi: chain ... |
883 |
} |
76e1d14b3 spi: atmel: Fix i... |
884 |
/* REVISIT: We're waiting for RXBUFF before we start the next |
754ce4f29 [PATCH] SPI: atme... |
885 |
* transfer because we need to handle some difficult timing |
76e1d14b3 spi: atmel: Fix i... |
886 887 888 889 |
* issues otherwise. If we wait for TXBUFE in one transfer and * then starts waiting for RXBUFF in the next, it's difficult * to tell the difference between the RXBUFF interrupt we're * actually waiting for and the RXBUFF interrupt of the |
754ce4f29 [PATCH] SPI: atme... |
890 891 892 893 |
* previous transfer. * * It should be doable, though. Just not now... */ |
76e1d14b3 spi: atmel: Fix i... |
894 |
spi_writel(as, IER, SPI_BIT(RXBUFF) | SPI_BIT(OVRES)); |
754ce4f29 [PATCH] SPI: atme... |
895 896 |
spi_writel(as, PTCR, SPI_BIT(TXTEN) | SPI_BIT(RXTEN)); } |
1ccc404a7 spi/spi-atmel: ad... |
897 |
/* |
8da0859a2 atmel_spi: minor ... |
898 899 |
* For DMA, tx_buf/tx_dma have the same relationship as rx_buf/rx_dma: * - The buffer is either valid for CPU access, else NULL |
b595076a1 tree-wide: fix co... |
900 |
* - If the buffer is valid, so is its DMA address |
8da0859a2 atmel_spi: minor ... |
901 |
* |
b595076a1 tree-wide: fix co... |
902 |
* This driver manages the dma address unless message->is_dma_mapped. |
8da0859a2 atmel_spi: minor ... |
903 904 |
*/ static int |
754ce4f29 [PATCH] SPI: atme... |
905 906 |
atmel_spi_dma_map_xfer(struct atmel_spi *as, struct spi_transfer *xfer) { |
8da0859a2 atmel_spi: minor ... |
907 |
struct device *dev = &as->pdev->dev; |
754ce4f29 [PATCH] SPI: atme... |
908 |
xfer->tx_dma = xfer->rx_dma = INVALID_DMA_ADDRESS; |
8da0859a2 atmel_spi: minor ... |
909 |
if (xfer->tx_buf) { |
214b574ab atmel_spi: fix wa... |
910 911 912 |
/* tx_buf is a const void* where we need a void * for the dma * mapping */ void *nonconst_tx = (void *)xfer->tx_buf; |
8da0859a2 atmel_spi: minor ... |
913 |
xfer->tx_dma = dma_map_single(dev, |
214b574ab atmel_spi: fix wa... |
914 |
nonconst_tx, xfer->len, |
754ce4f29 [PATCH] SPI: atme... |
915 |
DMA_TO_DEVICE); |
8d8bb39b9 dma-mapping: add ... |
916 |
if (dma_mapping_error(dev, xfer->tx_dma)) |
8da0859a2 atmel_spi: minor ... |
917 918 919 920 |
return -ENOMEM; } if (xfer->rx_buf) { xfer->rx_dma = dma_map_single(dev, |
754ce4f29 [PATCH] SPI: atme... |
921 922 |
xfer->rx_buf, xfer->len, DMA_FROM_DEVICE); |
8d8bb39b9 dma-mapping: add ... |
923 |
if (dma_mapping_error(dev, xfer->rx_dma)) { |
8da0859a2 atmel_spi: minor ... |
924 925 926 927 928 929 930 931 |
if (xfer->tx_buf) dma_unmap_single(dev, xfer->tx_dma, xfer->len, DMA_TO_DEVICE); return -ENOMEM; } } return 0; |
754ce4f29 [PATCH] SPI: atme... |
932 933 934 935 936 937 |
} static void atmel_spi_dma_unmap_xfer(struct spi_master *master, struct spi_transfer *xfer) { if (xfer->tx_dma != INVALID_DMA_ADDRESS) |
49dce689a spi doesn't need ... |
938 |
dma_unmap_single(master->dev.parent, xfer->tx_dma, |
754ce4f29 [PATCH] SPI: atme... |
939 940 |
xfer->len, DMA_TO_DEVICE); if (xfer->rx_dma != INVALID_DMA_ADDRESS) |
49dce689a spi doesn't need ... |
941 |
dma_unmap_single(master->dev.parent, xfer->rx_dma, |
754ce4f29 [PATCH] SPI: atme... |
942 943 |
xfer->len, DMA_FROM_DEVICE); } |
1ccc404a7 spi/spi-atmel: ad... |
944 945 946 947 |
static void atmel_spi_disable_pdc_transfer(struct atmel_spi *as) { spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS)); } |
1ccc404a7 spi/spi-atmel: ad... |
948 |
static void |
11f2764fe spi: atmel: add s... |
949 |
atmel_spi_pump_single_data(struct atmel_spi *as, struct spi_transfer *xfer) |
1ccc404a7 spi/spi-atmel: ad... |
950 |
{ |
1ccc404a7 spi/spi-atmel: ad... |
951 |
u8 *rxp; |
f557c98b1 spi/spi-atmel: BU... |
952 |
u16 *rxp16; |
1ccc404a7 spi/spi-atmel: ad... |
953 |
unsigned long xfer_pos = xfer->len - as->current_remaining_bytes; |
7910d9af0 spi: atmel: Use c... |
954 955 956 |
if (xfer->bits_per_word > 8) { rxp16 = (u16 *)(((u8 *)xfer->rx_buf) + xfer_pos); *rxp16 = spi_readl(as, RDR); |
1ccc404a7 spi/spi-atmel: ad... |
957 |
} else { |
7910d9af0 spi: atmel: Use c... |
958 959 |
rxp = ((u8 *)xfer->rx_buf) + xfer_pos; *rxp = spi_readl(as, RDR); |
1ccc404a7 spi/spi-atmel: ad... |
960 |
} |
f557c98b1 spi/spi-atmel: BU... |
961 |
if (xfer->bits_per_word > 8) { |
b112f0585 spi: atmel: fix i... |
962 963 964 |
if (as->current_remaining_bytes > 2) as->current_remaining_bytes -= 2; else |
f557c98b1 spi/spi-atmel: BU... |
965 966 967 968 |
as->current_remaining_bytes = 0; } else { as->current_remaining_bytes--; } |
1ccc404a7 spi/spi-atmel: ad... |
969 |
} |
11f2764fe spi: atmel: add s... |
970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 |
static void atmel_spi_pump_fifo_data(struct atmel_spi *as, struct spi_transfer *xfer) { u32 fifolr = spi_readl(as, FLR); u32 num_bytes, num_data = SPI_BFEXT(RXFL, fifolr); u32 offset = xfer->len - as->current_remaining_bytes; u16 *words = (u16 *)((u8 *)xfer->rx_buf + offset); u8 *bytes = (u8 *)((u8 *)xfer->rx_buf + offset); u16 rd; /* RD field is the lowest 16 bits of RDR */ /* Update the number of remaining bytes to transfer */ num_bytes = ((xfer->bits_per_word > 8) ? (num_data << 1) : num_data); if (as->current_remaining_bytes > num_bytes) as->current_remaining_bytes -= num_bytes; else as->current_remaining_bytes = 0; /* Handle odd number of bytes when data are more than 8bit width */ if (xfer->bits_per_word > 8) as->current_remaining_bytes &= ~0x1; /* Read data */ while (num_data) { rd = spi_readl(as, RDR); |
7910d9af0 spi: atmel: Use c... |
997 998 999 1000 |
if (xfer->bits_per_word > 8) *words++ = rd; else *bytes++ = rd; |
11f2764fe spi: atmel: add s... |
1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 |
num_data--; } } /* Called from IRQ * * Must update "current_remaining_bytes" to keep track of data * to transfer. */ static void atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer) { if (as->fifo_size) atmel_spi_pump_fifo_data(as, xfer); else atmel_spi_pump_single_data(as, xfer); } |
1ccc404a7 spi/spi-atmel: ad... |
1018 1019 1020 |
/* Interrupt * * No need for locking in this Interrupt handler: done_status is the |
8090d6d1a spi: atmel: Refac... |
1021 |
* only information modified. |
1ccc404a7 spi/spi-atmel: ad... |
1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 |
*/ static irqreturn_t atmel_spi_pio_interrupt(int irq, void *dev_id) { struct spi_master *master = dev_id; struct atmel_spi *as = spi_master_get_devdata(master); u32 status, pending, imr; struct spi_transfer *xfer; int ret = IRQ_NONE; imr = spi_readl(as, IMR); status = spi_readl(as, SR); pending = status & imr; if (pending & SPI_BIT(OVRES)) { ret = IRQ_HANDLED; spi_writel(as, IDR, SPI_BIT(OVRES)); dev_warn(master->dev.parent, "overrun "); /* * When we get an overrun, we disregard the current * transfer. Data will not be copied back from any * bounce buffer and msg->actual_len will not be * updated with the last xfer. * * We will also not process any remaning transfers in * the message. |
1ccc404a7 spi/spi-atmel: ad... |
1050 1051 1052 1053 1054 1055 |
*/ as->done_status = -EIO; smp_wmb(); /* Clear any overrun happening while cleaning up */ spi_readl(as, SR); |
8090d6d1a spi: atmel: Refac... |
1056 |
complete(&as->xfer_completion); |
1ccc404a7 spi/spi-atmel: ad... |
1057 |
|
11f2764fe spi: atmel: add s... |
1058 |
} else if (pending & (SPI_BIT(RDRF) | SPI_BIT(RXFTHF))) { |
1ccc404a7 spi/spi-atmel: ad... |
1059 1060 1061 1062 1063 1064 |
atmel_spi_lock(as); if (as->current_remaining_bytes) { ret = IRQ_HANDLED; xfer = as->current_transfer; atmel_spi_pump_pio_data(as, xfer); |
8090d6d1a spi: atmel: Refac... |
1065 |
if (!as->current_remaining_bytes) |
1ccc404a7 spi/spi-atmel: ad... |
1066 |
spi_writel(as, IDR, pending); |
8090d6d1a spi: atmel: Refac... |
1067 1068 |
complete(&as->xfer_completion); |
1ccc404a7 spi/spi-atmel: ad... |
1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 |
} atmel_spi_unlock(as); } else { WARN_ONCE(pending, "IRQ not handled, pending = %x ", pending); ret = IRQ_HANDLED; spi_writel(as, IDR, pending); } return ret; |
754ce4f29 [PATCH] SPI: atme... |
1080 1081 1082 |
} static irqreturn_t |
1ccc404a7 spi/spi-atmel: ad... |
1083 |
atmel_spi_pdc_interrupt(int irq, void *dev_id) |
754ce4f29 [PATCH] SPI: atme... |
1084 1085 1086 |
{ struct spi_master *master = dev_id; struct atmel_spi *as = spi_master_get_devdata(master); |
754ce4f29 [PATCH] SPI: atme... |
1087 1088 |
u32 status, pending, imr; int ret = IRQ_NONE; |
754ce4f29 [PATCH] SPI: atme... |
1089 1090 1091 1092 1093 |
imr = spi_readl(as, IMR); status = spi_readl(as, SR); pending = status & imr; if (pending & SPI_BIT(OVRES)) { |
754ce4f29 [PATCH] SPI: atme... |
1094 1095 |
ret = IRQ_HANDLED; |
dc329442b atmel_spi: fix ha... |
1096 |
spi_writel(as, IDR, (SPI_BIT(RXBUFF) | SPI_BIT(ENDRX) |
754ce4f29 [PATCH] SPI: atme... |
1097 |
| SPI_BIT(OVRES))); |
754ce4f29 [PATCH] SPI: atme... |
1098 1099 |
/* Clear any overrun happening while cleaning up */ spi_readl(as, SR); |
823cd0454 spi/spi-atmel: st... |
1100 |
as->done_status = -EIO; |
8090d6d1a spi: atmel: Refac... |
1101 1102 |
complete(&as->xfer_completion); |
dc329442b atmel_spi: fix ha... |
1103 |
} else if (pending & (SPI_BIT(RXBUFF) | SPI_BIT(ENDRX))) { |
754ce4f29 [PATCH] SPI: atme... |
1104 1105 1106 |
ret = IRQ_HANDLED; spi_writel(as, IDR, pending); |
8090d6d1a spi: atmel: Refac... |
1107 |
complete(&as->xfer_completion); |
754ce4f29 [PATCH] SPI: atme... |
1108 |
} |
754ce4f29 [PATCH] SPI: atme... |
1109 1110 |
return ret; } |
754ce4f29 [PATCH] SPI: atme... |
1111 1112 1113 |
static int atmel_spi_setup(struct spi_device *spi) { struct atmel_spi *as; |
5ee36c989 spi: atmel_spi up... |
1114 |
struct atmel_spi_device *asd; |
d3b72c7e6 spi: atmel: add s... |
1115 |
u32 csr; |
754ce4f29 [PATCH] SPI: atme... |
1116 |
unsigned int bits = spi->bits_per_word; |
754ce4f29 [PATCH] SPI: atme... |
1117 |
unsigned int npcs_pin; |
754ce4f29 [PATCH] SPI: atme... |
1118 1119 |
as = spi_master_get_devdata(spi->master); |
defbd3b4b atmel_spi: don't ... |
1120 |
/* see notes above re chipselect */ |
d4820b749 spi/spi-atmel: de... |
1121 |
if (!atmel_spi_is_v2(as) |
defbd3b4b atmel_spi: don't ... |
1122 1123 1124 1125 1126 1127 |
&& spi->chip_select == 0 && (spi->mode & SPI_CS_HIGH)) { dev_dbg(&spi->dev, "setup: can't be active-high "); return -EINVAL; } |
d3b72c7e6 spi: atmel: add s... |
1128 |
csr = SPI_BF(BITS, bits - 8); |
754ce4f29 [PATCH] SPI: atme... |
1129 1130 1131 1132 |
if (spi->mode & SPI_CPOL) csr |= SPI_BIT(CPOL); if (!(spi->mode & SPI_CPHA)) csr |= SPI_BIT(NCPHA); |
482030348 spi: atmel: add s... |
1133 1134 |
if (!as->use_cs_gpios) csr |= SPI_BIT(CSAAT); |
754ce4f29 [PATCH] SPI: atme... |
1135 |
|
1eed29df4 atmel_spi through... |
1136 1137 1138 1139 1140 1141 1142 1143 |
/* DLYBS is mostly irrelevant since we manage chipselect using GPIOs. * * DLYBCT would add delays between words, slowing down transfers. * It could potentially be useful to cope with DMA bottlenecks, but * in those cases it's probably best to just use a lower bitrate. */ csr |= SPI_BF(DLYBS, 0); csr |= SPI_BF(DLYBCT, 0); |
754ce4f29 [PATCH] SPI: atme... |
1144 1145 |
/* chipselect must have been muxed as GPIO (e.g. in board setup) */ |
67f08d690 spi/atmel: Fix po... |
1146 |
npcs_pin = (unsigned long)spi->controller_data; |
850a5b670 spi/atmel: add DT... |
1147 |
|
482030348 spi: atmel: add s... |
1148 1149 1150 |
if (!as->use_cs_gpios) npcs_pin = spi->chip_select; else if (gpio_is_valid(spi->cs_gpio)) |
850a5b670 spi/atmel: add DT... |
1151 |
npcs_pin = spi->cs_gpio; |
5ee36c989 spi: atmel_spi up... |
1152 1153 1154 1155 1156 |
asd = spi->controller_state; if (!asd) { asd = kzalloc(sizeof(struct atmel_spi_device), GFP_KERNEL); if (!asd) return -ENOMEM; |
961062007 spi: atmel: use m... |
1157 |
if (as->use_cs_gpios) |
482030348 spi: atmel: add s... |
1158 1159 |
gpio_direction_output(npcs_pin, !(spi->mode & SPI_CS_HIGH)); |
5ee36c989 spi: atmel_spi up... |
1160 1161 1162 |
asd->npcs_pin = npcs_pin; spi->controller_state = asd; |
754ce4f29 [PATCH] SPI: atme... |
1163 |
} |
5ee36c989 spi: atmel_spi up... |
1164 |
asd->csr = csr; |
754ce4f29 [PATCH] SPI: atme... |
1165 |
dev_dbg(&spi->dev, |
d3b72c7e6 spi: atmel: add s... |
1166 1167 1168 |
"setup: bpw %u mode 0x%x -> csr%d %08x ", bits, spi->mode, spi->chip_select, csr); |
754ce4f29 [PATCH] SPI: atme... |
1169 |
|
d4820b749 spi/spi-atmel: de... |
1170 |
if (!atmel_spi_is_v2(as)) |
5ee36c989 spi: atmel_spi up... |
1171 |
spi_writel(as, CSR0 + 4 * spi->chip_select, csr); |
754ce4f29 [PATCH] SPI: atme... |
1172 1173 1174 |
return 0; } |
8090d6d1a spi: atmel: Refac... |
1175 1176 1177 |
static int atmel_spi_one_transfer(struct spi_master *master, struct spi_message *msg, struct spi_transfer *xfer) |
754ce4f29 [PATCH] SPI: atme... |
1178 1179 |
{ struct atmel_spi *as; |
8090d6d1a spi: atmel: Refac... |
1180 |
struct spi_device *spi = msg->spi; |
b9d228f9e spi/atmel: let tr... |
1181 |
u8 bits; |
8090d6d1a spi: atmel: Refac... |
1182 |
u32 len; |
b9d228f9e spi/atmel: let tr... |
1183 |
struct atmel_spi_device *asd; |
8090d6d1a spi: atmel: Refac... |
1184 1185 |
int timeout; int ret; |
1369dea64 spi: atmel: clean... |
1186 |
unsigned long dma_timeout; |
754ce4f29 [PATCH] SPI: atme... |
1187 |
|
8090d6d1a spi: atmel: Refac... |
1188 |
as = spi_master_get_devdata(master); |
754ce4f29 [PATCH] SPI: atme... |
1189 |
|
8090d6d1a spi: atmel: Refac... |
1190 1191 1192 |
if (!(xfer->tx_buf || xfer->rx_buf) && xfer->len) { dev_dbg(&spi->dev, "missing rx or tx buf "); |
754ce4f29 [PATCH] SPI: atme... |
1193 |
return -EINVAL; |
8090d6d1a spi: atmel: Refac... |
1194 |
} |
754ce4f29 [PATCH] SPI: atme... |
1195 |
|
e8646580c spi: atmel: Remov... |
1196 1197 1198 1199 |
asd = spi->controller_state; bits = (asd->csr >> 4) & 0xf; if (bits != xfer->bits_per_word - 8) { dev_dbg(&spi->dev, |
8090d6d1a spi: atmel: Refac... |
1200 1201 |
"you can't yet change bits_per_word in transfers "); |
e8646580c spi: atmel: Remov... |
1202 |
return -ENOPROTOOPT; |
8090d6d1a spi: atmel: Refac... |
1203 |
} |
754ce4f29 [PATCH] SPI: atme... |
1204 |
|
8090d6d1a spi: atmel: Refac... |
1205 1206 1207 1208 1209 |
/* * DMA map early, for performance (empties dcache ASAP) and * better fault reporting. */ if ((!msg->is_dma_mapped) |
04242ca4e spi: atmel: Use S... |
1210 |
&& as->use_pdc) { |
8090d6d1a spi: atmel: Refac... |
1211 1212 1213 1214 1215 |
if (atmel_spi_dma_map_xfer(as, xfer) < 0) return -ENOMEM; } atmel_spi_set_xfer_speed(as, msg->spi, xfer); |
754ce4f29 [PATCH] SPI: atme... |
1216 |
|
8090d6d1a spi: atmel: Refac... |
1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 |
as->done_status = 0; as->current_transfer = xfer; as->current_remaining_bytes = xfer->len; while (as->current_remaining_bytes) { reinit_completion(&as->xfer_completion); if (as->use_pdc) { atmel_spi_pdc_next_xfer(master, msg, xfer); } else if (atmel_spi_use_dma(as, xfer)) { len = as->current_remaining_bytes; ret = atmel_spi_next_xfer_dma_submit(master, xfer, &len); if (ret) { dev_err(&spi->dev, "unable to use DMA, fallback to PIO "); atmel_spi_next_xfer_pio(master, xfer); } else { as->current_remaining_bytes -= len; |
0c3b97487 spi: atmel: Make ... |
1236 1237 |
if (as->current_remaining_bytes < 0) as->current_remaining_bytes = 0; |
b9d228f9e spi/atmel: let tr... |
1238 |
} |
8090d6d1a spi: atmel: Refac... |
1239 1240 |
} else { atmel_spi_next_xfer_pio(master, xfer); |
b9d228f9e spi/atmel: let tr... |
1241 |
} |
1676014ef spi: atmel: Fix s... |
1242 1243 |
/* interrupts are disabled, so free the lock for schedule */ atmel_spi_unlock(as); |
1369dea64 spi: atmel: clean... |
1244 1245 |
dma_timeout = wait_for_completion_timeout(&as->xfer_completion, SPI_DMA_TIMEOUT); |
1676014ef spi: atmel: Fix s... |
1246 |
atmel_spi_lock(as); |
1369dea64 spi: atmel: clean... |
1247 1248 1249 |
if (WARN_ON(dma_timeout == 0)) { dev_err(&spi->dev, "spi transfer timeout "); |
8090d6d1a spi: atmel: Refac... |
1250 |
as->done_status = -EIO; |
f557c98b1 spi/spi-atmel: BU... |
1251 |
} |
8090d6d1a spi: atmel: Refac... |
1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 |
if (as->done_status) break; } if (as->done_status) { if (as->use_pdc) { dev_warn(master->dev.parent, "overrun (%u/%u remaining) ", spi_readl(as, TCR), spi_readl(as, RCR)); /* * Clean up DMA registers and make sure the data * registers are empty. */ spi_writel(as, RNCR, 0); spi_writel(as, TNCR, 0); spi_writel(as, RCR, 0); spi_writel(as, TCR, 0); for (timeout = 1000; timeout; timeout--) if (spi_readl(as, SR) & SPI_BIT(TXEMPTY)) break; if (!timeout) dev_warn(master->dev.parent, "timeout waiting for TXEMPTY"); while (spi_readl(as, SR) & SPI_BIT(RDRF)) spi_readl(as, RDR); /* Clear any overrun happening while cleaning up */ spi_readl(as, SR); } else if (atmel_spi_use_dma(as, xfer)) { |
768f3d9d8 spi: atmel: remov... |
1284 |
atmel_spi_stop_dma(master); |
8090d6d1a spi: atmel: Refac... |
1285 1286 1287 |
} if (!msg->is_dma_mapped |
04242ca4e spi: atmel: Use S... |
1288 |
&& as->use_pdc) |
8090d6d1a spi: atmel: Refac... |
1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 |
atmel_spi_dma_unmap_xfer(master, xfer); return 0; } else { /* only update length if no error */ msg->actual_length += xfer->len; } if (!msg->is_dma_mapped |
04242ca4e spi: atmel: Use S... |
1299 |
&& as->use_pdc) |
8090d6d1a spi: atmel: Refac... |
1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 |
atmel_spi_dma_unmap_xfer(master, xfer); if (xfer->delay_usecs) udelay(xfer->delay_usecs); if (xfer->cs_change) { if (list_is_last(&xfer->transfer_list, &msg->transfers)) { as->keep_cs = true; } else { as->cs_active = !as->cs_active; if (as->cs_active) cs_activate(as, msg->spi); else cs_deactivate(as, msg->spi); |
8da0859a2 atmel_spi: minor ... |
1315 |
} |
754ce4f29 [PATCH] SPI: atme... |
1316 |
} |
8090d6d1a spi: atmel: Refac... |
1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 |
return 0; } static int atmel_spi_transfer_one_message(struct spi_master *master, struct spi_message *msg) { struct atmel_spi *as; struct spi_transfer *xfer; struct spi_device *spi = msg->spi; int ret = 0; as = spi_master_get_devdata(master); dev_dbg(&spi->dev, "new message %p submitted for %s ", msg, dev_name(&spi->dev)); |
8090d6d1a spi: atmel: Refac... |
1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 |
atmel_spi_lock(as); cs_activate(as, spi); as->cs_active = true; as->keep_cs = false; msg->status = 0; msg->actual_length = 0; list_for_each_entry(xfer, &msg->transfers, transfer_list) { ret = atmel_spi_one_transfer(master, msg, xfer); if (ret) goto msg_done; } if (as->use_pdc) atmel_spi_disable_pdc_transfer(as); |
754ce4f29 [PATCH] SPI: atme... |
1350 |
list_for_each_entry(xfer, &msg->transfers, transfer_list) { |
8090d6d1a spi: atmel: Refac... |
1351 |
dev_dbg(&spi->dev, |
54f4c51cc spi: atmel: fix p... |
1352 1353 |
" xfer %p: len %u tx %p/%pad rx %p/%pad ", |
754ce4f29 [PATCH] SPI: atme... |
1354 |
xfer, xfer->len, |
54f4c51cc spi: atmel: fix p... |
1355 1356 |
xfer->tx_buf, &xfer->tx_dma, xfer->rx_buf, &xfer->rx_dma); |
754ce4f29 [PATCH] SPI: atme... |
1357 |
} |
8090d6d1a spi: atmel: Refac... |
1358 1359 1360 |
msg_done: if (!as->keep_cs) cs_deactivate(as, msg->spi); |
754ce4f29 [PATCH] SPI: atme... |
1361 |
|
8aad7924b spi/spi-atmel: ad... |
1362 |
atmel_spi_unlock(as); |
754ce4f29 [PATCH] SPI: atme... |
1363 |
|
8090d6d1a spi: atmel: Refac... |
1364 1365 1366 1367 |
msg->status = as->done_status; spi_finalize_current_message(spi->master); return ret; |
754ce4f29 [PATCH] SPI: atme... |
1368 |
} |
bb2d1c36c [PATCH] SPI contr... |
1369 |
static void atmel_spi_cleanup(struct spi_device *spi) |
754ce4f29 [PATCH] SPI: atme... |
1370 |
{ |
5ee36c989 spi: atmel_spi up... |
1371 |
struct atmel_spi_device *asd = spi->controller_state; |
defbd3b4b atmel_spi: don't ... |
1372 |
|
5ee36c989 spi: atmel_spi up... |
1373 |
if (!asd) |
defbd3b4b atmel_spi: don't ... |
1374 |
return; |
5ee36c989 spi: atmel_spi up... |
1375 |
spi->controller_state = NULL; |
5ee36c989 spi: atmel_spi up... |
1376 |
kfree(asd); |
754ce4f29 [PATCH] SPI: atme... |
1377 |
} |
d4820b749 spi/spi-atmel: de... |
1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 |
static inline unsigned int atmel_get_version(struct atmel_spi *as) { return spi_readl(as, VERSION) & 0x00000fff; } static void atmel_get_caps(struct atmel_spi *as) { unsigned int version; version = atmel_get_version(as); |
d4820b749 spi/spi-atmel: de... |
1388 1389 1390 |
as->caps.is_spi2 = version > 0x121; as->caps.has_wdrbt = version >= 0x210; |
7094576cc spi: atmel: fix c... |
1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 |
#ifdef CONFIG_SOC_SAM_V4_V5 /* * Atmel SoCs based on ARM9 (SAM9x) cores should not use spi_map_buf() * since this later function tries to map buffers with dma_map_sg() * even if they have not been allocated inside DMA-safe areas. * On SoCs based on Cortex A5 (SAMA5Dx), it works anyway because for * those ARM cores, the data cache follows the PIPT model. * Also the L2 cache controller of SAMA5D2 uses the PIPT model too. * In case of PIPT caches, there cannot be cache aliases. * However on ARM9 cores, the data cache follows the VIVT model, hence * the cache aliases issue can occur when buffers are allocated from * DMA-unsafe areas, by vmalloc() for instance, where cache coherency is * not taken into account or at least not handled completely (cache * lines of aliases are not invalidated). * This is not a theorical issue: it was reproduced when trying to mount * a UBI file-system on a at91sam9g35ek board. */ as->caps.has_dma_support = false; #else |
d4820b749 spi/spi-atmel: de... |
1410 |
as->caps.has_dma_support = version >= 0x212; |
7094576cc spi: atmel: fix c... |
1411 1412 |
#endif as->caps.has_pdc_support = version < 0x212; |
d4820b749 spi/spi-atmel: de... |
1413 |
} |
754ce4f29 [PATCH] SPI: atme... |
1414 |
/*-------------------------------------------------------------------------*/ |
961062007 spi: atmel: use m... |
1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 |
static int atmel_spi_gpio_cs(struct platform_device *pdev) { struct spi_master *master = platform_get_drvdata(pdev); struct atmel_spi *as = spi_master_get_devdata(master); struct device_node *np = master->dev.of_node; int i; int ret = 0; int nb = 0; if (!as->use_cs_gpios) return 0; if (!np) return 0; nb = of_gpio_named_count(np, "cs-gpios"); for (i = 0; i < nb; i++) { int cs_gpio = of_get_named_gpio(pdev->dev.of_node, "cs-gpios", i); |
b52b3484e spi: atmel: fix i... |
1434 1435 |
if (cs_gpio == -EPROBE_DEFER) return cs_gpio; |
961062007 spi: atmel: use m... |
1436 |
|
b52b3484e spi: atmel: fix i... |
1437 1438 1439 1440 1441 1442 |
if (gpio_is_valid(cs_gpio)) { ret = devm_gpio_request(&pdev->dev, cs_gpio, dev_name(&pdev->dev)); if (ret) return ret; } |
961062007 spi: atmel: use m... |
1443 1444 1445 1446 |
} return 0; } |
754ce4f29 [PATCH] SPI: atme... |
1447 |
|
05514c869 spi: atmel: facto... |
1448 1449 1450 1451 |
static void atmel_spi_init(struct atmel_spi *as) { spi_writel(as, CR, SPI_BIT(SWRST)); spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ |
faddb1768 spi: atmel: init ... |
1452 1453 1454 1455 |
/* It is recommended to enable FIFOs first thing after reset */ if (as->fifo_size) spi_writel(as, CR, SPI_BIT(FIFOEN)); |
05514c869 spi: atmel: facto... |
1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 |
if (as->caps.has_wdrbt) { spi_writel(as, MR, SPI_BIT(WDRBT) | SPI_BIT(MODFDIS) | SPI_BIT(MSTR)); } else { spi_writel(as, MR, SPI_BIT(MSTR) | SPI_BIT(MODFDIS)); } if (as->use_pdc) spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS)); spi_writel(as, CR, SPI_BIT(SPIEN)); |
05514c869 spi: atmel: facto... |
1466 |
} |
fd4a319bc spi: Remove HOTPL... |
1467 |
static int atmel_spi_probe(struct platform_device *pdev) |
754ce4f29 [PATCH] SPI: atme... |
1468 1469 1470 1471 1472 1473 1474 |
{ struct resource *regs; int irq; struct clk *clk; int ret; struct spi_master *master; struct atmel_spi *as; |
5bdfd491a spi: atmel: adopt... |
1475 1476 |
/* Select default pin state */ pinctrl_pm_select_default_state(&pdev->dev); |
754ce4f29 [PATCH] SPI: atme... |
1477 1478 1479 1480 1481 1482 1483 |
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!regs) return -ENXIO; irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; |
9f87d6f26 spi: atmel: Use d... |
1484 |
clk = devm_clk_get(&pdev->dev, "spi_clk"); |
754ce4f29 [PATCH] SPI: atme... |
1485 1486 1487 1488 1489 |
if (IS_ERR(clk)) return PTR_ERR(clk); /* setup spi core then atmel-specific driver state */ ret = -ENOMEM; |
a536d7654 spi: atmel: Silen... |
1490 |
master = spi_alloc_master(&pdev->dev, sizeof(*as)); |
754ce4f29 [PATCH] SPI: atme... |
1491 1492 |
if (!master) goto out_free; |
e7db06b5d spi: move more sp... |
1493 1494 |
/* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; |
24778be20 spi: convert driv... |
1495 |
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 16); |
850a5b670 spi/atmel: add DT... |
1496 |
master->dev.of_node = pdev->dev.of_node; |
754ce4f29 [PATCH] SPI: atme... |
1497 |
master->bus_num = pdev->id; |
850a5b670 spi/atmel: add DT... |
1498 |
master->num_chipselect = master->dev.of_node ? 0 : 4; |
754ce4f29 [PATCH] SPI: atme... |
1499 |
master->setup = atmel_spi_setup; |
7910d9af0 spi: atmel: Use c... |
1500 |
master->flags = (SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX); |
8090d6d1a spi: atmel: Refac... |
1501 |
master->transfer_one_message = atmel_spi_transfer_one_message; |
754ce4f29 [PATCH] SPI: atme... |
1502 |
master->cleanup = atmel_spi_cleanup; |
ce0c4caf2 spi/atmel: add su... |
1503 |
master->auto_runtime_pm = true; |
7910d9af0 spi: atmel: Use c... |
1504 |
master->max_dma_len = SPI_MAX_DMA_XFER; |
04242ca4e spi: atmel: Use S... |
1505 |
master->can_dma = atmel_spi_can_dma; |
754ce4f29 [PATCH] SPI: atme... |
1506 1507 1508 |
platform_set_drvdata(pdev, master); as = spi_master_get_devdata(master); |
754ce4f29 [PATCH] SPI: atme... |
1509 |
spin_lock_init(&as->lock); |
1ccc404a7 spi/spi-atmel: ad... |
1510 |
|
754ce4f29 [PATCH] SPI: atme... |
1511 |
as->pdev = pdev; |
31407478a spi/atmel: Conver... |
1512 |
as->regs = devm_ioremap_resource(&pdev->dev, regs); |
543c954d6 spi: atmel: fix r... |
1513 1514 |
if (IS_ERR(as->regs)) { ret = PTR_ERR(as->regs); |
7910d9af0 spi: atmel: Use c... |
1515 |
goto out_unmap_regs; |
543c954d6 spi: atmel: fix r... |
1516 |
} |
dfab30ee6 spi/spi-atmel: ad... |
1517 |
as->phybase = regs->start; |
754ce4f29 [PATCH] SPI: atme... |
1518 1519 |
as->irq = irq; as->clk = clk; |
754ce4f29 [PATCH] SPI: atme... |
1520 |
|
8090d6d1a spi: atmel: Refac... |
1521 |
init_completion(&as->xfer_completion); |
d4820b749 spi/spi-atmel: de... |
1522 |
atmel_get_caps(as); |
482030348 spi: atmel: add s... |
1523 1524 |
as->use_cs_gpios = true; if (atmel_spi_is_v2(as) && |
70f340df2 spi: atmel: fix g... |
1525 |
pdev->dev.of_node && |
482030348 spi: atmel: add s... |
1526 1527 1528 1529 |
!of_get_property(pdev->dev.of_node, "cs-gpios", NULL)) { as->use_cs_gpios = false; master->num_chipselect = 4; } |
961062007 spi: atmel: use m... |
1530 1531 1532 |
ret = atmel_spi_gpio_cs(pdev); if (ret) goto out_unmap_regs; |
1ccc404a7 spi/spi-atmel: ad... |
1533 1534 1535 |
as->use_dma = false; as->use_pdc = false; if (as->caps.has_dma_support) { |
768f3d9d8 spi: atmel: remov... |
1536 |
ret = atmel_spi_configure_dma(master, as); |
04242ca4e spi: atmel: Use S... |
1537 |
if (ret == 0) { |
1ccc404a7 spi/spi-atmel: ad... |
1538 |
as->use_dma = true; |
04242ca4e spi: atmel: Use S... |
1539 |
} else if (ret == -EPROBE_DEFER) { |
5e9af37e4 spi: atmel: intro... |
1540 |
return ret; |
04242ca4e spi: atmel: Use S... |
1541 |
} |
7094576cc spi: atmel: fix c... |
1542 |
} else if (as->caps.has_pdc_support) { |
1ccc404a7 spi/spi-atmel: ad... |
1543 1544 1545 1546 1547 1548 1549 1550 |
as->use_pdc = true; } if (as->caps.has_dma_support && !as->use_dma) dev_info(&pdev->dev, "Atmel SPI Controller using PIO only "); if (as->use_pdc) { |
9f87d6f26 spi: atmel: Use d... |
1551 1552 |
ret = devm_request_irq(&pdev->dev, irq, atmel_spi_pdc_interrupt, 0, dev_name(&pdev->dev), master); |
1ccc404a7 spi/spi-atmel: ad... |
1553 |
} else { |
9f87d6f26 spi: atmel: Use d... |
1554 1555 |
ret = devm_request_irq(&pdev->dev, irq, atmel_spi_pio_interrupt, 0, dev_name(&pdev->dev), master); |
1ccc404a7 spi/spi-atmel: ad... |
1556 |
} |
754ce4f29 [PATCH] SPI: atme... |
1557 1558 1559 1560 |
if (ret) goto out_unmap_regs; /* Initialize the hardware */ |
dfec4a6e4 spi: atmel: prepa... |
1561 1562 |
ret = clk_prepare_enable(clk); if (ret) |
de8cc234a spi: atmel: Fix i... |
1563 |
goto out_free_irq; |
39fe33f98 spi: atmel: Fix s... |
1564 1565 |
as->spi_clk = clk_get_rate(clk); |
11f2764fe spi: atmel: add s... |
1566 1567 1568 1569 1570 |
as->fifo_size = 0; if (!of_property_read_u32(pdev->dev.of_node, "atmel,fifo-size", &as->fifo_size)) { dev_info(&pdev->dev, "Using FIFO (%u data) ", as->fifo_size); |
11f2764fe spi: atmel: add s... |
1571 |
} |
05514c869 spi: atmel: facto... |
1572 |
atmel_spi_init(as); |
ce0c4caf2 spi/atmel: add su... |
1573 1574 1575 1576 |
pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_TIMEOUT); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); |
9f87d6f26 spi: atmel: Use d... |
1577 |
ret = devm_spi_register_master(&pdev->dev, master); |
754ce4f29 [PATCH] SPI: atme... |
1578 |
if (ret) |
1ccc404a7 spi/spi-atmel: ad... |
1579 |
goto out_free_dma; |
754ce4f29 [PATCH] SPI: atme... |
1580 |
|
ce24a513f spi: atmel: trivi... |
1581 |
/* go! */ |
6aba9c656 spi: atmel: print... |
1582 1583 1584 1585 |
dev_info(&pdev->dev, "Atmel SPI Controller version 0x%x at 0x%08lx (irq %d) ", atmel_get_version(as), (unsigned long)regs->start, irq); |
ce24a513f spi: atmel: trivi... |
1586 |
|
754ce4f29 [PATCH] SPI: atme... |
1587 |
return 0; |
1ccc404a7 spi/spi-atmel: ad... |
1588 |
out_free_dma: |
ce0c4caf2 spi/atmel: add su... |
1589 1590 |
pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); |
1ccc404a7 spi/spi-atmel: ad... |
1591 |
if (as->use_dma) |
768f3d9d8 spi: atmel: remov... |
1592 |
atmel_spi_release_dma(master); |
1ccc404a7 spi/spi-atmel: ad... |
1593 |
|
754ce4f29 [PATCH] SPI: atme... |
1594 |
spi_writel(as, CR, SPI_BIT(SWRST)); |
50d7d5bf3 atmel_spi: work-a... |
1595 |
spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ |
dfec4a6e4 spi: atmel: prepa... |
1596 |
clk_disable_unprepare(clk); |
de8cc234a spi: atmel: Fix i... |
1597 |
out_free_irq: |
754ce4f29 [PATCH] SPI: atme... |
1598 |
out_unmap_regs: |
754ce4f29 [PATCH] SPI: atme... |
1599 |
out_free: |
754ce4f29 [PATCH] SPI: atme... |
1600 1601 1602 |
spi_master_put(master); return ret; } |
fd4a319bc spi: Remove HOTPL... |
1603 |
static int atmel_spi_remove(struct platform_device *pdev) |
754ce4f29 [PATCH] SPI: atme... |
1604 1605 1606 |
{ struct spi_master *master = platform_get_drvdata(pdev); struct atmel_spi *as = spi_master_get_devdata(master); |
754ce4f29 [PATCH] SPI: atme... |
1607 |
|
ce0c4caf2 spi/atmel: add su... |
1608 |
pm_runtime_get_sync(&pdev->dev); |
754ce4f29 [PATCH] SPI: atme... |
1609 |
/* reset the hardware and block queue progress */ |
1ccc404a7 spi/spi-atmel: ad... |
1610 |
if (as->use_dma) { |
768f3d9d8 spi: atmel: remov... |
1611 1612 |
atmel_spi_stop_dma(master); atmel_spi_release_dma(master); |
1ccc404a7 spi/spi-atmel: ad... |
1613 |
} |
3cf652bed spi: atmel: fixed... |
1614 |
spin_lock_irq(&as->lock); |
754ce4f29 [PATCH] SPI: atme... |
1615 |
spi_writel(as, CR, SPI_BIT(SWRST)); |
50d7d5bf3 atmel_spi: work-a... |
1616 |
spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ |
754ce4f29 [PATCH] SPI: atme... |
1617 1618 |
spi_readl(as, SR); spin_unlock_irq(&as->lock); |
dfec4a6e4 spi: atmel: prepa... |
1619 |
clk_disable_unprepare(as->clk); |
754ce4f29 [PATCH] SPI: atme... |
1620 |
|
ce0c4caf2 spi/atmel: add su... |
1621 1622 |
pm_runtime_put_noidle(&pdev->dev); pm_runtime_disable(&pdev->dev); |
754ce4f29 [PATCH] SPI: atme... |
1623 1624 |
return 0; } |
ce0c4caf2 spi/atmel: add su... |
1625 |
#ifdef CONFIG_PM |
c1ee8f3fd spi/atmel: improv... |
1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 |
static int atmel_spi_runtime_suspend(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); struct atmel_spi *as = spi_master_get_devdata(master); clk_disable_unprepare(as->clk); pinctrl_pm_select_sleep_state(dev); return 0; } static int atmel_spi_runtime_resume(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); struct atmel_spi *as = spi_master_get_devdata(master); pinctrl_pm_select_default_state(dev); return clk_prepare_enable(as->clk); } |
d630526d0 spi: atmel: remov... |
1646 |
#ifdef CONFIG_PM_SLEEP |
ec60dd37e spi: atmel: conve... |
1647 |
static int atmel_spi_suspend(struct device *dev) |
754ce4f29 [PATCH] SPI: atme... |
1648 |
{ |
c1ee8f3fd spi/atmel: improv... |
1649 |
struct spi_master *master = dev_get_drvdata(dev); |
ba938f3a2 spi: atmel: add m... |
1650 1651 1652 1653 1654 1655 1656 1657 1658 |
int ret; /* Stop the queue running */ ret = spi_master_suspend(master); if (ret) { dev_warn(dev, "cannot suspend master "); return ret; } |
754ce4f29 [PATCH] SPI: atme... |
1659 |
|
c1ee8f3fd spi/atmel: improv... |
1660 1661 |
if (!pm_runtime_suspended(dev)) atmel_spi_runtime_suspend(dev); |
5bdfd491a spi: atmel: adopt... |
1662 |
|
754ce4f29 [PATCH] SPI: atme... |
1663 1664 |
return 0; } |
ec60dd37e spi: atmel: conve... |
1665 |
static int atmel_spi_resume(struct device *dev) |
754ce4f29 [PATCH] SPI: atme... |
1666 |
{ |
c1ee8f3fd spi/atmel: improv... |
1667 |
struct spi_master *master = dev_get_drvdata(dev); |
e53800787 spi: atmel: add d... |
1668 |
struct atmel_spi *as = spi_master_get_devdata(master); |
ba938f3a2 spi: atmel: add m... |
1669 |
int ret; |
754ce4f29 [PATCH] SPI: atme... |
1670 |
|
e53800787 spi: atmel: add d... |
1671 1672 1673 1674 1675 1676 1677 |
ret = clk_prepare_enable(as->clk); if (ret) return ret; atmel_spi_init(as); clk_disable_unprepare(as->clk); |
ce0c4caf2 spi/atmel: add su... |
1678 |
if (!pm_runtime_suspended(dev)) { |
c1ee8f3fd spi/atmel: improv... |
1679 |
ret = atmel_spi_runtime_resume(dev); |
ce0c4caf2 spi/atmel: add su... |
1680 1681 1682 |
if (ret) return ret; } |
ba938f3a2 spi: atmel: add m... |
1683 1684 1685 1686 1687 1688 1689 1690 |
/* Start the queue running */ ret = spi_master_resume(master); if (ret) dev_err(dev, "problem starting queue (%d) ", ret); return ret; |
754ce4f29 [PATCH] SPI: atme... |
1691 |
} |
d630526d0 spi: atmel: remov... |
1692 |
#endif |
ce0c4caf2 spi/atmel: add su... |
1693 1694 1695 1696 1697 1698 |
static const struct dev_pm_ops atmel_spi_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(atmel_spi_suspend, atmel_spi_resume) SET_RUNTIME_PM_OPS(atmel_spi_runtime_suspend, atmel_spi_runtime_resume, NULL) }; |
ec60dd37e spi: atmel: conve... |
1699 |
#define ATMEL_SPI_PM_OPS (&atmel_spi_pm_ops) |
754ce4f29 [PATCH] SPI: atme... |
1700 |
#else |
ec60dd37e spi: atmel: conve... |
1701 |
#define ATMEL_SPI_PM_OPS NULL |
754ce4f29 [PATCH] SPI: atme... |
1702 |
#endif |
850a5b670 spi/atmel: add DT... |
1703 1704 1705 1706 1707 1708 1709 1710 |
#if defined(CONFIG_OF) static const struct of_device_id atmel_spi_dt_ids[] = { { .compatible = "atmel,at91rm9200-spi" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, atmel_spi_dt_ids); #endif |
754ce4f29 [PATCH] SPI: atme... |
1711 1712 1713 1714 |
static struct platform_driver atmel_spi_driver = { .driver = { .name = "atmel_spi", |
ec60dd37e spi: atmel: conve... |
1715 |
.pm = ATMEL_SPI_PM_OPS, |
850a5b670 spi/atmel: add DT... |
1716 |
.of_match_table = of_match_ptr(atmel_spi_dt_ids), |
754ce4f29 [PATCH] SPI: atme... |
1717 |
}, |
1cb201af6 atmel/spi: fix mi... |
1718 |
.probe = atmel_spi_probe, |
2deff8d60 spi: Remove erron... |
1719 |
.remove = atmel_spi_remove, |
754ce4f29 [PATCH] SPI: atme... |
1720 |
}; |
940ab8896 drivercore: Add h... |
1721 |
module_platform_driver(atmel_spi_driver); |
754ce4f29 [PATCH] SPI: atme... |
1722 1723 |
MODULE_DESCRIPTION("Atmel AT32/AT91 SPI Controller driver"); |
e05503ef1 Haavard Skinnemoe... |
1724 |
MODULE_AUTHOR("Haavard Skinnemoen (Atmel)"); |
754ce4f29 [PATCH] SPI: atme... |
1725 |
MODULE_LICENSE("GPL"); |
7e38c3c44 spi: fix platform... |
1726 |
MODULE_ALIAS("platform:atmel_spi"); |