linux/drivers/hwmon/pmbus/pxe1610.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Hardware monitoring driver for Infineon PXE1610
   4 *
   5 * Copyright (c) 2019 Facebook Inc
   6 *
   7 */
   8
   9#include <linux/err.h>
  10#include <linux/i2c.h>
  11#include <linux/init.h>
  12#include <linux/kernel.h>
  13#include <linux/module.h>
  14#include "pmbus.h"
  15
  16#define PXE1610_NUM_PAGES 3
  17
  18/* Identify chip parameters. */
  19static int pxe1610_identify(struct i2c_client *client,
  20                             struct pmbus_driver_info *info)
  21{
  22        int i;
  23
  24        for (i = 0; i < PXE1610_NUM_PAGES; i++) {
  25                if (pmbus_check_byte_register(client, i, PMBUS_VOUT_MODE)) {
  26                        u8 vout_mode;
  27                        int ret;
  28
  29                        /* Read the register with VOUT scaling value.*/
  30                        ret = pmbus_read_byte_data(client, i, PMBUS_VOUT_MODE);
  31                        if (ret < 0)
  32                                return ret;
  33
  34                        vout_mode = ret & GENMASK(4, 0);
  35
  36                        switch (vout_mode) {
  37                        case 1:
  38                                info->vrm_version[i] = vr12;
  39                                break;
  40                        case 2:
  41                                info->vrm_version[i] = vr13;
  42                                break;
  43                        default:
  44                                return -ENODEV;
  45                        }
  46                }
  47        }
  48
  49        return 0;
  50}
  51
  52static struct pmbus_driver_info pxe1610_info = {
  53        .pages = PXE1610_NUM_PAGES,
  54        .format[PSC_VOLTAGE_IN] = linear,
  55        .format[PSC_VOLTAGE_OUT] = vid,
  56        .format[PSC_CURRENT_IN] = linear,
  57        .format[PSC_CURRENT_OUT] = linear,
  58        .format[PSC_TEMPERATURE] = linear,
  59        .format[PSC_POWER] = linear,
  60        .func[0] = PMBUS_HAVE_VIN
  61                | PMBUS_HAVE_VOUT | PMBUS_HAVE_IIN
  62                | PMBUS_HAVE_IOUT | PMBUS_HAVE_PIN
  63                | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP
  64                | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT
  65                | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
  66        .func[1] = PMBUS_HAVE_VIN
  67                | PMBUS_HAVE_VOUT | PMBUS_HAVE_IIN
  68                | PMBUS_HAVE_IOUT | PMBUS_HAVE_PIN
  69                | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP
  70                | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT
  71                | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
  72        .func[2] = PMBUS_HAVE_VIN
  73                | PMBUS_HAVE_VOUT | PMBUS_HAVE_IIN
  74                | PMBUS_HAVE_IOUT | PMBUS_HAVE_PIN
  75                | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP
  76                | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT
  77                | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
  78        .identify = pxe1610_identify,
  79};
  80
  81static int pxe1610_probe(struct i2c_client *client)
  82{
  83        struct pmbus_driver_info *info;
  84        u8 buf[I2C_SMBUS_BLOCK_MAX];
  85        int ret;
  86
  87        if (!i2c_check_functionality(
  88                        client->adapter,
  89                        I2C_FUNC_SMBUS_READ_BYTE_DATA
  90                        | I2C_FUNC_SMBUS_READ_WORD_DATA
  91                        | I2C_FUNC_SMBUS_READ_BLOCK_DATA))
  92                return -ENODEV;
  93
  94        /*
  95         * By default this device doesn't boot to page 0, so set page 0
  96         * to access all pmbus registers.
  97         */
  98        i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
  99
 100        /* Read Manufacturer id */
 101        ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf);
 102        if (ret < 0) {
 103                dev_err(&client->dev, "Failed to read PMBUS_MFR_ID\n");
 104                return ret;
 105        }
 106        if (ret != 2 || strncmp(buf, "XP", 2)) {
 107                dev_err(&client->dev, "MFR_ID unrecognized\n");
 108                return -ENODEV;
 109        }
 110
 111        info = devm_kmemdup(&client->dev, &pxe1610_info,
 112                            sizeof(struct pmbus_driver_info),
 113                            GFP_KERNEL);
 114        if (!info)
 115                return -ENOMEM;
 116
 117        return pmbus_do_probe(client, info);
 118}
 119
 120static const struct i2c_device_id pxe1610_id[] = {
 121        {"pxe1610", 0},
 122        {"pxe1110", 0},
 123        {"pxm1310", 0},
 124        {}
 125};
 126
 127MODULE_DEVICE_TABLE(i2c, pxe1610_id);
 128
 129static struct i2c_driver pxe1610_driver = {
 130        .driver = {
 131                        .name = "pxe1610",
 132                        },
 133        .probe_new = pxe1610_probe,
 134        .remove = pmbus_do_remove,
 135        .id_table = pxe1610_id,
 136};
 137
 138module_i2c_driver(pxe1610_driver);
 139
 140MODULE_AUTHOR("Vijay Khemka <vijaykhemka@fb.com>");
 141MODULE_DESCRIPTION("PMBus driver for Infineon PXE1610, PXE1110 and PXM1310");
 142MODULE_LICENSE("GPL");
 143