linuxOS_D21X/source/linux-5.10/drivers/iio/adc/artinchip_psadc.c
2024-11-29 16:23:11 +08:00

528 lines
12 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* PSADC driver of Artinchip SoC
*
* Copyright (C) 2020-2023 Artinchip Technology Co., Ltd.
* Authors: Siyao.Li <siyao.li@artinchip.com>
*/
#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/delay.h>
#include <linux/reset.h>
#include <linux/iio/iio.h>
#include <linux/iio/events.h>
#define AIC_PSADC_NAME "aic-psadc"
#define AIC_PSADC_MAX_CH 12
#define AIC_PSADC_FIFO1_NUM_BITS 20
#define AIC_PSADC_FIFO2_NUM_BITS 12
#define AIC_PSADC_TIMEOUT msecs_to_jiffies(1000)
enum aic_psadc_mode {
AIC_PSADC_MODE_SINGLE = 0
};
/* Register definition of PSADC Controller */
#define PSADC_MCR 0x000
#define PSADC_TCR 0x004
#define PSADC_NODE1 0x008
#define PSADC_NODE2 0x00C
#define PSADC_MSR 0x010
#define PSADC_CALCSR 0x014
#define PSADC_FILTER 0x01C
#define PSADC_Q1FCR 0x020
#define PSADC_Q2FCR 0x024
#define PSADC_Q1FDR 0x040
#define PSADC_Q2FDR 0x080
#define PSADC_VERSION 0xFFC
#define PSADC_MCR_Q1_TRIGS BIT(22)
#define PSADC_MCR_Q1_INTE BIT(18)
#define PSADC_MCR_QUE_COMB BIT(1)
#define PSADC_MCR_EN BIT(0)
#define PSADC_MSR_Q1_FERR BIT(2)
#define PSADC_MSR_Q1_INT BIT(0)
#define PSADC_Q1FCR_FIFO_DRTH_SHIFT 11
#define PSADC_Q1FCR_UF_STS BIT(17)
#define PSADC_Q1FCR_OF_STS BIT(16)
#define PSADC_Q1FCR_FIFO_ERRIE BIT(3)
#define PSADC_Q1FCR_FIFO_FLUSH BIT(0)
#define PSADC_Q1FDR_CHNUM_SHIFT 12
#define PSADC_Q1FDR_DATA_MASK GENMASK(11, 0)
#define PSADC_Q1FDR_DATA BIT(0)
#define PSADC_INVALID_DATA 0xFFF
#define PSADC_ADC_DATA_MIN 0x0
#define PSADC_ADC_DATA_STEP 0x1
struct aic_psadc_ch {
u32 id;
bool available;
enum aic_psadc_mode mode;
u16 latest_data;
struct completion complete;
};
struct aic_psadc_data {
int num_bits;
const struct iio_chan_spec *channels;
int num_channels;
u32 fifo_depth[AIC_PSADC_MAX_CH];
};
struct aic_psadc_dev {
struct platform_device *pdev;
void __iomem *regs;
struct clk *clk;
struct reset_control *rst;
u32 irq;
u32 pclk_rate;
struct aic_psadc_ch chan[AIC_PSADC_MAX_CH];
const struct aic_psadc_data *data;
};
static DEFINE_SPINLOCK(user_lock);
static const int aic_psadc_adc_raw_available[] = {
PSADC_ADC_DATA_MIN, PSADC_ADC_DATA_STEP, PSADC_INVALID_DATA,
};
// TODO: Add the transform algorithm, offered by SD later
static s32 psadc_data2vol(u16 data)
{
return data;
}
static void psadc_reg_enable(void __iomem *base, int offset, int bit, int enable)
{
int tmp = readl(base + offset);
if (enable)
tmp |= bit;
else
tmp &= ~bit;
writel(tmp, base + offset);
}
static void psadc_enable(void __iomem *regs, int enable)
{
spin_lock(&user_lock);
psadc_reg_enable(regs, PSADC_MCR, PSADC_MCR_EN, enable);
spin_unlock(&user_lock);
}
static void psadc_single_queue_mode(void __iomem *regs, int enable)
{
psadc_reg_enable(regs, PSADC_MCR, PSADC_MCR_QUE_COMB, enable);
}
static void psadc_qc_irq_enable(void __iomem *regs, int enable)
{
psadc_reg_enable(regs, PSADC_MCR, PSADC_MCR_Q1_INTE, enable);
}
static void psadc_fifo_init(struct aic_psadc_dev *psadc)
{
u32 val = 0;
val = 1 << PSADC_Q1FCR_FIFO_DRTH_SHIFT;
writel(val, psadc->regs + PSADC_Q1FCR);
writel(val, psadc->regs + PSADC_Q2FCR);
}
static void psadc_fifo_flush(struct aic_psadc_dev *psadc, u32 ch)
{
struct device *dev = &psadc->pdev->dev;
void __iomem *regs = psadc->regs;
u32 val = readl(regs + PSADC_Q1FCR);
if (val & PSADC_Q1FCR_UF_STS)
dev_err(dev, "ch%d FIFO is Underflow!%#x\n", ch, val);
if (val & PSADC_Q1FCR_OF_STS)
dev_err(dev, "ch%d FIFO is Overflow!%#x\n", ch, val);
writel(val | PSADC_Q1FCR_FIFO_FLUSH, regs + PSADC_Q1FCR);
}
static int psadc_ch_init(struct aic_psadc_dev *psadc, u32 ch)
{
void __iomem *regs = psadc->regs;
struct aic_psadc_ch *chan = &psadc->chan[ch];
init_completion(&chan->complete);
psadc_fifo_init(psadc);
writel(ch, regs + PSADC_NODE1);
psadc_reg_enable(regs, PSADC_MCR, PSADC_MCR_Q1_TRIGS, 1);
psadc_reg_enable(regs, PSADC_MCR, PSADC_MCR_Q1_INTE, 1);
/* For single mode, should init the channel in .read_raw() */
return 0;
}
static int aic_psadc_read_dat(struct aic_psadc_dev *psadc, u32 ch)
{
void __iomem *regs = psadc->regs;
struct aic_psadc_ch *chan = &psadc->chan[ch];
chan->latest_data = readl(regs + PSADC_Q1FDR) & PSADC_Q1FDR_DATA_MASK;
return 0;
}
static int aic_psadc_read_raw(struct iio_dev *iodev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct aic_psadc_dev *psadc = iio_priv(iodev);
struct device *dev = &psadc->pdev->dev;
struct aic_psadc_ch *psadc_ch = NULL;
void __iomem *regs = psadc->regs;
u32 ch = chan->channel;
if (unlikely(chan->channel < 0 || chan->channel >= AIC_PSADC_MAX_CH)) {
dev_err(dev, "Invalid channel No.%d", chan->channel);
return -ENODEV;
}
psadc_ch = &psadc->chan[ch];
if (!psadc_ch->available) {
dev_warn(dev, "Channel %d is unavailable", ch);
return -ENODEV;
}
dev_dbg(&psadc->pdev->dev, "ch %d, mask %#lx\n", ch, mask);
switch (mask) {
case IIO_CHAN_INFO_RAW:
psadc_ch_init(psadc, ch);
spin_lock(&user_lock);
reinit_completion(&psadc_ch->complete);
spin_unlock(&user_lock);
if (!wait_for_completion_timeout(&psadc_ch->complete,
AIC_PSADC_TIMEOUT)) {
dev_err(dev, "Ch%d read timeout!\n", ch);
psadc_qc_irq_enable(regs, 0);
return -ETIMEDOUT;
}
psadc_qc_irq_enable(regs, 0);
*val = psadc_data2vol(psadc_ch->latest_data);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
default:
return -EINVAL;
}
}
static const struct iio_event_spec aic_psadc_event[] = {
{ .type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_RISING,
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_ENABLE),
},
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_ENABLE),
}
};
static irqreturn_t aic_psadc_isr(int irq, void *dev_id)
{
unsigned long flags;
u32 q_flag = 0, chan_flag = 0;
struct aic_psadc_ch *chan = NULL;
struct aic_psadc_dev *psadc = dev_id;
void __iomem *regs = psadc->regs;
spin_lock_irqsave(&user_lock, flags);
chan_flag = readl(regs + PSADC_NODE1);
q_flag = readl(regs + PSADC_MSR);
writel(q_flag, regs + PSADC_MSR);
chan = &psadc->chan[chan_flag];
if (q_flag | PSADC_MSR_Q1_INT) {
aic_psadc_read_dat(psadc, chan_flag);
complete(&chan->complete);
}
if (q_flag | PSADC_MSR_Q1_FERR)
psadc_fifo_flush(psadc, chan_flag);
spin_unlock_irqrestore(&user_lock, flags);
return IRQ_HANDLED;
}
static int aic_psadc_read_avail(struct iio_dev *iodev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
switch (mask) {
case IIO_CHAN_INFO_RAW:
*vals = aic_psadc_adc_raw_available;
*type = IIO_VAL_INT;
return IIO_AVAIL_RANGE;
default:
return -EINVAL;
}
}
static const struct iio_info aic_psadc_iio_info = {
.read_raw = aic_psadc_read_raw,
.read_avail = aic_psadc_read_avail,
};
#define PSADC_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), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), \
.datasheet_name = _id, \
}
static const struct iio_chan_spec aic_psadc_v1_iio_channels[] = {
PSADC_CHANNEL(0, "adc0"),
PSADC_CHANNEL(1, "adc1"),
PSADC_CHANNEL(2, "adc2"),
PSADC_CHANNEL(3, "adc3"),
PSADC_CHANNEL(4, "adc4"),
PSADC_CHANNEL(5, "adc5"),
PSADC_CHANNEL(6, "adc6"),
PSADC_CHANNEL(7, "adc7"),
PSADC_CHANNEL(8, "adc8"),
PSADC_CHANNEL(9, "adc9"),
PSADC_CHANNEL(10, "adc10"),
PSADC_CHANNEL(11, "adc11"),
{
.event_spec = aic_psadc_event,
.num_event_specs = ARRAY_SIZE(aic_psadc_event),
}
};
static const struct aic_psadc_data aic_psadc_v1_data = {
.num_bits = 12,
.channels = aic_psadc_v1_iio_channels,
.num_channels = ARRAY_SIZE(aic_psadc_v1_iio_channels),
.fifo_depth = {12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12},
};
static const struct of_device_id aic_psadc_match[] = {
{
.compatible = "artinchip,aic-psadc-v1.0",
.data = &aic_psadc_v1_data,
},
{},
};
MODULE_DEVICE_TABLE(of, aic_psadc_match);
#ifdef CONFIG_PM_SLEEP
static int aic_psadc_suspend(struct device *dev)
{
struct iio_dev *iodev = dev_get_drvdata(dev);
struct aic_psadc_dev *psadc = iio_priv(iodev);
psadc_enable(psadc->regs, 0);
reset_control_assert(psadc->rst);
clk_disable_unprepare(psadc->clk);
return 0;
}
static int aic_psadc_resume(struct device *dev)
{
struct iio_dev *iodev = dev_get_drvdata(dev);
struct aic_psadc_dev *psadc = iio_priv(iodev);
int ret;
ret = clk_prepare_enable(psadc->clk);
if (ret)
return ret;
ret = reset_control_deassert(psadc->rst);
if (ret)
return ret;
psadc_enable(psadc->regs, 1);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(aic_psadc_pm_ops, aic_psadc_suspend,
aic_psadc_resume);
static int psadc_parse_dt(struct aic_psadc_dev *psadc)
{
struct device *dev = &psadc->pdev->dev;
struct device_node *child, *np = dev->of_node;
u32 i = 0;
for_each_child_of_node(np, child) {
struct aic_psadc_ch *chan = &psadc->chan[i];
chan->id = i;
chan->available = of_device_is_available(child);
if (!chan->available) {
dev_dbg(dev, "ch%d is unavailable.\n", i);
i++;
continue;
}
dev_dbg(dev, "ch%d is available\n", i);
chan->mode = AIC_PSADC_MODE_SINGLE;
i++;
}
return 0;
}
static int aic_psadc_probe(struct platform_device *pdev)
{
struct aic_psadc_dev *psadc = NULL;
struct iio_dev *iodev = NULL;
const struct of_device_id *match;
struct clk *clk;
int ret, irq, i;
if (!pdev->dev.of_node)
return -ENODEV;
iodev = devm_iio_device_alloc(&pdev->dev, sizeof(*psadc));
if (!iodev)
return -ENOMEM;
psadc = iio_priv(iodev);
match = of_match_device(aic_psadc_match, &pdev->dev);
if (!match) {
dev_err(&pdev->dev, "failed to match device\n");
return -ENODEV;
}
psadc->data = match->data;
psadc->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(psadc->regs))
return PTR_ERR(psadc->regs);
clk = devm_clk_get(&pdev->dev, "pclk");
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "Failed to get pclk\n");
return PTR_ERR(clk);
}
psadc->pclk_rate = clk_get_rate(clk);
dev_dbg(&pdev->dev, "PCLK rate %d\n", psadc->pclk_rate);
psadc->clk = devm_clk_get(&pdev->dev, "psadc");
if (IS_ERR(psadc->clk)) {
dev_err(&pdev->dev, "no clock defined\n");
return PTR_ERR(psadc->clk);
}
ret = clk_prepare_enable(psadc->clk);
if (ret) {
dev_err(&pdev->dev, "Failed to enable clk, return %d\n", ret);
return ret;
}
psadc->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL);
if (IS_ERR(psadc->rst)) {
ret = PTR_ERR(psadc->rst);
goto disable_clk;
}
reset_control_deassert(psadc->rst);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "Failed to get irq\n");
goto disable_rst;
}
ret = devm_request_irq(&pdev->dev, irq, aic_psadc_isr,
0, AIC_PSADC_NAME, psadc);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to request IRQ %d\n", irq);
goto disable_rst;
}
psadc->irq = irq;
psadc->pdev = pdev;
psadc_single_queue_mode(psadc->regs, 1);
psadc_enable(psadc->regs, 1);
platform_set_drvdata(pdev, iodev);
psadc_parse_dt(psadc);
iodev->name = AIC_PSADC_NAME;
iodev->dev.parent = &pdev->dev;
iodev->dev.of_node = pdev->dev.of_node;
iodev->info = &aic_psadc_iio_info;
iodev->modes = INDIO_DIRECT_MODE;
iodev->channels = psadc->data->channels;
iodev->num_channels = psadc->data->num_channels;
ret = iio_device_register(iodev);
if (ret)
goto free_irq;
for (i = 0; i < AIC_PSADC_MAX_CH; i++) {
if (psadc->chan[i].available)
psadc_ch_init(psadc, i);
}
dev_info(&pdev->dev, "Artinchip PSADC Loaded.\n");
return 0;
free_irq:
free_irq(psadc->irq, psadc);
disable_rst:
reset_control_assert(psadc->rst);
disable_clk:
clk_disable_unprepare(psadc->clk);
return ret;
}
static int aic_psadc_remove(struct platform_device *pdev)
{
struct iio_dev *iodev = platform_get_drvdata(pdev);
struct aic_psadc_dev *psadc = iio_priv(iodev);
iio_device_unregister(iodev);
psadc_enable(psadc->regs, 0);
free_irq(psadc->irq, psadc);
reset_control_assert(psadc->rst);
clk_disable_unprepare(psadc->clk);
return 0;
}
static struct platform_driver aic_psadc_driver = {
.probe = aic_psadc_probe,
.remove = aic_psadc_remove,
.driver = {
.name = AIC_PSADC_NAME,
.of_match_table = aic_psadc_match,
.pm = &aic_psadc_pm_ops,
},
};
module_platform_driver(aic_psadc_driver);
MODULE_AUTHOR("Siyao.Li <siyao.li@artinchip.com>");
MODULE_DESCRIPTION("PSADC driver of Artinchip SoC");
MODULE_LICENSE("GPL");