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 .mask_base = SUN4I_GPADC_INT_FIFOC, 38 .init_ack_masked = true, 39 .mask_invert = true, 40 .irqs = sun4i_gpadc_regmap_irq, 41 .num_irqs = ARRAY_SIZE(sun4i_gpadc_regmap_irq), 42 .num_regs = 1, 43 }; 44 45 static struct mfd_cell sun4i_gpadc_cells[] = { 46 { 47 .name = "sun4i-a10-gpadc-iio", 48 .resources = adc_resources, 49 .num_resources = ARRAY_SIZE(adc_resources), 50 }, 51 { .name = "iio_hwmon" } 52 }; 53 54 static struct mfd_cell sun5i_gpadc_cells[] = { 55 { 56 .name = "sun5i-a13-gpadc-iio", 57 .resources = adc_resources, 58 .num_resources = ARRAY_SIZE(adc_resources), 59 }, 60 { .name = "iio_hwmon" }, 61 }; 62 63 static struct mfd_cell sun6i_gpadc_cells[] = { 64 { 65 .name = "sun6i-a31-gpadc-iio", 66 .resources = adc_resources, 67 .num_resources = ARRAY_SIZE(adc_resources), 68 }, 69 { .name = "iio_hwmon" }, 70 }; 71 72 static const struct regmap_config sun4i_gpadc_regmap_config = { 73 .reg_bits = 32, 74 .val_bits = 32, 75 .reg_stride = 4, 76 .fast_io = true, 77 }; 78 79 static const struct of_device_id sun4i_gpadc_of_match[] = { 80 { 81 .compatible = "allwinner,sun4i-a10-ts", 82 .data = (void *)ARCH_SUN4I_A10, 83 }, { 84 .compatible = "allwinner,sun5i-a13-ts", 85 .data = (void *)ARCH_SUN5I_A13, 86 }, { 87 .compatible = "allwinner,sun6i-a31-ts", 88 .data = (void *)ARCH_SUN6I_A31, 89 }, { /* sentinel */ } 90 }; 91 92 MODULE_DEVICE_TABLE(of, sun4i_gpadc_of_match); 93 94 static int sun4i_gpadc_probe(struct platform_device *pdev) 95 { 96 struct sun4i_gpadc_dev *dev; 97 struct resource *mem; 98 const struct of_device_id *of_id; 99 const struct mfd_cell *cells; 100 unsigned int irq, size; 101 int ret; 102 103 of_id = of_match_node(sun4i_gpadc_of_match, pdev->dev.of_node); 104 if (!of_id) 105 return -EINVAL; 106 107 switch ((long)of_id->data) { 108 case ARCH_SUN4I_A10: 109 cells = sun4i_gpadc_cells; 110 size = ARRAY_SIZE(sun4i_gpadc_cells); 111 break; 112 case ARCH_SUN5I_A13: 113 cells = sun5i_gpadc_cells; 114 size = ARRAY_SIZE(sun5i_gpadc_cells); 115 break; 116 case ARCH_SUN6I_A31: 117 cells = sun6i_gpadc_cells; 118 size = ARRAY_SIZE(sun6i_gpadc_cells); 119 break; 120 default: 121 return -EINVAL; 122 } 123 124 dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); 125 if (!dev) 126 return -ENOMEM; 127 128 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 129 dev->base = devm_ioremap_resource(&pdev->dev, mem); 130 if (IS_ERR(dev->base)) 131 return PTR_ERR(dev->base); 132 133 dev->dev = &pdev->dev; 134 dev_set_drvdata(dev->dev, dev); 135 136 dev->regmap = devm_regmap_init_mmio(dev->dev, dev->base, 137 &sun4i_gpadc_regmap_config); 138 if (IS_ERR(dev->regmap)) { 139 ret = PTR_ERR(dev->regmap); 140 dev_err(&pdev->dev, "failed to init regmap: %d\n", ret); 141 return ret; 142 } 143 144 /* Disable all interrupts */ 145 regmap_write(dev->regmap, SUN4I_GPADC_INT_FIFOC, 0); 146 147 irq = platform_get_irq(pdev, 0); 148 ret = devm_regmap_add_irq_chip(&pdev->dev, dev->regmap, irq, 149 IRQF_ONESHOT, 0, 150 &sun4i_gpadc_regmap_irq_chip, 151 &dev->regmap_irqc); 152 if (ret) { 153 dev_err(&pdev->dev, "failed to add irq chip: %d\n", ret); 154 return ret; 155 } 156 157 ret = devm_mfd_add_devices(dev->dev, 0, cells, size, NULL, 0, NULL); 158 if (ret) { 159 dev_err(&pdev->dev, "failed to add MFD devices: %d\n", ret); 160 return ret; 161 } 162 163 return 0; 164 } 165 166 static struct platform_driver sun4i_gpadc_driver = { 167 .driver = { 168 .name = "sun4i-gpadc", 169 .of_match_table = sun4i_gpadc_of_match, 170 }, 171 .probe = sun4i_gpadc_probe, 172 }; 173 174 module_platform_driver(sun4i_gpadc_driver); 175 176 MODULE_DESCRIPTION("Allwinner sunxi platforms' GPADC MFD core driver"); 177 MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>"); 178 MODULE_LICENSE("GPL v2"); 179