xen/efi: have a common runtime setup function
[linux-2.6-block.git] / drivers / mfd / jz4740-adc.c
CommitLineData
a912e80b 1// SPDX-License-Identifier: GPL-2.0-or-later
91f4debf
LPC
2/*
3 * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
4 * JZ4740 SoC ADC driver
5 *
91f4debf
LPC
6 * This driver synchronizes access to the JZ4740 ADC core between the
7 * JZ4740 battery and hwmon drivers.
8 */
9
10#include <linux/err.h>
fa860403 11#include <linux/io.h>
91f4debf
LPC
12#include <linux/irq.h>
13#include <linux/interrupt.h>
14#include <linux/kernel.h>
15#include <linux/module.h>
16#include <linux/platform_device.h>
17#include <linux/slab.h>
18#include <linux/spinlock.h>
19
20#include <linux/clk.h>
21#include <linux/mfd/core.h>
22
23#include <linux/jz4740-adc.h>
24
25
26#define JZ_REG_ADC_ENABLE 0x00
27#define JZ_REG_ADC_CFG 0x04
28#define JZ_REG_ADC_CTRL 0x08
29#define JZ_REG_ADC_STATUS 0x0c
30
31#define JZ_REG_ADC_TOUCHSCREEN_BASE 0x10
32#define JZ_REG_ADC_BATTERY_BASE 0x1c
33#define JZ_REG_ADC_HWMON_BASE 0x20
34
35#define JZ_ADC_ENABLE_TOUCH BIT(2)
36#define JZ_ADC_ENABLE_BATTERY BIT(1)
37#define JZ_ADC_ENABLE_ADCIN BIT(0)
38
39enum {
40 JZ_ADC_IRQ_ADCIN = 0,
41 JZ_ADC_IRQ_BATTERY,
42 JZ_ADC_IRQ_TOUCH,
43 JZ_ADC_IRQ_PENUP,
44 JZ_ADC_IRQ_PENDOWN,
45};
46
47struct jz4740_adc {
48 struct resource *mem;
49 void __iomem *base;
50
51 int irq;
914e6d4e 52 struct irq_chip_generic *gc;
91f4debf
LPC
53
54 struct clk *clk;
55 atomic_t clk_ref;
56
57 spinlock_t lock;
58};
59
bd0b9ac4 60static void jz4740_adc_irq_demux(struct irq_desc *desc)
91f4debf 61{
914e6d4e 62 struct irq_chip_generic *gc = irq_desc_get_handler_data(desc);
91f4debf
LPC
63 uint8_t status;
64 unsigned int i;
65
914e6d4e 66 status = readb(gc->reg_base + JZ_REG_ADC_STATUS);
91f4debf
LPC
67
68 for (i = 0; i < 5; ++i) {
69 if (status & BIT(i))
914e6d4e 70 generic_handle_irq(gc->irq_base + i);
91f4debf
LPC
71 }
72}
73
74
75/* Refcounting for the ADC clock is done in here instead of in the clock
76 * framework, because it is the only clock which is shared between multiple
77 * devices and thus is the only clock which needs refcounting */
78static inline void jz4740_adc_clk_enable(struct jz4740_adc *adc)
79{
80 if (atomic_inc_return(&adc->clk_ref) == 1)
404d5df4 81 clk_prepare_enable(adc->clk);
91f4debf
LPC
82}
83
84static inline void jz4740_adc_clk_disable(struct jz4740_adc *adc)
85{
86 if (atomic_dec_return(&adc->clk_ref) == 0)
404d5df4 87 clk_disable_unprepare(adc->clk);
91f4debf
LPC
88}
89
90static inline void jz4740_adc_set_enabled(struct jz4740_adc *adc, int engine,
91 bool enabled)
92{
93 unsigned long flags;
94 uint8_t val;
95
96 spin_lock_irqsave(&adc->lock, flags);
97
98 val = readb(adc->base + JZ_REG_ADC_ENABLE);
99 if (enabled)
100 val |= BIT(engine);
101 else
f9c28019 102 val &= ~BIT(engine);
91f4debf
LPC
103 writeb(val, adc->base + JZ_REG_ADC_ENABLE);
104
105 spin_unlock_irqrestore(&adc->lock, flags);
106}
107
108static int jz4740_adc_cell_enable(struct platform_device *pdev)
109{
110 struct jz4740_adc *adc = dev_get_drvdata(pdev->dev.parent);
111
112 jz4740_adc_clk_enable(adc);
113 jz4740_adc_set_enabled(adc, pdev->id, true);
114
115 return 0;
116}
117
118static int jz4740_adc_cell_disable(struct platform_device *pdev)
119{
120 struct jz4740_adc *adc = dev_get_drvdata(pdev->dev.parent);
121
122 jz4740_adc_set_enabled(adc, pdev->id, false);
123 jz4740_adc_clk_disable(adc);
124
125 return 0;
126}
127
128int jz4740_adc_set_config(struct device *dev, uint32_t mask, uint32_t val)
129{
130 struct jz4740_adc *adc = dev_get_drvdata(dev);
131 unsigned long flags;
132 uint32_t cfg;
133
134 if (!adc)
135 return -ENODEV;
136
137 spin_lock_irqsave(&adc->lock, flags);
138
139 cfg = readl(adc->base + JZ_REG_ADC_CFG);
140
141 cfg &= ~mask;
142 cfg |= val;
143
144 writel(cfg, adc->base + JZ_REG_ADC_CFG);
145
146 spin_unlock_irqrestore(&adc->lock, flags);
147
148 return 0;
149}
150EXPORT_SYMBOL_GPL(jz4740_adc_set_config);
151
152static struct resource jz4740_hwmon_resources[] = {
153 {
154 .start = JZ_ADC_IRQ_ADCIN,
155 .flags = IORESOURCE_IRQ,
156 },
157 {
158 .start = JZ_REG_ADC_HWMON_BASE,
159 .end = JZ_REG_ADC_HWMON_BASE + 3,
160 .flags = IORESOURCE_MEM,
161 },
162};
163
164static struct resource jz4740_battery_resources[] = {
165 {
166 .start = JZ_ADC_IRQ_BATTERY,
167 .flags = IORESOURCE_IRQ,
168 },
169 {
170 .start = JZ_REG_ADC_BATTERY_BASE,
171 .end = JZ_REG_ADC_BATTERY_BASE + 3,
172 .flags = IORESOURCE_MEM,
173 },
174};
175
5ac98553 176static const struct mfd_cell jz4740_adc_cells[] = {
91f4debf
LPC
177 {
178 .id = 0,
179 .name = "jz4740-hwmon",
180 .num_resources = ARRAY_SIZE(jz4740_hwmon_resources),
181 .resources = jz4740_hwmon_resources,
91f4debf
LPC
182
183 .enable = jz4740_adc_cell_enable,
184 .disable = jz4740_adc_cell_disable,
185 },
186 {
187 .id = 1,
188 .name = "jz4740-battery",
189 .num_resources = ARRAY_SIZE(jz4740_battery_resources),
190 .resources = jz4740_battery_resources,
91f4debf
LPC
191
192 .enable = jz4740_adc_cell_enable,
193 .disable = jz4740_adc_cell_disable,
194 },
195};
196
f791be49 197static int jz4740_adc_probe(struct platform_device *pdev)
91f4debf 198{
914e6d4e
LPC
199 struct irq_chip_generic *gc;
200 struct irq_chip_type *ct;
91f4debf
LPC
201 struct jz4740_adc *adc;
202 struct resource *mem_base;
914e6d4e
LPC
203 int ret;
204 int irq_base;
91f4debf 205
931cbaf3 206 adc = devm_kzalloc(&pdev->dev, sizeof(*adc), GFP_KERNEL);
17963b54 207 if (!adc)
789133b7 208 return -ENOMEM;
91f4debf
LPC
209
210 adc->irq = platform_get_irq(pdev, 0);
211 if (adc->irq < 0) {
212 ret = adc->irq;
213 dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret);
931cbaf3 214 return ret;
91f4debf
LPC
215 }
216
914e6d4e
LPC
217 irq_base = platform_get_irq(pdev, 1);
218 if (irq_base < 0) {
931cbaf3
DN
219 dev_err(&pdev->dev, "Failed to get irq base: %d\n", irq_base);
220 return irq_base;
91f4debf
LPC
221 }
222
223 mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
224 if (!mem_base) {
91f4debf 225 dev_err(&pdev->dev, "Failed to get platform mmio resource\n");
931cbaf3 226 return -ENOENT;
91f4debf
LPC
227 }
228
229 /* Only request the shared registers for the MFD driver */
230 adc->mem = request_mem_region(mem_base->start, JZ_REG_ADC_STATUS,
231 pdev->name);
232 if (!adc->mem) {
91f4debf 233 dev_err(&pdev->dev, "Failed to request mmio memory region\n");
931cbaf3 234 return -EBUSY;
91f4debf
LPC
235 }
236
237 adc->base = ioremap_nocache(adc->mem->start, resource_size(adc->mem));
238 if (!adc->base) {
239 ret = -EBUSY;
240 dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
241 goto err_release_mem_region;
242 }
243
244 adc->clk = clk_get(&pdev->dev, "adc");
245 if (IS_ERR(adc->clk)) {
246 ret = PTR_ERR(adc->clk);
247 dev_err(&pdev->dev, "Failed to get clock: %d\n", ret);
248 goto err_iounmap;
249 }
250
251 spin_lock_init(&adc->lock);
252 atomic_set(&adc->clk_ref, 0);
253
254 platform_set_drvdata(pdev, adc);
255
914e6d4e
LPC
256 gc = irq_alloc_generic_chip("INTC", 1, irq_base, adc->base,
257 handle_level_irq);
258
259 ct = gc->chip_types;
260 ct->regs.mask = JZ_REG_ADC_CTRL;
261 ct->regs.ack = JZ_REG_ADC_STATUS;
262 ct->chip.irq_mask = irq_gc_mask_set_bit;
263 ct->chip.irq_unmask = irq_gc_mask_clr_bit;
6fcb8a3a 264 ct->chip.irq_ack = irq_gc_ack_set_bit;
914e6d4e 265
5a688c45
MR
266 irq_setup_generic_chip(gc, IRQ_MSK(5), IRQ_GC_INIT_MASK_CACHE, 0,
267 IRQ_NOPROBE | IRQ_LEVEL);
914e6d4e
LPC
268
269 adc->gc = gc;
91f4debf 270
59fa909e 271 irq_set_chained_handler_and_data(adc->irq, jz4740_adc_irq_demux, gc);
91f4debf
LPC
272
273 writeb(0x00, adc->base + JZ_REG_ADC_ENABLE);
274 writeb(0xff, adc->base + JZ_REG_ADC_CTRL);
275
48736c80 276 ret = mfd_add_devices(&pdev->dev, 0, jz4740_adc_cells,
0848c94f
MB
277 ARRAY_SIZE(jz4740_adc_cells), mem_base,
278 irq_base, NULL);
48736c80
AL
279 if (ret < 0)
280 goto err_clk_put;
91f4debf 281
48736c80
AL
282 return 0;
283
284err_clk_put:
285 clk_put(adc->clk);
91f4debf 286err_iounmap:
91f4debf
LPC
287 iounmap(adc->base);
288err_release_mem_region:
289 release_mem_region(adc->mem->start, resource_size(adc->mem));
91f4debf
LPC
290 return ret;
291}
292
4740f73f 293static int jz4740_adc_remove(struct platform_device *pdev)
91f4debf
LPC
294{
295 struct jz4740_adc *adc = platform_get_drvdata(pdev);
296
297 mfd_remove_devices(&pdev->dev);
298
914e6d4e
LPC
299 irq_remove_generic_chip(adc->gc, IRQ_MSK(5), IRQ_NOPROBE | IRQ_LEVEL, 0);
300 kfree(adc->gc);
59fa909e 301 irq_set_chained_handler_and_data(adc->irq, NULL, NULL);
91f4debf
LPC
302
303 iounmap(adc->base);
304 release_mem_region(adc->mem->start, resource_size(adc->mem));
305
306 clk_put(adc->clk);
307
91f4debf
LPC
308 return 0;
309}
310
429c9ecc 311static struct platform_driver jz4740_adc_driver = {
91f4debf 312 .probe = jz4740_adc_probe,
84449216 313 .remove = jz4740_adc_remove,
91f4debf
LPC
314 .driver = {
315 .name = "jz4740-adc",
91f4debf
LPC
316 },
317};
318
65349d60 319module_platform_driver(jz4740_adc_driver);
91f4debf
LPC
320
321MODULE_DESCRIPTION("JZ4740 SoC ADC driver");
322MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
323MODULE_LICENSE("GPL");
324MODULE_ALIAS("platform:jz4740-adc");