1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Freescale MXS Low Resolution Analog-to-Digital Converter driver 4 * 5 * Copyright (c) 2012 DENX Software Engineering, GmbH. 6 * Copyright (c) 2017 Ksenija Stanojevic <ksenija.stanojevic@gmail.com> 7 * 8 * Authors: 9 * Marek Vasut <marex@denx.de> 10 * Ksenija Stanojevic <ksenija.stanojevic@gmail.com> 11 */ 12 13 #include <linux/clk.h> 14 #include <linux/device.h> 15 #include <linux/mfd/core.h> 16 #include <linux/mfd/mxs-lradc.h> 17 #include <linux/module.h> 18 #include <linux/of.h> 19 #include <linux/platform_device.h> 20 #include <linux/property.h> 21 #include <linux/slab.h> 22 23 #define ADC_CELL 0 24 #define TSC_CELL 1 25 #define RES_MEM 0 26 27 enum mx23_lradc_irqs { 28 MX23_LRADC_TS_IRQ = 0, 29 MX23_LRADC_CH0_IRQ, 30 MX23_LRADC_CH1_IRQ, 31 MX23_LRADC_CH2_IRQ, 32 MX23_LRADC_CH3_IRQ, 33 MX23_LRADC_CH4_IRQ, 34 MX23_LRADC_CH5_IRQ, 35 MX23_LRADC_CH6_IRQ, 36 MX23_LRADC_CH7_IRQ, 37 }; 38 39 enum mx28_lradc_irqs { 40 MX28_LRADC_TS_IRQ = 0, 41 MX28_LRADC_TRESH0_IRQ, 42 MX28_LRADC_TRESH1_IRQ, 43 MX28_LRADC_CH0_IRQ, 44 MX28_LRADC_CH1_IRQ, 45 MX28_LRADC_CH2_IRQ, 46 MX28_LRADC_CH3_IRQ, 47 MX28_LRADC_CH4_IRQ, 48 MX28_LRADC_CH5_IRQ, 49 MX28_LRADC_CH6_IRQ, 50 MX28_LRADC_CH7_IRQ, 51 MX28_LRADC_BUTTON0_IRQ, 52 MX28_LRADC_BUTTON1_IRQ, 53 }; 54 55 static struct resource mx23_adc_resources[] = { 56 DEFINE_RES_MEM(0x0, 0x0), 57 DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH0_IRQ, "mxs-lradc-channel0"), 58 DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH1_IRQ, "mxs-lradc-channel1"), 59 DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH2_IRQ, "mxs-lradc-channel2"), 60 DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH3_IRQ, "mxs-lradc-channel3"), 61 DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH4_IRQ, "mxs-lradc-channel4"), 62 DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH5_IRQ, "mxs-lradc-channel5"), 63 }; 64 65 static struct resource mx23_touchscreen_resources[] = { 66 DEFINE_RES_MEM(0x0, 0x0), 67 DEFINE_RES_IRQ_NAMED(MX23_LRADC_TS_IRQ, "mxs-lradc-touchscreen"), 68 DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH6_IRQ, "mxs-lradc-channel6"), 69 DEFINE_RES_IRQ_NAMED(MX23_LRADC_CH7_IRQ, "mxs-lradc-channel7"), 70 }; 71 72 static struct resource mx28_adc_resources[] = { 73 DEFINE_RES_MEM(0x0, 0x0), 74 DEFINE_RES_IRQ_NAMED(MX28_LRADC_TRESH0_IRQ, "mxs-lradc-thresh0"), 75 DEFINE_RES_IRQ_NAMED(MX28_LRADC_TRESH1_IRQ, "mxs-lradc-thresh1"), 76 DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH0_IRQ, "mxs-lradc-channel0"), 77 DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH1_IRQ, "mxs-lradc-channel1"), 78 DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH2_IRQ, "mxs-lradc-channel2"), 79 DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH3_IRQ, "mxs-lradc-channel3"), 80 DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH4_IRQ, "mxs-lradc-channel4"), 81 DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH5_IRQ, "mxs-lradc-channel5"), 82 DEFINE_RES_IRQ_NAMED(MX28_LRADC_BUTTON0_IRQ, "mxs-lradc-button0"), 83 DEFINE_RES_IRQ_NAMED(MX28_LRADC_BUTTON1_IRQ, "mxs-lradc-button1"), 84 }; 85 86 static struct resource mx28_touchscreen_resources[] = { 87 DEFINE_RES_MEM(0x0, 0x0), 88 DEFINE_RES_IRQ_NAMED(MX28_LRADC_TS_IRQ, "mxs-lradc-touchscreen"), 89 DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH6_IRQ, "mxs-lradc-channel6"), 90 DEFINE_RES_IRQ_NAMED(MX28_LRADC_CH7_IRQ, "mxs-lradc-channel7"), 91 }; 92 93 static struct mfd_cell mx23_cells[] = { 94 { 95 .name = "mxs-lradc-adc", 96 .resources = mx23_adc_resources, 97 .num_resources = ARRAY_SIZE(mx23_adc_resources), 98 }, 99 { 100 .name = "mxs-lradc-ts", 101 .resources = mx23_touchscreen_resources, 102 .num_resources = ARRAY_SIZE(mx23_touchscreen_resources), 103 }, 104 }; 105 106 static struct mfd_cell mx28_cells[] = { 107 { 108 .name = "mxs-lradc-adc", 109 .resources = mx28_adc_resources, 110 .num_resources = ARRAY_SIZE(mx28_adc_resources), 111 }, 112 { 113 .name = "mxs-lradc-ts", 114 .resources = mx28_touchscreen_resources, 115 .num_resources = ARRAY_SIZE(mx28_touchscreen_resources), 116 } 117 }; 118 119 static const struct of_device_id mxs_lradc_dt_ids[] = { 120 { .compatible = "fsl,imx23-lradc", .data = (void *)IMX23_LRADC, }, 121 { .compatible = "fsl,imx28-lradc", .data = (void *)IMX28_LRADC, }, 122 { /* sentinel */ } 123 }; 124 MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids); 125 126 static int mxs_lradc_probe(struct platform_device *pdev) 127 { 128 struct device *dev = &pdev->dev; 129 struct device_node *node = dev->of_node; 130 struct mxs_lradc *lradc; 131 struct mfd_cell *cells = NULL; 132 struct resource *res; 133 int ret = 0; 134 u32 ts_wires = 0; 135 136 lradc = devm_kzalloc(&pdev->dev, sizeof(*lradc), GFP_KERNEL); 137 if (!lradc) 138 return -ENOMEM; 139 140 lradc->soc = (kernel_ulong_t)device_get_match_data(&pdev->dev); 141 142 lradc->clk = devm_clk_get(&pdev->dev, NULL); 143 if (IS_ERR(lradc->clk)) { 144 dev_err(dev, "Failed to get the delay unit clock\n"); 145 return PTR_ERR(lradc->clk); 146 } 147 148 ret = clk_prepare_enable(lradc->clk); 149 if (ret) { 150 dev_err(dev, "Failed to enable the delay unit clock\n"); 151 return ret; 152 } 153 154 ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires", 155 &ts_wires); 156 157 if (!ret) { 158 lradc->buffer_vchans = BUFFER_VCHANS_LIMITED; 159 160 switch (ts_wires) { 161 case 4: 162 lradc->touchscreen_wire = MXS_LRADC_TOUCHSCREEN_4WIRE; 163 break; 164 case 5: 165 if (lradc->soc == IMX28_LRADC) { 166 lradc->touchscreen_wire = 167 MXS_LRADC_TOUCHSCREEN_5WIRE; 168 break; 169 } 170 fallthrough; /* to an error message for i.MX23 */ 171 default: 172 dev_err(&pdev->dev, 173 "Unsupported number of touchscreen wires (%d)\n" 174 , ts_wires); 175 ret = -EINVAL; 176 goto err_clk; 177 } 178 } else { 179 lradc->buffer_vchans = BUFFER_VCHANS_ALL; 180 } 181 182 platform_set_drvdata(pdev, lradc); 183 184 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 185 if (!res) { 186 ret = -ENOMEM; 187 goto err_clk; 188 } 189 190 switch (lradc->soc) { 191 case IMX23_LRADC: 192 mx23_adc_resources[RES_MEM] = *res; 193 mx23_touchscreen_resources[RES_MEM] = *res; 194 cells = mx23_cells; 195 break; 196 case IMX28_LRADC: 197 mx28_adc_resources[RES_MEM] = *res; 198 mx28_touchscreen_resources[RES_MEM] = *res; 199 cells = mx28_cells; 200 break; 201 default: 202 dev_err(dev, "Unsupported SoC\n"); 203 ret = -ENODEV; 204 goto err_clk; 205 } 206 207 ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, 208 &cells[ADC_CELL], 1, NULL, 0, NULL); 209 if (ret) { 210 dev_err(&pdev->dev, "Failed to add the ADC subdevice\n"); 211 goto err_clk; 212 } 213 214 if (!lradc->touchscreen_wire) 215 return 0; 216 217 ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, 218 &cells[TSC_CELL], 1, NULL, 0, NULL); 219 if (ret) { 220 dev_err(&pdev->dev, 221 "Failed to add the touchscreen subdevice\n"); 222 goto err_clk; 223 } 224 225 return 0; 226 227 err_clk: 228 clk_disable_unprepare(lradc->clk); 229 230 return ret; 231 } 232 233 static void mxs_lradc_remove(struct platform_device *pdev) 234 { 235 struct mxs_lradc *lradc = platform_get_drvdata(pdev); 236 237 clk_disable_unprepare(lradc->clk); 238 } 239 240 static struct platform_driver mxs_lradc_driver = { 241 .driver = { 242 .name = "mxs-lradc", 243 .of_match_table = mxs_lradc_dt_ids, 244 }, 245 .probe = mxs_lradc_probe, 246 .remove = mxs_lradc_remove, 247 }; 248 module_platform_driver(mxs_lradc_driver); 249 250 MODULE_AUTHOR("Ksenija Stanojevic <ksenija.stanojevic@gmail.com>"); 251 MODULE_DESCRIPTION("Freescale i.MX23/i.MX28 LRADC driver"); 252 MODULE_LICENSE("GPL"); 253 MODULE_ALIAS("platform:mxs-lradc"); 254