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