1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
4 * JZ4740 SoC ADC driver
6 * This driver synchronizes access to the JZ4740 ADC core between the
7 * JZ4740 battery and hwmon drivers.
10 #include <linux/err.h>
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>
20 #include <linux/clk.h>
21 #include <linux/mfd/core.h>
23 #include <linux/jz4740-adc.h>
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
31 #define JZ_REG_ADC_TOUCHSCREEN_BASE 0x10
32 #define JZ_REG_ADC_BATTERY_BASE 0x1c
33 #define JZ_REG_ADC_HWMON_BASE 0x20
35 #define JZ_ADC_ENABLE_TOUCH BIT(2)
36 #define JZ_ADC_ENABLE_BATTERY BIT(1)
37 #define JZ_ADC_ENABLE_ADCIN BIT(0)
52 struct irq_chip_generic *gc;
60 static void jz4740_adc_irq_demux(struct irq_desc *desc)
62 struct irq_chip_generic *gc = irq_desc_get_handler_data(desc);
66 status = readb(gc->reg_base + JZ_REG_ADC_STATUS);
68 for (i = 0; i < 5; ++i) {
70 generic_handle_irq(gc->irq_base + i);
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 */
78 static inline void jz4740_adc_clk_enable(struct jz4740_adc *adc)
80 if (atomic_inc_return(&adc->clk_ref) == 1)
81 clk_prepare_enable(adc->clk);
84 static inline void jz4740_adc_clk_disable(struct jz4740_adc *adc)
86 if (atomic_dec_return(&adc->clk_ref) == 0)
87 clk_disable_unprepare(adc->clk);
90 static inline void jz4740_adc_set_enabled(struct jz4740_adc *adc, int engine,
96 spin_lock_irqsave(&adc->lock, flags);
98 val = readb(adc->base + JZ_REG_ADC_ENABLE);
103 writeb(val, adc->base + JZ_REG_ADC_ENABLE);
105 spin_unlock_irqrestore(&adc->lock, flags);
108 static int jz4740_adc_cell_enable(struct platform_device *pdev)
110 struct jz4740_adc *adc = dev_get_drvdata(pdev->dev.parent);
112 jz4740_adc_clk_enable(adc);
113 jz4740_adc_set_enabled(adc, pdev->id, true);
118 static int jz4740_adc_cell_disable(struct platform_device *pdev)
120 struct jz4740_adc *adc = dev_get_drvdata(pdev->dev.parent);
122 jz4740_adc_set_enabled(adc, pdev->id, false);
123 jz4740_adc_clk_disable(adc);
128 int jz4740_adc_set_config(struct device *dev, uint32_t mask, uint32_t val)
130 struct jz4740_adc *adc = dev_get_drvdata(dev);
137 spin_lock_irqsave(&adc->lock, flags);
139 cfg = readl(adc->base + JZ_REG_ADC_CFG);
144 writel(cfg, adc->base + JZ_REG_ADC_CFG);
146 spin_unlock_irqrestore(&adc->lock, flags);
150 EXPORT_SYMBOL_GPL(jz4740_adc_set_config);
152 static struct resource jz4740_hwmon_resources[] = {
154 .start = JZ_ADC_IRQ_ADCIN,
155 .flags = IORESOURCE_IRQ,
158 .start = JZ_REG_ADC_HWMON_BASE,
159 .end = JZ_REG_ADC_HWMON_BASE + 3,
160 .flags = IORESOURCE_MEM,
164 static struct resource jz4740_battery_resources[] = {
166 .start = JZ_ADC_IRQ_BATTERY,
167 .flags = IORESOURCE_IRQ,
170 .start = JZ_REG_ADC_BATTERY_BASE,
171 .end = JZ_REG_ADC_BATTERY_BASE + 3,
172 .flags = IORESOURCE_MEM,
176 static const struct mfd_cell jz4740_adc_cells[] = {
179 .name = "jz4740-hwmon",
180 .num_resources = ARRAY_SIZE(jz4740_hwmon_resources),
181 .resources = jz4740_hwmon_resources,
183 .enable = jz4740_adc_cell_enable,
184 .disable = jz4740_adc_cell_disable,
188 .name = "jz4740-battery",
189 .num_resources = ARRAY_SIZE(jz4740_battery_resources),
190 .resources = jz4740_battery_resources,
192 .enable = jz4740_adc_cell_enable,
193 .disable = jz4740_adc_cell_disable,
197 static int jz4740_adc_probe(struct platform_device *pdev)
199 struct irq_chip_generic *gc;
200 struct irq_chip_type *ct;
201 struct jz4740_adc *adc;
202 struct resource *mem_base;
206 adc = devm_kzalloc(&pdev->dev, sizeof(*adc), GFP_KERNEL);
210 adc->irq = platform_get_irq(pdev, 0);
213 dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret);
217 irq_base = platform_get_irq(pdev, 1);
219 dev_err(&pdev->dev, "Failed to get irq base: %d\n", irq_base);
223 mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
225 dev_err(&pdev->dev, "Failed to get platform mmio resource\n");
229 /* Only request the shared registers for the MFD driver */
230 adc->mem = request_mem_region(mem_base->start, JZ_REG_ADC_STATUS,
233 dev_err(&pdev->dev, "Failed to request mmio memory region\n");
237 adc->base = ioremap_nocache(adc->mem->start, resource_size(adc->mem));
240 dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
241 goto err_release_mem_region;
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);
251 spin_lock_init(&adc->lock);
252 atomic_set(&adc->clk_ref, 0);
254 platform_set_drvdata(pdev, adc);
256 gc = irq_alloc_generic_chip("INTC", 1, irq_base, adc->base,
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;
264 ct->chip.irq_ack = irq_gc_ack_set_bit;
266 irq_setup_generic_chip(gc, IRQ_MSK(5), IRQ_GC_INIT_MASK_CACHE, 0,
267 IRQ_NOPROBE | IRQ_LEVEL);
271 irq_set_chained_handler_and_data(adc->irq, jz4740_adc_irq_demux, gc);
273 writeb(0x00, adc->base + JZ_REG_ADC_ENABLE);
274 writeb(0xff, adc->base + JZ_REG_ADC_CTRL);
276 ret = mfd_add_devices(&pdev->dev, 0, jz4740_adc_cells,
277 ARRAY_SIZE(jz4740_adc_cells), mem_base,
288 err_release_mem_region:
289 release_mem_region(adc->mem->start, resource_size(adc->mem));
293 static int jz4740_adc_remove(struct platform_device *pdev)
295 struct jz4740_adc *adc = platform_get_drvdata(pdev);
297 mfd_remove_devices(&pdev->dev);
299 irq_remove_generic_chip(adc->gc, IRQ_MSK(5), IRQ_NOPROBE | IRQ_LEVEL, 0);
301 irq_set_chained_handler_and_data(adc->irq, NULL, NULL);
304 release_mem_region(adc->mem->start, resource_size(adc->mem));
311 static struct platform_driver jz4740_adc_driver = {
312 .probe = jz4740_adc_probe,
313 .remove = jz4740_adc_remove,
315 .name = "jz4740-adc",
319 module_platform_driver(jz4740_adc_driver);
321 MODULE_DESCRIPTION("JZ4740 SoC ADC driver");
322 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
323 MODULE_LICENSE("GPL");
324 MODULE_ALIAS("platform:jz4740-adc");