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 |
