Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
panasonic-laptop.c
Go to the documentation of this file.
1 /*
2  * Panasonic HotKey and LCD brightness control driver
3  * (C) 2004 Hiroshi Miura <[email protected]>
4  * (C) 2004 NTT DATA Intellilink Co. http://www.intellilink.co.jp/
5  * (C) YOKOTA Hiroshi <yokota (at) netlab. is. tsukuba. ac. jp>
6  * (C) 2004 David Bronaugh <dbronaugh>
7  * (C) 2006-2008 Harald Welte <[email protected]>
8  *
9  * derived from toshiba_acpi.c, Copyright (C) 2002-2004 John Belmonte
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License version 2 as
13  * publicshed by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23  *
24  *---------------------------------------------------------------------------
25  *
26  * ChangeLog:
27  * Sep.23, 2008 Harald Welte <[email protected]>
28  * -v0.95 rename driver from drivers/acpi/pcc_acpi.c to
29  * drivers/misc/panasonic-laptop.c
30  *
31  * Jul.04, 2008 Harald Welte <[email protected]>
32  * -v0.94 replace /proc interface with device attributes
33  * support {set,get}keycode on th input device
34  *
35  * Jun.27, 2008 Harald Welte <[email protected]>
36  * -v0.92 merge with 2.6.26-rc6 input API changes
37  * remove broken <= 2.6.15 kernel support
38  * resolve all compiler warnings
39  * various coding style fixes (checkpatch.pl)
40  * add support for backlight api
41  * major code restructuring
42  *
43  * Dac.28, 2007 Harald Welte <[email protected]>
44  * -v0.91 merge with 2.6.24-rc6 ACPI changes
45  *
46  * Nov.04, 2006 Hiroshi Miura <[email protected]>
47  * -v0.9 remove warning about section reference.
48  * remove acpi_os_free
49  * add /proc/acpi/pcc/brightness interface for HAL access
50  * merge dbronaugh's enhancement
51  * Aug.17, 2004 David Bronaugh (dbronaugh)
52  * - Added screen brightness setting interface
53  * Thanks to FreeBSD crew (acpi_panasonic.c)
54  * for the ideas I needed to accomplish it
55  *
56  * May.29, 2006 Hiroshi Miura <[email protected]>
57  * -v0.8.4 follow to change keyinput structure
58  * thanks Fabian Yamaguchi <[email protected]>,
59  * Jacob Bower <[email protected]> and
60  * Hiroshi Yokota for providing solutions.
61  *
62  * Oct.02, 2004 Hiroshi Miura <[email protected]>
63  * -v0.8.2 merge code of YOKOTA Hiroshi
65  * Add sticky key mode interface.
66  * Refactoring acpi_pcc_generate_keyinput().
67  *
68  * Sep.15, 2004 Hiroshi Miura <[email protected]>
69  * -v0.8 Generate key input event on input subsystem.
70  * This is based on yet another driver written by
71  * Ryuta Nakanishi.
72  *
73  * Sep.10, 2004 Hiroshi Miura <[email protected]>
74  * -v0.7 Change proc interface functions using seq_file
75  * facility as same as other ACPI drivers.
76  *
77  * Aug.28, 2004 Hiroshi Miura <[email protected]>
78  * -v0.6.4 Fix a silly error with status checking
79  *
80  * Aug.25, 2004 Hiroshi Miura <[email protected]>
81  * -v0.6.3 replace read_acpi_int by standard function
82  * acpi_evaluate_integer
83  * some clean up and make smart copyright notice.
84  * fix return value of pcc_acpi_get_key()
85  * fix checking return value of acpi_bus_register_driver()
86  *
87  * Aug.22, 2004 David Bronaugh <[email protected]>
88  * -v0.6.2 Add check on ACPI data (num_sifr)
89  * Coding style cleanups, better error messages/handling
90  * Fixed an off-by-one error in memory allocation
91  *
92  * Aug.21, 2004 David Bronaugh <[email protected]>
93  * -v0.6.1 Fix a silly error with status checking
94  *
95  * Aug.20, 2004 David Bronaugh <[email protected]>
96  * - v0.6 Correct brightness controls to reflect reality
97  * based on information gleaned by Hiroshi Miura
98  * and discussions with Hiroshi Miura
99  *
100  * Aug.10, 2004 Hiroshi Miura <[email protected]>
101  * - v0.5 support LCD brightness control
102  * based on the disclosed information by MEI.
103  *
104  * Jul.25, 2004 Hiroshi Miura <[email protected]>
105  * - v0.4 first post version
106  * add function to retrive SIFR
107  *
108  * Jul.24, 2004 Hiroshi Miura <[email protected]>
109  * - v0.3 get proper status of hotkey
110  *
111  * Jul.22, 2004 Hiroshi Miura <[email protected]>
112  * - v0.2 add HotKey handler
113  *
114  * Jul.17, 2004 Hiroshi Miura <[email protected]>
115  * - v0.1 start from toshiba_acpi driver written by John Belmonte
116  *
117  */
118 
119 #include <linux/kernel.h>
120 #include <linux/module.h>
121 #include <linux/init.h>
122 #include <linux/types.h>
123 #include <linux/backlight.h>
124 #include <linux/ctype.h>
125 #include <linux/seq_file.h>
126 #include <linux/uaccess.h>
127 #include <linux/slab.h>
128 #include <acpi/acpi_bus.h>
129 #include <acpi/acpi_drivers.h>
130 #include <linux/input.h>
132 
133 
134 #ifndef ACPI_HOTKEY_COMPONENT
135 #define ACPI_HOTKEY_COMPONENT 0x10000000
136 #endif
137 
138 #define _COMPONENT ACPI_HOTKEY_COMPONENT
139 
140 MODULE_AUTHOR("Hiroshi Miura, David Bronaugh and Harald Welte");
141 MODULE_DESCRIPTION("ACPI HotKey driver for Panasonic Let's Note laptops");
142 MODULE_LICENSE("GPL");
143 
144 #define LOGPREFIX "pcc_acpi: "
145 
146 /* Define ACPI PATHs */
147 /* Lets note hotkeys */
148 #define METHOD_HKEY_QUERY "HINF"
149 #define METHOD_HKEY_SQTY "SQTY"
150 #define METHOD_HKEY_SINF "SINF"
151 #define METHOD_HKEY_SSET "SSET"
152 #define HKEY_NOTIFY 0x80
153 
154 #define ACPI_PCC_DRIVER_NAME "Panasonic Laptop Support"
155 #define ACPI_PCC_DEVICE_NAME "Hotkey"
156 #define ACPI_PCC_CLASS "pcc"
157 
158 #define ACPI_PCC_INPUT_PHYS "panasonic/hkey0"
159 
160 /* LCD_TYPEs: 0 = Normal, 1 = Semi-transparent
161  ENV_STATEs: Normal temp=0x01, High temp=0x81, N/A=0x00
162 */
175  };
176 /* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */
177 
178 static int acpi_pcc_hotkey_add(struct acpi_device *device);
179 static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type);
180 static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event);
181 
182 static const struct acpi_device_id pcc_device_ids[] = {
183  { "MAT0012", 0},
184  { "MAT0013", 0},
185  { "MAT0018", 0},
186  { "MAT0019", 0},
187  { "", 0},
188 };
189 MODULE_DEVICE_TABLE(acpi, pcc_device_ids);
190 
191 #ifdef CONFIG_PM_SLEEP
192 static int acpi_pcc_hotkey_resume(struct device *dev);
193 #endif
194 static SIMPLE_DEV_PM_OPS(acpi_pcc_hotkey_pm, NULL, acpi_pcc_hotkey_resume);
195 
196 static struct acpi_driver acpi_pcc_driver = {
197  .name = ACPI_PCC_DRIVER_NAME,
198  .class = ACPI_PCC_CLASS,
199  .ids = pcc_device_ids,
200  .ops = {
201  .add = acpi_pcc_hotkey_add,
202  .remove = acpi_pcc_hotkey_remove,
203  .notify = acpi_pcc_hotkey_notify,
204  },
205  .drv.pm = &acpi_pcc_hotkey_pm,
206 };
207 
208 static const struct key_entry panasonic_keymap[] = {
209  { KE_KEY, 0, { KEY_RESERVED } },
210  { KE_KEY, 1, { KEY_BRIGHTNESSDOWN } },
211  { KE_KEY, 2, { KEY_BRIGHTNESSUP } },
212  { KE_KEY, 3, { KEY_DISPLAYTOGGLE } },
213  { KE_KEY, 4, { KEY_MUTE } },
214  { KE_KEY, 5, { KEY_VOLUMEDOWN } },
215  { KE_KEY, 6, { KEY_VOLUMEUP } },
216  { KE_KEY, 7, { KEY_SLEEP } },
217  { KE_KEY, 8, { KEY_PROG1 } }, /* Change CPU boost */
218  { KE_KEY, 9, { KEY_BATTERY } },
219  { KE_KEY, 10, { KEY_SUSPEND } },
220  { KE_END, 0 }
221 };
222 
223 struct pcc_acpi {
225  unsigned long num_sifr;
228  struct acpi_device *device;
231 };
232 
233 struct pcc_keyinput {
234  struct acpi_hotkey *hotkey;
235 };
236 
237 /* method access functions */
238 static int acpi_pcc_write_sset(struct pcc_acpi *pcc, int func, int val)
239 {
240  union acpi_object in_objs[] = {
241  { .integer.type = ACPI_TYPE_INTEGER,
242  .integer.value = func, },
243  { .integer.type = ACPI_TYPE_INTEGER,
244  .integer.value = val, },
245  };
246  struct acpi_object_list params = {
247  .count = ARRAY_SIZE(in_objs),
248  .pointer = in_objs,
249  };
251 
253  &params, NULL);
254 
255  return (status == AE_OK) ? 0 : -EIO;
256 }
257 
258 static inline int acpi_pcc_get_sqty(struct acpi_device *device)
259 {
260  unsigned long long s;
262 
263  status = acpi_evaluate_integer(device->handle, METHOD_HKEY_SQTY,
264  NULL, &s);
265  if (ACPI_SUCCESS(status))
266  return s;
267  else {
268  ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
269  "evaluation error HKEY.SQTY\n"));
270  return -EINVAL;
271  }
272 }
273 
274 static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc)
275 {
278  union acpi_object *hkey = NULL;
279  int i;
280 
282  &buffer);
283  if (ACPI_FAILURE(status)) {
284  ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
285  "evaluation error HKEY.SINF\n"));
286  return 0;
287  }
288 
289  hkey = buffer.pointer;
290  if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) {
291  ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n"));
292  status = AE_ERROR;
293  goto end;
294  }
295 
296  if (pcc->num_sifr < hkey->package.count) {
297  ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
298  "SQTY reports bad SINF length\n"));
299  status = AE_ERROR;
300  goto end;
301  }
302 
303  for (i = 0; i < hkey->package.count; i++) {
304  union acpi_object *element = &(hkey->package.elements[i]);
305  if (likely(element->type == ACPI_TYPE_INTEGER)) {
306  pcc->sinf[i] = element->integer.value;
307  } else
308  ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
309  "Invalid HKEY.SINF data\n"));
310  }
311  pcc->sinf[hkey->package.count] = -1;
312 
313 end:
314  kfree(buffer.pointer);
315  return status == AE_OK;
316 }
317 
318 /* backlight API interface functions */
319 
320 /* This driver currently treats AC and DC brightness identical,
321  * since we don't need to invent an interface to the core ACPI
322  * logic to receive events in case a power supply is plugged in
323  * or removed */
324 
325 static int bl_get(struct backlight_device *bd)
326 {
327  struct pcc_acpi *pcc = bl_get_data(bd);
328 
329  if (!acpi_pcc_retrieve_biosdata(pcc))
330  return -EIO;
331 
332  return pcc->sinf[SINF_AC_CUR_BRIGHT];
333 }
334 
335 static int bl_set_status(struct backlight_device *bd)
336 {
337  struct pcc_acpi *pcc = bl_get_data(bd);
338  int bright = bd->props.brightness;
339  int rc;
340 
341  if (!acpi_pcc_retrieve_biosdata(pcc))
342  return -EIO;
343 
344  if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT])
345  bright = pcc->sinf[SINF_AC_MIN_BRIGHT];
346 
347  if (bright < pcc->sinf[SINF_DC_MIN_BRIGHT])
348  bright = pcc->sinf[SINF_DC_MIN_BRIGHT];
349 
350  if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT] ||
351  bright > pcc->sinf[SINF_AC_MAX_BRIGHT])
352  return -EINVAL;
353 
354  rc = acpi_pcc_write_sset(pcc, SINF_AC_CUR_BRIGHT, bright);
355  if (rc < 0)
356  return rc;
357 
358  return acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, bright);
359 }
360 
361 static const struct backlight_ops pcc_backlight_ops = {
362  .get_brightness = bl_get,
363  .update_status = bl_set_status,
364 };
365 
366 
367 /* sysfs user interface functions */
368 
369 static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr,
370  char *buf)
371 {
372  struct acpi_device *acpi = to_acpi_device(dev);
373  struct pcc_acpi *pcc = acpi_driver_data(acpi);
374 
375  if (!acpi_pcc_retrieve_biosdata(pcc))
376  return -EIO;
377 
378  return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]);
379 }
380 
381 static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr,
382  char *buf)
383 {
384  struct acpi_device *acpi = to_acpi_device(dev);
385  struct pcc_acpi *pcc = acpi_driver_data(acpi);
386 
387  if (!acpi_pcc_retrieve_biosdata(pcc))
388  return -EIO;
389 
390  return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_LCD_TYPE]);
391 }
392 
393 static ssize_t show_mute(struct device *dev, struct device_attribute *attr,
394  char *buf)
395 {
396  struct acpi_device *acpi = to_acpi_device(dev);
397  struct pcc_acpi *pcc = acpi_driver_data(acpi);
398 
399  if (!acpi_pcc_retrieve_biosdata(pcc))
400  return -EIO;
401 
402  return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_MUTE]);
403 }
404 
405 static ssize_t show_sticky(struct device *dev, struct device_attribute *attr,
406  char *buf)
407 {
408  struct acpi_device *acpi = to_acpi_device(dev);
409  struct pcc_acpi *pcc = acpi_driver_data(acpi);
410 
411  if (!acpi_pcc_retrieve_biosdata(pcc))
412  return -EIO;
413 
414  return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_STICKY_KEY]);
415 }
416 
417 static ssize_t set_sticky(struct device *dev, struct device_attribute *attr,
418  const char *buf, size_t count)
419 {
420  struct acpi_device *acpi = to_acpi_device(dev);
421  struct pcc_acpi *pcc = acpi_driver_data(acpi);
422  int val;
423 
424  if (count && sscanf(buf, "%i", &val) == 1 &&
425  (val == 0 || val == 1)) {
426  acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, val);
427  pcc->sticky_mode = val;
428  }
429 
430  return count;
431 }
432 
433 static DEVICE_ATTR(numbatt, S_IRUGO, show_numbatt, NULL);
434 static DEVICE_ATTR(lcdtype, S_IRUGO, show_lcdtype, NULL);
435 static DEVICE_ATTR(mute, S_IRUGO, show_mute, NULL);
436 static DEVICE_ATTR(sticky_key, S_IRUGO | S_IWUSR, show_sticky, set_sticky);
437 
438 static struct attribute *pcc_sysfs_entries[] = {
439  &dev_attr_numbatt.attr,
440  &dev_attr_lcdtype.attr,
441  &dev_attr_mute.attr,
442  &dev_attr_sticky_key.attr,
443  NULL,
444 };
445 
446 static struct attribute_group pcc_attr_group = {
447  .name = NULL, /* put in device directory */
448  .attrs = pcc_sysfs_entries,
449 };
450 
451 
452 /* hotkey input device driver */
453 
454 static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
455 {
456  struct input_dev *hotk_input_dev = pcc->input_dev;
457  int rc;
458  unsigned long long result;
459 
461  NULL, &result);
462  if (!ACPI_SUCCESS(rc)) {
463  ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
464  "error getting hotkey status\n"));
465  return;
466  }
467 
468  acpi_bus_generate_proc_event(pcc->device, HKEY_NOTIFY, result);
469 
470  if (!sparse_keymap_report_event(hotk_input_dev,
471  result & 0xf, result & 0x80, false))
472  ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
473  "Unknown hotkey event: %d\n", result));
474 }
475 
476 static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event)
477 {
478  struct pcc_acpi *pcc = acpi_driver_data(device);
479 
480  switch (event) {
481  case HKEY_NOTIFY:
482  acpi_pcc_generate_keyinput(pcc);
483  break;
484  default:
485  /* nothing to do */
486  break;
487  }
488 }
489 
490 static int acpi_pcc_init_input(struct pcc_acpi *pcc)
491 {
492  struct input_dev *input_dev;
493  int error;
494 
495  input_dev = input_allocate_device();
496  if (!input_dev) {
497  ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
498  "Couldn't allocate input device for hotkey"));
499  return -ENOMEM;
500  }
501 
502  input_dev->name = ACPI_PCC_DRIVER_NAME;
503  input_dev->phys = ACPI_PCC_INPUT_PHYS;
504  input_dev->id.bustype = BUS_HOST;
505  input_dev->id.vendor = 0x0001;
506  input_dev->id.product = 0x0001;
507  input_dev->id.version = 0x0100;
508 
509  error = sparse_keymap_setup(input_dev, panasonic_keymap, NULL);
510  if (error) {
511  ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
512  "Unable to setup input device keymap\n"));
513  goto err_free_dev;
514  }
515 
516  error = input_register_device(input_dev);
517  if (error) {
518  ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
519  "Unable to register input device\n"));
520  goto err_free_keymap;
521  }
522 
523  pcc->input_dev = input_dev;
524  return 0;
525 
526  err_free_keymap:
527  sparse_keymap_free(input_dev);
528  err_free_dev:
529  input_free_device(input_dev);
530  return error;
531 }
532 
533 static void acpi_pcc_destroy_input(struct pcc_acpi *pcc)
534 {
535  sparse_keymap_free(pcc->input_dev);
536  input_unregister_device(pcc->input_dev);
537  /*
538  * No need to input_free_device() since core input API refcounts
539  * and free()s the device.
540  */
541 }
542 
543 /* kernel module interface */
544 
545 #ifdef CONFIG_PM_SLEEP
546 static int acpi_pcc_hotkey_resume(struct device *dev)
547 {
548  struct pcc_acpi *pcc;
549 
550  if (!dev)
551  return -EINVAL;
552 
553  pcc = acpi_driver_data(to_acpi_device(dev));
554  if (!pcc)
555  return -EINVAL;
556 
557  ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Sticky mode restore: %d\n",
558  pcc->sticky_mode));
559 
560  return acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_mode);
561 }
562 #endif
563 
564 static int acpi_pcc_hotkey_add(struct acpi_device *device)
565 {
566  struct backlight_properties props;
567  struct pcc_acpi *pcc;
568  int num_sifr, result;
569 
570  if (!device)
571  return -EINVAL;
572 
573  num_sifr = acpi_pcc_get_sqty(device);
574 
575  if (num_sifr < 0 || num_sifr > 255) {
576  ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "num_sifr out of range"));
577  return -ENODEV;
578  }
579 
580  pcc = kzalloc(sizeof(struct pcc_acpi), GFP_KERNEL);
581  if (!pcc) {
582  ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
583  "Couldn't allocate mem for pcc"));
584  return -ENOMEM;
585  }
586 
587  pcc->sinf = kzalloc(sizeof(u32) * (num_sifr + 1), GFP_KERNEL);
588  if (!pcc->sinf) {
589  result = -ENOMEM;
590  goto out_hotkey;
591  }
592 
593  pcc->device = device;
594  pcc->handle = device->handle;
595  pcc->num_sifr = num_sifr;
596  device->driver_data = pcc;
597  strcpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME);
598  strcpy(acpi_device_class(device), ACPI_PCC_CLASS);
599 
600  result = acpi_pcc_init_input(pcc);
601  if (result) {
602  ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
603  "Error installing keyinput handler\n"));
604  goto out_sinf;
605  }
606 
607  if (!acpi_pcc_retrieve_biosdata(pcc)) {
608  ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
609  "Couldn't retrieve BIOS data\n"));
610  result = -EIO;
611  goto out_input;
612  }
613  /* initialize backlight */
614  memset(&props, 0, sizeof(struct backlight_properties));
615  props.type = BACKLIGHT_PLATFORM;
616  props.max_brightness = pcc->sinf[SINF_AC_MAX_BRIGHT];
617  pcc->backlight = backlight_device_register("panasonic", NULL, pcc,
618  &pcc_backlight_ops, &props);
619  if (IS_ERR(pcc->backlight)) {
620  result = PTR_ERR(pcc->backlight);
621  goto out_input;
622  }
623 
624  /* read the initial brightness setting from the hardware */
625  pcc->backlight->props.brightness = pcc->sinf[SINF_AC_CUR_BRIGHT];
626 
627  /* read the initial sticky key mode from the hardware */
628  pcc->sticky_mode = pcc->sinf[SINF_STICKY_KEY];
629 
630  /* add sysfs attributes */
631  result = sysfs_create_group(&device->dev.kobj, &pcc_attr_group);
632  if (result)
633  goto out_backlight;
634 
635  return 0;
636 
637 out_backlight:
639 out_input:
640  acpi_pcc_destroy_input(pcc);
641 out_sinf:
642  kfree(pcc->sinf);
643 out_hotkey:
644  kfree(pcc);
645 
646  return result;
647 }
648 
649 static int __init acpi_pcc_init(void)
650 {
651  int result = 0;
652 
653  if (acpi_disabled)
654  return -ENODEV;
655 
656  result = acpi_bus_register_driver(&acpi_pcc_driver);
657  if (result < 0) {
658  ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
659  "Error registering hotkey driver\n"));
660  return -ENODEV;
661  }
662 
663  return 0;
664 }
665 
666 static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type)
667 {
668  struct pcc_acpi *pcc = acpi_driver_data(device);
669 
670  if (!device || !pcc)
671  return -EINVAL;
672 
673  sysfs_remove_group(&device->dev.kobj, &pcc_attr_group);
674 
676 
677  acpi_pcc_destroy_input(pcc);
678 
679  kfree(pcc->sinf);
680  kfree(pcc);
681 
682  return 0;
683 }
684 
685 static void __exit acpi_pcc_exit(void)
686 {
687  acpi_bus_unregister_driver(&acpi_pcc_driver);
688 }
689 
690 module_init(acpi_pcc_init);
691 module_exit(acpi_pcc_exit);