fsp-3y.c (d1e7c13a9b0c27c9440e00865a7c46b7a87767ee) fsp-3y.c (c2a338c9395eb843a9a11a2385f4b00cd0978494)
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Hardware monitoring driver for FSP 3Y-Power PSUs
4 *
5 * Copyright (c) 2021 Václav Kubernát, CESNET
6 *
7 * This driver is mostly reverse engineered with the help of a tool called pmbus_peek written by
8 * David Brownell (and later adopted by Jan Kundrát). The device has some sort of a timing issue

--- 23 unchanged lines hidden (view full) ---

32 ym2151e,
33 yh5151e
34};
35
36struct fsp3y_data {
37 struct pmbus_driver_info info;
38 int chip;
39 int page;
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Hardware monitoring driver for FSP 3Y-Power PSUs
4 *
5 * Copyright (c) 2021 Václav Kubernát, CESNET
6 *
7 * This driver is mostly reverse engineered with the help of a tool called pmbus_peek written by
8 * David Brownell (and later adopted by Jan Kundrát). The device has some sort of a timing issue

--- 23 unchanged lines hidden (view full) ---

32 ym2151e,
33 yh5151e
34};
35
36struct fsp3y_data {
37 struct pmbus_driver_info info;
38 int chip;
39 int page;
40
41 bool vout_linear_11;
40};
41
42#define to_fsp3y_data(x) container_of(x, struct fsp3y_data, info)
43
44static int page_log_to_page_real(int page_log, enum chips chip)
45{
46 switch (chip) {
47 case ym2151e:

--- 55 unchanged lines hidden (view full) ---

103
104static int fsp3y_read_byte_data(struct i2c_client *client, int page, int reg)
105{
106 const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
107 struct fsp3y_data *data = to_fsp3y_data(info);
108 int rv;
109
110 /*
42};
43
44#define to_fsp3y_data(x) container_of(x, struct fsp3y_data, info)
45
46static int page_log_to_page_real(int page_log, enum chips chip)
47{
48 switch (chip) {
49 case ym2151e:

--- 55 unchanged lines hidden (view full) ---

105
106static int fsp3y_read_byte_data(struct i2c_client *client, int page, int reg)
107{
108 const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
109 struct fsp3y_data *data = to_fsp3y_data(info);
110 int rv;
111
112 /*
111 * YH5151-E outputs vout in linear11. The conversion is done when
112 * reading. Here, we have to inject pmbus_core with the correct
113 * exponent (it is -6).
113 * Inject an exponent for non-compliant YH5151-E.
114 */
114 */
115 if (data->chip == yh5151e && reg == PMBUS_VOUT_MODE)
115 if (data->vout_linear_11 && reg == PMBUS_VOUT_MODE)
116 return 0x1A;
117
118 rv = set_page(client, page);
119 if (rv < 0)
120 return rv;
121
122 return i2c_smbus_read_byte_data(client, reg);
123}

--- 32 unchanged lines hidden (view full) ---

156 if (rv < 0)
157 return rv;
158
159 rv = i2c_smbus_read_word_data(client, reg);
160 if (rv < 0)
161 return rv;
162
163 /*
116 return 0x1A;
117
118 rv = set_page(client, page);
119 if (rv < 0)
120 return rv;
121
122 return i2c_smbus_read_byte_data(client, reg);
123}

--- 32 unchanged lines hidden (view full) ---

156 if (rv < 0)
157 return rv;
158
159 rv = i2c_smbus_read_word_data(client, reg);
160 if (rv < 0)
161 return rv;
162
163 /*
164 * YH-5151E is non-compliant and outputs output voltages in linear11
165 * instead of linear16.
164 * Handle YH-5151E non-compliant linear11 vout voltage.
166 */
165 */
167 if (data->chip == yh5151e && reg == PMBUS_READ_VOUT)
166 if (data->vout_linear_11 && reg == PMBUS_READ_VOUT)
168 rv = sign_extend32(rv, 10) & 0xffff;
169
170 return rv;
171}
172
173static struct pmbus_driver_info fsp3y_info[] = {
174 [ym2151e] = {
175 .pages = 2,

--- 75 unchanged lines hidden (view full) ---

251
252 rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE);
253 if (rv < 0)
254 return rv;
255 data->page = rv;
256
257 data->info = fsp3y_info[data->chip];
258
167 rv = sign_extend32(rv, 10) & 0xffff;
168
169 return rv;
170}
171
172static struct pmbus_driver_info fsp3y_info[] = {
173 [ym2151e] = {
174 .pages = 2,

--- 75 unchanged lines hidden (view full) ---

250
251 rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE);
252 if (rv < 0)
253 return rv;
254 data->page = rv;
255
256 data->info = fsp3y_info[data->chip];
257
258 /*
259 * YH-5151E sometimes reports vout in linear11 and sometimes in
260 * linear16. This depends on the exact individual piece of hardware. One
261 * YH-5151E can use linear16 and another might use linear11 instead.
262 *
263 * The format can be recognized by reading VOUT_MODE - if it doesn't
264 * report a valid exponent, then vout uses linear11. Otherwise, the
265 * device is compliant and uses linear16.
266 */
267 data->vout_linear_11 = false;
268 if (data->chip == yh5151e) {
269 rv = i2c_smbus_read_byte_data(client, PMBUS_VOUT_MODE);
270 if (rv < 0)
271 return rv;
272
273 if (rv == 0xFF)
274 data->vout_linear_11 = true;
275 }
276
259 return pmbus_do_probe(client, &data->info);
260}
261
262MODULE_DEVICE_TABLE(i2c, fsp3y_id);
263
264static struct i2c_driver fsp3y_driver = {
265 .driver = {
266 .name = "fsp3y",
267 },
268 .probe_new = fsp3y_probe,
269 .id_table = fsp3y_id
270};
271
272module_i2c_driver(fsp3y_driver);
273
274MODULE_AUTHOR("Václav Kubernát");
275MODULE_DESCRIPTION("PMBus driver for FSP/3Y-Power power supplies");
276MODULE_LICENSE("GPL");
277MODULE_IMPORT_NS(PMBUS);
277 return pmbus_do_probe(client, &data->info);
278}
279
280MODULE_DEVICE_TABLE(i2c, fsp3y_id);
281
282static struct i2c_driver fsp3y_driver = {
283 .driver = {
284 .name = "fsp3y",
285 },
286 .probe_new = fsp3y_probe,
287 .id_table = fsp3y_id
288};
289
290module_i2c_driver(fsp3y_driver);
291
292MODULE_AUTHOR("Václav Kubernát");
293MODULE_DESCRIPTION("PMBus driver for FSP/3Y-Power power supplies");
294MODULE_LICENSE("GPL");
295MODULE_IMPORT_NS(PMBUS);