18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * mcp4922.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Driver for Microchip Digital to Analog Converters. 68c2ecf20Sopenharmony_ci * Supports MCP4902, MCP4912, and MCP4922. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (c) 2014 EMAC Inc. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 148c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 158c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 168c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 178c2ecf20Sopenharmony_ci#include <linux/bitops.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define MCP4922_NUM_CHANNELS 2 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cienum mcp4922_supported_device_ids { 228c2ecf20Sopenharmony_ci ID_MCP4902, 238c2ecf20Sopenharmony_ci ID_MCP4912, 248c2ecf20Sopenharmony_ci ID_MCP4922, 258c2ecf20Sopenharmony_ci}; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct mcp4922_state { 288c2ecf20Sopenharmony_ci struct spi_device *spi; 298c2ecf20Sopenharmony_ci unsigned int value[MCP4922_NUM_CHANNELS]; 308c2ecf20Sopenharmony_ci unsigned int vref_mv; 318c2ecf20Sopenharmony_ci struct regulator *vref_reg; 328c2ecf20Sopenharmony_ci u8 mosi[2] ____cacheline_aligned; 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define MCP4922_CHAN(chan, bits) { \ 368c2ecf20Sopenharmony_ci .type = IIO_VOLTAGE, \ 378c2ecf20Sopenharmony_ci .output = 1, \ 388c2ecf20Sopenharmony_ci .indexed = 1, \ 398c2ecf20Sopenharmony_ci .channel = chan, \ 408c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 418c2ecf20Sopenharmony_ci .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ 428c2ecf20Sopenharmony_ci .scan_type = { \ 438c2ecf20Sopenharmony_ci .sign = 'u', \ 448c2ecf20Sopenharmony_ci .realbits = (bits), \ 458c2ecf20Sopenharmony_ci .storagebits = 16, \ 468c2ecf20Sopenharmony_ci .shift = 12 - (bits), \ 478c2ecf20Sopenharmony_ci }, \ 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int mcp4922_spi_write(struct mcp4922_state *state, u8 addr, u32 val) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci state->mosi[1] = val & 0xff; 538c2ecf20Sopenharmony_ci state->mosi[0] = (addr == 0) ? 0x00 : 0x80; 548c2ecf20Sopenharmony_ci state->mosi[0] |= 0x30 | ((val >> 8) & 0x0f); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci return spi_write(state->spi, state->mosi, 2); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic int mcp4922_read_raw(struct iio_dev *indio_dev, 608c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 618c2ecf20Sopenharmony_ci int *val, 628c2ecf20Sopenharmony_ci int *val2, 638c2ecf20Sopenharmony_ci long mask) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct mcp4922_state *state = iio_priv(indio_dev); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci switch (mask) { 688c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 698c2ecf20Sopenharmony_ci *val = state->value[chan->channel]; 708c2ecf20Sopenharmony_ci return IIO_VAL_INT; 718c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 728c2ecf20Sopenharmony_ci *val = state->vref_mv; 738c2ecf20Sopenharmony_ci *val2 = chan->scan_type.realbits; 748c2ecf20Sopenharmony_ci return IIO_VAL_FRACTIONAL_LOG2; 758c2ecf20Sopenharmony_ci default: 768c2ecf20Sopenharmony_ci return -EINVAL; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic int mcp4922_write_raw(struct iio_dev *indio_dev, 818c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 828c2ecf20Sopenharmony_ci int val, 838c2ecf20Sopenharmony_ci int val2, 848c2ecf20Sopenharmony_ci long mask) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct mcp4922_state *state = iio_priv(indio_dev); 878c2ecf20Sopenharmony_ci int ret; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (val2 != 0) 908c2ecf20Sopenharmony_ci return -EINVAL; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci switch (mask) { 938c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 948c2ecf20Sopenharmony_ci if (val < 0 || val > GENMASK(chan->scan_type.realbits - 1, 0)) 958c2ecf20Sopenharmony_ci return -EINVAL; 968c2ecf20Sopenharmony_ci val <<= chan->scan_type.shift; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci ret = mcp4922_spi_write(state, chan->channel, val); 998c2ecf20Sopenharmony_ci if (!ret) 1008c2ecf20Sopenharmony_ci state->value[chan->channel] = val; 1018c2ecf20Sopenharmony_ci return ret; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci default: 1048c2ecf20Sopenharmony_ci return -EINVAL; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic const struct iio_chan_spec mcp4922_channels[3][MCP4922_NUM_CHANNELS] = { 1098c2ecf20Sopenharmony_ci [ID_MCP4902] = { MCP4922_CHAN(0, 8), MCP4922_CHAN(1, 8) }, 1108c2ecf20Sopenharmony_ci [ID_MCP4912] = { MCP4922_CHAN(0, 10), MCP4922_CHAN(1, 10) }, 1118c2ecf20Sopenharmony_ci [ID_MCP4922] = { MCP4922_CHAN(0, 12), MCP4922_CHAN(1, 12) }, 1128c2ecf20Sopenharmony_ci}; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic const struct iio_info mcp4922_info = { 1158c2ecf20Sopenharmony_ci .read_raw = &mcp4922_read_raw, 1168c2ecf20Sopenharmony_ci .write_raw = &mcp4922_write_raw, 1178c2ecf20Sopenharmony_ci}; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int mcp4922_probe(struct spi_device *spi) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 1228c2ecf20Sopenharmony_ci struct mcp4922_state *state; 1238c2ecf20Sopenharmony_ci const struct spi_device_id *id; 1248c2ecf20Sopenharmony_ci int ret; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state)); 1278c2ecf20Sopenharmony_ci if (indio_dev == NULL) 1288c2ecf20Sopenharmony_ci return -ENOMEM; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci state = iio_priv(indio_dev); 1318c2ecf20Sopenharmony_ci state->spi = spi; 1328c2ecf20Sopenharmony_ci state->vref_reg = devm_regulator_get(&spi->dev, "vref"); 1338c2ecf20Sopenharmony_ci if (IS_ERR(state->vref_reg)) { 1348c2ecf20Sopenharmony_ci dev_err(&spi->dev, "Vref regulator not specified\n"); 1358c2ecf20Sopenharmony_ci return PTR_ERR(state->vref_reg); 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci ret = regulator_enable(state->vref_reg); 1398c2ecf20Sopenharmony_ci if (ret) { 1408c2ecf20Sopenharmony_ci dev_err(&spi->dev, "Failed to enable vref regulator: %d\n", 1418c2ecf20Sopenharmony_ci ret); 1428c2ecf20Sopenharmony_ci return ret; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci ret = regulator_get_voltage(state->vref_reg); 1468c2ecf20Sopenharmony_ci if (ret < 0) { 1478c2ecf20Sopenharmony_ci dev_err(&spi->dev, "Failed to read vref regulator: %d\n", 1488c2ecf20Sopenharmony_ci ret); 1498c2ecf20Sopenharmony_ci goto error_disable_reg; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci state->vref_mv = ret / 1000; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci spi_set_drvdata(spi, indio_dev); 1548c2ecf20Sopenharmony_ci id = spi_get_device_id(spi); 1558c2ecf20Sopenharmony_ci indio_dev->info = &mcp4922_info; 1568c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 1578c2ecf20Sopenharmony_ci indio_dev->channels = mcp4922_channels[id->driver_data]; 1588c2ecf20Sopenharmony_ci indio_dev->num_channels = MCP4922_NUM_CHANNELS; 1598c2ecf20Sopenharmony_ci indio_dev->name = id->name; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci ret = iio_device_register(indio_dev); 1628c2ecf20Sopenharmony_ci if (ret) { 1638c2ecf20Sopenharmony_ci dev_err(&spi->dev, "Failed to register iio device: %d\n", 1648c2ecf20Sopenharmony_ci ret); 1658c2ecf20Sopenharmony_ci goto error_disable_reg; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return 0; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cierror_disable_reg: 1718c2ecf20Sopenharmony_ci regulator_disable(state->vref_reg); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return ret; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic int mcp4922_remove(struct spi_device *spi) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = spi_get_drvdata(spi); 1798c2ecf20Sopenharmony_ci struct mcp4922_state *state; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci iio_device_unregister(indio_dev); 1828c2ecf20Sopenharmony_ci state = iio_priv(indio_dev); 1838c2ecf20Sopenharmony_ci regulator_disable(state->vref_reg); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci return 0; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic const struct spi_device_id mcp4922_id[] = { 1898c2ecf20Sopenharmony_ci {"mcp4902", ID_MCP4902}, 1908c2ecf20Sopenharmony_ci {"mcp4912", ID_MCP4912}, 1918c2ecf20Sopenharmony_ci {"mcp4922", ID_MCP4922}, 1928c2ecf20Sopenharmony_ci {} 1938c2ecf20Sopenharmony_ci}; 1948c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(spi, mcp4922_id); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic struct spi_driver mcp4922_driver = { 1978c2ecf20Sopenharmony_ci .driver = { 1988c2ecf20Sopenharmony_ci .name = "mcp4922", 1998c2ecf20Sopenharmony_ci }, 2008c2ecf20Sopenharmony_ci .probe = mcp4922_probe, 2018c2ecf20Sopenharmony_ci .remove = mcp4922_remove, 2028c2ecf20Sopenharmony_ci .id_table = mcp4922_id, 2038c2ecf20Sopenharmony_ci}; 2048c2ecf20Sopenharmony_cimodule_spi_driver(mcp4922_driver); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michael Welling <mwelling@ieee.org>"); 2078c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Microchip MCP4902, MCP4912, MCP4922 DAC"); 2088c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 209