linux/drivers/iio/dac/lpc18xx_dac.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * IIO DAC driver for NXP LPC18xx DAC
   4 *
   5 * Copyright (C) 2016 Joachim Eastwood <manabian@gmail.com>
   6 *
   7 * UNSUPPORTED hardware features:
   8 *  - Interrupts
   9 *  - DMA
  10 */
  11
  12#include <linux/clk.h>
  13#include <linux/err.h>
  14#include <linux/iio/iio.h>
  15#include <linux/iio/driver.h>
  16#include <linux/io.h>
  17#include <linux/iopoll.h>
  18#include <linux/module.h>
  19#include <linux/mutex.h>
  20#include <linux/of.h>
  21#include <linux/of_device.h>
  22#include <linux/platform_device.h>
  23#include <linux/regulator/consumer.h>
  24
  25/* LPC18XX DAC registers and bits */
  26#define LPC18XX_DAC_CR                  0x000
  27#define  LPC18XX_DAC_CR_VALUE_SHIFT     6
  28#define  LPC18XX_DAC_CR_VALUE_MASK      0x3ff
  29#define  LPC18XX_DAC_CR_BIAS            BIT(16)
  30#define LPC18XX_DAC_CTRL                0x004
  31#define  LPC18XX_DAC_CTRL_DMA_ENA       BIT(3)
  32
  33struct lpc18xx_dac {
  34        struct regulator *vref;
  35        void __iomem *base;
  36        struct mutex lock;
  37        struct clk *clk;
  38};
  39
  40static const struct iio_chan_spec lpc18xx_dac_iio_channels[] = {
  41        {
  42                .type = IIO_VOLTAGE,
  43                .output = 1,
  44                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
  45                                      BIT(IIO_CHAN_INFO_SCALE),
  46        },
  47};
  48
  49static int lpc18xx_dac_read_raw(struct iio_dev *indio_dev,
  50                                struct iio_chan_spec const *chan,
  51                                int *val, int *val2, long mask)
  52{
  53        struct lpc18xx_dac *dac = iio_priv(indio_dev);
  54        u32 reg;
  55
  56        switch (mask) {
  57        case IIO_CHAN_INFO_RAW:
  58                reg = readl(dac->base + LPC18XX_DAC_CR);
  59                *val = reg >> LPC18XX_DAC_CR_VALUE_SHIFT;
  60                *val &= LPC18XX_DAC_CR_VALUE_MASK;
  61
  62                return IIO_VAL_INT;
  63
  64        case IIO_CHAN_INFO_SCALE:
  65                *val = regulator_get_voltage(dac->vref) / 1000;
  66                *val2 = 10;
  67
  68                return IIO_VAL_FRACTIONAL_LOG2;
  69        }
  70
  71        return -EINVAL;
  72}
  73
  74static int lpc18xx_dac_write_raw(struct iio_dev *indio_dev,
  75                                 struct iio_chan_spec const *chan,
  76                                 int val, int val2, long mask)
  77{
  78        struct lpc18xx_dac *dac = iio_priv(indio_dev);
  79        u32 reg;
  80
  81        switch (mask) {
  82        case IIO_CHAN_INFO_RAW:
  83                if (val < 0 || val > LPC18XX_DAC_CR_VALUE_MASK)
  84                        return -EINVAL;
  85
  86                reg = LPC18XX_DAC_CR_BIAS;
  87                reg |= val << LPC18XX_DAC_CR_VALUE_SHIFT;
  88
  89                mutex_lock(&dac->lock);
  90                writel(reg, dac->base + LPC18XX_DAC_CR);
  91                writel(LPC18XX_DAC_CTRL_DMA_ENA, dac->base + LPC18XX_DAC_CTRL);
  92                mutex_unlock(&dac->lock);
  93
  94                return 0;
  95        }
  96
  97        return -EINVAL;
  98}
  99
 100static const struct iio_info lpc18xx_dac_info = {
 101        .read_raw = lpc18xx_dac_read_raw,
 102        .write_raw = lpc18xx_dac_write_raw,
 103};
 104
 105static int lpc18xx_dac_probe(struct platform_device *pdev)
 106{
 107        struct iio_dev *indio_dev;
 108        struct lpc18xx_dac *dac;
 109        int ret;
 110
 111        indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*dac));
 112        if (!indio_dev)
 113                return -ENOMEM;
 114
 115        platform_set_drvdata(pdev, indio_dev);
 116        dac = iio_priv(indio_dev);
 117        mutex_init(&dac->lock);
 118
 119        dac->base = devm_platform_ioremap_resource(pdev, 0);
 120        if (IS_ERR(dac->base))
 121                return PTR_ERR(dac->base);
 122
 123        dac->clk = devm_clk_get(&pdev->dev, NULL);
 124        if (IS_ERR(dac->clk)) {
 125                dev_err(&pdev->dev, "error getting clock\n");
 126                return PTR_ERR(dac->clk);
 127        }
 128
 129        dac->vref = devm_regulator_get(&pdev->dev, "vref");
 130        if (IS_ERR(dac->vref)) {
 131                dev_err(&pdev->dev, "error getting regulator\n");
 132                return PTR_ERR(dac->vref);
 133        }
 134
 135        indio_dev->name = dev_name(&pdev->dev);
 136        indio_dev->info = &lpc18xx_dac_info;
 137        indio_dev->modes = INDIO_DIRECT_MODE;
 138        indio_dev->channels = lpc18xx_dac_iio_channels;
 139        indio_dev->num_channels = ARRAY_SIZE(lpc18xx_dac_iio_channels);
 140
 141        ret = regulator_enable(dac->vref);
 142        if (ret) {
 143                dev_err(&pdev->dev, "unable to enable regulator\n");
 144                return ret;
 145        }
 146
 147        ret = clk_prepare_enable(dac->clk);
 148        if (ret) {
 149                dev_err(&pdev->dev, "unable to enable clock\n");
 150                goto dis_reg;
 151        }
 152
 153        writel(0, dac->base + LPC18XX_DAC_CTRL);
 154        writel(0, dac->base + LPC18XX_DAC_CR);
 155
 156        ret = iio_device_register(indio_dev);
 157        if (ret) {
 158                dev_err(&pdev->dev, "unable to register device\n");
 159                goto dis_clk;
 160        }
 161
 162        return 0;
 163
 164dis_clk:
 165        clk_disable_unprepare(dac->clk);
 166dis_reg:
 167        regulator_disable(dac->vref);
 168        return ret;
 169}
 170
 171static int lpc18xx_dac_remove(struct platform_device *pdev)
 172{
 173        struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 174        struct lpc18xx_dac *dac = iio_priv(indio_dev);
 175
 176        iio_device_unregister(indio_dev);
 177
 178        writel(0, dac->base + LPC18XX_DAC_CTRL);
 179        clk_disable_unprepare(dac->clk);
 180        regulator_disable(dac->vref);
 181
 182        return 0;
 183}
 184
 185static const struct of_device_id lpc18xx_dac_match[] = {
 186        { .compatible = "nxp,lpc1850-dac" },
 187        { /* sentinel */ }
 188};
 189MODULE_DEVICE_TABLE(of, lpc18xx_dac_match);
 190
 191static struct platform_driver lpc18xx_dac_driver = {
 192        .probe  = lpc18xx_dac_probe,
 193        .remove = lpc18xx_dac_remove,
 194        .driver = {
 195                .name = "lpc18xx-dac",
 196                .of_match_table = lpc18xx_dac_match,
 197        },
 198};
 199module_platform_driver(lpc18xx_dac_driver);
 200
 201MODULE_DESCRIPTION("LPC18xx DAC driver");
 202MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
 203MODULE_LICENSE("GPL v2");
 204