Signed-off-by: Andrew Jeffery <and...@aj.id.au>
---
 drivers/hwmon/pmbus/Kconfig    |  10 ++
 drivers/hwmon/pmbus/Makefile   |   1 +
 drivers/hwmon/pmbus/max31785.c | 201 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 212 insertions(+)
 create mode 100644 drivers/hwmon/pmbus/max31785.c

diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index cad1229b7e17..5f2f3c6c7499 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -95,6 +95,16 @@ config SENSORS_MAX20751
          This driver can also be built as a module. If so, the module will
          be called max20751.
 
+config SENSORS_MAX31785
+       tristate "Maxim MAX31785 and compatibles"
+       default n
+       help
+         If you say yes here you get hardware monitoring support for Maxim
+         MAX31785.
+
+         This driver can also be built as a module. If so, the module will
+         be called max31785.
+
 config SENSORS_MAX34440
        tristate "Maxim MAX34440 and compatibles"
        default n
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index 24ff7ee7f8bb..acba1be0d9ad 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_SENSORS_PMBUS)   += pmbus.o
 # obj-$(CONFIG_SENSORS_LTC3815)        += ltc3815.o
 # obj-$(CONFIG_SENSORS_MAX16064)       += max16064.o
 # obj-$(CONFIG_SENSORS_MAX20751)       += max20751.o
+obj-$(CONFIG_SENSORS_MAX31785) += max31785.o
 # obj-$(CONFIG_SENSORS_MAX34440)       += max34440.o
 # obj-$(CONFIG_SENSORS_MAX8688)        += max8688.o
 # obj-$(CONFIG_SENSORS_TPS40422)       += tps40422.o
diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c
new file mode 100644
index 000000000000..8432ed5a0ad6
--- /dev/null
+++ b/drivers/hwmon/pmbus/max31785.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2017 IBM Corp.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include "pmbus.h"
+
+enum max31785_regs {
+       PMBUS_MFR_FAN_CONFIG            = 0xF1,
+       PMBUS_MFR_READ_FAN_PWM          = 0xF3,
+       PMBUS_MFR_FAN_FAULT_LIMIT       = 0xF5,
+       PMBUS_MFR_FAN_WARN_LIMIT        = 0xF6,
+       PMBUS_MFR_FAN_PWM_AVG           = 0xF8,
+};
+
+static const struct pmbus_coeffs fan_coeffs[] = {
+       [percent]       = { .m = 1, .b = 0, .R = 2 },
+       [rpm]           = { .m = 1, .b = 0, .R = 0 },
+};
+
+static const struct pmbus_coeffs *max31785_get_fan_coeffs(
+               const struct pmbus_driver_info *info, int index,
+               enum pmbus_fan_mode mode, int command)
+{
+       switch (command) {
+       case PMBUS_FAN_COMMAND_1:
+       case PMBUS_MFR_FAN_FAULT_LIMIT:
+       case PMBUS_MFR_FAN_WARN_LIMIT:
+               return &fan_coeffs[mode];
+       case PMBUS_READ_FAN_SPEED_1:
+               return &fan_coeffs[rpm];
+       case PMBUS_MFR_FAN_PWM_AVG:
+               return &fan_coeffs[percent];
+       default:
+               break;
+       }
+
+       return NULL;
+}
+
+static int max31785_get_pwm_mode(int id, u8 fan_config, u16 fan_command)
+{
+       enum pmbus_fan_mode mode;
+
+       /* MAX31785 only supports fan 1 on the fan pages */
+       if (WARN_ON(id > 0))
+               return -ENODEV;
+
+       mode = (fan_config & PB_FAN_1_RPM) ? rpm : percent;
+
+       switch (mode) {
+       case percent:
+               if (fan_command >= 0x8000)
+                       return 2;
+               else if (fan_command >= 0x2711)
+                       return 0;
+               else
+                       return 1;
+       case rpm:
+               if (fan_command >= 0x8000)
+                       return 2;
+               else
+                       return 1;
+               break;
+       }
+
+       return 0;
+}
+
+static const int max31785_pwm_modes[] = { 0x7fff, 0x2710, 0xffff };
+
+int max31785_set_pwm_mode(int id, long mode, u8 *fan_config, u16 *fan_command)
+{
+       /* MAX31785 only supports fan 1 on the fan pages */
+       if (WARN_ON(id > 0))
+               return -ENODEV;
+
+       *fan_config &= ~PB_FAN_1_RPM;
+
+       if (mode >= ARRAY_SIZE(max31785_pwm_modes))
+               return -ENOTSUPP;
+
+       *fan_command = max31785_pwm_modes[mode];
+
+       return 0;
+}
+
+static struct pmbus_driver_info max31785_info = {
+       .pages = 23,
+
+       .get_fan_coeffs = max31785_get_fan_coeffs,
+       .get_pwm_mode = max31785_get_pwm_mode,
+       .set_pwm_mode = max31785_set_pwm_mode,
+
+       .format[PSC_FAN] = direct,
+       .func[0] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12,
+       .func[1] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12,
+       .func[2] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12,
+       .func[3] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12,
+       .func[4] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12,
+       .func[5] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12,
+
+       .format[PSC_TEMPERATURE] = direct,
+       .coeffs[PSC_TEMPERATURE].m = 1,
+       .coeffs[PSC_TEMPERATURE].b = 0,
+       .coeffs[PSC_TEMPERATURE].R = 2,
+       .func[6]  = PMBUS_HAVE_STATUS_TEMP,
+       .func[7]  = PMBUS_HAVE_STATUS_TEMP,
+       .func[8]  = PMBUS_HAVE_STATUS_TEMP,
+       .func[9]  = PMBUS_HAVE_STATUS_TEMP,
+       .func[10] = PMBUS_HAVE_STATUS_TEMP,
+       .func[11] = PMBUS_HAVE_STATUS_TEMP,
+       .func[12] = PMBUS_HAVE_STATUS_TEMP,
+       .func[13] = PMBUS_HAVE_STATUS_TEMP,
+       .func[14] = PMBUS_HAVE_STATUS_TEMP,
+       .func[15] = PMBUS_HAVE_STATUS_TEMP,
+       .func[16] = PMBUS_HAVE_STATUS_TEMP,
+
+       .format[PSC_VOLTAGE_OUT] = direct,
+       .coeffs[PSC_VOLTAGE_OUT].m = 1,
+       .coeffs[PSC_VOLTAGE_OUT].b = 0,
+       .coeffs[PSC_VOLTAGE_OUT].R = 0,
+       .func[17] = PMBUS_HAVE_STATUS_VOUT,
+       .func[18] = PMBUS_HAVE_STATUS_VOUT,
+       .func[19] = PMBUS_HAVE_STATUS_VOUT,
+       .func[20] = PMBUS_HAVE_STATUS_VOUT,
+       .func[21] = PMBUS_HAVE_STATUS_VOUT,
+       .func[22] = PMBUS_HAVE_STATUS_VOUT,
+};
+
+#define MFR_FAN_CONFIG_TSFO            BIT(9)
+#define MFR_FAN_CONFIG_TACHO           BIT(8)
+
+static int max31785_probe(struct i2c_client *client,
+                         const struct i2c_device_id *id)
+{
+       int rv;
+       int i;
+
+       rv = pmbus_do_probe(client, id, &max31785_info);
+       if (rv < 0)
+               return rv;
+
+       for (i = 0; i < max31785_info.pages; i++) {
+               int reg;
+
+               if (!(max31785_info.func[i] & (PMBUS_HAVE_FAN12)))
+                       continue;
+
+               reg = pmbus_read_word_data(client, i, PMBUS_MFR_FAN_CONFIG);
+               if (reg < 0)
+                       continue;
+
+               /*
+                * XXX: Purely for RFC/testing purposes, don't ramp fans on fan
+                * or temperature sensor fault, or a failure to write
+                * FAN_COMMAND_1 inside a 10s window (watchdog mode).
+                *
+                * The TSFO bit controls both ramping on temp sensor failure
+                * AND whether FAN_COMMAND_1 is in watchdog mode.
+                */
+               reg = (reg | MFR_FAN_CONFIG_TSFO | MFR_FAN_CONFIG_TACHO);
+               reg &= 0xffff;
+
+               rv = pmbus_write_word_data(client, i, PMBUS_MFR_FAN_CONFIG,
+                                          reg);
+       }
+
+       return 0;
+}
+
+static const struct i2c_device_id max31785_id[] = {
+       { "max31785", 0 },
+       { },
+};
+
+MODULE_DEVICE_TABLE(i2c, max31785_id);
+
+static struct i2c_driver max31785_driver = {
+       .driver = {
+               .name = "max31785",
+       },
+       .probe = max31785_probe,
+       .remove = pmbus_do_remove,
+       .id_table = max31785_id,
+};
+
+module_i2c_driver(max31785_driver);
+
+MODULE_AUTHOR("Andrew Jeffery <and...@aj.id.au>");
+MODULE_DESCRIPTION("PMBus driver for the Maxim MAX31785");
+MODULE_LICENSE("GPL");
-- 
2.11.0

Reply via email to