1139749Simp/*-
2133912Sgibbs * FreeBSD, VLB/ISA product support functions
3133912Sgibbs *
4133912Sgibbs * Copyright (c) 2004 Justin T. Gibbs.
5133912Sgibbs * All rights reserved.
6133912Sgibbs *
7133912Sgibbs * Redistribution and use in source and binary forms, with or without
8133912Sgibbs * modification, are permitted provided that the following conditions
9133912Sgibbs * are met:
10133912Sgibbs * 1. Redistributions of source code must retain the above copyright
11133912Sgibbs *    notice, this list of conditions, and the following disclaimer,
12133912Sgibbs *    without modification.
13133912Sgibbs * 2. Redistributions in binary form must reproduce at minimum a disclaimer
14133912Sgibbs *    substantially similar to the "NO WARRANTY" disclaimer below
15133912Sgibbs *    ("Disclaimer") and any redistribution must be conditioned upon
16133912Sgibbs *    including a substantially similar Disclaimer requirement for further
17133912Sgibbs *    binary redistribution.
18133912Sgibbs * 3. Neither the names of the above-listed copyright holders nor the names
19133912Sgibbs *    of any contributors may be used to endorse or promote products derived
20133912Sgibbs *    from this software without specific prior written permission.
21133912Sgibbs *
22133912Sgibbs * NO WARRANTY
23133912Sgibbs * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24133912Sgibbs * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25133912Sgibbs * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
26133912Sgibbs * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27133912Sgibbs * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28133912Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29133912Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30133912Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31133912Sgibbs * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
32133912Sgibbs * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33133912Sgibbs * POSSIBILITY OF SUCH DAMAGES.
34133912Sgibbs *
35133912Sgibbs * $Id$
36133912Sgibbs */
37133912Sgibbs
38133912Sgibbs#include <sys/cdefs.h>
39133912Sgibbs__FBSDID("$FreeBSD$");
40133912Sgibbs
41133912Sgibbs#include <dev/aic7xxx/aic7xxx_osm.h>
42133912Sgibbs
43133912Sgibbs#include <sys/limits.h>		/* For CHAR_BIT*/
44133912Sgibbs#include <isa/isavar.h>		/* For ISA attach glue */
45133912Sgibbs
46133912Sgibbs
47133912Sgibbsstatic struct aic7770_identity *ahc_isa_find_device(bus_space_tag_t tag,
48133912Sgibbs						    bus_space_handle_t bsh);
49133912Sgibbsstatic void			ahc_isa_identify(driver_t *driver,
50133912Sgibbs						 device_t parent);
51133912Sgibbsstatic int			ahc_isa_probe(device_t dev);
52133912Sgibbsstatic int			ahc_isa_attach(device_t dev);
53133912Sgibbs
54133912Sgibbs/*
55133912Sgibbs * Perform an EISA probe of the address with the addition
56133912Sgibbs * of a "priming" step.  The 284X requires priming (a write
57133912Sgibbs * to offset 0x80, the first EISA ID register) to ensure it
58133912Sgibbs * is not mistaken as an EISA card.  Once we have the ID,
59133912Sgibbs * lookup the controller in the aic7770 table of supported
60133912Sgibbs * devices.
61133912Sgibbs */
62133912Sgibbsstatic struct aic7770_identity *
63133912Sgibbsahc_isa_find_device(bus_space_tag_t tag, bus_space_handle_t bsh) {
64133964Sgibbs	uint32_t  id;
65133912Sgibbs	u_int	  id_size;
66133912Sgibbs	int	  i;
67133912Sgibbs
68133912Sgibbs	id = 0;
69133912Sgibbs	id_size = sizeof(id);
70133912Sgibbs	for (i = 0; i < id_size; i++) {
71133912Sgibbs		bus_space_write_1(tag, bsh, 0x80, 0x80 + i);
72133912Sgibbs		id |= bus_space_read_1(tag, bsh, 0x80 + i)
73133912Sgibbs		   << ((id_size - i - 1) * CHAR_BIT);
74133912Sgibbs	}
75133912Sgibbs
76133912Sgibbs	return (aic7770_find_device(id));
77133912Sgibbs}
78133912Sgibbs
79133912Sgibbsstatic void
80133912Sgibbsahc_isa_identify(driver_t *driver, device_t parent)
81133912Sgibbs{
82133912Sgibbs	int slot;
83133912Sgibbs	int max_slot;
84133912Sgibbs
85133912Sgibbs	max_slot = 14;
86133912Sgibbs	for (slot = 0; slot <= max_slot; slot++) {
87133912Sgibbs		struct aic7770_identity *entry;
88133912Sgibbs		bus_space_tag_t	    tag;
89133912Sgibbs		bus_space_handle_t  bsh;
90133912Sgibbs		struct resource	   *regs;
91133912Sgibbs		uint32_t	    iobase;
92133912Sgibbs		int		    rid;
93133912Sgibbs
94133912Sgibbs		rid = 0;
95133964Sgibbs		iobase = (slot * AHC_EISA_SLOT_SIZE) + AHC_EISA_SLOT_OFFSET;
96133912Sgibbs		regs = bus_alloc_resource(parent, SYS_RES_IOPORT, &rid,
97133912Sgibbs					  iobase, iobase, AHC_EISA_IOSIZE,
98133912Sgibbs					  RF_ACTIVE);
99133912Sgibbs		if (regs == NULL) {
100133912Sgibbs			if (bootverbose)
101249574Sneel				printf("ahc_isa_identify %d: ioport 0x%x "
102133912Sgibbs				       "alloc failed\n", slot, iobase);
103133912Sgibbs			continue;
104133912Sgibbs		}
105133912Sgibbs
106133912Sgibbs		tag = rman_get_bustag(regs);
107133912Sgibbs		bsh = rman_get_bushandle(regs);
108133912Sgibbs
109133912Sgibbs		entry = ahc_isa_find_device(tag, bsh);
110133912Sgibbs		if (entry != NULL) {
111133912Sgibbs			device_t child;
112133912Sgibbs
113133912Sgibbs			child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE,
114133912Sgibbs					      "ahc", -1);
115133912Sgibbs			if (child != NULL) {
116133912Sgibbs				device_set_driver(child, driver);
117133912Sgibbs				bus_set_resource(child, SYS_RES_IOPORT,
118133912Sgibbs						 0, iobase, AHC_EISA_IOSIZE);
119133912Sgibbs			}
120133912Sgibbs		}
121133912Sgibbs		bus_release_resource(parent, SYS_RES_IOPORT, rid, regs);
122133912Sgibbs	}
123133912Sgibbs}
124133912Sgibbs
125133912Sgibbsstatic int
126133912Sgibbsahc_isa_probe(device_t dev)
127133912Sgibbs{
128133912Sgibbs	struct	  aic7770_identity *entry;
129133912Sgibbs	bus_space_tag_t	    tag;
130133912Sgibbs	bus_space_handle_t  bsh;
131133912Sgibbs	struct	  resource *regs;
132133912Sgibbs	struct	  resource *irq;
133133912Sgibbs	uint32_t  iobase;
134133912Sgibbs	u_int	  intdef;
135133912Sgibbs	u_int	  hcntrl;
136133912Sgibbs	int	  irq_num;
137133912Sgibbs	int	  error;
138133912Sgibbs	int	  zero;
139133912Sgibbs
140136569Sgibbs	error = ENXIO;
141133912Sgibbs	zero = 0;
142133912Sgibbs	regs = NULL;
143133912Sgibbs	irq = NULL;
144133912Sgibbs
145136569Sgibbs	/* Skip probes for ISA PnP devices */
146136569Sgibbs	if (isa_get_logicalid(dev) != 0)
147136569Sgibbs		return (error);
148136569Sgibbs
149133912Sgibbs	regs = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &zero, RF_ACTIVE);
150133912Sgibbs	if (regs == NULL) {
151134531Sgibbs		device_printf(dev, "No resources allocated.\n");
152133912Sgibbs		return (ENOMEM);
153133912Sgibbs	}
154133912Sgibbs
155133982Sgibbs	iobase = rman_get_start(regs);
156133912Sgibbs	tag = rman_get_bustag(regs);
157133912Sgibbs	bsh = rman_get_bushandle(regs);
158133912Sgibbs
159133912Sgibbs	entry = ahc_isa_find_device(tag, bsh);
160133912Sgibbs	if (entry == NULL)
161133912Sgibbs		goto cleanup;
162133912Sgibbs
163133912Sgibbs	/* Pause the card preseving the IRQ type */
164133912Sgibbs	hcntrl = bus_space_read_1(tag, bsh, HCNTRL) & IRQMS;
165133912Sgibbs	bus_space_write_1(tag, bsh, HCNTRL, hcntrl | PAUSE);
166133912Sgibbs	while ((bus_space_read_1(tag, bsh, HCNTRL) & PAUSE) == 0)
167133912Sgibbs		;
168133912Sgibbs
169133912Sgibbs	/* Make sure we have a valid interrupt vector */
170133912Sgibbs	intdef = bus_space_read_1(tag, bsh, INTDEF);
171133912Sgibbs	irq_num = intdef & VECTOR;
172133912Sgibbs	switch (irq_num) {
173133912Sgibbs	case 9:
174133912Sgibbs	case 10:
175133912Sgibbs	case 11:
176133912Sgibbs	case 12:
177133912Sgibbs	case 14:
178133912Sgibbs	case 15:
179133912Sgibbs		break;
180133912Sgibbs	default:
181133912Sgibbs		device_printf(dev, "@0x%x: illegal irq setting %d\n",
182133912Sgibbs			      iobase, irq_num);
183133912Sgibbs		goto cleanup;
184133912Sgibbs	}
185133912Sgibbs
186133912Sgibbs	if (bus_set_resource(dev, SYS_RES_IRQ, zero, irq_num, 1) != 0)
187133912Sgibbs		goto cleanup;
188133912Sgibbs
189133912Sgibbs	/*
190133912Sgibbs	 * The 284X only supports edge triggered interrupts,
191133912Sgibbs	 * so do not claim RF_SHAREABLE.
192133912Sgibbs	 */
193133912Sgibbs	irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &zero,
194133912Sgibbs				     0 /*!(RF_ACTIVE|RF_SHAREABLE)*/);
195133912Sgibbs	if (irq != NULL) {
196133912Sgibbs		error = 0;
197133912Sgibbs		device_set_desc(dev, entry->name);
198133912Sgibbs	} else
199133912Sgibbs		device_printf(dev, "@0x%x: irq %d allocation failed\n",
200133912Sgibbs			      iobase, irq_num);
201133912Sgibbs
202133912Sgibbscleanup:
203133912Sgibbs	if (regs != NULL) {
204133912Sgibbs		bus_release_resource(dev, SYS_RES_IOPORT, zero, regs);
205133912Sgibbs		regs = NULL;
206133912Sgibbs	}
207133912Sgibbs
208133912Sgibbs	if (irq != NULL) {
209133912Sgibbs		bus_release_resource(dev, SYS_RES_IRQ, zero, irq);
210133912Sgibbs		irq = NULL;
211133912Sgibbs	}
212133912Sgibbs
213133912Sgibbs	return (error);
214133912Sgibbs}
215133912Sgibbs
216133912Sgibbsstatic int
217133912Sgibbsahc_isa_attach(device_t dev)
218133912Sgibbs{
219133912Sgibbs	struct	 aic7770_identity *entry;
220133912Sgibbs	bus_space_tag_t	    tag;
221133912Sgibbs	bus_space_handle_t  bsh;
222133912Sgibbs	struct	  resource *regs;
223133912Sgibbs	struct	  ahc_softc *ahc;
224133912Sgibbs	char	 *name;
225133912Sgibbs	int	  zero;
226133912Sgibbs	int	  error;
227133912Sgibbs
228133912Sgibbs	zero = 0;
229133912Sgibbs	regs = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &zero, RF_ACTIVE);
230133912Sgibbs	if (regs == NULL)
231133912Sgibbs		return (ENOMEM);
232133912Sgibbs
233133912Sgibbs	tag = rman_get_bustag(regs);
234133912Sgibbs	bsh = rman_get_bushandle(regs);
235133912Sgibbs	entry = ahc_isa_find_device(tag, bsh);
236133912Sgibbs	bus_release_resource(dev, SYS_RES_IOPORT, zero, regs);
237133912Sgibbs	if (entry == NULL)
238133912Sgibbs		return (ENODEV);
239133912Sgibbs
240133912Sgibbs	/*
241133912Sgibbs	 * Allocate a softc for this card and
242133912Sgibbs	 * set it up for attachment by our
243133912Sgibbs	 * common detect routine.
244133912Sgibbs	 */
245133912Sgibbs	name = malloc(strlen(device_get_nameunit(dev)) + 1, M_DEVBUF, M_NOWAIT);
246133912Sgibbs	if (name == NULL)
247133912Sgibbs		return (ENOMEM);
248133912Sgibbs	strcpy(name, device_get_nameunit(dev));
249133912Sgibbs	ahc = ahc_alloc(dev, name);
250133912Sgibbs	if (ahc == NULL)
251133912Sgibbs		return (ENOMEM);
252133912Sgibbs
253133912Sgibbs	ahc_set_unit(ahc, device_get_unit(dev));
254133912Sgibbs
255133912Sgibbs	/* Allocate a dmatag for our SCB DMA maps */
256161928Sjmg	error = aic_dma_tag_create(ahc, /*parent*/bus_get_dma_tag(dev),
257161928Sjmg				   /*alignment*/1, /*boundary*/0,
258133912Sgibbs				   /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
259133912Sgibbs				   /*highaddr*/BUS_SPACE_MAXADDR,
260133912Sgibbs				   /*filter*/NULL, /*filterarg*/NULL,
261133912Sgibbs				   /*maxsize*/BUS_SPACE_MAXSIZE_32BIT,
262133912Sgibbs				   /*nsegments*/AHC_NSEG,
263133912Sgibbs				   /*maxsegsz*/AHC_MAXTRANSFER_SIZE,
264133912Sgibbs				   /*flags*/0,
265133912Sgibbs				   &ahc->parent_dmat);
266133912Sgibbs
267133912Sgibbs	if (error != 0) {
268133912Sgibbs		printf("ahc_isa_attach: Could not allocate DMA tag "
269133912Sgibbs		       "- error %d\n", error);
270133912Sgibbs		ahc_free(ahc);
271133912Sgibbs		return (ENOMEM);
272133912Sgibbs	}
273133912Sgibbs	ahc->dev_softc = dev;
274133912Sgibbs	error = aic7770_config(ahc, entry, /*unused ioport arg*/0);
275133912Sgibbs	if (error != 0) {
276133912Sgibbs		ahc_free(ahc);
277133912Sgibbs		return (error);
278133912Sgibbs	}
279133912Sgibbs
280133912Sgibbs	ahc_attach(ahc);
281133912Sgibbs	return (0);
282133912Sgibbs}
283133912Sgibbs
284133912Sgibbsstatic device_method_t ahc_isa_device_methods[] = {
285133912Sgibbs	/* Device interface */
286133912Sgibbs	DEVMETHOD(device_identify,      ahc_isa_identify),
287133912Sgibbs	DEVMETHOD(device_probe,		ahc_isa_probe),
288133912Sgibbs	DEVMETHOD(device_attach,	ahc_isa_attach),
289133912Sgibbs	DEVMETHOD(device_detach,	ahc_detach),
290133912Sgibbs	{ 0, 0 }
291133912Sgibbs};
292133912Sgibbs
293133912Sgibbsstatic driver_t ahc_isa_driver = {
294133912Sgibbs	"ahc",
295133912Sgibbs	ahc_isa_device_methods,
296133912Sgibbs	sizeof(struct ahc_softc)
297133912Sgibbs};
298133912Sgibbs
299133912SgibbsDRIVER_MODULE(ahc_isa, isa, ahc_isa_driver, ahc_devclass, 0, 0);
300133912SgibbsMODULE_DEPEND(ahc_isa, ahc, 1, 1, 1);
301133912SgibbsMODULE_VERSION(ahc_isa, 1);
302