diff options
43 files changed, 3830 insertions, 743 deletions
diff --git a/Documentation/ABI/testing/debugfs-ec b/Documentation/ABI/testing/debugfs-ec new file mode 100644 index 000000000000..6546115a94da --- /dev/null +++ b/Documentation/ABI/testing/debugfs-ec | |||
@@ -0,0 +1,20 @@ | |||
1 | What: /sys/kernel/debug/ec/*/{gpe,use_global_lock,io} | ||
2 | Date: July 2010 | ||
3 | Contact: Thomas Renninger <trenn@suse.de> | ||
4 | Description: | ||
5 | |||
6 | General information like which GPE is assigned to the EC and whether | ||
7 | the global lock should get used. | ||
8 | Knowing the EC GPE one can watch the amount of HW events related to | ||
9 | the EC here (XY -> GPE number from /sys/kernel/debug/ec/*/gpe): | ||
10 | /sys/firmware/acpi/interrupts/gpeXY | ||
11 | |||
12 | The io file is binary and a userspace tool located here: | ||
13 | ftp://ftp.suse.com/pub/people/trenn/sources/ec/ | ||
14 | should get used to read out the 256 Embedded Controller registers | ||
15 | or writing to them. | ||
16 | |||
17 | CAUTION: Do not write to the Embedded Controller if you don't know | ||
18 | what you are doing! Rebooting afterwards also is a good idea. | ||
19 | This can influence the way your machine is cooled and fans may | ||
20 | not get switched on again after you did a wrong write. | ||
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index fc15538d8b46..f6f80257addb 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt | |||
@@ -960,70 +960,21 @@ Sysfs notes: | |||
960 | subsystem, and follow all of the hwmon guidelines at | 960 | subsystem, and follow all of the hwmon guidelines at |
961 | Documentation/hwmon. | 961 | Documentation/hwmon. |
962 | 962 | ||
963 | EXPERIMENTAL: Embedded controller register dump | ||
964 | ----------------------------------------------- | ||
963 | 965 | ||
964 | EXPERIMENTAL: Embedded controller register dump -- /proc/acpi/ibm/ecdump | 966 | This feature is not included in the thinkpad driver anymore. |
965 | ------------------------------------------------------------------------ | 967 | Instead the EC can be accessed through /sys/kernel/debug/ec with |
966 | 968 | a userspace tool which can be found here: | |
967 | This feature is marked EXPERIMENTAL because the implementation | 969 | ftp://ftp.suse.com/pub/people/trenn/sources/ec |
968 | directly accesses hardware registers and may not work as expected. USE | ||
969 | WITH CAUTION! To use this feature, you need to supply the | ||
970 | experimental=1 parameter when loading the module. | ||
971 | |||
972 | This feature dumps the values of 256 embedded controller | ||
973 | registers. Values which have changed since the last time the registers | ||
974 | were dumped are marked with a star: | ||
975 | |||
976 | [root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump | ||
977 | EC +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f | ||
978 | EC 0x00: a7 47 87 01 fe 96 00 08 01 00 cb 00 00 00 40 00 | ||
979 | EC 0x10: 00 00 ff ff f4 3c 87 09 01 ff 42 01 ff ff 0d 00 | ||
980 | EC 0x20: 00 00 00 00 00 00 00 00 00 00 00 03 43 00 00 80 | ||
981 | EC 0x30: 01 07 1a 00 30 04 00 00 *85 00 00 10 00 50 00 00 | ||
982 | EC 0x40: 00 00 00 00 00 00 14 01 00 04 00 00 00 00 00 00 | ||
983 | EC 0x50: 00 c0 02 0d 00 01 01 02 02 03 03 03 03 *bc *02 *bc | ||
984 | EC 0x60: *02 *bc *02 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
985 | EC 0x70: 00 00 00 00 00 12 30 40 *24 *26 *2c *27 *20 80 *1f 80 | ||
986 | EC 0x80: 00 00 00 06 *37 *0e 03 00 00 00 0e 07 00 00 00 00 | ||
987 | EC 0x90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
988 | EC 0xa0: *ff 09 ff 09 ff ff *64 00 *00 *00 *a2 41 *ff *ff *e0 00 | ||
989 | EC 0xb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
990 | EC 0xc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
991 | EC 0xd0: 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
992 | EC 0xe0: 00 00 00 00 00 00 00 00 11 20 49 04 24 06 55 03 | ||
993 | EC 0xf0: 31 55 48 54 35 38 57 57 08 2f 45 73 07 65 6c 1a | ||
994 | |||
995 | This feature can be used to determine the register holding the fan | ||
996 | speed on some models. To do that, do the following: | ||
997 | 970 | ||
971 | Use it to determine the register holding the fan | ||
972 | speed on some models. To do that, do the following: | ||
998 | - make sure the battery is fully charged | 973 | - make sure the battery is fully charged |
999 | - make sure the fan is running | 974 | - make sure the fan is running |
1000 | - run 'cat /proc/acpi/ibm/ecdump' several times, once per second or so | 975 | - use above mentioned tool to read out the EC |
1001 | 976 | ||
1002 | The first step makes sure various charging-related values don't | 977 | Often fan and temperature values vary between |
1003 | vary. The second ensures that the fan-related values do vary, since | ||
1004 | the fan speed fluctuates a bit. The third will (hopefully) mark the | ||
1005 | fan register with a star: | ||
1006 | |||
1007 | [root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump | ||
1008 | EC +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f | ||
1009 | EC 0x00: a7 47 87 01 fe 96 00 08 01 00 cb 00 00 00 40 00 | ||
1010 | EC 0x10: 00 00 ff ff f4 3c 87 09 01 ff 42 01 ff ff 0d 00 | ||
1011 | EC 0x20: 00 00 00 00 00 00 00 00 00 00 00 03 43 00 00 80 | ||
1012 | EC 0x30: 01 07 1a 00 30 04 00 00 85 00 00 10 00 50 00 00 | ||
1013 | EC 0x40: 00 00 00 00 00 00 14 01 00 04 00 00 00 00 00 00 | ||
1014 | EC 0x50: 00 c0 02 0d 00 01 01 02 02 03 03 03 03 bc 02 bc | ||
1015 | EC 0x60: 02 bc 02 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
1016 | EC 0x70: 00 00 00 00 00 12 30 40 24 27 2c 27 21 80 1f 80 | ||
1017 | EC 0x80: 00 00 00 06 *be 0d 03 00 00 00 0e 07 00 00 00 00 | ||
1018 | EC 0x90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
1019 | EC 0xa0: ff 09 ff 09 ff ff 64 00 00 00 a2 41 ff ff e0 00 | ||
1020 | EC 0xb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
1021 | EC 0xc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
1022 | EC 0xd0: 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
1023 | EC 0xe0: 00 00 00 00 00 00 00 00 11 20 49 04 24 06 55 03 | ||
1024 | EC 0xf0: 31 55 48 54 35 38 57 57 08 2f 45 73 07 65 6c 1a | ||
1025 | |||
1026 | Another set of values that varies often is the temperature | ||
1027 | readings. Since temperatures don't change vary fast, you can take | 978 | readings. Since temperatures don't change vary fast, you can take |
1028 | several quick dumps to eliminate them. | 979 | several quick dumps to eliminate them. |
1029 | 980 | ||
diff --git a/arch/x86/include/asm/intel_scu_ipc.h b/arch/x86/include/asm/intel_scu_ipc.h index 4470c9ad4a3e..29f66793cc55 100644 --- a/arch/x86/include/asm/intel_scu_ipc.h +++ b/arch/x86/include/asm/intel_scu_ipc.h | |||
@@ -1,6 +1,12 @@ | |||
1 | #ifndef _ASM_X86_INTEL_SCU_IPC_H_ | 1 | #ifndef _ASM_X86_INTEL_SCU_IPC_H_ |
2 | #define _ASM_X86_INTEL_SCU_IPC_H_ | 2 | #define _ASM_X86_INTEL_SCU_IPC_H_ |
3 | 3 | ||
4 | #define IPCMSG_VRTC 0xFA /* Set vRTC device */ | ||
5 | |||
6 | /* Command id associated with message IPCMSG_VRTC */ | ||
7 | #define IPC_CMD_VRTC_SETTIME 1 /* Set time */ | ||
8 | #define IPC_CMD_VRTC_SETALARM 2 /* Set alarm */ | ||
9 | |||
4 | /* Read single register */ | 10 | /* Read single register */ |
5 | int intel_scu_ipc_ioread8(u16 addr, u8 *data); | 11 | int intel_scu_ipc_ioread8(u16 addr, u8 *data); |
6 | 12 | ||
@@ -28,20 +34,6 @@ int intel_scu_ipc_writev(u16 *addr, u8 *data, int len); | |||
28 | /* Update single register based on the mask */ | 34 | /* Update single register based on the mask */ |
29 | int intel_scu_ipc_update_register(u16 addr, u8 data, u8 mask); | 35 | int intel_scu_ipc_update_register(u16 addr, u8 data, u8 mask); |
30 | 36 | ||
31 | /* | ||
32 | * Indirect register read | ||
33 | * Can be used when SCCB(System Controller Configuration Block) register | ||
34 | * HRIM(Honor Restricted IPC Messages) is set (bit 23) | ||
35 | */ | ||
36 | int intel_scu_ipc_register_read(u32 addr, u32 *data); | ||
37 | |||
38 | /* | ||
39 | * Indirect register write | ||
40 | * Can be used when SCCB(System Controller Configuration Block) register | ||
41 | * HRIM(Honor Restricted IPC Messages) is set (bit 23) | ||
42 | */ | ||
43 | int intel_scu_ipc_register_write(u32 addr, u32 data); | ||
44 | |||
45 | /* Issue commands to the SCU with or without data */ | 37 | /* Issue commands to the SCU with or without data */ |
46 | int intel_scu_ipc_simple_command(int cmd, int sub); | 38 | int intel_scu_ipc_simple_command(int cmd, int sub); |
47 | int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen, | 39 | int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen, |
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 746411518802..08e0140920e1 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig | |||
@@ -104,6 +104,24 @@ config ACPI_SYSFS_POWER | |||
104 | help | 104 | help |
105 | Say N to disable power /sys interface | 105 | Say N to disable power /sys interface |
106 | 106 | ||
107 | config ACPI_EC_DEBUGFS | ||
108 | tristate "EC read/write access through /sys/kernel/debug/ec" | ||
109 | default n | ||
110 | help | ||
111 | Say N to disable Embedded Controller /sys/kernel/debug interface | ||
112 | |||
113 | Be aware that using this interface can confuse your Embedded | ||
114 | Controller in a way that a normal reboot is not enough. You then | ||
115 | have to power of your system, and remove the laptop battery for | ||
116 | some seconds. | ||
117 | An Embedded Controller typically is available on laptops and reads | ||
118 | sensor values like battery state and temperature. | ||
119 | The kernel accesses the EC through ACPI parsed code provided by BIOS | ||
120 | tables. This option allows to access the EC directly without ACPI | ||
121 | code being involved. | ||
122 | Thus this option is a debug option that helps to write ACPI drivers | ||
123 | and can be used to identify ACPI code or EC firmware bugs. | ||
124 | |||
107 | config ACPI_PROC_EVENT | 125 | config ACPI_PROC_EVENT |
108 | bool "Deprecated /proc/acpi/event support" | 126 | bool "Deprecated /proc/acpi/event support" |
109 | depends on PROC_FS | 127 | depends on PROC_FS |
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 6ee33169e1dc..833b582d1762 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile | |||
@@ -60,6 +60,7 @@ obj-$(CONFIG_ACPI_SBS) += sbshc.o | |||
60 | obj-$(CONFIG_ACPI_SBS) += sbs.o | 60 | obj-$(CONFIG_ACPI_SBS) += sbs.o |
61 | obj-$(CONFIG_ACPI_POWER_METER) += power_meter.o | 61 | obj-$(CONFIG_ACPI_POWER_METER) += power_meter.o |
62 | obj-$(CONFIG_ACPI_HED) += hed.o | 62 | obj-$(CONFIG_ACPI_HED) += hed.o |
63 | obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o | ||
63 | 64 | ||
64 | # processor has its own "processor." module_param namespace | 65 | # processor has its own "processor." module_param namespace |
65 | processor-y := processor_driver.o processor_throttling.o | 66 | processor-y := processor_driver.o processor_throttling.o |
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 5f2027d782e8..1fa0aafebe2a 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c | |||
@@ -34,8 +34,6 @@ | |||
34 | #include <linux/init.h> | 34 | #include <linux/init.h> |
35 | #include <linux/types.h> | 35 | #include <linux/types.h> |
36 | #include <linux/delay.h> | 36 | #include <linux/delay.h> |
37 | #include <linux/proc_fs.h> | ||
38 | #include <linux/seq_file.h> | ||
39 | #include <linux/interrupt.h> | 37 | #include <linux/interrupt.h> |
40 | #include <linux/list.h> | 38 | #include <linux/list.h> |
41 | #include <linux/spinlock.h> | 39 | #include <linux/spinlock.h> |
@@ -45,10 +43,13 @@ | |||
45 | #include <acpi/acpi_drivers.h> | 43 | #include <acpi/acpi_drivers.h> |
46 | #include <linux/dmi.h> | 44 | #include <linux/dmi.h> |
47 | 45 | ||
46 | #include "internal.h" | ||
47 | |||
48 | #define ACPI_EC_CLASS "embedded_controller" | 48 | #define ACPI_EC_CLASS "embedded_controller" |
49 | #define ACPI_EC_DEVICE_NAME "Embedded Controller" | 49 | #define ACPI_EC_DEVICE_NAME "Embedded Controller" |
50 | #define ACPI_EC_FILE_INFO "info" | 50 | #define ACPI_EC_FILE_INFO "info" |
51 | 51 | ||
52 | #undef PREFIX | ||
52 | #define PREFIX "ACPI: EC: " | 53 | #define PREFIX "ACPI: EC: " |
53 | 54 | ||
54 | /* EC status register */ | 55 | /* EC status register */ |
@@ -106,19 +107,8 @@ struct transaction { | |||
106 | bool done; | 107 | bool done; |
107 | }; | 108 | }; |
108 | 109 | ||
109 | static struct acpi_ec { | 110 | struct acpi_ec *boot_ec, *first_ec; |
110 | acpi_handle handle; | 111 | EXPORT_SYMBOL(first_ec); |
111 | unsigned long gpe; | ||
112 | unsigned long command_addr; | ||
113 | unsigned long data_addr; | ||
114 | unsigned long global_lock; | ||
115 | unsigned long flags; | ||
116 | struct mutex lock; | ||
117 | wait_queue_head_t wait; | ||
118 | struct list_head list; | ||
119 | struct transaction *curr; | ||
120 | spinlock_t curr_lock; | ||
121 | } *boot_ec, *first_ec; | ||
122 | 112 | ||
123 | static int EC_FLAGS_MSI; /* Out-of-spec MSI controller */ | 113 | static int EC_FLAGS_MSI; /* Out-of-spec MSI controller */ |
124 | static int EC_FLAGS_VALIDATE_ECDT; /* ASUStec ECDTs need to be validated */ | 114 | static int EC_FLAGS_VALIDATE_ECDT; /* ASUStec ECDTs need to be validated */ |
@@ -679,72 +669,6 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address, | |||
679 | } | 669 | } |
680 | 670 | ||
681 | /* -------------------------------------------------------------------------- | 671 | /* -------------------------------------------------------------------------- |
682 | FS Interface (/proc) | ||
683 | -------------------------------------------------------------------------- */ | ||
684 | |||
685 | static struct proc_dir_entry *acpi_ec_dir; | ||
686 | |||
687 | static int acpi_ec_read_info(struct seq_file *seq, void *offset) | ||
688 | { | ||
689 | struct acpi_ec *ec = seq->private; | ||
690 | |||
691 | if (!ec) | ||
692 | goto end; | ||
693 | |||
694 | seq_printf(seq, "gpe:\t\t\t0x%02x\n", (u32) ec->gpe); | ||
695 | seq_printf(seq, "ports:\t\t\t0x%02x, 0x%02x\n", | ||
696 | (unsigned)ec->command_addr, (unsigned)ec->data_addr); | ||
697 | seq_printf(seq, "use global lock:\t%s\n", | ||
698 | ec->global_lock ? "yes" : "no"); | ||
699 | end: | ||
700 | return 0; | ||
701 | } | ||
702 | |||
703 | static int acpi_ec_info_open_fs(struct inode *inode, struct file *file) | ||
704 | { | ||
705 | return single_open(file, acpi_ec_read_info, PDE(inode)->data); | ||
706 | } | ||
707 | |||
708 | static const struct file_operations acpi_ec_info_ops = { | ||
709 | .open = acpi_ec_info_open_fs, | ||
710 | .read = seq_read, | ||
711 | .llseek = seq_lseek, | ||
712 | .release = single_release, | ||
713 | .owner = THIS_MODULE, | ||
714 | }; | ||
715 | |||
716 | static int acpi_ec_add_fs(struct acpi_device *device) | ||
717 | { | ||
718 | struct proc_dir_entry *entry = NULL; | ||
719 | |||
720 | if (!acpi_device_dir(device)) { | ||
721 | acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), | ||
722 | acpi_ec_dir); | ||
723 | if (!acpi_device_dir(device)) | ||
724 | return -ENODEV; | ||
725 | } | ||
726 | |||
727 | entry = proc_create_data(ACPI_EC_FILE_INFO, S_IRUGO, | ||
728 | acpi_device_dir(device), | ||
729 | &acpi_ec_info_ops, acpi_driver_data(device)); | ||
730 | if (!entry) | ||
731 | return -ENODEV; | ||
732 | return 0; | ||
733 | } | ||
734 | |||
735 | static int acpi_ec_remove_fs(struct acpi_device *device) | ||
736 | { | ||
737 | |||
738 | if (acpi_device_dir(device)) { | ||
739 | remove_proc_entry(ACPI_EC_FILE_INFO, acpi_device_dir(device)); | ||
740 | remove_proc_entry(acpi_device_bid(device), acpi_ec_dir); | ||
741 | acpi_device_dir(device) = NULL; | ||
742 | } | ||
743 | |||
744 | return 0; | ||
745 | } | ||
746 | |||
747 | /* -------------------------------------------------------------------------- | ||
748 | Driver Interface | 672 | Driver Interface |
749 | -------------------------------------------------------------------------- */ | 673 | -------------------------------------------------------------------------- */ |
750 | static acpi_status | 674 | static acpi_status |
@@ -894,7 +818,12 @@ static int acpi_ec_add(struct acpi_device *device) | |||
894 | if (!first_ec) | 818 | if (!first_ec) |
895 | first_ec = ec; | 819 | first_ec = ec; |
896 | device->driver_data = ec; | 820 | device->driver_data = ec; |
897 | acpi_ec_add_fs(device); | 821 | |
822 | WARN(!request_region(ec->data_addr, 1, "EC data"), | ||
823 | "Could not request EC data io port 0x%lx", ec->data_addr); | ||
824 | WARN(!request_region(ec->command_addr, 1, "EC cmd"), | ||
825 | "Could not request EC cmd io port 0x%lx", ec->command_addr); | ||
826 | |||
898 | pr_info(PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx\n", | 827 | pr_info(PREFIX "GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx\n", |
899 | ec->gpe, ec->command_addr, ec->data_addr); | 828 | ec->gpe, ec->command_addr, ec->data_addr); |
900 | 829 | ||
@@ -921,7 +850,8 @@ static int acpi_ec_remove(struct acpi_device *device, int type) | |||
921 | kfree(handler); | 850 | kfree(handler); |
922 | } | 851 | } |
923 | mutex_unlock(&ec->lock); | 852 | mutex_unlock(&ec->lock); |
924 | acpi_ec_remove_fs(device); | 853 | release_region(ec->data_addr, 1); |
854 | release_region(ec->command_addr, 1); | ||
925 | device->driver_data = NULL; | 855 | device->driver_data = NULL; |
926 | if (ec == first_ec) | 856 | if (ec == first_ec) |
927 | first_ec = NULL; | 857 | first_ec = NULL; |
@@ -1120,16 +1050,10 @@ int __init acpi_ec_init(void) | |||
1120 | { | 1050 | { |
1121 | int result = 0; | 1051 | int result = 0; |
1122 | 1052 | ||
1123 | acpi_ec_dir = proc_mkdir(ACPI_EC_CLASS, acpi_root_dir); | ||
1124 | if (!acpi_ec_dir) | ||
1125 | return -ENODEV; | ||
1126 | |||
1127 | /* Now register the driver for the EC */ | 1053 | /* Now register the driver for the EC */ |
1128 | result = acpi_bus_register_driver(&acpi_ec_driver); | 1054 | result = acpi_bus_register_driver(&acpi_ec_driver); |
1129 | if (result < 0) { | 1055 | if (result < 0) |
1130 | remove_proc_entry(ACPI_EC_CLASS, acpi_root_dir); | ||
1131 | return -ENODEV; | 1056 | return -ENODEV; |
1132 | } | ||
1133 | 1057 | ||
1134 | return result; | 1058 | return result; |
1135 | } | 1059 | } |
@@ -1140,9 +1064,6 @@ static void __exit acpi_ec_exit(void) | |||
1140 | { | 1064 | { |
1141 | 1065 | ||
1142 | acpi_bus_unregister_driver(&acpi_ec_driver); | 1066 | acpi_bus_unregister_driver(&acpi_ec_driver); |
1143 | |||
1144 | remove_proc_entry(ACPI_EC_CLASS, acpi_root_dir); | ||
1145 | |||
1146 | return; | 1067 | return; |
1147 | } | 1068 | } |
1148 | #endif /* 0 */ | 1069 | #endif /* 0 */ |
diff --git a/drivers/acpi/ec_sys.c b/drivers/acpi/ec_sys.c new file mode 100644 index 000000000000..0e869b3f81ca --- /dev/null +++ b/drivers/acpi/ec_sys.c | |||
@@ -0,0 +1,160 @@ | |||
1 | /* | ||
2 | * ec_sys.c | ||
3 | * | ||
4 | * Copyright (C) 2010 SUSE Products GmbH/Novell | ||
5 | * Author: | ||
6 | * Thomas Renninger <trenn@suse.de> | ||
7 | * | ||
8 | * This work is licensed under the terms of the GNU GPL, version 2. | ||
9 | */ | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/acpi.h> | ||
13 | #include <linux/debugfs.h> | ||
14 | #include "internal.h" | ||
15 | |||
16 | MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>"); | ||
17 | MODULE_DESCRIPTION("ACPI EC sysfs access driver"); | ||
18 | MODULE_LICENSE("GPL"); | ||
19 | |||
20 | static bool write_support; | ||
21 | module_param(write_support, bool, 0644); | ||
22 | MODULE_PARM_DESC(write_support, "Dangerous, reboot and removal of battery may " | ||
23 | "be needed."); | ||
24 | |||
25 | #define EC_SPACE_SIZE 256 | ||
26 | |||
27 | struct sysdev_class acpi_ec_sysdev_class = { | ||
28 | .name = "ec", | ||
29 | }; | ||
30 | |||
31 | static struct dentry *acpi_ec_debugfs_dir; | ||
32 | |||
33 | static int acpi_ec_open_io(struct inode *i, struct file *f) | ||
34 | { | ||
35 | f->private_data = i->i_private; | ||
36 | return 0; | ||
37 | } | ||
38 | |||
39 | static ssize_t acpi_ec_read_io(struct file *f, char __user *buf, | ||
40 | size_t count, loff_t *off) | ||
41 | { | ||
42 | /* Use this if support reading/writing multiple ECs exists in ec.c: | ||
43 | * struct acpi_ec *ec = ((struct seq_file *)f->private_data)->private; | ||
44 | */ | ||
45 | unsigned int size = EC_SPACE_SIZE; | ||
46 | u8 *data = (u8 *) buf; | ||
47 | loff_t init_off = *off; | ||
48 | int err = 0; | ||
49 | |||
50 | if (*off >= size) | ||
51 | return 0; | ||
52 | if (*off + count >= size) { | ||
53 | size -= *off; | ||
54 | count = size; | ||
55 | } else | ||
56 | size = count; | ||
57 | |||
58 | while (size) { | ||
59 | err = ec_read(*off, &data[*off - init_off]); | ||
60 | if (err) | ||
61 | return err; | ||
62 | *off += 1; | ||
63 | size--; | ||
64 | } | ||
65 | return count; | ||
66 | } | ||
67 | |||
68 | static ssize_t acpi_ec_write_io(struct file *f, const char __user *buf, | ||
69 | size_t count, loff_t *off) | ||
70 | { | ||
71 | /* Use this if support reading/writing multiple ECs exists in ec.c: | ||
72 | * struct acpi_ec *ec = ((struct seq_file *)f->private_data)->private; | ||
73 | */ | ||
74 | |||
75 | unsigned int size = count; | ||
76 | loff_t init_off = *off; | ||
77 | u8 *data = (u8 *) buf; | ||
78 | int err = 0; | ||
79 | |||
80 | if (*off >= EC_SPACE_SIZE) | ||
81 | return 0; | ||
82 | if (*off + count >= EC_SPACE_SIZE) { | ||
83 | size = EC_SPACE_SIZE - *off; | ||
84 | count = size; | ||
85 | } | ||
86 | |||
87 | while (size) { | ||
88 | u8 byte_write = data[*off - init_off]; | ||
89 | err = ec_write(*off, byte_write); | ||
90 | if (err) | ||
91 | return err; | ||
92 | |||
93 | *off += 1; | ||
94 | size--; | ||
95 | } | ||
96 | return count; | ||
97 | } | ||
98 | |||
99 | static struct file_operations acpi_ec_io_ops = { | ||
100 | .owner = THIS_MODULE, | ||
101 | .open = acpi_ec_open_io, | ||
102 | .read = acpi_ec_read_io, | ||
103 | .write = acpi_ec_write_io, | ||
104 | }; | ||
105 | |||
106 | int acpi_ec_add_debugfs(struct acpi_ec *ec, unsigned int ec_device_count) | ||
107 | { | ||
108 | struct dentry *dev_dir; | ||
109 | char name[64]; | ||
110 | mode_t mode = 0400; | ||
111 | |||
112 | if (ec_device_count == 0) { | ||
113 | acpi_ec_debugfs_dir = debugfs_create_dir("ec", NULL); | ||
114 | if (!acpi_ec_debugfs_dir) | ||
115 | return -ENOMEM; | ||
116 | } | ||
117 | |||
118 | sprintf(name, "ec%u", ec_device_count); | ||
119 | dev_dir = debugfs_create_dir(name, acpi_ec_debugfs_dir); | ||
120 | if (!dev_dir) { | ||
121 | if (ec_device_count != 0) | ||
122 | goto error; | ||
123 | return -ENOMEM; | ||
124 | } | ||
125 | |||
126 | if (!debugfs_create_x32("gpe", 0444, dev_dir, (u32 *)&first_ec->gpe)) | ||
127 | goto error; | ||
128 | if (!debugfs_create_bool("use_global_lock", 0444, dev_dir, | ||
129 | (u32 *)&first_ec->global_lock)) | ||
130 | goto error; | ||
131 | |||
132 | if (write_support) | ||
133 | mode = 0600; | ||
134 | if (!debugfs_create_file("io", mode, dev_dir, ec, &acpi_ec_io_ops)) | ||
135 | goto error; | ||
136 | |||
137 | return 0; | ||
138 | |||
139 | error: | ||
140 | debugfs_remove_recursive(acpi_ec_debugfs_dir); | ||
141 | return -ENOMEM; | ||
142 | } | ||
143 | |||
144 | static int __init acpi_ec_sys_init(void) | ||
145 | { | ||
146 | int err = 0; | ||
147 | if (first_ec) | ||
148 | err = acpi_ec_add_debugfs(first_ec, 0); | ||
149 | else | ||
150 | err = -ENODEV; | ||
151 | return err; | ||
152 | } | ||
153 | |||
154 | static void __exit acpi_ec_sys_exit(void) | ||
155 | { | ||
156 | debugfs_remove_recursive(acpi_ec_debugfs_dir); | ||
157 | } | ||
158 | |||
159 | module_init(acpi_ec_sys_init); | ||
160 | module_exit(acpi_ec_sys_exit); | ||
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index f8f190ec066e..8ae27264a00e 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h | |||
@@ -18,6 +18,11 @@ | |||
18 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | 18 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. |
19 | */ | 19 | */ |
20 | 20 | ||
21 | #ifndef _ACPI_INTERNAL_H_ | ||
22 | #define _ACPI_INTERNAL_H_ | ||
23 | |||
24 | #include <linux/sysdev.h> | ||
25 | |||
21 | #define PREFIX "ACPI: " | 26 | #define PREFIX "ACPI: " |
22 | 27 | ||
23 | int init_acpi_device_notify(void); | 28 | int init_acpi_device_notify(void); |
@@ -46,6 +51,23 @@ void acpi_early_processor_set_pdc(void); | |||
46 | /* -------------------------------------------------------------------------- | 51 | /* -------------------------------------------------------------------------- |
47 | Embedded Controller | 52 | Embedded Controller |
48 | -------------------------------------------------------------------------- */ | 53 | -------------------------------------------------------------------------- */ |
54 | struct acpi_ec { | ||
55 | acpi_handle handle; | ||
56 | unsigned long gpe; | ||
57 | unsigned long command_addr; | ||
58 | unsigned long data_addr; | ||
59 | unsigned long global_lock; | ||
60 | unsigned long flags; | ||
61 | struct mutex lock; | ||
62 | wait_queue_head_t wait; | ||
63 | struct list_head list; | ||
64 | struct transaction *curr; | ||
65 | spinlock_t curr_lock; | ||
66 | struct sys_device sysdev; | ||
67 | }; | ||
68 | |||
69 | extern struct acpi_ec *first_ec; | ||
70 | |||
49 | int acpi_ec_init(void); | 71 | int acpi_ec_init(void); |
50 | int acpi_ec_ecdt_probe(void); | 72 | int acpi_ec_ecdt_probe(void); |
51 | int acpi_boot_ec_enable(void); | 73 | int acpi_boot_ec_enable(void); |
@@ -63,3 +85,5 @@ int acpi_sleep_proc_init(void); | |||
63 | #else | 85 | #else |
64 | static inline int acpi_sleep_proc_init(void) { return 0; } | 86 | static inline int acpi_sleep_proc_init(void) { return 0; } |
65 | #endif | 87 | #endif |
88 | |||
89 | #endif /* _ACPI_INTERNAL_H_ */ | ||
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 3e1b8a288719..79baa6368f79 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig | |||
@@ -5,6 +5,7 @@ | |||
5 | menuconfig X86_PLATFORM_DEVICES | 5 | menuconfig X86_PLATFORM_DEVICES |
6 | bool "X86 Platform Specific Device Drivers" | 6 | bool "X86 Platform Specific Device Drivers" |
7 | default y | 7 | default y |
8 | depends on X86 | ||
8 | ---help--- | 9 | ---help--- |
9 | Say Y here to get to see options for device drivers for various | 10 | Say Y here to get to see options for device drivers for various |
10 | x86 platforms, including vendor-specific laptop extension drivers. | 11 | x86 platforms, including vendor-specific laptop extension drivers. |
@@ -151,6 +152,7 @@ config MSI_LAPTOP | |||
151 | depends on ACPI | 152 | depends on ACPI |
152 | depends on BACKLIGHT_CLASS_DEVICE | 153 | depends on BACKLIGHT_CLASS_DEVICE |
153 | depends on RFKILL | 154 | depends on RFKILL |
155 | depends on SERIO_I8042 | ||
154 | ---help--- | 156 | ---help--- |
155 | This is a driver for laptops built by MSI (MICRO-STAR | 157 | This is a driver for laptops built by MSI (MICRO-STAR |
156 | INTERNATIONAL): | 158 | INTERNATIONAL): |
@@ -181,6 +183,8 @@ config COMPAL_LAPTOP | |||
181 | depends on ACPI | 183 | depends on ACPI |
182 | depends on BACKLIGHT_CLASS_DEVICE | 184 | depends on BACKLIGHT_CLASS_DEVICE |
183 | depends on RFKILL | 185 | depends on RFKILL |
186 | depends on HWMON | ||
187 | depends on POWER_SUPPLY | ||
184 | ---help--- | 188 | ---help--- |
185 | This is a driver for laptops built by Compal: | 189 | This is a driver for laptops built by Compal: |
186 | 190 | ||
@@ -520,6 +524,7 @@ config TOSHIBA_BT_RFKILL | |||
520 | config ACPI_CMPC | 524 | config ACPI_CMPC |
521 | tristate "CMPC Laptop Extras" | 525 | tristate "CMPC Laptop Extras" |
522 | depends on X86 && ACPI | 526 | depends on X86 && ACPI |
527 | depends on RFKILL || RFKILL=n | ||
523 | select INPUT | 528 | select INPUT |
524 | select BACKLIGHT_CLASS_DEVICE | 529 | select BACKLIGHT_CLASS_DEVICE |
525 | default n | 530 | default n |
@@ -537,4 +542,43 @@ config INTEL_SCU_IPC | |||
537 | some embedded Intel x86 platforms. This is not needed for PC-type | 542 | some embedded Intel x86 platforms. This is not needed for PC-type |
538 | machines. | 543 | machines. |
539 | 544 | ||
545 | config GPIO_INTEL_PMIC | ||
546 | bool "Intel PMIC GPIO support" | ||
547 | depends on INTEL_SCU_IPC && GPIOLIB | ||
548 | ---help--- | ||
549 | Say Y here to support GPIO via the SCU IPC interface | ||
550 | on Intel MID platforms. | ||
551 | |||
552 | config RAR_REGISTER | ||
553 | bool "Restricted Access Region Register Driver" | ||
554 | depends on PCI && X86_MRST | ||
555 | default n | ||
556 | ---help--- | ||
557 | This driver allows other kernel drivers access to the | ||
558 | contents of the restricted access region control registers. | ||
559 | |||
560 | The restricted access region control registers | ||
561 | (rar_registers) are used to pass address and | ||
562 | locking information on restricted access regions | ||
563 | to other drivers that use restricted access regions. | ||
564 | |||
565 | The restricted access regions are regions of memory | ||
566 | on the Intel MID Platform that are not accessible to | ||
567 | the x86 processor, but are accessible to dedicated | ||
568 | processors on board peripheral devices. | ||
569 | |||
570 | The purpose of the restricted access regions is to | ||
571 | protect sensitive data from compromise by unauthorized | ||
572 | programs running on the x86 processor. | ||
573 | |||
574 | config INTEL_IPS | ||
575 | tristate "Intel Intelligent Power Sharing" | ||
576 | depends on ACPI | ||
577 | ---help--- | ||
578 | Intel Calpella platforms support dynamic power sharing between the | ||
579 | CPU and GPU, maximizing performance in a given TDP. This driver, | ||
580 | along with the CPU frequency and i915 drivers, provides that | ||
581 | functionality. If in doubt, say Y here; it will only load on | ||
582 | supported platforms. | ||
583 | |||
540 | endif # X86_PLATFORM_DEVICES | 584 | endif # X86_PLATFORM_DEVICES |
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 8770bfe71431..4744c7744ffa 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile | |||
@@ -26,3 +26,7 @@ obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o | |||
26 | obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o | 26 | obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o |
27 | obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o | 27 | obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o |
28 | obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o | 28 | obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o |
29 | obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o | ||
30 | obj-$(CONFIG_INTEL_IPS) += intel_ips.o | ||
31 | obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o | ||
32 | |||
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 1ea6c434d330..2badee2fdeed 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c | |||
@@ -50,17 +50,6 @@ MODULE_LICENSE("GPL"); | |||
50 | #define ACER_INFO KERN_INFO ACER_LOGPREFIX | 50 | #define ACER_INFO KERN_INFO ACER_LOGPREFIX |
51 | 51 | ||
52 | /* | 52 | /* |
53 | * The following defines quirks to get some specific functions to work | ||
54 | * which are known to not be supported over ACPI-WMI (such as the mail LED | ||
55 | * on WMID based Acer's) | ||
56 | */ | ||
57 | struct acer_quirks { | ||
58 | const char *vendor; | ||
59 | const char *model; | ||
60 | u16 quirks; | ||
61 | }; | ||
62 | |||
63 | /* | ||
64 | * Magic Number | 53 | * Magic Number |
65 | * Meaning is unknown - this number is required for writing to ACPI for AMW0 | 54 | * Meaning is unknown - this number is required for writing to ACPI for AMW0 |
66 | * (it's also used in acerhk when directly accessing the BIOS) | 55 | * (it's also used in acerhk when directly accessing the BIOS) |
@@ -200,7 +189,7 @@ static void set_quirks(void) | |||
200 | static int dmi_matched(const struct dmi_system_id *dmi) | 189 | static int dmi_matched(const struct dmi_system_id *dmi) |
201 | { | 190 | { |
202 | quirks = dmi->driver_data; | 191 | quirks = dmi->driver_data; |
203 | return 0; | 192 | return 1; |
204 | } | 193 | } |
205 | 194 | ||
206 | static struct quirk_entry quirk_unknown = { | 195 | static struct quirk_entry quirk_unknown = { |
@@ -555,6 +544,7 @@ static acpi_status AMW0_find_mailled(void) | |||
555 | obj->buffer.length == sizeof(struct wmab_ret)) { | 544 | obj->buffer.length == sizeof(struct wmab_ret)) { |
556 | ret = *((struct wmab_ret *) obj->buffer.pointer); | 545 | ret = *((struct wmab_ret *) obj->buffer.pointer); |
557 | } else { | 546 | } else { |
547 | kfree(out.pointer); | ||
558 | return AE_ERROR; | 548 | return AE_ERROR; |
559 | } | 549 | } |
560 | 550 | ||
@@ -570,7 +560,7 @@ static acpi_status AMW0_set_capabilities(void) | |||
570 | { | 560 | { |
571 | struct wmab_args args; | 561 | struct wmab_args args; |
572 | struct wmab_ret ret; | 562 | struct wmab_ret ret; |
573 | acpi_status status = AE_OK; | 563 | acpi_status status; |
574 | struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; | 564 | struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; |
575 | union acpi_object *obj; | 565 | union acpi_object *obj; |
576 | 566 | ||
@@ -593,12 +583,13 @@ static acpi_status AMW0_set_capabilities(void) | |||
593 | if (ACPI_FAILURE(status)) | 583 | if (ACPI_FAILURE(status)) |
594 | return status; | 584 | return status; |
595 | 585 | ||
596 | obj = (union acpi_object *) out.pointer; | 586 | obj = out.pointer; |
597 | if (obj && obj->type == ACPI_TYPE_BUFFER && | 587 | if (obj && obj->type == ACPI_TYPE_BUFFER && |
598 | obj->buffer.length == sizeof(struct wmab_ret)) { | 588 | obj->buffer.length == sizeof(struct wmab_ret)) { |
599 | ret = *((struct wmab_ret *) obj->buffer.pointer); | 589 | ret = *((struct wmab_ret *) obj->buffer.pointer); |
600 | } else { | 590 | } else { |
601 | return AE_ERROR; | 591 | status = AE_ERROR; |
592 | goto out; | ||
602 | } | 593 | } |
603 | 594 | ||
604 | if (ret.eax & 0x1) | 595 | if (ret.eax & 0x1) |
@@ -607,23 +598,26 @@ static acpi_status AMW0_set_capabilities(void) | |||
607 | args.ebx = 2 << 8; | 598 | args.ebx = 2 << 8; |
608 | args.ebx |= ACER_AMW0_BLUETOOTH_MASK; | 599 | args.ebx |= ACER_AMW0_BLUETOOTH_MASK; |
609 | 600 | ||
601 | /* | ||
602 | * It's ok to use existing buffer for next wmab_execute call. | ||
603 | * But we need to kfree(out.pointer) if next wmab_execute fail. | ||
604 | */ | ||
610 | status = wmab_execute(&args, &out); | 605 | status = wmab_execute(&args, &out); |
611 | if (ACPI_FAILURE(status)) | 606 | if (ACPI_FAILURE(status)) |
612 | return status; | 607 | goto out; |
613 | 608 | ||
614 | obj = (union acpi_object *) out.pointer; | 609 | obj = (union acpi_object *) out.pointer; |
615 | if (obj && obj->type == ACPI_TYPE_BUFFER | 610 | if (obj && obj->type == ACPI_TYPE_BUFFER |
616 | && obj->buffer.length == sizeof(struct wmab_ret)) { | 611 | && obj->buffer.length == sizeof(struct wmab_ret)) { |
617 | ret = *((struct wmab_ret *) obj->buffer.pointer); | 612 | ret = *((struct wmab_ret *) obj->buffer.pointer); |
618 | } else { | 613 | } else { |
619 | return AE_ERROR; | 614 | status = AE_ERROR; |
615 | goto out; | ||
620 | } | 616 | } |
621 | 617 | ||
622 | if (ret.eax & 0x1) | 618 | if (ret.eax & 0x1) |
623 | interface->capability |= ACER_CAP_BLUETOOTH; | 619 | interface->capability |= ACER_CAP_BLUETOOTH; |
624 | 620 | ||
625 | kfree(out.pointer); | ||
626 | |||
627 | /* | 621 | /* |
628 | * This appears to be safe to enable, since all Wistron based laptops | 622 | * This appears to be safe to enable, since all Wistron based laptops |
629 | * appear to use the same EC register for brightness, even if they | 623 | * appear to use the same EC register for brightness, even if they |
@@ -632,7 +626,10 @@ static acpi_status AMW0_set_capabilities(void) | |||
632 | if (quirks->brightness >= 0) | 626 | if (quirks->brightness >= 0) |
633 | interface->capability |= ACER_CAP_BRIGHTNESS; | 627 | interface->capability |= ACER_CAP_BRIGHTNESS; |
634 | 628 | ||
635 | return AE_OK; | 629 | status = AE_OK; |
630 | out: | ||
631 | kfree(out.pointer); | ||
632 | return status; | ||
636 | } | 633 | } |
637 | 634 | ||
638 | static struct wmi_interface AMW0_interface = { | 635 | static struct wmi_interface AMW0_interface = { |
@@ -772,6 +769,7 @@ static acpi_status WMID_set_capabilities(void) | |||
772 | obj->buffer.length == sizeof(u32)) { | 769 | obj->buffer.length == sizeof(u32)) { |
773 | devices = *((u32 *) obj->buffer.pointer); | 770 | devices = *((u32 *) obj->buffer.pointer); |
774 | } else { | 771 | } else { |
772 | kfree(out.pointer); | ||
775 | return AE_ERROR; | 773 | return AE_ERROR; |
776 | } | 774 | } |
777 | 775 | ||
@@ -788,6 +786,7 @@ static acpi_status WMID_set_capabilities(void) | |||
788 | if (!(devices & 0x20)) | 786 | if (!(devices & 0x20)) |
789 | max_brightness = 0x9; | 787 | max_brightness = 0x9; |
790 | 788 | ||
789 | kfree(out.pointer); | ||
791 | return status; | 790 | return status; |
792 | } | 791 | } |
793 | 792 | ||
@@ -1084,8 +1083,7 @@ static ssize_t show_interface(struct device *dev, struct device_attribute *attr, | |||
1084 | } | 1083 | } |
1085 | } | 1084 | } |
1086 | 1085 | ||
1087 | static DEVICE_ATTR(interface, S_IWUGO | S_IRUGO | S_IWUSR, | 1086 | static DEVICE_ATTR(interface, S_IRUGO, show_interface, NULL); |
1088 | show_interface, NULL); | ||
1089 | 1087 | ||
1090 | /* | 1088 | /* |
1091 | * debugfs functions | 1089 | * debugfs functions |
@@ -1095,6 +1093,7 @@ static u32 get_wmid_devices(void) | |||
1095 | struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; | 1093 | struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; |
1096 | union acpi_object *obj; | 1094 | union acpi_object *obj; |
1097 | acpi_status status; | 1095 | acpi_status status; |
1096 | u32 devices = 0; | ||
1098 | 1097 | ||
1099 | status = wmi_query_block(WMID_GUID2, 1, &out); | 1098 | status = wmi_query_block(WMID_GUID2, 1, &out); |
1100 | if (ACPI_FAILURE(status)) | 1099 | if (ACPI_FAILURE(status)) |
@@ -1103,10 +1102,11 @@ static u32 get_wmid_devices(void) | |||
1103 | obj = (union acpi_object *) out.pointer; | 1102 | obj = (union acpi_object *) out.pointer; |
1104 | if (obj && obj->type == ACPI_TYPE_BUFFER && | 1103 | if (obj && obj->type == ACPI_TYPE_BUFFER && |
1105 | obj->buffer.length == sizeof(u32)) { | 1104 | obj->buffer.length == sizeof(u32)) { |
1106 | return *((u32 *) obj->buffer.pointer); | 1105 | devices = *((u32 *) obj->buffer.pointer); |
1107 | } else { | ||
1108 | return 0; | ||
1109 | } | 1106 | } |
1107 | |||
1108 | kfree(out.pointer); | ||
1109 | return devices; | ||
1110 | } | 1110 | } |
1111 | 1111 | ||
1112 | /* | 1112 | /* |
@@ -1327,22 +1327,31 @@ static int __init acer_wmi_init(void) | |||
1327 | "generic video driver\n"); | 1327 | "generic video driver\n"); |
1328 | } | 1328 | } |
1329 | 1329 | ||
1330 | if (platform_driver_register(&acer_platform_driver)) { | 1330 | err = platform_driver_register(&acer_platform_driver); |
1331 | if (err) { | ||
1331 | printk(ACER_ERR "Unable to register platform driver.\n"); | 1332 | printk(ACER_ERR "Unable to register platform driver.\n"); |
1332 | goto error_platform_register; | 1333 | goto error_platform_register; |
1333 | } | 1334 | } |
1335 | |||
1334 | acer_platform_device = platform_device_alloc("acer-wmi", -1); | 1336 | acer_platform_device = platform_device_alloc("acer-wmi", -1); |
1335 | platform_device_add(acer_platform_device); | 1337 | if (!acer_platform_device) { |
1338 | err = -ENOMEM; | ||
1339 | goto error_device_alloc; | ||
1340 | } | ||
1341 | |||
1342 | err = platform_device_add(acer_platform_device); | ||
1343 | if (err) | ||
1344 | goto error_device_add; | ||
1336 | 1345 | ||
1337 | err = create_sysfs(); | 1346 | err = create_sysfs(); |
1338 | if (err) | 1347 | if (err) |
1339 | return err; | 1348 | goto error_create_sys; |
1340 | 1349 | ||
1341 | if (wmi_has_guid(WMID_GUID2)) { | 1350 | if (wmi_has_guid(WMID_GUID2)) { |
1342 | interface->debug.wmid_devices = get_wmid_devices(); | 1351 | interface->debug.wmid_devices = get_wmid_devices(); |
1343 | err = create_debugfs(); | 1352 | err = create_debugfs(); |
1344 | if (err) | 1353 | if (err) |
1345 | return err; | 1354 | goto error_create_debugfs; |
1346 | } | 1355 | } |
1347 | 1356 | ||
1348 | /* Override any initial settings with values from the commandline */ | 1357 | /* Override any initial settings with values from the commandline */ |
@@ -1350,15 +1359,23 @@ static int __init acer_wmi_init(void) | |||
1350 | 1359 | ||
1351 | return 0; | 1360 | return 0; |
1352 | 1361 | ||
1362 | error_create_debugfs: | ||
1363 | remove_sysfs(acer_platform_device); | ||
1364 | error_create_sys: | ||
1365 | platform_device_del(acer_platform_device); | ||
1366 | error_device_add: | ||
1367 | platform_device_put(acer_platform_device); | ||
1368 | error_device_alloc: | ||
1369 | platform_driver_unregister(&acer_platform_driver); | ||
1353 | error_platform_register: | 1370 | error_platform_register: |
1354 | return -ENODEV; | 1371 | return err; |
1355 | } | 1372 | } |
1356 | 1373 | ||
1357 | static void __exit acer_wmi_exit(void) | 1374 | static void __exit acer_wmi_exit(void) |
1358 | { | 1375 | { |
1359 | remove_sysfs(acer_platform_device); | 1376 | remove_sysfs(acer_platform_device); |
1360 | remove_debugfs(); | 1377 | remove_debugfs(); |
1361 | platform_device_del(acer_platform_device); | 1378 | platform_device_unregister(acer_platform_device); |
1362 | platform_driver_unregister(&acer_platform_driver); | 1379 | platform_driver_unregister(&acer_platform_driver); |
1363 | 1380 | ||
1364 | printk(ACER_INFO "Acer Laptop WMI Extras unloaded\n"); | 1381 | printk(ACER_INFO "Acer Laptop WMI Extras unloaded\n"); |
diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index 7b2384d674d0..60f9cfcac93f 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c | |||
@@ -52,7 +52,7 @@ | |||
52 | */ | 52 | */ |
53 | #undef START_IN_KERNEL_MODE | 53 | #undef START_IN_KERNEL_MODE |
54 | 54 | ||
55 | #define DRV_VER "0.5.22" | 55 | #define DRV_VER "0.5.24" |
56 | 56 | ||
57 | /* | 57 | /* |
58 | * According to the Atom N270 datasheet, | 58 | * According to the Atom N270 datasheet, |
@@ -92,9 +92,9 @@ static unsigned int fanstate = ACERHDF_FAN_AUTO; | |||
92 | static char force_bios[16]; | 92 | static char force_bios[16]; |
93 | static char force_product[16]; | 93 | static char force_product[16]; |
94 | static unsigned int prev_interval; | 94 | static unsigned int prev_interval; |
95 | struct thermal_zone_device *thz_dev; | 95 | static struct thermal_zone_device *thz_dev; |
96 | struct thermal_cooling_device *cl_dev; | 96 | static struct thermal_cooling_device *cl_dev; |
97 | struct platform_device *acerhdf_dev; | 97 | static struct platform_device *acerhdf_dev; |
98 | 98 | ||
99 | module_param(kernelmode, uint, 0); | 99 | module_param(kernelmode, uint, 0); |
100 | MODULE_PARM_DESC(kernelmode, "Kernel mode fan control on / off"); | 100 | MODULE_PARM_DESC(kernelmode, "Kernel mode fan control on / off"); |
@@ -112,14 +112,12 @@ module_param_string(force_product, force_product, 16, 0); | |||
112 | MODULE_PARM_DESC(force_product, "Force BIOS product and omit BIOS check"); | 112 | MODULE_PARM_DESC(force_product, "Force BIOS product and omit BIOS check"); |
113 | 113 | ||
114 | /* | 114 | /* |
115 | * cmd_off: to switch the fan completely off | 115 | * cmd_off: to switch the fan completely off and check if the fan is off |
116 | * chk_off: to check if the fan is off | ||
117 | * cmd_auto: to set the BIOS in control of the fan. The BIOS regulates then | 116 | * cmd_auto: to set the BIOS in control of the fan. The BIOS regulates then |
118 | * the fan speed depending on the temperature | 117 | * the fan speed depending on the temperature |
119 | */ | 118 | */ |
120 | struct fancmd { | 119 | struct fancmd { |
121 | u8 cmd_off; | 120 | u8 cmd_off; |
122 | u8 chk_off; | ||
123 | u8 cmd_auto; | 121 | u8 cmd_auto; |
124 | }; | 122 | }; |
125 | 123 | ||
@@ -136,47 +134,81 @@ struct bios_settings_t { | |||
136 | /* Register addresses and values for different BIOS versions */ | 134 | /* Register addresses and values for different BIOS versions */ |
137 | static const struct bios_settings_t bios_tbl[] = { | 135 | static const struct bios_settings_t bios_tbl[] = { |
138 | /* AOA110 */ | 136 | /* AOA110 */ |
139 | {"Acer", "AOA110", "v0.3109", 0x55, 0x58, {0x1f, 0x1f, 0x00} }, | 137 | {"Acer", "AOA110", "v0.3109", 0x55, 0x58, {0x1f, 0x00} }, |
140 | {"Acer", "AOA110", "v0.3114", 0x55, 0x58, {0x1f, 0x1f, 0x00} }, | 138 | {"Acer", "AOA110", "v0.3114", 0x55, 0x58, {0x1f, 0x00} }, |
141 | {"Acer", "AOA110", "v0.3301", 0x55, 0x58, {0xaf, 0xaf, 0x00} }, | 139 | {"Acer", "AOA110", "v0.3301", 0x55, 0x58, {0xaf, 0x00} }, |
142 | {"Acer", "AOA110", "v0.3304", 0x55, 0x58, {0xaf, 0xaf, 0x00} }, | 140 | {"Acer", "AOA110", "v0.3304", 0x55, 0x58, {0xaf, 0x00} }, |
143 | {"Acer", "AOA110", "v0.3305", 0x55, 0x58, {0xaf, 0xaf, 0x00} }, | 141 | {"Acer", "AOA110", "v0.3305", 0x55, 0x58, {0xaf, 0x00} }, |
144 | {"Acer", "AOA110", "v0.3307", 0x55, 0x58, {0xaf, 0xaf, 0x00} }, | 142 | {"Acer", "AOA110", "v0.3307", 0x55, 0x58, {0xaf, 0x00} }, |
145 | {"Acer", "AOA110", "v0.3308", 0x55, 0x58, {0x21, 0x21, 0x00} }, | 143 | {"Acer", "AOA110", "v0.3308", 0x55, 0x58, {0x21, 0x00} }, |
146 | {"Acer", "AOA110", "v0.3309", 0x55, 0x58, {0x21, 0x21, 0x00} }, | 144 | {"Acer", "AOA110", "v0.3309", 0x55, 0x58, {0x21, 0x00} }, |
147 | {"Acer", "AOA110", "v0.3310", 0x55, 0x58, {0x21, 0x21, 0x00} }, | 145 | {"Acer", "AOA110", "v0.3310", 0x55, 0x58, {0x21, 0x00} }, |
148 | /* AOA150 */ | 146 | /* AOA150 */ |
149 | {"Acer", "AOA150", "v0.3114", 0x55, 0x58, {0x20, 0x20, 0x00} }, | 147 | {"Acer", "AOA150", "v0.3114", 0x55, 0x58, {0x1f, 0x00} }, |
150 | {"Acer", "AOA150", "v0.3301", 0x55, 0x58, {0x20, 0x20, 0x00} }, | 148 | {"Acer", "AOA150", "v0.3301", 0x55, 0x58, {0x20, 0x00} }, |
151 | {"Acer", "AOA150", "v0.3304", 0x55, 0x58, {0x20, 0x20, 0x00} }, | 149 | {"Acer", "AOA150", "v0.3304", 0x55, 0x58, {0x20, 0x00} }, |
152 | {"Acer", "AOA150", "v0.3305", 0x55, 0x58, {0x20, 0x20, 0x00} }, | 150 | {"Acer", "AOA150", "v0.3305", 0x55, 0x58, {0x20, 0x00} }, |
153 | {"Acer", "AOA150", "v0.3307", 0x55, 0x58, {0x20, 0x20, 0x00} }, | 151 | {"Acer", "AOA150", "v0.3307", 0x55, 0x58, {0x20, 0x00} }, |
154 | {"Acer", "AOA150", "v0.3308", 0x55, 0x58, {0x20, 0x20, 0x00} }, | 152 | {"Acer", "AOA150", "v0.3308", 0x55, 0x58, {0x20, 0x00} }, |
155 | {"Acer", "AOA150", "v0.3309", 0x55, 0x58, {0x20, 0x20, 0x00} }, | 153 | {"Acer", "AOA150", "v0.3309", 0x55, 0x58, {0x20, 0x00} }, |
156 | {"Acer", "AOA150", "v0.3310", 0x55, 0x58, {0x20, 0x20, 0x00} }, | 154 | {"Acer", "AOA150", "v0.3310", 0x55, 0x58, {0x20, 0x00} }, |
157 | /* Acer 1410 */ | 155 | /* Acer 1410 */ |
158 | {"Acer", "Aspire 1410", "v0.3120", 0x55, 0x58, {0x9e, 0x9e, 0x00} }, | 156 | {"Acer", "Aspire 1410", "v0.3108", 0x55, 0x58, {0x9e, 0x00} }, |
159 | {"Acer", "Aspire 1410", "v1.3303", 0x55, 0x58, {0x9e, 0x9e, 0x00} }, | 157 | {"Acer", "Aspire 1410", "v0.3113", 0x55, 0x58, {0x9e, 0x00} }, |
158 | {"Acer", "Aspire 1410", "v0.3115", 0x55, 0x58, {0x9e, 0x00} }, | ||
159 | {"Acer", "Aspire 1410", "v0.3117", 0x55, 0x58, {0x9e, 0x00} }, | ||
160 | {"Acer", "Aspire 1410", "v0.3119", 0x55, 0x58, {0x9e, 0x00} }, | ||
161 | {"Acer", "Aspire 1410", "v0.3120", 0x55, 0x58, {0x9e, 0x00} }, | ||
162 | {"Acer", "Aspire 1410", "v1.3204", 0x55, 0x58, {0x9e, 0x00} }, | ||
163 | {"Acer", "Aspire 1410", "v1.3303", 0x55, 0x58, {0x9e, 0x00} }, | ||
164 | {"Acer", "Aspire 1410", "v1.3308", 0x55, 0x58, {0x9e, 0x00} }, | ||
165 | {"Acer", "Aspire 1410", "v1.3310", 0x55, 0x58, {0x9e, 0x00} }, | ||
160 | /* Acer 1810xx */ | 166 | /* Acer 1810xx */ |
161 | {"Acer", "Aspire 1810TZ", "v0.3120", 0x55, 0x58, {0x9e, 0x9e, 0x00} }, | 167 | {"Acer", "Aspire 1810TZ", "v0.3108", 0x55, 0x58, {0x9e, 0x00} }, |
162 | {"Acer", "Aspire 1810T", "v0.3120", 0x55, 0x58, {0x9e, 0x9e, 0x00} }, | 168 | {"Acer", "Aspire 1810T", "v0.3108", 0x55, 0x58, {0x9e, 0x00} }, |
163 | {"Acer", "Aspire 1810T", "v1.3303", 0x55, 0x58, {0x9e, 0x9e, 0x00} }, | 169 | {"Acer", "Aspire 1810TZ", "v0.3113", 0x55, 0x58, {0x9e, 0x00} }, |
164 | {"Acer", "Aspire 1810TZ", "v1.3303", 0x55, 0x58, {0x9e, 0x9e, 0x00} }, | 170 | {"Acer", "Aspire 1810T", "v0.3113", 0x55, 0x58, {0x9e, 0x00} }, |
171 | {"Acer", "Aspire 1810TZ", "v0.3115", 0x55, 0x58, {0x9e, 0x00} }, | ||
172 | {"Acer", "Aspire 1810T", "v0.3115", 0x55, 0x58, {0x9e, 0x00} }, | ||
173 | {"Acer", "Aspire 1810TZ", "v0.3117", 0x55, 0x58, {0x9e, 0x00} }, | ||
174 | {"Acer", "Aspire 1810T", "v0.3117", 0x55, 0x58, {0x9e, 0x00} }, | ||
175 | {"Acer", "Aspire 1810TZ", "v0.3119", 0x55, 0x58, {0x9e, 0x00} }, | ||
176 | {"Acer", "Aspire 1810T", "v0.3119", 0x55, 0x58, {0x9e, 0x00} }, | ||
177 | {"Acer", "Aspire 1810TZ", "v0.3120", 0x55, 0x58, {0x9e, 0x00} }, | ||
178 | {"Acer", "Aspire 1810T", "v0.3120", 0x55, 0x58, {0x9e, 0x00} }, | ||
179 | {"Acer", "Aspire 1810TZ", "v1.3204", 0x55, 0x58, {0x9e, 0x00} }, | ||
180 | {"Acer", "Aspire 1810T", "v1.3204", 0x55, 0x58, {0x9e, 0x00} }, | ||
181 | {"Acer", "Aspire 1810TZ", "v1.3303", 0x55, 0x58, {0x9e, 0x00} }, | ||
182 | {"Acer", "Aspire 1810T", "v1.3303", 0x55, 0x58, {0x9e, 0x00} }, | ||
183 | {"Acer", "Aspire 1810TZ", "v1.3308", 0x55, 0x58, {0x9e, 0x00} }, | ||
184 | {"Acer", "Aspire 1810T", "v1.3308", 0x55, 0x58, {0x9e, 0x00} }, | ||
185 | {"Acer", "Aspire 1810TZ", "v1.3310", 0x55, 0x58, {0x9e, 0x00} }, | ||
186 | {"Acer", "Aspire 1810T", "v1.3310", 0x55, 0x58, {0x9e, 0x00} }, | ||
187 | /* Acer 531 */ | ||
188 | {"Acer", "AO531h", "v0.3201", 0x55, 0x58, {0x20, 0x00} }, | ||
165 | /* Gateway */ | 189 | /* Gateway */ |
166 | {"Gateway", "AOA110", "v0.3103", 0x55, 0x58, {0x21, 0x21, 0x00} }, | 190 | {"Gateway", "AOA110", "v0.3103", 0x55, 0x58, {0x21, 0x00} }, |
167 | {"Gateway", "AOA150", "v0.3103", 0x55, 0x58, {0x20, 0x20, 0x00} }, | 191 | {"Gateway", "AOA150", "v0.3103", 0x55, 0x58, {0x20, 0x00} }, |
168 | {"Gateway", "LT31", "v1.3103", 0x55, 0x58, {0x10, 0x0f, 0x00} }, | 192 | {"Gateway", "LT31", "v1.3103", 0x55, 0x58, {0x9e, 0x00} }, |
169 | {"Gateway", "LT31", "v1.3201", 0x55, 0x58, {0x10, 0x0f, 0x00} }, | 193 | {"Gateway", "LT31", "v1.3201", 0x55, 0x58, {0x9e, 0x00} }, |
170 | {"Gateway", "LT31", "v1.3302", 0x55, 0x58, {0x10, 0x0f, 0x00} }, | 194 | {"Gateway", "LT31", "v1.3302", 0x55, 0x58, {0x9e, 0x00} }, |
171 | /* Packard Bell */ | 195 | /* Packard Bell */ |
172 | {"Packard Bell", "DOA150", "v0.3104", 0x55, 0x58, {0x21, 0x21, 0x00} }, | 196 | {"Packard Bell", "DOA150", "v0.3104", 0x55, 0x58, {0x21, 0x00} }, |
173 | {"Packard Bell", "DOA150", "v0.3105", 0x55, 0x58, {0x20, 0x20, 0x00} }, | 197 | {"Packard Bell", "DOA150", "v0.3105", 0x55, 0x58, {0x20, 0x00} }, |
174 | {"Packard Bell", "AOA110", "v0.3105", 0x55, 0x58, {0x21, 0x21, 0x00} }, | 198 | {"Packard Bell", "AOA110", "v0.3105", 0x55, 0x58, {0x21, 0x00} }, |
175 | {"Packard Bell", "AOA150", "v0.3105", 0x55, 0x58, {0x20, 0x20, 0x00} }, | 199 | {"Packard Bell", "AOA150", "v0.3105", 0x55, 0x58, {0x20, 0x00} }, |
176 | {"Packard Bell", "DOTMU", "v1.3303", 0x55, 0x58, {0x9e, 0x9e, 0x00} }, | 200 | {"Packard Bell", "DOTMU", "v1.3303", 0x55, 0x58, {0x9e, 0x00} }, |
177 | {"Packard Bell", "DOTMU", "v0.3120", 0x55, 0x58, {0x9e, 0x9e, 0x00} }, | 201 | {"Packard Bell", "DOTMU", "v0.3120", 0x55, 0x58, {0x9e, 0x00} }, |
202 | {"Packard Bell", "DOTMU", "v0.3108", 0x55, 0x58, {0x9e, 0x00} }, | ||
203 | {"Packard Bell", "DOTMU", "v0.3113", 0x55, 0x58, {0x9e, 0x00} }, | ||
204 | {"Packard Bell", "DOTMU", "v0.3115", 0x55, 0x58, {0x9e, 0x00} }, | ||
205 | {"Packard Bell", "DOTMU", "v0.3117", 0x55, 0x58, {0x9e, 0x00} }, | ||
206 | {"Packard Bell", "DOTMU", "v0.3119", 0x55, 0x58, {0x9e, 0x00} }, | ||
207 | {"Packard Bell", "DOTMU", "v1.3204", 0x55, 0x58, {0x9e, 0x00} }, | ||
208 | {"Packard Bell", "DOTMA", "v1.3201", 0x55, 0x58, {0x9e, 0x00} }, | ||
209 | {"Packard Bell", "DOTMA", "v1.3302", 0x55, 0x58, {0x9e, 0x00} }, | ||
178 | /* pewpew-terminator */ | 210 | /* pewpew-terminator */ |
179 | {"", "", "", 0, 0, {0, 0, 0} } | 211 | {"", "", "", 0, 0, {0, 0} } |
180 | }; | 212 | }; |
181 | 213 | ||
182 | static const struct bios_settings_t *bios_cfg __read_mostly; | 214 | static const struct bios_settings_t *bios_cfg __read_mostly; |
@@ -200,7 +232,7 @@ static int acerhdf_get_fanstate(int *state) | |||
200 | if (ec_read(bios_cfg->fanreg, &fan)) | 232 | if (ec_read(bios_cfg->fanreg, &fan)) |
201 | return -EINVAL; | 233 | return -EINVAL; |
202 | 234 | ||
203 | if (fan != bios_cfg->cmd.chk_off) | 235 | if (fan != bios_cfg->cmd.cmd_off) |
204 | *state = ACERHDF_FAN_AUTO; | 236 | *state = ACERHDF_FAN_AUTO; |
205 | else | 237 | else |
206 | *state = ACERHDF_FAN_OFF; | 238 | *state = ACERHDF_FAN_OFF; |
@@ -374,7 +406,7 @@ static int acerhdf_get_crit_temp(struct thermal_zone_device *thermal, | |||
374 | } | 406 | } |
375 | 407 | ||
376 | /* bind callback functions to thermalzone */ | 408 | /* bind callback functions to thermalzone */ |
377 | struct thermal_zone_device_ops acerhdf_dev_ops = { | 409 | static struct thermal_zone_device_ops acerhdf_dev_ops = { |
378 | .bind = acerhdf_bind, | 410 | .bind = acerhdf_bind, |
379 | .unbind = acerhdf_unbind, | 411 | .unbind = acerhdf_unbind, |
380 | .get_temp = acerhdf_get_ec_temp, | 412 | .get_temp = acerhdf_get_ec_temp, |
@@ -449,7 +481,7 @@ err_out: | |||
449 | } | 481 | } |
450 | 482 | ||
451 | /* bind fan callbacks to fan device */ | 483 | /* bind fan callbacks to fan device */ |
452 | struct thermal_cooling_device_ops acerhdf_cooling_ops = { | 484 | static struct thermal_cooling_device_ops acerhdf_cooling_ops = { |
453 | .get_max_state = acerhdf_get_max_state, | 485 | .get_max_state = acerhdf_get_max_state, |
454 | .get_cur_state = acerhdf_get_cur_state, | 486 | .get_cur_state = acerhdf_get_cur_state, |
455 | .set_cur_state = acerhdf_set_cur_state, | 487 | .set_cur_state = acerhdf_set_cur_state, |
@@ -518,6 +550,10 @@ static int acerhdf_check_hardware(void) | |||
518 | version = dmi_get_system_info(DMI_BIOS_VERSION); | 550 | version = dmi_get_system_info(DMI_BIOS_VERSION); |
519 | product = dmi_get_system_info(DMI_PRODUCT_NAME); | 551 | product = dmi_get_system_info(DMI_PRODUCT_NAME); |
520 | 552 | ||
553 | if (!vendor || !version || !product) { | ||
554 | pr_err("error getting hardware information\n"); | ||
555 | return -EINVAL; | ||
556 | } | ||
521 | 557 | ||
522 | pr_info("Acer Aspire One Fan driver, v.%s\n", DRV_VER); | 558 | pr_info("Acer Aspire One Fan driver, v.%s\n", DRV_VER); |
523 | 559 | ||
@@ -579,17 +615,26 @@ static int acerhdf_register_platform(void) | |||
579 | return err; | 615 | return err; |
580 | 616 | ||
581 | acerhdf_dev = platform_device_alloc("acerhdf", -1); | 617 | acerhdf_dev = platform_device_alloc("acerhdf", -1); |
582 | platform_device_add(acerhdf_dev); | 618 | if (!acerhdf_dev) { |
619 | err = -ENOMEM; | ||
620 | goto err_device_alloc; | ||
621 | } | ||
622 | err = platform_device_add(acerhdf_dev); | ||
623 | if (err) | ||
624 | goto err_device_add; | ||
583 | 625 | ||
584 | return 0; | 626 | return 0; |
627 | |||
628 | err_device_add: | ||
629 | platform_device_put(acerhdf_dev); | ||
630 | err_device_alloc: | ||
631 | platform_driver_unregister(&acerhdf_driver); | ||
632 | return err; | ||
585 | } | 633 | } |
586 | 634 | ||
587 | static void acerhdf_unregister_platform(void) | 635 | static void acerhdf_unregister_platform(void) |
588 | { | 636 | { |
589 | if (!acerhdf_dev) | 637 | platform_device_unregister(acerhdf_dev); |
590 | return; | ||
591 | |||
592 | platform_device_del(acerhdf_dev); | ||
593 | platform_driver_unregister(&acerhdf_driver); | 638 | platform_driver_unregister(&acerhdf_driver); |
594 | } | 639 | } |
595 | 640 | ||
@@ -633,7 +678,7 @@ static int __init acerhdf_init(void) | |||
633 | 678 | ||
634 | err = acerhdf_register_platform(); | 679 | err = acerhdf_register_platform(); |
635 | if (err) | 680 | if (err) |
636 | goto err_unreg; | 681 | goto out_err; |
637 | 682 | ||
638 | err = acerhdf_register_thermal(); | 683 | err = acerhdf_register_thermal(); |
639 | if (err) | 684 | if (err) |
@@ -646,7 +691,7 @@ err_unreg: | |||
646 | acerhdf_unregister_platform(); | 691 | acerhdf_unregister_platform(); |
647 | 692 | ||
648 | out_err: | 693 | out_err: |
649 | return -ENODEV; | 694 | return err; |
650 | } | 695 | } |
651 | 696 | ||
652 | static void __exit acerhdf_exit(void) | 697 | static void __exit acerhdf_exit(void) |
@@ -662,11 +707,13 @@ MODULE_DESCRIPTION("Aspire One temperature and fan driver"); | |||
662 | MODULE_ALIAS("dmi:*:*Acer*:pnAOA*:"); | 707 | MODULE_ALIAS("dmi:*:*Acer*:pnAOA*:"); |
663 | MODULE_ALIAS("dmi:*:*Acer*:pnAspire 1410*:"); | 708 | MODULE_ALIAS("dmi:*:*Acer*:pnAspire 1410*:"); |
664 | MODULE_ALIAS("dmi:*:*Acer*:pnAspire 1810*:"); | 709 | MODULE_ALIAS("dmi:*:*Acer*:pnAspire 1810*:"); |
710 | MODULE_ALIAS("dmi:*:*Acer*:pnAO531*:"); | ||
665 | MODULE_ALIAS("dmi:*:*Gateway*:pnAOA*:"); | 711 | MODULE_ALIAS("dmi:*:*Gateway*:pnAOA*:"); |
666 | MODULE_ALIAS("dmi:*:*Gateway*:pnLT31*:"); | 712 | MODULE_ALIAS("dmi:*:*Gateway*:pnLT31*:"); |
667 | MODULE_ALIAS("dmi:*:*Packard Bell*:pnAOA*:"); | 713 | MODULE_ALIAS("dmi:*:*Packard Bell*:pnAOA*:"); |
668 | MODULE_ALIAS("dmi:*:*Packard Bell*:pnDOA*:"); | 714 | MODULE_ALIAS("dmi:*:*Packard Bell*:pnDOA*:"); |
669 | MODULE_ALIAS("dmi:*:*Packard Bell*:pnDOTMU*:"); | 715 | MODULE_ALIAS("dmi:*:*Packard Bell*:pnDOTMU*:"); |
716 | MODULE_ALIAS("dmi:*:*Packard Bell*:pnDOTMA*:"); | ||
670 | 717 | ||
671 | module_init(acerhdf_init); | 718 | module_init(acerhdf_init); |
672 | module_exit(acerhdf_exit); | 719 | module_exit(acerhdf_exit); |
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index efe8f6388906..b756e07d41b4 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c | |||
@@ -76,18 +76,18 @@ MODULE_LICENSE("GPL"); | |||
76 | * So, if something doesn't work as you want, just try other values =) | 76 | * So, if something doesn't work as you want, just try other values =) |
77 | */ | 77 | */ |
78 | static uint wapf = 1; | 78 | static uint wapf = 1; |
79 | module_param(wapf, uint, 0644); | 79 | module_param(wapf, uint, 0444); |
80 | MODULE_PARM_DESC(wapf, "WAPF value"); | 80 | MODULE_PARM_DESC(wapf, "WAPF value"); |
81 | 81 | ||
82 | static int wlan_status = 1; | 82 | static int wlan_status = 1; |
83 | static int bluetooth_status = 1; | 83 | static int bluetooth_status = 1; |
84 | 84 | ||
85 | module_param(wlan_status, int, 0644); | 85 | module_param(wlan_status, int, 0444); |
86 | MODULE_PARM_DESC(wlan_status, "Set the wireless status on boot " | 86 | MODULE_PARM_DESC(wlan_status, "Set the wireless status on boot " |
87 | "(0 = disabled, 1 = enabled, -1 = don't do anything). " | 87 | "(0 = disabled, 1 = enabled, -1 = don't do anything). " |
88 | "default is 1"); | 88 | "default is 1"); |
89 | 89 | ||
90 | module_param(bluetooth_status, int, 0644); | 90 | module_param(bluetooth_status, int, 0444); |
91 | MODULE_PARM_DESC(bluetooth_status, "Set the wireless status on boot " | 91 | MODULE_PARM_DESC(bluetooth_status, "Set the wireless status on boot " |
92 | "(0 = disabled, 1 = enabled, -1 = don't do anything). " | 92 | "(0 = disabled, 1 = enabled, -1 = don't do anything). " |
93 | "default is 1"); | 93 | "default is 1"); |
@@ -297,7 +297,7 @@ static int write_acpi_int_ret(acpi_handle handle, const char *method, int val, | |||
297 | acpi_status status; | 297 | acpi_status status; |
298 | 298 | ||
299 | if (!handle) | 299 | if (!handle) |
300 | return 0; | 300 | return -1; |
301 | 301 | ||
302 | params.count = 1; | 302 | params.count = 1; |
303 | params.pointer = &in_obj; | 303 | params.pointer = &in_obj; |
@@ -796,10 +796,11 @@ static ssize_t store_ledd(struct device *dev, struct device_attribute *attr, | |||
796 | 796 | ||
797 | rv = parse_arg(buf, count, &value); | 797 | rv = parse_arg(buf, count, &value); |
798 | if (rv > 0) { | 798 | if (rv > 0) { |
799 | if (write_acpi_int(asus->handle, METHOD_LEDD, value)) | 799 | if (write_acpi_int(asus->handle, METHOD_LEDD, value)) { |
800 | pr_warning("LED display write failed\n"); | 800 | pr_warning("LED display write failed\n"); |
801 | else | 801 | return -ENODEV; |
802 | asus->ledd_status = (u32) value; | 802 | } |
803 | asus->ledd_status = (u32) value; | ||
803 | } | 804 | } |
804 | return rv; | 805 | return rv; |
805 | } | 806 | } |
@@ -1123,7 +1124,7 @@ static int asus_input_init(struct asus_laptop *asus) | |||
1123 | input = input_allocate_device(); | 1124 | input = input_allocate_device(); |
1124 | if (!input) { | 1125 | if (!input) { |
1125 | pr_info("Unable to allocate input device\n"); | 1126 | pr_info("Unable to allocate input device\n"); |
1126 | return 0; | 1127 | return -ENOMEM; |
1127 | } | 1128 | } |
1128 | input->name = "Asus Laptop extra buttons"; | 1129 | input->name = "Asus Laptop extra buttons"; |
1129 | input->phys = ASUS_LAPTOP_FILE "/input0"; | 1130 | input->phys = ASUS_LAPTOP_FILE "/input0"; |
@@ -1134,20 +1135,20 @@ static int asus_input_init(struct asus_laptop *asus) | |||
1134 | error = sparse_keymap_setup(input, asus_keymap, NULL); | 1135 | error = sparse_keymap_setup(input, asus_keymap, NULL); |
1135 | if (error) { | 1136 | if (error) { |
1136 | pr_err("Unable to setup input device keymap\n"); | 1137 | pr_err("Unable to setup input device keymap\n"); |
1137 | goto err_keymap; | 1138 | goto err_free_dev; |
1138 | } | 1139 | } |
1139 | error = input_register_device(input); | 1140 | error = input_register_device(input); |
1140 | if (error) { | 1141 | if (error) { |
1141 | pr_info("Unable to register input device\n"); | 1142 | pr_info("Unable to register input device\n"); |
1142 | goto err_device; | 1143 | goto err_free_keymap; |
1143 | } | 1144 | } |
1144 | 1145 | ||
1145 | asus->inputdev = input; | 1146 | asus->inputdev = input; |
1146 | return 0; | 1147 | return 0; |
1147 | 1148 | ||
1148 | err_keymap: | 1149 | err_free_keymap: |
1149 | sparse_keymap_free(input); | 1150 | sparse_keymap_free(input); |
1150 | err_device: | 1151 | err_free_dev: |
1151 | input_free_device(input); | 1152 | input_free_device(input); |
1152 | return error; | 1153 | return error; |
1153 | } | 1154 | } |
@@ -1397,8 +1398,10 @@ static int asus_laptop_get_info(struct asus_laptop *asus) | |||
1397 | } | 1398 | } |
1398 | } | 1399 | } |
1399 | asus->name = kstrdup(string, GFP_KERNEL); | 1400 | asus->name = kstrdup(string, GFP_KERNEL); |
1400 | if (!asus->name) | 1401 | if (!asus->name) { |
1402 | kfree(buffer.pointer); | ||
1401 | return -ENOMEM; | 1403 | return -ENOMEM; |
1404 | } | ||
1402 | 1405 | ||
1403 | if (*string) | 1406 | if (*string) |
1404 | pr_notice(" %s model detected\n", string); | 1407 | pr_notice(" %s model detected\n", string); |
diff --git a/drivers/platform/x86/asus_acpi.c b/drivers/platform/x86/asus_acpi.c index 92fd30c9379c..e058c2ba2a15 100644 --- a/drivers/platform/x86/asus_acpi.c +++ b/drivers/platform/x86/asus_acpi.c | |||
@@ -1330,6 +1330,9 @@ static int asus_hotk_get_info(void) | |||
1330 | hotk->model = P30; | 1330 | hotk->model = P30; |
1331 | printk(KERN_NOTICE | 1331 | printk(KERN_NOTICE |
1332 | " Samsung P30 detected, supported\n"); | 1332 | " Samsung P30 detected, supported\n"); |
1333 | hotk->methods = &model_conf[hotk->model]; | ||
1334 | kfree(model); | ||
1335 | return 0; | ||
1333 | } else { | 1336 | } else { |
1334 | hotk->model = M2E; | 1337 | hotk->model = M2E; |
1335 | printk(KERN_NOTICE " unsupported model %s, trying " | 1338 | printk(KERN_NOTICE " unsupported model %s, trying " |
@@ -1339,8 +1342,6 @@ static int asus_hotk_get_info(void) | |||
1339 | kfree(model); | 1342 | kfree(model); |
1340 | return -ENODEV; | 1343 | return -ENODEV; |
1341 | } | 1344 | } |
1342 | hotk->methods = &model_conf[hotk->model]; | ||
1343 | return AE_OK; | ||
1344 | } | 1345 | } |
1345 | hotk->methods = &model_conf[hotk->model]; | 1346 | hotk->methods = &model_conf[hotk->model]; |
1346 | printk(KERN_NOTICE " %s model detected, supported\n", string); | 1347 | printk(KERN_NOTICE " %s model detected, supported\n", string); |
@@ -1374,7 +1375,7 @@ static int asus_hotk_get_info(void) | |||
1374 | 1375 | ||
1375 | kfree(model); | 1376 | kfree(model); |
1376 | 1377 | ||
1377 | return AE_OK; | 1378 | return 0; |
1378 | } | 1379 | } |
1379 | 1380 | ||
1380 | static int asus_hotk_check(void) | 1381 | static int asus_hotk_check(void) |
diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 3bf399fe2bbc..341cbfef93ee 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c | |||
@@ -208,7 +208,7 @@ static ssize_t cmpc_accel_sensitivity_store(struct device *dev, | |||
208 | return strnlen(buf, count); | 208 | return strnlen(buf, count); |
209 | } | 209 | } |
210 | 210 | ||
211 | struct device_attribute cmpc_accel_sensitivity_attr = { | 211 | static struct device_attribute cmpc_accel_sensitivity_attr = { |
212 | .attr = { .name = "sensitivity", .mode = 0660 }, | 212 | .attr = { .name = "sensitivity", .mode = 0660 }, |
213 | .show = cmpc_accel_sensitivity_show, | 213 | .show = cmpc_accel_sensitivity_show, |
214 | .store = cmpc_accel_sensitivity_store | 214 | .store = cmpc_accel_sensitivity_store |
@@ -573,16 +573,17 @@ static int cmpc_ipml_add(struct acpi_device *acpi) | |||
573 | 573 | ||
574 | ipml->rf = rfkill_alloc("cmpc_rfkill", &acpi->dev, RFKILL_TYPE_WLAN, | 574 | ipml->rf = rfkill_alloc("cmpc_rfkill", &acpi->dev, RFKILL_TYPE_WLAN, |
575 | &cmpc_rfkill_ops, acpi->handle); | 575 | &cmpc_rfkill_ops, acpi->handle); |
576 | /* rfkill_alloc may fail if RFKILL is disabled. We should still work | 576 | /* |
577 | * anyway. */ | 577 | * If RFKILL is disabled, rfkill_alloc will return ERR_PTR(-ENODEV). |
578 | if (!IS_ERR(ipml->rf)) { | 578 | * This is OK, however, since all other uses of the device will not |
579 | * derefence it. | ||
580 | */ | ||
581 | if (ipml->rf) { | ||
579 | retval = rfkill_register(ipml->rf); | 582 | retval = rfkill_register(ipml->rf); |
580 | if (retval) { | 583 | if (retval) { |
581 | rfkill_destroy(ipml->rf); | 584 | rfkill_destroy(ipml->rf); |
582 | ipml->rf = NULL; | 585 | ipml->rf = NULL; |
583 | } | 586 | } |
584 | } else { | ||
585 | ipml->rf = NULL; | ||
586 | } | 587 | } |
587 | 588 | ||
588 | dev_set_drvdata(&acpi->dev, ipml); | 589 | dev_set_drvdata(&acpi->dev, ipml); |
diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index 71ff1545a93e..d071ce056322 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c | |||
@@ -24,17 +24,50 @@ | |||
24 | */ | 24 | */ |
25 | 25 | ||
26 | /* | 26 | /* |
27 | * comapl-laptop.c - Compal laptop support. | 27 | * compal-laptop.c - Compal laptop support. |
28 | * | ||
29 | * This driver exports a few files in /sys/devices/platform/compal-laptop/: | ||
30 | * wake_up_XXX Whether or not we listen to such wake up events (rw) | ||
31 | * | ||
32 | * In addition to these platform device attributes the driver | ||
33 | * registers itself in the Linux backlight control, power_supply, rfkill | ||
34 | * and hwmon subsystem and is available to userspace under: | ||
35 | * | ||
36 | * /sys/class/backlight/compal-laptop/ | ||
37 | * /sys/class/power_supply/compal-laptop/ | ||
38 | * /sys/class/rfkill/rfkillX/ | ||
39 | * /sys/class/hwmon/hwmonX/ | ||
40 | * | ||
41 | * Notes on the power_supply battery interface: | ||
42 | * - the "minimum" design voltage is *the* design voltage | ||
43 | * - the ambient temperature is the average battery temperature | ||
44 | * and the value is an educated guess (see commented code below) | ||
28 | * | 45 | * |
29 | * The driver registers itself with the rfkill subsystem and | ||
30 | * the Linux backlight control subsystem. | ||
31 | * | 46 | * |
32 | * This driver might work on other laptops produced by Compal. If you | 47 | * This driver might work on other laptops produced by Compal. If you |
33 | * want to try it you can pass force=1 as argument to the module which | 48 | * want to try it you can pass force=1 as argument to the module which |
34 | * will force it to load even when the DMI data doesn't identify the | 49 | * will force it to load even when the DMI data doesn't identify the |
35 | * laptop as FL9x. | 50 | * laptop as compatible. |
51 | * | ||
52 | * Lots of data available at: | ||
53 | * http://service1.marasst.com/Compal/JHL90_91/Service%20Manual/ | ||
54 | * JHL90%20service%20manual-Final-0725.pdf | ||
55 | * | ||
56 | * | ||
57 | * | ||
58 | * Support for the Compal JHL90 added by Roald Frederickx | ||
59 | * (roald.frederickx@gmail.com): | ||
60 | * Driver got large revision. Added functionalities: backlight | ||
61 | * power, wake_on_XXX, a hwmon and power_supply interface. | ||
62 | * | ||
63 | * In case this gets merged into the kernel source: I want to dedicate this | ||
64 | * to Kasper Meerts, the awesome guy who showed me Linux and C! | ||
36 | */ | 65 | */ |
37 | 66 | ||
67 | /* NOTE: currently the wake_on_XXX, hwmon and power_supply interfaces are | ||
68 | * only enabled on a JHL90 board until it is verified that they work on the | ||
69 | * other boards too. See the extra_features variable. */ | ||
70 | |||
38 | #include <linux/module.h> | 71 | #include <linux/module.h> |
39 | #include <linux/kernel.h> | 72 | #include <linux/kernel.h> |
40 | #include <linux/init.h> | 73 | #include <linux/init.h> |
@@ -43,71 +76,296 @@ | |||
43 | #include <linux/backlight.h> | 76 | #include <linux/backlight.h> |
44 | #include <linux/platform_device.h> | 77 | #include <linux/platform_device.h> |
45 | #include <linux/rfkill.h> | 78 | #include <linux/rfkill.h> |
79 | #include <linux/hwmon.h> | ||
80 | #include <linux/hwmon-sysfs.h> | ||
81 | #include <linux/power_supply.h> | ||
82 | #include <linux/fb.h> | ||
83 | |||
84 | |||
85 | /* ======= */ | ||
86 | /* Defines */ | ||
87 | /* ======= */ | ||
88 | #define DRIVER_NAME "compal-laptop" | ||
89 | #define DRIVER_VERSION "0.2.7" | ||
90 | |||
91 | #define BACKLIGHT_LEVEL_ADDR 0xB9 | ||
92 | #define BACKLIGHT_LEVEL_MAX 7 | ||
93 | #define BACKLIGHT_STATE_ADDR 0x59 | ||
94 | #define BACKLIGHT_STATE_ON_DATA 0xE1 | ||
95 | #define BACKLIGHT_STATE_OFF_DATA 0xE2 | ||
96 | |||
97 | #define WAKE_UP_ADDR 0xA4 | ||
98 | #define WAKE_UP_PME (1 << 0) | ||
99 | #define WAKE_UP_MODEM (1 << 1) | ||
100 | #define WAKE_UP_LAN (1 << 2) | ||
101 | #define WAKE_UP_WLAN (1 << 4) | ||
102 | #define WAKE_UP_KEY (1 << 6) | ||
103 | #define WAKE_UP_MOUSE (1 << 7) | ||
104 | |||
105 | #define WIRELESS_ADDR 0xBB | ||
106 | #define WIRELESS_WLAN (1 << 0) | ||
107 | #define WIRELESS_BT (1 << 1) | ||
108 | #define WIRELESS_WLAN_EXISTS (1 << 2) | ||
109 | #define WIRELESS_BT_EXISTS (1 << 3) | ||
110 | #define WIRELESS_KILLSWITCH (1 << 4) | ||
111 | |||
112 | #define PWM_ADDRESS 0x46 | ||
113 | #define PWM_DISABLE_ADDR 0x59 | ||
114 | #define PWM_DISABLE_DATA 0xA5 | ||
115 | #define PWM_ENABLE_ADDR 0x59 | ||
116 | #define PWM_ENABLE_DATA 0xA8 | ||
117 | |||
118 | #define FAN_ADDRESS 0x46 | ||
119 | #define FAN_DATA 0x81 | ||
120 | #define FAN_FULL_ON_CMD 0x59 /* Doesn't seem to work. Just */ | ||
121 | #define FAN_FULL_ON_ENABLE 0x76 /* force the pwm signal to its */ | ||
122 | #define FAN_FULL_ON_DISABLE 0x77 /* maximum value instead */ | ||
123 | |||
124 | #define TEMP_CPU 0xB0 | ||
125 | #define TEMP_CPU_LOCAL 0xB1 | ||
126 | #define TEMP_CPU_DTS 0xB5 | ||
127 | #define TEMP_NORTHBRIDGE 0xB6 | ||
128 | #define TEMP_VGA 0xB4 | ||
129 | #define TEMP_SKIN 0xB2 | ||
130 | |||
131 | #define BAT_MANUFACTURER_NAME_ADDR 0x10 | ||
132 | #define BAT_MANUFACTURER_NAME_LEN 9 | ||
133 | #define BAT_MODEL_NAME_ADDR 0x19 | ||
134 | #define BAT_MODEL_NAME_LEN 6 | ||
135 | #define BAT_SERIAL_NUMBER_ADDR 0xC4 | ||
136 | #define BAT_SERIAL_NUMBER_LEN 5 | ||
137 | #define BAT_CHARGE_NOW 0xC2 | ||
138 | #define BAT_CHARGE_DESIGN 0xCA | ||
139 | #define BAT_VOLTAGE_NOW 0xC6 | ||
140 | #define BAT_VOLTAGE_DESIGN 0xC8 | ||
141 | #define BAT_CURRENT_NOW 0xD0 | ||
142 | #define BAT_CURRENT_AVG 0xD2 | ||
143 | #define BAT_POWER 0xD4 | ||
144 | #define BAT_CAPACITY 0xCE | ||
145 | #define BAT_TEMP 0xD6 | ||
146 | #define BAT_TEMP_AVG 0xD7 | ||
147 | #define BAT_STATUS0 0xC1 | ||
148 | #define BAT_STATUS1 0xF0 | ||
149 | #define BAT_STATUS2 0xF1 | ||
150 | #define BAT_STOP_CHARGE1 0xF2 | ||
151 | #define BAT_STOP_CHARGE2 0xF3 | ||
152 | |||
153 | #define BAT_S0_DISCHARGE (1 << 0) | ||
154 | #define BAT_S0_DISCHRG_CRITICAL (1 << 2) | ||
155 | #define BAT_S0_LOW (1 << 3) | ||
156 | #define BAT_S0_CHARGING (1 << 1) | ||
157 | #define BAT_S0_AC (1 << 7) | ||
158 | #define BAT_S1_EXISTS (1 << 0) | ||
159 | #define BAT_S1_FULL (1 << 1) | ||
160 | #define BAT_S1_EMPTY (1 << 2) | ||
161 | #define BAT_S1_LiION_OR_NiMH (1 << 7) | ||
162 | #define BAT_S2_LOW_LOW (1 << 0) | ||
163 | #define BAT_STOP_CHRG1_BAD_CELL (1 << 1) | ||
164 | #define BAT_STOP_CHRG1_COMM_FAIL (1 << 2) | ||
165 | #define BAT_STOP_CHRG1_OVERVOLTAGE (1 << 6) | ||
166 | #define BAT_STOP_CHRG1_OVERTEMPERATURE (1 << 7) | ||
167 | |||
168 | |||
169 | /* ======= */ | ||
170 | /* Structs */ | ||
171 | /* ======= */ | ||
172 | struct compal_data{ | ||
173 | /* Fan control */ | ||
174 | struct device *hwmon_dev; | ||
175 | int pwm_enable; /* 0:full on, 1:set by pwm1, 2:control by moterboard */ | ||
176 | unsigned char curr_pwm; | ||
177 | |||
178 | /* Power supply */ | ||
179 | struct power_supply psy; | ||
180 | struct power_supply_info psy_info; | ||
181 | char bat_model_name[BAT_MODEL_NAME_LEN + 1]; | ||
182 | char bat_manufacturer_name[BAT_MANUFACTURER_NAME_LEN + 1]; | ||
183 | char bat_serial_number[BAT_SERIAL_NUMBER_LEN + 1]; | ||
184 | }; | ||
46 | 185 | ||
47 | #define COMPAL_DRIVER_VERSION "0.2.6" | ||
48 | 186 | ||
49 | #define COMPAL_LCD_LEVEL_MAX 8 | 187 | /* =============== */ |
188 | /* General globals */ | ||
189 | /* =============== */ | ||
190 | static int force; | ||
191 | module_param(force, bool, 0); | ||
192 | MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); | ||
50 | 193 | ||
51 | #define COMPAL_EC_COMMAND_WIRELESS 0xBB | 194 | /* Support for the wake_on_XXX, hwmon and power_supply interface. Currently |
52 | #define COMPAL_EC_COMMAND_LCD_LEVEL 0xB9 | 195 | * only gets enabled on a JHL90 board. Might work with the others too */ |
196 | static bool extra_features; | ||
197 | |||
198 | /* Nasty stuff. For some reason the fan control is very un-linear. I've | ||
199 | * come up with these values by looping through the possible inputs and | ||
200 | * watching the output of address 0x4F (do an ec_transaction writing 0x33 | ||
201 | * into 0x4F and read a few bytes from the output, like so: | ||
202 | * u8 writeData = 0x33; | ||
203 | * ec_transaction(0x4F, &writeData, 1, buffer, 32, 0); | ||
204 | * That address is labled "fan1 table information" in the service manual. | ||
205 | * It should be clear which value in 'buffer' changes). This seems to be | ||
206 | * related to fan speed. It isn't a proper 'realtime' fan speed value | ||
207 | * though, because physically stopping or speeding up the fan doesn't | ||
208 | * change it. It might be the average voltage or current of the pwm output. | ||
209 | * Nevertheless, it is more fine-grained than the actual RPM reading */ | ||
210 | static const unsigned char pwm_lookup_table[256] = { | ||
211 | 0, 0, 0, 1, 1, 1, 2, 253, 254, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, | ||
212 | 7, 7, 7, 8, 86, 86, 9, 9, 9, 10, 10, 10, 11, 92, 92, 12, 12, 95, | ||
213 | 13, 66, 66, 14, 14, 98, 15, 15, 15, 16, 16, 67, 17, 17, 72, 18, 70, | ||
214 | 75, 19, 90, 90, 73, 73, 73, 21, 21, 91, 91, 91, 96, 23, 94, 94, 94, | ||
215 | 94, 94, 94, 94, 94, 94, 94, 141, 141, 238, 223, 192, 139, 139, 139, | ||
216 | 139, 139, 142, 142, 142, 142, 142, 78, 78, 78, 78, 78, 76, 76, 76, | ||
217 | 76, 76, 79, 79, 79, 79, 79, 79, 79, 20, 20, 20, 20, 20, 22, 22, 22, | ||
218 | 22, 22, 24, 24, 24, 24, 24, 24, 219, 219, 219, 219, 219, 219, 219, | ||
219 | 219, 27, 27, 188, 188, 28, 28, 28, 29, 186, 186, 186, 186, 186, | ||
220 | 186, 186, 186, 186, 186, 31, 31, 31, 31, 31, 32, 32, 32, 41, 33, | ||
221 | 33, 33, 33, 33, 252, 252, 34, 34, 34, 43, 35, 35, 35, 36, 36, 38, | ||
222 | 206, 206, 206, 206, 206, 206, 206, 206, 206, 37, 37, 37, 46, 46, | ||
223 | 47, 47, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 48, 48, | ||
224 | 48, 48, 48, 40, 40, 40, 49, 42, 42, 42, 42, 42, 42, 42, 42, 44, | ||
225 | 189, 189, 189, 189, 54, 54, 45, 45, 45, 45, 45, 45, 45, 45, 251, | ||
226 | 191, 199, 199, 199, 199, 199, 215, 215, 215, 215, 187, 187, 187, | ||
227 | 187, 187, 193, 50 | ||
228 | }; | ||
53 | 229 | ||
54 | #define KILLSWITCH_MASK 0x10 | ||
55 | #define WLAN_MASK 0x01 | ||
56 | #define BT_MASK 0x02 | ||
57 | 230 | ||
58 | static struct rfkill *wifi_rfkill; | ||
59 | static struct rfkill *bt_rfkill; | ||
60 | static struct platform_device *compal_device; | ||
61 | 231 | ||
62 | static int force; | ||
63 | module_param(force, bool, 0); | ||
64 | MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); | ||
65 | 232 | ||
66 | /* Hardware access */ | 233 | /* ========================= */ |
234 | /* Hardware access functions */ | ||
235 | /* ========================= */ | ||
236 | /* General access */ | ||
237 | static u8 ec_read_u8(u8 addr) | ||
238 | { | ||
239 | u8 value; | ||
240 | ec_read(addr, &value); | ||
241 | return value; | ||
242 | } | ||
243 | |||
244 | static s8 ec_read_s8(u8 addr) | ||
245 | { | ||
246 | return (s8)ec_read_u8(addr); | ||
247 | } | ||
248 | |||
249 | static u16 ec_read_u16(u8 addr) | ||
250 | { | ||
251 | int hi, lo; | ||
252 | lo = ec_read_u8(addr); | ||
253 | hi = ec_read_u8(addr + 1); | ||
254 | return (hi << 8) + lo; | ||
255 | } | ||
256 | |||
257 | static s16 ec_read_s16(u8 addr) | ||
258 | { | ||
259 | return (s16) ec_read_u16(addr); | ||
260 | } | ||
67 | 261 | ||
68 | static int set_lcd_level(int level) | 262 | static void ec_read_sequence(u8 addr, u8 *buf, int len) |
69 | { | 263 | { |
70 | if (level < 0 || level >= COMPAL_LCD_LEVEL_MAX) | 264 | int i; |
265 | for (i = 0; i < len; i++) | ||
266 | ec_read(addr + i, buf + i); | ||
267 | } | ||
268 | |||
269 | |||
270 | /* Backlight access */ | ||
271 | static int set_backlight_level(int level) | ||
272 | { | ||
273 | if (level < 0 || level > BACKLIGHT_LEVEL_MAX) | ||
71 | return -EINVAL; | 274 | return -EINVAL; |
72 | 275 | ||
73 | ec_write(COMPAL_EC_COMMAND_LCD_LEVEL, level); | 276 | ec_write(BACKLIGHT_LEVEL_ADDR, level); |
74 | 277 | ||
75 | return 0; | 278 | return 1; |
279 | } | ||
280 | |||
281 | static int get_backlight_level(void) | ||
282 | { | ||
283 | return (int) ec_read_u8(BACKLIGHT_LEVEL_ADDR); | ||
76 | } | 284 | } |
77 | 285 | ||
78 | static int get_lcd_level(void) | 286 | static void set_backlight_state(bool on) |
79 | { | 287 | { |
80 | u8 result; | 288 | u8 data = on ? BACKLIGHT_STATE_ON_DATA : BACKLIGHT_STATE_OFF_DATA; |
289 | ec_transaction(BACKLIGHT_STATE_ADDR, &data, 1, NULL, 0, 0); | ||
290 | } | ||
291 | |||
292 | |||
293 | /* Fan control access */ | ||
294 | static void pwm_enable_control(void) | ||
295 | { | ||
296 | unsigned char writeData = PWM_ENABLE_DATA; | ||
297 | ec_transaction(PWM_ENABLE_ADDR, &writeData, 1, NULL, 0, 0); | ||
298 | } | ||
299 | |||
300 | static void pwm_disable_control(void) | ||
301 | { | ||
302 | unsigned char writeData = PWM_DISABLE_DATA; | ||
303 | ec_transaction(PWM_DISABLE_ADDR, &writeData, 1, NULL, 0, 0); | ||
304 | } | ||
81 | 305 | ||
82 | ec_read(COMPAL_EC_COMMAND_LCD_LEVEL, &result); | 306 | static void set_pwm(int pwm) |
307 | { | ||
308 | ec_transaction(PWM_ADDRESS, &pwm_lookup_table[pwm], 1, NULL, 0, 0); | ||
309 | } | ||
310 | |||
311 | static int get_fan_rpm(void) | ||
312 | { | ||
313 | u8 value, data = FAN_DATA; | ||
314 | ec_transaction(FAN_ADDRESS, &data, 1, &value, 1, 0); | ||
315 | return 100 * (int)value; | ||
316 | } | ||
317 | |||
318 | |||
319 | |||
320 | |||
321 | /* =================== */ | ||
322 | /* Interface functions */ | ||
323 | /* =================== */ | ||
324 | |||
325 | /* Backlight interface */ | ||
326 | static int bl_get_brightness(struct backlight_device *b) | ||
327 | { | ||
328 | return get_backlight_level(); | ||
329 | } | ||
330 | |||
331 | static int bl_update_status(struct backlight_device *b) | ||
332 | { | ||
333 | int ret = set_backlight_level(b->props.brightness); | ||
334 | if (ret) | ||
335 | return ret; | ||
83 | 336 | ||
84 | return (int) result; | 337 | set_backlight_state((b->props.power == FB_BLANK_UNBLANK) |
338 | && !(b->props.state & BL_CORE_SUSPENDED) | ||
339 | && !(b->props.state & BL_CORE_FBBLANK)); | ||
340 | return 0; | ||
85 | } | 341 | } |
86 | 342 | ||
343 | static const struct backlight_ops compalbl_ops = { | ||
344 | .get_brightness = bl_get_brightness, | ||
345 | .update_status = bl_update_status, | ||
346 | }; | ||
347 | |||
348 | |||
349 | /* Wireless interface */ | ||
87 | static int compal_rfkill_set(void *data, bool blocked) | 350 | static int compal_rfkill_set(void *data, bool blocked) |
88 | { | 351 | { |
89 | unsigned long radio = (unsigned long) data; | 352 | unsigned long radio = (unsigned long) data; |
90 | u8 result, value; | 353 | u8 result = ec_read_u8(WIRELESS_ADDR); |
91 | 354 | u8 value; | |
92 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); | ||
93 | 355 | ||
94 | if (!blocked) | 356 | if (!blocked) |
95 | value = (u8) (result | radio); | 357 | value = (u8) (result | radio); |
96 | else | 358 | else |
97 | value = (u8) (result & ~radio); | 359 | value = (u8) (result & ~radio); |
98 | ec_write(COMPAL_EC_COMMAND_WIRELESS, value); | 360 | ec_write(WIRELESS_ADDR, value); |
99 | 361 | ||
100 | return 0; | 362 | return 0; |
101 | } | 363 | } |
102 | 364 | ||
103 | static void compal_rfkill_poll(struct rfkill *rfkill, void *data) | 365 | static void compal_rfkill_poll(struct rfkill *rfkill, void *data) |
104 | { | 366 | { |
105 | u8 result; | 367 | u8 result = ec_read_u8(WIRELESS_ADDR); |
106 | bool hw_blocked; | 368 | bool hw_blocked = !(result & WIRELESS_KILLSWITCH); |
107 | |||
108 | ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); | ||
109 | |||
110 | hw_blocked = !(result & KILLSWITCH_MASK); | ||
111 | rfkill_set_hw_state(rfkill, hw_blocked); | 369 | rfkill_set_hw_state(rfkill, hw_blocked); |
112 | } | 370 | } |
113 | 371 | ||
@@ -116,80 +374,404 @@ static const struct rfkill_ops compal_rfkill_ops = { | |||
116 | .set_block = compal_rfkill_set, | 374 | .set_block = compal_rfkill_set, |
117 | }; | 375 | }; |
118 | 376 | ||
119 | static int setup_rfkill(void) | 377 | |
378 | /* Wake_up interface */ | ||
379 | #define SIMPLE_MASKED_STORE_SHOW(NAME, ADDR, MASK) \ | ||
380 | static ssize_t NAME##_show(struct device *dev, \ | ||
381 | struct device_attribute *attr, char *buf) \ | ||
382 | { \ | ||
383 | return sprintf(buf, "%d\n", ((ec_read_u8(ADDR) & MASK) != 0)); \ | ||
384 | } \ | ||
385 | static ssize_t NAME##_store(struct device *dev, \ | ||
386 | struct device_attribute *attr, const char *buf, size_t count) \ | ||
387 | { \ | ||
388 | int state; \ | ||
389 | u8 old_val = ec_read_u8(ADDR); \ | ||
390 | if (sscanf(buf, "%d", &state) != 1 || (state < 0 || state > 1)) \ | ||
391 | return -EINVAL; \ | ||
392 | ec_write(ADDR, state ? (old_val | MASK) : (old_val & ~MASK)); \ | ||
393 | return count; \ | ||
394 | } | ||
395 | |||
396 | SIMPLE_MASKED_STORE_SHOW(wake_up_pme, WAKE_UP_ADDR, WAKE_UP_PME) | ||
397 | SIMPLE_MASKED_STORE_SHOW(wake_up_modem, WAKE_UP_ADDR, WAKE_UP_MODEM) | ||
398 | SIMPLE_MASKED_STORE_SHOW(wake_up_lan, WAKE_UP_ADDR, WAKE_UP_LAN) | ||
399 | SIMPLE_MASKED_STORE_SHOW(wake_up_wlan, WAKE_UP_ADDR, WAKE_UP_WLAN) | ||
400 | SIMPLE_MASKED_STORE_SHOW(wake_up_key, WAKE_UP_ADDR, WAKE_UP_KEY) | ||
401 | SIMPLE_MASKED_STORE_SHOW(wake_up_mouse, WAKE_UP_ADDR, WAKE_UP_MOUSE) | ||
402 | |||
403 | |||
404 | /* General hwmon interface */ | ||
405 | static ssize_t hwmon_name_show(struct device *dev, | ||
406 | struct device_attribute *attr, char *buf) | ||
120 | { | 407 | { |
121 | int ret; | 408 | return sprintf(buf, "%s\n", DRIVER_NAME); |
409 | } | ||
122 | 410 | ||
123 | wifi_rfkill = rfkill_alloc("compal-wifi", &compal_device->dev, | ||
124 | RFKILL_TYPE_WLAN, &compal_rfkill_ops, | ||
125 | (void *) WLAN_MASK); | ||
126 | if (!wifi_rfkill) | ||
127 | return -ENOMEM; | ||
128 | 411 | ||
129 | ret = rfkill_register(wifi_rfkill); | 412 | /* Fan control interface */ |
130 | if (ret) | 413 | static ssize_t pwm_enable_show(struct device *dev, |
131 | goto err_wifi; | 414 | struct device_attribute *attr, char *buf) |
415 | { | ||
416 | struct compal_data *data = dev_get_drvdata(dev); | ||
417 | return sprintf(buf, "%d\n", data->pwm_enable); | ||
418 | } | ||
132 | 419 | ||
133 | bt_rfkill = rfkill_alloc("compal-bluetooth", &compal_device->dev, | 420 | static ssize_t pwm_enable_store(struct device *dev, |
134 | RFKILL_TYPE_BLUETOOTH, &compal_rfkill_ops, | 421 | struct device_attribute *attr, const char *buf, size_t count) |
135 | (void *) BT_MASK); | 422 | { |
136 | if (!bt_rfkill) { | 423 | struct compal_data *data = dev_get_drvdata(dev); |
137 | ret = -ENOMEM; | 424 | long val; |
138 | goto err_allocate_bt; | 425 | int err; |
426 | err = strict_strtol(buf, 10, &val); | ||
427 | if (err) | ||
428 | return err; | ||
429 | if (val < 0) | ||
430 | return -EINVAL; | ||
431 | |||
432 | data->pwm_enable = val; | ||
433 | |||
434 | switch (val) { | ||
435 | case 0: /* Full speed */ | ||
436 | pwm_enable_control(); | ||
437 | set_pwm(255); | ||
438 | break; | ||
439 | case 1: /* As set by pwm1 */ | ||
440 | pwm_enable_control(); | ||
441 | set_pwm(data->curr_pwm); | ||
442 | break; | ||
443 | default: /* Control by motherboard */ | ||
444 | pwm_disable_control(); | ||
445 | break; | ||
139 | } | 446 | } |
140 | ret = rfkill_register(bt_rfkill); | ||
141 | if (ret) | ||
142 | goto err_register_bt; | ||
143 | 447 | ||
144 | return 0; | 448 | return count; |
449 | } | ||
145 | 450 | ||
146 | err_register_bt: | 451 | static ssize_t pwm_show(struct device *dev, struct device_attribute *attr, |
147 | rfkill_destroy(bt_rfkill); | 452 | char *buf) |
453 | { | ||
454 | struct compal_data *data = dev_get_drvdata(dev); | ||
455 | return sprintf(buf, "%hhu\n", data->curr_pwm); | ||
456 | } | ||
148 | 457 | ||
149 | err_allocate_bt: | 458 | static ssize_t pwm_store(struct device *dev, struct device_attribute *attr, |
150 | rfkill_unregister(wifi_rfkill); | 459 | const char *buf, size_t count) |
460 | { | ||
461 | struct compal_data *data = dev_get_drvdata(dev); | ||
462 | long val; | ||
463 | int err; | ||
464 | err = strict_strtol(buf, 10, &val); | ||
465 | if (err) | ||
466 | return err; | ||
467 | if (val < 0 || val > 255) | ||
468 | return -EINVAL; | ||
151 | 469 | ||
152 | err_wifi: | 470 | data->curr_pwm = val; |
153 | rfkill_destroy(wifi_rfkill); | ||
154 | 471 | ||
155 | return ret; | 472 | if (data->pwm_enable != 1) |
473 | return count; | ||
474 | set_pwm(val); | ||
475 | |||
476 | return count; | ||
156 | } | 477 | } |
157 | 478 | ||
158 | /* Backlight device stuff */ | 479 | static ssize_t fan_show(struct device *dev, struct device_attribute *attr, |
480 | char *buf) | ||
481 | { | ||
482 | return sprintf(buf, "%d\n", get_fan_rpm()); | ||
483 | } | ||
159 | 484 | ||
160 | static int bl_get_brightness(struct backlight_device *b) | 485 | |
486 | /* Temperature interface */ | ||
487 | #define TEMPERATURE_SHOW_TEMP_AND_LABEL(POSTFIX, ADDRESS, LABEL) \ | ||
488 | static ssize_t temp_##POSTFIX(struct device *dev, \ | ||
489 | struct device_attribute *attr, char *buf) \ | ||
490 | { \ | ||
491 | return sprintf(buf, "%d\n", 1000 * (int)ec_read_s8(ADDRESS)); \ | ||
492 | } \ | ||
493 | static ssize_t label_##POSTFIX(struct device *dev, \ | ||
494 | struct device_attribute *attr, char *buf) \ | ||
495 | { \ | ||
496 | return sprintf(buf, "%s\n", LABEL); \ | ||
497 | } | ||
498 | |||
499 | /* Labels as in service guide */ | ||
500 | TEMPERATURE_SHOW_TEMP_AND_LABEL(cpu, TEMP_CPU, "CPU_TEMP"); | ||
501 | TEMPERATURE_SHOW_TEMP_AND_LABEL(cpu_local, TEMP_CPU_LOCAL, "CPU_TEMP_LOCAL"); | ||
502 | TEMPERATURE_SHOW_TEMP_AND_LABEL(cpu_DTS, TEMP_CPU_DTS, "CPU_DTS"); | ||
503 | TEMPERATURE_SHOW_TEMP_AND_LABEL(northbridge,TEMP_NORTHBRIDGE,"NorthBridge"); | ||
504 | TEMPERATURE_SHOW_TEMP_AND_LABEL(vga, TEMP_VGA, "VGA_TEMP"); | ||
505 | TEMPERATURE_SHOW_TEMP_AND_LABEL(SKIN, TEMP_SKIN, "SKIN_TEMP90"); | ||
506 | |||
507 | |||
508 | /* Power supply interface */ | ||
509 | static int bat_status(void) | ||
510 | { | ||
511 | u8 status0 = ec_read_u8(BAT_STATUS0); | ||
512 | u8 status1 = ec_read_u8(BAT_STATUS1); | ||
513 | |||
514 | if (status0 & BAT_S0_CHARGING) | ||
515 | return POWER_SUPPLY_STATUS_CHARGING; | ||
516 | if (status0 & BAT_S0_DISCHARGE) | ||
517 | return POWER_SUPPLY_STATUS_DISCHARGING; | ||
518 | if (status1 & BAT_S1_FULL) | ||
519 | return POWER_SUPPLY_STATUS_FULL; | ||
520 | return POWER_SUPPLY_STATUS_NOT_CHARGING; | ||
521 | } | ||
522 | |||
523 | static int bat_health(void) | ||
161 | { | 524 | { |
162 | return get_lcd_level(); | 525 | u8 status = ec_read_u8(BAT_STOP_CHARGE1); |
526 | |||
527 | if (status & BAT_STOP_CHRG1_OVERTEMPERATURE) | ||
528 | return POWER_SUPPLY_HEALTH_OVERHEAT; | ||
529 | if (status & BAT_STOP_CHRG1_OVERVOLTAGE) | ||
530 | return POWER_SUPPLY_HEALTH_OVERVOLTAGE; | ||
531 | if (status & BAT_STOP_CHRG1_BAD_CELL) | ||
532 | return POWER_SUPPLY_HEALTH_DEAD; | ||
533 | if (status & BAT_STOP_CHRG1_COMM_FAIL) | ||
534 | return POWER_SUPPLY_HEALTH_UNKNOWN; | ||
535 | return POWER_SUPPLY_HEALTH_GOOD; | ||
163 | } | 536 | } |
164 | 537 | ||
538 | static int bat_is_present(void) | ||
539 | { | ||
540 | u8 status = ec_read_u8(BAT_STATUS2); | ||
541 | return ((status & BAT_S1_EXISTS) != 0); | ||
542 | } | ||
165 | 543 | ||
166 | static int bl_update_status(struct backlight_device *b) | 544 | static int bat_technology(void) |
167 | { | 545 | { |
168 | return set_lcd_level(b->props.brightness); | 546 | u8 status = ec_read_u8(BAT_STATUS1); |
547 | |||
548 | if (status & BAT_S1_LiION_OR_NiMH) | ||
549 | return POWER_SUPPLY_TECHNOLOGY_LION; | ||
550 | return POWER_SUPPLY_TECHNOLOGY_NiMH; | ||
169 | } | 551 | } |
170 | 552 | ||
171 | static struct backlight_ops compalbl_ops = { | 553 | static int bat_capacity_level(void) |
172 | .get_brightness = bl_get_brightness, | 554 | { |
173 | .update_status = bl_update_status, | 555 | u8 status0 = ec_read_u8(BAT_STATUS0); |
174 | }; | 556 | u8 status1 = ec_read_u8(BAT_STATUS1); |
557 | u8 status2 = ec_read_u8(BAT_STATUS2); | ||
558 | |||
559 | if (status0 & BAT_S0_DISCHRG_CRITICAL | ||
560 | || status1 & BAT_S1_EMPTY | ||
561 | || status2 & BAT_S2_LOW_LOW) | ||
562 | return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; | ||
563 | if (status0 & BAT_S0_LOW) | ||
564 | return POWER_SUPPLY_CAPACITY_LEVEL_LOW; | ||
565 | if (status1 & BAT_S1_FULL) | ||
566 | return POWER_SUPPLY_CAPACITY_LEVEL_FULL; | ||
567 | return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; | ||
568 | } | ||
569 | |||
570 | static int bat_get_property(struct power_supply *psy, | ||
571 | enum power_supply_property psp, | ||
572 | union power_supply_propval *val) | ||
573 | { | ||
574 | struct compal_data *data; | ||
575 | data = container_of(psy, struct compal_data, psy); | ||
576 | |||
577 | switch (psp) { | ||
578 | case POWER_SUPPLY_PROP_STATUS: | ||
579 | val->intval = bat_status(); | ||
580 | break; | ||
581 | case POWER_SUPPLY_PROP_HEALTH: | ||
582 | val->intval = bat_health(); | ||
583 | break; | ||
584 | case POWER_SUPPLY_PROP_PRESENT: | ||
585 | val->intval = bat_is_present(); | ||
586 | break; | ||
587 | case POWER_SUPPLY_PROP_TECHNOLOGY: | ||
588 | val->intval = bat_technology(); | ||
589 | break; | ||
590 | case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: /* THE design voltage... */ | ||
591 | val->intval = ec_read_u16(BAT_VOLTAGE_DESIGN) * 1000; | ||
592 | break; | ||
593 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | ||
594 | val->intval = ec_read_u16(BAT_VOLTAGE_NOW) * 1000; | ||
595 | break; | ||
596 | case POWER_SUPPLY_PROP_CURRENT_NOW: | ||
597 | val->intval = ec_read_s16(BAT_CURRENT_NOW) * 1000; | ||
598 | break; | ||
599 | case POWER_SUPPLY_PROP_CURRENT_AVG: | ||
600 | val->intval = ec_read_s16(BAT_CURRENT_AVG) * 1000; | ||
601 | break; | ||
602 | case POWER_SUPPLY_PROP_POWER_NOW: | ||
603 | val->intval = ec_read_u8(BAT_POWER) * 1000000; | ||
604 | break; | ||
605 | case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: | ||
606 | val->intval = ec_read_u16(BAT_CHARGE_DESIGN) * 1000; | ||
607 | break; | ||
608 | case POWER_SUPPLY_PROP_CHARGE_NOW: | ||
609 | val->intval = ec_read_u16(BAT_CHARGE_NOW) * 1000; | ||
610 | break; | ||
611 | case POWER_SUPPLY_PROP_CAPACITY: | ||
612 | val->intval = ec_read_u8(BAT_CAPACITY); | ||
613 | break; | ||
614 | case POWER_SUPPLY_PROP_CAPACITY_LEVEL: | ||
615 | val->intval = bat_capacity_level(); | ||
616 | break; | ||
617 | /* It smees that BAT_TEMP_AVG is a (2's complement?) value showing | ||
618 | * the number of degrees, whereas BAT_TEMP is somewhat more | ||
619 | * complicated. It looks like this is a negative nember with a | ||
620 | * 100/256 divider and an offset of 222. Both were determined | ||
621 | * experimentally by comparing BAT_TEMP and BAT_TEMP_AVG. */ | ||
622 | case POWER_SUPPLY_PROP_TEMP: | ||
623 | val->intval = ((222 - (int)ec_read_u8(BAT_TEMP)) * 1000) >> 8; | ||
624 | break; | ||
625 | case POWER_SUPPLY_PROP_TEMP_AMBIENT: /* Ambient, Avg, ... same thing */ | ||
626 | val->intval = ec_read_s8(BAT_TEMP_AVG) * 10; | ||
627 | break; | ||
628 | /* Neither the model name nor manufacturer name work for me. */ | ||
629 | case POWER_SUPPLY_PROP_MODEL_NAME: | ||
630 | val->strval = data->bat_model_name; | ||
631 | break; | ||
632 | case POWER_SUPPLY_PROP_MANUFACTURER: | ||
633 | val->strval = data->bat_manufacturer_name; | ||
634 | break; | ||
635 | case POWER_SUPPLY_PROP_SERIAL_NUMBER: | ||
636 | val->strval = data->bat_serial_number; | ||
637 | break; | ||
638 | default: | ||
639 | break; | ||
640 | } | ||
641 | return 0; | ||
642 | } | ||
175 | 643 | ||
176 | static struct backlight_device *compalbl_device; | ||
177 | 644 | ||
178 | 645 | ||
646 | |||
647 | |||
648 | /* ============== */ | ||
649 | /* Driver Globals */ | ||
650 | /* ============== */ | ||
651 | static DEVICE_ATTR(wake_up_pme, | ||
652 | 0644, wake_up_pme_show, wake_up_pme_store); | ||
653 | static DEVICE_ATTR(wake_up_modem, | ||
654 | 0644, wake_up_modem_show, wake_up_modem_store); | ||
655 | static DEVICE_ATTR(wake_up_lan, | ||
656 | 0644, wake_up_lan_show, wake_up_lan_store); | ||
657 | static DEVICE_ATTR(wake_up_wlan, | ||
658 | 0644, wake_up_wlan_show, wake_up_wlan_store); | ||
659 | static DEVICE_ATTR(wake_up_key, | ||
660 | 0644, wake_up_key_show, wake_up_key_store); | ||
661 | static DEVICE_ATTR(wake_up_mouse, | ||
662 | 0644, wake_up_mouse_show, wake_up_mouse_store); | ||
663 | |||
664 | static SENSOR_DEVICE_ATTR(name, S_IRUGO, hwmon_name_show, NULL, 1); | ||
665 | static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, fan_show, NULL, 1); | ||
666 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, temp_cpu, NULL, 1); | ||
667 | static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, temp_cpu_local, NULL, 1); | ||
668 | static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, temp_cpu_DTS, NULL, 1); | ||
669 | static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, temp_northbridge, NULL, 1); | ||
670 | static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, temp_vga, NULL, 1); | ||
671 | static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, temp_SKIN, NULL, 1); | ||
672 | static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, label_cpu, NULL, 1); | ||
673 | static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, label_cpu_local, NULL, 1); | ||
674 | static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, label_cpu_DTS, NULL, 1); | ||
675 | static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, label_northbridge, NULL, 1); | ||
676 | static SENSOR_DEVICE_ATTR(temp5_label, S_IRUGO, label_vga, NULL, 1); | ||
677 | static SENSOR_DEVICE_ATTR(temp6_label, S_IRUGO, label_SKIN, NULL, 1); | ||
678 | static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, pwm_show, pwm_store, 1); | ||
679 | static SENSOR_DEVICE_ATTR(pwm1_enable, | ||
680 | S_IRUGO | S_IWUSR, pwm_enable_show, pwm_enable_store, 0); | ||
681 | |||
682 | static struct attribute *compal_attributes[] = { | ||
683 | &dev_attr_wake_up_pme.attr, | ||
684 | &dev_attr_wake_up_modem.attr, | ||
685 | &dev_attr_wake_up_lan.attr, | ||
686 | &dev_attr_wake_up_wlan.attr, | ||
687 | &dev_attr_wake_up_key.attr, | ||
688 | &dev_attr_wake_up_mouse.attr, | ||
689 | /* Maybe put the sensor-stuff in a separate hwmon-driver? That way, | ||
690 | * the hwmon sysfs won't be cluttered with the above files. */ | ||
691 | &sensor_dev_attr_name.dev_attr.attr, | ||
692 | &sensor_dev_attr_pwm1_enable.dev_attr.attr, | ||
693 | &sensor_dev_attr_pwm1.dev_attr.attr, | ||
694 | &sensor_dev_attr_fan1_input.dev_attr.attr, | ||
695 | &sensor_dev_attr_temp1_input.dev_attr.attr, | ||
696 | &sensor_dev_attr_temp2_input.dev_attr.attr, | ||
697 | &sensor_dev_attr_temp3_input.dev_attr.attr, | ||
698 | &sensor_dev_attr_temp4_input.dev_attr.attr, | ||
699 | &sensor_dev_attr_temp5_input.dev_attr.attr, | ||
700 | &sensor_dev_attr_temp6_input.dev_attr.attr, | ||
701 | &sensor_dev_attr_temp1_label.dev_attr.attr, | ||
702 | &sensor_dev_attr_temp2_label.dev_attr.attr, | ||
703 | &sensor_dev_attr_temp3_label.dev_attr.attr, | ||
704 | &sensor_dev_attr_temp4_label.dev_attr.attr, | ||
705 | &sensor_dev_attr_temp5_label.dev_attr.attr, | ||
706 | &sensor_dev_attr_temp6_label.dev_attr.attr, | ||
707 | NULL | ||
708 | }; | ||
709 | |||
710 | static struct attribute_group compal_attribute_group = { | ||
711 | .attrs = compal_attributes | ||
712 | }; | ||
713 | |||
714 | static int __devinit compal_probe(struct platform_device *); | ||
715 | static int __devexit compal_remove(struct platform_device *); | ||
179 | static struct platform_driver compal_driver = { | 716 | static struct platform_driver compal_driver = { |
180 | .driver = { | 717 | .driver = { |
181 | .name = "compal-laptop", | 718 | .name = DRIVER_NAME, |
182 | .owner = THIS_MODULE, | 719 | .owner = THIS_MODULE, |
183 | } | 720 | }, |
721 | .probe = compal_probe, | ||
722 | .remove = __devexit_p(compal_remove) | ||
184 | }; | 723 | }; |
185 | 724 | ||
186 | /* Initialization */ | 725 | static enum power_supply_property compal_bat_properties[] = { |
726 | POWER_SUPPLY_PROP_STATUS, | ||
727 | POWER_SUPPLY_PROP_HEALTH, | ||
728 | POWER_SUPPLY_PROP_PRESENT, | ||
729 | POWER_SUPPLY_PROP_TECHNOLOGY, | ||
730 | POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, | ||
731 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | ||
732 | POWER_SUPPLY_PROP_CURRENT_NOW, | ||
733 | POWER_SUPPLY_PROP_CURRENT_AVG, | ||
734 | POWER_SUPPLY_PROP_POWER_NOW, | ||
735 | POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, | ||
736 | POWER_SUPPLY_PROP_CHARGE_NOW, | ||
737 | POWER_SUPPLY_PROP_CAPACITY, | ||
738 | POWER_SUPPLY_PROP_CAPACITY_LEVEL, | ||
739 | POWER_SUPPLY_PROP_TEMP, | ||
740 | POWER_SUPPLY_PROP_TEMP_AMBIENT, | ||
741 | POWER_SUPPLY_PROP_MODEL_NAME, | ||
742 | POWER_SUPPLY_PROP_MANUFACTURER, | ||
743 | POWER_SUPPLY_PROP_SERIAL_NUMBER, | ||
744 | }; | ||
745 | |||
746 | static struct backlight_device *compalbl_device; | ||
747 | |||
748 | static struct platform_device *compal_device; | ||
749 | |||
750 | static struct rfkill *wifi_rfkill; | ||
751 | static struct rfkill *bt_rfkill; | ||
752 | |||
753 | |||
754 | |||
755 | |||
756 | |||
757 | /* =================================== */ | ||
758 | /* Initialization & clean-up functions */ | ||
759 | /* =================================== */ | ||
187 | 760 | ||
188 | static int dmi_check_cb(const struct dmi_system_id *id) | 761 | static int dmi_check_cb(const struct dmi_system_id *id) |
189 | { | 762 | { |
190 | printk(KERN_INFO "compal-laptop: Identified laptop model '%s'.\n", | 763 | printk(KERN_INFO DRIVER_NAME": Identified laptop model '%s'\n", |
191 | id->ident); | 764 | id->ident); |
765 | extra_features = false; | ||
766 | return 0; | ||
767 | } | ||
192 | 768 | ||
769 | static int dmi_check_cb_extra(const struct dmi_system_id *id) | ||
770 | { | ||
771 | printk(KERN_INFO DRIVER_NAME": Identified laptop model '%s', " | ||
772 | "enabling extra features\n", | ||
773 | id->ident); | ||
774 | extra_features = true; | ||
193 | return 0; | 775 | return 0; |
194 | } | 776 | } |
195 | 777 | ||
@@ -274,27 +856,106 @@ static struct dmi_system_id __initdata compal_dmi_table[] = { | |||
274 | }, | 856 | }, |
275 | .callback = dmi_check_cb | 857 | .callback = dmi_check_cb |
276 | }, | 858 | }, |
277 | 859 | { | |
860 | .ident = "JHL90", | ||
861 | .matches = { | ||
862 | DMI_MATCH(DMI_BOARD_NAME, "JHL90"), | ||
863 | DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"), | ||
864 | }, | ||
865 | .callback = dmi_check_cb_extra | ||
866 | }, | ||
278 | { } | 867 | { } |
279 | }; | 868 | }; |
280 | 869 | ||
870 | static void initialize_power_supply_data(struct compal_data *data) | ||
871 | { | ||
872 | data->psy.name = DRIVER_NAME; | ||
873 | data->psy.type = POWER_SUPPLY_TYPE_BATTERY; | ||
874 | data->psy.properties = compal_bat_properties; | ||
875 | data->psy.num_properties = ARRAY_SIZE(compal_bat_properties); | ||
876 | data->psy.get_property = bat_get_property; | ||
877 | |||
878 | ec_read_sequence(BAT_MANUFACTURER_NAME_ADDR, | ||
879 | data->bat_manufacturer_name, | ||
880 | BAT_MANUFACTURER_NAME_LEN); | ||
881 | data->bat_manufacturer_name[BAT_MANUFACTURER_NAME_LEN] = 0; | ||
882 | |||
883 | ec_read_sequence(BAT_MODEL_NAME_ADDR, | ||
884 | data->bat_model_name, | ||
885 | BAT_MODEL_NAME_LEN); | ||
886 | data->bat_model_name[BAT_MODEL_NAME_LEN] = 0; | ||
887 | |||
888 | scnprintf(data->bat_serial_number, BAT_SERIAL_NUMBER_LEN + 1, "%d", | ||
889 | ec_read_u16(BAT_SERIAL_NUMBER_ADDR)); | ||
890 | } | ||
891 | |||
892 | static void initialize_fan_control_data(struct compal_data *data) | ||
893 | { | ||
894 | data->pwm_enable = 2; /* Keep motherboard in control for now */ | ||
895 | data->curr_pwm = 255; /* Try not to cause a CPU_on_fire exception | ||
896 | if we take over... */ | ||
897 | } | ||
898 | |||
899 | static int setup_rfkill(void) | ||
900 | { | ||
901 | int ret; | ||
902 | |||
903 | wifi_rfkill = rfkill_alloc("compal-wifi", &compal_device->dev, | ||
904 | RFKILL_TYPE_WLAN, &compal_rfkill_ops, | ||
905 | (void *) WIRELESS_WLAN); | ||
906 | if (!wifi_rfkill) | ||
907 | return -ENOMEM; | ||
908 | |||
909 | ret = rfkill_register(wifi_rfkill); | ||
910 | if (ret) | ||
911 | goto err_wifi; | ||
912 | |||
913 | bt_rfkill = rfkill_alloc("compal-bluetooth", &compal_device->dev, | ||
914 | RFKILL_TYPE_BLUETOOTH, &compal_rfkill_ops, | ||
915 | (void *) WIRELESS_BT); | ||
916 | if (!bt_rfkill) { | ||
917 | ret = -ENOMEM; | ||
918 | goto err_allocate_bt; | ||
919 | } | ||
920 | ret = rfkill_register(bt_rfkill); | ||
921 | if (ret) | ||
922 | goto err_register_bt; | ||
923 | |||
924 | return 0; | ||
925 | |||
926 | err_register_bt: | ||
927 | rfkill_destroy(bt_rfkill); | ||
928 | |||
929 | err_allocate_bt: | ||
930 | rfkill_unregister(wifi_rfkill); | ||
931 | |||
932 | err_wifi: | ||
933 | rfkill_destroy(wifi_rfkill); | ||
934 | |||
935 | return ret; | ||
936 | } | ||
937 | |||
281 | static int __init compal_init(void) | 938 | static int __init compal_init(void) |
282 | { | 939 | { |
283 | int ret; | 940 | int ret; |
284 | 941 | ||
285 | if (acpi_disabled) | 942 | if (acpi_disabled) { |
943 | printk(KERN_ERR DRIVER_NAME": ACPI needs to be enabled for " | ||
944 | "this driver to work!\n"); | ||
286 | return -ENODEV; | 945 | return -ENODEV; |
946 | } | ||
287 | 947 | ||
288 | if (!force && !dmi_check_system(compal_dmi_table)) | 948 | if (!force && !dmi_check_system(compal_dmi_table)) { |
949 | printk(KERN_ERR DRIVER_NAME": Motherboard not recognized (You " | ||
950 | "could try the module's force-parameter)"); | ||
289 | return -ENODEV; | 951 | return -ENODEV; |
290 | 952 | } | |
291 | /* Register backlight stuff */ | ||
292 | 953 | ||
293 | if (!acpi_video_backlight_support()) { | 954 | if (!acpi_video_backlight_support()) { |
294 | struct backlight_properties props; | 955 | struct backlight_properties props; |
295 | memset(&props, 0, sizeof(struct backlight_properties)); | 956 | memset(&props, 0, sizeof(struct backlight_properties)); |
296 | props.max_brightness = COMPAL_LCD_LEVEL_MAX - 1; | 957 | props.max_brightness = BACKLIGHT_LEVEL_MAX; |
297 | compalbl_device = backlight_device_register("compal-laptop", | 958 | compalbl_device = backlight_device_register(DRIVER_NAME, |
298 | NULL, NULL, | 959 | NULL, NULL, |
299 | &compalbl_ops, | 960 | &compalbl_ops, |
300 | &props); | 961 | &props); |
@@ -304,67 +965,122 @@ static int __init compal_init(void) | |||
304 | 965 | ||
305 | ret = platform_driver_register(&compal_driver); | 966 | ret = platform_driver_register(&compal_driver); |
306 | if (ret) | 967 | if (ret) |
307 | goto fail_backlight; | 968 | goto err_backlight; |
308 | 969 | ||
309 | /* Register platform stuff */ | 970 | compal_device = platform_device_alloc(DRIVER_NAME, -1); |
310 | |||
311 | compal_device = platform_device_alloc("compal-laptop", -1); | ||
312 | if (!compal_device) { | 971 | if (!compal_device) { |
313 | ret = -ENOMEM; | 972 | ret = -ENOMEM; |
314 | goto fail_platform_driver; | 973 | goto err_platform_driver; |
315 | } | 974 | } |
316 | 975 | ||
317 | ret = platform_device_add(compal_device); | 976 | ret = platform_device_add(compal_device); /* This calls compal_probe */ |
318 | if (ret) | 977 | if (ret) |
319 | goto fail_platform_device; | 978 | goto err_platform_device; |
320 | 979 | ||
321 | ret = setup_rfkill(); | 980 | ret = setup_rfkill(); |
322 | if (ret) | 981 | if (ret) |
323 | goto fail_rfkill; | 982 | goto err_rfkill; |
324 | |||
325 | printk(KERN_INFO "compal-laptop: driver "COMPAL_DRIVER_VERSION | ||
326 | " successfully loaded.\n"); | ||
327 | 983 | ||
984 | printk(KERN_INFO DRIVER_NAME": Driver "DRIVER_VERSION | ||
985 | " successfully loaded\n"); | ||
328 | return 0; | 986 | return 0; |
329 | 987 | ||
330 | fail_rfkill: | 988 | err_rfkill: |
331 | platform_device_del(compal_device); | 989 | platform_device_del(compal_device); |
332 | 990 | ||
333 | fail_platform_device: | 991 | err_platform_device: |
334 | |||
335 | platform_device_put(compal_device); | 992 | platform_device_put(compal_device); |
336 | 993 | ||
337 | fail_platform_driver: | 994 | err_platform_driver: |
338 | |||
339 | platform_driver_unregister(&compal_driver); | 995 | platform_driver_unregister(&compal_driver); |
340 | 996 | ||
341 | fail_backlight: | 997 | err_backlight: |
342 | |||
343 | backlight_device_unregister(compalbl_device); | 998 | backlight_device_unregister(compalbl_device); |
344 | 999 | ||
345 | return ret; | 1000 | return ret; |
346 | } | 1001 | } |
347 | 1002 | ||
348 | static void __exit compal_cleanup(void) | 1003 | static int __devinit compal_probe(struct platform_device *pdev) |
349 | { | 1004 | { |
1005 | int err; | ||
1006 | struct compal_data *data; | ||
1007 | |||
1008 | if (!extra_features) | ||
1009 | return 0; | ||
1010 | |||
1011 | /* Fan control */ | ||
1012 | data = kzalloc(sizeof(struct compal_data), GFP_KERNEL); | ||
1013 | if (!data) | ||
1014 | return -ENOMEM; | ||
1015 | |||
1016 | initialize_fan_control_data(data); | ||
1017 | |||
1018 | err = sysfs_create_group(&pdev->dev.kobj, &compal_attribute_group); | ||
1019 | if (err) | ||
1020 | return err; | ||
1021 | |||
1022 | data->hwmon_dev = hwmon_device_register(&pdev->dev); | ||
1023 | if (IS_ERR(data->hwmon_dev)) { | ||
1024 | err = PTR_ERR(data->hwmon_dev); | ||
1025 | sysfs_remove_group(&pdev->dev.kobj, | ||
1026 | &compal_attribute_group); | ||
1027 | kfree(data); | ||
1028 | return err; | ||
1029 | } | ||
1030 | |||
1031 | /* Power supply */ | ||
1032 | initialize_power_supply_data(data); | ||
1033 | power_supply_register(&compal_device->dev, &data->psy); | ||
1034 | |||
1035 | platform_set_drvdata(pdev, data); | ||
1036 | |||
1037 | return 0; | ||
1038 | } | ||
350 | 1039 | ||
1040 | static void __exit compal_cleanup(void) | ||
1041 | { | ||
351 | platform_device_unregister(compal_device); | 1042 | platform_device_unregister(compal_device); |
352 | platform_driver_unregister(&compal_driver); | 1043 | platform_driver_unregister(&compal_driver); |
353 | backlight_device_unregister(compalbl_device); | 1044 | backlight_device_unregister(compalbl_device); |
354 | rfkill_unregister(wifi_rfkill); | 1045 | rfkill_unregister(wifi_rfkill); |
355 | rfkill_destroy(wifi_rfkill); | ||
356 | rfkill_unregister(bt_rfkill); | 1046 | rfkill_unregister(bt_rfkill); |
1047 | rfkill_destroy(wifi_rfkill); | ||
357 | rfkill_destroy(bt_rfkill); | 1048 | rfkill_destroy(bt_rfkill); |
358 | 1049 | ||
359 | printk(KERN_INFO "compal-laptop: driver unloaded.\n"); | 1050 | printk(KERN_INFO DRIVER_NAME": Driver unloaded\n"); |
360 | } | 1051 | } |
361 | 1052 | ||
1053 | static int __devexit compal_remove(struct platform_device *pdev) | ||
1054 | { | ||
1055 | struct compal_data *data; | ||
1056 | |||
1057 | if (!extra_features) | ||
1058 | return 0; | ||
1059 | |||
1060 | printk(KERN_INFO DRIVER_NAME": Unloading: resetting fan control " | ||
1061 | "to motherboard\n"); | ||
1062 | pwm_disable_control(); | ||
1063 | |||
1064 | data = platform_get_drvdata(pdev); | ||
1065 | hwmon_device_unregister(data->hwmon_dev); | ||
1066 | power_supply_unregister(&data->psy); | ||
1067 | |||
1068 | platform_set_drvdata(pdev, NULL); | ||
1069 | kfree(data); | ||
1070 | |||
1071 | sysfs_remove_group(&pdev->dev.kobj, &compal_attribute_group); | ||
1072 | |||
1073 | return 0; | ||
1074 | } | ||
1075 | |||
1076 | |||
362 | module_init(compal_init); | 1077 | module_init(compal_init); |
363 | module_exit(compal_cleanup); | 1078 | module_exit(compal_cleanup); |
364 | 1079 | ||
365 | MODULE_AUTHOR("Cezary Jackiewicz"); | 1080 | MODULE_AUTHOR("Cezary Jackiewicz"); |
1081 | MODULE_AUTHOR("Roald Frederickx (roald.frederickx@gmail.com)"); | ||
366 | MODULE_DESCRIPTION("Compal Laptop Support"); | 1082 | MODULE_DESCRIPTION("Compal Laptop Support"); |
367 | MODULE_VERSION(COMPAL_DRIVER_VERSION); | 1083 | MODULE_VERSION(DRIVER_VERSION); |
368 | MODULE_LICENSE("GPL"); | 1084 | MODULE_LICENSE("GPL"); |
369 | 1085 | ||
370 | MODULE_ALIAS("dmi:*:rnIFL90:rvrIFT00:*"); | 1086 | MODULE_ALIAS("dmi:*:rnIFL90:rvrIFT00:*"); |
@@ -372,6 +1088,7 @@ MODULE_ALIAS("dmi:*:rnIFL90:rvrREFERENCE:*"); | |||
372 | MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*"); | 1088 | MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*"); |
373 | MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*"); | 1089 | MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*"); |
374 | MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*"); | 1090 | MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*"); |
1091 | MODULE_ALIAS("dmi:*:rnJHL90:rvrREFERENCE:*"); | ||
375 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron910:*"); | 1092 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron910:*"); |
376 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1010:*"); | 1093 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1010:*"); |
377 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1011:*"); | 1094 | MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1011:*"); |
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 661e3ac4d5b1..b41ed5cab3e7 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c | |||
@@ -83,6 +83,12 @@ static const struct dmi_system_id __initdata dell_device_table[] = { | |||
83 | }, | 83 | }, |
84 | }, | 84 | }, |
85 | { | 85 | { |
86 | .matches = { | ||
87 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
88 | DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /*Laptop*/ | ||
89 | }, | ||
90 | }, | ||
91 | { | ||
86 | .ident = "Dell Computer Corporation", | 92 | .ident = "Dell Computer Corporation", |
87 | .matches = { | 93 | .matches = { |
88 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), | 94 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), |
@@ -467,7 +473,7 @@ static struct backlight_ops dell_ops = { | |||
467 | .update_status = dell_send_intensity, | 473 | .update_status = dell_send_intensity, |
468 | }; | 474 | }; |
469 | 475 | ||
470 | bool dell_laptop_i8042_filter(unsigned char data, unsigned char str, | 476 | static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str, |
471 | struct serio *port) | 477 | struct serio *port) |
472 | { | 478 | { |
473 | static bool extended; | 479 | static bool extended; |
@@ -621,4 +627,5 @@ MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); | |||
621 | MODULE_DESCRIPTION("Dell laptop driver"); | 627 | MODULE_DESCRIPTION("Dell laptop driver"); |
622 | MODULE_LICENSE("GPL"); | 628 | MODULE_LICENSE("GPL"); |
623 | MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*"); | 629 | MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*"); |
630 | MODULE_ALIAS("dmi:*svnDellInc.:*:ct9:*"); | ||
624 | MODULE_ALIAS("dmi:*svnDellComputerCorporation.:*:ct8:*"); | 631 | MODULE_ALIAS("dmi:*svnDellComputerCorporation.:*:ct8:*"); |
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 66f53c3c35e8..08fb70f6d9bf 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c | |||
@@ -221,7 +221,7 @@ static void dell_wmi_notify(u32 value, void *context) | |||
221 | return; | 221 | return; |
222 | } | 222 | } |
223 | 223 | ||
224 | if (dell_new_hk_type) | 224 | if (dell_new_hk_type || buffer_entry[1] == 0x0) |
225 | reported_key = (int)buffer_entry[2]; | 225 | reported_key = (int)buffer_entry[2]; |
226 | else | 226 | else |
227 | reported_key = (int)buffer_entry[1] & 0xffff; | 227 | reported_key = (int)buffer_entry[1] & 0xffff; |
@@ -339,13 +339,18 @@ static int __init dell_wmi_init(void) | |||
339 | acpi_video = acpi_video_backlight_support(); | 339 | acpi_video = acpi_video_backlight_support(); |
340 | 340 | ||
341 | err = dell_wmi_input_setup(); | 341 | err = dell_wmi_input_setup(); |
342 | if (err) | 342 | if (err) { |
343 | if (dell_new_hk_type) | ||
344 | kfree(dell_wmi_keymap); | ||
343 | return err; | 345 | return err; |
346 | } | ||
344 | 347 | ||
345 | status = wmi_install_notify_handler(DELL_EVENT_GUID, | 348 | status = wmi_install_notify_handler(DELL_EVENT_GUID, |
346 | dell_wmi_notify, NULL); | 349 | dell_wmi_notify, NULL); |
347 | if (ACPI_FAILURE(status)) { | 350 | if (ACPI_FAILURE(status)) { |
348 | input_unregister_device(dell_wmi_input_dev); | 351 | input_unregister_device(dell_wmi_input_dev); |
352 | if (dell_new_hk_type) | ||
353 | kfree(dell_wmi_keymap); | ||
349 | printk(KERN_ERR | 354 | printk(KERN_ERR |
350 | "dell-wmi: Unable to register notify handler - %d\n", | 355 | "dell-wmi: Unable to register notify handler - %d\n", |
351 | status); | 356 | status); |
@@ -359,6 +364,8 @@ static void __exit dell_wmi_exit(void) | |||
359 | { | 364 | { |
360 | wmi_remove_notify_handler(DELL_EVENT_GUID); | 365 | wmi_remove_notify_handler(DELL_EVENT_GUID); |
361 | input_unregister_device(dell_wmi_input_dev); | 366 | input_unregister_device(dell_wmi_input_dev); |
367 | if (dell_new_hk_type) | ||
368 | kfree(dell_wmi_keymap); | ||
362 | } | 369 | } |
363 | 370 | ||
364 | module_init(dell_wmi_init); | 371 | module_init(dell_wmi_init); |
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 0306174ba875..6b8e06206c46 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c | |||
@@ -53,7 +53,7 @@ MODULE_LICENSE("GPL"); | |||
53 | 53 | ||
54 | static bool hotplug_disabled; | 54 | static bool hotplug_disabled; |
55 | 55 | ||
56 | module_param(hotplug_disabled, bool, 0644); | 56 | module_param(hotplug_disabled, bool, 0444); |
57 | MODULE_PARM_DESC(hotplug_disabled, | 57 | MODULE_PARM_DESC(hotplug_disabled, |
58 | "Disable hotplug for wireless device. " | 58 | "Disable hotplug for wireless device. " |
59 | "If your laptop need that, please report to " | 59 | "If your laptop need that, please report to " |
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index e325aeb37d2e..f44cd2620ff9 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c | |||
@@ -182,7 +182,7 @@ static enum led_brightness logolamp_get(struct led_classdev *cdev); | |||
182 | static void logolamp_set(struct led_classdev *cdev, | 182 | static void logolamp_set(struct led_classdev *cdev, |
183 | enum led_brightness brightness); | 183 | enum led_brightness brightness); |
184 | 184 | ||
185 | struct led_classdev logolamp_led = { | 185 | static struct led_classdev logolamp_led = { |
186 | .name = "fujitsu::logolamp", | 186 | .name = "fujitsu::logolamp", |
187 | .brightness_get = logolamp_get, | 187 | .brightness_get = logolamp_get, |
188 | .brightness_set = logolamp_set | 188 | .brightness_set = logolamp_set |
@@ -192,7 +192,7 @@ static enum led_brightness kblamps_get(struct led_classdev *cdev); | |||
192 | static void kblamps_set(struct led_classdev *cdev, | 192 | static void kblamps_set(struct led_classdev *cdev, |
193 | enum led_brightness brightness); | 193 | enum led_brightness brightness); |
194 | 194 | ||
195 | struct led_classdev kblamps_led = { | 195 | static struct led_classdev kblamps_led = { |
196 | .name = "fujitsu::kblamps", | 196 | .name = "fujitsu::kblamps", |
197 | .brightness_get = kblamps_get, | 197 | .brightness_get = kblamps_get, |
198 | .brightness_set = kblamps_set | 198 | .brightness_set = kblamps_set |
@@ -603,7 +603,7 @@ static int dmi_check_cb_s6410(const struct dmi_system_id *id) | |||
603 | dmi_check_cb_common(id); | 603 | dmi_check_cb_common(id); |
604 | fujitsu->keycode1 = KEY_SCREENLOCK; /* "Lock" */ | 604 | fujitsu->keycode1 = KEY_SCREENLOCK; /* "Lock" */ |
605 | fujitsu->keycode2 = KEY_HELP; /* "Mobility Center" */ | 605 | fujitsu->keycode2 = KEY_HELP; /* "Mobility Center" */ |
606 | return 0; | 606 | return 1; |
607 | } | 607 | } |
608 | 608 | ||
609 | static int dmi_check_cb_s6420(const struct dmi_system_id *id) | 609 | static int dmi_check_cb_s6420(const struct dmi_system_id *id) |
@@ -611,7 +611,7 @@ static int dmi_check_cb_s6420(const struct dmi_system_id *id) | |||
611 | dmi_check_cb_common(id); | 611 | dmi_check_cb_common(id); |
612 | fujitsu->keycode1 = KEY_SCREENLOCK; /* "Lock" */ | 612 | fujitsu->keycode1 = KEY_SCREENLOCK; /* "Lock" */ |
613 | fujitsu->keycode2 = KEY_HELP; /* "Mobility Center" */ | 613 | fujitsu->keycode2 = KEY_HELP; /* "Mobility Center" */ |
614 | return 0; | 614 | return 1; |
615 | } | 615 | } |
616 | 616 | ||
617 | static int dmi_check_cb_p8010(const struct dmi_system_id *id) | 617 | static int dmi_check_cb_p8010(const struct dmi_system_id *id) |
@@ -620,7 +620,7 @@ static int dmi_check_cb_p8010(const struct dmi_system_id *id) | |||
620 | fujitsu->keycode1 = KEY_HELP; /* "Support" */ | 620 | fujitsu->keycode1 = KEY_HELP; /* "Support" */ |
621 | fujitsu->keycode3 = KEY_SWITCHVIDEOMODE; /* "Presentation" */ | 621 | fujitsu->keycode3 = KEY_SWITCHVIDEOMODE; /* "Presentation" */ |
622 | fujitsu->keycode4 = KEY_WWW; /* "Internet" */ | 622 | fujitsu->keycode4 = KEY_WWW; /* "Internet" */ |
623 | return 0; | 623 | return 1; |
624 | } | 624 | } |
625 | 625 | ||
626 | static struct dmi_system_id fujitsu_dmi_table[] = { | 626 | static struct dmi_system_id fujitsu_dmi_table[] = { |
@@ -725,6 +725,7 @@ static int acpi_fujitsu_add(struct acpi_device *device) | |||
725 | 725 | ||
726 | err_unregister_input_dev: | 726 | err_unregister_input_dev: |
727 | input_unregister_device(input); | 727 | input_unregister_device(input); |
728 | input = NULL; | ||
728 | err_free_input_dev: | 729 | err_free_input_dev: |
729 | input_free_device(input); | 730 | input_free_device(input); |
730 | err_stop: | 731 | err_stop: |
@@ -738,8 +739,6 @@ static int acpi_fujitsu_remove(struct acpi_device *device, int type) | |||
738 | 739 | ||
739 | input_unregister_device(input); | 740 | input_unregister_device(input); |
740 | 741 | ||
741 | input_free_device(input); | ||
742 | |||
743 | fujitsu->acpi_handle = NULL; | 742 | fujitsu->acpi_handle = NULL; |
744 | 743 | ||
745 | return 0; | 744 | return 0; |
@@ -930,6 +929,7 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) | |||
930 | 929 | ||
931 | err_unregister_input_dev: | 930 | err_unregister_input_dev: |
932 | input_unregister_device(input); | 931 | input_unregister_device(input); |
932 | input = NULL; | ||
933 | err_free_input_dev: | 933 | err_free_input_dev: |
934 | input_free_device(input); | 934 | input_free_device(input); |
935 | err_free_fifo: | 935 | err_free_fifo: |
@@ -953,8 +953,6 @@ static int acpi_fujitsu_hotkey_remove(struct acpi_device *device, int type) | |||
953 | 953 | ||
954 | input_unregister_device(input); | 954 | input_unregister_device(input); |
955 | 955 | ||
956 | input_free_device(input); | ||
957 | |||
958 | kfifo_free(&fujitsu_hotkey->fifo); | 956 | kfifo_free(&fujitsu_hotkey->fifo); |
959 | 957 | ||
960 | fujitsu_hotkey->acpi_handle = NULL; | 958 | fujitsu_hotkey->acpi_handle = NULL; |
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 51c07a05a7bc..f15516374987 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c | |||
@@ -29,7 +29,6 @@ | |||
29 | #include <linux/slab.h> | 29 | #include <linux/slab.h> |
30 | #include <linux/types.h> | 30 | #include <linux/types.h> |
31 | #include <linux/input.h> | 31 | #include <linux/input.h> |
32 | #include <acpi/acpi_drivers.h> | ||
33 | #include <linux/platform_device.h> | 32 | #include <linux/platform_device.h> |
34 | #include <linux/acpi.h> | 33 | #include <linux/acpi.h> |
35 | #include <linux/rfkill.h> | 34 | #include <linux/rfkill.h> |
@@ -52,12 +51,25 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); | |||
52 | #define HPWMI_WIRELESS_QUERY 0x5 | 51 | #define HPWMI_WIRELESS_QUERY 0x5 |
53 | #define HPWMI_HOTKEY_QUERY 0xc | 52 | #define HPWMI_HOTKEY_QUERY 0xc |
54 | 53 | ||
54 | #define PREFIX "HP WMI: " | ||
55 | #define UNIMP "Unimplemented " | ||
56 | |||
55 | enum hp_wmi_radio { | 57 | enum hp_wmi_radio { |
56 | HPWMI_WIFI = 0, | 58 | HPWMI_WIFI = 0, |
57 | HPWMI_BLUETOOTH = 1, | 59 | HPWMI_BLUETOOTH = 1, |
58 | HPWMI_WWAN = 2, | 60 | HPWMI_WWAN = 2, |
59 | }; | 61 | }; |
60 | 62 | ||
63 | enum hp_wmi_event_ids { | ||
64 | HPWMI_DOCK_EVENT = 1, | ||
65 | HPWMI_PARK_HDD = 2, | ||
66 | HPWMI_SMART_ADAPTER = 3, | ||
67 | HPWMI_BEZEL_BUTTON = 4, | ||
68 | HPWMI_WIRELESS = 5, | ||
69 | HPWMI_CPU_BATTERY_THROTTLE = 6, | ||
70 | HPWMI_LOCK_SWITCH = 7, | ||
71 | }; | ||
72 | |||
61 | static int __devinit hp_wmi_bios_setup(struct platform_device *device); | 73 | static int __devinit hp_wmi_bios_setup(struct platform_device *device); |
62 | static int __exit hp_wmi_bios_remove(struct platform_device *device); | 74 | static int __exit hp_wmi_bios_remove(struct platform_device *device); |
63 | static int hp_wmi_resume_handler(struct device *device); | 75 | static int hp_wmi_resume_handler(struct device *device); |
@@ -67,13 +79,12 @@ struct bios_args { | |||
67 | u32 command; | 79 | u32 command; |
68 | u32 commandtype; | 80 | u32 commandtype; |
69 | u32 datasize; | 81 | u32 datasize; |
70 | u32 data; | 82 | char *data; |
71 | }; | 83 | }; |
72 | 84 | ||
73 | struct bios_return { | 85 | struct bios_return { |
74 | u32 sigpass; | 86 | u32 sigpass; |
75 | u32 return_code; | 87 | u32 return_code; |
76 | u32 value; | ||
77 | }; | 88 | }; |
78 | 89 | ||
79 | struct key_entry { | 90 | struct key_entry { |
@@ -88,6 +99,7 @@ static struct key_entry hp_wmi_keymap[] = { | |||
88 | {KE_KEY, 0x02, KEY_BRIGHTNESSUP}, | 99 | {KE_KEY, 0x02, KEY_BRIGHTNESSUP}, |
89 | {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN}, | 100 | {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN}, |
90 | {KE_KEY, 0x20e6, KEY_PROG1}, | 101 | {KE_KEY, 0x20e6, KEY_PROG1}, |
102 | {KE_KEY, 0x20e8, KEY_MEDIA}, | ||
91 | {KE_KEY, 0x2142, KEY_MEDIA}, | 103 | {KE_KEY, 0x2142, KEY_MEDIA}, |
92 | {KE_KEY, 0x213b, KEY_INFO}, | 104 | {KE_KEY, 0x213b, KEY_INFO}, |
93 | {KE_KEY, 0x2169, KEY_DIRECTION}, | 105 | {KE_KEY, 0x2169, KEY_DIRECTION}, |
@@ -117,7 +129,27 @@ static struct platform_driver hp_wmi_driver = { | |||
117 | .remove = hp_wmi_bios_remove, | 129 | .remove = hp_wmi_bios_remove, |
118 | }; | 130 | }; |
119 | 131 | ||
120 | static int hp_wmi_perform_query(int query, int write, int value) | 132 | /* |
133 | * hp_wmi_perform_query | ||
134 | * | ||
135 | * query: The commandtype -> What should be queried | ||
136 | * write: The command -> 0 read, 1 write, 3 ODM specific | ||
137 | * buffer: Buffer used as input and/or output | ||
138 | * buffersize: Size of buffer | ||
139 | * | ||
140 | * returns zero on success | ||
141 | * an HP WMI query specific error code (which is positive) | ||
142 | * -EINVAL if the query was not successful at all | ||
143 | * -EINVAL if the output buffer size exceeds buffersize | ||
144 | * | ||
145 | * Note: The buffersize must at least be the maximum of the input and output | ||
146 | * size. E.g. Battery info query (0x7) is defined to have 1 byte input | ||
147 | * and 128 byte output. The caller would do: | ||
148 | * buffer = kzalloc(128, GFP_KERNEL); | ||
149 | * ret = hp_wmi_perform_query(0x7, 0, buffer, 128) | ||
150 | */ | ||
151 | static int hp_wmi_perform_query(int query, int write, char *buffer, | ||
152 | int buffersize) | ||
121 | { | 153 | { |
122 | struct bios_return bios_return; | 154 | struct bios_return bios_return; |
123 | acpi_status status; | 155 | acpi_status status; |
@@ -126,8 +158,8 @@ static int hp_wmi_perform_query(int query, int write, int value) | |||
126 | .signature = 0x55434553, | 158 | .signature = 0x55434553, |
127 | .command = write ? 0x2 : 0x1, | 159 | .command = write ? 0x2 : 0x1, |
128 | .commandtype = query, | 160 | .commandtype = query, |
129 | .datasize = write ? 0x4 : 0, | 161 | .datasize = buffersize, |
130 | .data = value, | 162 | .data = buffer, |
131 | }; | 163 | }; |
132 | struct acpi_buffer input = { sizeof(struct bios_args), &args }; | 164 | struct acpi_buffer input = { sizeof(struct bios_args), &args }; |
133 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | 165 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; |
@@ -144,54 +176,90 @@ static int hp_wmi_perform_query(int query, int write, int value) | |||
144 | } | 176 | } |
145 | 177 | ||
146 | bios_return = *((struct bios_return *)obj->buffer.pointer); | 178 | bios_return = *((struct bios_return *)obj->buffer.pointer); |
179 | |||
180 | if (bios_return.return_code) { | ||
181 | printk(KERN_WARNING PREFIX "Query %d returned %d\n", query, | ||
182 | bios_return.return_code); | ||
183 | kfree(obj); | ||
184 | return bios_return.return_code; | ||
185 | } | ||
186 | if (obj->buffer.length - sizeof(bios_return) > buffersize) { | ||
187 | kfree(obj); | ||
188 | return -EINVAL; | ||
189 | } | ||
190 | |||
191 | memset(buffer, 0, buffersize); | ||
192 | memcpy(buffer, | ||
193 | ((char *)obj->buffer.pointer) + sizeof(struct bios_return), | ||
194 | obj->buffer.length - sizeof(bios_return)); | ||
147 | kfree(obj); | 195 | kfree(obj); |
148 | if (bios_return.return_code > 0) | 196 | return 0; |
149 | return bios_return.return_code * -1; | ||
150 | else | ||
151 | return bios_return.value; | ||
152 | } | 197 | } |
153 | 198 | ||
154 | static int hp_wmi_display_state(void) | 199 | static int hp_wmi_display_state(void) |
155 | { | 200 | { |
156 | return hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, 0); | 201 | int state; |
202 | int ret = hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, (char *)&state, | ||
203 | sizeof(state)); | ||
204 | if (ret) | ||
205 | return -EINVAL; | ||
206 | return state; | ||
157 | } | 207 | } |
158 | 208 | ||
159 | static int hp_wmi_hddtemp_state(void) | 209 | static int hp_wmi_hddtemp_state(void) |
160 | { | 210 | { |
161 | return hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, 0); | 211 | int state; |
212 | int ret = hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, (char *)&state, | ||
213 | sizeof(state)); | ||
214 | if (ret) | ||
215 | return -EINVAL; | ||
216 | return state; | ||
162 | } | 217 | } |
163 | 218 | ||
164 | static int hp_wmi_als_state(void) | 219 | static int hp_wmi_als_state(void) |
165 | { | 220 | { |
166 | return hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, 0); | 221 | int state; |
222 | int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, (char *)&state, | ||
223 | sizeof(state)); | ||
224 | if (ret) | ||
225 | return -EINVAL; | ||
226 | return state; | ||
167 | } | 227 | } |
168 | 228 | ||
169 | static int hp_wmi_dock_state(void) | 229 | static int hp_wmi_dock_state(void) |
170 | { | 230 | { |
171 | int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, 0); | 231 | int state; |
232 | int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, (char *)&state, | ||
233 | sizeof(state)); | ||
172 | 234 | ||
173 | if (ret < 0) | 235 | if (ret) |
174 | return ret; | 236 | return -EINVAL; |
175 | 237 | ||
176 | return ret & 0x1; | 238 | return state & 0x1; |
177 | } | 239 | } |
178 | 240 | ||
179 | static int hp_wmi_tablet_state(void) | 241 | static int hp_wmi_tablet_state(void) |
180 | { | 242 | { |
181 | int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, 0); | 243 | int state; |
182 | 244 | int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, (char *)&state, | |
183 | if (ret < 0) | 245 | sizeof(state)); |
246 | if (ret) | ||
184 | return ret; | 247 | return ret; |
185 | 248 | ||
186 | return (ret & 0x4) ? 1 : 0; | 249 | return (state & 0x4) ? 1 : 0; |
187 | } | 250 | } |
188 | 251 | ||
189 | static int hp_wmi_set_block(void *data, bool blocked) | 252 | static int hp_wmi_set_block(void *data, bool blocked) |
190 | { | 253 | { |
191 | enum hp_wmi_radio r = (enum hp_wmi_radio) data; | 254 | enum hp_wmi_radio r = (enum hp_wmi_radio) data; |
192 | int query = BIT(r + 8) | ((!blocked) << r); | 255 | int query = BIT(r + 8) | ((!blocked) << r); |
256 | int ret; | ||
193 | 257 | ||
194 | return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, query); | 258 | ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, |
259 | (char *)&query, sizeof(query)); | ||
260 | if (ret) | ||
261 | return -EINVAL; | ||
262 | return 0; | ||
195 | } | 263 | } |
196 | 264 | ||
197 | static const struct rfkill_ops hp_wmi_rfkill_ops = { | 265 | static const struct rfkill_ops hp_wmi_rfkill_ops = { |
@@ -200,8 +268,13 @@ static const struct rfkill_ops hp_wmi_rfkill_ops = { | |||
200 | 268 | ||
201 | static bool hp_wmi_get_sw_state(enum hp_wmi_radio r) | 269 | static bool hp_wmi_get_sw_state(enum hp_wmi_radio r) |
202 | { | 270 | { |
203 | int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); | 271 | int wireless; |
204 | int mask = 0x200 << (r * 8); | 272 | int mask; |
273 | hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, | ||
274 | (char *)&wireless, sizeof(wireless)); | ||
275 | /* TBD: Pass error */ | ||
276 | |||
277 | mask = 0x200 << (r * 8); | ||
205 | 278 | ||
206 | if (wireless & mask) | 279 | if (wireless & mask) |
207 | return false; | 280 | return false; |
@@ -211,8 +284,13 @@ static bool hp_wmi_get_sw_state(enum hp_wmi_radio r) | |||
211 | 284 | ||
212 | static bool hp_wmi_get_hw_state(enum hp_wmi_radio r) | 285 | static bool hp_wmi_get_hw_state(enum hp_wmi_radio r) |
213 | { | 286 | { |
214 | int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); | 287 | int wireless; |
215 | int mask = 0x800 << (r * 8); | 288 | int mask; |
289 | hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, | ||
290 | (char *)&wireless, sizeof(wireless)); | ||
291 | /* TBD: Pass error */ | ||
292 | |||
293 | mask = 0x800 << (r * 8); | ||
216 | 294 | ||
217 | if (wireless & mask) | 295 | if (wireless & mask) |
218 | return false; | 296 | return false; |
@@ -269,7 +347,11 @@ static ssize_t set_als(struct device *dev, struct device_attribute *attr, | |||
269 | const char *buf, size_t count) | 347 | const char *buf, size_t count) |
270 | { | 348 | { |
271 | u32 tmp = simple_strtoul(buf, NULL, 10); | 349 | u32 tmp = simple_strtoul(buf, NULL, 10); |
272 | hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, tmp); | 350 | int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, (char *)&tmp, |
351 | sizeof(tmp)); | ||
352 | if (ret) | ||
353 | return -EINVAL; | ||
354 | |||
273 | return count; | 355 | return count; |
274 | } | 356 | } |
275 | 357 | ||
@@ -338,47 +420,82 @@ static void hp_wmi_notify(u32 value, void *context) | |||
338 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | 420 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; |
339 | static struct key_entry *key; | 421 | static struct key_entry *key; |
340 | union acpi_object *obj; | 422 | union acpi_object *obj; |
341 | int eventcode; | 423 | u32 event_id, event_data; |
424 | int key_code, ret; | ||
425 | u32 *location; | ||
342 | acpi_status status; | 426 | acpi_status status; |
343 | 427 | ||
344 | status = wmi_get_event_data(value, &response); | 428 | status = wmi_get_event_data(value, &response); |
345 | if (status != AE_OK) { | 429 | if (status != AE_OK) { |
346 | printk(KERN_INFO "hp-wmi: bad event status 0x%x\n", status); | 430 | printk(KERN_INFO PREFIX "bad event status 0x%x\n", status); |
347 | return; | 431 | return; |
348 | } | 432 | } |
349 | 433 | ||
350 | obj = (union acpi_object *)response.pointer; | 434 | obj = (union acpi_object *)response.pointer; |
351 | 435 | ||
352 | if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length != 8) { | 436 | if (!obj) |
353 | printk(KERN_INFO "HP WMI: Unknown response received\n"); | 437 | return; |
438 | if (obj->type != ACPI_TYPE_BUFFER) { | ||
439 | printk(KERN_INFO "hp-wmi: Unknown response received %d\n", | ||
440 | obj->type); | ||
354 | kfree(obj); | 441 | kfree(obj); |
355 | return; | 442 | return; |
356 | } | 443 | } |
357 | 444 | ||
358 | eventcode = *((u8 *) obj->buffer.pointer); | 445 | /* |
446 | * Depending on ACPI version the concatenation of id and event data | ||
447 | * inside _WED function will result in a 8 or 16 byte buffer. | ||
448 | */ | ||
449 | location = (u32 *)obj->buffer.pointer; | ||
450 | if (obj->buffer.length == 8) { | ||
451 | event_id = *location; | ||
452 | event_data = *(location + 1); | ||
453 | } else if (obj->buffer.length == 16) { | ||
454 | event_id = *location; | ||
455 | event_data = *(location + 2); | ||
456 | } else { | ||
457 | printk(KERN_INFO "hp-wmi: Unknown buffer length %d\n", | ||
458 | obj->buffer.length); | ||
459 | kfree(obj); | ||
460 | return; | ||
461 | } | ||
359 | kfree(obj); | 462 | kfree(obj); |
360 | if (eventcode == 0x4) | 463 | |
361 | eventcode = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0, | 464 | switch (event_id) { |
362 | 0); | 465 | case HPWMI_DOCK_EVENT: |
363 | key = hp_wmi_get_entry_by_scancode(eventcode); | ||
364 | if (key) { | ||
365 | switch (key->type) { | ||
366 | case KE_KEY: | ||
367 | input_report_key(hp_wmi_input_dev, | ||
368 | key->keycode, 1); | ||
369 | input_sync(hp_wmi_input_dev); | ||
370 | input_report_key(hp_wmi_input_dev, | ||
371 | key->keycode, 0); | ||
372 | input_sync(hp_wmi_input_dev); | ||
373 | break; | ||
374 | } | ||
375 | } else if (eventcode == 0x1) { | ||
376 | input_report_switch(hp_wmi_input_dev, SW_DOCK, | 466 | input_report_switch(hp_wmi_input_dev, SW_DOCK, |
377 | hp_wmi_dock_state()); | 467 | hp_wmi_dock_state()); |
378 | input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, | 468 | input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, |
379 | hp_wmi_tablet_state()); | 469 | hp_wmi_tablet_state()); |
380 | input_sync(hp_wmi_input_dev); | 470 | input_sync(hp_wmi_input_dev); |
381 | } else if (eventcode == 0x5) { | 471 | break; |
472 | case HPWMI_PARK_HDD: | ||
473 | break; | ||
474 | case HPWMI_SMART_ADAPTER: | ||
475 | break; | ||
476 | case HPWMI_BEZEL_BUTTON: | ||
477 | ret = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0, | ||
478 | (char *)&key_code, | ||
479 | sizeof(key_code)); | ||
480 | if (ret) | ||
481 | break; | ||
482 | key = hp_wmi_get_entry_by_scancode(key_code); | ||
483 | if (key) { | ||
484 | switch (key->type) { | ||
485 | case KE_KEY: | ||
486 | input_report_key(hp_wmi_input_dev, | ||
487 | key->keycode, 1); | ||
488 | input_sync(hp_wmi_input_dev); | ||
489 | input_report_key(hp_wmi_input_dev, | ||
490 | key->keycode, 0); | ||
491 | input_sync(hp_wmi_input_dev); | ||
492 | break; | ||
493 | } | ||
494 | } else | ||
495 | printk(KERN_INFO PREFIX "Unknown key code - 0x%x\n", | ||
496 | key_code); | ||
497 | break; | ||
498 | case HPWMI_WIRELESS: | ||
382 | if (wifi_rfkill) | 499 | if (wifi_rfkill) |
383 | rfkill_set_states(wifi_rfkill, | 500 | rfkill_set_states(wifi_rfkill, |
384 | hp_wmi_get_sw_state(HPWMI_WIFI), | 501 | hp_wmi_get_sw_state(HPWMI_WIFI), |
@@ -391,9 +508,18 @@ static void hp_wmi_notify(u32 value, void *context) | |||
391 | rfkill_set_states(wwan_rfkill, | 508 | rfkill_set_states(wwan_rfkill, |
392 | hp_wmi_get_sw_state(HPWMI_WWAN), | 509 | hp_wmi_get_sw_state(HPWMI_WWAN), |
393 | hp_wmi_get_hw_state(HPWMI_WWAN)); | 510 | hp_wmi_get_hw_state(HPWMI_WWAN)); |
394 | } else | 511 | break; |
395 | printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n", | 512 | case HPWMI_CPU_BATTERY_THROTTLE: |
396 | eventcode); | 513 | printk(KERN_INFO PREFIX UNIMP "CPU throttle because of 3 Cell" |
514 | " battery event detected\n"); | ||
515 | break; | ||
516 | case HPWMI_LOCK_SWITCH: | ||
517 | break; | ||
518 | default: | ||
519 | printk(KERN_INFO PREFIX "Unknown event_id - %d - 0x%x\n", | ||
520 | event_id, event_data); | ||
521 | break; | ||
522 | } | ||
397 | } | 523 | } |
398 | 524 | ||
399 | static int __init hp_wmi_input_setup(void) | 525 | static int __init hp_wmi_input_setup(void) |
@@ -402,6 +528,8 @@ static int __init hp_wmi_input_setup(void) | |||
402 | int err; | 528 | int err; |
403 | 529 | ||
404 | hp_wmi_input_dev = input_allocate_device(); | 530 | hp_wmi_input_dev = input_allocate_device(); |
531 | if (!hp_wmi_input_dev) | ||
532 | return -ENOMEM; | ||
405 | 533 | ||
406 | hp_wmi_input_dev->name = "HP WMI hotkeys"; | 534 | hp_wmi_input_dev->name = "HP WMI hotkeys"; |
407 | hp_wmi_input_dev->phys = "wmi/input0"; | 535 | hp_wmi_input_dev->phys = "wmi/input0"; |
@@ -450,7 +578,12 @@ static void cleanup_sysfs(struct platform_device *device) | |||
450 | static int __devinit hp_wmi_bios_setup(struct platform_device *device) | 578 | static int __devinit hp_wmi_bios_setup(struct platform_device *device) |
451 | { | 579 | { |
452 | int err; | 580 | int err; |
453 | int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); | 581 | int wireless; |
582 | |||
583 | err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, (char *)&wireless, | ||
584 | sizeof(wireless)); | ||
585 | if (err) | ||
586 | return err; | ||
454 | 587 | ||
455 | err = device_create_file(&device->dev, &dev_attr_display); | 588 | err = device_create_file(&device->dev, &dev_attr_display); |
456 | if (err) | 589 | if (err) |
@@ -581,27 +714,51 @@ static int hp_wmi_resume_handler(struct device *device) | |||
581 | static int __init hp_wmi_init(void) | 714 | static int __init hp_wmi_init(void) |
582 | { | 715 | { |
583 | int err; | 716 | int err; |
717 | int event_capable = wmi_has_guid(HPWMI_EVENT_GUID); | ||
718 | int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID); | ||
584 | 719 | ||
585 | if (wmi_has_guid(HPWMI_EVENT_GUID)) { | 720 | if (event_capable) { |
586 | err = wmi_install_notify_handler(HPWMI_EVENT_GUID, | 721 | err = wmi_install_notify_handler(HPWMI_EVENT_GUID, |
587 | hp_wmi_notify, NULL); | 722 | hp_wmi_notify, NULL); |
588 | if (ACPI_SUCCESS(err)) | 723 | if (ACPI_FAILURE(err)) |
589 | hp_wmi_input_setup(); | 724 | return -EINVAL; |
725 | err = hp_wmi_input_setup(); | ||
726 | if (err) { | ||
727 | wmi_remove_notify_handler(HPWMI_EVENT_GUID); | ||
728 | return err; | ||
729 | } | ||
590 | } | 730 | } |
591 | 731 | ||
592 | if (wmi_has_guid(HPWMI_BIOS_GUID)) { | 732 | if (bios_capable) { |
593 | err = platform_driver_register(&hp_wmi_driver); | 733 | err = platform_driver_register(&hp_wmi_driver); |
594 | if (err) | 734 | if (err) |
595 | return 0; | 735 | goto err_driver_reg; |
596 | hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1); | 736 | hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1); |
597 | if (!hp_wmi_platform_dev) { | 737 | if (!hp_wmi_platform_dev) { |
598 | platform_driver_unregister(&hp_wmi_driver); | 738 | err = -ENOMEM; |
599 | return 0; | 739 | goto err_device_alloc; |
600 | } | 740 | } |
601 | platform_device_add(hp_wmi_platform_dev); | 741 | err = platform_device_add(hp_wmi_platform_dev); |
742 | if (err) | ||
743 | goto err_device_add; | ||
602 | } | 744 | } |
603 | 745 | ||
746 | if (!bios_capable && !event_capable) | ||
747 | return -ENODEV; | ||
748 | |||
604 | return 0; | 749 | return 0; |
750 | |||
751 | err_device_add: | ||
752 | platform_device_put(hp_wmi_platform_dev); | ||
753 | err_device_alloc: | ||
754 | platform_driver_unregister(&hp_wmi_driver); | ||
755 | err_driver_reg: | ||
756 | if (wmi_has_guid(HPWMI_EVENT_GUID)) { | ||
757 | input_unregister_device(hp_wmi_input_dev); | ||
758 | wmi_remove_notify_handler(HPWMI_EVENT_GUID); | ||
759 | } | ||
760 | |||
761 | return err; | ||
605 | } | 762 | } |
606 | 763 | ||
607 | static void __exit hp_wmi_exit(void) | 764 | static void __exit hp_wmi_exit(void) |
@@ -611,7 +768,7 @@ static void __exit hp_wmi_exit(void) | |||
611 | input_unregister_device(hp_wmi_input_dev); | 768 | input_unregister_device(hp_wmi_input_dev); |
612 | } | 769 | } |
613 | if (hp_wmi_platform_dev) { | 770 | if (hp_wmi_platform_dev) { |
614 | platform_device_del(hp_wmi_platform_dev); | 771 | platform_device_unregister(hp_wmi_platform_dev); |
615 | platform_driver_unregister(&hp_wmi_driver); | 772 | platform_driver_unregister(&hp_wmi_driver); |
616 | } | 773 | } |
617 | } | 774 | } |
diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c new file mode 100644 index 000000000000..afe82e50dfea --- /dev/null +++ b/drivers/platform/x86/intel_ips.c | |||
@@ -0,0 +1,1660 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2009-2010 Intel Corporation | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License along with | ||
14 | * this program; if not, write to the Free Software Foundation, Inc., | ||
15 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
16 | * | ||
17 | * The full GNU General Public License is included in this distribution in | ||
18 | * the file called "COPYING". | ||
19 | * | ||
20 | * Authors: | ||
21 | * Jesse Barnes <jbarnes@virtuousgeek.org> | ||
22 | */ | ||
23 | |||
24 | /* | ||
25 | * Some Intel Ibex Peak based platforms support so-called "intelligent | ||
26 | * power sharing", which allows the CPU and GPU to cooperate to maximize | ||
27 | * performance within a given TDP (thermal design point). This driver | ||
28 | * performs the coordination between the CPU and GPU, monitors thermal and | ||
29 | * power statistics in the platform, and initializes power monitoring | ||
30 | * hardware. It also provides a few tunables to control behavior. Its | ||
31 | * primary purpose is to safely allow CPU and GPU turbo modes to be enabled | ||
32 | * by tracking power and thermal budget; secondarily it can boost turbo | ||
33 | * performance by allocating more power or thermal budget to the CPU or GPU | ||
34 | * based on available headroom and activity. | ||
35 | * | ||
36 | * The basic algorithm is driven by a 5s moving average of tempurature. If | ||
37 | * thermal headroom is available, the CPU and/or GPU power clamps may be | ||
38 | * adjusted upwards. If we hit the thermal ceiling or a thermal trigger, | ||
39 | * we scale back the clamp. Aside from trigger events (when we're critically | ||
40 | * close or over our TDP) we don't adjust the clamps more than once every | ||
41 | * five seconds. | ||
42 | * | ||
43 | * The thermal device (device 31, function 6) has a set of registers that | ||
44 | * are updated by the ME firmware. The ME should also take the clamp values | ||
45 | * written to those registers and write them to the CPU, but we currently | ||
46 | * bypass that functionality and write the CPU MSR directly. | ||
47 | * | ||
48 | * UNSUPPORTED: | ||
49 | * - dual MCP configs | ||
50 | * | ||
51 | * TODO: | ||
52 | * - handle CPU hotplug | ||
53 | * - provide turbo enable/disable api | ||
54 | * - make sure we can write turbo enable/disable reg based on MISC_EN | ||
55 | * | ||
56 | * Related documents: | ||
57 | * - CDI 403777, 403778 - Auburndale EDS vol 1 & 2 | ||
58 | * - CDI 401376 - Ibex Peak EDS | ||
59 | * - ref 26037, 26641 - IPS BIOS spec | ||
60 | * - ref 26489 - Nehalem BIOS writer's guide | ||
61 | * - ref 26921 - Ibex Peak BIOS Specification | ||
62 | */ | ||
63 | |||
64 | #include <linux/debugfs.h> | ||
65 | #include <linux/delay.h> | ||
66 | #include <linux/interrupt.h> | ||
67 | #include <linux/kernel.h> | ||
68 | #include <linux/kthread.h> | ||
69 | #include <linux/module.h> | ||
70 | #include <linux/pci.h> | ||
71 | #include <linux/sched.h> | ||
72 | #include <linux/seq_file.h> | ||
73 | #include <linux/string.h> | ||
74 | #include <linux/tick.h> | ||
75 | #include <linux/timer.h> | ||
76 | #include <drm/i915_drm.h> | ||
77 | #include <asm/msr.h> | ||
78 | #include <asm/processor.h> | ||
79 | |||
80 | #define PCI_DEVICE_ID_INTEL_THERMAL_SENSOR 0x3b32 | ||
81 | |||
82 | /* | ||
83 | * Package level MSRs for monitor/control | ||
84 | */ | ||
85 | #define PLATFORM_INFO 0xce | ||
86 | #define PLATFORM_TDP (1<<29) | ||
87 | #define PLATFORM_RATIO (1<<28) | ||
88 | |||
89 | #define IA32_MISC_ENABLE 0x1a0 | ||
90 | #define IA32_MISC_TURBO_EN (1ULL<<38) | ||
91 | |||
92 | #define TURBO_POWER_CURRENT_LIMIT 0x1ac | ||
93 | #define TURBO_TDC_OVR_EN (1UL<<31) | ||
94 | #define TURBO_TDC_MASK (0x000000007fff0000UL) | ||
95 | #define TURBO_TDC_SHIFT (16) | ||
96 | #define TURBO_TDP_OVR_EN (1UL<<15) | ||
97 | #define TURBO_TDP_MASK (0x0000000000003fffUL) | ||
98 | |||
99 | /* | ||
100 | * Core/thread MSRs for monitoring | ||
101 | */ | ||
102 | #define IA32_PERF_CTL 0x199 | ||
103 | #define IA32_PERF_TURBO_DIS (1ULL<<32) | ||
104 | |||
105 | /* | ||
106 | * Thermal PCI device regs | ||
107 | */ | ||
108 | #define THM_CFG_TBAR 0x10 | ||
109 | #define THM_CFG_TBAR_HI 0x14 | ||
110 | |||
111 | #define THM_TSIU 0x00 | ||
112 | #define THM_TSE 0x01 | ||
113 | #define TSE_EN 0xb8 | ||
114 | #define THM_TSS 0x02 | ||
115 | #define THM_TSTR 0x03 | ||
116 | #define THM_TSTTP 0x04 | ||
117 | #define THM_TSCO 0x08 | ||
118 | #define THM_TSES 0x0c | ||
119 | #define THM_TSGPEN 0x0d | ||
120 | #define TSGPEN_HOT_LOHI (1<<1) | ||
121 | #define TSGPEN_CRIT_LOHI (1<<2) | ||
122 | #define THM_TSPC 0x0e | ||
123 | #define THM_PPEC 0x10 | ||
124 | #define THM_CTA 0x12 | ||
125 | #define THM_PTA 0x14 | ||
126 | #define PTA_SLOPE_MASK (0xff00) | ||
127 | #define PTA_SLOPE_SHIFT 8 | ||
128 | #define PTA_OFFSET_MASK (0x00ff) | ||
129 | #define THM_MGTA 0x16 | ||
130 | #define MGTA_SLOPE_MASK (0xff00) | ||
131 | #define MGTA_SLOPE_SHIFT 8 | ||
132 | #define MGTA_OFFSET_MASK (0x00ff) | ||
133 | #define THM_TRC 0x1a | ||
134 | #define TRC_CORE2_EN (1<<15) | ||
135 | #define TRC_THM_EN (1<<12) | ||
136 | #define TRC_C6_WAR (1<<8) | ||
137 | #define TRC_CORE1_EN (1<<7) | ||
138 | #define TRC_CORE_PWR (1<<6) | ||
139 | #define TRC_PCH_EN (1<<5) | ||
140 | #define TRC_MCH_EN (1<<4) | ||
141 | #define TRC_DIMM4 (1<<3) | ||
142 | #define TRC_DIMM3 (1<<2) | ||
143 | #define TRC_DIMM2 (1<<1) | ||
144 | #define TRC_DIMM1 (1<<0) | ||
145 | #define THM_TES 0x20 | ||
146 | #define THM_TEN 0x21 | ||
147 | #define TEN_UPDATE_EN 1 | ||
148 | #define THM_PSC 0x24 | ||
149 | #define PSC_NTG (1<<0) /* No GFX turbo support */ | ||
150 | #define PSC_NTPC (1<<1) /* No CPU turbo support */ | ||
151 | #define PSC_PP_DEF (0<<2) /* Perf policy up to driver */ | ||
152 | #define PSP_PP_PC (1<<2) /* BIOS prefers CPU perf */ | ||
153 | #define PSP_PP_BAL (2<<2) /* BIOS wants balanced perf */ | ||
154 | #define PSP_PP_GFX (3<<2) /* BIOS prefers GFX perf */ | ||
155 | #define PSP_PBRT (1<<4) /* BIOS run time support */ | ||
156 | #define THM_CTV1 0x30 | ||
157 | #define CTV_TEMP_ERROR (1<<15) | ||
158 | #define CTV_TEMP_MASK 0x3f | ||
159 | #define CTV_ | ||
160 | #define THM_CTV2 0x32 | ||
161 | #define THM_CEC 0x34 /* undocumented power accumulator in joules */ | ||
162 | #define THM_AE 0x3f | ||
163 | #define THM_HTS 0x50 /* 32 bits */ | ||
164 | #define HTS_PCPL_MASK (0x7fe00000) | ||
165 | #define HTS_PCPL_SHIFT 21 | ||
166 | #define HTS_GPL_MASK (0x001ff000) | ||
167 | #define HTS_GPL_SHIFT 12 | ||
168 | #define HTS_PP_MASK (0x00000c00) | ||
169 | #define HTS_PP_SHIFT 10 | ||
170 | #define HTS_PP_DEF 0 | ||
171 | #define HTS_PP_PROC 1 | ||
172 | #define HTS_PP_BAL 2 | ||
173 | #define HTS_PP_GFX 3 | ||
174 | #define HTS_PCTD_DIS (1<<9) | ||
175 | #define HTS_GTD_DIS (1<<8) | ||
176 | #define HTS_PTL_MASK (0x000000fe) | ||
177 | #define HTS_PTL_SHIFT 1 | ||
178 | #define HTS_NVV (1<<0) | ||
179 | #define THM_HTSHI 0x54 /* 16 bits */ | ||
180 | #define HTS2_PPL_MASK (0x03ff) | ||
181 | #define HTS2_PRST_MASK (0x3c00) | ||
182 | #define HTS2_PRST_SHIFT 10 | ||
183 | #define HTS2_PRST_UNLOADED 0 | ||
184 | #define HTS2_PRST_RUNNING 1 | ||
185 | #define HTS2_PRST_TDISOP 2 /* turbo disabled due to power */ | ||
186 | #define HTS2_PRST_TDISHT 3 /* turbo disabled due to high temp */ | ||
187 | #define HTS2_PRST_TDISUSR 4 /* user disabled turbo */ | ||
188 | #define HTS2_PRST_TDISPLAT 5 /* platform disabled turbo */ | ||
189 | #define HTS2_PRST_TDISPM 6 /* power management disabled turbo */ | ||
190 | #define HTS2_PRST_TDISERR 7 /* some kind of error disabled turbo */ | ||
191 | #define THM_PTL 0x56 | ||
192 | #define THM_MGTV 0x58 | ||
193 | #define TV_MASK 0x000000000000ff00 | ||
194 | #define TV_SHIFT 8 | ||
195 | #define THM_PTV 0x60 | ||
196 | #define PTV_MASK 0x00ff | ||
197 | #define THM_MMGPC 0x64 | ||
198 | #define THM_MPPC 0x66 | ||
199 | #define THM_MPCPC 0x68 | ||
200 | #define THM_TSPIEN 0x82 | ||
201 | #define TSPIEN_AUX_LOHI (1<<0) | ||
202 | #define TSPIEN_HOT_LOHI (1<<1) | ||
203 | #define TSPIEN_CRIT_LOHI (1<<2) | ||
204 | #define TSPIEN_AUX2_LOHI (1<<3) | ||
205 | #define THM_TSLOCK 0x83 | ||
206 | #define THM_ATR 0x84 | ||
207 | #define THM_TOF 0x87 | ||
208 | #define THM_STS 0x98 | ||
209 | #define STS_PCPL_MASK (0x7fe00000) | ||
210 | #define STS_PCPL_SHIFT 21 | ||
211 | #define STS_GPL_MASK (0x001ff000) | ||
212 | #define STS_GPL_SHIFT 12 | ||
213 | #define STS_PP_MASK (0x00000c00) | ||
214 | #define STS_PP_SHIFT 10 | ||
215 | #define STS_PP_DEF 0 | ||
216 | #define STS_PP_PROC 1 | ||
217 | #define STS_PP_BAL 2 | ||
218 | #define STS_PP_GFX 3 | ||
219 | #define STS_PCTD_DIS (1<<9) | ||
220 | #define STS_GTD_DIS (1<<8) | ||
221 | #define STS_PTL_MASK (0x000000fe) | ||
222 | #define STS_PTL_SHIFT 1 | ||
223 | #define STS_NVV (1<<0) | ||
224 | #define THM_SEC 0x9c | ||
225 | #define SEC_ACK (1<<0) | ||
226 | #define THM_TC3 0xa4 | ||
227 | #define THM_TC1 0xa8 | ||
228 | #define STS_PPL_MASK (0x0003ff00) | ||
229 | #define STS_PPL_SHIFT 16 | ||
230 | #define THM_TC2 0xac | ||
231 | #define THM_DTV 0xb0 | ||
232 | #define THM_ITV 0xd8 | ||
233 | #define ITV_ME_SEQNO_MASK 0x000f0000 /* ME should update every ~200ms */ | ||
234 | #define ITV_ME_SEQNO_SHIFT (16) | ||
235 | #define ITV_MCH_TEMP_MASK 0x0000ff00 | ||
236 | #define ITV_MCH_TEMP_SHIFT (8) | ||
237 | #define ITV_PCH_TEMP_MASK 0x000000ff | ||
238 | |||
239 | #define thm_readb(off) readb(ips->regmap + (off)) | ||
240 | #define thm_readw(off) readw(ips->regmap + (off)) | ||
241 | #define thm_readl(off) readl(ips->regmap + (off)) | ||
242 | #define thm_readq(off) readq(ips->regmap + (off)) | ||
243 | |||
244 | #define thm_writeb(off, val) writeb((val), ips->regmap + (off)) | ||
245 | #define thm_writew(off, val) writew((val), ips->regmap + (off)) | ||
246 | #define thm_writel(off, val) writel((val), ips->regmap + (off)) | ||
247 | |||
248 | static const int IPS_ADJUST_PERIOD = 5000; /* ms */ | ||
249 | |||
250 | /* For initial average collection */ | ||
251 | static const int IPS_SAMPLE_PERIOD = 200; /* ms */ | ||
252 | static const int IPS_SAMPLE_WINDOW = 5000; /* 5s moving window of samples */ | ||
253 | #define IPS_SAMPLE_COUNT (IPS_SAMPLE_WINDOW / IPS_SAMPLE_PERIOD) | ||
254 | |||
255 | /* Per-SKU limits */ | ||
256 | struct ips_mcp_limits { | ||
257 | int cpu_family; | ||
258 | int cpu_model; /* includes extended model... */ | ||
259 | int mcp_power_limit; /* mW units */ | ||
260 | int core_power_limit; | ||
261 | int mch_power_limit; | ||
262 | int core_temp_limit; /* degrees C */ | ||
263 | int mch_temp_limit; | ||
264 | }; | ||
265 | |||
266 | /* Max temps are -10 degrees C to avoid PROCHOT# */ | ||
267 | |||
268 | struct ips_mcp_limits ips_sv_limits = { | ||
269 | .mcp_power_limit = 35000, | ||
270 | .core_power_limit = 29000, | ||
271 | .mch_power_limit = 20000, | ||
272 | .core_temp_limit = 95, | ||
273 | .mch_temp_limit = 90 | ||
274 | }; | ||
275 | |||
276 | struct ips_mcp_limits ips_lv_limits = { | ||
277 | .mcp_power_limit = 25000, | ||
278 | .core_power_limit = 21000, | ||
279 | .mch_power_limit = 13000, | ||
280 | .core_temp_limit = 95, | ||
281 | .mch_temp_limit = 90 | ||
282 | }; | ||
283 | |||
284 | struct ips_mcp_limits ips_ulv_limits = { | ||
285 | .mcp_power_limit = 18000, | ||
286 | .core_power_limit = 14000, | ||
287 | .mch_power_limit = 11000, | ||
288 | .core_temp_limit = 95, | ||
289 | .mch_temp_limit = 90 | ||
290 | }; | ||
291 | |||
292 | struct ips_driver { | ||
293 | struct pci_dev *dev; | ||
294 | void *regmap; | ||
295 | struct task_struct *monitor; | ||
296 | struct task_struct *adjust; | ||
297 | struct dentry *debug_root; | ||
298 | |||
299 | /* Average CPU core temps (all averages in .01 degrees C for precision) */ | ||
300 | u16 ctv1_avg_temp; | ||
301 | u16 ctv2_avg_temp; | ||
302 | /* GMCH average */ | ||
303 | u16 mch_avg_temp; | ||
304 | /* Average for the CPU (both cores?) */ | ||
305 | u16 mcp_avg_temp; | ||
306 | /* Average power consumption (in mW) */ | ||
307 | u32 cpu_avg_power; | ||
308 | u32 mch_avg_power; | ||
309 | |||
310 | /* Offset values */ | ||
311 | u16 cta_val; | ||
312 | u16 pta_val; | ||
313 | u16 mgta_val; | ||
314 | |||
315 | /* Maximums & prefs, protected by turbo status lock */ | ||
316 | spinlock_t turbo_status_lock; | ||
317 | u16 mcp_temp_limit; | ||
318 | u16 mcp_power_limit; | ||
319 | u16 core_power_limit; | ||
320 | u16 mch_power_limit; | ||
321 | bool cpu_turbo_enabled; | ||
322 | bool __cpu_turbo_on; | ||
323 | bool gpu_turbo_enabled; | ||
324 | bool __gpu_turbo_on; | ||
325 | bool gpu_preferred; | ||
326 | bool poll_turbo_status; | ||
327 | bool second_cpu; | ||
328 | struct ips_mcp_limits *limits; | ||
329 | |||
330 | /* Optional MCH interfaces for if i915 is in use */ | ||
331 | unsigned long (*read_mch_val)(void); | ||
332 | bool (*gpu_raise)(void); | ||
333 | bool (*gpu_lower)(void); | ||
334 | bool (*gpu_busy)(void); | ||
335 | bool (*gpu_turbo_disable)(void); | ||
336 | |||
337 | /* For restoration at unload */ | ||
338 | u64 orig_turbo_limit; | ||
339 | u64 orig_turbo_ratios; | ||
340 | }; | ||
341 | |||
342 | /** | ||
343 | * ips_cpu_busy - is CPU busy? | ||
344 | * @ips: IPS driver struct | ||
345 | * | ||
346 | * Check CPU for load to see whether we should increase its thermal budget. | ||
347 | * | ||
348 | * RETURNS: | ||
349 | * True if the CPU could use more power, false otherwise. | ||
350 | */ | ||
351 | static bool ips_cpu_busy(struct ips_driver *ips) | ||
352 | { | ||
353 | if ((avenrun[0] >> FSHIFT) > 1) | ||
354 | return true; | ||
355 | |||
356 | return false; | ||
357 | } | ||
358 | |||
359 | /** | ||
360 | * ips_cpu_raise - raise CPU power clamp | ||
361 | * @ips: IPS driver struct | ||
362 | * | ||
363 | * Raise the CPU power clamp by %IPS_CPU_STEP, in accordance with TDP for | ||
364 | * this platform. | ||
365 | * | ||
366 | * We do this by adjusting the TURBO_POWER_CURRENT_LIMIT MSR upwards (as | ||
367 | * long as we haven't hit the TDP limit for the SKU). | ||
368 | */ | ||
369 | static void ips_cpu_raise(struct ips_driver *ips) | ||
370 | { | ||
371 | u64 turbo_override; | ||
372 | u16 cur_tdp_limit, new_tdp_limit; | ||
373 | |||
374 | if (!ips->cpu_turbo_enabled) | ||
375 | return; | ||
376 | |||
377 | rdmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override); | ||
378 | |||
379 | cur_tdp_limit = turbo_override & TURBO_TDP_MASK; | ||
380 | new_tdp_limit = cur_tdp_limit + 8; /* 1W increase */ | ||
381 | |||
382 | /* Clamp to SKU TDP limit */ | ||
383 | if (((new_tdp_limit * 10) / 8) > ips->core_power_limit) | ||
384 | new_tdp_limit = cur_tdp_limit; | ||
385 | |||
386 | thm_writew(THM_MPCPC, (new_tdp_limit * 10) / 8); | ||
387 | |||
388 | turbo_override |= TURBO_TDC_OVR_EN | TURBO_TDC_OVR_EN; | ||
389 | wrmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override); | ||
390 | |||
391 | turbo_override &= ~TURBO_TDP_MASK; | ||
392 | turbo_override |= new_tdp_limit; | ||
393 | |||
394 | wrmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override); | ||
395 | } | ||
396 | |||
397 | /** | ||
398 | * ips_cpu_lower - lower CPU power clamp | ||
399 | * @ips: IPS driver struct | ||
400 | * | ||
401 | * Lower CPU power clamp b %IPS_CPU_STEP if possible. | ||
402 | * | ||
403 | * We do this by adjusting the TURBO_POWER_CURRENT_LIMIT MSR down, going | ||
404 | * as low as the platform limits will allow (though we could go lower there | ||
405 | * wouldn't be much point). | ||
406 | */ | ||
407 | static void ips_cpu_lower(struct ips_driver *ips) | ||
408 | { | ||
409 | u64 turbo_override; | ||
410 | u16 cur_limit, new_limit; | ||
411 | |||
412 | rdmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override); | ||
413 | |||
414 | cur_limit = turbo_override & TURBO_TDP_MASK; | ||
415 | new_limit = cur_limit - 8; /* 1W decrease */ | ||
416 | |||
417 | /* Clamp to SKU TDP limit */ | ||
418 | if (((new_limit * 10) / 8) < (ips->orig_turbo_limit & TURBO_TDP_MASK)) | ||
419 | new_limit = ips->orig_turbo_limit & TURBO_TDP_MASK; | ||
420 | |||
421 | thm_writew(THM_MPCPC, (new_limit * 10) / 8); | ||
422 | |||
423 | turbo_override |= TURBO_TDC_OVR_EN | TURBO_TDC_OVR_EN; | ||
424 | wrmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override); | ||
425 | |||
426 | turbo_override &= ~TURBO_TDP_MASK; | ||
427 | turbo_override |= new_limit; | ||
428 | |||
429 | wrmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override); | ||
430 | } | ||
431 | |||
432 | /** | ||
433 | * do_enable_cpu_turbo - internal turbo enable function | ||
434 | * @data: unused | ||
435 | * | ||
436 | * Internal function for actually updating MSRs. When we enable/disable | ||
437 | * turbo, we need to do it on each CPU; this function is the one called | ||
438 | * by on_each_cpu() when needed. | ||
439 | */ | ||
440 | static void do_enable_cpu_turbo(void *data) | ||
441 | { | ||
442 | u64 perf_ctl; | ||
443 | |||
444 | rdmsrl(IA32_PERF_CTL, perf_ctl); | ||
445 | if (perf_ctl & IA32_PERF_TURBO_DIS) { | ||
446 | perf_ctl &= ~IA32_PERF_TURBO_DIS; | ||
447 | wrmsrl(IA32_PERF_CTL, perf_ctl); | ||
448 | } | ||
449 | } | ||
450 | |||
451 | /** | ||
452 | * ips_enable_cpu_turbo - enable turbo mode on all CPUs | ||
453 | * @ips: IPS driver struct | ||
454 | * | ||
455 | * Enable turbo mode by clearing the disable bit in IA32_PERF_CTL on | ||
456 | * all logical threads. | ||
457 | */ | ||
458 | static void ips_enable_cpu_turbo(struct ips_driver *ips) | ||
459 | { | ||
460 | /* Already on, no need to mess with MSRs */ | ||
461 | if (ips->__cpu_turbo_on) | ||
462 | return; | ||
463 | |||
464 | on_each_cpu(do_enable_cpu_turbo, ips, 1); | ||
465 | |||
466 | ips->__cpu_turbo_on = true; | ||
467 | } | ||
468 | |||
469 | /** | ||
470 | * do_disable_cpu_turbo - internal turbo disable function | ||
471 | * @data: unused | ||
472 | * | ||
473 | * Internal function for actually updating MSRs. When we enable/disable | ||
474 | * turbo, we need to do it on each CPU; this function is the one called | ||
475 | * by on_each_cpu() when needed. | ||
476 | */ | ||
477 | static void do_disable_cpu_turbo(void *data) | ||
478 | { | ||
479 | u64 perf_ctl; | ||
480 | |||
481 | rdmsrl(IA32_PERF_CTL, perf_ctl); | ||
482 | if (!(perf_ctl & IA32_PERF_TURBO_DIS)) { | ||
483 | perf_ctl |= IA32_PERF_TURBO_DIS; | ||
484 | wrmsrl(IA32_PERF_CTL, perf_ctl); | ||
485 | } | ||
486 | } | ||
487 | |||
488 | /** | ||
489 | * ips_disable_cpu_turbo - disable turbo mode on all CPUs | ||
490 | * @ips: IPS driver struct | ||
491 | * | ||
492 | * Disable turbo mode by setting the disable bit in IA32_PERF_CTL on | ||
493 | * all logical threads. | ||
494 | */ | ||
495 | static void ips_disable_cpu_turbo(struct ips_driver *ips) | ||
496 | { | ||
497 | /* Already off, leave it */ | ||
498 | if (!ips->__cpu_turbo_on) | ||
499 | return; | ||
500 | |||
501 | on_each_cpu(do_disable_cpu_turbo, ips, 1); | ||
502 | |||
503 | ips->__cpu_turbo_on = false; | ||
504 | } | ||
505 | |||
506 | /** | ||
507 | * ips_gpu_busy - is GPU busy? | ||
508 | * @ips: IPS driver struct | ||
509 | * | ||
510 | * Check GPU for load to see whether we should increase its thermal budget. | ||
511 | * We need to call into the i915 driver in this case. | ||
512 | * | ||
513 | * RETURNS: | ||
514 | * True if the GPU could use more power, false otherwise. | ||
515 | */ | ||
516 | static bool ips_gpu_busy(struct ips_driver *ips) | ||
517 | { | ||
518 | if (!ips->gpu_turbo_enabled) | ||
519 | return false; | ||
520 | |||
521 | return ips->gpu_busy(); | ||
522 | } | ||
523 | |||
524 | /** | ||
525 | * ips_gpu_raise - raise GPU power clamp | ||
526 | * @ips: IPS driver struct | ||
527 | * | ||
528 | * Raise the GPU frequency/power if possible. We need to call into the | ||
529 | * i915 driver in this case. | ||
530 | */ | ||
531 | static void ips_gpu_raise(struct ips_driver *ips) | ||
532 | { | ||
533 | if (!ips->gpu_turbo_enabled) | ||
534 | return; | ||
535 | |||
536 | if (!ips->gpu_raise()) | ||
537 | ips->gpu_turbo_enabled = false; | ||
538 | |||
539 | return; | ||
540 | } | ||
541 | |||
542 | /** | ||
543 | * ips_gpu_lower - lower GPU power clamp | ||
544 | * @ips: IPS driver struct | ||
545 | * | ||
546 | * Lower GPU frequency/power if possible. Need to call i915. | ||
547 | */ | ||
548 | static void ips_gpu_lower(struct ips_driver *ips) | ||
549 | { | ||
550 | if (!ips->gpu_turbo_enabled) | ||
551 | return; | ||
552 | |||
553 | if (!ips->gpu_lower()) | ||
554 | ips->gpu_turbo_enabled = false; | ||
555 | |||
556 | return; | ||
557 | } | ||
558 | |||
559 | /** | ||
560 | * ips_enable_gpu_turbo - notify the gfx driver turbo is available | ||
561 | * @ips: IPS driver struct | ||
562 | * | ||
563 | * Call into the graphics driver indicating that it can safely use | ||
564 | * turbo mode. | ||
565 | */ | ||
566 | static void ips_enable_gpu_turbo(struct ips_driver *ips) | ||
567 | { | ||
568 | if (ips->__gpu_turbo_on) | ||
569 | return; | ||
570 | ips->__gpu_turbo_on = true; | ||
571 | } | ||
572 | |||
573 | /** | ||
574 | * ips_disable_gpu_turbo - notify the gfx driver to disable turbo mode | ||
575 | * @ips: IPS driver struct | ||
576 | * | ||
577 | * Request that the graphics driver disable turbo mode. | ||
578 | */ | ||
579 | static void ips_disable_gpu_turbo(struct ips_driver *ips) | ||
580 | { | ||
581 | /* Avoid calling i915 if turbo is already disabled */ | ||
582 | if (!ips->__gpu_turbo_on) | ||
583 | return; | ||
584 | |||
585 | if (!ips->gpu_turbo_disable()) | ||
586 | dev_err(&ips->dev->dev, "failed to disable graphis turbo\n"); | ||
587 | else | ||
588 | ips->__gpu_turbo_on = false; | ||
589 | } | ||
590 | |||
591 | /** | ||
592 | * mcp_exceeded - check whether we're outside our thermal & power limits | ||
593 | * @ips: IPS driver struct | ||
594 | * | ||
595 | * Check whether the MCP is over its thermal or power budget. | ||
596 | */ | ||
597 | static bool mcp_exceeded(struct ips_driver *ips) | ||
598 | { | ||
599 | unsigned long flags; | ||
600 | bool ret = false; | ||
601 | |||
602 | spin_lock_irqsave(&ips->turbo_status_lock, flags); | ||
603 | if (ips->mcp_avg_temp > (ips->mcp_temp_limit * 100)) | ||
604 | ret = true; | ||
605 | if (ips->cpu_avg_power + ips->mch_avg_power > ips->mcp_power_limit) | ||
606 | ret = true; | ||
607 | spin_unlock_irqrestore(&ips->turbo_status_lock, flags); | ||
608 | |||
609 | if (ret) | ||
610 | dev_info(&ips->dev->dev, | ||
611 | "MCP power or thermal limit exceeded\n"); | ||
612 | |||
613 | return ret; | ||
614 | } | ||
615 | |||
616 | /** | ||
617 | * cpu_exceeded - check whether a CPU core is outside its limits | ||
618 | * @ips: IPS driver struct | ||
619 | * @cpu: CPU number to check | ||
620 | * | ||
621 | * Check a given CPU's average temp or power is over its limit. | ||
622 | */ | ||
623 | static bool cpu_exceeded(struct ips_driver *ips, int cpu) | ||
624 | { | ||
625 | unsigned long flags; | ||
626 | int avg; | ||
627 | bool ret = false; | ||
628 | |||
629 | spin_lock_irqsave(&ips->turbo_status_lock, flags); | ||
630 | avg = cpu ? ips->ctv2_avg_temp : ips->ctv1_avg_temp; | ||
631 | if (avg > (ips->limits->core_temp_limit * 100)) | ||
632 | ret = true; | ||
633 | if (ips->cpu_avg_power > ips->core_power_limit * 100) | ||
634 | ret = true; | ||
635 | spin_unlock_irqrestore(&ips->turbo_status_lock, flags); | ||
636 | |||
637 | if (ret) | ||
638 | dev_info(&ips->dev->dev, | ||
639 | "CPU power or thermal limit exceeded\n"); | ||
640 | |||
641 | return ret; | ||
642 | } | ||
643 | |||
644 | /** | ||
645 | * mch_exceeded - check whether the GPU is over budget | ||
646 | * @ips: IPS driver struct | ||
647 | * | ||
648 | * Check the MCH temp & power against their maximums. | ||
649 | */ | ||
650 | static bool mch_exceeded(struct ips_driver *ips) | ||
651 | { | ||
652 | unsigned long flags; | ||
653 | bool ret = false; | ||
654 | |||
655 | spin_lock_irqsave(&ips->turbo_status_lock, flags); | ||
656 | if (ips->mch_avg_temp > (ips->limits->mch_temp_limit * 100)) | ||
657 | ret = true; | ||
658 | if (ips->mch_avg_power > ips->mch_power_limit) | ||
659 | ret = true; | ||
660 | spin_unlock_irqrestore(&ips->turbo_status_lock, flags); | ||
661 | |||
662 | return ret; | ||
663 | } | ||
664 | |||
665 | /** | ||
666 | * update_turbo_limits - get various limits & settings from regs | ||
667 | * @ips: IPS driver struct | ||
668 | * | ||
669 | * Update the IPS power & temp limits, along with turbo enable flags, | ||
670 | * based on latest register contents. | ||
671 | * | ||
672 | * Used at init time and for runtime BIOS support, which requires polling | ||
673 | * the regs for updates (as a result of AC->DC transition for example). | ||
674 | * | ||
675 | * LOCKING: | ||
676 | * Caller must hold turbo_status_lock (outside of init) | ||
677 | */ | ||
678 | static void update_turbo_limits(struct ips_driver *ips) | ||
679 | { | ||
680 | u32 hts = thm_readl(THM_HTS); | ||
681 | |||
682 | ips->cpu_turbo_enabled = !(hts & HTS_PCTD_DIS); | ||
683 | ips->gpu_turbo_enabled = !(hts & HTS_GTD_DIS); | ||
684 | ips->core_power_limit = thm_readw(THM_MPCPC); | ||
685 | ips->mch_power_limit = thm_readw(THM_MMGPC); | ||
686 | ips->mcp_temp_limit = thm_readw(THM_PTL); | ||
687 | ips->mcp_power_limit = thm_readw(THM_MPPC); | ||
688 | |||
689 | /* Ignore BIOS CPU vs GPU pref */ | ||
690 | } | ||
691 | |||
692 | /** | ||
693 | * ips_adjust - adjust power clamp based on thermal state | ||
694 | * @data: ips driver structure | ||
695 | * | ||
696 | * Wake up every 5s or so and check whether we should adjust the power clamp. | ||
697 | * Check CPU and GPU load to determine which needs adjustment. There are | ||
698 | * several things to consider here: | ||
699 | * - do we need to adjust up or down? | ||
700 | * - is CPU busy? | ||
701 | * - is GPU busy? | ||
702 | * - is CPU in turbo? | ||
703 | * - is GPU in turbo? | ||
704 | * - is CPU or GPU preferred? (CPU is default) | ||
705 | * | ||
706 | * So, given the above, we do the following: | ||
707 | * - up (TDP available) | ||
708 | * - CPU not busy, GPU not busy - nothing | ||
709 | * - CPU busy, GPU not busy - adjust CPU up | ||
710 | * - CPU not busy, GPU busy - adjust GPU up | ||
711 | * - CPU busy, GPU busy - adjust preferred unit up, taking headroom from | ||
712 | * non-preferred unit if necessary | ||
713 | * - down (at TDP limit) | ||
714 | * - adjust both CPU and GPU down if possible | ||
715 | * | ||
716 | cpu+ gpu+ cpu+gpu- cpu-gpu+ cpu-gpu- | ||
717 | cpu < gpu < cpu+gpu+ cpu+ gpu+ nothing | ||
718 | cpu < gpu >= cpu+gpu-(mcp<) cpu+gpu-(mcp<) gpu- gpu- | ||
719 | cpu >= gpu < cpu-gpu+(mcp<) cpu- cpu-gpu+(mcp<) cpu- | ||
720 | cpu >= gpu >= cpu-gpu- cpu-gpu- cpu-gpu- cpu-gpu- | ||
721 | * | ||
722 | */ | ||
723 | static int ips_adjust(void *data) | ||
724 | { | ||
725 | struct ips_driver *ips = data; | ||
726 | unsigned long flags; | ||
727 | |||
728 | dev_dbg(&ips->dev->dev, "starting ips-adjust thread\n"); | ||
729 | |||
730 | /* | ||
731 | * Adjust CPU and GPU clamps every 5s if needed. Doing it more | ||
732 | * often isn't recommended due to ME interaction. | ||
733 | */ | ||
734 | do { | ||
735 | bool cpu_busy = ips_cpu_busy(ips); | ||
736 | bool gpu_busy = ips_gpu_busy(ips); | ||
737 | |||
738 | spin_lock_irqsave(&ips->turbo_status_lock, flags); | ||
739 | if (ips->poll_turbo_status) | ||
740 | update_turbo_limits(ips); | ||
741 | spin_unlock_irqrestore(&ips->turbo_status_lock, flags); | ||
742 | |||
743 | /* Update turbo status if necessary */ | ||
744 | if (ips->cpu_turbo_enabled) | ||
745 | ips_enable_cpu_turbo(ips); | ||
746 | else | ||
747 | ips_disable_cpu_turbo(ips); | ||
748 | |||
749 | if (ips->gpu_turbo_enabled) | ||
750 | ips_enable_gpu_turbo(ips); | ||
751 | else | ||
752 | ips_disable_gpu_turbo(ips); | ||
753 | |||
754 | /* We're outside our comfort zone, crank them down */ | ||
755 | if (mcp_exceeded(ips)) { | ||
756 | ips_cpu_lower(ips); | ||
757 | ips_gpu_lower(ips); | ||
758 | goto sleep; | ||
759 | } | ||
760 | |||
761 | if (!cpu_exceeded(ips, 0) && cpu_busy) | ||
762 | ips_cpu_raise(ips); | ||
763 | else | ||
764 | ips_cpu_lower(ips); | ||
765 | |||
766 | if (!mch_exceeded(ips) && gpu_busy) | ||
767 | ips_gpu_raise(ips); | ||
768 | else | ||
769 | ips_gpu_lower(ips); | ||
770 | |||
771 | sleep: | ||
772 | schedule_timeout_interruptible(msecs_to_jiffies(IPS_ADJUST_PERIOD)); | ||
773 | } while (!kthread_should_stop()); | ||
774 | |||
775 | dev_dbg(&ips->dev->dev, "ips-adjust thread stopped\n"); | ||
776 | |||
777 | return 0; | ||
778 | } | ||
779 | |||
780 | /* | ||
781 | * Helpers for reading out temp/power values and calculating their | ||
782 | * averages for the decision making and monitoring functions. | ||
783 | */ | ||
784 | |||
785 | static u16 calc_avg_temp(struct ips_driver *ips, u16 *array) | ||
786 | { | ||
787 | u64 total = 0; | ||
788 | int i; | ||
789 | u16 avg; | ||
790 | |||
791 | for (i = 0; i < IPS_SAMPLE_COUNT; i++) | ||
792 | total += (u64)(array[i] * 100); | ||
793 | |||
794 | do_div(total, IPS_SAMPLE_COUNT); | ||
795 | |||
796 | avg = (u16)total; | ||
797 | |||
798 | return avg; | ||
799 | } | ||
800 | |||
801 | static u16 read_mgtv(struct ips_driver *ips) | ||
802 | { | ||
803 | u16 ret; | ||
804 | u64 slope, offset; | ||
805 | u64 val; | ||
806 | |||
807 | val = thm_readq(THM_MGTV); | ||
808 | val = (val & TV_MASK) >> TV_SHIFT; | ||
809 | |||
810 | slope = offset = thm_readw(THM_MGTA); | ||
811 | slope = (slope & MGTA_SLOPE_MASK) >> MGTA_SLOPE_SHIFT; | ||
812 | offset = offset & MGTA_OFFSET_MASK; | ||
813 | |||
814 | ret = ((val * slope + 0x40) >> 7) + offset; | ||
815 | |||
816 | return 0; /* MCH temp reporting buggy */ | ||
817 | } | ||
818 | |||
819 | static u16 read_ptv(struct ips_driver *ips) | ||
820 | { | ||
821 | u16 val, slope, offset; | ||
822 | |||
823 | slope = (ips->pta_val & PTA_SLOPE_MASK) >> PTA_SLOPE_SHIFT; | ||
824 | offset = ips->pta_val & PTA_OFFSET_MASK; | ||
825 | |||
826 | val = thm_readw(THM_PTV) & PTV_MASK; | ||
827 | |||
828 | return val; | ||
829 | } | ||
830 | |||
831 | static u16 read_ctv(struct ips_driver *ips, int cpu) | ||
832 | { | ||
833 | int reg = cpu ? THM_CTV2 : THM_CTV1; | ||
834 | u16 val; | ||
835 | |||
836 | val = thm_readw(reg); | ||
837 | if (!(val & CTV_TEMP_ERROR)) | ||
838 | val = (val) >> 6; /* discard fractional component */ | ||
839 | else | ||
840 | val = 0; | ||
841 | |||
842 | return val; | ||
843 | } | ||
844 | |||
845 | static u32 get_cpu_power(struct ips_driver *ips, u32 *last, int period) | ||
846 | { | ||
847 | u32 val; | ||
848 | u32 ret; | ||
849 | |||
850 | /* | ||
851 | * CEC is in joules/65535. Take difference over time to | ||
852 | * get watts. | ||
853 | */ | ||
854 | val = thm_readl(THM_CEC); | ||
855 | |||
856 | /* period is in ms and we want mW */ | ||
857 | ret = (((val - *last) * 1000) / period); | ||
858 | ret = (ret * 1000) / 65535; | ||
859 | *last = val; | ||
860 | |||
861 | return ret; | ||
862 | } | ||
863 | |||
864 | static const u16 temp_decay_factor = 2; | ||
865 | static u16 update_average_temp(u16 avg, u16 val) | ||
866 | { | ||
867 | u16 ret; | ||
868 | |||
869 | /* Multiply by 100 for extra precision */ | ||
870 | ret = (val * 100 / temp_decay_factor) + | ||
871 | (((temp_decay_factor - 1) * avg) / temp_decay_factor); | ||
872 | return ret; | ||
873 | } | ||
874 | |||
875 | static const u16 power_decay_factor = 2; | ||
876 | static u16 update_average_power(u32 avg, u32 val) | ||
877 | { | ||
878 | u32 ret; | ||
879 | |||
880 | ret = (val / power_decay_factor) + | ||
881 | (((power_decay_factor - 1) * avg) / power_decay_factor); | ||
882 | |||
883 | return ret; | ||
884 | } | ||
885 | |||
886 | static u32 calc_avg_power(struct ips_driver *ips, u32 *array) | ||
887 | { | ||
888 | u64 total = 0; | ||
889 | u32 avg; | ||
890 | int i; | ||
891 | |||
892 | for (i = 0; i < IPS_SAMPLE_COUNT; i++) | ||
893 | total += array[i]; | ||
894 | |||
895 | do_div(total, IPS_SAMPLE_COUNT); | ||
896 | avg = (u32)total; | ||
897 | |||
898 | return avg; | ||
899 | } | ||
900 | |||
901 | static void monitor_timeout(unsigned long arg) | ||
902 | { | ||
903 | wake_up_process((struct task_struct *)arg); | ||
904 | } | ||
905 | |||
906 | /** | ||
907 | * ips_monitor - temp/power monitoring thread | ||
908 | * @data: ips driver structure | ||
909 | * | ||
910 | * This is the main function for the IPS driver. It monitors power and | ||
911 | * tempurature in the MCP and adjusts CPU and GPU power clams accordingly. | ||
912 | * | ||
913 | * We keep a 5s moving average of power consumption and tempurature. Using | ||
914 | * that data, along with CPU vs GPU preference, we adjust the power clamps | ||
915 | * up or down. | ||
916 | */ | ||
917 | static int ips_monitor(void *data) | ||
918 | { | ||
919 | struct ips_driver *ips = data; | ||
920 | struct timer_list timer; | ||
921 | unsigned long seqno_timestamp, expire, last_msecs, last_sample_period; | ||
922 | int i; | ||
923 | u32 *cpu_samples, *mchp_samples, old_cpu_power; | ||
924 | u16 *mcp_samples, *ctv1_samples, *ctv2_samples, *mch_samples; | ||
925 | u8 cur_seqno, last_seqno; | ||
926 | |||
927 | mcp_samples = kzalloc(sizeof(u16) * IPS_SAMPLE_COUNT, GFP_KERNEL); | ||
928 | ctv1_samples = kzalloc(sizeof(u16) * IPS_SAMPLE_COUNT, GFP_KERNEL); | ||
929 | ctv2_samples = kzalloc(sizeof(u16) * IPS_SAMPLE_COUNT, GFP_KERNEL); | ||
930 | mch_samples = kzalloc(sizeof(u16) * IPS_SAMPLE_COUNT, GFP_KERNEL); | ||
931 | cpu_samples = kzalloc(sizeof(u32) * IPS_SAMPLE_COUNT, GFP_KERNEL); | ||
932 | mchp_samples = kzalloc(sizeof(u32) * IPS_SAMPLE_COUNT, GFP_KERNEL); | ||
933 | if (!mcp_samples || !ctv1_samples || !ctv2_samples || !mch_samples || | ||
934 | !cpu_samples || !mchp_samples) { | ||
935 | dev_err(&ips->dev->dev, | ||
936 | "failed to allocate sample array, ips disabled\n"); | ||
937 | kfree(mcp_samples); | ||
938 | kfree(ctv1_samples); | ||
939 | kfree(ctv2_samples); | ||
940 | kfree(mch_samples); | ||
941 | kfree(cpu_samples); | ||
942 | kfree(mchp_samples); | ||
943 | kthread_stop(ips->adjust); | ||
944 | return -ENOMEM; | ||
945 | } | ||
946 | |||
947 | last_seqno = (thm_readl(THM_ITV) & ITV_ME_SEQNO_MASK) >> | ||
948 | ITV_ME_SEQNO_SHIFT; | ||
949 | seqno_timestamp = get_jiffies_64(); | ||
950 | |||
951 | old_cpu_power = thm_readl(THM_CEC) / 65535; | ||
952 | schedule_timeout_interruptible(msecs_to_jiffies(IPS_SAMPLE_PERIOD)); | ||
953 | |||
954 | /* Collect an initial average */ | ||
955 | for (i = 0; i < IPS_SAMPLE_COUNT; i++) { | ||
956 | u32 mchp, cpu_power; | ||
957 | u16 val; | ||
958 | |||
959 | mcp_samples[i] = read_ptv(ips); | ||
960 | |||
961 | val = read_ctv(ips, 0); | ||
962 | ctv1_samples[i] = val; | ||
963 | |||
964 | val = read_ctv(ips, 1); | ||
965 | ctv2_samples[i] = val; | ||
966 | |||
967 | val = read_mgtv(ips); | ||
968 | mch_samples[i] = val; | ||
969 | |||
970 | cpu_power = get_cpu_power(ips, &old_cpu_power, | ||
971 | IPS_SAMPLE_PERIOD); | ||
972 | cpu_samples[i] = cpu_power; | ||
973 | |||
974 | if (ips->read_mch_val) { | ||
975 | mchp = ips->read_mch_val(); | ||
976 | mchp_samples[i] = mchp; | ||
977 | } | ||
978 | |||
979 | schedule_timeout_interruptible(msecs_to_jiffies(IPS_SAMPLE_PERIOD)); | ||
980 | if (kthread_should_stop()) | ||
981 | break; | ||
982 | } | ||
983 | |||
984 | ips->mcp_avg_temp = calc_avg_temp(ips, mcp_samples); | ||
985 | ips->ctv1_avg_temp = calc_avg_temp(ips, ctv1_samples); | ||
986 | ips->ctv2_avg_temp = calc_avg_temp(ips, ctv2_samples); | ||
987 | ips->mch_avg_temp = calc_avg_temp(ips, mch_samples); | ||
988 | ips->cpu_avg_power = calc_avg_power(ips, cpu_samples); | ||
989 | ips->mch_avg_power = calc_avg_power(ips, mchp_samples); | ||
990 | kfree(mcp_samples); | ||
991 | kfree(ctv1_samples); | ||
992 | kfree(ctv2_samples); | ||
993 | kfree(mch_samples); | ||
994 | kfree(cpu_samples); | ||
995 | kfree(mchp_samples); | ||
996 | |||
997 | /* Start the adjustment thread now that we have data */ | ||
998 | wake_up_process(ips->adjust); | ||
999 | |||
1000 | /* | ||
1001 | * Ok, now we have an initial avg. From here on out, we track the | ||
1002 | * running avg using a decaying average calculation. This allows | ||
1003 | * us to reduce the sample frequency if the CPU and GPU are idle. | ||
1004 | */ | ||
1005 | old_cpu_power = thm_readl(THM_CEC); | ||
1006 | schedule_timeout_interruptible(msecs_to_jiffies(IPS_SAMPLE_PERIOD)); | ||
1007 | last_sample_period = IPS_SAMPLE_PERIOD; | ||
1008 | |||
1009 | setup_deferrable_timer_on_stack(&timer, monitor_timeout, | ||
1010 | (unsigned long)current); | ||
1011 | do { | ||
1012 | u32 cpu_val, mch_val; | ||
1013 | u16 val; | ||
1014 | |||
1015 | /* MCP itself */ | ||
1016 | val = read_ptv(ips); | ||
1017 | ips->mcp_avg_temp = update_average_temp(ips->mcp_avg_temp, val); | ||
1018 | |||
1019 | /* Processor 0 */ | ||
1020 | val = read_ctv(ips, 0); | ||
1021 | ips->ctv1_avg_temp = | ||
1022 | update_average_temp(ips->ctv1_avg_temp, val); | ||
1023 | /* Power */ | ||
1024 | cpu_val = get_cpu_power(ips, &old_cpu_power, | ||
1025 | last_sample_period); | ||
1026 | ips->cpu_avg_power = | ||
1027 | update_average_power(ips->cpu_avg_power, cpu_val); | ||
1028 | |||
1029 | if (ips->second_cpu) { | ||
1030 | /* Processor 1 */ | ||
1031 | val = read_ctv(ips, 1); | ||
1032 | ips->ctv2_avg_temp = | ||
1033 | update_average_temp(ips->ctv2_avg_temp, val); | ||
1034 | } | ||
1035 | |||
1036 | /* MCH */ | ||
1037 | val = read_mgtv(ips); | ||
1038 | ips->mch_avg_temp = update_average_temp(ips->mch_avg_temp, val); | ||
1039 | /* Power */ | ||
1040 | if (ips->read_mch_val) { | ||
1041 | mch_val = ips->read_mch_val(); | ||
1042 | ips->mch_avg_power = | ||
1043 | update_average_power(ips->mch_avg_power, | ||
1044 | mch_val); | ||
1045 | } | ||
1046 | |||
1047 | /* | ||
1048 | * Make sure ME is updating thermal regs. | ||
1049 | * Note: | ||
1050 | * If it's been more than a second since the last update, | ||
1051 | * the ME is probably hung. | ||
1052 | */ | ||
1053 | cur_seqno = (thm_readl(THM_ITV) & ITV_ME_SEQNO_MASK) >> | ||
1054 | ITV_ME_SEQNO_SHIFT; | ||
1055 | if (cur_seqno == last_seqno && | ||
1056 | time_after(jiffies, seqno_timestamp + HZ)) { | ||
1057 | dev_warn(&ips->dev->dev, "ME failed to update for more than 1s, likely hung\n"); | ||
1058 | } else { | ||
1059 | seqno_timestamp = get_jiffies_64(); | ||
1060 | last_seqno = cur_seqno; | ||
1061 | } | ||
1062 | |||
1063 | last_msecs = jiffies_to_msecs(jiffies); | ||
1064 | expire = jiffies + msecs_to_jiffies(IPS_SAMPLE_PERIOD); | ||
1065 | |||
1066 | __set_current_state(TASK_UNINTERRUPTIBLE); | ||
1067 | mod_timer(&timer, expire); | ||
1068 | schedule(); | ||
1069 | |||
1070 | /* Calculate actual sample period for power averaging */ | ||
1071 | last_sample_period = jiffies_to_msecs(jiffies) - last_msecs; | ||
1072 | if (!last_sample_period) | ||
1073 | last_sample_period = 1; | ||
1074 | } while (!kthread_should_stop()); | ||
1075 | |||
1076 | del_timer_sync(&timer); | ||
1077 | destroy_timer_on_stack(&timer); | ||
1078 | |||
1079 | dev_dbg(&ips->dev->dev, "ips-monitor thread stopped\n"); | ||
1080 | |||
1081 | return 0; | ||
1082 | } | ||
1083 | |||
1084 | #if 0 | ||
1085 | #define THM_DUMPW(reg) \ | ||
1086 | { \ | ||
1087 | u16 val = thm_readw(reg); \ | ||
1088 | dev_dbg(&ips->dev->dev, #reg ": 0x%04x\n", val); \ | ||
1089 | } | ||
1090 | #define THM_DUMPL(reg) \ | ||
1091 | { \ | ||
1092 | u32 val = thm_readl(reg); \ | ||
1093 | dev_dbg(&ips->dev->dev, #reg ": 0x%08x\n", val); \ | ||
1094 | } | ||
1095 | #define THM_DUMPQ(reg) \ | ||
1096 | { \ | ||
1097 | u64 val = thm_readq(reg); \ | ||
1098 | dev_dbg(&ips->dev->dev, #reg ": 0x%016x\n", val); \ | ||
1099 | } | ||
1100 | |||
1101 | static void dump_thermal_info(struct ips_driver *ips) | ||
1102 | { | ||
1103 | u16 ptl; | ||
1104 | |||
1105 | ptl = thm_readw(THM_PTL); | ||
1106 | dev_dbg(&ips->dev->dev, "Processor temp limit: %d\n", ptl); | ||
1107 | |||
1108 | THM_DUMPW(THM_CTA); | ||
1109 | THM_DUMPW(THM_TRC); | ||
1110 | THM_DUMPW(THM_CTV1); | ||
1111 | THM_DUMPL(THM_STS); | ||
1112 | THM_DUMPW(THM_PTV); | ||
1113 | THM_DUMPQ(THM_MGTV); | ||
1114 | } | ||
1115 | #endif | ||
1116 | |||
1117 | /** | ||
1118 | * ips_irq_handler - handle temperature triggers and other IPS events | ||
1119 | * @irq: irq number | ||
1120 | * @arg: unused | ||
1121 | * | ||
1122 | * Handle temperature limit trigger events, generally by lowering the clamps. | ||
1123 | * If we're at a critical limit, we clamp back to the lowest possible value | ||
1124 | * to prevent emergency shutdown. | ||
1125 | */ | ||
1126 | static irqreturn_t ips_irq_handler(int irq, void *arg) | ||
1127 | { | ||
1128 | struct ips_driver *ips = arg; | ||
1129 | u8 tses = thm_readb(THM_TSES); | ||
1130 | u8 tes = thm_readb(THM_TES); | ||
1131 | |||
1132 | if (!tses && !tes) | ||
1133 | return IRQ_NONE; | ||
1134 | |||
1135 | dev_info(&ips->dev->dev, "TSES: 0x%02x\n", tses); | ||
1136 | dev_info(&ips->dev->dev, "TES: 0x%02x\n", tes); | ||
1137 | |||
1138 | /* STS update from EC? */ | ||
1139 | if (tes & 1) { | ||
1140 | u32 sts, tc1; | ||
1141 | |||
1142 | sts = thm_readl(THM_STS); | ||
1143 | tc1 = thm_readl(THM_TC1); | ||
1144 | |||
1145 | if (sts & STS_NVV) { | ||
1146 | spin_lock(&ips->turbo_status_lock); | ||
1147 | ips->core_power_limit = (sts & STS_PCPL_MASK) >> | ||
1148 | STS_PCPL_SHIFT; | ||
1149 | ips->mch_power_limit = (sts & STS_GPL_MASK) >> | ||
1150 | STS_GPL_SHIFT; | ||
1151 | /* ignore EC CPU vs GPU pref */ | ||
1152 | ips->cpu_turbo_enabled = !(sts & STS_PCTD_DIS); | ||
1153 | ips->gpu_turbo_enabled = !(sts & STS_GTD_DIS); | ||
1154 | ips->mcp_temp_limit = (sts & STS_PTL_MASK) >> | ||
1155 | STS_PTL_SHIFT; | ||
1156 | ips->mcp_power_limit = (tc1 & STS_PPL_MASK) >> | ||
1157 | STS_PPL_SHIFT; | ||
1158 | spin_unlock(&ips->turbo_status_lock); | ||
1159 | |||
1160 | thm_writeb(THM_SEC, SEC_ACK); | ||
1161 | } | ||
1162 | thm_writeb(THM_TES, tes); | ||
1163 | } | ||
1164 | |||
1165 | /* Thermal trip */ | ||
1166 | if (tses) { | ||
1167 | dev_warn(&ips->dev->dev, | ||
1168 | "thermal trip occurred, tses: 0x%04x\n", tses); | ||
1169 | thm_writeb(THM_TSES, tses); | ||
1170 | } | ||
1171 | |||
1172 | return IRQ_HANDLED; | ||
1173 | } | ||
1174 | |||
1175 | #ifndef CONFIG_DEBUG_FS | ||
1176 | static void ips_debugfs_init(struct ips_driver *ips) { return; } | ||
1177 | static void ips_debugfs_cleanup(struct ips_driver *ips) { return; } | ||
1178 | #else | ||
1179 | |||
1180 | /* Expose current state and limits in debugfs if possible */ | ||
1181 | |||
1182 | struct ips_debugfs_node { | ||
1183 | struct ips_driver *ips; | ||
1184 | char *name; | ||
1185 | int (*show)(struct seq_file *m, void *data); | ||
1186 | }; | ||
1187 | |||
1188 | static int show_cpu_temp(struct seq_file *m, void *data) | ||
1189 | { | ||
1190 | struct ips_driver *ips = m->private; | ||
1191 | |||
1192 | seq_printf(m, "%d.%02d\n", ips->ctv1_avg_temp / 100, | ||
1193 | ips->ctv1_avg_temp % 100); | ||
1194 | |||
1195 | return 0; | ||
1196 | } | ||
1197 | |||
1198 | static int show_cpu_power(struct seq_file *m, void *data) | ||
1199 | { | ||
1200 | struct ips_driver *ips = m->private; | ||
1201 | |||
1202 | seq_printf(m, "%dmW\n", ips->cpu_avg_power); | ||
1203 | |||
1204 | return 0; | ||
1205 | } | ||
1206 | |||
1207 | static int show_cpu_clamp(struct seq_file *m, void *data) | ||
1208 | { | ||
1209 | u64 turbo_override; | ||
1210 | int tdp, tdc; | ||
1211 | |||
1212 | rdmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override); | ||
1213 | |||
1214 | tdp = (int)(turbo_override & TURBO_TDP_MASK); | ||
1215 | tdc = (int)((turbo_override & TURBO_TDC_MASK) >> TURBO_TDC_SHIFT); | ||
1216 | |||
1217 | /* Convert to .1W/A units */ | ||
1218 | tdp = tdp * 10 / 8; | ||
1219 | tdc = tdc * 10 / 8; | ||
1220 | |||
1221 | /* Watts Amperes */ | ||
1222 | seq_printf(m, "%d.%dW %d.%dA\n", tdp / 10, tdp % 10, | ||
1223 | tdc / 10, tdc % 10); | ||
1224 | |||
1225 | return 0; | ||
1226 | } | ||
1227 | |||
1228 | static int show_mch_temp(struct seq_file *m, void *data) | ||
1229 | { | ||
1230 | struct ips_driver *ips = m->private; | ||
1231 | |||
1232 | seq_printf(m, "%d.%02d\n", ips->mch_avg_temp / 100, | ||
1233 | ips->mch_avg_temp % 100); | ||
1234 | |||
1235 | return 0; | ||
1236 | } | ||
1237 | |||
1238 | static int show_mch_power(struct seq_file *m, void *data) | ||
1239 | { | ||
1240 | struct ips_driver *ips = m->private; | ||
1241 | |||
1242 | seq_printf(m, "%dmW\n", ips->mch_avg_power); | ||
1243 | |||
1244 | return 0; | ||
1245 | } | ||
1246 | |||
1247 | static struct ips_debugfs_node ips_debug_files[] = { | ||
1248 | { NULL, "cpu_temp", show_cpu_temp }, | ||
1249 | { NULL, "cpu_power", show_cpu_power }, | ||
1250 | { NULL, "cpu_clamp", show_cpu_clamp }, | ||
1251 | { NULL, "mch_temp", show_mch_temp }, | ||
1252 | { NULL, "mch_power", show_mch_power }, | ||
1253 | }; | ||
1254 | |||
1255 | static int ips_debugfs_open(struct inode *inode, struct file *file) | ||
1256 | { | ||
1257 | struct ips_debugfs_node *node = inode->i_private; | ||
1258 | |||
1259 | return single_open(file, node->show, node->ips); | ||
1260 | } | ||
1261 | |||
1262 | static const struct file_operations ips_debugfs_ops = { | ||
1263 | .owner = THIS_MODULE, | ||
1264 | .open = ips_debugfs_open, | ||
1265 | .read = seq_read, | ||
1266 | .llseek = seq_lseek, | ||
1267 | .release = single_release, | ||
1268 | }; | ||
1269 | |||
1270 | static void ips_debugfs_cleanup(struct ips_driver *ips) | ||
1271 | { | ||
1272 | if (ips->debug_root) | ||
1273 | debugfs_remove_recursive(ips->debug_root); | ||
1274 | return; | ||
1275 | } | ||
1276 | |||
1277 | static void ips_debugfs_init(struct ips_driver *ips) | ||
1278 | { | ||
1279 | int i; | ||
1280 | |||
1281 | ips->debug_root = debugfs_create_dir("ips", NULL); | ||
1282 | if (!ips->debug_root) { | ||
1283 | dev_err(&ips->dev->dev, | ||
1284 | "failed to create debugfs entries: %ld\n", | ||
1285 | PTR_ERR(ips->debug_root)); | ||
1286 | return; | ||
1287 | } | ||
1288 | |||
1289 | for (i = 0; i < ARRAY_SIZE(ips_debug_files); i++) { | ||
1290 | struct dentry *ent; | ||
1291 | struct ips_debugfs_node *node = &ips_debug_files[i]; | ||
1292 | |||
1293 | node->ips = ips; | ||
1294 | ent = debugfs_create_file(node->name, S_IFREG | S_IRUGO, | ||
1295 | ips->debug_root, node, | ||
1296 | &ips_debugfs_ops); | ||
1297 | if (!ent) { | ||
1298 | dev_err(&ips->dev->dev, | ||
1299 | "failed to create debug file: %ld\n", | ||
1300 | PTR_ERR(ent)); | ||
1301 | goto err_cleanup; | ||
1302 | } | ||
1303 | } | ||
1304 | |||
1305 | return; | ||
1306 | |||
1307 | err_cleanup: | ||
1308 | ips_debugfs_cleanup(ips); | ||
1309 | return; | ||
1310 | } | ||
1311 | #endif /* CONFIG_DEBUG_FS */ | ||
1312 | |||
1313 | /** | ||
1314 | * ips_detect_cpu - detect whether CPU supports IPS | ||
1315 | * | ||
1316 | * Walk our list and see if we're on a supported CPU. If we find one, | ||
1317 | * return the limits for it. | ||
1318 | */ | ||
1319 | static struct ips_mcp_limits *ips_detect_cpu(struct ips_driver *ips) | ||
1320 | { | ||
1321 | u64 turbo_power, misc_en; | ||
1322 | struct ips_mcp_limits *limits = NULL; | ||
1323 | u16 tdp; | ||
1324 | |||
1325 | if (!(boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 37)) { | ||
1326 | dev_info(&ips->dev->dev, "Non-IPS CPU detected.\n"); | ||
1327 | goto out; | ||
1328 | } | ||
1329 | |||
1330 | rdmsrl(IA32_MISC_ENABLE, misc_en); | ||
1331 | /* | ||
1332 | * If the turbo enable bit isn't set, we shouldn't try to enable/disable | ||
1333 | * turbo manually or we'll get an illegal MSR access, even though | ||
1334 | * turbo will still be available. | ||
1335 | */ | ||
1336 | if (!(misc_en & IA32_MISC_TURBO_EN)) | ||
1337 | ; /* add turbo MSR write allowed flag if necessary */ | ||
1338 | |||
1339 | if (strstr(boot_cpu_data.x86_model_id, "CPU M")) | ||
1340 | limits = &ips_sv_limits; | ||
1341 | else if (strstr(boot_cpu_data.x86_model_id, "CPU L")) | ||
1342 | limits = &ips_lv_limits; | ||
1343 | else if (strstr(boot_cpu_data.x86_model_id, "CPU U")) | ||
1344 | limits = &ips_ulv_limits; | ||
1345 | else | ||
1346 | dev_info(&ips->dev->dev, "No CPUID match found.\n"); | ||
1347 | |||
1348 | rdmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_power); | ||
1349 | tdp = turbo_power & TURBO_TDP_MASK; | ||
1350 | |||
1351 | /* Sanity check TDP against CPU */ | ||
1352 | if (limits->mcp_power_limit != (tdp / 8) * 1000) { | ||
1353 | dev_warn(&ips->dev->dev, "Warning: CPU TDP doesn't match expected value (found %d, expected %d)\n", | ||
1354 | tdp / 8, limits->mcp_power_limit / 1000); | ||
1355 | } | ||
1356 | |||
1357 | out: | ||
1358 | return limits; | ||
1359 | } | ||
1360 | |||
1361 | /** | ||
1362 | * ips_get_i915_syms - try to get GPU control methods from i915 driver | ||
1363 | * @ips: IPS driver | ||
1364 | * | ||
1365 | * The i915 driver exports several interfaces to allow the IPS driver to | ||
1366 | * monitor and control graphics turbo mode. If we can find them, we can | ||
1367 | * enable graphics turbo, otherwise we must disable it to avoid exceeding | ||
1368 | * thermal and power limits in the MCP. | ||
1369 | */ | ||
1370 | static bool ips_get_i915_syms(struct ips_driver *ips) | ||
1371 | { | ||
1372 | ips->read_mch_val = symbol_get(i915_read_mch_val); | ||
1373 | if (!ips->read_mch_val) | ||
1374 | goto out_err; | ||
1375 | ips->gpu_raise = symbol_get(i915_gpu_raise); | ||
1376 | if (!ips->gpu_raise) | ||
1377 | goto out_put_mch; | ||
1378 | ips->gpu_lower = symbol_get(i915_gpu_lower); | ||
1379 | if (!ips->gpu_lower) | ||
1380 | goto out_put_raise; | ||
1381 | ips->gpu_busy = symbol_get(i915_gpu_busy); | ||
1382 | if (!ips->gpu_busy) | ||
1383 | goto out_put_lower; | ||
1384 | ips->gpu_turbo_disable = symbol_get(i915_gpu_turbo_disable); | ||
1385 | if (!ips->gpu_turbo_disable) | ||
1386 | goto out_put_busy; | ||
1387 | |||
1388 | return true; | ||
1389 | |||
1390 | out_put_busy: | ||
1391 | symbol_put(i915_gpu_turbo_disable); | ||
1392 | out_put_lower: | ||
1393 | symbol_put(i915_gpu_lower); | ||
1394 | out_put_raise: | ||
1395 | symbol_put(i915_gpu_raise); | ||
1396 | out_put_mch: | ||
1397 | symbol_put(i915_read_mch_val); | ||
1398 | out_err: | ||
1399 | return false; | ||
1400 | } | ||
1401 | |||
1402 | static DEFINE_PCI_DEVICE_TABLE(ips_id_table) = { | ||
1403 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, | ||
1404 | PCI_DEVICE_ID_INTEL_THERMAL_SENSOR), }, | ||
1405 | { 0, } | ||
1406 | }; | ||
1407 | |||
1408 | MODULE_DEVICE_TABLE(pci, ips_id_table); | ||
1409 | |||
1410 | static int ips_probe(struct pci_dev *dev, const struct pci_device_id *id) | ||
1411 | { | ||
1412 | u64 platform_info; | ||
1413 | struct ips_driver *ips; | ||
1414 | u32 hts; | ||
1415 | int ret = 0; | ||
1416 | u16 htshi, trc, trc_required_mask; | ||
1417 | u8 tse; | ||
1418 | |||
1419 | ips = kzalloc(sizeof(struct ips_driver), GFP_KERNEL); | ||
1420 | if (!ips) | ||
1421 | return -ENOMEM; | ||
1422 | |||
1423 | pci_set_drvdata(dev, ips); | ||
1424 | ips->dev = dev; | ||
1425 | |||
1426 | ips->limits = ips_detect_cpu(ips); | ||
1427 | if (!ips->limits) { | ||
1428 | dev_info(&dev->dev, "IPS not supported on this CPU\n"); | ||
1429 | ret = -ENXIO; | ||
1430 | goto error_free; | ||
1431 | } | ||
1432 | |||
1433 | spin_lock_init(&ips->turbo_status_lock); | ||
1434 | |||
1435 | if (!pci_resource_start(dev, 0)) { | ||
1436 | dev_err(&dev->dev, "TBAR not assigned, aborting\n"); | ||
1437 | ret = -ENXIO; | ||
1438 | goto error_free; | ||
1439 | } | ||
1440 | |||
1441 | ret = pci_request_regions(dev, "ips thermal sensor"); | ||
1442 | if (ret) { | ||
1443 | dev_err(&dev->dev, "thermal resource busy, aborting\n"); | ||
1444 | goto error_free; | ||
1445 | } | ||
1446 | |||
1447 | ret = pci_enable_device(dev); | ||
1448 | if (ret) { | ||
1449 | dev_err(&dev->dev, "can't enable PCI device, aborting\n"); | ||
1450 | goto error_free; | ||
1451 | } | ||
1452 | |||
1453 | ips->regmap = ioremap(pci_resource_start(dev, 0), | ||
1454 | pci_resource_len(dev, 0)); | ||
1455 | if (!ips->regmap) { | ||
1456 | dev_err(&dev->dev, "failed to map thermal regs, aborting\n"); | ||
1457 | ret = -EBUSY; | ||
1458 | goto error_release; | ||
1459 | } | ||
1460 | |||
1461 | tse = thm_readb(THM_TSE); | ||
1462 | if (tse != TSE_EN) { | ||
1463 | dev_err(&dev->dev, "thermal device not enabled (0x%02x), aborting\n", tse); | ||
1464 | ret = -ENXIO; | ||
1465 | goto error_unmap; | ||
1466 | } | ||
1467 | |||
1468 | trc = thm_readw(THM_TRC); | ||
1469 | trc_required_mask = TRC_CORE1_EN | TRC_CORE_PWR | TRC_MCH_EN; | ||
1470 | if ((trc & trc_required_mask) != trc_required_mask) { | ||
1471 | dev_err(&dev->dev, "thermal reporting for required devices not enabled, aborting\n"); | ||
1472 | ret = -ENXIO; | ||
1473 | goto error_unmap; | ||
1474 | } | ||
1475 | |||
1476 | if (trc & TRC_CORE2_EN) | ||
1477 | ips->second_cpu = true; | ||
1478 | |||
1479 | update_turbo_limits(ips); | ||
1480 | dev_dbg(&dev->dev, "max cpu power clamp: %dW\n", | ||
1481 | ips->mcp_power_limit / 10); | ||
1482 | dev_dbg(&dev->dev, "max core power clamp: %dW\n", | ||
1483 | ips->core_power_limit / 10); | ||
1484 | /* BIOS may update limits at runtime */ | ||
1485 | if (thm_readl(THM_PSC) & PSP_PBRT) | ||
1486 | ips->poll_turbo_status = true; | ||
1487 | |||
1488 | if (!ips_get_i915_syms(ips)) { | ||
1489 | dev_err(&dev->dev, "failed to get i915 symbols, graphics turbo disabled\n"); | ||
1490 | ips->gpu_turbo_enabled = false; | ||
1491 | } else { | ||
1492 | dev_dbg(&dev->dev, "graphics turbo enabled\n"); | ||
1493 | ips->gpu_turbo_enabled = true; | ||
1494 | } | ||
1495 | |||
1496 | /* | ||
1497 | * Check PLATFORM_INFO MSR to make sure this chip is | ||
1498 | * turbo capable. | ||
1499 | */ | ||
1500 | rdmsrl(PLATFORM_INFO, platform_info); | ||
1501 | if (!(platform_info & PLATFORM_TDP)) { | ||
1502 | dev_err(&dev->dev, "platform indicates TDP override unavailable, aborting\n"); | ||
1503 | ret = -ENODEV; | ||
1504 | goto error_unmap; | ||
1505 | } | ||
1506 | |||
1507 | /* | ||
1508 | * IRQ handler for ME interaction | ||
1509 | * Note: don't use MSI here as the PCH has bugs. | ||
1510 | */ | ||
1511 | pci_disable_msi(dev); | ||
1512 | ret = request_irq(dev->irq, ips_irq_handler, IRQF_SHARED, "ips", | ||
1513 | ips); | ||
1514 | if (ret) { | ||
1515 | dev_err(&dev->dev, "request irq failed, aborting\n"); | ||
1516 | goto error_unmap; | ||
1517 | } | ||
1518 | |||
1519 | /* Enable aux, hot & critical interrupts */ | ||
1520 | thm_writeb(THM_TSPIEN, TSPIEN_AUX2_LOHI | TSPIEN_CRIT_LOHI | | ||
1521 | TSPIEN_HOT_LOHI | TSPIEN_AUX_LOHI); | ||
1522 | thm_writeb(THM_TEN, TEN_UPDATE_EN); | ||
1523 | |||
1524 | /* Collect adjustment values */ | ||
1525 | ips->cta_val = thm_readw(THM_CTA); | ||
1526 | ips->pta_val = thm_readw(THM_PTA); | ||
1527 | ips->mgta_val = thm_readw(THM_MGTA); | ||
1528 | |||
1529 | /* Save turbo limits & ratios */ | ||
1530 | rdmsrl(TURBO_POWER_CURRENT_LIMIT, ips->orig_turbo_limit); | ||
1531 | |||
1532 | ips_enable_cpu_turbo(ips); | ||
1533 | ips->cpu_turbo_enabled = true; | ||
1534 | |||
1535 | /* Set up the work queue and monitor/adjust threads */ | ||
1536 | ips->monitor = kthread_run(ips_monitor, ips, "ips-monitor"); | ||
1537 | if (IS_ERR(ips->monitor)) { | ||
1538 | dev_err(&dev->dev, | ||
1539 | "failed to create thermal monitor thread, aborting\n"); | ||
1540 | ret = -ENOMEM; | ||
1541 | goto error_free_irq; | ||
1542 | } | ||
1543 | |||
1544 | ips->adjust = kthread_create(ips_adjust, ips, "ips-adjust"); | ||
1545 | if (IS_ERR(ips->adjust)) { | ||
1546 | dev_err(&dev->dev, | ||
1547 | "failed to create thermal adjust thread, aborting\n"); | ||
1548 | ret = -ENOMEM; | ||
1549 | goto error_thread_cleanup; | ||
1550 | } | ||
1551 | |||
1552 | hts = (ips->core_power_limit << HTS_PCPL_SHIFT) | | ||
1553 | (ips->mcp_temp_limit << HTS_PTL_SHIFT) | HTS_NVV; | ||
1554 | htshi = HTS2_PRST_RUNNING << HTS2_PRST_SHIFT; | ||
1555 | |||
1556 | thm_writew(THM_HTSHI, htshi); | ||
1557 | thm_writel(THM_HTS, hts); | ||
1558 | |||
1559 | ips_debugfs_init(ips); | ||
1560 | |||
1561 | dev_info(&dev->dev, "IPS driver initialized, MCP temp limit %d\n", | ||
1562 | ips->mcp_temp_limit); | ||
1563 | return ret; | ||
1564 | |||
1565 | error_thread_cleanup: | ||
1566 | kthread_stop(ips->monitor); | ||
1567 | error_free_irq: | ||
1568 | free_irq(ips->dev->irq, ips); | ||
1569 | error_unmap: | ||
1570 | iounmap(ips->regmap); | ||
1571 | error_release: | ||
1572 | pci_release_regions(dev); | ||
1573 | error_free: | ||
1574 | kfree(ips); | ||
1575 | return ret; | ||
1576 | } | ||
1577 | |||
1578 | static void ips_remove(struct pci_dev *dev) | ||
1579 | { | ||
1580 | struct ips_driver *ips = pci_get_drvdata(dev); | ||
1581 | u64 turbo_override; | ||
1582 | |||
1583 | if (!ips) | ||
1584 | return; | ||
1585 | |||
1586 | ips_debugfs_cleanup(ips); | ||
1587 | |||
1588 | /* Release i915 driver */ | ||
1589 | if (ips->read_mch_val) | ||
1590 | symbol_put(i915_read_mch_val); | ||
1591 | if (ips->gpu_raise) | ||
1592 | symbol_put(i915_gpu_raise); | ||
1593 | if (ips->gpu_lower) | ||
1594 | symbol_put(i915_gpu_lower); | ||
1595 | if (ips->gpu_busy) | ||
1596 | symbol_put(i915_gpu_busy); | ||
1597 | if (ips->gpu_turbo_disable) | ||
1598 | symbol_put(i915_gpu_turbo_disable); | ||
1599 | |||
1600 | rdmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override); | ||
1601 | turbo_override &= ~(TURBO_TDC_OVR_EN | TURBO_TDP_OVR_EN); | ||
1602 | wrmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override); | ||
1603 | wrmsrl(TURBO_POWER_CURRENT_LIMIT, ips->orig_turbo_limit); | ||
1604 | |||
1605 | free_irq(ips->dev->irq, ips); | ||
1606 | if (ips->adjust) | ||
1607 | kthread_stop(ips->adjust); | ||
1608 | if (ips->monitor) | ||
1609 | kthread_stop(ips->monitor); | ||
1610 | iounmap(ips->regmap); | ||
1611 | pci_release_regions(dev); | ||
1612 | kfree(ips); | ||
1613 | dev_dbg(&dev->dev, "IPS driver removed\n"); | ||
1614 | } | ||
1615 | |||
1616 | #ifdef CONFIG_PM | ||
1617 | static int ips_suspend(struct pci_dev *dev, pm_message_t state) | ||
1618 | { | ||
1619 | return 0; | ||
1620 | } | ||
1621 | |||
1622 | static int ips_resume(struct pci_dev *dev) | ||
1623 | { | ||
1624 | return 0; | ||
1625 | } | ||
1626 | #else | ||
1627 | #define ips_suspend NULL | ||
1628 | #define ips_resume NULL | ||
1629 | #endif /* CONFIG_PM */ | ||
1630 | |||
1631 | static void ips_shutdown(struct pci_dev *dev) | ||
1632 | { | ||
1633 | } | ||
1634 | |||
1635 | static struct pci_driver ips_pci_driver = { | ||
1636 | .name = "intel ips", | ||
1637 | .id_table = ips_id_table, | ||
1638 | .probe = ips_probe, | ||
1639 | .remove = ips_remove, | ||
1640 | .suspend = ips_suspend, | ||
1641 | .resume = ips_resume, | ||
1642 | .shutdown = ips_shutdown, | ||
1643 | }; | ||
1644 | |||
1645 | static int __init ips_init(void) | ||
1646 | { | ||
1647 | return pci_register_driver(&ips_pci_driver); | ||
1648 | } | ||
1649 | module_init(ips_init); | ||
1650 | |||
1651 | static void ips_exit(void) | ||
1652 | { | ||
1653 | pci_unregister_driver(&ips_pci_driver); | ||
1654 | return; | ||
1655 | } | ||
1656 | module_exit(ips_exit); | ||
1657 | |||
1658 | MODULE_LICENSE("GPL"); | ||
1659 | MODULE_AUTHOR("Jesse Barnes <jbarnes@virtuousgeek.org>"); | ||
1660 | MODULE_DESCRIPTION("Intelligent Power Sharing Driver"); | ||
diff --git a/drivers/platform/x86/intel_menlow.c b/drivers/platform/x86/intel_menlow.c index 2f795ce2b939..eacd5da7dd24 100644 --- a/drivers/platform/x86/intel_menlow.c +++ b/drivers/platform/x86/intel_menlow.c | |||
@@ -53,6 +53,8 @@ MODULE_LICENSE("GPL"); | |||
53 | #define MEMORY_ARG_CUR_BANDWIDTH 1 | 53 | #define MEMORY_ARG_CUR_BANDWIDTH 1 |
54 | #define MEMORY_ARG_MAX_BANDWIDTH 0 | 54 | #define MEMORY_ARG_MAX_BANDWIDTH 0 |
55 | 55 | ||
56 | static void intel_menlow_unregister_sensor(void); | ||
57 | |||
56 | /* | 58 | /* |
57 | * GTHS returning 'n' would mean that [0,n-1] states are supported | 59 | * GTHS returning 'n' would mean that [0,n-1] states are supported |
58 | * In that case max_cstate would be n-1 | 60 | * In that case max_cstate would be n-1 |
@@ -406,8 +408,10 @@ static int intel_menlow_add_one_attribute(char *name, int mode, void *show, | |||
406 | attr->handle = handle; | 408 | attr->handle = handle; |
407 | 409 | ||
408 | result = device_create_file(dev, &attr->attr); | 410 | result = device_create_file(dev, &attr->attr); |
409 | if (result) | 411 | if (result) { |
412 | kfree(attr); | ||
410 | return result; | 413 | return result; |
414 | } | ||
411 | 415 | ||
412 | mutex_lock(&intel_menlow_attr_lock); | 416 | mutex_lock(&intel_menlow_attr_lock); |
413 | list_add_tail(&attr->node, &intel_menlow_attr_list); | 417 | list_add_tail(&attr->node, &intel_menlow_attr_list); |
@@ -431,11 +435,11 @@ static acpi_status intel_menlow_register_sensor(acpi_handle handle, u32 lvl, | |||
431 | /* _TZ must have the AUX0/1 methods */ | 435 | /* _TZ must have the AUX0/1 methods */ |
432 | status = acpi_get_handle(handle, GET_AUX0, &dummy); | 436 | status = acpi_get_handle(handle, GET_AUX0, &dummy); |
433 | if (ACPI_FAILURE(status)) | 437 | if (ACPI_FAILURE(status)) |
434 | goto not_found; | 438 | return (status == AE_NOT_FOUND) ? AE_OK : status; |
435 | 439 | ||
436 | status = acpi_get_handle(handle, SET_AUX0, &dummy); | 440 | status = acpi_get_handle(handle, SET_AUX0, &dummy); |
437 | if (ACPI_FAILURE(status)) | 441 | if (ACPI_FAILURE(status)) |
438 | goto not_found; | 442 | return (status == AE_NOT_FOUND) ? AE_OK : status; |
439 | 443 | ||
440 | result = intel_menlow_add_one_attribute("aux0", 0644, | 444 | result = intel_menlow_add_one_attribute("aux0", 0644, |
441 | aux0_show, aux0_store, | 445 | aux0_show, aux0_store, |
@@ -445,17 +449,19 @@ static acpi_status intel_menlow_register_sensor(acpi_handle handle, u32 lvl, | |||
445 | 449 | ||
446 | status = acpi_get_handle(handle, GET_AUX1, &dummy); | 450 | status = acpi_get_handle(handle, GET_AUX1, &dummy); |
447 | if (ACPI_FAILURE(status)) | 451 | if (ACPI_FAILURE(status)) |
448 | goto not_found; | 452 | goto aux1_not_found; |
449 | 453 | ||
450 | status = acpi_get_handle(handle, SET_AUX1, &dummy); | 454 | status = acpi_get_handle(handle, SET_AUX1, &dummy); |
451 | if (ACPI_FAILURE(status)) | 455 | if (ACPI_FAILURE(status)) |
452 | goto not_found; | 456 | goto aux1_not_found; |
453 | 457 | ||
454 | result = intel_menlow_add_one_attribute("aux1", 0644, | 458 | result = intel_menlow_add_one_attribute("aux1", 0644, |
455 | aux1_show, aux1_store, | 459 | aux1_show, aux1_store, |
456 | &thermal->device, handle); | 460 | &thermal->device, handle); |
457 | if (result) | 461 | if (result) { |
462 | intel_menlow_unregister_sensor(); | ||
458 | return AE_ERROR; | 463 | return AE_ERROR; |
464 | } | ||
459 | 465 | ||
460 | /* | 466 | /* |
461 | * create the "dabney_enabled" attribute which means the user app | 467 | * create the "dabney_enabled" attribute which means the user app |
@@ -465,14 +471,17 @@ static acpi_status intel_menlow_register_sensor(acpi_handle handle, u32 lvl, | |||
465 | result = intel_menlow_add_one_attribute("bios_enabled", 0444, | 471 | result = intel_menlow_add_one_attribute("bios_enabled", 0444, |
466 | bios_enabled_show, NULL, | 472 | bios_enabled_show, NULL, |
467 | &thermal->device, handle); | 473 | &thermal->device, handle); |
468 | if (result) | 474 | if (result) { |
475 | intel_menlow_unregister_sensor(); | ||
469 | return AE_ERROR; | 476 | return AE_ERROR; |
477 | } | ||
470 | 478 | ||
471 | not_found: | 479 | aux1_not_found: |
472 | if (status == AE_NOT_FOUND) | 480 | if (status == AE_NOT_FOUND) |
473 | return AE_OK; | 481 | return AE_OK; |
474 | else | 482 | |
475 | return status; | 483 | intel_menlow_unregister_sensor(); |
484 | return status; | ||
476 | } | 485 | } |
477 | 486 | ||
478 | static void intel_menlow_unregister_sensor(void) | 487 | static void intel_menlow_unregister_sensor(void) |
@@ -513,8 +522,10 @@ static int __init intel_menlow_module_init(void) | |||
513 | status = acpi_walk_namespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT, | 522 | status = acpi_walk_namespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT, |
514 | ACPI_UINT32_MAX, | 523 | ACPI_UINT32_MAX, |
515 | intel_menlow_register_sensor, NULL, NULL, NULL); | 524 | intel_menlow_register_sensor, NULL, NULL, NULL); |
516 | if (ACPI_FAILURE(status)) | 525 | if (ACPI_FAILURE(status)) { |
526 | acpi_bus_unregister_driver(&intel_menlow_memory_driver); | ||
517 | return -ENODEV; | 527 | return -ENODEV; |
528 | } | ||
518 | 529 | ||
519 | return 0; | 530 | return 0; |
520 | } | 531 | } |
diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c new file mode 100644 index 000000000000..5cdcff653918 --- /dev/null +++ b/drivers/platform/x86/intel_pmic_gpio.c | |||
@@ -0,0 +1,340 @@ | |||
1 | /* Moorestown PMIC GPIO (access through IPC) driver | ||
2 | * Copyright (c) 2008 - 2009, Intel Corporation. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program; if not, write to the Free Software | ||
15 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
16 | */ | ||
17 | |||
18 | /* Supports: | ||
19 | * Moorestown platform PMIC chip | ||
20 | */ | ||
21 | |||
22 | #include <linux/module.h> | ||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/stddef.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <linux/ioport.h> | ||
29 | #include <linux/init.h> | ||
30 | #include <linux/io.h> | ||
31 | #include <linux/gpio.h> | ||
32 | #include <linux/interrupt.h> | ||
33 | #include <asm/intel_scu_ipc.h> | ||
34 | #include <linux/device.h> | ||
35 | #include <linux/intel_pmic_gpio.h> | ||
36 | #include <linux/platform_device.h> | ||
37 | |||
38 | #define DRIVER_NAME "pmic_gpio" | ||
39 | |||
40 | /* register offset that IPC driver should use | ||
41 | * 8 GPIO + 8 GPOSW (6 controllable) + 8GPO | ||
42 | */ | ||
43 | enum pmic_gpio_register { | ||
44 | GPIO0 = 0xE0, | ||
45 | GPIO7 = 0xE7, | ||
46 | GPIOINT = 0xE8, | ||
47 | GPOSWCTL0 = 0xEC, | ||
48 | GPOSWCTL5 = 0xF1, | ||
49 | GPO = 0xF4, | ||
50 | }; | ||
51 | |||
52 | /* bits definition for GPIO & GPOSW */ | ||
53 | #define GPIO_DRV 0x01 | ||
54 | #define GPIO_DIR 0x02 | ||
55 | #define GPIO_DIN 0x04 | ||
56 | #define GPIO_DOU 0x08 | ||
57 | #define GPIO_INTCTL 0x30 | ||
58 | #define GPIO_DBC 0xc0 | ||
59 | |||
60 | #define GPOSW_DRV 0x01 | ||
61 | #define GPOSW_DOU 0x08 | ||
62 | #define GPOSW_RDRV 0x30 | ||
63 | |||
64 | |||
65 | #define NUM_GPIO 24 | ||
66 | |||
67 | struct pmic_gpio_irq { | ||
68 | spinlock_t lock; | ||
69 | u32 trigger[NUM_GPIO]; | ||
70 | u32 dirty; | ||
71 | struct work_struct work; | ||
72 | }; | ||
73 | |||
74 | |||
75 | struct pmic_gpio { | ||
76 | struct gpio_chip chip; | ||
77 | struct pmic_gpio_irq irqtypes; | ||
78 | void *gpiointr; | ||
79 | int irq; | ||
80 | unsigned irq_base; | ||
81 | }; | ||
82 | |||
83 | static void pmic_program_irqtype(int gpio, int type) | ||
84 | { | ||
85 | if (type & IRQ_TYPE_EDGE_RISING) | ||
86 | intel_scu_ipc_update_register(GPIO0 + gpio, 0x20, 0x20); | ||
87 | else | ||
88 | intel_scu_ipc_update_register(GPIO0 + gpio, 0x00, 0x20); | ||
89 | |||
90 | if (type & IRQ_TYPE_EDGE_FALLING) | ||
91 | intel_scu_ipc_update_register(GPIO0 + gpio, 0x10, 0x10); | ||
92 | else | ||
93 | intel_scu_ipc_update_register(GPIO0 + gpio, 0x00, 0x10); | ||
94 | }; | ||
95 | |||
96 | static void pmic_irqtype_work(struct work_struct *work) | ||
97 | { | ||
98 | struct pmic_gpio_irq *t = | ||
99 | container_of(work, struct pmic_gpio_irq, work); | ||
100 | unsigned long flags; | ||
101 | int i; | ||
102 | u16 type; | ||
103 | |||
104 | spin_lock_irqsave(&t->lock, flags); | ||
105 | /* As we drop the lock, we may need multiple scans if we race the | ||
106 | pmic_irq_type function */ | ||
107 | while (t->dirty) { | ||
108 | /* | ||
109 | * For each pin that has the dirty bit set send an IPC | ||
110 | * message to configure the hardware via the PMIC | ||
111 | */ | ||
112 | for (i = 0; i < NUM_GPIO; i++) { | ||
113 | if (!(t->dirty & (1 << i))) | ||
114 | continue; | ||
115 | t->dirty &= ~(1 << i); | ||
116 | /* We can't trust the array entry or dirty | ||
117 | once the lock is dropped */ | ||
118 | type = t->trigger[i]; | ||
119 | spin_unlock_irqrestore(&t->lock, flags); | ||
120 | pmic_program_irqtype(i, type); | ||
121 | spin_lock_irqsave(&t->lock, flags); | ||
122 | } | ||
123 | } | ||
124 | spin_unlock_irqrestore(&t->lock, flags); | ||
125 | } | ||
126 | |||
127 | static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned offset) | ||
128 | { | ||
129 | if (offset > 8) { | ||
130 | printk(KERN_ERR | ||
131 | "%s: only pin 0-7 support input\n", __func__); | ||
132 | return -1;/* we only have 8 GPIO can use as input */ | ||
133 | } | ||
134 | return intel_scu_ipc_update_register(GPIO0 + offset, | ||
135 | GPIO_DIR, GPIO_DIR); | ||
136 | } | ||
137 | |||
138 | static int pmic_gpio_direction_output(struct gpio_chip *chip, | ||
139 | unsigned offset, int value) | ||
140 | { | ||
141 | int rc = 0; | ||
142 | |||
143 | if (offset < 8)/* it is GPIO */ | ||
144 | rc = intel_scu_ipc_update_register(GPIO0 + offset, | ||
145 | GPIO_DRV | GPIO_DOU | GPIO_DIR, | ||
146 | GPIO_DRV | (value ? GPIO_DOU : 0)); | ||
147 | else if (offset < 16)/* it is GPOSW */ | ||
148 | rc = intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8, | ||
149 | GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV, | ||
150 | GPOSW_DRV | (value ? GPOSW_DOU : 0)); | ||
151 | else if (offset > 15 && offset < 24)/* it is GPO */ | ||
152 | rc = intel_scu_ipc_update_register(GPO, | ||
153 | 1 << (offset - 16), | ||
154 | value ? 1 << (offset - 16) : 0); | ||
155 | else { | ||
156 | printk(KERN_ERR | ||
157 | "%s: invalid PMIC GPIO pin %d!\n", __func__, offset); | ||
158 | WARN_ON(1); | ||
159 | } | ||
160 | |||
161 | return rc; | ||
162 | } | ||
163 | |||
164 | static int pmic_gpio_get(struct gpio_chip *chip, unsigned offset) | ||
165 | { | ||
166 | u8 r; | ||
167 | int ret; | ||
168 | |||
169 | /* we only have 8 GPIO pins we can use as input */ | ||
170 | if (offset > 8) | ||
171 | return -EOPNOTSUPP; | ||
172 | ret = intel_scu_ipc_ioread8(GPIO0 + offset, &r); | ||
173 | if (ret < 0) | ||
174 | return ret; | ||
175 | return r & GPIO_DIN; | ||
176 | } | ||
177 | |||
178 | static void pmic_gpio_set(struct gpio_chip *chip, unsigned offset, int value) | ||
179 | { | ||
180 | if (offset < 8)/* it is GPIO */ | ||
181 | intel_scu_ipc_update_register(GPIO0 + offset, | ||
182 | GPIO_DRV | GPIO_DOU, | ||
183 | GPIO_DRV | (value ? GPIO_DOU : 0)); | ||
184 | else if (offset < 16)/* it is GPOSW */ | ||
185 | intel_scu_ipc_update_register(GPOSWCTL0 + offset - 8, | ||
186 | GPOSW_DRV | GPOSW_DOU | GPOSW_RDRV, | ||
187 | GPOSW_DRV | (value ? GPOSW_DOU : 0)); | ||
188 | else if (offset > 15 && offset < 24) /* it is GPO */ | ||
189 | intel_scu_ipc_update_register(GPO, | ||
190 | 1 << (offset - 16), | ||
191 | value ? 1 << (offset - 16) : 0); | ||
192 | } | ||
193 | |||
194 | static int pmic_irq_type(unsigned irq, unsigned type) | ||
195 | { | ||
196 | struct pmic_gpio *pg = get_irq_chip_data(irq); | ||
197 | u32 gpio = irq - pg->irq_base; | ||
198 | unsigned long flags; | ||
199 | |||
200 | if (gpio > pg->chip.ngpio) | ||
201 | return -EINVAL; | ||
202 | |||
203 | spin_lock_irqsave(&pg->irqtypes.lock, flags); | ||
204 | pg->irqtypes.trigger[gpio] = type; | ||
205 | pg->irqtypes.dirty |= (1 << gpio); | ||
206 | spin_unlock_irqrestore(&pg->irqtypes.lock, flags); | ||
207 | schedule_work(&pg->irqtypes.work); | ||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | |||
212 | |||
213 | static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned offset) | ||
214 | { | ||
215 | struct pmic_gpio *pg = container_of(chip, struct pmic_gpio, chip); | ||
216 | |||
217 | return pg->irq_base + offset; | ||
218 | } | ||
219 | |||
220 | /* the gpiointr register is read-clear, so just do nothing. */ | ||
221 | static void pmic_irq_unmask(unsigned irq) | ||
222 | { | ||
223 | }; | ||
224 | |||
225 | static void pmic_irq_mask(unsigned irq) | ||
226 | { | ||
227 | }; | ||
228 | |||
229 | static struct irq_chip pmic_irqchip = { | ||
230 | .name = "PMIC-GPIO", | ||
231 | .mask = pmic_irq_mask, | ||
232 | .unmask = pmic_irq_unmask, | ||
233 | .set_type = pmic_irq_type, | ||
234 | }; | ||
235 | |||
236 | static void pmic_irq_handler(unsigned irq, struct irq_desc *desc) | ||
237 | { | ||
238 | struct pmic_gpio *pg = (struct pmic_gpio *)get_irq_data(irq); | ||
239 | u8 intsts = *((u8 *)pg->gpiointr + 4); | ||
240 | int gpio; | ||
241 | |||
242 | for (gpio = 0; gpio < 8; gpio++) { | ||
243 | if (intsts & (1 << gpio)) { | ||
244 | pr_debug("pmic pin %d triggered\n", gpio); | ||
245 | generic_handle_irq(pg->irq_base + gpio); | ||
246 | } | ||
247 | } | ||
248 | desc->chip->eoi(irq); | ||
249 | } | ||
250 | |||
251 | static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev) | ||
252 | { | ||
253 | struct device *dev = &pdev->dev; | ||
254 | int irq = platform_get_irq(pdev, 0); | ||
255 | struct intel_pmic_gpio_platform_data *pdata = dev->platform_data; | ||
256 | |||
257 | struct pmic_gpio *pg; | ||
258 | int retval; | ||
259 | int i; | ||
260 | |||
261 | if (irq < 0) { | ||
262 | dev_dbg(dev, "no IRQ line\n"); | ||
263 | return -EINVAL; | ||
264 | } | ||
265 | |||
266 | if (!pdata || !pdata->gpio_base || !pdata->irq_base) { | ||
267 | dev_dbg(dev, "incorrect or missing platform data\n"); | ||
268 | return -EINVAL; | ||
269 | } | ||
270 | |||
271 | pg = kzalloc(sizeof(*pg), GFP_KERNEL); | ||
272 | if (!pg) | ||
273 | return -ENOMEM; | ||
274 | |||
275 | dev_set_drvdata(dev, pg); | ||
276 | |||
277 | pg->irq = irq; | ||
278 | /* setting up SRAM mapping for GPIOINT register */ | ||
279 | pg->gpiointr = ioremap_nocache(pdata->gpiointr, 8); | ||
280 | if (!pg->gpiointr) { | ||
281 | printk(KERN_ERR "%s: Can not map GPIOINT.\n", __func__); | ||
282 | retval = -EINVAL; | ||
283 | goto err2; | ||
284 | } | ||
285 | pg->irq_base = pdata->irq_base; | ||
286 | pg->chip.label = "intel_pmic"; | ||
287 | pg->chip.direction_input = pmic_gpio_direction_input; | ||
288 | pg->chip.direction_output = pmic_gpio_direction_output; | ||
289 | pg->chip.get = pmic_gpio_get; | ||
290 | pg->chip.set = pmic_gpio_set; | ||
291 | pg->chip.to_irq = pmic_gpio_to_irq; | ||
292 | pg->chip.base = pdata->gpio_base; | ||
293 | pg->chip.ngpio = NUM_GPIO; | ||
294 | pg->chip.can_sleep = 1; | ||
295 | pg->chip.dev = dev; | ||
296 | |||
297 | INIT_WORK(&pg->irqtypes.work, pmic_irqtype_work); | ||
298 | spin_lock_init(&pg->irqtypes.lock); | ||
299 | |||
300 | pg->chip.dev = dev; | ||
301 | retval = gpiochip_add(&pg->chip); | ||
302 | if (retval) { | ||
303 | printk(KERN_ERR "%s: Can not add pmic gpio chip.\n", __func__); | ||
304 | goto err; | ||
305 | } | ||
306 | set_irq_data(pg->irq, pg); | ||
307 | set_irq_chained_handler(pg->irq, pmic_irq_handler); | ||
308 | for (i = 0; i < 8; i++) { | ||
309 | set_irq_chip_and_handler_name(i + pg->irq_base, &pmic_irqchip, | ||
310 | handle_simple_irq, "demux"); | ||
311 | set_irq_chip_data(i + pg->irq_base, pg); | ||
312 | } | ||
313 | return 0; | ||
314 | err: | ||
315 | iounmap(pg->gpiointr); | ||
316 | err2: | ||
317 | kfree(pg); | ||
318 | return retval; | ||
319 | } | ||
320 | |||
321 | /* at the same time, register a platform driver | ||
322 | * this supports the sfi 0.81 fw */ | ||
323 | static struct platform_driver platform_pmic_gpio_driver = { | ||
324 | .driver = { | ||
325 | .name = DRIVER_NAME, | ||
326 | .owner = THIS_MODULE, | ||
327 | }, | ||
328 | .probe = platform_pmic_gpio_probe, | ||
329 | }; | ||
330 | |||
331 | static int __init platform_pmic_gpio_init(void) | ||
332 | { | ||
333 | return platform_driver_register(&platform_pmic_gpio_driver); | ||
334 | } | ||
335 | |||
336 | subsys_initcall(platform_pmic_gpio_init); | ||
337 | |||
338 | MODULE_AUTHOR("Alek Du <alek.du@intel.com>"); | ||
339 | MODULE_DESCRIPTION("Intel Moorestown PMIC GPIO driver"); | ||
340 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/staging/rar_register/rar_register.c b/drivers/platform/x86/intel_rar_register.c index 618503f422ef..73f8e6d72669 100644 --- a/drivers/staging/rar_register/rar_register.c +++ b/drivers/platform/x86/intel_rar_register.c | |||
@@ -40,15 +40,12 @@ | |||
40 | * Initial publish | 40 | * Initial publish |
41 | */ | 41 | */ |
42 | 42 | ||
43 | #define DEBUG 1 | ||
44 | |||
45 | #include "rar_register.h" | ||
46 | |||
47 | #include <linux/module.h> | 43 | #include <linux/module.h> |
48 | #include <linux/pci.h> | 44 | #include <linux/pci.h> |
49 | #include <linux/spinlock.h> | 45 | #include <linux/spinlock.h> |
50 | #include <linux/device.h> | 46 | #include <linux/device.h> |
51 | #include <linux/kernel.h> | 47 | #include <linux/kernel.h> |
48 | #include <linux/rar_register.h> | ||
52 | 49 | ||
53 | /* === Lincroft Message Bus Interface === */ | 50 | /* === Lincroft Message Bus Interface === */ |
54 | #define LNC_MCR_OFFSET 0xD0 /* Message Control Register */ | 51 | #define LNC_MCR_OFFSET 0xD0 /* Message Control Register */ |
@@ -155,7 +152,6 @@ static struct rar_device *_rar_to_device(int rar, int *off) | |||
155 | return NULL; | 152 | return NULL; |
156 | } | 153 | } |
157 | 154 | ||
158 | |||
159 | /** | 155 | /** |
160 | * rar_to_device - return the device handling this RAR | 156 | * rar_to_device - return the device handling this RAR |
161 | * @rar: RAR number | 157 | * @rar: RAR number |
@@ -496,7 +492,7 @@ EXPORT_SYMBOL(rar_lock); | |||
496 | * a driver that do require a valid RAR address. One of those | 492 | * a driver that do require a valid RAR address. One of those |
497 | * steps would be to call rar_get_address() | 493 | * steps would be to call rar_get_address() |
498 | * | 494 | * |
499 | * This function return 0 on success an error code on failure. | 495 | * This function return 0 on success or an error code on failure. |
500 | */ | 496 | */ |
501 | int register_rar(int num, int (*callback)(unsigned long data), | 497 | int register_rar(int num, int (*callback)(unsigned long data), |
502 | unsigned long data) | 498 | unsigned long data) |
diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index bb2f1fba637b..943f9084dcb1 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c | |||
@@ -23,7 +23,7 @@ | |||
23 | #include <linux/pm.h> | 23 | #include <linux/pm.h> |
24 | #include <linux/pci.h> | 24 | #include <linux/pci.h> |
25 | #include <linux/interrupt.h> | 25 | #include <linux/interrupt.h> |
26 | #include <asm/setup.h> | 26 | #include <asm/mrst.h> |
27 | #include <asm/intel_scu_ipc.h> | 27 | #include <asm/intel_scu_ipc.h> |
28 | 28 | ||
29 | /* IPC defines the following message types */ | 29 | /* IPC defines the following message types */ |
@@ -38,10 +38,6 @@ | |||
38 | #define IPC_CMD_PCNTRL_R 1 /* Register read */ | 38 | #define IPC_CMD_PCNTRL_R 1 /* Register read */ |
39 | #define IPC_CMD_PCNTRL_M 2 /* Register read-modify-write */ | 39 | #define IPC_CMD_PCNTRL_M 2 /* Register read-modify-write */ |
40 | 40 | ||
41 | /* Miscelaneous Command ids */ | ||
42 | #define IPC_CMD_INDIRECT_RD 2 /* 32bit indirect read */ | ||
43 | #define IPC_CMD_INDIRECT_WR 5 /* 32bit indirect write */ | ||
44 | |||
45 | /* | 41 | /* |
46 | * IPC register summary | 42 | * IPC register summary |
47 | * | 43 | * |
@@ -62,8 +58,8 @@ | |||
62 | 58 | ||
63 | #define IPC_BASE_ADDR 0xFF11C000 /* IPC1 base register address */ | 59 | #define IPC_BASE_ADDR 0xFF11C000 /* IPC1 base register address */ |
64 | #define IPC_MAX_ADDR 0x100 /* Maximum IPC regisers */ | 60 | #define IPC_MAX_ADDR 0x100 /* Maximum IPC regisers */ |
65 | #define IPC_WWBUF_SIZE 16 /* IPC Write buffer Size */ | 61 | #define IPC_WWBUF_SIZE 20 /* IPC Write buffer Size */ |
66 | #define IPC_RWBUF_SIZE 16 /* IPC Read buffer Size */ | 62 | #define IPC_RWBUF_SIZE 20 /* IPC Read buffer Size */ |
67 | #define IPC_I2C_BASE 0xFF12B000 /* I2C control register base address */ | 63 | #define IPC_I2C_BASE 0xFF12B000 /* I2C control register base address */ |
68 | #define IPC_I2C_MAX_ADDR 0x10 /* Maximum I2C regisers */ | 64 | #define IPC_I2C_MAX_ADDR 0x10 /* Maximum I2C regisers */ |
69 | 65 | ||
@@ -78,12 +74,7 @@ struct intel_scu_ipc_dev { | |||
78 | 74 | ||
79 | static struct intel_scu_ipc_dev ipcdev; /* Only one for now */ | 75 | static struct intel_scu_ipc_dev ipcdev; /* Only one for now */ |
80 | 76 | ||
81 | static int platform = 1; | 77 | static int platform; /* Platform type */ |
82 | module_param(platform, int, 0); | ||
83 | MODULE_PARM_DESC(platform, "1 for moorestown platform"); | ||
84 | |||
85 | |||
86 | |||
87 | 78 | ||
88 | /* | 79 | /* |
89 | * IPC Read Buffer (Read Only): | 80 | * IPC Read Buffer (Read Only): |
@@ -119,24 +110,6 @@ static inline void ipc_data_writel(u32 data, u32 offset) /* Write ipc data */ | |||
119 | } | 110 | } |
120 | 111 | ||
121 | /* | 112 | /* |
122 | * IPC destination Pointer (Write Only): | ||
123 | * Use content as pointer for destination write | ||
124 | */ | ||
125 | static inline void ipc_write_dptr(u32 data) /* Write dptr data */ | ||
126 | { | ||
127 | writel(data, ipcdev.ipc_base + 0x0C); | ||
128 | } | ||
129 | |||
130 | /* | ||
131 | * IPC Source Pointer (Write Only): | ||
132 | * Use content as pointer for read location | ||
133 | */ | ||
134 | static inline void ipc_write_sptr(u32 data) /* Write dptr data */ | ||
135 | { | ||
136 | writel(data, ipcdev.ipc_base + 0x08); | ||
137 | } | ||
138 | |||
139 | /* | ||
140 | * Status Register (Read Only): | 113 | * Status Register (Read Only): |
141 | * Driver will read this register to get the ready/busy status of the IPC | 114 | * Driver will read this register to get the ready/busy status of the IPC |
142 | * block and error status of the IPC command that was just processed by SCU | 115 | * block and error status of the IPC command that was just processed by SCU |
@@ -154,7 +127,7 @@ static inline u8 ipc_data_readb(u32 offset) /* Read ipc byte data */ | |||
154 | return readb(ipcdev.ipc_base + IPC_READ_BUFFER + offset); | 127 | return readb(ipcdev.ipc_base + IPC_READ_BUFFER + offset); |
155 | } | 128 | } |
156 | 129 | ||
157 | static inline u8 ipc_data_readl(u32 offset) /* Read ipc u32 data */ | 130 | static inline u32 ipc_data_readl(u32 offset) /* Read ipc u32 data */ |
158 | { | 131 | { |
159 | return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset); | 132 | return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset); |
160 | } | 133 | } |
@@ -175,62 +148,73 @@ static inline int busy_loop(void) /* Wait till scu status is busy */ | |||
175 | return -ETIMEDOUT; | 148 | return -ETIMEDOUT; |
176 | } | 149 | } |
177 | } | 150 | } |
178 | return (status >> 1) & 1; | 151 | if ((status >> 1) & 1) |
152 | return -EIO; | ||
153 | |||
154 | return 0; | ||
179 | } | 155 | } |
180 | 156 | ||
181 | /* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */ | 157 | /* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */ |
182 | static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) | 158 | static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) |
183 | { | 159 | { |
184 | int nc; | 160 | int i, nc, bytes, d; |
185 | u32 offset = 0; | 161 | u32 offset = 0; |
186 | u32 err = 0; | 162 | u32 err = 0; |
187 | u8 cbuf[IPC_WWBUF_SIZE] = { '\0' }; | 163 | u8 cbuf[IPC_WWBUF_SIZE] = { }; |
188 | u32 *wbuf = (u32 *)&cbuf; | 164 | u32 *wbuf = (u32 *)&cbuf; |
189 | 165 | ||
190 | mutex_lock(&ipclock); | 166 | mutex_lock(&ipclock); |
167 | |||
168 | memset(cbuf, 0, sizeof(cbuf)); | ||
169 | |||
191 | if (ipcdev.pdev == NULL) { | 170 | if (ipcdev.pdev == NULL) { |
192 | mutex_unlock(&ipclock); | 171 | mutex_unlock(&ipclock); |
193 | return -ENODEV; | 172 | return -ENODEV; |
194 | } | 173 | } |
195 | 174 | ||
196 | if (platform == 1) { | 175 | if (platform != MRST_CPU_CHIP_PENWELL) { |
197 | /* Entry is 4 bytes for read/write, 5 bytes for read modify */ | 176 | bytes = 0; |
198 | for (nc = 0; nc < count; nc++) { | 177 | d = 0; |
178 | for (i = 0; i < count; i++) { | ||
179 | cbuf[bytes++] = addr[i]; | ||
180 | cbuf[bytes++] = addr[i] >> 8; | ||
181 | if (id != IPC_CMD_PCNTRL_R) | ||
182 | cbuf[bytes++] = data[d++]; | ||
183 | if (id == IPC_CMD_PCNTRL_M) | ||
184 | cbuf[bytes++] = data[d++]; | ||
185 | } | ||
186 | for (i = 0; i < bytes; i += 4) | ||
187 | ipc_data_writel(wbuf[i/4], i); | ||
188 | ipc_command(bytes << 16 | id << 12 | 0 << 8 | op); | ||
189 | } else { | ||
190 | for (nc = 0; nc < count; nc++, offset += 2) { | ||
199 | cbuf[offset] = addr[nc]; | 191 | cbuf[offset] = addr[nc]; |
200 | cbuf[offset + 1] = addr[nc] >> 8; | 192 | cbuf[offset + 1] = addr[nc] >> 8; |
201 | if (id != IPC_CMD_PCNTRL_R) | ||
202 | cbuf[offset + 2] = data[nc]; | ||
203 | if (id == IPC_CMD_PCNTRL_M) { | ||
204 | cbuf[offset + 3] = data[nc + 1]; | ||
205 | offset += 1; | ||
206 | } | ||
207 | offset += 3; | ||
208 | } | 193 | } |
209 | for (nc = 0, offset = 0; nc < count; nc++, offset += 4) | ||
210 | ipc_data_writel(wbuf[nc], offset); /* Write wbuff */ | ||
211 | 194 | ||
212 | } else { | 195 | if (id == IPC_CMD_PCNTRL_R) { |
213 | for (nc = 0, offset = 0; nc < count; nc++, offset += 2) | 196 | for (nc = 0, offset = 0; nc < count; nc++, offset += 4) |
214 | ipc_data_writel(addr[nc], offset); /* Write addresses */ | 197 | ipc_data_writel(wbuf[nc], offset); |
215 | if (id != IPC_CMD_PCNTRL_R) { | 198 | ipc_command((count*2) << 16 | id << 12 | 0 << 8 | op); |
216 | for (nc = 0; nc < count; nc++, offset++) | 199 | } else if (id == IPC_CMD_PCNTRL_W) { |
217 | ipc_data_writel(data[nc], offset); /* Write data */ | 200 | for (nc = 0; nc < count; nc++, offset += 1) |
218 | if (id == IPC_CMD_PCNTRL_M) | 201 | cbuf[offset] = data[nc]; |
219 | ipc_data_writel(data[nc + 1], offset); /* Mask value*/ | 202 | for (nc = 0, offset = 0; nc < count; nc++, offset += 4) |
203 | ipc_data_writel(wbuf[nc], offset); | ||
204 | ipc_command((count*3) << 16 | id << 12 | 0 << 8 | op); | ||
205 | } else if (id == IPC_CMD_PCNTRL_M) { | ||
206 | cbuf[offset] = data[0]; | ||
207 | cbuf[offset + 1] = data[1]; | ||
208 | ipc_data_writel(wbuf[0], 0); /* Write wbuff */ | ||
209 | ipc_command(4 << 16 | id << 12 | 0 << 8 | op); | ||
220 | } | 210 | } |
221 | } | 211 | } |
222 | 212 | ||
223 | if (id != IPC_CMD_PCNTRL_M) | ||
224 | ipc_command((count * 3) << 16 | id << 12 | 0 << 8 | op); | ||
225 | else | ||
226 | ipc_command((count * 4) << 16 | id << 12 | 0 << 8 | op); | ||
227 | |||
228 | err = busy_loop(); | 213 | err = busy_loop(); |
229 | |||
230 | if (id == IPC_CMD_PCNTRL_R) { /* Read rbuf */ | 214 | if (id == IPC_CMD_PCNTRL_R) { /* Read rbuf */ |
231 | /* Workaround: values are read as 0 without memcpy_fromio */ | 215 | /* Workaround: values are read as 0 without memcpy_fromio */ |
232 | memcpy_fromio(cbuf, ipcdev.ipc_base + IPC_READ_BUFFER, 16); | 216 | memcpy_fromio(cbuf, ipcdev.ipc_base + 0x90, 16); |
233 | if (platform == 1) { | 217 | if (platform != MRST_CPU_CHIP_PENWELL) { |
234 | for (nc = 0, offset = 2; nc < count; nc++, offset += 3) | 218 | for (nc = 0, offset = 2; nc < count; nc++, offset += 3) |
235 | data[nc] = ipc_data_readb(offset); | 219 | data[nc] = ipc_data_readb(offset); |
236 | } else { | 220 | } else { |
@@ -405,70 +389,6 @@ int intel_scu_ipc_update_register(u16 addr, u8 bits, u8 mask) | |||
405 | EXPORT_SYMBOL(intel_scu_ipc_update_register); | 389 | EXPORT_SYMBOL(intel_scu_ipc_update_register); |
406 | 390 | ||
407 | /** | 391 | /** |
408 | * intel_scu_ipc_register_read - 32bit indirect read | ||
409 | * @addr: register address | ||
410 | * @value: 32bit value return | ||
411 | * | ||
412 | * Performs IA 32 bit indirect read, returns 0 on success, or an | ||
413 | * error code. | ||
414 | * | ||
415 | * Can be used when SCCB(System Controller Configuration Block) register | ||
416 | * HRIM(Honor Restricted IPC Messages) is set (bit 23) | ||
417 | * | ||
418 | * This function may sleep. Locking for SCU accesses is handled for | ||
419 | * the caller. | ||
420 | */ | ||
421 | int intel_scu_ipc_register_read(u32 addr, u32 *value) | ||
422 | { | ||
423 | u32 err = 0; | ||
424 | |||
425 | mutex_lock(&ipclock); | ||
426 | if (ipcdev.pdev == NULL) { | ||
427 | mutex_unlock(&ipclock); | ||
428 | return -ENODEV; | ||
429 | } | ||
430 | ipc_write_sptr(addr); | ||
431 | ipc_command(4 << 16 | IPC_CMD_INDIRECT_RD); | ||
432 | err = busy_loop(); | ||
433 | *value = ipc_data_readl(0); | ||
434 | mutex_unlock(&ipclock); | ||
435 | return err; | ||
436 | } | ||
437 | EXPORT_SYMBOL(intel_scu_ipc_register_read); | ||
438 | |||
439 | /** | ||
440 | * intel_scu_ipc_register_write - 32bit indirect write | ||
441 | * @addr: register address | ||
442 | * @value: 32bit value to write | ||
443 | * | ||
444 | * Performs IA 32 bit indirect write, returns 0 on success, or an | ||
445 | * error code. | ||
446 | * | ||
447 | * Can be used when SCCB(System Controller Configuration Block) register | ||
448 | * HRIM(Honor Restricted IPC Messages) is set (bit 23) | ||
449 | * | ||
450 | * This function may sleep. Locking for SCU accesses is handled for | ||
451 | * the caller. | ||
452 | */ | ||
453 | int intel_scu_ipc_register_write(u32 addr, u32 value) | ||
454 | { | ||
455 | u32 err = 0; | ||
456 | |||
457 | mutex_lock(&ipclock); | ||
458 | if (ipcdev.pdev == NULL) { | ||
459 | mutex_unlock(&ipclock); | ||
460 | return -ENODEV; | ||
461 | } | ||
462 | ipc_write_dptr(addr); | ||
463 | ipc_data_writel(value, 0); | ||
464 | ipc_command(4 << 16 | IPC_CMD_INDIRECT_WR); | ||
465 | err = busy_loop(); | ||
466 | mutex_unlock(&ipclock); | ||
467 | return err; | ||
468 | } | ||
469 | EXPORT_SYMBOL(intel_scu_ipc_register_write); | ||
470 | |||
471 | /** | ||
472 | * intel_scu_ipc_simple_command - send a simple command | 392 | * intel_scu_ipc_simple_command - send a simple command |
473 | * @cmd: command | 393 | * @cmd: command |
474 | * @sub: sub type | 394 | * @sub: sub type |
@@ -524,7 +444,7 @@ int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen, | |||
524 | for (i = 0; i < inlen; i++) | 444 | for (i = 0; i < inlen; i++) |
525 | ipc_data_writel(*in++, 4 * i); | 445 | ipc_data_writel(*in++, 4 * i); |
526 | 446 | ||
527 | ipc_command((sub << 12) | cmd | (inlen << 18)); | 447 | ipc_command((inlen << 16) | (sub << 12) | cmd); |
528 | err = busy_loop(); | 448 | err = busy_loop(); |
529 | 449 | ||
530 | for (i = 0; i < outlen; i++) | 450 | for (i = 0; i < outlen; i++) |
@@ -803,6 +723,7 @@ static void ipc_remove(struct pci_dev *pdev) | |||
803 | 723 | ||
804 | static const struct pci_device_id pci_ids[] = { | 724 | static const struct pci_device_id pci_ids[] = { |
805 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080e)}, | 725 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080e)}, |
726 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x082a)}, | ||
806 | { 0,} | 727 | { 0,} |
807 | }; | 728 | }; |
808 | MODULE_DEVICE_TABLE(pci, pci_ids); | 729 | MODULE_DEVICE_TABLE(pci, pci_ids); |
@@ -817,6 +738,9 @@ static struct pci_driver ipc_driver = { | |||
817 | 738 | ||
818 | static int __init intel_scu_ipc_init(void) | 739 | static int __init intel_scu_ipc_init(void) |
819 | { | 740 | { |
741 | platform = mrst_identify_cpu(); | ||
742 | if (platform == 0) | ||
743 | return -ENODEV; | ||
820 | return pci_register_driver(&ipc_driver); | 744 | return pci_register_driver(&ipc_driver); |
821 | } | 745 | } |
822 | 746 | ||
diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index afd762b58ad9..7e9bb6df9d39 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c | |||
@@ -434,7 +434,7 @@ static int dmi_check_cb(const struct dmi_system_id *id) | |||
434 | { | 434 | { |
435 | printk(KERN_INFO "msi-laptop: Identified laptop model '%s'.\n", | 435 | printk(KERN_INFO "msi-laptop: Identified laptop model '%s'.\n", |
436 | id->ident); | 436 | id->ident); |
437 | return 0; | 437 | return 1; |
438 | } | 438 | } |
439 | 439 | ||
440 | static struct dmi_system_id __initdata msi_dmi_table[] = { | 440 | static struct dmi_system_id __initdata msi_dmi_table[] = { |
@@ -562,15 +562,15 @@ static int rfkill_threeg_set(void *data, bool blocked) | |||
562 | return 0; | 562 | return 0; |
563 | } | 563 | } |
564 | 564 | ||
565 | static struct rfkill_ops rfkill_bluetooth_ops = { | 565 | static const struct rfkill_ops rfkill_bluetooth_ops = { |
566 | .set_block = rfkill_bluetooth_set | 566 | .set_block = rfkill_bluetooth_set |
567 | }; | 567 | }; |
568 | 568 | ||
569 | static struct rfkill_ops rfkill_wlan_ops = { | 569 | static const struct rfkill_ops rfkill_wlan_ops = { |
570 | .set_block = rfkill_wlan_set | 570 | .set_block = rfkill_wlan_set |
571 | }; | 571 | }; |
572 | 572 | ||
573 | static struct rfkill_ops rfkill_threeg_ops = { | 573 | static const struct rfkill_ops rfkill_threeg_ops = { |
574 | .set_block = rfkill_threeg_set | 574 | .set_block = rfkill_threeg_set |
575 | }; | 575 | }; |
576 | 576 | ||
diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c index d1736009636f..42a5469a2459 100644 --- a/drivers/platform/x86/msi-wmi.c +++ b/drivers/platform/x86/msi-wmi.c | |||
@@ -57,7 +57,7 @@ static struct key_entry msi_wmi_keymap[] = { | |||
57 | }; | 57 | }; |
58 | static ktime_t last_pressed[ARRAY_SIZE(msi_wmi_keymap) - 1]; | 58 | static ktime_t last_pressed[ARRAY_SIZE(msi_wmi_keymap) - 1]; |
59 | 59 | ||
60 | struct backlight_device *backlight; | 60 | static struct backlight_device *backlight; |
61 | 61 | ||
62 | static int backlight_map[] = { 0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF }; | 62 | static int backlight_map[] = { 0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF }; |
63 | 63 | ||
diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index 2fb9a32926f8..ec01c3d8fc5a 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c | |||
@@ -248,7 +248,7 @@ static int acpi_pcc_write_sset(struct pcc_acpi *pcc, int func, int val) | |||
248 | status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SSET, | 248 | status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SSET, |
249 | ¶ms, NULL); | 249 | ¶ms, NULL); |
250 | 250 | ||
251 | return status == AE_OK; | 251 | return (status == AE_OK) ? 0 : -EIO; |
252 | } | 252 | } |
253 | 253 | ||
254 | static inline int acpi_pcc_get_sqty(struct acpi_device *device) | 254 | static inline int acpi_pcc_get_sqty(struct acpi_device *device) |
@@ -586,7 +586,6 @@ static int acpi_pcc_init_input(struct pcc_acpi *pcc) | |||
586 | static int acpi_pcc_hotkey_resume(struct acpi_device *device) | 586 | static int acpi_pcc_hotkey_resume(struct acpi_device *device) |
587 | { | 587 | { |
588 | struct pcc_acpi *pcc = acpi_driver_data(device); | 588 | struct pcc_acpi *pcc = acpi_driver_data(device); |
589 | acpi_status status = AE_OK; | ||
590 | 589 | ||
591 | if (device == NULL || pcc == NULL) | 590 | if (device == NULL || pcc == NULL) |
592 | return -EINVAL; | 591 | return -EINVAL; |
@@ -594,9 +593,7 @@ static int acpi_pcc_hotkey_resume(struct acpi_device *device) | |||
594 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Sticky mode restore: %d\n", | 593 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Sticky mode restore: %d\n", |
595 | pcc->sticky_mode)); | 594 | pcc->sticky_mode)); |
596 | 595 | ||
597 | status = acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_mode); | 596 | return acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_mode); |
598 | |||
599 | return status == AE_OK ? 0 : -EINVAL; | ||
600 | } | 597 | } |
601 | 598 | ||
602 | static int acpi_pcc_hotkey_add(struct acpi_device *device) | 599 | static int acpi_pcc_hotkey_add(struct acpi_device *device) |
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 1387c5f9c24d..e3154ff7a39f 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c | |||
@@ -561,8 +561,7 @@ static void sony_pf_remove(void) | |||
561 | if (!atomic_dec_and_test(&sony_pf_users)) | 561 | if (!atomic_dec_and_test(&sony_pf_users)) |
562 | return; | 562 | return; |
563 | 563 | ||
564 | platform_device_del(sony_pf_device); | 564 | platform_device_unregister(sony_pf_device); |
565 | platform_device_put(sony_pf_device); | ||
566 | platform_driver_unregister(&sony_pf_driver); | 565 | platform_driver_unregister(&sony_pf_driver); |
567 | } | 566 | } |
568 | 567 | ||
@@ -1196,9 +1195,13 @@ static void sony_nc_rfkill_setup(struct acpi_device *device) | |||
1196 | } | 1195 | } |
1197 | 1196 | ||
1198 | device_enum = (union acpi_object *) buffer.pointer; | 1197 | device_enum = (union acpi_object *) buffer.pointer; |
1199 | if (!device_enum || device_enum->type != ACPI_TYPE_BUFFER) { | 1198 | if (!device_enum) { |
1200 | printk(KERN_ERR "Invalid SN06 return object 0x%.2x\n", | 1199 | pr_err("Invalid SN06 return object\n"); |
1201 | device_enum->type); | 1200 | goto out_no_enum; |
1201 | } | ||
1202 | if (device_enum->type != ACPI_TYPE_BUFFER) { | ||
1203 | pr_err("Invalid SN06 return object type 0x%.2x\n", | ||
1204 | device_enum->type); | ||
1202 | goto out_no_enum; | 1205 | goto out_no_enum; |
1203 | } | 1206 | } |
1204 | 1207 | ||
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 4bdb13796e24..5d6119bed00c 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
@@ -5838,75 +5838,6 @@ static struct ibm_struct thermal_driver_data = { | |||
5838 | }; | 5838 | }; |
5839 | 5839 | ||
5840 | /************************************************************************* | 5840 | /************************************************************************* |
5841 | * EC Dump subdriver | ||
5842 | */ | ||
5843 | |||
5844 | static u8 ecdump_regs[256]; | ||
5845 | |||
5846 | static int ecdump_read(struct seq_file *m) | ||
5847 | { | ||
5848 | int i, j; | ||
5849 | u8 v; | ||
5850 | |||
5851 | seq_printf(m, "EC " | ||
5852 | " +00 +01 +02 +03 +04 +05 +06 +07" | ||
5853 | " +08 +09 +0a +0b +0c +0d +0e +0f\n"); | ||
5854 | for (i = 0; i < 256; i += 16) { | ||
5855 | seq_printf(m, "EC 0x%02x:", i); | ||
5856 | for (j = 0; j < 16; j++) { | ||
5857 | if (!acpi_ec_read(i + j, &v)) | ||
5858 | break; | ||
5859 | if (v != ecdump_regs[i + j]) | ||
5860 | seq_printf(m, " *%02x", v); | ||
5861 | else | ||
5862 | seq_printf(m, " %02x", v); | ||
5863 | ecdump_regs[i + j] = v; | ||
5864 | } | ||
5865 | seq_putc(m, '\n'); | ||
5866 | if (j != 16) | ||
5867 | break; | ||
5868 | } | ||
5869 | |||
5870 | /* These are way too dangerous to advertise openly... */ | ||
5871 | #if 0 | ||
5872 | seq_printf(m, "commands:\t0x<offset> 0x<value>" | ||
5873 | " (<offset> is 00-ff, <value> is 00-ff)\n"); | ||
5874 | seq_printf(m, "commands:\t0x<offset> <value> " | ||
5875 | " (<offset> is 00-ff, <value> is 0-255)\n"); | ||
5876 | #endif | ||
5877 | return 0; | ||
5878 | } | ||
5879 | |||
5880 | static int ecdump_write(char *buf) | ||
5881 | { | ||
5882 | char *cmd; | ||
5883 | int i, v; | ||
5884 | |||
5885 | while ((cmd = next_cmd(&buf))) { | ||
5886 | if (sscanf(cmd, "0x%x 0x%x", &i, &v) == 2) { | ||
5887 | /* i and v set */ | ||
5888 | } else if (sscanf(cmd, "0x%x %u", &i, &v) == 2) { | ||
5889 | /* i and v set */ | ||
5890 | } else | ||
5891 | return -EINVAL; | ||
5892 | if (i >= 0 && i < 256 && v >= 0 && v < 256) { | ||
5893 | if (!acpi_ec_write(i, v)) | ||
5894 | return -EIO; | ||
5895 | } else | ||
5896 | return -EINVAL; | ||
5897 | } | ||
5898 | |||
5899 | return 0; | ||
5900 | } | ||
5901 | |||
5902 | static struct ibm_struct ecdump_driver_data = { | ||
5903 | .name = "ecdump", | ||
5904 | .read = ecdump_read, | ||
5905 | .write = ecdump_write, | ||
5906 | .flags.experimental = 1, | ||
5907 | }; | ||
5908 | |||
5909 | /************************************************************************* | ||
5910 | * Backlight/brightness subdriver | 5841 | * Backlight/brightness subdriver |
5911 | */ | 5842 | */ |
5912 | 5843 | ||
@@ -8883,9 +8814,6 @@ static struct ibm_init_struct ibms_init[] __initdata = { | |||
8883 | .data = &thermal_driver_data, | 8814 | .data = &thermal_driver_data, |
8884 | }, | 8815 | }, |
8885 | { | 8816 | { |
8886 | .data = &ecdump_driver_data, | ||
8887 | }, | ||
8888 | { | ||
8889 | .init = brightness_init, | 8817 | .init = brightness_init, |
8890 | .data = &brightness_driver_data, | 8818 | .data = &brightness_driver_data, |
8891 | }, | 8819 | }, |
@@ -8993,7 +8921,6 @@ TPACPI_PARAM(light); | |||
8993 | TPACPI_PARAM(cmos); | 8921 | TPACPI_PARAM(cmos); |
8994 | TPACPI_PARAM(led); | 8922 | TPACPI_PARAM(led); |
8995 | TPACPI_PARAM(beep); | 8923 | TPACPI_PARAM(beep); |
8996 | TPACPI_PARAM(ecdump); | ||
8997 | TPACPI_PARAM(brightness); | 8924 | TPACPI_PARAM(brightness); |
8998 | TPACPI_PARAM(volume); | 8925 | TPACPI_PARAM(volume); |
8999 | TPACPI_PARAM(fan); | 8926 | TPACPI_PARAM(fan); |
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 37aa14798551..7d67a45bb2b0 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c | |||
@@ -4,6 +4,7 @@ | |||
4 | * | 4 | * |
5 | * Copyright (C) 2002-2004 John Belmonte | 5 | * Copyright (C) 2002-2004 John Belmonte |
6 | * Copyright (C) 2008 Philip Langdale | 6 | * Copyright (C) 2008 Philip Langdale |
7 | * Copyright (C) 2010 Pierre Ducroquet | ||
7 | * | 8 | * |
8 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by | 10 | * it under the terms of the GNU General Public License as published by |
@@ -47,6 +48,7 @@ | |||
47 | #include <linux/platform_device.h> | 48 | #include <linux/platform_device.h> |
48 | #include <linux/rfkill.h> | 49 | #include <linux/rfkill.h> |
49 | #include <linux/input.h> | 50 | #include <linux/input.h> |
51 | #include <linux/leds.h> | ||
50 | #include <linux/slab.h> | 52 | #include <linux/slab.h> |
51 | 53 | ||
52 | #include <asm/uaccess.h> | 54 | #include <asm/uaccess.h> |
@@ -129,6 +131,8 @@ enum {KE_KEY, KE_END}; | |||
129 | 131 | ||
130 | static struct key_entry toshiba_acpi_keymap[] = { | 132 | static struct key_entry toshiba_acpi_keymap[] = { |
131 | {KE_KEY, 0x101, KEY_MUTE}, | 133 | {KE_KEY, 0x101, KEY_MUTE}, |
134 | {KE_KEY, 0x102, KEY_ZOOMOUT}, | ||
135 | {KE_KEY, 0x103, KEY_ZOOMIN}, | ||
132 | {KE_KEY, 0x13b, KEY_COFFEE}, | 136 | {KE_KEY, 0x13b, KEY_COFFEE}, |
133 | {KE_KEY, 0x13c, KEY_BATTERY}, | 137 | {KE_KEY, 0x13c, KEY_BATTERY}, |
134 | {KE_KEY, 0x13d, KEY_SLEEP}, | 138 | {KE_KEY, 0x13d, KEY_SLEEP}, |
@@ -285,6 +289,7 @@ struct toshiba_acpi_dev { | |||
285 | struct platform_device *p_dev; | 289 | struct platform_device *p_dev; |
286 | struct rfkill *bt_rfk; | 290 | struct rfkill *bt_rfk; |
287 | struct input_dev *hotkey_dev; | 291 | struct input_dev *hotkey_dev; |
292 | int illumination_installed; | ||
288 | acpi_handle handle; | 293 | acpi_handle handle; |
289 | 294 | ||
290 | const char *bt_name; | 295 | const char *bt_name; |
@@ -292,6 +297,110 @@ struct toshiba_acpi_dev { | |||
292 | struct mutex mutex; | 297 | struct mutex mutex; |
293 | }; | 298 | }; |
294 | 299 | ||
300 | /* Illumination support */ | ||
301 | static int toshiba_illumination_available(void) | ||
302 | { | ||
303 | u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; | ||
304 | u32 out[HCI_WORDS]; | ||
305 | acpi_status status; | ||
306 | |||
307 | in[0] = 0xf100; | ||
308 | status = hci_raw(in, out); | ||
309 | if (ACPI_FAILURE(status)) { | ||
310 | printk(MY_INFO "Illumination device not available\n"); | ||
311 | return 0; | ||
312 | } | ||
313 | in[0] = 0xf400; | ||
314 | status = hci_raw(in, out); | ||
315 | return 1; | ||
316 | } | ||
317 | |||
318 | static void toshiba_illumination_set(struct led_classdev *cdev, | ||
319 | enum led_brightness brightness) | ||
320 | { | ||
321 | u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; | ||
322 | u32 out[HCI_WORDS]; | ||
323 | acpi_status status; | ||
324 | |||
325 | /* First request : initialize communication. */ | ||
326 | in[0] = 0xf100; | ||
327 | status = hci_raw(in, out); | ||
328 | if (ACPI_FAILURE(status)) { | ||
329 | printk(MY_INFO "Illumination device not available\n"); | ||
330 | return; | ||
331 | } | ||
332 | |||
333 | if (brightness) { | ||
334 | /* Switch the illumination on */ | ||
335 | in[0] = 0xf400; | ||
336 | in[1] = 0x14e; | ||
337 | in[2] = 1; | ||
338 | status = hci_raw(in, out); | ||
339 | if (ACPI_FAILURE(status)) { | ||
340 | printk(MY_INFO "ACPI call for illumination failed.\n"); | ||
341 | return; | ||
342 | } | ||
343 | } else { | ||
344 | /* Switch the illumination off */ | ||
345 | in[0] = 0xf400; | ||
346 | in[1] = 0x14e; | ||
347 | in[2] = 0; | ||
348 | status = hci_raw(in, out); | ||
349 | if (ACPI_FAILURE(status)) { | ||
350 | printk(MY_INFO "ACPI call for illumination failed.\n"); | ||
351 | return; | ||
352 | } | ||
353 | } | ||
354 | |||
355 | /* Last request : close communication. */ | ||
356 | in[0] = 0xf200; | ||
357 | in[1] = 0; | ||
358 | in[2] = 0; | ||
359 | hci_raw(in, out); | ||
360 | } | ||
361 | |||
362 | static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) | ||
363 | { | ||
364 | u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; | ||
365 | u32 out[HCI_WORDS]; | ||
366 | acpi_status status; | ||
367 | enum led_brightness result; | ||
368 | |||
369 | /*Â First request : initialize communication. */ | ||
370 | in[0] = 0xf100; | ||
371 | status = hci_raw(in, out); | ||
372 | if (ACPI_FAILURE(status)) { | ||
373 | printk(MY_INFO "Illumination device not available\n"); | ||
374 | return LED_OFF; | ||
375 | } | ||
376 | |||
377 | /* Check the illumination */ | ||
378 | in[0] = 0xf300; | ||
379 | in[1] = 0x14e; | ||
380 | status = hci_raw(in, out); | ||
381 | if (ACPI_FAILURE(status)) { | ||
382 | printk(MY_INFO "ACPI call for illumination failed.\n"); | ||
383 | return LED_OFF; | ||
384 | } | ||
385 | |||
386 | result = out[2] ? LED_FULL : LED_OFF; | ||
387 | |||
388 | /* Last request : close communication. */ | ||
389 | in[0] = 0xf200; | ||
390 | in[1] = 0; | ||
391 | in[2] = 0; | ||
392 | hci_raw(in, out); | ||
393 | |||
394 | return result; | ||
395 | } | ||
396 | |||
397 | static struct led_classdev toshiba_led = { | ||
398 | .name = "toshiba::illumination", | ||
399 | .max_brightness = 1, | ||
400 | .brightness_set = toshiba_illumination_set, | ||
401 | .brightness_get = toshiba_illumination_get, | ||
402 | }; | ||
403 | |||
295 | static struct toshiba_acpi_dev toshiba_acpi = { | 404 | static struct toshiba_acpi_dev toshiba_acpi = { |
296 | .bt_name = "Toshiba Bluetooth", | 405 | .bt_name = "Toshiba Bluetooth", |
297 | }; | 406 | }; |
@@ -720,25 +829,22 @@ static const struct file_operations version_proc_fops = { | |||
720 | 829 | ||
721 | #define PROC_TOSHIBA "toshiba" | 830 | #define PROC_TOSHIBA "toshiba" |
722 | 831 | ||
723 | static acpi_status __init add_device(void) | 832 | static void __init create_toshiba_proc_entries(void) |
724 | { | 833 | { |
725 | proc_create("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir, &lcd_proc_fops); | 834 | proc_create("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir, &lcd_proc_fops); |
726 | proc_create("video", S_IRUGO | S_IWUSR, toshiba_proc_dir, &video_proc_fops); | 835 | proc_create("video", S_IRUGO | S_IWUSR, toshiba_proc_dir, &video_proc_fops); |
727 | proc_create("fan", S_IRUGO | S_IWUSR, toshiba_proc_dir, &fan_proc_fops); | 836 | proc_create("fan", S_IRUGO | S_IWUSR, toshiba_proc_dir, &fan_proc_fops); |
728 | proc_create("keys", S_IRUGO | S_IWUSR, toshiba_proc_dir, &keys_proc_fops); | 837 | proc_create("keys", S_IRUGO | S_IWUSR, toshiba_proc_dir, &keys_proc_fops); |
729 | proc_create("version", S_IRUGO, toshiba_proc_dir, &version_proc_fops); | 838 | proc_create("version", S_IRUGO, toshiba_proc_dir, &version_proc_fops); |
730 | |||
731 | return AE_OK; | ||
732 | } | 839 | } |
733 | 840 | ||
734 | static acpi_status remove_device(void) | 841 | static void remove_toshiba_proc_entries(void) |
735 | { | 842 | { |
736 | remove_proc_entry("lcd", toshiba_proc_dir); | 843 | remove_proc_entry("lcd", toshiba_proc_dir); |
737 | remove_proc_entry("video", toshiba_proc_dir); | 844 | remove_proc_entry("video", toshiba_proc_dir); |
738 | remove_proc_entry("fan", toshiba_proc_dir); | 845 | remove_proc_entry("fan", toshiba_proc_dir); |
739 | remove_proc_entry("keys", toshiba_proc_dir); | 846 | remove_proc_entry("keys", toshiba_proc_dir); |
740 | remove_proc_entry("version", toshiba_proc_dir); | 847 | remove_proc_entry("version", toshiba_proc_dir); |
741 | return AE_OK; | ||
742 | } | 848 | } |
743 | 849 | ||
744 | static struct backlight_ops toshiba_backlight_data = { | 850 | static struct backlight_ops toshiba_backlight_data = { |
@@ -906,7 +1012,7 @@ static void toshiba_acpi_exit(void) | |||
906 | if (toshiba_backlight_device) | 1012 | if (toshiba_backlight_device) |
907 | backlight_device_unregister(toshiba_backlight_device); | 1013 | backlight_device_unregister(toshiba_backlight_device); |
908 | 1014 | ||
909 | remove_device(); | 1015 | remove_toshiba_proc_entries(); |
910 | 1016 | ||
911 | if (toshiba_proc_dir) | 1017 | if (toshiba_proc_dir) |
912 | remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); | 1018 | remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); |
@@ -914,6 +1020,9 @@ static void toshiba_acpi_exit(void) | |||
914 | acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY, | 1020 | acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY, |
915 | toshiba_acpi_notify); | 1021 | toshiba_acpi_notify); |
916 | 1022 | ||
1023 | if (toshiba_acpi.illumination_installed) | ||
1024 | led_classdev_unregister(&toshiba_led); | ||
1025 | |||
917 | platform_device_unregister(toshiba_acpi.p_dev); | 1026 | platform_device_unregister(toshiba_acpi.p_dev); |
918 | 1027 | ||
919 | return; | 1028 | return; |
@@ -921,7 +1030,6 @@ static void toshiba_acpi_exit(void) | |||
921 | 1030 | ||
922 | static int __init toshiba_acpi_init(void) | 1031 | static int __init toshiba_acpi_init(void) |
923 | { | 1032 | { |
924 | acpi_status status = AE_OK; | ||
925 | u32 hci_result; | 1033 | u32 hci_result; |
926 | bool bt_present; | 1034 | bool bt_present; |
927 | int ret = 0; | 1035 | int ret = 0; |
@@ -969,11 +1077,7 @@ static int __init toshiba_acpi_init(void) | |||
969 | toshiba_acpi_exit(); | 1077 | toshiba_acpi_exit(); |
970 | return -ENODEV; | 1078 | return -ENODEV; |
971 | } else { | 1079 | } else { |
972 | status = add_device(); | 1080 | create_toshiba_proc_entries(); |
973 | if (ACPI_FAILURE(status)) { | ||
974 | toshiba_acpi_exit(); | ||
975 | return -ENODEV; | ||
976 | } | ||
977 | } | 1081 | } |
978 | 1082 | ||
979 | props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; | 1083 | props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; |
@@ -1013,6 +1117,13 @@ static int __init toshiba_acpi_init(void) | |||
1013 | } | 1117 | } |
1014 | } | 1118 | } |
1015 | 1119 | ||
1120 | toshiba_acpi.illumination_installed = 0; | ||
1121 | if (toshiba_illumination_available()) { | ||
1122 | if (!led_classdev_register(&(toshiba_acpi.p_dev->dev), | ||
1123 | &toshiba_led)) | ||
1124 | toshiba_acpi.illumination_installed = 1; | ||
1125 | } | ||
1126 | |||
1016 | return 0; | 1127 | return 0; |
1017 | } | 1128 | } |
1018 | 1129 | ||
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index e4eaa14ed987..b2978a04317f 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c | |||
@@ -518,8 +518,13 @@ static void wmi_notify_debug(u32 value, void *context) | |||
518 | { | 518 | { |
519 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | 519 | struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; |
520 | union acpi_object *obj; | 520 | union acpi_object *obj; |
521 | acpi_status status; | ||
521 | 522 | ||
522 | wmi_get_event_data(value, &response); | 523 | status = wmi_get_event_data(value, &response); |
524 | if (status != AE_OK) { | ||
525 | printk(KERN_INFO "wmi: bad event status 0x%x\n", status); | ||
526 | return; | ||
527 | } | ||
523 | 528 | ||
524 | obj = (union acpi_object *)response.pointer; | 529 | obj = (union acpi_object *)response.pointer; |
525 | 530 | ||
@@ -543,6 +548,7 @@ static void wmi_notify_debug(u32 value, void *context) | |||
543 | default: | 548 | default: |
544 | printk("object type 0x%X\n", obj->type); | 549 | printk("object type 0x%X\n", obj->type); |
545 | } | 550 | } |
551 | kfree(obj); | ||
546 | } | 552 | } |
547 | 553 | ||
548 | /** | 554 | /** |
@@ -804,7 +810,7 @@ static bool guid_already_parsed(const char *guid_string) | |||
804 | /* | 810 | /* |
805 | * Parse the _WDG method for the GUID data blocks | 811 | * Parse the _WDG method for the GUID data blocks |
806 | */ | 812 | */ |
807 | static __init acpi_status parse_wdg(acpi_handle handle) | 813 | static acpi_status parse_wdg(acpi_handle handle) |
808 | { | 814 | { |
809 | struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; | 815 | struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; |
810 | union acpi_object *obj; | 816 | union acpi_object *obj; |
@@ -827,8 +833,10 @@ static __init acpi_status parse_wdg(acpi_handle handle) | |||
827 | total = obj->buffer.length / sizeof(struct guid_block); | 833 | total = obj->buffer.length / sizeof(struct guid_block); |
828 | 834 | ||
829 | gblock = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL); | 835 | gblock = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL); |
830 | if (!gblock) | 836 | if (!gblock) { |
831 | return AE_NO_MEMORY; | 837 | status = AE_NO_MEMORY; |
838 | goto out_free_pointer; | ||
839 | } | ||
832 | 840 | ||
833 | for (i = 0; i < total; i++) { | 841 | for (i = 0; i < total; i++) { |
834 | /* | 842 | /* |
@@ -848,8 +856,10 @@ static __init acpi_status parse_wdg(acpi_handle handle) | |||
848 | wmi_dump_wdg(&gblock[i]); | 856 | wmi_dump_wdg(&gblock[i]); |
849 | 857 | ||
850 | wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); | 858 | wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); |
851 | if (!wblock) | 859 | if (!wblock) { |
852 | return AE_NO_MEMORY; | 860 | status = AE_NO_MEMORY; |
861 | goto out_free_gblock; | ||
862 | } | ||
853 | 863 | ||
854 | wblock->gblock = gblock[i]; | 864 | wblock->gblock = gblock[i]; |
855 | wblock->handle = handle; | 865 | wblock->handle = handle; |
@@ -860,8 +870,10 @@ static __init acpi_status parse_wdg(acpi_handle handle) | |||
860 | list_add_tail(&wblock->list, &wmi_blocks.list); | 870 | list_add_tail(&wblock->list, &wmi_blocks.list); |
861 | } | 871 | } |
862 | 872 | ||
863 | kfree(out.pointer); | 873 | out_free_gblock: |
864 | kfree(gblock); | 874 | kfree(gblock); |
875 | out_free_pointer: | ||
876 | kfree(out.pointer); | ||
865 | 877 | ||
866 | return status; | 878 | return status; |
867 | } | 879 | } |
@@ -947,7 +959,7 @@ static int acpi_wmi_remove(struct acpi_device *device, int type) | |||
947 | return 0; | 959 | return 0; |
948 | } | 960 | } |
949 | 961 | ||
950 | static int __init acpi_wmi_add(struct acpi_device *device) | 962 | static int acpi_wmi_add(struct acpi_device *device) |
951 | { | 963 | { |
952 | acpi_status status; | 964 | acpi_status status; |
953 | int result = 0; | 965 | int result = 0; |
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 929651725855..0e4122ed1b36 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig | |||
@@ -109,8 +109,6 @@ source "drivers/staging/hv/Kconfig" | |||
109 | 109 | ||
110 | source "drivers/staging/vme/Kconfig" | 110 | source "drivers/staging/vme/Kconfig" |
111 | 111 | ||
112 | source "drivers/staging/rar_register/Kconfig" | ||
113 | |||
114 | source "drivers/staging/memrar/Kconfig" | 112 | source "drivers/staging/memrar/Kconfig" |
115 | 113 | ||
116 | source "drivers/staging/sep/Kconfig" | 114 | source "drivers/staging/sep/Kconfig" |
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 6c5b5237ccb9..ecfb0bb990b6 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile | |||
@@ -36,7 +36,6 @@ obj-$(CONFIG_VT6656) += vt6656/ | |||
36 | obj-$(CONFIG_FB_UDL) += udlfb/ | 36 | obj-$(CONFIG_FB_UDL) += udlfb/ |
37 | obj-$(CONFIG_HYPERV) += hv/ | 37 | obj-$(CONFIG_HYPERV) += hv/ |
38 | obj-$(CONFIG_VME_BUS) += vme/ | 38 | obj-$(CONFIG_VME_BUS) += vme/ |
39 | obj-$(CONFIG_RAR_REGISTER) += rar_register/ | ||
40 | obj-$(CONFIG_MRST_RAR_HANDLER) += memrar/ | 39 | obj-$(CONFIG_MRST_RAR_HANDLER) += memrar/ |
41 | obj-$(CONFIG_DX_SEP) += sep/ | 40 | obj-$(CONFIG_DX_SEP) += sep/ |
42 | obj-$(CONFIG_IIO) += iio/ | 41 | obj-$(CONFIG_IIO) += iio/ |
diff --git a/drivers/staging/memrar/memrar_handler.c b/drivers/staging/memrar/memrar_handler.c index efa7fd62d390..41876f2b0e54 100644 --- a/drivers/staging/memrar/memrar_handler.c +++ b/drivers/staging/memrar/memrar_handler.c | |||
@@ -47,8 +47,7 @@ | |||
47 | #include <linux/mm.h> | 47 | #include <linux/mm.h> |
48 | #include <linux/ioport.h> | 48 | #include <linux/ioport.h> |
49 | #include <linux/io.h> | 49 | #include <linux/io.h> |
50 | 50 | #include <linux/rar_register.h> | |
51 | #include "../rar_register/rar_register.h" | ||
52 | 51 | ||
53 | #include "memrar.h" | 52 | #include "memrar.h" |
54 | #include "memrar_allocator.h" | 53 | #include "memrar_allocator.h" |
diff --git a/drivers/staging/rar_register/Kconfig b/drivers/staging/rar_register/Kconfig deleted file mode 100644 index e9c27738199b..000000000000 --- a/drivers/staging/rar_register/Kconfig +++ /dev/null | |||
@@ -1,30 +0,0 @@ | |||
1 | # | ||
2 | # RAR device configuration | ||
3 | # | ||
4 | |||
5 | menu "RAR Register Driver" | ||
6 | # | ||
7 | # Restricted Access Register Manager | ||
8 | # | ||
9 | config RAR_REGISTER | ||
10 | tristate "Restricted Access Region Register Driver" | ||
11 | depends on PCI | ||
12 | default n | ||
13 | ---help--- | ||
14 | This driver allows other kernel drivers access to the | ||
15 | contents of the restricted access region control registers. | ||
16 | |||
17 | The restricted access region control registers | ||
18 | (rar_registers) are used to pass address and | ||
19 | locking information on restricted access regions | ||
20 | to other drivers that use restricted access regions. | ||
21 | |||
22 | The restricted access regions are regions of memory | ||
23 | on the Intel MID Platform that are not accessible to | ||
24 | the x86 processor, but are accessible to dedicated | ||
25 | processors on board peripheral devices. | ||
26 | |||
27 | The purpose of the restricted access regions is to | ||
28 | protect sensitive data from compromise by unauthorized | ||
29 | programs running on the x86 processor. | ||
30 | endmenu | ||
diff --git a/drivers/staging/rar_register/Makefile b/drivers/staging/rar_register/Makefile deleted file mode 100644 index d5954ccc16c9..000000000000 --- a/drivers/staging/rar_register/Makefile +++ /dev/null | |||
@@ -1,2 +0,0 @@ | |||
1 | EXTRA_CFLAGS += -DLITTLE__ENDIAN | ||
2 | obj-$(CONFIG_RAR_REGISTER) += rar_register.o | ||
diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index 7f0028e1010b..8f8b072c4c7b 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h | |||
@@ -33,6 +33,15 @@ | |||
33 | * subject to backwards-compatibility constraints. | 33 | * subject to backwards-compatibility constraints. |
34 | */ | 34 | */ |
35 | 35 | ||
36 | #ifdef __KERNEL__ | ||
37 | /* For use by IPS driver */ | ||
38 | extern unsigned long i915_read_mch_val(void); | ||
39 | extern bool i915_gpu_raise(void); | ||
40 | extern bool i915_gpu_lower(void); | ||
41 | extern bool i915_gpu_busy(void); | ||
42 | extern bool i915_gpu_turbo_disable(void); | ||
43 | #endif | ||
44 | |||
36 | /* Each region is a minimum of 16k, and there are at most 255 of them. | 45 | /* Each region is a minimum of 16k, and there are at most 255 of them. |
37 | */ | 46 | */ |
38 | #define I915_NR_TEX_REGIONS 255 /* table size 2k - maximum due to use | 47 | #define I915_NR_TEX_REGIONS 255 /* table size 2k - maximum due to use |
diff --git a/include/linux/intel_pmic_gpio.h b/include/linux/intel_pmic_gpio.h new file mode 100644 index 000000000000..920109a29191 --- /dev/null +++ b/include/linux/intel_pmic_gpio.h | |||
@@ -0,0 +1,15 @@ | |||
1 | #ifndef LINUX_INTEL_PMIC_H | ||
2 | #define LINUX_INTEL_PMIC_H | ||
3 | |||
4 | struct intel_pmic_gpio_platform_data { | ||
5 | /* the first IRQ of the chip */ | ||
6 | unsigned irq_base; | ||
7 | /* number assigned to the first GPIO */ | ||
8 | unsigned gpio_base; | ||
9 | /* sram address for gpiointr register, the langwell chip will map | ||
10 | * the PMIC spi GPIO expander's GPIOINTR register in sram. | ||
11 | */ | ||
12 | unsigned gpiointr; | ||
13 | }; | ||
14 | |||
15 | #endif | ||
diff --git a/drivers/staging/rar_register/rar_register.h b/include/linux/rar_register.h index ffa805780f85..ffa805780f85 100644 --- a/drivers/staging/rar_register/rar_register.h +++ b/include/linux/rar_register.h | |||
diff --git a/include/linux/timer.h b/include/linux/timer.h index ea965b857a50..38cf093ef62c 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h | |||
@@ -100,6 +100,13 @@ void init_timer_deferrable_key(struct timer_list *timer, | |||
100 | setup_timer_on_stack_key((timer), #timer, &__key, \ | 100 | setup_timer_on_stack_key((timer), #timer, &__key, \ |
101 | (fn), (data)); \ | 101 | (fn), (data)); \ |
102 | } while (0) | 102 | } while (0) |
103 | #define setup_deferrable_timer_on_stack(timer, fn, data) \ | ||
104 | do { \ | ||
105 | static struct lock_class_key __key; \ | ||
106 | setup_deferrable_timer_on_stack_key((timer), #timer, \ | ||
107 | &__key, (fn), \ | ||
108 | (data)); \ | ||
109 | } while (0) | ||
103 | #else | 110 | #else |
104 | #define init_timer(timer)\ | 111 | #define init_timer(timer)\ |
105 | init_timer_key((timer), NULL, NULL) | 112 | init_timer_key((timer), NULL, NULL) |
@@ -111,6 +118,8 @@ void init_timer_deferrable_key(struct timer_list *timer, | |||
111 | setup_timer_key((timer), NULL, NULL, (fn), (data)) | 118 | setup_timer_key((timer), NULL, NULL, (fn), (data)) |
112 | #define setup_timer_on_stack(timer, fn, data)\ | 119 | #define setup_timer_on_stack(timer, fn, data)\ |
113 | setup_timer_on_stack_key((timer), NULL, NULL, (fn), (data)) | 120 | setup_timer_on_stack_key((timer), NULL, NULL, (fn), (data)) |
121 | #define setup_deferrable_timer_on_stack(timer, fn, data)\ | ||
122 | setup_deferrable_timer_on_stack_key((timer), NULL, NULL, (fn), (data)) | ||
114 | #endif | 123 | #endif |
115 | 124 | ||
116 | #ifdef CONFIG_DEBUG_OBJECTS_TIMERS | 125 | #ifdef CONFIG_DEBUG_OBJECTS_TIMERS |
@@ -150,6 +159,12 @@ static inline void setup_timer_on_stack_key(struct timer_list *timer, | |||
150 | init_timer_on_stack_key(timer, name, key); | 159 | init_timer_on_stack_key(timer, name, key); |
151 | } | 160 | } |
152 | 161 | ||
162 | extern void setup_deferrable_timer_on_stack_key(struct timer_list *timer, | ||
163 | const char *name, | ||
164 | struct lock_class_key *key, | ||
165 | void (*function)(unsigned long), | ||
166 | unsigned long data); | ||
167 | |||
153 | /** | 168 | /** |
154 | * timer_pending - is a timer pending? | 169 | * timer_pending - is a timer pending? |
155 | * @timer: the timer in question | 170 | * @timer: the timer in question |
diff --git a/kernel/timer.c b/kernel/timer.c index ee305c8d4e18..efde11e197c4 100644 --- a/kernel/timer.c +++ b/kernel/timer.c | |||
@@ -577,6 +577,19 @@ static void __init_timer(struct timer_list *timer, | |||
577 | lockdep_init_map(&timer->lockdep_map, name, key, 0); | 577 | lockdep_init_map(&timer->lockdep_map, name, key, 0); |
578 | } | 578 | } |
579 | 579 | ||
580 | void setup_deferrable_timer_on_stack_key(struct timer_list *timer, | ||
581 | const char *name, | ||
582 | struct lock_class_key *key, | ||
583 | void (*function)(unsigned long), | ||
584 | unsigned long data) | ||
585 | { | ||
586 | timer->function = function; | ||
587 | timer->data = data; | ||
588 | init_timer_on_stack_key(timer, name, key); | ||
589 | timer_set_deferrable(timer); | ||
590 | } | ||
591 | EXPORT_SYMBOL_GPL(setup_deferrable_timer_on_stack_key); | ||
592 | |||
580 | /** | 593 | /** |
581 | * init_timer_key - initialize a timer | 594 | * init_timer_key - initialize a timer |
582 | * @timer: the timer to be initialized | 595 | * @timer: the timer to be initialized |