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