[PATCH 15/25] sony-laptop: add thermal control feature

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This feature is present on many Vaio notebooks and allows to choose between different thermal profiles (performance, silent, balanced) depending on the notebook capability.

Signed-off-by: Marco Chiappero <marco@xxxxxxxxxx>
---

--- a/drivers/platform/x86/sony-laptop.c
+++ b/drivers/platform/x86/sony-laptop.c
@@ -1748,6 +1748,166 @@ static int sony_nc_battery_care_cleanup(
 	return 0;
 }

+struct thermal_ctrl {
+	unsigned int mode;
+	unsigned int profiles;
+	struct device_attribute mode_attr;
+	struct device_attribute profiles_attr;
+};
+static struct thermal_ctrl *th_handle;
+
+static int sony_nc_thermal_mode_set(unsigned int profile)
+{
+	unsigned int cmd, result;
+
+	/* to avoid the 1 value hole when only 2 profiles are available */
+	switch (profile) {
+	case 1: /* performance */
+		cmd = 2;
+		break;
+	case 2: /* silent */
+		cmd = 1;
+		break;
+	default: /* balanced */
+		cmd = 0;
+		break;
+	}
+
+	if (sony_call_snc_handle(0x0122, cmd << 0x10 | 0x0200, &result))
+		return -EIO;
+
+	th_handle->mode = profile;
+
+	return 0;
+}
+
+static int sony_nc_thermal_mode_get(unsigned int *profile)
+{
+	unsigned int result;
+
+	if (sony_call_snc_handle(0x0122, 0x0100, &result))
+		return -EIO;
+
+	/* to avoid the 1 value hole when only 2 profiles are available */
+	switch (result & 0xff) {
+	case 2: /* performance */
+		*profile = 1;
+		break;
+	case 1: /* silent */
+		*profile = 2;
+		break;
+	default: /* balanced */
+		*profile = 0;
+		break;
+	}
+
+	return 0;
+}
+
+static ssize_t sony_nc_thermal_profiles_show(struct device *dev,
+		struct device_attribute *attr, char *buffer)
+{
+	return snprintf(buffer, PAGE_SIZE, "%u\n", th_handle->profiles);
+}
+
+static ssize_t sony_nc_thermal_mode_store(struct device *dev,
+		struct device_attribute *attr,
+		const char *buffer, size_t count)
+{
+	unsigned long value;
+
+	if (count > 31)
+		return -EINVAL;
+	if (strict_strtoul(buffer, 10, &value) ||
+		value > (th_handle->profiles - 1))
+		return -EINVAL;
+
+	if (sony_nc_thermal_mode_set(value))
+		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 profile;
+
+	if (sony_nc_thermal_mode_get(&profile))
+		return -EIO;
+
+	count = snprintf(buffer, PAGE_SIZE, "%d\n", profile);
+
+	return count;
+}
+
+static int sony_nc_thermal_setup(struct platform_device *pd)
+{
+	th_handle = kzalloc(sizeof(struct thermal_ctrl), GFP_KERNEL);
+	if (!th_handle)
+		return -ENOMEM;
+
+	if (sony_call_snc_handle(0x0122, 0x0000, &th_handle->profiles)) {
+		pr_warn("unable to retrieve the available profiles\n");
+		goto outkzalloc;
+	}
+
+	if (sony_nc_thermal_mode_get(&th_handle->mode)) {
+		pr_warn("unable to retrieve the current profile");
+		goto outkzalloc;
+	}
+
+	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;
+
+	if (device_create_file(&pd->dev, &th_handle->profiles_attr))
+		goto outkzalloc;
+
+	if (device_create_file(&pd->dev, &th_handle->mode_attr))
+		goto outprofiles;
+
+	return 0;
+
+outprofiles:
+	device_remove_file(&pd->dev, &th_handle->profiles_attr);
+outkzalloc:
+	kfree(th_handle);
+	th_handle = NULL;
+	return -1;
+}
+
+static int 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;
+	}
+
+	return 0;
+}
+
+static void sony_nc_thermal_resume(void)
+{
+	unsigned int status;
+
+	sony_nc_thermal_mode_get(&status);
+
+	if (status != th_handle->mode)
+		sony_nc_thermal_mode_set(th_handle->mode);
+}
+
+
 static void sony_nc_backlight_ng_read_limits(int handle,
 		struct sony_backlight_props *props)
 {
@@ -1889,6 +2049,9 @@ static void sony_nc_snc_setup_handles(st
 			sony_bc_handle = handle;
 			ret = sony_nc_battery_care_setup(pd);
 			break;
+		case 0x0122:
+			ret = sony_nc_thermal_setup(pd);
+			break;
 		case 0x0137:
 		case 0x0143:
 			sony_kbd_handle = handle;
@@ -1931,6 +2094,9 @@ static void sony_nc_snc_cleanup_handles(
 		case 0x013f:
 			sony_nc_battery_care_cleanup(pd);
 			break;
+		case 0x0122:
+			sony_nc_thermal_cleanup(pd);
+			break;
 		case 0x0137:
 		case 0x0143:
 			sony_nc_kbd_backlight_cleanup(pd);
@@ -2031,6 +2197,9 @@ static int sony_nc_snc_resume(void)
 		case 0x0102:
 			sony_nc_function_setup(handle);
 			break;
+		case 0x0122:
+			sony_nc_thermal_resume();
+			break;
 		case 0x0124:
 		case 0x0135:
 			/* re-read rfkill state */
--
To unsubscribe from this list: send the line "unsubscribe platform-driver-x86" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Kernel Development]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux