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