PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/module/eee.c

http://eeepc-linux.googlecode.com/
C | 461 lines | 327 code | 71 blank | 63 comment | 33 complexity | c05cc88579096a3799e29167b0e1fe5d MD5 | raw file
  1. /*
  2. * eee.c - Asus eeePC extras
  3. *
  4. * Copyright (C) 2007 Andrew Tipton
  5. *
  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. *
  11. * Ths program is distributed in the hope that it will be useful,
  12. * but WITOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTAILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 59 Template Place, Suite 330, Boston, MA 02111-1307 USA
  19. *
  20. * ---------
  21. *
  22. * WARNING: This is an extremely *experimental* module! This code has been
  23. * developed through trial-and-error, which means I don't really understand
  24. * 100% what's going on here... That means there's a chance that there could
  25. * be unintended side-effects which might cause data loss or even physical
  26. * damage!
  27. *
  28. * Again, this code comes WITHOUT ANY WARRANTY whatsoever.
  29. */
  30. #include <linux/module.h>
  31. #include <linux/kernel.h>
  32. #include <linux/proc_fs.h>
  33. #include <asm/uaccess.h>
  34. #include <asm/io.h> // For inb() and outb()
  35. #include <linux/i2c.h>
  36. #include <linux/mutex.h>
  37. /* Module info */
  38. MODULE_LICENSE("GPL");
  39. MODULE_AUTHOR("Andrew Tipton");
  40. MODULE_DESCRIPTION("Support for eeePC-specific functionality.");
  41. #define EEE_VERSION "0.2"
  42. /* PLL access functions.
  43. *
  44. * Note that this isn't really the "proper" way to use the I2C API... :)
  45. * I2C_SMBUS_BLOCK_MAX is 32, the maximum size of a block read/write.
  46. */
  47. static void eee_pll_init(void);
  48. static void eee_pll_read(void);
  49. static void eee_pll_write(void);
  50. static void eee_pll_cleanup(void);
  51. static struct i2c_client eee_pll_smbus_client = {
  52. .adapter = NULL,
  53. .addr = 0x69,
  54. .flags = 0,
  55. };
  56. static char eee_pll_data[I2C_SMBUS_BLOCK_MAX];
  57. static int eee_pll_datalen = 0;
  58. static void eee_pll_init(void) {
  59. eee_pll_smbus_client.adapter = i2c_get_adapter(0);
  60. // Fill the eee_pll_data buffer.
  61. eee_pll_read();
  62. }
  63. // Takes approx 150ms to execute.
  64. static void eee_pll_read(void) {
  65. memset(eee_pll_data, 0, I2C_SMBUS_BLOCK_MAX);
  66. eee_pll_datalen = i2c_smbus_read_block_data(&eee_pll_smbus_client, 0, eee_pll_data);
  67. }
  68. // Takes approx 150ms to execute ???
  69. static void eee_pll_write(void) {
  70. i2c_smbus_write_block_data(&eee_pll_smbus_client, 0, eee_pll_datalen, eee_pll_data);
  71. }
  72. static void eee_pll_cleanup(void) {
  73. i2c_put_adapter(eee_pll_smbus_client.adapter);
  74. }
  75. /* Embedded controller access functions.
  76. *
  77. * The ENE KB3310 embedded controller has a feature known as "Index IO"
  78. * which allows the entire 64KB address space of the controller to be
  79. * accessed via a set of ISA I/O ports at 0x380-0x384. This allows us
  80. * direct access to all of the controller's ROM, RAM, SFRs, and peripheral
  81. * registers; this access bypasses the EC firmware entirely.
  82. *
  83. * This is much faster than using ec_transaction(), and it also allows us to
  84. * do things which are not possible through the EC's official interface.
  85. *
  86. * An Indexed IO write to an EC register takes approx. 90us, while an EC
  87. * transaction takes approx. 2500ms.
  88. */
  89. #define EC_IDX_ADDRH 0x381
  90. #define EC_IDX_ADDRL 0x382
  91. #define EC_IDX_DATA 0x383
  92. #define HIGH_BYTE(x) ((x & 0xff00) >> 8)
  93. #define LOW_BYTE(x) (x & 0x00ff)
  94. static DEFINE_MUTEX(eee_ec_mutex);
  95. static unsigned char eee_ec_read(unsigned short addr) {
  96. unsigned char data;
  97. mutex_lock(&eee_ec_mutex);
  98. outb(HIGH_BYTE(addr), EC_IDX_ADDRH);
  99. outb(LOW_BYTE(addr), EC_IDX_ADDRL);
  100. data = inb(EC_IDX_DATA);
  101. mutex_unlock(&eee_ec_mutex);
  102. return data;
  103. }
  104. static void eee_ec_write(unsigned short addr, unsigned char data) {
  105. mutex_lock(&eee_ec_mutex);
  106. outb(HIGH_BYTE(addr), EC_IDX_ADDRH);
  107. outb(LOW_BYTE(addr), EC_IDX_ADDRL);
  108. outb(data, EC_IDX_DATA);
  109. mutex_unlock(&eee_ec_mutex);
  110. }
  111. static void eee_ec_gpio_set(int pin, int value) {
  112. unsigned short port;
  113. unsigned char mask;
  114. port = 0xFC20 + ((pin >> 3) & 0x1f);
  115. mask = 1 << (pin & 0x07);
  116. if (value) {
  117. eee_ec_write(port, eee_ec_read(port) | mask);
  118. } else {
  119. eee_ec_write(port, eee_ec_read(port) & ~mask);
  120. }
  121. }
  122. static int eee_ec_gpio_get(int pin) {
  123. unsigned short port;
  124. unsigned char mask;
  125. unsigned char status;
  126. port = 0xfc20 + ((pin >> 3) & 0x1f);
  127. mask = 1 << (pin & 0x07);
  128. status = eee_ec_read(port) & mask;
  129. return (status) ? 1 : 0;
  130. }
  131. /*** Fan and temperature functions ***/
  132. #define EC_ST00 0xF451 // Temperature of CPU (C)
  133. #define EC_SC02 0xF463 // Fan PWM duty cycle (%)
  134. #define EC_SC05 0xF466 // High byte of fan speed (RPM)
  135. #define EC_SC06 0xF467 // Low byte of fan speed (RPM)
  136. #define EC_SFB3 0xF4D3 // Flag byte containing SF25 (FANctrl)
  137. static unsigned int eee_get_temperature(void) {
  138. return eee_ec_read(EC_ST00);
  139. }
  140. static unsigned int eee_fan_get_rpm(void) {
  141. return (eee_ec_read(EC_SC05) << 8) | eee_ec_read(EC_SC06);
  142. }
  143. // 1 if fan is in manual mode, 0 if controlled by the EC
  144. static int eee_fan_get_manual(void) {
  145. return (eee_ec_read(EC_SFB3) & 0x02) ? 1 : 0;
  146. }
  147. static void eee_fan_set_manual(int manual) {
  148. if (manual) {
  149. // SF25=1: Prevent the EC from controlling the fan.
  150. eee_ec_write(EC_SFB3, eee_ec_read(EC_SFB3) | 0x02);
  151. } else {
  152. // SF25=0: Allow the EC to control the fan.
  153. eee_ec_write(EC_SFB3, eee_ec_read(EC_SFB3) & ~0x02);
  154. }
  155. }
  156. static void eee_fan_set_speed(unsigned int speed) {
  157. eee_ec_write(EC_SC02, (speed > 100) ? 100 : speed);
  158. }
  159. static unsigned int eee_fan_get_speed(void) {
  160. return eee_ec_read(EC_SC02);
  161. }
  162. /*** Voltage functions ***/
  163. #define EC_VOLTAGE_PIN 0x66
  164. enum eee_voltage { Low=0, High=1 };
  165. static enum eee_voltage eee_get_voltage(void) {
  166. return eee_ec_gpio_get(EC_VOLTAGE_PIN);
  167. }
  168. static void eee_set_voltage(enum eee_voltage voltage) {
  169. eee_ec_gpio_set(EC_VOLTAGE_PIN, voltage);
  170. }
  171. /*** FSB functions ***/
  172. static void eee_get_freq(int *n, int *m) {
  173. *m = eee_pll_data[11] & 0x3F;
  174. *n = eee_pll_data[12];
  175. }
  176. static void eee_set_freq(int n, int m) {
  177. int current_n = 0, current_m = 0;
  178. eee_get_freq(&current_n, &current_m);
  179. if (current_n != n || current_m != m) {
  180. eee_pll_data[11] = m & 0x3F;
  181. eee_pll_data[12] = n & 0xFF;
  182. eee_pll_write();
  183. }
  184. }
  185. /*** /proc file functions ***/
  186. static struct proc_dir_entry *eee_proc_rootdir;
  187. #define EEE_PROC_READFUNC(NAME) \
  188. void eee_proc_readfunc_##NAME (char *buf, int buflen, int *bufpos)
  189. #define EEE_PROC_WRITEFUNC(NAME) \
  190. void eee_proc_writefunc_##NAME (const char *buf, int buflen, int *bufpos)
  191. #define EEE_PROC_PRINTF(FMT, ARGS...) \
  192. *bufpos += snprintf(buf + *bufpos, buflen - *bufpos, FMT, ##ARGS)
  193. #define EEE_PROC_SCANF(COUNT, FMT, ARGS...) \
  194. do { \
  195. int len = 0; \
  196. int cnt = sscanf(buf + *bufpos, FMT "%n", ##ARGS, &len); \
  197. if (cnt < COUNT) { \
  198. printk(KERN_DEBUG "eee: scanf(\"%s\") wanted %d args, but got %d.\n", FMT, COUNT, cnt); \
  199. return; \
  200. } \
  201. *bufpos += len; \
  202. } while (0)
  203. #define EEE_PROC_MEMCPY(SRC, SRCLEN) \
  204. do { \
  205. int len = SRCLEN; \
  206. if (len > (buflen - *bufpos)) \
  207. len = buflen - *bufpos; \
  208. memcpy(buf + *bufpos, SRC, (SRCLEN > (buflen - *bufpos)) ? (buflen - *bufpos) : SRCLEN); \
  209. *bufpos += len; \
  210. } while (0)
  211. #define EEE_PROC_FILES_BEGIN \
  212. static struct eee_proc_file eee_proc_files[] = {
  213. #define EEE_PROC_RW(NAME, MODE) \
  214. { #NAME, MODE, &eee_proc_readfunc_##NAME, &eee_proc_writefunc_##NAME }
  215. #define EEE_PROC_RO(NAME, MODE) \
  216. { #NAME, MODE, &eee_proc_readfunc_##NAME, NULL }
  217. #define EEE_PROC_FILES_END \
  218. { NULL, 0, NULL, NULL } };
  219. struct eee_proc_file {
  220. char *name;
  221. int mode;
  222. void (*readfunc)(char *buf, int buflen, int *bufpos);
  223. void (*writefunc)(const char *buf, int buflen, int *bufpos);
  224. };
  225. EEE_PROC_READFUNC(fsb) {
  226. int n = 0;
  227. int m = 0;
  228. int voltage = 0;
  229. eee_get_freq(&n, &m);
  230. voltage = (int)eee_get_voltage();
  231. EEE_PROC_PRINTF("%d %d %d\n", n, m, voltage);
  232. }
  233. EEE_PROC_WRITEFUNC(fsb) {
  234. int n = 70; // sensible defaults
  235. int m = 24;
  236. int voltage = 0;
  237. EEE_PROC_SCANF(3, "%i %i %i", &n, &m, &voltage);
  238. eee_set_freq(n, m);
  239. eee_set_voltage(voltage);
  240. }
  241. EEE_PROC_READFUNC(pll) {
  242. eee_pll_read();
  243. EEE_PROC_MEMCPY(eee_pll_data, eee_pll_datalen);
  244. }
  245. EEE_PROC_READFUNC(fan_speed) {
  246. int speed = eee_fan_get_speed();
  247. EEE_PROC_PRINTF("%d\n", speed);
  248. }
  249. EEE_PROC_WRITEFUNC(fan_speed) {
  250. unsigned int speed = 0;
  251. EEE_PROC_SCANF(1, "%u", &speed);
  252. eee_fan_set_speed(speed);
  253. }
  254. EEE_PROC_READFUNC(fan_rpm) {
  255. int rpm = eee_fan_get_rpm();
  256. EEE_PROC_PRINTF("%d\n", rpm);
  257. }
  258. EEE_PROC_READFUNC(fan_manual) {
  259. EEE_PROC_PRINTF("%d\n", eee_fan_get_manual());
  260. }
  261. EEE_PROC_WRITEFUNC(fan_manual) {
  262. int manual = 0;
  263. EEE_PROC_SCANF(1, "%i", &manual);
  264. eee_fan_set_manual(manual);
  265. }
  266. #if 0
  267. EEE_PROC_READFUNC(fan_mode) {
  268. enum eee_fan_mode mode = eee_fan_get_mode();
  269. switch (mode) {
  270. case Manual: EEE_PROC_PRINTF("manual\n");
  271. break;
  272. case Automatic: EEE_PROC_PRINTF("auto\n");
  273. break;
  274. case Embedded: EEE_PROC_PRINTF("embedded\n");
  275. break;
  276. }
  277. }
  278. EEE_PROC_WRITEFUNC(fan_mode) {
  279. enum eee_fan_mode mode = Automatic;
  280. char inputstr[16];
  281. EEE_PROC_SCANF(1, "%15s", inputstr);
  282. if (strcmp(inputstr, "manual") == 0) {
  283. mode = Manual;
  284. } else if (strcmp(inputstr, "auto") == 0) {
  285. mode = Automatic;
  286. } else if (strcmp(inputstr, "embedded") == 0) {
  287. mode = Embedded;
  288. }
  289. eee_fan_set_mode(mode);
  290. }
  291. #endif
  292. EEE_PROC_READFUNC(temperature) {
  293. unsigned int t = eee_get_temperature();
  294. EEE_PROC_PRINTF("%d\n", t);
  295. }
  296. EEE_PROC_FILES_BEGIN
  297. EEE_PROC_RW(fsb, 0644),
  298. EEE_PROC_RO(pll, 0400),
  299. EEE_PROC_RW(fan_speed, 0644),
  300. EEE_PROC_RO(fan_rpm, 0444),
  301. EEE_PROC_RW(fan_manual, 0644),
  302. EEE_PROC_RO(temperature, 0444),
  303. EEE_PROC_FILES_END
  304. int eee_proc_readfunc(char *buffer, char **buffer_location, off_t offset,
  305. int buffer_length, int *eof, void *data)
  306. {
  307. struct eee_proc_file *procfile = (struct eee_proc_file *)data;
  308. int bufpos = 0;
  309. if (!procfile || !procfile->readfunc) {
  310. return -EIO;
  311. }
  312. *eof = 1;
  313. if (offset > 0) {
  314. return 0;
  315. }
  316. (*procfile->readfunc)(buffer, buffer_length, &bufpos);
  317. return bufpos;
  318. }
  319. int eee_proc_writefunc(struct file *file, const char *buffer,
  320. unsigned long count, void *data)
  321. {
  322. char userdata[129];
  323. int bufpos = 0;
  324. struct eee_proc_file *procfile = (struct eee_proc_file *)data;
  325. if (!procfile || !procfile->writefunc) {
  326. return -EIO;
  327. }
  328. if (copy_from_user(userdata, buffer, (count > 128) ? 128 : count)) {
  329. printk(KERN_DEBUG "eee: copy_from_user() failed\n");
  330. return -EIO;
  331. }
  332. userdata[128] = 0; // So that sscanf() doesn't overflow...
  333. (*procfile->writefunc)(userdata, count, &bufpos);
  334. return count;
  335. }
  336. int eee_proc_init(void) {
  337. int i;
  338. /* Create the /proc/eee directory. */
  339. eee_proc_rootdir = proc_mkdir("eee", &proc_root);
  340. if (!eee_proc_rootdir) {
  341. printk(KERN_ERR "eee: Unable to create /proc/eee\n");
  342. return false;
  343. }
  344. eee_proc_rootdir->owner = THIS_MODULE;
  345. /* Create the individual proc files. */
  346. for (i=0; eee_proc_files[i].name; i++) {
  347. struct proc_dir_entry *proc_file;
  348. struct eee_proc_file *f = &eee_proc_files[i];
  349. proc_file = create_proc_entry(f->name, f->mode, eee_proc_rootdir);
  350. if (!proc_file) {
  351. printk(KERN_ERR "eee: Unable to create /proc/eee/%s", f->name);
  352. goto proc_init_cleanup;
  353. }
  354. proc_file->read_proc = &eee_proc_readfunc;
  355. if (f->writefunc) {
  356. proc_file->write_proc = &eee_proc_writefunc;
  357. }
  358. proc_file->data = f;
  359. proc_file->owner = THIS_MODULE;
  360. proc_file->mode = S_IFREG | f->mode;
  361. proc_file->uid = 0;
  362. proc_file->gid = 0;
  363. }
  364. return true;
  365. /* We had an error, so cleanup all of the proc files... */
  366. proc_init_cleanup:
  367. for (; i >= 0; i--) {
  368. remove_proc_entry(eee_proc_files[i].name, eee_proc_rootdir);
  369. }
  370. remove_proc_entry("eee", &proc_root);
  371. return false;
  372. }
  373. void eee_proc_cleanup(void) {
  374. int i;
  375. for (i = 0; eee_proc_files[i].name; i++) {
  376. remove_proc_entry(eee_proc_files[i].name, eee_proc_rootdir);
  377. }
  378. remove_proc_entry("eee", &proc_root);
  379. }
  380. /*** Module initialization ***/
  381. int init_module(void) {
  382. eee_pll_init();
  383. eee_proc_init();
  384. printk(KERN_NOTICE "Asus eeePC extras, version %s\n", EEE_VERSION);
  385. return 0;
  386. }
  387. void cleanup_module(void) {
  388. eee_pll_cleanup();
  389. eee_proc_cleanup();
  390. }