[PATCH 5/5] iio:adc:spear_adc move out of staging

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This simple driver is ready to move out of staging.

Signed-off-by: Jonathan Cameron <jic23@xxxxxxxxxx>
---
 drivers/iio/adc/Kconfig             |   8 +
 drivers/iio/adc/Makefile            |   1 +
 drivers/iio/adc/spear_adc.c         | 429 ++++++++++++++++++++++++++++++++++++
 drivers/staging/iio/adc/Kconfig     |   8 -
 drivers/staging/iio/adc/Makefile    |   1 -
 drivers/staging/iio/adc/spear_adc.c | 429 ------------------------------------
 6 files changed, 438 insertions(+), 438 deletions(-)

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 5553206..2e3e1b0 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -164,6 +164,14 @@ config NAU7802
 	  To compile this driver as a module, choose M here: the
 	  module will be called nau7802.
 
+config SPEAR_ADC
+	tristate "ST SPEAr ADC"
+	depends on PLAT_SPEAR || COMPILE_TEST
+	depends on HAS_IOMEM
+	help
+	  Say yes here to build support for the integrated ADC inside the
+	  ST SPEAr SoC. Provides direct access via sysfs.
+
 config TI_ADC081C
 	tristate "Texas Instruments ADC081C021/027"
 	depends on I2C
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 89f1216..8378fb2 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_MAX1363) += max1363.o
 obj-$(CONFIG_MCP320X) += mcp320x.o
 obj-$(CONFIG_MCP3422) += mcp3422.o
 obj-$(CONFIG_NAU7802) += nau7802.o
+obj-$(CONFIG_SPEAR_ADC) += spear_adc.o
 obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
 obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
 obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c
new file mode 100644
index 0000000..d8232f1
--- /dev/null
+++ b/drivers/iio/adc/spear_adc.c
@@ -0,0 +1,429 @@
+/*
+ * ST SPEAr ADC driver
+ *
+ * Copyright 2012 Stefan Roese <sr@xxxxxxx>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/completion.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/* SPEAR registers definitions */
+#define SPEAR600_ADC_SCAN_RATE_LO(x)	((x) & 0xFFFF)
+#define SPEAR600_ADC_SCAN_RATE_HI(x)	(((x) >> 0x10) & 0xFFFF)
+#define SPEAR_ADC_CLK_LOW(x)		(((x) & 0xf) << 0)
+#define SPEAR_ADC_CLK_HIGH(x)		(((x) & 0xf) << 4)
+
+/* Bit definitions for SPEAR_ADC_STATUS */
+#define SPEAR_ADC_STATUS_START_CONVERSION	(1 << 0)
+#define SPEAR_ADC_STATUS_CHANNEL_NUM(x)		((x) << 1)
+#define SPEAR_ADC_STATUS_ADC_ENABLE		(1 << 4)
+#define SPEAR_ADC_STATUS_AVG_SAMPLE(x)		((x) << 5)
+#define SPEAR_ADC_STATUS_VREF_INTERNAL		(1 << 9)
+
+#define SPEAR_ADC_DATA_MASK		0x03ff
+#define SPEAR_ADC_DATA_BITS		10
+
+#define SPEAR_ADC_MOD_NAME "spear-adc"
+
+#define SPEAR_ADC_CHANNEL_NUM		8
+
+#define SPEAR_ADC_CLK_MIN			2500000
+#define SPEAR_ADC_CLK_MAX			20000000
+
+struct adc_regs_spear3xx {
+	u32 status;
+	u32 average;
+	u32 scan_rate;
+	u32 clk;	/* Not avail for 1340 & 1310 */
+	u32 ch_ctrl[SPEAR_ADC_CHANNEL_NUM];
+	u32 ch_data[SPEAR_ADC_CHANNEL_NUM];
+};
+
+struct chan_data {
+	u32 lsb;
+	u32 msb;
+};
+
+struct adc_regs_spear6xx {
+	u32 status;
+	u32 pad[2];
+	u32 clk;
+	u32 ch_ctrl[SPEAR_ADC_CHANNEL_NUM];
+	struct chan_data ch_data[SPEAR_ADC_CHANNEL_NUM];
+	u32 scan_rate_lo;
+	u32 scan_rate_hi;
+	struct chan_data average;
+};
+
+struct spear_adc_info;
+
+struct spear_adc_ops {
+	u32 (*get_average)(struct spear_adc_info *info);
+	void (*set_scanrate)(struct spear_adc_info *info, u32 rate);
+};
+
+struct spear_adc_info {
+	struct device_node *np;
+	union {
+		struct adc_regs_spear3xx __iomem *adc_base_spear3xx;
+		struct adc_regs_spear6xx __iomem *adc_base_spear6xx;
+	};
+	const struct spear_adc_ops *ops;
+	struct clk *clk;
+	struct completion completion;
+	u32 current_clk;
+	u32 sampling_freq;
+	u32 avg_samples;
+	u32 vref_external;
+	u32 value;
+};
+
+static void spear_adc_set_status(struct spear_adc_info *info, u32 val)
+{
+	__raw_writel(val, &info->adc_base_spear6xx->status);
+}
+
+static void spear_adc_set_clk(struct spear_adc_info *info, u32 val)
+{
+	u32 clk_high, clk_low, count;
+	u32 apb_clk = clk_get_rate(info->clk);
+
+	count = (apb_clk + val - 1) / val;
+	clk_low = count / 2;
+	clk_high = count - clk_low;
+	info->current_clk = apb_clk / count;
+
+	__raw_writel(SPEAR_ADC_CLK_LOW(clk_low) | SPEAR_ADC_CLK_HIGH(clk_high),
+		     &info->adc_base_spear6xx->clk);
+}
+
+static void spear_adc_set_ctrl(struct spear_adc_info *info, int n,
+			       u32 val)
+{
+	__raw_writel(val, &info->adc_base_spear6xx->ch_ctrl[n]);
+}
+
+static u32 spear600_adc_get_average(struct spear_adc_info *info)
+{
+	return __raw_readl(&info->adc_base_spear6xx->average.msb) &
+		SPEAR_ADC_DATA_MASK;
+}
+
+static u32 spear300_adc_get_average(struct spear_adc_info *info)
+{
+	return __raw_readl(&info->adc_base_spear3xx->average) &
+		SPEAR_ADC_DATA_MASK;
+}
+
+static void spear600_adc_set_scanrate(struct spear_adc_info *info, u32 rate)
+{
+	__raw_writel(SPEAR600_ADC_SCAN_RATE_LO(rate),
+		&info->adc_base_spear6xx->scan_rate_lo);
+	__raw_writel(SPEAR600_ADC_SCAN_RATE_HI(rate),
+		&info->adc_base_spear6xx->scan_rate_hi);
+}
+
+static void spear300_adc_set_scanrate(struct spear_adc_info *info, u32 rate)
+{
+	__raw_writel(rate, &info->adc_base_spear3xx->scan_rate);
+}
+
+static const struct spear_adc_ops spear600_adc_ops = {
+	.get_average = &spear600_adc_get_average,
+	.set_scanrate = &spear600_adc_set_scanrate,
+};
+
+static const struct spear_adc_ops spear300_adc_ops = {
+	.get_average = &spear300_adc_get_average,
+	.set_scanrate = &spear300_adc_set_scanrate,
+};
+
+static int spear_read_raw(struct iio_dev *indio_dev,
+			  struct iio_chan_spec const *chan,
+			  int *val,
+			  int *val2,
+			  long mask)
+{
+	struct spear_adc_info *info = iio_priv(indio_dev);
+	u32 status;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		mutex_lock(&indio_dev->mlock);
+
+		status = SPEAR_ADC_STATUS_CHANNEL_NUM(chan->channel) |
+			SPEAR_ADC_STATUS_AVG_SAMPLE(info->avg_samples) |
+			SPEAR_ADC_STATUS_START_CONVERSION |
+			SPEAR_ADC_STATUS_ADC_ENABLE;
+		if (info->vref_external == 0)
+			status |= SPEAR_ADC_STATUS_VREF_INTERNAL;
+
+		spear_adc_set_status(info, status);
+		wait_for_completion(&info->completion); /* set by ISR */
+		*val = info->value;
+
+		mutex_unlock(&indio_dev->mlock);
+
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_SCALE:
+		*val = info->vref_external;
+		*val2 = SPEAR_ADC_DATA_BITS;
+		return IIO_VAL_FRACTIONAL_LOG2;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		*val = info->current_clk;
+		return IIO_VAL_INT;
+	}
+
+	return -EINVAL;
+}
+
+static int spear_adc_write_raw(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       int val,
+			       int val2,
+			       long mask)
+{
+	struct spear_adc_info *info = iio_priv(indio_dev);
+	u32 clk_high, clk_low, count;
+	u32 apb_clk = clk_get_rate(info->clk);
+	int ret = 0;
+
+	mutex_lock(&indio_dev->mlock);
+
+	if ((val < SPEAR_ADC_CLK_MIN) ||
+		(val > SPEAR_ADC_CLK_MAX) ||
+		(val2 != 0)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	count = (apb_clk + val - 1) / val;
+	clk_low = count / 2;
+	clk_high = count - clk_low;
+	info->current_clk = apb_clk / count;
+	spear_adc_set_clk(info, val);
+
+out:
+	mutex_unlock(&indio_dev->mlock);
+	return ret;
+}
+
+#define SPEAR_ADC_CHAN(idx) {				\
+	.type = IIO_VOLTAGE,				\
+	.indexed = 1,					\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
+	.channel = idx,					\
+}
+
+static const struct iio_chan_spec spear_adc_iio_channels[] = {
+	SPEAR_ADC_CHAN(0),
+	SPEAR_ADC_CHAN(1),
+	SPEAR_ADC_CHAN(2),
+	SPEAR_ADC_CHAN(3),
+	SPEAR_ADC_CHAN(4),
+	SPEAR_ADC_CHAN(5),
+	SPEAR_ADC_CHAN(6),
+	SPEAR_ADC_CHAN(7),
+};
+
+static irqreturn_t spear_adc_isr(int irq, void *dev_id)
+{
+	struct spear_adc_info *info = (struct spear_adc_info *)dev_id;
+
+	/* Read value to clear IRQ */
+	info->value = info->ops->get_average(info);
+	complete(&info->completion);
+
+	return IRQ_HANDLED;
+}
+
+static int spear_adc_configure(struct spear_adc_info *info)
+{
+	int i;
+
+	/* Reset ADC core */
+	spear_adc_set_status(info, 0);
+	__raw_writel(0, &info->adc_base_spear6xx->clk);
+	for (i = 0; i < 8; i++)
+		spear_adc_set_ctrl(info, i, 0);
+	info->ops->set_scanrate(info, 0);
+
+	spear_adc_set_clk(info, info->sampling_freq);
+
+	return 0;
+}
+
+static const struct iio_info spear_adc_iio_info = {
+	.read_raw = &spear_read_raw,
+	.write_raw = &spear_adc_write_raw,
+	.driver_module = THIS_MODULE,
+};
+
+static int spear_adc_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct device *dev = &pdev->dev;
+	struct spear_adc_info *info;
+	struct iio_dev *iodev = NULL;
+	int ret = -ENODEV;
+	int irq;
+
+	iodev = devm_iio_device_alloc(dev, sizeof(struct spear_adc_info));
+	if (!iodev) {
+		dev_err(dev, "failed allocating iio device\n");
+		return -ENOMEM;
+	}
+
+	info = iio_priv(iodev);
+	info->np = np;
+
+	/*
+	 * SPEAr600 has a different register layout than other SPEAr SoC's
+	 * (e.g. SPEAr3xx). Let's provide two register base addresses
+	 * to support multi-arch kernels.
+	 */
+	if (of_device_is_compatible(info->np, "st,spear600-adc")) {
+		info->adc_base_spear6xx = of_iomap(np, 0);
+		if (!info->adc_base_spear6xx) {
+			dev_err(dev, "failed mapping memory\n");
+			return -ENOMEM;
+		}
+		info->ops = &spear600_adc_ops;
+	} else {
+		info->adc_base_spear3xx = of_iomap(np, 0);
+		if (!info->adc_base_spear3xx) {
+			dev_err(dev, "failed mapping memory\n");
+			return -ENOMEM;
+		}
+		info->ops = &spear300_adc_ops;
+	}
+
+	info->clk = clk_get(dev, NULL);
+	if (IS_ERR(info->clk)) {
+		dev_err(dev, "failed getting clock\n");
+		goto errout1;
+	}
+
+	ret = clk_prepare_enable(info->clk);
+	if (ret) {
+		dev_err(dev, "failed enabling clock\n");
+		goto errout2;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq <= 0) {
+		dev_err(dev, "failed getting interrupt resource\n");
+		ret = -EINVAL;
+		goto errout3;
+	}
+
+	ret = devm_request_irq(dev, irq, spear_adc_isr, 0, SPEAR_ADC_MOD_NAME,
+			info);
+	if (ret < 0) {
+		dev_err(dev, "failed requesting interrupt\n");
+		goto errout3;
+	}
+
+	if (of_property_read_u32(np, "sampling-frequency",
+				 &info->sampling_freq)) {
+		dev_err(dev, "sampling-frequency missing in DT\n");
+		ret = -EINVAL;
+		goto errout3;
+	}
+
+	/*
+	 * Optional avg_samples defaults to 0, resulting in single data
+	 * conversion
+	 */
+	of_property_read_u32(np, "average-samples", &info->avg_samples);
+
+	/*
+	 * Optional vref_external defaults to 0, resulting in internal vref
+	 * selection
+	 */
+	of_property_read_u32(np, "vref-external", &info->vref_external);
+
+	spear_adc_configure(info);
+
+	platform_set_drvdata(pdev, iodev);
+
+	init_completion(&info->completion);
+
+	iodev->name = SPEAR_ADC_MOD_NAME;
+	iodev->dev.parent = dev;
+	iodev->info = &spear_adc_iio_info;
+	iodev->modes = INDIO_DIRECT_MODE;
+	iodev->channels = spear_adc_iio_channels;
+	iodev->num_channels = ARRAY_SIZE(spear_adc_iio_channels);
+
+	ret = iio_device_register(iodev);
+	if (ret)
+		goto errout3;
+
+	dev_info(dev, "SPEAR ADC driver loaded, IRQ %d\n", irq);
+
+	return 0;
+
+errout3:
+	clk_disable_unprepare(info->clk);
+errout2:
+	clk_put(info->clk);
+errout1:
+	iounmap(info->adc_base_spear6xx);
+	return ret;
+}
+
+static int spear_adc_remove(struct platform_device *pdev)
+{
+	struct iio_dev *iodev = platform_get_drvdata(pdev);
+	struct spear_adc_info *info = iio_priv(iodev);
+
+	iio_device_unregister(iodev);
+	clk_disable_unprepare(info->clk);
+	clk_put(info->clk);
+	iounmap(info->adc_base_spear6xx);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id spear_adc_dt_ids[] = {
+	{ .compatible = "st,spear600-adc", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, spear_adc_dt_ids);
+#endif
+
+static struct platform_driver spear_adc_driver = {
+	.probe		= spear_adc_probe,
+	.remove		= spear_adc_remove,
+	.driver		= {
+		.name	= SPEAR_ADC_MOD_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(spear_adc_dt_ids),
+	},
+};
+
+module_platform_driver(spear_adc_driver);
+
+MODULE_AUTHOR("Stefan Roese <sr@xxxxxxx>");
+MODULE_DESCRIPTION("SPEAr ADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 3633298..a37d001 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -126,12 +126,4 @@ config MXS_LRADC
 	  To compile this driver as a module, choose M here: the
 	  module will be called mxs-lradc.
 
-config SPEAR_ADC
-	tristate "ST SPEAr ADC"
-	depends on PLAT_SPEAR || COMPILE_TEST
-	depends on HAS_IOMEM
-	help
-	  Say yes here to build support for the integrated ADC inside the
-	  ST SPEAr SoC. Provides direct access via sysfs.
-
 endmenu
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index 3e9fb14..b5915c1 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -19,4 +19,3 @@ obj-$(CONFIG_AD7192) += ad7192.o
 obj-$(CONFIG_AD7280) += ad7280a.o
 obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
 obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
-obj-$(CONFIG_SPEAR_ADC) += spear_adc.o
diff --git a/drivers/staging/iio/adc/spear_adc.c b/drivers/staging/iio/adc/spear_adc.c
deleted file mode 100644
index d8232f1..0000000
--- a/drivers/staging/iio/adc/spear_adc.c
+++ /dev/null
@@ -1,429 +0,0 @@
-/*
- * ST SPEAr ADC driver
- *
- * Copyright 2012 Stefan Roese <sr@xxxxxxx>
- *
- * Licensed under the GPL-2.
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/completion.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-
-#include <linux/iio/iio.h>
-#include <linux/iio/sysfs.h>
-
-/* SPEAR registers definitions */
-#define SPEAR600_ADC_SCAN_RATE_LO(x)	((x) & 0xFFFF)
-#define SPEAR600_ADC_SCAN_RATE_HI(x)	(((x) >> 0x10) & 0xFFFF)
-#define SPEAR_ADC_CLK_LOW(x)		(((x) & 0xf) << 0)
-#define SPEAR_ADC_CLK_HIGH(x)		(((x) & 0xf) << 4)
-
-/* Bit definitions for SPEAR_ADC_STATUS */
-#define SPEAR_ADC_STATUS_START_CONVERSION	(1 << 0)
-#define SPEAR_ADC_STATUS_CHANNEL_NUM(x)		((x) << 1)
-#define SPEAR_ADC_STATUS_ADC_ENABLE		(1 << 4)
-#define SPEAR_ADC_STATUS_AVG_SAMPLE(x)		((x) << 5)
-#define SPEAR_ADC_STATUS_VREF_INTERNAL		(1 << 9)
-
-#define SPEAR_ADC_DATA_MASK		0x03ff
-#define SPEAR_ADC_DATA_BITS		10
-
-#define SPEAR_ADC_MOD_NAME "spear-adc"
-
-#define SPEAR_ADC_CHANNEL_NUM		8
-
-#define SPEAR_ADC_CLK_MIN			2500000
-#define SPEAR_ADC_CLK_MAX			20000000
-
-struct adc_regs_spear3xx {
-	u32 status;
-	u32 average;
-	u32 scan_rate;
-	u32 clk;	/* Not avail for 1340 & 1310 */
-	u32 ch_ctrl[SPEAR_ADC_CHANNEL_NUM];
-	u32 ch_data[SPEAR_ADC_CHANNEL_NUM];
-};
-
-struct chan_data {
-	u32 lsb;
-	u32 msb;
-};
-
-struct adc_regs_spear6xx {
-	u32 status;
-	u32 pad[2];
-	u32 clk;
-	u32 ch_ctrl[SPEAR_ADC_CHANNEL_NUM];
-	struct chan_data ch_data[SPEAR_ADC_CHANNEL_NUM];
-	u32 scan_rate_lo;
-	u32 scan_rate_hi;
-	struct chan_data average;
-};
-
-struct spear_adc_info;
-
-struct spear_adc_ops {
-	u32 (*get_average)(struct spear_adc_info *info);
-	void (*set_scanrate)(struct spear_adc_info *info, u32 rate);
-};
-
-struct spear_adc_info {
-	struct device_node *np;
-	union {
-		struct adc_regs_spear3xx __iomem *adc_base_spear3xx;
-		struct adc_regs_spear6xx __iomem *adc_base_spear6xx;
-	};
-	const struct spear_adc_ops *ops;
-	struct clk *clk;
-	struct completion completion;
-	u32 current_clk;
-	u32 sampling_freq;
-	u32 avg_samples;
-	u32 vref_external;
-	u32 value;
-};
-
-static void spear_adc_set_status(struct spear_adc_info *info, u32 val)
-{
-	__raw_writel(val, &info->adc_base_spear6xx->status);
-}
-
-static void spear_adc_set_clk(struct spear_adc_info *info, u32 val)
-{
-	u32 clk_high, clk_low, count;
-	u32 apb_clk = clk_get_rate(info->clk);
-
-	count = (apb_clk + val - 1) / val;
-	clk_low = count / 2;
-	clk_high = count - clk_low;
-	info->current_clk = apb_clk / count;
-
-	__raw_writel(SPEAR_ADC_CLK_LOW(clk_low) | SPEAR_ADC_CLK_HIGH(clk_high),
-		     &info->adc_base_spear6xx->clk);
-}
-
-static void spear_adc_set_ctrl(struct spear_adc_info *info, int n,
-			       u32 val)
-{
-	__raw_writel(val, &info->adc_base_spear6xx->ch_ctrl[n]);
-}
-
-static u32 spear600_adc_get_average(struct spear_adc_info *info)
-{
-	return __raw_readl(&info->adc_base_spear6xx->average.msb) &
-		SPEAR_ADC_DATA_MASK;
-}
-
-static u32 spear300_adc_get_average(struct spear_adc_info *info)
-{
-	return __raw_readl(&info->adc_base_spear3xx->average) &
-		SPEAR_ADC_DATA_MASK;
-}
-
-static void spear600_adc_set_scanrate(struct spear_adc_info *info, u32 rate)
-{
-	__raw_writel(SPEAR600_ADC_SCAN_RATE_LO(rate),
-		&info->adc_base_spear6xx->scan_rate_lo);
-	__raw_writel(SPEAR600_ADC_SCAN_RATE_HI(rate),
-		&info->adc_base_spear6xx->scan_rate_hi);
-}
-
-static void spear300_adc_set_scanrate(struct spear_adc_info *info, u32 rate)
-{
-	__raw_writel(rate, &info->adc_base_spear3xx->scan_rate);
-}
-
-static const struct spear_adc_ops spear600_adc_ops = {
-	.get_average = &spear600_adc_get_average,
-	.set_scanrate = &spear600_adc_set_scanrate,
-};
-
-static const struct spear_adc_ops spear300_adc_ops = {
-	.get_average = &spear300_adc_get_average,
-	.set_scanrate = &spear300_adc_set_scanrate,
-};
-
-static int spear_read_raw(struct iio_dev *indio_dev,
-			  struct iio_chan_spec const *chan,
-			  int *val,
-			  int *val2,
-			  long mask)
-{
-	struct spear_adc_info *info = iio_priv(indio_dev);
-	u32 status;
-
-	switch (mask) {
-	case IIO_CHAN_INFO_RAW:
-		mutex_lock(&indio_dev->mlock);
-
-		status = SPEAR_ADC_STATUS_CHANNEL_NUM(chan->channel) |
-			SPEAR_ADC_STATUS_AVG_SAMPLE(info->avg_samples) |
-			SPEAR_ADC_STATUS_START_CONVERSION |
-			SPEAR_ADC_STATUS_ADC_ENABLE;
-		if (info->vref_external == 0)
-			status |= SPEAR_ADC_STATUS_VREF_INTERNAL;
-
-		spear_adc_set_status(info, status);
-		wait_for_completion(&info->completion); /* set by ISR */
-		*val = info->value;
-
-		mutex_unlock(&indio_dev->mlock);
-
-		return IIO_VAL_INT;
-
-	case IIO_CHAN_INFO_SCALE:
-		*val = info->vref_external;
-		*val2 = SPEAR_ADC_DATA_BITS;
-		return IIO_VAL_FRACTIONAL_LOG2;
-	case IIO_CHAN_INFO_SAMP_FREQ:
-		*val = info->current_clk;
-		return IIO_VAL_INT;
-	}
-
-	return -EINVAL;
-}
-
-static int spear_adc_write_raw(struct iio_dev *indio_dev,
-			       struct iio_chan_spec const *chan,
-			       int val,
-			       int val2,
-			       long mask)
-{
-	struct spear_adc_info *info = iio_priv(indio_dev);
-	u32 clk_high, clk_low, count;
-	u32 apb_clk = clk_get_rate(info->clk);
-	int ret = 0;
-
-	mutex_lock(&indio_dev->mlock);
-
-	if ((val < SPEAR_ADC_CLK_MIN) ||
-		(val > SPEAR_ADC_CLK_MAX) ||
-		(val2 != 0)) {
-		ret = -EINVAL;
-		goto out;
-	}
-
-	count = (apb_clk + val - 1) / val;
-	clk_low = count / 2;
-	clk_high = count - clk_low;
-	info->current_clk = apb_clk / count;
-	spear_adc_set_clk(info, val);
-
-out:
-	mutex_unlock(&indio_dev->mlock);
-	return ret;
-}
-
-#define SPEAR_ADC_CHAN(idx) {				\
-	.type = IIO_VOLTAGE,				\
-	.indexed = 1,					\
-	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
-	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
-	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
-	.channel = idx,					\
-}
-
-static const struct iio_chan_spec spear_adc_iio_channels[] = {
-	SPEAR_ADC_CHAN(0),
-	SPEAR_ADC_CHAN(1),
-	SPEAR_ADC_CHAN(2),
-	SPEAR_ADC_CHAN(3),
-	SPEAR_ADC_CHAN(4),
-	SPEAR_ADC_CHAN(5),
-	SPEAR_ADC_CHAN(6),
-	SPEAR_ADC_CHAN(7),
-};
-
-static irqreturn_t spear_adc_isr(int irq, void *dev_id)
-{
-	struct spear_adc_info *info = (struct spear_adc_info *)dev_id;
-
-	/* Read value to clear IRQ */
-	info->value = info->ops->get_average(info);
-	complete(&info->completion);
-
-	return IRQ_HANDLED;
-}
-
-static int spear_adc_configure(struct spear_adc_info *info)
-{
-	int i;
-
-	/* Reset ADC core */
-	spear_adc_set_status(info, 0);
-	__raw_writel(0, &info->adc_base_spear6xx->clk);
-	for (i = 0; i < 8; i++)
-		spear_adc_set_ctrl(info, i, 0);
-	info->ops->set_scanrate(info, 0);
-
-	spear_adc_set_clk(info, info->sampling_freq);
-
-	return 0;
-}
-
-static const struct iio_info spear_adc_iio_info = {
-	.read_raw = &spear_read_raw,
-	.write_raw = &spear_adc_write_raw,
-	.driver_module = THIS_MODULE,
-};
-
-static int spear_adc_probe(struct platform_device *pdev)
-{
-	struct device_node *np = pdev->dev.of_node;
-	struct device *dev = &pdev->dev;
-	struct spear_adc_info *info;
-	struct iio_dev *iodev = NULL;
-	int ret = -ENODEV;
-	int irq;
-
-	iodev = devm_iio_device_alloc(dev, sizeof(struct spear_adc_info));
-	if (!iodev) {
-		dev_err(dev, "failed allocating iio device\n");
-		return -ENOMEM;
-	}
-
-	info = iio_priv(iodev);
-	info->np = np;
-
-	/*
-	 * SPEAr600 has a different register layout than other SPEAr SoC's
-	 * (e.g. SPEAr3xx). Let's provide two register base addresses
-	 * to support multi-arch kernels.
-	 */
-	if (of_device_is_compatible(info->np, "st,spear600-adc")) {
-		info->adc_base_spear6xx = of_iomap(np, 0);
-		if (!info->adc_base_spear6xx) {
-			dev_err(dev, "failed mapping memory\n");
-			return -ENOMEM;
-		}
-		info->ops = &spear600_adc_ops;
-	} else {
-		info->adc_base_spear3xx = of_iomap(np, 0);
-		if (!info->adc_base_spear3xx) {
-			dev_err(dev, "failed mapping memory\n");
-			return -ENOMEM;
-		}
-		info->ops = &spear300_adc_ops;
-	}
-
-	info->clk = clk_get(dev, NULL);
-	if (IS_ERR(info->clk)) {
-		dev_err(dev, "failed getting clock\n");
-		goto errout1;
-	}
-
-	ret = clk_prepare_enable(info->clk);
-	if (ret) {
-		dev_err(dev, "failed enabling clock\n");
-		goto errout2;
-	}
-
-	irq = platform_get_irq(pdev, 0);
-	if (irq <= 0) {
-		dev_err(dev, "failed getting interrupt resource\n");
-		ret = -EINVAL;
-		goto errout3;
-	}
-
-	ret = devm_request_irq(dev, irq, spear_adc_isr, 0, SPEAR_ADC_MOD_NAME,
-			info);
-	if (ret < 0) {
-		dev_err(dev, "failed requesting interrupt\n");
-		goto errout3;
-	}
-
-	if (of_property_read_u32(np, "sampling-frequency",
-				 &info->sampling_freq)) {
-		dev_err(dev, "sampling-frequency missing in DT\n");
-		ret = -EINVAL;
-		goto errout3;
-	}
-
-	/*
-	 * Optional avg_samples defaults to 0, resulting in single data
-	 * conversion
-	 */
-	of_property_read_u32(np, "average-samples", &info->avg_samples);
-
-	/*
-	 * Optional vref_external defaults to 0, resulting in internal vref
-	 * selection
-	 */
-	of_property_read_u32(np, "vref-external", &info->vref_external);
-
-	spear_adc_configure(info);
-
-	platform_set_drvdata(pdev, iodev);
-
-	init_completion(&info->completion);
-
-	iodev->name = SPEAR_ADC_MOD_NAME;
-	iodev->dev.parent = dev;
-	iodev->info = &spear_adc_iio_info;
-	iodev->modes = INDIO_DIRECT_MODE;
-	iodev->channels = spear_adc_iio_channels;
-	iodev->num_channels = ARRAY_SIZE(spear_adc_iio_channels);
-
-	ret = iio_device_register(iodev);
-	if (ret)
-		goto errout3;
-
-	dev_info(dev, "SPEAR ADC driver loaded, IRQ %d\n", irq);
-
-	return 0;
-
-errout3:
-	clk_disable_unprepare(info->clk);
-errout2:
-	clk_put(info->clk);
-errout1:
-	iounmap(info->adc_base_spear6xx);
-	return ret;
-}
-
-static int spear_adc_remove(struct platform_device *pdev)
-{
-	struct iio_dev *iodev = platform_get_drvdata(pdev);
-	struct spear_adc_info *info = iio_priv(iodev);
-
-	iio_device_unregister(iodev);
-	clk_disable_unprepare(info->clk);
-	clk_put(info->clk);
-	iounmap(info->adc_base_spear6xx);
-
-	return 0;
-}
-
-#ifdef CONFIG_OF
-static const struct of_device_id spear_adc_dt_ids[] = {
-	{ .compatible = "st,spear600-adc", },
-	{ /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, spear_adc_dt_ids);
-#endif
-
-static struct platform_driver spear_adc_driver = {
-	.probe		= spear_adc_probe,
-	.remove		= spear_adc_remove,
-	.driver		= {
-		.name	= SPEAR_ADC_MOD_NAME,
-		.owner	= THIS_MODULE,
-		.of_match_table = of_match_ptr(spear_adc_dt_ids),
-	},
-};
-
-module_platform_driver(spear_adc_driver);
-
-MODULE_AUTHOR("Stefan Roese <sr@xxxxxxx>");
-MODULE_DESCRIPTION("SPEAr ADC driver");
-MODULE_LICENSE("GPL");
-- 
1.9.0

--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux