diff options
Diffstat (limited to 'sys/dev/pccbb')
-rw-r--r-- | sys/dev/pccbb/pccbb.c | 2234 | ||||
-rw-r--r-- | sys/dev/pccbb/pccbbdevid.h | 86 | ||||
-rw-r--r-- | sys/dev/pccbb/pccbbreg.h | 166 | ||||
-rw-r--r-- | sys/dev/pccbb/pccbbvar.h | 115 |
4 files changed, 2601 insertions, 0 deletions
diff --git a/sys/dev/pccbb/pccbb.c b/sys/dev/pccbb/pccbb.c new file mode 100644 index 0000000..1e9aa32 --- /dev/null +++ b/sys/dev/pccbb/pccbb.c @@ -0,0 +1,2234 @@ +/* + * Copyright (c) 2000,2001 Jonathan Chen. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Driver for PCI to Cardbus Bridge chips + * + * References: + * TI Datasheets: + * http://www-s.ti.com/cgi-bin/sc/generic2.cgi?family=PCI+CARDBUS+CONTROLLERS + * Much of the 16-bit PC Card compatibility code stolen from dev/pcic/i82365.c + * XXX and should be cleaned up to share as much as possible. + * + * Written by Jonathan Chen <jon@freebsd.org> + * The author would like to acknowledge: + * * HAYAKAWA Koichi: Author of the NetBSD code for the same thing + * * Warner Losh: Newbus/newcard guru and author of the pccard side of things + * * YAMAMOTO Shigeru: Author of another FreeBSD cardbus driver + * * David Cross: Author of the initial ugly hack for a specific cardbus card + */ + +#define CBB_DEBUG + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/kthread.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/sysctl.h> + +#include <sys/bus.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <pci/pcireg.h> +#include <pci/pcivar.h> +#include <machine/clock.h> + +#include <dev/pccard/pccardreg.h> +#include <dev/pccard/pccardvar.h> +#include <dev/pcic/i82365reg.h> + +#include <dev/pccbb/pccbbreg.h> +#include <dev/pccbb/pccbbvar.h> + +#include "power_if.h" +#include "card_if.h" +#include "pcib_if.h" + +#if defined CBB_DEBUG +#define DPRINTF(x) printf x +#define DEVPRINTF(x) device_printf x +#else +#define DPRINTF(x) +#define DEVPRINTF(x) +#endif + +#define PCI_MASK_CONFIG(DEV,REG,MASK,SIZE) \ + pci_write_config(DEV, REG, pci_read_config(DEV, REG, SIZE) MASK, SIZE) +#define PCI_MASK2_CONFIG(DEV,REG,MASK1,MASK2,SIZE) \ + pci_write_config(DEV, REG, ( \ + pci_read_config(DEV, REG, SIZE) MASK1) MASK2, SIZE) + +/* + * XXX all the pcic code really doesn't belong here and needs to be + * XXX migrated to its own file, shared with the 16-bit code + */ +#define PCIC_MASK(SC,REG,MASK) \ + PCIC_WRITE(SC,REG,PCIC_READ(SC,REG) MASK) +#define PCIC_MASK2(SC,REG,MASK,MASK2) \ + PCIC_WRITE(SC,REG,(PCIC_READ(SC,REG) MASK) MASK2) + +#define PCCBB_START_MEM 0x84000000 +#define PCCBB_START_IO 0x1000 + +struct pccbb_sclist { + struct pccbb_softc *sc; + STAILQ_ENTRY(pccbb_sclist) entries; +}; + +static STAILQ_HEAD(, pccbb_sclist) softcs; +static int softcs_init = 0; + +struct yenta_chipinfo { + uint32_t yc_id; + const char *yc_name; + int yc_chiptype; + int yc_flags; +} yc_chipsets[] = { + /* Texas Instruments chips */ + {PCI_DEVICE_ID_PCIC_TI1130, "TI1130 PCI-CardBus Bridge", CB_TI113X, + PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, + {PCI_DEVICE_ID_PCIC_TI1131, "TI1131 PCI-CardBus Bridge", CB_TI113X, + PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, + + {PCI_DEVICE_ID_PCIC_TI1211, "TI1211 PCI-CardBus Bridge", CB_TI12XX, + PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, + {PCI_DEVICE_ID_PCIC_TI1220, "TI1220 PCI-CardBus Bridge", CB_TI12XX, + PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, + {PCI_DEVICE_ID_PCIC_TI1221, "TI1221 PCI-CardBus Bridge", CB_TI12XX, + PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, + {PCI_DEVICE_ID_PCIC_TI1225, "TI1225 PCI-CardBus Bridge", CB_TI12XX, + PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, + {PCI_DEVICE_ID_PCIC_TI1250, "TI1250 PCI-CardBus Bridge", CB_TI12XX, + PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, + {PCI_DEVICE_ID_PCIC_TI1251, "TI1251 PCI-CardBus Bridge", CB_TI12XX, + PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, + {PCI_DEVICE_ID_PCIC_TI1251B,"TI1251B PCI-CardBus Bridge",CB_TI12XX, + PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, + {PCI_DEVICE_ID_PCIC_TI1410, "TI1410 PCI-CardBus Bridge", CB_TI12XX, + PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, + {PCI_DEVICE_ID_PCIC_TI1420, "TI1420 PCI-CardBus Bridge", CB_TI12XX, + PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, + {PCI_DEVICE_ID_PCIC_TI1450, "TI1450 PCI-CardBus Bridge", CB_TI12XX, + PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, + {PCI_DEVICE_ID_PCIC_TI1451, "TI1451 PCI-CardBus Bridge", CB_TI12XX, + PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, + {PCI_DEVICE_ID_PCIC_TI4410, "TI4410 PCI-CardBus Bridge", CB_TI12XX, + PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, + {PCI_DEVICE_ID_PCIC_TI4451, "TI4451 PCI-CardBus Bridge", CB_TI12XX, + PCCBB_PCIC_IO_RELOC | PCCBB_PCIC_MEM_32}, + + /* Ricoh chips */ + {PCI_DEVICE_ID_RICOH_RL5C465, "RF5C465 PCI-CardBus Bridge", + CB_RF5C46X, PCCBB_PCIC_MEM_32}, + {PCI_DEVICE_ID_RICOH_RL5C466, "RF5C466 PCI-CardBus Bridge", + CB_RF5C46X, PCCBB_PCIC_MEM_32}, + {PCI_DEVICE_ID_RICOH_RL5C475, "RF5C475 PCI-CardBus Bridge", + CB_RF5C47X, PCCBB_PCIC_MEM_32}, + {PCI_DEVICE_ID_RICOH_RL5C476, "RF5C476 PCI-CardBus Bridge", + CB_RF5C47X, PCCBB_PCIC_MEM_32}, + {PCI_DEVICE_ID_RICOH_RL5C478, "RF5C478 PCI-CardBus Bridge", + CB_RF5C47X, PCCBB_PCIC_MEM_32}, + + /* Toshiba products */ + {PCI_DEVICE_ID_TOSHIBA_TOPIC95, "ToPIC95 PCI-CardBus Bridge", + CB_TOPIC95, PCCBB_PCIC_MEM_32}, + {PCI_DEVICE_ID_TOSHIBA_TOPIC95B, "ToPIC95B PCI-CardBus Bridge", + CB_TOPIC95B, PCCBB_PCIC_MEM_32}, + {PCI_DEVICE_ID_TOSHIBA_TOPIC97, "ToPIC97 PCI-CardBus Bridge", + CB_TOPIC97, PCCBB_PCIC_MEM_32}, + {PCI_DEVICE_ID_TOSHIBA_TOPIC100, "ToPIC100 PCI-CardBus Bridge", + CB_TOPIC97, PCCBB_PCIC_MEM_32}, + + /* Cirrus Logic */ + {PCI_DEVICE_ID_PCIC_CLPD6832, "CLPD6832 PCI-CardBus Bridge", + CB_CIRRUS, PCCBB_PCIC_MEM_32}, + {PCI_DEVICE_ID_PCIC_CLPD6833, "CLPD6833 PCI-CardBus Bridge", + CB_CIRRUS, PCCBB_PCIC_MEM_32}, + + /* 02Micro */ + {PCI_DEVICE_ID_PCIC_OZ6832, "O2Mirco OZ6832/6833 PCI-CardBus Bridge", + CB_CIRRUS, PCCBB_PCIC_MEM_32}, + {PCI_DEVICE_ID_PCIC_OZ6860, "O2Mirco OZ6836/6860 PCI-CardBus Bridge", + CB_CIRRUS, PCCBB_PCIC_MEM_32}, + {PCI_DEVICE_ID_PCIC_OZ6872, "O2Mirco OZ6812/6872 PCI-CardBus Bridge", + CB_CIRRUS, PCCBB_PCIC_MEM_32}, + + /* sentinel */ + {0 /* null id */, "unknown", + CB_UNKNOWN, 0}, +}; + +/* sysctl vars */ +SYSCTL_NODE(_hw, OID_AUTO, pccbb, CTLFLAG_RD, 0, "PCCBB parameters"); + +/* There's no way to say TUNEABLE_LONG to get the right types */ +u_long pccbb_start_mem = PCCBB_START_MEM; +TUNABLE_INT("hw.pccbb.start_memory", (int *)&pccbb_start_mem); +SYSCTL_ULONG(_hw_pccbb, OID_AUTO, start_mem, CTLFLAG_RD, + &pccbb_start_mem, PCCBB_START_MEM, + "Starting address for memory allocations"); + +static int cb_chipset(uint32_t pci_id, const char **namep, int *flagp); +static int pccbb_probe(device_t brdev); +static void pccbb_chipinit(struct pccbb_softc *sc); +static int pccbb_attach(device_t brdev); +static int pccbb_detach(device_t brdev); +static int pccbb_shutdown(device_t brdev); +static void pccbb_driver_added(device_t brdev, driver_t *driver); +static void pccbb_child_detached(device_t brdev, device_t child); +static int pccbb_card_reprobe(device_t brdev, device_t busdev); +static void pccbb_event_thread(void *arg); +static void pccbb_create_event_thread(struct pccbb_softc *sc); +static void pccbb_start_threads(void *arg); +static void pccbb_insert(struct pccbb_softc *sc); +static void pccbb_removal(struct pccbb_softc *sc); +static void pccbb_intr(void *arg); +static int pccbb_detect_voltage(device_t brdev); +static int pccbb_power(device_t brdev, int volts); +static void pccbb_cardbus_reset(device_t brdev); +static int pccbb_cardbus_power_enable_socket(device_t brdev, + device_t child); +static void pccbb_cardbus_power_disable_socket(device_t brdev, + device_t child); +static int pccbb_cardbus_io_open(device_t brdev, int win, uint32_t start, + uint32_t end); +static int pccbb_cardbus_mem_open(device_t brdev, int win, + uint32_t start, uint32_t end); +static void pccbb_cardbus_auto_open(struct pccbb_softc *sc, int type); +static int pccbb_cardbus_activate_resource(device_t brdev, device_t child, + int type, int rid, struct resource *res); +static int pccbb_cardbus_deactivate_resource(device_t brdev, + device_t child, int type, int rid, struct resource *res); +static struct resource *pccbb_cardbus_alloc_resource(device_t brdev, + device_t child, int type, int *rid, u_long start, + u_long end, u_long count, uint flags); +static int pccbb_cardbus_release_resource(device_t brdev, device_t child, + int type, int rid, struct resource *res); +static int pccbb_pcic_power_enable_socket(device_t brdev, device_t child); +static void pccbb_pcic_power_disable_socket(device_t brdev, device_t child); +static void pccbb_pcic_wait_ready(struct pccbb_softc *sc); +static void pccbb_pcic_do_mem_map(struct pccbb_softc *sc, int win); +static int pccbb_pcic_mem_map(struct pccbb_softc *sc, int kind, + struct resource *res); +static void pccbb_pcic_mem_unmap(struct pccbb_softc *sc, int window); +static int pccbb_pcic_mem_findmap(struct pccbb_softc *sc, + struct resource *res); +static void pccbb_pcic_do_io_map(struct pccbb_softc *sc, int win); +static int pccbb_pcic_io_map(struct pccbb_softc *sc, int width, + struct resource *r); +static void pccbb_pcic_io_unmap(struct pccbb_softc *sc, int window); +static int pccbb_pcic_io_findmap(struct pccbb_softc *sc, + struct resource *res); +static int pccbb_pcic_activate_resource(device_t brdev, device_t child, + int type, int rid, struct resource *res); +static int pccbb_pcic_deactivate_resource(device_t brdev, device_t child, + int type, int rid, struct resource *res); +static struct resource *pccbb_pcic_alloc_resource(device_t brdev, + device_t child, int type, int *rid, u_long start, + u_long end, u_long count, uint flags); +static int pccbb_pcic_release_resource(device_t brdev, device_t child, + int type, int rid, struct resource *res); +static int pccbb_pcic_set_res_flags(device_t brdev, device_t child, + int type, int rid, uint32_t flags); +static int pccbb_pcic_set_memory_offset(device_t brdev, device_t child, + int rid, uint32_t cardaddr, uint32_t *deltap); +static int pccbb_power_enable_socket(device_t brdev, device_t child); +static void pccbb_power_disable_socket(device_t brdev, device_t child); +static int pccbb_activate_resource(device_t brdev, device_t child, + int type, int rid, struct resource *r); +static int pccbb_deactivate_resource(device_t brdev, device_t child, + int type, int rid, struct resource *r); +static struct resource *pccbb_alloc_resource(device_t brdev, device_t child, + int type, int *rid, u_long start, u_long end, u_long count, + uint flags); +static int pccbb_release_resource(device_t brdev, device_t child, + int type, int rid, struct resource *r); +static int pccbb_read_ivar(device_t brdev, device_t child, int which, + uintptr_t *result); +static int pccbb_write_ivar(device_t brdev, device_t child, int which, + uintptr_t value); +static int pccbb_maxslots(device_t brdev); +static uint32_t pccbb_read_config(device_t brdev, int b, int s, int f, + int reg, int width); +static void pccbb_write_config(device_t brdev, int b, int s, int f, + int reg, uint32_t val, int width); + +/* + */ +static __inline void +pccbb_set(struct pccbb_softc *sc, uint32_t reg, uint32_t val) +{ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, reg, val); +} + +static __inline uint32_t +pccbb_get(struct pccbb_softc *sc, uint32_t reg) +{ + return (bus_space_read_4(sc->sc_bst, sc->sc_bsh, reg)); +} + +static __inline void +pccbb_setb(struct pccbb_softc *sc, uint32_t reg, uint32_t bits) +{ + pccbb_set(sc, reg, pccbb_get(sc, reg) | bits); +} + +static __inline void +pccbb_clrb(struct pccbb_softc *sc, uint32_t reg, uint32_t bits) +{ + pccbb_set(sc, reg, pccbb_get(sc, reg) & ~bits); +} + +static __inline uint8_t +pccbb_pcic_read(struct pccbb_softc *sc, uint32_t reg) +{ + return (bus_space_read_1(sc->sc_bst, sc->sc_bsh, 0x800 + reg)); +} + +static __inline void +pccbb_pcic_write(struct pccbb_softc *sc, uint32_t reg, uint8_t val) +{ + return (bus_space_write_1(sc->sc_bst, sc->sc_bsh, 0x800 + reg, val)); +} +#define PCIC_READ(SC,REG) pccbb_pcic_read(SC, REG) +#define PCIC_WRITE(SC,REG,VAL) pccbb_pcic_write(SC, REG, VAL) + +/************************************************************************/ +/* Probe/Attach */ +/************************************************************************/ + +static int +cb_chipset(uint32_t pci_id, const char **namep, int *flagp) +{ + int loopend = sizeof(yc_chipsets)/sizeof(yc_chipsets[0]); + struct yenta_chipinfo *ycp, *ycend; + ycend = yc_chipsets + loopend; + + for (ycp = yc_chipsets; ycp < ycend && pci_id != ycp->yc_id; ++ycp); + if (ycp == ycend) { + /* not found */ + ycp = yc_chipsets + loopend - 1; /* to point the sentinel */ + } + if (namep != NULL) { + *namep = ycp->yc_name; + } + if (flagp != NULL) { + *flagp = ycp->yc_flags; + } + return (ycp->yc_chiptype); +} + +static int +pccbb_probe(device_t brdev) +{ + const char *name; + + if (cb_chipset(pci_get_devid(brdev), &name, NULL) == CB_UNKNOWN) + return (ENXIO); + device_set_desc(brdev, name); + return (0); +} + +static void +pccbb_chipinit(struct pccbb_softc *sc) +{ + /* Set CardBus latency timer */ + if (pci_read_config(sc->sc_dev, PCIR_SECLAT_1, 1) < 0x20) + pci_write_config(sc->sc_dev, PCIR_SECLAT_1, 0x20, 1); + + /* Set PCI latency timer */ + if (pci_read_config(sc->sc_dev, PCIR_LATTIMER, 1) < 0x20) + pci_write_config(sc->sc_dev, PCIR_LATTIMER, 0x20, 1); + + /* Enable memory access */ + PCI_MASK_CONFIG(sc->sc_dev, PCIR_COMMAND, + | PCIM_CMD_MEMEN + | PCIM_CMD_PORTEN + | PCIM_CMD_BUSMASTEREN, 2); + + /* disable Legacy IO */ + switch (sc->sc_chipset) { + case CB_RF5C46X: + PCI_MASK_CONFIG(sc->sc_dev, PCCBBR_BRIDGECTRL, + & ~(PCCBBM_BRIDGECTRL_RL_3E0_EN | + PCCBBM_BRIDGECTRL_RL_3E2_EN), 2); + break; + default: + pci_write_config(sc->sc_dev, PCCBBR_LEGACY, 0x0, 4); + break; + } + + /* Use PCI interrupt for interrupt routing */ + PCI_MASK2_CONFIG(sc->sc_dev, PCCBBR_BRIDGECTRL, + & ~(PCCBBM_BRIDGECTRL_MASTER_ABORT | + PCCBBM_BRIDGECTRL_INTR_IREQ_EN), + | PCCBBM_BRIDGECTRL_WRITE_POST_EN, + 2); + + /* XXX this should be a function table, ala OLDCARD. */ + switch (sc->sc_chipset) { + case CB_TI113X: + /* + * The TI 1030, TI 1130 and TI 1131 all require another bit + * be set to enable PCI routing of interrupts, and then + * a bit for each of the CSC and Function interrupts we + * want routed. + */ + PCI_MASK_CONFIG(sc->sc_dev, PCCBBR_CBCTRL, + | PCCBBM_CBCTRL_113X_PCI_INTR | + PCCBBM_CBCTRL_113X_PCI_CSC | PCCBBM_CBCTRL_113X_PCI_IRQ_EN, + 1); + PCI_MASK_CONFIG(sc->sc_dev, PCCBBR_DEVCTRL, + & ~(PCCBBM_DEVCTRL_INT_SERIAL | + PCCBBM_DEVCTRL_INT_PCI), 1); + PCIC_WRITE(sc, PCIC_INTR, PCIC_INTR_ENABLE); + PCIC_WRITE(sc, PCIC_CSC_INTR, 0); + break; + case CB_TI12XX: + PCIC_WRITE(sc, PCIC_INTR, PCIC_INTR_ENABLE); + PCIC_WRITE(sc, PCIC_CSC_INTR, 0); + break; + case CB_TOPIC95B: + PCI_MASK_CONFIG(sc->sc_dev, PCCBBR_TOPIC_SOCKETCTRL, + | PCCBBM_TOPIC_SOCKETCTRL_SCR_IRQSEL, 4); + PCI_MASK2_CONFIG(sc->sc_dev, PCCBBR_TOPIC_SLOTCTRL, + | PCCBBM_TOPIC_SLOTCTRL_SLOTON | + PCCBBM_TOPIC_SLOTCTRL_SLOTEN | + PCCBBM_TOPIC_SLOTCTRL_ID_LOCK | + PCCBBM_TOPIC_SLOTCTRL_CARDBUS, + & ~PCCBBM_TOPIC_SLOTCTRL_SWDETECT, 4); + break; + } + + /* close all memory and io windows */ + pci_write_config(sc->sc_dev, PCCBBR_MEMBASE0, 0xffffffff, 4); + pci_write_config(sc->sc_dev, PCCBBR_MEMLIMIT0, 0, 4); + pci_write_config(sc->sc_dev, PCCBBR_MEMBASE1, 0xffffffff, 4); + pci_write_config(sc->sc_dev, PCCBBR_MEMLIMIT1, 0, 4); + pci_write_config(sc->sc_dev, PCCBBR_IOBASE0, 0xffffffff, 4); + pci_write_config(sc->sc_dev, PCCBBR_IOLIMIT0, 0, 4); + pci_write_config(sc->sc_dev, PCCBBR_IOBASE1, 0xffffffff, 4); + pci_write_config(sc->sc_dev, PCCBBR_IOLIMIT1, 0, 4); +} + +static int +pccbb_attach(device_t brdev) +{ + struct pccbb_softc *sc = (struct pccbb_softc *)device_get_softc(brdev); + int rid; + uint32_t sockbase; + struct pccbb_sclist *sclist; + + if (!softcs_init) { + softcs_init = 1; + STAILQ_INIT(&softcs); + } + mtx_init(&sc->sc_mtx, device_get_nameunit(brdev), MTX_DEF); + sc->sc_chipset = cb_chipset(pci_get_devid(brdev), NULL, &sc->sc_flags); + sc->sc_dev = brdev; + sc->sc_cbdev = NULL; + sc->sc_pccarddev = NULL; + sc->sc_secbus = pci_read_config(brdev, PCIR_SECBUS_2, 1); + sc->sc_subbus = pci_read_config(brdev, PCIR_SUBBUS_2, 1); + sc->memalloc = 0; + sc->ioalloc = 0; + SLIST_INIT(&sc->rl); + + /* Ths PCI bus should have given me memory... right? */ + rid=PCCBBR_SOCKBASE; + sc->sc_base_res=bus_alloc_resource(brdev, SYS_RES_MEMORY, &rid, + 0,~0,1, RF_ACTIVE); + if (!sc->sc_base_res) { + /* + * XXX EVILE HACK BAD THING! XXX + * The pci bus device should do this for us. + * Some BIOSes doesn't assign a memory space properly. + * So we try to manually put one in... + */ + sockbase = pci_read_config(brdev, rid, 4); + if (sockbase < 0x100000 || sockbase >= 0xfffffff0) { + pci_write_config(brdev, rid, 0xffffffff, 4); + sockbase = pci_read_config(brdev, rid, 4); + sockbase = (sockbase & 0xfffffff0) & + -(sockbase & 0xfffffff0); + sc->sc_base_res = bus_generic_alloc_resource( + device_get_parent(brdev), brdev, SYS_RES_MEMORY, + &rid, pccbb_start_mem, ~0, sockbase, + RF_ACTIVE|rman_make_alignment_flags(sockbase)); + if (!sc->sc_base_res){ + device_printf(brdev, + "Could not grab register memory\n"); + mtx_destroy(&sc->sc_mtx); + return (ENOMEM); + } + pci_write_config(brdev, PCCBBR_SOCKBASE, + rman_get_start(sc->sc_base_res), 4); + DEVPRINTF((brdev, "PCI Memory allocated: %08lx\n", + rman_get_start(sc->sc_base_res))); + } else { + device_printf(brdev, "Could not map register memory\n"); + mtx_destroy(&sc->sc_mtx); + return (ENOMEM); + } + } + + sc->sc_bst = rman_get_bustag(sc->sc_base_res); + sc->sc_bsh = rman_get_bushandle(sc->sc_base_res); + pccbb_chipinit(sc); + + /* CSC Interrupt: Card detect interrupt on */ + pccbb_setb(sc, PCCBB_SOCKET_MASK, PCCBB_SOCKET_MASK_CD); + + /* reset interrupt */ + pccbb_set(sc, PCCBB_SOCKET_EVENT, pccbb_get(sc, PCCBB_SOCKET_EVENT)); + + /* Map and establish the interrupt. */ + rid=0; + sc->sc_irq_res=bus_alloc_resource(brdev, SYS_RES_IRQ, &rid, 0, ~0, 1, + RF_SHAREABLE | RF_ACTIVE); + if (sc->sc_irq_res == NULL) { + printf("pccbb: Unable to map IRQ...\n"); + bus_release_resource(brdev, SYS_RES_MEMORY, PCCBBR_SOCKBASE, + sc->sc_base_res); + mtx_destroy(&sc->sc_mtx); + return (ENOMEM); + } + + if (bus_setup_intr(brdev, sc->sc_irq_res, INTR_TYPE_BIO, pccbb_intr, sc, + &(sc->sc_intrhand))) { + device_printf(brdev, "couldn't establish interrupt"); + bus_release_resource(brdev, SYS_RES_IRQ, 0, sc->sc_irq_res); + bus_release_resource(brdev, SYS_RES_MEMORY, PCCBBR_SOCKBASE, + sc->sc_base_res); + mtx_destroy(&sc->sc_mtx); + return (ENOMEM); + } + + /* attach children */ + sc->sc_cbdev = device_add_child(brdev, "cardbus", -1); + if (sc->sc_cbdev == NULL) + DEVPRINTF((brdev, "WARNING: cannot add cardbus bus.\n")); + else if (device_probe_and_attach(sc->sc_cbdev) != 0) { + DEVPRINTF((brdev, "WARNING: cannot attach cardbus bus!\n")); + sc->sc_cbdev = NULL; + } + + sc->sc_pccarddev = device_add_child(brdev, "pccard", -1); + if (sc->sc_pccarddev == NULL) + DEVPRINTF((brdev, "WARNING: cannot add pccard bus.\n")); + else if (device_probe_and_attach(sc->sc_pccarddev) != 0) { + DEVPRINTF((brdev, "WARNING: cannot attach pccard bus.\n")); + sc->sc_pccarddev = NULL; + } + +#ifndef KLD_MODULE + if (sc->sc_cbdev == NULL && sc->sc_pccarddev == NULL) { + device_printf(brdev, + "ERROR: Failed to attach cardbus/pccard bus!\n"); + bus_teardown_intr(brdev, sc->sc_irq_res, sc->sc_intrhand); + bus_release_resource(brdev, SYS_RES_IRQ, 0, sc->sc_irq_res); + bus_release_resource(brdev, SYS_RES_MEMORY, PCCBBR_SOCKBASE, + sc->sc_base_res); + mtx_destroy(&sc->sc_mtx); + return (ENOMEM); + } +#endif + + sclist = malloc(sizeof(struct pccbb_sclist), M_DEVBUF, M_WAITOK); + sclist->sc = sc; + STAILQ_INSERT_TAIL(&softcs, sclist, entries); + return (0); +} + +static int +pccbb_detach(device_t brdev) +{ + struct pccbb_softc *sc = device_get_softc(brdev); + int numdevs; + device_t *devlist; + int tmp; + int error; + + device_get_children(brdev, &devlist, &numdevs); + + error = 0; + for (tmp = 0; tmp < numdevs; tmp++) { + if (device_detach(devlist[tmp]) == 0) + device_delete_child(brdev, devlist[tmp]); + else + error++; + } + free(devlist, M_TEMP); + if (error > 0) + return (ENXIO); + + mtx_lock(&sc->sc_mtx); + bus_teardown_intr(brdev, sc->sc_irq_res, sc->sc_intrhand); + + sc->sc_flags |= PCCBB_KTHREAD_DONE; + if (sc->sc_flags & PCCBB_KTHREAD_RUNNING) { + wakeup(sc); + mtx_unlock(&sc->sc_mtx); + DEVPRINTF((brdev, "waiting for kthread exit...")); + error = tsleep(sc, PWAIT, "pccbb-detach-wait", 60 * hz); + if (error) + DPRINTF(("timeout\n")); + else + DPRINTF(("done\n")); + } else { + mtx_unlock(&sc->sc_mtx); + } + + bus_release_resource(brdev, SYS_RES_IRQ, 0, sc->sc_irq_res); + bus_release_resource(brdev, SYS_RES_MEMORY, PCCBBR_SOCKBASE, + sc->sc_base_res); + mtx_destroy(&sc->sc_mtx); + return (0); +} + +static int +pccbb_shutdown(device_t brdev) +{ + struct pccbb_softc *sc = (struct pccbb_softc *)device_get_softc(brdev); + /* properly reset everything at shutdown */ + + PCI_MASK_CONFIG(brdev, PCCBBR_BRIDGECTRL, |PCCBBM_BRIDGECTRL_RESET, 2); + PCIC_MASK(sc, PCIC_INTR, & ~PCIC_INTR_RESET); + + pccbb_set(sc, PCCBB_SOCKET_MASK, 0); + + pccbb_power(brdev, CARD_VCC_0V | CARD_VPP_0V); + + PCIC_WRITE(sc, PCIC_ADDRWIN_ENABLE, 0); + pci_write_config(brdev, PCCBBR_MEMBASE0, 0, 4); + pci_write_config(brdev, PCCBBR_MEMLIMIT0, 0, 4); + pci_write_config(brdev, PCCBBR_MEMBASE1, 0, 4); + pci_write_config(brdev, PCCBBR_MEMLIMIT1, 0, 4); + pci_write_config(brdev, PCCBBR_IOBASE0, 0, 4); + pci_write_config(brdev, PCCBBR_IOLIMIT0, 0, 4); + pci_write_config(brdev, PCCBBR_IOBASE1, 0, 4); + pci_write_config(brdev, PCCBBR_IOLIMIT1, 0, 4); + pci_write_config(brdev, PCIR_COMMAND, 0, 2); + return (0); +} + +static void +pccbb_driver_added(device_t brdev, driver_t *driver) +{ + struct pccbb_softc *sc = device_get_softc(brdev); + device_t *devlist; + int tmp; + int numdevs; + int wake; + uint32_t sockstate; + + DEVICE_IDENTIFY(driver, brdev); + device_get_children(brdev, &devlist, &numdevs); + wake = 0; + sockstate = pccbb_get(sc, PCCBB_SOCKET_STATE); + for (tmp = 0; tmp < numdevs; tmp++) { + if (device_get_state(devlist[tmp]) == DS_NOTPRESENT && + device_probe_and_attach(devlist[tmp]) == 0) { + if (devlist[tmp] == NULL) + /* NOTHING */; + else if (strcmp(driver->name, "cardbus") == 0) { + sc->sc_cbdev = devlist[tmp]; + if (((sockstate & PCCBB_SOCKET_STAT_CD) == 0) && + (sockstate & PCCBB_SOCKET_STAT_CB)) + wake++; + } else if (strcmp(driver->name, "pccard") == 0) { + sc->sc_pccarddev = devlist[tmp]; + if (((sockstate & PCCBB_SOCKET_STAT_CD) == 0) && + (sockstate & PCCBB_SOCKET_STAT_16BIT)) + wake++; + } else + device_printf(brdev, + "Unsupported child bus: %s\n", + driver->name); + } + } + free(devlist, M_TEMP); + + if (wake > 0) { + if ((pccbb_get(sc, PCCBB_SOCKET_STATE) & PCCBB_SOCKET_STAT_CD) + == 0) { + mtx_lock(&sc->sc_mtx); + wakeup(sc); + mtx_unlock(&sc->sc_mtx); + } + } +} + +static void +pccbb_child_detached(device_t brdev, device_t child) +{ + struct pccbb_softc *sc = device_get_softc(brdev); + + if (child == sc->sc_cbdev) + sc->sc_cbdev = NULL; + else if (child == sc->sc_pccarddev) + sc->sc_pccarddev = NULL; + else + device_printf(brdev, "Unknown child detached: %s %p/%p\n", + device_get_nameunit(child), sc->sc_cbdev, sc->sc_pccarddev); +} + +static int +pccbb_card_reprobe(device_t brdev, device_t busdev) +{ + struct pccbb_softc *sc = device_get_softc(brdev); + int wake = 0; + uint32_t sockstate; + + sockstate = pccbb_get(sc, PCCBB_SOCKET_STATE); + + if ((sockstate & PCCBB_SOCKET_STAT_CD) == 0) { + if (busdev == sc->sc_cbdev && + (sockstate & PCCBB_SOCKET_STAT_CB)) + wake++; + else if (busdev == sc->sc_pccarddev && + (sockstate & PCCBB_SOCKET_STAT_16BIT)) + wake++; + + if (wake > 0) { + mtx_lock(&sc->sc_mtx); + wakeup(sc); + mtx_unlock(&sc->sc_mtx); + return (0); + } + return (EBUSY); + } + return (ENOENT); +} + +/************************************************************************/ +/* Kthreads */ +/************************************************************************/ + +static void +pccbb_event_thread(void *arg) +{ + struct pccbb_softc *sc = arg; + uint32_t status; + + mtx_lock(&Giant); + for(;;) { + if (!(sc->sc_flags & PCCBB_KTHREAD_RUNNING)) { + sc->sc_flags |= PCCBB_KTHREAD_RUNNING; + } else { + tsleep (sc, PWAIT, "pccbbev", 0); + /* + * Delay 1 second, make sure the user is done with + * whatever he is doing. We tsleep on sc->sc_flags, + * which should never be woken up. + * + * XXX Note: This can cause problems on card + * removal. See OLDCARD's ISR for how you may + * have to deal with the debouncing problem. The + * crux of the issue is interrupts delivered to + * the card after eject are unstable. + */ + tsleep (&sc->sc_flags, PWAIT, "pccbbev", 1*hz); + } + mtx_lock(&sc->sc_mtx); + if (sc->sc_flags & PCCBB_KTHREAD_DONE) + break; + + status = pccbb_get(sc, PCCBB_SOCKET_STATE); + if ((status & PCCBB_SOCKET_STAT_CD) == 0) { + pccbb_insert(sc); + } else { + pccbb_removal(sc); + } + mtx_unlock(&sc->sc_mtx); + } + mtx_unlock(&sc->sc_mtx); + sc->sc_flags &= ~PCCBB_KTHREAD_RUNNING; + wakeup(sc); + kthread_exit(0); +} + +static void +pccbb_create_event_thread(struct pccbb_softc *sc) +{ + if (kthread_create(pccbb_event_thread, sc, &sc->event_thread, + 0, "%s%d", device_get_name(sc->sc_dev), + device_get_unit(sc->sc_dev))) { + device_printf (sc->sc_dev, "unable to create event thread.\n"); + panic ("pccbb_create_event_thread"); + } +} + +static void +pccbb_start_threads(void *arg) +{ + struct pccbb_sclist *sclist; + + STAILQ_FOREACH(sclist, &softcs, entries) { + pccbb_create_event_thread(sclist->sc); + } +} + +/************************************************************************/ +/* Insert/removal */ +/************************************************************************/ + +static void +pccbb_insert(struct pccbb_softc *sc) +{ + uint32_t sockevent, sockstate; + int timeout = 30; + + do { + sockevent = pccbb_get(sc, PCCBB_SOCKET_EVENT); + sockstate = pccbb_get(sc, PCCBB_SOCKET_STATE); + } while (sockstate & PCCBB_SOCKET_STAT_CD && --timeout > 0); + + if (timeout < 0) { + device_printf (sc->sc_dev, "insert timeout"); + return; + } + + DEVPRINTF((sc->sc_dev, "card inserted: event=0x%08x, state=%08x\n", + sockevent, sockstate)); + + if (sockstate & PCCBB_SOCKET_STAT_16BIT && sc->sc_pccarddev != NULL) { + sc->sc_flags |= PCCBB_16BIT_CARD; + if (CARD_ATTACH_CARD(sc->sc_pccarddev) != 0) + device_printf(sc->sc_dev, "card activation failed\n"); + } else if (sockstate & PCCBB_SOCKET_STAT_CB && sc->sc_cbdev != NULL) { + sc->sc_flags &= ~PCCBB_16BIT_CARD; + if (CARD_ATTACH_CARD(sc->sc_cbdev) != 0) + device_printf(sc->sc_dev, "card activation failed\n"); + } else { + device_printf (sc->sc_dev, "Unsupported card type detected\n"); + } +} + +static void +pccbb_removal(struct pccbb_softc *sc) +{ + struct pccbb_reslist *rle; + + if (sc->sc_flags & PCCBB_16BIT_CARD && sc->sc_pccarddev != NULL) + CARD_DETACH_CARD(sc->sc_pccarddev, DETACH_FORCE); + else if ((!(sc->sc_flags & PCCBB_16BIT_CARD)) && sc->sc_cbdev != NULL) + CARD_DETACH_CARD(sc->sc_cbdev, DETACH_FORCE); + + while ((rle = SLIST_FIRST(&sc->rl)) != NULL) { + device_printf(sc->sc_dev, "Danger Will Robinson: Resource " + "left allocated! This is a bug... " + "(rid=%x, type=%d, addr=%lx)\n", rle->rid, rle->type, + rman_get_start(rle->res)); + SLIST_REMOVE_HEAD(&sc->rl, link); + free(rle, M_DEVBUF); + } +} + +/************************************************************************/ +/* Interrupt Handler */ +/************************************************************************/ + +static void +pccbb_intr(void *arg) +{ + struct pccbb_softc *sc = arg; + uint32_t sockevent; + + if (!(sockevent = pccbb_get(sc, PCCBB_SOCKET_EVENT))) { + /* not for me. */ + return; + } + + /* reset bit */ + pccbb_setb(sc, PCCBB_SOCKET_EVENT, 0x01); /* XXXmagic */ + + if (sockevent & PCCBB_SOCKET_EVENT_CD) { + mtx_lock(&sc->sc_mtx); + wakeup(sc); + mtx_unlock(&sc->sc_mtx); + } else { + if (sockevent & PCCBB_SOCKET_EVENT_CSTS) { + DPRINTF((" cstsevent occures, 0x%08x\n", + pccbb_get(sc, PCCBB_SOCKET_STATE))); + } + if (sockevent & PCCBB_SOCKET_EVENT_POWER) { + DPRINTF((" pwrevent occures, 0x%08x\n", + pccbb_get(sc, PCCBB_SOCKET_STATE))); + } + } +} + +/************************************************************************/ +/* Generic Power functions */ +/************************************************************************/ + +static int +pccbb_detect_voltage(device_t brdev) +{ + struct pccbb_softc *sc = device_get_softc(brdev); + uint32_t psr; + int vol = CARD_UKN_CARD; + + psr = pccbb_get(sc, PCCBB_SOCKET_STATE); + + if (psr & PCCBB_SOCKET_STAT_5VCARD) + vol |= CARD_5V_CARD; + if (psr & PCCBB_SOCKET_STAT_3VCARD) + vol |= CARD_3V_CARD; + if (psr & PCCBB_SOCKET_STAT_XVCARD) + vol |= CARD_XV_CARD; + if (psr & PCCBB_SOCKET_STAT_YVCARD) + vol |= CARD_YV_CARD; + + return (vol); +} + +static int +pccbb_power(device_t brdev, int volts) +{ + uint32_t status, sock_ctrl; + struct pccbb_softc *sc = device_get_softc(brdev); + int timeout; + uint32_t sockevent; + + DEVPRINTF((sc->sc_dev, "pccbb_power: %s and %s [%x]\n", + (volts & CARD_VCCMASK) == CARD_VCC_UC ? "CARD_VCC_UC" : + (volts & CARD_VCCMASK) == CARD_VCC_5V ? "CARD_VCC_5V" : + (volts & CARD_VCCMASK) == CARD_VCC_3V ? "CARD_VCC_3V" : + (volts & CARD_VCCMASK) == CARD_VCC_XV ? "CARD_VCC_XV" : + (volts & CARD_VCCMASK) == CARD_VCC_YV ? "CARD_VCC_YV" : + (volts & CARD_VCCMASK) == CARD_VCC_0V ? "CARD_VCC_0V" : + "VCC-UNKNOWN", + (volts & CARD_VPPMASK) == CARD_VPP_UC ? "CARD_VPP_UC" : + (volts & CARD_VPPMASK) == CARD_VPP_12V ? "CARD_VPP_12V" : + (volts & CARD_VPPMASK) == CARD_VPP_VCC ? "CARD_VPP_VCC" : + (volts & CARD_VPPMASK) == CARD_VPP_0V ? "CARD_VPP_0V" : + "VPP-UNKNOWN", + volts)); + + status = pccbb_get(sc, PCCBB_SOCKET_STATE); + sock_ctrl = pccbb_get(sc, PCCBB_SOCKET_CONTROL); + + switch (volts & CARD_VCCMASK) { + case CARD_VCC_UC: + break; + case CARD_VCC_5V: + if (PCCBB_SOCKET_STAT_5VCARD & status) { /* check 5 V card */ + sock_ctrl &= ~PCCBB_SOCKET_CTRL_VCCMASK; + sock_ctrl |= PCCBB_SOCKET_CTRL_VCC_5V; + } else { + device_printf(sc->sc_dev, + "BAD voltage request: no 5 V card\n"); + } + break; + case CARD_VCC_3V: + if (PCCBB_SOCKET_STAT_3VCARD & status) { + sock_ctrl &= ~PCCBB_SOCKET_CTRL_VCCMASK; + sock_ctrl |= PCCBB_SOCKET_CTRL_VCC_3V; + } else { + device_printf(sc->sc_dev, + "BAD voltage request: no 3.3 V card\n"); + } + break; + case CARD_VCC_0V: + sock_ctrl &= ~PCCBB_SOCKET_CTRL_VCCMASK; + break; + default: + return (0); /* power NEVER changed */ + break; + } + + switch (volts & CARD_VPPMASK) { + case CARD_VPP_UC: + break; + case CARD_VPP_0V: + sock_ctrl &= ~PCCBB_SOCKET_CTRL_VPPMASK; + break; + case CARD_VPP_VCC: + sock_ctrl &= ~PCCBB_SOCKET_CTRL_VPPMASK; + sock_ctrl |= ((sock_ctrl >> 4) & 0x07); + break; + case CARD_VPP_12V: + sock_ctrl &= ~PCCBB_SOCKET_CTRL_VPPMASK; + sock_ctrl |= PCCBB_SOCKET_CTRL_VPP_12V; + break; + } + + if (pccbb_get(sc, PCCBB_SOCKET_CONTROL) == sock_ctrl) + return (1); /* no change necessary */ + + pccbb_set(sc, PCCBB_SOCKET_CONTROL, sock_ctrl); + status = pccbb_get(sc, PCCBB_SOCKET_STATE); + + /* + * XXX This busy wait is bogus. We should wait for a power + * interrupt and then whine if the status is bad. If we're + * worried about the card not coming up, then we should also + * schedule a timeout which we can cacel in the power interrupt. + */ + timeout = 20; + do { + DELAY(20*1000); + sockevent = pccbb_get(sc, PCCBB_SOCKET_EVENT); + } while (!(sockevent & PCCBB_SOCKET_EVENT_POWER) && --timeout > 0); + /* reset event status */ + /* XXX should only reset EVENT_POWER */ + pccbb_set(sc, PCCBB_SOCKET_EVENT, sockevent); + if (timeout < 0) { + printf ("VCC supply failed.\n"); + return (0); + } + + /* XXX + * delay 400 ms: thgough the standard defines that the Vcc set-up time + * is 20 ms, some PC-Card bridge requires longer duration. + * XXX Note: We should check the stutus AFTER the delay to give time + * for things to stabilize. + */ + DELAY(400*1000); + + if (status & PCCBB_SOCKET_STAT_BADVCC) { + device_printf(sc->sc_dev, + "bad Vcc request. ctrl=0x%x, status=0x%x\n", + sock_ctrl ,status); + printf("pccbb_power: %s and %s [%x]\n", + (volts & CARD_VCCMASK) == CARD_VCC_UC ? "CARD_VCC_UC" : + (volts & CARD_VCCMASK) == CARD_VCC_5V ? "CARD_VCC_5V" : + (volts & CARD_VCCMASK) == CARD_VCC_3V ? "CARD_VCC_3V" : + (volts & CARD_VCCMASK) == CARD_VCC_XV ? "CARD_VCC_XV" : + (volts & CARD_VCCMASK) == CARD_VCC_YV ? "CARD_VCC_YV" : + (volts & CARD_VCCMASK) == CARD_VCC_0V ? "CARD_VCC_0V" : + "VCC-UNKNOWN", + (volts & CARD_VPPMASK) == CARD_VPP_UC ? "CARD_VPP_UC" : + (volts & CARD_VPPMASK) == CARD_VPP_12V ? "CARD_VPP_12V": + (volts & CARD_VPPMASK) == CARD_VPP_VCC ? "CARD_VPP_VCC": + (volts & CARD_VPPMASK) == CARD_VPP_0V ? "CARD_VPP_0V" : + "VPP-UNKNOWN", + volts); + return (0); + } + return (1); /* power changed correctly */ +} + +/************************************************************************/ +/* Cardbus power functions */ +/************************************************************************/ + +static void +pccbb_cardbus_reset(device_t brdev) +{ + struct pccbb_softc *sc = device_get_softc(brdev); + int delay_us; + + delay_us = sc->sc_chipset == CB_RF5C47X ? 400*1000 : 20*1000; + + PCI_MASK_CONFIG(brdev, PCCBBR_BRIDGECTRL, |PCCBBM_BRIDGECTRL_RESET, 2); + + DELAY(delay_us); + + /* If a card exists, unreset it! */ + if ((pccbb_get(sc, PCCBB_SOCKET_STATE) & PCCBB_SOCKET_STAT_CD) == 0) { + PCI_MASK_CONFIG(brdev, PCCBBR_BRIDGECTRL, + &~PCCBBM_BRIDGECTRL_RESET, 2); + DELAY(delay_us); + } +} + +static int +pccbb_cardbus_power_enable_socket(device_t brdev, device_t child) +{ + struct pccbb_softc *sc = device_get_softc(brdev); + int voltage; + + if ((pccbb_get(sc, PCCBB_SOCKET_STATE) & PCCBB_SOCKET_STAT_CD) == + PCCBB_SOCKET_STAT_CD) + return (ENODEV); + + voltage = pccbb_detect_voltage(brdev); + + pccbb_power(brdev, CARD_VCC_0V | CARD_VPP_0V); + if (voltage & CARD_5V_CARD) + pccbb_power(brdev, CARD_VCC_5V | CARD_VPP_VCC); + else if (voltage & CARD_3V_CARD) + pccbb_power(brdev, CARD_VCC_3V | CARD_VPP_VCC); + else { + device_printf(brdev, "Unknown card voltage\n"); + return (ENXIO); + } + + pccbb_cardbus_reset(brdev); + return (0); +} + +static void +pccbb_cardbus_power_disable_socket(device_t brdev, device_t child) +{ + pccbb_power(brdev, CARD_VCC_0V | CARD_VPP_0V); + pccbb_cardbus_reset(brdev); +} + +/************************************************************************/ +/* Cardbus Resource */ +/************************************************************************/ + +static int +pccbb_cardbus_io_open(device_t brdev, int win, uint32_t start, uint32_t end) +{ + int basereg; + int limitreg; + + if ((win < 0) || (win > 1)) { + DEVPRINTF((brdev, + "pccbb_cardbus_io_open: window out of range %d\n", win)); + return (EINVAL); + } + + basereg = win*8 + PCCBBR_IOBASE0; + limitreg = win*8 + PCCBBR_IOLIMIT0; + + pci_write_config(brdev, basereg, start, 4); + pci_write_config(brdev, limitreg, end, 4); + return (0); +} + +static int +pccbb_cardbus_mem_open(device_t brdev, int win, uint32_t start, uint32_t end) +{ + int basereg; + int limitreg; + + if ((win < 0) || (win > 1)) { + DEVPRINTF((brdev, + "pccbb_cardbus_mem_open: window out of range %d\n", win)); + return (EINVAL); + } + + basereg = win*8 + PCCBBR_MEMBASE0; + limitreg = win*8 + PCCBBR_MEMLIMIT0; + + pci_write_config(brdev, basereg, start, 4); + pci_write_config(brdev, limitreg, end, 4); + return (0); +} + +static void +pccbb_cardbus_auto_open(struct pccbb_softc *sc, int type) +{ + uint32_t starts[2]; + uint32_t ends[2]; + struct pccbb_reslist *rle; + int align; + int prefetchable[2]; + uint32_t reg; + + starts[0] = starts[1] = 0xffffffff; + ends[0] = ends[1] = 0; + + if (type == SYS_RES_MEMORY) + align = PCCBB_MEMALIGN; + else if (type == SYS_RES_IOPORT) + align = PCCBB_IOALIGN; + else + align = 1; + + SLIST_FOREACH(rle, &sc->rl, link) { + if (rle->type != type) + ; + else if (rle->res == NULL) { + device_printf(sc->sc_dev, "WARNING: Resource not reserved? " + "(type=%d, addr=%lx)\n", + rle->type, rman_get_start(rle->res)); + } else if (!(rman_get_flags(rle->res) & RF_ACTIVE)) { + /* XXX */ + } else if (starts[0] == 0xffffffff) { + starts[0] = rman_get_start(rle->res); + ends[0] = rman_get_end(rle->res); + prefetchable[0] = + rman_get_flags(rle->res) & RF_PREFETCHABLE; + } else if (rman_get_end(rle->res) > ends[0] && + rman_get_start(rle->res) - ends[0] < + PCCBB_AUTO_OPEN_SMALLHOLE && prefetchable[0] == + (rman_get_flags(rle->res) & RF_PREFETCHABLE)) { + ends[0] = rman_get_end(rle->res); + } else if (rman_get_start(rle->res) < starts[0] && + starts[0] - rman_get_end(rle->res) < + PCCBB_AUTO_OPEN_SMALLHOLE && prefetchable[0] == + (rman_get_flags(rle->res) & RF_PREFETCHABLE)) { + starts[0] = rman_get_start(rle->res); + } else if (starts[1] == 0xffffffff) { + starts[1] = rman_get_start(rle->res); + ends[1] = rman_get_end(rle->res); + prefetchable[1] = + rman_get_flags(rle->res) & RF_PREFETCHABLE; + } else if (rman_get_end(rle->res) > ends[1] && + rman_get_start(rle->res) - ends[1] < + PCCBB_AUTO_OPEN_SMALLHOLE && prefetchable[1] == + (rman_get_flags(rle->res) & RF_PREFETCHABLE)) { + ends[1] = rman_get_end(rle->res); + } else if (rman_get_start(rle->res) < starts[1] && + starts[1] - rman_get_end(rle->res) < + PCCBB_AUTO_OPEN_SMALLHOLE && prefetchable[1] == + (rman_get_flags(rle->res) & RF_PREFETCHABLE)) { + starts[1] = rman_get_start(rle->res); + } else { + uint32_t diffs[2]; + int win; + + diffs[0] = diffs[1] = 0xffffffff; + if (rman_get_start(rle->res) > ends[0]) + diffs[0] = rman_get_start(rle->res) - ends[0]; + else if (rman_get_end(rle->res) < starts[0]) + diffs[0] = starts[0] - rman_get_end(rle->res); + if (rman_get_start(rle->res) > ends[1]) + diffs[1] = rman_get_start(rle->res) - ends[1]; + else if (rman_get_end(rle->res) < starts[1]) + diffs[1] = starts[1] - rman_get_end(rle->res); + + win = (diffs[0] <= diffs[1])?0:1; + if (rman_get_start(rle->res) > ends[win]) + ends[win] = rman_get_end(rle->res); + else if (rman_get_end(rle->res) < starts[win]) + starts[win] = rman_get_start(rle->res); + if (!(rman_get_flags(rle->res) & RF_PREFETCHABLE)) + prefetchable[win] = 0; + } + + if (starts[0] != 0xffffffff) + starts[0] -= starts[0] % align; + if (starts[1] != 0xffffffff) + starts[1] -= starts[1] % align; + if (ends[0] % align != 0) + ends[0] += align - ends[0]%align - 1; + if (ends[1] % align != 0) + ends[1] += align - ends[1]%align - 1; + } + + if (type == SYS_RES_MEMORY) { + pccbb_cardbus_mem_open(sc->sc_dev, 0, starts[0], ends[0]); + pccbb_cardbus_mem_open(sc->sc_dev, 1, starts[1], ends[1]); + reg = pci_read_config(sc->sc_dev, PCCBBR_BRIDGECTRL, 2); + reg &= ~(PCCBBM_BRIDGECTRL_PREFETCH_0| + PCCBBM_BRIDGECTRL_PREFETCH_1); + reg |= (prefetchable[0]?PCCBBM_BRIDGECTRL_PREFETCH_0:0)| + (prefetchable[1]?PCCBBM_BRIDGECTRL_PREFETCH_1:0); + pci_write_config(sc->sc_dev, PCCBBR_BRIDGECTRL, reg, 2); + } else if (type == SYS_RES_IOPORT) { + pccbb_cardbus_io_open(sc->sc_dev, 0, starts[0], ends[0]); + pccbb_cardbus_io_open(sc->sc_dev, 1, starts[1], ends[1]); + } +} + +static int +pccbb_cardbus_activate_resource(device_t brdev, device_t child, int type, + int rid, struct resource *res) +{ + int ret; + + ret = BUS_ACTIVATE_RESOURCE(device_get_parent(brdev), child, + type, rid, res); + if (ret != 0) + return (ret); + pccbb_cardbus_auto_open(device_get_softc(brdev), type); + return (0); +} + +static int +pccbb_cardbus_deactivate_resource(device_t brdev, device_t child, int type, + int rid, struct resource *res) +{ + int ret; + + ret = BUS_DEACTIVATE_RESOURCE(device_get_parent(brdev), child, + type, rid, res); + if (ret != 0) + return (ret); + pccbb_cardbus_auto_open(device_get_softc(brdev), type); + return (0); +} + +static struct resource * +pccbb_cardbus_alloc_resource(device_t brdev, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, uint flags) +{ + struct pccbb_softc *sc = device_get_softc(brdev); + struct pccbb_reslist *rle; + int tmp; + struct resource *res; + + switch (type) { + case SYS_RES_IRQ: + tmp = rman_get_start(sc->sc_irq_res); + if (start > tmp || end < tmp || count != 1) { + device_printf(child, "requested interrupt %ld-%ld," + "count = %ld not supported by pccbb\n", + start, end, count); + return (NULL); + } + start = end = tmp; + break; + case SYS_RES_IOPORT: + if (start <= PCCBB_START_IO) + start = PCCBB_START_IO; + if (end < start) + end = start; + break; + case SYS_RES_MEMORY: + if (start <= pccbb_start_mem) + start = pccbb_start_mem; + if (end < start) + end = start; + break; + } + + res = BUS_ALLOC_RESOURCE(device_get_parent(brdev), child, type, rid, + start, end, count, flags & ~RF_ACTIVE); + if (res == NULL) { + printf("pccbb alloc res fail\n"); + return (NULL); + } + + /* + * Need to record allocated resource so we can iterate through + * it later. + */ + rle = malloc(sizeof(struct pccbb_reslist), M_DEVBUF, M_NOWAIT); + if (!res) + panic("pccbb_cardbus_alloc_resource: can't record entry!"); + rle->res = res; + rle->type = type; + rle->rid = *rid; + rle->cardaddr = 0; + SLIST_INSERT_HEAD(&sc->rl, rle, link); + + if (flags & RF_ACTIVE) + if (bus_activate_resource(child, type, *rid, res) != 0) { + bus_release_resource(child, type, *rid, res); + return (NULL); + } + + return (res); +} + +static int +pccbb_cardbus_release_resource(device_t brdev, device_t child, int type, + int rid, struct resource *res) +{ + struct pccbb_softc *sc = device_get_softc(brdev); + struct pccbb_reslist *rle; + int error; + + if (rman_get_flags(res) & RF_ACTIVE) { + error = bus_deactivate_resource(child, type, rid, res); + if (error != 0) + return (error); + } + + SLIST_FOREACH(rle, &sc->rl, link) { + if (rle->res == res) { + SLIST_REMOVE(&sc->rl, rle, pccbb_reslist, link); + free(rle, M_DEVBUF); + break; + } + } + + return (BUS_RELEASE_RESOURCE(device_get_parent(brdev), child, + type, rid, res)); +} + +/************************************************************************/ +/* PC Card Power Functions */ +/************************************************************************/ + +static int +pccbb_pcic_power_enable_socket(device_t brdev, device_t child) +{ + struct pccbb_softc *sc = device_get_softc(brdev); + int voltage; + int cardtype; + int win; + + DPRINTF(("pccbb_pcic_socket_enable:\n")); + + /* power down/up the socket to reset */ + voltage = pccbb_detect_voltage(brdev); + + pccbb_power(brdev, CARD_VCC_0V | CARD_VPP_0V); + if (voltage & CARD_5V_CARD) + pccbb_power(brdev, CARD_VCC_5V | CARD_VPP_VCC); + else if (voltage & CARD_3V_CARD) + pccbb_power(brdev, CARD_VCC_3V | CARD_VPP_VCC); + else { + device_printf(brdev, "Unknown card voltage\n"); + return (ENXIO); + } + + /* enable socket i/o */ + PCIC_MASK(sc, PCIC_PWRCTL, | PCIC_PWRCTL_OE); + + PCIC_WRITE(sc, PCIC_INTR, PCIC_INTR_ENABLE); + /* hold reset for 30ms */ + DELAY(30*1000); + /* clear the reset flag */ + PCIC_MASK(sc, PCIC_INTR, | PCIC_INTR_RESET); + /* wait 20ms as per pc card standard (r2.01) section 4.3.6 */ + DELAY(20*1000); + + pccbb_pcic_wait_ready(sc); + + /* disable all address windows */ + PCIC_WRITE(sc, PCIC_ADDRWIN_ENABLE, 0); + + CARD_GET_TYPE(child, &cardtype); + PCIC_MASK(sc, PCIC_INTR, | ((cardtype == PCCARD_IFTYPE_IO) ? + PCIC_INTR_CARDTYPE_IO : PCIC_INTR_CARDTYPE_MEM)); + DEVPRINTF((sc->sc_dev, "card type is %s\n", + (cardtype == PCCARD_IFTYPE_IO) ? "io" : "mem")); + + /* reinstall all the memory and io mappings */ + for (win = 0; win < PCIC_MEM_WINS; ++win) { + if (sc->memalloc & (1 << win)) { + pccbb_pcic_do_mem_map(sc, win); + } + } + for (win = 0; win < PCIC_IO_WINS; ++win) { + if (sc->ioalloc & (1 << win)) { + pccbb_pcic_do_io_map(sc, win); + } + } + return (0); +} + +static void +pccbb_pcic_power_disable_socket(device_t brdev, device_t child) +{ + struct pccbb_softc *sc = device_get_softc(brdev); + + DPRINTF(("pccbb_pcic_socket_disable\n")); + + /* reset signal asserting... */ + PCIC_MASK(sc, PCIC_INTR, & ~PCIC_INTR_RESET); + DELAY(2*1000); + + /* power down the socket */ + pccbb_power(brdev, CARD_VCC_0V | CARD_VPP_0V); + PCIC_MASK(sc, PCIC_PWRCTL, &~PCIC_PWRCTL_OE); + + /* wait 300ms until power fails (Tpf). */ + DELAY(300 * 1000); +} + +/************************************************************************/ +/* PC Card Resource Functions */ +/************************************************************************/ + +static void +pccbb_pcic_wait_ready(struct pccbb_softc *sc) +{ + int i; + DEVPRINTF((sc->sc_dev, "pccbb_pcic_wait_ready: status 0x%02x\n", + PCIC_READ(sc, PCIC_IF_STATUS))); + for (i = 0; i < 10000; i++) { + if (PCIC_READ(sc, PCIC_IF_STATUS) & PCIC_IF_STATUS_READY) { + return; + } + DELAY(500); + } + device_printf(sc->sc_dev, "ready never happened, status = %02x\n", + PCIC_READ(sc, PCIC_IF_STATUS)); +} + +#define PCIC_MEMINFO(NUM) { \ + PCIC_SYSMEM_ADDR ## NUM ## _START_LSB, \ + PCIC_SYSMEM_ADDR ## NUM ## _START_MSB, \ + PCIC_SYSMEM_ADDR ## NUM ## _STOP_LSB, \ + PCIC_SYSMEM_ADDR ## NUM ## _STOP_MSB, \ + PCIC_SYSMEM_ADDR ## NUM ## _WIN, \ + PCIC_CARDMEM_ADDR ## NUM ## _LSB, \ + PCIC_CARDMEM_ADDR ## NUM ## _MSB, \ + PCIC_ADDRWIN_ENABLE_MEM ## NUM ##, \ +} + +static struct mem_map_index_st { + int sysmem_start_lsb; + int sysmem_start_msb; + int sysmem_stop_lsb; + int sysmem_stop_msb; + int sysmem_win; + int cardmem_lsb; + int cardmem_msb; + int memenable; +} mem_map_index[] = { + PCIC_MEMINFO(0), + PCIC_MEMINFO(1), + PCIC_MEMINFO(2), + PCIC_MEMINFO(3), + PCIC_MEMINFO(4), +}; +#undef PCIC_MEMINFO + +static void +pccbb_pcic_do_mem_map(struct pccbb_softc *sc, int win) +{ + PCIC_WRITE(sc, mem_map_index[win].sysmem_start_lsb, + (sc->mem[win].addr >> PCIC_SYSMEM_ADDRX_SHIFT) & 0xff); + PCIC_WRITE(sc, mem_map_index[win].sysmem_start_msb, + ((sc->mem[win].addr >> (PCIC_SYSMEM_ADDRX_SHIFT + 8)) & + PCIC_SYSMEM_ADDRX_START_MSB_ADDR_MASK) | 0x80); + + PCIC_WRITE(sc, mem_map_index[win].sysmem_stop_lsb, + ((sc->mem[win].addr + sc->mem[win].realsize - 1) >> + PCIC_SYSMEM_ADDRX_SHIFT) & 0xff); + PCIC_WRITE(sc, mem_map_index[win].sysmem_stop_msb, + (((sc->mem[win].addr + sc->mem[win].realsize - 1) >> + (PCIC_SYSMEM_ADDRX_SHIFT + 8)) & + PCIC_SYSMEM_ADDRX_STOP_MSB_ADDR_MASK) | + PCIC_SYSMEM_ADDRX_STOP_MSB_WAIT2); + + PCIC_WRITE(sc, mem_map_index[win].sysmem_win, + (sc->mem[win].addr >> PCIC_MEMREG_WIN_SHIFT) & 0xff); + + PCIC_WRITE(sc, mem_map_index[win].cardmem_lsb, + (sc->mem[win].offset >> PCIC_CARDMEM_ADDRX_SHIFT) & 0xff); + PCIC_WRITE(sc, mem_map_index[win].cardmem_msb, + ((sc->mem[win].offset >> (PCIC_CARDMEM_ADDRX_SHIFT + 8)) & + PCIC_CARDMEM_ADDRX_MSB_ADDR_MASK) | + ((sc->mem[win].kind == PCCARD_MEM_ATTR) ? + PCIC_CARDMEM_ADDRX_MSB_REGACTIVE_ATTR : 0)); + + PCIC_MASK(sc, PCIC_ADDRWIN_ENABLE, | PCIC_ADDRWIN_ENABLE_MEMCS16 | + mem_map_index[win].memenable); + + DELAY(100); + +#ifdef CBB_DEBUG + { + int r1, r2, r3, r4, r5, r6, r7; + r1 = PCIC_READ(sc, mem_map_index[win].sysmem_start_msb); + r2 = PCIC_READ(sc, mem_map_index[win].sysmem_start_lsb); + r3 = PCIC_READ(sc, mem_map_index[win].sysmem_stop_msb); + r4 = PCIC_READ(sc, mem_map_index[win].sysmem_stop_lsb); + r5 = PCIC_READ(sc, mem_map_index[win].cardmem_msb); + r6 = PCIC_READ(sc, mem_map_index[win].cardmem_lsb); + r7 = PCIC_READ(sc, mem_map_index[win].sysmem_win); + DPRINTF(("pccbb_pcic_do_mem_map window %d: %02x%02x %02x%02x " + "%02x%02x %02x (%08x+%08x.%08x*%08lx)\n", + win, r1, r2, r3, r4, r5, r6, r7, + sc->mem[win].addr, sc->mem[win].size, sc->mem[win].realsize, + sc->mem[win].offset)); + } +#endif +} + +static int +pccbb_pcic_mem_map(struct pccbb_softc *sc, int kind, struct resource *res) +{ + int win; + struct pccbb_reslist *rle; + bus_addr_t card_addr; + + for (win = 0; win < PCIC_MEM_WINS; win++) { + if ((sc->memalloc & (1 << win)) == 0) { + sc->memalloc |= (1 << win); + break; + } + } + if (win >= PCIC_MEM_WINS) + return (1); + + SLIST_FOREACH(rle, &sc->rl, link) { + if (rle->res == res) + break; + } + if (!rle) { + device_printf(sc->sc_dev, + "pcic_map_mem: Memory resource not found\n"); + return (ENXIO); + } + card_addr = rle->cardaddr - rle->cardaddr % PCIC_MEM_PAGESIZE; + sc->mem[win].memt = rman_get_bustag(res); + sc->mem[win].memh = rman_get_bushandle(res); + sc->mem[win].addr = rman_get_start(res); + sc->mem[win].size = rman_get_end(res) - sc->mem[win].addr + 1; + sc->mem[win].realsize = sc->mem[win].size + PCIC_MEM_PAGESIZE - 1; + sc->mem[win].realsize = sc->mem[win].realsize - + (sc->mem[win].realsize % PCIC_MEM_PAGESIZE); + sc->mem[win].offset = ((long)card_addr) - + ((long)(sc->mem[win].addr)); + sc->mem[win].kind = kind; + + DPRINTF(("pccbb_pcic_mem_map window %d bus %x+%x+%lx card addr %x\n", + win, sc->mem[win].addr, sc->mem[win].size, + sc->mem[win].offset, card_addr)); + + pccbb_pcic_do_mem_map(sc, win); + + return (0); +} + +static void +pccbb_pcic_mem_unmap(struct pccbb_softc *sc, int window) +{ + if (window >= PCIC_MEM_WINS) + panic("pccbb_pcic_mem_unmap: window out of range"); + + PCIC_MASK(sc, PCIC_ADDRWIN_ENABLE, & ~mem_map_index[window].memenable); + sc->memalloc &= ~(1 << window); +} + +static int +pccbb_pcic_mem_findmap(struct pccbb_softc *sc, struct resource *res) +{ + int win; + + for (win = 0; win < PCIC_MEM_WINS; win++) { + if (sc->mem[win].memt == rman_get_bustag(res) && + sc->mem[win].addr == rman_get_start(res) && + sc->mem[win].size == rman_get_size(res)) + return (win); + } + device_printf(sc->sc_dev, "Memory map not found!\n"); + return (-1); +} + +#define PCIC_IOINFO(NUM) { \ + PCIC_IOADDR ## NUM ## _START_LSB, \ + PCIC_IOADDR ## NUM ## _START_MSB, \ + PCIC_IOADDR ## NUM ## _STOP_LSB, \ + PCIC_IOADDR ## NUM ## _STOP_MSB, \ + PCIC_ADDRWIN_ENABLE_IO ## NUM ##, \ + PCIC_IOCTL_IO ## NUM ## _WAITSTATE \ + | PCIC_IOCTL_IO ## NUM ## _ZEROWAIT \ + | PCIC_IOCTL_IO ## NUM ## _IOCS16SRC_MASK \ + | PCIC_IOCTL_IO ## NUM ## _DATASIZE_MASK, \ + { \ + PCIC_IOCTL_IO ## NUM ## _IOCS16SRC_CARD, \ + PCIC_IOCTL_IO ## NUM ## _IOCS16SRC_DATASIZE \ + | PCIC_IOCTL_IO ## NUM ## _DATASIZE_8BIT, \ + PCIC_IOCTL_IO ## NUM ## _IOCS16SRC_DATASIZE \ + | PCIC_IOCTL_IO ## NUM ## _DATASIZE_16BIT, \ + } \ +} + +static struct io_map_index_st { + int start_lsb; + int start_msb; + int stop_lsb; + int stop_msb; + int ioenable; + int ioctlmask; + int ioctlbits[3]; /* indexed by PCCARD_WIDTH_* */ +} io_map_index[] = { + PCIC_IOINFO(0), + PCIC_IOINFO(1), +}; +#undef PCIC_IOINFO + +static void +pccbb_pcic_do_io_map(struct pccbb_softc *sc, int win) +{ + PCIC_WRITE(sc, io_map_index[win].start_lsb, sc->io[win].addr & 0xff); + PCIC_WRITE(sc, io_map_index[win].start_msb, + (sc->io[win].addr >> 8) & 0xff); + + PCIC_WRITE(sc, io_map_index[win].stop_lsb, + (sc->io[win].addr + sc->io[win].size - 1) & 0xff); + PCIC_WRITE(sc, io_map_index[win].stop_msb, + ((sc->io[win].addr + sc->io[win].size - 1) >> 8) & 0xff); + + PCIC_MASK2(sc, PCIC_IOCTL, + & ~io_map_index[win].ioctlmask, + | io_map_index[win].ioctlbits[sc->io[win].width]); + + PCIC_MASK(sc, PCIC_ADDRWIN_ENABLE, | io_map_index[win].ioenable); +#ifdef CBB_DEBUG + { + int r1, r2, r3, r4; + r1 = PCIC_READ(sc, io_map_index[win].start_msb); + r2 = PCIC_READ(sc, io_map_index[win].start_lsb); + r3 = PCIC_READ(sc, io_map_index[win].stop_msb); + r4 = PCIC_READ(sc, io_map_index[win].stop_lsb); + DPRINTF(("pccbb_pcic_do_io_map window %d: %02x%02x %02x%02x " + "(%08x+%08x)\n", win, r1, r2, r3, r4, + sc->io[win].addr, sc->io[win].size)); + } +#endif +} + +static int +pccbb_pcic_io_map(struct pccbb_softc *sc, int width, struct resource *r) +{ + int win; +#ifdef CBB_DEBUG + static char *width_names[] = { "auto", "io8", "io16"}; +#endif + + for (win=0; win < PCIC_IO_WINS; win++) { + if ((sc->ioalloc & (1 << win)) == 0) { + sc->ioalloc |= (1 << win); + break; + } + } + if (win >= PCIC_IO_WINS) + return (1); + + sc->io[win].iot = rman_get_bustag(r); + sc->io[win].ioh = rman_get_bushandle(r); + sc->io[win].addr = rman_get_start(r); + sc->io[win].size = rman_get_end(r) - sc->io[win].addr + 1; + sc->io[win].flags = 0; + sc->io[win].width = width; + + DPRINTF(("pccbb_pcic_io_map window %d %s port %x+%x\n", + win, width_names[width], sc->io[win].addr, + sc->io[win].size)); + + pccbb_pcic_do_io_map(sc, win); + + return (0); +} + +static void +pccbb_pcic_io_unmap(struct pccbb_softc *sc, int window) +{ + if (window >= PCIC_IO_WINS) + panic("pccbb_pcic_io_unmap: window out of range"); + + PCIC_MASK(sc, PCIC_ADDRWIN_ENABLE, & ~io_map_index[window].ioenable); + + sc->ioalloc &= ~(1 << window); + + sc->io[window].iot = 0; + sc->io[window].ioh = 0; + sc->io[window].addr = 0; + sc->io[window].size = 0; + sc->io[window].flags = 0; + sc->io[window].width = 0; +} + +static int +pccbb_pcic_io_findmap(struct pccbb_softc *sc, struct resource *res) +{ + int win; + + for (win = 0; win < PCIC_IO_WINS; win++) { + if (sc->io[win].iot == rman_get_bustag(res) && + sc->io[win].addr == rman_get_start(res) && + sc->io[win].size == rman_get_size(res)) + return (win); + } + device_printf(sc->sc_dev, "IO map not found!\n"); + return (-1); +} + +static int +pccbb_pcic_activate_resource(device_t brdev, device_t child, int type, int rid, + struct resource *res) +{ + int err; + struct pccbb_softc *sc = device_get_softc(brdev); + if (!(rman_get_flags(res) & RF_ACTIVE)) { /* not already activated */ + switch (type) { + case SYS_RES_IOPORT: + err = pccbb_pcic_io_map(sc, 0, res); + break; + case SYS_RES_MEMORY: + err = pccbb_pcic_mem_map(sc, 0, res); + break; + default: + err = 0; + break; + } + if (err) + return (err); + + } + return (BUS_ACTIVATE_RESOURCE(device_get_parent(brdev), child, + type, rid, res)); +} + +static int +pccbb_pcic_deactivate_resource(device_t brdev, device_t child, int type, + int rid, struct resource *res) +{ + struct pccbb_softc *sc = device_get_softc(brdev); + int win; + + if (rman_get_flags(res) & RF_ACTIVE) { /* if activated */ + switch (type) { + case SYS_RES_IOPORT: + win = pccbb_pcic_io_findmap(sc, res); + if (win >= 0) + pccbb_pcic_io_unmap(sc, win); + else + return (ENOENT); + break; + case SYS_RES_MEMORY: + win = pccbb_pcic_mem_findmap(sc, res); + if (win >= 0) + pccbb_pcic_mem_unmap(sc, win); + else + return (ENOENT); + break; + } + } + return (BUS_DEACTIVATE_RESOURCE(device_get_parent(brdev), child, + type, rid, res)); +} + +static struct resource * +pccbb_pcic_alloc_resource(device_t brdev, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, uint flags) +{ + struct resource *res = NULL; + struct pccbb_softc *sc = device_get_softc(brdev); + struct pccbb_reslist *rle; + int tmp; + + if ((sc->sc_flags & PCCBB_PCIC_MEM_32) == 0) { + /* XXX: how do we do this? */ + panic("PCCBB bridge cannot handle non MEM_32 bridges\n"); + } + + switch (type) { + case SYS_RES_MEMORY: + if (start < pccbb_start_mem) + start = pccbb_start_mem; + if (end < start) + end = start; + flags = (flags & ~RF_ALIGNMENT_MASK) | + rman_make_alignment_flags(PCCBB_MEMALIGN); + break; + case SYS_RES_IOPORT: + if (start < 0x100) + start = 0x100; /* XXX tweakable? */ + if (end < start) + end = start; + break; + case SYS_RES_IRQ: + tmp = rman_get_start(sc->sc_irq_res); + if (start > tmp || end < tmp || count != 1) { + device_printf(child, "requested interrupt %ld-%ld," + "count = %ld not supported by pccbb\n", + start, end, count); + return (NULL); + } + flags |= RF_SHAREABLE; + start = end = rman_get_start(sc->sc_irq_res); + break; + } + res = BUS_ALLOC_RESOURCE(device_get_parent(brdev), child, type, rid, + start, end, count, flags & ~RF_ACTIVE); + if (res == NULL) + return (NULL); + + rle = malloc(sizeof(struct pccbb_reslist), M_DEVBUF, M_NOWAIT); + if (!rle) + panic("pccbb_pcic_alloc_resource: can't record entry!"); + rle->res = res; + rle->type = type; + rle->rid = *rid; + rle->cardaddr = 0; + SLIST_INSERT_HEAD(&sc->rl, rle, link); + + if (flags & RF_ACTIVE) { + if (bus_activate_resource(child, type, *rid, res) != 0) { + bus_release_resource(child, type, *rid, res); + return (NULL); + } + } + + return (res); +} + +static int +pccbb_pcic_release_resource(device_t brdev, device_t child, int type, + int rid, struct resource *res) +{ + struct pccbb_softc *sc = device_get_softc(brdev); + struct pccbb_reslist *rle; + int error; + + if (rman_get_flags(res) & RF_ACTIVE) { + error = bus_deactivate_resource(child, type, rid, res); + if (error != 0) + return (error); + } + + SLIST_FOREACH(rle, &sc->rl, link) { + if (rle->res == res) { + SLIST_REMOVE(&sc->rl, rle, pccbb_reslist, link); + free(rle, M_DEVBUF); + break; + } + } + + return (BUS_RELEASE_RESOURCE(device_get_parent(brdev), child, + type, rid, res)); +} + +/************************************************************************/ +/* PC Card methods */ +/************************************************************************/ + +static int +pccbb_pcic_set_res_flags(device_t brdev, device_t child, int type, int rid, + uint32_t flags) +{ + struct pccbb_softc *sc = device_get_softc(brdev); + struct resource *res; + struct pccbb_reslist *rle; + int win; + + res = NULL; + if (type != SYS_RES_MEMORY) + return (EINVAL); + SLIST_FOREACH(rle, &sc->rl, link) { + if (SYS_RES_MEMORY == rle->type && rid == rle->rid && + child == rle->res->r_dev) { + res = rle->res; + break; + } + } + + if (res == NULL) { + device_printf(brdev, + "set_res_flags: specified rid not found\n"); + return (ENOENT); + } + win = pccbb_pcic_mem_findmap(sc, res); + if (win < 0) { + device_printf(brdev, + "set_res_flags: specified resource not active\n"); + return (ENOENT); + } + + sc->mem[win].kind = flags; + pccbb_pcic_do_mem_map(sc, win); + return (0); +} + +static int +pccbb_pcic_set_memory_offset(device_t brdev, device_t child, int rid, + uint32_t cardaddr, uint32_t *deltap) +{ + struct pccbb_softc *sc = device_get_softc(brdev); + int win; + struct pccbb_reslist *rle; + struct resource *res; + uint32_t delta; + + win = -1; + + res = NULL; + SLIST_FOREACH(rle, &sc->rl, link) { + if (SYS_RES_MEMORY == rle->type && rid == rle->rid && + child == rle->res->r_dev) { + res = rle->res; + rle->cardaddr = cardaddr; + break; + } + } + + if (res == NULL) { + device_printf(brdev, + "set_memory_offset: specified rid not found\n"); + return (ENOENT); + } + win = pccbb_pcic_mem_findmap(sc, res); + if (win < 0) { + device_printf(brdev, + "set_memory_offset: specified resource not active\n"); + return (ENOENT); + } + + delta = cardaddr % PCIC_MEM_PAGESIZE; + if (deltap) + *deltap = delta; + cardaddr -= delta; + sc->mem[win].realsize = sc->mem[win].size + delta + + PCIC_MEM_PAGESIZE - 1; + sc->mem[win].realsize = sc->mem[win].realsize - + (sc->mem[win].realsize % PCIC_MEM_PAGESIZE); + sc->mem[win].offset = cardaddr - sc->mem[win].addr; + pccbb_pcic_do_mem_map(sc, win); + + return (0); +} + +/************************************************************************/ +/* POWER methods */ +/************************************************************************/ + +static int +pccbb_power_enable_socket(device_t brdev, device_t child) +{ + struct pccbb_softc *sc = device_get_softc(brdev); + + if (sc->sc_flags & PCCBB_16BIT_CARD) + return (pccbb_pcic_power_enable_socket(brdev, child)); + else + return (pccbb_cardbus_power_enable_socket(brdev, child)); +} + +static void +pccbb_power_disable_socket(device_t brdev, device_t child) +{ + struct pccbb_softc *sc = device_get_softc(brdev); + if (sc->sc_flags & PCCBB_16BIT_CARD) + pccbb_pcic_power_disable_socket(brdev, child); + else + pccbb_cardbus_power_disable_socket(brdev, child); +} +/************************************************************************/ +/* BUS Methods */ +/************************************************************************/ + + +static int +pccbb_activate_resource(device_t brdev, device_t child, int type, int rid, + struct resource *r) +{ + struct pccbb_softc *sc = device_get_softc(brdev); + + if (sc->sc_flags & PCCBB_16BIT_CARD) + return (pccbb_pcic_activate_resource(brdev, child, type, rid, r)); + else + return (pccbb_cardbus_activate_resource(brdev, child, type, rid, + r)); +} + +static int +pccbb_deactivate_resource(device_t brdev, device_t child, int type, + int rid, struct resource *r) +{ + struct pccbb_softc *sc = device_get_softc(brdev); + + if (sc->sc_flags & PCCBB_16BIT_CARD) + return (pccbb_pcic_deactivate_resource(brdev, child, type, + rid, r)); + else + return (pccbb_cardbus_deactivate_resource(brdev, child, type, + rid, r)); +} + +static struct resource * +pccbb_alloc_resource(device_t brdev, device_t child, int type, int *rid, + u_long start, u_long end, u_long count, uint flags) +{ + struct pccbb_softc *sc = device_get_softc(brdev); + + if (sc->sc_flags & PCCBB_16BIT_CARD) + return (pccbb_pcic_alloc_resource(brdev, child, type, rid, + start, end, count, flags)); + else + return (pccbb_cardbus_alloc_resource(brdev, child, type, rid, + start, end, count, flags)); +} + +static int +pccbb_release_resource(device_t brdev, device_t child, int type, int rid, + struct resource *r) +{ + struct pccbb_softc *sc = device_get_softc(brdev); + + if (sc->sc_flags & PCCBB_16BIT_CARD) + return (pccbb_pcic_release_resource(brdev, child, type, + rid, r)); + else + return (pccbb_cardbus_release_resource(brdev, child, type, + rid, r)); +} + +static int +pccbb_read_ivar(device_t brdev, device_t child, int which, uintptr_t *result) +{ + struct pccbb_softc *sc = device_get_softc(brdev); + + switch (which) { + case PCIB_IVAR_BUS: + *result = sc->sc_secbus; + return (0); + } + return (ENOENT); +} + +static int +pccbb_write_ivar(device_t brdev, device_t child, int which, uintptr_t value) +{ + struct pccbb_softc *sc = device_get_softc(brdev); + + switch (which) { + case PCIB_IVAR_BUS: + sc->sc_secbus = value; + break; + } + return (ENOENT); +} + +/************************************************************************/ +/* PCI compat methods */ +/************************************************************************/ + +static int +pccbb_maxslots(device_t brdev) +{ + return (0); +} + +static uint32_t +pccbb_read_config(device_t brdev, int b, int s, int f, int reg, int width) +{ + /* + * Pass through to the next ppb up the chain (i.e. our grandparent). + */ + return (PCIB_READ_CONFIG(device_get_parent(device_get_parent(brdev)), + b, s, f, reg, width)); +} + +static void +pccbb_write_config(device_t brdev, int b, int s, int f, int reg, uint32_t val, + int width) +{ + /* + * Pass through to the next ppb up the chain (i.e. our grandparent). + */ + PCIB_WRITE_CONFIG(device_get_parent(device_get_parent(brdev)), + b, s, f, reg, val, width); +} + +static int +pccbb_suspend(device_t self) +{ + int error = 0; + struct pccbb_softc* sc = device_get_softc(self); + + bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intrhand); + error = bus_generic_suspend(self); + return (error); +} + +static int +pccbb_resume(device_t self) +{ + int error = 0; + struct pccbb_softc *sc = (struct pccbb_softc *)device_get_softc(self); + uint32_t tmp; + + pci_write_config(self, PCCBBR_SOCKBASE, + rman_get_start(sc->sc_base_res), 4); + DEVPRINTF((self, "PCI Memory allocated: %08lx\n", + rman_get_start(sc->sc_base_res))); + + pccbb_chipinit(sc); + + /* CSC Interrupt: Card detect interrupt on */ + pccbb_setb(sc, PCCBB_SOCKET_MASK, PCCBB_SOCKET_MASK_CD); + + /* reset interrupt */ + tmp = pccbb_get(sc, PCCBB_SOCKET_EVENT); + pccbb_set(sc, PCCBB_SOCKET_EVENT, tmp); + + /* re-establish the interrupt. */ + if (bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO, pccbb_intr, sc, + &(sc->sc_intrhand))) { + device_printf(self, "couldn't re-establish interrupt"); + bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res); + bus_release_resource(self, SYS_RES_MEMORY, PCCBBR_SOCKBASE, + sc->sc_base_res); + mtx_destroy(&sc->sc_mtx); + error = ENOMEM; + } + bus_generic_resume(self); + + /* wakeup thread */ + if (!error) { + mtx_lock(&sc->sc_mtx); + wakeup(sc); + mtx_unlock(&sc->sc_mtx); + } + return (error); +} + +static device_method_t pccbb_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pccbb_probe), + DEVMETHOD(device_attach, pccbb_attach), + DEVMETHOD(device_detach, pccbb_detach), + DEVMETHOD(device_shutdown, pccbb_shutdown), + DEVMETHOD(device_suspend, pccbb_suspend), + DEVMETHOD(device_resume, pccbb_resume), + + /* bus methods */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_read_ivar, pccbb_read_ivar), + DEVMETHOD(bus_write_ivar, pccbb_write_ivar), + DEVMETHOD(bus_alloc_resource, pccbb_alloc_resource), + DEVMETHOD(bus_release_resource, pccbb_release_resource), + DEVMETHOD(bus_activate_resource, pccbb_activate_resource), + DEVMETHOD(bus_deactivate_resource, pccbb_deactivate_resource), + DEVMETHOD(bus_driver_added, pccbb_driver_added), + DEVMETHOD(bus_child_detached, pccbb_child_detached), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + + /* 16-bit card interface */ + DEVMETHOD(card_set_res_flags, pccbb_pcic_set_res_flags), + DEVMETHOD(card_set_memory_offset, pccbb_pcic_set_memory_offset), + DEVMETHOD(card_reprobe_card, pccbb_card_reprobe), + + /* power interface */ + DEVMETHOD(power_enable_socket, pccbb_power_enable_socket), + DEVMETHOD(power_disable_socket, pccbb_power_disable_socket), + + /* pcib compatibility interface */ + DEVMETHOD(pcib_maxslots, pccbb_maxslots), + DEVMETHOD(pcib_read_config, pccbb_read_config), + DEVMETHOD(pcib_write_config, pccbb_write_config), + {0,0} +}; + +static driver_t pccbb_driver = { + "pccbb", + pccbb_methods, + sizeof(struct pccbb_softc) +}; + +static devclass_t pccbb_devclass; + +DRIVER_MODULE(pccbb, pci, pccbb_driver, pccbb_devclass, 0, 0); + +SYSINIT(pccbb, SI_SUB_KTHREAD_IDLE, SI_ORDER_ANY, pccbb_start_threads, 0); diff --git a/sys/dev/pccbb/pccbbdevid.h b/sys/dev/pccbb/pccbbdevid.h new file mode 100644 index 0000000..1ac4380 --- /dev/null +++ b/sys/dev/pccbb/pccbbdevid.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2001 M. Warner Losh. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* Vendor/Device IDs */ +#define PCI_DEVICE_ID_INTEL_82092AA 0x12218086ul /* 16bit I/O */ +#define PCI_DEVICE_ID_PCIC_CLPD6729 0x11001013ul /* 16bit I/O */ +#define PCI_DEVICE_ID_PCIC_CLPD6832 0x11101013ul +#define PCI_DEVICE_ID_PCIC_CLPD6833 0x11131013ul +#define PCI_DEVICE_ID_PCIC_CLPD6834 0x11121013ul +#define PCI_DEVICE_ID_PCIC_OZ6729 0x67291217ul +#define PCI_DEVICE_ID_PCIC_OZ6730 0x673A1217ul +#define PCI_DEVICE_ID_PCIC_OZ6832 0x68321217ul /* Also 6833 */ +#define PCI_DEVICE_ID_PCIC_OZ6860 0x68361217ul /* Also 6836 */ +#define PCI_DEVICE_ID_PCIC_OZ6872 0x68721217ul /* Also 6812 */ +#define PCI_DEVICE_ID_PCIC_OZ6912 0x69721217ul /* Also 6972? */ +#define PCI_DEVICE_ID_PCIC_OZ6922 0x69251217ul +#define PCI_DEVICE_ID_PCIC_OZ6933 0x69331217ul +#define PCI_DEVICE_ID_RICOH_RL5C465 0x04651180ul +#define PCI_DEVICE_ID_RICOH_RL5C466 0x04661180ul +#define PCI_DEVICE_ID_RICOH_RL5C475 0x04751180ul +#define PCI_DEVICE_ID_RICOH_RL5C476 0x04761180ul +#define PCI_DEVICE_ID_RICOH_RL5C477 0x04771180ul +#define PCI_DEVICE_ID_RICOH_RL5C478 0x04781180ul +#define PCI_DEVICE_ID_OMEGA_82C094 0x1221119bul /* 16bit I/O */ +#define PCI_DEVICE_ID_PCIC_TI1031 0xac13104cul +#define PCI_DEVICE_ID_PCIC_TI1130 0xac12104cul +#define PCI_DEVICE_ID_PCIC_TI1131 0xac15104cul +#define PCI_DEVICE_ID_PCIC_TI1210 0xac1a104cul +#define PCI_DEVICE_ID_PCIC_TI1211 0xac1e104cul +#define PCI_DEVICE_ID_PCIC_TI1220 0xac17104cul +#define PCI_DEVICE_ID_PCIC_TI1221 0xac19104cul +#define PCI_DEVICE_ID_PCIC_TI1225 0xac1c104cul +#define PCI_DEVICE_ID_PCIC_TI1250 0xac16104cul +#define PCI_DEVICE_ID_PCIC_TI1260 0xac18104cul +#define PCI_DEVICE_ID_PCIC_TI1260B 0xac30104cul +#define PCI_DEVICE_ID_PCIC_TI1251 0xac1d104cul +#define PCI_DEVICE_ID_PCIC_TI1251B 0xac1f104cul +#define PCI_DEVICE_ID_PCIC_TI1410 0xac50104cul +#define PCI_DEVICE_ID_PCIC_TI1420 0xac51104cul +#define PCI_DEVICE_ID_PCIC_TI1421 0xac53104cul +#define PCI_DEVICE_ID_PCIC_TI1450 0xac1b104cul +#define PCI_DEVICE_ID_PCIC_TI1451 0xac52104cul +#define PCI_DEVICE_ID_PCIC_TI4410 0xac41104cul +#define PCI_DEVICE_ID_PCIC_TI4450 0xac40104cul +#define PCI_DEVICE_ID_PCIC_TI4451 0xac42104cul +#define PCI_DEVICE_ID_TOSHIBA_TOPIC95 0x06031179ul +#define PCI_DEVICE_ID_TOSHIBA_TOPIC95B 0x060a1179ul +#define PCI_DEVICE_ID_TOSHIBA_TOPIC97 0x060f1179ul +#define PCI_DEVICE_ID_TOSHIBA_TOPIC100 0x06171179ul + +/* + * Other ID, from sources too vague to be reliable + * Mfg model PCI ID + * smc/Databook DB87144 0x310610b3 + * SMC/databook smc34c90 0xb10610b3 + * Omega/Trident 82c194 0x01941023 + * Omega/Trident 82c722 0x07221023? + * Opti 82c814 0xc8141045 + * Opti 82c824 0xc8241045 + * NEC uPD66369 0x003e1033 + */ diff --git a/sys/dev/pccbb/pccbbreg.h b/sys/dev/pccbb/pccbbreg.h new file mode 100644 index 0000000..6705ede --- /dev/null +++ b/sys/dev/pccbb/pccbbreg.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2000,2001 Jonathan Chen. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Register definitions for PCI to Cardbus Bridge chips + */ + + +/* PCI header registers */ +#define PCCBBR_SOCKBASE 0x10 /* len=4 */ + +#define PCCBBR_MEMBASE0 0x1c /* len=4 */ +#define PCCBBR_MEMLIMIT0 0x20 /* len=4 */ +#define PCCBBR_MEMBASE1 0x24 /* len=4 */ +#define PCCBBR_MEMLIMIT1 0x28 /* len=4 */ +#define PCCBBR_IOBASE0 0x2c /* len=4 */ +#define PCCBBR_IOLIMIT0 0x30 /* len=4 */ +#define PCCBBR_IOBASE1 0x34 /* len=4 */ +#define PCCBBR_IOLIMIT1 0x38 /* len=4 */ +#define PCCBB_MEMALIGN 4096 +#define PCCBB_IOALIGN 4 + +#define PCCBBR_INTRLINE 0x3c /* len=1 */ +#define PCCBBR_INTRPIN 0x3d /* len=1 */ +#define PCCBBR_BRIDGECTRL 0x3e /* len=2 */ +# define PCCBBM_BRIDGECTRL_MASTER_ABORT 0x0020 +# define PCCBBM_BRIDGECTRL_RESET 0x0040 +# define PCCBBM_BRIDGECTRL_INTR_IREQ_EN 0x0080 +# define PCCBBM_BRIDGECTRL_PREFETCH_0 0x0100 +# define PCCBBM_BRIDGECTRL_PREFETCH_1 0x0200 +# define PCCBBM_BRIDGECTRL_WRITE_POST_EN 0x0400 + /* additional bit for RF5C46[567] */ +# define PCCBBM_BRIDGECTRL_RL_3E0_EN 0x0800 +# define PCCBBM_BRIDGECTRL_RL_3E2_EN 0x1000 + +#define PCCBBR_LEGACY 0x44 /* len=4 */ + +#define PCCBBR_CBCTRL 0x91 /* len=1 */ + /* bits for TI 113X */ +# define PCCBBM_CBCTRL_113X_RI_EN 0x80 +# define PCCBBM_CBCTRL_113X_ZV_EN 0x40 +# define PCCBBM_CBCTRL_113X_PCI_IRQ_EN 0x20 +# define PCCBBM_CBCTRL_113X_PCI_INTR 0x10 +# define PCCBBM_CBCTRL_113X_PCI_CSC 0x08 +# define PCCBBM_CBCTRL_113X_PCI_CSC_D 0x04 +# define PCCBBM_CBCTRL_113X_SPEAKER_EN 0x02 +# define PCCBBM_CBCTRL_113X_INTR_DET 0x01 + /* bits for TI 12XX */ +# define PCCBBM_CBCTRL_12XX_RI_EN 0x80 +# define PCCBBM_CBCTRL_12XX_ZV_EN 0x40 +# define PCCBBM_CBCTRL_12XX_AUD2MUX 0x04 +# define PCCBBM_CBCTRL_12XX_SPEAKER_EN 0x02 +# define PCCBBM_CBCTRL_12XX_INTR_DET 0x01 +#define PCCBBR_DEVCTRL 0x92 /* len=1 */ +# define PCCBBM_DEVCTRL_INT_SERIAL 0x04 +# define PCCBBM_DEVCTRL_INT_PCI 0x02 + +#define PCCBBR_TOPIC_SOCKETCTRL 0x90 +# define PCCBBM_TOPIC_SOCKETCTRL_SCR_IRQSEL 0x00000001 /* PCI intr */ + +#define PCCBBR_TOPIC_SLOTCTRL 0xa0 +# define PCCBBM_TOPIC_SLOTCTRL_SLOTON 0x00000080 +# define PCCBBM_TOPIC_SLOTCTRL_SLOTEN 0x00000040 +# define PCCBBM_TOPIC_SLOTCTRL_ID_LOCK 0x00000020 +# define PCCBBM_TOPIC_SLOTCTRL_ID_WP 0x00000010 +# define PCCBBM_TOPIC_SLOTCTRL_PORT_MASK 0x0000000c +# define PCCBBM_TOPIC_SLOTCTRL_PORT_SHIFT 2 +# define PCCBBM_TOPIC_SLOTCTRL_OSF_MASK 0x00000003 +# define PCCBBM_TOPIC_SLOTCTRL_OSF_SHIFT 0 +# define PCCBBM_TOPIC_SLOTCTRL_INTB 0x00002000 +# define PCCBBM_TOPIC_SLOTCTRL_INTA 0x00001000 +# define PCCBBM_TOPIC_SLOTCTRL_INT_MASK 0x00003000 +# define PCCBBM_TOPIC_SLOTCTRL_CLOCK_MASK 0x00000c00 +# define PCCBBM_TOPIC_SLOTCTRL_CLOCK_2 0x00000800 /* PCI Clk/2 */ +# define PCCBBM_TOPIC_SLOTCTRL_CLOCK_1 0x00000400 /* PCI Clk */ +# define PCCBBM_TOPIC_SLOTCTRL_CLOCK_0 0x00000000 /* no clock */ +# define PCCBBM_TOPIC_SLOTCTRL_CARDBUS 0x80000000 +# define PCCBBM_TOPIC_SLOTCTRL_VS1 0x04000000 +# define PCCBBM_TOPIC_SLOTCTRL_VS2 0x02000000 +# define PCCBBM_TOPIC_SLOTCTRL_SWDETECT 0x01000000 + +/* Socket definitions */ +#define PCCBB_SOCKET_EVENT_CSTS 0x01 /* Card Status Change */ +#define PCCBB_SOCKET_EVENT_CD1 0x02 /* Card Detect 1 */ +#define PCCBB_SOCKET_EVENT_CD2 0x04 /* Card Detect 2 */ +#define PCCBB_SOCKET_EVENT_CD 0x06 /* Card Detect all */ +#define PCCBB_SOCKET_EVENT_POWER 0x08 /* Power Cycle */ + +#define PCCBB_SOCKET_MASK_CSTS 0x01 /* Card Status Change */ +#define PCCBB_SOCKET_MASK_CD 0x06 /* Card Detect */ +#define PCCBB_SOCKET_MASK_POWER 0x08 /* Power Cycle */ + +#define PCCBB_SOCKET_STAT_CARDSTS 0x00000001 /* Card Status Change */ +#define PCCBB_SOCKET_STAT_CD1 0x00000002 /* Card Detect 1 */ +#define PCCBB_SOCKET_STAT_CD2 0x00000004 /* Card Detect 2 */ +#define PCCBB_SOCKET_STAT_CD 0x00000006 /* Card Detect all */ +#define PCCBB_SOCKET_STAT_PWRCYCLE 0x00000008 /* Power Cycle */ +#define PCCBB_SOCKET_STAT_16BIT 0x00000010 /* 16-bit Card */ +#define PCCBB_SOCKET_STAT_CB 0x00000020 /* Cardbus Card */ +#define PCCBB_SOCKET_STAT_IREQ 0x00000040 /* Ready */ +#define PCCBB_SOCKET_STAT_NOTCARD 0x00000080 /* Unrecognized Card */ +#define PCCBB_SOCKET_STAT_DATALOST 0x00000100 /* Data Lost */ +#define PCCBB_SOCKET_STAT_BADVCC 0x00000200 /* Bad VccRequest */ +#define PCCBB_SOCKET_STAT_5VCARD 0x00000400 /* 5 V Card */ +#define PCCBB_SOCKET_STAT_3VCARD 0x00000800 /* 3.3 V Card */ +#define PCCBB_SOCKET_STAT_XVCARD 0x00001000 /* X.X V Card */ +#define PCCBB_SOCKET_STAT_YVCARD 0x00002000 /* Y.Y V Card */ +#define PCCBB_SOCKET_STAT_5VSOCK 0x10000000 /* 5 V Socket */ +#define PCCBB_SOCKET_STAT_3VSOCK 0x20000000 /* 3.3 V Socket */ +#define PCCBB_SOCKET_STAT_XVSOCK 0x40000000 /* X.X V Socket */ +#define PCCBB_SOCKET_STAT_YVSOCK 0x80000000 /* Y.Y V Socket */ + +#define PCCBB_SOCKET_FORCE_BADVCC 0x0200 /* Bad Vcc Request */ + +#define PCCBB_SOCKET_CTRL_VPPMASK 0x07 +#define PCCBB_SOCKET_CTRL_VPP_OFF 0x00 +#define PCCBB_SOCKET_CTRL_VPP_12V 0x01 +#define PCCBB_SOCKET_CTRL_VPP_5V 0x02 +#define PCCBB_SOCKET_CTRL_VPP_3V 0x03 +#define PCCBB_SOCKET_CTRL_VPP_XV 0x04 +#define PCCBB_SOCKET_CTRL_VPP_YV 0x05 + +#define PCCBB_SOCKET_CTRL_VCCMASK 0x70 +#define PCCBB_SOCKET_CTRL_VCC_OFF 0x00 +#define PCCBB_SOCKET_CTRL_VCC_5V 0x20 +#define PCCBB_SOCKET_CTRL_VCC_3V 0x30 +#define PCCBB_SOCKET_CTRL_VCC_XV 0x40 +#define PCCBB_SOCKET_CTRL_VCC_YV 0x50 + +#define PCCBB_SOCKET_CTRL_STOPCLK 0x80 + +#include <dev/pccbb/pccbbdevid.h> + +#define PCCBB_SOCKET_EVENT 0x00 +#define PCCBB_SOCKET_MASK 0x04 +#define PCCBB_SOCKET_STATE 0x08 +#define PCCBB_SOCKET_FORCE 0x0c +#define PCCBB_SOCKET_CONTROL 0x10 +#define PCCBB_SOCKET_POWER 0x14 diff --git a/sys/dev/pccbb/pccbbvar.h b/sys/dev/pccbb/pccbbvar.h new file mode 100644 index 0000000..fad5a03 --- /dev/null +++ b/sys/dev/pccbb/pccbbvar.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2000,2001 Jonathan Chen. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Structure definitions for the Cardbus Bridge driver + */ + +struct intrhand { + void(*func)(void*arg); + void *arg; + STAILQ_ENTRY(intrhand) entries; +}; + +struct pccbb_reslist { + SLIST_ENTRY(pccbb_reslist) link; + struct resource *res; + int type; + int rid; + /* note: unlike the regular resource list, there can be + * duplicate rid's in the same list. However, the + * combination of rid and res->r_dev should be unique. + */ + bus_addr_t cardaddr; /* for 16-bit pccard memory */ +}; + +#define PCCBB_AUTO_OPEN_SMALLHOLE 0x100 + +struct pccbb_softc { + device_t sc_dev; + struct resource *sc_base_res; + struct resource *sc_irq_res; + void *sc_intrhand; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + u_int8_t sc_secbus; + u_int8_t sc_subbus; + struct mtx sc_mtx; + u_int32_t sc_flags; +#define PCCBB_PCIC_IO_RELOC 0x01 +#define PCCBB_PCIC_MEM_32 0x02 +#define PCCBB_16BIT_CARD 0x02000000 +#define PCCBB_KTHREAD_RUNNING 0x04000000 +#define PCCBB_KTHREAD_DONE 0x08000000 + int sc_chipset; /* chipset id */ +#define CB_UNKNOWN 0 /* NOT Cardbus-PCI bridge */ +#define CB_TI113X 1 /* TI PCI1130/1131 */ +#define CB_TI12XX 2 /* TI PCI1250/1220 */ +#define CB_RF5C47X 3 /* RICOH RF5C475/476/477 */ +#define CB_RF5C46X 4 /* RICOH RF5C465/466/467 */ +#define CB_TOPIC95 5 /* Toshiba ToPIC95 */ +#define CB_TOPIC95B 6 /* Toshiba ToPIC95B */ +#define CB_TOPIC97 7 /* Toshiba ToPIC97/100 */ +#define CB_CIRRUS 8 /* Cirrus Logic CLPD683x */ + SLIST_HEAD(, pccbb_reslist) rl; + + device_t sc_cbdev; + device_t sc_pccarddev; + + /* PC Card stuff */ + int memalloc; + struct pccard_mem_handle mem[PCIC_MEM_WINS]; + int ioalloc; + struct pccard_io_handle io[PCIC_IO_WINS]; + + /* kthread staff */ + struct proc *event_thread; +}; + +/* result of detect_card */ +#define CARD_UKN_CARD 0x00 +#define CARD_5V_CARD 0x01 +#define CARD_3V_CARD 0x02 +#define CARD_XV_CARD 0x04 +#define CARD_YV_CARD 0x08 + +/* for power_socket */ +#define CARD_VCC_UC 0x0000 +#define CARD_VCC_3V 0x0001 +#define CARD_VCC_XV 0x0002 +#define CARD_VCC_YV 0x0003 +#define CARD_VCC_0V 0x0004 +#define CARD_VCC_5V 0x0005 +#define CARD_VCCMASK 0x000f +#define CARD_VPP_UC 0x0000 +#define CARD_VPP_VCC 0x0010 +#define CARD_VPP_12V 0x0030 +#define CARD_VPP_0V 0x0040 +#define CARD_VPPMASK 0x00f0 |