18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * eeepc-laptop.c - Asus Eee PC extras 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Based on asus_acpi.c as patched for the Eee PC by Asus: 68c2ecf20Sopenharmony_ci * ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar 78c2ecf20Sopenharmony_ci * Based on eee.c from eeepc-linux 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/types.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/backlight.h> 188c2ecf20Sopenharmony_ci#include <linux/fb.h> 198c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 208c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/acpi.h> 238c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 248c2ecf20Sopenharmony_ci#include <linux/input.h> 258c2ecf20Sopenharmony_ci#include <linux/input/sparse-keymap.h> 268c2ecf20Sopenharmony_ci#include <linux/rfkill.h> 278c2ecf20Sopenharmony_ci#include <linux/pci.h> 288c2ecf20Sopenharmony_ci#include <linux/pci_hotplug.h> 298c2ecf20Sopenharmony_ci#include <linux/leds.h> 308c2ecf20Sopenharmony_ci#include <linux/dmi.h> 318c2ecf20Sopenharmony_ci#include <acpi/video.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define EEEPC_LAPTOP_VERSION "0.1" 348c2ecf20Sopenharmony_ci#define EEEPC_LAPTOP_NAME "Eee PC Hotkey Driver" 358c2ecf20Sopenharmony_ci#define EEEPC_LAPTOP_FILE "eeepc" 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define EEEPC_ACPI_CLASS "hotkey" 388c2ecf20Sopenharmony_ci#define EEEPC_ACPI_DEVICE_NAME "Hotkey" 398c2ecf20Sopenharmony_ci#define EEEPC_ACPI_HID "ASUS010" 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ciMODULE_AUTHOR("Corentin Chary, Eric Cooper"); 428c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(EEEPC_LAPTOP_NAME); 438c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic bool hotplug_disabled; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cimodule_param(hotplug_disabled, bool, 0444); 488c2ecf20Sopenharmony_ciMODULE_PARM_DESC(hotplug_disabled, 498c2ecf20Sopenharmony_ci "Disable hotplug for wireless device. " 508c2ecf20Sopenharmony_ci "If your laptop need that, please report to " 518c2ecf20Sopenharmony_ci "acpi4asus-user@lists.sourceforge.net."); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* 548c2ecf20Sopenharmony_ci * Definitions for Asus EeePC 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci#define NOTIFY_BRN_MIN 0x20 578c2ecf20Sopenharmony_ci#define NOTIFY_BRN_MAX 0x2f 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cienum { 608c2ecf20Sopenharmony_ci DISABLE_ASL_WLAN = 0x0001, 618c2ecf20Sopenharmony_ci DISABLE_ASL_BLUETOOTH = 0x0002, 628c2ecf20Sopenharmony_ci DISABLE_ASL_IRDA = 0x0004, 638c2ecf20Sopenharmony_ci DISABLE_ASL_CAMERA = 0x0008, 648c2ecf20Sopenharmony_ci DISABLE_ASL_TV = 0x0010, 658c2ecf20Sopenharmony_ci DISABLE_ASL_GPS = 0x0020, 668c2ecf20Sopenharmony_ci DISABLE_ASL_DISPLAYSWITCH = 0x0040, 678c2ecf20Sopenharmony_ci DISABLE_ASL_MODEM = 0x0080, 688c2ecf20Sopenharmony_ci DISABLE_ASL_CARDREADER = 0x0100, 698c2ecf20Sopenharmony_ci DISABLE_ASL_3G = 0x0200, 708c2ecf20Sopenharmony_ci DISABLE_ASL_WIMAX = 0x0400, 718c2ecf20Sopenharmony_ci DISABLE_ASL_HWCF = 0x0800 728c2ecf20Sopenharmony_ci}; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cienum { 758c2ecf20Sopenharmony_ci CM_ASL_WLAN = 0, 768c2ecf20Sopenharmony_ci CM_ASL_BLUETOOTH, 778c2ecf20Sopenharmony_ci CM_ASL_IRDA, 788c2ecf20Sopenharmony_ci CM_ASL_1394, 798c2ecf20Sopenharmony_ci CM_ASL_CAMERA, 808c2ecf20Sopenharmony_ci CM_ASL_TV, 818c2ecf20Sopenharmony_ci CM_ASL_GPS, 828c2ecf20Sopenharmony_ci CM_ASL_DVDROM, 838c2ecf20Sopenharmony_ci CM_ASL_DISPLAYSWITCH, 848c2ecf20Sopenharmony_ci CM_ASL_PANELBRIGHT, 858c2ecf20Sopenharmony_ci CM_ASL_BIOSFLASH, 868c2ecf20Sopenharmony_ci CM_ASL_ACPIFLASH, 878c2ecf20Sopenharmony_ci CM_ASL_CPUFV, 888c2ecf20Sopenharmony_ci CM_ASL_CPUTEMPERATURE, 898c2ecf20Sopenharmony_ci CM_ASL_FANCPU, 908c2ecf20Sopenharmony_ci CM_ASL_FANCHASSIS, 918c2ecf20Sopenharmony_ci CM_ASL_USBPORT1, 928c2ecf20Sopenharmony_ci CM_ASL_USBPORT2, 938c2ecf20Sopenharmony_ci CM_ASL_USBPORT3, 948c2ecf20Sopenharmony_ci CM_ASL_MODEM, 958c2ecf20Sopenharmony_ci CM_ASL_CARDREADER, 968c2ecf20Sopenharmony_ci CM_ASL_3G, 978c2ecf20Sopenharmony_ci CM_ASL_WIMAX, 988c2ecf20Sopenharmony_ci CM_ASL_HWCF, 998c2ecf20Sopenharmony_ci CM_ASL_LID, 1008c2ecf20Sopenharmony_ci CM_ASL_TYPE, 1018c2ecf20Sopenharmony_ci CM_ASL_PANELPOWER, /*P901*/ 1028c2ecf20Sopenharmony_ci CM_ASL_TPD 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic const char *cm_getv[] = { 1068c2ecf20Sopenharmony_ci "WLDG", "BTHG", NULL, NULL, 1078c2ecf20Sopenharmony_ci "CAMG", NULL, NULL, NULL, 1088c2ecf20Sopenharmony_ci NULL, "PBLG", NULL, NULL, 1098c2ecf20Sopenharmony_ci "CFVG", NULL, NULL, NULL, 1108c2ecf20Sopenharmony_ci "USBG", NULL, NULL, "MODG", 1118c2ecf20Sopenharmony_ci "CRDG", "M3GG", "WIMG", "HWCF", 1128c2ecf20Sopenharmony_ci "LIDG", "TYPE", "PBPG", "TPDG" 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic const char *cm_setv[] = { 1168c2ecf20Sopenharmony_ci "WLDS", "BTHS", NULL, NULL, 1178c2ecf20Sopenharmony_ci "CAMS", NULL, NULL, NULL, 1188c2ecf20Sopenharmony_ci "SDSP", "PBLS", "HDPS", NULL, 1198c2ecf20Sopenharmony_ci "CFVS", NULL, NULL, NULL, 1208c2ecf20Sopenharmony_ci "USBG", NULL, NULL, "MODS", 1218c2ecf20Sopenharmony_ci "CRDS", "M3GS", "WIMS", NULL, 1228c2ecf20Sopenharmony_ci NULL, NULL, "PBPS", "TPDS" 1238c2ecf20Sopenharmony_ci}; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic const struct key_entry eeepc_keymap[] = { 1268c2ecf20Sopenharmony_ci { KE_KEY, 0x10, { KEY_WLAN } }, 1278c2ecf20Sopenharmony_ci { KE_KEY, 0x11, { KEY_WLAN } }, 1288c2ecf20Sopenharmony_ci { KE_KEY, 0x12, { KEY_PROG1 } }, 1298c2ecf20Sopenharmony_ci { KE_KEY, 0x13, { KEY_MUTE } }, 1308c2ecf20Sopenharmony_ci { KE_KEY, 0x14, { KEY_VOLUMEDOWN } }, 1318c2ecf20Sopenharmony_ci { KE_KEY, 0x15, { KEY_VOLUMEUP } }, 1328c2ecf20Sopenharmony_ci { KE_KEY, 0x16, { KEY_DISPLAY_OFF } }, 1338c2ecf20Sopenharmony_ci { KE_KEY, 0x1a, { KEY_COFFEE } }, 1348c2ecf20Sopenharmony_ci { KE_KEY, 0x1b, { KEY_ZOOM } }, 1358c2ecf20Sopenharmony_ci { KE_KEY, 0x1c, { KEY_PROG2 } }, 1368c2ecf20Sopenharmony_ci { KE_KEY, 0x1d, { KEY_PROG3 } }, 1378c2ecf20Sopenharmony_ci { KE_KEY, NOTIFY_BRN_MIN, { KEY_BRIGHTNESSDOWN } }, 1388c2ecf20Sopenharmony_ci { KE_KEY, NOTIFY_BRN_MAX, { KEY_BRIGHTNESSUP } }, 1398c2ecf20Sopenharmony_ci { KE_KEY, 0x30, { KEY_SWITCHVIDEOMODE } }, 1408c2ecf20Sopenharmony_ci { KE_KEY, 0x31, { KEY_SWITCHVIDEOMODE } }, 1418c2ecf20Sopenharmony_ci { KE_KEY, 0x32, { KEY_SWITCHVIDEOMODE } }, 1428c2ecf20Sopenharmony_ci { KE_KEY, 0x37, { KEY_F13 } }, /* Disable Touchpad */ 1438c2ecf20Sopenharmony_ci { KE_KEY, 0x38, { KEY_F14 } }, 1448c2ecf20Sopenharmony_ci { KE_IGNORE, 0x50, { KEY_RESERVED } }, /* AC plugged */ 1458c2ecf20Sopenharmony_ci { KE_IGNORE, 0x51, { KEY_RESERVED } }, /* AC unplugged */ 1468c2ecf20Sopenharmony_ci { KE_END, 0 }, 1478c2ecf20Sopenharmony_ci}; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/* 1508c2ecf20Sopenharmony_ci * This is the main structure, we can use it to store useful information 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_cistruct eeepc_laptop { 1538c2ecf20Sopenharmony_ci acpi_handle handle; /* the handle of the acpi device */ 1548c2ecf20Sopenharmony_ci u32 cm_supported; /* the control methods supported 1558c2ecf20Sopenharmony_ci by this BIOS */ 1568c2ecf20Sopenharmony_ci bool cpufv_disabled; 1578c2ecf20Sopenharmony_ci bool hotplug_disabled; 1588c2ecf20Sopenharmony_ci u16 event_count[128]; /* count for each event */ 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci struct platform_device *platform_device; 1618c2ecf20Sopenharmony_ci struct acpi_device *device; /* the device we are in */ 1628c2ecf20Sopenharmony_ci struct backlight_device *backlight_device; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci struct input_dev *inputdev; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci struct rfkill *wlan_rfkill; 1678c2ecf20Sopenharmony_ci struct rfkill *bluetooth_rfkill; 1688c2ecf20Sopenharmony_ci struct rfkill *wwan3g_rfkill; 1698c2ecf20Sopenharmony_ci struct rfkill *wimax_rfkill; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci struct hotplug_slot hotplug_slot; 1728c2ecf20Sopenharmony_ci struct mutex hotplug_lock; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci struct led_classdev tpd_led; 1758c2ecf20Sopenharmony_ci int tpd_led_wk; 1768c2ecf20Sopenharmony_ci struct workqueue_struct *led_workqueue; 1778c2ecf20Sopenharmony_ci struct work_struct tpd_led_work; 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/* 1818c2ecf20Sopenharmony_ci * ACPI Helpers 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_cistatic int write_acpi_int(acpi_handle handle, const char *method, int val) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci acpi_status status; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci status = acpi_execute_simple_method(handle, (char *)method, val); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return (status == AE_OK ? 0 : -1); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic int read_acpi_int(acpi_handle handle, const char *method, int *val) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci acpi_status status; 1958c2ecf20Sopenharmony_ci unsigned long long result; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(handle, (char *)method, NULL, &result); 1988c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 1998c2ecf20Sopenharmony_ci *val = -1; 2008c2ecf20Sopenharmony_ci return -1; 2018c2ecf20Sopenharmony_ci } else { 2028c2ecf20Sopenharmony_ci *val = result; 2038c2ecf20Sopenharmony_ci return 0; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int set_acpi(struct eeepc_laptop *eeepc, int cm, int value) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci const char *method = cm_setv[cm]; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (method == NULL) 2128c2ecf20Sopenharmony_ci return -ENODEV; 2138c2ecf20Sopenharmony_ci if ((eeepc->cm_supported & (0x1 << cm)) == 0) 2148c2ecf20Sopenharmony_ci return -ENODEV; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (write_acpi_int(eeepc->handle, method, value)) 2178c2ecf20Sopenharmony_ci pr_warn("Error writing %s\n", method); 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic int get_acpi(struct eeepc_laptop *eeepc, int cm) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci const char *method = cm_getv[cm]; 2248c2ecf20Sopenharmony_ci int value; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (method == NULL) 2278c2ecf20Sopenharmony_ci return -ENODEV; 2288c2ecf20Sopenharmony_ci if ((eeepc->cm_supported & (0x1 << cm)) == 0) 2298c2ecf20Sopenharmony_ci return -ENODEV; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (read_acpi_int(eeepc->handle, method, &value)) 2328c2ecf20Sopenharmony_ci pr_warn("Error reading %s\n", method); 2338c2ecf20Sopenharmony_ci return value; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic int acpi_setter_handle(struct eeepc_laptop *eeepc, int cm, 2378c2ecf20Sopenharmony_ci acpi_handle *handle) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci const char *method = cm_setv[cm]; 2408c2ecf20Sopenharmony_ci acpi_status status; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (method == NULL) 2438c2ecf20Sopenharmony_ci return -ENODEV; 2448c2ecf20Sopenharmony_ci if ((eeepc->cm_supported & (0x1 << cm)) == 0) 2458c2ecf20Sopenharmony_ci return -ENODEV; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci status = acpi_get_handle(eeepc->handle, (char *)method, 2488c2ecf20Sopenharmony_ci handle); 2498c2ecf20Sopenharmony_ci if (status != AE_OK) { 2508c2ecf20Sopenharmony_ci pr_warn("Error finding %s\n", method); 2518c2ecf20Sopenharmony_ci return -ENODEV; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci return 0; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci/* 2588c2ecf20Sopenharmony_ci * Sys helpers 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_cistatic int parse_arg(const char *buf, int *val) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci if (sscanf(buf, "%i", val) != 1) 2638c2ecf20Sopenharmony_ci return -EINVAL; 2648c2ecf20Sopenharmony_ci return 0; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic ssize_t store_sys_acpi(struct device *dev, int cm, 2688c2ecf20Sopenharmony_ci const char *buf, size_t count) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct eeepc_laptop *eeepc = dev_get_drvdata(dev); 2718c2ecf20Sopenharmony_ci int rv, value; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci rv = parse_arg(buf, &value); 2748c2ecf20Sopenharmony_ci if (rv < 0) 2758c2ecf20Sopenharmony_ci return rv; 2768c2ecf20Sopenharmony_ci rv = set_acpi(eeepc, cm, value); 2778c2ecf20Sopenharmony_ci if (rv < 0) 2788c2ecf20Sopenharmony_ci return -EIO; 2798c2ecf20Sopenharmony_ci return count; 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic ssize_t show_sys_acpi(struct device *dev, int cm, char *buf) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci struct eeepc_laptop *eeepc = dev_get_drvdata(dev); 2858c2ecf20Sopenharmony_ci int value = get_acpi(eeepc, cm); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (value < 0) 2888c2ecf20Sopenharmony_ci return -EIO; 2898c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", value); 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci#define EEEPC_ACPI_SHOW_FUNC(_name, _cm) \ 2938c2ecf20Sopenharmony_ci static ssize_t _name##_show(struct device *dev, \ 2948c2ecf20Sopenharmony_ci struct device_attribute *attr, \ 2958c2ecf20Sopenharmony_ci char *buf) \ 2968c2ecf20Sopenharmony_ci { \ 2978c2ecf20Sopenharmony_ci return show_sys_acpi(dev, _cm, buf); \ 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci#define EEEPC_ACPI_STORE_FUNC(_name, _cm) \ 3018c2ecf20Sopenharmony_ci static ssize_t _name##_store(struct device *dev, \ 3028c2ecf20Sopenharmony_ci struct device_attribute *attr, \ 3038c2ecf20Sopenharmony_ci const char *buf, size_t count) \ 3048c2ecf20Sopenharmony_ci { \ 3058c2ecf20Sopenharmony_ci return store_sys_acpi(dev, _cm, buf, count); \ 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci#define EEEPC_CREATE_DEVICE_ATTR_RW(_name, _cm) \ 3098c2ecf20Sopenharmony_ci EEEPC_ACPI_SHOW_FUNC(_name, _cm) \ 3108c2ecf20Sopenharmony_ci EEEPC_ACPI_STORE_FUNC(_name, _cm) \ 3118c2ecf20Sopenharmony_ci static DEVICE_ATTR_RW(_name) 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci#define EEEPC_CREATE_DEVICE_ATTR_WO(_name, _cm) \ 3148c2ecf20Sopenharmony_ci EEEPC_ACPI_STORE_FUNC(_name, _cm) \ 3158c2ecf20Sopenharmony_ci static DEVICE_ATTR_WO(_name) 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ciEEEPC_CREATE_DEVICE_ATTR_RW(camera, CM_ASL_CAMERA); 3188c2ecf20Sopenharmony_ciEEEPC_CREATE_DEVICE_ATTR_RW(cardr, CM_ASL_CARDREADER); 3198c2ecf20Sopenharmony_ciEEEPC_CREATE_DEVICE_ATTR_WO(disp, CM_ASL_DISPLAYSWITCH); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistruct eeepc_cpufv { 3228c2ecf20Sopenharmony_ci int num; 3238c2ecf20Sopenharmony_ci int cur; 3248c2ecf20Sopenharmony_ci}; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic int get_cpufv(struct eeepc_laptop *eeepc, struct eeepc_cpufv *c) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci c->cur = get_acpi(eeepc, CM_ASL_CPUFV); 3298c2ecf20Sopenharmony_ci if (c->cur < 0) 3308c2ecf20Sopenharmony_ci return -ENODEV; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci c->num = (c->cur >> 8) & 0xff; 3338c2ecf20Sopenharmony_ci c->cur &= 0xff; 3348c2ecf20Sopenharmony_ci if (c->num == 0 || c->num > 12) 3358c2ecf20Sopenharmony_ci return -ENODEV; 3368c2ecf20Sopenharmony_ci return 0; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic ssize_t available_cpufv_show(struct device *dev, 3408c2ecf20Sopenharmony_ci struct device_attribute *attr, 3418c2ecf20Sopenharmony_ci char *buf) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci struct eeepc_laptop *eeepc = dev_get_drvdata(dev); 3448c2ecf20Sopenharmony_ci struct eeepc_cpufv c; 3458c2ecf20Sopenharmony_ci int i; 3468c2ecf20Sopenharmony_ci ssize_t len = 0; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (get_cpufv(eeepc, &c)) 3498c2ecf20Sopenharmony_ci return -ENODEV; 3508c2ecf20Sopenharmony_ci for (i = 0; i < c.num; i++) 3518c2ecf20Sopenharmony_ci len += sprintf(buf + len, "%d ", i); 3528c2ecf20Sopenharmony_ci len += sprintf(buf + len, "\n"); 3538c2ecf20Sopenharmony_ci return len; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic ssize_t cpufv_show(struct device *dev, 3578c2ecf20Sopenharmony_ci struct device_attribute *attr, 3588c2ecf20Sopenharmony_ci char *buf) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct eeepc_laptop *eeepc = dev_get_drvdata(dev); 3618c2ecf20Sopenharmony_ci struct eeepc_cpufv c; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (get_cpufv(eeepc, &c)) 3648c2ecf20Sopenharmony_ci return -ENODEV; 3658c2ecf20Sopenharmony_ci return sprintf(buf, "%#x\n", (c.num << 8) | c.cur); 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic ssize_t cpufv_store(struct device *dev, 3698c2ecf20Sopenharmony_ci struct device_attribute *attr, 3708c2ecf20Sopenharmony_ci const char *buf, size_t count) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct eeepc_laptop *eeepc = dev_get_drvdata(dev); 3738c2ecf20Sopenharmony_ci struct eeepc_cpufv c; 3748c2ecf20Sopenharmony_ci int rv, value; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (eeepc->cpufv_disabled) 3778c2ecf20Sopenharmony_ci return -EPERM; 3788c2ecf20Sopenharmony_ci if (get_cpufv(eeepc, &c)) 3798c2ecf20Sopenharmony_ci return -ENODEV; 3808c2ecf20Sopenharmony_ci rv = parse_arg(buf, &value); 3818c2ecf20Sopenharmony_ci if (rv < 0) 3828c2ecf20Sopenharmony_ci return rv; 3838c2ecf20Sopenharmony_ci if (value < 0 || value >= c.num) 3848c2ecf20Sopenharmony_ci return -EINVAL; 3858c2ecf20Sopenharmony_ci rv = set_acpi(eeepc, CM_ASL_CPUFV, value); 3868c2ecf20Sopenharmony_ci if (rv) 3878c2ecf20Sopenharmony_ci return rv; 3888c2ecf20Sopenharmony_ci return count; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic ssize_t cpufv_disabled_show(struct device *dev, 3928c2ecf20Sopenharmony_ci struct device_attribute *attr, 3938c2ecf20Sopenharmony_ci char *buf) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci struct eeepc_laptop *eeepc = dev_get_drvdata(dev); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", eeepc->cpufv_disabled); 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic ssize_t cpufv_disabled_store(struct device *dev, 4018c2ecf20Sopenharmony_ci struct device_attribute *attr, 4028c2ecf20Sopenharmony_ci const char *buf, size_t count) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct eeepc_laptop *eeepc = dev_get_drvdata(dev); 4058c2ecf20Sopenharmony_ci int rv, value; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci rv = parse_arg(buf, &value); 4088c2ecf20Sopenharmony_ci if (rv < 0) 4098c2ecf20Sopenharmony_ci return rv; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci switch (value) { 4128c2ecf20Sopenharmony_ci case 0: 4138c2ecf20Sopenharmony_ci if (eeepc->cpufv_disabled) 4148c2ecf20Sopenharmony_ci pr_warn("cpufv enabled (not officially supported on this model)\n"); 4158c2ecf20Sopenharmony_ci eeepc->cpufv_disabled = false; 4168c2ecf20Sopenharmony_ci return count; 4178c2ecf20Sopenharmony_ci case 1: 4188c2ecf20Sopenharmony_ci return -EPERM; 4198c2ecf20Sopenharmony_ci default: 4208c2ecf20Sopenharmony_ci return -EINVAL; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(cpufv); 4268c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(available_cpufv); 4278c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(cpufv_disabled); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic struct attribute *platform_attributes[] = { 4308c2ecf20Sopenharmony_ci &dev_attr_camera.attr, 4318c2ecf20Sopenharmony_ci &dev_attr_cardr.attr, 4328c2ecf20Sopenharmony_ci &dev_attr_disp.attr, 4338c2ecf20Sopenharmony_ci &dev_attr_cpufv.attr, 4348c2ecf20Sopenharmony_ci &dev_attr_available_cpufv.attr, 4358c2ecf20Sopenharmony_ci &dev_attr_cpufv_disabled.attr, 4368c2ecf20Sopenharmony_ci NULL 4378c2ecf20Sopenharmony_ci}; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic const struct attribute_group platform_attribute_group = { 4408c2ecf20Sopenharmony_ci .attrs = platform_attributes 4418c2ecf20Sopenharmony_ci}; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic int eeepc_platform_init(struct eeepc_laptop *eeepc) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci int result; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci eeepc->platform_device = platform_device_alloc(EEEPC_LAPTOP_FILE, -1); 4488c2ecf20Sopenharmony_ci if (!eeepc->platform_device) 4498c2ecf20Sopenharmony_ci return -ENOMEM; 4508c2ecf20Sopenharmony_ci platform_set_drvdata(eeepc->platform_device, eeepc); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci result = platform_device_add(eeepc->platform_device); 4538c2ecf20Sopenharmony_ci if (result) 4548c2ecf20Sopenharmony_ci goto fail_platform_device; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci result = sysfs_create_group(&eeepc->platform_device->dev.kobj, 4578c2ecf20Sopenharmony_ci &platform_attribute_group); 4588c2ecf20Sopenharmony_ci if (result) 4598c2ecf20Sopenharmony_ci goto fail_sysfs; 4608c2ecf20Sopenharmony_ci return 0; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cifail_sysfs: 4638c2ecf20Sopenharmony_ci platform_device_del(eeepc->platform_device); 4648c2ecf20Sopenharmony_cifail_platform_device: 4658c2ecf20Sopenharmony_ci platform_device_put(eeepc->platform_device); 4668c2ecf20Sopenharmony_ci return result; 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_cistatic void eeepc_platform_exit(struct eeepc_laptop *eeepc) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci sysfs_remove_group(&eeepc->platform_device->dev.kobj, 4728c2ecf20Sopenharmony_ci &platform_attribute_group); 4738c2ecf20Sopenharmony_ci platform_device_unregister(eeepc->platform_device); 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci/* 4778c2ecf20Sopenharmony_ci * LEDs 4788c2ecf20Sopenharmony_ci */ 4798c2ecf20Sopenharmony_ci/* 4808c2ecf20Sopenharmony_ci * These functions actually update the LED's, and are called from a 4818c2ecf20Sopenharmony_ci * workqueue. By doing this as separate work rather than when the LED 4828c2ecf20Sopenharmony_ci * subsystem asks, we avoid messing with the Asus ACPI stuff during a 4838c2ecf20Sopenharmony_ci * potentially bad time, such as a timer interrupt. 4848c2ecf20Sopenharmony_ci */ 4858c2ecf20Sopenharmony_cistatic void tpd_led_update(struct work_struct *work) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci struct eeepc_laptop *eeepc; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci eeepc = container_of(work, struct eeepc_laptop, tpd_led_work); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci set_acpi(eeepc, CM_ASL_TPD, eeepc->tpd_led_wk); 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic void tpd_led_set(struct led_classdev *led_cdev, 4958c2ecf20Sopenharmony_ci enum led_brightness value) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci struct eeepc_laptop *eeepc; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci eeepc->tpd_led_wk = (value > 0) ? 1 : 0; 5028c2ecf20Sopenharmony_ci queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic enum led_brightness tpd_led_get(struct led_classdev *led_cdev) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci struct eeepc_laptop *eeepc; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci return get_acpi(eeepc, CM_ASL_TPD); 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic int eeepc_led_init(struct eeepc_laptop *eeepc) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci int rv; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (get_acpi(eeepc, CM_ASL_TPD) == -ENODEV) 5198c2ecf20Sopenharmony_ci return 0; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue"); 5228c2ecf20Sopenharmony_ci if (!eeepc->led_workqueue) 5238c2ecf20Sopenharmony_ci return -ENOMEM; 5248c2ecf20Sopenharmony_ci INIT_WORK(&eeepc->tpd_led_work, tpd_led_update); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci eeepc->tpd_led.name = "eeepc::touchpad"; 5278c2ecf20Sopenharmony_ci eeepc->tpd_led.brightness_set = tpd_led_set; 5288c2ecf20Sopenharmony_ci if (get_acpi(eeepc, CM_ASL_TPD) >= 0) /* if method is available */ 5298c2ecf20Sopenharmony_ci eeepc->tpd_led.brightness_get = tpd_led_get; 5308c2ecf20Sopenharmony_ci eeepc->tpd_led.max_brightness = 1; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci rv = led_classdev_register(&eeepc->platform_device->dev, 5338c2ecf20Sopenharmony_ci &eeepc->tpd_led); 5348c2ecf20Sopenharmony_ci if (rv) { 5358c2ecf20Sopenharmony_ci destroy_workqueue(eeepc->led_workqueue); 5368c2ecf20Sopenharmony_ci return rv; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci return 0; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic void eeepc_led_exit(struct eeepc_laptop *eeepc) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci led_classdev_unregister(&eeepc->tpd_led); 5458c2ecf20Sopenharmony_ci if (eeepc->led_workqueue) 5468c2ecf20Sopenharmony_ci destroy_workqueue(eeepc->led_workqueue); 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci/* 5508c2ecf20Sopenharmony_ci * PCI hotplug (for wlan rfkill) 5518c2ecf20Sopenharmony_ci */ 5528c2ecf20Sopenharmony_cistatic bool eeepc_wlan_rfkill_blocked(struct eeepc_laptop *eeepc) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci if (get_acpi(eeepc, CM_ASL_WLAN) == 1) 5558c2ecf20Sopenharmony_ci return false; 5568c2ecf20Sopenharmony_ci return true; 5578c2ecf20Sopenharmony_ci} 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cistatic void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci struct pci_dev *port; 5628c2ecf20Sopenharmony_ci struct pci_dev *dev; 5638c2ecf20Sopenharmony_ci struct pci_bus *bus; 5648c2ecf20Sopenharmony_ci bool blocked = eeepc_wlan_rfkill_blocked(eeepc); 5658c2ecf20Sopenharmony_ci bool absent; 5668c2ecf20Sopenharmony_ci u32 l; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (eeepc->wlan_rfkill) 5698c2ecf20Sopenharmony_ci rfkill_set_sw_state(eeepc->wlan_rfkill, blocked); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci mutex_lock(&eeepc->hotplug_lock); 5728c2ecf20Sopenharmony_ci pci_lock_rescan_remove(); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if (!eeepc->hotplug_slot.ops) 5758c2ecf20Sopenharmony_ci goto out_unlock; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci port = acpi_get_pci_dev(handle); 5788c2ecf20Sopenharmony_ci if (!port) { 5798c2ecf20Sopenharmony_ci pr_warn("Unable to find port\n"); 5808c2ecf20Sopenharmony_ci goto out_unlock; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci bus = port->subordinate; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci if (!bus) { 5868c2ecf20Sopenharmony_ci pr_warn("Unable to find PCI bus 1?\n"); 5878c2ecf20Sopenharmony_ci goto out_put_dev; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) { 5918c2ecf20Sopenharmony_ci pr_err("Unable to read PCI config space?\n"); 5928c2ecf20Sopenharmony_ci goto out_put_dev; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci absent = (l == 0xffffffff); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (blocked != absent) { 5988c2ecf20Sopenharmony_ci pr_warn("BIOS says wireless lan is %s, but the pci device is %s\n", 5998c2ecf20Sopenharmony_ci blocked ? "blocked" : "unblocked", 6008c2ecf20Sopenharmony_ci absent ? "absent" : "present"); 6018c2ecf20Sopenharmony_ci pr_warn("skipped wireless hotplug as probably inappropriate for this model\n"); 6028c2ecf20Sopenharmony_ci goto out_put_dev; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (!blocked) { 6068c2ecf20Sopenharmony_ci dev = pci_get_slot(bus, 0); 6078c2ecf20Sopenharmony_ci if (dev) { 6088c2ecf20Sopenharmony_ci /* Device already present */ 6098c2ecf20Sopenharmony_ci pci_dev_put(dev); 6108c2ecf20Sopenharmony_ci goto out_put_dev; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci dev = pci_scan_single_device(bus, 0); 6138c2ecf20Sopenharmony_ci if (dev) { 6148c2ecf20Sopenharmony_ci pci_bus_assign_resources(bus); 6158c2ecf20Sopenharmony_ci pci_bus_add_device(dev); 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci } else { 6188c2ecf20Sopenharmony_ci dev = pci_get_slot(bus, 0); 6198c2ecf20Sopenharmony_ci if (dev) { 6208c2ecf20Sopenharmony_ci pci_stop_and_remove_bus_device(dev); 6218c2ecf20Sopenharmony_ci pci_dev_put(dev); 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ciout_put_dev: 6258c2ecf20Sopenharmony_ci pci_dev_put(port); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ciout_unlock: 6288c2ecf20Sopenharmony_ci pci_unlock_rescan_remove(); 6298c2ecf20Sopenharmony_ci mutex_unlock(&eeepc->hotplug_lock); 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic void eeepc_rfkill_hotplug_update(struct eeepc_laptop *eeepc, char *node) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci acpi_status status = AE_OK; 6358c2ecf20Sopenharmony_ci acpi_handle handle; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci status = acpi_get_handle(NULL, node, &handle); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci if (ACPI_SUCCESS(status)) 6408c2ecf20Sopenharmony_ci eeepc_rfkill_hotplug(eeepc, handle); 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_cistatic void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) 6448c2ecf20Sopenharmony_ci{ 6458c2ecf20Sopenharmony_ci struct eeepc_laptop *eeepc = data; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci if (event != ACPI_NOTIFY_BUS_CHECK) 6488c2ecf20Sopenharmony_ci return; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci eeepc_rfkill_hotplug(eeepc, handle); 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic int eeepc_register_rfkill_notifier(struct eeepc_laptop *eeepc, 6548c2ecf20Sopenharmony_ci char *node) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci acpi_status status; 6578c2ecf20Sopenharmony_ci acpi_handle handle; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci status = acpi_get_handle(NULL, node, &handle); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 6628c2ecf20Sopenharmony_ci return -ENODEV; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci status = acpi_install_notify_handler(handle, 6658c2ecf20Sopenharmony_ci ACPI_SYSTEM_NOTIFY, 6668c2ecf20Sopenharmony_ci eeepc_rfkill_notify, 6678c2ecf20Sopenharmony_ci eeepc); 6688c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 6698c2ecf20Sopenharmony_ci pr_warn("Failed to register notify on %s\n", node); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci /* 6728c2ecf20Sopenharmony_ci * Refresh pci hotplug in case the rfkill state was 6738c2ecf20Sopenharmony_ci * changed during setup. 6748c2ecf20Sopenharmony_ci */ 6758c2ecf20Sopenharmony_ci eeepc_rfkill_hotplug(eeepc, handle); 6768c2ecf20Sopenharmony_ci return 0; 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic void eeepc_unregister_rfkill_notifier(struct eeepc_laptop *eeepc, 6808c2ecf20Sopenharmony_ci char *node) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci acpi_status status = AE_OK; 6838c2ecf20Sopenharmony_ci acpi_handle handle; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci status = acpi_get_handle(NULL, node, &handle); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 6888c2ecf20Sopenharmony_ci return; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci status = acpi_remove_notify_handler(handle, 6918c2ecf20Sopenharmony_ci ACPI_SYSTEM_NOTIFY, 6928c2ecf20Sopenharmony_ci eeepc_rfkill_notify); 6938c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 6948c2ecf20Sopenharmony_ci pr_err("Error removing rfkill notify handler %s\n", 6958c2ecf20Sopenharmony_ci node); 6968c2ecf20Sopenharmony_ci /* 6978c2ecf20Sopenharmony_ci * Refresh pci hotplug in case the rfkill 6988c2ecf20Sopenharmony_ci * state was changed after 6998c2ecf20Sopenharmony_ci * eeepc_unregister_rfkill_notifier() 7008c2ecf20Sopenharmony_ci */ 7018c2ecf20Sopenharmony_ci eeepc_rfkill_hotplug(eeepc, handle); 7028c2ecf20Sopenharmony_ci} 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_cistatic int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, 7058c2ecf20Sopenharmony_ci u8 *value) 7068c2ecf20Sopenharmony_ci{ 7078c2ecf20Sopenharmony_ci struct eeepc_laptop *eeepc; 7088c2ecf20Sopenharmony_ci int val; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci eeepc = container_of(hotplug_slot, struct eeepc_laptop, hotplug_slot); 7118c2ecf20Sopenharmony_ci val = get_acpi(eeepc, CM_ASL_WLAN); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci if (val == 1 || val == 0) 7148c2ecf20Sopenharmony_ci *value = val; 7158c2ecf20Sopenharmony_ci else 7168c2ecf20Sopenharmony_ci return -EINVAL; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci return 0; 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_cistatic const struct hotplug_slot_ops eeepc_hotplug_slot_ops = { 7228c2ecf20Sopenharmony_ci .get_adapter_status = eeepc_get_adapter_status, 7238c2ecf20Sopenharmony_ci .get_power_status = eeepc_get_adapter_status, 7248c2ecf20Sopenharmony_ci}; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cistatic int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc) 7278c2ecf20Sopenharmony_ci{ 7288c2ecf20Sopenharmony_ci int ret = -ENOMEM; 7298c2ecf20Sopenharmony_ci struct pci_bus *bus = pci_find_bus(0, 1); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci if (!bus) { 7328c2ecf20Sopenharmony_ci pr_err("Unable to find wifi PCI bus\n"); 7338c2ecf20Sopenharmony_ci return -ENODEV; 7348c2ecf20Sopenharmony_ci } 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci eeepc->hotplug_slot.ops = &eeepc_hotplug_slot_ops; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci ret = pci_hp_register(&eeepc->hotplug_slot, bus, 0, "eeepc-wifi"); 7398c2ecf20Sopenharmony_ci if (ret) { 7408c2ecf20Sopenharmony_ci pr_err("Unable to register hotplug slot - %d\n", ret); 7418c2ecf20Sopenharmony_ci goto error_register; 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci return 0; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_cierror_register: 7478c2ecf20Sopenharmony_ci eeepc->hotplug_slot.ops = NULL; 7488c2ecf20Sopenharmony_ci return ret; 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci/* 7528c2ecf20Sopenharmony_ci * Rfkill devices 7538c2ecf20Sopenharmony_ci */ 7548c2ecf20Sopenharmony_cistatic int eeepc_rfkill_set(void *data, bool blocked) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci acpi_handle handle = data; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci return write_acpi_int(handle, NULL, !blocked); 7598c2ecf20Sopenharmony_ci} 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_cistatic const struct rfkill_ops eeepc_rfkill_ops = { 7628c2ecf20Sopenharmony_ci .set_block = eeepc_rfkill_set, 7638c2ecf20Sopenharmony_ci}; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_cistatic int eeepc_new_rfkill(struct eeepc_laptop *eeepc, 7668c2ecf20Sopenharmony_ci struct rfkill **rfkill, 7678c2ecf20Sopenharmony_ci const char *name, 7688c2ecf20Sopenharmony_ci enum rfkill_type type, int cm) 7698c2ecf20Sopenharmony_ci{ 7708c2ecf20Sopenharmony_ci acpi_handle handle; 7718c2ecf20Sopenharmony_ci int result; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci result = acpi_setter_handle(eeepc, cm, &handle); 7748c2ecf20Sopenharmony_ci if (result < 0) 7758c2ecf20Sopenharmony_ci return result; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, 7788c2ecf20Sopenharmony_ci &eeepc_rfkill_ops, handle); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci if (!*rfkill) 7818c2ecf20Sopenharmony_ci return -EINVAL; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci rfkill_init_sw_state(*rfkill, get_acpi(eeepc, cm) != 1); 7848c2ecf20Sopenharmony_ci result = rfkill_register(*rfkill); 7858c2ecf20Sopenharmony_ci if (result) { 7868c2ecf20Sopenharmony_ci rfkill_destroy(*rfkill); 7878c2ecf20Sopenharmony_ci *rfkill = NULL; 7888c2ecf20Sopenharmony_ci return result; 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci return 0; 7918c2ecf20Sopenharmony_ci} 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_cistatic char EEEPC_RFKILL_NODE_1[] = "\\_SB.PCI0.P0P5"; 7948c2ecf20Sopenharmony_cistatic char EEEPC_RFKILL_NODE_2[] = "\\_SB.PCI0.P0P6"; 7958c2ecf20Sopenharmony_cistatic char EEEPC_RFKILL_NODE_3[] = "\\_SB.PCI0.P0P7"; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_cistatic void eeepc_rfkill_exit(struct eeepc_laptop *eeepc) 7988c2ecf20Sopenharmony_ci{ 7998c2ecf20Sopenharmony_ci eeepc_unregister_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_1); 8008c2ecf20Sopenharmony_ci eeepc_unregister_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_2); 8018c2ecf20Sopenharmony_ci eeepc_unregister_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_3); 8028c2ecf20Sopenharmony_ci if (eeepc->wlan_rfkill) { 8038c2ecf20Sopenharmony_ci rfkill_unregister(eeepc->wlan_rfkill); 8048c2ecf20Sopenharmony_ci rfkill_destroy(eeepc->wlan_rfkill); 8058c2ecf20Sopenharmony_ci eeepc->wlan_rfkill = NULL; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci if (eeepc->hotplug_slot.ops) 8098c2ecf20Sopenharmony_ci pci_hp_deregister(&eeepc->hotplug_slot); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci if (eeepc->bluetooth_rfkill) { 8128c2ecf20Sopenharmony_ci rfkill_unregister(eeepc->bluetooth_rfkill); 8138c2ecf20Sopenharmony_ci rfkill_destroy(eeepc->bluetooth_rfkill); 8148c2ecf20Sopenharmony_ci eeepc->bluetooth_rfkill = NULL; 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci if (eeepc->wwan3g_rfkill) { 8178c2ecf20Sopenharmony_ci rfkill_unregister(eeepc->wwan3g_rfkill); 8188c2ecf20Sopenharmony_ci rfkill_destroy(eeepc->wwan3g_rfkill); 8198c2ecf20Sopenharmony_ci eeepc->wwan3g_rfkill = NULL; 8208c2ecf20Sopenharmony_ci } 8218c2ecf20Sopenharmony_ci if (eeepc->wimax_rfkill) { 8228c2ecf20Sopenharmony_ci rfkill_unregister(eeepc->wimax_rfkill); 8238c2ecf20Sopenharmony_ci rfkill_destroy(eeepc->wimax_rfkill); 8248c2ecf20Sopenharmony_ci eeepc->wimax_rfkill = NULL; 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci} 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_cistatic int eeepc_rfkill_init(struct eeepc_laptop *eeepc) 8298c2ecf20Sopenharmony_ci{ 8308c2ecf20Sopenharmony_ci int result = 0; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci mutex_init(&eeepc->hotplug_lock); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill, 8358c2ecf20Sopenharmony_ci "eeepc-wlan", RFKILL_TYPE_WLAN, 8368c2ecf20Sopenharmony_ci CM_ASL_WLAN); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci if (result && result != -ENODEV) 8398c2ecf20Sopenharmony_ci goto exit; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill, 8428c2ecf20Sopenharmony_ci "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH, 8438c2ecf20Sopenharmony_ci CM_ASL_BLUETOOTH); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci if (result && result != -ENODEV) 8468c2ecf20Sopenharmony_ci goto exit; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill, 8498c2ecf20Sopenharmony_ci "eeepc-wwan3g", RFKILL_TYPE_WWAN, 8508c2ecf20Sopenharmony_ci CM_ASL_3G); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci if (result && result != -ENODEV) 8538c2ecf20Sopenharmony_ci goto exit; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci result = eeepc_new_rfkill(eeepc, &eeepc->wimax_rfkill, 8568c2ecf20Sopenharmony_ci "eeepc-wimax", RFKILL_TYPE_WIMAX, 8578c2ecf20Sopenharmony_ci CM_ASL_WIMAX); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci if (result && result != -ENODEV) 8608c2ecf20Sopenharmony_ci goto exit; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci if (eeepc->hotplug_disabled) 8638c2ecf20Sopenharmony_ci return 0; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci result = eeepc_setup_pci_hotplug(eeepc); 8668c2ecf20Sopenharmony_ci /* 8678c2ecf20Sopenharmony_ci * If we get -EBUSY then something else is handling the PCI hotplug - 8688c2ecf20Sopenharmony_ci * don't fail in this case 8698c2ecf20Sopenharmony_ci */ 8708c2ecf20Sopenharmony_ci if (result == -EBUSY) 8718c2ecf20Sopenharmony_ci result = 0; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci eeepc_register_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_1); 8748c2ecf20Sopenharmony_ci eeepc_register_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_2); 8758c2ecf20Sopenharmony_ci eeepc_register_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_3); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ciexit: 8788c2ecf20Sopenharmony_ci if (result && result != -ENODEV) 8798c2ecf20Sopenharmony_ci eeepc_rfkill_exit(eeepc); 8808c2ecf20Sopenharmony_ci return result; 8818c2ecf20Sopenharmony_ci} 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci/* 8848c2ecf20Sopenharmony_ci * Platform driver - hibernate/resume callbacks 8858c2ecf20Sopenharmony_ci */ 8868c2ecf20Sopenharmony_cistatic int eeepc_hotk_thaw(struct device *device) 8878c2ecf20Sopenharmony_ci{ 8888c2ecf20Sopenharmony_ci struct eeepc_laptop *eeepc = dev_get_drvdata(device); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci if (eeepc->wlan_rfkill) { 8918c2ecf20Sopenharmony_ci int wlan; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci /* 8948c2ecf20Sopenharmony_ci * Work around bios bug - acpi _PTS turns off the wireless led 8958c2ecf20Sopenharmony_ci * during suspend. Normally it restores it on resume, but 8968c2ecf20Sopenharmony_ci * we should kick it ourselves in case hibernation is aborted. 8978c2ecf20Sopenharmony_ci */ 8988c2ecf20Sopenharmony_ci wlan = get_acpi(eeepc, CM_ASL_WLAN); 8998c2ecf20Sopenharmony_ci if (wlan >= 0) 9008c2ecf20Sopenharmony_ci set_acpi(eeepc, CM_ASL_WLAN, wlan); 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci return 0; 9048c2ecf20Sopenharmony_ci} 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_cistatic int eeepc_hotk_restore(struct device *device) 9078c2ecf20Sopenharmony_ci{ 9088c2ecf20Sopenharmony_ci struct eeepc_laptop *eeepc = dev_get_drvdata(device); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci /* Refresh both wlan rfkill state and pci hotplug */ 9118c2ecf20Sopenharmony_ci if (eeepc->wlan_rfkill) { 9128c2ecf20Sopenharmony_ci eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_1); 9138c2ecf20Sopenharmony_ci eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_2); 9148c2ecf20Sopenharmony_ci eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_3); 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci if (eeepc->bluetooth_rfkill) 9188c2ecf20Sopenharmony_ci rfkill_set_sw_state(eeepc->bluetooth_rfkill, 9198c2ecf20Sopenharmony_ci get_acpi(eeepc, CM_ASL_BLUETOOTH) != 1); 9208c2ecf20Sopenharmony_ci if (eeepc->wwan3g_rfkill) 9218c2ecf20Sopenharmony_ci rfkill_set_sw_state(eeepc->wwan3g_rfkill, 9228c2ecf20Sopenharmony_ci get_acpi(eeepc, CM_ASL_3G) != 1); 9238c2ecf20Sopenharmony_ci if (eeepc->wimax_rfkill) 9248c2ecf20Sopenharmony_ci rfkill_set_sw_state(eeepc->wimax_rfkill, 9258c2ecf20Sopenharmony_ci get_acpi(eeepc, CM_ASL_WIMAX) != 1); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci return 0; 9288c2ecf20Sopenharmony_ci} 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_cistatic const struct dev_pm_ops eeepc_pm_ops = { 9318c2ecf20Sopenharmony_ci .thaw = eeepc_hotk_thaw, 9328c2ecf20Sopenharmony_ci .restore = eeepc_hotk_restore, 9338c2ecf20Sopenharmony_ci}; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_cistatic struct platform_driver platform_driver = { 9368c2ecf20Sopenharmony_ci .driver = { 9378c2ecf20Sopenharmony_ci .name = EEEPC_LAPTOP_FILE, 9388c2ecf20Sopenharmony_ci .pm = &eeepc_pm_ops, 9398c2ecf20Sopenharmony_ci } 9408c2ecf20Sopenharmony_ci}; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci/* 9438c2ecf20Sopenharmony_ci * Hwmon device 9448c2ecf20Sopenharmony_ci */ 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci#define EEEPC_EC_SC00 0x61 9478c2ecf20Sopenharmony_ci#define EEEPC_EC_FAN_PWM (EEEPC_EC_SC00 + 2) /* Fan PWM duty cycle (%) */ 9488c2ecf20Sopenharmony_ci#define EEEPC_EC_FAN_HRPM (EEEPC_EC_SC00 + 5) /* High byte, fan speed (RPM) */ 9498c2ecf20Sopenharmony_ci#define EEEPC_EC_FAN_LRPM (EEEPC_EC_SC00 + 6) /* Low byte, fan speed (RPM) */ 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci#define EEEPC_EC_SFB0 0xD0 9528c2ecf20Sopenharmony_ci#define EEEPC_EC_FAN_CTRL (EEEPC_EC_SFB0 + 3) /* Byte containing SF25 */ 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_cistatic inline int eeepc_pwm_to_lmsensors(int value) 9558c2ecf20Sopenharmony_ci{ 9568c2ecf20Sopenharmony_ci return value * 255 / 100; 9578c2ecf20Sopenharmony_ci} 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_cistatic inline int eeepc_lmsensors_to_pwm(int value) 9608c2ecf20Sopenharmony_ci{ 9618c2ecf20Sopenharmony_ci value = clamp_val(value, 0, 255); 9628c2ecf20Sopenharmony_ci return value * 100 / 255; 9638c2ecf20Sopenharmony_ci} 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_cistatic int eeepc_get_fan_pwm(void) 9668c2ecf20Sopenharmony_ci{ 9678c2ecf20Sopenharmony_ci u8 value = 0; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci ec_read(EEEPC_EC_FAN_PWM, &value); 9708c2ecf20Sopenharmony_ci return eeepc_pwm_to_lmsensors(value); 9718c2ecf20Sopenharmony_ci} 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_cistatic void eeepc_set_fan_pwm(int value) 9748c2ecf20Sopenharmony_ci{ 9758c2ecf20Sopenharmony_ci value = eeepc_lmsensors_to_pwm(value); 9768c2ecf20Sopenharmony_ci ec_write(EEEPC_EC_FAN_PWM, value); 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_cistatic int eeepc_get_fan_rpm(void) 9808c2ecf20Sopenharmony_ci{ 9818c2ecf20Sopenharmony_ci u8 high = 0; 9828c2ecf20Sopenharmony_ci u8 low = 0; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci ec_read(EEEPC_EC_FAN_HRPM, &high); 9858c2ecf20Sopenharmony_ci ec_read(EEEPC_EC_FAN_LRPM, &low); 9868c2ecf20Sopenharmony_ci return high << 8 | low; 9878c2ecf20Sopenharmony_ci} 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci#define EEEPC_EC_FAN_CTRL_BIT 0x02 9908c2ecf20Sopenharmony_ci#define EEEPC_FAN_CTRL_MANUAL 1 9918c2ecf20Sopenharmony_ci#define EEEPC_FAN_CTRL_AUTO 2 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_cistatic int eeepc_get_fan_ctrl(void) 9948c2ecf20Sopenharmony_ci{ 9958c2ecf20Sopenharmony_ci u8 value = 0; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci ec_read(EEEPC_EC_FAN_CTRL, &value); 9988c2ecf20Sopenharmony_ci if (value & EEEPC_EC_FAN_CTRL_BIT) 9998c2ecf20Sopenharmony_ci return EEEPC_FAN_CTRL_MANUAL; 10008c2ecf20Sopenharmony_ci else 10018c2ecf20Sopenharmony_ci return EEEPC_FAN_CTRL_AUTO; 10028c2ecf20Sopenharmony_ci} 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_cistatic void eeepc_set_fan_ctrl(int manual) 10058c2ecf20Sopenharmony_ci{ 10068c2ecf20Sopenharmony_ci u8 value = 0; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci ec_read(EEEPC_EC_FAN_CTRL, &value); 10098c2ecf20Sopenharmony_ci if (manual == EEEPC_FAN_CTRL_MANUAL) 10108c2ecf20Sopenharmony_ci value |= EEEPC_EC_FAN_CTRL_BIT; 10118c2ecf20Sopenharmony_ci else 10128c2ecf20Sopenharmony_ci value &= ~EEEPC_EC_FAN_CTRL_BIT; 10138c2ecf20Sopenharmony_ci ec_write(EEEPC_EC_FAN_CTRL, value); 10148c2ecf20Sopenharmony_ci} 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_cistatic ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count) 10178c2ecf20Sopenharmony_ci{ 10188c2ecf20Sopenharmony_ci int rv, value; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci rv = parse_arg(buf, &value); 10218c2ecf20Sopenharmony_ci if (rv < 0) 10228c2ecf20Sopenharmony_ci return rv; 10238c2ecf20Sopenharmony_ci set(value); 10248c2ecf20Sopenharmony_ci return count; 10258c2ecf20Sopenharmony_ci} 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_cistatic ssize_t show_sys_hwmon(int (*get)(void), char *buf) 10288c2ecf20Sopenharmony_ci{ 10298c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", get()); 10308c2ecf20Sopenharmony_ci} 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci#define EEEPC_SENSOR_SHOW_FUNC(_name, _get) \ 10338c2ecf20Sopenharmony_ci static ssize_t _name##_show(struct device *dev, \ 10348c2ecf20Sopenharmony_ci struct device_attribute *attr, \ 10358c2ecf20Sopenharmony_ci char *buf) \ 10368c2ecf20Sopenharmony_ci { \ 10378c2ecf20Sopenharmony_ci return show_sys_hwmon(_get, buf); \ 10388c2ecf20Sopenharmony_ci } 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci#define EEEPC_SENSOR_STORE_FUNC(_name, _set) \ 10418c2ecf20Sopenharmony_ci static ssize_t _name##_store(struct device *dev, \ 10428c2ecf20Sopenharmony_ci struct device_attribute *attr, \ 10438c2ecf20Sopenharmony_ci const char *buf, size_t count) \ 10448c2ecf20Sopenharmony_ci { \ 10458c2ecf20Sopenharmony_ci return store_sys_hwmon(_set, buf, count); \ 10468c2ecf20Sopenharmony_ci } 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci#define EEEPC_CREATE_SENSOR_ATTR_RW(_name, _get, _set) \ 10498c2ecf20Sopenharmony_ci EEEPC_SENSOR_SHOW_FUNC(_name, _get) \ 10508c2ecf20Sopenharmony_ci EEEPC_SENSOR_STORE_FUNC(_name, _set) \ 10518c2ecf20Sopenharmony_ci static DEVICE_ATTR_RW(_name) 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci#define EEEPC_CREATE_SENSOR_ATTR_RO(_name, _get) \ 10548c2ecf20Sopenharmony_ci EEEPC_SENSOR_SHOW_FUNC(_name, _get) \ 10558c2ecf20Sopenharmony_ci static DEVICE_ATTR_RO(_name) 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ciEEEPC_CREATE_SENSOR_ATTR_RO(fan1_input, eeepc_get_fan_rpm); 10588c2ecf20Sopenharmony_ciEEEPC_CREATE_SENSOR_ATTR_RW(pwm1, eeepc_get_fan_pwm, 10598c2ecf20Sopenharmony_ci eeepc_set_fan_pwm); 10608c2ecf20Sopenharmony_ciEEEPC_CREATE_SENSOR_ATTR_RW(pwm1_enable, eeepc_get_fan_ctrl, 10618c2ecf20Sopenharmony_ci eeepc_set_fan_ctrl); 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_cistatic struct attribute *hwmon_attrs[] = { 10648c2ecf20Sopenharmony_ci &dev_attr_pwm1.attr, 10658c2ecf20Sopenharmony_ci &dev_attr_fan1_input.attr, 10668c2ecf20Sopenharmony_ci &dev_attr_pwm1_enable.attr, 10678c2ecf20Sopenharmony_ci NULL 10688c2ecf20Sopenharmony_ci}; 10698c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(hwmon); 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_cistatic int eeepc_hwmon_init(struct eeepc_laptop *eeepc) 10728c2ecf20Sopenharmony_ci{ 10738c2ecf20Sopenharmony_ci struct device *dev = &eeepc->platform_device->dev; 10748c2ecf20Sopenharmony_ci struct device *hwmon; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci hwmon = devm_hwmon_device_register_with_groups(dev, "eeepc", NULL, 10778c2ecf20Sopenharmony_ci hwmon_groups); 10788c2ecf20Sopenharmony_ci if (IS_ERR(hwmon)) { 10798c2ecf20Sopenharmony_ci pr_err("Could not register eeepc hwmon device\n"); 10808c2ecf20Sopenharmony_ci return PTR_ERR(hwmon); 10818c2ecf20Sopenharmony_ci } 10828c2ecf20Sopenharmony_ci return 0; 10838c2ecf20Sopenharmony_ci} 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci/* 10868c2ecf20Sopenharmony_ci * Backlight device 10878c2ecf20Sopenharmony_ci */ 10888c2ecf20Sopenharmony_cistatic int read_brightness(struct backlight_device *bd) 10898c2ecf20Sopenharmony_ci{ 10908c2ecf20Sopenharmony_ci struct eeepc_laptop *eeepc = bl_get_data(bd); 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci return get_acpi(eeepc, CM_ASL_PANELBRIGHT); 10938c2ecf20Sopenharmony_ci} 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_cistatic int set_brightness(struct backlight_device *bd, int value) 10968c2ecf20Sopenharmony_ci{ 10978c2ecf20Sopenharmony_ci struct eeepc_laptop *eeepc = bl_get_data(bd); 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci return set_acpi(eeepc, CM_ASL_PANELBRIGHT, value); 11008c2ecf20Sopenharmony_ci} 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_cistatic int update_bl_status(struct backlight_device *bd) 11038c2ecf20Sopenharmony_ci{ 11048c2ecf20Sopenharmony_ci return set_brightness(bd, bd->props.brightness); 11058c2ecf20Sopenharmony_ci} 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_cistatic const struct backlight_ops eeepcbl_ops = { 11088c2ecf20Sopenharmony_ci .get_brightness = read_brightness, 11098c2ecf20Sopenharmony_ci .update_status = update_bl_status, 11108c2ecf20Sopenharmony_ci}; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_cistatic int eeepc_backlight_notify(struct eeepc_laptop *eeepc) 11138c2ecf20Sopenharmony_ci{ 11148c2ecf20Sopenharmony_ci struct backlight_device *bd = eeepc->backlight_device; 11158c2ecf20Sopenharmony_ci int old = bd->props.brightness; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci return old; 11208c2ecf20Sopenharmony_ci} 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_cistatic int eeepc_backlight_init(struct eeepc_laptop *eeepc) 11238c2ecf20Sopenharmony_ci{ 11248c2ecf20Sopenharmony_ci struct backlight_properties props; 11258c2ecf20Sopenharmony_ci struct backlight_device *bd; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci memset(&props, 0, sizeof(struct backlight_properties)); 11288c2ecf20Sopenharmony_ci props.type = BACKLIGHT_PLATFORM; 11298c2ecf20Sopenharmony_ci props.max_brightness = 15; 11308c2ecf20Sopenharmony_ci bd = backlight_device_register(EEEPC_LAPTOP_FILE, 11318c2ecf20Sopenharmony_ci &eeepc->platform_device->dev, eeepc, 11328c2ecf20Sopenharmony_ci &eeepcbl_ops, &props); 11338c2ecf20Sopenharmony_ci if (IS_ERR(bd)) { 11348c2ecf20Sopenharmony_ci pr_err("Could not register eeepc backlight device\n"); 11358c2ecf20Sopenharmony_ci eeepc->backlight_device = NULL; 11368c2ecf20Sopenharmony_ci return PTR_ERR(bd); 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci eeepc->backlight_device = bd; 11398c2ecf20Sopenharmony_ci bd->props.brightness = read_brightness(bd); 11408c2ecf20Sopenharmony_ci bd->props.power = FB_BLANK_UNBLANK; 11418c2ecf20Sopenharmony_ci backlight_update_status(bd); 11428c2ecf20Sopenharmony_ci return 0; 11438c2ecf20Sopenharmony_ci} 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_cistatic void eeepc_backlight_exit(struct eeepc_laptop *eeepc) 11468c2ecf20Sopenharmony_ci{ 11478c2ecf20Sopenharmony_ci backlight_device_unregister(eeepc->backlight_device); 11488c2ecf20Sopenharmony_ci eeepc->backlight_device = NULL; 11498c2ecf20Sopenharmony_ci} 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci/* 11538c2ecf20Sopenharmony_ci * Input device (i.e. hotkeys) 11548c2ecf20Sopenharmony_ci */ 11558c2ecf20Sopenharmony_cistatic int eeepc_input_init(struct eeepc_laptop *eeepc) 11568c2ecf20Sopenharmony_ci{ 11578c2ecf20Sopenharmony_ci struct input_dev *input; 11588c2ecf20Sopenharmony_ci int error; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci input = input_allocate_device(); 11618c2ecf20Sopenharmony_ci if (!input) 11628c2ecf20Sopenharmony_ci return -ENOMEM; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci input->name = "Asus EeePC extra buttons"; 11658c2ecf20Sopenharmony_ci input->phys = EEEPC_LAPTOP_FILE "/input0"; 11668c2ecf20Sopenharmony_ci input->id.bustype = BUS_HOST; 11678c2ecf20Sopenharmony_ci input->dev.parent = &eeepc->platform_device->dev; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci error = sparse_keymap_setup(input, eeepc_keymap, NULL); 11708c2ecf20Sopenharmony_ci if (error) { 11718c2ecf20Sopenharmony_ci pr_err("Unable to setup input device keymap\n"); 11728c2ecf20Sopenharmony_ci goto err_free_dev; 11738c2ecf20Sopenharmony_ci } 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci error = input_register_device(input); 11768c2ecf20Sopenharmony_ci if (error) { 11778c2ecf20Sopenharmony_ci pr_err("Unable to register input device\n"); 11788c2ecf20Sopenharmony_ci goto err_free_dev; 11798c2ecf20Sopenharmony_ci } 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci eeepc->inputdev = input; 11828c2ecf20Sopenharmony_ci return 0; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_cierr_free_dev: 11858c2ecf20Sopenharmony_ci input_free_device(input); 11868c2ecf20Sopenharmony_ci return error; 11878c2ecf20Sopenharmony_ci} 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_cistatic void eeepc_input_exit(struct eeepc_laptop *eeepc) 11908c2ecf20Sopenharmony_ci{ 11918c2ecf20Sopenharmony_ci if (eeepc->inputdev) 11928c2ecf20Sopenharmony_ci input_unregister_device(eeepc->inputdev); 11938c2ecf20Sopenharmony_ci eeepc->inputdev = NULL; 11948c2ecf20Sopenharmony_ci} 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci/* 11978c2ecf20Sopenharmony_ci * ACPI driver 11988c2ecf20Sopenharmony_ci */ 11998c2ecf20Sopenharmony_cistatic void eeepc_input_notify(struct eeepc_laptop *eeepc, int event) 12008c2ecf20Sopenharmony_ci{ 12018c2ecf20Sopenharmony_ci if (!eeepc->inputdev) 12028c2ecf20Sopenharmony_ci return; 12038c2ecf20Sopenharmony_ci if (!sparse_keymap_report_event(eeepc->inputdev, event, 1, true)) 12048c2ecf20Sopenharmony_ci pr_info("Unknown key %x pressed\n", event); 12058c2ecf20Sopenharmony_ci} 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_cistatic void eeepc_acpi_notify(struct acpi_device *device, u32 event) 12088c2ecf20Sopenharmony_ci{ 12098c2ecf20Sopenharmony_ci struct eeepc_laptop *eeepc = acpi_driver_data(device); 12108c2ecf20Sopenharmony_ci int old_brightness, new_brightness; 12118c2ecf20Sopenharmony_ci u16 count; 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci if (event > ACPI_MAX_SYS_NOTIFY) 12148c2ecf20Sopenharmony_ci return; 12158c2ecf20Sopenharmony_ci count = eeepc->event_count[event % 128]++; 12168c2ecf20Sopenharmony_ci acpi_bus_generate_netlink_event(device->pnp.device_class, 12178c2ecf20Sopenharmony_ci dev_name(&device->dev), event, 12188c2ecf20Sopenharmony_ci count); 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci /* Brightness events are special */ 12218c2ecf20Sopenharmony_ci if (event < NOTIFY_BRN_MIN || event > NOTIFY_BRN_MAX) { 12228c2ecf20Sopenharmony_ci eeepc_input_notify(eeepc, event); 12238c2ecf20Sopenharmony_ci return; 12248c2ecf20Sopenharmony_ci } 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci /* Ignore them completely if the acpi video driver is used */ 12278c2ecf20Sopenharmony_ci if (!eeepc->backlight_device) 12288c2ecf20Sopenharmony_ci return; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci /* Update the backlight device. */ 12318c2ecf20Sopenharmony_ci old_brightness = eeepc_backlight_notify(eeepc); 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci /* Convert event to keypress (obsolescent hack) */ 12348c2ecf20Sopenharmony_ci new_brightness = event - NOTIFY_BRN_MIN; 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci if (new_brightness < old_brightness) { 12378c2ecf20Sopenharmony_ci event = NOTIFY_BRN_MIN; /* brightness down */ 12388c2ecf20Sopenharmony_ci } else if (new_brightness > old_brightness) { 12398c2ecf20Sopenharmony_ci event = NOTIFY_BRN_MAX; /* brightness up */ 12408c2ecf20Sopenharmony_ci } else { 12418c2ecf20Sopenharmony_ci /* 12428c2ecf20Sopenharmony_ci * no change in brightness - already at min/max, 12438c2ecf20Sopenharmony_ci * event will be desired value (or else ignored) 12448c2ecf20Sopenharmony_ci */ 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci eeepc_input_notify(eeepc, event); 12478c2ecf20Sopenharmony_ci} 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_cistatic void eeepc_dmi_check(struct eeepc_laptop *eeepc) 12508c2ecf20Sopenharmony_ci{ 12518c2ecf20Sopenharmony_ci const char *model; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci model = dmi_get_system_info(DMI_PRODUCT_NAME); 12548c2ecf20Sopenharmony_ci if (!model) 12558c2ecf20Sopenharmony_ci return; 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci /* 12588c2ecf20Sopenharmony_ci * Blacklist for setting cpufv (cpu speed). 12598c2ecf20Sopenharmony_ci * 12608c2ecf20Sopenharmony_ci * EeePC 4G ("701") implements CFVS, but it is not supported 12618c2ecf20Sopenharmony_ci * by the pre-installed OS, and the original option to change it 12628c2ecf20Sopenharmony_ci * in the BIOS setup screen was removed in later versions. 12638c2ecf20Sopenharmony_ci * 12648c2ecf20Sopenharmony_ci * Judging by the lack of "Super Hybrid Engine" on Asus product pages, 12658c2ecf20Sopenharmony_ci * this applies to all "701" models (4G/4G Surf/2G Surf). 12668c2ecf20Sopenharmony_ci * 12678c2ecf20Sopenharmony_ci * So Asus made a deliberate decision not to support it on this model. 12688c2ecf20Sopenharmony_ci * We have several reports that using it can cause the system to hang 12698c2ecf20Sopenharmony_ci * 12708c2ecf20Sopenharmony_ci * The hang has also been reported on a "702" (Model name "8G"?). 12718c2ecf20Sopenharmony_ci * 12728c2ecf20Sopenharmony_ci * We avoid dmi_check_system() / dmi_match(), because they use 12738c2ecf20Sopenharmony_ci * substring matching. We don't want to affect the "701SD" 12748c2ecf20Sopenharmony_ci * and "701SDX" models, because they do support S.H.E. 12758c2ecf20Sopenharmony_ci */ 12768c2ecf20Sopenharmony_ci if (strcmp(model, "701") == 0 || strcmp(model, "702") == 0) { 12778c2ecf20Sopenharmony_ci eeepc->cpufv_disabled = true; 12788c2ecf20Sopenharmony_ci pr_info("model %s does not officially support setting cpu speed\n", 12798c2ecf20Sopenharmony_ci model); 12808c2ecf20Sopenharmony_ci pr_info("cpufv disabled to avoid instability\n"); 12818c2ecf20Sopenharmony_ci } 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci /* 12848c2ecf20Sopenharmony_ci * Blacklist for wlan hotplug 12858c2ecf20Sopenharmony_ci * 12868c2ecf20Sopenharmony_ci * Eeepc 1005HA doesn't work like others models and don't need the 12878c2ecf20Sopenharmony_ci * hotplug code. In fact, current hotplug code seems to unplug another 12888c2ecf20Sopenharmony_ci * device... 12898c2ecf20Sopenharmony_ci */ 12908c2ecf20Sopenharmony_ci if (strcmp(model, "1005HA") == 0 || strcmp(model, "1201N") == 0 || 12918c2ecf20Sopenharmony_ci strcmp(model, "1005PE") == 0) { 12928c2ecf20Sopenharmony_ci eeepc->hotplug_disabled = true; 12938c2ecf20Sopenharmony_ci pr_info("wlan hotplug disabled\n"); 12948c2ecf20Sopenharmony_ci } 12958c2ecf20Sopenharmony_ci} 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_cistatic void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name) 12988c2ecf20Sopenharmony_ci{ 12998c2ecf20Sopenharmony_ci int dummy; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci /* Some BIOSes do not report cm although it is available. 13028c2ecf20Sopenharmony_ci Check if cm_getv[cm] works and, if yes, assume cm should be set. */ 13038c2ecf20Sopenharmony_ci if (!(eeepc->cm_supported & (1 << cm)) 13048c2ecf20Sopenharmony_ci && !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) { 13058c2ecf20Sopenharmony_ci pr_info("%s (%x) not reported by BIOS, enabling anyway\n", 13068c2ecf20Sopenharmony_ci name, 1 << cm); 13078c2ecf20Sopenharmony_ci eeepc->cm_supported |= 1 << cm; 13088c2ecf20Sopenharmony_ci } 13098c2ecf20Sopenharmony_ci} 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_cistatic void cmsg_quirks(struct eeepc_laptop *eeepc) 13128c2ecf20Sopenharmony_ci{ 13138c2ecf20Sopenharmony_ci cmsg_quirk(eeepc, CM_ASL_LID, "LID"); 13148c2ecf20Sopenharmony_ci cmsg_quirk(eeepc, CM_ASL_TYPE, "TYPE"); 13158c2ecf20Sopenharmony_ci cmsg_quirk(eeepc, CM_ASL_PANELPOWER, "PANELPOWER"); 13168c2ecf20Sopenharmony_ci cmsg_quirk(eeepc, CM_ASL_TPD, "TPD"); 13178c2ecf20Sopenharmony_ci} 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_cistatic int eeepc_acpi_init(struct eeepc_laptop *eeepc) 13208c2ecf20Sopenharmony_ci{ 13218c2ecf20Sopenharmony_ci unsigned int init_flags; 13228c2ecf20Sopenharmony_ci int result; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci result = acpi_bus_get_status(eeepc->device); 13258c2ecf20Sopenharmony_ci if (result) 13268c2ecf20Sopenharmony_ci return result; 13278c2ecf20Sopenharmony_ci if (!eeepc->device->status.present) { 13288c2ecf20Sopenharmony_ci pr_err("Hotkey device not present, aborting\n"); 13298c2ecf20Sopenharmony_ci return -ENODEV; 13308c2ecf20Sopenharmony_ci } 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci init_flags = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH; 13338c2ecf20Sopenharmony_ci pr_notice("Hotkey init flags 0x%x\n", init_flags); 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci if (write_acpi_int(eeepc->handle, "INIT", init_flags)) { 13368c2ecf20Sopenharmony_ci pr_err("Hotkey initialization failed\n"); 13378c2ecf20Sopenharmony_ci return -ENODEV; 13388c2ecf20Sopenharmony_ci } 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci /* get control methods supported */ 13418c2ecf20Sopenharmony_ci if (read_acpi_int(eeepc->handle, "CMSG", &eeepc->cm_supported)) { 13428c2ecf20Sopenharmony_ci pr_err("Get control methods supported failed\n"); 13438c2ecf20Sopenharmony_ci return -ENODEV; 13448c2ecf20Sopenharmony_ci } 13458c2ecf20Sopenharmony_ci cmsg_quirks(eeepc); 13468c2ecf20Sopenharmony_ci pr_info("Get control methods supported: 0x%x\n", eeepc->cm_supported); 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci return 0; 13498c2ecf20Sopenharmony_ci} 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_cistatic void eeepc_enable_camera(struct eeepc_laptop *eeepc) 13528c2ecf20Sopenharmony_ci{ 13538c2ecf20Sopenharmony_ci /* 13548c2ecf20Sopenharmony_ci * If the following call to set_acpi() fails, it's because there's no 13558c2ecf20Sopenharmony_ci * camera so we can ignore the error. 13568c2ecf20Sopenharmony_ci */ 13578c2ecf20Sopenharmony_ci if (get_acpi(eeepc, CM_ASL_CAMERA) == 0) 13588c2ecf20Sopenharmony_ci set_acpi(eeepc, CM_ASL_CAMERA, 1); 13598c2ecf20Sopenharmony_ci} 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_cistatic bool eeepc_device_present; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_cistatic int eeepc_acpi_add(struct acpi_device *device) 13648c2ecf20Sopenharmony_ci{ 13658c2ecf20Sopenharmony_ci struct eeepc_laptop *eeepc; 13668c2ecf20Sopenharmony_ci int result; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci pr_notice(EEEPC_LAPTOP_NAME "\n"); 13698c2ecf20Sopenharmony_ci eeepc = kzalloc(sizeof(struct eeepc_laptop), GFP_KERNEL); 13708c2ecf20Sopenharmony_ci if (!eeepc) 13718c2ecf20Sopenharmony_ci return -ENOMEM; 13728c2ecf20Sopenharmony_ci eeepc->handle = device->handle; 13738c2ecf20Sopenharmony_ci strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME); 13748c2ecf20Sopenharmony_ci strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS); 13758c2ecf20Sopenharmony_ci device->driver_data = eeepc; 13768c2ecf20Sopenharmony_ci eeepc->device = device; 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci eeepc->hotplug_disabled = hotplug_disabled; 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci eeepc_dmi_check(eeepc); 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci result = eeepc_acpi_init(eeepc); 13838c2ecf20Sopenharmony_ci if (result) 13848c2ecf20Sopenharmony_ci goto fail_platform; 13858c2ecf20Sopenharmony_ci eeepc_enable_camera(eeepc); 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci /* 13888c2ecf20Sopenharmony_ci * Register the platform device first. It is used as a parent for the 13898c2ecf20Sopenharmony_ci * sub-devices below. 13908c2ecf20Sopenharmony_ci * 13918c2ecf20Sopenharmony_ci * Note that if there are multiple instances of this ACPI device it 13928c2ecf20Sopenharmony_ci * will bail out, because the platform device is registered with a 13938c2ecf20Sopenharmony_ci * fixed name. Of course it doesn't make sense to have more than one, 13948c2ecf20Sopenharmony_ci * and machine-specific scripts find the fixed name convenient. But 13958c2ecf20Sopenharmony_ci * It's also good for us to exclude multiple instances because both 13968c2ecf20Sopenharmony_ci * our hwmon and our wlan rfkill subdevice use global ACPI objects 13978c2ecf20Sopenharmony_ci * (the EC and the wlan PCI slot respectively). 13988c2ecf20Sopenharmony_ci */ 13998c2ecf20Sopenharmony_ci result = eeepc_platform_init(eeepc); 14008c2ecf20Sopenharmony_ci if (result) 14018c2ecf20Sopenharmony_ci goto fail_platform; 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci if (acpi_video_get_backlight_type() == acpi_backlight_vendor) { 14048c2ecf20Sopenharmony_ci result = eeepc_backlight_init(eeepc); 14058c2ecf20Sopenharmony_ci if (result) 14068c2ecf20Sopenharmony_ci goto fail_backlight; 14078c2ecf20Sopenharmony_ci } 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci result = eeepc_input_init(eeepc); 14108c2ecf20Sopenharmony_ci if (result) 14118c2ecf20Sopenharmony_ci goto fail_input; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci result = eeepc_hwmon_init(eeepc); 14148c2ecf20Sopenharmony_ci if (result) 14158c2ecf20Sopenharmony_ci goto fail_hwmon; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci result = eeepc_led_init(eeepc); 14188c2ecf20Sopenharmony_ci if (result) 14198c2ecf20Sopenharmony_ci goto fail_led; 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci result = eeepc_rfkill_init(eeepc); 14228c2ecf20Sopenharmony_ci if (result) 14238c2ecf20Sopenharmony_ci goto fail_rfkill; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci eeepc_device_present = true; 14268c2ecf20Sopenharmony_ci return 0; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_cifail_rfkill: 14298c2ecf20Sopenharmony_ci eeepc_led_exit(eeepc); 14308c2ecf20Sopenharmony_cifail_led: 14318c2ecf20Sopenharmony_cifail_hwmon: 14328c2ecf20Sopenharmony_ci eeepc_input_exit(eeepc); 14338c2ecf20Sopenharmony_cifail_input: 14348c2ecf20Sopenharmony_ci eeepc_backlight_exit(eeepc); 14358c2ecf20Sopenharmony_cifail_backlight: 14368c2ecf20Sopenharmony_ci eeepc_platform_exit(eeepc); 14378c2ecf20Sopenharmony_cifail_platform: 14388c2ecf20Sopenharmony_ci kfree(eeepc); 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci return result; 14418c2ecf20Sopenharmony_ci} 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_cistatic int eeepc_acpi_remove(struct acpi_device *device) 14448c2ecf20Sopenharmony_ci{ 14458c2ecf20Sopenharmony_ci struct eeepc_laptop *eeepc = acpi_driver_data(device); 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci eeepc_backlight_exit(eeepc); 14488c2ecf20Sopenharmony_ci eeepc_rfkill_exit(eeepc); 14498c2ecf20Sopenharmony_ci eeepc_input_exit(eeepc); 14508c2ecf20Sopenharmony_ci eeepc_led_exit(eeepc); 14518c2ecf20Sopenharmony_ci eeepc_platform_exit(eeepc); 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci kfree(eeepc); 14548c2ecf20Sopenharmony_ci return 0; 14558c2ecf20Sopenharmony_ci} 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_cistatic const struct acpi_device_id eeepc_device_ids[] = { 14598c2ecf20Sopenharmony_ci {EEEPC_ACPI_HID, 0}, 14608c2ecf20Sopenharmony_ci {"", 0}, 14618c2ecf20Sopenharmony_ci}; 14628c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, eeepc_device_ids); 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_cistatic struct acpi_driver eeepc_acpi_driver = { 14658c2ecf20Sopenharmony_ci .name = EEEPC_LAPTOP_NAME, 14668c2ecf20Sopenharmony_ci .class = EEEPC_ACPI_CLASS, 14678c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 14688c2ecf20Sopenharmony_ci .ids = eeepc_device_ids, 14698c2ecf20Sopenharmony_ci .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, 14708c2ecf20Sopenharmony_ci .ops = { 14718c2ecf20Sopenharmony_ci .add = eeepc_acpi_add, 14728c2ecf20Sopenharmony_ci .remove = eeepc_acpi_remove, 14738c2ecf20Sopenharmony_ci .notify = eeepc_acpi_notify, 14748c2ecf20Sopenharmony_ci }, 14758c2ecf20Sopenharmony_ci}; 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_cistatic int __init eeepc_laptop_init(void) 14798c2ecf20Sopenharmony_ci{ 14808c2ecf20Sopenharmony_ci int result; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci result = platform_driver_register(&platform_driver); 14838c2ecf20Sopenharmony_ci if (result < 0) 14848c2ecf20Sopenharmony_ci return result; 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci result = acpi_bus_register_driver(&eeepc_acpi_driver); 14878c2ecf20Sopenharmony_ci if (result < 0) 14888c2ecf20Sopenharmony_ci goto fail_acpi_driver; 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci if (!eeepc_device_present) { 14918c2ecf20Sopenharmony_ci result = -ENODEV; 14928c2ecf20Sopenharmony_ci goto fail_no_device; 14938c2ecf20Sopenharmony_ci } 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci return 0; 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_cifail_no_device: 14988c2ecf20Sopenharmony_ci acpi_bus_unregister_driver(&eeepc_acpi_driver); 14998c2ecf20Sopenharmony_cifail_acpi_driver: 15008c2ecf20Sopenharmony_ci platform_driver_unregister(&platform_driver); 15018c2ecf20Sopenharmony_ci return result; 15028c2ecf20Sopenharmony_ci} 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_cistatic void __exit eeepc_laptop_exit(void) 15058c2ecf20Sopenharmony_ci{ 15068c2ecf20Sopenharmony_ci acpi_bus_unregister_driver(&eeepc_acpi_driver); 15078c2ecf20Sopenharmony_ci platform_driver_unregister(&platform_driver); 15088c2ecf20Sopenharmony_ci} 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_cimodule_init(eeepc_laptop_init); 15118c2ecf20Sopenharmony_cimodule_exit(eeepc_laptop_exit); 1512