diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h
index 962cd41a2cb5..a11fb8f214e5 100644
--- a/include/linux/moduleparam.h
+++ b/include/linux/moduleparam.h
@@ -77,6 +77,7 @@ struct kernel_param {
 		void *arg;
 		const struct kparam_string *str;
 		const struct kparam_array *arr;
+		const struct kparam_enum *enumeration;
 	};
 };
 
@@ -98,6 +99,19 @@ struct kparam_array
 	void *elem;
 };
 
+/* Special ones for enums */
+struct module_param_enumeration {
+	const char *key;
+	int val;
+};
+
+struct kparam_enum
+{
+	const struct module_param_enumeration *enums;
+	unsigned int num_enums;
+	int *val;
+};
+
 /**
  * module_param - typesafe helper for a module/cmdline parameter
  * @name: the variable to alter, and exposed parameter name.
@@ -484,6 +498,11 @@ extern int param_set_bint(const char *val, const struct kernel_param *kp);
 #define param_get_bint param_get_int
 #define param_check_bint param_check_int
 
+/* An enumeration */
+extern const struct kernel_param_ops param_ops_enum;
+extern int param_set_enum(const char *key, const struct kernel_param *kp);
+extern int param_get_enum(char *buffer, const struct kernel_param *kp);
+
 /**
  * module_param_array - a parameter which is an array of some type
  * @name: the name of the array variable
@@ -523,6 +542,51 @@ extern int param_set_bint(const char *val, const struct kernel_param *kp);
 			    perm, -1, 0);				\
 	__MODULE_PARM_TYPE(name, "array of " #type)
 
+/**
+ * module_param_enum - a parameter which is an enumeration
+ * @name: the variable to alter, and exposed parameter name
+ * @enumerations: array of struct module_param_enumeration defining the enums
+ * @perm: visibility in sysfs
+ *
+ * The userspace input and output are based on the names defined in the
+ * @enumerations array, which maps the names to values stored in the int
+ * variable defined by @name.
+ *
+ * When initializing or changing the variable @name, ensure the value is defined
+ * in @enumerations. Otherwise, reading the parameter value via sysfs will
+ * output "(unknown)".
+ *
+ * ARRAY_SIZE(@enumerations) is used to determine the number of elements in the
+ * enumerations array, so the definition must be visible.
+ */
+
+#define module_param_enum(name, enumerations, perm)			\
+	module_param_enum_named(name, name, enumerations, perm)
+
+/**
+ * module_param_enum_named - a renamed parameter which is an enumeration
+ * @name: a valid C identifier which is the parameter name
+ * @value: the actual lvalue int variable to alter
+ * @enumerations: array of struct module_param_enumeration defining the enums
+ * @perm: visibility in sysfs
+ *
+ * This exposes a different name than the actual variable name.  See
+ * module_param_named() for why this might be necessary.
+ */
+#define module_param_enum_named(name, value, enumerations, perm)	\
+	param_check_int(name, &(value));				\
+	static const struct kparam_enum __param_arr_##name =		\
+	{								\
+		.enums = enumerations,					\
+		.num_enums = ARRAY_SIZE(enumerations),			\
+		.val = &value						\
+	};								\
+	__module_param_call(MODULE_PARAM_PREFIX, name,			\
+			    &param_ops_enum,				\
+			    .enumeration = &__param_arr_##name,		\
+			    perm, -1, 0);				\
+	__MODULE_PARM_TYPE(name, "enumeration")
+
 enum hwparam_type {
 	hwparam_ioport,		/* Module parameter configures an I/O port */
 	hwparam_iomem,		/* Module parameter configures an I/O mem address */
diff --git a/kernel/params.c b/kernel/params.c
index 5b92310425c5..749fe42b1a44 100644
--- a/kernel/params.c
+++ b/kernel/params.c
@@ -534,6 +534,47 @@ const struct kernel_param_ops param_ops_string = {
 };
 EXPORT_SYMBOL(param_ops_string);
 
+int param_set_enum(const char *key, const struct kernel_param *kp)
+{
+	const struct kparam_enum *e = kp->enumeration;
+	unsigned int i;
+
+	for (i = 0; i < e->num_enums; i++) {
+		if (sysfs_streq(key, e->enums[i].key)) {
+			*(e->val) = e->enums[i].val;
+			return 0;
+		}
+	}
+
+	pr_err("%s: unknown key %s to enum parameter\n", kp->name, key);
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(param_set_enum);
+
+int param_get_enum(char *buffer, const struct kernel_param *kp)
+{
+	const struct kparam_enum *e = kp->enumeration;
+	unsigned int i;
+
+	for (i = 0; i < e->num_enums; i++) {
+		if (*(e->val) == e->enums[i].val)
+			return sysfs_emit(buffer, "%s\n", e->enums[i].key);
+	}
+
+	pr_err("%s: enum parameter set to unknown value %d\n",
+	       kp->name, *(e->val));
+
+	return sysfs_emit(buffer, "(unknown)\n");
+}
+EXPORT_SYMBOL(param_get_enum);
+
+const struct kernel_param_ops param_ops_enum = {
+	.set = param_set_enum,
+	.get = param_get_enum,
+};
+EXPORT_SYMBOL(param_ops_enum);
+
 /* sysfs output in /sys/modules/XYZ/parameters/ */
 #define to_module_attr(n) container_of(n, struct module_attribute, attr)
 #define to_module_kobject(n) container_of(n, struct module_kobject, kobj)
