Commit | Line | Data |
---|---|---|
66bfc528 HC |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * | |
4 | * x9250.c -- Renesas X9250 potentiometers IIO driver | |
5 | * | |
6 | * Copyright 2023 CS GROUP France | |
7 | * | |
8 | * Author: Herve Codina <herve.codina@bootlin.com> | |
9 | */ | |
10 | ||
11 | #include <linux/delay.h> | |
12 | #include <linux/gpio/consumer.h> | |
13 | #include <linux/iio/iio.h> | |
14 | #include <linux/limits.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/regulator/consumer.h> | |
17 | #include <linux/slab.h> | |
18 | #include <linux/spi/spi.h> | |
19 | ||
20 | struct x9250_cfg { | |
21 | const char *name; | |
22 | int kohms; | |
23 | }; | |
24 | ||
25 | struct x9250 { | |
26 | struct spi_device *spi; | |
27 | const struct x9250_cfg *cfg; | |
28 | struct gpio_desc *wp_gpio; | |
29 | }; | |
30 | ||
31 | #define X9250_ID 0x50 | |
32 | #define X9250_CMD_RD_WCR(_p) (0x90 | (_p)) | |
33 | #define X9250_CMD_WR_WCR(_p) (0xa0 | (_p)) | |
34 | ||
35 | static int x9250_write8(struct x9250 *x9250, u8 cmd, u8 val) | |
36 | { | |
37 | u8 txbuf[3]; | |
38 | ||
39 | txbuf[0] = X9250_ID; | |
40 | txbuf[1] = cmd; | |
41 | txbuf[2] = val; | |
42 | ||
43 | return spi_write_then_read(x9250->spi, txbuf, ARRAY_SIZE(txbuf), NULL, 0); | |
44 | } | |
45 | ||
46 | static int x9250_read8(struct x9250 *x9250, u8 cmd, u8 *val) | |
47 | { | |
48 | u8 txbuf[2]; | |
49 | ||
50 | txbuf[0] = X9250_ID; | |
51 | txbuf[1] = cmd; | |
52 | ||
53 | return spi_write_then_read(x9250->spi, txbuf, ARRAY_SIZE(txbuf), val, 1); | |
54 | } | |
55 | ||
56 | #define X9250_CHANNEL(ch) { \ | |
57 | .type = IIO_RESISTANCE, \ | |
58 | .indexed = 1, \ | |
59 | .output = 1, \ | |
60 | .channel = (ch), \ | |
61 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | |
62 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ | |
63 | .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_RAW), \ | |
64 | } | |
65 | ||
66 | static const struct iio_chan_spec x9250_channels[] = { | |
67 | X9250_CHANNEL(0), | |
68 | X9250_CHANNEL(1), | |
69 | X9250_CHANNEL(2), | |
70 | X9250_CHANNEL(3), | |
71 | }; | |
72 | ||
73 | static int x9250_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, | |
74 | int *val, int *val2, long mask) | |
75 | { | |
76 | struct x9250 *x9250 = iio_priv(indio_dev); | |
77 | int ch = chan->channel; | |
78 | int ret; | |
79 | u8 v; | |
80 | ||
81 | switch (mask) { | |
82 | case IIO_CHAN_INFO_RAW: | |
83 | ret = x9250_read8(x9250, X9250_CMD_RD_WCR(ch), &v); | |
84 | if (ret) | |
85 | return ret; | |
86 | *val = v; | |
87 | return IIO_VAL_INT; | |
88 | ||
89 | case IIO_CHAN_INFO_SCALE: | |
90 | *val = 1000 * x9250->cfg->kohms; | |
91 | *val2 = U8_MAX; | |
92 | return IIO_VAL_FRACTIONAL; | |
93 | } | |
94 | ||
95 | return -EINVAL; | |
96 | } | |
97 | ||
98 | static int x9250_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, | |
99 | const int **vals, int *type, int *length, long mask) | |
100 | { | |
101 | static const int range[] = {0, 1, 255}; /* min, step, max */ | |
102 | ||
103 | switch (mask) { | |
104 | case IIO_CHAN_INFO_RAW: | |
105 | *length = ARRAY_SIZE(range); | |
106 | *vals = range; | |
107 | *type = IIO_VAL_INT; | |
108 | return IIO_AVAIL_RANGE; | |
109 | } | |
110 | ||
111 | return -EINVAL; | |
112 | } | |
113 | ||
114 | static int x9250_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, | |
115 | int val, int val2, long mask) | |
116 | { | |
117 | struct x9250 *x9250 = iio_priv(indio_dev); | |
118 | int ch = chan->channel; | |
119 | int ret; | |
120 | ||
121 | if (mask != IIO_CHAN_INFO_RAW) | |
122 | return -EINVAL; | |
123 | ||
124 | if (val > U8_MAX || val < 0) | |
125 | return -EINVAL; | |
126 | ||
127 | gpiod_set_value_cansleep(x9250->wp_gpio, 0); | |
128 | ret = x9250_write8(x9250, X9250_CMD_WR_WCR(ch), val); | |
129 | gpiod_set_value_cansleep(x9250->wp_gpio, 1); | |
130 | ||
131 | return ret; | |
132 | } | |
133 | ||
134 | static const struct iio_info x9250_info = { | |
135 | .read_raw = x9250_read_raw, | |
136 | .read_avail = x9250_read_avail, | |
137 | .write_raw = x9250_write_raw, | |
138 | }; | |
139 | ||
140 | enum x9250_type { | |
141 | X9250T, | |
142 | X9250U, | |
143 | }; | |
144 | ||
145 | static const struct x9250_cfg x9250_cfg[] = { | |
146 | [X9250T] = { .name = "x9250t", .kohms = 100, }, | |
147 | [X9250U] = { .name = "x9250u", .kohms = 50, }, | |
148 | }; | |
149 | ||
150 | static const char *const x9250_regulator_names[] = { | |
151 | "vcc", | |
152 | "avp", | |
153 | "avn", | |
154 | }; | |
155 | ||
156 | static int x9250_probe(struct spi_device *spi) | |
157 | { | |
158 | struct iio_dev *indio_dev; | |
159 | struct x9250 *x9250; | |
160 | int ret; | |
161 | ||
162 | ret = devm_regulator_bulk_get_enable(&spi->dev, ARRAY_SIZE(x9250_regulator_names), | |
163 | x9250_regulator_names); | |
164 | if (ret) | |
165 | return dev_err_probe(&spi->dev, ret, "Failed to get regulators\n"); | |
166 | ||
167 | /* | |
168 | * The x9250 needs a 5ms maximum delay after the power-supplies are set | |
169 | * before performing the first write (1ms for the first read). | |
170 | */ | |
171 | usleep_range(5000, 6000); | |
172 | ||
173 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*x9250)); | |
174 | if (!indio_dev) | |
175 | return -ENOMEM; | |
176 | ||
177 | x9250 = iio_priv(indio_dev); | |
178 | x9250->spi = spi; | |
179 | x9250->cfg = spi_get_device_match_data(spi); | |
180 | x9250->wp_gpio = devm_gpiod_get_optional(&spi->dev, "wp", GPIOD_OUT_LOW); | |
181 | if (IS_ERR(x9250->wp_gpio)) | |
182 | return dev_err_probe(&spi->dev, PTR_ERR(x9250->wp_gpio), | |
183 | "failed to get wp gpio\n"); | |
184 | ||
185 | indio_dev->info = &x9250_info; | |
186 | indio_dev->channels = x9250_channels; | |
187 | indio_dev->num_channels = ARRAY_SIZE(x9250_channels); | |
188 | indio_dev->name = x9250->cfg->name; | |
189 | ||
190 | return devm_iio_device_register(&spi->dev, indio_dev); | |
191 | } | |
192 | ||
193 | static const struct of_device_id x9250_of_match[] = { | |
194 | { .compatible = "renesas,x9250t", .data = &x9250_cfg[X9250T]}, | |
195 | { .compatible = "renesas,x9250u", .data = &x9250_cfg[X9250U]}, | |
196 | { } | |
197 | }; | |
198 | MODULE_DEVICE_TABLE(of, x9250_of_match); | |
199 | ||
200 | static const struct spi_device_id x9250_id_table[] = { | |
201 | { "x9250t", (kernel_ulong_t)&x9250_cfg[X9250T] }, | |
202 | { "x9250u", (kernel_ulong_t)&x9250_cfg[X9250U] }, | |
203 | { } | |
204 | }; | |
205 | MODULE_DEVICE_TABLE(spi, x9250_id_table); | |
206 | ||
207 | static struct spi_driver x9250_spi_driver = { | |
208 | .driver = { | |
209 | .name = "x9250", | |
210 | .of_match_table = x9250_of_match, | |
211 | }, | |
212 | .id_table = x9250_id_table, | |
213 | .probe = x9250_probe, | |
214 | }; | |
215 | ||
216 | module_spi_driver(x9250_spi_driver); | |
217 | ||
218 | MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>"); | |
219 | MODULE_DESCRIPTION("X9250 ALSA SoC driver"); | |
220 | MODULE_LICENSE("GPL"); |