162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Hardware monitoring driver for IR35221
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) IBM Corporation 2017.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/err.h>
962306a36Sopenharmony_ci#include <linux/i2c.h>
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include "pmbus.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define IR35221_MFR_VIN_PEAK		0xc5
1662306a36Sopenharmony_ci#define IR35221_MFR_VOUT_PEAK		0xc6
1762306a36Sopenharmony_ci#define IR35221_MFR_IOUT_PEAK		0xc7
1862306a36Sopenharmony_ci#define IR35221_MFR_TEMP_PEAK		0xc8
1962306a36Sopenharmony_ci#define IR35221_MFR_VIN_VALLEY		0xc9
2062306a36Sopenharmony_ci#define IR35221_MFR_VOUT_VALLEY		0xca
2162306a36Sopenharmony_ci#define IR35221_MFR_IOUT_VALLEY		0xcb
2262306a36Sopenharmony_ci#define IR35221_MFR_TEMP_VALLEY		0xcc
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic int ir35221_read_word_data(struct i2c_client *client, int page,
2562306a36Sopenharmony_ci				  int phase, int reg)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	int ret;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	switch (reg) {
3062306a36Sopenharmony_ci	case PMBUS_VIRT_READ_VIN_MAX:
3162306a36Sopenharmony_ci		ret = pmbus_read_word_data(client, page, phase,
3262306a36Sopenharmony_ci					   IR35221_MFR_VIN_PEAK);
3362306a36Sopenharmony_ci		break;
3462306a36Sopenharmony_ci	case PMBUS_VIRT_READ_VOUT_MAX:
3562306a36Sopenharmony_ci		ret = pmbus_read_word_data(client, page, phase,
3662306a36Sopenharmony_ci					   IR35221_MFR_VOUT_PEAK);
3762306a36Sopenharmony_ci		break;
3862306a36Sopenharmony_ci	case PMBUS_VIRT_READ_IOUT_MAX:
3962306a36Sopenharmony_ci		ret = pmbus_read_word_data(client, page, phase,
4062306a36Sopenharmony_ci					   IR35221_MFR_IOUT_PEAK);
4162306a36Sopenharmony_ci		break;
4262306a36Sopenharmony_ci	case PMBUS_VIRT_READ_TEMP_MAX:
4362306a36Sopenharmony_ci		ret = pmbus_read_word_data(client, page, phase,
4462306a36Sopenharmony_ci					   IR35221_MFR_TEMP_PEAK);
4562306a36Sopenharmony_ci		break;
4662306a36Sopenharmony_ci	case PMBUS_VIRT_READ_VIN_MIN:
4762306a36Sopenharmony_ci		ret = pmbus_read_word_data(client, page, phase,
4862306a36Sopenharmony_ci					   IR35221_MFR_VIN_VALLEY);
4962306a36Sopenharmony_ci		break;
5062306a36Sopenharmony_ci	case PMBUS_VIRT_READ_VOUT_MIN:
5162306a36Sopenharmony_ci		ret = pmbus_read_word_data(client, page, phase,
5262306a36Sopenharmony_ci					   IR35221_MFR_VOUT_VALLEY);
5362306a36Sopenharmony_ci		break;
5462306a36Sopenharmony_ci	case PMBUS_VIRT_READ_IOUT_MIN:
5562306a36Sopenharmony_ci		ret = pmbus_read_word_data(client, page, phase,
5662306a36Sopenharmony_ci					   IR35221_MFR_IOUT_VALLEY);
5762306a36Sopenharmony_ci		break;
5862306a36Sopenharmony_ci	case PMBUS_VIRT_READ_TEMP_MIN:
5962306a36Sopenharmony_ci		ret = pmbus_read_word_data(client, page, phase,
6062306a36Sopenharmony_ci					   IR35221_MFR_TEMP_VALLEY);
6162306a36Sopenharmony_ci		break;
6262306a36Sopenharmony_ci	default:
6362306a36Sopenharmony_ci		ret = -ENODATA;
6462306a36Sopenharmony_ci		break;
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	return ret;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic int ir35221_probe(struct i2c_client *client)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct pmbus_driver_info *info;
7362306a36Sopenharmony_ci	u8 buf[I2C_SMBUS_BLOCK_MAX];
7462306a36Sopenharmony_ci	int ret;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	if (!i2c_check_functionality(client->adapter,
7762306a36Sopenharmony_ci				     I2C_FUNC_SMBUS_READ_BYTE_DATA
7862306a36Sopenharmony_ci				| I2C_FUNC_SMBUS_READ_WORD_DATA
7962306a36Sopenharmony_ci				| I2C_FUNC_SMBUS_READ_BLOCK_DATA))
8062306a36Sopenharmony_ci		return -ENODEV;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf);
8362306a36Sopenharmony_ci	if (ret < 0) {
8462306a36Sopenharmony_ci		dev_err(&client->dev, "Failed to read PMBUS_MFR_ID\n");
8562306a36Sopenharmony_ci		return ret;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci	if (ret != 2 || strncmp(buf, "RI", strlen("RI"))) {
8862306a36Sopenharmony_ci		dev_err(&client->dev, "MFR_ID unrecognised\n");
8962306a36Sopenharmony_ci		return -ENODEV;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf);
9362306a36Sopenharmony_ci	if (ret < 0) {
9462306a36Sopenharmony_ci		dev_err(&client->dev, "Failed to read PMBUS_MFR_MODEL\n");
9562306a36Sopenharmony_ci		return ret;
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci	if (ret != 2 || !(buf[0] == 0x6c && buf[1] == 0x00)) {
9862306a36Sopenharmony_ci		dev_err(&client->dev, "MFR_MODEL unrecognised\n");
9962306a36Sopenharmony_ci		return -ENODEV;
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	info = devm_kzalloc(&client->dev, sizeof(struct pmbus_driver_info),
10362306a36Sopenharmony_ci			    GFP_KERNEL);
10462306a36Sopenharmony_ci	if (!info)
10562306a36Sopenharmony_ci		return -ENOMEM;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	info->read_word_data = ir35221_read_word_data;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	info->pages = 2;
11062306a36Sopenharmony_ci	info->format[PSC_VOLTAGE_IN] = linear;
11162306a36Sopenharmony_ci	info->format[PSC_VOLTAGE_OUT] = linear;
11262306a36Sopenharmony_ci	info->format[PSC_CURRENT_IN] = linear;
11362306a36Sopenharmony_ci	info->format[PSC_CURRENT_OUT] = linear;
11462306a36Sopenharmony_ci	info->format[PSC_POWER] = linear;
11562306a36Sopenharmony_ci	info->format[PSC_TEMPERATURE] = linear;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	info->func[0] = PMBUS_HAVE_VIN
11862306a36Sopenharmony_ci		| PMBUS_HAVE_VOUT | PMBUS_HAVE_IIN
11962306a36Sopenharmony_ci		| PMBUS_HAVE_IOUT | PMBUS_HAVE_PIN
12062306a36Sopenharmony_ci		| PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP
12162306a36Sopenharmony_ci		| PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT
12262306a36Sopenharmony_ci		| PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP;
12362306a36Sopenharmony_ci	info->func[1] = info->func[0];
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	return pmbus_do_probe(client, info);
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic const struct i2c_device_id ir35221_id[] = {
12962306a36Sopenharmony_ci	{"ir35221", 0},
13062306a36Sopenharmony_ci	{}
13162306a36Sopenharmony_ci};
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ir35221_id);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic struct i2c_driver ir35221_driver = {
13662306a36Sopenharmony_ci	.driver = {
13762306a36Sopenharmony_ci		.name	= "ir35221",
13862306a36Sopenharmony_ci	},
13962306a36Sopenharmony_ci	.probe		= ir35221_probe,
14062306a36Sopenharmony_ci	.id_table	= ir35221_id,
14162306a36Sopenharmony_ci};
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cimodule_i2c_driver(ir35221_driver);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ciMODULE_AUTHOR("Samuel Mendoza-Jonas <sam@mendozajonas.com");
14662306a36Sopenharmony_ciMODULE_DESCRIPTION("PMBus driver for IR35221");
14762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
14862306a36Sopenharmony_ciMODULE_IMPORT_NS(PMBUS);
149