1/*- 2 * Copyright (c) 1999 M. Warner Losh <imp@village.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 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 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25/* 26 * Modifications for Megahertz X-Jack Ethernet Card (XJ-10BT) 27 * 28 * Copyright (c) 1996 by Tatsumi Hosokawa <hosokawa@jp.FreeBSD.org> 29 * BSD-nomads, Tokyo, Japan. 30 */ 31 32#include <sys/cdefs.h> 33__FBSDID("$FreeBSD$"); 34 35#include <sys/param.h> 36#include <sys/bus.h> 37#include <sys/kernel.h> 38#include <sys/module.h> 39#include <sys/socket.h> 40#include <sys/systm.h> 41 42#include <net/ethernet.h> 43#include <net/if.h> 44#include <net/if_arp.h> 45 46#include <machine/bus.h> 47#include <machine/resource.h> 48#include <sys/rman.h> 49 50#include <dev/pccard/pccardvar.h> 51#include <dev/pccard/pccard_cis.h> 52#include <dev/sn/if_snreg.h> 53#include <dev/sn/if_snvar.h> 54 55#include "card_if.h" 56#include "pccarddevs.h" 57 58typedef int sn_get_enaddr_t(device_t dev, u_char *eaddr); 59typedef int sn_activate_t(device_t dev); 60 61struct sn_sw 62{ 63 int type; 64#define SN_NORMAL 1 65#define SN_MEGAHERTZ 2 66#define SN_OSITECH 3 67#define SN_OSI_SOD 4 68#define SN_MOTO_MARINER 5 69 char *typestr; 70 sn_get_enaddr_t *get_mac; 71 sn_activate_t *activate; 72}; 73 74static sn_get_enaddr_t sn_pccard_normal_get_mac; 75static sn_activate_t sn_pccard_normal_activate; 76const static struct sn_sw sn_normal_sw = { 77 SN_NORMAL, "plain", 78 sn_pccard_normal_get_mac, 79 sn_pccard_normal_activate 80}; 81 82static sn_get_enaddr_t sn_pccard_megahertz_get_mac; 83static sn_activate_t sn_pccard_megahertz_activate; 84const static struct sn_sw sn_mhz_sw = { 85 SN_MEGAHERTZ, "Megahertz", 86 sn_pccard_megahertz_get_mac, 87 sn_pccard_megahertz_activate 88}; 89 90static const struct sn_product { 91 struct pccard_product prod; 92 const struct sn_sw *sw; 93} sn_pccard_products[] = { 94 { PCMCIA_CARD(DSPSI, XJEM1144), &sn_mhz_sw }, 95 { PCMCIA_CARD(DSPSI, XJACK), &sn_normal_sw }, 96/* { PCMCIA_CARD(MOTOROLA, MARINER), SN_MOTO_MARINER }, */ 97 { PCMCIA_CARD(NEWMEDIA, BASICS), &sn_normal_sw }, 98 { PCMCIA_CARD(MEGAHERTZ, VARIOUS), &sn_mhz_sw}, 99 { PCMCIA_CARD(MEGAHERTZ, XJEM3336), &sn_mhz_sw}, 100/* { PCMCIA_CARD(OSITECH, TRUMP_SOD), SN_OSI_SOD }, */ 101/* { PCMCIA_CARD(OSITECH, TRUMP_JOH), SN_OSITECH }, */ 102/* { PCMCIA_CARD(PSION, GOLDCARD), SN_OSITECH }, */ 103/* { PCMCIA_CARD(PSION, NETGLOBAL), SNI_OSI_SOD }, */ 104/* { PCMCIA_CARD(PSION, NETGLOBAL2), SN_OSITECH }, */ 105 { PCMCIA_CARD(SMC, 8020BT), &sn_normal_sw }, 106 { PCMCIA_CARD(SMC, SMC91C96), &sn_normal_sw }, 107 { { NULL } } 108 109}; 110 111static const struct sn_product * 112sn_pccard_lookup(device_t dev) 113{ 114 115 return ((const struct sn_product *) 116 pccard_product_lookup(dev, 117 (const struct pccard_product *)sn_pccard_products, 118 sizeof(sn_pccard_products[0]), NULL)); 119} 120 121static int 122sn_pccard_probe(device_t dev) 123{ 124 const struct sn_product *pp; 125 126 if ((pp = sn_pccard_lookup(dev)) != NULL) { 127 if (pp->prod.pp_name != NULL) 128 device_set_desc(dev, pp->prod.pp_name); 129 return 0; 130 } 131 return EIO; 132} 133 134static int 135sn_pccard_ascii_enaddr(const char *str, u_char *enet) 136{ 137 uint8_t digit; 138 int i; 139 140 memset(enet, 0, ETHER_ADDR_LEN); 141 for (i = 0, digit = 0; i < (ETHER_ADDR_LEN * 2); i++) { 142 if (str[i] >= '0' && str[i] <= '9') 143 digit |= str[i] - '0'; 144 else if (str[i] >= 'a' && str[i] <= 'f') 145 digit |= (str[i] - 'a') + 10; 146 else if (str[i] >= 'A' && str[i] <= 'F') 147 digit |= (str[i] - 'A') + 10; 148 else 149 return (0); /* Bogus digit!! */ 150 151 /* Compensate for ordering of digits. */ 152 if (i & 1) { 153 enet[i >> 1] = digit; 154 digit = 0; 155 } else 156 digit <<= 4; 157 } 158 159 return (1); 160} 161 162static int 163sn_pccard_normal_get_mac(device_t dev, u_char *eaddr) 164{ 165 int i, sum; 166 const char *cisstr; 167 168 pccard_get_ether(dev, eaddr); 169 for (i = 0, sum = 0; i < ETHER_ADDR_LEN; i++) 170 sum |= eaddr[i]; 171 if (sum == 0) { 172 pccard_get_cis3_str(dev, &cisstr); 173 if (cisstr && strlen(cisstr) == ETHER_ADDR_LEN * 2) 174 sum = sn_pccard_ascii_enaddr(cisstr, eaddr); 175 } 176 if (sum == 0) { 177 pccard_get_cis4_str(dev, &cisstr); 178 if (cisstr && strlen(cisstr) == ETHER_ADDR_LEN * 2) 179 sum = sn_pccard_ascii_enaddr(cisstr, eaddr); 180 } 181 return sum; 182} 183 184static int 185sn_pccard_normal_activate(device_t dev) 186{ 187 int err; 188 189 err = sn_activate(dev); 190 if (err) 191 sn_deactivate(dev); 192 return (err); 193} 194 195static int 196sn_pccard_megahertz_mac(const struct pccard_tuple *tuple, void *argp) 197{ 198 uint8_t *enaddr = argp; 199 int i; 200 uint8_t buffer[ETHER_ADDR_LEN * 2]; 201 202 /* Code 0x81 is Megahertz' special cis node contianing the MAC */ 203 if (tuple->code != 0x81) 204 return (0); 205 206 /* Make sure this is a sane node, as ASCII digits */ 207 if (tuple->length != ETHER_ADDR_LEN * 2 + 1) 208 return (0); 209 210 /* Copy the MAC ADDR and return success if decoded */ 211 for (i = 0; i < ETHER_ADDR_LEN * 2; i++) 212 buffer[i] = pccard_tuple_read_1(tuple, i); 213 return (sn_pccard_ascii_enaddr(buffer, enaddr)); 214} 215 216static int 217sn_pccard_megahertz_get_mac(device_t dev, u_char *eaddr) 218{ 219 220 if (sn_pccard_normal_get_mac(dev, eaddr)) 221 return 1; 222 /* 223 * If that fails, try the special CIS tuple 0x81 that the 224 * '3288 and '3336 cards have. That tuple specifies an ASCII 225 * string, ala CIS3 or CIS4 in the 'normal' cards. 226 */ 227 return (pccard_cis_scan(dev, sn_pccard_megahertz_mac, eaddr)); 228} 229 230static int 231sn_pccard_megahertz_activate(device_t dev) 232{ 233 int err; 234 struct sn_softc *sc = device_get_softc(dev); 235 u_long start; 236 237 err = sn_activate(dev); 238 if (err) { 239 sn_deactivate(dev); 240 return (err); 241 } 242 /* 243 * CIS resource is the modem one, so save it away. 244 */ 245 sc->modem_rid = sc->port_rid; 246 sc->modem_res = sc->port_res; 247 248 /* 249 * The MHz XJEM/CCEM series of cards just need to have any 250 * old resource allocated for the ethernet side of things, 251 * provided bit 0x80 isn't set in the address. That bit is 252 * evidentially reserved for modem function and is how the 253 * card steers the addresses internally. 254 */ 255 sc->port_res = NULL; 256 start = 0; 257 do 258 { 259 sc->port_rid = 1; 260 sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, 261 &sc->port_rid, start, ~0, SMC_IO_EXTENT, RF_ACTIVE); 262 if (sc->port_res == NULL) 263 break; 264 if (!(rman_get_start(sc->port_res) & 0x80)) 265 break; 266 start = rman_get_start(sc->port_res) + SMC_IO_EXTENT; 267 bus_release_resource(dev, SYS_RES_IOPORT, sc->port_rid, 268 sc->port_res); 269 } while (start < 0xff80); 270 if (sc->port_res == NULL) { 271 sn_deactivate(dev); 272 return ENOMEM; 273 } 274 return 0; 275} 276 277static int 278sn_pccard_attach(device_t dev) 279{ 280 struct sn_softc *sc = device_get_softc(dev); 281 u_char eaddr[ETHER_ADDR_LEN]; 282 int i, err; 283 uint16_t w; 284 u_char sum; 285 const struct sn_product *pp; 286 287 pp = sn_pccard_lookup(dev); 288 sum = pp->sw->get_mac(dev, eaddr); 289 290 /* Allocate resources so we can program the ether addr */ 291 sc->dev = dev; 292 err = pp->sw->activate(dev); 293 if (err != 0) 294 return (err); 295 296 if (sum) { 297 printf("Programming sn card's addr\n"); 298 SMC_SELECT_BANK(sc, 1); 299 for (i = 0; i < 3; i++) { 300 w = (uint16_t)eaddr[i * 2] | 301 (((uint16_t)eaddr[i * 2 + 1]) << 8); 302 CSR_WRITE_2(sc, IAR_ADDR0_REG_W + i * 2, w); 303 } 304 } 305 err = sn_attach(dev); 306 if (err) 307 sn_deactivate(dev); 308 return (err); 309} 310 311static device_method_t sn_pccard_methods[] = { 312 /* Device interface */ 313 DEVMETHOD(device_probe, sn_pccard_probe), 314 DEVMETHOD(device_attach, sn_pccard_attach), 315 DEVMETHOD(device_detach, sn_detach), 316 317 { 0, 0 } 318}; 319 320static driver_t sn_pccard_driver = { 321 "sn", 322 sn_pccard_methods, 323 sizeof(struct sn_softc), 324}; 325 326extern devclass_t sn_devclass; 327 328DRIVER_MODULE(sn, pccard, sn_pccard_driver, sn_devclass, 0, 0); 329MODULE_DEPEND(sn, ether, 1, 1, 1); 330