Home | History | Annotate | Line # | Download | only in altboot
pci.c revision 1.2
      1  1.2       phx /* $NetBSD: pci.c,v 1.2 2011/01/27 17:38:04 phx Exp $ */
      2  1.1  nisimura 
      3  1.1  nisimura /*-
      4  1.1  nisimura  * Copyright (c) 2007 The NetBSD Foundation, Inc.
      5  1.1  nisimura  * All rights reserved.
      6  1.1  nisimura  *
      7  1.1  nisimura  * This code is derived from software contributed to The NetBSD Foundation
      8  1.1  nisimura  * by Tohru Nishimura.
      9  1.1  nisimura  *
     10  1.1  nisimura  * Redistribution and use in source and binary forms, with or without
     11  1.1  nisimura  * modification, are permitted provided that the following conditions
     12  1.1  nisimura  * are met:
     13  1.1  nisimura  * 1. Redistributions of source code must retain the above copyright
     14  1.1  nisimura  *    notice, this list of conditions and the following disclaimer.
     15  1.1  nisimura  * 2. Redistributions in binary form must reproduce the above copyright
     16  1.1  nisimura  *    notice, this list of conditions and the following disclaimer in the
     17  1.1  nisimura  *    documentation and/or other materials provided with the distribution.
     18  1.1  nisimura  *
     19  1.1  nisimura  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  1.1  nisimura  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  1.1  nisimura  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  1.1  nisimura  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  1.1  nisimura  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  1.1  nisimura  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  1.1  nisimura  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  1.1  nisimura  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  1.1  nisimura  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  1.1  nisimura  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  1.1  nisimura  * POSSIBILITY OF SUCH DAMAGE.
     30  1.1  nisimura  */
     31  1.1  nisimura 
     32  1.1  nisimura #include <sys/param.h>
     33  1.1  nisimura 
     34  1.1  nisimura #include <lib/libsa/stand.h>
     35  1.1  nisimura 
     36  1.1  nisimura 
     37  1.1  nisimura #define MAXNDEVS 32
     38  1.1  nisimura 
     39  1.1  nisimura #include "globals.h"
     40  1.1  nisimura 
     41  1.1  nisimura static unsigned cfgread(int, int, int, int);
     42  1.1  nisimura static void cfgwrite(int, int, int, int, unsigned);
     43  1.1  nisimura static void _buswalk(int,
     44  1.1  nisimura 		int (*)(int, int, int, unsigned long), unsigned long);
     45  1.1  nisimura static int _pcilookup(int,
     46  1.1  nisimura 		int (*)(int, int, int, unsigned long), unsigned long,
     47  1.1  nisimura 		unsigned [][2], int, int);
     48  1.1  nisimura static int deviceinit(int, int, int, unsigned long);
     49  1.1  nisimura static void memassign(int, int, int);
     50  1.1  nisimura static int devmatch(int, int, int, unsigned long);
     51  1.1  nisimura static int clsmatch(int, int, int, unsigned long);
     52  1.1  nisimura 
     53  1.1  nisimura unsigned memstart, memlimit;
     54  1.1  nisimura unsigned iostart, iolimit;
     55  1.1  nisimura unsigned maxbus;
     56  1.1  nisimura 
     57  1.1  nisimura void
     58  1.1  nisimura pcisetup(void)
     59  1.1  nisimura {
     60  1.1  nisimura 
     61  1.1  nisimura 	memstart = PCI_MEMBASE;
     62  1.1  nisimura 	memlimit = PCI_MEMLIMIT;
     63  1.1  nisimura 	iostart =  PCI_IOBASE;
     64  1.1  nisimura 	iolimit =  PCI_IOLIMIT;
     65  1.1  nisimura 	maxbus = 0;
     66  1.1  nisimura 
     67  1.1  nisimura 	(void)_buswalk(0, deviceinit, 0UL);
     68  1.1  nisimura }
     69  1.1  nisimura 
     70  1.1  nisimura int
     71  1.1  nisimura pcifinddev(unsigned vend, unsigned prod, unsigned *tag)
     72  1.1  nisimura {
     73  1.1  nisimura 	unsigned pciid, target[1][2];
     74  1.1  nisimura 
     75  1.1  nisimura 	pciid = PCI_DEVICE(vend, prod);
     76  1.1  nisimura 	if (_pcilookup(0, devmatch, pciid, target, 0, 1)) {
     77  1.1  nisimura 		*tag = target[0][1];
     78  1.1  nisimura 		return 0;
     79  1.1  nisimura 	}
     80  1.1  nisimura 	*tag = ~0;
     81  1.1  nisimura 	return -1;
     82  1.1  nisimura }
     83  1.1  nisimura 
     84  1.1  nisimura int
     85  1.1  nisimura pcilookup(type, list, max)
     86  1.1  nisimura 	unsigned type;
     87  1.1  nisimura 	unsigned list[][2];
     88  1.1  nisimura 	int max;
     89  1.1  nisimura {
     90  1.1  nisimura 
     91  1.1  nisimura 	return _pcilookup(0, clsmatch, type, list, 0, max);
     92  1.1  nisimura }
     93  1.1  nisimura 
     94  1.1  nisimura unsigned
     95  1.1  nisimura pcimaketag(int b, int d, int f)
     96  1.1  nisimura {
     97  1.1  nisimura 
     98  1.1  nisimura 	return (1U << 31) | (b << 16) | (d << 11) | (f << 8);
     99  1.1  nisimura }
    100  1.1  nisimura 
    101  1.1  nisimura void
    102  1.1  nisimura pcidecomposetag(unsigned tag, int *b, int *d, int *f)
    103  1.1  nisimura {
    104  1.1  nisimura 
    105  1.1  nisimura 	if (b != NULL)
    106  1.1  nisimura 		*b = (tag >> 16) & 0xff;
    107  1.1  nisimura 	if (d != NULL)
    108  1.1  nisimura 		*d = (tag >> 11) & 0x1f;
    109  1.1  nisimura 	if (f != NULL)
    110  1.1  nisimura 		*f = (tag >> 8) & 0x7;
    111  1.1  nisimura 	return;
    112  1.1  nisimura }
    113  1.1  nisimura 
    114  1.1  nisimura unsigned
    115  1.1  nisimura pcicfgread(unsigned tag, int off)
    116  1.1  nisimura {
    117  1.1  nisimura 	unsigned cfg;
    118  1.1  nisimura 
    119  1.1  nisimura 	cfg = tag | (off &~ 03);
    120  1.1  nisimura 	iohtole32(CONFIG_ADDR, cfg);
    121  1.1  nisimura 	return iole32toh(CONFIG_DATA);
    122  1.1  nisimura }
    123  1.1  nisimura 
    124  1.1  nisimura void
    125  1.1  nisimura pcicfgwrite(unsigned tag, int off, unsigned val)
    126  1.1  nisimura {
    127  1.1  nisimura 	unsigned cfg;
    128  1.1  nisimura 
    129  1.1  nisimura 	cfg = tag | (off &~ 03);
    130  1.1  nisimura 	iohtole32(CONFIG_ADDR, cfg);
    131  1.1  nisimura 	iohtole32(CONFIG_DATA, val);
    132  1.1  nisimura }
    133  1.1  nisimura 
    134  1.1  nisimura static unsigned
    135  1.1  nisimura cfgread(int b, int d, int f, int off)
    136  1.1  nisimura {
    137  1.1  nisimura 	unsigned cfg;
    138  1.1  nisimura 
    139  1.1  nisimura 	off &= ~03;
    140  1.1  nisimura 	cfg = (1U << 31) | (b << 16) | (d << 11) | (f << 8) | off | 0;
    141  1.1  nisimura 	iohtole32(CONFIG_ADDR, cfg);
    142  1.1  nisimura 	return iole32toh(CONFIG_DATA);
    143  1.1  nisimura }
    144  1.1  nisimura 
    145  1.1  nisimura static void
    146  1.1  nisimura cfgwrite(int b, int d, int f, int off, unsigned val)
    147  1.1  nisimura {
    148  1.1  nisimura 	unsigned cfg;
    149  1.1  nisimura 
    150  1.1  nisimura 	off &= ~03;
    151  1.1  nisimura 	cfg = (1U << 31) | (b << 16) | (d << 11) | (f << 8) | off | 0;
    152  1.1  nisimura 	iohtole32(CONFIG_ADDR, cfg);
    153  1.1  nisimura 	iohtole32(CONFIG_DATA, val);
    154  1.1  nisimura }
    155  1.1  nisimura 
    156  1.1  nisimura static void
    157  1.1  nisimura _buswalk(int bus, int (*proc)(int, int, int, unsigned long), unsigned long data)
    158  1.1  nisimura {
    159  1.1  nisimura 	int device, function, nfunctions;
    160  1.1  nisimura 	unsigned pciid, bhlcr;
    161  1.1  nisimura 
    162  1.1  nisimura 	for (device = 0; device < MAXNDEVS; device++) {
    163  1.1  nisimura 		pciid = cfgread(bus, device, 0, PCI_ID_REG);
    164  1.1  nisimura 		if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID)
    165  1.1  nisimura 			continue;
    166  1.1  nisimura 		if (PCI_VENDOR(pciid) == 0)
    167  1.1  nisimura 			continue;
    168  1.1  nisimura 		bhlcr = cfgread(bus, device, 0, PCI_BHLC_REG);
    169  1.1  nisimura 		nfunctions = (PCI_HDRTYPE_MULTIFN(bhlcr)) ? 8 : 1;
    170  1.1  nisimura 		for (function = 0; function < nfunctions; function++) {
    171  1.1  nisimura 			pciid = cfgread(bus, device, function, PCI_ID_REG);
    172  1.1  nisimura 			if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID)
    173  1.1  nisimura 				continue;
    174  1.1  nisimura 			if (PCI_VENDOR(pciid) == 0)
    175  1.1  nisimura 				continue;
    176  1.1  nisimura 
    177  1.1  nisimura 			if ((*proc)(bus, device, function, data) != 0)
    178  1.1  nisimura 				goto out; /* early exit */
    179  1.1  nisimura 		}
    180  1.1  nisimura 	}
    181  1.1  nisimura   out:;
    182  1.1  nisimura }
    183  1.1  nisimura 
    184  1.1  nisimura static int
    185  1.1  nisimura deviceinit(int bus, int dev, int func, unsigned long data)
    186  1.1  nisimura {
    187  1.1  nisimura 	unsigned val;
    188  1.1  nisimura 
    189  1.1  nisimura 	/* 0x00 */
    190  1.2       phx #ifdef DEBUG
    191  1.1  nisimura 	printf("%02d:%02d:%02d:", bus, dev, func);
    192  1.1  nisimura 	val = cfgread(bus, dev, func, 0x00);
    193  1.1  nisimura 	printf(" chip %04x.%04x", val & 0xffff, val>>16);
    194  1.1  nisimura 	val = cfgread(bus, dev, func, 0x2c);
    195  1.1  nisimura 	printf(" card %04x.%04x", val & 0xffff, val>>16);
    196  1.1  nisimura 	val = cfgread(bus, dev, func, 0x08);
    197  1.1  nisimura 	printf(" rev %02x class %02x.%02x.%02x",
    198  1.1  nisimura 		val & 0xff, (val>>24), (val>>16) & 0xff, (val>>8) & 0xff);
    199  1.1  nisimura 	val = cfgread(bus, dev, func, 0x0c);
    200  1.1  nisimura 	printf(" hdr %02x\n", (val>>16) & 0xff);
    201  1.2       phx #endif
    202  1.1  nisimura 
    203  1.1  nisimura 	/* 0x04 */
    204  1.1  nisimura 	val = cfgread(bus, dev, func, 0x04);
    205  1.1  nisimura 	val |= 0xffff0107; /* enable IO,MEM,MASTER,SERR */
    206  1.1  nisimura 	cfgwrite(bus, dev, func, 0x04, val);
    207  1.1  nisimura 
    208  1.1  nisimura 	/* 0x0c */
    209  1.1  nisimura 	val = 0x80 << 8 | 0x08 /* 32B cache line */;
    210  1.1  nisimura 	cfgwrite(bus, dev, func, 0x0c, val);
    211  1.1  nisimura 
    212  1.1  nisimura 	/* skip legacy mode IDE controller BAR assignment */
    213  1.1  nisimura 	val = cfgread(bus, dev, func, PCI_CLASS_REG);
    214  1.1  nisimura 	if ((val >> 16) == PCI_CLASS_IDE && ((val >> 8) & 0x05) == 0)
    215  1.1  nisimura 		return 0;
    216  1.1  nisimura 
    217  1.1  nisimura 	memassign(bus, dev, func);
    218  1.1  nisimura 
    219  1.1  nisimura 	/* descending toward PCI-PCI bridge */
    220  1.1  nisimura 	if ((cfgread(bus, dev, func, 0x08) >> 16) == PCI_CLASS_PPB) {
    221  1.1  nisimura 		unsigned new;
    222  1.1  nisimura 
    223  1.1  nisimura 		/* 0x18 */
    224  1.1  nisimura 		new = (maxbus += 1);
    225  1.1  nisimura 		val = (0xff << 16) | (new << 8) | bus;
    226  1.1  nisimura 		cfgwrite(bus, dev, func, 0x18, val);
    227  1.1  nisimura 
    228  1.1  nisimura 		/* 0x1c and 0x30 */
    229  1.1  nisimura 		val = (iostart + (0xfff)) & ~0xfff; /* 4KB boundary */
    230  1.1  nisimura 		iostart = val;
    231  1.1  nisimura 		val = 0xffff0000 | (iolimit & 0xf000) | (val & 0xf000) >> 8;
    232  1.1  nisimura 		cfgwrite(bus, dev, func, 0x1c, val);
    233  1.1  nisimura 		val = (iolimit & 0xffff0000) | (val & 0xffff0000) >> 16;
    234  1.1  nisimura 		cfgwrite(bus, dev, func, 0x30, val);
    235  1.1  nisimura 
    236  1.1  nisimura 		/* 0x20 */
    237  1.1  nisimura 		val = (memstart + 0xfffff) &~ 0xfffff; /* 1MB boundary */
    238  1.1  nisimura 		memstart = val;
    239  1.1  nisimura 		val = (memlimit & 0xffff0000) | (val & 0xffff0000) >> 16;
    240  1.1  nisimura 		cfgwrite(bus, dev, func, 0x20, val);
    241  1.1  nisimura 
    242  1.1  nisimura 		/* redo 0x04 */
    243  1.1  nisimura 		val = cfgread(bus, dev, func, 0x04);
    244  1.1  nisimura 		val |= 0xffff0107;
    245  1.1  nisimura 		cfgwrite(bus, dev, func, 0x04, val);
    246  1.1  nisimura 
    247  1.1  nisimura 		_buswalk(new, deviceinit, data);
    248  1.1  nisimura 
    249  1.1  nisimura 		/* adjust 0x18 */
    250  1.1  nisimura 		val = cfgread(bus, dev, func, 0x18);
    251  1.1  nisimura 		val = (maxbus << 16) | (val & 0xffff);
    252  1.1  nisimura 		cfgwrite(bus, dev, func, 0x18, val);
    253  1.1  nisimura 	}
    254  1.1  nisimura 	return 0;
    255  1.1  nisimura }
    256  1.1  nisimura 
    257  1.1  nisimura static void
    258  1.1  nisimura memassign(int bus, int dev, int func)
    259  1.1  nisimura {
    260  1.1  nisimura 	unsigned val, maxbar, mapr, req, mapbase, size;
    261  1.1  nisimura 
    262  1.1  nisimura 	val = cfgread(bus, dev, func, 0x0c);
    263  1.1  nisimura 	switch (PCI_HDRTYPE_TYPE(val)) {
    264  1.1  nisimura 	case 0:
    265  1.1  nisimura 		maxbar = 0x10 + 6 * 4; break;
    266  1.1  nisimura 	case 1:
    267  1.1  nisimura 		maxbar = 0x10 + 2 * 4; break;
    268  1.1  nisimura 	default:
    269  1.1  nisimura 		maxbar = 0x10 + 1 * 4; break;
    270  1.1  nisimura 	}
    271  1.1  nisimura 	for (mapr = 0x10; mapr < maxbar; mapr += 4) {
    272  1.1  nisimura 		cfgwrite(bus, dev, func, mapr, 0xffffffff);
    273  1.1  nisimura 		val = cfgread(bus, dev, func, mapr);
    274  1.1  nisimura 		if (val & 01) {
    275  1.1  nisimura 			/* PCI IO space */
    276  1.1  nisimura 			req = ~(val & 0xfffffffc) + 1;
    277  1.1  nisimura 			if (req & (req - 1))	/* power of 2 */
    278  1.1  nisimura 				continue;
    279  1.1  nisimura 			if (req == 0)		/* ever exists */
    280  1.1  nisimura 				continue;
    281  1.1  nisimura 			size = (req > 0x10) ? req : 0x10;
    282  1.1  nisimura 			mapbase = (iostart + size - 1) & ~(size - 1);
    283  1.1  nisimura 			if (mapbase + size > iolimit)
    284  1.1  nisimura 				continue;
    285  1.1  nisimura 
    286  1.1  nisimura 			iostart = mapbase + size;
    287  1.1  nisimura 			/* establish IO space */
    288  1.1  nisimura 			cfgwrite(bus, dev, func, mapr, mapbase | 01);
    289  1.1  nisimura 		}
    290  1.1  nisimura 		else {
    291  1.1  nisimura 			/* PCI memory space */
    292  1.1  nisimura 			req = ~(val & 0xfffffff0) + 1;
    293  1.1  nisimura 			if (req & (req - 1))	/* power of 2 */
    294  1.1  nisimura 				continue;
    295  1.1  nisimura 			if (req == 0)		/* ever exists */
    296  1.1  nisimura 				continue;
    297  1.1  nisimura 			val &= 0x6;
    298  1.1  nisimura 			if (val == 2 || val == 6)
    299  1.1  nisimura 				continue;
    300  1.1  nisimura 			size = (req > 0x1000) ? req : 0x1000;
    301  1.1  nisimura 			mapbase = (memstart + size - 1) & ~(size - 1);
    302  1.1  nisimura 			if (mapbase + size > memlimit)
    303  1.1  nisimura 				continue;
    304  1.1  nisimura 
    305  1.1  nisimura 			memstart = mapbase + size;
    306  1.1  nisimura 			/* establish memory space */
    307  1.1  nisimura 			cfgwrite(bus, dev, func, mapr, mapbase);
    308  1.1  nisimura 			if (val == 4) {
    309  1.1  nisimura 				mapr += 4;
    310  1.1  nisimura 				cfgwrite(bus, dev, func, mapr, 0);
    311  1.1  nisimura 			}
    312  1.1  nisimura 		}
    313  1.2       phx 		DPRINTF(("%s base %x size %x\n", (val & 01) ? "i/o" : "mem",
    314  1.2       phx 		    mapbase, size));
    315  1.1  nisimura 	}
    316  1.1  nisimura }
    317  1.1  nisimura 
    318  1.1  nisimura static int
    319  1.1  nisimura devmatch(int bus, int dev, int func, unsigned long data)
    320  1.1  nisimura {
    321  1.1  nisimura 	unsigned pciid;
    322  1.1  nisimura 
    323  1.1  nisimura 	pciid = cfgread(bus, dev, func, PCI_ID_REG);
    324  1.1  nisimura 	return (pciid == (unsigned)data);
    325  1.1  nisimura }
    326  1.1  nisimura 
    327  1.1  nisimura static int
    328  1.1  nisimura clsmatch(int bus, int dev, int func, unsigned long data)
    329  1.1  nisimura {
    330  1.1  nisimura 	unsigned class;
    331  1.1  nisimura 
    332  1.1  nisimura 	class = cfgread(bus, dev, func, PCI_CLASS_REG);
    333  1.1  nisimura 	return ((class >> 16) == (unsigned)data);
    334  1.1  nisimura }
    335  1.1  nisimura 
    336  1.1  nisimura static int
    337  1.1  nisimura _pcilookup(int bus, int (*match)(int, int, int, unsigned long), unsigned long data, unsigned list[][2], int index, int limit)
    338  1.1  nisimura {
    339  1.1  nisimura 	int device, function, nfuncs;
    340  1.1  nisimura 	unsigned pciid, bhlcr, class;
    341  1.1  nisimura 
    342  1.1  nisimura 	for (device = 0; device < MAXNDEVS; device++) {
    343  1.1  nisimura 		pciid = cfgread(bus, device, 0, PCI_ID_REG);
    344  1.1  nisimura 		if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID)
    345  1.1  nisimura 			continue;
    346  1.1  nisimura 		if (PCI_VENDOR(pciid) == 0)
    347  1.1  nisimura 			continue;
    348  1.1  nisimura 		class = cfgread(bus, device, 0, PCI_CLASS_REG);
    349  1.1  nisimura 		if ((class >> 16) == PCI_CLASS_PPB) {
    350  1.1  nisimura 			/* exploring bus beyond PCI-PCI bridge */
    351  1.1  nisimura 			index = _pcilookup(bus + 1,
    352  1.1  nisimura 				    match, data, list, index, limit);
    353  1.1  nisimura 			if (index >= limit)
    354  1.1  nisimura 				goto out;
    355  1.1  nisimura 			continue;
    356  1.1  nisimura 		}
    357  1.1  nisimura 		bhlcr = cfgread(bus, device, 0, PCI_BHLC_REG);
    358  1.1  nisimura 		nfuncs = (PCI_HDRTYPE_MULTIFN(bhlcr)) ? 8 : 1;
    359  1.1  nisimura 		for (function = 0; function < nfuncs; function++) {
    360  1.1  nisimura 			pciid = cfgread(bus, device, function, PCI_ID_REG);
    361  1.1  nisimura 			if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID)
    362  1.1  nisimura 				continue;
    363  1.1  nisimura 			if (PCI_VENDOR(pciid) == 0)
    364  1.1  nisimura 				continue;
    365  1.1  nisimura 			if ((*match)(bus, device, function, data)) {
    366  1.1  nisimura 				list[index][0] = pciid;
    367  1.1  nisimura 				list[index][1] =
    368  1.1  nisimura 				     pcimaketag(bus, device, function);
    369  1.1  nisimura 				index += 1;
    370  1.1  nisimura 				if (index >= limit)
    371  1.1  nisimura 					goto out;
    372  1.1  nisimura 			}
    373  1.1  nisimura 		}
    374  1.1  nisimura 	}
    375  1.1  nisimura   out:
    376  1.1  nisimura 	return index;
    377  1.1  nisimura }
    378