1/* 2 * Copyright (C) 2012 Avionic Design GmbH 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 */ 8 9#include <linux/err.h> 10#include <linux/i2c.h> 11#include <linux/module.h> 12#include <linux/of.h> 13 14#include <linux/iio/iio.h> 15#include <linux/regulator/consumer.h> 16 17struct adc081c { 18 struct i2c_client *i2c; 19 struct regulator *ref; 20}; 21 22#define REG_CONV_RES 0x00 23 24static int adc081c_read_raw(struct iio_dev *iio, 25 struct iio_chan_spec const *channel, int *value, 26 int *shift, long mask) 27{ 28 struct adc081c *adc = iio_priv(iio); 29 int err; 30 31 switch (mask) { 32 case IIO_CHAN_INFO_RAW: 33 err = i2c_smbus_read_word_swapped(adc->i2c, REG_CONV_RES); 34 if (err < 0) 35 return err; 36 37 *value = (err >> 4) & 0xff; 38 return IIO_VAL_INT; 39 40 case IIO_CHAN_INFO_SCALE: 41 err = regulator_get_voltage(adc->ref); 42 if (err < 0) 43 return err; 44 45 *value = err / 1000; 46 *shift = 8; 47 48 return IIO_VAL_FRACTIONAL_LOG2; 49 50 default: 51 break; 52 } 53 54 return -EINVAL; 55} 56 57static const struct iio_chan_spec adc081c_channel = { 58 .type = IIO_VOLTAGE, 59 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), 60 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 61}; 62 63static const struct iio_info adc081c_info = { 64 .read_raw = adc081c_read_raw, 65 .driver_module = THIS_MODULE, 66}; 67 68static int adc081c_probe(struct i2c_client *client, 69 const struct i2c_device_id *id) 70{ 71 struct iio_dev *iio; 72 struct adc081c *adc; 73 int err; 74 75 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) 76 return -ENODEV; 77 78 iio = devm_iio_device_alloc(&client->dev, sizeof(*adc)); 79 if (!iio) 80 return -ENOMEM; 81 82 adc = iio_priv(iio); 83 adc->i2c = client; 84 85 adc->ref = devm_regulator_get(&client->dev, "vref"); 86 if (IS_ERR(adc->ref)) 87 return PTR_ERR(adc->ref); 88 89 err = regulator_enable(adc->ref); 90 if (err < 0) 91 return err; 92 93 iio->dev.parent = &client->dev; 94 iio->name = dev_name(&client->dev); 95 iio->modes = INDIO_DIRECT_MODE; 96 iio->info = &adc081c_info; 97 98 iio->channels = &adc081c_channel; 99 iio->num_channels = 1; 100 101 err = iio_device_register(iio); 102 if (err < 0) 103 goto regulator_disable; 104 105 i2c_set_clientdata(client, iio); 106 107 return 0; 108 109regulator_disable: 110 regulator_disable(adc->ref); 111 112 return err; 113} 114 115static int adc081c_remove(struct i2c_client *client) 116{ 117 struct iio_dev *iio = i2c_get_clientdata(client); 118 struct adc081c *adc = iio_priv(iio); 119 120 iio_device_unregister(iio); 121 regulator_disable(adc->ref); 122 123 return 0; 124} 125 126static const struct i2c_device_id adc081c_id[] = { 127 { "adc081c", 0 }, 128 { } 129}; 130MODULE_DEVICE_TABLE(i2c, adc081c_id); 131 132#ifdef CONFIG_OF 133static const struct of_device_id adc081c_of_match[] = { 134 { .compatible = "ti,adc081c" }, 135 { } 136}; 137MODULE_DEVICE_TABLE(of, adc081c_of_match); 138#endif 139 140static struct i2c_driver adc081c_driver = { 141 .driver = { 142 .name = "adc081c", 143 .owner = THIS_MODULE, 144 .of_match_table = of_match_ptr(adc081c_of_match), 145 }, 146 .probe = adc081c_probe, 147 .remove = adc081c_remove, 148 .id_table = adc081c_id, 149}; 150module_i2c_driver(adc081c_driver); 151 152MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>"); 153MODULE_DESCRIPTION("Texas Instruments ADC081C021/027 driver"); 154MODULE_LICENSE("GPL v2"); 155