mcp41010.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Industrial I/O driver for Microchip digital potentiometers
  4. *
  5. * Copyright (c) 2018 Chris Coffey <cmc@babblebit.net>
  6. * Based on: Slawomir Stepien's code from mcp4131.c
  7. *
  8. * Datasheet: https://ww1.microchip.com/downloads/en/devicedoc/11195c.pdf
  9. *
  10. * DEVID #Wipers #Positions Resistance (kOhm)
  11. * mcp41010 1 256 10
  12. * mcp41050 1 256 50
  13. * mcp41100 1 256 100
  14. * mcp42010 2 256 10
  15. * mcp42050 2 256 50
  16. * mcp42100 2 256 100
  17. */
  18. #include <linux/cache.h>
  19. #include <linux/err.h>
  20. #include <linux/iio/iio.h>
  21. #include <linux/iio/types.h>
  22. #include <linux/module.h>
  23. #include <linux/mutex.h>
  24. #include <linux/of.h>
  25. #include <linux/of_device.h>
  26. #include <linux/spi/spi.h>
  27. #define MCP41010_MAX_WIPERS 2
  28. #define MCP41010_WRITE BIT(4)
  29. #define MCP41010_WIPER_MAX 255
  30. #define MCP41010_WIPER_CHANNEL BIT(0)
  31. struct mcp41010_cfg {
  32. char name[16];
  33. int wipers;
  34. int kohms;
  35. };
  36. enum mcp41010_type {
  37. MCP41010,
  38. MCP41050,
  39. MCP41100,
  40. MCP42010,
  41. MCP42050,
  42. MCP42100,
  43. };
  44. static const struct mcp41010_cfg mcp41010_cfg[] = {
  45. [MCP41010] = { .name = "mcp41010", .wipers = 1, .kohms = 10, },
  46. [MCP41050] = { .name = "mcp41050", .wipers = 1, .kohms = 50, },
  47. [MCP41100] = { .name = "mcp41100", .wipers = 1, .kohms = 100, },
  48. [MCP42010] = { .name = "mcp42010", .wipers = 2, .kohms = 10, },
  49. [MCP42050] = { .name = "mcp42050", .wipers = 2, .kohms = 50, },
  50. [MCP42100] = { .name = "mcp42100", .wipers = 2, .kohms = 100, },
  51. };
  52. struct mcp41010_data {
  53. struct spi_device *spi;
  54. const struct mcp41010_cfg *cfg;
  55. struct mutex lock; /* Protect write sequences */
  56. unsigned int value[MCP41010_MAX_WIPERS]; /* Cache wiper values */
  57. u8 buf[2] ____cacheline_aligned;
  58. };
  59. #define MCP41010_CHANNEL(ch) { \
  60. .type = IIO_RESISTANCE, \
  61. .indexed = 1, \
  62. .output = 1, \
  63. .channel = (ch), \
  64. .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
  65. .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
  66. }
  67. static const struct iio_chan_spec mcp41010_channels[] = {
  68. MCP41010_CHANNEL(0),
  69. MCP41010_CHANNEL(1),
  70. };
  71. static int mcp41010_read_raw(struct iio_dev *indio_dev,
  72. struct iio_chan_spec const *chan,
  73. int *val, int *val2, long mask)
  74. {
  75. struct mcp41010_data *data = iio_priv(indio_dev);
  76. int channel = chan->channel;
  77. switch (mask) {
  78. case IIO_CHAN_INFO_RAW:
  79. *val = data->value[channel];
  80. return IIO_VAL_INT;
  81. case IIO_CHAN_INFO_SCALE:
  82. *val = 1000 * data->cfg->kohms;
  83. *val2 = MCP41010_WIPER_MAX;
  84. return IIO_VAL_FRACTIONAL;
  85. }
  86. return -EINVAL;
  87. }
  88. static int mcp41010_write_raw(struct iio_dev *indio_dev,
  89. struct iio_chan_spec const *chan,
  90. int val, int val2, long mask)
  91. {
  92. int err;
  93. struct mcp41010_data *data = iio_priv(indio_dev);
  94. int channel = chan->channel;
  95. if (mask != IIO_CHAN_INFO_RAW)
  96. return -EINVAL;
  97. if (val > MCP41010_WIPER_MAX || val < 0)
  98. return -EINVAL;
  99. mutex_lock(&data->lock);
  100. data->buf[0] = MCP41010_WIPER_CHANNEL << channel;
  101. data->buf[0] |= MCP41010_WRITE;
  102. data->buf[1] = val & 0xff;
  103. err = spi_write(data->spi, data->buf, sizeof(data->buf));
  104. if (!err)
  105. data->value[channel] = val;
  106. mutex_unlock(&data->lock);
  107. return err;
  108. }
  109. static const struct iio_info mcp41010_info = {
  110. .read_raw = mcp41010_read_raw,
  111. .write_raw = mcp41010_write_raw,
  112. };
  113. static int mcp41010_probe(struct spi_device *spi)
  114. {
  115. int err;
  116. struct device *dev = &spi->dev;
  117. struct mcp41010_data *data;
  118. struct iio_dev *indio_dev;
  119. indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
  120. if (!indio_dev)
  121. return -ENOMEM;
  122. data = iio_priv(indio_dev);
  123. spi_set_drvdata(spi, indio_dev);
  124. data->spi = spi;
  125. data->cfg = of_device_get_match_data(&spi->dev);
  126. if (!data->cfg)
  127. data->cfg = &mcp41010_cfg[spi_get_device_id(spi)->driver_data];
  128. mutex_init(&data->lock);
  129. indio_dev->info = &mcp41010_info;
  130. indio_dev->channels = mcp41010_channels;
  131. indio_dev->num_channels = data->cfg->wipers;
  132. indio_dev->name = data->cfg->name;
  133. err = devm_iio_device_register(dev, indio_dev);
  134. if (err)
  135. dev_info(&spi->dev, "Unable to register %s\n", indio_dev->name);
  136. return err;
  137. }
  138. static const struct of_device_id mcp41010_match[] = {
  139. { .compatible = "microchip,mcp41010", .data = &mcp41010_cfg[MCP41010] },
  140. { .compatible = "microchip,mcp41050", .data = &mcp41010_cfg[MCP41050] },
  141. { .compatible = "microchip,mcp41100", .data = &mcp41010_cfg[MCP41100] },
  142. { .compatible = "microchip,mcp42010", .data = &mcp41010_cfg[MCP42010] },
  143. { .compatible = "microchip,mcp42050", .data = &mcp41010_cfg[MCP42050] },
  144. { .compatible = "microchip,mcp42100", .data = &mcp41010_cfg[MCP42100] },
  145. {}
  146. };
  147. MODULE_DEVICE_TABLE(of, mcp41010_match);
  148. static const struct spi_device_id mcp41010_id[] = {
  149. { "mcp41010", MCP41010 },
  150. { "mcp41050", MCP41050 },
  151. { "mcp41100", MCP41100 },
  152. { "mcp42010", MCP42010 },
  153. { "mcp42050", MCP42050 },
  154. { "mcp42100", MCP42100 },
  155. {}
  156. };
  157. MODULE_DEVICE_TABLE(spi, mcp41010_id);
  158. static struct spi_driver mcp41010_driver = {
  159. .driver = {
  160. .name = "mcp41010",
  161. .of_match_table = mcp41010_match,
  162. },
  163. .probe = mcp41010_probe,
  164. .id_table = mcp41010_id,
  165. };
  166. module_spi_driver(mcp41010_driver);
  167. MODULE_AUTHOR("Chris Coffey <cmc@babblebit.net>");
  168. MODULE_DESCRIPTION("MCP41010 digital potentiometer");
  169. MODULE_LICENSE("GPL v2");