Skip to content

Commit

Permalink
drivers: k230: Add adc driver
Browse files Browse the repository at this point in the history
Signed-off-by: wycwyhwyq <[email protected]>
  • Loading branch information
wycwyhwyq committed Nov 19, 2024
1 parent cb33eeb commit 2daeb2d
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 1 deletion.
6 changes: 6 additions & 0 deletions arch/riscv/boot/dts/canaan/k230.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,12 @@
clocks = <&wdt1>;
};

adc: adc@9140d000 {
compatible = "canaan,k230-adc";
reg = <0x0 0x9140d000 0x0 0x1000>;
clocks = <&adc_clk>;
};

vpu: vpu@0x90400000 {
status = "okay";
compatible = "canaan,vpu";
Expand Down
2 changes: 1 addition & 1 deletion arch/riscv/configs/k230_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ CONFIG_RPMSG_CHAR=y
CONFIG_RPMSG_CTRL=y
CONFIG_RPMSG_VIRTIO=y
CONFIG_IIO=y
CONFIG_XUANTIE_TH1520_ADC=m
CONFIG_K230_ADC=y
CONFIG_PWM=y
CONFIG_PWM_XUANTIE=m
CONFIG_PHY_SUN4I_USB=m
Expand Down
9 changes: 9 additions & 0 deletions drivers/iio/adc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1454,4 +1454,13 @@ config XUANTIE_TH1520_ADC
This driver can also be built as a module. If so, the module will be
called xuantie-th1520-adc.

config K230_ADC

Check failure on line 1457 in drivers/iio/adc/Kconfig

View workflow job for this annotation

GitHub Actions / checkpatch

WARNING: please write a help paragraph that fully describes the config symbol
tristate "Canaan K230 ADC driver"
default ARCH_CANAAN
depends on RISCV && OF && ARCH_CANAAN
help
Say yes here to support for Canaan K230 analog-to-digital converter.
This driver can also be built as a module. If so, the module will be
called k230-adc.

endmenu
1 change: 1 addition & 0 deletions drivers/iio/adc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,4 @@ obj-$(CONFIG_XILINX_XADC) += xilinx-xadc.o
obj-$(CONFIG_XILINX_AMS) += xilinx-ams.o
obj-$(CONFIG_SD_ADC_MODULATOR) += sd_adc_modulator.o
obj-$(CONFIG_XUANTIE_TH1520_ADC) += th1520-adc.o
obj-$(CONFIG_K230_ADC) += k230-adc.o
203 changes: 203 additions & 0 deletions drivers/iio/adc/k230-adc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2023, Canaan Bright Sight Co., Ltd
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/iio/iio.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>

#define ADC_MAX_CHANNEL 6
#define ADC_MAX_DMA_CHN 3

struct k230_adc_regs {
uint32_t trim_reg;
uint32_t cfg_reg;
uint32_t mode_reg;
uint32_t thsd_reg;
uint32_t dma_intr_reg;
uint32_t data_reg[ADC_MAX_CHANNEL];
uint32_t data_dma[ADC_MAX_DMA_CHN];
};

struct k230_adc_priv {
void __iomem *base;
struct clk *clk;
struct mutex lock;
};

#define K230_ADC_CHANNEL(n) \
{ \
.channel = n, \
.datasheet_name = "channel"#n, \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
}

static const struct iio_chan_spec k230_adc_channels[] = {
K230_ADC_CHANNEL(0),
K230_ADC_CHANNEL(1),
K230_ADC_CHANNEL(2),
K230_ADC_CHANNEL(3),
K230_ADC_CHANNEL(4),
K230_ADC_CHANNEL(5),
};

static int k230_adc_init(struct k230_adc_priv *dev)
{
struct k230_adc_regs *regs = dev->base;
uint32_t data;

data = readl(&regs->trim_reg);
data &= ~(0x1);
writel(data, &regs->trim_reg);
data |= (0x1) | (0x1 << 20);
writel(data, &regs->trim_reg);
fsleep(1000);
data &= ~(0x1 << 20);
writel(data, &regs->trim_reg);

writel(0x0, &regs->mode_reg);
mutex_init(&dev->lock);

return 0;
}

static int k230_adc_read(struct k230_adc_priv *dev, int channel)
{
struct k230_adc_regs *regs = dev->base;
int ret = -ETIMEDOUT, timeout;

mutex_lock(&dev->lock);

writel(channel | 0x10, &regs->cfg_reg);
for (timeout = 0; timeout < 1000; timeout++) {
if ((readl(&regs->cfg_reg) & 0x10100) == 0x10000) {
ret = readl(&regs->data_reg[channel]);
break;
}
fsleep(1);
}

mutex_unlock(&dev->lock);

return ret;
}

static int k230_adc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct k230_adc_priv *priv = iio_priv(indio_dev);

switch (mask) {
case IIO_CHAN_INFO_RAW:
if (chan->type != IIO_VOLTAGE)
return -EINVAL;

*val = k230_adc_read(priv, chan->channel);
if (*val < 0)
return *val;

return IIO_VAL_INT;
default:
break;
}

return -EINVAL;
}

static const struct iio_info k230_adc_info = {
.read_raw = k230_adc_read_raw,
};

static int k230_adc_probe(struct platform_device *pdev)
{
struct iio_dev *indio_dev;
struct k230_adc_priv *priv;
struct device *dev = &pdev->dev;
int ret;

indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
if (!indio_dev) {
dev_err(dev, "Failed allocating iio device\n");
return -ENOMEM;
}

priv = iio_priv(indio_dev);
priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);

priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk)) {
dev_err(dev, "Failed getting clock\n");
return ret;
}

ret = clk_prepare_enable(priv->clk);
if (ret) {
dev_err(dev, "Could not prepare or enable clock.\n");
return ret;
}

k230_adc_init(priv);

indio_dev->name = dev_name(dev);
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &k230_adc_info;
indio_dev->channels = k230_adc_channels;
indio_dev->num_channels = ARRAY_SIZE(k230_adc_channels);

ret = devm_iio_device_register(dev, indio_dev);
if (ret)
dev_err(dev, "could not register k230-adc");

return ret;
}

static const struct of_device_id k230_adc_match[] = {
{ .compatible = "canaan,k230-adc", },
{ },
};
MODULE_DEVICE_TABLE(of, k230_adc_match);

static struct platform_driver k230_adc_driver = {
.driver = {
.name = "k230-adc",
.of_match_table = k230_adc_match,
},
.probe = k230_adc_probe,
};
module_platform_driver(k230_adc_driver);

MODULE_AUTHOR("Canaan SDK Team");
MODULE_DESCRIPTION("Canaan Kendyte K230 chip ADC Driver");
MODULE_LICENSE("GPL");

0 comments on commit 2daeb2d

Please sign in to comment.