|
@@ -141,6 +141,27 @@ MODULE_PARM_DESC(kbd_backlight_timeout,
|
|
|
"(default: 0)");
|
|
|
|
|
|
static void sony_nc_kbd_backlight_resume(void);
|
|
|
+static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
|
|
|
+ unsigned int handle);
|
|
|
+static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd);
|
|
|
+
|
|
|
+static int sony_nc_battery_care_setup(struct platform_device *pd,
|
|
|
+ unsigned int handle);
|
|
|
+static void sony_nc_battery_care_cleanup(struct platform_device *pd);
|
|
|
+
|
|
|
+static int sony_nc_thermal_setup(struct platform_device *pd);
|
|
|
+static void sony_nc_thermal_cleanup(struct platform_device *pd);
|
|
|
+static void sony_nc_thermal_resume(void);
|
|
|
+
|
|
|
+static int sony_nc_lid_resume_setup(struct platform_device *pd);
|
|
|
+static void sony_nc_lid_resume_cleanup(struct platform_device *pd);
|
|
|
+
|
|
|
+static int sony_nc_highspeed_charging_setup(struct platform_device *pd);
|
|
|
+static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd);
|
|
|
+
|
|
|
+static int sony_nc_touchpad_setup(struct platform_device *pd,
|
|
|
+ unsigned int handle);
|
|
|
+static void sony_nc_touchpad_cleanup(struct platform_device *pd);
|
|
|
|
|
|
enum sony_nc_rfkill {
|
|
|
SONY_WIFI,
|
|
@@ -153,6 +174,9 @@ enum sony_nc_rfkill {
|
|
|
static int sony_rfkill_handle;
|
|
|
static struct rfkill *sony_rfkill_devices[N_SONY_RFKILL];
|
|
|
static int sony_rfkill_address[N_SONY_RFKILL] = {0x300, 0x500, 0x700, 0x900};
|
|
|
+static int sony_nc_rfkill_setup(struct acpi_device *device,
|
|
|
+ unsigned int handle);
|
|
|
+static void sony_nc_rfkill_cleanup(void);
|
|
|
static void sony_nc_rfkill_update(void);
|
|
|
|
|
|
/*********** Input Devices ***********/
|
|
@@ -691,59 +715,97 @@ static struct acpi_device *sony_nc_acpi_device = NULL;
|
|
|
|
|
|
/*
|
|
|
* acpi_evaluate_object wrappers
|
|
|
+ * all useful calls into SNC methods take one or zero parameters and return
|
|
|
+ * integers or arrays.
|
|
|
*/
|
|
|
-static int acpi_callgetfunc(acpi_handle handle, char *name, int *result)
|
|
|
+static union acpi_object *__call_snc_method(acpi_handle handle, char *method,
|
|
|
+ u64 *value)
|
|
|
{
|
|
|
- struct acpi_buffer output;
|
|
|
- union acpi_object out_obj;
|
|
|
+ union acpi_object *result = NULL;
|
|
|
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
|
|
acpi_status status;
|
|
|
|
|
|
- output.length = sizeof(out_obj);
|
|
|
- output.pointer = &out_obj;
|
|
|
+ if (value) {
|
|
|
+ struct acpi_object_list params;
|
|
|
+ union acpi_object in;
|
|
|
+ in.type = ACPI_TYPE_INTEGER;
|
|
|
+ in.integer.value = *value;
|
|
|
+ params.count = 1;
|
|
|
+ params.pointer = ∈
|
|
|
+ status = acpi_evaluate_object(handle, method, ¶ms, &output);
|
|
|
+ dprintk("__call_snc_method: [%s:0x%.8x%.8x]\n", method,
|
|
|
+ (unsigned int)(*value >> 32),
|
|
|
+ (unsigned int)*value & 0xffffffff);
|
|
|
+ } else {
|
|
|
+ status = acpi_evaluate_object(handle, method, NULL, &output);
|
|
|
+ dprintk("__call_snc_method: [%s]\n", method);
|
|
|
+ }
|
|
|
|
|
|
- status = acpi_evaluate_object(handle, name, NULL, &output);
|
|
|
- if ((status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER)) {
|
|
|
- *result = out_obj.integer.value;
|
|
|
- return 0;
|
|
|
+ if (ACPI_FAILURE(status)) {
|
|
|
+ pr_err("Failed to evaluate [%s]\n", method);
|
|
|
+ return NULL;
|
|
|
}
|
|
|
|
|
|
- pr_warn("acpi_callreadfunc failed\n");
|
|
|
+ result = (union acpi_object *) output.pointer;
|
|
|
+ if (!result)
|
|
|
+ dprintk("No return object [%s]\n", method);
|
|
|
|
|
|
- return -1;
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
-static int acpi_callsetfunc(acpi_handle handle, char *name, int value,
|
|
|
- int *result)
|
|
|
+static int sony_nc_int_call(acpi_handle handle, char *name, int *value,
|
|
|
+ int *result)
|
|
|
{
|
|
|
- struct acpi_object_list params;
|
|
|
- union acpi_object in_obj;
|
|
|
- struct acpi_buffer output;
|
|
|
- union acpi_object out_obj;
|
|
|
- acpi_status status;
|
|
|
-
|
|
|
- params.count = 1;
|
|
|
- params.pointer = &in_obj;
|
|
|
- in_obj.type = ACPI_TYPE_INTEGER;
|
|
|
- in_obj.integer.value = value;
|
|
|
+ union acpi_object *object = NULL;
|
|
|
+ if (value) {
|
|
|
+ u64 v = *value;
|
|
|
+ object = __call_snc_method(handle, name, &v);
|
|
|
+ } else
|
|
|
+ object = __call_snc_method(handle, name, NULL);
|
|
|
|
|
|
- output.length = sizeof(out_obj);
|
|
|
- output.pointer = &out_obj;
|
|
|
+ if (!object)
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- status = acpi_evaluate_object(handle, name, ¶ms, &output);
|
|
|
- if (status == AE_OK) {
|
|
|
- if (result != NULL) {
|
|
|
- if (out_obj.type != ACPI_TYPE_INTEGER) {
|
|
|
- pr_warn("acpi_evaluate_object bad return type\n");
|
|
|
- return -1;
|
|
|
- }
|
|
|
- *result = out_obj.integer.value;
|
|
|
- }
|
|
|
- return 0;
|
|
|
+ if (object->type != ACPI_TYPE_INTEGER) {
|
|
|
+ pr_warn("Invalid acpi_object: expected 0x%x got 0x%x\n",
|
|
|
+ ACPI_TYPE_INTEGER, object->type);
|
|
|
+ kfree(object);
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- pr_warn("acpi_evaluate_object failed\n");
|
|
|
+ if (result)
|
|
|
+ *result = object->integer.value;
|
|
|
+
|
|
|
+ kfree(object);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+#define MIN(a, b) (a > b ? b : a)
|
|
|
+static int sony_nc_buffer_call(acpi_handle handle, char *name, u64 *value,
|
|
|
+ void *buffer, size_t buflen)
|
|
|
+{
|
|
|
+ size_t len = len;
|
|
|
+ union acpi_object *object = __call_snc_method(handle, name, value);
|
|
|
+
|
|
|
+ if (!object)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (object->type == ACPI_TYPE_BUFFER)
|
|
|
+ len = MIN(buflen, object->buffer.length);
|
|
|
+
|
|
|
+ else if (object->type == ACPI_TYPE_INTEGER)
|
|
|
+ len = MIN(buflen, sizeof(object->integer.value));
|
|
|
+
|
|
|
+ else {
|
|
|
+ pr_warn("Invalid acpi_object: expected 0x%x got 0x%x\n",
|
|
|
+ ACPI_TYPE_BUFFER, object->type);
|
|
|
+ kfree(object);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
|
|
|
- return -1;
|
|
|
+ memcpy(buffer, object->buffer.pointer, len);
|
|
|
+ kfree(object);
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
struct sony_nc_handles {
|
|
@@ -770,16 +832,17 @@ static ssize_t sony_nc_handles_show(struct device *dev,
|
|
|
|
|
|
static int sony_nc_handles_setup(struct platform_device *pd)
|
|
|
{
|
|
|
- int i;
|
|
|
- int result;
|
|
|
+ int i, r, result, arg;
|
|
|
|
|
|
handles = kzalloc(sizeof(*handles), GFP_KERNEL);
|
|
|
if (!handles)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(handles->cap); i++) {
|
|
|
- if (!acpi_callsetfunc(sony_nc_acpi_handle,
|
|
|
- "SN00", i + 0x20, &result)) {
|
|
|
+ arg = i + 0x20;
|
|
|
+ r = sony_nc_int_call(sony_nc_acpi_handle, "SN00", &arg,
|
|
|
+ &result);
|
|
|
+ if (!r) {
|
|
|
dprintk("caching handle 0x%.4x (offset: 0x%.2x)\n",
|
|
|
result, i);
|
|
|
handles->cap[i] = result;
|
|
@@ -819,8 +882,8 @@ static int sony_find_snc_handle(int handle)
|
|
|
int i;
|
|
|
|
|
|
/* not initialized yet, return early */
|
|
|
- if (!handles)
|
|
|
- return -1;
|
|
|
+ if (!handles || !handle)
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
for (i = 0; i < 0x10; i++) {
|
|
|
if (handles->cap[i] == handle) {
|
|
@@ -830,21 +893,20 @@ static int sony_find_snc_handle(int handle)
|
|
|
}
|
|
|
}
|
|
|
dprintk("handle 0x%.4x not found\n", handle);
|
|
|
- return -1;
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
|
|
|
static int sony_call_snc_handle(int handle, int argument, int *result)
|
|
|
{
|
|
|
- int ret = 0;
|
|
|
+ int arg, ret = 0;
|
|
|
int offset = sony_find_snc_handle(handle);
|
|
|
|
|
|
if (offset < 0)
|
|
|
- return -1;
|
|
|
+ return offset;
|
|
|
|
|
|
- ret = acpi_callsetfunc(sony_nc_acpi_handle, "SN07", offset | argument,
|
|
|
- result);
|
|
|
- dprintk("called SN07 with 0x%.4x (result: 0x%.4x)\n", offset | argument,
|
|
|
- *result);
|
|
|
+ arg = offset | argument;
|
|
|
+ ret = sony_nc_int_call(sony_nc_acpi_handle, "SN07", &arg, result);
|
|
|
+ dprintk("called SN07 with 0x%.4x (result: 0x%.4x)\n", arg, *result);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -889,14 +951,16 @@ static int boolean_validate(const int direction, const int value)
|
|
|
static ssize_t sony_nc_sysfs_show(struct device *dev, struct device_attribute *attr,
|
|
|
char *buffer)
|
|
|
{
|
|
|
- int value;
|
|
|
+ int value, ret = 0;
|
|
|
struct sony_nc_value *item =
|
|
|
container_of(attr, struct sony_nc_value, devattr);
|
|
|
|
|
|
if (!*item->acpiget)
|
|
|
return -EIO;
|
|
|
|
|
|
- if (acpi_callgetfunc(sony_nc_acpi_handle, *item->acpiget, &value) < 0)
|
|
|
+ ret = sony_nc_int_call(sony_nc_acpi_handle, *item->acpiget, NULL,
|
|
|
+ &value);
|
|
|
+ if (ret < 0)
|
|
|
return -EIO;
|
|
|
|
|
|
if (item->validate)
|
|
@@ -909,7 +973,8 @@ static ssize_t sony_nc_sysfs_store(struct device *dev,
|
|
|
struct device_attribute *attr,
|
|
|
const char *buffer, size_t count)
|
|
|
{
|
|
|
- int value;
|
|
|
+ unsigned long value = 0;
|
|
|
+ int ret = 0;
|
|
|
struct sony_nc_value *item =
|
|
|
container_of(attr, struct sony_nc_value, devattr);
|
|
|
|
|
@@ -919,7 +984,8 @@ static ssize_t sony_nc_sysfs_store(struct device *dev,
|
|
|
if (count > 31)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- value = simple_strtoul(buffer, NULL, 10);
|
|
|
+ if (kstrtoul(buffer, 10, &value))
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
if (item->validate)
|
|
|
value = item->validate(SNC_VALIDATE_IN, value);
|
|
@@ -927,8 +993,11 @@ static ssize_t sony_nc_sysfs_store(struct device *dev,
|
|
|
if (value < 0)
|
|
|
return value;
|
|
|
|
|
|
- if (acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, value, NULL) < 0)
|
|
|
+ ret = sony_nc_int_call(sony_nc_acpi_handle, *item->acpiset,
|
|
|
+ (int *)&value, NULL);
|
|
|
+ if (ret < 0)
|
|
|
return -EIO;
|
|
|
+
|
|
|
item->value = value;
|
|
|
item->valid = 1;
|
|
|
return count;
|
|
@@ -948,15 +1017,15 @@ struct sony_backlight_props sony_bl_props;
|
|
|
|
|
|
static int sony_backlight_update_status(struct backlight_device *bd)
|
|
|
{
|
|
|
- return acpi_callsetfunc(sony_nc_acpi_handle, "SBRT",
|
|
|
- bd->props.brightness + 1, NULL);
|
|
|
+ int arg = bd->props.brightness + 1;
|
|
|
+ return sony_nc_int_call(sony_nc_acpi_handle, "SBRT", &arg, NULL);
|
|
|
}
|
|
|
|
|
|
static int sony_backlight_get_brightness(struct backlight_device *bd)
|
|
|
{
|
|
|
int value;
|
|
|
|
|
|
- if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value))
|
|
|
+ if (sony_nc_int_call(sony_nc_acpi_handle, "GBRT", NULL, &value))
|
|
|
return 0;
|
|
|
/* brightness levels are 1-based, while backlight ones are 0-based */
|
|
|
return value - 1;
|
|
@@ -1024,10 +1093,14 @@ static struct sony_nc_event sony_100_events[] = {
|
|
|
{ 0x06, SONYPI_EVENT_FNKEY_RELEASED },
|
|
|
{ 0x87, SONYPI_EVENT_FNKEY_F7 },
|
|
|
{ 0x07, SONYPI_EVENT_FNKEY_RELEASED },
|
|
|
+ { 0x88, SONYPI_EVENT_FNKEY_F8 },
|
|
|
+ { 0x08, SONYPI_EVENT_FNKEY_RELEASED },
|
|
|
{ 0x89, SONYPI_EVENT_FNKEY_F9 },
|
|
|
{ 0x09, SONYPI_EVENT_FNKEY_RELEASED },
|
|
|
{ 0x8A, SONYPI_EVENT_FNKEY_F10 },
|
|
|
{ 0x0A, SONYPI_EVENT_FNKEY_RELEASED },
|
|
|
+ { 0x8B, SONYPI_EVENT_FNKEY_F11 },
|
|
|
+ { 0x0B, SONYPI_EVENT_FNKEY_RELEASED },
|
|
|
{ 0x8C, SONYPI_EVENT_FNKEY_F12 },
|
|
|
{ 0x0C, SONYPI_EVENT_FNKEY_RELEASED },
|
|
|
{ 0x9d, SONYPI_EVENT_ZOOM_PRESSED },
|
|
@@ -1063,63 +1136,116 @@ static struct sony_nc_event sony_127_events[] = {
|
|
|
{ 0, 0 },
|
|
|
};
|
|
|
|
|
|
+static int sony_nc_hotkeys_decode(u32 event, unsigned int handle)
|
|
|
+{
|
|
|
+ int ret = -EINVAL;
|
|
|
+ unsigned int result = 0;
|
|
|
+ struct sony_nc_event *key_event;
|
|
|
+
|
|
|
+ if (sony_call_snc_handle(handle, 0x200, &result)) {
|
|
|
+ dprintk("Unable to decode event 0x%.2x 0x%.2x\n", handle,
|
|
|
+ event);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ result &= 0xFF;
|
|
|
+
|
|
|
+ if (handle == 0x0100)
|
|
|
+ key_event = sony_100_events;
|
|
|
+ else
|
|
|
+ key_event = sony_127_events;
|
|
|
+
|
|
|
+ for (; key_event->data; key_event++) {
|
|
|
+ if (key_event->data == result) {
|
|
|
+ ret = key_event->event;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!key_event->data)
|
|
|
+ pr_info("Unknown hotkey 0x%.2x/0x%.2x (handle 0x%.2x)\n",
|
|
|
+ event, result, handle);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* ACPI callbacks
|
|
|
*/
|
|
|
static void sony_nc_notify(struct acpi_device *device, u32 event)
|
|
|
{
|
|
|
- u32 ev = event;
|
|
|
+ u32 real_ev = event;
|
|
|
+ u8 ev_type = 0;
|
|
|
+ dprintk("sony_nc_notify, event: 0x%.2x\n", event);
|
|
|
+
|
|
|
+ if (event >= 0x90) {
|
|
|
+ unsigned int result = 0;
|
|
|
+ unsigned int arg = 0;
|
|
|
+ unsigned int handle = 0;
|
|
|
+ unsigned int offset = event - 0x90;
|
|
|
+
|
|
|
+ if (offset >= ARRAY_SIZE(handles->cap)) {
|
|
|
+ pr_err("Event 0x%x outside of capabilities list\n",
|
|
|
+ event);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ handle = handles->cap[offset];
|
|
|
+
|
|
|
+ /* list of handles known for generating events */
|
|
|
+ switch (handle) {
|
|
|
+ /* hotkey event */
|
|
|
+ case 0x0100:
|
|
|
+ case 0x0127:
|
|
|
+ ev_type = 1;
|
|
|
+ real_ev = sony_nc_hotkeys_decode(event, handle);
|
|
|
+
|
|
|
+ if (real_ev > 0)
|
|
|
+ sony_laptop_report_input_event(real_ev);
|
|
|
+ else
|
|
|
+ /* restore the original event for reporting */
|
|
|
+ real_ev = event;
|
|
|
|
|
|
- if (ev >= 0x90) {
|
|
|
- /* New-style event */
|
|
|
- int result;
|
|
|
- int key_handle = 0;
|
|
|
- ev -= 0x90;
|
|
|
-
|
|
|
- if (sony_find_snc_handle(0x100) == ev)
|
|
|
- key_handle = 0x100;
|
|
|
- if (sony_find_snc_handle(0x127) == ev)
|
|
|
- key_handle = 0x127;
|
|
|
-
|
|
|
- if (key_handle) {
|
|
|
- struct sony_nc_event *key_event;
|
|
|
-
|
|
|
- if (sony_call_snc_handle(key_handle, 0x200, &result)) {
|
|
|
- dprintk("sony_nc_notify, unable to decode"
|
|
|
- " event 0x%.2x 0x%.2x\n", key_handle,
|
|
|
- ev);
|
|
|
- /* restore the original event */
|
|
|
- ev = event;
|
|
|
- } else {
|
|
|
- ev = result & 0xFF;
|
|
|
-
|
|
|
- if (key_handle == 0x100)
|
|
|
- key_event = sony_100_events;
|
|
|
- else
|
|
|
- key_event = sony_127_events;
|
|
|
-
|
|
|
- for (; key_event->data; key_event++) {
|
|
|
- if (key_event->data == ev) {
|
|
|
- ev = key_event->event;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
+ break;
|
|
|
|
|
|
- if (!key_event->data)
|
|
|
- pr_info("Unknown event: 0x%x 0x%x\n",
|
|
|
- key_handle, ev);
|
|
|
- else
|
|
|
- sony_laptop_report_input_event(ev);
|
|
|
- }
|
|
|
- } else if (sony_find_snc_handle(sony_rfkill_handle) == ev) {
|
|
|
- sony_nc_rfkill_update();
|
|
|
- return;
|
|
|
+ /* wlan switch */
|
|
|
+ case 0x0124:
|
|
|
+ case 0x0135:
|
|
|
+ /* events on this handle are reported when the
|
|
|
+ * switch changes position or for battery
|
|
|
+ * events. We'll notify both of them but only
|
|
|
+ * update the rfkill device status when the
|
|
|
+ * switch is moved.
|
|
|
+ */
|
|
|
+ ev_type = 2;
|
|
|
+ sony_call_snc_handle(handle, 0x0100, &result);
|
|
|
+ real_ev = result & 0x03;
|
|
|
+
|
|
|
+ /* hw switch event */
|
|
|
+ if (real_ev == 1)
|
|
|
+ sony_nc_rfkill_update();
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ dprintk("Unknown event 0x%x for handle 0x%x\n",
|
|
|
+ event, handle);
|
|
|
+ break;
|
|
|
}
|
|
|
- } else
|
|
|
- sony_laptop_report_input_event(ev);
|
|
|
|
|
|
- dprintk("sony_nc_notify, event: 0x%.2x\n", ev);
|
|
|
- acpi_bus_generate_proc_event(sony_nc_acpi_device, 1, ev);
|
|
|
+ /* clear the event (and the event reason when present) */
|
|
|
+ arg = 1 << offset;
|
|
|
+ sony_nc_int_call(sony_nc_acpi_handle, "SN05", &arg, &result);
|
|
|
+
|
|
|
+ } else {
|
|
|
+ /* old style event */
|
|
|
+ ev_type = 1;
|
|
|
+ sony_laptop_report_input_event(real_ev);
|
|
|
+ }
|
|
|
+
|
|
|
+ acpi_bus_generate_proc_event(sony_nc_acpi_device, ev_type, real_ev);
|
|
|
+
|
|
|
+ acpi_bus_generate_netlink_event(sony_nc_acpi_device->pnp.device_class,
|
|
|
+ dev_name(&sony_nc_acpi_device->dev), ev_type, real_ev);
|
|
|
}
|
|
|
|
|
|
static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
|
|
@@ -1140,20 +1266,190 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
|
|
|
/*
|
|
|
* ACPI device
|
|
|
*/
|
|
|
-static int sony_nc_function_setup(struct acpi_device *device)
|
|
|
+static void sony_nc_function_setup(struct acpi_device *device,
|
|
|
+ struct platform_device *pf_device)
|
|
|
{
|
|
|
- int result;
|
|
|
+ unsigned int i, result, bitmask, arg;
|
|
|
+
|
|
|
+ if (!handles)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* setup found handles here */
|
|
|
+ for (i = 0; i < ARRAY_SIZE(handles->cap); i++) {
|
|
|
+ unsigned int handle = handles->cap[i];
|
|
|
+
|
|
|
+ if (!handle)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ dprintk("setting up handle 0x%.4x\n", handle);
|
|
|
+
|
|
|
+ switch (handle) {
|
|
|
+ case 0x0100:
|
|
|
+ case 0x0101:
|
|
|
+ case 0x0127:
|
|
|
+ /* setup hotkeys */
|
|
|
+ sony_call_snc_handle(handle, 0, &result);
|
|
|
+ break;
|
|
|
+ case 0x0102:
|
|
|
+ /* setup hotkeys */
|
|
|
+ sony_call_snc_handle(handle, 0x100, &result);
|
|
|
+ break;
|
|
|
+ case 0x0105:
|
|
|
+ case 0x0148:
|
|
|
+ /* touchpad enable/disable */
|
|
|
+ result = sony_nc_touchpad_setup(pf_device, handle);
|
|
|
+ if (result)
|
|
|
+ pr_err("couldn't set up touchpad control function (%d)\n",
|
|
|
+ result);
|
|
|
+ break;
|
|
|
+ case 0x0115:
|
|
|
+ case 0x0136:
|
|
|
+ case 0x013f:
|
|
|
+ result = sony_nc_battery_care_setup(pf_device, handle);
|
|
|
+ if (result)
|
|
|
+ pr_err("couldn't set up battery care function (%d)\n",
|
|
|
+ result);
|
|
|
+ break;
|
|
|
+ case 0x0119:
|
|
|
+ result = sony_nc_lid_resume_setup(pf_device);
|
|
|
+ if (result)
|
|
|
+ pr_err("couldn't set up lid resume function (%d)\n",
|
|
|
+ result);
|
|
|
+ break;
|
|
|
+ case 0x0122:
|
|
|
+ result = sony_nc_thermal_setup(pf_device);
|
|
|
+ if (result)
|
|
|
+ pr_err("couldn't set up thermal profile function (%d)\n",
|
|
|
+ result);
|
|
|
+ break;
|
|
|
+ case 0x0131:
|
|
|
+ result = sony_nc_highspeed_charging_setup(pf_device);
|
|
|
+ if (result)
|
|
|
+ pr_err("couldn't set up high speed charging function (%d)\n",
|
|
|
+ result);
|
|
|
+ break;
|
|
|
+ case 0x0124:
|
|
|
+ case 0x0135:
|
|
|
+ result = sony_nc_rfkill_setup(device, handle);
|
|
|
+ if (result)
|
|
|
+ pr_err("couldn't set up rfkill support (%d)\n",
|
|
|
+ result);
|
|
|
+ break;
|
|
|
+ case 0x0137:
|
|
|
+ case 0x0143:
|
|
|
+ result = sony_nc_kbd_backlight_setup(pf_device, handle);
|
|
|
+ if (result)
|
|
|
+ pr_err("couldn't set up keyboard backlight function (%d)\n",
|
|
|
+ result);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
/* Enable all events */
|
|
|
- acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0xffff, &result);
|
|
|
+ arg = 0x10;
|
|
|
+ if (!sony_nc_int_call(sony_nc_acpi_handle, "SN00", &arg, &bitmask))
|
|
|
+ sony_nc_int_call(sony_nc_acpi_handle, "SN02", &bitmask,
|
|
|
+ &result);
|
|
|
+}
|
|
|
|
|
|
- /* Setup hotkeys */
|
|
|
- sony_call_snc_handle(0x0100, 0, &result);
|
|
|
- sony_call_snc_handle(0x0101, 0, &result);
|
|
|
- sony_call_snc_handle(0x0102, 0x100, &result);
|
|
|
- sony_call_snc_handle(0x0127, 0, &result);
|
|
|
+static void sony_nc_function_cleanup(struct platform_device *pd)
|
|
|
+{
|
|
|
+ unsigned int i, result, bitmask, handle;
|
|
|
|
|
|
- return 0;
|
|
|
+ /* get enabled events and disable them */
|
|
|
+ sony_nc_int_call(sony_nc_acpi_handle, "SN01", NULL, &bitmask);
|
|
|
+ sony_nc_int_call(sony_nc_acpi_handle, "SN03", &bitmask, &result);
|
|
|
+
|
|
|
+ /* cleanup handles here */
|
|
|
+ for (i = 0; i < ARRAY_SIZE(handles->cap); i++) {
|
|
|
+
|
|
|
+ handle = handles->cap[i];
|
|
|
+
|
|
|
+ if (!handle)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ switch (handle) {
|
|
|
+ case 0x0105:
|
|
|
+ case 0x0148:
|
|
|
+ sony_nc_touchpad_cleanup(pd);
|
|
|
+ break;
|
|
|
+ case 0x0115:
|
|
|
+ case 0x0136:
|
|
|
+ case 0x013f:
|
|
|
+ sony_nc_battery_care_cleanup(pd);
|
|
|
+ break;
|
|
|
+ case 0x0119:
|
|
|
+ sony_nc_lid_resume_cleanup(pd);
|
|
|
+ break;
|
|
|
+ case 0x0122:
|
|
|
+ sony_nc_thermal_cleanup(pd);
|
|
|
+ break;
|
|
|
+ case 0x0131:
|
|
|
+ sony_nc_highspeed_charging_cleanup(pd);
|
|
|
+ break;
|
|
|
+ case 0x0124:
|
|
|
+ case 0x0135:
|
|
|
+ sony_nc_rfkill_cleanup();
|
|
|
+ break;
|
|
|
+ case 0x0137:
|
|
|
+ case 0x0143:
|
|
|
+ sony_nc_kbd_backlight_cleanup(pd);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* finally cleanup the handles list */
|
|
|
+ sony_nc_handles_cleanup(pd);
|
|
|
+}
|
|
|
+
|
|
|
+static void sony_nc_function_resume(void)
|
|
|
+{
|
|
|
+ unsigned int i, result, bitmask, arg;
|
|
|
+
|
|
|
+ dprintk("Resuming SNC device\n");
|
|
|
+
|
|
|
+ for (i = 0; i < ARRAY_SIZE(handles->cap); i++) {
|
|
|
+ unsigned int handle = handles->cap[i];
|
|
|
+
|
|
|
+ if (!handle)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ switch (handle) {
|
|
|
+ case 0x0100:
|
|
|
+ case 0x0101:
|
|
|
+ case 0x0127:
|
|
|
+ /* re-enable hotkeys */
|
|
|
+ sony_call_snc_handle(handle, 0, &result);
|
|
|
+ break;
|
|
|
+ case 0x0102:
|
|
|
+ /* re-enable hotkeys */
|
|
|
+ sony_call_snc_handle(handle, 0x100, &result);
|
|
|
+ break;
|
|
|
+ case 0x0122:
|
|
|
+ sony_nc_thermal_resume();
|
|
|
+ break;
|
|
|
+ case 0x0124:
|
|
|
+ case 0x0135:
|
|
|
+ sony_nc_rfkill_update();
|
|
|
+ break;
|
|
|
+ case 0x0137:
|
|
|
+ case 0x0143:
|
|
|
+ sony_nc_kbd_backlight_resume();
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Enable all events */
|
|
|
+ arg = 0x10;
|
|
|
+ if (!sony_nc_int_call(sony_nc_acpi_handle, "SN00", &arg, &bitmask))
|
|
|
+ sony_nc_int_call(sony_nc_acpi_handle, "SN02", &bitmask,
|
|
|
+ &result);
|
|
|
}
|
|
|
|
|
|
static int sony_nc_resume(struct acpi_device *device)
|
|
@@ -1166,8 +1462,8 @@ static int sony_nc_resume(struct acpi_device *device)
|
|
|
|
|
|
if (!item->valid)
|
|
|
continue;
|
|
|
- ret = acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset,
|
|
|
- item->value, NULL);
|
|
|
+ ret = sony_nc_int_call(sony_nc_acpi_handle, *item->acpiset,
|
|
|
+ &item->value, NULL);
|
|
|
if (ret < 0) {
|
|
|
pr_err("%s: %d\n", __func__, ret);
|
|
|
break;
|
|
@@ -1176,21 +1472,14 @@ static int sony_nc_resume(struct acpi_device *device)
|
|
|
|
|
|
if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON",
|
|
|
&handle))) {
|
|
|
- if (acpi_callsetfunc(sony_nc_acpi_handle, "ECON", 1, NULL))
|
|
|
+ int arg = 1;
|
|
|
+ if (sony_nc_int_call(sony_nc_acpi_handle, "ECON", &arg, NULL))
|
|
|
dprintk("ECON Method failed\n");
|
|
|
}
|
|
|
|
|
|
if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00",
|
|
|
- &handle))) {
|
|
|
- dprintk("Doing SNC setup\n");
|
|
|
- sony_nc_function_setup(device);
|
|
|
- }
|
|
|
-
|
|
|
- /* re-read rfkill state */
|
|
|
- sony_nc_rfkill_update();
|
|
|
-
|
|
|
- /* restore kbd backlight states */
|
|
|
- sony_nc_kbd_backlight_resume();
|
|
|
+ &handle)))
|
|
|
+ sony_nc_function_resume();
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -1213,7 +1502,7 @@ static int sony_nc_rfkill_set(void *data, bool blocked)
|
|
|
int argument = sony_rfkill_address[(long) data] + 0x100;
|
|
|
|
|
|
if (!blocked)
|
|
|
- argument |= 0xff0000;
|
|
|
+ argument |= 0x030000;
|
|
|
|
|
|
return sony_call_snc_handle(sony_rfkill_handle, argument, &result);
|
|
|
}
|
|
@@ -1230,7 +1519,7 @@ static int sony_nc_setup_rfkill(struct acpi_device *device,
|
|
|
enum rfkill_type type;
|
|
|
const char *name;
|
|
|
int result;
|
|
|
- bool hwblock;
|
|
|
+ bool hwblock, swblock;
|
|
|
|
|
|
switch (nc_type) {
|
|
|
case SONY_WIFI:
|
|
@@ -1258,8 +1547,21 @@ static int sony_nc_setup_rfkill(struct acpi_device *device,
|
|
|
if (!rfk)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
- sony_call_snc_handle(sony_rfkill_handle, 0x200, &result);
|
|
|
+ if (sony_call_snc_handle(sony_rfkill_handle, 0x200, &result) < 0) {
|
|
|
+ rfkill_destroy(rfk);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
hwblock = !(result & 0x1);
|
|
|
+
|
|
|
+ if (sony_call_snc_handle(sony_rfkill_handle,
|
|
|
+ sony_rfkill_address[nc_type],
|
|
|
+ &result) < 0) {
|
|
|
+ rfkill_destroy(rfk);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ swblock = !(result & 0x2);
|
|
|
+
|
|
|
+ rfkill_init_sw_state(rfk, swblock);
|
|
|
rfkill_set_hw_state(rfk, hwblock);
|
|
|
|
|
|
err = rfkill_register(rfk);
|
|
@@ -1295,101 +1597,79 @@ static void sony_nc_rfkill_update(void)
|
|
|
|
|
|
sony_call_snc_handle(sony_rfkill_handle, argument, &result);
|
|
|
rfkill_set_states(sony_rfkill_devices[i],
|
|
|
- !(result & 0xf), false);
|
|
|
+ !(result & 0x2), false);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static void sony_nc_rfkill_setup(struct acpi_device *device)
|
|
|
+static int sony_nc_rfkill_setup(struct acpi_device *device,
|
|
|
+ unsigned int handle)
|
|
|
{
|
|
|
- int offset;
|
|
|
- u8 dev_code, i;
|
|
|
- acpi_status status;
|
|
|
- struct acpi_object_list params;
|
|
|
- union acpi_object in_obj;
|
|
|
- union acpi_object *device_enum;
|
|
|
- struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
|
|
-
|
|
|
- offset = sony_find_snc_handle(0x124);
|
|
|
- if (offset == -1) {
|
|
|
- offset = sony_find_snc_handle(0x135);
|
|
|
- if (offset == -1)
|
|
|
- return;
|
|
|
- else
|
|
|
- sony_rfkill_handle = 0x135;
|
|
|
- } else
|
|
|
- sony_rfkill_handle = 0x124;
|
|
|
- dprintk("Found rkfill handle: 0x%.4x\n", sony_rfkill_handle);
|
|
|
-
|
|
|
- /* need to read the whole buffer returned by the acpi call to SN06
|
|
|
- * here otherwise we may miss some features
|
|
|
- */
|
|
|
- params.count = 1;
|
|
|
- params.pointer = &in_obj;
|
|
|
- in_obj.type = ACPI_TYPE_INTEGER;
|
|
|
- in_obj.integer.value = offset;
|
|
|
- status = acpi_evaluate_object(sony_nc_acpi_handle, "SN06", ¶ms,
|
|
|
- &buffer);
|
|
|
- if (ACPI_FAILURE(status)) {
|
|
|
- dprintk("Radio device enumeration failed\n");
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- device_enum = (union acpi_object *) buffer.pointer;
|
|
|
- if (!device_enum) {
|
|
|
- pr_err("No SN06 return object\n");
|
|
|
- goto out_no_enum;
|
|
|
- }
|
|
|
- if (device_enum->type != ACPI_TYPE_BUFFER) {
|
|
|
- pr_err("Invalid SN06 return object 0x%.2x\n",
|
|
|
- device_enum->type);
|
|
|
- goto out_no_enum;
|
|
|
- }
|
|
|
+ u64 offset;
|
|
|
+ int i;
|
|
|
+ unsigned char buffer[32] = { 0 };
|
|
|
|
|
|
- /* the buffer is filled with magic numbers describing the devices
|
|
|
- * available, 0xff terminates the enumeration
|
|
|
+ offset = sony_find_snc_handle(handle);
|
|
|
+ sony_rfkill_handle = handle;
|
|
|
+
|
|
|
+ i = sony_nc_buffer_call(sony_nc_acpi_handle, "SN06", &offset, buffer,
|
|
|
+ 32);
|
|
|
+ if (i < 0)
|
|
|
+ return i;
|
|
|
+
|
|
|
+ /* The buffer is filled with magic numbers describing the devices
|
|
|
+ * available, 0xff terminates the enumeration.
|
|
|
+ * Known codes:
|
|
|
+ * 0x00 WLAN
|
|
|
+ * 0x10 BLUETOOTH
|
|
|
+ * 0x20 WWAN GPRS-EDGE
|
|
|
+ * 0x21 WWAN HSDPA
|
|
|
+ * 0x22 WWAN EV-DO
|
|
|
+ * 0x23 WWAN GPS
|
|
|
+ * 0x25 Gobi WWAN no GPS
|
|
|
+ * 0x26 Gobi WWAN + GPS
|
|
|
+ * 0x28 Gobi WWAN no GPS
|
|
|
+ * 0x29 Gobi WWAN + GPS
|
|
|
+ * 0x30 WIMAX
|
|
|
+ * 0x50 Gobi WWAN no GPS
|
|
|
+ * 0x51 Gobi WWAN + GPS
|
|
|
+ * 0x70 no SIM card slot
|
|
|
+ * 0x71 SIM card slot
|
|
|
*/
|
|
|
- for (i = 0; i < device_enum->buffer.length; i++) {
|
|
|
+ for (i = 0; i < ARRAY_SIZE(buffer); i++) {
|
|
|
|
|
|
- dev_code = *(device_enum->buffer.pointer + i);
|
|
|
- if (dev_code == 0xff)
|
|
|
+ if (buffer[i] == 0xff)
|
|
|
break;
|
|
|
|
|
|
- dprintk("Radio devices, looking at 0x%.2x\n", dev_code);
|
|
|
+ dprintk("Radio devices, found 0x%.2x\n", buffer[i]);
|
|
|
|
|
|
- if (dev_code == 0 && !sony_rfkill_devices[SONY_WIFI])
|
|
|
+ if (buffer[i] == 0 && !sony_rfkill_devices[SONY_WIFI])
|
|
|
sony_nc_setup_rfkill(device, SONY_WIFI);
|
|
|
|
|
|
- if (dev_code == 0x10 && !sony_rfkill_devices[SONY_BLUETOOTH])
|
|
|
+ if (buffer[i] == 0x10 && !sony_rfkill_devices[SONY_BLUETOOTH])
|
|
|
sony_nc_setup_rfkill(device, SONY_BLUETOOTH);
|
|
|
|
|
|
- if ((0xf0 & dev_code) == 0x20 &&
|
|
|
+ if (((0xf0 & buffer[i]) == 0x20 ||
|
|
|
+ (0xf0 & buffer[i]) == 0x50) &&
|
|
|
!sony_rfkill_devices[SONY_WWAN])
|
|
|
sony_nc_setup_rfkill(device, SONY_WWAN);
|
|
|
|
|
|
- if (dev_code == 0x30 && !sony_rfkill_devices[SONY_WIMAX])
|
|
|
+ if (buffer[i] == 0x30 && !sony_rfkill_devices[SONY_WIMAX])
|
|
|
sony_nc_setup_rfkill(device, SONY_WIMAX);
|
|
|
}
|
|
|
-
|
|
|
-out_no_enum:
|
|
|
- kfree(buffer.pointer);
|
|
|
- return;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
/* Keyboard backlight feature */
|
|
|
-#define KBDBL_HANDLER 0x137
|
|
|
-#define KBDBL_PRESENT 0xB00
|
|
|
-#define SET_MODE 0xC00
|
|
|
-#define SET_STATE 0xD00
|
|
|
-#define SET_TIMEOUT 0xE00
|
|
|
-
|
|
|
struct kbd_backlight {
|
|
|
- int mode;
|
|
|
- int timeout;
|
|
|
+ unsigned int handle;
|
|
|
+ unsigned int base;
|
|
|
+ unsigned int mode;
|
|
|
+ unsigned int timeout;
|
|
|
struct device_attribute mode_attr;
|
|
|
struct device_attribute timeout_attr;
|
|
|
};
|
|
|
|
|
|
-static struct kbd_backlight *kbdbl_handle;
|
|
|
+static struct kbd_backlight *kbdbl_ctl;
|
|
|
|
|
|
static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value)
|
|
|
{
|
|
@@ -1398,15 +1678,15 @@ static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value)
|
|
|
if (value > 1)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- if (sony_call_snc_handle(KBDBL_HANDLER,
|
|
|
- (value << 0x10) | SET_MODE, &result))
|
|
|
+ if (sony_call_snc_handle(kbdbl_ctl->handle,
|
|
|
+ (value << 0x10) | (kbdbl_ctl->base), &result))
|
|
|
return -EIO;
|
|
|
|
|
|
/* Try to turn the light on/off immediately */
|
|
|
- sony_call_snc_handle(KBDBL_HANDLER, (value << 0x10) | SET_STATE,
|
|
|
- &result);
|
|
|
+ sony_call_snc_handle(kbdbl_ctl->handle,
|
|
|
+ (value << 0x10) | (kbdbl_ctl->base + 0x100), &result);
|
|
|
|
|
|
- kbdbl_handle->mode = value;
|
|
|
+ kbdbl_ctl->mode = value;
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -1421,7 +1701,7 @@ static ssize_t sony_nc_kbd_backlight_mode_store(struct device *dev,
|
|
|
if (count > 31)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- if (strict_strtoul(buffer, 10, &value))
|
|
|
+ if (kstrtoul(buffer, 10, &value))
|
|
|
return -EINVAL;
|
|
|
|
|
|
ret = __sony_nc_kbd_backlight_mode_set(value);
|
|
@@ -1435,7 +1715,7 @@ static ssize_t sony_nc_kbd_backlight_mode_show(struct device *dev,
|
|
|
struct device_attribute *attr, char *buffer)
|
|
|
{
|
|
|
ssize_t count = 0;
|
|
|
- count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_handle->mode);
|
|
|
+ count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_ctl->mode);
|
|
|
return count;
|
|
|
}
|
|
|
|
|
@@ -1446,11 +1726,11 @@ static int __sony_nc_kbd_backlight_timeout_set(u8 value)
|
|
|
if (value > 3)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- if (sony_call_snc_handle(KBDBL_HANDLER,
|
|
|
- (value << 0x10) | SET_TIMEOUT, &result))
|
|
|
+ if (sony_call_snc_handle(kbdbl_ctl->handle, (value << 0x10) |
|
|
|
+ (kbdbl_ctl->base + 0x200), &result))
|
|
|
return -EIO;
|
|
|
|
|
|
- kbdbl_handle->timeout = value;
|
|
|
+ kbdbl_ctl->timeout = value;
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -1465,7 +1745,7 @@ static ssize_t sony_nc_kbd_backlight_timeout_store(struct device *dev,
|
|
|
if (count > 31)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- if (strict_strtoul(buffer, 10, &value))
|
|
|
+ if (kstrtoul(buffer, 10, &value))
|
|
|
return -EINVAL;
|
|
|
|
|
|
ret = __sony_nc_kbd_backlight_timeout_set(value);
|
|
@@ -1479,39 +1759,58 @@ static ssize_t sony_nc_kbd_backlight_timeout_show(struct device *dev,
|
|
|
struct device_attribute *attr, char *buffer)
|
|
|
{
|
|
|
ssize_t count = 0;
|
|
|
- count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_handle->timeout);
|
|
|
+ count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_ctl->timeout);
|
|
|
return count;
|
|
|
}
|
|
|
|
|
|
-static int sony_nc_kbd_backlight_setup(struct platform_device *pd)
|
|
|
+static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
|
|
|
+ unsigned int handle)
|
|
|
{
|
|
|
int result;
|
|
|
+ int ret = 0;
|
|
|
|
|
|
- if (sony_call_snc_handle(KBDBL_HANDLER, KBDBL_PRESENT, &result))
|
|
|
- return 0;
|
|
|
- if (!(result & 0x02))
|
|
|
+ /* verify the kbd backlight presence, these handles are not used for
|
|
|
+ * keyboard backlight only
|
|
|
+ */
|
|
|
+ ret = sony_call_snc_handle(handle, handle == 0x0137 ? 0x0B00 : 0x0100,
|
|
|
+ &result);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ if ((handle == 0x0137 && !(result & 0x02)) ||
|
|
|
+ !(result & 0x01)) {
|
|
|
+ dprintk("no backlight keyboard found\n");
|
|
|
return 0;
|
|
|
+ }
|
|
|
|
|
|
- kbdbl_handle = kzalloc(sizeof(*kbdbl_handle), GFP_KERNEL);
|
|
|
- if (!kbdbl_handle)
|
|
|
+ kbdbl_ctl = kzalloc(sizeof(*kbdbl_ctl), GFP_KERNEL);
|
|
|
+ if (!kbdbl_ctl)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
- sysfs_attr_init(&kbdbl_handle->mode_attr.attr);
|
|
|
- kbdbl_handle->mode_attr.attr.name = "kbd_backlight";
|
|
|
- kbdbl_handle->mode_attr.attr.mode = S_IRUGO | S_IWUSR;
|
|
|
- kbdbl_handle->mode_attr.show = sony_nc_kbd_backlight_mode_show;
|
|
|
- kbdbl_handle->mode_attr.store = sony_nc_kbd_backlight_mode_store;
|
|
|
+ kbdbl_ctl->handle = handle;
|
|
|
+ if (handle == 0x0137)
|
|
|
+ kbdbl_ctl->base = 0x0C00;
|
|
|
+ else
|
|
|
+ kbdbl_ctl->base = 0x4000;
|
|
|
+
|
|
|
+ sysfs_attr_init(&kbdbl_ctl->mode_attr.attr);
|
|
|
+ kbdbl_ctl->mode_attr.attr.name = "kbd_backlight";
|
|
|
+ kbdbl_ctl->mode_attr.attr.mode = S_IRUGO | S_IWUSR;
|
|
|
+ kbdbl_ctl->mode_attr.show = sony_nc_kbd_backlight_mode_show;
|
|
|
+ kbdbl_ctl->mode_attr.store = sony_nc_kbd_backlight_mode_store;
|
|
|
|
|
|
- sysfs_attr_init(&kbdbl_handle->timeout_attr.attr);
|
|
|
- kbdbl_handle->timeout_attr.attr.name = "kbd_backlight_timeout";
|
|
|
- kbdbl_handle->timeout_attr.attr.mode = S_IRUGO | S_IWUSR;
|
|
|
- kbdbl_handle->timeout_attr.show = sony_nc_kbd_backlight_timeout_show;
|
|
|
- kbdbl_handle->timeout_attr.store = sony_nc_kbd_backlight_timeout_store;
|
|
|
+ sysfs_attr_init(&kbdbl_ctl->timeout_attr.attr);
|
|
|
+ kbdbl_ctl->timeout_attr.attr.name = "kbd_backlight_timeout";
|
|
|
+ kbdbl_ctl->timeout_attr.attr.mode = S_IRUGO | S_IWUSR;
|
|
|
+ kbdbl_ctl->timeout_attr.show = sony_nc_kbd_backlight_timeout_show;
|
|
|
+ kbdbl_ctl->timeout_attr.store = sony_nc_kbd_backlight_timeout_store;
|
|
|
|
|
|
- if (device_create_file(&pd->dev, &kbdbl_handle->mode_attr))
|
|
|
+ ret = device_create_file(&pd->dev, &kbdbl_ctl->mode_attr);
|
|
|
+ if (ret)
|
|
|
goto outkzalloc;
|
|
|
|
|
|
- if (device_create_file(&pd->dev, &kbdbl_handle->timeout_attr))
|
|
|
+ ret = device_create_file(&pd->dev, &kbdbl_ctl->timeout_attr);
|
|
|
+ if (ret)
|
|
|
goto outmode;
|
|
|
|
|
|
__sony_nc_kbd_backlight_mode_set(kbd_backlight);
|
|
@@ -1520,131 +1819,716 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd)
|
|
|
return 0;
|
|
|
|
|
|
outmode:
|
|
|
- device_remove_file(&pd->dev, &kbdbl_handle->mode_attr);
|
|
|
+ device_remove_file(&pd->dev, &kbdbl_ctl->mode_attr);
|
|
|
outkzalloc:
|
|
|
- kfree(kbdbl_handle);
|
|
|
- kbdbl_handle = NULL;
|
|
|
- return -1;
|
|
|
+ kfree(kbdbl_ctl);
|
|
|
+ kbdbl_ctl = NULL;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
-static int sony_nc_kbd_backlight_cleanup(struct platform_device *pd)
|
|
|
+static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd)
|
|
|
{
|
|
|
- if (kbdbl_handle) {
|
|
|
+ if (kbdbl_ctl) {
|
|
|
int result;
|
|
|
|
|
|
- device_remove_file(&pd->dev, &kbdbl_handle->mode_attr);
|
|
|
- device_remove_file(&pd->dev, &kbdbl_handle->timeout_attr);
|
|
|
+ device_remove_file(&pd->dev, &kbdbl_ctl->mode_attr);
|
|
|
+ device_remove_file(&pd->dev, &kbdbl_ctl->timeout_attr);
|
|
|
|
|
|
/* restore the default hw behaviour */
|
|
|
- sony_call_snc_handle(KBDBL_HANDLER, 0x1000 | SET_MODE, &result);
|
|
|
- sony_call_snc_handle(KBDBL_HANDLER, SET_TIMEOUT, &result);
|
|
|
+ sony_call_snc_handle(kbdbl_ctl->handle,
|
|
|
+ kbdbl_ctl->base | 0x10000, &result);
|
|
|
+ sony_call_snc_handle(kbdbl_ctl->handle,
|
|
|
+ kbdbl_ctl->base + 0x200, &result);
|
|
|
|
|
|
- kfree(kbdbl_handle);
|
|
|
+ kfree(kbdbl_ctl);
|
|
|
+ kbdbl_ctl = NULL;
|
|
|
}
|
|
|
- return 0;
|
|
|
}
|
|
|
|
|
|
static void sony_nc_kbd_backlight_resume(void)
|
|
|
{
|
|
|
int ignore = 0;
|
|
|
|
|
|
- if (!kbdbl_handle)
|
|
|
+ if (!kbdbl_ctl)
|
|
|
return;
|
|
|
|
|
|
- if (kbdbl_handle->mode == 0)
|
|
|
- sony_call_snc_handle(KBDBL_HANDLER, SET_MODE, &ignore);
|
|
|
-
|
|
|
- if (kbdbl_handle->timeout != 0)
|
|
|
- sony_call_snc_handle(KBDBL_HANDLER,
|
|
|
- (kbdbl_handle->timeout << 0x10) | SET_TIMEOUT,
|
|
|
+ if (kbdbl_ctl->mode == 0)
|
|
|
+ sony_call_snc_handle(kbdbl_ctl->handle, kbdbl_ctl->base,
|
|
|
&ignore);
|
|
|
+
|
|
|
+ if (kbdbl_ctl->timeout != 0)
|
|
|
+ sony_call_snc_handle(kbdbl_ctl->handle,
|
|
|
+ (kbdbl_ctl->base + 0x200) |
|
|
|
+ (kbdbl_ctl->timeout << 0x10), &ignore);
|
|
|
}
|
|
|
|
|
|
-static void sony_nc_backlight_ng_read_limits(int handle,
|
|
|
- struct sony_backlight_props *props)
|
|
|
+struct battery_care_control {
|
|
|
+ struct device_attribute attrs[2];
|
|
|
+ unsigned int handle;
|
|
|
+};
|
|
|
+static struct battery_care_control *bcare_ctl;
|
|
|
+
|
|
|
+static ssize_t sony_nc_battery_care_limit_store(struct device *dev,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ const char *buffer, size_t count)
|
|
|
{
|
|
|
- int offset;
|
|
|
- acpi_status status;
|
|
|
- u8 brlvl, i;
|
|
|
- u8 min = 0xff, max = 0x00;
|
|
|
- struct acpi_object_list params;
|
|
|
- union acpi_object in_obj;
|
|
|
- union acpi_object *lvl_enum;
|
|
|
- struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
|
|
+ unsigned int result, cmd;
|
|
|
+ unsigned long value;
|
|
|
|
|
|
- props->handle = handle;
|
|
|
- props->offset = 0;
|
|
|
- props->maxlvl = 0xff;
|
|
|
+ if (count > 31)
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- offset = sony_find_snc_handle(handle);
|
|
|
- if (offset < 0)
|
|
|
- return;
|
|
|
+ if (kstrtoul(buffer, 10, &value))
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- /* try to read the boundaries from ACPI tables, if we fail the above
|
|
|
- * defaults should be reasonable
|
|
|
+ /* limit values (2 bits):
|
|
|
+ * 00 - none
|
|
|
+ * 01 - 80%
|
|
|
+ * 10 - 50%
|
|
|
+ * 11 - 100%
|
|
|
+ *
|
|
|
+ * bit 0: 0 disable BCL, 1 enable BCL
|
|
|
+ * bit 1: 1 tell to store the battery limit (see bits 6,7) too
|
|
|
+ * bits 2,3: reserved
|
|
|
+ * bits 4,5: store the limit into the EC
|
|
|
+ * bits 6,7: store the limit into the battery
|
|
|
*/
|
|
|
- params.count = 1;
|
|
|
- params.pointer = &in_obj;
|
|
|
- in_obj.type = ACPI_TYPE_INTEGER;
|
|
|
- in_obj.integer.value = offset;
|
|
|
- status = acpi_evaluate_object(sony_nc_acpi_handle, "SN06", ¶ms,
|
|
|
- &buffer);
|
|
|
- if (ACPI_FAILURE(status))
|
|
|
- return;
|
|
|
-
|
|
|
- lvl_enum = (union acpi_object *) buffer.pointer;
|
|
|
- if (!lvl_enum) {
|
|
|
- pr_err("No SN06 return object.");
|
|
|
- return;
|
|
|
- }
|
|
|
- if (lvl_enum->type != ACPI_TYPE_BUFFER) {
|
|
|
- pr_err("Invalid SN06 return object 0x%.2x\n",
|
|
|
- lvl_enum->type);
|
|
|
- goto out_invalid;
|
|
|
- }
|
|
|
|
|
|
- /* the buffer lists brightness levels available, brightness levels are
|
|
|
- * from 0 to 8 in the array, other values are used by ALS control.
|
|
|
+ /*
|
|
|
+ * handle 0x0115 should allow storing on battery too;
|
|
|
+ * handle 0x0136 same as 0x0115 + health status;
|
|
|
+ * handle 0x013f, same as 0x0136 but no storing on the battery
|
|
|
+ *
|
|
|
+ * Store only inside the EC for now, regardless the handle number
|
|
|
*/
|
|
|
- for (i = 0; i < 9 && i < lvl_enum->buffer.length; i++) {
|
|
|
+ if (value == 0)
|
|
|
+ /* disable limits */
|
|
|
+ cmd = 0x0;
|
|
|
|
|
|
- brlvl = *(lvl_enum->buffer.pointer + i);
|
|
|
- dprintk("Brightness level: %d\n", brlvl);
|
|
|
+ else if (value <= 50)
|
|
|
+ cmd = 0x21;
|
|
|
|
|
|
- if (!brlvl)
|
|
|
- break;
|
|
|
+ else if (value <= 80)
|
|
|
+ cmd = 0x11;
|
|
|
|
|
|
- if (brlvl > max)
|
|
|
- max = brlvl;
|
|
|
- if (brlvl < min)
|
|
|
- min = brlvl;
|
|
|
- }
|
|
|
- props->offset = min;
|
|
|
- props->maxlvl = max;
|
|
|
- dprintk("Brightness levels: min=%d max=%d\n", props->offset,
|
|
|
- props->maxlvl);
|
|
|
+ else if (value <= 100)
|
|
|
+ cmd = 0x31;
|
|
|
+
|
|
|
+ else
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (sony_call_snc_handle(bcare_ctl->handle, (cmd << 0x10) | 0x0100,
|
|
|
+ &result))
|
|
|
+ return -EIO;
|
|
|
|
|
|
-out_invalid:
|
|
|
- kfree(buffer.pointer);
|
|
|
- return;
|
|
|
+ return count;
|
|
|
}
|
|
|
|
|
|
-static void sony_nc_backlight_setup(void)
|
|
|
+static ssize_t sony_nc_battery_care_limit_show(struct device *dev,
|
|
|
+ struct device_attribute *attr, char *buffer)
|
|
|
{
|
|
|
- acpi_handle unused;
|
|
|
- int max_brightness = 0;
|
|
|
- const struct backlight_ops *ops = NULL;
|
|
|
- struct backlight_properties props;
|
|
|
+ unsigned int result, status;
|
|
|
|
|
|
- if (sony_find_snc_handle(0x12f) != -1) {
|
|
|
- ops = &sony_backlight_ng_ops;
|
|
|
- sony_nc_backlight_ng_read_limits(0x12f, &sony_bl_props);
|
|
|
- max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;
|
|
|
+ if (sony_call_snc_handle(bcare_ctl->handle, 0x0000, &result))
|
|
|
+ return -EIO;
|
|
|
|
|
|
- } else if (sony_find_snc_handle(0x137) != -1) {
|
|
|
- ops = &sony_backlight_ng_ops;
|
|
|
- sony_nc_backlight_ng_read_limits(0x137, &sony_bl_props);
|
|
|
- max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;
|
|
|
+ status = (result & 0x01) ? ((result & 0x30) >> 0x04) : 0;
|
|
|
+ switch (status) {
|
|
|
+ case 1:
|
|
|
+ status = 80;
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ status = 50;
|
|
|
+ break;
|
|
|
+ case 3:
|
|
|
+ status = 100;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ status = 0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return snprintf(buffer, PAGE_SIZE, "%d\n", status);
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t sony_nc_battery_care_health_show(struct device *dev,
|
|
|
+ struct device_attribute *attr, char *buffer)
|
|
|
+{
|
|
|
+ ssize_t count = 0;
|
|
|
+ unsigned int health;
|
|
|
+
|
|
|
+ if (sony_call_snc_handle(bcare_ctl->handle, 0x0200, &health))
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ count = snprintf(buffer, PAGE_SIZE, "%d\n", health & 0xff);
|
|
|
+
|
|
|
+ return count;
|
|
|
+}
|
|
|
+
|
|
|
+static int sony_nc_battery_care_setup(struct platform_device *pd,
|
|
|
+ unsigned int handle)
|
|
|
+{
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ bcare_ctl = kzalloc(sizeof(struct battery_care_control), GFP_KERNEL);
|
|
|
+ if (!bcare_ctl)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ bcare_ctl->handle = handle;
|
|
|
+
|
|
|
+ sysfs_attr_init(&bcare_ctl->attrs[0].attr);
|
|
|
+ bcare_ctl->attrs[0].attr.name = "battery_care_limiter";
|
|
|
+ bcare_ctl->attrs[0].attr.mode = S_IRUGO | S_IWUSR;
|
|
|
+ bcare_ctl->attrs[0].show = sony_nc_battery_care_limit_show;
|
|
|
+ bcare_ctl->attrs[0].store = sony_nc_battery_care_limit_store;
|
|
|
+
|
|
|
+ ret = device_create_file(&pd->dev, &bcare_ctl->attrs[0]);
|
|
|
+ if (ret)
|
|
|
+ goto outkzalloc;
|
|
|
+
|
|
|
+ /* 0x0115 is for models with no health reporting capability */
|
|
|
+ if (handle == 0x0115)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ sysfs_attr_init(&bcare_ctl->attrs[1].attr);
|
|
|
+ bcare_ctl->attrs[1].attr.name = "battery_care_health";
|
|
|
+ bcare_ctl->attrs[1].attr.mode = S_IRUGO;
|
|
|
+ bcare_ctl->attrs[1].show = sony_nc_battery_care_health_show;
|
|
|
+
|
|
|
+ ret = device_create_file(&pd->dev, &bcare_ctl->attrs[1]);
|
|
|
+ if (ret)
|
|
|
+ goto outlimiter;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+outlimiter:
|
|
|
+ device_remove_file(&pd->dev, &bcare_ctl->attrs[0]);
|
|
|
+
|
|
|
+outkzalloc:
|
|
|
+ kfree(bcare_ctl);
|
|
|
+ bcare_ctl = NULL;
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void sony_nc_battery_care_cleanup(struct platform_device *pd)
|
|
|
+{
|
|
|
+ if (bcare_ctl) {
|
|
|
+ device_remove_file(&pd->dev, &bcare_ctl->attrs[0]);
|
|
|
+ if (bcare_ctl->handle != 0x0115)
|
|
|
+ device_remove_file(&pd->dev, &bcare_ctl->attrs[1]);
|
|
|
+
|
|
|
+ kfree(bcare_ctl);
|
|
|
+ bcare_ctl = NULL;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+struct snc_thermal_ctrl {
|
|
|
+ unsigned int mode;
|
|
|
+ unsigned int profiles;
|
|
|
+ struct device_attribute mode_attr;
|
|
|
+ struct device_attribute profiles_attr;
|
|
|
+};
|
|
|
+static struct snc_thermal_ctrl *th_handle;
|
|
|
+
|
|
|
+#define THM_PROFILE_MAX 3
|
|
|
+static const char * const snc_thermal_profiles[] = {
|
|
|
+ "balanced",
|
|
|
+ "silent",
|
|
|
+ "performance"
|
|
|
+};
|
|
|
+
|
|
|
+static int sony_nc_thermal_mode_set(unsigned short mode)
|
|
|
+{
|
|
|
+ unsigned int result;
|
|
|
+
|
|
|
+ /* the thermal profile seems to be a two bit bitmask:
|
|
|
+ * lsb -> silent
|
|
|
+ * msb -> performance
|
|
|
+ * no bit set is the normal operation and is always valid
|
|
|
+ * Some vaio models only have "balanced" and "performance"
|
|
|
+ */
|
|
|
+ if ((mode && !(th_handle->profiles & mode)) || mode >= THM_PROFILE_MAX)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (sony_call_snc_handle(0x0122, mode << 0x10 | 0x0200, &result))
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ th_handle->mode = mode;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int sony_nc_thermal_mode_get(void)
|
|
|
+{
|
|
|
+ unsigned int result;
|
|
|
+
|
|
|
+ if (sony_call_snc_handle(0x0122, 0x0100, &result))
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ return result & 0xff;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t sony_nc_thermal_profiles_show(struct device *dev,
|
|
|
+ struct device_attribute *attr, char *buffer)
|
|
|
+{
|
|
|
+ short cnt;
|
|
|
+ size_t idx = 0;
|
|
|
+
|
|
|
+ for (cnt = 0; cnt < THM_PROFILE_MAX; cnt++) {
|
|
|
+ if (!cnt || (th_handle->profiles & cnt))
|
|
|
+ idx += snprintf(buffer + idx, PAGE_SIZE - idx, "%s ",
|
|
|
+ snc_thermal_profiles[cnt]);
|
|
|
+ }
|
|
|
+ idx += snprintf(buffer + idx, PAGE_SIZE - idx, "\n");
|
|
|
+
|
|
|
+ return idx;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t sony_nc_thermal_mode_store(struct device *dev,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ const char *buffer, size_t count)
|
|
|
+{
|
|
|
+ unsigned short cmd;
|
|
|
+ size_t len = count;
|
|
|
+
|
|
|
+ if (count == 0)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ /* skip the newline if present */
|
|
|
+ if (buffer[len - 1] == '\n')
|
|
|
+ len--;
|
|
|
+
|
|
|
+ for (cmd = 0; cmd < THM_PROFILE_MAX; cmd++)
|
|
|
+ if (strncmp(buffer, snc_thermal_profiles[cmd], len) == 0)
|
|
|
+ break;
|
|
|
+
|
|
|
+ if (sony_nc_thermal_mode_set(cmd))
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ return count;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t sony_nc_thermal_mode_show(struct device *dev,
|
|
|
+ struct device_attribute *attr, char *buffer)
|
|
|
+{
|
|
|
+ ssize_t count = 0;
|
|
|
+ unsigned int mode = sony_nc_thermal_mode_get();
|
|
|
+
|
|
|
+ if (mode < 0)
|
|
|
+ return mode;
|
|
|
+
|
|
|
+ count = snprintf(buffer, PAGE_SIZE, "%s\n", snc_thermal_profiles[mode]);
|
|
|
+
|
|
|
+ return count;
|
|
|
+}
|
|
|
+
|
|
|
+static int sony_nc_thermal_setup(struct platform_device *pd)
|
|
|
+{
|
|
|
+ int ret = 0;
|
|
|
+ th_handle = kzalloc(sizeof(struct snc_thermal_ctrl), GFP_KERNEL);
|
|
|
+ if (!th_handle)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ ret = sony_call_snc_handle(0x0122, 0x0000, &th_handle->profiles);
|
|
|
+ if (ret) {
|
|
|
+ pr_warn("couldn't to read the thermal profiles\n");
|
|
|
+ goto outkzalloc;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = sony_nc_thermal_mode_get();
|
|
|
+ if (ret < 0) {
|
|
|
+ pr_warn("couldn't to read the current thermal profile");
|
|
|
+ goto outkzalloc;
|
|
|
+ }
|
|
|
+ th_handle->mode = ret;
|
|
|
+
|
|
|
+ sysfs_attr_init(&th_handle->profiles_attr.attr);
|
|
|
+ th_handle->profiles_attr.attr.name = "thermal_profiles";
|
|
|
+ th_handle->profiles_attr.attr.mode = S_IRUGO;
|
|
|
+ th_handle->profiles_attr.show = sony_nc_thermal_profiles_show;
|
|
|
+
|
|
|
+ sysfs_attr_init(&th_handle->mode_attr.attr);
|
|
|
+ th_handle->mode_attr.attr.name = "thermal_control";
|
|
|
+ th_handle->mode_attr.attr.mode = S_IRUGO | S_IWUSR;
|
|
|
+ th_handle->mode_attr.show = sony_nc_thermal_mode_show;
|
|
|
+ th_handle->mode_attr.store = sony_nc_thermal_mode_store;
|
|
|
+
|
|
|
+ ret = device_create_file(&pd->dev, &th_handle->profiles_attr);
|
|
|
+ if (ret)
|
|
|
+ goto outkzalloc;
|
|
|
+
|
|
|
+ ret = device_create_file(&pd->dev, &th_handle->mode_attr);
|
|
|
+ if (ret)
|
|
|
+ goto outprofiles;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+outprofiles:
|
|
|
+ device_remove_file(&pd->dev, &th_handle->profiles_attr);
|
|
|
+outkzalloc:
|
|
|
+ kfree(th_handle);
|
|
|
+ th_handle = NULL;
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void sony_nc_thermal_cleanup(struct platform_device *pd)
|
|
|
+{
|
|
|
+ if (th_handle) {
|
|
|
+ device_remove_file(&pd->dev, &th_handle->profiles_attr);
|
|
|
+ device_remove_file(&pd->dev, &th_handle->mode_attr);
|
|
|
+ kfree(th_handle);
|
|
|
+ th_handle = NULL;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void sony_nc_thermal_resume(void)
|
|
|
+{
|
|
|
+ unsigned int status = sony_nc_thermal_mode_get();
|
|
|
+
|
|
|
+ if (status != th_handle->mode)
|
|
|
+ sony_nc_thermal_mode_set(th_handle->mode);
|
|
|
+}
|
|
|
+
|
|
|
+/* resume on LID open */
|
|
|
+struct snc_lid_resume_control {
|
|
|
+ struct device_attribute attrs[3];
|
|
|
+ unsigned int status;
|
|
|
+};
|
|
|
+static struct snc_lid_resume_control *lid_ctl;
|
|
|
+
|
|
|
+static ssize_t sony_nc_lid_resume_store(struct device *dev,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ const char *buffer, size_t count)
|
|
|
+{
|
|
|
+ unsigned int result, pos;
|
|
|
+ unsigned long value;
|
|
|
+ if (count > 31)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (kstrtoul(buffer, 10, &value) || value > 1)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ /* the value we have to write to SNC is a bitmask:
|
|
|
+ * +--------------+
|
|
|
+ * | S3 | S4 | S5 |
|
|
|
+ * +--------------+
|
|
|
+ * 2 1 0
|
|
|
+ */
|
|
|
+ if (strcmp(attr->attr.name, "lid_resume_S3") == 0)
|
|
|
+ pos = 2;
|
|
|
+ else if (strcmp(attr->attr.name, "lid_resume_S4") == 0)
|
|
|
+ pos = 1;
|
|
|
+ else if (strcmp(attr->attr.name, "lid_resume_S5") == 0)
|
|
|
+ pos = 0;
|
|
|
+ else
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (value)
|
|
|
+ value = lid_ctl->status | (1 << pos);
|
|
|
+ else
|
|
|
+ value = lid_ctl->status & ~(1 << pos);
|
|
|
+
|
|
|
+ if (sony_call_snc_handle(0x0119, value << 0x10 | 0x0100, &result))
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ lid_ctl->status = value;
|
|
|
+
|
|
|
+ return count;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t sony_nc_lid_resume_show(struct device *dev,
|
|
|
+ struct device_attribute *attr, char *buffer)
|
|
|
+{
|
|
|
+ unsigned int pos;
|
|
|
+
|
|
|
+ if (strcmp(attr->attr.name, "lid_resume_S3") == 0)
|
|
|
+ pos = 2;
|
|
|
+ else if (strcmp(attr->attr.name, "lid_resume_S4") == 0)
|
|
|
+ pos = 1;
|
|
|
+ else if (strcmp(attr->attr.name, "lid_resume_S5") == 0)
|
|
|
+ pos = 0;
|
|
|
+ else
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ return snprintf(buffer, PAGE_SIZE, "%d\n",
|
|
|
+ (lid_ctl->status >> pos) & 0x01);
|
|
|
+}
|
|
|
+
|
|
|
+static int sony_nc_lid_resume_setup(struct platform_device *pd)
|
|
|
+{
|
|
|
+ unsigned int result;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (sony_call_snc_handle(0x0119, 0x0000, &result))
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ lid_ctl = kzalloc(sizeof(struct snc_lid_resume_control), GFP_KERNEL);
|
|
|
+ if (!lid_ctl)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ lid_ctl->status = result & 0x7;
|
|
|
+
|
|
|
+ sysfs_attr_init(&lid_ctl->attrs[0].attr);
|
|
|
+ lid_ctl->attrs[0].attr.name = "lid_resume_S3";
|
|
|
+ lid_ctl->attrs[0].attr.mode = S_IRUGO | S_IWUSR;
|
|
|
+ lid_ctl->attrs[0].show = sony_nc_lid_resume_show;
|
|
|
+ lid_ctl->attrs[0].store = sony_nc_lid_resume_store;
|
|
|
+
|
|
|
+ sysfs_attr_init(&lid_ctl->attrs[1].attr);
|
|
|
+ lid_ctl->attrs[1].attr.name = "lid_resume_S4";
|
|
|
+ lid_ctl->attrs[1].attr.mode = S_IRUGO | S_IWUSR;
|
|
|
+ lid_ctl->attrs[1].show = sony_nc_lid_resume_show;
|
|
|
+ lid_ctl->attrs[1].store = sony_nc_lid_resume_store;
|
|
|
+
|
|
|
+ sysfs_attr_init(&lid_ctl->attrs[2].attr);
|
|
|
+ lid_ctl->attrs[2].attr.name = "lid_resume_S5";
|
|
|
+ lid_ctl->attrs[2].attr.mode = S_IRUGO | S_IWUSR;
|
|
|
+ lid_ctl->attrs[2].show = sony_nc_lid_resume_show;
|
|
|
+ lid_ctl->attrs[2].store = sony_nc_lid_resume_store;
|
|
|
+
|
|
|
+ for (i = 0; i < 3; i++) {
|
|
|
+ result = device_create_file(&pd->dev, &lid_ctl->attrs[i]);
|
|
|
+ if (result)
|
|
|
+ goto liderror;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+liderror:
|
|
|
+ for (; i > 0; i--)
|
|
|
+ device_remove_file(&pd->dev, &lid_ctl->attrs[i]);
|
|
|
+
|
|
|
+ kfree(lid_ctl);
|
|
|
+ lid_ctl = NULL;
|
|
|
+
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+static void sony_nc_lid_resume_cleanup(struct platform_device *pd)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (lid_ctl) {
|
|
|
+ for (i = 0; i < 3; i++)
|
|
|
+ device_remove_file(&pd->dev, &lid_ctl->attrs[i]);
|
|
|
+
|
|
|
+ kfree(lid_ctl);
|
|
|
+ lid_ctl = NULL;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* High speed charging function */
|
|
|
+static struct device_attribute *hsc_handle;
|
|
|
+
|
|
|
+static ssize_t sony_nc_highspeed_charging_store(struct device *dev,
|
|
|
+ struct device_attribute *attr,
|
|
|
+ const char *buffer, size_t count)
|
|
|
+{
|
|
|
+ unsigned int result;
|
|
|
+ unsigned long value;
|
|
|
+
|
|
|
+ if (count > 31)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (kstrtoul(buffer, 10, &value) || value > 1)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (sony_call_snc_handle(0x0131, value << 0x10 | 0x0200, &result))
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ return count;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t sony_nc_highspeed_charging_show(struct device *dev,
|
|
|
+ struct device_attribute *attr, char *buffer)
|
|
|
+{
|
|
|
+ unsigned int result;
|
|
|
+
|
|
|
+ if (sony_call_snc_handle(0x0131, 0x0100, &result))
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01);
|
|
|
+}
|
|
|
+
|
|
|
+static int sony_nc_highspeed_charging_setup(struct platform_device *pd)
|
|
|
+{
|
|
|
+ unsigned int result;
|
|
|
+
|
|
|
+ if (sony_call_snc_handle(0x0131, 0x0000, &result) || !(result & 0x01)) {
|
|
|
+ /* some models advertise the handle but have no implementation
|
|
|
+ * for it
|
|
|
+ */
|
|
|
+ pr_info("No High Speed Charging capability found\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ hsc_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);
|
|
|
+ if (!hsc_handle)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ sysfs_attr_init(&hsc_handle->attr);
|
|
|
+ hsc_handle->attr.name = "battery_highspeed_charging";
|
|
|
+ hsc_handle->attr.mode = S_IRUGO | S_IWUSR;
|
|
|
+ hsc_handle->show = sony_nc_highspeed_charging_show;
|
|
|
+ hsc_handle->store = sony_nc_highspeed_charging_store;
|
|
|
+
|
|
|
+ result = device_create_file(&pd->dev, hsc_handle);
|
|
|
+ if (result) {
|
|
|
+ kfree(hsc_handle);
|
|
|
+ hsc_handle = NULL;
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd)
|
|
|
+{
|
|
|
+ if (hsc_handle) {
|
|
|
+ device_remove_file(&pd->dev, hsc_handle);
|
|
|
+ kfree(hsc_handle);
|
|
|
+ hsc_handle = NULL;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* Touchpad enable/disable */
|
|
|
+struct touchpad_control {
|
|
|
+ struct device_attribute attr;
|
|
|
+ int handle;
|
|
|
+};
|
|
|
+static struct touchpad_control *tp_ctl;
|
|
|
+
|
|
|
+static ssize_t sony_nc_touchpad_store(struct device *dev,
|
|
|
+ struct device_attribute *attr, const char *buffer, size_t count)
|
|
|
+{
|
|
|
+ unsigned int result;
|
|
|
+ unsigned long value;
|
|
|
+
|
|
|
+ if (count > 31)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (kstrtoul(buffer, 10, &value) || value > 1)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ /* sysfs: 0 disabled, 1 enabled
|
|
|
+ * EC: 0 enabled, 1 disabled
|
|
|
+ */
|
|
|
+ if (sony_call_snc_handle(tp_ctl->handle,
|
|
|
+ (!value << 0x10) | 0x100, &result))
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ return count;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t sony_nc_touchpad_show(struct device *dev,
|
|
|
+ struct device_attribute *attr, char *buffer)
|
|
|
+{
|
|
|
+ unsigned int result;
|
|
|
+
|
|
|
+ if (sony_call_snc_handle(tp_ctl->handle, 0x000, &result))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ return snprintf(buffer, PAGE_SIZE, "%d\n", !(result & 0x01));
|
|
|
+}
|
|
|
+
|
|
|
+static int sony_nc_touchpad_setup(struct platform_device *pd,
|
|
|
+ unsigned int handle)
|
|
|
+{
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ tp_ctl = kzalloc(sizeof(struct touchpad_control), GFP_KERNEL);
|
|
|
+ if (!tp_ctl)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ tp_ctl->handle = handle;
|
|
|
+
|
|
|
+ sysfs_attr_init(&tp_ctl->attr.attr);
|
|
|
+ tp_ctl->attr.attr.name = "touchpad";
|
|
|
+ tp_ctl->attr.attr.mode = S_IRUGO | S_IWUSR;
|
|
|
+ tp_ctl->attr.show = sony_nc_touchpad_show;
|
|
|
+ tp_ctl->attr.store = sony_nc_touchpad_store;
|
|
|
+
|
|
|
+ ret = device_create_file(&pd->dev, &tp_ctl->attr);
|
|
|
+ if (ret) {
|
|
|
+ kfree(tp_ctl);
|
|
|
+ tp_ctl = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void sony_nc_touchpad_cleanup(struct platform_device *pd)
|
|
|
+{
|
|
|
+ if (tp_ctl) {
|
|
|
+ device_remove_file(&pd->dev, &tp_ctl->attr);
|
|
|
+ kfree(tp_ctl);
|
|
|
+ tp_ctl = NULL;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void sony_nc_backlight_ng_read_limits(int handle,
|
|
|
+ struct sony_backlight_props *props)
|
|
|
+{
|
|
|
+ u64 offset;
|
|
|
+ int i;
|
|
|
+ u8 min = 0xff, max = 0x00;
|
|
|
+ unsigned char buffer[32] = { 0 };
|
|
|
+
|
|
|
+ props->handle = handle;
|
|
|
+ props->offset = 0;
|
|
|
+ props->maxlvl = 0xff;
|
|
|
+
|
|
|
+ offset = sony_find_snc_handle(handle);
|
|
|
+ if (offset < 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* try to read the boundaries from ACPI tables, if we fail the above
|
|
|
+ * defaults should be reasonable
|
|
|
+ */
|
|
|
+ i = sony_nc_buffer_call(sony_nc_acpi_handle, "SN06", &offset, buffer,
|
|
|
+ 32);
|
|
|
+ if (i < 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* the buffer lists brightness levels available, brightness levels are
|
|
|
+ * from position 0 to 8 in the array, other values are used by ALS
|
|
|
+ * control.
|
|
|
+ */
|
|
|
+ for (i = 0; i < 9 && i < ARRAY_SIZE(buffer); i++) {
|
|
|
+
|
|
|
+ dprintk("Brightness level: %d\n", buffer[i]);
|
|
|
+
|
|
|
+ if (!buffer[i])
|
|
|
+ break;
|
|
|
+
|
|
|
+ if (buffer[i] > max)
|
|
|
+ max = buffer[i];
|
|
|
+ if (buffer[i] < min)
|
|
|
+ min = buffer[i];
|
|
|
+ }
|
|
|
+ props->offset = min;
|
|
|
+ props->maxlvl = max;
|
|
|
+ dprintk("Brightness levels: min=%d max=%d\n", props->offset,
|
|
|
+ props->maxlvl);
|
|
|
+}
|
|
|
+
|
|
|
+static void sony_nc_backlight_setup(void)
|
|
|
+{
|
|
|
+ acpi_handle unused;
|
|
|
+ int max_brightness = 0;
|
|
|
+ const struct backlight_ops *ops = NULL;
|
|
|
+ struct backlight_properties props;
|
|
|
+
|
|
|
+ if (sony_find_snc_handle(0x12f) != -1) {
|
|
|
+ ops = &sony_backlight_ng_ops;
|
|
|
+ sony_nc_backlight_ng_read_limits(0x12f, &sony_bl_props);
|
|
|
+ max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;
|
|
|
+
|
|
|
+ } else if (sony_find_snc_handle(0x137) != -1) {
|
|
|
+ ops = &sony_backlight_ng_ops;
|
|
|
+ sony_nc_backlight_ng_read_limits(0x137, &sony_bl_props);
|
|
|
+ max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;
|
|
|
|
|
|
} else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT",
|
|
|
&unused))) {
|
|
@@ -1715,28 +2599,25 @@ static int sony_nc_add(struct acpi_device *device)
|
|
|
|
|
|
if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON",
|
|
|
&handle))) {
|
|
|
- if (acpi_callsetfunc(sony_nc_acpi_handle, "ECON", 1, NULL))
|
|
|
+ int arg = 1;
|
|
|
+ if (sony_nc_int_call(sony_nc_acpi_handle, "ECON", &arg, NULL))
|
|
|
dprintk("ECON Method failed\n");
|
|
|
}
|
|
|
|
|
|
if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00",
|
|
|
&handle))) {
|
|
|
dprintk("Doing SNC setup\n");
|
|
|
+ /* retrieve the available handles */
|
|
|
result = sony_nc_handles_setup(sony_pf_device);
|
|
|
- if (result)
|
|
|
- goto outpresent;
|
|
|
- result = sony_nc_kbd_backlight_setup(sony_pf_device);
|
|
|
- if (result)
|
|
|
- goto outsnc;
|
|
|
- sony_nc_function_setup(device);
|
|
|
- sony_nc_rfkill_setup(device);
|
|
|
+ if (!result)
|
|
|
+ sony_nc_function_setup(device, sony_pf_device);
|
|
|
}
|
|
|
|
|
|
/* setup input devices and helper fifo */
|
|
|
result = sony_laptop_setup_input(device);
|
|
|
if (result) {
|
|
|
pr_err("Unable to create input devices\n");
|
|
|
- goto outkbdbacklight;
|
|
|
+ goto outsnc;
|
|
|
}
|
|
|
|
|
|
if (acpi_video_backlight_support()) {
|
|
@@ -1794,10 +2675,8 @@ static int sony_nc_add(struct acpi_device *device)
|
|
|
|
|
|
sony_laptop_remove_input();
|
|
|
|
|
|
- outkbdbacklight:
|
|
|
- sony_nc_kbd_backlight_cleanup(sony_pf_device);
|
|
|
-
|
|
|
outsnc:
|
|
|
+ sony_nc_function_cleanup(sony_pf_device);
|
|
|
sony_nc_handles_cleanup(sony_pf_device);
|
|
|
|
|
|
outpresent:
|
|
@@ -1820,11 +2699,10 @@ static int sony_nc_remove(struct acpi_device *device, int type)
|
|
|
device_remove_file(&sony_pf_device->dev, &item->devattr);
|
|
|
}
|
|
|
|
|
|
- sony_nc_kbd_backlight_cleanup(sony_pf_device);
|
|
|
+ sony_nc_function_cleanup(sony_pf_device);
|
|
|
sony_nc_handles_cleanup(sony_pf_device);
|
|
|
sony_pf_remove();
|
|
|
sony_laptop_remove_input();
|
|
|
- sony_nc_rfkill_cleanup();
|
|
|
dprintk(SONY_NC_DRIVER_NAME " removed.\n");
|
|
|
|
|
|
return 0;
|
|
@@ -2437,7 +3315,9 @@ static ssize_t sony_pic_wwanpower_store(struct device *dev,
|
|
|
if (count > 31)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- value = simple_strtoul(buffer, NULL, 10);
|
|
|
+ if (kstrtoul(buffer, 10, &value))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
mutex_lock(&spic_dev.lock);
|
|
|
__sony_pic_set_wwanpower(value);
|
|
|
mutex_unlock(&spic_dev.lock);
|
|
@@ -2474,7 +3354,9 @@ static ssize_t sony_pic_bluetoothpower_store(struct device *dev,
|
|
|
if (count > 31)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- value = simple_strtoul(buffer, NULL, 10);
|
|
|
+ if (kstrtoul(buffer, 10, &value))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
mutex_lock(&spic_dev.lock);
|
|
|
__sony_pic_set_bluetoothpower(value);
|
|
|
mutex_unlock(&spic_dev.lock);
|
|
@@ -2513,7 +3395,9 @@ static ssize_t sony_pic_fanspeed_store(struct device *dev,
|
|
|
if (count > 31)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- value = simple_strtoul(buffer, NULL, 10);
|
|
|
+ if (kstrtoul(buffer, 10, &value))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
if (sony_pic_set_fanspeed(value))
|
|
|
return -EIO;
|
|
|
|
|
@@ -2671,7 +3555,8 @@ static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd,
|
|
|
ret = -EIO;
|
|
|
break;
|
|
|
}
|
|
|
- if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value)) {
|
|
|
+ if (sony_nc_int_call(sony_nc_acpi_handle, "GBRT", NULL,
|
|
|
+ &value)) {
|
|
|
ret = -EIO;
|
|
|
break;
|
|
|
}
|
|
@@ -2688,8 +3573,9 @@ static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd,
|
|
|
ret = -EFAULT;
|
|
|
break;
|
|
|
}
|
|
|
- if (acpi_callsetfunc(sony_nc_acpi_handle, "SBRT",
|
|
|
- (val8 >> 5) + 1, NULL)) {
|
|
|
+ value = (val8 >> 5) + 1;
|
|
|
+ if (sony_nc_int_call(sony_nc_acpi_handle, "SBRT", &value,
|
|
|
+ NULL)) {
|
|
|
ret = -EIO;
|
|
|
break;
|
|
|
}
|