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