1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Hardware monitoring driver for Analog Devices LT3074 4 * 5 * Copyright (C) 2025 Analog Devices, Inc. 6 */ 7 #include <linux/err.h> 8 #include <linux/i2c.h> 9 #include <linux/mod_devicetable.h> 10 #include <linux/module.h> 11 12 #include "pmbus.h" 13 14 #define LT3074_MFR_READ_VBIAS 0xc6 15 #define LT3074_MFR_BIAS_OV_WARN_LIMIT 0xc7 16 #define LT3074_MFR_BIAS_UV_WARN_LIMIT 0xc8 17 #define LT3074_MFR_SPECIAL_ID 0xe7 18 19 #define LT3074_SPECIAL_ID_VALUE 0x1c1d 20 21 static const struct regulator_desc __maybe_unused lt3074_reg_desc[] = { 22 PMBUS_REGULATOR_ONE("regulator"), 23 }; 24 25 static int lt3074_read_word_data(struct i2c_client *client, int page, 26 int phase, int reg) 27 { 28 switch (reg) { 29 case PMBUS_VIRT_READ_VMON: 30 return pmbus_read_word_data(client, page, phase, 31 LT3074_MFR_READ_VBIAS); 32 case PMBUS_VIRT_VMON_UV_WARN_LIMIT: 33 return pmbus_read_word_data(client, page, phase, 34 LT3074_MFR_BIAS_UV_WARN_LIMIT); 35 case PMBUS_VIRT_VMON_OV_WARN_LIMIT: 36 return pmbus_read_word_data(client, page, phase, 37 LT3074_MFR_BIAS_OV_WARN_LIMIT); 38 default: 39 return -ENODATA; 40 } 41 } 42 43 static int lt3074_write_word_data(struct i2c_client *client, int page, 44 int reg, u16 word) 45 { 46 switch (reg) { 47 case PMBUS_VIRT_VMON_UV_WARN_LIMIT: 48 return pmbus_write_word_data(client, 0, 49 LT3074_MFR_BIAS_UV_WARN_LIMIT, 50 word); 51 case PMBUS_VIRT_VMON_OV_WARN_LIMIT: 52 return pmbus_write_word_data(client, 0, 53 LT3074_MFR_BIAS_OV_WARN_LIMIT, 54 word); 55 default: 56 return -ENODATA; 57 } 58 } 59 60 static struct pmbus_driver_info lt3074_info = { 61 .pages = 1, 62 .format[PSC_VOLTAGE_IN] = linear, 63 .format[PSC_VOLTAGE_OUT] = linear, 64 .format[PSC_CURRENT_OUT] = linear, 65 .format[PSC_TEMPERATURE] = linear, 66 .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | 67 PMBUS_HAVE_TEMP | PMBUS_HAVE_VMON | 68 PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | 69 PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP, 70 .read_word_data = lt3074_read_word_data, 71 .write_word_data = lt3074_write_word_data, 72 #if IS_ENABLED(CONFIG_SENSORS_LT3074_REGULATOR) 73 .num_regulators = 1, 74 .reg_desc = lt3074_reg_desc, 75 #endif 76 }; 77 78 static int lt3074_probe(struct i2c_client *client) 79 { 80 int ret; 81 struct device *dev = &client->dev; 82 83 if (!i2c_check_functionality(client->adapter, 84 I2C_FUNC_SMBUS_READ_WORD_DATA)) 85 return -ENODEV; 86 87 ret = i2c_smbus_read_word_data(client, LT3074_MFR_SPECIAL_ID); 88 if (ret < 0) 89 return dev_err_probe(dev, ret, "Failed to read ID\n"); 90 91 if (ret != LT3074_SPECIAL_ID_VALUE) 92 return dev_err_probe(dev, -ENODEV, "ID mismatch\n"); 93 94 return pmbus_do_probe(client, <3074_info); 95 } 96 97 static const struct i2c_device_id lt3074_id[] = { 98 { "lt3074", 0 }, 99 {} 100 }; 101 MODULE_DEVICE_TABLE(i2c, lt3074_id); 102 103 static const struct of_device_id __maybe_unused lt3074_of_match[] = { 104 { .compatible = "adi,lt3074" }, 105 {} 106 }; 107 MODULE_DEVICE_TABLE(of, lt3074_of_match); 108 109 static struct i2c_driver lt3074_driver = { 110 .driver = { 111 .name = "lt3074", 112 .of_match_table = of_match_ptr(lt3074_of_match), 113 }, 114 .probe = lt3074_probe, 115 .id_table = lt3074_id, 116 }; 117 module_i2c_driver(lt3074_driver); 118 119 MODULE_AUTHOR("Cedric Encarnacion <cedricjustine.encarnacion@analog.com>"); 120 MODULE_DESCRIPTION("PMBus driver for Analog Devices LT3074"); 121 MODULE_LICENSE("GPL"); 122 MODULE_IMPORT_NS("PMBUS"); 123