1 // SPDX-License-Identifier: GPL-2.0-only 2 /* ADC MFD core driver for sunxi platforms 3 * 4 * Copyright (c) 2016 Quentin Schulz <quentin.schulz@free-electrons.com> 5 */ 6 7 #include <linux/interrupt.h> 8 #include <linux/kernel.h> 9 #include <linux/mfd/core.h> 10 #include <linux/module.h> 11 #include <linux/of_device.h> 12 #include <linux/of_irq.h> 13 #include <linux/regmap.h> 14 15 #include <linux/mfd/sun4i-gpadc.h> 16 17 #define ARCH_SUN4I_A10 0 18 #define ARCH_SUN5I_A13 1 19 #define ARCH_SUN6I_A31 2 20 21 static const struct resource adc_resources[] = { 22 DEFINE_RES_IRQ_NAMED(SUN4I_GPADC_IRQ_FIFO_DATA, "FIFO_DATA_PENDING"), 23 DEFINE_RES_IRQ_NAMED(SUN4I_GPADC_IRQ_TEMP_DATA, "TEMP_DATA_PENDING"), 24 }; 25 26 static const struct regmap_irq sun4i_gpadc_regmap_irq[] = { 27 REGMAP_IRQ_REG(SUN4I_GPADC_IRQ_FIFO_DATA, 0, 28 SUN4I_GPADC_INT_FIFOC_TP_DATA_IRQ_EN), 29 REGMAP_IRQ_REG(SUN4I_GPADC_IRQ_TEMP_DATA, 0, 30 SUN4I_GPADC_INT_FIFOC_TEMP_IRQ_EN), 31 }; 32 33 static const struct regmap_irq_chip sun4i_gpadc_regmap_irq_chip = { 34 .name = "sun4i_gpadc_irq_chip", 35 .status_base = SUN4I_GPADC_INT_FIFOS, 36 .ack_base = SUN4I_GPADC_INT_FIFOS, 37 .unmask_base = SUN4I_GPADC_INT_FIFOC, 38 .init_ack_masked = true, 39 .irqs = sun4i_gpadc_regmap_irq, 40 .num_irqs = ARRAY_SIZE(sun4i_gpadc_regmap_irq), 41 .num_regs = 1, 42 }; 43 44 static struct mfd_cell sun4i_gpadc_cells[] = { 45 { 46 .name = "sun4i-a10-gpadc-iio", 47 .resources = adc_resources, 48 .num_resources = ARRAY_SIZE(adc_resources), 49 }, 50 { .name = "iio_hwmon" } 51 }; 52 53 static struct mfd_cell sun5i_gpadc_cells[] = { 54 { 55 .name = "sun5i-a13-gpadc-iio", 56 .resources = adc_resources, 57 .num_resources = ARRAY_SIZE(adc_resources), 58 }, 59 { .name = "iio_hwmon" }, 60 }; 61 62 static struct mfd_cell sun6i_gpadc_cells[] = { 63 { 64 .name = "sun6i-a31-gpadc-iio", 65 .resources = adc_resources, 66 .num_resources = ARRAY_SIZE(adc_resources), 67 }, 68 { .name = "iio_hwmon" }, 69 }; 70 71 static const struct regmap_config sun4i_gpadc_regmap_config = { 72 .reg_bits = 32, 73 .val_bits = 32, 74 .reg_stride = 4, 75 .fast_io = true, 76 }; 77 78 static const struct of_device_id sun4i_gpadc_of_match[] = { 79 { 80 .compatible = "allwinner,sun4i-a10-ts", 81 .data = (void *)ARCH_SUN4I_A10, 82 }, { 83 .compatible = "allwinner,sun5i-a13-ts", 84 .data = (void *)ARCH_SUN5I_A13, 85 }, { 86 .compatible = "allwinner,sun6i-a31-ts", 87 .data = (void *)ARCH_SUN6I_A31, 88 }, { /* sentinel */ } 89 }; 90 91 MODULE_DEVICE_TABLE(of, sun4i_gpadc_of_match); 92 93 static int sun4i_gpadc_probe(struct platform_device *pdev) 94 { 95 struct sun4i_gpadc_dev *dev; 96 struct resource *mem; 97 const struct of_device_id *of_id; 98 const struct mfd_cell *cells; 99 unsigned int irq, size; 100 int ret; 101 102 of_id = of_match_node(sun4i_gpadc_of_match, pdev->dev.of_node); 103 if (!of_id) 104 return -EINVAL; 105 106 switch ((long)of_id->data) { 107 case ARCH_SUN4I_A10: 108 cells = sun4i_gpadc_cells; 109 size = ARRAY_SIZE(sun4i_gpadc_cells); 110 break; 111 case ARCH_SUN5I_A13: 112 cells = sun5i_gpadc_cells; 113 size = ARRAY_SIZE(sun5i_gpadc_cells); 114 break; 115 case ARCH_SUN6I_A31: 116 cells = sun6i_gpadc_cells; 117 size = ARRAY_SIZE(sun6i_gpadc_cells); 118 break; 119 default: 120 return -EINVAL; 121 } 122 123 dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); 124 if (!dev) 125 return -ENOMEM; 126 127 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 128 dev->base = devm_ioremap_resource(&pdev->dev, mem); 129 if (IS_ERR(dev->base)) 130 return PTR_ERR(dev->base); 131 132 dev->dev = &pdev->dev; 133 dev_set_drvdata(dev->dev, dev); 134 135 dev->regmap = devm_regmap_init_mmio(dev->dev, dev->base, 136 &sun4i_gpadc_regmap_config); 137 if (IS_ERR(dev->regmap)) { 138 ret = PTR_ERR(dev->regmap); 139 dev_err(&pdev->dev, "failed to init regmap: %d\n", ret); 140 return ret; 141 } 142 143 /* Disable all interrupts */ 144 regmap_write(dev->regmap, SUN4I_GPADC_INT_FIFOC, 0); 145 146 irq = platform_get_irq(pdev, 0); 147 ret = devm_regmap_add_irq_chip(&pdev->dev, dev->regmap, irq, 148 IRQF_ONESHOT, 0, 149 &sun4i_gpadc_regmap_irq_chip, 150 &dev->regmap_irqc); 151 if (ret) { 152 dev_err(&pdev->dev, "failed to add irq chip: %d\n", ret); 153 return ret; 154 } 155 156 ret = devm_mfd_add_devices(dev->dev, 0, cells, size, NULL, 0, NULL); 157 if (ret) { 158 dev_err(&pdev->dev, "failed to add MFD devices: %d\n", ret); 159 return ret; 160 } 161 162 return 0; 163 } 164 165 static struct platform_driver sun4i_gpadc_driver = { 166 .driver = { 167 .name = "sun4i-gpadc", 168 .of_match_table = sun4i_gpadc_of_match, 169 }, 170 .probe = sun4i_gpadc_probe, 171 }; 172 173 module_platform_driver(sun4i_gpadc_driver); 174 175 MODULE_DESCRIPTION("Allwinner sunxi platforms' GPADC MFD core driver"); 176 MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>"); 177 MODULE_LICENSE("GPL v2"); 178