xref: /openbmc/linux/drivers/hwmon/pmbus/tps53679.c (revision 30572c7b)
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>
1539f03438SRob Herring #include <linux/of.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 
tps53679_identify_mode(struct i2c_client * client,struct pmbus_driver_info * info)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 
tps53679_identify_phases(struct i2c_client * client,struct pmbus_driver_info * info)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 
tps53679_identify_chip(struct i2c_client * client,u8 revision,u16 id)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  */
tps53679_identify_multiphase(struct i2c_client * client,struct pmbus_driver_info * info,int pmbus_rev,int device_id)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 
tps53679_identify(struct i2c_client * client,struct pmbus_driver_info * info)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 
tps53681_identify(struct i2c_client * client,struct pmbus_driver_info * info)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 
tps53676_identify(struct i2c_client * client,struct pmbus_driver_info * info)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 
tps53681_read_word_data(struct i2c_client * client,int page,int phase,int reg)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 
tps53679_probe(struct i2c_client * client)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)
238*30572c7bSKrzysztof Kozlowski 		chip_id = (uintptr_t)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 	},
3021975d167SUwe Kleine-König 	.probe = 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");
311b94ca77eSGuenter Roeck MODULE_IMPORT_NS(PMBUS);
312