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

pim4328.c (5922B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Hardware monitoring driver for PIM4006, PIM4328 and PIM4820
      4 *
      5 * Copyright (c) 2021 Flextronics International Sweden AB
      6 */
      7
      8#include <linux/err.h>
      9#include <linux/i2c.h>
     10#include <linux/init.h>
     11#include <linux/kernel.h>
     12#include <linux/module.h>
     13#include <linux/pmbus.h>
     14#include <linux/slab.h>
     15#include "pmbus.h"
     16
     17enum chips { pim4006, pim4328, pim4820 };
     18
     19struct pim4328_data {
     20	enum chips id;
     21	struct pmbus_driver_info info;
     22};
     23
     24#define to_pim4328_data(x)  container_of(x, struct pim4328_data, info)
     25
     26/* PIM4006 and PIM4328 */
     27#define PIM4328_MFR_READ_VINA		0xd3
     28#define PIM4328_MFR_READ_VINB		0xd4
     29
     30/* PIM4006 */
     31#define PIM4328_MFR_READ_IINA		0xd6
     32#define PIM4328_MFR_READ_IINB		0xd7
     33#define PIM4328_MFR_FET_CHECKSTATUS	0xd9
     34
     35/* PIM4328 */
     36#define PIM4328_MFR_STATUS_BITS		0xd5
     37
     38/* PIM4820 */
     39#define PIM4328_MFR_READ_STATUS		0xd0
     40
     41static const struct i2c_device_id pim4328_id[] = {
     42	{"bmr455", pim4328},
     43	{"pim4006", pim4006},
     44	{"pim4106", pim4006},
     45	{"pim4206", pim4006},
     46	{"pim4306", pim4006},
     47	{"pim4328", pim4328},
     48	{"pim4406", pim4006},
     49	{"pim4820", pim4820},
     50	{}
     51};
     52MODULE_DEVICE_TABLE(i2c, pim4328_id);
     53
     54static int pim4328_read_word_data(struct i2c_client *client, int page,
     55				  int phase, int reg)
     56{
     57	int ret;
     58
     59	if (page > 0)
     60		return -ENXIO;
     61
     62	if (phase == 0xff)
     63		return -ENODATA;
     64
     65	switch (reg) {
     66	case PMBUS_READ_VIN:
     67		ret = pmbus_read_word_data(client, page, phase,
     68					   phase == 0 ? PIM4328_MFR_READ_VINA
     69						      : PIM4328_MFR_READ_VINB);
     70		break;
     71	case PMBUS_READ_IIN:
     72		ret = pmbus_read_word_data(client, page, phase,
     73					   phase == 0 ? PIM4328_MFR_READ_IINA
     74						      : PIM4328_MFR_READ_IINB);
     75		break;
     76	default:
     77		ret = -ENODATA;
     78	}
     79
     80	return ret;
     81}
     82
     83static int pim4328_read_byte_data(struct i2c_client *client, int page, int reg)
     84{
     85	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
     86	struct pim4328_data *data = to_pim4328_data(info);
     87	int ret, status;
     88
     89	if (page > 0)
     90		return -ENXIO;
     91
     92	switch (reg) {
     93	case PMBUS_STATUS_BYTE:
     94		ret = pmbus_read_byte_data(client, page, PMBUS_STATUS_BYTE);
     95		if (ret < 0)
     96			return ret;
     97		if (data->id == pim4006) {
     98			status = pmbus_read_word_data(client, page, 0xff,
     99						      PIM4328_MFR_FET_CHECKSTATUS);
    100			if (status < 0)
    101				return status;
    102			if (status & 0x0630) /* Input UV */
    103				ret |= PB_STATUS_VIN_UV;
    104		} else if (data->id == pim4328) {
    105			status = pmbus_read_byte_data(client, page,
    106						      PIM4328_MFR_STATUS_BITS);
    107			if (status < 0)
    108				return status;
    109			if (status & 0x04) /* Input UV */
    110				ret |= PB_STATUS_VIN_UV;
    111			if (status & 0x40) /* Output UV */
    112				ret |= PB_STATUS_NONE_ABOVE;
    113		} else if (data->id == pim4820) {
    114			status = pmbus_read_byte_data(client, page,
    115						      PIM4328_MFR_READ_STATUS);
    116			if (status < 0)
    117				return status;
    118			if (status & 0x05) /* Input OV or OC */
    119				ret |= PB_STATUS_NONE_ABOVE;
    120			if (status & 0x1a) /* Input UV */
    121				ret |= PB_STATUS_VIN_UV;
    122			if (status & 0x40) /* OT */
    123				ret |= PB_STATUS_TEMPERATURE;
    124		}
    125		break;
    126	default:
    127		ret = -ENODATA;
    128	}
    129
    130	return ret;
    131}
    132
    133static int pim4328_probe(struct i2c_client *client)
    134{
    135	int status;
    136	u8 device_id[I2C_SMBUS_BLOCK_MAX + 1];
    137	const struct i2c_device_id *mid;
    138	struct pim4328_data *data;
    139	struct pmbus_driver_info *info;
    140	struct pmbus_platform_data *pdata;
    141	struct device *dev = &client->dev;
    142
    143	if (!i2c_check_functionality(client->adapter,
    144				     I2C_FUNC_SMBUS_READ_BYTE_DATA
    145				     | I2C_FUNC_SMBUS_BLOCK_DATA))
    146		return -ENODEV;
    147
    148	data = devm_kzalloc(&client->dev, sizeof(struct pim4328_data),
    149			    GFP_KERNEL);
    150	if (!data)
    151		return -ENOMEM;
    152
    153	status = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, device_id);
    154	if (status < 0) {
    155		dev_err(&client->dev, "Failed to read Manufacturer Model\n");
    156		return status;
    157	}
    158	for (mid = pim4328_id; mid->name[0]; mid++) {
    159		if (!strncasecmp(mid->name, device_id, strlen(mid->name)))
    160			break;
    161	}
    162	if (!mid->name[0]) {
    163		dev_err(&client->dev, "Unsupported device\n");
    164		return -ENODEV;
    165	}
    166
    167	if (strcmp(client->name, mid->name))
    168		dev_notice(&client->dev,
    169			   "Device mismatch: Configured %s, detected %s\n",
    170			   client->name, mid->name);
    171
    172	data->id = mid->driver_data;
    173	info = &data->info;
    174	info->pages = 1;
    175	info->read_byte_data = pim4328_read_byte_data;
    176	info->read_word_data = pim4328_read_word_data;
    177
    178	pdata = devm_kzalloc(dev, sizeof(struct pmbus_platform_data),
    179			     GFP_KERNEL);
    180	if (!pdata)
    181		return -ENOMEM;
    182	dev->platform_data = pdata;
    183	pdata->flags = PMBUS_NO_CAPABILITY | PMBUS_NO_WRITE_PROTECT;
    184
    185	switch (data->id) {
    186	case pim4006:
    187		info->phases[0] = 2;
    188		info->func[0] = PMBUS_PHASE_VIRTUAL | PMBUS_HAVE_VIN
    189			| PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT;
    190		info->pfunc[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN;
    191		info->pfunc[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN;
    192		break;
    193	case pim4328:
    194		info->phases[0] = 2;
    195		info->func[0] = PMBUS_PHASE_VIRTUAL
    196			| PMBUS_HAVE_VCAP | PMBUS_HAVE_VIN
    197			| PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT;
    198		info->pfunc[0] = PMBUS_HAVE_VIN;
    199		info->pfunc[1] = PMBUS_HAVE_VIN;
    200		info->format[PSC_VOLTAGE_IN] = direct;
    201		info->format[PSC_TEMPERATURE] = direct;
    202		info->format[PSC_CURRENT_OUT] = direct;
    203		pdata->flags |= PMBUS_USE_COEFFICIENTS_CMD;
    204		break;
    205	case pim4820:
    206		info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_TEMP
    207			| PMBUS_HAVE_IIN;
    208		info->format[PSC_VOLTAGE_IN] = direct;
    209		info->format[PSC_TEMPERATURE] = direct;
    210		info->format[PSC_CURRENT_IN] = direct;
    211		pdata->flags |= PMBUS_USE_COEFFICIENTS_CMD;
    212		break;
    213	default:
    214		return -ENODEV;
    215	}
    216
    217	return pmbus_do_probe(client, info);
    218}
    219
    220static struct i2c_driver pim4328_driver = {
    221	.driver = {
    222		   .name = "pim4328",
    223		   },
    224	.probe_new = pim4328_probe,
    225	.id_table = pim4328_id,
    226};
    227
    228module_i2c_driver(pim4328_driver);
    229
    230MODULE_AUTHOR("Erik Rosen <erik.rosen@metormote.com>");
    231MODULE_DESCRIPTION("PMBus driver for PIM4006, PIM4328, PIM4820 power interface modules");
    232MODULE_LICENSE("GPL");
    233MODULE_IMPORT_NS(PMBUS);