]>
Commit | Line | Data |
---|---|---|
1b983bf4 SM |
1 | /* |
2 | * Freescale Vybrid vf610 DAC driver | |
3 | * | |
4 | * Copyright 2016 Toradex AG | |
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 as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | */ | |
16 | ||
17 | #include <linux/clk.h> | |
18 | #include <linux/err.h> | |
19 | #include <linux/interrupt.h> | |
20 | #include <linux/io.h> | |
21 | #include <linux/kernel.h> | |
22 | #include <linux/module.h> | |
23 | #include <linux/platform_device.h> | |
24 | #include <linux/regulator/consumer.h> | |
25 | #include <linux/slab.h> | |
26 | ||
27 | #include <linux/iio/iio.h> | |
28 | #include <linux/iio/sysfs.h> | |
29 | ||
30 | #define VF610_DACx_STATCTRL 0x20 | |
31 | ||
32 | #define VF610_DAC_DACEN BIT(15) | |
33 | #define VF610_DAC_DACRFS BIT(14) | |
34 | #define VF610_DAC_LPEN BIT(11) | |
35 | ||
36 | #define VF610_DAC_DAT0(x) ((x) & 0xFFF) | |
37 | ||
38 | enum vf610_conversion_mode_sel { | |
39 | VF610_DAC_CONV_HIGH_POWER, | |
40 | VF610_DAC_CONV_LOW_POWER, | |
41 | }; | |
42 | ||
43 | struct vf610_dac { | |
44 | struct clk *clk; | |
45 | struct device *dev; | |
46 | enum vf610_conversion_mode_sel conv_mode; | |
47 | void __iomem *regs; | |
48 | }; | |
49 | ||
50 | static void vf610_dac_init(struct vf610_dac *info) | |
51 | { | |
52 | int val; | |
53 | ||
54 | info->conv_mode = VF610_DAC_CONV_LOW_POWER; | |
55 | val = VF610_DAC_DACEN | VF610_DAC_DACRFS | | |
56 | VF610_DAC_LPEN; | |
57 | writel(val, info->regs + VF610_DACx_STATCTRL); | |
58 | } | |
59 | ||
60 | static void vf610_dac_exit(struct vf610_dac *info) | |
61 | { | |
62 | int val; | |
63 | ||
64 | val = readl(info->regs + VF610_DACx_STATCTRL); | |
65 | val &= ~VF610_DAC_DACEN; | |
66 | writel(val, info->regs + VF610_DACx_STATCTRL); | |
67 | } | |
68 | ||
69 | static int vf610_set_conversion_mode(struct iio_dev *indio_dev, | |
70 | const struct iio_chan_spec *chan, | |
71 | unsigned int mode) | |
72 | { | |
73 | struct vf610_dac *info = iio_priv(indio_dev); | |
74 | int val; | |
75 | ||
76 | mutex_lock(&indio_dev->mlock); | |
77 | info->conv_mode = mode; | |
78 | val = readl(info->regs + VF610_DACx_STATCTRL); | |
79 | if (mode) | |
80 | val |= VF610_DAC_LPEN; | |
81 | else | |
82 | val &= ~VF610_DAC_LPEN; | |
83 | writel(val, info->regs + VF610_DACx_STATCTRL); | |
84 | mutex_unlock(&indio_dev->mlock); | |
85 | ||
86 | return 0; | |
87 | } | |
88 | ||
89 | static int vf610_get_conversion_mode(struct iio_dev *indio_dev, | |
90 | const struct iio_chan_spec *chan) | |
91 | { | |
92 | struct vf610_dac *info = iio_priv(indio_dev); | |
93 | ||
94 | return info->conv_mode; | |
95 | } | |
96 | ||
97 | static const char * const vf610_conv_modes[] = { "high-power", "low-power" }; | |
98 | ||
99 | static const struct iio_enum vf610_conversion_mode = { | |
100 | .items = vf610_conv_modes, | |
101 | .num_items = ARRAY_SIZE(vf610_conv_modes), | |
102 | .get = vf610_get_conversion_mode, | |
103 | .set = vf610_set_conversion_mode, | |
104 | }; | |
105 | ||
106 | static const struct iio_chan_spec_ext_info vf610_ext_info[] = { | |
107 | IIO_ENUM("conversion_mode", IIO_SHARED_BY_DIR, | |
108 | &vf610_conversion_mode), | |
109 | {}, | |
110 | }; | |
111 | ||
112 | #define VF610_DAC_CHAN(_chan_type) { \ | |
113 | .type = (_chan_type), \ | |
114 | .output = 1, \ | |
115 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | |
116 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ | |
117 | .ext_info = vf610_ext_info, \ | |
118 | } | |
119 | ||
120 | static const struct iio_chan_spec vf610_dac_iio_channels[] = { | |
121 | VF610_DAC_CHAN(IIO_VOLTAGE), | |
122 | }; | |
123 | ||
124 | static int vf610_read_raw(struct iio_dev *indio_dev, | |
125 | struct iio_chan_spec const *chan, | |
126 | int *val, int *val2, | |
127 | long mask) | |
128 | { | |
129 | struct vf610_dac *info = iio_priv(indio_dev); | |
130 | ||
131 | switch (mask) { | |
132 | case IIO_CHAN_INFO_RAW: | |
133 | *val = VF610_DAC_DAT0(readl(info->regs)); | |
134 | return IIO_VAL_INT; | |
135 | case IIO_CHAN_INFO_SCALE: | |
136 | /* | |
137 | * DACRFS is always 1 for valid reference and typical | |
138 | * reference voltage as per Vybrid datasheet is 3.3V | |
139 | * from section 9.1.2.1 of Vybrid datasheet | |
140 | */ | |
141 | *val = 3300 /* mV */; | |
142 | *val2 = 12; | |
143 | return IIO_VAL_FRACTIONAL_LOG2; | |
144 | ||
145 | default: | |
146 | return -EINVAL; | |
147 | } | |
148 | } | |
149 | ||
150 | static int vf610_write_raw(struct iio_dev *indio_dev, | |
151 | struct iio_chan_spec const *chan, | |
152 | int val, int val2, | |
153 | long mask) | |
154 | { | |
155 | struct vf610_dac *info = iio_priv(indio_dev); | |
156 | ||
157 | switch (mask) { | |
158 | case IIO_CHAN_INFO_RAW: | |
159 | mutex_lock(&indio_dev->mlock); | |
160 | writel(VF610_DAC_DAT0(val), info->regs); | |
161 | mutex_unlock(&indio_dev->mlock); | |
162 | return 0; | |
163 | ||
164 | default: | |
165 | return -EINVAL; | |
166 | } | |
167 | } | |
168 | ||
169 | static const struct iio_info vf610_dac_iio_info = { | |
1b983bf4 SM |
170 | .read_raw = &vf610_read_raw, |
171 | .write_raw = &vf610_write_raw, | |
172 | }; | |
173 | ||
174 | static const struct of_device_id vf610_dac_match[] = { | |
175 | { .compatible = "fsl,vf610-dac", }, | |
176 | { /* sentinel */ } | |
177 | }; | |
178 | MODULE_DEVICE_TABLE(of, vf610_dac_match); | |
179 | ||
180 | static int vf610_dac_probe(struct platform_device *pdev) | |
181 | { | |
182 | struct iio_dev *indio_dev; | |
183 | struct vf610_dac *info; | |
184 | struct resource *mem; | |
185 | int ret; | |
186 | ||
187 | indio_dev = devm_iio_device_alloc(&pdev->dev, | |
188 | sizeof(struct vf610_dac)); | |
189 | if (!indio_dev) { | |
190 | dev_err(&pdev->dev, "Failed allocating iio device\n"); | |
191 | return -ENOMEM; | |
192 | } | |
193 | ||
194 | info = iio_priv(indio_dev); | |
195 | info->dev = &pdev->dev; | |
196 | ||
197 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
198 | info->regs = devm_ioremap_resource(&pdev->dev, mem); | |
199 | if (IS_ERR(info->regs)) | |
200 | return PTR_ERR(info->regs); | |
201 | ||
202 | info->clk = devm_clk_get(&pdev->dev, "dac"); | |
203 | if (IS_ERR(info->clk)) { | |
204 | dev_err(&pdev->dev, "Failed getting clock, err = %ld\n", | |
205 | PTR_ERR(info->clk)); | |
206 | return PTR_ERR(info->clk); | |
207 | } | |
208 | ||
209 | platform_set_drvdata(pdev, indio_dev); | |
210 | ||
211 | indio_dev->name = dev_name(&pdev->dev); | |
212 | indio_dev->dev.parent = &pdev->dev; | |
213 | indio_dev->dev.of_node = pdev->dev.of_node; | |
214 | indio_dev->info = &vf610_dac_iio_info; | |
215 | indio_dev->modes = INDIO_DIRECT_MODE; | |
216 | indio_dev->channels = vf610_dac_iio_channels; | |
217 | indio_dev->num_channels = ARRAY_SIZE(vf610_dac_iio_channels); | |
218 | ||
219 | ret = clk_prepare_enable(info->clk); | |
220 | if (ret) { | |
221 | dev_err(&pdev->dev, | |
222 | "Could not prepare or enable the clock\n"); | |
223 | return ret; | |
224 | } | |
225 | ||
226 | vf610_dac_init(info); | |
227 | ||
228 | ret = iio_device_register(indio_dev); | |
229 | if (ret) { | |
230 | dev_err(&pdev->dev, "Couldn't register the device\n"); | |
231 | goto error_iio_device_register; | |
232 | } | |
233 | ||
234 | return 0; | |
235 | ||
236 | error_iio_device_register: | |
237 | clk_disable_unprepare(info->clk); | |
238 | ||
239 | return ret; | |
240 | } | |
241 | ||
242 | static int vf610_dac_remove(struct platform_device *pdev) | |
243 | { | |
244 | struct iio_dev *indio_dev = platform_get_drvdata(pdev); | |
245 | struct vf610_dac *info = iio_priv(indio_dev); | |
246 | ||
247 | iio_device_unregister(indio_dev); | |
248 | vf610_dac_exit(info); | |
249 | clk_disable_unprepare(info->clk); | |
250 | ||
251 | return 0; | |
252 | } | |
253 | ||
254 | #ifdef CONFIG_PM_SLEEP | |
255 | static int vf610_dac_suspend(struct device *dev) | |
256 | { | |
257 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
258 | struct vf610_dac *info = iio_priv(indio_dev); | |
259 | ||
260 | vf610_dac_exit(info); | |
261 | clk_disable_unprepare(info->clk); | |
262 | ||
263 | return 0; | |
264 | } | |
265 | ||
266 | static int vf610_dac_resume(struct device *dev) | |
267 | { | |
268 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
269 | struct vf610_dac *info = iio_priv(indio_dev); | |
270 | int ret; | |
271 | ||
272 | ret = clk_prepare_enable(info->clk); | |
273 | if (ret) | |
274 | return ret; | |
275 | ||
276 | vf610_dac_init(info); | |
277 | ||
278 | return 0; | |
279 | } | |
280 | #endif | |
281 | ||
282 | static SIMPLE_DEV_PM_OPS(vf610_dac_pm_ops, vf610_dac_suspend, vf610_dac_resume); | |
283 | ||
284 | static struct platform_driver vf610_dac_driver = { | |
285 | .probe = vf610_dac_probe, | |
286 | .remove = vf610_dac_remove, | |
287 | .driver = { | |
288 | .name = "vf610-dac", | |
289 | .of_match_table = vf610_dac_match, | |
290 | .pm = &vf610_dac_pm_ops, | |
291 | }, | |
292 | }; | |
293 | module_platform_driver(vf610_dac_driver); | |
294 | ||
295 | MODULE_AUTHOR("Sanchayan Maity <[email protected]>"); | |
296 | MODULE_DESCRIPTION("Freescale VF610 DAC driver"); | |
297 | MODULE_LICENSE("GPL v2"); |