18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * tpl0102.c - Support for Texas Instruments digital potentiometers
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2016, 2018
68c2ecf20Sopenharmony_ci * Author: Matt Ranostay <matt.ranostay@konsulko.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * TODO: enable/disable hi-z output control
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/i2c.h>
138c2ecf20Sopenharmony_ci#include <linux/regmap.h>
148c2ecf20Sopenharmony_ci#include <linux/iio/iio.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistruct tpl0102_cfg {
178c2ecf20Sopenharmony_ci	int wipers;
188c2ecf20Sopenharmony_ci	int avail[3];
198c2ecf20Sopenharmony_ci	int kohms;
208c2ecf20Sopenharmony_ci};
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cienum tpl0102_type {
238c2ecf20Sopenharmony_ci	CAT5140_503,
248c2ecf20Sopenharmony_ci	CAT5140_104,
258c2ecf20Sopenharmony_ci	TPL0102_104,
268c2ecf20Sopenharmony_ci	TPL0401_103,
278c2ecf20Sopenharmony_ci};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic const struct tpl0102_cfg tpl0102_cfg[] = {
308c2ecf20Sopenharmony_ci	/* on-semiconductor parts */
318c2ecf20Sopenharmony_ci	[CAT5140_503] = { .wipers = 1, .avail = { 0, 1, 255 }, .kohms = 50, },
328c2ecf20Sopenharmony_ci	[CAT5140_104] = { .wipers = 1, .avail = { 0, 1, 255 }, .kohms = 100, },
338c2ecf20Sopenharmony_ci	/* ti parts */
348c2ecf20Sopenharmony_ci	[TPL0102_104] = { .wipers = 2, .avail = { 0, 1, 255 }, .kohms = 100 },
358c2ecf20Sopenharmony_ci	[TPL0401_103] = { .wipers = 1, .avail = { 0, 1, 127 }, .kohms = 10, },
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistruct tpl0102_data {
398c2ecf20Sopenharmony_ci	struct regmap *regmap;
408c2ecf20Sopenharmony_ci	const struct tpl0102_cfg *cfg;
418c2ecf20Sopenharmony_ci};
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic const struct regmap_config tpl0102_regmap_config = {
448c2ecf20Sopenharmony_ci	.reg_bits = 8,
458c2ecf20Sopenharmony_ci	.val_bits = 8,
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define TPL0102_CHANNEL(ch) {					\
498c2ecf20Sopenharmony_ci	.type = IIO_RESISTANCE,					\
508c2ecf20Sopenharmony_ci	.indexed = 1,						\
518c2ecf20Sopenharmony_ci	.output = 1,						\
528c2ecf20Sopenharmony_ci	.channel = (ch),					\
538c2ecf20Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
548c2ecf20Sopenharmony_ci	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
558c2ecf20Sopenharmony_ci	.info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW),	\
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic const struct iio_chan_spec tpl0102_channels[] = {
598c2ecf20Sopenharmony_ci	TPL0102_CHANNEL(0),
608c2ecf20Sopenharmony_ci	TPL0102_CHANNEL(1),
618c2ecf20Sopenharmony_ci};
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic int tpl0102_read_raw(struct iio_dev *indio_dev,
648c2ecf20Sopenharmony_ci			    struct iio_chan_spec const *chan,
658c2ecf20Sopenharmony_ci			    int *val, int *val2, long mask)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct tpl0102_data *data = iio_priv(indio_dev);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	switch (mask) {
708c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_RAW: {
718c2ecf20Sopenharmony_ci		int ret = regmap_read(data->regmap, chan->channel, val);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci		return ret ? ret : IIO_VAL_INT;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
768c2ecf20Sopenharmony_ci		*val = 1000 * data->cfg->kohms;
778c2ecf20Sopenharmony_ci		*val2 = data->cfg->avail[2] + 1;
788c2ecf20Sopenharmony_ci		return IIO_VAL_FRACTIONAL;
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	return -EINVAL;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic int tpl0102_read_avail(struct iio_dev *indio_dev,
858c2ecf20Sopenharmony_ci			      struct iio_chan_spec const *chan,
868c2ecf20Sopenharmony_ci			      const int **vals, int *type, int *length,
878c2ecf20Sopenharmony_ci			      long mask)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	struct tpl0102_data *data = iio_priv(indio_dev);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	switch (mask) {
928c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
938c2ecf20Sopenharmony_ci		*length = ARRAY_SIZE(data->cfg->avail);
948c2ecf20Sopenharmony_ci		*vals = data->cfg->avail;
958c2ecf20Sopenharmony_ci		*type = IIO_VAL_INT;
968c2ecf20Sopenharmony_ci		return IIO_AVAIL_RANGE;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	return -EINVAL;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic int tpl0102_write_raw(struct iio_dev *indio_dev,
1038c2ecf20Sopenharmony_ci			     struct iio_chan_spec const *chan,
1048c2ecf20Sopenharmony_ci			     int val, int val2, long mask)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct tpl0102_data *data = iio_priv(indio_dev);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (mask != IIO_CHAN_INFO_RAW)
1098c2ecf20Sopenharmony_ci		return -EINVAL;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if (val > data->cfg->avail[2] || val < 0)
1128c2ecf20Sopenharmony_ci		return -EINVAL;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	return regmap_write(data->regmap, chan->channel, val);
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic const struct iio_info tpl0102_info = {
1188c2ecf20Sopenharmony_ci	.read_raw = tpl0102_read_raw,
1198c2ecf20Sopenharmony_ci	.read_avail = tpl0102_read_avail,
1208c2ecf20Sopenharmony_ci	.write_raw = tpl0102_write_raw,
1218c2ecf20Sopenharmony_ci};
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic int tpl0102_probe(struct i2c_client *client,
1248c2ecf20Sopenharmony_ci			 const struct i2c_device_id *id)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	struct device *dev = &client->dev;
1278c2ecf20Sopenharmony_ci	struct tpl0102_data *data;
1288c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
1318c2ecf20Sopenharmony_ci	if (!indio_dev)
1328c2ecf20Sopenharmony_ci		return -ENOMEM;
1338c2ecf20Sopenharmony_ci	data = iio_priv(indio_dev);
1348c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, indio_dev);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	data->cfg = &tpl0102_cfg[id->driver_data];
1378c2ecf20Sopenharmony_ci	data->regmap = devm_regmap_init_i2c(client, &tpl0102_regmap_config);
1388c2ecf20Sopenharmony_ci	if (IS_ERR(data->regmap)) {
1398c2ecf20Sopenharmony_ci		dev_err(dev, "regmap initialization failed\n");
1408c2ecf20Sopenharmony_ci		return PTR_ERR(data->regmap);
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	indio_dev->info = &tpl0102_info;
1448c2ecf20Sopenharmony_ci	indio_dev->channels = tpl0102_channels;
1458c2ecf20Sopenharmony_ci	indio_dev->num_channels = data->cfg->wipers;
1468c2ecf20Sopenharmony_ci	indio_dev->name = client->name;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	return devm_iio_device_register(dev, indio_dev);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic const struct i2c_device_id tpl0102_id[] = {
1528c2ecf20Sopenharmony_ci	{ "cat5140-503", CAT5140_503 },
1538c2ecf20Sopenharmony_ci	{ "cat5140-104", CAT5140_104 },
1548c2ecf20Sopenharmony_ci	{ "tpl0102-104", TPL0102_104 },
1558c2ecf20Sopenharmony_ci	{ "tpl0401-103", TPL0401_103 },
1568c2ecf20Sopenharmony_ci	{}
1578c2ecf20Sopenharmony_ci};
1588c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tpl0102_id);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic struct i2c_driver tpl0102_driver = {
1618c2ecf20Sopenharmony_ci	.driver = {
1628c2ecf20Sopenharmony_ci		.name = "tpl0102",
1638c2ecf20Sopenharmony_ci	},
1648c2ecf20Sopenharmony_ci	.probe = tpl0102_probe,
1658c2ecf20Sopenharmony_ci	.id_table = tpl0102_id,
1668c2ecf20Sopenharmony_ci};
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cimodule_i2c_driver(tpl0102_driver);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ciMODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
1718c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TPL0102 digital potentiometer");
1728c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
173