aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi_s3c24xx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi/spi_s3c24xx.c')
-rw-r--r--drivers/spi/spi_s3c24xx.c159
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 */
38struct s3c24xx_spi_devstate {
39 unsigned int hz;
40 unsigned int mode;
41 u8 spcon;
42 u8 sppre;
43};
44
34struct s3c24xx_spi { 45struct 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
72static void s3c24xx_spi_chipsel(struct spi_device *spi, int value) 83static 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
107static int s3c24xx_spi_setupxfer(struct spi_device *spi, 105static 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
159static 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
152static int s3c24xx_spi_setup(struct spi_device *spi) 173static 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
207static void s3c24xx_spi_cleanup(struct spi_device *spi)
208{
209 kfree(spi->controller_state);
210}
211
165static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count) 212static 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
424static int s3c24xx_spi_suspend(struct platform_device *pdev, pm_message_t msg) 473static 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
435static int s3c24xx_spi_resume(struct platform_device *pdev) 484static 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
492static 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
448MODULE_ALIAS("platform:s3c2410-spi"); 502MODULE_ALIAS("platform:s3c2410-spi");
449static struct platform_driver s3c24xx_spi_driver = { 503static 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