162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2020 Nvidia Technologies Ltd. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/err.h> 962306a36Sopenharmony_ci#include <linux/i2c.h> 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/of_device.h> 1462306a36Sopenharmony_ci#include "pmbus.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* Vendor specific registers. */ 1762306a36Sopenharmony_ci#define MP2975_MFR_APS_HYS_R2 0x0d 1862306a36Sopenharmony_ci#define MP2975_MFR_SLOPE_TRIM3 0x1d 1962306a36Sopenharmony_ci#define MP2975_MFR_VR_MULTI_CONFIG_R1 0x0d 2062306a36Sopenharmony_ci#define MP2975_MFR_VR_MULTI_CONFIG_R2 0x1d 2162306a36Sopenharmony_ci#define MP2975_MFR_APS_DECAY_ADV 0x56 2262306a36Sopenharmony_ci#define MP2975_MFR_DC_LOOP_CTRL 0x59 2362306a36Sopenharmony_ci#define MP2975_MFR_OCP_UCP_PHASE_SET 0x65 2462306a36Sopenharmony_ci#define MP2975_MFR_VR_CONFIG1 0x68 2562306a36Sopenharmony_ci#define MP2975_MFR_READ_CS1_2 0x82 2662306a36Sopenharmony_ci#define MP2975_MFR_READ_CS3_4 0x83 2762306a36Sopenharmony_ci#define MP2975_MFR_READ_CS5_6 0x84 2862306a36Sopenharmony_ci#define MP2975_MFR_READ_CS7_8 0x85 2962306a36Sopenharmony_ci#define MP2975_MFR_READ_CS9_10 0x86 3062306a36Sopenharmony_ci#define MP2975_MFR_READ_CS11_12 0x87 3162306a36Sopenharmony_ci#define MP2975_MFR_READ_IOUT_PK 0x90 3262306a36Sopenharmony_ci#define MP2975_MFR_READ_POUT_PK 0x91 3362306a36Sopenharmony_ci#define MP2975_MFR_READ_VREF_R1 0xa1 3462306a36Sopenharmony_ci#define MP2975_MFR_READ_VREF_R2 0xa3 3562306a36Sopenharmony_ci#define MP2975_MFR_OVP_TH_SET 0xe5 3662306a36Sopenharmony_ci#define MP2975_MFR_UVP_SET 0xe6 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define MP2973_MFR_RESO_SET 0xc7 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define MP2975_VOUT_FORMAT BIT(15) 4162306a36Sopenharmony_ci#define MP2975_VID_STEP_SEL_R1 BIT(4) 4262306a36Sopenharmony_ci#define MP2975_IMVP9_EN_R1 BIT(13) 4362306a36Sopenharmony_ci#define MP2975_VID_STEP_SEL_R2 BIT(3) 4462306a36Sopenharmony_ci#define MP2975_IMVP9_EN_R2 BIT(12) 4562306a36Sopenharmony_ci#define MP2975_PRT_THRES_DIV_OV_EN BIT(14) 4662306a36Sopenharmony_ci#define MP2975_DRMOS_KCS GENMASK(13, 12) 4762306a36Sopenharmony_ci#define MP2975_PROT_DEV_OV_OFF 10 4862306a36Sopenharmony_ci#define MP2975_PROT_DEV_OV_ON 5 4962306a36Sopenharmony_ci#define MP2975_SENSE_AMPL BIT(11) 5062306a36Sopenharmony_ci#define MP2975_SENSE_AMPL_UNIT 1 5162306a36Sopenharmony_ci#define MP2975_SENSE_AMPL_HALF 2 5262306a36Sopenharmony_ci#define MP2975_VIN_UV_LIMIT_UNIT 8 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define MP2973_VOUT_FORMAT_R1 GENMASK(7, 6) 5562306a36Sopenharmony_ci#define MP2973_VOUT_FORMAT_R2 GENMASK(4, 3) 5662306a36Sopenharmony_ci#define MP2973_VOUT_FORMAT_DIRECT_R1 BIT(7) 5762306a36Sopenharmony_ci#define MP2973_VOUT_FORMAT_LINEAR_R1 BIT(6) 5862306a36Sopenharmony_ci#define MP2973_VOUT_FORMAT_DIRECT_R2 BIT(4) 5962306a36Sopenharmony_ci#define MP2973_VOUT_FORMAT_LINEAR_R2 BIT(3) 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define MP2973_MFR_VR_MULTI_CONFIG_R1 0x0d 6262306a36Sopenharmony_ci#define MP2973_MFR_VR_MULTI_CONFIG_R2 0x1d 6362306a36Sopenharmony_ci#define MP2973_VID_STEP_SEL_R1 BIT(4) 6462306a36Sopenharmony_ci#define MP2973_IMVP9_EN_R1 BIT(14) 6562306a36Sopenharmony_ci#define MP2973_VID_STEP_SEL_R2 BIT(3) 6662306a36Sopenharmony_ci#define MP2973_IMVP9_EN_R2 BIT(13) 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define MP2973_MFR_OCP_TOTAL_SET 0x5f 6962306a36Sopenharmony_ci#define MP2973_OCP_TOTAL_CUR_MASK GENMASK(6, 0) 7062306a36Sopenharmony_ci#define MP2973_MFR_OCP_LEVEL_RES BIT(15) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define MP2973_MFR_READ_IOUT_PK 0x90 7362306a36Sopenharmony_ci#define MP2973_MFR_READ_POUT_PK 0x91 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci#define MP2975_MAX_PHASE_RAIL1 8 7662306a36Sopenharmony_ci#define MP2975_MAX_PHASE_RAIL2 4 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci#define MP2973_MAX_PHASE_RAIL1 14 7962306a36Sopenharmony_ci#define MP2973_MAX_PHASE_RAIL2 6 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#define MP2971_MAX_PHASE_RAIL1 8 8262306a36Sopenharmony_ci#define MP2971_MAX_PHASE_RAIL2 3 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define MP2975_PAGE_NUM 2 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define MP2975_RAIL2_FUNC (PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | \ 8762306a36Sopenharmony_ci PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | \ 8862306a36Sopenharmony_ci PMBUS_HAVE_POUT | PMBUS_PHASE_VIRTUAL) 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cienum chips { 9162306a36Sopenharmony_ci mp2971, mp2973, mp2975 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic const int mp2975_max_phases[][MP2975_PAGE_NUM] = { 9562306a36Sopenharmony_ci [mp2975] = { MP2975_MAX_PHASE_RAIL1, MP2975_MAX_PHASE_RAIL2 }, 9662306a36Sopenharmony_ci [mp2973] = { MP2973_MAX_PHASE_RAIL1, MP2973_MAX_PHASE_RAIL2 }, 9762306a36Sopenharmony_ci [mp2971] = { MP2971_MAX_PHASE_RAIL1, MP2971_MAX_PHASE_RAIL2 }, 9862306a36Sopenharmony_ci}; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistruct mp2975_data { 10162306a36Sopenharmony_ci struct pmbus_driver_info info; 10262306a36Sopenharmony_ci enum chips chip_id; 10362306a36Sopenharmony_ci int vout_scale; 10462306a36Sopenharmony_ci int max_phases[MP2975_PAGE_NUM]; 10562306a36Sopenharmony_ci int vid_step[MP2975_PAGE_NUM]; 10662306a36Sopenharmony_ci int vref[MP2975_PAGE_NUM]; 10762306a36Sopenharmony_ci int vref_off[MP2975_PAGE_NUM]; 10862306a36Sopenharmony_ci int vout_max[MP2975_PAGE_NUM]; 10962306a36Sopenharmony_ci int vout_ov_fixed[MP2975_PAGE_NUM]; 11062306a36Sopenharmony_ci int curr_sense_gain[MP2975_PAGE_NUM]; 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic const struct i2c_device_id mp2975_id[] = { 11462306a36Sopenharmony_ci {"mp2971", mp2971}, 11562306a36Sopenharmony_ci {"mp2973", mp2973}, 11662306a36Sopenharmony_ci {"mp2975", mp2975}, 11762306a36Sopenharmony_ci {} 11862306a36Sopenharmony_ci}; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, mp2975_id); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic const struct regulator_desc __maybe_unused mp2975_reg_desc[] = { 12362306a36Sopenharmony_ci PMBUS_REGULATOR("vout", 0), 12462306a36Sopenharmony_ci PMBUS_REGULATOR("vout", 1), 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci#define to_mp2975_data(x) container_of(x, struct mp2975_data, info) 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic int 13062306a36Sopenharmony_cimp2975_read_word_helper(struct i2c_client *client, int page, int phase, u8 reg, 13162306a36Sopenharmony_ci u16 mask) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci int ret = pmbus_read_word_data(client, page, phase, reg); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return (ret > 0) ? ret & mask : ret; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic int 13962306a36Sopenharmony_cimp2975_vid2direct(int vrf, int val) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci switch (vrf) { 14262306a36Sopenharmony_ci case vr12: 14362306a36Sopenharmony_ci if (val >= 0x01) 14462306a36Sopenharmony_ci return 250 + (val - 1) * 5; 14562306a36Sopenharmony_ci break; 14662306a36Sopenharmony_ci case vr13: 14762306a36Sopenharmony_ci if (val >= 0x01) 14862306a36Sopenharmony_ci return 500 + (val - 1) * 10; 14962306a36Sopenharmony_ci break; 15062306a36Sopenharmony_ci case imvp9: 15162306a36Sopenharmony_ci if (val >= 0x01) 15262306a36Sopenharmony_ci return 200 + (val - 1) * 10; 15362306a36Sopenharmony_ci break; 15462306a36Sopenharmony_ci default: 15562306a36Sopenharmony_ci return -EINVAL; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci return 0; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci#define MAX_LIN_MANTISSA (1023 * 1000) 16162306a36Sopenharmony_ci#define MIN_LIN_MANTISSA (511 * 1000) 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* Converts a milli-unit DIRECT value to LINEAR11 format */ 16462306a36Sopenharmony_cistatic u16 mp2975_data2reg_linear11(s64 val) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci s16 exponent = 0, mantissa; 16762306a36Sopenharmony_ci bool negative = false; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* simple case */ 17062306a36Sopenharmony_ci if (val == 0) 17162306a36Sopenharmony_ci return 0; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* Reduce large mantissa until it fits into 10 bit */ 17462306a36Sopenharmony_ci while (val >= MAX_LIN_MANTISSA && exponent < 15) { 17562306a36Sopenharmony_ci exponent++; 17662306a36Sopenharmony_ci val >>= 1; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci /* Increase small mantissa to improve precision */ 17962306a36Sopenharmony_ci while (val < MIN_LIN_MANTISSA && exponent > -15) { 18062306a36Sopenharmony_ci exponent--; 18162306a36Sopenharmony_ci val <<= 1; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* Convert mantissa from milli-units to units */ 18562306a36Sopenharmony_ci mantissa = clamp_val(DIV_ROUND_CLOSEST_ULL(val, 1000), 0, 0x3ff); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* restore sign */ 18862306a36Sopenharmony_ci if (negative) 18962306a36Sopenharmony_ci mantissa = -mantissa; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* Convert to 5 bit exponent, 11 bit mantissa */ 19262306a36Sopenharmony_ci return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic int 19662306a36Sopenharmony_cimp2975_read_phase(struct i2c_client *client, struct mp2975_data *data, 19762306a36Sopenharmony_ci int page, int phase, u8 reg) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci int ph_curr, ret; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci ret = pmbus_read_word_data(client, page, phase, reg); 20262306a36Sopenharmony_ci if (ret < 0) 20362306a36Sopenharmony_ci return ret; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (!((phase + 1) % MP2975_PAGE_NUM)) 20662306a36Sopenharmony_ci ret >>= 8; 20762306a36Sopenharmony_ci ret &= 0xff; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* 21062306a36Sopenharmony_ci * Output value is calculated as: (READ_CSx / 80 – 1.23) / (Kcs * Rcs) 21162306a36Sopenharmony_ci * where: 21262306a36Sopenharmony_ci * - Kcs is the DrMOS current sense gain of power stage, which is 21362306a36Sopenharmony_ci * obtained from the register MP2975_MFR_VR_CONFIG1, bits 13-12 with 21462306a36Sopenharmony_ci * the following selection of DrMOS (data->curr_sense_gain[page]): 21562306a36Sopenharmony_ci * 00b - 5µA/A, 01b - 8.5µA/A, 10b - 9.7µA/A, 11b - 10µA/A. 21662306a36Sopenharmony_ci * - Rcs is the internal phase current sense resistor which is constant 21762306a36Sopenharmony_ci * value 1kΩ. 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ci ph_curr = ret * 100 - 9800; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* 22262306a36Sopenharmony_ci * Current phase sensing, providing by the device is not accurate 22362306a36Sopenharmony_ci * for the light load. This because sampling of current occurrence of 22462306a36Sopenharmony_ci * bit weight has a big deviation for light load. For handling such 22562306a36Sopenharmony_ci * case phase current is represented as the maximum between the value 22662306a36Sopenharmony_ci * calculated above and total rail current divided by number phases. 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci ret = pmbus_read_word_data(client, page, phase, PMBUS_READ_IOUT); 22962306a36Sopenharmony_ci if (ret < 0) 23062306a36Sopenharmony_ci return ret; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci return max_t(int, DIV_ROUND_CLOSEST(ret, data->info.phases[page]), 23362306a36Sopenharmony_ci DIV_ROUND_CLOSEST(ph_curr, data->curr_sense_gain[page])); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic int 23762306a36Sopenharmony_cimp2975_read_phases(struct i2c_client *client, struct mp2975_data *data, 23862306a36Sopenharmony_ci int page, int phase) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci int ret; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (page) { 24362306a36Sopenharmony_ci switch (phase) { 24462306a36Sopenharmony_ci case 0 ... 1: 24562306a36Sopenharmony_ci ret = mp2975_read_phase(client, data, page, phase, 24662306a36Sopenharmony_ci MP2975_MFR_READ_CS7_8); 24762306a36Sopenharmony_ci break; 24862306a36Sopenharmony_ci case 2 ... 3: 24962306a36Sopenharmony_ci ret = mp2975_read_phase(client, data, page, phase, 25062306a36Sopenharmony_ci MP2975_MFR_READ_CS9_10); 25162306a36Sopenharmony_ci break; 25262306a36Sopenharmony_ci case 4 ... 5: 25362306a36Sopenharmony_ci ret = mp2975_read_phase(client, data, page, phase, 25462306a36Sopenharmony_ci MP2975_MFR_READ_CS11_12); 25562306a36Sopenharmony_ci break; 25662306a36Sopenharmony_ci default: 25762306a36Sopenharmony_ci return -ENODATA; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci } else { 26062306a36Sopenharmony_ci switch (phase) { 26162306a36Sopenharmony_ci case 0 ... 1: 26262306a36Sopenharmony_ci ret = mp2975_read_phase(client, data, page, phase, 26362306a36Sopenharmony_ci MP2975_MFR_READ_CS1_2); 26462306a36Sopenharmony_ci break; 26562306a36Sopenharmony_ci case 2 ... 3: 26662306a36Sopenharmony_ci ret = mp2975_read_phase(client, data, page, phase, 26762306a36Sopenharmony_ci MP2975_MFR_READ_CS3_4); 26862306a36Sopenharmony_ci break; 26962306a36Sopenharmony_ci case 4 ... 5: 27062306a36Sopenharmony_ci ret = mp2975_read_phase(client, data, page, phase, 27162306a36Sopenharmony_ci MP2975_MFR_READ_CS5_6); 27262306a36Sopenharmony_ci break; 27362306a36Sopenharmony_ci case 6 ... 7: 27462306a36Sopenharmony_ci ret = mp2975_read_phase(client, data, page, phase, 27562306a36Sopenharmony_ci MP2975_MFR_READ_CS7_8); 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci case 8 ... 9: 27862306a36Sopenharmony_ci ret = mp2975_read_phase(client, data, page, phase, 27962306a36Sopenharmony_ci MP2975_MFR_READ_CS9_10); 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci case 10 ... 11: 28262306a36Sopenharmony_ci ret = mp2975_read_phase(client, data, page, phase, 28362306a36Sopenharmony_ci MP2975_MFR_READ_CS11_12); 28462306a36Sopenharmony_ci break; 28562306a36Sopenharmony_ci default: 28662306a36Sopenharmony_ci return -ENODATA; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci return ret; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic int mp2973_read_word_data(struct i2c_client *client, int page, 29362306a36Sopenharmony_ci int phase, int reg) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci const struct pmbus_driver_info *info = pmbus_get_driver_info(client); 29662306a36Sopenharmony_ci struct mp2975_data *data = to_mp2975_data(info); 29762306a36Sopenharmony_ci int ret; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci switch (reg) { 30062306a36Sopenharmony_ci case PMBUS_STATUS_WORD: 30162306a36Sopenharmony_ci /* MP2973 & MP2971 return PGOOD instead of PB_STATUS_POWER_GOOD_N. */ 30262306a36Sopenharmony_ci ret = pmbus_read_word_data(client, page, phase, reg); 30362306a36Sopenharmony_ci ret ^= PB_STATUS_POWER_GOOD_N; 30462306a36Sopenharmony_ci break; 30562306a36Sopenharmony_ci case PMBUS_OT_FAULT_LIMIT: 30662306a36Sopenharmony_ci ret = mp2975_read_word_helper(client, page, phase, reg, 30762306a36Sopenharmony_ci GENMASK(7, 0)); 30862306a36Sopenharmony_ci break; 30962306a36Sopenharmony_ci case PMBUS_VIN_OV_FAULT_LIMIT: 31062306a36Sopenharmony_ci ret = mp2975_read_word_helper(client, page, phase, reg, 31162306a36Sopenharmony_ci GENMASK(7, 0)); 31262306a36Sopenharmony_ci if (ret < 0) 31362306a36Sopenharmony_ci return ret; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci ret = DIV_ROUND_CLOSEST(ret, MP2975_VIN_UV_LIMIT_UNIT); 31662306a36Sopenharmony_ci break; 31762306a36Sopenharmony_ci case PMBUS_VOUT_OV_FAULT_LIMIT: 31862306a36Sopenharmony_ci /* 31962306a36Sopenharmony_ci * MP2971 and mp2973 only supports tracking (ovp1) mode. 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_ci ret = mp2975_read_word_helper(client, page, phase, 32262306a36Sopenharmony_ci MP2975_MFR_OVP_TH_SET, 32362306a36Sopenharmony_ci GENMASK(2, 0)); 32462306a36Sopenharmony_ci if (ret < 0) 32562306a36Sopenharmony_ci return ret; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci ret = data->vout_max[page] + 50 * (ret + 1); 32862306a36Sopenharmony_ci break; 32962306a36Sopenharmony_ci case PMBUS_VOUT_UV_FAULT_LIMIT: 33062306a36Sopenharmony_ci ret = mp2975_read_word_helper(client, page, phase, reg, 33162306a36Sopenharmony_ci GENMASK(8, 0)); 33262306a36Sopenharmony_ci if (ret < 0) 33362306a36Sopenharmony_ci return ret; 33462306a36Sopenharmony_ci ret = mp2975_vid2direct(info->vrm_version[page], ret); 33562306a36Sopenharmony_ci break; 33662306a36Sopenharmony_ci case PMBUS_VIRT_READ_POUT_MAX: 33762306a36Sopenharmony_ci ret = pmbus_read_word_data(client, page, phase, 33862306a36Sopenharmony_ci MP2973_MFR_READ_POUT_PK); 33962306a36Sopenharmony_ci break; 34062306a36Sopenharmony_ci case PMBUS_VIRT_READ_IOUT_MAX: 34162306a36Sopenharmony_ci ret = pmbus_read_word_data(client, page, phase, 34262306a36Sopenharmony_ci MP2973_MFR_READ_IOUT_PK); 34362306a36Sopenharmony_ci break; 34462306a36Sopenharmony_ci case PMBUS_IOUT_OC_FAULT_LIMIT: 34562306a36Sopenharmony_ci ret = mp2975_read_word_helper(client, page, phase, 34662306a36Sopenharmony_ci MP2973_MFR_OCP_TOTAL_SET, 34762306a36Sopenharmony_ci GENMASK(15, 0)); 34862306a36Sopenharmony_ci if (ret < 0) 34962306a36Sopenharmony_ci return ret; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (ret & MP2973_MFR_OCP_LEVEL_RES) 35262306a36Sopenharmony_ci ret = 2 * (ret & MP2973_OCP_TOTAL_CUR_MASK); 35362306a36Sopenharmony_ci else 35462306a36Sopenharmony_ci ret = ret & MP2973_OCP_TOTAL_CUR_MASK; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci ret = mp2975_data2reg_linear11(ret * info->phases[page] * 1000); 35762306a36Sopenharmony_ci break; 35862306a36Sopenharmony_ci case PMBUS_UT_WARN_LIMIT: 35962306a36Sopenharmony_ci case PMBUS_UT_FAULT_LIMIT: 36062306a36Sopenharmony_ci case PMBUS_VIN_UV_WARN_LIMIT: 36162306a36Sopenharmony_ci case PMBUS_VIN_UV_FAULT_LIMIT: 36262306a36Sopenharmony_ci case PMBUS_VOUT_UV_WARN_LIMIT: 36362306a36Sopenharmony_ci case PMBUS_VOUT_OV_WARN_LIMIT: 36462306a36Sopenharmony_ci case PMBUS_VIN_OV_WARN_LIMIT: 36562306a36Sopenharmony_ci case PMBUS_IIN_OC_FAULT_LIMIT: 36662306a36Sopenharmony_ci case PMBUS_IOUT_OC_LV_FAULT_LIMIT: 36762306a36Sopenharmony_ci case PMBUS_IOUT_OC_WARN_LIMIT: 36862306a36Sopenharmony_ci case PMBUS_IOUT_UC_FAULT_LIMIT: 36962306a36Sopenharmony_ci case PMBUS_POUT_OP_FAULT_LIMIT: 37062306a36Sopenharmony_ci case PMBUS_POUT_OP_WARN_LIMIT: 37162306a36Sopenharmony_ci case PMBUS_PIN_OP_WARN_LIMIT: 37262306a36Sopenharmony_ci return -ENXIO; 37362306a36Sopenharmony_ci default: 37462306a36Sopenharmony_ci return -ENODATA; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci return ret; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic int mp2975_read_word_data(struct i2c_client *client, int page, 38162306a36Sopenharmony_ci int phase, int reg) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci const struct pmbus_driver_info *info = pmbus_get_driver_info(client); 38462306a36Sopenharmony_ci struct mp2975_data *data = to_mp2975_data(info); 38562306a36Sopenharmony_ci int ret; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci switch (reg) { 38862306a36Sopenharmony_ci case PMBUS_OT_FAULT_LIMIT: 38962306a36Sopenharmony_ci ret = mp2975_read_word_helper(client, page, phase, reg, 39062306a36Sopenharmony_ci GENMASK(7, 0)); 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci case PMBUS_VIN_OV_FAULT_LIMIT: 39362306a36Sopenharmony_ci ret = mp2975_read_word_helper(client, page, phase, reg, 39462306a36Sopenharmony_ci GENMASK(7, 0)); 39562306a36Sopenharmony_ci if (ret < 0) 39662306a36Sopenharmony_ci return ret; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci ret = DIV_ROUND_CLOSEST(ret, MP2975_VIN_UV_LIMIT_UNIT); 39962306a36Sopenharmony_ci break; 40062306a36Sopenharmony_ci case PMBUS_VOUT_OV_FAULT_LIMIT: 40162306a36Sopenharmony_ci /* 40262306a36Sopenharmony_ci * Register provides two values for over-voltage protection 40362306a36Sopenharmony_ci * threshold for fixed (ovp2) and tracking (ovp1) modes. The 40462306a36Sopenharmony_ci * minimum of these two values is provided as over-voltage 40562306a36Sopenharmony_ci * fault alarm. 40662306a36Sopenharmony_ci */ 40762306a36Sopenharmony_ci ret = mp2975_read_word_helper(client, page, phase, 40862306a36Sopenharmony_ci MP2975_MFR_OVP_TH_SET, 40962306a36Sopenharmony_ci GENMASK(2, 0)); 41062306a36Sopenharmony_ci if (ret < 0) 41162306a36Sopenharmony_ci return ret; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci ret = min_t(int, data->vout_max[page] + 50 * (ret + 1), 41462306a36Sopenharmony_ci data->vout_ov_fixed[page]); 41562306a36Sopenharmony_ci break; 41662306a36Sopenharmony_ci case PMBUS_VOUT_UV_FAULT_LIMIT: 41762306a36Sopenharmony_ci ret = mp2975_read_word_helper(client, page, phase, 41862306a36Sopenharmony_ci MP2975_MFR_UVP_SET, 41962306a36Sopenharmony_ci GENMASK(2, 0)); 42062306a36Sopenharmony_ci if (ret < 0) 42162306a36Sopenharmony_ci return ret; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci ret = DIV_ROUND_CLOSEST(data->vref[page] * 10 - 50 * 42462306a36Sopenharmony_ci (ret + 1) * data->vout_scale, 10); 42562306a36Sopenharmony_ci break; 42662306a36Sopenharmony_ci case PMBUS_VIRT_READ_POUT_MAX: 42762306a36Sopenharmony_ci ret = mp2975_read_word_helper(client, page, phase, 42862306a36Sopenharmony_ci MP2975_MFR_READ_POUT_PK, 42962306a36Sopenharmony_ci GENMASK(12, 0)); 43062306a36Sopenharmony_ci if (ret < 0) 43162306a36Sopenharmony_ci return ret; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci ret = DIV_ROUND_CLOSEST(ret, 4); 43462306a36Sopenharmony_ci break; 43562306a36Sopenharmony_ci case PMBUS_VIRT_READ_IOUT_MAX: 43662306a36Sopenharmony_ci ret = mp2975_read_word_helper(client, page, phase, 43762306a36Sopenharmony_ci MP2975_MFR_READ_IOUT_PK, 43862306a36Sopenharmony_ci GENMASK(12, 0)); 43962306a36Sopenharmony_ci if (ret < 0) 44062306a36Sopenharmony_ci return ret; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci ret = DIV_ROUND_CLOSEST(ret, 4); 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci case PMBUS_READ_IOUT: 44562306a36Sopenharmony_ci ret = mp2975_read_phases(client, data, page, phase); 44662306a36Sopenharmony_ci if (ret < 0) 44762306a36Sopenharmony_ci return ret; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci case PMBUS_UT_WARN_LIMIT: 45162306a36Sopenharmony_ci case PMBUS_UT_FAULT_LIMIT: 45262306a36Sopenharmony_ci case PMBUS_VIN_UV_WARN_LIMIT: 45362306a36Sopenharmony_ci case PMBUS_VIN_UV_FAULT_LIMIT: 45462306a36Sopenharmony_ci case PMBUS_VOUT_UV_WARN_LIMIT: 45562306a36Sopenharmony_ci case PMBUS_VOUT_OV_WARN_LIMIT: 45662306a36Sopenharmony_ci case PMBUS_VIN_OV_WARN_LIMIT: 45762306a36Sopenharmony_ci case PMBUS_IIN_OC_FAULT_LIMIT: 45862306a36Sopenharmony_ci case PMBUS_IOUT_OC_LV_FAULT_LIMIT: 45962306a36Sopenharmony_ci case PMBUS_IIN_OC_WARN_LIMIT: 46062306a36Sopenharmony_ci case PMBUS_IOUT_OC_WARN_LIMIT: 46162306a36Sopenharmony_ci case PMBUS_IOUT_OC_FAULT_LIMIT: 46262306a36Sopenharmony_ci case PMBUS_IOUT_UC_FAULT_LIMIT: 46362306a36Sopenharmony_ci case PMBUS_POUT_OP_FAULT_LIMIT: 46462306a36Sopenharmony_ci case PMBUS_POUT_OP_WARN_LIMIT: 46562306a36Sopenharmony_ci case PMBUS_PIN_OP_WARN_LIMIT: 46662306a36Sopenharmony_ci return -ENXIO; 46762306a36Sopenharmony_ci default: 46862306a36Sopenharmony_ci return -ENODATA; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci return ret; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic int mp2975_identify_multiphase_rail2(struct i2c_client *client, 47562306a36Sopenharmony_ci struct mp2975_data *data) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci int ret; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* 48062306a36Sopenharmony_ci * Identify multiphase for rail 2 - could be from 0 to data->max_phases[1]. 48162306a36Sopenharmony_ci * In case phase number is zero – only page zero is supported 48262306a36Sopenharmony_ci */ 48362306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2); 48462306a36Sopenharmony_ci if (ret < 0) 48562306a36Sopenharmony_ci return ret; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci ret = i2c_smbus_read_word_data(client, MP2975_MFR_VR_MULTI_CONFIG_R2); 48862306a36Sopenharmony_ci if (ret < 0) 48962306a36Sopenharmony_ci return ret; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci ret &= GENMASK(2, 0); 49262306a36Sopenharmony_ci return (ret >= data->max_phases[1]) ? data->max_phases[1] : ret; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic void mp2975_set_phase_rail1(struct pmbus_driver_info *info) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci int i; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci for (i = 0 ; i < info->phases[0]; i++) 50062306a36Sopenharmony_ci info->pfunc[i] = PMBUS_HAVE_IOUT; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic void 50462306a36Sopenharmony_cimp2975_set_phase_rail2(struct pmbus_driver_info *info, int num_phases) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci int i; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* Set phases for rail 2 from upper to lower. */ 50962306a36Sopenharmony_ci for (i = 1; i <= num_phases; i++) 51062306a36Sopenharmony_ci info->pfunc[MP2975_MAX_PHASE_RAIL1 - i] = PMBUS_HAVE_IOUT; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic int 51462306a36Sopenharmony_cimp2975_identify_multiphase(struct i2c_client *client, struct mp2975_data *data, 51562306a36Sopenharmony_ci struct pmbus_driver_info *info) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci int num_phases2, ret; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2); 52062306a36Sopenharmony_ci if (ret < 0) 52162306a36Sopenharmony_ci return ret; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Identify multiphase for rail 1 - could be from 1 to data->max_phases[0]. */ 52462306a36Sopenharmony_ci ret = i2c_smbus_read_word_data(client, MP2975_MFR_VR_MULTI_CONFIG_R1); 52562306a36Sopenharmony_ci if (ret <= 0) 52662306a36Sopenharmony_ci return ret; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci info->phases[0] = ret & GENMASK(3, 0); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* 53162306a36Sopenharmony_ci * The device provides a total of $n PWM pins, and can be configured 53262306a36Sopenharmony_ci * to different phase count applications for rail 1 and rail 2. 53362306a36Sopenharmony_ci * Rail 1 can be set to $n phases, while rail 2 can be set to less than 53462306a36Sopenharmony_ci * that. When rail 1’s phase count is configured as 0, rail 53562306a36Sopenharmony_ci * 1 operates with 1-phase DCM. When rail 2 phase count is configured 53662306a36Sopenharmony_ci * as 0, rail 2 is disabled. 53762306a36Sopenharmony_ci */ 53862306a36Sopenharmony_ci if (info->phases[0] > data->max_phases[0]) 53962306a36Sopenharmony_ci return -EINVAL; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci if (data->chip_id == mp2975) { 54262306a36Sopenharmony_ci mp2975_set_phase_rail1(info); 54362306a36Sopenharmony_ci num_phases2 = min(data->max_phases[0] - info->phases[0], 54462306a36Sopenharmony_ci data->max_phases[1]); 54562306a36Sopenharmony_ci if (info->phases[1] && info->phases[1] <= num_phases2) 54662306a36Sopenharmony_ci mp2975_set_phase_rail2(info, num_phases2); 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci return 0; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic int 55362306a36Sopenharmony_cimp2975_identify_vid(struct i2c_client *client, struct mp2975_data *data, 55462306a36Sopenharmony_ci struct pmbus_driver_info *info, u32 reg, int page, 55562306a36Sopenharmony_ci u32 imvp_bit, u32 vr_bit) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci int ret; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* Identify VID mode and step selection. */ 56062306a36Sopenharmony_ci ret = i2c_smbus_read_word_data(client, reg); 56162306a36Sopenharmony_ci if (ret < 0) 56262306a36Sopenharmony_ci return ret; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci if (ret & imvp_bit) { 56562306a36Sopenharmony_ci info->vrm_version[page] = imvp9; 56662306a36Sopenharmony_ci data->vid_step[page] = MP2975_PROT_DEV_OV_OFF; 56762306a36Sopenharmony_ci } else if (ret & vr_bit) { 56862306a36Sopenharmony_ci info->vrm_version[page] = vr12; 56962306a36Sopenharmony_ci data->vid_step[page] = MP2975_PROT_DEV_OV_ON; 57062306a36Sopenharmony_ci } else { 57162306a36Sopenharmony_ci info->vrm_version[page] = vr13; 57262306a36Sopenharmony_ci data->vid_step[page] = MP2975_PROT_DEV_OV_OFF; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci return 0; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic int 57962306a36Sopenharmony_cimp2975_identify_rails_vid(struct i2c_client *client, struct mp2975_data *data, 58062306a36Sopenharmony_ci struct pmbus_driver_info *info) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci int ret; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2); 58562306a36Sopenharmony_ci if (ret < 0) 58662306a36Sopenharmony_ci return ret; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* Identify VID mode for rail 1. */ 58962306a36Sopenharmony_ci ret = mp2975_identify_vid(client, data, info, 59062306a36Sopenharmony_ci MP2975_MFR_VR_MULTI_CONFIG_R1, 0, 59162306a36Sopenharmony_ci MP2975_IMVP9_EN_R1, MP2975_VID_STEP_SEL_R1); 59262306a36Sopenharmony_ci if (ret < 0) 59362306a36Sopenharmony_ci return ret; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci /* Identify VID mode for rail 2, if connected. */ 59662306a36Sopenharmony_ci if (info->phases[1]) 59762306a36Sopenharmony_ci ret = mp2975_identify_vid(client, data, info, 59862306a36Sopenharmony_ci MP2975_MFR_VR_MULTI_CONFIG_R2, 1, 59962306a36Sopenharmony_ci MP2975_IMVP9_EN_R2, 60062306a36Sopenharmony_ci MP2975_VID_STEP_SEL_R2); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci return ret; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic int 60662306a36Sopenharmony_cimp2973_identify_rails_vid(struct i2c_client *client, struct mp2975_data *data, 60762306a36Sopenharmony_ci struct pmbus_driver_info *info) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci int ret; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2); 61262306a36Sopenharmony_ci if (ret < 0) 61362306a36Sopenharmony_ci return ret; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci /* Identify VID mode for rail 1. */ 61662306a36Sopenharmony_ci ret = mp2975_identify_vid(client, data, info, 61762306a36Sopenharmony_ci MP2973_MFR_VR_MULTI_CONFIG_R1, 0, 61862306a36Sopenharmony_ci MP2973_IMVP9_EN_R1, MP2973_VID_STEP_SEL_R1); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (ret < 0) 62162306a36Sopenharmony_ci return ret; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci /* Identify VID mode for rail 2, if connected. */ 62462306a36Sopenharmony_ci if (info->phases[1]) 62562306a36Sopenharmony_ci ret = mp2975_identify_vid(client, data, info, 62662306a36Sopenharmony_ci MP2973_MFR_VR_MULTI_CONFIG_R2, 1, 62762306a36Sopenharmony_ci MP2973_IMVP9_EN_R2, 62862306a36Sopenharmony_ci MP2973_VID_STEP_SEL_R2); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci return ret; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic int 63462306a36Sopenharmony_cimp2975_current_sense_gain_get(struct i2c_client *client, 63562306a36Sopenharmony_ci struct mp2975_data *data) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci int i, ret; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci /* 64062306a36Sopenharmony_ci * Obtain DrMOS current sense gain of power stage from the register 64162306a36Sopenharmony_ci * MP2975_MFR_VR_CONFIG1, bits 13-12. The value is selected as below: 64262306a36Sopenharmony_ci * 00b - 5µA/A, 01b - 8.5µA/A, 10b - 9.7µA/A, 11b - 10µA/A. Other 64362306a36Sopenharmony_ci * values are invalid. 64462306a36Sopenharmony_ci */ 64562306a36Sopenharmony_ci for (i = 0 ; i < data->info.pages; i++) { 64662306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i); 64762306a36Sopenharmony_ci if (ret < 0) 64862306a36Sopenharmony_ci return ret; 64962306a36Sopenharmony_ci ret = i2c_smbus_read_word_data(client, 65062306a36Sopenharmony_ci MP2975_MFR_VR_CONFIG1); 65162306a36Sopenharmony_ci if (ret < 0) 65262306a36Sopenharmony_ci return ret; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci switch ((ret & MP2975_DRMOS_KCS) >> 12) { 65562306a36Sopenharmony_ci case 0: 65662306a36Sopenharmony_ci data->curr_sense_gain[i] = 50; 65762306a36Sopenharmony_ci break; 65862306a36Sopenharmony_ci case 1: 65962306a36Sopenharmony_ci data->curr_sense_gain[i] = 85; 66062306a36Sopenharmony_ci break; 66162306a36Sopenharmony_ci case 2: 66262306a36Sopenharmony_ci data->curr_sense_gain[i] = 97; 66362306a36Sopenharmony_ci break; 66462306a36Sopenharmony_ci default: 66562306a36Sopenharmony_ci data->curr_sense_gain[i] = 100; 66662306a36Sopenharmony_ci break; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci return 0; 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic int 67462306a36Sopenharmony_cimp2975_vref_get(struct i2c_client *client, struct mp2975_data *data, 67562306a36Sopenharmony_ci struct pmbus_driver_info *info) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci int ret; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 3); 68062306a36Sopenharmony_ci if (ret < 0) 68162306a36Sopenharmony_ci return ret; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci /* Get voltage reference value for rail 1. */ 68462306a36Sopenharmony_ci ret = i2c_smbus_read_word_data(client, MP2975_MFR_READ_VREF_R1); 68562306a36Sopenharmony_ci if (ret < 0) 68662306a36Sopenharmony_ci return ret; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci data->vref[0] = ret * data->vid_step[0]; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci /* Get voltage reference value for rail 2, if connected. */ 69162306a36Sopenharmony_ci if (data->info.pages == MP2975_PAGE_NUM) { 69262306a36Sopenharmony_ci ret = i2c_smbus_read_word_data(client, MP2975_MFR_READ_VREF_R2); 69362306a36Sopenharmony_ci if (ret < 0) 69462306a36Sopenharmony_ci return ret; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci data->vref[1] = ret * data->vid_step[1]; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci return 0; 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic int 70262306a36Sopenharmony_cimp2975_vref_offset_get(struct i2c_client *client, struct mp2975_data *data, 70362306a36Sopenharmony_ci int page) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci int ret; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci ret = i2c_smbus_read_word_data(client, MP2975_MFR_OVP_TH_SET); 70862306a36Sopenharmony_ci if (ret < 0) 70962306a36Sopenharmony_ci return ret; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci switch ((ret & GENMASK(5, 3)) >> 3) { 71262306a36Sopenharmony_ci case 1: 71362306a36Sopenharmony_ci data->vref_off[page] = 140; 71462306a36Sopenharmony_ci break; 71562306a36Sopenharmony_ci case 2: 71662306a36Sopenharmony_ci data->vref_off[page] = 220; 71762306a36Sopenharmony_ci break; 71862306a36Sopenharmony_ci case 4: 71962306a36Sopenharmony_ci data->vref_off[page] = 400; 72062306a36Sopenharmony_ci break; 72162306a36Sopenharmony_ci default: 72262306a36Sopenharmony_ci return -EINVAL; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci return 0; 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic int 72862306a36Sopenharmony_cimp2975_vout_max_get(struct i2c_client *client, struct mp2975_data *data, 72962306a36Sopenharmony_ci struct pmbus_driver_info *info, int page) 73062306a36Sopenharmony_ci{ 73162306a36Sopenharmony_ci int ret; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci /* Get maximum reference voltage of VID-DAC in VID format. */ 73462306a36Sopenharmony_ci ret = i2c_smbus_read_word_data(client, PMBUS_VOUT_MAX); 73562306a36Sopenharmony_ci if (ret < 0) 73662306a36Sopenharmony_ci return ret; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci data->vout_max[page] = mp2975_vid2direct(info->vrm_version[page], ret & 73962306a36Sopenharmony_ci GENMASK(8, 0)); 74062306a36Sopenharmony_ci return 0; 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_cistatic int 74462306a36Sopenharmony_cimp2975_set_vout_format(struct i2c_client *client, 74562306a36Sopenharmony_ci struct mp2975_data *data, int page) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci int ret, i; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci /* Enable DIRECT VOUT format 1mV/LSB */ 75062306a36Sopenharmony_ci if (data->chip_id == mp2975) { 75162306a36Sopenharmony_ci ret = i2c_smbus_read_word_data(client, MP2975_MFR_DC_LOOP_CTRL); 75262306a36Sopenharmony_ci if (ret < 0) 75362306a36Sopenharmony_ci return ret; 75462306a36Sopenharmony_ci if (ret & MP2975_VOUT_FORMAT) { 75562306a36Sopenharmony_ci ret &= ~MP2975_VOUT_FORMAT; 75662306a36Sopenharmony_ci ret = i2c_smbus_write_word_data(client, MP2975_MFR_DC_LOOP_CTRL, ret); 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci } else { 75962306a36Sopenharmony_ci ret = i2c_smbus_read_word_data(client, MP2973_MFR_RESO_SET); 76062306a36Sopenharmony_ci if (ret < 0) 76162306a36Sopenharmony_ci return ret; 76262306a36Sopenharmony_ci i = ret; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (page == 0) { 76562306a36Sopenharmony_ci i &= ~MP2973_VOUT_FORMAT_R1; 76662306a36Sopenharmony_ci i |= MP2973_VOUT_FORMAT_DIRECT_R1; 76762306a36Sopenharmony_ci } else { 76862306a36Sopenharmony_ci i &= ~MP2973_VOUT_FORMAT_R2; 76962306a36Sopenharmony_ci i |= MP2973_VOUT_FORMAT_DIRECT_R2; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci if (i != ret) 77262306a36Sopenharmony_ci ret = i2c_smbus_write_word_data(client, MP2973_MFR_RESO_SET, i); 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci return ret; 77562306a36Sopenharmony_ci} 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cistatic int 77862306a36Sopenharmony_cimp2975_vout_ov_scale_get(struct i2c_client *client, struct mp2975_data *data, 77962306a36Sopenharmony_ci struct pmbus_driver_info *info) 78062306a36Sopenharmony_ci{ 78162306a36Sopenharmony_ci int thres_dev, sense_ampl, ret; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); 78462306a36Sopenharmony_ci if (ret < 0) 78562306a36Sopenharmony_ci return ret; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci /* 78862306a36Sopenharmony_ci * Get divider for over- and under-voltage protection thresholds 78962306a36Sopenharmony_ci * configuration from the Advanced Options of Auto Phase Shedding and 79062306a36Sopenharmony_ci * decay register. 79162306a36Sopenharmony_ci */ 79262306a36Sopenharmony_ci ret = i2c_smbus_read_word_data(client, MP2975_MFR_APS_DECAY_ADV); 79362306a36Sopenharmony_ci if (ret < 0) 79462306a36Sopenharmony_ci return ret; 79562306a36Sopenharmony_ci thres_dev = ret & MP2975_PRT_THRES_DIV_OV_EN ? MP2975_PROT_DEV_OV_ON : 79662306a36Sopenharmony_ci MP2975_PROT_DEV_OV_OFF; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci /* Select the gain of remote sense amplifier. */ 79962306a36Sopenharmony_ci ret = i2c_smbus_read_word_data(client, PMBUS_VOUT_SCALE_LOOP); 80062306a36Sopenharmony_ci if (ret < 0) 80162306a36Sopenharmony_ci return ret; 80262306a36Sopenharmony_ci sense_ampl = ret & MP2975_SENSE_AMPL ? MP2975_SENSE_AMPL_HALF : 80362306a36Sopenharmony_ci MP2975_SENSE_AMPL_UNIT; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci data->vout_scale = sense_ampl * thres_dev; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci return 0; 80862306a36Sopenharmony_ci} 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_cistatic int 81162306a36Sopenharmony_cimp2975_vout_per_rail_config_get(struct i2c_client *client, 81262306a36Sopenharmony_ci struct mp2975_data *data, 81362306a36Sopenharmony_ci struct pmbus_driver_info *info) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci int i, ret; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci for (i = 0; i < data->info.pages; i++) { 81862306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i); 81962306a36Sopenharmony_ci if (ret < 0) 82062306a36Sopenharmony_ci continue; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci /* Set VOUT format for READ_VOUT command : direct. */ 82362306a36Sopenharmony_ci ret = mp2975_set_vout_format(client, data, i); 82462306a36Sopenharmony_ci if (ret < 0) 82562306a36Sopenharmony_ci return ret; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci /* Obtain maximum voltage values. */ 82862306a36Sopenharmony_ci ret = mp2975_vout_max_get(client, data, info, i); 82962306a36Sopenharmony_ci if (ret < 0) 83062306a36Sopenharmony_ci return ret; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci /* Skip if reading Vref is unsupported */ 83362306a36Sopenharmony_ci if (data->chip_id != mp2975) 83462306a36Sopenharmony_ci continue; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci /* Obtain voltage reference offsets. */ 83762306a36Sopenharmony_ci ret = mp2975_vref_offset_get(client, data, i); 83862306a36Sopenharmony_ci if (ret < 0) 83962306a36Sopenharmony_ci return ret; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci /* 84262306a36Sopenharmony_ci * Set over-voltage fixed value. Thresholds are provided as 84362306a36Sopenharmony_ci * fixed value, and tracking value. The minimum of them are 84462306a36Sopenharmony_ci * exposed as over-voltage critical threshold. 84562306a36Sopenharmony_ci */ 84662306a36Sopenharmony_ci data->vout_ov_fixed[i] = data->vref[i] + 84762306a36Sopenharmony_ci DIV_ROUND_CLOSEST(data->vref_off[i] * 84862306a36Sopenharmony_ci data->vout_scale, 84962306a36Sopenharmony_ci 10); 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci return 0; 85362306a36Sopenharmony_ci} 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_cistatic struct pmbus_driver_info mp2975_info = { 85662306a36Sopenharmony_ci .pages = 1, 85762306a36Sopenharmony_ci .format[PSC_VOLTAGE_IN] = linear, 85862306a36Sopenharmony_ci .format[PSC_VOLTAGE_OUT] = direct, 85962306a36Sopenharmony_ci .format[PSC_TEMPERATURE] = direct, 86062306a36Sopenharmony_ci .format[PSC_CURRENT_IN] = linear, 86162306a36Sopenharmony_ci .format[PSC_CURRENT_OUT] = direct, 86262306a36Sopenharmony_ci .format[PSC_POWER] = direct, 86362306a36Sopenharmony_ci .m[PSC_TEMPERATURE] = 1, 86462306a36Sopenharmony_ci .m[PSC_VOLTAGE_OUT] = 1, 86562306a36Sopenharmony_ci .R[PSC_VOLTAGE_OUT] = 3, 86662306a36Sopenharmony_ci .m[PSC_CURRENT_OUT] = 1, 86762306a36Sopenharmony_ci .m[PSC_POWER] = 1, 86862306a36Sopenharmony_ci .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | 86962306a36Sopenharmony_ci PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | 87062306a36Sopenharmony_ci PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_POUT | 87162306a36Sopenharmony_ci PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT | PMBUS_PHASE_VIRTUAL, 87262306a36Sopenharmony_ci .read_word_data = mp2975_read_word_data, 87362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SENSORS_MP2975_REGULATOR) 87462306a36Sopenharmony_ci .num_regulators = 1, 87562306a36Sopenharmony_ci .reg_desc = mp2975_reg_desc, 87662306a36Sopenharmony_ci#endif 87762306a36Sopenharmony_ci}; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cistatic struct pmbus_driver_info mp2973_info = { 88062306a36Sopenharmony_ci .pages = 1, 88162306a36Sopenharmony_ci .format[PSC_VOLTAGE_IN] = linear, 88262306a36Sopenharmony_ci .format[PSC_VOLTAGE_OUT] = direct, 88362306a36Sopenharmony_ci .format[PSC_TEMPERATURE] = linear, 88462306a36Sopenharmony_ci .format[PSC_CURRENT_IN] = linear, 88562306a36Sopenharmony_ci .format[PSC_CURRENT_OUT] = linear, 88662306a36Sopenharmony_ci .format[PSC_POWER] = linear, 88762306a36Sopenharmony_ci .m[PSC_VOLTAGE_OUT] = 1, 88862306a36Sopenharmony_ci .R[PSC_VOLTAGE_OUT] = 3, 88962306a36Sopenharmony_ci .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | 89062306a36Sopenharmony_ci PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | 89162306a36Sopenharmony_ci PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_POUT | 89262306a36Sopenharmony_ci PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT, 89362306a36Sopenharmony_ci .read_word_data = mp2973_read_word_data, 89462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SENSORS_MP2975_REGULATOR) 89562306a36Sopenharmony_ci .num_regulators = 1, 89662306a36Sopenharmony_ci .reg_desc = mp2975_reg_desc, 89762306a36Sopenharmony_ci#endif 89862306a36Sopenharmony_ci}; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_cistatic int mp2975_probe(struct i2c_client *client) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci struct pmbus_driver_info *info; 90362306a36Sopenharmony_ci struct mp2975_data *data; 90462306a36Sopenharmony_ci int ret; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci data = devm_kzalloc(&client->dev, sizeof(struct mp2975_data), 90762306a36Sopenharmony_ci GFP_KERNEL); 90862306a36Sopenharmony_ci if (!data) 90962306a36Sopenharmony_ci return -ENOMEM; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci if (client->dev.of_node) 91262306a36Sopenharmony_ci data->chip_id = (enum chips)(unsigned long)of_device_get_match_data(&client->dev); 91362306a36Sopenharmony_ci else 91462306a36Sopenharmony_ci data->chip_id = i2c_match_id(mp2975_id, client)->driver_data; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci memcpy(data->max_phases, mp2975_max_phases[data->chip_id], 91762306a36Sopenharmony_ci sizeof(data->max_phases)); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci if (data->chip_id == mp2975) 92062306a36Sopenharmony_ci memcpy(&data->info, &mp2975_info, sizeof(*info)); 92162306a36Sopenharmony_ci else 92262306a36Sopenharmony_ci memcpy(&data->info, &mp2973_info, sizeof(*info)); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci info = &data->info; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci /* Identify multiphase configuration for rail 2. */ 92762306a36Sopenharmony_ci ret = mp2975_identify_multiphase_rail2(client, data); 92862306a36Sopenharmony_ci if (ret < 0) 92962306a36Sopenharmony_ci return ret; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci if (ret) { 93262306a36Sopenharmony_ci /* Two rails are connected. */ 93362306a36Sopenharmony_ci data->info.pages = MP2975_PAGE_NUM; 93462306a36Sopenharmony_ci data->info.phases[1] = ret; 93562306a36Sopenharmony_ci data->info.func[1] = MP2975_RAIL2_FUNC; 93662306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SENSORS_MP2975_REGULATOR)) 93762306a36Sopenharmony_ci data->info.num_regulators = MP2975_PAGE_NUM; 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci /* Identify multiphase configuration. */ 94162306a36Sopenharmony_ci ret = mp2975_identify_multiphase(client, data, info); 94262306a36Sopenharmony_ci if (ret) 94362306a36Sopenharmony_ci return ret; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci if (data->chip_id == mp2975) { 94662306a36Sopenharmony_ci /* Identify VID setting per rail. */ 94762306a36Sopenharmony_ci ret = mp2975_identify_rails_vid(client, data, info); 94862306a36Sopenharmony_ci if (ret < 0) 94962306a36Sopenharmony_ci return ret; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci /* Obtain current sense gain of power stage. */ 95262306a36Sopenharmony_ci ret = mp2975_current_sense_gain_get(client, data); 95362306a36Sopenharmony_ci if (ret) 95462306a36Sopenharmony_ci return ret; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci /* Obtain voltage reference values. */ 95762306a36Sopenharmony_ci ret = mp2975_vref_get(client, data, info); 95862306a36Sopenharmony_ci if (ret) 95962306a36Sopenharmony_ci return ret; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci /* Obtain vout over-voltage scales. */ 96262306a36Sopenharmony_ci ret = mp2975_vout_ov_scale_get(client, data, info); 96362306a36Sopenharmony_ci if (ret < 0) 96462306a36Sopenharmony_ci return ret; 96562306a36Sopenharmony_ci } else { 96662306a36Sopenharmony_ci /* Identify VID setting per rail. */ 96762306a36Sopenharmony_ci ret = mp2973_identify_rails_vid(client, data, info); 96862306a36Sopenharmony_ci if (ret < 0) 96962306a36Sopenharmony_ci return ret; 97062306a36Sopenharmony_ci } 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci /* Obtain offsets, maximum and format for vout. */ 97362306a36Sopenharmony_ci ret = mp2975_vout_per_rail_config_get(client, data, info); 97462306a36Sopenharmony_ci if (ret) 97562306a36Sopenharmony_ci return ret; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci return pmbus_do_probe(client, info); 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_cistatic const struct of_device_id __maybe_unused mp2975_of_match[] = { 98162306a36Sopenharmony_ci {.compatible = "mps,mp2971", .data = (void *)mp2971}, 98262306a36Sopenharmony_ci {.compatible = "mps,mp2973", .data = (void *)mp2973}, 98362306a36Sopenharmony_ci {.compatible = "mps,mp2975", .data = (void *)mp2975}, 98462306a36Sopenharmony_ci {} 98562306a36Sopenharmony_ci}; 98662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mp2975_of_match); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_cistatic struct i2c_driver mp2975_driver = { 98962306a36Sopenharmony_ci .driver = { 99062306a36Sopenharmony_ci .name = "mp2975", 99162306a36Sopenharmony_ci .of_match_table = of_match_ptr(mp2975_of_match), 99262306a36Sopenharmony_ci }, 99362306a36Sopenharmony_ci .probe = mp2975_probe, 99462306a36Sopenharmony_ci .id_table = mp2975_id, 99562306a36Sopenharmony_ci}; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_cimodule_i2c_driver(mp2975_driver); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ciMODULE_AUTHOR("Vadim Pasternak <vadimp@nvidia.com>"); 100062306a36Sopenharmony_ciMODULE_DESCRIPTION("PMBus driver for MPS MP2975 device"); 100162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 100262306a36Sopenharmony_ciMODULE_IMPORT_NS(PMBUS); 1003