diff --git a/Documentation/devicetree/bindings/regulator/madera-micsupp.txt b/Documentation/devicetree/bindings/regulator/madera-micsupp.txt
new file mode 100644
index 0000000..08aec7f
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/madera-micsupp.txt
@@ -0,0 +1,27 @@
+Cirrus Logic Madera class audio codecs mic supply regulator driver
+
+Only required if the codec has an internal mic supply regulator.
+This is a subnode of the parent mfd node.
+
+See also the core bindings for the parent MFD driver:
+See Documentation/devicetree/bindings/mfd/madera.txt
+
+Required properties:
+  - compatible :  must be "cirrus,madera-micsupp"
+
+Optional subnodes:
+  Standard regulator bindings as described in bindings/regulator/regulator.txt
+
+Example:
+
+codec: cs47l85@0 {
+	compatible = "cirrus,cs47l85";
+
+	micvdd {
+		compatible = "cirrus,madera-micsupp";
+		regulator-min-microvolt = <900000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-allow-bypass = <1>;
+	};
+};
+
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index c96d9a6..4f4b531 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -376,6 +376,14 @@ config REGULATOR_MADERA_LDO1
 	  to supply DCVDD you must include this driver. If you are using an
 	  external DCVDD regulator you do not need this driver.
 
+config REGULATOR_MADERA_MICSUPP
+	tristate "Cirrus Logic Madera codecs mic supply regulator"
+	depends on MFD_MADERA
+	depends on SND_SOC
+	help
+	  Support for the microphone supply regulator on Cirrus Logic
+	  Madera class codecs
+
 config REGULATOR_MAX14577
 	tristate "Maxim 14577/77836 regulator"
 	depends on MFD_MAX14577
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 2db3592..28cdf6e 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -50,6 +50,7 @@ obj-$(CONFIG_REGULATOR_LP8755) += lp8755.o
 obj-$(CONFIG_REGULATOR_LTC3589) += ltc3589.o
 obj-$(CONFIG_REGULATOR_LTC3676) += ltc3676.o
 obj-$(CONFIG_REGULATOR_MADERA_LDO1) += madera-ldo1.o
+obj-$(CONFIG_REGULATOR_MADERA_MICSUPP) += madera-micsupp.o
 obj-$(CONFIG_REGULATOR_MAX14577) += max14577-regulator.o
 obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o
 obj-$(CONFIG_REGULATOR_MAX77620) += max77620-regulator.o
diff --git a/drivers/regulator/madera-micsupp.c b/drivers/regulator/madera-micsupp.c
new file mode 100644
index 0000000..ac8bcbd
--- /dev/null
+++ b/drivers/regulator/madera-micsupp.c
@@ -0,0 +1,260 @@
+/*
+ * madera-micsupp.c  --  Driver for the mic supply regulator on Madera codecs
+ *
+ * Copyright 2015-2017 Cirrus Logic
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/workqueue.h>
+#include <sound/soc.h>
+
+#include <linux/regulator/madera-micsupp.h>
+
+#include <linux/mfd/madera/core.h>
+#include <linux/mfd/madera/pdata.h>
+#include <linux/mfd/madera/registers.h>
+
+struct madera_micsupp {
+	struct regulator_dev *regulator;
+	struct madera *madera;
+
+	struct regulator_consumer_supply supply;
+	struct regulator_init_data init_data;
+
+	struct work_struct check_cp_work;
+};
+
+static void madera_micsupp_check_cp(struct work_struct *work)
+{
+	struct madera_micsupp *micsupp =
+		container_of(work, struct madera_micsupp, check_cp_work);
+	struct snd_soc_dapm_context *dapm = micsupp->madera->dapm;
+	struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+	struct madera *madera = micsupp->madera;
+	struct regmap *regmap = madera->regmap;
+	unsigned int reg;
+	int ret;
+
+	if (dapm) {
+		ret = regmap_read(regmap, MADERA_MIC_CHARGE_PUMP_1, &reg);
+		if (ret) {
+			dev_err(madera->dev,
+				"Failed to read CP state: %d\n", ret);
+			return;
+		}
+
+		reg &= MADERA_CPMIC_ENA | MADERA_CPMIC_BYPASS;
+		if (reg == MADERA_CPMIC_ENA)
+			snd_soc_component_force_enable_pin(component,
+							   "MICSUPP");
+		else
+			snd_soc_component_disable_pin(component, "MICSUPP");
+
+		snd_soc_dapm_sync(dapm);
+	}
+}
+
+static int madera_micsupp_enable(struct regulator_dev *rdev)
+{
+	struct madera_micsupp *micsupp = rdev_get_drvdata(rdev);
+	int ret;
+
+	ret = regulator_enable_regmap(rdev);
+	if (ret == 0)
+		schedule_work(&micsupp->check_cp_work);
+
+	return ret;
+}
+
+static int madera_micsupp_disable(struct regulator_dev *rdev)
+{
+	struct madera_micsupp *micsupp = rdev_get_drvdata(rdev);
+	int ret;
+
+	ret = regulator_disable_regmap(rdev);
+	if (ret == 0)
+		schedule_work(&micsupp->check_cp_work);
+
+	return ret;
+}
+
+static int madera_micsupp_set_bypass(struct regulator_dev *rdev, bool ena)
+{
+	struct madera_micsupp *micsupp = rdev_get_drvdata(rdev);
+	int ret;
+
+	ret = regulator_set_bypass_regmap(rdev, ena);
+	if (ret == 0)
+		schedule_work(&micsupp->check_cp_work);
+
+	return ret;
+}
+
+static const struct regulator_ops madera_micsupp_ops = {
+	.enable = madera_micsupp_enable,
+	.disable = madera_micsupp_disable,
+	.is_enabled = regulator_is_enabled_regmap,
+
+	.list_voltage = regulator_list_voltage_linear_range,
+	.map_voltage = regulator_map_voltage_linear_range,
+
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+	.set_voltage_sel = regulator_set_voltage_sel_regmap,
+
+	.get_bypass = regulator_get_bypass_regmap,
+	.set_bypass = madera_micsupp_set_bypass,
+};
+
+static const struct regulator_linear_range madera_micsupp_ranges[] = {
+	REGULATOR_LINEAR_RANGE(900000,  0,    0x14, 25000),
+	REGULATOR_LINEAR_RANGE(1500000, 0x15, 0x27, 100000),
+};
+
+static const struct regulator_desc madera_micsupp = {
+	.name = "MICVDD",
+	.supply_name = "CPVDD1",
+	.type = REGULATOR_VOLTAGE,
+	.n_voltages = 40,
+	.ops = &madera_micsupp_ops,
+
+	.vsel_reg = MADERA_LDO2_CONTROL_1,
+	.vsel_mask = MADERA_LDO2_VSEL_MASK,
+	.enable_reg = MADERA_MIC_CHARGE_PUMP_1,
+	.enable_mask = MADERA_CPMIC_ENA,
+	.bypass_reg = MADERA_MIC_CHARGE_PUMP_1,
+	.bypass_mask = MADERA_CPMIC_BYPASS,
+
+	.linear_ranges = madera_micsupp_ranges,
+	.n_linear_ranges = ARRAY_SIZE(madera_micsupp_ranges),
+
+	.enable_time = 3000,
+
+	.owner = THIS_MODULE,
+};
+
+static const struct regulator_init_data madera_micsupp_default = {
+	.constraints = {
+		.valid_ops_mask = REGULATOR_CHANGE_STATUS |
+				REGULATOR_CHANGE_VOLTAGE |
+				REGULATOR_CHANGE_BYPASS,
+		.min_uV = 900000,
+		.max_uV = 3300000,
+	},
+
+	.num_consumer_supplies = 1,
+};
+
+static int madera_micsupp_of_get_pdata(struct madera *madera,
+					struct regulator_config *config,
+					const struct regulator_desc *desc)
+{
+	struct madera_pdata *pdata = &madera->pdata;
+	struct madera_micsupp *micsupp = config->driver_data;
+	struct regulator_init_data *init_data;
+
+	init_data = of_get_regulator_init_data(config->dev, config->of_node,
+						desc);
+
+	if (init_data) {
+		init_data->consumer_supplies = &micsupp->supply;
+		init_data->num_consumer_supplies = 1;
+		pdata->micsupp.init_data = init_data;
+	}
+
+	return 0;
+}
+
+static int madera_micsupp_probe(struct platform_device *pdev)
+{
+	struct madera *madera = dev_get_drvdata(pdev->dev.parent);
+	const struct regulator_desc *desc;
+	struct regulator_config config = { };
+	struct madera_micsupp *micsupp;
+	int ret;
+
+	micsupp = devm_kzalloc(&pdev->dev, sizeof(*micsupp), GFP_KERNEL);
+	if (!micsupp)
+		return -ENOMEM;
+
+	micsupp->madera = madera;
+	INIT_WORK(&micsupp->check_cp_work, madera_micsupp_check_cp);
+
+	/*
+	 * Since the chip usually supplies itself we provide some
+	 * default init_data for it.  This will be overridden with
+	 * platform data if provided.
+	 */
+	desc = &madera_micsupp;
+	micsupp->init_data = madera_micsupp_default;
+
+	micsupp->init_data.consumer_supplies = &micsupp->supply;
+	micsupp->supply.supply = "MICVDD";
+	micsupp->supply.dev_name = dev_name(madera->dev);
+
+	config.dev = madera->dev;
+	config.of_node = config.dev->of_node;
+	config.driver_data = micsupp;
+	config.regmap = madera->regmap;
+
+	if (IS_ENABLED(CONFIG_OF)) {
+		if (!dev_get_platdata(madera->dev)) {
+			ret = madera_micsupp_of_get_pdata(madera, &config,
+							  desc);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	if (madera->pdata.micsupp.init_data)
+		config.init_data = madera->pdata.micsupp.init_data;
+	else
+		config.init_data = &micsupp->init_data;
+
+	/* Default to regulated mode, in case bypass is not in constraints */
+	regmap_update_bits(madera->regmap, MADERA_MIC_CHARGE_PUMP_1,
+			   MADERA_CPMIC_BYPASS, 0);
+
+	micsupp->regulator = devm_regulator_register(&pdev->dev, desc,
+						     &config);
+
+	of_node_put(config.of_node);
+
+	if (IS_ERR(micsupp->regulator)) {
+		ret = PTR_ERR(micsupp->regulator);
+		dev_err(madera->dev, "Failed to register mic supply: %d\n",
+			ret);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, micsupp);
+
+	return 0;
+}
+
+static struct platform_driver madera_micsupp_driver = {
+	.probe = madera_micsupp_probe,
+	.driver		= {
+		.name	= "madera-micsupp",
+	},
+};
+
+module_platform_driver(madera_micsupp_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("Madera microphone supply driver");
+MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.wolfsonmicro.com>");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:madera-micsupp");
diff --git a/include/linux/regulator/madera-micsupp.h b/include/linux/regulator/madera-micsupp.h
new file mode 100644
index 0000000..9913bc6
--- /dev/null
+++ b/include/linux/regulator/madera-micsupp.h
@@ -0,0 +1,21 @@
+/*
+ * Platform data for Madera codecs MICSUPP regulator
+ *
+ * Copyright 2016-2017 Cirrus Logic
+ *
+ * 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.
+ */
+
+#ifndef MADERA_MICSUPP_H
+#define MADERA_MICSUPP_H
+
+struct regulator_init_data;
+
+struct madera_micsupp_pdata {
+	/** Regulator configuration for MICSUPP */
+	const struct regulator_init_data *init_data;
+};
+
+#endif
