brintos

brintos / linux-shallow public Read only

0
0
Text · 119.1 KiB · 3197aaa Raw
4907 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * ACPI Sony Notebook Control Driver (SNC and SPIC)4 *5 * Copyright (C) 2004-2005 Stelian Pop <stelian@popies.net>6 * Copyright (C) 2007-2009 Mattia Dongili <malattia@linux.it>7 *8 * Parts of this driver inspired from asus_acpi.c and ibm_acpi.c9 * which are copyrighted by their respective authors.10 *11 * The SNY6001 driver part is based on the sonypi driver which includes12 * material from:13 *14 * Copyright (C) 2001-2005 Stelian Pop <stelian@popies.net>15 *16 * Copyright (C) 2005 Narayanan R S <nars@kadamba.org>17 *18 * Copyright (C) 2001-2002 Alcôve <www.alcove.com>19 *20 * Copyright (C) 2001 Michael Ashley <m.ashley@unsw.edu.au>21 *22 * Copyright (C) 2001 Junichi Morita <jun1m@mars.dti.ne.jp>23 *24 * Copyright (C) 2000 Takaya Kinjo <t-kinjo@tc4.so-net.ne.jp>25 *26 * Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com>27 *28 * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras.29 */30 31#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt32 33#include <linux/kernel.h>34#include <linux/module.h>35#include <linux/moduleparam.h>36#include <linux/init.h>37#include <linux/types.h>38#include <linux/backlight.h>39#include <linux/platform_device.h>40#include <linux/err.h>41#include <linux/dmi.h>42#include <linux/pci.h>43#include <linux/interrupt.h>44#include <linux/delay.h>45#include <linux/input.h>46#include <linux/kfifo.h>47#include <linux/workqueue.h>48#include <linux/acpi.h>49#include <linux/slab.h>50#include <linux/sonypi.h>51#include <linux/sony-laptop.h>52#include <linux/rfkill.h>53#ifdef CONFIG_SONYPI_COMPAT54#include <linux/poll.h>55#include <linux/miscdevice.h>56#endif57#include <linux/uaccess.h>58#include <acpi/video.h>59 60#define dprintk(fmt, ...)			\61do {						\62	if (debug)				\63		pr_warn(fmt, ##__VA_ARGS__);	\64} while (0)65 66#define SONY_NC_CLASS		"sony-nc"67#define SONY_NC_HID		"SNY5001"68#define SONY_NC_DRIVER_NAME	"Sony Notebook Control Driver"69 70#define SONY_PIC_CLASS		"sony-pic"71#define SONY_PIC_HID		"SNY6001"72#define SONY_PIC_DRIVER_NAME	"Sony Programmable IO Control Driver"73 74MODULE_AUTHOR("Stelian Pop, Mattia Dongili");75MODULE_DESCRIPTION("Sony laptop extras driver (SPIC and SNC ACPI device)");76MODULE_LICENSE("GPL");77 78static int debug;79module_param(debug, int, 0);80MODULE_PARM_DESC(debug, "set this to 1 (and RTFM) if you want to help "81		 "the development of this driver");82 83static int no_spic;		/* = 0 */84module_param(no_spic, int, 0444);85MODULE_PARM_DESC(no_spic,86		 "set this if you don't want to enable the SPIC device");87 88static int compat;		/* = 0 */89module_param(compat, int, 0444);90MODULE_PARM_DESC(compat,91		 "set this if you want to enable backward compatibility mode");92 93static unsigned long mask = 0xffffffff;94module_param(mask, ulong, 0644);95MODULE_PARM_DESC(mask,96		 "set this to the mask of event you want to enable (see doc)");97 98static int camera;		/* = 0 */99module_param(camera, int, 0444);100MODULE_PARM_DESC(camera,101		 "set this to 1 to enable Motion Eye camera controls "102		 "(only use it if you have a C1VE or C1VN model)");103 104#ifdef CONFIG_SONYPI_COMPAT105static int minor = -1;106module_param(minor, int, 0);107MODULE_PARM_DESC(minor,108		 "minor number of the misc device for the SPIC compatibility code, "109		 "default is -1 (automatic)");110#endif111 112static int kbd_backlight = -1;113module_param(kbd_backlight, int, 0444);114MODULE_PARM_DESC(kbd_backlight,115		 "set this to 0 to disable keyboard backlight, "116		 "1 to enable it with automatic control and 2 to have it always "117		 "on (default: no change from current value)");118 119static int kbd_backlight_timeout = -1;120module_param(kbd_backlight_timeout, int, 0444);121MODULE_PARM_DESC(kbd_backlight_timeout,122		 "meaningful values vary from 0 to 3 and their meaning depends "123		 "on the model (default: no change from current value)");124 125#ifdef CONFIG_PM_SLEEP126static void sony_nc_thermal_resume(void);127#endif128static int sony_nc_kbd_backlight_setup(struct platform_device *pd,129		unsigned int handle);130static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd,131		unsigned int handle);132 133static int sony_nc_battery_care_setup(struct platform_device *pd,134		unsigned int handle);135static void sony_nc_battery_care_cleanup(struct platform_device *pd);136 137static int sony_nc_thermal_setup(struct platform_device *pd);138static void sony_nc_thermal_cleanup(struct platform_device *pd);139 140static int sony_nc_lid_resume_setup(struct platform_device *pd,141				    unsigned int handle);142static void sony_nc_lid_resume_cleanup(struct platform_device *pd);143 144static int sony_nc_gfx_switch_setup(struct platform_device *pd,145		unsigned int handle);146static void sony_nc_gfx_switch_cleanup(struct platform_device *pd);147static int __sony_nc_gfx_switch_status_get(void);148 149static int sony_nc_highspeed_charging_setup(struct platform_device *pd);150static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd);151 152static int sony_nc_lowbatt_setup(struct platform_device *pd);153static void sony_nc_lowbatt_cleanup(struct platform_device *pd);154 155static int sony_nc_fanspeed_setup(struct platform_device *pd);156static void sony_nc_fanspeed_cleanup(struct platform_device *pd);157 158static int sony_nc_usb_charge_setup(struct platform_device *pd);159static void sony_nc_usb_charge_cleanup(struct platform_device *pd);160 161static int sony_nc_panelid_setup(struct platform_device *pd);162static void sony_nc_panelid_cleanup(struct platform_device *pd);163 164static int sony_nc_smart_conn_setup(struct platform_device *pd);165static void sony_nc_smart_conn_cleanup(struct platform_device *pd);166 167static int sony_nc_touchpad_setup(struct platform_device *pd,168				  unsigned int handle);169static void sony_nc_touchpad_cleanup(struct platform_device *pd);170 171enum sony_nc_rfkill {172	SONY_WIFI,173	SONY_BLUETOOTH,174	SONY_WWAN,175	SONY_WIMAX,176	N_SONY_RFKILL,177};178 179static int sony_rfkill_handle;180static struct rfkill *sony_rfkill_devices[N_SONY_RFKILL];181static int sony_rfkill_address[N_SONY_RFKILL] = {0x300, 0x500, 0x700, 0x900};182static int sony_nc_rfkill_setup(struct acpi_device *device,183		unsigned int handle);184static void sony_nc_rfkill_cleanup(void);185static void sony_nc_rfkill_update(void);186 187/*********** Input Devices ***********/188 189#define SONY_LAPTOP_BUF_SIZE	128190struct sony_laptop_input_s {191	atomic_t		users;192	struct input_dev	*jog_dev;193	struct input_dev	*key_dev;194	struct kfifo		fifo;195	spinlock_t		fifo_lock;196	struct timer_list	release_key_timer;197};198 199static struct sony_laptop_input_s sony_laptop_input = {200	.users = ATOMIC_INIT(0),201};202 203struct sony_laptop_keypress {204	struct input_dev *dev;205	int key;206};207 208/* Correspondance table between sonypi events209 * and input layer indexes in the keymap210 */211static const int sony_laptop_input_index[] = {212	-1,	/*  0 no event */213	-1,	/*  1 SONYPI_EVENT_JOGDIAL_DOWN */214	-1,	/*  2 SONYPI_EVENT_JOGDIAL_UP */215	-1,	/*  3 SONYPI_EVENT_JOGDIAL_DOWN_PRESSED */216	-1,	/*  4 SONYPI_EVENT_JOGDIAL_UP_PRESSED */217	-1,	/*  5 SONYPI_EVENT_JOGDIAL_PRESSED */218	-1,	/*  6 SONYPI_EVENT_JOGDIAL_RELEASED */219	 0,	/*  7 SONYPI_EVENT_CAPTURE_PRESSED */220	 1,	/*  8 SONYPI_EVENT_CAPTURE_RELEASED */221	 2,	/*  9 SONYPI_EVENT_CAPTURE_PARTIALPRESSED */222	 3,	/* 10 SONYPI_EVENT_CAPTURE_PARTIALRELEASED */223	 4,	/* 11 SONYPI_EVENT_FNKEY_ESC */224	 5,	/* 12 SONYPI_EVENT_FNKEY_F1 */225	 6,	/* 13 SONYPI_EVENT_FNKEY_F2 */226	 7,	/* 14 SONYPI_EVENT_FNKEY_F3 */227	 8,	/* 15 SONYPI_EVENT_FNKEY_F4 */228	 9,	/* 16 SONYPI_EVENT_FNKEY_F5 */229	10,	/* 17 SONYPI_EVENT_FNKEY_F6 */230	11,	/* 18 SONYPI_EVENT_FNKEY_F7 */231	12,	/* 19 SONYPI_EVENT_FNKEY_F8 */232	13,	/* 20 SONYPI_EVENT_FNKEY_F9 */233	14,	/* 21 SONYPI_EVENT_FNKEY_F10 */234	15,	/* 22 SONYPI_EVENT_FNKEY_F11 */235	16,	/* 23 SONYPI_EVENT_FNKEY_F12 */236	17,	/* 24 SONYPI_EVENT_FNKEY_1 */237	18,	/* 25 SONYPI_EVENT_FNKEY_2 */238	19,	/* 26 SONYPI_EVENT_FNKEY_D */239	20,	/* 27 SONYPI_EVENT_FNKEY_E */240	21,	/* 28 SONYPI_EVENT_FNKEY_F */241	22,	/* 29 SONYPI_EVENT_FNKEY_S */242	23,	/* 30 SONYPI_EVENT_FNKEY_B */243	24,	/* 31 SONYPI_EVENT_BLUETOOTH_PRESSED */244	25,	/* 32 SONYPI_EVENT_PKEY_P1 */245	26,	/* 33 SONYPI_EVENT_PKEY_P2 */246	27,	/* 34 SONYPI_EVENT_PKEY_P3 */247	28,	/* 35 SONYPI_EVENT_BACK_PRESSED */248	-1,	/* 36 SONYPI_EVENT_LID_CLOSED */249	-1,	/* 37 SONYPI_EVENT_LID_OPENED */250	29,	/* 38 SONYPI_EVENT_BLUETOOTH_ON */251	30,	/* 39 SONYPI_EVENT_BLUETOOTH_OFF */252	31,	/* 40 SONYPI_EVENT_HELP_PRESSED */253	32,	/* 41 SONYPI_EVENT_FNKEY_ONLY */254	33,	/* 42 SONYPI_EVENT_JOGDIAL_FAST_DOWN */255	34,	/* 43 SONYPI_EVENT_JOGDIAL_FAST_UP */256	35,	/* 44 SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED */257	36,	/* 45 SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED */258	37,	/* 46 SONYPI_EVENT_JOGDIAL_VFAST_DOWN */259	38,	/* 47 SONYPI_EVENT_JOGDIAL_VFAST_UP */260	39,	/* 48 SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED */261	40,	/* 49 SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED */262	41,	/* 50 SONYPI_EVENT_ZOOM_PRESSED */263	42,	/* 51 SONYPI_EVENT_THUMBPHRASE_PRESSED */264	43,	/* 52 SONYPI_EVENT_MEYE_FACE */265	44,	/* 53 SONYPI_EVENT_MEYE_OPPOSITE */266	45,	/* 54 SONYPI_EVENT_MEMORYSTICK_INSERT */267	46,	/* 55 SONYPI_EVENT_MEMORYSTICK_EJECT */268	-1,	/* 56 SONYPI_EVENT_ANYBUTTON_RELEASED */269	-1,	/* 57 SONYPI_EVENT_BATTERY_INSERT */270	-1,	/* 58 SONYPI_EVENT_BATTERY_REMOVE */271	-1,	/* 59 SONYPI_EVENT_FNKEY_RELEASED */272	47,	/* 60 SONYPI_EVENT_WIRELESS_ON */273	48,	/* 61 SONYPI_EVENT_WIRELESS_OFF */274	49,	/* 62 SONYPI_EVENT_ZOOM_IN_PRESSED */275	50,	/* 63 SONYPI_EVENT_ZOOM_OUT_PRESSED */276	51,	/* 64 SONYPI_EVENT_CD_EJECT_PRESSED */277	52,	/* 65 SONYPI_EVENT_MODEKEY_PRESSED */278	53,	/* 66 SONYPI_EVENT_PKEY_P4 */279	54,	/* 67 SONYPI_EVENT_PKEY_P5 */280	55,	/* 68 SONYPI_EVENT_SETTINGKEY_PRESSED */281	56,	/* 69 SONYPI_EVENT_VOLUME_INC_PRESSED */282	57,	/* 70 SONYPI_EVENT_VOLUME_DEC_PRESSED */283	-1,	/* 71 SONYPI_EVENT_BRIGHTNESS_PRESSED */284	58,	/* 72 SONYPI_EVENT_MEDIA_PRESSED */285	59,	/* 72 SONYPI_EVENT_VENDOR_PRESSED */286};287 288static int sony_laptop_input_keycode_map[] = {289	KEY_CAMERA,	/*  0 SONYPI_EVENT_CAPTURE_PRESSED */290	KEY_RESERVED,	/*  1 SONYPI_EVENT_CAPTURE_RELEASED */291	KEY_RESERVED,	/*  2 SONYPI_EVENT_CAPTURE_PARTIALPRESSED */292	KEY_RESERVED,	/*  3 SONYPI_EVENT_CAPTURE_PARTIALRELEASED */293	KEY_FN_ESC,	/*  4 SONYPI_EVENT_FNKEY_ESC */294	KEY_FN_F1,	/*  5 SONYPI_EVENT_FNKEY_F1 */295	KEY_FN_F2,	/*  6 SONYPI_EVENT_FNKEY_F2 */296	KEY_FN_F3,	/*  7 SONYPI_EVENT_FNKEY_F3 */297	KEY_FN_F4,	/*  8 SONYPI_EVENT_FNKEY_F4 */298	KEY_FN_F5,	/*  9 SONYPI_EVENT_FNKEY_F5 */299	KEY_FN_F6,	/* 10 SONYPI_EVENT_FNKEY_F6 */300	KEY_FN_F7,	/* 11 SONYPI_EVENT_FNKEY_F7 */301	KEY_FN_F8,	/* 12 SONYPI_EVENT_FNKEY_F8 */302	KEY_FN_F9,	/* 13 SONYPI_EVENT_FNKEY_F9 */303	KEY_FN_F10,	/* 14 SONYPI_EVENT_FNKEY_F10 */304	KEY_FN_F11,	/* 15 SONYPI_EVENT_FNKEY_F11 */305	KEY_FN_F12,	/* 16 SONYPI_EVENT_FNKEY_F12 */306	KEY_FN_1,	/* 17 SONYPI_EVENT_FNKEY_1 */307	KEY_FN_2,	/* 18 SONYPI_EVENT_FNKEY_2 */308	KEY_FN_D,	/* 19 SONYPI_EVENT_FNKEY_D */309	KEY_FN_E,	/* 20 SONYPI_EVENT_FNKEY_E */310	KEY_FN_F,	/* 21 SONYPI_EVENT_FNKEY_F */311	KEY_FN_S,	/* 22 SONYPI_EVENT_FNKEY_S */312	KEY_FN_B,	/* 23 SONYPI_EVENT_FNKEY_B */313	KEY_BLUETOOTH,	/* 24 SONYPI_EVENT_BLUETOOTH_PRESSED */314	KEY_PROG1,	/* 25 SONYPI_EVENT_PKEY_P1 */315	KEY_PROG2,	/* 26 SONYPI_EVENT_PKEY_P2 */316	KEY_PROG3,	/* 27 SONYPI_EVENT_PKEY_P3 */317	KEY_BACK,	/* 28 SONYPI_EVENT_BACK_PRESSED */318	KEY_BLUETOOTH,	/* 29 SONYPI_EVENT_BLUETOOTH_ON */319	KEY_BLUETOOTH,	/* 30 SONYPI_EVENT_BLUETOOTH_OFF */320	KEY_HELP,	/* 31 SONYPI_EVENT_HELP_PRESSED */321	KEY_FN,		/* 32 SONYPI_EVENT_FNKEY_ONLY */322	KEY_RESERVED,	/* 33 SONYPI_EVENT_JOGDIAL_FAST_DOWN */323	KEY_RESERVED,	/* 34 SONYPI_EVENT_JOGDIAL_FAST_UP */324	KEY_RESERVED,	/* 35 SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED */325	KEY_RESERVED,	/* 36 SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED */326	KEY_RESERVED,	/* 37 SONYPI_EVENT_JOGDIAL_VFAST_DOWN */327	KEY_RESERVED,	/* 38 SONYPI_EVENT_JOGDIAL_VFAST_UP */328	KEY_RESERVED,	/* 39 SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED */329	KEY_RESERVED,	/* 40 SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED */330	KEY_ZOOM,	/* 41 SONYPI_EVENT_ZOOM_PRESSED */331	BTN_THUMB,	/* 42 SONYPI_EVENT_THUMBPHRASE_PRESSED */332	KEY_RESERVED,	/* 43 SONYPI_EVENT_MEYE_FACE */333	KEY_RESERVED,	/* 44 SONYPI_EVENT_MEYE_OPPOSITE */334	KEY_RESERVED,	/* 45 SONYPI_EVENT_MEMORYSTICK_INSERT */335	KEY_RESERVED,	/* 46 SONYPI_EVENT_MEMORYSTICK_EJECT */336	KEY_WLAN,	/* 47 SONYPI_EVENT_WIRELESS_ON */337	KEY_WLAN,	/* 48 SONYPI_EVENT_WIRELESS_OFF */338	KEY_ZOOMIN,	/* 49 SONYPI_EVENT_ZOOM_IN_PRESSED */339	KEY_ZOOMOUT,	/* 50 SONYPI_EVENT_ZOOM_OUT_PRESSED */340	KEY_EJECTCD,	/* 51 SONYPI_EVENT_CD_EJECT_PRESSED */341	KEY_F13,	/* 52 SONYPI_EVENT_MODEKEY_PRESSED */342	KEY_PROG4,	/* 53 SONYPI_EVENT_PKEY_P4 */343	KEY_F14,	/* 54 SONYPI_EVENT_PKEY_P5 */344	KEY_F15,	/* 55 SONYPI_EVENT_SETTINGKEY_PRESSED */345	KEY_VOLUMEUP,	/* 56 SONYPI_EVENT_VOLUME_INC_PRESSED */346	KEY_VOLUMEDOWN,	/* 57 SONYPI_EVENT_VOLUME_DEC_PRESSED */347	KEY_MEDIA,	/* 58 SONYPI_EVENT_MEDIA_PRESSED */348	KEY_VENDOR,	/* 59 SONYPI_EVENT_VENDOR_PRESSED */349};350 351/* release buttons after a short delay if pressed */352static void do_sony_laptop_release_key(struct timer_list *unused)353{354	struct sony_laptop_keypress kp;355	unsigned long flags;356 357	spin_lock_irqsave(&sony_laptop_input.fifo_lock, flags);358 359	if (kfifo_out(&sony_laptop_input.fifo,360		      (unsigned char *)&kp, sizeof(kp)) == sizeof(kp)) {361		input_report_key(kp.dev, kp.key, 0);362		input_sync(kp.dev);363	}364 365	/* If there is something in the fifo schedule next release. */366	if (kfifo_len(&sony_laptop_input.fifo) != 0)367		mod_timer(&sony_laptop_input.release_key_timer,368			  jiffies + msecs_to_jiffies(10));369 370	spin_unlock_irqrestore(&sony_laptop_input.fifo_lock, flags);371}372 373/* forward event to the input subsystem */374static void sony_laptop_report_input_event(u8 event)375{376	struct input_dev *jog_dev = sony_laptop_input.jog_dev;377	struct input_dev *key_dev = sony_laptop_input.key_dev;378	struct sony_laptop_keypress kp = { NULL };379	int scancode = -1;380 381	if (event == SONYPI_EVENT_FNKEY_RELEASED ||382			event == SONYPI_EVENT_ANYBUTTON_RELEASED) {383		/* Nothing, not all VAIOs generate this event */384		return;385	}386 387	/* report events */388	switch (event) {389	/* jog_dev events */390	case SONYPI_EVENT_JOGDIAL_UP:391	case SONYPI_EVENT_JOGDIAL_UP_PRESSED:392		input_report_rel(jog_dev, REL_WHEEL, 1);393		input_sync(jog_dev);394		return;395 396	case SONYPI_EVENT_JOGDIAL_DOWN:397	case SONYPI_EVENT_JOGDIAL_DOWN_PRESSED:398		input_report_rel(jog_dev, REL_WHEEL, -1);399		input_sync(jog_dev);400		return;401 402	/* key_dev events */403	case SONYPI_EVENT_JOGDIAL_PRESSED:404		kp.key = BTN_MIDDLE;405		kp.dev = jog_dev;406		break;407 408	default:409		if (event >= ARRAY_SIZE(sony_laptop_input_index)) {410			dprintk("sony_laptop_report_input_event, event not known: %d\n", event);411			break;412		}413		if ((scancode = sony_laptop_input_index[event]) != -1) {414			kp.key = sony_laptop_input_keycode_map[scancode];415			if (kp.key != KEY_UNKNOWN)416				kp.dev = key_dev;417		}418		break;419	}420 421	if (kp.dev) {422		/* if we have a scancode we emit it so we can always423		    remap the key */424		if (scancode != -1)425			input_event(kp.dev, EV_MSC, MSC_SCAN, scancode);426		input_report_key(kp.dev, kp.key, 1);427		input_sync(kp.dev);428 429		/* schedule key release */430		kfifo_in_locked(&sony_laptop_input.fifo,431				(unsigned char *)&kp, sizeof(kp),432				&sony_laptop_input.fifo_lock);433		mod_timer(&sony_laptop_input.release_key_timer,434			  jiffies + msecs_to_jiffies(10));435	} else436		dprintk("unknown input event %.2x\n", event);437}438 439static int sony_laptop_setup_input(struct acpi_device *acpi_device)440{441	struct input_dev *jog_dev;442	struct input_dev *key_dev;443	int i;444	int error;445 446	/* don't run again if already initialized */447	if (atomic_add_return(1, &sony_laptop_input.users) > 1)448		return 0;449 450	/* kfifo */451	spin_lock_init(&sony_laptop_input.fifo_lock);452	error = kfifo_alloc(&sony_laptop_input.fifo,453			    SONY_LAPTOP_BUF_SIZE, GFP_KERNEL);454	if (error) {455		pr_err("kfifo_alloc failed\n");456		goto err_dec_users;457	}458 459	timer_setup(&sony_laptop_input.release_key_timer,460		    do_sony_laptop_release_key, 0);461 462	/* input keys */463	key_dev = input_allocate_device();464	if (!key_dev) {465		error = -ENOMEM;466		goto err_free_kfifo;467	}468 469	key_dev->name = "Sony Vaio Keys";470	key_dev->id.bustype = BUS_ISA;471	key_dev->id.vendor = PCI_VENDOR_ID_SONY;472	key_dev->dev.parent = &acpi_device->dev;473 474	/* Initialize the Input Drivers: special keys */475	input_set_capability(key_dev, EV_MSC, MSC_SCAN);476 477	__set_bit(EV_KEY, key_dev->evbit);478	key_dev->keycodesize = sizeof(sony_laptop_input_keycode_map[0]);479	key_dev->keycodemax = ARRAY_SIZE(sony_laptop_input_keycode_map);480	key_dev->keycode = &sony_laptop_input_keycode_map;481	for (i = 0; i < ARRAY_SIZE(sony_laptop_input_keycode_map); i++)482		__set_bit(sony_laptop_input_keycode_map[i], key_dev->keybit);483	__clear_bit(KEY_RESERVED, key_dev->keybit);484 485	error = input_register_device(key_dev);486	if (error)487		goto err_free_keydev;488 489	sony_laptop_input.key_dev = key_dev;490 491	/* jogdial */492	jog_dev = input_allocate_device();493	if (!jog_dev) {494		error = -ENOMEM;495		goto err_unregister_keydev;496	}497 498	jog_dev->name = "Sony Vaio Jogdial";499	jog_dev->id.bustype = BUS_ISA;500	jog_dev->id.vendor = PCI_VENDOR_ID_SONY;501	jog_dev->dev.parent = &acpi_device->dev;502 503	input_set_capability(jog_dev, EV_KEY, BTN_MIDDLE);504	input_set_capability(jog_dev, EV_REL, REL_WHEEL);505 506	error = input_register_device(jog_dev);507	if (error)508		goto err_free_jogdev;509 510	sony_laptop_input.jog_dev = jog_dev;511 512	return 0;513 514err_free_jogdev:515	input_free_device(jog_dev);516 517err_unregister_keydev:518	input_unregister_device(key_dev);519	/* to avoid kref underflow below at input_free_device */520	key_dev = NULL;521 522err_free_keydev:523	input_free_device(key_dev);524 525err_free_kfifo:526	kfifo_free(&sony_laptop_input.fifo);527 528err_dec_users:529	atomic_dec(&sony_laptop_input.users);530	return error;531}532 533static void sony_laptop_remove_input(void)534{535	struct sony_laptop_keypress kp = { NULL };536 537	/* Cleanup only after the last user has gone */538	if (!atomic_dec_and_test(&sony_laptop_input.users))539		return;540 541	del_timer_sync(&sony_laptop_input.release_key_timer);542 543	/*544	 * Generate key-up events for remaining keys. Note that we don't545	 * need locking since nobody is adding new events to the kfifo.546	 */547	while (kfifo_out(&sony_laptop_input.fifo,548			 (unsigned char *)&kp, sizeof(kp)) == sizeof(kp)) {549		input_report_key(kp.dev, kp.key, 0);550		input_sync(kp.dev);551	}552 553	/* destroy input devs */554	input_unregister_device(sony_laptop_input.key_dev);555	sony_laptop_input.key_dev = NULL;556 557	if (sony_laptop_input.jog_dev) {558		input_unregister_device(sony_laptop_input.jog_dev);559		sony_laptop_input.jog_dev = NULL;560	}561 562	kfifo_free(&sony_laptop_input.fifo);563}564 565/*********** Platform Device ***********/566 567static atomic_t sony_pf_users = ATOMIC_INIT(0);568static struct platform_driver sony_pf_driver = {569	.driver = {570		   .name = "sony-laptop",571		   }572};573static struct platform_device *sony_pf_device;574 575static int sony_pf_add(void)576{577	int ret = 0;578 579	/* don't run again if already initialized */580	if (atomic_add_return(1, &sony_pf_users) > 1)581		return 0;582 583	ret = platform_driver_register(&sony_pf_driver);584	if (ret)585		goto out;586 587	sony_pf_device = platform_device_alloc("sony-laptop", PLATFORM_DEVID_NONE);588	if (!sony_pf_device) {589		ret = -ENOMEM;590		goto out_platform_registered;591	}592 593	ret = platform_device_add(sony_pf_device);594	if (ret)595		goto out_platform_alloced;596 597	return 0;598 599      out_platform_alloced:600	platform_device_put(sony_pf_device);601	sony_pf_device = NULL;602      out_platform_registered:603	platform_driver_unregister(&sony_pf_driver);604      out:605	atomic_dec(&sony_pf_users);606	return ret;607}608 609static void sony_pf_remove(void)610{611	/* deregister only after the last user has gone */612	if (!atomic_dec_and_test(&sony_pf_users))613		return;614 615	platform_device_unregister(sony_pf_device);616	platform_driver_unregister(&sony_pf_driver);617}618 619/*********** SNC (SNY5001) Device ***********/620 621/* the device uses 1-based values, while the backlight subsystem uses622   0-based values */623#define SONY_MAX_BRIGHTNESS	8624 625#define SNC_VALIDATE_IN		0626#define SNC_VALIDATE_OUT	1627 628static ssize_t sony_nc_sysfs_show(struct device *, struct device_attribute *,629			      char *);630static ssize_t sony_nc_sysfs_store(struct device *, struct device_attribute *,631			       const char *, size_t);632static int boolean_validate(const int, const int);633static int brightness_default_validate(const int, const int);634 635struct sony_nc_value {636	char *name;		/* name of the entry */637	char **acpiget;		/* names of the ACPI get function */638	char **acpiset;		/* names of the ACPI set function */639	int (*validate)(const int, const int);	/* input/output validation */640	int value;		/* current setting */641	int valid;		/* Has ever been set */642	int debug;		/* active only in debug mode ? */643	struct device_attribute devattr;	/* sysfs attribute */644};645 646#define SNC_HANDLE_NAMES(_name, _values...) \647	static char *snc_##_name[] = { _values, NULL }648 649#define SNC_HANDLE(_name, _getters, _setters, _validate, _debug) \650	{ \651		.name		= __stringify(_name), \652		.acpiget	= _getters, \653		.acpiset	= _setters, \654		.validate	= _validate, \655		.debug		= _debug, \656		.devattr	= __ATTR(_name, 0, sony_nc_sysfs_show, sony_nc_sysfs_store), \657	}658 659#define SNC_HANDLE_NULL	{ .name = NULL }660 661SNC_HANDLE_NAMES(fnkey_get, "GHKE");662 663SNC_HANDLE_NAMES(brightness_def_get, "GPBR");664SNC_HANDLE_NAMES(brightness_def_set, "SPBR");665 666SNC_HANDLE_NAMES(cdpower_get, "GCDP");667SNC_HANDLE_NAMES(cdpower_set, "SCDP", "CDPW");668 669SNC_HANDLE_NAMES(audiopower_get, "GAZP");670SNC_HANDLE_NAMES(audiopower_set, "AZPW");671 672SNC_HANDLE_NAMES(lanpower_get, "GLNP");673SNC_HANDLE_NAMES(lanpower_set, "LNPW");674 675SNC_HANDLE_NAMES(lidstate_get, "GLID");676 677SNC_HANDLE_NAMES(indicatorlamp_get, "GILS");678SNC_HANDLE_NAMES(indicatorlamp_set, "SILS");679 680SNC_HANDLE_NAMES(gainbass_get, "GMGB");681SNC_HANDLE_NAMES(gainbass_set, "CMGB");682 683SNC_HANDLE_NAMES(PID_get, "GPID");684 685SNC_HANDLE_NAMES(CTR_get, "GCTR");686SNC_HANDLE_NAMES(CTR_set, "SCTR");687 688SNC_HANDLE_NAMES(PCR_get, "GPCR");689SNC_HANDLE_NAMES(PCR_set, "SPCR");690 691SNC_HANDLE_NAMES(CMI_get, "GCMI");692SNC_HANDLE_NAMES(CMI_set, "SCMI");693 694static struct sony_nc_value sony_nc_values[] = {695	SNC_HANDLE(brightness_default, snc_brightness_def_get,696			snc_brightness_def_set, brightness_default_validate, 0),697	SNC_HANDLE(fnkey, snc_fnkey_get, NULL, NULL, 0),698	SNC_HANDLE(cdpower, snc_cdpower_get, snc_cdpower_set, boolean_validate, 0),699	SNC_HANDLE(audiopower, snc_audiopower_get, snc_audiopower_set,700			boolean_validate, 0),701	SNC_HANDLE(lanpower, snc_lanpower_get, snc_lanpower_set,702			boolean_validate, 1),703	SNC_HANDLE(lidstate, snc_lidstate_get, NULL,704			boolean_validate, 0),705	SNC_HANDLE(indicatorlamp, snc_indicatorlamp_get, snc_indicatorlamp_set,706			boolean_validate, 0),707	SNC_HANDLE(gainbass, snc_gainbass_get, snc_gainbass_set,708			boolean_validate, 0),709	/* unknown methods */710	SNC_HANDLE(PID, snc_PID_get, NULL, NULL, 1),711	SNC_HANDLE(CTR, snc_CTR_get, snc_CTR_set, NULL, 1),712	SNC_HANDLE(PCR, snc_PCR_get, snc_PCR_set, NULL, 1),713	SNC_HANDLE(CMI, snc_CMI_get, snc_CMI_set, NULL, 1),714	SNC_HANDLE_NULL715};716 717static acpi_handle sony_nc_acpi_handle;718static struct acpi_device *sony_nc_acpi_device = NULL;719 720/*721 * acpi_evaluate_object wrappers722 * all useful calls into SNC methods take one or zero parameters and return723 * integers or arrays.724 */725static union acpi_object *__call_snc_method(acpi_handle handle, char *method,726		u64 *value)727{728	union acpi_object *result = NULL;729	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };730	acpi_status status;731 732	if (value) {733		struct acpi_object_list params;734		union acpi_object in;735		in.type = ACPI_TYPE_INTEGER;736		in.integer.value = *value;737		params.count = 1;738		params.pointer = &in;739		status = acpi_evaluate_object(handle, method, &params, &output);740		dprintk("__call_snc_method: [%s:0x%.8x%.8x]\n", method,741				(unsigned int)(*value >> 32),742				(unsigned int)*value & 0xffffffff);743	} else {744		status = acpi_evaluate_object(handle, method, NULL, &output);745		dprintk("__call_snc_method: [%s]\n", method);746	}747 748	if (ACPI_FAILURE(status)) {749		pr_err("Failed to evaluate [%s]\n", method);750		return NULL;751	}752 753	result = (union acpi_object *) output.pointer;754	if (!result)755		dprintk("No return object [%s]\n", method);756 757	return result;758}759 760static int sony_nc_buffer_call(acpi_handle handle, char *name, u64 *value,761		void *buffer, size_t buflen)762{763	int ret = 0;764	size_t len;765	union acpi_object *object = __call_snc_method(handle, name, value);766 767	if (!object)768		return -EINVAL;769 770	if (!buffer) {771		/* do nothing */772	} else if (object->type == ACPI_TYPE_BUFFER) {773		len = MIN(buflen, object->buffer.length);774		memset(buffer, 0, buflen);775		memcpy(buffer, object->buffer.pointer, len);776 777	} else if (object->type == ACPI_TYPE_INTEGER) {778		len = MIN(buflen, sizeof(object->integer.value));779		memset(buffer, 0, buflen);780		memcpy(buffer, &object->integer.value, len);781 782	} else {783		pr_warn("Unexpected acpi_object: 0x%x\n", object->type);784		ret = -EINVAL;785	}786 787	kfree(object);788	return ret;789}790 791static int sony_nc_int_call(acpi_handle handle, char *name, int *value, int792		*result)793{794	int ret;795 796	if (value) {797		u64 v = *value;798 799		ret = sony_nc_buffer_call(handle, name, &v, result,800				sizeof(*result));801	} else {802		ret =  sony_nc_buffer_call(handle, name, NULL, result,803				sizeof(*result));804	}805	return ret;806}807 808struct sony_nc_handles {809	u16 cap[0x10];810	struct device_attribute devattr;811};812 813static struct sony_nc_handles *handles;814 815static ssize_t sony_nc_handles_show(struct device *dev,816		struct device_attribute *attr, char *buffer)817{818	ssize_t len = 0;819	int i;820 821	for (i = 0; i < ARRAY_SIZE(handles->cap); i++) {822		len += sysfs_emit_at(buffer, len, "0x%.4x ", handles->cap[i]);823	}824	len += sysfs_emit_at(buffer, len, "\n");825 826	return len;827}828 829static int sony_nc_handles_setup(struct platform_device *pd)830{831	int i, r, result, arg;832 833	handles = kzalloc(sizeof(*handles), GFP_KERNEL);834	if (!handles)835		return -ENOMEM;836 837	for (i = 0; i < ARRAY_SIZE(handles->cap); i++) {838		arg = i + 0x20;839		r = sony_nc_int_call(sony_nc_acpi_handle, "SN00", &arg,840					&result);841		if (!r) {842			dprintk("caching handle 0x%.4x (offset: 0x%.2x)\n",843					result, i);844			handles->cap[i] = result;845		}846	}847 848	if (debug) {849		sysfs_attr_init(&handles->devattr.attr);850		handles->devattr.attr.name = "handles";851		handles->devattr.attr.mode = S_IRUGO;852		handles->devattr.show = sony_nc_handles_show;853 854		/* allow reading capabilities via sysfs */855		if (device_create_file(&pd->dev, &handles->devattr)) {856			kfree(handles);857			handles = NULL;858			return -1;859		}860	}861 862	return 0;863}864 865static int sony_nc_handles_cleanup(struct platform_device *pd)866{867	if (handles) {868		if (debug)869			device_remove_file(&pd->dev, &handles->devattr);870		kfree(handles);871		handles = NULL;872	}873	return 0;874}875 876static int sony_find_snc_handle(int handle)877{878	int i;879 880	/* not initialized yet, return early */881	if (!handles || !handle)882		return -EINVAL;883 884	for (i = 0; i < 0x10; i++) {885		if (handles->cap[i] == handle) {886			dprintk("found handle 0x%.4x (offset: 0x%.2x)\n",887					handle, i);888			return i;889		}890	}891	dprintk("handle 0x%.4x not found\n", handle);892	return -EINVAL;893}894 895static int sony_call_snc_handle(int handle, int argument, int *result)896{897	int arg, ret = 0;898	int offset = sony_find_snc_handle(handle);899 900	if (offset < 0)901		return offset;902 903	arg = offset | argument;904	ret = sony_nc_int_call(sony_nc_acpi_handle, "SN07", &arg, result);905	dprintk("called SN07 with 0x%.4x (result: 0x%.4x)\n", arg, *result);906	return ret;907}908 909/*910 * sony_nc_values input/output validate functions911 */912 913/* brightness_default_validate:914 *915 * manipulate input output values to keep consistency with the916 * backlight framework for which brightness values are 0-based.917 */918static int brightness_default_validate(const int direction, const int value)919{920	switch (direction) {921		case SNC_VALIDATE_OUT:922			return value - 1;923		case SNC_VALIDATE_IN:924			if (value >= 0 && value < SONY_MAX_BRIGHTNESS)925				return value + 1;926	}927	return -EINVAL;928}929 930/* boolean_validate:931 *932 * on input validate boolean values 0/1, on output just pass the933 * received value.934 */935static int boolean_validate(const int direction, const int value)936{937	if (direction == SNC_VALIDATE_IN) {938		if (value != 0 && value != 1)939			return -EINVAL;940	}941	return value;942}943 944/*945 * Sysfs show/store common to all sony_nc_values946 */947static ssize_t sony_nc_sysfs_show(struct device *dev, struct device_attribute *attr,948			      char *buffer)949{950	int value, ret = 0;951	struct sony_nc_value *item =952	    container_of(attr, struct sony_nc_value, devattr);953 954	if (!*item->acpiget)955		return -EIO;956 957	ret = sony_nc_int_call(sony_nc_acpi_handle, *item->acpiget, NULL,958				&value);959	if (ret < 0)960		return -EIO;961 962	if (item->validate)963		value = item->validate(SNC_VALIDATE_OUT, value);964 965	return sysfs_emit(buffer, "%d\n", value);966}967 968static ssize_t sony_nc_sysfs_store(struct device *dev,969			       struct device_attribute *attr,970			       const char *buffer, size_t count)971{972	int value;973	int ret = 0;974	struct sony_nc_value *item =975	    container_of(attr, struct sony_nc_value, devattr);976 977	if (!item->acpiset)978		return -EIO;979 980	if (count > 31)981		return -EINVAL;982 983	if (kstrtoint(buffer, 10, &value))984		return -EINVAL;985 986	if (item->validate)987		value = item->validate(SNC_VALIDATE_IN, value);988 989	if (value < 0)990		return value;991 992	ret = sony_nc_int_call(sony_nc_acpi_handle, *item->acpiset,993			       &value, NULL);994	if (ret < 0)995		return -EIO;996 997	item->value = value;998	item->valid = 1;999	return count;1000}1001 1002 1003/*1004 * Backlight device1005 */1006struct sony_backlight_props {1007	struct backlight_device *dev;1008	int			handle;1009	int			cmd_base;1010	u8			offset;1011	u8			maxlvl;1012};1013static struct sony_backlight_props sony_bl_props;1014 1015static int sony_backlight_update_status(struct backlight_device *bd)1016{1017	int arg = bd->props.brightness + 1;1018	return sony_nc_int_call(sony_nc_acpi_handle, "SBRT", &arg, NULL);1019}1020 1021static int sony_backlight_get_brightness(struct backlight_device *bd)1022{1023	int value;1024 1025	if (sony_nc_int_call(sony_nc_acpi_handle, "GBRT", NULL, &value))1026		return 0;1027	/* brightness levels are 1-based, while backlight ones are 0-based */1028	return value - 1;1029}1030 1031static int sony_nc_get_brightness_ng(struct backlight_device *bd)1032{1033	int result;1034	struct sony_backlight_props *sdev =1035		(struct sony_backlight_props *)bl_get_data(bd);1036 1037	sony_call_snc_handle(sdev->handle, sdev->cmd_base + 0x100, &result);1038 1039	return (result & 0xff) - sdev->offset;1040}1041 1042static int sony_nc_update_status_ng(struct backlight_device *bd)1043{1044	int value, result;1045	struct sony_backlight_props *sdev =1046		(struct sony_backlight_props *)bl_get_data(bd);1047 1048	value = bd->props.brightness + sdev->offset;1049	if (sony_call_snc_handle(sdev->handle, sdev->cmd_base | (value << 0x10),1050				&result))1051		return -EIO;1052 1053	return value;1054}1055 1056static const struct backlight_ops sony_backlight_ops = {1057	.options = BL_CORE_SUSPENDRESUME,1058	.update_status = sony_backlight_update_status,1059	.get_brightness = sony_backlight_get_brightness,1060};1061static const struct backlight_ops sony_backlight_ng_ops = {1062	.options = BL_CORE_SUSPENDRESUME,1063	.update_status = sony_nc_update_status_ng,1064	.get_brightness = sony_nc_get_brightness_ng,1065};1066 1067/*1068 * New SNC-only Vaios event mapping to driver known keys1069 */1070struct sony_nc_event {1071	u8	data;1072	u8	event;1073};1074 1075static struct sony_nc_event sony_100_events[] = {1076	{ 0x90, SONYPI_EVENT_PKEY_P1 },1077	{ 0x10, SONYPI_EVENT_ANYBUTTON_RELEASED },1078	{ 0x91, SONYPI_EVENT_PKEY_P2 },1079	{ 0x11, SONYPI_EVENT_ANYBUTTON_RELEASED },1080	{ 0x81, SONYPI_EVENT_FNKEY_F1 },1081	{ 0x01, SONYPI_EVENT_FNKEY_RELEASED },1082	{ 0x82, SONYPI_EVENT_FNKEY_F2 },1083	{ 0x02, SONYPI_EVENT_FNKEY_RELEASED },1084	{ 0x83, SONYPI_EVENT_FNKEY_F3 },1085	{ 0x03, SONYPI_EVENT_FNKEY_RELEASED },1086	{ 0x84, SONYPI_EVENT_FNKEY_F4 },1087	{ 0x04, SONYPI_EVENT_FNKEY_RELEASED },1088	{ 0x85, SONYPI_EVENT_FNKEY_F5 },1089	{ 0x05, SONYPI_EVENT_FNKEY_RELEASED },1090	{ 0x86, SONYPI_EVENT_FNKEY_F6 },1091	{ 0x06, SONYPI_EVENT_FNKEY_RELEASED },1092	{ 0x87, SONYPI_EVENT_FNKEY_F7 },1093	{ 0x07, SONYPI_EVENT_FNKEY_RELEASED },1094	{ 0x88, SONYPI_EVENT_FNKEY_F8 },1095	{ 0x08, SONYPI_EVENT_FNKEY_RELEASED },1096	{ 0x89, SONYPI_EVENT_FNKEY_F9 },1097	{ 0x09, SONYPI_EVENT_FNKEY_RELEASED },1098	{ 0x8A, SONYPI_EVENT_FNKEY_F10 },1099	{ 0x0A, SONYPI_EVENT_FNKEY_RELEASED },1100	{ 0x8B, SONYPI_EVENT_FNKEY_F11 },1101	{ 0x0B, SONYPI_EVENT_FNKEY_RELEASED },1102	{ 0x8C, SONYPI_EVENT_FNKEY_F12 },1103	{ 0x0C, SONYPI_EVENT_FNKEY_RELEASED },1104	{ 0x9d, SONYPI_EVENT_ZOOM_PRESSED },1105	{ 0x1d, SONYPI_EVENT_ANYBUTTON_RELEASED },1106	{ 0x9f, SONYPI_EVENT_CD_EJECT_PRESSED },1107	{ 0x1f, SONYPI_EVENT_ANYBUTTON_RELEASED },1108	{ 0xa1, SONYPI_EVENT_MEDIA_PRESSED },1109	{ 0x21, SONYPI_EVENT_ANYBUTTON_RELEASED },1110	{ 0xa4, SONYPI_EVENT_CD_EJECT_PRESSED },1111	{ 0x24, SONYPI_EVENT_ANYBUTTON_RELEASED },1112	{ 0xa5, SONYPI_EVENT_VENDOR_PRESSED },1113	{ 0x25, SONYPI_EVENT_ANYBUTTON_RELEASED },1114	{ 0xa6, SONYPI_EVENT_HELP_PRESSED },1115	{ 0x26, SONYPI_EVENT_ANYBUTTON_RELEASED },1116	{ 0xa8, SONYPI_EVENT_FNKEY_1 },1117	{ 0x28, SONYPI_EVENT_ANYBUTTON_RELEASED },1118	{ 0, 0 },1119};1120 1121static struct sony_nc_event sony_127_events[] = {1122	{ 0x81, SONYPI_EVENT_MODEKEY_PRESSED },1123	{ 0x01, SONYPI_EVENT_ANYBUTTON_RELEASED },1124	{ 0x82, SONYPI_EVENT_PKEY_P1 },1125	{ 0x02, SONYPI_EVENT_ANYBUTTON_RELEASED },1126	{ 0x83, SONYPI_EVENT_PKEY_P2 },1127	{ 0x03, SONYPI_EVENT_ANYBUTTON_RELEASED },1128	{ 0x84, SONYPI_EVENT_PKEY_P3 },1129	{ 0x04, SONYPI_EVENT_ANYBUTTON_RELEASED },1130	{ 0x85, SONYPI_EVENT_PKEY_P4 },1131	{ 0x05, SONYPI_EVENT_ANYBUTTON_RELEASED },1132	{ 0x86, SONYPI_EVENT_PKEY_P5 },1133	{ 0x06, SONYPI_EVENT_ANYBUTTON_RELEASED },1134	{ 0x87, SONYPI_EVENT_SETTINGKEY_PRESSED },1135	{ 0x07, SONYPI_EVENT_ANYBUTTON_RELEASED },1136	{ 0, 0 },1137};1138 1139static int sony_nc_hotkeys_decode(u32 event, unsigned int handle)1140{1141	int ret = -EINVAL;1142	unsigned int result = 0;1143	struct sony_nc_event *key_event;1144 1145	if (sony_call_snc_handle(handle, 0x200, &result)) {1146		dprintk("Unable to decode event 0x%.2x 0x%.2x\n", handle,1147				event);1148		return -EINVAL;1149	}1150 1151	result &= 0xFF;1152 1153	if (handle == 0x0100)1154		key_event = sony_100_events;1155	else1156		key_event = sony_127_events;1157 1158	for (; key_event->data; key_event++) {1159		if (key_event->data == result) {1160			ret = key_event->event;1161			break;1162		}1163	}1164 1165	if (!key_event->data)1166		pr_info("Unknown hotkey 0x%.2x/0x%.2x (handle 0x%.2x)\n",1167				event, result, handle);1168 1169	return ret;1170}1171 1172/*1173 * ACPI callbacks1174 */1175enum event_types {1176	HOTKEY = 1,1177	KILLSWITCH,1178	GFX_SWITCH1179};1180static void sony_nc_notify(struct acpi_device *device, u32 event)1181{1182	u32 real_ev = event;1183	u8 ev_type = 0;1184	int ret;1185 1186	dprintk("sony_nc_notify, event: 0x%.2x\n", event);1187 1188	if (event >= 0x90) {1189		unsigned int result = 0;1190		unsigned int arg = 0;1191		unsigned int handle = 0;1192		unsigned int offset = event - 0x90;1193 1194		if (offset >= ARRAY_SIZE(handles->cap)) {1195			pr_err("Event 0x%x outside of capabilities list\n",1196					event);1197			return;1198		}1199		handle = handles->cap[offset];1200 1201		/* list of handles known for generating events */1202		switch (handle) {1203		/* hotkey event */1204		case 0x0100:1205		case 0x0127:1206			ev_type = HOTKEY;1207			ret = sony_nc_hotkeys_decode(event, handle);1208 1209			if (ret > 0) {1210				sony_laptop_report_input_event(ret);1211				real_ev = ret;1212			}1213 1214			break;1215 1216		/* wlan switch */1217		case 0x0124:1218		case 0x0135:1219			/* events on this handle are reported when the1220			 * switch changes position or for battery1221			 * events. We'll notify both of them but only1222			 * update the rfkill device status when the1223			 * switch is moved.1224			 */1225			ev_type = KILLSWITCH;1226			sony_call_snc_handle(handle, 0x0100, &result);1227			real_ev = result & 0x03;1228 1229			/* hw switch event */1230			if (real_ev == 1)1231				sony_nc_rfkill_update();1232 1233			break;1234 1235		case 0x0128:1236		case 0x0146:1237			/* Hybrid GFX switching */1238			sony_call_snc_handle(handle, 0x0000, &result);1239			dprintk("GFX switch event received (reason: %s)\n",1240					(result == 0x1) ? "switch change" :1241					(result == 0x2) ? "output switch" :1242					(result == 0x3) ? "output switch" :1243					"");1244 1245			ev_type = GFX_SWITCH;1246			real_ev = __sony_nc_gfx_switch_status_get();1247			break;1248 1249		case 0x015B:1250			/* Hybrid GFX switching SVS151290S */1251			ev_type = GFX_SWITCH;1252			real_ev = __sony_nc_gfx_switch_status_get();1253			break;1254		default:1255			dprintk("Unknown event 0x%x for handle 0x%x\n",1256					event, handle);1257			break;1258		}1259 1260		/* clear the event (and the event reason when present) */1261		arg = 1 << offset;1262		sony_nc_int_call(sony_nc_acpi_handle, "SN05", &arg, &result);1263 1264	} else {1265		/* old style event */1266		ev_type = HOTKEY;1267		sony_laptop_report_input_event(real_ev);1268	}1269	acpi_bus_generate_netlink_event(sony_nc_acpi_device->pnp.device_class,1270			dev_name(&sony_nc_acpi_device->dev), ev_type, real_ev);1271}1272 1273static acpi_status sony_walk_callback(acpi_handle handle, u32 level,1274				      void *context, void **return_value)1275{1276	struct acpi_device_info *info;1277 1278	if (ACPI_SUCCESS(acpi_get_object_info(handle, &info))) {1279		pr_warn("method: name: %4.4s, args %X\n",1280			(char *)&info->name, info->param_count);1281 1282		kfree(info);1283	}1284 1285	return AE_OK;1286}1287 1288/*1289 * ACPI device1290 */1291static void sony_nc_function_setup(struct acpi_device *device,1292		struct platform_device *pf_device)1293{1294	unsigned int i, result, bitmask, arg;1295 1296	if (!handles)1297		return;1298 1299	/* setup found handles here */1300	for (i = 0; i < ARRAY_SIZE(handles->cap); i++) {1301		unsigned int handle = handles->cap[i];1302 1303		if (!handle)1304			continue;1305 1306		dprintk("setting up handle 0x%.4x\n", handle);1307 1308		switch (handle) {1309		case 0x0100:1310		case 0x0101:1311		case 0x0127:1312			/* setup hotkeys */1313			sony_call_snc_handle(handle, 0, &result);1314			break;1315		case 0x0102:1316			/* setup hotkeys */1317			sony_call_snc_handle(handle, 0x100, &result);1318			break;1319		case 0x0105:1320		case 0x0148:1321			/* touchpad enable/disable */1322			result = sony_nc_touchpad_setup(pf_device, handle);1323			if (result)1324				pr_err("couldn't set up touchpad control function (%d)\n",1325						result);1326			break;1327		case 0x0115:1328		case 0x0136:1329		case 0x013f:1330			result = sony_nc_battery_care_setup(pf_device, handle);1331			if (result)1332				pr_err("couldn't set up battery care function (%d)\n",1333						result);1334			break;1335		case 0x0119:1336		case 0x015D:1337			result = sony_nc_lid_resume_setup(pf_device, handle);1338			if (result)1339				pr_err("couldn't set up lid resume function (%d)\n",1340						result);1341			break;1342		case 0x0122:1343			result = sony_nc_thermal_setup(pf_device);1344			if (result)1345				pr_err("couldn't set up thermal profile function (%d)\n",1346						result);1347			break;1348		case 0x0128:1349		case 0x0146:1350		case 0x015B:1351			result = sony_nc_gfx_switch_setup(pf_device, handle);1352			if (result)1353				pr_err("couldn't set up GFX Switch status (%d)\n",1354						result);1355			break;1356		case 0x0131:1357			result = sony_nc_highspeed_charging_setup(pf_device);1358			if (result)1359				pr_err("couldn't set up high speed charging function (%d)\n",1360				       result);1361			break;1362		case 0x0124:1363		case 0x0135:1364			result = sony_nc_rfkill_setup(device, handle);1365			if (result)1366				pr_err("couldn't set up rfkill support (%d)\n",1367						result);1368			break;1369		case 0x0137:1370		case 0x0143:1371		case 0x014b:1372		case 0x014c:1373		case 0x0153:1374		case 0x0163:1375			result = sony_nc_kbd_backlight_setup(pf_device, handle);1376			if (result)1377				pr_err("couldn't set up keyboard backlight function (%d)\n",1378						result);1379			break;1380		case 0x0121:1381			result = sony_nc_lowbatt_setup(pf_device);1382			if (result)1383				pr_err("couldn't set up low battery function (%d)\n",1384				       result);1385			break;1386		case 0x0149:1387			result = sony_nc_fanspeed_setup(pf_device);1388			if (result)1389				pr_err("couldn't set up fan speed function (%d)\n",1390				       result);1391			break;1392		case 0x0155:1393			result = sony_nc_usb_charge_setup(pf_device);1394			if (result)1395				pr_err("couldn't set up USB charge support (%d)\n",1396						result);1397			break;1398		case 0x011D:1399			result = sony_nc_panelid_setup(pf_device);1400			if (result)1401				pr_err("couldn't set up panel ID function (%d)\n",1402				       result);1403			break;1404		case 0x0168:1405			result = sony_nc_smart_conn_setup(pf_device);1406			if (result)1407				pr_err("couldn't set up smart connect support (%d)\n",1408						result);1409			break;1410		default:1411			continue;1412		}1413	}1414 1415	/* Enable all events */1416	arg = 0x10;1417	if (!sony_nc_int_call(sony_nc_acpi_handle, "SN00", &arg, &bitmask))1418		sony_nc_int_call(sony_nc_acpi_handle, "SN02", &bitmask,1419				&result);1420}1421 1422static void sony_nc_function_cleanup(struct platform_device *pd)1423{1424	unsigned int i, result, bitmask, handle;1425 1426	if (!handles)1427		return;1428 1429	/* get enabled events and disable them */1430	sony_nc_int_call(sony_nc_acpi_handle, "SN01", NULL, &bitmask);1431	sony_nc_int_call(sony_nc_acpi_handle, "SN03", &bitmask, &result);1432 1433	/* cleanup handles here */1434	for (i = 0; i < ARRAY_SIZE(handles->cap); i++) {1435 1436		handle = handles->cap[i];1437 1438		if (!handle)1439			continue;1440 1441		switch (handle) {1442		case 0x0105:1443		case 0x0148:1444			sony_nc_touchpad_cleanup(pd);1445			break;1446		case 0x0115:1447		case 0x0136:1448		case 0x013f:1449			sony_nc_battery_care_cleanup(pd);1450			break;1451		case 0x0119:1452		case 0x015D:1453			sony_nc_lid_resume_cleanup(pd);1454			break;1455		case 0x0122:1456			sony_nc_thermal_cleanup(pd);1457			break;1458		case 0x0128:1459		case 0x0146:1460		case 0x015B:1461			sony_nc_gfx_switch_cleanup(pd);1462			break;1463		case 0x0131:1464			sony_nc_highspeed_charging_cleanup(pd);1465			break;1466		case 0x0124:1467		case 0x0135:1468			sony_nc_rfkill_cleanup();1469			break;1470		case 0x0137:1471		case 0x0143:1472		case 0x014b:1473		case 0x014c:1474		case 0x0153:1475		case 0x0163:1476			sony_nc_kbd_backlight_cleanup(pd, handle);1477			break;1478		case 0x0121:1479			sony_nc_lowbatt_cleanup(pd);1480			break;1481		case 0x0149:1482			sony_nc_fanspeed_cleanup(pd);1483			break;1484		case 0x0155:1485			sony_nc_usb_charge_cleanup(pd);1486			break;1487		case 0x011D:1488			sony_nc_panelid_cleanup(pd);1489			break;1490		case 0x0168:1491			sony_nc_smart_conn_cleanup(pd);1492			break;1493		default:1494			continue;1495		}1496	}1497 1498	/* finally cleanup the handles list */1499	sony_nc_handles_cleanup(pd);1500}1501 1502#ifdef CONFIG_PM_SLEEP1503static void sony_nc_function_resume(void)1504{1505	unsigned int i, result, bitmask, arg;1506 1507	dprintk("Resuming SNC device\n");1508 1509	for (i = 0; i < ARRAY_SIZE(handles->cap); i++) {1510		unsigned int handle = handles->cap[i];1511 1512		if (!handle)1513			continue;1514 1515		switch (handle) {1516		case 0x0100:1517		case 0x0101:1518		case 0x0127:1519			/* re-enable hotkeys */1520			sony_call_snc_handle(handle, 0, &result);1521			break;1522		case 0x0102:1523			/* re-enable hotkeys */1524			sony_call_snc_handle(handle, 0x100, &result);1525			break;1526		case 0x0122:1527			sony_nc_thermal_resume();1528			break;1529		case 0x0124:1530		case 0x0135:1531			sony_nc_rfkill_update();1532			break;1533		default:1534			continue;1535		}1536	}1537 1538	/* Enable all events */1539	arg = 0x10;1540	if (!sony_nc_int_call(sony_nc_acpi_handle, "SN00", &arg, &bitmask))1541		sony_nc_int_call(sony_nc_acpi_handle, "SN02", &bitmask,1542				&result);1543}1544 1545static int sony_nc_resume(struct device *dev)1546{1547	struct sony_nc_value *item;1548 1549	for (item = sony_nc_values; item->name; item++) {1550		int ret;1551 1552		if (!item->valid)1553			continue;1554		ret = sony_nc_int_call(sony_nc_acpi_handle, *item->acpiset,1555				       &item->value, NULL);1556		if (ret < 0) {1557			pr_err("%s: %d\n", __func__, ret);1558			break;1559		}1560	}1561 1562	if (acpi_has_method(sony_nc_acpi_handle, "ECON")) {1563		int arg = 1;1564		if (sony_nc_int_call(sony_nc_acpi_handle, "ECON", &arg, NULL))1565			dprintk("ECON Method failed\n");1566	}1567 1568	if (acpi_has_method(sony_nc_acpi_handle, "SN00"))1569		sony_nc_function_resume();1570 1571	return 0;1572}1573#endif1574 1575static SIMPLE_DEV_PM_OPS(sony_nc_pm, NULL, sony_nc_resume);1576 1577static void sony_nc_rfkill_cleanup(void)1578{1579	int i;1580 1581	for (i = 0; i < N_SONY_RFKILL; i++) {1582		if (sony_rfkill_devices[i]) {1583			rfkill_unregister(sony_rfkill_devices[i]);1584			rfkill_destroy(sony_rfkill_devices[i]);1585		}1586	}1587}1588 1589static int sony_nc_rfkill_set(void *data, bool blocked)1590{1591	int result;1592	int argument = sony_rfkill_address[(long) data] + 0x100;1593 1594	if (!blocked)1595		argument |= 0x070000;1596 1597	return sony_call_snc_handle(sony_rfkill_handle, argument, &result);1598}1599 1600static const struct rfkill_ops sony_rfkill_ops = {1601	.set_block = sony_nc_rfkill_set,1602};1603 1604static int sony_nc_setup_rfkill(struct acpi_device *device,1605				enum sony_nc_rfkill nc_type)1606{1607	int err;1608	struct rfkill *rfk;1609	enum rfkill_type type;1610	const char *name;1611	int result;1612	bool hwblock, swblock;1613 1614	switch (nc_type) {1615	case SONY_WIFI:1616		type = RFKILL_TYPE_WLAN;1617		name = "sony-wifi";1618		break;1619	case SONY_BLUETOOTH:1620		type = RFKILL_TYPE_BLUETOOTH;1621		name = "sony-bluetooth";1622		break;1623	case SONY_WWAN:1624		type = RFKILL_TYPE_WWAN;1625		name = "sony-wwan";1626		break;1627	case SONY_WIMAX:1628		type = RFKILL_TYPE_WIMAX;1629		name = "sony-wimax";1630		break;1631	default:1632		return -EINVAL;1633	}1634 1635	rfk = rfkill_alloc(name, &device->dev, type,1636			   &sony_rfkill_ops, (void *)nc_type);1637	if (!rfk)1638		return -ENOMEM;1639 1640	err = sony_call_snc_handle(sony_rfkill_handle, 0x200, &result);1641	if (err < 0) {1642		rfkill_destroy(rfk);1643		return err;1644	}1645	hwblock = !(result & 0x1);1646 1647	err = sony_call_snc_handle(sony_rfkill_handle,1648				   sony_rfkill_address[nc_type],1649				   &result);1650	if (err < 0) {1651		rfkill_destroy(rfk);1652		return err;1653	}1654	swblock = !(result & 0x2);1655 1656	rfkill_init_sw_state(rfk, swblock);1657	rfkill_set_hw_state(rfk, hwblock);1658 1659	err = rfkill_register(rfk);1660	if (err) {1661		rfkill_destroy(rfk);1662		return err;1663	}1664	sony_rfkill_devices[nc_type] = rfk;1665	return err;1666}1667 1668static void sony_nc_rfkill_update(void)1669{1670	enum sony_nc_rfkill i;1671	int result;1672	bool hwblock;1673 1674	sony_call_snc_handle(sony_rfkill_handle, 0x200, &result);1675	hwblock = !(result & 0x1);1676 1677	for (i = 0; i < N_SONY_RFKILL; i++) {1678		int argument = sony_rfkill_address[i];1679 1680		if (!sony_rfkill_devices[i])1681			continue;1682 1683		if (hwblock) {1684			if (rfkill_set_hw_state(sony_rfkill_devices[i], true)) {1685				/* we already know we're blocked */1686			}1687			continue;1688		}1689 1690		sony_call_snc_handle(sony_rfkill_handle, argument, &result);1691		rfkill_set_states(sony_rfkill_devices[i],1692				  !(result & 0x2), false);1693	}1694}1695 1696static int sony_nc_rfkill_setup(struct acpi_device *device,1697		unsigned int handle)1698{1699	u64 offset;1700	int i;1701	unsigned char buffer[32] = { 0 };1702 1703	offset = sony_find_snc_handle(handle);1704	sony_rfkill_handle = handle;1705 1706	i = sony_nc_buffer_call(sony_nc_acpi_handle, "SN06", &offset, buffer,1707			32);1708	if (i < 0)1709		return i;1710 1711	/* The buffer is filled with magic numbers describing the devices1712	 * available, 0xff terminates the enumeration.1713	 * Known codes:1714	 *	0x00 WLAN1715	 *	0x10 BLUETOOTH1716	 *	0x20 WWAN GPRS-EDGE1717	 *	0x21 WWAN HSDPA1718	 *	0x22 WWAN EV-DO1719	 *	0x23 WWAN GPS1720	 *	0x25 Gobi WWAN no GPS1721	 *	0x26 Gobi WWAN + GPS1722	 *	0x28 Gobi WWAN no GPS1723	 *	0x29 Gobi WWAN + GPS1724	 *	0x30 WIMAX1725	 *	0x50 Gobi WWAN no GPS1726	 *	0x51 Gobi WWAN + GPS1727	 *	0x70 no SIM card slot1728	 *	0x71 SIM card slot1729	 */1730	for (i = 0; i < ARRAY_SIZE(buffer); i++) {1731 1732		if (buffer[i] == 0xff)1733			break;1734 1735		dprintk("Radio devices, found 0x%.2x\n", buffer[i]);1736 1737		if (buffer[i] == 0 && !sony_rfkill_devices[SONY_WIFI])1738			sony_nc_setup_rfkill(device, SONY_WIFI);1739 1740		if (buffer[i] == 0x10 && !sony_rfkill_devices[SONY_BLUETOOTH])1741			sony_nc_setup_rfkill(device, SONY_BLUETOOTH);1742 1743		if (((0xf0 & buffer[i]) == 0x20 ||1744					(0xf0 & buffer[i]) == 0x50) &&1745				!sony_rfkill_devices[SONY_WWAN])1746			sony_nc_setup_rfkill(device, SONY_WWAN);1747 1748		if (buffer[i] == 0x30 && !sony_rfkill_devices[SONY_WIMAX])1749			sony_nc_setup_rfkill(device, SONY_WIMAX);1750	}1751	return 0;1752}1753 1754/* Keyboard backlight feature */1755struct kbd_backlight {1756	unsigned int handle;1757	unsigned int base;1758	unsigned int mode;1759	unsigned int timeout;1760	unsigned int has_timeout;1761	struct device_attribute mode_attr;1762	struct device_attribute timeout_attr;1763};1764 1765static struct kbd_backlight *kbdbl_ctl;1766 1767static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value)1768{1769	int result;1770 1771	if (value > 2)1772		return -EINVAL;1773 1774	if (sony_call_snc_handle(kbdbl_ctl->handle,1775				(value << 0x10) | (kbdbl_ctl->base), &result))1776		return -EIO;1777 1778	/* Try to turn the light on/off immediately */1779	if (value != 1)1780		sony_call_snc_handle(kbdbl_ctl->handle,1781				(value << 0x0f) | (kbdbl_ctl->base + 0x100),1782				&result);1783 1784	kbdbl_ctl->mode = value;1785 1786	return 0;1787}1788 1789static ssize_t sony_nc_kbd_backlight_mode_store(struct device *dev,1790		struct device_attribute *attr,1791		const char *buffer, size_t count)1792{1793	int ret = 0;1794	unsigned long value;1795 1796	if (count > 31)1797		return -EINVAL;1798 1799	if (kstrtoul(buffer, 10, &value))1800		return -EINVAL;1801 1802	ret = __sony_nc_kbd_backlight_mode_set(value);1803	if (ret < 0)1804		return ret;1805 1806	return count;1807}1808 1809static ssize_t sony_nc_kbd_backlight_mode_show(struct device *dev,1810		struct device_attribute *attr, char *buffer)1811{1812	return sysfs_emit(buffer, "%d\n", kbdbl_ctl->mode);1813}1814 1815static int __sony_nc_kbd_backlight_timeout_set(u8 value)1816{1817	int result;1818 1819	if (value > 3)1820		return -EINVAL;1821 1822	if (sony_call_snc_handle(kbdbl_ctl->handle, (value << 0x10) |1823				(kbdbl_ctl->base + 0x200), &result))1824		return -EIO;1825 1826	kbdbl_ctl->timeout = value;1827 1828	return 0;1829}1830 1831static ssize_t sony_nc_kbd_backlight_timeout_store(struct device *dev,1832		struct device_attribute *attr,1833		const char *buffer, size_t count)1834{1835	int ret = 0;1836	unsigned long value;1837 1838	if (count > 31)1839		return -EINVAL;1840 1841	if (kstrtoul(buffer, 10, &value))1842		return -EINVAL;1843 1844	ret = __sony_nc_kbd_backlight_timeout_set(value);1845	if (ret < 0)1846		return ret;1847 1848	return count;1849}1850 1851static ssize_t sony_nc_kbd_backlight_timeout_show(struct device *dev,1852		struct device_attribute *attr, char *buffer)1853{1854	return sysfs_emit(buffer, "%d\n", kbdbl_ctl->timeout);1855}1856 1857static int sony_nc_kbd_backlight_setup(struct platform_device *pd,1858		unsigned int handle)1859{1860	int result;1861	int probe_base = 0;1862	int ctl_base = 0;1863	int ret = 0;1864 1865	if (kbdbl_ctl) {1866		pr_warn("handle 0x%.4x: keyboard backlight setup already done for 0x%.4x\n",1867				handle, kbdbl_ctl->handle);1868		return -EBUSY;1869	}1870 1871	/* verify the kbd backlight presence, some of these handles are not used1872	 * for keyboard backlight only1873	 */1874	switch (handle) {1875	case 0x0153:1876		probe_base = 0x0;1877		ctl_base = 0x0;1878		break;1879	case 0x0137:1880		probe_base = 0x0B00;1881		ctl_base = 0x0C00;1882		break;1883	default:1884		probe_base = 0x0100;1885		ctl_base = 0x4000;1886		break;1887	}1888 1889	/*1890	 * Only probe if there is a separate probe_base, otherwise the probe call1891	 * is equivalent to __sony_nc_kbd_backlight_mode_set(0), resulting in1892	 * the keyboard backlight being turned off.1893	 */1894	if (probe_base) {1895		ret = sony_call_snc_handle(handle, probe_base, &result);1896		if (ret)1897			return ret;1898 1899		if ((handle == 0x0137 && !(result & 0x02)) ||1900				!(result & 0x01)) {1901			dprintk("no backlight keyboard found\n");1902			return 0;1903		}1904	}1905 1906	kbdbl_ctl = kzalloc(sizeof(*kbdbl_ctl), GFP_KERNEL);1907	if (!kbdbl_ctl)1908		return -ENOMEM;1909 1910	kbdbl_ctl->mode = kbd_backlight;1911	kbdbl_ctl->timeout = kbd_backlight_timeout;1912	kbdbl_ctl->handle = handle;1913	kbdbl_ctl->base = ctl_base;1914	/* Some models do not allow timeout control */1915	kbdbl_ctl->has_timeout = handle != 0x0153;1916 1917	sysfs_attr_init(&kbdbl_ctl->mode_attr.attr);1918	kbdbl_ctl->mode_attr.attr.name = "kbd_backlight";1919	kbdbl_ctl->mode_attr.attr.mode = S_IRUGO | S_IWUSR;1920	kbdbl_ctl->mode_attr.show = sony_nc_kbd_backlight_mode_show;1921	kbdbl_ctl->mode_attr.store = sony_nc_kbd_backlight_mode_store;1922 1923	ret = device_create_file(&pd->dev, &kbdbl_ctl->mode_attr);1924	if (ret)1925		goto outkzalloc;1926 1927	__sony_nc_kbd_backlight_mode_set(kbdbl_ctl->mode);1928 1929	if (kbdbl_ctl->has_timeout) {1930		sysfs_attr_init(&kbdbl_ctl->timeout_attr.attr);1931		kbdbl_ctl->timeout_attr.attr.name = "kbd_backlight_timeout";1932		kbdbl_ctl->timeout_attr.attr.mode = S_IRUGO | S_IWUSR;1933		kbdbl_ctl->timeout_attr.show =1934			sony_nc_kbd_backlight_timeout_show;1935		kbdbl_ctl->timeout_attr.store =1936			sony_nc_kbd_backlight_timeout_store;1937 1938		ret = device_create_file(&pd->dev, &kbdbl_ctl->timeout_attr);1939		if (ret)1940			goto outmode;1941 1942		__sony_nc_kbd_backlight_timeout_set(kbdbl_ctl->timeout);1943	}1944 1945 1946	return 0;1947 1948outmode:1949	device_remove_file(&pd->dev, &kbdbl_ctl->mode_attr);1950outkzalloc:1951	kfree(kbdbl_ctl);1952	kbdbl_ctl = NULL;1953	return ret;1954}1955 1956static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd,1957		unsigned int handle)1958{1959	if (kbdbl_ctl && handle == kbdbl_ctl->handle) {1960		device_remove_file(&pd->dev, &kbdbl_ctl->mode_attr);1961		if (kbdbl_ctl->has_timeout)1962			device_remove_file(&pd->dev, &kbdbl_ctl->timeout_attr);1963		kfree(kbdbl_ctl);1964		kbdbl_ctl = NULL;1965	}1966}1967 1968struct battery_care_control {1969	struct device_attribute attrs[2];1970	unsigned int handle;1971};1972static struct battery_care_control *bcare_ctl;1973 1974static ssize_t sony_nc_battery_care_limit_store(struct device *dev,1975		struct device_attribute *attr,1976		const char *buffer, size_t count)1977{1978	unsigned int result, cmd;1979	unsigned long value;1980 1981	if (count > 31)1982		return -EINVAL;1983 1984	if (kstrtoul(buffer, 10, &value))1985		return -EINVAL;1986 1987	/*  limit values (2 bits):1988	 *  00 - none1989	 *  01 - 80%1990	 *  10 - 50%1991	 *  11 - 100%1992	 *1993	 *  bit 0: 0 disable BCL, 1 enable BCL1994	 *  bit 1: 1 tell to store the battery limit (see bits 6,7) too1995	 *  bits 2,3: reserved1996	 *  bits 4,5: store the limit into the EC1997	 *  bits 6,7: store the limit into the battery1998	 */1999	cmd = 0;2000 2001	if (value > 0) {2002		if (value <= 50)2003			cmd = 0x20;2004 2005		else if (value <= 80)2006			cmd = 0x10;2007 2008		else if (value <= 100)2009			cmd = 0x30;2010 2011		else2012			return -EINVAL;2013 2014		/*2015		 * handle 0x0115 should allow storing on battery too;2016		 * handle 0x0136 same as 0x0115 + health status;2017		 * handle 0x013f, same as 0x0136 but no storing on the battery2018		 */2019		if (bcare_ctl->handle != 0x013f)2020			cmd = cmd | (cmd << 2);2021 2022		cmd = (cmd | 0x1) << 0x10;2023	}2024 2025	if (sony_call_snc_handle(bcare_ctl->handle, cmd | 0x0100, &result))2026		return -EIO;2027 2028	return count;2029}2030 2031static ssize_t sony_nc_battery_care_limit_show(struct device *dev,2032		struct device_attribute *attr, char *buffer)2033{2034	unsigned int result, status;2035 2036	if (sony_call_snc_handle(bcare_ctl->handle, 0x0000, &result))2037		return -EIO;2038 2039	status = (result & 0x01) ? ((result & 0x30) >> 0x04) : 0;2040	switch (status) {2041	case 1:2042		status = 80;2043		break;2044	case 2:2045		status = 50;2046		break;2047	case 3:2048		status = 100;2049		break;2050	default:2051		status = 0;2052		break;2053	}2054 2055	return sysfs_emit(buffer, "%d\n", status);2056}2057 2058static ssize_t sony_nc_battery_care_health_show(struct device *dev,2059		struct device_attribute *attr, char *buffer)2060{2061	unsigned int health;2062 2063	if (sony_call_snc_handle(bcare_ctl->handle, 0x0200, &health))2064		return -EIO;2065 2066	return sysfs_emit(buffer, "%d\n", health & 0xff);2067}2068 2069static int sony_nc_battery_care_setup(struct platform_device *pd,2070		unsigned int handle)2071{2072	int ret = 0;2073 2074	bcare_ctl = kzalloc(sizeof(struct battery_care_control), GFP_KERNEL);2075	if (!bcare_ctl)2076		return -ENOMEM;2077 2078	bcare_ctl->handle = handle;2079 2080	sysfs_attr_init(&bcare_ctl->attrs[0].attr);2081	bcare_ctl->attrs[0].attr.name = "battery_care_limiter";2082	bcare_ctl->attrs[0].attr.mode = S_IRUGO | S_IWUSR;2083	bcare_ctl->attrs[0].show = sony_nc_battery_care_limit_show;2084	bcare_ctl->attrs[0].store = sony_nc_battery_care_limit_store;2085 2086	ret = device_create_file(&pd->dev, &bcare_ctl->attrs[0]);2087	if (ret)2088		goto outkzalloc;2089 2090	/* 0x0115 is for models with no health reporting capability */2091	if (handle == 0x0115)2092		return 0;2093 2094	sysfs_attr_init(&bcare_ctl->attrs[1].attr);2095	bcare_ctl->attrs[1].attr.name = "battery_care_health";2096	bcare_ctl->attrs[1].attr.mode = S_IRUGO;2097	bcare_ctl->attrs[1].show = sony_nc_battery_care_health_show;2098 2099	ret = device_create_file(&pd->dev, &bcare_ctl->attrs[1]);2100	if (ret)2101		goto outlimiter;2102 2103	return 0;2104 2105outlimiter:2106	device_remove_file(&pd->dev, &bcare_ctl->attrs[0]);2107 2108outkzalloc:2109	kfree(bcare_ctl);2110	bcare_ctl = NULL;2111 2112	return ret;2113}2114 2115static void sony_nc_battery_care_cleanup(struct platform_device *pd)2116{2117	if (bcare_ctl) {2118		device_remove_file(&pd->dev, &bcare_ctl->attrs[0]);2119		if (bcare_ctl->handle != 0x0115)2120			device_remove_file(&pd->dev, &bcare_ctl->attrs[1]);2121 2122		kfree(bcare_ctl);2123		bcare_ctl = NULL;2124	}2125}2126 2127struct snc_thermal_ctrl {2128	unsigned int mode;2129	unsigned int profiles;2130	struct device_attribute mode_attr;2131	struct device_attribute profiles_attr;2132};2133static struct snc_thermal_ctrl *th_handle;2134 2135#define THM_PROFILE_MAX 32136static const char * const snc_thermal_profiles[] = {2137	"balanced",2138	"silent",2139	"performance"2140};2141 2142static int sony_nc_thermal_mode_set(unsigned short mode)2143{2144	unsigned int result;2145 2146	/* the thermal profile seems to be a two bit bitmask:2147	 * lsb -> silent2148	 * msb -> performance2149	 * no bit set is the normal operation and is always valid2150	 * Some vaio models only have "balanced" and "performance"2151	 */2152	if ((mode && !(th_handle->profiles & mode)) || mode >= THM_PROFILE_MAX)2153		return -EINVAL;2154 2155	if (sony_call_snc_handle(0x0122, mode << 0x10 | 0x0200, &result))2156		return -EIO;2157 2158	th_handle->mode = mode;2159 2160	return 0;2161}2162 2163static int sony_nc_thermal_mode_get(void)2164{2165	unsigned int result;2166 2167	if (sony_call_snc_handle(0x0122, 0x0100, &result))2168		return -EIO;2169 2170	return result & 0xff;2171}2172 2173static ssize_t sony_nc_thermal_profiles_show(struct device *dev,2174		struct device_attribute *attr, char *buffer)2175{2176	short cnt;2177	size_t idx = 0;2178 2179	for (cnt = 0; cnt < THM_PROFILE_MAX; cnt++) {2180		if (!cnt || (th_handle->profiles & cnt))2181			idx += sysfs_emit_at(buffer, idx, "%s ", snc_thermal_profiles[cnt]);2182	}2183	idx += sysfs_emit_at(buffer, idx, "\n");2184 2185	return idx;2186}2187 2188static ssize_t sony_nc_thermal_mode_store(struct device *dev,2189		struct device_attribute *attr,2190		const char *buffer, size_t count)2191{2192	unsigned short cmd;2193	size_t len = count;2194 2195	if (count == 0)2196		return -EINVAL;2197 2198	/* skip the newline if present */2199	if (buffer[len - 1] == '\n')2200		len--;2201 2202	for (cmd = 0; cmd < THM_PROFILE_MAX; cmd++)2203		if (strncmp(buffer, snc_thermal_profiles[cmd], len) == 0)2204			break;2205 2206	if (sony_nc_thermal_mode_set(cmd))2207		return -EIO;2208 2209	return count;2210}2211 2212static ssize_t sony_nc_thermal_mode_show(struct device *dev,2213		struct device_attribute *attr, char *buffer)2214{2215	int mode = sony_nc_thermal_mode_get();2216 2217	if (mode < 0)2218		return mode;2219 2220	return sysfs_emit(buffer, "%s\n", snc_thermal_profiles[mode]);2221}2222 2223static int sony_nc_thermal_setup(struct platform_device *pd)2224{2225	int ret = 0;2226	th_handle = kzalloc(sizeof(struct snc_thermal_ctrl), GFP_KERNEL);2227	if (!th_handle)2228		return -ENOMEM;2229 2230	ret = sony_call_snc_handle(0x0122, 0x0000, &th_handle->profiles);2231	if (ret) {2232		pr_warn("couldn't to read the thermal profiles\n");2233		goto outkzalloc;2234	}2235 2236	ret = sony_nc_thermal_mode_get();2237	if (ret < 0) {2238		pr_warn("couldn't to read the current thermal profile");2239		goto outkzalloc;2240	}2241	th_handle->mode = ret;2242 2243	sysfs_attr_init(&th_handle->profiles_attr.attr);2244	th_handle->profiles_attr.attr.name = "thermal_profiles";2245	th_handle->profiles_attr.attr.mode = S_IRUGO;2246	th_handle->profiles_attr.show = sony_nc_thermal_profiles_show;2247 2248	sysfs_attr_init(&th_handle->mode_attr.attr);2249	th_handle->mode_attr.attr.name = "thermal_control";2250	th_handle->mode_attr.attr.mode = S_IRUGO | S_IWUSR;2251	th_handle->mode_attr.show = sony_nc_thermal_mode_show;2252	th_handle->mode_attr.store = sony_nc_thermal_mode_store;2253 2254	ret = device_create_file(&pd->dev, &th_handle->profiles_attr);2255	if (ret)2256		goto outkzalloc;2257 2258	ret = device_create_file(&pd->dev, &th_handle->mode_attr);2259	if (ret)2260		goto outprofiles;2261 2262	return 0;2263 2264outprofiles:2265	device_remove_file(&pd->dev, &th_handle->profiles_attr);2266outkzalloc:2267	kfree(th_handle);2268	th_handle = NULL;2269	return ret;2270}2271 2272static void sony_nc_thermal_cleanup(struct platform_device *pd)2273{2274	if (th_handle) {2275		device_remove_file(&pd->dev, &th_handle->profiles_attr);2276		device_remove_file(&pd->dev, &th_handle->mode_attr);2277		kfree(th_handle);2278		th_handle = NULL;2279	}2280}2281 2282#ifdef CONFIG_PM_SLEEP2283static void sony_nc_thermal_resume(void)2284{2285	int status;2286 2287	if (!th_handle)2288		return;2289 2290	status = sony_nc_thermal_mode_get();2291 2292	if (status != th_handle->mode)2293		sony_nc_thermal_mode_set(th_handle->mode);2294}2295#endif2296 2297/* resume on LID open */2298#define LID_RESUME_S5	02299#define LID_RESUME_S4	12300#define LID_RESUME_S3	22301#define LID_RESUME_MAX	32302struct snc_lid_resume_control {2303	struct device_attribute attrs[LID_RESUME_MAX];2304	unsigned int status;2305	int handle;2306};2307static struct snc_lid_resume_control *lid_ctl;2308 2309static ssize_t sony_nc_lid_resume_store(struct device *dev,2310					struct device_attribute *attr,2311					const char *buffer, size_t count)2312{2313	unsigned int result;2314	unsigned long value;2315	unsigned int pos = LID_RESUME_S5;2316	if (count > 31)2317		return -EINVAL;2318 2319	if (kstrtoul(buffer, 10, &value) || value > 1)2320		return -EINVAL;2321 2322	/* the value we have to write to SNC is a bitmask:2323	 * +--------------+2324	 * | S3 | S4 | S5 |2325	 * +--------------+2326	 *   2    1    02327	 */2328	while (pos < LID_RESUME_MAX) {2329		if (&lid_ctl->attrs[pos].attr == &attr->attr)2330			break;2331		pos++;2332	}2333	if (pos == LID_RESUME_MAX)2334		return -EINVAL;2335 2336	if (value)2337		value = lid_ctl->status | (1 << pos);2338	else2339		value = lid_ctl->status & ~(1 << pos);2340 2341	if (sony_call_snc_handle(lid_ctl->handle, value << 0x10 | 0x0100,2342				&result))2343		return -EIO;2344 2345	lid_ctl->status = value;2346 2347	return count;2348}2349 2350static ssize_t sony_nc_lid_resume_show(struct device *dev,2351					struct device_attribute *attr,2352					char *buffer)2353{2354	unsigned int pos = LID_RESUME_S5;2355 2356	while (pos < LID_RESUME_MAX) {2357		if (&lid_ctl->attrs[pos].attr == &attr->attr)2358			return sysfs_emit(buffer, "%d\n",2359					(lid_ctl->status >> pos) & 0x01);2360		pos++;2361	}2362	return -EINVAL;2363}2364 2365static int sony_nc_lid_resume_setup(struct platform_device *pd,2366					unsigned int handle)2367{2368	unsigned int result;2369	int i;2370 2371	if (sony_call_snc_handle(handle, 0x0000, &result))2372		return -EIO;2373 2374	lid_ctl = kzalloc(sizeof(struct snc_lid_resume_control), GFP_KERNEL);2375	if (!lid_ctl)2376		return -ENOMEM;2377 2378	lid_ctl->status = result & 0x7;2379	lid_ctl->handle = handle;2380 2381	sysfs_attr_init(&lid_ctl->attrs[0].attr);2382	lid_ctl->attrs[LID_RESUME_S5].attr.name = "lid_resume_S5";2383	lid_ctl->attrs[LID_RESUME_S5].attr.mode = S_IRUGO | S_IWUSR;2384	lid_ctl->attrs[LID_RESUME_S5].show = sony_nc_lid_resume_show;2385	lid_ctl->attrs[LID_RESUME_S5].store = sony_nc_lid_resume_store;2386 2387	if (handle == 0x0119) {2388		sysfs_attr_init(&lid_ctl->attrs[1].attr);2389		lid_ctl->attrs[LID_RESUME_S4].attr.name = "lid_resume_S4";2390		lid_ctl->attrs[LID_RESUME_S4].attr.mode = S_IRUGO | S_IWUSR;2391		lid_ctl->attrs[LID_RESUME_S4].show = sony_nc_lid_resume_show;2392		lid_ctl->attrs[LID_RESUME_S4].store = sony_nc_lid_resume_store;2393 2394		sysfs_attr_init(&lid_ctl->attrs[2].attr);2395		lid_ctl->attrs[LID_RESUME_S3].attr.name = "lid_resume_S3";2396		lid_ctl->attrs[LID_RESUME_S3].attr.mode = S_IRUGO | S_IWUSR;2397		lid_ctl->attrs[LID_RESUME_S3].show = sony_nc_lid_resume_show;2398		lid_ctl->attrs[LID_RESUME_S3].store = sony_nc_lid_resume_store;2399	}2400	for (i = 0; i < LID_RESUME_MAX &&2401			lid_ctl->attrs[i].attr.name; i++) {2402		result = device_create_file(&pd->dev, &lid_ctl->attrs[i]);2403		if (result)2404			goto liderror;2405	}2406 2407	return 0;2408 2409liderror:2410	for (i--; i >= 0; i--)2411		device_remove_file(&pd->dev, &lid_ctl->attrs[i]);2412 2413	kfree(lid_ctl);2414	lid_ctl = NULL;2415 2416	return result;2417}2418 2419static void sony_nc_lid_resume_cleanup(struct platform_device *pd)2420{2421	int i;2422 2423	if (lid_ctl) {2424		for (i = 0; i < LID_RESUME_MAX; i++) {2425			if (!lid_ctl->attrs[i].attr.name)2426				break;2427 2428			device_remove_file(&pd->dev, &lid_ctl->attrs[i]);2429		}2430 2431		kfree(lid_ctl);2432		lid_ctl = NULL;2433	}2434}2435 2436/* GFX Switch position */2437enum gfx_switch {2438	SPEED,2439	STAMINA,2440	AUTO2441};2442struct snc_gfx_switch_control {2443	struct device_attribute attr;2444	unsigned int handle;2445};2446static struct snc_gfx_switch_control *gfxs_ctl;2447 2448/* returns 0 for speed, 1 for stamina */2449static int __sony_nc_gfx_switch_status_get(void)2450{2451	unsigned int result;2452 2453	if (sony_call_snc_handle(gfxs_ctl->handle,2454				gfxs_ctl->handle == 0x015B ? 0x0000 : 0x0100,2455				&result))2456		return -EIO;2457 2458	switch (gfxs_ctl->handle) {2459	case 0x0146:2460		/* 1: discrete GFX (speed)2461		 * 0: integrated GFX (stamina)2462		 */2463		return result & 0x1 ? SPEED : STAMINA;2464	case 0x015B:2465		/* 0: discrete GFX (speed)2466		 * 1: integrated GFX (stamina)2467		 */2468		return result & 0x1 ? STAMINA : SPEED;2469	case 0x0128:2470		/* it's a more elaborated bitmask, for now:2471		 * 2: integrated GFX (stamina)2472		 * 0: discrete GFX (speed)2473		 */2474		dprintk("GFX Status: 0x%x\n", result);2475		return result & 0x80 ? AUTO :2476			result & 0x02 ? STAMINA : SPEED;2477	}2478	return -EINVAL;2479}2480 2481static ssize_t sony_nc_gfx_switch_status_show(struct device *dev,2482				       struct device_attribute *attr,2483				       char *buffer)2484{2485	int pos = __sony_nc_gfx_switch_status_get();2486 2487	if (pos < 0)2488		return pos;2489 2490	return sysfs_emit(buffer, "%s\n",2491					pos == SPEED ? "speed" :2492					pos == STAMINA ? "stamina" :2493					pos == AUTO ? "auto" : "unknown");2494}2495 2496static int sony_nc_gfx_switch_setup(struct platform_device *pd,2497		unsigned int handle)2498{2499	unsigned int result;2500 2501	gfxs_ctl = kzalloc(sizeof(struct snc_gfx_switch_control), GFP_KERNEL);2502	if (!gfxs_ctl)2503		return -ENOMEM;2504 2505	gfxs_ctl->handle = handle;2506 2507	sysfs_attr_init(&gfxs_ctl->attr.attr);2508	gfxs_ctl->attr.attr.name = "gfx_switch_status";2509	gfxs_ctl->attr.attr.mode = S_IRUGO;2510	gfxs_ctl->attr.show = sony_nc_gfx_switch_status_show;2511 2512	result = device_create_file(&pd->dev, &gfxs_ctl->attr);2513	if (result)2514		goto gfxerror;2515 2516	return 0;2517 2518gfxerror:2519	kfree(gfxs_ctl);2520	gfxs_ctl = NULL;2521 2522	return result;2523}2524 2525static void sony_nc_gfx_switch_cleanup(struct platform_device *pd)2526{2527	if (gfxs_ctl) {2528		device_remove_file(&pd->dev, &gfxs_ctl->attr);2529 2530		kfree(gfxs_ctl);2531		gfxs_ctl = NULL;2532	}2533}2534 2535/* High speed charging function */2536static struct device_attribute *hsc_handle;2537 2538static ssize_t sony_nc_highspeed_charging_store(struct device *dev,2539		struct device_attribute *attr,2540		const char *buffer, size_t count)2541{2542	unsigned int result;2543	unsigned long value;2544 2545	if (count > 31)2546		return -EINVAL;2547 2548	if (kstrtoul(buffer, 10, &value) || value > 1)2549		return -EINVAL;2550 2551	if (sony_call_snc_handle(0x0131, value << 0x10 | 0x0200, &result))2552		return -EIO;2553 2554	return count;2555}2556 2557static ssize_t sony_nc_highspeed_charging_show(struct device *dev,2558		struct device_attribute *attr, char *buffer)2559{2560	unsigned int result;2561 2562	if (sony_call_snc_handle(0x0131, 0x0100, &result))2563		return -EIO;2564 2565	return sysfs_emit(buffer, "%d\n", result & 0x01);2566}2567 2568static int sony_nc_highspeed_charging_setup(struct platform_device *pd)2569{2570	unsigned int result;2571 2572	if (sony_call_snc_handle(0x0131, 0x0000, &result) || !(result & 0x01)) {2573		/* some models advertise the handle but have no implementation2574		 * for it2575		 */2576		pr_info("No High Speed Charging capability found\n");2577		return 0;2578	}2579 2580	hsc_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);2581	if (!hsc_handle)2582		return -ENOMEM;2583 2584	sysfs_attr_init(&hsc_handle->attr);2585	hsc_handle->attr.name = "battery_highspeed_charging";2586	hsc_handle->attr.mode = S_IRUGO | S_IWUSR;2587	hsc_handle->show = sony_nc_highspeed_charging_show;2588	hsc_handle->store = sony_nc_highspeed_charging_store;2589 2590	result = device_create_file(&pd->dev, hsc_handle);2591	if (result) {2592		kfree(hsc_handle);2593		hsc_handle = NULL;2594		return result;2595	}2596 2597	return 0;2598}2599 2600static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd)2601{2602	if (hsc_handle) {2603		device_remove_file(&pd->dev, hsc_handle);2604		kfree(hsc_handle);2605		hsc_handle = NULL;2606	}2607}2608 2609/* low battery function */2610static struct device_attribute *lowbatt_handle;2611 2612static ssize_t sony_nc_lowbatt_store(struct device *dev,2613		struct device_attribute *attr,2614		const char *buffer, size_t count)2615{2616	unsigned int result;2617	unsigned long value;2618 2619	if (count > 31)2620		return -EINVAL;2621 2622	if (kstrtoul(buffer, 10, &value) || value > 1)2623		return -EINVAL;2624 2625	if (sony_call_snc_handle(0x0121, value << 8, &result))2626		return -EIO;2627 2628	return count;2629}2630 2631static ssize_t sony_nc_lowbatt_show(struct device *dev,2632		struct device_attribute *attr, char *buffer)2633{2634	unsigned int result;2635 2636	if (sony_call_snc_handle(0x0121, 0x0200, &result))2637		return -EIO;2638 2639	return sysfs_emit(buffer, "%d\n", result & 1);2640}2641 2642static int sony_nc_lowbatt_setup(struct platform_device *pd)2643{2644	unsigned int result;2645 2646	lowbatt_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);2647	if (!lowbatt_handle)2648		return -ENOMEM;2649 2650	sysfs_attr_init(&lowbatt_handle->attr);2651	lowbatt_handle->attr.name = "lowbatt_hibernate";2652	lowbatt_handle->attr.mode = S_IRUGO | S_IWUSR;2653	lowbatt_handle->show = sony_nc_lowbatt_show;2654	lowbatt_handle->store = sony_nc_lowbatt_store;2655 2656	result = device_create_file(&pd->dev, lowbatt_handle);2657	if (result) {2658		kfree(lowbatt_handle);2659		lowbatt_handle = NULL;2660		return result;2661	}2662 2663	return 0;2664}2665 2666static void sony_nc_lowbatt_cleanup(struct platform_device *pd)2667{2668	if (lowbatt_handle) {2669		device_remove_file(&pd->dev, lowbatt_handle);2670		kfree(lowbatt_handle);2671		lowbatt_handle = NULL;2672	}2673}2674 2675/* fan speed function */2676static struct device_attribute *fan_handle, *hsf_handle;2677 2678static ssize_t sony_nc_hsfan_store(struct device *dev,2679		struct device_attribute *attr,2680		const char *buffer, size_t count)2681{2682	unsigned int result;2683	unsigned long value;2684 2685	if (count > 31)2686		return -EINVAL;2687 2688	if (kstrtoul(buffer, 10, &value) || value > 1)2689		return -EINVAL;2690 2691	if (sony_call_snc_handle(0x0149, value << 0x10 | 0x0200, &result))2692		return -EIO;2693 2694	return count;2695}2696 2697static ssize_t sony_nc_hsfan_show(struct device *dev,2698		struct device_attribute *attr, char *buffer)2699{2700	unsigned int result;2701 2702	if (sony_call_snc_handle(0x0149, 0x0100, &result))2703		return -EIO;2704 2705	return sysfs_emit(buffer, "%d\n", result & 0x01);2706}2707 2708static ssize_t sony_nc_fanspeed_show(struct device *dev,2709		struct device_attribute *attr, char *buffer)2710{2711	unsigned int result;2712 2713	if (sony_call_snc_handle(0x0149, 0x0300, &result))2714		return -EIO;2715 2716	return sysfs_emit(buffer, "%d\n", result & 0xff);2717}2718 2719static int sony_nc_fanspeed_setup(struct platform_device *pd)2720{2721	unsigned int result;2722 2723	fan_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);2724	if (!fan_handle)2725		return -ENOMEM;2726 2727	hsf_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);2728	if (!hsf_handle) {2729		result = -ENOMEM;2730		goto out_hsf_handle_alloc;2731	}2732 2733	sysfs_attr_init(&fan_handle->attr);2734	fan_handle->attr.name = "fanspeed";2735	fan_handle->attr.mode = S_IRUGO;2736	fan_handle->show = sony_nc_fanspeed_show;2737	fan_handle->store = NULL;2738 2739	sysfs_attr_init(&hsf_handle->attr);2740	hsf_handle->attr.name = "fan_forced";2741	hsf_handle->attr.mode = S_IRUGO | S_IWUSR;2742	hsf_handle->show = sony_nc_hsfan_show;2743	hsf_handle->store = sony_nc_hsfan_store;2744 2745	result = device_create_file(&pd->dev, fan_handle);2746	if (result)2747		goto out_fan_handle;2748 2749	result = device_create_file(&pd->dev, hsf_handle);2750	if (result)2751		goto out_hsf_handle;2752 2753	return 0;2754 2755out_hsf_handle:2756	device_remove_file(&pd->dev, fan_handle);2757 2758out_fan_handle:2759	kfree(hsf_handle);2760	hsf_handle = NULL;2761 2762out_hsf_handle_alloc:2763	kfree(fan_handle);2764	fan_handle = NULL;2765	return result;2766}2767 2768static void sony_nc_fanspeed_cleanup(struct platform_device *pd)2769{2770	if (fan_handle) {2771		device_remove_file(&pd->dev, fan_handle);2772		kfree(fan_handle);2773		fan_handle = NULL;2774	}2775	if (hsf_handle) {2776		device_remove_file(&pd->dev, hsf_handle);2777		kfree(hsf_handle);2778		hsf_handle = NULL;2779	}2780}2781 2782/* USB charge function */2783static struct device_attribute *uc_handle;2784 2785static ssize_t sony_nc_usb_charge_store(struct device *dev,2786		struct device_attribute *attr,2787		const char *buffer, size_t count)2788{2789	unsigned int result;2790	unsigned long value;2791 2792	if (count > 31)2793		return -EINVAL;2794 2795	if (kstrtoul(buffer, 10, &value) || value > 1)2796		return -EINVAL;2797 2798	if (sony_call_snc_handle(0x0155, value << 0x10 | 0x0100, &result))2799		return -EIO;2800 2801	return count;2802}2803 2804static ssize_t sony_nc_usb_charge_show(struct device *dev,2805		struct device_attribute *attr, char *buffer)2806{2807	unsigned int result;2808 2809	if (sony_call_snc_handle(0x0155, 0x0000, &result))2810		return -EIO;2811 2812	return sysfs_emit(buffer, "%d\n", result & 0x01);2813}2814 2815static int sony_nc_usb_charge_setup(struct platform_device *pd)2816{2817	unsigned int result;2818 2819	if (sony_call_snc_handle(0x0155, 0x0000, &result) || !(result & 0x01)) {2820		/* some models advertise the handle but have no implementation2821		 * for it2822		 */2823		pr_info("No USB Charge capability found\n");2824		return 0;2825	}2826 2827	uc_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);2828	if (!uc_handle)2829		return -ENOMEM;2830 2831	sysfs_attr_init(&uc_handle->attr);2832	uc_handle->attr.name = "usb_charge";2833	uc_handle->attr.mode = S_IRUGO | S_IWUSR;2834	uc_handle->show = sony_nc_usb_charge_show;2835	uc_handle->store = sony_nc_usb_charge_store;2836 2837	result = device_create_file(&pd->dev, uc_handle);2838	if (result) {2839		kfree(uc_handle);2840		uc_handle = NULL;2841		return result;2842	}2843 2844	return 0;2845}2846 2847static void sony_nc_usb_charge_cleanup(struct platform_device *pd)2848{2849	if (uc_handle) {2850		device_remove_file(&pd->dev, uc_handle);2851		kfree(uc_handle);2852		uc_handle = NULL;2853	}2854}2855 2856/* Panel ID function */2857static struct device_attribute *panel_handle;2858 2859static ssize_t sony_nc_panelid_show(struct device *dev,2860		struct device_attribute *attr, char *buffer)2861{2862	unsigned int result;2863 2864	if (sony_call_snc_handle(0x011D, 0x0000, &result))2865		return -EIO;2866 2867	return sysfs_emit(buffer, "%d\n", result);2868}2869 2870static int sony_nc_panelid_setup(struct platform_device *pd)2871{2872	unsigned int result;2873 2874	panel_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);2875	if (!panel_handle)2876		return -ENOMEM;2877 2878	sysfs_attr_init(&panel_handle->attr);2879	panel_handle->attr.name = "panel_id";2880	panel_handle->attr.mode = S_IRUGO;2881	panel_handle->show = sony_nc_panelid_show;2882	panel_handle->store = NULL;2883 2884	result = device_create_file(&pd->dev, panel_handle);2885	if (result) {2886		kfree(panel_handle);2887		panel_handle = NULL;2888		return result;2889	}2890 2891	return 0;2892}2893 2894static void sony_nc_panelid_cleanup(struct platform_device *pd)2895{2896	if (panel_handle) {2897		device_remove_file(&pd->dev, panel_handle);2898		kfree(panel_handle);2899		panel_handle = NULL;2900	}2901}2902 2903/* smart connect function */2904static struct device_attribute *sc_handle;2905 2906static ssize_t sony_nc_smart_conn_store(struct device *dev,2907		struct device_attribute *attr,2908		const char *buffer, size_t count)2909{2910	unsigned int result;2911	unsigned long value;2912 2913	if (count > 31)2914		return -EINVAL;2915 2916	if (kstrtoul(buffer, 10, &value) || value > 1)2917		return -EINVAL;2918 2919	if (sony_call_snc_handle(0x0168, value << 0x10, &result))2920		return -EIO;2921 2922	return count;2923}2924 2925static int sony_nc_smart_conn_setup(struct platform_device *pd)2926{2927	unsigned int result;2928 2929	sc_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);2930	if (!sc_handle)2931		return -ENOMEM;2932 2933	sysfs_attr_init(&sc_handle->attr);2934	sc_handle->attr.name = "smart_connect";2935	sc_handle->attr.mode = S_IWUSR;2936	sc_handle->show = NULL;2937	sc_handle->store = sony_nc_smart_conn_store;2938 2939	result = device_create_file(&pd->dev, sc_handle);2940	if (result) {2941		kfree(sc_handle);2942		sc_handle = NULL;2943		return result;2944	}2945 2946	return 0;2947}2948 2949static void sony_nc_smart_conn_cleanup(struct platform_device *pd)2950{2951	if (sc_handle) {2952		device_remove_file(&pd->dev, sc_handle);2953		kfree(sc_handle);2954		sc_handle = NULL;2955	}2956}2957 2958/* Touchpad enable/disable */2959struct touchpad_control {2960	struct device_attribute attr;2961	int handle;2962};2963static struct touchpad_control *tp_ctl;2964 2965static ssize_t sony_nc_touchpad_store(struct device *dev,2966		struct device_attribute *attr, const char *buffer, size_t count)2967{2968	unsigned int result;2969	unsigned long value;2970 2971	if (count > 31)2972		return -EINVAL;2973 2974	if (kstrtoul(buffer, 10, &value) || value > 1)2975		return -EINVAL;2976 2977	/* sysfs: 0 disabled, 1 enabled2978	 * EC: 0 enabled, 1 disabled2979	 */2980	if (sony_call_snc_handle(tp_ctl->handle,2981				(!value << 0x10) | 0x100, &result))2982		return -EIO;2983 2984	return count;2985}2986 2987static ssize_t sony_nc_touchpad_show(struct device *dev,2988		struct device_attribute *attr, char *buffer)2989{2990	unsigned int result;2991 2992	if (sony_call_snc_handle(tp_ctl->handle, 0x000, &result))2993		return -EINVAL;2994 2995	return sysfs_emit(buffer, "%d\n", !(result & 0x01));2996}2997 2998static int sony_nc_touchpad_setup(struct platform_device *pd,2999		unsigned int handle)3000{3001	int ret = 0;3002 3003	tp_ctl = kzalloc(sizeof(struct touchpad_control), GFP_KERNEL);3004	if (!tp_ctl)3005		return -ENOMEM;3006 3007	tp_ctl->handle = handle;3008 3009	sysfs_attr_init(&tp_ctl->attr.attr);3010	tp_ctl->attr.attr.name = "touchpad";3011	tp_ctl->attr.attr.mode = S_IRUGO | S_IWUSR;3012	tp_ctl->attr.show = sony_nc_touchpad_show;3013	tp_ctl->attr.store = sony_nc_touchpad_store;3014 3015	ret = device_create_file(&pd->dev, &tp_ctl->attr);3016	if (ret) {3017		kfree(tp_ctl);3018		tp_ctl = NULL;3019	}3020 3021	return ret;3022}3023 3024static void sony_nc_touchpad_cleanup(struct platform_device *pd)3025{3026	if (tp_ctl) {3027		device_remove_file(&pd->dev, &tp_ctl->attr);3028		kfree(tp_ctl);3029		tp_ctl = NULL;3030	}3031}3032 3033static void sony_nc_backlight_ng_read_limits(int handle,3034		struct sony_backlight_props *props)3035{3036	u64 offset;3037	int i;3038	int lvl_table_len = 0;3039	u8 min = 0xff, max = 0x00;3040	unsigned char buffer[32] = { 0 };3041 3042	props->handle = handle;3043	props->offset = 0;3044	props->maxlvl = 0xff;3045 3046	offset = sony_find_snc_handle(handle);3047 3048	/* try to read the boundaries from ACPI tables, if we fail the above3049	 * defaults should be reasonable3050	 */3051	i = sony_nc_buffer_call(sony_nc_acpi_handle, "SN06", &offset, buffer,3052			32);3053	if (i < 0)3054		return;3055 3056	switch (handle) {3057	case 0x012f:3058	case 0x0137:3059		lvl_table_len = 9;3060		break;3061	case 0x143:3062	case 0x14b:3063	case 0x14c:3064		lvl_table_len = 16;3065		break;3066	}3067 3068	/* the buffer lists brightness levels available, brightness levels are3069	 * from position 0 to 8 in the array, other values are used by ALS3070	 * control.3071	 */3072	for (i = 0; i < lvl_table_len && i < ARRAY_SIZE(buffer); i++) {3073 3074		dprintk("Brightness level: %d\n", buffer[i]);3075 3076		if (!buffer[i])3077			break;3078 3079		if (buffer[i] > max)3080			max = buffer[i];3081		if (buffer[i] < min)3082			min = buffer[i];3083	}3084	props->offset = min;3085	props->maxlvl = max;3086	dprintk("Brightness levels: min=%d max=%d\n", props->offset,3087			props->maxlvl);3088}3089 3090static void sony_nc_backlight_setup(void)3091{3092	int max_brightness = 0;3093	const struct backlight_ops *ops = NULL;3094	struct backlight_properties props;3095 3096	if (sony_find_snc_handle(0x12f) >= 0) {3097		ops = &sony_backlight_ng_ops;3098		sony_bl_props.cmd_base = 0x0100;3099		sony_nc_backlight_ng_read_limits(0x12f, &sony_bl_props);3100		max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;3101 3102	} else if (sony_find_snc_handle(0x137) >= 0) {3103		ops = &sony_backlight_ng_ops;3104		sony_bl_props.cmd_base = 0x0100;3105		sony_nc_backlight_ng_read_limits(0x137, &sony_bl_props);3106		max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;3107 3108	} else if (sony_find_snc_handle(0x143) >= 0) {3109		ops = &sony_backlight_ng_ops;3110		sony_bl_props.cmd_base = 0x3000;3111		sony_nc_backlight_ng_read_limits(0x143, &sony_bl_props);3112		max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;3113 3114	} else if (sony_find_snc_handle(0x14b) >= 0) {3115		ops = &sony_backlight_ng_ops;3116		sony_bl_props.cmd_base = 0x3000;3117		sony_nc_backlight_ng_read_limits(0x14b, &sony_bl_props);3118		max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;3119 3120	} else if (sony_find_snc_handle(0x14c) >= 0) {3121		ops = &sony_backlight_ng_ops;3122		sony_bl_props.cmd_base = 0x3000;3123		sony_nc_backlight_ng_read_limits(0x14c, &sony_bl_props);3124		max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;3125 3126	} else if (acpi_has_method(sony_nc_acpi_handle, "GBRT")) {3127		ops = &sony_backlight_ops;3128		max_brightness = SONY_MAX_BRIGHTNESS - 1;3129 3130	} else3131		return;3132 3133	memset(&props, 0, sizeof(struct backlight_properties));3134	props.type = BACKLIGHT_PLATFORM;3135	props.max_brightness = max_brightness;3136	sony_bl_props.dev = backlight_device_register("sony", NULL,3137						      &sony_bl_props,3138						      ops, &props);3139 3140	if (IS_ERR(sony_bl_props.dev)) {3141		pr_warn("unable to register backlight device\n");3142		sony_bl_props.dev = NULL;3143	} else3144		sony_bl_props.dev->props.brightness =3145			ops->get_brightness(sony_bl_props.dev);3146}3147 3148static void sony_nc_backlight_cleanup(void)3149{3150	backlight_device_unregister(sony_bl_props.dev);3151}3152 3153static int sony_nc_add(struct acpi_device *device)3154{3155	acpi_status status;3156	int result = 0;3157	struct sony_nc_value *item;3158 3159	sony_nc_acpi_device = device;3160	strcpy(acpi_device_class(device), "sony/hotkey");3161 3162	sony_nc_acpi_handle = device->handle;3163 3164	/* read device status */3165	result = acpi_bus_get_status(device);3166	/* bail IFF the above call was successful and the device is not present */3167	if (!result && !device->status.present) {3168		dprintk("Device not present\n");3169		result = -ENODEV;3170		goto outwalk;3171	}3172 3173	result = sony_pf_add();3174	if (result)3175		goto outpresent;3176 3177	if (debug) {3178		status = acpi_walk_namespace(ACPI_TYPE_METHOD,3179				sony_nc_acpi_handle, 1, sony_walk_callback,3180				NULL, NULL, NULL);3181		if (ACPI_FAILURE(status)) {3182			pr_warn("unable to walk acpi resources\n");3183			result = -ENODEV;3184			goto outpresent;3185		}3186	}3187 3188	result = sony_laptop_setup_input(device);3189	if (result) {3190		pr_err("Unable to create input devices\n");3191		goto outplatform;3192	}3193 3194	if (acpi_has_method(sony_nc_acpi_handle, "ECON")) {3195		int arg = 1;3196		if (sony_nc_int_call(sony_nc_acpi_handle, "ECON", &arg, NULL))3197			dprintk("ECON Method failed\n");3198	}3199 3200	if (acpi_has_method(sony_nc_acpi_handle, "SN00")) {3201		dprintk("Doing SNC setup\n");3202		/* retrieve the available handles */3203		result = sony_nc_handles_setup(sony_pf_device);3204		if (!result)3205			sony_nc_function_setup(device, sony_pf_device);3206	}3207 3208	if (acpi_video_get_backlight_type() == acpi_backlight_vendor)3209		sony_nc_backlight_setup();3210 3211	/* create sony_pf sysfs attributes related to the SNC device */3212	for (item = sony_nc_values; item->name; ++item) {3213 3214		if (!debug && item->debug)3215			continue;3216 3217		/* find the available acpiget as described in the DSDT */3218		for (; item->acpiget && *item->acpiget; ++item->acpiget) {3219			if (acpi_has_method(sony_nc_acpi_handle,3220							*item->acpiget)) {3221				dprintk("Found %s getter: %s\n",3222						item->name, *item->acpiget);3223				item->devattr.attr.mode |= S_IRUGO;3224				break;3225			}3226		}3227 3228		/* find the available acpiset as described in the DSDT */3229		for (; item->acpiset && *item->acpiset; ++item->acpiset) {3230			if (acpi_has_method(sony_nc_acpi_handle,3231							*item->acpiset)) {3232				dprintk("Found %s setter: %s\n",3233						item->name, *item->acpiset);3234				item->devattr.attr.mode |= S_IWUSR;3235				break;3236			}3237		}3238 3239		if (item->devattr.attr.mode != 0) {3240			result =3241			    device_create_file(&sony_pf_device->dev,3242					       &item->devattr);3243			if (result)3244				goto out_sysfs;3245		}3246	}3247 3248	pr_info("SNC setup done.\n");3249	return 0;3250 3251out_sysfs:3252	for (item = sony_nc_values; item->name; ++item) {3253		device_remove_file(&sony_pf_device->dev, &item->devattr);3254	}3255	sony_nc_backlight_cleanup();3256	sony_nc_function_cleanup(sony_pf_device);3257	sony_nc_handles_cleanup(sony_pf_device);3258 3259outplatform:3260	sony_laptop_remove_input();3261 3262outpresent:3263	sony_pf_remove();3264 3265outwalk:3266	sony_nc_rfkill_cleanup();3267	return result;3268}3269 3270static void sony_nc_remove(struct acpi_device *device)3271{3272	struct sony_nc_value *item;3273 3274	sony_nc_backlight_cleanup();3275 3276	sony_nc_acpi_device = NULL;3277 3278	for (item = sony_nc_values; item->name; ++item) {3279		device_remove_file(&sony_pf_device->dev, &item->devattr);3280	}3281 3282	sony_nc_function_cleanup(sony_pf_device);3283	sony_nc_handles_cleanup(sony_pf_device);3284	sony_pf_remove();3285	sony_laptop_remove_input();3286	dprintk(SONY_NC_DRIVER_NAME " removed.\n");3287}3288 3289static const struct acpi_device_id sony_device_ids[] __maybe_unused = {3290	{SONY_NC_HID, 0},3291	{SONY_PIC_HID, 0},3292	{"", 0},3293};3294MODULE_DEVICE_TABLE(acpi, sony_device_ids);3295 3296static const struct acpi_device_id sony_nc_device_ids[] = {3297	{SONY_NC_HID, 0},3298	{"", 0},3299};3300 3301static struct acpi_driver sony_nc_driver = {3302	.name = SONY_NC_DRIVER_NAME,3303	.class = SONY_NC_CLASS,3304	.ids = sony_nc_device_ids,3305	.ops = {3306		.add = sony_nc_add,3307		.remove = sony_nc_remove,3308		.notify = sony_nc_notify,3309		},3310	.drv.pm = &sony_nc_pm,3311};3312 3313/*********** SPIC (SNY6001) Device ***********/3314 3315#define SONYPI_DEVICE_TYPE1	0x000000013316#define SONYPI_DEVICE_TYPE2	0x000000023317#define SONYPI_DEVICE_TYPE3	0x000000043318 3319#define SONYPI_TYPE1_OFFSET	0x043320#define SONYPI_TYPE2_OFFSET	0x123321#define SONYPI_TYPE3_OFFSET	0x123322 3323struct sony_pic_ioport {3324	struct acpi_resource_io	io1;3325	struct acpi_resource_io	io2;3326	struct list_head	list;3327};3328 3329struct sony_pic_irq {3330	struct acpi_resource_irq	irq;3331	struct list_head		list;3332};3333 3334struct sonypi_eventtypes {3335	u8			data;3336	unsigned long		mask;3337	struct sonypi_event	*events;3338};3339 3340struct sony_pic_dev {3341	struct acpi_device		*acpi_dev;3342	struct sony_pic_irq		*cur_irq;3343	struct sony_pic_ioport		*cur_ioport;3344	struct list_head		interrupts;3345	struct list_head		ioports;3346	struct mutex			lock;3347	struct sonypi_eventtypes	*event_types;3348	int                             (*handle_irq)(const u8, const u8);3349	int				model;3350	u16				evport_offset;3351	u8				camera_power;3352	u8				bluetooth_power;3353	u8				wwan_power;3354};3355 3356static struct sony_pic_dev spic_dev = {3357	.interrupts	= LIST_HEAD_INIT(spic_dev.interrupts),3358	.ioports	= LIST_HEAD_INIT(spic_dev.ioports),3359};3360 3361static int spic_drv_registered;3362 3363/* Event masks */3364#define SONYPI_JOGGER_MASK			0x000000013365#define SONYPI_CAPTURE_MASK			0x000000023366#define SONYPI_FNKEY_MASK			0x000000043367#define SONYPI_BLUETOOTH_MASK			0x000000083368#define SONYPI_PKEY_MASK			0x000000103369#define SONYPI_BACK_MASK			0x000000203370#define SONYPI_HELP_MASK			0x000000403371#define SONYPI_LID_MASK				0x000000803372#define SONYPI_ZOOM_MASK			0x000001003373#define SONYPI_THUMBPHRASE_MASK			0x000002003374#define SONYPI_MEYE_MASK			0x000004003375#define SONYPI_MEMORYSTICK_MASK			0x000008003376#define SONYPI_BATTERY_MASK			0x000010003377#define SONYPI_WIRELESS_MASK			0x000020003378 3379struct sonypi_event {3380	u8	data;3381	u8	event;3382};3383 3384/* The set of possible button release events */3385static struct sonypi_event sonypi_releaseev[] = {3386	{ 0x00, SONYPI_EVENT_ANYBUTTON_RELEASED },3387	{ 0, 0 }3388};3389 3390/* The set of possible jogger events  */3391static struct sonypi_event sonypi_joggerev[] = {3392	{ 0x1f, SONYPI_EVENT_JOGDIAL_UP },3393	{ 0x01, SONYPI_EVENT_JOGDIAL_DOWN },3394	{ 0x5f, SONYPI_EVENT_JOGDIAL_UP_PRESSED },3395	{ 0x41, SONYPI_EVENT_JOGDIAL_DOWN_PRESSED },3396	{ 0x1e, SONYPI_EVENT_JOGDIAL_FAST_UP },3397	{ 0x02, SONYPI_EVENT_JOGDIAL_FAST_DOWN },3398	{ 0x5e, SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED },3399	{ 0x42, SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED },3400	{ 0x1d, SONYPI_EVENT_JOGDIAL_VFAST_UP },3401	{ 0x03, SONYPI_EVENT_JOGDIAL_VFAST_DOWN },3402	{ 0x5d, SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED },3403	{ 0x43, SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED },3404	{ 0x40, SONYPI_EVENT_JOGDIAL_PRESSED },3405	{ 0, 0 }3406};3407 3408/* The set of possible capture button events */3409static struct sonypi_event sonypi_captureev[] = {3410	{ 0x05, SONYPI_EVENT_CAPTURE_PARTIALPRESSED },3411	{ 0x07, SONYPI_EVENT_CAPTURE_PRESSED },3412	{ 0x40, SONYPI_EVENT_CAPTURE_PRESSED },3413	{ 0x01, SONYPI_EVENT_CAPTURE_PARTIALRELEASED },3414	{ 0, 0 }3415};3416 3417/* The set of possible fnkeys events */3418static struct sonypi_event sonypi_fnkeyev[] = {3419	{ 0x10, SONYPI_EVENT_FNKEY_ESC },3420	{ 0x11, SONYPI_EVENT_FNKEY_F1 },3421	{ 0x12, SONYPI_EVENT_FNKEY_F2 },3422	{ 0x13, SONYPI_EVENT_FNKEY_F3 },3423	{ 0x14, SONYPI_EVENT_FNKEY_F4 },3424	{ 0x15, SONYPI_EVENT_FNKEY_F5 },3425	{ 0x16, SONYPI_EVENT_FNKEY_F6 },3426	{ 0x17, SONYPI_EVENT_FNKEY_F7 },3427	{ 0x18, SONYPI_EVENT_FNKEY_F8 },3428	{ 0x19, SONYPI_EVENT_FNKEY_F9 },3429	{ 0x1a, SONYPI_EVENT_FNKEY_F10 },3430	{ 0x1b, SONYPI_EVENT_FNKEY_F11 },3431	{ 0x1c, SONYPI_EVENT_FNKEY_F12 },3432	{ 0x1f, SONYPI_EVENT_FNKEY_RELEASED },3433	{ 0x21, SONYPI_EVENT_FNKEY_1 },3434	{ 0x22, SONYPI_EVENT_FNKEY_2 },3435	{ 0x31, SONYPI_EVENT_FNKEY_D },3436	{ 0x32, SONYPI_EVENT_FNKEY_E },3437	{ 0x33, SONYPI_EVENT_FNKEY_F },3438	{ 0x34, SONYPI_EVENT_FNKEY_S },3439	{ 0x35, SONYPI_EVENT_FNKEY_B },3440	{ 0x36, SONYPI_EVENT_FNKEY_ONLY },3441	{ 0, 0 }3442};3443 3444/* The set of possible program key events */3445static struct sonypi_event sonypi_pkeyev[] = {3446	{ 0x01, SONYPI_EVENT_PKEY_P1 },3447	{ 0x02, SONYPI_EVENT_PKEY_P2 },3448	{ 0x04, SONYPI_EVENT_PKEY_P3 },3449	{ 0x20, SONYPI_EVENT_PKEY_P1 },3450	{ 0, 0 }3451};3452 3453/* The set of possible bluetooth events */3454static struct sonypi_event sonypi_blueev[] = {3455	{ 0x55, SONYPI_EVENT_BLUETOOTH_PRESSED },3456	{ 0x59, SONYPI_EVENT_BLUETOOTH_ON },3457	{ 0x5a, SONYPI_EVENT_BLUETOOTH_OFF },3458	{ 0, 0 }3459};3460 3461/* The set of possible wireless events */3462static struct sonypi_event sonypi_wlessev[] = {3463	{ 0x59, SONYPI_EVENT_IGNORE },3464	{ 0x5a, SONYPI_EVENT_IGNORE },3465	{ 0, 0 }3466};3467 3468/* The set of possible back button events */3469static struct sonypi_event sonypi_backev[] = {3470	{ 0x20, SONYPI_EVENT_BACK_PRESSED },3471	{ 0, 0 }3472};3473 3474/* The set of possible help button events */3475static struct sonypi_event sonypi_helpev[] = {3476	{ 0x3b, SONYPI_EVENT_HELP_PRESSED },3477	{ 0, 0 }3478};3479 3480 3481/* The set of possible lid events */3482static struct sonypi_event sonypi_lidev[] = {3483	{ 0x51, SONYPI_EVENT_LID_CLOSED },3484	{ 0x50, SONYPI_EVENT_LID_OPENED },3485	{ 0, 0 }3486};3487 3488/* The set of possible zoom events */3489static struct sonypi_event sonypi_zoomev[] = {3490	{ 0x39, SONYPI_EVENT_ZOOM_PRESSED },3491	{ 0x10, SONYPI_EVENT_ZOOM_IN_PRESSED },3492	{ 0x20, SONYPI_EVENT_ZOOM_OUT_PRESSED },3493	{ 0x04, SONYPI_EVENT_ZOOM_PRESSED },3494	{ 0, 0 }3495};3496 3497/* The set of possible thumbphrase events */3498static struct sonypi_event sonypi_thumbphraseev[] = {3499	{ 0x3a, SONYPI_EVENT_THUMBPHRASE_PRESSED },3500	{ 0, 0 }3501};3502 3503/* The set of possible motioneye camera events */3504static struct sonypi_event sonypi_meyeev[] = {3505	{ 0x00, SONYPI_EVENT_MEYE_FACE },3506	{ 0x01, SONYPI_EVENT_MEYE_OPPOSITE },3507	{ 0, 0 }3508};3509 3510/* The set of possible memorystick events */3511static struct sonypi_event sonypi_memorystickev[] = {3512	{ 0x53, SONYPI_EVENT_MEMORYSTICK_INSERT },3513	{ 0x54, SONYPI_EVENT_MEMORYSTICK_EJECT },3514	{ 0, 0 }3515};3516 3517/* The set of possible battery events */3518static struct sonypi_event sonypi_batteryev[] = {3519	{ 0x20, SONYPI_EVENT_BATTERY_INSERT },3520	{ 0x30, SONYPI_EVENT_BATTERY_REMOVE },3521	{ 0, 0 }3522};3523 3524/* The set of possible volume events */3525static struct sonypi_event sonypi_volumeev[] = {3526	{ 0x01, SONYPI_EVENT_VOLUME_INC_PRESSED },3527	{ 0x02, SONYPI_EVENT_VOLUME_DEC_PRESSED },3528	{ 0, 0 }3529};3530 3531/* The set of possible brightness events */3532static struct sonypi_event sonypi_brightnessev[] = {3533	{ 0x80, SONYPI_EVENT_BRIGHTNESS_PRESSED },3534	{ 0, 0 }3535};3536 3537static struct sonypi_eventtypes type1_events[] = {3538	{ 0, 0xffffffff, sonypi_releaseev },3539	{ 0x70, SONYPI_MEYE_MASK, sonypi_meyeev },3540	{ 0x30, SONYPI_LID_MASK, sonypi_lidev },3541	{ 0x60, SONYPI_CAPTURE_MASK, sonypi_captureev },3542	{ 0x10, SONYPI_JOGGER_MASK, sonypi_joggerev },3543	{ 0x20, SONYPI_FNKEY_MASK, sonypi_fnkeyev },3544	{ 0x30, SONYPI_BLUETOOTH_MASK, sonypi_blueev },3545	{ 0x40, SONYPI_PKEY_MASK, sonypi_pkeyev },3546	{ 0x30, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },3547	{ 0x40, SONYPI_BATTERY_MASK, sonypi_batteryev },3548	{ 0 },3549};3550static struct sonypi_eventtypes type2_events[] = {3551	{ 0, 0xffffffff, sonypi_releaseev },3552	{ 0x38, SONYPI_LID_MASK, sonypi_lidev },3553	{ 0x11, SONYPI_JOGGER_MASK, sonypi_joggerev },3554	{ 0x61, SONYPI_CAPTURE_MASK, sonypi_captureev },3555	{ 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },3556	{ 0x31, SONYPI_BLUETOOTH_MASK, sonypi_blueev },3557	{ 0x08, SONYPI_PKEY_MASK, sonypi_pkeyev },3558	{ 0x11, SONYPI_BACK_MASK, sonypi_backev },3559	{ 0x21, SONYPI_HELP_MASK, sonypi_helpev },3560	{ 0x21, SONYPI_ZOOM_MASK, sonypi_zoomev },3561	{ 0x20, SONYPI_THUMBPHRASE_MASK, sonypi_thumbphraseev },3562	{ 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },3563	{ 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },3564	{ 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev },3565	{ 0 },3566};3567static struct sonypi_eventtypes type3_events[] = {3568	{ 0, 0xffffffff, sonypi_releaseev },3569	{ 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },3570	{ 0x31, SONYPI_WIRELESS_MASK, sonypi_wlessev },3571	{ 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },3572	{ 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },3573	{ 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev },3574	{ 0x05, SONYPI_PKEY_MASK, sonypi_pkeyev },3575	{ 0x05, SONYPI_ZOOM_MASK, sonypi_zoomev },3576	{ 0x05, SONYPI_CAPTURE_MASK, sonypi_captureev },3577	{ 0x05, SONYPI_PKEY_MASK, sonypi_volumeev },3578	{ 0x05, SONYPI_PKEY_MASK, sonypi_brightnessev },3579	{ 0 },3580};3581 3582/* low level spic calls */3583#define ITERATIONS_LONG		100003584#define ITERATIONS_SHORT	103585#define wait_on_command(command, iterations) {				\3586	unsigned int n = iterations;					\3587	while (--n && (command))					\3588		udelay(1);						\3589	if (!n)								\3590		dprintk("command failed at %s : %s (line %d)\n",	\3591				__FILE__, __func__, __LINE__);	\3592}3593 3594static u8 sony_pic_call1(u8 dev)3595{3596	u8 v1, v2;3597 3598	wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2,3599			ITERATIONS_LONG);3600	outb(dev, spic_dev.cur_ioport->io1.minimum + 4);3601	v1 = inb_p(spic_dev.cur_ioport->io1.minimum + 4);3602	v2 = inb_p(spic_dev.cur_ioport->io1.minimum);3603	dprintk("sony_pic_call1(0x%.2x): 0x%.4x\n", dev, (v2 << 8) | v1);3604	return v2;3605}3606 3607static u8 sony_pic_call2(u8 dev, u8 fn)3608{3609	u8 v1;3610 3611	wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2,3612			ITERATIONS_LONG);3613	outb(dev, spic_dev.cur_ioport->io1.minimum + 4);3614	wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2,3615			ITERATIONS_LONG);3616	outb(fn, spic_dev.cur_ioport->io1.minimum);3617	v1 = inb_p(spic_dev.cur_ioport->io1.minimum);3618	dprintk("sony_pic_call2(0x%.2x - 0x%.2x): 0x%.4x\n", dev, fn, v1);3619	return v1;3620}3621 3622static u8 sony_pic_call3(u8 dev, u8 fn, u8 v)3623{3624	u8 v1;3625 3626	wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG);3627	outb(dev, spic_dev.cur_ioport->io1.minimum + 4);3628	wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG);3629	outb(fn, spic_dev.cur_ioport->io1.minimum);3630	wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG);3631	outb(v, spic_dev.cur_ioport->io1.minimum);3632	v1 = inb_p(spic_dev.cur_ioport->io1.minimum);3633	dprintk("sony_pic_call3(0x%.2x - 0x%.2x - 0x%.2x): 0x%.4x\n",3634			dev, fn, v, v1);3635	return v1;3636}3637 3638/*3639 * minidrivers for SPIC models3640 */3641static int type3_handle_irq(const u8 data_mask, const u8 ev)3642{3643	/*3644	 * 0x31 could mean we have to take some extra action and wait for3645	 * the next irq for some Type3 models, it will generate a new3646	 * irq and we can read new data from the device:3647	 *  - 0x5c and 0x5f requires 0xA03648	 *  - 0x61 requires 0xB33649	 */3650	if (data_mask == 0x31) {3651		if (ev == 0x5c || ev == 0x5f)3652			sony_pic_call1(0xA0);3653		else if (ev == 0x61)3654			sony_pic_call1(0xB3);3655		return 0;3656	}3657	return 1;3658}3659 3660static void sony_pic_detect_device_type(struct sony_pic_dev *dev)3661{3662	struct pci_dev *pcidev;3663 3664	pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,3665			PCI_DEVICE_ID_INTEL_82371AB_3, NULL);3666	if (pcidev) {3667		dev->model = SONYPI_DEVICE_TYPE1;3668		dev->evport_offset = SONYPI_TYPE1_OFFSET;3669		dev->event_types = type1_events;3670		goto out;3671	}3672 3673	pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,3674			PCI_DEVICE_ID_INTEL_ICH6_1, NULL);3675	if (pcidev) {3676		dev->model = SONYPI_DEVICE_TYPE2;3677		dev->evport_offset = SONYPI_TYPE2_OFFSET;3678		dev->event_types = type2_events;3679		goto out;3680	}3681 3682	pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,3683			PCI_DEVICE_ID_INTEL_ICH7_1, NULL);3684	if (pcidev) {3685		dev->model = SONYPI_DEVICE_TYPE3;3686		dev->handle_irq = type3_handle_irq;3687		dev->evport_offset = SONYPI_TYPE3_OFFSET;3688		dev->event_types = type3_events;3689		goto out;3690	}3691 3692	pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,3693			PCI_DEVICE_ID_INTEL_ICH8_4, NULL);3694	if (pcidev) {3695		dev->model = SONYPI_DEVICE_TYPE3;3696		dev->handle_irq = type3_handle_irq;3697		dev->evport_offset = SONYPI_TYPE3_OFFSET;3698		dev->event_types = type3_events;3699		goto out;3700	}3701 3702	pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,3703			PCI_DEVICE_ID_INTEL_ICH9_1, NULL);3704	if (pcidev) {3705		dev->model = SONYPI_DEVICE_TYPE3;3706		dev->handle_irq = type3_handle_irq;3707		dev->evport_offset = SONYPI_TYPE3_OFFSET;3708		dev->event_types = type3_events;3709		goto out;3710	}3711 3712	/* default */3713	dev->model = SONYPI_DEVICE_TYPE2;3714	dev->evport_offset = SONYPI_TYPE2_OFFSET;3715	dev->event_types = type2_events;3716 3717out:3718	pci_dev_put(pcidev);3719 3720	pr_info("detected Type%d model\n",3721		dev->model == SONYPI_DEVICE_TYPE1 ? 1 :3722		dev->model == SONYPI_DEVICE_TYPE2 ? 2 : 3);3723}3724 3725/* camera tests and poweron/poweroff */3726#define SONYPI_CAMERA_PICTURE		53727#define SONYPI_CAMERA_CONTROL		0x103728 3729#define SONYPI_CAMERA_BRIGHTNESS		03730#define SONYPI_CAMERA_CONTRAST			13731#define SONYPI_CAMERA_HUE			23732#define SONYPI_CAMERA_COLOR			33733#define SONYPI_CAMERA_SHARPNESS			43734 3735#define SONYPI_CAMERA_EXPOSURE_MASK		0xC3736#define SONYPI_CAMERA_WHITE_BALANCE_MASK	0x33737#define SONYPI_CAMERA_PICTURE_MODE_MASK		0x303738#define SONYPI_CAMERA_MUTE_MASK			0x403739 3740/* the rest don't need a loop until not 0xff */3741#define SONYPI_CAMERA_AGC			63742#define SONYPI_CAMERA_AGC_MASK			0x303743#define SONYPI_CAMERA_SHUTTER_MASK 		0x73744 3745#define SONYPI_CAMERA_SHUTDOWN_REQUEST		73746#define SONYPI_CAMERA_CONTROL			0x103747 3748#define SONYPI_CAMERA_STATUS 			73749#define SONYPI_CAMERA_STATUS_READY 		0x23750#define SONYPI_CAMERA_STATUS_POSITION		0x43751 3752#define SONYPI_DIRECTION_BACKWARDS 		0x43753 3754#define SONYPI_CAMERA_REVISION 			83755#define SONYPI_CAMERA_ROMVERSION 		93756 3757static int __sony_pic_camera_ready(void)3758{3759	u8 v;3760 3761	v = sony_pic_call2(0x8f, SONYPI_CAMERA_STATUS);3762	return (v != 0xff && (v & SONYPI_CAMERA_STATUS_READY));3763}3764 3765static int __sony_pic_camera_off(void)3766{3767	if (!camera) {3768		pr_warn("camera control not enabled\n");3769		return -ENODEV;3770	}3771 3772	wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE,3773				SONYPI_CAMERA_MUTE_MASK),3774			ITERATIONS_SHORT);3775 3776	if (spic_dev.camera_power) {3777		sony_pic_call2(0x91, 0);3778		spic_dev.camera_power = 0;3779	}3780	return 0;3781}3782 3783static int __sony_pic_camera_on(void)3784{3785	int i, j, x;3786 3787	if (!camera) {3788		pr_warn("camera control not enabled\n");3789		return -ENODEV;3790	}3791 3792	if (spic_dev.camera_power)3793		return 0;3794 3795	for (j = 5; j > 0; j--) {3796 3797		for (x = 0; x < 100 && sony_pic_call2(0x91, 0x1); x++)3798			msleep(10);3799		sony_pic_call1(0x93);3800 3801		for (i = 400; i > 0; i--) {3802			if (__sony_pic_camera_ready())3803				break;3804			msleep(10);3805		}3806		if (i)3807			break;3808	}3809 3810	if (j == 0) {3811		pr_warn("failed to power on camera\n");3812		return -ENODEV;3813	}3814 3815	wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTROL,3816				0x5a),3817			ITERATIONS_SHORT);3818 3819	spic_dev.camera_power = 1;3820	return 0;3821}3822 3823/* External camera command (exported to the motion eye v4l driver) */3824int sony_pic_camera_command(int command, u8 value)3825{3826	if (!camera)3827		return -EIO;3828 3829	mutex_lock(&spic_dev.lock);3830 3831	switch (command) {3832	case SONY_PIC_COMMAND_SETCAMERA:3833		if (value)3834			__sony_pic_camera_on();3835		else3836			__sony_pic_camera_off();3837		break;3838	case SONY_PIC_COMMAND_SETCAMERABRIGHTNESS:3839		wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_BRIGHTNESS, value),3840				ITERATIONS_SHORT);3841		break;3842	case SONY_PIC_COMMAND_SETCAMERACONTRAST:3843		wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTRAST, value),3844				ITERATIONS_SHORT);3845		break;3846	case SONY_PIC_COMMAND_SETCAMERAHUE:3847		wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_HUE, value),3848				ITERATIONS_SHORT);3849		break;3850	case SONY_PIC_COMMAND_SETCAMERACOLOR:3851		wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_COLOR, value),3852				ITERATIONS_SHORT);3853		break;3854	case SONY_PIC_COMMAND_SETCAMERASHARPNESS:3855		wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_SHARPNESS, value),3856				ITERATIONS_SHORT);3857		break;3858	case SONY_PIC_COMMAND_SETCAMERAPICTURE:3859		wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE, value),3860				ITERATIONS_SHORT);3861		break;3862	case SONY_PIC_COMMAND_SETCAMERAAGC:3863		wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_AGC, value),3864				ITERATIONS_SHORT);3865		break;3866	default:3867		pr_err("sony_pic_camera_command invalid: %d\n", command);3868		break;3869	}3870	mutex_unlock(&spic_dev.lock);3871	return 0;3872}3873EXPORT_SYMBOL(sony_pic_camera_command);3874 3875/* gprs/edge modem (SZ460N and SZ210P), thanks to Joshua Wise */3876static void __sony_pic_set_wwanpower(u8 state)3877{3878	state = !!state;3879	if (spic_dev.wwan_power == state)3880		return;3881	sony_pic_call2(0xB0, state);3882	sony_pic_call1(0x82);3883	spic_dev.wwan_power = state;3884}3885 3886static ssize_t sony_pic_wwanpower_store(struct device *dev,3887		struct device_attribute *attr,3888		const char *buffer, size_t count)3889{3890	unsigned long value;3891	if (count > 31)3892		return -EINVAL;3893 3894	if (kstrtoul(buffer, 10, &value))3895		return -EINVAL;3896 3897	mutex_lock(&spic_dev.lock);3898	__sony_pic_set_wwanpower(value);3899	mutex_unlock(&spic_dev.lock);3900 3901	return count;3902}3903 3904static ssize_t sony_pic_wwanpower_show(struct device *dev,3905		struct device_attribute *attr, char *buffer)3906{3907	ssize_t count;3908	mutex_lock(&spic_dev.lock);3909	count = sysfs_emit(buffer, "%d\n", spic_dev.wwan_power);3910	mutex_unlock(&spic_dev.lock);3911	return count;3912}3913 3914/* bluetooth subsystem power state */3915static void __sony_pic_set_bluetoothpower(u8 state)3916{3917	state = !!state;3918	if (spic_dev.bluetooth_power == state)3919		return;3920	sony_pic_call2(0x96, state);3921	sony_pic_call1(0x82);3922	spic_dev.bluetooth_power = state;3923}3924 3925static ssize_t sony_pic_bluetoothpower_store(struct device *dev,3926		struct device_attribute *attr,3927		const char *buffer, size_t count)3928{3929	unsigned long value;3930	if (count > 31)3931		return -EINVAL;3932 3933	if (kstrtoul(buffer, 10, &value))3934		return -EINVAL;3935 3936	mutex_lock(&spic_dev.lock);3937	__sony_pic_set_bluetoothpower(value);3938	mutex_unlock(&spic_dev.lock);3939 3940	return count;3941}3942 3943static ssize_t sony_pic_bluetoothpower_show(struct device *dev,3944		struct device_attribute *attr, char *buffer)3945{3946	ssize_t count = 0;3947	mutex_lock(&spic_dev.lock);3948	count = sysfs_emit(buffer, "%d\n", spic_dev.bluetooth_power);3949	mutex_unlock(&spic_dev.lock);3950	return count;3951}3952 3953/* fan speed */3954/* FAN0 information (reverse engineered from ACPI tables) */3955#define SONY_PIC_FAN0_STATUS	0x933956static int sony_pic_set_fanspeed(unsigned long value)3957{3958	return ec_write(SONY_PIC_FAN0_STATUS, value);3959}3960 3961static int sony_pic_get_fanspeed(u8 *value)3962{3963	return ec_read(SONY_PIC_FAN0_STATUS, value);3964}3965 3966static ssize_t sony_pic_fanspeed_store(struct device *dev,3967		struct device_attribute *attr,3968		const char *buffer, size_t count)3969{3970	unsigned long value;3971	if (count > 31)3972		return -EINVAL;3973 3974	if (kstrtoul(buffer, 10, &value))3975		return -EINVAL;3976 3977	if (sony_pic_set_fanspeed(value))3978		return -EIO;3979 3980	return count;3981}3982 3983static ssize_t sony_pic_fanspeed_show(struct device *dev,3984		struct device_attribute *attr, char *buffer)3985{3986	u8 value = 0;3987	if (sony_pic_get_fanspeed(&value))3988		return -EIO;3989 3990	return sysfs_emit(buffer, "%d\n", value);3991}3992 3993#define SPIC_ATTR(_name, _mode)					\3994struct device_attribute spic_attr_##_name = __ATTR(_name,	\3995		_mode, sony_pic_## _name ##_show,		\3996		sony_pic_## _name ##_store)3997 3998static SPIC_ATTR(bluetoothpower, 0644);3999static SPIC_ATTR(wwanpower, 0644);4000static SPIC_ATTR(fanspeed, 0644);4001 4002static struct attribute *spic_attributes[] = {4003	&spic_attr_bluetoothpower.attr,4004	&spic_attr_wwanpower.attr,4005	&spic_attr_fanspeed.attr,4006	NULL4007};4008 4009static const struct attribute_group spic_attribute_group = {4010	.attrs = spic_attributes4011};4012 4013/******** SONYPI compatibility **********/4014#ifdef CONFIG_SONYPI_COMPAT4015 4016/* battery / brightness / temperature  addresses */4017#define SONYPI_BAT_FLAGS	0x814018#define SONYPI_LCD_LIGHT	0x964019#define SONYPI_BAT1_PCTRM	0xa04020#define SONYPI_BAT1_LEFT	0xa24021#define SONYPI_BAT1_MAXRT	0xa44022#define SONYPI_BAT2_PCTRM	0xa84023#define SONYPI_BAT2_LEFT	0xaa4024#define SONYPI_BAT2_MAXRT	0xac4025#define SONYPI_BAT1_MAXTK	0xb04026#define SONYPI_BAT1_FULL	0xb24027#define SONYPI_BAT2_MAXTK	0xb84028#define SONYPI_BAT2_FULL	0xba4029#define SONYPI_TEMP_STATUS	0xC14030 4031struct sonypi_compat_s {4032	struct fasync_struct	*fifo_async;4033	struct kfifo		fifo;4034	spinlock_t		fifo_lock;4035	wait_queue_head_t	fifo_proc_list;4036	atomic_t		open_count;4037};4038static struct sonypi_compat_s sonypi_compat = {4039	.open_count = ATOMIC_INIT(0),4040};4041 4042static int sonypi_misc_fasync(int fd, struct file *filp, int on)4043{4044	return fasync_helper(fd, filp, on, &sonypi_compat.fifo_async);4045}4046 4047static int sonypi_misc_release(struct inode *inode, struct file *file)4048{4049	atomic_dec(&sonypi_compat.open_count);4050	return 0;4051}4052 4053static int sonypi_misc_open(struct inode *inode, struct file *file)4054{4055	/* Flush input queue on first open */4056	unsigned long flags;4057 4058	spin_lock_irqsave(&sonypi_compat.fifo_lock, flags);4059 4060	if (atomic_inc_return(&sonypi_compat.open_count) == 1)4061		kfifo_reset(&sonypi_compat.fifo);4062 4063	spin_unlock_irqrestore(&sonypi_compat.fifo_lock, flags);4064 4065	return 0;4066}4067 4068static ssize_t sonypi_misc_read(struct file *file, char __user *buf,4069				size_t count, loff_t *pos)4070{4071	ssize_t ret;4072	unsigned char c;4073 4074	if ((kfifo_len(&sonypi_compat.fifo) == 0) &&4075	    (file->f_flags & O_NONBLOCK))4076		return -EAGAIN;4077 4078	ret = wait_event_interruptible(sonypi_compat.fifo_proc_list,4079				       kfifo_len(&sonypi_compat.fifo) != 0);4080	if (ret)4081		return ret;4082 4083	while (ret < count &&4084	       (kfifo_out_locked(&sonypi_compat.fifo, &c, sizeof(c),4085			  &sonypi_compat.fifo_lock) == sizeof(c))) {4086		if (put_user(c, buf++))4087			return -EFAULT;4088		ret++;4089	}4090 4091	if (ret > 0) {4092		struct inode *inode = file_inode(file);4093		inode_set_atime_to_ts(inode, current_time(inode));4094	}4095 4096	return ret;4097}4098 4099static __poll_t sonypi_misc_poll(struct file *file, poll_table *wait)4100{4101	poll_wait(file, &sonypi_compat.fifo_proc_list, wait);4102	if (kfifo_len(&sonypi_compat.fifo))4103		return EPOLLIN | EPOLLRDNORM;4104	return 0;4105}4106 4107static int ec_read16(u8 addr, u16 *value)4108{4109	u8 val_lb, val_hb;4110	if (ec_read(addr, &val_lb))4111		return -1;4112	if (ec_read(addr + 1, &val_hb))4113		return -1;4114	*value = val_lb | (val_hb << 8);4115	return 0;4116}4117 4118static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd,4119							unsigned long arg)4120{4121	int ret = 0;4122	void __user *argp = (void __user *)arg;4123	u8 val8;4124	u16 val16;4125	int value;4126 4127	mutex_lock(&spic_dev.lock);4128	switch (cmd) {4129	case SONYPI_IOCGBRT:4130		if (sony_bl_props.dev == NULL) {4131			ret = -EIO;4132			break;4133		}4134		if (sony_nc_int_call(sony_nc_acpi_handle, "GBRT", NULL,4135					&value)) {4136			ret = -EIO;4137			break;4138		}4139		val8 = ((value & 0xff) - 1) << 5;4140		if (copy_to_user(argp, &val8, sizeof(val8)))4141				ret = -EFAULT;4142		break;4143	case SONYPI_IOCSBRT:4144		if (sony_bl_props.dev == NULL) {4145			ret = -EIO;4146			break;4147		}4148		if (copy_from_user(&val8, argp, sizeof(val8))) {4149			ret = -EFAULT;4150			break;4151		}4152		value = (val8 >> 5) + 1;4153		if (sony_nc_int_call(sony_nc_acpi_handle, "SBRT", &value,4154					NULL)) {4155			ret = -EIO;4156			break;4157		}4158		/* sync the backlight device status */4159		sony_bl_props.dev->props.brightness =4160		    sony_backlight_get_brightness(sony_bl_props.dev);4161		break;4162	case SONYPI_IOCGBAT1CAP:4163		if (ec_read16(SONYPI_BAT1_FULL, &val16)) {4164			ret = -EIO;4165			break;4166		}4167		if (copy_to_user(argp, &val16, sizeof(val16)))4168			ret = -EFAULT;4169		break;4170	case SONYPI_IOCGBAT1REM:4171		if (ec_read16(SONYPI_BAT1_LEFT, &val16)) {4172			ret = -EIO;4173			break;4174		}4175		if (copy_to_user(argp, &val16, sizeof(val16)))4176			ret = -EFAULT;4177		break;4178	case SONYPI_IOCGBAT2CAP:4179		if (ec_read16(SONYPI_BAT2_FULL, &val16)) {4180			ret = -EIO;4181			break;4182		}4183		if (copy_to_user(argp, &val16, sizeof(val16)))4184			ret = -EFAULT;4185		break;4186	case SONYPI_IOCGBAT2REM:4187		if (ec_read16(SONYPI_BAT2_LEFT, &val16)) {4188			ret = -EIO;4189			break;4190		}4191		if (copy_to_user(argp, &val16, sizeof(val16)))4192			ret = -EFAULT;4193		break;4194	case SONYPI_IOCGBATFLAGS:4195		if (ec_read(SONYPI_BAT_FLAGS, &val8)) {4196			ret = -EIO;4197			break;4198		}4199		val8 &= 0x07;4200		if (copy_to_user(argp, &val8, sizeof(val8)))4201			ret = -EFAULT;4202		break;4203	case SONYPI_IOCGBLUE:4204		val8 = spic_dev.bluetooth_power;4205		if (copy_to_user(argp, &val8, sizeof(val8)))4206			ret = -EFAULT;4207		break;4208	case SONYPI_IOCSBLUE:4209		if (copy_from_user(&val8, argp, sizeof(val8))) {4210			ret = -EFAULT;4211			break;4212		}4213		__sony_pic_set_bluetoothpower(val8);4214		break;4215	/* FAN Controls */4216	case SONYPI_IOCGFAN:4217		if (sony_pic_get_fanspeed(&val8)) {4218			ret = -EIO;4219			break;4220		}4221		if (copy_to_user(argp, &val8, sizeof(val8)))4222			ret = -EFAULT;4223		break;4224	case SONYPI_IOCSFAN:4225		if (copy_from_user(&val8, argp, sizeof(val8))) {4226			ret = -EFAULT;4227			break;4228		}4229		if (sony_pic_set_fanspeed(val8))4230			ret = -EIO;4231		break;4232	/* GET Temperature (useful under APM) */4233	case SONYPI_IOCGTEMP:4234		if (ec_read(SONYPI_TEMP_STATUS, &val8)) {4235			ret = -EIO;4236			break;4237		}4238		if (copy_to_user(argp, &val8, sizeof(val8)))4239			ret = -EFAULT;4240		break;4241	default:4242		ret = -EINVAL;4243	}4244	mutex_unlock(&spic_dev.lock);4245	return ret;4246}4247 4248static const struct file_operations sonypi_misc_fops = {4249	.owner		= THIS_MODULE,4250	.read		= sonypi_misc_read,4251	.poll		= sonypi_misc_poll,4252	.open		= sonypi_misc_open,4253	.release	= sonypi_misc_release,4254	.fasync		= sonypi_misc_fasync,4255	.unlocked_ioctl	= sonypi_misc_ioctl,4256	.llseek		= noop_llseek,4257};4258 4259static struct miscdevice sonypi_misc_device = {4260	.minor		= MISC_DYNAMIC_MINOR,4261	.name		= "sonypi",4262	.fops		= &sonypi_misc_fops,4263};4264 4265static void sonypi_compat_report_event(u8 event)4266{4267	kfifo_in_locked(&sonypi_compat.fifo, (unsigned char *)&event,4268			sizeof(event), &sonypi_compat.fifo_lock);4269	kill_fasync(&sonypi_compat.fifo_async, SIGIO, POLL_IN);4270	wake_up_interruptible(&sonypi_compat.fifo_proc_list);4271}4272 4273static int sonypi_compat_init(void)4274{4275	int error;4276 4277	spin_lock_init(&sonypi_compat.fifo_lock);4278	error =4279	 kfifo_alloc(&sonypi_compat.fifo, SONY_LAPTOP_BUF_SIZE, GFP_KERNEL);4280	if (error) {4281		pr_err("kfifo_alloc failed\n");4282		return error;4283	}4284 4285	init_waitqueue_head(&sonypi_compat.fifo_proc_list);4286 4287	if (minor != -1)4288		sonypi_misc_device.minor = minor;4289	error = misc_register(&sonypi_misc_device);4290	if (error) {4291		pr_err("misc_register failed\n");4292		goto err_free_kfifo;4293	}4294	if (minor == -1)4295		pr_info("device allocated minor is %d\n",4296			sonypi_misc_device.minor);4297 4298	return 0;4299 4300err_free_kfifo:4301	kfifo_free(&sonypi_compat.fifo);4302	return error;4303}4304 4305static void sonypi_compat_exit(void)4306{4307	misc_deregister(&sonypi_misc_device);4308	kfifo_free(&sonypi_compat.fifo);4309}4310#else4311static int sonypi_compat_init(void) { return 0; }4312static void sonypi_compat_exit(void) { }4313static void sonypi_compat_report_event(u8 event) { }4314#endif /* CONFIG_SONYPI_COMPAT */4315 4316/*4317 * ACPI callbacks4318 */4319static acpi_status4320sony_pic_read_possible_resource(struct acpi_resource *resource, void *context)4321{4322	u32 i;4323	struct sony_pic_dev *dev = (struct sony_pic_dev *)context;4324 4325	switch (resource->type) {4326	case ACPI_RESOURCE_TYPE_START_DEPENDENT:4327		{4328			/* start IO enumeration */4329			struct sony_pic_ioport *ioport = kzalloc(sizeof(*ioport), GFP_KERNEL);4330			if (!ioport)4331				return AE_ERROR;4332 4333			list_add(&ioport->list, &dev->ioports);4334			return AE_OK;4335		}4336 4337	case ACPI_RESOURCE_TYPE_END_DEPENDENT:4338		/* end IO enumeration */4339		return AE_OK;4340 4341	case ACPI_RESOURCE_TYPE_IRQ:4342		{4343			struct acpi_resource_irq *p = &resource->data.irq;4344			struct sony_pic_irq *interrupt = NULL;4345			if (!p->interrupt_count) {4346				/*4347				 * IRQ descriptors may have no IRQ# bits set,4348				 * particularly those those w/ _STA disabled4349				 */4350				dprintk("Blank IRQ resource\n");4351				return AE_OK;4352			}4353			for (i = 0; i < p->interrupt_count; i++) {4354				if (!p->interrupts[i]) {4355					pr_warn("Invalid IRQ %d\n",4356						p->interrupts[i]);4357					continue;4358				}4359				interrupt = kzalloc(sizeof(*interrupt),4360						GFP_KERNEL);4361				if (!interrupt)4362					return AE_ERROR;4363 4364				list_add(&interrupt->list, &dev->interrupts);4365				interrupt->irq.triggering = p->triggering;4366				interrupt->irq.polarity = p->polarity;4367				interrupt->irq.shareable = p->shareable;4368				interrupt->irq.interrupt_count = 1;4369				interrupt->irq.interrupts[0] = p->interrupts[i];4370			}4371			return AE_OK;4372		}4373	case ACPI_RESOURCE_TYPE_IO:4374		{4375			struct acpi_resource_io *io = &resource->data.io;4376			struct sony_pic_ioport *ioport =4377				list_first_entry(&dev->ioports, struct sony_pic_ioport, list);4378			if (!ioport->io1.minimum) {4379				memcpy(&ioport->io1, io, sizeof(*io));4380				dprintk("IO1 at 0x%.4x (0x%.2x)\n", ioport->io1.minimum,4381						ioport->io1.address_length);4382			}4383			else if (!ioport->io2.minimum) {4384				memcpy(&ioport->io2, io, sizeof(*io));4385				dprintk("IO2 at 0x%.4x (0x%.2x)\n", ioport->io2.minimum,4386						ioport->io2.address_length);4387			}4388			else {4389				pr_err("Unknown SPIC Type, more than 2 IO Ports\n");4390				return AE_ERROR;4391			}4392			return AE_OK;4393		}4394 4395	case ACPI_RESOURCE_TYPE_END_TAG:4396		return AE_OK;4397 4398	default:4399		dprintk("Resource %d isn't an IRQ nor an IO port\n",4400			resource->type);4401		return AE_CTRL_TERMINATE;4402 4403	}4404}4405 4406static int sony_pic_possible_resources(struct acpi_device *device)4407{4408	int result = 0;4409	acpi_status status = AE_OK;4410 4411	if (!device)4412		return -EINVAL;4413 4414	/* get device status */4415	/* see acpi_pci_link_get_current acpi_pci_link_get_possible */4416	dprintk("Evaluating _STA\n");4417	result = acpi_bus_get_status(device);4418	if (result) {4419		pr_warn("Unable to read status\n");4420		goto end;4421	}4422 4423	if (!device->status.enabled)4424		dprintk("Device disabled\n");4425	else4426		dprintk("Device enabled\n");4427 4428	/*4429	 * Query and parse 'method'4430	 */4431	dprintk("Evaluating %s\n", METHOD_NAME__PRS);4432	status = acpi_walk_resources(device->handle, METHOD_NAME__PRS,4433			sony_pic_read_possible_resource, &spic_dev);4434	if (ACPI_FAILURE(status)) {4435		pr_warn("Failure evaluating %s\n", METHOD_NAME__PRS);4436		result = -ENODEV;4437	}4438end:4439	return result;4440}4441 4442/*4443 *  Disable the spic device by calling its _DIS method4444 */4445static int sony_pic_disable(struct acpi_device *device)4446{4447	acpi_status ret = acpi_evaluate_object(device->handle, "_DIS", NULL,4448					       NULL);4449 4450	if (ACPI_FAILURE(ret) && ret != AE_NOT_FOUND)4451		return -ENXIO;4452 4453	dprintk("Device disabled\n");4454	return 0;4455}4456 4457 4458/*4459 *  Based on drivers/acpi/pci_link.c:acpi_pci_link_set4460 *4461 *  Call _SRS to set current resources4462 */4463static int sony_pic_enable(struct acpi_device *device,4464		struct sony_pic_ioport *ioport, struct sony_pic_irq *irq)4465{4466	acpi_status status;4467	int result = 0;4468	/* Type 1 resource layout is:4469	 *    IO4470	 *    IO4471	 *    IRQNoFlags4472	 *    End4473	 *4474	 * Type 2 and 3 resource layout is:4475	 *    IO4476	 *    IRQNoFlags4477	 *    End4478	 */4479	struct {4480		struct acpi_resource res1;4481		struct acpi_resource res2;4482		struct acpi_resource res3;4483		struct acpi_resource res4;4484	} *resource;4485	struct acpi_buffer buffer = { 0, NULL };4486 4487	if (!ioport || !irq)4488		return -EINVAL;4489 4490	/* init acpi_buffer */4491	resource = kzalloc(sizeof(*resource) + 1, GFP_KERNEL);4492	if (!resource)4493		return -ENOMEM;4494 4495	buffer.length = sizeof(*resource) + 1;4496	buffer.pointer = resource;4497 4498	/* setup Type 1 resources */4499	if (spic_dev.model == SONYPI_DEVICE_TYPE1) {4500 4501		/* setup io resources */4502		resource->res1.type = ACPI_RESOURCE_TYPE_IO;4503		resource->res1.length = sizeof(struct acpi_resource);4504		memcpy(&resource->res1.data.io, &ioport->io1,4505				sizeof(struct acpi_resource_io));4506 4507		resource->res2.type = ACPI_RESOURCE_TYPE_IO;4508		resource->res2.length = sizeof(struct acpi_resource);4509		memcpy(&resource->res2.data.io, &ioport->io2,4510				sizeof(struct acpi_resource_io));4511 4512		/* setup irq resource */4513		resource->res3.type = ACPI_RESOURCE_TYPE_IRQ;4514		resource->res3.length = sizeof(struct acpi_resource);4515		memcpy(&resource->res3.data.irq, &irq->irq,4516				sizeof(struct acpi_resource_irq));4517		/* we requested a shared irq */4518		resource->res3.data.irq.shareable = ACPI_SHARED;4519 4520		resource->res4.type = ACPI_RESOURCE_TYPE_END_TAG;4521		resource->res4.length = sizeof(struct acpi_resource);4522	}4523	/* setup Type 2/3 resources */4524	else {4525		/* setup io resource */4526		resource->res1.type = ACPI_RESOURCE_TYPE_IO;4527		resource->res1.length = sizeof(struct acpi_resource);4528		memcpy(&resource->res1.data.io, &ioport->io1,4529				sizeof(struct acpi_resource_io));4530 4531		/* setup irq resource */4532		resource->res2.type = ACPI_RESOURCE_TYPE_IRQ;4533		resource->res2.length = sizeof(struct acpi_resource);4534		memcpy(&resource->res2.data.irq, &irq->irq,4535				sizeof(struct acpi_resource_irq));4536		/* we requested a shared irq */4537		resource->res2.data.irq.shareable = ACPI_SHARED;4538 4539		resource->res3.type = ACPI_RESOURCE_TYPE_END_TAG;4540		resource->res3.length = sizeof(struct acpi_resource);4541	}4542 4543	/* Attempt to set the resource */4544	dprintk("Evaluating _SRS\n");4545	status = acpi_set_current_resources(device->handle, &buffer);4546 4547	/* check for total failure */4548	if (ACPI_FAILURE(status)) {4549		pr_err("Error evaluating _SRS\n");4550		result = -ENODEV;4551		goto end;4552	}4553 4554	/* Necessary device initializations calls (from sonypi) */4555	sony_pic_call1(0x82);4556	sony_pic_call2(0x81, 0xff);4557	sony_pic_call1(compat ? 0x92 : 0x82);4558 4559end:4560	kfree(resource);4561	return result;4562}4563 4564/*****************4565 *4566 * ISR: some event is available4567 *4568 *****************/4569static irqreturn_t sony_pic_irq(int irq, void *dev_id)4570{4571	int i, j;4572	u8 ev = 0;4573	u8 data_mask = 0;4574	u8 device_event = 0;4575 4576	struct sony_pic_dev *dev = (struct sony_pic_dev *) dev_id;4577 4578	ev = inb_p(dev->cur_ioport->io1.minimum);4579	if (dev->cur_ioport->io2.minimum)4580		data_mask = inb_p(dev->cur_ioport->io2.minimum);4581	else4582		data_mask = inb_p(dev->cur_ioport->io1.minimum +4583				dev->evport_offset);4584 4585	dprintk("event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n",4586			ev, data_mask, dev->cur_ioport->io1.minimum,4587			dev->evport_offset);4588 4589	if (ev == 0x00 || ev == 0xff)4590		return IRQ_HANDLED;4591 4592	for (i = 0; dev->event_types[i].mask; i++) {4593 4594		if ((data_mask & dev->event_types[i].data) !=4595		    dev->event_types[i].data)4596			continue;4597 4598		if (!(mask & dev->event_types[i].mask))4599			continue;4600 4601		for (j = 0; dev->event_types[i].events[j].event; j++) {4602			if (ev == dev->event_types[i].events[j].data) {4603				device_event =4604					dev->event_types[i].events[j].event;4605				/* some events may require ignoring */4606				if (!device_event)4607					return IRQ_HANDLED;4608				goto found;4609			}4610		}4611	}4612	/* Still not able to decode the event try to pass4613	 * it over to the minidriver4614	 */4615	if (dev->handle_irq && dev->handle_irq(data_mask, ev) == 0)4616		return IRQ_HANDLED;4617 4618	dprintk("unknown event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n",4619			ev, data_mask, dev->cur_ioport->io1.minimum,4620			dev->evport_offset);4621	return IRQ_HANDLED;4622 4623found:4624	sony_laptop_report_input_event(device_event);4625	sonypi_compat_report_event(device_event);4626	return IRQ_HANDLED;4627}4628 4629/*****************4630 *4631 *  ACPI driver4632 *4633 *****************/4634static void sony_pic_remove(struct acpi_device *device)4635{4636	struct sony_pic_ioport *io, *tmp_io;4637	struct sony_pic_irq *irq, *tmp_irq;4638 4639	if (sony_pic_disable(device)) {4640		pr_err("Couldn't disable device\n");4641		return;4642	}4643 4644	free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev);4645	release_region(spic_dev.cur_ioport->io1.minimum,4646			spic_dev.cur_ioport->io1.address_length);4647	if (spic_dev.cur_ioport->io2.minimum)4648		release_region(spic_dev.cur_ioport->io2.minimum,4649				spic_dev.cur_ioport->io2.address_length);4650 4651	sonypi_compat_exit();4652 4653	sony_laptop_remove_input();4654 4655	/* pf attrs */4656	sysfs_remove_group(&sony_pf_device->dev.kobj, &spic_attribute_group);4657	sony_pf_remove();4658 4659	list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) {4660		list_del(&io->list);4661		kfree(io);4662	}4663	list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) {4664		list_del(&irq->list);4665		kfree(irq);4666	}4667	spic_dev.cur_ioport = NULL;4668	spic_dev.cur_irq = NULL;4669 4670	dprintk(SONY_PIC_DRIVER_NAME " removed.\n");4671}4672 4673static int sony_pic_add(struct acpi_device *device)4674{4675	int result;4676	struct sony_pic_ioport *io, *tmp_io;4677	struct sony_pic_irq *irq, *tmp_irq;4678 4679	spic_dev.acpi_dev = device;4680	strcpy(acpi_device_class(device), "sony/hotkey");4681	sony_pic_detect_device_type(&spic_dev);4682	mutex_init(&spic_dev.lock);4683 4684	/* read _PRS resources */4685	result = sony_pic_possible_resources(device);4686	if (result) {4687		pr_err("Unable to read possible resources\n");4688		goto err_free_resources;4689	}4690 4691	/* setup input devices and helper fifo */4692	result = sony_laptop_setup_input(device);4693	if (result) {4694		pr_err("Unable to create input devices\n");4695		goto err_free_resources;4696	}4697 4698	result = sonypi_compat_init();4699	if (result)4700		goto err_remove_input;4701 4702	/* request io port */4703	list_for_each_entry_reverse(io, &spic_dev.ioports, list) {4704		if (request_region(io->io1.minimum, io->io1.address_length,4705					"Sony Programmable I/O Device")) {4706			dprintk("I/O port1: 0x%.4x (0x%.4x) + 0x%.2x\n",4707					io->io1.minimum, io->io1.maximum,4708					io->io1.address_length);4709			/* Type 1 have 2 ioports */4710			if (io->io2.minimum) {4711				if (request_region(io->io2.minimum,4712						io->io2.address_length,4713						"Sony Programmable I/O Device")) {4714					dprintk("I/O port2: 0x%.4x (0x%.4x) + 0x%.2x\n",4715							io->io2.minimum, io->io2.maximum,4716							io->io2.address_length);4717					spic_dev.cur_ioport = io;4718					break;4719				}4720				else {4721					dprintk("Unable to get I/O port2: "4722							"0x%.4x (0x%.4x) + 0x%.2x\n",4723							io->io2.minimum, io->io2.maximum,4724							io->io2.address_length);4725					release_region(io->io1.minimum,4726							io->io1.address_length);4727				}4728			}4729			else {4730				spic_dev.cur_ioport = io;4731				break;4732			}4733		}4734	}4735	if (!spic_dev.cur_ioport) {4736		pr_err("Failed to request_region\n");4737		result = -ENODEV;4738		goto err_remove_compat;4739	}4740 4741	/* request IRQ */4742	list_for_each_entry_reverse(irq, &spic_dev.interrupts, list) {4743		if (!request_irq(irq->irq.interrupts[0], sony_pic_irq,4744					0, "sony-laptop", &spic_dev)) {4745			dprintk("IRQ: %d - triggering: %d - "4746					"polarity: %d - shr: %d\n",4747					irq->irq.interrupts[0],4748					irq->irq.triggering,4749					irq->irq.polarity,4750					irq->irq.shareable);4751			spic_dev.cur_irq = irq;4752			break;4753		}4754	}4755	if (!spic_dev.cur_irq) {4756		pr_err("Failed to request_irq\n");4757		result = -ENODEV;4758		goto err_release_region;4759	}4760 4761	/* set resource status _SRS */4762	result = sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq);4763	if (result) {4764		pr_err("Couldn't enable device\n");4765		goto err_free_irq;4766	}4767 4768	spic_dev.bluetooth_power = -1;4769	/* create device attributes */4770	result = sony_pf_add();4771	if (result)4772		goto err_disable_device;4773 4774	result = sysfs_create_group(&sony_pf_device->dev.kobj, &spic_attribute_group);4775	if (result)4776		goto err_remove_pf;4777 4778	pr_info("SPIC setup done.\n");4779	return 0;4780 4781err_remove_pf:4782	sony_pf_remove();4783 4784err_disable_device:4785	sony_pic_disable(device);4786 4787err_free_irq:4788	free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev);4789 4790err_release_region:4791	release_region(spic_dev.cur_ioport->io1.minimum,4792			spic_dev.cur_ioport->io1.address_length);4793	if (spic_dev.cur_ioport->io2.minimum)4794		release_region(spic_dev.cur_ioport->io2.minimum,4795				spic_dev.cur_ioport->io2.address_length);4796 4797err_remove_compat:4798	sonypi_compat_exit();4799 4800err_remove_input:4801	sony_laptop_remove_input();4802 4803err_free_resources:4804	list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) {4805		list_del(&io->list);4806		kfree(io);4807	}4808	list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) {4809		list_del(&irq->list);4810		kfree(irq);4811	}4812	spic_dev.cur_ioport = NULL;4813	spic_dev.cur_irq = NULL;4814 4815	return result;4816}4817 4818#ifdef CONFIG_PM_SLEEP4819static int sony_pic_suspend(struct device *dev)4820{4821	if (sony_pic_disable(to_acpi_device(dev)))4822		return -ENXIO;4823	return 0;4824}4825 4826static int sony_pic_resume(struct device *dev)4827{4828	sony_pic_enable(to_acpi_device(dev),4829			spic_dev.cur_ioport, spic_dev.cur_irq);4830	return 0;4831}4832#endif4833 4834static SIMPLE_DEV_PM_OPS(sony_pic_pm, sony_pic_suspend, sony_pic_resume);4835 4836static const struct acpi_device_id sony_pic_device_ids[] = {4837	{SONY_PIC_HID, 0},4838	{"", 0},4839};4840 4841static struct acpi_driver sony_pic_driver = {4842	.name = SONY_PIC_DRIVER_NAME,4843	.class = SONY_PIC_CLASS,4844	.ids = sony_pic_device_ids,4845	.ops = {4846		.add = sony_pic_add,4847		.remove = sony_pic_remove,4848		},4849	.drv.pm = &sony_pic_pm,4850};4851 4852static const struct dmi_system_id sonypi_dmi_table[] __initconst = {4853	{4854		.ident = "Sony Vaio",4855		.matches = {4856			DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),4857			DMI_MATCH(DMI_PRODUCT_NAME, "PCG-"),4858		},4859	},4860	{4861		.ident = "Sony Vaio",4862		.matches = {4863			DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),4864			DMI_MATCH(DMI_PRODUCT_NAME, "VGN-"),4865		},4866	},4867	{ }4868};4869 4870static int __init sony_laptop_init(void)4871{4872	int result;4873 4874	if (!no_spic && dmi_check_system(sonypi_dmi_table)) {4875		result = acpi_bus_register_driver(&sony_pic_driver);4876		if (result) {4877			pr_err("Unable to register SPIC driver\n");4878			goto out;4879		}4880		spic_drv_registered = 1;4881	}4882 4883	result = acpi_bus_register_driver(&sony_nc_driver);4884	if (result) {4885		pr_err("Unable to register SNC driver\n");4886		goto out_unregister_pic;4887	}4888 4889	return 0;4890 4891out_unregister_pic:4892	if (spic_drv_registered)4893		acpi_bus_unregister_driver(&sony_pic_driver);4894out:4895	return result;4896}4897 4898static void __exit sony_laptop_exit(void)4899{4900	acpi_bus_unregister_driver(&sony_nc_driver);4901	if (spic_drv_registered)4902		acpi_bus_unregister_driver(&sony_pic_driver);4903}4904 4905module_init(sony_laptop_init);4906module_exit(sony_laptop_exit);4907