// SPDX-License-Identifier: GPL-2.0-only /* * adt7475 - Thermal sensor driver for the ADT7475 chip and derivatives * Copyright (C) 2007-2008, Advanced Micro Devices, Inc. * Copyright (C) 2008 Jordan Crouse <jordan@cosmicpenguin.net> * Copyright (C) 2008 Hans de Goede <hdegoede@redhat.com> * Copyright (C) 2009 Jean Delvare <jdelvare@suse.de> * * Derived from the lm83 driver by Jean Delvare
*/
/* Indexes for the sysfs hooks */ enum adt_sysfs_id {
INPUT = 0,
MIN = 1,
MAX = 2,
CONTROL = 3,
OFFSET = 3, // Dup
AUTOMIN = 4,
THERM = 5,
HYSTERSIS = 6, /* * These are unique identifiers for the sysfs functions - unlike the * numbers above, these are not also indexes into an array
*/
ALARM = 9,
FAULT = 10,
};
/* 7475 Common Registers */
#define REG_DEVREV2 0x12 /* ADT7490 only */ #define REG_IMON 0x1D /* ADT7490 only */
#define REG_VTT 0x1E /* ADT7490 only */ #define REG_EXTEND3 0x1F /* ADT7490 only */
switch (sattr->nr) { case HYSTERSIS:
mutex_lock(&data->lock);
out = data->temp[sattr->nr][sattr->index]; if (sattr->index != 1)
out = (out >> 4) & 0xF; else
out = (out & 0xF); /* * Show the value as an absolute number tied to * THERM
*/
out = reg2temp(data, data->temp[THERM][sattr->index]) -
out * 1000;
mutex_unlock(&data->lock); break;
case OFFSET: /* * Offset is always 2's complement, regardless of the * setting in CONFIG5
*/
mutex_lock(&data->lock);
out = (s8)data->temp[sattr->nr][sattr->index]; if (data->config5 & CONFIG5_TEMPOFFSET)
out *= 1000; else
out *= 500;
mutex_unlock(&data->lock); break;
case ALARM:
out = (data->alarms >> (sattr->index + 4)) & 1; break;
case FAULT: /* Note - only for remote1 and remote2 */
out = !!(data->alarms & (sattr->index ? 0x8000 : 0x4000)); break;
default: /* All other temp values are in the configured format */
out = reg2temp(data, data->temp[sattr->nr][sattr->index]);
}
/* We need the config register in all cases for temp <-> reg conv. */
data->config5 = adt7475_read(REG_CONFIG5);
switch (sattr->nr) { case OFFSET: if (data->config5 & CONFIG5_TEMPOFFSET) {
val = clamp_val(val, -63000, 127000);
out = data->temp[OFFSET][sattr->index] = val / 1000;
} else {
val = clamp_val(val, -63000, 64000);
out = data->temp[OFFSET][sattr->index] = val / 500;
} break;
case HYSTERSIS: /* * The value will be given as an absolute value, turn it * into an offset based on THERM
*/
/* Read fresh THERM and HYSTERSIS values from the chip */
data->temp[THERM][sattr->index] =
adt7475_read(TEMP_THERM_REG(sattr->index)) << 2;
adt7475_read_hystersis(client);
temp = reg2temp(data, data->temp[THERM][sattr->index]);
val = clamp_val(val, temp - 15000, temp);
val = (temp - val) / 1000;
/* * We maintain an extra 2 digits of precision for simplicity * - shift those back off before writing the value
*/
out = (u8) (data->temp[sattr->nr][sattr->index] >> 2);
}
switch (sattr->nr) { case MIN:
reg = TEMP_MIN_REG(sattr->index); break; case MAX:
reg = TEMP_MAX_REG(sattr->index); break; case OFFSET:
reg = TEMP_OFFSET_REG(sattr->index); break; case AUTOMIN:
reg = TEMP_TMIN_REG(sattr->index); break; case THERM:
reg = TEMP_THERM_REG(sattr->index); break; case HYSTERSIS: if (sattr->index != 2)
reg = REG_REMOTE1_HYSTERSIS; else
reg = REG_REMOTE2_HYSTERSIS;
/* * Table of autorange values - the user will write the value in millidegrees, * and we'll convert it
*/ staticconstint autorange_table[] = {
2000, 2500, 3330, 4000, 5000, 6670, 8000,
10000, 13330, 16000, 20000, 26670, 32000, 40000,
53330, 80000
};
/* Get a fresh copy of the needed registers */
data->config5 = adt7475_read(REG_CONFIG5);
data->temp[AUTOMIN][sattr->index] =
adt7475_read(TEMP_TMIN_REG(sattr->index)) << 2;
data->range[sattr->index] =
adt7475_read(TEMP_TRANGE_REG(sattr->index));
/* * The user will write an absolute value, so subtract the start point * to figure the range
*/
temp = reg2temp(data, data->temp[AUTOMIN][sattr->index]);
val = clamp_val(val, temp + autorange_table[0],
temp + autorange_table[ARRAY_SIZE(autorange_table) - 1]);
val -= temp;
/* Find the nearest table entry to what the user wrote */
val = find_closest(val, autorange_table, ARRAY_SIZE(autorange_table));
data->range[sattr->index] &= ~0xF0;
data->range[sattr->index] |= val << 4;
switch (sattr->nr) { case INPUT: /* Get a fresh value for CONTROL */
data->pwm[CONTROL][sattr->index] =
adt7475_read(PWM_CONFIG_REG(sattr->index));
/* * If we are not in manual mode, then we shouldn't allow * the user to set the pwm speed
*/ if (((data->pwm[CONTROL][sattr->index] >> 5) & 7) != 7) {
mutex_unlock(&data->lock); return count;
}
reg = PWM_REG(sattr->index); break;
case MIN:
reg = PWM_MIN_REG(sattr->index); break;
case MAX:
reg = PWM_MAX_REG(sattr->index); break;
}
staticint hw_set_pwm(struct i2c_client *client, int index, unsignedint pwmctl, unsignedint pwmchan)
{ struct adt7475_data *data = i2c_get_clientdata(client); long val = 0;
switch (pwmctl) { case 0:
val = 0x03; /* Run at full speed */ break; case 1:
val = 0x07; /* Manual mode */ break; case 2: switch (pwmchan) { case 1: /* Remote1 controls PWM */
val = 0x00; break; case 2: /* local controls PWM */
val = 0x01; break; case 4: /* remote2 controls PWM */
val = 0x02; break; case 6: /* local/remote2 control PWM */
val = 0x05; break; case 7: /* All three control PWM */
val = 0x06; break; default: return -EINVAL;
} break; default: return -EINVAL;
}
devid = adt7475_read(REG_DEVID); if (devid == 0x73)
name = "adt7473"; elseif (devid == 0x75 && client->addr == 0x2e)
name = "adt7475"; elseif (devid == 0x76)
name = "adt7476"; elseif ((devid2 & 0xfc) == 0x6c)
name = "adt7490"; else {
dev_dbg(&adapter->dev, "Couldn't detect an ADT7473/75/76/90 part at " "0x%02x\n", (unsignedint)client->addr); return -ENODEV;
}
strscpy(info->type, name, I2C_NAME_SIZE);
return 0;
}
staticint adt7475_update_limits(struct i2c_client *client)
{ struct adt7475_data *data = i2c_get_clientdata(client); int i; int ret;
ret = adt7475_read(REG_CONFIG4); if (ret < 0) return ret;
data->config4 = ret;
ret = adt7475_read(REG_CONFIG5); if (ret < 0) return ret;
data->config5 = ret;
for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++) { if (!(data->has_voltage & (1 << i))) continue; /* Adjust values so they match the input precision */
ret = adt7475_read(VOLTAGE_MIN_REG(i)); if (ret < 0) return ret;
data->voltage[MIN][i] = ret << 2;
ret = adt7475_read(VOLTAGE_MAX_REG(i)); if (ret < 0) return ret;
data->voltage[MAX][i] = ret << 2;
}
if (data->has_voltage & (1 << 5)) {
ret = adt7475_read(REG_VTT_MIN); if (ret < 0) return ret;
data->voltage[MIN][5] = ret << 2;
ret = adt7475_read(REG_VTT_MAX); if (ret < 0) return ret;
data->voltage[MAX][5] = ret << 2;
}
if (data->has_voltage & (1 << 6)) {
ret = adt7475_read(REG_IMON_MIN); if (ret < 0) return ret;
data->voltage[MIN][6] = ret << 2;
ret = adt7475_read(REG_IMON_MAX); if (ret < 0) return ret;
data->voltage[MAX][6] = ret << 2;
}
for (i = 0; i < ADT7475_TEMP_COUNT; i++) { /* Adjust values so they match the input precision */
ret = adt7475_read(TEMP_MIN_REG(i)); if (ret < 0) return ret;
data->temp[MIN][i] = ret << 2;
ret = adt7475_read(TEMP_MAX_REG(i)); if (ret < 0) return ret;
data->temp[MAX][i] = ret << 2;
ret = adt7475_read(TEMP_TMIN_REG(i)); if (ret < 0) return ret;
data->temp[AUTOMIN][i] = ret << 2;
ret = adt7475_read(TEMP_THERM_REG(i)); if (ret < 0) return ret;
data->temp[THERM][i] = ret << 2;
ret = adt7475_read(TEMP_OFFSET_REG(i)); if (ret < 0) return ret;
data->temp[OFFSET][i] = ret;
}
adt7475_read_hystersis(client);
for (i = 0; i < ADT7475_TACH_COUNT; i++) { if (i == 3 && !data->has_fan4) continue;
ret = adt7475_read_word(client, TACH_MIN_REG(i)); if (ret < 0) return ret;
data->tach[MIN][i] = ret;
}
for (i = 0; i < ADT7475_PWM_COUNT; i++) { if (i == 1 && !data->has_pwm2) continue;
ret = adt7475_read(PWM_MAX_REG(i)); if (ret < 0) return ret;
data->pwm[MAX][i] = ret;
ret = adt7475_read(PWM_MIN_REG(i)); if (ret < 0) return ret;
data->pwm[MIN][i] = ret; /* Set the channel and control information */
adt7475_read_pwm(client, i);
}
ret = adt7475_read(TEMP_TRANGE_REG(0)); if (ret < 0) return ret;
data->range[0] = ret;
ret = adt7475_read(TEMP_TRANGE_REG(1)); if (ret < 0) return ret;
data->range[1] = ret;
ret = adt7475_read(TEMP_TRANGE_REG(2)); if (ret < 0) return ret;
data->range[2] = ret;
ret = device_property_read_u32_array(&client->dev, "adi,pwm-active-state", states,
ARRAY_SIZE(states)); if (ret) return ret;
for (i = 0; i < ADT7475_PWM_COUNT; i++) {
ret = adt7475_read(PWM_CONFIG_REG(i)); if (ret < 0) return ret;
val = ret; if (states[i])
val &= ~BIT(4); else
val |= BIT(4);
ret = i2c_smbus_write_byte_data(client, PWM_CONFIG_REG(i), val); if (ret) return ret;
}
return 0;
}
struct adt7475_pwm_config { int index; int freq; int flags; int duty;
};
staticint _adt7475_pwm_properties_parse_args(u32 args[4], struct adt7475_pwm_config *cfg)
{ int freq_hz; int duty;
ret = load_config(client, chip); if (ret) return ret;
config3 = adt7475_read(REG_CONFIG3); /* Pin PWM2 may alternatively be used for ALERT output */ if (!(config3 & CONFIG3_SMBALERT))
data->has_pwm2 = 1; /* Meaning of this bit is inverted for the ADT7473-1 */ if (chip == adt7473 && revision >= 1)
data->has_pwm2 = !data->has_pwm2;
data->config4 = adt7475_read(REG_CONFIG4); /* Pin TACH4 may alternatively be used for THERM */ if ((data->config4 & CONFIG4_PINFUNC) == 0x0)
data->has_fan4 = 1;
/* * THERM configuration is more complex on the ADT7476 and ADT7490, * because 2 different pins (TACH4 and +2.5 Vin) can be used for * this function
*/ if (chip == adt7490) { if ((data->config4 & CONFIG4_PINFUNC) == 0x1 &&
!(config3 & CONFIG3_THERM))
data->has_fan4 = 1;
} if (chip == adt7476 || chip == adt7490) { if (!(config3 & CONFIG3_THERM) ||
(data->config4 & CONFIG4_PINFUNC) == 0x1)
data->has_voltage |= (1 << 0); /* in0 */
}
/* * On the ADT7476, the +12V input pin may instead be used as VID5, * and VID pins may alternatively be used as GPIO
*/ if (chip == adt7476) {
u8 vid = adt7475_read(REG_VID); if (!(vid & VID_VIDSEL))
data->has_voltage |= (1 << 4); /* in4 */
/* Voltage attenuators can be bypassed, globally or individually */
data->config2 = adt7475_read(REG_CONFIG2);
ret = load_attenuators(client, chip, data); if (ret)
dev_warn(&client->dev, "Error configuring attenuator bypass\n");
/* * Call adt7475_read_pwm for all pwm's as this will reprogram any * pwm's which are disabled to manual mode with 0% duty cycle
*/ for (i = 0; i < ADT7475_PWM_COUNT; i++)
adt7475_read_pwm(client, i);
ret = adt7475_set_pwm_polarity(client); if (ret && ret != -EINVAL)
dev_warn(&client->dev, "Error configuring pwm polarity\n");
ret = adt7475_fan_pwm_config(client); if (ret)
dev_warn(&client->dev, "Error %d configuring fan/pwm\n", ret);
/* Start monitoring */ switch (chip) { case adt7475: case adt7476:
i2c_smbus_write_byte_data(client, REG_CONFIG1,
adt7475_read(REG_CONFIG1) | 0x01); break; default: break;
}
data->groups[group_num++] = &adt7475_attr_group;
/* Features that can be disabled individually */ if (data->has_fan4) {
data->groups[group_num++] = &fan4_attr_group;
} if (data->has_pwm2) {
data->groups[group_num++] = &pwm2_attr_group;
} if (data->has_voltage & (1 << 0)) {
data->groups[group_num++] = &in0_attr_group;
} if (data->has_voltage & (1 << 3)) {
data->groups[group_num++] = &in3_attr_group;
} if (data->has_voltage & (1 << 4)) {
data->groups[group_num++] = &in4_attr_group;
} if (data->has_voltage & (1 << 5)) {
data->groups[group_num++] = &in5_attr_group;
} if (data->has_voltage & (1 << 6)) {
data->groups[group_num++] = &in6_attr_group;
} if (data->has_vid) {
data->vrm = vid_which_vrm();
data->groups[group_num] = &vid_attr_group;
}
/* register device with all the acquired attributes */
hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
client->name, data,
data->groups);
if (IS_ERR(hwmon_dev)) {
ret = PTR_ERR(hwmon_dev); return ret;
}
/* * Figure out the internal value for pwmctrl and pwmchan * based on the current settings
*/
v = (data->pwm[CONTROL][index] >> 5) & 7;
if (v == 3)
data->pwmctl[index] = 0; elseif (v == 7)
data->pwmctl[index] = 1; elseif (v == 4) { /* * The fan is disabled - we don't want to * support that, so change to manual mode and * set the duty cycle to 0 instead
*/
data->pwm[INPUT][index] = 0;
data->pwm[CONTROL][index] &= ~0xE0;
data->pwm[CONTROL][index] |= (7 << 5);
ret = adt7475_read(REG_STATUS2); if (ret < 0) return ret;
data->alarms = ret << 8;
ret = adt7475_read(REG_STATUS1); if (ret < 0) return ret;
data->alarms |= ret;
ret = adt7475_read(REG_EXTEND2); if (ret < 0) return ret;
ext = (ret << 8);
ret = adt7475_read(REG_EXTEND1); if (ret < 0) return ret;
ext |= ret;
for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++) { if (!(data->has_voltage & (1 << i))) continue;
ret = adt7475_read(VOLTAGE_REG(i)); if (ret < 0) return ret;
data->voltage[INPUT][i] =
(ret << 2) |
((ext >> (i * 2)) & 3);
}
for (i = 0; i < ADT7475_TEMP_COUNT; i++) {
ret = adt7475_read(TEMP_REG(i)); if (ret < 0) return ret;
data->temp[INPUT][i] =
(ret << 2) |
((ext >> ((i + 5) * 2)) & 3);
}
if (data->has_voltage & (1 << 5)) {
ret = adt7475_read(REG_STATUS4); if (ret < 0) return ret;
data->alarms |= ret << 24;
ret = adt7475_read(REG_EXTEND3); if (ret < 0) return ret;
ext = ret;
ret = adt7475_read(REG_VTT); if (ret < 0) return ret;
data->voltage[INPUT][5] = ret << 2 |
((ext >> 4) & 3);
}
if (data->has_voltage & (1 << 6)) {
ret = adt7475_read(REG_STATUS4); if (ret < 0) return ret;
data->alarms |= ret << 24;
ret = adt7475_read(REG_EXTEND3); if (ret < 0) return ret;
ext = ret;
ret = adt7475_read(REG_IMON); if (ret < 0) return ret;
data->voltage[INPUT][6] = ret << 2 |
((ext >> 6) & 3);
}
for (i = 0; i < ADT7475_TACH_COUNT; i++) { if (i == 3 && !data->has_fan4) continue;
ret = adt7475_read_word(client, TACH_REG(i)); if (ret < 0) return ret;
data->tach[INPUT][i] = ret;
}
/* Updated by hw when in auto mode */ for (i = 0; i < ADT7475_PWM_COUNT; i++) { if (i == 1 && !data->has_pwm2) continue;
ret = adt7475_read(PWM_REG(i)); if (ret < 0) return ret;
data->pwm[INPUT][i] = ret;
}
if (data->has_vid) {
ret = adt7475_read(REG_VID); if (ret < 0) return ret;
data->vid = ret & 0x3f;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.