From patchwork Wed Aug 24 08:13:26 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mike Looijmans X-Patchwork-Id: 9297059 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 5C765607EE for ; Wed, 24 Aug 2016 08:14:44 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4F4FA28E6D for ; Wed, 24 Aug 2016 08:14:44 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 42B7E28E71; Wed, 24 Aug 2016 08:14:44 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5A68928E6D for ; Wed, 24 Aug 2016 08:14:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753147AbcHXIOl (ORCPT ); Wed, 24 Aug 2016 04:14:41 -0400 Received: from atl4mhob09.myregisteredsite.com ([209.17.115.47]:59340 "EHLO atl4mhob09.myregisteredsite.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753198AbcHXIOi (ORCPT ); Wed, 24 Aug 2016 04:14:38 -0400 Received: from mailpod.hostingplatform.com ([10.30.71.210]) by atl4mhob09.myregisteredsite.com (8.14.4/8.14.4) with ESMTP id u7O8DVou008731 for ; Wed, 24 Aug 2016 04:13:32 -0400 Received: (qmail 10517 invoked by uid 0); 24 Aug 2016 08:13:31 -0000 X-TCPREMOTEIP: 37.74.225.130 X-Authenticated-UID: mike@milosoftware.com Received: from unknown (HELO mikebuntu.TOPIC.LOCAL) (mike@milosoftware.com@37.74.225.130) by 0 with ESMTPA; 24 Aug 2016 08:13:31 -0000 From: Mike Looijmans To: linux-hwmon@vger.kernel.org, devicetree@vger.kernel.org Cc: linux@roeck-us.net, linux-kernel@vger.kernel.org, Mike Looijmans Subject: [PATCH 1/2] hwmon: (max6650) Allow fan shutdown and initial rpm target Date: Wed, 24 Aug 2016 10:13:26 +0200 Message-Id: <1472026407-3448-1-git-send-email-mike.looijmans@topic.nl> X-Mailer: git-send-email 1.9.1 References: <1471334034-14894-1-git-send-email-mike.looijmans@topic.nl> <57B6A8BF.2080508@topic.nl> In-Reply-To: <5f14ba10-60dd-0e0a-dcd0-f8bbbed33510@roeck-us.net> Sender: linux-hwmon-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-hwmon@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The fan can be stopped by writing "3" to pwm1_enable in sysfs. Add devicetree property for early initialization of the fan controller to prevent overheating, for example when resetting the board while the fan was completely turned off. Also improve error reporting, I2C failures were ignored while writing new values. Signed-off-by: Mike Looijmans --- Documentation/hwmon/max6650 | 1 + drivers/hwmon/max6650.c | 108 +++++++++++++++++++++++++++----------------- 2 files changed, 68 insertions(+), 41 deletions(-) diff --git a/Documentation/hwmon/max6650 b/Documentation/hwmon/max6650 index 58d9644..dff1d29 100644 --- a/Documentation/hwmon/max6650 +++ b/Documentation/hwmon/max6650 @@ -34,6 +34,7 @@ fan3_input ro " fan4_input ro " fan1_target rw desired fan speed in RPM (closed loop mode only) pwm1_enable rw regulator mode, 0=full on, 1=open loop, 2=closed loop + 3=off pwm1 rw relative speed (0-255), 255=max. speed. Used in open loop mode only. fan1_div rw sets the speed range the inputs can handle. Legal diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c index c87517a..a993b44 100644 --- a/drivers/hwmon/max6650.c +++ b/drivers/hwmon/max6650.c @@ -185,6 +185,30 @@ static struct max6650_data *max6650_update_device(struct device *dev) return data; } +/* + * Change the operating mode of the chip (if needed). + * mode is one of the MAX6650_CFG_MODE_* values. + */ +static int max6650_set_operating_mode(struct max6650_data *data, u8 mode) +{ + int result; + u8 config = data->config; + + if (mode == (config & MAX6650_CFG_MODE_MASK)) + return 0; + + config = (config & ~MAX6650_CFG_MODE_MASK) | mode; + + result = i2c_smbus_write_byte_data(data->client, MAX6650_REG_CONFIG, + config); + if (result < 0) + return result; + + data->config = config; + + return 0; +} + static ssize_t get_fan(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -266,18 +290,12 @@ static ssize_t get_target(struct device *dev, struct device_attribute *devattr, return sprintf(buf, "%d\n", rpm); } -static ssize_t set_target(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) +static int max6650_set_target(struct max6650_data *data, unsigned long rpm) { - struct max6650_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; int kscale, ktach; - unsigned long rpm; - int err; - err = kstrtoul(buf, 10, &rpm); - if (err) - return err; + if (rpm == 0) + return max6650_set_operating_mode(data, MAX6650_CFG_MODE_OFF); rpm = clamp_val(rpm, FAN_RPM_MIN, FAN_RPM_MAX); @@ -288,8 +306,6 @@ static ssize_t set_target(struct device *dev, struct device_attribute *devattr, * KTACH = [(fCLK x KSCALE) / (256 x FanSpeed)] - 1 */ - mutex_lock(&data->update_lock); - kscale = DIV_FROM_REG(data->config); ktach = ((clock * kscale) / (256 * rpm / 60)) - 1; if (ktach < 0) @@ -298,10 +314,30 @@ static ssize_t set_target(struct device *dev, struct device_attribute *devattr, ktach = 255; data->speed = ktach; - i2c_smbus_write_byte_data(client, MAX6650_REG_SPEED, data->speed); + return i2c_smbus_write_byte_data(data->client, MAX6650_REG_SPEED, + data->speed); +} + +static ssize_t set_target(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct max6650_data *data = dev_get_drvdata(dev); + unsigned long rpm; + int err; + + err = kstrtoul(buf, 10, &rpm); + if (err) + return err; + + mutex_lock(&data->update_lock); + + err = max6650_set_target(data, rpm); mutex_unlock(&data->update_lock); + if (err < 0) + return err; + return count; } @@ -355,12 +391,11 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, data->dac = 180 - (180 * pwm)/255; else data->dac = 76 - (76 * pwm)/255; - - i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, data->dac); + err = i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, data->dac); mutex_unlock(&data->update_lock); - return count; + return err < 0 ? err : count; } /* @@ -369,14 +404,14 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, * 0 = Fan always on * 1 = Open loop, Voltage is set according to speed, not regulated. * 2 = Closed loop, RPM for all fans regulated by fan1 tachometer + * 3 = Fan off */ - static ssize_t get_enable(struct device *dev, struct device_attribute *devattr, char *buf) { struct max6650_data *data = max6650_update_device(dev); int mode = (data->config & MAX6650_CFG_MODE_MASK) >> 4; - int sysfs_modes[4] = {0, 1, 2, 1}; + int sysfs_modes[4] = {0, 3, 2, 1}; return sprintf(buf, "%d\n", sysfs_modes[mode]); } @@ -385,25 +420,25 @@ static ssize_t set_enable(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct max6650_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - int max6650_modes[3] = {0, 3, 2}; unsigned long mode; int err; + const u8 max6650_modes[] = { + MAX6650_CFG_MODE_ON, + MAX6650_CFG_MODE_OPEN_LOOP, + MAX6650_CFG_MODE_CLOSED_LOOP, + MAX6650_CFG_MODE_OFF, + }; err = kstrtoul(buf, 10, &mode); if (err) return err; - if (mode > 2) + if (mode >= ARRAY_SIZE(max6650_modes)) return -EINVAL; mutex_lock(&data->update_lock); - data->config = i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG); - data->config = (data->config & ~MAX6650_CFG_MODE_MASK) - | (max6650_modes[mode] << 4); - - i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, data->config); + max6650_set_operating_mode(data, max6650_modes[mode]); mutex_unlock(&data->update_lock); @@ -582,6 +617,7 @@ static int max6650_init_client(struct max6650_data *data, int err = -EIO; u32 voltage; u32 prescale; + u32 target_rpm; if (of_property_read_u32(dev->of_node, "maxim,fan-microvolt", &voltage)) @@ -642,22 +678,6 @@ static int max6650_init_client(struct max6650_data *data, (config & MAX6650_CFG_V12) ? 12 : 5, 1 << (config & MAX6650_CFG_PRESCALER_MASK)); - /* - * If mode is set to "full off", we change it to "open loop" and - * set DAC to 255, which has the same effect. We do this because - * there's no "full off" mode defined in hwmon specifications. - */ - - if ((config & MAX6650_CFG_MODE_MASK) == MAX6650_CFG_MODE_OFF) { - dev_dbg(dev, "Change mode to open loop, full off.\n"); - config = (config & ~MAX6650_CFG_MODE_MASK) - | MAX6650_CFG_MODE_OPEN_LOOP; - if (i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, 255)) { - dev_err(dev, "DAC write error, aborting.\n"); - return err; - } - } - if (i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, config)) { dev_err(dev, "Config write error, aborting.\n"); return err; @@ -666,6 +686,12 @@ static int max6650_init_client(struct max6650_data *data, data->config = config; data->count = i2c_smbus_read_byte_data(client, MAX6650_REG_COUNT); + if (!of_property_read_u32(client->dev.of_node, "maxim,fan-target-rpm", + &target_rpm)) { + max6650_set_target(data, target_rpm); + max6650_set_operating_mode(data, MAX6650_CFG_MODE_CLOSED_LOOP); + } + return 0; }