pccard.c revision 55500
1170530Ssam/* $NetBSD: pcmcia.c,v 1.13 1998/12/24 04:51:59 marc Exp $ */ 2186904Ssam/* $FreeBSD: head/sys/dev/pccard/pccard.c 55500 2000-01-06 07:30:28Z imp $ */ 3170530Ssam 4170530Ssam/* 5170530Ssam * Copyright (c) 1997 Marc Horowitz. All rights reserved. 6170530Ssam * 7170530Ssam * Redistribution and use in source and binary forms, with or without 8170530Ssam * modification, are permitted provided that the following conditions 9170530Ssam * are met: 10170530Ssam * 1. Redistributions of source code must retain the above copyright 11170530Ssam * notice, this list of conditions and the following disclaimer. 12170530Ssam * 2. Redistributions in binary form must reproduce the above copyright 13170530Ssam * notice, this list of conditions and the following disclaimer in the 14170530Ssam * documentation and/or other materials provided with the distribution. 15170530Ssam * 3. All advertising materials mentioning features or use of this software 16170530Ssam * must display the following acknowledgement: 17170530Ssam * This product includes software developed by Marc Horowitz. 18170530Ssam * 4. The name of the author may not be used to endorse or promote products 19170530Ssam * derived from this software without specific prior written permission. 20170530Ssam * 21170530Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22170530Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23170530Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24170530Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25170530Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26170530Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27170530Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28170530Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29170530Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30170530Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31170530Ssam */ 32178354Ssam 33178354Ssam#include <sys/param.h> 34170530Ssam#include <sys/systm.h> 35191746Sthompsa#include <sys/malloc.h> 36170530Ssam#include <sys/module.h> 37170530Ssam#include <sys/kernel.h> 38191746Sthompsa#include <sys/queue.h> 39170530Ssam#include <sys/types.h> 40170530Ssam 41170530Ssam#include <sys/bus.h> 42170530Ssam#include <machine/bus.h> 43170530Ssam#include <sys/rman.h> 44170530Ssam#include <machine/resource.h> 45170530Ssam 46178354Ssam#include <dev/pccard/pccardreg.h> 47178354Ssam#include <dev/pccard/pccardchip.h> 48186904Ssam#include <dev/pccard/pccardvar.h> 49186904Ssam 50186904Ssam#include "power_if.h" 51195618Srpaulo 52195618Srpaulo#define PCCARDDEBUG 53195618Srpaulo 54170530Ssam#ifdef PCCARDDEBUG 55170530Ssamint pccard_debug = 1; 56170530Ssam#define DPRINTF(arg) if (pccard_debug) printf arg 57170530Ssam#define DEVPRINTF(arg) if (pccard_debug) device_printf arg 58170530Ssamint pccardintr_debug = 0; 59170530Ssam/* this is done this way to avoid doing lots of conditionals 60170530Ssam at interrupt level. */ 61170530Ssam#define PCCARD_CARD_INTR (pccardintr_debug?pccard_card_intrdebug:pccard_card_intr) 62170530Ssam#else 63170530Ssam#define DPRINTF(arg) 64170530Ssam#define DEVPRINTF(arg) 65170530Ssam#define PCCARD_CARD_INTR (pccard_card_intr) 66170530Ssam#endif 67170530Ssam 68170530Ssam#ifdef PCCARDVERBOSE 69170530Ssamint pccard_verbose = 1; 70170530Ssam#else 71170530Ssamint pccard_verbose = 0; 72170530Ssam#endif 73170530Ssam 74170530Ssamint pccard_print(void *, const char *); 75170530Ssam 76170530Ssamint pccard_card_intr(void *); 77170530Ssam#ifdef PCCARDDEBUG 78170530Ssamint pccard_card_intrdebug(void *); 79170530Ssam#endif 80170530Ssam 81170530Ssam/* XXX Shouldn't be touching hardware, that's a layering violation */ 82184302Ssam/* XXX imp */ 83170530Ssamint 84170530Ssampccard_ccr_read(pf, ccr) 85170530Ssam struct pccard_function *pf; 86170530Ssam int ccr; 87170530Ssam{ 88178354Ssam return (bus_space_read_1(pf->pf_ccrt, pf->pf_ccrh, 89170530Ssam pf->pf_ccr_offset + ccr)); 90170530Ssam} 91170530Ssam 92170530Ssamvoid 93170530Ssampccard_ccr_write(pf, ccr, val) 94170530Ssam struct pccard_function *pf; 95170530Ssam int ccr; 96186107Ssam int val; 97186107Ssam{ 98186107Ssam 99170530Ssam if ((pf->ccr_mask) & (1 << (ccr / 2))) { 100206617Srpaulo bus_space_write_1(pf->pf_ccrt, pf->pf_ccrh, 101170530Ssam pf->pf_ccr_offset + ccr, val); 102170530Ssam } 103178354Ssam} 104178354Ssam 105178354Ssamint 106170530Ssampccard_card_attach(device_t dev) 107178354Ssam{ 108186107Ssam struct pccard_softc *sc = (struct pccard_softc *) 109170530Ssam device_get_softc(dev); 110170530Ssam struct pccard_function *pf; 111170530Ssam int attached; 112171409Ssam 113171409Ssam DEVPRINTF((dev, "pccard_card_attach\n")); 114171409Ssam /* 115171409Ssam * this is here so that when socket_enable calls gettype, trt happens 116171409Ssam */ 117171409Ssam STAILQ_INIT(&sc->card.pf_head); 118195618Srpaulo 119195618Srpaulo DEVPRINTF((dev, "chip_socket_enable\n")); 120195618Srpaulo POWER_ENABLE_SOCKET(device_get_parent(dev), dev); 121195618Srpaulo 122195618Srpaulo DEVPRINTF((dev, "read_cis\n")); 123195618Srpaulo pccard_read_cis(sc); 124195618Srpaulo 125195618Srpaulo DEVPRINTF((dev, "chip_socket_disable\n")); 126195618Srpaulo POWER_DISABLE_SOCKET(device_get_parent(dev), dev); 127195618Srpaulo 128195618Srpaulo DEVPRINTF((dev, "check_cis_quirks\n")); 129195618Srpaulo pccard_check_cis_quirks(dev); 130195618Srpaulo 131195618Srpaulo /* 132195618Srpaulo * bail now if the card has no functions, or if there was an error in 133195618Srpaulo * the cis. 134195618Srpaulo */ 135178354Ssam 136170530Ssam if (sc->card.error) 137178354Ssam return (1); 138170530Ssam if (STAILQ_EMPTY(&sc->card.pf_head)) 139178354Ssam return (1); 140178354Ssam 141178354Ssam if (pccard_verbose) 142178354Ssam pccard_print_cis(dev); 143178354Ssam 144178354Ssam attached = 0; 145170530Ssam 146170530Ssam DEVPRINTF((dev, "functions scanning\n")); 147195618Srpaulo STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { 148195618Srpaulo if (STAILQ_EMPTY(&pf->cfe_head)) 149195618Srpaulo continue; 150195618Srpaulo 151170530Ssam#ifdef DIAGNOSTIC 152170530Ssam if (pf->child != NULL) { 153170530Ssam device_printf(sc->dev, 154170530Ssam "%s still attached to function %d!\n", 155170530Ssam device_get_name(pf->child), pf->number); 156170530Ssam panic("pccard_card_attach"); 157170530Ssam } 158170530Ssam#endif 159170530Ssam pf->sc = sc; 160186302Ssam pf->child = NULL; 161170530Ssam pf->cfe = NULL; 162170530Ssam pf->ih_fct = NULL; 163170530Ssam pf->ih_arg = NULL; 164206617Srpaulo } 165170530Ssam 166170530Ssam STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { 167170530Ssam if (STAILQ_EMPTY(&pf->cfe_head)) 168170530Ssam continue; 169170530Ssam 170170530Ssam#if XXX 171170530Ssam if (attach_child()) { 172170530Ssam attached++; 173170530Ssam 174170530Ssam DEVPRINTF((sc->dev, "function %d CCR at %d " 175170530Ssam "offset %lx: %x %x %x %x, %x %x %x %x, %x\n", 176170530Ssam pf->number, pf->pf_ccr_window, pf->pf_ccr_offset, 177170530Ssam pccard_ccr_read(pf, 0x00), 178170530Ssam pccard_ccr_read(pf, 0x02), pccard_ccr_read(pf, 0x04), 179170530Ssam pccard_ccr_read(pf, 0x06), pccard_ccr_read(pf, 0x0A), 180170530Ssam pccard_ccr_read(pf, 0x0C), pccard_ccr_read(pf, 0x0E), 181170530Ssam pccard_ccr_read(pf, 0x10), pccard_ccr_read(pf, 0x12))); 182206617Srpaulo } 183170530Ssam#endif 184186302Ssam } 185170530Ssam 186170530Ssam return (attached ? 0 : 1); 187170530Ssam} 188170530Ssam 189170530Ssamvoid 190170530Ssampccard_card_detach(device_t dev, int flags) 191170530Ssam{ 192170530Ssam struct pccard_softc *sc = (struct pccard_softc *) 193170530Ssam device_get_softc(dev); 194170530Ssam struct pccard_function *pf; 195170530Ssam#if XXX 196170530Ssam int error; 197170530Ssam#endif 198170530Ssam 199206617Srpaulo /* 200170530Ssam * We are running on either the PCCARD socket's event thread 201206617Srpaulo * or in user context detaching a device by user request. 202170530Ssam */ 203170530Ssam STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { 204170530Ssam if (STAILQ_FIRST(&pf->cfe_head) == NULL) 205170530Ssam continue; 206170530Ssam if (pf->child == NULL) 207170530Ssam continue; 208170530Ssam DEVPRINTF((sc->dev, "detaching %s (function %d)\n", 209170530Ssam device_get_name(pf->child), pf->number)); 210170530Ssam#if XXX 211170530Ssam if ((error = config_detach(pf->child, flags)) != 0) { 212170530Ssam device_printf(sc->dev, 213170530Ssam "error %d detaching %s (function %d)\n", 214170530Ssam error, device_get_name(pf->child), pf->number); 215170530Ssam } else 216170530Ssam pf->child = NULL; 217178354Ssam#endif 218186302Ssam } 219170530Ssam} 220178354Ssam 221170530Ssamvoid 222170530Ssampccard_card_deactivate(device_t dev) 223170530Ssam{ 224170530Ssam struct pccard_softc *sc = (struct pccard_softc *) 225170530Ssam device_get_softc(dev); 226170530Ssam struct pccard_function *pf; 227170530Ssam 228170530Ssam /* 229170530Ssam * We're in the chip's card removal interrupt handler. 230170530Ssam * Deactivate the child driver. The PCCARD socket's 231192468Ssam * event thread will run later to finish the detach. 232170530Ssam */ 233170530Ssam STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { 234170530Ssam if (STAILQ_FIRST(&pf->cfe_head) == NULL) 235170530Ssam continue; 236170530Ssam if (pf->child == NULL) 237170530Ssam continue; 238170530Ssam DEVPRINTF((sc->dev, "deactivating %s (function %d)\n", 239178354Ssam device_get_name(pf->child), pf->number)); 240178354Ssam#if XXX 241170530Ssam config_deactivate(pf->child); 242170530Ssam#endif 243178354Ssam } 244170530Ssam} 245170530Ssam 246170530Ssamint 247206617Srpaulopccard_card_gettype(device_t dev) 248170530Ssam{ 249170530Ssam struct pccard_softc *sc = (struct pccard_softc *) 250170530Ssam device_get_softc(dev); 251186302Ssam struct pccard_function *pf; 252170530Ssam 253170530Ssam /* 254206617Srpaulo * set the iftype to memory if this card has no functions (not yet 255170530Ssam * probed), or only one function, and that is not initialized yet or 256170530Ssam * that is memory. 257178354Ssam */ 258178354Ssam pf = STAILQ_FIRST(&sc->card.pf_head); 259170530Ssam if (pf == NULL || 260170530Ssam (STAILQ_NEXT(pf, pf_list) == NULL && 261170530Ssam (pf->cfe == NULL || pf->cfe->iftype == PCCARD_IFTYPE_MEMORY))) 262170530Ssam return (PCCARD_IFTYPE_MEMORY); 263170530Ssam else 264170530Ssam return (PCCARD_IFTYPE_IO); 265170530Ssam} 266170530Ssam 267170530Ssam/* 268170530Ssam * Initialize a PCCARD function. May be called as long as the function is 269170530Ssam * disabled. 270170530Ssam */ 271170530Ssamvoid 272170530Ssampccard_function_init(pf, cfe) 273178354Ssam struct pccard_function *pf; 274170530Ssam struct pccard_config_entry *cfe; 275170530Ssam{ 276170530Ssam if (pf->pf_flags & PFF_ENABLED) 277170530Ssam panic("pccard_function_init: function is enabled"); 278170530Ssam 279178354Ssam /* Remember which configuration entry we are using. */ 280173368Ssam pf->cfe = cfe; 281173368Ssam} 282173368Ssam 283173368Ssam/* Enable a PCCARD function */ 284173368Ssamint 285173368Ssampccard_function_enable(pf) 286178354Ssam struct pccard_function *pf; 287178354Ssam{ 288173368Ssam struct pccard_function *tmp; 289173368Ssam int reg; 290170530Ssam 291170530Ssam if (pf->cfe == NULL) 292170530Ssam panic("pccard_function_enable: function not initialized"); 293195618Srpaulo 294195618Srpaulo /* 295195618Srpaulo * Increase the reference count on the socket, enabling power, if 296195618Srpaulo * necessary. 297173368Ssam */ 298173368Ssam if (pf->sc->sc_enabled_count++ == 0) 299173368Ssam POWER_ENABLE_SOCKET(device_get_parent(pf->sc->dev), 300178354Ssam pf->sc->dev); 301173368Ssam DEVPRINTF((pf->sc->dev, "++enabled_count = %d\n", 302178354Ssam pf->sc->sc_enabled_count)); 303173862Ssam 304173368Ssam if (pf->pf_flags & PFF_ENABLED) { 305173862Ssam /* 306178354Ssam * Don't do anything if we're already enabled. 307178354Ssam */ 308178354Ssam return (0); 309173368Ssam } 310178354Ssam 311178354Ssam /* 312173956Ssam * it's possible for different functions' CCRs to be in the same 313173956Ssam * underlying page. Check for that. 314173956Ssam */ 315173862Ssam 316178354Ssam STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) { 317173862Ssam if ((tmp->pf_flags & PFF_ENABLED) && 318173862Ssam (pf->ccr_base >= (tmp->ccr_base - tmp->pf_ccr_offset)) && 319178354Ssam ((pf->ccr_base + PCCARD_CCR_SIZE) <= 320170530Ssam (tmp->ccr_base - tmp->pf_ccr_offset + 321170530Ssam tmp->pf_ccr_realsize))) { 322170530Ssam pf->pf_ccrt = tmp->pf_ccrt; 323170530Ssam pf->pf_ccrh = tmp->pf_ccrh; 324170530Ssam pf->pf_ccr_realsize = tmp->pf_ccr_realsize; 325170530Ssam 326170530Ssam /* 327170530Ssam * pf->pf_ccr_offset = (tmp->pf_ccr_offset - 328170530Ssam * tmp->ccr_base) + pf->ccr_base; 329178354Ssam */ 330178354Ssam pf->pf_ccr_offset = 331178354Ssam (tmp->pf_ccr_offset + pf->ccr_base) - 332178354Ssam tmp->ccr_base; 333178354Ssam pf->pf_ccr_window = tmp->pf_ccr_window; 334178354Ssam break; 335178354Ssam } 336178354Ssam } 337178354Ssam 338178354Ssam if (tmp == NULL) { 339178354Ssam if (pccard_mem_alloc(pf, PCCARD_CCR_SIZE, &pf->pf_pcmh)) 340178354Ssam goto bad; 341178354Ssam 342178354Ssam if (pccard_mem_map(pf, PCCARD_MEM_ATTR, pf->ccr_base, 343178354Ssam PCCARD_CCR_SIZE, &pf->pf_pcmh, &pf->pf_ccr_offset, 344178354Ssam &pf->pf_ccr_window)) { 345178354Ssam pccard_mem_free(pf, &pf->pf_pcmh); 346178354Ssam goto bad; 347178354Ssam } 348178354Ssam } 349178354Ssam 350178354Ssam reg = (pf->cfe->number & PCCARD_CCR_OPTION_CFINDEX); 351178354Ssam reg |= PCCARD_CCR_OPTION_LEVIREQ; 352178354Ssam if (pccard_mfc(pf->sc)) { 353178354Ssam reg |= (PCCARD_CCR_OPTION_FUNC_ENABLE | 354178354Ssam PCCARD_CCR_OPTION_ADDR_DECODE); 355178354Ssam if (pf->ih_fct) 356178354Ssam reg |= PCCARD_CCR_OPTION_IREQ_ENABLE; 357170530Ssam 358170530Ssam } 359170530Ssam pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg); 360170530Ssam 361178354Ssam reg = 0; 362170530Ssam 363170530Ssam if ((pf->cfe->flags & PCCARD_CFE_IO16) == 0) 364170530Ssam reg |= PCCARD_CCR_STATUS_IOIS8; 365170530Ssam if (pf->cfe->flags & PCCARD_CFE_AUDIO) 366170530Ssam reg |= PCCARD_CCR_STATUS_AUDIO; 367170530Ssam pccard_ccr_write(pf, PCCARD_CCR_STATUS, reg); 368170530Ssam 369186107Ssam pccard_ccr_write(pf, PCCARD_CCR_SOCKETCOPY, 0); 370178354Ssam 371178354Ssam if (pccard_mfc(pf->sc)) { 372178354Ssam long tmp, iosize; 373206617Srpaulo 374170530Ssam tmp = pf->pf_mfc_iomax - pf->pf_mfc_iobase; 375170530Ssam /* round up to nearest (2^n)-1 */ 376170530Ssam for (iosize = 1; iosize < tmp; iosize <<= 1) 377170530Ssam ; 378170530Ssam iosize--; 379178354Ssam 380170530Ssam pccard_ccr_write(pf, PCCARD_CCR_IOBASE0, 381170530Ssam pf->pf_mfc_iobase & 0xff); 382170530Ssam pccard_ccr_write(pf, PCCARD_CCR_IOBASE1, 383170530Ssam (pf->pf_mfc_iobase >> 8) & 0xff); 384170530Ssam pccard_ccr_write(pf, PCCARD_CCR_IOBASE2, 0); 385170530Ssam pccard_ccr_write(pf, PCCARD_CCR_IOBASE3, 0); 386170530Ssam 387170530Ssam pccard_ccr_write(pf, PCCARD_CCR_IOSIZE, iosize); 388170530Ssam } 389170530Ssam 390170530Ssam#ifdef PCCARDDEBUG 391178354Ssam if (pccard_debug) { 392170530Ssam STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) { 393178354Ssam device_printf(tmp->sc->dev, 394178354Ssam "function %d CCR at %d offset %x: " 395178354Ssam "%x %x %x %x, %x %x %x %x, %x\n", 396170530Ssam tmp->number, tmp->pf_ccr_window, 397170530Ssam tmp->pf_ccr_offset, 398170530Ssam pccard_ccr_read(tmp, 0x00), 399170530Ssam pccard_ccr_read(tmp, 0x02), 400170530Ssam pccard_ccr_read(tmp, 0x04), 401170530Ssam pccard_ccr_read(tmp, 0x06), 402170530Ssam pccard_ccr_read(tmp, 0x0A), 403170530Ssam pccard_ccr_read(tmp, 0x0C), 404170530Ssam pccard_ccr_read(tmp, 0x0E), 405170530Ssam pccard_ccr_read(tmp, 0x10), 406170530Ssam pccard_ccr_read(tmp, 0x12)); 407170530Ssam } 408170530Ssam } 409170530Ssam#endif 410170530Ssam 411187991Ssam pf->pf_flags |= PFF_ENABLED; 412170530Ssam return (0); 413170530Ssam 414170530Ssam bad: 415170530Ssam /* 416187991Ssam * Decrement the reference count, and power down the socket, if 417170530Ssam * necessary. 418170530Ssam */ 419170530Ssam if (--pf->sc->sc_enabled_count == 0) 420170530Ssam POWER_DISABLE_SOCKET(device_get_parent(pf->sc->dev), 421188777Ssam pf->sc->dev); 422170530Ssam DEVPRINTF((pf->sc->dev, "--enabled_count = %d\n", 423188777Ssam pf->sc->sc_enabled_count)); 424188777Ssam 425188777Ssam return (1); 426188777Ssam} 427188777Ssam 428188777Ssam/* Disable PCCARD function. */ 429188777Ssamvoid 430188777Ssampccard_function_disable(pf) 431188777Ssam struct pccard_function *pf; 432188782Ssam{ 433188782Ssam struct pccard_function *tmp; 434188777Ssam 435188777Ssam if (pf->cfe == NULL) 436188777Ssam panic("pccard_function_enable: function not initialized"); 437170530Ssam 438170530Ssam if ((pf->pf_flags & PFF_ENABLED) == 0) { 439170530Ssam /* 440178354Ssam * Don't do anything if we're already disabled. 441170530Ssam */ 442170530Ssam return; 443170530Ssam } 444170530Ssam 445178354Ssam /* 446170530Ssam * it's possible for different functions' CCRs to be in the same 447170530Ssam * underlying page. Check for that. Note we mark us as disabled 448170530Ssam * first to avoid matching ourself. 449170530Ssam */ 450170530Ssam 451170530Ssam pf->pf_flags &= ~PFF_ENABLED; 452170530Ssam STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) { 453170530Ssam if ((tmp->pf_flags & PFF_ENABLED) && 454170530Ssam (pf->ccr_base >= (tmp->ccr_base - tmp->pf_ccr_offset)) && 455170530Ssam ((pf->ccr_base + PCCARD_CCR_SIZE) <= 456170530Ssam (tmp->ccr_base - tmp->pf_ccr_offset + tmp->pf_ccr_realsize))) 457178354Ssam break; 458170530Ssam } 459170530Ssam 460170530Ssam /* Not used by anyone else; unmap the CCR. */ 461170530Ssam if (tmp == NULL) { 462178354Ssam pccard_mem_unmap(pf, pf->pf_ccr_window); 463170530Ssam pccard_mem_free(pf, &pf->pf_pcmh); 464178354Ssam } 465178354Ssam 466178354Ssam /* 467170530Ssam * Decrement the reference count, and power down the socket, if 468170530Ssam * necessary. 469170530Ssam */ 470170530Ssam if (--pf->sc->sc_enabled_count == 0) 471170530Ssam POWER_DISABLE_SOCKET(device_get_parent(pf->sc->dev), 472170530Ssam pf->sc->dev); 473170530Ssam DEVPRINTF((pf->sc->dev, "--enabled_count = %d\n", 474170530Ssam pf->sc->sc_enabled_count)); 475170530Ssam} 476170530Ssam 477170530Ssamint 478170530Ssampccard_io_map(pf, width, offset, size, pcihp, windowp) 479170530Ssam struct pccard_function *pf; 480170530Ssam int width; 481170530Ssam bus_addr_t offset; 482170530Ssam bus_size_t size; 483170530Ssam struct pccard_io_handle *pcihp; 484170530Ssam int *windowp; 485170530Ssam{ 486170530Ssam int reg; 487170530Ssam 488170530Ssam if (pccard_chip_io_map(pf->sc->pct, pf->sc->pch, 489170530Ssam width, offset, size, pcihp, windowp)) 490170530Ssam return (1); 491170530Ssam 492189377Ssam /* 493189377Ssam * XXX in the multifunction multi-iospace-per-function case, this 494189377Ssam * needs to cooperate with io_alloc to make sure that the spaces 495189377Ssam * don't overlap, and that the ccr's are set correctly 496189377Ssam */ 497189377Ssam 498189377Ssam if (pccard_mfc(pf->sc)) { 499189377Ssam long tmp, iosize; 500189377Ssam 501189377Ssam if (pf->pf_mfc_iomax == 0) { 502189377Ssam pf->pf_mfc_iobase = pcihp->addr + offset; 503189377Ssam pf->pf_mfc_iomax = pf->pf_mfc_iobase + size; 504176653Ssam } else { 505178354Ssam /* this makes the assumption that nothing overlaps */ 506176653Ssam if (pf->pf_mfc_iobase > pcihp->addr + offset) 507176653Ssam pf->pf_mfc_iobase = pcihp->addr + offset; 508178354Ssam if (pf->pf_mfc_iomax < pcihp->addr + offset + size) 509176653Ssam pf->pf_mfc_iomax = pcihp->addr + offset + size; 510176653Ssam } 511176653Ssam 512176653Ssam tmp = pf->pf_mfc_iomax - pf->pf_mfc_iobase; 513176653Ssam /* round up to nearest (2^n)-1 */ 514176653Ssam for (iosize = 1; iosize >= tmp; iosize <<= 1) 515176653Ssam ; 516176653Ssam iosize--; 517176653Ssam 518176653Ssam pccard_ccr_write(pf, PCCARD_CCR_IOBASE0, 519176653Ssam pf->pf_mfc_iobase & 0xff); 520176653Ssam pccard_ccr_write(pf, PCCARD_CCR_IOBASE1, 521176653Ssam (pf->pf_mfc_iobase >> 8) & 0xff); 522176653Ssam pccard_ccr_write(pf, PCCARD_CCR_IOBASE2, 0); 523176653Ssam pccard_ccr_write(pf, PCCARD_CCR_IOBASE3, 0); 524176653Ssam 525176653Ssam pccard_ccr_write(pf, PCCARD_CCR_IOSIZE, iosize); 526176653Ssam 527176653Ssam reg = pccard_ccr_read(pf, PCCARD_CCR_OPTION); 528176653Ssam reg |= PCCARD_CCR_OPTION_ADDR_DECODE; 529176653Ssam pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg); 530178354Ssam } 531178354Ssam return (0); 532176653Ssam} 533176653Ssam 534176653Ssamvoid 535176653Ssampccard_io_unmap(pf, window) 536176653Ssam struct pccard_function *pf; 537178354Ssam int window; 538176653Ssam{ 539176653Ssam 540176653Ssam pccard_chip_io_unmap(pf->sc->pct, pf->sc->pch, window); 541176653Ssam 542176653Ssam /* XXX Anything for multi-function cards? */ 543176653Ssam} 544176653Ssam 545176653Ssamvoid * 546176653Ssampccard_intr_establish(pf, ipl, ih_fct, ih_arg) 547176653Ssam struct pccard_function *pf; 548176653Ssam int ipl; 549176653Ssam int (*ih_fct)(void *); 550176653Ssam void *ih_arg; 551176653Ssam{ 552189377Ssam void *ret; 553189377Ssam 554189377Ssam /* behave differently if this is a multifunction card */ 555189377Ssam 556189377Ssam if (pccard_mfc(pf->sc)) { 557189377Ssam int s, ihcnt, hiipl, reg; 558189377Ssam struct pccard_function *pf2; 559189377Ssam 560189377Ssam /* 561189377Ssam * mask all the ipl's which are already used by this card, 562189377Ssam * and find the highest ipl number (lowest priority) 563189377Ssam */ 564189377Ssam 565189377Ssam ihcnt = 0; 566189377Ssam s = 0; /* this is only here to keep the compiler 567176653Ssam happy */ 568176653Ssam hiipl = 0; /* this is only here to keep the compiler 569178354Ssam happy */ 570178354Ssam 571178354Ssam STAILQ_FOREACH(pf2, &pf->sc->card.pf_head, pf_list) { 572170530Ssam if (pf2->ih_fct) { 573170530Ssam DEVPRINTF((pf2->sc->dev, 574170530Ssam "function %d has ih_fct %p\n", 575170530Ssam pf2->number, pf2->ih_fct)); 576170530Ssam 577170530Ssam if (ihcnt == 0) { 578170530Ssam hiipl = pf2->ih_ipl; 579170530Ssam } else { 580170530Ssam if (pf2->ih_ipl > hiipl) 581170530Ssam hiipl = pf2->ih_ipl; 582178354Ssam } 583170530Ssam 584178354Ssam ihcnt++; 585170530Ssam } 586170530Ssam } 587170530Ssam 588170530Ssam /* 589178354Ssam * establish the real interrupt, changing the ipl if 590170530Ssam * necessary 591170530Ssam */ 592170530Ssam 593170530Ssam if (ihcnt == 0) { 594170530Ssam#ifdef DIAGNOSTIC 595178354Ssam if (pf->sc->ih != NULL) 596170530Ssam panic("card has intr handler, but no function does"); 597170530Ssam#endif 598170530Ssam s = splhigh(); 599170530Ssam 600170530Ssam /* set up the handler for the new function */ 601170530Ssam 602170530Ssam pf->ih_fct = ih_fct; 603170530Ssam pf->ih_arg = ih_arg; 604170530Ssam pf->ih_ipl = ipl; 605170530Ssam 606170530Ssam pf->sc->ih = pccard_chip_intr_establish(pf->sc->pct, 607170530Ssam pf->sc->pch, pf, ipl, PCCARD_CARD_INTR, pf->sc); 608170530Ssam splx(s); 609170530Ssam } else if (ipl > hiipl) { 610178354Ssam#ifdef DIAGNOSTIC 611170530Ssam if (pf->sc->ih == NULL) 612170530Ssam panic("functions have ih, but the card does not"); 613170530Ssam#endif 614170530Ssam 615170530Ssam /* XXX need #ifdef for splserial on x86 */ 616170530Ssam s = splhigh(); 617170530Ssam 618170530Ssam pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch, 619170530Ssam pf->sc->ih); 620178354Ssam 621170530Ssam /* set up the handler for the new function */ 622170530Ssam pf->ih_fct = ih_fct; 623170530Ssam pf->ih_arg = ih_arg; 624178354Ssam pf->ih_ipl = ipl; 625178354Ssam 626170530Ssam pf->sc->ih = pccard_chip_intr_establish(pf->sc->pct, 627178354Ssam pf->sc->pch, pf, ipl, PCCARD_CARD_INTR, pf->sc); 628178354Ssam 629170530Ssam splx(s); 630178354Ssam } else { 631178354Ssam s = splhigh(); 632178354Ssam 633178354Ssam /* set up the handler for the new function */ 634178354Ssam 635178354Ssam pf->ih_fct = ih_fct; 636178354Ssam pf->ih_arg = ih_arg; 637178354Ssam pf->ih_ipl = ipl; 638178354Ssam 639178354Ssam splx(s); 640178354Ssam } 641178354Ssam 642178354Ssam ret = pf->sc->ih; 643178354Ssam 644178354Ssam if (ret != NULL) { 645178354Ssam reg = pccard_ccr_read(pf, PCCARD_CCR_OPTION); 646178354Ssam reg |= PCCARD_CCR_OPTION_IREQ_ENABLE; 647178354Ssam pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg); 648178354Ssam 649178354Ssam reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); 650178354Ssam reg |= PCCARD_CCR_STATUS_INTRACK; 651178354Ssam pccard_ccr_write(pf, PCCARD_CCR_STATUS, reg); 652178354Ssam } 653178354Ssam } else { 654178354Ssam ret = pccard_chip_intr_establish(pf->sc->pct, pf->sc->pch, 655178354Ssam pf, ipl, ih_fct, ih_arg); 656178354Ssam } 657178354Ssam 658178354Ssam return (ret); 659178354Ssam} 660170530Ssam 661178354Ssamvoid 662170530Ssampccard_intr_disestablish(pf, ih) 663178354Ssam struct pccard_function *pf; 664178354Ssam void *ih; 665178354Ssam{ 666178354Ssam /* behave differently if this is a multifunction card */ 667178354Ssam 668178354Ssam if (pccard_mfc(pf->sc)) { 669178354Ssam int s, ihcnt, hiipl; 670178354Ssam struct pccard_function *pf2; 671178354Ssam 672178354Ssam /* 673178354Ssam * mask all the ipl's which are already used by this card, 674178354Ssam * and find the highest ipl number (lowest priority). Skip 675178354Ssam * the current function. 676178354Ssam */ 677178354Ssam 678178354Ssam ihcnt = 0; 679178354Ssam s = 0; /* this is only here to keep the compipler 680178354Ssam happy */ 681178354Ssam hiipl = 0; /* this is only here to keep the compipler 682178354Ssam happy */ 683178354Ssam 684178354Ssam STAILQ_FOREACH(pf2, &pf->sc->card.pf_head, pf_list) { 685178354Ssam if (pf2 == pf) 686178354Ssam continue; 687178354Ssam 688178354Ssam if (pf2->ih_fct) { 689178354Ssam if (ihcnt == 0) { 690178354Ssam hiipl = pf2->ih_ipl; 691178354Ssam } else { 692178354Ssam if (pf2->ih_ipl > hiipl) 693178354Ssam hiipl = pf2->ih_ipl; 694178354Ssam } 695178354Ssam ihcnt++; 696178354Ssam } 697178354Ssam } 698178354Ssam 699178354Ssam /* 700178354Ssam * if the ih being removed is lower priority than the lowest 701170530Ssam * priority remaining interrupt, up the priority. 702170530Ssam */ 703170530Ssam 704170530Ssam /* ihcnt is the number of interrupt handlers *not* including 705170530Ssam the one about to be removed. */ 706170530Ssam 707178354Ssam if (ihcnt == 0) { 708178354Ssam int reg; 709170530Ssam 710170530Ssam#ifdef DIAGNOSTIC 711178354Ssam if (pf->sc->ih == NULL) 712170530Ssam panic("disestablishing last function, but card has no ih"); 713170530Ssam#endif 714170530Ssam pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch, 715170530Ssam pf->sc->ih); 716170530Ssam 717170530Ssam reg = pccard_ccr_read(pf, PCCARD_CCR_OPTION); 718170530Ssam reg &= ~PCCARD_CCR_OPTION_IREQ_ENABLE; 719170530Ssam pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg); 720170530Ssam 721170530Ssam pf->ih_fct = NULL; 722170530Ssam pf->ih_arg = NULL; 723178354Ssam 724170530Ssam pf->sc->ih = NULL; 725170530Ssam } else if (pf->ih_ipl > hiipl) { 726170530Ssam#ifdef DIAGNOSTIC 727170530Ssam if (pf->sc->ih == NULL) 728178354Ssam panic("changing ih ipl, but card has no ih"); 729178354Ssam#endif 730178354Ssam /* XXX need #ifdef for splserial on x86 */ 731178354Ssam s = splhigh(); 732178354Ssam 733184302Ssam pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch, 734184302Ssam pf->sc->ih); 735184302Ssam pf->sc->ih = pccard_chip_intr_establish(pf->sc->pct, 736184302Ssam pf->sc->pch, pf, hiipl, PCCARD_CARD_INTR, pf->sc); 737184302Ssam 738184302Ssam /* null out the handler for this function */ 739184302Ssam 740184302Ssam pf->ih_fct = NULL; 741184302Ssam pf->ih_arg = NULL; 742178354Ssam 743184302Ssam splx(s); 744184302Ssam } else { 745184302Ssam s = splhigh(); 746184302Ssam 747184302Ssam pf->ih_fct = NULL; 748184302Ssam pf->ih_arg = NULL; 749184302Ssam 750184302Ssam splx(s); 751184302Ssam } 752184302Ssam } else { 753184302Ssam pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch, ih); 754178354Ssam } 755170530Ssam} 756170530Ssam 757178354Ssamint 758178354Ssampccard_card_intr(arg) 759178354Ssam void *arg; 760178354Ssam{ 761170530Ssam struct pccard_softc *sc = arg; 762170530Ssam struct pccard_function *pf; 763178354Ssam int reg, ret, ret2; 764178354Ssam 765178354Ssam ret = 0; 766178354Ssam 767178354Ssam STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { 768178354Ssam if (pf->ih_fct != NULL && 769178354Ssam (pf->ccr_mask & (1 << (PCCARD_CCR_STATUS / 2)))) { 770178354Ssam reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); 771178354Ssam if (reg & PCCARD_CCR_STATUS_INTR) { 772178354Ssam ret2 = (*pf->ih_fct)(pf->ih_arg); 773178354Ssam if (ret2 != 0 && ret == 0) 774178354Ssam ret = ret2; 775178354Ssam reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); 776178354Ssam pccard_ccr_write(pf, PCCARD_CCR_STATUS, 777178354Ssam reg & ~PCCARD_CCR_STATUS_INTR); 778178354Ssam } 779178354Ssam } 780178354Ssam } 781170530Ssam 782170530Ssam return (ret); 783170530Ssam} 784170530Ssam 785170530Ssam#ifdef PCCARDDEBUG 786170530Ssamint 787170530Ssampccard_card_intrdebug(arg) 788170530Ssam void *arg; 789170530Ssam{ 790170530Ssam struct pccard_softc *sc = arg; 791170530Ssam struct pccard_function *pf; 792170530Ssam int reg, ret, ret2; 793170530Ssam 794170530Ssam ret = 0; 795170530Ssam 796170530Ssam STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { 797170530Ssam device_printf(sc->dev, 798170530Ssam "intr flags=%x fct=%d cor=%02x csr=%02x pin=%02x", 799170530Ssam pf->pf_flags, pf->number, 800170530Ssam pccard_ccr_read(pf, PCCARD_CCR_OPTION), 801170530Ssam pccard_ccr_read(pf, PCCARD_CCR_STATUS), 802170530Ssam pccard_ccr_read(pf, PCCARD_CCR_PIN)); 803170530Ssam if (pf->ih_fct != NULL && 804170530Ssam (pf->ccr_mask & (1 << (PCCARD_CCR_STATUS / 2)))) { 805170530Ssam reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); 806170530Ssam if (reg & PCCARD_CCR_STATUS_INTR) { 807170530Ssam ret2 = (*pf->ih_fct)(pf->ih_arg); 808178354Ssam if (ret2 != 0 && ret == 0) 809170530Ssam ret = ret2; 810170530Ssam reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); 811170530Ssam printf("; csr %02x->%02x", 812170530Ssam reg, reg & ~PCCARD_CCR_STATUS_INTR); 813170530Ssam pccard_ccr_write(pf, PCCARD_CCR_STATUS, 814170530Ssam reg & ~PCCARD_CCR_STATUS_INTR); 815170530Ssam } 816170530Ssam } 817170530Ssam printf("\n"); 818170530Ssam } 819170530Ssam 820170530Ssam return (ret); 821170530Ssam} 822170530Ssam#endif 823170530Ssam 824170530Ssam#define PCCARD_NPORT 2 825170530Ssam#define PCCARD_NMEM 5 826170530Ssam#define PCCARD_NIRQ 1 827170530Ssam#define PCCARD_NDRQ 0 828170530Ssam 829170530Ssamstatic int 830170530Ssampccard_add_children(device_t dev, int busno) 831170530Ssam{ 832170530Ssam return 0; 833170530Ssam} 834170530Ssam 835170530Ssamstatic int 836170530Ssampccard_probe(device_t dev) 837170530Ssam{ 838170530Ssam device_set_desc(dev, "PC Card bus -- newconfig version"); 839178354Ssam return pccard_add_children(dev, device_get_unit(dev)); 840178354Ssam} 841170530Ssam 842170530Ssamstatic void 843170530Ssampccard_print_resources(struct resource_list *rl, const char *name, int type, 844170530Ssam int count, const char *format) 845170530Ssam{ 846170530Ssam struct resource_list_entry *rle; 847170530Ssam int printed; 848170530Ssam int i; 849178354Ssam 850170530Ssam printed = 0; 851170530Ssam for (i = 0; i < count; i++) { 852184302Ssam rle = resource_list_find(rl, type, i); 853184302Ssam if (rle) { 854170530Ssam if (printed == 0) 855170530Ssam printf(" %s ", name); 856170530Ssam else if (printed > 0) 857178354Ssam printf(","); 858170530Ssam printed++; 859170530Ssam printf(format, rle->start); 860178354Ssam if (rle->count > 1) { 861170530Ssam printf("-"); 862184302Ssam printf(format, rle->start + rle->count - 1); 863170530Ssam } 864170530Ssam } else if (i > 3) { 865178354Ssam /* check the first few regardless */ 866184302Ssam break; 867170530Ssam } 868170530Ssam } 869170530Ssam} 870170530Ssam 871170530Ssamstatic int 872170530Ssampccard_print_child(device_t dev, device_t child) 873170530Ssam{ 874170530Ssam struct pccard_ivar *devi = (struct pccard_ivar *) device_get_ivars(child); 875178354Ssam struct resource_list *rl = &devi->resources; 876170530Ssam int retval = 0; 877170530Ssam 878170530Ssam retval += bus_print_child_header(dev, child); 879170530Ssam retval += printf(" at"); 880170530Ssam 881170530Ssam if (devi) { 882170530Ssam pccard_print_resources(rl, "port", SYS_RES_IOPORT, 883170530Ssam PCCARD_NPORT, "%#lx"); 884170530Ssam pccard_print_resources(rl, "iomem", SYS_RES_MEMORY, 885170530Ssam PCCARD_NMEM, "%#lx"); 886170530Ssam pccard_print_resources(rl, "irq", SYS_RES_IRQ, PCCARD_NIRQ, 887170530Ssam "%ld"); 888170530Ssam pccard_print_resources(rl, "drq", SYS_RES_DRQ, PCCARD_NDRQ, 889170530Ssam "%ld"); 890170530Ssam retval += printf(" slot %d", devi->slotnum); 891170530Ssam } 892170530Ssam 893170530Ssam retval += bus_print_child_footer(dev, child); 894170530Ssam 895170530Ssam return (retval); 896170530Ssam} 897170530Ssam 898170530Ssamstatic int 899170530Ssampccard_set_resource(device_t dev, device_t child, int type, int rid, 900170530Ssam u_long start, u_long count) 901170530Ssam{ 902170530Ssam struct pccard_ivar *devi = (struct pccard_ivar *) device_get_ivars(child); 903170530Ssam struct resource_list *rl = &devi->resources; 904178354Ssam 905170530Ssam if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY 906170530Ssam && type != SYS_RES_IRQ && type != SYS_RES_DRQ) 907170530Ssam return EINVAL; 908170530Ssam if (rid < 0) 909170530Ssam return EINVAL; 910170530Ssam if (type == SYS_RES_IOPORT && rid >= PCCARD_NPORT) 911195618Srpaulo return EINVAL; 912195618Srpaulo if (type == SYS_RES_MEMORY && rid >= PCCARD_NMEM) 913195618Srpaulo return EINVAL; 914195618Srpaulo if (type == SYS_RES_IRQ && rid >= PCCARD_NIRQ) 915195618Srpaulo return EINVAL; 916195618Srpaulo if (type == SYS_RES_DRQ && rid >= PCCARD_NDRQ) 917170530Ssam return EINVAL; 918170530Ssam 919170530Ssam resource_list_add(rl, type, rid, start, start + count - 1, count); 920170530Ssam 921170530Ssam return 0; 922170530Ssam} 923170530Ssam 924195618Srpaulostatic int 925170530Ssampccard_get_resource(device_t dev, device_t child, int type, int rid, 926170530Ssam u_long *startp, u_long *countp) 927170530Ssam{ 928170530Ssam struct pccard_ivar *devi = (struct pccard_ivar *) device_get_ivars(child); 929170530Ssam struct resource_list *rl = &devi->resources; 930186904Ssam struct resource_list_entry *rle; 931186904Ssam 932186904Ssam rle = resource_list_find(rl, type, rid); 933186904Ssam if (!rle) 934186904Ssam return ENOENT; 935186904Ssam 936186904Ssam if (startp) 937186904Ssam *startp = rle->start; 938186904Ssam if (countp) 939186904Ssam *countp = rle->count; 940186904Ssam 941186904Ssam return 0; 942186904Ssam} 943186904Ssam 944170530Ssamstatic void 945170530Ssampccard_delete_resource(device_t dev, device_t child, int type, int rid) 946170530Ssam{ 947170530Ssam struct pccard_ivar *devi = (struct pccard_ivar *) device_get_ivars(child); 948178354Ssam struct resource_list *rl = &devi->resources; 949170530Ssam resource_list_delete(rl, type, rid); 950170530Ssam} 951170530Ssam 952178354Ssamstatic device_method_t pccard_methods[] = { 953170530Ssam /* Device interface */ 954178354Ssam DEVMETHOD(device_probe, pccard_probe), 955178354Ssam DEVMETHOD(device_attach, bus_generic_attach), 956170530Ssam DEVMETHOD(device_shutdown, bus_generic_shutdown), 957170530Ssam DEVMETHOD(device_suspend, bus_generic_suspend), 958170530Ssam DEVMETHOD(device_resume, bus_generic_resume), 959171409Ssam 960170530Ssam /* Bus interface */ 961170530Ssam DEVMETHOD(bus_print_child, pccard_print_child), 962170530Ssam DEVMETHOD(bus_driver_added, bus_generic_driver_added), 963170530Ssam DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), 964170530Ssam DEVMETHOD(bus_release_resource, bus_generic_release_resource), 965170530Ssam DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 966170530Ssam DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 967178354Ssam DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 968170530Ssam DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 969178354Ssam DEVMETHOD(bus_set_resource, pccard_set_resource), 970171409Ssam DEVMETHOD(bus_get_resource, pccard_get_resource), 971178354Ssam DEVMETHOD(bus_delete_resource, pccard_delete_resource), 972170530Ssam 973171409Ssam { 0, 0 } 974186904Ssam}; 975186904Ssam 976186904Ssamstatic driver_t pccard_driver = { 977186904Ssam "pccard", 978186904Ssam pccard_methods, 979186904Ssam 1, /* no softc */ 980186904Ssam}; 981186904Ssam 982186904Ssamdevclass_t pccard_devclass; 983186904Ssam 984186904SsamDRIVER_MODULE(pccard, pcic, pccard_driver, pccard_devclass, 0, 0); 985186904SsamDRIVER_MODULE(pccard, pc98pcic, pccard_driver, pccard_devclass, 0, 0); 986186904SsamDRIVER_MODULE(pccard, pccbb, pccard_driver, pccard_devclass, 0, 0); 987186904SsamDRIVER_MODULE(pccard, tcic, pccard_driver, pccard_devclass, 0, 0); 988186904Ssam