Home | History | Annotate | Line # | Download | only in pci
pchb.c revision 1.7
      1 /*	$NetBSD: pchb.c,v 1.7 2008/02/28 18:51:18 drochner Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 1996, 1998, 2000 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Jason R. Thorpe.
      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  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *        This product includes software developed by the NetBSD
     21  *        Foundation, Inc. and its contributors.
     22  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  *    contributors may be used to endorse or promote products derived
     24  *    from this software without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  * POSSIBILITY OF SUCH DAMAGE.
     37  */
     38 
     39 #include <sys/cdefs.h>
     40 __KERNEL_RCSID(0, "$NetBSD: pchb.c,v 1.7 2008/02/28 18:51:18 drochner Exp $");
     41 
     42 #include <sys/types.h>
     43 #include <sys/param.h>
     44 #include <sys/systm.h>
     45 #include <sys/device.h>
     46 
     47 #include <machine/bus.h>
     48 
     49 #include <dev/pci/pcivar.h>
     50 #include <dev/pci/pcireg.h>
     51 
     52 #include <dev/pci/pcidevs.h>
     53 
     54 #include <dev/pci/agpreg.h>
     55 #include <dev/pci/agpvar.h>
     56 
     57 #include <arch/x86/pci/pchbvar.h>
     58 
     59 #include "rnd.h"
     60 
     61 #define PCISET_BRIDGETYPE_MASK	0x3
     62 #define PCISET_TYPE_COMPAT	0x1
     63 #define PCISET_TYPE_AUX		0x2
     64 
     65 #define PCISET_BUSCONFIG_REG	0x48
     66 #define PCISET_BRIDGE_NUMBER(reg)	(((reg) >> 8) & 0xff)
     67 #define PCISET_PCI_BUS_NUMBER(reg)	(((reg) >> 16) & 0xff)
     68 
     69 /* XXX should be in dev/ic/i82443reg.h */
     70 #define	I82443BX_SDRAMC_REG	0x74 /* upper 16 bits */
     71 
     72 /* XXX should be in dev/ic/i82424{reg.var}.h */
     73 #define I82424_CPU_BCTL_REG		0x53
     74 #define I82424_PCI_BCTL_REG		0x54
     75 
     76 #define I82424_BCTL_CPUMEM_POSTEN	0x01
     77 #define I82424_BCTL_CPUPCI_POSTEN	0x02
     78 #define I82424_BCTL_PCIMEM_BURSTEN	0x01
     79 #define I82424_BCTL_PCI_BURSTEN		0x02
     80 
     81 int	pchbmatch(struct device *, struct cfdata *, void *);
     82 void	pchbattach(struct device *, struct device *, void *);
     83 int	pchbdetach(device_t, int);
     84 
     85 static bool	pchb_resume(device_t);
     86 static bool	pchb_suspend(device_t);
     87 
     88 CFATTACH_DECL(pchb, sizeof(struct pchb_softc),
     89     pchbmatch, pchbattach, pchbdetach, NULL);
     90 
     91 int
     92 pchbmatch(struct device *parent, struct cfdata *match, void *aux)
     93 {
     94 	struct pci_attach_args *pa = aux;
     95 
     96 	if (PCI_CLASS(pa->pa_class) == PCI_CLASS_BRIDGE &&
     97 	    PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_BRIDGE_HOST)
     98 		return 1;
     99 
    100 	return 0;
    101 }
    102 
    103 void
    104 pchbattach(struct device *parent, struct device *self, void *aux)
    105 {
    106 #if NRND > 0
    107 	struct pchb_softc *sc = (void *) self;
    108 #endif
    109 	struct pci_attach_args *pa = aux;
    110 	char devinfo[256];
    111 	struct pcibus_attach_args pba;
    112 	struct agpbus_attach_args apa;
    113 	pcireg_t bcreg;
    114 	u_char bdnum, pbnum = 0; /* XXX: gcc */
    115 	pcitag_t tag;
    116 	int doattach, attachflags, has_agp;
    117 
    118 	aprint_naive("\n");
    119 	aprint_normal("\n");
    120 
    121 	doattach = 0;
    122 	has_agp = 0;
    123 	attachflags = pa->pa_flags;
    124 
    125 	/*
    126 	 * Print out a description, and configure certain chipsets which
    127 	 * have auxiliary PCI buses.
    128 	 */
    129 
    130 	pci_devinfo(pa->pa_id, pa->pa_class, 0, devinfo, sizeof(devinfo));
    131 	aprint_normal("%s: %s (rev. 0x%02x)\n", self->dv_xname, devinfo,
    132 	    PCI_REVISION(pa->pa_class));
    133 
    134 	switch (PCI_VENDOR(pa->pa_id)) {
    135 	/*
    136 	 * i386 stuff.
    137 	 */
    138 	case PCI_VENDOR_SERVERWORKS:
    139 		pbnum = pci_conf_read(pa->pa_pc, pa->pa_tag, 0x44) & 0xff;
    140 
    141 		if (pbnum == 0)
    142 			break;
    143 
    144 		/*
    145 		 * This host bridge has a second PCI bus.
    146 		 * Configure it.
    147 		 */
    148 		switch (PCI_PRODUCT(pa->pa_id)) {
    149 		case PCI_PRODUCT_SERVERWORKS_CSB5:
    150 		case PCI_PRODUCT_SERVERWORKS_CSB6:
    151 			/* These devices show up as host bridges, but are
    152 			   really southbridges. */
    153 			break;
    154 		case PCI_PRODUCT_SERVERWORKS_CMIC_HE:
    155 		case PCI_PRODUCT_SERVERWORKS_CMIC_LE:
    156 		case PCI_PRODUCT_SERVERWORKS_CMIC_SL:
    157 			/* CNBs and CIOBs are connected to these using a
    158 			   private bus.  The bus number register is that of
    159 			   the first PCI bus hanging off the CIOB.  We let
    160 			   the CIOB attachment handle configuring the PCI
    161 			   buses. */
    162 			break;
    163 		default:
    164 			aprint_error("%s: unknown ServerWorks chip ID "
    165 			    "0x%04x; trying to attach PCI buses behind it\n",
    166 			    self->dv_xname, PCI_PRODUCT(pa->pa_id));
    167 			/* FALLTHROUGH */
    168 		case PCI_PRODUCT_SERVERWORKS_CNB20_LE_AGP:
    169 		case PCI_PRODUCT_SERVERWORKS_CNB30_LE_PCI:
    170 		case PCI_PRODUCT_SERVERWORKS_CNB20_LE_PCI:
    171 		case PCI_PRODUCT_SERVERWORKS_CNB20_HE_PCI:
    172 		case PCI_PRODUCT_SERVERWORKS_CNB20_HE_AGP:
    173 		case PCI_PRODUCT_SERVERWORKS_CIOB_X:
    174 		case PCI_PRODUCT_SERVERWORKS_CNB30_HE:
    175 		case PCI_PRODUCT_SERVERWORKS_CNB20_HE_PCI2:
    176 		case PCI_PRODUCT_SERVERWORKS_CIOB_X2:
    177 		case PCI_PRODUCT_SERVERWORKS_CIOB_E:
    178 			switch (attachflags &
    179 			    (PCI_FLAGS_IO_ENABLED | PCI_FLAGS_MEM_ENABLED)) {
    180 			case 0:
    181 				/* Doesn't smell like there's anything there. */
    182 				break;
    183 			case PCI_FLAGS_MEM_ENABLED:
    184 				attachflags |= PCI_FLAGS_IO_ENABLED;
    185 				/* FALLTHROUGH */
    186 			default:
    187 				doattach = 1;
    188 				break;
    189 			}
    190 			break;
    191 		}
    192 		break;
    193 	case PCI_VENDOR_INTEL:
    194 		switch (PCI_PRODUCT(pa->pa_id)) {
    195 		case PCI_PRODUCT_INTEL_82452_PB:
    196 			bcreg = pci_conf_read(pa->pa_pc, pa->pa_tag, 0x40);
    197 			pbnum = PCISET_BRIDGE_NUMBER(bcreg);
    198 			if (pbnum != 0xff) {
    199 				pbnum++;
    200 				doattach = 1;
    201 			}
    202 			break;
    203 		case PCI_PRODUCT_INTEL_82443BX_AGP:
    204 		case PCI_PRODUCT_INTEL_82443BX_NOAGP:
    205 		/*
    206 		 * http://www.intel.com/design/chipsets/specupdt/290639.htm
    207 		 * says this bug is fixed in steppings >= C0 (erratum 11),
    208 		 * so don't tweak the bits in that case.
    209 		 */
    210 			if (!(PCI_REVISION(pa->pa_class) >= 0x03)) {
    211 				/*
    212 				 * BIOS BUG WORKAROUND!  The 82443BX
    213 				 * datasheet indicates that the only
    214 				 * legal setting for the "Idle/Pipeline
    215 				 * DRAM Leadoff Timing (IPLDT)" parameter
    216 				 * (bits 9:8) is 01.  Unfortunately, some
    217 				 * BIOSs do not set these bits properly.
    218 				 */
    219 				bcreg = pci_conf_read(pa->pa_pc, pa->pa_tag,
    220 				    I82443BX_SDRAMC_REG);
    221 				if ((bcreg & 0x03000000) != 0x01000000) {
    222 					aprint_verbose("%s: fixing "
    223 					    "Idle/Pipeline DRAM "
    224 					    "Leadoff Timing\n", self->dv_xname);
    225 					bcreg &= ~0x03000000;
    226 					bcreg |=  0x01000000;
    227 					pci_conf_write(pa->pa_pc, pa->pa_tag,
    228 					    I82443BX_SDRAMC_REG, bcreg);
    229 				}
    230 			}
    231 			break;
    232 
    233 		case PCI_PRODUCT_INTEL_PCI450_PB:
    234 			bcreg = pci_conf_read(pa->pa_pc, pa->pa_tag,
    235 					      PCISET_BUSCONFIG_REG);
    236 			bdnum = PCISET_BRIDGE_NUMBER(bcreg);
    237 			pbnum = PCISET_PCI_BUS_NUMBER(bcreg);
    238 			switch (bdnum & PCISET_BRIDGETYPE_MASK) {
    239 			default:
    240 				aprint_error("%s: bdnum=%x (reserved)\n",
    241 				       self->dv_xname, bdnum);
    242 				break;
    243 			case PCISET_TYPE_COMPAT:
    244 				aprint_verbose(
    245 				    "%s: Compatibility PB (bus %d)\n",
    246 				    self->dv_xname, pbnum);
    247 				break;
    248 			case PCISET_TYPE_AUX:
    249 				aprint_verbose("%s: Auxiliary PB (bus %d)\n",
    250 				       self->dv_xname, pbnum);
    251 				/*
    252 				 * This host bridge has a second PCI bus.
    253 				 * Configure it.
    254 				 */
    255 				doattach = 1;
    256 				break;
    257 			}
    258 			break;
    259 		case PCI_PRODUCT_INTEL_CDC:
    260 			bcreg = pci_conf_read(pa->pa_pc, pa->pa_tag,
    261 					      I82424_CPU_BCTL_REG);
    262 			if (bcreg & I82424_BCTL_CPUPCI_POSTEN) {
    263 				bcreg &= ~I82424_BCTL_CPUPCI_POSTEN;
    264 				pci_conf_write(pa->pa_pc, pa->pa_tag,
    265 					       I82424_CPU_BCTL_REG, bcreg);
    266 				aprint_verbose(
    267 				    "%s: disabled CPU-PCI write posting\n",
    268 				    self->dv_xname);
    269 			}
    270 			break;
    271 		case PCI_PRODUCT_INTEL_82451NX_PXB:
    272 			/*
    273 			 * The NX chipset supports up to 2 "PXB" chips
    274 			 * which can drive 2 PCI buses each. Each bus
    275 			 * shows up as logical PCI device, with fixed
    276 			 * device numbers between 18 and 21.
    277 			 * See the datasheet at
    278 		ftp://download.intel.com/design/chipsets/datashts/24377102.pdf
    279 			 * for details.
    280 			 * (It would be easier to attach all the buses
    281 			 * at the MIOC, but less aesthetical imho.)
    282 			 */
    283 			if ((attachflags &
    284 			    (PCI_FLAGS_IO_ENABLED | PCI_FLAGS_MEM_ENABLED)) ==
    285 			    PCI_FLAGS_MEM_ENABLED)
    286 				attachflags |= PCI_FLAGS_IO_ENABLED;
    287 
    288 			pbnum = 0;
    289 			switch (pa->pa_device) {
    290 			case 18: /* PXB 0 bus A - primary bus */
    291 				break;
    292 			case 19: /* PXB 0 bus B */
    293 				/* read SUBA0 from MIOC */
    294 				tag = pci_make_tag(pa->pa_pc, 0, 16, 0);
    295 				bcreg = pci_conf_read(pa->pa_pc, tag, 0xd0);
    296 				pbnum = ((bcreg & 0x0000ff00) >> 8) + 1;
    297 				break;
    298 			case 20: /* PXB 1 bus A */
    299 				/* read BUSNO1 from MIOC */
    300 				tag = pci_make_tag(pa->pa_pc, 0, 16, 0);
    301 				bcreg = pci_conf_read(pa->pa_pc, tag, 0xd0);
    302 				pbnum = (bcreg & 0xff000000) >> 24;
    303 				break;
    304 			case 21: /* PXB 1 bus B */
    305 				/* read SUBA1 from MIOC */
    306 				tag = pci_make_tag(pa->pa_pc, 0, 16, 0);
    307 				bcreg = pci_conf_read(pa->pa_pc, tag, 0xd4);
    308 				pbnum = (bcreg & 0x000000ff) + 1;
    309 				break;
    310 			}
    311 			if (pbnum != 0)
    312 				doattach = 1;
    313 			break;
    314 
    315 		/*
    316 		 * i386 and amd64 stuff.
    317 		 */
    318 		case PCI_PRODUCT_INTEL_82810_MCH:
    319 		case PCI_PRODUCT_INTEL_82810_DC100_MCH:
    320 		case PCI_PRODUCT_INTEL_82810E_MCH:
    321 		case PCI_PRODUCT_INTEL_82815_FULL_HUB:
    322 		case PCI_PRODUCT_INTEL_82830MP_IO_1:
    323 		case PCI_PRODUCT_INTEL_82845G_DRAM:
    324 		case PCI_PRODUCT_INTEL_82855GM_MCH:
    325 		case PCI_PRODUCT_INTEL_82865_HB:
    326 		case PCI_PRODUCT_INTEL_82915G_HB:
    327 		case PCI_PRODUCT_INTEL_82915GM_HB:
    328 		case PCI_PRODUCT_INTEL_82945P_MCH:
    329 		case PCI_PRODUCT_INTEL_82945GM_HB:
    330 		case PCI_PRODUCT_INTEL_82965Q_HB:
    331 		case PCI_PRODUCT_INTEL_82965G_HB:
    332 		case PCI_PRODUCT_INTEL_82965PM_HB:
    333 		case PCI_PRODUCT_INTEL_82Q35_HB:
    334 		case PCI_PRODUCT_INTEL_82G33_HB:
    335 		case PCI_PRODUCT_INTEL_82Q33_HB:
    336 			/*
    337 			 * The host bridge is either in GFX mode (internal
    338 			 * graphics) or in AGP mode. In GFX mode, we pretend
    339 			 * to have AGP because the graphics memory access
    340 			 * is very similar and the AGP GATT code will
    341 			 * deal with this. In the latter case, the
    342 			 * pci_get_capability(PCI_CAP_AGP) test below will
    343 			 * fire, so we do no harm by already setting the flag.
    344 			 */
    345 			has_agp = 1;
    346 			break;
    347 		}
    348 		break;
    349 	}
    350 
    351 #if NRND > 0
    352 	/*
    353 	 * Attach a random number generator, if there is one.
    354 	 */
    355 	pchb_attach_rnd(sc, pa);
    356 #endif
    357 
    358 	if (!pmf_device_register(self, pchb_suspend, pchb_resume))
    359 		aprint_error_dev(self, "couldn't establish power handler\n");
    360 
    361 	/*
    362 	 * If we haven't detected AGP yet (via a product ID),
    363 	 * then check for AGP capability on the device.
    364 	 */
    365 	if (has_agp ||
    366 	    pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_AGP,
    367 			       NULL, NULL) != 0) {
    368 		apa.apa_pci_args = *pa;
    369 		config_found_ia(self, "agpbus", &apa, agpbusprint);
    370 	}
    371 
    372 	if (doattach) {
    373 		pba.pba_iot = pa->pa_iot;
    374 		pba.pba_memt = pa->pa_memt;
    375 		pba.pba_dmat = pa->pa_dmat;
    376 		pba.pba_dmat64 = pa->pa_dmat64;
    377 		pba.pba_pc = pa->pa_pc;
    378 		pba.pba_flags = attachflags;
    379 		pba.pba_bus = pbnum;
    380 		pba.pba_bridgetag = NULL;
    381 		pba.pba_pc = pa->pa_pc;
    382 		pba.pba_intrswiz = 0;
    383 		memset(&pba.pba_intrtag, 0, sizeof(pba.pba_intrtag));
    384 		config_found_ia(self, "pcibus", &pba, pcibusprint);
    385 	}
    386 }
    387 
    388 int
    389 pchbdetach(device_t self, int flags)
    390 {
    391 	int rc;
    392 #if NRND > 0
    393 	struct pchb_softc *sc = device_private(self);
    394 #endif
    395 
    396 	if ((rc = config_detach_children(self, flags)) != 0)
    397 		return rc;
    398 
    399 	pmf_device_deregister(self);
    400 
    401 #if NRND > 0
    402 	/*
    403 	 * Attach a random number generator, if there is one.
    404 	 */
    405 	pchb_detach_rnd(sc);
    406 #endif
    407 	return 0;
    408 }
    409 
    410 static bool
    411 pchb_suspend(device_t dv)
    412 {
    413 	struct pchb_softc *sc = device_private(dv);
    414 	pci_chipset_tag_t pc;
    415 	pcitag_t tag;
    416 	int off;
    417 
    418 	pc = sc->sc_pc;
    419 	tag = sc->sc_tag;
    420 
    421 	for (off = 0x40; off <= 0xff; off += 4)
    422 		sc->sc_pciconfext[(off - 0x40) / 4] = pci_conf_read(pc, tag, off);
    423 
    424 	return true;
    425 }
    426 
    427 static bool
    428 pchb_resume(device_t dv)
    429 {
    430 	struct pchb_softc *sc = device_private(dv);
    431 	pci_chipset_tag_t pc;
    432 	pcitag_t tag;
    433 	int off;
    434 
    435 	pc = sc->sc_pc;
    436 	tag = sc->sc_tag;
    437 
    438 	for (off = 0x40; off <= 0xff; off += 4)
    439 		pci_conf_write(pc, tag, off, sc->sc_pciconfext[(off - 0x40) / 4]);
    440 
    441 	return true;
    442 }
    443