Home | History | Annotate | Line # | Download | only in altboot
      1  1.5       phx /* $NetBSD: pci.c,v 1.5 2011/03/10 21:11:49 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.3  nisimura 		struct pcidev *, 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.3  nisimura 	unsigned pciid;
     74  1.3  nisimura 	struct pcidev target;
     75  1.1  nisimura 
     76  1.1  nisimura 	pciid = PCI_DEVICE(vend, prod);
     77  1.3  nisimura 	if (_pcilookup(0, devmatch, pciid, &target, 0, 1)) {
     78  1.3  nisimura 		*tag = target.bdf;
     79  1.1  nisimura 		return 0;
     80  1.1  nisimura 	}
     81  1.1  nisimura 	*tag = ~0;
     82  1.1  nisimura 	return -1;
     83  1.1  nisimura }
     84  1.1  nisimura 
     85  1.1  nisimura int
     86  1.1  nisimura pcilookup(type, list, max)
     87  1.1  nisimura 	unsigned type;
     88  1.3  nisimura 	struct pcidev *list;
     89  1.1  nisimura 	int max;
     90  1.1  nisimura {
     91  1.1  nisimura 
     92  1.1  nisimura 	return _pcilookup(0, clsmatch, type, list, 0, max);
     93  1.1  nisimura }
     94  1.1  nisimura 
     95  1.1  nisimura unsigned
     96  1.1  nisimura pcimaketag(int b, int d, int f)
     97  1.1  nisimura {
     98  1.1  nisimura 
     99  1.1  nisimura 	return (1U << 31) | (b << 16) | (d << 11) | (f << 8);
    100  1.1  nisimura }
    101  1.1  nisimura 
    102  1.1  nisimura void
    103  1.1  nisimura pcidecomposetag(unsigned tag, int *b, int *d, int *f)
    104  1.1  nisimura {
    105  1.1  nisimura 
    106  1.1  nisimura 	if (b != NULL)
    107  1.1  nisimura 		*b = (tag >> 16) & 0xff;
    108  1.1  nisimura 	if (d != NULL)
    109  1.1  nisimura 		*d = (tag >> 11) & 0x1f;
    110  1.1  nisimura 	if (f != NULL)
    111  1.1  nisimura 		*f = (tag >> 8) & 0x7;
    112  1.1  nisimura 	return;
    113  1.1  nisimura }
    114  1.1  nisimura 
    115  1.1  nisimura unsigned
    116  1.1  nisimura pcicfgread(unsigned tag, int off)
    117  1.1  nisimura {
    118  1.1  nisimura 	unsigned cfg;
    119  1.1  nisimura 
    120  1.1  nisimura 	cfg = tag | (off &~ 03);
    121  1.1  nisimura 	iohtole32(CONFIG_ADDR, cfg);
    122  1.1  nisimura 	return iole32toh(CONFIG_DATA);
    123  1.1  nisimura }
    124  1.1  nisimura 
    125  1.1  nisimura void
    126  1.1  nisimura pcicfgwrite(unsigned tag, int off, unsigned val)
    127  1.1  nisimura {
    128  1.1  nisimura 	unsigned cfg;
    129  1.1  nisimura 
    130  1.1  nisimura 	cfg = tag | (off &~ 03);
    131  1.1  nisimura 	iohtole32(CONFIG_ADDR, cfg);
    132  1.1  nisimura 	iohtole32(CONFIG_DATA, val);
    133  1.1  nisimura }
    134  1.1  nisimura 
    135  1.1  nisimura static unsigned
    136  1.1  nisimura cfgread(int b, int d, int f, int off)
    137  1.1  nisimura {
    138  1.1  nisimura 	unsigned cfg;
    139  1.1  nisimura 
    140  1.1  nisimura 	off &= ~03;
    141  1.1  nisimura 	cfg = (1U << 31) | (b << 16) | (d << 11) | (f << 8) | off | 0;
    142  1.1  nisimura 	iohtole32(CONFIG_ADDR, cfg);
    143  1.1  nisimura 	return iole32toh(CONFIG_DATA);
    144  1.1  nisimura }
    145  1.1  nisimura 
    146  1.1  nisimura static void
    147  1.1  nisimura cfgwrite(int b, int d, int f, int off, unsigned val)
    148  1.1  nisimura {
    149  1.1  nisimura 	unsigned cfg;
    150  1.1  nisimura 
    151  1.1  nisimura 	off &= ~03;
    152  1.1  nisimura 	cfg = (1U << 31) | (b << 16) | (d << 11) | (f << 8) | off | 0;
    153  1.1  nisimura 	iohtole32(CONFIG_ADDR, cfg);
    154  1.1  nisimura 	iohtole32(CONFIG_DATA, val);
    155  1.1  nisimura }
    156  1.1  nisimura 
    157  1.1  nisimura static void
    158  1.1  nisimura _buswalk(int bus, int (*proc)(int, int, int, unsigned long), unsigned long data)
    159  1.1  nisimura {
    160  1.1  nisimura 	int device, function, nfunctions;
    161  1.1  nisimura 	unsigned pciid, bhlcr;
    162  1.1  nisimura 
    163  1.1  nisimura 	for (device = 0; device < MAXNDEVS; device++) {
    164  1.1  nisimura 		pciid = cfgread(bus, device, 0, PCI_ID_REG);
    165  1.1  nisimura 		if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID)
    166  1.1  nisimura 			continue;
    167  1.1  nisimura 		if (PCI_VENDOR(pciid) == 0)
    168  1.1  nisimura 			continue;
    169  1.1  nisimura 		bhlcr = cfgread(bus, device, 0, PCI_BHLC_REG);
    170  1.1  nisimura 		nfunctions = (PCI_HDRTYPE_MULTIFN(bhlcr)) ? 8 : 1;
    171  1.1  nisimura 		for (function = 0; function < nfunctions; function++) {
    172  1.1  nisimura 			pciid = cfgread(bus, device, function, PCI_ID_REG);
    173  1.1  nisimura 			if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID)
    174  1.1  nisimura 				continue;
    175  1.1  nisimura 			if (PCI_VENDOR(pciid) == 0)
    176  1.1  nisimura 				continue;
    177  1.1  nisimura 
    178  1.1  nisimura 			if ((*proc)(bus, device, function, data) != 0)
    179  1.1  nisimura 				goto out; /* early exit */
    180  1.1  nisimura 		}
    181  1.1  nisimura 	}
    182  1.1  nisimura   out:;
    183  1.1  nisimura }
    184  1.1  nisimura 
    185  1.1  nisimura static int
    186  1.1  nisimura deviceinit(int bus, int dev, int func, unsigned long data)
    187  1.1  nisimura {
    188  1.1  nisimura 	unsigned val;
    189  1.1  nisimura 
    190  1.1  nisimura 	/* 0x00 */
    191  1.2       phx #ifdef DEBUG
    192  1.1  nisimura 	printf("%02d:%02d:%02d:", bus, dev, func);
    193  1.5       phx 	val = cfgread(bus, dev, func, PCI_ID_REG);
    194  1.1  nisimura 	printf(" chip %04x.%04x", val & 0xffff, val>>16);
    195  1.1  nisimura 	val = cfgread(bus, dev, func, 0x2c);
    196  1.1  nisimura 	printf(" card %04x.%04x", val & 0xffff, val>>16);
    197  1.5       phx 	val = cfgread(bus, dev, func, PCI_CLASS_REG);
    198  1.1  nisimura 	printf(" rev %02x class %02x.%02x.%02x",
    199  1.5       phx 	    PCI_REVISION(val), (val>>24), (val>>16) & 0xff,
    200  1.5       phx 	    PCI_INTERFACE(val));
    201  1.5       phx 	val = cfgread(bus, dev, func, PCI_BHLC_REG);
    202  1.1  nisimura 	printf(" hdr %02x\n", (val>>16) & 0xff);
    203  1.2       phx #endif
    204  1.1  nisimura 
    205  1.1  nisimura 	/* 0x04 */
    206  1.5       phx 	val = cfgread(bus, dev, func, PCI_COMMAND_STATUS_REG);
    207  1.1  nisimura 	val |= 0xffff0107; /* enable IO,MEM,MASTER,SERR */
    208  1.1  nisimura 	cfgwrite(bus, dev, func, 0x04, val);
    209  1.1  nisimura 
    210  1.1  nisimura 	/* 0x0c */
    211  1.1  nisimura 	val = 0x80 << 8 | 0x08 /* 32B cache line */;
    212  1.5       phx 	cfgwrite(bus, dev, func, PCI_BHLC_REG, val);
    213  1.1  nisimura 
    214  1.4  nisimura 	/* 0x3c */
    215  1.4  nisimura 	val = cfgread(bus, dev, func, 0x3c) & ~0xff;
    216  1.4  nisimura 	val |= dev; /* assign IDSEL */
    217  1.4  nisimura 	cfgwrite(bus, dev, func, 0x3c, val);
    218  1.4  nisimura 
    219  1.1  nisimura 	/* skip legacy mode IDE controller BAR assignment */
    220  1.1  nisimura 	val = cfgread(bus, dev, func, PCI_CLASS_REG);
    221  1.5       phx 	if (PCI_CLASS(val) == PCI_CLASS_IDE && (PCI_INTERFACE(val) & 0x05) == 0)
    222  1.1  nisimura 		return 0;
    223  1.1  nisimura 
    224  1.1  nisimura 	memassign(bus, dev, func);
    225  1.1  nisimura 
    226  1.1  nisimura 	/* descending toward PCI-PCI bridge */
    227  1.5       phx 	if ((cfgread(bus, dev, func, PCI_CLASS_REG) >> 16) == PCI_CLASS_PPB) {
    228  1.1  nisimura 		unsigned new;
    229  1.1  nisimura 
    230  1.1  nisimura 		/* 0x18 */
    231  1.1  nisimura 		new = (maxbus += 1);
    232  1.1  nisimura 		val = (0xff << 16) | (new << 8) | bus;
    233  1.1  nisimura 		cfgwrite(bus, dev, func, 0x18, val);
    234  1.1  nisimura 
    235  1.1  nisimura 		/* 0x1c and 0x30 */
    236  1.1  nisimura 		val = (iostart + (0xfff)) & ~0xfff; /* 4KB boundary */
    237  1.1  nisimura 		iostart = val;
    238  1.1  nisimura 		val = 0xffff0000 | (iolimit & 0xf000) | (val & 0xf000) >> 8;
    239  1.1  nisimura 		cfgwrite(bus, dev, func, 0x1c, val);
    240  1.1  nisimura 		val = (iolimit & 0xffff0000) | (val & 0xffff0000) >> 16;
    241  1.1  nisimura 		cfgwrite(bus, dev, func, 0x30, val);
    242  1.1  nisimura 
    243  1.1  nisimura 		/* 0x20 */
    244  1.1  nisimura 		val = (memstart + 0xfffff) &~ 0xfffff; /* 1MB boundary */
    245  1.1  nisimura 		memstart = val;
    246  1.1  nisimura 		val = (memlimit & 0xffff0000) | (val & 0xffff0000) >> 16;
    247  1.1  nisimura 		cfgwrite(bus, dev, func, 0x20, val);
    248  1.1  nisimura 
    249  1.1  nisimura 		/* redo 0x04 */
    250  1.1  nisimura 		val = cfgread(bus, dev, func, 0x04);
    251  1.1  nisimura 		val |= 0xffff0107;
    252  1.1  nisimura 		cfgwrite(bus, dev, func, 0x04, val);
    253  1.1  nisimura 
    254  1.1  nisimura 		_buswalk(new, deviceinit, data);
    255  1.1  nisimura 
    256  1.1  nisimura 		/* adjust 0x18 */
    257  1.1  nisimura 		val = cfgread(bus, dev, func, 0x18);
    258  1.1  nisimura 		val = (maxbus << 16) | (val & 0xffff);
    259  1.1  nisimura 		cfgwrite(bus, dev, func, 0x18, val);
    260  1.1  nisimura 	}
    261  1.1  nisimura 	return 0;
    262  1.1  nisimura }
    263  1.1  nisimura 
    264  1.1  nisimura static void
    265  1.1  nisimura memassign(int bus, int dev, int func)
    266  1.1  nisimura {
    267  1.1  nisimura 	unsigned val, maxbar, mapr, req, mapbase, size;
    268  1.1  nisimura 
    269  1.1  nisimura 	val = cfgread(bus, dev, func, 0x0c);
    270  1.1  nisimura 	switch (PCI_HDRTYPE_TYPE(val)) {
    271  1.1  nisimura 	case 0:
    272  1.1  nisimura 		maxbar = 0x10 + 6 * 4; break;
    273  1.1  nisimura 	case 1:
    274  1.1  nisimura 		maxbar = 0x10 + 2 * 4; break;
    275  1.1  nisimura 	default:
    276  1.1  nisimura 		maxbar = 0x10 + 1 * 4; break;
    277  1.1  nisimura 	}
    278  1.1  nisimura 	for (mapr = 0x10; mapr < maxbar; mapr += 4) {
    279  1.1  nisimura 		cfgwrite(bus, dev, func, mapr, 0xffffffff);
    280  1.1  nisimura 		val = cfgread(bus, dev, func, mapr);
    281  1.1  nisimura 		if (val & 01) {
    282  1.1  nisimura 			/* PCI IO space */
    283  1.1  nisimura 			req = ~(val & 0xfffffffc) + 1;
    284  1.1  nisimura 			if (req & (req - 1))	/* power of 2 */
    285  1.1  nisimura 				continue;
    286  1.1  nisimura 			if (req == 0)		/* ever exists */
    287  1.1  nisimura 				continue;
    288  1.1  nisimura 			size = (req > 0x10) ? req : 0x10;
    289  1.1  nisimura 			mapbase = (iostart + size - 1) & ~(size - 1);
    290  1.1  nisimura 			if (mapbase + size > iolimit)
    291  1.1  nisimura 				continue;
    292  1.1  nisimura 
    293  1.1  nisimura 			iostart = mapbase + size;
    294  1.1  nisimura 			/* establish IO space */
    295  1.1  nisimura 			cfgwrite(bus, dev, func, mapr, mapbase | 01);
    296  1.1  nisimura 		}
    297  1.1  nisimura 		else {
    298  1.1  nisimura 			/* PCI memory space */
    299  1.1  nisimura 			req = ~(val & 0xfffffff0) + 1;
    300  1.1  nisimura 			if (req & (req - 1))	/* power of 2 */
    301  1.1  nisimura 				continue;
    302  1.1  nisimura 			if (req == 0)		/* ever exists */
    303  1.1  nisimura 				continue;
    304  1.1  nisimura 			val &= 0x6;
    305  1.1  nisimura 			if (val == 2 || val == 6)
    306  1.1  nisimura 				continue;
    307  1.1  nisimura 			size = (req > 0x1000) ? req : 0x1000;
    308  1.1  nisimura 			mapbase = (memstart + size - 1) & ~(size - 1);
    309  1.1  nisimura 			if (mapbase + size > memlimit)
    310  1.1  nisimura 				continue;
    311  1.1  nisimura 
    312  1.1  nisimura 			memstart = mapbase + size;
    313  1.1  nisimura 			/* establish memory space */
    314  1.1  nisimura 			cfgwrite(bus, dev, func, mapr, mapbase);
    315  1.1  nisimura 			if (val == 4) {
    316  1.1  nisimura 				mapr += 4;
    317  1.1  nisimura 				cfgwrite(bus, dev, func, mapr, 0);
    318  1.1  nisimura 			}
    319  1.1  nisimura 		}
    320  1.2       phx 		DPRINTF(("%s base %x size %x\n", (val & 01) ? "i/o" : "mem",
    321  1.2       phx 		    mapbase, size));
    322  1.1  nisimura 	}
    323  1.1  nisimura }
    324  1.1  nisimura 
    325  1.1  nisimura static int
    326  1.1  nisimura devmatch(int bus, int dev, int func, unsigned long data)
    327  1.1  nisimura {
    328  1.1  nisimura 	unsigned pciid;
    329  1.1  nisimura 
    330  1.1  nisimura 	pciid = cfgread(bus, dev, func, PCI_ID_REG);
    331  1.1  nisimura 	return (pciid == (unsigned)data);
    332  1.1  nisimura }
    333  1.1  nisimura 
    334  1.1  nisimura static int
    335  1.1  nisimura clsmatch(int bus, int dev, int func, unsigned long data)
    336  1.1  nisimura {
    337  1.1  nisimura 	unsigned class;
    338  1.1  nisimura 
    339  1.1  nisimura 	class = cfgread(bus, dev, func, PCI_CLASS_REG);
    340  1.5       phx 	return PCI_CLASS(class) == (unsigned)data;
    341  1.1  nisimura }
    342  1.1  nisimura 
    343  1.1  nisimura static int
    344  1.3  nisimura _pcilookup(int bus, int (*match)(int, int, int, unsigned long), unsigned long data, struct pcidev *list, int index, int limit)
    345  1.1  nisimura {
    346  1.1  nisimura 	int device, function, nfuncs;
    347  1.1  nisimura 	unsigned pciid, bhlcr, class;
    348  1.1  nisimura 
    349  1.1  nisimura 	for (device = 0; device < MAXNDEVS; device++) {
    350  1.1  nisimura 		pciid = cfgread(bus, device, 0, PCI_ID_REG);
    351  1.1  nisimura 		if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID)
    352  1.1  nisimura 			continue;
    353  1.1  nisimura 		if (PCI_VENDOR(pciid) == 0)
    354  1.1  nisimura 			continue;
    355  1.1  nisimura 		class = cfgread(bus, device, 0, PCI_CLASS_REG);
    356  1.5       phx 		if (PCI_CLASS(class) == PCI_CLASS_PPB) {
    357  1.1  nisimura 			/* exploring bus beyond PCI-PCI bridge */
    358  1.1  nisimura 			index = _pcilookup(bus + 1,
    359  1.1  nisimura 				    match, data, list, index, limit);
    360  1.1  nisimura 			if (index >= limit)
    361  1.1  nisimura 				goto out;
    362  1.1  nisimura 			continue;
    363  1.1  nisimura 		}
    364  1.1  nisimura 		bhlcr = cfgread(bus, device, 0, PCI_BHLC_REG);
    365  1.1  nisimura 		nfuncs = (PCI_HDRTYPE_MULTIFN(bhlcr)) ? 8 : 1;
    366  1.1  nisimura 		for (function = 0; function < nfuncs; function++) {
    367  1.1  nisimura 			pciid = cfgread(bus, device, function, PCI_ID_REG);
    368  1.1  nisimura 			if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID)
    369  1.1  nisimura 				continue;
    370  1.1  nisimura 			if (PCI_VENDOR(pciid) == 0)
    371  1.1  nisimura 				continue;
    372  1.1  nisimura 			if ((*match)(bus, device, function, data)) {
    373  1.3  nisimura 				list[index].pvd = pciid;
    374  1.3  nisimura 				list[index].bdf =
    375  1.1  nisimura 				     pcimaketag(bus, device, function);
    376  1.1  nisimura 				index += 1;
    377  1.1  nisimura 				if (index >= limit)
    378  1.1  nisimura 					goto out;
    379  1.1  nisimura 			}
    380  1.1  nisimura 		}
    381  1.1  nisimura 	}
    382  1.1  nisimura   out:
    383  1.1  nisimura 	return index;
    384  1.1  nisimura }
    385