* [PATCH v3 1/3] iio: dac: mcp47feb02: refactor MCP47FEB02 I2C driver into two modules
2026-08-04 13:29 [PATCH v3 0/3] Refactor Microchip MCP47FEB02 I2C driver in separate modules to add support for MCP48FEB02 SPI driver Ariana Lazar
@ 2026-08-04 13:29 ` Ariana Lazar
2026-08-04 13:46 ` sashiko-bot
` (2 more replies)
2026-08-04 13:29 ` [PATCH v3 2/3] dt-bindings: iio: dac: add support for MCP48FEB02 SPI Ariana Lazar
2026-08-04 13:29 ` [PATCH v3 3/3] iio: dac: add support for Microchip MCP48FEB02 Ariana Lazar
2 siblings, 3 replies; 8+ messages in thread
From: Ariana Lazar @ 2026-08-04 13:29 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-kernel, linux-iio, devicetree, Ariana Lazar
Prepare the driver for the bus-specific code by refactoring into separate
files. The renamed file will contain the common DAC functionality shared by
the MCP47FxBy1/2/4/8 I2C and MCP48FxBy1/2/4/8 SPI drivers. The MCP47FEB02
driver was refactored into two modules: mcp47feb02-core.c and
mcp47feb02-i2c.c in order to prepare the support for SPI MCP48FxBy1/2/4/8
DAC family on top of the current implementation.
Signed-off-by: Ariana Lazar <ariana.lazar@microchip.com>
---
MAINTAINERS | 4 +-
drivers/iio/dac/Kconfig | 10 +-
drivers/iio/dac/Makefile | 3 +-
.../iio/dac/{mcp47feb02.c => mcp47feb02-core.c} | 336 ++-------------------
drivers/iio/dac/mcp47feb02-i2c.c | 144 +++++++++
drivers/iio/dac/mcp47feb02.h | 48 +++
6 files changed, 233 insertions(+), 312 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index 6b4560681b51cfabf0b0e98fd89b69d8c527228b..fc87609865f26a9f484edc42058f7df659a0fa4f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16118,7 +16118,9 @@ M: Ariana Lazar <ariana.lazar@microchip.com>
L: linux-iio@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml
-F: drivers/iio/dac/mcp47feb02.c
+F: drivers/iio/dac/mcp47feb02-core.c
+F: drivers/iio/dac/mcp47feb02-i2c.c
+F: drivers/iio/dac/mcp47feb02.h
MCP4821 DAC DRIVER
M: Anshul Dalal <anshulusr@gmail.com>
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index 657c68e75542e47ad5603165bcbc3660e90c6450..cc10d43ffd1e89bbf945ef89ea5af0abe1a91084 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -550,8 +550,13 @@ config MCP4728
will be called mcp4728.
config MCP47FEB02
- tristate "MCP47F(E/V)B01/02/04/08/11/12/14/18/21/22/24/28 DAC driver"
+ tristate
+
+config MCP47FEB02_I2C
+ tristate "MCP47F(E/V)B01/02/04/08/11/12/14/18/21/22/24/28 I2C DAC driver"
depends on I2C
+ select REGMAP_I2C
+ select MCP47FEB02
help
Say yes here if you want to build the driver for the Microchip:
- 8-bit DAC:
@@ -567,7 +572,8 @@ config MCP47FEB02
(DAC) with I2C interface.
To compile this driver as a module, choose M here: the module
- will be called mcp47feb02.
+ will be called mcp47feb02_i2c and you will also get
+ mcp47feb02_core for the core module.
config MCP4821
tristate "MCP4801/02/11/12/21/22 DAC driver"
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 0034317984985035f7987a744899924bfd4612e3..28c8646a987cff1ac4d693fe86898d66c901d5bd 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -54,7 +54,8 @@ obj-$(CONFIG_MAX5522) += max5522.o
obj-$(CONFIG_MAX5821) += max5821.o
obj-$(CONFIG_MCP4725) += mcp4725.o
obj-$(CONFIG_MCP4728) += mcp4728.o
-obj-$(CONFIG_MCP47FEB02) += mcp47feb02.o
+obj-$(CONFIG_MCP47FEB02) += mcp47feb02-core.o
+obj-$(CONFIG_MCP47FEB02_I2C) += mcp47feb02-i2c.o
obj-$(CONFIG_MCP4821) += mcp4821.o
obj-$(CONFIG_MCP4922) += mcp4922.o
obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o
diff --git a/drivers/iio/dac/mcp47feb02.c b/drivers/iio/dac/mcp47feb02-core.c
similarity index 72%
rename from drivers/iio/dac/mcp47feb02.c
rename to drivers/iio/dac/mcp47feb02-core.c
index a823c2a673a26d70e5829cb587034da435af0451..e686c511d73f2be978f3a77af7a4004194e5d284 100644
--- a/drivers/iio/dac/mcp47feb02.c
+++ b/drivers/iio/dac/mcp47feb02-core.c
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * IIO driver for MCP47FEB02 Multi-Channel DAC with I2C interface
+ * IIO driver for MCP47FEB02 Multi-Channel DAC with I2C and SPI interface
*
- * Copyright (C) 2025 Microchip Technology Inc. and its subsidiaries
+ * Copyright (C) 2025-2026 Microchip Technology Inc. and its subsidiaries
*
* Author: Ariana Lazar <ariana.lazar@microchip.com>
*
@@ -15,13 +15,12 @@
#include <linux/bits.h>
#include <linux/bitfield.h>
#include <linux/delay.h>
+#include <linux/dev_printk.h>
#include <linux/err.h>
-#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/kstrtox.h>
#include <linux/module.h>
-#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include <linux/property.h>
#include <linux/regmap.h>
@@ -30,11 +29,13 @@
#include <linux/types.h>
#include <linux/units.h>
+#include "mcp47feb02.h"
+
/* Register addresses must be left shifted with 3 positions in order to append command mask */
#define MCP47FEB02_DAC0_REG_ADDR 0x00
#define MCP47FEB02_VREF_REG_ADDR 0x40
#define MCP47FEB02_POWER_DOWN_REG_ADDR 0x48
-#define MCP47FEB02_DAC_CTRL_MASK GENMASK(1, 0)
+#define MCP47FEB02_CMD_MASK GENMASK(1, 0)
#define MCP47FEB02_GAIN_CTRL_STATUS_REG_ADDR 0x50
#define MCP47FEB02_GAIN_BIT_MASK BIT(0)
@@ -53,7 +54,7 @@
#define DAC_CTRL_MASK(ch) (GENMASK(1, 0) << (2 * (ch)))
#define DAC_CTRL_VAL(ch, val) ((val) << (2 * (ch)))
-/* Gain Control and I2C Slave Address Reguster fields */
+/* Gain Control and I2C Slave Address Register fields */
#define DAC_GAIN_MASK(ch) (BIT(0) << (8 + (ch)))
#define DAC_GAIN_VAL(ch, val) ((val) << (8 + (ch)))
@@ -86,220 +87,12 @@ enum mcp47feb02_gain_bit_mode {
MCP47FEB02_GAIN_BIT_X2 = 1,
};
-static const char * const mcp47feb02_powerdown_modes[] = {
+const char * const mcp47feb02_powerdown_modes[] = {
"1kohm_to_gnd",
"100kohm_to_gnd",
"open_circuit",
};
-/**
- * struct mcp47feb02_features - chip specific data
- * @name: device name
- * @phys_channels: number of hardware channels
- * @resolution: DAC resolution
- * @have_ext_vref1: does the hardware have an the second external voltage reference?
- * @have_eeprom: does the hardware have an internal eeprom?
- */
-struct mcp47feb02_features {
- const char *name;
- unsigned int phys_channels;
- unsigned int resolution;
- bool have_ext_vref1;
- bool have_eeprom;
-};
-
-static const struct mcp47feb02_features mcp47feb01_chip_features = {
- .name = "mcp47feb01",
- .phys_channels = 1,
- .resolution = 8,
- .have_ext_vref1 = false,
- .have_eeprom = true,
-};
-
-static const struct mcp47feb02_features mcp47feb02_chip_features = {
- .name = "mcp47feb02",
- .phys_channels = 2,
- .resolution = 8,
- .have_ext_vref1 = false,
- .have_eeprom = true,
-};
-
-static const struct mcp47feb02_features mcp47feb04_chip_features = {
- .name = "mcp47feb04",
- .phys_channels = 4,
- .resolution = 8,
- .have_ext_vref1 = true,
- .have_eeprom = true,
-};
-
-static const struct mcp47feb02_features mcp47feb08_chip_features = {
- .name = "mcp47feb08",
- .phys_channels = 8,
- .resolution = 8,
- .have_ext_vref1 = true,
- .have_eeprom = true,
-};
-
-static const struct mcp47feb02_features mcp47feb11_chip_features = {
- .name = "mcp47feb11",
- .phys_channels = 1,
- .resolution = 10,
- .have_ext_vref1 = false,
- .have_eeprom = true,
-};
-
-static const struct mcp47feb02_features mcp47feb12_chip_features = {
- .name = "mcp47feb12",
- .phys_channels = 2,
- .resolution = 10,
- .have_ext_vref1 = false,
- .have_eeprom = true,
-};
-
-static const struct mcp47feb02_features mcp47feb14_chip_features = {
- .name = "mcp47feb14",
- .phys_channels = 4,
- .resolution = 10,
- .have_ext_vref1 = true,
- .have_eeprom = true,
-};
-
-static const struct mcp47feb02_features mcp47feb18_chip_features = {
- .name = "mcp47feb18",
- .phys_channels = 8,
- .resolution = 10,
- .have_ext_vref1 = true,
- .have_eeprom = true,
-};
-
-static const struct mcp47feb02_features mcp47feb21_chip_features = {
- .name = "mcp47feb21",
- .phys_channels = 1,
- .resolution = 12,
- .have_ext_vref1 = false,
- .have_eeprom = true,
-};
-
-static const struct mcp47feb02_features mcp47feb22_chip_features = {
- .name = "mcp47feb22",
- .phys_channels = 2,
- .resolution = 12,
- .have_ext_vref1 = false,
- .have_eeprom = true,
-};
-
-static const struct mcp47feb02_features mcp47feb24_chip_features = {
- .name = "mcp47feb24",
- .phys_channels = 4,
- .resolution = 12,
- .have_ext_vref1 = true,
- .have_eeprom = true,
-};
-
-static const struct mcp47feb02_features mcp47feb28_chip_features = {
- .name = "mcp47feb28",
- .phys_channels = 8,
- .resolution = 12,
- .have_ext_vref1 = true,
- .have_eeprom = true,
-};
-
-static const struct mcp47feb02_features mcp47fvb01_chip_features = {
- .name = "mcp47fvb01",
- .phys_channels = 1,
- .resolution = 8,
- .have_ext_vref1 = false,
- .have_eeprom = false,
-};
-
-static const struct mcp47feb02_features mcp47fvb02_chip_features = {
- .name = "mcp47fvb02",
- .phys_channels = 2,
- .resolution = 8,
- .have_ext_vref1 = false,
- .have_eeprom = false,
-};
-
-static const struct mcp47feb02_features mcp47fvb04_chip_features = {
- .name = "mcp47fvb04",
- .phys_channels = 4,
- .resolution = 8,
- .have_ext_vref1 = true,
- .have_eeprom = false,
-};
-
-static const struct mcp47feb02_features mcp47fvb08_chip_features = {
- .name = "mcp47fvb08",
- .phys_channels = 8,
- .resolution = 8,
- .have_ext_vref1 = true,
- .have_eeprom = false,
-};
-
-static const struct mcp47feb02_features mcp47fvb11_chip_features = {
- .name = "mcp47fvb11",
- .phys_channels = 1,
- .resolution = 10,
- .have_ext_vref1 = false,
- .have_eeprom = false,
-};
-
-static const struct mcp47feb02_features mcp47fvb12_chip_features = {
- .name = "mcp47fvb12",
- .phys_channels = 2,
- .resolution = 10,
- .have_ext_vref1 = false,
- .have_eeprom = false,
-};
-
-static const struct mcp47feb02_features mcp47fvb14_chip_features = {
- .name = "mcp47fvb14",
- .phys_channels = 4,
- .resolution = 10,
- .have_ext_vref1 = true,
- .have_eeprom = false,
-};
-
-static const struct mcp47feb02_features mcp47fvb18_chip_features = {
- .name = "mcp47fvb18",
- .phys_channels = 8,
- .resolution = 10,
- .have_ext_vref1 = true,
- .have_eeprom = false,
-};
-
-static const struct mcp47feb02_features mcp47fvb21_chip_features = {
- .name = "mcp47fvb21",
- .phys_channels = 1,
- .resolution = 12,
- .have_ext_vref1 = false,
- .have_eeprom = false,
-};
-
-static const struct mcp47feb02_features mcp47fvb22_chip_features = {
- .name = "mcp47fvb22",
- .phys_channels = 2,
- .resolution = 12,
- .have_ext_vref1 = false,
- .have_eeprom = false,
-};
-
-static const struct mcp47feb02_features mcp47fvb24_chip_features = {
- .name = "mcp47fvb24",
- .phys_channels = 4,
- .resolution = 12,
- .have_ext_vref1 = true,
- .have_eeprom = false,
-};
-
-static const struct mcp47feb02_features mcp47fvb28_chip_features = {
- .name = "mcp47fvb28",
- .phys_channels = 8,
- .resolution = 12,
- .have_ext_vref1 = true,
- .have_eeprom = false,
-};
-
/**
* struct mcp47feb02_channel_data - channel configuration
* @ref_mode: chosen voltage for reference
@@ -380,7 +173,7 @@ static const struct regmap_access_table mcp47feb02_volatile_table = {
.n_yes_ranges = ARRAY_SIZE(mcp47feb02_volatile_ranges),
};
-static const struct regmap_config mcp47feb02_regmap_config = {
+const struct regmap_config mcp47feb02_regmap_config = {
.name = "mcp47feb02_regmap",
.reg_bits = 8,
.val_bits = 16,
@@ -392,6 +185,7 @@ static const struct regmap_config mcp47feb02_regmap_config = {
.cache_type = REGCACHE_MAPLE,
.val_format_endian = REGMAP_ENDIAN_BIG,
};
+EXPORT_SYMBOL_NS_GPL(mcp47feb02_regmap_config, "IIO_MCP47FEB02");
/* For devices that doesn't have nonvolatile memory */
static const struct regmap_range mcp47fvb02_readable_ranges[] = {
@@ -422,7 +216,7 @@ static const struct regmap_access_table mcp47fvb02_volatile_table = {
.n_yes_ranges = ARRAY_SIZE(mcp47fvb02_volatile_ranges),
};
-static const struct regmap_config mcp47fvb02_regmap_config = {
+const struct regmap_config mcp47fvb02_regmap_config = {
.name = "mcp47fvb02_regmap",
.reg_bits = 8,
.val_bits = 16,
@@ -434,6 +228,7 @@ static const struct regmap_config mcp47fvb02_regmap_config = {
.cache_type = REGCACHE_MAPLE,
.val_format_endian = REGMAP_ENDIAN_BIG,
};
+EXPORT_SYMBOL_NS_GPL(mcp47fvb02_regmap_config, "IIO_MCP47FEB02");
static int mcp47feb02_write_to_eeprom(struct mcp47feb02_data *data, unsigned int reg,
unsigned int val)
@@ -444,8 +239,6 @@ static int mcp47feb02_write_to_eeprom(struct mcp47feb02_data *data, unsigned int
* Wait until the currently occurring EEPROM Write Cycle is completed.
* Only serial commands to the volatile memory are allowed.
*/
- guard(mutex)(&data->lock);
-
ret = regmap_read_poll_timeout(data->regmap, MCP47FEB02_GAIN_CTRL_STATUS_REG_ADDR,
eewa_val,
!(eewa_val & MCP47FEB02_GAIN_BIT_STATUS_EEWA_MASK),
@@ -471,6 +264,8 @@ static ssize_t store_eeprom_store(struct device *dev, struct device_attribute *a
if (!state)
return 0;
+ guard(mutex)(&data->lock);
+
/*
* Verify DAC Wiper and DAC Configuration are unlocked. If both are disabled,
* writing to EEPROM is available.
@@ -577,18 +372,17 @@ static int mcp47feb02_resume(struct device *dev)
guard(mutex)(&data->lock);
for_each_set_bit(ch, &data->active_channels_mask, data->phys_channels) {
- u8 pd_mode;
int ret;
data->chdata[ch].powerdown = false;
- pd_mode = data->chdata[ch].powerdown_mode + 1;
ret = regmap_write(data->regmap, REG_ADDR(ch), data->chdata[ch].dac_data);
if (ret)
return ret;
ret = regmap_update_bits(data->regmap, MCP47FEB02_VREF_REG_ADDR,
- DAC_CTRL_MASK(ch), DAC_CTRL_VAL(ch, pd_mode));
+ DAC_CTRL_MASK(ch),
+ DAC_CTRL_VAL(ch, data->chdata[ch].ref_mode));
if (ret)
return ret;
@@ -666,7 +460,7 @@ static ssize_t mcp47feb02_write_powerdown(struct iio_dev *indio_dev, uintptr_t p
return len;
}
-static DEFINE_SIMPLE_DEV_PM_OPS(mcp47feb02_pm_ops, mcp47feb02_suspend, mcp47feb02_resume);
+EXPORT_SIMPLE_DEV_PM_OPS(mcp47feb02_pm_ops, mcp47feb02_suspend, mcp47feb02_resume);
static const struct iio_enum mcp47febxx_powerdown_mode_enum = {
.items = mcp47feb02_powerdown_modes,
@@ -1022,7 +816,7 @@ static int mcp47feb02_init_ctrl_regs(struct mcp47feb02_data *data)
struct device *dev = regmap_get_device(data->regmap);
unsigned int pd_tmp;
- data->chdata[i].ref_mode = (vref_ch >> (2 * i)) & MCP47FEB02_DAC_CTRL_MASK;
+ data->chdata[i].ref_mode = (vref_ch >> (2 * i)) & MCP47FEB02_CMD_MASK;
data->chdata[i].use_2x_gain = (gain_ch >> i) & MCP47FEB02_GAIN_BIT_MASK;
/*
@@ -1066,7 +860,7 @@ static int mcp47feb02_init_ctrl_regs(struct mcp47feb02_data *data)
break;
}
- pd_tmp = (pd_ch >> (2 * i)) & MCP47FEB02_DAC_CTRL_MASK;
+ pd_tmp = (pd_ch >> (2 * i)) & MCP47FEB02_CMD_MASK;
data->chdata[i].powerdown_mode = pd_tmp ? (pd_tmp - 1) : pd_tmp;
data->chdata[i].powerdown = !!(data->chdata[i].powerdown_mode);
}
@@ -1091,10 +885,10 @@ static int mcp47feb02_init_ch_scales(struct mcp47feb02_data *data, int vdd_uV,
return 0;
}
-static int mcp47feb02_probe(struct i2c_client *client)
+int mcp47feb02_common_probe(const struct mcp47feb02_features *chip_features,
+ struct regmap *regmap)
{
- const struct mcp47feb02_features *chip_features;
- struct device *dev = &client->dev;
+ struct device *dev = regmap_get_device(regmap);
struct mcp47feb02_data *data;
struct iio_dev *indio_dev;
int vref1_uV, vref_uV, vdd_uV, ret;
@@ -1103,22 +897,16 @@ static int mcp47feb02_probe(struct i2c_client *client)
if (!indio_dev)
return -ENOMEM;
- data = iio_priv(indio_dev);
- chip_features = i2c_get_match_data(client);
- if (!chip_features)
- return -EINVAL;
+ dev_set_drvdata(dev, indio_dev);
+ data = iio_priv(indio_dev);
data->chip_features = chip_features;
+ data->regmap = regmap;
- if (chip_features->have_eeprom) {
- data->regmap = devm_regmap_init_i2c(client, &mcp47feb02_regmap_config);
+ if (chip_features->have_eeprom)
indio_dev->info = &mcp47feb02_info;
- } else {
- data->regmap = devm_regmap_init_i2c(client, &mcp47fvb02_regmap_config);
+ else
indio_dev->info = &mcp47fvb02_info;
- }
- if (IS_ERR(data->regmap))
- return dev_err_probe(dev, PTR_ERR(data->regmap), "Error initializing i2c regmap\n");
indio_dev->name = chip_features->name;
@@ -1175,75 +963,7 @@ static int mcp47feb02_probe(struct i2c_client *client)
return devm_iio_device_register(dev, indio_dev);
}
-
-static const struct i2c_device_id mcp47feb02_id[] = {
- { .name = "mcp47feb01", .driver_data = (kernel_ulong_t)&mcp47feb01_chip_features },
- { .name = "mcp47feb02", .driver_data = (kernel_ulong_t)&mcp47feb02_chip_features },
- { .name = "mcp47feb04", .driver_data = (kernel_ulong_t)&mcp47feb04_chip_features },
- { .name = "mcp47feb08", .driver_data = (kernel_ulong_t)&mcp47feb08_chip_features },
- { .name = "mcp47feb11", .driver_data = (kernel_ulong_t)&mcp47feb11_chip_features },
- { .name = "mcp47feb12", .driver_data = (kernel_ulong_t)&mcp47feb12_chip_features },
- { .name = "mcp47feb14", .driver_data = (kernel_ulong_t)&mcp47feb14_chip_features },
- { .name = "mcp47feb18", .driver_data = (kernel_ulong_t)&mcp47feb18_chip_features },
- { .name = "mcp47feb21", .driver_data = (kernel_ulong_t)&mcp47feb21_chip_features },
- { .name = "mcp47feb22", .driver_data = (kernel_ulong_t)&mcp47feb22_chip_features },
- { .name = "mcp47feb24", .driver_data = (kernel_ulong_t)&mcp47feb24_chip_features },
- { .name = "mcp47feb28", .driver_data = (kernel_ulong_t)&mcp47feb28_chip_features },
- { .name = "mcp47fvb01", .driver_data = (kernel_ulong_t)&mcp47fvb01_chip_features },
- { .name = "mcp47fvb02", .driver_data = (kernel_ulong_t)&mcp47fvb02_chip_features },
- { .name = "mcp47fvb04", .driver_data = (kernel_ulong_t)&mcp47fvb04_chip_features },
- { .name = "mcp47fvb08", .driver_data = (kernel_ulong_t)&mcp47fvb08_chip_features },
- { .name = "mcp47fvb11", .driver_data = (kernel_ulong_t)&mcp47fvb11_chip_features },
- { .name = "mcp47fvb12", .driver_data = (kernel_ulong_t)&mcp47fvb12_chip_features },
- { .name = "mcp47fvb14", .driver_data = (kernel_ulong_t)&mcp47fvb14_chip_features },
- { .name = "mcp47fvb18", .driver_data = (kernel_ulong_t)&mcp47fvb18_chip_features },
- { .name = "mcp47fvb21", .driver_data = (kernel_ulong_t)&mcp47fvb21_chip_features },
- { .name = "mcp47fvb22", .driver_data = (kernel_ulong_t)&mcp47fvb22_chip_features },
- { .name = "mcp47fvb24", .driver_data = (kernel_ulong_t)&mcp47fvb24_chip_features },
- { .name = "mcp47fvb28", .driver_data = (kernel_ulong_t)&mcp47fvb28_chip_features },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, mcp47feb02_id);
-
-static const struct of_device_id mcp47feb02_of_match[] = {
- { .compatible = "microchip,mcp47feb01", .data = &mcp47feb01_chip_features },
- { .compatible = "microchip,mcp47feb02", .data = &mcp47feb02_chip_features },
- { .compatible = "microchip,mcp47feb04", .data = &mcp47feb04_chip_features },
- { .compatible = "microchip,mcp47feb08", .data = &mcp47feb08_chip_features },
- { .compatible = "microchip,mcp47feb11", .data = &mcp47feb11_chip_features },
- { .compatible = "microchip,mcp47feb12", .data = &mcp47feb12_chip_features },
- { .compatible = "microchip,mcp47feb14", .data = &mcp47feb14_chip_features },
- { .compatible = "microchip,mcp47feb18", .data = &mcp47feb18_chip_features },
- { .compatible = "microchip,mcp47feb21", .data = &mcp47feb21_chip_features },
- { .compatible = "microchip,mcp47feb22", .data = &mcp47feb22_chip_features },
- { .compatible = "microchip,mcp47feb24", .data = &mcp47feb24_chip_features },
- { .compatible = "microchip,mcp47feb28", .data = &mcp47feb28_chip_features },
- { .compatible = "microchip,mcp47fvb01", .data = &mcp47fvb01_chip_features },
- { .compatible = "microchip,mcp47fvb02", .data = &mcp47fvb02_chip_features },
- { .compatible = "microchip,mcp47fvb04", .data = &mcp47fvb04_chip_features },
- { .compatible = "microchip,mcp47fvb08", .data = &mcp47fvb08_chip_features },
- { .compatible = "microchip,mcp47fvb11", .data = &mcp47fvb11_chip_features },
- { .compatible = "microchip,mcp47fvb12", .data = &mcp47fvb12_chip_features },
- { .compatible = "microchip,mcp47fvb14", .data = &mcp47fvb14_chip_features },
- { .compatible = "microchip,mcp47fvb18", .data = &mcp47fvb18_chip_features },
- { .compatible = "microchip,mcp47fvb21", .data = &mcp47fvb21_chip_features },
- { .compatible = "microchip,mcp47fvb22", .data = &mcp47fvb22_chip_features },
- { .compatible = "microchip,mcp47fvb24", .data = &mcp47fvb24_chip_features },
- { .compatible = "microchip,mcp47fvb28", .data = &mcp47fvb28_chip_features },
- { }
-};
-MODULE_DEVICE_TABLE(of, mcp47feb02_of_match);
-
-static struct i2c_driver mcp47feb02_driver = {
- .driver = {
- .name = "mcp47feb02",
- .of_match_table = mcp47feb02_of_match,
- .pm = pm_sleep_ptr(&mcp47feb02_pm_ops),
- },
- .probe = mcp47feb02_probe,
- .id_table = mcp47feb02_id,
-};
-module_i2c_driver(mcp47feb02_driver);
+EXPORT_SYMBOL_NS(mcp47feb02_common_probe, "IIO_MCP47FEB02");
MODULE_AUTHOR("Ariana Lazar <ariana.lazar@microchip.com>");
MODULE_DESCRIPTION("IIO driver for MCP47FEB02 Multi-Channel DAC with I2C interface");
diff --git a/drivers/iio/dac/mcp47feb02-i2c.c b/drivers/iio/dac/mcp47feb02-i2c.c
new file mode 100644
index 0000000000000000000000000000000000000000..5b942dbee8517244a33e3dc43b7b88d10b5342b8
--- /dev/null
+++ b/drivers/iio/dac/mcp47feb02-i2c.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * IIO driver for MCP47FEB02 Multi-Channel DAC with I2C interface
+ *
+ * Copyright (C) 2025-2026 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Ariana Lazar <ariana.lazar@microchip.com>
+ *
+ * Datasheet links for devices with I2C interface:
+ * [MCP47FEBxx] https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005375A.pdf
+ * [MCP47FVBxx] https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005405A.pdf
+ * [MCP47FxBx4/8] https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP47FXBX48-Data-Sheet-DS200006368A.pdf
+ */
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+
+#include "mcp47feb02.h"
+
+/* Parts with EEPROM memory */
+MCP47FEB02_CHIP_INFO(mcp47feb01, 1, 8, false, true);
+MCP47FEB02_CHIP_INFO(mcp47feb02, 2, 8, false, true);
+MCP47FEB02_CHIP_INFO(mcp47feb04, 4, 8, true, true);
+MCP47FEB02_CHIP_INFO(mcp47feb08, 8, 8, true, true);
+MCP47FEB02_CHIP_INFO(mcp47feb11, 1, 10, false, true);
+MCP47FEB02_CHIP_INFO(mcp47feb12, 2, 10, false, true);
+MCP47FEB02_CHIP_INFO(mcp47feb14, 4, 10, true, true);
+MCP47FEB02_CHIP_INFO(mcp47feb18, 8, 10, true, true);
+MCP47FEB02_CHIP_INFO(mcp47feb21, 1, 12, false, true);
+MCP47FEB02_CHIP_INFO(mcp47feb22, 2, 12, false, true);
+MCP47FEB02_CHIP_INFO(mcp47feb24, 4, 12, true, true);
+MCP47FEB02_CHIP_INFO(mcp47feb28, 8, 12, true, true);
+
+/* Parts without EEPROM memory */
+MCP47FEB02_CHIP_INFO(mcp47fvb01, 1, 8, false, false);
+MCP47FEB02_CHIP_INFO(mcp47fvb02, 2, 8, false, false);
+MCP47FEB02_CHIP_INFO(mcp47fvb04, 4, 8, true, false);
+MCP47FEB02_CHIP_INFO(mcp47fvb08, 8, 8, true, false);
+MCP47FEB02_CHIP_INFO(mcp47fvb11, 1, 10, false, false);
+MCP47FEB02_CHIP_INFO(mcp47fvb12, 2, 10, false, false);
+MCP47FEB02_CHIP_INFO(mcp47fvb14, 4, 10, true, false);
+MCP47FEB02_CHIP_INFO(mcp47fvb18, 8, 10, true, false);
+MCP47FEB02_CHIP_INFO(mcp47fvb21, 1, 12, false, false);
+MCP47FEB02_CHIP_INFO(mcp47fvb22, 2, 12, false, false);
+MCP47FEB02_CHIP_INFO(mcp47fvb24, 4, 12, true, false);
+MCP47FEB02_CHIP_INFO(mcp47fvb28, 8, 12, true, false);
+
+static int mcp47feb02_i2c_probe(struct i2c_client *client)
+{
+ const struct mcp47feb02_features *chip_features;
+ struct device *dev = &client->dev;
+ struct regmap *regmap;
+
+ chip_features = i2c_get_match_data(client);
+ if (!chip_features)
+ return dev_err_probe(dev, -ENODEV, "No I2C device found\n");
+
+ if (chip_features->have_eeprom)
+ regmap = devm_regmap_init_i2c(client, &mcp47feb02_regmap_config);
+ else
+ regmap = devm_regmap_init_i2c(client, &mcp47fvb02_regmap_config);
+
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "Error initializing I2C regmap\n");
+
+ return mcp47feb02_common_probe(chip_features, regmap);
+}
+
+static const struct i2c_device_id mcp47feb02_i2c_id[] = {
+ { .name = "mcp47feb01", .driver_data = (kernel_ulong_t)&mcp47feb01_chip_features },
+ { .name = "mcp47feb02", .driver_data = (kernel_ulong_t)&mcp47feb02_chip_features },
+ { .name = "mcp47feb04", .driver_data = (kernel_ulong_t)&mcp47feb04_chip_features },
+ { .name = "mcp47feb08", .driver_data = (kernel_ulong_t)&mcp47feb08_chip_features },
+ { .name = "mcp47feb11", .driver_data = (kernel_ulong_t)&mcp47feb11_chip_features },
+ { .name = "mcp47feb12", .driver_data = (kernel_ulong_t)&mcp47feb12_chip_features },
+ { .name = "mcp47feb14", .driver_data = (kernel_ulong_t)&mcp47feb14_chip_features },
+ { .name = "mcp47feb18", .driver_data = (kernel_ulong_t)&mcp47feb18_chip_features },
+ { .name = "mcp47feb21", .driver_data = (kernel_ulong_t)&mcp47feb21_chip_features },
+ { .name = "mcp47feb22", .driver_data = (kernel_ulong_t)&mcp47feb22_chip_features },
+ { .name = "mcp47feb24", .driver_data = (kernel_ulong_t)&mcp47feb24_chip_features },
+ { .name = "mcp47feb28", .driver_data = (kernel_ulong_t)&mcp47feb28_chip_features },
+ { .name = "mcp47fvb01", .driver_data = (kernel_ulong_t)&mcp47fvb01_chip_features },
+ { .name = "mcp47fvb02", .driver_data = (kernel_ulong_t)&mcp47fvb02_chip_features },
+ { .name = "mcp47fvb04", .driver_data = (kernel_ulong_t)&mcp47fvb04_chip_features },
+ { .name = "mcp47fvb08", .driver_data = (kernel_ulong_t)&mcp47fvb08_chip_features },
+ { .name = "mcp47fvb11", .driver_data = (kernel_ulong_t)&mcp47fvb11_chip_features },
+ { .name = "mcp47fvb12", .driver_data = (kernel_ulong_t)&mcp47fvb12_chip_features },
+ { .name = "mcp47fvb14", .driver_data = (kernel_ulong_t)&mcp47fvb14_chip_features },
+ { .name = "mcp47fvb18", .driver_data = (kernel_ulong_t)&mcp47fvb18_chip_features },
+ { .name = "mcp47fvb21", .driver_data = (kernel_ulong_t)&mcp47fvb21_chip_features },
+ { .name = "mcp47fvb22", .driver_data = (kernel_ulong_t)&mcp47fvb22_chip_features },
+ { .name = "mcp47fvb24", .driver_data = (kernel_ulong_t)&mcp47fvb24_chip_features },
+ { .name = "mcp47fvb28", .driver_data = (kernel_ulong_t)&mcp47fvb28_chip_features },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mcp47feb02_i2c_id);
+
+static const struct of_device_id mcp47feb02_of_i2c_match[] = {
+ { .compatible = "microchip,mcp47feb01", .data = &mcp47feb01_chip_features },
+ { .compatible = "microchip,mcp47feb02", .data = &mcp47feb02_chip_features },
+ { .compatible = "microchip,mcp47feb04", .data = &mcp47feb04_chip_features },
+ { .compatible = "microchip,mcp47feb08", .data = &mcp47feb08_chip_features },
+ { .compatible = "microchip,mcp47feb11", .data = &mcp47feb11_chip_features },
+ { .compatible = "microchip,mcp47feb12", .data = &mcp47feb12_chip_features },
+ { .compatible = "microchip,mcp47feb14", .data = &mcp47feb14_chip_features },
+ { .compatible = "microchip,mcp47feb18", .data = &mcp47feb18_chip_features },
+ { .compatible = "microchip,mcp47feb21", .data = &mcp47feb21_chip_features },
+ { .compatible = "microchip,mcp47feb22", .data = &mcp47feb22_chip_features },
+ { .compatible = "microchip,mcp47feb24", .data = &mcp47feb24_chip_features },
+ { .compatible = "microchip,mcp47feb28", .data = &mcp47feb28_chip_features },
+ { .compatible = "microchip,mcp47fvb01", .data = &mcp47fvb01_chip_features },
+ { .compatible = "microchip,mcp47fvb02", .data = &mcp47fvb02_chip_features },
+ { .compatible = "microchip,mcp47fvb04", .data = &mcp47fvb04_chip_features },
+ { .compatible = "microchip,mcp47fvb08", .data = &mcp47fvb08_chip_features },
+ { .compatible = "microchip,mcp47fvb11", .data = &mcp47fvb11_chip_features },
+ { .compatible = "microchip,mcp47fvb12", .data = &mcp47fvb12_chip_features },
+ { .compatible = "microchip,mcp47fvb14", .data = &mcp47fvb14_chip_features },
+ { .compatible = "microchip,mcp47fvb18", .data = &mcp47fvb18_chip_features },
+ { .compatible = "microchip,mcp47fvb21", .data = &mcp47fvb21_chip_features },
+ { .compatible = "microchip,mcp47fvb22", .data = &mcp47fvb22_chip_features },
+ { .compatible = "microchip,mcp47fvb24", .data = &mcp47fvb24_chip_features },
+ { .compatible = "microchip,mcp47fvb28", .data = &mcp47fvb28_chip_features },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mcp47feb02_of_i2c_match);
+
+static struct i2c_driver mcp47feb02_i2c_driver = {
+ .driver = {
+ .name = "mcp47feb02",
+ .of_match_table = mcp47feb02_of_i2c_match,
+ .pm = pm_sleep_ptr(&mcp47feb02_pm_ops),
+ },
+ .probe = mcp47feb02_i2c_probe,
+ .id_table = mcp47feb02_i2c_id,
+};
+module_i2c_driver(mcp47feb02_i2c_driver);
+
+MODULE_AUTHOR("Ariana Lazar <ariana.lazar@microchip.com>");
+MODULE_DESCRIPTION("IIO driver for MCP47FEB02 Multi-Channel DAC with I2C interface");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_MCP47FEB02");
diff --git a/drivers/iio/dac/mcp47feb02.h b/drivers/iio/dac/mcp47feb02.h
new file mode 100644
index 0000000000000000000000000000000000000000..e772ca3647e7c25731396635f5078919ab7556ae
--- /dev/null
+++ b/drivers/iio/dac/mcp47feb02.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef __DRIVERS_IIO_DAC_MCP47FEB02_H__
+#define __DRIVERS_IIO_DAC_MCP47FEB02_H__
+
+#include <linux/bits.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+
+extern const char * const mcp47feb02_powerdown_modes[];
+
+/* Macro used for generating chip features structures */
+#define MCP47FEB02_CHIP_INFO(_name, _channels, _res, _vref1, _eeprom) \
+static const struct mcp47feb02_features _name##_chip_features = { \
+ .name = #_name, \
+ .phys_channels = _channels, \
+ .resolution = _res, \
+ .have_ext_vref1 = _vref1, \
+ .have_eeprom = _eeprom, \
+}
+
+/**
+ * struct mcp47feb02_features - chip specific data
+ * @name: device name
+ * @phys_channels: number of hardware channels
+ * @resolution: DAC resolution
+ * @have_ext_vref1: does the hardware have an the second external voltage reference?
+ * @have_eeprom: does the hardware have an internal eeprom?
+ */
+struct mcp47feb02_features {
+ const char *name;
+ unsigned int phys_channels;
+ unsigned int resolution;
+ bool have_ext_vref1;
+ bool have_eeprom;
+};
+
+extern const struct regmap_config mcp47feb02_regmap_config;
+extern const struct regmap_config mcp47fvb02_regmap_config;
+
+/* Properties shared by I2C and SPI families */
+int mcp47feb02_common_probe(const struct mcp47feb02_features *chip_features, struct regmap *regmap);
+
+extern const struct dev_pm_ops mcp47feb02_pm_ops;
+
+#endif /* __DRIVERS_IIO_DAC_MCP47FEB02_H__ */
+
--
2.43.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v3 2/3] dt-bindings: iio: dac: add support for MCP48FEB02 SPI
2026-08-04 13:29 [PATCH v3 0/3] Refactor Microchip MCP47FEB02 I2C driver in separate modules to add support for MCP48FEB02 SPI driver Ariana Lazar
2026-08-04 13:29 ` [PATCH v3 1/3] iio: dac: mcp47feb02: refactor MCP47FEB02 I2C driver into two modules Ariana Lazar
@ 2026-08-04 13:29 ` Ariana Lazar
2026-08-04 13:29 ` [PATCH v3 3/3] iio: dac: add support for Microchip MCP48FEB02 Ariana Lazar
2 siblings, 0 replies; 8+ messages in thread
From: Ariana Lazar @ 2026-08-04 13:29 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-kernel, linux-iio, devicetree, Ariana Lazar
Add the SPI MCP48FxBy1/2/4/8 part numbers, the spi-max-frequency property
and a devicetree example for SPI usage to the existing binding.
Signed-off-by: Ariana Lazar <ariana.lazar@microchip.com>
---
.../bindings/iio/dac/microchip,mcp47feb02.yaml | 221 ++++++++++++++++-----
1 file changed, 173 insertions(+), 48 deletions(-)
diff --git a/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml b/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml
index d131f136bd15161666932261bcacd6d124b23ede..a86d094f0d4b54f9d9cc39fd54222f5968714668 100644
--- a/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml
+++ b/Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml
@@ -4,58 +4,92 @@
$id: http://devicetree.org/schemas/iio/dac/microchip,mcp47feb02.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: Microchip MCP47F(E/V)B(0/1/2)(1/2/4/8) DAC with I2C Interface Families
+title: Microchip MCP4(7/8)F(E/V)B(0/1/2)(1/2/4/8) DAC with I2C/SPI Interface Families
maintainers:
- Ariana Lazar <ariana.lazar@microchip.com>
description: |
- Datasheet for MCP47FEB01, MCP47FEB11, MCP47FEB21, MCP47FEB02, MCP47FEB12,
- MCP47FEB22 can be found here:
- https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005375A.pdf
- Datasheet for MCP47FVB01, MCP47FVB11, MCP47FVB21, MCP47FVB02, MCP47FVB12,
- MCP47FVB22 can be found here:
- https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005405A.pdf
- Datasheet for MCP47FEB04, MCP47FEB14, MCP47FEB24, MCP47FEB08, MCP47FEB18,
- MCP47FEB28, MCP47FVB04, MCP47FVB14, MCP47FVB24, MCP47FVB08, MCP47FVB18,
- MCP47FVB28 can be found here:
- https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP47FXBX48-Data-Sheet-DS200006368A.pdf
-
- +------------+--------------+-------------+-------------+------------+
- | Device | Resolution | Channels | Vref number | Memory |
- |------------|--------------|-------------|-------------|------------|
- | MCP47FEB01 | 8-bit | 1 | 1 | EEPROM |
- | MCP47FEB11 | 10-bit | 1 | 1 | EEPROM |
- | MCP47FEB21 | 12-bit | 1 | 1 | EEPROM |
- |------------|--------------|-------------|-------------|------------|
- | MCP47FEB02 | 8-bit | 2 | 1 | EEPROM |
- | MCP47FEB12 | 10-bit | 2 | 1 | EEPROM |
- | MCP47FEB22 | 12-bit | 2 | 1 | EEPROM |
- |------------|--------------|-------------|-------------|------------|
- | MCP47FVB01 | 8-bit | 1 | 1 | RAM |
- | MCP47FVB11 | 10-bit | 1 | 1 | RAM |
- | MCP47FVB21 | 12-bit | 1 | 1 | RAM |
- |------------|--------------|-------------|-------------|------------|
- | MCP47FVB02 | 8-bit | 2 | 1 | RAM |
- | MCP47FVB12 | 10-bit | 2 | 1 | RAM |
- | MCP47FVB22 | 12-bit | 2 | 1 | RAM |
- |------------|--------------|-------------|-------------|------------|
- | MCP47FVB04 | 8-bit | 4 | 2 | RAM |
- | MCP47FVB14 | 10-bit | 4 | 2 | RAM |
- | MCP47FVB24 | 12-bit | 4 | 2 | RAM |
- |------------|--------------|-------------|-------------|------------|
- | MCP47FVB08 | 8-bit | 8 | 2 | RAM |
- | MCP47FVB18 | 10-bit | 8 | 2 | RAM |
- | MCP47FVB28 | 12-bit | 8 | 2 | RAM |
- |------------|--------------|-------------|-------------|------------|
- | MCP47FEB04 | 8-bit | 4 | 2 | EEPROM |
- | MCP47FEB14 | 10-bit | 4 | 2 | EEPROM |
- | MCP47FEB24 | 12-bit | 4 | 2 | EEPROM |
- |------------|--------------|-------------|-------------|------------|
- | MCP47FEB08 | 8-bit | 8 | 2 | EEPROM |
- | MCP47FEB18 | 10-bit | 8 | 2 | EEPROM |
- | MCP47FEB28 | 12-bit | 8 | 2 | EEPROM |
- +------------+--------------+-------------+-------------+------------+
+ Datasheets for MCP47F(E/V)B(0/1/2)(1/2/4/8) DAC with I2C Interface Families:
+ Datasheet for MCP47FEB01, MCP47FEB02, MCP47FEB11, MCP47FEB12, MCP47FEB21,
+ MCP47FEB22 can be found here:
+ https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005375A.pdf
+ Datasheet for MCP47FVB01, MCP47FVB02, MCP47FVB11, MCP47FVB12, MCP47FVB21,
+ MCP47FVB22 can be found here:
+ https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005405A.pdf
+ Datasheet for MCP47FEB04, MCP47FEB08, MCP47FEB14, MCP47FEB18, MCP47FEB24,
+ MCP47FEB28, MCP47FVB04, MCP47FVB08, MCP47FVB14, MCP47FVB18, MCP47FVB24,
+ MCP47FVB28 can be found here:
+ https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP47FXBX48-Data-Sheet-DS200006368A.pdf
+ Datasheets for MCP48F(E/V)B(0/1/2)(1/2/4/8) DAC with SPI Interface Families:
+ Datasheet for MCP48FEB01, MCP48FEB02, MCP48FEB11, MCP48FEB12, MCP48FEB21,
+ MCP48FEB22 can be found here:
+ https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005429B.pdf
+ Datasheet for MCP48FVB01, MCP48FVB02, MCP48FVB11, MCP48FVB12, MCP48FVB21,
+ MCP48FVB22 can be found here:
+ https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005466A.pdf
+ Datasheet for MCP48FEB04, MCP48FEB14, MCP48FEB24, MCP48FEB08, MCP48FEB18,
+ MCP48FEB28, MCP48FVB04, MCP48FVB14, MCP48FVB24, MCP48FVB08, MCP48FVB18,
+ MCP48FVB28 can be found here:
+ https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP48FXBX4-8-Family-Data-Sheet-DS20006362A.pdf
+
+ +------------+--------------+-------------+-------------+------------+------------+
+ | Device | Resolution | Channels | Vref number | Memory | Interface |
+ |------------|--------------|-------------|-------------|------------|------------|
+ | MCP47FEB01 | | 1 | 1 | EEPROM | I2C |
+ | MCP47FEB02 | | 2 | 1 | EEPROM | I2C |
+ | MCP47FEB04 | | 4 | 2 | EEPROM | I2C |
+ | MCP47FEB08 | 8-bit | 8 | 2 | EEPROM | I2C |
+ | MCP48FEB01 | | 1 | 1 | EEPROM | SPI |
+ | MCP48FEB02 | | 2 | 1 | EEPROM | SPI |
+ | MCP48FEB04 | | 4 | 2 | EEPROM | SPI |
+ | MCP48FEB08 | | 8 | 2 | EEPROM | SPI |
+ |------------|--------------|-------------|-------------|------------|------------|
+ | MCP47FEB11 | | 1 | 1 | EEPROM | I2C |
+ | MCP47FEB12 | | 2 | 1 | EEPROM | I2C |
+ | MCP47FEB14 | | 4 | 2 | EEPROM | I2C |
+ | MCP47FEB18 | 10-bit | 8 | 2 | EEPROM | I2C |
+ | MCP48FEB11 | | 1 | 1 | EEPROM | SPI |
+ | MCP48FEB12 | | 2 | 1 | EEPROM | SPI |
+ | MCP48FEB14 | | 4 | 2 | EEPROM | SPI |
+ | MCP48FEB18 | | 8 | 2 | EEPROM | SPI |
+ |------------|--------------|-------------|-------------|------------|------------|
+ | MCP47FEB21 | | 1 | 1 | EEPROM | I2C |
+ | MCP47FEB22 | | 2 | 1 | EEPROM | I2C |
+ | MCP47FEB24 | | 4 | 2 | EEPROM | I2C |
+ | MCP47FEB28 | 12-bit | 8 | 2 | EEPROM | I2C |
+ | MCP48FEB21 | | 1 | 1 | EEPROM | SPI |
+ | MCP48FEB22 | | 2 | 1 | EEPROM | SPI |
+ | MCP48FEB24 | | 4 | 2 | EEPROM | SPI |
+ | MCP48FEB28 | | 8 | 2 | EEPROM | SPI |
+ |------------|--------------|-------------|-------------|------------|------------|
+ | MCP47FVB01 | | 1 | 1 | RAM | I2C |
+ | MCP47FVB02 | | 2 | 1 | RAM | I2C |
+ | MCP47FVB04 | | 4 | 2 | RAM | I2C |
+ | MCP47FVB08 | 8-bit | 8 | 2 | RAM | I2C |
+ | MCP48FVB01 | | 1 | 1 | RAM | SPI |
+ | MCP48FVB02 | | 2 | 1 | RAM | SPI |
+ | MCP48FVB04 | | 4 | 2 | RAM | SPI |
+ | MCP48FVB08 | | 8 | 2 | RAM | SPI |
+ |------------|--------------|-------------|-------------|------------|------------|
+ | MCP47FVB11 | | 1 | 1 | RAM | I2C |
+ | MCP47FVB12 | | 2 | 1 | RAM | I2C |
+ | MCP47FVB14 | | 4 | 2 | RAM | I2C |
+ | MCP47FVB18 | 10-bit | 8 | 2 | RAM | I2C |
+ | MCP48FVB11 | | 1 | 1 | RAM | SPI |
+ | MCP48FVB12 | | 2 | 1 | RAM | SPI |
+ | MCP48FVB14 | | 4 | 2 | RAM | SPI |
+ | MCP48FVB18 | | 8 | 2 | RAM | SPI |
+ |------------|--------------|-------------|-------------|------------|------------|
+ | MCP47FVB21 | | 1 | 1 | RAM | I2C |
+ | MCP47FVB22 | | 2 | 1 | RAM | I2C |
+ | MCP47FVB24 | | 4 | 2 | RAM | I2C |
+ | MCP47FVB28 | 12-bit | 8 | 2 | RAM | I2C |
+ | MCP48FVB21 | | 1 | 1 | RAM | SPI |
+ | MCP48FVB22 | | 2 | 1 | RAM | SPI |
+ | MCP48FVB24 | | 4 | 2 | RAM | SPI |
+ | MCP48FVB28 | | 8 | 2 | RAM | SPI |
+ +------------+--------------+-------------+-------------+------------+------------+
properties:
compatible:
@@ -84,10 +118,37 @@ properties:
- microchip,mcp47feb08
- microchip,mcp47feb18
- microchip,mcp47feb28
+ - microchip,mcp48feb01
+ - microchip,mcp48feb02
+ - microchip,mcp48feb04
+ - microchip,mcp48feb08
+ - microchip,mcp48feb11
+ - microchip,mcp48feb12
+ - microchip,mcp48feb14
+ - microchip,mcp48feb18
+ - microchip,mcp48feb21
+ - microchip,mcp48feb22
+ - microchip,mcp48feb24
+ - microchip,mcp48feb28
+ - microchip,mcp48fvb01
+ - microchip,mcp48fvb02
+ - microchip,mcp48fvb04
+ - microchip,mcp48fvb08
+ - microchip,mcp48fvb11
+ - microchip,mcp48fvb12
+ - microchip,mcp48fvb14
+ - microchip,mcp48fvb18
+ - microchip,mcp48fvb21
+ - microchip,mcp48fvb22
+ - microchip,mcp48fvb24
+ - microchip,mcp48fvb28
reg:
maxItems: 1
+ spi-max-frequency:
+ maximum: 10000000
+
"#address-cells":
const: 1
@@ -178,6 +239,21 @@ required:
- vdd-supply
allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ pattern: "^microchip,mcp48f[ev]b[0-2][1248]$"
+ then:
+ $ref: /schemas/spi/spi-peripheral-props.yaml#
+ dependencies:
+ spi-cpha: [ spi-cpol ]
+ spi-cpol: [ spi-cpha ]
+ else:
+ properties:
+ spi-max-frequency: false
+ spi-cpha: false
+ spi-cpol: false
- if:
properties:
compatible:
@@ -189,6 +265,12 @@ allOf:
- microchip,mcp47fvb01
- microchip,mcp47fvb11
- microchip,mcp47fvb21
+ - microchip,mcp48feb01
+ - microchip,mcp48feb11
+ - microchip,mcp48feb21
+ - microchip,mcp48fvb01
+ - microchip,mcp48fvb11
+ - microchip,mcp48fvb21
then:
properties:
lat1-gpios: false
@@ -211,6 +293,12 @@ allOf:
- microchip,mcp47fvb02
- microchip,mcp47fvb12
- microchip,mcp47fvb22
+ - microchip,mcp48feb02
+ - microchip,mcp48feb12
+ - microchip,mcp48feb22
+ - microchip,mcp48fvb02
+ - microchip,mcp48fvb12
+ - microchip,mcp48fvb22
then:
properties:
lat1-gpios: false
@@ -233,6 +321,12 @@ allOf:
- microchip,mcp47feb04
- microchip,mcp47feb14
- microchip,mcp47feb24
+ - microchip,mcp48feb04
+ - microchip,mcp48feb14
+ - microchip,mcp48feb24
+ - microchip,mcp48fvb04
+ - microchip,mcp48fvb14
+ - microchip,mcp48fvb24
then:
patternProperties:
"^channel@[0-3]$":
@@ -251,6 +345,12 @@ allOf:
- microchip,mcp47feb08
- microchip,mcp47feb18
- microchip,mcp47feb28
+ - microchip,mcp48feb08
+ - microchip,mcp48feb18
+ - microchip,mcp48feb28
+ - microchip,mcp48fvb08
+ - microchip,mcp48fvb18
+ - microchip,mcp48fvb28
then:
patternProperties:
"^channel@[0-7]$":
@@ -272,7 +372,7 @@ allOf:
properties:
microchip,vref1-buffered: false
-additionalProperties: false
+unevaluatedProperties: false
examples:
- |
@@ -299,4 +399,29 @@ examples:
};
};
};
+ - |
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dac@0 {
+ compatible = "microchip,mcp48feb02";
+ reg = <0x0>;
+ vdd-supply = <&vdac_vdd>;
+ vref-supply = <&vref_reg>;
+ spi-max-frequency = <10000000>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+ channel@0 {
+ reg = <0>;
+ label = "Adjustable_voltage_ch0";
+ };
+
+ channel@1 {
+ reg = <0x1>;
+ label = "Adjustable_voltage_ch1";
+ };
+ };
+ };
...
--
2.43.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH v3 3/3] iio: dac: add support for Microchip MCP48FEB02
2026-08-04 13:29 [PATCH v3 0/3] Refactor Microchip MCP47FEB02 I2C driver in separate modules to add support for MCP48FEB02 SPI driver Ariana Lazar
2026-08-04 13:29 ` [PATCH v3 1/3] iio: dac: mcp47feb02: refactor MCP47FEB02 I2C driver into two modules Ariana Lazar
2026-08-04 13:29 ` [PATCH v3 2/3] dt-bindings: iio: dac: add support for MCP48FEB02 SPI Ariana Lazar
@ 2026-08-04 13:29 ` Ariana Lazar
2026-08-04 13:53 ` Andy Shevchenko
2 siblings, 1 reply; 8+ messages in thread
From: Ariana Lazar @ 2026-08-04 13:29 UTC (permalink / raw)
To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-kernel, linux-iio, devicetree, Ariana Lazar
This is the iio driver for Microchip MCP48FxBy1/2/4/8 series of buffered
voltage output Digital-to-Analog Converters with nonvolatile or volatile
memory and an SPI Interface.
The families support up to 8 output channels.
The devices can be 8-bit, 10-bit and 12-bit.
Signed-off-by: Ariana Lazar <ariana.lazar@microchip.com>
---
MAINTAINERS | 1 +
drivers/iio/dac/Kconfig | 23 +++++++
drivers/iio/dac/Makefile | 1 +
drivers/iio/dac/mcp47feb02-spi.c | 145 +++++++++++++++++++++++++++++++++++++++
4 files changed, 170 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index fc87609865f26a9f484edc42058f7df659a0fa4f..b2ffcc0161ba0d147df35558312cbf99052ed13e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16120,6 +16120,7 @@ S: Supported
F: Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml
F: drivers/iio/dac/mcp47feb02-core.c
F: drivers/iio/dac/mcp47feb02-i2c.c
+F: drivers/iio/dac/mcp47feb02-spi.c
F: drivers/iio/dac/mcp47feb02.h
MCP4821 DAC DRIVER
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index cc10d43ffd1e89bbf945ef89ea5af0abe1a91084..d941f78f7110b185fee6f025be829bae1e4534da 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -575,6 +575,29 @@ config MCP47FEB02_I2C
will be called mcp47feb02_i2c and you will also get
mcp47feb02_core for the core module.
+config MCP47FEB02_SPI
+ tristate "MCP48F(E/V)B01/02/04/08/11/12/14/18/21/22/24/28 SPI DAC driver"
+ depends on SPI
+ select REGMAP_SPI
+ select MCP47FEB02
+ help
+ Say yes here if you want to build the driver for the Microchip:
+ - 8-bit DAC:
+ MCP48FEB01, MCP48FEB02, MCP48FEB04, MCP48FEB08,
+ MCP48FVB01, MCP48FVB02, MCP48FVB04, MCP48FVB08
+ - 10-bit DAC:
+ MCP48FEB11, MCP48FEB12, MCP48FEB14, MCP48FEB18,
+ MCP48FVB11, MCP48FVB12, MCP48FVB14, MCP48FVB18
+ - 12-bit DAC:
+ MCP48FEB21, MCP48FEB22, MCP48FEB24, MCP48FEB28,
+ MCP48FVB21, MCP48FVB22, MCP48FVB24, MCP48FVB28
+ having 1 to 8 channels, 8/10/12-bit digital-to-analog converter
+ (DAC) with SPI interface.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mcp47feb02_spi and you will also get
+ mcp47feb02_core for the core module.
+
config MCP4821
tristate "MCP4801/02/11/12/21/22 DAC driver"
depends on SPI
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 28c8646a987cff1ac4d693fe86898d66c901d5bd..9903198b934e8b66a4ea6562b662d8073ed9cc59 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -56,6 +56,7 @@ obj-$(CONFIG_MCP4725) += mcp4725.o
obj-$(CONFIG_MCP4728) += mcp4728.o
obj-$(CONFIG_MCP47FEB02) += mcp47feb02-core.o
obj-$(CONFIG_MCP47FEB02_I2C) += mcp47feb02-i2c.o
+obj-$(CONFIG_MCP47FEB02_SPI) += mcp47feb02-spi.o
obj-$(CONFIG_MCP4821) += mcp4821.o
obj-$(CONFIG_MCP4922) += mcp4922.o
obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o
diff --git a/drivers/iio/dac/mcp47feb02-spi.c b/drivers/iio/dac/mcp47feb02-spi.c
new file mode 100644
index 0000000000000000000000000000000000000000..49949466613c99ac62d5eda7d640421b602a58b4
--- /dev/null
+++ b/drivers/iio/dac/mcp47feb02-spi.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * IIO driver for MCP48FEB02 Multi-Channel DAC with SPI interface
+ *
+ * Copyright (C) 2025-2026 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Ariana Lazar <ariana.lazar@microchip.com>
+ *
+ * Datasheet links for devices with SPI interface:
+ * [MCP48FEBxx] https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005429B.pdf
+ * [MCP48FVBxx] https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005466A.pdf
+ * [MCP48FxBx4/8] https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP48FXBX4-8-Family-Data-Sheet-DS20006362A.pdf
+ */
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include "mcp47feb02.h"
+
+/* Parts with EEPROM memory */
+MCP47FEB02_CHIP_INFO(mcp48feb01, 1, 8, false, true);
+MCP47FEB02_CHIP_INFO(mcp48feb02, 2, 8, false, true);
+MCP47FEB02_CHIP_INFO(mcp48feb04, 4, 8, true, true);
+MCP47FEB02_CHIP_INFO(mcp48feb08, 8, 8, true, true);
+MCP47FEB02_CHIP_INFO(mcp48feb11, 1, 10, false, true);
+MCP47FEB02_CHIP_INFO(mcp48feb12, 2, 10, false, true);
+MCP47FEB02_CHIP_INFO(mcp48feb14, 4, 10, true, true);
+MCP47FEB02_CHIP_INFO(mcp48feb18, 8, 10, true, true);
+MCP47FEB02_CHIP_INFO(mcp48feb21, 1, 12, false, true);
+MCP47FEB02_CHIP_INFO(mcp48feb22, 2, 12, false, true);
+MCP47FEB02_CHIP_INFO(mcp48feb24, 4, 12, true, true);
+MCP47FEB02_CHIP_INFO(mcp48feb28, 8, 12, true, true);
+
+/* Parts without EEPROM memory */
+MCP47FEB02_CHIP_INFO(mcp48fvb01, 1, 8, false, false);
+MCP47FEB02_CHIP_INFO(mcp48fvb02, 2, 8, false, false);
+MCP47FEB02_CHIP_INFO(mcp48fvb04, 4, 8, true, false);
+MCP47FEB02_CHIP_INFO(mcp48fvb08, 8, 8, true, false);
+MCP47FEB02_CHIP_INFO(mcp48fvb11, 1, 10, false, false);
+MCP47FEB02_CHIP_INFO(mcp48fvb12, 2, 10, false, false);
+MCP47FEB02_CHIP_INFO(mcp48fvb14, 4, 10, true, false);
+MCP47FEB02_CHIP_INFO(mcp48fvb18, 8, 10, true, false);
+MCP47FEB02_CHIP_INFO(mcp48fvb21, 1, 12, false, false);
+MCP47FEB02_CHIP_INFO(mcp48fvb22, 2, 12, false, false);
+MCP47FEB02_CHIP_INFO(mcp48fvb24, 4, 12, true, false);
+MCP47FEB02_CHIP_INFO(mcp48fvb28, 8, 12, true, false);
+
+static int mcp47feb02_spi_probe(struct spi_device *spi)
+{
+ const struct mcp47feb02_features *chip_features;
+ struct device *dev = &spi->dev;
+ struct regmap *regmap;
+
+ chip_features = spi_get_device_match_data(spi);
+ if (!chip_features)
+ return dev_err_probe(dev, -ENODEV, "No SPI device found\n");
+
+ if (chip_features->have_eeprom)
+ regmap = devm_regmap_init_spi(spi, &mcp47feb02_regmap_config);
+ else
+ regmap = devm_regmap_init_spi(spi, &mcp47fvb02_regmap_config);
+
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "Error initializing SPI regmap\n");
+
+ return mcp47feb02_common_probe(chip_features, regmap);
+}
+
+static const struct spi_device_id mcp47feb02_spi_id[] = {
+ { .name = "mcp48feb01", .driver_data = (kernel_ulong_t)&mcp48feb01_chip_features },
+ { .name = "mcp48feb02", .driver_data = (kernel_ulong_t)&mcp48feb02_chip_features },
+ { .name = "mcp48feb04", .driver_data = (kernel_ulong_t)&mcp48feb04_chip_features },
+ { .name = "mcp48feb08", .driver_data = (kernel_ulong_t)&mcp48feb08_chip_features },
+ { .name = "mcp48feb11", .driver_data = (kernel_ulong_t)&mcp48feb11_chip_features },
+ { .name = "mcp48feb12", .driver_data = (kernel_ulong_t)&mcp48feb12_chip_features },
+ { .name = "mcp48feb14", .driver_data = (kernel_ulong_t)&mcp48feb14_chip_features },
+ { .name = "mcp48feb18", .driver_data = (kernel_ulong_t)&mcp48feb18_chip_features },
+ { .name = "mcp48feb21", .driver_data = (kernel_ulong_t)&mcp48feb21_chip_features },
+ { .name = "mcp48feb22", .driver_data = (kernel_ulong_t)&mcp48feb22_chip_features },
+ { .name = "mcp48feb24", .driver_data = (kernel_ulong_t)&mcp48feb24_chip_features },
+ { .name = "mcp48feb28", .driver_data = (kernel_ulong_t)&mcp48feb28_chip_features },
+ { .name = "mcp48fvb01", .driver_data = (kernel_ulong_t)&mcp48fvb01_chip_features },
+ { .name = "mcp48fvb02", .driver_data = (kernel_ulong_t)&mcp48fvb02_chip_features },
+ { .name = "mcp48fvb04", .driver_data = (kernel_ulong_t)&mcp48fvb04_chip_features },
+ { .name = "mcp48fvb08", .driver_data = (kernel_ulong_t)&mcp48fvb08_chip_features },
+ { .name = "mcp48fvb11", .driver_data = (kernel_ulong_t)&mcp48fvb11_chip_features },
+ { .name = "mcp48fvb12", .driver_data = (kernel_ulong_t)&mcp48fvb12_chip_features },
+ { .name = "mcp48fvb14", .driver_data = (kernel_ulong_t)&mcp48fvb14_chip_features },
+ { .name = "mcp48fvb18", .driver_data = (kernel_ulong_t)&mcp48fvb18_chip_features },
+ { .name = "mcp48fvb21", .driver_data = (kernel_ulong_t)&mcp48fvb21_chip_features },
+ { .name = "mcp48fvb22", .driver_data = (kernel_ulong_t)&mcp48fvb22_chip_features },
+ { .name = "mcp48fvb24", .driver_data = (kernel_ulong_t)&mcp48fvb24_chip_features },
+ { .name = "mcp48fvb28", .driver_data = (kernel_ulong_t)&mcp48fvb28_chip_features },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, mcp47feb02_spi_id);
+
+static const struct of_device_id mcp47feb02_of_spi_match[] = {
+ { .compatible = "microchip,mcp48feb01", .data = &mcp48feb01_chip_features },
+ { .compatible = "microchip,mcp48feb02", .data = &mcp48feb02_chip_features },
+ { .compatible = "microchip,mcp48feb04", .data = &mcp48feb04_chip_features },
+ { .compatible = "microchip,mcp48feb08", .data = &mcp48feb08_chip_features },
+ { .compatible = "microchip,mcp48feb11", .data = &mcp48feb11_chip_features },
+ { .compatible = "microchip,mcp48feb12", .data = &mcp48feb12_chip_features },
+ { .compatible = "microchip,mcp48feb14", .data = &mcp48feb14_chip_features },
+ { .compatible = "microchip,mcp48feb18", .data = &mcp48feb18_chip_features },
+ { .compatible = "microchip,mcp48feb21", .data = &mcp48feb21_chip_features },
+ { .compatible = "microchip,mcp48feb22", .data = &mcp48feb22_chip_features },
+ { .compatible = "microchip,mcp48feb24", .data = &mcp48feb24_chip_features },
+ { .compatible = "microchip,mcp48feb28", .data = &mcp48feb28_chip_features },
+ { .compatible = "microchip,mcp48fvb01", .data = &mcp48fvb01_chip_features },
+ { .compatible = "microchip,mcp48fvb02", .data = &mcp48fvb02_chip_features },
+ { .compatible = "microchip,mcp48fvb04", .data = &mcp48fvb04_chip_features },
+ { .compatible = "microchip,mcp48fvb08", .data = &mcp48fvb08_chip_features },
+ { .compatible = "microchip,mcp48fvb11", .data = &mcp48fvb11_chip_features },
+ { .compatible = "microchip,mcp48fvb12", .data = &mcp48fvb12_chip_features },
+ { .compatible = "microchip,mcp48fvb14", .data = &mcp48fvb14_chip_features },
+ { .compatible = "microchip,mcp48fvb18", .data = &mcp48fvb18_chip_features },
+ { .compatible = "microchip,mcp48fvb21", .data = &mcp48fvb21_chip_features },
+ { .compatible = "microchip,mcp48fvb22", .data = &mcp48fvb22_chip_features },
+ { .compatible = "microchip,mcp48fvb24", .data = &mcp48fvb24_chip_features },
+ { .compatible = "microchip,mcp48fvb28", .data = &mcp48fvb28_chip_features },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mcp47feb02_of_spi_match);
+
+static struct spi_driver mcp47feb02_spi_driver = {
+ .driver = {
+ .name = "mcp47feb02",
+ .of_match_table = mcp47feb02_of_spi_match,
+ .pm = pm_sleep_ptr(&mcp47feb02_pm_ops),
+ },
+ .probe = mcp47feb02_spi_probe,
+ .id_table = mcp47feb02_spi_id,
+};
+module_spi_driver(mcp47feb02_spi_driver);
+
+MODULE_AUTHOR("Ariana Lazar <ariana.lazar@microchip.com>");
+MODULE_DESCRIPTION("IIO driver for MCP48FEB02 Multi-Channel DAC with SPI interface");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_MCP47FEB02");
--
2.43.0
^ permalink raw reply related [flat|nested] 8+ messages in thread