From patchwork Wed Jul 26 11:24:35 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mykola Kostenok X-Patchwork-Id: 9864577 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 EC63D60382 for ; Wed, 26 Jul 2017 11:24:47 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id F270B2874E for ; Wed, 26 Jul 2017 11:24:47 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id E5A8E28754; Wed, 26 Jul 2017 11:24:47 +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, UNPARSEABLE_RELAY 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 183842874E for ; Wed, 26 Jul 2017 11:24:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751325AbdGZLYp (ORCPT ); Wed, 26 Jul 2017 07:24:45 -0400 Received: from mail-il-dmz.mellanox.com ([193.47.165.129]:59159 "EHLO mellanox.co.il" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1751034AbdGZLYp (ORCPT ); Wed, 26 Jul 2017 07:24:45 -0400 Received: from Internal Mail-Server by MTLPINE1 (envelope-from c?mykolak@mellanox.com) with ESMTPS (AES256-SHA encrypted); 26 Jul 2017 14:24:39 +0300 Received: from r-vnc15.mtr.labs.mlnx (r-vnc15.mtr.labs.mlnx [10.208.0.15]) by labmailer.mlnx (8.13.8/8.13.8) with ESMTP id v6QBOc4S008136; Wed, 26 Jul 2017 14:24:38 +0300 From: Mykola Kostenok To: Guenter Roeck , Jean Delvare , linux-hwmon@vger.kernel.org Cc: Mykola Kostenok , Rob Herring , Jaghathiswari Rankappagounder Natarajan , openbmc@lists.ozlabs.org, Patrick Venture , Vadim Pasternak , Ohad Oz , Joel Stanley Subject: [patch v4] hwmon: (aspeed-pwm-tacho) cooling device support. Date: Wed, 26 Jul 2017 14:24:35 +0300 Message-Id: <20170726112435.32575-1-c_mykolak@mellanox.com> X-Mailer: git-send-email 2.8.4 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 Add support in aspeed-pwm-tacho driver for cooling device creation. This cooling device could be bound to a thermal zone for the thermal control. Device will appear in /sys/class/thermal folder as cooling_deviceX. Then it could be bound to particular thermal zones. Allow specification of the cooling levels vector - PWM duty cycle values in a range from 0 to 255 which correspond to thermal cooling states. Signed-off-by: Mykola Kostenok v1 -> v2: - Remove device tree binding file from the patch. - Move of_node_put out of cycle because of_get_next_child already do of_put_node on previous child. v2 -> v3: Pointed out by Rob Herring: - Put cooling-levels under fan subnodes. v3 -> v4: Pointed out by Joel Stanley: - Move patch history after Signoff. - Remove unnecessary type cast. - Use array instead of pointers for colling_levels. - Rename num_level to num_levels. - Use local variable to make function easier to read. - Drop unnesesary sizeof(u8). - Use IS_ERR instead of PTR_ERR_OR_ZERO. --- drivers/hwmon/aspeed-pwm-tacho.c | 115 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 113 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c index ddfe66b..9adf9d7 100644 --- a/drivers/hwmon/aspeed-pwm-tacho.c +++ b/drivers/hwmon/aspeed-pwm-tacho.c @@ -20,6 +20,7 @@ #include #include #include +#include /* ASPEED PWM & FAN Tach Register Definition */ #define ASPEED_PTCR_CTRL 0x00 @@ -166,6 +167,16 @@ /* How long we sleep in us while waiting for an RPM result. */ #define ASPEED_RPM_STATUS_SLEEP_USEC 500 +struct aspeed_cooling_device { + char name[16]; + struct aspeed_pwm_tacho_data *priv; + struct thermal_cooling_device *tcdev; + int pwm_port; + u8 *cooling_levels; + u8 max_state; + u8 cur_state; +}; + struct aspeed_pwm_tacho_data { struct regmap *regmap; unsigned long clk_freq; @@ -180,6 +191,7 @@ struct aspeed_pwm_tacho_data { u8 pwm_port_type[8]; u8 pwm_port_fan_ctrl[8]; u8 fan_tach_ch_source[16]; + struct aspeed_cooling_device *cdev[8]; const struct attribute_group *groups[3]; }; @@ -765,6 +777,94 @@ static void aspeed_create_fan_tach_channel(struct aspeed_pwm_tacho_data *priv, } } +static int +aspeed_pwm_cz_get_max_state(struct thermal_cooling_device *tcdev, + unsigned long *state) +{ + struct aspeed_cooling_device *cdev = tcdev->devdata; + + *state = cdev->max_state; + + return 0; +} + +static int +aspeed_pwm_cz_get_cur_state(struct thermal_cooling_device *tcdev, + unsigned long *state) +{ + struct aspeed_cooling_device *cdev = tcdev->devdata; + + *state = cdev->cur_state; + + return 0; +} + +static int +aspeed_pwm_cz_set_cur_state(struct thermal_cooling_device *tcdev, + unsigned long state) +{ + struct aspeed_cooling_device *cdev = tcdev->devdata; + + if (state > cdev->max_state) + return -EINVAL; + + cdev->cur_state = state; + cdev->priv->pwm_port_fan_ctrl[cdev->pwm_port] = + cdev->cooling_levels[cdev->cur_state]; + aspeed_set_pwm_port_fan_ctrl(cdev->priv, cdev->pwm_port, + cdev->cooling_levels[cdev->cur_state]); + + return 0; +} + +static const struct thermal_cooling_device_ops aspeed_pwm_cool_ops = { + .get_max_state = aspeed_pwm_cz_get_max_state, + .get_cur_state = aspeed_pwm_cz_get_cur_state, + .set_cur_state = aspeed_pwm_cz_set_cur_state, +}; + +static int aspeed_create_pwm_cooling(struct device *dev, + struct device_node *child, + struct aspeed_pwm_tacho_data *priv, + u32 pwm_port, u8 num_levels) +{ + int ret; + struct aspeed_cooling_device *cdev; + + cdev = devm_kzalloc(dev, sizeof(*cdev), GFP_KERNEL); + + if (!cdev) + return -ENOMEM; + + cdev->cooling_levels = devm_kzalloc(dev, num_levels, GFP_KERNEL); + if (!cdev->cooling_levels) + return -ENOMEM; + + cdev->max_state = num_levels - 1; + ret = of_property_read_u8_array(child, "cooling-levels", + cdev->cooling_levels, + num_levels); + if (ret) { + dev_err(dev, "Property 'cooling-levels' cannot be read.\n"); + return ret; + } + sprintf(cdev->name, "%s%d", child->name, pwm_port); + + cdev->tcdev = thermal_of_cooling_device_register(child, + cdev->name, + cdev, + &aspeed_pwm_cool_ops); + if (IS_ERR(cdev->tcdev)) + return PTR_ERR(cdev->tcdev); + + cdev->priv = priv; + cdev->pwm_port = pwm_port; + + priv->cdev[pwm_port] = cdev; + + return 0; +} + static int aspeed_create_fan(struct device *dev, struct device_node *child, struct aspeed_pwm_tacho_data *priv) @@ -778,6 +878,15 @@ static int aspeed_create_fan(struct device *dev, return ret; aspeed_create_pwm_port(priv, (u8)pwm_port); + ret = of_property_count_u8_elems(child, "cooling-levels"); + + if (ret > 0) { + ret = aspeed_create_pwm_cooling(dev, child, priv, pwm_port, + ret); + if (ret) + return ret; + } + count = of_property_count_u8_elems(child, "aspeed,fan-tach-ch"); if (count < 1) return -EINVAL; @@ -834,10 +943,12 @@ static int aspeed_pwm_tacho_probe(struct platform_device *pdev) for_each_child_of_node(np, child) { ret = aspeed_create_fan(dev, child, priv); - of_node_put(child); - if (ret) + if (ret) { + of_node_put(child); return ret; + } } + of_node_put(child); priv->groups[0] = &pwm_dev_group; priv->groups[1] = &fan_dev_group;