Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
ca632f55 | 2 | /* |
7fba5340 | 3 | * Copyright (c) 2006 Ben Dooks |
bec0806c | 4 | * Copyright 2006-2009 Simtec Electronics |
7fba5340 | 5 | * Ben Dooks <ben@simtec.co.uk> |
7fba5340 BD |
6 | */ |
7 | ||
7fba5340 | 8 | #include <linux/spinlock.h> |
7fba5340 BD |
9 | #include <linux/interrupt.h> |
10 | #include <linux/delay.h> | |
11 | #include <linux/errno.h> | |
12 | #include <linux/err.h> | |
13 | #include <linux/clk.h> | |
14 | #include <linux/platform_device.h> | |
1a0c220f | 15 | #include <linux/io.h> |
5a0e3ad6 | 16 | #include <linux/slab.h> |
7fba5340 BD |
17 | |
18 | #include <linux/spi/spi.h> | |
19 | #include <linux/spi/spi_bitbang.h> | |
f35ef7ca | 20 | #include <linux/spi/s3c24xx.h> |
f131a444 | 21 | #include <linux/spi/s3c24xx-fiq.h> |
d7614de4 | 22 | #include <linux/module.h> |
7fba5340 | 23 | |
bec0806c BD |
24 | #include <asm/fiq.h> |
25 | ||
0144e3fc | 26 | #include "spi-s3c24xx-regs.h" |
bec0806c | 27 | |
570327d9 | 28 | /** |
e1cc0388 | 29 | * struct s3c24xx_spi_devstate - per device data |
570327d9 BD |
30 | * @hz: Last frequency calculated for @sppre field. |
31 | * @mode: Last mode setting for the @spcon field. | |
32 | * @spcon: Value to write to the SPCON register. | |
33 | * @sppre: Value to write to the SPPRE register. | |
34 | */ | |
35 | struct s3c24xx_spi_devstate { | |
36 | unsigned int hz; | |
37 | unsigned int mode; | |
38 | u8 spcon; | |
39 | u8 sppre; | |
40 | }; | |
41 | ||
bec0806c BD |
42 | enum spi_fiq_mode { |
43 | FIQ_MODE_NONE = 0, | |
44 | FIQ_MODE_TX = 1, | |
45 | FIQ_MODE_RX = 2, | |
46 | FIQ_MODE_TXRX = 3, | |
47 | }; | |
48 | ||
7fba5340 BD |
49 | struct s3c24xx_spi { |
50 | /* bitbang has to be first */ | |
51 | struct spi_bitbang bitbang; | |
52 | struct completion done; | |
53 | ||
54 | void __iomem *regs; | |
55 | int irq; | |
56 | int len; | |
57 | int count; | |
58 | ||
bec0806c BD |
59 | struct fiq_handler fiq_handler; |
60 | enum spi_fiq_mode fiq_mode; | |
61 | unsigned char fiq_inuse; | |
62 | unsigned char fiq_claimed; | |
63 | ||
7fba5340 BD |
64 | /* data buffers */ |
65 | const unsigned char *tx; | |
66 | unsigned char *rx; | |
67 | ||
68 | struct clk *clk; | |
7fba5340 BD |
69 | struct spi_master *master; |
70 | struct spi_device *curdev; | |
71 | struct device *dev; | |
72 | struct s3c2410_spi_info *pdata; | |
73 | }; | |
74 | ||
75 | #define SPCON_DEFAULT (S3C2410_SPCON_MSTR | S3C2410_SPCON_SMOD_INT) | |
76 | #define SPPIN_DEFAULT (S3C2410_SPPIN_KEEP) | |
77 | ||
78 | static inline struct s3c24xx_spi *to_hw(struct spi_device *sdev) | |
79 | { | |
80 | return spi_master_get_devdata(sdev->master); | |
81 | } | |
82 | ||
83 | static void s3c24xx_spi_chipsel(struct spi_device *spi, int value) | |
84 | { | |
570327d9 | 85 | struct s3c24xx_spi_devstate *cs = spi->controller_state; |
7fba5340 | 86 | struct s3c24xx_spi *hw = to_hw(spi); |
570327d9 BD |
87 | |
88 | /* change the chipselect state and the state of the spi engine clock */ | |
7fba5340 BD |
89 | |
90 | switch (value) { | |
91 | case BITBANG_CS_INACTIVE: | |
570327d9 | 92 | writeb(cs->spcon, hw->regs + S3C2410_SPCON); |
7fba5340 BD |
93 | break; |
94 | ||
95 | case BITBANG_CS_ACTIVE: | |
570327d9 BD |
96 | writeb(cs->spcon | S3C2410_SPCON_ENSCK, |
97 | hw->regs + S3C2410_SPCON); | |
7fba5340 | 98 | break; |
7fba5340 BD |
99 | } |
100 | } | |
101 | ||
570327d9 BD |
102 | static int s3c24xx_spi_update_state(struct spi_device *spi, |
103 | struct spi_transfer *t) | |
7fba5340 BD |
104 | { |
105 | struct s3c24xx_spi *hw = to_hw(spi); | |
570327d9 | 106 | struct s3c24xx_spi_devstate *cs = spi->controller_state; |
7fba5340 BD |
107 | unsigned int hz; |
108 | unsigned int div; | |
b8978784 | 109 | unsigned long clk; |
7fba5340 | 110 | |
7fba5340 BD |
111 | hz = t ? t->speed_hz : spi->max_speed_hz; |
112 | ||
19152975 BD |
113 | if (!hz) |
114 | hz = spi->max_speed_hz; | |
115 | ||
570327d9 | 116 | if (spi->mode != cs->mode) { |
bec0806c | 117 | u8 spcon = SPCON_DEFAULT | S3C2410_SPCON_ENSCK; |
570327d9 BD |
118 | |
119 | if (spi->mode & SPI_CPHA) | |
120 | spcon |= S3C2410_SPCON_CPHA_FMTB; | |
7fba5340 | 121 | |
570327d9 BD |
122 | if (spi->mode & SPI_CPOL) |
123 | spcon |= S3C2410_SPCON_CPOL_HIGH; | |
7fba5340 | 124 | |
570327d9 BD |
125 | cs->mode = spi->mode; |
126 | cs->spcon = spcon; | |
127 | } | |
b8978784 | 128 | |
570327d9 BD |
129 | if (cs->hz != hz) { |
130 | clk = clk_get_rate(hw->clk); | |
131 | div = DIV_ROUND_UP(clk, hz * 2) - 1; | |
b8978784 | 132 | |
570327d9 BD |
133 | if (div > 255) |
134 | div = 255; | |
7fba5340 | 135 | |
570327d9 BD |
136 | dev_dbg(&spi->dev, "pre-scaler=%d (wanted %d, got %ld)\n", |
137 | div, hz, clk / (2 * (div + 1))); | |
138 | ||
139 | cs->hz = hz; | |
140 | cs->sppre = div; | |
7fba5340 | 141 | } |
7fba5340 BD |
142 | |
143 | return 0; | |
144 | } | |
145 | ||
570327d9 BD |
146 | static int s3c24xx_spi_setupxfer(struct spi_device *spi, |
147 | struct spi_transfer *t) | |
148 | { | |
149 | struct s3c24xx_spi_devstate *cs = spi->controller_state; | |
150 | struct s3c24xx_spi *hw = to_hw(spi); | |
151 | int ret; | |
152 | ||
153 | ret = s3c24xx_spi_update_state(spi, t); | |
154 | if (!ret) | |
155 | writeb(cs->sppre, hw->regs + S3C2410_SPPRE); | |
156 | ||
157 | return ret; | |
158 | } | |
159 | ||
7fba5340 BD |
160 | static int s3c24xx_spi_setup(struct spi_device *spi) |
161 | { | |
570327d9 BD |
162 | struct s3c24xx_spi_devstate *cs = spi->controller_state; |
163 | struct s3c24xx_spi *hw = to_hw(spi); | |
7fba5340 BD |
164 | int ret; |
165 | ||
570327d9 BD |
166 | /* allocate settings on the first call */ |
167 | if (!cs) { | |
c586feba AL |
168 | cs = devm_kzalloc(&spi->dev, |
169 | sizeof(struct s3c24xx_spi_devstate), | |
170 | GFP_KERNEL); | |
0375cff5 | 171 | if (!cs) |
570327d9 | 172 | return -ENOMEM; |
570327d9 BD |
173 | |
174 | cs->spcon = SPCON_DEFAULT; | |
175 | cs->hz = -1; | |
176 | spi->controller_state = cs; | |
177 | } | |
178 | ||
179 | /* initialise the state from the device */ | |
180 | ret = s3c24xx_spi_update_state(spi, NULL); | |
181 | if (ret) | |
7fba5340 | 182 | return ret; |
570327d9 | 183 | |
c15f6ed3 | 184 | mutex_lock(&hw->bitbang.lock); |
570327d9 BD |
185 | if (!hw->bitbang.busy) { |
186 | hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE); | |
187 | /* need to ndelay for 0.5 clocktick ? */ | |
7fba5340 | 188 | } |
c15f6ed3 | 189 | mutex_unlock(&hw->bitbang.lock); |
7fba5340 | 190 | |
7fba5340 BD |
191 | return 0; |
192 | } | |
193 | ||
194 | static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count) | |
195 | { | |
4b1badf5 | 196 | return hw->tx ? hw->tx[count] : 0; |
7fba5340 BD |
197 | } |
198 | ||
bec0806c BD |
199 | #ifdef CONFIG_SPI_S3C24XX_FIQ |
200 | /* Support for FIQ based pseudo-DMA to improve the transfer speed. | |
201 | * | |
202 | * This code uses the assembly helper in spi_s3c24xx_spi.S which is | |
203 | * used by the FIQ core to move data between main memory and the peripheral | |
204 | * block. Since this is code running on the processor, there is no problem | |
205 | * with cache coherency of the buffers, so we can use any buffer we like. | |
206 | */ | |
207 | ||
208 | /** | |
209 | * struct spi_fiq_code - FIQ code and header | |
210 | * @length: The length of the code fragment, excluding this header. | |
211 | * @ack_offset: The offset from @data to the word to place the IRQ ACK bit at. | |
212 | * @data: The code itself to install as a FIQ handler. | |
213 | */ | |
214 | struct spi_fiq_code { | |
215 | u32 length; | |
216 | u32 ack_offset; | |
2d4ccc2a | 217 | u8 data[]; |
bec0806c BD |
218 | }; |
219 | ||
bec0806c BD |
220 | /** |
221 | * s3c24xx_spi_tryfiq - attempt to claim and setup FIQ for transfer | |
222 | * @hw: The hardware state. | |
223 | * | |
224 | * Claim the FIQ handler (only one can be active at any one time) and | |
225 | * then setup the correct transfer code for this transfer. | |
226 | * | |
3ad2f3fb | 227 | * This call updates all the necessary state information if successful, |
bec0806c BD |
228 | * so the caller does not need to do anything more than start the transfer |
229 | * as normal, since the IRQ will have been re-routed to the FIQ handler. | |
230 | */ | |
cfeb3312 | 231 | static void s3c24xx_spi_tryfiq(struct s3c24xx_spi *hw) |
bec0806c BD |
232 | { |
233 | struct pt_regs regs; | |
234 | enum spi_fiq_mode mode; | |
235 | struct spi_fiq_code *code; | |
cd4bd8f9 | 236 | u32 *ack_ptr = NULL; |
bec0806c BD |
237 | int ret; |
238 | ||
239 | if (!hw->fiq_claimed) { | |
240 | /* try and claim fiq if we haven't got it, and if not | |
241 | * then return and simply use another transfer method */ | |
242 | ||
243 | ret = claim_fiq(&hw->fiq_handler); | |
244 | if (ret) | |
245 | return; | |
246 | } | |
247 | ||
248 | if (hw->tx && !hw->rx) | |
249 | mode = FIQ_MODE_TX; | |
250 | else if (hw->rx && !hw->tx) | |
251 | mode = FIQ_MODE_RX; | |
252 | else | |
253 | mode = FIQ_MODE_TXRX; | |
254 | ||
255 | regs.uregs[fiq_rspi] = (long)hw->regs; | |
256 | regs.uregs[fiq_rrx] = (long)hw->rx; | |
257 | regs.uregs[fiq_rtx] = (long)hw->tx + 1; | |
258 | regs.uregs[fiq_rcount] = hw->len - 1; | |
bec0806c BD |
259 | |
260 | set_fiq_regs(®s); | |
261 | ||
262 | if (hw->fiq_mode != mode) { | |
bec0806c BD |
263 | hw->fiq_mode = mode; |
264 | ||
265 | switch (mode) { | |
266 | case FIQ_MODE_TX: | |
267 | code = &s3c24xx_spi_fiq_tx; | |
268 | break; | |
269 | case FIQ_MODE_RX: | |
270 | code = &s3c24xx_spi_fiq_rx; | |
271 | break; | |
272 | case FIQ_MODE_TXRX: | |
273 | code = &s3c24xx_spi_fiq_txrx; | |
274 | break; | |
275 | default: | |
276 | code = NULL; | |
277 | } | |
278 | ||
279 | BUG_ON(!code); | |
280 | ||
281 | ack_ptr = (u32 *)&code->data[code->ack_offset]; | |
bec0806c BD |
282 | set_fiq_handler(&code->data, code->length); |
283 | } | |
284 | ||
cd4bd8f9 | 285 | s3c24xx_set_fiq(hw->irq, ack_ptr, true); |
bec0806c BD |
286 | |
287 | hw->fiq_mode = mode; | |
288 | hw->fiq_inuse = 1; | |
289 | } | |
290 | ||
291 | /** | |
292 | * s3c24xx_spi_fiqop - FIQ core code callback | |
293 | * @pw: Data registered with the handler | |
294 | * @release: Whether this is a release or a return. | |
295 | * | |
296 | * Called by the FIQ code when another module wants to use the FIQ, so | |
297 | * return whether we are currently using this or not and then update our | |
298 | * internal state. | |
299 | */ | |
300 | static int s3c24xx_spi_fiqop(void *pw, int release) | |
301 | { | |
302 | struct s3c24xx_spi *hw = pw; | |
303 | int ret = 0; | |
304 | ||
305 | if (release) { | |
306 | if (hw->fiq_inuse) | |
307 | ret = -EBUSY; | |
308 | ||
309 | /* note, we do not need to unroute the FIQ, as the FIQ | |
310 | * vector code de-routes it to signal the end of transfer */ | |
311 | ||
312 | hw->fiq_mode = FIQ_MODE_NONE; | |
313 | hw->fiq_claimed = 0; | |
314 | } else { | |
315 | hw->fiq_claimed = 1; | |
316 | } | |
317 | ||
318 | return ret; | |
319 | } | |
320 | ||
321 | /** | |
322 | * s3c24xx_spi_initfiq - setup the information for the FIQ core | |
323 | * @hw: The hardware state. | |
324 | * | |
325 | * Setup the fiq_handler block to pass to the FIQ core. | |
326 | */ | |
327 | static inline void s3c24xx_spi_initfiq(struct s3c24xx_spi *hw) | |
328 | { | |
329 | hw->fiq_handler.dev_id = hw; | |
330 | hw->fiq_handler.name = dev_name(hw->dev); | |
331 | hw->fiq_handler.fiq_op = s3c24xx_spi_fiqop; | |
332 | } | |
333 | ||
334 | /** | |
335 | * s3c24xx_spi_usefiq - return if we should be using FIQ. | |
336 | * @hw: The hardware state. | |
337 | * | |
338 | * Return true if the platform data specifies whether this channel is | |
339 | * allowed to use the FIQ. | |
340 | */ | |
341 | static inline bool s3c24xx_spi_usefiq(struct s3c24xx_spi *hw) | |
342 | { | |
343 | return hw->pdata->use_fiq; | |
344 | } | |
345 | ||
346 | /** | |
347 | * s3c24xx_spi_usingfiq - return if channel is using FIQ | |
348 | * @spi: The hardware state. | |
349 | * | |
350 | * Return whether the channel is currently using the FIQ (separate from | |
351 | * whether the FIQ is claimed). | |
352 | */ | |
353 | static inline bool s3c24xx_spi_usingfiq(struct s3c24xx_spi *spi) | |
354 | { | |
355 | return spi->fiq_inuse; | |
356 | } | |
357 | #else | |
358 | ||
359 | static inline void s3c24xx_spi_initfiq(struct s3c24xx_spi *s) { } | |
360 | static inline void s3c24xx_spi_tryfiq(struct s3c24xx_spi *s) { } | |
361 | static inline bool s3c24xx_spi_usefiq(struct s3c24xx_spi *s) { return false; } | |
362 | static inline bool s3c24xx_spi_usingfiq(struct s3c24xx_spi *s) { return false; } | |
363 | ||
364 | #endif /* CONFIG_SPI_S3C24XX_FIQ */ | |
365 | ||
7fba5340 BD |
366 | static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t) |
367 | { | |
368 | struct s3c24xx_spi *hw = to_hw(spi); | |
369 | ||
7fba5340 BD |
370 | hw->tx = t->tx_buf; |
371 | hw->rx = t->rx_buf; | |
372 | hw->len = t->len; | |
373 | hw->count = 0; | |
374 | ||
4bb5eba0 BD |
375 | init_completion(&hw->done); |
376 | ||
bec0806c BD |
377 | hw->fiq_inuse = 0; |
378 | if (s3c24xx_spi_usefiq(hw) && t->len >= 3) | |
379 | s3c24xx_spi_tryfiq(hw); | |
380 | ||
7fba5340 BD |
381 | /* send the first byte */ |
382 | writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT); | |
4bb5eba0 | 383 | |
7fba5340 | 384 | wait_for_completion(&hw->done); |
7fba5340 BD |
385 | return hw->count; |
386 | } | |
387 | ||
7d12e780 | 388 | static irqreturn_t s3c24xx_spi_irq(int irq, void *dev) |
7fba5340 BD |
389 | { |
390 | struct s3c24xx_spi *hw = dev; | |
391 | unsigned int spsta = readb(hw->regs + S3C2410_SPSTA); | |
392 | unsigned int count = hw->count; | |
393 | ||
394 | if (spsta & S3C2410_SPSTA_DCOL) { | |
395 | dev_dbg(hw->dev, "data-collision\n"); | |
396 | complete(&hw->done); | |
397 | goto irq_done; | |
398 | } | |
399 | ||
400 | if (!(spsta & S3C2410_SPSTA_READY)) { | |
401 | dev_dbg(hw->dev, "spi not ready for tx?\n"); | |
402 | complete(&hw->done); | |
403 | goto irq_done; | |
404 | } | |
405 | ||
bec0806c BD |
406 | if (!s3c24xx_spi_usingfiq(hw)) { |
407 | hw->count++; | |
7fba5340 | 408 | |
bec0806c BD |
409 | if (hw->rx) |
410 | hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT); | |
7fba5340 | 411 | |
bec0806c BD |
412 | count++; |
413 | ||
414 | if (count < hw->len) | |
415 | writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT); | |
416 | else | |
417 | complete(&hw->done); | |
418 | } else { | |
419 | hw->count = hw->len; | |
420 | hw->fiq_inuse = 0; | |
421 | ||
422 | if (hw->rx) | |
423 | hw->rx[hw->len-1] = readb(hw->regs + S3C2410_SPRDAT); | |
7fba5340 | 424 | |
7fba5340 | 425 | complete(&hw->done); |
bec0806c | 426 | } |
7fba5340 BD |
427 | |
428 | irq_done: | |
429 | return IRQ_HANDLED; | |
430 | } | |
431 | ||
5aa6cf30 BD |
432 | static void s3c24xx_spi_initialsetup(struct s3c24xx_spi *hw) |
433 | { | |
434 | /* for the moment, permanently enable the clock */ | |
435 | ||
436 | clk_enable(hw->clk); | |
437 | ||
438 | /* program defaults into the registers */ | |
439 | ||
440 | writeb(0xff, hw->regs + S3C2410_SPPRE); | |
441 | writeb(SPPIN_DEFAULT, hw->regs + S3C2410_SPPIN); | |
442 | writeb(SPCON_DEFAULT, hw->regs + S3C2410_SPCON); | |
443 | } | |
444 | ||
fd4a319b | 445 | static int s3c24xx_spi_probe(struct platform_device *pdev) |
7fba5340 | 446 | { |
50f426b5 | 447 | struct s3c2410_spi_info *pdata; |
7fba5340 BD |
448 | struct s3c24xx_spi *hw; |
449 | struct spi_master *master; | |
7fba5340 | 450 | int err = 0; |
7fba5340 | 451 | |
a6bfc42f | 452 | master = devm_spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi)); |
7fba5340 BD |
453 | if (master == NULL) { |
454 | dev_err(&pdev->dev, "No memory for spi_master\n"); | |
c9f722e8 | 455 | return -ENOMEM; |
7fba5340 BD |
456 | } |
457 | ||
458 | hw = spi_master_get_devdata(master); | |
7fba5340 | 459 | |
94c69f76 | 460 | hw->master = master; |
8074cf06 | 461 | hw->pdata = pdata = dev_get_platdata(&pdev->dev); |
7fba5340 BD |
462 | hw->dev = &pdev->dev; |
463 | ||
50f426b5 | 464 | if (pdata == NULL) { |
7fba5340 | 465 | dev_err(&pdev->dev, "No platform data supplied\n"); |
a6bfc42f | 466 | return -ENOENT; |
7fba5340 BD |
467 | } |
468 | ||
469 | platform_set_drvdata(pdev, hw); | |
470 | init_completion(&hw->done); | |
471 | ||
bec0806c BD |
472 | /* initialise fiq handler */ |
473 | ||
474 | s3c24xx_spi_initfiq(hw); | |
475 | ||
d1e77806 BD |
476 | /* setup the master state. */ |
477 | ||
e7db06b5 DB |
478 | /* the spi->mode bits understood by this driver: */ |
479 | master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; | |
480 | ||
d1e77806 | 481 | master->num_chipselect = hw->pdata->num_cs; |
cb1d0a7a | 482 | master->bus_num = pdata->bus_num; |
08850fa9 | 483 | master->bits_per_word_mask = SPI_BPW_MASK(8); |
7f2a3cf4 LW |
484 | /* we need to call the local chipselect callback */ |
485 | master->flags = SPI_MASTER_GPIO_SS; | |
486 | master->use_gpio_descriptors = true; | |
d1e77806 | 487 | |
7fba5340 BD |
488 | /* setup the state for the bitbang driver */ |
489 | ||
490 | hw->bitbang.master = hw->master; | |
491 | hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer; | |
492 | hw->bitbang.chipselect = s3c24xx_spi_chipsel; | |
493 | hw->bitbang.txrx_bufs = s3c24xx_spi_txrx; | |
570327d9 BD |
494 | |
495 | hw->master->setup = s3c24xx_spi_setup; | |
7fba5340 BD |
496 | |
497 | dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang); | |
498 | ||
499 | /* find and map our resources */ | |
b38f1f9b | 500 | hw->regs = devm_platform_ioremap_resource(pdev, 0); |
a6bfc42f YY |
501 | if (IS_ERR(hw->regs)) |
502 | return PTR_ERR(hw->regs); | |
7fba5340 BD |
503 | |
504 | hw->irq = platform_get_irq(pdev, 0); | |
a6bfc42f YY |
505 | if (hw->irq < 0) |
506 | return -ENOENT; | |
7fba5340 | 507 | |
c9f722e8 JH |
508 | err = devm_request_irq(&pdev->dev, hw->irq, s3c24xx_spi_irq, 0, |
509 | pdev->name, hw); | |
7fba5340 BD |
510 | if (err) { |
511 | dev_err(&pdev->dev, "Cannot claim IRQ\n"); | |
a6bfc42f | 512 | return err; |
7fba5340 BD |
513 | } |
514 | ||
c9f722e8 | 515 | hw->clk = devm_clk_get(&pdev->dev, "spi"); |
7fba5340 BD |
516 | if (IS_ERR(hw->clk)) { |
517 | dev_err(&pdev->dev, "No clock for device\n"); | |
a6bfc42f | 518 | return PTR_ERR(hw->clk); |
7fba5340 BD |
519 | } |
520 | ||
ee9c1fbf BD |
521 | s3c24xx_spi_initialsetup(hw); |
522 | ||
7fba5340 BD |
523 | /* register our spi controller */ |
524 | ||
525 | err = spi_bitbang_start(&hw->bitbang); | |
526 | if (err) { | |
527 | dev_err(&pdev->dev, "Failed to register SPI master\n"); | |
528 | goto err_register; | |
529 | } | |
530 | ||
7fba5340 BD |
531 | return 0; |
532 | ||
533 | err_register: | |
534 | clk_disable(hw->clk); | |
7fba5340 | 535 | |
7fba5340 BD |
536 | return err; |
537 | } | |
538 | ||
fd4a319b | 539 | static int s3c24xx_spi_remove(struct platform_device *dev) |
7fba5340 BD |
540 | { |
541 | struct s3c24xx_spi *hw = platform_get_drvdata(dev); | |
542 | ||
c6e7b8cb | 543 | spi_bitbang_stop(&hw->bitbang); |
7fba5340 | 544 | clk_disable(hw->clk); |
7fba5340 BD |
545 | spi_master_put(hw->master); |
546 | return 0; | |
547 | } | |
548 | ||
549 | ||
550 | #ifdef CONFIG_PM | |
551 | ||
6d613207 | 552 | static int s3c24xx_spi_suspend(struct device *dev) |
7fba5340 | 553 | { |
a1216394 | 554 | struct s3c24xx_spi *hw = dev_get_drvdata(dev); |
38060371 AL |
555 | int ret; |
556 | ||
557 | ret = spi_master_suspend(hw->master); | |
558 | if (ret) | |
559 | return ret; | |
7fba5340 BD |
560 | |
561 | clk_disable(hw->clk); | |
562 | return 0; | |
563 | } | |
564 | ||
6d613207 | 565 | static int s3c24xx_spi_resume(struct device *dev) |
7fba5340 | 566 | { |
a1216394 | 567 | struct s3c24xx_spi *hw = dev_get_drvdata(dev); |
7fba5340 | 568 | |
5aa6cf30 | 569 | s3c24xx_spi_initialsetup(hw); |
38060371 | 570 | return spi_master_resume(hw->master); |
7fba5340 BD |
571 | } |
572 | ||
47145210 | 573 | static const struct dev_pm_ops s3c24xx_spi_pmops = { |
6d613207 BD |
574 | .suspend = s3c24xx_spi_suspend, |
575 | .resume = s3c24xx_spi_resume, | |
576 | }; | |
577 | ||
578 | #define S3C24XX_SPI_PMOPS &s3c24xx_spi_pmops | |
7fba5340 | 579 | #else |
6d613207 BD |
580 | #define S3C24XX_SPI_PMOPS NULL |
581 | #endif /* CONFIG_PM */ | |
7fba5340 | 582 | |
7e38c3c4 | 583 | MODULE_ALIAS("platform:s3c2410-spi"); |
42cde430 | 584 | static struct platform_driver s3c24xx_spi_driver = { |
940ab889 | 585 | .probe = s3c24xx_spi_probe, |
fd4a319b | 586 | .remove = s3c24xx_spi_remove, |
7fba5340 BD |
587 | .driver = { |
588 | .name = "s3c2410-spi", | |
6d613207 | 589 | .pm = S3C24XX_SPI_PMOPS, |
7fba5340 BD |
590 | }, |
591 | }; | |
940ab889 | 592 | module_platform_driver(s3c24xx_spi_driver); |
7fba5340 BD |
593 | |
594 | MODULE_DESCRIPTION("S3C24XX SPI Driver"); | |
595 | MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); | |
596 | MODULE_LICENSE("GPL"); |