Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Hardware monitoring driver for Infineon PXE1610
0004  *
0005  * Copyright (c) 2019 Facebook Inc
0006  *
0007  */
0008 
0009 #include <linux/err.h>
0010 #include <linux/i2c.h>
0011 #include <linux/init.h>
0012 #include <linux/kernel.h>
0013 #include <linux/module.h>
0014 #include "pmbus.h"
0015 
0016 #define PXE1610_NUM_PAGES 3
0017 
0018 /* Identify chip parameters. */
0019 static int pxe1610_identify(struct i2c_client *client,
0020                  struct pmbus_driver_info *info)
0021 {
0022     int i;
0023 
0024     for (i = 0; i < PXE1610_NUM_PAGES; i++) {
0025         if (pmbus_check_byte_register(client, i, PMBUS_VOUT_MODE)) {
0026             u8 vout_mode;
0027             int ret;
0028 
0029             /* Read the register with VOUT scaling value.*/
0030             ret = pmbus_read_byte_data(client, i, PMBUS_VOUT_MODE);
0031             if (ret < 0)
0032                 return ret;
0033 
0034             vout_mode = ret & GENMASK(4, 0);
0035 
0036             switch (vout_mode) {
0037             case 1:
0038                 info->vrm_version[i] = vr12;
0039                 break;
0040             case 2:
0041                 info->vrm_version[i] = vr13;
0042                 break;
0043             default:
0044                 /*
0045                  * If prior pages are available limit operation
0046                  * to them
0047                  */
0048                 if (i != 0) {
0049                     info->pages = i;
0050                     return 0;
0051                 }
0052 
0053                 return -ENODEV;
0054             }
0055         }
0056     }
0057 
0058     return 0;
0059 }
0060 
0061 static struct pmbus_driver_info pxe1610_info = {
0062     .pages = PXE1610_NUM_PAGES,
0063     .format[PSC_VOLTAGE_IN] = linear,
0064     .format[PSC_VOLTAGE_OUT] = vid,
0065     .format[PSC_CURRENT_IN] = linear,
0066     .format[PSC_CURRENT_OUT] = linear,
0067     .format[PSC_TEMPERATURE] = linear,
0068     .format[PSC_POWER] = linear,
0069     .func[0] = PMBUS_HAVE_VIN
0070         | PMBUS_HAVE_VOUT | PMBUS_HAVE_IIN
0071         | PMBUS_HAVE_IOUT | PMBUS_HAVE_PIN
0072         | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP
0073         | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT
0074         | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
0075     .func[1] = PMBUS_HAVE_VIN
0076         | PMBUS_HAVE_VOUT | PMBUS_HAVE_IIN
0077         | PMBUS_HAVE_IOUT | PMBUS_HAVE_PIN
0078         | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP
0079         | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT
0080         | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
0081     .func[2] = PMBUS_HAVE_VIN
0082         | PMBUS_HAVE_VOUT | PMBUS_HAVE_IIN
0083         | PMBUS_HAVE_IOUT | PMBUS_HAVE_PIN
0084         | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP
0085         | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT
0086         | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
0087     .identify = pxe1610_identify,
0088 };
0089 
0090 static int pxe1610_probe(struct i2c_client *client)
0091 {
0092     struct pmbus_driver_info *info;
0093     u8 buf[I2C_SMBUS_BLOCK_MAX];
0094     int ret;
0095 
0096     if (!i2c_check_functionality(
0097             client->adapter,
0098             I2C_FUNC_SMBUS_READ_BYTE_DATA
0099             | I2C_FUNC_SMBUS_READ_WORD_DATA
0100             | I2C_FUNC_SMBUS_READ_BLOCK_DATA))
0101         return -ENODEV;
0102 
0103     /*
0104      * By default this device doesn't boot to page 0, so set page 0
0105      * to access all pmbus registers.
0106      */
0107     i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
0108 
0109     /* Read Manufacturer id */
0110     ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf);
0111     if (ret < 0) {
0112         dev_err(&client->dev, "Failed to read PMBUS_MFR_ID\n");
0113         return ret;
0114     }
0115     if (ret != 2 || strncmp(buf, "XP", 2)) {
0116         dev_err(&client->dev, "MFR_ID unrecognized\n");
0117         return -ENODEV;
0118     }
0119 
0120     info = devm_kmemdup(&client->dev, &pxe1610_info,
0121                 sizeof(struct pmbus_driver_info),
0122                 GFP_KERNEL);
0123     if (!info)
0124         return -ENOMEM;
0125 
0126     return pmbus_do_probe(client, info);
0127 }
0128 
0129 static const struct i2c_device_id pxe1610_id[] = {
0130     {"pxe1610", 0},
0131     {"pxe1110", 0},
0132     {"pxm1310", 0},
0133     {}
0134 };
0135 
0136 MODULE_DEVICE_TABLE(i2c, pxe1610_id);
0137 
0138 static struct i2c_driver pxe1610_driver = {
0139     .driver = {
0140             .name = "pxe1610",
0141             },
0142     .probe_new = pxe1610_probe,
0143     .id_table = pxe1610_id,
0144 };
0145 
0146 module_i2c_driver(pxe1610_driver);
0147 
0148 MODULE_AUTHOR("Vijay Khemka <vijaykhemka@fb.com>");
0149 MODULE_DESCRIPTION("PMBus driver for Infineon PXE1610, PXE1110 and PXM1310");
0150 MODULE_LICENSE("GPL");
0151 MODULE_IMPORT_NS(PMBUS);