blob: 4ad32fffe138c6028ae7046045841cc9ad51dad6 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Synaptics AS370 ADC driver
*
* Copyright (C) 2018 Synaptics Incorporated
*
* Jisheng Zhang <jszhang@kernel.org>
*/
#include <linux/delay.h>
#include <linux/iio/iio.h>
#include <linux/iio/driver.h>
#include <linux/iio/machine.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/wait.h>
#define CTRL 0x0
#define CTRL_SEL_CHA_SHIFT 0
#define CTRL_SEL_CHA_MASK (7 << 0)
#define CTRL_PD_ADC BIT(3)
#define CTRL_MODE_SHIFT 4
#define CTRL_MODE_MASK (0x3 << 4)
#define CTRL_SOC BIT(6)
#define CTRL_EN_VCM BIT(7)
#define CTRL_SEL_CMP BIT(8)
#define CTRL_INTR_MASK BIT(12)
#define CTRL_INTR_CLR BIT(13)
#define STS 0x4
#define STS_DATA_MASK 0xFFF
#define STS_EOC BIT(13)
struct as370_adc_priv {
void __iomem *base;
u32 vref_mv;
struct mutex lock;
wait_queue_head_t wq;
int data;
bool data_available;
};
#define ADC_CHANNEL(n, t) \
{ \
.channel = n, \
.datasheet_name = "channel"#n, \
.type = t, \
.indexed = 1, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
}
static const struct iio_chan_spec as370_adc_channels[] = {
ADC_CHANNEL(0, IIO_VOLTAGE),
ADC_CHANNEL(1, IIO_VOLTAGE),
ADC_CHANNEL(2, IIO_VOLTAGE),
ADC_CHANNEL(3, IIO_VOLTAGE),
ADC_CHANNEL(4, IIO_VOLTAGE),
ADC_CHANNEL(5, IIO_VOLTAGE),
ADC_CHANNEL(7, IIO_VOLTAGE),
IIO_CHAN_SOFT_TIMESTAMP(8),
};
static inline u32 rdl(struct as370_adc_priv *priv, u32 offset)
{
return readl_relaxed(priv->base + offset);
}
static inline void wrl(struct as370_adc_priv *priv, u32 offset, u32 data)
{
writel_relaxed(data, priv->base + offset);
}
static int as370_adc_read(struct iio_dev *indio_dev, int channel)
{
u32 val;
int data, ret;
struct as370_adc_priv *priv = iio_priv(indio_dev);
mutex_lock(&priv->lock);
val = 0;
val &= ~CTRL_SEL_CHA_MASK;
val |= (channel << CTRL_SEL_CHA_SHIFT);
val |= CTRL_SEL_CMP;
val |= CTRL_EN_VCM;
val &= ~CTRL_INTR_MASK;
wrl(priv, CTRL, val);
local_irq_disable();
val |= CTRL_SOC;
wrl(priv, CTRL, val);
udelay(1);
val &= ~CTRL_SOC;
wrl(priv, CTRL, val);
local_irq_enable();
ret = wait_event_interruptible_timeout(priv->wq, priv->data_available,
msecs_to_jiffies(1000));
val |= CTRL_INTR_MASK;
wrl(priv, CTRL, val);
if (ret == 0)
ret = -ETIMEDOUT;
if (ret < 0) {
mutex_unlock(&priv->lock);
return ret;
}
data = priv->data;
priv->data_available = false;
mutex_unlock(&priv->lock);
return data;
}
static int as370_adc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
int tmp;
struct as370_adc_priv *priv = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (chan->type != IIO_VOLTAGE)
return -EINVAL;
tmp = as370_adc_read(indio_dev, chan->channel);
if (tmp < 0)
return tmp;
*val = tmp;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
/*
* vref_mv / 4095 * raw
* The max value of vref_mv is 1800
*/
*val = 0;
*val2 = priv->vref_mv * 244200;
return IIO_VAL_INT_PLUS_NANO;
default:
break;
}
return -EINVAL;
}
static irqreturn_t as370_adc_irq(int irq, void *private)
{
u32 val;
struct as370_adc_priv *priv = iio_priv(private);
val = rdl(priv, CTRL);
val |= CTRL_INTR_MASK;
val |= CTRL_INTR_CLR;
wrl(priv, CTRL, val);
val = rdl(priv, STS);
if (val & STS_EOC) {
priv->data = val & STS_DATA_MASK;
priv->data_available = true;
wake_up_interruptible(&priv->wq);
}
return IRQ_HANDLED;
}
static const struct iio_info as370_adc_info = {
.driver_module = THIS_MODULE,
.read_raw = as370_adc_read_raw,
};
static int as370_adc_probe(struct platform_device *pdev)
{
u32 val;
int irq, ret;
struct as370_adc_priv *priv;
struct iio_dev *indio_dev;
struct resource *res;
struct device *dev = &pdev->dev;
struct device_node *np = pdev->dev.of_node;
indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
if (!indio_dev)
return -ENOMEM;
priv = iio_priv(indio_dev);
platform_set_drvdata(pdev, indio_dev);
if (of_property_read_u32(np, "syna,adc-vref", &val)) {
dev_err(dev, "Missing adc-vref property in the DT.\n");
return -EINVAL;
}
priv->vref_mv = val;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->base = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
val = rdl(priv, CTRL);
val |= CTRL_PD_ADC;
wrl(priv, CTRL, val);
val &= ~CTRL_PD_ADC;
val |= CTRL_INTR_MASK;
val |= CTRL_INTR_CLR;
wrl(priv, CTRL, val);
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
ret = devm_request_irq(dev, irq, as370_adc_irq, 0,
dev->driver->name, indio_dev);
if (ret)
return ret;
init_waitqueue_head(&priv->wq);
mutex_init(&priv->lock);
indio_dev->dev.parent = dev;
indio_dev->name = dev_name(dev);
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &as370_adc_info;
indio_dev->channels = as370_adc_channels;
indio_dev->num_channels = ARRAY_SIZE(as370_adc_channels);
return devm_iio_device_register(dev, indio_dev);
}
static int as370_adc_remove(struct platform_device *pdev)
{
u32 val;
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct as370_adc_priv *priv = iio_priv(indio_dev);
/* Power down ADC */
val = rdl(priv, CTRL);
val |= CTRL_PD_ADC;
wrl(priv, CTRL, val);
return 0;
}
static const struct of_device_id as370_adc_match[] = {
{ .compatible = "syna,as370-adc", },
{ },
};
MODULE_DEVICE_TABLE(of, as370_adc_match);
static struct platform_driver as370_adc_driver = {
.driver = {
.name = "as370-adc",
.of_match_table = as370_adc_match,
},
.probe = as370_adc_probe,
.remove = as370_adc_remove,
};
module_platform_driver(as370_adc_driver);
MODULE_AUTHOR("Jisheng Zhang <jszhang@kernel.org>");
MODULE_DESCRIPTION("Synaptics AS370 ADC driver");
MODULE_LICENSE("GPL v2");