pci_cfgreg.c revision 16471
150276Speter/**************************************************************************
250276Speter**
350276Speter**  $Id: pcibus.c,v 1.25 1996/06/13 21:50:41 se Exp $
450276Speter**
550276Speter**  pci bus subroutines for i386 architecture.
650276Speter**
750276Speter**  FreeBSD
850276Speter**
950276Speter**-------------------------------------------------------------------------
1050276Speter**
1150276Speter** Copyright (c) 1994 Wolfgang Stanglmeier.  All rights reserved.
1250276Speter**
1350276Speter** Redistribution and use in source and binary forms, with or without
1450276Speter** modification, are permitted provided that the following conditions
1550276Speter** are met:
1650276Speter** 1. Redistributions of source code must retain the above copyright
1750276Speter**    notice, this list of conditions and the following disclaimer.
1850276Speter** 2. Redistributions in binary form must reproduce the above copyright
1950276Speter**    notice, this list of conditions and the following disclaimer in the
2050276Speter**    documentation and/or other materials provided with the distribution.
2150276Speter** 3. The name of the author may not be used to endorse or promote products
2250276Speter**    derived from this software without specific prior written permission.
2350276Speter**
2450276Speter** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2550276Speter** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2650276Speter** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2750276Speter** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2850276Speter** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2950276Speter** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3050276Speter** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3150276Speter** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3250276Speter** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3350276Speter** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3450276Speter**
3550276Speter***************************************************************************
3650276Speter*/
3750276Speter
3850276Speter#include "vector.h"
3950276Speter
4050276Speter#include <sys/param.h>
4150276Speter#include <sys/systm.h>
4250276Speter#include <sys/kernel.h>
4350276Speter
4450276Speter#include <i386/isa/icu.h>
4550276Speter#include <i386/isa/isa_device.h>
4650276Speter
4750276Speter#include <pci/pcivar.h>
4850276Speter#include <pci/pcireg.h>
4950276Speter#include <pci/pcibus.h>
5050276Speter
5150276Speter/*-----------------------------------------------------------------
5250276Speter**
5350276Speter**	The following functions are provided by the pci bios.
5450276Speter**	They are used only by the pci configuration.
5550276Speter**
5650276Speter**	pcibus_setup():
5750276Speter**		Probes for a pci system.
5850276Speter**		Sets pci_maxdevice and pci_mechanism.
5950276Speter**
6050276Speter**	pcibus_tag():
6150276Speter**		Creates a handle for pci configuration space access.
6250276Speter**		This handle is given to the read/write functions.
6350276Speter**
6450276Speter**	pcibus_ftag():
6550276Speter**		Creates a modified handle.
6650276Speter**
6750276Speter**	pcibus_read():
6850276Speter**		Read a long word from the pci configuration space.
6950276Speter**		Requires a tag (from pcitag) and the register
7050276Speter**		number (should be a long word alligned one).
7150276Speter**
7250276Speter**	pcibus_write():
7350276Speter**		Writes a long word to the pci configuration space.
7450276Speter**		Requires a tag (from pcitag), the register number
7550276Speter**		(should be a long word alligned one), and a value.
7650276Speter**
7750276Speter**	pcibus_regirq():
7850276Speter**		Register an interupt handler for a pci device.
7950276Speter**		Requires a tag (from pcitag), the register number
8050276Speter**		(should be a long word alligned one), and a value.
8150276Speter**
82**-----------------------------------------------------------------
83*/
84
85static int
86pcibus_check (void);
87
88static void
89pcibus_setup (void);
90
91static pcici_t
92pcibus_tag (u_char bus, u_char device, u_char func);
93
94static pcici_t
95pcibus_ftag (pcici_t tag, u_char func);
96
97static u_long
98pcibus_read (pcici_t tag, u_long reg);
99
100static void
101pcibus_write (pcici_t tag, u_long reg, u_long data);
102
103static int
104pcibus_ihandler_attach (int irq, inthand2_t *func, int arg, unsigned* maskptr);
105
106static int
107pcibus_ihandler_detach (int irq, inthand2_t *func);
108
109static int
110pcibus_imask_include (int irq, unsigned* maskptr);
111
112static int
113pcibus_imask_exclude (int irq, unsigned* maskptr);
114
115static struct pcibus i386pci = {
116	"pci",
117	pcibus_setup,
118	pcibus_tag,
119	pcibus_ftag,
120	pcibus_read,
121	pcibus_write,
122	ICU_LEN,
123	pcibus_ihandler_attach,
124	pcibus_ihandler_detach,
125	pcibus_imask_include,
126	pcibus_imask_exclude,
127};
128
129/*
130**	Announce structure to generic driver
131*/
132
133DATA_SET (pcibus_set, i386pci);
134
135/*--------------------------------------------------------------------
136**
137**      Determine configuration mode
138**
139**--------------------------------------------------------------------
140*/
141
142
143#define CONF1_ADDR_PORT    0x0cf8
144#define CONF1_DATA_PORT    0x0cfc
145
146#define CONF1_ENABLE       0x80000000ul
147#define CONF1_ENABLE_CHK   0x80000000ul
148#define CONF1_ENABLE_MSK   0x7ff00000ul
149#define CONF1_ENABLE_CHK1  0xff000001ul
150#define CONF1_ENABLE_MSK1  0x80000001ul
151#define CONF1_ENABLE_RES1  0x80000000ul
152
153#define CONF2_ENABLE_PORT  0x0cf8
154#define CONF2_FORWARD_PORT 0x0cfa
155
156#define CONF2_ENABLE_CHK   0x0e
157#define CONF2_ENABLE_RES   0x0e
158
159static int
160pcibus_check (void)
161{
162	u_char device;
163
164	if (bootverbose) printf ("pcibus_check:\tdevice ");
165
166	for (device = 0; device < pci_maxdevice; device++) {
167		unsigned long id;
168		if (bootverbose)
169			printf ("%d ", device);
170		id = pcibus_read (pcibus_tag (0,device,0), 0);
171		if (id && id != 0xfffffffful) {
172			if (bootverbose) printf ("is there (id=%08lx)\n", id);
173			return 1;
174		}
175	}
176	if (bootverbose)
177		printf ("-- nothing found\n");
178	return 0;
179}
180
181static void
182pcibus_setup (void)
183{
184	unsigned long mode1res,oldval1;
185	unsigned char mode2res,oldval2;
186
187	oldval1 = inl (CONF1_ADDR_PORT);
188
189	if (bootverbose) {
190		printf ("pcibus_setup(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", oldval1);
191	}
192
193	/*---------------------------------------
194	**      Assume configuration mechanism 1 for now ...
195	**---------------------------------------
196	*/
197
198	if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
199
200		pci_mechanism = 1;
201		pci_maxdevice = 32;
202
203		outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
204		outb (CONF1_ADDR_PORT +3, 0);
205		mode1res = inl (CONF1_ADDR_PORT);
206		outl (CONF1_ADDR_PORT, oldval1);
207
208		if (bootverbose)
209		    printf ("pcibus_setup(1a):\tmode1res=0x%08lx (0x%08lx)\n",
210			    mode1res, CONF1_ENABLE_CHK);
211
212		if (mode1res) {
213			if (pcibus_check())
214				return;
215		};
216
217		outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
218		mode1res = inl(CONF1_ADDR_PORT);
219		outl (CONF1_ADDR_PORT, oldval1);
220
221		if (bootverbose)
222		    printf ("pcibus_setup(1b):\tmode1res=0x%08lx (0x%08lx)\n",
223			    mode1res, CONF1_ENABLE_CHK1);
224
225		if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
226			if (pcibus_check())
227				return;
228		};
229	}
230
231	/*---------------------------------------
232	**      Try configuration mechanism 2 ...
233	**---------------------------------------
234	*/
235
236	oldval2 = inb (CONF2_ENABLE_PORT);
237
238	if (bootverbose) {
239		printf ("pcibus_setup(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", oldval2);
240	}
241
242	if ((oldval2 & 0xf0) == 0) {
243
244		pci_mechanism = 2;
245		pci_maxdevice = 16;
246
247		outb (CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
248		mode2res = inb(CONF2_ENABLE_PORT);
249		outb (CONF2_ENABLE_PORT, oldval2);
250
251		if (bootverbose)
252		    printf ("pcibus_setup(2a):\tmode2res=0x%02x (0x%02x)\n",
253			    mode2res, CONF2_ENABLE_CHK);
254
255		if (mode2res == CONF2_ENABLE_RES) {
256		    if (bootverbose)
257			printf ("pcibus_setup(2a):\tnow trying mechanism 2\n");
258
259			if (pcibus_check())
260				return;
261		}
262	}
263
264	/*---------------------------------------
265	**      No PCI bus host bridge found
266	**---------------------------------------
267	*/
268
269	pci_mechanism = 0;
270	pci_maxdevice = 0;
271}
272
273/*--------------------------------------------------------------------
274**
275**      Build a pcitag from bus, device and function number
276**
277**--------------------------------------------------------------------
278*/
279
280static pcici_t
281pcibus_tag (unsigned char bus, unsigned char device, unsigned char func)
282{
283	pcici_t tag;
284
285	tag.cfg1 = 0;
286	if (func   >=  8) return tag;
287
288	switch (pci_mechanism) {
289
290	case 1:
291		if (device < 32) {
292			tag.cfg1 = CONF1_ENABLE
293				| (((u_long) bus   ) << 16ul)
294				| (((u_long) device) << 11ul)
295				| (((u_long) func  ) <<  8ul);
296		}
297		break;
298	case 2:
299		if (device < 16) {
300			tag.cfg2.port    = 0xc000 | (device << 8ul);
301			tag.cfg2.enable  = 0xf0 | (func << 1ul);
302			tag.cfg2.forward = bus;
303		}
304		break;
305	};
306	return tag;
307}
308
309static pcici_t
310pcibus_ftag (pcici_t tag, u_char func)
311{
312	switch (pci_mechanism) {
313
314	case 1:
315		tag.cfg1 &= ~0x700ul;
316		tag.cfg1 |= (((u_long) func) << 8ul);
317		break;
318	case 2:
319		tag.cfg2.enable  = 0xf0 | (func << 1ul);
320		break;
321	};
322	return tag;
323}
324
325/*--------------------------------------------------------------------
326**
327**      Read register from configuration space.
328**
329**--------------------------------------------------------------------
330*/
331
332static u_long
333pcibus_read (pcici_t tag, u_long reg)
334{
335	u_long addr, data = 0;
336
337	if (!tag.cfg1) return (0xfffffffful);
338
339	switch (pci_mechanism) {
340
341	case 1:
342		addr = tag.cfg1 | (reg & 0xfc);
343#ifdef PCI_DEBUG
344		printf ("pci_conf_read(1): addr=%x ", addr);
345#endif
346		outl (CONF1_ADDR_PORT, addr);
347		data = inl (CONF1_DATA_PORT);
348		outl (CONF1_ADDR_PORT, 0   );
349		break;
350
351	case 2:
352		addr = tag.cfg2.port | (reg & 0xfc);
353#ifdef PCI_DEBUG
354		printf ("pci_conf_read(2): addr=%x ", addr);
355#endif
356		outb (CONF2_ENABLE_PORT , tag.cfg2.enable );
357		outb (CONF2_FORWARD_PORT, tag.cfg2.forward);
358
359		data = inl ((u_short) addr);
360
361		outb (CONF2_ENABLE_PORT,  0);
362		outb (CONF2_FORWARD_PORT, 0);
363		break;
364	};
365
366#ifdef PCI_DEBUG
367	printf ("data=%x\n", data);
368#endif
369
370	return (data);
371}
372
373/*--------------------------------------------------------------------
374**
375**      Write register into configuration space.
376**
377**--------------------------------------------------------------------
378*/
379
380static void
381pcibus_write (pcici_t tag, u_long reg, u_long data)
382{
383	u_long addr;
384
385	if (!tag.cfg1) return;
386
387	switch (pci_mechanism) {
388
389	case 1:
390		addr = tag.cfg1 | (reg & 0xfc);
391#ifdef PCI_DEBUG
392		printf ("pci_conf_write(1): addr=%x data=%x\n",
393			addr, data);
394#endif
395		outl (CONF1_ADDR_PORT, addr);
396		outl (CONF1_DATA_PORT, data);
397		outl (CONF1_ADDR_PORT,   0 );
398		break;
399
400	case 2:
401		addr = tag.cfg2.port | (reg & 0xfc);
402#ifdef PCI_DEBUG
403		printf ("pci_conf_write(2): addr=%x data=%x\n",
404			addr, data);
405#endif
406		outb (CONF2_ENABLE_PORT,  tag.cfg2.enable);
407		outb (CONF2_FORWARD_PORT, tag.cfg2.forward);
408
409		outl ((u_short) addr, data);
410
411		outb (CONF2_ENABLE_PORT,  0);
412		outb (CONF2_FORWARD_PORT, 0);
413		break;
414	};
415}
416
417/*-----------------------------------------------------------------------
418**
419**	Register an interupt handler for a pci device.
420**
421**-----------------------------------------------------------------------
422*/
423
424static int
425pcibus_ihandler_attach (int irq, inthand2_t *func, int arg, unsigned * maskptr)
426{
427	char buf[16];
428	char *cp;
429	int free_id, id, result;
430
431	sprintf(buf, "pci irq%d", irq);
432	for (cp = intrnames, free_id = 0, id = 0; id < NR_DEVICES; id++) {
433		if (strcmp(cp, buf) == 0)
434			break;
435		if (free_id <= 0 && strcmp(cp, "pci irqnn") == 0)
436			free_id = id;
437		while (*cp++ != '\0')
438			;
439	}
440	if (id == NR_DEVICES) {
441		id = free_id;
442		if (id == 0) {
443			/*
444			 * All pci irq counters are in use, perhaps because
445			 * config is old so there aren't any.  Abuse the
446			 * clk0 counter.
447			 */
448			printf (
449		"pcibus_ihandler_attach: counting pci irq%d's as clk0 irqs\n",
450				irq);
451		}
452	}
453	result = register_intr(
454		irq,		    /* isa irq	    */
455		id,		    /* device id    */
456		0,		    /* flags?	    */
457		func,		    /* handler	    */
458		maskptr,	    /* mask pointer */
459		arg);		    /* handler arg  */
460
461	if (result) {
462		printf ("@@@ pcibus_ihandler_attach: result=%d\n", result);
463		return (result);
464	};
465	update_intr_masks();
466
467	INTREN ((1ul<<irq));
468	return (0);
469}
470
471static int
472pcibus_ihandler_detach (int irq, inthand2_t *func)
473{
474	int result;
475
476	INTRDIS ((1ul<<irq));
477
478	result = unregister_intr (irq, func);
479
480	if (result)
481		printf ("@@@ pcibus_ihandler_detach: result=%d\n", result);
482
483	update_intr_masks();
484
485	return (result);
486}
487
488static int
489pcibus_imask_include (int irq, unsigned* maskptr)
490{
491	unsigned mask;
492
493	if (!maskptr) return (0);
494
495	mask = 1ul << irq;
496
497	if (*maskptr & mask)
498		return (-1);
499
500	INTRMASK (*maskptr, mask);
501	update_intr_masks();
502
503	return (0);
504}
505
506static int
507pcibus_imask_exclude (int irq, unsigned* maskptr)
508{
509	unsigned mask;
510
511	if (!maskptr) return (0);
512
513	mask = 1ul << irq;
514
515	if (! (*maskptr & mask))
516		return (-1);
517
518	*maskptr &= ~mask;
519	update_intr_masks();
520
521	return (0);
522}
523