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