intpm: add support for AMD SBxxx SMBus controller
Andriy Gapon
avg at freebsd.org
Sun Sep 6 20:36:30 UTC 2009
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
More information about the freebsd-acpi
mailing list