bcm63xx: fix spi transfer handling
[openwrt/staging/yousong.git] / target / linux / brcm63xx / patches-3.3 / 110-spi-bcm63xx-fix-multi-transfer-messages.patch
1 From 0f2ae1e1282ff64f74a5e36f7da874f94911225e Mon Sep 17 00:00:00 2001
2 From: Jonas Gorski <jonas.gorski@gmail.com>
3 Date: Wed, 14 Nov 2012 22:22:33 +0100
4 Subject: [PATCH] spi/bcm63xx: fix multi transfer messages
5
6 The BCM63XX SPI controller does not support keeping CS asserted after
7 sending its buffer. This breaks common usages like spi_write_then_read,
8 where it is expected to be kept active during the whole transfers.
9
10 Work around this by combining the transfers into one if the buffer
11 allows. For spi_write_then_read, use the prepend byte feature to write
12 to "prepend" the write if it is less than 15 bytes, allowing the whole
13 fifo size for the read.
14
15 Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
16 ---
17 Tested on a SPI conntected switch which required keeping CS active between
18 the register read command and reading the register contents.
19
20 Based on Mark's spi/next.
21
22 Not sure if this is stable material, as it's quite invasive.
23
24 drivers/spi/spi-bcm63xx.c | 172 ++++++++++++++++++++++++++++++---------------
25 1 file changed, 117 insertions(+), 55 deletions(-)
26
27 --- a/drivers/spi/spi-bcm63xx.c
28 +++ b/drivers/spi/spi-bcm63xx.c
29 @@ -38,6 +38,8 @@
30 #define PFX KBUILD_MODNAME
31 #define DRV_VER "0.1.2"
32
33 +#define BCM63XX_SPI_MAX_PREPEND 15
34 +
35 struct bcm63xx_spi {
36 struct completion done;
37
38 @@ -50,16 +52,10 @@ struct bcm63xx_spi {
39 unsigned int msg_type_shift;
40 unsigned int msg_ctl_width;
41
42 - /* Data buffers */
43 - const unsigned char *tx_ptr;
44 - unsigned char *rx_ptr;
45 -
46 /* data iomem */
47 u8 __iomem *tx_io;
48 const u8 __iomem *rx_io;
49
50 - int remaining_bytes;
51 -
52 struct clk *clk;
53 struct platform_device *pdev;
54 };
55 @@ -184,50 +180,60 @@ static int bcm63xx_spi_setup(struct spi_
56 return 0;
57 }
58
59 -/* Fill the TX FIFO with as many bytes as possible */
60 -static void bcm63xx_spi_fill_tx_fifo(struct bcm63xx_spi *bs)
61 -{
62 - u8 size;
63 -
64 - /* Fill the Tx FIFO with as many bytes as possible */
65 - size = bs->remaining_bytes < bs->fifo_size ? bs->remaining_bytes :
66 - bs->fifo_size;
67 - memcpy_toio(bs->tx_io, bs->tx_ptr, size);
68 - bs->remaining_bytes -= size;
69 -}
70 -
71 static unsigned int bcm63xx_txrx_bufs(struct spi_device *spi,
72 - struct spi_transfer *t)
73 + struct spi_transfer *first,
74 + unsigned int n_transfers)
75 {
76 struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
77 u16 msg_ctl;
78 u16 cmd;
79 + unsigned int i, timeout, total_len = 0, prepend_len = 0, len = 0;
80 + struct spi_transfer *t = first;
81 + u8 rx_tail;
82 + bool do_rx = false;
83 + bool do_tx = false;
84
85 /* Disable the CMD_DONE interrupt */
86 bcm_spi_writeb(bs, 0, SPI_INT_MASK);
87
88 - dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
89 - t->tx_buf, t->rx_buf, t->len);
90 + if (n_transfers > 1 && t->tx_buf && t->len <= BCM63XX_SPI_MAX_PREPEND)
91 + prepend_len = t->len;
92 +
93 + /* prepare the buffer */
94 + for (i = 0; i < n_transfers; i++) {
95 + if (t->tx_buf) {
96 + do_tx = true;
97 + memcpy_toio(bs->tx_io + total_len, t->tx_buf, t->len);
98 +
99 + /* don't prepend more than one tx */
100 + if (t != first)
101 + prepend_len = 0;
102 + }
103 +
104 + if (t->rx_buf) {
105 + do_rx = true;
106 + if (t == first)
107 + prepend_len = 0;
108 + }
109
110 - /* Transmitter is inhibited */
111 - bs->tx_ptr = t->tx_buf;
112 - bs->rx_ptr = t->rx_buf;
113 -
114 - if (t->tx_buf) {
115 - bs->remaining_bytes = t->len;
116 - bcm63xx_spi_fill_tx_fifo(bs);
117 + total_len += t->len;
118 +
119 + t = list_entry(t->transfer_list.next, struct spi_transfer,
120 + transfer_list);
121 }
122
123 + len = total_len - prepend_len;
124 +
125 init_completion(&bs->done);
126
127 /* Fill in the Message control register */
128 - msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT);
129 + msg_ctl = (len << SPI_BYTE_CNT_SHIFT);
130
131 - if (t->rx_buf && t->tx_buf)
132 + if (do_rx && do_tx && prepend_len == 0)
133 msg_ctl |= (SPI_FD_RW << bs->msg_type_shift);
134 - else if (t->rx_buf)
135 + else if (do_rx)
136 msg_ctl |= (SPI_HD_R << bs->msg_type_shift);
137 - else if (t->tx_buf)
138 + else if (do_tx)
139 msg_ctl |= (SPI_HD_W << bs->msg_type_shift);
140
141 switch (bs->msg_ctl_width) {
142 @@ -245,14 +251,41 @@ static unsigned int bcm63xx_txrx_bufs(st
143
144 /* Issue the transfer */
145 cmd = SPI_CMD_START_IMMEDIATE;
146 - cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT);
147 + cmd |= (prepend_len << SPI_CMD_PREPEND_BYTE_CNT_SHIFT);
148 cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT);
149 bcm_spi_writew(bs, cmd, SPI_CMD);
150
151 /* Enable the CMD_DONE interrupt */
152 bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK);
153
154 - return t->len - bs->remaining_bytes;
155 + timeout = wait_for_completion_timeout(&bs->done, HZ);
156 + if (!timeout)
157 + return -ETIMEDOUT;
158 +
159 + /* read out all data */
160 + rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL);
161 +
162 + if (do_rx && rx_tail != len)
163 + return -EINVAL;
164 +
165 + if (!rx_tail)
166 + return total_len;
167 +
168 + len = 0;
169 + t = first;
170 + /* Read out all the data */
171 + for (i = 0; i < n_transfers; i++) {
172 + if (t->rx_buf)
173 + memcpy_fromio(t->rx_buf, bs->rx_io + len, t->len);
174 +
175 + if (t != first || prepend_len == 0)
176 + len += t->len;
177 +
178 + t = list_entry(t->transfer_list.next, struct spi_transfer,
179 + transfer_list);
180 + }
181 +
182 + return total_len;
183 }
184
185 static int bcm63xx_spi_prepare_transfer(struct spi_master *master)
186 @@ -277,42 +310,71 @@ static int bcm63xx_spi_transfer_one(stru
187 struct spi_message *m)
188 {
189 struct bcm63xx_spi *bs = spi_master_get_devdata(master);
190 - struct spi_transfer *t;
191 + struct spi_transfer *t, *first = NULL;
192 struct spi_device *spi = m->spi;
193 int status = 0;
194 - unsigned int timeout = 0;
195 + unsigned int n_transfers = 0, total_len = 0;
196 + bool can_use_prepend = false;
197
198 + /*
199 + * This SPI controller does not support keeping CS active after a
200 + * transfer, so we need to combine the transfers into one until we may
201 + * deassert CS.
202 + */
203 list_for_each_entry(t, &m->transfers, transfer_list) {
204 - unsigned int len = t->len;
205 - u8 rx_tail;
206 -
207 status = bcm63xx_spi_check_transfer(spi, t);
208 if (status < 0)
209 goto exit;
210
211 - /* configure adapter for a new transfer */
212 - bcm63xx_spi_setup_transfer(spi, t);
213 + if (!first)
214 + first = t;
215
216 - while (len) {
217 - /* send the data */
218 - len -= bcm63xx_txrx_bufs(spi, t);
219 -
220 - timeout = wait_for_completion_timeout(&bs->done, HZ);
221 - if (!timeout) {
222 - status = -ETIMEDOUT;
223 - goto exit;
224 - }
225 + n_transfers++;
226 + total_len += t->len;
227 +
228 + if (n_transfers == 2 && !first->rx_buf && !t->tx_buf &&
229 + first->len <= BCM63XX_SPI_MAX_PREPEND)
230 + can_use_prepend = true;
231 + else if (can_use_prepend && t->tx_buf)
232 + can_use_prepend = false;
233 +
234 + if ((can_use_prepend &&
235 + total_len > (bs->fifo_size + BCM63XX_SPI_MAX_PREPEND)) ||
236 + (!can_use_prepend && total_len > bs->fifo_size)) {
237 + status = -EINVAL;
238 + goto exit;
239 + }
240
241 - /* read out all data */
242 - rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL);
243 + /* all transfers have to be made at the same speed */
244 + if (t->speed_hz != first->speed_hz) {
245 + status = -EINVAL;
246 + goto exit;
247 + }
248
249 - /* Read out all the data */
250 - if (rx_tail)
251 - memcpy_fromio(bs->rx_ptr, bs->rx_io, rx_tail);
252 + /* CS will be deasserted directly after the transfer */
253 + if (t->delay_usecs) {
254 + status = -EINVAL;
255 + goto exit;
256 }
257
258 - m->actual_length += t->len;
259 + if (t->cs_change ||
260 + list_is_last(&t->transfer_list, &m->transfers)) {
261 + /* configure adapter for a new transfer */
262 + bcm63xx_spi_setup_transfer(spi, first);
263 +
264 + status = bcm63xx_txrx_bufs(spi, first, n_transfers);
265 + if (status < 0)
266 + goto exit;
267 +
268 + m->actual_length += status;
269 + first = NULL;
270 + status = 0;
271 + n_transfers = 0;
272 + total_len = 0;
273 + can_use_prepend = false;
274 + }
275 }
276 +
277 exit:
278 m->status = status;
279 spi_finalize_current_message(master);