compal-laptop.c 30 KB

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