198 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Hardware monitoring driver for Infineon Multi-phase Digital VR Controllers4 *5 * Copyright (c) 2020 Mellanox Technologies. All rights reserved.6 */7 8#include <linux/err.h>9#include <linux/i2c.h>10#include <linux/init.h>11#include <linux/kernel.h>12#include <linux/module.h>13#include <linux/regulator/driver.h>14 15#include "pmbus.h"16 17#define XDPE122_PROT_VR12_5MV 0x01 /* VR12.0 mode, 5-mV DAC */18#define XDPE122_PROT_VR12_5_10MV 0x02 /* VR12.5 mode, 10-mV DAC */19#define XDPE122_PROT_IMVP9_10MV 0x03 /* IMVP9 mode, 10-mV DAC */20#define XDPE122_AMD_625MV 0x10 /* AMD mode 6.25mV */21#define XDPE122_PAGE_NUM 222 23static int xdpe122_read_word_data(struct i2c_client *client, int page,24 int phase, int reg)25{26 const struct pmbus_driver_info *info = pmbus_get_driver_info(client);27 long val;28 s16 exponent;29 s32 mantissa;30 int ret;31 32 switch (reg) {33 case PMBUS_VOUT_OV_FAULT_LIMIT:34 case PMBUS_VOUT_UV_FAULT_LIMIT:35 ret = pmbus_read_word_data(client, page, phase, reg);36 if (ret < 0)37 return ret;38 39 /* Convert register value to LINEAR11 data. */40 exponent = ((s16)ret) >> 11;41 mantissa = ((s16)((ret & GENMASK(10, 0)) << 5)) >> 5;42 val = mantissa * 1000L;43 if (exponent >= 0)44 val <<= exponent;45 else46 val >>= -exponent;47 48 /* Convert data to VID register. */49 switch (info->vrm_version[page]) {50 case vr13:51 if (val >= 500)52 return 1 + DIV_ROUND_CLOSEST(val - 500, 10);53 return 0;54 case vr12:55 if (val >= 250)56 return 1 + DIV_ROUND_CLOSEST(val - 250, 5);57 return 0;58 case imvp9:59 if (val >= 200)60 return 1 + DIV_ROUND_CLOSEST(val - 200, 10);61 return 0;62 case amd625mv:63 if (val >= 200 && val <= 1550)64 return DIV_ROUND_CLOSEST((1550 - val) * 100,65 625);66 return 0;67 default:68 return -EINVAL;69 }70 default:71 return -ENODATA;72 }73 74 return 0;75}76 77static int xdpe122_identify(struct i2c_client *client,78 struct pmbus_driver_info *info)79{80 u8 vout_params;81 int i, ret, vout_mode;82 83 vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE);84 if (vout_mode >= 0 && vout_mode != 0xff) {85 switch (vout_mode >> 5) {86 case 0:87 info->format[PSC_VOLTAGE_OUT] = linear;88 return 0;89 case 1:90 info->format[PSC_VOLTAGE_OUT] = vid;91 info->read_word_data = xdpe122_read_word_data;92 break;93 default:94 return -ENODEV;95 }96 }97 98 for (i = 0; i < XDPE122_PAGE_NUM; i++) {99 /* Read the register with VOUT scaling value.*/100 ret = pmbus_read_byte_data(client, i, PMBUS_VOUT_MODE);101 if (ret < 0)102 return ret;103 104 vout_params = ret & GENMASK(4, 0);105 106 switch (vout_params) {107 case XDPE122_PROT_VR12_5_10MV:108 info->vrm_version[i] = vr13;109 break;110 case XDPE122_PROT_VR12_5MV:111 info->vrm_version[i] = vr12;112 break;113 case XDPE122_PROT_IMVP9_10MV:114 info->vrm_version[i] = imvp9;115 break;116 case XDPE122_AMD_625MV:117 info->vrm_version[i] = amd625mv;118 break;119 default:120 return -EINVAL;121 }122 }123 124 return 0;125}126 127static const struct regulator_desc __maybe_unused xdpe122_reg_desc[] = {128 PMBUS_REGULATOR("vout", 0),129 PMBUS_REGULATOR("vout", 1),130};131 132static struct pmbus_driver_info xdpe122_info = {133 .pages = XDPE122_PAGE_NUM,134 .format[PSC_VOLTAGE_IN] = linear,135 .format[PSC_TEMPERATURE] = linear,136 .format[PSC_CURRENT_IN] = linear,137 .format[PSC_CURRENT_OUT] = linear,138 .format[PSC_POWER] = linear,139 .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |140 PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |141 PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP |142 PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT,143 .func[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |144 PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |145 PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP |146 PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT,147 .identify = xdpe122_identify,148#if IS_ENABLED(CONFIG_SENSORS_XDPE122_REGULATOR)149 .num_regulators = 2,150 .reg_desc = xdpe122_reg_desc,151#endif152};153 154static int xdpe122_probe(struct i2c_client *client)155{156 struct pmbus_driver_info *info;157 158 info = devm_kmemdup(&client->dev, &xdpe122_info, sizeof(*info),159 GFP_KERNEL);160 if (!info)161 return -ENOMEM;162 163 return pmbus_do_probe(client, info);164}165 166static const struct i2c_device_id xdpe122_id[] = {167 {"xdpe11280"},168 {"xdpe12254"},169 {"xdpe12284"},170 {}171};172 173MODULE_DEVICE_TABLE(i2c, xdpe122_id);174 175static const struct of_device_id __maybe_unused xdpe122_of_match[] = {176 {.compatible = "infineon,xdpe11280"},177 {.compatible = "infineon,xdpe12254"},178 {.compatible = "infineon,xdpe12284"},179 {}180};181MODULE_DEVICE_TABLE(of, xdpe122_of_match);182 183static struct i2c_driver xdpe122_driver = {184 .driver = {185 .name = "xdpe12284",186 .of_match_table = of_match_ptr(xdpe122_of_match),187 },188 .probe = xdpe122_probe,189 .id_table = xdpe122_id,190};191 192module_i2c_driver(xdpe122_driver);193 194MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");195MODULE_DESCRIPTION("PMBus driver for Infineon XDPE122 family");196MODULE_LICENSE("GPL");197MODULE_IMPORT_NS(PMBUS);198