1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * tsi.c - AMD SBTSI I2C/I3C core driver. Probes the SBTSI device over I2C/I3C 4 * and publishes an auxiliary device on the auxiliary bus. 5 * 6 * Copyright (C) 2026 Advanced Micro Devices, Inc. 7 */ 8 9 #include <linux/auxiliary_bus.h> 10 #include <linux/bitfield.h> 11 #include <linux/module.h> 12 #include <linux/of.h> 13 #include <linux/slab.h> 14 #include "tsi-core.h" 15 16 #define SBTSI_REG_CONFIG 0x03 /* RO */ 17 18 /* 19 * Bit for reporting value with temperature measurement range. 20 * bit == 0: Use default temperature range (0C to 255.875C). 21 * bit == 1: Use extended temperature range (-49C to +206.875C). 22 */ 23 #define SBTSI_CONFIG_EXT_RANGE_SHIFT 2 24 25 /* 26 * ReadOrder bit specifies the reading order of integer and decimal part of 27 * CPU temperature for atomic reads. If bit == 0, reading integer part triggers 28 * latching of the decimal part, so integer part should be read first. 29 */ 30 #define SBTSI_CONFIG_READ_ORDER_SHIFT 5 31 32 static void sbtsi_adev_release(struct device *dev) 33 { 34 kfree(to_auxiliary_dev(dev)); 35 } 36 37 static void sbtsi_unregister_hwmon_adev(void *_adev) 38 { 39 struct auxiliary_device *adev = _adev; 40 41 auxiliary_device_delete(adev); 42 auxiliary_device_uninit(adev); 43 } 44 45 static void sbtsi_misc_unregister(void *arg) 46 { 47 struct sbtsi_data *data = arg; 48 49 misc_deregister(&data->sbtsi_misc_dev); 50 51 guard(sbtsi)(data); 52 data->detached = true; 53 } 54 55 static void sbtsi_driver_unref(void *arg) 56 { 57 struct sbtsi_data *data = arg; 58 59 kref_put(&data->kref, sbtsi_data_release); 60 } 61 62 /* 63 * Create and publish an auxiliary device. The hwmon driver in 64 * drivers/hwmon/sbtsi_temp.c binds to this device. 65 * 66 * @dev: I2C device (parent of the auxiliary device) 67 * @dev_addr: I2C address — used as the auxiliary device instance ID so that 68 * each socket gets a unique name. 69 */ 70 static int sbtsi_create_hwmon_adev(struct device *dev, u8 dev_addr) 71 { 72 struct auxiliary_device *adev; 73 int ret; 74 75 adev = kzalloc_obj(*adev); 76 if (!adev) 77 return -ENOMEM; 78 79 adev->name = AMD_SBTSI_AUX_HWMON; 80 adev->id = dev_addr; 81 adev->dev.parent = dev; 82 adev->dev.release = sbtsi_adev_release; 83 84 ret = auxiliary_device_init(adev); 85 if (ret) { 86 kfree(adev); 87 return ret; 88 } 89 90 ret = __auxiliary_device_add(adev, AMD_SBTSI_ADEV); 91 if (ret) { 92 auxiliary_device_uninit(adev); 93 return ret; 94 } 95 96 return devm_add_action_or_reset(dev, sbtsi_unregister_hwmon_adev, adev); 97 } 98 99 static int sbtsi_probe_common(struct device *dev, struct sbtsi_data *data) 100 { 101 u8 val; 102 int err; 103 104 mutex_init(&data->lock); 105 kref_init(&data->kref); 106 107 err = devm_add_action_or_reset(dev, sbtsi_driver_unref, data); 108 if (err) 109 return err; 110 111 err = sbtsi_xfer(data, SBTSI_REG_CONFIG, &val, true); 112 if (err) 113 return err; 114 115 data->ext_range_mode = FIELD_GET(BIT(SBTSI_CONFIG_EXT_RANGE_SHIFT), val); 116 data->read_order = FIELD_GET(BIT(SBTSI_CONFIG_READ_ORDER_SHIFT), val); 117 118 dev_set_drvdata(dev, data); 119 err = sbtsi_create_hwmon_adev(dev, data->dev_addr); 120 if (err < 0) 121 return err; 122 123 err = create_misc_tsi_device(data, dev); 124 if (err) 125 return err; 126 127 return devm_add_action_or_reset(dev, sbtsi_misc_unregister, data); 128 } 129 130 static int sbtsi_i2c_probe(struct i2c_client *client) 131 { 132 struct device *dev = &client->dev; 133 struct sbtsi_data *data; 134 135 data = kzalloc_obj(*data); 136 if (!data) 137 return -ENOMEM; 138 139 data->is_i3c = false; 140 data->client = client; 141 142 /* In a multi-socket system, devices that are otherwise identical do not 143 * share the same static address; each instance resides at a unique I2C 144 * client address on the same or different bus. Use the I2C client 145 * address as the auxiliary device instance ID to ensure each socket 146 * receives a distinct auxiliary device name. 147 */ 148 data->dev_addr = client->addr; 149 return sbtsi_probe_common(dev, data); 150 } 151 152 static const struct i2c_device_id sbtsi_id[] = { 153 { .name = "sbtsi" }, 154 { } 155 }; 156 MODULE_DEVICE_TABLE(i2c, sbtsi_id); 157 158 static const struct of_device_id __maybe_unused sbtsi_of_match[] = { 159 { 160 .compatible = "amd,sbtsi", 161 }, 162 { }, 163 }; 164 MODULE_DEVICE_TABLE(of, sbtsi_of_match); 165 166 static struct i2c_driver sbtsi_driver = { 167 .driver = { 168 .name = "sbtsi-i2c", 169 .of_match_table = of_match_ptr(sbtsi_of_match), 170 }, 171 .probe = sbtsi_i2c_probe, 172 .id_table = sbtsi_id, 173 }; 174 175 static int sbtsi_i3c_probe(struct i3c_device *i3cdev) 176 { 177 struct device *dev = i3cdev_to_dev(i3cdev); 178 struct i3c_device_info devinfo; 179 struct sbtsi_i3c_priv *i3c_priv; 180 struct sbtsi_data *data; 181 182 /* 183 * AMD OOB devices differ on basis of Instance ID, 184 * for SBTSI, instance ID is 0. 185 * As the device Id match is not on basis of Instance ID, 186 * add the below check to probe the SBTSI device only and 187 * not other OOB devices. 188 */ 189 i3c_device_get_info(i3cdev, &devinfo); 190 if (I3C_PID_INSTANCE_ID(devinfo.pid) != 0) 191 return -ENXIO; 192 193 i3c_priv = kzalloc_obj(*i3c_priv); 194 if (!i3c_priv) 195 return -ENOMEM; 196 197 data = &i3c_priv->data; 198 data->i3cdev = i3cdev; 199 data->is_i3c = true; 200 /* 201 * In a multi-socket system, otherwise identical devices do not share 202 * the same address; each instance is enumerated with a distinct dynamic 203 * (assigned) address on the I3C bus. Use that address (passed in as 204 * dev_addr) as the auxiliary device instance ID so that every socket 205 * gets a unique auxiliary device name. 206 */ 207 data->dev_addr = devinfo.dyn_addr; 208 209 return sbtsi_probe_common(dev, data); 210 } 211 212 static const struct i3c_device_id sbtsi_i3c_id[] = { 213 /* PID for AMD SBTSI device */ 214 I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x1, NULL), /* Socket:0, Turin and Genoa */ 215 I3C_DEVICE_EXTRA_INFO(0x0, 0x0, 0x118, NULL), /* Socket:0, Venice */ 216 I3C_DEVICE_EXTRA_INFO(0x0, 0x100, 0x118, NULL), /* Socket:1, Venice */ 217 I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x119, NULL), /* Socket:0, Venice */ 218 I3C_DEVICE_EXTRA_INFO(0x112, 0x100, 0x119, NULL), /* Socket:1, Venice */ 219 {} 220 }; 221 MODULE_DEVICE_TABLE(i3c, sbtsi_i3c_id); 222 223 static struct i3c_driver sbtsi_i3c_driver = { 224 .driver = { 225 .name = "sbtsi-i3c", 226 }, 227 .probe = sbtsi_i3c_probe, 228 .id_table = sbtsi_i3c_id, 229 }; 230 231 module_i3c_i2c_driver(sbtsi_i3c_driver, &sbtsi_driver); 232 233 MODULE_DESCRIPTION("AMD SB-TSI I2C/I3C core driver"); 234 MODULE_LICENSE("GPL"); 235