Module Name: src Committed By: jmcneill Date: Sun Oct 2 23:25:20 UTC 2011
Modified Files: src/sys/dev/pci: piixpm.c Log Message: Newer ATI SB800 SMBus controllers don't report the base address in PCI config space. According to the SB800-Series Southbridges Register Reference Guide, we can still read this value from PM config space using indirect I/O. To generate a diff of this commit: cvs rdiff -u -r1.35 -r1.36 src/sys/dev/pci/piixpm.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/pci/piixpm.c diff -u src/sys/dev/pci/piixpm.c:1.35 src/sys/dev/pci/piixpm.c:1.36 --- src/sys/dev/pci/piixpm.c:1.35 Sun Feb 13 11:20:12 2011 +++ src/sys/dev/pci/piixpm.c Sun Oct 2 23:25:20 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: piixpm.c,v 1.35 2011/02/13 11:20:12 hannken Exp $ */ +/* $NetBSD: piixpm.c,v 1.36 2011/10/02 23:25:20 jmcneill Exp $ */ /* $OpenBSD: piixpm.c,v 1.20 2006/02/27 08:25:02 grange Exp $ */ /* @@ -22,7 +22,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: piixpm.c,v 1.35 2011/02/13 11:20:12 hannken Exp $"); +__KERNEL_RCSID(0, "$NetBSD: piixpm.c,v 1.36 2011/10/02 23:25:20 jmcneill Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -55,6 +55,17 @@ __KERNEL_RCSID(0, "$NetBSD: piixpm.c,v 1 #define PIIXPM_DELAY 200 #define PIIXPM_TIMEOUT 1 +#define PIIXPM_INDIRECTIO_BASE 0xcd6 +#define PIIXPM_INDIRECTIO_SIZE 2 +#define PIIXPM_INDIRECTIO_INDEX 0 +#define PIIXPM_INDIRECTIO_DATA 1 + +#define SB800_PM_SMBUS0EN_LO 0x2c +#define SB800_PM_SMBUS0EN_HI 0x2d + +#define SB800_PM_SMBUS0EN_ENABLE 0x0001 +#define SB800_PM_SMBUS0EN_BADDR 0xffe0 + struct piixpm_softc { device_t sc_dev; @@ -89,6 +100,8 @@ static void piixpm_attach(device_t, devi static bool piixpm_suspend(device_t, const pmf_qual_t *); static bool piixpm_resume(device_t, const pmf_qual_t *); +static int piixpm_sb800_init(struct piixpm_softc *, + struct pci_attach_args *); static void piixpm_csb5_reset(void *); static int piixpm_i2c_acquire_bus(void *, int); static void piixpm_i2c_release_bus(void *, int); @@ -195,6 +208,17 @@ piixpm_attach(device_t parent, device_t (PCI_REVISION(pa->pa_class) < 3) ? ACPIPMT_BADLATCH : 0 ); nopowermanagement: + + /* SB800 rev 0x40+ needs special initialization */ + if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ATI && + PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ATI_SB600_SMB && + PCI_REVISION(pa->pa_class) >= 0x40) { + if (piixpm_sb800_init(sc, pa) == 0) + goto attach_i2c; + aprint_normal_dev(self, "SMBus disabled\n"); + return; + } + if ((conf & PIIX_SMB_HOSTC_HSTEN) == 0) { aprint_normal_dev(self, "SMBus disabled\n"); return; @@ -229,6 +253,7 @@ nopowermanagement: if (sc->sc_poll) aprint_normal("polling"); +attach_i2c: aprint_normal("\n"); /* Attach I2C bus */ @@ -272,6 +297,51 @@ piixpm_resume(device_t dv, const pmf_qua return true; } +/* + * Extract SMBus base address from SB800 Power Management (PM) registers. + * The PM registers can be accessed either through indirect I/O (CD6/CD7) or + * direct mapping if AcpiMMioDecodeEn is enabled. Since this function is only + * called once it uses indirect I/O for simplicity. + */ +static int +piixpm_sb800_init(struct piixpm_softc *sc, struct pci_attach_args *pa) +{ + bus_space_tag_t iot = pa->pa_iot; + bus_space_handle_t ioh; /* indirect I/O handle */ + uint16_t val, base_addr; + + /* Fetch SMB base address */ + if (bus_space_map(iot, + PIIXPM_INDIRECTIO_BASE, PIIXPM_INDIRECTIO_SIZE, 0, &ioh)) { + device_printf(sc->sc_dev, "couldn't map indirect I/O space\n"); + return EBUSY; + } + bus_space_write_1(iot, ioh, PIIXPM_INDIRECTIO_INDEX, + SB800_PM_SMBUS0EN_LO); + val = bus_space_read_1(iot, ioh, PIIXPM_INDIRECTIO_DATA); + bus_space_write_1(iot, ioh, PIIXPM_INDIRECTIO_INDEX, + SB800_PM_SMBUS0EN_HI); + val |= bus_space_read_1(iot, ioh, PIIXPM_INDIRECTIO_DATA) << 8; + bus_space_unmap(iot, ioh, 2); + + if ((val & SB800_PM_SMBUS0EN_ENABLE) == 0) + return ENOENT; + + base_addr = val & SB800_PM_SMBUS0EN_BADDR; + + aprint_debug_dev(sc->sc_dev, "SMBus @ 0x%04x\n", base_addr); + + sc->sc_smb_iot = iot; + if (bus_space_map(sc->sc_smb_iot, PCI_MAPREG_IO_ADDR(base_addr), + PIIX_SMB_SIZE, 0, &sc->sc_smb_ioh)) { + aprint_error_dev(sc->sc_dev, "can't map smbus I/O space\n"); + return EBUSY; + } + sc->sc_poll = 1; + + return 0; +} + static void piixpm_csb5_reset(void *arg) {