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};
 107
 108static int lpc18xx_dac_probe(struct platform_device *pdev)
 109{
 110        struct iio_dev *indio_dev;
 111        struct lpc18xx_dac *dac;
 112        struct resource *res;
 113        int ret;
 114
 115        indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*dac));
 116        if (!indio_dev)
 117                return -ENOMEM;
 118
 119        platform_set_drvdata(pdev, indio_dev);
 120        dac = iio_priv(indio_dev);
 121        mutex_init(&dac->lock);
 122
 123        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 124        dac->base = devm_ioremap_resource(&pdev->dev, res);
 125        if (IS_ERR(dac->base))
 126                return PTR_ERR(dac->base);
 127
 128        dac->clk = devm_clk_get(&pdev->dev, NULL);
 129        if (IS_ERR(dac->clk)) {
 130                dev_err(&pdev->dev, "error getting clock\n");
 131                return PTR_ERR(dac->clk);
 132        }
 133
 134        dac->vref = devm_regulator_get(&pdev->dev, "vref");
 135        if (IS_ERR(dac->vref)) {
 136                dev_err(&pdev->dev, "error getting regulator\n");
 137                return PTR_ERR(dac->vref);
 138        }
 139
 140        indio_dev->name = dev_name(&pdev->dev);
 141        indio_dev->dev.parent = &pdev->dev;
 142        indio_dev->info = &lpc18xx_dac_info;
 143        indio_dev->modes = INDIO_DIRECT_MODE;
 144        indio_dev->channels = lpc18xx_dac_iio_channels;
 145        indio_dev->num_channels = ARRAY_SIZE(lpc18xx_dac_iio_channels);
 146
 147        ret = regulator_enable(dac->vref);
 148        if (ret) {
 149                dev_err(&pdev->dev, "unable to enable regulator\n");
 150                return ret;
 151        }
 152
 153        ret = clk_prepare_enable(dac->clk);
 154        if (ret) {
 155                dev_err(&pdev->dev, "unable to enable clock\n");
 156                goto dis_reg;
 157        }
 158
 159        writel(0, dac->base + LPC18XX_DAC_CTRL);
 160        writel(0, dac->base + LPC18XX_DAC_CR);
 161
 162        ret = iio_device_register(indio_dev);
 163        if (ret) {
 164                dev_err(&pdev->dev, "unable to register device\n");
 165                goto dis_clk;
 166        }
 167
 168        return 0;
 169
 170dis_clk:
 171        clk_disable_unprepare(dac->clk);
 172dis_reg:
 173        regulator_disable(dac->vref);
 174        return ret;
 175}
 176
 177static int lpc18xx_dac_remove(struct platform_device *pdev)
 178{
 179        struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 180        struct lpc18xx_dac *dac = iio_priv(indio_dev);
 181
 182        iio_device_unregister(indio_dev);
 183
 184        writel(0, dac->base + LPC18XX_DAC_CTRL);
 185        clk_disable_unprepare(dac->clk);
 186        regulator_disable(dac->vref);
 187
 188        return 0;
 189}
 190
 191static const struct of_device_id lpc18xx_dac_match[] = {
 192        { .compatible = "nxp,lpc1850-dac" },
 193        { /* sentinel */ }
 194};
 195MODULE_DEVICE_TABLE(of, lpc18xx_dac_match);
 196
 197static struct platform_driver lpc18xx_dac_driver = {
 198        .probe  = lpc18xx_dac_probe,
 199        .remove = lpc18xx_dac_remove,
 200        .driver = {
 201                .name = "lpc18xx-dac",
 202                .of_match_table = lpc18xx_dac_match,
 203        },
 204};
 205module_platform_driver(lpc18xx_dac_driver);
 206
 207MODULE_DESCRIPTION("LPC18xx DAC driver");
 208MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
 209MODULE_LICENSE("GPL v2");
 210