493 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Industrial I/O driver for Microchip digital potentiometers4 *5 * Copyright (c) 2016 Slawomir Stepien6 * Based on: Peter Rosin's code from mcp4531.c7 *8 * Datasheet: https://ww1.microchip.com/downloads/en/DeviceDoc/22060b.pdf9 *10 * DEVID #Wipers #Positions Resistor Opts (kOhm)11 * mcp4131 1 129 5, 10, 50, 10012 * mcp4132 1 129 5, 10, 50, 10013 * mcp4141 1 129 5, 10, 50, 10014 * mcp4142 1 129 5, 10, 50, 10015 * mcp4151 1 257 5, 10, 50, 10016 * mcp4152 1 257 5, 10, 50, 10017 * mcp4161 1 257 5, 10, 50, 10018 * mcp4162 1 257 5, 10, 50, 10019 * mcp4231 2 129 5, 10, 50, 10020 * mcp4232 2 129 5, 10, 50, 10021 * mcp4241 2 129 5, 10, 50, 10022 * mcp4242 2 129 5, 10, 50, 10023 * mcp4251 2 257 5, 10, 50, 10024 * mcp4252 2 257 5, 10, 50, 10025 * mcp4261 2 257 5, 10, 50, 10026 * mcp4262 2 257 5, 10, 50, 10027 */28 29/*30 * TODO:31 * 1. Write wiper setting to EEPROM for EEPROM capable models.32 */33 34#include <linux/cache.h>35#include <linux/err.h>36#include <linux/export.h>37#include <linux/iio/iio.h>38#include <linux/iio/types.h>39#include <linux/module.h>40#include <linux/mod_devicetable.h>41#include <linux/mutex.h>42#include <linux/property.h>43#include <linux/spi/spi.h>44 45#define MCP4131_WRITE (0x00 << 2)46#define MCP4131_READ (0x03 << 2)47 48#define MCP4131_WIPER_SHIFT 449#define MCP4131_CMDERR(r) ((r[0]) & 0x02)50#define MCP4131_RAW(r) ((r[0]) == 0xff ? 0x100 : (r[1]))51 52struct mcp4131_cfg {53 int wipers;54 int max_pos;55 int kohms;56};57 58enum mcp4131_type {59 MCP413x_502 = 0,60 MCP413x_103,61 MCP413x_503,62 MCP413x_104,63 MCP414x_502,64 MCP414x_103,65 MCP414x_503,66 MCP414x_104,67 MCP415x_502,68 MCP415x_103,69 MCP415x_503,70 MCP415x_104,71 MCP416x_502,72 MCP416x_103,73 MCP416x_503,74 MCP416x_104,75 MCP423x_502,76 MCP423x_103,77 MCP423x_503,78 MCP423x_104,79 MCP424x_502,80 MCP424x_103,81 MCP424x_503,82 MCP424x_104,83 MCP425x_502,84 MCP425x_103,85 MCP425x_503,86 MCP425x_104,87 MCP426x_502,88 MCP426x_103,89 MCP426x_503,90 MCP426x_104,91};92 93static const struct mcp4131_cfg mcp4131_cfg[] = {94 [MCP413x_502] = { .wipers = 1, .max_pos = 128, .kohms = 5, },95 [MCP413x_103] = { .wipers = 1, .max_pos = 128, .kohms = 10, },96 [MCP413x_503] = { .wipers = 1, .max_pos = 128, .kohms = 50, },97 [MCP413x_104] = { .wipers = 1, .max_pos = 128, .kohms = 100, },98 [MCP414x_502] = { .wipers = 1, .max_pos = 128, .kohms = 5, },99 [MCP414x_103] = { .wipers = 1, .max_pos = 128, .kohms = 10, },100 [MCP414x_503] = { .wipers = 1, .max_pos = 128, .kohms = 50, },101 [MCP414x_104] = { .wipers = 1, .max_pos = 128, .kohms = 100, },102 [MCP415x_502] = { .wipers = 1, .max_pos = 256, .kohms = 5, },103 [MCP415x_103] = { .wipers = 1, .max_pos = 256, .kohms = 10, },104 [MCP415x_503] = { .wipers = 1, .max_pos = 256, .kohms = 50, },105 [MCP415x_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, },106 [MCP416x_502] = { .wipers = 1, .max_pos = 256, .kohms = 5, },107 [MCP416x_103] = { .wipers = 1, .max_pos = 256, .kohms = 10, },108 [MCP416x_503] = { .wipers = 1, .max_pos = 256, .kohms = 50, },109 [MCP416x_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, },110 [MCP423x_502] = { .wipers = 2, .max_pos = 128, .kohms = 5, },111 [MCP423x_103] = { .wipers = 2, .max_pos = 128, .kohms = 10, },112 [MCP423x_503] = { .wipers = 2, .max_pos = 128, .kohms = 50, },113 [MCP423x_104] = { .wipers = 2, .max_pos = 128, .kohms = 100, },114 [MCP424x_502] = { .wipers = 2, .max_pos = 128, .kohms = 5, },115 [MCP424x_103] = { .wipers = 2, .max_pos = 128, .kohms = 10, },116 [MCP424x_503] = { .wipers = 2, .max_pos = 128, .kohms = 50, },117 [MCP424x_104] = { .wipers = 2, .max_pos = 128, .kohms = 100, },118 [MCP425x_502] = { .wipers = 2, .max_pos = 256, .kohms = 5, },119 [MCP425x_103] = { .wipers = 2, .max_pos = 256, .kohms = 10, },120 [MCP425x_503] = { .wipers = 2, .max_pos = 256, .kohms = 50, },121 [MCP425x_104] = { .wipers = 2, .max_pos = 256, .kohms = 100, },122 [MCP426x_502] = { .wipers = 2, .max_pos = 256, .kohms = 5, },123 [MCP426x_103] = { .wipers = 2, .max_pos = 256, .kohms = 10, },124 [MCP426x_503] = { .wipers = 2, .max_pos = 256, .kohms = 50, },125 [MCP426x_104] = { .wipers = 2, .max_pos = 256, .kohms = 100, },126};127 128struct mcp4131_data {129 struct spi_device *spi;130 const struct mcp4131_cfg *cfg;131 struct mutex lock;132 u8 buf[2] __aligned(IIO_DMA_MINALIGN);133};134 135#define MCP4131_CHANNEL(ch) { \136 .type = IIO_RESISTANCE, \137 .indexed = 1, \138 .output = 1, \139 .channel = (ch), \140 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \141 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \142}143 144static const struct iio_chan_spec mcp4131_channels[] = {145 MCP4131_CHANNEL(0),146 MCP4131_CHANNEL(1),147};148 149static int mcp4131_read(struct spi_device *spi, void *buf, size_t len)150{151 struct spi_transfer t = {152 .tx_buf = buf, /* We need to send addr, cmd and 12 bits */153 .rx_buf = buf,154 .len = len,155 };156 struct spi_message m;157 158 spi_message_init(&m);159 spi_message_add_tail(&t, &m);160 161 return spi_sync(spi, &m);162}163 164static int mcp4131_read_raw(struct iio_dev *indio_dev,165 struct iio_chan_spec const *chan,166 int *val, int *val2, long mask)167{168 int err;169 struct mcp4131_data *data = iio_priv(indio_dev);170 int address = chan->channel;171 172 switch (mask) {173 case IIO_CHAN_INFO_RAW:174 mutex_lock(&data->lock);175 176 data->buf[0] = (address << MCP4131_WIPER_SHIFT) | MCP4131_READ;177 data->buf[1] = 0;178 179 err = mcp4131_read(data->spi, data->buf, 2);180 if (err) {181 mutex_unlock(&data->lock);182 return err;183 }184 185 /* Error, bad address/command combination */186 if (!MCP4131_CMDERR(data->buf)) {187 mutex_unlock(&data->lock);188 return -EIO;189 }190 191 *val = MCP4131_RAW(data->buf);192 mutex_unlock(&data->lock);193 194 return IIO_VAL_INT;195 196 case IIO_CHAN_INFO_SCALE:197 *val = 1000 * data->cfg->kohms;198 *val2 = data->cfg->max_pos;199 return IIO_VAL_FRACTIONAL;200 }201 202 return -EINVAL;203}204 205static int mcp4131_write_raw(struct iio_dev *indio_dev,206 struct iio_chan_spec const *chan,207 int val, int val2, long mask)208{209 int err;210 struct mcp4131_data *data = iio_priv(indio_dev);211 int address = chan->channel << MCP4131_WIPER_SHIFT;212 213 switch (mask) {214 case IIO_CHAN_INFO_RAW:215 if (val > data->cfg->max_pos || val < 0)216 return -EINVAL;217 break;218 219 default:220 return -EINVAL;221 }222 223 mutex_lock(&data->lock);224 225 data->buf[0] = address << MCP4131_WIPER_SHIFT;226 data->buf[0] |= MCP4131_WRITE | (val >> 8);227 data->buf[1] = val & 0xFF; /* 8 bits here */228 229 err = spi_write(data->spi, data->buf, 2);230 mutex_unlock(&data->lock);231 232 return err;233}234 235static const struct iio_info mcp4131_info = {236 .read_raw = mcp4131_read_raw,237 .write_raw = mcp4131_write_raw,238};239 240static int mcp4131_probe(struct spi_device *spi)241{242 int err;243 struct device *dev = &spi->dev;244 unsigned long devid;245 struct mcp4131_data *data;246 struct iio_dev *indio_dev;247 248 indio_dev = devm_iio_device_alloc(dev, sizeof(*data));249 if (!indio_dev)250 return -ENOMEM;251 252 data = iio_priv(indio_dev);253 spi_set_drvdata(spi, indio_dev);254 data->spi = spi;255 data->cfg = device_get_match_data(&spi->dev);256 if (!data->cfg) {257 devid = spi_get_device_id(spi)->driver_data;258 data->cfg = &mcp4131_cfg[devid];259 }260 261 mutex_init(&data->lock);262 263 indio_dev->info = &mcp4131_info;264 indio_dev->channels = mcp4131_channels;265 indio_dev->num_channels = data->cfg->wipers;266 indio_dev->name = spi_get_device_id(spi)->name;267 268 err = devm_iio_device_register(dev, indio_dev);269 if (err) {270 dev_info(&spi->dev, "Unable to register %s\n", indio_dev->name);271 return err;272 }273 274 return 0;275}276 277static const struct of_device_id mcp4131_dt_ids[] = {278 { .compatible = "microchip,mcp4131-502",279 .data = &mcp4131_cfg[MCP413x_502] },280 { .compatible = "microchip,mcp4131-103",281 .data = &mcp4131_cfg[MCP413x_103] },282 { .compatible = "microchip,mcp4131-503",283 .data = &mcp4131_cfg[MCP413x_503] },284 { .compatible = "microchip,mcp4131-104",285 .data = &mcp4131_cfg[MCP413x_104] },286 { .compatible = "microchip,mcp4132-502",287 .data = &mcp4131_cfg[MCP413x_502] },288 { .compatible = "microchip,mcp4132-103",289 .data = &mcp4131_cfg[MCP413x_103] },290 { .compatible = "microchip,mcp4132-503",291 .data = &mcp4131_cfg[MCP413x_503] },292 { .compatible = "microchip,mcp4132-104",293 .data = &mcp4131_cfg[MCP413x_104] },294 { .compatible = "microchip,mcp4141-502",295 .data = &mcp4131_cfg[MCP414x_502] },296 { .compatible = "microchip,mcp4141-103",297 .data = &mcp4131_cfg[MCP414x_103] },298 { .compatible = "microchip,mcp4141-503",299 .data = &mcp4131_cfg[MCP414x_503] },300 { .compatible = "microchip,mcp4141-104",301 .data = &mcp4131_cfg[MCP414x_104] },302 { .compatible = "microchip,mcp4142-502",303 .data = &mcp4131_cfg[MCP414x_502] },304 { .compatible = "microchip,mcp4142-103",305 .data = &mcp4131_cfg[MCP414x_103] },306 { .compatible = "microchip,mcp4142-503",307 .data = &mcp4131_cfg[MCP414x_503] },308 { .compatible = "microchip,mcp4142-104",309 .data = &mcp4131_cfg[MCP414x_104] },310 { .compatible = "microchip,mcp4151-502",311 .data = &mcp4131_cfg[MCP415x_502] },312 { .compatible = "microchip,mcp4151-103",313 .data = &mcp4131_cfg[MCP415x_103] },314 { .compatible = "microchip,mcp4151-503",315 .data = &mcp4131_cfg[MCP415x_503] },316 { .compatible = "microchip,mcp4151-104",317 .data = &mcp4131_cfg[MCP415x_104] },318 { .compatible = "microchip,mcp4152-502",319 .data = &mcp4131_cfg[MCP415x_502] },320 { .compatible = "microchip,mcp4152-103",321 .data = &mcp4131_cfg[MCP415x_103] },322 { .compatible = "microchip,mcp4152-503",323 .data = &mcp4131_cfg[MCP415x_503] },324 { .compatible = "microchip,mcp4152-104",325 .data = &mcp4131_cfg[MCP415x_104] },326 { .compatible = "microchip,mcp4161-502",327 .data = &mcp4131_cfg[MCP416x_502] },328 { .compatible = "microchip,mcp4161-103",329 .data = &mcp4131_cfg[MCP416x_103] },330 { .compatible = "microchip,mcp4161-503",331 .data = &mcp4131_cfg[MCP416x_503] },332 { .compatible = "microchip,mcp4161-104",333 .data = &mcp4131_cfg[MCP416x_104] },334 { .compatible = "microchip,mcp4162-502",335 .data = &mcp4131_cfg[MCP416x_502] },336 { .compatible = "microchip,mcp4162-103",337 .data = &mcp4131_cfg[MCP416x_103] },338 { .compatible = "microchip,mcp4162-503",339 .data = &mcp4131_cfg[MCP416x_503] },340 { .compatible = "microchip,mcp4162-104",341 .data = &mcp4131_cfg[MCP416x_104] },342 { .compatible = "microchip,mcp4231-502",343 .data = &mcp4131_cfg[MCP423x_502] },344 { .compatible = "microchip,mcp4231-103",345 .data = &mcp4131_cfg[MCP423x_103] },346 { .compatible = "microchip,mcp4231-503",347 .data = &mcp4131_cfg[MCP423x_503] },348 { .compatible = "microchip,mcp4231-104",349 .data = &mcp4131_cfg[MCP423x_104] },350 { .compatible = "microchip,mcp4232-502",351 .data = &mcp4131_cfg[MCP423x_502] },352 { .compatible = "microchip,mcp4232-103",353 .data = &mcp4131_cfg[MCP423x_103] },354 { .compatible = "microchip,mcp4232-503",355 .data = &mcp4131_cfg[MCP423x_503] },356 { .compatible = "microchip,mcp4232-104",357 .data = &mcp4131_cfg[MCP423x_104] },358 { .compatible = "microchip,mcp4241-502",359 .data = &mcp4131_cfg[MCP424x_502] },360 { .compatible = "microchip,mcp4241-103",361 .data = &mcp4131_cfg[MCP424x_103] },362 { .compatible = "microchip,mcp4241-503",363 .data = &mcp4131_cfg[MCP424x_503] },364 { .compatible = "microchip,mcp4241-104",365 .data = &mcp4131_cfg[MCP424x_104] },366 { .compatible = "microchip,mcp4242-502",367 .data = &mcp4131_cfg[MCP424x_502] },368 { .compatible = "microchip,mcp4242-103",369 .data = &mcp4131_cfg[MCP424x_103] },370 { .compatible = "microchip,mcp4242-503",371 .data = &mcp4131_cfg[MCP424x_503] },372 { .compatible = "microchip,mcp4242-104",373 .data = &mcp4131_cfg[MCP424x_104] },374 { .compatible = "microchip,mcp4251-502",375 .data = &mcp4131_cfg[MCP425x_502] },376 { .compatible = "microchip,mcp4251-103",377 .data = &mcp4131_cfg[MCP425x_103] },378 { .compatible = "microchip,mcp4251-503",379 .data = &mcp4131_cfg[MCP425x_503] },380 { .compatible = "microchip,mcp4251-104",381 .data = &mcp4131_cfg[MCP425x_104] },382 { .compatible = "microchip,mcp4252-502",383 .data = &mcp4131_cfg[MCP425x_502] },384 { .compatible = "microchip,mcp4252-103",385 .data = &mcp4131_cfg[MCP425x_103] },386 { .compatible = "microchip,mcp4252-503",387 .data = &mcp4131_cfg[MCP425x_503] },388 { .compatible = "microchip,mcp4252-104",389 .data = &mcp4131_cfg[MCP425x_104] },390 { .compatible = "microchip,mcp4261-502",391 .data = &mcp4131_cfg[MCP426x_502] },392 { .compatible = "microchip,mcp4261-103",393 .data = &mcp4131_cfg[MCP426x_103] },394 { .compatible = "microchip,mcp4261-503",395 .data = &mcp4131_cfg[MCP426x_503] },396 { .compatible = "microchip,mcp4261-104",397 .data = &mcp4131_cfg[MCP426x_104] },398 { .compatible = "microchip,mcp4262-502",399 .data = &mcp4131_cfg[MCP426x_502] },400 { .compatible = "microchip,mcp4262-103",401 .data = &mcp4131_cfg[MCP426x_103] },402 { .compatible = "microchip,mcp4262-503",403 .data = &mcp4131_cfg[MCP426x_503] },404 { .compatible = "microchip,mcp4262-104",405 .data = &mcp4131_cfg[MCP426x_104] },406 {}407};408MODULE_DEVICE_TABLE(of, mcp4131_dt_ids);409 410static const struct spi_device_id mcp4131_id[] = {411 { "mcp4131-502", MCP413x_502 },412 { "mcp4131-103", MCP413x_103 },413 { "mcp4131-503", MCP413x_503 },414 { "mcp4131-104", MCP413x_104 },415 { "mcp4132-502", MCP413x_502 },416 { "mcp4132-103", MCP413x_103 },417 { "mcp4132-503", MCP413x_503 },418 { "mcp4132-104", MCP413x_104 },419 { "mcp4141-502", MCP414x_502 },420 { "mcp4141-103", MCP414x_103 },421 { "mcp4141-503", MCP414x_503 },422 { "mcp4141-104", MCP414x_104 },423 { "mcp4142-502", MCP414x_502 },424 { "mcp4142-103", MCP414x_103 },425 { "mcp4142-503", MCP414x_503 },426 { "mcp4142-104", MCP414x_104 },427 { "mcp4151-502", MCP415x_502 },428 { "mcp4151-103", MCP415x_103 },429 { "mcp4151-503", MCP415x_503 },430 { "mcp4151-104", MCP415x_104 },431 { "mcp4152-502", MCP415x_502 },432 { "mcp4152-103", MCP415x_103 },433 { "mcp4152-503", MCP415x_503 },434 { "mcp4152-104", MCP415x_104 },435 { "mcp4161-502", MCP416x_502 },436 { "mcp4161-103", MCP416x_103 },437 { "mcp4161-503", MCP416x_503 },438 { "mcp4161-104", MCP416x_104 },439 { "mcp4162-502", MCP416x_502 },440 { "mcp4162-103", MCP416x_103 },441 { "mcp4162-503", MCP416x_503 },442 { "mcp4162-104", MCP416x_104 },443 { "mcp4231-502", MCP423x_502 },444 { "mcp4231-103", MCP423x_103 },445 { "mcp4231-503", MCP423x_503 },446 { "mcp4231-104", MCP423x_104 },447 { "mcp4232-502", MCP423x_502 },448 { "mcp4232-103", MCP423x_103 },449 { "mcp4232-503", MCP423x_503 },450 { "mcp4232-104", MCP423x_104 },451 { "mcp4241-502", MCP424x_502 },452 { "mcp4241-103", MCP424x_103 },453 { "mcp4241-503", MCP424x_503 },454 { "mcp4241-104", MCP424x_104 },455 { "mcp4242-502", MCP424x_502 },456 { "mcp4242-103", MCP424x_103 },457 { "mcp4242-503", MCP424x_503 },458 { "mcp4242-104", MCP424x_104 },459 { "mcp4251-502", MCP425x_502 },460 { "mcp4251-103", MCP425x_103 },461 { "mcp4251-503", MCP425x_503 },462 { "mcp4251-104", MCP425x_104 },463 { "mcp4252-502", MCP425x_502 },464 { "mcp4252-103", MCP425x_103 },465 { "mcp4252-503", MCP425x_503 },466 { "mcp4252-104", MCP425x_104 },467 { "mcp4261-502", MCP426x_502 },468 { "mcp4261-103", MCP426x_103 },469 { "mcp4261-503", MCP426x_503 },470 { "mcp4261-104", MCP426x_104 },471 { "mcp4262-502", MCP426x_502 },472 { "mcp4262-103", MCP426x_103 },473 { "mcp4262-503", MCP426x_503 },474 { "mcp4262-104", MCP426x_104 },475 {}476};477MODULE_DEVICE_TABLE(spi, mcp4131_id);478 479static struct spi_driver mcp4131_driver = {480 .driver = {481 .name = "mcp4131",482 .of_match_table = mcp4131_dt_ids,483 },484 .probe = mcp4131_probe,485 .id_table = mcp4131_id,486};487 488module_spi_driver(mcp4131_driver);489 490MODULE_AUTHOR("Slawomir Stepien <sst@poczta.fm>");491MODULE_DESCRIPTION("MCP4131 digital potentiometer");492MODULE_LICENSE("GPL v2");493