summaryrefslogtreecommitdiffstats
path: root/drivers/aiodev
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/aiodev')
-rw-r--r--drivers/aiodev/Kconfig24
-rw-r--r--drivers/aiodev/Makefile4
-rw-r--r--drivers/aiodev/am335x_adc.c9
-rw-r--r--drivers/aiodev/core.c24
-rw-r--r--drivers/aiodev/imx7d_adc.c432
-rw-r--r--drivers/aiodev/imx_thermal.c44
-rw-r--r--drivers/aiodev/lm75.c6
-rw-r--r--drivers/aiodev/mc13xxx_adc.c12
-rw-r--r--drivers/aiodev/qoriq_thermal.c13
-rw-r--r--drivers/aiodev/rockchip_saradc.c7
-rw-r--r--drivers/aiodev/st_gyro.c123
-rw-r--r--drivers/aiodev/stm32-adc-core.c11
-rw-r--r--drivers/aiodev/stm32-adc-core.h2
-rw-r--r--drivers/aiodev/stm32-adc.c39
-rw-r--r--drivers/aiodev/vf610_adc.c620
15 files changed, 1276 insertions, 94 deletions
diff --git a/drivers/aiodev/Kconfig b/drivers/aiodev/Kconfig
index 03c688ae61..e1edc25320 100644
--- a/drivers/aiodev/Kconfig
+++ b/drivers/aiodev/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Misc strange devices
#
@@ -29,6 +30,12 @@ config LM75
help
Support for LM75 and similar devices
+config ST_GYRO
+ tristate "ST L3GD20 SPI gyro driver"
+ depends on SPI
+ help
+ Support for L3GD20 three-axis angular rate sensor.
+
config MC13XXX_ADC
tristate "MC13XXX ADC driver"
depends on MFD_MC13XXX
@@ -45,7 +52,7 @@ config AM335X_ADC
config STM32_ADC
tristate "STM32 ADC driver"
- depends on ARCH_STM32MP || COMPILE_TEST
+ depends on ARCH_STM32 || COMPILE_TEST
help
Support for ADC on STM32. Supports simple one-shot readings
rather than continuous sampling with DMA, etc. ADC channels should be
@@ -58,4 +65,19 @@ config ROCKCHIP_SARADC
help
Support for Successive Approximation Register (SAR) ADC in Rockchip
SoCs.
+
+config IMX7D_ADC
+ tristate "Freescale IMX7D ADC driver"
+ depends on ARCH_IMX7 || COMPILE_TEST
+ depends on OFDEVICE
+ help
+ Say yes here to build support for IMX7D ADC.
+
+config VF610_ADC
+ tristate "Freescale vf610 ADC driver"
+ depends on ARCH_IMX6 || COMPILE_TEST
+ help
+ Say yes here to support for Vybrid board analog-to-digital converter.
+ Since the IP is used for i.MX6SLX, the driver also support i.MX6SLX.
+
endif
diff --git a/drivers/aiodev/Makefile b/drivers/aiodev/Makefile
index 1b480f6fa3..ce95d5be2f 100644
--- a/drivers/aiodev/Makefile
+++ b/drivers/aiodev/Makefile
@@ -1,9 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_AIODEV) += core.o
obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
obj-$(CONFIG_LM75) += lm75.o
+obj-$(CONFIG_ST_GYRO) += st_gyro.o
obj-$(CONFIG_MC13XXX_ADC) += mc13xxx_adc.o
obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
obj-$(CONFIG_AM335X_ADC) += am335x_adc.o
obj-$(CONFIG_STM32_ADC) += stm32-adc.o stm32-adc-core.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
+obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
+obj-$(CONFIG_VF610_ADC) += vf610_adc.o
diff --git a/drivers/aiodev/am335x_adc.c b/drivers/aiodev/am335x_adc.c
index 0d6cc426eb..512f0a86fa 100644
--- a/drivers/aiodev/am335x_adc.c
+++ b/drivers/aiodev/am335x_adc.c
@@ -19,7 +19,7 @@
#include <io.h>
#include <linux/log2.h>
#include <aiodev.h>
-#include <mach/am33xx-clock.h>
+#include <mach/omap/am33xx-clock.h>
#include "ti_am335x_tscadc.h"
struct am335x_adc_data {
@@ -72,7 +72,7 @@ static int am335x_adc_read(struct aiochannel *chan, int *val)
return 0;
}
-static int am335x_adc_probe(struct device_d *dev)
+static int am335x_adc_probe(struct device *dev)
{
struct device_node *node;
struct am335x_adc_data *data;
@@ -87,7 +87,7 @@ static int am335x_adc_probe(struct device_d *dev)
goto fail_data;
}
- node = of_find_compatible_node(dev->device_node, NULL, "ti,am3359-adc");
+ node = of_find_compatible_node(dev->of_node, NULL, "ti,am3359-adc");
if (!node) {
ret = -EINVAL;
goto fail_data;
@@ -174,8 +174,9 @@ static const struct of_device_id of_am335x_adc_match[] = {
{ .compatible = "ti,am3359-tscadc", },
{ /* end */ }
};
+MODULE_DEVICE_TABLE(of, of_am335x_adc_match);
-static struct driver_d am335x_adc_driver = {
+static struct driver am335x_adc_driver = {
.name = "am335x_adc",
.probe = am335x_adc_probe,
.of_compatible = DRV_OF_COMPAT(of_am335x_adc_match),
diff --git a/drivers/aiodev/core.c b/drivers/aiodev/core.c
index 7240de2c40..1fbb7b9188 100644
--- a/drivers/aiodev/core.c
+++ b/drivers/aiodev/core.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* core.c - Code implementing core functionality of AIODEV susbsystem
*
@@ -5,15 +6,6 @@
*
* Copyright (c) 2015 Zodiac Inflight Innovation
* Author: Andrey Smirnov <andrew.smirnov@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- *
- * 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 <common.h>
@@ -37,18 +29,18 @@ struct aiochannel *aiochannel_by_name(const char *name)
return ERR_PTR(-ENOENT);
}
-EXPORT_SYMBOL(aiochannel_get_by_name);
+EXPORT_SYMBOL(aiochannel_by_name);
-struct aiochannel *aiochannel_get(struct device_d *dev, int index)
+struct aiochannel *aiochannel_get(struct device *dev, int index)
{
struct of_phandle_args spec;
struct aiodevice *aiodev;
int ret, chnum = 0;
- if (!dev->device_node)
+ if (!dev->of_node)
return ERR_PTR(-EINVAL);
- ret = of_parse_phandle_with_args(dev->device_node,
+ ret = of_parse_phandle_with_args(dev->of_node,
"io-channels",
"#io-channel-cells",
index, &spec);
@@ -56,7 +48,7 @@ struct aiochannel *aiochannel_get(struct device_d *dev, int index)
return ERR_PTR(ret);
list_for_each_entry(aiodev, &aiodevices, list) {
- if (aiodev->hwdev->device_node == spec.np)
+ if (aiodev->hwdev->of_node == spec.np)
goto found;
}
@@ -99,10 +91,10 @@ int aiodevice_register(struct aiodevice *aiodev)
int i, ret;
if (!aiodev->name && aiodev->hwdev &&
- aiodev->hwdev->device_node) {
+ aiodev->hwdev->of_node) {
aiodev->dev.id = DEVICE_ID_SINGLE;
- aiodev->name = of_alias_get(aiodev->hwdev->device_node);
+ aiodev->name = of_alias_get(aiodev->hwdev->of_node);
}
if (!aiodev->name) {
diff --git a/drivers/aiodev/imx7d_adc.c b/drivers/aiodev/imx7d_adc.c
new file mode 100644
index 0000000000..4d1f902031
--- /dev/null
+++ b/drivers/aiodev/imx7d_adc.c
@@ -0,0 +1,432 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Freescale i.MX7D ADC driver
+ *
+ * Copyright (C) 2015 Freescale Semiconductor, Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <io.h>
+#include <linux/printk.h>
+#include <driver.h>
+#include <init.h>
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <regulator.h>
+#include <linux/barebox-wrapper.h>
+
+#include <linux/iopoll.h>
+#include <aiodev.h>
+
+/* ADC register */
+#define IMX7D_REG_ADC_TIMER_UNIT 0x90
+#define IMX7D_REG_ADC_INT_STATUS 0xe0
+#define IMX7D_REG_ADC_CHA_B_CNV_RSLT 0xf0
+#define IMX7D_REG_ADC_CHC_D_CNV_RSLT 0x100
+#define IMX7D_REG_ADC_ADC_CFG 0x130
+
+#define IMX7D_REG_ADC_CFG1(ch) ((ch) * 0x20)
+#define IMX7D_REG_ADC_CFG2(ch) ((ch) * 0x20 + 0x10)
+
+#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN (0x1 << 31)
+#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE BIT(30)
+#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN BIT(29)
+#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(x) ((x) << 24)
+
+#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4 (0x0 << 12)
+#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8 (0x1 << 12)
+#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16 (0x2 << 12)
+#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32 (0x3 << 12)
+
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4 (0x0 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8 (0x1 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16 (0x2 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32 (0x3 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64 (0x4 << 29)
+#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128 (0x5 << 29)
+
+#define IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN BIT(31)
+#define IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN BIT(1)
+#define IMX7D_REG_ADC_ADC_CFG_ADC_EN BIT(0)
+
+#define IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS 0xf00
+#define IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT 0xf0000
+
+#define IMX7D_ADC_TIMEOUT_NSEC (100 * NSEC_PER_MSEC)
+#define IMX7D_ADC_INPUT_CLK 24000000
+
+enum imx7d_adc_clk_pre_div {
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_4,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_8,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_16,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_32,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_64,
+ IMX7D_ADC_ANALOG_CLK_PRE_DIV_128,
+};
+
+enum imx7d_adc_average_num {
+ IMX7D_ADC_AVERAGE_NUM_4,
+ IMX7D_ADC_AVERAGE_NUM_8,
+ IMX7D_ADC_AVERAGE_NUM_16,
+ IMX7D_ADC_AVERAGE_NUM_32,
+};
+
+struct imx7d_adc_feature {
+ enum imx7d_adc_clk_pre_div clk_pre_div;
+ enum imx7d_adc_average_num avg_num;
+
+ u32 core_time_unit; /* impact the sample rate */
+};
+
+struct imx7d_adc {
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *clk;
+ struct aiodevice aiodev;
+ void (*aiodev_info)(struct device *);
+
+ u32 vref_uv;
+ u32 pre_div_num;
+
+ struct regulator *vref;
+ struct imx7d_adc_feature adc_feature;
+
+ struct aiochannel aiochan[16];
+};
+
+struct imx7d_adc_analogue_core_clk {
+ u32 pre_div;
+ u32 reg_config;
+};
+
+#define IMX7D_ADC_ANALOGUE_CLK_CONFIG(_pre_div, _reg_conf) { \
+ .pre_div = (_pre_div), \
+ .reg_config = (_reg_conf), \
+}
+
+static const struct imx7d_adc_analogue_core_clk imx7d_adc_analogue_clk[] = {
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(4, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(8, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(16, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(32, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(64, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64),
+ IMX7D_ADC_ANALOGUE_CLK_CONFIG(128, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128),
+};
+
+static const u32 imx7d_adc_average_num[] = {
+ IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4,
+ IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8,
+ IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16,
+ IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32,
+};
+
+static void imx7d_adc_feature_config(struct imx7d_adc *info)
+{
+ info->adc_feature.clk_pre_div = IMX7D_ADC_ANALOG_CLK_PRE_DIV_4;
+ info->adc_feature.avg_num = IMX7D_ADC_AVERAGE_NUM_32;
+ info->adc_feature.core_time_unit = 1;
+}
+
+static void imx7d_adc_sample_rate_set(struct imx7d_adc *info)
+{
+ struct imx7d_adc_feature *adc_feature = &info->adc_feature;
+ struct imx7d_adc_analogue_core_clk adc_analogue_clk;
+ unsigned i;
+ u32 tmp_cfg1;
+ u32 sample_rate = 0;
+
+ /*
+ * Before sample set, disable channel A,B,C,D. Here we
+ * clear the bit 31 of register REG_ADC_CH_A\B\C\D_CFG1.
+ */
+ for (i = 0; i < 4; i++) {
+ tmp_cfg1 = readl(info->regs + IMX7D_REG_ADC_CFG1(i));
+ tmp_cfg1 &= ~IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN;
+ writel(tmp_cfg1, info->regs + IMX7D_REG_ADC_CFG1(i));
+ }
+
+ adc_analogue_clk = imx7d_adc_analogue_clk[adc_feature->clk_pre_div];
+ sample_rate |= adc_analogue_clk.reg_config;
+ info->pre_div_num = adc_analogue_clk.pre_div;
+
+ sample_rate |= adc_feature->core_time_unit;
+ writel(sample_rate, info->regs + IMX7D_REG_ADC_TIMER_UNIT);
+}
+
+static void imx7d_adc_hw_init(struct imx7d_adc *info)
+{
+ u32 cfg;
+
+ /* power up and enable adc analogue core */
+ cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG);
+ cfg &= ~(IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN |
+ IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN);
+ cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_EN;
+ writel(cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);
+
+ imx7d_adc_sample_rate_set(info);
+}
+
+static void imx7d_adc_channel_set(struct imx7d_adc *info, u32 channel)
+{
+ u32 cfg1 = 0;
+ u32 cfg2;
+
+ /* the channel choose single conversion, and enable average mode */
+ cfg1 |= (IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN |
+ IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE |
+ IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN);
+
+ /*
+ * physical channel 0 chose logical channel A
+ * physical channel 1 chose logical channel B
+ * physical channel 2 chose logical channel C
+ * physical channel 3 chose logical channel D
+ */
+ cfg1 |= IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(channel);
+
+ /*
+ * read register REG_ADC_CH_A\B\C\D_CFG2, according to the
+ * channel chosen
+ */
+ cfg2 = readl(info->regs + IMX7D_REG_ADC_CFG2(channel));
+
+ cfg2 |= imx7d_adc_average_num[info->adc_feature.avg_num];
+
+ /*
+ * write the register REG_ADC_CH_A\B\C\D_CFG2, according to
+ * the channel chosen
+ */
+ writel(cfg2, info->regs + IMX7D_REG_ADC_CFG2(channel));
+ writel(cfg1, info->regs + IMX7D_REG_ADC_CFG1(channel));
+}
+
+static u16 __imx7d_adc_read_data(struct imx7d_adc *info, u32 channel)
+{
+ u32 value;
+
+ /*
+ * channel A and B conversion result share one register,
+ * bit[27~16] is the channel B conversion result,
+ * bit[11~0] is the channel A conversion result.
+ * channel C and D is the same.
+ */
+ if (channel < 2)
+ value = readl(info->regs + IMX7D_REG_ADC_CHA_B_CNV_RSLT);
+ else
+ value = readl(info->regs + IMX7D_REG_ADC_CHC_D_CNV_RSLT);
+ if (channel & 0x1) /* channel B or D */
+ value = (value >> 16) & 0xFFF;
+ else /* channel A or C */
+ value &= 0xFFF;
+
+ return value;
+}
+
+static int imx7d_adc_read_data(struct imx7d_adc *info, u32 channel)
+{
+ int ret = -EAGAIN;
+ int status;
+
+ status = readl(info->regs + IMX7D_REG_ADC_INT_STATUS);
+ if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS) {
+ ret = __imx7d_adc_read_data(info, channel);
+
+ /*
+ * The register IMX7D_REG_ADC_INT_STATUS can't clear
+ * itself after read operation, need software to write
+ * 0 to the related bit. Here we clear the channel A/B/C/D
+ * conversion finished flag.
+ */
+ status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS;
+ writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);
+ }
+
+ /*
+ * If the channel A/B/C/D conversion timeout, report it and clear these
+ * timeout flags.
+ */
+ if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT) {
+ dev_err(info->dev,
+ "ADC got conversion time out interrupt: 0x%08x\n",
+ status);
+ status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT;
+ writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);
+ ret = -ETIMEDOUT;
+ }
+
+ return ret;
+}
+
+
+static int imx7d_adc_read_raw(struct aiochannel *chan, int *data)
+{
+ struct imx7d_adc *info = container_of(chan->aiodev, struct imx7d_adc, aiodev);
+ u64 raw64, start;
+ u32 channel;
+ int ret;
+
+ channel = chan->index & 0x03;
+ imx7d_adc_channel_set(info, channel);
+
+ start = get_time_ns();
+ do {
+ if (is_timeout(start, IMX7D_ADC_TIMEOUT_NSEC)) {
+ ret = -ETIMEDOUT;
+ break;
+ }
+
+ ret = imx7d_adc_read_data(info, channel);
+ } while (ret == -EAGAIN);
+
+ if (ret < 0)
+ return ret;
+
+ raw64 = ret;
+ raw64 *= info->vref_uv;
+ raw64 = div_u64(raw64, 1000);
+ *data = div_u64(raw64, (1 << 12));
+
+ return 0;
+}
+
+static const struct of_device_id imx7d_adc_match[] = {
+ { .compatible = "fsl,imx7d-adc", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx7d_adc_match);
+
+static void imx7d_adc_power_down(struct imx7d_adc *info)
+{
+ u32 adc_cfg;
+
+ adc_cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG);
+ adc_cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN |
+ IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN;
+ adc_cfg &= ~IMX7D_REG_ADC_ADC_CFG_ADC_EN;
+ writel(adc_cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);
+}
+
+static int imx7d_adc_enable(struct imx7d_adc *info)
+{
+ struct device *dev = info->dev;
+ int ret;
+
+ ret = regulator_enable(info->vref);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Can't enable adc reference top voltage\n");
+
+ ret = clk_enable(info->clk);
+ if (ret) {
+ regulator_disable(info->vref);
+ return dev_err_probe(dev, ret, "Could not enable clock.\n");
+ }
+
+ imx7d_adc_hw_init(info);
+
+ ret = regulator_get_voltage(info->vref);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "can't get vref-supply value\n");
+
+ info->vref_uv = ret;
+ return 0;
+}
+
+static u32 imx7d_adc_get_sample_rate(struct imx7d_adc *info)
+{
+ u32 analogue_core_clk;
+ u32 core_time_unit = info->adc_feature.core_time_unit;
+ u32 tmp;
+
+ analogue_core_clk = IMX7D_ADC_INPUT_CLK / info->pre_div_num;
+ tmp = (core_time_unit + 1) * 6;
+
+ return analogue_core_clk / tmp;
+}
+
+static void imx7d_adc_devinfo(struct device *dev)
+{
+ struct imx7d_adc *info = dev->parent->priv;
+
+ if (info->aiodev_info)
+ info->aiodev_info(dev);
+
+ printf("Sample Rate: %u\n", imx7d_adc_get_sample_rate(info));
+}
+
+static int imx7d_adc_probe(struct device *dev)
+{
+ struct aiodevice *aiodev;
+ struct imx7d_adc *info;
+ int ret, i;
+
+ info = xzalloc(sizeof(*info));
+
+ info->dev = dev;
+
+ info->clk = clk_get(dev, "adc");
+ if (IS_ERR(info->clk))
+ return dev_err_probe(dev, PTR_ERR(info->clk), "Failed getting clock\n");
+
+ info->vref = regulator_get(dev, "vref");
+ if (IS_ERR(info->vref))
+ return dev_err_probe(dev, PTR_ERR(info->vref),
+ "Failed getting reference voltage\n");
+
+ info->regs = dev_request_mem_region(dev, 0);
+ if (IS_ERR(info->regs))
+ return dev_err_probe(dev, PTR_ERR(info->regs),
+ "Failed to get memory region\n");
+
+ dev->priv = info;
+ aiodev = &info->aiodev;
+
+ aiodev->num_channels = 4;
+ aiodev->hwdev = dev;
+ aiodev->read = imx7d_adc_read_raw;
+ aiodev->channels = xzalloc(aiodev->num_channels * sizeof(aiodev->channels[0]));
+
+ for (i = 0; i < aiodev->num_channels; i++) {
+ aiodev->channels[i] = &info->aiochan[i];
+ info->aiochan[i].unit = "mV";
+ }
+
+ imx7d_adc_feature_config(info);
+
+ ret = imx7d_adc_enable(info);
+ if (ret)
+ return ret;
+
+ ret = aiodevice_register(aiodev);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to register aiodev\n");
+
+ info->aiodev_info = aiodev->dev.info;
+ aiodev->dev.info = imx7d_adc_devinfo;
+
+ return 0;
+}
+
+static void imx7d_adc_disable(struct device *dev)
+{
+ struct imx7d_adc *info = dev->priv;
+
+ imx7d_adc_power_down(info);
+
+ clk_disable(info->clk);
+ regulator_disable(info->vref);
+}
+
+static struct driver imx7d_adc_driver = {
+ .probe = imx7d_adc_probe,
+ .name = "imx7d_adc",
+ .of_compatible = imx7d_adc_match,
+ .remove = imx7d_adc_disable,
+};
+device_platform_driver(imx7d_adc_driver);
+
+MODULE_AUTHOR("Haibo Chen <haibo.chen@freescale.com>");
+MODULE_DESCRIPTION("Freescale IMX7D ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/aiodev/imx_thermal.c b/drivers/aiodev/imx_thermal.c
index 6dd2baa36c..2693ad05e0 100644
--- a/drivers/aiodev/imx_thermal.c
+++ b/drivers/aiodev/imx_thermal.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* imx_thermal
*
@@ -8,15 +9,6 @@
* (C) Copyright 2014-2015 Freescale Semiconductor, Inc.
* Author: Nitin Garg <nitin.garg@freescale.com>
* Ye Li <Ye.Li@freescale.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- *
- * 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 <common.h>
@@ -30,14 +22,14 @@
#include <linux/math64.h>
#include <linux/log2.h>
#include <linux/clk.h>
-#include <mach/imx6-anadig.h>
+#include <linux/nvmem-consumer.h>
+#include <mach/imx/imx6-anadig.h>
#include <io.h>
#include <aiodev.h>
#include <mfd/syscon.h>
#define FACTOR0 10000000
#define MEASURE_FREQ 327
-#define OCOTP_ANA1_OFFSET (0xE * sizeof(uint32_t))
struct imx_thermal_data {
int c1, c2;
@@ -114,36 +106,19 @@ static int imx_thermal_read(struct aiochannel *chan, int *val)
return 0;
}
-static int imx_thermal_probe(struct device_d *dev)
+static int imx_thermal_probe(struct device *dev)
{
uint32_t ocotp_ana1;
- struct device_node *node;
struct imx_thermal_data *imx_thermal;
- struct cdev *ocotp;
int t1, n1, t2, n2;
int ret;
- node = of_parse_phandle(dev->device_node, "fsl,tempmon-data", 0);
- if (!node) {
- dev_err(dev, "No calibration data source\n");
- return -ENODEV;
- }
-
- ocotp = cdev_by_device_node(node);
- if (!ocotp) {
- dev_err(dev, "No OCOTP character device\n");
- return -ENODEV;
- }
-
- ret = cdev_read(ocotp, &ocotp_ana1, sizeof(ocotp_ana1),
- OCOTP_ANA1_OFFSET, 0);
- if (ret != sizeof(ocotp_ana1)) {
- dev_err(dev, "Failed to read calibration data\n");
- return ret < 0 ? ret : -EIO;
- }
+ ret = nvmem_cell_read_variable_le_u32(dev, "calib", &ocotp_ana1);
+ if (ret)
+ return ret;
imx_thermal = xzalloc(sizeof(*imx_thermal));
- imx_thermal->base = syscon_base_lookup_by_phandle(dev->device_node,
+ imx_thermal->base = syscon_base_lookup_by_phandle(dev->of_node,
"fsl,tempmon");
if (IS_ERR(imx_thermal->base)) {
dev_err(dev, "Could not get ANATOP address\n");
@@ -200,9 +175,10 @@ static const struct of_device_id of_imx_thermal_match[] = {
{ .compatible = "fsl,imx6sx-tempmon", },
{ /* end */ }
};
+MODULE_DEVICE_TABLE(of, of_imx_thermal_match);
-static struct driver_d imx_thermal_driver = {
+static struct driver imx_thermal_driver = {
.name = "imx_thermal",
.probe = imx_thermal_probe,
.of_compatible = DRV_OF_COMPAT(of_imx_thermal_match),
diff --git a/drivers/aiodev/lm75.c b/drivers/aiodev/lm75.c
index 1900636555..13b7ac4710 100644
--- a/drivers/aiodev/lm75.c
+++ b/drivers/aiodev/lm75.c
@@ -57,7 +57,7 @@ static const u8 LM75_REG_TEMP[3] = {
/* Each client has this additional data */
struct lm75_data {
struct i2c_client *client;
- struct device_d dev;
+ struct device dev;
u8 resolution; /* In bits, between 9 and 12 */
struct aiochannel aiochan;
struct aiodevice aiodev;
@@ -102,7 +102,7 @@ static int lm75_get_temp(struct aiochannel *chan, int *val)
return 0;
}
-static int lm75_probe(struct device_d *dev)
+static int lm75_probe(struct device *dev)
{
struct lm75_data *data;
int status;
@@ -237,7 +237,7 @@ static const struct platform_device_id lm75_ids[] = {
{ /* LIST END */ }
};
-static struct driver_d lm75_driver = {
+static struct driver lm75_driver = {
.name = "lm75",
.probe = lm75_probe,
.id_table = lm75_ids,
diff --git a/drivers/aiodev/mc13xxx_adc.c b/drivers/aiodev/mc13xxx_adc.c
index c5d1efdf8d..21eea1f525 100644
--- a/drivers/aiodev/mc13xxx_adc.c
+++ b/drivers/aiodev/mc13xxx_adc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* mc13xxx_adc
*
@@ -6,15 +7,6 @@
* Based on the code of analogous driver from Linux:
* Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright (C) 2009 Sascha Hauer, Pengutronix
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- *
- * 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 <common.h>
@@ -186,7 +178,7 @@ err:
return ret;
}
-int mc13xxx_adc_probe(struct device_d *dev, struct mc13xxx *mc_dev)
+int mc13xxx_adc_probe(struct device *dev, struct mc13xxx *mc_dev)
{
int i;
int ret;
diff --git a/drivers/aiodev/qoriq_thermal.c b/drivers/aiodev/qoriq_thermal.c
index d62048ef13..20998a0f97 100644
--- a/drivers/aiodev/qoriq_thermal.c
+++ b/drivers/aiodev/qoriq_thermal.c
@@ -67,7 +67,7 @@ struct qoriq_tmu_regs {
* Thermal zone data
*/
struct qoriq_tmu_data {
- struct device_d *dev;
+ struct device *dev;
struct clk *clk;
struct qoriq_tmu_regs __iomem *regs;
int sensor_id;
@@ -118,7 +118,7 @@ static int qoriq_tmu_get_sensor_id(void)
struct of_phandle_args sensor_specs;
struct device_node *np, *sensor_np;
- np = of_find_node_by_name(NULL, "thermal-zones");
+ np = of_find_node_by_name_address(NULL, "thermal-zones");
if (!np)
return -ENODEV;
@@ -146,7 +146,7 @@ static int qoriq_tmu_calibration(struct qoriq_tmu_data *data)
int i, val, len;
u32 range[4];
const u32 *calibration;
- struct device_node *np = data->dev->device_node;
+ struct device_node *np = data->dev->of_node;
if (of_property_read_u32_array(np, "fsl,tmu-range", range, 4)) {
dev_err(data->dev, "missing calibration range.\n");
@@ -187,9 +187,9 @@ static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
tmu_write(data, TMR_DISABLE, &data->regs->tmr);
}
-static int qoriq_tmu_probe(struct device_d *dev)
+static int qoriq_tmu_probe(struct device *dev)
{
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
struct qoriq_tmu_data *data;
u32 site;
int ret;
@@ -245,8 +245,9 @@ static const struct of_device_id qoriq_tmu_match[] = {
{ .compatible = "fsl,imx8mq-tmu",},
{},
};
+MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
-static struct driver_d imx_thermal_driver = {
+static struct driver imx_thermal_driver = {
.name = "qoriq_thermal",
.probe = qoriq_tmu_probe,
.of_compatible = DRV_OF_COMPAT(qoriq_tmu_match),
diff --git a/drivers/aiodev/rockchip_saradc.c b/drivers/aiodev/rockchip_saradc.c
index 302f73c2c3..3c5c0e94da 100644
--- a/drivers/aiodev/rockchip_saradc.c
+++ b/drivers/aiodev/rockchip_saradc.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2021, WolfVision GmbH
* Author: Michael Riesch <michael.riesch@wolfvision.net>
@@ -85,7 +85,7 @@ static int rockchip_saradc_read(struct aiochannel *chan, int *val)
return 0;
}
-static int rockchip_saradc_probe(struct device_d *dev)
+static int rockchip_saradc_probe(struct device *dev)
{
struct rockchip_saradc_data *data;
int i, ret;
@@ -188,8 +188,9 @@ static const struct of_device_id of_rockchip_saradc_match[] = {
{ .compatible = "rockchip,rk3568-saradc", .data = &rk3568_saradc_cfg },
{ /* end */ }
};
+MODULE_DEVICE_TABLE(of, of_rockchip_saradc_match);
-static struct driver_d rockchip_saradc_driver = {
+static struct driver rockchip_saradc_driver = {
.name = "rockchip_saradc",
.probe = rockchip_saradc_probe,
.of_compatible = DRV_OF_COMPAT(of_rockchip_saradc_match),
diff --git a/drivers/aiodev/st_gyro.c b/drivers/aiodev/st_gyro.c
new file mode 100644
index 0000000000..3a8b6a7761
--- /dev/null
+++ b/drivers/aiodev/st_gyro.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2022 Ahmad Fatoum
+
+#include <common.h>
+#include <driver.h>
+#include <xfuncs.h>
+#include <spi/spi.h>
+#include <aiodev.h>
+
+#define ST_GYRO_WHO_AM_I 0x0F
+#define ST_GYRO_CTRL_REG1 0x20
+
+#define ST_GYRO_DEFAULT_OUT_TEMP_ADDR 0x26
+#define ST_GYRO_DEFAULT_OUT_X_L_ADDR 0x28
+#define ST_GYRO_DEFAULT_OUT_Y_L_ADDR 0x2a
+#define ST_GYRO_DEFAULT_OUT_Z_L_ADDR 0x2c
+
+#define ST_GYRO_OUT_L_ADDR(idx) \
+ (ST_GYRO_DEFAULT_OUT_X_L_ADDR + 2 * (idx))
+
+#define ST_GYRO_OUT_H_ADDR(idx) \
+ (ST_GYRO_OUT_L_ADDR(idx) + 1)
+
+#define ST_GYRO_READ 0x80
+#define ST_GYRO_WRITE 0x00
+#define ST_GYRO_MULTI 0x40
+
+struct st_gyro {
+ struct aiodevice aiodev;
+ struct aiochannel aiochan[4];
+ struct spi_device *spi;
+};
+#define to_st_gyro(chan) container_of(chan->aiodev, struct st_gyro, aiodev)
+
+static int st_gyro_read(struct aiochannel *chan, int *val)
+{
+ struct st_gyro *gyro = to_st_gyro(chan);
+ int ret;
+ u8 tx;
+ u8 rx_h, rx_l;
+
+ if (chan->index == 3) {
+ tx = ST_GYRO_DEFAULT_OUT_TEMP_ADDR | ST_GYRO_READ;
+ ret = spi_write_then_read(gyro->spi, &tx, 1, &rx_l, 1);
+ if (ret)
+ return ret;
+
+ *val = (s8)rx_l;
+ return 0;
+ }
+
+ tx = ST_GYRO_OUT_H_ADDR(chan->index) | ST_GYRO_READ;
+ ret = spi_write_then_read(gyro->spi, &tx, 1, &rx_h, 1);
+ if (ret)
+ return ret;
+
+ tx = ST_GYRO_OUT_L_ADDR(chan->index) | ST_GYRO_READ;
+ ret = spi_write_then_read(gyro->spi, &tx, 1, &rx_l, 1);
+ if (ret)
+ return ret;
+
+ *val = (s16)((rx_h << 8) | rx_l);
+ *val *= 250;
+ *val >>= 16;
+
+ return 0;
+}
+
+static int st_gyro_probe(struct device *dev)
+{
+ u8 tx[2], rx[2];
+ struct st_gyro *gyro;
+ int ret, i;
+
+ gyro = xzalloc(sizeof(*gyro));
+ gyro->spi = to_spi_device(dev);
+
+ tx[0] = ST_GYRO_WHO_AM_I | ST_GYRO_READ;
+ ret = spi_write_then_read(gyro->spi, tx, 1, rx, 1);
+ if (ret)
+ return -EIO;
+ if (rx[0] != 0xD4)
+ return dev_err_probe(dev, -ENODEV, "unexpected device WAI: %02x\n", rx[0]);
+
+ /* initialize device */
+ tx[0] = ST_GYRO_CTRL_REG1 | ST_GYRO_WRITE;
+ tx[1] = 0xF; /* normal mode, 3 channels */
+ ret = spi_write(gyro->spi, tx, 2);
+ if (ret)
+ return -EIO;
+
+ gyro->aiodev.num_channels = 4;
+ gyro->aiodev.hwdev = dev;
+ gyro->aiodev.read = st_gyro_read;
+ gyro->aiodev.name = "gyroscope";
+ gyro->aiodev.channels =
+ xmalloc(gyro->aiodev.num_channels *
+ sizeof(gyro->aiodev.channels[0]));
+ for (i = 0; i < 3; i++) {
+ gyro->aiodev.channels[i] = &gyro->aiochan[i];
+ gyro->aiochan[i].unit = "dps";
+ gyro->aiochan[i].index = i;
+ }
+
+ gyro->aiodev.channels[3] = &gyro->aiochan[3];
+ gyro->aiochan[3].unit = "C";
+ gyro->aiochan[3].index = 3;
+
+ return aiodevice_register(&gyro->aiodev);
+}
+
+static const struct of_device_id st_gyro_match[] = {
+ { .compatible = "st,l3gd20-gyro" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, st_gyro_match);
+
+static struct driver st_gyro_driver = {
+ .name = "st_gyro",
+ .probe = st_gyro_probe,
+ .of_compatible = st_gyro_match,
+};
+device_spi_driver(st_gyro_driver);
diff --git a/drivers/aiodev/stm32-adc-core.c b/drivers/aiodev/stm32-adc-core.c
index 410e2a894e..f4e22d47af 100644
--- a/drivers/aiodev/stm32-adc-core.c
+++ b/drivers/aiodev/stm32-adc-core.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2018, STMicroelectronics - All Rights Reserved
* Author: Fabrice Gasnier <fabrice.gasnier@st.com>
@@ -56,7 +56,7 @@ static const struct stm32h7_adc_ck_spec stm32h7_adc_ckmodes_spec[] = {
{ 3, 0, 4 },
};
-static int stm32h7_adc_clk_sel(struct device_d *dev,
+static int stm32h7_adc_clk_sel(struct device *dev,
struct stm32_adc_common *common)
{
u32 ckmode, presc;
@@ -137,7 +137,7 @@ out:
return 0;
}
-static int stm32_adc_core_probe(struct device_d *dev)
+static int stm32_adc_core_probe(struct device *dev)
{
struct stm32_adc_common *common;
int ret;
@@ -186,7 +186,7 @@ static int stm32_adc_core_probe(struct device_d *dev)
goto err_bclk_disable;
dev->priv = common;
- return of_platform_populate(dev->device_node, NULL, dev);
+ return of_platform_populate(dev->of_node, NULL, dev);
err_bclk_disable:
clk_disable(common->bclk);
@@ -202,8 +202,9 @@ static const struct of_device_id stm32_adc_core_ids[] = {
{ .compatible = "st,stm32mp1-adc-core" },
{}
};
+MODULE_DEVICE_TABLE(of, stm32_adc_core_ids);
-static struct driver_d stm32_adc_core_driver = {
+static struct driver stm32_adc_core_driver = {
.name = "stm32-adc-core",
.probe = stm32_adc_core_probe,
.of_compatible = DRV_OF_COMPAT(stm32_adc_core_ids),
diff --git a/drivers/aiodev/stm32-adc-core.h b/drivers/aiodev/stm32-adc-core.h
index de6c0b9495..29695c3e58 100644
--- a/drivers/aiodev/stm32-adc-core.h
+++ b/drivers/aiodev/stm32-adc-core.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2018, STMicroelectronics - All Rights Reserved
* Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
diff --git a/drivers/aiodev/stm32-adc.c b/drivers/aiodev/stm32-adc.c
index c99b995eaf..a1998da62c 100644
--- a/drivers/aiodev/stm32-adc.c
+++ b/drivers/aiodev/stm32-adc.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2018, STMicroelectronics - All Rights Reserved
* Author: Fabrice Gasnier <fabrice.gasnier@st.com>
@@ -91,7 +91,7 @@ static void stm32_adc_stop(struct stm32_adc *adc)
static int stm32_adc_start_channel(struct stm32_adc *adc, int channel)
{
- struct device_d *dev = adc->aiodev.hwdev;
+ struct device *dev = adc->aiodev.hwdev;
struct stm32_adc_common *common = adc->common;
int ret;
u32 val;
@@ -153,7 +153,7 @@ static int stm32_adc_start_channel(struct stm32_adc *adc, int channel)
static int stm32_adc_channel_data(struct stm32_adc *adc, int channel,
int *data)
{
- struct device_d *dev = &adc->aiodev.dev;
+ struct device *dev = &adc->aiodev.dev;
int ret;
u32 val;
@@ -211,7 +211,7 @@ static void stm32_adc_smpr_init(struct stm32_adc *adc, int channel, u32 smp_ns)
adc->smpr_val[r] = (adc->smpr_val[r] & ~mask) | (smp << shift);
}
-static int stm32_adc_chan_of_init(struct device_d *dev, struct stm32_adc *adc)
+static int stm32_adc_chan_of_init(struct device *dev, struct stm32_adc *adc)
{
unsigned int i;
int num_channels = 0, num_times = 0;
@@ -219,7 +219,7 @@ static int stm32_adc_chan_of_init(struct device_d *dev, struct stm32_adc *adc)
int ret;
/* Retrieve single ended channels listed in device tree */
- of_get_property(dev->device_node, "st,adc-channels", &num_channels);
+ of_get_property(dev->of_node, "st,adc-channels", &num_channels);
num_channels /= sizeof(__be32);
if (num_channels > adc->cfg->max_channels) {
@@ -228,7 +228,7 @@ static int stm32_adc_chan_of_init(struct device_d *dev, struct stm32_adc *adc)
}
/* Optional sample time is provided either for each, or all channels */
- of_get_property(dev->device_node, "st,min-sample-time-nsecs", &num_times);
+ of_get_property(dev->of_node, "st,min-sample-time-nsecs", &num_times);
num_times /= sizeof(__be32);
if (num_times > 1 && num_times != num_channels) {
dev_err(dev, "Invalid st,min-sample-time-nsecs\n");
@@ -252,7 +252,8 @@ static int stm32_adc_chan_of_init(struct device_d *dev, struct stm32_adc *adc)
for (i = 0; i < num_channels; i++) {
u32 chan;
- ret = of_property_read_u32_index(dev->device_node, "st,adc-channels", i, &chan);
+ ret = of_property_read_u32_index(dev->of_node,
+ "st,adc-channels", i, &chan);
if (ret)
return ret;
@@ -273,7 +274,8 @@ static int stm32_adc_chan_of_init(struct device_d *dev, struct stm32_adc *adc)
* get either no value, 1 shared value for all indexes, or one
* value per channel.
*/
- of_property_read_u32_index(dev->device_node, "st,min-sample-time-nsecs",
+ of_property_read_u32_index(dev->of_node,
+ "st,min-sample-time-nsecs",
i, &smp);
/* Prepare sampling time settings */
stm32_adc_smpr_init(adc, chan, smp);
@@ -288,14 +290,14 @@ static int stm32_adc_chan_of_init(struct device_d *dev, struct stm32_adc *adc)
return ret;
}
-static int stm32_adc_probe(struct device_d *dev)
+static int stm32_adc_probe(struct device *dev)
{
struct stm32_adc_common *common = dev->parent->priv;
struct stm32_adc *adc;
u32 offset;
int ret;
- ret = of_property_read_u32(dev->device_node, "reg", &offset);
+ ret = of_property_read_u32(dev->of_node, "reg", &offset);
if (ret) {
dev_err(dev, "Can't read reg property\n");
return ret;
@@ -360,13 +362,28 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
.has_vregready = true,
};
+/* STM32MP13 programmable sampling time (ADC clock cycles, rounded down) */
+static const unsigned int stm32mp13_adc_smp_cycles[STM32_ADC_MAX_SMP + 1] = {
+ 2, 6, 12, 24, 47, 92, 247, 640,
+};
+
+static const struct stm32_adc_cfg stm32mp13_adc_cfg = {
+ .num_bits = 16,
+ .max_channels = 19,
+ .smp_bits = stm32h7_smp_bits,
+ .smp_cycles = stm32mp13_adc_smp_cycles,
+ .has_vregready = false,
+};
+
static const struct of_device_id stm32_adc_match[] = {
{ .compatible = "st,stm32h7-adc", .data = &stm32h7_adc_cfg },
{ .compatible = "st,stm32mp1-adc", .data = &stm32mp1_adc_cfg },
+ { .compatible = "st,stm32mp13-adc", .data = &stm32mp13_adc_cfg },
{}
};
+MODULE_DEVICE_TABLE(of, stm32_adc_match);
-static struct driver_d stm32_adc_driver = {
+static struct driver stm32_adc_driver = {
.name = "stm32-adc",
.probe = stm32_adc_probe,
.of_compatible = DRV_OF_COMPAT(stm32_adc_match),
diff --git a/drivers/aiodev/vf610_adc.c b/drivers/aiodev/vf610_adc.c
new file mode 100644
index 0000000000..cb062e0846
--- /dev/null
+++ b/drivers/aiodev/vf610_adc.c
@@ -0,0 +1,620 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Freescale Vybrid vf610 ADC driver
+ *
+ * Copyright 2013 Freescale Semiconductor, Inc.
+ */
+
+#include <clock.h>
+#include <io.h>
+#include <linux/printk.h>
+#include <driver.h>
+#include <init.h>
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <regulator.h>
+#include <linux/err.h>
+
+#include <aiodev.h>
+
+/* This will be the driver name the kernel reports */
+#define DRIVER_NAME "vf610-adc"
+
+/* Vybrid/IMX ADC registers */
+#define VF610_REG_ADC_HC0 0x00
+#define VF610_REG_ADC_HS 0x08
+#define VF610_REG_ADC_R0 0x0c
+#define VF610_REG_ADC_CFG 0x14
+#define VF610_REG_ADC_GC 0x18
+#define VF610_REG_ADC_GS 0x1c
+
+/* Configuration register field define */
+#define VF610_ADC_MODE_BIT8 0x00
+#define VF610_ADC_MODE_BIT10 0x04
+#define VF610_ADC_MODE_BIT12 0x08
+#define VF610_ADC_MODE_MASK 0x0c
+#define VF610_ADC_BUSCLK2_SEL 0x01
+#define VF610_ADC_ALTCLK_SEL 0x02
+#define VF610_ADC_ADACK_SEL 0x03
+#define VF610_ADC_ADCCLK_MASK 0x03
+#define VF610_ADC_CLK_DIV2 0x20
+#define VF610_ADC_CLK_DIV4 0x40
+#define VF610_ADC_CLK_DIV8 0x60
+#define VF610_ADC_CLK_MASK 0x60
+#define VF610_ADC_ADLSMP_LONG 0x10
+#define VF610_ADC_ADSTS_SHORT 0x100
+#define VF610_ADC_ADSTS_NORMAL 0x200
+#define VF610_ADC_ADSTS_LONG 0x300
+#define VF610_ADC_ADSTS_MASK 0x300
+#define VF610_ADC_ADLPC_EN 0x80
+#define VF610_ADC_ADHSC_EN 0x400
+#define VF610_ADC_REFSEL_VALT 0x800
+#define VF610_ADC_REFSEL_VBG 0x1000
+#define VF610_ADC_AVGS_8 0x4000
+#define VF610_ADC_AVGS_16 0x8000
+#define VF610_ADC_AVGS_32 0xC000
+#define VF610_ADC_AVGS_MASK 0xC000
+#define VF610_ADC_OVWREN 0x10000
+
+/* General control register field define */
+#define VF610_ADC_AVGEN 0x20
+#define VF610_ADC_CAL 0x80
+
+/* Other field define */
+#define VF610_ADC_ADCHC(x) ((x) & 0x1F)
+#define VF610_ADC_CONV_DISABLE 0x1F
+#define VF610_ADC_HS_COCO0 0x1
+#define VF610_ADC_CALF 0x2
+#define VF610_ADC_TIMEOUT_NSEC (100 * NSEC_PER_MSEC)
+
+#define DEFAULT_SAMPLE_TIME 1000
+
+enum clk_sel {
+ VF610_ADCIOC_BUSCLK_SET,
+ VF610_ADCIOC_ALTCLK_SET,
+ VF610_ADCIOC_ADACK_SET,
+};
+
+enum vol_ref {
+ VF610_ADCIOC_VR_VREF_SET,
+ VF610_ADCIOC_VR_VALT_SET,
+ VF610_ADCIOC_VR_VBG_SET,
+};
+
+enum average_sel {
+ VF610_ADC_SAMPLE_1,
+ VF610_ADC_SAMPLE_4,
+ VF610_ADC_SAMPLE_8,
+ VF610_ADC_SAMPLE_16,
+ VF610_ADC_SAMPLE_32,
+};
+
+enum conversion_mode_sel {
+ VF610_ADC_CONV_NORMAL,
+ VF610_ADC_CONV_HIGH_SPEED,
+ VF610_ADC_CONV_LOW_POWER,
+};
+
+enum lst_adder_sel {
+ VF610_ADCK_CYCLES_3,
+ VF610_ADCK_CYCLES_5,
+ VF610_ADCK_CYCLES_7,
+ VF610_ADCK_CYCLES_9,
+ VF610_ADCK_CYCLES_13,
+ VF610_ADCK_CYCLES_17,
+ VF610_ADCK_CYCLES_21,
+ VF610_ADCK_CYCLES_25,
+};
+
+struct vf610_adc_feature {
+ enum clk_sel clk_sel;
+ enum vol_ref vol_ref;
+ enum conversion_mode_sel conv_mode;
+
+ int clk_div;
+ int sample_rate;
+ int res_mode;
+ u32 lst_adder_index;
+ u32 default_sample_time;
+
+ bool calibration;
+ bool ovwren;
+};
+
+struct vf610_adc {
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *clk;
+ struct aiodevice aiodev;
+ void (*aiodev_info)(struct device *);
+
+ u32 vref_uv;
+ u32 value;
+ struct regulator *vref;
+
+ u32 max_adck_rate[3];
+ struct vf610_adc_feature adc_feature;
+
+ u32 sample_freq_avail[5];
+
+ struct aiochannel aiochan[16];
+};
+
+static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 };
+static const u32 vf610_lst_adder[] = { 3, 5, 7, 9, 13, 17, 21, 25 };
+
+static inline void vf610_adc_calculate_rates(struct vf610_adc *info)
+{
+ struct vf610_adc_feature *adc_feature = &info->adc_feature;
+ unsigned long adck_rate, ipg_rate = clk_get_rate(info->clk);
+ u32 adck_period, lst_addr_min;
+ int divisor, i;
+
+ adck_rate = info->max_adck_rate[adc_feature->conv_mode];
+
+ if (adck_rate) {
+ /* calculate clk divider which is within specification */
+ divisor = ipg_rate / adck_rate;
+ adc_feature->clk_div = 1 << fls(divisor + 1);
+ } else {
+ /* fall-back value using a safe divisor */
+ adc_feature->clk_div = 8;
+ }
+
+ adck_rate = ipg_rate / adc_feature->clk_div;
+
+ /*
+ * Determine the long sample time adder value to be used based
+ * on the default minimum sample time provided.
+ */
+ adck_period = NSEC_PER_SEC / adck_rate;
+ lst_addr_min = adc_feature->default_sample_time / adck_period;
+ for (i = 0; i < ARRAY_SIZE(vf610_lst_adder); i++) {
+ if (vf610_lst_adder[i] > lst_addr_min) {
+ adc_feature->lst_adder_index = i;
+ break;
+ }
+ }
+
+ /*
+ * Calculate ADC sample frequencies
+ * Sample time unit is ADCK cycles. ADCK clk source is ipg clock,
+ * which is the same as bus clock.
+ *
+ * ADC conversion time = SFCAdder + AverageNum x (BCT + LSTAdder)
+ * SFCAdder: fixed to 6 ADCK cycles
+ * AverageNum: 1, 4, 8, 16, 32 samples for hardware average.
+ * BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode
+ * LSTAdder(Long Sample Time): 3, 5, 7, 9, 13, 17, 21, 25 ADCK cycles
+ */
+ for (i = 0; i < ARRAY_SIZE(vf610_hw_avgs); i++)
+ info->sample_freq_avail[i] =
+ adck_rate / (6 + vf610_hw_avgs[i] *
+ (25 + vf610_lst_adder[adc_feature->lst_adder_index]));
+}
+
+static inline void vf610_adc_cfg_init(struct vf610_adc *info)
+{
+ struct vf610_adc_feature *adc_feature = &info->adc_feature;
+
+ /* set default Configuration for ADC controller */
+ adc_feature->clk_sel = VF610_ADCIOC_BUSCLK_SET;
+ adc_feature->vol_ref = VF610_ADCIOC_VR_VREF_SET;
+
+ adc_feature->calibration = true;
+ adc_feature->ovwren = true;
+
+ adc_feature->res_mode = 12;
+ adc_feature->sample_rate = 1;
+
+ adc_feature->conv_mode = VF610_ADC_CONV_LOW_POWER;
+
+ vf610_adc_calculate_rates(info);
+}
+
+static void vf610_adc_cfg_post_set(struct vf610_adc *info)
+{
+ struct vf610_adc_feature *adc_feature = &info->adc_feature;
+ int cfg_data = 0;
+ int gc_data = 0;
+
+ switch (adc_feature->clk_sel) {
+ case VF610_ADCIOC_ALTCLK_SET:
+ cfg_data |= VF610_ADC_ALTCLK_SEL;
+ break;
+ case VF610_ADCIOC_ADACK_SET:
+ cfg_data |= VF610_ADC_ADACK_SEL;
+ break;
+ default:
+ break;
+ }
+
+ /* low power set for calibration */
+ cfg_data |= VF610_ADC_ADLPC_EN;
+
+ /* enable high speed for calibration */
+ cfg_data |= VF610_ADC_ADHSC_EN;
+
+ /* voltage reference */
+ switch (adc_feature->vol_ref) {
+ case VF610_ADCIOC_VR_VREF_SET:
+ break;
+ case VF610_ADCIOC_VR_VALT_SET:
+ cfg_data |= VF610_ADC_REFSEL_VALT;
+ break;
+ case VF610_ADCIOC_VR_VBG_SET:
+ cfg_data |= VF610_ADC_REFSEL_VBG;
+ break;
+ default:
+ dev_err(info->dev, "error voltage reference\n");
+ }
+
+ /* data overwrite enable */
+ if (adc_feature->ovwren)
+ cfg_data |= VF610_ADC_OVWREN;
+
+ writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
+ writel(gc_data, info->regs + VF610_REG_ADC_GC);
+}
+
+static void vf610_adc_calibration(struct vf610_adc *info)
+{
+ int adc_gc, hc_cfg;
+ u64 start;
+ int coco;
+
+ if (!info->adc_feature.calibration)
+ return;
+
+ hc_cfg = VF610_ADC_CONV_DISABLE;
+ writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
+
+ adc_gc = readl(info->regs + VF610_REG_ADC_GC);
+ writel(adc_gc | VF610_ADC_CAL, info->regs + VF610_REG_ADC_GC);
+
+ start = get_time_ns();
+ do {
+ if (is_timeout(start, VF610_ADC_TIMEOUT_NSEC)) {
+ dev_err(info->dev, "Timeout for adc calibration\n");
+ break;
+ }
+
+ coco = readl(info->regs + VF610_REG_ADC_HS);
+ } while (!(coco & VF610_ADC_HS_COCO0));
+
+ adc_gc = readl(info->regs + VF610_REG_ADC_GS);
+ if (adc_gc & VF610_ADC_CALF)
+ dev_err(info->dev, "ADC calibration failed\n");
+
+ info->adc_feature.calibration = false;
+}
+
+static void vf610_adc_cfg_set(struct vf610_adc *info)
+{
+ struct vf610_adc_feature *adc_feature = &(info->adc_feature);
+ int cfg_data;
+
+ cfg_data = readl(info->regs + VF610_REG_ADC_CFG);
+
+ cfg_data &= ~VF610_ADC_ADLPC_EN;
+ if (adc_feature->conv_mode == VF610_ADC_CONV_LOW_POWER)
+ cfg_data |= VF610_ADC_ADLPC_EN;
+
+ cfg_data &= ~VF610_ADC_ADHSC_EN;
+ if (adc_feature->conv_mode == VF610_ADC_CONV_HIGH_SPEED)
+ cfg_data |= VF610_ADC_ADHSC_EN;
+
+ writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
+}
+
+static void vf610_adc_sample_set(struct vf610_adc *info)
+{
+ struct vf610_adc_feature *adc_feature = &(info->adc_feature);
+ int cfg_data, gc_data;
+
+ cfg_data = readl(info->regs + VF610_REG_ADC_CFG);
+ gc_data = readl(info->regs + VF610_REG_ADC_GC);
+
+ /* resolution mode */
+ cfg_data &= ~VF610_ADC_MODE_MASK;
+ switch (adc_feature->res_mode) {
+ case 8:
+ cfg_data |= VF610_ADC_MODE_BIT8;
+ break;
+ case 10:
+ cfg_data |= VF610_ADC_MODE_BIT10;
+ break;
+ case 12:
+ cfg_data |= VF610_ADC_MODE_BIT12;
+ break;
+ default:
+ dev_err(info->dev, "error resolution mode\n");
+ break;
+ }
+
+ /* clock select and clock divider */
+ cfg_data &= ~(VF610_ADC_CLK_MASK | VF610_ADC_ADCCLK_MASK);
+ switch (adc_feature->clk_div) {
+ case 1:
+ break;
+ case 2:
+ cfg_data |= VF610_ADC_CLK_DIV2;
+ break;
+ case 4:
+ cfg_data |= VF610_ADC_CLK_DIV4;
+ break;
+ case 8:
+ cfg_data |= VF610_ADC_CLK_DIV8;
+ break;
+ case 16:
+ switch (adc_feature->clk_sel) {
+ case VF610_ADCIOC_BUSCLK_SET:
+ cfg_data |= VF610_ADC_BUSCLK2_SEL | VF610_ADC_CLK_DIV8;
+ break;
+ default:
+ dev_err(info->dev, "error clk divider\n");
+ break;
+ }
+ break;
+ }
+
+ /*
+ * Set ADLSMP and ADSTS based on the Long Sample Time Adder value
+ * determined.
+ */
+ switch (adc_feature->lst_adder_index) {
+ case VF610_ADCK_CYCLES_3:
+ break;
+ case VF610_ADCK_CYCLES_5:
+ cfg_data |= VF610_ADC_ADSTS_SHORT;
+ break;
+ case VF610_ADCK_CYCLES_7:
+ cfg_data |= VF610_ADC_ADSTS_NORMAL;
+ break;
+ case VF610_ADCK_CYCLES_9:
+ cfg_data |= VF610_ADC_ADSTS_LONG;
+ break;
+ case VF610_ADCK_CYCLES_13:
+ cfg_data |= VF610_ADC_ADLSMP_LONG;
+ break;
+ case VF610_ADCK_CYCLES_17:
+ cfg_data |= VF610_ADC_ADLSMP_LONG;
+ cfg_data |= VF610_ADC_ADSTS_SHORT;
+ break;
+ case VF610_ADCK_CYCLES_21:
+ cfg_data |= VF610_ADC_ADLSMP_LONG;
+ cfg_data |= VF610_ADC_ADSTS_NORMAL;
+ break;
+ case VF610_ADCK_CYCLES_25:
+ cfg_data |= VF610_ADC_ADLSMP_LONG;
+ cfg_data |= VF610_ADC_ADSTS_NORMAL;
+ break;
+ default:
+ dev_err(info->dev, "error in sample time select\n");
+ }
+
+ /* update hardware average selection */
+ cfg_data &= ~VF610_ADC_AVGS_MASK;
+ gc_data &= ~VF610_ADC_AVGEN;
+ switch (adc_feature->sample_rate) {
+ case VF610_ADC_SAMPLE_1:
+ break;
+ case VF610_ADC_SAMPLE_4:
+ gc_data |= VF610_ADC_AVGEN;
+ break;
+ case VF610_ADC_SAMPLE_8:
+ gc_data |= VF610_ADC_AVGEN;
+ cfg_data |= VF610_ADC_AVGS_8;
+ break;
+ case VF610_ADC_SAMPLE_16:
+ gc_data |= VF610_ADC_AVGEN;
+ cfg_data |= VF610_ADC_AVGS_16;
+ break;
+ case VF610_ADC_SAMPLE_32:
+ gc_data |= VF610_ADC_AVGEN;
+ cfg_data |= VF610_ADC_AVGS_32;
+ break;
+ default:
+ dev_err(info->dev,
+ "error hardware sample average select\n");
+ }
+
+ writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
+ writel(gc_data, info->regs + VF610_REG_ADC_GC);
+}
+
+static void vf610_adc_hw_init(struct vf610_adc *info)
+{
+ /* CFG: Feature set */
+ vf610_adc_cfg_post_set(info);
+ vf610_adc_sample_set(info);
+
+ /* adc calibration */
+ vf610_adc_calibration(info);
+
+ /* CFG: power and speed set */
+ vf610_adc_cfg_set(info);
+}
+
+static int __vf610_adc_read_data(struct vf610_adc *info)
+{
+ int result;
+
+ result = readl(info->regs + VF610_REG_ADC_R0);
+
+ switch (info->adc_feature.res_mode) {
+ case 8:
+ result &= 0xFF;
+ break;
+ case 10:
+ result &= 0x3FF;
+ break;
+ case 12:
+ result &= 0xFFF;
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+static int vf610_adc_read_data(struct vf610_adc *info)
+{
+ int coco;
+ int ret = -EAGAIN;
+
+ coco = readl(info->regs + VF610_REG_ADC_HS);
+ if (coco & VF610_ADC_HS_COCO0) {
+ ret = __vf610_adc_read_data(info);
+ }
+
+ return ret;
+}
+
+static int vf610_read_sample(struct aiochannel *chan, int *val)
+{
+ struct vf610_adc *info = container_of(chan->aiodev, struct vf610_adc, aiodev);
+ unsigned int hc_cfg;
+ u64 raw64, start;
+ int ret;
+
+ hc_cfg = VF610_ADC_ADCHC(chan->index & 0x0f);
+ writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
+
+ start = get_time_ns();
+ do {
+ if (is_timeout(start, VF610_ADC_TIMEOUT_NSEC)) {
+ ret = -ETIMEDOUT;
+ break;
+ }
+
+ ret = vf610_adc_read_data(info);
+ } while (ret == -EAGAIN);
+
+ if (ret < 0)
+ return ret;
+
+ raw64 = ret;
+ raw64 *= info->vref_uv;
+ raw64 = div_u64(raw64, 1000);
+ *val = div_u64(raw64, (1 << 12));
+
+ return 0;
+}
+
+
+static const struct of_device_id vf610_adc_match[] = {
+ { .compatible = "fsl,vf610-adc", },
+ { /* sentinel */ }
+};
+
+static void vf610_adc_devinfo(struct device *dev)
+{
+ struct vf610_adc *info = dev->parent->priv;
+
+ if (info->aiodev_info)
+ info->aiodev_info(dev);
+
+ pr_info("Sample Rate: %u\n", info->sample_freq_avail[info->adc_feature.sample_rate]);
+}
+
+static int vf610_adc_probe(struct device *dev)
+{
+ struct aiodevice *aiodev;
+ struct vf610_adc *info;
+ int ret, i;
+
+ info = xzalloc(sizeof(*info));
+ info->dev = dev;
+
+ info->regs = dev_request_mem_region(dev, 0);
+ if (IS_ERR(info->regs))
+ return PTR_ERR(info->regs);
+
+ info->clk = clk_get(dev, "adc");
+ if (IS_ERR(info->clk)) {
+ dev_err(dev, "failed getting clock, err = %ld\n",
+ PTR_ERR(info->clk));
+ return PTR_ERR(info->clk);
+ }
+
+ info->vref = regulator_get(dev, "vref");
+ if (IS_ERR(info->vref))
+ return PTR_ERR(info->vref);
+
+ of_property_read_u32_array(dev->device_node, "fsl,adck-max-frequency", info->max_adck_rate, 3);
+
+ info->adc_feature.default_sample_time = DEFAULT_SAMPLE_TIME;
+ of_property_read_u32(dev->device_node, "min-sample-time", &info->adc_feature.default_sample_time);
+
+ dev->priv = info;
+ aiodev = &info->aiodev;
+
+ aiodev->num_channels = 16;
+ aiodev->hwdev = dev;
+ aiodev->read = vf610_read_sample;
+ aiodev->channels = xzalloc(aiodev->num_channels * sizeof(aiodev->channels[0]));
+
+ for (i = 0; i < aiodev->num_channels; i++) {
+ aiodev->channels[i] = &info->aiochan[i];
+ info->aiochan[i].unit = "mV";
+ }
+
+ ret = regulator_enable(info->vref);
+ if (ret)
+ return ret;
+
+ info->vref_uv = regulator_get_voltage(info->vref);
+
+ ret = clk_enable(info->clk);
+ if (ret) {
+ dev_err(dev,
+ "Could not prepare or enable the clock.\n");
+ goto error_adc_clk_enable;
+ }
+
+ vf610_adc_cfg_init(info);
+ vf610_adc_hw_init(info);
+
+ ret = aiodevice_register(aiodev);
+ if (ret < 0) {
+ dev_err(dev, "Couldn't register the device.\n");
+ goto error_adc_buffer_init;
+ }
+
+ info->aiodev_info = aiodev->dev.info;
+ aiodev->dev.info = vf610_adc_devinfo;
+
+ return 0;
+
+error_adc_buffer_init:
+ clk_disable(info->clk);
+error_adc_clk_enable:
+ regulator_disable(info->vref);
+
+ return ret;
+}
+
+static void vf610_adc_remove(struct device *dev)
+{
+ struct vf610_adc *info = dev->priv;
+
+ regulator_disable(info->vref);
+ clk_disable(info->clk);
+}
+
+static struct driver vf610_adc_driver = {
+ .probe = vf610_adc_probe,
+ .remove = vf610_adc_remove,
+ .name = DRIVER_NAME,
+ .of_compatible = vf610_adc_match,
+};
+
+device_platform_driver(vf610_adc_driver);
+
+MODULE_AUTHOR("Fugang Duan <B38611@freescale.com>");
+MODULE_DESCRIPTION("Freescale VF610 ADC driver");
+MODULE_LICENSE("GPL v2");