From patchwork Tue Oct 8 12:48:44 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Felipe Contreras X-Patchwork-Id: 3003601 Return-Path: X-Original-To: patchwork-linux-pm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id CB8DB9F2B8 for ; Tue, 8 Oct 2013 12:54:54 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 706DC201EF for ; Tue, 8 Oct 2013 12:54:53 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 67D1A2013A for ; Tue, 8 Oct 2013 12:54:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752510Ab3JHMyr (ORCPT ); Tue, 8 Oct 2013 08:54:47 -0400 Received: from mail-ob0-f171.google.com ([209.85.214.171]:42137 "EHLO mail-ob0-f171.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753635Ab3JHMyq (ORCPT ); Tue, 8 Oct 2013 08:54:46 -0400 Received: by mail-ob0-f171.google.com with SMTP id uy5so440437obc.30 for ; Tue, 08 Oct 2013 05:54:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=wJknPH/o6P1FCRr2HZqXj3h5qu3r3dB0Y/MOPVWCueg=; b=E9vGoLQETJmiil3Oqu7vz9ICYWPYCcLXECe3OPuxjWMGmaqiZKniQTMyng44rV+S8W Dobq6U+srF6Cp3iNV5SPs4v87qZ59hWzGXfmO0JXQCGkikijFdPZUKe8GxovTLW/QOuP myCiHUciT9VJVTNJW9p5l+TtNb24VEoe+y2rUE+gxzhCyGvlZrkDr4MsEvI/kAQnFe89 L+8M00M7oQlZh2+jsKAJREgG7L5dbtwVFW87rP/gGJC4J8ifSIipPSeTj4ugz5YEzW5D fgs54MSSr/wq6TkDsSr5BqNqKXmlS1t5M1E91z5tKa6ibMxHYP9jMO/17K6wCZyJV8ff 6dbg== X-Received: by 10.60.118.70 with SMTP id kk6mr1146350oeb.22.1381236883237; Tue, 08 Oct 2013 05:54:43 -0700 (PDT) Received: from localhost (187-162-140-241.static.axtel.net. [187.162.140.241]) by mx.google.com with ESMTPSA id wd7sm3628545obc.3.1969.12.31.16.00.00 (version=TLSv1.2 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 08 Oct 2013 05:54:42 -0700 (PDT) From: Felipe Contreras To: linux-kernel@vger.kernel.org Cc: platform-driver-x86@vger.kernel.org, acpi4asus-user@lists.sourceforge.net, linux-pm@vger.kernel.org, Matthew Garrett , Corentin Chary , Felipe Contreras Subject: [PATCH v2] platform: x86: asus-wmi: add fan control Date: Tue, 8 Oct 2013 07:48:44 -0500 Message-Id: <1381236524-19633-1-git-send-email-felipe.contreras@gmail.com> X-Mailer: git-send-email 1.8.4-fc Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Spam-Status: No, score=-7.1 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Simple driver to enable control of the fan in ASUS laptops. So far this has only been tested in ASUS Zenbook Prime UX31A, but according to some online reference [1], it should work in other models as well. The implementation is very straight-forward, the only caveat is that the fan speed needs to be saved after it has been manually changed because it won't be reported properly until it goes back to 'auto' mode. [1] http://forum.notebookreview.com/asus/705656-fan-control-asus-prime-ux31-ux31a-ux32a-ux32vd.html Signed-off-by: Felipe Contreras --- drivers/platform/x86/asus-wmi.c | 105 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 19c313b..5fdfed6 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -78,6 +78,7 @@ MODULE_LICENSE("GPL"); #define ASUS_WMI_METHODID_GPID 0x44495047 /* Get Panel ID?? (Resol) */ #define ASUS_WMI_METHODID_QMOD 0x444F4D51 /* Quiet MODe */ #define ASUS_WMI_METHODID_SPLV 0x4C425053 /* Set Panel Light Value */ +#define ASUS_WMI_METHODID_AGFN 0x4E464741 #define ASUS_WMI_METHODID_SFUN 0x4E554653 /* FUNCtionalities */ #define ASUS_WMI_METHODID_SDSP 0x50534453 /* Set DiSPlay output */ #define ASUS_WMI_METHODID_GDSP 0x50534447 /* Get DiSPlay output */ @@ -155,6 +156,16 @@ struct bios_args { u32 arg1; } __packed; +struct fan_args { + u16 mfun; + u16 sfun; + u16 len; + u8 stas; + u8 err; + u8 fan; + u32 speed; +} __packed; + /* * / - debugfs root directory * dev_id - current dev_id @@ -205,6 +216,9 @@ struct asus_wmi { struct asus_rfkill gps; struct asus_rfkill uwb; + struct thermal_cooling_device *fan; + int fan_speed; + struct hotplug_slot *hotplug_slot; struct mutex hotplug_lock; struct mutex wmi_lock; @@ -1022,6 +1036,90 @@ exit: } /* + * Fan + */ + +static int fan_speed(struct asus_wmi *asus, int write, int fan, unsigned long *speed) +{ + struct fan_args args = { + .len = sizeof(args), + .mfun = 0x13, + .sfun = write ? 0x07 : 0x06, + .fan = fan, + .speed = write ? *speed : 0, + }; + int r; + u32 value; + + if (!write && asus->fan_speed >= 0) { + *speed = asus->fan_speed; + return 0; + } + + r = asus_wmi_evaluate_method(ASUS_WMI_METHODID_AGFN, virt_to_phys(&args), 0, &value); + if (r || value || args.err) + return -1; + + if (write) + asus->fan_speed = fan > 0 ? *speed : -1; + else + *speed = args.speed; + + return 0; +} + +static int fan_set_auto(struct asus_wmi *asus) +{ + unsigned long speed = 0; + return fan_speed(asus, 1, 0, &speed); +} + +static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) +{ + *state = 0xff; + return 0; +} + +static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state) +{ + return fan_speed(cdev->devdata, 0, 1, state); +} + +static int fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) +{ + return fan_speed(cdev->devdata, 1, 1, &state); +} + +static const struct thermal_cooling_device_ops fan_cooling_ops = { + .get_max_state = fan_get_max_state, + .get_cur_state = fan_get_cur_state, + .set_cur_state = fan_set_cur_state, +}; + +static int asus_wmi_fan_init(struct asus_wmi *asus) +{ + struct thermal_cooling_device *cdev; + + if (fan_set_auto(asus)) + return 0; + + cdev = thermal_cooling_device_register("Fan", asus, &fan_cooling_ops); + if (IS_ERR(cdev)) + return PTR_ERR(cdev); + asus->fan = cdev; + return 0; +} + +static void asus_wmi_fan_exit(struct asus_wmi *asus) +{ + if (!asus->fan) + return; + fan_set_auto(asus); + thermal_cooling_device_unregister(asus->fan); + asus->fan = NULL; +} + +/* * Hwmon device */ static ssize_t asus_hwmon_pwm1(struct device *dev, @@ -1796,6 +1894,10 @@ static int asus_wmi_add(struct platform_device *pdev) if (err) goto fail_rfkill; + err = asus_wmi_fan_init(asus); + if (err) + goto fail_fan; + if (asus->driver->quirks->wmi_backlight_power) acpi_video_dmi_promote_vendor(); if (!acpi_video_backlight_support()) { @@ -1830,6 +1932,8 @@ fail_debugfs: fail_wmi_handler: asus_wmi_backlight_exit(asus); fail_backlight: + asus_wmi_fan_exit(asus); +fail_fan: asus_wmi_rfkill_exit(asus); fail_rfkill: asus_wmi_led_exit(asus); @@ -1855,6 +1959,7 @@ static int asus_wmi_remove(struct platform_device *device) asus_wmi_hwmon_exit(asus); asus_wmi_led_exit(asus); asus_wmi_rfkill_exit(asus); + asus_wmi_fan_exit(asus); asus_wmi_debugfs_exit(asus); asus_wmi_platform_exit(asus);