linux/drivers/iio/adc/rockchip_saradc.c
Linus Torvalds dab363f938 Staging patches for 3.19-rc1
Here's the big staging tree pull request for 3.19-rc1.
 
 We continued to delete more lines than were added, always a good thing,
 but not at a huge rate this release, only about 70k lines removed
 overall mostly from removing the horrid bcm driver.
 
 Lots of normal staging driver cleanups and fixes all over the place,
 well over a thousand of them, the shortlog shows all the horrid details.
 
 The "contentious" thing here is the movement of the Android binder code
 out of staging into the "real" part of the kernel.  This is code that
 has been stable for a few years now and is working as-is in the tens of
 millions of devices with no issues.  Yes, the code is horrid, and the
 userspace api leaves a lot to be desired, but it's not going to change
 due to legacy issues that we have no control over.  Because so many
 devices and companies rely on this, and the code is stable, might as
 well promote it out of staging.
 
 This was all discussed at the Linux Plumbers conference, and everyone
 participating agreed that this was the best way forward.
 
 There is work happening to replace the binder code with something new
 that is happening right now, but I don't expect to see the results of
 that work for another year at the earliest.  If that ever happens, and
 Android switches over to it, I'll gladly remove this version.
 
 As for maintainers, I'll be glad to maintain this code, I've been doing
 it for the past few years with no problems.  I'll send a MAINTAINERS
 entry for it before 3.19-final is out, still need to talk to the Google
 developers about if they are willing to help with it or not, last I
 checked they were, which was good.
 
 All of these patches have been in linux-next for a while with no
 reported issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iEYEABECAAYFAlSPICkACgkQMUfUDdst+yksdwCfSLE9VUy1o2sAPDRe+J3bQced
 EWEAoL3RtnejKbo5tHS2IT69pLrwiIDS
 =YXyM
 -----END PGP SIGNATURE-----

Merge tag 'staging-3.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging

Pull staging driver updates from Greg KH:
 "Here's the big staging tree pull request for 3.19-rc1.

  We continued to delete more lines than were added, always a good
  thing, but not at a huge rate this release, only about 70k lines
  removed overall mostly from removing the horrid bcm driver.

  Lots of normal staging driver cleanups and fixes all over the place,
  well over a thousand of them, the shortlog shows all the horrid
  details.

  The "contentious" thing here is the movement of the Android binder
  code out of staging into the "real" part of the kernel.  This is code
  that has been stable for a few years now and is working as-is in the
  tens of millions of devices with no issues.  Yes, the code is horrid,
  and the userspace api leaves a lot to be desired, but it's not going
  to change due to legacy issues that we have no control over.  Because
  so many devices and companies rely on this, and the code is stable,
  might as well promote it out of staging.

  This was all discussed at the Linux Plumbers conference, and everyone
  participating agreed that this was the best way forward.

  There is work happening to replace the binder code with something new
  that is happening right now, but I don't expect to see the results of
  that work for another year at the earliest.  If that ever happens, and
  Android switches over to it, I'll gladly remove this version.

  As for maintainers, I'll be glad to maintain this code, I've been
  doing it for the past few years with no problems.  I'll send a
  MAINTAINERS entry for it before 3.19-final is out, still need to talk
  to the Google developers about if they are willing to help with it or
  not, last I checked they were, which was good.

  All of these patches have been in linux-next for a while with no
  reported issues"

* tag 'staging-3.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (1382 commits)
  Staging: slicoss: Fix long line issues in slicoss.c
  staging: rtl8712: remove unnecessary else after return
  staging: comedi: change some printk calls to pr_err
  staging: rtl8723au: hal: Removed the extra semicolon
  lustre: Deletion of unnecessary checks before three function calls
  staging: lustre: fix sparse warnings: static function declaration
  staging: lustre: fixed sparse warnings related to static declarations
  staging: unisys: remove duplicate header
  staging: unisys: remove unneeded structure
  staging: ft1000 : replace __attribute ((__packed__) with __packed
  drivers: staging: rtl8192e: Include "asm/unaligned.h" instead of "access_ok.h" in "rtl819x_BAProc.c"
  Drivers:staging:rtl8192e: Fixed checkpatch warning
  Drivers:staging:clocking-wizard: Added a newline
  staging: clocking-wizard: check for a valid clk_name pointer
  staging: rtl8723au: Hal_InitPGData() avoid unnecessary typecasts
  staging: rtl8723au: _DisableAnalog(): Avoid zero-init variables unnecessarily
  staging: rtl8723au: Remove unnecessary wrapper _ResetDigitalProcedure1()
  staging: rtl8723au: _ResetDigitalProcedure1_92C() reduce code obfuscation
  staging: rtl8723au: Remove unnecessary wrapper _DisableRFAFEAndResetBB()
  staging: rtl8723au: _DisableRFAFEAndResetBB8192C(): Reduce code obfuscation
  ...
2014-12-15 18:06:13 -08:00

352 lines
8.6 KiB
C

/*
* Rockchip Successive Approximation Register (SAR) A/D Converter
* Copyright (C) 2014 ROCKCHIP, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/regulator/consumer.h>
#include <linux/iio/iio.h>
#define SARADC_DATA 0x00
#define SARADC_STAS 0x04
#define SARADC_STAS_BUSY BIT(0)
#define SARADC_CTRL 0x08
#define SARADC_CTRL_IRQ_STATUS BIT(6)
#define SARADC_CTRL_IRQ_ENABLE BIT(5)
#define SARADC_CTRL_POWER_CTRL BIT(3)
#define SARADC_CTRL_CHN_MASK 0x7
#define SARADC_DLY_PU_SOC 0x0c
#define SARADC_DLY_PU_SOC_MASK 0x3f
#define SARADC_TIMEOUT msecs_to_jiffies(100)
struct rockchip_saradc_data {
int num_bits;
const struct iio_chan_spec *channels;
int num_channels;
unsigned long clk_rate;
};
struct rockchip_saradc {
void __iomem *regs;
struct clk *pclk;
struct clk *clk;
struct completion completion;
struct regulator *vref;
const struct rockchip_saradc_data *data;
u16 last_val;
};
static int rockchip_saradc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct rockchip_saradc *info = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&indio_dev->mlock);
reinit_completion(&info->completion);
/* 8 clock periods as delay between power up and start cmd */
writel_relaxed(8, info->regs + SARADC_DLY_PU_SOC);
/* Select the channel to be used and trigger conversion */
writel(SARADC_CTRL_POWER_CTRL
| (chan->channel & SARADC_CTRL_CHN_MASK)
| SARADC_CTRL_IRQ_ENABLE,
info->regs + SARADC_CTRL);
if (!wait_for_completion_timeout(&info->completion,
SARADC_TIMEOUT)) {
writel_relaxed(0, info->regs + SARADC_CTRL);
mutex_unlock(&indio_dev->mlock);
return -ETIMEDOUT;
}
*val = info->last_val;
mutex_unlock(&indio_dev->mlock);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
ret = regulator_get_voltage(info->vref);
if (ret < 0) {
dev_err(&indio_dev->dev, "failed to get voltage\n");
return ret;
}
*val = ret / 1000;
*val2 = info->data->num_bits;
return IIO_VAL_FRACTIONAL_LOG2;
default:
return -EINVAL;
}
}
static irqreturn_t rockchip_saradc_isr(int irq, void *dev_id)
{
struct rockchip_saradc *info = (struct rockchip_saradc *)dev_id;
/* Read value */
info->last_val = readl_relaxed(info->regs + SARADC_DATA);
info->last_val &= GENMASK(info->data->num_bits - 1, 0);
/* Clear irq & power down adc */
writel_relaxed(0, info->regs + SARADC_CTRL);
complete(&info->completion);
return IRQ_HANDLED;
}
static const struct iio_info rockchip_saradc_iio_info = {
.read_raw = rockchip_saradc_read_raw,
.driver_module = THIS_MODULE,
};
#define ADC_CHANNEL(_index, _id) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = _index, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.datasheet_name = _id, \
}
static const struct iio_chan_spec rockchip_saradc_iio_channels[] = {
ADC_CHANNEL(0, "adc0"),
ADC_CHANNEL(1, "adc1"),
ADC_CHANNEL(2, "adc2"),
};
static const struct rockchip_saradc_data saradc_data = {
.num_bits = 10,
.channels = rockchip_saradc_iio_channels,
.num_channels = ARRAY_SIZE(rockchip_saradc_iio_channels),
.clk_rate = 1000000,
};
static const struct iio_chan_spec rockchip_rk3066_tsadc_iio_channels[] = {
ADC_CHANNEL(0, "adc0"),
ADC_CHANNEL(1, "adc1"),
};
static const struct rockchip_saradc_data rk3066_tsadc_data = {
.num_bits = 12,
.channels = rockchip_rk3066_tsadc_iio_channels,
.num_channels = ARRAY_SIZE(rockchip_rk3066_tsadc_iio_channels),
.clk_rate = 50000,
};
static const struct of_device_id rockchip_saradc_match[] = {
{
.compatible = "rockchip,saradc",
.data = &saradc_data,
}, {
.compatible = "rockchip,rk3066-tsadc",
.data = &rk3066_tsadc_data,
},
{},
};
MODULE_DEVICE_TABLE(of, rockchip_saradc_match);
static int rockchip_saradc_probe(struct platform_device *pdev)
{
struct rockchip_saradc *info = NULL;
struct device_node *np = pdev->dev.of_node;
struct iio_dev *indio_dev = NULL;
struct resource *mem;
const struct of_device_id *match;
int ret;
int irq;
if (!np)
return -ENODEV;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
if (!indio_dev) {
dev_err(&pdev->dev, "failed allocating iio device\n");
return -ENOMEM;
}
info = iio_priv(indio_dev);
match = of_match_device(rockchip_saradc_match, &pdev->dev);
info->data = match->data;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
info->regs = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(info->regs))
return PTR_ERR(info->regs);
init_completion(&info->completion);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no irq resource?\n");
return irq;
}
ret = devm_request_irq(&pdev->dev, irq, rockchip_saradc_isr,
0, dev_name(&pdev->dev), info);
if (ret < 0) {
dev_err(&pdev->dev, "failed requesting irq %d\n", irq);
return ret;
}
info->pclk = devm_clk_get(&pdev->dev, "apb_pclk");
if (IS_ERR(info->pclk)) {
dev_err(&pdev->dev, "failed to get pclk\n");
return PTR_ERR(info->pclk);
}
info->clk = devm_clk_get(&pdev->dev, "saradc");
if (IS_ERR(info->clk)) {
dev_err(&pdev->dev, "failed to get adc clock\n");
return PTR_ERR(info->clk);
}
info->vref = devm_regulator_get(&pdev->dev, "vref");
if (IS_ERR(info->vref)) {
dev_err(&pdev->dev, "failed to get regulator, %ld\n",
PTR_ERR(info->vref));
return PTR_ERR(info->vref);
}
/*
* Use a default value for the converter clock.
* This may become user-configurable in the future.
*/
ret = clk_set_rate(info->clk, info->data->clk_rate);
if (ret < 0) {
dev_err(&pdev->dev, "failed to set adc clk rate, %d\n", ret);
return ret;
}
ret = regulator_enable(info->vref);
if (ret < 0) {
dev_err(&pdev->dev, "failed to enable vref regulator\n");
return ret;
}
ret = clk_prepare_enable(info->pclk);
if (ret < 0) {
dev_err(&pdev->dev, "failed to enable pclk\n");
goto err_reg_voltage;
}
ret = clk_prepare_enable(info->clk);
if (ret < 0) {
dev_err(&pdev->dev, "failed to enable converter clock\n");
goto err_pclk;
}
platform_set_drvdata(pdev, indio_dev);
indio_dev->name = dev_name(&pdev->dev);
indio_dev->dev.parent = &pdev->dev;
indio_dev->dev.of_node = pdev->dev.of_node;
indio_dev->info = &rockchip_saradc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = info->data->channels;
indio_dev->num_channels = info->data->num_channels;
ret = iio_device_register(indio_dev);
if (ret)
goto err_clk;
return 0;
err_clk:
clk_disable_unprepare(info->clk);
err_pclk:
clk_disable_unprepare(info->pclk);
err_reg_voltage:
regulator_disable(info->vref);
return ret;
}
static int rockchip_saradc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct rockchip_saradc *info = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
clk_disable_unprepare(info->clk);
clk_disable_unprepare(info->pclk);
regulator_disable(info->vref);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int rockchip_saradc_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct rockchip_saradc *info = iio_priv(indio_dev);
clk_disable_unprepare(info->clk);
clk_disable_unprepare(info->pclk);
regulator_disable(info->vref);
return 0;
}
static int rockchip_saradc_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct rockchip_saradc *info = iio_priv(indio_dev);
int ret;
ret = regulator_enable(info->vref);
if (ret)
return ret;
ret = clk_prepare_enable(info->pclk);
if (ret)
return ret;
ret = clk_prepare_enable(info->clk);
if (ret)
return ret;
return ret;
}
#endif
static SIMPLE_DEV_PM_OPS(rockchip_saradc_pm_ops,
rockchip_saradc_suspend, rockchip_saradc_resume);
static struct platform_driver rockchip_saradc_driver = {
.probe = rockchip_saradc_probe,
.remove = rockchip_saradc_remove,
.driver = {
.name = "rockchip-saradc",
.of_match_table = rockchip_saradc_match,
.pm = &rockchip_saradc_pm_ops,
},
};
module_platform_driver(rockchip_saradc_driver);