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/of_device.h> 20 #include <linux/platform_device.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 const struct of_device_id *of_id; 129 struct device *dev = &pdev->dev; 130 struct device_node *node = dev->of_node; 131 struct mxs_lradc *lradc; 132 struct mfd_cell *cells = NULL; 133 struct resource *res; 134 int ret = 0; 135 u32 ts_wires = 0; 136 137 lradc = devm_kzalloc(&pdev->dev, sizeof(*lradc), GFP_KERNEL); 138 if (!lradc) 139 return -ENOMEM; 140 141 of_id = of_match_device(mxs_lradc_dt_ids, &pdev->dev); 142 if (!of_id) 143 return -EINVAL; 144 145 lradc->soc = (enum mxs_lradc_id)of_id->data; 146 147 lradc->clk = devm_clk_get(&pdev->dev, NULL); 148 if (IS_ERR(lradc->clk)) { 149 dev_err(dev, "Failed to get the delay unit clock\n"); 150 return PTR_ERR(lradc->clk); 151 } 152 153 ret = clk_prepare_enable(lradc->clk); 154 if (ret) { 155 dev_err(dev, "Failed to enable the delay unit clock\n"); 156 return ret; 157 } 158 159 ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires", 160 &ts_wires); 161 162 if (!ret) { 163 lradc->buffer_vchans = BUFFER_VCHANS_LIMITED; 164 165 switch (ts_wires) { 166 case 4: 167 lradc->touchscreen_wire = MXS_LRADC_TOUCHSCREEN_4WIRE; 168 break; 169 case 5: 170 if (lradc->soc == IMX28_LRADC) { 171 lradc->touchscreen_wire = 172 MXS_LRADC_TOUCHSCREEN_5WIRE; 173 break; 174 } 175 /* fall through - to an error message for i.MX23 */ 176 default: 177 dev_err(&pdev->dev, 178 "Unsupported number of touchscreen wires (%d)\n" 179 , ts_wires); 180 ret = -EINVAL; 181 goto err_clk; 182 } 183 } else { 184 lradc->buffer_vchans = BUFFER_VCHANS_ALL; 185 } 186 187 platform_set_drvdata(pdev, lradc); 188 189 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 190 if (!res) { 191 ret = -ENOMEM; 192 goto err_clk; 193 } 194 195 switch (lradc->soc) { 196 case IMX23_LRADC: 197 mx23_adc_resources[RES_MEM] = *res; 198 mx23_touchscreen_resources[RES_MEM] = *res; 199 cells = mx23_cells; 200 break; 201 case IMX28_LRADC: 202 mx28_adc_resources[RES_MEM] = *res; 203 mx28_touchscreen_resources[RES_MEM] = *res; 204 cells = mx28_cells; 205 break; 206 default: 207 dev_err(dev, "Unsupported SoC\n"); 208 ret = -ENODEV; 209 goto err_clk; 210 } 211 212 ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, 213 &cells[ADC_CELL], 1, NULL, 0, NULL); 214 if (ret) { 215 dev_err(&pdev->dev, "Failed to add the ADC subdevice\n"); 216 goto err_clk; 217 } 218 219 if (!lradc->touchscreen_wire) 220 return 0; 221 222 ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, 223 &cells[TSC_CELL], 1, NULL, 0, NULL); 224 if (ret) { 225 dev_err(&pdev->dev, 226 "Failed to add the touchscreen subdevice\n"); 227 goto err_clk; 228 } 229 230 return 0; 231 232 err_clk: 233 clk_disable_unprepare(lradc->clk); 234 235 return ret; 236 } 237 238 static int mxs_lradc_remove(struct platform_device *pdev) 239 { 240 struct mxs_lradc *lradc = platform_get_drvdata(pdev); 241 242 clk_disable_unprepare(lradc->clk); 243 244 return 0; 245 } 246 247 static struct platform_driver mxs_lradc_driver = { 248 .driver = { 249 .name = "mxs-lradc", 250 .of_match_table = mxs_lradc_dt_ids, 251 }, 252 .probe = mxs_lradc_probe, 253 .remove = mxs_lradc_remove, 254 }; 255 module_platform_driver(mxs_lradc_driver); 256 257 MODULE_AUTHOR("Ksenija Stanojevic <ksenija.stanojevic@gmail.com>"); 258 MODULE_DESCRIPTION("Freescale i.MX23/i.MX28 LRADC driver"); 259 MODULE_LICENSE("GPL"); 260 MODULE_ALIAS("platform:mxs-lradc"); 261