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