Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
9a47894f CM |
2 | /* |
3 | * max5487.c - Support for MAX5487, MAX5488, MAX5489 digital potentiometers | |
4 | * | |
5 | * Copyright (C) 2016 Cristina-Gabriela Moraru <cristina.moraru09@gmail.com> | |
9a47894f CM |
6 | */ |
7 | #include <linux/module.h> | |
8 | #include <linux/spi/spi.h> | |
9 | #include <linux/acpi.h> | |
10 | ||
11 | #include <linux/iio/sysfs.h> | |
12 | #include <linux/iio/iio.h> | |
13 | ||
14 | #define MAX5487_WRITE_WIPER_A (0x01 << 8) | |
15 | #define MAX5487_WRITE_WIPER_B (0x02 << 8) | |
16 | ||
17 | /* copy both wiper regs to NV regs */ | |
18 | #define MAX5487_COPY_AB_TO_NV (0x23 << 8) | |
19 | /* copy both NV regs to wiper regs */ | |
20 | #define MAX5487_COPY_NV_TO_AB (0x33 << 8) | |
21 | ||
22 | #define MAX5487_MAX_POS 255 | |
23 | ||
24 | struct max5487_data { | |
25 | struct spi_device *spi; | |
26 | int kohms; | |
27 | }; | |
28 | ||
29 | #define MAX5487_CHANNEL(ch, addr) { \ | |
30 | .type = IIO_RESISTANCE, \ | |
31 | .indexed = 1, \ | |
32 | .output = 1, \ | |
33 | .channel = ch, \ | |
34 | .address = addr, \ | |
35 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | |
36 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ | |
37 | } | |
38 | ||
39 | static const struct iio_chan_spec max5487_channels[] = { | |
40 | MAX5487_CHANNEL(0, MAX5487_WRITE_WIPER_A), | |
41 | MAX5487_CHANNEL(1, MAX5487_WRITE_WIPER_B), | |
42 | }; | |
43 | ||
44 | static int max5487_write_cmd(struct spi_device *spi, u16 cmd) | |
45 | { | |
46 | return spi_write(spi, (const void *) &cmd, sizeof(u16)); | |
47 | } | |
48 | ||
49 | static int max5487_read_raw(struct iio_dev *indio_dev, | |
50 | struct iio_chan_spec const *chan, | |
51 | int *val, int *val2, long mask) | |
52 | { | |
53 | struct max5487_data *data = iio_priv(indio_dev); | |
54 | ||
55 | if (mask != IIO_CHAN_INFO_SCALE) | |
56 | return -EINVAL; | |
57 | ||
58 | *val = 1000 * data->kohms; | |
59 | *val2 = MAX5487_MAX_POS; | |
60 | ||
61 | return IIO_VAL_FRACTIONAL; | |
62 | } | |
63 | ||
64 | static int max5487_write_raw(struct iio_dev *indio_dev, | |
65 | struct iio_chan_spec const *chan, | |
66 | int val, int val2, long mask) | |
67 | { | |
68 | struct max5487_data *data = iio_priv(indio_dev); | |
69 | ||
70 | if (mask != IIO_CHAN_INFO_RAW) | |
71 | return -EINVAL; | |
72 | ||
73 | if (val < 0 || val > MAX5487_MAX_POS) | |
74 | return -EINVAL; | |
75 | ||
76 | return max5487_write_cmd(data->spi, chan->address | val); | |
77 | } | |
78 | ||
79 | static const struct iio_info max5487_info = { | |
80 | .read_raw = max5487_read_raw, | |
81 | .write_raw = max5487_write_raw, | |
9a47894f CM |
82 | }; |
83 | ||
84 | static int max5487_spi_probe(struct spi_device *spi) | |
85 | { | |
86 | struct iio_dev *indio_dev; | |
87 | struct max5487_data *data; | |
88 | const struct spi_device_id *id = spi_get_device_id(spi); | |
89 | int ret; | |
90 | ||
91 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); | |
92 | if (!indio_dev) | |
93 | return -ENOMEM; | |
94 | ||
95 | dev_set_drvdata(&spi->dev, indio_dev); | |
96 | data = iio_priv(indio_dev); | |
97 | ||
98 | data->spi = spi; | |
99 | data->kohms = id->driver_data; | |
100 | ||
101 | indio_dev->info = &max5487_info; | |
102 | indio_dev->name = id->name; | |
103 | indio_dev->dev.parent = &spi->dev; | |
104 | indio_dev->modes = INDIO_DIRECT_MODE; | |
105 | indio_dev->channels = max5487_channels; | |
106 | indio_dev->num_channels = ARRAY_SIZE(max5487_channels); | |
107 | ||
108 | /* restore both wiper regs from NV regs */ | |
109 | ret = max5487_write_cmd(data->spi, MAX5487_COPY_NV_TO_AB); | |
110 | if (ret < 0) | |
111 | return ret; | |
112 | ||
113 | return iio_device_register(indio_dev); | |
114 | } | |
115 | ||
116 | static int max5487_spi_remove(struct spi_device *spi) | |
117 | { | |
118 | struct iio_dev *indio_dev = dev_get_drvdata(&spi->dev); | |
119 | ||
120 | iio_device_unregister(indio_dev); | |
121 | ||
122 | /* save both wiper regs to NV regs */ | |
123 | return max5487_write_cmd(spi, MAX5487_COPY_AB_TO_NV); | |
124 | } | |
125 | ||
126 | static const struct spi_device_id max5487_id[] = { | |
127 | { "MAX5487", 10 }, | |
128 | { "MAX5488", 50 }, | |
129 | { "MAX5489", 100 }, | |
130 | { } | |
131 | }; | |
132 | MODULE_DEVICE_TABLE(spi, max5487_id); | |
133 | ||
134 | static const struct acpi_device_id max5487_acpi_match[] = { | |
135 | { "MAX5487", 10 }, | |
136 | { "MAX5488", 50 }, | |
137 | { "MAX5489", 100 }, | |
138 | { }, | |
139 | }; | |
140 | MODULE_DEVICE_TABLE(acpi, max5487_acpi_match); | |
141 | ||
142 | static struct spi_driver max5487_driver = { | |
143 | .driver = { | |
144 | .name = "max5487", | |
9a47894f CM |
145 | .acpi_match_table = ACPI_PTR(max5487_acpi_match), |
146 | }, | |
147 | .id_table = max5487_id, | |
148 | .probe = max5487_spi_probe, | |
149 | .remove = max5487_spi_remove | |
150 | }; | |
151 | module_spi_driver(max5487_driver); | |
152 | ||
153 | MODULE_AUTHOR("Cristina-Gabriela Moraru <cristina.moraru09@gmail.com>"); | |
154 | MODULE_DESCRIPTION("max5487 SPI driver"); | |
155 | MODULE_LICENSE("GPL v2"); |