diff --git a/MAINTAINERS b/MAINTAINERS
index 2281af4b41b6..8b7c37bed252 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6693,6 +6693,13 @@ F:	Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
 F:	Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
 F:	drivers/iio/adc/envelope-detector.c
 
+IIO GENERIC COUNTER INTERFACE
+M:	William Breathitt Gray <vilhelm.gray@gmail.com>
+L:	linux-iio@vger.kernel.org
+S:	Maintained
+F:	drivers/iio/industrialio-counter.c
+F:	include/linux/iio/counter.h
+
 IIO MULTIPLEXER
 M:	Peter Rosin <peda@axentia.se>
 L:	linux-iio@vger.kernel.org
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index b3c8c6ef0dff..78e01f4f5937 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -30,6 +30,14 @@ config IIO_CONFIGFS
 	  (e.g. software triggers). For more info see
 	  Documentation/iio/iio_configfs.txt.
 
+config IIO_COUNTER
+	bool "Enable IIO counter support"
+	help
+	  Provides IIO core support for counters. This API provides
+	  a generic interface that serves as the building blocks to
+	  create more complex counter interfaces. Rudimentary support
+	  for counters is enabled.
+
 config IIO_TRIGGER
 	bool "Enable triggered sampling support"
 	help
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 93c769cd99bf..6427ff38f964 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -5,6 +5,7 @@
 obj-$(CONFIG_IIO) += industrialio.o
 industrialio-y := industrialio-core.o industrialio-event.o inkern.o
 industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
+industrialio-$(CONFIG_IIO_COUNTER) += industrialio-counter.o
 industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
 
 obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
index 474e1ac4e7c0..c8becfe78e28 100644
--- a/drivers/iio/counter/Kconfig
+++ b/drivers/iio/counter/Kconfig
@@ -4,6 +4,7 @@
 # When adding new entries keep the list in alphabetical order
 
 menu "Counters"
+	depends on IIO_COUNTER
 
 config 104_QUAD_8
 	tristate "ACCES 104-QUAD-8 driver"
diff --git a/drivers/iio/industrialio-counter.c b/drivers/iio/industrialio-counter.c
new file mode 100644
index 000000000000..dfb982dae3a8
--- /dev/null
+++ b/drivers/iio/industrialio-counter.c
@@ -0,0 +1,900 @@
+/*
+ * Industrial I/O counter interface functions
+ * Copyright (C) 2017 William Breathitt Gray
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/printk.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+
+#include <linux/iio/counter.h>
+
+#define iio_priv_get_counter(_indio_dev) \
+	(*(struct iio_counter **)iio_priv(_indio_dev))
+
+static struct iio_counter_signal *iio_counter_signal_find_by_id(
+	const struct iio_counter *const counter, const int id)
+{
+	size_t i;
+	struct iio_counter_signal *signal;
+
+	for (i = 0; i < counter->num_signals; i++) {
+		signal = counter->signals + i;
+		if (signal->id == id)
+			return signal;
+	}
+
+	return NULL;
+}
+
+static struct iio_counter_trigger *iio_counter_trigger_find_by_id(
+	const struct iio_counter_value *const value, const int id)
+{
+	size_t i;
+	struct iio_counter_trigger *trigger;
+
+	for (i = 0; i < value->num_triggers; i++) {
+		trigger = value->triggers + i;
+		if (trigger->signal->id == id)
+			return trigger;
+	}
+
+	return NULL;
+}
+
+static struct iio_counter_value *iio_counter_value_find_by_id(
+	const struct iio_counter *const counter, const int id)
+{
+	size_t i;
+	struct iio_counter_value *value;
+
+	for (i = 0; i < counter->num_values; i++) {
+		value = counter->values + i;
+		if (value->id == id)
+			return value;
+	}
+
+	return NULL;
+}
+
+static ssize_t iio_counter_signal_name_read(struct iio_dev *indio_dev,
+	uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	const struct iio_counter_signal *signal;
+
+	signal = iio_counter_signal_find_by_id(counter, chan->channel2);
+	if (!signal)
+		return -EINVAL;
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n", signal->name);
+}
+
+static ssize_t iio_counter_value_name_read(struct iio_dev *indio_dev,
+	uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	const struct iio_counter_value *value;
+
+	value = iio_counter_value_find_by_id(counter, chan->channel2);
+	if (!value)
+		return -EINVAL;
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n", value->name);
+}
+
+static ssize_t iio_counter_value_triggers_read(struct iio_dev *indio_dev,
+	uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_value *value;
+	size_t i;
+	const struct iio_counter_trigger *trigger;
+	ssize_t len = 0;
+
+	value = iio_counter_value_find_by_id(counter, chan->channel2);
+	if (!value)
+		return -EINVAL;
+
+	/* Print out a list of every Signal association to Value */
+	for (i = 0; i < value->num_triggers; i++) {
+		trigger = value->triggers + i;
+		len += snprintf(buf + len, PAGE_SIZE - len, "%d\t%s\t%s\n",
+			trigger->signal->id, trigger->signal->name,
+			trigger->trigger_modes[trigger->mode]);
+		if (len >= PAGE_SIZE)
+			return -ENOMEM;
+	}
+
+	return len;
+}
+
+static ssize_t iio_counter_trigger_mode_read(struct iio_dev *indio_dev,
+	uintptr_t priv, const struct iio_chan_spec *chan, char *buf)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_value *value;
+	struct iio_counter_trigger *trigger;
+	const int signal_id = *(int *)((void *)priv);
+	int mode;
+
+	if (!counter->ops->trigger_mode_get)
+		return -EINVAL;
+
+	value = iio_counter_value_find_by_id(counter, chan->channel2);
+	if (!value)
+		return -EINVAL;
+
+	trigger = iio_counter_trigger_find_by_id(value, signal_id);
+	if (!trigger)
+		return -EINVAL;
+
+	mode = counter->ops->trigger_mode_get(counter, value, trigger);
+
+	if (mode < 0)
+		return mode;
+	else if (mode >= trigger->num_trigger_modes)
+		return -EINVAL;
+
+	trigger->mode = mode;
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n", trigger->trigger_modes[mode]);
+}
+
+static ssize_t iio_counter_trigger_mode_write(struct iio_dev *indio_dev,
+	uintptr_t priv, const struct iio_chan_spec *chan, const char *buf,
+	size_t len)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_value *value;
+	ssize_t err;
+	struct iio_counter_trigger *trigger;
+	const int signal_id = *(int *)((void *)priv);
+	unsigned int mode;
+
+	if (!counter->ops->trigger_mode_set)
+		return -EINVAL;
+
+	value = iio_counter_value_find_by_id(counter, chan->channel2);
+	if (!value)
+		return -EINVAL;
+
+	trigger = iio_counter_trigger_find_by_id(value, signal_id);
+	if (!trigger)
+		return -EINVAL;
+
+	for (mode = 0; mode < trigger->num_trigger_modes; mode++)
+		if (sysfs_streq(buf, trigger->trigger_modes[mode]))
+			break;
+
+	if (mode >= trigger->num_trigger_modes)
+		return -EINVAL;
+
+	err = counter->ops->trigger_mode_set(counter, value, trigger, mode);
+	if (err)
+		return err;
+
+	trigger->mode = mode;
+
+	return len;
+}
+
+static ssize_t iio_counter_trigger_mode_available_read(
+	struct iio_dev *indio_dev, uintptr_t priv,
+	const struct iio_chan_spec *chan, char *buf)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_value *value;
+	ssize_t len = 0;
+	struct iio_counter_trigger *trigger;
+	const int signal_id = *(int *)((void *)priv);
+	unsigned int i;
+
+	value = iio_counter_value_find_by_id(counter, chan->channel2);
+	if (!value)
+		return -EINVAL;
+
+	trigger = iio_counter_trigger_find_by_id(value, signal_id);
+	if (!trigger)
+		return -EINVAL;
+
+	for (i = 0; i < trigger->num_trigger_modes; i++)
+		len += scnprintf(buf + len, PAGE_SIZE - len, "%s ",
+			trigger->trigger_modes[i]);
+
+	buf[len - 1] = '\n';
+
+	return len;
+}
+
+static int iio_counter_value_function_set(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan, unsigned int mode)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_value *value;
+	int err;
+
+	if (!counter->ops->value_function_set)
+		return -EINVAL;
+
+	/* Find relevant Value */
+	value = iio_counter_value_find_by_id(counter, chan->channel2);
+	if (!value)
+		return -EINVAL;
+
+	/* Map IIO core function_set to Generic Counter value_function_set */
+	err = counter->ops->value_function_set(counter, value, mode);
+	if (err)
+		return err;
+
+	value->mode = mode;
+
+	return 0;
+}
+
+static int iio_counter_value_function_get(struct iio_dev *indio_dev,
+	const struct iio_chan_spec *chan)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_value *value;
+	int retval;
+
+	if (!counter->ops->value_function_get)
+		return -EINVAL;
+
+	/* Find relevant Value */
+	value = iio_counter_value_find_by_id(counter, chan->channel2);
+	if (!value)
+		return -EINVAL;
+
+	/* Map IIO core function_get to Generic Counter value_function_get */
+	retval = counter->ops->value_function_get(counter, value);
+	if (retval < 0)
+		return retval;
+	else if (retval >= value->num_function_modes)
+		return -EINVAL;
+
+	value->mode = retval;
+
+	return retval;
+}
+
+static int iio_counter_value_ext_info_alloc(struct iio_chan_spec *const chan,
+	struct iio_counter_value *const value)
+{
+	const struct iio_chan_spec_ext_info ext_info_default[] = {
+		{
+			.name = "name",
+			.shared = IIO_SEPARATE,
+			.read = iio_counter_value_name_read
+		},
+		IIO_ENUM("function", IIO_SEPARATE, &value->function_enum),
+		{
+			.name = "function_available",
+			.shared = IIO_SEPARATE,
+			.read = iio_enum_available_read,
+			.private = (void *)&value->function_enum
+		},
+		{
+			.name = "triggers",
+			.shared = IIO_SEPARATE,
+			.read = iio_counter_value_triggers_read
+		}
+	};
+	const size_t num_default = ARRAY_SIZE(ext_info_default);
+	const struct iio_chan_spec_ext_info ext_info_trigger[] = {
+		{
+			.shared = IIO_SEPARATE,
+			.read = iio_counter_trigger_mode_read,
+			.write = iio_counter_trigger_mode_write
+		},
+		{
+			.shared = IIO_SEPARATE,
+			.read = iio_counter_trigger_mode_available_read
+		}
+	};
+	const size_t num_ext_info_trigger = ARRAY_SIZE(ext_info_trigger);
+	const size_t num_triggers_ext_info = num_ext_info_trigger *
+		value->num_triggers;
+	const size_t num_ext_info = num_default + num_triggers_ext_info + 1;
+	int err;
+	struct iio_chan_spec_ext_info *ext_info;
+	const struct iio_counter_trigger *trigger;
+	size_t i;
+	size_t j;
+
+	/* Construct function_enum for Value */
+	value->function_enum.items = value->function_modes;
+	value->function_enum.num_items = value->num_function_modes;
+	value->function_enum.set = iio_counter_value_function_set;
+	value->function_enum.get = iio_counter_value_function_get;
+
+	ext_info = kmalloc_array(num_ext_info, sizeof(*ext_info), GFP_KERNEL);
+	if (!ext_info)
+		return -ENOMEM;
+	ext_info[num_ext_info - 1].name = NULL;
+
+	/* Add ext_info for the name, function, function_available, and triggers
+	 * attributes
+	 */
+	memcpy(ext_info, ext_info_default, sizeof(ext_info_default));
+	/* Add ext_info for the trigger_signalX-Z and
+	 * trigger_signalX-Z_available attributes for each Trigger
+	 */
+	for (i = 0; i < num_triggers_ext_info; i += num_ext_info_trigger)
+		memcpy(ext_info + num_default + i, ext_info_trigger,
+			sizeof(ext_info_trigger));
+
+	/* Set name for each trigger_signalX-Z and trigger_signalX-Z_available
+	 * attribute; store the respective Signal ID address in each ext_info
+	 * private member
+	 */
+	for (i = num_default, j = 0; j < value->num_triggers; i++, j++) {
+		trigger = value->triggers + j;
+
+		ext_info[i].name = kasprintf(GFP_KERNEL, "trigger_signal%d-%d",
+			chan->channel, trigger->signal->id);
+		if (!ext_info[i].name) {
+			err = -ENOMEM;
+			goto err_name_alloc;
+		}
+		ext_info[i].private = (void *)&trigger->signal->id;
+		i++;
+
+		ext_info[i].name = kasprintf(GFP_KERNEL,
+			"trigger_signal%d-%d_available",
+			chan->channel, trigger->signal->id);
+		if (!ext_info[i].name) {
+			err = -ENOMEM;
+			goto err_name_alloc;
+		}
+		ext_info[i].private = (void *)&trigger->signal->id;
+	}
+
+	chan->ext_info = ext_info;
+
+	return 0;
+
+err_name_alloc:
+	while (i-- > num_default)
+		kfree(ext_info[i].name);
+	kfree(ext_info);
+	return err;
+}
+
+static void iio_counter_value_ext_info_free(
+	const struct iio_chan_spec *const channel)
+{
+	size_t i;
+	const char *const prefix = "trigger_signal";
+	const size_t prefix_len = strlen(prefix);
+
+	for (i = 0; channel->ext_info[i].name; i++)
+		if (!strncmp(channel->ext_info[i].name, prefix, prefix_len))
+			kfree(channel->ext_info[i].name);
+	kfree(channel->ext_info);
+}
+
+static const struct iio_chan_spec_ext_info iio_counter_signal_ext_info[] = {
+	{
+		.name = "name",
+		.shared = IIO_SEPARATE,
+		.read = iio_counter_signal_name_read
+	},
+	{}
+};
+
+static int iio_counter_channels_alloc(struct iio_counter *const counter)
+{
+	const size_t num_channels = counter->num_signals + counter->num_values +
+		counter->num_channels;
+	int err;
+	struct iio_chan_spec *channels;
+	size_t j;
+	struct iio_counter_value *value;
+	size_t i = counter->num_channels;
+	const struct iio_counter_signal *signal;
+
+	channels = kcalloc(num_channels, sizeof(*channels), GFP_KERNEL);
+	if (!channels)
+		return -ENOMEM;
+
+	/* If any channels were supplied on Counter registration,
+	 * we add them here to the front of the array
+	 */
+	memcpy(channels, counter->channels,
+		counter->num_channels * sizeof(*counter->channels));
+
+	/* Add channel for each Value */
+	for (j = 0; j < counter->num_values; j++) {
+		value = counter->values + j;
+
+		channels[i].type = IIO_COUNT;
+		channels[i].channel = counter->id;
+		channels[i].channel2 = value->id;
+		channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+		channels[i].indexed = 1;
+		channels[i].counter = 1;
+
+		/* Add channels for Triggers associated with Value */
+		err = iio_counter_value_ext_info_alloc(channels + i, value);
+		if (err)
+			goto err_value_ext_info_alloc;
+
+		i++;
+	}
+
+	/* Add channel for each Signal */
+	for (j = 0; j < counter->num_signals; j++) {
+		signal = counter->signals + j;
+
+		channels[i].type = IIO_SIGNAL;
+		channels[i].channel = counter->id;
+		channels[i].channel2 = signal->id;
+		channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+		channels[i].indexed = 1;
+		channels[i].counter = 1;
+		channels[i].ext_info = iio_counter_signal_ext_info;
+
+		i++;
+	}
+
+	counter->indio_dev->num_channels = num_channels;
+	counter->indio_dev->channels = channels;
+
+	return 0;
+
+err_value_ext_info_alloc:
+	while (i-- > counter->num_channels)
+		iio_counter_value_ext_info_free(channels + i);
+	kfree(channels);
+	return err;
+}
+
+static void iio_counter_channels_free(const struct iio_counter *const counter)
+{
+	size_t i = counter->num_channels + counter->indio_dev->num_channels;
+	const struct iio_chan_spec *const chans = counter->indio_dev->channels;
+
+	while (i-- > counter->num_channels)
+		/* Only IIO_COUNT channels need to be freed here */
+		if (chans[i].type == IIO_COUNT)
+			iio_counter_value_ext_info_free(chans + i);
+
+	kfree(chans);
+}
+
+static int iio_counter_read_raw(struct iio_dev *indio_dev,
+	struct iio_chan_spec const *chan, int *val, int *val2, long mask)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_signal *signal;
+	struct iio_counter_value *value;
+
+	if (mask != IIO_CHAN_INFO_RAW)
+		return -EINVAL;
+
+	switch (chan->type) {
+	/* Map read_raw to signal_read for Signal */
+	case IIO_SIGNAL:
+		if (!counter->ops->signal_read)
+			return -EINVAL;
+
+		signal = iio_counter_signal_find_by_id(counter, chan->channel2);
+		if (!signal)
+			return -EINVAL;
+
+		return counter->ops->signal_read(counter, signal, val, val2);
+	/* Map read_raw to value_read for Value */
+	case IIO_COUNT:
+		if (!counter->ops->value_read)
+			return -EINVAL;
+
+		value = iio_counter_value_find_by_id(counter, chan->channel2);
+		if (!value)
+			return -EINVAL;
+
+		return counter->ops->value_read(counter, value, val, val2);
+	/* Map read_raw to read_raw for non-counter channel */
+	default:
+		if (counter->info && counter->info->read_raw)
+			return counter->info->read_raw(indio_dev, chan, val,
+				val2, mask);
+	}
+
+	return -EINVAL;
+}
+
+static int iio_counter_write_raw(struct iio_dev *indio_dev,
+	struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+	struct iio_counter *const counter = iio_priv_get_counter(indio_dev);
+	struct iio_counter_signal *signal;
+	struct iio_counter_value *value;
+
+	if (mask != IIO_CHAN_INFO_RAW)
+		return -EINVAL;
+
+	switch (chan->type) {
+	/* Map write_raw to signal_write for Signal */
+	case IIO_SIGNAL:
+		if (!counter->ops->signal_write)
+			return -EINVAL;
+
+		signal = iio_counter_signal_find_by_id(counter, chan->channel2);
+		if (!signal)
+			return -EINVAL;
+
+		return counter->ops->signal_write(counter, signal, val, val2);
+	/* Map write_raw to value_write for Value */
+	case IIO_COUNT:
+		if (!counter->ops->value_write)
+			return -EINVAL;
+
+		value = iio_counter_value_find_by_id(counter, chan->channel2);
+		if (!value)
+			return -EINVAL;
+
+		return counter->ops->value_write(counter, value, val, val2);
+	/* Map write_raw to write_raw for non-counter channel */
+	default:
+		if (counter->info && counter->info->write_raw)
+			return counter->info->write_raw(indio_dev, chan, val,
+				val2, mask);
+	}
+
+	return -EINVAL;
+}
+
+static int iio_counter_signals_validate(const struct iio_counter *const counter)
+{
+	size_t i;
+	const struct iio_counter_signal *signal;
+	size_t j;
+	int curr_id;
+
+	/* At least one Signal must be defined */
+	if (!counter->num_signals || !counter->signals) {
+		pr_err("Counter '%d' Signals undefined\n", counter->id);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < counter->num_signals; i++) {
+		signal = counter->signals + i;
+		/* No two Signals may share the same ID */
+		for (j = 0; j < i; j++) {
+			curr_id = counter->signals[j].id;
+			if (curr_id == signal->id) {
+				pr_err("Duplicate Counter '%d' Signal '%d'\n",
+						counter->id, signal->id);
+				return -EEXIST;
+			}
+		}
+		for (j = i + 1; j < counter->num_signals; j++) {
+			curr_id = counter->signals[j].id;
+			if (curr_id == signal->id) {
+				pr_err("Duplicate Counter '%d' Signal '%d'\n",
+						counter->id, signal->id);
+				return -EEXIST;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int iio_counter_triggers_validate(const int counter_id,
+	const struct iio_counter_value *const value)
+{
+	size_t i;
+	const struct iio_counter_trigger *trigger;
+	size_t j;
+	int curr_id;
+
+	/* At least one Trigger must be defined */
+	if (!value->num_triggers || !value->triggers) {
+		pr_err("Counter '%d' Value '%d' Triggers undefined\n",
+			counter_id, value->id);
+		return -EINVAL;
+	}
+
+	/* Ensure all Triggers have a defined Signal; this prevents a possible
+	 * NULL pointer dereference when later validating Trigger Signal IDs
+	 */
+	for (i = 0; i < value->num_triggers; i++)
+		if (!value->triggers[i].signal) {
+			pr_err("Counter '%d' Trigger '%zu' Signal undefined\n",
+				counter_id, i);
+			return -EINVAL;
+		}
+
+	/* Verify validity of each Trigger */
+	for (i = 0; i < value->num_triggers; i++) {
+		trigger = value->triggers + i;
+		/* No two Trigger Signals may share the same ID */
+		for (j = 0; j < i; j++) {
+			curr_id = value->triggers[j].signal->id;
+			if (curr_id == trigger->signal->id) {
+				pr_err("Signal '%d' is already linked to Counter '%d' Value '%d'\n",
+					trigger->signal->id, counter_id,
+					value->id);
+				return -EEXIST;
+			}
+		}
+		for (j = i + 1; j < value->num_triggers; j++) {
+			curr_id = value->triggers[j].signal->id;
+			if (curr_id == trigger->signal->id) {
+				pr_err("Signal '%d' is already linked to Counter '%d' Value '%d'\n",
+					trigger->signal->id, counter_id,
+					value->id);
+				return -EEXIST;
+			}
+		}
+
+		/* At least one trigger mode must be defined for each Trigger */
+		if (!trigger->num_trigger_modes || !trigger->trigger_modes) {
+			pr_err("Counter '%d' Signal '%d' trigger modes undefined\n",
+				counter_id, trigger->signal->id);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int iio_counter_values_validate(const struct iio_counter *const counter)
+{
+	size_t i;
+	const struct iio_counter_value *value;
+	size_t j;
+	int curr_id;
+	int err;
+
+	/* At least one Value must be defined */
+	if (!counter->num_values || !counter->values) {
+		pr_err("Counter '%d' Values undefined\n", counter->id);
+		return -EINVAL;
+	}
+
+	/* Verify validity of each Value */
+	for (i = 0; i < counter->num_values; i++) {
+		value = counter->values + i;
+		/* No two Values may share the same ID */
+		for (j = 0; j < i; j++) {
+			curr_id = counter->values[j].id;
+			if (curr_id == value->id) {
+				pr_err("Duplicate Counter '%d' Value '%d'\n",
+					counter->id, value->id);
+				return -EEXIST;
+			}
+		}
+		for (j = i + 1; j < counter->num_values; j++) {
+			curr_id = counter->values[j].id;
+			if (curr_id == value->id) {
+				pr_err("Duplicate Counter '%d' Value '%d'\n",
+					counter->id, value->id);
+				return -EEXIST;
+			}
+		}
+
+		/* At least one function mode must be defined for each Value */
+		if (!value->num_function_modes || !value->function_modes) {
+			pr_err("Counter '%d' Value '%d' function modes undefined\n",
+				counter->id, value->id);
+			return -EINVAL;
+		}
+
+		/* Verify the Triggers associated with each Value */
+		err = iio_counter_triggers_validate(counter->id, value);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+/**
+ * iio_counter_register - register Counter to the system
+ * @counter: pointer to IIO Counter to register
+ *
+ * This function piggybacks off of iio_device_register. First, the relevant
+ * Counter members are validated; the signals and values members must be defined
+ * and populated by valid Signal and Value structures respectively. Next, a
+ * struct iio_dev is allocated by a call to iio_device_alloc and initialized for
+ * the Counter, IIO channels are allocated, the Counter address is stored as the
+ * private data, and finally iio_device_register is called.
+ */
+int iio_counter_register(struct iio_counter *const counter)
+{
+	const struct iio_info info_default = {
+		.read_raw = iio_counter_read_raw,
+		.write_raw = iio_counter_write_raw
+	};
+	int err;
+	struct iio_info *info;
+	struct iio_counter **priv;
+
+	if (!counter)
+		return -EINVAL;
+
+	/* Verify that Signals are valid and IDs do not conflict */
+	err = iio_counter_signals_validate(counter);
+	if (err)
+		return err;
+
+	/* Verify that Values are valid and IDs do not conflict;
+	 * Triggers for each Value are also verified for validity
+	 */
+	err = iio_counter_values_validate(counter);
+	if (err)
+		return err;
+
+	counter->indio_dev = iio_device_alloc(sizeof(counter));
+	if (!counter->indio_dev)
+		return -ENOMEM;
+
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (!info) {
+		err = -ENOMEM;
+		goto err_info_alloc;
+	}
+	/* If an iio_info has been supplied than we use that,
+	 * otherwise we set all callbacks to NULL; iio_counter_read_raw
+	 * and iio_counter_write_raw is used for read_raw and write_raw
+	 * for either case in order to support counter functionality
+	 * (supplied read_raw/write_raw will be called from within
+	 * iio_counter_read_raw/iio_counter_write_raw for non-counter
+	 * channels)
+	 */
+	if (counter->info) {
+		memcpy(info, counter->info, sizeof(*counter->info));
+		info->read_raw = iio_counter_read_raw;
+		info->write_raw = iio_counter_write_raw;
+	} else {
+		memcpy(info, &info_default, sizeof(info_default));
+	}
+
+	counter->indio_dev->info = info;
+	counter->indio_dev->modes = INDIO_DIRECT_MODE;
+	counter->indio_dev->name = counter->name;
+	counter->indio_dev->dev.parent = counter->dev;
+
+	/* IIO channels are allocated and set for Signals, Values, and Triggers;
+	 * any auxiliary IIO channels provided in iio_counter are also set
+	 */
+	err = iio_counter_channels_alloc(counter);
+	if (err)
+		goto err_channels_alloc;
+
+	/* Pointer to the counter is stored in indio_dev as a way to refer
+	 * back to the counter from within various IIO callbacks
+	 */
+	priv = iio_priv(counter->indio_dev);
+	memcpy(priv, &counter, sizeof(*priv));
+
+	err = iio_device_register(counter->indio_dev);
+	if (err)
+		goto err_iio_device_register;
+
+	return 0;
+
+err_iio_device_register:
+	iio_counter_channels_free(counter);
+err_channels_alloc:
+	kfree(info);
+err_info_alloc:
+	iio_device_free(counter->indio_dev);
+	return err;
+}
+EXPORT_SYMBOL(iio_counter_register);
+
+/**
+ * iio_counter_unregister - unregister Counter from the system
+ * @counter: pointer to IIO Counter to unregister
+ *
+ * The Counter is unregistered from the system. The indio_dev is unregistered
+ * and all allocated memory is freed.
+ */
+void iio_counter_unregister(struct iio_counter *const counter)
+{
+	const struct iio_info *const info = counter->indio_dev->info;
+
+	if (!counter)
+		return;
+
+	iio_device_unregister(counter->indio_dev);
+
+	iio_counter_channels_free(counter);
+
+	kfree(info);
+	iio_device_free(counter->indio_dev);
+}
+EXPORT_SYMBOL(iio_counter_unregister);
+
+static void devm_iio_counter_unreg(struct device *dev, void *res)
+{
+	iio_counter_unregister(*(struct iio_counter **)res);
+}
+
+/**
+ * devm_iio_counter_register - Resource-managed iio_counter_register
+ * @dev: Device to allocate iio_counter for
+ * @counter: pointer to IIO Counter to register
+ *
+ * Managed iio_counter_register. The IIO counter registered with this
+ * function is automatically unregistered on driver detach. This function
+ * calls iio_counter_register internally. Refer to that function for more
+ * information.
+ *
+ * If an iio counter registered with this function needs to be unregistered
+ * separately, devm_iio_counter_unregister must be used.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int devm_iio_counter_register(struct device *dev,
+	struct iio_counter *const counter)
+{
+	struct iio_counter **ptr;
+	int ret;
+
+	ptr = devres_alloc(devm_iio_counter_unreg, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return -ENOMEM;
+
+	*ptr = counter;
+	ret = iio_counter_register(counter);
+	if (!ret)
+		devres_add(dev, ptr);
+	else
+		devres_free(ptr);
+
+	return ret;
+}
+EXPORT_SYMBOL(devm_iio_counter_register);
+
+static int devm_iio_counter_match(struct device *dev, void *res, void *data)
+{
+	struct iio_counter **r = res;
+
+	if (!r || !*r) {
+		WARN_ON(!r || !*r);
+		return 0;
+	}
+
+	return *r == data;
+}
+
+/**
+ * devm_iio_counter_unregister - Resource-managed iio_counter_unregister
+ * @dev: Device this iio_counter belongs to
+ * @counter: the iio counter associated with the device
+ *
+ * Unregister iio counter registered with devm_iio_counter_register.
+ */
+void devm_iio_counter_unregister(struct device *dev,
+	struct iio_counter *const counter)
+{
+	int rc;
+
+	rc = devres_release(dev, devm_iio_counter_unreg,
+		devm_iio_counter_match, counter);
+	WARN_ON(rc);
+}
+EXPORT_SYMBOL(devm_iio_counter_unregister);
diff --git a/include/linux/iio/counter.h b/include/linux/iio/counter.h
new file mode 100644
index 000000000000..35645406711a
--- /dev/null
+++ b/include/linux/iio/counter.h
@@ -0,0 +1,166 @@
+/*
+ * Industrial I/O counter interface
+ * Copyright (C) 2017 William Breathitt Gray
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+#ifndef _IIO_COUNTER_H_
+#define _IIO_COUNTER_H_
+
+#ifdef CONFIG_IIO_COUNTER
+
+#include <linux/device.h>
+#include <linux/types.h>
+
+#include <linux/iio/iio.h>
+
+/**
+ * struct iio_counter_signal - IIO Counter Signal node
+ * @id:		[DRIVER] unique ID used to identify signal
+ * @name:	[DRIVER] device-specific signal name
+ */
+struct iio_counter_signal {
+	int		id;
+	const char	*name;
+};
+
+/**
+ * struct iio_counter_trigger - IIO Counter Trigger node
+ * @mode:		[DRIVER] current trigger mode state
+ * @trigger_modes:	[DRIVER] available trigger modes
+ * @num_trigger_modes:	[DRIVER] number of modes specified in @trigger_modes
+ * @signal:		[DRIVER] pointer to associated signal
+ */
+struct iio_counter_trigger {
+	unsigned int			mode;
+	const char *const		*trigger_modes;
+	unsigned int			num_trigger_modes;
+	struct iio_counter_signal	*signal;
+};
+
+/**
+ * struct iio_counter_value - IIO Counter Value node
+ * @id:			[DRIVER] unique ID used to identify value
+ * @name:		[DRIVER] device-specific value name
+ * @mode:		[DRIVER] current function mode state
+ * @function_modes:	[DRIVER] available function modes
+ * @num_function_modes:	[DRIVER] number of modes specified in @function_modes
+ * @triggers:		[DRIVER] array of triggers for initialization
+ * @num_triggers:	[DRIVER] number of triggers specified in @triggers
+ * @function_enum:	[INTERN] used internally to generate function attributes
+ */
+struct iio_counter_value {
+	int			id;
+	const char		*name;
+	unsigned int		mode;
+	const char *const	*function_modes;
+	unsigned int		num_function_modes;
+
+	struct iio_counter_trigger	*triggers;
+	size_t				num_triggers;
+
+	struct iio_enum		function_enum;
+};
+
+struct iio_counter;
+
+/**
+ * struct iio_counter_ops - IIO Counter related callbacks
+ * @signal_read:	function to request a signal value from the device.
+ *			Return value will specify the type of value returned by
+ *			the device. val and val2 will contain the elements
+ *			making up the returned value.
+ * @signal_write:	function to write a signal value to the device.
+ *                      Parameters are interpreted the same as signal_read.
+ * @trigger_mode_set:	function to set the trigger mode. mode is the index of
+ *			the requested mode from the value trigger_modes array.
+ * @trigger_mode_get:	function to get the current trigger mode. Return value
+ *			will specify the index of the current mode from the
+ *			value trigger_modes array.
+ * @value_read:		function to request a value value from the device.
+ *			Return value will specify the type of value returned by
+ *			the device. val and val2 will contain the elements
+ *			making up the returned value.
+ * @value_write:	function to write a value value to the device.
+ *                      Parameters are interpreted the same as value_read.
+ * @value_function_set: function to set the value function mode. mode is the
+ *			index of the requested mode from the value
+ *			function_modes array.
+ * @value_function_get: function to get the current value function mode. Return
+ *			value will specify the index of the current mode from
+ *			the value function_modes array.
+ */
+struct iio_counter_ops {
+	int (*signal_read)(struct iio_counter *counter,
+		struct iio_counter_signal *signal, int *val, int *val2);
+	int (*signal_write)(struct iio_counter *counter,
+		struct iio_counter_signal *signal, int val, int val2);
+	int (*trigger_mode_set)(struct iio_counter *counter,
+		struct iio_counter_value *value,
+		struct iio_counter_trigger *trigger, unsigned int mode);
+	int (*trigger_mode_get)(struct iio_counter *counter,
+		struct iio_counter_value *value,
+		struct iio_counter_trigger *trigger);
+	int (*value_read)(struct iio_counter *counter,
+		struct iio_counter_value *value, int *val, int *val2);
+	int (*value_write)(struct iio_counter *counter,
+		struct iio_counter_value *value, int val, int val2);
+	int (*value_function_set)(struct iio_counter *counter,
+		struct iio_counter_value *value, unsigned int mode);
+	int (*value_function_get)(struct iio_counter *counter,
+		struct iio_counter_value *value);
+};
+
+/**
+ * struct iio_counter - IIO Counter data structure
+ * @id:			[DRIVER] unique ID used to identify counter
+ * @name:		[DRIVER] name of the device
+ * @dev:		[DRIVER] device structure, should be assigned a parent
+ *			and owner
+ * @ops:		[DRIVER] callbacks from driver for counter components
+ * @signals:		[DRIVER] array of signals for initialization
+ * @num_signals:	[DRIVER] number of signals specified in @signals
+ * @values:		[DRIVER] array of values for initialization
+ * @num_values:		[DRIVER] number of values specified in @values
+ * @channels:		[DRIVER] channel specification structure table
+ * @num_channels:	[DRIVER] number of channels specified in @channels
+ * @info:		[DRIVER] callbacks and constant info from driver
+ * @indio_dev:		[INTERN] industrial I/O device structure
+ * @driver_data:	[DRIVER] driver data
+ */
+struct iio_counter {
+	int				id;
+	const char			*name;
+	struct device			*dev;
+	const struct iio_counter_ops	*ops;
+
+	struct iio_counter_signal	*signals;
+	size_t				num_signals;
+	struct iio_counter_value	*values;
+	size_t				num_values;
+
+	const struct iio_chan_spec	*channels;
+	size_t				num_channels;
+	const struct iio_info		*info;
+
+	struct iio_dev	*indio_dev;
+	void		*driver_data;
+};
+
+int iio_counter_register(struct iio_counter *const counter);
+void iio_counter_unregister(struct iio_counter *const counter);
+int devm_iio_counter_register(struct device *dev,
+	struct iio_counter *const counter);
+void devm_iio_counter_unregister(struct device *dev,
+	struct iio_counter *const counter);
+
+#endif /* CONFIG_IIO_COUNTER */
+
+#endif /* _IIO_COUNTER_H_ */
