1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * max5487.c - Support for MAX5487, MAX5488, MAX5489 digital potentiometers |
4 | * |
5 | * Copyright (C) 2016 Cristina-Gabriela Moraru <cristina.moraru09@gmail.com> |
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, buf: (const void *) &cmd, len: 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(spi: data->spi, cmd: 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, |
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(sdev: spi); |
89 | int ret; |
90 | |
91 | indio_dev = devm_iio_device_alloc(parent: &spi->dev, sizeof_priv: sizeof(*data)); |
92 | if (!indio_dev) |
93 | return -ENOMEM; |
94 | |
95 | spi_set_drvdata(spi, data: 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->modes = INDIO_DIRECT_MODE; |
104 | indio_dev->channels = max5487_channels; |
105 | indio_dev->num_channels = ARRAY_SIZE(max5487_channels); |
106 | |
107 | /* restore both wiper regs from NV regs */ |
108 | ret = max5487_write_cmd(spi: data->spi, MAX5487_COPY_NV_TO_AB); |
109 | if (ret < 0) |
110 | return ret; |
111 | |
112 | return iio_device_register(indio_dev); |
113 | } |
114 | |
115 | static void max5487_spi_remove(struct spi_device *spi) |
116 | { |
117 | struct iio_dev *indio_dev = spi_get_drvdata(spi); |
118 | int ret; |
119 | |
120 | iio_device_unregister(indio_dev); |
121 | |
122 | /* save both wiper regs to NV regs */ |
123 | ret = max5487_write_cmd(spi, MAX5487_COPY_AB_TO_NV); |
124 | if (ret) |
125 | dev_warn(&spi->dev, "Failed to save wiper regs to NV regs\n" ); |
126 | } |
127 | |
128 | static const struct spi_device_id max5487_id[] = { |
129 | { "MAX5487" , 10 }, |
130 | { "MAX5488" , 50 }, |
131 | { "MAX5489" , 100 }, |
132 | { } |
133 | }; |
134 | MODULE_DEVICE_TABLE(spi, max5487_id); |
135 | |
136 | static const struct acpi_device_id max5487_acpi_match[] = { |
137 | { "MAX5487" , 10 }, |
138 | { "MAX5488" , 50 }, |
139 | { "MAX5489" , 100 }, |
140 | { }, |
141 | }; |
142 | MODULE_DEVICE_TABLE(acpi, max5487_acpi_match); |
143 | |
144 | static struct spi_driver max5487_driver = { |
145 | .driver = { |
146 | .name = "max5487" , |
147 | .acpi_match_table = ACPI_PTR(max5487_acpi_match), |
148 | }, |
149 | .id_table = max5487_id, |
150 | .probe = max5487_spi_probe, |
151 | .remove = max5487_spi_remove |
152 | }; |
153 | module_spi_driver(max5487_driver); |
154 | |
155 | MODULE_AUTHOR("Cristina-Gabriela Moraru <cristina.moraru09@gmail.com>" ); |
156 | MODULE_DESCRIPTION("max5487 SPI driver" ); |
157 | MODULE_LICENSE("GPL v2" ); |
158 | |