1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Microchip MCP47A1 DAC driver 4 * 5 * Copyright (c) 2026 Joshua Crofts <joshua.crofts1@gmail.com> 6 * 7 * Datasheet: https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/25154A.pdf 8 */ 9 10 #include <linux/delay.h> 11 #include <linux/err.h> 12 #include <linux/i2c.h> 13 #include <linux/minmax.h> 14 #include <linux/module.h> 15 #include <linux/regulator/consumer.h> 16 #include <linux/types.h> 17 #include <linux/units.h> 18 19 #include <linux/iio/iio.h> 20 21 #define MCP47A1_CMD_CODE 0x00 22 #define MCP47A1_MAX_STEPS 64 23 24 struct mcp47a1_data { 25 struct i2c_client *client; 26 int vref_mV; 27 }; 28 29 static const int mcp47a1_raw_avail[] = { 0, 1, MCP47A1_MAX_STEPS - 1 }; 30 31 static const struct iio_chan_spec mcp47a1_channel = { 32 .type = IIO_VOLTAGE, 33 .indexed = 1, 34 .output = 1, 35 .channel = 0, 36 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 37 .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), 38 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), 39 }; 40 41 static int mcp47a1_write(struct iio_dev *indio_dev, 42 struct iio_chan_spec const *chan, 43 int val, int val2, long mask) 44 { 45 struct mcp47a1_data *data = iio_priv(indio_dev); 46 47 switch (mask) { 48 case IIO_CHAN_INFO_RAW: 49 if (!in_range(val, 0, MCP47A1_MAX_STEPS)) 50 return -EINVAL; 51 52 return i2c_smbus_write_byte_data(data->client, MCP47A1_CMD_CODE, 53 val); 54 default: 55 return -EINVAL; 56 } 57 } 58 59 static int mcp47a1_read(struct iio_dev *indio_dev, 60 struct iio_chan_spec const *chan, 61 int *val, int *val2, long mask) 62 { 63 struct mcp47a1_data *data = iio_priv(indio_dev); 64 int ret; 65 66 switch (mask) { 67 case IIO_CHAN_INFO_RAW: 68 ret = i2c_smbus_read_byte_data(data->client, MCP47A1_CMD_CODE); 69 if (ret < 0) 70 return ret; 71 72 *val = ret; 73 74 return IIO_VAL_INT; 75 case IIO_CHAN_INFO_SCALE: 76 *val = data->vref_mV; 77 *val2 = MCP47A1_MAX_STEPS; 78 79 return IIO_VAL_FRACTIONAL; 80 default: 81 return -EINVAL; 82 } 83 } 84 85 static int mcp47a1_read_avail(struct iio_dev *indio_dev, 86 struct iio_chan_spec const *chan, 87 const int **vals, int *type, int *length, 88 long mask) 89 { 90 switch (mask) { 91 case IIO_CHAN_INFO_RAW: 92 *vals = mcp47a1_raw_avail; 93 *type = IIO_VAL_INT; 94 return IIO_AVAIL_RANGE; 95 default: 96 return -EINVAL; 97 } 98 } 99 100 static const struct iio_info mcp47a1_info = { 101 .write_raw = mcp47a1_write, 102 .read_raw = mcp47a1_read, 103 .read_avail = mcp47a1_read_avail, 104 }; 105 106 static int mcp47a1_probe(struct i2c_client *client) 107 { 108 struct device *dev = &client->dev; 109 struct mcp47a1_data *data; 110 struct iio_dev *indio_dev; 111 int ret; 112 113 indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); 114 if (!indio_dev) 115 return -ENOMEM; 116 117 data = iio_priv(indio_dev); 118 data->client = client; 119 120 ret = devm_regulator_get_enable(dev, "vdd"); 121 if (ret) 122 return dev_err_probe(dev, ret, "Failed to enable regulator\n"); 123 124 /* Delay after device exits reset state (see AC/DC characteristics) */ 125 fsleep(20); 126 127 ret = devm_regulator_get_enable_read_voltage(dev, "vref"); 128 if (ret < 0) 129 return dev_err_probe(dev, ret, "Failed to read vref\n"); 130 131 data->vref_mV = ret / (MICRO / MILLI); 132 133 indio_dev->name = "mcp47a1"; 134 indio_dev->modes = INDIO_DIRECT_MODE; 135 indio_dev->info = &mcp47a1_info; 136 indio_dev->channels = &mcp47a1_channel; 137 indio_dev->num_channels = 1; 138 139 return devm_iio_device_register(dev, indio_dev); 140 } 141 142 static const struct of_device_id mcp47a1_of_match[] = { 143 { .compatible = "microchip,mcp47a1" }, 144 { } 145 }; 146 MODULE_DEVICE_TABLE(of, mcp47a1_of_match); 147 148 static const struct i2c_device_id mcp47a1_id[] = { 149 { .name = "mcp47a1" }, 150 { } 151 }; 152 MODULE_DEVICE_TABLE(i2c, mcp47a1_id); 153 154 static struct i2c_driver mcp47a1_driver = { 155 .driver = { 156 .name = "mcp47a1", 157 .of_match_table = mcp47a1_of_match, 158 }, 159 .probe = mcp47a1_probe, 160 .id_table = mcp47a1_id, 161 }; 162 module_i2c_driver(mcp47a1_driver); 163 164 MODULE_AUTHOR("Joshua Crofts <joshua.crofts1@gmail.com>"); 165 MODULE_DESCRIPTION("Microchip MCP47A1 DAC Driver"); 166 MODULE_LICENSE("GPL"); 167