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 = { | |
170 | .driver_module = THIS_MODULE, | |
171 | .read_raw = &vf610_read_raw, | |
172 | .write_raw = &vf610_write_raw, | |
173 | }; | |
174 | ||
175 | static const struct of_device_id vf610_dac_match[] = { | |
176 | { .compatible = "fsl,vf610-dac", }, | |
177 | { /* sentinel */ } | |
178 | }; | |
179 | MODULE_DEVICE_TABLE(of, vf610_dac_match); | |
180 | ||
181 | static int vf610_dac_probe(struct platform_device *pdev) | |
182 | { | |
183 | struct iio_dev *indio_dev; | |
184 | struct vf610_dac *info; | |
185 | struct resource *mem; | |
186 | int ret; | |
187 | ||
188 | indio_dev = devm_iio_device_alloc(&pdev->dev, | |
189 | sizeof(struct vf610_dac)); | |
190 | if (!indio_dev) { | |
191 | dev_err(&pdev->dev, "Failed allocating iio device\n"); | |
192 | return -ENOMEM; | |
193 | } | |
194 | ||
195 | info = iio_priv(indio_dev); | |
196 | info->dev = &pdev->dev; | |
197 | ||
198 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
199 | info->regs = devm_ioremap_resource(&pdev->dev, mem); | |
200 | if (IS_ERR(info->regs)) | |
201 | return PTR_ERR(info->regs); | |
202 | ||
203 | info->clk = devm_clk_get(&pdev->dev, "dac"); | |
204 | if (IS_ERR(info->clk)) { | |
205 | dev_err(&pdev->dev, "Failed getting clock, err = %ld\n", | |
206 | PTR_ERR(info->clk)); | |
207 | return PTR_ERR(info->clk); | |
208 | } | |
209 | ||
210 | platform_set_drvdata(pdev, indio_dev); | |
211 | ||
212 | indio_dev->name = dev_name(&pdev->dev); | |
213 | indio_dev->dev.parent = &pdev->dev; | |
214 | indio_dev->dev.of_node = pdev->dev.of_node; | |
215 | indio_dev->info = &vf610_dac_iio_info; | |
216 | indio_dev->modes = INDIO_DIRECT_MODE; | |
217 | indio_dev->channels = vf610_dac_iio_channels; | |
218 | indio_dev->num_channels = ARRAY_SIZE(vf610_dac_iio_channels); | |
219 | ||
220 | ret = clk_prepare_enable(info->clk); | |
221 | if (ret) { | |
222 | dev_err(&pdev->dev, | |
223 | "Could not prepare or enable the clock\n"); | |
224 | return ret; | |
225 | } | |
226 | ||
227 | vf610_dac_init(info); | |
228 | ||
229 | ret = iio_device_register(indio_dev); | |
230 | if (ret) { | |
231 | dev_err(&pdev->dev, "Couldn't register the device\n"); | |
232 | goto error_iio_device_register; | |
233 | } | |
234 | ||
235 | return 0; | |
236 | ||
237 | error_iio_device_register: | |
238 | clk_disable_unprepare(info->clk); | |
239 | ||
240 | return ret; | |
241 | } | |
242 | ||
243 | static int vf610_dac_remove(struct platform_device *pdev) | |
244 | { | |
245 | struct iio_dev *indio_dev = platform_get_drvdata(pdev); | |
246 | struct vf610_dac *info = iio_priv(indio_dev); | |
247 | ||
248 | iio_device_unregister(indio_dev); | |
249 | vf610_dac_exit(info); | |
250 | clk_disable_unprepare(info->clk); | |
251 | ||
252 | return 0; | |
253 | } | |
254 | ||
255 | #ifdef CONFIG_PM_SLEEP | |
256 | static int vf610_dac_suspend(struct device *dev) | |
257 | { | |
258 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
259 | struct vf610_dac *info = iio_priv(indio_dev); | |
260 | ||
261 | vf610_dac_exit(info); | |
262 | clk_disable_unprepare(info->clk); | |
263 | ||
264 | return 0; | |
265 | } | |
266 | ||
267 | static int vf610_dac_resume(struct device *dev) | |
268 | { | |
269 | struct iio_dev *indio_dev = dev_get_drvdata(dev); | |
270 | struct vf610_dac *info = iio_priv(indio_dev); | |
271 | int ret; | |
272 | ||
273 | ret = clk_prepare_enable(info->clk); | |
274 | if (ret) | |
275 | return ret; | |
276 | ||
277 | vf610_dac_init(info); | |
278 | ||
279 | return 0; | |
280 | } | |
281 | #endif | |
282 | ||
283 | static SIMPLE_DEV_PM_OPS(vf610_dac_pm_ops, vf610_dac_suspend, vf610_dac_resume); | |
284 | ||
285 | static struct platform_driver vf610_dac_driver = { | |
286 | .probe = vf610_dac_probe, | |
287 | .remove = vf610_dac_remove, | |
288 | .driver = { | |
289 | .name = "vf610-dac", | |
290 | .of_match_table = vf610_dac_match, | |
291 | .pm = &vf610_dac_pm_ops, | |
292 | }, | |
293 | }; | |
294 | module_platform_driver(vf610_dac_driver); | |
295 | ||
296 | MODULE_AUTHOR("Sanchayan Maity <sanchayan.maity@toradex.com>"); | |
297 | MODULE_DESCRIPTION("Freescale VF610 DAC driver"); | |
298 | MODULE_LICENSE("GPL v2"); |