Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 23 Aug 2016 10:40:53 +0000 (UTC)
From:      Andriy Gapon <avg@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r304674 - head/sys/dev/intpm
Message-ID:  <201608231040.u7NAerJt019297@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: avg
Date: Tue Aug 23 10:40:53 2016
New Revision: 304674
URL: https://svnweb.freebsd.org/changeset/base/304674

Log:
  intpm: add support for SB800
  
  This code should be able to support later AMD chipsets as well, but that
  hasn't been tested.
  
  SB800 supports accessing several different SMBus buses using the same
  set of constrol registeirs plus special PMIO registers that control which
  bus is selected.  This could be exposed to consumers as several smb devices
  each talking to its bus.  This feature is not implemented yet.
  
  MFC after:	2 weeks

Modified:
  head/sys/dev/intpm/intpm.c

Modified: head/sys/dev/intpm/intpm.c
==============================================================================
--- head/sys/dev/intpm/intpm.c	Tue Aug 23 08:13:08 2016	(r304673)
+++ head/sys/dev/intpm/intpm.c	Tue Aug 23 10:40:53 2016	(r304674)
@@ -52,8 +52,10 @@ struct intsmb_softc {
 	struct resource		*irq_res;
 	void			*irq_hand;
 	device_t		smbus;
+	int			io_rid;
 	int			isbusy;
 	int			cfg_irq9;
+	int			sb8xx;
 	int			poll;
 	struct mtx		lock;
 };
@@ -102,10 +104,8 @@ intsmb_probe(device_t dev)
 		device_set_desc(dev, "ATI IXP400 SMBus Controller");
 		break;
 	case 0x43851002:
-		/* SB800 and newer can not be configured in a compatible way. */
-		if (pci_get_revid(dev) >= 0x40)
-			return (ENXIO);
-		device_set_desc(dev, "AMD SB600/700/710/750 SMBus Controller");
+	case 0x780b1022:	/* AMD Hudson */
+		device_set_desc(dev, "AMD SB600/7xx/8xx SMBus Controller");
 		/* XXX Maybe force polling right here? */
 		break;
 	default:
@@ -115,6 +115,87 @@ intsmb_probe(device_t dev)
 	return (BUS_PROBE_DEFAULT);
 }
 
+static uint8_t
+sb8xx_pmio_read(struct resource *res, uint8_t reg)
+{
+	bus_write_1(res, 0, reg);	/* Index */
+	return (bus_read_1(res, 1));	/* Data */
+}
+
+static int
+sb8xx_attach(device_t dev)
+{
+	static const int	AMDSB_PMIO_INDEX = 0xcd6;
+	static const int	AMDSB_PMIO_WIDTH = 2;
+	static const int	AMDSB8_SMBUS_ADDR = 0x2c;
+	static const int		AMDSB8_SMBUS_EN = 0x01;
+	static const int		AMDSB8_SMBUS_ADDR_MASK = ~0x1fu;
+	static const int	AMDSB_SMBIO_WIDTH = 0x14;
+	static const int	AMDSB_SMBUS_CFG = 0x10;
+	static const int		AMDSB_SMBUS_IRQ = 0x01;
+	static const int		AMDSB_SMBUS_REV_MASK = ~0x0fu;
+	static const int		AMDSB_SMBUS_REV_SHIFT = 4;
+	static const int	AMDSB_IO_RID = 0;
+
+	struct intsmb_softc	*sc;
+	struct resource		*res;
+	uint16_t		addr;
+	uint8_t			cfg;
+	int			rid;
+	int			rc;
+
+	sc = device_get_softc(dev);
+	rid = AMDSB_IO_RID;
+	rc = bus_set_resource(dev, SYS_RES_IOPORT, rid, AMDSB_PMIO_INDEX,
+	    AMDSB_PMIO_WIDTH);
+	if (rc != 0) {
+		device_printf(dev, "bus_set_resource for PM IO failed\n");
+		return (ENXIO);
+	}
+	res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
+	    RF_ACTIVE | RF_SHAREABLE);
+	if (res == NULL) {
+		device_printf(dev, "bus_alloc_resource for PM IO failed\n");
+		return (ENXIO);
+	}
+
+	addr = sb8xx_pmio_read(res, AMDSB8_SMBUS_ADDR + 1);
+	addr <<= 8;
+	addr |= sb8xx_pmio_read(res, AMDSB8_SMBUS_ADDR);
+
+	bus_release_resource(dev, SYS_RES_IOPORT, rid, res);
+	bus_delete_resource(dev, SYS_RES_IOPORT, rid);
+
+	if ((addr & AMDSB8_SMBUS_EN) == 0) {
+		device_printf(dev, "SB8xx SMBus not enabled\n");
+		return (ENXIO);
+	}
+
+	addr &= AMDSB8_SMBUS_ADDR_MASK;
+	sc->io_rid = AMDSB_IO_RID;
+	rc = bus_set_resource(dev, SYS_RES_IOPORT, sc->io_rid, addr,
+	    AMDSB_SMBIO_WIDTH);
+	if (rc != 0) {
+		device_printf(dev, "bus_set_resource for SMBus IO failed\n");
+		return (ENXIO);
+	}
+	if (res == NULL) {
+		device_printf(dev, "bus_alloc_resource for SMBus IO failed\n");
+		return (ENXIO);
+	}
+	sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->io_rid,
+	    RF_ACTIVE | RF_SHAREABLE);
+	cfg = bus_read_1(sc->io_res, AMDSB_SMBUS_CFG);
+
+	sc->poll = 1;
+	device_printf(dev, "intr %s disabled ",
+	    (cfg & AMDSB_SMBUS_IRQ) != 0 ? "IRQ" : "SMI");
+	printf("revision %d\n",
+	    (cfg & AMDSB_SMBUS_REV_MASK) >> AMDSB_SMBUS_REV_SHIFT);
+
+	return (0);
+}
+
 static int
 intsmb_attach(device_t dev)
 {
@@ -128,18 +209,31 @@ intsmb_attach(device_t dev)
 	mtx_init(&sc->lock, device_get_nameunit(dev), "intsmb", MTX_DEF);
 
 	sc->cfg_irq9 = 0;
-#ifndef NO_CHANGE_PCICONF
 	switch (pci_get_devid(dev)) {
+#ifndef NO_CHANGE_PCICONF
 	case 0x71138086:	/* Intel 82371AB */
 	case 0x719b8086:	/* Intel 82443MX */
 		/* Changing configuration is allowed. */
 		sc->cfg_irq9 = 1;
 		break;
-	}
 #endif
+	case 0x43851002:
+	case 0x780b1022:
+		if (pci_get_revid(dev) >= 0x40)
+			sc->sb8xx = 1;
+		break;
+	}
+
+	if (sc->sb8xx) {
+		error = sb8xx_attach(dev);
+		if (error != 0)
+			goto fail;
+		else
+			goto no_intr;
+	}
 
-	rid = PCI_BASE_ADDR_SMB;
-	sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
+	sc->io_rid = PCI_BASE_ADDR_SMB;
+	sc->io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->io_rid,
 	    RF_ACTIVE);
 	if (sc->io_res == NULL) {
 		device_printf(dev, "Could not allocate I/O space\n");
@@ -247,7 +341,7 @@ intsmb_detach(device_t dev)
 	if (sc->irq_res)
 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
 	if (sc->io_res)
-		bus_release_resource(dev, SYS_RES_IOPORT, PCI_BASE_ADDR_SMB,
+		bus_release_resource(dev, SYS_RES_IOPORT, sc->io_rid,
 		    sc->io_res);
 	mtx_destroy(&sc->lock);
 	return (0);



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