Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
a583c24d JE |
2 | /* |
3 | * IIO ADC driver for NXP LPC18xx ADC | |
4 | * | |
5 | * Copyright (C) 2016 Joachim Eastwood <manabian@gmail.com> | |
6 | * | |
a583c24d JE |
7 | * UNSUPPORTED hardware features: |
8 | * - Hardware triggers | |
9 | * - Burst mode | |
10 | * - Interrupts | |
11 | * - DMA | |
12 | */ | |
13 | ||
14 | #include <linux/clk.h> | |
15 | #include <linux/err.h> | |
16 | #include <linux/iio/iio.h> | |
17 | #include <linux/iio/driver.h> | |
18 | #include <linux/io.h> | |
19 | #include <linux/iopoll.h> | |
7db52e25 | 20 | #include <linux/mod_devicetable.h> |
a583c24d JE |
21 | #include <linux/module.h> |
22 | #include <linux/mutex.h> | |
a583c24d JE |
23 | #include <linux/platform_device.h> |
24 | #include <linux/regulator/consumer.h> | |
25 | ||
26 | /* LPC18XX ADC registers and bits */ | |
27 | #define LPC18XX_ADC_CR 0x000 | |
28 | #define LPC18XX_ADC_CR_CLKDIV_SHIFT 8 | |
29 | #define LPC18XX_ADC_CR_PDN BIT(21) | |
30 | #define LPC18XX_ADC_CR_START_NOW (0x1 << 24) | |
31 | #define LPC18XX_ADC_GDR 0x004 | |
32 | ||
33 | /* Data register bits */ | |
34 | #define LPC18XX_ADC_SAMPLE_SHIFT 6 | |
35 | #define LPC18XX_ADC_SAMPLE_MASK 0x3ff | |
36 | #define LPC18XX_ADC_CONV_DONE BIT(31) | |
37 | ||
38 | /* Clock should be 4.5 MHz or less */ | |
39 | #define LPC18XX_ADC_CLK_TARGET 4500000 | |
40 | ||
41 | struct lpc18xx_adc { | |
42 | struct regulator *vref; | |
43 | void __iomem *base; | |
44 | struct device *dev; | |
45 | struct mutex lock; | |
46 | struct clk *clk; | |
47 | u32 cr_reg; | |
48 | }; | |
49 | ||
50 | #define LPC18XX_ADC_CHAN(_idx) { \ | |
51 | .type = IIO_VOLTAGE, \ | |
52 | .indexed = 1, \ | |
53 | .channel = _idx, \ | |
54 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | |
55 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ | |
56 | } | |
57 | ||
58 | static const struct iio_chan_spec lpc18xx_adc_iio_channels[] = { | |
59 | LPC18XX_ADC_CHAN(0), | |
60 | LPC18XX_ADC_CHAN(1), | |
61 | LPC18XX_ADC_CHAN(2), | |
62 | LPC18XX_ADC_CHAN(3), | |
63 | LPC18XX_ADC_CHAN(4), | |
64 | LPC18XX_ADC_CHAN(5), | |
65 | LPC18XX_ADC_CHAN(6), | |
66 | LPC18XX_ADC_CHAN(7), | |
67 | }; | |
68 | ||
69 | static int lpc18xx_adc_read_chan(struct lpc18xx_adc *adc, unsigned int ch) | |
70 | { | |
71 | int ret; | |
72 | u32 reg; | |
73 | ||
74 | reg = adc->cr_reg | BIT(ch) | LPC18XX_ADC_CR_START_NOW; | |
75 | writel(reg, adc->base + LPC18XX_ADC_CR); | |
76 | ||
77 | ret = readl_poll_timeout(adc->base + LPC18XX_ADC_GDR, reg, | |
78 | reg & LPC18XX_ADC_CONV_DONE, 3, 9); | |
79 | if (ret) { | |
80 | dev_warn(adc->dev, "adc read timed out\n"); | |
81 | return ret; | |
82 | } | |
83 | ||
84 | return (reg >> LPC18XX_ADC_SAMPLE_SHIFT) & LPC18XX_ADC_SAMPLE_MASK; | |
85 | } | |
86 | ||
87 | static int lpc18xx_adc_read_raw(struct iio_dev *indio_dev, | |
88 | struct iio_chan_spec const *chan, | |
89 | int *val, int *val2, long mask) | |
90 | { | |
91 | struct lpc18xx_adc *adc = iio_priv(indio_dev); | |
92 | ||
93 | switch (mask) { | |
94 | case IIO_CHAN_INFO_RAW: | |
95 | mutex_lock(&adc->lock); | |
96 | *val = lpc18xx_adc_read_chan(adc, chan->channel); | |
97 | mutex_unlock(&adc->lock); | |
98 | if (*val < 0) | |
99 | return *val; | |
100 | ||
101 | return IIO_VAL_INT; | |
102 | ||
103 | case IIO_CHAN_INFO_SCALE: | |
104 | *val = regulator_get_voltage(adc->vref) / 1000; | |
105 | *val2 = 10; | |
106 | ||
107 | return IIO_VAL_FRACTIONAL_LOG2; | |
108 | } | |
109 | ||
110 | return -EINVAL; | |
111 | } | |
112 | ||
113 | static const struct iio_info lpc18xx_adc_info = { | |
114 | .read_raw = lpc18xx_adc_read_raw, | |
a583c24d JE |
115 | }; |
116 | ||
0be84447 AGNL |
117 | static void lpc18xx_clear_cr_reg(void *data) |
118 | { | |
119 | struct lpc18xx_adc *adc = data; | |
120 | ||
121 | writel(0, adc->base + LPC18XX_ADC_CR); | |
122 | } | |
123 | ||
0be84447 AGNL |
124 | static void lpc18xx_regulator_disable(void *vref) |
125 | { | |
126 | regulator_disable(vref); | |
127 | } | |
128 | ||
a583c24d JE |
129 | static int lpc18xx_adc_probe(struct platform_device *pdev) |
130 | { | |
131 | struct iio_dev *indio_dev; | |
132 | struct lpc18xx_adc *adc; | |
a583c24d JE |
133 | unsigned int clkdiv; |
134 | unsigned long rate; | |
135 | int ret; | |
136 | ||
137 | indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc)); | |
138 | if (!indio_dev) | |
139 | return -ENOMEM; | |
140 | ||
a583c24d JE |
141 | adc = iio_priv(indio_dev); |
142 | adc->dev = &pdev->dev; | |
143 | mutex_init(&adc->lock); | |
144 | ||
18d031f4 | 145 | adc->base = devm_platform_ioremap_resource(pdev, 0); |
a583c24d JE |
146 | if (IS_ERR(adc->base)) |
147 | return PTR_ERR(adc->base); | |
148 | ||
4004912e | 149 | adc->clk = devm_clk_get_enabled(&pdev->dev, NULL); |
922f694b CH |
150 | if (IS_ERR(adc->clk)) |
151 | return dev_err_probe(&pdev->dev, PTR_ERR(adc->clk), | |
152 | "error getting clock\n"); | |
a583c24d | 153 | |
a583c24d | 154 | adc->vref = devm_regulator_get(&pdev->dev, "vref"); |
922f694b CH |
155 | if (IS_ERR(adc->vref)) |
156 | return dev_err_probe(&pdev->dev, PTR_ERR(adc->vref), | |
157 | "error getting regulator\n"); | |
a583c24d JE |
158 | |
159 | indio_dev->name = dev_name(&pdev->dev); | |
a583c24d JE |
160 | indio_dev->info = &lpc18xx_adc_info; |
161 | indio_dev->modes = INDIO_DIRECT_MODE; | |
162 | indio_dev->channels = lpc18xx_adc_iio_channels; | |
163 | indio_dev->num_channels = ARRAY_SIZE(lpc18xx_adc_iio_channels); | |
164 | ||
165 | ret = regulator_enable(adc->vref); | |
166 | if (ret) { | |
167 | dev_err(&pdev->dev, "unable to enable regulator\n"); | |
168 | return ret; | |
169 | } | |
170 | ||
0be84447 AGNL |
171 | ret = devm_add_action_or_reset(&pdev->dev, lpc18xx_regulator_disable, adc->vref); |
172 | if (ret) | |
173 | return ret; | |
174 | ||
8eebe628 AGNL |
175 | rate = clk_get_rate(adc->clk); |
176 | clkdiv = DIV_ROUND_UP(rate, LPC18XX_ADC_CLK_TARGET); | |
177 | ||
a583c24d JE |
178 | adc->cr_reg = (clkdiv << LPC18XX_ADC_CR_CLKDIV_SHIFT) | |
179 | LPC18XX_ADC_CR_PDN; | |
180 | writel(adc->cr_reg, adc->base + LPC18XX_ADC_CR); | |
181 | ||
0be84447 AGNL |
182 | ret = devm_add_action_or_reset(&pdev->dev, lpc18xx_clear_cr_reg, adc); |
183 | if (ret) | |
184 | return ret; | |
a583c24d | 185 | |
0be84447 | 186 | return devm_iio_device_register(&pdev->dev, indio_dev); |
a583c24d JE |
187 | } |
188 | ||
189 | static const struct of_device_id lpc18xx_adc_match[] = { | |
190 | { .compatible = "nxp,lpc1850-adc" }, | |
191 | { /* sentinel */ } | |
192 | }; | |
193 | MODULE_DEVICE_TABLE(of, lpc18xx_adc_match); | |
194 | ||
195 | static struct platform_driver lpc18xx_adc_driver = { | |
196 | .probe = lpc18xx_adc_probe, | |
a583c24d JE |
197 | .driver = { |
198 | .name = "lpc18xx-adc", | |
199 | .of_match_table = lpc18xx_adc_match, | |
200 | }, | |
201 | }; | |
202 | module_platform_driver(lpc18xx_adc_driver); | |
203 | ||
204 | MODULE_DESCRIPTION("LPC18xx ADC driver"); | |
205 | MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>"); | |
206 | MODULE_LICENSE("GPL v2"); |