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.h> 12 #include <linux/platform_device.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 const struct of_device_id *of_id; 97 const struct mfd_cell *cells; 98 unsigned int irq, size; 99 int ret; 100 101 of_id = of_match_node(sun4i_gpadc_of_match, pdev->dev.of_node); 102 if (!of_id) 103 return -EINVAL; 104 105 switch ((long)of_id->data) { 106 case ARCH_SUN4I_A10: 107 cells = sun4i_gpadc_cells; 108 size = ARRAY_SIZE(sun4i_gpadc_cells); 109 break; 110 case ARCH_SUN5I_A13: 111 cells = sun5i_gpadc_cells; 112 size = ARRAY_SIZE(sun5i_gpadc_cells); 113 break; 114 case ARCH_SUN6I_A31: 115 cells = sun6i_gpadc_cells; 116 size = ARRAY_SIZE(sun6i_gpadc_cells); 117 break; 118 default: 119 return -EINVAL; 120 } 121 122 dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); 123 if (!dev) 124 return -ENOMEM; 125 126 dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); 127 if (IS_ERR(dev->base)) 128 return PTR_ERR(dev->base); 129 130 dev->dev = &pdev->dev; 131 dev_set_drvdata(dev->dev, dev); 132 133 dev->regmap = devm_regmap_init_mmio(dev->dev, dev->base, 134 &sun4i_gpadc_regmap_config); 135 if (IS_ERR(dev->regmap)) { 136 ret = PTR_ERR(dev->regmap); 137 dev_err(&pdev->dev, "failed to init regmap: %d\n", ret); 138 return ret; 139 } 140 141 /* Disable all interrupts */ 142 regmap_write(dev->regmap, SUN4I_GPADC_INT_FIFOC, 0); 143 144 irq = platform_get_irq(pdev, 0); 145 ret = devm_regmap_add_irq_chip(&pdev->dev, dev->regmap, irq, 146 IRQF_ONESHOT, 0, 147 &sun4i_gpadc_regmap_irq_chip, 148 &dev->regmap_irqc); 149 if (ret) { 150 dev_err(&pdev->dev, "failed to add irq chip: %d\n", ret); 151 return ret; 152 } 153 154 ret = devm_mfd_add_devices(dev->dev, 0, cells, size, NULL, 0, NULL); 155 if (ret) { 156 dev_err(&pdev->dev, "failed to add MFD devices: %d\n", ret); 157 return ret; 158 } 159 160 return 0; 161 } 162 163 static struct platform_driver sun4i_gpadc_driver = { 164 .driver = { 165 .name = "sun4i-gpadc", 166 .of_match_table = sun4i_gpadc_of_match, 167 }, 168 .probe = sun4i_gpadc_probe, 169 }; 170 171 module_platform_driver(sun4i_gpadc_driver); 172 173 MODULE_DESCRIPTION("Allwinner sunxi platforms' GPADC MFD core driver"); 174 MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>"); 175 MODULE_LICENSE("GPL v2"); 176