162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * eeepc-laptop.c - Asus Eee PC extras 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Based on asus_acpi.c as patched for the Eee PC by Asus: 662306a36Sopenharmony_ci * ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar 762306a36Sopenharmony_ci * Based on eee.c from eeepc-linux 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/types.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/backlight.h> 1862306a36Sopenharmony_ci#include <linux/fb.h> 1962306a36Sopenharmony_ci#include <linux/hwmon.h> 2062306a36Sopenharmony_ci#include <linux/hwmon-sysfs.h> 2162306a36Sopenharmony_ci#include <linux/slab.h> 2262306a36Sopenharmony_ci#include <linux/acpi.h> 2362306a36Sopenharmony_ci#include <linux/uaccess.h> 2462306a36Sopenharmony_ci#include <linux/input.h> 2562306a36Sopenharmony_ci#include <linux/input/sparse-keymap.h> 2662306a36Sopenharmony_ci#include <linux/rfkill.h> 2762306a36Sopenharmony_ci#include <linux/pci.h> 2862306a36Sopenharmony_ci#include <linux/pci_hotplug.h> 2962306a36Sopenharmony_ci#include <linux/leds.h> 3062306a36Sopenharmony_ci#include <linux/dmi.h> 3162306a36Sopenharmony_ci#include <acpi/video.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define EEEPC_LAPTOP_VERSION "0.1" 3462306a36Sopenharmony_ci#define EEEPC_LAPTOP_NAME "Eee PC Hotkey Driver" 3562306a36Sopenharmony_ci#define EEEPC_LAPTOP_FILE "eeepc" 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define EEEPC_ACPI_CLASS "hotkey" 3862306a36Sopenharmony_ci#define EEEPC_ACPI_DEVICE_NAME "Hotkey" 3962306a36Sopenharmony_ci#define EEEPC_ACPI_HID "ASUS010" 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ciMODULE_AUTHOR("Corentin Chary, Eric Cooper"); 4262306a36Sopenharmony_ciMODULE_DESCRIPTION(EEEPC_LAPTOP_NAME); 4362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic bool hotplug_disabled; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cimodule_param(hotplug_disabled, bool, 0444); 4862306a36Sopenharmony_ciMODULE_PARM_DESC(hotplug_disabled, 4962306a36Sopenharmony_ci "Disable hotplug for wireless device. " 5062306a36Sopenharmony_ci "If your laptop need that, please report to " 5162306a36Sopenharmony_ci "acpi4asus-user@lists.sourceforge.net."); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* 5462306a36Sopenharmony_ci * Definitions for Asus EeePC 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_ci#define NOTIFY_BRN_MIN 0x20 5762306a36Sopenharmony_ci#define NOTIFY_BRN_MAX 0x2f 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cienum { 6062306a36Sopenharmony_ci DISABLE_ASL_WLAN = 0x0001, 6162306a36Sopenharmony_ci DISABLE_ASL_BLUETOOTH = 0x0002, 6262306a36Sopenharmony_ci DISABLE_ASL_IRDA = 0x0004, 6362306a36Sopenharmony_ci DISABLE_ASL_CAMERA = 0x0008, 6462306a36Sopenharmony_ci DISABLE_ASL_TV = 0x0010, 6562306a36Sopenharmony_ci DISABLE_ASL_GPS = 0x0020, 6662306a36Sopenharmony_ci DISABLE_ASL_DISPLAYSWITCH = 0x0040, 6762306a36Sopenharmony_ci DISABLE_ASL_MODEM = 0x0080, 6862306a36Sopenharmony_ci DISABLE_ASL_CARDREADER = 0x0100, 6962306a36Sopenharmony_ci DISABLE_ASL_3G = 0x0200, 7062306a36Sopenharmony_ci DISABLE_ASL_WIMAX = 0x0400, 7162306a36Sopenharmony_ci DISABLE_ASL_HWCF = 0x0800 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cienum { 7562306a36Sopenharmony_ci CM_ASL_WLAN = 0, 7662306a36Sopenharmony_ci CM_ASL_BLUETOOTH, 7762306a36Sopenharmony_ci CM_ASL_IRDA, 7862306a36Sopenharmony_ci CM_ASL_1394, 7962306a36Sopenharmony_ci CM_ASL_CAMERA, 8062306a36Sopenharmony_ci CM_ASL_TV, 8162306a36Sopenharmony_ci CM_ASL_GPS, 8262306a36Sopenharmony_ci CM_ASL_DVDROM, 8362306a36Sopenharmony_ci CM_ASL_DISPLAYSWITCH, 8462306a36Sopenharmony_ci CM_ASL_PANELBRIGHT, 8562306a36Sopenharmony_ci CM_ASL_BIOSFLASH, 8662306a36Sopenharmony_ci CM_ASL_ACPIFLASH, 8762306a36Sopenharmony_ci CM_ASL_CPUFV, 8862306a36Sopenharmony_ci CM_ASL_CPUTEMPERATURE, 8962306a36Sopenharmony_ci CM_ASL_FANCPU, 9062306a36Sopenharmony_ci CM_ASL_FANCHASSIS, 9162306a36Sopenharmony_ci CM_ASL_USBPORT1, 9262306a36Sopenharmony_ci CM_ASL_USBPORT2, 9362306a36Sopenharmony_ci CM_ASL_USBPORT3, 9462306a36Sopenharmony_ci CM_ASL_MODEM, 9562306a36Sopenharmony_ci CM_ASL_CARDREADER, 9662306a36Sopenharmony_ci CM_ASL_3G, 9762306a36Sopenharmony_ci CM_ASL_WIMAX, 9862306a36Sopenharmony_ci CM_ASL_HWCF, 9962306a36Sopenharmony_ci CM_ASL_LID, 10062306a36Sopenharmony_ci CM_ASL_TYPE, 10162306a36Sopenharmony_ci CM_ASL_PANELPOWER, /*P901*/ 10262306a36Sopenharmony_ci CM_ASL_TPD 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic const char *cm_getv[] = { 10662306a36Sopenharmony_ci "WLDG", "BTHG", NULL, NULL, 10762306a36Sopenharmony_ci "CAMG", NULL, NULL, NULL, 10862306a36Sopenharmony_ci NULL, "PBLG", NULL, NULL, 10962306a36Sopenharmony_ci "CFVG", NULL, NULL, NULL, 11062306a36Sopenharmony_ci "USBG", NULL, NULL, "MODG", 11162306a36Sopenharmony_ci "CRDG", "M3GG", "WIMG", "HWCF", 11262306a36Sopenharmony_ci "LIDG", "TYPE", "PBPG", "TPDG" 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic const char *cm_setv[] = { 11662306a36Sopenharmony_ci "WLDS", "BTHS", NULL, NULL, 11762306a36Sopenharmony_ci "CAMS", NULL, NULL, NULL, 11862306a36Sopenharmony_ci "SDSP", "PBLS", "HDPS", NULL, 11962306a36Sopenharmony_ci "CFVS", NULL, NULL, NULL, 12062306a36Sopenharmony_ci "USBG", NULL, NULL, "MODS", 12162306a36Sopenharmony_ci "CRDS", "M3GS", "WIMS", NULL, 12262306a36Sopenharmony_ci NULL, NULL, "PBPS", "TPDS" 12362306a36Sopenharmony_ci}; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic const struct key_entry eeepc_keymap[] = { 12662306a36Sopenharmony_ci { KE_KEY, 0x10, { KEY_WLAN } }, 12762306a36Sopenharmony_ci { KE_KEY, 0x11, { KEY_WLAN } }, 12862306a36Sopenharmony_ci { KE_KEY, 0x12, { KEY_PROG1 } }, 12962306a36Sopenharmony_ci { KE_KEY, 0x13, { KEY_MUTE } }, 13062306a36Sopenharmony_ci { KE_KEY, 0x14, { KEY_VOLUMEDOWN } }, 13162306a36Sopenharmony_ci { KE_KEY, 0x15, { KEY_VOLUMEUP } }, 13262306a36Sopenharmony_ci { KE_KEY, 0x16, { KEY_DISPLAY_OFF } }, 13362306a36Sopenharmony_ci { KE_KEY, 0x1a, { KEY_COFFEE } }, 13462306a36Sopenharmony_ci { KE_KEY, 0x1b, { KEY_ZOOM } }, 13562306a36Sopenharmony_ci { KE_KEY, 0x1c, { KEY_PROG2 } }, 13662306a36Sopenharmony_ci { KE_KEY, 0x1d, { KEY_PROG3 } }, 13762306a36Sopenharmony_ci { KE_KEY, NOTIFY_BRN_MIN, { KEY_BRIGHTNESSDOWN } }, 13862306a36Sopenharmony_ci { KE_KEY, NOTIFY_BRN_MAX, { KEY_BRIGHTNESSUP } }, 13962306a36Sopenharmony_ci { KE_KEY, 0x30, { KEY_SWITCHVIDEOMODE } }, 14062306a36Sopenharmony_ci { KE_KEY, 0x31, { KEY_SWITCHVIDEOMODE } }, 14162306a36Sopenharmony_ci { KE_KEY, 0x32, { KEY_SWITCHVIDEOMODE } }, 14262306a36Sopenharmony_ci { KE_KEY, 0x37, { KEY_F13 } }, /* Disable Touchpad */ 14362306a36Sopenharmony_ci { KE_KEY, 0x38, { KEY_F14 } }, 14462306a36Sopenharmony_ci { KE_IGNORE, 0x50, { KEY_RESERVED } }, /* AC plugged */ 14562306a36Sopenharmony_ci { KE_IGNORE, 0x51, { KEY_RESERVED } }, /* AC unplugged */ 14662306a36Sopenharmony_ci { KE_END, 0 }, 14762306a36Sopenharmony_ci}; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* 15062306a36Sopenharmony_ci * This is the main structure, we can use it to store useful information 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_cistruct eeepc_laptop { 15362306a36Sopenharmony_ci acpi_handle handle; /* the handle of the acpi device */ 15462306a36Sopenharmony_ci u32 cm_supported; /* the control methods supported 15562306a36Sopenharmony_ci by this BIOS */ 15662306a36Sopenharmony_ci bool cpufv_disabled; 15762306a36Sopenharmony_ci bool hotplug_disabled; 15862306a36Sopenharmony_ci u16 event_count[128]; /* count for each event */ 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci struct platform_device *platform_device; 16162306a36Sopenharmony_ci struct acpi_device *device; /* the device we are in */ 16262306a36Sopenharmony_ci struct backlight_device *backlight_device; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci struct input_dev *inputdev; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci struct rfkill *wlan_rfkill; 16762306a36Sopenharmony_ci struct rfkill *bluetooth_rfkill; 16862306a36Sopenharmony_ci struct rfkill *wwan3g_rfkill; 16962306a36Sopenharmony_ci struct rfkill *wimax_rfkill; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci struct hotplug_slot hotplug_slot; 17262306a36Sopenharmony_ci struct mutex hotplug_lock; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci struct led_classdev tpd_led; 17562306a36Sopenharmony_ci int tpd_led_wk; 17662306a36Sopenharmony_ci struct workqueue_struct *led_workqueue; 17762306a36Sopenharmony_ci struct work_struct tpd_led_work; 17862306a36Sopenharmony_ci}; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/* 18162306a36Sopenharmony_ci * ACPI Helpers 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_cistatic int write_acpi_int(acpi_handle handle, const char *method, int val) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci acpi_status status; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci status = acpi_execute_simple_method(handle, (char *)method, val); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return (status == AE_OK ? 0 : -1); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic int read_acpi_int(acpi_handle handle, const char *method, int *val) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci acpi_status status; 19562306a36Sopenharmony_ci unsigned long long result; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci status = acpi_evaluate_integer(handle, (char *)method, NULL, &result); 19862306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 19962306a36Sopenharmony_ci *val = -1; 20062306a36Sopenharmony_ci return -1; 20162306a36Sopenharmony_ci } else { 20262306a36Sopenharmony_ci *val = result; 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic int set_acpi(struct eeepc_laptop *eeepc, int cm, int value) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci const char *method = cm_setv[cm]; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (method == NULL) 21262306a36Sopenharmony_ci return -ENODEV; 21362306a36Sopenharmony_ci if ((eeepc->cm_supported & (0x1 << cm)) == 0) 21462306a36Sopenharmony_ci return -ENODEV; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (write_acpi_int(eeepc->handle, method, value)) 21762306a36Sopenharmony_ci pr_warn("Error writing %s\n", method); 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic int get_acpi(struct eeepc_laptop *eeepc, int cm) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci const char *method = cm_getv[cm]; 22462306a36Sopenharmony_ci int value; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (method == NULL) 22762306a36Sopenharmony_ci return -ENODEV; 22862306a36Sopenharmony_ci if ((eeepc->cm_supported & (0x1 << cm)) == 0) 22962306a36Sopenharmony_ci return -ENODEV; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (read_acpi_int(eeepc->handle, method, &value)) 23262306a36Sopenharmony_ci pr_warn("Error reading %s\n", method); 23362306a36Sopenharmony_ci return value; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic int acpi_setter_handle(struct eeepc_laptop *eeepc, int cm, 23762306a36Sopenharmony_ci acpi_handle *handle) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci const char *method = cm_setv[cm]; 24062306a36Sopenharmony_ci acpi_status status; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (method == NULL) 24362306a36Sopenharmony_ci return -ENODEV; 24462306a36Sopenharmony_ci if ((eeepc->cm_supported & (0x1 << cm)) == 0) 24562306a36Sopenharmony_ci return -ENODEV; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci status = acpi_get_handle(eeepc->handle, (char *)method, 24862306a36Sopenharmony_ci handle); 24962306a36Sopenharmony_ci if (status != AE_OK) { 25062306a36Sopenharmony_ci pr_warn("Error finding %s\n", method); 25162306a36Sopenharmony_ci return -ENODEV; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci return 0; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci/* 25862306a36Sopenharmony_ci * Sys helpers 25962306a36Sopenharmony_ci */ 26062306a36Sopenharmony_cistatic int parse_arg(const char *buf, int *val) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci if (sscanf(buf, "%i", val) != 1) 26362306a36Sopenharmony_ci return -EINVAL; 26462306a36Sopenharmony_ci return 0; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic ssize_t store_sys_acpi(struct device *dev, int cm, 26862306a36Sopenharmony_ci const char *buf, size_t count) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct eeepc_laptop *eeepc = dev_get_drvdata(dev); 27162306a36Sopenharmony_ci int rv, value; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci rv = parse_arg(buf, &value); 27462306a36Sopenharmony_ci if (rv < 0) 27562306a36Sopenharmony_ci return rv; 27662306a36Sopenharmony_ci rv = set_acpi(eeepc, cm, value); 27762306a36Sopenharmony_ci if (rv < 0) 27862306a36Sopenharmony_ci return -EIO; 27962306a36Sopenharmony_ci return count; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic ssize_t show_sys_acpi(struct device *dev, int cm, char *buf) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci struct eeepc_laptop *eeepc = dev_get_drvdata(dev); 28562306a36Sopenharmony_ci int value = get_acpi(eeepc, cm); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (value < 0) 28862306a36Sopenharmony_ci return -EIO; 28962306a36Sopenharmony_ci return sprintf(buf, "%d\n", value); 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci#define EEEPC_ACPI_SHOW_FUNC(_name, _cm) \ 29362306a36Sopenharmony_ci static ssize_t _name##_show(struct device *dev, \ 29462306a36Sopenharmony_ci struct device_attribute *attr, \ 29562306a36Sopenharmony_ci char *buf) \ 29662306a36Sopenharmony_ci { \ 29762306a36Sopenharmony_ci return show_sys_acpi(dev, _cm, buf); \ 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci#define EEEPC_ACPI_STORE_FUNC(_name, _cm) \ 30162306a36Sopenharmony_ci static ssize_t _name##_store(struct device *dev, \ 30262306a36Sopenharmony_ci struct device_attribute *attr, \ 30362306a36Sopenharmony_ci const char *buf, size_t count) \ 30462306a36Sopenharmony_ci { \ 30562306a36Sopenharmony_ci return store_sys_acpi(dev, _cm, buf, count); \ 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci#define EEEPC_CREATE_DEVICE_ATTR_RW(_name, _cm) \ 30962306a36Sopenharmony_ci EEEPC_ACPI_SHOW_FUNC(_name, _cm) \ 31062306a36Sopenharmony_ci EEEPC_ACPI_STORE_FUNC(_name, _cm) \ 31162306a36Sopenharmony_ci static DEVICE_ATTR_RW(_name) 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci#define EEEPC_CREATE_DEVICE_ATTR_WO(_name, _cm) \ 31462306a36Sopenharmony_ci EEEPC_ACPI_STORE_FUNC(_name, _cm) \ 31562306a36Sopenharmony_ci static DEVICE_ATTR_WO(_name) 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ciEEEPC_CREATE_DEVICE_ATTR_RW(camera, CM_ASL_CAMERA); 31862306a36Sopenharmony_ciEEEPC_CREATE_DEVICE_ATTR_RW(cardr, CM_ASL_CARDREADER); 31962306a36Sopenharmony_ciEEEPC_CREATE_DEVICE_ATTR_WO(disp, CM_ASL_DISPLAYSWITCH); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistruct eeepc_cpufv { 32262306a36Sopenharmony_ci int num; 32362306a36Sopenharmony_ci int cur; 32462306a36Sopenharmony_ci}; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic int get_cpufv(struct eeepc_laptop *eeepc, struct eeepc_cpufv *c) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci c->cur = get_acpi(eeepc, CM_ASL_CPUFV); 32962306a36Sopenharmony_ci if (c->cur < 0) 33062306a36Sopenharmony_ci return -ENODEV; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci c->num = (c->cur >> 8) & 0xff; 33362306a36Sopenharmony_ci c->cur &= 0xff; 33462306a36Sopenharmony_ci if (c->num == 0 || c->num > 12) 33562306a36Sopenharmony_ci return -ENODEV; 33662306a36Sopenharmony_ci return 0; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic ssize_t available_cpufv_show(struct device *dev, 34062306a36Sopenharmony_ci struct device_attribute *attr, 34162306a36Sopenharmony_ci char *buf) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci struct eeepc_laptop *eeepc = dev_get_drvdata(dev); 34462306a36Sopenharmony_ci struct eeepc_cpufv c; 34562306a36Sopenharmony_ci int i; 34662306a36Sopenharmony_ci ssize_t len = 0; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (get_cpufv(eeepc, &c)) 34962306a36Sopenharmony_ci return -ENODEV; 35062306a36Sopenharmony_ci for (i = 0; i < c.num; i++) 35162306a36Sopenharmony_ci len += sprintf(buf + len, "%d ", i); 35262306a36Sopenharmony_ci len += sprintf(buf + len, "\n"); 35362306a36Sopenharmony_ci return len; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic ssize_t cpufv_show(struct device *dev, 35762306a36Sopenharmony_ci struct device_attribute *attr, 35862306a36Sopenharmony_ci char *buf) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct eeepc_laptop *eeepc = dev_get_drvdata(dev); 36162306a36Sopenharmony_ci struct eeepc_cpufv c; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (get_cpufv(eeepc, &c)) 36462306a36Sopenharmony_ci return -ENODEV; 36562306a36Sopenharmony_ci return sprintf(buf, "%#x\n", (c.num << 8) | c.cur); 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic ssize_t cpufv_store(struct device *dev, 36962306a36Sopenharmony_ci struct device_attribute *attr, 37062306a36Sopenharmony_ci const char *buf, size_t count) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct eeepc_laptop *eeepc = dev_get_drvdata(dev); 37362306a36Sopenharmony_ci struct eeepc_cpufv c; 37462306a36Sopenharmony_ci int rv, value; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (eeepc->cpufv_disabled) 37762306a36Sopenharmony_ci return -EPERM; 37862306a36Sopenharmony_ci if (get_cpufv(eeepc, &c)) 37962306a36Sopenharmony_ci return -ENODEV; 38062306a36Sopenharmony_ci rv = parse_arg(buf, &value); 38162306a36Sopenharmony_ci if (rv < 0) 38262306a36Sopenharmony_ci return rv; 38362306a36Sopenharmony_ci if (value < 0 || value >= c.num) 38462306a36Sopenharmony_ci return -EINVAL; 38562306a36Sopenharmony_ci rv = set_acpi(eeepc, CM_ASL_CPUFV, value); 38662306a36Sopenharmony_ci if (rv) 38762306a36Sopenharmony_ci return rv; 38862306a36Sopenharmony_ci return count; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic ssize_t cpufv_disabled_show(struct device *dev, 39262306a36Sopenharmony_ci struct device_attribute *attr, 39362306a36Sopenharmony_ci char *buf) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct eeepc_laptop *eeepc = dev_get_drvdata(dev); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci return sprintf(buf, "%d\n", eeepc->cpufv_disabled); 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic ssize_t cpufv_disabled_store(struct device *dev, 40162306a36Sopenharmony_ci struct device_attribute *attr, 40262306a36Sopenharmony_ci const char *buf, size_t count) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct eeepc_laptop *eeepc = dev_get_drvdata(dev); 40562306a36Sopenharmony_ci int rv, value; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci rv = parse_arg(buf, &value); 40862306a36Sopenharmony_ci if (rv < 0) 40962306a36Sopenharmony_ci return rv; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci switch (value) { 41262306a36Sopenharmony_ci case 0: 41362306a36Sopenharmony_ci if (eeepc->cpufv_disabled) 41462306a36Sopenharmony_ci pr_warn("cpufv enabled (not officially supported on this model)\n"); 41562306a36Sopenharmony_ci eeepc->cpufv_disabled = false; 41662306a36Sopenharmony_ci return count; 41762306a36Sopenharmony_ci case 1: 41862306a36Sopenharmony_ci return -EPERM; 41962306a36Sopenharmony_ci default: 42062306a36Sopenharmony_ci return -EINVAL; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic DEVICE_ATTR_RW(cpufv); 42662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(available_cpufv); 42762306a36Sopenharmony_cistatic DEVICE_ATTR_RW(cpufv_disabled); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic struct attribute *platform_attributes[] = { 43062306a36Sopenharmony_ci &dev_attr_camera.attr, 43162306a36Sopenharmony_ci &dev_attr_cardr.attr, 43262306a36Sopenharmony_ci &dev_attr_disp.attr, 43362306a36Sopenharmony_ci &dev_attr_cpufv.attr, 43462306a36Sopenharmony_ci &dev_attr_available_cpufv.attr, 43562306a36Sopenharmony_ci &dev_attr_cpufv_disabled.attr, 43662306a36Sopenharmony_ci NULL 43762306a36Sopenharmony_ci}; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic const struct attribute_group platform_attribute_group = { 44062306a36Sopenharmony_ci .attrs = platform_attributes 44162306a36Sopenharmony_ci}; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic int eeepc_platform_init(struct eeepc_laptop *eeepc) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci int result; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci eeepc->platform_device = platform_device_alloc(EEEPC_LAPTOP_FILE, PLATFORM_DEVID_NONE); 44862306a36Sopenharmony_ci if (!eeepc->platform_device) 44962306a36Sopenharmony_ci return -ENOMEM; 45062306a36Sopenharmony_ci platform_set_drvdata(eeepc->platform_device, eeepc); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci result = platform_device_add(eeepc->platform_device); 45362306a36Sopenharmony_ci if (result) 45462306a36Sopenharmony_ci goto fail_platform_device; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci result = sysfs_create_group(&eeepc->platform_device->dev.kobj, 45762306a36Sopenharmony_ci &platform_attribute_group); 45862306a36Sopenharmony_ci if (result) 45962306a36Sopenharmony_ci goto fail_sysfs; 46062306a36Sopenharmony_ci return 0; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cifail_sysfs: 46362306a36Sopenharmony_ci platform_device_del(eeepc->platform_device); 46462306a36Sopenharmony_cifail_platform_device: 46562306a36Sopenharmony_ci platform_device_put(eeepc->platform_device); 46662306a36Sopenharmony_ci return result; 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic void eeepc_platform_exit(struct eeepc_laptop *eeepc) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci sysfs_remove_group(&eeepc->platform_device->dev.kobj, 47262306a36Sopenharmony_ci &platform_attribute_group); 47362306a36Sopenharmony_ci platform_device_unregister(eeepc->platform_device); 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci/* 47762306a36Sopenharmony_ci * LEDs 47862306a36Sopenharmony_ci */ 47962306a36Sopenharmony_ci/* 48062306a36Sopenharmony_ci * These functions actually update the LED's, and are called from a 48162306a36Sopenharmony_ci * workqueue. By doing this as separate work rather than when the LED 48262306a36Sopenharmony_ci * subsystem asks, we avoid messing with the Asus ACPI stuff during a 48362306a36Sopenharmony_ci * potentially bad time, such as a timer interrupt. 48462306a36Sopenharmony_ci */ 48562306a36Sopenharmony_cistatic void tpd_led_update(struct work_struct *work) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci struct eeepc_laptop *eeepc; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci eeepc = container_of(work, struct eeepc_laptop, tpd_led_work); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci set_acpi(eeepc, CM_ASL_TPD, eeepc->tpd_led_wk); 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic void tpd_led_set(struct led_classdev *led_cdev, 49562306a36Sopenharmony_ci enum led_brightness value) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci struct eeepc_laptop *eeepc; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci eeepc->tpd_led_wk = (value > 0) ? 1 : 0; 50262306a36Sopenharmony_ci queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic enum led_brightness tpd_led_get(struct led_classdev *led_cdev) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci struct eeepc_laptop *eeepc; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci return get_acpi(eeepc, CM_ASL_TPD); 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic int eeepc_led_init(struct eeepc_laptop *eeepc) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci int rv; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci if (get_acpi(eeepc, CM_ASL_TPD) == -ENODEV) 51962306a36Sopenharmony_ci return 0; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue"); 52262306a36Sopenharmony_ci if (!eeepc->led_workqueue) 52362306a36Sopenharmony_ci return -ENOMEM; 52462306a36Sopenharmony_ci INIT_WORK(&eeepc->tpd_led_work, tpd_led_update); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci eeepc->tpd_led.name = "eeepc::touchpad"; 52762306a36Sopenharmony_ci eeepc->tpd_led.brightness_set = tpd_led_set; 52862306a36Sopenharmony_ci if (get_acpi(eeepc, CM_ASL_TPD) >= 0) /* if method is available */ 52962306a36Sopenharmony_ci eeepc->tpd_led.brightness_get = tpd_led_get; 53062306a36Sopenharmony_ci eeepc->tpd_led.max_brightness = 1; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci rv = led_classdev_register(&eeepc->platform_device->dev, 53362306a36Sopenharmony_ci &eeepc->tpd_led); 53462306a36Sopenharmony_ci if (rv) { 53562306a36Sopenharmony_ci destroy_workqueue(eeepc->led_workqueue); 53662306a36Sopenharmony_ci return rv; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci return 0; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic void eeepc_led_exit(struct eeepc_laptop *eeepc) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci led_classdev_unregister(&eeepc->tpd_led); 54562306a36Sopenharmony_ci if (eeepc->led_workqueue) 54662306a36Sopenharmony_ci destroy_workqueue(eeepc->led_workqueue); 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci/* 55062306a36Sopenharmony_ci * PCI hotplug (for wlan rfkill) 55162306a36Sopenharmony_ci */ 55262306a36Sopenharmony_cistatic bool eeepc_wlan_rfkill_blocked(struct eeepc_laptop *eeepc) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci if (get_acpi(eeepc, CM_ASL_WLAN) == 1) 55562306a36Sopenharmony_ci return false; 55662306a36Sopenharmony_ci return true; 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci struct pci_dev *port; 56262306a36Sopenharmony_ci struct pci_dev *dev; 56362306a36Sopenharmony_ci struct pci_bus *bus; 56462306a36Sopenharmony_ci bool blocked = eeepc_wlan_rfkill_blocked(eeepc); 56562306a36Sopenharmony_ci bool absent; 56662306a36Sopenharmony_ci u32 l; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (eeepc->wlan_rfkill) 56962306a36Sopenharmony_ci rfkill_set_sw_state(eeepc->wlan_rfkill, blocked); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci mutex_lock(&eeepc->hotplug_lock); 57262306a36Sopenharmony_ci pci_lock_rescan_remove(); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci if (!eeepc->hotplug_slot.ops) 57562306a36Sopenharmony_ci goto out_unlock; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci port = acpi_get_pci_dev(handle); 57862306a36Sopenharmony_ci if (!port) { 57962306a36Sopenharmony_ci pr_warn("Unable to find port\n"); 58062306a36Sopenharmony_ci goto out_unlock; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci bus = port->subordinate; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (!bus) { 58662306a36Sopenharmony_ci pr_warn("Unable to find PCI bus 1?\n"); 58762306a36Sopenharmony_ci goto out_put_dev; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) { 59162306a36Sopenharmony_ci pr_err("Unable to read PCI config space?\n"); 59262306a36Sopenharmony_ci goto out_put_dev; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci absent = (l == 0xffffffff); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (blocked != absent) { 59862306a36Sopenharmony_ci pr_warn("BIOS says wireless lan is %s, but the pci device is %s\n", 59962306a36Sopenharmony_ci blocked ? "blocked" : "unblocked", 60062306a36Sopenharmony_ci absent ? "absent" : "present"); 60162306a36Sopenharmony_ci pr_warn("skipped wireless hotplug as probably inappropriate for this model\n"); 60262306a36Sopenharmony_ci goto out_put_dev; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci if (!blocked) { 60662306a36Sopenharmony_ci dev = pci_get_slot(bus, 0); 60762306a36Sopenharmony_ci if (dev) { 60862306a36Sopenharmony_ci /* Device already present */ 60962306a36Sopenharmony_ci pci_dev_put(dev); 61062306a36Sopenharmony_ci goto out_put_dev; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci dev = pci_scan_single_device(bus, 0); 61362306a36Sopenharmony_ci if (dev) { 61462306a36Sopenharmony_ci pci_bus_assign_resources(bus); 61562306a36Sopenharmony_ci pci_bus_add_device(dev); 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci } else { 61862306a36Sopenharmony_ci dev = pci_get_slot(bus, 0); 61962306a36Sopenharmony_ci if (dev) { 62062306a36Sopenharmony_ci pci_stop_and_remove_bus_device(dev); 62162306a36Sopenharmony_ci pci_dev_put(dev); 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ciout_put_dev: 62562306a36Sopenharmony_ci pci_dev_put(port); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ciout_unlock: 62862306a36Sopenharmony_ci pci_unlock_rescan_remove(); 62962306a36Sopenharmony_ci mutex_unlock(&eeepc->hotplug_lock); 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistatic void eeepc_rfkill_hotplug_update(struct eeepc_laptop *eeepc, char *node) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci acpi_status status = AE_OK; 63562306a36Sopenharmony_ci acpi_handle handle; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci status = acpi_get_handle(NULL, node, &handle); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (ACPI_SUCCESS(status)) 64062306a36Sopenharmony_ci eeepc_rfkill_hotplug(eeepc, handle); 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci struct eeepc_laptop *eeepc = data; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci if (event != ACPI_NOTIFY_BUS_CHECK) 64862306a36Sopenharmony_ci return; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci eeepc_rfkill_hotplug(eeepc, handle); 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic int eeepc_register_rfkill_notifier(struct eeepc_laptop *eeepc, 65462306a36Sopenharmony_ci char *node) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci acpi_status status; 65762306a36Sopenharmony_ci acpi_handle handle; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci status = acpi_get_handle(NULL, node, &handle); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 66262306a36Sopenharmony_ci return -ENODEV; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci status = acpi_install_notify_handler(handle, 66562306a36Sopenharmony_ci ACPI_SYSTEM_NOTIFY, 66662306a36Sopenharmony_ci eeepc_rfkill_notify, 66762306a36Sopenharmony_ci eeepc); 66862306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 66962306a36Sopenharmony_ci pr_warn("Failed to register notify on %s\n", node); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci /* 67262306a36Sopenharmony_ci * Refresh pci hotplug in case the rfkill state was 67362306a36Sopenharmony_ci * changed during setup. 67462306a36Sopenharmony_ci */ 67562306a36Sopenharmony_ci eeepc_rfkill_hotplug(eeepc, handle); 67662306a36Sopenharmony_ci return 0; 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_cistatic void eeepc_unregister_rfkill_notifier(struct eeepc_laptop *eeepc, 68062306a36Sopenharmony_ci char *node) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci acpi_status status = AE_OK; 68362306a36Sopenharmony_ci acpi_handle handle; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci status = acpi_get_handle(NULL, node, &handle); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 68862306a36Sopenharmony_ci return; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci status = acpi_remove_notify_handler(handle, 69162306a36Sopenharmony_ci ACPI_SYSTEM_NOTIFY, 69262306a36Sopenharmony_ci eeepc_rfkill_notify); 69362306a36Sopenharmony_ci if (ACPI_FAILURE(status)) 69462306a36Sopenharmony_ci pr_err("Error removing rfkill notify handler %s\n", 69562306a36Sopenharmony_ci node); 69662306a36Sopenharmony_ci /* 69762306a36Sopenharmony_ci * Refresh pci hotplug in case the rfkill 69862306a36Sopenharmony_ci * state was changed after 69962306a36Sopenharmony_ci * eeepc_unregister_rfkill_notifier() 70062306a36Sopenharmony_ci */ 70162306a36Sopenharmony_ci eeepc_rfkill_hotplug(eeepc, handle); 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, 70562306a36Sopenharmony_ci u8 *value) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci struct eeepc_laptop *eeepc; 70862306a36Sopenharmony_ci int val; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci eeepc = container_of(hotplug_slot, struct eeepc_laptop, hotplug_slot); 71162306a36Sopenharmony_ci val = get_acpi(eeepc, CM_ASL_WLAN); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci if (val == 1 || val == 0) 71462306a36Sopenharmony_ci *value = val; 71562306a36Sopenharmony_ci else 71662306a36Sopenharmony_ci return -EINVAL; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci return 0; 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_cistatic const struct hotplug_slot_ops eeepc_hotplug_slot_ops = { 72262306a36Sopenharmony_ci .get_adapter_status = eeepc_get_adapter_status, 72362306a36Sopenharmony_ci .get_power_status = eeepc_get_adapter_status, 72462306a36Sopenharmony_ci}; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci int ret = -ENOMEM; 72962306a36Sopenharmony_ci struct pci_bus *bus = pci_find_bus(0, 1); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci if (!bus) { 73262306a36Sopenharmony_ci pr_err("Unable to find wifi PCI bus\n"); 73362306a36Sopenharmony_ci return -ENODEV; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci eeepc->hotplug_slot.ops = &eeepc_hotplug_slot_ops; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci ret = pci_hp_register(&eeepc->hotplug_slot, bus, 0, "eeepc-wifi"); 73962306a36Sopenharmony_ci if (ret) { 74062306a36Sopenharmony_ci pr_err("Unable to register hotplug slot - %d\n", ret); 74162306a36Sopenharmony_ci goto error_register; 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci return 0; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cierror_register: 74762306a36Sopenharmony_ci eeepc->hotplug_slot.ops = NULL; 74862306a36Sopenharmony_ci return ret; 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci/* 75262306a36Sopenharmony_ci * Rfkill devices 75362306a36Sopenharmony_ci */ 75462306a36Sopenharmony_cistatic int eeepc_rfkill_set(void *data, bool blocked) 75562306a36Sopenharmony_ci{ 75662306a36Sopenharmony_ci acpi_handle handle = data; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci return write_acpi_int(handle, NULL, !blocked); 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_cistatic const struct rfkill_ops eeepc_rfkill_ops = { 76262306a36Sopenharmony_ci .set_block = eeepc_rfkill_set, 76362306a36Sopenharmony_ci}; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cistatic int eeepc_new_rfkill(struct eeepc_laptop *eeepc, 76662306a36Sopenharmony_ci struct rfkill **rfkill, 76762306a36Sopenharmony_ci const char *name, 76862306a36Sopenharmony_ci enum rfkill_type type, int cm) 76962306a36Sopenharmony_ci{ 77062306a36Sopenharmony_ci acpi_handle handle; 77162306a36Sopenharmony_ci int result; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci result = acpi_setter_handle(eeepc, cm, &handle); 77462306a36Sopenharmony_ci if (result < 0) 77562306a36Sopenharmony_ci return result; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, 77862306a36Sopenharmony_ci &eeepc_rfkill_ops, handle); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (!*rfkill) 78162306a36Sopenharmony_ci return -EINVAL; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci rfkill_init_sw_state(*rfkill, get_acpi(eeepc, cm) != 1); 78462306a36Sopenharmony_ci result = rfkill_register(*rfkill); 78562306a36Sopenharmony_ci if (result) { 78662306a36Sopenharmony_ci rfkill_destroy(*rfkill); 78762306a36Sopenharmony_ci *rfkill = NULL; 78862306a36Sopenharmony_ci return result; 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci return 0; 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic char EEEPC_RFKILL_NODE_1[] = "\\_SB.PCI0.P0P5"; 79462306a36Sopenharmony_cistatic char EEEPC_RFKILL_NODE_2[] = "\\_SB.PCI0.P0P6"; 79562306a36Sopenharmony_cistatic char EEEPC_RFKILL_NODE_3[] = "\\_SB.PCI0.P0P7"; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_cistatic void eeepc_rfkill_exit(struct eeepc_laptop *eeepc) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci eeepc_unregister_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_1); 80062306a36Sopenharmony_ci eeepc_unregister_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_2); 80162306a36Sopenharmony_ci eeepc_unregister_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_3); 80262306a36Sopenharmony_ci if (eeepc->wlan_rfkill) { 80362306a36Sopenharmony_ci rfkill_unregister(eeepc->wlan_rfkill); 80462306a36Sopenharmony_ci rfkill_destroy(eeepc->wlan_rfkill); 80562306a36Sopenharmony_ci eeepc->wlan_rfkill = NULL; 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (eeepc->hotplug_slot.ops) 80962306a36Sopenharmony_ci pci_hp_deregister(&eeepc->hotplug_slot); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci if (eeepc->bluetooth_rfkill) { 81262306a36Sopenharmony_ci rfkill_unregister(eeepc->bluetooth_rfkill); 81362306a36Sopenharmony_ci rfkill_destroy(eeepc->bluetooth_rfkill); 81462306a36Sopenharmony_ci eeepc->bluetooth_rfkill = NULL; 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci if (eeepc->wwan3g_rfkill) { 81762306a36Sopenharmony_ci rfkill_unregister(eeepc->wwan3g_rfkill); 81862306a36Sopenharmony_ci rfkill_destroy(eeepc->wwan3g_rfkill); 81962306a36Sopenharmony_ci eeepc->wwan3g_rfkill = NULL; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci if (eeepc->wimax_rfkill) { 82262306a36Sopenharmony_ci rfkill_unregister(eeepc->wimax_rfkill); 82362306a36Sopenharmony_ci rfkill_destroy(eeepc->wimax_rfkill); 82462306a36Sopenharmony_ci eeepc->wimax_rfkill = NULL; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci} 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cistatic int eeepc_rfkill_init(struct eeepc_laptop *eeepc) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci int result = 0; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci mutex_init(&eeepc->hotplug_lock); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill, 83562306a36Sopenharmony_ci "eeepc-wlan", RFKILL_TYPE_WLAN, 83662306a36Sopenharmony_ci CM_ASL_WLAN); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci if (result && result != -ENODEV) 83962306a36Sopenharmony_ci goto exit; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill, 84262306a36Sopenharmony_ci "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH, 84362306a36Sopenharmony_ci CM_ASL_BLUETOOTH); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci if (result && result != -ENODEV) 84662306a36Sopenharmony_ci goto exit; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill, 84962306a36Sopenharmony_ci "eeepc-wwan3g", RFKILL_TYPE_WWAN, 85062306a36Sopenharmony_ci CM_ASL_3G); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci if (result && result != -ENODEV) 85362306a36Sopenharmony_ci goto exit; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci result = eeepc_new_rfkill(eeepc, &eeepc->wimax_rfkill, 85662306a36Sopenharmony_ci "eeepc-wimax", RFKILL_TYPE_WIMAX, 85762306a36Sopenharmony_ci CM_ASL_WIMAX); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci if (result && result != -ENODEV) 86062306a36Sopenharmony_ci goto exit; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci if (eeepc->hotplug_disabled) 86362306a36Sopenharmony_ci return 0; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci result = eeepc_setup_pci_hotplug(eeepc); 86662306a36Sopenharmony_ci /* 86762306a36Sopenharmony_ci * If we get -EBUSY then something else is handling the PCI hotplug - 86862306a36Sopenharmony_ci * don't fail in this case 86962306a36Sopenharmony_ci */ 87062306a36Sopenharmony_ci if (result == -EBUSY) 87162306a36Sopenharmony_ci result = 0; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci eeepc_register_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_1); 87462306a36Sopenharmony_ci eeepc_register_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_2); 87562306a36Sopenharmony_ci eeepc_register_rfkill_notifier(eeepc, EEEPC_RFKILL_NODE_3); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ciexit: 87862306a36Sopenharmony_ci if (result && result != -ENODEV) 87962306a36Sopenharmony_ci eeepc_rfkill_exit(eeepc); 88062306a36Sopenharmony_ci return result; 88162306a36Sopenharmony_ci} 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci/* 88462306a36Sopenharmony_ci * Platform driver - hibernate/resume callbacks 88562306a36Sopenharmony_ci */ 88662306a36Sopenharmony_cistatic int eeepc_hotk_thaw(struct device *device) 88762306a36Sopenharmony_ci{ 88862306a36Sopenharmony_ci struct eeepc_laptop *eeepc = dev_get_drvdata(device); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci if (eeepc->wlan_rfkill) { 89162306a36Sopenharmony_ci int wlan; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci /* 89462306a36Sopenharmony_ci * Work around bios bug - acpi _PTS turns off the wireless led 89562306a36Sopenharmony_ci * during suspend. Normally it restores it on resume, but 89662306a36Sopenharmony_ci * we should kick it ourselves in case hibernation is aborted. 89762306a36Sopenharmony_ci */ 89862306a36Sopenharmony_ci wlan = get_acpi(eeepc, CM_ASL_WLAN); 89962306a36Sopenharmony_ci if (wlan >= 0) 90062306a36Sopenharmony_ci set_acpi(eeepc, CM_ASL_WLAN, wlan); 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci return 0; 90462306a36Sopenharmony_ci} 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_cistatic int eeepc_hotk_restore(struct device *device) 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci struct eeepc_laptop *eeepc = dev_get_drvdata(device); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci /* Refresh both wlan rfkill state and pci hotplug */ 91162306a36Sopenharmony_ci if (eeepc->wlan_rfkill) { 91262306a36Sopenharmony_ci eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_1); 91362306a36Sopenharmony_ci eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_2); 91462306a36Sopenharmony_ci eeepc_rfkill_hotplug_update(eeepc, EEEPC_RFKILL_NODE_3); 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci if (eeepc->bluetooth_rfkill) 91862306a36Sopenharmony_ci rfkill_set_sw_state(eeepc->bluetooth_rfkill, 91962306a36Sopenharmony_ci get_acpi(eeepc, CM_ASL_BLUETOOTH) != 1); 92062306a36Sopenharmony_ci if (eeepc->wwan3g_rfkill) 92162306a36Sopenharmony_ci rfkill_set_sw_state(eeepc->wwan3g_rfkill, 92262306a36Sopenharmony_ci get_acpi(eeepc, CM_ASL_3G) != 1); 92362306a36Sopenharmony_ci if (eeepc->wimax_rfkill) 92462306a36Sopenharmony_ci rfkill_set_sw_state(eeepc->wimax_rfkill, 92562306a36Sopenharmony_ci get_acpi(eeepc, CM_ASL_WIMAX) != 1); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci return 0; 92862306a36Sopenharmony_ci} 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_cistatic const struct dev_pm_ops eeepc_pm_ops = { 93162306a36Sopenharmony_ci .thaw = eeepc_hotk_thaw, 93262306a36Sopenharmony_ci .restore = eeepc_hotk_restore, 93362306a36Sopenharmony_ci}; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_cistatic struct platform_driver platform_driver = { 93662306a36Sopenharmony_ci .driver = { 93762306a36Sopenharmony_ci .name = EEEPC_LAPTOP_FILE, 93862306a36Sopenharmony_ci .pm = &eeepc_pm_ops, 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci}; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci/* 94362306a36Sopenharmony_ci * Hwmon device 94462306a36Sopenharmony_ci */ 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci#define EEEPC_EC_SC00 0x61 94762306a36Sopenharmony_ci#define EEEPC_EC_FAN_PWM (EEEPC_EC_SC00 + 2) /* Fan PWM duty cycle (%) */ 94862306a36Sopenharmony_ci#define EEEPC_EC_FAN_HRPM (EEEPC_EC_SC00 + 5) /* High byte, fan speed (RPM) */ 94962306a36Sopenharmony_ci#define EEEPC_EC_FAN_LRPM (EEEPC_EC_SC00 + 6) /* Low byte, fan speed (RPM) */ 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci#define EEEPC_EC_SFB0 0xD0 95262306a36Sopenharmony_ci#define EEEPC_EC_FAN_CTRL (EEEPC_EC_SFB0 + 3) /* Byte containing SF25 */ 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_cistatic inline int eeepc_pwm_to_lmsensors(int value) 95562306a36Sopenharmony_ci{ 95662306a36Sopenharmony_ci return value * 255 / 100; 95762306a36Sopenharmony_ci} 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_cistatic inline int eeepc_lmsensors_to_pwm(int value) 96062306a36Sopenharmony_ci{ 96162306a36Sopenharmony_ci value = clamp_val(value, 0, 255); 96262306a36Sopenharmony_ci return value * 100 / 255; 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_cistatic int eeepc_get_fan_pwm(void) 96662306a36Sopenharmony_ci{ 96762306a36Sopenharmony_ci u8 value = 0; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci ec_read(EEEPC_EC_FAN_PWM, &value); 97062306a36Sopenharmony_ci return eeepc_pwm_to_lmsensors(value); 97162306a36Sopenharmony_ci} 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_cistatic void eeepc_set_fan_pwm(int value) 97462306a36Sopenharmony_ci{ 97562306a36Sopenharmony_ci value = eeepc_lmsensors_to_pwm(value); 97662306a36Sopenharmony_ci ec_write(EEEPC_EC_FAN_PWM, value); 97762306a36Sopenharmony_ci} 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_cistatic int eeepc_get_fan_rpm(void) 98062306a36Sopenharmony_ci{ 98162306a36Sopenharmony_ci u8 high = 0; 98262306a36Sopenharmony_ci u8 low = 0; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci ec_read(EEEPC_EC_FAN_HRPM, &high); 98562306a36Sopenharmony_ci ec_read(EEEPC_EC_FAN_LRPM, &low); 98662306a36Sopenharmony_ci return high << 8 | low; 98762306a36Sopenharmony_ci} 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci#define EEEPC_EC_FAN_CTRL_BIT 0x02 99062306a36Sopenharmony_ci#define EEEPC_FAN_CTRL_MANUAL 1 99162306a36Sopenharmony_ci#define EEEPC_FAN_CTRL_AUTO 2 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_cistatic int eeepc_get_fan_ctrl(void) 99462306a36Sopenharmony_ci{ 99562306a36Sopenharmony_ci u8 value = 0; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci ec_read(EEEPC_EC_FAN_CTRL, &value); 99862306a36Sopenharmony_ci if (value & EEEPC_EC_FAN_CTRL_BIT) 99962306a36Sopenharmony_ci return EEEPC_FAN_CTRL_MANUAL; 100062306a36Sopenharmony_ci else 100162306a36Sopenharmony_ci return EEEPC_FAN_CTRL_AUTO; 100262306a36Sopenharmony_ci} 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_cistatic void eeepc_set_fan_ctrl(int manual) 100562306a36Sopenharmony_ci{ 100662306a36Sopenharmony_ci u8 value = 0; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci ec_read(EEEPC_EC_FAN_CTRL, &value); 100962306a36Sopenharmony_ci if (manual == EEEPC_FAN_CTRL_MANUAL) 101062306a36Sopenharmony_ci value |= EEEPC_EC_FAN_CTRL_BIT; 101162306a36Sopenharmony_ci else 101262306a36Sopenharmony_ci value &= ~EEEPC_EC_FAN_CTRL_BIT; 101362306a36Sopenharmony_ci ec_write(EEEPC_EC_FAN_CTRL, value); 101462306a36Sopenharmony_ci} 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_cistatic ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count) 101762306a36Sopenharmony_ci{ 101862306a36Sopenharmony_ci int rv, value; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci rv = parse_arg(buf, &value); 102162306a36Sopenharmony_ci if (rv < 0) 102262306a36Sopenharmony_ci return rv; 102362306a36Sopenharmony_ci set(value); 102462306a36Sopenharmony_ci return count; 102562306a36Sopenharmony_ci} 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_cistatic ssize_t show_sys_hwmon(int (*get)(void), char *buf) 102862306a36Sopenharmony_ci{ 102962306a36Sopenharmony_ci return sprintf(buf, "%d\n", get()); 103062306a36Sopenharmony_ci} 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci#define EEEPC_SENSOR_SHOW_FUNC(_name, _get) \ 103362306a36Sopenharmony_ci static ssize_t _name##_show(struct device *dev, \ 103462306a36Sopenharmony_ci struct device_attribute *attr, \ 103562306a36Sopenharmony_ci char *buf) \ 103662306a36Sopenharmony_ci { \ 103762306a36Sopenharmony_ci return show_sys_hwmon(_get, buf); \ 103862306a36Sopenharmony_ci } 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci#define EEEPC_SENSOR_STORE_FUNC(_name, _set) \ 104162306a36Sopenharmony_ci static ssize_t _name##_store(struct device *dev, \ 104262306a36Sopenharmony_ci struct device_attribute *attr, \ 104362306a36Sopenharmony_ci const char *buf, size_t count) \ 104462306a36Sopenharmony_ci { \ 104562306a36Sopenharmony_ci return store_sys_hwmon(_set, buf, count); \ 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci#define EEEPC_CREATE_SENSOR_ATTR_RW(_name, _get, _set) \ 104962306a36Sopenharmony_ci EEEPC_SENSOR_SHOW_FUNC(_name, _get) \ 105062306a36Sopenharmony_ci EEEPC_SENSOR_STORE_FUNC(_name, _set) \ 105162306a36Sopenharmony_ci static DEVICE_ATTR_RW(_name) 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci#define EEEPC_CREATE_SENSOR_ATTR_RO(_name, _get) \ 105462306a36Sopenharmony_ci EEEPC_SENSOR_SHOW_FUNC(_name, _get) \ 105562306a36Sopenharmony_ci static DEVICE_ATTR_RO(_name) 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ciEEEPC_CREATE_SENSOR_ATTR_RO(fan1_input, eeepc_get_fan_rpm); 105862306a36Sopenharmony_ciEEEPC_CREATE_SENSOR_ATTR_RW(pwm1, eeepc_get_fan_pwm, 105962306a36Sopenharmony_ci eeepc_set_fan_pwm); 106062306a36Sopenharmony_ciEEEPC_CREATE_SENSOR_ATTR_RW(pwm1_enable, eeepc_get_fan_ctrl, 106162306a36Sopenharmony_ci eeepc_set_fan_ctrl); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_cistatic struct attribute *hwmon_attrs[] = { 106462306a36Sopenharmony_ci &dev_attr_pwm1.attr, 106562306a36Sopenharmony_ci &dev_attr_fan1_input.attr, 106662306a36Sopenharmony_ci &dev_attr_pwm1_enable.attr, 106762306a36Sopenharmony_ci NULL 106862306a36Sopenharmony_ci}; 106962306a36Sopenharmony_ciATTRIBUTE_GROUPS(hwmon); 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_cistatic int eeepc_hwmon_init(struct eeepc_laptop *eeepc) 107262306a36Sopenharmony_ci{ 107362306a36Sopenharmony_ci struct device *dev = &eeepc->platform_device->dev; 107462306a36Sopenharmony_ci struct device *hwmon; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci hwmon = devm_hwmon_device_register_with_groups(dev, "eeepc", NULL, 107762306a36Sopenharmony_ci hwmon_groups); 107862306a36Sopenharmony_ci if (IS_ERR(hwmon)) { 107962306a36Sopenharmony_ci pr_err("Could not register eeepc hwmon device\n"); 108062306a36Sopenharmony_ci return PTR_ERR(hwmon); 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci return 0; 108362306a36Sopenharmony_ci} 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci/* 108662306a36Sopenharmony_ci * Backlight device 108762306a36Sopenharmony_ci */ 108862306a36Sopenharmony_cistatic int read_brightness(struct backlight_device *bd) 108962306a36Sopenharmony_ci{ 109062306a36Sopenharmony_ci struct eeepc_laptop *eeepc = bl_get_data(bd); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci return get_acpi(eeepc, CM_ASL_PANELBRIGHT); 109362306a36Sopenharmony_ci} 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_cistatic int set_brightness(struct backlight_device *bd, int value) 109662306a36Sopenharmony_ci{ 109762306a36Sopenharmony_ci struct eeepc_laptop *eeepc = bl_get_data(bd); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci return set_acpi(eeepc, CM_ASL_PANELBRIGHT, value); 110062306a36Sopenharmony_ci} 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_cistatic int update_bl_status(struct backlight_device *bd) 110362306a36Sopenharmony_ci{ 110462306a36Sopenharmony_ci return set_brightness(bd, bd->props.brightness); 110562306a36Sopenharmony_ci} 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_cistatic const struct backlight_ops eeepcbl_ops = { 110862306a36Sopenharmony_ci .get_brightness = read_brightness, 110962306a36Sopenharmony_ci .update_status = update_bl_status, 111062306a36Sopenharmony_ci}; 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_cistatic int eeepc_backlight_notify(struct eeepc_laptop *eeepc) 111362306a36Sopenharmony_ci{ 111462306a36Sopenharmony_ci struct backlight_device *bd = eeepc->backlight_device; 111562306a36Sopenharmony_ci int old = bd->props.brightness; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY); 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci return old; 112062306a36Sopenharmony_ci} 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_cistatic int eeepc_backlight_init(struct eeepc_laptop *eeepc) 112362306a36Sopenharmony_ci{ 112462306a36Sopenharmony_ci struct backlight_properties props; 112562306a36Sopenharmony_ci struct backlight_device *bd; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci memset(&props, 0, sizeof(struct backlight_properties)); 112862306a36Sopenharmony_ci props.type = BACKLIGHT_PLATFORM; 112962306a36Sopenharmony_ci props.max_brightness = 15; 113062306a36Sopenharmony_ci bd = backlight_device_register(EEEPC_LAPTOP_FILE, 113162306a36Sopenharmony_ci &eeepc->platform_device->dev, eeepc, 113262306a36Sopenharmony_ci &eeepcbl_ops, &props); 113362306a36Sopenharmony_ci if (IS_ERR(bd)) { 113462306a36Sopenharmony_ci pr_err("Could not register eeepc backlight device\n"); 113562306a36Sopenharmony_ci eeepc->backlight_device = NULL; 113662306a36Sopenharmony_ci return PTR_ERR(bd); 113762306a36Sopenharmony_ci } 113862306a36Sopenharmony_ci eeepc->backlight_device = bd; 113962306a36Sopenharmony_ci bd->props.brightness = read_brightness(bd); 114062306a36Sopenharmony_ci bd->props.power = FB_BLANK_UNBLANK; 114162306a36Sopenharmony_ci backlight_update_status(bd); 114262306a36Sopenharmony_ci return 0; 114362306a36Sopenharmony_ci} 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_cistatic void eeepc_backlight_exit(struct eeepc_laptop *eeepc) 114662306a36Sopenharmony_ci{ 114762306a36Sopenharmony_ci backlight_device_unregister(eeepc->backlight_device); 114862306a36Sopenharmony_ci eeepc->backlight_device = NULL; 114962306a36Sopenharmony_ci} 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci/* 115362306a36Sopenharmony_ci * Input device (i.e. hotkeys) 115462306a36Sopenharmony_ci */ 115562306a36Sopenharmony_cistatic int eeepc_input_init(struct eeepc_laptop *eeepc) 115662306a36Sopenharmony_ci{ 115762306a36Sopenharmony_ci struct input_dev *input; 115862306a36Sopenharmony_ci int error; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci input = input_allocate_device(); 116162306a36Sopenharmony_ci if (!input) 116262306a36Sopenharmony_ci return -ENOMEM; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci input->name = "Asus EeePC extra buttons"; 116562306a36Sopenharmony_ci input->phys = EEEPC_LAPTOP_FILE "/input0"; 116662306a36Sopenharmony_ci input->id.bustype = BUS_HOST; 116762306a36Sopenharmony_ci input->dev.parent = &eeepc->platform_device->dev; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci error = sparse_keymap_setup(input, eeepc_keymap, NULL); 117062306a36Sopenharmony_ci if (error) { 117162306a36Sopenharmony_ci pr_err("Unable to setup input device keymap\n"); 117262306a36Sopenharmony_ci goto err_free_dev; 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci error = input_register_device(input); 117662306a36Sopenharmony_ci if (error) { 117762306a36Sopenharmony_ci pr_err("Unable to register input device\n"); 117862306a36Sopenharmony_ci goto err_free_dev; 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci eeepc->inputdev = input; 118262306a36Sopenharmony_ci return 0; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_cierr_free_dev: 118562306a36Sopenharmony_ci input_free_device(input); 118662306a36Sopenharmony_ci return error; 118762306a36Sopenharmony_ci} 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_cistatic void eeepc_input_exit(struct eeepc_laptop *eeepc) 119062306a36Sopenharmony_ci{ 119162306a36Sopenharmony_ci if (eeepc->inputdev) 119262306a36Sopenharmony_ci input_unregister_device(eeepc->inputdev); 119362306a36Sopenharmony_ci eeepc->inputdev = NULL; 119462306a36Sopenharmony_ci} 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci/* 119762306a36Sopenharmony_ci * ACPI driver 119862306a36Sopenharmony_ci */ 119962306a36Sopenharmony_cistatic void eeepc_input_notify(struct eeepc_laptop *eeepc, int event) 120062306a36Sopenharmony_ci{ 120162306a36Sopenharmony_ci if (!eeepc->inputdev) 120262306a36Sopenharmony_ci return; 120362306a36Sopenharmony_ci if (!sparse_keymap_report_event(eeepc->inputdev, event, 1, true)) 120462306a36Sopenharmony_ci pr_info("Unknown key %x pressed\n", event); 120562306a36Sopenharmony_ci} 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_cistatic void eeepc_acpi_notify(struct acpi_device *device, u32 event) 120862306a36Sopenharmony_ci{ 120962306a36Sopenharmony_ci struct eeepc_laptop *eeepc = acpi_driver_data(device); 121062306a36Sopenharmony_ci int old_brightness, new_brightness; 121162306a36Sopenharmony_ci u16 count; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci if (event > ACPI_MAX_SYS_NOTIFY) 121462306a36Sopenharmony_ci return; 121562306a36Sopenharmony_ci count = eeepc->event_count[event % 128]++; 121662306a36Sopenharmony_ci acpi_bus_generate_netlink_event(device->pnp.device_class, 121762306a36Sopenharmony_ci dev_name(&device->dev), event, 121862306a36Sopenharmony_ci count); 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci /* Brightness events are special */ 122162306a36Sopenharmony_ci if (event < NOTIFY_BRN_MIN || event > NOTIFY_BRN_MAX) { 122262306a36Sopenharmony_ci eeepc_input_notify(eeepc, event); 122362306a36Sopenharmony_ci return; 122462306a36Sopenharmony_ci } 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci /* Ignore them completely if the acpi video driver is used */ 122762306a36Sopenharmony_ci if (!eeepc->backlight_device) 122862306a36Sopenharmony_ci return; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci /* Update the backlight device. */ 123162306a36Sopenharmony_ci old_brightness = eeepc_backlight_notify(eeepc); 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci /* Convert event to keypress (obsolescent hack) */ 123462306a36Sopenharmony_ci new_brightness = event - NOTIFY_BRN_MIN; 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci if (new_brightness < old_brightness) { 123762306a36Sopenharmony_ci event = NOTIFY_BRN_MIN; /* brightness down */ 123862306a36Sopenharmony_ci } else if (new_brightness > old_brightness) { 123962306a36Sopenharmony_ci event = NOTIFY_BRN_MAX; /* brightness up */ 124062306a36Sopenharmony_ci } else { 124162306a36Sopenharmony_ci /* 124262306a36Sopenharmony_ci * no change in brightness - already at min/max, 124362306a36Sopenharmony_ci * event will be desired value (or else ignored) 124462306a36Sopenharmony_ci */ 124562306a36Sopenharmony_ci } 124662306a36Sopenharmony_ci eeepc_input_notify(eeepc, event); 124762306a36Sopenharmony_ci} 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_cistatic void eeepc_dmi_check(struct eeepc_laptop *eeepc) 125062306a36Sopenharmony_ci{ 125162306a36Sopenharmony_ci const char *model; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci model = dmi_get_system_info(DMI_PRODUCT_NAME); 125462306a36Sopenharmony_ci if (!model) 125562306a36Sopenharmony_ci return; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci /* 125862306a36Sopenharmony_ci * Blacklist for setting cpufv (cpu speed). 125962306a36Sopenharmony_ci * 126062306a36Sopenharmony_ci * EeePC 4G ("701") implements CFVS, but it is not supported 126162306a36Sopenharmony_ci * by the pre-installed OS, and the original option to change it 126262306a36Sopenharmony_ci * in the BIOS setup screen was removed in later versions. 126362306a36Sopenharmony_ci * 126462306a36Sopenharmony_ci * Judging by the lack of "Super Hybrid Engine" on Asus product pages, 126562306a36Sopenharmony_ci * this applies to all "701" models (4G/4G Surf/2G Surf). 126662306a36Sopenharmony_ci * 126762306a36Sopenharmony_ci * So Asus made a deliberate decision not to support it on this model. 126862306a36Sopenharmony_ci * We have several reports that using it can cause the system to hang 126962306a36Sopenharmony_ci * 127062306a36Sopenharmony_ci * The hang has also been reported on a "702" (Model name "8G"?). 127162306a36Sopenharmony_ci * 127262306a36Sopenharmony_ci * We avoid dmi_check_system() / dmi_match(), because they use 127362306a36Sopenharmony_ci * substring matching. We don't want to affect the "701SD" 127462306a36Sopenharmony_ci * and "701SDX" models, because they do support S.H.E. 127562306a36Sopenharmony_ci */ 127662306a36Sopenharmony_ci if (strcmp(model, "701") == 0 || strcmp(model, "702") == 0) { 127762306a36Sopenharmony_ci eeepc->cpufv_disabled = true; 127862306a36Sopenharmony_ci pr_info("model %s does not officially support setting cpu speed\n", 127962306a36Sopenharmony_ci model); 128062306a36Sopenharmony_ci pr_info("cpufv disabled to avoid instability\n"); 128162306a36Sopenharmony_ci } 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci /* 128462306a36Sopenharmony_ci * Blacklist for wlan hotplug 128562306a36Sopenharmony_ci * 128662306a36Sopenharmony_ci * Eeepc 1005HA doesn't work like others models and don't need the 128762306a36Sopenharmony_ci * hotplug code. In fact, current hotplug code seems to unplug another 128862306a36Sopenharmony_ci * device... 128962306a36Sopenharmony_ci */ 129062306a36Sopenharmony_ci if (strcmp(model, "1005HA") == 0 || strcmp(model, "1201N") == 0 || 129162306a36Sopenharmony_ci strcmp(model, "1005PE") == 0) { 129262306a36Sopenharmony_ci eeepc->hotplug_disabled = true; 129362306a36Sopenharmony_ci pr_info("wlan hotplug disabled\n"); 129462306a36Sopenharmony_ci } 129562306a36Sopenharmony_ci} 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_cistatic void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name) 129862306a36Sopenharmony_ci{ 129962306a36Sopenharmony_ci int dummy; 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci /* Some BIOSes do not report cm although it is available. 130262306a36Sopenharmony_ci Check if cm_getv[cm] works and, if yes, assume cm should be set. */ 130362306a36Sopenharmony_ci if (!(eeepc->cm_supported & (1 << cm)) 130462306a36Sopenharmony_ci && !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) { 130562306a36Sopenharmony_ci pr_info("%s (%x) not reported by BIOS, enabling anyway\n", 130662306a36Sopenharmony_ci name, 1 << cm); 130762306a36Sopenharmony_ci eeepc->cm_supported |= 1 << cm; 130862306a36Sopenharmony_ci } 130962306a36Sopenharmony_ci} 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_cistatic void cmsg_quirks(struct eeepc_laptop *eeepc) 131262306a36Sopenharmony_ci{ 131362306a36Sopenharmony_ci cmsg_quirk(eeepc, CM_ASL_LID, "LID"); 131462306a36Sopenharmony_ci cmsg_quirk(eeepc, CM_ASL_TYPE, "TYPE"); 131562306a36Sopenharmony_ci cmsg_quirk(eeepc, CM_ASL_PANELPOWER, "PANELPOWER"); 131662306a36Sopenharmony_ci cmsg_quirk(eeepc, CM_ASL_TPD, "TPD"); 131762306a36Sopenharmony_ci} 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_cistatic int eeepc_acpi_init(struct eeepc_laptop *eeepc) 132062306a36Sopenharmony_ci{ 132162306a36Sopenharmony_ci unsigned int init_flags; 132262306a36Sopenharmony_ci int result; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci result = acpi_bus_get_status(eeepc->device); 132562306a36Sopenharmony_ci if (result) 132662306a36Sopenharmony_ci return result; 132762306a36Sopenharmony_ci if (!eeepc->device->status.present) { 132862306a36Sopenharmony_ci pr_err("Hotkey device not present, aborting\n"); 132962306a36Sopenharmony_ci return -ENODEV; 133062306a36Sopenharmony_ci } 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci init_flags = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH; 133362306a36Sopenharmony_ci pr_notice("Hotkey init flags 0x%x\n", init_flags); 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci if (write_acpi_int(eeepc->handle, "INIT", init_flags)) { 133662306a36Sopenharmony_ci pr_err("Hotkey initialization failed\n"); 133762306a36Sopenharmony_ci return -ENODEV; 133862306a36Sopenharmony_ci } 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci /* get control methods supported */ 134162306a36Sopenharmony_ci if (read_acpi_int(eeepc->handle, "CMSG", &eeepc->cm_supported)) { 134262306a36Sopenharmony_ci pr_err("Get control methods supported failed\n"); 134362306a36Sopenharmony_ci return -ENODEV; 134462306a36Sopenharmony_ci } 134562306a36Sopenharmony_ci cmsg_quirks(eeepc); 134662306a36Sopenharmony_ci pr_info("Get control methods supported: 0x%x\n", eeepc->cm_supported); 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci return 0; 134962306a36Sopenharmony_ci} 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_cistatic void eeepc_enable_camera(struct eeepc_laptop *eeepc) 135262306a36Sopenharmony_ci{ 135362306a36Sopenharmony_ci /* 135462306a36Sopenharmony_ci * If the following call to set_acpi() fails, it's because there's no 135562306a36Sopenharmony_ci * camera so we can ignore the error. 135662306a36Sopenharmony_ci */ 135762306a36Sopenharmony_ci if (get_acpi(eeepc, CM_ASL_CAMERA) == 0) 135862306a36Sopenharmony_ci set_acpi(eeepc, CM_ASL_CAMERA, 1); 135962306a36Sopenharmony_ci} 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_cistatic bool eeepc_device_present; 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_cistatic int eeepc_acpi_add(struct acpi_device *device) 136462306a36Sopenharmony_ci{ 136562306a36Sopenharmony_ci struct eeepc_laptop *eeepc; 136662306a36Sopenharmony_ci int result; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci pr_notice(EEEPC_LAPTOP_NAME "\n"); 136962306a36Sopenharmony_ci eeepc = kzalloc(sizeof(struct eeepc_laptop), GFP_KERNEL); 137062306a36Sopenharmony_ci if (!eeepc) 137162306a36Sopenharmony_ci return -ENOMEM; 137262306a36Sopenharmony_ci eeepc->handle = device->handle; 137362306a36Sopenharmony_ci strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME); 137462306a36Sopenharmony_ci strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS); 137562306a36Sopenharmony_ci device->driver_data = eeepc; 137662306a36Sopenharmony_ci eeepc->device = device; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci eeepc->hotplug_disabled = hotplug_disabled; 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci eeepc_dmi_check(eeepc); 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci result = eeepc_acpi_init(eeepc); 138362306a36Sopenharmony_ci if (result) 138462306a36Sopenharmony_ci goto fail_platform; 138562306a36Sopenharmony_ci eeepc_enable_camera(eeepc); 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci /* 138862306a36Sopenharmony_ci * Register the platform device first. It is used as a parent for the 138962306a36Sopenharmony_ci * sub-devices below. 139062306a36Sopenharmony_ci * 139162306a36Sopenharmony_ci * Note that if there are multiple instances of this ACPI device it 139262306a36Sopenharmony_ci * will bail out, because the platform device is registered with a 139362306a36Sopenharmony_ci * fixed name. Of course it doesn't make sense to have more than one, 139462306a36Sopenharmony_ci * and machine-specific scripts find the fixed name convenient. But 139562306a36Sopenharmony_ci * It's also good for us to exclude multiple instances because both 139662306a36Sopenharmony_ci * our hwmon and our wlan rfkill subdevice use global ACPI objects 139762306a36Sopenharmony_ci * (the EC and the PCI wlan slot respectively). 139862306a36Sopenharmony_ci */ 139962306a36Sopenharmony_ci result = eeepc_platform_init(eeepc); 140062306a36Sopenharmony_ci if (result) 140162306a36Sopenharmony_ci goto fail_platform; 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci if (acpi_video_get_backlight_type() == acpi_backlight_vendor) { 140462306a36Sopenharmony_ci result = eeepc_backlight_init(eeepc); 140562306a36Sopenharmony_ci if (result) 140662306a36Sopenharmony_ci goto fail_backlight; 140762306a36Sopenharmony_ci } 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci result = eeepc_input_init(eeepc); 141062306a36Sopenharmony_ci if (result) 141162306a36Sopenharmony_ci goto fail_input; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci result = eeepc_hwmon_init(eeepc); 141462306a36Sopenharmony_ci if (result) 141562306a36Sopenharmony_ci goto fail_hwmon; 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci result = eeepc_led_init(eeepc); 141862306a36Sopenharmony_ci if (result) 141962306a36Sopenharmony_ci goto fail_led; 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci result = eeepc_rfkill_init(eeepc); 142262306a36Sopenharmony_ci if (result) 142362306a36Sopenharmony_ci goto fail_rfkill; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci eeepc_device_present = true; 142662306a36Sopenharmony_ci return 0; 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_cifail_rfkill: 142962306a36Sopenharmony_ci eeepc_led_exit(eeepc); 143062306a36Sopenharmony_cifail_led: 143162306a36Sopenharmony_cifail_hwmon: 143262306a36Sopenharmony_ci eeepc_input_exit(eeepc); 143362306a36Sopenharmony_cifail_input: 143462306a36Sopenharmony_ci eeepc_backlight_exit(eeepc); 143562306a36Sopenharmony_cifail_backlight: 143662306a36Sopenharmony_ci eeepc_platform_exit(eeepc); 143762306a36Sopenharmony_cifail_platform: 143862306a36Sopenharmony_ci kfree(eeepc); 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci return result; 144162306a36Sopenharmony_ci} 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_cistatic void eeepc_acpi_remove(struct acpi_device *device) 144462306a36Sopenharmony_ci{ 144562306a36Sopenharmony_ci struct eeepc_laptop *eeepc = acpi_driver_data(device); 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci eeepc_backlight_exit(eeepc); 144862306a36Sopenharmony_ci eeepc_rfkill_exit(eeepc); 144962306a36Sopenharmony_ci eeepc_input_exit(eeepc); 145062306a36Sopenharmony_ci eeepc_led_exit(eeepc); 145162306a36Sopenharmony_ci eeepc_platform_exit(eeepc); 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci kfree(eeepc); 145462306a36Sopenharmony_ci} 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_cistatic const struct acpi_device_id eeepc_device_ids[] = { 145862306a36Sopenharmony_ci {EEEPC_ACPI_HID, 0}, 145962306a36Sopenharmony_ci {"", 0}, 146062306a36Sopenharmony_ci}; 146162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, eeepc_device_ids); 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_cistatic struct acpi_driver eeepc_acpi_driver = { 146462306a36Sopenharmony_ci .name = EEEPC_LAPTOP_NAME, 146562306a36Sopenharmony_ci .class = EEEPC_ACPI_CLASS, 146662306a36Sopenharmony_ci .owner = THIS_MODULE, 146762306a36Sopenharmony_ci .ids = eeepc_device_ids, 146862306a36Sopenharmony_ci .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, 146962306a36Sopenharmony_ci .ops = { 147062306a36Sopenharmony_ci .add = eeepc_acpi_add, 147162306a36Sopenharmony_ci .remove = eeepc_acpi_remove, 147262306a36Sopenharmony_ci .notify = eeepc_acpi_notify, 147362306a36Sopenharmony_ci }, 147462306a36Sopenharmony_ci}; 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_cistatic int __init eeepc_laptop_init(void) 147862306a36Sopenharmony_ci{ 147962306a36Sopenharmony_ci int result; 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci result = platform_driver_register(&platform_driver); 148262306a36Sopenharmony_ci if (result < 0) 148362306a36Sopenharmony_ci return result; 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci result = acpi_bus_register_driver(&eeepc_acpi_driver); 148662306a36Sopenharmony_ci if (result < 0) 148762306a36Sopenharmony_ci goto fail_acpi_driver; 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci if (!eeepc_device_present) { 149062306a36Sopenharmony_ci result = -ENODEV; 149162306a36Sopenharmony_ci goto fail_no_device; 149262306a36Sopenharmony_ci } 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci return 0; 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_cifail_no_device: 149762306a36Sopenharmony_ci acpi_bus_unregister_driver(&eeepc_acpi_driver); 149862306a36Sopenharmony_cifail_acpi_driver: 149962306a36Sopenharmony_ci platform_driver_unregister(&platform_driver); 150062306a36Sopenharmony_ci return result; 150162306a36Sopenharmony_ci} 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_cistatic void __exit eeepc_laptop_exit(void) 150462306a36Sopenharmony_ci{ 150562306a36Sopenharmony_ci acpi_bus_unregister_driver(&eeepc_acpi_driver); 150662306a36Sopenharmony_ci platform_driver_unregister(&platform_driver); 150762306a36Sopenharmony_ci} 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_cimodule_init(eeepc_laptop_init); 151062306a36Sopenharmony_cimodule_exit(eeepc_laptop_exit); 1511