// SPDX-License-Identifier: GPL-2.0-or-later
/*
* amc6821.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
* Copyright (C) 2009 T. Mertelj <tomaz.mertelj@guest.arnes.si>
*
* Based on max6650.c:
* Copyright (C) 2007 Hans J. Koch <hjk@hansjkoch.de>
*
* Conversion to regmap and with_info API:
* Copyright (C) 2024 Guenter Roeck <linux@roeck-us.net>
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/bits.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_platform.h>
#include <linux/pwm.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/thermal.h>
#include <dt-bindings/pwm/pwm.h>
/*
* Addresses to scan.
*/
static const unsigned short normal_i2c[] = {0x18, 0x19, 0x1a, 0x2c, 0x2d, 0x2e,
0x4c, 0x4d, 0x4e, I2C_CLIENT_END};
/*
* Insmod parameters
*/
static int pwminv = -1; /*Inverted PWM output. */
module_param(pwminv, int, 0444);
static int init = 1; /*Power-on initialization.*/
module_param(init, int, 0444);
#define AMC6821_REG_DEV_ID 0x3D
#define AMC6821_REG_COMP_ID 0x3E
#define AMC6821_REG_CONF1 0x00
#define AMC6821_REG_CONF2 0x01
#define AMC6821_REG_CONF3 0x3F
#define AMC6821_REG_CONF4 0x04
#define AMC6821_REG_STAT1 0x02
#define AMC6821_REG_STAT2 0x03
#define AMC6821_REG_TEMP_LO 0x06
#define AMC6821_REG_TDATA_LOW 0x08
#define AMC6821_REG_TDATA_HI 0x09
#define AMC6821_REG_LTEMP_HI 0x0A
#define AMC6821_REG_RTEMP_HI 0x0B
#define AMC6821_REG_LTEMP_LIMIT_MIN 0x15
#define AMC6821_REG_LTEMP_LIMIT_MAX 0x14
#define AMC6821_REG_RTEMP_LIMIT_MIN 0x19
#define AMC6821_REG_RTEMP_LIMIT_MAX 0x18
#define AMC6821_REG_LTEMP_CRIT 0x1B
#define AMC6821_REG_RTEMP_CRIT 0x1D
#define AMC6821_REG_PSV_TEMP 0x1C
#define AMC6821_REG_DCY 0x22
#define AMC6821_REG_LTEMP_FAN_CTRL 0x24
#define AMC6821_REG_RTEMP_FAN_CTRL 0x25
#define AMC6821_REG_DCY_LOW_TEMP 0x21
#define AMC6821_REG_TACH_LLIMITL 0x10
#define AMC6821_REG_TACH_HLIMITL 0x12
#define AMC6821_REG_TACH_SETTINGL 0x1e
#define AMC6821_CONF1_START BIT(0)
#define AMC6821_CONF1_FAN_INT_EN BIT(1)
#define AMC6821_CONF1_FANIE BIT(2)
#define AMC6821_CONF1_PWMINV BIT(3)
#define AMC6821_CONF1_FAN_FAULT_EN BIT(4)
#define AMC6821_CONF1_FDRC0 BIT(5)
#define AMC6821_CONF1_FDRC1 BIT(6)
#define AMC6821_CONF1_THERMOVIE BIT(7)
#define AMC6821_CONF2_PWM_EN BIT(0)
#define AMC6821_CONF2_TACH_MODE BIT(1)
#define AMC6821_CONF2_TACH_EN BIT(2)
#define AMC6821_CONF2_RTFIE BIT(3)
#define AMC6821_CONF2_LTOIE BIT(4)
#define AMC6821_CONF2_RTOIE BIT(5)
#define AMC6821_CONF2_PSVIE BIT(6)
#define AMC6821_CONF2_RST BIT(7)
#define AMC6821_CONF3_THERM_FAN_EN BIT(7)
#define AMC6821_CONF3_REV_MASK GENMASK(3, 0)
#define AMC6821_CONF4_OVREN BIT(4)
#define AMC6821_CONF4_TACH_FAST BIT(5)
#define AMC6821_CONF4_PSPR BIT(6)
#define AMC6821_CONF4_MODE BIT(7)
#define AMC6821_STAT1_RPM_ALARM BIT(0)
#define AMC6821_STAT1_FANS BIT(1)
#define AMC6821_STAT1_RTH BIT(2)
#define AMC6821_STAT1_RTL BIT(3)
#define AMC6821_STAT1_R_THERM BIT(4)
#define AMC6821_STAT1_RTF BIT(5)
#define AMC6821_STAT1_LTH BIT(6)
#define AMC6821_STAT1_LTL BIT(7)
#define AMC6821_STAT2_RTC BIT(3)
#define AMC6821_STAT2_LTC BIT(4)
#define AMC6821_STAT2_LPSV BIT(5)
#define AMC6821_STAT2_L_THERM BIT(6)
#define AMC6821_STAT2_THERM_IN BIT(7)
#define AMC6821_TEMP_SLOPE_MASK GENMASK(2, 0)
#define AMC6821_TEMP_LIMIT_MASK GENMASK(7, 3)
/*
* Client data (each client gets its own)
*/
struct amc6821_data {
struct regmap *regmap;
struct mutex update_lock;
unsigned long fan_state;
unsigned long fan_max_state;
unsigned int *fan_cooling_levels;
enum pwm_polarity pwm_polarity;
};
/*
* Return 0 on success or negative error code.
*
* temps returns set of three temperatures, in °C:
* temps[0]: Passive cooling temperature, applies to both channels
* temps[1]: Low temperature, start slope calculations
* temps[2]: High temperature
*
* Channel 0: local, channel 1: remote.
*/
static int amc6821_get_auto_point_temps(struct regmap *regmap, int channel, u8 *temps)
{
u32 regs[] = {
AMC6821_REG_DCY_LOW_TEMP,
AMC6821_REG_PSV_TEMP,
channel ? AMC6821_REG_RTEMP_FAN_CTRL : AMC6821_REG_LTEMP_FAN_CTRL
};
u8 regvals[3];
int slope;
int err;
err = regmap_multi_reg_read(regmap, regs, regvals, 3);
if (err)
return err;
temps[0] = regvals[1];
temps[1] = FIELD_GET(AMC6821_TEMP_LIMIT_MASK, regvals[2]) * 4;
/* slope is 32 >> <slope bits> in °C */
slope = 32 >> FIELD_GET(AMC6821_TEMP_SLOPE_MASK, regvals[2]);
if (slope)
temps[2] = temps[1] + DIV_ROUND_CLOSEST(255 - regvals[0], slope);
else
temps[2] = 255;
return 0;
}
static int amc6821_temp_read_values(struct regmap *regmap, u32 attr, int channel, long *val)
{
int reg, err;
u32 regval;
switch (attr) {
case hwmon_temp_input:
reg = channel ? AMC6821_