]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/dev/iicbus/ad7417.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / dev / iicbus / ad7417.c
1 /*-
2  * Copyright (c) 2010 Andreas Tobler
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/bus.h>
32 #include <sys/systm.h>
33 #include <sys/module.h>
34 #include <sys/callout.h>
35 #include <sys/conf.h>
36 #include <sys/cpu.h>
37 #include <sys/ctype.h>
38 #include <sys/kernel.h>
39 #include <sys/reboot.h>
40 #include <sys/rman.h>
41 #include <sys/sysctl.h>
42 #include <sys/limits.h>
43
44 #include <machine/bus.h>
45 #include <machine/md_var.h>
46
47 #include <dev/iicbus/iicbus.h>
48 #include <dev/iicbus/iiconf.h>
49
50 #include <dev/ofw/openfirm.h>
51 #include <dev/ofw/ofw_bus.h>
52 #include <powerpc/powermac/powermac_thermal.h>
53
54 /* CPU A/B sensors, temp and adc: AD7417. */
55
56 #define AD7417_TEMP         0x00
57 #define AD7417_CONFIG       0x01
58 #define AD7417_ADC          0x04
59 #define AD7417_CONFIG2      0x05
60 #define AD7417_CONFMASK     0xe0
61
62 uint8_t adc741x_config;
63
64 struct ad7417_sensor {
65         struct  pmac_therm therm;
66         device_t dev;
67         int     id;
68         enum {
69                 ADC7417_TEMP_SENSOR,
70                 ADC7417_ADC_SENSOR
71         } type;
72 };
73
74 struct write_data {
75         uint8_t reg;
76         uint8_t val;
77 };
78
79 struct read_data {
80         uint8_t reg;
81         uint16_t val;
82 };
83
84 /* Regular bus attachment functions */
85 static int ad7417_probe(device_t);
86 static int ad7417_attach(device_t);
87
88 /* Utility functions */
89 static int ad7417_sensor_sysctl(SYSCTL_HANDLER_ARGS);
90 static int ad7417_write(device_t dev, uint32_t addr, uint8_t reg,
91                         uint8_t *buf, int len);
92 static int ad7417_read_1(device_t dev, uint32_t addr, uint8_t reg,
93                          uint8_t *data);
94 static int ad7417_read_2(device_t dev, uint32_t addr, uint8_t reg,
95                          uint16_t *data);
96 static int ad7417_write_read(device_t dev, uint32_t addr,
97                              struct write_data out, struct read_data *in);
98 static int ad7417_diode_read(struct ad7417_sensor *sens);
99 static int ad7417_adc_read(struct ad7417_sensor *sens);
100 static int ad7417_sensor_read(struct ad7417_sensor *sens);
101
102 struct ad7417_softc {
103         device_t                sc_dev;
104         uint32_t                sc_addr;
105         struct ad7417_sensor    *sc_sensors;
106         int                     sc_nsensors;
107 };
108 static device_method_t  ad7417_methods[] = {
109         /* Device interface */
110         DEVMETHOD(device_probe,         ad7417_probe),
111         DEVMETHOD(device_attach,        ad7417_attach),
112         { 0, 0 },
113 };
114
115 static driver_t ad7417_driver = {
116         "ad7417",
117         ad7417_methods,
118         sizeof(struct ad7417_softc)
119 };
120
121 static devclass_t ad7417_devclass;
122
123 DRIVER_MODULE(ad7417, iicbus, ad7417_driver, ad7417_devclass, 0, 0);
124 static MALLOC_DEFINE(M_AD7417, "ad7417", "Supply-Monitor AD7417");
125
126
127 static int
128 ad7417_write(device_t dev, uint32_t addr, uint8_t reg, uint8_t *buff, int len)
129 {
130         unsigned char buf[4];
131         int try = 0;
132
133         struct iic_msg msg[] = {
134                 { addr, IIC_M_WR, 0, buf }
135         };
136
137         msg[0].len = len + 1;
138         buf[0] = reg;
139         memcpy(buf + 1, buff, len);
140
141         for (;;)
142         {
143                 if (iicbus_transfer(dev, msg, 1) == 0)
144                         return (0);
145
146                 if (++try > 5) {
147                         device_printf(dev, "iicbus write failed\n");
148                         return (-1);
149                 }
150                 pause("ad7417_write", hz);
151         }
152 }
153
154 static int
155 ad7417_read_1(device_t dev, uint32_t addr, uint8_t reg, uint8_t *data)
156 {
157         uint8_t buf[4];
158         int err, try = 0;
159
160         struct iic_msg msg[2] = {
161             { addr, IIC_M_WR | IIC_M_NOSTOP, 1, &reg },
162             { addr, IIC_M_RD, 1, buf },
163         };
164
165         for (;;)
166         {
167                 err = iicbus_transfer(dev, msg, 2);
168                 if (err != 0)
169                         goto retry;
170
171                 *data = *((uint8_t*)buf);
172                 return (0);
173         retry:
174                 if (++try > 5) {
175                         device_printf(dev, "iicbus read failed\n");
176                         return (-1);
177                 }
178                 pause("ad7417_read_1", hz);
179         }
180 }
181
182 static int
183 ad7417_read_2(device_t dev, uint32_t addr, uint8_t reg, uint16_t *data)
184 {
185         uint8_t buf[4];
186         int err, try = 0;
187
188         struct iic_msg msg[2] = {
189             { addr, IIC_M_WR | IIC_M_NOSTOP, 1, &reg },
190             { addr, IIC_M_RD, 2, buf },
191         };
192
193         for (;;)
194         {
195                 err = iicbus_transfer(dev, msg, 2);
196                 if (err != 0)
197                         goto retry;
198
199                 *data = *((uint16_t*)buf);
200                 return (0);
201         retry:
202                 if (++try > 5) {
203                         device_printf(dev, "iicbus read failed\n");
204                         return (-1);
205                 }
206                 pause("ad7417_read_2", hz);
207         }
208 }
209
210 static int
211 ad7417_write_read(device_t dev, uint32_t addr, struct write_data out,
212                   struct read_data *in)
213 {
214         uint8_t buf[4];
215         int err, try = 0;
216
217         /* Do a combined write/read. */
218         struct iic_msg msg[3] = {
219             { addr, IIC_M_WR, 2, buf },
220             { addr, IIC_M_WR | IIC_M_NOSTOP, 1, &in->reg },
221             { addr, IIC_M_RD, 2, buf },
222         };
223
224         /* Prepare the write msg. */
225         buf[0] = out.reg;
226         buf[1] = out.val & 0xff;
227
228         for (;;)
229         {
230                 err = iicbus_transfer(dev, msg, 3);
231                 if (err != 0)
232                         goto retry;
233
234                 in->val = *((uint16_t*)buf);
235                 return (0);
236         retry:
237                 if (++try > 5) {
238                         device_printf(dev, "iicbus write/read failed\n");
239                         return (-1);
240                 }
241                 pause("ad7417_write_read", hz);
242         }
243 }
244
245 static int
246 ad7417_init_adc(device_t dev, uint32_t addr)
247 {
248         uint8_t buf;
249         int err;
250
251         adc741x_config = 0;
252         /* Clear Config2 */
253         buf = 0;
254
255         err = ad7417_write(dev, addr, AD7417_CONFIG2, &buf, 1);
256
257          /* Read & cache Config1 */
258         buf = 0;
259         err = ad7417_write(dev, addr, AD7417_CONFIG, &buf, 1);
260         err = ad7417_read_1(dev, addr, AD7417_CONFIG, &buf);
261         adc741x_config = (uint8_t)buf;
262
263         /* Disable shutdown mode */
264         adc741x_config &= 0xfe;
265         buf = adc741x_config;
266         err = ad7417_write(dev, addr, AD7417_CONFIG, &buf, 1);
267         if (err < 0)
268                 return (-1);
269
270         return (0);
271
272 }
273 static int
274 ad7417_probe(device_t dev)
275 {
276         const char  *name, *compatible;
277         struct ad7417_softc *sc;
278
279         name = ofw_bus_get_name(dev);
280         compatible = ofw_bus_get_compat(dev);
281
282         if (!name)
283                 return (ENXIO);
284
285         if (strcmp(name, "supply-monitor") != 0 ||
286             strcmp(compatible, "ad7417") != 0)
287                 return (ENXIO);
288
289         sc = device_get_softc(dev);
290         sc->sc_dev = dev;
291         sc->sc_addr = iicbus_get_addr(dev);
292
293         device_set_desc(dev, "Supply-Monitor AD7417");
294
295         return (0);
296 }
297
298 /*
299  * This function returns the number of sensors. If we call it the second time
300  * and we have allocated memory for sc->sc_sensors, we fill in the properties.
301  */
302 static int
303 ad7417_fill_sensor_prop(device_t dev)
304 {
305         phandle_t child;
306         struct ad7417_softc *sc;
307         u_int id[10];
308         char location[96];
309         char type[32];
310         int i = 0, j, len = 0, prop_len, prev_len = 0;
311
312         sc = device_get_softc(dev);
313
314         child = ofw_bus_get_node(dev);
315
316         /* Fill the sensor location property. */
317         prop_len = OF_getprop(child, "hwsensor-location", location,
318                               sizeof(location));
319         while (len < prop_len) {
320                 if (sc->sc_sensors != NULL)
321                         strcpy(sc->sc_sensors[i].therm.name, location + len);
322                 prev_len = strlen(location + len) + 1;
323                 len += prev_len;
324                 i++;
325         }
326         if (sc->sc_sensors == NULL)
327                 return (i);
328
329         /* Fill the sensor type property. */
330         len = 0;
331         i = 0;
332         prev_len = 0;
333         prop_len = OF_getprop(child, "hwsensor-type", type, sizeof(type));
334         while (len < prop_len) {
335                 if (strcmp(type + len, "temperature") == 0)
336                         sc->sc_sensors[i].type = ADC7417_TEMP_SENSOR;
337                 else
338                         sc->sc_sensors[i].type = ADC7417_ADC_SENSOR;
339                 prev_len = strlen(type + len) + 1;
340                 len += prev_len;
341                 i++;
342         }
343
344         /* Fill the sensor id property. Taken from OF. */
345         prop_len = OF_getprop(child, "hwsensor-id", id, sizeof(id));
346         for (j = 0; j < i; j++)
347                 sc->sc_sensors[j].id = id[j];
348
349         /* Fill the sensor zone property. Taken from OF. */
350         prop_len = OF_getprop(child, "hwsensor-zone", id, sizeof(id));
351         for (j = 0; j < i; j++)
352                 sc->sc_sensors[j].therm.zone = id[j];
353
354         /* Finish setting up sensor properties */
355         for (j = 0; j < i; j++) {
356                 sc->sc_sensors[j].dev = dev;
357         
358                 /* HACK: Apple wired a random diode to the ADC line */
359                 if (strstr(sc->sc_sensors[j].therm.name, "DIODE TEMP")
360                     != NULL) {
361                         sc->sc_sensors[j].type = ADC7417_TEMP_SENSOR;
362                         sc->sc_sensors[j].therm.read =
363                             (int (*)(struct pmac_therm *))(ad7417_diode_read);
364                 } else {
365                         sc->sc_sensors[j].therm.read =
366                             (int (*)(struct pmac_therm *))(ad7417_sensor_read);
367                 }
368                         
369                 if (sc->sc_sensors[j].type != ADC7417_TEMP_SENSOR)
370                         continue;
371
372                 /* Make up some ranges */
373                 sc->sc_sensors[j].therm.target_temp = 500 + ZERO_C_TO_K;
374                 sc->sc_sensors[j].therm.max_temp = 900 + ZERO_C_TO_K;
375                 
376                 pmac_thermal_sensor_register(&sc->sc_sensors[j].therm);
377         }
378
379         return (i);
380 }
381
382 static int
383 ad7417_attach(device_t dev)
384 {
385         struct ad7417_softc *sc;
386         struct sysctl_oid *oid, *sensroot_oid;
387         struct sysctl_ctx_list *ctx;
388         char sysctl_name[32];
389         int i, j;
390         const char *unit;
391         const char *desc;
392
393         sc = device_get_softc(dev);
394
395         sc->sc_nsensors = 0;
396
397         /* Count the actual number of sensors. */
398         sc->sc_nsensors = ad7417_fill_sensor_prop(dev);
399
400         device_printf(dev, "%d sensors detected.\n", sc->sc_nsensors);
401
402         if (sc->sc_nsensors == 0)
403                 device_printf(dev, "WARNING: No AD7417 sensors detected!\n");
404
405         sc->sc_sensors = malloc (sc->sc_nsensors * sizeof(struct ad7417_sensor),
406                                  M_AD7417, M_WAITOK | M_ZERO);
407
408         ctx = device_get_sysctl_ctx(dev);
409         sensroot_oid = SYSCTL_ADD_NODE(ctx,
410             SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor",
411             CTLFLAG_RD, 0, "AD7417 Sensor Information");
412
413         /* Now we can fill the properties into the allocated struct. */
414         sc->sc_nsensors = ad7417_fill_sensor_prop(dev);
415
416         /* Add sysctls for the sensors. */
417         for (i = 0; i < sc->sc_nsensors; i++) {
418                 for (j = 0; j < strlen(sc->sc_sensors[i].therm.name); j++) {
419                         sysctl_name[j] =
420                             tolower(sc->sc_sensors[i].therm.name[j]);
421                         if (isspace(sysctl_name[j]))
422                                 sysctl_name[j] = '_';
423                 }
424                 sysctl_name[j] = 0;
425
426                 oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(sensroot_oid),
427                                       OID_AUTO,
428                                       sysctl_name, CTLFLAG_RD, 0,
429                                       "Sensor Information");
430
431                 if (sc->sc_sensors[i].type == ADC7417_TEMP_SENSOR) {
432                         unit = "temp";
433                         desc = "Sensor temp in C";
434                 } else {
435                         unit = "volt";
436                         desc = "Sensor Volt in V";
437                 }
438                 /* I use i to pass the sensor id. */
439                 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
440                                 unit, CTLTYPE_INT | CTLFLAG_RD, dev,
441                                 i, ad7417_sensor_sysctl,
442                                 sc->sc_sensors[i].type == ADC7417_TEMP_SENSOR ?
443                                 "IK" : "I", desc);
444         }
445         /* Dump sensor location, ID & type. */
446         if (bootverbose) {
447                 device_printf(dev, "Sensors\n");
448                 for (i = 0; i < sc->sc_nsensors; i++) {
449                         device_printf(dev, "Location: %s ID: %d type: %d\n",
450                                       sc->sc_sensors[i].therm.name,
451                                       sc->sc_sensors[i].id,
452                                       sc->sc_sensors[i].type);
453                 }
454         }
455
456         return (0);
457 }
458
459 static int
460 ad7417_get_temp(device_t dev, uint32_t addr, int *temp)
461 {
462         uint16_t buf[2];
463         uint16_t read;
464         int err;
465
466         err = ad7417_read_2(dev, addr, AD7417_TEMP, buf);
467
468         if (err < 0)
469                 return (-1);
470
471         read = *((int16_t*)buf);
472
473         /* The ADC is 10 bit, the resolution is 0.25 C.
474            The temperature is in tenth kelvin.
475         */
476         *temp = (((int16_t)(read & 0xffc0)) >> 6) * 25 / 10;
477         return (0);
478 }
479
480 static int
481 ad7417_get_adc(device_t dev, uint32_t addr, unsigned int *value,
482                uint8_t chan)
483 {
484         uint8_t tmp;
485         int err;
486         struct write_data config;
487         struct read_data data;
488
489         tmp = chan << 5;
490         config.reg = AD7417_CONFIG;
491         data.reg = AD7417_ADC;
492         data.val = 0;
493
494         err = ad7417_read_1(dev, addr, AD7417_CONFIG, &config.val);
495
496         config.val = (config.val & ~AD7417_CONFMASK) | (tmp & AD7417_CONFMASK);
497
498         err = ad7417_write_read(dev, addr, config, &data);
499         if (err < 0)
500                 return (-1);
501
502         *value = ((uint32_t)data.val) >> 6;
503
504         return (0);
505 }
506
507 static int
508 ad7417_diode_read(struct ad7417_sensor *sens)
509 {
510         static int eeprom_read = 0;
511         static cell_t eeprom[2][40];
512         phandle_t eeprom_node;
513         int rawval, diode_slope, diode_offset;
514         int temp;
515
516         if (!eeprom_read) {
517                 eeprom_node = OF_finddevice("/u3/i2c/cpuid@a0");
518                 OF_getprop(eeprom_node, "cpuid", eeprom[0], sizeof(eeprom[0]));
519                 eeprom_node = OF_finddevice("/u3/i2c/cpuid@a2");
520                 OF_getprop(eeprom_node, "cpuid", eeprom[1], sizeof(eeprom[1]));
521                 eeprom_read = 1;
522         }
523
524         rawval = ad7417_adc_read(sens);
525         if (rawval < 0)
526                 return (-1);
527
528         if (strstr(sens->therm.name, "CPU B") != NULL) {
529                 diode_slope = eeprom[1][0x11] >> 16;
530                 diode_offset = (int16_t)(eeprom[1][0x11] & 0xffff) << 12;
531         } else {
532                 diode_slope = eeprom[0][0x11] >> 16;
533                 diode_offset = (int16_t)(eeprom[0][0x11] & 0xffff) << 12;
534         }
535
536         temp = (rawval*diode_slope + diode_offset) >> 2;
537         temp = (10*(temp >> 16)) + ((10*(temp & 0xffff)) >> 16);
538         
539         return (temp + ZERO_C_TO_K);
540 }
541
542 static int
543 ad7417_adc_read(struct ad7417_sensor *sens)
544 {
545         struct ad7417_softc *sc;
546         uint8_t chan;
547         int temp;
548
549         sc = device_get_softc(sens->dev);
550
551         switch (sens->id) {
552         case 11:
553         case 16:
554                 chan = 1;
555                 break;
556         case 12:
557         case 17:
558                 chan = 2;
559                 break;
560         case 13:
561         case 18:
562                 chan = 3;
563                 break;
564         case 14:
565         case 19:
566                 chan = 4;
567                 break;
568         default:
569                 chan = 1;
570         }
571
572         if (ad7417_get_adc(sc->sc_dev, sc->sc_addr, &temp, chan) < 0)
573                 return (-1);
574
575         return (temp);
576 }
577
578
579 static int
580 ad7417_sensor_read(struct ad7417_sensor *sens)
581 {
582         struct ad7417_softc *sc;
583         int temp;
584
585         sc = device_get_softc(sens->dev);
586
587         /* Init the ADC. */
588         if (ad7417_init_adc(sc->sc_dev, sc->sc_addr) < 0)
589                 return (-1);
590
591         if (sens->type == ADC7417_TEMP_SENSOR) {
592                 if (ad7417_get_temp(sc->sc_dev, sc->sc_addr, &temp) < 0)
593                         return (-1);
594                 temp += ZERO_C_TO_K;
595         } else {
596                 temp = ad7417_adc_read(sens);
597         }
598         return (temp);
599 }
600
601 static int
602 ad7417_sensor_sysctl(SYSCTL_HANDLER_ARGS)
603 {
604         device_t dev;
605         struct ad7417_softc *sc;
606         struct ad7417_sensor *sens;
607         int value = 0;
608         int error;
609
610         dev = arg1;
611         sc = device_get_softc(dev);
612         sens = &sc->sc_sensors[arg2];
613
614         value = sens->therm.read(&sens->therm);
615         if (value < 0)
616                 return (ENXIO);
617
618         error = sysctl_handle_int(oidp, &value, 0, req);
619
620         return (error);
621 }