1319138ffSPrathima // SPDX-License-Identifier: GPL-2.0-or-later 2319138ffSPrathima /* 3f61a6fd4SPrathima * tsi.c - AMD SBTSI I2C/I3C core driver. Probes the SBTSI device over I2C/I3C 4319138ffSPrathima * and publishes an auxiliary device on the auxiliary bus. 5319138ffSPrathima * 6319138ffSPrathima * Copyright (C) 2026 Advanced Micro Devices, Inc. 7319138ffSPrathima */ 8319138ffSPrathima 9319138ffSPrathima #include <linux/auxiliary_bus.h> 10319138ffSPrathima #include <linux/bitfield.h> 11319138ffSPrathima #include <linux/module.h> 12319138ffSPrathima #include <linux/of.h> 13319138ffSPrathima #include <linux/slab.h> 14f61a6fd4SPrathima #include "tsi-core.h" 15319138ffSPrathima 16319138ffSPrathima #define SBTSI_REG_CONFIG 0x03 /* RO */ 17319138ffSPrathima 18319138ffSPrathima /* 19319138ffSPrathima * Bit for reporting value with temperature measurement range. 20319138ffSPrathima * bit == 0: Use default temperature range (0C to 255.875C). 21319138ffSPrathima * bit == 1: Use extended temperature range (-49C to +206.875C). 22319138ffSPrathima */ 23319138ffSPrathima #define SBTSI_CONFIG_EXT_RANGE_SHIFT 2 24319138ffSPrathima 25319138ffSPrathima /* 26319138ffSPrathima * ReadOrder bit specifies the reading order of integer and decimal part of 27319138ffSPrathima * CPU temperature for atomic reads. If bit == 0, reading integer part triggers 28319138ffSPrathima * latching of the decimal part, so integer part should be read first. 29319138ffSPrathima */ 30319138ffSPrathima #define SBTSI_CONFIG_READ_ORDER_SHIFT 5 31319138ffSPrathima 32319138ffSPrathima static void sbtsi_adev_release(struct device *dev) 33319138ffSPrathima { 34319138ffSPrathima kfree(to_auxiliary_dev(dev)); 35319138ffSPrathima } 36319138ffSPrathima 37319138ffSPrathima static void sbtsi_unregister_hwmon_adev(void *_adev) 38319138ffSPrathima { 39319138ffSPrathima struct auxiliary_device *adev = _adev; 40319138ffSPrathima 41319138ffSPrathima auxiliary_device_delete(adev); 42319138ffSPrathima auxiliary_device_uninit(adev); 43319138ffSPrathima } 44319138ffSPrathima 45*48ad55cdSPrathima static void sbtsi_misc_unregister(void *arg) 46*48ad55cdSPrathima { 47*48ad55cdSPrathima struct sbtsi_data *data = arg; 48*48ad55cdSPrathima 49*48ad55cdSPrathima misc_deregister(&data->sbtsi_misc_dev); 50*48ad55cdSPrathima 51*48ad55cdSPrathima guard(sbtsi)(data); 52*48ad55cdSPrathima data->detached = true; 53*48ad55cdSPrathima } 54*48ad55cdSPrathima 55*48ad55cdSPrathima static void sbtsi_driver_unref(void *arg) 56*48ad55cdSPrathima { 57*48ad55cdSPrathima struct sbtsi_data *data = arg; 58*48ad55cdSPrathima 59*48ad55cdSPrathima kref_put(&data->kref, sbtsi_data_release); 60*48ad55cdSPrathima } 61*48ad55cdSPrathima 62319138ffSPrathima /* 63319138ffSPrathima * Create and publish an auxiliary device. The hwmon driver in 64319138ffSPrathima * drivers/hwmon/sbtsi_temp.c binds to this device. 65319138ffSPrathima * 66319138ffSPrathima * @dev: I2C device (parent of the auxiliary device) 67319138ffSPrathima * @dev_addr: I2C address — used as the auxiliary device instance ID so that 68319138ffSPrathima * each socket gets a unique name. 69319138ffSPrathima */ 70319138ffSPrathima static int sbtsi_create_hwmon_adev(struct device *dev, u8 dev_addr) 71319138ffSPrathima { 72319138ffSPrathima struct auxiliary_device *adev; 73319138ffSPrathima int ret; 74319138ffSPrathima 75319138ffSPrathima adev = kzalloc_obj(*adev); 76319138ffSPrathima if (!adev) 77319138ffSPrathima return -ENOMEM; 78319138ffSPrathima 79319138ffSPrathima adev->name = AMD_SBTSI_AUX_HWMON; 80319138ffSPrathima adev->id = dev_addr; 81319138ffSPrathima adev->dev.parent = dev; 82319138ffSPrathima adev->dev.release = sbtsi_adev_release; 83319138ffSPrathima 84319138ffSPrathima ret = auxiliary_device_init(adev); 85319138ffSPrathima if (ret) { 86319138ffSPrathima kfree(adev); 87319138ffSPrathima return ret; 88319138ffSPrathima } 89319138ffSPrathima 90319138ffSPrathima ret = __auxiliary_device_add(adev, AMD_SBTSI_ADEV); 91319138ffSPrathima if (ret) { 92319138ffSPrathima auxiliary_device_uninit(adev); 93319138ffSPrathima return ret; 94319138ffSPrathima } 95319138ffSPrathima 96319138ffSPrathima return devm_add_action_or_reset(dev, sbtsi_unregister_hwmon_adev, adev); 97319138ffSPrathima } 98319138ffSPrathima 99ba27ea7aSPrathima static int sbtsi_probe_common(struct device *dev, struct sbtsi_data *data) 100ba27ea7aSPrathima { 101ba27ea7aSPrathima u8 val; 102ba27ea7aSPrathima int err; 103ba27ea7aSPrathima 104*48ad55cdSPrathima mutex_init(&data->lock); 105*48ad55cdSPrathima kref_init(&data->kref); 106*48ad55cdSPrathima 107*48ad55cdSPrathima err = devm_add_action_or_reset(dev, sbtsi_driver_unref, data); 108*48ad55cdSPrathima if (err) 109*48ad55cdSPrathima return err; 110*48ad55cdSPrathima 111ba27ea7aSPrathima err = sbtsi_xfer(data, SBTSI_REG_CONFIG, &val, true); 112ba27ea7aSPrathima if (err) 113ba27ea7aSPrathima return err; 114ba27ea7aSPrathima 115ba27ea7aSPrathima data->ext_range_mode = FIELD_GET(BIT(SBTSI_CONFIG_EXT_RANGE_SHIFT), val); 116ba27ea7aSPrathima data->read_order = FIELD_GET(BIT(SBTSI_CONFIG_READ_ORDER_SHIFT), val); 117ba27ea7aSPrathima 118ba27ea7aSPrathima dev_set_drvdata(dev, data); 119*48ad55cdSPrathima err = sbtsi_create_hwmon_adev(dev, data->dev_addr); 120*48ad55cdSPrathima if (err < 0) 121*48ad55cdSPrathima return err; 122*48ad55cdSPrathima 123*48ad55cdSPrathima err = create_misc_tsi_device(data, dev); 124*48ad55cdSPrathima if (err) 125*48ad55cdSPrathima return err; 126*48ad55cdSPrathima 127*48ad55cdSPrathima return devm_add_action_or_reset(dev, sbtsi_misc_unregister, data); 128ba27ea7aSPrathima } 129ba27ea7aSPrathima 130319138ffSPrathima static int sbtsi_i2c_probe(struct i2c_client *client) 131319138ffSPrathima { 132319138ffSPrathima struct device *dev = &client->dev; 133319138ffSPrathima struct sbtsi_data *data; 134319138ffSPrathima 135*48ad55cdSPrathima data = kzalloc_obj(*data); 136319138ffSPrathima if (!data) 137319138ffSPrathima return -ENOMEM; 138319138ffSPrathima 139f61a6fd4SPrathima data->is_i3c = false; 140319138ffSPrathima data->client = client; 141319138ffSPrathima 142319138ffSPrathima /* In a multi-socket system, devices that are otherwise identical do not 143319138ffSPrathima * share the same static address; each instance resides at a unique I2C 144319138ffSPrathima * client address on the same or different bus. Use the I2C client 145319138ffSPrathima * address as the auxiliary device instance ID to ensure each socket 146319138ffSPrathima * receives a distinct auxiliary device name. 147319138ffSPrathima */ 148ba27ea7aSPrathima data->dev_addr = client->addr; 149ba27ea7aSPrathima return sbtsi_probe_common(dev, data); 150319138ffSPrathima } 151319138ffSPrathima 152319138ffSPrathima static const struct i2c_device_id sbtsi_id[] = { 153319138ffSPrathima { .name = "sbtsi" }, 154319138ffSPrathima { } 155319138ffSPrathima }; 156319138ffSPrathima MODULE_DEVICE_TABLE(i2c, sbtsi_id); 157319138ffSPrathima 158319138ffSPrathima static const struct of_device_id __maybe_unused sbtsi_of_match[] = { 159319138ffSPrathima { 160319138ffSPrathima .compatible = "amd,sbtsi", 161319138ffSPrathima }, 162319138ffSPrathima { }, 163319138ffSPrathima }; 164319138ffSPrathima MODULE_DEVICE_TABLE(of, sbtsi_of_match); 165319138ffSPrathima 166319138ffSPrathima static struct i2c_driver sbtsi_driver = { 167319138ffSPrathima .driver = { 168319138ffSPrathima .name = "sbtsi-i2c", 169319138ffSPrathima .of_match_table = of_match_ptr(sbtsi_of_match), 170319138ffSPrathima }, 171319138ffSPrathima .probe = sbtsi_i2c_probe, 172319138ffSPrathima .id_table = sbtsi_id, 173319138ffSPrathima }; 174319138ffSPrathima 175f61a6fd4SPrathima static int sbtsi_i3c_probe(struct i3c_device *i3cdev) 176f61a6fd4SPrathima { 177f61a6fd4SPrathima struct device *dev = i3cdev_to_dev(i3cdev); 178f61a6fd4SPrathima struct i3c_device_info devinfo; 179f61a6fd4SPrathima struct sbtsi_i3c_priv *i3c_priv; 180f61a6fd4SPrathima struct sbtsi_data *data; 181319138ffSPrathima 182f61a6fd4SPrathima /* 183f61a6fd4SPrathima * AMD OOB devices differ on basis of Instance ID, 184f61a6fd4SPrathima * for SBTSI, instance ID is 0. 185f61a6fd4SPrathima * As the device Id match is not on basis of Instance ID, 186f61a6fd4SPrathima * add the below check to probe the SBTSI device only and 187f61a6fd4SPrathima * not other OOB devices. 188f61a6fd4SPrathima */ 189f61a6fd4SPrathima i3c_device_get_info(i3cdev, &devinfo); 190f61a6fd4SPrathima if (I3C_PID_INSTANCE_ID(devinfo.pid) != 0) 191f61a6fd4SPrathima return -ENXIO; 192f61a6fd4SPrathima 193*48ad55cdSPrathima i3c_priv = kzalloc_obj(*i3c_priv); 194f61a6fd4SPrathima if (!i3c_priv) 195f61a6fd4SPrathima return -ENOMEM; 196f61a6fd4SPrathima 197f61a6fd4SPrathima data = &i3c_priv->data; 198f61a6fd4SPrathima data->i3cdev = i3cdev; 199f61a6fd4SPrathima data->is_i3c = true; 200f61a6fd4SPrathima /* 201f61a6fd4SPrathima * In a multi-socket system, otherwise identical devices do not share 202f61a6fd4SPrathima * the same address; each instance is enumerated with a distinct dynamic 203f61a6fd4SPrathima * (assigned) address on the I3C bus. Use that address (passed in as 204f61a6fd4SPrathima * dev_addr) as the auxiliary device instance ID so that every socket 205f61a6fd4SPrathima * gets a unique auxiliary device name. 206f61a6fd4SPrathima */ 207f61a6fd4SPrathima data->dev_addr = devinfo.dyn_addr; 208f61a6fd4SPrathima 209f61a6fd4SPrathima return sbtsi_probe_common(dev, data); 210f61a6fd4SPrathima } 211f61a6fd4SPrathima 212f61a6fd4SPrathima static const struct i3c_device_id sbtsi_i3c_id[] = { 213f61a6fd4SPrathima /* PID for AMD SBTSI device */ 214f61a6fd4SPrathima I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x1, NULL), /* Socket:0, Turin and Genoa */ 215f61a6fd4SPrathima I3C_DEVICE_EXTRA_INFO(0x0, 0x0, 0x118, NULL), /* Socket:0, Venice */ 216f61a6fd4SPrathima I3C_DEVICE_EXTRA_INFO(0x0, 0x100, 0x118, NULL), /* Socket:1, Venice */ 217f61a6fd4SPrathima I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x119, NULL), /* Socket:0, Venice */ 218f61a6fd4SPrathima I3C_DEVICE_EXTRA_INFO(0x112, 0x100, 0x119, NULL), /* Socket:1, Venice */ 219f61a6fd4SPrathima {} 220f61a6fd4SPrathima }; 221f61a6fd4SPrathima MODULE_DEVICE_TABLE(i3c, sbtsi_i3c_id); 222f61a6fd4SPrathima 223f61a6fd4SPrathima static struct i3c_driver sbtsi_i3c_driver = { 224f61a6fd4SPrathima .driver = { 225f61a6fd4SPrathima .name = "sbtsi-i3c", 226f61a6fd4SPrathima }, 227f61a6fd4SPrathima .probe = sbtsi_i3c_probe, 228f61a6fd4SPrathima .id_table = sbtsi_i3c_id, 229f61a6fd4SPrathima }; 230f61a6fd4SPrathima 231f61a6fd4SPrathima module_i3c_i2c_driver(sbtsi_i3c_driver, &sbtsi_driver); 232f61a6fd4SPrathima 233f61a6fd4SPrathima MODULE_DESCRIPTION("AMD SB-TSI I2C/I3C core driver"); 234319138ffSPrathima MODULE_LICENSE("GPL"); 235