File Index
Files and directories
Sign in
FILE

mcp4725.c

File preview with syntax highlighting.

linux-6.19.14/drivers/iio/dac/mcp4725.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * mcp4725.c - Support for Microchip MCP4725/6
  4 *
  5 * Copyright (C) 2012 Peter Meerwald <pmeerw@pmeerw.net>
  6 *
  7 * Based on max517 by Roland Stigge <stigge@antcom.de>
  8 *
  9 * driver for the Microchip I2C 12-bit digital-to-analog converter (DAC)
 10 * (7-bit I2C slave address 0x60, the three LSBs can be configured in
 11 * hardware)
 12 */
 13
 14#include <linux/module.h>
 15#include <linux/i2c.h>
 16#include <linux/err.h>
 17#include <linux/delay.h>
 18#include <linux/regulator/consumer.h>
 19#include <linux/mod_devicetable.h>
 20#include <linux/property.h>
 21
 22#include <linux/iio/iio.h>
 23#include <linux/iio/sysfs.h>
 24
 25#include <linux/iio/dac/mcp4725.h>
 26
 27#define MCP472X_REF_VDD			0x00
 28#define MCP472X_REF_VREF_UNBUFFERED	0x02
 29#define MCP472X_REF_VREF_BUFFERED	0x03
 30
 31struct mcp4725_chip_info {
 32	const struct iio_chan_spec *chan_spec;
 33	u8 dac_reg_offset;
 34	bool use_ext_ref_voltage;
 35};
 36
 37struct mcp4725_data {
 38	struct i2c_client *client;
 39	unsigned ref_mode;
 40	bool vref_buffered;
 41	u16 dac_value;
 42	bool powerdown;
 43	unsigned powerdown_mode;
 44	struct regulator *vdd_reg;
 45	struct regulator *vref_reg;
 46};
 47
 48static int mcp4725_suspend(struct device *dev)
 49{
 50	struct mcp4725_data *data = iio_priv(i2c_get_clientdata(
 51		to_i2c_client(dev)));
 52	u8 outbuf[2];
 53	int ret;
 54
 55	outbuf[0] = (data->powerdown_mode + 1) << 4;
 56	outbuf[1] = 0;
 57	data->powerdown = true;
 58
 59	ret = i2c_master_send(data->client, outbuf, 2);
 60	if (ret < 0)
 61		return ret;
 62	else if (ret != 2)
 63		return -EIO;
 64	return 0;
 65}
 66
 67static int mcp4725_resume(struct device *dev)
 68{
 69	struct mcp4725_data *data = iio_priv(i2c_get_clientdata(
 70		to_i2c_client(dev)));
 71	u8 outbuf[2];
 72	int ret;
 73
 74	/* restore previous DAC value */
 75	outbuf[0] = (data->dac_value >> 8) & 0xf;
 76	outbuf[1] = data->dac_value & 0xff;
 77	data->powerdown = false;
 78
 79	ret = i2c_master_send(data->client, outbuf, 2);
 80	if (ret < 0)
 81		return ret;
 82	else if (ret != 2)
 83		return -EIO;
 84	return 0;
 85}
 86static DEFINE_SIMPLE_DEV_PM_OPS(mcp4725_pm_ops, mcp4725_suspend,
 87				mcp4725_resume);
 88
 89static ssize_t mcp4725_store_eeprom(struct device *dev,
 90	struct device_attribute *attr, const char *buf, size_t len)
 91{
 92	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
 93	struct mcp4725_data *data = iio_priv(indio_dev);
 94	int tries = 20;
 95	u8 inoutbuf[3];
 96	bool state;
 97	int ret;
 98
 99	ret = kstrtobool(buf, &state);
100	if (ret < 0)
101		return ret;
102
103	if (!state)
104		return 0;
105
106	inoutbuf[0] = 0x60; /* write EEPROM */
107	inoutbuf[0] |= data->ref_mode << 3;
108	inoutbuf[0] |= data->powerdown ? ((data->powerdown_mode + 1) << 1) : 0;
109	inoutbuf[1] = data->dac_value >> 4;
110	inoutbuf[2] = (data->dac_value & 0xf) << 4;
111
112	ret = i2c_master_send(data->client, inoutbuf, 3);
113	if (ret < 0)
114		return ret;
115	else if (ret != 3)
116		return -EIO;
117
118	/* wait for write complete, takes up to 50ms */
119	while (tries--) {
120		msleep(20);
121		ret = i2c_master_recv(data->client, inoutbuf, 3);
122		if (ret < 0)
123			return ret;
124		else if (ret != 3)
125			return -EIO;
126
127		if (inoutbuf[0] & 0x80)
128			break;
129	}
130
131	if (tries < 0) {
132		dev_err(&data->client->dev,
133			"mcp4725_store_eeprom() failed, incomplete\n");
134		return -EIO;
135	}
136
137	return len;
138}
139
140static IIO_DEVICE_ATTR(store_eeprom, S_IWUSR, NULL, mcp4725_store_eeprom, 0);
141
142static struct attribute *mcp4725_attributes[] = {
143	&iio_dev_attr_store_eeprom.dev_attr.attr,
144	NULL,
145};
146
147static const struct attribute_group mcp4725_attribute_group = {
148	.attrs = mcp4725_attributes,
149};
150
151static const char * const mcp4725_powerdown_modes[] = {
152	"1kohm_to_gnd",
153	"100kohm_to_gnd",
154	"500kohm_to_gnd"
155};
156
157static const char * const mcp4726_powerdown_modes[] = {
158	"1kohm_to_gnd",
159	"125kohm_to_gnd",
160	"640kohm_to_gnd"
161};
162
163static int mcp4725_get_powerdown_mode(struct iio_dev *indio_dev,
164	const struct iio_chan_spec *chan)
165{
166	struct mcp4725_data *data = iio_priv(indio_dev);
167
168	return data->powerdown_mode;
169}
170
171static int mcp4725_set_powerdown_mode(struct iio_dev *indio_dev,
172	const struct iio_chan_spec *chan, unsigned mode)
173{
174	struct mcp4725_data *data = iio_priv(indio_dev);
175
176	data->powerdown_mode = mode;
177
178	return 0;
179}
180
181static ssize_t mcp4725_read_powerdown(struct iio_dev *indio_dev,
182	uintptr_t private, const struct iio_chan_spec *chan, char *buf)
183{
184	struct mcp4725_data *data = iio_priv(indio_dev);
185
186	return sysfs_emit(buf, "%d\n", data->powerdown);
187}
188
189static ssize_t mcp4725_write_powerdown(struct iio_dev *indio_dev,
190	 uintptr_t private, const struct iio_chan_spec *chan,
191	 const char *buf, size_t len)
192{
193	struct mcp4725_data *data = iio_priv(indio_dev);
194	bool state;
195	int ret;
196
197	ret = kstrtobool(buf, &state);
198	if (ret)
199		return ret;
200
201	if (state)
202		ret = mcp4725_suspend(&data->client->dev);
203	else
204		ret = mcp4725_resume(&data->client->dev);
205	if (ret < 0)
206		return ret;
207
208	return len;
209}
210
211enum chip_id {
212	MCP4725,
213	MCP4726,
214};
215
216static const struct iio_enum mcp472x_powerdown_mode_enum[] = {
217	[MCP4725] = {
218		.items = mcp4725_powerdown_modes,
219		.num_items = ARRAY_SIZE(mcp4725_powerdown_modes),
220		.get = mcp4725_get_powerdown_mode,
221		.set = mcp4725_set_powerdown_mode,
222	},
223	[MCP4726] = {
224		.items = mcp4726_powerdown_modes,
225		.num_items = ARRAY_SIZE(mcp4726_powerdown_modes),
226		.get = mcp4725_get_powerdown_mode,
227		.set = mcp4725_set_powerdown_mode,
228	},
229};
230
231static const struct iio_chan_spec_ext_info mcp4725_ext_info[] = {
232	{
233		.name = "powerdown",
234		.read = mcp4725_read_powerdown,
235		.write = mcp4725_write_powerdown,
236		.shared = IIO_SEPARATE,
237	},
238	IIO_ENUM("powerdown_mode", IIO_SEPARATE,
239			&mcp472x_powerdown_mode_enum[MCP4725]),
240	IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE,
241			   &mcp472x_powerdown_mode_enum[MCP4725]),
242	{ }
243};
244
245static const struct iio_chan_spec_ext_info mcp4726_ext_info[] = {
246	{
247		.name = "powerdown",
248		.read = mcp4725_read_powerdown,
249		.write = mcp4725_write_powerdown,
250		.shared = IIO_SEPARATE,
251	},
252	IIO_ENUM("powerdown_mode", IIO_SEPARATE,
253			&mcp472x_powerdown_mode_enum[MCP4726]),
254	IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE,
255			   &mcp472x_powerdown_mode_enum[MCP4726]),
256	{ }
257};
258
259static const struct iio_chan_spec mcp472x_channel[] = {
260	[MCP4725] = {
261		.type		= IIO_VOLTAGE,
262		.indexed	= 1,
263		.output		= 1,
264		.channel	= 0,
265		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
266		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
267		.ext_info	= mcp4725_ext_info,
268	},
269	[MCP4726] = {
270		.type		= IIO_VOLTAGE,
271		.indexed	= 1,
272		.output		= 1,
273		.channel	= 0,
274		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
275		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
276		.ext_info	= mcp4726_ext_info,
277	},
278};
279
280static int mcp4725_set_value(struct iio_dev *indio_dev, int val)
281{
282	struct mcp4725_data *data = iio_priv(indio_dev);
283	u8 outbuf[2];
284	int ret;
285
286	if (val >= (1 << 12) || val < 0)
287		return -EINVAL;
288
289	outbuf[0] = (val >> 8) & 0xf;
290	outbuf[1] = val & 0xff;
291
292	ret = i2c_master_send(data->client, outbuf, 2);
293	if (ret < 0)
294		return ret;
295	else if (ret != 2)
296		return -EIO;
297	else
298		return 0;
299}
300
301static int mcp4726_set_cfg(struct iio_dev *indio_dev)
302{
303	struct mcp4725_data *data = iio_priv(indio_dev);
304	u8 outbuf[3];
305	int ret;
306
307	outbuf[0] = 0x40;
308	outbuf[0] |= data->ref_mode << 3;
309	if (data->powerdown)
310		outbuf[0] |= data->powerdown << 1;
311	outbuf[1] = data->dac_value >> 4;
312	outbuf[2] = (data->dac_value & 0xf) << 4;
313
314	ret = i2c_master_send(data->client, outbuf, 3);
315	if (ret < 0)
316		return ret;
317	else if (ret != 3)
318		return -EIO;
319	else
320		return 0;
321}
322
323static int mcp4725_read_raw(struct iio_dev *indio_dev,
324			   struct iio_chan_spec const *chan,
325			   int *val, int *val2, long mask)
326{
327	struct mcp4725_data *data = iio_priv(indio_dev);
328	int ret;
329
330	switch (mask) {
331	case IIO_CHAN_INFO_RAW:
332		*val = data->dac_value;
333		return IIO_VAL_INT;
334	case IIO_CHAN_INFO_SCALE:
335		if (data->ref_mode == MCP472X_REF_VDD)
336			ret = regulator_get_voltage(data->vdd_reg);
337		else
338			ret = regulator_get_voltage(data->vref_reg);
339
340		if (ret < 0)
341			return ret;
342
343		*val = ret / 1000;
344		*val2 = 12;
345		return IIO_VAL_FRACTIONAL_LOG2;
346	}
347	return -EINVAL;
348}
349
350static int mcp4725_write_raw(struct iio_dev *indio_dev,
351			       struct iio_chan_spec const *chan,
352			       int val, int val2, long mask)
353{
354	struct mcp4725_data *data = iio_priv(indio_dev);
355	int ret;
356
357	switch (mask) {
358	case IIO_CHAN_INFO_RAW:
359		ret = mcp4725_set_value(indio_dev, val);
360		data->dac_value = val;
361		break;
362	default:
363		ret = -EINVAL;
364		break;
365	}
366
367	return ret;
368}
369
370static const struct iio_info mcp4725_info = {
371	.read_raw = mcp4725_read_raw,
372	.write_raw = mcp4725_write_raw,
373	.attrs = &mcp4725_attribute_group,
374};
375
376static int mcp4725_probe_dt(struct device *dev,
377			    struct mcp4725_platform_data *pdata)
378{
379	/* check if is the vref-supply defined */
380	pdata->use_vref = device_property_present(dev, "vref-supply");
381	pdata->vref_buffered =
382		device_property_read_bool(dev, "microchip,vref-buffered");
383
384	return 0;
385}
386
387static int mcp4725_probe(struct i2c_client *client)
388{
389	const struct i2c_device_id *id = i2c_client_get_device_id(client);
390	const struct mcp4725_chip_info *info;
391	struct mcp4725_data *data;
392	struct iio_dev *indio_dev;
393	struct mcp4725_platform_data *pdata, pdata_dt;
394	u8 inbuf[4];
395	u8 pd;
396	u8 ref;
397	int err;
398
399	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
400	if (indio_dev == NULL)
401		return -ENOMEM;
402	data = iio_priv(indio_dev);
403	i2c_set_clientdata(client, indio_dev);
404	data->client = client;
405	info = i2c_get_match_data(client);
406	pdata = dev_get_platdata(&client->dev);
407
408	if (!pdata) {
409		err = mcp4725_probe_dt(&client->dev, &pdata_dt);
410		if (err) {
411			dev_err(&client->dev,
412				"invalid platform or devicetree data");
413			return err;
414		}
415		pdata = &pdata_dt;
416	}
417
418	if (info->use_ext_ref_voltage && pdata->use_vref) {
419		dev_err(&client->dev,
420			"external reference is unavailable on MCP4725");
421		return -EINVAL;
422	}
423
424	if (!pdata->use_vref && pdata->vref_buffered) {
425		dev_err(&client->dev,
426			"buffering is unavailable on the internal reference");
427		return -EINVAL;
428	}
429
430	if (!pdata->use_vref)
431		data->ref_mode = MCP472X_REF_VDD;
432	else
433		data->ref_mode = pdata->vref_buffered ?
434			MCP472X_REF_VREF_BUFFERED :
435			MCP472X_REF_VREF_UNBUFFERED;
436
437	data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
438	if (IS_ERR(data->vdd_reg))
439		return PTR_ERR(data->vdd_reg);
440
441	err = regulator_enable(data->vdd_reg);
442	if (err)
443		return err;
444
445	if (pdata->use_vref) {
446		data->vref_reg = devm_regulator_get(&client->dev, "vref");
447		if (IS_ERR(data->vref_reg)) {
448			err = PTR_ERR(data->vref_reg);
449			goto err_disable_vdd_reg;
450		}
451
452		err = regulator_enable(data->vref_reg);
453		if (err)
454			goto err_disable_vdd_reg;
455	}
456
457	indio_dev->name = id->name;
458	indio_dev->info = &mcp4725_info;
459	indio_dev->channels = info->chan_spec;
460	indio_dev->num_channels = 1;
461	indio_dev->modes = INDIO_DIRECT_MODE;
462
463	/* read current DAC value and settings */
464	err = i2c_master_recv(client, inbuf, info->dac_reg_offset);
465
466	if (err < 0) {
467		dev_err(&client->dev, "failed to read DAC value");
468		goto err_disable_vref_reg;
469	}
470	pd = (inbuf[0] >> 1) & 0x3;
471	data->powerdown = pd > 0;
472	data->powerdown_mode = pd ? pd - 1 : 2; /* largest resistor to gnd */
473	data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4);
474	if (!info->use_ext_ref_voltage)
475		ref = (inbuf[3] >> 3) & 0x3;
476
477	if (!info->use_ext_ref_voltage && ref != data->ref_mode) {
478		dev_info(&client->dev,
479			"voltage reference mode differs (conf: %u, eeprom: %u), setting %u",
480			data->ref_mode, ref, data->ref_mode);
481		err = mcp4726_set_cfg(indio_dev);
482		if (err < 0)
483			goto err_disable_vref_reg;
484	}
485 
486	err = iio_device_register(indio_dev);
487	if (err)
488		goto err_disable_vref_reg;
489
490	return 0;
491
492err_disable_vref_reg:
493	if (data->vref_reg)
494		regulator_disable(data->vref_reg);
495
496err_disable_vdd_reg:
497	regulator_disable(data->vdd_reg);
498
499	return err;
500}
501
502static void mcp4725_remove(struct i2c_client *client)
503{
504	struct iio_dev *indio_dev = i2c_get_clientdata(client);
505	struct mcp4725_data *data = iio_priv(indio_dev);
506
507	iio_device_unregister(indio_dev);
508
509	if (data->vref_reg)
510		regulator_disable(data->vref_reg);
511	regulator_disable(data->vdd_reg);
512}
513
514static const struct mcp4725_chip_info mcp4725 = {
515	.chan_spec = &mcp472x_channel[MCP4725],
516	.dac_reg_offset = 3,
517	.use_ext_ref_voltage = true,
518};
519
520static const struct mcp4725_chip_info mcp4726 = {
521	.chan_spec = &mcp472x_channel[MCP4726],
522	.dac_reg_offset = 4,
523};
524
525static const struct i2c_device_id mcp4725_id[] = {
526	{ "mcp4725", (kernel_ulong_t)&mcp4725 },
527	{ "mcp4726", (kernel_ulong_t)&mcp4726 },
528	{ }
529};
530MODULE_DEVICE_TABLE(i2c, mcp4725_id);
531
532static const struct of_device_id mcp4725_of_match[] = {
533	{
534		.compatible = "microchip,mcp4725",
535		.data = &mcp4725
536	},
537	{
538		.compatible = "microchip,mcp4726",
539		.data = &mcp4726
540	},
541	{ }
542};
543MODULE_DEVICE_TABLE(of, mcp4725_of_match);
544
545static struct i2c_driver mcp4725_driver = {
546	.driver = {
547		.name	= "mcp4725",
548		.of_match_table = mcp4725_of_match,
549		.pm	= pm_sleep_ptr(&mcp4725_pm_ops),
550	},
551	.probe		= mcp4725_probe,
552	.remove		= mcp4725_remove,
553	.id_table	= mcp4725_id,
554};
555module_i2c_driver(mcp4725_driver);
556
557MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
558MODULE_DESCRIPTION("MCP4725/6 12-bit DAC");
559MODULE_LICENSE("GPL");
~/ Read-only file preview.