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