linux/drivers/mfd/mxs-lradc.c
<<
>>
Prefs
   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
  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        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                        fallthrough;    /* 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
 232err_clk:
 233        clk_disable_unprepare(lradc->clk);
 234
 235        return ret;
 236}
 237
 238static 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
 247static 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};
 255module_platform_driver(mxs_lradc_driver);
 256
 257MODULE_AUTHOR("Ksenija Stanojevic <ksenija.stanojevic@gmail.com>");
 258MODULE_DESCRIPTION("Freescale i.MX23/i.MX28 LRADC driver");
 259MODULE_LICENSE("GPL");
 260MODULE_ALIAS("platform:mxs-lradc");
 261