]>
Commit | Line | Data |
---|---|---|
fda8d26e | 1 | // SPDX-License-Identifier: GPL-2.0-only |
56ca9db8 PC |
2 | /* |
3 | * AD5592R Digital <-> Analog converters driver | |
4 | * | |
5 | * Copyright 2015-2016 Analog Devices Inc. | |
6 | * Author: Paul Cercueil <paul.cercueil@analog.com> | |
56ca9db8 PC |
7 | */ |
8 | ||
9 | #include "ad5592r-base.h" | |
10 | ||
11 | #include <linux/bitops.h> | |
12 | #include <linux/module.h> | |
13 | #include <linux/of.h> | |
14 | #include <linux/spi/spi.h> | |
b0570bce | 15 | #include <linux/acpi.h> |
56ca9db8 PC |
16 | |
17 | #define AD5592R_GPIO_READBACK_EN BIT(10) | |
18 | #define AD5592R_LDAC_READBACK_EN BIT(6) | |
19 | ||
38e442fc | 20 | static int ad5592r_spi_wnop_r16(struct ad5592r_state *st, __be16 *buf) |
56ca9db8 PC |
21 | { |
22 | struct spi_device *spi = container_of(st->dev, struct spi_device, dev); | |
23 | struct spi_transfer t = { | |
24 | .tx_buf = &st->spi_msg_nop, | |
25 | .rx_buf = buf, | |
26 | .len = 2 | |
27 | }; | |
28 | ||
29 | st->spi_msg_nop = 0; /* NOP */ | |
30 | ||
31 | return spi_sync_transfer(spi, &t, 1); | |
32 | } | |
33 | ||
34 | static int ad5592r_write_dac(struct ad5592r_state *st, unsigned chan, u16 value) | |
35 | { | |
36 | struct spi_device *spi = container_of(st->dev, struct spi_device, dev); | |
37 | ||
38 | st->spi_msg = cpu_to_be16(BIT(15) | (chan << 12) | value); | |
39 | ||
40 | return spi_write(spi, &st->spi_msg, sizeof(st->spi_msg)); | |
41 | } | |
42 | ||
43 | static int ad5592r_read_adc(struct ad5592r_state *st, unsigned chan, u16 *value) | |
44 | { | |
45 | struct spi_device *spi = container_of(st->dev, struct spi_device, dev); | |
46 | int ret; | |
47 | ||
48 | st->spi_msg = cpu_to_be16((AD5592R_REG_ADC_SEQ << 11) | BIT(chan)); | |
49 | ||
50 | ret = spi_write(spi, &st->spi_msg, sizeof(st->spi_msg)); | |
51 | if (ret) | |
52 | return ret; | |
53 | ||
54 | /* | |
55 | * Invalid data: | |
56 | * See Figure 40. Single-Channel ADC Conversion Sequence | |
57 | */ | |
58 | ret = ad5592r_spi_wnop_r16(st, &st->spi_msg); | |
59 | if (ret) | |
60 | return ret; | |
61 | ||
62 | ret = ad5592r_spi_wnop_r16(st, &st->spi_msg); | |
63 | if (ret) | |
64 | return ret; | |
65 | ||
66 | *value = be16_to_cpu(st->spi_msg); | |
67 | ||
68 | return 0; | |
69 | } | |
70 | ||
71 | static int ad5592r_reg_write(struct ad5592r_state *st, u8 reg, u16 value) | |
72 | { | |
73 | struct spi_device *spi = container_of(st->dev, struct spi_device, dev); | |
74 | ||
75 | st->spi_msg = cpu_to_be16((reg << 11) | value); | |
76 | ||
77 | return spi_write(spi, &st->spi_msg, sizeof(st->spi_msg)); | |
78 | } | |
79 | ||
80 | static int ad5592r_reg_read(struct ad5592r_state *st, u8 reg, u16 *value) | |
81 | { | |
82 | struct spi_device *spi = container_of(st->dev, struct spi_device, dev); | |
83 | int ret; | |
84 | ||
85 | st->spi_msg = cpu_to_be16((AD5592R_REG_LDAC << 11) | | |
86 | AD5592R_LDAC_READBACK_EN | (reg << 2)); | |
87 | ||
88 | ret = spi_write(spi, &st->spi_msg, sizeof(st->spi_msg)); | |
89 | if (ret) | |
90 | return ret; | |
91 | ||
92 | ret = ad5592r_spi_wnop_r16(st, &st->spi_msg); | |
93 | if (ret) | |
94 | return ret; | |
95 | ||
96 | *value = be16_to_cpu(st->spi_msg); | |
97 | ||
98 | return 0; | |
99 | } | |
100 | ||
101 | static int ad5593r_gpio_read(struct ad5592r_state *st, u8 *value) | |
102 | { | |
103 | int ret; | |
104 | ||
105 | ret = ad5592r_reg_write(st, AD5592R_REG_GPIO_IN_EN, | |
106 | AD5592R_GPIO_READBACK_EN | st->gpio_in); | |
107 | if (ret) | |
108 | return ret; | |
109 | ||
110 | ret = ad5592r_spi_wnop_r16(st, &st->spi_msg); | |
111 | if (ret) | |
112 | return ret; | |
113 | ||
114 | *value = (u8) be16_to_cpu(st->spi_msg); | |
115 | ||
116 | return 0; | |
117 | } | |
118 | ||
119 | static const struct ad5592r_rw_ops ad5592r_rw_ops = { | |
120 | .write_dac = ad5592r_write_dac, | |
121 | .read_adc = ad5592r_read_adc, | |
122 | .reg_write = ad5592r_reg_write, | |
123 | .reg_read = ad5592r_reg_read, | |
124 | .gpio_read = ad5593r_gpio_read, | |
125 | }; | |
126 | ||
127 | static int ad5592r_spi_probe(struct spi_device *spi) | |
128 | { | |
129 | const struct spi_device_id *id = spi_get_device_id(spi); | |
130 | ||
131 | return ad5592r_probe(&spi->dev, id->name, &ad5592r_rw_ops); | |
132 | } | |
133 | ||
134 | static int ad5592r_spi_remove(struct spi_device *spi) | |
135 | { | |
136 | return ad5592r_remove(&spi->dev); | |
137 | } | |
138 | ||
139 | static const struct spi_device_id ad5592r_spi_ids[] = { | |
140 | { .name = "ad5592r", }, | |
141 | {} | |
142 | }; | |
143 | MODULE_DEVICE_TABLE(spi, ad5592r_spi_ids); | |
144 | ||
145 | static const struct of_device_id ad5592r_of_match[] = { | |
146 | { .compatible = "adi,ad5592r", }, | |
147 | {}, | |
148 | }; | |
149 | MODULE_DEVICE_TABLE(of, ad5592r_of_match); | |
150 | ||
b0570bce MH |
151 | static const struct acpi_device_id ad5592r_acpi_match[] = { |
152 | {"ADS5592", }, | |
153 | { }, | |
154 | }; | |
155 | MODULE_DEVICE_TABLE(acpi, ad5592r_acpi_match); | |
156 | ||
56ca9db8 PC |
157 | static struct spi_driver ad5592r_spi_driver = { |
158 | .driver = { | |
159 | .name = "ad5592r", | |
160 | .of_match_table = of_match_ptr(ad5592r_of_match), | |
b0570bce | 161 | .acpi_match_table = ACPI_PTR(ad5592r_acpi_match), |
56ca9db8 PC |
162 | }, |
163 | .probe = ad5592r_spi_probe, | |
164 | .remove = ad5592r_spi_remove, | |
165 | .id_table = ad5592r_spi_ids, | |
166 | }; | |
167 | module_spi_driver(ad5592r_spi_driver); | |
168 | ||
169 | MODULE_AUTHOR("Paul Cercueil <paul.cercueil@analog.com>"); | |
170 | MODULE_DESCRIPTION("Analog Devices AD5592R multi-channel converters"); | |
171 | MODULE_LICENSE("GPL v2"); |