compal-laptop.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098
  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 = power_supply_get_drvdata(psy);
  479. switch (psp) {
  480. case POWER_SUPPLY_PROP_STATUS:
  481. val->intval = bat_status();
  482. break;
  483. case POWER_SUPPLY_PROP_HEALTH:
  484. val->intval = bat_health();
  485. break;
  486. case POWER_SUPPLY_PROP_PRESENT:
  487. val->intval = bat_is_present();
  488. break;
  489. case POWER_SUPPLY_PROP_TECHNOLOGY:
  490. val->intval = bat_technology();
  491. break;
  492. case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: /* THE design voltage... */
  493. val->intval = ec_read_u16(BAT_VOLTAGE_DESIGN) * 1000;
  494. break;
  495. case POWER_SUPPLY_PROP_VOLTAGE_NOW:
  496. val->intval = ec_read_u16(BAT_VOLTAGE_NOW) * 1000;
  497. break;
  498. case POWER_SUPPLY_PROP_CURRENT_NOW:
  499. val->intval = ec_read_s16(BAT_CURRENT_NOW) * 1000;
  500. break;
  501. case POWER_SUPPLY_PROP_CURRENT_AVG:
  502. val->intval = ec_read_s16(BAT_CURRENT_AVG) * 1000;
  503. break;
  504. case POWER_SUPPLY_PROP_POWER_NOW:
  505. val->intval = ec_read_u8(BAT_POWER) * 1000000;
  506. break;
  507. case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
  508. val->intval = ec_read_u16(BAT_CHARGE_DESIGN) * 1000;
  509. break;
  510. case POWER_SUPPLY_PROP_CHARGE_NOW:
  511. val->intval = ec_read_u16(BAT_CHARGE_NOW) * 1000;
  512. break;
  513. case POWER_SUPPLY_PROP_CAPACITY:
  514. val->intval = ec_read_u8(BAT_CAPACITY);
  515. break;
  516. case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
  517. val->intval = bat_capacity_level();
  518. break;
  519. /* It smees that BAT_TEMP_AVG is a (2's complement?) value showing
  520. * the number of degrees, whereas BAT_TEMP is somewhat more
  521. * complicated. It looks like this is a negative nember with a
  522. * 100/256 divider and an offset of 222. Both were determined
  523. * experimentally by comparing BAT_TEMP and BAT_TEMP_AVG. */
  524. case POWER_SUPPLY_PROP_TEMP:
  525. val->intval = ((222 - (int)ec_read_u8(BAT_TEMP)) * 1000) >> 8;
  526. break;
  527. case POWER_SUPPLY_PROP_TEMP_AMBIENT: /* Ambient, Avg, ... same thing */
  528. val->intval = ec_read_s8(BAT_TEMP_AVG) * 10;
  529. break;
  530. /* Neither the model name nor manufacturer name work for me. */
  531. case POWER_SUPPLY_PROP_MODEL_NAME:
  532. val->strval = data->bat_model_name;
  533. break;
  534. case POWER_SUPPLY_PROP_MANUFACTURER:
  535. val->strval = data->bat_manufacturer_name;
  536. break;
  537. case POWER_SUPPLY_PROP_SERIAL_NUMBER:
  538. val->strval = data->bat_serial_number;
  539. break;
  540. default:
  541. break;
  542. }
  543. return 0;
  544. }
  545. /* ============== */
  546. /* Driver Globals */
  547. /* ============== */
  548. static DEVICE_ATTR(wake_up_pme,
  549. 0644, wake_up_pme_show, wake_up_pme_store);
  550. static DEVICE_ATTR(wake_up_modem,
  551. 0644, wake_up_modem_show, wake_up_modem_store);
  552. static DEVICE_ATTR(wake_up_lan,
  553. 0644, wake_up_lan_show, wake_up_lan_store);
  554. static DEVICE_ATTR(wake_up_wlan,
  555. 0644, wake_up_wlan_show, wake_up_wlan_store);
  556. static DEVICE_ATTR(wake_up_key,
  557. 0644, wake_up_key_show, wake_up_key_store);
  558. static DEVICE_ATTR(wake_up_mouse,
  559. 0644, wake_up_mouse_show, wake_up_mouse_store);
  560. static DEVICE_ATTR(fan1_input, S_IRUGO, fan_show, NULL);
  561. static DEVICE_ATTR(temp1_input, S_IRUGO, temp_cpu, NULL);
  562. static DEVICE_ATTR(temp2_input, S_IRUGO, temp_cpu_local, NULL);
  563. static DEVICE_ATTR(temp3_input, S_IRUGO, temp_cpu_DTS, NULL);
  564. static DEVICE_ATTR(temp4_input, S_IRUGO, temp_northbridge, NULL);
  565. static DEVICE_ATTR(temp5_input, S_IRUGO, temp_vga, NULL);
  566. static DEVICE_ATTR(temp6_input, S_IRUGO, temp_SKIN, NULL);
  567. static DEVICE_ATTR(temp1_label, S_IRUGO, label_cpu, NULL);
  568. static DEVICE_ATTR(temp2_label, S_IRUGO, label_cpu_local, NULL);
  569. static DEVICE_ATTR(temp3_label, S_IRUGO, label_cpu_DTS, NULL);
  570. static DEVICE_ATTR(temp4_label, S_IRUGO, label_northbridge, NULL);
  571. static DEVICE_ATTR(temp5_label, S_IRUGO, label_vga, NULL);
  572. static DEVICE_ATTR(temp6_label, S_IRUGO, label_SKIN, NULL);
  573. static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, pwm_show, pwm_store);
  574. static DEVICE_ATTR(pwm1_enable,
  575. S_IRUGO | S_IWUSR, pwm_enable_show, pwm_enable_store);
  576. static struct attribute *compal_platform_attrs[] = {
  577. &dev_attr_wake_up_pme.attr,
  578. &dev_attr_wake_up_modem.attr,
  579. &dev_attr_wake_up_lan.attr,
  580. &dev_attr_wake_up_wlan.attr,
  581. &dev_attr_wake_up_key.attr,
  582. &dev_attr_wake_up_mouse.attr,
  583. NULL
  584. };
  585. static struct attribute_group compal_platform_attr_group = {
  586. .attrs = compal_platform_attrs
  587. };
  588. static struct attribute *compal_hwmon_attrs[] = {
  589. &dev_attr_pwm1_enable.attr,
  590. &dev_attr_pwm1.attr,
  591. &dev_attr_fan1_input.attr,
  592. &dev_attr_temp1_input.attr,
  593. &dev_attr_temp2_input.attr,
  594. &dev_attr_temp3_input.attr,
  595. &dev_attr_temp4_input.attr,
  596. &dev_attr_temp5_input.attr,
  597. &dev_attr_temp6_input.attr,
  598. &dev_attr_temp1_label.attr,
  599. &dev_attr_temp2_label.attr,
  600. &dev_attr_temp3_label.attr,
  601. &dev_attr_temp4_label.attr,
  602. &dev_attr_temp5_label.attr,
  603. &dev_attr_temp6_label.attr,
  604. NULL
  605. };
  606. ATTRIBUTE_GROUPS(compal_hwmon);
  607. static int compal_probe(struct platform_device *);
  608. static int compal_remove(struct platform_device *);
  609. static struct platform_driver compal_driver = {
  610. .driver = {
  611. .name = DRIVER_NAME,
  612. },
  613. .probe = compal_probe,
  614. .remove = compal_remove,
  615. };
  616. static enum power_supply_property compal_bat_properties[] = {
  617. POWER_SUPPLY_PROP_STATUS,
  618. POWER_SUPPLY_PROP_HEALTH,
  619. POWER_SUPPLY_PROP_PRESENT,
  620. POWER_SUPPLY_PROP_TECHNOLOGY,
  621. POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
  622. POWER_SUPPLY_PROP_VOLTAGE_NOW,
  623. POWER_SUPPLY_PROP_CURRENT_NOW,
  624. POWER_SUPPLY_PROP_CURRENT_AVG,
  625. POWER_SUPPLY_PROP_POWER_NOW,
  626. POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
  627. POWER_SUPPLY_PROP_CHARGE_NOW,
  628. POWER_SUPPLY_PROP_CAPACITY,
  629. POWER_SUPPLY_PROP_CAPACITY_LEVEL,
  630. POWER_SUPPLY_PROP_TEMP,
  631. POWER_SUPPLY_PROP_TEMP_AMBIENT,
  632. POWER_SUPPLY_PROP_MODEL_NAME,
  633. POWER_SUPPLY_PROP_MANUFACTURER,
  634. POWER_SUPPLY_PROP_SERIAL_NUMBER,
  635. };
  636. static struct backlight_device *compalbl_device;
  637. static struct platform_device *compal_device;
  638. static struct rfkill *wifi_rfkill;
  639. static struct rfkill *bt_rfkill;
  640. /* =================================== */
  641. /* Initialization & clean-up functions */
  642. /* =================================== */
  643. static int dmi_check_cb(const struct dmi_system_id *id)
  644. {
  645. pr_info("Identified laptop model '%s'\n", id->ident);
  646. extra_features = false;
  647. return 1;
  648. }
  649. static int dmi_check_cb_extra(const struct dmi_system_id *id)
  650. {
  651. pr_info("Identified laptop model '%s', enabling extra features\n",
  652. id->ident);
  653. extra_features = true;
  654. return 1;
  655. }
  656. static struct dmi_system_id __initdata compal_dmi_table[] = {
  657. {
  658. .ident = "FL90/IFL90",
  659. .matches = {
  660. DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
  661. DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
  662. },
  663. .callback = dmi_check_cb
  664. },
  665. {
  666. .ident = "FL90/IFL90",
  667. .matches = {
  668. DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
  669. DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
  670. },
  671. .callback = dmi_check_cb
  672. },
  673. {
  674. .ident = "FL91/IFL91",
  675. .matches = {
  676. DMI_MATCH(DMI_BOARD_NAME, "IFL91"),
  677. DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
  678. },
  679. .callback = dmi_check_cb
  680. },
  681. {
  682. .ident = "FL92/JFL92",
  683. .matches = {
  684. DMI_MATCH(DMI_BOARD_NAME, "JFL92"),
  685. DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
  686. },
  687. .callback = dmi_check_cb
  688. },
  689. {
  690. .ident = "FT00/IFT00",
  691. .matches = {
  692. DMI_MATCH(DMI_BOARD_NAME, "IFT00"),
  693. DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
  694. },
  695. .callback = dmi_check_cb
  696. },
  697. {
  698. .ident = "Dell Mini 9",
  699. .matches = {
  700. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  701. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"),
  702. },
  703. .callback = dmi_check_cb
  704. },
  705. {
  706. .ident = "Dell Mini 10",
  707. .matches = {
  708. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  709. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"),
  710. },
  711. .callback = dmi_check_cb
  712. },
  713. {
  714. .ident = "Dell Mini 10v",
  715. .matches = {
  716. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  717. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"),
  718. },
  719. .callback = dmi_check_cb
  720. },
  721. {
  722. .ident = "Dell Mini 1012",
  723. .matches = {
  724. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  725. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1012"),
  726. },
  727. .callback = dmi_check_cb
  728. },
  729. {
  730. .ident = "Dell Inspiron 11z",
  731. .matches = {
  732. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  733. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"),
  734. },
  735. .callback = dmi_check_cb
  736. },
  737. {
  738. .ident = "Dell Mini 12",
  739. .matches = {
  740. DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  741. DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"),
  742. },
  743. .callback = dmi_check_cb
  744. },
  745. {
  746. .ident = "JHL90",
  747. .matches = {
  748. DMI_MATCH(DMI_BOARD_NAME, "JHL90"),
  749. DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
  750. },
  751. .callback = dmi_check_cb_extra
  752. },
  753. {
  754. .ident = "KHLB2",
  755. .matches = {
  756. DMI_MATCH(DMI_BOARD_NAME, "KHLB2"),
  757. DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
  758. },
  759. .callback = dmi_check_cb_extra
  760. },
  761. { }
  762. };
  763. MODULE_DEVICE_TABLE(dmi, compal_dmi_table);
  764. static const struct power_supply_desc psy_bat_desc = {
  765. .name = DRIVER_NAME,
  766. .type = POWER_SUPPLY_TYPE_BATTERY,
  767. .properties = compal_bat_properties,
  768. .num_properties = ARRAY_SIZE(compal_bat_properties),
  769. .get_property = bat_get_property,
  770. };
  771. static void initialize_power_supply_data(struct compal_data *data)
  772. {
  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. struct power_supply_config psy_cfg = {};
  875. if (!extra_features)
  876. return 0;
  877. /* Fan control */
  878. data = devm_kzalloc(&pdev->dev, sizeof(struct compal_data), GFP_KERNEL);
  879. if (!data)
  880. return -ENOMEM;
  881. initialize_fan_control_data(data);
  882. err = sysfs_create_group(&pdev->dev.kobj, &compal_platform_attr_group);
  883. if (err)
  884. return err;
  885. hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev,
  886. "compal", data,
  887. compal_hwmon_groups);
  888. if (IS_ERR(hwmon_dev)) {
  889. err = PTR_ERR(hwmon_dev);
  890. goto remove;
  891. }
  892. /* Power supply */
  893. initialize_power_supply_data(data);
  894. psy_cfg.drv_data = data;
  895. data->psy = power_supply_register(&compal_device->dev, &psy_bat_desc,
  896. &psy_cfg);
  897. if (IS_ERR(data->psy)) {
  898. err = PTR_ERR(data->psy);
  899. goto remove;
  900. }
  901. platform_set_drvdata(pdev, data);
  902. return 0;
  903. remove:
  904. sysfs_remove_group(&pdev->dev.kobj, &compal_platform_attr_group);
  905. return err;
  906. }
  907. static void __exit compal_cleanup(void)
  908. {
  909. platform_device_unregister(compal_device);
  910. platform_driver_unregister(&compal_driver);
  911. backlight_device_unregister(compalbl_device);
  912. rfkill_unregister(wifi_rfkill);
  913. rfkill_unregister(bt_rfkill);
  914. rfkill_destroy(wifi_rfkill);
  915. rfkill_destroy(bt_rfkill);
  916. pr_info("Driver unloaded\n");
  917. }
  918. static int compal_remove(struct platform_device *pdev)
  919. {
  920. struct compal_data *data;
  921. if (!extra_features)
  922. return 0;
  923. pr_info("Unloading: resetting fan control to motherboard\n");
  924. pwm_disable_control();
  925. data = platform_get_drvdata(pdev);
  926. power_supply_unregister(data->psy);
  927. sysfs_remove_group(&pdev->dev.kobj, &compal_platform_attr_group);
  928. return 0;
  929. }
  930. module_init(compal_init);
  931. module_exit(compal_cleanup);
  932. MODULE_AUTHOR("Cezary Jackiewicz");
  933. MODULE_AUTHOR("Roald Frederickx (roald.frederickx@gmail.com)");
  934. MODULE_DESCRIPTION("Compal Laptop Support");
  935. MODULE_VERSION(DRIVER_VERSION);
  936. MODULE_LICENSE("GPL");