162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Hardware monitoring driver for Texas Instruments TPS53679 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2017 Mellanox Technologies. All rights reserved. 662306a36Sopenharmony_ci * Copyright (c) 2017 Vadim Pasternak <vadimp@mellanox.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/bits.h> 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/i2c.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include "pmbus.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cienum chips { 1962306a36Sopenharmony_ci tps53647, tps53667, tps53676, tps53679, tps53681, tps53688 2062306a36Sopenharmony_ci}; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define TPS53647_PAGE_NUM 1 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define TPS53676_USER_DATA_03 0xb3 2562306a36Sopenharmony_ci#define TPS53676_MAX_PHASES 7 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define TPS53679_PROT_VR12_5MV 0x01 /* VR12.0 mode, 5-mV DAC */ 2862306a36Sopenharmony_ci#define TPS53679_PROT_VR12_5_10MV 0x02 /* VR12.5 mode, 10-mV DAC */ 2962306a36Sopenharmony_ci#define TPS53679_PROT_VR13_10MV 0x04 /* VR13.0 mode, 10-mV DAC */ 3062306a36Sopenharmony_ci#define TPS53679_PROT_IMVP8_5MV 0x05 /* IMVP8 mode, 5-mV DAC */ 3162306a36Sopenharmony_ci#define TPS53679_PROT_VR13_5MV 0x07 /* VR13.0 mode, 5-mV DAC */ 3262306a36Sopenharmony_ci#define TPS53679_PAGE_NUM 2 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define TPS53681_DEVICE_ID 0x81 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define TPS53681_PMBUS_REVISION 0x33 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define TPS53681_MFR_SPECIFIC_20 0xe4 /* Number of phases, per page */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic const struct i2c_device_id tps53679_id[]; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic int tps53679_identify_mode(struct i2c_client *client, 4362306a36Sopenharmony_ci struct pmbus_driver_info *info) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci u8 vout_params; 4662306a36Sopenharmony_ci int i, ret; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci for (i = 0; i < info->pages; i++) { 4962306a36Sopenharmony_ci /* Read the register with VOUT scaling value.*/ 5062306a36Sopenharmony_ci ret = pmbus_read_byte_data(client, i, PMBUS_VOUT_MODE); 5162306a36Sopenharmony_ci if (ret < 0) 5262306a36Sopenharmony_ci return ret; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci vout_params = ret & GENMASK(4, 0); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci switch (vout_params) { 5762306a36Sopenharmony_ci case TPS53679_PROT_VR13_10MV: 5862306a36Sopenharmony_ci case TPS53679_PROT_VR12_5_10MV: 5962306a36Sopenharmony_ci info->vrm_version[i] = vr13; 6062306a36Sopenharmony_ci break; 6162306a36Sopenharmony_ci case TPS53679_PROT_VR13_5MV: 6262306a36Sopenharmony_ci case TPS53679_PROT_VR12_5MV: 6362306a36Sopenharmony_ci case TPS53679_PROT_IMVP8_5MV: 6462306a36Sopenharmony_ci info->vrm_version[i] = vr12; 6562306a36Sopenharmony_ci break; 6662306a36Sopenharmony_ci default: 6762306a36Sopenharmony_ci return -EINVAL; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci return 0; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic int tps53679_identify_phases(struct i2c_client *client, 7562306a36Sopenharmony_ci struct pmbus_driver_info *info) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci int ret; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* On TPS53681, only channel A provides per-phase output current */ 8062306a36Sopenharmony_ci ret = pmbus_read_byte_data(client, 0, TPS53681_MFR_SPECIFIC_20); 8162306a36Sopenharmony_ci if (ret < 0) 8262306a36Sopenharmony_ci return ret; 8362306a36Sopenharmony_ci info->phases[0] = (ret & 0x07) + 1; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int tps53679_identify_chip(struct i2c_client *client, 8962306a36Sopenharmony_ci u8 revision, u16 id) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci u8 buf[I2C_SMBUS_BLOCK_MAX]; 9262306a36Sopenharmony_ci int ret; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci ret = pmbus_read_byte_data(client, 0, PMBUS_REVISION); 9562306a36Sopenharmony_ci if (ret < 0) 9662306a36Sopenharmony_ci return ret; 9762306a36Sopenharmony_ci if (ret != revision) { 9862306a36Sopenharmony_ci dev_err(&client->dev, "Unexpected PMBus revision 0x%x\n", ret); 9962306a36Sopenharmony_ci return -ENODEV; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf); 10362306a36Sopenharmony_ci if (ret < 0) 10462306a36Sopenharmony_ci return ret; 10562306a36Sopenharmony_ci if (ret != 1 || buf[0] != id) { 10662306a36Sopenharmony_ci dev_err(&client->dev, "Unexpected device ID 0x%x\n", buf[0]); 10762306a36Sopenharmony_ci return -ENODEV; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci return 0; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* 11362306a36Sopenharmony_ci * Common identification function for chips with multi-phase support. 11462306a36Sopenharmony_ci * Since those chips have special configuration registers, we want to have 11562306a36Sopenharmony_ci * some level of reassurance that we are really talking with the chip 11662306a36Sopenharmony_ci * being probed. Check PMBus revision and chip ID. 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_cistatic int tps53679_identify_multiphase(struct i2c_client *client, 11962306a36Sopenharmony_ci struct pmbus_driver_info *info, 12062306a36Sopenharmony_ci int pmbus_rev, int device_id) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci int ret; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci ret = tps53679_identify_chip(client, pmbus_rev, device_id); 12562306a36Sopenharmony_ci if (ret < 0) 12662306a36Sopenharmony_ci return ret; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci ret = tps53679_identify_mode(client, info); 12962306a36Sopenharmony_ci if (ret < 0) 13062306a36Sopenharmony_ci return ret; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return tps53679_identify_phases(client, info); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic int tps53679_identify(struct i2c_client *client, 13662306a36Sopenharmony_ci struct pmbus_driver_info *info) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci return tps53679_identify_mode(client, info); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic int tps53681_identify(struct i2c_client *client, 14262306a36Sopenharmony_ci struct pmbus_driver_info *info) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci return tps53679_identify_multiphase(client, info, 14562306a36Sopenharmony_ci TPS53681_PMBUS_REVISION, 14662306a36Sopenharmony_ci TPS53681_DEVICE_ID); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic int tps53676_identify(struct i2c_client *client, 15062306a36Sopenharmony_ci struct pmbus_driver_info *info) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci u8 buf[I2C_SMBUS_BLOCK_MAX]; 15362306a36Sopenharmony_ci int phases_a = 0, phases_b = 0; 15462306a36Sopenharmony_ci int i, ret; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf); 15762306a36Sopenharmony_ci if (ret < 0) 15862306a36Sopenharmony_ci return ret; 15962306a36Sopenharmony_ci if (strncmp("TI\x53\x67\x60", buf, 5)) { 16062306a36Sopenharmony_ci dev_err(&client->dev, "Unexpected device ID: %s\n", buf); 16162306a36Sopenharmony_ci return -ENODEV; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci ret = i2c_smbus_read_block_data(client, TPS53676_USER_DATA_03, buf); 16562306a36Sopenharmony_ci if (ret < 0) 16662306a36Sopenharmony_ci return ret; 16762306a36Sopenharmony_ci if (ret != 24) 16862306a36Sopenharmony_ci return -EIO; 16962306a36Sopenharmony_ci for (i = 0; i < 2 * TPS53676_MAX_PHASES; i += 2) { 17062306a36Sopenharmony_ci if (buf[i + 1] & 0x80) { 17162306a36Sopenharmony_ci if (buf[i] & 0x08) 17262306a36Sopenharmony_ci phases_b++; 17362306a36Sopenharmony_ci else 17462306a36Sopenharmony_ci phases_a++; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci info->format[PSC_VOLTAGE_OUT] = linear; 17962306a36Sopenharmony_ci info->pages = 1; 18062306a36Sopenharmony_ci info->phases[0] = phases_a; 18162306a36Sopenharmony_ci if (phases_b > 0) { 18262306a36Sopenharmony_ci info->pages = 2; 18362306a36Sopenharmony_ci info->phases[1] = phases_b; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic int tps53681_read_word_data(struct i2c_client *client, int page, 18962306a36Sopenharmony_ci int phase, int reg) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci /* 19262306a36Sopenharmony_ci * For reading the total output current (READ_IOUT) for all phases, 19362306a36Sopenharmony_ci * the chip datasheet is a bit vague. It says "PHASE must be set to 19462306a36Sopenharmony_ci * FFh to access all phases simultaneously. PHASE may also be set to 19562306a36Sopenharmony_ci * 80h readack (!) the total phase current". 19662306a36Sopenharmony_ci * Experiments show that the command does _not_ report the total 19762306a36Sopenharmony_ci * current for all phases if the phase is set to 0xff. Instead, it 19862306a36Sopenharmony_ci * appears to report the current of one of the phases. Override phase 19962306a36Sopenharmony_ci * parameter with 0x80 when reading the total output current on page 0. 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_ci if (reg == PMBUS_READ_IOUT && page == 0 && phase == 0xff) 20262306a36Sopenharmony_ci return pmbus_read_word_data(client, page, 0x80, reg); 20362306a36Sopenharmony_ci return -ENODATA; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic struct pmbus_driver_info tps53679_info = { 20762306a36Sopenharmony_ci .format[PSC_VOLTAGE_IN] = linear, 20862306a36Sopenharmony_ci .format[PSC_VOLTAGE_OUT] = vid, 20962306a36Sopenharmony_ci .format[PSC_TEMPERATURE] = linear, 21062306a36Sopenharmony_ci .format[PSC_CURRENT_OUT] = linear, 21162306a36Sopenharmony_ci .format[PSC_POWER] = linear, 21262306a36Sopenharmony_ci .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | 21362306a36Sopenharmony_ci PMBUS_HAVE_STATUS_INPUT | 21462306a36Sopenharmony_ci PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | 21562306a36Sopenharmony_ci PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | 21662306a36Sopenharmony_ci PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | 21762306a36Sopenharmony_ci PMBUS_HAVE_POUT, 21862306a36Sopenharmony_ci .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | 21962306a36Sopenharmony_ci PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | 22062306a36Sopenharmony_ci PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | 22162306a36Sopenharmony_ci PMBUS_HAVE_POUT, 22262306a36Sopenharmony_ci .pfunc[0] = PMBUS_HAVE_IOUT, 22362306a36Sopenharmony_ci .pfunc[1] = PMBUS_HAVE_IOUT, 22462306a36Sopenharmony_ci .pfunc[2] = PMBUS_HAVE_IOUT, 22562306a36Sopenharmony_ci .pfunc[3] = PMBUS_HAVE_IOUT, 22662306a36Sopenharmony_ci .pfunc[4] = PMBUS_HAVE_IOUT, 22762306a36Sopenharmony_ci .pfunc[5] = PMBUS_HAVE_IOUT, 22862306a36Sopenharmony_ci .pfunc[6] = PMBUS_HAVE_IOUT, 22962306a36Sopenharmony_ci}; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic int tps53679_probe(struct i2c_client *client) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct device *dev = &client->dev; 23462306a36Sopenharmony_ci struct pmbus_driver_info *info; 23562306a36Sopenharmony_ci enum chips chip_id; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (dev->of_node) 23862306a36Sopenharmony_ci chip_id = (uintptr_t)of_device_get_match_data(dev); 23962306a36Sopenharmony_ci else 24062306a36Sopenharmony_ci chip_id = i2c_match_id(tps53679_id, client)->driver_data; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci info = devm_kmemdup(dev, &tps53679_info, sizeof(*info), GFP_KERNEL); 24362306a36Sopenharmony_ci if (!info) 24462306a36Sopenharmony_ci return -ENOMEM; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci switch (chip_id) { 24762306a36Sopenharmony_ci case tps53647: 24862306a36Sopenharmony_ci case tps53667: 24962306a36Sopenharmony_ci info->pages = TPS53647_PAGE_NUM; 25062306a36Sopenharmony_ci info->identify = tps53679_identify; 25162306a36Sopenharmony_ci break; 25262306a36Sopenharmony_ci case tps53676: 25362306a36Sopenharmony_ci info->identify = tps53676_identify; 25462306a36Sopenharmony_ci break; 25562306a36Sopenharmony_ci case tps53679: 25662306a36Sopenharmony_ci case tps53688: 25762306a36Sopenharmony_ci info->pages = TPS53679_PAGE_NUM; 25862306a36Sopenharmony_ci info->identify = tps53679_identify; 25962306a36Sopenharmony_ci break; 26062306a36Sopenharmony_ci case tps53681: 26162306a36Sopenharmony_ci info->pages = TPS53679_PAGE_NUM; 26262306a36Sopenharmony_ci info->phases[0] = 6; 26362306a36Sopenharmony_ci info->identify = tps53681_identify; 26462306a36Sopenharmony_ci info->read_word_data = tps53681_read_word_data; 26562306a36Sopenharmony_ci break; 26662306a36Sopenharmony_ci default: 26762306a36Sopenharmony_ci return -ENODEV; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return pmbus_do_probe(client, info); 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic const struct i2c_device_id tps53679_id[] = { 27462306a36Sopenharmony_ci {"bmr474", tps53676}, 27562306a36Sopenharmony_ci {"tps53647", tps53647}, 27662306a36Sopenharmony_ci {"tps53667", tps53667}, 27762306a36Sopenharmony_ci {"tps53676", tps53676}, 27862306a36Sopenharmony_ci {"tps53679", tps53679}, 27962306a36Sopenharmony_ci {"tps53681", tps53681}, 28062306a36Sopenharmony_ci {"tps53688", tps53688}, 28162306a36Sopenharmony_ci {} 28262306a36Sopenharmony_ci}; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tps53679_id); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic const struct of_device_id __maybe_unused tps53679_of_match[] = { 28762306a36Sopenharmony_ci {.compatible = "ti,tps53647", .data = (void *)tps53647}, 28862306a36Sopenharmony_ci {.compatible = "ti,tps53667", .data = (void *)tps53667}, 28962306a36Sopenharmony_ci {.compatible = "ti,tps53676", .data = (void *)tps53676}, 29062306a36Sopenharmony_ci {.compatible = "ti,tps53679", .data = (void *)tps53679}, 29162306a36Sopenharmony_ci {.compatible = "ti,tps53681", .data = (void *)tps53681}, 29262306a36Sopenharmony_ci {.compatible = "ti,tps53688", .data = (void *)tps53688}, 29362306a36Sopenharmony_ci {} 29462306a36Sopenharmony_ci}; 29562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tps53679_of_match); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic struct i2c_driver tps53679_driver = { 29862306a36Sopenharmony_ci .driver = { 29962306a36Sopenharmony_ci .name = "tps53679", 30062306a36Sopenharmony_ci .of_match_table = of_match_ptr(tps53679_of_match), 30162306a36Sopenharmony_ci }, 30262306a36Sopenharmony_ci .probe = tps53679_probe, 30362306a36Sopenharmony_ci .id_table = tps53679_id, 30462306a36Sopenharmony_ci}; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cimodule_i2c_driver(tps53679_driver); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ciMODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>"); 30962306a36Sopenharmony_ciMODULE_DESCRIPTION("PMBus driver for Texas Instruments TPS53679"); 31062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 31162306a36Sopenharmony_ciMODULE_IMPORT_NS(PMBUS); 312