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