1/* 2 * Copyright (c) 1998 Matthias Apitz. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the author nor the names of any co-contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 4. Altered versions must be plainly marked as such, and must not be 17 * misrepresented as being the original software and/or documentation. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 *--------------------------------------------------------------------------- 32 * 33 * Fritz!Card pcmcia specific routines for isic driver 34 * --------------------------------------------------- 35 * 36 * $Id: isic_pcmcia_avm_fritz.c,v 1.12 2007/10/19 12:01:05 ad Exp $ 37 * 38 * last edit-date: [Fri Jan 5 11:39:32 2001] 39 * 40 * -ap added support for AVM PCMCIA Fritz!Card 41 * -mh split into separate file 42 * 43 *---------------------------------------------------------------------------*/ 44 45#include <sys/cdefs.h> 46__KERNEL_RCSID(0, "$NetBSD: isic_pcmcia_avm_fritz.c,v 1.11 2007/03/04 06:02:28 christos Exp $"); 47 48#include "opt_isicpcmcia.h" 49#ifdef ISICPCMCIA_AVM_A1 50 51#include <sys/param.h> 52#if defined(__FreeBSD__) && __FreeBSD__ >= 3 53#include <sys/ioccom.h> 54#else 55#include <sys/ioctl.h> 56#endif 57#include <sys/kernel.h> 58#include <sys/systm.h> 59#include <sys/mbuf.h> 60 61#ifdef __FreeBSD__ 62#include <machine/clock.h> 63#include <i386/isa/isa_device.h> 64#else 65#include <sys/bus.h> 66#include <sys/device.h> 67#endif 68 69#include <sys/socket.h> 70#include <net/if.h> 71 72#if defined(__NetBSD__) && __NetBSD_Version__ >= 104230000 73#include <sys/callout.h> 74#endif 75 76#ifdef __FreeBSD__ 77#include <machine/i4b_debug.h> 78#include <machine/i4b_ioctl.h> 79#else 80#include <netisdn/i4b_debug.h> 81#include <netisdn/i4b_ioctl.h> 82#include <netisdn/i4b_l2.h> 83#include <netisdn/i4b_l1l2.h> 84#include <dev/pcmcia/pcmciareg.h> 85#include <dev/pcmcia/pcmciavar.h> 86#endif 87 88#include <dev/ic/isic_l1.h> 89#include <dev/ic/isac.h> 90#include <dev/ic/hscx.h> 91 92#ifndef __FreeBSD__ 93#include <dev/pcmcia/isic_pcmcia.h> 94 95/* PCMCIA support routines */ 96static u_int8_t avma1_pcmcia_read_reg(struct isic_softc *sc, int what, bus_size_t offs); 97static void avma1_pcmcia_write_reg(struct isic_softc *sc, int what, bus_size_t offs, u_int8_t data); 98static void avma1_pcmcia_read_fifo(struct isic_softc *sc, int what, void *buf, size_t size); 99static void avma1_pcmcia_write_fifo(struct isic_softc *sc, int what, const void *data, size_t size); 100#endif 101 102/*---------------------------------------------------------------------------* 103 * AVM PCMCIA Fritz!Card special registers 104 *---------------------------------------------------------------------------*/ 105 106/* 107 * register offsets from i/o base 0x140 or 0x300 108 */ 109#define ADDR_REG_OFFSET 0x02 110#define DATA_REG_OFFSET 0x03 111#define STAT0_OFFSET 0x04 112#define STAT1_OFFSET 0x05 113#define MODREG_OFFSET 0x06 114#define VERREG_OFFSET 0x07 115/* 116 * AVM PCMCIA Status Latch 0 read only bits 117 */ 118#define ASL_IRQ_TIMER 0x10 /* Timer interrupt, active low */ 119#define ASL_IRQ_ISAC 0x20 /* ISAC interrupt, active low */ 120#define ASL_IRQ_HSCX 0x40 /* HSX interrupt, active low */ 121#define ASL_IRQ_BCHAN ASL_IRQ_HSCX 122#define ASL_IRQ_Pending (ASL_IRQ_ISAC | ASL_IRQ_HSCX | ASL_IRQ_TIMER) 123/* 124 * AVM Status Latch 0 write only bits 125 */ 126#define ASL_RESET_ALL 0x01 /* reset siemens IC's, active 1 */ 127#define ASL_TIMERDISABLE 0x02 /* active high */ 128#define ASL_TIMERRESET 0x04 /* active high */ 129#define ASL_ENABLE_INT 0x08 /* active high */ 130/* 131 * AVM Status Latch 1 write only bits 132 */ 133#define ASL1_LED0 0x10 /* active high */ 134#define ASL1_LED1 0x20 /* active high */ 135 136#define ASL1_ENABLE_S0 0xc0 /* enable active S0 I/F */ 137 138/*----- EEpromless controller -----*/ 139/* 140 * AVM Status Latch read/write bit 141 */ 142 143#define ASL_TESTBIT 0x80 144 145 146/*---------------------------------------------------------------------------* 147 * AVM read fifo routines 148 *---------------------------------------------------------------------------*/ 149#ifdef __FreeBSD__ 150static int PCMCIA_IO_BASE = 0; /* ap: XXX hack */ 151static void 152avma1_pcmcia_read_fifo(void *buf, const void *base, size_t len) 153{ 154 outb(PCMCIA_IO_BASE + ADDR_REG_OFFSET, (int)base - 0x20); 155 insb(PCMCIA_IO_BASE + DATA_REG_OFFSET, (u_char *)buf, (u_int)len); 156} 157#else 158/* offsets of the different 'what' arguments */ 159static u_int8_t what_map[] = { 160 0x20-0x20, /* ISIC_WHAT_ISAC */ 161 0xA0-0x20, /* ISIC_WHAT_HSCXA */ 162 0xE0-0x20 /* ISIC_WHAT_HSCXB */ 163}; 164static void 165avma1_pcmcia_read_fifo(struct isic_softc *sc, int what, void *buf, size_t size) 166{ 167 bus_space_tag_t t = sc->sc_maps[0].t; 168 bus_space_handle_t h = sc->sc_maps[0].h; 169 bus_space_write_1(t, h, ADDR_REG_OFFSET, what_map[what]); 170 bus_space_read_multi_1(t, h, DATA_REG_OFFSET, buf, size); 171} 172#endif 173 174/*---------------------------------------------------------------------------* 175 * AVM write fifo routines 176 *---------------------------------------------------------------------------*/ 177#ifdef __FreeBSD__ 178static void 179avma1_pcmcia_write_fifo(void *base, const void *buf, size_t len) 180{ 181 outb(PCMCIA_IO_BASE + ADDR_REG_OFFSET, (int)base - 0x20); 182 outsb(PCMCIA_IO_BASE + DATA_REG_OFFSET, (u_char *)buf, (u_int)len); 183} 184#else 185static void 186avma1_pcmcia_write_fifo(struct isic_softc *sc, int what, const void *buf, size_t size) 187{ 188 bus_space_tag_t t = sc->sc_maps[0].t; 189 bus_space_handle_t h = sc->sc_maps[0].h; 190 bus_space_write_1(t, h, ADDR_REG_OFFSET, what_map[what]); 191 bus_space_write_multi_1(t, h, DATA_REG_OFFSET, buf, size); 192} 193#endif 194 195/*---------------------------------------------------------------------------* 196 * AVM write register routines 197 *---------------------------------------------------------------------------*/ 198#ifdef __FreeBSD__ 199static void 200avma1_pcmcia_write_reg(u_char *base, u_int offset, u_int v) 201{ 202 /* offset includes 0x20 FIFO ! */ 203 outb(PCMCIA_IO_BASE + ADDR_REG_OFFSET, (int)base+offset-0x20); 204 outb(PCMCIA_IO_BASE + DATA_REG_OFFSET, (u_char)v); 205} 206#else 207static void 208avma1_pcmcia_write_reg(struct isic_softc *sc, int what, bus_size_t offs, u_int8_t data) 209{ 210 bus_space_tag_t t = sc->sc_maps[0].t; 211 bus_space_handle_t h = sc->sc_maps[0].h; 212 bus_space_write_1(t, h, ADDR_REG_OFFSET, what_map[what]+offs); 213 bus_space_write_1(t, h, DATA_REG_OFFSET, data); 214} 215#endif 216 217/*---------------------------------------------------------------------------* 218 * AVM read register routines 219 *---------------------------------------------------------------------------*/ 220#ifdef __FreeBSD__ 221static u_char 222avma1_pcmcia_read_reg(u_char *base, u_int offset) 223{ 224 /* offset includes 0x20 FIFO ! */ 225 outb(PCMCIA_IO_BASE + ADDR_REG_OFFSET, (int)base+offset-0x20); 226 return (inb(PCMCIA_IO_BASE + DATA_REG_OFFSET)); 227} 228#else 229static u_int8_t 230avma1_pcmcia_read_reg(struct isic_softc *sc, int what, bus_size_t offs) 231{ 232 bus_space_tag_t t = sc->sc_maps[0].t; 233 bus_space_handle_t h = sc->sc_maps[0].h; 234 bus_space_write_1(t, h, ADDR_REG_OFFSET, what_map[what]+offs); 235 return bus_space_read_1(t, h, DATA_REG_OFFSET); 236} 237#endif 238 239/*---------------------------------------------------------------------------* 240 * isic_probe_avma1_pcmcia - probe for AVM PCMCIA Fritz!Card 241 * This is in the bus attachemnt part on NetBSD (isic_pcmcia.c), no 242 * card specicfic probe is needed on direct config buses like pcmcia. 243 *---------------------------------------------------------------------------*/ 244#ifdef __FreeBSD__ 245int 246isic_probe_avma1_pcmcia(struct isa_device *dev) 247{ 248 struct isic_softc *sc = &l1_sc[dev->id_unit]; 249 u_char byte; 250 int i; 251 u_int cardinfo; 252 253 /* check max unit range */ 254 255 if(dev->id_unit > 1) 256 { 257 printf("isic%d: Error, unit %d > MAXUNIT for AVM PCMCIA Fritz!Card\n", 258 dev->id_unit, dev->id_unit); 259 return(0); 260 } 261 sc->sc_unit = dev->id_unit; 262 263 /* 264 * we trust the IRQ we got from PCCARD service 265 */ 266 sc->sc_irq = dev->id_irq; 267 268 /* check if we got an iobase */ 269 270 switch(dev->id_iobase) 271 { 272 case 0x140: 273 case 0x300: 274 break; 275 default: 276 printf("isic%d: Error, invalid iobase 0x%x specified for AVM PCMCIA Fritz!Card.\n", 277 dev->id_unit, dev->id_iobase); 278 return(0); 279 break; 280 } 281 sc->sc_port = dev->id_iobase; 282 283 /* ResetController */ 284 285 outb(dev->id_iobase + STAT0_OFFSET, 0x00); 286 DELAY(SEC_DELAY / 20); 287 outb(dev->id_iobase + STAT0_OFFSET, 0x01); 288 DELAY(SEC_DELAY / 20); 289 outb(dev->id_iobase + STAT0_OFFSET, 0x00); 290 291 /* 292 * CheckController 293 * The logic to check for the PCMCIA was adapted as 294 * described by AVM. 295 */ 296 297 outb(dev->id_iobase + ADDR_REG_OFFSET, 0x21); /* ISAC STAR */ 298 if ( (byte=inb(dev->id_iobase + DATA_REG_OFFSET) & 0xfd) != 0x48 ) 299 { 300 printf("isic%d: Error, ISAC STAR for AVM PCMCIA is 0x%0x (should be 0x48)\n", 301 dev->id_unit, byte); 302 return(0); 303 } 304 305 outb(dev->id_iobase + ADDR_REG_OFFSET, 0xa1); /* HSCX STAR */ 306 if ( (byte=inb(dev->id_iobase + DATA_REG_OFFSET) & 0xfd) != 0x48 ) 307 { 308 printf("isic%d: Error, HSCX STAR for AVM PCMCIA is 0x%0x (should be 0x48)\n", 309 dev->id_unit, byte); 310 return(0); 311 } 312 313 byte = ASL_TESTBIT; 314 for (i=0; i<256; i++) { 315 byte = byte ? 0 : ASL_TESTBIT; 316 outb(dev->id_iobase + STAT0_OFFSET, byte); 317 if ((inb(dev->id_iobase+STAT0_OFFSET)&ASL_TESTBIT)!=byte) { 318 printf("isic%d: Error during toggle of AVM PCMCIA Status Latch0\n", 319 dev->id_unit); 320 return(0); 321 } 322 } 323 324 sc->clearirq = NULL; 325 sc->readreg = avma1_pcmcia_read_reg; 326 sc->writereg = avma1_pcmcia_write_reg; 327 328 sc->readfifo = avma1_pcmcia_read_fifo; 329 sc->writefifo = avma1_pcmcia_write_fifo; 330 331 /* setup IOM bus type */ 332 333 sc->sc_bustyp = BUS_TYPE_IOM2; /* ap: XXX ??? */ 334 335 sc->sc_ipac = 0; 336 sc->sc_bfifolen = HSCX_FIFO_LEN; 337 338 /* setup ISAC and HSCX base addr */ 339 /* 340 * NOTE: for PCMCIA these are no real addrs; they are 341 * offsets to be written into the base+ADDR_REG_OFFSET register 342 * to pick up the values of the bytes fro base+DATA_REG_OFFSET 343 * 344 * see also the logic in the avma1_pcmcia_* routines; 345 * therefore we also must have the base addr in some static 346 * space or struct; XXX better solution? 347 */ 348 349 PCMCIA_IO_BASE = dev->id_iobase; 350 ISAC_BASE = (void *)0x20; 351 352 HSCX_A_BASE = (void *)0xA0; 353 HSCX_B_BASE = (void *)0xE0; 354 355 /* 356 * Read HSCX A/B VSTR. 357 * Expected value for AVM A1 is 0x04 or 0x05 and for the 358 * AVM Fritz!Card is 0x05 in the least significant bits. 359 */ 360 361 if( (((HSCX_READ(0, H_VSTR) & 0xf) != 0x5) && 362 ((HSCX_READ(0, H_VSTR) & 0xf) != 0x4)) || 363 (((HSCX_READ(1, H_VSTR) & 0xf) != 0x5) && 364 ((HSCX_READ(1, H_VSTR) & 0xf) != 0x4)) ) 365 { 366 printf("isic%d: HSCX VSTR test failed for AVM PCMCIA Fritz!Card\n", 367 dev->id_unit); 368 printf("isic%d: HSC0: VSTR: 0x%0x\n", 369 dev->id_unit, HSCX_READ(0, H_VSTR)); 370 printf("isic%d: HSC1: VSTR: 0x%0x\n", 371 dev->id_unit, HSCX_READ(1, H_VSTR)); 372 return (0); 373 } 374 375 /* 376 * seems we really have an AVM PCMCIA Fritz!Card controller 377 */ 378 cardinfo = inb(dev->id_iobase + VERREG_OFFSET)<<8 | inb(dev->id_iobase + MODREG_OFFSET); 379 printf("isic%d: successfully detect AVM PCMCIA cardinfo = 0x%0x\n", 380 dev->id_unit, cardinfo); 381 dev->id_flags = FLAG_AVM_A1_PCMCIA; 382 return (1); 383} 384#endif /* __FreeBSD__ */ 385 386 387 388/*---------------------------------------------------------------------------* 389 * isic_attach_fritzpcmcia - attach Fritz!Card 390 *---------------------------------------------------------------------------*/ 391#ifdef __FreeBSD__ 392int 393isic_attach_fritzpcmcia(struct isa_device *dev) 394{ 395 /* ResetController again just to make sure... */ 396 397 outb(dev->id_iobase + STAT0_OFFSET, 0x00); 398 DELAY(SEC_DELAY / 10); 399 outb(dev->id_iobase + STAT0_OFFSET, 0x01); 400 DELAY(SEC_DELAY / 10); 401 outb(dev->id_iobase + STAT0_OFFSET, 0x00); 402 DELAY(SEC_DELAY / 10); 403 404 /* enable IRQ, disable counter IRQ */ 405 406 outb(dev->id_iobase + STAT0_OFFSET, ASL_TIMERDISABLE | 407 ASL_TIMERRESET | ASL_ENABLE_INT); 408 /* DELAY(SEC_DELAY / 10); */ 409 410 return(1); 411} 412 413#else 414 415int 416isic_attach_fritzpcmcia(struct pcmcia_isic_softc *psc, struct pcmcia_config_entry *cfe, struct pcmcia_attach_args *pa) 417{ 418 struct isic_softc *sc = &psc->sc_isic; 419 bus_space_tag_t t; 420 bus_space_handle_t h; 421 int i; 422 423 /* Validate config info */ 424 if (cfe->num_memspace != 0) 425 printf(": unexpected number of memory spaces %d should be 0\n", 426 cfe->num_memspace); 427 if (cfe->num_iospace != 1) 428 printf(": unexpected number of memory spaces %d should be 1\n", 429 cfe->num_iospace); 430 431 /* Allocate pcmcia space - but don't listen to the card, it's lying 432 about the size needed! */ 433 for (i = 0; i < cfe->num_iospace; i++) 434 if (pcmcia_io_alloc(pa->pf, cfe->iospace[i].start, cfe->iospace[i].length, 1, &psc->sc_pcioh) == 0) 435 break; 436 if (i >= cfe->num_iospace) { 437 printf(": can't allocate i/o space\n"); 438 return 0; 439 } 440 441 /* map the selected space */ 442 if (pcmcia_io_map(pa->pf, ((cfe->flags & PCMCIA_CFE_IO16) ? 443 PCMCIA_WIDTH_IO16 : PCMCIA_WIDTH_IO8), &psc->sc_pcioh, 444 &psc->sc_io_window)) { 445 printf(": can't map i/o space\n"); 446 return 0; 447 } 448 449 /* Setup bus space maps */ 450 sc->sc_num_mappings = 1; 451 MALLOC_MAPS(sc); 452 453 /* Copy our handles/tags to the MI maps */ 454 sc->sc_maps[0].t = psc->sc_pcioh.iot; 455 sc->sc_maps[0].h = psc->sc_pcioh.ioh; 456 sc->sc_maps[0].offset = 0; 457 sc->sc_maps[0].size = 0; /* not our mapping */ 458 459 t = sc->sc_maps[0].t; 460 h = sc->sc_maps[0].h; 461 462 sc->clearirq = NULL; 463 sc->readreg = avma1_pcmcia_read_reg; 464 sc->writereg = avma1_pcmcia_write_reg; 465 466 sc->readfifo = avma1_pcmcia_read_fifo; 467 sc->writefifo = avma1_pcmcia_write_fifo; 468 469 /* setup IOM bus type */ 470 471 sc->sc_bustyp = BUS_TYPE_IOM2; 472 473 sc->sc_ipac = 0; 474 sc->sc_bfifolen = HSCX_FIFO_LEN; 475 476 /* Reset controller again just to make sure... */ 477 478 bus_space_write_1(t, h, STAT0_OFFSET, 0x00); 479 DELAY(SEC_DELAY / 10); 480 bus_space_write_1(t, h, STAT0_OFFSET, 0x01); 481 DELAY(SEC_DELAY / 10); 482 bus_space_write_1(t, h, STAT0_OFFSET, 0x00); 483 DELAY(SEC_DELAY / 10); 484 485 /* enable IRQ, disable counter IRQ */ 486 487 bus_space_write_1(t, h, STAT0_OFFSET, ASL_TIMERDISABLE | 488 ASL_TIMERRESET | ASL_ENABLE_INT); 489 490 return 1; 491} 492#endif 493 494#endif /* ISICPCMCIA_AVM_A1 */ 495