cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

tps53679.c (8284B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Hardware monitoring driver for Texas Instruments TPS53679
      4 *
      5 * Copyright (c) 2017 Mellanox Technologies. All rights reserved.
      6 * Copyright (c) 2017 Vadim Pasternak <vadimp@mellanox.com>
      7 */
      8
      9#include <linux/bits.h>
     10#include <linux/err.h>
     11#include <linux/i2c.h>
     12#include <linux/init.h>
     13#include <linux/kernel.h>
     14#include <linux/module.h>
     15#include <linux/of_device.h>
     16#include "pmbus.h"
     17
     18enum chips {
     19	tps53647, tps53667, tps53676, tps53679, tps53681, tps53688
     20};
     21
     22#define TPS53647_PAGE_NUM		1
     23
     24#define TPS53676_USER_DATA_03		0xb3
     25#define TPS53676_MAX_PHASES		7
     26
     27#define TPS53679_PROT_VR12_5MV		0x01 /* VR12.0 mode, 5-mV DAC */
     28#define TPS53679_PROT_VR12_5_10MV	0x02 /* VR12.5 mode, 10-mV DAC */
     29#define TPS53679_PROT_VR13_10MV		0x04 /* VR13.0 mode, 10-mV DAC */
     30#define TPS53679_PROT_IMVP8_5MV		0x05 /* IMVP8 mode, 5-mV DAC */
     31#define TPS53679_PROT_VR13_5MV		0x07 /* VR13.0 mode, 5-mV DAC */
     32#define TPS53679_PAGE_NUM		2
     33
     34#define TPS53681_DEVICE_ID		0x81
     35
     36#define TPS53681_PMBUS_REVISION		0x33
     37
     38#define TPS53681_MFR_SPECIFIC_20	0xe4	/* Number of phases, per page */
     39
     40static const struct i2c_device_id tps53679_id[];
     41
     42static int tps53679_identify_mode(struct i2c_client *client,
     43				  struct pmbus_driver_info *info)
     44{
     45	u8 vout_params;
     46	int i, ret;
     47
     48	for (i = 0; i < info->pages; i++) {
     49		/* Read the register with VOUT scaling value.*/
     50		ret = pmbus_read_byte_data(client, i, PMBUS_VOUT_MODE);
     51		if (ret < 0)
     52			return ret;
     53
     54		vout_params = ret & GENMASK(4, 0);
     55
     56		switch (vout_params) {
     57		case TPS53679_PROT_VR13_10MV:
     58		case TPS53679_PROT_VR12_5_10MV:
     59			info->vrm_version[i] = vr13;
     60			break;
     61		case TPS53679_PROT_VR13_5MV:
     62		case TPS53679_PROT_VR12_5MV:
     63		case TPS53679_PROT_IMVP8_5MV:
     64			info->vrm_version[i] = vr12;
     65			break;
     66		default:
     67			return -EINVAL;
     68		}
     69	}
     70
     71	return 0;
     72}
     73
     74static int tps53679_identify_phases(struct i2c_client *client,
     75				    struct pmbus_driver_info *info)
     76{
     77	int ret;
     78
     79	/* On TPS53681, only channel A provides per-phase output current */
     80	ret = pmbus_read_byte_data(client, 0, TPS53681_MFR_SPECIFIC_20);
     81	if (ret < 0)
     82		return ret;
     83	info->phases[0] = (ret & 0x07) + 1;
     84
     85	return 0;
     86}
     87
     88static int tps53679_identify_chip(struct i2c_client *client,
     89				  u8 revision, u16 id)
     90{
     91	u8 buf[I2C_SMBUS_BLOCK_MAX];
     92	int ret;
     93
     94	ret = pmbus_read_byte_data(client, 0, PMBUS_REVISION);
     95	if (ret < 0)
     96		return ret;
     97	if (ret != revision) {
     98		dev_err(&client->dev, "Unexpected PMBus revision 0x%x\n", ret);
     99		return -ENODEV;
    100	}
    101
    102	ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf);
    103	if (ret < 0)
    104		return ret;
    105	if (ret != 1 || buf[0] != id) {
    106		dev_err(&client->dev, "Unexpected device ID 0x%x\n", buf[0]);
    107		return -ENODEV;
    108	}
    109	return 0;
    110}
    111
    112/*
    113 * Common identification function for chips with multi-phase support.
    114 * Since those chips have special configuration registers, we want to have
    115 * some level of reassurance that we are really talking with the chip
    116 * being probed. Check PMBus revision and chip ID.
    117 */
    118static int tps53679_identify_multiphase(struct i2c_client *client,
    119					struct pmbus_driver_info *info,
    120					int pmbus_rev, int device_id)
    121{
    122	int ret;
    123
    124	ret = tps53679_identify_chip(client, pmbus_rev, device_id);
    125	if (ret < 0)
    126		return ret;
    127
    128	ret = tps53679_identify_mode(client, info);
    129	if (ret < 0)
    130		return ret;
    131
    132	return tps53679_identify_phases(client, info);
    133}
    134
    135static int tps53679_identify(struct i2c_client *client,
    136			     struct pmbus_driver_info *info)
    137{
    138	return tps53679_identify_mode(client, info);
    139}
    140
    141static int tps53681_identify(struct i2c_client *client,
    142			     struct pmbus_driver_info *info)
    143{
    144	return tps53679_identify_multiphase(client, info,
    145					    TPS53681_PMBUS_REVISION,
    146					    TPS53681_DEVICE_ID);
    147}
    148
    149static int tps53676_identify(struct i2c_client *client,
    150			     struct pmbus_driver_info *info)
    151{
    152	u8 buf[I2C_SMBUS_BLOCK_MAX];
    153	int phases_a = 0, phases_b = 0;
    154	int i, ret;
    155
    156	ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf);
    157	if (ret < 0)
    158		return ret;
    159	if (strncmp("TI\x53\x67\x60", buf, 5)) {
    160		dev_err(&client->dev, "Unexpected device ID: %s\n", buf);
    161		return -ENODEV;
    162	}
    163
    164	ret = i2c_smbus_read_block_data(client, TPS53676_USER_DATA_03, buf);
    165	if (ret < 0)
    166		return ret;
    167	if (ret != 24)
    168		return -EIO;
    169	for (i = 0; i < 2 * TPS53676_MAX_PHASES; i += 2) {
    170		if (buf[i + 1] & 0x80) {
    171			if (buf[i] & 0x08)
    172				phases_b++;
    173			else
    174				phases_a++;
    175		}
    176	}
    177
    178	info->format[PSC_VOLTAGE_OUT] = linear;
    179	info->pages = 1;
    180	info->phases[0] = phases_a;
    181	if (phases_b > 0) {
    182		info->pages = 2;
    183		info->phases[1] = phases_b;
    184	}
    185	return 0;
    186}
    187
    188static int tps53681_read_word_data(struct i2c_client *client, int page,
    189				   int phase, int reg)
    190{
    191	/*
    192	 * For reading the total output current (READ_IOUT) for all phases,
    193	 * the chip datasheet is a bit vague. It says "PHASE must be set to
    194	 * FFh to access all phases simultaneously. PHASE may also be set to
    195	 * 80h readack (!) the total phase current".
    196	 * Experiments show that the command does _not_ report the total
    197	 * current for all phases if the phase is set to 0xff. Instead, it
    198	 * appears to report the current of one of the phases. Override phase
    199	 * parameter with 0x80 when reading the total output current on page 0.
    200	 */
    201	if (reg == PMBUS_READ_IOUT && page == 0 && phase == 0xff)
    202		return pmbus_read_word_data(client, page, 0x80, reg);
    203	return -ENODATA;
    204}
    205
    206static struct pmbus_driver_info tps53679_info = {
    207	.format[PSC_VOLTAGE_IN] = linear,
    208	.format[PSC_VOLTAGE_OUT] = vid,
    209	.format[PSC_TEMPERATURE] = linear,
    210	.format[PSC_CURRENT_OUT] = linear,
    211	.format[PSC_POWER] = linear,
    212	.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN |
    213		PMBUS_HAVE_STATUS_INPUT |
    214		PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
    215		PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
    216		PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP |
    217		PMBUS_HAVE_POUT,
    218	.func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
    219		PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
    220		PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP |
    221		PMBUS_HAVE_POUT,
    222	.pfunc[0] = PMBUS_HAVE_IOUT,
    223	.pfunc[1] = PMBUS_HAVE_IOUT,
    224	.pfunc[2] = PMBUS_HAVE_IOUT,
    225	.pfunc[3] = PMBUS_HAVE_IOUT,
    226	.pfunc[4] = PMBUS_HAVE_IOUT,
    227	.pfunc[5] = PMBUS_HAVE_IOUT,
    228	.pfunc[6] = PMBUS_HAVE_IOUT,
    229};
    230
    231static int tps53679_probe(struct i2c_client *client)
    232{
    233	struct device *dev = &client->dev;
    234	struct pmbus_driver_info *info;
    235	enum chips chip_id;
    236
    237	if (dev->of_node)
    238		chip_id = (enum chips)of_device_get_match_data(dev);
    239	else
    240		chip_id = i2c_match_id(tps53679_id, client)->driver_data;
    241
    242	info = devm_kmemdup(dev, &tps53679_info, sizeof(*info), GFP_KERNEL);
    243	if (!info)
    244		return -ENOMEM;
    245
    246	switch (chip_id) {
    247	case tps53647:
    248	case tps53667:
    249		info->pages = TPS53647_PAGE_NUM;
    250		info->identify = tps53679_identify;
    251		break;
    252	case tps53676:
    253		info->identify = tps53676_identify;
    254		break;
    255	case tps53679:
    256	case tps53688:
    257		info->pages = TPS53679_PAGE_NUM;
    258		info->identify = tps53679_identify;
    259		break;
    260	case tps53681:
    261		info->pages = TPS53679_PAGE_NUM;
    262		info->phases[0] = 6;
    263		info->identify = tps53681_identify;
    264		info->read_word_data = tps53681_read_word_data;
    265		break;
    266	default:
    267		return -ENODEV;
    268	}
    269
    270	return pmbus_do_probe(client, info);
    271}
    272
    273static const struct i2c_device_id tps53679_id[] = {
    274	{"bmr474", tps53676},
    275	{"tps53647", tps53647},
    276	{"tps53667", tps53667},
    277	{"tps53676", tps53676},
    278	{"tps53679", tps53679},
    279	{"tps53681", tps53681},
    280	{"tps53688", tps53688},
    281	{}
    282};
    283
    284MODULE_DEVICE_TABLE(i2c, tps53679_id);
    285
    286static const struct of_device_id __maybe_unused tps53679_of_match[] = {
    287	{.compatible = "ti,tps53647", .data = (void *)tps53647},
    288	{.compatible = "ti,tps53667", .data = (void *)tps53667},
    289	{.compatible = "ti,tps53676", .data = (void *)tps53676},
    290	{.compatible = "ti,tps53679", .data = (void *)tps53679},
    291	{.compatible = "ti,tps53681", .data = (void *)tps53681},
    292	{.compatible = "ti,tps53688", .data = (void *)tps53688},
    293	{}
    294};
    295MODULE_DEVICE_TABLE(of, tps53679_of_match);
    296
    297static struct i2c_driver tps53679_driver = {
    298	.driver = {
    299		.name = "tps53679",
    300		.of_match_table = of_match_ptr(tps53679_of_match),
    301	},
    302	.probe_new = tps53679_probe,
    303	.id_table = tps53679_id,
    304};
    305
    306module_i2c_driver(tps53679_driver);
    307
    308MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
    309MODULE_DESCRIPTION("PMBus driver for Texas Instruments TPS53679");
    310MODULE_LICENSE("GPL");
    311MODULE_IMPORT_NS(PMBUS);