diff options
Diffstat (limited to 'drivers/spi/spi-bcm63xx.c')
-rw-r--r-- | drivers/spi/spi-bcm63xx.c | 163 |
1 files changed, 92 insertions, 71 deletions
diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index f01b2648452e..7491971139a6 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * Broadcom BCM63xx SPI controller support | 2 | * Broadcom BCM63xx SPI controller support |
3 | * | 3 | * |
4 | * Copyright (C) 2009-2011 Florian Fainelli <florian@openwrt.org> | 4 | * Copyright (C) 2009-2012 Florian Fainelli <florian@openwrt.org> |
5 | * Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com> | 5 | * Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com> |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or | 7 | * This program is free software; you can redistribute it and/or |
@@ -30,6 +30,8 @@ | |||
30 | #include <linux/spi/spi.h> | 30 | #include <linux/spi/spi.h> |
31 | #include <linux/completion.h> | 31 | #include <linux/completion.h> |
32 | #include <linux/err.h> | 32 | #include <linux/err.h> |
33 | #include <linux/workqueue.h> | ||
34 | #include <linux/pm_runtime.h> | ||
33 | 35 | ||
34 | #include <bcm63xx_dev_spi.h> | 36 | #include <bcm63xx_dev_spi.h> |
35 | 37 | ||
@@ -37,8 +39,6 @@ | |||
37 | #define DRV_VER "0.1.2" | 39 | #define DRV_VER "0.1.2" |
38 | 40 | ||
39 | struct bcm63xx_spi { | 41 | struct bcm63xx_spi { |
40 | spinlock_t lock; | ||
41 | int stopping; | ||
42 | struct completion done; | 42 | struct completion done; |
43 | 43 | ||
44 | void __iomem *regs; | 44 | void __iomem *regs; |
@@ -96,17 +96,12 @@ static const unsigned bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = { | |||
96 | { 391000, SPI_CLK_0_391MHZ } | 96 | { 391000, SPI_CLK_0_391MHZ } |
97 | }; | 97 | }; |
98 | 98 | ||
99 | static int bcm63xx_spi_setup_transfer(struct spi_device *spi, | 99 | static int bcm63xx_spi_check_transfer(struct spi_device *spi, |
100 | struct spi_transfer *t) | 100 | struct spi_transfer *t) |
101 | { | 101 | { |
102 | struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); | ||
103 | u8 bits_per_word; | 102 | u8 bits_per_word; |
104 | u8 clk_cfg, reg; | ||
105 | u32 hz; | ||
106 | int i; | ||
107 | 103 | ||
108 | bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word; | 104 | bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word; |
109 | hz = (t) ? t->speed_hz : spi->max_speed_hz; | ||
110 | if (bits_per_word != 8) { | 105 | if (bits_per_word != 8) { |
111 | dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", | 106 | dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", |
112 | __func__, bits_per_word); | 107 | __func__, bits_per_word); |
@@ -119,6 +114,19 @@ static int bcm63xx_spi_setup_transfer(struct spi_device *spi, | |||
119 | return -EINVAL; | 114 | return -EINVAL; |
120 | } | 115 | } |
121 | 116 | ||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | static void bcm63xx_spi_setup_transfer(struct spi_device *spi, | ||
121 | struct spi_transfer *t) | ||
122 | { | ||
123 | struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); | ||
124 | u32 hz; | ||
125 | u8 clk_cfg, reg; | ||
126 | int i; | ||
127 | |||
128 | hz = (t) ? t->speed_hz : spi->max_speed_hz; | ||
129 | |||
122 | /* Find the closest clock configuration */ | 130 | /* Find the closest clock configuration */ |
123 | for (i = 0; i < SPI_CLK_MASK; i++) { | 131 | for (i = 0; i < SPI_CLK_MASK; i++) { |
124 | if (hz <= bcm63xx_spi_freq_table[i][0]) { | 132 | if (hz <= bcm63xx_spi_freq_table[i][0]) { |
@@ -139,8 +147,6 @@ static int bcm63xx_spi_setup_transfer(struct spi_device *spi, | |||
139 | bcm_spi_writeb(bs, reg, SPI_CLK_CFG); | 147 | bcm_spi_writeb(bs, reg, SPI_CLK_CFG); |
140 | dev_dbg(&spi->dev, "Setting clock register to %02x (hz %d)\n", | 148 | dev_dbg(&spi->dev, "Setting clock register to %02x (hz %d)\n", |
141 | clk_cfg, hz); | 149 | clk_cfg, hz); |
142 | |||
143 | return 0; | ||
144 | } | 150 | } |
145 | 151 | ||
146 | /* the spi->mode bits understood by this driver: */ | 152 | /* the spi->mode bits understood by this driver: */ |
@@ -153,9 +159,6 @@ static int bcm63xx_spi_setup(struct spi_device *spi) | |||
153 | 159 | ||
154 | bs = spi_master_get_devdata(spi->master); | 160 | bs = spi_master_get_devdata(spi->master); |
155 | 161 | ||
156 | if (bs->stopping) | ||
157 | return -ESHUTDOWN; | ||
158 | |||
159 | if (!spi->bits_per_word) | 162 | if (!spi->bits_per_word) |
160 | spi->bits_per_word = 8; | 163 | spi->bits_per_word = 8; |
161 | 164 | ||
@@ -165,7 +168,7 @@ static int bcm63xx_spi_setup(struct spi_device *spi) | |||
165 | return -EINVAL; | 168 | return -EINVAL; |
166 | } | 169 | } |
167 | 170 | ||
168 | ret = bcm63xx_spi_setup_transfer(spi, NULL); | 171 | ret = bcm63xx_spi_check_transfer(spi, NULL); |
169 | if (ret < 0) { | 172 | if (ret < 0) { |
170 | dev_err(&spi->dev, "setup: unsupported mode bits %x\n", | 173 | dev_err(&spi->dev, "setup: unsupported mode bits %x\n", |
171 | spi->mode & ~MODEBITS); | 174 | spi->mode & ~MODEBITS); |
@@ -190,28 +193,29 @@ static void bcm63xx_spi_fill_tx_fifo(struct bcm63xx_spi *bs) | |||
190 | bs->remaining_bytes -= size; | 193 | bs->remaining_bytes -= size; |
191 | } | 194 | } |
192 | 195 | ||
193 | static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) | 196 | static unsigned int bcm63xx_txrx_bufs(struct spi_device *spi, |
197 | struct spi_transfer *t) | ||
194 | { | 198 | { |
195 | struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); | 199 | struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); |
196 | u16 msg_ctl; | 200 | u16 msg_ctl; |
197 | u16 cmd; | 201 | u16 cmd; |
198 | 202 | ||
203 | /* Disable the CMD_DONE interrupt */ | ||
204 | bcm_spi_writeb(bs, 0, SPI_INT_MASK); | ||
205 | |||
199 | dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", | 206 | dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", |
200 | t->tx_buf, t->rx_buf, t->len); | 207 | t->tx_buf, t->rx_buf, t->len); |
201 | 208 | ||
202 | /* Transmitter is inhibited */ | 209 | /* Transmitter is inhibited */ |
203 | bs->tx_ptr = t->tx_buf; | 210 | bs->tx_ptr = t->tx_buf; |
204 | bs->rx_ptr = t->rx_buf; | 211 | bs->rx_ptr = t->rx_buf; |
205 | init_completion(&bs->done); | ||
206 | 212 | ||
207 | if (t->tx_buf) { | 213 | if (t->tx_buf) { |
208 | bs->remaining_bytes = t->len; | 214 | bs->remaining_bytes = t->len; |
209 | bcm63xx_spi_fill_tx_fifo(bs); | 215 | bcm63xx_spi_fill_tx_fifo(bs); |
210 | } | 216 | } |
211 | 217 | ||
212 | /* Enable the command done interrupt which | 218 | init_completion(&bs->done); |
213 | * we use to determine completion of a command */ | ||
214 | bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK); | ||
215 | 219 | ||
216 | /* Fill in the Message control register */ | 220 | /* Fill in the Message control register */ |
217 | msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT); | 221 | msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT); |
@@ -230,33 +234,76 @@ static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) | |||
230 | cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); | 234 | cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); |
231 | cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT); | 235 | cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT); |
232 | bcm_spi_writew(bs, cmd, SPI_CMD); | 236 | bcm_spi_writew(bs, cmd, SPI_CMD); |
233 | wait_for_completion(&bs->done); | ||
234 | 237 | ||
235 | /* Disable the CMD_DONE interrupt */ | 238 | /* Enable the CMD_DONE interrupt */ |
236 | bcm_spi_writeb(bs, 0, SPI_INT_MASK); | 239 | bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK); |
237 | 240 | ||
238 | return t->len - bs->remaining_bytes; | 241 | return t->len - bs->remaining_bytes; |
239 | } | 242 | } |
240 | 243 | ||
241 | static int bcm63xx_transfer(struct spi_device *spi, struct spi_message *m) | 244 | static int bcm63xx_spi_prepare_transfer(struct spi_master *master) |
242 | { | 245 | { |
243 | struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); | 246 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); |
244 | struct spi_transfer *t; | ||
245 | int ret = 0; | ||
246 | 247 | ||
247 | if (unlikely(list_empty(&m->transfers))) | 248 | pm_runtime_get_sync(&bs->pdev->dev); |
248 | return -EINVAL; | ||
249 | 249 | ||
250 | if (bs->stopping) | 250 | return 0; |
251 | return -ESHUTDOWN; | 251 | } |
252 | |||
253 | static int bcm63xx_spi_unprepare_transfer(struct spi_master *master) | ||
254 | { | ||
255 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); | ||
256 | |||
257 | pm_runtime_put(&bs->pdev->dev); | ||
258 | |||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | static int bcm63xx_spi_transfer_one(struct spi_master *master, | ||
263 | struct spi_message *m) | ||
264 | { | ||
265 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); | ||
266 | struct spi_transfer *t; | ||
267 | struct spi_device *spi = m->spi; | ||
268 | int status = 0; | ||
269 | unsigned int timeout = 0; | ||
252 | 270 | ||
253 | list_for_each_entry(t, &m->transfers, transfer_list) { | 271 | list_for_each_entry(t, &m->transfers, transfer_list) { |
254 | ret += bcm63xx_txrx_bufs(spi, t); | 272 | unsigned int len = t->len; |
255 | } | 273 | u8 rx_tail; |
256 | 274 | ||
257 | m->complete(m->context); | 275 | status = bcm63xx_spi_check_transfer(spi, t); |
276 | if (status < 0) | ||
277 | goto exit; | ||
258 | 278 | ||
259 | return ret; | 279 | /* configure adapter for a new transfer */ |
280 | bcm63xx_spi_setup_transfer(spi, t); | ||
281 | |||
282 | while (len) { | ||
283 | /* send the data */ | ||
284 | len -= bcm63xx_txrx_bufs(spi, t); | ||
285 | |||
286 | timeout = wait_for_completion_timeout(&bs->done, HZ); | ||
287 | if (!timeout) { | ||
288 | status = -ETIMEDOUT; | ||
289 | goto exit; | ||
290 | } | ||
291 | |||
292 | /* read out all data */ | ||
293 | rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); | ||
294 | |||
295 | /* Read out all the data */ | ||
296 | if (rx_tail) | ||
297 | memcpy_fromio(bs->rx_ptr, bs->rx_io, rx_tail); | ||
298 | } | ||
299 | |||
300 | m->actual_length += t->len; | ||
301 | } | ||
302 | exit: | ||
303 | m->status = status; | ||
304 | spi_finalize_current_message(master); | ||
305 | |||
306 | return 0; | ||
260 | } | 307 | } |
261 | 308 | ||
262 | /* This driver supports single master mode only. Hence | 309 | /* This driver supports single master mode only. Hence |
@@ -267,39 +314,15 @@ static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id) | |||
267 | struct spi_master *master = (struct spi_master *)dev_id; | 314 | struct spi_master *master = (struct spi_master *)dev_id; |
268 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); | 315 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); |
269 | u8 intr; | 316 | u8 intr; |
270 | u16 cmd; | ||
271 | 317 | ||
272 | /* Read interupts and clear them immediately */ | 318 | /* Read interupts and clear them immediately */ |
273 | intr = bcm_spi_readb(bs, SPI_INT_STATUS); | 319 | intr = bcm_spi_readb(bs, SPI_INT_STATUS); |
274 | bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); | 320 | bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); |
275 | bcm_spi_writeb(bs, 0, SPI_INT_MASK); | 321 | bcm_spi_writeb(bs, 0, SPI_INT_MASK); |
276 | 322 | ||
277 | /* A tansfer completed */ | 323 | /* A transfer completed */ |
278 | if (intr & SPI_INTR_CMD_DONE) { | 324 | if (intr & SPI_INTR_CMD_DONE) |
279 | u8 rx_tail; | 325 | complete(&bs->done); |
280 | |||
281 | rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); | ||
282 | |||
283 | /* Read out all the data */ | ||
284 | if (rx_tail) | ||
285 | memcpy_fromio(bs->rx_ptr, bs->rx_io, rx_tail); | ||
286 | |||
287 | /* See if there is more data to send */ | ||
288 | if (bs->remaining_bytes > 0) { | ||
289 | bcm63xx_spi_fill_tx_fifo(bs); | ||
290 | |||
291 | /* Start the transfer */ | ||
292 | bcm_spi_writew(bs, SPI_HD_W << SPI_MSG_TYPE_SHIFT, | ||
293 | SPI_MSG_CTL); | ||
294 | cmd = bcm_spi_readw(bs, SPI_CMD); | ||
295 | cmd |= SPI_CMD_START_IMMEDIATE; | ||
296 | cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); | ||
297 | bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK); | ||
298 | bcm_spi_writew(bs, cmd, SPI_CMD); | ||
299 | } else { | ||
300 | complete(&bs->done); | ||
301 | } | ||
302 | } | ||
303 | 326 | ||
304 | return IRQ_HANDLED; | 327 | return IRQ_HANDLED; |
305 | } | 328 | } |
@@ -345,7 +368,6 @@ static int __devinit bcm63xx_spi_probe(struct platform_device *pdev) | |||
345 | } | 368 | } |
346 | 369 | ||
347 | bs = spi_master_get_devdata(master); | 370 | bs = spi_master_get_devdata(master); |
348 | init_completion(&bs->done); | ||
349 | 371 | ||
350 | platform_set_drvdata(pdev, master); | 372 | platform_set_drvdata(pdev, master); |
351 | bs->pdev = pdev; | 373 | bs->pdev = pdev; |
@@ -379,12 +401,13 @@ static int __devinit bcm63xx_spi_probe(struct platform_device *pdev) | |||
379 | master->bus_num = pdata->bus_num; | 401 | master->bus_num = pdata->bus_num; |
380 | master->num_chipselect = pdata->num_chipselect; | 402 | master->num_chipselect = pdata->num_chipselect; |
381 | master->setup = bcm63xx_spi_setup; | 403 | master->setup = bcm63xx_spi_setup; |
382 | master->transfer = bcm63xx_transfer; | 404 | master->prepare_transfer_hardware = bcm63xx_spi_prepare_transfer; |
405 | master->unprepare_transfer_hardware = bcm63xx_spi_unprepare_transfer; | ||
406 | master->transfer_one_message = bcm63xx_spi_transfer_one; | ||
407 | master->mode_bits = MODEBITS; | ||
383 | bs->speed_hz = pdata->speed_hz; | 408 | bs->speed_hz = pdata->speed_hz; |
384 | bs->stopping = 0; | ||
385 | bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA)); | 409 | bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA)); |
386 | bs->rx_io = (const u8 *)(bs->regs + bcm63xx_spireg(SPI_RX_DATA)); | 410 | bs->rx_io = (const u8 *)(bs->regs + bcm63xx_spireg(SPI_RX_DATA)); |
387 | spin_lock_init(&bs->lock); | ||
388 | 411 | ||
389 | /* Initialize hardware */ | 412 | /* Initialize hardware */ |
390 | clk_enable(bs->clk); | 413 | clk_enable(bs->clk); |
@@ -418,18 +441,16 @@ static int __devexit bcm63xx_spi_remove(struct platform_device *pdev) | |||
418 | struct spi_master *master = platform_get_drvdata(pdev); | 441 | struct spi_master *master = platform_get_drvdata(pdev); |
419 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); | 442 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); |
420 | 443 | ||
444 | spi_unregister_master(master); | ||
445 | |||
421 | /* reset spi block */ | 446 | /* reset spi block */ |
422 | bcm_spi_writeb(bs, 0, SPI_INT_MASK); | 447 | bcm_spi_writeb(bs, 0, SPI_INT_MASK); |
423 | spin_lock(&bs->lock); | ||
424 | bs->stopping = 1; | ||
425 | 448 | ||
426 | /* HW shutdown */ | 449 | /* HW shutdown */ |
427 | clk_disable(bs->clk); | 450 | clk_disable(bs->clk); |
428 | clk_put(bs->clk); | 451 | clk_put(bs->clk); |
429 | 452 | ||
430 | spin_unlock(&bs->lock); | ||
431 | platform_set_drvdata(pdev, 0); | 453 | platform_set_drvdata(pdev, 0); |
432 | spi_unregister_master(master); | ||
433 | 454 | ||
434 | return 0; | 455 | return 0; |
435 | } | 456 | } |