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