summaryrefslogtreecommitdiffstats
path: root/drivers/iio/dac/ad5592r.c
blob: 0b235a2c73593326b396b33bdc0d8b46acd3e8a0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/*
 * AD5592R Digital <-> Analog converters driver
 *
 * Copyright 2015-2016 Analog Devices Inc.
 * Author: Paul Cercueil <paul.cercueil@analog.com>
 *
 * Licensed under the GPL-2.
 */

#include "ad5592r-base.h"

#include <linux/bitops.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/spi/spi.h>

#define AD5592R_GPIO_READBACK_EN	BIT(10)
#define AD5592R_LDAC_READBACK_EN	BIT(6)

static int ad5592r_spi_wnop_r16(struct ad5592r_state *st, u16 *buf)
{
	struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
	struct spi_transfer t = {
			.tx_buf	= &st->spi_msg_nop,
			.rx_buf	= buf,
			.len = 2
		};

	st->spi_msg_nop = 0; /* NOP */

	return spi_sync_transfer(spi, &t, 1);
}

static int ad5592r_write_dac(struct ad5592r_state *st, unsigned chan, u16 value)
{
	struct spi_device *spi = container_of(st->dev, struct spi_device, dev);

	st->spi_msg = cpu_to_be16(BIT(15) | (chan << 12) | value);

	return spi_write(spi, &st->spi_msg, sizeof(st->spi_msg));
}

static int ad5592r_read_adc(struct ad5592r_state *st, unsigned chan, u16 *value)
{
	struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
	int ret;

	st->spi_msg = cpu_to_be16((AD5592R_REG_ADC_SEQ << 11) | BIT(chan));

	ret = spi_write(spi, &st->spi_msg, sizeof(st->spi_msg));
	if (ret)
		return ret;

	/*
	 * Invalid data:
	 * See Figure 40. Single-Channel ADC Conversion Sequence
	 */
	ret = ad5592r_spi_wnop_r16(st, &st->spi_msg);
	if (ret)
		return ret;

	ret = ad5592r_spi_wnop_r16(st, &st->spi_msg);
	if (ret)
		return ret;

	*value = be16_to_cpu(st->spi_msg);

	return 0;
}

static int ad5592r_reg_write(struct ad5592r_state *st, u8 reg, u16 value)
{
	struct spi_device *spi = container_of(st->dev, struct spi_device, dev);

	st->spi_msg = cpu_to_be16((reg << 11) | value);

	return spi_write(spi, &st->spi_msg, sizeof(st->spi_msg));
}

static int ad5592r_reg_read(struct ad5592r_state *st, u8 reg, u16 *value)
{
	struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
	int ret;

	st->spi_msg = cpu_to_be16((AD5592R_REG_LDAC << 11) |
				   AD5592R_LDAC_READBACK_EN | (reg << 2));

	ret = spi_write(spi, &st->spi_msg, sizeof(st->spi_msg));
	if (ret)
		return ret;

	ret = ad5592r_spi_wnop_r16(st, &st->spi_msg);
	if (ret)
		return ret;

	*value = be16_to_cpu(st->spi_msg);

	return 0;
}

static int ad5593r_gpio_read(struct ad5592r_state *st, u8 *value)
{
	int ret;

	ret = ad5592r_reg_write(st, AD5592R_REG_GPIO_IN_EN,
				AD5592R_GPIO_READBACK_EN | st->gpio_in);
	if (ret)
		return ret;

	ret = ad5592r_spi_wnop_r16(st, &st->spi_msg);
	if (ret)
		return ret;

	*value = (u8) be16_to_cpu(st->spi_msg);

	return 0;
}

static const struct ad5592r_rw_ops ad5592r_rw_ops = {
	.write_dac = ad5592r_write_dac,
	.read_adc = ad5592r_read_adc,
	.reg_write = ad5592r_reg_write,
	.reg_read = ad5592r_reg_read,
	.gpio_read = ad5593r_gpio_read,
};

static int ad5592r_spi_probe(struct spi_device *spi)
{
	const struct spi_device_id *id = spi_get_device_id(spi);

	return ad5592r_probe(&spi->dev, id->name, &ad5592r_rw_ops);
}

static int ad5592r_spi_remove(struct spi_device *spi)
{
	return ad5592r_remove(&spi->dev);
}

static const struct spi_device_id ad5592r_spi_ids[] = {
	{ .name = "ad5592r", },
	{}
};
MODULE_DEVICE_TABLE(spi, ad5592r_spi_ids);

static const struct of_device_id ad5592r_of_match[] = {
	{ .compatible = "adi,ad5592r", },
	{},
};
MODULE_DEVICE_TABLE(of, ad5592r_of_match);

static struct spi_driver ad5592r_spi_driver = {
	.driver = {
		.name = "ad5592r",
		.of_match_table = of_match_ptr(ad5592r_of_match),
	},
	.probe = ad5592r_spi_probe,
	.remove = ad5592r_spi_remove,
	.id_table = ad5592r_spi_ids,
};
module_spi_driver(ad5592r_spi_driver);

MODULE_AUTHOR("Paul Cercueil <paul.cercueil@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD5592R multi-channel converters");
MODULE_LICENSE("GPL v2");