diff options
author | Martin Sperl <kernel@martin.sperl.org> | 2015-09-11 07:22:04 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2015-10-07 06:42:57 -0400 |
commit | 1ea29b39f4c812ece2f936065a0a3d6fe44a263e (patch) | |
tree | d65f3f668362e63fbb729e0eb8e6fff1ba530c46 | |
parent | 541cf5de027b7e9afe3a8f0ce205d2ccc02ac3fe (diff) |
spi: bcm2835aux: add bcm2835 auxiliary spi device driver
The bcm2835 has 2 auxiliary spi bus masters spi1 and spi2.
This implements the driver to enable these devices.
The driver does not implement native chip-selects but uses
the aribtrary GPIO-chip-selects provided by the spi-chipselect.
Note that this driver relies on the fact that
the clock is implemented by the clk-bcm2835-aux driver,
which enables/disables the HW block when requesting/releasing
the clock.
Signed-off-by: Martin Sperl <kernel@martin.sperl.org>
Acked-by: Eric Anholt <eric@anholt.net>
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | drivers/spi/Kconfig | 11 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/spi-bcm2835aux.c | 493 |
3 files changed, 505 insertions, 0 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 4887f317ea58..26b860534fa7 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig | |||
@@ -88,6 +88,17 @@ config SPI_BCM2835 | |||
88 | is for the regular SPI controller. Slave mode operation is not also | 88 | is for the regular SPI controller. Slave mode operation is not also |
89 | not supported. | 89 | not supported. |
90 | 90 | ||
91 | config SPI_BCM2835AUX | ||
92 | tristate "BCM2835 SPI auxiliary controller" | ||
93 | depends on ARCH_BCM2835 || COMPILE_TEST | ||
94 | depends on GPIOLIB | ||
95 | help | ||
96 | This selects a driver for the Broadcom BCM2835 SPI aux master. | ||
97 | |||
98 | The BCM2835 contains two types of SPI master controller; the | ||
99 | "universal SPI master", and the regular SPI controller. | ||
100 | This driver is for the universal/auxiliary SPI controller. | ||
101 | |||
91 | config SPI_BFIN5XX | 102 | config SPI_BFIN5XX |
92 | tristate "SPI controller driver for ADI Blackfin5xx" | 103 | tristate "SPI controller driver for ADI Blackfin5xx" |
93 | depends on BLACKFIN && !BF60x | 104 | depends on BLACKFIN && !BF60x |
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 6a7f6f9d0d1c..31fb7fb2a0b6 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile | |||
@@ -15,6 +15,7 @@ obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o | |||
15 | obj-$(CONFIG_SPI_ATH79) += spi-ath79.o | 15 | obj-$(CONFIG_SPI_ATH79) += spi-ath79.o |
16 | obj-$(CONFIG_SPI_AU1550) += spi-au1550.o | 16 | obj-$(CONFIG_SPI_AU1550) += spi-au1550.o |
17 | obj-$(CONFIG_SPI_BCM2835) += spi-bcm2835.o | 17 | obj-$(CONFIG_SPI_BCM2835) += spi-bcm2835.o |
18 | obj-$(CONFIG_SPI_BCM2835AUX) += spi-bcm2835aux.o | ||
18 | obj-$(CONFIG_SPI_BCM53XX) += spi-bcm53xx.o | 19 | obj-$(CONFIG_SPI_BCM53XX) += spi-bcm53xx.o |
19 | obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o | 20 | obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o |
20 | obj-$(CONFIG_SPI_BCM63XX_HSSPI) += spi-bcm63xx-hsspi.o | 21 | obj-$(CONFIG_SPI_BCM63XX_HSSPI) += spi-bcm63xx-hsspi.o |
diff --git a/drivers/spi/spi-bcm2835aux.c b/drivers/spi/spi-bcm2835aux.c new file mode 100644 index 000000000000..f92b4a69fffa --- /dev/null +++ b/drivers/spi/spi-bcm2835aux.c | |||
@@ -0,0 +1,493 @@ | |||
1 | /* | ||
2 | * Driver for Broadcom BCM2835 auxiliary SPI Controllers | ||
3 | * | ||
4 | * the driver does not rely on the native chipselects at all | ||
5 | * but only uses the gpio type chipselects | ||
6 | * | ||
7 | * Based on: spi-bcm2835.c | ||
8 | * | ||
9 | * Copyright (C) 2015 Martin Sperl | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | */ | ||
21 | |||
22 | #include <linux/clk.h> | ||
23 | #include <linux/completion.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/err.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | #include <linux/io.h> | ||
28 | #include <linux/kernel.h> | ||
29 | #include <linux/module.h> | ||
30 | #include <linux/of.h> | ||
31 | #include <linux/of_address.h> | ||
32 | #include <linux/of_device.h> | ||
33 | #include <linux/of_gpio.h> | ||
34 | #include <linux/of_irq.h> | ||
35 | #include <linux/regmap.h> | ||
36 | #include <linux/spi/spi.h> | ||
37 | #include <linux/spinlock.h> | ||
38 | |||
39 | /* | ||
40 | * spi register defines | ||
41 | * | ||
42 | * note there is garbage in the "official" documentation, | ||
43 | * so some data is taken from the file: | ||
44 | * brcm_usrlib/dag/vmcsx/vcinclude/bcm2708_chip/aux_io.h | ||
45 | * inside of: | ||
46 | * http://www.broadcom.com/docs/support/videocore/Brcm_Android_ICS_Graphics_Stack.tar.gz | ||
47 | */ | ||
48 | |||
49 | /* SPI register offsets */ | ||
50 | #define BCM2835_AUX_SPI_CNTL0 0x00 | ||
51 | #define BCM2835_AUX_SPI_CNTL1 0x04 | ||
52 | #define BCM2835_AUX_SPI_STAT 0x08 | ||
53 | #define BCM2835_AUX_SPI_PEEK 0x0C | ||
54 | #define BCM2835_AUX_SPI_IO 0x20 | ||
55 | #define BCM2835_AUX_SPI_TXHOLD 0x30 | ||
56 | |||
57 | /* Bitfields in CNTL0 */ | ||
58 | #define BCM2835_AUX_SPI_CNTL0_SPEED 0xFFF00000 | ||
59 | #define BCM2835_AUX_SPI_CNTL0_SPEED_MAX 0xFFF | ||
60 | #define BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT 20 | ||
61 | #define BCM2835_AUX_SPI_CNTL0_CS 0x000E0000 | ||
62 | #define BCM2835_AUX_SPI_CNTL0_POSTINPUT 0x00010000 | ||
63 | #define BCM2835_AUX_SPI_CNTL0_VAR_CS 0x00008000 | ||
64 | #define BCM2835_AUX_SPI_CNTL0_VAR_WIDTH 0x00004000 | ||
65 | #define BCM2835_AUX_SPI_CNTL0_DOUTHOLD 0x00003000 | ||
66 | #define BCM2835_AUX_SPI_CNTL0_ENABLE 0x00000800 | ||
67 | #define BCM2835_AUX_SPI_CNTL0_CPHA_IN 0x00000400 | ||
68 | #define BCM2835_AUX_SPI_CNTL0_CLEARFIFO 0x00000200 | ||
69 | #define BCM2835_AUX_SPI_CNTL0_CPHA_OUT 0x00000100 | ||
70 | #define BCM2835_AUX_SPI_CNTL0_CPOL 0x00000080 | ||
71 | #define BCM2835_AUX_SPI_CNTL0_MSBF_OUT 0x00000040 | ||
72 | #define BCM2835_AUX_SPI_CNTL0_SHIFTLEN 0x0000003F | ||
73 | |||
74 | /* Bitfields in CNTL1 */ | ||
75 | #define BCM2835_AUX_SPI_CNTL1_CSHIGH 0x00000700 | ||
76 | #define BCM2835_AUX_SPI_CNTL1_IDLE 0x00000080 | ||
77 | #define BCM2835_AUX_SPI_CNTL1_TXEMPTY 0x00000040 | ||
78 | #define BCM2835_AUX_SPI_CNTL1_MSBF_IN 0x00000002 | ||
79 | #define BCM2835_AUX_SPI_CNTL1_KEEP_IN 0x00000001 | ||
80 | |||
81 | /* Bitfields in STAT */ | ||
82 | #define BCM2835_AUX_SPI_STAT_TX_LVL 0xFF000000 | ||
83 | #define BCM2835_AUX_SPI_STAT_RX_LVL 0x00FF0000 | ||
84 | #define BCM2835_AUX_SPI_STAT_TX_FULL 0x00000400 | ||
85 | #define BCM2835_AUX_SPI_STAT_TX_EMPTY 0x00000200 | ||
86 | #define BCM2835_AUX_SPI_STAT_RX_FULL 0x00000100 | ||
87 | #define BCM2835_AUX_SPI_STAT_RX_EMPTY 0x00000080 | ||
88 | #define BCM2835_AUX_SPI_STAT_BUSY 0x00000040 | ||
89 | #define BCM2835_AUX_SPI_STAT_BITCOUNT 0x0000003F | ||
90 | |||
91 | /* timeout values */ | ||
92 | #define BCM2835_AUX_SPI_POLLING_LIMIT_US 30 | ||
93 | #define BCM2835_AUX_SPI_POLLING_JIFFIES 2 | ||
94 | |||
95 | #define BCM2835_AUX_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \ | ||
96 | | SPI_NO_CS) | ||
97 | |||
98 | struct bcm2835aux_spi { | ||
99 | void __iomem *regs; | ||
100 | struct clk *clk; | ||
101 | int irq; | ||
102 | u32 cntl[2]; | ||
103 | const u8 *tx_buf; | ||
104 | u8 *rx_buf; | ||
105 | int tx_len; | ||
106 | int rx_len; | ||
107 | }; | ||
108 | |||
109 | static inline u32 bcm2835aux_rd(struct bcm2835aux_spi *bs, unsigned reg) | ||
110 | { | ||
111 | return readl(bs->regs + reg); | ||
112 | } | ||
113 | |||
114 | static inline void bcm2835aux_wr(struct bcm2835aux_spi *bs, unsigned reg, | ||
115 | u32 val) | ||
116 | { | ||
117 | writel(val, bs->regs + reg); | ||
118 | } | ||
119 | |||
120 | static inline void bcm2835aux_rd_fifo(struct bcm2835aux_spi *bs) | ||
121 | { | ||
122 | u32 data; | ||
123 | int i; | ||
124 | int count = min(bs->rx_len, 3); | ||
125 | |||
126 | data = bcm2835aux_rd(bs, BCM2835_AUX_SPI_IO); | ||
127 | if (bs->rx_buf) { | ||
128 | for (i = 0; i < count; i++) | ||
129 | *bs->rx_buf++ = (data >> (8 * (2 - i))) & 0xff; | ||
130 | } | ||
131 | bs->rx_len -= count; | ||
132 | } | ||
133 | |||
134 | static inline void bcm2835aux_wr_fifo(struct bcm2835aux_spi *bs) | ||
135 | { | ||
136 | u32 data; | ||
137 | u8 byte; | ||
138 | int count; | ||
139 | int i; | ||
140 | |||
141 | /* gather up to 3 bytes to write to the FIFO */ | ||
142 | count = min(bs->tx_len, 3); | ||
143 | data = 0; | ||
144 | for (i = 0; i < count; i++) { | ||
145 | byte = bs->tx_buf ? *bs->tx_buf++ : 0; | ||
146 | data |= byte << (8 * (2 - i)); | ||
147 | } | ||
148 | |||
149 | /* and set the variable bit-length */ | ||
150 | data |= (count * 8) << 24; | ||
151 | |||
152 | /* and decrement length */ | ||
153 | bs->tx_len -= count; | ||
154 | |||
155 | /* write to the correct TX-register */ | ||
156 | if (bs->tx_len) | ||
157 | bcm2835aux_wr(bs, BCM2835_AUX_SPI_TXHOLD, data); | ||
158 | else | ||
159 | bcm2835aux_wr(bs, BCM2835_AUX_SPI_IO, data); | ||
160 | } | ||
161 | |||
162 | static void bcm2835aux_spi_reset_hw(struct bcm2835aux_spi *bs) | ||
163 | { | ||
164 | /* disable spi clearing fifo and interrupts */ | ||
165 | bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, 0); | ||
166 | bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL0, | ||
167 | BCM2835_AUX_SPI_CNTL0_CLEARFIFO); | ||
168 | } | ||
169 | |||
170 | static irqreturn_t bcm2835aux_spi_interrupt(int irq, void *dev_id) | ||
171 | { | ||
172 | struct spi_master *master = dev_id; | ||
173 | struct bcm2835aux_spi *bs = spi_master_get_devdata(master); | ||
174 | irqreturn_t ret = IRQ_NONE; | ||
175 | |||
176 | /* check if we have data to read */ | ||
177 | while (bs->rx_len && | ||
178 | (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT) & | ||
179 | BCM2835_AUX_SPI_STAT_RX_EMPTY))) { | ||
180 | bcm2835aux_rd_fifo(bs); | ||
181 | ret = IRQ_HANDLED; | ||
182 | } | ||
183 | |||
184 | /* check if we have data to write */ | ||
185 | while (bs->tx_len && | ||
186 | (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT) & | ||
187 | BCM2835_AUX_SPI_STAT_TX_FULL))) { | ||
188 | bcm2835aux_wr_fifo(bs); | ||
189 | ret = IRQ_HANDLED; | ||
190 | } | ||
191 | |||
192 | /* and check if we have reached "done" */ | ||
193 | while (bs->rx_len && | ||
194 | (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT) & | ||
195 | BCM2835_AUX_SPI_STAT_BUSY))) { | ||
196 | bcm2835aux_rd_fifo(bs); | ||
197 | ret = IRQ_HANDLED; | ||
198 | } | ||
199 | |||
200 | /* and if rx_len is 0 then wake up completion and disable spi */ | ||
201 | if (!bs->rx_len) { | ||
202 | bcm2835aux_spi_reset_hw(bs); | ||
203 | complete(&master->xfer_completion); | ||
204 | } | ||
205 | |||
206 | /* and return */ | ||
207 | return ret; | ||
208 | } | ||
209 | |||
210 | static int __bcm2835aux_spi_transfer_one_irq(struct spi_master *master, | ||
211 | struct spi_device *spi, | ||
212 | struct spi_transfer *tfr) | ||
213 | { | ||
214 | struct bcm2835aux_spi *bs = spi_master_get_devdata(master); | ||
215 | |||
216 | /* enable interrupts */ | ||
217 | bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, bs->cntl[1] | | ||
218 | BCM2835_AUX_SPI_CNTL1_TXEMPTY | | ||
219 | BCM2835_AUX_SPI_CNTL1_IDLE); | ||
220 | |||
221 | /* and wait for finish... */ | ||
222 | return 1; | ||
223 | } | ||
224 | |||
225 | static int bcm2835aux_spi_transfer_one_irq(struct spi_master *master, | ||
226 | struct spi_device *spi, | ||
227 | struct spi_transfer *tfr) | ||
228 | { | ||
229 | struct bcm2835aux_spi *bs = spi_master_get_devdata(master); | ||
230 | |||
231 | /* fill in registers and fifos before enabling interrupts */ | ||
232 | bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, bs->cntl[1]); | ||
233 | bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL0, bs->cntl[0]); | ||
234 | |||
235 | /* fill in tx fifo with data before enabling interrupts */ | ||
236 | while ((bs->tx_len) && | ||
237 | (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT) & | ||
238 | BCM2835_AUX_SPI_STAT_TX_FULL))) { | ||
239 | bcm2835aux_wr_fifo(bs); | ||
240 | } | ||
241 | |||
242 | /* now run the interrupt mode */ | ||
243 | return __bcm2835aux_spi_transfer_one_irq(master, spi, tfr); | ||
244 | } | ||
245 | |||
246 | static int bcm2835aux_spi_transfer_one_poll(struct spi_master *master, | ||
247 | struct spi_device *spi, | ||
248 | struct spi_transfer *tfr, | ||
249 | unsigned long xfer_time_us) | ||
250 | { | ||
251 | struct bcm2835aux_spi *bs = spi_master_get_devdata(master); | ||
252 | unsigned long timeout; | ||
253 | u32 stat; | ||
254 | |||
255 | /* configure spi */ | ||
256 | bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, bs->cntl[1]); | ||
257 | bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL0, bs->cntl[0]); | ||
258 | |||
259 | /* set the timeout */ | ||
260 | timeout = jiffies + BCM2835_AUX_SPI_POLLING_JIFFIES; | ||
261 | |||
262 | /* loop until finished the transfer */ | ||
263 | while (bs->rx_len) { | ||
264 | /* read status */ | ||
265 | stat = bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT); | ||
266 | |||
267 | /* fill in tx fifo with remaining data */ | ||
268 | if ((bs->tx_len) && (!(stat & BCM2835_AUX_SPI_STAT_TX_FULL))) { | ||
269 | bcm2835aux_wr_fifo(bs); | ||
270 | continue; | ||
271 | } | ||
272 | |||
273 | /* read data from fifo for both cases */ | ||
274 | if (!(stat & BCM2835_AUX_SPI_STAT_RX_EMPTY)) { | ||
275 | bcm2835aux_rd_fifo(bs); | ||
276 | continue; | ||
277 | } | ||
278 | if (!(stat & BCM2835_AUX_SPI_STAT_BUSY)) { | ||
279 | bcm2835aux_rd_fifo(bs); | ||
280 | continue; | ||
281 | } | ||
282 | |||
283 | /* there is still data pending to read check the timeout */ | ||
284 | if (bs->rx_len && time_after(jiffies, timeout)) { | ||
285 | dev_dbg_ratelimited(&spi->dev, | ||
286 | "timeout period reached: jiffies: %lu remaining tx/rx: %d/%d - falling back to interrupt mode\n", | ||
287 | jiffies - timeout, | ||
288 | bs->tx_len, bs->rx_len); | ||
289 | /* forward to interrupt handler */ | ||
290 | return __bcm2835aux_spi_transfer_one_irq(master, | ||
291 | spi, tfr); | ||
292 | } | ||
293 | } | ||
294 | |||
295 | /* Transfer complete - reset SPI HW */ | ||
296 | bcm2835aux_spi_reset_hw(bs); | ||
297 | |||
298 | /* and return without waiting for completion */ | ||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | static int bcm2835aux_spi_transfer_one(struct spi_master *master, | ||
303 | struct spi_device *spi, | ||
304 | struct spi_transfer *tfr) | ||
305 | { | ||
306 | struct bcm2835aux_spi *bs = spi_master_get_devdata(master); | ||
307 | unsigned long spi_hz, clk_hz, speed; | ||
308 | unsigned long spi_used_hz, xfer_time_us; | ||
309 | |||
310 | /* calculate the registers to handle | ||
311 | * | ||
312 | * note that we use the variable data mode, which | ||
313 | * is not optimal for longer transfers as we waste registers | ||
314 | * resulting (potentially) in more interrupts when transferring | ||
315 | * more than 12 bytes | ||
316 | */ | ||
317 | bs->cntl[0] = BCM2835_AUX_SPI_CNTL0_ENABLE | | ||
318 | BCM2835_AUX_SPI_CNTL0_VAR_WIDTH | | ||
319 | BCM2835_AUX_SPI_CNTL0_MSBF_OUT; | ||
320 | bs->cntl[1] = BCM2835_AUX_SPI_CNTL1_MSBF_IN; | ||
321 | |||
322 | /* set clock */ | ||
323 | spi_hz = tfr->speed_hz; | ||
324 | clk_hz = clk_get_rate(bs->clk); | ||
325 | |||
326 | if (spi_hz >= clk_hz / 2) { | ||
327 | speed = 0; | ||
328 | } else if (spi_hz) { | ||
329 | speed = DIV_ROUND_UP(clk_hz, 2 * spi_hz) - 1; | ||
330 | if (speed > BCM2835_AUX_SPI_CNTL0_SPEED_MAX) | ||
331 | speed = BCM2835_AUX_SPI_CNTL0_SPEED_MAX; | ||
332 | } else { /* the slowest we can go */ | ||
333 | speed = BCM2835_AUX_SPI_CNTL0_SPEED_MAX; | ||
334 | } | ||
335 | bs->cntl[0] |= speed << BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT; | ||
336 | |||
337 | spi_used_hz = clk_hz / (2 * (speed + 1)); | ||
338 | |||
339 | /* handle all the modes */ | ||
340 | if (spi->mode & SPI_CPOL) | ||
341 | bs->cntl[0] |= BCM2835_AUX_SPI_CNTL0_CPOL; | ||
342 | if (spi->mode & SPI_CPHA) | ||
343 | bs->cntl[0] |= BCM2835_AUX_SPI_CNTL0_CPHA_OUT | | ||
344 | BCM2835_AUX_SPI_CNTL0_CPHA_IN; | ||
345 | |||
346 | /* set transmit buffers and length */ | ||
347 | bs->tx_buf = tfr->tx_buf; | ||
348 | bs->rx_buf = tfr->rx_buf; | ||
349 | bs->tx_len = tfr->len; | ||
350 | bs->rx_len = tfr->len; | ||
351 | |||
352 | /* calculate the estimated time in us the transfer runs */ | ||
353 | xfer_time_us = tfr->len | ||
354 | * 9 /* clocks/byte - SPI-HW waits 1 clock after each byte */ | ||
355 | * 1000000 / spi_used_hz; | ||
356 | |||
357 | /* run in polling mode for short transfers */ | ||
358 | if (xfer_time_us < BCM2835_AUX_SPI_POLLING_LIMIT_US) | ||
359 | return bcm2835aux_spi_transfer_one_poll(master, spi, tfr, | ||
360 | xfer_time_us); | ||
361 | |||
362 | /* run in interrupt mode for all others */ | ||
363 | return bcm2835aux_spi_transfer_one_irq(master, spi, tfr); | ||
364 | } | ||
365 | |||
366 | static void bcm2835aux_spi_handle_err(struct spi_master *master, | ||
367 | struct spi_message *msg) | ||
368 | { | ||
369 | struct bcm2835aux_spi *bs = spi_master_get_devdata(master); | ||
370 | |||
371 | bcm2835aux_spi_reset_hw(bs); | ||
372 | } | ||
373 | |||
374 | static int bcm2835aux_spi_probe(struct platform_device *pdev) | ||
375 | { | ||
376 | struct spi_master *master; | ||
377 | struct bcm2835aux_spi *bs; | ||
378 | struct resource *res; | ||
379 | unsigned long clk_hz; | ||
380 | int err; | ||
381 | |||
382 | master = spi_alloc_master(&pdev->dev, sizeof(*bs)); | ||
383 | if (!master) { | ||
384 | dev_err(&pdev->dev, "spi_alloc_master() failed\n"); | ||
385 | return -ENOMEM; | ||
386 | } | ||
387 | |||
388 | platform_set_drvdata(pdev, master); | ||
389 | master->mode_bits = BCM2835_AUX_SPI_MODE_BITS; | ||
390 | master->bits_per_word_mask = SPI_BPW_MASK(8); | ||
391 | master->num_chipselect = -1; | ||
392 | master->transfer_one = bcm2835aux_spi_transfer_one; | ||
393 | master->handle_err = bcm2835aux_spi_handle_err; | ||
394 | master->dev.of_node = pdev->dev.of_node; | ||
395 | |||
396 | bs = spi_master_get_devdata(master); | ||
397 | |||
398 | /* the main area */ | ||
399 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
400 | bs->regs = devm_ioremap_resource(&pdev->dev, res); | ||
401 | if (IS_ERR(bs->regs)) { | ||
402 | err = PTR_ERR(bs->regs); | ||
403 | goto out_master_put; | ||
404 | } | ||
405 | |||
406 | bs->clk = devm_clk_get(&pdev->dev, NULL); | ||
407 | if ((!bs->clk) || (IS_ERR(bs->clk))) { | ||
408 | err = PTR_ERR(bs->clk); | ||
409 | dev_err(&pdev->dev, "could not get clk: %d\n", err); | ||
410 | goto out_master_put; | ||
411 | } | ||
412 | |||
413 | bs->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); | ||
414 | if (bs->irq <= 0) { | ||
415 | dev_err(&pdev->dev, "could not get IRQ: %d\n", bs->irq); | ||
416 | err = bs->irq ? bs->irq : -ENODEV; | ||
417 | goto out_master_put; | ||
418 | } | ||
419 | |||
420 | /* this also enables the HW block */ | ||
421 | err = clk_prepare_enable(bs->clk); | ||
422 | if (err) { | ||
423 | dev_err(&pdev->dev, "could not prepare clock: %d\n", err); | ||
424 | goto out_master_put; | ||
425 | } | ||
426 | |||
427 | /* just checking if the clock returns a sane value */ | ||
428 | clk_hz = clk_get_rate(bs->clk); | ||
429 | if (!clk_hz) { | ||
430 | dev_err(&pdev->dev, "clock returns 0 Hz\n"); | ||
431 | err = -ENODEV; | ||
432 | goto out_clk_disable; | ||
433 | } | ||
434 | |||
435 | err = devm_request_irq(&pdev->dev, bs->irq, | ||
436 | bcm2835aux_spi_interrupt, | ||
437 | IRQF_SHARED, | ||
438 | dev_name(&pdev->dev), master); | ||
439 | if (err) { | ||
440 | dev_err(&pdev->dev, "could not request IRQ: %d\n", err); | ||
441 | goto out_clk_disable; | ||
442 | } | ||
443 | |||
444 | /* reset SPI-HW block */ | ||
445 | bcm2835aux_spi_reset_hw(bs); | ||
446 | |||
447 | err = devm_spi_register_master(&pdev->dev, master); | ||
448 | if (err) { | ||
449 | dev_err(&pdev->dev, "could not register SPI master: %d\n", err); | ||
450 | goto out_clk_disable; | ||
451 | } | ||
452 | |||
453 | return 0; | ||
454 | |||
455 | out_clk_disable: | ||
456 | clk_disable_unprepare(bs->clk); | ||
457 | out_master_put: | ||
458 | spi_master_put(master); | ||
459 | return err; | ||
460 | } | ||
461 | |||
462 | static int bcm2835aux_spi_remove(struct platform_device *pdev) | ||
463 | { | ||
464 | struct spi_master *master = platform_get_drvdata(pdev); | ||
465 | struct bcm2835aux_spi *bs = spi_master_get_devdata(master); | ||
466 | |||
467 | bcm2835aux_spi_reset_hw(bs); | ||
468 | |||
469 | /* disable the HW block by releasing the clock */ | ||
470 | clk_disable_unprepare(bs->clk); | ||
471 | |||
472 | return 0; | ||
473 | } | ||
474 | |||
475 | static const struct of_device_id bcm2835aux_spi_match[] = { | ||
476 | { .compatible = "brcm,bcm2835-aux-spi", }, | ||
477 | {} | ||
478 | }; | ||
479 | MODULE_DEVICE_TABLE(of, bcm2835aux_spi_match); | ||
480 | |||
481 | static struct platform_driver bcm2835aux_spi_driver = { | ||
482 | .driver = { | ||
483 | .name = "spi-bcm2835aux", | ||
484 | .of_match_table = bcm2835aux_spi_match, | ||
485 | }, | ||
486 | .probe = bcm2835aux_spi_probe, | ||
487 | .remove = bcm2835aux_spi_remove, | ||
488 | }; | ||
489 | module_platform_driver(bcm2835aux_spi_driver); | ||
490 | |||
491 | MODULE_DESCRIPTION("SPI controller driver for Broadcom BCM2835 aux"); | ||
492 | MODULE_AUTHOR("Martin Sperl <kernel@martin.sperl.org>"); | ||
493 | MODULE_LICENSE("GPL v2"); | ||