fcu.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. /*-
  2. * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
  3. *
  4. * Copyright (c) 2010 Andreas Tobler
  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. #include <sys/cdefs.h>
  29. __FBSDID("$FreeBSD$");
  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. #include <machine/bus.h>
  44. #include <machine/md_var.h>
  45. #include <dev/iicbus/iicbus.h>
  46. #include <dev/iicbus/iiconf.h>
  47. #include <dev/ofw/openfirm.h>
  48. #include <dev/ofw/ofw_bus.h>
  49. #include <powerpc/powermac/powermac_thermal.h>
  50. /* FCU registers
  51. * /u3@0,f8000000/i2c@f8001000/fan@15e
  52. */
  53. #define FCU_RPM_FAIL 0x0b /* fans states in bits 0<1-6>7 */
  54. #define FCU_RPM_AVAILABLE 0x0c
  55. #define FCU_RPM_ACTIVE 0x0d
  56. #define FCU_RPM_READ(x) 0x11 + (x) * 2
  57. #define FCU_RPM_SET(x) 0x10 + (x) * 2
  58. #define FCU_PWM_FAIL 0x2b
  59. #define FCU_PWM_AVAILABLE 0x2c
  60. #define FCU_PWM_ACTIVE 0x2d
  61. #define FCU_PWM_RPM(x) 0x31 + (x) * 2 /* Get RPM. */
  62. #define FCU_PWM_SGET(x) 0x30 + (x) * 2 /* Set or get PWM. */
  63. struct fcu_fan {
  64. struct pmac_fan fan;
  65. device_t dev;
  66. int id;
  67. enum {
  68. FCU_FAN_RPM,
  69. FCU_FAN_PWM
  70. } type;
  71. int setpoint;
  72. int rpm;
  73. };
  74. struct fcu_softc {
  75. device_t sc_dev;
  76. struct intr_config_hook enum_hook;
  77. uint32_t sc_addr;
  78. struct fcu_fan *sc_fans;
  79. int sc_nfans;
  80. };
  81. /* We can read the PWM and the RPM from a PWM controlled fan.
  82. * Offer both values via sysctl.
  83. */
  84. enum {
  85. FCU_PWM_SYSCTL_PWM = 1 << 8,
  86. FCU_PWM_SYSCTL_RPM = 2 << 8
  87. };
  88. static int fcu_rpm_shift;
  89. /* Regular bus attachment functions */
  90. static int fcu_probe(device_t);
  91. static int fcu_attach(device_t);
  92. /* Utility functions */
  93. static void fcu_attach_fans(device_t dev);
  94. static int fcu_fill_fan_prop(device_t dev);
  95. static int fcu_fan_set_rpm(struct fcu_fan *fan, int rpm);
  96. static int fcu_fan_get_rpm(struct fcu_fan *fan);
  97. static int fcu_fan_set_pwm(struct fcu_fan *fan, int pwm);
  98. static int fcu_fan_get_pwm(device_t dev, struct fcu_fan *fan, int *pwm,
  99. int *rpm);
  100. static int fcu_fanrpm_sysctl(SYSCTL_HANDLER_ARGS);
  101. static void fcu_start(void *xdev);
  102. static int fcu_write(device_t dev, uint32_t addr, uint8_t reg, uint8_t *buf,
  103. int len);
  104. static int fcu_read_1(device_t dev, uint32_t addr, uint8_t reg, uint8_t *data);
  105. static device_method_t fcu_methods[] = {
  106. /* Device interface */
  107. DEVMETHOD(device_probe, fcu_probe),
  108. DEVMETHOD(device_attach, fcu_attach),
  109. { 0, 0 },
  110. };
  111. static driver_t fcu_driver = {
  112. "fcu",
  113. fcu_methods,
  114. sizeof(struct fcu_softc)
  115. };
  116. static devclass_t fcu_devclass;
  117. DRIVER_MODULE(fcu, iicbus, fcu_driver, fcu_devclass, 0, 0);
  118. static MALLOC_DEFINE(M_FCU, "fcu", "FCU Sensor Information");
  119. static int
  120. fcu_write(device_t dev, uint32_t addr, uint8_t reg, uint8_t *buff,
  121. int len)
  122. {
  123. unsigned char buf[4];
  124. int try = 0;
  125. struct iic_msg msg[] = {
  126. { addr, IIC_M_WR, 0, buf }
  127. };
  128. msg[0].len = len + 1;
  129. buf[0] = reg;
  130. memcpy(buf + 1, buff, len);
  131. for (;;)
  132. {
  133. if (iicbus_transfer(dev, msg, 1) == 0)
  134. return (0);
  135. if (++try > 5) {
  136. device_printf(dev, "iicbus write failed\n");
  137. return (-1);
  138. }
  139. pause("fcu_write", hz);
  140. }
  141. }
  142. static int
  143. fcu_read_1(device_t dev, uint32_t addr, uint8_t reg, uint8_t *data)
  144. {
  145. uint8_t buf[4];
  146. int err, try = 0;
  147. struct iic_msg msg[2] = {
  148. { addr, IIC_M_WR | IIC_M_NOSTOP, 1, &reg },
  149. { addr, IIC_M_RD, 1, buf },
  150. };
  151. for (;;)
  152. {
  153. err = iicbus_transfer(dev, msg, 2);
  154. if (err != 0)
  155. goto retry;
  156. *data = *((uint8_t*)buf);
  157. return (0);
  158. retry:
  159. if (++try > 5) {
  160. device_printf(dev, "iicbus read failed\n");
  161. return (-1);
  162. }
  163. pause("fcu_read_1", hz);
  164. }
  165. }
  166. static int
  167. fcu_probe(device_t dev)
  168. {
  169. const char *name, *compatible;
  170. struct fcu_softc *sc;
  171. name = ofw_bus_get_name(dev);
  172. compatible = ofw_bus_get_compat(dev);
  173. if (!name)
  174. return (ENXIO);
  175. if (strcmp(name, "fan") != 0 || strcmp(compatible, "fcu") != 0)
  176. return (ENXIO);
  177. sc = device_get_softc(dev);
  178. sc->sc_dev = dev;
  179. sc->sc_addr = iicbus_get_addr(dev);
  180. device_set_desc(dev, "Apple Fan Control Unit");
  181. return (0);
  182. }
  183. static int
  184. fcu_attach(device_t dev)
  185. {
  186. struct fcu_softc *sc;
  187. sc = device_get_softc(dev);
  188. sc->enum_hook.ich_func = fcu_start;
  189. sc->enum_hook.ich_arg = dev;
  190. /* We have to wait until interrupts are enabled. I2C read and write
  191. * only works if the interrupts are available.
  192. * The unin/i2c is controlled by the htpic on unin. But this is not
  193. * the master. The openpic on mac-io is controlling the htpic.
  194. * This one gets attached after the mac-io probing and then the
  195. * interrupts will be available.
  196. */
  197. if (config_intrhook_establish(&sc->enum_hook) != 0)
  198. return (ENOMEM);
  199. return (0);
  200. }
  201. static void
  202. fcu_start(void *xdev)
  203. {
  204. unsigned char buf[1] = { 0xff };
  205. struct fcu_softc *sc;
  206. device_t dev = (device_t)xdev;
  207. sc = device_get_softc(dev);
  208. /* Start the fcu device. */
  209. fcu_write(sc->sc_dev, sc->sc_addr, 0xe, buf, 1);
  210. fcu_write(sc->sc_dev, sc->sc_addr, 0x2e, buf, 1);
  211. fcu_read_1(sc->sc_dev, sc->sc_addr, 0, buf);
  212. fcu_rpm_shift = (buf[0] == 1) ? 2 : 3;
  213. device_printf(dev, "FCU initialized, RPM shift: %d\n",
  214. fcu_rpm_shift);
  215. /* Detect and attach child devices. */
  216. fcu_attach_fans(dev);
  217. config_intrhook_disestablish(&sc->enum_hook);
  218. }
  219. static int
  220. fcu_fan_set_rpm(struct fcu_fan *fan, int rpm)
  221. {
  222. uint8_t reg;
  223. struct fcu_softc *sc;
  224. unsigned char buf[2];
  225. sc = device_get_softc(fan->dev);
  226. /* Clamp to allowed range */
  227. rpm = max(fan->fan.min_rpm, rpm);
  228. rpm = min(fan->fan.max_rpm, rpm);
  229. if (fan->type == FCU_FAN_RPM) {
  230. reg = FCU_RPM_SET(fan->id);
  231. fan->setpoint = rpm;
  232. } else {
  233. device_printf(fan->dev, "Unknown fan type: %d\n", fan->type);
  234. return (ENXIO);
  235. }
  236. buf[0] = rpm >> (8 - fcu_rpm_shift);
  237. buf[1] = rpm << fcu_rpm_shift;
  238. if (fcu_write(sc->sc_dev, sc->sc_addr, reg, buf, 2) < 0)
  239. return (EIO);
  240. return (0);
  241. }
  242. static int
  243. fcu_fan_get_rpm(struct fcu_fan *fan)
  244. {
  245. uint8_t reg;
  246. struct fcu_softc *sc;
  247. uint8_t buff[2] = { 0, 0 };
  248. uint8_t active = 0, avail = 0, fail = 0;
  249. int rpm;
  250. sc = device_get_softc(fan->dev);
  251. if (fan->type == FCU_FAN_RPM) {
  252. /* Check if the fan is available. */
  253. reg = FCU_RPM_AVAILABLE;
  254. if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &avail) < 0)
  255. return (-1);
  256. if ((avail & (1 << fan->id)) == 0) {
  257. device_printf(fan->dev,
  258. "RPM Fan not available ID: %d\n", fan->id);
  259. return (-1);
  260. }
  261. /* Check if we have a failed fan. */
  262. reg = FCU_RPM_FAIL;
  263. if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &fail) < 0)
  264. return (-1);
  265. if ((fail & (1 << fan->id)) != 0) {
  266. device_printf(fan->dev,
  267. "RPM Fan failed ID: %d\n", fan->id);
  268. return (-1);
  269. }
  270. /* Check if fan is active. */
  271. reg = FCU_RPM_ACTIVE;
  272. if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &active) < 0)
  273. return (-1);
  274. if ((active & (1 << fan->id)) == 0) {
  275. device_printf(fan->dev, "RPM Fan not active ID: %d\n",
  276. fan->id);
  277. return (-1);
  278. }
  279. reg = FCU_RPM_READ(fan->id);
  280. } else {
  281. device_printf(fan->dev, "Unknown fan type: %d\n", fan->type);
  282. return (-1);
  283. }
  284. /* It seems that we can read the fans rpm. */
  285. if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, buff) < 0)
  286. return (-1);
  287. rpm = (buff[0] << (8 - fcu_rpm_shift)) | buff[1] >> fcu_rpm_shift;
  288. return (rpm);
  289. }
  290. static int
  291. fcu_fan_set_pwm(struct fcu_fan *fan, int pwm)
  292. {
  293. uint8_t reg;
  294. struct fcu_softc *sc;
  295. uint8_t buf[2];
  296. sc = device_get_softc(fan->dev);
  297. /* Clamp to allowed range */
  298. pwm = max(fan->fan.min_rpm, pwm);
  299. pwm = min(fan->fan.max_rpm, pwm);
  300. if (fan->type == FCU_FAN_PWM) {
  301. reg = FCU_PWM_SGET(fan->id);
  302. if (pwm > 100)
  303. pwm = 100;
  304. if (pwm < 30)
  305. pwm = 30;
  306. fan->setpoint = pwm;
  307. } else {
  308. device_printf(fan->dev, "Unknown fan type: %d\n", fan->type);
  309. return (EIO);
  310. }
  311. buf[0] = (pwm * 2550) / 1000;
  312. if (fcu_write(sc->sc_dev, sc->sc_addr, reg, buf, 1) < 0)
  313. return (EIO);
  314. return (0);
  315. }
  316. static int
  317. fcu_fan_get_pwm(device_t dev, struct fcu_fan *fan, int *pwm, int *rpm)
  318. {
  319. uint8_t reg;
  320. struct fcu_softc *sc;
  321. uint8_t buf[2];
  322. uint8_t active = 0, avail = 0, fail = 0;
  323. sc = device_get_softc(dev);
  324. if (fan->type == FCU_FAN_PWM) {
  325. /* Check if the fan is available. */
  326. reg = FCU_PWM_AVAILABLE;
  327. if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &avail) < 0)
  328. return (-1);
  329. if ((avail & (1 << fan->id)) == 0) {
  330. device_printf(dev, "PWM Fan not available ID: %d\n",
  331. fan->id);
  332. return (-1);
  333. }
  334. /* Check if we have a failed fan. */
  335. reg = FCU_PWM_FAIL;
  336. if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &fail) < 0)
  337. return (-1);
  338. if ((fail & (1 << fan->id)) != 0) {
  339. device_printf(dev, "PWM Fan failed ID: %d\n", fan->id);
  340. return (-1);
  341. }
  342. /* Check if fan is active. */
  343. reg = FCU_PWM_ACTIVE;
  344. if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, &active) < 0)
  345. return (-1);
  346. if ((active & (1 << fan->id)) == 0) {
  347. device_printf(dev, "PWM Fan not active ID: %d\n",
  348. fan->id);
  349. return (-1);
  350. }
  351. reg = FCU_PWM_SGET(fan->id);
  352. } else {
  353. device_printf(dev, "Unknown fan type: %d\n", fan->type);
  354. return (EIO);
  355. }
  356. /* It seems that we can read the fans pwm. */
  357. if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, buf) < 0)
  358. return (-1);
  359. *pwm = (buf[0] * 1000) / 2550;
  360. /* Now read the rpm. */
  361. reg = FCU_PWM_RPM(fan->id);
  362. if (fcu_read_1(sc->sc_dev, sc->sc_addr, reg, buf) < 0)
  363. return (-1);
  364. *rpm = (buf[0] << (8 - fcu_rpm_shift)) | buf[1] >> fcu_rpm_shift;
  365. return (0);
  366. }
  367. /*
  368. * This function returns the number of fans. If we call it the second time
  369. * and we have allocated memory for sc->sc_fans, we fill in the properties.
  370. */
  371. static int
  372. fcu_fill_fan_prop(device_t dev)
  373. {
  374. phandle_t child;
  375. struct fcu_softc *sc;
  376. u_int id[12];
  377. char location[144];
  378. char type[96];
  379. int i = 0, j, len = 0, prop_len, prev_len = 0;
  380. sc = device_get_softc(dev);
  381. child = ofw_bus_get_node(dev);
  382. /* Fill the fan location property. */
  383. prop_len = OF_getprop(child, "hwctrl-location", location,
  384. sizeof(location));
  385. while (len < prop_len) {
  386. if (sc->sc_fans != NULL) {
  387. strcpy(sc->sc_fans[i].fan.name, location + len);
  388. }
  389. prev_len = strlen(location + len) + 1;
  390. len += prev_len;
  391. i++;
  392. }
  393. if (sc->sc_fans == NULL)
  394. return (i);
  395. /* Fill the fan type property. */
  396. len = 0;
  397. i = 0;
  398. prev_len = 0;
  399. prop_len = OF_getprop(child, "hwctrl-type", type, sizeof(type));
  400. while (len < prop_len) {
  401. if (strcmp(type + len, "fan-rpm") == 0)
  402. sc->sc_fans[i].type = FCU_FAN_RPM;
  403. else
  404. sc->sc_fans[i].type = FCU_FAN_PWM;
  405. prev_len = strlen(type + len) + 1;
  406. len += prev_len;
  407. i++;
  408. }
  409. /* Fill the fan ID property. */
  410. prop_len = OF_getprop(child, "hwctrl-id", id, sizeof(id));
  411. for (j = 0; j < i; j++)
  412. sc->sc_fans[j].id = ((id[j] >> 8) & 0x0f) % 8;
  413. /* Fill the fan zone property. */
  414. prop_len = OF_getprop(child, "hwctrl-zone", id, sizeof(id));
  415. for (j = 0; j < i; j++)
  416. sc->sc_fans[j].fan.zone = id[j];
  417. /* Finish setting up fan properties */
  418. for (j = 0; j < i; j++) {
  419. sc->sc_fans[j].dev = sc->sc_dev;
  420. if (sc->sc_fans[j].type == FCU_FAN_RPM) {
  421. sc->sc_fans[j].fan.min_rpm = 4800 >> fcu_rpm_shift;
  422. sc->sc_fans[j].fan.max_rpm = 56000 >> fcu_rpm_shift;
  423. sc->sc_fans[j].setpoint =
  424. fcu_fan_get_rpm(&sc->sc_fans[j]);
  425. sc->sc_fans[j].fan.read =
  426. (int (*)(struct pmac_fan *))(fcu_fan_get_rpm);
  427. sc->sc_fans[j].fan.set =
  428. (int (*)(struct pmac_fan *, int))(fcu_fan_set_rpm);
  429. } else {
  430. sc->sc_fans[j].fan.min_rpm = 30; /* Percent */
  431. sc->sc_fans[j].fan.max_rpm = 100;
  432. sc->sc_fans[j].fan.read = NULL;
  433. sc->sc_fans[j].fan.set =
  434. (int (*)(struct pmac_fan *, int))(fcu_fan_set_pwm);
  435. }
  436. sc->sc_fans[j].fan.default_rpm = sc->sc_fans[j].fan.max_rpm;
  437. }
  438. return (i);
  439. }
  440. static int
  441. fcu_fanrpm_sysctl(SYSCTL_HANDLER_ARGS)
  442. {
  443. device_t fcu;
  444. struct fcu_softc *sc;
  445. struct fcu_fan *fan;
  446. int rpm = 0, pwm = 0, error = 0;
  447. fcu = arg1;
  448. sc = device_get_softc(fcu);
  449. fan = &sc->sc_fans[arg2 & 0x00ff];
  450. if (fan->type == FCU_FAN_RPM) {
  451. rpm = fcu_fan_get_rpm(fan);
  452. if (rpm < 0)
  453. return (EIO);
  454. error = sysctl_handle_int(oidp, &rpm, 0, req);
  455. } else {
  456. error = fcu_fan_get_pwm(fcu, fan, &pwm, &rpm);
  457. if (error < 0)
  458. return (EIO);
  459. switch (arg2 & 0xff00) {
  460. case FCU_PWM_SYSCTL_PWM:
  461. error = sysctl_handle_int(oidp, &pwm, 0, req);
  462. break;
  463. case FCU_PWM_SYSCTL_RPM:
  464. error = sysctl_handle_int(oidp, &rpm, 0, req);
  465. break;
  466. default:
  467. /* This should never happen */
  468. return (EINVAL);
  469. }
  470. }
  471. /* We can only read the RPM from a PWM controlled fan, so return. */
  472. if ((arg2 & 0xff00) == FCU_PWM_SYSCTL_RPM)
  473. return (0);
  474. if (error || !req->newptr)
  475. return (error);
  476. if (fan->type == FCU_FAN_RPM)
  477. return (fcu_fan_set_rpm(fan, rpm));
  478. else
  479. return (fcu_fan_set_pwm(fan, pwm));
  480. }
  481. static void
  482. fcu_attach_fans(device_t dev)
  483. {
  484. struct fcu_softc *sc;
  485. struct sysctl_oid *oid, *fanroot_oid;
  486. struct sysctl_ctx_list *ctx;
  487. char sysctl_name[32];
  488. int i, j;
  489. sc = device_get_softc(dev);
  490. sc->sc_nfans = 0;
  491. /* Count the actual number of fans. */
  492. sc->sc_nfans = fcu_fill_fan_prop(dev);
  493. device_printf(dev, "%d fans detected!\n", sc->sc_nfans);
  494. if (sc->sc_nfans == 0) {
  495. device_printf(dev, "WARNING: No fans detected!\n");
  496. return;
  497. }
  498. sc->sc_fans = malloc(sc->sc_nfans * sizeof(struct fcu_fan), M_FCU,
  499. M_WAITOK | M_ZERO);
  500. ctx = device_get_sysctl_ctx(dev);
  501. fanroot_oid = SYSCTL_ADD_NODE(ctx,
  502. SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "fans",
  503. CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "FCU Fan Information");
  504. /* Now we can fill the properties into the allocated struct. */
  505. sc->sc_nfans = fcu_fill_fan_prop(dev);
  506. /* Register fans with pmac_thermal */
  507. for (i = 0; i < sc->sc_nfans; i++)
  508. pmac_thermal_fan_register(&sc->sc_fans[i].fan);
  509. /* Add sysctls for the fans. */
  510. for (i = 0; i < sc->sc_nfans; i++) {
  511. for (j = 0; j < strlen(sc->sc_fans[i].fan.name); j++) {
  512. sysctl_name[j] = tolower(sc->sc_fans[i].fan.name[j]);
  513. if (isspace(sysctl_name[j]))
  514. sysctl_name[j] = '_';
  515. }
  516. sysctl_name[j] = 0;
  517. if (sc->sc_fans[i].type == FCU_FAN_RPM) {
  518. oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(fanroot_oid),
  519. OID_AUTO, sysctl_name, CTLFLAG_RD | CTLFLAG_MPSAFE,
  520. 0, "Fan Information");
  521. SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
  522. "minrpm", CTLFLAG_RD,
  523. &(sc->sc_fans[i].fan.min_rpm), 0,
  524. "Minimum allowed RPM");
  525. SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
  526. "maxrpm", CTLFLAG_RD,
  527. &(sc->sc_fans[i].fan.max_rpm), 0,
  528. "Maximum allowed RPM");
  529. /* I use i to pass the fan id. */
  530. SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
  531. "rpm", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
  532. dev, i, fcu_fanrpm_sysctl, "I", "Fan RPM");
  533. } else {
  534. fcu_fan_get_pwm(dev, &sc->sc_fans[i],
  535. &sc->sc_fans[i].setpoint,
  536. &sc->sc_fans[i].rpm);
  537. oid = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(fanroot_oid),
  538. OID_AUTO, sysctl_name, CTLFLAG_RD | CTLFLAG_MPSAFE,
  539. 0, "Fan Information");
  540. SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
  541. "minpwm", CTLFLAG_RD,
  542. &(sc->sc_fans[i].fan.min_rpm), 0,
  543. "Minimum allowed PWM in %");
  544. SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
  545. "maxpwm", CTLFLAG_RD,
  546. &(sc->sc_fans[i].fan.max_rpm), 0,
  547. "Maximum allowed PWM in %");
  548. /* I use i to pass the fan id or'ed with the type
  549. * of info I want to display/modify.
  550. */
  551. SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
  552. "pwm", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
  553. dev, FCU_PWM_SYSCTL_PWM | i, fcu_fanrpm_sysctl, "I",
  554. "Fan PWM in %");
  555. SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
  556. "rpm", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
  557. dev, FCU_PWM_SYSCTL_RPM | i, fcu_fanrpm_sysctl, "I",
  558. "Fan RPM");
  559. }
  560. }
  561. /* Dump fan location, type & RPM. */
  562. if (bootverbose) {
  563. device_printf(dev, "Fans\n");
  564. for (i = 0; i < sc->sc_nfans; i++) {
  565. device_printf(dev, "Location: %s type: %d ID: %d "
  566. "RPM: %d\n", sc->sc_fans[i].fan.name,
  567. sc->sc_fans[i].type, sc->sc_fans[i].id,
  568. (sc->sc_fans[i].type == FCU_FAN_RPM) ?
  569. sc->sc_fans[i].setpoint :
  570. sc->sc_fans[i].rpm );
  571. }
  572. }
  573. }