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

dpot-dac.c (6240B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * IIO DAC emulation driver using a digital potentiometer
      4 *
      5 * Copyright (C) 2016 Axentia Technologies AB
      6 *
      7 * Author: Peter Rosin <peda@axentia.se>
      8 */
      9
     10/*
     11 * It is assumed that the dpot is used as a voltage divider between the
     12 * current dpot wiper setting and the maximum resistance of the dpot. The
     13 * divided voltage is provided by a vref regulator.
     14 *
     15 *                   .------.
     16 *    .-----------.  |      |
     17 *    | vref      |--'    .---.
     18 *    | regulator |--.    |   |
     19 *    '-----------'  |    | d |
     20 *                   |    | p |
     21 *                   |    | o |  wiper
     22 *                   |    | t |<---------+
     23 *                   |    |   |
     24 *                   |    '---'       dac output voltage
     25 *                   |      |
     26 *                   '------+------------+
     27 */
     28
     29#include <linux/err.h>
     30#include <linux/iio/consumer.h>
     31#include <linux/iio/iio.h>
     32#include <linux/module.h>
     33#include <linux/mod_devicetable.h>
     34#include <linux/platform_device.h>
     35#include <linux/regulator/consumer.h>
     36
     37struct dpot_dac {
     38	struct regulator *vref;
     39	struct iio_channel *dpot;
     40	u32 max_ohms;
     41};
     42
     43static const struct iio_chan_spec dpot_dac_iio_channel = {
     44	.type = IIO_VOLTAGE,
     45	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
     46			    | BIT(IIO_CHAN_INFO_SCALE),
     47	.info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW),
     48	.output = 1,
     49	.indexed = 1,
     50};
     51
     52static int dpot_dac_read_raw(struct iio_dev *indio_dev,
     53			     struct iio_chan_spec const *chan,
     54			     int *val, int *val2, long mask)
     55{
     56	struct dpot_dac *dac = iio_priv(indio_dev);
     57	int ret;
     58	unsigned long long tmp;
     59
     60	switch (mask) {
     61	case IIO_CHAN_INFO_RAW:
     62		return iio_read_channel_raw(dac->dpot, val);
     63
     64	case IIO_CHAN_INFO_SCALE:
     65		ret = iio_read_channel_scale(dac->dpot, val, val2);
     66		switch (ret) {
     67		case IIO_VAL_FRACTIONAL_LOG2:
     68			tmp = *val * 1000000000LL;
     69			do_div(tmp, dac->max_ohms);
     70			tmp *= regulator_get_voltage(dac->vref) / 1000;
     71			do_div(tmp, 1000000000LL);
     72			*val = tmp;
     73			return ret;
     74		case IIO_VAL_INT:
     75			/*
     76			 * Convert integer scale to fractional scale by
     77			 * setting the denominator (val2) to one...
     78			 */
     79			*val2 = 1;
     80			ret = IIO_VAL_FRACTIONAL;
     81			/* ...and fall through. Say it again for GCC. */
     82			fallthrough;
     83		case IIO_VAL_FRACTIONAL:
     84			*val *= regulator_get_voltage(dac->vref) / 1000;
     85			*val2 *= dac->max_ohms;
     86			break;
     87		}
     88
     89		return ret;
     90	}
     91
     92	return -EINVAL;
     93}
     94
     95static int dpot_dac_read_avail(struct iio_dev *indio_dev,
     96			       struct iio_chan_spec const *chan,
     97			       const int **vals, int *type, int *length,
     98			       long mask)
     99{
    100	struct dpot_dac *dac = iio_priv(indio_dev);
    101
    102	switch (mask) {
    103	case IIO_CHAN_INFO_RAW:
    104		*type = IIO_VAL_INT;
    105		return iio_read_avail_channel_raw(dac->dpot, vals, length);
    106	}
    107
    108	return -EINVAL;
    109}
    110
    111static int dpot_dac_write_raw(struct iio_dev *indio_dev,
    112			      struct iio_chan_spec const *chan,
    113			      int val, int val2, long mask)
    114{
    115	struct dpot_dac *dac = iio_priv(indio_dev);
    116
    117	switch (mask) {
    118	case IIO_CHAN_INFO_RAW:
    119		return iio_write_channel_raw(dac->dpot, val);
    120	}
    121
    122	return -EINVAL;
    123}
    124
    125static const struct iio_info dpot_dac_info = {
    126	.read_raw = dpot_dac_read_raw,
    127	.read_avail = dpot_dac_read_avail,
    128	.write_raw = dpot_dac_write_raw,
    129};
    130
    131static int dpot_dac_channel_max_ohms(struct iio_dev *indio_dev)
    132{
    133	struct device *dev = &indio_dev->dev;
    134	struct dpot_dac *dac = iio_priv(indio_dev);
    135	unsigned long long tmp;
    136	int ret;
    137	int val;
    138	int val2;
    139	int max;
    140
    141	ret = iio_read_max_channel_raw(dac->dpot, &max);
    142	if (ret < 0) {
    143		dev_err(dev, "dpot does not indicate its raw maximum value\n");
    144		return ret;
    145	}
    146
    147	switch (iio_read_channel_scale(dac->dpot, &val, &val2)) {
    148	case IIO_VAL_INT:
    149		return max * val;
    150	case IIO_VAL_FRACTIONAL:
    151		tmp = (unsigned long long)max * val;
    152		do_div(tmp, val2);
    153		return tmp;
    154	case IIO_VAL_FRACTIONAL_LOG2:
    155		tmp = val * 1000000000LL * max >> val2;
    156		do_div(tmp, 1000000000LL);
    157		return tmp;
    158	default:
    159		dev_err(dev, "dpot has a scale that is too weird\n");
    160	}
    161
    162	return -EINVAL;
    163}
    164
    165static int dpot_dac_probe(struct platform_device *pdev)
    166{
    167	struct device *dev = &pdev->dev;
    168	struct iio_dev *indio_dev;
    169	struct dpot_dac *dac;
    170	enum iio_chan_type type;
    171	int ret;
    172
    173	indio_dev = devm_iio_device_alloc(dev, sizeof(*dac));
    174	if (!indio_dev)
    175		return -ENOMEM;
    176
    177	platform_set_drvdata(pdev, indio_dev);
    178	dac = iio_priv(indio_dev);
    179
    180	indio_dev->name = dev_name(dev);
    181	indio_dev->info = &dpot_dac_info;
    182	indio_dev->modes = INDIO_DIRECT_MODE;
    183	indio_dev->channels = &dpot_dac_iio_channel;
    184	indio_dev->num_channels = 1;
    185
    186	dac->vref = devm_regulator_get(dev, "vref");
    187	if (IS_ERR(dac->vref))
    188		return dev_err_probe(&pdev->dev, PTR_ERR(dac->vref),
    189				     "failed to get vref regulator\n");
    190
    191	dac->dpot = devm_iio_channel_get(dev, "dpot");
    192	if (IS_ERR(dac->dpot))
    193		return dev_err_probe(&pdev->dev, PTR_ERR(dac->dpot),
    194				     "failed to get dpot input channel\n");
    195
    196	ret = iio_get_channel_type(dac->dpot, &type);
    197	if (ret < 0)
    198		return ret;
    199
    200	if (type != IIO_RESISTANCE) {
    201		dev_err(dev, "dpot is of the wrong type\n");
    202		return -EINVAL;
    203	}
    204
    205	ret = dpot_dac_channel_max_ohms(indio_dev);
    206	if (ret < 0)
    207		return ret;
    208	dac->max_ohms = ret;
    209
    210	ret = regulator_enable(dac->vref);
    211	if (ret) {
    212		dev_err(dev, "failed to enable the vref regulator\n");
    213		return ret;
    214	}
    215
    216	ret = iio_device_register(indio_dev);
    217	if (ret) {
    218		dev_err(dev, "failed to register iio device\n");
    219		goto disable_reg;
    220	}
    221
    222	return 0;
    223
    224disable_reg:
    225	regulator_disable(dac->vref);
    226	return ret;
    227}
    228
    229static int dpot_dac_remove(struct platform_device *pdev)
    230{
    231	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
    232	struct dpot_dac *dac = iio_priv(indio_dev);
    233
    234	iio_device_unregister(indio_dev);
    235	regulator_disable(dac->vref);
    236
    237	return 0;
    238}
    239
    240static const struct of_device_id dpot_dac_match[] = {
    241	{ .compatible = "dpot-dac" },
    242	{ /* sentinel */ }
    243};
    244MODULE_DEVICE_TABLE(of, dpot_dac_match);
    245
    246static struct platform_driver dpot_dac_driver = {
    247	.probe = dpot_dac_probe,
    248	.remove = dpot_dac_remove,
    249	.driver = {
    250		.name = "iio-dpot-dac",
    251		.of_match_table = dpot_dac_match,
    252	},
    253};
    254module_platform_driver(dpot_dac_driver);
    255
    256MODULE_DESCRIPTION("DAC emulation driver using a digital potentiometer");
    257MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
    258MODULE_LICENSE("GPL v2");