bcm63xx: update patches with upstream submissions
[openwrt/svn-archive/archive.git] / target / linux / brcm63xx / patches-3.8 / 002-spi-bcm63xx-fix-multi-transfer-messages.patch
1 From 725e81d507b1098cd275d4e3333c77c4b750fa79 Mon Sep 17 00:00:00 2001
2 From: Jonas Gorski <jogo@openwrt.org>
3 Date: Sun, 9 Dec 2012 01:53:05 +0100
4 Subject: [PATCH V2 2/2] spi/bcm63xx: work around inability to keep CS up
5
6 This SPI controller does not support keeping CS asserted after sending
7 a transfer.
8 Since messages expected on this SPI controller are rather short, we can
9 work around it for normal use cases by sending all transfers at once in
10 a big full duplex stream.
11
12 This means that we cannot change the speed between transfers if they
13 require CS to be kept asserted, but these would have been rejected
14 before anyway because of the inability of keeping CS asserted.
15
16 Signed-off-by: Jonas Gorski <jogo@openwrt.org>
17 ---
18 V1 -> V2:
19 * split out rejection logic into separate patch
20 * fixed return type of bcm63xx_txrx_bufs()
21 * slightly reworked bcm63xx_txrx_bufs, obsoleting one local variable
22
23 drivers/spi/spi-bcm63xx.c | 134 +++++++++++++++++++++++++++++++++++----------
24 1 file changed, 106 insertions(+), 28 deletions(-)
25
26 --- a/drivers/spi/spi-bcm63xx.c
27 +++ b/drivers/spi/spi-bcm63xx.c
28 @@ -37,6 +37,8 @@
29
30 #define PFX KBUILD_MODNAME
31
32 +#define BCM63XX_SPI_MAX_PREPEND 15
33 +
34 struct bcm63xx_spi {
35 struct completion done;
36
37 @@ -169,13 +171,17 @@ static int bcm63xx_spi_setup(struct spi_
38 return 0;
39 }
40
41 -static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
42 +static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first,
43 + unsigned int num_transfers)
44 {
45 struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
46 u16 msg_ctl;
47 u16 cmd;
48 u8 rx_tail;
49 - unsigned int timeout = 0;
50 + unsigned int i, timeout = 0, prepend_len = 0, len = 0;
51 + struct spi_transfer *t = first;
52 + bool do_rx = false;
53 + bool do_tx = false;
54
55 /* Disable the CMD_DONE interrupt */
56 bcm_spi_writeb(bs, 0, SPI_INT_MASK);
57 @@ -183,19 +189,45 @@ static int bcm63xx_txrx_bufs(struct spi_
58 dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
59 t->tx_buf, t->rx_buf, t->len);
60
61 - if (t->tx_buf)
62 - memcpy_toio(bs->tx_io, t->tx_buf, t->len);
63 + if (num_transfers > 1 && t->tx_buf && t->len <= BCM63XX_SPI_MAX_PREPEND)
64 + prepend_len = t->len;
65 +
66 + /* prepare the buffer */
67 + for (i = 0; i < num_transfers; i++) {
68 + if (t->tx_buf) {
69 + do_tx = true;
70 + memcpy_toio(bs->tx_io + len, t->tx_buf, t->len);
71 +
72 + /* don't prepend more than one tx */
73 + if (t != first)
74 + prepend_len = 0;
75 + }
76 +
77 + if (t->rx_buf) {
78 + do_rx = true;
79 + /* prepend is half-duplex write only */
80 + if (t == first)
81 + prepend_len = 0;
82 + }
83 +
84 + len += t->len;
85 +
86 + t = list_entry(t->transfer_list.next, struct spi_transfer,
87 + transfer_list);
88 + }
89 +
90 + len -= prepend_len;
91
92 init_completion(&bs->done);
93
94 /* Fill in the Message control register */
95 - msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT);
96 + msg_ctl = (len << SPI_BYTE_CNT_SHIFT);
97
98 - if (t->rx_buf && t->tx_buf)
99 + if (do_rx && do_tx && prepend_len == 0)
100 msg_ctl |= (SPI_FD_RW << bs->msg_type_shift);
101 - else if (t->rx_buf)
102 + else if (do_rx)
103 msg_ctl |= (SPI_HD_R << bs->msg_type_shift);
104 - else if (t->tx_buf)
105 + else if (do_tx)
106 msg_ctl |= (SPI_HD_W << bs->msg_type_shift);
107
108 switch (bs->msg_ctl_width) {
109 @@ -209,7 +241,7 @@ static int bcm63xx_txrx_bufs(struct spi_
110
111 /* Issue the transfer */
112 cmd = SPI_CMD_START_IMMEDIATE;
113 - cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT);
114 + cmd |= (prepend_len << SPI_CMD_PREPEND_BYTE_CNT_SHIFT);
115 cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT);
116 bcm_spi_writew(bs, cmd, SPI_CMD);
117
118 @@ -223,9 +255,25 @@ static int bcm63xx_txrx_bufs(struct spi_
119 /* read out all data */
120 rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL);
121
122 + if (do_rx && rx_tail != len)
123 + return -EIO;
124 +
125 + if (!rx_tail)
126 + return 0;
127 +
128 + len = 0;
129 + t = first;
130 /* Read out all the data */
131 - if (rx_tail)
132 - memcpy_fromio(t->rx_ptr, bs->rx_io, rx_tail);
133 + for (i = 0; i < num_transfers; i++) {
134 + if (t->rx_buf)
135 + memcpy_fromio(t->rx_buf, bs->rx_io + len, t->len);
136 +
137 + if (t != first || prepend_len == 0)
138 + len += t->len;
139 +
140 + t = list_entry(t->transfer_list.next, struct spi_transfer,
141 + transfer_list);
142 + }
143
144 return 0;
145 }
146 @@ -252,46 +300,76 @@ static int bcm63xx_spi_transfer_one(stru
147 struct spi_message *m)
148 {
149 struct bcm63xx_spi *bs = spi_master_get_devdata(master);
150 - struct spi_transfer *t;
151 + struct spi_transfer *t, *first = NULL;
152 struct spi_device *spi = m->spi;
153 int status = 0;
154 + unsigned int n_transfers = 0, total_len = 0;
155 + bool can_use_prepend = false;
156
157 + /*
158 + * This SPI controller does not support keeping CS active after a
159 + * transfer.
160 + * Work around this by merging as many transfers we can into one big
161 + * full-duplex transfers.
162 + */
163 list_for_each_entry(t, &m->transfers, transfer_list) {
164 status = bcm63xx_spi_check_transfer(spi, t);
165 if (status < 0)
166 goto exit;
167
168 + if (!first)
169 + first = t;
170 +
171 + n_transfers++;
172 + total_len += t->len;
173 +
174 + if (n_transfers == 2 && !first->rx_buf && !t->tx_buf &&
175 + first->len <= BCM63XX_SPI_MAX_PREPEND)
176 + can_use_prepend = true;
177 + else if (can_use_prepend && t->tx_buf)
178 + can_use_prepend = false;
179 +
180 /* we can only transfer one fifo worth of data */
181 - if (t->len > bs->fifo_size) {
182 + if ((can_use_prepend &&
183 + total_len > (bs->fifo_size + BCM63XX_SPI_MAX_PREPEND)) ||
184 + (!can_use_prepend && total_len > bs->fifo_size)) {
185 dev_err(&spi->dev, "unable to do transfers larger than FIFO size (%i > %i)\n",
186 - t->len, bs->fifo_size);
187 + total_len, bs->fifo_size);
188 status = -EINVAL;
189 goto exit;
190 }
191
192 - /* CS will be deasserted directly after transfer */
193 - if (t->delay_usecs) {
194 - dev_err(&spi->dev, "unable to keep CS asserted after transfer\n");
195 + /* all combined transfers have to have the same speed */
196 + if (t->speed_hz != first->speed_hz) {
197 + dev_err(&spi->dev, "unable to change speed between transfers\n");
198 status = -EINVAL;
199 goto exit;
200 }
201
202 - if (!t->cs_change &&
203 - !list_is_last(&t->transfer_list, &m->transfers)) {
204 - dev_err(&spi->dev, "unable to keep CS asserted between transfers\n");
205 + /* CS will be deasserted directly after transfer */
206 + if (t->delay_usecs) {
207 + dev_err(&spi->dev, "unable to keep CS asserted after transfer\n");
208 status = -EINVAL;
209 goto exit;
210 }
211
212 - /* configure adapter for a new transfer */
213 - bcm63xx_spi_setup_transfer(spi, t);
214 -
215 - /* send the data */
216 - status = bcm63xx_txrx_bufs(spi, t);
217 - if (status)
218 - goto exit;
219 -
220 - m->actual_length += t->len;
221 + if (t->cs_change ||
222 + list_is_last(&t->transfer_list, &m->transfers)) {
223 + /* configure adapter for a new transfer */
224 + bcm63xx_spi_setup_transfer(spi, first);
225 +
226 + /* send the data */
227 + status = bcm63xx_txrx_bufs(spi, first, n_transfers);
228 + if (status)
229 + goto exit;
230 +
231 + m->actual_length += total_len;
232 +
233 + first = NULL;
234 + n_transfers = 0;
235 + total_len = 0;
236 + can_use_prepend = false;
237 + }
238 }
239 exit:
240 m->status = status;