diff options
Diffstat (limited to 'drivers/spi/spi_s3c24xx.c')
-rw-r--r-- | drivers/spi/spi_s3c24xx.c | 159 |
1 files changed, 106 insertions, 53 deletions
diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c index 3f3119d760db..33d94f76b9ef 100644 --- a/drivers/spi/spi_s3c24xx.c +++ b/drivers/spi/spi_s3c24xx.c | |||
@@ -20,17 +20,28 @@ | |||
20 | #include <linux/clk.h> | 20 | #include <linux/clk.h> |
21 | #include <linux/platform_device.h> | 21 | #include <linux/platform_device.h> |
22 | #include <linux/gpio.h> | 22 | #include <linux/gpio.h> |
23 | #include <linux/io.h> | ||
23 | 24 | ||
24 | #include <linux/spi/spi.h> | 25 | #include <linux/spi/spi.h> |
25 | #include <linux/spi/spi_bitbang.h> | 26 | #include <linux/spi/spi_bitbang.h> |
26 | 27 | ||
27 | #include <asm/io.h> | ||
28 | #include <asm/dma.h> | ||
29 | #include <mach/hardware.h> | ||
30 | |||
31 | #include <plat/regs-spi.h> | 28 | #include <plat/regs-spi.h> |
32 | #include <mach/spi.h> | 29 | #include <mach/spi.h> |
33 | 30 | ||
31 | /** | ||
32 | * s3c24xx_spi_devstate - per device data | ||
33 | * @hz: Last frequency calculated for @sppre field. | ||
34 | * @mode: Last mode setting for the @spcon field. | ||
35 | * @spcon: Value to write to the SPCON register. | ||
36 | * @sppre: Value to write to the SPPRE register. | ||
37 | */ | ||
38 | struct s3c24xx_spi_devstate { | ||
39 | unsigned int hz; | ||
40 | unsigned int mode; | ||
41 | u8 spcon; | ||
42 | u8 sppre; | ||
43 | }; | ||
44 | |||
34 | struct s3c24xx_spi { | 45 | struct s3c24xx_spi { |
35 | /* bitbang has to be first */ | 46 | /* bitbang has to be first */ |
36 | struct spi_bitbang bitbang; | 47 | struct spi_bitbang bitbang; |
@@ -71,43 +82,31 @@ static void s3c24xx_spi_gpiocs(struct s3c2410_spi_info *spi, int cs, int pol) | |||
71 | 82 | ||
72 | static void s3c24xx_spi_chipsel(struct spi_device *spi, int value) | 83 | static void s3c24xx_spi_chipsel(struct spi_device *spi, int value) |
73 | { | 84 | { |
85 | struct s3c24xx_spi_devstate *cs = spi->controller_state; | ||
74 | struct s3c24xx_spi *hw = to_hw(spi); | 86 | struct s3c24xx_spi *hw = to_hw(spi); |
75 | unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0; | 87 | unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0; |
76 | unsigned int spcon; | 88 | |
89 | /* change the chipselect state and the state of the spi engine clock */ | ||
77 | 90 | ||
78 | switch (value) { | 91 | switch (value) { |
79 | case BITBANG_CS_INACTIVE: | 92 | case BITBANG_CS_INACTIVE: |
80 | hw->set_cs(hw->pdata, spi->chip_select, cspol^1); | 93 | hw->set_cs(hw->pdata, spi->chip_select, cspol^1); |
94 | writeb(cs->spcon, hw->regs + S3C2410_SPCON); | ||
81 | break; | 95 | break; |
82 | 96 | ||
83 | case BITBANG_CS_ACTIVE: | 97 | case BITBANG_CS_ACTIVE: |
84 | spcon = readb(hw->regs + S3C2410_SPCON); | 98 | writeb(cs->spcon | S3C2410_SPCON_ENSCK, |
85 | 99 | hw->regs + S3C2410_SPCON); | |
86 | if (spi->mode & SPI_CPHA) | ||
87 | spcon |= S3C2410_SPCON_CPHA_FMTB; | ||
88 | else | ||
89 | spcon &= ~S3C2410_SPCON_CPHA_FMTB; | ||
90 | |||
91 | if (spi->mode & SPI_CPOL) | ||
92 | spcon |= S3C2410_SPCON_CPOL_HIGH; | ||
93 | else | ||
94 | spcon &= ~S3C2410_SPCON_CPOL_HIGH; | ||
95 | |||
96 | spcon |= S3C2410_SPCON_ENSCK; | ||
97 | |||
98 | /* write new configration */ | ||
99 | |||
100 | writeb(spcon, hw->regs + S3C2410_SPCON); | ||
101 | hw->set_cs(hw->pdata, spi->chip_select, cspol); | 100 | hw->set_cs(hw->pdata, spi->chip_select, cspol); |
102 | |||
103 | break; | 101 | break; |
104 | } | 102 | } |
105 | } | 103 | } |
106 | 104 | ||
107 | static int s3c24xx_spi_setupxfer(struct spi_device *spi, | 105 | static int s3c24xx_spi_update_state(struct spi_device *spi, |
108 | struct spi_transfer *t) | 106 | struct spi_transfer *t) |
109 | { | 107 | { |
110 | struct s3c24xx_spi *hw = to_hw(spi); | 108 | struct s3c24xx_spi *hw = to_hw(spi); |
109 | struct s3c24xx_spi_devstate *cs = spi->controller_state; | ||
111 | unsigned int bpw; | 110 | unsigned int bpw; |
112 | unsigned int hz; | 111 | unsigned int hz; |
113 | unsigned int div; | 112 | unsigned int div; |
@@ -127,41 +126,89 @@ static int s3c24xx_spi_setupxfer(struct spi_device *spi, | |||
127 | return -EINVAL; | 126 | return -EINVAL; |
128 | } | 127 | } |
129 | 128 | ||
130 | clk = clk_get_rate(hw->clk); | 129 | if (spi->mode != cs->mode) { |
131 | div = DIV_ROUND_UP(clk, hz * 2) - 1; | 130 | u8 spcon = SPCON_DEFAULT; |
132 | 131 | ||
133 | if (div > 255) | 132 | if (spi->mode & SPI_CPHA) |
134 | div = 255; | 133 | spcon |= S3C2410_SPCON_CPHA_FMTB; |
135 | 134 | ||
136 | dev_dbg(&spi->dev, "setting pre-scaler to %d (wanted %d, got %ld)\n", | 135 | if (spi->mode & SPI_CPOL) |
137 | div, hz, clk / (2 * (div + 1))); | 136 | spcon |= S3C2410_SPCON_CPOL_HIGH; |
138 | 137 | ||
138 | cs->mode = spi->mode; | ||
139 | cs->spcon = spcon; | ||
140 | } | ||
139 | 141 | ||
140 | writeb(div, hw->regs + S3C2410_SPPRE); | 142 | if (cs->hz != hz) { |
143 | clk = clk_get_rate(hw->clk); | ||
144 | div = DIV_ROUND_UP(clk, hz * 2) - 1; | ||
141 | 145 | ||
142 | spin_lock(&hw->bitbang.lock); | 146 | if (div > 255) |
143 | if (!hw->bitbang.busy) { | 147 | div = 255; |
144 | hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE); | 148 | |
145 | /* need to ndelay for 0.5 clocktick ? */ | 149 | dev_dbg(&spi->dev, "pre-scaler=%d (wanted %d, got %ld)\n", |
150 | div, hz, clk / (2 * (div + 1))); | ||
151 | |||
152 | cs->hz = hz; | ||
153 | cs->sppre = div; | ||
146 | } | 154 | } |
147 | spin_unlock(&hw->bitbang.lock); | ||
148 | 155 | ||
149 | return 0; | 156 | return 0; |
150 | } | 157 | } |
151 | 158 | ||
159 | static int s3c24xx_spi_setupxfer(struct spi_device *spi, | ||
160 | struct spi_transfer *t) | ||
161 | { | ||
162 | struct s3c24xx_spi_devstate *cs = spi->controller_state; | ||
163 | struct s3c24xx_spi *hw = to_hw(spi); | ||
164 | int ret; | ||
165 | |||
166 | ret = s3c24xx_spi_update_state(spi, t); | ||
167 | if (!ret) | ||
168 | writeb(cs->sppre, hw->regs + S3C2410_SPPRE); | ||
169 | |||
170 | return ret; | ||
171 | } | ||
172 | |||
152 | static int s3c24xx_spi_setup(struct spi_device *spi) | 173 | static int s3c24xx_spi_setup(struct spi_device *spi) |
153 | { | 174 | { |
175 | struct s3c24xx_spi_devstate *cs = spi->controller_state; | ||
176 | struct s3c24xx_spi *hw = to_hw(spi); | ||
154 | int ret; | 177 | int ret; |
155 | 178 | ||
156 | ret = s3c24xx_spi_setupxfer(spi, NULL); | 179 | /* allocate settings on the first call */ |
157 | if (ret < 0) { | 180 | if (!cs) { |
158 | dev_err(&spi->dev, "setupxfer returned %d\n", ret); | 181 | cs = kzalloc(sizeof(struct s3c24xx_spi_devstate), GFP_KERNEL); |
182 | if (!cs) { | ||
183 | dev_err(&spi->dev, "no memory for controller state\n"); | ||
184 | return -ENOMEM; | ||
185 | } | ||
186 | |||
187 | cs->spcon = SPCON_DEFAULT; | ||
188 | cs->hz = -1; | ||
189 | spi->controller_state = cs; | ||
190 | } | ||
191 | |||
192 | /* initialise the state from the device */ | ||
193 | ret = s3c24xx_spi_update_state(spi, NULL); | ||
194 | if (ret) | ||
159 | return ret; | 195 | return ret; |
196 | |||
197 | spin_lock(&hw->bitbang.lock); | ||
198 | if (!hw->bitbang.busy) { | ||
199 | hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE); | ||
200 | /* need to ndelay for 0.5 clocktick ? */ | ||
160 | } | 201 | } |
202 | spin_unlock(&hw->bitbang.lock); | ||
161 | 203 | ||
162 | return 0; | 204 | return 0; |
163 | } | 205 | } |
164 | 206 | ||
207 | static void s3c24xx_spi_cleanup(struct spi_device *spi) | ||
208 | { | ||
209 | kfree(spi->controller_state); | ||
210 | } | ||
211 | |||
165 | static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count) | 212 | static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count) |
166 | { | 213 | { |
167 | return hw->tx ? hw->tx[count] : 0; | 214 | return hw->tx ? hw->tx[count] : 0; |
@@ -289,7 +336,9 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev) | |||
289 | hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer; | 336 | hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer; |
290 | hw->bitbang.chipselect = s3c24xx_spi_chipsel; | 337 | hw->bitbang.chipselect = s3c24xx_spi_chipsel; |
291 | hw->bitbang.txrx_bufs = s3c24xx_spi_txrx; | 338 | hw->bitbang.txrx_bufs = s3c24xx_spi_txrx; |
292 | hw->bitbang.master->setup = s3c24xx_spi_setup; | 339 | |
340 | hw->master->setup = s3c24xx_spi_setup; | ||
341 | hw->master->cleanup = s3c24xx_spi_cleanup; | ||
293 | 342 | ||
294 | dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang); | 343 | dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang); |
295 | 344 | ||
@@ -302,7 +351,7 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev) | |||
302 | goto err_no_iores; | 351 | goto err_no_iores; |
303 | } | 352 | } |
304 | 353 | ||
305 | hw->ioarea = request_mem_region(res->start, (res->end - res->start)+1, | 354 | hw->ioarea = request_mem_region(res->start, resource_size(res), |
306 | pdev->name); | 355 | pdev->name); |
307 | 356 | ||
308 | if (hw->ioarea == NULL) { | 357 | if (hw->ioarea == NULL) { |
@@ -311,7 +360,7 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev) | |||
311 | goto err_no_iores; | 360 | goto err_no_iores; |
312 | } | 361 | } |
313 | 362 | ||
314 | hw->regs = ioremap(res->start, (res->end - res->start)+1); | 363 | hw->regs = ioremap(res->start, resource_size(res)); |
315 | if (hw->regs == NULL) { | 364 | if (hw->regs == NULL) { |
316 | dev_err(&pdev->dev, "Cannot map IO\n"); | 365 | dev_err(&pdev->dev, "Cannot map IO\n"); |
317 | err = -ENXIO; | 366 | err = -ENXIO; |
@@ -388,7 +437,7 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev) | |||
388 | 437 | ||
389 | err_no_iores: | 438 | err_no_iores: |
390 | err_no_pdata: | 439 | err_no_pdata: |
391 | spi_master_put(hw->master);; | 440 | spi_master_put(hw->master); |
392 | 441 | ||
393 | err_nomem: | 442 | err_nomem: |
394 | return err; | 443 | return err; |
@@ -421,9 +470,9 @@ static int __exit s3c24xx_spi_remove(struct platform_device *dev) | |||
421 | 470 | ||
422 | #ifdef CONFIG_PM | 471 | #ifdef CONFIG_PM |
423 | 472 | ||
424 | static int s3c24xx_spi_suspend(struct platform_device *pdev, pm_message_t msg) | 473 | static int s3c24xx_spi_suspend(struct device *dev) |
425 | { | 474 | { |
426 | struct s3c24xx_spi *hw = platform_get_drvdata(pdev); | 475 | struct s3c24xx_spi *hw = platform_get_drvdata(to_platform_device(dev)); |
427 | 476 | ||
428 | if (hw->pdata && hw->pdata->gpio_setup) | 477 | if (hw->pdata && hw->pdata->gpio_setup) |
429 | hw->pdata->gpio_setup(hw->pdata, 0); | 478 | hw->pdata->gpio_setup(hw->pdata, 0); |
@@ -432,27 +481,31 @@ static int s3c24xx_spi_suspend(struct platform_device *pdev, pm_message_t msg) | |||
432 | return 0; | 481 | return 0; |
433 | } | 482 | } |
434 | 483 | ||
435 | static int s3c24xx_spi_resume(struct platform_device *pdev) | 484 | static int s3c24xx_spi_resume(struct device *dev) |
436 | { | 485 | { |
437 | struct s3c24xx_spi *hw = platform_get_drvdata(pdev); | 486 | struct s3c24xx_spi *hw = platform_get_drvdata(to_platform_device(dev)); |
438 | 487 | ||
439 | s3c24xx_spi_initialsetup(hw); | 488 | s3c24xx_spi_initialsetup(hw); |
440 | return 0; | 489 | return 0; |
441 | } | 490 | } |
442 | 491 | ||
492 | static struct dev_pm_ops s3c24xx_spi_pmops = { | ||
493 | .suspend = s3c24xx_spi_suspend, | ||
494 | .resume = s3c24xx_spi_resume, | ||
495 | }; | ||
496 | |||
497 | #define S3C24XX_SPI_PMOPS &s3c24xx_spi_pmops | ||
443 | #else | 498 | #else |
444 | #define s3c24xx_spi_suspend NULL | 499 | #define S3C24XX_SPI_PMOPS NULL |
445 | #define s3c24xx_spi_resume NULL | 500 | #endif /* CONFIG_PM */ |
446 | #endif | ||
447 | 501 | ||
448 | MODULE_ALIAS("platform:s3c2410-spi"); | 502 | MODULE_ALIAS("platform:s3c2410-spi"); |
449 | static struct platform_driver s3c24xx_spi_driver = { | 503 | static struct platform_driver s3c24xx_spi_driver = { |
450 | .remove = __exit_p(s3c24xx_spi_remove), | 504 | .remove = __exit_p(s3c24xx_spi_remove), |
451 | .suspend = s3c24xx_spi_suspend, | ||
452 | .resume = s3c24xx_spi_resume, | ||
453 | .driver = { | 505 | .driver = { |
454 | .name = "s3c2410-spi", | 506 | .name = "s3c2410-spi", |
455 | .owner = THIS_MODULE, | 507 | .owner = THIS_MODULE, |
508 | .pm = S3C24XX_SPI_PMOPS, | ||
456 | }, | 509 | }, |
457 | }; | 510 | }; |
458 | 511 | ||