Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Hardware monitoring driver for PIM4006, PIM4328 and PIM4820
0004  *
0005  * Copyright (c) 2021 Flextronics International Sweden AB
0006  */
0007 
0008 #include <linux/err.h>
0009 #include <linux/i2c.h>
0010 #include <linux/init.h>
0011 #include <linux/kernel.h>
0012 #include <linux/module.h>
0013 #include <linux/pmbus.h>
0014 #include <linux/slab.h>
0015 #include "pmbus.h"
0016 
0017 enum chips { pim4006, pim4328, pim4820 };
0018 
0019 struct pim4328_data {
0020     enum chips id;
0021     struct pmbus_driver_info info;
0022 };
0023 
0024 #define to_pim4328_data(x)  container_of(x, struct pim4328_data, info)
0025 
0026 /* PIM4006 and PIM4328 */
0027 #define PIM4328_MFR_READ_VINA       0xd3
0028 #define PIM4328_MFR_READ_VINB       0xd4
0029 
0030 /* PIM4006 */
0031 #define PIM4328_MFR_READ_IINA       0xd6
0032 #define PIM4328_MFR_READ_IINB       0xd7
0033 #define PIM4328_MFR_FET_CHECKSTATUS 0xd9
0034 
0035 /* PIM4328 */
0036 #define PIM4328_MFR_STATUS_BITS     0xd5
0037 
0038 /* PIM4820 */
0039 #define PIM4328_MFR_READ_STATUS     0xd0
0040 
0041 static const struct i2c_device_id pim4328_id[] = {
0042     {"bmr455", pim4328},
0043     {"pim4006", pim4006},
0044     {"pim4106", pim4006},
0045     {"pim4206", pim4006},
0046     {"pim4306", pim4006},
0047     {"pim4328", pim4328},
0048     {"pim4406", pim4006},
0049     {"pim4820", pim4820},
0050     {}
0051 };
0052 MODULE_DEVICE_TABLE(i2c, pim4328_id);
0053 
0054 static int pim4328_read_word_data(struct i2c_client *client, int page,
0055                   int phase, int reg)
0056 {
0057     int ret;
0058 
0059     if (page > 0)
0060         return -ENXIO;
0061 
0062     if (phase == 0xff)
0063         return -ENODATA;
0064 
0065     switch (reg) {
0066     case PMBUS_READ_VIN:
0067         ret = pmbus_read_word_data(client, page, phase,
0068                        phase == 0 ? PIM4328_MFR_READ_VINA
0069                               : PIM4328_MFR_READ_VINB);
0070         break;
0071     case PMBUS_READ_IIN:
0072         ret = pmbus_read_word_data(client, page, phase,
0073                        phase == 0 ? PIM4328_MFR_READ_IINA
0074                               : PIM4328_MFR_READ_IINB);
0075         break;
0076     default:
0077         ret = -ENODATA;
0078     }
0079 
0080     return ret;
0081 }
0082 
0083 static int pim4328_read_byte_data(struct i2c_client *client, int page, int reg)
0084 {
0085     const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
0086     struct pim4328_data *data = to_pim4328_data(info);
0087     int ret, status;
0088 
0089     if (page > 0)
0090         return -ENXIO;
0091 
0092     switch (reg) {
0093     case PMBUS_STATUS_BYTE:
0094         ret = pmbus_read_byte_data(client, page, PMBUS_STATUS_BYTE);
0095         if (ret < 0)
0096             return ret;
0097         if (data->id == pim4006) {
0098             status = pmbus_read_word_data(client, page, 0xff,
0099                               PIM4328_MFR_FET_CHECKSTATUS);
0100             if (status < 0)
0101                 return status;
0102             if (status & 0x0630) /* Input UV */
0103                 ret |= PB_STATUS_VIN_UV;
0104         } else if (data->id == pim4328) {
0105             status = pmbus_read_byte_data(client, page,
0106                               PIM4328_MFR_STATUS_BITS);
0107             if (status < 0)
0108                 return status;
0109             if (status & 0x04) /* Input UV */
0110                 ret |= PB_STATUS_VIN_UV;
0111             if (status & 0x40) /* Output UV */
0112                 ret |= PB_STATUS_NONE_ABOVE;
0113         } else if (data->id == pim4820) {
0114             status = pmbus_read_byte_data(client, page,
0115                               PIM4328_MFR_READ_STATUS);
0116             if (status < 0)
0117                 return status;
0118             if (status & 0x05) /* Input OV or OC */
0119                 ret |= PB_STATUS_NONE_ABOVE;
0120             if (status & 0x1a) /* Input UV */
0121                 ret |= PB_STATUS_VIN_UV;
0122             if (status & 0x40) /* OT */
0123                 ret |= PB_STATUS_TEMPERATURE;
0124         }
0125         break;
0126     default:
0127         ret = -ENODATA;
0128     }
0129 
0130     return ret;
0131 }
0132 
0133 static int pim4328_probe(struct i2c_client *client)
0134 {
0135     int status;
0136     u8 device_id[I2C_SMBUS_BLOCK_MAX + 1];
0137     const struct i2c_device_id *mid;
0138     struct pim4328_data *data;
0139     struct pmbus_driver_info *info;
0140     struct pmbus_platform_data *pdata;
0141     struct device *dev = &client->dev;
0142 
0143     if (!i2c_check_functionality(client->adapter,
0144                      I2C_FUNC_SMBUS_READ_BYTE_DATA
0145                      | I2C_FUNC_SMBUS_BLOCK_DATA))
0146         return -ENODEV;
0147 
0148     data = devm_kzalloc(&client->dev, sizeof(struct pim4328_data),
0149                 GFP_KERNEL);
0150     if (!data)
0151         return -ENOMEM;
0152 
0153     status = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, device_id);
0154     if (status < 0) {
0155         dev_err(&client->dev, "Failed to read Manufacturer Model\n");
0156         return status;
0157     }
0158     for (mid = pim4328_id; mid->name[0]; mid++) {
0159         if (!strncasecmp(mid->name, device_id, strlen(mid->name)))
0160             break;
0161     }
0162     if (!mid->name[0]) {
0163         dev_err(&client->dev, "Unsupported device\n");
0164         return -ENODEV;
0165     }
0166 
0167     if (strcmp(client->name, mid->name))
0168         dev_notice(&client->dev,
0169                "Device mismatch: Configured %s, detected %s\n",
0170                client->name, mid->name);
0171 
0172     data->id = mid->driver_data;
0173     info = &data->info;
0174     info->pages = 1;
0175     info->read_byte_data = pim4328_read_byte_data;
0176     info->read_word_data = pim4328_read_word_data;
0177 
0178     pdata = devm_kzalloc(dev, sizeof(struct pmbus_platform_data),
0179                  GFP_KERNEL);
0180     if (!pdata)
0181         return -ENOMEM;
0182     dev->platform_data = pdata;
0183     pdata->flags = PMBUS_NO_CAPABILITY | PMBUS_NO_WRITE_PROTECT;
0184 
0185     switch (data->id) {
0186     case pim4006:
0187         info->phases[0] = 2;
0188         info->func[0] = PMBUS_PHASE_VIRTUAL | PMBUS_HAVE_VIN
0189             | PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT;
0190         info->pfunc[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN;
0191         info->pfunc[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN;
0192         break;
0193     case pim4328:
0194         info->phases[0] = 2;
0195         info->func[0] = PMBUS_PHASE_VIRTUAL
0196             | PMBUS_HAVE_VCAP | PMBUS_HAVE_VIN
0197             | PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT;
0198         info->pfunc[0] = PMBUS_HAVE_VIN;
0199         info->pfunc[1] = PMBUS_HAVE_VIN;
0200         info->format[PSC_VOLTAGE_IN] = direct;
0201         info->format[PSC_TEMPERATURE] = direct;
0202         info->format[PSC_CURRENT_OUT] = direct;
0203         pdata->flags |= PMBUS_USE_COEFFICIENTS_CMD;
0204         break;
0205     case pim4820:
0206         info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_TEMP
0207             | PMBUS_HAVE_IIN;
0208         info->format[PSC_VOLTAGE_IN] = direct;
0209         info->format[PSC_TEMPERATURE] = direct;
0210         info->format[PSC_CURRENT_IN] = direct;
0211         pdata->flags |= PMBUS_USE_COEFFICIENTS_CMD;
0212         break;
0213     default:
0214         return -ENODEV;
0215     }
0216 
0217     return pmbus_do_probe(client, info);
0218 }
0219 
0220 static struct i2c_driver pim4328_driver = {
0221     .driver = {
0222            .name = "pim4328",
0223            },
0224     .probe_new = pim4328_probe,
0225     .id_table = pim4328_id,
0226 };
0227 
0228 module_i2c_driver(pim4328_driver);
0229 
0230 MODULE_AUTHOR("Erik Rosen <erik.rosen@metormote.com>");
0231 MODULE_DESCRIPTION("PMBus driver for PIM4006, PIM4328, PIM4820 power interface modules");
0232 MODULE_LICENSE("GPL");
0233 MODULE_IMPORT_NS(PMBUS);