@@ -2309,12 +2309,15 @@ config SENSORS_INA3221
config SENSORS_SPD5118
tristate "SPD5118 Compliant Temperature Sensors"
- depends on I2C
+ depends on I3C_OR_I2C
select REGMAP_I2C
+ select REGMAP_I3C if I3C
help
If you say yes here you get support for SPD5118 (JEDEC JESD300)
- compliant temperature sensors. Such sensors are found on DDR5 memory
- modules.
+ compliant temperature sensors using I2C or I3C bus interface.
+ Such sensors are found on DDR5 memory modules.
+
+ This driver supports both I2C and I3C interfaces.
This driver can also be built as a module. If so, the module
will be called spd5118.
@@ -18,6 +18,7 @@
#include <linux/bits.h>
#include <linux/err.h>
#include <linux/i2c.h>
+#include <linux/i3c/device.h>
#include <linux/hwmon.h>
#include <linux/module.h>
#include <linux/mutex.h>
@@ -464,6 +465,27 @@ static const struct regmap_config spd5118_regmap8_config = {
.num_ranges = ARRAY_SIZE(spd5118_i2c_regmap_range_cfg),
};
+/*
+ * SPD5118 2-byte register address format (JESD300-5, Tables 7 & 20):
+ * Byte 1 (on wire first): MemReg | BlkAddr[0] | Address[5:0]
+ * Byte 2 (on wire second): 0000 | BlkAddr[4:1]
+ *
+ * The address byte (with MemReg and lower address bits) must be sent first,
+ * followed by the upper block address byte. With regmap 16-bit register
+ * format, this maps to little-endian: the low byte of the 16-bit value is
+ * transmitted first. No range config is needed since I3C does not use MR11
+ * page switching.
+ */
+static const struct regmap_config spd5118_regmap_i3c_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = 0x7ff,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .writeable_reg = spd5118_writeable_reg,
+ .volatile_reg = spd5118_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+
static int spd5118_suspend(struct device *dev)
{
struct spd5118_data *data = dev_get_drvdata(dev);
@@ -701,7 +723,39 @@ static struct i2c_driver spd5118_i2c_driver = {
.address_list = IS_ENABLED(CONFIG_SENSORS_SPD5118_DETECT) ? normal_i2c : NULL,
};
-module_i2c_driver(spd5118_i2c_driver);
+/* I3C */
+
+static int spd5118_i3c_probe(struct i3c_device *i3cdev)
+{
+ struct device *dev = i3cdev_to_dev(i3cdev);
+ struct regmap *regmap;
+ u8 regval[2];
+ int err;
+
+ regmap = devm_regmap_init_i3c(i3cdev, &spd5118_regmap_i3c_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n");
+
+ err = regmap_bulk_read(regmap, SPD5118_REG_TYPE, regval, 2);
+ if (err)
+ return err;
+
+ if (regval[0] != 0x51 || regval[1] != 0x18)
+ return -ENODEV;
+
+ return spd5118_common_probe(dev, regmap);
+}
+
+static struct i3c_driver spd5118_i3c_driver = {
+ .driver = {
+ .name = "spd5118_i3c",
+ .of_match_table = spd5118_of_ids,
+ .pm = pm_sleep_ptr(&spd5118_pm_ops),
+ },
+ .probe = spd5118_i3c_probe,
+};
+
+module_i3c_i2c_driver(spd5118_i3c_driver, &spd5118_i2c_driver);
MODULE_AUTHOR("René Rebe <rene@exactcode.de>");
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");