compal-laptop.c 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090
  1. /*-*-linux-c-*-*/
  2. /*
  3. Copyright (C) 2008 Cezary Jackiewicz <cezary.jackiewicz (at) gmail.com>
  4. based on MSI driver
  5. Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
  6. This program is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful, but
  11. WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program; if not, write to the Free Software
  16. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  17. 02110-1301, USA.
  18. */
  19. /*
  20. * compal-laptop.c - Compal laptop support.
  21. *
  22. * This driver exports a few files in /sys/devices/platform/compal-laptop/:
  23. * wake_up_XXX Whether or not we listen to such wake up events (rw)
  24. *
  25. * In addition to these platform device attributes the driver
  26. * registers itself in the Linux backlight control, power_supply, rfkill
  27. * and hwmon subsystem and is available to userspace under:
  28. *
  29. * /sys/class/backlight/compal-laptop/
  30. * /sys/class/power_supply/compal-laptop/
  31. * /sys/class/rfkill/rfkillX/
  32. * /sys/class/hwmon/hwmonX/
  33. *
  34. * Notes on the power_supply battery interface:
  35. * - the "minimum" design voltage is *the* design voltage
  36. * - the ambient temperature is the average battery temperature
  37. * and the value is an educated guess (see commented code below)
  38. *
  39. *
  40. * This driver might work on other laptops produced by Compal. If you
  41. * want to try it you can pass force=1 as argument to the module which
  42. * will force it to load even when the DMI data doesn't identify the
  43. * laptop as compatible.
  44. *
  45. * Lots of data available at:
  46. * http://service1.marasst.com/Compal/JHL90_91/Service%20Manual/
  47. * JHL90%20service%20manual-Final-0725.pdf
  48. *
  49. *
  50. *
  51. * Support for the Compal JHL90 added by Roald Frederickx
  52. * (roald.frederickx@gmail.com):
  53. * Driver got large revision. Added functionalities: backlight
  54. * power, wake_on_XXX, a hwmon and power_supply interface.
  55. *
  56. * In case this gets merged into the kernel source: I want to dedicate this
  57. * to Kasper Meerts, the awesome guy who showed me Linux and C!
  58. */
  59. /* NOTE: currently the wake_on_XXX, hwmon and power_supply interfaces are
  60. * only enabled on a JHL90 board until it is verified that they work on the
  61. * other boards too. See the extra_features variable. */
  62. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  63. #include <linux/module.h>
  64. #include <linux/kernel.h>
  65. #include <linux/init.h>
  66. #include <linux/acpi.h>
  67. #include <linux/dmi.h>
  68. #include <linux/backlight.h>
  69. #include <linux/platform_device.h>
  70. #include <linux/rfkill.h>
  71. #include <linux/hwmon.h>
  72. #include <linux/hwmon-sysfs.h>
  73. #include <linux/power_supply.h>
  74. #include <linux/fb.h>
  75. /* ======= */
  76. /* Defines */
  77. /* ======= */
  78. #define DRIVER_NAME "compal-laptop"
  79. #define DRIVER_VERSION "0.2.7"
  80. #define BACKLIGHT_LEVEL_ADDR 0xB9
  81. #define BACKLIGHT_LEVEL_MAX 7
  82. #define BACKLIGHT_STATE_ADDR 0x59
  83. #define BACKLIGHT_STATE_ON_DATA 0xE1
  84. #define BACKLIGHT_STATE_OFF_DATA 0xE2
  85. #define WAKE_UP_ADDR 0xA4
  86. #define WAKE_UP_PME (1 << 0)
  87. #define WAKE_UP_MODEM (1 << 1)
  88. #define WAKE_UP_LAN (1 << 2)
  89. #define WAKE_UP_WLAN (1 << 4)
  90. #define WAKE_UP_KEY (1 << 6)
  91. #define WAKE_UP_MOUSE (1 << 7)
  92. #define WIRELESS_ADDR 0xBB
  93. #define WIRELESS_WLAN (1 << 0)
  94. #define WIRELESS_BT (1 << 1)
  95. #define WIRELESS_WLAN_EXISTS (1 << 2)
  96. #define WIRELESS_BT_EXISTS (1 << 3)
  97. #define WIRELESS_KILLSWITCH (1 << 4)
  98. #define PWM_ADDRESS 0x46
  99. #define PWM_DISABLE_ADDR 0x59
  100. #define PWM_DISABLE_DATA 0xA5
  101. #define PWM_ENABLE_ADDR 0x59
  102. #define PWM_ENABLE_DATA 0xA8
  103. #define FAN_ADDRESS 0x46
  104. #define FAN_DATA 0x81
  105. #define FAN_FULL_ON_CMD 0x59 /* Doesn't seem to work. Just */
  106. #define FAN_FULL_ON_ENABLE 0x76 /* force the pwm signal to its */
  107. #define FAN_FULL_ON_DISABLE 0x77 /* maximum value instead */
  108. #define TEMP_CPU 0xB0
  109. #define TEMP_CPU_LOCAL 0xB1
  110. #define TEMP_CPU_DTS 0xB5
  111. #define TEMP_NORTHBRIDGE 0xB6
  112. #define TEMP_VGA 0xB4
  113. #define TEMP_SKIN 0xB2
  114. #define BAT_MANUFACTURER_NAME_ADDR 0x10
  115. #define BAT_MANUFACTURER_NAME_LEN 9
  116. #define BAT_MODEL_NAME_ADDR 0x19
  117. #define BAT_MODEL_NAME_LEN 6
  118. #define BAT_SERIAL_NUMBER_ADDR 0xC4
  119. #define BAT_SERIAL_NUMBER_LEN 5
  120. #define BAT_CHARGE_NOW 0xC2
  121. #define BAT_CHARGE_DESIGN 0xCA
  122. #define BAT_VOLTAGE_NOW 0xC6
  123. #define BAT_VOLTAGE_DESIGN 0xC8
  124. #define BAT_CURRENT_NOW 0xD0
  125. #define BAT_CURRENT_AVG 0xD2
  126. #define BAT_POWER 0xD4
  127. #define BAT_CAPACITY 0xCE
  128. #define BAT_TEMP 0xD6
  129. #define BAT_TEMP_AVG 0xD7
  130. #define BAT_STATUS0 0xC1
  131. #define BAT_STATUS1 0xF0
  132. #define BAT_STATUS2 0xF1
  133. #define BAT_STOP_CHARGE1 0xF2
  134. #define BAT_STOP_CHARGE2 0xF3
  135. #define BAT_S0_DISCHARGE (1 << 0)
  136. #define BAT_S0_DISCHRG_CRITICAL (1 << 2)
  137. #define BAT_S0_LOW (1 << 3)
  138. #define BAT_S0_CHARGING (1 << 1)
  139. #define BAT_S0_AC (1 << 7)
  140. #define BAT_S1_EXISTS (1 << 0)
  141. #define BAT_S1_FULL (1 << 1)
  142. #define BAT_S1_EMPTY (1 << 2)
  143. #define BAT_S1_LiION_OR_NiMH (1 << 7)
  144. #define BAT_S2_LOW_LOW (1 << 0)
  145. #define BAT_STOP_CHRG1_BAD_CELL (1 << 1)
  146. #define BAT_STOP_CHRG1_COMM_FAIL (1 << 2)
  147. #define BAT_STOP_CHRG1_OVERVOLTAGE (1 << 6)
  148. #define BAT_STOP_CHRG1_OVERTEMPERATURE (1 << 7)
  149. /* ======= */
  150. /* Structs */
  151. /* ======= */
  152. struct compal_data{
  153. /* Fan control */
  154. int pwm_enable; /* 0:full on, 1:set by pwm1, 2:control by motherboard */
  155. unsigned char curr_pwm;
  156. /* Power supply */
  157. struct power_supply psy;
  158. struct power_supply_info psy_info;
  159. char bat_model_name[BAT_MODEL_NAME_LEN + 1];
  160. char bat_manufacturer_name[BAT_MANUFACTURER_NAME_LEN + 1];
  161. char bat_serial_number[BAT_SERIAL_NUMBER_LEN + 1];
  162. };
  163. /* =============== */
  164. /* General globals */
  165. /* =============== */
  166. static bool force;
  167. module_param(force, bool, 0);
  168. MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
  169. /* Support for the wake_on_XXX, hwmon and power_supply interface. Currently
  170. * only gets enabled on a JHL90 board. Might work with the others too */
  171. static bool extra_features;
  172. /* Nasty stuff. For some reason the fan control is very un-linear. I've
  173. * come up with these values by looping through the possible inputs and
  174. * watching the output of address 0x4F (do an ec_transaction writing 0x33
  175. * into 0x4F and read a few bytes from the output, like so:
  176. * u8 writeData = 0x33;
  177. * ec_transaction(0x4F, &writeData, 1, buffer, 32);
  178. * That address is labeled "fan1 table information" in the service manual.
  179. * It should be clear which value in 'buffer' changes). This seems to be
  180. * related to fan speed. It isn't a proper 'realtime' fan speed value
  181. * though, because physically stopping or speeding up the fan doesn't
  182. * change it. It might be the average voltage or current of the pwm output.
  183. * Nevertheless, it is more fine-grained than the actual RPM reading */
  184. static const unsigned char pwm_lookup_table[256] = {
  185. 0, 0, 0, 1, 1, 1, 2, 253, 254, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6,
  186. 7, 7, 7, 8, 86, 86, 9, 9, 9, 10, 10, 10, 11, 92, 92, 12, 12, 95,
  187. 13, 66, 66, 14, 14, 98, 15, 15, 15, 16, 16, 67, 17, 17, 72, 18, 70,
  188. 75, 19, 90, 90, 73, 73, 73, 21, 21, 91, 91, 91, 96, 23, 94, 94, 94,
  189. 94, 94, 94, 94, 94, 94, 94, 141, 141, 238, 223, 192, 139, 139, 139,
  190. 139, 139, 142, 142, 142, 142, 142, 78, 78, 78, 78, 78, 76, 76, 76,
  191. 76, 76, 79, 79, 79, 79, 79, 79, 79, 20, 20, 20, 20, 20, 22, 22, 22,
  192. 22, 22, 24, 24, 24, 24, 24, 24, 219, 219, 219, 219, 219, 219, 219,
  193. 219, 27, 27, 188, 188, 28, 28, 28, 29, 186, 186, 186, 186, 186,
  194. 186, 186, 186, 186, 186, 31, 31, 31, 31, 31, 32, 32, 32, 41, 33,
  195. 33, 33, 33, 33, 252, 252, 34, 34, 34, 43, 35, 35, 35, 36, 36, 38,
  196. 206, 206, 206, 206, 206, 206, 206, 206, 206, 37, 37, 37, 46, 46,
  197. 47, 47, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 48, 48,
  198. 48, 48, 48, 40, 40, 40, 49, 42, 42, 42, 42, 42, 42, 42, 42, 44,
  199. 189, 189, 189, 189, 54, 54, 45, 45, 45, 45, 45, 45, 45, 45, 251,
  200. 191, 199, 199, 199, 199, 199, 215, 215, 215, 215, 187, 187, 187,
  201. 187, 187, 193, 50
  202. };
  203. /* ========================= */
  204. /* Hardware access functions */
  205. /* ========================= */
  206. /* General access */
  207. static u8 ec_read_u8(u8 addr)
  208. {
  209. u8 value;
  210. ec_read(addr, &value);
  211. return value;
  212. }
  213. static s8 ec_read_s8(u8 addr)
  214. {
  215. return (s8)ec_read_u8(addr);
  216. }
  217. static u16 ec_read_u16(u8 addr)
  218. {
  219. int hi, lo;
  220. lo = ec_read_u8(addr);
  221. hi = ec_read_u8(addr + 1);
  222. return (hi << 8) + lo;
  223. }
  224. static s16 ec_read_s16(u8 addr)
  225. {
  226. return (s16) ec_read_u16(addr);
  227. }
  228. static void ec_read_sequence(u8 addr, u8 *buf, int len)
  229. {
  230. int i;
  231. for (i = 0; i < len; i++)
  232. ec_read(addr + i, buf + i);
  233. }
  234. /* Backlight access */
  235. static int set_backlight_level(int level)
  236. {
  237. if (level < 0 || level > BACKLIGHT_LEVEL_MAX)
  238. return -EINVAL;
  239. ec_write(BACKLIGHT_LEVEL_ADDR, level);
  240. return 0;
  241. }
  242. static int get_backlight_level(void)
  243. {
  244. return (int) ec_read_u8(BACKLIGHT_LEVEL_ADDR);
  245. }
  246. static void set_backlight_state(bool on)
  247. {
  248. u8 data = on ? BACKLIGHT_STATE_ON_DATA : BACKLIGHT_STATE_OFF_DATA;
  249. ec_transaction(BACKLIGHT_STATE_ADDR, &data, 1, NULL, 0);
  250. }
  251. /* Fan control access */
  252. static void pwm_enable_control(void)
  253. {
  254. unsigned char writeData = PWM_ENABLE_DATA;
  255. ec_transaction(PWM_ENABLE_ADDR, &writeData, 1, NULL, 0);
  256. }
  257. static void pwm_disable_control(void)
  258. {
  259. unsigned char writeData = PWM_DISABLE_DATA;
  260. ec_transaction(PWM_DISABLE_ADDR, &writeData, 1, NULL, 0);
  261. }
  262. static void set_pwm(int pwm)
  263. {
  264. ec_transaction(PWM_ADDRESS, &pwm_lookup_table[pwm], 1, NULL, 0);
  265. }
  266. static int get_fan_rpm(void)
  267. {
  268. u8 value, data = FAN_DATA;
  269. ec_transaction(FAN_ADDRESS, &data, 1, &value, 1);
  270. return 100 * (int)value;
  271. }
  272. /* =================== */
  273. /* Interface functions */
  274. /* =================== */
  275. /* Backlight interface */
  276. static int bl_get_brightness(struct backlight_device *b)
  277. {
  278. return get_backlight_level();
  279. }
  280. static int bl_update_status(struct backlight_device *b)
  281. {
  282. int ret = set_backlight_level(b->props.brightness);
  283. if (ret)
  284. return ret;
  285. set_backlight_state((b->props.power == FB_BLANK_UNBLANK)
  286. && !(b->props.state & BL_CORE_SUSPENDED)
  287. && !(b->props.state & BL_CORE_FBBLANK));
  288. return 0;
  289. }
  290. static const struct backlight_ops compalbl_ops = {
  291. .get_brightness = bl_get_brightness,
  292. .update_status = bl_update_status,
  293. };
  294. /* Wireless interface */
  295. static int compal_rfkill_set(void *data, bool blocked)
  296. {
  297. unsigned long radio = (unsigned long) data;
  298. u8 result = ec_read_u8(WIRELESS_ADDR);
  299. u8 value;
  300. if (!blocked)
  301. value = (u8) (result | radio);
  302. else
  303. value = (u8) (result & ~radio);
  304. ec_write(WIRELESS_ADDR, value);
  305. return 0;
  306. }
  307. static void compal_rfkill_poll(struct rfkill *rfkill, void *data)
  308. {
  309. u8 result = ec_read_u8(WIRELESS_ADDR);
  310. bool hw_blocked = !(result & WIRELESS_KILLSWITCH);
  311. rfkill_set_hw_state(rfkill, hw_blocked);
  312. }
  313. static const struct rfkill_ops compal_rfkill_ops = {
  314. .poll = compal_rfkill_poll,
  315. .set_block = compal_rfkill_set,
  316. };
  317. /* Wake_up interface */
  318. #define SIMPLE_MASKED_STORE_SHOW(NAME, ADDR, MASK) \
  319. static ssize_t NAME##_show(struct device *dev, \
  320. struct device_attribute *attr, char *buf) \
  321. { \
  322. return sprintf(buf, "%d\n", ((ec_read_u8(ADDR) & MASK) != 0)); \
  323. } \
  324. static ssize_t NAME##_store(struct device *dev, \
  325. struct device_attribute *attr, const char *buf, size_t count) \
  326. { \
  327. int state; \
  328. u8 old_val = ec_read_u8(ADDR); \
  329. if (sscanf(buf, "%d", &state) != 1 || (state < 0 || state > 1)) \
  330. return -EINVAL; \
  331. ec_write(ADDR, state ? (old_val | MASK) : (old_val & ~MASK)); \
  332. return count; \
  333. }
  334. SIMPLE_MASKED_STORE_SHOW(wake_up_pme, WAKE_UP_ADDR, WAKE_UP_PME)
  335. SIMPLE_MASKED_STORE_SHOW(wake_up_modem, WAKE_UP_ADDR, WAKE_UP_MODEM)
  336. SIMPLE_MASKED_STORE_SHOW(wake_up_lan, WAKE_UP_ADDR, WAKE_UP_LAN)
  337. SIMPLE_MASKED_STORE_SHOW(wake_up_wlan, WAKE_UP_ADDR, WAKE_UP_WLAN)
  338. SIMPLE_MASKED_STORE_SHOW(wake_up_key, WAKE_UP_ADDR, WAKE_UP_KEY)
  339. SIMPLE_MASKED_STORE_SHOW(wake_up_mouse, WAKE_UP_ADDR, WAKE_UP_MOUSE)
  340. /* Fan control interface */
  341. static ssize_t pwm_enable_show(struct device *dev,
  342. struct device_attribute *attr, char *buf)
  343. {
  344. struct compal_data *data = dev_get_drvdata(dev);
  345. return sprintf(buf, "%d\n", data->pwm_enable);
  346. }
  347. static ssize_t pwm_enable_store(struct device *dev,
  348. struct device_attribute *attr, const char *buf, size_t count)
  349. {
  350. struct compal_data *data = dev_get_drvdata(dev);
  351. long val;
  352. int err;
  353. err = kstrtol(buf, 10, &val);
  354. if (err)
  355. return err;
  356. if (val < 0)
  357. return -EINVAL;
  358. data->pwm_enable = val;
  359. switch (val) {
  360. case 0: /* Full speed */
  361. pwm_enable_control();
  362. set_pwm(255);
  363. break;
  364. case 1: /* As set by pwm1 */
  365. pwm_enable_control();
  366. set_pwm(data->curr_pwm);
  367. break;
  368. default: /* Control by motherboard */
  369. pwm_disable_control();
  370. break;
  371. }
  372. return count;
  373. }
  374. static ssize_t pwm_show(struct device *dev, struct device_attribute *attr,
  375. char *buf)
  376. {
  377. struct compal_data *data = dev_get_drvdata(dev);
  378. return sprintf(buf, "%hhu\n", data->curr_pwm);
  379. }
  380. static ssize_t pwm_store(struct device *dev, struct device_attribute *attr,
  381. const char *buf, size_t count)
  382. {
  383. struct compal_data *data = dev_get_drvdata(dev);
  384. long val;
  385. int err;
  386. err = kstrtol(buf, 10, &val);
  387. if (err)
  388. return err;
  389. if (val < 0 || val > 255)
  390. return -EINVAL;
  391. data->curr_pwm = val;
  392. if (data->pwm_enable != 1)
  393. return count;
  394. set_pwm(val);
  395. return count;
  396. }
  397. static ssize_t fan_show(struct device *dev, struct device_attribute *attr,
  398. char *buf)
  399. {
  400. return sprintf(buf, "%d\n", get_fan_rpm());
  401. }
  402. /* Temperature interface */
  403. #define TEMPERATURE_SHOW_TEMP_AND_LABEL(POSTFIX, ADDRESS, LABEL) \
  404. static ssize_t temp_##POSTFIX(struct device *dev, \
  405. struct device_attribute *attr, char *buf) \
  406. { \
  407. return sprintf(buf, "%d\n", 1000 * (int)ec_read_s8(ADDRESS)); \
  408. } \
  409. static ssize_t label_##POSTFIX(struct device *dev, \
  410. struct device_attribute *attr, char *buf) \
  411. { \
  412. return sprintf(buf, "%s\n", LABEL); \
  413. }
  414. /* Labels as in service guide */
  415. TEMPERATURE_SHOW_TEMP_AND_LABEL(cpu, TEMP_CPU, "CPU_TEMP");
  416. TEMPERATURE_SHOW_TEMP_AND_LABEL(cpu_local, TEMP_CPU_LOCAL, "CPU_TEMP_LOCAL");
  417. TEMPERATURE_SHOW_TEMP_AND_LABEL(cpu_DTS, TEMP_CPU_DTS, "CPU_DTS");
  418. TEMPERATURE_SHOW_TEMP_AND_LABEL(northbridge,TEMP_NORTHBRIDGE,"NorthBridge");
  419. TEMPERATURE_SHOW_TEMP_AND_LABEL(vga, TEMP_VGA, "VGA_TEMP");
  420. TEMPERATURE_SHOW_TEMP_AND_LABEL(SKIN, TEMP_SKIN, "SKIN_TEMP90");
  421. /* Power supply interface */
  422. static int bat_status(void)
  423. {
  424. u8 status0 = ec_read_u8(BAT_STATUS0);
  425. u8 status1 = ec_read_u8(BAT_STATUS1);
  426. if (status0 & BAT_S0_CHARGING)
  427. return POWER_SUPPLY_STATUS_CHARGING;
  428. if (status0 & BAT_S0_DISCHARGE)
  429. return POWER_SUPPLY_STATUS_DISCHARGING;
  430. if (status1 & BAT_S1_FULL)
  431. return POWER_SUPPLY_STATUS_FULL;
  432. return POWER_SUPPLY_STATUS_NOT_CHARGING;
  433. }
  434. static int bat_health(void)
  435. {
  436. u8 status = ec_read_u8(BAT_STOP_CHARGE1);
  437. if (status & BAT_STOP_CHRG1_OVERTEMPERATURE)
  438. return POWER_SUPPLY_HEALTH_OVERHEAT;
  439. if (status & BAT_STOP_CHRG1_OVERVOLTAGE)
  440. return POWER_SUPPLY_HEALTH_OVERVOLTAGE;
  441. if (status & BAT_STOP_CHRG1_BAD_CELL)
  442. return POWER_SUPPLY_HEALTH_DEAD;
  443. if (status & BAT_STOP_CHRG1_COMM_FAIL)
  444. return POWER_SUPPLY_HEALTH_UNKNOWN;
  445. return POWER_SUPPLY_HEALTH_GOOD;
  446. }
  447. static int bat_is_present(void)
  448. {
  449. u8 status = ec_read_u8(BAT_STATUS2);
  450. return ((status & BAT_S1_EXISTS) != 0);
  451. }
  452. static int bat_technology(void)
  453. {
  454. u8 status = ec_read_u8(BAT_STATUS1);
  455. if (status & BAT_S1_LiION_OR_NiMH)
  456. return POWER_SUPPLY_TECHNOLOGY_LION;
  457. return POWER_SUPPLY_TECHNOLOGY_NiMH;
  458. }
  459. static int bat_capacity_level(void)
  460. {
  461. u8 status0 = ec_read_u8(BAT_STATUS0);
  462. u8 status1 = ec_read_u8(BAT_STATUS1);
  463. u8 status2 = ec_read_u8(BAT_STATUS2);
  464. if (status0 & BAT_S0_DISCHRG_CRITICAL
  465. || status1 & BAT_S1_EMPTY
  466. || status2 & BAT_S2_LOW_LOW)
  467. return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
  468. if (status0 & BAT_S0_LOW)
  469. return POWER_SUPPLY_CAPACITY_LEVEL_LOW;
  470. if (status1 & BAT_S1_FULL)
  471. return POWER_SUPPLY_CAPACITY_LEVEL_FULL;
  472. return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
  473. }
  474. static int bat_get_property(struct power_supply *psy,
  475. enum power_supply_property psp,
  476. union power_supply_propval *val)
  477. {
  478. struct compal_data *data;
  479. data = container_of(psy, struct compal_data, psy);
  480. switch (psp) {
  481. case POWER_SUPPLY_PROP_STATUS:
  482. val->intval = bat_status();
  483. break;
  484. case POWER_SUPPLY_PROP_HEALTH:
  485. val->intval = bat_health();
  486. break;
  487. case POWER_SUPPLY_PROP_PRESENT:
  488. val->intval = bat_is_present();
  489. break;
  490. case POWER_SUPPLY_PROP_TECHNOLOGY:
  491. val->intval = bat_technology();
  492. break;
  493. case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: /* THE design voltage... */
  494. val->intval = ec_read_u16(BAT_VOLTAGE_DESIGN) * 1000;
  495. break;
  496. case POWER_SUPPLY_PROP_VOLTAGE_NOW:
  497. val->intval = ec_read_u16(BAT_VOLTAGE_NOW) * 1000;
  498. break;
  499. case POWER_SUPPLY_PROP_CURRENT_NOW:
  500. val->intval = ec_read_s16(BAT_CURRENT_NOW) * 1000;
  501. break;
  502. case POWER_SUPPLY_PROP_CURRENT_AVG:
  503. val->intval = ec_read_s16(BAT_CURRENT_AVG) * 1000;
  504. break;
  505. case POWER_SUPPLY_PROP_POWER_NOW:
  506. val->intval = ec_read_u8(BAT_POWER) * 1000000;
  507. break;
  508. case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
  509. val->intval = ec_read_u16(BAT_CHARGE_DESIGN) * 1000;
  510. break;
  511. case POWER_SUPPLY_PROP_CHARGE_NOW:
  512. val->intval = ec_read_u16(BAT_CHARGE_NOW) * 1000;
  513. break;
  514. case POWER_SUPPLY_PROP_CAPACITY:
  515. val->intval = ec_read_u8(BAT_CAPACITY);
  516. break;
  517. case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
  518. val->intval = bat_capacity_level();
  519. break;
  520. /* It smees that BAT_TEMP_AVG is a (2's complement?) value showing
  521. * the number of degrees, whereas BAT_TEMP is somewhat more
  522. * complicated. It looks like this is a negative nember with a
  523. * 100/256 divider and an offset of 222. Both were determined
  524. * experimentally by comparing BAT_TEMP and BAT_TEMP_AVG. */
  525. case POWER_SUPPLY_PROP_TEMP:
  526. val->intval = ((222 - (int)ec_read_u8(BAT_TEMP)) * 1000) >> 8;
  527. break;
  528. case POWER_SUPPLY_PROP_TEMP_AMBIENT: /* Ambient, Avg, ... same thing */
  529. val->intval = ec_read_s8(BAT_TEMP_AVG) * 10;
  530. break;
  531. /* Neither the model name nor manufacturer name work for me. */
  532. case POWER_SUPPLY_PROP_MODEL_NAME:
  533. val->strval = data->bat_model_name;
  534. break;
  535. case POWER_SUPPLY_PROP_MANUFACTURER:
  536. val->strval = data->bat_manufacturer_name;
  537. break;
  538. case POWER_SUPPLY_PROP_SERIAL_NUMBER:
  539. val->strval = data->bat_serial_number;
  540. break;
  541. default:
  542. break;
  543. }
  544. return 0;
  545. }
  546. /* ============== */
  547. /* Driver Globals */
  548. /* ============== */
  549. static DEVICE_ATTR(wake_up_pme,
  550. 0644, wake_up_pme_show, wake_up_pme_store);
  551. static DEVICE_ATTR(wake_up_modem,
  552. 0644, wake_up_modem_show, wake_up_modem_store);
  553. static DEVICE_ATTR(wake_up_lan,
  554. 0644, wake_up_lan_show, wake_up_lan_store);
  555. static DEVICE_ATTR(wake_up_wlan,
  556. 0644, wake_up_wlan_show, wake_up_wlan_store);
  557. static DEVICE_ATTR(wake_up_key,
  558. 0644, wake_up_key_show, wake_up_key_store);
  559. static DEVICE_ATTR(wake_up_mouse,
  560. 0644, wake_up_mouse_show, wake_up_mouse_store);
  561. static DEVICE_ATTR(fan1_input, S_IRUGO, fan_show, NULL);
  562. static DEVICE_ATTR(temp1_input, S_IRUGO, temp_cpu, NULL);
  563. static DEVICE_ATTR(temp2_input, S_IRUGO, temp_cpu_local, NULL);
  564. static DEVICE_ATTR(temp3_input, S_IRUGO, temp_cpu_DTS, NULL);
  565. static DEVICE_ATTR(temp4_input, S_IRUGO, temp_northbridge, NULL);
  566. static DEVICE_ATTR(temp5_input, S_IRUGO, temp_vga, NULL);
  567. static DEVICE_ATTR(temp6_input, S_IRUGO, temp_SKIN, NULL);
  568. static DEVICE_ATTR(temp1_label, S_IRUGO, label_cpu, NULL);
  569. static DEVICE_ATTR(temp2_label, S_IRUGO, label_cpu_local, NULL);
  570. static DEVICE_ATTR(temp3_label, S_IRUGO, label_cpu_DTS, NULL);
  571. static DEVICE_ATTR(temp4_label, S_IRUGO, label_northbridge, NULL);
  572. static DEVICE_ATTR(temp5_label, S_IRUGO, label_vga, NULL);
  573. static DEVICE_ATTR(temp6_label, S_IRUGO, label_SKIN, NULL);
  574. static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, pwm_show, pwm_store);
  575. static DEVICE_ATTR(pwm1_enable,
  576. S_IRUGO | S_IWUSR, pwm_enable_show, pwm_enable_store);
  577. static struct attribute *compal_platform_attrs[] = {
  578. &dev_attr_wake_up_pme.attr,
  579. &dev_attr_wake_up_modem.attr,
  580. &dev_attr_wake_up_lan.attr,
  581. &dev_attr_wake_up_wlan.attr,
  582. &dev_attr_wake_up_key.attr,
  583. &dev_attr_wake_up_mouse.attr,
  584. NULL
  585. };
  586. static struct attribute_group compal_platform_attr_group = {
  587. .attrs = compal_platform_attrs
  588. };
  589. static struct attribute *compal_hwmon_attrs[] = {
  590. &dev_attr_pwm1_enable.attr,
  591. &dev_attr_pwm1.attr,
  592. &dev_attr_fan1_input.attr,
  593. &dev_attr_temp1_input.attr,
  594. &dev_attr_temp2_input.attr,
  595. &dev_attr_temp3_input.attr,
  596. &dev_attr_temp4_input.attr,
  597. &dev_attr_temp5_input.attr,
  598. &dev_attr_temp6_input.attr,
  599. &dev_attr_temp1_label.attr,
  600. &dev_attr_temp2_label.attr,
  601. &dev_attr_temp3_label.attr,
  602. &dev_attr_temp4_label.attr,
  603. &dev_attr_temp5_label.attr,
  604. &dev_attr_temp6_label.attr,
  605. NULL
  606. };
  607. ATTRIBUTE_GROUPS(compal_hwmon);
  608. static int compal_probe(struct platform_device *);
  609. static int compal_remove(struct platform_device *);
  610. static struct platform_driver compal_driver = {
  611. .driver = {
  612. .name = DRIVER_NAME,
  613. .owner = THIS_MODULE,
  614. },
  615. .probe = compal_probe,
  616. .remove = compal_remove,
  617. };
  618. static enum power_supply_property compal_bat_properties[] = {
  619. POWER_SUPPLY_PROP_STATUS,
  620. POWER_SUPPLY_PROP_HEALTH,
  621. POWER_SUPPLY_PROP_PRESENT,
  622. POWER_SUPPLY_PROP_TECHNOLOGY,
  623. POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
  624. POWER_SUPPLY_PROP_VOLTAGE_NOW,
  625. POWER_SUPPLY_PROP_CURRENT_NOW,
  626. POWER_SUPPLY_PROP_CURRENT_AVG,
  627. POWER_SUPPLY_PROP_POWER_NOW,
  628. POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
  629. POWER_SUPPLY_PROP_CHARGE_NOW,
  630. POWER_SUPPLY_PROP_CAPACITY,
  631. POWER_SUPPLY_PROP_CAPACITY_LEVEL,
  632. POWER_SUPPLY_PROP_TEMP,
  633. POWER_SUPPLY_PROP_TEMP_AMBIENT,
  634. POWER_SUPPLY_PROP_MODEL_NAME,
  635. POWER_SUPPLY_PROP_MANUFACTURER,
  636. POWER_SUPPLY_PROP_SERIAL_NUMBER,
  637. };
  638. static struct backlight_device *compalbl_device;
  639. static struct platform_device *compal_device;
  640. static struct rfkill *wifi_rfkill;
  641. static struct rfkill *bt_rfkill;
  642. /* =================================== */
  643. /* Initialization & clean-up functions */
  644. /* =================================== */
  645. static int dmi_check_cb(const struct dmi_system_id *id)
  646. {
  647. pr_info("Identified laptop model '%s'\n", id->ident);
  648. extra_features = false;
  649. return 1;
  650. }
  651. static int dmi_check_cb_extra(const struct dmi_system_id *id)
  652. {
  653. pr_info("Identified laptop model '%s', enabling extra features\n",
  654. id->ident);
  655. extra_features = true;
  656. return 1;
  657. }
  658. static struct dmi_system_id __initdata compal_dmi_table[] = {
  659. {
  660. .ident = "FL90/IFL90",
  661. .matches = {
  662. DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
  663. DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
  664. },
  665. .callback = dmi_check_cb
  666. },
  667. {
  668. .ident = "FL90/IFL90",
  669. .matches = {
  670. DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
  671. DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
  672. },
  673. .callback = dmi_check_cb
  674. },
  675. {
  676. .ident = "FL91/IFL91",
  677. .matches = {
  678. DMI_MATCH(DMI_BOARD_NAME, "IFL91"),
  679. DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
  680. },
  681. .callback = dmi_check_cb
  682. },
  683. {
  684. .ident = "FL92/JFL92",
  685. .matches = {
  686. DMI_MATCH(DMI_BOARD_NAME, "JFL92"),
  687. DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
  688. },
  689. .callback = dmi_check_cb
  690. },
  691. {
  692. .ident = "FT00/IFT00",
  693. .matches = {
  694. DMI_MATCH(DMI_BOARD_NAME, "IFT00"),
  695. DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
  696. },
  697. .callback = dmi_check_cb
  698. },
  699. {
  700. .ident = "Dell Mini 9",
  701. .matches = {
  702. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  703. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"),
  704. },
  705. .callback = dmi_check_cb
  706. },
  707. {
  708. .ident = "Dell Mini 10",
  709. .matches = {
  710. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  711. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"),
  712. },
  713. .callback = dmi_check_cb
  714. },
  715. {
  716. .ident = "Dell Mini 10v",
  717. .matches = {
  718. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  719. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"),
  720. },
  721. .callback = dmi_check_cb
  722. },
  723. {
  724. .ident = "Dell Mini 1012",
  725. .matches = {
  726. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  727. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1012"),
  728. },
  729. .callback = dmi_check_cb
  730. },
  731. {
  732. .ident = "Dell Inspiron 11z",
  733. .matches = {
  734. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  735. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"),
  736. },
  737. .callback = dmi_check_cb
  738. },
  739. {
  740. .ident = "Dell Mini 12",
  741. .matches = {
  742. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  743. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"),
  744. },
  745. .callback = dmi_check_cb
  746. },
  747. {
  748. .ident = "JHL90",
  749. .matches = {
  750. DMI_MATCH(DMI_BOARD_NAME, "JHL90"),
  751. DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
  752. },
  753. .callback = dmi_check_cb_extra
  754. },
  755. {
  756. .ident = "KHLB2",
  757. .matches = {
  758. DMI_MATCH(DMI_BOARD_NAME, "KHLB2"),
  759. DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
  760. },
  761. .callback = dmi_check_cb_extra
  762. },
  763. { }
  764. };
  765. MODULE_DEVICE_TABLE(dmi, compal_dmi_table);
  766. static void initialize_power_supply_data(struct compal_data *data)
  767. {
  768. data->psy.name = DRIVER_NAME;
  769. data->psy.type = POWER_SUPPLY_TYPE_BATTERY;
  770. data->psy.properties = compal_bat_properties;
  771. data->psy.num_properties = ARRAY_SIZE(compal_bat_properties);
  772. data->psy.get_property = bat_get_property;
  773. ec_read_sequence(BAT_MANUFACTURER_NAME_ADDR,
  774. data->bat_manufacturer_name,
  775. BAT_MANUFACTURER_NAME_LEN);
  776. data->bat_manufacturer_name[BAT_MANUFACTURER_NAME_LEN] = 0;
  777. ec_read_sequence(BAT_MODEL_NAME_ADDR,
  778. data->bat_model_name,
  779. BAT_MODEL_NAME_LEN);
  780. data->bat_model_name[BAT_MODEL_NAME_LEN] = 0;
  781. scnprintf(data->bat_serial_number, BAT_SERIAL_NUMBER_LEN + 1, "%d",
  782. ec_read_u16(BAT_SERIAL_NUMBER_ADDR));
  783. }
  784. static void initialize_fan_control_data(struct compal_data *data)
  785. {
  786. data->pwm_enable = 2; /* Keep motherboard in control for now */
  787. data->curr_pwm = 255; /* Try not to cause a CPU_on_fire exception
  788. if we take over... */
  789. }
  790. static int setup_rfkill(void)
  791. {
  792. int ret;
  793. wifi_rfkill = rfkill_alloc("compal-wifi", &compal_device->dev,
  794. RFKILL_TYPE_WLAN, &compal_rfkill_ops,
  795. (void *) WIRELESS_WLAN);
  796. if (!wifi_rfkill)
  797. return -ENOMEM;
  798. ret = rfkill_register(wifi_rfkill);
  799. if (ret)
  800. goto err_wifi;
  801. bt_rfkill = rfkill_alloc("compal-bluetooth", &compal_device->dev,
  802. RFKILL_TYPE_BLUETOOTH, &compal_rfkill_ops,
  803. (void *) WIRELESS_BT);
  804. if (!bt_rfkill) {
  805. ret = -ENOMEM;
  806. goto err_allocate_bt;
  807. }
  808. ret = rfkill_register(bt_rfkill);
  809. if (ret)
  810. goto err_register_bt;
  811. return 0;
  812. err_register_bt:
  813. rfkill_destroy(bt_rfkill);
  814. err_allocate_bt:
  815. rfkill_unregister(wifi_rfkill);
  816. err_wifi:
  817. rfkill_destroy(wifi_rfkill);
  818. return ret;
  819. }
  820. static int __init compal_init(void)
  821. {
  822. int ret;
  823. if (acpi_disabled) {
  824. pr_err("ACPI needs to be enabled for this driver to work!\n");
  825. return -ENODEV;
  826. }
  827. if (!force && !dmi_check_system(compal_dmi_table)) {
  828. pr_err("Motherboard not recognized (You could try the module's force-parameter)\n");
  829. return -ENODEV;
  830. }
  831. if (!acpi_video_backlight_support()) {
  832. struct backlight_properties props;
  833. memset(&props, 0, sizeof(struct backlight_properties));
  834. props.type = BACKLIGHT_PLATFORM;
  835. props.max_brightness = BACKLIGHT_LEVEL_MAX;
  836. compalbl_device = backlight_device_register(DRIVER_NAME,
  837. NULL, NULL,
  838. &compalbl_ops,
  839. &props);
  840. if (IS_ERR(compalbl_device))
  841. return PTR_ERR(compalbl_device);
  842. }
  843. ret = platform_driver_register(&compal_driver);
  844. if (ret)
  845. goto err_backlight;
  846. compal_device = platform_device_alloc(DRIVER_NAME, -1);
  847. if (!compal_device) {
  848. ret = -ENOMEM;
  849. goto err_platform_driver;
  850. }
  851. ret = platform_device_add(compal_device); /* This calls compal_probe */
  852. if (ret)
  853. goto err_platform_device;
  854. ret = setup_rfkill();
  855. if (ret)
  856. goto err_rfkill;
  857. pr_info("Driver " DRIVER_VERSION " successfully loaded\n");
  858. return 0;
  859. err_rfkill:
  860. platform_device_del(compal_device);
  861. err_platform_device:
  862. platform_device_put(compal_device);
  863. err_platform_driver:
  864. platform_driver_unregister(&compal_driver);
  865. err_backlight:
  866. backlight_device_unregister(compalbl_device);
  867. return ret;
  868. }
  869. static int compal_probe(struct platform_device *pdev)
  870. {
  871. int err;
  872. struct compal_data *data;
  873. struct device *hwmon_dev;
  874. if (!extra_features)
  875. return 0;
  876. /* Fan control */
  877. data = devm_kzalloc(&pdev->dev, sizeof(struct compal_data), GFP_KERNEL);
  878. if (!data)
  879. return -ENOMEM;
  880. initialize_fan_control_data(data);
  881. err = sysfs_create_group(&pdev->dev.kobj, &compal_platform_attr_group);
  882. if (err)
  883. return err;
  884. hwmon_dev = hwmon_device_register_with_groups(&pdev->dev,
  885. DRIVER_NAME, data,
  886. compal_hwmon_groups);
  887. if (IS_ERR(hwmon_dev)) {
  888. err = PTR_ERR(hwmon_dev);
  889. goto remove;
  890. }
  891. /* Power supply */
  892. initialize_power_supply_data(data);
  893. power_supply_register(&compal_device->dev, &data->psy);
  894. platform_set_drvdata(pdev, data);
  895. return 0;
  896. remove:
  897. sysfs_remove_group(&pdev->dev.kobj, &compal_platform_attr_group);
  898. return err;
  899. }
  900. static void __exit compal_cleanup(void)
  901. {
  902. platform_device_unregister(compal_device);
  903. platform_driver_unregister(&compal_driver);
  904. backlight_device_unregister(compalbl_device);
  905. rfkill_unregister(wifi_rfkill);
  906. rfkill_unregister(bt_rfkill);
  907. rfkill_destroy(wifi_rfkill);
  908. rfkill_destroy(bt_rfkill);
  909. pr_info("Driver unloaded\n");
  910. }
  911. static int compal_remove(struct platform_device *pdev)
  912. {
  913. struct compal_data *data;
  914. if (!extra_features)
  915. return 0;
  916. pr_info("Unloading: resetting fan control to motherboard\n");
  917. pwm_disable_control();
  918. data = platform_get_drvdata(pdev);
  919. power_supply_unregister(&data->psy);
  920. sysfs_remove_group(&pdev->dev.kobj, &compal_platform_attr_group);
  921. return 0;
  922. }
  923. module_init(compal_init);
  924. module_exit(compal_cleanup);
  925. MODULE_AUTHOR("Cezary Jackiewicz");
  926. MODULE_AUTHOR("Roald Frederickx (roald.frederickx@gmail.com)");
  927. MODULE_DESCRIPTION("Compal Laptop Support");
  928. MODULE_VERSION(DRIVER_VERSION);
  929. MODULE_LICENSE("GPL");