pccard.c revision 52506
152506Simp/* $NetBSD: pcmcia.c,v 1.13 1998/12/24 04:51:59 marc Exp $ */ 252506Simp/* $FreeBSD: head/sys/dev/pccard/pccard.c 52506 1999-10-26 06:52:31Z imp $ */ 352506Simp 452506Simp/* 552506Simp * Copyright (c) 1997 Marc Horowitz. All rights reserved. 652506Simp * 752506Simp * Redistribution and use in source and binary forms, with or without 852506Simp * modification, are permitted provided that the following conditions 952506Simp * are met: 1052506Simp * 1. Redistributions of source code must retain the above copyright 1152506Simp * notice, this list of conditions and the following disclaimer. 1252506Simp * 2. Redistributions in binary form must reproduce the above copyright 1352506Simp * notice, this list of conditions and the following disclaimer in the 1452506Simp * documentation and/or other materials provided with the distribution. 1552506Simp * 3. All advertising materials mentioning features or use of this software 1652506Simp * must display the following acknowledgement: 1752506Simp * This product includes software developed by Marc Horowitz. 1852506Simp * 4. The name of the author may not be used to endorse or promote products 1952506Simp * derived from this software without specific prior written permission. 2052506Simp * 2152506Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2252506Simp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2352506Simp * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2452506Simp * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2552506Simp * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2652506Simp * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2752506Simp * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2852506Simp * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2952506Simp * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3052506Simp * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3152506Simp */ 3252506Simp 3352506Simp#include <sys/param.h> 3452506Simp#include <sys/systm.h> 3552506Simp#include <sys/malloc.h> 3652506Simp#include <sys/module.h> 3752506Simp#include <sys/kernel.h> 3852506Simp#include <sys/queue.h> 3952506Simp#include <sys/types.h> 4052506Simp 4152506Simp#include <sys/bus.h> 4252506Simp#include <machine/bus.h> 4352506Simp#include <sys/rman.h> 4452506Simp#include <machine/resource.h> 4552506Simp 4652506Simp#include <dev/pccard/pccardreg.h> 4752506Simp#include <dev/pccard/pccardchip.h> 4852506Simp#include <dev/pccard/pccardvar.h> 4952506Simp 5052506Simp#ifdef PCCARDDEBUG 5152506Simpint pccard_debug = 0; 5252506Simp#define DPRINTF(arg) if (pccard_debug) printf arg 5352506Simpint pccardintr_debug = 0; 5452506Simp/* this is done this way to avoid doing lots of conditionals 5552506Simp at interrupt level. */ 5652506Simp#define PCCARD_CARD_INTR (pccardintr_debug?pccard_card_intrdebug:pccard_card_intr) 5752506Simp#else 5852506Simp#define DPRINTF(arg) 5952506Simp#define PCCARD_CARD_INTR (pccard_card_intr) 6052506Simp#endif 6152506Simp 6252506Simp#ifdef PCCARDVERBOSE 6352506Simpint pccard_verbose = 1; 6452506Simp#else 6552506Simpint pccard_verbose = 0; 6652506Simp#endif 6752506Simp 6852506Simpint pccard_print __P((void *, const char *)); 6952506Simp 7052506Simpstatic __inline void pccard_socket_enable __P((pccard_chipset_tag_t, 7152506Simp pccard_chipset_handle_t *)); 7252506Simpstatic __inline void pccard_socket_disable __P((pccard_chipset_tag_t, 7352506Simp pccard_chipset_handle_t *)); 7452506Simp 7552506Simpint pccard_card_intr __P((void *)); 7652506Simp#ifdef PCCARDDEBUG 7752506Simpint pccard_card_intrdebug __P((void *)); 7852506Simp#endif 7952506Simp 8052506Simpint 8152506Simppccard_ccr_read(pf, ccr) 8252506Simp struct pccard_function *pf; 8352506Simp int ccr; 8452506Simp{ 8552506Simp 8652506Simp return (bus_space_read_1(pf->pf_ccrt, pf->pf_ccrh, 8752506Simp pf->pf_ccr_offset + ccr)); 8852506Simp} 8952506Simp 9052506Simpvoid 9152506Simppccard_ccr_write(pf, ccr, val) 9252506Simp struct pccard_function *pf; 9352506Simp int ccr; 9452506Simp int val; 9552506Simp{ 9652506Simp 9752506Simp if ((pf->ccr_mask) & (1 << (ccr / 2))) { 9852506Simp bus_space_write_1(pf->pf_ccrt, pf->pf_ccrh, 9952506Simp pf->pf_ccr_offset + ccr, val); 10052506Simp } 10152506Simp} 10252506Simp 10352506Simpstatic int 10452506Simppccard_card_attach(device_t dev) 10552506Simp{ 10652506Simp struct pccard_softc *sc = (struct pccard_softc *) 10752506Simp device_get_softc(dev); 10852506Simp struct pccard_function *pf; 10952506Simp struct pccard_attach_args paa; 11052506Simp int attached; 11152506Simp 11252506Simp /* 11352506Simp * this is here so that when socket_enable calls gettype, trt happens 11452506Simp */ 11552506Simp STAILQ_INIT(&sc->card.pf_head); 11652506Simp 11752506Simp pccard_chip_socket_enable(sc->pct, sc->pch); 11852506Simp 11952506Simp pccard_read_cis(sc); 12052506Simp 12152506Simp pccard_chip_socket_disable(sc->pct, sc->pch); 12252506Simp 12352506Simp pccard_check_cis_quirks(dev); 12452506Simp 12552506Simp /* 12652506Simp * bail now if the card has no functions, or if there was an error in 12752506Simp * the cis. 12852506Simp */ 12952506Simp 13052506Simp if (sc->card.error) 13152506Simp return (1); 13252506Simp if (STAILQ_EMPTY(&sc->card.pf_head)) 13352506Simp return (1); 13452506Simp 13552506Simp if (pccard_verbose) 13652506Simp pccard_print_cis(dev); 13752506Simp 13852506Simp attached = 0; 13952506Simp 14052506Simp STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { 14152506Simp if (STAILQ_EMPTY(&pf->cfe_head)) 14252506Simp continue; 14352506Simp 14452506Simp#ifdef DIAGNOSTIC 14552506Simp if (pf->child != NULL) { 14652506Simp printf("%s: %s still attached to function %d!\n", 14752506Simp sc->dev.dv_xname, pf->child->dv_xname, 14852506Simp pf->number); 14952506Simp panic("pccard_card_attach"); 15052506Simp } 15152506Simp#endif 15252506Simp pf->sc = sc; 15352506Simp pf->child = NULL; 15452506Simp pf->cfe = NULL; 15552506Simp pf->ih_fct = NULL; 15652506Simp pf->ih_arg = NULL; 15752506Simp } 15852506Simp 15952506Simp STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { 16052506Simp if (STAILQ_EMPTY(&pf->cfe_head)) 16152506Simp continue; 16252506Simp 16352506Simp paa.manufacturer = sc->card.manufacturer; 16452506Simp paa.product = sc->card.product; 16552506Simp paa.card = &sc->card; 16652506Simp paa.pf = pf; 16752506Simp 16852506Simp#if XXX 16952506Simp if (attach_child()) { 17052506Simp attached++; 17152506Simp 17252506Simp DPRINTF(("%s: function %d CCR at %d " 17352506Simp "offset %lx: %x %x %x %x, %x %x %x %x, %x\n", 17452506Simp sc->dev.dv_xname, pf->number, 17552506Simp pf->pf_ccr_window, pf->pf_ccr_offset, 17652506Simp pccard_ccr_read(pf, 0x00), 17752506Simp pccard_ccr_read(pf, 0x02), pccard_ccr_read(pf, 0x04), 17852506Simp pccard_ccr_read(pf, 0x06), pccard_ccr_read(pf, 0x0A), 17952506Simp pccard_ccr_read(pf, 0x0C), pccard_ccr_read(pf, 0x0E), 18052506Simp pccard_ccr_read(pf, 0x10), pccard_ccr_read(pf, 0x12))); 18152506Simp } 18252506Simp#endif 18352506Simp } 18452506Simp 18552506Simp return (attached ? 0 : 1); 18652506Simp} 18752506Simp 18852506Simpstatic void 18952506Simppccard_card_detach(device_t dev, int flags) 19052506Simp{ 19152506Simp struct pccard_softc *sc = (struct pccard_softc *) 19252506Simp device_get_softc(dev); 19352506Simp struct pccard_function *pf; 19452506Simp#if XXX 19552506Simp int error; 19652506Simp#endif 19752506Simp 19852506Simp /* 19952506Simp * We are running on either the PCCARD socket's event thread 20052506Simp * or in user context detaching a device by user request. 20152506Simp */ 20252506Simp STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { 20352506Simp if (STAILQ_FIRST(&pf->cfe_head) == NULL) 20452506Simp continue; 20552506Simp if (pf->child == NULL) 20652506Simp continue; 20752506Simp DPRINTF(("%s: detaching %s (function %d)\n", 20852506Simp sc->dev.dv_xname, pf->child->dv_xname, pf->number)); 20952506Simp#if XXX 21052506Simp if ((error = config_detach(pf->child, flags)) != 0) { 21152506Simp printf("%s: error %d detaching %s (function %d)\n", 21252506Simp sc->dev.dv_xname, error, pf->child->dv_xname, 21352506Simp pf->number); 21452506Simp } else 21552506Simp pf->child = NULL; 21652506Simp#endif 21752506Simp } 21852506Simp} 21952506Simp 22052506Simpstatic void 22152506Simppccard_card_deactivate(device_t dev) 22252506Simp{ 22352506Simp struct pccard_softc *sc = (struct pccard_softc *) 22452506Simp device_get_softc(dev); 22552506Simp struct pccard_function *pf; 22652506Simp 22752506Simp /* 22852506Simp * We're in the chip's card removal interrupt handler. 22952506Simp * Deactivate the child driver. The PCCARD socket's 23052506Simp * event thread will run later to finish the detach. 23152506Simp */ 23252506Simp STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { 23352506Simp if (STAILQ_FIRST(&pf->cfe_head) == NULL) 23452506Simp continue; 23552506Simp if (pf->child == NULL) 23652506Simp continue; 23752506Simp DPRINTF(("%s: deactivating %s (function %d)\n", 23852506Simp sc->dev.dv_xname, pf->child->dv_xname, pf->number)); 23952506Simp#if XXX 24052506Simp config_deactivate(pf->child); 24152506Simp#endif 24252506Simp } 24352506Simp} 24452506Simp 24552506Simpstatic int 24652506Simppccard_card_gettype(device_t dev) 24752506Simp{ 24852506Simp struct pccard_softc *sc = (struct pccard_softc *) 24952506Simp device_get_softc(dev); 25052506Simp struct pccard_function *pf; 25152506Simp 25252506Simp /* 25352506Simp * set the iftype to memory if this card has no functions (not yet 25452506Simp * probed), or only one function, and that is not initialized yet or 25552506Simp * that is memory. 25652506Simp */ 25752506Simp pf = STAILQ_FIRST(&sc->card.pf_head); 25852506Simp if (pf == NULL || 25952506Simp (STAILQ_NEXT(pf, pf_list) == NULL && 26052506Simp (pf->cfe == NULL || pf->cfe->iftype == PCCARD_IFTYPE_MEMORY))) 26152506Simp return (PCCARD_IFTYPE_MEMORY); 26252506Simp else 26352506Simp return (PCCARD_IFTYPE_IO); 26452506Simp} 26552506Simp 26652506Simp/* 26752506Simp * Initialize a PCCARD function. May be called as long as the function is 26852506Simp * disabled. 26952506Simp */ 27052506Simpvoid 27152506Simppccard_function_init(pf, cfe) 27252506Simp struct pccard_function *pf; 27352506Simp struct pccard_config_entry *cfe; 27452506Simp{ 27552506Simp if (pf->pf_flags & PFF_ENABLED) 27652506Simp panic("pccard_function_init: function is enabled"); 27752506Simp 27852506Simp /* Remember which configuration entry we are using. */ 27952506Simp pf->cfe = cfe; 28052506Simp} 28152506Simp 28252506Simpstatic __inline void pccard_socket_enable(pct, pch) 28352506Simp pccard_chipset_tag_t pct; 28452506Simp pccard_chipset_handle_t *pch; 28552506Simp{ 28652506Simp pccard_chip_socket_enable(pct, pch); 28752506Simp} 28852506Simp 28952506Simpstatic __inline void pccard_socket_disable(pct, pch) 29052506Simp pccard_chipset_tag_t pct; 29152506Simp pccard_chipset_handle_t *pch; 29252506Simp{ 29352506Simp pccard_chip_socket_disable(pct, pch); 29452506Simp} 29552506Simp 29652506Simp/* Enable a PCCARD function */ 29752506Simpint 29852506Simppccard_function_enable(pf) 29952506Simp struct pccard_function *pf; 30052506Simp{ 30152506Simp struct pccard_function *tmp; 30252506Simp int reg; 30352506Simp 30452506Simp if (pf->cfe == NULL) 30552506Simp panic("pccard_function_enable: function not initialized"); 30652506Simp 30752506Simp /* 30852506Simp * Increase the reference count on the socket, enabling power, if 30952506Simp * necessary. 31052506Simp */ 31152506Simp if (pf->sc->sc_enabled_count++ == 0) 31252506Simp pccard_chip_socket_enable(pf->sc->pct, pf->sc->pch); 31352506Simp DPRINTF(("%s: ++enabled_count = %d\n", pf->sc->dev.dv_xname, 31452506Simp pf->sc->sc_enabled_count)); 31552506Simp 31652506Simp if (pf->pf_flags & PFF_ENABLED) { 31752506Simp /* 31852506Simp * Don't do anything if we're already enabled. 31952506Simp */ 32052506Simp return (0); 32152506Simp } 32252506Simp 32352506Simp /* 32452506Simp * it's possible for different functions' CCRs to be in the same 32552506Simp * underlying page. Check for that. 32652506Simp */ 32752506Simp 32852506Simp STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) { 32952506Simp if ((tmp->pf_flags & PFF_ENABLED) && 33052506Simp (pf->ccr_base >= (tmp->ccr_base - tmp->pf_ccr_offset)) && 33152506Simp ((pf->ccr_base + PCCARD_CCR_SIZE) <= 33252506Simp (tmp->ccr_base - tmp->pf_ccr_offset + 33352506Simp tmp->pf_ccr_realsize))) { 33452506Simp pf->pf_ccrt = tmp->pf_ccrt; 33552506Simp pf->pf_ccrh = tmp->pf_ccrh; 33652506Simp pf->pf_ccr_realsize = tmp->pf_ccr_realsize; 33752506Simp 33852506Simp /* 33952506Simp * pf->pf_ccr_offset = (tmp->pf_ccr_offset - 34052506Simp * tmp->ccr_base) + pf->ccr_base; 34152506Simp */ 34252506Simp pf->pf_ccr_offset = 34352506Simp (tmp->pf_ccr_offset + pf->ccr_base) - 34452506Simp tmp->ccr_base; 34552506Simp pf->pf_ccr_window = tmp->pf_ccr_window; 34652506Simp break; 34752506Simp } 34852506Simp } 34952506Simp 35052506Simp if (tmp == NULL) { 35152506Simp if (pccard_mem_alloc(pf, PCCARD_CCR_SIZE, &pf->pf_pcmh)) 35252506Simp goto bad; 35352506Simp 35452506Simp if (pccard_mem_map(pf, PCCARD_MEM_ATTR, pf->ccr_base, 35552506Simp PCCARD_CCR_SIZE, &pf->pf_pcmh, &pf->pf_ccr_offset, 35652506Simp &pf->pf_ccr_window)) { 35752506Simp pccard_mem_free(pf, &pf->pf_pcmh); 35852506Simp goto bad; 35952506Simp } 36052506Simp } 36152506Simp 36252506Simp reg = (pf->cfe->number & PCCARD_CCR_OPTION_CFINDEX); 36352506Simp reg |= PCCARD_CCR_OPTION_LEVIREQ; 36452506Simp if (pccard_mfc(pf->sc)) { 36552506Simp reg |= (PCCARD_CCR_OPTION_FUNC_ENABLE | 36652506Simp PCCARD_CCR_OPTION_ADDR_DECODE); 36752506Simp if (pf->ih_fct) 36852506Simp reg |= PCCARD_CCR_OPTION_IREQ_ENABLE; 36952506Simp 37052506Simp } 37152506Simp pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg); 37252506Simp 37352506Simp reg = 0; 37452506Simp 37552506Simp if ((pf->cfe->flags & PCCARD_CFE_IO16) == 0) 37652506Simp reg |= PCCARD_CCR_STATUS_IOIS8; 37752506Simp if (pf->cfe->flags & PCCARD_CFE_AUDIO) 37852506Simp reg |= PCCARD_CCR_STATUS_AUDIO; 37952506Simp pccard_ccr_write(pf, PCCARD_CCR_STATUS, reg); 38052506Simp 38152506Simp pccard_ccr_write(pf, PCCARD_CCR_SOCKETCOPY, 0); 38252506Simp 38352506Simp if (pccard_mfc(pf->sc)) { 38452506Simp long tmp, iosize; 38552506Simp 38652506Simp tmp = pf->pf_mfc_iomax - pf->pf_mfc_iobase; 38752506Simp /* round up to nearest (2^n)-1 */ 38852506Simp for (iosize = 1; iosize < tmp; iosize <<= 1) 38952506Simp ; 39052506Simp iosize--; 39152506Simp 39252506Simp pccard_ccr_write(pf, PCCARD_CCR_IOBASE0, 39352506Simp pf->pf_mfc_iobase & 0xff); 39452506Simp pccard_ccr_write(pf, PCCARD_CCR_IOBASE1, 39552506Simp (pf->pf_mfc_iobase >> 8) & 0xff); 39652506Simp pccard_ccr_write(pf, PCCARD_CCR_IOBASE2, 0); 39752506Simp pccard_ccr_write(pf, PCCARD_CCR_IOBASE3, 0); 39852506Simp 39952506Simp pccard_ccr_write(pf, PCCARD_CCR_IOSIZE, iosize); 40052506Simp } 40152506Simp 40252506Simp#ifdef PCCARDDEBUG 40352506Simp if (pccard_debug) { 40452506Simp STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) { 40552506Simp printf("%s: function %d CCR at %d offset %lx: " 40652506Simp "%x %x %x %x, %x %x %x %x, %x\n", 40752506Simp tmp->sc->dev.dv_xname, tmp->number, 40852506Simp tmp->pf_ccr_window, tmp->pf_ccr_offset, 40952506Simp pccard_ccr_read(tmp, 0x00), 41052506Simp pccard_ccr_read(tmp, 0x02), 41152506Simp pccard_ccr_read(tmp, 0x04), 41252506Simp pccard_ccr_read(tmp, 0x06), 41352506Simp 41452506Simp pccard_ccr_read(tmp, 0x0A), 41552506Simp pccard_ccr_read(tmp, 0x0C), 41652506Simp pccard_ccr_read(tmp, 0x0E), 41752506Simp pccard_ccr_read(tmp, 0x10), 41852506Simp 41952506Simp pccard_ccr_read(tmp, 0x12)); 42052506Simp } 42152506Simp } 42252506Simp#endif 42352506Simp 42452506Simp pf->pf_flags |= PFF_ENABLED; 42552506Simp return (0); 42652506Simp 42752506Simp bad: 42852506Simp /* 42952506Simp * Decrement the reference count, and power down the socket, if 43052506Simp * necessary. 43152506Simp */ 43252506Simp if (--pf->sc->sc_enabled_count == 0) 43352506Simp pccard_chip_socket_disable(pf->sc->pct, pf->sc->pch); 43452506Simp DPRINTF(("%s: --enabled_count = %d\n", pf->sc->dev.dv_xname, 43552506Simp pf->sc->sc_enabled_count)); 43652506Simp 43752506Simp return (1); 43852506Simp} 43952506Simp 44052506Simp/* Disable PCCARD function. */ 44152506Simpvoid 44252506Simppccard_function_disable(pf) 44352506Simp struct pccard_function *pf; 44452506Simp{ 44552506Simp struct pccard_function *tmp; 44652506Simp 44752506Simp if (pf->cfe == NULL) 44852506Simp panic("pccard_function_enable: function not initialized"); 44952506Simp 45052506Simp if ((pf->pf_flags & PFF_ENABLED) == 0) { 45152506Simp /* 45252506Simp * Don't do anything if we're already disabled. 45352506Simp */ 45452506Simp return; 45552506Simp } 45652506Simp 45752506Simp /* 45852506Simp * it's possible for different functions' CCRs to be in the same 45952506Simp * underlying page. Check for that. Note we mark us as disabled 46052506Simp * first to avoid matching ourself. 46152506Simp */ 46252506Simp 46352506Simp pf->pf_flags &= ~PFF_ENABLED; 46452506Simp STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) { 46552506Simp if ((tmp->pf_flags & PFF_ENABLED) && 46652506Simp (pf->ccr_base >= (tmp->ccr_base - tmp->pf_ccr_offset)) && 46752506Simp ((pf->ccr_base + PCCARD_CCR_SIZE) <= 46852506Simp (tmp->ccr_base - tmp->pf_ccr_offset + tmp->pf_ccr_realsize))) 46952506Simp break; 47052506Simp } 47152506Simp 47252506Simp /* Not used by anyone else; unmap the CCR. */ 47352506Simp if (tmp == NULL) { 47452506Simp pccard_mem_unmap(pf, pf->pf_ccr_window); 47552506Simp pccard_mem_free(pf, &pf->pf_pcmh); 47652506Simp } 47752506Simp 47852506Simp /* 47952506Simp * Decrement the reference count, and power down the socket, if 48052506Simp * necessary. 48152506Simp */ 48252506Simp if (--pf->sc->sc_enabled_count == 0) 48352506Simp pccard_chip_socket_disable(pf->sc->pct, pf->sc->pch); 48452506Simp DPRINTF(("%s: --enabled_count = %d\n", pf->sc->dev.dv_xname, 48552506Simp pf->sc->sc_enabled_count)); 48652506Simp} 48752506Simp 48852506Simpint 48952506Simppccard_io_map(pf, width, offset, size, pcihp, windowp) 49052506Simp struct pccard_function *pf; 49152506Simp int width; 49252506Simp bus_addr_t offset; 49352506Simp bus_size_t size; 49452506Simp struct pccard_io_handle *pcihp; 49552506Simp int *windowp; 49652506Simp{ 49752506Simp int reg; 49852506Simp 49952506Simp if (pccard_chip_io_map(pf->sc->pct, pf->sc->pch, 50052506Simp width, offset, size, pcihp, windowp)) 50152506Simp return (1); 50252506Simp 50352506Simp /* 50452506Simp * XXX in the multifunction multi-iospace-per-function case, this 50552506Simp * needs to cooperate with io_alloc to make sure that the spaces 50652506Simp * don't overlap, and that the ccr's are set correctly 50752506Simp */ 50852506Simp 50952506Simp if (pccard_mfc(pf->sc)) { 51052506Simp long tmp, iosize; 51152506Simp 51252506Simp if (pf->pf_mfc_iomax == 0) { 51352506Simp pf->pf_mfc_iobase = pcihp->addr + offset; 51452506Simp pf->pf_mfc_iomax = pf->pf_mfc_iobase + size; 51552506Simp } else { 51652506Simp /* this makes the assumption that nothing overlaps */ 51752506Simp if (pf->pf_mfc_iobase > pcihp->addr + offset) 51852506Simp pf->pf_mfc_iobase = pcihp->addr + offset; 51952506Simp if (pf->pf_mfc_iomax < pcihp->addr + offset + size) 52052506Simp pf->pf_mfc_iomax = pcihp->addr + offset + size; 52152506Simp } 52252506Simp 52352506Simp tmp = pf->pf_mfc_iomax - pf->pf_mfc_iobase; 52452506Simp /* round up to nearest (2^n)-1 */ 52552506Simp for (iosize = 1; iosize >= tmp; iosize <<= 1) 52652506Simp ; 52752506Simp iosize--; 52852506Simp 52952506Simp pccard_ccr_write(pf, PCCARD_CCR_IOBASE0, 53052506Simp pf->pf_mfc_iobase & 0xff); 53152506Simp pccard_ccr_write(pf, PCCARD_CCR_IOBASE1, 53252506Simp (pf->pf_mfc_iobase >> 8) & 0xff); 53352506Simp pccard_ccr_write(pf, PCCARD_CCR_IOBASE2, 0); 53452506Simp pccard_ccr_write(pf, PCCARD_CCR_IOBASE3, 0); 53552506Simp 53652506Simp pccard_ccr_write(pf, PCCARD_CCR_IOSIZE, iosize); 53752506Simp 53852506Simp reg = pccard_ccr_read(pf, PCCARD_CCR_OPTION); 53952506Simp reg |= PCCARD_CCR_OPTION_ADDR_DECODE; 54052506Simp pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg); 54152506Simp } 54252506Simp return (0); 54352506Simp} 54452506Simp 54552506Simpvoid 54652506Simppccard_io_unmap(pf, window) 54752506Simp struct pccard_function *pf; 54852506Simp int window; 54952506Simp{ 55052506Simp 55152506Simp pccard_chip_io_unmap(pf->sc->pct, pf->sc->pch, window); 55252506Simp 55352506Simp /* XXX Anything for multi-function cards? */ 55452506Simp} 55552506Simp 55652506Simpvoid * 55752506Simppccard_intr_establish(pf, ipl, ih_fct, ih_arg) 55852506Simp struct pccard_function *pf; 55952506Simp int ipl; 56052506Simp int (*ih_fct) __P((void *)); 56152506Simp void *ih_arg; 56252506Simp{ 56352506Simp void *ret; 56452506Simp 56552506Simp /* behave differently if this is a multifunction card */ 56652506Simp 56752506Simp if (pccard_mfc(pf->sc)) { 56852506Simp int s, ihcnt, hiipl, reg; 56952506Simp struct pccard_function *pf2; 57052506Simp 57152506Simp /* 57252506Simp * mask all the ipl's which are already used by this card, 57352506Simp * and find the highest ipl number (lowest priority) 57452506Simp */ 57552506Simp 57652506Simp ihcnt = 0; 57752506Simp s = 0; /* this is only here to keep the compiler 57852506Simp happy */ 57952506Simp hiipl = 0; /* this is only here to keep the compiler 58052506Simp happy */ 58152506Simp 58252506Simp STAILQ_FOREACH(pf2, &pf->sc->card.pf_head, pf_list) { 58352506Simp if (pf2->ih_fct) { 58452506Simp DPRINTF(("%s: function %d has ih_fct %p\n", 58552506Simp pf->sc->dev.dv_xname, pf2->number, 58652506Simp pf2->ih_fct)); 58752506Simp 58852506Simp if (ihcnt == 0) { 58952506Simp hiipl = pf2->ih_ipl; 59052506Simp } else { 59152506Simp if (pf2->ih_ipl > hiipl) 59252506Simp hiipl = pf2->ih_ipl; 59352506Simp } 59452506Simp 59552506Simp ihcnt++; 59652506Simp } 59752506Simp } 59852506Simp 59952506Simp /* 60052506Simp * establish the real interrupt, changing the ipl if 60152506Simp * necessary 60252506Simp */ 60352506Simp 60452506Simp if (ihcnt == 0) { 60552506Simp#ifdef DIAGNOSTIC 60652506Simp if (pf->sc->ih != NULL) 60752506Simp panic("card has intr handler, but no function does"); 60852506Simp#endif 60952506Simp s = splhigh(); 61052506Simp 61152506Simp /* set up the handler for the new function */ 61252506Simp 61352506Simp pf->ih_fct = ih_fct; 61452506Simp pf->ih_arg = ih_arg; 61552506Simp pf->ih_ipl = ipl; 61652506Simp 61752506Simp pf->sc->ih = pccard_chip_intr_establish(pf->sc->pct, 61852506Simp pf->sc->pch, pf, ipl, PCCARD_CARD_INTR, pf->sc); 61952506Simp splx(s); 62052506Simp } else if (ipl > hiipl) { 62152506Simp#ifdef DIAGNOSTIC 62252506Simp if (pf->sc->ih == NULL) 62352506Simp panic("functions have ih, but the card does not"); 62452506Simp#endif 62552506Simp 62652506Simp /* XXX need #ifdef for splserial on x86 */ 62752506Simp s = splhigh(); 62852506Simp 62952506Simp pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch, 63052506Simp pf->sc->ih); 63152506Simp 63252506Simp /* set up the handler for the new function */ 63352506Simp pf->ih_fct = ih_fct; 63452506Simp pf->ih_arg = ih_arg; 63552506Simp pf->ih_ipl = ipl; 63652506Simp 63752506Simp pf->sc->ih = pccard_chip_intr_establish(pf->sc->pct, 63852506Simp pf->sc->pch, pf, ipl, PCCARD_CARD_INTR, pf->sc); 63952506Simp 64052506Simp splx(s); 64152506Simp } else { 64252506Simp s = splhigh(); 64352506Simp 64452506Simp /* set up the handler for the new function */ 64552506Simp 64652506Simp pf->ih_fct = ih_fct; 64752506Simp pf->ih_arg = ih_arg; 64852506Simp pf->ih_ipl = ipl; 64952506Simp 65052506Simp splx(s); 65152506Simp } 65252506Simp 65352506Simp ret = pf->sc->ih; 65452506Simp 65552506Simp if (ret != NULL) { 65652506Simp reg = pccard_ccr_read(pf, PCCARD_CCR_OPTION); 65752506Simp reg |= PCCARD_CCR_OPTION_IREQ_ENABLE; 65852506Simp pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg); 65952506Simp 66052506Simp reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); 66152506Simp reg |= PCCARD_CCR_STATUS_INTRACK; 66252506Simp pccard_ccr_write(pf, PCCARD_CCR_STATUS, reg); 66352506Simp } 66452506Simp } else { 66552506Simp ret = pccard_chip_intr_establish(pf->sc->pct, pf->sc->pch, 66652506Simp pf, ipl, ih_fct, ih_arg); 66752506Simp } 66852506Simp 66952506Simp return (ret); 67052506Simp} 67152506Simp 67252506Simpvoid 67352506Simppccard_intr_disestablish(pf, ih) 67452506Simp struct pccard_function *pf; 67552506Simp void *ih; 67652506Simp{ 67752506Simp /* behave differently if this is a multifunction card */ 67852506Simp 67952506Simp if (pccard_mfc(pf->sc)) { 68052506Simp int s, ihcnt, hiipl; 68152506Simp struct pccard_function *pf2; 68252506Simp 68352506Simp /* 68452506Simp * mask all the ipl's which are already used by this card, 68552506Simp * and find the highest ipl number (lowest priority). Skip 68652506Simp * the current function. 68752506Simp */ 68852506Simp 68952506Simp ihcnt = 0; 69052506Simp s = 0; /* this is only here to keep the compipler 69152506Simp happy */ 69252506Simp hiipl = 0; /* this is only here to keep the compipler 69352506Simp happy */ 69452506Simp 69552506Simp STAILQ_FOREACH(pf2, &pf->sc->card.pf_head, pf_list) { 69652506Simp if (pf2 == pf) 69752506Simp continue; 69852506Simp 69952506Simp if (pf2->ih_fct) { 70052506Simp if (ihcnt == 0) { 70152506Simp hiipl = pf2->ih_ipl; 70252506Simp } else { 70352506Simp if (pf2->ih_ipl > hiipl) 70452506Simp hiipl = pf2->ih_ipl; 70552506Simp } 70652506Simp ihcnt++; 70752506Simp } 70852506Simp } 70952506Simp 71052506Simp /* 71152506Simp * if the ih being removed is lower priority than the lowest 71252506Simp * priority remaining interrupt, up the priority. 71352506Simp */ 71452506Simp 71552506Simp /* ihcnt is the number of interrupt handlers *not* including 71652506Simp the one about to be removed. */ 71752506Simp 71852506Simp if (ihcnt == 0) { 71952506Simp int reg; 72052506Simp 72152506Simp#ifdef DIAGNOSTIC 72252506Simp if (pf->sc->ih == NULL) 72352506Simp panic("disestablishing last function, but card has no ih"); 72452506Simp#endif 72552506Simp pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch, 72652506Simp pf->sc->ih); 72752506Simp 72852506Simp reg = pccard_ccr_read(pf, PCCARD_CCR_OPTION); 72952506Simp reg &= ~PCCARD_CCR_OPTION_IREQ_ENABLE; 73052506Simp pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg); 73152506Simp 73252506Simp pf->ih_fct = NULL; 73352506Simp pf->ih_arg = NULL; 73452506Simp 73552506Simp pf->sc->ih = NULL; 73652506Simp } else if (pf->ih_ipl > hiipl) { 73752506Simp#ifdef DIAGNOSTIC 73852506Simp if (pf->sc->ih == NULL) 73952506Simp panic("changing ih ipl, but card has no ih"); 74052506Simp#endif 74152506Simp /* XXX need #ifdef for splserial on x86 */ 74252506Simp s = splhigh(); 74352506Simp 74452506Simp pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch, 74552506Simp pf->sc->ih); 74652506Simp pf->sc->ih = pccard_chip_intr_establish(pf->sc->pct, 74752506Simp pf->sc->pch, pf, hiipl, PCCARD_CARD_INTR, pf->sc); 74852506Simp 74952506Simp /* null out the handler for this function */ 75052506Simp 75152506Simp pf->ih_fct = NULL; 75252506Simp pf->ih_arg = NULL; 75352506Simp 75452506Simp splx(s); 75552506Simp } else { 75652506Simp s = splhigh(); 75752506Simp 75852506Simp pf->ih_fct = NULL; 75952506Simp pf->ih_arg = NULL; 76052506Simp 76152506Simp splx(s); 76252506Simp } 76352506Simp } else { 76452506Simp pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch, ih); 76552506Simp } 76652506Simp} 76752506Simp 76852506Simpint 76952506Simppccard_card_intr(arg) 77052506Simp void *arg; 77152506Simp{ 77252506Simp struct pccard_softc *sc = arg; 77352506Simp struct pccard_function *pf; 77452506Simp int reg, ret, ret2; 77552506Simp 77652506Simp ret = 0; 77752506Simp 77852506Simp STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { 77952506Simp if (pf->ih_fct != NULL && 78052506Simp (pf->ccr_mask & (1 << (PCCARD_CCR_STATUS / 2)))) { 78152506Simp reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); 78252506Simp if (reg & PCCARD_CCR_STATUS_INTR) { 78352506Simp ret2 = (*pf->ih_fct)(pf->ih_arg); 78452506Simp if (ret2 != 0 && ret == 0) 78552506Simp ret = ret2; 78652506Simp reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); 78752506Simp pccard_ccr_write(pf, PCCARD_CCR_STATUS, 78852506Simp reg & ~PCCARD_CCR_STATUS_INTR); 78952506Simp } 79052506Simp } 79152506Simp } 79252506Simp 79352506Simp return (ret); 79452506Simp} 79552506Simp 79652506Simp#ifdef PCCARDDEBUG 79752506Simpint 79852506Simppccard_card_intrdebug(arg) 79952506Simp void *arg; 80052506Simp{ 80152506Simp struct pccard_softc *sc = arg; 80252506Simp struct pccard_function *pf; 80352506Simp int reg, ret, ret2; 80452506Simp 80552506Simp ret = 0; 80652506Simp 80752506Simp STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { 80852506Simp printf("%s: intr flags=%x fct=%d cor=%02x csr=%02x pin=%02x", 80952506Simp sc->dev.dv_xname, pf->pf_flags, pf->number, 81052506Simp pccard_ccr_read(pf, PCCARD_CCR_OPTION), 81152506Simp pccard_ccr_read(pf, PCCARD_CCR_STATUS), 81252506Simp pccard_ccr_read(pf, PCCARD_CCR_PIN)); 81352506Simp if (pf->ih_fct != NULL && 81452506Simp (pf->ccr_mask & (1 << (PCCARD_CCR_STATUS / 2)))) { 81552506Simp reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); 81652506Simp if (reg & PCCARD_CCR_STATUS_INTR) { 81752506Simp ret2 = (*pf->ih_fct)(pf->ih_arg); 81852506Simp if (ret2 != 0 && ret == 0) 81952506Simp ret = ret2; 82052506Simp reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); 82152506Simp printf("; csr %02x->%02x", 82252506Simp reg, reg & ~PCCARD_CCR_STATUS_INTR); 82352506Simp pccard_ccr_write(pf, PCCARD_CCR_STATUS, 82452506Simp reg & ~PCCARD_CCR_STATUS_INTR); 82552506Simp } 82652506Simp } 82752506Simp printf("\n"); 82852506Simp } 82952506Simp 83052506Simp return (ret); 83152506Simp} 83252506Simp#endif 83352506Simp 83452506Simpstatic int 83552506Simppccard_add_children(device_t dev, int busno) 83652506Simp{ 83752506Simp device_add_child(dev, NULL, -1, NULL); 83852506Simp return 0; 83952506Simp} 84052506Simp 84152506Simpstatic int 84252506Simppccard_probe(device_t dev) 84352506Simp{ 84452506Simp device_set_desc(dev, "PC Card bus -- newconfig version"); 84552506Simp return pccard_add_children(dev, device_get_unit(dev)); 84652506Simp} 84752506Simp 84852506Simpstatic device_method_t pccard_methods[] = { 84952506Simp /* Device interface */ 85052506Simp DEVMETHOD(device_probe, pccard_probe), 85152506Simp DEVMETHOD(device_attach, bus_generic_attach), 85252506Simp DEVMETHOD(device_shutdown, bus_generic_shutdown), 85352506Simp DEVMETHOD(device_suspend, bus_generic_suspend), 85452506Simp DEVMETHOD(device_resume, bus_generic_resume), 85552506Simp 85652506Simp /* Bus interface */ 85752506Simp#if 0 85852506Simp DEVMETHOD(bus_print_child, pccard_print_child), 85952506Simp#endif 86052506Simp DEVMETHOD(bus_driver_added, bus_generic_driver_added), 86152506Simp#if 0 86252506Simp DEVMETHOD(bus_alloc_resource, pccard_alloc_resource), 86352506Simp DEVMETHOD(bus_release_resource, pccard_release_resource), 86452506Simp#endif 86552506Simp DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 86652506Simp DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 86752506Simp DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 86852506Simp DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 86952506Simp#if 0 87052506Simp DEVMETHOD(bus_set_resource, pccard_set_resource), 87152506Simp DEVMETHOD(bus_get_resource, pccard_get_resource), 87252506Simp DEVMETHOD(bus_delete_resource, pccard_delete_resource), 87352506Simp#endif 87452506Simp 87552506Simp { 0, 0 } 87652506Simp}; 87752506Simp 87852506Simpstatic driver_t pccard_driver = { 87952506Simp "pccard", 88052506Simp pccard_methods, 88152506Simp 1, /* no softc */ 88252506Simp}; 88352506Simp 88452506Simpdevclass_t pccard_devclass; 88552506Simp 88652506SimpDRIVER_MODULE(pccard, pcicx, pccard_driver, pccard_devclass, 0, 0); 88752506SimpDRIVER_MODULE(pccard, pc98pcic, pccard_driver, pccard_devclass, 0, 0); 88852506SimpDRIVER_MODULE(pccard, pccbb, pccard_driver, pccard_devclass, 0, 0); 889