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