diff options
author | Uwe Kleine-König <u.kleine-koenig@pengutronix.de> | 2010-09-09 15:02:48 -0400 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2010-10-01 03:32:13 -0400 |
commit | 0b599603d8534bc3946a0f07e461c76d7947dfcf (patch) | |
tree | 18d5b7a0279f54f2f939b1399aca5f443cbc431f /drivers/spi/spi_imx.c | |
parent | 3b2aa89eb381d2f445aa3c60d8f070a3f55efa63 (diff) |
spi/imx: add support for imx51's eCSPI and CSPI
i.MX51 comes with two eCSPI interfaces (that are quite different from
what was known before---the tried and tested Freescale way) and a CSPI
interface that is identical to the devices found on i.MX25 and i.MX35.
This patch is a merge of two very similar patches (by Jason Wang and Sascha
Hauer resp.) plus a (now hopefully correct) reimplementation of the
clock calculation.
Acked-by: Jason Wang <jason77.wang@gmail.com>
Acked-by: Grant Likely <grant.likely@secretlab.ca>
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/spi/spi_imx.c')
-rw-r--r-- | drivers/spi/spi_imx.c | 140 |
1 files changed, 139 insertions, 1 deletions
diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c index 23db9840b9ae..6bab2cfd93c1 100644 --- a/drivers/spi/spi_imx.c +++ b/drivers/spi/spi_imx.c | |||
@@ -65,6 +65,7 @@ enum spi_imx_devtype { | |||
65 | SPI_IMX_VER_0_4, | 65 | SPI_IMX_VER_0_4, |
66 | SPI_IMX_VER_0_5, | 66 | SPI_IMX_VER_0_5, |
67 | SPI_IMX_VER_0_7, | 67 | SPI_IMX_VER_0_7, |
68 | SPI_IMX_VER_2_3, | ||
68 | SPI_IMX_VER_AUTODETECT, | 69 | SPI_IMX_VER_AUTODETECT, |
69 | }; | 70 | }; |
70 | 71 | ||
@@ -155,7 +156,7 @@ static unsigned int spi_imx_clkdiv_1(unsigned int fin, | |||
155 | return max; | 156 | return max; |
156 | } | 157 | } |
157 | 158 | ||
158 | /* MX1, MX31, MX35 */ | 159 | /* MX1, MX31, MX35, MX51 CSPI */ |
159 | static unsigned int spi_imx_clkdiv_2(unsigned int fin, | 160 | static unsigned int spi_imx_clkdiv_2(unsigned int fin, |
160 | unsigned int fspi) | 161 | unsigned int fspi) |
161 | { | 162 | { |
@@ -170,6 +171,128 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin, | |||
170 | return 7; | 171 | return 7; |
171 | } | 172 | } |
172 | 173 | ||
174 | #define SPI_IMX2_3_CTRL 0x08 | ||
175 | #define SPI_IMX2_3_CTRL_ENABLE (1 << 0) | ||
176 | #define SPI_IMX2_3_CTRL_XCH (1 << 2) | ||
177 | #define SPI_IMX2_3_CTRL_MODE(cs) (1 << ((cs) + 4)) | ||
178 | #define SPI_IMX2_3_CTRL_POSTDIV_OFFSET 8 | ||
179 | #define SPI_IMX2_3_CTRL_PREDIV_OFFSET 12 | ||
180 | #define SPI_IMX2_3_CTRL_CS(cs) ((cs) << 18) | ||
181 | #define SPI_IMX2_3_CTRL_BL_OFFSET 20 | ||
182 | |||
183 | #define SPI_IMX2_3_CONFIG 0x0c | ||
184 | #define SPI_IMX2_3_CONFIG_SCLKPHA(cs) (1 << ((cs) + 0)) | ||
185 | #define SPI_IMX2_3_CONFIG_SCLKPOL(cs) (1 << ((cs) + 4)) | ||
186 | #define SPI_IMX2_3_CONFIG_SBBCTRL(cs) (1 << ((cs) + 8)) | ||
187 | #define SPI_IMX2_3_CONFIG_SSBPOL(cs) (1 << ((cs) + 12)) | ||
188 | |||
189 | #define SPI_IMX2_3_INT 0x10 | ||
190 | #define SPI_IMX2_3_INT_TEEN (1 << 0) | ||
191 | #define SPI_IMX2_3_INT_RREN (1 << 3) | ||
192 | |||
193 | #define SPI_IMX2_3_STAT 0x18 | ||
194 | #define SPI_IMX2_3_STAT_RR (1 << 3) | ||
195 | |||
196 | /* MX51 eCSPI */ | ||
197 | static unsigned int spi_imx2_3_clkdiv(unsigned int fin, unsigned int fspi) | ||
198 | { | ||
199 | /* | ||
200 | * there are two 4-bit dividers, the pre-divider divides by | ||
201 | * $pre, the post-divider by 2^$post | ||
202 | */ | ||
203 | unsigned int pre, post; | ||
204 | |||
205 | if (unlikely(fspi > fin)) | ||
206 | return 0; | ||
207 | |||
208 | post = fls(fin) - fls(fspi); | ||
209 | if (fin > fspi << post) | ||
210 | post++; | ||
211 | |||
212 | /* now we have: (fin <= fspi << post) with post being minimal */ | ||
213 | |||
214 | post = max(4U, post) - 4; | ||
215 | if (unlikely(post > 0xf)) { | ||
216 | pr_err("%s: cannot set clock freq: %u (base freq: %u)\n", | ||
217 | __func__, fspi, fin); | ||
218 | return 0xff; | ||
219 | } | ||
220 | |||
221 | pre = DIV_ROUND_UP(fin, fspi << post) - 1; | ||
222 | |||
223 | pr_debug("%s: fin: %u, fspi: %u, post: %u, pre: %u\n", | ||
224 | __func__, fin, fspi, post, pre); | ||
225 | return (pre << SPI_IMX2_3_CTRL_PREDIV_OFFSET) | | ||
226 | (post << SPI_IMX2_3_CTRL_POSTDIV_OFFSET); | ||
227 | } | ||
228 | |||
229 | static void __maybe_unused spi_imx2_3_intctrl(struct spi_imx_data *spi_imx, int enable) | ||
230 | { | ||
231 | unsigned val = 0; | ||
232 | |||
233 | if (enable & MXC_INT_TE) | ||
234 | val |= SPI_IMX2_3_INT_TEEN; | ||
235 | |||
236 | if (enable & MXC_INT_RR) | ||
237 | val |= SPI_IMX2_3_INT_RREN; | ||
238 | |||
239 | writel(val, spi_imx->base + SPI_IMX2_3_INT); | ||
240 | } | ||
241 | |||
242 | static void __maybe_unused spi_imx2_3_trigger(struct spi_imx_data *spi_imx) | ||
243 | { | ||
244 | u32 reg; | ||
245 | |||
246 | reg = readl(spi_imx->base + SPI_IMX2_3_CTRL); | ||
247 | reg |= SPI_IMX2_3_CTRL_XCH; | ||
248 | writel(reg, spi_imx->base + SPI_IMX2_3_CTRL); | ||
249 | } | ||
250 | |||
251 | static int __maybe_unused spi_imx2_3_config(struct spi_imx_data *spi_imx, | ||
252 | struct spi_imx_config *config) | ||
253 | { | ||
254 | u32 ctrl = SPI_IMX2_3_CTRL_ENABLE, cfg = 0; | ||
255 | |||
256 | /* set master mode */ | ||
257 | ctrl |= SPI_IMX2_3_CTRL_MODE(config->cs); | ||
258 | |||
259 | /* set clock speed */ | ||
260 | ctrl |= spi_imx2_3_clkdiv(spi_imx->spi_clk, config->speed_hz); | ||
261 | |||
262 | /* set chip select to use */ | ||
263 | ctrl |= SPI_IMX2_3_CTRL_CS(config->cs); | ||
264 | |||
265 | ctrl |= (config->bpw - 1) << SPI_IMX2_3_CTRL_BL_OFFSET; | ||
266 | |||
267 | cfg |= SPI_IMX2_3_CONFIG_SBBCTRL(config->cs); | ||
268 | |||
269 | if (config->mode & SPI_CPHA) | ||
270 | cfg |= SPI_IMX2_3_CONFIG_SCLKPHA(config->cs); | ||
271 | |||
272 | if (config->mode & SPI_CPOL) | ||
273 | cfg |= SPI_IMX2_3_CONFIG_SCLKPOL(config->cs); | ||
274 | |||
275 | if (config->mode & SPI_CS_HIGH) | ||
276 | cfg |= SPI_IMX2_3_CONFIG_SSBPOL(config->cs); | ||
277 | |||
278 | writel(ctrl, spi_imx->base + SPI_IMX2_3_CTRL); | ||
279 | writel(cfg, spi_imx->base + SPI_IMX2_3_CONFIG); | ||
280 | |||
281 | return 0; | ||
282 | } | ||
283 | |||
284 | static int __maybe_unused spi_imx2_3_rx_available(struct spi_imx_data *spi_imx) | ||
285 | { | ||
286 | return readl(spi_imx->base + SPI_IMX2_3_STAT) & SPI_IMX2_3_STAT_RR; | ||
287 | } | ||
288 | |||
289 | static void __maybe_unused spi_imx2_3_reset(struct spi_imx_data *spi_imx) | ||
290 | { | ||
291 | /* drain receive buffer */ | ||
292 | while (spi_imx2_3_rx_available(spi_imx)) | ||
293 | readl(spi_imx->base + MXC_CSPIRXDATA); | ||
294 | } | ||
295 | |||
173 | #define MX31_INTREG_TEEN (1 << 0) | 296 | #define MX31_INTREG_TEEN (1 << 0) |
174 | #define MX31_INTREG_RREN (1 << 3) | 297 | #define MX31_INTREG_RREN (1 << 3) |
175 | 298 | ||
@@ -447,6 +570,15 @@ static struct spi_imx_devtype_data spi_imx_devtype_data[] __devinitdata = { | |||
447 | .reset = spi_imx0_4_reset, | 570 | .reset = spi_imx0_4_reset, |
448 | }, | 571 | }, |
449 | #endif | 572 | #endif |
573 | #ifdef CONFIG_SPI_IMX_VER_2_3 | ||
574 | [SPI_IMX_VER_2_3] = { | ||
575 | .intctrl = spi_imx2_3_intctrl, | ||
576 | .config = spi_imx2_3_config, | ||
577 | .trigger = spi_imx2_3_trigger, | ||
578 | .rx_available = spi_imx2_3_rx_available, | ||
579 | .reset = spi_imx2_3_reset, | ||
580 | }, | ||
581 | #endif | ||
450 | }; | 582 | }; |
451 | 583 | ||
452 | static void spi_imx_chipselect(struct spi_device *spi, int is_active) | 584 | static void spi_imx_chipselect(struct spi_device *spi, int is_active) |
@@ -603,6 +735,12 @@ static struct platform_device_id spi_imx_devtype[] = { | |||
603 | .name = "imx35-cspi", | 735 | .name = "imx35-cspi", |
604 | .driver_data = SPI_IMX_VER_0_7, | 736 | .driver_data = SPI_IMX_VER_0_7, |
605 | }, { | 737 | }, { |
738 | .name = "imx51-cspi", | ||
739 | .driver_data = SPI_IMX_VER_0_7, | ||
740 | }, { | ||
741 | .name = "imx51-ecspi", | ||
742 | .driver_data = SPI_IMX_VER_2_3, | ||
743 | }, { | ||
606 | /* sentinel */ | 744 | /* sentinel */ |
607 | } | 745 | } |
608 | }; | 746 | }; |