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