linux/drivers/iio/adc/mt6577_auxadc.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2016 MediaTek Inc.
   3 * Author: Zhiyong Tao <zhiyong.tao@mediatek.com>
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License version 2 as
   7 * published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope that it will be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License for more details.
  13 */
  14
  15#include <linux/clk.h>
  16#include <linux/delay.h>
  17#include <linux/err.h>
  18#include <linux/kernel.h>
  19#include <linux/module.h>
  20#include <linux/of.h>
  21#include <linux/of_device.h>
  22#include <linux/platform_device.h>
  23#include <linux/iopoll.h>
  24#include <linux/io.h>
  25#include <linux/iio/iio.h>
  26
  27/* Register definitions */
  28#define MT6577_AUXADC_CON0                    0x00
  29#define MT6577_AUXADC_CON1                    0x04
  30#define MT6577_AUXADC_CON2                    0x10
  31#define MT6577_AUXADC_STA                     BIT(0)
  32
  33#define MT6577_AUXADC_DAT0                    0x14
  34#define MT6577_AUXADC_RDY0                    BIT(12)
  35
  36#define MT6577_AUXADC_MISC                    0x94
  37#define MT6577_AUXADC_PDN_EN                  BIT(14)
  38
  39#define MT6577_AUXADC_DAT_MASK                0xfff
  40#define MT6577_AUXADC_SLEEP_US                1000
  41#define MT6577_AUXADC_TIMEOUT_US              10000
  42#define MT6577_AUXADC_POWER_READY_MS          1
  43#define MT6577_AUXADC_SAMPLE_READY_US         25
  44
  45struct mt6577_auxadc_device {
  46        void __iomem *reg_base;
  47        struct clk *adc_clk;
  48        struct mutex lock;
  49};
  50
  51#define MT6577_AUXADC_CHANNEL(idx) {                                \
  52                .type = IIO_VOLTAGE,                                \
  53                .indexed = 1,                                       \
  54                .channel = (idx),                                   \
  55                .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
  56}
  57
  58static const struct iio_chan_spec mt6577_auxadc_iio_channels[] = {
  59        MT6577_AUXADC_CHANNEL(0),
  60        MT6577_AUXADC_CHANNEL(1),
  61        MT6577_AUXADC_CHANNEL(2),
  62        MT6577_AUXADC_CHANNEL(3),
  63        MT6577_AUXADC_CHANNEL(4),
  64        MT6577_AUXADC_CHANNEL(5),
  65        MT6577_AUXADC_CHANNEL(6),
  66        MT6577_AUXADC_CHANNEL(7),
  67        MT6577_AUXADC_CHANNEL(8),
  68        MT6577_AUXADC_CHANNEL(9),
  69        MT6577_AUXADC_CHANNEL(10),
  70        MT6577_AUXADC_CHANNEL(11),
  71        MT6577_AUXADC_CHANNEL(12),
  72        MT6577_AUXADC_CHANNEL(13),
  73        MT6577_AUXADC_CHANNEL(14),
  74        MT6577_AUXADC_CHANNEL(15),
  75};
  76
  77static inline void mt6577_auxadc_mod_reg(void __iomem *reg,
  78                                         u32 or_mask, u32 and_mask)
  79{
  80        u32 val;
  81
  82        val = readl(reg);
  83        val |= or_mask;
  84        val &= ~and_mask;
  85        writel(val, reg);
  86}
  87
  88static int mt6577_auxadc_read(struct iio_dev *indio_dev,
  89                              struct iio_chan_spec const *chan)
  90{
  91        u32 val;
  92        void __iomem *reg_channel;
  93        int ret;
  94        struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
  95
  96        reg_channel = adc_dev->reg_base + MT6577_AUXADC_DAT0 +
  97                      chan->channel * 0x04;
  98
  99        mutex_lock(&adc_dev->lock);
 100
 101        mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1,
 102                              0, 1 << chan->channel);
 103
 104        /* read channel and make sure old ready bit == 0 */
 105        ret = readl_poll_timeout(reg_channel, val,
 106                                 ((val & MT6577_AUXADC_RDY0) == 0),
 107                                 MT6577_AUXADC_SLEEP_US,
 108                                 MT6577_AUXADC_TIMEOUT_US);
 109        if (ret < 0) {
 110                dev_err(indio_dev->dev.parent,
 111                        "wait for channel[%d] ready bit clear time out\n",
 112                        chan->channel);
 113                goto err_timeout;
 114        }
 115
 116        /* set bit to trigger sample */
 117        mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1,
 118                              1 << chan->channel, 0);
 119
 120        /* we must delay here for hardware sample channel data */
 121        udelay(MT6577_AUXADC_SAMPLE_READY_US);
 122
 123        /* check MTK_AUXADC_CON2 if auxadc is idle */
 124        ret = readl_poll_timeout(adc_dev->reg_base + MT6577_AUXADC_CON2, val,
 125                                 ((val & MT6577_AUXADC_STA) == 0),
 126                                 MT6577_AUXADC_SLEEP_US,
 127                                 MT6577_AUXADC_TIMEOUT_US);
 128        if (ret < 0) {
 129                dev_err(indio_dev->dev.parent,
 130                        "wait for auxadc idle time out\n");
 131                goto err_timeout;
 132        }
 133
 134        /* read channel and make sure ready bit == 1 */
 135        ret = readl_poll_timeout(reg_channel, val,
 136                                 ((val & MT6577_AUXADC_RDY0) != 0),
 137                                 MT6577_AUXADC_SLEEP_US,
 138                                 MT6577_AUXADC_TIMEOUT_US);
 139        if (ret < 0) {
 140                dev_err(indio_dev->dev.parent,
 141                        "wait for channel[%d] data ready time out\n",
 142                        chan->channel);
 143                goto err_timeout;
 144        }
 145
 146        /* read data */
 147        val = readl(reg_channel) & MT6577_AUXADC_DAT_MASK;
 148
 149        mutex_unlock(&adc_dev->lock);
 150
 151        return val;
 152
 153err_timeout:
 154
 155        mutex_unlock(&adc_dev->lock);
 156
 157        return -ETIMEDOUT;
 158}
 159
 160static int mt6577_auxadc_read_raw(struct iio_dev *indio_dev,
 161                                  struct iio_chan_spec const *chan,
 162                                  int *val,
 163                                  int *val2,
 164                                  long info)
 165{
 166        switch (info) {
 167        case IIO_CHAN_INFO_PROCESSED:
 168                *val = mt6577_auxadc_read(indio_dev, chan);
 169                if (*val < 0) {
 170                        dev_err(indio_dev->dev.parent,
 171                                "failed to sample data on channel[%d]\n",
 172                                chan->channel);
 173                        return *val;
 174                }
 175                return IIO_VAL_INT;
 176
 177        default:
 178                return -EINVAL;
 179        }
 180}
 181
 182static const struct iio_info mt6577_auxadc_info = {
 183        .read_raw = &mt6577_auxadc_read_raw,
 184};
 185
 186static int __maybe_unused mt6577_auxadc_resume(struct device *dev)
 187{
 188        struct iio_dev *indio_dev = dev_get_drvdata(dev);
 189        struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
 190        int ret;
 191
 192        ret = clk_prepare_enable(adc_dev->adc_clk);
 193        if (ret) {
 194                pr_err("failed to enable auxadc clock\n");
 195                return ret;
 196        }
 197
 198        mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
 199                              MT6577_AUXADC_PDN_EN, 0);
 200        mdelay(MT6577_AUXADC_POWER_READY_MS);
 201
 202        return 0;
 203}
 204
 205static int __maybe_unused mt6577_auxadc_suspend(struct device *dev)
 206{
 207        struct iio_dev *indio_dev = dev_get_drvdata(dev);
 208        struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
 209
 210        mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
 211                              0, MT6577_AUXADC_PDN_EN);
 212        clk_disable_unprepare(adc_dev->adc_clk);
 213
 214        return 0;
 215}
 216
 217static int mt6577_auxadc_probe(struct platform_device *pdev)
 218{
 219        struct mt6577_auxadc_device *adc_dev;
 220        unsigned long adc_clk_rate;
 221        struct resource *res;
 222        struct iio_dev *indio_dev;
 223        int ret;
 224
 225        indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev));
 226        if (!indio_dev)
 227                return -ENOMEM;
 228
 229        adc_dev = iio_priv(indio_dev);
 230        indio_dev->dev.parent = &pdev->dev;
 231        indio_dev->name = dev_name(&pdev->dev);
 232        indio_dev->info = &mt6577_auxadc_info;
 233        indio_dev->modes = INDIO_DIRECT_MODE;
 234        indio_dev->channels = mt6577_auxadc_iio_channels;
 235        indio_dev->num_channels = ARRAY_SIZE(mt6577_auxadc_iio_channels);
 236
 237        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 238        adc_dev->reg_base = devm_ioremap_resource(&pdev->dev, res);
 239        if (IS_ERR(adc_dev->reg_base)) {
 240                dev_err(&pdev->dev, "failed to get auxadc base address\n");
 241                return PTR_ERR(adc_dev->reg_base);
 242        }
 243
 244        adc_dev->adc_clk = devm_clk_get(&pdev->dev, "main");
 245        if (IS_ERR(adc_dev->adc_clk)) {
 246                dev_err(&pdev->dev, "failed to get auxadc clock\n");
 247                return PTR_ERR(adc_dev->adc_clk);
 248        }
 249
 250        ret = clk_prepare_enable(adc_dev->adc_clk);
 251        if (ret) {
 252                dev_err(&pdev->dev, "failed to enable auxadc clock\n");
 253                return ret;
 254        }
 255
 256        adc_clk_rate = clk_get_rate(adc_dev->adc_clk);
 257        if (!adc_clk_rate) {
 258                ret = -EINVAL;
 259                dev_err(&pdev->dev, "null clock rate\n");
 260                goto err_disable_clk;
 261        }
 262
 263        mutex_init(&adc_dev->lock);
 264
 265        mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
 266                              MT6577_AUXADC_PDN_EN, 0);
 267        mdelay(MT6577_AUXADC_POWER_READY_MS);
 268
 269        platform_set_drvdata(pdev, indio_dev);
 270
 271        ret = iio_device_register(indio_dev);
 272        if (ret < 0) {
 273                dev_err(&pdev->dev, "failed to register iio device\n");
 274                goto err_power_off;
 275        }
 276
 277        return 0;
 278
 279err_power_off:
 280        mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
 281                              0, MT6577_AUXADC_PDN_EN);
 282err_disable_clk:
 283        clk_disable_unprepare(adc_dev->adc_clk);
 284        return ret;
 285}
 286
 287static int mt6577_auxadc_remove(struct platform_device *pdev)
 288{
 289        struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 290        struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
 291
 292        iio_device_unregister(indio_dev);
 293
 294        mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
 295                              0, MT6577_AUXADC_PDN_EN);
 296
 297        clk_disable_unprepare(adc_dev->adc_clk);
 298
 299        return 0;
 300}
 301
 302static SIMPLE_DEV_PM_OPS(mt6577_auxadc_pm_ops,
 303                         mt6577_auxadc_suspend,
 304                         mt6577_auxadc_resume);
 305
 306static const struct of_device_id mt6577_auxadc_of_match[] = {
 307        { .compatible = "mediatek,mt2701-auxadc", },
 308        { .compatible = "mediatek,mt2712-auxadc", },
 309        { .compatible = "mediatek,mt7622-auxadc", },
 310        { .compatible = "mediatek,mt8173-auxadc", },
 311        { }
 312};
 313MODULE_DEVICE_TABLE(of, mt6577_auxadc_of_match);
 314
 315static struct platform_driver mt6577_auxadc_driver = {
 316        .driver = {
 317                .name   = "mt6577-auxadc",
 318                .of_match_table = mt6577_auxadc_of_match,
 319                .pm = &mt6577_auxadc_pm_ops,
 320        },
 321        .probe  = mt6577_auxadc_probe,
 322        .remove = mt6577_auxadc_remove,
 323};
 324module_platform_driver(mt6577_auxadc_driver);
 325
 326MODULE_AUTHOR("Zhiyong Tao <zhiyong.tao@mediatek.com>");
 327MODULE_DESCRIPTION("MTK AUXADC Device Driver");
 328MODULE_LICENSE("GPL v2");
 329