cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

mcp3021.c (5056B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * mcp3021.c - driver for Microchip MCP3021 and MCP3221
      4 *
      5 * Copyright (C) 2008-2009, 2012 Freescale Semiconductor, Inc.
      6 * Author: Mingkai Hu <Mingkai.hu@freescale.com>
      7 * Reworked by Sven Schuchmann <schuchmann@schleissheimer.de>
      8 * DT support added by Clemens Gruber <clemens.gruber@pqgruber.com>
      9 *
     10 * This driver export the value of analog input voltage to sysfs, the
     11 * voltage unit is mV. Through the sysfs interface, lm-sensors tool
     12 * can also display the input voltage.
     13 */
     14
     15#include <linux/kernel.h>
     16#include <linux/module.h>
     17#include <linux/hwmon.h>
     18#include <linux/slab.h>
     19#include <linux/i2c.h>
     20#include <linux/err.h>
     21#include <linux/device.h>
     22#include <linux/of.h>
     23#include <linux/of_device.h>
     24
     25/* Vdd / reference voltage in millivolt */
     26#define MCP3021_VDD_REF_MAX	5500
     27#define MCP3021_VDD_REF_MIN	2700
     28#define MCP3021_VDD_REF_DEFAULT	3300
     29
     30/* output format */
     31#define MCP3021_SAR_SHIFT	2
     32#define MCP3021_SAR_MASK	0x3ff
     33#define MCP3021_OUTPUT_RES	10	/* 10-bit resolution */
     34
     35#define MCP3221_SAR_SHIFT	0
     36#define MCP3221_SAR_MASK	0xfff
     37#define MCP3221_OUTPUT_RES	12	/* 12-bit resolution */
     38
     39enum chips {
     40	mcp3021,
     41	mcp3221
     42};
     43
     44/*
     45 * Client data (each client gets its own)
     46 */
     47struct mcp3021_data {
     48	struct device *hwmon_dev;
     49	u32 vdd;        /* supply and reference voltage in millivolt */
     50	u16 sar_shift;
     51	u16 sar_mask;
     52	u8 output_res;
     53};
     54
     55static int mcp3021_read16(struct i2c_client *client)
     56{
     57	struct mcp3021_data *data = i2c_get_clientdata(client);
     58	int ret;
     59	u16 reg;
     60	__be16 buf;
     61
     62	ret = i2c_master_recv(client, (char *)&buf, 2);
     63	if (ret < 0)
     64		return ret;
     65	if (ret != 2)
     66		return -EIO;
     67
     68	/* The output code of the MCP3021 is transmitted with MSB first. */
     69	reg = be16_to_cpu(buf);
     70
     71	/*
     72	 * The ten-bit output code is composed of the lower 4-bit of the
     73	 * first byte and the upper 6-bit of the second byte.
     74	 */
     75	reg = (reg >> data->sar_shift) & data->sar_mask;
     76
     77	return reg;
     78}
     79
     80static inline u16 volts_from_reg(struct mcp3021_data *data, u16 val)
     81{
     82	return DIV_ROUND_CLOSEST(data->vdd * val, 1 << data->output_res);
     83}
     84
     85static ssize_t in0_input_show(struct device *dev,
     86			      struct device_attribute *attr, char *buf)
     87{
     88	struct i2c_client *client = to_i2c_client(dev);
     89	struct mcp3021_data *data = i2c_get_clientdata(client);
     90	int reg, in_input;
     91
     92	reg = mcp3021_read16(client);
     93	if (reg < 0)
     94		return reg;
     95
     96	in_input = volts_from_reg(data, reg);
     97
     98	return sprintf(buf, "%d\n", in_input);
     99}
    100
    101static DEVICE_ATTR_RO(in0_input);
    102
    103static const struct i2c_device_id mcp3021_id[];
    104
    105static int mcp3021_probe(struct i2c_client *client)
    106{
    107	int err;
    108	struct mcp3021_data *data = NULL;
    109	struct device_node *np = client->dev.of_node;
    110
    111	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
    112		return -ENODEV;
    113
    114	data = devm_kzalloc(&client->dev, sizeof(struct mcp3021_data),
    115			    GFP_KERNEL);
    116	if (!data)
    117		return -ENOMEM;
    118
    119	i2c_set_clientdata(client, data);
    120
    121	if (np) {
    122		if (!of_property_read_u32(np, "reference-voltage-microvolt",
    123					  &data->vdd))
    124			data->vdd /= 1000;
    125		else
    126			data->vdd = MCP3021_VDD_REF_DEFAULT;
    127	} else {
    128		u32 *pdata = dev_get_platdata(&client->dev);
    129
    130		if (pdata)
    131			data->vdd = *pdata;
    132		else
    133			data->vdd = MCP3021_VDD_REF_DEFAULT;
    134	}
    135
    136	switch (i2c_match_id(mcp3021_id, client)->driver_data) {
    137	case mcp3021:
    138		data->sar_shift = MCP3021_SAR_SHIFT;
    139		data->sar_mask = MCP3021_SAR_MASK;
    140		data->output_res = MCP3021_OUTPUT_RES;
    141		break;
    142
    143	case mcp3221:
    144		data->sar_shift = MCP3221_SAR_SHIFT;
    145		data->sar_mask = MCP3221_SAR_MASK;
    146		data->output_res = MCP3221_OUTPUT_RES;
    147		break;
    148	}
    149
    150	if (data->vdd > MCP3021_VDD_REF_MAX || data->vdd < MCP3021_VDD_REF_MIN)
    151		return -EINVAL;
    152
    153	err = sysfs_create_file(&client->dev.kobj, &dev_attr_in0_input.attr);
    154	if (err)
    155		return err;
    156
    157	data->hwmon_dev = hwmon_device_register(&client->dev);
    158	if (IS_ERR(data->hwmon_dev)) {
    159		err = PTR_ERR(data->hwmon_dev);
    160		goto exit_remove;
    161	}
    162
    163	return 0;
    164
    165exit_remove:
    166	sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr);
    167	return err;
    168}
    169
    170static int mcp3021_remove(struct i2c_client *client)
    171{
    172	struct mcp3021_data *data = i2c_get_clientdata(client);
    173
    174	hwmon_device_unregister(data->hwmon_dev);
    175	sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr);
    176
    177	return 0;
    178}
    179
    180static const struct i2c_device_id mcp3021_id[] = {
    181	{ "mcp3021", mcp3021 },
    182	{ "mcp3221", mcp3221 },
    183	{ }
    184};
    185MODULE_DEVICE_TABLE(i2c, mcp3021_id);
    186
    187#ifdef CONFIG_OF
    188static const struct of_device_id of_mcp3021_match[] = {
    189	{ .compatible = "microchip,mcp3021", .data = (void *)mcp3021 },
    190	{ .compatible = "microchip,mcp3221", .data = (void *)mcp3221 },
    191	{ }
    192};
    193MODULE_DEVICE_TABLE(of, of_mcp3021_match);
    194#endif
    195
    196static struct i2c_driver mcp3021_driver = {
    197	.driver = {
    198		.name = "mcp3021",
    199		.of_match_table = of_match_ptr(of_mcp3021_match),
    200	},
    201	.probe_new = mcp3021_probe,
    202	.remove = mcp3021_remove,
    203	.id_table = mcp3021_id,
    204};
    205
    206module_i2c_driver(mcp3021_driver);
    207
    208MODULE_AUTHOR("Mingkai Hu <Mingkai.hu@freescale.com>");
    209MODULE_DESCRIPTION("Microchip MCP3021/MCP3221 driver");
    210MODULE_LICENSE("GPL");