From patchwork Fri Oct 7 15:21:08 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pantelis Antoniou X-Patchwork-Id: 9366307 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 A06566075E for ; Fri, 7 Oct 2016 15:26:09 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9098F296C3 for ; Fri, 7 Oct 2016 15:26:09 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 84945296DA; Fri, 7 Oct 2016 15:26:09 +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.3 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI, RCVD_IN_SORBS_SPAM, T_DKIM_INVALID autolearn=unavailable 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 C376B296DD for ; Fri, 7 Oct 2016 15:26:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757213AbcJGPYp (ORCPT ); Fri, 7 Oct 2016 11:24:45 -0400 Received: from mail-wm0-f49.google.com ([74.125.82.49]:37873 "EHLO mail-wm0-f49.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S938927AbcJGPXy (ORCPT ); Fri, 7 Oct 2016 11:23:54 -0400 Received: by mail-wm0-f49.google.com with SMTP id b201so46115351wmb.0 for ; Fri, 07 Oct 2016 08:23:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=konsulko.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=GwyYjEUWiay8mw7RqluUEvy9cZZt2LkmKQTus4Dqv1g=; b=TlwuSDYHHeQqx9q9q5xKNX93kPnUIDDBpqQ8564YDj3MOlqAi94BxeZmftqPkqTBk6 nyJA3Y/8gO5WxN/FJy7Q+5vkMtMeujl/Lxa2vjhhj0x6liHl4sm1x5vvd7YMXICEhmLk VfadrB6ne8zCazeS6xsbI+GR4qT6b0FRgS5W4= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=GwyYjEUWiay8mw7RqluUEvy9cZZt2LkmKQTus4Dqv1g=; b=b3daM0TVVpSwI+ud6MldySOZEx7ZfgHIe83zR3Gk6NFIb1UCQFrtVATx+j5kAlwheY JGttb8pvtFZk8bn/5whdTYjjx0fcwOVFTZMdOkxFRJDLOXv9JclSQ4luXLcbLsBBVZvP PvAkI1lXpvrnpswxf8hIz4kr48VQgI2UfjguIcv15FcllVU8JPD4/DZtmV+d46KjYTp1 ZvlkTOLwO1PHm17IuJuEB96Kg7habxcwc3NDUHt90RJ4CoBx2Zn/XjFC2V0j+ZVDt+J1 cv59XZtVSSFLf39FDQSR7r24fuOxYITO13pNh+04iXiH5KVMy8fq9Ljrckul3ovgyJsE g4iQ== X-Gm-Message-State: AA6/9Rn+YEywFuekTsooUQIl23P/B2FvUYGAZw24X8mKTQWutd/1/IQVtUBTciHOzQVBsw== X-Received: by 10.28.47.150 with SMTP id v144mr1538064wmv.20.1475853827874; Fri, 07 Oct 2016 08:23:47 -0700 (PDT) Received: from localhost.localdomain ([195.97.110.117]) by smtp.gmail.com with ESMTPSA id n5sm20048269wjv.35.2016.10.07.08.23.44 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 07 Oct 2016 08:23:47 -0700 (PDT) From: Pantelis Antoniou To: Lee Jones Cc: Linus Walleij , Alexandre Courbot , Rob Herring , Mark Rutland , Frank Rowand , Wolfram Sang , Richard Purdie , Jacek Anaszewski , Jean Delvare , Peter Rosin , Avirup Banerjee , Georgi Vlaev , Guenter Roeck , JawaharBalaji Thirumalaisamy , Pantelis Antoniou , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, linux-i2c@vger.kernel.org, linux-leds@vger.kernel.org, linux-hwmon@vger.kernel.org Subject: [PATCH 09/10] hwmon: Add driver for Fan Tray on Juniper I2CS FGPA Date: Fri, 7 Oct 2016 18:21:08 +0300 Message-Id: <1475853669-22480-10-git-send-email-pantelis.antoniou@konsulko.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1475853669-22480-1-git-send-email-pantelis.antoniou@konsulko.com> References: <1475853669-22480-1-git-send-email-pantelis.antoniou@konsulko.com> 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 From: Avirup Banerjee Add a hwmon driver for Fan Trays using Juniper's I2CS FPGA. Signed-off-by: Avirup Banerjee Signed-off-by: Georgi Vlaev Signed-off-by: Guenter Roeck Signed-off-by: JawaharBalaji Thirumalaisamy [Ported from Juniper kernel] Signed-off-by: Pantelis Antoniou --- drivers/hwmon/Kconfig | 11 + drivers/hwmon/Makefile | 1 + drivers/hwmon/jnx-fan.c | 471 +++++++++++++++++++++++++++++ include/linux/platform_data/jnx-i2cs-fan.h | 13 + 4 files changed, 496 insertions(+) create mode 100644 drivers/hwmon/jnx-fan.c create mode 100644 include/linux/platform_data/jnx-i2cs-fan.h diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 45cef3d..b9348d2 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -663,6 +663,17 @@ config SENSORS_JC42 This driver can also be built as a module. If so, the module will be called jc42. +config SENSORS_JNX_FAN + tristate "Juniper Fan Tray driver" + depends on I2C && MFD_JUNIPER_I2CS + select REGMAP_I2C + help + If you say yes here you get support for the Juniper Networks + Fan Tray Driver. + + This driver can also be built as a module. If so, the module + will be called jnx-fan. + config SENSORS_POWR1220 tristate "Lattice POWR1220 Power Monitoring" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index aecf4ba..eea631e 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -81,6 +81,7 @@ obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o obj-$(CONFIG_SENSORS_INA3221) += ina3221.o obj-$(CONFIG_SENSORS_IT87) += it87.o obj-$(CONFIG_SENSORS_JC42) += jc42.o +obj-$(CONFIG_SENSORS_JNX_FAN) += jnx-fan.o obj-$(CONFIG_SENSORS_JZ4740) += jz4740-hwmon.o obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o diff --git a/drivers/hwmon/jnx-fan.c b/drivers/hwmon/jnx-fan.c new file mode 100644 index 0000000..d04e3ce --- /dev/null +++ b/drivers/hwmon/jnx-fan.c @@ -0,0 +1,471 @@ +/* + * hwmon: Driver for Juniper Fan Tray Controller + * + * Copyright (c) 2014 Juniper Networks. All rights reserved. + * Author: Avirup Banerjee + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "i2cs_fan_hwmon" + +/* + * Fan fpga register offsets + */ + +#define I2CS_FAN_ADEC_VER 0x01 +#define I2CS_FAN_SELECT 0x40 +#define I2CS_FAN_SPEED_CTRL 0x41 +#define I2CS_FAN_MAX_TACH 0x42 +#define I2CS_FAN_MIN_TACH 0x43 +#define I2CS_FAN_TACH 0x44 +#define I2CS_FAN_INT_SRC 0x45 +#define I2CS_FAN_INT_MASK 0x46 +#define I2CS_FAN_INT_1_7 0x47 +#define I2CS_FAN_INT_8_14 0x48 +#define I2CS_FAN_SPARE_FAN_INT_15_22 0x49 +#define I2CS_FAN_MODE_AND_TEST 0x4A +#define I2CS_FAN_HW_DEBUG_1 0x4B +#define I2CS_FAN_HW_DEBUG_2 0x4C +#define I2CS_FAN_SW_FAN_POWER_SPEED_FAIL 0x4D +#define I2CS_FAN_ADEC_MASK_WAIT_SECOND 0x4F +#define I2CS_FAN_RESET_WAIT_CONTROL 0x50 +#define I2CS_FAN_BOARD_STAT_1 0x51 +#define I2CS_FAN_BOARD_STAT_2 0x52 +#define I2CS_FAN_OK_THRESHOLD 0x53 +#define I2CS_FAN_SPARE 0x54 +#define I2CS_FAN_SPARE_OE 0x55 + +#define FAN_TACH_FACTOR 120 +#define NUM_FANS_PER_TRAY 14 + +struct jnx_fan_data { + struct regmap *regmap; + struct device *hwmon_dev; + struct mutex update_lock; + int fan_index; + int num_fans; + int factor; +}; + +static int jnx_fan_select(struct jnx_fan_data *data, int index) +{ + /* Return if fan has already been selected */ + if (data->fan_index == index) + return 0; + + data->fan_index = index; + + return regmap_write(data->regmap, I2CS_FAN_SELECT, index); +} + +static int jnx_fan_read_reg(struct jnx_fan_data *data, u8 reg, int index) +{ + unsigned int value; + int ret; + + mutex_lock(&data->update_lock); + + ret = jnx_fan_select(data, index); + if (ret < 0) + goto done; + + ret = regmap_read(data->regmap, reg, &value); + if (ret < 0) + goto done; + ret = value; + +done: + mutex_unlock(&data->update_lock); + return ret; +} + +static int jnx_fan_write_reg(struct jnx_fan_data *data, u8 reg, + unsigned int value, int index) +{ + int ret; + + mutex_lock(&data->update_lock); + ret = jnx_fan_select(data, index); + if (ret < 0) + goto done; + + ret = regmap_write(data->regmap, reg, value); + +done: + mutex_unlock(&data->update_lock); + return ret; +} + +static ssize_t jnx_fan_set_pwm(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct jnx_fan_data *data = dev_get_drvdata(dev); + unsigned long val; + int ret; + + if (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; + if (val > 255) + return -EINVAL; + + ret = jnx_fan_write_reg(data, I2CS_FAN_SPEED_CTRL, val, attr->index); + return ret ? ret : count; +} + +static ssize_t jnx_fan_show_pwm(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct jnx_fan_data *data = dev_get_drvdata(dev); + int ret; + + ret = jnx_fan_read_reg(data, I2CS_FAN_SPEED_CTRL, attr->index); + if (ret < 0) + return ret; + + return snprintf(buf, PAGE_SIZE, "%d\n", ret); +} + +static ssize_t jnx_fan_show(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da); + struct jnx_fan_data *data = dev_get_drvdata(dev); + int ret; + + ret = jnx_fan_read_reg(data, attr->nr, attr->index); + if (ret < 0) + return ret; + + return snprintf(buf, PAGE_SIZE, "%d\n", ret * data->factor); +} + +static ssize_t jnx_fan_set(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da); + struct jnx_fan_data *data = dev_get_drvdata(dev); + unsigned long val; + int ret; + + if (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; + + DIV_ROUND_CLOSEST(val, data->factor); + clamp_val(val, 0, 255); + + ret = jnx_fan_write_reg(data, attr->nr, val, attr->index); + return ret ? ret : count; +} + +static umode_t jnx_fan_is_visible(struct kobject *kobj, struct attribute *a, + int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct jnx_fan_data *data = dev_get_drvdata(dev); + unsigned int index = n % 14; + + if (index < data->num_fans) + return a->mode; + + return 0; +} + +static struct regmap_config jnx_fan_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = I2CS_FAN_SPARE_OE, +}; + +/* Fan speed */ +static SENSOR_DEVICE_ATTR_2(fan1_input, S_IRUGO, jnx_fan_show, NULL, + I2CS_FAN_TACH, 1); +static SENSOR_DEVICE_ATTR_2(fan2_input, S_IRUGO, jnx_fan_show, NULL, + I2CS_FAN_TACH, 2); +static SENSOR_DEVICE_ATTR_2(fan3_input, S_IRUGO, jnx_fan_show, NULL, + I2CS_FAN_TACH, 3); +static SENSOR_DEVICE_ATTR_2(fan4_input, S_IRUGO, jnx_fan_show, NULL, + I2CS_FAN_TACH, 4); +static SENSOR_DEVICE_ATTR_2(fan5_input, S_IRUGO, jnx_fan_show, NULL, + I2CS_FAN_TACH, 5); +static SENSOR_DEVICE_ATTR_2(fan6_input, S_IRUGO, jnx_fan_show, NULL, + I2CS_FAN_TACH, 6); +static SENSOR_DEVICE_ATTR_2(fan7_input, S_IRUGO, jnx_fan_show, NULL, + I2CS_FAN_TACH, 7); +static SENSOR_DEVICE_ATTR_2(fan8_input, S_IRUGO, jnx_fan_show, NULL, + I2CS_FAN_TACH, 8); +static SENSOR_DEVICE_ATTR_2(fan9_input, S_IRUGO, jnx_fan_show, NULL, + I2CS_FAN_TACH, 9); +static SENSOR_DEVICE_ATTR_2(fan10_input, S_IRUGO, jnx_fan_show, NULL, + I2CS_FAN_TACH, 10); +static SENSOR_DEVICE_ATTR_2(fan11_input, S_IRUGO, jnx_fan_show, NULL, + I2CS_FAN_TACH, 11); +static SENSOR_DEVICE_ATTR_2(fan12_input, S_IRUGO, jnx_fan_show, NULL, + I2CS_FAN_TACH, 12); +static SENSOR_DEVICE_ATTR_2(fan13_input, S_IRUGO, jnx_fan_show, NULL, + I2CS_FAN_TACH, 13); +static SENSOR_DEVICE_ATTR_2(fan14_input, S_IRUGO, jnx_fan_show, NULL, + I2CS_FAN_TACH, 14); + +/* PWM values */ +static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, jnx_fan_show_pwm, + jnx_fan_set_pwm, 1); +static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, jnx_fan_show_pwm, + jnx_fan_set_pwm, 2); +static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, jnx_fan_show_pwm, + jnx_fan_set_pwm, 3); +static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, jnx_fan_show_pwm, + jnx_fan_set_pwm, 4); +static SENSOR_DEVICE_ATTR(pwm5, S_IWUSR | S_IRUGO, jnx_fan_show_pwm, + jnx_fan_set_pwm, 5); +static SENSOR_DEVICE_ATTR(pwm6, S_IWUSR | S_IRUGO, jnx_fan_show_pwm, + jnx_fan_set_pwm, 6); +static SENSOR_DEVICE_ATTR(pwm7, S_IWUSR | S_IRUGO, jnx_fan_show_pwm, + jnx_fan_set_pwm, 7); +static SENSOR_DEVICE_ATTR(pwm8, S_IWUSR | S_IRUGO, jnx_fan_show_pwm, + jnx_fan_set_pwm, 8); +static SENSOR_DEVICE_ATTR(pwm9, S_IWUSR | S_IRUGO, jnx_fan_show_pwm, + jnx_fan_set_pwm, 9); +static SENSOR_DEVICE_ATTR(pwm10, S_IWUSR | S_IRUGO, jnx_fan_show_pwm, + jnx_fan_set_pwm, 10); +static SENSOR_DEVICE_ATTR(pwm11, S_IWUSR | S_IRUGO, jnx_fan_show_pwm, + jnx_fan_set_pwm, 11); +static SENSOR_DEVICE_ATTR(pwm12, S_IWUSR | S_IRUGO, jnx_fan_show_pwm, + jnx_fan_set_pwm, 12); +static SENSOR_DEVICE_ATTR(pwm13, S_IWUSR | S_IRUGO, jnx_fan_show_pwm, + jnx_fan_set_pwm, 13); +static SENSOR_DEVICE_ATTR(pwm14, S_IWUSR | S_IRUGO, jnx_fan_show_pwm, + jnx_fan_set_pwm, 14); + +/* Fan Thresholds */ + +/* Min */ +static SENSOR_DEVICE_ATTR_2(fan1_min, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MIN_TACH, 1); +static SENSOR_DEVICE_ATTR_2(fan2_min, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MIN_TACH, 2); +static SENSOR_DEVICE_ATTR_2(fan3_min, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MIN_TACH, 3); +static SENSOR_DEVICE_ATTR_2(fan4_min, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MIN_TACH, 4); +static SENSOR_DEVICE_ATTR_2(fan5_min, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MIN_TACH, 5); +static SENSOR_DEVICE_ATTR_2(fan6_min, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MIN_TACH, 6); +static SENSOR_DEVICE_ATTR_2(fan7_min, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MIN_TACH, 7); +static SENSOR_DEVICE_ATTR_2(fan8_min, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MIN_TACH, 8); +static SENSOR_DEVICE_ATTR_2(fan9_min, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MIN_TACH, 9); +static SENSOR_DEVICE_ATTR_2(fan10_min, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MIN_TACH, 10); +static SENSOR_DEVICE_ATTR_2(fan11_min, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MIN_TACH, 11); +static SENSOR_DEVICE_ATTR_2(fan12_min, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MIN_TACH, 12); +static SENSOR_DEVICE_ATTR_2(fan13_min, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MIN_TACH, 13); +static SENSOR_DEVICE_ATTR_2(fan14_min, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MIN_TACH, 14); +/* Max */ +static SENSOR_DEVICE_ATTR_2(fan1_max, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MAX_TACH, 1); +static SENSOR_DEVICE_ATTR_2(fan2_max, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MAX_TACH, 2); +static SENSOR_DEVICE_ATTR_2(fan3_max, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MAX_TACH, 3); +static SENSOR_DEVICE_ATTR_2(fan4_max, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MAX_TACH, 4); +static SENSOR_DEVICE_ATTR_2(fan5_max, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MAX_TACH, 5); +static SENSOR_DEVICE_ATTR_2(fan6_max, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MAX_TACH, 6); +static SENSOR_DEVICE_ATTR_2(fan7_max, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MAX_TACH, 7); +static SENSOR_DEVICE_ATTR_2(fan8_max, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MAX_TACH, 8); +static SENSOR_DEVICE_ATTR_2(fan9_max, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MAX_TACH, 9); +static SENSOR_DEVICE_ATTR_2(fan10_max, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MAX_TACH, 10); +static SENSOR_DEVICE_ATTR_2(fan11_max, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MAX_TACH, 11); +static SENSOR_DEVICE_ATTR_2(fan12_max, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MAX_TACH, 12); +static SENSOR_DEVICE_ATTR_2(fan13_max, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MAX_TACH, 13); +static SENSOR_DEVICE_ATTR_2(fan14_max, S_IWUSR | S_IRUGO, jnx_fan_show, + jnx_fan_set, I2CS_FAN_MAX_TACH, 14); + +static struct attribute *jnx_fan_attrs[] = { + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan4_input.dev_attr.attr, + &sensor_dev_attr_fan5_input.dev_attr.attr, + &sensor_dev_attr_fan6_input.dev_attr.attr, + &sensor_dev_attr_fan7_input.dev_attr.attr, + &sensor_dev_attr_fan8_input.dev_attr.attr, + &sensor_dev_attr_fan9_input.dev_attr.attr, + &sensor_dev_attr_fan10_input.dev_attr.attr, + &sensor_dev_attr_fan11_input.dev_attr.attr, + &sensor_dev_attr_fan12_input.dev_attr.attr, + &sensor_dev_attr_fan13_input.dev_attr.attr, + &sensor_dev_attr_fan14_input.dev_attr.attr, + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm2.dev_attr.attr, + &sensor_dev_attr_pwm3.dev_attr.attr, + &sensor_dev_attr_pwm4.dev_attr.attr, + &sensor_dev_attr_pwm5.dev_attr.attr, + &sensor_dev_attr_pwm6.dev_attr.attr, + &sensor_dev_attr_pwm7.dev_attr.attr, + &sensor_dev_attr_pwm8.dev_attr.attr, + &sensor_dev_attr_pwm9.dev_attr.attr, + &sensor_dev_attr_pwm10.dev_attr.attr, + &sensor_dev_attr_pwm11.dev_attr.attr, + &sensor_dev_attr_pwm12.dev_attr.attr, + &sensor_dev_attr_pwm13.dev_attr.attr, + &sensor_dev_attr_pwm14.dev_attr.attr, + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan2_min.dev_attr.attr, + &sensor_dev_attr_fan3_min.dev_attr.attr, + &sensor_dev_attr_fan4_min.dev_attr.attr, + &sensor_dev_attr_fan5_min.dev_attr.attr, + &sensor_dev_attr_fan6_min.dev_attr.attr, + &sensor_dev_attr_fan7_min.dev_attr.attr, + &sensor_dev_attr_fan8_min.dev_attr.attr, + &sensor_dev_attr_fan9_min.dev_attr.attr, + &sensor_dev_attr_fan10_min.dev_attr.attr, + &sensor_dev_attr_fan11_min.dev_attr.attr, + &sensor_dev_attr_fan12_min.dev_attr.attr, + &sensor_dev_attr_fan13_min.dev_attr.attr, + &sensor_dev_attr_fan14_min.dev_attr.attr, + &sensor_dev_attr_fan1_max.dev_attr.attr, + &sensor_dev_attr_fan2_max.dev_attr.attr, + &sensor_dev_attr_fan3_max.dev_attr.attr, + &sensor_dev_attr_fan4_max.dev_attr.attr, + &sensor_dev_attr_fan5_max.dev_attr.attr, + &sensor_dev_attr_fan6_max.dev_attr.attr, + &sensor_dev_attr_fan7_max.dev_attr.attr, + &sensor_dev_attr_fan8_max.dev_attr.attr, + &sensor_dev_attr_fan9_max.dev_attr.attr, + &sensor_dev_attr_fan10_max.dev_attr.attr, + &sensor_dev_attr_fan11_max.dev_attr.attr, + &sensor_dev_attr_fan12_max.dev_attr.attr, + &sensor_dev_attr_fan13_max.dev_attr.attr, + &sensor_dev_attr_fan14_max.dev_attr.attr, + NULL, +}; + +static const struct attribute_group jnx_fan_group = { + .attrs = jnx_fan_attrs, + .is_visible = jnx_fan_is_visible, +}; +__ATTRIBUTE_GROUPS(jnx_fan); + +static int jnx_fan_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct i2cs_fan_platform_data *pdata = dev_get_platdata(dev); + struct i2c_client *client; + struct jnx_fan_data *data; + + if (!dev->parent) + return -ENODEV; + + client = i2c_verify_client(dev->parent); + if (!client) + return -ENODEV; + + data = devm_kzalloc(dev, sizeof(struct jnx_fan_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->regmap = devm_regmap_init_i2c(client, &jnx_fan_regmap_config); + if (IS_ERR(data->regmap)) { + dev_err(dev, "failed to allocate register map\n"); + return PTR_ERR(data->regmap); + } + + if (pdata) { + data->num_fans = pdata->num_fans; + data->factor = pdata->factor; + } else { + data->num_fans = NUM_FANS_PER_TRAY; + data->factor = FAN_TACH_FACTOR; + } + + if (dev->of_node) { + of_property_read_u32(dev->of_node, + "num-fans", &data->num_fans); + of_property_read_u32(dev->of_node, + "tach-factor", &data->factor); + } + + data->fan_index = -1; + mutex_init(&data->update_lock); + + platform_set_drvdata(pdev, data); + + data->hwmon_dev = hwmon_device_register_with_groups(dev->parent, + "i2cs_fan", data, + jnx_fan_groups); + return PTR_ERR_OR_ZERO(data->hwmon_dev); +} + +static int jnx_fan_remove(struct platform_device *pdev) +{ + struct jnx_fan_data *data = platform_get_drvdata(pdev); + + hwmon_device_unregister(data->hwmon_dev); + + return 0; +} + +static const struct of_device_id jnx_fan_of_match[] = { + { .compatible = "jnx,i2cs-fan-hwmon", }, + { }, +}; +MODULE_DEVICE_TABLE(of, jnx_fan_of_match); + +static struct platform_driver jnx_fan_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(jnx_fan_of_match), + }, + .probe = jnx_fan_probe, + .remove = jnx_fan_remove, +}; + +module_platform_driver(jnx_fan_driver); + +MODULE_AUTHOR("Avirup Banerjee "); +MODULE_DESCRIPTION("JNPR FAN driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/include/linux/platform_data/jnx-i2cs-fan.h b/include/linux/platform_data/jnx-i2cs-fan.h new file mode 100644 index 0000000..b3fc8c2 --- /dev/null +++ b/include/linux/platform_data/jnx-i2cs-fan.h @@ -0,0 +1,13 @@ +/* + * i2cs-fan.h + */ + +#ifndef I2CS_FAN_H +#define I2CS_FAN_H + +struct i2cs_fan_platform_data { + int num_fans; /* Number of fans in tray */ + int factor; /* fan speed multiplication factor */ +}; + +#endif /* I2CS_FAN_H */