Sync usage with man page.
[netbsd-mini2440.git] / sys / arch / arm / imx / imx_pcic.c
bloba519eaec98ccf60d80e541fc74831ae97a07bb73
1 /* $Id: imx_pcic.c,v 1.3 2008/06/30 00:46:41 perry Exp $ */
3 /*
4 * IMX CF interface to pcic/pcmcia
5 * derived from pxa2x0_pcic
6 * Sun Apr 1 21:42:37 PDT 2007
7 */
9 /* $NetBSD: imx_pcic.c,v 1.2 2008/04/27 18:58:44 matt Exp $ */
10 /* $OpenBSD: pxa2x0_pcic.c,v 1.17 2005/12/14 15:08:51 uwe Exp $ */
13 * Copyright (c) 2005 Dale Rahn <drahn@openbsd.org>
15 * Permission to use, copy, modify, and distribute this software for any
16 * purpose with or without fee is hereby granted, provided that the above
17 * copyright notice and this permission notice appear in all copies.
19 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
20 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
21 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
22 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
23 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
24 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
25 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
28 #include <sys/cdefs.h>
29 __KERNEL_RCSID(0, "$Id: imx_pcic.c,v 1.3 2008/06/30 00:46:41 perry Exp $");
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/device.h>
34 #include <sys/kernel.h>
35 #include <sys/kthread.h>
36 #include <sys/malloc.h>
38 #include <uvm/uvm.h>
40 #include <machine/bus.h>
41 #include <machine/intr.h>
43 #include <dev/pcmcia/pcmciareg.h>
44 #include <dev/pcmcia/pcmciavar.h>
45 #include <dev/pcmcia/pcmciachip.h>
47 #ifdef NOTYET
48 #include <arm/imx/imx_gpio.h>
49 #endif
50 #include <arm/imx/imx_pcic.h>
52 static int imx_pcic_print(void *, const char *);
54 static void imx_pcic_event_thread(void *);
55 #ifdef NOTYET
56 static void imx_pcic_event_process(struct imx_pcic_socket *);
57 static void imx_pcic_attach_card(struct imx_pcic_socket *);
58 #endif
59 #ifdef NOTYET
60 static void imx_pcic_detach_card(struct imx_pcic_socket *, int);
61 #endif
63 static int imx_pcic_mem_alloc(pcmcia_chipset_handle_t, bus_size_t,
64 struct pcmcia_mem_handle *);
65 static void imx_pcic_mem_free(pcmcia_chipset_handle_t,
66 struct pcmcia_mem_handle *);
67 static int imx_pcic_mem_map(pcmcia_chipset_handle_t, int, bus_addr_t,
68 bus_size_t, struct pcmcia_mem_handle *, bus_size_t *, int *);
69 static void imx_pcic_mem_unmap(pcmcia_chipset_handle_t, int);
71 static int imx_pcic_io_alloc(pcmcia_chipset_handle_t, bus_addr_t,
72 bus_size_t, bus_size_t, struct pcmcia_io_handle *);
73 static void imx_pcic_io_free(pcmcia_chipset_handle_t,
74 struct pcmcia_io_handle *);
75 static int imx_pcic_io_map(pcmcia_chipset_handle_t, int,
76 bus_addr_t, bus_size_t, struct pcmcia_io_handle *, int *);
77 static void imx_pcic_io_unmap(pcmcia_chipset_handle_t, int);
79 static void *imx_pcic_intr_establish(pcmcia_chipset_handle_t,
80 struct pcmcia_function *, int, int (*)(void *), void *);
81 static void imx_pcic_intr_disestablish(pcmcia_chipset_handle_t, void *);
83 static void imx_pcic_socket_enable(pcmcia_chipset_handle_t);
84 static void imx_pcic_socket_disable(pcmcia_chipset_handle_t);
85 static void imx_pcic_socket_settype(pcmcia_chipset_handle_t, int);
88 * PCMCIA chipset methods
90 static struct pcmcia_chip_functions imx_pcic_pcmcia_functions = {
91 imx_pcic_mem_alloc,
92 imx_pcic_mem_free,
93 imx_pcic_mem_map,
94 imx_pcic_mem_unmap,
96 imx_pcic_io_alloc,
97 imx_pcic_io_free,
98 imx_pcic_io_map,
99 imx_pcic_io_unmap,
101 imx_pcic_intr_establish,
102 imx_pcic_intr_disestablish,
104 imx_pcic_socket_enable,
105 imx_pcic_socket_disable,
106 imx_pcic_socket_settype,
109 #define IMX_MEMCTL_BASE 0x08000000 /* XXX */
110 #define IMX_MEMCTL_SIZE 0x00000010 /* XXX */
111 #define IMX_PCIC_SOCKET_BASE 0x08009000 /* XXX */
112 #define IMX_PCIC_SOCKET_OFFSET 0x00000000 /* XXX */
113 #define IMX_PCIC_ATTR_OFFSET 0x00000800 /* XXX 5912 */
114 #define IMX_PCIC_COMMON_OFFSET 0x00000000 /* XXX 5912 */
119 * PCMCIA Helpers
121 static int
122 imx_pcic_mem_alloc(pcmcia_chipset_handle_t pch, bus_size_t size,
123 struct pcmcia_mem_handle *pmh)
125 struct imx_pcic_socket *so = (struct imx_pcic_socket *)pch;
127 /* All we need is the bus space tag */
128 memset(pmh, 0, sizeof(*pmh));
129 pmh->memt = so->sc->sc_iot;
131 return 0;
134 static void
135 imx_pcic_mem_free(pcmcia_chipset_handle_t pch, struct pcmcia_mem_handle *pmh)
138 /* Nothing to do */
141 static int
142 imx_pcic_mem_map(pcmcia_chipset_handle_t pch, int kind, bus_addr_t card_addr,
143 bus_size_t size, struct pcmcia_mem_handle *pmh, bus_size_t *offsetp,
144 int *windowp)
146 struct imx_pcic_socket *so = (struct imx_pcic_socket *)pch;
147 int error;
148 bus_addr_t pa;
150 printf("%s: card_addr %lx\n", __func__, card_addr);
151 pa = trunc_page(card_addr);
152 *offsetp = card_addr - pa;
153 printf("%s: offset %lx\n", __func__, *offsetp);
154 size = round_page(card_addr + size) - pa;
155 pmh->realsize = size;
157 pa += IMX_PCIC_SOCKET_BASE;
158 pa += IMX_PCIC_SOCKET_OFFSET * so->socket;
159 printf("%s: pa %lx\n", __func__, pa);
160 printf("%s: kind %x\n", __func__, kind);
162 switch (kind & ~PCMCIA_WIDTH_MEM_MASK) {
163 case PCMCIA_MEM_ATTR:
164 pa += IMX_PCIC_ATTR_OFFSET;
165 break;
166 case PCMCIA_MEM_COMMON:
167 pa += IMX_PCIC_COMMON_OFFSET;
168 break;
169 default:
170 panic("imx_pcic_mem_map: bogus kind");
173 printf("%s: pa %lx\n", __func__, pa);
174 Debugger();
175 error = bus_space_map(so->sc->sc_iot, pa, size, 0, &pmh->memh);
176 if (error)
177 return error;
179 *windowp = (int)pmh->memh;
180 return 0;
183 static void
184 imx_pcic_mem_unmap(pcmcia_chipset_handle_t pch, int window)
186 struct imx_pcic_socket *so = (struct imx_pcic_socket *)pch;
188 bus_space_unmap(so->sc->sc_iot, (bus_addr_t)window, 4096); /* XXX */
191 static int
192 imx_pcic_io_alloc(pcmcia_chipset_handle_t pch, bus_addr_t start,
193 bus_size_t size, bus_size_t align, struct pcmcia_io_handle *pih)
195 struct imx_pcic_socket *so = (struct imx_pcic_socket *)pch;
196 bus_addr_t pa;
197 int error;
199 memset(pih, 0, sizeof(*pih));
200 pih->iot = so->sc->sc_iot;
201 pih->addr = start;
202 pih->size = size;
204 pa = pih->addr;
205 pa += IMX_PCIC_SOCKET_BASE;
206 pa += IMX_PCIC_SOCKET_OFFSET * so->socket;
208 /* XXX Are we ignoring alignment constraints? */
209 error = bus_space_map(so->sc->sc_iot, pa, size, 0, &pih->ioh);
211 return error;
214 static void
215 imx_pcic_io_free(pcmcia_chipset_handle_t pch, struct pcmcia_io_handle *pih)
217 struct imx_pcic_socket *so = (struct imx_pcic_socket *)pch;
219 bus_space_unmap(so->sc->sc_iot, pih->ioh, pih->size);
222 static int
223 imx_pcic_io_map(pcmcia_chipset_handle_t pch, int width, bus_addr_t offset,
224 bus_size_t size, struct pcmcia_io_handle *pih, int *windowp)
227 return 0;
230 static void
231 imx_pcic_io_unmap(pcmcia_chipset_handle_t pch, int window)
234 /* Nothing to do */
237 static void *
238 imx_pcic_intr_establish(pcmcia_chipset_handle_t pch,
239 struct pcmcia_function *pf, int ipl, int (*fct)(void *), void *arg)
241 struct imx_pcic_socket *so = (struct imx_pcic_socket *)pch;
242 /* XXX need to check if something should be done here */
244 return (*so->pcictag->intr_establish)(so, ipl, fct, arg);
247 static void
248 imx_pcic_intr_disestablish(pcmcia_chipset_handle_t pch, void *ih)
250 struct imx_pcic_socket *so = (struct imx_pcic_socket *)pch;
252 (*so->pcictag->intr_disestablish)(so, ih);
255 static void
256 imx_pcic_socket_enable(pcmcia_chipset_handle_t pch)
258 #ifdef NOTYET
259 struct imx_pcic_socket *so = (struct imx_pcic_socket *)pch;
260 int i;
262 /* Power down the card and socket before setting the voltage. */
263 (*so->pcictag->write)(so, IMX_PCIC_CARD_POWER, IMX_PCIC_POWER_OFF);
264 (*so->pcictag->set_power)(so, IMX_PCIC_POWER_OFF);
267 * Wait 300ms until power fails (Tpf). Then, wait 100ms since
268 * we are changing Vcc (Toff).
270 delay((300 + 100) * 1000);
272 /* Power up the socket and card at appropriate voltage. */
273 if (so->power_capability & IMX_PCIC_POWER_5V) {
274 (*so->pcictag->set_power)(so, IMX_PCIC_POWER_5V);
275 (*so->pcictag->write)(so, IMX_PCIC_CARD_POWER,
276 IMX_PCIC_POWER_5V);
277 } else {
278 (*so->pcictag->set_power)(so, IMX_PCIC_POWER_3V);
279 (*so->pcictag->write)(so, IMX_PCIC_CARD_POWER,
280 IMX_PCIC_POWER_3V);
284 * Wait 100ms until power raise (Tpr) and 20ms to become
285 * stable (Tsu(Vcc)).
287 * Some machines require some more time to be settled
288 * (another 200ms is added here).
290 delay((100 + 20 + 200) * 1000);
292 /* Hold RESET at least 10us. */
293 (*so->pcictag->write)(so, IMX_PCIC_CARD_RESET, 1);
294 delay(10);
295 /* XXX wrong, but lets TE-CF100 cards work for some reason. */
296 delay(3000);
297 (*so->pcictag->write)(so, IMX_PCIC_CARD_RESET, 0);
299 /* Wait 20ms as per PC Card standard (r2.01) section 4.3.6. */
300 delay(20 * 1000);
302 /* Wait for the card to become ready. */
303 for (i = 0; i < 10000; i++) {
304 if ((*so->pcictag->read)(so, IMX_PCIC_CARD_READY))
305 break;
306 delay(500);
308 #else
309 printf("%s: (stubbed)\n", __func__);
310 #endif /* NOTYET */
313 static void
314 imx_pcic_socket_disable(pcmcia_chipset_handle_t pch)
316 #ifdef NOTYET
317 struct imx_pcic_socket *so = (struct imx_pcic_socket *)pch;
319 #ifdef PCICDEBUG
320 printf("imx_pcic_socket_disable: socket %d\n", so->socket);
321 #endif
323 /* Power down the card and socket. */
324 (*so->pcictag->write)(so, IMX_PCIC_CARD_POWER, IMX_PCIC_POWER_OFF);
325 (*so->pcictag->set_power)(so, IMX_PCIC_POWER_OFF);
326 #endif /* NOTYET */
329 static void
330 imx_pcic_socket_settype(pcmcia_chipset_handle_t pch, int type)
333 #ifdef PCICDEBUG
334 printf("imx_pcic_socket_settype: type=%d",type);
336 switch (type) {
337 case PCMCIA_IFTYPE_MEMORY:
338 printf("(Memory)\n");
339 break;
340 case PCMCIA_IFTYPE_IO:
341 printf("(I/O)\n");
342 break;
343 default:
344 printf("(unknown)\n");
345 break;
347 #endif
351 * Attachment and initialization
353 static int
354 imx_pcic_print(void *aux, const char *name)
357 return UNCONF;
360 void
361 imx_pcic_attach_common(struct imx_pcic_softc *sc,
362 void (*socket_setup_hook)(struct imx_pcic_socket *))
364 struct pcmciabus_attach_args paa;
365 struct imx_pcic_socket *so;
366 int s[IMX_PCIC_NSLOT];
367 int i;
369 printf(": %d slot%s\n", sc->sc_nslots, sc->sc_nslots < 2 ? "" : "s");
371 if (sc->sc_nslots == 0) {
372 aprint_error("%s: can't attach\n", sc->sc_dev.dv_xname);
373 return;
376 if (bus_space_map(sc->sc_iot, IMX_MEMCTL_BASE, IMX_MEMCTL_SIZE,
377 0, &sc->sc_memctl_ioh)) {
378 aprint_error("%s: failed to map MEMCTL\n", sc->sc_dev.dv_xname);
379 return;
382 #if 0
383 /* Clear CIT (card present) and set NOS correctly. */
384 bus_space_write_4(sc->sc_iot, sc->sc_memctl_ioh, MEMCTL_MECR,
385 (sc->sc_nslots == 2) ? MECR_NOS : 0);
386 #endif
388 if (sc->sc_flags & PPF_REVERSE_ORDER) {
389 for (i = 0; i < sc->sc_nslots; i++) {
390 s[i] = sc->sc_nslots - 1 - i;
392 } else {
393 for (i = 0; i < sc->sc_nslots; i++) {
394 s[i] = i;
398 for (i = 0; i < sc->sc_nslots; i++) {
399 so = &sc->sc_socket[s[i]];
400 so->sc = sc;
401 so->socket = s[i];
402 so->flags = 0;
404 (*socket_setup_hook)(so);
406 paa.paa_busname = "pcmcia";
407 paa.pct = (pcmcia_chipset_tag_t)&imx_pcic_pcmcia_functions;
408 paa.pch = (pcmcia_chipset_handle_t)so;
409 printf("%s: sc_pa %lx\n", __func__, sc->sc_pa);
410 paa.iobase = sc->sc_pa;
411 paa.iosize = 0x2000;
413 so->pcmcia = config_found_ia(&sc->sc_dev, "pcmciabus", &paa,
414 imx_pcic_print);
416 #ifdef NOTYET
417 imx_gpio_set_direction(sc->sc_irqpin[s[i]], GPIO_IN);
418 imx_gpio_set_direction(sc->sc_irqcfpin[s[i]], GPIO_IN);
420 /* Card slot interrupt */
421 so->irq = imx_gpio_intr_establish(sc->sc_irqcfpin[s[i]],
422 IST_EDGE_BOTH, IPL_BIO /* XXX */, "pcic",
423 imx_pcic_intr, so);
425 /* GPIO pin for interrupt */
426 so->irqpin = sc->sc_irqpin[s[i]];
427 #else
428 so->irqpin = sc->sc_irqpin[s[i]];
429 printf("%s: slot %d, irqpin %d\n", __func__, s[i], sc->sc_irqpin[s[i]]);
430 #endif /* NOTYET */
432 if (kthread_create(PRI_NONE, 0, NULL,
433 imx_pcic_event_thread, so, &so->event_thread,
434 "%s,%d", sc->sc_dev.dv_xname, so->socket) != 0) {
435 printf("%s: unable to create event thread for %d\n",
436 sc->sc_dev.dv_xname, so->socket);
442 * Card slot interrupt handling
445 imx_pcic_intr(void *arg)
447 struct imx_pcic_socket *so = (struct imx_pcic_socket *)arg;
449 (*so->pcictag->clear_intr)(so);
450 wakeup(so);
452 return 1;
455 static void
456 imx_pcic_event_thread(void *arg)
458 #ifdef NOTYET
459 struct imx_pcic_socket *sock = (struct imx_pcic_socket *)arg;
460 u_int cs;
461 int present;
463 while (sock->sc->sc_shutdown == 0) {
464 (void) tsleep(sock, PWAIT, "imx_pcicev", 0);
466 /* sleep .25s to avoid chattering interrupts */
467 (void) tsleep((void *)sock, PWAIT, "imx_pcicss", hz/4);
469 cs = (*sock->pcictag->read)(sock, IMX_PCIC_CARD_STATUS);
470 present = sock->flags & IMX_PCIC_FLAG_CARDP;
471 if ((cs == IMX_PCIC_CARD_VALID) == (present == 1)) {
472 continue; /* state unchanged */
475 /* XXX Do both? */
476 imx_pcic_event_process(sock);
478 sock->event_thread = NULL;
480 /* In case parent is waiting for us to exit. */
481 wakeup(sock->sc);
482 kthread_exit(0);
483 #endif /* NOTYET */
486 #ifdef NOTYET
487 static void
488 imx_pcic_event_process(struct imx_pcic_socket *sock)
490 u_int cs;
492 cs = (*sock->pcictag->read)(sock, IMX_PCIC_CARD_STATUS);
493 if (cs == IMX_PCIC_CARD_VALID) {
494 if (!(sock->flags & IMX_PCIC_FLAG_CARDP)) {
495 imx_pcic_attach_card(sock);
497 } else {
498 if ((sock->flags & IMX_PCIC_FLAG_CARDP)) {
499 imx_pcic_detach_card(sock, DETACH_FORCE);
504 static void
505 imx_pcic_attach_card(struct imx_pcic_socket *h)
508 if (h->flags & IMX_PCIC_FLAG_CARDP)
509 panic("pcic_attach_card: already attached");
510 h->flags |= IMX_PCIC_FLAG_CARDP;
513 /* call the MI attach function */
514 pcmcia_card_attach(h->pcmcia);
516 #endif /* NOTYET */
518 #ifdef NOTYET
519 static void
520 imx_pcic_detach_card(struct imx_pcic_socket *h, int flags)
522 struct imx_pcic_softc *sc = h->sc;
523 int i;
525 if (h->flags & IMX_PCIC_FLAG_CARDP) {
526 h->flags &= ~IMX_PCIC_FLAG_CARDP;
528 /* call the MI detach function */
529 pcmcia_card_detach(h->pcmcia, flags);
532 /* Clear CIT if no other card is present. */
533 for (i = 0; i < sc->sc_nslots; i++) {
534 if (sc->sc_socket[i].flags & IMX_PCIC_FLAG_CARDP) {
535 return;
539 #endif /* NOTYET */