]>
Commit | Line | Data |
---|---|---|
43058349 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
906ecf69 RS |
2 | /* |
3 | * lpc32xx_adc.c - Support for ADC in LPC32XX | |
4 | * | |
5 | * 3-channel, 10-bit ADC | |
6 | * | |
7 | * Copyright (C) 2011, 2012 Roland Stigge <stigge@antcom.de> | |
906ecf69 RS |
8 | */ |
9 | ||
10 | #include <linux/module.h> | |
11 | #include <linux/platform_device.h> | |
12 | #include <linux/interrupt.h> | |
13 | #include <linux/device.h> | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/slab.h> | |
16 | #include <linux/io.h> | |
17 | #include <linux/clk.h> | |
18 | #include <linux/err.h> | |
19 | #include <linux/completion.h> | |
1f9e3494 | 20 | #include <linux/of.h> |
906ecf69 | 21 | |
06458e27 JC |
22 | #include <linux/iio/iio.h> |
23 | #include <linux/iio/sysfs.h> | |
906ecf69 RS |
24 | |
25 | /* | |
26 | * LPC32XX registers definitions | |
27 | */ | |
5023574a JC |
28 | #define LPC32XXAD_SELECT(x) ((x) + 0x04) |
29 | #define LPC32XXAD_CTRL(x) ((x) + 0x08) | |
30 | #define LPC32XXAD_VALUE(x) ((x) + 0x48) | |
906ecf69 | 31 | |
5023574a JC |
32 | /* Bit definitions for LPC32XXAD_SELECT: */ |
33 | /* constant, always write this value! */ | |
34 | #define LPC32XXAD_REFm 0x00000200 | |
35 | /* constant, always write this value! */ | |
36 | #define LPC32XXAD_REFp 0x00000080 | |
37 | /* multiple of this is the channel number: 0, 1, 2 */ | |
38 | #define LPC32XXAD_IN 0x00000010 | |
39 | /* constant, always write this value! */ | |
40 | #define LPC32XXAD_INTERNAL 0x00000004 | |
906ecf69 | 41 | |
5023574a JC |
42 | /* Bit definitions for LPC32XXAD_CTRL: */ |
43 | #define LPC32XXAD_STROBE 0x00000002 | |
44 | #define LPC32XXAD_PDN_CTRL 0x00000004 | |
906ecf69 | 45 | |
5023574a JC |
46 | /* Bit definitions for LPC32XXAD_VALUE: */ |
47 | #define LPC32XXAD_VALUE_MASK 0x000003FF | |
906ecf69 | 48 | |
5023574a | 49 | #define LPC32XXAD_NAME "lpc32xx-adc" |
906ecf69 | 50 | |
7901b2a1 | 51 | struct lpc32xx_adc_state { |
906ecf69 RS |
52 | void __iomem *adc_base; |
53 | struct clk *clk; | |
54 | struct completion completion; | |
55 | ||
56 | u32 value; | |
57 | }; | |
58 | ||
59 | static int lpc32xx_read_raw(struct iio_dev *indio_dev, | |
e8ef49f0 IC |
60 | struct iio_chan_spec const *chan, |
61 | int *val, | |
62 | int *val2, | |
63 | long mask) | |
906ecf69 | 64 | { |
7901b2a1 | 65 | struct lpc32xx_adc_state *st = iio_priv(indio_dev); |
42d97ac6 | 66 | int ret; |
b11f98ff | 67 | if (mask == IIO_CHAN_INFO_RAW) { |
906ecf69 | 68 | mutex_lock(&indio_dev->mlock); |
42d97ac6 AY |
69 | ret = clk_prepare_enable(st->clk); |
70 | if (ret) { | |
71 | mutex_unlock(&indio_dev->mlock); | |
72 | return ret; | |
73 | } | |
906ecf69 | 74 | /* Measurement setup */ |
5023574a JC |
75 | __raw_writel(LPC32XXAD_INTERNAL | (chan->address) | |
76 | LPC32XXAD_REFp | LPC32XXAD_REFm, | |
7901b2a1 | 77 | LPC32XXAD_SELECT(st->adc_base)); |
906ecf69 | 78 | /* Trigger conversion */ |
5023574a | 79 | __raw_writel(LPC32XXAD_PDN_CTRL | LPC32XXAD_STROBE, |
7901b2a1 JC |
80 | LPC32XXAD_CTRL(st->adc_base)); |
81 | wait_for_completion(&st->completion); /* set by ISR */ | |
82 | clk_disable_unprepare(st->clk); | |
83 | *val = st->value; | |
906ecf69 RS |
84 | mutex_unlock(&indio_dev->mlock); |
85 | ||
86 | return IIO_VAL_INT; | |
87 | } | |
88 | ||
89 | return -EINVAL; | |
90 | } | |
91 | ||
92 | static const struct iio_info lpc32xx_adc_iio_info = { | |
93 | .read_raw = &lpc32xx_read_raw, | |
906ecf69 RS |
94 | }; |
95 | ||
b11f98ff JC |
96 | #define LPC32XX_ADC_CHANNEL(_index) { \ |
97 | .type = IIO_VOLTAGE, \ | |
98 | .indexed = 1, \ | |
99 | .channel = _index, \ | |
066f9051 | 100 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ |
5023574a | 101 | .address = LPC32XXAD_IN * _index, \ |
b11f98ff | 102 | .scan_index = _index, \ |
906ecf69 RS |
103 | } |
104 | ||
f4e4b955 | 105 | static const struct iio_chan_spec lpc32xx_adc_iio_channels[] = { |
906ecf69 RS |
106 | LPC32XX_ADC_CHANNEL(0), |
107 | LPC32XX_ADC_CHANNEL(1), | |
108 | LPC32XX_ADC_CHANNEL(2), | |
109 | }; | |
110 | ||
111 | static irqreturn_t lpc32xx_adc_isr(int irq, void *dev_id) | |
112 | { | |
7901b2a1 | 113 | struct lpc32xx_adc_state *st = dev_id; |
906ecf69 RS |
114 | |
115 | /* Read value and clear irq */ | |
7901b2a1 JC |
116 | st->value = __raw_readl(LPC32XXAD_VALUE(st->adc_base)) & |
117 | LPC32XXAD_VALUE_MASK; | |
118 | complete(&st->completion); | |
906ecf69 RS |
119 | |
120 | return IRQ_HANDLED; | |
121 | } | |
122 | ||
4ae1c61f | 123 | static int lpc32xx_adc_probe(struct platform_device *pdev) |
906ecf69 | 124 | { |
7901b2a1 | 125 | struct lpc32xx_adc_state *st = NULL; |
906ecf69 RS |
126 | struct resource *res; |
127 | int retval = -ENODEV; | |
128 | struct iio_dev *iodev = NULL; | |
129 | int irq; | |
130 | ||
131 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
132 | if (!res) { | |
133 | dev_err(&pdev->dev, "failed to get platform I/O memory\n"); | |
f6707ef7 | 134 | return -ENXIO; |
906ecf69 RS |
135 | } |
136 | ||
7901b2a1 | 137 | iodev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); |
7d456e4e SK |
138 | if (!iodev) |
139 | return -ENOMEM; | |
906ecf69 | 140 | |
7901b2a1 | 141 | st = iio_priv(iodev); |
906ecf69 | 142 | |
7901b2a1 JC |
143 | st->adc_base = devm_ioremap(&pdev->dev, res->start, |
144 | resource_size(res)); | |
145 | if (!st->adc_base) { | |
906ecf69 | 146 | dev_err(&pdev->dev, "failed mapping memory\n"); |
7d456e4e | 147 | return -EBUSY; |
906ecf69 RS |
148 | } |
149 | ||
7901b2a1 JC |
150 | st->clk = devm_clk_get(&pdev->dev, NULL); |
151 | if (IS_ERR(st->clk)) { | |
906ecf69 | 152 | dev_err(&pdev->dev, "failed getting clock\n"); |
7901b2a1 | 153 | return PTR_ERR(st->clk); |
906ecf69 RS |
154 | } |
155 | ||
156 | irq = platform_get_irq(pdev, 0); | |
2918ad14 | 157 | if (irq <= 0) { |
906ecf69 | 158 | dev_err(&pdev->dev, "failed getting interrupt resource\n"); |
f6707ef7 | 159 | return -ENXIO; |
906ecf69 RS |
160 | } |
161 | ||
7d456e4e | 162 | retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0, |
7901b2a1 | 163 | LPC32XXAD_NAME, st); |
906ecf69 RS |
164 | if (retval < 0) { |
165 | dev_err(&pdev->dev, "failed requesting interrupt\n"); | |
7d456e4e | 166 | return retval; |
906ecf69 RS |
167 | } |
168 | ||
169 | platform_set_drvdata(pdev, iodev); | |
170 | ||
7901b2a1 | 171 | init_completion(&st->completion); |
906ecf69 | 172 | |
5023574a | 173 | iodev->name = LPC32XXAD_NAME; |
906ecf69 RS |
174 | iodev->dev.parent = &pdev->dev; |
175 | iodev->info = &lpc32xx_adc_iio_info; | |
176 | iodev->modes = INDIO_DIRECT_MODE; | |
177 | iodev->channels = lpc32xx_adc_iio_channels; | |
178 | iodev->num_channels = ARRAY_SIZE(lpc32xx_adc_iio_channels); | |
179 | ||
afb6fd5f | 180 | retval = devm_iio_device_register(&pdev->dev, iodev); |
906ecf69 | 181 | if (retval) |
7d456e4e | 182 | return retval; |
906ecf69 RS |
183 | |
184 | dev_info(&pdev->dev, "LPC32XX ADC driver loaded, IRQ %d\n", irq); | |
185 | ||
186 | return 0; | |
906ecf69 RS |
187 | } |
188 | ||
1f9e3494 RS |
189 | #ifdef CONFIG_OF |
190 | static const struct of_device_id lpc32xx_adc_match[] = { | |
191 | { .compatible = "nxp,lpc3220-adc" }, | |
192 | {}, | |
193 | }; | |
194 | MODULE_DEVICE_TABLE(of, lpc32xx_adc_match); | |
195 | #endif | |
196 | ||
906ecf69 RS |
197 | static struct platform_driver lpc32xx_adc_driver = { |
198 | .probe = lpc32xx_adc_probe, | |
906ecf69 | 199 | .driver = { |
5023574a | 200 | .name = LPC32XXAD_NAME, |
1f9e3494 | 201 | .of_match_table = of_match_ptr(lpc32xx_adc_match), |
906ecf69 RS |
202 | }, |
203 | }; | |
204 | ||
205 | module_platform_driver(lpc32xx_adc_driver); | |
206 | ||
207 | MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>"); | |
208 | MODULE_DESCRIPTION("LPC32XX ADC driver"); | |
209 | MODULE_LICENSE("GPL"); |