| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Maxim Integrated MAX31760 Precision Fan-Speed Controller driver |
| * |
| * Copyright (C) 2019 Google, Inc. |
| * Author: muirj |
| * |
| * 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/bitops.h> |
| #include <linux/delay.h> |
| #include <linux/device.h> |
| #include <linux/err.h> |
| #include <linux/gpio/consumer.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/mutex.h> |
| #include <linux/of.h> |
| #include <linux/regmap.h> |
| #include <linux/regulator/consumer.h> |
| #include <linux/workqueue.h> |
| |
| #define DRIVER_NAME "max31760" |
| |
| /* |
| * MAX31760 registers. |
| * Indentation helps identify how these constants apply: |
| * Register number. |
| * Per-register bit fields. |
| * Values for multi-bit fields. |
| */ |
| /* clang-format off */ |
| #define MAX31760_REG_CR1 0x00 /* Control Register 1 */ |
| #define MAX31760_CR1_TIS 0x01 /* Temperature Index Source */ |
| #define MAX31760_CR1_MTI 0x02 /* Maximum Temperature Index */ |
| #define MAX31760_CR1_PPS 0x04 /* PWM Polarity */ |
| #define MAX31760_CR1_DRV 0x18 /* PWM Frequency */ |
| #define MAX31760_DRV_33HZ 0x00 |
| #define MAX31760_DRV_150HZ 0x08 |
| #define MAX31760_DRV_1500HZ 0x10 |
| #define MAX31760_DRV_25KHZ MAX31760_CR1_DRV |
| #define MAX31760_CR1_HYST 0x20 /* Lookup Table Hysteresis: 2C or 4C */ |
| #define MAX31760_CR1_POR 0x40 /* Software Power-On Reset */ |
| #define MAX31760_CR1_ALTMSK 0x80 /* Alert Mask */ |
| #define MAX31760_REG_CR2 0x01 /* Control Register 2 */ |
| #define MAX31760_CR2_DFC 0x01 /* Direct Fan Control */ |
| #define MAX31760_CR2_FSST 0x02 /* Fan Sense Signal Type */ |
| #define MAX31760_CR2_RDPS 0x04 /* RD Polarity Selection */ |
| #define MAX31760_CR2_FSEN 0x08 /* FS Input Enable */ |
| #define MAX31760_CR2_FFMODE 0x10 /* FF Functionality Selection */ |
| #define MAX31760_CR2_SPEN 0x20 /* Spin-up Enable */ |
| #define MAX31760_CR2_ALERTS 0x40 /* Alerts Functionality Selection */ |
| #define MAX31760_CR2_STBY 0x80 /* Standby Mode Enable */ |
| #define MAX31760_REG_CR3 0x02 /* Control Register 3 */ |
| #define MAX31760_CR3_TACH1E 0x01 /* Tachometer 1 Enable */ |
| #define MAX31760_CR3_TACH2E 0x02 /* Tachometer 2 Enable */ |
| #define MAX31760_CR3_PSEN 0x04 /* Pulse Stretch Enable */ |
| #define MAX31760_CR3_TACHFL 0x08 /* Fan Fail When 100% Duty Cycle Only */ |
| #define MAX31760_CR3_RAMP 0x30 /* PWM Duty-Cycle Ramp Rate */ |
| #define MAX31760_CR3_RAMP_SLOW 0x00 |
| #define MAX31760_CR3_RAMP_SMED 0x10 |
| #define MAX31760_CR3_RAMP_MEDF 0x20 |
| #define MAX31760_CR3_RAMP_FAST MAX31760_CR3_RAMP |
| #define MAX31760_CR3_FF_0 0x40 /* 0 Duty-Cycle Fan-Fail Detection */ |
| #define MAX31760_CR3_CLR_FF 0x80 /* Clear Fan Fail */ |
| #define MAX31760_REG_FFDC 0x03 /* Fan Fault Duty Cycle */ |
| #define MAX31760_REG_MASK 0x04 /* Alert Mask Register */ |
| #define MAX31760_MASK_TACH1AM 0x01 /* TACH1 Alarm Mask */ |
| #define MAX31760_MASK_TACH2AM 0x02 /* TACH2 Alarm Mask */ |
| #define MAX31760_MASK_ROTAM 0x04 /* Remote Overtemperature Alarm Mask */ |
| #define MAX31760_MASK_RHAM 0x08 /* Remote High Temperature Alarm Mask */ |
| #define MAX31760_MASK_LOTAM 0x10 /* Local Overtemperature Alarm Mask */ |
| #define MAX31760_MASK_LHAM 0x20 /* Local High Temperature Alarm Mask */ |
| #define MAX31760_REG_IFR 0x05 /* Ideality Factor Register */ |
| #define MAX31760_IFR_MASK 0x3f /* Mask for value of the IFR */ |
| #define MAX31760_REG_RHSH 0x06 /* Remote High Set-point MSB */ |
| #define MAX31760_REG_RHSL 0x07 /* Remote High Set-point LSB */ |
| #define MAX31760_REG_LOTSH 0x08 /* Local Overtemperature Set-point MSB */ |
| #define MAX31760_REG_LOTSL 0x09 /* Local Overtemperature Set-point LSB */ |
| #define MAX31760_REG_ROTSH 0x0a /* Remote Overtemperature Set-point MSB */ |
| #define MAX31760_REG_ROTSL 0x0b /* Remote Overtemperature Set-point LSB */ |
| #define MAX31760_REG_LHSH 0x0c /* Local High Set-point MSB */ |
| #define MAX31760_REG_LHSL 0x0d /* Local High Set-point LSB */ |
| #define MAX31760_REG_TCTH 0x0e /* TACH Count Threshold Register, MSB */ |
| #define MAX31760_REG_TCTL 0x0f /* TACH Count Threshold Register, LSB */ |
| #define MAX31760_REG_USER 0x10 /* 8 bytes General Purpose User Memory */ |
| #define MAX31760_REG_USER0 0x10 /* Custom Control Register USER0 */ |
| #define MAX31760_USER0_PULSE1 0x07 /* Fan1 Pulses per revolution */ |
| #define MAX31760_USER0_PULSE2 0x38 /* Fan2 Pulses per revolution */ |
| #define MAX31760_USER0_RAMP 0xC0 /* User-specified PWM Ramp Rate. */ |
| #define MAX31760_USER0_RAMP_SLOW 0x00 |
| #define MAX31760_USER0_RAMP_SMED 0x40 |
| #define MAX31760_USER0_RAMP_MEDF 0x80 |
| #define MAX31760_USER0_RAMP_FAST MAX31760_USER0_RAMP |
| #define MAX31760_REG_USER1_PWMR 0x11 /* Manual mode PWM value for resume */ |
| #define MAX31760_REG_USER2 0x12 /* Custom Control Register USER2 */ |
| #define MAX31760_USER2_DFC 0x01 /* User-specified Direct Fan Control. */ |
| #define MAX31760_REG_USER3_SPINUP_DELAY 0x13 /* Stores spin-up delay decisec. */ |
| #define MAX31760_REG_USER4_RESTORE_DELAY 0x14 /* Stores reg restore delay. */ |
| #define MAX31760_REG_RESERVED1_START 0x18 /* Reserved area 1 start */ |
| #define MAX31760_REG_RESERVED1_END 0x1f /* Reserved area 1 end */ |
| #define MAX31760_REG_LUT 0x20 /* 48-Byte Lookup Table (LUT) */ |
| #define MAX31760_LUT_COUNT 48 |
| #define MAX31760_REG_PWMR 0x50 /* Direct Duty-Cycle Control Register */ |
| |
| #define MAX31760_REG_PWMV 0x51 /* Current PWM Duty-Cycle Register */ |
| #define MAX31760_REG_TC1H 0x52 /* TACH1 Count Register, MSB */ |
| #define MAX31760_REG_TC1L 0x53 /* TACH1 Count Register, LSB */ |
| #define MAX31760_REG_TC2H 0x54 /* TACH2 Count Register, MSB */ |
| #define MAX31760_REG_TC2L 0x55 /* TACH2 Count Register, LSB */ |
| #define MAX31760_REG_RTH 0x56 /* Remote Temperature Reading Register, MSB */ |
| #define MAX31760_REG_RTL 0x57 /* Remote Temperature Reading Register, LSB */ |
| #define MAX31760_REG_LTH 0x58 /* Local Temperature Reading Register, MSB */ |
| #define MAX31760_REG_LTL 0x59 /* Local Temperature Reading Register, LSB */ |
| #define MAX31760_REG_SR 0x5a /* Status Register */ |
| #define MAX31760_SR_TACH1A 0x01 /* TACH1 Alarm */ |
| #define MAX31760_SR_TACH2A 0x02 /* TACH2 Alarm */ |
| #define MAX31760_SR_ROTA 0x04 /* Remote Overtemperature Alarm */ |
| #define MAX31760_SR_RHA 0x08 /* Remote High Temperature Alarm */ |
| #define MAX31760_SR_LOTA 0x10 /* Local Overtemperature Alarm */ |
| #define MAX31760_SR_LHA 0x20 /* Local High Temperature Alarm */ |
| #define MAX31760_SR_RDFA 0x40 /* Remote Diode Fault Alarm */ |
| #define MAX31760_SR_PC 0x80 /* Program Corrupt Bit */ |
| |
| #define MAX31760_REG_EEX 0x5b /* Load EEPROM to RAM; Write RAM to EEPROM */ |
| #define MAX31760_EEX_LW 0x80 /* Load from or write to EEPROM */ |
| #define MAX31760_EEX_BLKS 0x1F /* Blocks to load/write */ |
| #define MAX31760_EEX_WRITE_TIME_MS 550 /* Time to write all blocks to EEPROM */ |
| /* clang-format on */ |
| |
| #define MAX31760_NUM_FANS 2 /* Number of fans. */ |
| #define MAX31760_FAN_PULSES_DEF 2 /* Default number of fan pulses. */ |
| #define MAX31760_FAN_PULSES_MAX 8 /* Maximum number of fan pulses. */ |
| #define MAX31760_FAN_SUPPLY_NAME_SIZE 12 /* Fit: maxim,fan1\0. */ |
| |
| #define MAX31760_PWM_COUNT 2 /* Pretend there is a PWM per fan. */ |
| #define MAX31760_PWM_ENABLE_FULL 0 /* pwmX_enable: Set PWM at full power. */ |
| #define MAX31760_PWM_ENABLE_MANUAL 1 /* pwmX_enable: Set manual mode. */ |
| #define MAX31760_PWM_ENABLE_AUTO 2 /* pwmX_enable: Set automatic mode. */ |
| |
| #define MAX31760_NUM_TEMPS 2 /* Number of temperature sensors. */ |
| #define MAX31760_TEMP_MIN_MC -40000 /* Minimum Millicelcius */ |
| #define MAX31760_TEMP_MAX_MC 127875 /* Maximum Millicelcius */ |
| #define MAX31760_TEMP_HIGH_HYST 1000 /* 1C hysteresis on high temp alarms. */ |
| #define MAX31760_TEMP_OVER_HYST 10000 /* 10C hysteresis on over temp alarms. */ |
| |
| #define MAX31760_LUT_HYST_CLEAR 2000 /* LUT hysteresis: bit clear. */ |
| #define MAX31760_LYT_HYST_THRESH 3000 /* LUT hysteresis: store threshold. */ |
| #define MAX31760_LUT_HYST_SET 4000 /* LUT hysteresis: bit set. */ |
| #define MAX31760_LUT_AUTO_ATTRS 3 /* Number of LUT auto-point attributes. */ |
| #define MAX31760_LUT_AUTO_ATTR_COUNT \ |
| (MAX31760_PWM_COUNT * MAX31760_LUT_COUNT * MAX31760_LUT_AUTO_ATTRS) |
| #define MAX31760_LUT_NAME_SIZE 32 /* Fit: pwm1_auto_pointXX_temp_hyst\0. */ |
| |
| /* |
| * Delay types. |
| * @SPINUP: Delay fan spin-up after resume. |
| * @RESTORE: Delay restoring user-specified register values. |
| */ |
| enum max31760_delay_type { |
| MAX31760_DELAY_TYPE_SPINUP, |
| MAX31760_DELAY_TYPE_RESTORE, |
| }; |
| |
| #define MAX31760_DELAY_FACTOR 100 |
| #define MAX31760_DELAY_MAX_MSEC (U8_MAX * MAX31760_DELAY_FACTOR) /* 25.5s */ |
| |
| /* |
| * struct max31760_delay_info |
| * @property: Property name for this delay. |
| * @reg: Storage register for this delay. |
| */ |
| struct max31760_delay_info { |
| const char *property; |
| u8 reg; |
| }; |
| |
| static const struct max31760_delay_info max31760_delays[] = { |
| { |
| .property = "maxim,spin-up-delay", |
| .reg = MAX31760_REG_USER3_SPINUP_DELAY, |
| }, |
| { |
| .property = "maxim,register-restore-delay", |
| .reg = MAX31760_REG_USER4_RESTORE_DELAY, |
| }, |
| }; |
| |
| /* |
| * Driver states: See max31760_state_do_work_locked(). |
| * @UNDEFINED: Null state. |
| * @SLEEP: Device is sleeping (in standby, or system suspended). |
| * @WAKE: Device is asked to wake up. |
| * @SPINUP_DELAY: Delay before moving to the SPINUP state. |
| * @SPINUP: Device is spinning-up the fans. |
| * @RESTORE_DELAY: Delay before moving to the RESTORE state. |
| * @RESTORE: Restore user-specified register values. |
| * @RUNNING: Device is running normally. |
| */ |
| enum max31760_state { |
| MAX31760_STATE_UNDEFINED, |
| MAX31760_STATE_SLEEP, |
| MAX31760_STATE_WAKE, |
| MAX31760_STATE_SPINUP_DELAY, |
| MAX31760_STATE_SPINUP, |
| MAX31760_STATE_RESTORE_DELAY, |
| MAX31760_STATE_RESTORE, |
| MAX31760_STATE_RUNNING, |
| }; |
| |
| /* Mode for delays during state changes. */ |
| enum max31760_delay_mode { |
| MAX31760_DELAY_MODE_ALLOWED, /* Delays are allowed. */ |
| MAX31760_DELAY_MODE_SKIP, /* Change state immediately. */ |
| }; |
| |
| /* |
| * struct max31760_fan - fan device data |
| * @pulses: Number of tach pulses per rotation. |
| * @enabled: True when the fan is enabled. |
| * @label: Label if provided in open firmware. |
| * @enable_gpio: GPIO that enables this fan. |
| * @supply: Power supply for this fan. |
| */ |
| struct max31760_fan { |
| const char *label; |
| int pulses; |
| bool enabled; |
| struct gpio_desc *enable_gpio; |
| struct regulator *supply; |
| }; |
| |
| /* |
| * struct max31760_dev_attr - for generated device attributes |
| * @sdattr: Sensor device attribute. |
| * @name: Name of this attribute. |
| */ |
| struct max31760_dev_attr { |
| struct sensor_device_attribute sdattr; |
| char name[MAX31760_LUT_NAME_SIZE]; |
| }; |
| |
| /* |
| * struct max31760 - device data |
| * @dev: Pointer to parent device structure. |
| * @lock: Mutex locked while manipulating this device. |
| * @regmap: Register map. |
| * @stay_awake: Stay awake and continue controlling fans during system suspend. |
| * @vdd_supply: Optional regulator that supplies VDD. |
| * @fan: Fan data. |
| * @temp_label: Labels for the temperature sensors if provided in open firmware. |
| * @lut_dev_attrs: Device attributes for the temperature to PWM lookup table. |
| * @lut_attrs: Pointers to the struct attribute in each lut_dev_attr. |
| * @lut_group: Attribute group for the LUT attributes. |
| * @attr_groups: Sysfs attribute groups for this device. |
| * @state: The state of the driver's state machine. |
| * @logged_state: The last state printed to the (console) log. |
| * @state_work: State machine's delayed work. |
| * @delay_jiffies: Delay times (in jiffies). |
| */ |
| struct max31760 { |
| struct device *dev; |
| struct mutex lock; |
| struct regmap *regmap; |
| bool stay_awake; |
| struct regulator *vdd_supply; |
| struct max31760_fan fan[MAX31760_NUM_FANS]; |
| const char *temp_label[MAX31760_NUM_TEMPS]; |
| struct max31760_dev_attr lut_dev_attrs[MAX31760_LUT_AUTO_ATTR_COUNT]; |
| struct attribute *lut_attrs[MAX31760_LUT_AUTO_ATTR_COUNT + 1]; |
| struct attribute_group lut_group; |
| const struct attribute_group *attr_groups[4]; |
| enum max31760_state state; |
| enum max31760_state logged_state; |
| struct delayed_work state_work; |
| unsigned long delay_jiffies[ARRAY_SIZE(max31760_delays)]; |
| }; |
| |
| static const struct regmap_range max31760_not_readable_ranges[] = { |
| regmap_reg_range(MAX31760_REG_RESERVED1_START, |
| MAX31760_REG_RESERVED1_END), |
| regmap_reg_range(MAX31760_REG_EEX, MAX31760_REG_EEX), |
| }; |
| |
| static const struct regmap_access_table max31760_readable_table = { |
| .no_ranges = max31760_not_readable_ranges, |
| .n_no_ranges = ARRAY_SIZE(max31760_not_readable_ranges), |
| }; |
| |
| static const struct regmap_range max31760_not_writable_ranges[] = { |
| regmap_reg_range(MAX31760_REG_RESERVED1_START, |
| MAX31760_REG_RESERVED1_END), |
| regmap_reg_range(MAX31760_REG_PWMV, MAX31760_REG_SR), |
| }; |
| |
| static const struct regmap_access_table max31760_writable_table = { |
| .no_ranges = max31760_not_writable_ranges, |
| .n_no_ranges = ARRAY_SIZE(max31760_not_writable_ranges), |
| }; |
| |
| static const struct regmap_range max31760_is_volatile_ranges[] = { |
| regmap_reg_range(MAX31760_REG_MASK, MAX31760_REG_MASK), |
| regmap_reg_range(MAX31760_REG_PWMR, MAX31760_REG_EEX), |
| }; |
| |
| static const struct regmap_access_table max31760_volatile_table = { |
| .yes_ranges = max31760_is_volatile_ranges, |
| .n_yes_ranges = ARRAY_SIZE(max31760_is_volatile_ranges), |
| }; |
| |
| static bool max31760_writeable_noinc_reg(struct device *dev, unsigned int reg) |
| { |
| /* |
| * Can write consecutive bytes on a memory "page", where each page is 8 |
| * bytes (starting on 0x00 or 0x08). In other words, we cannot increment |
| * automatically into registers with addresses ending in 0x0 or 0x8. |
| */ |
| reg &= 0xf; |
| return reg != 0x0 && reg != 0x8; |
| } |
| |
| static const struct regmap_config max31760_regmap_config = { |
| /* |
| * Device has an EEPROM to store the register values, so don't define |
| * reg_defaults: read the current values from the hardware. |
| */ |
| .reg_bits = 8, |
| .val_bits = 8, |
| .disable_locking = true, |
| .max_register = MAX31760_REG_EEX, |
| .wr_table = &max31760_writable_table, |
| .rd_table = &max31760_readable_table, |
| .volatile_table = &max31760_volatile_table, |
| .writeable_noinc_reg = max31760_writeable_noinc_reg, |
| .val_format_endian = REGMAP_ENDIAN_BIG, |
| .cache_type = REGCACHE_RBTREE, |
| .can_multi_write = true, |
| }; |
| |
| /* Read pulses for fan with index |fan_index|. */ |
| static int max31760_read_fan_pulses(struct max31760 *max31760, |
| unsigned int fan_index, |
| unsigned int *pulses) |
| { |
| int err; |
| unsigned int regval; |
| |
| if (fan_index >= MAX31760_NUM_FANS) |
| return -EINVAL; |
| |
| err = regmap_read(max31760->regmap, MAX31760_REG_USER0, ®val); |
| if (err) |
| return err; |
| /* Only supports two fans at the moment. */ |
| if (fan_index == 1) |
| *pulses = (regval & MAX31760_USER0_PULSE2) >> 3; |
| else |
| *pulses = regval & MAX31760_USER0_PULSE1; |
| if (*pulses == 0) |
| *pulses = MAX31760_FAN_PULSES_DEF; |
| return 0; |
| } |
| |
| /* Force the regcache to be invalidated, and restore fan pulses values. */ |
| static int max31760_regcache_drop(struct max31760 *max31760) |
| { |
| int err; |
| int i; |
| unsigned int regval; |
| |
| err = regcache_drop_region(max31760->regmap, 0, |
| max31760_regmap_config.max_register); |
| if (err) |
| return err; |
| |
| /* Restore fan pulses values. */ |
| for (i = 0; i < MAX31760_NUM_FANS; i++) { |
| err = max31760_read_fan_pulses(max31760, i, ®val); |
| if (err) |
| return err; |
| max31760->fan[i].pulses = regval; |
| } |
| |
| /* Restore delay times. */ |
| for (i = 0; i < ARRAY_SIZE(max31760_delays); i++) { |
| err = regmap_read(max31760->regmap, max31760_delays[i].reg, |
| ®val); |
| if (err) |
| return err; |
| max31760->delay_jiffies[i] = |
| msecs_to_jiffies(regval * MAX31760_DELAY_FACTOR); |
| } |
| |
| return 0; |
| } |
| |
| /* Set or clear the standby bit. */ |
| static int max31760_set_standby(struct max31760 *max31760, bool standby) |
| { |
| int err; |
| unsigned int regval = standby ? MAX31760_CR2_STBY : 0; |
| |
| err = regmap_update_bits(max31760->regmap, MAX31760_REG_CR2, |
| MAX31760_CR2_STBY, regval); |
| if (err) |
| dev_err(max31760->dev, "Could not %s standby bit: %d", |
| standby ? "set" : "clear", err); |
| return err; |
| } |
| |
| /* Disable fans and make changes immediate. */ |
| static int max31760_disable_fans_fast(struct max31760 *max31760) |
| { |
| int err; |
| |
| /* Set DFC on, PWMR to 0, and RAMP rate to fast. */ |
| err = regmap_update_bits(max31760->regmap, MAX31760_REG_CR2, |
| MAX31760_CR2_DFC, MAX31760_CR2_DFC); |
| if (err) |
| return err; |
| |
| err = regmap_write(max31760->regmap, MAX31760_REG_PWMR, 0); |
| if (err) |
| return err; |
| |
| err = regmap_update_bits(max31760->regmap, MAX31760_REG_CR3, |
| MAX31760_CR3_RAMP, MAX31760_CR3_RAMP_FAST); |
| if (err) |
| return err; |
| |
| return 0; |
| } |
| |
| /* Update the ramp rate from the user-specified value. */ |
| static int max31760_update_ramp_rate(struct max31760 *max31760) |
| { |
| unsigned int regval; |
| int err; |
| |
| err = regmap_read(max31760->regmap, MAX31760_REG_USER0, ®val); |
| if (err) |
| return err; |
| switch (regval & MAX31760_USER0_RAMP) { |
| case MAX31760_USER0_RAMP_SLOW: |
| regval = MAX31760_CR3_RAMP_SLOW; |
| break; |
| case MAX31760_USER0_RAMP_SMED: |
| regval = MAX31760_CR3_RAMP_SMED; |
| break; |
| case MAX31760_USER0_RAMP_MEDF: |
| regval = MAX31760_CR3_RAMP_MEDF; |
| break; |
| case MAX31760_USER0_RAMP_FAST: |
| regval = MAX31760_CR3_RAMP_FAST; |
| break; |
| } |
| return regmap_update_bits(max31760->regmap, MAX31760_REG_CR3, |
| MAX31760_CR3_RAMP, regval); |
| } |
| |
| /* Update the PWMR from the user-specified value. */ |
| static int max31760_update_pwmr(struct max31760 *max31760) |
| { |
| unsigned int regval; |
| int err; |
| |
| err = regmap_read(max31760->regmap, MAX31760_REG_USER1_PWMR, ®val); |
| if (err) |
| return err; |
| return regmap_write(max31760->regmap, MAX31760_REG_PWMR, regval); |
| } |
| |
| /* Update the DFC from the user-specified value. */ |
| static int max31760_update_dfc(struct max31760 *max31760) |
| { |
| unsigned int regval; |
| int err; |
| |
| err = regmap_read(max31760->regmap, MAX31760_REG_USER2, ®val); |
| if (err) |
| return err; |
| regval = (regval & MAX31760_USER2_DFC) ? MAX31760_CR2_DFC : 0; |
| return regmap_update_bits(max31760->regmap, MAX31760_REG_CR2, |
| MAX31760_CR2_DFC, regval); |
| } |
| |
| /* Return a string representing the state. */ |
| static const char *max31760_state_to_string(enum max31760_state state) |
| { |
| switch (state) { |
| case MAX31760_STATE_UNDEFINED: |
| break; |
| case MAX31760_STATE_SLEEP: |
| return "sleep"; |
| case MAX31760_STATE_WAKE: |
| return "wake"; |
| case MAX31760_STATE_SPINUP_DELAY: |
| return "spin-up delay"; |
| case MAX31760_STATE_SPINUP: |
| return "spin-up"; |
| case MAX31760_STATE_RESTORE_DELAY: |
| return "restore delay"; |
| case MAX31760_STATE_RESTORE: |
| return "restore"; |
| case MAX31760_STATE_RUNNING: |
| return "running"; |
| } |
| return "undef"; |
| } |
| |
| /* |
| * Execute work for every state of enum max31760_state. |
| * The mutex must be locked by the caller. |
| */ |
| static int max31760_state_do_work_locked(struct max31760 *max31760, |
| enum max31760_delay_mode delay_mode, |
| enum max31760_state *err_state) |
| { |
| int err; |
| unsigned int regval; |
| |
| switch (max31760->state) { |
| case MAX31760_STATE_UNDEFINED: |
| return -EINVAL; |
| case MAX31760_STATE_SLEEP: |
| /* |
| * This state is used in error conditions and must not reset |
| * max31760->state. |
| */ |
| *err_state = MAX31760_STATE_UNDEFINED; |
| err = max31760_set_standby(max31760, true); |
| break; |
| case MAX31760_STATE_WAKE: |
| *err_state = MAX31760_STATE_SLEEP; |
| /* Reset the register cache and restore local variables. */ |
| err = max31760_regcache_drop(max31760); |
| if (err) |
| return err; |
| /* Set DFC on, PWMR to 0, and RAMP rate to fast. */ |
| err = max31760_disable_fans_fast(max31760); |
| if (err) |
| return err; |
| /* Clear the standby bit. */ |
| err = max31760_set_standby(max31760, false); |
| if (err) |
| return err; |
| /* If there is a spin-up delay, then sleep for a bit. */ |
| if (max31760->delay_jiffies[MAX31760_DELAY_TYPE_SPINUP] == 0 || |
| delay_mode == MAX31760_DELAY_MODE_SKIP) { |
| max31760->state = MAX31760_STATE_SPINUP; |
| } else { |
| max31760->state = MAX31760_STATE_SPINUP_DELAY; |
| if (!schedule_delayed_work( |
| &max31760->state_work, |
| max31760->delay_jiffies |
| [MAX31760_DELAY_TYPE_SPINUP])) |
| return -EINTR; |
| } |
| break; |
| case MAX31760_STATE_SPINUP_DELAY: |
| *err_state = MAX31760_STATE_SLEEP; |
| /* If the timer has expired, then we spinup. */ |
| if (delay_mode == MAX31760_DELAY_MODE_ALLOWED && |
| time_is_after_jiffies(max31760->state_work.timer.expires)) |
| break; |
| max31760->state = MAX31760_STATE_SPINUP; |
| break; |
| case MAX31760_STATE_SPINUP: |
| *err_state = MAX31760_STATE_SLEEP; |
| /* Restore PWM and DFC. Try again on error. */ |
| err = max31760_update_pwmr(max31760); |
| if (err) |
| return err; |
| err = max31760_update_dfc(max31760); |
| if (err) |
| return err; |
| /* If we have an update delay, and SPEN is set, then we wait. */ |
| if (max31760->delay_jiffies[MAX31760_DELAY_TYPE_RESTORE] > 0) { |
| err = regmap_read(max31760->regmap, MAX31760_REG_CR2, |
| ®val); |
| if (err) |
| return err; |
| } |
| if (delay_mode == MAX31760_DELAY_MODE_ALLOWED && |
| max31760->delay_jiffies[MAX31760_DELAY_TYPE_RESTORE] > 0 && |
| (regval & MAX31760_CR2_SPEN)) { |
| max31760->state = MAX31760_STATE_RESTORE_DELAY; |
| if (!schedule_delayed_work( |
| &max31760->state_work, |
| max31760->delay_jiffies |
| [MAX31760_DELAY_TYPE_RESTORE])) |
| return -EINTR; |
| } else { |
| max31760->state = MAX31760_STATE_RESTORE; |
| } |
| break; |
| case MAX31760_STATE_RESTORE_DELAY: |
| *err_state = MAX31760_STATE_WAKE; |
| /* If the timer has expired, then we update registers. */ |
| if (delay_mode == MAX31760_DELAY_MODE_ALLOWED && |
| time_is_after_jiffies(max31760->state_work.timer.expires)) |
| break; |
| max31760->state = MAX31760_STATE_RESTORE; |
| break; |
| case MAX31760_STATE_RESTORE: |
| /* Restore RAMP. Retry everything on error. */ |
| *err_state = MAX31760_STATE_WAKE; |
| err = max31760_update_ramp_rate(max31760); |
| if (err) |
| return err; |
| max31760->state = MAX31760_STATE_RUNNING; |
| break; |
| case MAX31760_STATE_RUNNING: |
| /* Do nothing. */ |
| break; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * Execute state work while we are changing the state. |
| * The mutex must be locked by the caller. |
| */ |
| static int max31760_state_execute_locked(struct max31760 *max31760, |
| enum max31760_delay_mode delay_mode) |
| { |
| enum max31760_state err_state = MAX31760_STATE_SLEEP; |
| enum max31760_state state = MAX31760_STATE_UNDEFINED; |
| int err = 0; |
| |
| while (state != max31760->state) { |
| state = max31760->state; |
| if (max31760->logged_state != max31760->state) { |
| dev_info(max31760->dev, "state: %s", |
| max31760_state_to_string(max31760->state)); |
| max31760->logged_state = max31760->state; |
| } |
| |
| err = max31760_state_do_work_locked(max31760, delay_mode, |
| &err_state); |
| if (err) |
| max31760->state = err_state; |
| } |
| |
| return err; |
| } |
| |
| /* Delayed work function. */ |
| static void max31760_state_work(struct work_struct *work) |
| { |
| struct max31760 *max31760 = container_of(to_delayed_work(work), |
| struct max31760, state_work); |
| |
| mutex_lock(&max31760->lock); |
| max31760_state_execute_locked(max31760, MAX31760_DELAY_MODE_ALLOWED); |
| mutex_unlock(&max31760->lock); |
| } |
| |
| /* Set the state and execute it. */ |
| static int max31760_set_state_locked(struct max31760 *max31760, |
| enum max31760_state state, |
| enum max31760_delay_mode delay_mode) |
| { |
| int err = 0; |
| |
| if (state == max31760->state) |
| return 0; |
| switch (max31760->state) { |
| case MAX31760_STATE_SPINUP_DELAY: |
| case MAX31760_STATE_RESTORE_DELAY: |
| /* |
| * Invalidate the state in case of a race condition while we |
| * cancel the work. |
| */ |
| max31760->state = MAX31760_STATE_UNDEFINED; |
| mutex_unlock(&max31760->lock); |
| if (!cancel_delayed_work_sync(&max31760->state_work)) { |
| dev_err(max31760->dev, "Failed to cancel work."); |
| err = -EINTR; |
| } |
| mutex_lock(&max31760->lock); |
| break; |
| default: |
| break; |
| } |
| if (err == 0) |
| max31760->state = state; |
| else |
| max31760->state = MAX31760_STATE_SLEEP; |
| return max31760_state_execute_locked(max31760, delay_mode); |
| } |
| |
| /* Action to take when the user-specified ramp rate has changed. */ |
| static int max31760_ramp_rate_changed(struct max31760 *max31760) |
| { |
| if (max31760->state == MAX31760_STATE_RUNNING) |
| return max31760_update_ramp_rate(max31760); |
| return 0; |
| } |
| |
| /* Action to take when the user-specified DFC or PWMR have changed. */ |
| static int max31760_dfc_pwmr_changed(struct max31760 *max31760) |
| { |
| int err = 0; |
| |
| switch (max31760->state) { |
| case MAX31760_STATE_SLEEP: |
| case MAX31760_STATE_WAKE: |
| case MAX31760_STATE_SPINUP_DELAY: |
| case MAX31760_STATE_SPINUP: |
| break; |
| case MAX31760_STATE_RESTORE_DELAY: |
| case MAX31760_STATE_RESTORE: |
| case MAX31760_STATE_RUNNING: |
| case MAX31760_STATE_UNDEFINED: |
| err = max31760_update_pwmr(max31760); |
| if (!err) |
| err = max31760_update_dfc(max31760); |
| break; |
| } |
| |
| return err; |
| } |
| |
| /* Perform the actions for waking up the device. */ |
| static int max31760_state_wake_up(struct max31760 *max31760, |
| enum max31760_delay_mode delay_mode) |
| { |
| int err; |
| |
| mutex_lock(&max31760->lock); |
| if (max31760->state == MAX31760_STATE_RUNNING) { |
| mutex_unlock(&max31760->lock); |
| return 0; |
| } |
| err = max31760_set_state_locked(max31760, MAX31760_STATE_WAKE, |
| delay_mode); |
| mutex_unlock(&max31760->lock); |
| |
| return err; |
| } |
| |
| /* Perform the actions for going to sleep. */ |
| static int max31760_state_go_to_sleep(struct max31760 *max31760) |
| { |
| int err; |
| |
| mutex_lock(&max31760->lock); |
| err = max31760_set_state_locked(max31760, MAX31760_STATE_SLEEP, |
| MAX31760_DELAY_MODE_SKIP); |
| mutex_unlock(&max31760->lock); |
| |
| return err; |
| } |
| |
| /* Convert tachometer value to RPM. */ |
| static inline long max31760_rpm_from_tach(u16 tach_count, int pulses) |
| { |
| if (!tach_count) |
| return 0; |
| |
| return 60L * 100000L / (long)tach_count / (long)pulses; |
| } |
| |
| /* Convert RPM to tachometer value. */ |
| static inline u16 max31760_tach_from_rpm(long rpm, int pulses) |
| { |
| long tach = 60L * 100000L / rpm / (long)pulses; |
| |
| if (tach < 0) |
| tach = 0; |
| else if (tach > (long)USHRT_MAX) |
| tach = USHRT_MAX; |
| |
| return tach; |
| } |
| |
| /* |
| * Read two subsequent registers into a 16-bit word, treating the first as the |
| * most significant byte. |
| */ |
| static int max31760_read_word(struct regmap *regmap, unsigned int regmsb, |
| u16 *word) |
| { |
| int err; |
| u8 val[2]; |
| |
| err = regmap_bulk_read(regmap, regmsb, val, 2); |
| if (err < 0) |
| return err; |
| |
| *word = ((val[0] << 8) & 0xff00) | (val[1] & 0xff); |
| return 0; |
| } |
| |
| /* |
| * Read an alarm which may be flagged in the status register, or masked in the |
| * alarm mask register. Reading from the status register will cause the bit in |
| * the mask register to be set. |
| */ |
| static int max31760_read_alarm(struct max31760 *max31760, unsigned int srflag, |
| unsigned int maskflag, long *val) |
| { |
| unsigned int srval; |
| unsigned int maskval; |
| int err; |
| |
| err = regmap_read(max31760->regmap, MAX31760_REG_SR, &srval); |
| if (err < 0) |
| return err; |
| err = regmap_read(max31760->regmap, MAX31760_REG_MASK, &maskval); |
| if (err < 0) |
| return err; |
| |
| *val = !!((srval & srflag) | (maskval & maskflag)); |
| return 0; |
| } |
| |
| static int max31760_read_fan(struct max31760 *max31760, u32 attr, int channel, |
| long *val) |
| { |
| u16 tach_count; |
| unsigned int reg = 0; |
| unsigned int srflag; |
| unsigned int maskflag; |
| int err; |
| |
| mutex_lock(&max31760->lock); |
| switch (attr) { |
| case hwmon_fan_input: |
| case hwmon_fan_min: |
| switch (attr) { |
| case hwmon_fan_input: |
| reg = channel ? MAX31760_REG_TC2H : MAX31760_REG_TC1H; |
| break; |
| case hwmon_fan_min: |
| reg = MAX31760_REG_TCTH; |
| break; |
| } |
| err = max31760_read_word(max31760->regmap, reg, &tach_count); |
| if (err) |
| break; |
| *val = max31760_rpm_from_tach(tach_count, |
| max31760->fan[channel].pulses); |
| break; |
| case hwmon_fan_min_alarm: |
| srflag = channel ? MAX31760_SR_TACH2A : MAX31760_SR_TACH1A; |
| maskflag = |
| channel ? MAX31760_MASK_TACH2AM : MAX31760_MASK_TACH1AM; |
| err = max31760_read_alarm(max31760, srflag, maskflag, val); |
| break; |
| case hwmon_fan_pulses: |
| err = max31760_read_fan_pulses(max31760, channel, ®); |
| if (err) |
| break; |
| *val = reg; |
| break; |
| default: |
| err = -EOPNOTSUPP; |
| } |
| mutex_unlock(&max31760->lock); |
| |
| return err; |
| } |
| |
| static int max31760_read_pwm(struct max31760 *max31760, u32 attr, int channel, |
| long *val) |
| { |
| unsigned int regval; |
| int err; |
| |
| mutex_lock(&max31760->lock); |
| switch (attr) { |
| case hwmon_pwm_input: |
| /* |
| * Note that this is the current value, not the value stored to |
| * the duty-cycle register. |
| */ |
| err = regmap_read(max31760->regmap, MAX31760_REG_PWMV, ®val); |
| if (err) |
| break; |
| *val = regval; |
| break; |
| case hwmon_pwm_enable: |
| err = regmap_read(max31760->regmap, MAX31760_REG_USER2, |
| ®val); |
| if (err) |
| break; |
| if (regval & MAX31760_USER2_DFC) |
| *val = MAX31760_PWM_ENABLE_MANUAL; |
| else |
| *val = MAX31760_PWM_ENABLE_AUTO; |
| break; |
| case hwmon_pwm_freq: |
| err = regmap_read(max31760->regmap, MAX31760_REG_CR1, ®val); |
| if (err) |
| break; |
| switch (regval & MAX31760_CR1_DRV) { |
| case MAX31760_DRV_33HZ: |
| default: |
| *val = 33; |
| break; |
| case MAX31760_DRV_150HZ: |
| *val = 150; |
| break; |
| case MAX31760_DRV_1500HZ: |
| *val = 1500; |
| break; |
| case MAX31760_DRV_25KHZ: |
| *val = 25000; |
| break; |
| } |
| break; |
| default: |
| err = -EOPNOTSUPP; |
| break; |
| } |
| mutex_unlock(&max31760->lock); |
| |
| return err; |
| } |
| |
| /* Convert 11-bit MAX31760 register value to milliCelsius */ |
| static inline int max31760_temp_reg_to_mC(s16 val) |
| { |
| /* (val & ~0x1f) >> 5 * 0.125 * 1000 */ |
| return (val & ~0x1f) * 1000 / 256; |
| } |
| |
| /* Convert milliCelsius to left adjusted 11-bit MAX31760 register value */ |
| static inline u16 max31760_mC_to_temp_reg(int val) |
| { |
| return (val * 256) / 1000; |
| } |
| |
| static int max31760_read_temp(struct max31760 *max31760, u32 attr, int channel, |
| long *val) |
| { |
| unsigned int reg = 0; |
| unsigned int regval; |
| unsigned int srflag = 0; |
| unsigned int maskflag = 0; |
| u16 temp; |
| int err; |
| int hyst = 0; |
| |
| mutex_lock(&max31760->lock); |
| switch (attr) { |
| case hwmon_temp_emergency_hyst: |
| hyst = MAX31760_TEMP_OVER_HYST; |
| /* fallthrough */ |
| case hwmon_temp_max_hyst: |
| if (attr == hwmon_temp_max_hyst) |
| hyst = MAX31760_TEMP_HIGH_HYST; |
| /* fallthrough */ |
| case hwmon_temp_input: |
| case hwmon_temp_max: |
| case hwmon_temp_emergency: |
| switch (attr) { |
| case hwmon_temp_input: |
| reg = channel ? MAX31760_REG_RTH : MAX31760_REG_LTH; |
| break; |
| case hwmon_temp_max_hyst: |
| case hwmon_temp_max: |
| reg = channel ? MAX31760_REG_RHSH : MAX31760_REG_LHSH; |
| break; |
| case hwmon_temp_emergency_hyst: |
| case hwmon_temp_emergency: |
| reg = channel ? MAX31760_REG_ROTSH : MAX31760_REG_LOTSH; |
| break; |
| } |
| err = max31760_read_word(max31760->regmap, reg, &temp); |
| if (err < 0) |
| break; |
| *val = max31760_temp_reg_to_mC(temp) - hyst; |
| break; |
| case hwmon_temp_max_alarm: |
| case hwmon_temp_emergency_alarm: |
| switch (attr) { |
| case hwmon_temp_max_alarm: |
| srflag = channel ? MAX31760_SR_RHA : MAX31760_SR_LHA; |
| maskflag = channel ? MAX31760_MASK_RHAM : |
| MAX31760_MASK_LHAM; |
| break; |
| case hwmon_temp_emergency_alarm: |
| srflag = channel ? MAX31760_SR_ROTA : MAX31760_SR_LOTA; |
| maskflag = channel ? MAX31760_MASK_RHAM : |
| MAX31760_MASK_LHAM; |
| break; |
| } |
| err = max31760_read_alarm(max31760, srflag, maskflag, val); |
| break; |
| case hwmon_temp_fault: |
| err = regmap_read(max31760->regmap, MAX31760_REG_SR, ®val); |
| if (err < 0) |
| break; |
| *val = !!(regval & MAX31760_SR_RDFA); |
| break; |
| default: |
| err = -EOPNOTSUPP; |
| break; |
| } |
| mutex_unlock(&max31760->lock); |
| |
| return err; |
| } |
| |
| static int max31760_read(struct device *dev, enum hwmon_sensor_types type, |
| u32 attr, int channel, long *val) |
| { |
| struct max31760 *max31760 = dev_get_drvdata(dev); |
| |
| switch (type) { |
| case hwmon_fan: |
| return max31760_read_fan(max31760, attr, channel, val); |
| case hwmon_pwm: |
| return max31760_read_pwm(max31760, attr, channel, val); |
| case hwmon_temp: |
| return max31760_read_temp(max31760, attr, channel, val); |
| default: |
| return -EOPNOTSUPP; |
| } |
| } |
| |
| static int max31760_read_string(struct device *dev, |
| enum hwmon_sensor_types type, u32 attr, |
| int channel, const char **str) |
| { |
| struct max31760 *max31760 = dev_get_drvdata(dev); |
| |
| switch (type) { |
| case hwmon_fan: |
| if (attr != hwmon_fan_label) |
| return -EOPNOTSUPP; |
| *str = max31760->fan[channel].label; |
| break; |
| case hwmon_temp: |
| if (attr != hwmon_temp_label) |
| return -EOPNOTSUPP; |
| *str = max31760->temp_label[channel]; |
| break; |
| default: |
| return -EOPNOTSUPP; |
| } |
| |
| return 0; |
| } |
| |
| /* Update the quick access fan pulses value. */ |
| static void max31760_update_fan_pulses(struct max31760 *max31760, int channel, |
| int pulses) |
| { |
| if (pulses > MAX31760_FAN_PULSES_MAX) |
| pulses = MAX31760_FAN_PULSES_MAX; |
| else if (pulses <= 0) |
| pulses = MAX31760_FAN_PULSES_DEF; |
| max31760->fan[channel].pulses = pulses; |
| } |
| |
| /* |
| * Write a 16-bit word into two subsequent registers, treating the first as the |
| * most significant byte. |
| */ |
| static int max31760_write_word(struct regmap *regmap, unsigned int regmsb, |
| u16 word) |
| { |
| u8 val[2]; |
| |
| val[0] = (word >> 8) & 0xff; |
| val[1] = word & 0xff; |
| |
| return regmap_bulk_write(regmap, regmsb, val, 2); |
| } |
| |
| static int max31760_write_fan(struct max31760 *max31760, u32 attr, int channel, |
| long val) |
| { |
| unsigned int regval; |
| unsigned int mask; |
| u16 tach; |
| int err; |
| |
| mutex_lock(&max31760->lock); |
| switch (attr) { |
| case hwmon_fan_min: |
| tach = max31760_tach_from_rpm(val, |
| max31760->fan[channel].pulses); |
| err = max31760_write_word(max31760->regmap, MAX31760_REG_TCTH, |
| tach); |
| break; |
| case hwmon_fan_pulses: |
| max31760_update_fan_pulses(max31760, channel, val); |
| regval = (unsigned int)max31760->fan[channel].pulses; |
| if (channel) { |
| regval <<= 3; |
| mask = MAX31760_USER0_PULSE2; |
| } else { |
| mask = MAX31760_USER0_PULSE1; |
| } |
| err = regmap_update_bits(max31760->regmap, MAX31760_REG_USER0, |
| mask, regval); |
| break; |
| default: |
| err = -EOPNOTSUPP; |
| break; |
| } |
| mutex_unlock(&max31760->lock); |
| |
| return err; |
| } |
| |
| static int max31760_write_pwm(struct max31760 *max31760, u32 attr, int channel, |
| long val) |
| { |
| unsigned int regval; |
| int err; |
| |
| mutex_lock(&max31760->lock); |
| switch (attr) { |
| case hwmon_pwm_input: |
| regval = (unsigned int)val & 0xff; |
| err = regmap_write(max31760->regmap, MAX31760_REG_USER1_PWMR, |
| regval); |
| if (err) |
| break; |
| err = max31760_dfc_pwmr_changed(max31760); |
| break; |
| case hwmon_pwm_enable: |
| switch (val) { |
| case MAX31760_PWM_ENABLE_FULL: |
| err = regmap_write(max31760->regmap, |
| MAX31760_REG_USER1_PWMR, 0xff); |
| if (err) |
| break; |
| /* fallthrough */ |
| case MAX31760_PWM_ENABLE_MANUAL: |
| err = regmap_update_bits(max31760->regmap, |
| MAX31760_REG_USER2, |
| MAX31760_USER2_DFC, |
| MAX31760_USER2_DFC); |
| break; |
| default: |
| case MAX31760_PWM_ENABLE_AUTO: |
| err = regmap_update_bits(max31760->regmap, |
| MAX31760_REG_USER2, |
| MAX31760_USER2_DFC, 0); |
| break; |
| } |
| if (err) |
| break; |
| err = max31760_dfc_pwmr_changed(max31760); |
| break; |
| case hwmon_pwm_freq: |
| if (val < 91) |
| regval = MAX31760_DRV_33HZ; |
| else if (val < 825) |
| regval = MAX31760_DRV_150HZ; |
| else if (val < 11000) |
| regval = MAX31760_DRV_1500HZ; |
| else |
| regval = MAX31760_DRV_25KHZ; |
| err = regmap_update_bits(max31760->regmap, MAX31760_REG_CR1, |
| MAX31760_CR1_DRV, regval); |
| break; |
| default: |
| err = -EOPNOTSUPP; |
| break; |
| } |
| mutex_unlock(&max31760->lock); |
| |
| return err; |
| } |
| |
| /* Write a temperature to the two adjacent registers starting at regmsb. */ |
| static int max31760_write_temp_reg(struct regmap *regmap, unsigned int regmsb, |
| long temp) |
| { |
| u16 word; |
| |
| temp = clamp_val(temp, MAX31760_TEMP_MIN_MC, MAX31760_TEMP_MAX_MC); |
| word = max31760_mC_to_temp_reg(temp); |
| |
| return max31760_write_word(regmap, regmsb, word); |
| } |
| |
| static int max31760_write_temp(struct max31760 *max31760, u32 attr, int channel, |
| long val) |
| { |
| int err; |
| |
| mutex_lock(&max31760->lock); |
| switch (attr) { |
| case hwmon_temp_max: |
| err = max31760_write_temp_reg( |
| max31760->regmap, |
| channel ? MAX31760_REG_RHSH : MAX31760_REG_LHSH, val); |
| break; |
| case hwmon_temp_emergency: |
| err = max31760_write_temp_reg( |
| max31760->regmap, |
| channel ? MAX31760_REG_ROTSH : MAX31760_REG_LOTSH, val); |
| break; |
| default: |
| err = -EOPNOTSUPP; |
| break; |
| } |
| mutex_unlock(&max31760->lock); |
| |
| return err; |
| } |
| |
| static int max31760_write(struct device *dev, enum hwmon_sensor_types type, |
| u32 attr, int channel, long val) |
| { |
| struct max31760 *max31760 = dev_get_drvdata(dev); |
| |
| switch (type) { |
| case hwmon_fan: |
| return max31760_write_fan(max31760, attr, channel, val); |
| case hwmon_pwm: |
| return max31760_write_pwm(max31760, attr, channel, val); |
| case hwmon_temp: |
| return max31760_write_temp(max31760, attr, channel, val); |
| default: |
| return -EOPNOTSUPP; |
| } |
| } |
| |
| static umode_t max31760_is_visible(const void *dvrdata, |
| enum hwmon_sensor_types type, u32 attr, |
| int channel) |
| { |
| struct max31760 *max31760 = (struct max31760 *)dvrdata; |
| |
| switch (type) { |
| case hwmon_fan: |
| switch (attr) { |
| case hwmon_fan_input: |
| case hwmon_fan_min_alarm: |
| return 0444; |
| case hwmon_fan_label: |
| if (max31760->fan[channel].label) |
| return 0444; |
| return 0; |
| case hwmon_fan_min: |
| case hwmon_fan_pulses: |
| return 0644; |
| } |
| break; |
| case hwmon_pwm: |
| switch (attr) { |
| case hwmon_pwm_input: |
| case hwmon_pwm_enable: |
| case hwmon_pwm_freq: |
| return 0644; |
| } |
| break; |
| case hwmon_temp: |
| switch (attr) { |
| case hwmon_temp_input: |
| case hwmon_temp_max_hyst: |
| case hwmon_temp_max_alarm: |
| case hwmon_temp_emergency_hyst: |
| case hwmon_temp_emergency_alarm: |
| case hwmon_temp_fault: |
| return 0444; |
| case hwmon_temp_label: |
| if (max31760->temp_label[channel]) |
| return 0444; |
| return 0; |
| case hwmon_temp_max: |
| case hwmon_temp_emergency: |
| return 0644; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static u32 max31760_chip_config[] = { HWMON_C_REGISTER_TZ, 0 }; |
| |
| static const struct hwmon_channel_info max31760_chip = { |
| .type = hwmon_chip, |
| .config = max31760_chip_config, |
| }; |
| |
| static u32 max31760_fan_config[] = { HWMON_F_INPUT | HWMON_F_MIN | |
| HWMON_F_MIN_ALARM | |
| HWMON_F_PULSES | HWMON_F_LABEL, |
| HWMON_F_INPUT | HWMON_F_MIN | |
| HWMON_F_MIN_ALARM | |
| HWMON_F_PULSES | HWMON_F_LABEL, |
| 0 }; |
| |
| static const struct hwmon_channel_info max31760_fan = { |
| .type = hwmon_fan, |
| .config = max31760_fan_config, |
| }; |
| |
| static u32 max31760_pwm_config[] = { |
| HWMON_PWM_INPUT | HWMON_PWM_ENABLE | HWMON_PWM_FREQ, |
| HWMON_PWM_INPUT | HWMON_PWM_ENABLE | HWMON_PWM_FREQ, 0 |
| }; |
| |
| static const struct hwmon_channel_info max31760_pwm = { |
| .type = hwmon_pwm, |
| .config = max31760_pwm_config, |
| }; |
| |
| static u32 max31760_temp_config[] = { |
| /* |
| * Local temperature sensor: |
| * Local high set point (LHS) -> MAX, |
| * Local over-temperature set point (LOTS) -> EMERGENCY |
| * There is no fault flag for this temperature sensor. |
| */ |
| HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MAX | HWMON_T_EMERGENCY | |
| HWMON_T_MAX_ALARM | HWMON_T_EMERGENCY_ALARM | HWMON_T_MAX_HYST | |
| HWMON_T_EMERGENCY_HYST, |
| /* |
| * Remote temperature sensor: |
| * Remote high set point (RHS) -> MAX, |
| * Remote over-temperature set point (ROTS) -> EMERGENCY |
| */ |
| HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MAX | HWMON_T_EMERGENCY | |
| HWMON_T_MAX_ALARM | HWMON_T_EMERGENCY_ALARM | HWMON_T_MAX_HYST | |
| HWMON_T_EMERGENCY_HYST | HWMON_T_FAULT, |
| 0 |
| }; |
| |
| static const struct hwmon_channel_info max31760_temp = { |
| .type = hwmon_temp, |
| .config = max31760_temp_config, |
| }; |
| |
| static const struct hwmon_channel_info *max31760_info[] = { |
| &max31760_chip, &max31760_fan, &max31760_pwm, &max31760_temp, NULL |
| }; |
| |
| static const struct hwmon_ops max31760_hwmon_ops = { |
| .is_visible = max31760_is_visible, |
| .read = max31760_read, |
| .read_string = max31760_read_string, |
| .write = max31760_write, |
| }; |
| |
| static const struct hwmon_chip_info max31760_chip_info = { |
| .ops = &max31760_hwmon_ops, |
| .info = max31760_info, |
| }; |
| |
| /* Show which temperature sensors are used to drive the PWM lookup table. */ |
| static ssize_t max31760_pwm_auto_channels_temp_show( |
| struct device *dev, struct device_attribute *devattr, char *buf) |
| { |
| struct max31760 *max31760 = dev_get_drvdata(dev); |
| unsigned int regval; |
| int channels; |
| int err; |
| |
| mutex_lock(&max31760->lock); |
| err = regmap_read(max31760->regmap, MAX31760_REG_CR1, ®val); |
| mutex_unlock(&max31760->lock); |
| if (err < 0) |
| return err; |
| |
| /* |
| * Auto channels is a bit-field. TIS bit clear: temp1 (local) is used |
| * for the LUT. TIS bit set: temp2 (remote) is used for the LUT. |
| * MTI bit set: maximum temp from both is used, TIS bit is ignored. |
| */ |
| if (regval & MAX31760_CR1_MTI) |
| channels = 3; |
| else if (regval & MAX31760_CR1_TIS) |
| channels = 2; |
| else |
| channels = 1; |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", channels); |
| } |
| |
| /* Store which temperature sensors are used to drive the PWM lookup table. */ |
| static ssize_t |
| max31760_pwm_auto_channels_temp_store(struct device *dev, |
| struct device_attribute *devattr, |
| const char *buf, size_t count) |
| { |
| struct max31760 *max31760 = dev_get_drvdata(dev); |
| unsigned int regval; |
| unsigned int mask; |
| unsigned long channels; |
| int err; |
| |
| err = kstrtoul(buf, 10, &channels); |
| if (err < 0) |
| return err; |
| |
| switch (channels & 0x3) { |
| case 3: |
| mask = MAX31760_CR1_MTI; |
| regval = MAX31760_CR1_MTI; |
| break; |
| case 1: |
| mask = MAX31760_CR1_TIS | MAX31760_CR1_MTI; |
| regval = 0; |
| break; |
| default: |
| case 2: |
| mask = MAX31760_CR1_TIS | MAX31760_CR1_MTI; |
| regval = MAX31760_CR1_TIS; |
| break; |
| } |
| |
| mutex_lock(&max31760->lock); |
| err = regmap_update_bits(max31760->regmap, MAX31760_REG_CR1, mask, |
| regval); |
| mutex_unlock(&max31760->lock); |
| if (err < 0) |
| return err; |
| |
| return count; |
| } |
| |
| /* Show the PWM value at a lookup table index. */ |
| static ssize_t |
| max31760_pwm_auto_point_pwm_show(struct device *dev, |
| struct device_attribute *devattr, char *buf) |
| { |
| struct max31760 *max31760 = dev_get_drvdata(dev); |
| struct sensor_device_attribute *sensor_dev_attr = |
| to_sensor_dev_attr(devattr); |
| unsigned int reg = MAX31760_REG_LUT + sensor_dev_attr->index; |
| unsigned int regval; |
| int err; |
| |
| mutex_lock(&max31760->lock); |
| err = regmap_read(max31760->regmap, reg, ®val); |
| mutex_unlock(&max31760->lock); |
| if (err < 0) |
| return err; |
| |
| return snprintf(buf, PAGE_SIZE, "%u\n", regval); |
| } |
| |
| /* Store the PWM value at a lookup table index. */ |
| static ssize_t |
| max31760_pwm_auto_point_pwm_store(struct device *dev, |
| struct device_attribute *devattr, |
| const char *buf, size_t count) |
| { |
| struct max31760 *max31760 = dev_get_drvdata(dev); |
| struct sensor_device_attribute *sensor_dev_attr = |
| to_sensor_dev_attr(devattr); |
| unsigned int reg = MAX31760_REG_LUT + sensor_dev_attr->index; |
| unsigned int regval; |
| unsigned long pwm; |
| int err; |
| |
| err = kstrtoul(buf, 10, &pwm); |
| if (err < 0) |
| return err; |
| regval = pwm & 0xff; |
| |
| mutex_lock(&max31760->lock); |
| err = regmap_write(max31760->regmap, reg, regval); |
| mutex_unlock(&max31760->lock); |
| if (err < 0) |
| return err; |
| |
| return count; |
| } |
| |
| /* Returns the temperature for the given PWM lookup table index. */ |
| static int max31760_pwm_auto_point_temp(int index) |
| { |
| if (index == 0) |
| return MAX31760_TEMP_MIN_MC; |
| else |
| return (16 + index * 2) * 1000; |
| } |
| |
| /* Show the temperature for a PWM lookup table index. */ |
| static ssize_t |
| max31760_pwm_auto_point_temp_show(struct device *dev, |
| struct device_attribute *devattr, char *buf) |
| { |
| struct sensor_device_attribute *sensor_dev_attr = |
| to_sensor_dev_attr(devattr); |
| int temp = max31760_pwm_auto_point_temp(sensor_dev_attr->index); |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", temp); |
| } |
| |
| /* Show the temperature hysteresis for a PWM lookup table index. */ |
| static ssize_t max31760_pwm_auto_point_temp_hyst_show( |
| struct device *dev, struct device_attribute *devattr, char *buf) |
| { |
| struct max31760 *max31760 = dev_get_drvdata(dev); |
| struct sensor_device_attribute *sensor_dev_attr = |
| to_sensor_dev_attr(devattr); |
| int temp = max31760_pwm_auto_point_temp(sensor_dev_attr->index); |
| unsigned int regval; |
| int err; |
| |
| mutex_lock(&max31760->lock); |
| err = regmap_read(max31760->regmap, MAX31760_REG_CR1, ®val); |
| mutex_unlock(&max31760->lock); |
| if (err < 0) |
| return err; |
| |
| if (regval & MAX31760_CR1_HYST) |
| temp -= MAX31760_LUT_HYST_SET; |
| else |
| temp -= MAX31760_LUT_HYST_CLEAR; |
| |
| return snprintf(buf, PAGE_SIZE, "%d\n", temp); |
| } |
| |
| /* Store the temperature hysteresis for a PWM lookup table index. */ |
| static ssize_t |
| max31760_pwm_auto_point_temp_hyst_store(struct device *dev, |
| struct device_attribute *devattr, |
| const char *buf, size_t count) |
| { |
| struct max31760 *max31760 = dev_get_drvdata(dev); |
| struct sensor_device_attribute *sensor_dev_attr = |
| to_sensor_dev_attr(devattr); |
| int temp = max31760_pwm_auto_point_temp(sensor_dev_attr->index); |
| unsigned int regval; |
| long hyst; |
| int err; |
| |
| err = kstrtol(buf, 10, &hyst); |
| if (err < 0) |
| return err; |
| |
| temp -= hyst; |
| if (temp >= MAX31760_LYT_HYST_THRESH) |
| regval = MAX31760_CR1_HYST; |
| else |
| regval = 0; |
| mutex_lock(&max31760->lock); |
| err = regmap_update_bits(max31760->regmap, MAX31760_REG_CR1, |
| MAX31760_CR1_HYST, regval); |
| mutex_unlock(&max31760->lock); |
| if (err < 0) |
| return err; |
| return count; |
| } |
| |
| static SENSOR_DEVICE_ATTR(pwm1_auto_channels_temp, 0644, |
| max31760_pwm_auto_channels_temp_show, |
| max31760_pwm_auto_channels_temp_store, 0); |
| static SENSOR_DEVICE_ATTR(pwm2_auto_channels_temp, 0644, |
| max31760_pwm_auto_channels_temp_show, |
| max31760_pwm_auto_channels_temp_store, 0); |
| static struct attribute *max31760_attrs[] = { |
| &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr, |
| &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr, NULL |
| }; |
| static const struct attribute_group max31760_group = { |
| .attrs = max31760_attrs, |
| }; |
| |
| /* Writes to the 'config_load' attribute. */ |
| static ssize_t max31760_config_load_store(struct device *dev, |
| struct device_attribute *devattr, |
| const char *buf, size_t count) |
| { |
| struct max31760 *max31760 = dev_get_drvdata(dev); |
| int err; |
| |
| mutex_lock(&max31760->lock); |
| err = regmap_write(max31760->regmap, MAX31760_REG_EEX, |
| MAX31760_EEX_BLKS | MAX31760_EEX_LW); |
| if (!err) |
| err = max31760_regcache_drop(max31760); |
| mutex_unlock(&max31760->lock); |
| |
| if (err < 0) |
| return err; |
| return count; |
| } |
| |
| /* Writes to the 'config_store' attribute. */ |
| static ssize_t max31760_config_store_store(struct device *dev, |
| struct device_attribute *devattr, |
| const char *buf, size_t count) |
| { |
| struct max31760 *max31760 = dev_get_drvdata(dev); |
| int err = -EBUSY; |
| |
| mutex_lock(&max31760->lock); |
| if (max31760->state != MAX31760_STATE_RUNNING) |
| goto error; |
| /* |
| * Store the EEPROM data with the fans disabled so that the device |
| * starts with the fans disabled. When the driver goes through the |
| * spin-up state, it will restore the user settings for the fans. |
| */ |
| err = max31760_disable_fans_fast(max31760); |
| if (err) |
| goto error; |
| err = regmap_write(max31760->regmap, MAX31760_REG_EEX, |
| MAX31760_EEX_BLKS); |
| if (err) |
| goto error; |
| /* |
| * Sleep 110ms for every 16 byte block of data written, then restart |
| * the fans. |
| */ |
| msleep(MAX31760_EEX_WRITE_TIME_MS); |
| err = max31760_set_state_locked(max31760, MAX31760_STATE_SPINUP, |
| MAX31760_DELAY_MODE_ALLOWED); |
| if (err) |
| goto error; |
| err = count; |
| |
| error: |
| mutex_unlock(&max31760->lock); |
| return err; |
| } |
| |
| /* Reads from a delay_* attribute. */ |
| static ssize_t max31760_delay_show(struct device *dev, |
| struct device_attribute *devattr, char *buf) |
| { |
| struct max31760 *max31760 = dev_get_drvdata(dev); |
| struct sensor_device_attribute *sensor_dev_attr = |
| to_sensor_dev_attr(devattr); |
| unsigned int regval; |
| int err; |
| |
| mutex_lock(&max31760->lock); |
| err = regmap_read(max31760->regmap, |
| max31760_delays[sensor_dev_attr->index].reg, ®val); |
| mutex_unlock(&max31760->lock); |
| if (err < 0) |
| return err; |
| |
| /* Print the value in milliseconds. */ |
| return snprintf(buf, PAGE_SIZE, "%u\n", regval * MAX31760_DELAY_FACTOR); |
| } |
| |
| /* Writes to a delay_* attribute. */ |
| static ssize_t max31760_delay_store(struct device *dev, |
| struct device_attribute *devattr, |
| const char *buf, size_t count) |
| { |
| struct max31760 *max31760 = dev_get_drvdata(dev); |
| struct sensor_device_attribute *sensor_dev_attr = |
| to_sensor_dev_attr(devattr); |
| unsigned int regval; |
| unsigned long val; |
| int err; |
| |
| err = kstrtoul(buf, 10, &val); |
| if (err) |
| return err; |
| /* Convert from milliseconds to deciseconds, and truncate. */ |
| regval = (val / MAX31760_DELAY_FACTOR) & 0xff; |
| |
| mutex_lock(&max31760->lock); |
| err = regmap_write(max31760->regmap, |
| max31760_delays[sensor_dev_attr->index].reg, regval); |
| if (!err) |
| max31760->delay_jiffies[sensor_dev_attr->index] = |
| msecs_to_jiffies(regval * MAX31760_DELAY_FACTOR); |
| mutex_unlock(&max31760->lock); |
| |
| if (err < 0) |
| return err; |
| return count; |
| } |
| |
| /* Reads from the 'pwmX_fallback' attribute. */ |
| static ssize_t max31760_pwm_fallback_show(struct device *dev, |
| struct device_attribute *devattr, |
| char *buf) |
| { |
| struct max31760 *max31760 = dev_get_drvdata(dev); |
| unsigned int regval; |
| int err; |
| |
| mutex_lock(&max31760->lock); |
| err = regmap_read(max31760->regmap, MAX31760_REG_FFDC, ®val); |
| mutex_unlock(&max31760->lock); |
| if (err < 0) |
| return err; |
| |
| return snprintf(buf, PAGE_SIZE, "%u\n", regval); |
| } |
| |
| /* Writes to the 'pwmX_fallback' attribute. */ |
| static ssize_t max31760_pwm_fallback_store(struct device *dev, |
| struct device_attribute *devattr, |
| const char *buf, size_t count) |
| { |
| struct max31760 *max31760 = dev_get_drvdata(dev); |
| unsigned int regval; |
| unsigned long val; |
| int err; |
| |
| err = kstrtoul(buf, 10, &val); |
| if (err) |
| return err; |
| regval = val & 0xff; |
| |
| mutex_lock(&max31760->lock); |
| err = regmap_write(max31760->regmap, MAX31760_REG_FFDC, regval); |
| mutex_unlock(&max31760->lock); |
| if (err < 0) |
| return err; |
| return count; |
| } |
| |
| /* Reads from the 'pwmX_ramp_rate' attribute. */ |
| static ssize_t max31760_pwm_ramp_rate_show(struct device *dev, |
| struct device_attribute *devattr, |
| char *buf) |
| { |
| struct max31760 *max31760 = dev_get_drvdata(dev); |
| unsigned int regval; |
| int err; |
| |
| mutex_lock(&max31760->lock); |
| err = regmap_read(max31760->regmap, MAX31760_REG_USER0, ®val); |
| mutex_unlock(&max31760->lock); |
| if (err < 0) |
| return err; |
| switch (regval & MAX31760_USER0_RAMP) { |
| case MAX31760_USER0_RAMP_SLOW: |
| regval = 8; |
| break; |
| case MAX31760_USER0_RAMP_SMED: |
| regval = 16; |
| break; |
| case MAX31760_USER0_RAMP_MEDF: |
| regval = 32; |
| break; |
| case MAX31760_USER0_RAMP_FAST: |
| regval = 255; |
| break; |
| } |
| |
| return snprintf(buf, PAGE_SIZE, "%u\n", regval); |
| } |
| |
| /* Writes to the 'pwmX_ramp_rate' attribute. */ |
| static ssize_t max31760_pwm_ramp_rate_store(struct device *dev, |
| struct device_attribute *devattr, |
| const char *buf, size_t count) |
| { |
| struct max31760 *max31760 = dev_get_drvdata(dev); |
| unsigned int regval; |
| unsigned long val; |
| int err; |
| |
| err = kstrtoul(buf, 10, &val); |
| if (err) |
| return err; |
| if (val <= 12) |
| regval = MAX31760_USER0_RAMP_SLOW; |
| else if (val <= 24) |
| regval = MAX31760_USER0_RAMP_SMED; |
| else if (val <= 143) |
| regval = MAX31760_USER0_RAMP_MEDF; |
| else |
| regval = MAX31760_USER0_RAMP_FAST; |
| |
| mutex_lock(&max31760->lock); |
| err = regmap_update_bits(max31760->regmap, MAX31760_REG_USER0, |
| MAX31760_USER0_RAMP, regval); |
| if (!err) |
| err = max31760_ramp_rate_changed(max31760); |
| mutex_unlock(&max31760->lock); |
| |
| if (err < 0) |
| return err; |
| return count; |
| } |
| |
| static SENSOR_DEVICE_ATTR(config_load, 0200, NULL, max31760_config_load_store, |
| 0); |
| static SENSOR_DEVICE_ATTR(config_store, 0200, NULL, max31760_config_store_store, |
| 0); |
| static SENSOR_DEVICE_ATTR(delay_spinup, 0644, max31760_delay_show, |
| max31760_delay_store, 0); |
| static SENSOR_DEVICE_ATTR(delay_register_restore, 0644, max31760_delay_show, |
| max31760_delay_store, 1); |
| static SENSOR_DEVICE_ATTR(pwm1_fallback, 0644, max31760_pwm_fallback_show, |
| max31760_pwm_fallback_store, 0); |
| static SENSOR_DEVICE_ATTR(pwm2_fallback, 0644, max31760_pwm_fallback_show, |
| max31760_pwm_fallback_store, 0); |
| static SENSOR_DEVICE_ATTR(pwm1_ramp_rate, 0644, max31760_pwm_ramp_rate_show, |
| max31760_pwm_ramp_rate_store, 0); |
| static SENSOR_DEVICE_ATTR(pwm2_ramp_rate, 0644, max31760_pwm_ramp_rate_show, |
| max31760_pwm_ramp_rate_store, 0); |
| |
| static struct attribute *max31760_custom_attrs[] = { |
| &sensor_dev_attr_config_load.dev_attr.attr, |
| &sensor_dev_attr_config_store.dev_attr.attr, |
| &sensor_dev_attr_delay_spinup.dev_attr.attr, |
| &sensor_dev_attr_delay_register_restore.dev_attr.attr, |
| &sensor_dev_attr_pwm1_fallback.dev_attr.attr, |
| &sensor_dev_attr_pwm2_fallback.dev_attr.attr, |
| &sensor_dev_attr_pwm1_ramp_rate.dev_attr.attr, |
| &sensor_dev_attr_pwm2_ramp_rate.dev_attr.attr, |
| NULL |
| }; |
| static const struct attribute_group max31760_custom_group = { |
| .name = "custom", |
| .attrs = max31760_custom_attrs, |
| }; |
| |
| /* Generate auto_point sensor attributes. */ |
| static void max31760_setup_attr_groups(struct max31760 *max31760) |
| { |
| struct max31760_dev_attr *lut_dev_attr = max31760->lut_dev_attrs; |
| struct device_attribute *dev_attr; |
| int attr_index = 0; |
| int pwm = 0; |
| int i; |
| |
| while (pwm < MAX31760_PWM_COUNT) { |
| ++pwm; |
| for (i = 0; i < MAX31760_LUT_COUNT; i++, lut_dev_attr++) { |
| snprintf(lut_dev_attr->name, MAX31760_LUT_NAME_SIZE, |
| "pwm%d_auto_point%02d_pwm", pwm, i); |
| lut_dev_attr->sdattr.index = i; |
| dev_attr = &lut_dev_attr->sdattr.dev_attr; |
| dev_attr->attr.name = lut_dev_attr->name; |
| dev_attr->attr.mode = 0644; |
| dev_attr->show = max31760_pwm_auto_point_pwm_show; |
| dev_attr->store = max31760_pwm_auto_point_pwm_store; |
| max31760->lut_attrs[attr_index++] = |
| &lut_dev_attr->sdattr.dev_attr.attr; |
| } |
| |
| for (i = 0; i < MAX31760_LUT_COUNT; i++, lut_dev_attr++) { |
| snprintf(lut_dev_attr->name, MAX31760_LUT_NAME_SIZE, |
| "pwm%d_auto_point%02d_temp", pwm, i); |
| lut_dev_attr->sdattr.index = i; |
| dev_attr = &lut_dev_attr->sdattr.dev_attr; |
| dev_attr->attr.name = lut_dev_attr->name; |
| dev_attr->attr.mode = 0444; |
| dev_attr->show = max31760_pwm_auto_point_temp_show; |
| max31760->lut_attrs[attr_index++] = |
| &lut_dev_attr->sdattr.dev_attr.attr; |
| } |
| |
| for (i = 0; i < MAX31760_LUT_COUNT; i++, lut_dev_attr++) { |
| snprintf(lut_dev_attr->name, MAX31760_LUT_NAME_SIZE, |
| "pwm%d_auto_point%02d_temp_hyst", pwm, i); |
| lut_dev_attr->sdattr.index = i; |
| dev_attr = &lut_dev_attr->sdattr.dev_attr; |
| dev_attr->attr.name = lut_dev_attr->name; |
| dev_attr->attr.mode = 0644; |
| dev_attr->show = max31760_pwm_auto_point_temp_hyst_show; |
| dev_attr->store = |
| max31760_pwm_auto_point_temp_hyst_store; |
| max31760->lut_attrs[attr_index++] = |
| &lut_dev_attr->sdattr.dev_attr.attr; |
| } |
| } |
| |
| max31760->lut_group.attrs = max31760->lut_attrs; |
| max31760->attr_groups[0] = &max31760->lut_group; |
| max31760->attr_groups[1] = &max31760_group; |
| max31760->attr_groups[2] = &max31760_custom_group; |
| } |
| |
| /* Enable a fan GPIO and regulator. */ |
| static int max31760_fan_enable(struct max31760 *max31760, u32 index, |
| bool enable) |
| { |
| struct max31760_fan *fan; |
| bool is_enabled; |
| int err = 0; |
| |
| if (index >= MAX31760_NUM_FANS) |
| return -EINVAL; |
| |
| fan = &max31760->fan[index]; |
| is_enabled = enable && fan->enabled; |
| |
| if (fan->enable_gpio) |
| gpiod_set_value_cansleep(fan->enable_gpio, is_enabled); |
| |
| if (fan->supply) { |
| if (is_enabled) |
| err = regulator_enable(fan->supply); |
| else |
| err = regulator_disable(fan->supply); |
| if (err) |
| dev_err(max31760->dev, "Failed to %s fan %u vdd-supply", |
| is_enabled ? "enabled" : "disable", index); |
| } |
| |
| return err; |
| } |
| |
| /* Read properties of a fan node. */ |
| static int max31760_of_init_fan(struct max31760 *max31760, |
| struct device_node *fan_node, u32 reg) |
| { |
| struct device *dev = max31760->dev; |
| char supply_name[MAX31760_FAN_SUPPLY_NAME_SIZE]; |
| struct max31760_fan *fan; |
| int err; |
| |
| if (reg >= MAX31760_NUM_FANS) { |
| dev_err(dev, "invalid reg on fan: %s", fan_node->name); |
| return -EINVAL; |
| } |
| |
| fan = &max31760->fan[reg]; |
| err = of_property_read_string(fan_node, "label", &fan->label); |
| if (err) |
| fan->label = NULL; |
| |
| fan->enabled = of_device_is_available(fan_node); |
| |
| fan->enable_gpio = devm_gpiod_get_from_of_node( |
| dev, fan_node, "maxim,enable", 0, GPIOD_ASIS, fan->label); |
| err = PTR_ERR(fan->enable_gpio); |
| if (err == -ENOENT || err == -ENODEV) { |
| fan->enable_gpio = NULL; |
| } else if (err == -EPROBE_DEFER) { |
| dev_dbg(dev, "Defer due to fan enable gpio."); |
| return err; |
| } else if (IS_ERR(fan->enable_gpio)) { |
| dev_err(dev, "Error getting fan enable gpio: %d", err); |
| return err; |
| } |
| |
| snprintf(supply_name, MAX31760_FAN_SUPPLY_NAME_SIZE, "maxim,fan%u", |
| reg); |
| fan->supply = devm_regulator_get_optional(dev, supply_name); |
| err = PTR_ERR(fan->supply); |
| if (err == -ENODEV) { |
| fan->supply = NULL; |
| } else if (err == -EPROBE_DEFER) { |
| dev_dbg(dev, "Defer due to %s-supply.", supply_name); |
| return err; |
| } else if (err == -ENOENT) { |
| dev_err(dev, "Could not find regulator for %s-supply.", |
| supply_name); |
| return err; |
| } else if (IS_ERR(fan->supply)) { |
| dev_err(dev, "Get '%s-supply' error: %d", supply_name, err); |
| return err; |
| } |
| |
| return max31760_fan_enable(max31760, reg, true); |
| } |
| |
| /* Read properties of a temp node. */ |
| static int max31760_of_init_temp(struct max31760 *max31760, |
| struct device_node *temp_node, u32 reg) |
| { |
| const char *label; |
| int err; |
| u32 val; |
| |
| if (reg >= MAX31760_NUM_TEMPS) { |
| dev_err(max31760->dev, "invalid reg on temp: %s", |
| temp_node->name); |
| return -EINVAL; |
| } |
| |
| err = of_property_read_string(temp_node, "label", &label); |
| if (!err) |
| max31760->temp_label[reg] = label; |
| |
| err = of_property_read_u32(temp_node, "maxim,ideality", &val); |
| if (!err && reg == 1) { |
| /* Only external temp sensor has ideality. */ |
| err = regmap_write(max31760->regmap, MAX31760_REG_IFR, |
| val & 0x3f); |
| } |
| |
| return 0; |
| } |
| |
| /* Configure registers which have associated device properties. */ |
| static int max31760_of_init(struct max31760 *max31760) |
| { |
| struct device *dev = max31760->dev; |
| struct device_node *node; |
| int err; |
| int i; |
| u32 reg; |
| u32 val; |
| u32 freq = 0; |
| u32 pwm_enable_mode = 0; |
| |
| if (of_property_read_bool(dev->of_node, "maxim,stay-awake")) |
| max31760->stay_awake = true; |
| |
| for_each_available_child_of_node (dev->of_node, node) { |
| err = of_property_read_u32(node, "reg", ®); |
| if (err) { |
| dev_err(dev, "invalid reg on sub-node: %s", node->name); |
| return err; |
| } |
| |
| if (!strcmp(node->name, "fan")) |
| err = max31760_of_init_fan(max31760, node, reg); |
| else if (!strcmp(node->name, "temp")) |
| err = max31760_of_init_temp(max31760, node, reg); |
| else |
| dev_err(dev, "invalid subnode with name: %s", |
| node->name); |
| if (err) { |
| dev_err(dev, "child '%s' err: %d", node->name, err); |
| return err; |
| } |
| } |
| |
| val = 0; |
| if (of_property_read_u32(dev->of_node, "maxim,pwm-frequency", &freq) == 0) { |
| switch (freq) { |
| case 33: |
| val |= MAX31760_DRV_33HZ; |
| break; |
| case 150: |
| val |= MAX31760_DRV_150HZ; |
| break; |
| case 1500: |
| val |= MAX31760_DRV_1500HZ; |
| break; |
| case 25000: |
| val |= MAX31760_DRV_25KHZ; |
| break; |
| default: |
| dev_err(dev, "invalid frequency setting of %d", freq); |
| return -EINVAL; |
| |
| } |
| err = regmap_update_bits(max31760->regmap, MAX31760_REG_CR1, |
| MAX31760_CR1_DRV, val); |
| if (err) |
| return err; |
| } |
| |
| if (of_property_read_u32(dev->of_node, "maxim,pwm-enable-mode", &pwm_enable_mode) == 0) { |
| |
| err = max31760_write_pwm(max31760, hwmon_pwm_enable, 0, pwm_enable_mode); |
| |
| if (err) |
| return err; |
| } |
| |
| val = 0; |
| if (of_property_read_bool(dev->of_node, "maxim,pwm-polarity-negative")) |
| val |= MAX31760_CR1_PPS; |
| err = regmap_update_bits(max31760->regmap, MAX31760_REG_CR1, |
| MAX31760_CR1_PPS, val); |
| if (err) |
| return err; |
| |
| /* Put ALERT pin into comparator mode: interrupts aren't supported. */ |
| val = MAX31760_CR2_ALERTS | MAX31760_CR2_FFMODE; |
| if (of_property_read_bool(dev->of_node, "maxim,fan-spin-up-enabled")) |
| val |= MAX31760_CR2_SPEN; |
| if (of_property_read_bool(dev->of_node, "maxim,fan-rd-signal")) |
| val |= MAX31760_CR2_FSST; |
| if (of_property_read_bool(dev->of_node, "maxim,fan-rd-polarity-high")) |
| val |= MAX31760_CR2_RDPS; |
| if (of_property_read_bool(dev->of_node, "maxim,fan-signal-enabled")) |
| val |= MAX31760_CR2_FSEN; |
| /* |
| * Future properties: |
| * maxim,fan-fail-interrupt -> remove MAX31760_CR2_FFMODE |
| * maxim,temp-alert-interrupt -> remove MAX31760_CR2_ALERTS |
| */ |
| |
| err = regmap_update_bits(max31760->regmap, MAX31760_REG_CR2, |
| MAX31760_CR2_SPEN | MAX31760_CR2_FSST | |
| MAX31760_CR2_RDPS | MAX31760_CR2_FSEN | |
| MAX31760_CR2_ALERTS | |
| MAX31760_CR2_FFMODE, |
| val); |
| if (err) |
| return err; |
| |
| val = (max31760->fan[0].enabled ? MAX31760_CR3_TACH1E : 0) | |
| (max31760->fan[1].enabled ? MAX31760_CR3_TACH2E : 0); |
| if (of_property_read_bool(dev->of_node, "maxim,fan-fail-full-only")) |
| val |= MAX31760_CR3_TACHFL; |
| if (of_property_read_bool(dev->of_node, |
| "maxim,pwm-pulse-stretch-enabled")) |
| val |= MAX31760_CR3_PSEN; |
| if (of_property_read_bool(dev->of_node, "maxim,pwm-zero-fan-can-fail")) |
| val |= MAX31760_CR3_FF_0; |
| |
| err = regmap_update_bits(max31760->regmap, MAX31760_REG_CR3, |
| MAX31760_CR3_TACH1E | MAX31760_CR3_TACH2E | |
| MAX31760_CR3_TACHFL | |
| MAX31760_CR3_PSEN | MAX31760_CR3_FF_0, |
| val); |
| if (err) |
| return err; |
| |
| for (i = 0; i < ARRAY_SIZE(max31760_delays); i++) { |
| if (of_property_read_u32(dev->of_node, |
| max31760_delays[i].property, |
| &val) == 0) { |
| if (val > MAX31760_DELAY_MAX_MSEC) |
| val = MAX31760_DELAY_MAX_MSEC; |
| err = regmap_write(max31760->regmap, |
| max31760_delays[i].reg, |
| (val / MAX31760_DELAY_FACTOR) |
| & 0xff); |
| if (err) |
| return err; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* Toggle the fan GPIOs and regulators to match enable state. */ |
| static int max31760_enable_power(struct max31760 *max31760, bool enable) |
| { |
| int last_err; |
| int err = 0; |
| u32 i; |
| |
| for (i = 0; i < MAX31760_NUM_FANS; i++) { |
| last_err = max31760_fan_enable(max31760, i, enable); |
| if (last_err) |
| err = last_err; |
| } |
| |
| if (max31760->vdd_supply) { |
| if (enable) |
| last_err = regulator_enable(max31760->vdd_supply); |
| else if (regulator_is_enabled(max31760->vdd_supply)) |
| last_err = regulator_disable(max31760->vdd_supply); |
| else |
| last_err = 0; |
| if (last_err) { |
| err = last_err; |
| dev_err(max31760->dev, "Failed to %s vdd-supply", |
| enable ? "enable" : "disable"); |
| } |
| } |
| |
| return err; |
| } |
| |
| static int max31760_probe(struct i2c_client *client, |
| const struct i2c_device_id *id) |
| { |
| struct device *dev = &client->dev; |
| struct device *hwmon_dev; |
| struct max31760 *max31760; |
| int err; |
| |
| max31760 = devm_kzalloc(dev, sizeof(*max31760), GFP_KERNEL); |
| if (!max31760) |
| return -ENOMEM; |
| |
| max31760->dev = dev; |
| dev_set_drvdata(dev, max31760); |
| mutex_init(&max31760->lock); |
| INIT_DELAYED_WORK(&max31760->state_work, max31760_state_work); |
| |
| max31760->vdd_supply = devm_regulator_get_optional(dev, "vdd"); |
| err = PTR_ERR(max31760->vdd_supply); |
| if (err == -ENODEV) { |
| max31760->vdd_supply = NULL; |
| } else if (err == -EPROBE_DEFER) { |
| dev_dbg(dev, "Defer due to vdd-supply."); |
| return err; |
| } else if (err == -ENOENT) { |
| dev_err(dev, "Could not find regulator for vdd-supply."); |
| return err; |
| } else if (IS_ERR(max31760->vdd_supply)) { |
| dev_err(dev, "Unhandled error %d getting vdd-supply.", err); |
| return err; |
| } |
| |
| err = max31760_enable_power(max31760, true); |
| if (err) |
| return err; |
| |
| max31760->regmap = |
| devm_regmap_init_i2c(client, &max31760_regmap_config); |
| if (IS_ERR(max31760->regmap)) { |
| err = PTR_ERR(max31760->regmap); |
| dev_err(dev, "regmap init failed: %d", err); |
| goto err_disable_regulator; |
| } |
| |
| err = max31760_of_init(max31760); |
| if (err) { |
| dev_err(dev, "failed to initialize from firmware: %d", err); |
| goto err_disable_regulator; |
| } |
| |
| max31760_setup_attr_groups(max31760); |
| hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, |
| max31760, |
| &max31760_chip_info, |
| max31760->attr_groups); |
| err = PTR_ERR_OR_ZERO(hwmon_dev); |
| if (err) |
| goto err_disable_regulator; |
| |
| err = max31760_state_wake_up(max31760, MAX31760_DELAY_MODE_ALLOWED); |
| if (err) |
| goto err_disable_regulator; |
| |
| return 0; |
| |
| err_disable_regulator: |
| max31760_enable_power(max31760, false); |
| return err; |
| } |
| |
| static int max31760_remove(struct i2c_client *client) |
| { |
| struct max31760 *max31760 = i2c_get_clientdata(client); |
| |
| if (!max31760) |
| return 0; |
| |
| max31760_state_go_to_sleep(max31760); |
| |
| return max31760_enable_power(max31760, false); |
| } |
| |
| static int __maybe_unused max31760_suspend(struct device *dev) |
| { |
| struct max31760 *max31760 = dev_get_drvdata(dev); |
| int err; |
| |
| if (max31760->stay_awake) { |
| /* |
| * Stop any delayed work and move immediately to the normal |
| * running state. |
| */ |
| return max31760_state_wake_up(max31760, |
| MAX31760_DELAY_MODE_SKIP); |
| } |
| |
| err = max31760_state_go_to_sleep(max31760); |
| if (err) |
| return err; |
| |
| return max31760_enable_power(max31760, false); |
| } |
| |
| static int __maybe_unused max31760_resume(struct device *dev) |
| { |
| struct max31760 *max31760 = dev_get_drvdata(dev); |
| int err; |
| |
| if (max31760->stay_awake) |
| return 0; |
| |
| err = max31760_enable_power(max31760, true); |
| if (err) |
| return err; |
| |
| return max31760_state_wake_up(max31760, MAX31760_DELAY_MODE_ALLOWED); |
| } |
| |
| static SIMPLE_DEV_PM_OPS(max31760_dev_pm_ops, max31760_suspend, |
| max31760_resume); |
| |
| static const struct i2c_device_id max31760_i2c_ids[] = { |
| { |
| .name = DRIVER_NAME, |
| }, |
| {} |
| }; |
| MODULE_DEVICE_TABLE(i2c, max31760_i2c_ids); |
| |
| #ifdef CONFIG_OF |
| static const struct of_device_id max31760_of_ids[] = { |
| { |
| .compatible = "maxim,max31760", |
| }, |
| {} |
| }; |
| MODULE_DEVICE_TABLE(of, max31760_of_ids); |
| #endif |
| |
| static struct i2c_driver max31760_driver = { |
| .driver = { |
| .name = DRIVER_NAME, |
| .pm = &max31760_dev_pm_ops, |
| .of_match_table = of_match_ptr(max31760_of_ids), |
| }, |
| .probe = max31760_probe, |
| .remove = max31760_remove, |
| .id_table = max31760_i2c_ids, |
| }; |
| |
| module_i2c_driver(max31760_driver); |
| |
| MODULE_AUTHOR("John Muir <muirj@google.com>"); |
| MODULE_DESCRIPTION( |
| "Maxim Integrated MAX31760 Precision Fan-Speed Controller Driver"); |
| MODULE_LICENSE("GPL"); |