diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/spi/spi-bcm63xx.c | 149 |
1 files changed, 89 insertions, 60 deletions
diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index f01b2648452e..63b0028a8bd3 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 | ||
@@ -96,17 +98,12 @@ static const unsigned bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = { | |||
96 | { 391000, SPI_CLK_0_391MHZ } | 98 | { 391000, SPI_CLK_0_391MHZ } |
97 | }; | 99 | }; |
98 | 100 | ||
99 | static int bcm63xx_spi_setup_transfer(struct spi_device *spi, | 101 | static int bcm63xx_spi_check_transfer(struct spi_device *spi, |
100 | struct spi_transfer *t) | 102 | struct spi_transfer *t) |
101 | { | 103 | { |
102 | struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); | ||
103 | u8 bits_per_word; | 104 | u8 bits_per_word; |
104 | u8 clk_cfg, reg; | ||
105 | u32 hz; | ||
106 | int i; | ||
107 | 105 | ||
108 | bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word; | 106 | 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) { | 107 | if (bits_per_word != 8) { |
111 | dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", | 108 | dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", |
112 | __func__, bits_per_word); | 109 | __func__, bits_per_word); |
@@ -119,6 +116,19 @@ static int bcm63xx_spi_setup_transfer(struct spi_device *spi, | |||
119 | return -EINVAL; | 116 | return -EINVAL; |
120 | } | 117 | } |
121 | 118 | ||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | static void bcm63xx_spi_setup_transfer(struct spi_device *spi, | ||
123 | struct spi_transfer *t) | ||
124 | { | ||
125 | struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); | ||
126 | u32 hz; | ||
127 | u8 clk_cfg, reg; | ||
128 | int i; | ||
129 | |||
130 | hz = (t) ? t->speed_hz : spi->max_speed_hz; | ||
131 | |||
122 | /* Find the closest clock configuration */ | 132 | /* Find the closest clock configuration */ |
123 | for (i = 0; i < SPI_CLK_MASK; i++) { | 133 | for (i = 0; i < SPI_CLK_MASK; i++) { |
124 | if (hz <= bcm63xx_spi_freq_table[i][0]) { | 134 | if (hz <= bcm63xx_spi_freq_table[i][0]) { |
@@ -139,8 +149,6 @@ static int bcm63xx_spi_setup_transfer(struct spi_device *spi, | |||
139 | bcm_spi_writeb(bs, reg, SPI_CLK_CFG); | 149 | bcm_spi_writeb(bs, reg, SPI_CLK_CFG); |
140 | dev_dbg(&spi->dev, "Setting clock register to %02x (hz %d)\n", | 150 | dev_dbg(&spi->dev, "Setting clock register to %02x (hz %d)\n", |
141 | clk_cfg, hz); | 151 | clk_cfg, hz); |
142 | |||
143 | return 0; | ||
144 | } | 152 | } |
145 | 153 | ||
146 | /* the spi->mode bits understood by this driver: */ | 154 | /* the spi->mode bits understood by this driver: */ |
@@ -165,7 +173,7 @@ static int bcm63xx_spi_setup(struct spi_device *spi) | |||
165 | return -EINVAL; | 173 | return -EINVAL; |
166 | } | 174 | } |
167 | 175 | ||
168 | ret = bcm63xx_spi_setup_transfer(spi, NULL); | 176 | ret = bcm63xx_spi_check_transfer(spi, NULL); |
169 | if (ret < 0) { | 177 | if (ret < 0) { |
170 | dev_err(&spi->dev, "setup: unsupported mode bits %x\n", | 178 | dev_err(&spi->dev, "setup: unsupported mode bits %x\n", |
171 | spi->mode & ~MODEBITS); | 179 | spi->mode & ~MODEBITS); |
@@ -190,28 +198,29 @@ static void bcm63xx_spi_fill_tx_fifo(struct bcm63xx_spi *bs) | |||
190 | bs->remaining_bytes -= size; | 198 | bs->remaining_bytes -= size; |
191 | } | 199 | } |
192 | 200 | ||
193 | static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) | 201 | static unsigned int bcm63xx_txrx_bufs(struct spi_device *spi, |
202 | struct spi_transfer *t) | ||
194 | { | 203 | { |
195 | struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); | 204 | struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); |
196 | u16 msg_ctl; | 205 | u16 msg_ctl; |
197 | u16 cmd; | 206 | u16 cmd; |
198 | 207 | ||
208 | /* Disable the CMD_DONE interrupt */ | ||
209 | bcm_spi_writeb(bs, 0, SPI_INT_MASK); | ||
210 | |||
199 | dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", | 211 | dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", |
200 | t->tx_buf, t->rx_buf, t->len); | 212 | t->tx_buf, t->rx_buf, t->len); |
201 | 213 | ||
202 | /* Transmitter is inhibited */ | 214 | /* Transmitter is inhibited */ |
203 | bs->tx_ptr = t->tx_buf; | 215 | bs->tx_ptr = t->tx_buf; |
204 | bs->rx_ptr = t->rx_buf; | 216 | bs->rx_ptr = t->rx_buf; |
205 | init_completion(&bs->done); | ||
206 | 217 | ||
207 | if (t->tx_buf) { | 218 | if (t->tx_buf) { |
208 | bs->remaining_bytes = t->len; | 219 | bs->remaining_bytes = t->len; |
209 | bcm63xx_spi_fill_tx_fifo(bs); | 220 | bcm63xx_spi_fill_tx_fifo(bs); |
210 | } | 221 | } |
211 | 222 | ||
212 | /* Enable the command done interrupt which | 223 | 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 | 224 | ||
216 | /* Fill in the Message control register */ | 225 | /* Fill in the Message control register */ |
217 | msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT); | 226 | msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT); |
@@ -230,33 +239,76 @@ static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) | |||
230 | cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); | 239 | cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); |
231 | cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT); | 240 | cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT); |
232 | bcm_spi_writew(bs, cmd, SPI_CMD); | 241 | bcm_spi_writew(bs, cmd, SPI_CMD); |
233 | wait_for_completion(&bs->done); | ||
234 | 242 | ||
235 | /* Disable the CMD_DONE interrupt */ | 243 | /* Enable the CMD_DONE interrupt */ |
236 | bcm_spi_writeb(bs, 0, SPI_INT_MASK); | 244 | bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK); |
237 | 245 | ||
238 | return t->len - bs->remaining_bytes; | 246 | return t->len - bs->remaining_bytes; |
239 | } | 247 | } |
240 | 248 | ||
241 | static int bcm63xx_transfer(struct spi_device *spi, struct spi_message *m) | 249 | static int bcm63xx_spi_prepare_transfer(struct spi_master *master) |
242 | { | 250 | { |
243 | struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); | 251 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); |
244 | struct spi_transfer *t; | ||
245 | int ret = 0; | ||
246 | 252 | ||
247 | if (unlikely(list_empty(&m->transfers))) | 253 | pm_runtime_get_sync(&bs->pdev->dev); |
248 | return -EINVAL; | ||
249 | 254 | ||
250 | if (bs->stopping) | 255 | return 0; |
251 | return -ESHUTDOWN; | 256 | } |
257 | |||
258 | static int bcm63xx_spi_unprepare_transfer(struct spi_master *master) | ||
259 | { | ||
260 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); | ||
261 | |||
262 | pm_runtime_put(&bs->pdev->dev); | ||
263 | |||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | static int bcm63xx_spi_transfer_one(struct spi_master *master, | ||
268 | struct spi_message *m) | ||
269 | { | ||
270 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); | ||
271 | struct spi_transfer *t; | ||
272 | struct spi_device *spi = m->spi; | ||
273 | int status = 0; | ||
274 | unsigned int timeout = 0; | ||
252 | 275 | ||
253 | list_for_each_entry(t, &m->transfers, transfer_list) { | 276 | list_for_each_entry(t, &m->transfers, transfer_list) { |
254 | ret += bcm63xx_txrx_bufs(spi, t); | 277 | unsigned int len = t->len; |
255 | } | 278 | u8 rx_tail; |
256 | 279 | ||
257 | m->complete(m->context); | 280 | status = bcm63xx_spi_check_transfer(spi, t); |
281 | if (status < 0) | ||
282 | goto exit; | ||
258 | 283 | ||
259 | return ret; | 284 | /* configure adapter for a new transfer */ |
285 | bcm63xx_spi_setup_transfer(spi, t); | ||
286 | |||
287 | while (len) { | ||
288 | /* send the data */ | ||
289 | len -= bcm63xx_txrx_bufs(spi, t); | ||
290 | |||
291 | timeout = wait_for_completion_timeout(&bs->done, HZ); | ||
292 | if (!timeout) { | ||
293 | status = -ETIMEDOUT; | ||
294 | goto exit; | ||
295 | } | ||
296 | |||
297 | /* read out all data */ | ||
298 | rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); | ||
299 | |||
300 | /* Read out all the data */ | ||
301 | if (rx_tail) | ||
302 | memcpy_fromio(bs->rx_ptr, bs->rx_io, rx_tail); | ||
303 | } | ||
304 | |||
305 | m->actual_length += t->len; | ||
306 | } | ||
307 | exit: | ||
308 | m->status = status; | ||
309 | spi_finalize_current_message(master); | ||
310 | |||
311 | return 0; | ||
260 | } | 312 | } |
261 | 313 | ||
262 | /* This driver supports single master mode only. Hence | 314 | /* This driver supports single master mode only. Hence |
@@ -267,39 +319,15 @@ static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id) | |||
267 | struct spi_master *master = (struct spi_master *)dev_id; | 319 | struct spi_master *master = (struct spi_master *)dev_id; |
268 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); | 320 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); |
269 | u8 intr; | 321 | u8 intr; |
270 | u16 cmd; | ||
271 | 322 | ||
272 | /* Read interupts and clear them immediately */ | 323 | /* Read interupts and clear them immediately */ |
273 | intr = bcm_spi_readb(bs, SPI_INT_STATUS); | 324 | intr = bcm_spi_readb(bs, SPI_INT_STATUS); |
274 | bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); | 325 | bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); |
275 | bcm_spi_writeb(bs, 0, SPI_INT_MASK); | 326 | bcm_spi_writeb(bs, 0, SPI_INT_MASK); |
276 | 327 | ||
277 | /* A tansfer completed */ | 328 | /* A transfer completed */ |
278 | if (intr & SPI_INTR_CMD_DONE) { | 329 | if (intr & SPI_INTR_CMD_DONE) |
279 | u8 rx_tail; | 330 | 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 | 331 | ||
304 | return IRQ_HANDLED; | 332 | return IRQ_HANDLED; |
305 | } | 333 | } |
@@ -345,7 +373,6 @@ static int __devinit bcm63xx_spi_probe(struct platform_device *pdev) | |||
345 | } | 373 | } |
346 | 374 | ||
347 | bs = spi_master_get_devdata(master); | 375 | bs = spi_master_get_devdata(master); |
348 | init_completion(&bs->done); | ||
349 | 376 | ||
350 | platform_set_drvdata(pdev, master); | 377 | platform_set_drvdata(pdev, master); |
351 | bs->pdev = pdev; | 378 | bs->pdev = pdev; |
@@ -379,7 +406,9 @@ static int __devinit bcm63xx_spi_probe(struct platform_device *pdev) | |||
379 | master->bus_num = pdata->bus_num; | 406 | master->bus_num = pdata->bus_num; |
380 | master->num_chipselect = pdata->num_chipselect; | 407 | master->num_chipselect = pdata->num_chipselect; |
381 | master->setup = bcm63xx_spi_setup; | 408 | master->setup = bcm63xx_spi_setup; |
382 | master->transfer = bcm63xx_transfer; | 409 | master->prepare_transfer_hardware = bcm63xx_spi_prepare_transfer; |
410 | master->unprepare_transfer_hardware = bcm63xx_spi_unprepare_transfer; | ||
411 | master->transfer_one_message = bcm63xx_spi_transfer_one; | ||
383 | bs->speed_hz = pdata->speed_hz; | 412 | bs->speed_hz = pdata->speed_hz; |
384 | bs->stopping = 0; | 413 | bs->stopping = 0; |
385 | bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA)); | 414 | bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA)); |