xref: /linux/drivers/hwmon/pmbus/fsp-3y.c (revision b94ca77eeae79258bc7497ebe47bb5c085acf002)
11734b413SVáclav Kubernát // SPDX-License-Identifier: GPL-2.0-or-later
21734b413SVáclav Kubernát /*
31734b413SVáclav Kubernát  * Hardware monitoring driver for FSP 3Y-Power PSUs
41734b413SVáclav Kubernát  *
51734b413SVáclav Kubernát  * Copyright (c) 2021 Václav Kubernát, CESNET
61734b413SVáclav Kubernát  *
71734b413SVáclav Kubernát  * This driver is mostly reverse engineered with the help of a tool called pmbus_peek written by
81734b413SVáclav Kubernát  * David Brownell (and later adopted by Jan Kundrát). The device has some sort of a timing issue
91734b413SVáclav Kubernát  * when switching pages, details are explained in the code. The driver support is limited. It
101734b413SVáclav Kubernát  * exposes only the values, that have been tested to work correctly. Unsupported values either
111734b413SVáclav Kubernát  * aren't supported by the devices or their encondings are unknown.
121734b413SVáclav Kubernát  */
131734b413SVáclav Kubernát 
141734b413SVáclav Kubernát #include <linux/delay.h>
151734b413SVáclav Kubernát #include <linux/i2c.h>
161734b413SVáclav Kubernát #include <linux/kernel.h>
171734b413SVáclav Kubernát #include <linux/module.h>
181734b413SVáclav Kubernát #include "pmbus.h"
191734b413SVáclav Kubernát 
201734b413SVáclav Kubernát #define YM2151_PAGE_12V_LOG	0x00
211734b413SVáclav Kubernát #define YM2151_PAGE_12V_REAL	0x00
221734b413SVáclav Kubernát #define YM2151_PAGE_5VSB_LOG	0x01
231734b413SVáclav Kubernát #define YM2151_PAGE_5VSB_REAL	0x20
241734b413SVáclav Kubernát #define YH5151E_PAGE_12V_LOG	0x00
251734b413SVáclav Kubernát #define YH5151E_PAGE_12V_REAL	0x00
261734b413SVáclav Kubernát #define YH5151E_PAGE_5V_LOG	0x01
271734b413SVáclav Kubernát #define YH5151E_PAGE_5V_REAL	0x10
281734b413SVáclav Kubernát #define YH5151E_PAGE_3V3_LOG	0x02
291734b413SVáclav Kubernát #define YH5151E_PAGE_3V3_REAL	0x11
301734b413SVáclav Kubernát 
311734b413SVáclav Kubernát enum chips {
321734b413SVáclav Kubernát 	ym2151e,
331734b413SVáclav Kubernát 	yh5151e
341734b413SVáclav Kubernát };
351734b413SVáclav Kubernát 
361734b413SVáclav Kubernát struct fsp3y_data {
371734b413SVáclav Kubernát 	struct pmbus_driver_info info;
381734b413SVáclav Kubernát 	int chip;
391734b413SVáclav Kubernát 	int page;
401734b413SVáclav Kubernát };
411734b413SVáclav Kubernát 
421734b413SVáclav Kubernát #define to_fsp3y_data(x) container_of(x, struct fsp3y_data, info)
431734b413SVáclav Kubernát 
441734b413SVáclav Kubernát static int page_log_to_page_real(int page_log, enum chips chip)
451734b413SVáclav Kubernát {
461734b413SVáclav Kubernát 	switch (chip) {
471734b413SVáclav Kubernát 	case ym2151e:
481734b413SVáclav Kubernát 		switch (page_log) {
491734b413SVáclav Kubernát 		case YM2151_PAGE_12V_LOG:
501734b413SVáclav Kubernát 			return YM2151_PAGE_12V_REAL;
511734b413SVáclav Kubernát 		case YM2151_PAGE_5VSB_LOG:
521734b413SVáclav Kubernát 			return YM2151_PAGE_5VSB_REAL;
531734b413SVáclav Kubernát 		}
541734b413SVáclav Kubernát 		return -EINVAL;
551734b413SVáclav Kubernát 	case yh5151e:
561734b413SVáclav Kubernát 		switch (page_log) {
571734b413SVáclav Kubernát 		case YH5151E_PAGE_12V_LOG:
581734b413SVáclav Kubernát 			return YH5151E_PAGE_12V_REAL;
591734b413SVáclav Kubernát 		case YH5151E_PAGE_5V_LOG:
601734b413SVáclav Kubernát 			return YH5151E_PAGE_5V_LOG;
611734b413SVáclav Kubernát 		case YH5151E_PAGE_3V3_LOG:
621734b413SVáclav Kubernát 			return YH5151E_PAGE_3V3_REAL;
631734b413SVáclav Kubernát 		}
641734b413SVáclav Kubernát 		return -EINVAL;
651734b413SVáclav Kubernát 	}
661734b413SVáclav Kubernát 
671734b413SVáclav Kubernát 	return -EINVAL;
681734b413SVáclav Kubernát }
691734b413SVáclav Kubernát 
701734b413SVáclav Kubernát static int set_page(struct i2c_client *client, int page_log)
711734b413SVáclav Kubernát {
721734b413SVáclav Kubernát 	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
731734b413SVáclav Kubernát 	struct fsp3y_data *data = to_fsp3y_data(info);
741734b413SVáclav Kubernát 	int rv;
751734b413SVáclav Kubernát 	int page_real;
761734b413SVáclav Kubernát 
771734b413SVáclav Kubernát 	if (page_log < 0)
781734b413SVáclav Kubernát 		return 0;
791734b413SVáclav Kubernát 
801734b413SVáclav Kubernát 	page_real = page_log_to_page_real(page_log, data->chip);
811734b413SVáclav Kubernát 	if (page_real < 0)
821734b413SVáclav Kubernát 		return page_real;
831734b413SVáclav Kubernát 
841734b413SVáclav Kubernát 	if (data->page != page_real) {
851734b413SVáclav Kubernát 		rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page_real);
861734b413SVáclav Kubernát 		if (rv < 0)
871734b413SVáclav Kubernát 			return rv;
881734b413SVáclav Kubernát 
891734b413SVáclav Kubernát 		data->page = page_real;
901734b413SVáclav Kubernát 
911734b413SVáclav Kubernát 		/*
921734b413SVáclav Kubernát 		 * Testing showed that the device has a timing issue. After
931734b413SVáclav Kubernát 		 * setting a page, it takes a while, before the device actually
941734b413SVáclav Kubernát 		 * gives the correct values from the correct page. 20 ms was
951734b413SVáclav Kubernát 		 * tested to be enough to not give wrong values (15 ms wasn't
961734b413SVáclav Kubernát 		 * enough).
971734b413SVáclav Kubernát 		 */
981734b413SVáclav Kubernát 		usleep_range(20000, 30000);
991734b413SVáclav Kubernát 	}
1001734b413SVáclav Kubernát 
1011734b413SVáclav Kubernát 	return 0;
1021734b413SVáclav Kubernát }
1031734b413SVáclav Kubernát 
1041734b413SVáclav Kubernát static int fsp3y_read_byte_data(struct i2c_client *client, int page, int reg)
1051734b413SVáclav Kubernát {
1061734b413SVáclav Kubernát 	int rv;
1071734b413SVáclav Kubernát 
1081734b413SVáclav Kubernát 	rv = set_page(client, page);
1091734b413SVáclav Kubernát 	if (rv < 0)
1101734b413SVáclav Kubernát 		return rv;
1111734b413SVáclav Kubernát 
1121734b413SVáclav Kubernát 	return i2c_smbus_read_byte_data(client, reg);
1131734b413SVáclav Kubernát }
1141734b413SVáclav Kubernát 
1151734b413SVáclav Kubernát static int fsp3y_read_word_data(struct i2c_client *client, int page, int phase, int reg)
1161734b413SVáclav Kubernát {
1171734b413SVáclav Kubernát 	int rv;
1181734b413SVáclav Kubernát 
1191734b413SVáclav Kubernát 	/*
1201734b413SVáclav Kubernát 	 * This masks commands which weren't tested to work correctly. Some of
1211734b413SVáclav Kubernát 	 * the masked commands return 0xFFFF. These would probably get tagged as
1221734b413SVáclav Kubernát 	 * invalid by pmbus_core. Other ones do return values which might be
1231734b413SVáclav Kubernát 	 * useful (that is, they are not 0xFFFF), but their encoding is unknown,
1241734b413SVáclav Kubernát 	 * and so they are unsupported.
1251734b413SVáclav Kubernát 	 */
1261734b413SVáclav Kubernát 	switch (reg) {
1271734b413SVáclav Kubernát 	case PMBUS_READ_FAN_SPEED_1:
1281734b413SVáclav Kubernát 	case PMBUS_READ_IIN:
1291734b413SVáclav Kubernát 	case PMBUS_READ_IOUT:
1301734b413SVáclav Kubernát 	case PMBUS_READ_PIN:
1311734b413SVáclav Kubernát 	case PMBUS_READ_POUT:
1321734b413SVáclav Kubernát 	case PMBUS_READ_TEMPERATURE_1:
1331734b413SVáclav Kubernát 	case PMBUS_READ_TEMPERATURE_2:
1341734b413SVáclav Kubernát 	case PMBUS_READ_TEMPERATURE_3:
1351734b413SVáclav Kubernát 	case PMBUS_READ_VIN:
1361734b413SVáclav Kubernát 	case PMBUS_READ_VOUT:
1371734b413SVáclav Kubernát 	case PMBUS_STATUS_WORD:
1381734b413SVáclav Kubernát 		break;
1391734b413SVáclav Kubernát 	default:
1401734b413SVáclav Kubernát 		return -ENXIO;
1411734b413SVáclav Kubernát 	}
1421734b413SVáclav Kubernát 
1431734b413SVáclav Kubernát 	rv = set_page(client, page);
1441734b413SVáclav Kubernát 	if (rv < 0)
1451734b413SVáclav Kubernát 		return rv;
1461734b413SVáclav Kubernát 
1471734b413SVáclav Kubernát 	return i2c_smbus_read_word_data(client, reg);
1481734b413SVáclav Kubernát }
1491734b413SVáclav Kubernát 
1501734b413SVáclav Kubernát static struct pmbus_driver_info fsp3y_info[] = {
1511734b413SVáclav Kubernát 	[ym2151e] = {
1521734b413SVáclav Kubernát 		.pages = 2,
1531734b413SVáclav Kubernát 		.func[YM2151_PAGE_12V_LOG] =
1541734b413SVáclav Kubernát 			PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
1551734b413SVáclav Kubernát 			PMBUS_HAVE_PIN | PMBUS_HAVE_POUT  |
1561734b413SVáclav Kubernát 			PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 |
1571734b413SVáclav Kubernát 			PMBUS_HAVE_VIN | PMBUS_HAVE_IIN |
1581734b413SVáclav Kubernát 			PMBUS_HAVE_FAN12,
1591734b413SVáclav Kubernát 		.func[YM2151_PAGE_5VSB_LOG] =
1601734b413SVáclav Kubernát 			PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT,
1611734b413SVáclav Kubernát 			PMBUS_HAVE_IIN,
1621734b413SVáclav Kubernát 		.read_word_data = fsp3y_read_word_data,
1631734b413SVáclav Kubernát 		.read_byte_data = fsp3y_read_byte_data,
1641734b413SVáclav Kubernát 	},
1651734b413SVáclav Kubernát 	[yh5151e] = {
1661734b413SVáclav Kubernát 		.pages = 3,
1671734b413SVáclav Kubernát 		.func[YH5151E_PAGE_12V_LOG] =
1681734b413SVáclav Kubernát 			PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
1691734b413SVáclav Kubernát 			PMBUS_HAVE_POUT  |
1701734b413SVáclav Kubernát 			PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3,
1711734b413SVáclav Kubernát 		.func[YH5151E_PAGE_5V_LOG] =
1721734b413SVáclav Kubernát 			PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
1731734b413SVáclav Kubernát 			PMBUS_HAVE_POUT,
1741734b413SVáclav Kubernát 		.func[YH5151E_PAGE_3V3_LOG] =
1751734b413SVáclav Kubernát 			PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
1761734b413SVáclav Kubernát 			PMBUS_HAVE_POUT,
1771734b413SVáclav Kubernát 		.read_word_data = fsp3y_read_word_data,
1781734b413SVáclav Kubernát 		.read_byte_data = fsp3y_read_byte_data,
1791734b413SVáclav Kubernát 	}
1801734b413SVáclav Kubernát };
1811734b413SVáclav Kubernát 
1821734b413SVáclav Kubernát static int fsp3y_detect(struct i2c_client *client)
1831734b413SVáclav Kubernát {
1841734b413SVáclav Kubernát 	int rv;
1851734b413SVáclav Kubernát 	u8 buf[I2C_SMBUS_BLOCK_MAX + 1];
1861734b413SVáclav Kubernát 
1871734b413SVáclav Kubernát 	rv = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf);
1881734b413SVáclav Kubernát 	if (rv < 0)
1891734b413SVáclav Kubernát 		return rv;
1901734b413SVáclav Kubernát 
1911734b413SVáclav Kubernát 	buf[rv] = '\0';
1921734b413SVáclav Kubernát 
1931734b413SVáclav Kubernát 	if (rv == 8) {
1941734b413SVáclav Kubernát 		if (!strcmp(buf, "YM-2151E"))
1951734b413SVáclav Kubernát 			return ym2151e;
1961734b413SVáclav Kubernát 		else if (!strcmp(buf, "YH-5151E"))
1971734b413SVáclav Kubernát 			return yh5151e;
1981734b413SVáclav Kubernát 	}
1991734b413SVáclav Kubernát 
2001734b413SVáclav Kubernát 	dev_err(&client->dev, "Unsupported model %.*s\n", rv, buf);
2011734b413SVáclav Kubernát 	return -ENODEV;
2021734b413SVáclav Kubernát }
2031734b413SVáclav Kubernát 
2041734b413SVáclav Kubernát static const struct i2c_device_id fsp3y_id[] = {
2051734b413SVáclav Kubernát 	{"ym2151e", ym2151e},
2061734b413SVáclav Kubernát 	{"yh5151e", yh5151e},
2071734b413SVáclav Kubernát 	{ }
2081734b413SVáclav Kubernát };
2091734b413SVáclav Kubernát 
2101734b413SVáclav Kubernát static int fsp3y_probe(struct i2c_client *client)
2111734b413SVáclav Kubernát {
2121734b413SVáclav Kubernát 	struct fsp3y_data *data;
2131734b413SVáclav Kubernát 	const struct i2c_device_id *id;
2141734b413SVáclav Kubernát 	int rv;
2151734b413SVáclav Kubernát 
2161734b413SVáclav Kubernát 	data = devm_kzalloc(&client->dev, sizeof(struct fsp3y_data), GFP_KERNEL);
2171734b413SVáclav Kubernát 	if (!data)
2181734b413SVáclav Kubernát 		return -ENOMEM;
2191734b413SVáclav Kubernát 
2201734b413SVáclav Kubernát 	data->chip = fsp3y_detect(client);
2211734b413SVáclav Kubernát 	if (data->chip < 0)
2221734b413SVáclav Kubernát 		return data->chip;
2231734b413SVáclav Kubernát 
2241734b413SVáclav Kubernát 	id = i2c_match_id(fsp3y_id, client);
2251734b413SVáclav Kubernát 	if (data->chip != id->driver_data)
2261734b413SVáclav Kubernát 		dev_warn(&client->dev, "Device mismatch: Configured %s (%d), detected %d\n",
2271734b413SVáclav Kubernát 			 id->name, (int)id->driver_data, data->chip);
2281734b413SVáclav Kubernát 
2291734b413SVáclav Kubernát 	rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE);
2301734b413SVáclav Kubernát 	if (rv < 0)
2311734b413SVáclav Kubernát 		return rv;
2321734b413SVáclav Kubernát 	data->page = rv;
2331734b413SVáclav Kubernát 
2341734b413SVáclav Kubernát 	data->info = fsp3y_info[data->chip];
2351734b413SVáclav Kubernát 
2361734b413SVáclav Kubernát 	return pmbus_do_probe(client, &data->info);
2371734b413SVáclav Kubernát }
2381734b413SVáclav Kubernát 
2391734b413SVáclav Kubernát MODULE_DEVICE_TABLE(i2c, fsp3y_id);
2401734b413SVáclav Kubernát 
2411734b413SVáclav Kubernát static struct i2c_driver fsp3y_driver = {
2421734b413SVáclav Kubernát 	.driver = {
2431734b413SVáclav Kubernát 		   .name = "fsp3y",
2441734b413SVáclav Kubernát 		   },
2451734b413SVáclav Kubernát 	.probe_new = fsp3y_probe,
2461734b413SVáclav Kubernát 	.id_table = fsp3y_id
2471734b413SVáclav Kubernát };
2481734b413SVáclav Kubernát 
2491734b413SVáclav Kubernát module_i2c_driver(fsp3y_driver);
2501734b413SVáclav Kubernát 
2511734b413SVáclav Kubernát MODULE_AUTHOR("Václav Kubernát");
2521734b413SVáclav Kubernát MODULE_DESCRIPTION("PMBus driver for FSP/3Y-Power power supplies");
2531734b413SVáclav Kubernát MODULE_LICENSE("GPL");
254*b94ca77eSGuenter Roeck MODULE_IMPORT_NS(PMBUS);
255