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