kernel: add md5sum for 3.9 final
[oweals/openwrt.git] / target / linux / brcm63xx / patches-3.7 / 107-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 @@ -241,14 +247,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 @@ -273,42 +306,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);