Add support for SPD5118 (Jedec JESD300-5B.01) compliant temperature
sensors. Such sensors are typically found on DDR5 memory modules.
The first patch of the series adds SPD5118 devicetree bindings. The second
patch adds support for SPD5118 temperature sensors. The third patch adds
support for suspend/resume.
Note: The driver introduced with this patch series does not currently
support accessing the SPD5118 EEPROM, or accessing SPD5118 compatible chips
in I3C mode.
v2: Drop PEC support; it only applies to I3C mode.
Update documentation
Add suspend/resume support
----------------------------------------------------------------
Guenter Roeck (3):
dt-bindings: hwmon: jedec,spd5118: Add bindings
hwmon: Add support for SPD5118 compliant temperature sensors
hwmon: (spd5118) Add suspend/resume support
.../devicetree/bindings/hwmon/jedec,spd5118.yaml | 48 ++
Documentation/hwmon/index.rst | 1 +
Documentation/hwmon/spd5118.rst | 56 +++
drivers/hwmon/Kconfig | 12 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/spd5118.c | 510 +++++++++++++++++++++
6 files changed, 628 insertions(+)
create mode 100644 Documentation/devicetree/bindings/hwmon/jedec,spd5118.yaml
create mode 100644 Documentation/hwmon/spd5118.rst
create mode 100644 drivers/hwmon/spd5118.c
Add device tree bindings for the SPD hub present in DDR5 modules.
Signed-off-by: Guenter Roeck <[email protected]>
---
v2: Drop pec-enable property
.../bindings/hwmon/jedec,spd5118.yaml | 48 +++++++++++++++++++
1 file changed, 48 insertions(+)
create mode 100644 Documentation/devicetree/bindings/hwmon/jedec,spd5118.yaml
diff --git a/Documentation/devicetree/bindings/hwmon/jedec,spd5118.yaml b/Documentation/devicetree/bindings/hwmon/jedec,spd5118.yaml
new file mode 100644
index 000000000000..05c39f7083db
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/jedec,spd5118.yaml
@@ -0,0 +1,48 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/jedec,spd5118.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: JEDEC JESD300-5B (SPD5118) compatible DDR5 SPD hub
+
+maintainers:
+ - Guenter Roeck <[email protected]>
+
+description: |
+ JEDEC JESD300-5B.01 SPD5118 Hub and Serial Presence Detect
+ https://www.jedec.org/standards-documents/docs/jesd300-5b01
+
+select:
+ properties:
+ compatible:
+ const: jedec,spd5118
+
+ required:
+ - compatible
+ - reg
+
+properties:
+ compatible:
+ const: jedec,spd5118
+
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ temp-sensor@51 {
+ compatible = "jedec,spd5118";
+ reg = <0x51>;
+ };
+ };
--
2.39.2
Add suspend/resume support to ensure that limit and configuration
registers are updated and synchronized after a suspend/resume cycle.
Cc: Armin Wolf <[email protected]>
Signed-off-by: Guenter Roeck <[email protected]>
---
v2: New patch
RFT: I tested the patch through a suspend/resume cycle, and it seems
to work, but I am not sure if that had any effect because,
after all, the memory is still active during suspend/resume.
I was unable to test a hibernation cycle with my system.
drivers/hwmon/spd5118.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/drivers/hwmon/spd5118.c b/drivers/hwmon/spd5118.c
index 3c75964a0fba..ea807a77a4ee 100644
--- a/drivers/hwmon/spd5118.c
+++ b/drivers/hwmon/spd5118.c
@@ -20,6 +20,7 @@
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/module.h>
+#include <linux/pm.h>
#include <linux/regmap.h>
#include <linux/units.h>
@@ -432,6 +433,8 @@ static int spd5118_probe(struct i2c_client *client)
if (!spd5118_vendor_valid(bank, vendor))
return -ENODEV;
+ dev_set_drvdata(dev, regmap);
+
hwmon_dev = devm_hwmon_device_register_with_info(dev, "spd5118",
regmap, &spd5118_chip_info,
NULL);
@@ -449,6 +452,31 @@ static int spd5118_probe(struct i2c_client *client)
return 0;
}
+static int spd5118_suspend(struct device *dev)
+{
+ struct regmap *regmap = dev_get_drvdata(dev);
+
+ regcache_cache_bypass(regmap, true);
+ regmap_update_bits(regmap, SPD5118_REG_TEMP_CONFIG, SPD5118_TS_DISABLE,
+ SPD5118_TS_DISABLE);
+ regcache_cache_bypass(regmap, false);
+
+ regcache_cache_only(regmap, true);
+ regcache_mark_dirty(regmap);
+
+ return 0;
+}
+
+static int spd5118_resume(struct device *dev)
+{
+ struct regmap *regmap = dev_get_drvdata(dev);
+
+ regcache_cache_only(regmap, false);
+ return regcache_sync(regmap);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(spd5118_pm_ops, spd5118_suspend, spd5118_resume);
+
static const struct i2c_device_id spd5118_id[] = {
{ "spd5118", 0 },
{ }
@@ -466,6 +494,7 @@ static struct i2c_driver spd5118_driver = {
.driver = {
.name = "spd5118",
.of_match_table = spd5118_of_ids,
+ .pm = pm_sleep_ptr(&spd5118_pm_ops),
},
.probe = spd5118_probe,
.id_table = spd5118_id,
--
2.39.2
Add support for SPD5118 (Jedec JESD300-5B.01) compliant temperature
sensors. Such sensors are typically found on DDR5 memory modules.
Cc: René Rebe <[email protected]>
Cc: Thomas Weißschuh <[email protected]>
Reviewed-by: Thomas Weißschuh <[email protected]>
Tested-by: Thomas Weißschuh <[email protected]>
Signed-off-by: Guenter Roeck <[email protected]>
---
v2: Drop PEC property documentation
Add note indicating that alarm attributes are sticky until read
to documentation
Fix detect function
Fix misspelling in Makefile (CONFIG_SENSORS_SPD5118->CONFIG_SENSORS_SPD5118)
Documentation/hwmon/index.rst | 1 +
Documentation/hwmon/spd5118.rst | 56 ++++
drivers/hwmon/Kconfig | 12 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/spd5118.c | 481 ++++++++++++++++++++++++++++++++
5 files changed, 551 insertions(+)
create mode 100644 Documentation/hwmon/spd5118.rst
create mode 100644 drivers/hwmon/spd5118.c
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index 03d313af469a..6e7b8726b60c 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -215,6 +215,7 @@ Hardware Monitoring Kernel Drivers
smsc47m192
smsc47m1
sparx5-temp
+ spd5118
stpddc60
surface_fan
sy7636a-hwmon
diff --git a/Documentation/hwmon/spd5118.rst b/Documentation/hwmon/spd5118.rst
new file mode 100644
index 000000000000..5bf7b7e7ca67
--- /dev/null
+++ b/Documentation/hwmon/spd5118.rst
@@ -0,0 +1,56 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+Kernel driver spd5118
+=====================
+
+Supported chips:
+
+ * SPD5118 (JEDEC JESD300-5B.01) compliant temperature sensor chips
+
+ JEDEC standard download:
+ https://www.jedec.org/standards-documents/docs/jesd300-5b01
+ (account required)
+
+
+ Prefix: 'spd5118'
+
+ Addresses scanned: I2C 0x50 - 0x57
+
+Author:
+ Guenter Roeck <[email protected]>
+
+
+Description
+-----------
+
+This driver implements support for SPD5118 (JEDEC JESD300-5B.01) compliant
+temperature sensors, which are used on many DDR5 memory modules. Some systems
+use the sensor to prevent memory overheating by automatically throttling
+the memory controller.
+
+The driver auto-detects SPD5118 compliant chips, but can also be instantiated
+using devicetree/firmware nodes.
+
+A SPD5118 compliant chip supports a single temperature sensor. Critical minimum,
+minimum, maximum, and critical temperature can be configured. There are alarms
+for low critical, low, high, and critical thresholds.
+
+
+Hardware monitoring sysfs entries
+---------------------------------
+
+======================= ==================================
+temp1_input Temperature (RO)
+temp1_lcrit Low critical high temperature (RW)
+temp1_min Minimum temperature (RW)
+temp1_max Maximum temperature (RW)
+temp1_crit Critical high temperature (RW)
+
+temp1_lcrit_alarm Temperature low critical alarm
+temp1_min_alarm Temperature low alarm
+temp1_max_alarm Temperature high alarm
+temp1_crit_alarm Temperature critical alarm
+======================= ==================================
+
+Alarm attributes are sticky until read and will be cleared afterwards
+unless the alarm condition still applies.
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index e14ae18a973b..d0fb5fe1b2ac 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -2181,6 +2181,18 @@ config SENSORS_INA3221
This driver can also be built as a module. If so, the module
will be called ina3221.
+config SENSORS_SPD5118
+ tristate "SPD5118 Compliant Temperature Sensors"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for SPD5118 (JEDEC JESD300-5B)
+ compliant temperature sensors. Such sensors are found on DDR5 memory
+ modules.
+
+ This driver can also be built as a module. If so, the module
+ will be called spd5118.
+
config SENSORS_TC74
tristate "Microchip TC74"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index e3f25475d1f0..6574ca67d761 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -207,6 +207,7 @@ obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
obj-$(CONFIG_SENSORS_SPARX5) += sparx5-temp.o
+obj-$(CONFIG_SENSORS_SPD5118) += spd5118.o
obj-$(CONFIG_SENSORS_STTS751) += stts751.o
obj-$(CONFIG_SENSORS_SURFACE_FAN)+= surface_fan.o
obj-$(CONFIG_SENSORS_SY7636A) += sy7636a-hwmon.o
diff --git a/drivers/hwmon/spd5118.c b/drivers/hwmon/spd5118.c
new file mode 100644
index 000000000000..3c75964a0fba
--- /dev/null
+++ b/drivers/hwmon/spd5118.c
@@ -0,0 +1,481 @@
+// 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.
+ */
+
+#include <linux/bitops.h>
+#include <linux/bits.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/units.h>
+
+/* Addresses to scan */
+static const unsigned short normal_i2c[] = {
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, I2C_CLIENT_END };
+
+/* SPD5118 registers. */
+#define SPD5118_REG_TYPE 0x00 /* MR0:MR1 */
+#define SPD5118_REG_REVISION 0x02 /* MR2 */
+#define SPD5118_REG_VENDOR 0x03 /* MR3:MR4 */
+#define SPD5118_REG_CAPABILITY 0x05 /* MR5 */
+#define SPD5118_REG_I2C_LEGACY_MODE 0x0B /* MR11 */
+#define SPD5118_REG_TEMP_CLR 0x13 /* MR19 */
+#define SPD5118_REG_ERROR_CLR 0x14 /* MR20 */
+#define SPD5118_REG_TEMP_CONFIG 0x1A /* MR26 */
+#define SPD5118_REG_TEMP_MAX 0x1c /* MR28:MR29 */
+#define SPD5118_REG_TEMP_MIN 0x1e /* MR30:MR31 */
+#define SPD5118_REG_TEMP_CRIT 0x20 /* MR32:MR33 */
+#define SPD5118_REG_TEMP_LCRIT 0x22 /* MR34:MR35 */
+#define SPD5118_REG_TEMP 0x31 /* MR49:MR50 */
+#define SPD5118_REG_TEMP_STATUS 0x33 /* MR51 */
+
+#define SPD5118_TEMP_STATUS_HIGH BIT(0)
+#define SPD5118_TEMP_STATUS_LOW BIT(1)
+#define SPD5118_TEMP_STATUS_CRIT BIT(2)
+#define SPD5118_TEMP_STATUS_LCRIT BIT(3)
+
+#define SPD5118_CAP_TS_SUPPORT BIT(1) /* temperature sensor support */
+
+#define SPD5118_TS_DISABLE BIT(0) /* temperature sensor disable */
+
+/* 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
+
+static int spd5118_temp_from_reg(u16 reg)
+{
+ int temp = sign_extend32(reg >> 2, 10);
+
+ return temp * SPD5118_TEMP_UNIT;
+}
+
+static u16 spd5118_temp_to_reg(long temp)
+{
+ temp = clamp_val(temp, SPD5118_TEMP_RANGE_MIN, SPD5118_TEMP_RANGE_MAX);
+ return (DIV_ROUND_CLOSEST(temp, SPD5118_TEMP_UNIT) & 0x7ff) << 2;
+}
+
+static int spd5118_read_temp(struct regmap *regmap, u32 attr, long *val)
+{
+ int reg, err;
+ u8 regval[2];
+ u16 temp;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ reg = SPD5118_REG_TEMP;
+ break;
+ 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;
+ }
+
+ err = regmap_bulk_read(regmap, reg, regval, 2);
+ if (err)
+ return err;
+
+ temp = (regval[1] << 8) | regval[0];
+
+ *val = spd5118_temp_from_reg(temp);
+ return 0;
+}
+
+static int spd5118_read_alarm(struct regmap *regmap, u32 attr, long *val)
+{
+ unsigned int mask, regval;
+ int err;
+
+ switch (attr) {
+ case hwmon_temp_max_alarm:
+ mask = SPD5118_TEMP_STATUS_HIGH;
+ break;
+ case hwmon_temp_min_alarm:
+ mask = SPD5118_TEMP_STATUS_LOW;
+ break;
+ case hwmon_temp_crit_alarm:
+ mask = SPD5118_TEMP_STATUS_CRIT;
+ break;
+ case hwmon_temp_lcrit_alarm:
+ mask = SPD5118_TEMP_STATUS_LCRIT;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ err = regmap_read(regmap, SPD5118_REG_TEMP_STATUS, ®val);
+ if (err < 0)
+ return err;
+ *val = !!(regval & mask);
+ if (*val)
+ return regmap_write(regmap, SPD5118_REG_TEMP_CLR, mask);
+ return 0;
+}
+
+static int spd5118_read_enable(struct regmap *regmap, u32 attr, long *val)
+{
+ u32 regval;
+ int err;
+
+ err = regmap_read(regmap, SPD5118_REG_TEMP_CONFIG, ®val);
+ if (err < 0)
+ return err;
+ *val = !(regval & SPD5118_TS_DISABLE);
+ return 0;
+}
+
+static int 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, attr, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int 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;
+ }
+
+ temp = spd5118_temp_to_reg(val);
+ regval[0] = temp & 0xff;
+ regval[1] = temp >> 8;
+
+ return regmap_bulk_write(regmap, reg, regval, 2);
+}
+
+static int spd5118_write_enable(struct regmap *regmap, u32 attr, long val)
+{
+ if (val && val != 1)
+ return -EINVAL;
+
+ return regmap_update_bits(regmap, SPD5118_REG_TEMP_CONFIG,
+ SPD5118_TS_DISABLE,
+ val ? 0 : SPD5118_TS_DISABLE);
+}
+
+static int spd5118_temp_write(struct regmap *regmap, u32 attr, long val)
+{
+ switch (attr) {
+ case hwmon_temp_max:
+ case hwmon_temp_min:
+ case hwmon_temp_crit:
+ case hwmon_temp_lcrit:
+ return spd5118_write_temp(regmap, attr, val);
+ case hwmon_temp_enable:
+ return spd5118_write_enable(regmap, attr, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int spd5118_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ struct regmap *regmap = dev_get_drvdata(dev);
+
+ switch (type) {
+ case hwmon_temp:
+ return spd5118_temp_write(regmap, attr, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t spd5118_is_visible(const void *_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;
+ }
+}
+
+static inline bool spd5118_parity8(u8 w)
+{
+ w ^= w >> 4;
+ return (0x6996 >> (w & 0xf)) & 1;
+}
+
+/*
+ * 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.
+ */
+static bool spd5118_vendor_valid(u8 bank, u8 id)
+{
+ if (!spd5118_parity8(bank) || !spd5118_parity8(id))
+ return false;
+
+ id &= 0x7f;
+ return id && id != 0x7f;
+}
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int spd5118_detect(struct i2c_client *client, struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int regval;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
+ if (regval != 0x5118)
+ return -ENODEV;
+
+ regval = i2c_smbus_read_word_data(client, SPD5118_REG_VENDOR);
+ if (regval < 0 || !spd5118_vendor_valid(regval & 0xff, regval >> 8))
+ return -ENODEV;
+
+ regval = i2c_smbus_read_byte_data(client, SPD5118_REG_CAPABILITY);
+ if (regval < 0)
+ return -ENODEV;
+ if (!(regval & SPD5118_CAP_TS_SUPPORT) || (regval & 0xfc))
+ return -ENODEV;
+
+ regval = i2c_smbus_read_byte_data(client, SPD5118_REG_TEMP_CLR);
+ if (regval)
+ return -ENODEV;
+ regval = i2c_smbus_read_byte_data(client, SPD5118_REG_ERROR_CLR);
+ if (regval)
+ return -ENODEV;
+
+ regval = i2c_smbus_read_byte_data(client, SPD5118_REG_REVISION);
+ if (regval < 0 || (regval & 0xc1))
+ return -ENODEV;
+
+ regval = i2c_smbus_read_byte_data(client, SPD5118_REG_TEMP_CONFIG);
+ if (regval < 0)
+ return -ENODEV;
+ if (regval & ~SPD5118_TS_DISABLE)
+ return -ENODEV;
+
+ strscpy(info->type, "spd5118", I2C_NAME_SIZE);
+ return 0;
+}
+
+static const struct hwmon_channel_info *spd5118_info[] = {
+ HWMON_CHANNEL_INFO(chip,
+ HWMON_C_REGISTER_TZ),
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT |
+ HWMON_T_LCRIT | HWMON_T_LCRIT_ALARM |
+ HWMON_T_MIN | HWMON_T_MIN_ALARM |
+ HWMON_T_MAX | HWMON_T_MAX_ALARM |
+ HWMON_T_CRIT | HWMON_T_CRIT_ALARM |
+ HWMON_T_ENABLE),
+ NULL
+};
+
+static const struct hwmon_ops spd5118_hwmon_ops = {
+ .is_visible = spd5118_is_visible,
+ .read = spd5118_read,
+ .write = spd5118_write,
+};
+
+static const struct hwmon_chip_info spd5118_chip_info = {
+ .ops = &spd5118_hwmon_ops,
+ .info = spd5118_info,
+};
+
+static bool spd5118_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ 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:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool spd5118_volatile_reg(struct device *dev, unsigned int 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:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config spd5118_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = SPD5118_REG_TEMP_STATUS,
+ .writeable_reg = spd5118_writeable_reg,
+ .volatile_reg = spd5118_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static int spd5118_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ unsigned int regval, revision, vendor, bank;
+ struct device *hwmon_dev;
+ struct regmap *regmap;
+ int err;
+
+ regmap = devm_regmap_init_i2c(client, &spd5118_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n");
+
+ err = regmap_read(regmap, SPD5118_REG_CAPABILITY, ®val);
+ if (err)
+ return err;
+ if (!(regval & SPD5118_CAP_TS_SUPPORT))
+ return -ENODEV;
+
+ err = regmap_read(regmap, SPD5118_REG_REVISION, &revision);
+ if (err)
+ return err;
+
+ err = regmap_read(regmap, SPD5118_REG_VENDOR, &bank);
+ if (err)
+ return err;
+ err = regmap_read(regmap, SPD5118_REG_VENDOR + 1, &vendor);
+ if (err)
+ return err;
+ if (!spd5118_vendor_valid(bank, vendor))
+ return -ENODEV;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, "spd5118",
+ regmap, &spd5118_chip_info,
+ NULL);
+ if (IS_ERR(hwmon_dev))
+ return PTR_ERR(hwmon_dev);
+
+ /*
+ * From JESD300-5B
+ * MR2 bits [5:4]: Major revision, 1..4
+ * MR2 bits [3:1]: Minor revision, 0..8? Probably a typo, assume 1..8
+ */
+ dev_info(dev, "DDR5 temperature sensor: vendor 0x%02x:0x%02x revision %d.%d\n",
+ bank & 0x7f, vendor, ((revision >> 4) & 0x03) + 1, ((revision >> 1) & 0x07) + 1);
+
+ return 0;
+}
+
+static const struct i2c_device_id spd5118_id[] = {
+ { "spd5118", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, spd5118_id);
+
+static const struct of_device_id spd5118_of_ids[] = {
+ { .compatible = "jedec,spd5118", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, spd5118_of_ids);
+
+static struct i2c_driver spd5118_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "spd5118",
+ .of_match_table = spd5118_of_ids,
+ },
+ .probe = spd5118_probe,
+ .id_table = spd5118_id,
+ .detect = spd5118_detect,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(spd5118_driver);
+
+MODULE_AUTHOR("René Rebe <[email protected]>");
+MODULE_AUTHOR("Guenter Roeck <[email protected]>");
+MODULE_DESCRIPTION("SPD 5118 driver");
+MODULE_LICENSE("GPL");
--
2.39.2
On 5/30/24 15:39, Guenter Roeck wrote:
> Add support for SPD5118 (Jedec JESD300-5B.01) compliant temperature
> sensors. Such sensors are typically found on DDR5 memory modules.
>
> The first patch of the series adds SPD5118 devicetree bindings. The second
> patch adds support for SPD5118 temperature sensors. The third patch adds
> support for suspend/resume.
>
> Note: The driver introduced with this patch series does not currently
> support accessing the SPD5118 EEPROM, or accessing SPD5118 compatible chips
> in I3C mode.
>
> v2: Drop PEC support; it only applies to I3C mode.
> Update documentation
> Add suspend/resume support
>
> ----------------------------------------------------------------
> Guenter Roeck (3):
> dt-bindings: hwmon: jedec,spd5118: Add bindings
> hwmon: Add support for SPD5118 compliant temperature sensors
> hwmon: (spd5118) Add suspend/resume support
>
> .../devicetree/bindings/hwmon/jedec,spd5118.yaml | 48 ++
> Documentation/hwmon/index.rst | 1 +
> Documentation/hwmon/spd5118.rst | 56 +++
> drivers/hwmon/Kconfig | 12 +
> drivers/hwmon/Makefile | 1 +
> drivers/hwmon/spd5118.c | 510 +++++++++++++++++++++
> 6 files changed, 628 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/hwmon/jedec,spd5118.yaml
> create mode 100644 Documentation/hwmon/spd5118.rst
> create mode 100644 drivers/hwmon/spd5118.c
Obviously this should have been patch 0/3. Sory.
Guenter
On 31/05/2024 00:39, Guenter Roeck wrote:
> Add device tree bindings for the SPD hub present in DDR5 modules.
>
> Signed-off-by: Guenter Roeck <[email protected]>
..
> +title: JEDEC JESD300-5B (SPD5118) compatible DDR5 SPD hub
> +
> +maintainers:
> + - Guenter Roeck <[email protected]>
> +
> +description: |
> + JEDEC JESD300-5B.01 SPD5118 Hub and Serial Presence Detect
> + https://www.jedec.org/standards-documents/docs/jesd300-5b01
> +
> +select:
> + properties:
> + compatible:
> + const: jedec,spd5118
Why do you need the select? This s needed for cases with multiple
bindings using parts of compatible list. I don't see the case here so far.
Best regards,
Krzysztof
On 5/31/24 01:15, Krzysztof Kozlowski wrote:
> On 31/05/2024 00:39, Guenter Roeck wrote:
>> Add device tree bindings for the SPD hub present in DDR5 modules.
>>
>> Signed-off-by: Guenter Roeck <[email protected]>
>
> ...
>
>> +title: JEDEC JESD300-5B (SPD5118) compatible DDR5 SPD hub
>> +
>> +maintainers:
>> + - Guenter Roeck <[email protected]>
>> +
>> +description: |
>> + JEDEC JESD300-5B.01 SPD5118 Hub and Serial Presence Detect
>> + https://www.jedec.org/standards-documents/docs/jesd300-5b01
>> +
>> +select:
>> + properties:
>> + compatible:
>> + const: jedec,spd5118
>
> Why do you need the select? This s needed for cases with multiple
> bindings using parts of compatible list. I don't see the case here so far.
>
Sorry, I am not a devicetree expert, I don't really know what I am doing,
and just copied this from some other binding. I'll try to find a better
example. Actually, I'll just add the binding to trivial devices.
Thanks,
Guenter
On 31/05/2024 15:09, Guenter Roeck wrote:
> On 5/31/24 01:15, Krzysztof Kozlowski wrote:
>> On 31/05/2024 00:39, Guenter Roeck wrote:
>>> Add device tree bindings for the SPD hub present in DDR5 modules.
>>>
>>> Signed-off-by: Guenter Roeck <[email protected]>
>>
>> ...
>>
>>> +title: JEDEC JESD300-5B (SPD5118) compatible DDR5 SPD hub
>>> +
>>> +maintainers:
>>> + - Guenter Roeck <[email protected]>
>>> +
>>> +description: |
>>> + JEDEC JESD300-5B.01 SPD5118 Hub and Serial Presence Detect
>>> + https://www.jedec.org/standards-documents/docs/jesd300-5b01
>>> +
>>> +select:
>>> + properties:
>>> + compatible:
>>> + const: jedec,spd5118
>>
>> Why do you need the select? This s needed for cases with multiple
>> bindings using parts of compatible list. I don't see the case here so far.
>>
>
> Sorry, I am not a devicetree expert, I don't really know what I am doing,
> and just copied this from some other binding. I'll try to find a better
> example. Actually, I'll just add the binding to trivial devices.
Sounds good.
Best regards,
Krzysztof