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.c126
1 files changed, 89 insertions, 37 deletions
diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c
index 03695b67ebd5..33d94f76b9ef 100644
--- a/drivers/spi/spi_s3c24xx.c
+++ b/drivers/spi/spi_s3c24xx.c
@@ -28,6 +28,20 @@
28#include <plat/regs-spi.h> 28#include <plat/regs-spi.h>
29#include <mach/spi.h> 29#include <mach/spi.h>
30 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
31struct s3c24xx_spi { 45struct s3c24xx_spi {
32 /* bitbang has to be first */ 46 /* bitbang has to be first */
33 struct spi_bitbang bitbang; 47 struct spi_bitbang bitbang;
@@ -68,43 +82,31 @@ static void s3c24xx_spi_gpiocs(struct s3c2410_spi_info *spi, int cs, int pol)
68 82
69static void s3c24xx_spi_chipsel(struct spi_device *spi, int value) 83static void s3c24xx_spi_chipsel(struct spi_device *spi, int value)
70{ 84{
85 struct s3c24xx_spi_devstate *cs = spi->controller_state;
71 struct s3c24xx_spi *hw = to_hw(spi); 86 struct s3c24xx_spi *hw = to_hw(spi);
72 unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0; 87 unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0;
73 unsigned int spcon; 88
89 /* change the chipselect state and the state of the spi engine clock */
74 90
75 switch (value) { 91 switch (value) {
76 case BITBANG_CS_INACTIVE: 92 case BITBANG_CS_INACTIVE:
77 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);
78 break; 95 break;
79 96
80 case BITBANG_CS_ACTIVE: 97 case BITBANG_CS_ACTIVE:
81 spcon = readb(hw->regs + S3C2410_SPCON); 98 writeb(cs->spcon | S3C2410_SPCON_ENSCK,
82 99 hw->regs + S3C2410_SPCON);
83 if (spi->mode & SPI_CPHA)
84 spcon |= S3C2410_SPCON_CPHA_FMTB;
85 else
86 spcon &= ~S3C2410_SPCON_CPHA_FMTB;
87
88 if (spi->mode & SPI_CPOL)
89 spcon |= S3C2410_SPCON_CPOL_HIGH;
90 else
91 spcon &= ~S3C2410_SPCON_CPOL_HIGH;
92
93 spcon |= S3C2410_SPCON_ENSCK;
94
95 /* write new configration */
96
97 writeb(spcon, hw->regs + S3C2410_SPCON);
98 hw->set_cs(hw->pdata, spi->chip_select, cspol); 100 hw->set_cs(hw->pdata, spi->chip_select, cspol);
99
100 break; 101 break;
101 } 102 }
102} 103}
103 104
104static int s3c24xx_spi_setupxfer(struct spi_device *spi, 105static int s3c24xx_spi_update_state(struct spi_device *spi,
105 struct spi_transfer *t) 106 struct spi_transfer *t)
106{ 107{
107 struct s3c24xx_spi *hw = to_hw(spi); 108 struct s3c24xx_spi *hw = to_hw(spi);
109 struct s3c24xx_spi_devstate *cs = spi->controller_state;
108 unsigned int bpw; 110 unsigned int bpw;
109 unsigned int hz; 111 unsigned int hz;
110 unsigned int div; 112 unsigned int div;
@@ -124,41 +126,89 @@ static int s3c24xx_spi_setupxfer(struct spi_device *spi,
124 return -EINVAL; 126 return -EINVAL;
125 } 127 }
126 128
127 clk = clk_get_rate(hw->clk); 129 if (spi->mode != cs->mode) {
128 div = DIV_ROUND_UP(clk, hz * 2) - 1; 130 u8 spcon = SPCON_DEFAULT;
129 131
130 if (div > 255) 132 if (spi->mode & SPI_CPHA)
131 div = 255; 133 spcon |= S3C2410_SPCON_CPHA_FMTB;
132 134
133 dev_dbg(&spi->dev, "setting pre-scaler to %d (wanted %d, got %ld)\n", 135 if (spi->mode & SPI_CPOL)
134 div, hz, clk / (2 * (div + 1))); 136 spcon |= S3C2410_SPCON_CPOL_HIGH;
135 137
138 cs->mode = spi->mode;
139 cs->spcon = spcon;
140 }
136 141
137 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;
138 145
139 spin_lock(&hw->bitbang.lock); 146 if (div > 255)
140 if (!hw->bitbang.busy) { 147 div = 255;
141 hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE); 148
142 /* 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;
143 } 154 }
144 spin_unlock(&hw->bitbang.lock);
145 155
146 return 0; 156 return 0;
147} 157}
148 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
149static int s3c24xx_spi_setup(struct spi_device *spi) 173static int s3c24xx_spi_setup(struct spi_device *spi)
150{ 174{
175 struct s3c24xx_spi_devstate *cs = spi->controller_state;
176 struct s3c24xx_spi *hw = to_hw(spi);
151 int ret; 177 int ret;
152 178
153 ret = s3c24xx_spi_setupxfer(spi, NULL); 179 /* allocate settings on the first call */
154 if (ret < 0) { 180 if (!cs) {
155 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)
156 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 ? */
157 } 201 }
202 spin_unlock(&hw->bitbang.lock);
158 203
159 return 0; 204 return 0;
160} 205}
161 206
207static void s3c24xx_spi_cleanup(struct spi_device *spi)
208{
209 kfree(spi->controller_state);
210}
211
162static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count) 212static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count)
163{ 213{
164 return hw->tx ? hw->tx[count] : 0; 214 return hw->tx ? hw->tx[count] : 0;
@@ -286,7 +336,9 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev)
286 hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer; 336 hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer;
287 hw->bitbang.chipselect = s3c24xx_spi_chipsel; 337 hw->bitbang.chipselect = s3c24xx_spi_chipsel;
288 hw->bitbang.txrx_bufs = s3c24xx_spi_txrx; 338 hw->bitbang.txrx_bufs = s3c24xx_spi_txrx;
289 hw->bitbang.master->setup = s3c24xx_spi_setup; 339
340 hw->master->setup = s3c24xx_spi_setup;
341 hw->master->cleanup = s3c24xx_spi_cleanup;
290 342
291 dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang); 343 dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang);
292 344