acerhdf.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. /*
  2. * acerhdf - A driver which monitors the temperature
  3. * of the aspire one netbook, turns on/off the fan
  4. * as soon as the upper/lower threshold is reached.
  5. *
  6. * (C) 2009 - Peter Feuerer peter (a) piie.net
  7. * http://piie.net
  8. * 2009 Borislav Petkov <petkovbb@gmail.com>
  9. *
  10. * Inspired by and many thanks to:
  11. * o acerfand - Rachel Greenham
  12. * o acer_ec.pl - Michael Kurz michi.kurz (at) googlemail.com
  13. * - Petr Tomasek tomasek (#) etf,cuni,cz
  14. * - Carlos Corbacho cathectic (at) gmail.com
  15. * o lkml - Matthew Garrett
  16. * - Borislav Petkov
  17. * - Andreas Mohr
  18. *
  19. * This program is free software; you can redistribute it and/or modify
  20. * it under the terms of the GNU General Public License as published by
  21. * the Free Software Foundation; either version 2 of the License, or
  22. * (at your option) any later version.
  23. *
  24. * This program is distributed in the hope that it will be useful,
  25. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  26. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  27. * GNU General Public License for more details.
  28. *
  29. * You should have received a copy of the GNU General Public License
  30. * along with this program; if not, write to the Free Software
  31. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  32. */
  33. #define pr_fmt(fmt) "acerhdf: " fmt
  34. #include <linux/kernel.h>
  35. #include <linux/module.h>
  36. #include <linux/fs.h>
  37. #include <linux/dmi.h>
  38. #include <acpi/acpi_drivers.h>
  39. #include <linux/sched.h>
  40. #include <linux/thermal.h>
  41. #include <linux/platform_device.h>
  42. /*
  43. * The driver is started with "kernel mode off" by default. That means, the BIOS
  44. * is still in control of the fan. In this mode the driver allows to read the
  45. * temperature of the cpu and a userspace tool may take over control of the fan.
  46. * If the driver is switched to "kernel mode" (e.g. via module parameter) the
  47. * driver is in full control of the fan. If you want the module to be started in
  48. * kernel mode by default, define the following:
  49. */
  50. #undef START_IN_KERNEL_MODE
  51. #define DRV_VER "0.5.13"
  52. /*
  53. * According to the Atom N270 datasheet,
  54. * (http://download.intel.com/design/processor/datashts/320032.pdf) the
  55. * CPU's optimal operating limits denoted in junction temperature as
  56. * measured by the on-die thermal monitor are within 0 <= Tj <= 90. So,
  57. * assume 89°C is critical temperature.
  58. */
  59. #define ACERHDF_TEMP_CRIT 89
  60. #define ACERHDF_FAN_OFF 0
  61. #define ACERHDF_FAN_AUTO 1
  62. /*
  63. * No matter what value the user puts into the fanon variable, turn on the fan
  64. * at 80 degree Celsius to prevent hardware damage
  65. */
  66. #define ACERHDF_MAX_FANON 80
  67. /*
  68. * Maximum interval between two temperature checks is 15 seconds, as the die
  69. * can get hot really fast under heavy load (plus we shouldn't forget about
  70. * possible impact of _external_ aggressive sources such as heaters, sun etc.)
  71. */
  72. #define ACERHDF_MAX_INTERVAL 15
  73. #ifdef START_IN_KERNEL_MODE
  74. static int kernelmode = 1;
  75. #else
  76. static int kernelmode;
  77. #endif
  78. static unsigned int interval = 10;
  79. static unsigned int fanon = 63;
  80. static unsigned int fanoff = 58;
  81. static unsigned int verbose;
  82. static unsigned int fanstate = ACERHDF_FAN_AUTO;
  83. static char force_bios[16];
  84. static unsigned int prev_interval;
  85. struct thermal_zone_device *thz_dev;
  86. struct thermal_cooling_device *cl_dev;
  87. struct platform_device *acerhdf_dev;
  88. module_param(kernelmode, uint, 0);
  89. MODULE_PARM_DESC(kernelmode, "Kernel mode fan control on / off");
  90. module_param(interval, uint, 0600);
  91. MODULE_PARM_DESC(interval, "Polling interval of temperature check");
  92. module_param(fanon, uint, 0600);
  93. MODULE_PARM_DESC(fanon, "Turn the fan on above this temperature");
  94. module_param(fanoff, uint, 0600);
  95. MODULE_PARM_DESC(fanoff, "Turn the fan off below this temperature");
  96. module_param(verbose, uint, 0600);
  97. MODULE_PARM_DESC(verbose, "Enable verbose dmesg output");
  98. module_param_string(force_bios, force_bios, 16, 0);
  99. MODULE_PARM_DESC(force_bios, "Force BIOS version and omit BIOS check");
  100. /* BIOS settings */
  101. struct bios_settings_t {
  102. const char *vendor;
  103. const char *version;
  104. unsigned char fanreg;
  105. unsigned char tempreg;
  106. unsigned char fancmd[2]; /* fan off and auto commands */
  107. };
  108. /* Register addresses and values for different BIOS versions */
  109. static const struct bios_settings_t bios_tbl[] = {
  110. {"Acer", "v0.3109", 0x55, 0x58, {0x1f, 0x00} },
  111. {"Acer", "v0.3114", 0x55, 0x58, {0x1f, 0x00} },
  112. {"Acer", "v0.3301", 0x55, 0x58, {0xaf, 0x00} },
  113. {"Acer", "v0.3304", 0x55, 0x58, {0xaf, 0x00} },
  114. {"Acer", "v0.3305", 0x55, 0x58, {0xaf, 0x00} },
  115. {"Acer", "v0.3308", 0x55, 0x58, {0x21, 0x00} },
  116. {"Acer", "v0.3309", 0x55, 0x58, {0x21, 0x00} },
  117. {"Acer", "v0.3310", 0x55, 0x58, {0x21, 0x00} },
  118. {"Gateway", "v0.3103", 0x55, 0x58, {0x21, 0x00} },
  119. {"Packard Bell", "v0.3105", 0x55, 0x58, {0x21, 0x00} },
  120. {"", "", 0, 0, {0, 0} }
  121. };
  122. static const struct bios_settings_t *bios_cfg __read_mostly;
  123. static int acerhdf_get_temp(int *temp)
  124. {
  125. u8 read_temp;
  126. if (ec_read(bios_cfg->tempreg, &read_temp))
  127. return -EINVAL;
  128. *temp = read_temp;
  129. return 0;
  130. }
  131. static int acerhdf_get_fanstate(int *state)
  132. {
  133. u8 fan;
  134. bool tmp;
  135. if (ec_read(bios_cfg->fanreg, &fan))
  136. return -EINVAL;
  137. tmp = (fan == bios_cfg->fancmd[ACERHDF_FAN_OFF]);
  138. *state = tmp ? ACERHDF_FAN_OFF : ACERHDF_FAN_AUTO;
  139. return 0;
  140. }
  141. static void acerhdf_change_fanstate(int state)
  142. {
  143. unsigned char cmd;
  144. if (verbose)
  145. pr_notice("fan %s\n", (state == ACERHDF_FAN_OFF) ?
  146. "OFF" : "ON");
  147. if ((state != ACERHDF_FAN_OFF) && (state != ACERHDF_FAN_AUTO)) {
  148. pr_err("invalid fan state %d requested, setting to auto!\n",
  149. state);
  150. state = ACERHDF_FAN_AUTO;
  151. }
  152. cmd = bios_cfg->fancmd[state];
  153. fanstate = state;
  154. ec_write(bios_cfg->fanreg, cmd);
  155. }
  156. static void acerhdf_check_param(struct thermal_zone_device *thermal)
  157. {
  158. if (fanon > ACERHDF_MAX_FANON) {
  159. pr_err("fanon temperature too high, set to %d\n",
  160. ACERHDF_MAX_FANON);
  161. fanon = ACERHDF_MAX_FANON;
  162. }
  163. if (kernelmode && prev_interval != interval) {
  164. if (interval > ACERHDF_MAX_INTERVAL) {
  165. pr_err("interval too high, set to %d\n",
  166. ACERHDF_MAX_INTERVAL);
  167. interval = ACERHDF_MAX_INTERVAL;
  168. }
  169. if (verbose)
  170. pr_notice("interval changed to: %d\n",
  171. interval);
  172. thermal->polling_delay = interval*1000;
  173. prev_interval = interval;
  174. }
  175. }
  176. /*
  177. * This is the thermal zone callback which does the delayed polling of the fan
  178. * state. We do check /sysfs-originating settings here in acerhdf_check_param()
  179. * as late as the polling interval is since we can't do that in the respective
  180. * accessors of the module parameters.
  181. */
  182. static int acerhdf_get_ec_temp(struct thermal_zone_device *thermal,
  183. unsigned long *t)
  184. {
  185. int temp, err = 0;
  186. acerhdf_check_param(thermal);
  187. err = acerhdf_get_temp(&temp);
  188. if (err)
  189. return err;
  190. if (verbose)
  191. pr_notice("temp %d\n", temp);
  192. *t = temp;
  193. return 0;
  194. }
  195. static int acerhdf_bind(struct thermal_zone_device *thermal,
  196. struct thermal_cooling_device *cdev)
  197. {
  198. /* if the cooling device is the one from acerhdf bind it */
  199. if (cdev != cl_dev)
  200. return 0;
  201. if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) {
  202. pr_err("error binding cooling dev\n");
  203. return -EINVAL;
  204. }
  205. return 0;
  206. }
  207. static int acerhdf_unbind(struct thermal_zone_device *thermal,
  208. struct thermal_cooling_device *cdev)
  209. {
  210. if (cdev != cl_dev)
  211. return 0;
  212. if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) {
  213. pr_err("error unbinding cooling dev\n");
  214. return -EINVAL;
  215. }
  216. return 0;
  217. }
  218. static inline void acerhdf_revert_to_bios_mode(void)
  219. {
  220. acerhdf_change_fanstate(ACERHDF_FAN_AUTO);
  221. kernelmode = 0;
  222. if (thz_dev)
  223. thz_dev->polling_delay = 0;
  224. pr_notice("kernel mode fan control OFF\n");
  225. }
  226. static inline void acerhdf_enable_kernelmode(void)
  227. {
  228. kernelmode = 1;
  229. thz_dev->polling_delay = interval*1000;
  230. thermal_zone_device_update(thz_dev);
  231. pr_notice("kernel mode fan control ON\n");
  232. }
  233. static int acerhdf_get_mode(struct thermal_zone_device *thermal,
  234. enum thermal_device_mode *mode)
  235. {
  236. if (verbose)
  237. pr_notice("kernel mode fan control %d\n", kernelmode);
  238. *mode = (kernelmode) ? THERMAL_DEVICE_ENABLED
  239. : THERMAL_DEVICE_DISABLED;
  240. return 0;
  241. }
  242. /*
  243. * set operation mode;
  244. * enabled: the thermal layer of the kernel takes care about
  245. * the temperature and the fan.
  246. * disabled: the BIOS takes control of the fan.
  247. */
  248. static int acerhdf_set_mode(struct thermal_zone_device *thermal,
  249. enum thermal_device_mode mode)
  250. {
  251. if (mode == THERMAL_DEVICE_DISABLED && kernelmode)
  252. acerhdf_revert_to_bios_mode();
  253. else if (mode == THERMAL_DEVICE_ENABLED && !kernelmode)
  254. acerhdf_enable_kernelmode();
  255. return 0;
  256. }
  257. static int acerhdf_get_trip_type(struct thermal_zone_device *thermal, int trip,
  258. enum thermal_trip_type *type)
  259. {
  260. if (trip == 0)
  261. *type = THERMAL_TRIP_ACTIVE;
  262. return 0;
  263. }
  264. static int acerhdf_get_trip_temp(struct thermal_zone_device *thermal, int trip,
  265. unsigned long *temp)
  266. {
  267. if (trip == 0)
  268. *temp = fanon;
  269. return 0;
  270. }
  271. static int acerhdf_get_crit_temp(struct thermal_zone_device *thermal,
  272. unsigned long *temperature)
  273. {
  274. *temperature = ACERHDF_TEMP_CRIT;
  275. return 0;
  276. }
  277. /* bind callback functions to thermalzone */
  278. struct thermal_zone_device_ops acerhdf_dev_ops = {
  279. .bind = acerhdf_bind,
  280. .unbind = acerhdf_unbind,
  281. .get_temp = acerhdf_get_ec_temp,
  282. .get_mode = acerhdf_get_mode,
  283. .set_mode = acerhdf_set_mode,
  284. .get_trip_type = acerhdf_get_trip_type,
  285. .get_trip_temp = acerhdf_get_trip_temp,
  286. .get_crit_temp = acerhdf_get_crit_temp,
  287. };
  288. /*
  289. * cooling device callback functions
  290. * get maximal fan cooling state
  291. */
  292. static int acerhdf_get_max_state(struct thermal_cooling_device *cdev,
  293. unsigned long *state)
  294. {
  295. *state = 1;
  296. return 0;
  297. }
  298. static int acerhdf_get_cur_state(struct thermal_cooling_device *cdev,
  299. unsigned long *state)
  300. {
  301. int err = 0, tmp;
  302. err = acerhdf_get_fanstate(&tmp);
  303. if (err)
  304. return err;
  305. *state = (tmp == ACERHDF_FAN_AUTO) ? 1 : 0;
  306. return 0;
  307. }
  308. /* change current fan state - is overwritten when running in kernel mode */
  309. static int acerhdf_set_cur_state(struct thermal_cooling_device *cdev,
  310. unsigned long state)
  311. {
  312. int cur_temp, cur_state, err = 0;
  313. if (!kernelmode)
  314. return 0;
  315. err = acerhdf_get_temp(&cur_temp);
  316. if (err) {
  317. pr_err("error reading temperature, hand off control to BIOS\n");
  318. goto err_out;
  319. }
  320. err = acerhdf_get_fanstate(&cur_state);
  321. if (err) {
  322. pr_err("error reading fan state, hand off control to BIOS\n");
  323. goto err_out;
  324. }
  325. if (state == 0) {
  326. /* turn fan off only if below fanoff temperature */
  327. if ((cur_state == ACERHDF_FAN_AUTO) &&
  328. (cur_temp < fanoff))
  329. acerhdf_change_fanstate(ACERHDF_FAN_OFF);
  330. } else {
  331. if (cur_state == ACERHDF_FAN_OFF)
  332. acerhdf_change_fanstate(ACERHDF_FAN_AUTO);
  333. }
  334. return 0;
  335. err_out:
  336. acerhdf_revert_to_bios_mode();
  337. return -EINVAL;
  338. }
  339. /* bind fan callbacks to fan device */
  340. struct thermal_cooling_device_ops acerhdf_cooling_ops = {
  341. .get_max_state = acerhdf_get_max_state,
  342. .get_cur_state = acerhdf_get_cur_state,
  343. .set_cur_state = acerhdf_set_cur_state,
  344. };
  345. /* suspend / resume functionality */
  346. static int acerhdf_suspend(struct platform_device *dev, pm_message_t state)
  347. {
  348. if (kernelmode)
  349. acerhdf_change_fanstate(ACERHDF_FAN_AUTO);
  350. if (verbose)
  351. pr_notice("going suspend\n");
  352. return 0;
  353. }
  354. static int acerhdf_resume(struct platform_device *device)
  355. {
  356. if (verbose)
  357. pr_notice("resuming\n");
  358. return 0;
  359. }
  360. static int __devinit acerhdf_probe(struct platform_device *device)
  361. {
  362. return 0;
  363. }
  364. static int acerhdf_remove(struct platform_device *device)
  365. {
  366. return 0;
  367. }
  368. struct platform_driver acerhdf_drv = {
  369. .driver = {
  370. .name = "acerhdf",
  371. .owner = THIS_MODULE,
  372. },
  373. .probe = acerhdf_probe,
  374. .remove = acerhdf_remove,
  375. .suspend = acerhdf_suspend,
  376. .resume = acerhdf_resume,
  377. };
  378. /* check hardware */
  379. static int acerhdf_check_hardware(void)
  380. {
  381. char const *vendor, *version, *product;
  382. int i;
  383. /* get BIOS data */
  384. vendor = dmi_get_system_info(DMI_SYS_VENDOR);
  385. version = dmi_get_system_info(DMI_BIOS_VERSION);
  386. product = dmi_get_system_info(DMI_PRODUCT_NAME);
  387. pr_info("Acer Aspire One Fan driver, v.%s\n", DRV_VER);
  388. if (!force_bios[0]) {
  389. if (strncmp(product, "AO", 2)) {
  390. pr_err("no Aspire One hardware found\n");
  391. return -EINVAL;
  392. }
  393. } else {
  394. pr_info("forcing BIOS version: %s\n", version);
  395. version = force_bios;
  396. kernelmode = 0;
  397. }
  398. if (verbose)
  399. pr_info("BIOS info: %s %s, product: %s\n",
  400. vendor, version, product);
  401. /* search BIOS version and vendor in BIOS settings table */
  402. for (i = 0; bios_tbl[i].version[0]; i++) {
  403. if (!strcmp(bios_tbl[i].vendor, vendor) &&
  404. !strcmp(bios_tbl[i].version, version)) {
  405. bios_cfg = &bios_tbl[i];
  406. break;
  407. }
  408. }
  409. if (!bios_cfg) {
  410. pr_err("unknown (unsupported) BIOS version %s/%s, "
  411. "please report, aborting!\n", vendor, version);
  412. return -EINVAL;
  413. }
  414. /*
  415. * if started with kernel mode off, prevent the kernel from switching
  416. * off the fan
  417. */
  418. if (!kernelmode) {
  419. pr_notice("Fan control off, to enable do:\n");
  420. pr_notice("echo -n \"enabled\" > "
  421. "/sys/class/thermal/thermal_zone0/mode\n");
  422. }
  423. return 0;
  424. }
  425. static int acerhdf_register_platform(void)
  426. {
  427. int err = 0;
  428. err = platform_driver_register(&acerhdf_drv);
  429. if (err)
  430. return err;
  431. acerhdf_dev = platform_device_alloc("acerhdf", -1);
  432. platform_device_add(acerhdf_dev);
  433. return 0;
  434. }
  435. static void acerhdf_unregister_platform(void)
  436. {
  437. if (!acerhdf_dev)
  438. return;
  439. platform_device_del(acerhdf_dev);
  440. platform_driver_unregister(&acerhdf_drv);
  441. }
  442. static int acerhdf_register_thermal(void)
  443. {
  444. cl_dev = thermal_cooling_device_register("acerhdf-fan", NULL,
  445. &acerhdf_cooling_ops);
  446. if (IS_ERR(cl_dev))
  447. return -EINVAL;
  448. thz_dev = thermal_zone_device_register("acerhdf", 1, NULL,
  449. &acerhdf_dev_ops, 0, 0, 0,
  450. (kernelmode) ? interval*1000 : 0);
  451. if (IS_ERR(thz_dev))
  452. return -EINVAL;
  453. return 0;
  454. }
  455. static void acerhdf_unregister_thermal(void)
  456. {
  457. if (cl_dev) {
  458. thermal_cooling_device_unregister(cl_dev);
  459. cl_dev = NULL;
  460. }
  461. if (thz_dev) {
  462. thermal_zone_device_unregister(thz_dev);
  463. thz_dev = NULL;
  464. }
  465. }
  466. static int __init acerhdf_init(void)
  467. {
  468. int err = 0;
  469. err = acerhdf_check_hardware();
  470. if (err)
  471. goto out_err;
  472. err = acerhdf_register_platform();
  473. if (err)
  474. goto err_unreg;
  475. err = acerhdf_register_thermal();
  476. if (err)
  477. goto err_unreg;
  478. return 0;
  479. err_unreg:
  480. acerhdf_unregister_thermal();
  481. acerhdf_unregister_platform();
  482. out_err:
  483. return -ENODEV;
  484. }
  485. static void __exit acerhdf_exit(void)
  486. {
  487. acerhdf_change_fanstate(ACERHDF_FAN_AUTO);
  488. acerhdf_unregister_thermal();
  489. acerhdf_unregister_platform();
  490. }
  491. MODULE_LICENSE("GPL");
  492. MODULE_AUTHOR("Peter Feuerer");
  493. MODULE_DESCRIPTION("Aspire One temperature and fan driver");
  494. MODULE_ALIAS("dmi:*:*Acer*:*:");
  495. MODULE_ALIAS("dmi:*:*Gateway*:*:");
  496. MODULE_ALIAS("dmi:*:*Packard Bell*:*:");
  497. module_init(acerhdf_init);
  498. module_exit(acerhdf_exit);