svn commit: r208844 - in projects/ppc64/sys: conf dev/iicbus
powerpc/powermac
Nathan Whitehorn
nwhitehorn at FreeBSD.org
Sat Jun 5 17:58:11 UTC 2010
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", ®, 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, ®},
+ {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);
+}
+
More information about the svn-src-projects
mailing list