1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 261052652SVadim Pasternak /* 361052652SVadim Pasternak * Hardware monitoring driver for Texas Instruments TPS53679 461052652SVadim Pasternak * 561052652SVadim Pasternak * Copyright (c) 2017 Mellanox Technologies. All rights reserved. 661052652SVadim Pasternak * Copyright (c) 2017 Vadim Pasternak <vadimp@mellanox.com> 761052652SVadim Pasternak */ 861052652SVadim Pasternak 953030bccSGuenter Roeck #include <linux/bits.h> 1061052652SVadim Pasternak #include <linux/err.h> 1161052652SVadim Pasternak #include <linux/i2c.h> 1261052652SVadim Pasternak #include <linux/init.h> 1361052652SVadim Pasternak #include <linux/kernel.h> 1461052652SVadim Pasternak #include <linux/module.h> 1563eb4587SGuenter Roeck #include <linux/of_device.h> 1661052652SVadim Pasternak #include "pmbus.h" 1761052652SVadim Pasternak 1863eb4587SGuenter Roeck enum chips { 19cb3d37b5SErik Rosen tps53647, tps53667, tps53676, tps53679, tps53681, tps53688 2063eb4587SGuenter Roeck }; 2163eb4587SGuenter Roeck 226f944004SGuenter Roeck #define TPS53647_PAGE_NUM 1 236f944004SGuenter Roeck 24cb3d37b5SErik Rosen #define TPS53676_USER_DATA_03 0xb3 25cb3d37b5SErik Rosen #define TPS53676_MAX_PHASES 7 26cb3d37b5SErik Rosen 2761052652SVadim Pasternak #define TPS53679_PROT_VR12_5MV 0x01 /* VR12.0 mode, 5-mV DAC */ 2861052652SVadim Pasternak #define TPS53679_PROT_VR12_5_10MV 0x02 /* VR12.5 mode, 10-mV DAC */ 2961052652SVadim Pasternak #define TPS53679_PROT_VR13_10MV 0x04 /* VR13.0 mode, 10-mV DAC */ 3061052652SVadim Pasternak #define TPS53679_PROT_IMVP8_5MV 0x05 /* IMVP8 mode, 5-mV DAC */ 3161052652SVadim Pasternak #define TPS53679_PROT_VR13_5MV 0x07 /* VR13.0 mode, 5-mV DAC */ 3261052652SVadim Pasternak #define TPS53679_PAGE_NUM 2 3361052652SVadim Pasternak 3453030bccSGuenter Roeck #define TPS53681_DEVICE_ID 0x81 3553030bccSGuenter Roeck 3653030bccSGuenter Roeck #define TPS53681_PMBUS_REVISION 0x33 3753030bccSGuenter Roeck 3853030bccSGuenter Roeck #define TPS53681_MFR_SPECIFIC_20 0xe4 /* Number of phases, per page */ 3953030bccSGuenter Roeck 40dd431939SStephen Kitt static const struct i2c_device_id tps53679_id[]; 41dd431939SStephen Kitt 4253030bccSGuenter Roeck static int tps53679_identify_mode(struct i2c_client *client, 4361052652SVadim Pasternak struct pmbus_driver_info *info) 4461052652SVadim Pasternak { 4561052652SVadim Pasternak u8 vout_params; 46b9fa0a3aSVadim Pasternak int i, ret; 4761052652SVadim Pasternak 486f944004SGuenter Roeck for (i = 0; i < info->pages; i++) { 4961052652SVadim Pasternak /* Read the register with VOUT scaling value.*/ 50b9fa0a3aSVadim Pasternak ret = pmbus_read_byte_data(client, i, PMBUS_VOUT_MODE); 5161052652SVadim Pasternak if (ret < 0) 5261052652SVadim Pasternak return ret; 5361052652SVadim Pasternak 5461052652SVadim Pasternak vout_params = ret & GENMASK(4, 0); 5561052652SVadim Pasternak 5661052652SVadim Pasternak switch (vout_params) { 5761052652SVadim Pasternak case TPS53679_PROT_VR13_10MV: 5861052652SVadim Pasternak case TPS53679_PROT_VR12_5_10MV: 59b9fa0a3aSVadim Pasternak info->vrm_version[i] = vr13; 6061052652SVadim Pasternak break; 6161052652SVadim Pasternak case TPS53679_PROT_VR13_5MV: 6261052652SVadim Pasternak case TPS53679_PROT_VR12_5MV: 6361052652SVadim Pasternak case TPS53679_PROT_IMVP8_5MV: 64b9fa0a3aSVadim Pasternak info->vrm_version[i] = vr12; 6561052652SVadim Pasternak break; 6661052652SVadim Pasternak default: 6761052652SVadim Pasternak return -EINVAL; 6861052652SVadim Pasternak } 69b9fa0a3aSVadim Pasternak } 7061052652SVadim Pasternak 7161052652SVadim Pasternak return 0; 7261052652SVadim Pasternak } 7361052652SVadim Pasternak 7453030bccSGuenter Roeck static int tps53679_identify_phases(struct i2c_client *client, 7553030bccSGuenter Roeck struct pmbus_driver_info *info) 7653030bccSGuenter Roeck { 7753030bccSGuenter Roeck int ret; 7853030bccSGuenter Roeck 7953030bccSGuenter Roeck /* On TPS53681, only channel A provides per-phase output current */ 8053030bccSGuenter Roeck ret = pmbus_read_byte_data(client, 0, TPS53681_MFR_SPECIFIC_20); 8153030bccSGuenter Roeck if (ret < 0) 8253030bccSGuenter Roeck return ret; 8353030bccSGuenter Roeck info->phases[0] = (ret & 0x07) + 1; 8453030bccSGuenter Roeck 8553030bccSGuenter Roeck return 0; 8653030bccSGuenter Roeck } 8753030bccSGuenter Roeck 8853030bccSGuenter Roeck static int tps53679_identify_chip(struct i2c_client *client, 8953030bccSGuenter Roeck u8 revision, u16 id) 9053030bccSGuenter Roeck { 9153030bccSGuenter Roeck u8 buf[I2C_SMBUS_BLOCK_MAX]; 9253030bccSGuenter Roeck int ret; 9353030bccSGuenter Roeck 9453030bccSGuenter Roeck ret = pmbus_read_byte_data(client, 0, PMBUS_REVISION); 9553030bccSGuenter Roeck if (ret < 0) 9653030bccSGuenter Roeck return ret; 9753030bccSGuenter Roeck if (ret != revision) { 9853030bccSGuenter Roeck dev_err(&client->dev, "Unexpected PMBus revision 0x%x\n", ret); 9953030bccSGuenter Roeck return -ENODEV; 10053030bccSGuenter Roeck } 10153030bccSGuenter Roeck 10253030bccSGuenter Roeck ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf); 10353030bccSGuenter Roeck if (ret < 0) 10453030bccSGuenter Roeck return ret; 10553030bccSGuenter Roeck if (ret != 1 || buf[0] != id) { 10653030bccSGuenter Roeck dev_err(&client->dev, "Unexpected device ID 0x%x\n", buf[0]); 10753030bccSGuenter Roeck return -ENODEV; 10853030bccSGuenter Roeck } 10953030bccSGuenter Roeck return 0; 11053030bccSGuenter Roeck } 11153030bccSGuenter Roeck 11253030bccSGuenter Roeck /* 11353030bccSGuenter Roeck * Common identification function for chips with multi-phase support. 11453030bccSGuenter Roeck * Since those chips have special configuration registers, we want to have 11553030bccSGuenter Roeck * some level of reassurance that we are really talking with the chip 11653030bccSGuenter Roeck * being probed. Check PMBus revision and chip ID. 11753030bccSGuenter Roeck */ 11853030bccSGuenter Roeck static int tps53679_identify_multiphase(struct i2c_client *client, 11953030bccSGuenter Roeck struct pmbus_driver_info *info, 12053030bccSGuenter Roeck int pmbus_rev, int device_id) 12153030bccSGuenter Roeck { 12253030bccSGuenter Roeck int ret; 12353030bccSGuenter Roeck 12453030bccSGuenter Roeck ret = tps53679_identify_chip(client, pmbus_rev, device_id); 12553030bccSGuenter Roeck if (ret < 0) 12653030bccSGuenter Roeck return ret; 12753030bccSGuenter Roeck 12853030bccSGuenter Roeck ret = tps53679_identify_mode(client, info); 12953030bccSGuenter Roeck if (ret < 0) 13053030bccSGuenter Roeck return ret; 13153030bccSGuenter Roeck 13253030bccSGuenter Roeck return tps53679_identify_phases(client, info); 13353030bccSGuenter Roeck } 13453030bccSGuenter Roeck 13553030bccSGuenter Roeck static int tps53679_identify(struct i2c_client *client, 13653030bccSGuenter Roeck struct pmbus_driver_info *info) 13753030bccSGuenter Roeck { 13853030bccSGuenter Roeck return tps53679_identify_mode(client, info); 13953030bccSGuenter Roeck } 14053030bccSGuenter Roeck 14153030bccSGuenter Roeck static int tps53681_identify(struct i2c_client *client, 14253030bccSGuenter Roeck struct pmbus_driver_info *info) 14353030bccSGuenter Roeck { 14453030bccSGuenter Roeck return tps53679_identify_multiphase(client, info, 14553030bccSGuenter Roeck TPS53681_PMBUS_REVISION, 14653030bccSGuenter Roeck TPS53681_DEVICE_ID); 14753030bccSGuenter Roeck } 14853030bccSGuenter Roeck 149cb3d37b5SErik Rosen static int tps53676_identify(struct i2c_client *client, 150cb3d37b5SErik Rosen struct pmbus_driver_info *info) 151cb3d37b5SErik Rosen { 152cb3d37b5SErik Rosen u8 buf[I2C_SMBUS_BLOCK_MAX]; 153cb3d37b5SErik Rosen int phases_a = 0, phases_b = 0; 154cb3d37b5SErik Rosen int i, ret; 155cb3d37b5SErik Rosen 156cb3d37b5SErik Rosen ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf); 157cb3d37b5SErik Rosen if (ret < 0) 158cb3d37b5SErik Rosen return ret; 159cb3d37b5SErik Rosen if (strncmp("TI\x53\x67\x60", buf, 5)) { 160cb3d37b5SErik Rosen dev_err(&client->dev, "Unexpected device ID: %s\n", buf); 161cb3d37b5SErik Rosen return -ENODEV; 162cb3d37b5SErik Rosen } 163cb3d37b5SErik Rosen 164cb3d37b5SErik Rosen ret = i2c_smbus_read_block_data(client, TPS53676_USER_DATA_03, buf); 165cb3d37b5SErik Rosen if (ret < 0) 166cb3d37b5SErik Rosen return ret; 167cb3d37b5SErik Rosen if (ret != 24) 168cb3d37b5SErik Rosen return -EIO; 169cb3d37b5SErik Rosen for (i = 0; i < 2 * TPS53676_MAX_PHASES; i += 2) { 170cb3d37b5SErik Rosen if (buf[i + 1] & 0x80) { 171cb3d37b5SErik Rosen if (buf[i] & 0x08) 172cb3d37b5SErik Rosen phases_b++; 173cb3d37b5SErik Rosen else 174cb3d37b5SErik Rosen phases_a++; 175cb3d37b5SErik Rosen } 176cb3d37b5SErik Rosen } 177cb3d37b5SErik Rosen 178cb3d37b5SErik Rosen info->format[PSC_VOLTAGE_OUT] = linear; 179cb3d37b5SErik Rosen info->pages = 1; 180cb3d37b5SErik Rosen info->phases[0] = phases_a; 181cb3d37b5SErik Rosen if (phases_b > 0) { 182cb3d37b5SErik Rosen info->pages = 2; 183cb3d37b5SErik Rosen info->phases[1] = phases_b; 184cb3d37b5SErik Rosen } 185cb3d37b5SErik Rosen return 0; 186cb3d37b5SErik Rosen } 187cb3d37b5SErik Rosen 18853030bccSGuenter Roeck static int tps53681_read_word_data(struct i2c_client *client, int page, 18953030bccSGuenter Roeck int phase, int reg) 19053030bccSGuenter Roeck { 19153030bccSGuenter Roeck /* 19253030bccSGuenter Roeck * For reading the total output current (READ_IOUT) for all phases, 19353030bccSGuenter Roeck * the chip datasheet is a bit vague. It says "PHASE must be set to 19453030bccSGuenter Roeck * FFh to access all phases simultaneously. PHASE may also be set to 19553030bccSGuenter Roeck * 80h readack (!) the total phase current". 19653030bccSGuenter Roeck * Experiments show that the command does _not_ report the total 19753030bccSGuenter Roeck * current for all phases if the phase is set to 0xff. Instead, it 19853030bccSGuenter Roeck * appears to report the current of one of the phases. Override phase 19953030bccSGuenter Roeck * parameter with 0x80 when reading the total output current on page 0. 20053030bccSGuenter Roeck */ 20153030bccSGuenter Roeck if (reg == PMBUS_READ_IOUT && page == 0 && phase == 0xff) 20253030bccSGuenter Roeck return pmbus_read_word_data(client, page, 0x80, reg); 20353030bccSGuenter Roeck return -ENODATA; 20453030bccSGuenter Roeck } 20553030bccSGuenter Roeck 20661052652SVadim Pasternak static struct pmbus_driver_info tps53679_info = { 20761052652SVadim Pasternak .format[PSC_VOLTAGE_IN] = linear, 20861052652SVadim Pasternak .format[PSC_VOLTAGE_OUT] = vid, 20961052652SVadim Pasternak .format[PSC_TEMPERATURE] = linear, 21061052652SVadim Pasternak .format[PSC_CURRENT_OUT] = linear, 21161052652SVadim Pasternak .format[PSC_POWER] = linear, 21239c749acSGuenter Roeck .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | 21339c749acSGuenter Roeck PMBUS_HAVE_STATUS_INPUT | 21463eb4587SGuenter Roeck PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | 21561052652SVadim Pasternak PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | 21661052652SVadim Pasternak PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | 21761052652SVadim Pasternak PMBUS_HAVE_POUT, 21839c749acSGuenter Roeck .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | 21961052652SVadim Pasternak PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | 22061052652SVadim Pasternak PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | 22161052652SVadim Pasternak PMBUS_HAVE_POUT, 22253030bccSGuenter Roeck .pfunc[0] = PMBUS_HAVE_IOUT, 22353030bccSGuenter Roeck .pfunc[1] = PMBUS_HAVE_IOUT, 22453030bccSGuenter Roeck .pfunc[2] = PMBUS_HAVE_IOUT, 22553030bccSGuenter Roeck .pfunc[3] = PMBUS_HAVE_IOUT, 22653030bccSGuenter Roeck .pfunc[4] = PMBUS_HAVE_IOUT, 22753030bccSGuenter Roeck .pfunc[5] = PMBUS_HAVE_IOUT, 228cb3d37b5SErik Rosen .pfunc[6] = PMBUS_HAVE_IOUT, 22961052652SVadim Pasternak }; 23061052652SVadim Pasternak 231dd431939SStephen Kitt static int tps53679_probe(struct i2c_client *client) 23261052652SVadim Pasternak { 23363eb4587SGuenter Roeck struct device *dev = &client->dev; 234ff066653SVadim Pasternak struct pmbus_driver_info *info; 23563eb4587SGuenter Roeck enum chips chip_id; 236ff066653SVadim Pasternak 23763eb4587SGuenter Roeck if (dev->of_node) 23863eb4587SGuenter Roeck chip_id = (enum chips)of_device_get_match_data(dev); 23963eb4587SGuenter Roeck else 240dd431939SStephen Kitt chip_id = i2c_match_id(tps53679_id, client)->driver_data; 24163eb4587SGuenter Roeck 24263eb4587SGuenter Roeck info = devm_kmemdup(dev, &tps53679_info, sizeof(*info), GFP_KERNEL); 243ff066653SVadim Pasternak if (!info) 244ff066653SVadim Pasternak return -ENOMEM; 245ff066653SVadim Pasternak 24663eb4587SGuenter Roeck switch (chip_id) { 2476f944004SGuenter Roeck case tps53647: 2486f944004SGuenter Roeck case tps53667: 2496f944004SGuenter Roeck info->pages = TPS53647_PAGE_NUM; 2506f944004SGuenter Roeck info->identify = tps53679_identify; 2516f944004SGuenter Roeck break; 252cb3d37b5SErik Rosen case tps53676: 253cb3d37b5SErik Rosen info->identify = tps53676_identify; 254cb3d37b5SErik Rosen break; 25563eb4587SGuenter Roeck case tps53679: 25663eb4587SGuenter Roeck case tps53688: 25763eb4587SGuenter Roeck info->pages = TPS53679_PAGE_NUM; 25863eb4587SGuenter Roeck info->identify = tps53679_identify; 25963eb4587SGuenter Roeck break; 26053030bccSGuenter Roeck case tps53681: 26153030bccSGuenter Roeck info->pages = TPS53679_PAGE_NUM; 26253030bccSGuenter Roeck info->phases[0] = 6; 26353030bccSGuenter Roeck info->identify = tps53681_identify; 26453030bccSGuenter Roeck info->read_word_data = tps53681_read_word_data; 26553030bccSGuenter Roeck break; 26663eb4587SGuenter Roeck default: 26763eb4587SGuenter Roeck return -ENODEV; 26863eb4587SGuenter Roeck } 26963eb4587SGuenter Roeck 270dd431939SStephen Kitt return pmbus_do_probe(client, info); 27161052652SVadim Pasternak } 27261052652SVadim Pasternak 27361052652SVadim Pasternak static const struct i2c_device_id tps53679_id[] = { 274cb3d37b5SErik Rosen {"bmr474", tps53676}, 2756f944004SGuenter Roeck {"tps53647", tps53647}, 2766f944004SGuenter Roeck {"tps53667", tps53667}, 277cb3d37b5SErik Rosen {"tps53676", tps53676}, 27863eb4587SGuenter Roeck {"tps53679", tps53679}, 27953030bccSGuenter Roeck {"tps53681", tps53681}, 28063eb4587SGuenter Roeck {"tps53688", tps53688}, 28161052652SVadim Pasternak {} 28261052652SVadim Pasternak }; 28361052652SVadim Pasternak 28461052652SVadim Pasternak MODULE_DEVICE_TABLE(i2c, tps53679_id); 28561052652SVadim Pasternak 286e91cb17bSGuenter Roeck static const struct of_device_id __maybe_unused tps53679_of_match[] = { 2876f944004SGuenter Roeck {.compatible = "ti,tps53647", .data = (void *)tps53647}, 2886f944004SGuenter Roeck {.compatible = "ti,tps53667", .data = (void *)tps53667}, 289cb3d37b5SErik Rosen {.compatible = "ti,tps53676", .data = (void *)tps53676}, 29063eb4587SGuenter Roeck {.compatible = "ti,tps53679", .data = (void *)tps53679}, 29153030bccSGuenter Roeck {.compatible = "ti,tps53681", .data = (void *)tps53681}, 29263eb4587SGuenter Roeck {.compatible = "ti,tps53688", .data = (void *)tps53688}, 29361052652SVadim Pasternak {} 29461052652SVadim Pasternak }; 29561052652SVadim Pasternak MODULE_DEVICE_TABLE(of, tps53679_of_match); 29661052652SVadim Pasternak 29761052652SVadim Pasternak static struct i2c_driver tps53679_driver = { 29861052652SVadim Pasternak .driver = { 29961052652SVadim Pasternak .name = "tps53679", 30061052652SVadim Pasternak .of_match_table = of_match_ptr(tps53679_of_match), 30161052652SVadim Pasternak }, 302dd431939SStephen Kitt .probe_new = tps53679_probe, 30361052652SVadim Pasternak .id_table = tps53679_id, 30461052652SVadim Pasternak }; 30561052652SVadim Pasternak 30661052652SVadim Pasternak module_i2c_driver(tps53679_driver); 30761052652SVadim Pasternak 30861052652SVadim Pasternak MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>"); 30961052652SVadim Pasternak MODULE_DESCRIPTION("PMBus driver for Texas Instruments TPS53679"); 31061052652SVadim Pasternak MODULE_LICENSE("GPL"); 311*b94ca77eSGuenter Roeck MODULE_IMPORT_NS(PMBUS); 312