// SPDX-License-Identifier: GPL-2.0-or-later /* * Driver for Jedec 5118 compliant temperature sensors * * Derived from https://github.com/Steve-Tech/SPD5118-DKMS * Originally from T/2 driver at https://t2sde.org/packages/linux * Copyright (c) 2023 René Rebe, ExactCODE GmbH; Germany. * * Copyright (c) 2024 Guenter Roeck * * Inspired by ee1004.c and jc42.c. * * SPD5118 compliant temperature sensors are typically used on DDR5 * memory modules.
*/
/* Temperature unit in millicelsius */ #define SPD5118_TEMP_UNIT (MILLIDEGREE_PER_DEGREE / 4) /* Representable temperature range in millicelsius */ #define SPD5118_TEMP_RANGE_MIN -256000 #define SPD5118_TEMP_RANGE_MAX 255750
staticint spd5118_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{ struct regmap *regmap = dev_get_drvdata(dev);
if (type != hwmon_temp) return -EOPNOTSUPP;
switch (attr) { case hwmon_temp_input: case hwmon_temp_max: case hwmon_temp_min: case hwmon_temp_crit: case hwmon_temp_lcrit: return spd5118_read_temp(regmap, attr, val); case hwmon_temp_max_alarm: case hwmon_temp_min_alarm: case hwmon_temp_crit_alarm: case hwmon_temp_lcrit_alarm: return spd5118_read_alarm(regmap, attr, val); case hwmon_temp_enable: return spd5118_read_enable(regmap, val); default: return -EOPNOTSUPP;
}
}
staticint spd5118_write_temp(struct regmap *regmap, u32 attr, long val)
{
u8 regval[2];
u16 temp; int reg;
switch (attr) { case hwmon_temp_max:
reg = SPD5118_REG_TEMP_MAX; break; case hwmon_temp_min:
reg = SPD5118_REG_TEMP_MIN; break; case hwmon_temp_crit:
reg = SPD5118_REG_TEMP_CRIT; break; case hwmon_temp_lcrit:
reg = SPD5118_REG_TEMP_LCRIT; break; default: return -EOPNOTSUPP;
}
static umode_t spd5118_is_visible(constvoid *_data, enum hwmon_sensor_types type,
u32 attr, int channel)
{ if (type != hwmon_temp) return 0;
switch (attr) { case hwmon_temp_input: return 0444; case hwmon_temp_min: case hwmon_temp_max: case hwmon_temp_lcrit: case hwmon_temp_crit: case hwmon_temp_enable: return 0644; case hwmon_temp_min_alarm: case hwmon_temp_max_alarm: case hwmon_temp_crit_alarm: case hwmon_temp_lcrit_alarm: return 0444; default: return 0;
}
}
/* * Bank and vendor id are 8-bit fields with seven data bits and odd parity. * Vendor IDs 0 and 0x7f are invalid. * See Jedec standard JEP106BJ for details and a list of assigned vendor IDs.
*/ staticbool spd5118_vendor_valid(u8 bank, u8 id)
{ if (parity8(bank) == 0 || parity8(id) == 0) returnfalse;
staticbool spd5118_writeable_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case SPD5118_REG_I2C_LEGACY_MODE: case SPD5118_REG_TEMP_CLR: case SPD5118_REG_TEMP_CONFIG: case SPD5118_REG_TEMP_MAX: case SPD5118_REG_TEMP_MAX + 1: case SPD5118_REG_TEMP_MIN: case SPD5118_REG_TEMP_MIN + 1: case SPD5118_REG_TEMP_CRIT: case SPD5118_REG_TEMP_CRIT + 1: case SPD5118_REG_TEMP_LCRIT: case SPD5118_REG_TEMP_LCRIT + 1: returntrue; default: returnfalse;
}
}
staticbool spd5118_volatile_reg(struct device *dev, unsignedint reg)
{ switch (reg) { case SPD5118_REG_TEMP_CLR: case SPD5118_REG_ERROR_CLR: case SPD5118_REG_TEMP: case SPD5118_REG_TEMP + 1: case SPD5118_REG_TEMP_STATUS: returntrue; default: returnfalse;
}
}
/* * Make sure the configuration register in the regmap cache is current * before bypassing it.
*/
err = regmap_read(regmap, SPD5118_REG_TEMP_CONFIG, ®val); if (err < 0) return err;
/* * If the device type registers return 0, it is possible that the chip * has a non-zero page selected and takes the specification literally, * i.e. disables access to volatile registers besides the page register * if the page is not 0. The Renesas/ITD SPD5118 Hub Controller is known * to show this behavior. Try to identify such chips.
*/ if (!regval) { /* Vendor ID registers must also be 0 */
regval = i2c_smbus_read_word_data(client, SPD5118_REG_VENDOR); if (regval) return -ENODEV;
/* The selected page in MR11 must not be 0 */
mode = i2c_smbus_read_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE); if (mode < 0 || (mode & ~SPD5118_LEGACY_MODE_MASK) ||
!(mode & SPD5118_LEGACY_PAGE_MASK)) return -ENODEV;
/* * If the device type registers are still bad after selecting * page 0, this is not a SPD5118 device. Restore original * legacy mode register value and abort.
*/
regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE); if (regval != 0x5118) {
i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE, mode); return -ENODEV;
}
}
/* We are reasonably sure that this is really a SPD5118 hub controller */ return 0;
}
/* * 16-bit addressing note: * * If I2C_FUNC_I2C is not supported by an I2C adapter driver, regmap uses * SMBus operations as alternative. To simulate a read operation with a 16-bit * address, it writes the address using i2c_smbus_write_byte_data(), followed * by one or more calls to i2c_smbus_read_byte() to read the data. * Per spd5118 standard, a read operation after writing the address must start * with <Sr> (Repeat Start). However, a SMBus read byte operation starts with * <S> (Start). This resets the register address in the spd5118 chip. As result, * i2c_smbus_read_byte() always returns data from register address 0x00. * * A working alternative to access chips with 16-bit register addresses in the * absence of I2C_FUNC_I2C support is not known. * * For this reason, 16-bit addressing can only be supported with I2C if the * adapter supports I2C_FUNC_I2C. * * For I2C, the addressing mode selected by the BIOS must not be changed. * Experiments show that at least some PC BIOS versions will not change the * addressing mode on a soft reboot and end up in setup, claiming that some * configuration change happened. This will happen again after a power cycle, * which does reset the addressing mode. To prevent this from happening, * detect if 16-bit addressing is enabled and always use the currently * configured addressing mode.
*/
err = spd5118_i2c_init(client); if (err) return err;
mode = i2c_smbus_read_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE); if (mode < 0) return mode;
is_16bit = mode & SPD5118_LEGACY_MODE_ADDR; if (is_16bit) { /* * See 16-bit addressing note above explaining why it is * necessary to check for I2C_FUNC_I2C support here.
*/ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(dev, "Adapter does not support 16-bit register addresses\n"); return -ENODEV;
}
config = &spd5118_regmap16_config;
} else {
config = &spd5118_regmap8_config;
}
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.