1*626bb2f3STao Ren // SPDX-License-Identifier: GPL-2.0+ 2*626bb2f3STao Ren /* 3*626bb2f3STao Ren * Hardware monitoring driver for BEL PFE family power supplies. 4*626bb2f3STao Ren * 5*626bb2f3STao Ren * Copyright (c) 2019 Facebook Inc. 6*626bb2f3STao Ren */ 7*626bb2f3STao Ren 8*626bb2f3STao Ren #include <linux/err.h> 9*626bb2f3STao Ren #include <linux/i2c.h> 10*626bb2f3STao Ren #include <linux/init.h> 11*626bb2f3STao Ren #include <linux/kernel.h> 12*626bb2f3STao Ren #include <linux/module.h> 13*626bb2f3STao Ren #include <linux/pmbus.h> 14*626bb2f3STao Ren 15*626bb2f3STao Ren #include "pmbus.h" 16*626bb2f3STao Ren 17*626bb2f3STao Ren enum chips {pfe1100, pfe3000}; 18*626bb2f3STao Ren 19*626bb2f3STao Ren /* 20*626bb2f3STao Ren * Disable status check for pfe3000 devices, because some devices report 21*626bb2f3STao Ren * communication error (invalid command) for VOUT_MODE command (0x20) 22*626bb2f3STao Ren * although correct VOUT_MODE (0x16) is returned: it leads to incorrect 23*626bb2f3STao Ren * exponent in linear mode. 24*626bb2f3STao Ren */ 25*626bb2f3STao Ren static struct pmbus_platform_data pfe3000_plat_data = { 26*626bb2f3STao Ren .flags = PMBUS_SKIP_STATUS_CHECK, 27*626bb2f3STao Ren }; 28*626bb2f3STao Ren 29*626bb2f3STao Ren static struct pmbus_driver_info pfe_driver_info[] = { 30*626bb2f3STao Ren [pfe1100] = { 31*626bb2f3STao Ren .pages = 1, 32*626bb2f3STao Ren .format[PSC_VOLTAGE_IN] = linear, 33*626bb2f3STao Ren .format[PSC_VOLTAGE_OUT] = linear, 34*626bb2f3STao Ren .format[PSC_CURRENT_IN] = linear, 35*626bb2f3STao Ren .format[PSC_CURRENT_OUT] = linear, 36*626bb2f3STao Ren .format[PSC_POWER] = linear, 37*626bb2f3STao Ren .format[PSC_TEMPERATURE] = linear, 38*626bb2f3STao Ren .format[PSC_FAN] = linear, 39*626bb2f3STao Ren 40*626bb2f3STao Ren .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | 41*626bb2f3STao Ren PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | 42*626bb2f3STao Ren PMBUS_HAVE_POUT | 43*626bb2f3STao Ren PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | 44*626bb2f3STao Ren PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT | 45*626bb2f3STao Ren PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | 46*626bb2f3STao Ren PMBUS_HAVE_STATUS_TEMP | 47*626bb2f3STao Ren PMBUS_HAVE_FAN12, 48*626bb2f3STao Ren }, 49*626bb2f3STao Ren 50*626bb2f3STao Ren [pfe3000] = { 51*626bb2f3STao Ren .pages = 7, 52*626bb2f3STao Ren .format[PSC_VOLTAGE_IN] = linear, 53*626bb2f3STao Ren .format[PSC_VOLTAGE_OUT] = linear, 54*626bb2f3STao Ren .format[PSC_CURRENT_IN] = linear, 55*626bb2f3STao Ren .format[PSC_CURRENT_OUT] = linear, 56*626bb2f3STao Ren .format[PSC_POWER] = linear, 57*626bb2f3STao Ren .format[PSC_TEMPERATURE] = linear, 58*626bb2f3STao Ren .format[PSC_FAN] = linear, 59*626bb2f3STao Ren 60*626bb2f3STao Ren /* Page 0: V1. */ 61*626bb2f3STao Ren .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | 62*626bb2f3STao Ren PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | 63*626bb2f3STao Ren PMBUS_HAVE_POUT | PMBUS_HAVE_FAN12 | 64*626bb2f3STao Ren PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | 65*626bb2f3STao Ren PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT | 66*626bb2f3STao Ren PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | 67*626bb2f3STao Ren PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_TEMP | 68*626bb2f3STao Ren PMBUS_HAVE_VCAP, 69*626bb2f3STao Ren 70*626bb2f3STao Ren /* Page 1: Vsb. */ 71*626bb2f3STao Ren .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | 72*626bb2f3STao Ren PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | 73*626bb2f3STao Ren PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT | 74*626bb2f3STao Ren PMBUS_HAVE_POUT, 75*626bb2f3STao Ren 76*626bb2f3STao Ren /* 77*626bb2f3STao Ren * Page 2: V1 Ishare. 78*626bb2f3STao Ren * Page 3: Reserved. 79*626bb2f3STao Ren * Page 4: V1 Cathode. 80*626bb2f3STao Ren * Page 5: Vsb Cathode. 81*626bb2f3STao Ren * Page 6: V1 Sense. 82*626bb2f3STao Ren */ 83*626bb2f3STao Ren .func[2] = PMBUS_HAVE_VOUT, 84*626bb2f3STao Ren .func[4] = PMBUS_HAVE_VOUT, 85*626bb2f3STao Ren .func[5] = PMBUS_HAVE_VOUT, 86*626bb2f3STao Ren .func[6] = PMBUS_HAVE_VOUT, 87*626bb2f3STao Ren }, 88*626bb2f3STao Ren }; 89*626bb2f3STao Ren 90*626bb2f3STao Ren static int pfe_pmbus_probe(struct i2c_client *client, 91*626bb2f3STao Ren const struct i2c_device_id *id) 92*626bb2f3STao Ren { 93*626bb2f3STao Ren int model; 94*626bb2f3STao Ren 95*626bb2f3STao Ren model = (int)id->driver_data; 96*626bb2f3STao Ren 97*626bb2f3STao Ren /* 98*626bb2f3STao Ren * PFE3000-12-069RA devices may not stay in page 0 during device 99*626bb2f3STao Ren * probe which leads to probe failure (read status word failed). 100*626bb2f3STao Ren * So let's set the device to page 0 at the beginning. 101*626bb2f3STao Ren */ 102*626bb2f3STao Ren if (model == pfe3000) { 103*626bb2f3STao Ren client->dev.platform_data = &pfe3000_plat_data; 104*626bb2f3STao Ren i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); 105*626bb2f3STao Ren } 106*626bb2f3STao Ren 107*626bb2f3STao Ren return pmbus_do_probe(client, id, &pfe_driver_info[model]); 108*626bb2f3STao Ren } 109*626bb2f3STao Ren 110*626bb2f3STao Ren static const struct i2c_device_id pfe_device_id[] = { 111*626bb2f3STao Ren {"pfe1100", pfe1100}, 112*626bb2f3STao Ren {"pfe3000", pfe3000}, 113*626bb2f3STao Ren {} 114*626bb2f3STao Ren }; 115*626bb2f3STao Ren 116*626bb2f3STao Ren MODULE_DEVICE_TABLE(i2c, pfe_device_id); 117*626bb2f3STao Ren 118*626bb2f3STao Ren static struct i2c_driver pfe_pmbus_driver = { 119*626bb2f3STao Ren .driver = { 120*626bb2f3STao Ren .name = "bel-pfe", 121*626bb2f3STao Ren }, 122*626bb2f3STao Ren .probe = pfe_pmbus_probe, 123*626bb2f3STao Ren .remove = pmbus_do_remove, 124*626bb2f3STao Ren .id_table = pfe_device_id, 125*626bb2f3STao Ren }; 126*626bb2f3STao Ren 127*626bb2f3STao Ren module_i2c_driver(pfe_pmbus_driver); 128*626bb2f3STao Ren 129*626bb2f3STao Ren MODULE_AUTHOR("Tao Ren <rentao.bupt@gmail.com>"); 130*626bb2f3STao Ren MODULE_DESCRIPTION("PMBus driver for BEL PFE Family Power Supplies"); 131*626bb2f3STao Ren MODULE_LICENSE("GPL"); 132