Blame view
drivers/spi/spi-omap2-mcspi.c
32.4 KB
ccdc7bf92 SPI: omap2_mcspi ... |
1 2 3 4 5 |
/* * OMAP2 McSPI controller driver * * Copyright (C) 2005, 2006 Nokia Corporation * Author: Samuel Ortiz <samuel.ortiz@nokia.com> and |
1a5d81905 OMAP: devices: Mo... |
6 |
* Juha Yrj�l� <juha.yrjola@nokia.com> |
ccdc7bf92 SPI: omap2_mcspi ... |
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include <linux/kernel.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/module.h> #include <linux/device.h> #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/platform_device.h> #include <linux/err.h> #include <linux/clk.h> #include <linux/io.h> |
5a0e3ad6a include cleanup: ... |
35 |
#include <linux/slab.h> |
1f1a4384b OMAP: runtime: Mc... |
36 |
#include <linux/pm_runtime.h> |
ccdc7bf92 SPI: omap2_mcspi ... |
37 38 |
#include <linux/spi/spi.h> |
ce491cf85 omap: headers: Mo... |
39 40 |
#include <plat/dma.h> #include <plat/clock.h> |
4743a0f88 spi/omap2_mcspi: ... |
41 |
#include <plat/mcspi.h> |
ccdc7bf92 SPI: omap2_mcspi ... |
42 43 |
#define OMAP2_MCSPI_MAX_FREQ 48000000 |
a41ae1ad9 spi: McSPI off-mo... |
44 45 |
/* OMAP2 has 3 SPI controllers, while OMAP3 has 4 */ #define OMAP2_MCSPI_MAX_CTRL 4 |
ccdc7bf92 SPI: omap2_mcspi ... |
46 |
#define OMAP2_MCSPI_REVISION 0x00 |
ccdc7bf92 SPI: omap2_mcspi ... |
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
#define OMAP2_MCSPI_SYSSTATUS 0x14 #define OMAP2_MCSPI_IRQSTATUS 0x18 #define OMAP2_MCSPI_IRQENABLE 0x1c #define OMAP2_MCSPI_WAKEUPENABLE 0x20 #define OMAP2_MCSPI_SYST 0x24 #define OMAP2_MCSPI_MODULCTRL 0x28 /* per-channel banks, 0x14 bytes each, first is: */ #define OMAP2_MCSPI_CHCONF0 0x2c #define OMAP2_MCSPI_CHSTAT0 0x30 #define OMAP2_MCSPI_CHCTRL0 0x34 #define OMAP2_MCSPI_TX0 0x38 #define OMAP2_MCSPI_RX0 0x3c /* per-register bitmasks: */ |
7a8fa725b spi: omap2_mcspi ... |
62 63 64 |
#define OMAP2_MCSPI_MODULCTRL_SINGLE BIT(0) #define OMAP2_MCSPI_MODULCTRL_MS BIT(2) #define OMAP2_MCSPI_MODULCTRL_STEST BIT(3) |
ccdc7bf92 SPI: omap2_mcspi ... |
65 |
|
7a8fa725b spi: omap2_mcspi ... |
66 67 |
#define OMAP2_MCSPI_CHCONF_PHA BIT(0) #define OMAP2_MCSPI_CHCONF_POL BIT(1) |
ccdc7bf92 SPI: omap2_mcspi ... |
68 |
#define OMAP2_MCSPI_CHCONF_CLKD_MASK (0x0f << 2) |
7a8fa725b spi: omap2_mcspi ... |
69 |
#define OMAP2_MCSPI_CHCONF_EPOL BIT(6) |
ccdc7bf92 SPI: omap2_mcspi ... |
70 |
#define OMAP2_MCSPI_CHCONF_WL_MASK (0x1f << 7) |
7a8fa725b spi: omap2_mcspi ... |
71 72 |
#define OMAP2_MCSPI_CHCONF_TRM_RX_ONLY BIT(12) #define OMAP2_MCSPI_CHCONF_TRM_TX_ONLY BIT(13) |
ccdc7bf92 SPI: omap2_mcspi ... |
73 |
#define OMAP2_MCSPI_CHCONF_TRM_MASK (0x03 << 12) |
7a8fa725b spi: omap2_mcspi ... |
74 75 76 77 78 79 80 |
#define OMAP2_MCSPI_CHCONF_DMAW BIT(14) #define OMAP2_MCSPI_CHCONF_DMAR BIT(15) #define OMAP2_MCSPI_CHCONF_DPE0 BIT(16) #define OMAP2_MCSPI_CHCONF_DPE1 BIT(17) #define OMAP2_MCSPI_CHCONF_IS BIT(18) #define OMAP2_MCSPI_CHCONF_TURBO BIT(19) #define OMAP2_MCSPI_CHCONF_FORCE BIT(20) |
ccdc7bf92 SPI: omap2_mcspi ... |
81 |
|
7a8fa725b spi: omap2_mcspi ... |
82 83 84 |
#define OMAP2_MCSPI_CHSTAT_RXS BIT(0) #define OMAP2_MCSPI_CHSTAT_TXS BIT(1) #define OMAP2_MCSPI_CHSTAT_EOT BIT(2) |
ccdc7bf92 SPI: omap2_mcspi ... |
85 |
|
7a8fa725b spi: omap2_mcspi ... |
86 |
#define OMAP2_MCSPI_CHCTRL_EN BIT(0) |
ccdc7bf92 SPI: omap2_mcspi ... |
87 |
|
7a8fa725b spi: omap2_mcspi ... |
88 |
#define OMAP2_MCSPI_WAKEUPENABLE_WKEN BIT(0) |
ccdc7bf92 SPI: omap2_mcspi ... |
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
/* We have 2 DMA channels per CS, one for RX and one for TX */ struct omap2_mcspi_dma { int dma_tx_channel; int dma_rx_channel; int dma_tx_sync_dev; int dma_rx_sync_dev; struct completion dma_tx_completion; struct completion dma_rx_completion; }; /* use PIO for small transfers, avoiding DMA setup/teardown overhead and * cache operations; better heuristics consider wordsize and bitrate. */ |
8b66c1347 spi/omap2_mcspi: ... |
105 |
#define DMA_MIN_BYTES 160 |
ccdc7bf92 SPI: omap2_mcspi ... |
106 107 108 109 110 111 112 113 |
struct omap2_mcspi { struct work_struct work; /* lock protects queue and registers */ spinlock_t lock; struct list_head msg_queue; struct spi_master *master; |
ccdc7bf92 SPI: omap2_mcspi ... |
114 115 |
/* Virtual base address of the controller */ void __iomem *base; |
e5480b739 [ARM] omap: remov... |
116 |
unsigned long phys; |
ccdc7bf92 SPI: omap2_mcspi ... |
117 118 |
/* SPI1 has 4 channels, while SPI2 has 2 */ struct omap2_mcspi_dma *dma_channels; |
1f1a4384b OMAP: runtime: Mc... |
119 |
struct device *dev; |
2856ac13b spi/omap: Use a w... |
120 |
struct workqueue_struct *wq; |
ccdc7bf92 SPI: omap2_mcspi ... |
121 122 123 124 |
}; struct omap2_mcspi_cs { void __iomem *base; |
e5480b739 [ARM] omap: remov... |
125 |
unsigned long phys; |
ccdc7bf92 SPI: omap2_mcspi ... |
126 |
int word_len; |
89c05372d spi: McSPI saves ... |
127 |
struct list_head node; |
a41ae1ad9 spi: McSPI off-mo... |
128 129 130 131 132 133 134 135 |
/* Context save and restore shadow register */ u32 chconf0; }; /* used for context save and restore, structure members to be updated whenever * corresponding registers are modified. */ struct omap2_mcspi_regs { |
a41ae1ad9 spi: McSPI off-mo... |
136 137 |
u32 modulctrl; u32 wakeupenable; |
89c05372d spi: McSPI saves ... |
138 |
struct list_head cs; |
ccdc7bf92 SPI: omap2_mcspi ... |
139 |
}; |
a41ae1ad9 spi: McSPI off-mo... |
140 |
static struct omap2_mcspi_regs omap2_mcspi_ctx[OMAP2_MCSPI_MAX_CTRL]; |
ccdc7bf92 SPI: omap2_mcspi ... |
141 142 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 |
#define MOD_REG_BIT(val, mask, set) do { \ if (set) \ val |= mask; \ else \ val &= ~mask; \ } while (0) static inline void mcspi_write_reg(struct spi_master *master, int idx, u32 val) { struct omap2_mcspi *mcspi = spi_master_get_devdata(master); __raw_writel(val, mcspi->base + idx); } static inline u32 mcspi_read_reg(struct spi_master *master, int idx) { struct omap2_mcspi *mcspi = spi_master_get_devdata(master); return __raw_readl(mcspi->base + idx); } static inline void mcspi_write_cs_reg(const struct spi_device *spi, int idx, u32 val) { struct omap2_mcspi_cs *cs = spi->controller_state; __raw_writel(val, cs->base + idx); } static inline u32 mcspi_read_cs_reg(const struct spi_device *spi, int idx) { struct omap2_mcspi_cs *cs = spi->controller_state; return __raw_readl(cs->base + idx); } |
a41ae1ad9 spi: McSPI off-mo... |
177 178 179 180 181 182 183 184 185 186 187 188 189 |
static inline u32 mcspi_cached_chconf0(const struct spi_device *spi) { struct omap2_mcspi_cs *cs = spi->controller_state; return cs->chconf0; } static inline void mcspi_write_chconf0(const struct spi_device *spi, u32 val) { struct omap2_mcspi_cs *cs = spi->controller_state; cs->chconf0 = val; mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, val); |
a330ce200 omap2_mcspi: Flus... |
190 |
mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); |
a41ae1ad9 spi: McSPI off-mo... |
191 |
} |
ccdc7bf92 SPI: omap2_mcspi ... |
192 193 194 195 |
static void omap2_mcspi_set_dma_req(const struct spi_device *spi, int is_read, int enable) { u32 l, rw; |
a41ae1ad9 spi: McSPI off-mo... |
196 |
l = mcspi_cached_chconf0(spi); |
ccdc7bf92 SPI: omap2_mcspi ... |
197 198 199 200 201 202 203 |
if (is_read) /* 1 is read, 0 write */ rw = OMAP2_MCSPI_CHCONF_DMAR; else rw = OMAP2_MCSPI_CHCONF_DMAW; MOD_REG_BIT(l, rw, enable); |
a41ae1ad9 spi: McSPI off-mo... |
204 |
mcspi_write_chconf0(spi, l); |
ccdc7bf92 SPI: omap2_mcspi ... |
205 206 207 208 209 210 211 212 |
} static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable) { u32 l; l = enable ? OMAP2_MCSPI_CHCTRL_EN : 0; mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCTRL0, l); |
4743a0f88 spi/omap2_mcspi: ... |
213 214 |
/* Flash post-writes */ mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCTRL0); |
ccdc7bf92 SPI: omap2_mcspi ... |
215 216 217 218 219 |
} static void omap2_mcspi_force_cs(struct spi_device *spi, int cs_active) { u32 l; |
a41ae1ad9 spi: McSPI off-mo... |
220 |
l = mcspi_cached_chconf0(spi); |
ccdc7bf92 SPI: omap2_mcspi ... |
221 |
MOD_REG_BIT(l, OMAP2_MCSPI_CHCONF_FORCE, cs_active); |
a41ae1ad9 spi: McSPI off-mo... |
222 |
mcspi_write_chconf0(spi, l); |
ccdc7bf92 SPI: omap2_mcspi ... |
223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
} static void omap2_mcspi_set_master_mode(struct spi_master *master) { u32 l; /* setup when switching from (reset default) slave mode * to single-channel master mode */ l = mcspi_read_reg(master, OMAP2_MCSPI_MODULCTRL); MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_STEST, 0); MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_MS, 0); MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_SINGLE, 1); mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, l); |
a41ae1ad9 spi: McSPI off-mo... |
237 238 239 240 241 242 243 |
omap2_mcspi_ctx[master->bus_num - 1].modulctrl = l; } static void omap2_mcspi_restore_ctx(struct omap2_mcspi *mcspi) { struct spi_master *spi_cntrl; |
89c05372d spi: McSPI saves ... |
244 |
struct omap2_mcspi_cs *cs; |
a41ae1ad9 spi: McSPI off-mo... |
245 246 247 248 249 |
spi_cntrl = mcspi->master; /* McSPI: context restore */ mcspi_write_reg(spi_cntrl, OMAP2_MCSPI_MODULCTRL, omap2_mcspi_ctx[spi_cntrl->bus_num - 1].modulctrl); |
a41ae1ad9 spi: McSPI off-mo... |
250 251 |
mcspi_write_reg(spi_cntrl, OMAP2_MCSPI_WAKEUPENABLE, omap2_mcspi_ctx[spi_cntrl->bus_num - 1].wakeupenable); |
89c05372d spi: McSPI saves ... |
252 253 254 255 |
list_for_each_entry(cs, &omap2_mcspi_ctx[spi_cntrl->bus_num - 1].cs, node) __raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0); |
a41ae1ad9 spi: McSPI off-mo... |
256 257 258 |
} static void omap2_mcspi_disable_clocks(struct omap2_mcspi *mcspi) { |
1f1a4384b OMAP: runtime: Mc... |
259 |
pm_runtime_put_sync(mcspi->dev); |
a41ae1ad9 spi: McSPI off-mo... |
260 261 262 263 |
} static int omap2_mcspi_enable_clocks(struct omap2_mcspi *mcspi) { |
1f1a4384b OMAP: runtime: Mc... |
264 |
return pm_runtime_get_sync(mcspi->dev); |
ccdc7bf92 SPI: omap2_mcspi ... |
265 |
} |
2764c500b spi/omap2_mcspi: ... |
266 267 268 269 270 271 272 273 274 275 276 277 |
static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit) { unsigned long timeout; timeout = jiffies + msecs_to_jiffies(1000); while (!(__raw_readl(reg) & bit)) { if (time_after(jiffies, timeout)) return -1; cpu_relax(); } return 0; } |
ccdc7bf92 SPI: omap2_mcspi ... |
278 279 280 281 282 283 284 285 286 |
static unsigned omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) { struct omap2_mcspi *mcspi; struct omap2_mcspi_cs *cs = spi->controller_state; struct omap2_mcspi_dma *mcspi_dma; unsigned int count, c; unsigned long base, tx_reg, rx_reg; int word_len, data_type, element_count; |
8b20c8cb8 spi/omap2: fix un... |
287 |
int elements = 0; |
4743a0f88 spi/omap2_mcspi: ... |
288 |
u32 l; |
ccdc7bf92 SPI: omap2_mcspi ... |
289 290 |
u8 * rx; const u8 * tx; |
2764c500b spi/omap2_mcspi: ... |
291 |
void __iomem *chstat_reg; |
ccdc7bf92 SPI: omap2_mcspi ... |
292 293 294 |
mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; |
4743a0f88 spi/omap2_mcspi: ... |
295 |
l = mcspi_cached_chconf0(spi); |
ccdc7bf92 SPI: omap2_mcspi ... |
296 |
|
2764c500b spi/omap2_mcspi: ... |
297 |
chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0; |
ccdc7bf92 SPI: omap2_mcspi ... |
298 299 300 |
count = xfer->len; c = count; word_len = cs->word_len; |
e5480b739 [ARM] omap: remov... |
301 |
base = cs->phys; |
ccdc7bf92 SPI: omap2_mcspi ... |
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
tx_reg = base + OMAP2_MCSPI_TX0; rx_reg = base + OMAP2_MCSPI_RX0; rx = xfer->rx_buf; tx = xfer->tx_buf; if (word_len <= 8) { data_type = OMAP_DMA_DATA_TYPE_S8; element_count = count; } else if (word_len <= 16) { data_type = OMAP_DMA_DATA_TYPE_S16; element_count = count >> 1; } else /* word_len <= 32 */ { data_type = OMAP_DMA_DATA_TYPE_S32; element_count = count >> 2; } if (tx != NULL) { omap_set_dma_transfer_params(mcspi_dma->dma_tx_channel, data_type, element_count, 1, OMAP_DMA_SYNC_ELEMENT, mcspi_dma->dma_tx_sync_dev, 0); omap_set_dma_dest_params(mcspi_dma->dma_tx_channel, 0, OMAP_DMA_AMODE_CONSTANT, tx_reg, 0, 0); omap_set_dma_src_params(mcspi_dma->dma_tx_channel, 0, OMAP_DMA_AMODE_POST_INC, xfer->tx_dma, 0, 0); } if (rx != NULL) { |
4743a0f88 spi/omap2_mcspi: ... |
334 335 336 |
elements = element_count - 1; if (l & OMAP2_MCSPI_CHCONF_TURBO) elements--; |
ccdc7bf92 SPI: omap2_mcspi ... |
337 |
omap_set_dma_transfer_params(mcspi_dma->dma_rx_channel, |
4743a0f88 spi/omap2_mcspi: ... |
338 |
data_type, elements, 1, |
ccdc7bf92 SPI: omap2_mcspi ... |
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 |
OMAP_DMA_SYNC_ELEMENT, mcspi_dma->dma_rx_sync_dev, 1); omap_set_dma_src_params(mcspi_dma->dma_rx_channel, 0, OMAP_DMA_AMODE_CONSTANT, rx_reg, 0, 0); omap_set_dma_dest_params(mcspi_dma->dma_rx_channel, 0, OMAP_DMA_AMODE_POST_INC, xfer->rx_dma, 0, 0); } if (tx != NULL) { omap_start_dma(mcspi_dma->dma_tx_channel); omap2_mcspi_set_dma_req(spi, 0, 1); } if (rx != NULL) { omap_start_dma(mcspi_dma->dma_rx_channel); omap2_mcspi_set_dma_req(spi, 1, 1); } if (tx != NULL) { wait_for_completion(&mcspi_dma->dma_tx_completion); |
07fe03517 spi/omap: Fix DMA... |
363 |
dma_unmap_single(&spi->dev, xfer->tx_dma, count, DMA_TO_DEVICE); |
2764c500b spi/omap2_mcspi: ... |
364 365 366 367 368 369 370 371 372 373 374 375 |
/* for TX_ONLY mode, be sure all words have shifted out */ if (rx == NULL) { if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_TXS) < 0) dev_err(&spi->dev, "TXS timed out "); else if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_EOT) < 0) dev_err(&spi->dev, "EOT timed out "); } |
ccdc7bf92 SPI: omap2_mcspi ... |
376 377 378 379 |
} if (rx != NULL) { wait_for_completion(&mcspi_dma->dma_rx_completion); |
07fe03517 spi/omap: Fix DMA... |
380 |
dma_unmap_single(&spi->dev, xfer->rx_dma, count, DMA_FROM_DEVICE); |
57c5c28db spi: omap2_mcspi ... |
381 |
omap2_mcspi_set_enable(spi, 0); |
4743a0f88 spi/omap2_mcspi: ... |
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 |
if (l & OMAP2_MCSPI_CHCONF_TURBO) { if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0) & OMAP2_MCSPI_CHSTAT_RXS)) { u32 w; w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0); if (word_len <= 8) ((u8 *)xfer->rx_buf)[elements++] = w; else if (word_len <= 16) ((u16 *)xfer->rx_buf)[elements++] = w; else /* word_len <= 32 */ ((u32 *)xfer->rx_buf)[elements++] = w; } else { dev_err(&spi->dev, "DMA RX penultimate word empty"); count -= (word_len <= 8) ? 2 : (word_len <= 16) ? 4 : /* word_len <= 32 */ 8; omap2_mcspi_set_enable(spi, 1); return count; } } |
57c5c28db spi: omap2_mcspi ... |
406 407 408 409 410 411 |
if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0) & OMAP2_MCSPI_CHSTAT_RXS)) { u32 w; w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0); if (word_len <= 8) |
4743a0f88 spi/omap2_mcspi: ... |
412 |
((u8 *)xfer->rx_buf)[elements] = w; |
57c5c28db spi: omap2_mcspi ... |
413 |
else if (word_len <= 16) |
4743a0f88 spi/omap2_mcspi: ... |
414 |
((u16 *)xfer->rx_buf)[elements] = w; |
57c5c28db spi: omap2_mcspi ... |
415 |
else /* word_len <= 32 */ |
4743a0f88 spi/omap2_mcspi: ... |
416 |
((u32 *)xfer->rx_buf)[elements] = w; |
57c5c28db spi: omap2_mcspi ... |
417 418 419 420 421 422 423 |
} else { dev_err(&spi->dev, "DMA RX last word empty"); count -= (word_len <= 8) ? 1 : (word_len <= 16) ? 2 : /* word_len <= 32 */ 4; } omap2_mcspi_set_enable(spi, 1); |
ccdc7bf92 SPI: omap2_mcspi ... |
424 425 426 |
} return count; } |
ccdc7bf92 SPI: omap2_mcspi ... |
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 |
static unsigned omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) { struct omap2_mcspi *mcspi; struct omap2_mcspi_cs *cs = spi->controller_state; unsigned int count, c; u32 l; void __iomem *base = cs->base; void __iomem *tx_reg; void __iomem *rx_reg; void __iomem *chstat_reg; int word_len; mcspi = spi_master_get_devdata(spi->master); count = xfer->len; c = count; word_len = cs->word_len; |
a41ae1ad9 spi: McSPI off-mo... |
444 |
l = mcspi_cached_chconf0(spi); |
ccdc7bf92 SPI: omap2_mcspi ... |
445 446 447 448 449 450 |
/* We store the pre-calculated register addresses on stack to speed * up the transfer loop. */ tx_reg = base + OMAP2_MCSPI_TX0; rx_reg = base + OMAP2_MCSPI_RX0; chstat_reg = base + OMAP2_MCSPI_CHSTAT0; |
adef658dd spi/omap_mcspi: c... |
451 452 |
if (c < (word_len>>3)) return 0; |
ccdc7bf92 SPI: omap2_mcspi ... |
453 454 455 456 457 458 459 460 |
if (word_len <= 8) { u8 *rx; const u8 *tx; rx = xfer->rx_buf; tx = xfer->tx_buf; do { |
feed9bab7 spi: omap2_mcspi ... |
461 |
c -= 1; |
ccdc7bf92 SPI: omap2_mcspi ... |
462 463 464 465 466 467 468 |
if (tx != NULL) { if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_TXS) < 0) { dev_err(&spi->dev, "TXS timed out "); goto out; } |
079a176d8 spi: omap2_mcspi:... |
469 470 |
dev_vdbg(&spi->dev, "write-%d %02x ", |
ccdc7bf92 SPI: omap2_mcspi ... |
471 |
word_len, *tx); |
ccdc7bf92 SPI: omap2_mcspi ... |
472 473 474 475 476 477 478 479 480 |
__raw_writel(*tx++, tx_reg); } if (rx != NULL) { if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_RXS) < 0) { dev_err(&spi->dev, "RXS timed out "); goto out; } |
4743a0f88 spi/omap2_mcspi: ... |
481 482 483 484 485 |
if (c == 1 && tx == NULL && (l & OMAP2_MCSPI_CHCONF_TURBO)) { omap2_mcspi_set_enable(spi, 0); *rx++ = __raw_readl(rx_reg); |
079a176d8 spi: omap2_mcspi:... |
486 487 |
dev_vdbg(&spi->dev, "read-%d %02x ", |
4743a0f88 spi/omap2_mcspi: ... |
488 |
word_len, *(rx - 1)); |
4743a0f88 spi/omap2_mcspi: ... |
489 490 491 492 493 494 495 496 497 498 499 |
if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_RXS) < 0) { dev_err(&spi->dev, "RXS timed out "); goto out; } c = 0; } else if (c == 0 && tx == NULL) { omap2_mcspi_set_enable(spi, 0); } |
ccdc7bf92 SPI: omap2_mcspi ... |
500 |
*rx++ = __raw_readl(rx_reg); |
079a176d8 spi: omap2_mcspi:... |
501 502 |
dev_vdbg(&spi->dev, "read-%d %02x ", |
ccdc7bf92 SPI: omap2_mcspi ... |
503 |
word_len, *(rx - 1)); |
ccdc7bf92 SPI: omap2_mcspi ... |
504 |
} |
95c5c3ab7 spi/omap_mcspi: F... |
505 |
} while (c); |
ccdc7bf92 SPI: omap2_mcspi ... |
506 507 508 509 510 511 512 |
} else if (word_len <= 16) { u16 *rx; const u16 *tx; rx = xfer->rx_buf; tx = xfer->tx_buf; do { |
feed9bab7 spi: omap2_mcspi ... |
513 |
c -= 2; |
ccdc7bf92 SPI: omap2_mcspi ... |
514 515 516 517 518 519 520 |
if (tx != NULL) { if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_TXS) < 0) { dev_err(&spi->dev, "TXS timed out "); goto out; } |
079a176d8 spi: omap2_mcspi:... |
521 522 |
dev_vdbg(&spi->dev, "write-%d %04x ", |
ccdc7bf92 SPI: omap2_mcspi ... |
523 |
word_len, *tx); |
ccdc7bf92 SPI: omap2_mcspi ... |
524 525 526 527 528 529 530 531 532 |
__raw_writel(*tx++, tx_reg); } if (rx != NULL) { if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_RXS) < 0) { dev_err(&spi->dev, "RXS timed out "); goto out; } |
4743a0f88 spi/omap2_mcspi: ... |
533 534 535 536 537 |
if (c == 2 && tx == NULL && (l & OMAP2_MCSPI_CHCONF_TURBO)) { omap2_mcspi_set_enable(spi, 0); *rx++ = __raw_readl(rx_reg); |
079a176d8 spi: omap2_mcspi:... |
538 539 |
dev_vdbg(&spi->dev, "read-%d %04x ", |
4743a0f88 spi/omap2_mcspi: ... |
540 |
word_len, *(rx - 1)); |
4743a0f88 spi/omap2_mcspi: ... |
541 542 543 544 545 546 547 548 549 550 551 |
if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_RXS) < 0) { dev_err(&spi->dev, "RXS timed out "); goto out; } c = 0; } else if (c == 0 && tx == NULL) { omap2_mcspi_set_enable(spi, 0); } |
ccdc7bf92 SPI: omap2_mcspi ... |
552 |
*rx++ = __raw_readl(rx_reg); |
079a176d8 spi: omap2_mcspi:... |
553 554 |
dev_vdbg(&spi->dev, "read-%d %04x ", |
ccdc7bf92 SPI: omap2_mcspi ... |
555 |
word_len, *(rx - 1)); |
ccdc7bf92 SPI: omap2_mcspi ... |
556 |
} |
95c5c3ab7 spi/omap_mcspi: F... |
557 |
} while (c >= 2); |
ccdc7bf92 SPI: omap2_mcspi ... |
558 559 560 561 562 563 564 |
} else if (word_len <= 32) { u32 *rx; const u32 *tx; rx = xfer->rx_buf; tx = xfer->tx_buf; do { |
feed9bab7 spi: omap2_mcspi ... |
565 |
c -= 4; |
ccdc7bf92 SPI: omap2_mcspi ... |
566 567 568 569 570 571 572 |
if (tx != NULL) { if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_TXS) < 0) { dev_err(&spi->dev, "TXS timed out "); goto out; } |
079a176d8 spi: omap2_mcspi:... |
573 574 |
dev_vdbg(&spi->dev, "write-%d %08x ", |
ccdc7bf92 SPI: omap2_mcspi ... |
575 |
word_len, *tx); |
ccdc7bf92 SPI: omap2_mcspi ... |
576 577 578 579 580 581 582 583 584 |
__raw_writel(*tx++, tx_reg); } if (rx != NULL) { if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_RXS) < 0) { dev_err(&spi->dev, "RXS timed out "); goto out; } |
4743a0f88 spi/omap2_mcspi: ... |
585 586 587 588 589 |
if (c == 4 && tx == NULL && (l & OMAP2_MCSPI_CHCONF_TURBO)) { omap2_mcspi_set_enable(spi, 0); *rx++ = __raw_readl(rx_reg); |
079a176d8 spi: omap2_mcspi:... |
590 591 |
dev_vdbg(&spi->dev, "read-%d %08x ", |
4743a0f88 spi/omap2_mcspi: ... |
592 |
word_len, *(rx - 1)); |
4743a0f88 spi/omap2_mcspi: ... |
593 594 595 596 597 598 599 600 601 602 603 |
if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_RXS) < 0) { dev_err(&spi->dev, "RXS timed out "); goto out; } c = 0; } else if (c == 0 && tx == NULL) { omap2_mcspi_set_enable(spi, 0); } |
ccdc7bf92 SPI: omap2_mcspi ... |
604 |
*rx++ = __raw_readl(rx_reg); |
079a176d8 spi: omap2_mcspi:... |
605 606 |
dev_vdbg(&spi->dev, "read-%d %08x ", |
ccdc7bf92 SPI: omap2_mcspi ... |
607 |
word_len, *(rx - 1)); |
ccdc7bf92 SPI: omap2_mcspi ... |
608 |
} |
95c5c3ab7 spi/omap_mcspi: F... |
609 |
} while (c >= 4); |
ccdc7bf92 SPI: omap2_mcspi ... |
610 611 612 613 614 615 616 617 618 619 620 621 |
} /* for TX_ONLY mode, be sure all words have shifted out */ if (xfer->rx_buf == NULL) { if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_TXS) < 0) { dev_err(&spi->dev, "TXS timed out "); } else if (mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_EOT) < 0) dev_err(&spi->dev, "EOT timed out "); |
e1993ed64 spi/omap2_mcspi: ... |
622 623 624 625 626 627 |
/* disable chan to purge rx datas received in TX_ONLY transfer, * otherwise these rx datas will affect the direct following * RX_ONLY transfer. */ omap2_mcspi_set_enable(spi, 0); |
ccdc7bf92 SPI: omap2_mcspi ... |
628 629 |
} out: |
4743a0f88 spi/omap2_mcspi: ... |
630 |
omap2_mcspi_set_enable(spi, 1); |
ccdc7bf92 SPI: omap2_mcspi ... |
631 632 |
return count - c; } |
57d9c10dd spi/omap_mcspi: O... |
633 634 635 636 637 638 639 640 641 642 |
static u32 omap2_mcspi_calc_divisor(u32 speed_hz) { u32 div; for (div = 0; div < 15; div++) if (speed_hz >= (OMAP2_MCSPI_MAX_FREQ >> div)) return div; return 15; } |
ccdc7bf92 SPI: omap2_mcspi ... |
643 644 645 646 647 648 |
/* called only when no transfer is active to this device */ static int omap2_mcspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) { struct omap2_mcspi_cs *cs = spi->controller_state; struct omap2_mcspi *mcspi; |
a41ae1ad9 spi: McSPI off-mo... |
649 |
struct spi_master *spi_cntrl; |
ccdc7bf92 SPI: omap2_mcspi ... |
650 651 |
u32 l = 0, div = 0; u8 word_len = spi->bits_per_word; |
9bd4517dd spi/omap2_mcspi: ... |
652 |
u32 speed_hz = spi->max_speed_hz; |
ccdc7bf92 SPI: omap2_mcspi ... |
653 654 |
mcspi = spi_master_get_devdata(spi->master); |
a41ae1ad9 spi: McSPI off-mo... |
655 |
spi_cntrl = mcspi->master; |
ccdc7bf92 SPI: omap2_mcspi ... |
656 657 658 659 660 |
if (t != NULL && t->bits_per_word) word_len = t->bits_per_word; cs->word_len = word_len; |
9bd4517dd spi/omap2_mcspi: ... |
661 662 |
if (t && t->speed_hz) speed_hz = t->speed_hz; |
57d9c10dd spi/omap_mcspi: O... |
663 664 |
speed_hz = min_t(u32, speed_hz, OMAP2_MCSPI_MAX_FREQ); div = omap2_mcspi_calc_divisor(speed_hz); |
ccdc7bf92 SPI: omap2_mcspi ... |
665 |
|
a41ae1ad9 spi: McSPI off-mo... |
666 |
l = mcspi_cached_chconf0(spi); |
ccdc7bf92 SPI: omap2_mcspi ... |
667 668 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 |
/* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS * REVISIT: this controller could support SPI_3WIRE mode. */ l &= ~(OMAP2_MCSPI_CHCONF_IS|OMAP2_MCSPI_CHCONF_DPE1); l |= OMAP2_MCSPI_CHCONF_DPE0; /* wordlength */ l &= ~OMAP2_MCSPI_CHCONF_WL_MASK; l |= (word_len - 1) << 7; /* set chipselect polarity; manage with FORCE */ if (!(spi->mode & SPI_CS_HIGH)) l |= OMAP2_MCSPI_CHCONF_EPOL; /* active-low; normal */ else l &= ~OMAP2_MCSPI_CHCONF_EPOL; /* set clock divisor */ l &= ~OMAP2_MCSPI_CHCONF_CLKD_MASK; l |= div << 2; /* set SPI mode 0..3 */ if (spi->mode & SPI_CPOL) l |= OMAP2_MCSPI_CHCONF_POL; else l &= ~OMAP2_MCSPI_CHCONF_POL; if (spi->mode & SPI_CPHA) l |= OMAP2_MCSPI_CHCONF_PHA; else l &= ~OMAP2_MCSPI_CHCONF_PHA; |
a41ae1ad9 spi: McSPI off-mo... |
697 |
mcspi_write_chconf0(spi, l); |
ccdc7bf92 SPI: omap2_mcspi ... |
698 699 700 |
dev_dbg(&spi->dev, "setup: speed %d, sample %s edge, clk %s ", |
57d9c10dd spi/omap_mcspi: O... |
701 |
OMAP2_MCSPI_MAX_FREQ >> div, |
ccdc7bf92 SPI: omap2_mcspi ... |
702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 |
(spi->mode & SPI_CPHA) ? "trailing" : "leading", (spi->mode & SPI_CPOL) ? "inverted" : "normal"); return 0; } static void omap2_mcspi_dma_rx_callback(int lch, u16 ch_status, void *data) { struct spi_device *spi = data; struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *mcspi_dma; mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &(mcspi->dma_channels[spi->chip_select]); complete(&mcspi_dma->dma_rx_completion); /* We must disable the DMA RX request */ omap2_mcspi_set_dma_req(spi, 1, 0); } static void omap2_mcspi_dma_tx_callback(int lch, u16 ch_status, void *data) { struct spi_device *spi = data; struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *mcspi_dma; mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &(mcspi->dma_channels[spi->chip_select]); complete(&mcspi_dma->dma_tx_completion); /* We must disable the DMA TX request */ omap2_mcspi_set_dma_req(spi, 0, 0); } static int omap2_mcspi_request_dma(struct spi_device *spi) { struct spi_master *master = spi->master; struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *mcspi_dma; mcspi = spi_master_get_devdata(master); mcspi_dma = mcspi->dma_channels + spi->chip_select; if (omap_request_dma(mcspi_dma->dma_rx_sync_dev, "McSPI RX", omap2_mcspi_dma_rx_callback, spi, &mcspi_dma->dma_rx_channel)) { dev_err(&spi->dev, "no RX DMA channel for McSPI "); return -EAGAIN; } if (omap_request_dma(mcspi_dma->dma_tx_sync_dev, "McSPI TX", omap2_mcspi_dma_tx_callback, spi, &mcspi_dma->dma_tx_channel)) { omap_free_dma(mcspi_dma->dma_rx_channel); mcspi_dma->dma_rx_channel = -1; dev_err(&spi->dev, "no TX DMA channel for McSPI "); return -EAGAIN; } init_completion(&mcspi_dma->dma_rx_completion); init_completion(&mcspi_dma->dma_tx_completion); return 0; } |
ccdc7bf92 SPI: omap2_mcspi ... |
770 771 772 773 774 775 |
static int omap2_mcspi_setup(struct spi_device *spi) { int ret; struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *mcspi_dma; struct omap2_mcspi_cs *cs = spi->controller_state; |
7d0771970 spi: move common ... |
776 |
if (spi->bits_per_word < 4 || spi->bits_per_word > 32) { |
ccdc7bf92 SPI: omap2_mcspi ... |
777 778 779 780 781 782 783 784 785 786 787 788 789 790 |
dev_dbg(&spi->dev, "setup: unsupported %d bit words ", spi->bits_per_word); return -EINVAL; } mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; if (!cs) { cs = kzalloc(sizeof *cs, GFP_KERNEL); if (!cs) return -ENOMEM; cs->base = mcspi->base + spi->chip_select * 0x14; |
e5480b739 [ARM] omap: remov... |
791 |
cs->phys = mcspi->phys + spi->chip_select * 0x14; |
a41ae1ad9 spi: McSPI off-mo... |
792 |
cs->chconf0 = 0; |
ccdc7bf92 SPI: omap2_mcspi ... |
793 |
spi->controller_state = cs; |
89c05372d spi: McSPI saves ... |
794 795 796 |
/* Link this to context save list */ list_add_tail(&cs->node, &omap2_mcspi_ctx[mcspi->master->bus_num - 1].cs); |
ccdc7bf92 SPI: omap2_mcspi ... |
797 798 799 800 801 802 803 804 |
} if (mcspi_dma->dma_rx_channel == -1 || mcspi_dma->dma_tx_channel == -1) { ret = omap2_mcspi_request_dma(spi); if (ret < 0) return ret; } |
1f1a4384b OMAP: runtime: Mc... |
805 806 807 |
ret = omap2_mcspi_enable_clocks(mcspi); if (ret < 0) return ret; |
a41ae1ad9 spi: McSPI off-mo... |
808 |
|
86eeb6fe7 OMAP2 McSPI code ... |
809 |
ret = omap2_mcspi_setup_transfer(spi, NULL); |
a41ae1ad9 spi: McSPI off-mo... |
810 |
omap2_mcspi_disable_clocks(mcspi); |
ccdc7bf92 SPI: omap2_mcspi ... |
811 812 813 814 815 816 817 818 |
return ret; } static void omap2_mcspi_cleanup(struct spi_device *spi) { struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *mcspi_dma; |
89c05372d spi: McSPI saves ... |
819 |
struct omap2_mcspi_cs *cs; |
ccdc7bf92 SPI: omap2_mcspi ... |
820 821 |
mcspi = spi_master_get_devdata(spi->master); |
ccdc7bf92 SPI: omap2_mcspi ... |
822 |
|
5e7749436 spi/omap2_mcspi: ... |
823 824 825 826 |
if (spi->controller_state) { /* Unlink controller state from context save list */ cs = spi->controller_state; list_del(&cs->node); |
89c05372d spi: McSPI saves ... |
827 |
|
5e7749436 spi/omap2_mcspi: ... |
828 829 |
kfree(spi->controller_state); } |
ccdc7bf92 SPI: omap2_mcspi ... |
830 |
|
99f1a43f4 spi/omap2_mcspi: ... |
831 832 833 834 835 836 837 838 839 840 841 |
if (spi->chip_select < spi->master->num_chipselect) { mcspi_dma = &mcspi->dma_channels[spi->chip_select]; if (mcspi_dma->dma_rx_channel != -1) { omap_free_dma(mcspi_dma->dma_rx_channel); mcspi_dma->dma_rx_channel = -1; } if (mcspi_dma->dma_tx_channel != -1) { omap_free_dma(mcspi_dma->dma_tx_channel); mcspi_dma->dma_tx_channel = -1; } |
ccdc7bf92 SPI: omap2_mcspi ... |
842 843 844 845 846 847 848 849 |
} } static void omap2_mcspi_work(struct work_struct *work) { struct omap2_mcspi *mcspi; mcspi = container_of(work, struct omap2_mcspi, work); |
ccdc7bf92 SPI: omap2_mcspi ... |
850 |
|
1f1a4384b OMAP: runtime: Mc... |
851 852 853 854 |
if (omap2_mcspi_enable_clocks(mcspi) < 0) return; spin_lock_irq(&mcspi->lock); |
ccdc7bf92 SPI: omap2_mcspi ... |
855 856 857 858 859 860 861 862 863 864 865 866 |
/* We only enable one channel at a time -- the one whose message is * at the head of the queue -- although this controller would gladly * arbitrate among multiple channels. This corresponds to "single * channel" master mode. As a side effect, we need to manage the * chipselect with the FORCE bit ... CS != channel enable. */ while (!list_empty(&mcspi->msg_queue)) { struct spi_message *m; struct spi_device *spi; struct spi_transfer *t = NULL; int cs_active = 0; |
ccdc7bf92 SPI: omap2_mcspi ... |
867 |
struct omap2_mcspi_cs *cs; |
4743a0f88 spi/omap2_mcspi: ... |
868 |
struct omap2_mcspi_device_config *cd; |
ccdc7bf92 SPI: omap2_mcspi ... |
869 870 871 872 873 874 875 876 877 878 879 |
int par_override = 0; int status = 0; u32 chconf; m = container_of(mcspi->msg_queue.next, struct spi_message, queue); list_del_init(&m->queue); spin_unlock_irq(&mcspi->lock); spi = m->spi; |
ccdc7bf92 SPI: omap2_mcspi ... |
880 |
cs = spi->controller_state; |
4743a0f88 spi/omap2_mcspi: ... |
881 |
cd = spi->controller_data; |
ccdc7bf92 SPI: omap2_mcspi ... |
882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 |
omap2_mcspi_set_enable(spi, 1); list_for_each_entry(t, &m->transfers, transfer_list) { if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { status = -EINVAL; break; } if (par_override || t->speed_hz || t->bits_per_word) { par_override = 1; status = omap2_mcspi_setup_transfer(spi, t); if (status < 0) break; if (!t->speed_hz && !t->bits_per_word) par_override = 0; } if (!cs_active) { omap2_mcspi_force_cs(spi, 1); cs_active = 1; } |
a41ae1ad9 spi: McSPI off-mo... |
902 |
chconf = mcspi_cached_chconf0(spi); |
ccdc7bf92 SPI: omap2_mcspi ... |
903 |
chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; |
4743a0f88 spi/omap2_mcspi: ... |
904 |
chconf &= ~OMAP2_MCSPI_CHCONF_TURBO; |
ccdc7bf92 SPI: omap2_mcspi ... |
905 906 907 908 |
if (t->tx_buf == NULL) chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY; else if (t->rx_buf == NULL) chconf |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY; |
4743a0f88 spi/omap2_mcspi: ... |
909 910 911 912 913 914 |
if (cd && cd->turbo_mode && t->tx_buf == NULL) { /* Turbo mode is for more than one word */ if (t->len > ((cs->word_len + 7) >> 3)) chconf |= OMAP2_MCSPI_CHCONF_TURBO; } |
a41ae1ad9 spi: McSPI off-mo... |
915 |
mcspi_write_chconf0(spi, chconf); |
ccdc7bf92 SPI: omap2_mcspi ... |
916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 |
if (t->len) { unsigned count; /* RX_ONLY mode needs dummy data in TX reg */ if (t->tx_buf == NULL) __raw_writel(0, cs->base + OMAP2_MCSPI_TX0); if (m->is_dma_mapped || t->len >= DMA_MIN_BYTES) count = omap2_mcspi_txrx_dma(spi, t); else count = omap2_mcspi_txrx_pio(spi, t); m->actual_length += count; if (count != t->len) { status = -EIO; break; } } if (t->delay_usecs) udelay(t->delay_usecs); /* ignore the "leave it on after last xfer" hint */ if (t->cs_change) { omap2_mcspi_force_cs(spi, 0); cs_active = 0; } } /* Restore defaults if they were overriden */ if (par_override) { par_override = 0; status = omap2_mcspi_setup_transfer(spi, NULL); } if (cs_active) omap2_mcspi_force_cs(spi, 0); omap2_mcspi_set_enable(spi, 0); m->status = status; m->complete(m->context); spin_lock_irq(&mcspi->lock); } |
ccdc7bf92 SPI: omap2_mcspi ... |
963 |
spin_unlock_irq(&mcspi->lock); |
1f1a4384b OMAP: runtime: Mc... |
964 965 |
omap2_mcspi_disable_clocks(mcspi); |
ccdc7bf92 SPI: omap2_mcspi ... |
966 967 968 969 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 997 998 |
} static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m) { struct omap2_mcspi *mcspi; unsigned long flags; struct spi_transfer *t; m->actual_length = 0; m->status = 0; /* reject invalid messages and transfers */ if (list_empty(&m->transfers) || !m->complete) return -EINVAL; list_for_each_entry(t, &m->transfers, transfer_list) { const void *tx_buf = t->tx_buf; void *rx_buf = t->rx_buf; unsigned len = t->len; if (t->speed_hz > OMAP2_MCSPI_MAX_FREQ || (len && !(rx_buf || tx_buf)) || (t->bits_per_word && ( t->bits_per_word < 4 || t->bits_per_word > 32))) { dev_dbg(&spi->dev, "transfer: %d Hz, %d %s%s, %d bpw ", t->speed_hz, len, tx_buf ? "tx" : "", rx_buf ? "rx" : "", t->bits_per_word); return -EINVAL; } |
57d9c10dd spi/omap_mcspi: O... |
999 1000 1001 1002 1003 |
if (t->speed_hz && t->speed_hz < (OMAP2_MCSPI_MAX_FREQ >> 15)) { dev_dbg(&spi->dev, "speed_hz %d below minimum %d Hz ", t->speed_hz, OMAP2_MCSPI_MAX_FREQ >> 15); |
ccdc7bf92 SPI: omap2_mcspi ... |
1004 1005 1006 1007 1008 |
return -EINVAL; } if (m->is_dma_mapped || len < DMA_MIN_BYTES) continue; |
ccdc7bf92 SPI: omap2_mcspi ... |
1009 1010 1011 |
if (tx_buf != NULL) { t->tx_dma = dma_map_single(&spi->dev, (void *) tx_buf, len, DMA_TO_DEVICE); |
8d8bb39b9 dma-mapping: add ... |
1012 |
if (dma_mapping_error(&spi->dev, t->tx_dma)) { |
ccdc7bf92 SPI: omap2_mcspi ... |
1013 1014 1015 1016 1017 1018 1019 1020 1021 |
dev_dbg(&spi->dev, "dma %cX %d bytes error ", 'T', len); return -EINVAL; } } if (rx_buf != NULL) { t->rx_dma = dma_map_single(&spi->dev, rx_buf, t->len, DMA_FROM_DEVICE); |
8d8bb39b9 dma-mapping: add ... |
1022 |
if (dma_mapping_error(&spi->dev, t->rx_dma)) { |
ccdc7bf92 SPI: omap2_mcspi ... |
1023 1024 1025 1026 |
dev_dbg(&spi->dev, "dma %cX %d bytes error ", 'R', len); if (tx_buf != NULL) |
07fe03517 spi/omap: Fix DMA... |
1027 |
dma_unmap_single(&spi->dev, t->tx_dma, |
ccdc7bf92 SPI: omap2_mcspi ... |
1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 |
len, DMA_TO_DEVICE); return -EINVAL; } } } mcspi = spi_master_get_devdata(spi->master); spin_lock_irqsave(&mcspi->lock, flags); list_add_tail(&m->queue, &mcspi->msg_queue); |
2856ac13b spi/omap: Use a w... |
1038 |
queue_work(mcspi->wq, &mcspi->work); |
ccdc7bf92 SPI: omap2_mcspi ... |
1039 1040 1041 1042 |
spin_unlock_irqrestore(&mcspi->lock, flags); return 0; } |
1f1a4384b OMAP: runtime: Mc... |
1043 |
static int __init omap2_mcspi_master_setup(struct omap2_mcspi *mcspi) |
ccdc7bf92 SPI: omap2_mcspi ... |
1044 1045 1046 |
{ struct spi_master *master = mcspi->master; u32 tmp; |
1f1a4384b OMAP: runtime: Mc... |
1047 |
int ret = 0; |
ccdc7bf92 SPI: omap2_mcspi ... |
1048 |
|
1f1a4384b OMAP: runtime: Mc... |
1049 1050 1051 |
ret = omap2_mcspi_enable_clocks(mcspi); if (ret < 0) return ret; |
ddb22195c spi: omap2_mcspi ... |
1052 |
|
a41ae1ad9 spi: McSPI off-mo... |
1053 1054 1055 |
tmp = OMAP2_MCSPI_WAKEUPENABLE_WKEN; mcspi_write_reg(master, OMAP2_MCSPI_WAKEUPENABLE, tmp); omap2_mcspi_ctx[master->bus_num - 1].wakeupenable = tmp; |
ccdc7bf92 SPI: omap2_mcspi ... |
1056 1057 |
omap2_mcspi_set_master_mode(master); |
a41ae1ad9 spi: McSPI off-mo... |
1058 |
omap2_mcspi_disable_clocks(mcspi); |
ccdc7bf92 SPI: omap2_mcspi ... |
1059 1060 |
return 0; } |
1f1a4384b OMAP: runtime: Mc... |
1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 |
static int omap_mcspi_runtime_resume(struct device *dev) { struct omap2_mcspi *mcspi; struct spi_master *master; master = dev_get_drvdata(dev); mcspi = spi_master_get_devdata(master); omap2_mcspi_restore_ctx(mcspi); return 0; } |
ccc7baed1 spi: omap2_mcspi ... |
1072 |
|
ccdc7bf92 SPI: omap2_mcspi ... |
1073 1074 1075 |
static int __init omap2_mcspi_probe(struct platform_device *pdev) { struct spi_master *master; |
1a5d81905 OMAP: devices: Mo... |
1076 |
struct omap2_mcspi_platform_config *pdata = pdev->dev.platform_data; |
ccdc7bf92 SPI: omap2_mcspi ... |
1077 1078 1079 |
struct omap2_mcspi *mcspi; struct resource *r; int status = 0, i; |
2856ac13b spi/omap: Use a w... |
1080 |
char wq_name[20]; |
ccdc7bf92 SPI: omap2_mcspi ... |
1081 1082 1083 1084 1085 1086 1087 |
master = spi_alloc_master(&pdev->dev, sizeof *mcspi); if (master == NULL) { dev_dbg(&pdev->dev, "master allocation failed "); return -ENOMEM; } |
e7db06b5d spi: move more sp... |
1088 1089 |
/* the spi->mode bits understood by this driver: */ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; |
ccdc7bf92 SPI: omap2_mcspi ... |
1090 1091 1092 1093 1094 1095 |
if (pdev->id != -1) master->bus_num = pdev->id; master->setup = omap2_mcspi_setup; master->transfer = omap2_mcspi_transfer; master->cleanup = omap2_mcspi_cleanup; |
1a5d81905 OMAP: devices: Mo... |
1096 |
master->num_chipselect = pdata->num_cs; |
ccdc7bf92 SPI: omap2_mcspi ... |
1097 1098 1099 1100 1101 |
dev_set_drvdata(&pdev->dev, master); mcspi = spi_master_get_devdata(master); mcspi->master = master; |
2856ac13b spi/omap: Use a w... |
1102 1103 1104 1105 |
sprintf(wq_name, "omap2_mcspi/%d", master->bus_num); mcspi->wq = alloc_workqueue(wq_name, WQ_MEM_RECLAIM, 1); if (mcspi->wq == NULL) { status = -ENOMEM; |
39f1b5659 spi/omap: Correct... |
1106 |
goto free_master; |
2856ac13b spi/omap: Use a w... |
1107 |
} |
ccdc7bf92 SPI: omap2_mcspi ... |
1108 1109 1110 |
r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) { status = -ENODEV; |
39f1b5659 spi/omap: Correct... |
1111 |
goto free_master; |
ccdc7bf92 SPI: omap2_mcspi ... |
1112 |
} |
1458d160d OMAP: SPI: Fix th... |
1113 1114 1115 1116 |
r->start += pdata->regs_offset; r->end += pdata->regs_offset; mcspi->phys = r->start; |
8e2943c04 spi: Convert uses... |
1117 1118 |
if (!request_mem_region(r->start, resource_size(r), dev_name(&pdev->dev))) { |
ccdc7bf92 SPI: omap2_mcspi ... |
1119 |
status = -EBUSY; |
39f1b5659 spi/omap: Correct... |
1120 |
goto free_master; |
ccdc7bf92 SPI: omap2_mcspi ... |
1121 |
} |
8e2943c04 spi: Convert uses... |
1122 |
mcspi->base = ioremap(r->start, resource_size(r)); |
55c381e48 [ARM] omap: conve... |
1123 1124 1125 1126 |
if (!mcspi->base) { dev_dbg(&pdev->dev, "can't ioremap MCSPI "); status = -ENOMEM; |
39f1b5659 spi/omap: Correct... |
1127 |
goto release_region; |
55c381e48 [ARM] omap: conve... |
1128 |
} |
ccdc7bf92 SPI: omap2_mcspi ... |
1129 |
|
1f1a4384b OMAP: runtime: Mc... |
1130 |
mcspi->dev = &pdev->dev; |
ccdc7bf92 SPI: omap2_mcspi ... |
1131 1132 1133 1134 |
INIT_WORK(&mcspi->work, omap2_mcspi_work); spin_lock_init(&mcspi->lock); INIT_LIST_HEAD(&mcspi->msg_queue); |
89c05372d spi: McSPI saves ... |
1135 |
INIT_LIST_HEAD(&omap2_mcspi_ctx[master->bus_num - 1].cs); |
ccdc7bf92 SPI: omap2_mcspi ... |
1136 |
|
ccdc7bf92 SPI: omap2_mcspi ... |
1137 1138 1139 1140 1141 |
mcspi->dma_channels = kcalloc(master->num_chipselect, sizeof(struct omap2_mcspi_dma), GFP_KERNEL); if (mcspi->dma_channels == NULL) |
39f1b5659 spi/omap: Correct... |
1142 |
goto unmap_io; |
ccdc7bf92 SPI: omap2_mcspi ... |
1143 |
|
1a5d81905 OMAP: devices: Mo... |
1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 |
for (i = 0; i < master->num_chipselect; i++) { char dma_ch_name[14]; struct resource *dma_res; sprintf(dma_ch_name, "rx%d", i); dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA, dma_ch_name); if (!dma_res) { dev_dbg(&pdev->dev, "cannot get DMA RX channel "); status = -ENODEV; break; } |
ccdc7bf92 SPI: omap2_mcspi ... |
1157 |
mcspi->dma_channels[i].dma_rx_channel = -1; |
1a5d81905 OMAP: devices: Mo... |
1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 |
mcspi->dma_channels[i].dma_rx_sync_dev = dma_res->start; sprintf(dma_ch_name, "tx%d", i); dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA, dma_ch_name); if (!dma_res) { dev_dbg(&pdev->dev, "cannot get DMA TX channel "); status = -ENODEV; break; } |
ccdc7bf92 SPI: omap2_mcspi ... |
1168 |
mcspi->dma_channels[i].dma_tx_channel = -1; |
1a5d81905 OMAP: devices: Mo... |
1169 |
mcspi->dma_channels[i].dma_tx_sync_dev = dma_res->start; |
ccdc7bf92 SPI: omap2_mcspi ... |
1170 |
} |
39f1b5659 spi/omap: Correct... |
1171 1172 |
if (status < 0) goto dma_chnl_free; |
1f1a4384b OMAP: runtime: Mc... |
1173 1174 1175 |
pm_runtime_enable(&pdev->dev); if (status || omap2_mcspi_master_setup(mcspi) < 0) |
39f1b5659 spi/omap: Correct... |
1176 |
goto disable_pm; |
ccdc7bf92 SPI: omap2_mcspi ... |
1177 1178 1179 |
status = spi_register_master(master); if (status < 0) |
39f1b5659 spi/omap: Correct... |
1180 |
goto err_spi_register; |
ccdc7bf92 SPI: omap2_mcspi ... |
1181 1182 |
return status; |
39f1b5659 spi/omap: Correct... |
1183 |
err_spi_register: |
1f1a4384b OMAP: runtime: Mc... |
1184 |
spi_master_put(master); |
39f1b5659 spi/omap: Correct... |
1185 |
disable_pm: |
751c925cb spi/omap: call pm... |
1186 |
pm_runtime_disable(&pdev->dev); |
39f1b5659 spi/omap: Correct... |
1187 |
dma_chnl_free: |
1f1a4384b OMAP: runtime: Mc... |
1188 |
kfree(mcspi->dma_channels); |
39f1b5659 spi/omap: Correct... |
1189 |
unmap_io: |
1f1a4384b OMAP: runtime: Mc... |
1190 |
iounmap(mcspi->base); |
39f1b5659 spi/omap: Correct... |
1191 1192 1193 1194 1195 |
release_region: release_mem_region(r->start, resource_size(r)); free_master: kfree(master); platform_set_drvdata(pdev, NULL); |
ccdc7bf92 SPI: omap2_mcspi ... |
1196 1197 1198 1199 1200 1201 1202 1203 1204 |
return status; } static int __exit omap2_mcspi_remove(struct platform_device *pdev) { struct spi_master *master; struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *dma_channels; struct resource *r; |
55c381e48 [ARM] omap: conve... |
1205 |
void __iomem *base; |
ccdc7bf92 SPI: omap2_mcspi ... |
1206 1207 1208 1209 |
master = dev_get_drvdata(&pdev->dev); mcspi = spi_master_get_devdata(master); dma_channels = mcspi->dma_channels; |
1f1a4384b OMAP: runtime: Mc... |
1210 |
omap2_mcspi_disable_clocks(mcspi); |
751c925cb spi/omap: call pm... |
1211 |
pm_runtime_disable(&pdev->dev); |
ccdc7bf92 SPI: omap2_mcspi ... |
1212 |
r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
8e2943c04 spi: Convert uses... |
1213 |
release_mem_region(r->start, resource_size(r)); |
ccdc7bf92 SPI: omap2_mcspi ... |
1214 |
|
55c381e48 [ARM] omap: conve... |
1215 |
base = mcspi->base; |
ccdc7bf92 SPI: omap2_mcspi ... |
1216 |
spi_unregister_master(master); |
55c381e48 [ARM] omap: conve... |
1217 |
iounmap(base); |
ccdc7bf92 SPI: omap2_mcspi ... |
1218 |
kfree(dma_channels); |
2856ac13b spi/omap: Use a w... |
1219 |
destroy_workqueue(mcspi->wq); |
39f1b5659 spi/omap: Correct... |
1220 |
platform_set_drvdata(pdev, NULL); |
ccdc7bf92 SPI: omap2_mcspi ... |
1221 1222 1223 |
return 0; } |
7e38c3c44 spi: fix platform... |
1224 1225 |
/* work with hotplug and coldplug */ MODULE_ALIAS("platform:omap2_mcspi"); |
42ce7fd63 spi/omap2_mcspi.c... |
1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 |
#ifdef CONFIG_SUSPEND /* * When SPI wake up from off-mode, CS is in activate state. If it was in * unactive state when driver was suspend, then force it to unactive state at * wake up. */ static int omap2_mcspi_resume(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); struct omap2_mcspi *mcspi = spi_master_get_devdata(master); struct omap2_mcspi_cs *cs; omap2_mcspi_enable_clocks(mcspi); list_for_each_entry(cs, &omap2_mcspi_ctx[master->bus_num - 1].cs, node) { if ((cs->chconf0 & OMAP2_MCSPI_CHCONF_FORCE) == 0) { /* * We need to toggle CS state for OMAP take this * change in account. */ MOD_REG_BIT(cs->chconf0, OMAP2_MCSPI_CHCONF_FORCE, 1); __raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0); MOD_REG_BIT(cs->chconf0, OMAP2_MCSPI_CHCONF_FORCE, 0); __raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0); } } omap2_mcspi_disable_clocks(mcspi); return 0; } #else #define omap2_mcspi_resume NULL #endif static const struct dev_pm_ops omap2_mcspi_pm_ops = { .resume = omap2_mcspi_resume, |
1f1a4384b OMAP: runtime: Mc... |
1262 |
.runtime_resume = omap_mcspi_runtime_resume, |
42ce7fd63 spi/omap2_mcspi.c... |
1263 |
}; |
ccdc7bf92 SPI: omap2_mcspi ... |
1264 1265 1266 1267 |
static struct platform_driver omap2_mcspi_driver = { .driver = { .name = "omap2_mcspi", .owner = THIS_MODULE, |
42ce7fd63 spi/omap2_mcspi.c... |
1268 |
.pm = &omap2_mcspi_pm_ops |
ccdc7bf92 SPI: omap2_mcspi ... |
1269 1270 1271 1272 1273 1274 1275 |
}, .remove = __exit_p(omap2_mcspi_remove), }; static int __init omap2_mcspi_init(void) { |
ccdc7bf92 SPI: omap2_mcspi ... |
1276 1277 1278 1279 1280 1281 1282 |
return platform_driver_probe(&omap2_mcspi_driver, omap2_mcspi_probe); } subsys_initcall(omap2_mcspi_init); static void __exit omap2_mcspi_exit(void) { platform_driver_unregister(&omap2_mcspi_driver); |
ccdc7bf92 SPI: omap2_mcspi ... |
1283 1284 1285 1286 |
} module_exit(omap2_mcspi_exit); MODULE_LICENSE("GPL"); |