Commit a051d507 authored by Guenter Roeck's avatar Guenter Roeck

hwmon: (amc6821) Convert to use regmap

Use regmap for register accesses and caching.

While at it, use sysfs_emit() instead of sprintf() to write sysfs
attribute data, and remove spurious debug messages which would only
be seen as result of a bug in the code. Also make sure that error
codes are propagated and not replaced with -EIO.

While at it, introduce rounding of written temperature values and for
internal calculations to reduce deviation from written values and as
much as possible.

No functional change intended except for differences introduced by
rounding.
Reviewed-by: default avatarQuentin Schulz <quentin.schulz@cherry.de>
Signed-off-by: default avatarGuenter Roeck <linux@roeck-us.net>
parent 8d061050
...@@ -2127,6 +2127,7 @@ config SENSORS_ADS7871 ...@@ -2127,6 +2127,7 @@ config SENSORS_ADS7871
config SENSORS_AMC6821 config SENSORS_AMC6821
tristate "Texas Instruments AMC6821" tristate "Texas Instruments AMC6821"
depends on I2C depends on I2C
select REGMAP_I2C
help help
If you say yes here you get support for the Texas Instruments If you say yes here you get support for the Texas Instruments
AMC6821 hardware monitoring chips. AMC6821 hardware monitoring chips.
......
...@@ -8,15 +8,18 @@ ...@@ -8,15 +8,18 @@
* Copyright (C) 2007 Hans J. Koch <hjk@hansjkoch.de> * Copyright (C) 2007 Hans J. Koch <hjk@hansjkoch.de>
*/ */
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/bits.h> #include <linux/bits.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/hwmon.h> #include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h> #include <linux/hwmon-sysfs.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/jiffies.h> #include <linux/minmax.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/slab.h> #include <linux/slab.h>
/* /*
...@@ -44,6 +47,7 @@ module_param(init, int, 0444); ...@@ -44,6 +47,7 @@ module_param(init, int, 0444);
#define AMC6821_REG_CONF4 0x04 #define AMC6821_REG_CONF4 0x04
#define AMC6821_REG_STAT1 0x02 #define AMC6821_REG_STAT1 0x02
#define AMC6821_REG_STAT2 0x03 #define AMC6821_REG_STAT2 0x03
#define AMC6821_REG_TEMP_LO 0x06
#define AMC6821_REG_TDATA_LOW 0x08 #define AMC6821_REG_TDATA_LOW 0x08
#define AMC6821_REG_TDATA_HI 0x09 #define AMC6821_REG_TDATA_HI 0x09
#define AMC6821_REG_LTEMP_HI 0x0A #define AMC6821_REG_LTEMP_HI 0x0A
...@@ -61,11 +65,8 @@ module_param(init, int, 0444); ...@@ -61,11 +65,8 @@ module_param(init, int, 0444);
#define AMC6821_REG_DCY_LOW_TEMP 0x21 #define AMC6821_REG_DCY_LOW_TEMP 0x21
#define AMC6821_REG_TACH_LLIMITL 0x10 #define AMC6821_REG_TACH_LLIMITL 0x10
#define AMC6821_REG_TACH_LLIMITH 0x11
#define AMC6821_REG_TACH_HLIMITL 0x12 #define AMC6821_REG_TACH_HLIMITL 0x12
#define AMC6821_REG_TACH_HLIMITH 0x13
#define AMC6821_REG_TACH_SETTINGL 0x1e #define AMC6821_REG_TACH_SETTINGL 0x1e
#define AMC6821_REG_TACH_SETTINGH 0x1f
#define AMC6821_CONF1_START BIT(0) #define AMC6821_CONF1_START BIT(0)
#define AMC6821_CONF1_FAN_INT_EN BIT(1) #define AMC6821_CONF1_FAN_INT_EN BIT(1)
...@@ -108,6 +109,9 @@ module_param(init, int, 0444); ...@@ -108,6 +109,9 @@ module_param(init, int, 0444);
#define AMC6821_STAT2_L_THERM BIT(6) #define AMC6821_STAT2_L_THERM BIT(6)
#define AMC6821_STAT2_THERM_IN BIT(7) #define AMC6821_STAT2_THERM_IN BIT(7)
#define AMC6821_TEMP_SLOPE_MASK GENMASK(2, 0)
#define AMC6821_TEMP_LIMIT_MASK GENMASK(7, 3)
enum {IDX_TEMP1_INPUT = 0, IDX_TEMP1_MIN, IDX_TEMP1_MAX, enum {IDX_TEMP1_INPUT = 0, IDX_TEMP1_MIN, IDX_TEMP1_MAX,
IDX_TEMP1_CRIT, IDX_TEMP2_INPUT, IDX_TEMP2_MIN, IDX_TEMP1_CRIT, IDX_TEMP2_INPUT, IDX_TEMP2_MIN,
IDX_TEMP2_MAX, IDX_TEMP2_CRIT, IDX_TEMP2_MAX, IDX_TEMP2_CRIT,
...@@ -130,224 +134,159 @@ static const u8 fan_reg_low[] = {AMC6821_REG_TDATA_LOW, ...@@ -130,224 +134,159 @@ static const u8 fan_reg_low[] = {AMC6821_REG_TDATA_LOW,
AMC6821_REG_TACH_HLIMITL, AMC6821_REG_TACH_HLIMITL,
AMC6821_REG_TACH_SETTINGL, }; AMC6821_REG_TACH_SETTINGL, };
static const u8 fan_reg_hi[] = {AMC6821_REG_TDATA_HI,
AMC6821_REG_TACH_LLIMITH,
AMC6821_REG_TACH_HLIMITH,
AMC6821_REG_TACH_SETTINGH, };
/* /*
* Client data (each client gets its own) * Client data (each client gets its own)
*/ */
struct amc6821_data { struct amc6821_data {
struct i2c_client *client; struct regmap *regmap;
struct mutex update_lock; struct mutex update_lock;
bool valid; /* false until following fields are valid */
unsigned long last_updated; /* in jiffies */
/* register values */
int temp[TEMP_IDX_LEN];
u16 fan[FAN1_IDX_LEN];
u8 fan1_pulses;
u8 pwm1;
u8 temp1_auto_point_temp[3];
u8 temp2_auto_point_temp[3];
u8 pwm1_auto_point_pwm[3];
u8 pwm1_enable;
u8 pwm1_auto_channels_temp;
u8 stat1;
u8 stat2;
}; };
static struct amc6821_data *amc6821_update_device(struct device *dev) /*
* 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)
{ {
struct amc6821_data *data = dev_get_drvdata(dev); u32 pwm, regval;
struct i2c_client *client = data->client; int err;
int timeout = HZ;
u8 reg;
int i;
mutex_lock(&data->update_lock); err = regmap_read(regmap, AMC6821_REG_DCY_LOW_TEMP, &pwm);
if (err)
return err;
if (time_after(jiffies, data->last_updated + timeout) || err = regmap_read(regmap, AMC6821_REG_PSV_TEMP, &regval);
!data->valid) { if (err)
return err;
for (i = 0; i < TEMP_IDX_LEN; i++) temps[0] = regval;
data->temp[i] = (int8_t)i2c_smbus_read_byte_data(
client, temp_reg[i]);
data->stat1 = i2c_smbus_read_byte_data(client,
AMC6821_REG_STAT1);
data->stat2 = i2c_smbus_read_byte_data(client,
AMC6821_REG_STAT2);
data->pwm1 = i2c_smbus_read_byte_data(client,
AMC6821_REG_DCY);
for (i = 0; i < FAN1_IDX_LEN; i++) {
data->fan[i] = i2c_smbus_read_byte_data(
client,
fan_reg_low[i]);
data->fan[i] += i2c_smbus_read_byte_data(
client,
fan_reg_hi[i]) << 8;
}
data->fan1_pulses = i2c_smbus_read_byte_data(client,
AMC6821_REG_CONF4);
data->fan1_pulses = data->fan1_pulses & AMC6821_CONF4_PSPR ? 4 : 2;
data->pwm1_auto_point_pwm[0] = 0;
data->pwm1_auto_point_pwm[2] = 255;
data->pwm1_auto_point_pwm[1] = i2c_smbus_read_byte_data(client,
AMC6821_REG_DCY_LOW_TEMP);
data->temp1_auto_point_temp[0] =
i2c_smbus_read_byte_data(client,
AMC6821_REG_PSV_TEMP);
data->temp2_auto_point_temp[0] =
data->temp1_auto_point_temp[0];
reg = i2c_smbus_read_byte_data(client,
AMC6821_REG_LTEMP_FAN_CTRL);
data->temp1_auto_point_temp[1] = (reg & 0xF8) >> 1;
reg &= 0x07;
reg = 0x20 >> reg;
if (reg > 0)
data->temp1_auto_point_temp[2] =
data->temp1_auto_point_temp[1] +
(data->pwm1_auto_point_pwm[2] -
data->pwm1_auto_point_pwm[1]) / reg;
else
data->temp1_auto_point_temp[2] = 255;
reg = i2c_smbus_read_byte_data(client,
AMC6821_REG_RTEMP_FAN_CTRL);
data->temp2_auto_point_temp[1] = (reg & 0xF8) >> 1;
reg &= 0x07;
reg = 0x20 >> reg;
if (reg > 0)
data->temp2_auto_point_temp[2] =
data->temp2_auto_point_temp[1] +
(data->pwm1_auto_point_pwm[2] -
data->pwm1_auto_point_pwm[1]) / reg;
else
data->temp2_auto_point_temp[2] = 255;
reg = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1);
reg = (reg >> 5) & 0x3;
switch (reg) {
case 0: /*open loop: software sets pwm1*/
data->pwm1_auto_channels_temp = 0;
data->pwm1_enable = 1;
break;
case 2: /*closed loop: remote T (temp2)*/
data->pwm1_auto_channels_temp = 2;
data->pwm1_enable = 2;
break;
case 3: /*closed loop: local and remote T (temp2)*/
data->pwm1_auto_channels_temp = 3;
data->pwm1_enable = 3;
break;
case 1: /*
* semi-open loop: software sets rpm, chip controls
* pwm1
*/
data->pwm1_auto_channels_temp = 0;
data->pwm1_enable = 4;
break;
}
data->last_updated = jiffies; err = regmap_read(regmap,
data->valid = true; channel ? AMC6821_REG_RTEMP_FAN_CTRL : AMC6821_REG_LTEMP_FAN_CTRL,
} &regval);
mutex_unlock(&data->update_lock); if (err)
return data; return err;
temps[1] = FIELD_GET(AMC6821_TEMP_LIMIT_MASK, regval) * 4;
/* slope is 32 >> <slope bits> in °C */
regval = 32 >> FIELD_GET(AMC6821_TEMP_SLOPE_MASK, regval);
if (regval)
temps[2] = temps[1] + DIV_ROUND_CLOSEST(255 - pwm, regval);
else
temps[2] = 255;
return 0;
} }
static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, static ssize_t temp_show(struct device *dev, struct device_attribute *devattr,
char *buf) char *buf)
{ {
struct amc6821_data *data = amc6821_update_device(dev); struct amc6821_data *data = dev_get_drvdata(dev);
int ix = to_sensor_dev_attr(devattr)->index; int ix = to_sensor_dev_attr(devattr)->index;
u32 regval;
int err;
return sprintf(buf, "%d\n", data->temp[ix] * 1000); err = regmap_read(data->regmap, temp_reg[ix], &regval);
if (err)
return err;
return sysfs_emit(buf, "%d\n", sign_extend32(regval, 7) * 1000);
} }
static ssize_t temp_store(struct device *dev, struct device_attribute *attr, static ssize_t temp_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct amc6821_data *data = dev_get_drvdata(dev); struct amc6821_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int ix = to_sensor_dev_attr(attr)->index; int ix = to_sensor_dev_attr(attr)->index;
long val; long val;
int err;
int ret = kstrtol(buf, 10, &val); int ret = kstrtol(buf, 10, &val);
if (ret) if (ret)
return ret; return ret;
val = clamp_val(val / 1000, -128, 127); val = clamp_val(val / 1000, -128, 127);
mutex_lock(&data->update_lock); err = regmap_write(data->regmap, temp_reg[ix], val);
data->temp[ix] = val; if (err)
if (i2c_smbus_write_byte_data(client, temp_reg[ix], data->temp[ix])) { return err;
dev_err(&client->dev, "Register write error, aborting.\n");
count = -EIO;
}
mutex_unlock(&data->update_lock);
return count; return count;
} }
static ssize_t temp_alarm_show(struct device *dev, static ssize_t temp_alarm_show(struct device *dev,
struct device_attribute *devattr, char *buf) struct device_attribute *devattr, char *buf)
{ {
struct amc6821_data *data = amc6821_update_device(dev); struct amc6821_data *data = dev_get_drvdata(dev);
int ix = to_sensor_dev_attr(devattr)->index; int ix = to_sensor_dev_attr(devattr)->index;
u8 flag; u32 regval, mask, reg;
int err;
switch (ix) { switch (ix) {
case IDX_TEMP1_MIN: case IDX_TEMP1_MIN:
flag = data->stat1 & AMC6821_STAT1_LTL; reg = AMC6821_REG_STAT1;
mask = AMC6821_STAT1_LTL;
break; break;
case IDX_TEMP1_MAX: case IDX_TEMP1_MAX:
flag = data->stat1 & AMC6821_STAT1_LTH; reg = AMC6821_REG_STAT1;
mask = AMC6821_STAT1_LTH;
break; break;
case IDX_TEMP1_CRIT: case IDX_TEMP1_CRIT:
flag = data->stat2 & AMC6821_STAT2_LTC; reg = AMC6821_REG_STAT2;
mask = AMC6821_STAT2_LTC;
break; break;
case IDX_TEMP2_MIN: case IDX_TEMP2_MIN:
flag = data->stat1 & AMC6821_STAT1_RTL; reg = AMC6821_REG_STAT1;
mask = AMC6821_STAT1_RTL;
break; break;
case IDX_TEMP2_MAX: case IDX_TEMP2_MAX:
flag = data->stat1 & AMC6821_STAT1_RTH; reg = AMC6821_REG_STAT1;
mask = AMC6821_STAT1_RTH;
break; break;
case IDX_TEMP2_CRIT: case IDX_TEMP2_CRIT:
flag = data->stat2 & AMC6821_STAT2_RTC; reg = AMC6821_REG_STAT2;
mask = AMC6821_STAT2_RTC;
break; break;
default: default:
dev_dbg(dev, "Unknown attr->index (%d).\n", ix);
return -EINVAL; return -EINVAL;
} }
if (flag) err = regmap_read(data->regmap, reg, &regval);
return sprintf(buf, "1"); if (err)
else return err;
return sprintf(buf, "0"); return sysfs_emit(buf, "%d\n", !!(regval & mask));
} }
static ssize_t temp2_fault_show(struct device *dev, static ssize_t temp2_fault_show(struct device *dev,
struct device_attribute *devattr, char *buf) struct device_attribute *devattr, char *buf)
{ {
struct amc6821_data *data = amc6821_update_device(dev); struct amc6821_data *data = dev_get_drvdata(dev);
if (data->stat1 & AMC6821_STAT1_RTF) u32 regval;
return sprintf(buf, "1"); int err;
else
return sprintf(buf, "0"); err = regmap_read(data->regmap, AMC6821_REG_STAT1, &regval);
if (err)
return err;
return sysfs_emit(buf, "%d\n", !!(regval & AMC6821_STAT1_RTF));
} }
static ssize_t pwm1_show(struct device *dev, struct device_attribute *devattr, static ssize_t pwm1_show(struct device *dev, struct device_attribute *devattr,
char *buf) char *buf)
{ {
struct amc6821_data *data = amc6821_update_device(dev); struct amc6821_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", data->pwm1); u32 regval;
int err;
err = regmap_read(data->regmap, AMC6821_REG_DCY, &regval);
if (err)
return err;
return sysfs_emit(buf, "%d\n", regval);
} }
static ssize_t pwm1_store(struct device *dev, static ssize_t pwm1_store(struct device *dev,
...@@ -355,24 +294,43 @@ static ssize_t pwm1_store(struct device *dev, ...@@ -355,24 +294,43 @@ static ssize_t pwm1_store(struct device *dev,
size_t count) size_t count)
{ {
struct amc6821_data *data = dev_get_drvdata(dev); struct amc6821_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
u8 val; u8 val;
int ret = kstrtou8(buf, 10, &val); int ret = kstrtou8(buf, 10, &val);
if (ret) if (ret)
return ret; return ret;
mutex_lock(&data->update_lock); ret = regmap_write(data->regmap, AMC6821_REG_DCY, val);
data->pwm1 = val; if (ret)
i2c_smbus_write_byte_data(client, AMC6821_REG_DCY, data->pwm1); return ret;
mutex_unlock(&data->update_lock);
return count; return count;
} }
static ssize_t pwm1_enable_show(struct device *dev, static ssize_t pwm1_enable_show(struct device *dev,
struct device_attribute *devattr, char *buf) struct device_attribute *devattr, char *buf)
{ {
struct amc6821_data *data = amc6821_update_device(dev); struct amc6821_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", data->pwm1_enable); int err;
u32 val;
err = regmap_read(data->regmap, AMC6821_REG_CONF1, &val);
if (err)
return err;
switch (val & (AMC6821_CONF1_FDRC0 | AMC6821_CONF1_FDRC1)) {
case 0:
val = 1; /* manual */
break;
case AMC6821_CONF1_FDRC0:
val = 4; /* target rpm (fan1_target) controlled */
break;
case AMC6821_CONF1_FDRC1:
val = 2; /* remote temp controlled */
break;
default:
val = 3; /* max(local, remote) temp controlled */
break;
}
return sysfs_emit(buf, "%d\n", val);
} }
static ssize_t pwm1_enable_store(struct device *dev, static ssize_t pwm1_enable_store(struct device *dev,
...@@ -380,49 +338,37 @@ static ssize_t pwm1_enable_store(struct device *dev, ...@@ -380,49 +338,37 @@ static ssize_t pwm1_enable_store(struct device *dev,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct amc6821_data *data = dev_get_drvdata(dev); struct amc6821_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
long val; long val;
int config = kstrtol(buf, 10, &val); u32 mode;
if (config) int err;
return config;
mutex_lock(&data->update_lock); err = kstrtol(buf, 10, &val);
config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1); if (err)
if (config < 0) { return err;
dev_err(&client->dev,
"Error reading configuration register, aborting.\n");
count = config;
goto unlock;
}
switch (val) { switch (val) {
case 1: case 1:
config &= ~AMC6821_CONF1_FDRC0; mode = 0;
config &= ~AMC6821_CONF1_FDRC1;
break; break;
case 2: case 2:
config &= ~AMC6821_CONF1_FDRC0; mode = AMC6821_CONF1_FDRC1;
config |= AMC6821_CONF1_FDRC1;
break; break;
case 3: case 3:
config |= AMC6821_CONF1_FDRC0; mode = AMC6821_CONF1_FDRC0 | AMC6821_CONF1_FDRC1;
config |= AMC6821_CONF1_FDRC1;
break; break;
case 4: case 4:
config |= AMC6821_CONF1_FDRC0; mode = AMC6821_CONF1_FDRC0;
config &= ~AMC6821_CONF1_FDRC1;
break; break;
default: default:
count = -EINVAL; return -EINVAL;
goto unlock;
}
if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF1, config)) {
dev_err(&client->dev,
"Configuration register write error, aborting.\n");
count = -EIO;
} }
unlock:
mutex_unlock(&data->update_lock); err = regmap_update_bits(data->regmap, AMC6821_REG_CONF1,
AMC6821_CONF1_FDRC0 | AMC6821_CONF1_FDRC1,
mode);
if (err)
return err;
return count; return count;
} }
...@@ -430,130 +376,166 @@ static ssize_t pwm1_auto_channels_temp_show(struct device *dev, ...@@ -430,130 +376,166 @@ static ssize_t pwm1_auto_channels_temp_show(struct device *dev,
struct device_attribute *devattr, struct device_attribute *devattr,
char *buf) char *buf)
{ {
struct amc6821_data *data = amc6821_update_device(dev); struct amc6821_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", data->pwm1_auto_channels_temp); u32 val;
int err;
err = regmap_read(data->regmap, AMC6821_REG_CONF1, &val);
if (err)
return err;
switch (val & (AMC6821_CONF1_FDRC0 | AMC6821_CONF1_FDRC1)) {
case 0:
case AMC6821_CONF1_FDRC0:
val = 0; /* manual or target rpm controlled */
break;
case AMC6821_CONF1_FDRC1:
val = 2; /* remote temp controlled */
break;
default:
val = 3; /* max(local, remote) temp controlled */
break;
}
return sysfs_emit(buf, "%d\n", val);
} }
static ssize_t temp_auto_point_temp_show(struct device *dev, static ssize_t temp_auto_point_temp_show(struct device *dev,
struct device_attribute *devattr, struct device_attribute *devattr,
char *buf) char *buf)
{ {
struct amc6821_data *data = dev_get_drvdata(dev);
int ix = to_sensor_dev_attr_2(devattr)->index; int ix = to_sensor_dev_attr_2(devattr)->index;
int nr = to_sensor_dev_attr_2(devattr)->nr; int nr = to_sensor_dev_attr_2(devattr)->nr;
struct amc6821_data *data = amc6821_update_device(dev); u8 temps[3];
switch (nr) { int err;
case 1:
return sprintf(buf, "%d\n", mutex_lock(&data->update_lock);
data->temp1_auto_point_temp[ix] * 1000); err = amc6821_get_auto_point_temps(data->regmap, nr, temps);
case 2: mutex_unlock(&data->update_lock);
return sprintf(buf, "%d\n", if (err)
data->temp2_auto_point_temp[ix] * 1000); return err;
default:
dev_dbg(dev, "Unknown attr->nr (%d).\n", nr); return sysfs_emit(buf, "%d\n", temps[ix] * 1000);
return -EINVAL;
}
} }
static ssize_t pwm1_auto_point_pwm_show(struct device *dev, static ssize_t pwm1_auto_point_pwm_show(struct device *dev,
struct device_attribute *devattr, struct device_attribute *devattr,
char *buf) char *buf)
{ {
struct amc6821_data *data = dev_get_drvdata(dev);
int ix = to_sensor_dev_attr(devattr)->index; int ix = to_sensor_dev_attr(devattr)->index;
struct amc6821_data *data = amc6821_update_device(dev); u32 val;
return sprintf(buf, "%d\n", data->pwm1_auto_point_pwm[ix]); int err;
switch (ix) {
case 0:
val = 0;
break;
case 1:
err = regmap_read(data->regmap, AMC6821_REG_DCY_LOW_TEMP, &val);
if (err)
return err;
break;
default:
val = 255;
break;
}
return sysfs_emit(buf, "%d\n", val);
} }
static inline ssize_t set_slope_register(struct i2c_client *client, /*
u8 reg, * Set TEMP[0-4] (low temperature) and SLP[0-2] (slope) of local or remote
u8 dpwm, * TEMP-FAN control register.
u8 *ptemp) *
* Return 0 on success or negative error code.
*
* Channel 0: local, channel 1: remote
*/
static inline int set_slope_register(struct regmap *regmap, int channel, u8 *temps)
{ {
int dt; u8 regval = FIELD_PREP(AMC6821_TEMP_LIMIT_MASK, temps[1] / 4);
u8 tmp; u8 tmp, dpwm;
int err, dt;
u32 pwm;
err = regmap_read(regmap, AMC6821_REG_DCY_LOW_TEMP, &pwm);
if (err)
return err;
dt = ptemp[2]-ptemp[1]; dpwm = 255 - pwm;
dt = temps[2] - temps[1];
for (tmp = 4; tmp > 0; tmp--) { for (tmp = 4; tmp > 0; tmp--) {
if (dt * (0x20 >> tmp) >= dpwm) if (dt * (32 >> tmp) >= dpwm)
break; break;
} }
tmp |= (ptemp[1] & 0x7C) << 1; regval |= FIELD_PREP(AMC6821_TEMP_SLOPE_MASK, tmp);
if (i2c_smbus_write_byte_data(client,
reg, tmp)) { return regmap_write(regmap,
dev_err(&client->dev, "Register write error, aborting.\n"); channel ? AMC6821_REG_RTEMP_FAN_CTRL : AMC6821_REG_LTEMP_FAN_CTRL,
return -EIO; regval);
}
return 0;
} }
static ssize_t temp_auto_point_temp_store(struct device *dev, static ssize_t temp_auto_point_temp_store(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct amc6821_data *data = amc6821_update_device(dev); struct amc6821_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int ix = to_sensor_dev_attr_2(attr)->index; int ix = to_sensor_dev_attr_2(attr)->index;
int nr = to_sensor_dev_attr_2(attr)->nr; int nr = to_sensor_dev_attr_2(attr)->nr;
u8 *ptemp; struct regmap *regmap = data->regmap;
u8 reg; u8 temps[3], otemps[3];
int dpwm;
long val; long val;
int ret = kstrtol(buf, 10, &val); int ret;
ret = kstrtol(buf, 10, &val);
if (ret) if (ret)
return ret; return ret;
switch (nr) {
case 1:
ptemp = data->temp1_auto_point_temp;
reg = AMC6821_REG_LTEMP_FAN_CTRL;
break;
case 2:
ptemp = data->temp2_auto_point_temp;
reg = AMC6821_REG_RTEMP_FAN_CTRL;
break;
default:
dev_dbg(dev, "Unknown attr->nr (%d).\n", nr);
return -EINVAL;
}
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
data->valid = false;
ret = amc6821_get_auto_point_temps(data->regmap, nr, temps);
if (ret)
goto unlock;
switch (ix) { switch (ix) {
case 0: case 0:
ptemp[0] = clamp_val(val / 1000, 0, /*
data->temp1_auto_point_temp[1]); * Passive cooling temperature. Range limit against low limit
ptemp[0] = clamp_val(ptemp[0], 0, * of both channels.
data->temp2_auto_point_temp[1]); */
ptemp[0] = clamp_val(ptemp[0], 0, 63); ret = amc6821_get_auto_point_temps(data->regmap, 1 - nr, otemps);
if (i2c_smbus_write_byte_data( if (ret)
client, goto unlock;
AMC6821_REG_PSV_TEMP, val = DIV_ROUND_CLOSEST(clamp_val(val, 0, 63000), 1000);
ptemp[0])) { val = clamp_val(val, 0, min(temps[1], otemps[1]));
dev_err(&client->dev, ret = regmap_write(regmap, AMC6821_REG_PSV_TEMP, val);
"Register write error, aborting.\n"); break;
count = -EIO;
}
goto EXIT;
case 1: case 1:
ptemp[1] = clamp_val(val / 1000, (ptemp[0] & 0x7C) + 4, 124); /*
ptemp[1] &= 0x7C; * Low limit; must be between passive and high limit,
ptemp[2] = clamp_val(ptemp[2], ptemp[1] + 1, 255); * and not exceed 124. Step size is 4 degrees C.
*/
val = clamp_val(val, DIV_ROUND_UP(temps[0], 4) * 4000, 124000);
temps[1] = DIV_ROUND_CLOSEST(val, 4000) * 4;
val = temps[1] / 4;
/* Auto-adjust high limit if necessary */
temps[2] = clamp_val(temps[2], temps[1] + 1, 255);
ret = set_slope_register(regmap, nr, temps);
break; break;
case 2: case 2:
ptemp[2] = clamp_val(val / 1000, ptemp[1]+1, 255); /* high limit, must be higher than low limit */
val = clamp_val(val, (temps[1] + 1) * 1000, 255000);
temps[2] = DIV_ROUND_CLOSEST(val, 1000);
ret = set_slope_register(regmap, nr, temps);
break; break;
default: default:
dev_dbg(dev, "Unknown attr->index (%d).\n", ix); ret = -EINVAL;
count = -EINVAL; break;
goto EXIT;
} }
dpwm = data->pwm1_auto_point_pwm[2] - data->pwm1_auto_point_pwm[1]; unlock:
if (set_slope_register(client, reg, dpwm, ptemp))
count = -EIO;
EXIT:
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
return count; return ret ? : count;
} }
static ssize_t pwm1_auto_point_pwm_store(struct device *dev, static ssize_t pwm1_auto_point_pwm_store(struct device *dev,
...@@ -561,101 +543,107 @@ static ssize_t pwm1_auto_point_pwm_store(struct device *dev, ...@@ -561,101 +543,107 @@ static ssize_t pwm1_auto_point_pwm_store(struct device *dev,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct amc6821_data *data = dev_get_drvdata(dev); struct amc6821_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client; struct regmap *regmap = data->regmap;
int dpwm; int i, ret;
u8 val; u8 val;
int ret;
ret = kstrtou8(buf, 10, &val); ret = kstrtou8(buf, 10, &val);
if (ret) if (ret)
return ret; return ret;
mutex_lock(&data->update_lock); mutex_lock(&data->update_lock);
data->pwm1_auto_point_pwm[1] = val; ret = regmap_write(regmap, AMC6821_REG_DCY_LOW_TEMP, val);
if (i2c_smbus_write_byte_data(client, AMC6821_REG_DCY_LOW_TEMP, if (ret)
data->pwm1_auto_point_pwm[1])) { goto unlock;
dev_err(&client->dev, "Register write error, aborting.\n");
count = -EIO; for (i = 0; i < 2; i++) {
goto EXIT; u8 temps[3];
}
dpwm = data->pwm1_auto_point_pwm[2] - data->pwm1_auto_point_pwm[1];
if (set_slope_register(client, AMC6821_REG_LTEMP_FAN_CTRL, dpwm,
data->temp1_auto_point_temp)) {
count = -EIO;
goto EXIT;
}
if (set_slope_register(client, AMC6821_REG_RTEMP_FAN_CTRL, dpwm,
data->temp2_auto_point_temp)) {
count = -EIO;
goto EXIT;
}
EXIT: ret = amc6821_get_auto_point_temps(regmap, i, temps);
data->valid = false; if (ret)
break;
ret = set_slope_register(regmap, i, temps);
if (ret)
break;
}
unlock:
mutex_unlock(&data->update_lock); mutex_unlock(&data->update_lock);
return count; return ret ? : count;
} }
static ssize_t fan_show(struct device *dev, struct device_attribute *devattr, static ssize_t fan_show(struct device *dev, struct device_attribute *devattr,
char *buf) char *buf)
{ {
struct amc6821_data *data = amc6821_update_device(dev); struct amc6821_data *data = dev_get_drvdata(dev);
int ix = to_sensor_dev_attr(devattr)->index; int ix = to_sensor_dev_attr(devattr)->index;
if (0 == data->fan[ix]) u32 regval;
return sprintf(buf, "0"); u8 regs[2];
return sprintf(buf, "%d\n", (int)(6000000 / data->fan[ix])); int err;
err = regmap_bulk_read(data->regmap, fan_reg_low[ix], regs, 2);
if (err)
return err;
regval = (regs[1] << 8) | regs[0];
return sysfs_emit(buf, "%d\n", regval ? 6000000 / regval : 0);
} }
static ssize_t fan1_fault_show(struct device *dev, static ssize_t fan1_fault_show(struct device *dev,
struct device_attribute *devattr, char *buf) struct device_attribute *devattr, char *buf)
{ {
struct amc6821_data *data = amc6821_update_device(dev); struct amc6821_data *data = dev_get_drvdata(dev);
if (data->stat1 & AMC6821_STAT1_FANS) u32 regval;
return sprintf(buf, "1"); int err;
else
return sprintf(buf, "0"); err = regmap_read(data->regmap, AMC6821_REG_STAT1, &regval);
if (err)
return err;
return sysfs_emit(buf, "%d\n", !!(regval & AMC6821_STAT1_FANS));
} }
static ssize_t fan_store(struct device *dev, struct device_attribute *attr, static ssize_t fan_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct amc6821_data *data = dev_get_drvdata(dev); struct amc6821_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
unsigned long val;
int ix = to_sensor_dev_attr(attr)->index; int ix = to_sensor_dev_attr(attr)->index;
int ret = kstrtoul(buf, 10, &val); unsigned long val;
if (ret) u8 regs[2];
return ret; int err;
err = kstrtoul(buf, 10, &val);
if (err)
return err;
/* Minimum and target fan speed must not be unlimited (0) */ /* Minimum and target fan speed must not be unlimited (0) */
if ((ix == IDX_FAN1_MIN || ix == IDX_FAN1_TARGET) && !val) if ((ix == IDX_FAN1_MIN || ix == IDX_FAN1_TARGET) && !val)
return -EINVAL; return -EINVAL;
val = val > 0 ? 6000000 / clamp_val(val, 1, 6000000) : 0; val = val > 0 ? 6000000 / min(val, 6000000) : 0;
val = clamp_val(val, 0, 0xFFFF);
regs[0] = val & 0xff;
regs[1] = val >> 8;
err = regmap_bulk_write(data->regmap, fan_reg_low[ix], regs, 2);
if (err)
return err;
mutex_lock(&data->update_lock);
data->fan[ix] = clamp_val(val, 0, 0xFFFF);
if (i2c_smbus_write_byte_data(client, fan_reg_low[ix],
data->fan[ix] & 0xFF)) {
dev_err(&client->dev, "Register write error, aborting.\n");
count = -EIO;
goto EXIT;
}
if (i2c_smbus_write_byte_data(client,
fan_reg_hi[ix], data->fan[ix] >> 8)) {
dev_err(&client->dev, "Register write error, aborting.\n");
count = -EIO;
}
EXIT:
mutex_unlock(&data->update_lock);
return count; return count;
} }
static ssize_t fan1_pulses_show(struct device *dev, static ssize_t fan1_pulses_show(struct device *dev,
struct device_attribute *devattr, char *buf) struct device_attribute *devattr, char *buf)
{ {
struct amc6821_data *data = amc6821_update_device(dev); struct amc6821_data *data = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", data->fan1_pulses); u32 regval;
int err;
err = regmap_read(data->regmap, AMC6821_REG_CONF4, &regval);
if (err)
return err;
return sysfs_emit(buf, "%d\n", (regval & AMC6821_CONF4_PSPR) ? 4 : 2);
} }
static ssize_t fan1_pulses_store(struct device *dev, static ssize_t fan1_pulses_store(struct device *dev,
...@@ -663,40 +651,22 @@ static ssize_t fan1_pulses_store(struct device *dev, ...@@ -663,40 +651,22 @@ static ssize_t fan1_pulses_store(struct device *dev,
size_t count) size_t count)
{ {
struct amc6821_data *data = dev_get_drvdata(dev); struct amc6821_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
long val; long val;
int config = kstrtol(buf, 10, &val); int err;
if (config)
return config; err = kstrtol(buf, 10, &val);
if (err)
return err;
if (val != 2 && val != 4)
return -EINVAL;
err = regmap_update_bits(data->regmap, AMC6821_REG_CONF4,
AMC6821_CONF4_PSPR,
val == 4 ? AMC6821_CONF4_PSPR : 0);
if (err)
return err;
mutex_lock(&data->update_lock);
config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF4);
if (config < 0) {
dev_err(&client->dev,
"Error reading configuration register, aborting.\n");
count = config;
goto EXIT;
}
switch (val) {
case 2:
config &= ~AMC6821_CONF4_PSPR;
data->fan1_pulses = 2;
break;
case 4:
config |= AMC6821_CONF4_PSPR;
data->fan1_pulses = 4;
break;
default:
count = -EINVAL;
goto EXIT;
}
if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF4, config)) {
dev_err(&client->dev,
"Configuration register write error, aborting.\n");
count = -EIO;
}
EXIT:
mutex_unlock(&data->update_lock);
return count; return count;
} }
...@@ -730,18 +700,18 @@ static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point3_pwm, pwm1_auto_point_pwm, 2); ...@@ -730,18 +700,18 @@ static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point3_pwm, pwm1_auto_point_pwm, 2);
static SENSOR_DEVICE_ATTR_RO(pwm1_auto_channels_temp, pwm1_auto_channels_temp, static SENSOR_DEVICE_ATTR_RO(pwm1_auto_channels_temp, pwm1_auto_channels_temp,
0); 0);
static SENSOR_DEVICE_ATTR_2_RO(temp1_auto_point1_temp, temp_auto_point_temp, static SENSOR_DEVICE_ATTR_2_RO(temp1_auto_point1_temp, temp_auto_point_temp,
1, 0); 0, 0);
static SENSOR_DEVICE_ATTR_2_RW(temp1_auto_point2_temp, temp_auto_point_temp, static SENSOR_DEVICE_ATTR_2_RW(temp1_auto_point2_temp, temp_auto_point_temp,
1, 1); 0, 1);
static SENSOR_DEVICE_ATTR_2_RW(temp1_auto_point3_temp, temp_auto_point_temp, static SENSOR_DEVICE_ATTR_2_RW(temp1_auto_point3_temp, temp_auto_point_temp,
1, 2); 0, 2);
static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_point1_temp, temp_auto_point_temp, static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_point1_temp, temp_auto_point_temp,
2, 0); 1, 0);
static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_point2_temp, temp_auto_point_temp, static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_point2_temp, temp_auto_point_temp,
2, 1); 1, 1);
static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_point3_temp, temp_auto_point_temp, static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_point3_temp, temp_auto_point_temp,
2, 2); 1, 2);
static struct attribute *amc6821_attrs[] = { static struct attribute *amc6821_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_input.dev_attr.attr,
...@@ -828,110 +798,79 @@ static int amc6821_detect( ...@@ -828,110 +798,79 @@ static int amc6821_detect(
return 0; return 0;
} }
static int amc6821_init_client(struct i2c_client *client) static int amc6821_init_client(struct amc6821_data *data)
{ {
int config; struct regmap *regmap = data->regmap;
int err = -EIO; int err;
if (init) { if (init) {
config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF4); err = regmap_set_bits(regmap, AMC6821_REG_CONF4, AMC6821_CONF4_MODE);
if (err)
if (config < 0) {
dev_err(&client->dev,
"Error reading configuration register, aborting.\n");
return err;
}
config |= AMC6821_CONF4_MODE;
if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF4,
config)) {
dev_err(&client->dev,
"Configuration register write error, aborting.\n");
return err;
}
config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF3);
if (config < 0) {
dev_err(&client->dev,
"Error reading configuration register, aborting.\n");
return err;
}
dev_info(&client->dev, "Revision %d\n", config & 0x0f);
config &= ~AMC6821_CONF3_THERM_FAN_EN;
if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF3,
config)) {
dev_err(&client->dev,
"Configuration register write error, aborting.\n");
return err; return err;
} err = regmap_clear_bits(regmap, AMC6821_REG_CONF3, AMC6821_CONF3_THERM_FAN_EN);
if (err)
config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF2);
if (config < 0) {
dev_err(&client->dev,
"Error reading configuration register, aborting.\n");
return err; return err;
} err = regmap_clear_bits(regmap, AMC6821_REG_CONF2,
AMC6821_CONF2_RTFIE |
config &= ~AMC6821_CONF2_RTFIE; AMC6821_CONF2_LTOIE |
config &= ~AMC6821_CONF2_LTOIE; AMC6821_CONF2_RTOIE);
config &= ~AMC6821_CONF2_RTOIE; if (err)
if (i2c_smbus_write_byte_data(client,
AMC6821_REG_CONF2, config)) {
dev_err(&client->dev,
"Configuration register write error, aborting.\n");
return err; return err;
}
config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1);
if (config < 0) { err = regmap_update_bits(regmap, AMC6821_REG_CONF1,
dev_err(&client->dev, AMC6821_CONF1_THERMOVIE | AMC6821_CONF1_FANIE |
"Error reading configuration register, aborting.\n"); AMC6821_CONF1_START | AMC6821_CONF1_PWMINV,
AMC6821_CONF1_START |
(pwminv ? AMC6821_CONF1_PWMINV : 0));
if (err)
return err; return err;
}
config &= ~AMC6821_CONF1_THERMOVIE;
config &= ~AMC6821_CONF1_FANIE;
config |= AMC6821_CONF1_START;
if (pwminv)
config |= AMC6821_CONF1_PWMINV;
else
config &= ~AMC6821_CONF1_PWMINV;
if (i2c_smbus_write_byte_data(
client, AMC6821_REG_CONF1, config)) {
dev_err(&client->dev,
"Configuration register write error, aborting.\n");
return err;
}
} }
return 0; return 0;
} }
static bool amc6821_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case AMC6821_REG_STAT1:
case AMC6821_REG_STAT2:
case AMC6821_REG_TEMP_LO:
case AMC6821_REG_TDATA_LOW:
case AMC6821_REG_LTEMP_HI:
case AMC6821_REG_RTEMP_HI:
case AMC6821_REG_TDATA_HI:
return true;
default:
return false;
}
}
static const struct regmap_config amc6821_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = AMC6821_REG_CONF3,
.volatile_reg = amc6821_volatile_reg,
.cache_type = REGCACHE_MAPLE,
};
static int amc6821_probe(struct i2c_client *client) static int amc6821_probe(struct i2c_client *client)
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct amc6821_data *data; struct amc6821_data *data;
struct device *hwmon_dev; struct device *hwmon_dev;
struct regmap *regmap;
int err; int err;
data = devm_kzalloc(dev, sizeof(struct amc6821_data), GFP_KERNEL); data = devm_kzalloc(dev, sizeof(struct amc6821_data), GFP_KERNEL);
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
data->client = client; regmap = devm_regmap_init_i2c(client, &amc6821_regmap_config);
mutex_init(&data->update_lock); if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap),
"Failed to initialize regmap\n");
data->regmap = regmap;
/* err = amc6821_init_client(data);
* Initialize the amc6821 chip
*/
err = amc6821_init_client(client);
if (err) if (err)
return err; return err;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment