254 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Freescale MXS Low Resolution Analog-to-Digital Converter driver4 *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 024#define TSC_CELL 125#define RES_MEM 026 27enum 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 39enum 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 55static 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 65static 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 72static 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 86static 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 93static 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 106static 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 119static 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};124MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids);125 126static 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 227err_clk:228 clk_disable_unprepare(lradc->clk);229 230 return ret;231}232 233static 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 240static 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_new = mxs_lradc_remove,247};248module_platform_driver(mxs_lradc_driver);249 250MODULE_AUTHOR("Ksenija Stanojevic <ksenija.stanojevic@gmail.com>");251MODULE_DESCRIPTION("Freescale i.MX23/i.MX28 LRADC driver");252MODULE_LICENSE("GPL");253MODULE_ALIAS("platform:mxs-lradc");254