xref: /linux/drivers/hwmon/pmbus/vt7505.c (revision 7acf90feab8b009fde7def08ff2c622d0f10e99f)
1*97c1723aSGeorgi Vlaev // SPDX-License-Identifier: GPL-2.0-or-later
2*97c1723aSGeorgi Vlaev /*
3*97c1723aSGeorgi Vlaev  * Hardware monitoring driver for Analog Devices MAX16545/MAX16550 and
4*97c1723aSGeorgi Vlaev  * Volterra VT7505 PMBus controllers.
5*97c1723aSGeorgi Vlaev  *
6*97c1723aSGeorgi Vlaev  * Copyright 2026 Hewlett Packard Enterprise Development LP
7*97c1723aSGeorgi Vlaev  */
8*97c1723aSGeorgi Vlaev 
9*97c1723aSGeorgi Vlaev #include <linux/bitfield.h>
10*97c1723aSGeorgi Vlaev #include <linux/i2c.h>
11*97c1723aSGeorgi Vlaev #include <linux/kernel.h>
12*97c1723aSGeorgi Vlaev #include <linux/module.h>
13*97c1723aSGeorgi Vlaev #include <linux/of.h>
14*97c1723aSGeorgi Vlaev #include <linux/pmbus.h>
15*97c1723aSGeorgi Vlaev 
16*97c1723aSGeorgi Vlaev #include "pmbus.h"
17*97c1723aSGeorgi Vlaev 
18*97c1723aSGeorgi Vlaev #define VT7505_MFR_CONFIG		0xd0
19*97c1723aSGeorgi Vlaev #define VT7505_CFG_OCP_S_FILT_MASK	GENMASK(15, 14)
20*97c1723aSGeorgi Vlaev 
21*97c1723aSGeorgi Vlaev #define VT7505_MFR_PEAK_VIN		0xd1
22*97c1723aSGeorgi Vlaev #define VT7505_MFR_PEAK_IOUT		0xd2
23*97c1723aSGeorgi Vlaev #define VT7505_MFR_PEAK_PIN		0xd3
24*97c1723aSGeorgi Vlaev #define VT7505_MFR_PEAK_TEMP		0xd4
25*97c1723aSGeorgi Vlaev #define VT7505_MFR_CLEAR_PEAKS		0xd5
26*97c1723aSGeorgi Vlaev #define VT7505_MFR_PEAK_VOUT		0xfd
27*97c1723aSGeorgi Vlaev 
28*97c1723aSGeorgi Vlaev #define VT7505_RLOAD_DEFAULT		4750
29*97c1723aSGeorgi Vlaev /* Largest RLOAD for which coeff * rload / 1000 still fits in s32. */
30*97c1723aSGeorgi Vlaev #define VT7505_RLOAD_MAX		283383959U
31*97c1723aSGeorgi Vlaev 
32*97c1723aSGeorgi Vlaev struct vt7505_chip_data {
33*97c1723aSGeorgi Vlaev 	int temp_m;
34*97c1723aSGeorgi Vlaev 	int temp_b;
35*97c1723aSGeorgi Vlaev 	bool has_ocp_filter;
36*97c1723aSGeorgi Vlaev };
37*97c1723aSGeorgi Vlaev 
38*97c1723aSGeorgi Vlaev static const struct vt7505_chip_data max16545_data = {
39*97c1723aSGeorgi Vlaev 	.temp_m = 205,
40*97c1723aSGeorgi Vlaev 	.temp_b = 6545,
41*97c1723aSGeorgi Vlaev };
42*97c1723aSGeorgi Vlaev 
43*97c1723aSGeorgi Vlaev static const struct vt7505_chip_data max16550_data = {
44*97c1723aSGeorgi Vlaev 	.temp_m = 199,
45*97c1723aSGeorgi Vlaev 	.temp_b = 7046,
46*97c1723aSGeorgi Vlaev 	.has_ocp_filter = true,
47*97c1723aSGeorgi Vlaev };
48*97c1723aSGeorgi Vlaev 
49*97c1723aSGeorgi Vlaev static const struct vt7505_chip_data vt7505_data = {
50*97c1723aSGeorgi Vlaev 	.temp_m = 205,
51*97c1723aSGeorgi Vlaev 	.temp_b = 6545,
52*97c1723aSGeorgi Vlaev 	.has_ocp_filter = true,
53*97c1723aSGeorgi Vlaev };
54*97c1723aSGeorgi Vlaev 
55*97c1723aSGeorgi Vlaev static int vt7505_read_word_data(struct i2c_client *client, int page,
56*97c1723aSGeorgi Vlaev 				 int phase, int reg)
57*97c1723aSGeorgi Vlaev {
58*97c1723aSGeorgi Vlaev 	switch (reg) {
59*97c1723aSGeorgi Vlaev 	case PMBUS_VIRT_READ_VIN_MAX:
60*97c1723aSGeorgi Vlaev 		return pmbus_read_word_data(client, page, phase,
61*97c1723aSGeorgi Vlaev 					    VT7505_MFR_PEAK_VIN);
62*97c1723aSGeorgi Vlaev 	case PMBUS_VIRT_READ_IOUT_MAX:
63*97c1723aSGeorgi Vlaev 		return pmbus_read_word_data(client, page, phase,
64*97c1723aSGeorgi Vlaev 					    VT7505_MFR_PEAK_IOUT);
65*97c1723aSGeorgi Vlaev 	case PMBUS_VIRT_READ_PIN_MAX:
66*97c1723aSGeorgi Vlaev 		return pmbus_read_word_data(client, page, phase,
67*97c1723aSGeorgi Vlaev 					    VT7505_MFR_PEAK_PIN);
68*97c1723aSGeorgi Vlaev 	case PMBUS_VIRT_READ_TEMP_MAX:
69*97c1723aSGeorgi Vlaev 		return pmbus_read_word_data(client, page, phase,
70*97c1723aSGeorgi Vlaev 					    VT7505_MFR_PEAK_TEMP);
71*97c1723aSGeorgi Vlaev 	case PMBUS_VIRT_READ_VOUT_MAX:
72*97c1723aSGeorgi Vlaev 		return pmbus_read_word_data(client, page, phase,
73*97c1723aSGeorgi Vlaev 					    VT7505_MFR_PEAK_VOUT);
74*97c1723aSGeorgi Vlaev 	case PMBUS_VIRT_RESET_VIN_HISTORY:
75*97c1723aSGeorgi Vlaev 	case PMBUS_VIRT_RESET_IOUT_HISTORY:
76*97c1723aSGeorgi Vlaev 	case PMBUS_VIRT_RESET_PIN_HISTORY:
77*97c1723aSGeorgi Vlaev 	case PMBUS_VIRT_RESET_TEMP_HISTORY:
78*97c1723aSGeorgi Vlaev 	case PMBUS_VIRT_RESET_VOUT_HISTORY:
79*97c1723aSGeorgi Vlaev 		return 0;
80*97c1723aSGeorgi Vlaev 	default:
81*97c1723aSGeorgi Vlaev 		return -ENODATA;
82*97c1723aSGeorgi Vlaev 	}
83*97c1723aSGeorgi Vlaev }
84*97c1723aSGeorgi Vlaev 
85*97c1723aSGeorgi Vlaev static int vt7505_write_word_data(struct i2c_client *client, int page,
86*97c1723aSGeorgi Vlaev 				  int reg, u16 word)
87*97c1723aSGeorgi Vlaev {
88*97c1723aSGeorgi Vlaev 	switch (reg) {
89*97c1723aSGeorgi Vlaev 	/*
90*97c1723aSGeorgi Vlaev 	 * A single reset command clears all peak values. CLEAR_PEAKS is a
91*97c1723aSGeorgi Vlaev 	 * send-byte command; the device NAKs a word or byte-data write to it.
92*97c1723aSGeorgi Vlaev 	 */
93*97c1723aSGeorgi Vlaev 	case PMBUS_VIRT_RESET_VIN_HISTORY:
94*97c1723aSGeorgi Vlaev 	case PMBUS_VIRT_RESET_IOUT_HISTORY:
95*97c1723aSGeorgi Vlaev 	case PMBUS_VIRT_RESET_PIN_HISTORY:
96*97c1723aSGeorgi Vlaev 	case PMBUS_VIRT_RESET_TEMP_HISTORY:
97*97c1723aSGeorgi Vlaev 	case PMBUS_VIRT_RESET_VOUT_HISTORY:
98*97c1723aSGeorgi Vlaev 		return pmbus_write_byte(client, page,
99*97c1723aSGeorgi Vlaev 					VT7505_MFR_CLEAR_PEAKS);
100*97c1723aSGeorgi Vlaev 	default:
101*97c1723aSGeorgi Vlaev 		return -ENODATA;
102*97c1723aSGeorgi Vlaev 	}
103*97c1723aSGeorgi Vlaev }
104*97c1723aSGeorgi Vlaev 
105*97c1723aSGeorgi Vlaev /*
106*97c1723aSGeorgi Vlaev  * None of these controllers implement the standard PMBus WRITE_PROTECT
107*97c1723aSGeorgi Vlaev  * (0x10) register, so tell the core not to access it.
108*97c1723aSGeorgi Vlaev  */
109*97c1723aSGeorgi Vlaev static struct pmbus_platform_data vt7505_pdata = {
110*97c1723aSGeorgi Vlaev 	.flags = PMBUS_NO_WRITE_PROTECT,
111*97c1723aSGeorgi Vlaev };
112*97c1723aSGeorgi Vlaev 
113*97c1723aSGeorgi Vlaev static int vt7505_set_ocp_filter(struct i2c_client *client)
114*97c1723aSGeorgi Vlaev {
115*97c1723aSGeorgi Vlaev 	u32 ocp_us;
116*97c1723aSGeorgi Vlaev 	u8 field;
117*97c1723aSGeorgi Vlaev 	int ret;
118*97c1723aSGeorgi Vlaev 	u16 word;
119*97c1723aSGeorgi Vlaev 
120*97c1723aSGeorgi Vlaev 	if (of_property_read_u32(client->dev.of_node, "adi,ocp-severe-filter-us",
121*97c1723aSGeorgi Vlaev 				 &ocp_us))
122*97c1723aSGeorgi Vlaev 		return 0;
123*97c1723aSGeorgi Vlaev 
124*97c1723aSGeorgi Vlaev 	switch (ocp_us) {
125*97c1723aSGeorgi Vlaev 	case 0:
126*97c1723aSGeorgi Vlaev 		field = 0;
127*97c1723aSGeorgi Vlaev 		break;
128*97c1723aSGeorgi Vlaev 	case 1:
129*97c1723aSGeorgi Vlaev 		field = 1;
130*97c1723aSGeorgi Vlaev 		break;
131*97c1723aSGeorgi Vlaev 	case 2:
132*97c1723aSGeorgi Vlaev 		field = 2;
133*97c1723aSGeorgi Vlaev 		break;
134*97c1723aSGeorgi Vlaev 	case 10:
135*97c1723aSGeorgi Vlaev 		field = 3;
136*97c1723aSGeorgi Vlaev 		break;
137*97c1723aSGeorgi Vlaev 	default:
138*97c1723aSGeorgi Vlaev 		return dev_err_probe(&client->dev, -EINVAL,
139*97c1723aSGeorgi Vlaev 				     "invalid adi,ocp-severe-filter-us value %u\n",
140*97c1723aSGeorgi Vlaev 				     ocp_us);
141*97c1723aSGeorgi Vlaev 	}
142*97c1723aSGeorgi Vlaev 
143*97c1723aSGeorgi Vlaev 	ret = i2c_smbus_read_word_data(client, VT7505_MFR_CONFIG);
144*97c1723aSGeorgi Vlaev 	if (ret < 0)
145*97c1723aSGeorgi Vlaev 		return dev_err_probe(&client->dev, ret,
146*97c1723aSGeorgi Vlaev 				     "failed to read MFR_CONFIG\n");
147*97c1723aSGeorgi Vlaev 
148*97c1723aSGeorgi Vlaev 	word = ret & ~VT7505_CFG_OCP_S_FILT_MASK;
149*97c1723aSGeorgi Vlaev 	word |= FIELD_PREP(VT7505_CFG_OCP_S_FILT_MASK, field);
150*97c1723aSGeorgi Vlaev 
151*97c1723aSGeorgi Vlaev 	ret = i2c_smbus_write_word_data(client, VT7505_MFR_CONFIG, word);
152*97c1723aSGeorgi Vlaev 	if (ret < 0)
153*97c1723aSGeorgi Vlaev 		return dev_err_probe(&client->dev, ret,
154*97c1723aSGeorgi Vlaev 				     "failed to write MFR_CONFIG\n");
155*97c1723aSGeorgi Vlaev 
156*97c1723aSGeorgi Vlaev 	return 0;
157*97c1723aSGeorgi Vlaev }
158*97c1723aSGeorgi Vlaev 
159*97c1723aSGeorgi Vlaev static void vt7505_set_m(int *m, u32 rload)
160*97c1723aSGeorgi Vlaev {
161*97c1723aSGeorgi Vlaev 	u64 val = (u64)*m * rload;
162*97c1723aSGeorgi Vlaev 
163*97c1723aSGeorgi Vlaev 	/* rload is range-checked in probe, so the result fits in int. */
164*97c1723aSGeorgi Vlaev 	*m = DIV_ROUND_CLOSEST_ULL(val, 1000);
165*97c1723aSGeorgi Vlaev }
166*97c1723aSGeorgi Vlaev 
167*97c1723aSGeorgi Vlaev static int vt7505_probe(struct i2c_client *client)
168*97c1723aSGeorgi Vlaev {
169*97c1723aSGeorgi Vlaev 	struct device *dev = &client->dev;
170*97c1723aSGeorgi Vlaev 	const struct vt7505_chip_data *chip;
171*97c1723aSGeorgi Vlaev 	struct pmbus_driver_info *info;
172*97c1723aSGeorgi Vlaev 	u32 rload;
173*97c1723aSGeorgi Vlaev 	int ret;
174*97c1723aSGeorgi Vlaev 
175*97c1723aSGeorgi Vlaev 	chip = i2c_get_match_data(client);
176*97c1723aSGeorgi Vlaev 	if (!chip)
177*97c1723aSGeorgi Vlaev 		return -ENODEV;
178*97c1723aSGeorgi Vlaev 
179*97c1723aSGeorgi Vlaev 	info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
180*97c1723aSGeorgi Vlaev 	if (!info)
181*97c1723aSGeorgi Vlaev 		return -ENOMEM;
182*97c1723aSGeorgi Vlaev 
183*97c1723aSGeorgi Vlaev 	dev->platform_data = &vt7505_pdata;
184*97c1723aSGeorgi Vlaev 
185*97c1723aSGeorgi Vlaev 	/*
186*97c1723aSGeorgi Vlaev 	 * The m coefficient used in the direct-format current and power
187*97c1723aSGeorgi Vlaev 	 * calculations depends on RLOAD, the external current-report resistor
188*97c1723aSGeorgi Vlaev 	 * connected between the ILOAD pin and ground. Use the default value if
189*97c1723aSGeorgi Vlaev 	 * none is specified.
190*97c1723aSGeorgi Vlaev 	 */
191*97c1723aSGeorgi Vlaev 	if (of_property_read_u32(dev->of_node, "adi,rload-ohms", &rload))
192*97c1723aSGeorgi Vlaev 		rload = VT7505_RLOAD_DEFAULT;
193*97c1723aSGeorgi Vlaev 
194*97c1723aSGeorgi Vlaev 	if (!rload || rload > VT7505_RLOAD_MAX)
195*97c1723aSGeorgi Vlaev 		return dev_err_probe(dev, -EINVAL,
196*97c1723aSGeorgi Vlaev 				     "adi,rload-ohms must be 1-%u\n",
197*97c1723aSGeorgi Vlaev 				     VT7505_RLOAD_MAX);
198*97c1723aSGeorgi Vlaev 
199*97c1723aSGeorgi Vlaev 	info->pages = 1;
200*97c1723aSGeorgi Vlaev 	info->read_word_data = vt7505_read_word_data;
201*97c1723aSGeorgi Vlaev 	info->write_word_data = vt7505_write_word_data;
202*97c1723aSGeorgi Vlaev 
203*97c1723aSGeorgi Vlaev 	info->format[PSC_VOLTAGE_IN] = direct;
204*97c1723aSGeorgi Vlaev 	info->format[PSC_VOLTAGE_OUT] = direct;
205*97c1723aSGeorgi Vlaev 	info->format[PSC_CURRENT_IN] = direct;
206*97c1723aSGeorgi Vlaev 	info->format[PSC_CURRENT_OUT] = direct;
207*97c1723aSGeorgi Vlaev 	info->format[PSC_POWER] = direct;
208*97c1723aSGeorgi Vlaev 	info->format[PSC_TEMPERATURE] = direct;
209*97c1723aSGeorgi Vlaev 
210*97c1723aSGeorgi Vlaev 	/*
211*97c1723aSGeorgi Vlaev 	 * Direct data format coefficients from the device datasheet ("PMBus
212*97c1723aSGeorgi Vlaev 	 * Equation Parameters"). The current and power m coefficients scale
213*97c1723aSGeorgi Vlaev 	 * with RLOAD; vt7505_set_m() applies the 1/1000 factor below, giving
214*97c1723aSGeorgi Vlaev 	 * m = 3.824 * RLOAD for current and 0.895 * RLOAD for power. The
215*97c1723aSGeorgi Vlaev 	 * temperature coefficients are chip specific (see the chip data).
216*97c1723aSGeorgi Vlaev 	 */
217*97c1723aSGeorgi Vlaev 	info->m[PSC_VOLTAGE_IN] = 7578;
218*97c1723aSGeorgi Vlaev 	info->R[PSC_VOLTAGE_IN] = -2;
219*97c1723aSGeorgi Vlaev 	info->m[PSC_VOLTAGE_OUT] = 7578;
220*97c1723aSGeorgi Vlaev 	info->R[PSC_VOLTAGE_OUT] = -2;
221*97c1723aSGeorgi Vlaev 	info->m[PSC_CURRENT_IN] = 3824;
222*97c1723aSGeorgi Vlaev 	info->b[PSC_CURRENT_IN] = -4300;
223*97c1723aSGeorgi Vlaev 	info->R[PSC_CURRENT_IN] = -3;
224*97c1723aSGeorgi Vlaev 	info->m[PSC_CURRENT_OUT] = 3824;
225*97c1723aSGeorgi Vlaev 	info->b[PSC_CURRENT_OUT] = -4300;
226*97c1723aSGeorgi Vlaev 	info->R[PSC_CURRENT_OUT] = -3;
227*97c1723aSGeorgi Vlaev 	info->m[PSC_POWER] = 895;
228*97c1723aSGeorgi Vlaev 	info->b[PSC_POWER] = -9100;
229*97c1723aSGeorgi Vlaev 	info->R[PSC_POWER] = -2;
230*97c1723aSGeorgi Vlaev 
231*97c1723aSGeorgi Vlaev 	vt7505_set_m(&info->m[PSC_CURRENT_IN], rload);
232*97c1723aSGeorgi Vlaev 	vt7505_set_m(&info->m[PSC_CURRENT_OUT], rload);
233*97c1723aSGeorgi Vlaev 	vt7505_set_m(&info->m[PSC_POWER], rload);
234*97c1723aSGeorgi Vlaev 
235*97c1723aSGeorgi Vlaev 	info->m[PSC_TEMPERATURE] = chip->temp_m;
236*97c1723aSGeorgi Vlaev 	info->b[PSC_TEMPERATURE] = chip->temp_b;
237*97c1723aSGeorgi Vlaev 	info->R[PSC_TEMPERATURE] = -2;
238*97c1723aSGeorgi Vlaev 
239*97c1723aSGeorgi Vlaev 	info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT |
240*97c1723aSGeorgi Vlaev 			PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
241*97c1723aSGeorgi Vlaev 			PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
242*97c1723aSGeorgi Vlaev 			PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP |
243*97c1723aSGeorgi Vlaev 			PMBUS_HAVE_IIN | PMBUS_HAVE_PIN;
244*97c1723aSGeorgi Vlaev 
245*97c1723aSGeorgi Vlaev 	/*
246*97c1723aSGeorgi Vlaev 	 * The severe OCP deglitch filter is programmable on the MAX16550 and
247*97c1723aSGeorgi Vlaev 	 * the VT7505, but fixed on the MAX16545.
248*97c1723aSGeorgi Vlaev 	 */
249*97c1723aSGeorgi Vlaev 	if (chip->has_ocp_filter) {
250*97c1723aSGeorgi Vlaev 		ret = vt7505_set_ocp_filter(client);
251*97c1723aSGeorgi Vlaev 		if (ret)
252*97c1723aSGeorgi Vlaev 			return ret;
253*97c1723aSGeorgi Vlaev 	}
254*97c1723aSGeorgi Vlaev 
255*97c1723aSGeorgi Vlaev 	return pmbus_do_probe(client, info);
256*97c1723aSGeorgi Vlaev }
257*97c1723aSGeorgi Vlaev 
258*97c1723aSGeorgi Vlaev static const struct i2c_device_id vt7505_id[] = {
259*97c1723aSGeorgi Vlaev 	{ .name = "max16545", .driver_data = (kernel_ulong_t)&max16545_data },
260*97c1723aSGeorgi Vlaev 	{ .name = "max16550", .driver_data = (kernel_ulong_t)&max16550_data },
261*97c1723aSGeorgi Vlaev 	{ .name = "vt7505", .driver_data = (kernel_ulong_t)&vt7505_data },
262*97c1723aSGeorgi Vlaev 	{ }
263*97c1723aSGeorgi Vlaev };
264*97c1723aSGeorgi Vlaev MODULE_DEVICE_TABLE(i2c, vt7505_id);
265*97c1723aSGeorgi Vlaev 
266*97c1723aSGeorgi Vlaev static const struct of_device_id vt7505_of_match[] = {
267*97c1723aSGeorgi Vlaev 	{ .compatible = "adi,max16545", .data = &max16545_data },
268*97c1723aSGeorgi Vlaev 	{ .compatible = "adi,max16550", .data = &max16550_data },
269*97c1723aSGeorgi Vlaev 	{ .compatible = "adi,vt7505", .data = &vt7505_data },
270*97c1723aSGeorgi Vlaev 	{ }
271*97c1723aSGeorgi Vlaev };
272*97c1723aSGeorgi Vlaev MODULE_DEVICE_TABLE(of, vt7505_of_match);
273*97c1723aSGeorgi Vlaev 
274*97c1723aSGeorgi Vlaev static struct i2c_driver vt7505_driver = {
275*97c1723aSGeorgi Vlaev 	.driver = {
276*97c1723aSGeorgi Vlaev 		.name = "vt7505",
277*97c1723aSGeorgi Vlaev 		.of_match_table = vt7505_of_match,
278*97c1723aSGeorgi Vlaev 	},
279*97c1723aSGeorgi Vlaev 	.probe = vt7505_probe,
280*97c1723aSGeorgi Vlaev 	.id_table = vt7505_id,
281*97c1723aSGeorgi Vlaev };
282*97c1723aSGeorgi Vlaev module_i2c_driver(vt7505_driver);
283*97c1723aSGeorgi Vlaev 
284*97c1723aSGeorgi Vlaev MODULE_AUTHOR("Georgi Vlaev <gvlaev@juniper.net>");
285*97c1723aSGeorgi Vlaev MODULE_DESCRIPTION("PMBus driver for Analog Devices MAX16545/MAX16550 and Volterra VT7505");
286*97c1723aSGeorgi Vlaev MODULE_LICENSE("GPL");
287*97c1723aSGeorgi Vlaev MODULE_IMPORT_NS("PMBUS");
288