smusat.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. /*-
  2. * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
  3. *
  4. * Copyright (c) 2010 Nathan Whitehorn
  5. * All rights reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. * 1. Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * 2. Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  17. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  18. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  19. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  20. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  21. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  23. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  24. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  25. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  26. * SUCH DAMAGE.
  27. *
  28. */
  29. #include <sys/cdefs.h>
  30. __FBSDID("$FreeBSD$");
  31. #include <sys/param.h>
  32. #include <sys/systm.h>
  33. #include <sys/module.h>
  34. #include <sys/bus.h>
  35. #include <sys/conf.h>
  36. #include <sys/cpu.h>
  37. #include <sys/ctype.h>
  38. #include <sys/kernel.h>
  39. #include <sys/sysctl.h>
  40. #include <dev/iicbus/iicbus.h>
  41. #include <dev/iicbus/iiconf.h>
  42. #include <dev/ofw/ofw_bus.h>
  43. #include <dev/ofw/openfirm.h>
  44. #include <powerpc/powermac/powermac_thermal.h>
  45. struct smu_sensor {
  46. struct pmac_therm therm;
  47. device_t dev;
  48. cell_t reg;
  49. enum {
  50. SMU_CURRENT_SENSOR,
  51. SMU_VOLTAGE_SENSOR,
  52. SMU_POWER_SENSOR,
  53. SMU_TEMP_SENSOR
  54. } type;
  55. };
  56. static int smusat_probe(device_t);
  57. static int smusat_attach(device_t);
  58. static int smusat_sensor_sysctl(SYSCTL_HANDLER_ARGS);
  59. static int smusat_sensor_read(struct smu_sensor *sens);
  60. static MALLOC_DEFINE(M_SMUSAT, "smusat", "SMU Sattelite Sensors");
  61. static device_method_t smusat_methods[] = {
  62. /* Device interface */
  63. DEVMETHOD(device_probe, smusat_probe),
  64. DEVMETHOD(device_attach, smusat_attach),
  65. { 0, 0 },
  66. };
  67. struct smusat_softc {
  68. struct smu_sensor *sc_sensors;
  69. int sc_nsensors;
  70. uint8_t sc_cache[16];
  71. time_t sc_last_update;
  72. };
  73. static driver_t smusat_driver = {
  74. "smusat",
  75. smusat_methods,
  76. sizeof(struct smusat_softc)
  77. };
  78. static devclass_t smusat_devclass;
  79. DRIVER_MODULE(smusat, iicbus, smusat_driver, smusat_devclass, 0, 0);
  80. static int
  81. smusat_probe(device_t dev)
  82. {
  83. const char *compat = ofw_bus_get_compat(dev);
  84. if (compat == NULL || strcmp(compat, "smu-sat") != 0)
  85. return (ENXIO);
  86. device_set_desc(dev, "SMU Satellite Sensors");
  87. return (0);
  88. }
  89. static int
  90. smusat_attach(device_t dev)
  91. {
  92. phandle_t child;
  93. struct smu_sensor *sens;
  94. struct smusat_softc *sc;
  95. struct sysctl_oid *sensroot_oid;
  96. struct sysctl_ctx_list *ctx;
  97. char type[32];
  98. int i;
  99. sc = device_get_softc(dev);
  100. sc->sc_nsensors = 0;
  101. sc->sc_last_update = 0;
  102. for (child = OF_child(ofw_bus_get_node(dev)); child != 0;
  103. child = OF_peer(child))
  104. sc->sc_nsensors++;
  105. if (sc->sc_nsensors == 0) {
  106. device_printf(dev, "WARNING: No sensors detected!\n");
  107. return (-1);
  108. }
  109. sc->sc_sensors = malloc(sc->sc_nsensors * sizeof(struct smu_sensor),
  110. M_SMUSAT, M_WAITOK | M_ZERO);
  111. sens = sc->sc_sensors;
  112. sc->sc_nsensors = 0;
  113. ctx = device_get_sysctl_ctx(dev);
  114. sensroot_oid = device_get_sysctl_tree(dev);
  115. for (child = OF_child(ofw_bus_get_node(dev)); child != 0;
  116. child = OF_peer(child)) {
  117. char sysctl_name[40], sysctl_desc[40];
  118. const char *units;
  119. sens->dev = dev;
  120. sens->reg = 0;
  121. OF_getprop(child, "reg", &sens->reg, sizeof(sens->reg));
  122. if (sens->reg < 0x30)
  123. continue;
  124. sens->reg -= 0x30;
  125. OF_getprop(child, "zone", &sens->therm.zone, sizeof(int));
  126. OF_getprop(child, "location", sens->therm.name,
  127. sizeof(sens->therm.name));
  128. OF_getprop(child, "device_type", type, sizeof(type));
  129. if (strcmp(type, "current-sensor") == 0) {
  130. sens->type = SMU_CURRENT_SENSOR;
  131. units = "mA";
  132. } else if (strcmp(type, "temp-sensor") == 0) {
  133. sens->type = SMU_TEMP_SENSOR;
  134. units = "C";
  135. } else if (strcmp(type, "voltage-sensor") == 0) {
  136. sens->type = SMU_VOLTAGE_SENSOR;
  137. units = "mV";
  138. } else if (strcmp(type, "power-sensor") == 0) {
  139. sens->type = SMU_POWER_SENSOR;
  140. units = "mW";
  141. } else {
  142. continue;
  143. }
  144. for (i = 0; i < strlen(sens->therm.name); i++) {
  145. sysctl_name[i] = tolower(sens->therm.name[i]);
  146. if (isspace(sysctl_name[i]))
  147. sysctl_name[i] = '_';
  148. }
  149. sysctl_name[i] = 0;
  150. sprintf(sysctl_desc,"%s (%s)", sens->therm.name, units);
  151. SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sensroot_oid), OID_AUTO,
  152. sysctl_name, CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
  153. sc->sc_nsensors, smusat_sensor_sysctl,
  154. (sens->type == SMU_TEMP_SENSOR) ? "IK" : "I", sysctl_desc);
  155. if (sens->type == SMU_TEMP_SENSOR) {
  156. /* Make up some numbers */
  157. sens->therm.target_temp = 500 + 2731; /* 50 C */
  158. sens->therm.max_temp = 900 + 2731; /* 90 C */
  159. sens->therm.read =
  160. (int (*)(struct pmac_therm *))smusat_sensor_read;
  161. pmac_thermal_sensor_register(&sens->therm);
  162. }
  163. sens++;
  164. sc->sc_nsensors++;
  165. }
  166. return (0);
  167. }
  168. static int
  169. smusat_updatecache(device_t dev)
  170. {
  171. uint8_t reg = 0x3f;
  172. uint8_t value[16];
  173. struct smusat_softc *sc = device_get_softc(dev);
  174. int error;
  175. struct iic_msg msgs[2] = {
  176. {0, IIC_M_WR | IIC_M_NOSTOP, 1, &reg},
  177. {0, IIC_M_RD, 16, value},
  178. };
  179. msgs[0].slave = msgs[1].slave = iicbus_get_addr(dev);
  180. error = iicbus_transfer(dev, msgs, 2);
  181. if (error)
  182. return (error);
  183. sc->sc_last_update = time_uptime;
  184. memcpy(sc->sc_cache, value, sizeof(value));
  185. return (0);
  186. }
  187. static int
  188. smusat_sensor_read(struct smu_sensor *sens)
  189. {
  190. int value, error;
  191. device_t dev;
  192. struct smusat_softc *sc;
  193. dev = sens->dev;
  194. sc = device_get_softc(dev);
  195. error = 0;
  196. if (time_uptime - sc->sc_last_update > 1)
  197. error = smusat_updatecache(dev);
  198. if (error)
  199. return (-error);
  200. value = (sc->sc_cache[sens->reg*2] << 8) +
  201. sc->sc_cache[sens->reg*2 + 1];
  202. if (value == 0xffff) {
  203. sc->sc_last_update = 0; /* Result was bad, don't cache */
  204. return (-EINVAL);
  205. }
  206. switch (sens->type) {
  207. case SMU_TEMP_SENSOR:
  208. /* 16.16 */
  209. value <<= 10;
  210. /* From 16.16 to 0.1 C */
  211. value = 10*(value >> 16) + ((10*(value & 0xffff)) >> 16) + 2731;
  212. break;
  213. case SMU_VOLTAGE_SENSOR:
  214. /* 16.16 */
  215. value <<= 4;
  216. /* Kill the .16 */
  217. value >>= 16;
  218. break;
  219. case SMU_CURRENT_SENSOR:
  220. /* 16.16 */
  221. value <<= 8;
  222. /* Kill the .16 */
  223. value >>= 16;
  224. break;
  225. case SMU_POWER_SENSOR:
  226. /* Doesn't exist */
  227. break;
  228. }
  229. return (value);
  230. }
  231. static int
  232. smusat_sensor_sysctl(SYSCTL_HANDLER_ARGS)
  233. {
  234. device_t dev;
  235. struct smusat_softc *sc;
  236. struct smu_sensor *sens;
  237. int value, error;
  238. dev = arg1;
  239. sc = device_get_softc(dev);
  240. sens = &sc->sc_sensors[arg2];
  241. value = smusat_sensor_read(sens);
  242. if (value < 0)
  243. return (EBUSY);
  244. error = sysctl_handle_int(oidp, &value, 0, req);
  245. return (error);
  246. }