brintos

brintos / linux-shallow public Read only

0
0
Text · 5.3 KiB · 5f0d947 Raw
216 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * The LTC2309 is an 8-Channel, 12-Bit SAR ADC with an I2C Interface.4 *5 * Datasheet:6 * https://www.analog.com/media/en/technical-documentation/data-sheets/2309fd.pdf7 *8 * Copyright (c) 2023, Liam Beguin <liambeguin@gmail.com>9 */10#include <linux/bitfield.h>11#include <linux/i2c.h>12#include <linux/iio/iio.h>13#include <linux/kernel.h>14#include <linux/module.h>15#include <linux/mutex.h>16#include <linux/regulator/consumer.h>17 18#define LTC2309_ADC_RESOLUTION	1219#define LTC2309_INTERNAL_REF_MV 409620 21#define LTC2309_DIN_CH_MASK	GENMASK(7, 4)22#define LTC2309_DIN_SDN		BIT(7)23#define LTC2309_DIN_OSN		BIT(6)24#define LTC2309_DIN_S1		BIT(5)25#define LTC2309_DIN_S0		BIT(4)26#define LTC2309_DIN_UNI		BIT(3)27#define LTC2309_DIN_SLEEP	BIT(2)28 29/**30 * struct ltc2309 - internal device data structure31 * @dev:	Device reference32 * @client:	I2C reference33 * @lock:	Lock to serialize data access34 * @vref_mv:	Internal voltage reference35 */36struct ltc2309 {37	struct device		*dev;38	struct i2c_client	*client;39	struct mutex		lock; /* serialize data access */40	int			vref_mv;41};42 43/* Order matches expected channel address, See datasheet Table 1. */44enum ltc2309_channels {45	LTC2309_CH0_CH1 = 0,46	LTC2309_CH2_CH3,47	LTC2309_CH4_CH5,48	LTC2309_CH6_CH7,49	LTC2309_CH1_CH0,50	LTC2309_CH3_CH2,51	LTC2309_CH5_CH4,52	LTC2309_CH7_CH6,53	LTC2309_CH0,54	LTC2309_CH2,55	LTC2309_CH4,56	LTC2309_CH6,57	LTC2309_CH1,58	LTC2309_CH3,59	LTC2309_CH5,60	LTC2309_CH7,61};62 63#define LTC2309_CHAN(_chan, _addr) {				\64	.type = IIO_VOLTAGE,					\65	.indexed = 1,						\66	.address = _addr,					\67	.channel = _chan,					\68	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\69	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\70}71 72#define LTC2309_DIFF_CHAN(_chan, _chan2, _addr) {		\73	.type = IIO_VOLTAGE,					\74	.differential = 1,					\75	.indexed = 1,						\76	.address = _addr,					\77	.channel = _chan,					\78	.channel2 = _chan2,					\79	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\80	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\81}82 83static const struct iio_chan_spec ltc2309_channels[] = {84	LTC2309_CHAN(0, LTC2309_CH0),85	LTC2309_CHAN(1, LTC2309_CH1),86	LTC2309_CHAN(2, LTC2309_CH2),87	LTC2309_CHAN(3, LTC2309_CH3),88	LTC2309_CHAN(4, LTC2309_CH4),89	LTC2309_CHAN(5, LTC2309_CH5),90	LTC2309_CHAN(6, LTC2309_CH6),91	LTC2309_CHAN(7, LTC2309_CH7),92	LTC2309_DIFF_CHAN(0, 1, LTC2309_CH0_CH1),93	LTC2309_DIFF_CHAN(2, 3, LTC2309_CH2_CH3),94	LTC2309_DIFF_CHAN(4, 5, LTC2309_CH4_CH5),95	LTC2309_DIFF_CHAN(6, 7, LTC2309_CH6_CH7),96	LTC2309_DIFF_CHAN(1, 0, LTC2309_CH1_CH0),97	LTC2309_DIFF_CHAN(3, 2, LTC2309_CH3_CH2),98	LTC2309_DIFF_CHAN(5, 4, LTC2309_CH5_CH4),99	LTC2309_DIFF_CHAN(7, 6, LTC2309_CH7_CH6),100};101 102static int ltc2309_read_raw_channel(struct ltc2309 *ltc2309,103				    unsigned long address, int *val)104{105	int ret;106	__be16 buf;107	u8 din;108 109	din = FIELD_PREP(LTC2309_DIN_CH_MASK, address & 0x0f) |110		FIELD_PREP(LTC2309_DIN_UNI, 1) |111		FIELD_PREP(LTC2309_DIN_SLEEP, 0);112 113	ret = i2c_smbus_write_byte(ltc2309->client, din);114	if (ret < 0) {115		dev_err(ltc2309->dev, "i2c command failed: %pe\n",116			ERR_PTR(ret));117		return ret;118	}119 120	ret = i2c_master_recv(ltc2309->client, (char *)&buf, 2);121	if (ret < 0) {122		dev_err(ltc2309->dev, "i2c read failed: %pe\n", ERR_PTR(ret));123		return ret;124	}125 126	*val = be16_to_cpu(buf) >> 4;127 128	return ret;129}130 131static int ltc2309_read_raw(struct iio_dev *indio_dev,132			    struct iio_chan_spec const *chan, int *val,133			    int *val2, long mask)134{135	struct ltc2309 *ltc2309 = iio_priv(indio_dev);136	int ret;137 138	switch (mask) {139	case IIO_CHAN_INFO_RAW:140		mutex_lock(&ltc2309->lock);141		ret = ltc2309_read_raw_channel(ltc2309, chan->address, val);142		mutex_unlock(&ltc2309->lock);143		if (ret < 0)144			return -EINVAL;145		return IIO_VAL_INT;146	case IIO_CHAN_INFO_SCALE:147		*val = ltc2309->vref_mv;148		*val2 = LTC2309_ADC_RESOLUTION;149		return IIO_VAL_FRACTIONAL_LOG2;150	default:151		return -EINVAL;152	}153}154 155static const struct iio_info ltc2309_info = {156	.read_raw = ltc2309_read_raw,157};158 159static int ltc2309_probe(struct i2c_client *client)160{161	struct iio_dev *indio_dev;162	struct ltc2309 *ltc2309;163	int ret;164 165	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*ltc2309));166	if (!indio_dev)167		return -ENOMEM;168 169	ltc2309 = iio_priv(indio_dev);170	ltc2309->dev = &indio_dev->dev;171	ltc2309->client = client;172 173	indio_dev->name = "ltc2309";174	indio_dev->modes = INDIO_DIRECT_MODE;175	indio_dev->channels = ltc2309_channels;176	indio_dev->num_channels = ARRAY_SIZE(ltc2309_channels);177	indio_dev->info = &ltc2309_info;178 179	ret = devm_regulator_get_enable_read_voltage(&client->dev, "vref");180	if (ret < 0 && ret != -ENODEV)181		return dev_err_probe(ltc2309->dev, ret,182				     "failed to get vref voltage\n");183 184	ltc2309->vref_mv = ret == -ENODEV ? LTC2309_INTERNAL_REF_MV : ret / 1000;185 186	mutex_init(&ltc2309->lock);187 188	return devm_iio_device_register(&client->dev, indio_dev);189}190 191static const struct of_device_id ltc2309_of_match[] = {192	{ .compatible = "lltc,ltc2309" },193	{ }194};195MODULE_DEVICE_TABLE(of, ltc2309_of_match);196 197static const struct i2c_device_id ltc2309_id[] = {198	{ "ltc2309" },199	{ }200};201MODULE_DEVICE_TABLE(i2c, ltc2309_id);202 203static struct i2c_driver ltc2309_driver = {204	.driver = {205		.name = "ltc2309",206		.of_match_table = ltc2309_of_match,207	},208	.probe		= ltc2309_probe,209	.id_table	= ltc2309_id,210};211module_i2c_driver(ltc2309_driver);212 213MODULE_AUTHOR("Liam Beguin <liambeguin@gmail.com>");214MODULE_DESCRIPTION("Linear Technology LTC2309 ADC");215MODULE_LICENSE("GPL v2");216