18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * IIO rescale driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2018 Axentia Technologies AB
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Peter Rosin <peda@axentia.se>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/err.h>
118c2ecf20Sopenharmony_ci#include <linux/gcd.h>
128c2ecf20Sopenharmony_ci#include <linux/iio/consumer.h>
138c2ecf20Sopenharmony_ci#include <linux/iio/iio.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/of.h>
168c2ecf20Sopenharmony_ci#include <linux/of_device.h>
178c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
188c2ecf20Sopenharmony_ci#include <linux/property.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct rescale;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistruct rescale_cfg {
238c2ecf20Sopenharmony_ci	enum iio_chan_type type;
248c2ecf20Sopenharmony_ci	int (*props)(struct device *dev, struct rescale *rescale);
258c2ecf20Sopenharmony_ci};
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistruct rescale {
288c2ecf20Sopenharmony_ci	const struct rescale_cfg *cfg;
298c2ecf20Sopenharmony_ci	struct iio_channel *source;
308c2ecf20Sopenharmony_ci	struct iio_chan_spec chan;
318c2ecf20Sopenharmony_ci	struct iio_chan_spec_ext_info *ext_info;
328c2ecf20Sopenharmony_ci	s32 numerator;
338c2ecf20Sopenharmony_ci	s32 denominator;
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic int rescale_read_raw(struct iio_dev *indio_dev,
378c2ecf20Sopenharmony_ci			    struct iio_chan_spec const *chan,
388c2ecf20Sopenharmony_ci			    int *val, int *val2, long mask)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	struct rescale *rescale = iio_priv(indio_dev);
418c2ecf20Sopenharmony_ci	s64 tmp;
428c2ecf20Sopenharmony_ci	int ret;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	switch (mask) {
458c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
468c2ecf20Sopenharmony_ci		return iio_read_channel_raw(rescale->source, val);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
498c2ecf20Sopenharmony_ci		ret = iio_read_channel_scale(rescale->source, val, val2);
508c2ecf20Sopenharmony_ci		switch (ret) {
518c2ecf20Sopenharmony_ci		case IIO_VAL_FRACTIONAL:
528c2ecf20Sopenharmony_ci			*val *= rescale->numerator;
538c2ecf20Sopenharmony_ci			*val2 *= rescale->denominator;
548c2ecf20Sopenharmony_ci			return ret;
558c2ecf20Sopenharmony_ci		case IIO_VAL_INT:
568c2ecf20Sopenharmony_ci			*val *= rescale->numerator;
578c2ecf20Sopenharmony_ci			if (rescale->denominator == 1)
588c2ecf20Sopenharmony_ci				return ret;
598c2ecf20Sopenharmony_ci			*val2 = rescale->denominator;
608c2ecf20Sopenharmony_ci			return IIO_VAL_FRACTIONAL;
618c2ecf20Sopenharmony_ci		case IIO_VAL_FRACTIONAL_LOG2:
628c2ecf20Sopenharmony_ci			tmp = (s64)*val * 1000000000LL;
638c2ecf20Sopenharmony_ci			tmp = div_s64(tmp, rescale->denominator);
648c2ecf20Sopenharmony_ci			tmp *= rescale->numerator;
658c2ecf20Sopenharmony_ci			tmp = div_s64(tmp, 1000000000LL);
668c2ecf20Sopenharmony_ci			*val = tmp;
678c2ecf20Sopenharmony_ci			return ret;
688c2ecf20Sopenharmony_ci		default:
698c2ecf20Sopenharmony_ci			return -EOPNOTSUPP;
708c2ecf20Sopenharmony_ci		}
718c2ecf20Sopenharmony_ci	default:
728c2ecf20Sopenharmony_ci		return -EINVAL;
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int rescale_read_avail(struct iio_dev *indio_dev,
778c2ecf20Sopenharmony_ci			      struct iio_chan_spec const *chan,
788c2ecf20Sopenharmony_ci			      const int **vals, int *type, int *length,
798c2ecf20Sopenharmony_ci			      long mask)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	struct rescale *rescale = iio_priv(indio_dev);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	switch (mask) {
848c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
858c2ecf20Sopenharmony_ci		*type = IIO_VAL_INT;
868c2ecf20Sopenharmony_ci		return iio_read_avail_channel_raw(rescale->source,
878c2ecf20Sopenharmony_ci						  vals, length);
888c2ecf20Sopenharmony_ci	default:
898c2ecf20Sopenharmony_ci		return -EINVAL;
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic const struct iio_info rescale_info = {
948c2ecf20Sopenharmony_ci	.read_raw = rescale_read_raw,
958c2ecf20Sopenharmony_ci	.read_avail = rescale_read_avail,
968c2ecf20Sopenharmony_ci};
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic ssize_t rescale_read_ext_info(struct iio_dev *indio_dev,
998c2ecf20Sopenharmony_ci				     uintptr_t private,
1008c2ecf20Sopenharmony_ci				     struct iio_chan_spec const *chan,
1018c2ecf20Sopenharmony_ci				     char *buf)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	struct rescale *rescale = iio_priv(indio_dev);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	return iio_read_channel_ext_info(rescale->source,
1068c2ecf20Sopenharmony_ci					 rescale->ext_info[private].name,
1078c2ecf20Sopenharmony_ci					 buf);
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic ssize_t rescale_write_ext_info(struct iio_dev *indio_dev,
1118c2ecf20Sopenharmony_ci				      uintptr_t private,
1128c2ecf20Sopenharmony_ci				      struct iio_chan_spec const *chan,
1138c2ecf20Sopenharmony_ci				      const char *buf, size_t len)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct rescale *rescale = iio_priv(indio_dev);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	return iio_write_channel_ext_info(rescale->source,
1188c2ecf20Sopenharmony_ci					  rescale->ext_info[private].name,
1198c2ecf20Sopenharmony_ci					  buf, len);
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic int rescale_configure_channel(struct device *dev,
1238c2ecf20Sopenharmony_ci				     struct rescale *rescale)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	struct iio_chan_spec *chan = &rescale->chan;
1268c2ecf20Sopenharmony_ci	struct iio_chan_spec const *schan = rescale->source->channel;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	chan->indexed = 1;
1298c2ecf20Sopenharmony_ci	chan->output = schan->output;
1308c2ecf20Sopenharmony_ci	chan->ext_info = rescale->ext_info;
1318c2ecf20Sopenharmony_ci	chan->type = rescale->cfg->type;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	if (!iio_channel_has_info(schan, IIO_CHAN_INFO_RAW) ||
1348c2ecf20Sopenharmony_ci	    !iio_channel_has_info(schan, IIO_CHAN_INFO_SCALE)) {
1358c2ecf20Sopenharmony_ci		dev_err(dev, "source channel does not support raw/scale\n");
1368c2ecf20Sopenharmony_ci		return -EINVAL;
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
1408c2ecf20Sopenharmony_ci		BIT(IIO_CHAN_INFO_SCALE);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (iio_channel_has_available(schan, IIO_CHAN_INFO_RAW))
1438c2ecf20Sopenharmony_ci		chan->info_mask_separate_available |= BIT(IIO_CHAN_INFO_RAW);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	return 0;
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic int rescale_current_sense_amplifier_props(struct device *dev,
1498c2ecf20Sopenharmony_ci						 struct rescale *rescale)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	u32 sense;
1528c2ecf20Sopenharmony_ci	u32 gain_mult = 1;
1538c2ecf20Sopenharmony_ci	u32 gain_div = 1;
1548c2ecf20Sopenharmony_ci	u32 factor;
1558c2ecf20Sopenharmony_ci	int ret;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	ret = device_property_read_u32(dev, "sense-resistor-micro-ohms",
1588c2ecf20Sopenharmony_ci				       &sense);
1598c2ecf20Sopenharmony_ci	if (ret) {
1608c2ecf20Sopenharmony_ci		dev_err(dev, "failed to read the sense resistance: %d\n", ret);
1618c2ecf20Sopenharmony_ci		return ret;
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	device_property_read_u32(dev, "sense-gain-mult", &gain_mult);
1658c2ecf20Sopenharmony_ci	device_property_read_u32(dev, "sense-gain-div", &gain_div);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	/*
1688c2ecf20Sopenharmony_ci	 * Calculate the scaling factor, 1 / (gain * sense), or
1698c2ecf20Sopenharmony_ci	 * gain_div / (gain_mult * sense), while trying to keep the
1708c2ecf20Sopenharmony_ci	 * numerator/denominator from overflowing.
1718c2ecf20Sopenharmony_ci	 */
1728c2ecf20Sopenharmony_ci	factor = gcd(sense, 1000000);
1738c2ecf20Sopenharmony_ci	rescale->numerator = 1000000 / factor;
1748c2ecf20Sopenharmony_ci	rescale->denominator = sense / factor;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	factor = gcd(rescale->numerator, gain_mult);
1778c2ecf20Sopenharmony_ci	rescale->numerator /= factor;
1788c2ecf20Sopenharmony_ci	rescale->denominator *= gain_mult / factor;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	factor = gcd(rescale->denominator, gain_div);
1818c2ecf20Sopenharmony_ci	rescale->numerator *= gain_div / factor;
1828c2ecf20Sopenharmony_ci	rescale->denominator /= factor;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	return 0;
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic int rescale_current_sense_shunt_props(struct device *dev,
1888c2ecf20Sopenharmony_ci					     struct rescale *rescale)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	u32 shunt;
1918c2ecf20Sopenharmony_ci	u32 factor;
1928c2ecf20Sopenharmony_ci	int ret;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	ret = device_property_read_u32(dev, "shunt-resistor-micro-ohms",
1958c2ecf20Sopenharmony_ci				       &shunt);
1968c2ecf20Sopenharmony_ci	if (ret) {
1978c2ecf20Sopenharmony_ci		dev_err(dev, "failed to read the shunt resistance: %d\n", ret);
1988c2ecf20Sopenharmony_ci		return ret;
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	factor = gcd(shunt, 1000000);
2028c2ecf20Sopenharmony_ci	rescale->numerator = 1000000 / factor;
2038c2ecf20Sopenharmony_ci	rescale->denominator = shunt / factor;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	return 0;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic int rescale_voltage_divider_props(struct device *dev,
2098c2ecf20Sopenharmony_ci					 struct rescale *rescale)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	int ret;
2128c2ecf20Sopenharmony_ci	u32 factor;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	ret = device_property_read_u32(dev, "output-ohms",
2158c2ecf20Sopenharmony_ci				       &rescale->denominator);
2168c2ecf20Sopenharmony_ci	if (ret) {
2178c2ecf20Sopenharmony_ci		dev_err(dev, "failed to read output-ohms: %d\n", ret);
2188c2ecf20Sopenharmony_ci		return ret;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	ret = device_property_read_u32(dev, "full-ohms",
2228c2ecf20Sopenharmony_ci				       &rescale->numerator);
2238c2ecf20Sopenharmony_ci	if (ret) {
2248c2ecf20Sopenharmony_ci		dev_err(dev, "failed to read full-ohms: %d\n", ret);
2258c2ecf20Sopenharmony_ci		return ret;
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	factor = gcd(rescale->numerator, rescale->denominator);
2298c2ecf20Sopenharmony_ci	rescale->numerator /= factor;
2308c2ecf20Sopenharmony_ci	rescale->denominator /= factor;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	return 0;
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cienum rescale_variant {
2368c2ecf20Sopenharmony_ci	CURRENT_SENSE_AMPLIFIER,
2378c2ecf20Sopenharmony_ci	CURRENT_SENSE_SHUNT,
2388c2ecf20Sopenharmony_ci	VOLTAGE_DIVIDER,
2398c2ecf20Sopenharmony_ci};
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic const struct rescale_cfg rescale_cfg[] = {
2428c2ecf20Sopenharmony_ci	[CURRENT_SENSE_AMPLIFIER] = {
2438c2ecf20Sopenharmony_ci		.type = IIO_CURRENT,
2448c2ecf20Sopenharmony_ci		.props = rescale_current_sense_amplifier_props,
2458c2ecf20Sopenharmony_ci	},
2468c2ecf20Sopenharmony_ci	[CURRENT_SENSE_SHUNT] = {
2478c2ecf20Sopenharmony_ci		.type = IIO_CURRENT,
2488c2ecf20Sopenharmony_ci		.props = rescale_current_sense_shunt_props,
2498c2ecf20Sopenharmony_ci	},
2508c2ecf20Sopenharmony_ci	[VOLTAGE_DIVIDER] = {
2518c2ecf20Sopenharmony_ci		.type = IIO_VOLTAGE,
2528c2ecf20Sopenharmony_ci		.props = rescale_voltage_divider_props,
2538c2ecf20Sopenharmony_ci	},
2548c2ecf20Sopenharmony_ci};
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic const struct of_device_id rescale_match[] = {
2578c2ecf20Sopenharmony_ci	{ .compatible = "current-sense-amplifier",
2588c2ecf20Sopenharmony_ci	  .data = &rescale_cfg[CURRENT_SENSE_AMPLIFIER], },
2598c2ecf20Sopenharmony_ci	{ .compatible = "current-sense-shunt",
2608c2ecf20Sopenharmony_ci	  .data = &rescale_cfg[CURRENT_SENSE_SHUNT], },
2618c2ecf20Sopenharmony_ci	{ .compatible = "voltage-divider",
2628c2ecf20Sopenharmony_ci	  .data = &rescale_cfg[VOLTAGE_DIVIDER], },
2638c2ecf20Sopenharmony_ci	{ /* sentinel */ }
2648c2ecf20Sopenharmony_ci};
2658c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, rescale_match);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic int rescale_probe(struct platform_device *pdev)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
2708c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev;
2718c2ecf20Sopenharmony_ci	struct iio_channel *source;
2728c2ecf20Sopenharmony_ci	struct rescale *rescale;
2738c2ecf20Sopenharmony_ci	int sizeof_ext_info;
2748c2ecf20Sopenharmony_ci	int sizeof_priv;
2758c2ecf20Sopenharmony_ci	int i;
2768c2ecf20Sopenharmony_ci	int ret;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	source = devm_iio_channel_get(dev, NULL);
2798c2ecf20Sopenharmony_ci	if (IS_ERR(source))
2808c2ecf20Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(source),
2818c2ecf20Sopenharmony_ci				     "failed to get source channel\n");
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	sizeof_ext_info = iio_get_channel_ext_info_count(source);
2848c2ecf20Sopenharmony_ci	if (sizeof_ext_info) {
2858c2ecf20Sopenharmony_ci		sizeof_ext_info += 1; /* one extra entry for the sentinel */
2868c2ecf20Sopenharmony_ci		sizeof_ext_info *= sizeof(*rescale->ext_info);
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	sizeof_priv = sizeof(*rescale) + sizeof_ext_info;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	indio_dev = devm_iio_device_alloc(dev, sizeof_priv);
2928c2ecf20Sopenharmony_ci	if (!indio_dev)
2938c2ecf20Sopenharmony_ci		return -ENOMEM;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	rescale = iio_priv(indio_dev);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	rescale->cfg = of_device_get_match_data(dev);
2988c2ecf20Sopenharmony_ci	rescale->numerator = 1;
2998c2ecf20Sopenharmony_ci	rescale->denominator = 1;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	ret = rescale->cfg->props(dev, rescale);
3028c2ecf20Sopenharmony_ci	if (ret)
3038c2ecf20Sopenharmony_ci		return ret;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	if (!rescale->numerator || !rescale->denominator) {
3068c2ecf20Sopenharmony_ci		dev_err(dev, "invalid scaling factor.\n");
3078c2ecf20Sopenharmony_ci		return -EINVAL;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, indio_dev);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	rescale->source = source;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	indio_dev->name = dev_name(dev);
3158c2ecf20Sopenharmony_ci	indio_dev->info = &rescale_info;
3168c2ecf20Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
3178c2ecf20Sopenharmony_ci	indio_dev->channels = &rescale->chan;
3188c2ecf20Sopenharmony_ci	indio_dev->num_channels = 1;
3198c2ecf20Sopenharmony_ci	if (sizeof_ext_info) {
3208c2ecf20Sopenharmony_ci		rescale->ext_info = devm_kmemdup(dev,
3218c2ecf20Sopenharmony_ci						 source->channel->ext_info,
3228c2ecf20Sopenharmony_ci						 sizeof_ext_info, GFP_KERNEL);
3238c2ecf20Sopenharmony_ci		if (!rescale->ext_info)
3248c2ecf20Sopenharmony_ci			return -ENOMEM;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci		for (i = 0; rescale->ext_info[i].name; ++i) {
3278c2ecf20Sopenharmony_ci			struct iio_chan_spec_ext_info *ext_info =
3288c2ecf20Sopenharmony_ci				&rescale->ext_info[i];
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci			if (source->channel->ext_info[i].read)
3318c2ecf20Sopenharmony_ci				ext_info->read = rescale_read_ext_info;
3328c2ecf20Sopenharmony_ci			if (source->channel->ext_info[i].write)
3338c2ecf20Sopenharmony_ci				ext_info->write = rescale_write_ext_info;
3348c2ecf20Sopenharmony_ci			ext_info->private = i;
3358c2ecf20Sopenharmony_ci		}
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	ret = rescale_configure_channel(dev, rescale);
3398c2ecf20Sopenharmony_ci	if (ret)
3408c2ecf20Sopenharmony_ci		return ret;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	return devm_iio_device_register(dev, indio_dev);
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_cistatic struct platform_driver rescale_driver = {
3468c2ecf20Sopenharmony_ci	.probe = rescale_probe,
3478c2ecf20Sopenharmony_ci	.driver = {
3488c2ecf20Sopenharmony_ci		.name = "iio-rescale",
3498c2ecf20Sopenharmony_ci		.of_match_table = rescale_match,
3508c2ecf20Sopenharmony_ci	},
3518c2ecf20Sopenharmony_ci};
3528c2ecf20Sopenharmony_cimodule_platform_driver(rescale_driver);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IIO rescale driver");
3558c2ecf20Sopenharmony_ciMODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
3568c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
357