Commit | Line | Data |
---|---|---|
b886d83c | 1 | // SPDX-License-Identifier: GPL-2.0-only |
74aeac4d JT |
2 | /* |
3 | * MEN 16z188 Analog to Digial Converter | |
4 | * | |
5 | * Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de) | |
6 | * Author: Johannes Thumshirn <johannes.thumshirn@men.de> | |
74aeac4d JT |
7 | */ |
8 | ||
9 | #include <linux/kernel.h> | |
10 | #include <linux/module.h> | |
11 | #include <linux/mcb.h> | |
35272754 | 12 | #include <linux/io.h> |
74aeac4d JT |
13 | #include <linux/iio/iio.h> |
14 | ||
15 | #define Z188_ADC_MAX_CHAN 8 | |
16 | #define Z188_ADC_GAIN 0x0700000 | |
17 | #define Z188_MODE_VOLTAGE BIT(27) | |
18 | #define Z188_CFG_AUTO 0x1 | |
19 | #define Z188_CTRL_REG 0x40 | |
20 | ||
21 | #define ADC_DATA(x) (((x) >> 2) & 0x7ffffc) | |
22 | #define ADC_OVR(x) ((x) & 0x1) | |
23 | ||
24 | struct z188_adc { | |
25 | struct resource *mem; | |
26 | void __iomem *base; | |
27 | }; | |
28 | ||
29 | #define Z188_ADC_CHANNEL(idx) { \ | |
30 | .type = IIO_VOLTAGE, \ | |
31 | .indexed = 1, \ | |
32 | .channel = (idx), \ | |
33 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | |
34 | } | |
35 | ||
36 | static const struct iio_chan_spec z188_adc_iio_channels[] = { | |
37 | Z188_ADC_CHANNEL(0), | |
38 | Z188_ADC_CHANNEL(1), | |
39 | Z188_ADC_CHANNEL(2), | |
40 | Z188_ADC_CHANNEL(3), | |
41 | Z188_ADC_CHANNEL(4), | |
42 | Z188_ADC_CHANNEL(5), | |
43 | Z188_ADC_CHANNEL(6), | |
44 | Z188_ADC_CHANNEL(7), | |
45 | }; | |
46 | ||
47 | static int z188_iio_read_raw(struct iio_dev *iio_dev, | |
48 | struct iio_chan_spec const *chan, | |
49 | int *val, | |
50 | int *val2, | |
51 | long info) | |
52 | { | |
53 | struct z188_adc *adc = iio_priv(iio_dev); | |
54 | int ret; | |
55 | u16 tmp; | |
56 | ||
57 | switch (info) { | |
58 | case IIO_CHAN_INFO_RAW: | |
59 | tmp = readw(adc->base + chan->channel * 4); | |
60 | ||
61 | if (ADC_OVR(tmp)) { | |
62 | dev_info(&iio_dev->dev, | |
63 | "Oversampling error on ADC channel %d\n", | |
64 | chan->channel); | |
65 | return -EIO; | |
66 | } | |
67 | *val = ADC_DATA(tmp); | |
68 | ret = IIO_VAL_INT; | |
69 | break; | |
70 | default: | |
71 | ret = -EINVAL; | |
72 | break; | |
73 | } | |
74 | ||
75 | return ret; | |
76 | } | |
77 | ||
52ceb773 | 78 | static const struct iio_info z188_adc_info = { |
74aeac4d | 79 | .read_raw = &z188_iio_read_raw, |
74aeac4d JT |
80 | }; |
81 | ||
82 | static void men_z188_config_channels(void __iomem *addr) | |
83 | { | |
84 | int i; | |
85 | u32 cfg; | |
86 | u32 ctl; | |
87 | ||
88 | ctl = readl(addr + Z188_CTRL_REG); | |
89 | ctl |= Z188_CFG_AUTO; | |
90 | writel(ctl, addr + Z188_CTRL_REG); | |
91 | ||
92 | for (i = 0; i < Z188_ADC_MAX_CHAN; i++) { | |
93 | cfg = readl(addr + i); | |
94 | cfg &= ~Z188_ADC_GAIN; | |
95 | cfg |= Z188_MODE_VOLTAGE; | |
96 | writel(cfg, addr + i); | |
97 | } | |
98 | } | |
99 | ||
100 | static int men_z188_probe(struct mcb_device *dev, | |
101 | const struct mcb_device_id *id) | |
102 | { | |
103 | struct z188_adc *adc; | |
104 | struct iio_dev *indio_dev; | |
105 | struct resource *mem; | |
106 | ||
107 | indio_dev = devm_iio_device_alloc(&dev->dev, sizeof(struct z188_adc)); | |
108 | if (!indio_dev) | |
109 | return -ENOMEM; | |
110 | ||
111 | adc = iio_priv(indio_dev); | |
112 | indio_dev->name = "z188-adc"; | |
113 | indio_dev->dev.parent = &dev->dev; | |
114 | indio_dev->info = &z188_adc_info; | |
115 | indio_dev->modes = INDIO_DIRECT_MODE; | |
116 | indio_dev->channels = z188_adc_iio_channels; | |
117 | indio_dev->num_channels = ARRAY_SIZE(z188_adc_iio_channels); | |
118 | ||
119 | mem = mcb_request_mem(dev, "z188-adc"); | |
e94f62e7 DC |
120 | if (IS_ERR(mem)) |
121 | return PTR_ERR(mem); | |
74aeac4d JT |
122 | |
123 | adc->base = ioremap(mem->start, resource_size(mem)); | |
124 | if (adc->base == NULL) | |
125 | goto err; | |
126 | ||
127 | men_z188_config_channels(adc->base); | |
128 | ||
129 | adc->mem = mem; | |
130 | mcb_set_drvdata(dev, indio_dev); | |
131 | ||
132 | return iio_device_register(indio_dev); | |
133 | ||
134 | err: | |
135 | mcb_release_mem(mem); | |
136 | return -ENXIO; | |
137 | } | |
138 | ||
139 | static void men_z188_remove(struct mcb_device *dev) | |
140 | { | |
141 | struct iio_dev *indio_dev = mcb_get_drvdata(dev); | |
142 | struct z188_adc *adc = iio_priv(indio_dev); | |
143 | ||
144 | iio_device_unregister(indio_dev); | |
145 | iounmap(adc->base); | |
146 | mcb_release_mem(adc->mem); | |
147 | } | |
148 | ||
149 | static const struct mcb_device_id men_z188_ids[] = { | |
150 | { .device = 0xbc }, | |
fbbba1f8 | 151 | { } |
74aeac4d JT |
152 | }; |
153 | MODULE_DEVICE_TABLE(mcb, men_z188_ids); | |
154 | ||
155 | static struct mcb_driver men_z188_driver = { | |
156 | .driver = { | |
157 | .name = "z188-adc", | |
158 | .owner = THIS_MODULE, | |
159 | }, | |
160 | .probe = men_z188_probe, | |
161 | .remove = men_z188_remove, | |
162 | .id_table = men_z188_ids, | |
163 | }; | |
164 | module_mcb_driver(men_z188_driver); | |
165 | ||
166 | MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>"); | |
167 | MODULE_LICENSE("GPL"); | |
168 | MODULE_DESCRIPTION("IIO ADC driver for MEN 16z188 ADC Core"); | |
169 | MODULE_ALIAS("mcb:16z188"); | |
891e6036 | 170 | MODULE_IMPORT_NS(MCB); |