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