xref: /OK3568_Linux_fs/kernel/drivers/iio/afe/iio-rescale.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * IIO rescale driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2018 Axentia Technologies AB
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Author: Peter Rosin <peda@axentia.se>
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include <linux/err.h>
11*4882a593Smuzhiyun #include <linux/gcd.h>
12*4882a593Smuzhiyun #include <linux/iio/consumer.h>
13*4882a593Smuzhiyun #include <linux/iio/iio.h>
14*4882a593Smuzhiyun #include <linux/module.h>
15*4882a593Smuzhiyun #include <linux/of.h>
16*4882a593Smuzhiyun #include <linux/of_device.h>
17*4882a593Smuzhiyun #include <linux/platform_device.h>
18*4882a593Smuzhiyun #include <linux/property.h>
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun struct rescale;
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun struct rescale_cfg {
23*4882a593Smuzhiyun 	enum iio_chan_type type;
24*4882a593Smuzhiyun 	int (*props)(struct device *dev, struct rescale *rescale);
25*4882a593Smuzhiyun };
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun struct rescale {
28*4882a593Smuzhiyun 	const struct rescale_cfg *cfg;
29*4882a593Smuzhiyun 	struct iio_channel *source;
30*4882a593Smuzhiyun 	struct iio_chan_spec chan;
31*4882a593Smuzhiyun 	struct iio_chan_spec_ext_info *ext_info;
32*4882a593Smuzhiyun 	s32 numerator;
33*4882a593Smuzhiyun 	s32 denominator;
34*4882a593Smuzhiyun };
35*4882a593Smuzhiyun 
rescale_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)36*4882a593Smuzhiyun static int rescale_read_raw(struct iio_dev *indio_dev,
37*4882a593Smuzhiyun 			    struct iio_chan_spec const *chan,
38*4882a593Smuzhiyun 			    int *val, int *val2, long mask)
39*4882a593Smuzhiyun {
40*4882a593Smuzhiyun 	struct rescale *rescale = iio_priv(indio_dev);
41*4882a593Smuzhiyun 	s64 tmp;
42*4882a593Smuzhiyun 	int ret;
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun 	switch (mask) {
45*4882a593Smuzhiyun 	case IIO_CHAN_INFO_RAW:
46*4882a593Smuzhiyun 		return iio_read_channel_raw(rescale->source, val);
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun 	case IIO_CHAN_INFO_SCALE:
49*4882a593Smuzhiyun 		ret = iio_read_channel_scale(rescale->source, val, val2);
50*4882a593Smuzhiyun 		switch (ret) {
51*4882a593Smuzhiyun 		case IIO_VAL_FRACTIONAL:
52*4882a593Smuzhiyun 			*val *= rescale->numerator;
53*4882a593Smuzhiyun 			*val2 *= rescale->denominator;
54*4882a593Smuzhiyun 			return ret;
55*4882a593Smuzhiyun 		case IIO_VAL_INT:
56*4882a593Smuzhiyun 			*val *= rescale->numerator;
57*4882a593Smuzhiyun 			if (rescale->denominator == 1)
58*4882a593Smuzhiyun 				return ret;
59*4882a593Smuzhiyun 			*val2 = rescale->denominator;
60*4882a593Smuzhiyun 			return IIO_VAL_FRACTIONAL;
61*4882a593Smuzhiyun 		case IIO_VAL_FRACTIONAL_LOG2:
62*4882a593Smuzhiyun 			tmp = (s64)*val * 1000000000LL;
63*4882a593Smuzhiyun 			tmp = div_s64(tmp, rescale->denominator);
64*4882a593Smuzhiyun 			tmp *= rescale->numerator;
65*4882a593Smuzhiyun 			tmp = div_s64(tmp, 1000000000LL);
66*4882a593Smuzhiyun 			*val = tmp;
67*4882a593Smuzhiyun 			return ret;
68*4882a593Smuzhiyun 		default:
69*4882a593Smuzhiyun 			return -EOPNOTSUPP;
70*4882a593Smuzhiyun 		}
71*4882a593Smuzhiyun 	default:
72*4882a593Smuzhiyun 		return -EINVAL;
73*4882a593Smuzhiyun 	}
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun 
rescale_read_avail(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,const int ** vals,int * type,int * length,long mask)76*4882a593Smuzhiyun static int rescale_read_avail(struct iio_dev *indio_dev,
77*4882a593Smuzhiyun 			      struct iio_chan_spec const *chan,
78*4882a593Smuzhiyun 			      const int **vals, int *type, int *length,
79*4882a593Smuzhiyun 			      long mask)
80*4882a593Smuzhiyun {
81*4882a593Smuzhiyun 	struct rescale *rescale = iio_priv(indio_dev);
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 	switch (mask) {
84*4882a593Smuzhiyun 	case IIO_CHAN_INFO_RAW:
85*4882a593Smuzhiyun 		*type = IIO_VAL_INT;
86*4882a593Smuzhiyun 		return iio_read_avail_channel_raw(rescale->source,
87*4882a593Smuzhiyun 						  vals, length);
88*4882a593Smuzhiyun 	default:
89*4882a593Smuzhiyun 		return -EINVAL;
90*4882a593Smuzhiyun 	}
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun static const struct iio_info rescale_info = {
94*4882a593Smuzhiyun 	.read_raw = rescale_read_raw,
95*4882a593Smuzhiyun 	.read_avail = rescale_read_avail,
96*4882a593Smuzhiyun };
97*4882a593Smuzhiyun 
rescale_read_ext_info(struct iio_dev * indio_dev,uintptr_t private,struct iio_chan_spec const * chan,char * buf)98*4882a593Smuzhiyun static ssize_t rescale_read_ext_info(struct iio_dev *indio_dev,
99*4882a593Smuzhiyun 				     uintptr_t private,
100*4882a593Smuzhiyun 				     struct iio_chan_spec const *chan,
101*4882a593Smuzhiyun 				     char *buf)
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun 	struct rescale *rescale = iio_priv(indio_dev);
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	return iio_read_channel_ext_info(rescale->source,
106*4882a593Smuzhiyun 					 rescale->ext_info[private].name,
107*4882a593Smuzhiyun 					 buf);
108*4882a593Smuzhiyun }
109*4882a593Smuzhiyun 
rescale_write_ext_info(struct iio_dev * indio_dev,uintptr_t private,struct iio_chan_spec const * chan,const char * buf,size_t len)110*4882a593Smuzhiyun static ssize_t rescale_write_ext_info(struct iio_dev *indio_dev,
111*4882a593Smuzhiyun 				      uintptr_t private,
112*4882a593Smuzhiyun 				      struct iio_chan_spec const *chan,
113*4882a593Smuzhiyun 				      const char *buf, size_t len)
114*4882a593Smuzhiyun {
115*4882a593Smuzhiyun 	struct rescale *rescale = iio_priv(indio_dev);
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	return iio_write_channel_ext_info(rescale->source,
118*4882a593Smuzhiyun 					  rescale->ext_info[private].name,
119*4882a593Smuzhiyun 					  buf, len);
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun 
rescale_configure_channel(struct device * dev,struct rescale * rescale)122*4882a593Smuzhiyun static int rescale_configure_channel(struct device *dev,
123*4882a593Smuzhiyun 				     struct rescale *rescale)
124*4882a593Smuzhiyun {
125*4882a593Smuzhiyun 	struct iio_chan_spec *chan = &rescale->chan;
126*4882a593Smuzhiyun 	struct iio_chan_spec const *schan = rescale->source->channel;
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	chan->indexed = 1;
129*4882a593Smuzhiyun 	chan->output = schan->output;
130*4882a593Smuzhiyun 	chan->ext_info = rescale->ext_info;
131*4882a593Smuzhiyun 	chan->type = rescale->cfg->type;
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	if (!iio_channel_has_info(schan, IIO_CHAN_INFO_RAW) ||
134*4882a593Smuzhiyun 	    !iio_channel_has_info(schan, IIO_CHAN_INFO_SCALE)) {
135*4882a593Smuzhiyun 		dev_err(dev, "source channel does not support raw/scale\n");
136*4882a593Smuzhiyun 		return -EINVAL;
137*4882a593Smuzhiyun 	}
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
140*4882a593Smuzhiyun 		BIT(IIO_CHAN_INFO_SCALE);
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	if (iio_channel_has_available(schan, IIO_CHAN_INFO_RAW))
143*4882a593Smuzhiyun 		chan->info_mask_separate_available |= BIT(IIO_CHAN_INFO_RAW);
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	return 0;
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun 
rescale_current_sense_amplifier_props(struct device * dev,struct rescale * rescale)148*4882a593Smuzhiyun static int rescale_current_sense_amplifier_props(struct device *dev,
149*4882a593Smuzhiyun 						 struct rescale *rescale)
150*4882a593Smuzhiyun {
151*4882a593Smuzhiyun 	u32 sense;
152*4882a593Smuzhiyun 	u32 gain_mult = 1;
153*4882a593Smuzhiyun 	u32 gain_div = 1;
154*4882a593Smuzhiyun 	u32 factor;
155*4882a593Smuzhiyun 	int ret;
156*4882a593Smuzhiyun 
157*4882a593Smuzhiyun 	ret = device_property_read_u32(dev, "sense-resistor-micro-ohms",
158*4882a593Smuzhiyun 				       &sense);
159*4882a593Smuzhiyun 	if (ret) {
160*4882a593Smuzhiyun 		dev_err(dev, "failed to read the sense resistance: %d\n", ret);
161*4882a593Smuzhiyun 		return ret;
162*4882a593Smuzhiyun 	}
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	device_property_read_u32(dev, "sense-gain-mult", &gain_mult);
165*4882a593Smuzhiyun 	device_property_read_u32(dev, "sense-gain-div", &gain_div);
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	/*
168*4882a593Smuzhiyun 	 * Calculate the scaling factor, 1 / (gain * sense), or
169*4882a593Smuzhiyun 	 * gain_div / (gain_mult * sense), while trying to keep the
170*4882a593Smuzhiyun 	 * numerator/denominator from overflowing.
171*4882a593Smuzhiyun 	 */
172*4882a593Smuzhiyun 	factor = gcd(sense, 1000000);
173*4882a593Smuzhiyun 	rescale->numerator = 1000000 / factor;
174*4882a593Smuzhiyun 	rescale->denominator = sense / factor;
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 	factor = gcd(rescale->numerator, gain_mult);
177*4882a593Smuzhiyun 	rescale->numerator /= factor;
178*4882a593Smuzhiyun 	rescale->denominator *= gain_mult / factor;
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	factor = gcd(rescale->denominator, gain_div);
181*4882a593Smuzhiyun 	rescale->numerator *= gain_div / factor;
182*4882a593Smuzhiyun 	rescale->denominator /= factor;
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	return 0;
185*4882a593Smuzhiyun }
186*4882a593Smuzhiyun 
rescale_current_sense_shunt_props(struct device * dev,struct rescale * rescale)187*4882a593Smuzhiyun static int rescale_current_sense_shunt_props(struct device *dev,
188*4882a593Smuzhiyun 					     struct rescale *rescale)
189*4882a593Smuzhiyun {
190*4882a593Smuzhiyun 	u32 shunt;
191*4882a593Smuzhiyun 	u32 factor;
192*4882a593Smuzhiyun 	int ret;
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	ret = device_property_read_u32(dev, "shunt-resistor-micro-ohms",
195*4882a593Smuzhiyun 				       &shunt);
196*4882a593Smuzhiyun 	if (ret) {
197*4882a593Smuzhiyun 		dev_err(dev, "failed to read the shunt resistance: %d\n", ret);
198*4882a593Smuzhiyun 		return ret;
199*4882a593Smuzhiyun 	}
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	factor = gcd(shunt, 1000000);
202*4882a593Smuzhiyun 	rescale->numerator = 1000000 / factor;
203*4882a593Smuzhiyun 	rescale->denominator = shunt / factor;
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	return 0;
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun 
rescale_voltage_divider_props(struct device * dev,struct rescale * rescale)208*4882a593Smuzhiyun static int rescale_voltage_divider_props(struct device *dev,
209*4882a593Smuzhiyun 					 struct rescale *rescale)
210*4882a593Smuzhiyun {
211*4882a593Smuzhiyun 	int ret;
212*4882a593Smuzhiyun 	u32 factor;
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	ret = device_property_read_u32(dev, "output-ohms",
215*4882a593Smuzhiyun 				       &rescale->denominator);
216*4882a593Smuzhiyun 	if (ret) {
217*4882a593Smuzhiyun 		dev_err(dev, "failed to read output-ohms: %d\n", ret);
218*4882a593Smuzhiyun 		return ret;
219*4882a593Smuzhiyun 	}
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 	ret = device_property_read_u32(dev, "full-ohms",
222*4882a593Smuzhiyun 				       &rescale->numerator);
223*4882a593Smuzhiyun 	if (ret) {
224*4882a593Smuzhiyun 		dev_err(dev, "failed to read full-ohms: %d\n", ret);
225*4882a593Smuzhiyun 		return ret;
226*4882a593Smuzhiyun 	}
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	factor = gcd(rescale->numerator, rescale->denominator);
229*4882a593Smuzhiyun 	rescale->numerator /= factor;
230*4882a593Smuzhiyun 	rescale->denominator /= factor;
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	return 0;
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun enum rescale_variant {
236*4882a593Smuzhiyun 	CURRENT_SENSE_AMPLIFIER,
237*4882a593Smuzhiyun 	CURRENT_SENSE_SHUNT,
238*4882a593Smuzhiyun 	VOLTAGE_DIVIDER,
239*4882a593Smuzhiyun };
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun static const struct rescale_cfg rescale_cfg[] = {
242*4882a593Smuzhiyun 	[CURRENT_SENSE_AMPLIFIER] = {
243*4882a593Smuzhiyun 		.type = IIO_CURRENT,
244*4882a593Smuzhiyun 		.props = rescale_current_sense_amplifier_props,
245*4882a593Smuzhiyun 	},
246*4882a593Smuzhiyun 	[CURRENT_SENSE_SHUNT] = {
247*4882a593Smuzhiyun 		.type = IIO_CURRENT,
248*4882a593Smuzhiyun 		.props = rescale_current_sense_shunt_props,
249*4882a593Smuzhiyun 	},
250*4882a593Smuzhiyun 	[VOLTAGE_DIVIDER] = {
251*4882a593Smuzhiyun 		.type = IIO_VOLTAGE,
252*4882a593Smuzhiyun 		.props = rescale_voltage_divider_props,
253*4882a593Smuzhiyun 	},
254*4882a593Smuzhiyun };
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun static const struct of_device_id rescale_match[] = {
257*4882a593Smuzhiyun 	{ .compatible = "current-sense-amplifier",
258*4882a593Smuzhiyun 	  .data = &rescale_cfg[CURRENT_SENSE_AMPLIFIER], },
259*4882a593Smuzhiyun 	{ .compatible = "current-sense-shunt",
260*4882a593Smuzhiyun 	  .data = &rescale_cfg[CURRENT_SENSE_SHUNT], },
261*4882a593Smuzhiyun 	{ .compatible = "voltage-divider",
262*4882a593Smuzhiyun 	  .data = &rescale_cfg[VOLTAGE_DIVIDER], },
263*4882a593Smuzhiyun 	{ /* sentinel */ }
264*4882a593Smuzhiyun };
265*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, rescale_match);
266*4882a593Smuzhiyun 
rescale_probe(struct platform_device * pdev)267*4882a593Smuzhiyun static int rescale_probe(struct platform_device *pdev)
268*4882a593Smuzhiyun {
269*4882a593Smuzhiyun 	struct device *dev = &pdev->dev;
270*4882a593Smuzhiyun 	struct iio_dev *indio_dev;
271*4882a593Smuzhiyun 	struct iio_channel *source;
272*4882a593Smuzhiyun 	struct rescale *rescale;
273*4882a593Smuzhiyun 	int sizeof_ext_info;
274*4882a593Smuzhiyun 	int sizeof_priv;
275*4882a593Smuzhiyun 	int i;
276*4882a593Smuzhiyun 	int ret;
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 	source = devm_iio_channel_get(dev, NULL);
279*4882a593Smuzhiyun 	if (IS_ERR(source))
280*4882a593Smuzhiyun 		return dev_err_probe(dev, PTR_ERR(source),
281*4882a593Smuzhiyun 				     "failed to get source channel\n");
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 	sizeof_ext_info = iio_get_channel_ext_info_count(source);
284*4882a593Smuzhiyun 	if (sizeof_ext_info) {
285*4882a593Smuzhiyun 		sizeof_ext_info += 1; /* one extra entry for the sentinel */
286*4882a593Smuzhiyun 		sizeof_ext_info *= sizeof(*rescale->ext_info);
287*4882a593Smuzhiyun 	}
288*4882a593Smuzhiyun 
289*4882a593Smuzhiyun 	sizeof_priv = sizeof(*rescale) + sizeof_ext_info;
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 	indio_dev = devm_iio_device_alloc(dev, sizeof_priv);
292*4882a593Smuzhiyun 	if (!indio_dev)
293*4882a593Smuzhiyun 		return -ENOMEM;
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun 	rescale = iio_priv(indio_dev);
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun 	rescale->cfg = of_device_get_match_data(dev);
298*4882a593Smuzhiyun 	rescale->numerator = 1;
299*4882a593Smuzhiyun 	rescale->denominator = 1;
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 	ret = rescale->cfg->props(dev, rescale);
302*4882a593Smuzhiyun 	if (ret)
303*4882a593Smuzhiyun 		return ret;
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 	if (!rescale->numerator || !rescale->denominator) {
306*4882a593Smuzhiyun 		dev_err(dev, "invalid scaling factor.\n");
307*4882a593Smuzhiyun 		return -EINVAL;
308*4882a593Smuzhiyun 	}
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 	platform_set_drvdata(pdev, indio_dev);
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun 	rescale->source = source;
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	indio_dev->name = dev_name(dev);
315*4882a593Smuzhiyun 	indio_dev->info = &rescale_info;
316*4882a593Smuzhiyun 	indio_dev->modes = INDIO_DIRECT_MODE;
317*4882a593Smuzhiyun 	indio_dev->channels = &rescale->chan;
318*4882a593Smuzhiyun 	indio_dev->num_channels = 1;
319*4882a593Smuzhiyun 	if (sizeof_ext_info) {
320*4882a593Smuzhiyun 		rescale->ext_info = devm_kmemdup(dev,
321*4882a593Smuzhiyun 						 source->channel->ext_info,
322*4882a593Smuzhiyun 						 sizeof_ext_info, GFP_KERNEL);
323*4882a593Smuzhiyun 		if (!rescale->ext_info)
324*4882a593Smuzhiyun 			return -ENOMEM;
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 		for (i = 0; rescale->ext_info[i].name; ++i) {
327*4882a593Smuzhiyun 			struct iio_chan_spec_ext_info *ext_info =
328*4882a593Smuzhiyun 				&rescale->ext_info[i];
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun 			if (source->channel->ext_info[i].read)
331*4882a593Smuzhiyun 				ext_info->read = rescale_read_ext_info;
332*4882a593Smuzhiyun 			if (source->channel->ext_info[i].write)
333*4882a593Smuzhiyun 				ext_info->write = rescale_write_ext_info;
334*4882a593Smuzhiyun 			ext_info->private = i;
335*4882a593Smuzhiyun 		}
336*4882a593Smuzhiyun 	}
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun 	ret = rescale_configure_channel(dev, rescale);
339*4882a593Smuzhiyun 	if (ret)
340*4882a593Smuzhiyun 		return ret;
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun 	return devm_iio_device_register(dev, indio_dev);
343*4882a593Smuzhiyun }
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun static struct platform_driver rescale_driver = {
346*4882a593Smuzhiyun 	.probe = rescale_probe,
347*4882a593Smuzhiyun 	.driver = {
348*4882a593Smuzhiyun 		.name = "iio-rescale",
349*4882a593Smuzhiyun 		.of_match_table = rescale_match,
350*4882a593Smuzhiyun 	},
351*4882a593Smuzhiyun };
352*4882a593Smuzhiyun module_platform_driver(rescale_driver);
353*4882a593Smuzhiyun 
354*4882a593Smuzhiyun MODULE_DESCRIPTION("IIO rescale driver");
355*4882a593Smuzhiyun MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
356*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
357