1213904Sandreast/*-
2213904Sandreast * Copyright (c) 2010 Andreas Tobler
3213904Sandreast * All rights reserved.
4213904Sandreast *
5213904Sandreast * Redistribution and use in source and binary forms, with or without
6213904Sandreast * modification, are permitted provided that the following conditions
7213904Sandreast * are met:
8213904Sandreast * 1. Redistributions of source code must retain the above copyright
9213904Sandreast *    notice, this list of conditions and the following disclaimer.
10213904Sandreast * 2. Redistributions in binary form must reproduce the above copyright
11213904Sandreast *    notice, this list of conditions and the following disclaimer in the
12213904Sandreast *    documentation and/or other materials provided with the distribution.
13213904Sandreast *
14213904Sandreast * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15213904Sandreast * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16213904Sandreast * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17213904Sandreast * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18213904Sandreast * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19213904Sandreast * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20213904Sandreast * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21213904Sandreast * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22213904Sandreast * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23213904Sandreast * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24213904Sandreast * SUCH DAMAGE.
25213904Sandreast */
26213904Sandreast
27213904Sandreast#include <sys/cdefs.h>
28213904Sandreast__FBSDID("$FreeBSD$");
29213904Sandreast
30213904Sandreast#include <sys/param.h>
31213904Sandreast#include <sys/bus.h>
32213904Sandreast#include <sys/systm.h>
33213904Sandreast#include <sys/module.h>
34213904Sandreast#include <sys/callout.h>
35213904Sandreast#include <sys/conf.h>
36213904Sandreast#include <sys/cpu.h>
37213904Sandreast#include <sys/ctype.h>
38213904Sandreast#include <sys/kernel.h>
39213904Sandreast#include <sys/reboot.h>
40213904Sandreast#include <sys/rman.h>
41213904Sandreast#include <sys/sysctl.h>
42213904Sandreast#include <sys/limits.h>
43213904Sandreast
44213904Sandreast#include <machine/bus.h>
45213904Sandreast#include <machine/md_var.h>
46213904Sandreast
47213904Sandreast#include <dev/iicbus/iicbus.h>
48213904Sandreast#include <dev/iicbus/iiconf.h>
49213904Sandreast
50213904Sandreast#include <dev/ofw/openfirm.h>
51213904Sandreast#include <dev/ofw/ofw_bus.h>
52222458Snwhitehorn#include <powerpc/powermac/powermac_thermal.h>
53213904Sandreast
54213904Sandreast/* Drivebay sensor: LM75/DS1775. */
55213904Sandreast#define DS1775_TEMP         0x0
56213904Sandreast
57213904Sandreast/* Regular bus attachment functions */
58213904Sandreaststatic int  ds1775_probe(device_t);
59213904Sandreaststatic int  ds1775_attach(device_t);
60213904Sandreast
61222458Snwhitehornstruct ds1775_softc {
62222458Snwhitehorn	struct pmac_therm	sc_sensor;
63222458Snwhitehorn	device_t		sc_dev;
64222458Snwhitehorn	struct intr_config_hook enum_hook;
65222458Snwhitehorn	uint32_t                sc_addr;
66222458Snwhitehorn};
67222458Snwhitehorn
68213904Sandreast/* Utility functions */
69222458Snwhitehornstatic int  ds1775_sensor_read(struct ds1775_softc *sc);
70213904Sandreaststatic int  ds1775_sensor_sysctl(SYSCTL_HANDLER_ARGS);
71213904Sandreaststatic void ds1775_start(void *xdev);
72213904Sandreaststatic int  ds1775_read_2(device_t dev, uint32_t addr, uint8_t reg,
73213904Sandreast			  uint16_t *data);
74213904Sandreast
75213904Sandreaststatic device_method_t  ds1775_methods[] = {
76213904Sandreast	/* Device interface */
77213904Sandreast	DEVMETHOD(device_probe,		ds1775_probe),
78213904Sandreast	DEVMETHOD(device_attach,	ds1775_attach),
79213904Sandreast	{ 0, 0 },
80213904Sandreast};
81213904Sandreast
82213904Sandreaststatic driver_t ds1775_driver = {
83213904Sandreast	"ds1775",
84213904Sandreast	ds1775_methods,
85213904Sandreast	sizeof(struct ds1775_softc)
86213904Sandreast};
87213904Sandreast
88213904Sandreaststatic devclass_t ds1775_devclass;
89213904Sandreast
90232400SandreastDRIVER_MODULE(ds1775, iicbus, ds1775_driver, ds1775_devclass, 0, 0);
91213904Sandreast
92213904Sandreaststatic int
93213904Sandreastds1775_read_2(device_t dev, uint32_t addr, uint8_t reg, uint16_t *data)
94213904Sandreast{
95213904Sandreast	uint8_t buf[4];
96222658Sandreast	int err, try = 0;
97213904Sandreast
98213904Sandreast	struct iic_msg msg[2] = {
99213904Sandreast	    { addr, IIC_M_WR | IIC_M_NOSTOP, 1, &reg },
100213904Sandreast	    { addr, IIC_M_RD, 2, buf },
101213904Sandreast	};
102213904Sandreast
103222658Sandreast	for (;;)
104222658Sandreast	{
105222658Sandreast		err = iicbus_transfer(dev, msg, 2);
106222658Sandreast		if (err != 0)
107222658Sandreast			goto retry;
108222658Sandreast
109222658Sandreast		*data = *((uint16_t*)buf);
110222658Sandreast		return (0);
111222658Sandreast	retry:
112222658Sandreast		if (++try > 5) {
113222658Sandreast			device_printf(dev, "iicbus read failed\n");
114222658Sandreast			return (-1);
115222658Sandreast		}
116222658Sandreast		pause("ds1775_read_2", hz);
117213904Sandreast	}
118213904Sandreast}
119213904Sandreast
120213904Sandreaststatic int
121213904Sandreastds1775_probe(device_t dev)
122213904Sandreast{
123213904Sandreast	const char  *name, *compatible;
124213904Sandreast	struct ds1775_softc *sc;
125213904Sandreast
126213904Sandreast	name = ofw_bus_get_name(dev);
127213904Sandreast	compatible = ofw_bus_get_compat(dev);
128213904Sandreast
129213904Sandreast	if (!name)
130213904Sandreast		return (ENXIO);
131213904Sandreast
132213904Sandreast	if (strcmp(name, "temp-monitor") != 0 ||
133216360Sandreast	    (strcmp(compatible, "ds1775") != 0 &&
134216360Sandreast	     strcmp(compatible, "lm75") != 0))
135213904Sandreast		return (ENXIO);
136213904Sandreast
137213904Sandreast	sc = device_get_softc(dev);
138213904Sandreast	sc->sc_dev = dev;
139213904Sandreast	sc->sc_addr = iicbus_get_addr(dev);
140213904Sandreast
141216360Sandreast	device_set_desc(dev, "Temp-Monitor DS1775");
142213904Sandreast
143213904Sandreast	return (0);
144213904Sandreast}
145213904Sandreast
146213904Sandreaststatic int
147213904Sandreastds1775_attach(device_t dev)
148213904Sandreast{
149213904Sandreast	struct ds1775_softc *sc;
150213904Sandreast
151213904Sandreast	sc = device_get_softc(dev);
152213904Sandreast
153213904Sandreast	sc->enum_hook.ich_func = ds1775_start;
154213904Sandreast	sc->enum_hook.ich_arg = dev;
155213904Sandreast
156213904Sandreast	/* We have to wait until interrupts are enabled. I2C read and write
157213904Sandreast	 * only works if the interrupts are available.
158213904Sandreast	 * The unin/i2c is controlled by the htpic on unin. But this is not
159213904Sandreast	 * the master. The openpic on mac-io is controlling the htpic.
160213904Sandreast	 * This one gets attached after the mac-io probing and then the
161213904Sandreast	 * interrupts will be available.
162213904Sandreast	 */
163213904Sandreast
164213904Sandreast	if (config_intrhook_establish(&sc->enum_hook) != 0)
165213904Sandreast		return (ENOMEM);
166213904Sandreast
167213904Sandreast	return (0);
168213904Sandreast}
169213904Sandreast
170213904Sandreaststatic void
171213904Sandreastds1775_start(void *xdev)
172213904Sandreast{
173213904Sandreast	phandle_t child;
174213904Sandreast	struct ds1775_softc *sc;
175239399Sandreast	struct sysctl_oid *oid, *sensroot_oid;
176213904Sandreast	struct sysctl_ctx_list *ctx;
177217560Sandreast	ssize_t plen;
178213904Sandreast	int i;
179213904Sandreast	char sysctl_name[40], sysctl_desc[40];
180213904Sandreast
181213904Sandreast	device_t dev = (device_t)xdev;
182213904Sandreast
183213904Sandreast	sc = device_get_softc(dev);
184213904Sandreast
185213904Sandreast	child = ofw_bus_get_node(dev);
186213904Sandreast
187213904Sandreast	ctx = device_get_sysctl_ctx(dev);
188239399Sandreast	sensroot_oid = SYSCTL_ADD_NODE(ctx,
189239399Sandreast	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor",
190239399Sandreast	    CTLFLAG_RD, 0, "DS1775 Sensor Information");
191213904Sandreast
192222658Sandreast	if (OF_getprop(child, "hwsensor-zone", &sc->sc_sensor.zone,
193222658Sandreast		       sizeof(int)) < 0)
194222658Sandreast		sc->sc_sensor.zone = 0;
195222658Sandreast
196222458Snwhitehorn	plen = OF_getprop(child, "hwsensor-location", sc->sc_sensor.name,
197222458Snwhitehorn			  sizeof(sc->sc_sensor.name));
198213904Sandreast
199217560Sandreast	if (plen == -1) {
200217560Sandreast		strcpy(sysctl_name, "sensor");
201217560Sandreast	} else {
202222458Snwhitehorn		for (i = 0; i < strlen(sc->sc_sensor.name); i++) {
203222458Snwhitehorn			sysctl_name[i] = tolower(sc->sc_sensor.name[i]);
204217560Sandreast			if (isspace(sysctl_name[i]))
205217560Sandreast				sysctl_name[i] = '_';
206217560Sandreast		}
207217560Sandreast		sysctl_name[i] = 0;
208213904Sandreast	}
209213904Sandreast
210222458Snwhitehorn	/* Make up target temperatures. These are low, for the drive bay. */
211222658Sandreast	if (sc->sc_sensor.zone == 0) {
212222673Sandreast		sc->sc_sensor.target_temp = 500 + ZERO_C_TO_K;
213222673Sandreast		sc->sc_sensor.max_temp = 600 + ZERO_C_TO_K;
214222658Sandreast	}
215222658Sandreast	else {
216222673Sandreast		sc->sc_sensor.target_temp = 300 + ZERO_C_TO_K;
217222673Sandreast		sc->sc_sensor.max_temp = 600 + ZERO_C_TO_K;
218222658Sandreast	}
219222458Snwhitehorn
220222458Snwhitehorn	sc->sc_sensor.read =
221222458Snwhitehorn	    (int (*)(struct pmac_therm *sc))(ds1775_sensor_read);
222222458Snwhitehorn	pmac_thermal_sensor_register(&sc->sc_sensor);
223222458Snwhitehorn
224239399Sandreast	sprintf(sysctl_desc,"%s %s", sc->sc_sensor.name, "(C)");
225239399Sandreast	oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(sensroot_oid),
226239399Sandreast			      OID_AUTO, sysctl_name, CTLFLAG_RD, 0,
227239399Sandreast			      "Sensor Information");
228239399Sandreast	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "temp",
229213904Sandreast			CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
230213904Sandreast			0, ds1775_sensor_sysctl, "IK", sysctl_desc);
231213904Sandreast
232213904Sandreast	config_intrhook_disestablish(&sc->enum_hook);
233213904Sandreast}
234213904Sandreast
235213904Sandreaststatic int
236222458Snwhitehornds1775_sensor_read(struct ds1775_softc *sc)
237213904Sandreast{
238213904Sandreast	uint16_t buf[2];
239213904Sandreast	uint16_t read;
240222658Sandreast	int err;
241213904Sandreast
242222658Sandreast	err = ds1775_read_2(sc->sc_dev, sc->sc_addr, DS1775_TEMP, buf);
243222658Sandreast	if (err < 0)
244222658Sandreast		return (-1);
245213904Sandreast
246213904Sandreast	read = *((int16_t *)buf);
247213904Sandreast
248213904Sandreast	/* The default mode of the ADC is 9 bit, the resolution is 0.5 C per
249213904Sandreast	   bit. The temperature is in tenth kelvin.
250213904Sandreast	*/
251222673Sandreast	return (((int16_t)(read) >> 7) * 5 + ZERO_C_TO_K);
252222458Snwhitehorn}
253213904Sandreast
254213904Sandreaststatic int
255213904Sandreastds1775_sensor_sysctl(SYSCTL_HANDLER_ARGS)
256213904Sandreast{
257213904Sandreast	device_t dev;
258213904Sandreast	struct ds1775_softc *sc;
259213904Sandreast	int error;
260213904Sandreast	unsigned int temp;
261213904Sandreast
262213904Sandreast	dev = arg1;
263213904Sandreast	sc = device_get_softc(dev);
264213904Sandreast
265222458Snwhitehorn	temp = ds1775_sensor_read(sc);
266222658Sandreast	if (temp < 0)
267222658Sandreast		return (EIO);
268213904Sandreast
269213904Sandreast	error = sysctl_handle_int(oidp, &temp, 0, req);
270213904Sandreast
271213904Sandreast	return (error);
272213904Sandreast}
273