Blame view
drivers/spi/spi-dw-mid.c
7.87 KB
7063c0d94 spi/dw_spi: add D... |
1 |
/* |
ca632f556 spi: reorganize d... |
2 |
* Special handling for DW core on Intel MID platform |
7063c0d94 spi/dw_spi: add D... |
3 |
* |
197e96b4d spi: dw-mid: remo... |
4 |
* Copyright (c) 2009, 2014 Intel Corporation. |
7063c0d94 spi/dw_spi: add D... |
5 6 7 8 9 10 11 12 13 |
* * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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. |
7063c0d94 spi/dw_spi: add D... |
14 15 16 17 18 19 20 |
*/ #include <linux/dma-mapping.h> #include <linux/dmaengine.h> #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/spi/spi.h> |
258aea76f dmaengine: Pass d... |
21 |
#include <linux/types.h> |
568a60eda spi/dw_spi: move ... |
22 |
|
ca632f556 spi: reorganize d... |
23 |
#include "spi-dw.h" |
7063c0d94 spi/dw_spi: add D... |
24 25 |
#ifdef CONFIG_SPI_DW_MID_DMA |
7063c0d94 spi/dw_spi: add D... |
26 |
#include <linux/pci.h> |
d744f8268 spi: dw-mid: conv... |
27 |
#include <linux/platform_data/dma-dw.h> |
7063c0d94 spi/dw_spi: add D... |
28 |
|
30c8eb52c spi: dw-mid: spli... |
29 30 |
#define RX_BUSY 0 #define TX_BUSY 1 |
d744f8268 spi: dw-mid: conv... |
31 32 |
static struct dw_dma_slave mid_dma_tx = { .dst_id = 1 }; static struct dw_dma_slave mid_dma_rx = { .src_id = 0 }; |
7063c0d94 spi/dw_spi: add D... |
33 34 35 |
static bool mid_spi_dma_chan_filter(struct dma_chan *chan, void *param) { |
d744f8268 spi: dw-mid: conv... |
36 37 38 39 |
struct dw_dma_slave *s = param; if (s->dma_dev != chan->device->dev) return false; |
7063c0d94 spi/dw_spi: add D... |
40 |
|
d744f8268 spi: dw-mid: conv... |
41 42 |
chan->private = s; return true; |
7063c0d94 spi/dw_spi: add D... |
43 44 45 46 |
} static int mid_spi_dma_init(struct dw_spi *dws) { |
b89e9c87d spi: dw-mid: remo... |
47 |
struct pci_dev *dma_dev; |
d744f8268 spi: dw-mid: conv... |
48 49 |
struct dw_dma_slave *tx = dws->dma_tx; struct dw_dma_slave *rx = dws->dma_rx; |
7063c0d94 spi/dw_spi: add D... |
50 51 52 53 |
dma_cap_mask_t mask; /* * Get pci device for DMA controller, currently it could only |
ea092455d spi: dw-mid: remo... |
54 |
* be the DMA controller of Medfield |
7063c0d94 spi/dw_spi: add D... |
55 |
*/ |
b89e9c87d spi: dw-mid: remo... |
56 57 58 |
dma_dev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0827, NULL); if (!dma_dev) return -ENODEV; |
7063c0d94 spi/dw_spi: add D... |
59 60 61 62 |
dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); /* 1. Init rx channel */ |
d744f8268 spi: dw-mid: conv... |
63 64 |
rx->dma_dev = &dma_dev->dev; dws->rxchan = dma_request_channel(mask, mid_spi_dma_chan_filter, rx); |
7063c0d94 spi/dw_spi: add D... |
65 66 |
if (!dws->rxchan) goto err_exit; |
f89a6d8f4 spi: dw-mid: move... |
67 |
dws->master->dma_rx = dws->rxchan; |
7063c0d94 spi/dw_spi: add D... |
68 69 |
/* 2. Init tx channel */ |
d744f8268 spi: dw-mid: conv... |
70 71 |
tx->dma_dev = &dma_dev->dev; dws->txchan = dma_request_channel(mask, mid_spi_dma_chan_filter, tx); |
7063c0d94 spi/dw_spi: add D... |
72 73 |
if (!dws->txchan) goto free_rxchan; |
f89a6d8f4 spi: dw-mid: move... |
74 |
dws->master->dma_tx = dws->txchan; |
7063c0d94 spi/dw_spi: add D... |
75 76 77 78 79 80 81 |
dws->dma_inited = 1; return 0; free_rxchan: dma_release_channel(dws->rxchan); err_exit: |
b89e9c87d spi: dw-mid: remo... |
82 |
return -EBUSY; |
7063c0d94 spi/dw_spi: add D... |
83 84 85 86 |
} static void mid_spi_dma_exit(struct dw_spi *dws) { |
fb57862ea spi: dw-mid: chec... |
87 88 |
if (!dws->dma_inited) return; |
8e45ef682 spi: dw-mid: term... |
89 |
|
a3ff95823 spi: dw-mid: swit... |
90 |
dmaengine_terminate_sync(dws->txchan); |
7063c0d94 spi/dw_spi: add D... |
91 |
dma_release_channel(dws->txchan); |
8e45ef682 spi: dw-mid: term... |
92 |
|
a3ff95823 spi: dw-mid: swit... |
93 |
dmaengine_terminate_sync(dws->rxchan); |
7063c0d94 spi/dw_spi: add D... |
94 95 |
dma_release_channel(dws->rxchan); } |
f051fc8f1 spi: dw-mid: take... |
96 97 |
static irqreturn_t dma_transfer(struct dw_spi *dws) { |
dd1144432 spi: dw-spi: Conv... |
98 |
u16 irq_status = dw_readl(dws, DW_SPI_ISR); |
f051fc8f1 spi: dw-mid: take... |
99 100 101 |
if (!irq_status) return IRQ_NONE; |
dd1144432 spi: dw-spi: Conv... |
102 |
dw_readl(dws, DW_SPI_ICR); |
f051fc8f1 spi: dw-mid: take... |
103 104 105 106 107 108 109 110 |
spi_reset_chip(dws); dev_err(&dws->master->dev, "%s: FIFO overrun/underrun ", __func__); dws->master->cur_msg->status = -EIO; spi_finalize_current_transfer(dws->master); return IRQ_HANDLED; } |
f89a6d8f4 spi: dw-mid: move... |
111 112 113 114 115 116 117 118 119 120 |
static bool mid_spi_can_dma(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) { struct dw_spi *dws = spi_master_get_devdata(master); if (!dws->dma_inited) return false; return xfer->len > dws->fifo_len; } |
e31abce77 spi: dw-mid: conv... |
121 122 123 124 125 126 127 128 |
static enum dma_slave_buswidth convert_dma_width(u32 dma_width) { if (dma_width == 1) return DMA_SLAVE_BUSWIDTH_1_BYTE; else if (dma_width == 2) return DMA_SLAVE_BUSWIDTH_2_BYTES; return DMA_SLAVE_BUSWIDTH_UNDEFINED; } |
7063c0d94 spi/dw_spi: add D... |
129 |
/* |
30c8eb52c spi: dw-mid: spli... |
130 131 |
* dws->dma_chan_busy is set before the dma transfer starts, callback for tx * channel will clear a corresponding bit. |
7063c0d94 spi/dw_spi: add D... |
132 |
*/ |
30c8eb52c spi: dw-mid: spli... |
133 |
static void dw_spi_dma_tx_done(void *arg) |
7063c0d94 spi/dw_spi: add D... |
134 135 |
{ struct dw_spi *dws = arg; |
854d2f241 spi: dw-mid: clea... |
136 137 |
clear_bit(TX_BUSY, &dws->dma_chan_busy); if (test_bit(RX_BUSY, &dws->dma_chan_busy)) |
7063c0d94 spi/dw_spi: add D... |
138 |
return; |
c22c62db3 spi: dw: move to ... |
139 |
spi_finalize_current_transfer(dws->master); |
7063c0d94 spi/dw_spi: add D... |
140 |
} |
f89a6d8f4 spi: dw-mid: move... |
141 142 |
static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws, struct spi_transfer *xfer) |
7063c0d94 spi/dw_spi: add D... |
143 |
{ |
a5c2db964 spi: dw-mid: refa... |
144 145 |
struct dma_slave_config txconf; struct dma_async_tx_descriptor *txdesc; |
7063c0d94 spi/dw_spi: add D... |
146 |
|
f89a6d8f4 spi: dw-mid: move... |
147 |
if (!xfer->tx_buf) |
30c8eb52c spi: dw-mid: spli... |
148 |
return NULL; |
a485df4b4 spi, serial: move... |
149 |
txconf.direction = DMA_MEM_TO_DEV; |
7063c0d94 spi/dw_spi: add D... |
150 |
txconf.dst_addr = dws->dma_addr; |
d744f8268 spi: dw-mid: conv... |
151 |
txconf.dst_maxburst = 16; |
7063c0d94 spi/dw_spi: add D... |
152 |
txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; |
e31abce77 spi: dw-mid: conv... |
153 |
txconf.dst_addr_width = convert_dma_width(dws->dma_width); |
258aea76f dmaengine: Pass d... |
154 |
txconf.device_fc = false; |
7063c0d94 spi/dw_spi: add D... |
155 |
|
2a2852994 spi: dw-mid: conv... |
156 |
dmaengine_slave_config(dws->txchan, &txconf); |
7063c0d94 spi/dw_spi: add D... |
157 |
|
2a2852994 spi: dw-mid: conv... |
158 |
txdesc = dmaengine_prep_slave_sg(dws->txchan, |
f89a6d8f4 spi: dw-mid: move... |
159 160 |
xfer->tx_sg.sgl, xfer->tx_sg.nents, |
a485df4b4 spi, serial: move... |
161 |
DMA_MEM_TO_DEV, |
f7477c2be spi: dw-mid: foll... |
162 |
DMA_PREP_INTERRUPT | DMA_CTRL_ACK); |
c9dafb27c spi: dw-mid: avoi... |
163 164 |
if (!txdesc) return NULL; |
30c8eb52c spi: dw-mid: spli... |
165 |
txdesc->callback = dw_spi_dma_tx_done; |
7063c0d94 spi/dw_spi: add D... |
166 |
txdesc->callback_param = dws; |
a5c2db964 spi: dw-mid: refa... |
167 168 |
return txdesc; } |
30c8eb52c spi: dw-mid: spli... |
169 170 171 172 173 174 175 |
/* * dws->dma_chan_busy is set before the dma transfer starts, callback for rx * channel will clear a corresponding bit. */ static void dw_spi_dma_rx_done(void *arg) { struct dw_spi *dws = arg; |
854d2f241 spi: dw-mid: clea... |
176 177 |
clear_bit(RX_BUSY, &dws->dma_chan_busy); if (test_bit(TX_BUSY, &dws->dma_chan_busy)) |
30c8eb52c spi: dw-mid: spli... |
178 |
return; |
c22c62db3 spi: dw: move to ... |
179 |
spi_finalize_current_transfer(dws->master); |
30c8eb52c spi: dw-mid: spli... |
180 |
} |
f89a6d8f4 spi: dw-mid: move... |
181 182 |
static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, struct spi_transfer *xfer) |
a5c2db964 spi: dw-mid: refa... |
183 184 185 |
{ struct dma_slave_config rxconf; struct dma_async_tx_descriptor *rxdesc; |
f89a6d8f4 spi: dw-mid: move... |
186 |
if (!xfer->rx_buf) |
30c8eb52c spi: dw-mid: spli... |
187 |
return NULL; |
a485df4b4 spi, serial: move... |
188 |
rxconf.direction = DMA_DEV_TO_MEM; |
7063c0d94 spi/dw_spi: add D... |
189 |
rxconf.src_addr = dws->dma_addr; |
d744f8268 spi: dw-mid: conv... |
190 |
rxconf.src_maxburst = 16; |
7063c0d94 spi/dw_spi: add D... |
191 |
rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; |
e31abce77 spi: dw-mid: conv... |
192 |
rxconf.src_addr_width = convert_dma_width(dws->dma_width); |
258aea76f dmaengine: Pass d... |
193 |
rxconf.device_fc = false; |
7063c0d94 spi/dw_spi: add D... |
194 |
|
2a2852994 spi: dw-mid: conv... |
195 |
dmaengine_slave_config(dws->rxchan, &rxconf); |
7063c0d94 spi/dw_spi: add D... |
196 |
|
2a2852994 spi: dw-mid: conv... |
197 |
rxdesc = dmaengine_prep_slave_sg(dws->rxchan, |
f89a6d8f4 spi: dw-mid: move... |
198 199 |
xfer->rx_sg.sgl, xfer->rx_sg.nents, |
a485df4b4 spi, serial: move... |
200 |
DMA_DEV_TO_MEM, |
f7477c2be spi: dw-mid: foll... |
201 |
DMA_PREP_INTERRUPT | DMA_CTRL_ACK); |
c9dafb27c spi: dw-mid: avoi... |
202 203 |
if (!rxdesc) return NULL; |
30c8eb52c spi: dw-mid: spli... |
204 |
rxdesc->callback = dw_spi_dma_rx_done; |
7063c0d94 spi/dw_spi: add D... |
205 |
rxdesc->callback_param = dws; |
a5c2db964 spi: dw-mid: refa... |
206 207 |
return rxdesc; } |
f89a6d8f4 spi: dw-mid: move... |
208 |
static int mid_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) |
a5c2db964 spi: dw-mid: refa... |
209 210 |
{ u16 dma_ctrl = 0; |
dd1144432 spi: dw-spi: Conv... |
211 212 |
dw_writel(dws, DW_SPI_DMARDLR, 0xf); dw_writel(dws, DW_SPI_DMATDLR, 0x10); |
a5c2db964 spi: dw-mid: refa... |
213 |
|
f89a6d8f4 spi: dw-mid: move... |
214 |
if (xfer->tx_buf) |
a5c2db964 spi: dw-mid: refa... |
215 |
dma_ctrl |= SPI_DMA_TDMAE; |
f89a6d8f4 spi: dw-mid: move... |
216 |
if (xfer->rx_buf) |
a5c2db964 spi: dw-mid: refa... |
217 |
dma_ctrl |= SPI_DMA_RDMAE; |
dd1144432 spi: dw-spi: Conv... |
218 |
dw_writel(dws, DW_SPI_DMACR, dma_ctrl); |
a5c2db964 spi: dw-mid: refa... |
219 |
|
f051fc8f1 spi: dw-mid: take... |
220 221 222 223 |
/* Set the interrupt mask */ spi_umask_intr(dws, SPI_INT_TXOI | SPI_INT_RXUI | SPI_INT_RXOI); dws->transfer_handler = dma_transfer; |
9f14538ec spi: dw-mid: spli... |
224 |
return 0; |
a5c2db964 spi: dw-mid: refa... |
225 |
} |
f89a6d8f4 spi: dw-mid: move... |
226 |
static int mid_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) |
a5c2db964 spi: dw-mid: refa... |
227 228 |
{ struct dma_async_tx_descriptor *txdesc, *rxdesc; |
9f14538ec spi: dw-mid: spli... |
229 |
/* Prepare the TX dma transfer */ |
f89a6d8f4 spi: dw-mid: move... |
230 |
txdesc = dw_spi_dma_prepare_tx(dws, xfer); |
a5c2db964 spi: dw-mid: refa... |
231 |
|
9f14538ec spi: dw-mid: spli... |
232 |
/* Prepare the RX dma transfer */ |
f89a6d8f4 spi: dw-mid: move... |
233 |
rxdesc = dw_spi_dma_prepare_rx(dws, xfer); |
a5c2db964 spi: dw-mid: refa... |
234 |
|
7063c0d94 spi/dw_spi: add D... |
235 |
/* rx must be started before tx due to spi instinct */ |
30c8eb52c spi: dw-mid: spli... |
236 237 238 239 240 241 242 243 244 245 246 |
if (rxdesc) { set_bit(RX_BUSY, &dws->dma_chan_busy); dmaengine_submit(rxdesc); dma_async_issue_pending(dws->rxchan); } if (txdesc) { set_bit(TX_BUSY, &dws->dma_chan_busy); dmaengine_submit(txdesc); dma_async_issue_pending(dws->txchan); } |
f7477c2be spi: dw-mid: foll... |
247 |
|
7063c0d94 spi/dw_spi: add D... |
248 249 |
return 0; } |
4d5ac1edf spi: dw-mid: clea... |
250 251 252 |
static void mid_spi_dma_stop(struct dw_spi *dws) { if (test_bit(TX_BUSY, &dws->dma_chan_busy)) { |
cf1716e9d spi: dw-mid: swit... |
253 |
dmaengine_terminate_sync(dws->txchan); |
4d5ac1edf spi: dw-mid: clea... |
254 255 256 |
clear_bit(TX_BUSY, &dws->dma_chan_busy); } if (test_bit(RX_BUSY, &dws->dma_chan_busy)) { |
cf1716e9d spi: dw-mid: swit... |
257 |
dmaengine_terminate_sync(dws->rxchan); |
4d5ac1edf spi: dw-mid: clea... |
258 259 260 |
clear_bit(RX_BUSY, &dws->dma_chan_busy); } } |
4fe338c94 spi: dw-mid: cons... |
261 |
static const struct dw_spi_dma_ops mid_dma_ops = { |
7063c0d94 spi/dw_spi: add D... |
262 263 |
.dma_init = mid_spi_dma_init, .dma_exit = mid_spi_dma_exit, |
9f14538ec spi: dw-mid: spli... |
264 |
.dma_setup = mid_spi_dma_setup, |
f89a6d8f4 spi: dw-mid: move... |
265 |
.can_dma = mid_spi_can_dma, |
7063c0d94 spi/dw_spi: add D... |
266 |
.dma_transfer = mid_spi_dma_transfer, |
4d5ac1edf spi: dw-mid: clea... |
267 |
.dma_stop = mid_spi_dma_stop, |
7063c0d94 spi/dw_spi: add D... |
268 269 |
}; #endif |
ea092455d spi: dw-mid: remo... |
270 |
/* Some specific info for SPI0 controller on Intel MID */ |
7063c0d94 spi/dw_spi: add D... |
271 |
|
d9c14743a spi: dw-mid: get ... |
272 |
/* HW info for MRST Clk Control Unit, 32b reg per controller */ |
7063c0d94 spi/dw_spi: add D... |
273 |
#define MRST_SPI_CLK_BASE 100000000 /* 100m */ |
d9c14743a spi: dw-mid: get ... |
274 |
#define MRST_CLK_SPI_REG 0xff11d86c |
7063c0d94 spi/dw_spi: add D... |
275 276 277 278 279 280 281 282 |
#define CLK_SPI_BDIV_OFFSET 0 #define CLK_SPI_BDIV_MASK 0x00000007 #define CLK_SPI_CDIV_OFFSET 9 #define CLK_SPI_CDIV_MASK 0x00000e00 #define CLK_SPI_DISABLE_OFFSET 8 int dw_spi_mid_init(struct dw_spi *dws) { |
7eb187b3c spi: spi-dw: fix ... |
283 284 |
void __iomem *clk_reg; u32 clk_cdiv; |
7063c0d94 spi/dw_spi: add D... |
285 |
|
d9c14743a spi: dw-mid: get ... |
286 |
clk_reg = ioremap_nocache(MRST_CLK_SPI_REG, 16); |
7063c0d94 spi/dw_spi: add D... |
287 288 |
if (!clk_reg) return -ENOMEM; |
d9c14743a spi: dw-mid: get ... |
289 290 291 292 |
/* Get SPI controller operating freq info */ clk_cdiv = readl(clk_reg + dws->bus_num * sizeof(u32)); clk_cdiv &= CLK_SPI_CDIV_MASK; clk_cdiv >>= CLK_SPI_CDIV_OFFSET; |
7063c0d94 spi/dw_spi: add D... |
293 |
dws->max_freq = MRST_SPI_CLK_BASE / (clk_cdiv + 1); |
d9c14743a spi: dw-mid: get ... |
294 |
|
7063c0d94 spi/dw_spi: add D... |
295 |
iounmap(clk_reg); |
7063c0d94 spi/dw_spi: add D... |
296 |
#ifdef CONFIG_SPI_DW_MID_DMA |
d744f8268 spi: dw-mid: conv... |
297 298 |
dws->dma_tx = &mid_dma_tx; dws->dma_rx = &mid_dma_rx; |
7063c0d94 spi/dw_spi: add D... |
299 300 301 302 |
dws->dma_ops = &mid_dma_ops; #endif return 0; } |