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