summaryrefslogtreecommitdiff
path: root/sys/arch
diff options
context:
space:
mode:
authorDale Rahn <drahn@cvs.openbsd.org>2004-12-30 23:48:18 +0000
committerDale Rahn <drahn@cvs.openbsd.org>2004-12-30 23:48:18 +0000
commit5964e793205ec0e85b0e4a9fdf19a469788eb6f4 (patch)
treef04bfe7e161a82a6e470047d2ae8eddcdc31c887 /sys/arch
parent2178e27bc6241f5a6af886559429e72753101356 (diff)
pcmcia (CF) adapter. Needs to be split into seperate layers so
that the offchip portion (not on pxa*) is not embedded here, interrupt routing is performed offchip.
Diffstat (limited to 'sys/arch')
-rw-r--r--sys/arch/arm/xscale/pxa2x0_pcic.c579
1 files changed, 579 insertions, 0 deletions
diff --git a/sys/arch/arm/xscale/pxa2x0_pcic.c b/sys/arch/arm/xscale/pxa2x0_pcic.c
new file mode 100644
index 00000000000..c59496f3750
--- /dev/null
+++ b/sys/arch/arm/xscale/pxa2x0_pcic.c
@@ -0,0 +1,579 @@
+/* $OpenBSD: pxa2x0_pcic.c,v 1.1 2004/12/30 23:48:17 drahn Exp $ */
+/*
+ * Copyright (c) Dale Rahn <drahn@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/timeout.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/malloc.h>
+#include <uvm/uvm.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/pcmcia/pcmciareg.h>
+#include <dev/pcmcia/pcmciavar.h>
+#include <dev/pcmcia/pcmciachip.h>
+#include <arm/xscale/pxa2x0var.h>
+#include <arm/xscale/pxa2x0_gpio.h>
+#include <arm/xscale/pxapcicvar.h>
+
+int pxapcic_mem_alloc(pcmcia_chipset_handle_t, bus_size_t,
+ struct pcmcia_mem_handle *);
+void pxapcic_mem_free(pcmcia_chipset_handle_t,
+ struct pcmcia_mem_handle *);
+int pxapcic_mem_map(pcmcia_chipset_handle_t, int, bus_addr_t,
+ bus_size_t, struct pcmcia_mem_handle *, bus_addr_t *, int *);
+void pxapcic_mem_unmap(pcmcia_chipset_handle_t, int);
+int pxapcic_io_alloc(pcmcia_chipset_handle_t, bus_addr_t,
+ bus_size_t, bus_size_t, struct pcmcia_io_handle *);
+void pxapcic_io_free(pcmcia_chipset_handle_t,
+ struct pcmcia_io_handle *);
+int pxapcic_io_map(pcmcia_chipset_handle_t, int,
+ bus_addr_t, bus_size_t, struct pcmcia_io_handle *, int *);
+void pxapcic_io_unmap(pcmcia_chipset_handle_t, int);
+void *pxapcic_intr_establish(pcmcia_chipset_handle_t,
+ struct pcmcia_function *, int, int (*)(void *), void *, char *);
+void pxapcic_intr_disestablish(pcmcia_chipset_handle_t, void *);
+void pxapcic_socket_enable(pcmcia_chipset_handle_t);
+void pxapcic_socket_disable(pcmcia_chipset_handle_t);
+
+void pxapcic_event_thread(void *);
+
+void pxapcic_delay(int, const char *);
+
+int pxapcic_match(struct device *, void *, void *);
+void pxapcic_attach(struct device *, struct device *, void *);
+void pxapcic_create_event_thread(void *arg);
+int pxapcic_submatch(struct device *parent, void *, void *aux);
+int pxapcic_print(void *aux, const char *name);
+int pxapcic_intr(void *arg);
+
+void pxapcic_event_process(struct pxapcic_socket *);
+
+void pxapcic_attach_card(struct pxapcic_socket *h);
+void pxapcic_detach_card(struct pxapcic_socket *h, int flags);
+
+int pxapcic_intr_detect(void *arg);
+
+/* DONT CONFIGURE CF slot 1 for now */
+#define NUM_CF_CARDS 1
+
+struct cfattach pxapcic_ca = {
+ sizeof(struct pxapcic_softc), pxapcic_match, pxapcic_attach
+};
+
+struct cfdriver pxapcic_cd = {
+ NULL, "pxapcic", DV_DULL
+};
+
+struct pcmcia_chip_functions pxapcic_pcmcia_functions = {
+ pxapcic_mem_alloc,
+ pxapcic_mem_free,
+ pxapcic_mem_map,
+ pxapcic_mem_unmap,
+
+ pxapcic_io_alloc,
+ pxapcic_io_free,
+ pxapcic_io_map,
+ pxapcic_io_unmap,
+
+ pxapcic_intr_establish,
+ pxapcic_intr_disestablish,
+
+ pxapcic_socket_enable,
+ pxapcic_socket_disable,
+};
+
+
+
+int
+pxapcic_mem_alloc(pch, size, pmh)
+ pcmcia_chipset_handle_t pch;
+ bus_size_t size;
+ struct pcmcia_mem_handle *pmh;
+{
+ struct pxapcic_socket *so = pch;
+
+ /* All we need is bus space tag */
+ memset(pmh, 0, sizeof(*pmh));
+ pmh->memt = so->sc->sc_iot;
+ return (0);
+}
+
+void
+pxapcic_mem_free(pch, pmh)
+ pcmcia_chipset_handle_t pch;
+ struct pcmcia_mem_handle *pmh;
+{
+}
+
+int
+pxapcic_mem_map(pch, kind, card_addr, size, pmh, offsetp, windowp)
+ pcmcia_chipset_handle_t pch;
+ int kind;
+ bus_addr_t card_addr;
+ bus_size_t size;
+ struct pcmcia_mem_handle *pmh;
+ bus_addr_t *offsetp;
+ int *windowp;
+{
+ struct pxapcic_socket *so = pch;
+ int error;
+ bus_addr_t pa;
+
+ pa = trunc_page(card_addr);
+ *offsetp = card_addr - pa;
+ size = round_page(card_addr + size) - pa;
+ pmh->realsize = size;
+
+#define PXAPCIC_BASE_OFFSET 0x20000000
+#define PXAPCIC_SOCKET_OFFSET 0x10000000
+#define PXAPCIC_ATTR_OFFSET 0x08000000
+#define PXAPCIC_COMMON_OFFSET 0x0C000000
+
+ pa += PXAPCIC_BASE_OFFSET;
+ pa += PXAPCIC_SOCKET_OFFSET * so->socket;
+
+ switch (kind & ~PCMCIA_WIDTH_MEM_MASK) {
+ case PCMCIA_MEM_ATTR:
+ pa += PXAPCIC_ATTR_OFFSET;
+ break;
+ case PCMCIA_MEM_COMMON:
+ pa += PXAPCIC_COMMON_OFFSET;
+ break;
+ default:
+ panic("pxapcic_mem_map: bogus kind");
+ }
+
+ error = bus_space_map(so->sc->sc_iot, pa, size, 0, &pmh->memh);
+ if (! error)
+ *windowp = (int)pmh->memh;
+ return (error);
+}
+
+void
+pxapcic_mem_unmap(pch, window)
+ pcmcia_chipset_handle_t pch;
+ int window;
+{
+ struct pxapcic_socket *so = pch;
+
+ bus_space_unmap(so->sc->sc_iot, (bus_addr_t)window, 4096); /* XXX */
+}
+
+int
+pxapcic_io_alloc(pch, start, size, align, pih)
+ pcmcia_chipset_handle_t pch;
+ bus_addr_t start;
+ bus_size_t size;
+ bus_size_t align;
+ struct pcmcia_io_handle *pih;
+{
+ struct pxapcic_socket *so = pch;
+ int error;
+ bus_addr_t pa;
+
+ memset(pih, 0, sizeof(*pih));
+ pih->iot = so->sc->sc_iot;
+ pih->addr = start;
+ pih->size = size;
+
+ pa = pih->addr;
+ pa += PXAPCIC_BASE_OFFSET;
+ pa += PXAPCIC_SOCKET_OFFSET * so->socket;
+
+#if 0
+ printf("pxapcic_io_alloc: %x %x\n", (unsigned int)pa,
+ (unsigned int)size);
+#endif
+ /* XXX Are we ignoring alignment constraints? */
+ error = bus_space_map(so->sc->sc_iot, pa, size, 0, &pih->ioh);
+
+ return (error);
+}
+
+void
+pxapcic_io_free(pch, pih)
+ pcmcia_chipset_handle_t pch;
+ struct pcmcia_io_handle *pih;
+{
+ struct pxapcic_socket *so = pch;
+
+ bus_space_unmap(so->sc->sc_iot, pih->ioh, pih->size);
+}
+
+int
+pxapcic_io_map(pch, width, offset, size, pih, windowp)
+ pcmcia_chipset_handle_t pch;
+ int width;
+ bus_addr_t offset;
+ bus_size_t size;
+ struct pcmcia_io_handle *pih;
+ int *windowp;
+{
+ return (0);
+}
+
+void pxapcic_io_unmap(pch, window)
+ pcmcia_chipset_handle_t pch;
+ int window;
+{
+}
+
+void *
+pxapcic_intr_establish(pch, pf, ipl, fct, arg, name)
+ pcmcia_chipset_handle_t pch;
+ struct pcmcia_function *pf;
+ int ipl;
+ int (*fct)(void *);
+ void *arg;
+ char *name;
+{
+ struct pxapcic_socket *so = pch;
+ struct pxapcic_softc *sc = so->sc;
+
+ /* XXX need to check if something should be done here */
+
+ return pxa2x0_gpio_intr_establish(sc->sc_gpio, IST_EDGE_FALLING,
+ /* IST_EDGE_RISING,*/
+ ipl, fct, arg, name);
+}
+
+void
+pxapcic_intr_disestablish(pch, ih)
+ pcmcia_chipset_handle_t pch;
+ void *ih;
+{
+ pxa2x0_gpio_intr_disestablish(ih);
+}
+
+void
+pxapcic_socket_enable(pch)
+ pcmcia_chipset_handle_t pch;
+{
+ /* XXX */
+}
+
+void
+pxapcic_socket_disable(pch)
+ pcmcia_chipset_handle_t pch;
+{
+ /* XXX */
+}
+
+#if 0
+void
+pxapcic_socket_settype(pch, type)
+ pcmcia_chipset_handle_t pch;
+ int type;
+{
+
+ /* XXX */
+}
+#endif
+
+#if 0
+void
+pxapcic_socket_setup(struct pxapcic_socket *sp)
+{
+ /*
+ sp->power_capability = PXAPCIC_POWER_3V;
+ */
+ sp->pcictag = &pxapcic_sacpcic_functions;
+}
+#endif
+
+#if 0
+void
+pxapcic_set_power(struct pxapcic_socket *so, int arg)
+{
+ /* 3 volt only, not supported - XXX */
+}
+
+#endif
+int
+pxapcic_match(struct device *parent, void *v, void *aux)
+{
+ return (1);
+}
+
+void
+pxapcic_event_thread(void *arg)
+{
+ struct pxapcic_softc *sc = arg;
+ u_int16_t csr;
+ int present;
+
+ while (sc->sc_shutdown == 0) {
+ /* sleep .25s to avoid chatterling interrupts */
+
+ (void) tsleep((caddr_t)sc, PWAIT,
+ "pxapcicss", hz/4);
+
+ csr = bus_space_read_2(sc->sc_iot, sc->sc_scooph,
+ SCOOP_REG_CSR);
+
+ present = sc->sc_socket[0].flags & PXAPCIC_FLAG_CARDP;
+
+ if (((csr & SCP_CSR_MISSING) == 0) == (present == 1))
+ continue; /* state unchanged */
+
+
+#if 0
+ printf("pxapcic_event_thread\n");
+
+ printf("SCOOP_CSR 0x%04x\n",
+ bus_space_read_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_CSR));
+ printf("SCOOP_CDR 0x%04x\n",
+ bus_space_read_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_CDR));
+ printf("SCOOP_CPR 0x%04x\n",
+ bus_space_read_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_CPR));
+ printf("SCOOP_CCR 0x%04x\n",
+ bus_space_read_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_CCR));
+ printf("SCOOP_MCR 0x%04x\n",
+ bus_space_read_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_MCR));
+ printf("SCOOP_IMR 0x%04x\n",
+ bus_space_read_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_IMR));
+ printf("SCOOP_IRR 0x%04x\n",
+ bus_space_read_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_IRR));
+#endif
+
+ /* XXX Do both? */
+ pxapcic_event_process(&sc->sc_socket[0]);
+#if NUM_CF_CARDS > 1
+ pxapcic_event_process(&sc->sc_socket[1]);
+#endif
+ }
+
+ sc->sc_event_thread = NULL;
+
+ /* In case parent is waiting for us to exit. */
+ wakeup(sc);
+
+ kthread_exit(0);
+}
+
+void
+pxapcic_attach_card(struct pxapcic_socket *h)
+{
+ if (h->flags & PXAPCIC_FLAG_CARDP)
+ panic("pcic_attach_card: already attached");
+ h->flags |= PXAPCIC_FLAG_CARDP;
+
+ /* call the MI attach function */
+ pcmcia_card_attach(h->pcmcia);
+}
+
+void
+pxapcic_detach_card(struct pxapcic_socket *h, int flags)
+{
+ if (h->flags & PXAPCIC_FLAG_CARDP) {
+ h->flags &= ~PXAPCIC_FLAG_CARDP;
+
+ /* call the MI detach function */
+ pcmcia_card_detach(h->pcmcia, flags);
+ } else {
+ //DPRINTF(("pcic_detach_card: already detached"));
+ }
+}
+
+
+void pxapcic_event_process_st(void *h);
+void
+pxapcic_event_process_st(void *v)
+{
+ struct pxapcic_socket *h = v;
+ pxapcic_event_process(h);
+}
+void
+pxapcic_event_process(h)
+ struct pxapcic_socket *h;
+{
+ struct pxapcic_softc *sc = h->sc;
+ u_int16_t csr;
+
+ csr = bus_space_read_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_CSR);
+
+ switch (csr & SCP_CSR_MISSING) {
+ case 0: /* PRESENT */
+ //DPRINTF(("%s: insertion event\n", h->sc->dv_xname));
+ if (!(h->flags & PXAPCIC_FLAG_CARDP))
+ pxapcic_attach_card(h);
+ break;
+
+ case SCP_CSR_MISSING:
+ //DPRINTF(("%s: removal event\n", h->sc->dv_xname));
+ if ((h->flags & PXAPCIC_FLAG_CARDP))
+ pxapcic_detach_card(h, DETACH_FORCE);
+ break;
+ }
+}
+
+void
+pxapcic_create_event_thread(void *arg)
+{
+ struct pxapcic_softc *sc = arg;
+ u_int16_t csr;
+
+ csr = bus_space_read_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_CSR);
+
+ /* if there's a card there, then attach it */
+
+ switch (csr & SCP_CSR_MISSING) {
+ case 0: /* PRESENT */
+ pxapcic_attach_card(&sc->sc_socket[0]);
+ break;
+ default:
+ ;
+ }
+
+ if (kthread_create(pxapcic_event_thread, sc, &sc->sc_event_thread,
+ sc->sc_dev.dv_xname, "0")) {
+ printf("%s: unable to create event thread for %s\n",
+ sc->sc_dev.dv_xname, "0");
+ }
+ config_pending_decr();
+}
+
+int
+pxapcic_submatch(struct device *parent, void *match, void *aux)
+{
+ struct cfdata *cf = match;
+
+ return ((*cf->cf_attach->ca_match)(parent, cf, aux));
+
+}
+
+int
+pxapcic_print(void *aux, const char *name)
+{
+ return (UNCONF);
+}
+
+
+void
+pxapcic_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct pcmciabus_attach_args paa;
+ struct pxapcic_softc *sc = (struct pxapcic_softc *)self;
+ struct pxaip_attach_args *pxa = aux;
+ struct pxapcic_socket *so;
+ int i;
+ int error;
+ bus_addr_t pa = 0x10800000;
+ bus_size_t size = 0x100;
+
+ sc->sc_iot = pxa->pxa_iot;
+
+/*
+ pxa->pxa_addr;
+ pxa->pxa_size = 0x20;
+*/
+ sc->sc_shutdown = 0;
+
+ /* scoop? */
+ error = bus_space_map(sc->sc_iot, pa, size, 0, &sc->sc_scooph);
+
+ bus_space_write_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_IMR,
+ 0x00c0);
+
+
+#if 0
+ printf("SCOOP_CSR 0x%04x\n",
+ bus_space_read_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_CSR));
+ printf("SCOOP_CDR 0x%04x\n",
+ bus_space_read_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_CDR));
+ printf("SCOOP_CPR 0x%04x\n",
+ bus_space_read_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_CPR));
+ printf("SCOOP_CCR 0x%04x\n",
+ bus_space_read_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_CCR));
+ printf("SCOOP_MCR 0x%04x\n",
+ bus_space_read_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_MCR));
+ printf("SCOOP_IMR 0x%04x\n",
+ bus_space_read_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_IMR));
+ printf("SCOOP_IRR 0x%04x\n",
+ bus_space_read_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_IRR));
+#endif
+
+ bus_space_write_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_MCR, 0x0100);
+ bus_space_write_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_CDR, 0x0000);
+ bus_space_write_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_CPR, 0x0000);
+ bus_space_write_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_IMR, 0x0000);
+ bus_space_write_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_IRM, 0x00ff);
+ bus_space_write_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_ISR, 0x0000);
+ bus_space_write_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_IMR, 0x0000);
+
+ bus_space_write_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_CPR,
+ SCP_CPR_PWR|SCP_CPR_3V);
+
+ printf("\n");
+
+ for(i = 0; i < NUM_CF_CARDS; i++) {
+ so = &sc->sc_socket[i];
+ so->sc = sc;
+ so->socket = i;
+ so->flags = 0;
+
+ /* setup */
+
+ paa.paa_busname = "pcmcia";
+ paa.pct = (pcmcia_chipset_tag_t) &pxapcic_pcmcia_functions;
+ paa.pch = (pcmcia_chipset_handle_t)&sc->sc_socket[i];
+ paa.iobase = 0;
+ paa.iosize = 0x4000000;
+
+ so->pcmcia = config_found_sm(&sc->sc_dev, &paa, pxapcic_print,
+ pxapcic_submatch);
+ }
+
+ pxa2x0_gpio_set_function(14, GPIO_IN);
+ pxa2x0_gpio_set_function(17, GPIO_IN);
+
+
+ sc->sc_irq = pxa2x0_gpio_intr_establish(14 /*???*/, IST_EDGE_FALLING,
+ IPL_BIO /* XXX */, pxapcic_intr_detect, sc, sc->sc_dev.dv_xname);
+
+
+
+ bus_space_write_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_IMR, 0x00ce);
+ bus_space_write_2(sc->sc_iot, sc->sc_scooph, SCOOP_REG_MCR, 0x0111);
+
+
+ sc->sc_gpio = 17; /* GPIO pin for interrupt */
+
+ config_pending_incr();
+ kthread_create_deferred(pxapcic_create_event_thread, sc);
+
+}
+
+int
+pxapcic_intr_detect(void *arg)
+{
+ struct pxapcic_socket *so = arg;
+
+ /*
+ (so->pcictag->clear_intr)(so->socket);
+ */
+ wakeup(so->sc);
+ return 1;
+}
+
+