svn commit: r213904 - in head/sys: conf dev/iicbus powerpc/conf powerpc/powermac

Andreas Tobler andreast at FreeBSD.org
Fri Oct 15 20:08:17 UTC 2010


Author: andreast
Date: Fri Oct 15 20:08:16 2010
New Revision: 213904
URL: http://svn.freebsd.org/changeset/base/213904

Log:
  Add three new drivers for fan control and temperature reading on the
  PowerMac7,2.
  
  - The fcu driver lets us read and write the fan RPMs for all fans in the
    PowerMac7,2. This driver is PowerMac specific.
  - The ds1775 is a driver to read the temperature for the drive bay sensor.
  - The max6690 is another driver to read temperatures. Here it is used to
    read the inlet, the backside and the U3 heatsink temperature.
  
  An additional driver, the ad7417, will follow later.
  
  Thanks to nwhitehorn for guiding me through this driver development.
  
  Approved by:	nwhitehorn (mentor)

Added:
  head/sys/dev/iicbus/ds1775.c   (contents, props changed)
  head/sys/dev/iicbus/max6690.c   (contents, props changed)
  head/sys/powerpc/powermac/fcu.c   (contents, props changed)
Modified:
  head/sys/conf/files.powerpc
  head/sys/powerpc/conf/GENERIC
  head/sys/powerpc/conf/GENERIC64
  head/sys/powerpc/conf/NOTES

Modified: head/sys/conf/files.powerpc
==============================================================================
--- head/sys/conf/files.powerpc	Fri Oct 15 20:01:35 2010	(r213903)
+++ head/sys/conf/files.powerpc	Fri Oct 15 20:08:16 2010	(r213904)
@@ -26,6 +26,8 @@ dev/cfi/cfi_bus_fdt.c		optional	cfi fdt
 dev/fb/fb.c			optional	sc
 dev/fdt/fdt_powerpc.c		optional	fdt
 dev/hwpmc/hwpmc_powerpc.c	optional	hwpmc
+dev/iicbus/ds1775.c		optional	ds1775 powermac
+dev/iicbus/max6690.c		optional	max6690 powermac
 dev/kbd/kbd.c			optional	sc
 dev/mem/memutil.c		optional	mem
 dev/ofw/openfirm.c		optional	aim | fdt
@@ -138,6 +140,7 @@ powerpc/powermac/ata_dbdma.c	optional	po
 powerpc/powermac/cuda.c		optional	powermac cuda
 powerpc/powermac/cpcht.c	optional	powermac pci
 powerpc/powermac/dbdma.c	optional	powermac pci
+powerpc/powermac/fcu.c		optional	powermac fcu
 powerpc/powermac/grackle.c	optional	powermac pci
 powerpc/powermac/hrowpic.c	optional	powermac pci
 powerpc/powermac/kiic.c		optional	powermac kiic

Added: head/sys/dev/iicbus/ds1775.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/iicbus/ds1775.c	Fri Oct 15 20:08:16 2010	(r213904)
@@ -0,0 +1,255 @@
+/*-
+ * Copyright (c) 2010 Andreas Tobler
+ * 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/bus.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/callout.h>
+#include <sys/conf.h>
+#include <sys/cpu.h>
+#include <sys/ctype.h>
+#include <sys/kernel.h>
+#include <sys/reboot.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/limits.h>
+
+#include <machine/bus.h>
+#include <machine/md_var.h>
+
+#include <dev/iicbus/iicbus.h>
+#include <dev/iicbus/iiconf.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+
+#define FCU_ZERO_C_TO_K     2732
+
+/* Drivebay sensor: LM75/DS1775. */
+#define DS1775_TEMP         0x0
+
+struct ds1775_sensor {
+	char    location[32];
+};
+
+/* Regular bus attachment functions */
+static int  ds1775_probe(device_t);
+static int  ds1775_attach(device_t);
+
+/* Utility functions */
+static int  ds1775_sensor_sysctl(SYSCTL_HANDLER_ARGS);
+static void ds1775_start(void *xdev);
+static int  ds1775_read_2(device_t dev, uint32_t addr, uint8_t reg,
+			  uint16_t *data);
+
+struct ds1775_softc {
+	device_t		sc_dev;
+	struct intr_config_hook enum_hook;
+	uint32_t                sc_addr;
+	struct ds1775_sensor    *sc_sensors;
+
+};
+static device_method_t  ds1775_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		ds1775_probe),
+	DEVMETHOD(device_attach,	ds1775_attach),
+	{ 0, 0 },
+};
+
+static driver_t ds1775_driver = {
+	"ds1775",
+	ds1775_methods,
+	sizeof(struct ds1775_softc)
+};
+
+static devclass_t ds1775_devclass;
+
+DRIVER_MODULE(ds1755, iicbus, ds1775_driver, ds1775_devclass, 0, 0);
+MALLOC_DEFINE(M_DS1775, "ds1775", "Temp-Monitor DS1775");
+
+static int
+ds1775_read_2(device_t dev, uint32_t addr, uint8_t reg, uint16_t *data)
+{
+	uint8_t buf[4];
+
+	struct iic_msg msg[2] = {
+	    { addr, IIC_M_WR | IIC_M_NOSTOP, 1, &reg },
+	    { addr, IIC_M_RD, 2, buf },
+	};
+
+	if (iicbus_transfer(dev, msg, 2) != 0) {
+		device_printf(dev, "iicbus read failed\n");
+		return (EIO);
+	}
+
+	*data = *((uint16_t*)buf);
+
+	return (0);
+}
+
+static int
+ds1775_probe(device_t dev)
+{
+	const char  *name, *compatible;
+	struct ds1775_softc *sc;
+
+	name = ofw_bus_get_name(dev);
+	compatible = ofw_bus_get_compat(dev);
+
+	if (!name)
+		return (ENXIO);
+
+	if (strcmp(name, "temp-monitor") != 0 ||
+	    strcmp(compatible, "ds1775") != 0)
+		return (ENXIO);
+
+	sc = device_get_softc(dev);
+	sc->sc_dev = dev;
+	sc->sc_addr = iicbus_get_addr(dev);
+
+	device_set_desc(dev, "Temp-Monitor DS1755");
+
+	return (0);
+}
+
+static int
+ds1775_attach(device_t dev)
+{
+	struct ds1775_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	sc->enum_hook.ich_func = ds1775_start;
+	sc->enum_hook.ich_arg = dev;
+
+	/* We have to wait until interrupts are enabled. I2C read and write
+	 * only works if the interrupts are available.
+	 * The unin/i2c is controlled by the htpic on unin. But this is not
+	 * the master. The openpic on mac-io is controlling the htpic.
+	 * This one gets attached after the mac-io probing and then the
+	 * interrupts will be available.
+	 */
+
+	if (config_intrhook_establish(&sc->enum_hook) != 0)
+		return (ENOMEM);
+
+	return (0);
+}
+
+static void
+ds1775_start(void *xdev)
+{
+	phandle_t child;
+	struct ds1775_softc *sc;
+	struct ds1775_sensor *sens;
+	struct sysctl_oid *sensroot_oid;
+	struct sysctl_ctx_list *ctx;
+	int i;
+	char sysctl_name[40], sysctl_desc[40];
+	const char *units;
+
+	device_t dev = (device_t)xdev;
+
+	sc = device_get_softc(dev);
+
+	child = ofw_bus_get_node(dev);
+
+	sc->sc_sensors = malloc (sizeof(struct ds1775_sensor),
+				 M_DS1775, M_WAITOK | M_ZERO);
+
+	sens = sc->sc_sensors;
+
+	ctx = device_get_sysctl_ctx(dev);
+	sensroot_oid = device_get_sysctl_tree(dev);
+
+	OF_getprop(child, "hwsensor-location", sens->location,
+		   sizeof(sens->location));
+	units = "C";
+
+	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,
+			0, ds1775_sensor_sysctl, "IK", sysctl_desc);
+
+	config_intrhook_disestablish(&sc->enum_hook);
+}
+
+static int
+ds1775_sensor_read(device_t dev, struct ds1775_sensor *sens, int *temp)
+{
+	struct ds1775_softc *sc;
+	uint16_t buf[2];
+	uint16_t read;
+
+	sc = device_get_softc(dev);
+
+	ds1775_read_2(sc->sc_dev, sc->sc_addr, DS1775_TEMP, buf);
+
+	read = *((int16_t *)buf);
+
+	/* The default mode of the ADC is 9 bit, the resolution is 0.5 C per
+	   bit. The temperature is in tenth kelvin.
+	*/
+	*temp = ((int16_t)(read) >> 7) * 5;
+
+	return (0);
+}
+static int
+ds1775_sensor_sysctl(SYSCTL_HANDLER_ARGS)
+{
+	device_t dev;
+	struct ds1775_softc *sc;
+	struct ds1775_sensor *sens;
+	int value;
+	int error;
+	unsigned int temp;
+
+	dev = arg1;
+	sc = device_get_softc(dev);
+	sens = &sc->sc_sensors[arg2];
+
+	error = ds1775_sensor_read(dev, sens, &value);
+	if (error != 0)
+		return (error);
+
+	temp = value + FCU_ZERO_C_TO_K;
+
+	error = sysctl_handle_int(oidp, &temp, 0, req);
+
+	return (error);
+}

Added: head/sys/dev/iicbus/max6690.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/iicbus/max6690.c	Fri Oct 15 20:08:16 2010	(r213904)
@@ -0,0 +1,335 @@
+/*-
+ * Copyright (c) 2010 Andreas Tobler
+ * 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/bus.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/callout.h>
+#include <sys/conf.h>
+#include <sys/cpu.h>
+#include <sys/ctype.h>
+#include <sys/kernel.h>
+#include <sys/reboot.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/limits.h>
+
+#include <machine/bus.h>
+#include <machine/md_var.h>
+
+#include <dev/iicbus/iicbus.h>
+#include <dev/iicbus/iiconf.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+
+#define FCU_ZERO_C_TO_K     2732
+
+/* Inlet, Backside, U3 Heatsink sensor: MAX6690. */
+
+#define MAX6690_INT_TEMP    0x0
+#define MAX6690_EXT_TEMP    0x1
+#define MAX6690_EEXT_TEMP   0x10
+#define MAX6690_IEXT_TEMP   0x11
+#define MAX6690_TEMP_MASK   0xe0
+
+struct max6690_sensor {
+	int     id;
+	char    location[32];
+};
+
+/* Regular bus attachment functions */
+static int  max6690_probe(device_t);
+static int  max6690_attach(device_t);
+
+/* Utility functions */
+static int  max6690_sensor_sysctl(SYSCTL_HANDLER_ARGS);
+static void max6690_start(void *xdev);
+static int  max6690_read_1(device_t dev, uint32_t addr, uint8_t reg,
+			   uint8_t *data);
+
+struct max6690_softc {
+	device_t		sc_dev;
+	struct intr_config_hook enum_hook;
+	uint32_t                sc_addr;
+	struct max6690_sensor   *sc_sensors;
+	int                     sc_nsensors;
+};
+static device_method_t  max6690_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		max6690_probe),
+	DEVMETHOD(device_attach,	max6690_attach),
+	{ 0, 0 },
+};
+
+static driver_t max6690_driver = {
+	"max6690",
+	max6690_methods,
+	sizeof(struct max6690_softc)
+};
+
+static devclass_t max6690_devclass;
+
+DRIVER_MODULE(max6690, iicbus, max6690_driver, max6690_devclass, 0, 0);
+MALLOC_DEFINE(M_MAX6690, "max6690", "Temp-Monitor MAX6690");
+
+static int
+max6690_read_1(device_t dev, uint32_t addr, uint8_t reg, uint8_t *data)
+{
+	uint8_t buf[4];
+
+	struct iic_msg msg[2] = {
+	    { addr, IIC_M_WR | IIC_M_NOSTOP, 1, &reg },
+	    { addr, IIC_M_RD, 1, buf },
+	};
+
+	if (iicbus_transfer(dev, msg, 2) != 0) {
+		device_printf(dev, "iicbus read failed\n");
+		return (EIO);
+	}
+
+	*data = *((uint8_t*)buf);
+
+	return (0);
+}
+
+static int
+max6690_probe(device_t dev)
+{
+	const char  *name, *compatible;
+	struct max6690_softc *sc;
+
+	name = ofw_bus_get_name(dev);
+	compatible = ofw_bus_get_compat(dev);
+
+	if (!name)
+		return (ENXIO);
+
+	if (strcmp(name, "temp-monitor") != 0 ||
+	    strcmp(compatible, "max6690") != 0)
+		return (ENXIO);
+
+	sc = device_get_softc(dev);
+	sc->sc_dev = dev;
+	sc->sc_addr = iicbus_get_addr(dev);
+
+	device_set_desc(dev, "Temp-Monitor MAX6690");
+
+	return (0);
+}
+
+/*
+ * This function returns the number of sensors. If we call it the second time
+ * and we have allocated memory for sc->sc_sensors, we fill in the properties.
+ */
+static int
+max6690_fill_sensor_prop(device_t dev)
+{
+	phandle_t child;
+	struct max6690_softc *sc;
+	u_int id[8];
+	char location[96];
+	int i = 0, j, len = 0, prop_len, prev_len = 0;
+
+	sc = device_get_softc(dev);
+
+	child = ofw_bus_get_node(dev);
+
+	/* Fill the sensor location property. */
+	prop_len = OF_getprop(child, "hwsensor-location", location,
+			      sizeof(location));
+	while (len < prop_len) {
+		if (sc->sc_sensors != NULL)
+			strcpy(sc->sc_sensors[i].location, location + len);
+		prev_len = strlen(location + len) + 1;
+		len += prev_len;
+		i++;
+	}
+	if (sc->sc_sensors == NULL)
+		return (i);
+
+	/* Fill the sensor id property. */
+	prop_len = OF_getprop(child, "hwsensor-id", id, sizeof(id));
+	for (j = 0; j < i; j++)
+		sc->sc_sensors[j].id = (id[j] & 0xf);
+
+	return (i);
+}
+static int
+max6690_attach(device_t dev)
+{
+	struct max6690_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	sc->enum_hook.ich_func = max6690_start;
+	sc->enum_hook.ich_arg = dev;
+
+	/* We have to wait until interrupts are enabled. I2C read and write
+	 * only works if the interrupts are available.
+	 * The unin/i2c is controlled by the htpic on unin. But this is not
+	 * the master. The openpic on mac-io is controlling the htpic.
+	 * This one gets attached after the mac-io probing and then the
+	 * interrupts will be available.
+	 */
+
+	if (config_intrhook_establish(&sc->enum_hook) != 0)
+		return (ENOMEM);
+
+	return (0);
+}
+
+static void
+max6690_start(void *xdev)
+{
+	phandle_t child;
+	struct max6690_softc *sc;
+	struct sysctl_oid *oid, *sensroot_oid;
+	struct sysctl_ctx_list *ctx;
+	char sysctl_name[32];
+	int i, j;
+
+	device_t dev = (device_t)xdev;
+
+	sc = device_get_softc(dev);
+
+	sc->sc_nsensors = 0;
+
+	child = ofw_bus_get_node(dev);
+
+	/* Count the actual number of sensors. */
+	sc->sc_nsensors = max6690_fill_sensor_prop(dev);
+
+	device_printf(dev, "%d sensors detected.\n", sc->sc_nsensors);
+
+	if (sc->sc_nsensors == 0)
+		device_printf(dev, "WARNING: No MAX6690 sensors detected!\n");
+
+	sc->sc_sensors = malloc (sc->sc_nsensors * sizeof(struct max6690_sensor),
+				 M_MAX6690, M_WAITOK | M_ZERO);
+
+	ctx = device_get_sysctl_ctx(dev);
+	sensroot_oid = SYSCTL_ADD_NODE(ctx,
+	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor",
+	    CTLFLAG_RD, 0, "MAX6690 Sensor Information");
+
+	/* Now we can fill the properties into the allocated struct. */
+	sc->sc_nsensors = max6690_fill_sensor_prop(dev);
+
+	/* Add sysctls for the sensors. */
+	for (i = 0; i < sc->sc_nsensors; i++) {
+		for (j = 0; j < strlen(sc->sc_sensors[i].location); j++) {
+			sysctl_name[j] = tolower(sc->sc_sensors[i].location[j]);
+			if (isspace(sysctl_name[j]))
+				sysctl_name[j] = '_';
+		}
+		sysctl_name[j] = 0;
+
+		oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(sensroot_oid),
+				      OID_AUTO,
+				      sysctl_name, CTLFLAG_RD, 0,
+				      "Sensor Information");
+		/* I use i to pass the sensor id. */
+		SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "temp",
+				CTLTYPE_INT | CTLFLAG_RD, dev, i % 2,
+				max6690_sensor_sysctl, "IK",
+				"Sensor Temp in °C");
+
+	}
+	/* Dump sensor location & ID. */
+	if (bootverbose) {
+		device_printf(dev, "Sensors\n");
+		for (i = 0; i < sc->sc_nsensors; i++) {
+			device_printf(dev, "Location : %s ID: %d\n",
+				      sc->sc_sensors[i].location,
+				      sc->sc_sensors[i].id);
+		}
+	}
+
+	config_intrhook_disestablish(&sc->enum_hook);
+}
+
+static int
+max6690_sensor_read(device_t dev, struct max6690_sensor *sens, int *temp)
+{
+	uint8_t reg_int = 0, reg_ext = 0;
+	uint8_t integer;
+	uint8_t fraction;
+	struct max6690_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	/* The internal sensor id's are even, the external ar odd. */
+	if ((sens->id % 2) == 0) {
+		reg_int = MAX6690_INT_TEMP;
+		reg_ext = MAX6690_IEXT_TEMP;
+	} else {
+		reg_int = MAX6690_EXT_TEMP;
+		reg_ext = MAX6690_EEXT_TEMP;
+	}
+
+	max6690_read_1(sc->sc_dev, sc->sc_addr, reg_int, &integer);
+
+	max6690_read_1(sc->sc_dev, sc->sc_addr, reg_ext, &fraction);
+
+	fraction &= MAX6690_TEMP_MASK;
+
+	/* The temperature is in tenth kelvin, the fractional part resolution
+	   is 0.125.
+	*/
+	*temp = (integer * 10) + (fraction >> 5) * 10 / 8;
+
+	return (0);
+}
+
+static int
+max6690_sensor_sysctl(SYSCTL_HANDLER_ARGS)
+{
+	device_t dev;
+	struct max6690_softc *sc;
+	struct max6690_sensor *sens;
+	int value = 0;
+	int error;
+	unsigned int temp;
+
+	dev = arg1;
+	sc = device_get_softc(dev);
+	sens = &sc->sc_sensors[arg2];
+
+	error = max6690_sensor_read(dev, sens, &value);
+	if (error != 0)
+		return (error);
+
+	temp = value + FCU_ZERO_C_TO_K;
+
+	error = sysctl_handle_int(oidp, &temp, 0, req);
+
+	return (error);
+}

Modified: head/sys/powerpc/conf/GENERIC
==============================================================================
--- head/sys/powerpc/conf/GENERIC	Fri Oct 15 20:01:35 2010	(r213903)
+++ head/sys/powerpc/conf/GENERIC	Fri Oct 15 20:08:16 2010	(r213904)
@@ -173,6 +173,9 @@ device		sbp		# SCSI over FireWire (Requi
 device		fwe		# Ethernet over FireWire (non-standard!)
 
 # Misc
+device		ds1775		# PowerMac7,2 temperature sensor
+device		fcu		# Apple Fan Control Unit
+device		max6690		# PowerMac7,2 temperature sensor
 device		powermac_nvram	# Open Firmware configuration NVRAM
 device		smu		# Apple System Management Unit
 

Modified: head/sys/powerpc/conf/GENERIC64
==============================================================================
--- head/sys/powerpc/conf/GENERIC64	Fri Oct 15 20:01:35 2010	(r213903)
+++ head/sys/powerpc/conf/GENERIC64	Fri Oct 15 20:08:16 2010	(r213904)
@@ -170,6 +170,9 @@ device		sbp		# SCSI over FireWire (Requi
 device		fwe		# Ethernet over FireWire (non-standard!)
 
 # Misc
+device		ds1775		# PowerMac7,2 temperature sensor
+device		fcu		# Apple Fan Control Unit
+device		max6690		# PowerMac7,2 temperature sensor
 device		powermac_nvram	# Open Firmware configuration NVRAM
 device		smu		# Apple System Management Unit
 

Modified: head/sys/powerpc/conf/NOTES
==============================================================================
--- head/sys/powerpc/conf/NOTES	Fri Oct 15 20:01:35 2010	(r213903)
+++ head/sys/powerpc/conf/NOTES	Fri Oct 15 20:08:16 2010	(r213904)
@@ -36,6 +36,9 @@ device		kiic		# Apple Keywest I2C Contro
 device		ofwd		# Open Firmware disks
 device		adb		# Apple Desktop Bus
 device		cuda		# VIA-CUDA ADB interface
+device		ds1775		# PowerMac7,2 temperature sensor
+device		fcu		# Apple Fan Control Unit
+device		max6690		# PowerMac7,2 temperature sensor
 device		pmu		# Apple Power Management Unit
 device		smu		# Apple System Management Unit
 device		snd_ai2s	# Apple I2S Audio

Added: head/sys/powerpc/powermac/fcu.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/powerpc/powermac/fcu.c	Fri Oct 15 20:08:16 2010	(r213904)
@@ -0,0 +1,506 @@
+/*-
+ * Copyright (c) 2010 Andreas Tobler
+ * 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/bus.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/callout.h>
+#include <sys/conf.h>
+#include <sys/cpu.h>
+#include <sys/ctype.h>
+#include <sys/kernel.h>
+#include <sys/reboot.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/limits.h>
+
+#include <machine/bus.h>
+#include <machine/md_var.h>
+
+#include <dev/iicbus/iicbus.h>
+#include <dev/iicbus/iiconf.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+
+/* FCU registers
+ * /u3 at 0,f8000000/i2c at f8001000/fan at 15e
+ */
+#define FCU_RPM_FAIL      0x0b      /* fans states in bits 0<1-6>7 */
+#define FCU_RPM_AVAILABLE 0x0c
+#define FCU_RPM_ACTIVE    0x0d
+#define FCU_RPM_READ(x)   0x11 + (x) * 2
+#define FCU_RPM_SET(x)    0x10 + (x) * 2
+
+#define FCU_PWM_FAIL      0x2b
+#define FCU_PWM_AVAILABLE 0x2c
+#define FCU_PWM_ACTIVE    0x2d
+#define FCU_PWM_READ(x)   0x31 + (x) * 2
+#define FCU_PWM_SET(x)    0x30 + (x) * 2
+
+struct fcu_fan {
+	int     id;
+	cell_t	min_rpm;
+	cell_t	max_rpm;
+	char	location[32];
+	enum {
+		FCU_FAN_RPM,
+		FCU_FAN_PWM
+	} type;
+	int     setpoint;
+};
+
+struct fcu_softc {
+	device_t		sc_dev;
+	struct intr_config_hook enum_hook;
+	uint32_t                sc_addr;
+	struct fcu_fan		*sc_fans;
+	int			sc_nfans;
+};
+
+static int fcu_rpm_shift;
+
+/* Regular bus attachment functions */
+static int  fcu_probe(device_t);
+static int  fcu_attach(device_t);
+
+/* Utility functions */
+static void fcu_attach_fans(device_t dev);
+static int  fcu_fill_fan_prop(device_t dev);
+static int  fcu_fan_set_rpm(device_t dev, struct fcu_fan *fan, int rpm);
+static int  fcu_fan_get_rpm(device_t dev, struct fcu_fan *fan, int *rpm);
+static int  fcu_fanrpm_sysctl(SYSCTL_HANDLER_ARGS);
+static void fcu_start(void *xdev);
+static int  fcu_write(device_t dev, uint32_t addr, uint8_t reg, uint8_t *buf,
+		      int len);
+static int  fcu_read_1(device_t dev, uint32_t addr, uint8_t reg, uint8_t *data);
+
+static device_method_t  fcu_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		fcu_probe),
+	DEVMETHOD(device_attach,	fcu_attach),
+	{ 0, 0 },
+};
+
+static driver_t fcu_driver = {
+	"fcu",
+	fcu_methods,
+	sizeof(struct fcu_softc)
+};
+
+static devclass_t fcu_devclass;
+
+DRIVER_MODULE(fcu, iicbus, fcu_driver, fcu_devclass, 0, 0);
+MALLOC_DEFINE(M_FCU, "fcu", "FCU Sensor Information");
+
+static int
+fcu_write(device_t dev, uint32_t addr, uint8_t reg, uint8_t *buff,
+	  int len)
+{
+	unsigned char buf[4];
+	struct iic_msg msg[] = {
+		{ addr, IIC_M_WR, 0, buf }
+	};
+
+	msg[0].len = len + 1;
+	buf[0] = reg;
+	memcpy(buf + 1, buff, len);
+	if (iicbus_transfer(dev, msg, 1) != 0) {
+		device_printf(dev, "iicbus write failed\n");
+		return (EIO);
+	}
+
+	return (0);
+
+}
+
+static int
+fcu_read_1(device_t dev, uint32_t addr, uint8_t reg, uint8_t *data)
+{
+	uint8_t buf[4];
+
+	struct iic_msg msg[2] = {
+	    { addr, IIC_M_WR | IIC_M_NOSTOP, 1, &reg },
+	    { addr, IIC_M_RD, 1, buf },
+	};
+
+	if (iicbus_transfer(dev, msg, 2) != 0) {
+		device_printf(dev, "iicbus read failed\n");
+		return (EIO);
+	}
+
+	*data = *((uint8_t*)buf);
+
+	return (0);
+}
+
+static int
+fcu_probe(device_t dev)
+{
+	const char  *name, *compatible;
+	struct fcu_softc *sc;
+
+	name = ofw_bus_get_name(dev);
+	compatible = ofw_bus_get_compat(dev);
+
+	if (!name)
+		return (ENXIO);
+
+	if (strcmp(name, "fan") != 0 || strcmp(compatible, "fcu") != 0)
+		return (ENXIO);
+
+	sc = device_get_softc(dev);
+	sc->sc_dev = dev;
+	sc->sc_addr = iicbus_get_addr(dev);
+
+	device_set_desc(dev, "Apple Fan Control Unit");
+
+	return (0);
+}
+
+static int
+fcu_attach(device_t dev)
+{
+	struct fcu_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	sc->enum_hook.ich_func = fcu_start;
+	sc->enum_hook.ich_arg = dev;
+
+	/* We have to wait until interrupts are enabled. I2C read and write
+	 * only works if the interrupts are available.
+	 * The unin/i2c is controlled by the htpic on unin. But this is not
+	 * the master. The openpic on mac-io is controlling the htpic.
+	 * This one gets attached after the mac-io probing and then the
+	 * interrupts will be available.
+	 */
+
+	if (config_intrhook_establish(&sc->enum_hook) != 0)
+		return (ENOMEM);
+
+	return (0);
+}
+
+static void
+fcu_start(void *xdev)
+{
+	unsigned char buf[1] = { 0xff };
+	struct fcu_softc *sc;
+
+	device_t dev = (device_t)xdev;
+
+	sc = device_get_softc(dev);
+
+	/* Start the fcu device. */
+	fcu_write(sc->sc_dev, sc->sc_addr, 0xe, buf, 1);
+	fcu_write(sc->sc_dev, sc->sc_addr, 0x2e, buf, 1);
+	fcu_read_1(sc->sc_dev, sc->sc_addr, 0, buf);
+	fcu_rpm_shift = (buf[0] == 1) ? 2 : 3;
+
+	device_printf(dev, "FCU initialized, RPM shift: %d\n",
+		      fcu_rpm_shift);
+
+	/* Detect and attach child devices. */
+
+	fcu_attach_fans(dev);
+
+	config_intrhook_disestablish(&sc->enum_hook);
+
+}
+
+static int
+fcu_fan_set_rpm(device_t dev, struct fcu_fan *fan, int rpm)
+{
+	uint8_t reg;
+	struct fcu_softc *sc;
+	unsigned char buf[2];
+
+	sc = device_get_softc(dev);
+
+	/* Clamp to allowed range */
+	rpm = max(fan->min_rpm, rpm);
+	rpm = min(fan->max_rpm, rpm);
+
+	if (fan->type == FCU_FAN_RPM) {
+		reg = FCU_RPM_SET(fan->id);
+		fan->setpoint = rpm;
+	} else if (fan->type == FCU_FAN_PWM) {
+		reg = FCU_PWM_SET(fan->id);
+		if (rpm > 3500)
+			rpm = 3500;
+		if (rpm < 500)
+			rpm = 500;
+		fan->setpoint = rpm;
+		/* PWM 30: 550 rpm, PWM 255: 3400 rpm.  */
+		rpm = (rpm * 255) / 3500;
+	} else {
+		device_printf(dev, "Unknown fan type: %d\n", fan->type);
+		return (EIO);
+	}
+
+	if (fan->type == FCU_FAN_RPM) {
+		buf[0] = rpm >> (8 - fcu_rpm_shift);
+		buf[1] = rpm << fcu_rpm_shift;
+		fcu_write(sc->sc_dev, sc->sc_addr, reg, buf, 2);
+	} else {
+		buf[0] = rpm;
+		fcu_write(sc->sc_dev, sc->sc_addr, reg, buf, 1);
+	}
+
+	return (0);
+}
+
+static int
+fcu_fan_get_rpm(device_t dev, struct fcu_fan *fan, int *rpm)
+{
+	uint8_t reg;
+	struct fcu_softc *sc;
+	uint8_t buff[2] = { 0, 0 };
+	uint8_t active = 0, avail = 0, fail = 0;
+
+	sc = device_get_softc(dev);
+
+	if (fan->type == FCU_FAN_RPM) {
+		/* Check if the fan is available. */
+		reg = FCU_RPM_AVAILABLE;
+		fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &avail);
+		if ((avail & (1 << fan->id)) == 0) {
+			device_printf(dev, "RPM Fan not available ID: %d\n",
+				      fan->id);
+			return (EIO);
+		}
+		/* Check if we have a failed fan. */
+		reg = FCU_RPM_FAIL;
+		fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &fail);
+		if ((fail & (1 << fan->id)) != 0) {
+			device_printf(dev, "RPM Fan failed ID: %d\n", fan->id);
+			return (EIO);
+		}
+		/* Check if fan is active. */
+		reg = FCU_RPM_ACTIVE;
+		fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &active);
+		if ((active & (1 << fan->id)) == 0) {
+			device_printf(dev, "RPM Fan not active ID: %d\n",
+				      fan->id);
+			return (ENXIO);
+		}
+		reg = FCU_RPM_READ(fan->id);
+	} else if (fan->type == FCU_FAN_PWM) {
+		/* Check if the fan is available. */
+		reg = FCU_PWM_AVAILABLE;
+		fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &avail);
+		if ((avail & (1 << fan->id)) == 0) {
+			device_printf(dev, "PWM Fan not available ID: %d\n",
+				      fan->id);
+			return (EIO);
+		}
+		/* Check if we have a failed fan. */
+		reg = FCU_PWM_FAIL;
+		fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &fail);
+		if ((fail & (1 << fan->id)) != 0) {
+			device_printf(dev, "PWM Fan failed ID: %d\n", fan->id);
+			return (EIO);
+		}
+		/* Check if fan is active. */
+		reg = FCU_PWM_ACTIVE;
+		fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &active);
+		if ((active & (1 << fan->id)) == 0) {
+			device_printf(dev, "PWM Fan not active ID: %d\n",
+				      fan->id);
+			return (ENXIO);
+		}
+		reg = FCU_PWM_READ(fan->id);
+	} else {
+		device_printf(dev, "Unknown fan type: %d\n", fan->type);
+		return (EIO);
+	}
+
+	/* It seems that we can read the fans rpm. */

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-head mailing list