diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/arm/mach-pxa/ssp.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/arm/mach-pxa/ssp.c')
-rw-r--r-- | arch/arm/mach-pxa/ssp.c | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/arch/arm/mach-pxa/ssp.c b/arch/arm/mach-pxa/ssp.c new file mode 100644 index 000000000000..4d826c021315 --- /dev/null +++ b/arch/arm/mach-pxa/ssp.c | |||
@@ -0,0 +1,363 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/mach-pxa/ssp.c | ||
3 | * | ||
4 | * based on linux/arch/arm/mach-sa1100/ssp.c by Russell King | ||
5 | * | ||
6 | * Copyright (C) 2003 Russell King. | ||
7 | * Copyright (C) 2003 Wolfson Microelectronics PLC | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | * | ||
13 | * PXA2xx SSP driver. This provides the generic core for simple | ||
14 | * IO-based SSP applications and allows easy port setup for DMA access. | ||
15 | * | ||
16 | * Author: Liam Girdwood <liam.girdwood@wolfsonmicro.com> | ||
17 | * | ||
18 | * Revision history: | ||
19 | * 22nd Aug 2003 Initial version. | ||
20 | * 20th Dec 2004 Added ssp_config for changing port config without | ||
21 | * closing the port. | ||
22 | */ | ||
23 | |||
24 | #include <linux/module.h> | ||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/sched.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <linux/errno.h> | ||
29 | #include <linux/interrupt.h> | ||
30 | #include <linux/ioport.h> | ||
31 | #include <linux/init.h> | ||
32 | #include <asm/io.h> | ||
33 | #include <asm/irq.h> | ||
34 | #include <asm/hardware.h> | ||
35 | #include <asm/arch/ssp.h> | ||
36 | #include <asm/arch/pxa-regs.h> | ||
37 | |||
38 | #define PXA_SSP_PORTS 3 | ||
39 | |||
40 | static DECLARE_MUTEX(sem); | ||
41 | static int use_count[PXA_SSP_PORTS] = {0, 0, 0}; | ||
42 | |||
43 | static irqreturn_t ssp_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
44 | { | ||
45 | struct ssp_dev *dev = (struct ssp_dev*) dev_id; | ||
46 | unsigned int status = SSSR_P(dev->port); | ||
47 | |||
48 | SSSR_P(dev->port) = status; /* clear status bits */ | ||
49 | |||
50 | if (status & SSSR_ROR) | ||
51 | printk(KERN_WARNING "SSP(%d): receiver overrun\n", dev->port); | ||
52 | |||
53 | if (status & SSSR_TUR) | ||
54 | printk(KERN_WARNING "SSP(%d): transmitter underrun\n", dev->port); | ||
55 | |||
56 | if (status & SSSR_BCE) | ||
57 | printk(KERN_WARNING "SSP(%d): bit count error\n", dev->port); | ||
58 | |||
59 | return IRQ_HANDLED; | ||
60 | } | ||
61 | |||
62 | /** | ||
63 | * ssp_write_word - write a word to the SSP port | ||
64 | * @data: 32-bit, MSB justified data to write. | ||
65 | * | ||
66 | * Wait for a free entry in the SSP transmit FIFO, and write a data | ||
67 | * word to the SSP port. | ||
68 | * | ||
69 | * The caller is expected to perform the necessary locking. | ||
70 | * | ||
71 | * Returns: | ||
72 | * %-ETIMEDOUT timeout occurred (for future) | ||
73 | * 0 success | ||
74 | */ | ||
75 | int ssp_write_word(struct ssp_dev *dev, u32 data) | ||
76 | { | ||
77 | while (!(SSSR_P(dev->port) & SSSR_TNF)) | ||
78 | cpu_relax(); | ||
79 | |||
80 | SSDR_P(dev->port) = data; | ||
81 | |||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | /** | ||
86 | * ssp_read_word - read a word from the SSP port | ||
87 | * | ||
88 | * Wait for a data word in the SSP receive FIFO, and return the | ||
89 | * received data. Data is LSB justified. | ||
90 | * | ||
91 | * Note: Currently, if data is not expected to be received, this | ||
92 | * function will wait for ever. | ||
93 | * | ||
94 | * The caller is expected to perform the necessary locking. | ||
95 | * | ||
96 | * Returns: | ||
97 | * %-ETIMEDOUT timeout occurred (for future) | ||
98 | * 32-bit data success | ||
99 | */ | ||
100 | int ssp_read_word(struct ssp_dev *dev) | ||
101 | { | ||
102 | while (!(SSSR_P(dev->port) & SSSR_RNE)) | ||
103 | cpu_relax(); | ||
104 | |||
105 | return SSDR_P(dev->port); | ||
106 | } | ||
107 | |||
108 | /** | ||
109 | * ssp_flush - flush the transmit and receive FIFOs | ||
110 | * | ||
111 | * Wait for the SSP to idle, and ensure that the receive FIFO | ||
112 | * is empty. | ||
113 | * | ||
114 | * The caller is expected to perform the necessary locking. | ||
115 | */ | ||
116 | void ssp_flush(struct ssp_dev *dev) | ||
117 | { | ||
118 | do { | ||
119 | while (SSSR_P(dev->port) & SSSR_RNE) { | ||
120 | (void) SSDR_P(dev->port); | ||
121 | } | ||
122 | } while (SSSR_P(dev->port) & SSSR_BSY); | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * ssp_enable - enable the SSP port | ||
127 | * | ||
128 | * Turn on the SSP port. | ||
129 | */ | ||
130 | void ssp_enable(struct ssp_dev *dev) | ||
131 | { | ||
132 | SSCR0_P(dev->port) |= SSCR0_SSE; | ||
133 | } | ||
134 | |||
135 | /** | ||
136 | * ssp_disable - shut down the SSP port | ||
137 | * | ||
138 | * Turn off the SSP port, optionally powering it down. | ||
139 | */ | ||
140 | void ssp_disable(struct ssp_dev *dev) | ||
141 | { | ||
142 | SSCR0_P(dev->port) &= ~SSCR0_SSE; | ||
143 | } | ||
144 | |||
145 | /** | ||
146 | * ssp_save_state - save the SSP configuration | ||
147 | * @ssp: pointer to structure to save SSP configuration | ||
148 | * | ||
149 | * Save the configured SSP state for suspend. | ||
150 | */ | ||
151 | void ssp_save_state(struct ssp_dev *dev, struct ssp_state *ssp) | ||
152 | { | ||
153 | ssp->cr0 = SSCR0_P(dev->port); | ||
154 | ssp->cr1 = SSCR1_P(dev->port); | ||
155 | ssp->to = SSTO_P(dev->port); | ||
156 | ssp->psp = SSPSP_P(dev->port); | ||
157 | |||
158 | SSCR0_P(dev->port) &= ~SSCR0_SSE; | ||
159 | } | ||
160 | |||
161 | /** | ||
162 | * ssp_restore_state - restore a previously saved SSP configuration | ||
163 | * @ssp: pointer to configuration saved by ssp_save_state | ||
164 | * | ||
165 | * Restore the SSP configuration saved previously by ssp_save_state. | ||
166 | */ | ||
167 | void ssp_restore_state(struct ssp_dev *dev, struct ssp_state *ssp) | ||
168 | { | ||
169 | SSSR_P(dev->port) = SSSR_ROR | SSSR_TUR | SSSR_BCE; | ||
170 | |||
171 | SSCR0_P(dev->port) = ssp->cr0 & ~SSCR0_SSE; | ||
172 | SSCR1_P(dev->port) = ssp->cr1; | ||
173 | SSTO_P(dev->port) = ssp->to; | ||
174 | SSPSP_P(dev->port) = ssp->psp; | ||
175 | |||
176 | SSCR0_P(dev->port) = ssp->cr0; | ||
177 | } | ||
178 | |||
179 | /** | ||
180 | * ssp_config - configure SSP port settings | ||
181 | * @mode: port operating mode | ||
182 | * @flags: port config flags | ||
183 | * @psp_flags: port PSP config flags | ||
184 | * @speed: port speed | ||
185 | * | ||
186 | * Port MUST be disabled by ssp_disable before making any config changes. | ||
187 | */ | ||
188 | int ssp_config(struct ssp_dev *dev, u32 mode, u32 flags, u32 psp_flags, u32 speed) | ||
189 | { | ||
190 | dev->mode = mode; | ||
191 | dev->flags = flags; | ||
192 | dev->psp_flags = psp_flags; | ||
193 | dev->speed = speed; | ||
194 | |||
195 | /* set up port type, speed, port settings */ | ||
196 | SSCR0_P(dev->port) = (dev->speed | dev->mode); | ||
197 | SSCR1_P(dev->port) = dev->flags; | ||
198 | SSPSP_P(dev->port) = dev->psp_flags; | ||
199 | |||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | /** | ||
204 | * ssp_init - setup the SSP port | ||
205 | * | ||
206 | * initialise and claim resources for the SSP port. | ||
207 | * | ||
208 | * Returns: | ||
209 | * %-ENODEV if the SSP port is unavailable | ||
210 | * %-EBUSY if the resources are already in use | ||
211 | * %0 on success | ||
212 | */ | ||
213 | int ssp_init(struct ssp_dev *dev, u32 port) | ||
214 | { | ||
215 | int ret, irq; | ||
216 | |||
217 | if (port > PXA_SSP_PORTS || port == 0) | ||
218 | return -ENODEV; | ||
219 | |||
220 | down(&sem); | ||
221 | if (use_count[port - 1]) { | ||
222 | up(&sem); | ||
223 | return -EBUSY; | ||
224 | } | ||
225 | use_count[port - 1]++; | ||
226 | |||
227 | if (!request_mem_region(__PREG(SSCR0_P(port)), 0x2c, "SSP")) { | ||
228 | use_count[port - 1]--; | ||
229 | up(&sem); | ||
230 | return -EBUSY; | ||
231 | } | ||
232 | |||
233 | switch (port) { | ||
234 | case 1: | ||
235 | irq = IRQ_SSP; | ||
236 | break; | ||
237 | #if defined (CONFIG_PXA27x) | ||
238 | case 2: | ||
239 | irq = IRQ_SSP2; | ||
240 | break; | ||
241 | case 3: | ||
242 | irq = IRQ_SSP3; | ||
243 | break; | ||
244 | #else | ||
245 | case 2: | ||
246 | irq = IRQ_NSSP; | ||
247 | break; | ||
248 | case 3: | ||
249 | irq = IRQ_ASSP; | ||
250 | break; | ||
251 | #endif | ||
252 | default: | ||
253 | return -ENODEV; | ||
254 | } | ||
255 | |||
256 | dev->port = port; | ||
257 | |||
258 | ret = request_irq(irq, ssp_interrupt, 0, "SSP", dev); | ||
259 | if (ret) | ||
260 | goto out_region; | ||
261 | |||
262 | /* turn on SSP port clock */ | ||
263 | switch (dev->port) { | ||
264 | #if defined (CONFIG_PXA27x) | ||
265 | case 1: | ||
266 | pxa_set_cken(CKEN23_SSP1, 1); | ||
267 | break; | ||
268 | case 2: | ||
269 | pxa_set_cken(CKEN3_SSP2, 1); | ||
270 | break; | ||
271 | case 3: | ||
272 | pxa_set_cken(CKEN4_SSP3, 1); | ||
273 | break; | ||
274 | #else | ||
275 | case 1: | ||
276 | pxa_set_cken(CKEN3_SSP, 1); | ||
277 | break; | ||
278 | case 2: | ||
279 | pxa_set_cken(CKEN9_NSSP, 1); | ||
280 | break; | ||
281 | case 3: | ||
282 | pxa_set_cken(CKEN10_ASSP, 1); | ||
283 | break; | ||
284 | #endif | ||
285 | } | ||
286 | |||
287 | up(&sem); | ||
288 | return 0; | ||
289 | |||
290 | out_region: | ||
291 | release_mem_region(__PREG(SSCR0_P(port)), 0x2c); | ||
292 | use_count[port - 1]--; | ||
293 | up(&sem); | ||
294 | return ret; | ||
295 | } | ||
296 | |||
297 | /** | ||
298 | * ssp_exit - undo the effects of ssp_init | ||
299 | * | ||
300 | * release and free resources for the SSP port. | ||
301 | */ | ||
302 | void ssp_exit(struct ssp_dev *dev) | ||
303 | { | ||
304 | int irq; | ||
305 | |||
306 | down(&sem); | ||
307 | SSCR0_P(dev->port) &= ~SSCR0_SSE; | ||
308 | |||
309 | /* find irq, save power and turn off SSP port clock */ | ||
310 | switch (dev->port) { | ||
311 | #if defined (CONFIG_PXA27x) | ||
312 | case 1: | ||
313 | irq = IRQ_SSP; | ||
314 | pxa_set_cken(CKEN23_SSP1, 0); | ||
315 | break; | ||
316 | case 2: | ||
317 | irq = IRQ_SSP2; | ||
318 | pxa_set_cken(CKEN3_SSP2, 0); | ||
319 | break; | ||
320 | case 3: | ||
321 | irq = IRQ_SSP3; | ||
322 | pxa_set_cken(CKEN4_SSP3, 0); | ||
323 | break; | ||
324 | #else | ||
325 | case 1: | ||
326 | irq = IRQ_SSP; | ||
327 | pxa_set_cken(CKEN3_SSP, 0); | ||
328 | break; | ||
329 | case 2: | ||
330 | irq = IRQ_NSSP; | ||
331 | pxa_set_cken(CKEN9_NSSP, 0); | ||
332 | break; | ||
333 | case 3: | ||
334 | irq = IRQ_ASSP; | ||
335 | pxa_set_cken(CKEN10_ASSP, 0); | ||
336 | break; | ||
337 | #endif | ||
338 | default: | ||
339 | printk(KERN_WARNING "SSP: tried to close invalid port\n"); | ||
340 | return; | ||
341 | } | ||
342 | |||
343 | free_irq(irq, dev); | ||
344 | release_mem_region(__PREG(SSCR0_P(dev->port)), 0x2c); | ||
345 | use_count[dev->port - 1]--; | ||
346 | up(&sem); | ||
347 | } | ||
348 | |||
349 | EXPORT_SYMBOL(ssp_write_word); | ||
350 | EXPORT_SYMBOL(ssp_read_word); | ||
351 | EXPORT_SYMBOL(ssp_flush); | ||
352 | EXPORT_SYMBOL(ssp_enable); | ||
353 | EXPORT_SYMBOL(ssp_disable); | ||
354 | EXPORT_SYMBOL(ssp_save_state); | ||
355 | EXPORT_SYMBOL(ssp_restore_state); | ||
356 | EXPORT_SYMBOL(ssp_init); | ||
357 | EXPORT_SYMBOL(ssp_exit); | ||
358 | EXPORT_SYMBOL(ssp_config); | ||
359 | |||
360 | MODULE_DESCRIPTION("PXA SSP driver"); | ||
361 | MODULE_AUTHOR("Liam Girdwood"); | ||
362 | MODULE_LICENSE("GPL"); | ||
363 | |||