iio-rescale.c 8.6 KB

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