root/drivers/hwmon/max127.c
// SPDX-License-Identifier: GPL-2.0+
/*
 * Hardware monitoring driver for MAX127.
 *
 * Copyright (c) 2020 Facebook Inc.
 */

#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>

/*
 * MAX127 Control Byte. Refer to MAX127 datasheet, Table 1 "Control-Byte
 * Format" for details.
 */
#define MAX127_CTRL_START       BIT(7)
#define MAX127_CTRL_SEL_SHIFT   4
#define MAX127_CTRL_RNG         BIT(3)
#define MAX127_CTRL_BIP         BIT(2)
#define MAX127_CTRL_PD1         BIT(1)
#define MAX127_CTRL_PD0         BIT(0)

#define MAX127_NUM_CHANNELS     8
#define MAX127_SET_CHANNEL(ch)  (((ch) & 7) << MAX127_CTRL_SEL_SHIFT)

/*
 * MAX127 channel input ranges. Refer to MAX127 datasheet, Table 3 "Range
 * and Polarity Selection" for details.
 */
#define MAX127_FULL_RANGE       10000   /* 10V */
#define MAX127_HALF_RANGE       5000    /* 5V */

/*
 * MAX127 returns 2 bytes at read:
 *   - the first byte contains data[11:4].
 *   - the second byte contains data[3:0] (MSB) and 4 dummy 0s (LSB).
 * Refer to MAX127 datasheet, "Read a Conversion (Read Cycle)" section
 * for details.
 */
#define MAX127_DATA_LEN         2
#define MAX127_DATA_SHIFT       4

#define MAX127_SIGN_BIT         BIT(11)

struct max127_data {
        struct i2c_client *client;
        u8 ctrl_byte[MAX127_NUM_CHANNELS];
};

static int max127_select_channel(struct i2c_client *client, u8 ctrl_byte)
{
        int status;
        struct i2c_msg msg = {
                .addr = client->addr,
                .flags = 0,
                .len = sizeof(ctrl_byte),
                .buf = &ctrl_byte,
        };

        status = i2c_transfer(client->adapter, &msg, 1);
        if (status < 0)
                return status;
        if (status != 1)
                return -EIO;

        return 0;
}

static int max127_read_channel(struct i2c_client *client, long *val)
{
        int status;
        u8 i2c_data[MAX127_DATA_LEN];
        struct i2c_msg msg = {
                .addr = client->addr,
                .flags = I2C_M_RD,
                .len = sizeof(i2c_data),
                .buf = i2c_data,
        };

        status = i2c_transfer(client->adapter, &msg, 1);
        if (status < 0)
                return status;
        if (status != 1)
                return -EIO;

        *val = (i2c_data[1] >> MAX127_DATA_SHIFT) |
                ((u16)i2c_data[0] << MAX127_DATA_SHIFT);
        return 0;
}

static long max127_process_raw(u8 ctrl_byte, long raw)
{
        long scale, weight;

        /*
         * MAX127's data coding is binary in unipolar mode with 1 LSB =
         * (Full-Scale/4096) and two’s complement binary in bipolar mode
         * with 1 LSB = [(2 x |FS|)/4096].
         * Refer to MAX127 datasheet, "Transfer Function" section for
         * details.
         */
        scale = (ctrl_byte & MAX127_CTRL_RNG) ? MAX127_FULL_RANGE :
                                                MAX127_HALF_RANGE;
        if (ctrl_byte & MAX127_CTRL_BIP) {
                weight = (raw & MAX127_SIGN_BIT);
                raw &= ~MAX127_SIGN_BIT;
                raw -= weight;
                raw *= 2;
        }

        return raw * scale / 4096;
}

static int max127_read_input(struct max127_data *data, int channel, long *val)
{
        long raw;
        int status;
        struct i2c_client *client = data->client;
        u8 ctrl_byte = data->ctrl_byte[channel];

        status = max127_select_channel(client, ctrl_byte);
        if (status)
                return status;

        status = max127_read_channel(client, &raw);
        if (status)
                return status;

        *val = max127_process_raw(ctrl_byte, raw);
        return 0;
}

static int max127_read_min(struct max127_data *data, int channel, long *val)
{
        u8 rng_bip = (data->ctrl_byte[channel] >> 2) & 3;
        static const int min_input_map[4] = {
                0,                      /* RNG=0, BIP=0 */
                -MAX127_HALF_RANGE,     /* RNG=0, BIP=1 */
                0,                      /* RNG=1, BIP=0 */
                -MAX127_FULL_RANGE,     /* RNG=1, BIP=1 */
        };

        *val = min_input_map[rng_bip];
        return 0;
}

static int max127_read_max(struct max127_data *data, int channel, long *val)
{
        u8 rng_bip = (data->ctrl_byte[channel] >> 2) & 3;
        static const int max_input_map[4] = {
                MAX127_HALF_RANGE,      /* RNG=0, BIP=0 */
                MAX127_HALF_RANGE,      /* RNG=0, BIP=1 */
                MAX127_FULL_RANGE,      /* RNG=1, BIP=0 */
                MAX127_FULL_RANGE,      /* RNG=1, BIP=1 */
        };

        *val = max_input_map[rng_bip];
        return 0;
}

static int max127_write_min(struct max127_data *data, int channel, long val)
{
        u8 ctrl;

        ctrl = data->ctrl_byte[channel];
        if (val <= -MAX127_FULL_RANGE) {
                ctrl |= (MAX127_CTRL_RNG | MAX127_CTRL_BIP);
        } else if (val < 0) {
                ctrl |= MAX127_CTRL_BIP;
                ctrl &= ~MAX127_CTRL_RNG;
        } else {
                ctrl &= ~MAX127_CTRL_BIP;
        }
        data->ctrl_byte[channel] = ctrl;
        return 0;
}

static int max127_write_max(struct max127_data *data, int channel, long val)
{
        if (val >= MAX127_FULL_RANGE)
                data->ctrl_byte[channel] |= MAX127_CTRL_RNG;
        else
                data->ctrl_byte[channel] &= ~MAX127_CTRL_RNG;
        return 0;
}

static umode_t max127_is_visible(const void *_data,
                                 enum hwmon_sensor_types type,
                                 u32 attr, int channel)
{
        if (type == hwmon_in) {
                switch (attr) {
                case hwmon_in_input:
                        return 0444;

                case hwmon_in_min:
                case hwmon_in_max:
                        return 0644;

                default:
                        break;
                }
        }

        return 0;
}

static int max127_read(struct device *dev, enum hwmon_sensor_types type,
                        u32 attr, int channel, long *val)
{
        int status;
        struct max127_data *data = dev_get_drvdata(dev);

        if (type != hwmon_in)
                return -EOPNOTSUPP;

        switch (attr) {
        case hwmon_in_input:
                status = max127_read_input(data, channel, val);
                break;

        case hwmon_in_min:
                status = max127_read_min(data, channel, val);
                break;

        case hwmon_in_max:
                status = max127_read_max(data, channel, val);
                break;

        default:
                status = -EOPNOTSUPP;
                break;
        }

        return status;
}

static int max127_write(struct device *dev, enum hwmon_sensor_types type,
                        u32 attr, int channel, long val)
{
        int status;
        struct max127_data *data = dev_get_drvdata(dev);

        if (type != hwmon_in)
                return -EOPNOTSUPP;

        switch (attr) {
        case hwmon_in_min:
                status = max127_write_min(data, channel, val);
                break;

        case hwmon_in_max:
                status = max127_write_max(data, channel, val);
                break;

        default:
                status = -EOPNOTSUPP;
                break;
        }

        return status;
}

static const struct hwmon_ops max127_hwmon_ops = {
        .is_visible = max127_is_visible,
        .read = max127_read,
        .write = max127_write,
};

static const struct hwmon_channel_info * const max127_info[] = {
        HWMON_CHANNEL_INFO(in,
                           HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX,
                           HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX,
                           HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX,
                           HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX,
                           HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX,
                           HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX,
                           HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX,
                           HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX),
        NULL,
};

static const struct hwmon_chip_info max127_chip_info = {
        .ops = &max127_hwmon_ops,
        .info = max127_info,
};

static int max127_probe(struct i2c_client *client)
{
        int i;
        struct device *hwmon_dev;
        struct max127_data *data;
        struct device *dev = &client->dev;

        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;

        data->client = client;
        for (i = 0; i < ARRAY_SIZE(data->ctrl_byte); i++)
                data->ctrl_byte[i] = (MAX127_CTRL_START |
                                      MAX127_SET_CHANNEL(i));

        hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
                                                         data,
                                                         &max127_chip_info,
                                                         NULL);

        return PTR_ERR_OR_ZERO(hwmon_dev);
}

static const struct i2c_device_id max127_id[] = {
        { "max127" },
        { }
};
MODULE_DEVICE_TABLE(i2c, max127_id);

static struct i2c_driver max127_driver = {
        .driver = {
                .name   = "max127",
        },
        .probe          = max127_probe,
        .id_table       = max127_id,
};

module_i2c_driver(max127_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mike Choi <mikechoi@fb.com>");
MODULE_AUTHOR("Tao Ren <rentao.bupt@gmail.com>");
MODULE_DESCRIPTION("MAX127 Hardware Monitoring driver");