aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorBen Dooks <ben@simtec.co.uk>2009-09-22 19:46:14 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-09-23 10:39:44 -0400
commit570327d9f48b0be5ca626bcfd80266b7ee2001aa (patch)
tree273502722726dab6e8a1c1fa903e642d22be3373 /drivers
parent6d61320707ac2960bc820616bd5921885b874780 (diff)
spi_s3c24xx: cache device setup data
With the update to the spi_bitbang driver, the transfer setup code is being called more often, and thus is often re-doing calculations that have been done before. The SPI layer allows our driver to add its own data to each device so add a result cache to each device. This should also remove the problem where we where directly setting up registers in the setup call which meant we might overwrite the state of an extant transfer., Signed-off-by: Ben Dooks <ben@simtec.co.uk> Cc: David Brownell <david-b@pacbell.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-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