xref: /linux/drivers/hwmon/pmbus/fsp-3y.c (revision 1734b4135a62fd2402232346b809e99177ea6b4c)
1*1734b413SVáclav Kubernát // SPDX-License-Identifier: GPL-2.0-or-later
2*1734b413SVáclav Kubernát /*
3*1734b413SVáclav Kubernát  * Hardware monitoring driver for FSP 3Y-Power PSUs
4*1734b413SVáclav Kubernát  *
5*1734b413SVáclav Kubernát  * Copyright (c) 2021 Václav Kubernát, CESNET
6*1734b413SVáclav Kubernát  *
7*1734b413SVáclav Kubernát  * This driver is mostly reverse engineered with the help of a tool called pmbus_peek written by
8*1734b413SVáclav Kubernát  * David Brownell (and later adopted by Jan Kundrát). The device has some sort of a timing issue
9*1734b413SVáclav Kubernát  * when switching pages, details are explained in the code. The driver support is limited. It
10*1734b413SVáclav Kubernát  * exposes only the values, that have been tested to work correctly. Unsupported values either
11*1734b413SVáclav Kubernát  * aren't supported by the devices or their encondings are unknown.
12*1734b413SVáclav Kubernát  */
13*1734b413SVáclav Kubernát 
14*1734b413SVáclav Kubernát #include <linux/delay.h>
15*1734b413SVáclav Kubernát #include <linux/i2c.h>
16*1734b413SVáclav Kubernát #include <linux/kernel.h>
17*1734b413SVáclav Kubernát #include <linux/module.h>
18*1734b413SVáclav Kubernát #include "pmbus.h"
19*1734b413SVáclav Kubernát 
20*1734b413SVáclav Kubernát #define YM2151_PAGE_12V_LOG	0x00
21*1734b413SVáclav Kubernát #define YM2151_PAGE_12V_REAL	0x00
22*1734b413SVáclav Kubernát #define YM2151_PAGE_5VSB_LOG	0x01
23*1734b413SVáclav Kubernát #define YM2151_PAGE_5VSB_REAL	0x20
24*1734b413SVáclav Kubernát #define YH5151E_PAGE_12V_LOG	0x00
25*1734b413SVáclav Kubernát #define YH5151E_PAGE_12V_REAL	0x00
26*1734b413SVáclav Kubernát #define YH5151E_PAGE_5V_LOG	0x01
27*1734b413SVáclav Kubernát #define YH5151E_PAGE_5V_REAL	0x10
28*1734b413SVáclav Kubernát #define YH5151E_PAGE_3V3_LOG	0x02
29*1734b413SVáclav Kubernát #define YH5151E_PAGE_3V3_REAL	0x11
30*1734b413SVáclav Kubernát 
31*1734b413SVáclav Kubernát enum chips {
32*1734b413SVáclav Kubernát 	ym2151e,
33*1734b413SVáclav Kubernát 	yh5151e
34*1734b413SVáclav Kubernát };
35*1734b413SVáclav Kubernát 
36*1734b413SVáclav Kubernát struct fsp3y_data {
37*1734b413SVáclav Kubernát 	struct pmbus_driver_info info;
38*1734b413SVáclav Kubernát 	int chip;
39*1734b413SVáclav Kubernát 	int page;
40*1734b413SVáclav Kubernát };
41*1734b413SVáclav Kubernát 
42*1734b413SVáclav Kubernát #define to_fsp3y_data(x) container_of(x, struct fsp3y_data, info)
43*1734b413SVáclav Kubernát 
44*1734b413SVáclav Kubernát static int page_log_to_page_real(int page_log, enum chips chip)
45*1734b413SVáclav Kubernát {
46*1734b413SVáclav Kubernát 	switch (chip) {
47*1734b413SVáclav Kubernát 	case ym2151e:
48*1734b413SVáclav Kubernát 		switch (page_log) {
49*1734b413SVáclav Kubernát 		case YM2151_PAGE_12V_LOG:
50*1734b413SVáclav Kubernát 			return YM2151_PAGE_12V_REAL;
51*1734b413SVáclav Kubernát 		case YM2151_PAGE_5VSB_LOG:
52*1734b413SVáclav Kubernát 			return YM2151_PAGE_5VSB_REAL;
53*1734b413SVáclav Kubernát 		}
54*1734b413SVáclav Kubernát 		return -EINVAL;
55*1734b413SVáclav Kubernát 	case yh5151e:
56*1734b413SVáclav Kubernát 		switch (page_log) {
57*1734b413SVáclav Kubernát 		case YH5151E_PAGE_12V_LOG:
58*1734b413SVáclav Kubernát 			return YH5151E_PAGE_12V_REAL;
59*1734b413SVáclav Kubernát 		case YH5151E_PAGE_5V_LOG:
60*1734b413SVáclav Kubernát 			return YH5151E_PAGE_5V_LOG;
61*1734b413SVáclav Kubernát 		case YH5151E_PAGE_3V3_LOG:
62*1734b413SVáclav Kubernát 			return YH5151E_PAGE_3V3_REAL;
63*1734b413SVáclav Kubernát 		}
64*1734b413SVáclav Kubernát 		return -EINVAL;
65*1734b413SVáclav Kubernát 	}
66*1734b413SVáclav Kubernát 
67*1734b413SVáclav Kubernát 	return -EINVAL;
68*1734b413SVáclav Kubernát }
69*1734b413SVáclav Kubernát 
70*1734b413SVáclav Kubernát static int set_page(struct i2c_client *client, int page_log)
71*1734b413SVáclav Kubernát {
72*1734b413SVáclav Kubernát 	const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
73*1734b413SVáclav Kubernát 	struct fsp3y_data *data = to_fsp3y_data(info);
74*1734b413SVáclav Kubernát 	int rv;
75*1734b413SVáclav Kubernát 	int page_real;
76*1734b413SVáclav Kubernát 
77*1734b413SVáclav Kubernát 	if (page_log < 0)
78*1734b413SVáclav Kubernát 		return 0;
79*1734b413SVáclav Kubernát 
80*1734b413SVáclav Kubernát 	page_real = page_log_to_page_real(page_log, data->chip);
81*1734b413SVáclav Kubernát 	if (page_real < 0)
82*1734b413SVáclav Kubernát 		return page_real;
83*1734b413SVáclav Kubernát 
84*1734b413SVáclav Kubernát 	if (data->page != page_real) {
85*1734b413SVáclav Kubernát 		rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page_real);
86*1734b413SVáclav Kubernát 		if (rv < 0)
87*1734b413SVáclav Kubernát 			return rv;
88*1734b413SVáclav Kubernát 
89*1734b413SVáclav Kubernát 		data->page = page_real;
90*1734b413SVáclav Kubernát 
91*1734b413SVáclav Kubernát 		/*
92*1734b413SVáclav Kubernát 		 * Testing showed that the device has a timing issue. After
93*1734b413SVáclav Kubernát 		 * setting a page, it takes a while, before the device actually
94*1734b413SVáclav Kubernát 		 * gives the correct values from the correct page. 20 ms was
95*1734b413SVáclav Kubernát 		 * tested to be enough to not give wrong values (15 ms wasn't
96*1734b413SVáclav Kubernát 		 * enough).
97*1734b413SVáclav Kubernát 		 */
98*1734b413SVáclav Kubernát 		usleep_range(20000, 30000);
99*1734b413SVáclav Kubernát 	}
100*1734b413SVáclav Kubernát 
101*1734b413SVáclav Kubernát 	return 0;
102*1734b413SVáclav Kubernát }
103*1734b413SVáclav Kubernát 
104*1734b413SVáclav Kubernát static int fsp3y_read_byte_data(struct i2c_client *client, int page, int reg)
105*1734b413SVáclav Kubernát {
106*1734b413SVáclav Kubernát 	int rv;
107*1734b413SVáclav Kubernát 
108*1734b413SVáclav Kubernát 	rv = set_page(client, page);
109*1734b413SVáclav Kubernát 	if (rv < 0)
110*1734b413SVáclav Kubernát 		return rv;
111*1734b413SVáclav Kubernát 
112*1734b413SVáclav Kubernát 	return i2c_smbus_read_byte_data(client, reg);
113*1734b413SVáclav Kubernát }
114*1734b413SVáclav Kubernát 
115*1734b413SVáclav Kubernát static int fsp3y_read_word_data(struct i2c_client *client, int page, int phase, int reg)
116*1734b413SVáclav Kubernát {
117*1734b413SVáclav Kubernát 	int rv;
118*1734b413SVáclav Kubernát 
119*1734b413SVáclav Kubernát 	/*
120*1734b413SVáclav Kubernát 	 * This masks commands which weren't tested to work correctly. Some of
121*1734b413SVáclav Kubernát 	 * the masked commands return 0xFFFF. These would probably get tagged as
122*1734b413SVáclav Kubernát 	 * invalid by pmbus_core. Other ones do return values which might be
123*1734b413SVáclav Kubernát 	 * useful (that is, they are not 0xFFFF), but their encoding is unknown,
124*1734b413SVáclav Kubernát 	 * and so they are unsupported.
125*1734b413SVáclav Kubernát 	 */
126*1734b413SVáclav Kubernát 	switch (reg) {
127*1734b413SVáclav Kubernát 	case PMBUS_READ_FAN_SPEED_1:
128*1734b413SVáclav Kubernát 	case PMBUS_READ_IIN:
129*1734b413SVáclav Kubernát 	case PMBUS_READ_IOUT:
130*1734b413SVáclav Kubernát 	case PMBUS_READ_PIN:
131*1734b413SVáclav Kubernát 	case PMBUS_READ_POUT:
132*1734b413SVáclav Kubernát 	case PMBUS_READ_TEMPERATURE_1:
133*1734b413SVáclav Kubernát 	case PMBUS_READ_TEMPERATURE_2:
134*1734b413SVáclav Kubernát 	case PMBUS_READ_TEMPERATURE_3:
135*1734b413SVáclav Kubernát 	case PMBUS_READ_VIN:
136*1734b413SVáclav Kubernát 	case PMBUS_READ_VOUT:
137*1734b413SVáclav Kubernát 	case PMBUS_STATUS_WORD:
138*1734b413SVáclav Kubernát 		break;
139*1734b413SVáclav Kubernát 	default:
140*1734b413SVáclav Kubernát 		return -ENXIO;
141*1734b413SVáclav Kubernát 	}
142*1734b413SVáclav Kubernát 
143*1734b413SVáclav Kubernát 	rv = set_page(client, page);
144*1734b413SVáclav Kubernát 	if (rv < 0)
145*1734b413SVáclav Kubernát 		return rv;
146*1734b413SVáclav Kubernát 
147*1734b413SVáclav Kubernát 	return i2c_smbus_read_word_data(client, reg);
148*1734b413SVáclav Kubernát }
149*1734b413SVáclav Kubernát 
150*1734b413SVáclav Kubernát static struct pmbus_driver_info fsp3y_info[] = {
151*1734b413SVáclav Kubernát 	[ym2151e] = {
152*1734b413SVáclav Kubernát 		.pages = 2,
153*1734b413SVáclav Kubernát 		.func[YM2151_PAGE_12V_LOG] =
154*1734b413SVáclav Kubernát 			PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
155*1734b413SVáclav Kubernát 			PMBUS_HAVE_PIN | PMBUS_HAVE_POUT  |
156*1734b413SVáclav Kubernát 			PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 |
157*1734b413SVáclav Kubernát 			PMBUS_HAVE_VIN | PMBUS_HAVE_IIN |
158*1734b413SVáclav Kubernát 			PMBUS_HAVE_FAN12,
159*1734b413SVáclav Kubernát 		.func[YM2151_PAGE_5VSB_LOG] =
160*1734b413SVáclav Kubernát 			PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT,
161*1734b413SVáclav Kubernát 			PMBUS_HAVE_IIN,
162*1734b413SVáclav Kubernát 		.read_word_data = fsp3y_read_word_data,
163*1734b413SVáclav Kubernát 		.read_byte_data = fsp3y_read_byte_data,
164*1734b413SVáclav Kubernát 	},
165*1734b413SVáclav Kubernát 	[yh5151e] = {
166*1734b413SVáclav Kubernát 		.pages = 3,
167*1734b413SVáclav Kubernát 		.func[YH5151E_PAGE_12V_LOG] =
168*1734b413SVáclav Kubernát 			PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
169*1734b413SVáclav Kubernát 			PMBUS_HAVE_POUT  |
170*1734b413SVáclav Kubernát 			PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3,
171*1734b413SVáclav Kubernát 		.func[YH5151E_PAGE_5V_LOG] =
172*1734b413SVáclav Kubernát 			PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
173*1734b413SVáclav Kubernát 			PMBUS_HAVE_POUT,
174*1734b413SVáclav Kubernát 		.func[YH5151E_PAGE_3V3_LOG] =
175*1734b413SVáclav Kubernát 			PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
176*1734b413SVáclav Kubernát 			PMBUS_HAVE_POUT,
177*1734b413SVáclav Kubernát 		.read_word_data = fsp3y_read_word_data,
178*1734b413SVáclav Kubernát 		.read_byte_data = fsp3y_read_byte_data,
179*1734b413SVáclav Kubernát 	}
180*1734b413SVáclav Kubernát };
181*1734b413SVáclav Kubernát 
182*1734b413SVáclav Kubernát static int fsp3y_detect(struct i2c_client *client)
183*1734b413SVáclav Kubernát {
184*1734b413SVáclav Kubernát 	int rv;
185*1734b413SVáclav Kubernát 	u8 buf[I2C_SMBUS_BLOCK_MAX + 1];
186*1734b413SVáclav Kubernát 
187*1734b413SVáclav Kubernát 	rv = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf);
188*1734b413SVáclav Kubernát 	if (rv < 0)
189*1734b413SVáclav Kubernát 		return rv;
190*1734b413SVáclav Kubernát 
191*1734b413SVáclav Kubernát 	buf[rv] = '\0';
192*1734b413SVáclav Kubernát 
193*1734b413SVáclav Kubernát 	if (rv == 8) {
194*1734b413SVáclav Kubernát 		if (!strcmp(buf, "YM-2151E"))
195*1734b413SVáclav Kubernát 			return ym2151e;
196*1734b413SVáclav Kubernát 		else if (!strcmp(buf, "YH-5151E"))
197*1734b413SVáclav Kubernát 			return yh5151e;
198*1734b413SVáclav Kubernát 	}
199*1734b413SVáclav Kubernát 
200*1734b413SVáclav Kubernát 	dev_err(&client->dev, "Unsupported model %.*s\n", rv, buf);
201*1734b413SVáclav Kubernát 	return -ENODEV;
202*1734b413SVáclav Kubernát }
203*1734b413SVáclav Kubernát 
204*1734b413SVáclav Kubernát static const struct i2c_device_id fsp3y_id[] = {
205*1734b413SVáclav Kubernát 	{"ym2151e", ym2151e},
206*1734b413SVáclav Kubernát 	{"yh5151e", yh5151e},
207*1734b413SVáclav Kubernát 	{ }
208*1734b413SVáclav Kubernát };
209*1734b413SVáclav Kubernát 
210*1734b413SVáclav Kubernát static int fsp3y_probe(struct i2c_client *client)
211*1734b413SVáclav Kubernát {
212*1734b413SVáclav Kubernát 	struct fsp3y_data *data;
213*1734b413SVáclav Kubernát 	const struct i2c_device_id *id;
214*1734b413SVáclav Kubernát 	int rv;
215*1734b413SVáclav Kubernát 
216*1734b413SVáclav Kubernát 	data = devm_kzalloc(&client->dev, sizeof(struct fsp3y_data), GFP_KERNEL);
217*1734b413SVáclav Kubernát 	if (!data)
218*1734b413SVáclav Kubernát 		return -ENOMEM;
219*1734b413SVáclav Kubernát 
220*1734b413SVáclav Kubernát 	data->chip = fsp3y_detect(client);
221*1734b413SVáclav Kubernát 	if (data->chip < 0)
222*1734b413SVáclav Kubernát 		return data->chip;
223*1734b413SVáclav Kubernát 
224*1734b413SVáclav Kubernát 	id = i2c_match_id(fsp3y_id, client);
225*1734b413SVáclav Kubernát 	if (data->chip != id->driver_data)
226*1734b413SVáclav Kubernát 		dev_warn(&client->dev, "Device mismatch: Configured %s (%d), detected %d\n",
227*1734b413SVáclav Kubernát 			 id->name, (int)id->driver_data, data->chip);
228*1734b413SVáclav Kubernát 
229*1734b413SVáclav Kubernát 	rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE);
230*1734b413SVáclav Kubernát 	if (rv < 0)
231*1734b413SVáclav Kubernát 		return rv;
232*1734b413SVáclav Kubernát 	data->page = rv;
233*1734b413SVáclav Kubernát 
234*1734b413SVáclav Kubernát 	data->info = fsp3y_info[data->chip];
235*1734b413SVáclav Kubernát 
236*1734b413SVáclav Kubernát 	return pmbus_do_probe(client, &data->info);
237*1734b413SVáclav Kubernát }
238*1734b413SVáclav Kubernát 
239*1734b413SVáclav Kubernát MODULE_DEVICE_TABLE(i2c, fsp3y_id);
240*1734b413SVáclav Kubernát 
241*1734b413SVáclav Kubernát static struct i2c_driver fsp3y_driver = {
242*1734b413SVáclav Kubernát 	.driver = {
243*1734b413SVáclav Kubernát 		   .name = "fsp3y",
244*1734b413SVáclav Kubernát 		   },
245*1734b413SVáclav Kubernát 	.probe_new = fsp3y_probe,
246*1734b413SVáclav Kubernát 	.id_table = fsp3y_id
247*1734b413SVáclav Kubernát };
248*1734b413SVáclav Kubernát 
249*1734b413SVáclav Kubernát module_i2c_driver(fsp3y_driver);
250*1734b413SVáclav Kubernát 
251*1734b413SVáclav Kubernát MODULE_AUTHOR("Václav Kubernát");
252*1734b413SVáclav Kubernát MODULE_DESCRIPTION("PMBus driver for FSP/3Y-Power power supplies");
253*1734b413SVáclav Kubernát MODULE_LICENSE("GPL");
254