[PATCH v2] Initial driver for the MAX31785 intelligent fan controller

Joel Stanley joel at jms.id.au
Wed Oct 12 16:56:01 AEDT 2016


On Wed, Oct 12, 2016 at 1:35 AM, Timothy Pearson
<tpearson at raptorengineering.com> wrote:
> 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 at raptorengineering.com>

Thanks, applied to dev-4.7.

The next step is to get it enabled in the kernel configuration for
Firestone. I think it's the same chip used in Witherspoon, so we can
enable that too.

Cheers,

Joel

> ---
>  Documentation/hwmon/max31785 |   36 +++
>  drivers/hwmon/Kconfig        |   10 +
>  drivers/hwmon/Makefile       |    1 +
>  drivers/hwmon/max31785.c     |  714 ++++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 768 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 at 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 3b34ba9..d4de8d5 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -874,6 +874,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 c0f3201..4455478 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -117,6 +117,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 at raptorengineering.com>");
> +MODULE_DESCRIPTION("MAX31785 sensor driver");
> +MODULE_LICENSE("GPL");
> -- 1.7.9.5
> _______________________________________________
> openbmc mailing list
> openbmc at lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/openbmc


More information about the openbmc mailing list