summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/iio/dac/Kconfig11
-rw-r--r--drivers/iio/dac/Makefile1
-rw-r--r--drivers/iio/dac/mcf54415_dac.c180
3 files changed, 192 insertions, 0 deletions
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index 657c68e75542..ebf7144f922a 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -527,6 +527,17 @@ config MAX5821
Say yes here to build support for Maxim MAX5821
10 bits DAC.
+config MCF54415_DAC
+ tristate "NXP MCF54415 DAC driver"
+ depends on M5441x || COMPILE_TEST
+ select REGMAP_MMIO
+ help
+ Say yes here if you want to build support for NXP ColdFire
+ MCF54415/6/7/8 12-bit DAC module.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mcf54415_dac.
+
config MCP4725
tristate "MCP4725/6 DAC driver"
depends on I2C
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 003431798498..5d20d37e44ce 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_MAX517) += max517.o
obj-$(CONFIG_MAX22007) += max22007.o
obj-$(CONFIG_MAX5522) += max5522.o
obj-$(CONFIG_MAX5821) += max5821.o
+obj-$(CONFIG_MCF54415_DAC) += mcf54415_dac.o
obj-$(CONFIG_MCP4725) += mcp4725.o
obj-$(CONFIG_MCP4728) += mcp4728.o
obj-$(CONFIG_MCP47FEB02) += mcp47feb02.o
diff --git a/drivers/iio/dac/mcf54415_dac.c b/drivers/iio/dac/mcf54415_dac.c
new file mode 100644
index 000000000000..e2c12241a534
--- /dev/null
+++ b/drivers/iio/dac/mcf54415_dac.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * NXP mcf54415 DAC driver
+ *
+ * Copyright 2026 BayLibre - adureghello@baylibre.com
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#include <linux/iio/iio.h>
+
+#define MCF54415_DAC_CR 0x00
+#define MCF54415_DAC_CR_PDN BIT(0)
+#define MCF54415_DAC_CR_HSLS BIT(6)
+#define MCF54415_DAC_CR_WMLVL GENMASK(9, 8)
+#define MCF54415_DAC_CR_FILT BIT(12)
+
+#define MCF54415_DAC_DATA 0x02
+
+struct mcf54415_dac {
+ struct regmap *map;
+ struct clk *clk;
+};
+
+static const struct regmap_config mcf54415_dac_regmap_config = {
+ .reg_bits = 16,
+ .reg_stride = 2,
+ .val_bits = 16,
+ .max_register = 0x0c, /* DACX_FILTCNT, R.M. Table 30-2 */
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static int mcf54415_dac_init(struct mcf54415_dac *info)
+{
+ u16 val = MCF54415_DAC_CR_FILT | FIELD_PREP(MCF54415_DAC_CR_WMLVL, 1);
+ int ret;
+
+ /* Fixed defaults and enable DAC (bit 0 set to 0) */
+ ret = regmap_write(info->map, MCF54415_DAC_CR, val);
+ if (ret)
+ return ret;
+
+ /* DAC is ready after 12us, from RM table 40-3 */
+ fsleep(12);
+
+ return 0;
+}
+
+static void mcf54415_dac_exit(void *data)
+{
+ struct mcf54415_dac *info = data;
+
+ regmap_set_bits(info->map, MCF54415_DAC_CR, MCF54415_DAC_CR_PDN);
+}
+
+static const struct iio_chan_spec mcf54415_dac_iio_channel = {
+ .type = IIO_VOLTAGE,
+ .output = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+};
+
+static int mcf54415_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct mcf54415_dac *info = iio_priv(indio_dev);
+ unsigned int reg;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = regmap_read(info->map, MCF54415_DAC_DATA, &reg);
+ if (ret)
+ return ret;
+ *val = reg & GENMASK(11, 0);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ /* Reference voltage as per ColdFire datasheet is 3.3V */
+ *val = 3300 /* mV */;
+ *val2 = 12;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mcf54415_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct mcf54415_dac *info = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ /* Check based on RM 30.3.2 (DACn_DATA) reg. resolution */
+ if (val < 0 || val > 4095)
+ return -EINVAL;
+ return regmap_write(info->map, MCF54415_DAC_DATA, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info mcf54415_dac_iio_info = {
+ .read_raw = &mcf54415_read_raw,
+ .write_raw = &mcf54415_write_raw,
+};
+
+static int mcf54415_dac_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct iio_dev *indio_dev;
+ struct mcf54415_dac *info;
+ void __iomem *regs;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ info = iio_priv(indio_dev);
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return dev_err_probe(dev, PTR_ERR(regs), "failed to get io regs\n");
+
+ info->map = devm_regmap_init_mmio(dev, regs, &mcf54415_dac_regmap_config);
+ if (IS_ERR(info->map))
+ return PTR_ERR(info->map);
+
+ info->clk = devm_clk_get_enabled(dev, "dac");
+ if (IS_ERR(info->clk))
+ return dev_err_probe(dev, PTR_ERR(info->clk), "failed getting clock\n");
+
+ indio_dev->name = "mcf54415";
+ indio_dev->info = &mcf54415_dac_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = &mcf54415_dac_iio_channel;
+ indio_dev->num_channels = 1;
+
+ ret = mcf54415_dac_init(info);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, mcf54415_dac_exit, info);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct platform_device_id mcf54415_dac_ids[] = {
+ { .name = "mcfdac" },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, mcf54415_dac_ids);
+
+static struct platform_driver mcf54415_dac_driver = {
+ .driver = {
+ .name = "mcf54415_dac",
+ },
+ .probe = mcf54415_dac_probe,
+ .id_table = mcf54415_dac_ids,
+};
+module_platform_driver(mcf54415_dac_driver);
+
+MODULE_AUTHOR("Angelo Dureghello <angelo@kernel-space.org>");
+MODULE_DESCRIPTION("NXP MCF54415 DAC driver");
+MODULE_LICENSE("GPL");