18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2020 Nvidia Technologies Ltd. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/err.h> 98c2ecf20Sopenharmony_ci#include <linux/i2c.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include "pmbus.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/* Vendor specific registers. */ 168c2ecf20Sopenharmony_ci#define MP2975_MFR_APS_HYS_R2 0x0d 178c2ecf20Sopenharmony_ci#define MP2975_MFR_SLOPE_TRIM3 0x1d 188c2ecf20Sopenharmony_ci#define MP2975_MFR_VR_MULTI_CONFIG_R1 0x0d 198c2ecf20Sopenharmony_ci#define MP2975_MFR_VR_MULTI_CONFIG_R2 0x1d 208c2ecf20Sopenharmony_ci#define MP2975_MFR_APS_DECAY_ADV 0x56 218c2ecf20Sopenharmony_ci#define MP2975_MFR_DC_LOOP_CTRL 0x59 228c2ecf20Sopenharmony_ci#define MP2975_MFR_OCP_UCP_PHASE_SET 0x65 238c2ecf20Sopenharmony_ci#define MP2975_MFR_VR_CONFIG1 0x68 248c2ecf20Sopenharmony_ci#define MP2975_MFR_READ_CS1_2 0x82 258c2ecf20Sopenharmony_ci#define MP2975_MFR_READ_CS3_4 0x83 268c2ecf20Sopenharmony_ci#define MP2975_MFR_READ_CS5_6 0x84 278c2ecf20Sopenharmony_ci#define MP2975_MFR_READ_CS7_8 0x85 288c2ecf20Sopenharmony_ci#define MP2975_MFR_READ_CS9_10 0x86 298c2ecf20Sopenharmony_ci#define MP2975_MFR_READ_CS11_12 0x87 308c2ecf20Sopenharmony_ci#define MP2975_MFR_READ_IOUT_PK 0x90 318c2ecf20Sopenharmony_ci#define MP2975_MFR_READ_POUT_PK 0x91 328c2ecf20Sopenharmony_ci#define MP2975_MFR_READ_VREF_R1 0xa1 338c2ecf20Sopenharmony_ci#define MP2975_MFR_READ_VREF_R2 0xa3 348c2ecf20Sopenharmony_ci#define MP2975_MFR_OVP_TH_SET 0xe5 358c2ecf20Sopenharmony_ci#define MP2975_MFR_UVP_SET 0xe6 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define MP2975_VOUT_FORMAT BIT(15) 388c2ecf20Sopenharmony_ci#define MP2975_VID_STEP_SEL_R1 BIT(4) 398c2ecf20Sopenharmony_ci#define MP2975_IMVP9_EN_R1 BIT(13) 408c2ecf20Sopenharmony_ci#define MP2975_VID_STEP_SEL_R2 BIT(3) 418c2ecf20Sopenharmony_ci#define MP2975_IMVP9_EN_R2 BIT(12) 428c2ecf20Sopenharmony_ci#define MP2975_PRT_THRES_DIV_OV_EN BIT(14) 438c2ecf20Sopenharmony_ci#define MP2975_DRMOS_KCS GENMASK(13, 12) 448c2ecf20Sopenharmony_ci#define MP2975_PROT_DEV_OV_OFF 10 458c2ecf20Sopenharmony_ci#define MP2975_PROT_DEV_OV_ON 5 468c2ecf20Sopenharmony_ci#define MP2975_SENSE_AMPL BIT(11) 478c2ecf20Sopenharmony_ci#define MP2975_SENSE_AMPL_UNIT 1 488c2ecf20Sopenharmony_ci#define MP2975_SENSE_AMPL_HALF 2 498c2ecf20Sopenharmony_ci#define MP2975_VIN_UV_LIMIT_UNIT 8 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define MP2975_MAX_PHASE_RAIL1 8 528c2ecf20Sopenharmony_ci#define MP2975_MAX_PHASE_RAIL2 4 538c2ecf20Sopenharmony_ci#define MP2975_PAGE_NUM 2 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define MP2975_RAIL2_FUNC (PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | \ 568c2ecf20Sopenharmony_ci PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | \ 578c2ecf20Sopenharmony_ci PMBUS_HAVE_POUT | PMBUS_PHASE_VIRTUAL) 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistruct mp2975_data { 608c2ecf20Sopenharmony_ci struct pmbus_driver_info info; 618c2ecf20Sopenharmony_ci int vout_scale; 628c2ecf20Sopenharmony_ci int vid_step[MP2975_PAGE_NUM]; 638c2ecf20Sopenharmony_ci int vref[MP2975_PAGE_NUM]; 648c2ecf20Sopenharmony_ci int vref_off[MP2975_PAGE_NUM]; 658c2ecf20Sopenharmony_ci int vout_max[MP2975_PAGE_NUM]; 668c2ecf20Sopenharmony_ci int vout_ov_fixed[MP2975_PAGE_NUM]; 678c2ecf20Sopenharmony_ci int vout_format[MP2975_PAGE_NUM]; 688c2ecf20Sopenharmony_ci int curr_sense_gain[MP2975_PAGE_NUM]; 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define to_mp2975_data(x) container_of(x, struct mp2975_data, info) 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int mp2975_read_byte_data(struct i2c_client *client, int page, int reg) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci switch (reg) { 768c2ecf20Sopenharmony_ci case PMBUS_VOUT_MODE: 778c2ecf20Sopenharmony_ci /* 788c2ecf20Sopenharmony_ci * Enforce VOUT direct format, since device allows to set the 798c2ecf20Sopenharmony_ci * different formats for the different rails. Conversion from 808c2ecf20Sopenharmony_ci * VID to direct provided by driver internally, in case it is 818c2ecf20Sopenharmony_ci * necessary. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci return PB_VOUT_MODE_DIRECT; 848c2ecf20Sopenharmony_ci default: 858c2ecf20Sopenharmony_ci return -ENODATA; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic int 908c2ecf20Sopenharmony_cimp2975_read_word_helper(struct i2c_client *client, int page, int phase, u8 reg, 918c2ecf20Sopenharmony_ci u16 mask) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci int ret = pmbus_read_word_data(client, page, phase, reg); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return (ret > 0) ? ret & mask : ret; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int 998c2ecf20Sopenharmony_cimp2975_vid2direct(int vrf, int val) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci switch (vrf) { 1028c2ecf20Sopenharmony_ci case vr12: 1038c2ecf20Sopenharmony_ci if (val >= 0x01) 1048c2ecf20Sopenharmony_ci return 250 + (val - 1) * 5; 1058c2ecf20Sopenharmony_ci break; 1068c2ecf20Sopenharmony_ci case vr13: 1078c2ecf20Sopenharmony_ci if (val >= 0x01) 1088c2ecf20Sopenharmony_ci return 500 + (val - 1) * 10; 1098c2ecf20Sopenharmony_ci break; 1108c2ecf20Sopenharmony_ci case imvp9: 1118c2ecf20Sopenharmony_ci if (val >= 0x01) 1128c2ecf20Sopenharmony_ci return 200 + (val - 1) * 10; 1138c2ecf20Sopenharmony_ci break; 1148c2ecf20Sopenharmony_ci default: 1158c2ecf20Sopenharmony_ci return -EINVAL; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci return 0; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int 1218c2ecf20Sopenharmony_cimp2975_read_phase(struct i2c_client *client, struct mp2975_data *data, 1228c2ecf20Sopenharmony_ci int page, int phase, u8 reg) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci int ph_curr, ret; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci ret = pmbus_read_word_data(client, page, phase, reg); 1278c2ecf20Sopenharmony_ci if (ret < 0) 1288c2ecf20Sopenharmony_ci return ret; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (!((phase + 1) % MP2975_PAGE_NUM)) 1318c2ecf20Sopenharmony_ci ret >>= 8; 1328c2ecf20Sopenharmony_ci ret &= 0xff; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* 1358c2ecf20Sopenharmony_ci * Output value is calculated as: (READ_CSx / 80 – 1.23) / (Kcs * Rcs) 1368c2ecf20Sopenharmony_ci * where: 1378c2ecf20Sopenharmony_ci * - Kcs is the DrMOS current sense gain of power stage, which is 1388c2ecf20Sopenharmony_ci * obtained from the register MP2975_MFR_VR_CONFIG1, bits 13-12 with 1398c2ecf20Sopenharmony_ci * the following selection of DrMOS (data->curr_sense_gain[page]): 1408c2ecf20Sopenharmony_ci * 00b - 5µA/A, 01b - 8.5µA/A, 10b - 9.7µA/A, 11b - 10µA/A. 1418c2ecf20Sopenharmony_ci * - Rcs is the internal phase current sense resistor which is constant 1428c2ecf20Sopenharmony_ci * value 1kΩ. 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_ci ph_curr = ret * 100 - 9800; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* 1478c2ecf20Sopenharmony_ci * Current phase sensing, providing by the device is not accurate 1488c2ecf20Sopenharmony_ci * for the light load. This because sampling of current occurrence of 1498c2ecf20Sopenharmony_ci * bit weight has a big deviation for light load. For handling such 1508c2ecf20Sopenharmony_ci * case phase current is represented as the maximum between the value 1518c2ecf20Sopenharmony_ci * calculated above and total rail current divided by number phases. 1528c2ecf20Sopenharmony_ci */ 1538c2ecf20Sopenharmony_ci ret = pmbus_read_word_data(client, page, phase, PMBUS_READ_IOUT); 1548c2ecf20Sopenharmony_ci if (ret < 0) 1558c2ecf20Sopenharmony_ci return ret; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return max_t(int, DIV_ROUND_CLOSEST(ret, data->info.phases[page]), 1588c2ecf20Sopenharmony_ci DIV_ROUND_CLOSEST(ph_curr, data->curr_sense_gain[page])); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic int 1628c2ecf20Sopenharmony_cimp2975_read_phases(struct i2c_client *client, struct mp2975_data *data, 1638c2ecf20Sopenharmony_ci int page, int phase) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci int ret; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (page) { 1688c2ecf20Sopenharmony_ci switch (phase) { 1698c2ecf20Sopenharmony_ci case 0 ... 1: 1708c2ecf20Sopenharmony_ci ret = mp2975_read_phase(client, data, page, phase, 1718c2ecf20Sopenharmony_ci MP2975_MFR_READ_CS7_8); 1728c2ecf20Sopenharmony_ci break; 1738c2ecf20Sopenharmony_ci case 2 ... 3: 1748c2ecf20Sopenharmony_ci ret = mp2975_read_phase(client, data, page, phase, 1758c2ecf20Sopenharmony_ci MP2975_MFR_READ_CS9_10); 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci case 4 ... 5: 1788c2ecf20Sopenharmony_ci ret = mp2975_read_phase(client, data, page, phase, 1798c2ecf20Sopenharmony_ci MP2975_MFR_READ_CS11_12); 1808c2ecf20Sopenharmony_ci break; 1818c2ecf20Sopenharmony_ci default: 1828c2ecf20Sopenharmony_ci return -ENODATA; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci } else { 1858c2ecf20Sopenharmony_ci switch (phase) { 1868c2ecf20Sopenharmony_ci case 0 ... 1: 1878c2ecf20Sopenharmony_ci ret = mp2975_read_phase(client, data, page, phase, 1888c2ecf20Sopenharmony_ci MP2975_MFR_READ_CS1_2); 1898c2ecf20Sopenharmony_ci break; 1908c2ecf20Sopenharmony_ci case 2 ... 3: 1918c2ecf20Sopenharmony_ci ret = mp2975_read_phase(client, data, page, phase, 1928c2ecf20Sopenharmony_ci MP2975_MFR_READ_CS3_4); 1938c2ecf20Sopenharmony_ci break; 1948c2ecf20Sopenharmony_ci case 4 ... 5: 1958c2ecf20Sopenharmony_ci ret = mp2975_read_phase(client, data, page, phase, 1968c2ecf20Sopenharmony_ci MP2975_MFR_READ_CS5_6); 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci case 6 ... 7: 1998c2ecf20Sopenharmony_ci ret = mp2975_read_phase(client, data, page, phase, 2008c2ecf20Sopenharmony_ci MP2975_MFR_READ_CS7_8); 2018c2ecf20Sopenharmony_ci break; 2028c2ecf20Sopenharmony_ci case 8 ... 9: 2038c2ecf20Sopenharmony_ci ret = mp2975_read_phase(client, data, page, phase, 2048c2ecf20Sopenharmony_ci MP2975_MFR_READ_CS9_10); 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci case 10 ... 11: 2078c2ecf20Sopenharmony_ci ret = mp2975_read_phase(client, data, page, phase, 2088c2ecf20Sopenharmony_ci MP2975_MFR_READ_CS11_12); 2098c2ecf20Sopenharmony_ci break; 2108c2ecf20Sopenharmony_ci default: 2118c2ecf20Sopenharmony_ci return -ENODATA; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci return ret; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int mp2975_read_word_data(struct i2c_client *client, int page, 2188c2ecf20Sopenharmony_ci int phase, int reg) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci const struct pmbus_driver_info *info = pmbus_get_driver_info(client); 2218c2ecf20Sopenharmony_ci struct mp2975_data *data = to_mp2975_data(info); 2228c2ecf20Sopenharmony_ci int ret; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci switch (reg) { 2258c2ecf20Sopenharmony_ci case PMBUS_OT_FAULT_LIMIT: 2268c2ecf20Sopenharmony_ci ret = mp2975_read_word_helper(client, page, phase, reg, 2278c2ecf20Sopenharmony_ci GENMASK(7, 0)); 2288c2ecf20Sopenharmony_ci break; 2298c2ecf20Sopenharmony_ci case PMBUS_VIN_OV_FAULT_LIMIT: 2308c2ecf20Sopenharmony_ci ret = mp2975_read_word_helper(client, page, phase, reg, 2318c2ecf20Sopenharmony_ci GENMASK(7, 0)); 2328c2ecf20Sopenharmony_ci if (ret < 0) 2338c2ecf20Sopenharmony_ci return ret; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci ret = DIV_ROUND_CLOSEST(ret, MP2975_VIN_UV_LIMIT_UNIT); 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci case PMBUS_VOUT_OV_FAULT_LIMIT: 2388c2ecf20Sopenharmony_ci /* 2398c2ecf20Sopenharmony_ci * Register provides two values for over-voltage protection 2408c2ecf20Sopenharmony_ci * threshold for fixed (ovp2) and tracking (ovp1) modes. The 2418c2ecf20Sopenharmony_ci * minimum of these two values is provided as over-voltage 2428c2ecf20Sopenharmony_ci * fault alarm. 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_ci ret = mp2975_read_word_helper(client, page, phase, 2458c2ecf20Sopenharmony_ci MP2975_MFR_OVP_TH_SET, 2468c2ecf20Sopenharmony_ci GENMASK(2, 0)); 2478c2ecf20Sopenharmony_ci if (ret < 0) 2488c2ecf20Sopenharmony_ci return ret; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci ret = min_t(int, data->vout_max[page] + 50 * (ret + 1), 2518c2ecf20Sopenharmony_ci data->vout_ov_fixed[page]); 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci case PMBUS_VOUT_UV_FAULT_LIMIT: 2548c2ecf20Sopenharmony_ci ret = mp2975_read_word_helper(client, page, phase, 2558c2ecf20Sopenharmony_ci MP2975_MFR_UVP_SET, 2568c2ecf20Sopenharmony_ci GENMASK(2, 0)); 2578c2ecf20Sopenharmony_ci if (ret < 0) 2588c2ecf20Sopenharmony_ci return ret; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci ret = DIV_ROUND_CLOSEST(data->vref[page] * 10 - 50 * 2618c2ecf20Sopenharmony_ci (ret + 1) * data->vout_scale, 10); 2628c2ecf20Sopenharmony_ci break; 2638c2ecf20Sopenharmony_ci case PMBUS_READ_VOUT: 2648c2ecf20Sopenharmony_ci ret = mp2975_read_word_helper(client, page, phase, reg, 2658c2ecf20Sopenharmony_ci GENMASK(11, 0)); 2668c2ecf20Sopenharmony_ci if (ret < 0) 2678c2ecf20Sopenharmony_ci return ret; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* 2708c2ecf20Sopenharmony_ci * READ_VOUT can be provided in VID or direct format. The 2718c2ecf20Sopenharmony_ci * format type is specified by bit 15 of the register 2728c2ecf20Sopenharmony_ci * MP2975_MFR_DC_LOOP_CTRL. The driver enforces VOUT direct 2738c2ecf20Sopenharmony_ci * format, since device allows to set the different formats for 2748c2ecf20Sopenharmony_ci * the different rails and also all VOUT limits registers are 2758c2ecf20Sopenharmony_ci * provided in a direct format. In case format is VID - convert 2768c2ecf20Sopenharmony_ci * to direct. 2778c2ecf20Sopenharmony_ci */ 2788c2ecf20Sopenharmony_ci if (data->vout_format[page] == vid) 2798c2ecf20Sopenharmony_ci ret = mp2975_vid2direct(info->vrm_version[page], ret); 2808c2ecf20Sopenharmony_ci break; 2818c2ecf20Sopenharmony_ci case PMBUS_VIRT_READ_POUT_MAX: 2828c2ecf20Sopenharmony_ci ret = mp2975_read_word_helper(client, page, phase, 2838c2ecf20Sopenharmony_ci MP2975_MFR_READ_POUT_PK, 2848c2ecf20Sopenharmony_ci GENMASK(12, 0)); 2858c2ecf20Sopenharmony_ci if (ret < 0) 2868c2ecf20Sopenharmony_ci return ret; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ret = DIV_ROUND_CLOSEST(ret, 4); 2898c2ecf20Sopenharmony_ci break; 2908c2ecf20Sopenharmony_ci case PMBUS_VIRT_READ_IOUT_MAX: 2918c2ecf20Sopenharmony_ci ret = mp2975_read_word_helper(client, page, phase, 2928c2ecf20Sopenharmony_ci MP2975_MFR_READ_IOUT_PK, 2938c2ecf20Sopenharmony_ci GENMASK(12, 0)); 2948c2ecf20Sopenharmony_ci if (ret < 0) 2958c2ecf20Sopenharmony_ci return ret; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci ret = DIV_ROUND_CLOSEST(ret, 4); 2988c2ecf20Sopenharmony_ci break; 2998c2ecf20Sopenharmony_ci case PMBUS_READ_IOUT: 3008c2ecf20Sopenharmony_ci ret = mp2975_read_phases(client, data, page, phase); 3018c2ecf20Sopenharmony_ci if (ret < 0) 3028c2ecf20Sopenharmony_ci return ret; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci break; 3058c2ecf20Sopenharmony_ci case PMBUS_UT_WARN_LIMIT: 3068c2ecf20Sopenharmony_ci case PMBUS_UT_FAULT_LIMIT: 3078c2ecf20Sopenharmony_ci case PMBUS_VIN_UV_WARN_LIMIT: 3088c2ecf20Sopenharmony_ci case PMBUS_VIN_UV_FAULT_LIMIT: 3098c2ecf20Sopenharmony_ci case PMBUS_VOUT_UV_WARN_LIMIT: 3108c2ecf20Sopenharmony_ci case PMBUS_VOUT_OV_WARN_LIMIT: 3118c2ecf20Sopenharmony_ci case PMBUS_VIN_OV_WARN_LIMIT: 3128c2ecf20Sopenharmony_ci case PMBUS_IIN_OC_FAULT_LIMIT: 3138c2ecf20Sopenharmony_ci case PMBUS_IOUT_OC_LV_FAULT_LIMIT: 3148c2ecf20Sopenharmony_ci case PMBUS_IIN_OC_WARN_LIMIT: 3158c2ecf20Sopenharmony_ci case PMBUS_IOUT_OC_WARN_LIMIT: 3168c2ecf20Sopenharmony_ci case PMBUS_IOUT_OC_FAULT_LIMIT: 3178c2ecf20Sopenharmony_ci case PMBUS_IOUT_UC_FAULT_LIMIT: 3188c2ecf20Sopenharmony_ci case PMBUS_POUT_OP_FAULT_LIMIT: 3198c2ecf20Sopenharmony_ci case PMBUS_POUT_OP_WARN_LIMIT: 3208c2ecf20Sopenharmony_ci case PMBUS_PIN_OP_WARN_LIMIT: 3218c2ecf20Sopenharmony_ci return -ENXIO; 3228c2ecf20Sopenharmony_ci default: 3238c2ecf20Sopenharmony_ci return -ENODATA; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return ret; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic int mp2975_identify_multiphase_rail2(struct i2c_client *client) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci int ret; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci /* 3348c2ecf20Sopenharmony_ci * Identify multiphase for rail 2 - could be from 0 to 4. 3358c2ecf20Sopenharmony_ci * In case phase number is zero – only page zero is supported 3368c2ecf20Sopenharmony_ci */ 3378c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2); 3388c2ecf20Sopenharmony_ci if (ret < 0) 3398c2ecf20Sopenharmony_ci return ret; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* Identify multiphase for rail 2 - could be from 0 to 4. */ 3428c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(client, MP2975_MFR_VR_MULTI_CONFIG_R2); 3438c2ecf20Sopenharmony_ci if (ret < 0) 3448c2ecf20Sopenharmony_ci return ret; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci ret &= GENMASK(2, 0); 3478c2ecf20Sopenharmony_ci return (ret >= 4) ? 4 : ret; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic void mp2975_set_phase_rail1(struct pmbus_driver_info *info) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci int i; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci for (i = 0 ; i < info->phases[0]; i++) 3558c2ecf20Sopenharmony_ci info->pfunc[i] = PMBUS_HAVE_IOUT; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic void 3598c2ecf20Sopenharmony_cimp2975_set_phase_rail2(struct pmbus_driver_info *info, int num_phases) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci int i; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* Set phases for rail 2 from upper to lower. */ 3648c2ecf20Sopenharmony_ci for (i = 1; i <= num_phases; i++) 3658c2ecf20Sopenharmony_ci info->pfunc[MP2975_MAX_PHASE_RAIL1 - i] = PMBUS_HAVE_IOUT; 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic int 3698c2ecf20Sopenharmony_cimp2975_identify_multiphase(struct i2c_client *client, struct mp2975_data *data, 3708c2ecf20Sopenharmony_ci struct pmbus_driver_info *info) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci int num_phases2, ret; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2); 3758c2ecf20Sopenharmony_ci if (ret < 0) 3768c2ecf20Sopenharmony_ci return ret; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* Identify multiphase for rail 1 - could be from 1 to 8. */ 3798c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(client, MP2975_MFR_VR_MULTI_CONFIG_R1); 3808c2ecf20Sopenharmony_ci if (ret <= 0) 3818c2ecf20Sopenharmony_ci return ret; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci info->phases[0] = ret & GENMASK(3, 0); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci /* 3868c2ecf20Sopenharmony_ci * The device provides a total of 8 PWM pins, and can be configured 3878c2ecf20Sopenharmony_ci * to different phase count applications for rail 1 and rail 2. 3888c2ecf20Sopenharmony_ci * Rail 1 can be set to 8 phases, while rail 2 can only be set to 4 3898c2ecf20Sopenharmony_ci * phases at most. When rail 1’s phase count is configured as 0, rail 3908c2ecf20Sopenharmony_ci * 1 operates with 1-phase DCM. When rail 2 phase count is configured 3918c2ecf20Sopenharmony_ci * as 0, rail 2 is disabled. 3928c2ecf20Sopenharmony_ci */ 3938c2ecf20Sopenharmony_ci if (info->phases[0] > MP2975_MAX_PHASE_RAIL1) 3948c2ecf20Sopenharmony_ci return -EINVAL; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci mp2975_set_phase_rail1(info); 3978c2ecf20Sopenharmony_ci num_phases2 = min(MP2975_MAX_PHASE_RAIL1 - info->phases[0], 3988c2ecf20Sopenharmony_ci MP2975_MAX_PHASE_RAIL2); 3998c2ecf20Sopenharmony_ci if (info->phases[1] && info->phases[1] <= num_phases2) 4008c2ecf20Sopenharmony_ci mp2975_set_phase_rail2(info, num_phases2); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci return 0; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic int 4068c2ecf20Sopenharmony_cimp2975_identify_vid(struct i2c_client *client, struct mp2975_data *data, 4078c2ecf20Sopenharmony_ci struct pmbus_driver_info *info, u32 reg, int page, 4088c2ecf20Sopenharmony_ci u32 imvp_bit, u32 vr_bit) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci int ret; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci /* Identify VID mode and step selection. */ 4138c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(client, reg); 4148c2ecf20Sopenharmony_ci if (ret < 0) 4158c2ecf20Sopenharmony_ci return ret; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (ret & imvp_bit) { 4188c2ecf20Sopenharmony_ci info->vrm_version[page] = imvp9; 4198c2ecf20Sopenharmony_ci data->vid_step[page] = MP2975_PROT_DEV_OV_OFF; 4208c2ecf20Sopenharmony_ci } else if (ret & vr_bit) { 4218c2ecf20Sopenharmony_ci info->vrm_version[page] = vr12; 4228c2ecf20Sopenharmony_ci data->vid_step[page] = MP2975_PROT_DEV_OV_ON; 4238c2ecf20Sopenharmony_ci } else { 4248c2ecf20Sopenharmony_ci info->vrm_version[page] = vr13; 4258c2ecf20Sopenharmony_ci data->vid_step[page] = MP2975_PROT_DEV_OV_OFF; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci return 0; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic int 4328c2ecf20Sopenharmony_cimp2975_identify_rails_vid(struct i2c_client *client, struct mp2975_data *data, 4338c2ecf20Sopenharmony_ci struct pmbus_driver_info *info) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci int ret; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2); 4388c2ecf20Sopenharmony_ci if (ret < 0) 4398c2ecf20Sopenharmony_ci return ret; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* Identify VID mode for rail 1. */ 4428c2ecf20Sopenharmony_ci ret = mp2975_identify_vid(client, data, info, 4438c2ecf20Sopenharmony_ci MP2975_MFR_VR_MULTI_CONFIG_R1, 0, 4448c2ecf20Sopenharmony_ci MP2975_IMVP9_EN_R1, MP2975_VID_STEP_SEL_R1); 4458c2ecf20Sopenharmony_ci if (ret < 0) 4468c2ecf20Sopenharmony_ci return ret; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* Identify VID mode for rail 2, if connected. */ 4498c2ecf20Sopenharmony_ci if (info->phases[1]) 4508c2ecf20Sopenharmony_ci ret = mp2975_identify_vid(client, data, info, 4518c2ecf20Sopenharmony_ci MP2975_MFR_VR_MULTI_CONFIG_R2, 1, 4528c2ecf20Sopenharmony_ci MP2975_IMVP9_EN_R2, 4538c2ecf20Sopenharmony_ci MP2975_VID_STEP_SEL_R2); 4548c2ecf20Sopenharmony_ci return ret; 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic int 4588c2ecf20Sopenharmony_cimp2975_current_sense_gain_get(struct i2c_client *client, 4598c2ecf20Sopenharmony_ci struct mp2975_data *data) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci int i, ret; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* 4648c2ecf20Sopenharmony_ci * Obtain DrMOS current sense gain of power stage from the register 4658c2ecf20Sopenharmony_ci * MP2975_MFR_VR_CONFIG1, bits 13-12. The value is selected as below: 4668c2ecf20Sopenharmony_ci * 00b - 5µA/A, 01b - 8.5µA/A, 10b - 9.7µA/A, 11b - 10µA/A. Other 4678c2ecf20Sopenharmony_ci * values are invalid. 4688c2ecf20Sopenharmony_ci */ 4698c2ecf20Sopenharmony_ci for (i = 0 ; i < data->info.pages; i++) { 4708c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i); 4718c2ecf20Sopenharmony_ci if (ret < 0) 4728c2ecf20Sopenharmony_ci return ret; 4738c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(client, 4748c2ecf20Sopenharmony_ci MP2975_MFR_VR_CONFIG1); 4758c2ecf20Sopenharmony_ci if (ret < 0) 4768c2ecf20Sopenharmony_ci return ret; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci switch ((ret & MP2975_DRMOS_KCS) >> 12) { 4798c2ecf20Sopenharmony_ci case 0: 4808c2ecf20Sopenharmony_ci data->curr_sense_gain[i] = 50; 4818c2ecf20Sopenharmony_ci break; 4828c2ecf20Sopenharmony_ci case 1: 4838c2ecf20Sopenharmony_ci data->curr_sense_gain[i] = 85; 4848c2ecf20Sopenharmony_ci break; 4858c2ecf20Sopenharmony_ci case 2: 4868c2ecf20Sopenharmony_ci data->curr_sense_gain[i] = 97; 4878c2ecf20Sopenharmony_ci break; 4888c2ecf20Sopenharmony_ci default: 4898c2ecf20Sopenharmony_ci data->curr_sense_gain[i] = 100; 4908c2ecf20Sopenharmony_ci break; 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci return 0; 4958c2ecf20Sopenharmony_ci} 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cistatic int 4988c2ecf20Sopenharmony_cimp2975_vref_get(struct i2c_client *client, struct mp2975_data *data, 4998c2ecf20Sopenharmony_ci struct pmbus_driver_info *info) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci int ret; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 3); 5048c2ecf20Sopenharmony_ci if (ret < 0) 5058c2ecf20Sopenharmony_ci return ret; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci /* Get voltage reference value for rail 1. */ 5088c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(client, MP2975_MFR_READ_VREF_R1); 5098c2ecf20Sopenharmony_ci if (ret < 0) 5108c2ecf20Sopenharmony_ci return ret; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci data->vref[0] = ret * data->vid_step[0]; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci /* Get voltage reference value for rail 2, if connected. */ 5158c2ecf20Sopenharmony_ci if (data->info.pages == MP2975_PAGE_NUM) { 5168c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(client, MP2975_MFR_READ_VREF_R2); 5178c2ecf20Sopenharmony_ci if (ret < 0) 5188c2ecf20Sopenharmony_ci return ret; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci data->vref[1] = ret * data->vid_step[1]; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci return 0; 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic int 5268c2ecf20Sopenharmony_cimp2975_vref_offset_get(struct i2c_client *client, struct mp2975_data *data, 5278c2ecf20Sopenharmony_ci int page) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci int ret; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(client, MP2975_MFR_OVP_TH_SET); 5328c2ecf20Sopenharmony_ci if (ret < 0) 5338c2ecf20Sopenharmony_ci return ret; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci switch ((ret & GENMASK(5, 3)) >> 3) { 5368c2ecf20Sopenharmony_ci case 1: 5378c2ecf20Sopenharmony_ci data->vref_off[page] = 140; 5388c2ecf20Sopenharmony_ci break; 5398c2ecf20Sopenharmony_ci case 2: 5408c2ecf20Sopenharmony_ci data->vref_off[page] = 220; 5418c2ecf20Sopenharmony_ci break; 5428c2ecf20Sopenharmony_ci case 4: 5438c2ecf20Sopenharmony_ci data->vref_off[page] = 400; 5448c2ecf20Sopenharmony_ci break; 5458c2ecf20Sopenharmony_ci default: 5468c2ecf20Sopenharmony_ci return -EINVAL; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci return 0; 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cistatic int 5528c2ecf20Sopenharmony_cimp2975_vout_max_get(struct i2c_client *client, struct mp2975_data *data, 5538c2ecf20Sopenharmony_ci struct pmbus_driver_info *info, int page) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci int ret; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* Get maximum reference voltage of VID-DAC in VID format. */ 5588c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(client, PMBUS_VOUT_MAX); 5598c2ecf20Sopenharmony_ci if (ret < 0) 5608c2ecf20Sopenharmony_ci return ret; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci data->vout_max[page] = mp2975_vid2direct(info->vrm_version[page], ret & 5638c2ecf20Sopenharmony_ci GENMASK(8, 0)); 5648c2ecf20Sopenharmony_ci return 0; 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic int 5688c2ecf20Sopenharmony_cimp2975_identify_vout_format(struct i2c_client *client, 5698c2ecf20Sopenharmony_ci struct mp2975_data *data, int page) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci int ret; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(client, MP2975_MFR_DC_LOOP_CTRL); 5748c2ecf20Sopenharmony_ci if (ret < 0) 5758c2ecf20Sopenharmony_ci return ret; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci if (ret & MP2975_VOUT_FORMAT) 5788c2ecf20Sopenharmony_ci data->vout_format[page] = vid; 5798c2ecf20Sopenharmony_ci else 5808c2ecf20Sopenharmony_ci data->vout_format[page] = direct; 5818c2ecf20Sopenharmony_ci return 0; 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cistatic int 5858c2ecf20Sopenharmony_cimp2975_vout_ov_scale_get(struct i2c_client *client, struct mp2975_data *data, 5868c2ecf20Sopenharmony_ci struct pmbus_driver_info *info) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci int thres_dev, sense_ampl, ret; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); 5918c2ecf20Sopenharmony_ci if (ret < 0) 5928c2ecf20Sopenharmony_ci return ret; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci /* 5958c2ecf20Sopenharmony_ci * Get divider for over- and under-voltage protection thresholds 5968c2ecf20Sopenharmony_ci * configuration from the Advanced Options of Auto Phase Shedding and 5978c2ecf20Sopenharmony_ci * decay register. 5988c2ecf20Sopenharmony_ci */ 5998c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(client, MP2975_MFR_APS_DECAY_ADV); 6008c2ecf20Sopenharmony_ci if (ret < 0) 6018c2ecf20Sopenharmony_ci return ret; 6028c2ecf20Sopenharmony_ci thres_dev = ret & MP2975_PRT_THRES_DIV_OV_EN ? MP2975_PROT_DEV_OV_ON : 6038c2ecf20Sopenharmony_ci MP2975_PROT_DEV_OV_OFF; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci /* Select the gain of remote sense amplifier. */ 6068c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(client, PMBUS_VOUT_SCALE_LOOP); 6078c2ecf20Sopenharmony_ci if (ret < 0) 6088c2ecf20Sopenharmony_ci return ret; 6098c2ecf20Sopenharmony_ci sense_ampl = ret & MP2975_SENSE_AMPL ? MP2975_SENSE_AMPL_HALF : 6108c2ecf20Sopenharmony_ci MP2975_SENSE_AMPL_UNIT; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci data->vout_scale = sense_ampl * thres_dev; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci return 0; 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cistatic int 6188c2ecf20Sopenharmony_cimp2975_vout_per_rail_config_get(struct i2c_client *client, 6198c2ecf20Sopenharmony_ci struct mp2975_data *data, 6208c2ecf20Sopenharmony_ci struct pmbus_driver_info *info) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci int i, ret; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci for (i = 0; i < data->info.pages; i++) { 6258c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i); 6268c2ecf20Sopenharmony_ci if (ret < 0) 6278c2ecf20Sopenharmony_ci return ret; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* Obtain voltage reference offsets. */ 6308c2ecf20Sopenharmony_ci ret = mp2975_vref_offset_get(client, data, i); 6318c2ecf20Sopenharmony_ci if (ret < 0) 6328c2ecf20Sopenharmony_ci return ret; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci /* Obtain maximum voltage values. */ 6358c2ecf20Sopenharmony_ci ret = mp2975_vout_max_get(client, data, info, i); 6368c2ecf20Sopenharmony_ci if (ret < 0) 6378c2ecf20Sopenharmony_ci return ret; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci /* 6408c2ecf20Sopenharmony_ci * Get VOUT format for READ_VOUT command : VID or direct. 6418c2ecf20Sopenharmony_ci * Pages on same device can be configured with different 6428c2ecf20Sopenharmony_ci * formats. 6438c2ecf20Sopenharmony_ci */ 6448c2ecf20Sopenharmony_ci ret = mp2975_identify_vout_format(client, data, i); 6458c2ecf20Sopenharmony_ci if (ret < 0) 6468c2ecf20Sopenharmony_ci return ret; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci /* 6498c2ecf20Sopenharmony_ci * Set over-voltage fixed value. Thresholds are provided as 6508c2ecf20Sopenharmony_ci * fixed value, and tracking value. The minimum of them are 6518c2ecf20Sopenharmony_ci * exposed as over-voltage critical threshold. 6528c2ecf20Sopenharmony_ci */ 6538c2ecf20Sopenharmony_ci data->vout_ov_fixed[i] = data->vref[i] + 6548c2ecf20Sopenharmony_ci DIV_ROUND_CLOSEST(data->vref_off[i] * 6558c2ecf20Sopenharmony_ci data->vout_scale, 6568c2ecf20Sopenharmony_ci 10); 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci return 0; 6608c2ecf20Sopenharmony_ci} 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_cistatic struct pmbus_driver_info mp2975_info = { 6638c2ecf20Sopenharmony_ci .pages = 1, 6648c2ecf20Sopenharmony_ci .format[PSC_VOLTAGE_IN] = linear, 6658c2ecf20Sopenharmony_ci .format[PSC_VOLTAGE_OUT] = direct, 6668c2ecf20Sopenharmony_ci .format[PSC_TEMPERATURE] = direct, 6678c2ecf20Sopenharmony_ci .format[PSC_CURRENT_IN] = linear, 6688c2ecf20Sopenharmony_ci .format[PSC_CURRENT_OUT] = direct, 6698c2ecf20Sopenharmony_ci .format[PSC_POWER] = direct, 6708c2ecf20Sopenharmony_ci .m[PSC_TEMPERATURE] = 1, 6718c2ecf20Sopenharmony_ci .m[PSC_VOLTAGE_OUT] = 1, 6728c2ecf20Sopenharmony_ci .R[PSC_VOLTAGE_OUT] = 3, 6738c2ecf20Sopenharmony_ci .m[PSC_CURRENT_OUT] = 1, 6748c2ecf20Sopenharmony_ci .m[PSC_POWER] = 1, 6758c2ecf20Sopenharmony_ci .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | 6768c2ecf20Sopenharmony_ci PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | 6778c2ecf20Sopenharmony_ci PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_POUT | 6788c2ecf20Sopenharmony_ci PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT | PMBUS_PHASE_VIRTUAL, 6798c2ecf20Sopenharmony_ci .read_byte_data = mp2975_read_byte_data, 6808c2ecf20Sopenharmony_ci .read_word_data = mp2975_read_word_data, 6818c2ecf20Sopenharmony_ci}; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_cistatic int mp2975_probe(struct i2c_client *client) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci struct pmbus_driver_info *info; 6868c2ecf20Sopenharmony_ci struct mp2975_data *data; 6878c2ecf20Sopenharmony_ci int ret; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci data = devm_kzalloc(&client->dev, sizeof(struct mp2975_data), 6908c2ecf20Sopenharmony_ci GFP_KERNEL); 6918c2ecf20Sopenharmony_ci if (!data) 6928c2ecf20Sopenharmony_ci return -ENOMEM; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci memcpy(&data->info, &mp2975_info, sizeof(*info)); 6958c2ecf20Sopenharmony_ci info = &data->info; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci /* Identify multiphase configuration for rail 2. */ 6988c2ecf20Sopenharmony_ci ret = mp2975_identify_multiphase_rail2(client); 6998c2ecf20Sopenharmony_ci if (ret < 0) 7008c2ecf20Sopenharmony_ci return ret; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci if (ret) { 7038c2ecf20Sopenharmony_ci /* Two rails are connected. */ 7048c2ecf20Sopenharmony_ci data->info.pages = MP2975_PAGE_NUM; 7058c2ecf20Sopenharmony_ci data->info.phases[1] = ret; 7068c2ecf20Sopenharmony_ci data->info.func[1] = MP2975_RAIL2_FUNC; 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci /* Identify multiphase configuration. */ 7108c2ecf20Sopenharmony_ci ret = mp2975_identify_multiphase(client, data, info); 7118c2ecf20Sopenharmony_ci if (ret) 7128c2ecf20Sopenharmony_ci return ret; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci /* Identify VID setting per rail. */ 7158c2ecf20Sopenharmony_ci ret = mp2975_identify_rails_vid(client, data, info); 7168c2ecf20Sopenharmony_ci if (ret < 0) 7178c2ecf20Sopenharmony_ci return ret; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci /* Obtain current sense gain of power stage. */ 7208c2ecf20Sopenharmony_ci ret = mp2975_current_sense_gain_get(client, data); 7218c2ecf20Sopenharmony_ci if (ret) 7228c2ecf20Sopenharmony_ci return ret; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci /* Obtain voltage reference values. */ 7258c2ecf20Sopenharmony_ci ret = mp2975_vref_get(client, data, info); 7268c2ecf20Sopenharmony_ci if (ret) 7278c2ecf20Sopenharmony_ci return ret; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci /* Obtain vout over-voltage scales. */ 7308c2ecf20Sopenharmony_ci ret = mp2975_vout_ov_scale_get(client, data, info); 7318c2ecf20Sopenharmony_ci if (ret < 0) 7328c2ecf20Sopenharmony_ci return ret; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci /* Obtain offsets, maximum and format for vout. */ 7358c2ecf20Sopenharmony_ci ret = mp2975_vout_per_rail_config_get(client, data, info); 7368c2ecf20Sopenharmony_ci if (ret) 7378c2ecf20Sopenharmony_ci return ret; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci return pmbus_do_probe(client, info); 7408c2ecf20Sopenharmony_ci} 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_cistatic const struct i2c_device_id mp2975_id[] = { 7438c2ecf20Sopenharmony_ci {"mp2975", 0}, 7448c2ecf20Sopenharmony_ci {} 7458c2ecf20Sopenharmony_ci}; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, mp2975_id); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_cistatic const struct of_device_id __maybe_unused mp2975_of_match[] = { 7508c2ecf20Sopenharmony_ci {.compatible = "mps,mp2975"}, 7518c2ecf20Sopenharmony_ci {} 7528c2ecf20Sopenharmony_ci}; 7538c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mp2975_of_match); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_cistatic struct i2c_driver mp2975_driver = { 7568c2ecf20Sopenharmony_ci .driver = { 7578c2ecf20Sopenharmony_ci .name = "mp2975", 7588c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(mp2975_of_match), 7598c2ecf20Sopenharmony_ci }, 7608c2ecf20Sopenharmony_ci .probe_new = mp2975_probe, 7618c2ecf20Sopenharmony_ci .remove = pmbus_do_remove, 7628c2ecf20Sopenharmony_ci .id_table = mp2975_id, 7638c2ecf20Sopenharmony_ci}; 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_cimodule_i2c_driver(mp2975_driver); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vadim Pasternak <vadimp@nvidia.com>"); 7688c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PMBus driver for MPS MP2975 device"); 7698c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 770