xref: /linux/drivers/hwmon/pmbus/xdpe1a2g7b.c (revision 46576fa32908043975471bd26fe833a7d8015b35)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Hardware monitoring driver for Infineon Multi-phase Digital XDPE1A2G5B
4  * and XDPE1A2G7B Controllers
5  *
6  * Copyright (c) 2026 Infineon Technologies. All rights reserved.
7  */
8 
9 #include <linux/err.h>
10 #include <linux/i2c.h>
11 #include <linux/init.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include "pmbus.h"
15 
16 #define XDPE1A2G7B_PAGE_NUM 2
17 #define XDPE1A2G7B_NVIDIA_195MV 0x1E /* NVIDIA mode 1.95mV, VID step is 5mV */
18 
19 static int xdpe1a2g7b_identify(struct i2c_client *client,
20 			       struct pmbus_driver_info *info)
21 {
22 	u8 vout_params;
23 	int vout_mode;
24 
25 	/*
26 	 * XDPE1A2G5B and XDPE1A2G7B support both Linear and NVIDIA PWM VID data
27 	 * formats via VOUT_MODE. Note that the device pages/loops are not fully
28 	 * independent: configuration is shared, so programming each page/loop
29 	 * separately is not supported.
30 	 */
31 	vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE);
32 	if (vout_mode < 0)
33 		return vout_mode;
34 
35 	switch (vout_mode >> 5) {
36 	case 0:
37 		info->format[PSC_VOLTAGE_OUT] = linear;
38 		return 0;
39 	case 1:
40 		info->format[PSC_VOLTAGE_OUT] = vid;
41 		vout_params = vout_mode & GENMASK(4, 0);
42 		/* Check for VID Code Type */
43 		switch (vout_params) {
44 		case XDPE1A2G7B_NVIDIA_195MV:
45 			/* VID vrm_version for PAGE0 and PAGE1 */
46 			info->vrm_version[0] = nvidia195mv;
47 			info->vrm_version[1] = nvidia195mv;
48 			break;
49 		default:
50 			return -EINVAL;
51 		}
52 		break;
53 	default:
54 		return -ENODEV;
55 	}
56 
57 	return 0;
58 }
59 
60 static struct pmbus_driver_info xdpe1a2g7b_info = {
61 	.pages = XDPE1A2G7B_PAGE_NUM,
62 	.identify = xdpe1a2g7b_identify,
63 	.format[PSC_VOLTAGE_IN] = linear,
64 	.format[PSC_TEMPERATURE] = linear,
65 	.format[PSC_CURRENT_IN] = linear,
66 	.format[PSC_CURRENT_OUT] = linear,
67 	.format[PSC_POWER] = linear,
68 	.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
69 		   PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
70 		   PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP |
71 		   PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT,
72 	.func[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
73 		   PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
74 		   PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | PMBUS_HAVE_STATUS_INPUT,
75 };
76 
77 static int xdpe1a2g7b_probe(struct i2c_client *client)
78 {
79 	struct pmbus_driver_info *info;
80 
81 	info = devm_kmemdup(&client->dev, &xdpe1a2g7b_info, sizeof(*info),
82 			    GFP_KERNEL);
83 	if (!info)
84 		return -ENOMEM;
85 
86 	return pmbus_do_probe(client, info);
87 }
88 
89 static const struct i2c_device_id xdpe1a2g7b_id[] = {
90 	{ "xdpe1a2g5b" },
91 	{ "xdpe1a2g7b" },
92 	{}
93 };
94 
95 MODULE_DEVICE_TABLE(i2c, xdpe1a2g7b_id);
96 
97 static const struct of_device_id __maybe_unused xdpe1a2g7b_of_match[] = {
98 	{ .compatible = "infineon,xdpe1a2g5b" },
99 	{ .compatible = "infineon,xdpe1a2g7b" },
100 	{}
101 };
102 
103 MODULE_DEVICE_TABLE(of, xdpe1a2g7b_of_match);
104 
105 static struct i2c_driver xdpe1a2g7b_driver = {
106 	.driver = {
107 		.name = "xdpe1a2g7b",
108 		.of_match_table = of_match_ptr(xdpe1a2g7b_of_match),
109 	},
110 	.probe = xdpe1a2g7b_probe,
111 	.id_table = xdpe1a2g7b_id,
112 };
113 
114 module_i2c_driver(xdpe1a2g7b_driver);
115 
116 MODULE_AUTHOR("Ashish Yadav <ashish.yadav@infineon.com>");
117 MODULE_DESCRIPTION("PMBus driver for Infineon XDPE1A2G5B/7B");
118 MODULE_LICENSE("GPL");
119 MODULE_IMPORT_NS("PMBUS");
120