Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 5 Jun 2010 17:58:10 +0000 (UTC)
From:      Nathan Whitehorn <nwhitehorn@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r208844 - in projects/ppc64/sys: conf dev/iicbus powerpc/powermac
Message-ID:  <201006051758.o55HwAUx095327@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: nwhitehorn
Date: Sat Jun  5 17:58:10 2010
New Revision: 208844
URL: http://svn.freebsd.org/changeset/base/208844

Log:
  IFC @ 208843

Added:
  projects/ppc64/sys/powerpc/powermac/smusat.c
     - copied unchanged from r208843, head/sys/powerpc/powermac/smusat.c
Modified:
  projects/ppc64/sys/conf/files.powerpc
  projects/ppc64/sys/dev/iicbus/iic.h
  projects/ppc64/sys/powerpc/powermac/kiic.c
  projects/ppc64/sys/powerpc/powermac/smu.c
Directory Properties:
  projects/ppc64/   (props changed)
  projects/ppc64/cddl/contrib/opensolaris/   (props changed)
  projects/ppc64/contrib/ee/   (props changed)
  projects/ppc64/contrib/expat/   (props changed)
  projects/ppc64/contrib/file/   (props changed)
  projects/ppc64/contrib/gdb/   (props changed)
  projects/ppc64/contrib/gnu-sort/   (props changed)
  projects/ppc64/contrib/groff/   (props changed)
  projects/ppc64/contrib/less/   (props changed)
  projects/ppc64/contrib/libpcap/   (props changed)
  projects/ppc64/contrib/ncurses/   (props changed)
  projects/ppc64/contrib/one-true-awk/   (props changed)
  projects/ppc64/contrib/openbsm/   (props changed)
  projects/ppc64/contrib/openpam/   (props changed)
  projects/ppc64/contrib/pf/   (props changed)
  projects/ppc64/contrib/tcpdump/   (props changed)
  projects/ppc64/contrib/tcsh/   (props changed)
  projects/ppc64/contrib/tzcode/stdtime/   (props changed)
  projects/ppc64/contrib/tzcode/zic/   (props changed)
  projects/ppc64/contrib/tzdata/   (props changed)
  projects/ppc64/contrib/wpa/   (props changed)
  projects/ppc64/lib/libutil/   (props changed)
  projects/ppc64/lib/libz/   (props changed)
  projects/ppc64/sbin/   (props changed)
  projects/ppc64/sbin/ipfw/   (props changed)
  projects/ppc64/sys/   (props changed)
  projects/ppc64/sys/amd64/include/xen/   (props changed)
  projects/ppc64/sys/cddl/contrib/opensolaris/   (props changed)
  projects/ppc64/sys/contrib/dev/acpica/   (props changed)
  projects/ppc64/sys/contrib/x86emu/   (props changed)
  projects/ppc64/sys/dev/xen/xenpci/   (props changed)
  projects/ppc64/usr.bin/csup/   (props changed)
  projects/ppc64/usr.bin/procstat/   (props changed)

Modified: projects/ppc64/sys/conf/files.powerpc
==============================================================================
--- projects/ppc64/sys/conf/files.powerpc	Sat Jun  5 17:53:41 2010	(r208843)
+++ projects/ppc64/sys/conf/files.powerpc	Sat Jun  5 17:58:10 2010	(r208844)
@@ -139,6 +139,7 @@ powerpc/powermac/openpic_macio.c optiona
 powerpc/powermac/pswitch.c	optional	powermac pswitch
 powerpc/powermac/pmu.c		optional	powermac pmu 
 powerpc/powermac/smu.c		optional	powermac smu 
+powerpc/powermac/smusat.c	optional	powermac smu
 powerpc/powermac/uninorth.c	optional	powermac
 powerpc/powermac/uninorthpci.c	optional	powermac pci
 powerpc/powermac/vcoregpio.c	optional	powermac 

Modified: projects/ppc64/sys/dev/iicbus/iic.h
==============================================================================
--- projects/ppc64/sys/dev/iicbus/iic.h	Sat Jun  5 17:53:41 2010	(r208843)
+++ projects/ppc64/sys/dev/iicbus/iic.h	Sat Jun  5 17:58:10 2010	(r208844)
@@ -38,6 +38,8 @@ struct iic_msg
 	uint16_t	flags;
 #define	IIC_M_WR	0	/* Fake flag for write */
 #define	IIC_M_RD	0x0001	/* read vs write */
+#define	IIC_M_NOSTOP	0x0002	/* do not send a I2C stop after message */
+#define	IIC_M_NOSTART	0x0004	/* do not send a I2C start before message */
 	uint16_t	len;	/* msg legnth */
 	uint8_t *	buf;
 };

Modified: projects/ppc64/sys/powerpc/powermac/kiic.c
==============================================================================
--- projects/ppc64/sys/powerpc/powermac/kiic.c	Sat Jun  5 17:53:41 2010	(r208843)
+++ projects/ppc64/sys/powerpc/powermac/kiic.c	Sat Jun  5 17:58:10 2010	(r208844)
@@ -108,6 +108,7 @@ struct kiic_softc {
 	u_int 			 sc_flags;
 	u_char			*sc_data;
 	int 			 sc_resid;
+	uint16_t		 sc_i2c_base;
 	device_t 		 sc_iicbus;
 };
 
@@ -115,6 +116,7 @@ static int 	kiic_probe(device_t dev);
 static int 	kiic_attach(device_t dev);
 static void 	kiic_writereg(struct kiic_softc *sc, u_int, u_int);
 static u_int 	kiic_readreg(struct kiic_softc *, u_int);
+static void 	kiic_setport(struct kiic_softc *, u_int);
 static void 	kiic_setmode(struct kiic_softc *, u_int);
 static void 	kiic_setspeed(struct kiic_softc *, u_int);
 static void 	kiic_intr(void *xsc);
@@ -198,12 +200,25 @@ kiic_attach(device_t self)
 	 * underneath them.  Some have a single 'iicbus' child with the
 	 * devices underneath that.  Sort this out, and make sure that the
 	 * OFW I2C layer has the correct node.
+	 *
+	 * Note: the I2C children of the Uninorth bridges have two ports.
+	 *  In general, the port is designated in the 9th bit of the I2C
+	 *  address. However, for kiic devices with children attached below
+	 *  an i2c-bus node, the port is indicated in the 'reg' property
+	 *  of the i2c-bus node.
 	 */
 
-	sc->sc_node = OF_child(node);
-	if (OF_getprop(sc->sc_node,"name",name,sizeof(name)) > 0) {
-		if (strcmp(name,"i2c-bus") != 0)
+	sc->sc_node = node;
+
+	node = OF_child(node);
+	if (OF_getprop(node, "name", name, sizeof(name)) > 0) {
+		if (strcmp(name,"i2c-bus") == 0) {
+			phandle_t reg;
+			if (OF_getprop(node, "reg", &reg, sizeof(reg)) > 0)
+				sc->sc_i2c_base = reg << 8;
+
 			sc->sc_node = node;
+		}
 	}
 
 	mtx_init(&sc->sc_mutex, "kiic", NULL, MTX_DEF);
@@ -213,8 +228,8 @@ kiic_attach(device_t self)
 	bus_setup_intr(self, sc->sc_irq, INTR_TYPE_MISC | INTR_MPSAFE, NULL,
 	    kiic_intr, sc, &sc->sc_ih);
 
+	kiic_writereg(sc, ISR, kiic_readreg(sc, ISR));
 	kiic_writereg(sc, STATUS, 0);
-	kiic_writereg(sc, ISR, 0);
 	kiic_writereg(sc, IER, 0);
 
 	kiic_setmode(sc, I2C_STDMODE);
@@ -257,6 +272,18 @@ kiic_setmode(struct kiic_softc *sc, u_in
 }
 
 static void
+kiic_setport(struct kiic_softc *sc, u_int port)
+{
+	u_int x;
+
+	KASSERT(port == 1 || port == 0, ("bad port"));
+	x = kiic_readreg(sc, MODE);
+	x &= ~I2C_PORT;
+	x |= (port << 4);
+	kiic_writereg(sc, MODE, x);
+}
+
+static void
 kiic_setspeed(struct kiic_softc *sc, u_int speed)
 {
 	u_int x;
@@ -299,7 +326,8 @@ kiic_intr(void *xsc)
 				*sc->sc_data++ = kiic_readreg(sc, DATA);
 				sc->sc_resid--;
 			}
-
+			if (sc->sc_resid == 0)  /* done */
+				kiic_writereg(sc, CONTROL, 0);
 		} else {
 			if (sc->sc_resid == 0) {
 				x = kiic_readreg(sc, CONTROL);
@@ -327,10 +355,12 @@ kiic_transfer(device_t dev, struct iic_m
 {
 	struct kiic_softc *sc;
 	int i, x, timo, err;
-	uint8_t addr;
+	uint16_t addr;
+	uint8_t subaddr;
 
 	sc = device_get_softc(dev);
 	timo = 100;
+	subaddr = 0;
 
 	mtx_lock(&sc->sc_mutex);
 
@@ -344,7 +374,23 @@ kiic_transfer(device_t dev, struct iic_m
 		
 	sc->sc_flags = I2C_BUSY;
 
+	/* Clear pending interrupts, and reset controller */
+	kiic_writereg(sc, ISR, kiic_readreg(sc, ISR));
+	kiic_writereg(sc, STATUS, 0);
+
 	for (i = 0; i < nmsgs; i++) {
+		if (msgs[i].flags & IIC_M_NOSTOP) {
+			if (msgs[i+1].flags & IIC_M_RD)
+				kiic_setmode(sc, I2C_COMBMODE);
+			else
+				kiic_setmode(sc, I2C_STDSUBMODE);
+			KASSERT(msgs[i].len == 1, ("oversize I2C message"));
+			subaddr = msgs[i].buf[0];
+			i++;
+		} else {
+			kiic_setmode(sc, I2C_STDMODE);
+		}
+
 		sc->sc_data = msgs[i].buf;
 		sc->sc_resid = msgs[i].len;
 		sc->sc_flags = I2C_BUSY;
@@ -357,8 +403,11 @@ kiic_transfer(device_t dev, struct iic_m
 			addr |= 1;
 		}
 
-		kiic_writereg(sc, ADDR, addr);
-		kiic_writereg(sc, SUBADDR, 0x04);
+		addr |= sc->sc_i2c_base;
+
+		kiic_setport(sc, (addr & 0x100) >> 8);
+		kiic_writereg(sc, ADDR, addr & 0xff);
+		kiic_writereg(sc, SUBADDR, subaddr);
 
 		x = kiic_readreg(sc, CONTROL) | I2C_CT_ADDR;
 		kiic_writereg(sc, CONTROL, x);

Modified: projects/ppc64/sys/powerpc/powermac/smu.c
==============================================================================
--- projects/ppc64/sys/powerpc/powermac/smu.c	Sat Jun  5 17:53:41 2010	(r208843)
+++ projects/ppc64/sys/powerpc/powermac/smu.c	Sat Jun  5 17:58:10 2010	(r208844)
@@ -47,12 +47,16 @@ __FBSDID("$FreeBSD$");
 #include <machine/intr_machdep.h>
 #include <machine/md_var.h>
 
+#include <dev/iicbus/iicbus.h>
+#include <dev/iicbus/iiconf.h>
 #include <dev/led/led.h>
 #include <dev/ofw/openfirm.h>
 #include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
 #include <powerpc/powermac/macgpiovar.h>
 
 #include "clock_if.h"
+#include "iicbus_if.h"
 
 struct smu_cmd {
 	volatile uint8_t cmd;
@@ -137,6 +141,8 @@ struct smu_softc {
 
 static int	smu_probe(device_t);
 static int	smu_attach(device_t);
+static const struct ofw_bus_devinfo *
+    smu_get_devinfo(device_t bus, device_t dev);
 
 /* cpufreq notification hooks */
 
@@ -151,6 +157,7 @@ static int	smu_settime(device_t dev, str
 static int	smu_run_cmd(device_t dev, struct smu_cmd *cmd, int wait);
 static int	smu_get_datablock(device_t dev, int8_t id, uint8_t *buf,
 		    size_t len);
+static void	smu_attach_i2c(device_t dev, phandle_t i2croot);
 static void	smu_attach_fans(device_t dev, phandle_t fanroot);
 static void	smu_attach_sensors(device_t dev, phandle_t sensroot);
 static void	smu_fan_management_proc(void *xdev);
@@ -171,6 +178,16 @@ static device_method_t  smu_methods[] = 
 	/* Clock interface */
 	DEVMETHOD(clock_gettime,	smu_gettime),
 	DEVMETHOD(clock_settime,	smu_settime),
+
+	/* ofw_bus interface */
+	DEVMETHOD(bus_child_pnpinfo_str,ofw_bus_gen_child_pnpinfo_str),
+	DEVMETHOD(ofw_bus_get_devinfo,	smu_get_devinfo),
+	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
+	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
+	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
+	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
+	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
+
 	{ 0, 0 },
 };
 
@@ -300,8 +317,14 @@ smu_attach(device_t dev)
 
 		if (strncmp(name, "sensors", 8) == 0)
 			smu_attach_sensors(dev, child);
+
+		if (strncmp(name, "smu-i2c-control", 15) == 0)
+			smu_attach_i2c(dev, child);
 	}
 
+	/* Some SMUs have the I2C children directly under the bus. */
+	smu_attach_i2c(dev, node);
+
 	/*
 	 * Collect calibration constants.
 	 */
@@ -368,7 +391,14 @@ smu_attach(device_t dev)
 	 */
 	clock_register(dev, 1000);
 
-	return (0);
+	return (bus_generic_attach(dev));
+}
+
+static const struct ofw_bus_devinfo *
+smu_get_devinfo(device_t bus, device_t dev)
+{
+
+	return (device_get_ivars(dev));
 }
 
 static void
@@ -787,8 +817,8 @@ smu_attach_fans(device_t dev, phandle_t 
 		    CTLTYPE_INT | CTLFLAG_RD, &fan->max_rpm, sizeof(cell_t),
 		    "Maximum allowed RPM");
 		SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "rpm",
-		    CTLTYPE_INT | CTLFLAG_RW, dev, sc->sc_nfans,
-		    smu_fanrpm_sysctl, "I", "Fan RPM");
+		    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev,
+		    sc->sc_nfans, smu_fanrpm_sysctl, "I", "Fan RPM");
 
 		fan++;
 		sc->sc_nfans++;
@@ -951,8 +981,8 @@ smu_attach_sensors(device_t dev, phandle
 		sprintf(sysctl_desc,"%s (%s)", sens->location, units);
 
 		SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sensroot_oid), OID_AUTO,
-		    sysctl_name, CTLTYPE_INT | CTLFLAG_RD, dev, sc->sc_nsensors,
-		    smu_sensor_sysctl, "I", sysctl_desc);
+		    sysctl_name, CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
+		    dev, sc->sc_nsensors, smu_sensor_sysctl, "I", sysctl_desc);
 
 		sens++;
 		sc->sc_nsensors++;
@@ -988,13 +1018,6 @@ smu_manage_fans(device_t smu)
 			maxtemp = temp;
 	}
 
-	if (maxtemp < 10) { /* Bail if no good sensors */
-		for (i = 0; i < sc->sc_nfans; i++) 
-			smu_fan_set_rpm(smu, &sc->sc_fans[i],
-			    sc->sc_fans[i].unmanaged_rpm);
-		return;
-	}
-
 	if (maxtemp > sc->sc_critical_temp) {
 		device_printf(smu, "WARNING: Current system temperature (%d C) "
 		    "exceeds critical temperature (%d C)! Shutting down!\n",
@@ -1016,6 +1039,13 @@ smu_manage_fans(device_t smu)
 		return;
 	}
 
+	if (maxtemp < 10) { /* Bail if no good sensors */
+		for (i = 0; i < sc->sc_nfans; i++) 
+			smu_fan_set_rpm(smu, &sc->sc_fans[i],
+			    sc->sc_fans[i].unmanaged_rpm);
+		return;
+	}
+
 	if (maxtemp - sc->sc_target_temp > 4) 
 		factor = 110;
 	else if (maxtemp - sc->sc_target_temp > 1) 
@@ -1133,3 +1163,202 @@ smu_settime(device_t dev, struct timespe
 	return (smu_run_cmd(dev, &cmd, 1));
 }
 
+/* SMU I2C Interface */
+
+static int smuiic_probe(device_t dev);
+static int smuiic_attach(device_t dev);
+static int smuiic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs);
+static phandle_t smuiic_get_node(device_t bus, device_t dev);
+
+static device_method_t smuiic_methods[] = {
+	/* device interface */
+	DEVMETHOD(device_probe,         smuiic_probe),
+	DEVMETHOD(device_attach,        smuiic_attach),
+
+	/* iicbus interface */
+	DEVMETHOD(iicbus_callback,      iicbus_null_callback),
+	DEVMETHOD(iicbus_transfer,      smuiic_transfer),
+
+	/* ofw_bus interface */
+	DEVMETHOD(ofw_bus_get_node,     smuiic_get_node),
+
+	{ 0, 0 }
+};
+
+struct smuiic_softc {
+	struct mtx	sc_mtx;
+	volatile int	sc_iic_inuse;
+	int		sc_busno;
+};
+
+static driver_t smuiic_driver = {
+	"iichb",
+	smuiic_methods,
+	sizeof(struct smuiic_softc)
+};
+static devclass_t smuiic_devclass;
+
+DRIVER_MODULE(smuiic, smu, smuiic_driver, smuiic_devclass, 0, 0);
+
+static void
+smu_attach_i2c(device_t smu, phandle_t i2croot)
+{
+	phandle_t child;
+	device_t cdev;
+	struct ofw_bus_devinfo *dinfo;
+	char name[32];
+
+	for (child = OF_child(i2croot); child != 0; child = OF_peer(child)) {
+		if (OF_getprop(child, "name", name, sizeof(name)) <= 0)
+			continue;
+
+		if (strcmp(name, "i2c-bus") != 0 && strcmp(name, "i2c") != 0)
+			continue;
+
+		dinfo = malloc(sizeof(struct ofw_bus_devinfo), M_SMU,
+		    M_WAITOK | M_ZERO);
+		if (ofw_bus_gen_setup_devinfo(dinfo, child) != 0) {
+			free(dinfo, M_SMU);
+			continue;
+		}
+
+		cdev = device_add_child(smu, NULL, -1);
+		if (cdev == NULL) {
+			device_printf(smu, "<%s>: device_add_child failed\n",
+			    dinfo->obd_name);
+			ofw_bus_gen_destroy_devinfo(dinfo);
+			free(dinfo, M_SMU);
+			continue;
+		}
+		device_set_ivars(cdev, dinfo);
+	}
+}
+
+static int
+smuiic_probe(device_t dev)
+{
+	const char *name;
+
+	name = ofw_bus_get_name(dev);
+	if (name == NULL)
+		return (ENXIO);
+
+	if (strcmp(name, "i2c-bus") == 0 || strcmp(name, "i2c") == 0) {
+		device_set_desc(dev, "SMU I2C controller");
+		return (0);
+	}
+
+	return (ENXIO);
+}
+
+static int
+smuiic_attach(device_t dev)
+{
+	struct smuiic_softc *sc = device_get_softc(dev);
+	mtx_init(&sc->sc_mtx, "smuiic", NULL, MTX_DEF);
+	sc->sc_iic_inuse = 0;
+
+	/* Get our bus number */
+	OF_getprop(ofw_bus_get_node(dev), "reg", &sc->sc_busno,
+	    sizeof(sc->sc_busno));
+
+	/* Add the IIC bus layer */
+	device_add_child(dev, "iicbus", -1);
+
+	return (bus_generic_attach(dev));
+}
+
+static int
+smuiic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
+{
+	struct smuiic_softc *sc = device_get_softc(dev);
+	struct smu_cmd cmd;
+	int i, j, error;
+
+	mtx_lock(&sc->sc_mtx);
+	while (sc->sc_iic_inuse)
+		mtx_sleep(sc, &sc->sc_mtx, 0, "smuiic", 100);
+
+	sc->sc_iic_inuse = 1;
+	error = 0;
+
+	for (i = 0; i < nmsgs; i++) {
+		cmd.cmd = SMU_I2C;
+		cmd.data[0] = sc->sc_busno;
+		if (msgs[i].flags & IIC_M_NOSTOP)
+			cmd.data[1] = SMU_I2C_COMBINED;
+		else
+			cmd.data[1] = SMU_I2C_SIMPLE;
+
+		cmd.data[2] = msgs[i].slave;
+		if (msgs[i].flags & IIC_M_RD)
+			cmd.data[2] |= 1; 
+
+		if (msgs[i].flags & IIC_M_NOSTOP) {
+			KASSERT(msgs[i].len < 4,
+			    ("oversize I2C combined message"));
+
+			cmd.data[3] = min(msgs[i].len, 3);
+			memcpy(&cmd.data[4], msgs[i].buf, min(msgs[i].len, 3));
+			i++; /* Advance to next part of message */
+		} else {
+			cmd.data[3] = 0;
+			memset(&cmd.data[4], 0, 3);
+		}
+
+		cmd.data[7] = msgs[i].slave;
+		if (msgs[i].flags & IIC_M_RD)
+			cmd.data[7] |= 1; 
+
+		cmd.data[8] = msgs[i].len;
+		if (msgs[i].flags & IIC_M_RD) {
+			memset(&cmd.data[9], 0xff, msgs[i].len);
+			cmd.len = 9;
+		} else {
+			memcpy(&cmd.data[9], msgs[i].buf, msgs[i].len);
+			cmd.len = 9 + msgs[i].len;
+		}
+
+		mtx_unlock(&sc->sc_mtx);
+		smu_run_cmd(device_get_parent(dev), &cmd, 1);
+		mtx_lock(&sc->sc_mtx);
+
+		for (j = 0; j < 10; j++) {
+			cmd.cmd = SMU_I2C;
+			cmd.len = 1;
+			cmd.data[0] = 0;
+			memset(&cmd.data[1], 0xff, msgs[i].len);
+			
+			mtx_unlock(&sc->sc_mtx);
+			smu_run_cmd(device_get_parent(dev), &cmd, 1);
+			mtx_lock(&sc->sc_mtx);
+			
+			if (!(cmd.data[0] & 0x80))
+				break;
+
+			mtx_sleep(sc, &sc->sc_mtx, 0, "smuiic", 10);
+		}
+		
+		if (cmd.data[0] & 0x80) {
+			error = EIO;
+			msgs[i].len = 0;
+			goto exit;
+		}
+		memcpy(msgs[i].buf, &cmd.data[1], msgs[i].len);
+		msgs[i].len = cmd.len - 1;
+	}
+
+    exit:
+	sc->sc_iic_inuse = 0;
+	mtx_unlock(&sc->sc_mtx);
+	wakeup(sc);
+	return (error);
+}
+
+static phandle_t
+smuiic_get_node(device_t bus, device_t dev)
+{
+
+	return (ofw_bus_get_node(bus));
+}
+

Copied: projects/ppc64/sys/powerpc/powermac/smusat.c (from r208843, head/sys/powerpc/powermac/smusat.c)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ projects/ppc64/sys/powerpc/powermac/smusat.c	Sat Jun  5 17:58:10 2010	(r208844, copy of r208843, head/sys/powerpc/powermac/smusat.c)
@@ -0,0 +1,262 @@
+/*-
+ * Copyright (c) 2010 Nathan Whitehorn
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/cpu.h>
+#include <sys/ctype.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+
+#include <dev/iicbus/iicbus.h>
+#include <dev/iicbus/iiconf.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/openfirm.h>
+
+struct smu_sensor {
+	cell_t	reg;
+	char	location[32];
+	enum {
+		SMU_CURRENT_SENSOR,
+		SMU_VOLTAGE_SENSOR,
+		SMU_POWER_SENSOR,
+		SMU_TEMP_SENSOR
+	} type;
+};
+
+static int	smusat_probe(device_t);
+static int	smusat_attach(device_t);
+static int	smusat_sensor_sysctl(SYSCTL_HANDLER_ARGS);
+
+MALLOC_DEFINE(M_SMUSAT, "smusat", "SMU Sattelite Sensors");
+
+static device_method_t  smusat_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		smusat_probe),
+	DEVMETHOD(device_attach,	smusat_attach),
+	{ 0, 0 },
+};
+
+struct smusat_softc {
+	struct smu_sensor *sc_sensors;
+	int	sc_nsensors;
+
+	uint8_t	sc_cache[16];
+	time_t	sc_last_update;
+};
+
+static driver_t smusat_driver = {
+	"smusat",
+	smusat_methods,
+	sizeof(struct smusat_softc)
+};
+
+static devclass_t smusat_devclass;
+
+DRIVER_MODULE(smusat, iicbus, smusat_driver, smusat_devclass, 0, 0);
+
+static int
+smusat_probe(device_t dev)
+{
+	const char *compat = ofw_bus_get_compat(dev);
+
+	if (compat == NULL || strcmp(compat, "smu-sat") != 0)
+		return (ENXIO);
+
+	device_set_desc(dev, "SMU Satellite Sensors");
+	return (0);
+}
+
+static int
+smusat_attach(device_t dev)
+{
+	phandle_t child;
+	struct smu_sensor *sens;
+	struct smusat_softc *sc;
+	struct sysctl_oid *sensroot_oid;
+	struct sysctl_ctx_list *ctx;
+	char type[32];
+	int i;
+
+	sc = device_get_softc(dev);
+	sc->sc_nsensors = 0;
+	sc->sc_last_update = 0;
+
+	for (child = OF_child(ofw_bus_get_node(dev)); child != 0;
+	    child = OF_peer(child))
+		sc->sc_nsensors++;
+
+	if (sc->sc_nsensors == 0) {
+		device_printf(dev, "WARNING: No sensors detected!\n");
+		return (-1);
+	}
+	    
+	sc->sc_sensors = malloc(sc->sc_nsensors * sizeof(struct smu_sensor),
+	    M_SMUSAT, M_WAITOK | M_ZERO);
+
+	sens = sc->sc_sensors;
+	sc->sc_nsensors = 0;
+
+	ctx = device_get_sysctl_ctx(dev);
+	sensroot_oid = device_get_sysctl_tree(dev);
+
+	for (child = OF_child(ofw_bus_get_node(dev)); child != 0;
+	    child = OF_peer(child)) {
+		char sysctl_name[40], sysctl_desc[40];
+		const char *units;
+
+		sens->reg = 0;
+		OF_getprop(child, "reg", &sens->reg, sizeof(sens->reg));
+		if (sens->reg < 0x30)
+			continue;
+
+		sens->reg -= 0x30;
+		OF_getprop(child, "location", sens->location,
+		    sizeof(sens->location));
+
+		OF_getprop(child, "device_type", type, sizeof(type));
+
+		if (strcmp(type, "current-sensor") == 0) {
+			sens->type = SMU_CURRENT_SENSOR;
+			units = "mA";
+		} else if (strcmp(type, "temp-sensor") == 0) {
+			sens->type = SMU_TEMP_SENSOR;
+			units = "C";
+		} else if (strcmp(type, "voltage-sensor") == 0) {
+			sens->type = SMU_VOLTAGE_SENSOR;
+			units = "mV";
+		} else if (strcmp(type, "power-sensor") == 0) {
+			sens->type = SMU_POWER_SENSOR;
+			units = "mW";
+		} else {
+			continue;
+		}
+
+		for (i = 0; i < strlen(sens->location); i++) {
+			sysctl_name[i] = tolower(sens->location[i]);
+			if (isspace(sysctl_name[i]))
+				sysctl_name[i] = '_';
+		}
+		sysctl_name[i] = 0;
+
+		sprintf(sysctl_desc,"%s (%s)", sens->location, units);
+		SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sensroot_oid), OID_AUTO,
+		    sysctl_name, CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
+		    sc->sc_nsensors, smusat_sensor_sysctl, "I", sysctl_desc);
+
+		sens++;
+		sc->sc_nsensors++;
+	}
+
+	return (0);
+}
+
+static int
+smusat_updatecache(device_t dev)
+{
+	uint8_t reg = 0x3f;
+	struct smusat_softc *sc = device_get_softc(dev);
+	struct iic_msg msgs[2] = {
+	    {0, IIC_M_WR | IIC_M_NOSTOP, 1, &reg},
+	    {0, IIC_M_RD, 16, sc->sc_cache},
+	};
+
+	msgs[0].slave = msgs[1].slave = iicbus_get_addr(dev);
+	sc->sc_last_update = time_uptime;
+
+	return (iicbus_transfer(dev, msgs, 2));
+}
+
+static int
+smusat_sensor_read(device_t dev, struct smu_sensor *sens, int *val)
+{
+	int value;
+	struct smusat_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	if (time_uptime - sc->sc_last_update > 1)
+		smusat_updatecache(dev);
+
+	value = (sc->sc_cache[sens->reg*2] << 8) +
+	    sc->sc_cache[sens->reg*2 + 1];
+
+	switch (sens->type) {
+	case SMU_TEMP_SENSOR:
+		/* 16.16 */
+		value <<= 10;
+		/* Kill the .16 */
+		value >>= 16;
+		break;
+	case SMU_VOLTAGE_SENSOR:
+		/* 16.16 */
+		value <<= 4;
+		/* Kill the .16 */
+		value >>= 16;
+		break;
+	case SMU_CURRENT_SENSOR:
+		/* 16.16 */
+		value <<= 8;
+		/* Kill the .16 */
+		value >>= 16;
+		break;
+	case SMU_POWER_SENSOR:
+		/* Doesn't exist */
+		break;
+	}
+
+	*val = value;
+	return (0);
+}
+
+static int
+smusat_sensor_sysctl(SYSCTL_HANDLER_ARGS)
+{
+	device_t dev;
+	struct smusat_softc *sc;
+	struct smu_sensor *sens;
+	int value, error;
+
+	dev = arg1;
+	sc = device_get_softc(dev);
+	sens = &sc->sc_sensors[arg2];
+
+	error = smusat_sensor_read(dev, sens, &value);
+	if (error != 0)
+		return (error);
+
+	error = sysctl_handle_int(oidp, &value, 0, req);
+
+	return (error);
+}
+



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