From owner-svn-src-projects@FreeBSD.ORG Sat Jun 5 17:58:11 2010 Return-Path: Delivered-To: svn-src-projects@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id E753A106566B; Sat, 5 Jun 2010 17:58:10 +0000 (UTC) (envelope-from nwhitehorn@FreeBSD.org) Received: from svn.freebsd.org (unknown [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id D53948FC0A; Sat, 5 Jun 2010 17:58:10 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id o55HwAbL095333; Sat, 5 Jun 2010 17:58:10 GMT (envelope-from nwhitehorn@svn.freebsd.org) Received: (from nwhitehorn@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id o55HwAUx095327; Sat, 5 Jun 2010 17:58:10 GMT (envelope-from nwhitehorn@svn.freebsd.org) Message-Id: <201006051758.o55HwAUx095327@svn.freebsd.org> From: Nathan Whitehorn Date: Sat, 5 Jun 2010 17:58:10 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org X-SVN-Group: projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r208844 - in projects/ppc64/sys: conf dev/iicbus powerpc/powermac X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 05 Jun 2010 17:58:11 -0000 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 #include +#include +#include #include #include #include +#include #include #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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +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); +} +