aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/spi
diff options
context:
space:
mode:
authorJonas Gorski <jogo@openwrt.org>2013-02-03 09:15:12 -0500
committerGrant Likely <grant.likely@secretlab.ca>2013-02-05 09:44:13 -0500
commitc0fde3ba0e03af30f9fa9cdf180442189b3f7e9c (patch)
tree1ce171f8a63d1a031eb1ccc948be2bba891f90dd /drivers/spi
parent2cd94c8a1b4184b48ecbc16b353dcbb513053285 (diff)
spi/bcm63xx: reject transfers unable to transfer
The hardware does not support keeping CS asserted after sending one FIFO buffer worth of data, so reject transfers requiring CS being kept asserted, either between transers or for a certain time after it, or exceeding the FIFO size. Signed-off-by: Jonas Gorski <jogo@openwrt.org> Acked-by: Florian Fainelli <florian@openwrt.org> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/spi-bcm63xx.c91
1 files changed, 42 insertions, 49 deletions
diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c
index f44ab5508535..27667c17ce90 100644
--- a/drivers/spi/spi-bcm63xx.c
+++ b/drivers/spi/spi-bcm63xx.c
@@ -49,16 +49,10 @@ struct bcm63xx_spi {
49 unsigned int msg_type_shift; 49 unsigned int msg_type_shift;
50 unsigned int msg_ctl_width; 50 unsigned int msg_ctl_width;
51 51
52 /* Data buffers */
53 const unsigned char *tx_ptr;
54 unsigned char *rx_ptr;
55
56 /* data iomem */ 52 /* data iomem */
57 u8 __iomem *tx_io; 53 u8 __iomem *tx_io;
58 const u8 __iomem *rx_io; 54 const u8 __iomem *rx_io;
59 55
60 int remaining_bytes;
61
62 struct clk *clk; 56 struct clk *clk;
63 struct platform_device *pdev; 57 struct platform_device *pdev;
64}; 58};
@@ -175,24 +169,13 @@ static int bcm63xx_spi_setup(struct spi_device *spi)
175 return 0; 169 return 0;
176} 170}
177 171
178/* Fill the TX FIFO with as many bytes as possible */ 172static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
179static void bcm63xx_spi_fill_tx_fifo(struct bcm63xx_spi *bs)
180{
181 u8 size;
182
183 /* Fill the Tx FIFO with as many bytes as possible */
184 size = bs->remaining_bytes < bs->fifo_size ? bs->remaining_bytes :
185 bs->fifo_size;
186 memcpy_toio(bs->tx_io, bs->tx_ptr, size);
187 bs->remaining_bytes -= size;
188}
189
190static unsigned int bcm63xx_txrx_bufs(struct spi_device *spi,
191 struct spi_transfer *t)
192{ 173{
193 struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); 174 struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
194 u16 msg_ctl; 175 u16 msg_ctl;
195 u16 cmd; 176 u16 cmd;
177 u8 rx_tail;
178 unsigned int timeout = 0;
196 179
197 /* Disable the CMD_DONE interrupt */ 180 /* Disable the CMD_DONE interrupt */
198 bcm_spi_writeb(bs, 0, SPI_INT_MASK); 181 bcm_spi_writeb(bs, 0, SPI_INT_MASK);
@@ -200,14 +183,8 @@ static unsigned int bcm63xx_txrx_bufs(struct spi_device *spi,
200 dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", 183 dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
201 t->tx_buf, t->rx_buf, t->len); 184 t->tx_buf, t->rx_buf, t->len);
202 185
203 /* Transmitter is inhibited */ 186 if (t->tx_buf)
204 bs->tx_ptr = t->tx_buf; 187 memcpy_toio(bs->tx_io, t->tx_buf, t->len);
205 bs->rx_ptr = t->rx_buf;
206
207 if (t->tx_buf) {
208 bs->remaining_bytes = t->len;
209 bcm63xx_spi_fill_tx_fifo(bs);
210 }
211 188
212 init_completion(&bs->done); 189 init_completion(&bs->done);
213 190
@@ -239,7 +216,18 @@ static unsigned int bcm63xx_txrx_bufs(struct spi_device *spi,
239 /* Enable the CMD_DONE interrupt */ 216 /* Enable the CMD_DONE interrupt */
240 bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK); 217 bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK);
241 218
242 return t->len - bs->remaining_bytes; 219 timeout = wait_for_completion_timeout(&bs->done, HZ);
220 if (!timeout)
221 return -ETIMEDOUT;
222
223 /* read out all data */
224 rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL);
225
226 /* Read out all the data */
227 if (rx_tail)
228 memcpy_fromio(t->rx_ptr, bs->rx_io, rx_tail);
229
230 return 0;
243} 231}
244 232
245static int bcm63xx_spi_prepare_transfer(struct spi_master *master) 233static int bcm63xx_spi_prepare_transfer(struct spi_master *master)
@@ -267,36 +255,41 @@ static int bcm63xx_spi_transfer_one(struct spi_master *master,
267 struct spi_transfer *t; 255 struct spi_transfer *t;
268 struct spi_device *spi = m->spi; 256 struct spi_device *spi = m->spi;
269 int status = 0; 257 int status = 0;
270 unsigned int timeout = 0;
271 258
272 list_for_each_entry(t, &m->transfers, transfer_list) { 259 list_for_each_entry(t, &m->transfers, transfer_list) {
273 unsigned int len = t->len;
274 u8 rx_tail;
275
276 status = bcm63xx_spi_check_transfer(spi, t); 260 status = bcm63xx_spi_check_transfer(spi, t);
277 if (status < 0) 261 if (status < 0)
278 goto exit; 262 goto exit;
279 263
280 /* configure adapter for a new transfer */ 264 /* we can only transfer one fifo worth of data */
281 bcm63xx_spi_setup_transfer(spi, t); 265 if (t->len > bs->fifo_size) {
266 dev_err(&spi->dev, "unable to do transfers larger than FIFO size (%i > %i)\n",
267 t->len, bs->fifo_size);
268 status = -EINVAL;
269 goto exit;
270 }
282 271
283 while (len) { 272 /* CS will be deasserted directly after transfer */
284 /* send the data */ 273 if (t->delay_usecs) {
285 len -= bcm63xx_txrx_bufs(spi, t); 274 dev_err(&spi->dev, "unable to keep CS asserted after transfer\n");
275 status = -EINVAL;
276 goto exit;
277 }
286 278
287 timeout = wait_for_completion_timeout(&bs->done, HZ); 279 if (!t->cs_change &&
288 if (!timeout) { 280 !list_is_last(&t->transfer_list, &m->transfers)) {
289 status = -ETIMEDOUT; 281 dev_err(&spi->dev, "unable to keep CS asserted between transfers\n");
290 goto exit; 282 status = -EINVAL;
291 } 283 goto exit;
284 }
292 285
293 /* read out all data */ 286 /* configure adapter for a new transfer */
294 rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); 287 bcm63xx_spi_setup_transfer(spi, t);
295 288
296 /* Read out all the data */ 289 /* send the data */
297 if (rx_tail) 290 status = bcm63xx_txrx_bufs(spi, t);
298 memcpy_fromio(bs->rx_ptr, bs->rx_io, rx_tail); 291 if (status)
299 } 292 goto exit;
300 293
301 m->actual_length += t->len; 294 m->actual_length += t->len;
302 } 295 }