diff options
author | andreast <andreast@FreeBSD.org> | 2011-05-29 14:25:42 +0000 |
---|---|---|
committer | andreast <andreast@FreeBSD.org> | 2011-05-29 14:25:42 +0000 |
commit | 8671d4f9f28b50e500fdeb972f3487feba0b172d (patch) | |
tree | ea5557a54f2f7c3c9dbde484db8ad3eb78991813 /sys | |
parent | 900f95596c1c622d5672a0ec3fff1f68ae6fa0a5 (diff) | |
download | FreeBSD-src-8671d4f9f28b50e500fdeb972f3487feba0b172d.zip FreeBSD-src-8671d4f9f28b50e500fdeb972f3487feba0b172d.tar.gz |
Add a new driver, the ad7417, to read temperatures and voltages on some
PowerMac's.
Approved by: nwhitehorn (mentor)
Diffstat (limited to 'sys')
-rw-r--r-- | sys/conf/files.powerpc | 1 | ||||
-rw-r--r-- | sys/dev/iicbus/ad7417.c | 457 | ||||
-rw-r--r-- | sys/powerpc/conf/GENERIC | 1 | ||||
-rw-r--r-- | sys/powerpc/conf/GENERIC64 | 1 | ||||
-rw-r--r-- | sys/powerpc/conf/NOTES | 1 |
5 files changed, 461 insertions, 0 deletions
diff --git a/sys/conf/files.powerpc b/sys/conf/files.powerpc index 7e72b19..7d4e2a2 100644 --- a/sys/conf/files.powerpc +++ b/sys/conf/files.powerpc @@ -27,6 +27,7 @@ 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/ad7417.c optional ad7417 powermac dev/iicbus/ds1775.c optional ds1775 powermac dev/iicbus/max6690.c optional max6690 powermac dev/kbd/kbd.c optional sc diff --git a/sys/dev/iicbus/ad7417.c b/sys/dev/iicbus/ad7417.c new file mode 100644 index 0000000..aa26d6f --- /dev/null +++ b/sys/dev/iicbus/ad7417.c @@ -0,0 +1,457 @@ +/*- + * 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 + +/* CPU A/B sensors, temp and adc: AD7417. */ + +#define AD7417_TEMP 0x00 +#define AD7417_CONFIG 0x01 +#define AD7417_ADC 0x04 +#define AD7417_CONFIG2 0x05 +#define AD7417_CONFMASK 0xe0 + +uint8_t adc741x_config; + +struct ad7417_sensor { + int id; + char location[32]; + enum { + ADC7417_TEMP_SENSOR, + ADC7417_ADC_SENSOR + } type; +}; + +/* Regular bus attachment functions */ +static int ad7417_probe(device_t); +static int ad7417_attach(device_t); + +/* Utility functions */ +static int ad7417_sensor_sysctl(SYSCTL_HANDLER_ARGS); +static int ad7417_write(device_t dev, uint32_t addr, uint8_t reg, + uint8_t *buf, int len); +static int ad7417_read_1(device_t dev, uint32_t addr, uint8_t reg, + uint8_t *data); +static int ad7417_read_2(device_t dev, uint32_t addr, uint8_t reg, + uint16_t *data); + +struct ad7417_softc { + device_t sc_dev; + uint32_t sc_addr; + struct ad7417_sensor *sc_sensors; + int sc_nsensors; +}; +static device_method_t ad7417_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ad7417_probe), + DEVMETHOD(device_attach, ad7417_attach), + { 0, 0 }, +}; + +static driver_t ad7417_driver = { + "ad7417", + ad7417_methods, + sizeof(struct ad7417_softc) +}; + +static devclass_t ad7417_devclass; + +DRIVER_MODULE(ad7417, iicbus, ad7417_driver, ad7417_devclass, 0, 0); +MALLOC_DEFINE(M_AD7417, "ad7417", "Supply-Monitor AD7417"); + + +static int +ad7417_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 +ad7417_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, ® }, + { 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 +ad7417_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, ® }, + { 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 +ad7417_init_adc(device_t dev, uint32_t addr) +{ + uint8_t buf; + + adc741x_config = 0; + /* Clear Config2 */ + buf = 0; + ad7417_write(dev, addr, AD7417_CONFIG2, &buf, 1); + + /* Read & cache Config1 */ + buf = 0; + ad7417_write(dev, addr, AD7417_CONFIG, &buf, 1); + + ad7417_read_1(dev, addr, AD7417_CONFIG, &buf); + adc741x_config = (uint8_t)buf; + + /* Disable shutdown mode */ + adc741x_config &= 0xfe; + buf = adc741x_config; + ad7417_write(dev, addr, AD7417_CONFIG, &buf, 1); + + return (0); + +} +static int +ad7417_probe(device_t dev) +{ + const char *name, *compatible; + struct ad7417_softc *sc; + + name = ofw_bus_get_name(dev); + compatible = ofw_bus_get_compat(dev); + + if (!name) + return (ENXIO); + + if (strcmp(name, "supply-monitor") != 0 || + strcmp(compatible, "ad7417") != 0) + return (ENXIO); + + sc = device_get_softc(dev); + sc->sc_dev = dev; + sc->sc_addr = iicbus_get_addr(dev); + + device_set_desc(dev, "Supply-Monitor AD7417"); + + 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 +ad7417_fill_sensor_prop(device_t dev) +{ + phandle_t child; + struct ad7417_softc *sc; + u_int id[10]; + char location[96]; + char type[32]; + 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 fan type property. */ + len = 0; + i = 0; + prev_len = 0; + prop_len = OF_getprop(child, "hwsensor-type", type, sizeof(type)); + while (len < prop_len) { + if (strcmp(type + len, "temperature") == 0) + sc->sc_sensors[i].type = ADC7417_TEMP_SENSOR; + else + sc->sc_sensors[i].type = ADC7417_ADC_SENSOR; + prev_len = strlen(type + len) + 1; + len += prev_len; + i++; + } + + /* Fill the sensor id property. Taken from OF. */ + prop_len = OF_getprop(child, "hwsensor-id", id, sizeof(id)); + for (j = 0; j < i; j++) + sc->sc_sensors[j].id = id[j]; + + return (i); +} + +static int +ad7417_attach(device_t dev) +{ + struct ad7417_softc *sc; + struct sysctl_oid *oid, *sensroot_oid; + struct sysctl_ctx_list *ctx; + char sysctl_name[32]; + int i, j; + const char *unit; + const char *desc; + + sc = device_get_softc(dev); + + sc->sc_nsensors = 0; + + /* Count the actual number of sensors. */ + sc->sc_nsensors = ad7417_fill_sensor_prop(dev); + + device_printf(dev, "%d sensors detected.\n", sc->sc_nsensors); + + if (sc->sc_nsensors == 0) + device_printf(dev, "WARNING: No AD7417 sensors detected!\n"); + + sc->sc_sensors = malloc (sc->sc_nsensors * sizeof(struct ad7417_sensor), + M_AD7417, 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, "AD7417 Sensor Information"); + + /* Now we can fill the properties into the allocated struct. */ + sc->sc_nsensors = ad7417_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"); + + if (sc->sc_sensors[i].type == ADC7417_TEMP_SENSOR) { + unit = "temp"; + desc = "Sensor temp in C"; + } else { + unit = "volt"; + desc = "Sensor Volt in V"; + } + /* I use i to pass the sensor id. */ + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, + unit, CTLTYPE_INT | CTLFLAG_RD, dev, + i, ad7417_sensor_sysctl, + sc->sc_sensors[i].type == ADC7417_TEMP_SENSOR ? + "IK" : "I", desc); + } + /* Dump sensor location, ID & type. */ + if (bootverbose) { + device_printf(dev, "Sensors\n"); + for (i = 0; i < sc->sc_nsensors; i++) { + device_printf(dev, "Location: %s ID: %d type: %d\n", + sc->sc_sensors[i].location, + sc->sc_sensors[i].id, + sc->sc_sensors[i].type); + } + } + + return (0); +} + +static int +ad7417_get_temp(device_t dev, uint32_t addr, int *temp) +{ + uint16_t buf[2]; + uint16_t read; + + ad7417_read_2(dev, addr, AD7417_TEMP, buf); + read = *((int16_t*)buf); + + /* The ADC is 10 bit, the resolution is 0.25 C. + The temperature is in tenth kelvin. + */ + *temp = (((int16_t)(read & 0xffc0)) >> 6) * 25 / 10; + return (0); +} + +static int +ad7417_get_adc(device_t dev, uint32_t addr, unsigned int *value, + uint8_t chan) +{ + uint8_t cfg1, tmp; + uint16_t read, buf[2]; + + ad7417_read_1(dev, addr, AD7417_CONFIG, &cfg1); + + tmp = chan << 5; + + cfg1 = (cfg1 & ~AD7417_CONFMASK) | (tmp & AD7417_CONFMASK); + + ad7417_write(dev, addr, AD7417_CONFIG, &cfg1, 1); + + ad7417_read_2(dev, addr, AD7417_ADC, buf); + + read = *((uint16_t*)buf); + + *value = ((uint32_t)read) >> 6; + + return (0); +} + +static int +ad7417_sensor_read(device_t dev, struct ad7417_sensor *sens, int *temp) +{ + struct ad7417_softc *sc; + + sc = device_get_softc(dev); + + /* Init the ADC. */ + ad7417_init_adc(sc->sc_dev, sc->sc_addr); + + if (sens->type == ADC7417_TEMP_SENSOR) { + ad7417_get_temp(sc->sc_dev, sc->sc_addr, temp); + *temp += FCU_ZERO_C_TO_K; + } else { + uint8_t chan; + switch (sens->id) { + case 11: + case 16: + chan = 1; + break; + case 12: + case 17: + chan = 2; + break; + case 13: + case 18: + chan = 3; + break; + case 14: + case 19: + chan = 4; + break; + default: + chan = 1; + } + + ad7417_get_adc(sc->sc_dev, sc->sc_addr, temp, chan); + } + return (0); +} + +static int +ad7417_sensor_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev; + struct ad7417_softc *sc; + struct ad7417_sensor *sens; + int value = 0; + int error; + int temp; + + dev = arg1; + sc = device_get_softc(dev); + sens = &sc->sc_sensors[arg2]; + + error = ad7417_sensor_read(dev, sens, &value); + if (error != 0) + return (error); + + temp = value; + + error = sysctl_handle_int(oidp, &temp, 0, req); + + return (error); +} diff --git a/sys/powerpc/conf/GENERIC b/sys/powerpc/conf/GENERIC index d221a97..af4b19a 100644 --- a/sys/powerpc/conf/GENERIC +++ b/sys/powerpc/conf/GENERIC @@ -174,6 +174,7 @@ device sbp # SCSI over FireWire (Requires scbus and da) device fwe # Ethernet over FireWire (non-standard!) # Misc +device ad7417 # PowerMac7,2 temperature sensor device ds1775 # PowerMac7,2 temperature sensor device fcu # Apple Fan Control Unit device max6690 # PowerMac7,2 temperature sensor diff --git a/sys/powerpc/conf/GENERIC64 b/sys/powerpc/conf/GENERIC64 index d7526b3..ab4a0dc 100644 --- a/sys/powerpc/conf/GENERIC64 +++ b/sys/powerpc/conf/GENERIC64 @@ -175,6 +175,7 @@ device sbp # SCSI over FireWire (Requires scbus and da) device fwe # Ethernet over FireWire (non-standard!) # Misc +device ad7417 # PowerMac7,2 temperature sensor device ds1775 # PowerMac7,2 temperature sensor device fcu # Apple Fan Control Unit device max6690 # PowerMac7,2 temperature sensor diff --git a/sys/powerpc/conf/NOTES b/sys/powerpc/conf/NOTES index b80a817..374106d 100644 --- a/sys/powerpc/conf/NOTES +++ b/sys/powerpc/conf/NOTES @@ -39,6 +39,7 @@ device kiic # Apple Keywest I2C Controller device ofwd # Open Firmware disks device adb # Apple Desktop Bus device cuda # VIA-CUDA ADB interface +device ad7417 # PowerMac7,2 temperature sensor device ds1775 # PowerMac7,2 temperature sensor device fcu # Apple Fan Control Unit device max6690 # PowerMac7,2 temperature sensor |