diff options
Diffstat (limited to 'drivers/spi/spi-dw-mid.c')
-rw-r--r-- | drivers/spi/spi-dw-mid.c | 114 |
1 files changed, 83 insertions, 31 deletions
diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 46c6d58e1fda..7281316a5ecb 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c | |||
@@ -26,6 +26,9 @@ | |||
26 | #include <linux/intel_mid_dma.h> | 26 | #include <linux/intel_mid_dma.h> |
27 | #include <linux/pci.h> | 27 | #include <linux/pci.h> |
28 | 28 | ||
29 | #define RX_BUSY 0 | ||
30 | #define TX_BUSY 1 | ||
31 | |||
29 | struct mid_dma { | 32 | struct mid_dma { |
30 | struct intel_mid_dma_slave dmas_tx; | 33 | struct intel_mid_dma_slave dmas_tx; |
31 | struct intel_mid_dma_slave dmas_rx; | 34 | struct intel_mid_dma_slave dmas_rx; |
@@ -98,41 +101,26 @@ static void mid_spi_dma_exit(struct dw_spi *dws) | |||
98 | } | 101 | } |
99 | 102 | ||
100 | /* | 103 | /* |
101 | * dws->dma_chan_done is cleared before the dma transfer starts, | 104 | * dws->dma_chan_busy is set before the dma transfer starts, callback for tx |
102 | * callback for rx/tx channel will each increment it by 1. | 105 | * channel will clear a corresponding bit. |
103 | * Reaching 2 means the whole spi transaction is done. | ||
104 | */ | 106 | */ |
105 | static void dw_spi_dma_done(void *arg) | 107 | static void dw_spi_dma_tx_done(void *arg) |
106 | { | 108 | { |
107 | struct dw_spi *dws = arg; | 109 | struct dw_spi *dws = arg; |
108 | 110 | ||
109 | if (++dws->dma_chan_done != 2) | 111 | if (test_and_clear_bit(TX_BUSY, &dws->dma_chan_busy) & BIT(RX_BUSY)) |
110 | return; | 112 | return; |
111 | dw_spi_xfer_done(dws); | 113 | dw_spi_xfer_done(dws); |
112 | } | 114 | } |
113 | 115 | ||
114 | static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change) | 116 | static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws) |
115 | { | 117 | { |
116 | struct dma_async_tx_descriptor *txdesc, *rxdesc; | 118 | struct dma_slave_config txconf; |
117 | struct dma_slave_config txconf, rxconf; | 119 | struct dma_async_tx_descriptor *txdesc; |
118 | u16 dma_ctrl = 0; | ||
119 | |||
120 | /* 1. setup DMA related registers */ | ||
121 | if (cs_change) { | ||
122 | spi_enable_chip(dws, 0); | ||
123 | dw_writew(dws, DW_SPI_DMARDLR, 0xf); | ||
124 | dw_writew(dws, DW_SPI_DMATDLR, 0x10); | ||
125 | if (dws->tx_dma) | ||
126 | dma_ctrl |= SPI_DMA_TDMAE; | ||
127 | if (dws->rx_dma) | ||
128 | dma_ctrl |= SPI_DMA_RDMAE; | ||
129 | dw_writew(dws, DW_SPI_DMACR, dma_ctrl); | ||
130 | spi_enable_chip(dws, 1); | ||
131 | } | ||
132 | 120 | ||
133 | dws->dma_chan_done = 0; | 121 | if (!dws->tx_dma) |
122 | return NULL; | ||
134 | 123 | ||
135 | /* 2. Prepare the TX dma transfer */ | ||
136 | txconf.direction = DMA_MEM_TO_DEV; | 124 | txconf.direction = DMA_MEM_TO_DEV; |
137 | txconf.dst_addr = dws->dma_addr; | 125 | txconf.dst_addr = dws->dma_addr; |
138 | txconf.dst_maxburst = LNW_DMA_MSIZE_16; | 126 | txconf.dst_maxburst = LNW_DMA_MSIZE_16; |
@@ -151,10 +139,33 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change) | |||
151 | 1, | 139 | 1, |
152 | DMA_MEM_TO_DEV, | 140 | DMA_MEM_TO_DEV, |
153 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | 141 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); |
154 | txdesc->callback = dw_spi_dma_done; | 142 | txdesc->callback = dw_spi_dma_tx_done; |
155 | txdesc->callback_param = dws; | 143 | txdesc->callback_param = dws; |
156 | 144 | ||
157 | /* 3. Prepare the RX dma transfer */ | 145 | return txdesc; |
146 | } | ||
147 | |||
148 | /* | ||
149 | * dws->dma_chan_busy is set before the dma transfer starts, callback for rx | ||
150 | * channel will clear a corresponding bit. | ||
151 | */ | ||
152 | static void dw_spi_dma_rx_done(void *arg) | ||
153 | { | ||
154 | struct dw_spi *dws = arg; | ||
155 | |||
156 | if (test_and_clear_bit(RX_BUSY, &dws->dma_chan_busy) & BIT(TX_BUSY)) | ||
157 | return; | ||
158 | dw_spi_xfer_done(dws); | ||
159 | } | ||
160 | |||
161 | static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws) | ||
162 | { | ||
163 | struct dma_slave_config rxconf; | ||
164 | struct dma_async_tx_descriptor *rxdesc; | ||
165 | |||
166 | if (!dws->rx_dma) | ||
167 | return NULL; | ||
168 | |||
158 | rxconf.direction = DMA_DEV_TO_MEM; | 169 | rxconf.direction = DMA_DEV_TO_MEM; |
159 | rxconf.src_addr = dws->dma_addr; | 170 | rxconf.src_addr = dws->dma_addr; |
160 | rxconf.src_maxburst = LNW_DMA_MSIZE_16; | 171 | rxconf.src_maxburst = LNW_DMA_MSIZE_16; |
@@ -173,15 +184,56 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change) | |||
173 | 1, | 184 | 1, |
174 | DMA_DEV_TO_MEM, | 185 | DMA_DEV_TO_MEM, |
175 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | 186 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); |
176 | rxdesc->callback = dw_spi_dma_done; | 187 | rxdesc->callback = dw_spi_dma_rx_done; |
177 | rxdesc->callback_param = dws; | 188 | rxdesc->callback_param = dws; |
178 | 189 | ||
190 | return rxdesc; | ||
191 | } | ||
192 | |||
193 | static void dw_spi_dma_setup(struct dw_spi *dws) | ||
194 | { | ||
195 | u16 dma_ctrl = 0; | ||
196 | |||
197 | spi_enable_chip(dws, 0); | ||
198 | |||
199 | dw_writew(dws, DW_SPI_DMARDLR, 0xf); | ||
200 | dw_writew(dws, DW_SPI_DMATDLR, 0x10); | ||
201 | |||
202 | if (dws->tx_dma) | ||
203 | dma_ctrl |= SPI_DMA_TDMAE; | ||
204 | if (dws->rx_dma) | ||
205 | dma_ctrl |= SPI_DMA_RDMAE; | ||
206 | dw_writew(dws, DW_SPI_DMACR, dma_ctrl); | ||
207 | |||
208 | spi_enable_chip(dws, 1); | ||
209 | } | ||
210 | |||
211 | static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change) | ||
212 | { | ||
213 | struct dma_async_tx_descriptor *txdesc, *rxdesc; | ||
214 | |||
215 | /* 1. setup DMA related registers */ | ||
216 | if (cs_change) | ||
217 | dw_spi_dma_setup(dws); | ||
218 | |||
219 | /* 2. Prepare the TX dma transfer */ | ||
220 | txdesc = dw_spi_dma_prepare_tx(dws); | ||
221 | |||
222 | /* 3. Prepare the RX dma transfer */ | ||
223 | rxdesc = dw_spi_dma_prepare_rx(dws); | ||
224 | |||
179 | /* rx must be started before tx due to spi instinct */ | 225 | /* rx must be started before tx due to spi instinct */ |
180 | dmaengine_submit(rxdesc); | 226 | if (rxdesc) { |
181 | dma_async_issue_pending(dws->rxchan); | 227 | set_bit(RX_BUSY, &dws->dma_chan_busy); |
228 | dmaengine_submit(rxdesc); | ||
229 | dma_async_issue_pending(dws->rxchan); | ||
230 | } | ||
182 | 231 | ||
183 | dmaengine_submit(txdesc); | 232 | if (txdesc) { |
184 | dma_async_issue_pending(dws->txchan); | 233 | set_bit(TX_BUSY, &dws->dma_chan_busy); |
234 | dmaengine_submit(txdesc); | ||
235 | dma_async_issue_pending(dws->txchan); | ||
236 | } | ||
185 | 237 | ||
186 | return 0; | 238 | return 0; |
187 | } | 239 | } |