Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 06 Sep 2009 23:36:26 +0300
From:      Andriy Gapon <avg@freebsd.org>
To:        freebsd-acpi@freebsd.org
Subject:   intpm: add support for AMD SBxxx SMBus controller
Message-ID:  <4AA41D4A.4080805@freebsd.org>

next in thread | raw e-mail | index | archive | help

Please review the included patch that adds support from SMBus controller found
in AMD SB600/700/710/750 south-bridges (not sure about SB800).
As I understand, this controller works only in polling mode, so support for this
mode was enabled in the code.

There are two places that I was not sure about, so I marked them with XXX.
The static variable intsmb_cfg_irq9 would be problematic if there are multiple
SMBus controllers in a system.

Also, PCI_INTR_SMB_IRQ_AMD is probably not the best name. Maybe something like
PCI_INTR_SMB_IRQ_OTHER or just PCI_INTR_SMB_IRQ would be better?

Almost forgot: bogus check for PIIX4_SMBHSTSTAT_INTR bit in polling mode was
removed.

diff --git a/sys/pci/intpm.c b/sys/pci/intpm.c
index 63eb4c4..894ed02 100644
--- a/sys/pci/intpm.c
+++ b/sys/pci/intpm.c
@@ -53,6 +53,7 @@ struct intsmb_softc {
 	void			*irq_hand;
 	device_t		smbus;
 	int			isbusy;
+	int			poll;
 	struct mtx		lock;
 };

@@ -83,6 +84,9 @@ static int intsmb_stop_poll(struct intsmb_softc *sc);
 static int intsmb_free(struct intsmb_softc *sc);
 static void intsmb_rawintr(void *arg);

+/* XXX Is there a better way than a static variable? */
+static int intsmb_cfg_irq9 = 0;
+
 static int
 intsmb_probe(device_t dev)
 {
@@ -95,6 +99,15 @@ intsmb_probe(device_t dev)
 	case 0x02001166:	/* ServerWorks OSB4 */
 #endif
 		device_set_desc(dev, "Intel PIIX4 SMBUS Interface");
+#ifndef NO_CHANGE_PCICONF
+		/* Changing configuration is not allowed. */
+		intsmb_cfg_irq9 = 1;
+#endif
+		break;
+	case 0x43851002:
+		device_set_desc(dev, "AMD SB600/700/710/750 SMBus Controller");
+		/* XXX Maybe force polling right here? */
+		intsmb_cfg_irq9 = 0;
 		break;
 	default:
 		return (ENXIO);
@@ -108,6 +121,7 @@ intsmb_attach(device_t dev)
 {
 	struct intsmb_softc *sc = device_get_softc(dev);
 	int error, rid, value;
+	int intr;
 	char *str;

 	sc->dev = dev;
@@ -123,27 +137,36 @@ intsmb_attach(device_t dev)
 		goto fail;
 	}

-#ifndef NO_CHANGE_PCICONF
-	pci_write_config(dev, PCIR_INTLINE, 0x9, 1);
-	pci_write_config(dev, PCI_HST_CFG_SMB,
-	    PCI_INTR_SMB_IRQ9 | PCI_INTR_SMB_ENABLE, 1);
-#endif
+	if (intsmb_cfg_irq9) {
+		pci_write_config(dev, PCIR_INTLINE, 0x9, 1);
+		pci_write_config(dev, PCI_HST_CFG_SMB,
+		    PCI_INTR_SMB_IRQ9 | PCI_INTR_SMB_ENABLE, 1);
+	}
 	value = pci_read_config(dev, PCI_HST_CFG_SMB, 1);
-	switch (value & 0xe) {
+	sc->poll = value & PCI_INTR_SMB_ENABLE;
+	intr = value & PCI_INTR_SMB_MASK;
+	switch (intr) {
 	case PCI_INTR_SMB_SMI:
 		str = "SMI";
 		break;
 	case PCI_INTR_SMB_IRQ9:
 		str = "IRQ 9";
 		break;
+	case PCI_INTR_SMB_IRQ_AMD:
+		str = "IRQ";
+		break;
 	default:
 		str = "BOGUS";
 	}
+
 	device_printf(dev, "intr %s %s ", str,
-	    (value & 1) ? "enabled" : "disabled");
+	    sc->poll == 0 ? "enabled" : "disabled");
 	printf("revision %d\n", pci_read_config(dev, PCI_REVID_SMB, 1));

-	if ((value & 0xe) != PCI_INTR_SMB_IRQ9) {
+	if (sc->poll)
+	    goto no_intr;
+
+	if (intr != PCI_INTR_SMB_IRQ9 && intr != PCI_INTR_SMB_IRQ_AMD) {
 		device_printf(dev, "Unsupported interrupt mode\n");
 		error = ENXIO;
 		goto fail;
@@ -151,7 +174,9 @@ intsmb_attach(device_t dev)

 	/* Force IRQ 9. */
 	rid = 0;
-	bus_set_resource(dev, SYS_RES_IRQ, rid, 9, 1);
+	if (intsmb_cfg_irq9)
+		bus_set_resource(dev, SYS_RES_IRQ, rid, 9, 1);
+
 	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
 	    RF_SHAREABLE | RF_ACTIVE);
 	if (sc->irq_res == NULL) {
@@ -167,6 +192,7 @@ intsmb_attach(device_t dev)
 		goto fail;
 	}

+no_intr:
 	sc->isbusy = 0;
 	sc->smbus = device_add_child(dev, "smbus", -1);
 	if (sc->smbus == NULL) {
@@ -361,7 +387,7 @@ intsmb_start(struct intsmb_softc *sc, unsigned char cmd, int
nointr)
 	tmp |= PIIX4_SMBHSTCNT_START;

 	/* While not in autoconfiguration enable interrupts. */
-	if (!cold && !nointr)
+	if (!sc->poll && !cold && !nointr)
 		tmp |= PIIX4_SMBHSTCNT_INTREN;
 	bus_write_1(sc->io_res, PIIX4_SMBHSTCNT, tmp);
 }
@@ -411,8 +437,6 @@ intsmb_stop_poll(struct intsmb_softc *sc)
 		if (!(status & PIIX4_SMBHSTSTAT_BUSY)) {
 			sc->isbusy = 0;
 			error = intsmb_error(sc->dev, status);
-			if (error == 0 && !(status & PIIX4_SMBHSTSTAT_INTR))
-				device_printf(sc->dev, "unknown cause why?\n");
 			return (error);
 		}
 	}
@@ -434,7 +458,7 @@ intsmb_stop(struct intsmb_softc *sc)

 	INTSMB_LOCK_ASSERT(sc);

-	if (cold)
+	if (sc->poll || cold)
 		/* So that it can use device during device probe on SMBus. */
 		return (intsmb_stop_poll(sc));

diff --git a/sys/pci/intpmreg.h b/sys/pci/intpmreg.h
index 236c737..4a3e599 100644
--- a/sys/pci/intpmreg.h
+++ b/sys/pci/intpmreg.h
@@ -35,7 +35,9 @@
 #define	PCI_BASE_ADDR_SMB	0x90	/* IO BAR. */
 #define	PCI_BASE_ADDR_PM	0x40
 #define	PCI_HST_CFG_SMB		0xd2	/* Host Configuration */
+#define	PCI_INTR_SMB_MASK	0xe
 #define	PCI_INTR_SMB_SMI	0
+#define	PCI_INTR_SMB_IRQ_AMD	2
 #define	PCI_INTR_SMB_IRQ9	8
 #define	PCI_INTR_SMB_ENABLE	1
 #define	PCI_SLV_CMD_SMB		0xd3 /*SLAVE COMMAND*/

-- 
Andriy Gapon



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?4AA41D4A.4080805>