// 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>
*/
staticint amc6821_temp_read(struct device *dev, u32 attr, int channel, long *val)
{ struct amc6821_data *data = dev_get_drvdata(dev);
switch (attr) { case hwmon_temp_input: case hwmon_temp_min: case hwmon_temp_max: case hwmon_temp_crit: return amc6821_temp_read_values(data->regmap, attr, channel, val); case hwmon_temp_min_alarm: case hwmon_temp_max_alarm: case hwmon_temp_crit_alarm: case hwmon_temp_fault: return amc6821_read_alarms(data->regmap, hwmon_temp, attr, channel, val); default: return -EOPNOTSUPP;
}
}
staticint amc6821_temp_write(struct device *dev, u32 attr, int channel, long val)
{ struct amc6821_data *data = dev_get_drvdata(dev); int reg;
val = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000);
static ssize_t temp_auto_point_temp_store(struct device *dev, struct device_attribute *attr, constchar *buf, size_t count)
{ struct amc6821_data *data = dev_get_drvdata(dev); int ix = to_sensor_dev_attr_2(attr)->index; int nr = to_sensor_dev_attr_2(attr)->nr; struct regmap *regmap = data->regmap;
u8 temps[3], otemps[3]; long val; int ret;
ret = kstrtol(buf, 10, &val); if (ret) return ret;
mutex_lock(&data->update_lock);
ret = amc6821_get_auto_point_temps(data->regmap, nr, temps); if (ret) goto unlock;
switch (ix) { case 0: /* * Passive cooling temperature. Range limit against low limit * of both channels.
*/
ret = amc6821_get_auto_point_temps(data->regmap, 1 - nr, otemps); if (ret) goto unlock;
val = DIV_ROUND_CLOSEST(clamp_val(val, 0, 63000), 1000);
val = clamp_val(val, 0, min(temps[1], otemps[1]));
ret = regmap_write(regmap, AMC6821_REG_PSV_TEMP, val); break; case 1: /* * Low limit; must be between passive and high limit, * 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; case 2: /* 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; default:
ret = -EINVAL; break;
}
unlock:
mutex_unlock(&data->update_lock); return ret ? : count;
}
ret = kstrtou8(buf, 10, &val); if (ret) return ret;
mutex_lock(&data->update_lock);
ret = regmap_write(regmap, AMC6821_REG_DCY_LOW_TEMP, val); if (ret) goto unlock;
for (i = 0; i < 2; i++) {
u8 temps[3];
ret = amc6821_get_auto_point_temps(regmap, i, temps); if (ret) break;
ret = set_slope_register(regmap, i, temps); if (ret) break;
}
unlock:
mutex_unlock(&data->update_lock); return ret ? : count;
}
staticint amc6821_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{ switch (type) { case hwmon_temp: return amc6821_temp_read(dev, attr, channel, val); case hwmon_fan: return amc6821_fan_read(dev, attr, val); case hwmon_pwm: return amc6821_pwm_read(dev, attr, val); default: return -EOPNOTSUPP;
}
}
staticint amc6821_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{ switch (type) { case hwmon_temp: return amc6821_temp_write(dev, attr, channel, val); case hwmon_fan: return amc6821_fan_write(dev, attr, val); case hwmon_pwm: return amc6821_pwm_write(dev, attr, val); default: return -EOPNOTSUPP;
}
}
static umode_t amc6821_is_visible(constvoid *data, enum hwmon_sensor_types type,
u32 attr, int channel)
{ switch (type) { case hwmon_temp: switch (attr) { case hwmon_temp_input: case hwmon_temp_min_alarm: case hwmon_temp_max_alarm: case hwmon_temp_crit_alarm: case hwmon_temp_fault: return 0444; case hwmon_temp_min: case hwmon_temp_max: case hwmon_temp_crit: return 0644; default: return 0;
} case hwmon_fan: switch (attr) { case hwmon_fan_input: case hwmon_fan_fault: return 0444; case hwmon_fan_pulses: case hwmon_fan_min: case hwmon_fan_max: case hwmon_fan_target: return 0644; default: return 0;
} case hwmon_pwm: switch (attr) { case hwmon_pwm_mode: case hwmon_pwm_enable: case hwmon_pwm_input: return 0644; case hwmon_pwm_auto_channels_temp: return 0444; default: return 0;
} default: return 0;
}
}
/* * Bit 7 of the address register is ignored, so we can check the * ID registers again
*/
dev_id = i2c_smbus_read_byte_data(client, 0x80 | AMC6821_REG_DEV_ID);
comp_id = i2c_smbus_read_byte_data(client, 0x80 | AMC6821_REG_COMP_ID); if (dev_id != 0x21 || comp_id != 0x49) {
dev_dbg(&adapter->dev, "amc6821: detection failed at 0x%02x.\n",
address); return -ENODEV;
}
dev_info(&adapter->dev, "amc6821: chip found at 0x%02x.\n", address);
strscpy(info->type, "amc6821", I2C_NAME_SIZE);
/* * For backward compatibility, the pwminv module parameter takes * always the precedence over any other device description
*/ if (pwminv == 0) return PWM_POLARITY_NORMAL; if (pwminv > 0) return PWM_POLARITY_INVERSED;
if (of_parse_phandle_with_args(fan_np, "pwms", "#pwm-cells", 0, &args)) goto out;
of_node_put(args.np);
/* Software DCY-control mode with fan enabled when cooling-levels present */ if (data->fan_cooling_levels) {
err = amc6821_set_sw_dcy(data,
data->fan_cooling_levels[data->fan_max_state]); if (err) return err;
}
} return 0;
}
staticbool amc6821_volatile_reg(struct device *dev, unsignedint 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: returntrue; default: returnfalse;
}
}
data = devm_kzalloc(dev, sizeof(struct amc6821_data), GFP_KERNEL); if (!data) return -ENOMEM;
regmap = devm_regmap_init_i2c(client, &amc6821_regmap_config); if (IS_ERR(regmap)) return dev_err_probe(dev, PTR_ERR(regmap), "Failed to initialize regmap\n");
data->regmap = regmap;
fan_np = of_get_child_by_name(dev->of_node, "fan"); if (fan_np) {
err = amc6821_of_fan_read_data(client, data, fan_np); if (err) return dev_err_probe(dev, err, "Failed to read fan device tree data\n");
}
err = amc6821_init_client(client, data); if (err) return err;
if (of_device_is_compatible(dev->of_node, "tsd,mule")) {
err = devm_of_platform_populate(dev); if (err) return dev_err_probe(dev, err, "Failed to create sub-devices\n");
}
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
data, &amc6821_chip_info,
amc6821_groups); if (IS_ERR(hwmon_dev)) return dev_err_probe(dev, PTR_ERR(hwmon_dev), "Failed to initialize hwmon\n");
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.