diff options
Diffstat (limited to 'meta-phosphor/common/recipes-kernel/linux/linux-obmc/0001-hwmon-Add-support-for-MAX31785-intelligent-fan-contr.patch')
-rw-r--r-- | meta-phosphor/common/recipes-kernel/linux/linux-obmc/0001-hwmon-Add-support-for-MAX31785-intelligent-fan-contr.patch | 821 |
1 files changed, 821 insertions, 0 deletions
diff --git a/meta-phosphor/common/recipes-kernel/linux/linux-obmc/0001-hwmon-Add-support-for-MAX31785-intelligent-fan-contr.patch b/meta-phosphor/common/recipes-kernel/linux/linux-obmc/0001-hwmon-Add-support-for-MAX31785-intelligent-fan-contr.patch new file mode 100644 index 000000000..8ade1be61 --- /dev/null +++ b/meta-phosphor/common/recipes-kernel/linux/linux-obmc/0001-hwmon-Add-support-for-MAX31785-intelligent-fan-contr.patch @@ -0,0 +1,821 @@ +From b3991e27b26930ef46206dad17715d8ac2f38a4e Mon Sep 17 00:00:00 2001 +From: Timothy Pearson <tpearson@raptorengineering.com> +Date: Tue, 11 Oct 2016 09:35:51 -0500 +Subject: [PATCH] hwmon: Add support for MAX31785 intelligent fan controller + +Add a basic driver for the MAX31785, focusing on the fan control +features but ignoring the temperature and voltage monitoring +features of the device. + +This driver supports all fan control modes and tachometer / PWM +readback where applicable. + +Signed-off-by: Timothy Pearson <tpearson@raptorengineering.com> +Signed-off-by: Joel Stanley <joel@jms.id.au> +--- + Documentation/hwmon/max31785 | 36 +++ + drivers/hwmon/Kconfig | 10 + + drivers/hwmon/Makefile | 1 + + drivers/hwmon/max31785.c | 714 +++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 761 insertions(+) + create mode 100644 Documentation/hwmon/max31785 + create mode 100644 drivers/hwmon/max31785.c + +diff --git a/Documentation/hwmon/max31785 b/Documentation/hwmon/max31785 +new file mode 100644 +index 0000000..0911d20 +--- /dev/null ++++ b/Documentation/hwmon/max31785 +@@ -0,0 +1,36 @@ ++Kernel driver max31785 ++====================== ++ ++Supported chips: ++ * Maxim MAX31785 ++ Prefix: 'max31785' ++ Addresses scanned: 0x52 0x53 0x54 0x55 ++ Datasheet: http://pdfserv.maximintegrated.com/en/ds/MAX31785.pdf ++ ++Author: Timothy Pearson <tpearson@raptorengineering.com> ++ ++ ++Description ++----------- ++ ++This driver implements support for the Maxim MAX31785 chip. ++ ++The MAX31785 controls the speeds of up to six fans using six independent ++PWM outputs. The desired fan speeds (or PWM duty cycles) are written ++through the I2C interface. The outputs drive "4-wire" fans directly, ++or can be used to modulate the fan's power terminals using an external ++pass transistor. ++ ++Tachometer inputs monitor fan tachometer logic outputs for precise (+/-1%) ++monitoring and control of fan RPM as well as detection of fan failure. ++ ++ ++Sysfs entries ++------------- ++ ++fan[1-6]_input RO fan tachometer speed in RPM ++fan[1-6]_fault RO fan experienced fault ++fan[1-6]_pulses RW tachometer pulses per fan revolution ++fan[1-6]_target RW desired fan speed in RPM ++pwm[1-6]_enable RW pwm mode, 0=disabled, 1=pwm, 2=rpm, 3=automatic ++pwm[1-6] RW fan target duty cycle (0-255) +diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig +index 190d270..136605d 100644 +--- a/drivers/hwmon/Kconfig ++++ b/drivers/hwmon/Kconfig +@@ -886,6 +886,16 @@ config SENSORS_MAX6697 + This driver can also be built as a module. If so, the module + will be called max6697. + ++config SENSORS_MAX31785 ++ tristate "Maxim MAX31785 sensor chip" ++ depends on I2C ++ help ++ If you say yes here you get support for 6-Channel PWM-Output ++ Fan RPM Controller. ++ ++ This driver can also be built as a module. If so, the module ++ will be called max31785. ++ + config SENSORS_MAX31790 + tristate "Maxim MAX31790 sensor chip" + depends on I2C +diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile +index d2cb7e8..e8ba5c6 100644 +--- a/drivers/hwmon/Makefile ++++ b/drivers/hwmon/Makefile +@@ -119,6 +119,7 @@ obj-$(CONFIG_SENSORS_MAX6639) += max6639.o + obj-$(CONFIG_SENSORS_MAX6642) += max6642.o + obj-$(CONFIG_SENSORS_MAX6650) += max6650.o + obj-$(CONFIG_SENSORS_MAX6697) += max6697.o ++obj-$(CONFIG_SENSORS_MAX31785) += max31785.o + obj-$(CONFIG_SENSORS_MAX31790) += max31790.o + obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o + obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o +diff --git a/drivers/hwmon/max31785.c b/drivers/hwmon/max31785.c +new file mode 100644 +index 0000000..fb7b3f0 +--- /dev/null ++++ b/drivers/hwmon/max31785.c +@@ -0,0 +1,714 @@ ++/* ++ * max31785.c - Part of lm_sensors, Linux kernel modules for hardware ++ * monitoring. ++ * ++ * (C) 2016 Raptor Engineering, LLC ++ * ++ * 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 <linux/err.h> ++#include <linux/hwmon.h> ++#include <linux/hwmon-sysfs.h> ++#include <linux/i2c.h> ++#include <linux/init.h> ++#include <linux/jiffies.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++ ++/* MAX31785 device IDs */ ++#define MAX31785_MFR_ID 0x4d ++#define MAX31785_MFR_MODEL 0x53 ++ ++/* MAX31785 registers */ ++#define MAX31785_REG_PAGE 0x00 ++#define MAX31785_PAGE_FAN_CONFIG(ch) (0x00 + (ch)) ++#define MAX31785_REG_FAN_CONFIG_1_2 0x3a ++#define MAX31785_REG_FAN_COMMAND_1 0x3b ++#define MAX31785_REG_STATUS_FANS_1_2 0x81 ++#define MAX31785_REG_FAN_SPEED_1 0x90 ++#define MAX31785_REG_MFR_ID 0x99 ++#define MAX31785_REG_MFR_MODEL 0x9a ++#define MAX31785_REG_MFR_FAN_CONFIG 0xf1 ++#define MAX31785_REG_READ_FAN_PWM 0xf3 ++ ++/* Fan Config register bits */ ++#define MAX31785_FAN_CFG_PWM_ENABLE 0x80 ++#define MAX31785_FAN_CFG_CONTROL_MODE_RPM 0x40 ++#define MAX31785_FAN_CFG_PULSE_MASK 0x30 ++#define MAX31785_FAN_CFG_PULSE_SHIFT 4 ++#define MAX31785_FAN_CFG_PULSE_OFFSET 1 ++ ++/* Fan Status register bits */ ++#define MAX31785_FAN_STATUS_FAULT_MASK 0x80 ++ ++/* Fan Command constants */ ++#define MAX31785_FAN_COMMAND_PWM_RATIO 40 ++ ++#define NR_CHANNEL 6 ++ ++/* Addresses to scan */ ++static const unsigned short normal_i2c[] = { 0x52, 0x53, 0x54, 0x55, ++ I2C_CLIENT_END }; ++ ++/* ++ * Client data (each client gets its own) ++ */ ++struct max31785_data { ++ struct i2c_client *client; ++ struct mutex device_lock; ++ bool valid; /* zero until following fields are valid */ ++ unsigned long last_updated; /* in jiffies */ ++ ++ /* register values */ ++ u8 fan_config[NR_CHANNEL]; ++ u16 fan_command[NR_CHANNEL]; ++ u8 mfr_fan_config[NR_CHANNEL]; ++ u8 fault_status[NR_CHANNEL]; ++ u16 tach_rpm[NR_CHANNEL]; ++ u16 pwm[NR_CHANNEL]; ++}; ++ ++static int max31785_set_page(struct i2c_client *client, ++ u8 page) ++{ ++ return i2c_smbus_write_byte_data(client, ++ MAX31785_REG_PAGE, ++ page); ++} ++ ++static int max31785_read_fan_data(struct i2c_client *client, ++ u8 fan, u8 reg, u8 byte_mode) ++{ ++ int rv; ++ ++ rv = max31785_set_page(client, MAX31785_PAGE_FAN_CONFIG(fan)); ++ if (rv < 0) ++ return rv; ++ ++ if (byte_mode) ++ rv = i2c_smbus_read_byte_data(client, reg); ++ else ++ rv = i2c_smbus_read_word_data(client, reg); ++ ++ return rv; ++} ++ ++static int max31785_write_fan_data(struct i2c_client *client, ++ u8 fan, u8 reg, u16 data, ++ u8 byte_mode) ++{ ++ int err; ++ ++ err = max31785_set_page(client, MAX31785_PAGE_FAN_CONFIG(fan)); ++ if (err < 0) ++ return err; ++ ++ if (byte_mode) ++ err = i2c_smbus_write_byte_data(client, reg, data); ++ else ++ err = i2c_smbus_write_word_data(client, reg, data); ++ ++ if (err < 0) ++ return err; ++ ++ return 0; ++} ++ ++static bool is_automatic_control_mode(struct max31785_data *data, ++ int index) ++{ ++ if (data->fan_command[index] > 0x7fff) ++ return true; ++ else ++ return false; ++} ++ ++static struct max31785_data *max31785_update_device(struct device *dev) ++{ ++ struct max31785_data *data = dev_get_drvdata(dev); ++ struct i2c_client *client = data->client; ++ struct max31785_data *ret = data; ++ int i; ++ int rv; ++ ++ mutex_lock(&data->device_lock); ++ ++ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { ++ for (i = 0; i < NR_CHANNEL; i++) { ++ rv = max31785_read_fan_data(client, i, ++ MAX31785_REG_STATUS_FANS_1_2, 1); ++ if (rv < 0) ++ goto abort; ++ data->fault_status[i] = rv; ++ ++ rv = max31785_read_fan_data(client, i, ++ MAX31785_REG_FAN_SPEED_1, 0); ++ if (rv < 0) ++ goto abort; ++ data->tach_rpm[i] = rv; ++ ++ if ((data->fan_config[i] ++ & MAX31785_FAN_CFG_CONTROL_MODE_RPM) ++ || is_automatic_control_mode(data, i)) { ++ rv = max31785_read_fan_data(client, i, ++ MAX31785_REG_READ_FAN_PWM, 0); ++ if (rv < 0) ++ goto abort; ++ data->pwm[i] = rv; ++ } ++ ++ if (!is_automatic_control_mode(data, i)) { ++ /* Poke watchdog for manual fan control */ ++ rv = max31785_write_fan_data(client, ++ i, MAX31785_REG_FAN_COMMAND_1, ++ data->fan_command[i], 0); ++ if (rv < 0) ++ goto abort; ++ } ++ } ++ ++ data->last_updated = jiffies; ++ data->valid = true; ++ } ++ goto done; ++ ++abort: ++ data->valid = false; ++ ret = ERR_PTR(rv); ++ ++done: ++ mutex_unlock(&data->device_lock); ++ ++ return ret; ++} ++ ++static ssize_t get_fan(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); ++ struct max31785_data *data = max31785_update_device(dev); ++ ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ ++ return sprintf(buf, "%d\n", data->tach_rpm[attr->index]); ++} ++ ++static ssize_t get_fan_target(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); ++ struct max31785_data *data = max31785_update_device(dev); ++ int rpm; ++ ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ ++ if (data->fan_config[attr->index] ++ & MAX31785_FAN_CFG_CONTROL_MODE_RPM) ++ rpm = data->fan_command[attr->index]; ++ else ++ rpm = data->fan_command[attr->index] ++ / MAX31785_FAN_COMMAND_PWM_RATIO; ++ ++ return sprintf(buf, "%d\n", rpm); ++} ++ ++static ssize_t set_fan_target(struct device *dev, ++ struct device_attribute *devattr, ++ const char *buf, size_t count) ++{ ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); ++ struct max31785_data *data = dev_get_drvdata(dev); ++ struct i2c_client *client = data->client; ++ unsigned long rpm; ++ int err; ++ ++ err = kstrtoul(buf, 10, &rpm); ++ if (err) ++ return err; ++ ++ if (rpm > 0x7fff) ++ return -EINVAL; ++ ++ mutex_lock(&data->device_lock); ++ ++ /* Write new RPM value */ ++ data->fan_command[attr->index] = rpm; ++ err = max31785_write_fan_data(client, attr->index, ++ MAX31785_REG_FAN_COMMAND_1, ++ data->fan_command[attr->index], 0); ++ ++ mutex_unlock(&data->device_lock); ++ ++ if (err < 0) ++ return err; ++ ++ return count; ++} ++ ++static ssize_t get_fan_pulses(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); ++ struct max31785_data *data = max31785_update_device(dev); ++ int pulses; ++ ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ ++ pulses = ((data->fan_config[attr->index] & MAX31785_FAN_CFG_PULSE_MASK) ++ >> MAX31785_FAN_CFG_PULSE_SHIFT) ++ + MAX31785_FAN_CFG_PULSE_OFFSET; ++ ++ return sprintf(buf, "%d\n", pulses); ++} ++ ++static ssize_t set_fan_pulses(struct device *dev, ++ struct device_attribute *devattr, ++ const char *buf, size_t count) ++{ ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); ++ struct max31785_data *data = dev_get_drvdata(dev); ++ struct i2c_client *client = data->client; ++ unsigned long pulses; ++ int err; ++ ++ err = kstrtoul(buf, 10, &pulses); ++ if (err) ++ return err; ++ ++ if (pulses > 4) ++ return -EINVAL; ++ ++ data->fan_config[attr->index] &= MAX31785_FAN_CFG_PULSE_MASK; ++ data->fan_config[attr->index] |= ++ ((pulses - MAX31785_FAN_CFG_PULSE_OFFSET) ++ << MAX31785_FAN_CFG_PULSE_SHIFT); ++ ++ mutex_lock(&data->device_lock); ++ ++ /* Write new pulse value */ ++ data->fan_command[attr->index] = pulses; ++ err = max31785_write_fan_data(client, attr->index, ++ MAX31785_REG_FAN_CONFIG_1_2, ++ data->fan_config[attr->index], 1); ++ ++ mutex_unlock(&data->device_lock); ++ ++ if (err < 0) ++ return err; ++ ++ return count; ++} ++ ++static ssize_t get_pwm(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); ++ struct max31785_data *data = max31785_update_device(dev); ++ int pwm; ++ ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ ++ if ((data->fan_config[attr->index] ++ & MAX31785_FAN_CFG_CONTROL_MODE_RPM) ++ || is_automatic_control_mode(data, attr->index)) ++ pwm = data->pwm[attr->index] / 100; ++ else ++ pwm = data->fan_command[attr->index] ++ / MAX31785_FAN_COMMAND_PWM_RATIO; ++ ++ return sprintf(buf, "%d\n", pwm); ++} ++ ++static ssize_t set_pwm(struct device *dev, ++ struct device_attribute *devattr, ++ const char *buf, size_t count) ++{ ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); ++ struct max31785_data *data = dev_get_drvdata(dev); ++ struct i2c_client *client = data->client; ++ unsigned long pwm; ++ int err; ++ ++ err = kstrtoul(buf, 10, &pwm); ++ if (err) ++ return err; ++ ++ if (pwm > 255) ++ return -EINVAL; ++ ++ mutex_lock(&data->device_lock); ++ ++ /* Write new PWM value */ ++ data->fan_command[attr->index] = pwm * MAX31785_FAN_COMMAND_PWM_RATIO; ++ err = max31785_write_fan_data(client, attr->index, ++ MAX31785_REG_FAN_COMMAND_1, ++ data->fan_command[attr->index], 0); ++ ++ mutex_unlock(&data->device_lock); ++ ++ if (err < 0) ++ return err; ++ ++ return count; ++} ++ ++static ssize_t get_pwm_enable(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); ++ struct max31785_data *data = max31785_update_device(dev); ++ int mode; ++ ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ ++ if (!(data->fan_config[attr->index] & MAX31785_FAN_CFG_PWM_ENABLE)) ++ mode = 0; ++ else if (is_automatic_control_mode(data, attr->index)) ++ mode = 3; ++ else if (data->fan_config[attr->index] ++ & MAX31785_FAN_CFG_CONTROL_MODE_RPM) ++ mode = 2; ++ else ++ mode = 1; ++ ++ return sprintf(buf, "%d\n", mode); ++} ++ ++static ssize_t set_pwm_enable(struct device *dev, ++ struct device_attribute *devattr, ++ const char *buf, size_t count) ++{ ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); ++ struct max31785_data *data = dev_get_drvdata(dev); ++ struct i2c_client *client = data->client; ++ unsigned long mode; ++ int err; ++ ++ err = kstrtoul(buf, 10, &mode); ++ if (err) ++ return err; ++ ++ switch (mode) { ++ case 0: ++ data->fan_config[attr->index] = ++ data->fan_config[attr->index] ++ & ~MAX31785_FAN_CFG_PWM_ENABLE; ++ break; ++ case 1: ++ case 2: ++ case 3: ++ data->fan_config[attr->index] = ++ data->fan_config[attr->index] ++ | MAX31785_FAN_CFG_PWM_ENABLE; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (mode) { ++ case 0: ++ break; ++ case 1: ++ data->fan_config[attr->index] = ++ data->fan_config[attr->index] ++ & ~MAX31785_FAN_CFG_CONTROL_MODE_RPM; ++ break; ++ case 2: ++ data->fan_config[attr->index] = ++ data->fan_config[attr->index] ++ | MAX31785_FAN_CFG_CONTROL_MODE_RPM; ++ break; ++ case 3: ++ data->fan_command[attr->index] = 0xffff; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ mutex_lock(&data->device_lock); ++ ++ err = max31785_write_fan_data(client, attr->index, ++ MAX31785_REG_FAN_CONFIG_1_2, ++ data->fan_config[attr->index], 1); ++ ++ if (err < 0) ++ goto abort; ++ ++ err = max31785_write_fan_data(client, attr->index, ++ MAX31785_REG_FAN_COMMAND_1, ++ data->fan_command[attr->index], 0); ++ ++abort: ++ mutex_unlock(&data->device_lock); ++ ++ if (err < 0) ++ return err; ++ ++ return count; ++} ++ ++static ssize_t get_fan_fault(struct device *dev, ++ struct device_attribute *devattr, char *buf) ++{ ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); ++ struct max31785_data *data = max31785_update_device(dev); ++ int fault; ++ ++ if (IS_ERR(data)) ++ return PTR_ERR(data); ++ ++ fault = !!(data->fault_status[attr->index] ++ & MAX31785_FAN_STATUS_FAULT_MASK); ++ ++ return sprintf(buf, "%d\n", fault); ++} ++ ++static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan, NULL, 0); ++static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, get_fan, NULL, 1); ++static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, get_fan, NULL, 2); ++static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, get_fan, NULL, 3); ++static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, get_fan, NULL, 4); ++static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, get_fan, NULL, 5); ++ ++static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, get_fan_fault, NULL, 0); ++static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, get_fan_fault, NULL, 1); ++static SENSOR_DEVICE_ATTR(fan3_fault, S_IRUGO, get_fan_fault, NULL, 2); ++static SENSOR_DEVICE_ATTR(fan4_fault, S_IRUGO, get_fan_fault, NULL, 3); ++static SENSOR_DEVICE_ATTR(fan5_fault, S_IRUGO, get_fan_fault, NULL, 4); ++static SENSOR_DEVICE_ATTR(fan6_fault, S_IRUGO, get_fan_fault, NULL, 5); ++ ++static SENSOR_DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, ++ get_fan_target, set_fan_target, 0); ++static SENSOR_DEVICE_ATTR(fan2_target, S_IWUSR | S_IRUGO, ++ get_fan_target, set_fan_target, 1); ++static SENSOR_DEVICE_ATTR(fan3_target, S_IWUSR | S_IRUGO, ++ get_fan_target, set_fan_target, 2); ++static SENSOR_DEVICE_ATTR(fan4_target, S_IWUSR | S_IRUGO, ++ get_fan_target, set_fan_target, 3); ++static SENSOR_DEVICE_ATTR(fan5_target, S_IWUSR | S_IRUGO, ++ get_fan_target, set_fan_target, 4); ++static SENSOR_DEVICE_ATTR(fan6_target, S_IWUSR | S_IRUGO, ++ get_fan_target, set_fan_target, 5); ++ ++static SENSOR_DEVICE_ATTR(fan1_pulses, S_IWUSR | S_IRUGO, ++ get_fan_pulses, set_fan_pulses, 0); ++static SENSOR_DEVICE_ATTR(fan2_pulses, S_IWUSR | S_IRUGO, ++ get_fan_pulses, set_fan_pulses, 1); ++static SENSOR_DEVICE_ATTR(fan3_pulses, S_IWUSR | S_IRUGO, ++ get_fan_pulses, set_fan_pulses, 2); ++static SENSOR_DEVICE_ATTR(fan4_pulses, S_IWUSR | S_IRUGO, ++ get_fan_pulses, set_fan_pulses, 3); ++static SENSOR_DEVICE_ATTR(fan5_pulses, S_IWUSR | S_IRUGO, ++ get_fan_pulses, set_fan_pulses, 4); ++static SENSOR_DEVICE_ATTR(fan6_pulses, S_IWUSR | S_IRUGO, ++ get_fan_pulses, set_fan_pulses, 5); ++ ++static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 0); ++static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 1); ++static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 2); ++static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 3); ++static SENSOR_DEVICE_ATTR(pwm5, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 4); ++static SENSOR_DEVICE_ATTR(pwm6, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 5); ++ ++static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, ++ get_pwm_enable, set_pwm_enable, 0); ++static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, ++ get_pwm_enable, set_pwm_enable, 1); ++static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, ++ get_pwm_enable, set_pwm_enable, 2); ++static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, ++ get_pwm_enable, set_pwm_enable, 3); ++static SENSOR_DEVICE_ATTR(pwm5_enable, S_IWUSR | S_IRUGO, ++ get_pwm_enable, set_pwm_enable, 4); ++static SENSOR_DEVICE_ATTR(pwm6_enable, S_IWUSR | S_IRUGO, ++ get_pwm_enable, set_pwm_enable, 5); ++ ++static struct attribute *max31785_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_fan1_fault.dev_attr.attr, ++ &sensor_dev_attr_fan2_fault.dev_attr.attr, ++ &sensor_dev_attr_fan3_fault.dev_attr.attr, ++ &sensor_dev_attr_fan4_fault.dev_attr.attr, ++ &sensor_dev_attr_fan5_fault.dev_attr.attr, ++ &sensor_dev_attr_fan6_fault.dev_attr.attr, ++ ++ &sensor_dev_attr_fan1_target.dev_attr.attr, ++ &sensor_dev_attr_fan2_target.dev_attr.attr, ++ &sensor_dev_attr_fan3_target.dev_attr.attr, ++ &sensor_dev_attr_fan4_target.dev_attr.attr, ++ &sensor_dev_attr_fan5_target.dev_attr.attr, ++ &sensor_dev_attr_fan6_target.dev_attr.attr, ++ ++ &sensor_dev_attr_fan1_pulses.dev_attr.attr, ++ &sensor_dev_attr_fan2_pulses.dev_attr.attr, ++ &sensor_dev_attr_fan3_pulses.dev_attr.attr, ++ &sensor_dev_attr_fan4_pulses.dev_attr.attr, ++ &sensor_dev_attr_fan5_pulses.dev_attr.attr, ++ &sensor_dev_attr_fan6_pulses.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_pwm1_enable.dev_attr.attr, ++ &sensor_dev_attr_pwm2_enable.dev_attr.attr, ++ &sensor_dev_attr_pwm3_enable.dev_attr.attr, ++ &sensor_dev_attr_pwm4_enable.dev_attr.attr, ++ &sensor_dev_attr_pwm5_enable.dev_attr.attr, ++ &sensor_dev_attr_pwm6_enable.dev_attr.attr, ++ NULL ++}; ++ ++static umode_t max31785_attrs_visible(struct kobject *kobj, ++ struct attribute *a, int n) ++{ ++ return a->mode; ++} ++ ++static const struct attribute_group max31785_group = { ++ .attrs = max31785_attrs, ++ .is_visible = max31785_attrs_visible, ++}; ++__ATTRIBUTE_GROUPS(max31785); ++ ++static int max31785_init_client(struct i2c_client *client, ++ struct max31785_data *data) ++{ ++ int i, rv; ++ ++ for (i = 0; i < NR_CHANNEL; i++) { ++ rv = max31785_read_fan_data(client, i, ++ MAX31785_REG_FAN_CONFIG_1_2, 1); ++ if (rv < 0) ++ return rv; ++ data->fan_config[i] = rv; ++ ++ rv = max31785_read_fan_data(client, i, ++ MAX31785_REG_FAN_COMMAND_1, 0); ++ if (rv < 0) ++ return rv; ++ data->fan_command[i] = rv; ++ ++ rv = max31785_read_fan_data(client, i, ++ MAX31785_REG_MFR_FAN_CONFIG, 1); ++ if (rv < 0) ++ return rv; ++ data->mfr_fan_config[i] = rv; ++ ++ if (!((data->fan_config[i] ++ & MAX31785_FAN_CFG_CONTROL_MODE_RPM) ++ || is_automatic_control_mode(data, i))) { ++ data->pwm[i] = 0; ++ } ++ } ++ ++ return rv; ++} ++ ++/* Return 0 if detection is successful, -ENODEV otherwise */ ++static int max31785_detect(struct i2c_client *client, ++ struct i2c_board_info *info) ++{ ++ struct i2c_adapter *adapter = client->adapter; ++ int rv; ++ ++ if (!i2c_check_functionality(adapter, ++ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) ++ return -ENODEV; ++ ++ /* Probe manufacturer / model registers */ ++ rv = i2c_smbus_read_byte_data(client, MAX31785_REG_MFR_ID); ++ if (rv < 0) ++ return -ENODEV; ++ if (rv != MAX31785_MFR_ID) ++ return -ENODEV; ++ ++ rv = i2c_smbus_read_byte_data(client, MAX31785_REG_MFR_MODEL); ++ if (rv < 0) ++ return -ENODEV; ++ if (rv != MAX31785_MFR_MODEL) ++ return -ENODEV; ++ ++ strlcpy(info->type, "max31785", I2C_NAME_SIZE); ++ ++ return 0; ++} ++ ++static int max31785_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct i2c_adapter *adapter = client->adapter; ++ struct device *dev = &client->dev; ++ struct max31785_data *data; ++ struct device *hwmon_dev; ++ int err; ++ ++ if (!i2c_check_functionality(adapter, ++ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) ++ return -ENODEV; ++ ++ data = devm_kzalloc(dev, sizeof(struct max31785_data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ data->client = client; ++ mutex_init(&data->device_lock); ++ ++ /* ++ * Initialize the max31785 chip ++ */ ++ err = max31785_init_client(client, data); ++ if (err) ++ return err; ++ ++ hwmon_dev = devm_hwmon_device_register_with_groups(dev, ++ client->name, data, max31785_groups); ++ ++ return PTR_ERR_OR_ZERO(hwmon_dev); ++} ++ ++static const struct i2c_device_id max31785_id[] = { ++ { "max31785", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, max31785_id); ++ ++static struct i2c_driver max31785_driver = { ++ .class = I2C_CLASS_HWMON, ++ .probe = max31785_probe, ++ .driver = { ++ .name = "max31785", ++ }, ++ .id_table = max31785_id, ++ .detect = max31785_detect, ++ .address_list = normal_i2c, ++}; ++ ++module_i2c_driver(max31785_driver); ++ ++MODULE_AUTHOR("Timothy Pearson <tpearson@raptorengineering.com>"); ++MODULE_DESCRIPTION("MAX31785 sensor driver"); ++MODULE_LICENSE("GPL"); +-- +1.8.3.1 + |