Commit 58d8bebea57b519cb606a59dc1263556e8746119
Committed by
Mark Brown
1 parent
e2bdae0632
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
spi/bcm63xx: simplify bcm63xx_spi_check_transfer
bcm63xx_spi_check_transfer is only called from one place that has t always set, so directly check the transfer's bits_per_word. Signed-off-by: Jonas Gorski <jogo@openwrt.org> Acked-by: Florian Fainelli <florian@openwrt.org> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Showing 1 changed file with 2 additions and 5 deletions Inline Diff
drivers/spi/spi-bcm63xx.c
1 | /* | 1 | /* |
2 | * Broadcom BCM63xx SPI controller support | 2 | * Broadcom BCM63xx SPI controller support |
3 | * | 3 | * |
4 | * Copyright (C) 2009-2012 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 |
8 | * modify it under the terms of the GNU General Public License | 8 | * modify it under the terms of the GNU General Public License |
9 | * as published by the Free Software Foundation; either version 2 | 9 | * as published by the Free Software Foundation; either version 2 |
10 | * of the License, or (at your option) any later version. | 10 | * of the License, or (at your option) any later version. |
11 | * | 11 | * |
12 | * This program is distributed in the hope that it will be useful, | 12 | * This program is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | * GNU General Public License for more details. | 15 | * GNU General Public License for more details. |
16 | * | 16 | * |
17 | * You should have received a copy of the GNU General Public License | 17 | * You should have received a copy of the GNU General Public License |
18 | * along with this program; if not, write to the | 18 | * along with this program; if not, write to the |
19 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 19 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include <linux/kernel.h> | 22 | #include <linux/kernel.h> |
23 | #include <linux/init.h> | 23 | #include <linux/init.h> |
24 | #include <linux/clk.h> | 24 | #include <linux/clk.h> |
25 | #include <linux/io.h> | 25 | #include <linux/io.h> |
26 | #include <linux/module.h> | 26 | #include <linux/module.h> |
27 | #include <linux/platform_device.h> | 27 | #include <linux/platform_device.h> |
28 | #include <linux/delay.h> | 28 | #include <linux/delay.h> |
29 | #include <linux/interrupt.h> | 29 | #include <linux/interrupt.h> |
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> | 33 | #include <linux/workqueue.h> |
34 | #include <linux/pm_runtime.h> | 34 | #include <linux/pm_runtime.h> |
35 | 35 | ||
36 | #include <bcm63xx_dev_spi.h> | 36 | #include <bcm63xx_dev_spi.h> |
37 | 37 | ||
38 | #define PFX KBUILD_MODNAME | 38 | #define PFX KBUILD_MODNAME |
39 | 39 | ||
40 | #define BCM63XX_SPI_MAX_PREPEND 15 | 40 | #define BCM63XX_SPI_MAX_PREPEND 15 |
41 | 41 | ||
42 | struct bcm63xx_spi { | 42 | struct bcm63xx_spi { |
43 | struct completion done; | 43 | struct completion done; |
44 | 44 | ||
45 | void __iomem *regs; | 45 | void __iomem *regs; |
46 | int irq; | 46 | int irq; |
47 | 47 | ||
48 | /* Platform data */ | 48 | /* Platform data */ |
49 | u32 speed_hz; | 49 | u32 speed_hz; |
50 | unsigned fifo_size; | 50 | unsigned fifo_size; |
51 | unsigned int msg_type_shift; | 51 | unsigned int msg_type_shift; |
52 | unsigned int msg_ctl_width; | 52 | unsigned int msg_ctl_width; |
53 | 53 | ||
54 | /* data iomem */ | 54 | /* data iomem */ |
55 | u8 __iomem *tx_io; | 55 | u8 __iomem *tx_io; |
56 | const u8 __iomem *rx_io; | 56 | const u8 __iomem *rx_io; |
57 | 57 | ||
58 | struct clk *clk; | 58 | struct clk *clk; |
59 | struct platform_device *pdev; | 59 | struct platform_device *pdev; |
60 | }; | 60 | }; |
61 | 61 | ||
62 | static inline u8 bcm_spi_readb(struct bcm63xx_spi *bs, | 62 | static inline u8 bcm_spi_readb(struct bcm63xx_spi *bs, |
63 | unsigned int offset) | 63 | unsigned int offset) |
64 | { | 64 | { |
65 | return bcm_readb(bs->regs + bcm63xx_spireg(offset)); | 65 | return bcm_readb(bs->regs + bcm63xx_spireg(offset)); |
66 | } | 66 | } |
67 | 67 | ||
68 | static inline u16 bcm_spi_readw(struct bcm63xx_spi *bs, | 68 | static inline u16 bcm_spi_readw(struct bcm63xx_spi *bs, |
69 | unsigned int offset) | 69 | unsigned int offset) |
70 | { | 70 | { |
71 | return bcm_readw(bs->regs + bcm63xx_spireg(offset)); | 71 | return bcm_readw(bs->regs + bcm63xx_spireg(offset)); |
72 | } | 72 | } |
73 | 73 | ||
74 | static inline void bcm_spi_writeb(struct bcm63xx_spi *bs, | 74 | static inline void bcm_spi_writeb(struct bcm63xx_spi *bs, |
75 | u8 value, unsigned int offset) | 75 | u8 value, unsigned int offset) |
76 | { | 76 | { |
77 | bcm_writeb(value, bs->regs + bcm63xx_spireg(offset)); | 77 | bcm_writeb(value, bs->regs + bcm63xx_spireg(offset)); |
78 | } | 78 | } |
79 | 79 | ||
80 | static inline void bcm_spi_writew(struct bcm63xx_spi *bs, | 80 | static inline void bcm_spi_writew(struct bcm63xx_spi *bs, |
81 | u16 value, unsigned int offset) | 81 | u16 value, unsigned int offset) |
82 | { | 82 | { |
83 | bcm_writew(value, bs->regs + bcm63xx_spireg(offset)); | 83 | bcm_writew(value, bs->regs + bcm63xx_spireg(offset)); |
84 | } | 84 | } |
85 | 85 | ||
86 | static const unsigned bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = { | 86 | static const unsigned bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = { |
87 | { 20000000, SPI_CLK_20MHZ }, | 87 | { 20000000, SPI_CLK_20MHZ }, |
88 | { 12500000, SPI_CLK_12_50MHZ }, | 88 | { 12500000, SPI_CLK_12_50MHZ }, |
89 | { 6250000, SPI_CLK_6_250MHZ }, | 89 | { 6250000, SPI_CLK_6_250MHZ }, |
90 | { 3125000, SPI_CLK_3_125MHZ }, | 90 | { 3125000, SPI_CLK_3_125MHZ }, |
91 | { 1563000, SPI_CLK_1_563MHZ }, | 91 | { 1563000, SPI_CLK_1_563MHZ }, |
92 | { 781000, SPI_CLK_0_781MHZ }, | 92 | { 781000, SPI_CLK_0_781MHZ }, |
93 | { 391000, SPI_CLK_0_391MHZ } | 93 | { 391000, SPI_CLK_0_391MHZ } |
94 | }; | 94 | }; |
95 | 95 | ||
96 | static int bcm63xx_spi_check_transfer(struct spi_device *spi, | 96 | static int bcm63xx_spi_check_transfer(struct spi_device *spi, |
97 | struct spi_transfer *t) | 97 | struct spi_transfer *t) |
98 | { | 98 | { |
99 | u8 bits_per_word; | 99 | if (t->bits_per_word != 8) { |
100 | |||
101 | bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word; | ||
102 | if (bits_per_word != 8) { | ||
103 | dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", | 100 | dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", |
104 | __func__, bits_per_word); | 101 | __func__, t->bits_per_word); |
105 | return -EINVAL; | 102 | return -EINVAL; |
106 | } | 103 | } |
107 | 104 | ||
108 | if (spi->chip_select > spi->master->num_chipselect) { | 105 | if (spi->chip_select > spi->master->num_chipselect) { |
109 | dev_err(&spi->dev, "%s, unsupported slave %d\n", | 106 | dev_err(&spi->dev, "%s, unsupported slave %d\n", |
110 | __func__, spi->chip_select); | 107 | __func__, spi->chip_select); |
111 | return -EINVAL; | 108 | return -EINVAL; |
112 | } | 109 | } |
113 | 110 | ||
114 | return 0; | 111 | return 0; |
115 | } | 112 | } |
116 | 113 | ||
117 | static void bcm63xx_spi_setup_transfer(struct spi_device *spi, | 114 | static void bcm63xx_spi_setup_transfer(struct spi_device *spi, |
118 | struct spi_transfer *t) | 115 | struct spi_transfer *t) |
119 | { | 116 | { |
120 | struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); | 117 | struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); |
121 | u32 hz; | 118 | u32 hz; |
122 | u8 clk_cfg, reg; | 119 | u8 clk_cfg, reg; |
123 | int i; | 120 | int i; |
124 | 121 | ||
125 | hz = (t) ? t->speed_hz : spi->max_speed_hz; | 122 | hz = (t) ? t->speed_hz : spi->max_speed_hz; |
126 | 123 | ||
127 | /* Find the closest clock configuration */ | 124 | /* Find the closest clock configuration */ |
128 | for (i = 0; i < SPI_CLK_MASK; i++) { | 125 | for (i = 0; i < SPI_CLK_MASK; i++) { |
129 | if (hz >= bcm63xx_spi_freq_table[i][0]) { | 126 | if (hz >= bcm63xx_spi_freq_table[i][0]) { |
130 | clk_cfg = bcm63xx_spi_freq_table[i][1]; | 127 | clk_cfg = bcm63xx_spi_freq_table[i][1]; |
131 | break; | 128 | break; |
132 | } | 129 | } |
133 | } | 130 | } |
134 | 131 | ||
135 | /* No matching configuration found, default to lowest */ | 132 | /* No matching configuration found, default to lowest */ |
136 | if (i == SPI_CLK_MASK) | 133 | if (i == SPI_CLK_MASK) |
137 | clk_cfg = SPI_CLK_0_391MHZ; | 134 | clk_cfg = SPI_CLK_0_391MHZ; |
138 | 135 | ||
139 | /* clear existing clock configuration bits of the register */ | 136 | /* clear existing clock configuration bits of the register */ |
140 | reg = bcm_spi_readb(bs, SPI_CLK_CFG); | 137 | reg = bcm_spi_readb(bs, SPI_CLK_CFG); |
141 | reg &= ~SPI_CLK_MASK; | 138 | reg &= ~SPI_CLK_MASK; |
142 | reg |= clk_cfg; | 139 | reg |= clk_cfg; |
143 | 140 | ||
144 | bcm_spi_writeb(bs, reg, SPI_CLK_CFG); | 141 | bcm_spi_writeb(bs, reg, SPI_CLK_CFG); |
145 | dev_dbg(&spi->dev, "Setting clock register to %02x (hz %d)\n", | 142 | dev_dbg(&spi->dev, "Setting clock register to %02x (hz %d)\n", |
146 | clk_cfg, hz); | 143 | clk_cfg, hz); |
147 | } | 144 | } |
148 | 145 | ||
149 | /* the spi->mode bits understood by this driver: */ | 146 | /* the spi->mode bits understood by this driver: */ |
150 | #define MODEBITS (SPI_CPOL | SPI_CPHA) | 147 | #define MODEBITS (SPI_CPOL | SPI_CPHA) |
151 | 148 | ||
152 | static int bcm63xx_spi_setup(struct spi_device *spi) | 149 | static int bcm63xx_spi_setup(struct spi_device *spi) |
153 | { | 150 | { |
154 | if (spi->bits_per_word != 8) { | 151 | if (spi->bits_per_word != 8) { |
155 | dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", | 152 | dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", |
156 | __func__, spi->bits_per_word); | 153 | __func__, spi->bits_per_word); |
157 | return -EINVAL; | 154 | return -EINVAL; |
158 | } | 155 | } |
159 | 156 | ||
160 | return 0; | 157 | return 0; |
161 | } | 158 | } |
162 | 159 | ||
163 | static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first, | 160 | static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first, |
164 | unsigned int num_transfers) | 161 | unsigned int num_transfers) |
165 | { | 162 | { |
166 | struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); | 163 | struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); |
167 | u16 msg_ctl; | 164 | u16 msg_ctl; |
168 | u16 cmd; | 165 | u16 cmd; |
169 | u8 rx_tail; | 166 | u8 rx_tail; |
170 | unsigned int i, timeout = 0, prepend_len = 0, len = 0; | 167 | unsigned int i, timeout = 0, prepend_len = 0, len = 0; |
171 | struct spi_transfer *t = first; | 168 | struct spi_transfer *t = first; |
172 | bool do_rx = false; | 169 | bool do_rx = false; |
173 | bool do_tx = false; | 170 | bool do_tx = false; |
174 | 171 | ||
175 | /* Disable the CMD_DONE interrupt */ | 172 | /* Disable the CMD_DONE interrupt */ |
176 | bcm_spi_writeb(bs, 0, SPI_INT_MASK); | 173 | bcm_spi_writeb(bs, 0, SPI_INT_MASK); |
177 | 174 | ||
178 | dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", | 175 | dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", |
179 | t->tx_buf, t->rx_buf, t->len); | 176 | t->tx_buf, t->rx_buf, t->len); |
180 | 177 | ||
181 | if (num_transfers > 1 && t->tx_buf && t->len <= BCM63XX_SPI_MAX_PREPEND) | 178 | if (num_transfers > 1 && t->tx_buf && t->len <= BCM63XX_SPI_MAX_PREPEND) |
182 | prepend_len = t->len; | 179 | prepend_len = t->len; |
183 | 180 | ||
184 | /* prepare the buffer */ | 181 | /* prepare the buffer */ |
185 | for (i = 0; i < num_transfers; i++) { | 182 | for (i = 0; i < num_transfers; i++) { |
186 | if (t->tx_buf) { | 183 | if (t->tx_buf) { |
187 | do_tx = true; | 184 | do_tx = true; |
188 | memcpy_toio(bs->tx_io + len, t->tx_buf, t->len); | 185 | memcpy_toio(bs->tx_io + len, t->tx_buf, t->len); |
189 | 186 | ||
190 | /* don't prepend more than one tx */ | 187 | /* don't prepend more than one tx */ |
191 | if (t != first) | 188 | if (t != first) |
192 | prepend_len = 0; | 189 | prepend_len = 0; |
193 | } | 190 | } |
194 | 191 | ||
195 | if (t->rx_buf) { | 192 | if (t->rx_buf) { |
196 | do_rx = true; | 193 | do_rx = true; |
197 | /* prepend is half-duplex write only */ | 194 | /* prepend is half-duplex write only */ |
198 | if (t == first) | 195 | if (t == first) |
199 | prepend_len = 0; | 196 | prepend_len = 0; |
200 | } | 197 | } |
201 | 198 | ||
202 | len += t->len; | 199 | len += t->len; |
203 | 200 | ||
204 | t = list_entry(t->transfer_list.next, struct spi_transfer, | 201 | t = list_entry(t->transfer_list.next, struct spi_transfer, |
205 | transfer_list); | 202 | transfer_list); |
206 | } | 203 | } |
207 | 204 | ||
208 | len -= prepend_len; | 205 | len -= prepend_len; |
209 | 206 | ||
210 | init_completion(&bs->done); | 207 | init_completion(&bs->done); |
211 | 208 | ||
212 | /* Fill in the Message control register */ | 209 | /* Fill in the Message control register */ |
213 | msg_ctl = (len << SPI_BYTE_CNT_SHIFT); | 210 | msg_ctl = (len << SPI_BYTE_CNT_SHIFT); |
214 | 211 | ||
215 | if (do_rx && do_tx && prepend_len == 0) | 212 | if (do_rx && do_tx && prepend_len == 0) |
216 | msg_ctl |= (SPI_FD_RW << bs->msg_type_shift); | 213 | msg_ctl |= (SPI_FD_RW << bs->msg_type_shift); |
217 | else if (do_rx) | 214 | else if (do_rx) |
218 | msg_ctl |= (SPI_HD_R << bs->msg_type_shift); | 215 | msg_ctl |= (SPI_HD_R << bs->msg_type_shift); |
219 | else if (do_tx) | 216 | else if (do_tx) |
220 | msg_ctl |= (SPI_HD_W << bs->msg_type_shift); | 217 | msg_ctl |= (SPI_HD_W << bs->msg_type_shift); |
221 | 218 | ||
222 | switch (bs->msg_ctl_width) { | 219 | switch (bs->msg_ctl_width) { |
223 | case 8: | 220 | case 8: |
224 | bcm_spi_writeb(bs, msg_ctl, SPI_MSG_CTL); | 221 | bcm_spi_writeb(bs, msg_ctl, SPI_MSG_CTL); |
225 | break; | 222 | break; |
226 | case 16: | 223 | case 16: |
227 | bcm_spi_writew(bs, msg_ctl, SPI_MSG_CTL); | 224 | bcm_spi_writew(bs, msg_ctl, SPI_MSG_CTL); |
228 | break; | 225 | break; |
229 | } | 226 | } |
230 | 227 | ||
231 | /* Issue the transfer */ | 228 | /* Issue the transfer */ |
232 | cmd = SPI_CMD_START_IMMEDIATE; | 229 | cmd = SPI_CMD_START_IMMEDIATE; |
233 | cmd |= (prepend_len << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); | 230 | cmd |= (prepend_len << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); |
234 | cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT); | 231 | cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT); |
235 | bcm_spi_writew(bs, cmd, SPI_CMD); | 232 | bcm_spi_writew(bs, cmd, SPI_CMD); |
236 | 233 | ||
237 | /* Enable the CMD_DONE interrupt */ | 234 | /* Enable the CMD_DONE interrupt */ |
238 | bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK); | 235 | bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK); |
239 | 236 | ||
240 | timeout = wait_for_completion_timeout(&bs->done, HZ); | 237 | timeout = wait_for_completion_timeout(&bs->done, HZ); |
241 | if (!timeout) | 238 | if (!timeout) |
242 | return -ETIMEDOUT; | 239 | return -ETIMEDOUT; |
243 | 240 | ||
244 | /* read out all data */ | 241 | /* read out all data */ |
245 | rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); | 242 | rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); |
246 | 243 | ||
247 | if (do_rx && rx_tail != len) | 244 | if (do_rx && rx_tail != len) |
248 | return -EIO; | 245 | return -EIO; |
249 | 246 | ||
250 | if (!rx_tail) | 247 | if (!rx_tail) |
251 | return 0; | 248 | return 0; |
252 | 249 | ||
253 | len = 0; | 250 | len = 0; |
254 | t = first; | 251 | t = first; |
255 | /* Read out all the data */ | 252 | /* Read out all the data */ |
256 | for (i = 0; i < num_transfers; i++) { | 253 | for (i = 0; i < num_transfers; i++) { |
257 | if (t->rx_buf) | 254 | if (t->rx_buf) |
258 | memcpy_fromio(t->rx_buf, bs->rx_io + len, t->len); | 255 | memcpy_fromio(t->rx_buf, bs->rx_io + len, t->len); |
259 | 256 | ||
260 | if (t != first || prepend_len == 0) | 257 | if (t != first || prepend_len == 0) |
261 | len += t->len; | 258 | len += t->len; |
262 | 259 | ||
263 | t = list_entry(t->transfer_list.next, struct spi_transfer, | 260 | t = list_entry(t->transfer_list.next, struct spi_transfer, |
264 | transfer_list); | 261 | transfer_list); |
265 | } | 262 | } |
266 | 263 | ||
267 | return 0; | 264 | return 0; |
268 | } | 265 | } |
269 | 266 | ||
270 | static int bcm63xx_spi_prepare_transfer(struct spi_master *master) | 267 | static int bcm63xx_spi_prepare_transfer(struct spi_master *master) |
271 | { | 268 | { |
272 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); | 269 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); |
273 | 270 | ||
274 | pm_runtime_get_sync(&bs->pdev->dev); | 271 | pm_runtime_get_sync(&bs->pdev->dev); |
275 | 272 | ||
276 | return 0; | 273 | return 0; |
277 | } | 274 | } |
278 | 275 | ||
279 | static int bcm63xx_spi_unprepare_transfer(struct spi_master *master) | 276 | static int bcm63xx_spi_unprepare_transfer(struct spi_master *master) |
280 | { | 277 | { |
281 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); | 278 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); |
282 | 279 | ||
283 | pm_runtime_put(&bs->pdev->dev); | 280 | pm_runtime_put(&bs->pdev->dev); |
284 | 281 | ||
285 | return 0; | 282 | return 0; |
286 | } | 283 | } |
287 | 284 | ||
288 | static int bcm63xx_spi_transfer_one(struct spi_master *master, | 285 | static int bcm63xx_spi_transfer_one(struct spi_master *master, |
289 | struct spi_message *m) | 286 | struct spi_message *m) |
290 | { | 287 | { |
291 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); | 288 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); |
292 | struct spi_transfer *t, *first = NULL; | 289 | struct spi_transfer *t, *first = NULL; |
293 | struct spi_device *spi = m->spi; | 290 | struct spi_device *spi = m->spi; |
294 | int status = 0; | 291 | int status = 0; |
295 | unsigned int n_transfers = 0, total_len = 0; | 292 | unsigned int n_transfers = 0, total_len = 0; |
296 | bool can_use_prepend = false; | 293 | bool can_use_prepend = false; |
297 | 294 | ||
298 | /* | 295 | /* |
299 | * This SPI controller does not support keeping CS active after a | 296 | * This SPI controller does not support keeping CS active after a |
300 | * transfer. | 297 | * transfer. |
301 | * Work around this by merging as many transfers we can into one big | 298 | * Work around this by merging as many transfers we can into one big |
302 | * full-duplex transfers. | 299 | * full-duplex transfers. |
303 | */ | 300 | */ |
304 | list_for_each_entry(t, &m->transfers, transfer_list) { | 301 | list_for_each_entry(t, &m->transfers, transfer_list) { |
305 | status = bcm63xx_spi_check_transfer(spi, t); | 302 | status = bcm63xx_spi_check_transfer(spi, t); |
306 | if (status < 0) | 303 | if (status < 0) |
307 | goto exit; | 304 | goto exit; |
308 | 305 | ||
309 | if (!first) | 306 | if (!first) |
310 | first = t; | 307 | first = t; |
311 | 308 | ||
312 | n_transfers++; | 309 | n_transfers++; |
313 | total_len += t->len; | 310 | total_len += t->len; |
314 | 311 | ||
315 | if (n_transfers == 2 && !first->rx_buf && !t->tx_buf && | 312 | if (n_transfers == 2 && !first->rx_buf && !t->tx_buf && |
316 | first->len <= BCM63XX_SPI_MAX_PREPEND) | 313 | first->len <= BCM63XX_SPI_MAX_PREPEND) |
317 | can_use_prepend = true; | 314 | can_use_prepend = true; |
318 | else if (can_use_prepend && t->tx_buf) | 315 | else if (can_use_prepend && t->tx_buf) |
319 | can_use_prepend = false; | 316 | can_use_prepend = false; |
320 | 317 | ||
321 | /* we can only transfer one fifo worth of data */ | 318 | /* we can only transfer one fifo worth of data */ |
322 | if ((can_use_prepend && | 319 | if ((can_use_prepend && |
323 | total_len > (bs->fifo_size + BCM63XX_SPI_MAX_PREPEND)) || | 320 | total_len > (bs->fifo_size + BCM63XX_SPI_MAX_PREPEND)) || |
324 | (!can_use_prepend && total_len > bs->fifo_size)) { | 321 | (!can_use_prepend && total_len > bs->fifo_size)) { |
325 | dev_err(&spi->dev, "unable to do transfers larger than FIFO size (%i > %i)\n", | 322 | dev_err(&spi->dev, "unable to do transfers larger than FIFO size (%i > %i)\n", |
326 | total_len, bs->fifo_size); | 323 | total_len, bs->fifo_size); |
327 | status = -EINVAL; | 324 | status = -EINVAL; |
328 | goto exit; | 325 | goto exit; |
329 | } | 326 | } |
330 | 327 | ||
331 | /* all combined transfers have to have the same speed */ | 328 | /* all combined transfers have to have the same speed */ |
332 | if (t->speed_hz != first->speed_hz) { | 329 | if (t->speed_hz != first->speed_hz) { |
333 | dev_err(&spi->dev, "unable to change speed between transfers\n"); | 330 | dev_err(&spi->dev, "unable to change speed between transfers\n"); |
334 | status = -EINVAL; | 331 | status = -EINVAL; |
335 | goto exit; | 332 | goto exit; |
336 | } | 333 | } |
337 | 334 | ||
338 | /* CS will be deasserted directly after transfer */ | 335 | /* CS will be deasserted directly after transfer */ |
339 | if (t->delay_usecs) { | 336 | if (t->delay_usecs) { |
340 | dev_err(&spi->dev, "unable to keep CS asserted after transfer\n"); | 337 | dev_err(&spi->dev, "unable to keep CS asserted after transfer\n"); |
341 | status = -EINVAL; | 338 | status = -EINVAL; |
342 | goto exit; | 339 | goto exit; |
343 | } | 340 | } |
344 | 341 | ||
345 | if (t->cs_change || | 342 | if (t->cs_change || |
346 | list_is_last(&t->transfer_list, &m->transfers)) { | 343 | list_is_last(&t->transfer_list, &m->transfers)) { |
347 | /* configure adapter for a new transfer */ | 344 | /* configure adapter for a new transfer */ |
348 | bcm63xx_spi_setup_transfer(spi, first); | 345 | bcm63xx_spi_setup_transfer(spi, first); |
349 | 346 | ||
350 | /* send the data */ | 347 | /* send the data */ |
351 | status = bcm63xx_txrx_bufs(spi, first, n_transfers); | 348 | status = bcm63xx_txrx_bufs(spi, first, n_transfers); |
352 | if (status) | 349 | if (status) |
353 | goto exit; | 350 | goto exit; |
354 | 351 | ||
355 | m->actual_length += total_len; | 352 | m->actual_length += total_len; |
356 | 353 | ||
357 | first = NULL; | 354 | first = NULL; |
358 | n_transfers = 0; | 355 | n_transfers = 0; |
359 | total_len = 0; | 356 | total_len = 0; |
360 | can_use_prepend = false; | 357 | can_use_prepend = false; |
361 | } | 358 | } |
362 | } | 359 | } |
363 | exit: | 360 | exit: |
364 | m->status = status; | 361 | m->status = status; |
365 | spi_finalize_current_message(master); | 362 | spi_finalize_current_message(master); |
366 | 363 | ||
367 | return 0; | 364 | return 0; |
368 | } | 365 | } |
369 | 366 | ||
370 | /* This driver supports single master mode only. Hence | 367 | /* This driver supports single master mode only. Hence |
371 | * CMD_DONE is the only interrupt we care about | 368 | * CMD_DONE is the only interrupt we care about |
372 | */ | 369 | */ |
373 | static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id) | 370 | static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id) |
374 | { | 371 | { |
375 | struct spi_master *master = (struct spi_master *)dev_id; | 372 | struct spi_master *master = (struct spi_master *)dev_id; |
376 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); | 373 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); |
377 | u8 intr; | 374 | u8 intr; |
378 | 375 | ||
379 | /* Read interupts and clear them immediately */ | 376 | /* Read interupts and clear them immediately */ |
380 | intr = bcm_spi_readb(bs, SPI_INT_STATUS); | 377 | intr = bcm_spi_readb(bs, SPI_INT_STATUS); |
381 | bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); | 378 | bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); |
382 | bcm_spi_writeb(bs, 0, SPI_INT_MASK); | 379 | bcm_spi_writeb(bs, 0, SPI_INT_MASK); |
383 | 380 | ||
384 | /* A transfer completed */ | 381 | /* A transfer completed */ |
385 | if (intr & SPI_INTR_CMD_DONE) | 382 | if (intr & SPI_INTR_CMD_DONE) |
386 | complete(&bs->done); | 383 | complete(&bs->done); |
387 | 384 | ||
388 | return IRQ_HANDLED; | 385 | return IRQ_HANDLED; |
389 | } | 386 | } |
390 | 387 | ||
391 | 388 | ||
392 | static int bcm63xx_spi_probe(struct platform_device *pdev) | 389 | static int bcm63xx_spi_probe(struct platform_device *pdev) |
393 | { | 390 | { |
394 | struct resource *r; | 391 | struct resource *r; |
395 | struct device *dev = &pdev->dev; | 392 | struct device *dev = &pdev->dev; |
396 | struct bcm63xx_spi_pdata *pdata = pdev->dev.platform_data; | 393 | struct bcm63xx_spi_pdata *pdata = pdev->dev.platform_data; |
397 | int irq; | 394 | int irq; |
398 | struct spi_master *master; | 395 | struct spi_master *master; |
399 | struct clk *clk; | 396 | struct clk *clk; |
400 | struct bcm63xx_spi *bs; | 397 | struct bcm63xx_spi *bs; |
401 | int ret; | 398 | int ret; |
402 | 399 | ||
403 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 400 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
404 | if (!r) { | 401 | if (!r) { |
405 | dev_err(dev, "no iomem\n"); | 402 | dev_err(dev, "no iomem\n"); |
406 | ret = -ENXIO; | 403 | ret = -ENXIO; |
407 | goto out; | 404 | goto out; |
408 | } | 405 | } |
409 | 406 | ||
410 | irq = platform_get_irq(pdev, 0); | 407 | irq = platform_get_irq(pdev, 0); |
411 | if (irq < 0) { | 408 | if (irq < 0) { |
412 | dev_err(dev, "no irq\n"); | 409 | dev_err(dev, "no irq\n"); |
413 | ret = -ENXIO; | 410 | ret = -ENXIO; |
414 | goto out; | 411 | goto out; |
415 | } | 412 | } |
416 | 413 | ||
417 | clk = clk_get(dev, "spi"); | 414 | clk = clk_get(dev, "spi"); |
418 | if (IS_ERR(clk)) { | 415 | if (IS_ERR(clk)) { |
419 | dev_err(dev, "no clock for device\n"); | 416 | dev_err(dev, "no clock for device\n"); |
420 | ret = PTR_ERR(clk); | 417 | ret = PTR_ERR(clk); |
421 | goto out; | 418 | goto out; |
422 | } | 419 | } |
423 | 420 | ||
424 | master = spi_alloc_master(dev, sizeof(*bs)); | 421 | master = spi_alloc_master(dev, sizeof(*bs)); |
425 | if (!master) { | 422 | if (!master) { |
426 | dev_err(dev, "out of memory\n"); | 423 | dev_err(dev, "out of memory\n"); |
427 | ret = -ENOMEM; | 424 | ret = -ENOMEM; |
428 | goto out_clk; | 425 | goto out_clk; |
429 | } | 426 | } |
430 | 427 | ||
431 | bs = spi_master_get_devdata(master); | 428 | bs = spi_master_get_devdata(master); |
432 | 429 | ||
433 | platform_set_drvdata(pdev, master); | 430 | platform_set_drvdata(pdev, master); |
434 | bs->pdev = pdev; | 431 | bs->pdev = pdev; |
435 | 432 | ||
436 | if (!devm_request_mem_region(&pdev->dev, r->start, | 433 | if (!devm_request_mem_region(&pdev->dev, r->start, |
437 | resource_size(r), PFX)) { | 434 | resource_size(r), PFX)) { |
438 | dev_err(dev, "iomem request failed\n"); | 435 | dev_err(dev, "iomem request failed\n"); |
439 | ret = -ENXIO; | 436 | ret = -ENXIO; |
440 | goto out_err; | 437 | goto out_err; |
441 | } | 438 | } |
442 | 439 | ||
443 | bs->regs = devm_ioremap_nocache(&pdev->dev, r->start, | 440 | bs->regs = devm_ioremap_nocache(&pdev->dev, r->start, |
444 | resource_size(r)); | 441 | resource_size(r)); |
445 | if (!bs->regs) { | 442 | if (!bs->regs) { |
446 | dev_err(dev, "unable to ioremap regs\n"); | 443 | dev_err(dev, "unable to ioremap regs\n"); |
447 | ret = -ENOMEM; | 444 | ret = -ENOMEM; |
448 | goto out_err; | 445 | goto out_err; |
449 | } | 446 | } |
450 | 447 | ||
451 | bs->irq = irq; | 448 | bs->irq = irq; |
452 | bs->clk = clk; | 449 | bs->clk = clk; |
453 | bs->fifo_size = pdata->fifo_size; | 450 | bs->fifo_size = pdata->fifo_size; |
454 | 451 | ||
455 | ret = devm_request_irq(&pdev->dev, irq, bcm63xx_spi_interrupt, 0, | 452 | ret = devm_request_irq(&pdev->dev, irq, bcm63xx_spi_interrupt, 0, |
456 | pdev->name, master); | 453 | pdev->name, master); |
457 | if (ret) { | 454 | if (ret) { |
458 | dev_err(dev, "unable to request irq\n"); | 455 | dev_err(dev, "unable to request irq\n"); |
459 | goto out_err; | 456 | goto out_err; |
460 | } | 457 | } |
461 | 458 | ||
462 | master->bus_num = pdata->bus_num; | 459 | master->bus_num = pdata->bus_num; |
463 | master->num_chipselect = pdata->num_chipselect; | 460 | master->num_chipselect = pdata->num_chipselect; |
464 | master->setup = bcm63xx_spi_setup; | 461 | master->setup = bcm63xx_spi_setup; |
465 | master->prepare_transfer_hardware = bcm63xx_spi_prepare_transfer; | 462 | master->prepare_transfer_hardware = bcm63xx_spi_prepare_transfer; |
466 | master->unprepare_transfer_hardware = bcm63xx_spi_unprepare_transfer; | 463 | master->unprepare_transfer_hardware = bcm63xx_spi_unprepare_transfer; |
467 | master->transfer_one_message = bcm63xx_spi_transfer_one; | 464 | master->transfer_one_message = bcm63xx_spi_transfer_one; |
468 | master->mode_bits = MODEBITS; | 465 | master->mode_bits = MODEBITS; |
469 | bs->speed_hz = pdata->speed_hz; | 466 | bs->speed_hz = pdata->speed_hz; |
470 | bs->msg_type_shift = pdata->msg_type_shift; | 467 | bs->msg_type_shift = pdata->msg_type_shift; |
471 | bs->msg_ctl_width = pdata->msg_ctl_width; | 468 | bs->msg_ctl_width = pdata->msg_ctl_width; |
472 | bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA)); | 469 | bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA)); |
473 | bs->rx_io = (const u8 *)(bs->regs + bcm63xx_spireg(SPI_RX_DATA)); | 470 | bs->rx_io = (const u8 *)(bs->regs + bcm63xx_spireg(SPI_RX_DATA)); |
474 | 471 | ||
475 | switch (bs->msg_ctl_width) { | 472 | switch (bs->msg_ctl_width) { |
476 | case 8: | 473 | case 8: |
477 | case 16: | 474 | case 16: |
478 | break; | 475 | break; |
479 | default: | 476 | default: |
480 | dev_err(dev, "unsupported MSG_CTL width: %d\n", | 477 | dev_err(dev, "unsupported MSG_CTL width: %d\n", |
481 | bs->msg_ctl_width); | 478 | bs->msg_ctl_width); |
482 | goto out_err; | 479 | goto out_err; |
483 | } | 480 | } |
484 | 481 | ||
485 | /* Initialize hardware */ | 482 | /* Initialize hardware */ |
486 | clk_prepare_enable(bs->clk); | 483 | clk_prepare_enable(bs->clk); |
487 | bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); | 484 | bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); |
488 | 485 | ||
489 | /* register and we are done */ | 486 | /* register and we are done */ |
490 | ret = spi_register_master(master); | 487 | ret = spi_register_master(master); |
491 | if (ret) { | 488 | if (ret) { |
492 | dev_err(dev, "spi register failed\n"); | 489 | dev_err(dev, "spi register failed\n"); |
493 | goto out_clk_disable; | 490 | goto out_clk_disable; |
494 | } | 491 | } |
495 | 492 | ||
496 | dev_info(dev, "at 0x%08x (irq %d, FIFOs size %d)\n", | 493 | dev_info(dev, "at 0x%08x (irq %d, FIFOs size %d)\n", |
497 | r->start, irq, bs->fifo_size); | 494 | r->start, irq, bs->fifo_size); |
498 | 495 | ||
499 | return 0; | 496 | return 0; |
500 | 497 | ||
501 | out_clk_disable: | 498 | out_clk_disable: |
502 | clk_disable_unprepare(clk); | 499 | clk_disable_unprepare(clk); |
503 | out_err: | 500 | out_err: |
504 | platform_set_drvdata(pdev, NULL); | 501 | platform_set_drvdata(pdev, NULL); |
505 | spi_master_put(master); | 502 | spi_master_put(master); |
506 | out_clk: | 503 | out_clk: |
507 | clk_put(clk); | 504 | clk_put(clk); |
508 | out: | 505 | out: |
509 | return ret; | 506 | return ret; |
510 | } | 507 | } |
511 | 508 | ||
512 | static int bcm63xx_spi_remove(struct platform_device *pdev) | 509 | static int bcm63xx_spi_remove(struct platform_device *pdev) |
513 | { | 510 | { |
514 | struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); | 511 | struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); |
515 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); | 512 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); |
516 | 513 | ||
517 | spi_unregister_master(master); | 514 | spi_unregister_master(master); |
518 | 515 | ||
519 | /* reset spi block */ | 516 | /* reset spi block */ |
520 | bcm_spi_writeb(bs, 0, SPI_INT_MASK); | 517 | bcm_spi_writeb(bs, 0, SPI_INT_MASK); |
521 | 518 | ||
522 | /* HW shutdown */ | 519 | /* HW shutdown */ |
523 | clk_disable_unprepare(bs->clk); | 520 | clk_disable_unprepare(bs->clk); |
524 | clk_put(bs->clk); | 521 | clk_put(bs->clk); |
525 | 522 | ||
526 | platform_set_drvdata(pdev, 0); | 523 | platform_set_drvdata(pdev, 0); |
527 | 524 | ||
528 | spi_master_put(master); | 525 | spi_master_put(master); |
529 | 526 | ||
530 | return 0; | 527 | return 0; |
531 | } | 528 | } |
532 | 529 | ||
533 | #ifdef CONFIG_PM | 530 | #ifdef CONFIG_PM |
534 | static int bcm63xx_spi_suspend(struct device *dev) | 531 | static int bcm63xx_spi_suspend(struct device *dev) |
535 | { | 532 | { |
536 | struct spi_master *master = | 533 | struct spi_master *master = |
537 | platform_get_drvdata(to_platform_device(dev)); | 534 | platform_get_drvdata(to_platform_device(dev)); |
538 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); | 535 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); |
539 | 536 | ||
540 | spi_master_suspend(master); | 537 | spi_master_suspend(master); |
541 | 538 | ||
542 | clk_disable_unprepare(bs->clk); | 539 | clk_disable_unprepare(bs->clk); |
543 | 540 | ||
544 | return 0; | 541 | return 0; |
545 | } | 542 | } |
546 | 543 | ||
547 | static int bcm63xx_spi_resume(struct device *dev) | 544 | static int bcm63xx_spi_resume(struct device *dev) |
548 | { | 545 | { |
549 | struct spi_master *master = | 546 | struct spi_master *master = |
550 | platform_get_drvdata(to_platform_device(dev)); | 547 | platform_get_drvdata(to_platform_device(dev)); |
551 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); | 548 | struct bcm63xx_spi *bs = spi_master_get_devdata(master); |
552 | 549 | ||
553 | clk_prepare_enable(bs->clk); | 550 | clk_prepare_enable(bs->clk); |
554 | 551 | ||
555 | spi_master_resume(master); | 552 | spi_master_resume(master); |
556 | 553 | ||
557 | return 0; | 554 | return 0; |
558 | } | 555 | } |
559 | 556 | ||
560 | static const struct dev_pm_ops bcm63xx_spi_pm_ops = { | 557 | static const struct dev_pm_ops bcm63xx_spi_pm_ops = { |
561 | .suspend = bcm63xx_spi_suspend, | 558 | .suspend = bcm63xx_spi_suspend, |
562 | .resume = bcm63xx_spi_resume, | 559 | .resume = bcm63xx_spi_resume, |
563 | }; | 560 | }; |
564 | 561 | ||
565 | #define BCM63XX_SPI_PM_OPS (&bcm63xx_spi_pm_ops) | 562 | #define BCM63XX_SPI_PM_OPS (&bcm63xx_spi_pm_ops) |
566 | #else | 563 | #else |
567 | #define BCM63XX_SPI_PM_OPS NULL | 564 | #define BCM63XX_SPI_PM_OPS NULL |
568 | #endif | 565 | #endif |
569 | 566 | ||
570 | static struct platform_driver bcm63xx_spi_driver = { | 567 | static struct platform_driver bcm63xx_spi_driver = { |
571 | .driver = { | 568 | .driver = { |
572 | .name = "bcm63xx-spi", | 569 | .name = "bcm63xx-spi", |
573 | .owner = THIS_MODULE, | 570 | .owner = THIS_MODULE, |
574 | .pm = BCM63XX_SPI_PM_OPS, | 571 | .pm = BCM63XX_SPI_PM_OPS, |
575 | }, | 572 | }, |
576 | .probe = bcm63xx_spi_probe, | 573 | .probe = bcm63xx_spi_probe, |
577 | .remove = bcm63xx_spi_remove, | 574 | .remove = bcm63xx_spi_remove, |
578 | }; | 575 | }; |
579 | 576 | ||
580 | module_platform_driver(bcm63xx_spi_driver); | 577 | module_platform_driver(bcm63xx_spi_driver); |
581 | 578 | ||
582 | MODULE_ALIAS("platform:bcm63xx_spi"); | 579 | MODULE_ALIAS("platform:bcm63xx_spi"); |
583 | MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); | 580 | MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); |
584 | MODULE_AUTHOR("Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>"); | 581 | MODULE_AUTHOR("Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>"); |
585 | MODULE_DESCRIPTION("Broadcom BCM63xx SPI Controller driver"); | 582 | MODULE_DESCRIPTION("Broadcom BCM63xx SPI Controller driver"); |
586 | MODULE_LICENSE("GPL"); | 583 | MODULE_LICENSE("GPL"); |
587 | 584 |