]>
Commit | Line | Data |
---|---|---|
9bbccbe1 JE |
1 | /* |
2 | * IIO DAC driver for NXP LPC18xx DAC | |
3 | * | |
4 | * Copyright (C) 2016 Joachim Eastwood <[email protected]> | |
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 | * UNSUPPORTED hardware features: | |
11 | * - Interrupts | |
12 | * - DMA | |
13 | */ | |
14 | ||
15 | #include <linux/clk.h> | |
16 | #include <linux/err.h> | |
17 | #include <linux/iio/iio.h> | |
18 | #include <linux/iio/driver.h> | |
19 | #include <linux/io.h> | |
20 | #include <linux/iopoll.h> | |
21 | #include <linux/module.h> | |
22 | #include <linux/mutex.h> | |
23 | #include <linux/of.h> | |
24 | #include <linux/of_device.h> | |
25 | #include <linux/platform_device.h> | |
26 | #include <linux/regulator/consumer.h> | |
27 | ||
28 | /* LPC18XX DAC registers and bits */ | |
29 | #define LPC18XX_DAC_CR 0x000 | |
30 | #define LPC18XX_DAC_CR_VALUE_SHIFT 6 | |
31 | #define LPC18XX_DAC_CR_VALUE_MASK 0x3ff | |
32 | #define LPC18XX_DAC_CR_BIAS BIT(16) | |
33 | #define LPC18XX_DAC_CTRL 0x004 | |
34 | #define LPC18XX_DAC_CTRL_DMA_ENA BIT(3) | |
35 | ||
36 | struct lpc18xx_dac { | |
37 | struct regulator *vref; | |
38 | void __iomem *base; | |
39 | struct mutex lock; | |
40 | struct clk *clk; | |
41 | }; | |
42 | ||
43 | static const struct iio_chan_spec lpc18xx_dac_iio_channels[] = { | |
44 | { | |
45 | .type = IIO_VOLTAGE, | |
46 | .output = 1, | |
47 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
48 | BIT(IIO_CHAN_INFO_SCALE), | |
49 | }, | |
50 | }; | |
51 | ||
52 | static int lpc18xx_dac_read_raw(struct iio_dev *indio_dev, | |
53 | struct iio_chan_spec const *chan, | |
54 | int *val, int *val2, long mask) | |
55 | { | |
56 | struct lpc18xx_dac *dac = iio_priv(indio_dev); | |
57 | u32 reg; | |
58 | ||
59 | switch (mask) { | |
60 | case IIO_CHAN_INFO_RAW: | |
61 | reg = readl(dac->base + LPC18XX_DAC_CR); | |
62 | *val = reg >> LPC18XX_DAC_CR_VALUE_SHIFT; | |
63 | *val &= LPC18XX_DAC_CR_VALUE_MASK; | |
64 | ||
65 | return IIO_VAL_INT; | |
66 | ||
67 | case IIO_CHAN_INFO_SCALE: | |
68 | *val = regulator_get_voltage(dac->vref) / 1000; | |
69 | *val2 = 10; | |
70 | ||
71 | return IIO_VAL_FRACTIONAL_LOG2; | |
72 | } | |
73 | ||
74 | return -EINVAL; | |
75 | } | |
76 | ||
77 | static int lpc18xx_dac_write_raw(struct iio_dev *indio_dev, | |
78 | struct iio_chan_spec const *chan, | |
79 | int val, int val2, long mask) | |
80 | { | |
81 | struct lpc18xx_dac *dac = iio_priv(indio_dev); | |
82 | u32 reg; | |
83 | ||
84 | switch (mask) { | |
85 | case IIO_CHAN_INFO_RAW: | |
86 | if (val < 0 || val > LPC18XX_DAC_CR_VALUE_MASK) | |
87 | return -EINVAL; | |
88 | ||
89 | reg = LPC18XX_DAC_CR_BIAS; | |
90 | reg |= val << LPC18XX_DAC_CR_VALUE_SHIFT; | |
91 | ||
92 | mutex_lock(&dac->lock); | |
93 | writel(reg, dac->base + LPC18XX_DAC_CR); | |
94 | writel(LPC18XX_DAC_CTRL_DMA_ENA, dac->base + LPC18XX_DAC_CTRL); | |
95 | mutex_unlock(&dac->lock); | |
96 | ||
97 | return 0; | |
98 | } | |
99 | ||
100 | return -EINVAL; | |
101 | } | |
102 | ||
103 | static const struct iio_info lpc18xx_dac_info = { | |
104 | .read_raw = lpc18xx_dac_read_raw, | |
105 | .write_raw = lpc18xx_dac_write_raw, | |
9bbccbe1 JE |
106 | }; |
107 | ||
108 | static int lpc18xx_dac_probe(struct platform_device *pdev) | |
109 | { | |
110 | struct iio_dev *indio_dev; | |
111 | struct lpc18xx_dac *dac; | |
112 | struct resource *res; | |
113 | int ret; | |
114 | ||
115 | indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*dac)); | |
116 | if (!indio_dev) | |
117 | return -ENOMEM; | |
118 | ||
119 | platform_set_drvdata(pdev, indio_dev); | |
120 | dac = iio_priv(indio_dev); | |
121 | mutex_init(&dac->lock); | |
122 | ||
123 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
124 | dac->base = devm_ioremap_resource(&pdev->dev, res); | |
125 | if (IS_ERR(dac->base)) | |
126 | return PTR_ERR(dac->base); | |
127 | ||
128 | dac->clk = devm_clk_get(&pdev->dev, NULL); | |
129 | if (IS_ERR(dac->clk)) { | |
130 | dev_err(&pdev->dev, "error getting clock\n"); | |
131 | return PTR_ERR(dac->clk); | |
132 | } | |
133 | ||
134 | dac->vref = devm_regulator_get(&pdev->dev, "vref"); | |
135 | if (IS_ERR(dac->vref)) { | |
136 | dev_err(&pdev->dev, "error getting regulator\n"); | |
137 | return PTR_ERR(dac->vref); | |
138 | } | |
139 | ||
140 | indio_dev->name = dev_name(&pdev->dev); | |
141 | indio_dev->dev.parent = &pdev->dev; | |
142 | indio_dev->info = &lpc18xx_dac_info; | |
143 | indio_dev->modes = INDIO_DIRECT_MODE; | |
144 | indio_dev->channels = lpc18xx_dac_iio_channels; | |
145 | indio_dev->num_channels = ARRAY_SIZE(lpc18xx_dac_iio_channels); | |
146 | ||
147 | ret = regulator_enable(dac->vref); | |
148 | if (ret) { | |
149 | dev_err(&pdev->dev, "unable to enable regulator\n"); | |
150 | return ret; | |
151 | } | |
152 | ||
153 | ret = clk_prepare_enable(dac->clk); | |
154 | if (ret) { | |
155 | dev_err(&pdev->dev, "unable to enable clock\n"); | |
156 | goto dis_reg; | |
157 | } | |
158 | ||
159 | writel(0, dac->base + LPC18XX_DAC_CTRL); | |
160 | writel(0, dac->base + LPC18XX_DAC_CR); | |
161 | ||
162 | ret = iio_device_register(indio_dev); | |
163 | if (ret) { | |
164 | dev_err(&pdev->dev, "unable to register device\n"); | |
165 | goto dis_clk; | |
166 | } | |
167 | ||
168 | return 0; | |
169 | ||
170 | dis_clk: | |
171 | clk_disable_unprepare(dac->clk); | |
172 | dis_reg: | |
173 | regulator_disable(dac->vref); | |
174 | return ret; | |
175 | } | |
176 | ||
177 | static int lpc18xx_dac_remove(struct platform_device *pdev) | |
178 | { | |
179 | struct iio_dev *indio_dev = platform_get_drvdata(pdev); | |
180 | struct lpc18xx_dac *dac = iio_priv(indio_dev); | |
181 | ||
182 | iio_device_unregister(indio_dev); | |
183 | ||
184 | writel(0, dac->base + LPC18XX_DAC_CTRL); | |
185 | clk_disable_unprepare(dac->clk); | |
186 | regulator_disable(dac->vref); | |
187 | ||
188 | return 0; | |
189 | } | |
190 | ||
191 | static const struct of_device_id lpc18xx_dac_match[] = { | |
192 | { .compatible = "nxp,lpc1850-dac" }, | |
193 | { /* sentinel */ } | |
194 | }; | |
195 | MODULE_DEVICE_TABLE(of, lpc18xx_dac_match); | |
196 | ||
197 | static struct platform_driver lpc18xx_dac_driver = { | |
198 | .probe = lpc18xx_dac_probe, | |
199 | .remove = lpc18xx_dac_remove, | |
200 | .driver = { | |
201 | .name = "lpc18xx-dac", | |
202 | .of_match_table = lpc18xx_dac_match, | |
203 | }, | |
204 | }; | |
205 | module_platform_driver(lpc18xx_dac_driver); | |
206 | ||
207 | MODULE_DESCRIPTION("LPC18xx DAC driver"); | |
208 | MODULE_AUTHOR("Joachim Eastwood <[email protected]>"); | |
209 | MODULE_LICENSE("GPL v2"); |