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