1 /* 2 * Copyright (C) 2013 Oskar Andero <oskar.andero@gmail.com> 3 * 4 * Driver for Microchip Technology's MCP3204 and MCP3208 ADC chips. 5 * Datasheet can be found here: 6 * http://ww1.microchip.com/downloads/en/devicedoc/21298c.pdf 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 */ 12 13 #include <linux/err.h> 14 #include <linux/spi/spi.h> 15 #include <linux/module.h> 16 #include <linux/iio/iio.h> 17 #include <linux/regulator/consumer.h> 18 19 #define MCP_SINGLE_ENDED (1 << 3) 20 #define MCP_START_BIT (1 << 4) 21 22 enum { 23 mcp3204, 24 mcp3208, 25 }; 26 27 struct mcp320x { 28 struct spi_device *spi; 29 struct spi_message msg; 30 struct spi_transfer transfer[2]; 31 32 u8 tx_buf; 33 u8 rx_buf[2]; 34 35 struct regulator *reg; 36 struct mutex lock; 37 }; 38 39 static int mcp320x_adc_conversion(struct mcp320x *adc, u8 msg) 40 { 41 int ret; 42 43 adc->tx_buf = msg; 44 ret = spi_sync(adc->spi, &adc->msg); 45 if (ret < 0) 46 return ret; 47 48 return ((adc->rx_buf[0] & 0x3f) << 6) | 49 (adc->rx_buf[1] >> 2); 50 } 51 52 static int mcp320x_read_raw(struct iio_dev *indio_dev, 53 struct iio_chan_spec const *channel, int *val, 54 int *val2, long mask) 55 { 56 struct mcp320x *adc = iio_priv(indio_dev); 57 int ret = -EINVAL; 58 59 mutex_lock(&adc->lock); 60 61 switch (mask) { 62 case IIO_CHAN_INFO_RAW: 63 if (channel->differential) 64 ret = mcp320x_adc_conversion(adc, 65 MCP_START_BIT | channel->address); 66 else 67 ret = mcp320x_adc_conversion(adc, 68 MCP_START_BIT | MCP_SINGLE_ENDED | 69 channel->address); 70 if (ret < 0) 71 goto out; 72 73 *val = ret; 74 ret = IIO_VAL_INT; 75 break; 76 77 case IIO_CHAN_INFO_SCALE: 78 /* Digital output code = (4096 * Vin) / Vref */ 79 ret = regulator_get_voltage(adc->reg); 80 if (ret < 0) 81 goto out; 82 83 *val = ret / 1000; 84 *val2 = 12; 85 ret = IIO_VAL_FRACTIONAL_LOG2; 86 break; 87 88 default: 89 break; 90 } 91 92 out: 93 mutex_unlock(&adc->lock); 94 95 return ret; 96 } 97 98 #define MCP320X_VOLTAGE_CHANNEL(num) \ 99 { \ 100 .type = IIO_VOLTAGE, \ 101 .indexed = 1, \ 102 .channel = (num), \ 103 .address = (num), \ 104 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 105 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ 106 } 107 108 #define MCP320X_VOLTAGE_CHANNEL_DIFF(num) \ 109 { \ 110 .type = IIO_VOLTAGE, \ 111 .indexed = 1, \ 112 .channel = (num * 2), \ 113 .channel2 = (num * 2 + 1), \ 114 .address = (num * 2), \ 115 .differential = 1, \ 116 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 117 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ 118 } 119 120 static const struct iio_chan_spec mcp3204_channels[] = { 121 MCP320X_VOLTAGE_CHANNEL(0), 122 MCP320X_VOLTAGE_CHANNEL(1), 123 MCP320X_VOLTAGE_CHANNEL(2), 124 MCP320X_VOLTAGE_CHANNEL(3), 125 MCP320X_VOLTAGE_CHANNEL_DIFF(0), 126 MCP320X_VOLTAGE_CHANNEL_DIFF(1), 127 }; 128 129 static const struct iio_chan_spec mcp3208_channels[] = { 130 MCP320X_VOLTAGE_CHANNEL(0), 131 MCP320X_VOLTAGE_CHANNEL(1), 132 MCP320X_VOLTAGE_CHANNEL(2), 133 MCP320X_VOLTAGE_CHANNEL(3), 134 MCP320X_VOLTAGE_CHANNEL(4), 135 MCP320X_VOLTAGE_CHANNEL(5), 136 MCP320X_VOLTAGE_CHANNEL(6), 137 MCP320X_VOLTAGE_CHANNEL(7), 138 MCP320X_VOLTAGE_CHANNEL_DIFF(0), 139 MCP320X_VOLTAGE_CHANNEL_DIFF(1), 140 MCP320X_VOLTAGE_CHANNEL_DIFF(2), 141 MCP320X_VOLTAGE_CHANNEL_DIFF(3), 142 }; 143 144 static const struct iio_info mcp320x_info = { 145 .read_raw = mcp320x_read_raw, 146 .driver_module = THIS_MODULE, 147 }; 148 149 struct mcp3208_chip_info { 150 const struct iio_chan_spec *channels; 151 unsigned int num_channels; 152 }; 153 154 static const struct mcp3208_chip_info mcp3208_chip_infos[] = { 155 [mcp3204] = { 156 .channels = mcp3204_channels, 157 .num_channels = ARRAY_SIZE(mcp3204_channels) 158 }, 159 [mcp3208] = { 160 .channels = mcp3208_channels, 161 .num_channels = ARRAY_SIZE(mcp3208_channels) 162 }, 163 }; 164 165 static int mcp320x_probe(struct spi_device *spi) 166 { 167 struct iio_dev *indio_dev; 168 struct mcp320x *adc; 169 const struct mcp3208_chip_info *chip_info; 170 int ret; 171 172 indio_dev = iio_device_alloc(sizeof(*adc)); 173 if (!indio_dev) 174 return -ENOMEM; 175 176 adc = iio_priv(indio_dev); 177 adc->spi = spi; 178 179 indio_dev->dev.parent = &spi->dev; 180 indio_dev->name = spi_get_device_id(spi)->name; 181 indio_dev->modes = INDIO_DIRECT_MODE; 182 indio_dev->info = &mcp320x_info; 183 184 chip_info = &mcp3208_chip_infos[spi_get_device_id(spi)->driver_data]; 185 indio_dev->channels = chip_info->channels; 186 indio_dev->num_channels = chip_info->num_channels; 187 188 adc->transfer[0].tx_buf = &adc->tx_buf; 189 adc->transfer[0].len = sizeof(adc->tx_buf); 190 adc->transfer[1].rx_buf = adc->rx_buf; 191 adc->transfer[1].len = sizeof(adc->rx_buf); 192 193 spi_message_init_with_transfers(&adc->msg, adc->transfer, 194 ARRAY_SIZE(adc->transfer)); 195 196 adc->reg = regulator_get(&spi->dev, "vref"); 197 if (IS_ERR(adc->reg)) { 198 ret = PTR_ERR(adc->reg); 199 goto iio_free; 200 } 201 202 ret = regulator_enable(adc->reg); 203 if (ret < 0) 204 goto reg_free; 205 206 mutex_init(&adc->lock); 207 208 ret = iio_device_register(indio_dev); 209 if (ret < 0) 210 goto reg_disable; 211 212 return 0; 213 214 reg_disable: 215 regulator_disable(adc->reg); 216 reg_free: 217 regulator_put(adc->reg); 218 iio_free: 219 iio_device_free(indio_dev); 220 221 return ret; 222 } 223 224 static int mcp320x_remove(struct spi_device *spi) 225 { 226 struct iio_dev *indio_dev = spi_get_drvdata(spi); 227 struct mcp320x *adc = iio_priv(indio_dev); 228 229 iio_device_unregister(indio_dev); 230 regulator_disable(adc->reg); 231 regulator_put(adc->reg); 232 iio_device_free(indio_dev); 233 234 return 0; 235 } 236 237 static const struct spi_device_id mcp320x_id[] = { 238 { "mcp3204", mcp3204 }, 239 { "mcp3208", mcp3208 }, 240 { } 241 }; 242 MODULE_DEVICE_TABLE(spi, mcp320x_id); 243 244 static struct spi_driver mcp320x_driver = { 245 .driver = { 246 .name = "mcp320x", 247 .owner = THIS_MODULE, 248 }, 249 .probe = mcp320x_probe, 250 .remove = mcp320x_remove, 251 .id_table = mcp320x_id, 252 }; 253 module_spi_driver(mcp320x_driver); 254 255 MODULE_AUTHOR("Oskar Andero <oskar.andero@gmail.com>"); 256 MODULE_DESCRIPTION("Microchip Technology MCP3204/08"); 257 MODULE_LICENSE("GPL v2"); 258