1/*	$OpenBSD: uha_eisa.c,v 1.16 2022/04/06 18:59:28 naddy Exp $	*/
2/*	$NetBSD: uha_eisa.c,v 1.5 1996/10/21 22:31:07 thorpej Exp $	*/
3
4/*
5 * Copyright (c) 1994, 1996 Charles M. Hannum.  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 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *	This product includes software developed by Charles M. Hannum.
18 * 4. The name of the author may not be used to endorse or promote products
19 *    derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include <sys/param.h>
34#include <sys/systm.h>
35#include <sys/device.h>
36#include <sys/kernel.h>
37#include <uvm/uvm_extern.h>
38
39#include <machine/bus.h>
40#include <machine/intr.h>
41
42#include <scsi/scsi_all.h>
43#include <scsi/scsiconf.h>
44
45#include <dev/eisa/eisavar.h>
46#include <dev/eisa/eisadevs.h>
47
48#include <dev/ic/uhareg.h>
49#include <dev/ic/uhavar.h>
50
51#define	UHA_EISA_SLOT_OFFSET	0xc80
52#define	UHA_EISA_IOSIZE		0x020
53
54int	uha_eisa_match(struct device *, void *, void *);
55void	uha_eisa_attach(struct device *, struct device *, void *);
56
57const struct cfattach uha_eisa_ca = {
58	sizeof(struct uha_softc), uha_eisa_match, uha_eisa_attach
59};
60
61#define KVTOPHYS(x)	vtophys((vaddr_t)(x))
62
63int u24_find(bus_space_tag_t, bus_space_handle_t, struct uha_softc *);
64void u24_start_mbox(struct uha_softc *, struct uha_mscp *);
65int u24_poll(struct uha_softc *, struct scsi_xfer *, int);
66int u24_intr(void *);
67void u24_init(struct uha_softc *);
68
69/*
70 * Check the slots looking for a board we recognise
71 * If we find one, note its address (slot) and call
72 * the actual probe routine to check it out.
73 */
74int
75uha_eisa_match(struct device *parent, void *match, void *aux)
76{
77	struct eisa_attach_args *ea = aux;
78	bus_space_tag_t iot = ea->ea_iot;
79	bus_space_handle_t ioh;
80	int rv;
81
82	/* must match one of our known ID strings */
83	if (strncmp(ea->ea_idstring, "USC024", 6))
84		return (0);
85
86	if (bus_space_map(iot, EISA_SLOT_ADDR(ea->ea_slot) +
87	    UHA_EISA_SLOT_OFFSET, UHA_EISA_IOSIZE, 0, &ioh))
88		return (0);
89
90	rv = u24_find(iot, ioh, NULL);
91
92	bus_space_unmap(iot, ioh, UHA_EISA_IOSIZE);
93
94	return (rv);
95}
96
97/*
98 * Attach all the sub-devices we can find
99 */
100void
101uha_eisa_attach(struct device *parent, struct device *self, void *aux)
102{
103	struct eisa_attach_args *ea = aux;
104	struct uha_softc *sc = (void *)self;
105	bus_space_tag_t iot = ea->ea_iot;
106	bus_space_handle_t ioh;
107	eisa_chipset_tag_t ec = ea->ea_ec;
108	eisa_intr_handle_t ih;
109	const char *model, *intrstr;
110
111	if (!strncmp(ea->ea_idstring, "USC024", 6))
112		model = EISA_PRODUCT_USC0240;
113	else
114		model = "unknown model!";
115	printf(": %s\n", model);
116
117	if (bus_space_map(iot, EISA_SLOT_ADDR(ea->ea_slot) +
118	    UHA_EISA_SLOT_OFFSET, UHA_EISA_IOSIZE, 0, &ioh))
119		panic("uha_attach: can't map I/O addresses");
120
121	sc->sc_iot = iot;
122	sc->sc_ioh = ioh;
123	if (!u24_find(iot, ioh, sc))
124		panic("uha_attach: u24_find failed!");
125
126	if (eisa_intr_map(ec, sc->sc_irq, &ih)) {
127		printf("%s: couldn't map interrupt (%d)\n",
128		    sc->sc_dev.dv_xname, sc->sc_irq);
129		return;
130	}
131	intrstr = eisa_intr_string(ec, ih);
132	sc->sc_ih = eisa_intr_establish(ec, ih, IST_LEVEL, IPL_BIO,
133	    u24_intr, sc, sc->sc_dev.dv_xname);
134	if (sc->sc_ih == NULL) {
135		printf("%s: couldn't establish interrupt",
136		    sc->sc_dev.dv_xname);
137		if (intrstr != NULL)
138			printf(" at %s", intrstr);
139		printf("\n");
140		return;
141	}
142	printf("%s: interrupting at %s\n", sc->sc_dev.dv_xname, intrstr);
143
144	/* Save function pointers for later use. */
145	sc->start_mbox = u24_start_mbox;
146	sc->poll = u24_poll;
147	sc->init = u24_init;
148
149	uha_attach(sc);
150}
151
152int
153u24_find(bus_space_tag_t iot, bus_space_handle_t ioh, struct uha_softc *sc)
154{
155	u_int8_t config0, config1, config2;
156	int irq, drq;
157	int resetcount = 4000;	/* 4 secs? */
158
159	config0 = bus_space_read_1(iot, ioh, U24_CONFIG + 0);
160	config1 = bus_space_read_1(iot, ioh, U24_CONFIG + 1);
161	config2 = bus_space_read_1(iot, ioh, U24_CONFIG + 2);
162	if ((config0 & U24_MAGIC1) == 0 ||
163	    (config1 & U24_MAGIC2) == 0)
164		return (0);
165
166	drq = -1;
167
168	switch (config0 & U24_IRQ_MASK) {
169	case U24_IRQ10:
170		irq = 10;
171		break;
172	case U24_IRQ11:
173		irq = 11;
174		break;
175	case U24_IRQ14:
176		irq = 14;
177		break;
178	case U24_IRQ15:
179		irq = 15;
180		break;
181	default:
182		printf("u24_find: illegal irq setting %x\n",
183		    config0 & U24_IRQ_MASK);
184		return (0);
185	}
186
187	bus_space_write_1(iot, ioh, U24_LINT, UHA_ASRST);
188
189	while (--resetcount) {
190		if (bus_space_read_1(iot, ioh, U24_LINT))
191			break;
192		delay(1000);	/* 1 mSec per loop */
193	}
194	if (!resetcount) {
195		printf("u24_find: board timed out during reset\n");
196		return (0);
197	}
198
199	/* if we want to fill in softc, do so now */
200	if (sc != NULL) {
201		sc->sc_irq = irq;
202		sc->sc_drq = drq;
203		sc->sc_scsi_dev = config2 & U24_HOSTID_MASK;
204	}
205
206	return (1);
207}
208
209void
210u24_start_mbox(struct uha_softc *sc, struct uha_mscp *mscp)
211{
212	bus_space_tag_t iot = sc->sc_iot;
213	bus_space_handle_t ioh = sc->sc_ioh;
214	int spincount = 100000;	/* 1s should be enough */
215
216	while (--spincount) {
217		if ((bus_space_read_1(iot, ioh, U24_LINT) & U24_LDIP) == 0)
218			break;
219		delay(100);
220	}
221	if (!spincount)
222		panic("%s: uha_start_mbox, board not responding",
223		    sc->sc_dev.dv_xname);
224
225	bus_space_write_4(iot, ioh, U24_OGMPTR, KVTOPHYS(mscp));
226	if (mscp->flags & MSCP_ABORT)
227		bus_space_write_1(iot, ioh, U24_OGMCMD, 0x80);
228	else
229		bus_space_write_1(iot, ioh, U24_OGMCMD, 0x01);
230	bus_space_write_1(iot, ioh, U24_LINT, U24_OGMFULL);
231
232	if ((mscp->xs->flags & SCSI_POLL) == 0)
233		timeout_add_msec(&mscp->xs->stimeout, mscp->timeout);
234}
235
236int
237u24_poll(struct uha_softc *sc, struct scsi_xfer *xs, int count)
238{
239	bus_space_tag_t iot = sc->sc_iot;
240	bus_space_handle_t ioh = sc->sc_ioh;
241	int s;
242
243	while (count) {
244		/*
245		 * If we had interrupts enabled, would we
246		 * have got an interrupt?
247		 */
248		if (bus_space_read_1(iot, ioh, U24_SINT) & U24_SDIP) {
249			s = splbio();
250			u24_intr(sc);
251			splx(s);
252		}
253		if (xs->flags & ITSDONE)
254			return (0);
255		delay(1000);
256		count--;
257	}
258	return (1);
259}
260
261int
262u24_intr(void *arg)
263{
264	struct uha_softc *sc = arg;
265	bus_space_tag_t iot = sc->sc_iot;
266	bus_space_handle_t ioh = sc->sc_ioh;
267	struct uha_mscp *mscp;
268	u_char uhastat;
269	u_long mboxval;
270
271#ifdef	UHADEBUG
272	printf("%s: uhaintr ", sc->sc_dev.dv_xname);
273#endif /*UHADEBUG */
274
275	if ((bus_space_read_1(iot, ioh, U24_SINT) & U24_SDIP) == 0)
276		return (0);
277
278	for (;;) {
279		/*
280		 * First get all the information and then
281		 * acknowledge the interrupt
282		 */
283		uhastat = bus_space_read_1(iot, ioh, U24_SINT);
284		mboxval = bus_space_read_4(iot, ioh, U24_ICMPTR);
285		bus_space_write_1(iot, ioh, U24_SINT, U24_ICM_ACK);
286		bus_space_write_1(iot, ioh, U24_ICMCMD, 0);
287
288#ifdef	UHADEBUG
289		printf("status = 0x%x ", uhastat);
290#endif /*UHADEBUG*/
291
292		/*
293		 * Process the completed operation
294		 */
295		mscp = uha_mscp_phys_kv(sc, mboxval);
296		if (!mscp) {
297			printf("%s: BAD MSCP RETURNED!\n",
298			    sc->sc_dev.dv_xname);
299			continue;	/* whatever it was, it'll timeout */
300		}
301		timeout_del(&mscp->xs->stimeout);
302		uha_done(sc, mscp);
303
304		if ((bus_space_read_1(iot, ioh, U24_SINT) & U24_SDIP) == 0)
305			return (1);
306	}
307}
308
309void
310u24_init(struct uha_softc *sc)
311{
312	bus_space_tag_t iot = sc->sc_iot;
313	bus_space_handle_t ioh = sc->sc_ioh;
314
315	/* free OGM and ICM */
316	bus_space_write_1(iot, ioh, U24_OGMCMD, 0);
317	bus_space_write_1(iot, ioh, U24_ICMCMD, 0);
318	/* make sure interrupts are enabled */
319#ifdef UHADEBUG
320	printf("u24_init: lmask=%02x, smask=%02x\n",
321	    bus_space_read_1(iot, ioh, U24_LMASK),
322	    bus_space_read_1(iot, ioh, U24_SMASK));
323#endif
324	bus_space_write_1(iot, ioh, U24_LMASK, 0xd2);	/* XXX */
325	bus_space_write_1(iot, ioh, U24_SMASK, 0x92);	/* XXX */
326}
327