All of lore.kernel.org
 help / color / mirror / Atom feed
From: Andrew Jeffery <andrew@aj.id.au>
To: linux-kernel@vger.kernel.org
Cc: Andrew Jeffery <andrew@aj.id.au>,
	robh+dt@kernel.org, mark.rutland@arm.com, joel@jms.id.au,
	gregkh@linuxfoundation.org, Eugene.Cho@dell.com,
	a.amelkin@yadro.com, stewart@linux.ibm.com,
	benh@kernel.crashing.org, openbmc@lists.ozlabs.org,
	devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org
Subject: [RFC PATCH 3/4] misc: Add bmc-misc-ctrl
Date: Tue,  3 Jul 2018 17:04:12 +1000	[thread overview]
Message-ID: <20180703070413.28756-4-andrew@aj.id.au> (raw)
In-Reply-To: <20180703070413.28756-1-andrew@aj.id.au>

bmc-misc-ctrl is used to expose miscellaneous Baseboard
Management Controller (BMC) hardware features described in the devicetree
to userspace.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 MAINTAINERS                  |   1 +
 drivers/misc/Kconfig         |   8 +
 drivers/misc/Makefile        |   1 +
 drivers/misc/bmc-misc-ctrl.c | 363 +++++++++++++++++++++++++++++++++++
 4 files changed, 373 insertions(+)
 create mode 100644 drivers/misc/bmc-misc-ctrl.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 9766d7832d8b..30d39440b6f2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2741,6 +2741,7 @@ R:	Andrew Jeffery <andrew@aj.id.au>
 L:	openbmc@lists.ozlabs.org (moderated for non-subscribers)
 S:	Supported
 F:	Documentation/devicetree/bindings/misc/bmc-misc-ctrl.txt
+F:	drivers/misc/bmc-misc-ctrl.c
 
 BPF (Safe dynamic programs and tools)
 M:	Alexei Starovoitov <ast@kernel.org>
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 3726eacdf65d..f46bc8208b50 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -513,6 +513,14 @@ config MISC_RTSX
 	tristate
 	default MISC_RTSX_PCI || MISC_RTSX_USB
 
+config BMC_MISC_CTRL
+	tristate "Miscellaneous BMC Control Interfaces"
+	depends on REGMAP && MFD_SYSCON
+	help
+	  Say yes to expose scratch registers used to communicate between the
+	  host and BMC along with other miscellaneous control interfaces
+	  provided by BMC SoCs
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index af22bbc3d00c..4fb2fac7a486 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -58,3 +58,4 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP)	+= aspeed-lpc-snoop.o
 obj-$(CONFIG_PCI_ENDPOINT_TEST)	+= pci_endpoint_test.o
 obj-$(CONFIG_OCXL)		+= ocxl/
 obj-$(CONFIG_MISC_RTSX)		+= cardreader/
+obj-$(CONFIG_BMC_MISC_CTRL)     += bmc-misc-ctrl.o
diff --git a/drivers/misc/bmc-misc-ctrl.c b/drivers/misc/bmc-misc-ctrl.c
new file mode 100644
index 000000000000..ff907029163f
--- /dev/null
+++ b/drivers/misc/bmc-misc-ctrl.c
@@ -0,0 +1,363 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2018 IBM Corp.
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/kobject.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+struct bmc_ctrl {
+	struct device *dev;
+	struct regmap *map;
+	bool ro;
+	u32 shift;
+	u32 mask;
+	struct kobj_attribute mask_attr;
+	const char *label;
+	struct kobj_attribute label_attr;
+	union {
+		struct {
+			u32 value;
+			struct kobj_attribute value_attr;
+		};
+		struct {
+			u32 read;
+			u32 set;
+			u32 clear;
+			struct kobj_attribute read_attr;
+			struct kobj_attribute set_attr;
+			struct kobj_attribute clear_attr;
+		};
+	};
+};
+
+static ssize_t bmc_misc_rmw_show(struct kobject *kobj,
+					struct kobj_attribute *attr, char *buf)
+{
+	struct bmc_ctrl *ctrl;
+	unsigned int val;
+	int rc;
+
+	ctrl = container_of(attr, struct bmc_ctrl, value_attr);
+	rc = regmap_read(ctrl->map, ctrl->value, &val);
+	if (rc)
+		return rc;
+
+	val = (val & ctrl->mask) >> ctrl->shift;
+
+	return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t bmc_misc_rmw_store(struct kobject *kobj,
+					 struct kobj_attribute *attr,
+					 const char *buf, size_t count)
+{
+	struct bmc_ctrl *ctrl;
+	long val;
+	int rc;
+
+	rc = kstrtol(buf, 0, &val);
+	if (rc)
+		return rc;
+
+	ctrl = container_of(attr, struct bmc_ctrl, value_attr);
+	val <<= ctrl->shift;
+	if (val & ~ctrl->mask)
+		return -EINVAL;
+	rc = regmap_update_bits(ctrl->map, ctrl->value, ctrl->mask, val);
+
+	return rc < 0 ? rc : count;
+}
+
+static void bmc_misc_add_rmw_attrs(struct bmc_ctrl *ctrl,
+				   struct attribute *attrs[6])
+{
+	sysfs_attr_init(&ctrl->attr.attr);
+	ctrl->value_attr.attr.name = "value";
+	ctrl->value_attr.attr.mode = 0664;
+	ctrl->value_attr.show = bmc_misc_rmw_show;
+	ctrl->value_attr.store = bmc_misc_rmw_store;
+	attrs[2] = &ctrl->value_attr.attr;
+	attrs[3] = NULL;
+}
+
+static int bmc_misc_init_rmw(struct bmc_ctrl *ctrl, struct device_node *node,
+			     struct attribute *attrs[6])
+{
+	u32 val;
+	int rc;
+
+	rc = of_property_read_u32(node, "offset", &ctrl->value);
+	if (rc < 0)
+		return rc;
+
+	if (!of_property_read_u32(node, "default-value", &val)) {
+		val <<= ctrl->shift;
+		if (val & ~ctrl->mask)
+			return -EINVAL;
+		val &= ctrl->mask;
+		regmap_update_bits(ctrl->map, ctrl->value, ctrl->mask, val);
+	}
+
+	bmc_misc_add_rmw_attrs(ctrl, attrs);
+
+	return 0;
+}
+
+static ssize_t bmc_misc_sc_read_show(struct kobject *kobj,
+					struct kobj_attribute *attr, char *buf)
+{
+	struct bmc_ctrl *ctrl;
+	unsigned int val;
+	int rc;
+
+	ctrl = container_of(attr, struct bmc_ctrl, read_attr);
+	rc = regmap_read(ctrl->map, ctrl->read, &val);
+	if (rc)
+		return rc;
+
+	val = (val & ctrl->mask) >> ctrl->shift;
+
+	return sprintf(buf, "%u\n", val);
+
+}
+
+static ssize_t bmc_misc_sc_set_store(struct kobject *kobj,
+					    struct kobj_attribute *attr,
+					    const char *buf, size_t count)
+{
+	struct bmc_ctrl *ctrl;
+	long val;
+	int rc;
+
+	rc = kstrtol(buf, 0, &val);
+	if (rc)
+		return rc;
+
+	ctrl = container_of(attr, struct bmc_ctrl, set_attr);
+	val <<= ctrl->shift;
+	if (val & ~ctrl->mask)
+		return -EINVAL;
+	val &= ctrl->mask;
+	rc = regmap_write(ctrl->map, ctrl->set, val);
+
+	return rc < 0 ? rc : count;
+}
+
+static ssize_t bmc_misc_sc_clear_store(struct kobject *kobj,
+					      struct kobj_attribute *attr,
+					      const char *buf, size_t count)
+{
+	struct bmc_ctrl *ctrl;
+	long val;
+	int rc;
+
+	rc = kstrtol(buf, 0, &val);
+	if (rc)
+		return rc;
+
+	ctrl = container_of(attr, struct bmc_ctrl, clear_attr);
+	val <<= ctrl->shift;
+	if (val & ~ctrl->mask)
+		return -EINVAL;
+	val &= ctrl->mask;
+	rc = regmap_write(ctrl->map, ctrl->clear, val);
+
+	return rc < 0 ? rc : count;
+}
+
+static void bmc_misc_add_sc_attrs(struct bmc_ctrl *ctrl,
+				  struct attribute *attrs[6])
+{
+	sysfs_attr_init(&ctrl->read_attr.attr);
+	ctrl->read_attr.attr.name = "value";
+	ctrl->read_attr.attr.mode = 0444;
+	ctrl->read_attr.show = bmc_misc_sc_read_show;
+	attrs[2] = &ctrl->read_attr.attr;
+
+	sysfs_attr_init(&ctrl->set_attr.attr);
+	ctrl->set_attr.attr.name = "set";
+	ctrl->set_attr.attr.mode = 0200;
+	ctrl->set_attr.store = bmc_misc_sc_set_store;
+	attrs[3] = &ctrl->set_attr.attr;
+
+	sysfs_attr_init(&ctrl->clear_attr.attr);
+	ctrl->clear_attr.attr.name = "clear";
+	ctrl->clear_attr.attr.mode = 0200;
+	ctrl->clear_attr.store = bmc_misc_sc_clear_store;
+	attrs[4] = &ctrl->clear_attr.attr;
+
+	attrs[5] = NULL;
+}
+
+static int bmc_misc_init_sc(struct bmc_ctrl *ctrl, struct device_node *node,
+			    struct attribute *attrs[6])
+{
+	int rc;
+
+	rc = of_property_read_u32_array(node, "offset", &ctrl->read, 3);
+	if (rc < 0)
+		return rc;
+
+	if (of_property_read_bool(node, "default-set"))
+		regmap_write(ctrl->map, ctrl->set, ctrl->mask);
+	else if (of_property_read_bool(node, "default-clear"))
+		regmap_write(ctrl->map, ctrl->clear, ctrl->mask);
+
+	bmc_misc_add_sc_attrs(ctrl, attrs);
+
+	return 0;
+}
+
+static ssize_t bmc_misc_label_show(struct kobject *kobj,
+				   struct kobj_attribute *attr, char *buf)
+{
+	struct bmc_ctrl *ctrl = container_of(attr, struct bmc_ctrl, label_attr);
+
+	return sprintf(buf, "%s\n", ctrl->label);
+}
+
+static ssize_t bmc_misc_mask_show(struct kobject *kobj,
+				   struct kobj_attribute *attr, char *buf)
+{
+	struct bmc_ctrl *ctrl = container_of(attr, struct bmc_ctrl, mask_attr);
+
+	return sprintf(buf, "0x%x\n", ctrl->mask >> ctrl->shift);
+}
+
+static int bmc_misc_init_dt(struct bmc_ctrl *ctrl, struct device_node *node,
+			    struct attribute *attrs[6])
+{
+	int rc;
+
+	/* Example children:
+	 *
+	 * field@80.6 {
+	 *      compatible = "bmc-misc-control";
+	 *      reg = <0x80>;
+	 *      bit-mask = <0x1>;
+	 *      bit-shift = <6>;
+	 *      label = "ilpc2ahb-disable";
+	 * };
+	 *
+	 * field@70.6 {
+	 *      compatible = "bmc-misc-control";
+	 *      // Write-1-set/Write-1-clear semantics
+	 *      set-clear;
+	 *      default-set;
+	 *      reg = <0x70 0x70 0x7c>
+	 *      bit-mask = <0x1>;
+	 *      bit-shift = <6>;
+	 *      label = "lpc-sio-decode-disable";
+	 * };
+	 *
+	 * field@50.0 {
+	 *	compatible = "bmc-misc-control";
+	 *	read-only;
+	 *	reg = <0x50>;
+	 *	bit-mask = <0xffffffff>;
+	 *	bit-shift = <0>;
+	 *	label = "vga-scratch-1";
+	 * };
+	 */
+	rc = of_property_read_u32(node, "mask", &ctrl->mask);
+	if (rc < 0)
+		return rc;
+
+	ctrl->shift = __ffs(ctrl->mask);
+	ctrl->ro = of_property_read_bool(node, "read-only");
+
+	rc = of_property_read_string(node, "label", &ctrl->label);
+	if (rc < 0)
+		return rc;
+
+	ctrl->label_attr.attr.name = "label";
+	ctrl->label_attr.attr.mode = 0444;
+	ctrl->label_attr.show = bmc_misc_label_show;
+	attrs[0] = &ctrl->label_attr.attr;
+
+	ctrl->mask_attr.attr.name = "mask";
+	ctrl->mask_attr.attr.mode = 0444;
+	ctrl->mask_attr.show = bmc_misc_mask_show;
+	attrs[1] = &ctrl->mask_attr.attr;
+
+	if (of_property_read_bool(node, "set-clear"))
+		return bmc_misc_init_sc(ctrl, node, attrs);
+
+	return bmc_misc_init_rmw(ctrl, node, attrs);
+}
+
+static struct class bmc_class = {
+	.name =		"bmc",
+};
+
+static int bmc_misc_probe(struct platform_device *pdev)
+{
+	struct attribute *attrs[6];
+	struct attribute **attr;
+	struct bmc_ctrl *ctrl;
+	int rc;
+
+	ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL);
+	if (!ctrl)
+		return -ENOMEM;
+
+	ctrl->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
+
+	rc = bmc_misc_init_dt(ctrl, pdev->dev.of_node, attrs);
+	if (rc < 0)
+		return rc;
+
+	ctrl->dev = device_create(&bmc_class, &pdev->dev, 0, ctrl, "%s",
+			     ctrl->label);
+	if (IS_ERR(ctrl->dev))
+		return PTR_ERR(ctrl->dev);
+
+	for (attr = &attrs[0]; *attr; attr++) {
+		rc = sysfs_create_file(&ctrl->dev->kobj, *attr);
+		if (rc < 0)
+			/* FIXME: Cleanup */
+			return rc;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id bmc_misc_match[] = {
+	{ .compatible = "bmc-misc-ctrl" },
+	{ },
+};
+
+static struct platform_driver bmc_misc = {
+	.driver = {
+		.name		=  "bmc-misc-ctrl",
+		.of_match_table = bmc_misc_match,
+	},
+	.probe = bmc_misc_probe,
+};
+
+static int bmc_misc_ctrl_init(void)
+{
+	int rc;
+
+	rc = class_register(&bmc_class);
+	if (rc < 0)
+		return rc;
+
+	return platform_driver_register(&bmc_misc);
+}
+module_init(bmc_misc_ctrl_init);
+
+static void bmc_misc_ctrl_exit(void)
+{
+	platform_driver_unregister(&bmc_misc);
+	class_unregister(&bmc_class);
+}
+module_exit(bmc_misc_ctrl_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
-- 
2.17.1


WARNING: multiple messages have this Message-ID (diff)
From: andrew@aj.id.au (Andrew Jeffery)
To: linux-arm-kernel@lists.infradead.org
Subject: [RFC PATCH 3/4] misc: Add bmc-misc-ctrl
Date: Tue,  3 Jul 2018 17:04:12 +1000	[thread overview]
Message-ID: <20180703070413.28756-4-andrew@aj.id.au> (raw)
In-Reply-To: <20180703070413.28756-1-andrew@aj.id.au>

bmc-misc-ctrl is used to expose miscellaneous Baseboard
Management Controller (BMC) hardware features described in the devicetree
to userspace.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
---
 MAINTAINERS                  |   1 +
 drivers/misc/Kconfig         |   8 +
 drivers/misc/Makefile        |   1 +
 drivers/misc/bmc-misc-ctrl.c | 363 +++++++++++++++++++++++++++++++++++
 4 files changed, 373 insertions(+)
 create mode 100644 drivers/misc/bmc-misc-ctrl.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 9766d7832d8b..30d39440b6f2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2741,6 +2741,7 @@ R:	Andrew Jeffery <andrew@aj.id.au>
 L:	openbmc at lists.ozlabs.org (moderated for non-subscribers)
 S:	Supported
 F:	Documentation/devicetree/bindings/misc/bmc-misc-ctrl.txt
+F:	drivers/misc/bmc-misc-ctrl.c
 
 BPF (Safe dynamic programs and tools)
 M:	Alexei Starovoitov <ast@kernel.org>
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 3726eacdf65d..f46bc8208b50 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -513,6 +513,14 @@ config MISC_RTSX
 	tristate
 	default MISC_RTSX_PCI || MISC_RTSX_USB
 
+config BMC_MISC_CTRL
+	tristate "Miscellaneous BMC Control Interfaces"
+	depends on REGMAP && MFD_SYSCON
+	help
+	  Say yes to expose scratch registers used to communicate between the
+	  host and BMC along with other miscellaneous control interfaces
+	  provided by BMC SoCs
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index af22bbc3d00c..4fb2fac7a486 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -58,3 +58,4 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP)	+= aspeed-lpc-snoop.o
 obj-$(CONFIG_PCI_ENDPOINT_TEST)	+= pci_endpoint_test.o
 obj-$(CONFIG_OCXL)		+= ocxl/
 obj-$(CONFIG_MISC_RTSX)		+= cardreader/
+obj-$(CONFIG_BMC_MISC_CTRL)     += bmc-misc-ctrl.o
diff --git a/drivers/misc/bmc-misc-ctrl.c b/drivers/misc/bmc-misc-ctrl.c
new file mode 100644
index 000000000000..ff907029163f
--- /dev/null
+++ b/drivers/misc/bmc-misc-ctrl.c
@@ -0,0 +1,363 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2018 IBM Corp.
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/kobject.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+struct bmc_ctrl {
+	struct device *dev;
+	struct regmap *map;
+	bool ro;
+	u32 shift;
+	u32 mask;
+	struct kobj_attribute mask_attr;
+	const char *label;
+	struct kobj_attribute label_attr;
+	union {
+		struct {
+			u32 value;
+			struct kobj_attribute value_attr;
+		};
+		struct {
+			u32 read;
+			u32 set;
+			u32 clear;
+			struct kobj_attribute read_attr;
+			struct kobj_attribute set_attr;
+			struct kobj_attribute clear_attr;
+		};
+	};
+};
+
+static ssize_t bmc_misc_rmw_show(struct kobject *kobj,
+					struct kobj_attribute *attr, char *buf)
+{
+	struct bmc_ctrl *ctrl;
+	unsigned int val;
+	int rc;
+
+	ctrl = container_of(attr, struct bmc_ctrl, value_attr);
+	rc = regmap_read(ctrl->map, ctrl->value, &val);
+	if (rc)
+		return rc;
+
+	val = (val & ctrl->mask) >> ctrl->shift;
+
+	return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t bmc_misc_rmw_store(struct kobject *kobj,
+					 struct kobj_attribute *attr,
+					 const char *buf, size_t count)
+{
+	struct bmc_ctrl *ctrl;
+	long val;
+	int rc;
+
+	rc = kstrtol(buf, 0, &val);
+	if (rc)
+		return rc;
+
+	ctrl = container_of(attr, struct bmc_ctrl, value_attr);
+	val <<= ctrl->shift;
+	if (val & ~ctrl->mask)
+		return -EINVAL;
+	rc = regmap_update_bits(ctrl->map, ctrl->value, ctrl->mask, val);
+
+	return rc < 0 ? rc : count;
+}
+
+static void bmc_misc_add_rmw_attrs(struct bmc_ctrl *ctrl,
+				   struct attribute *attrs[6])
+{
+	sysfs_attr_init(&ctrl->attr.attr);
+	ctrl->value_attr.attr.name = "value";
+	ctrl->value_attr.attr.mode = 0664;
+	ctrl->value_attr.show = bmc_misc_rmw_show;
+	ctrl->value_attr.store = bmc_misc_rmw_store;
+	attrs[2] = &ctrl->value_attr.attr;
+	attrs[3] = NULL;
+}
+
+static int bmc_misc_init_rmw(struct bmc_ctrl *ctrl, struct device_node *node,
+			     struct attribute *attrs[6])
+{
+	u32 val;
+	int rc;
+
+	rc = of_property_read_u32(node, "offset", &ctrl->value);
+	if (rc < 0)
+		return rc;
+
+	if (!of_property_read_u32(node, "default-value", &val)) {
+		val <<= ctrl->shift;
+		if (val & ~ctrl->mask)
+			return -EINVAL;
+		val &= ctrl->mask;
+		regmap_update_bits(ctrl->map, ctrl->value, ctrl->mask, val);
+	}
+
+	bmc_misc_add_rmw_attrs(ctrl, attrs);
+
+	return 0;
+}
+
+static ssize_t bmc_misc_sc_read_show(struct kobject *kobj,
+					struct kobj_attribute *attr, char *buf)
+{
+	struct bmc_ctrl *ctrl;
+	unsigned int val;
+	int rc;
+
+	ctrl = container_of(attr, struct bmc_ctrl, read_attr);
+	rc = regmap_read(ctrl->map, ctrl->read, &val);
+	if (rc)
+		return rc;
+
+	val = (val & ctrl->mask) >> ctrl->shift;
+
+	return sprintf(buf, "%u\n", val);
+
+}
+
+static ssize_t bmc_misc_sc_set_store(struct kobject *kobj,
+					    struct kobj_attribute *attr,
+					    const char *buf, size_t count)
+{
+	struct bmc_ctrl *ctrl;
+	long val;
+	int rc;
+
+	rc = kstrtol(buf, 0, &val);
+	if (rc)
+		return rc;
+
+	ctrl = container_of(attr, struct bmc_ctrl, set_attr);
+	val <<= ctrl->shift;
+	if (val & ~ctrl->mask)
+		return -EINVAL;
+	val &= ctrl->mask;
+	rc = regmap_write(ctrl->map, ctrl->set, val);
+
+	return rc < 0 ? rc : count;
+}
+
+static ssize_t bmc_misc_sc_clear_store(struct kobject *kobj,
+					      struct kobj_attribute *attr,
+					      const char *buf, size_t count)
+{
+	struct bmc_ctrl *ctrl;
+	long val;
+	int rc;
+
+	rc = kstrtol(buf, 0, &val);
+	if (rc)
+		return rc;
+
+	ctrl = container_of(attr, struct bmc_ctrl, clear_attr);
+	val <<= ctrl->shift;
+	if (val & ~ctrl->mask)
+		return -EINVAL;
+	val &= ctrl->mask;
+	rc = regmap_write(ctrl->map, ctrl->clear, val);
+
+	return rc < 0 ? rc : count;
+}
+
+static void bmc_misc_add_sc_attrs(struct bmc_ctrl *ctrl,
+				  struct attribute *attrs[6])
+{
+	sysfs_attr_init(&ctrl->read_attr.attr);
+	ctrl->read_attr.attr.name = "value";
+	ctrl->read_attr.attr.mode = 0444;
+	ctrl->read_attr.show = bmc_misc_sc_read_show;
+	attrs[2] = &ctrl->read_attr.attr;
+
+	sysfs_attr_init(&ctrl->set_attr.attr);
+	ctrl->set_attr.attr.name = "set";
+	ctrl->set_attr.attr.mode = 0200;
+	ctrl->set_attr.store = bmc_misc_sc_set_store;
+	attrs[3] = &ctrl->set_attr.attr;
+
+	sysfs_attr_init(&ctrl->clear_attr.attr);
+	ctrl->clear_attr.attr.name = "clear";
+	ctrl->clear_attr.attr.mode = 0200;
+	ctrl->clear_attr.store = bmc_misc_sc_clear_store;
+	attrs[4] = &ctrl->clear_attr.attr;
+
+	attrs[5] = NULL;
+}
+
+static int bmc_misc_init_sc(struct bmc_ctrl *ctrl, struct device_node *node,
+			    struct attribute *attrs[6])
+{
+	int rc;
+
+	rc = of_property_read_u32_array(node, "offset", &ctrl->read, 3);
+	if (rc < 0)
+		return rc;
+
+	if (of_property_read_bool(node, "default-set"))
+		regmap_write(ctrl->map, ctrl->set, ctrl->mask);
+	else if (of_property_read_bool(node, "default-clear"))
+		regmap_write(ctrl->map, ctrl->clear, ctrl->mask);
+
+	bmc_misc_add_sc_attrs(ctrl, attrs);
+
+	return 0;
+}
+
+static ssize_t bmc_misc_label_show(struct kobject *kobj,
+				   struct kobj_attribute *attr, char *buf)
+{
+	struct bmc_ctrl *ctrl = container_of(attr, struct bmc_ctrl, label_attr);
+
+	return sprintf(buf, "%s\n", ctrl->label);
+}
+
+static ssize_t bmc_misc_mask_show(struct kobject *kobj,
+				   struct kobj_attribute *attr, char *buf)
+{
+	struct bmc_ctrl *ctrl = container_of(attr, struct bmc_ctrl, mask_attr);
+
+	return sprintf(buf, "0x%x\n", ctrl->mask >> ctrl->shift);
+}
+
+static int bmc_misc_init_dt(struct bmc_ctrl *ctrl, struct device_node *node,
+			    struct attribute *attrs[6])
+{
+	int rc;
+
+	/* Example children:
+	 *
+	 * field at 80.6 {
+	 *      compatible = "bmc-misc-control";
+	 *      reg = <0x80>;
+	 *      bit-mask = <0x1>;
+	 *      bit-shift = <6>;
+	 *      label = "ilpc2ahb-disable";
+	 * };
+	 *
+	 * field at 70.6 {
+	 *      compatible = "bmc-misc-control";
+	 *      // Write-1-set/Write-1-clear semantics
+	 *      set-clear;
+	 *      default-set;
+	 *      reg = <0x70 0x70 0x7c>
+	 *      bit-mask = <0x1>;
+	 *      bit-shift = <6>;
+	 *      label = "lpc-sio-decode-disable";
+	 * };
+	 *
+	 * field at 50.0 {
+	 *	compatible = "bmc-misc-control";
+	 *	read-only;
+	 *	reg = <0x50>;
+	 *	bit-mask = <0xffffffff>;
+	 *	bit-shift = <0>;
+	 *	label = "vga-scratch-1";
+	 * };
+	 */
+	rc = of_property_read_u32(node, "mask", &ctrl->mask);
+	if (rc < 0)
+		return rc;
+
+	ctrl->shift = __ffs(ctrl->mask);
+	ctrl->ro = of_property_read_bool(node, "read-only");
+
+	rc = of_property_read_string(node, "label", &ctrl->label);
+	if (rc < 0)
+		return rc;
+
+	ctrl->label_attr.attr.name = "label";
+	ctrl->label_attr.attr.mode = 0444;
+	ctrl->label_attr.show = bmc_misc_label_show;
+	attrs[0] = &ctrl->label_attr.attr;
+
+	ctrl->mask_attr.attr.name = "mask";
+	ctrl->mask_attr.attr.mode = 0444;
+	ctrl->mask_attr.show = bmc_misc_mask_show;
+	attrs[1] = &ctrl->mask_attr.attr;
+
+	if (of_property_read_bool(node, "set-clear"))
+		return bmc_misc_init_sc(ctrl, node, attrs);
+
+	return bmc_misc_init_rmw(ctrl, node, attrs);
+}
+
+static struct class bmc_class = {
+	.name =		"bmc",
+};
+
+static int bmc_misc_probe(struct platform_device *pdev)
+{
+	struct attribute *attrs[6];
+	struct attribute **attr;
+	struct bmc_ctrl *ctrl;
+	int rc;
+
+	ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL);
+	if (!ctrl)
+		return -ENOMEM;
+
+	ctrl->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
+
+	rc = bmc_misc_init_dt(ctrl, pdev->dev.of_node, attrs);
+	if (rc < 0)
+		return rc;
+
+	ctrl->dev = device_create(&bmc_class, &pdev->dev, 0, ctrl, "%s",
+			     ctrl->label);
+	if (IS_ERR(ctrl->dev))
+		return PTR_ERR(ctrl->dev);
+
+	for (attr = &attrs[0]; *attr; attr++) {
+		rc = sysfs_create_file(&ctrl->dev->kobj, *attr);
+		if (rc < 0)
+			/* FIXME: Cleanup */
+			return rc;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id bmc_misc_match[] = {
+	{ .compatible = "bmc-misc-ctrl" },
+	{ },
+};
+
+static struct platform_driver bmc_misc = {
+	.driver = {
+		.name		=  "bmc-misc-ctrl",
+		.of_match_table = bmc_misc_match,
+	},
+	.probe = bmc_misc_probe,
+};
+
+static int bmc_misc_ctrl_init(void)
+{
+	int rc;
+
+	rc = class_register(&bmc_class);
+	if (rc < 0)
+		return rc;
+
+	return platform_driver_register(&bmc_misc);
+}
+module_init(bmc_misc_ctrl_init);
+
+static void bmc_misc_ctrl_exit(void)
+{
+	platform_driver_unregister(&bmc_misc);
+	class_unregister(&bmc_class);
+}
+module_exit(bmc_misc_ctrl_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
-- 
2.17.1

  parent reply	other threads:[~2018-07-03  7:04 UTC|newest]

Thread overview: 36+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-07-03  7:04 [RFC PATCH 0/4] sysfs interface to miscellaneous BMC controls and fields Andrew Jeffery
2018-07-03  7:04 ` Andrew Jeffery
2018-07-03  7:04 ` [RFC PATCH 1/4] dts: misc: Add bindings documentation for bmc-misc-ctrl Andrew Jeffery
2018-07-03  7:04   ` Andrew Jeffery
2018-07-03  7:50   ` Greg KH
2018-07-03  7:50     ` Greg KH
2018-07-03 14:16     ` Benjamin Herrenschmidt
2018-07-03 14:16       ` Benjamin Herrenschmidt
2018-07-03 14:16       ` Benjamin Herrenschmidt
2018-07-03 14:31       ` Greg KH
2018-07-03 14:31         ` Greg KH
2018-07-03 15:39         ` Benjamin Herrenschmidt
2018-07-03 15:39           ` Benjamin Herrenschmidt
2018-07-03 15:39           ` Benjamin Herrenschmidt
2018-07-04  6:28     ` Andrew Jeffery
2018-07-04  6:28       ` Andrew Jeffery
2018-07-04  6:28       ` Andrew Jeffery
2018-07-03  7:04 ` [RFC PATCH 2/4] Documentation: ABI: Add sysfs-class-bmc documentation to testing Andrew Jeffery
2018-07-03  7:04   ` Andrew Jeffery
2018-07-03  7:50   ` Greg KH
2018-07-03  7:50     ` Greg KH
2018-07-03  7:50     ` Greg KH
2018-07-04  6:29     ` Andrew Jeffery
2018-07-04  6:29       ` Andrew Jeffery
2018-07-03  7:04 ` Andrew Jeffery [this message]
2018-07-03  7:04   ` [RFC PATCH 3/4] misc: Add bmc-misc-ctrl Andrew Jeffery
2018-07-03  7:54   ` Greg KH
2018-07-03  7:54     ` Greg KH
2018-07-04  7:18     ` Andrew Jeffery
2018-07-04  7:18       ` Andrew Jeffery
2018-07-03  7:04 ` [RFC PATCH 4/4] dts: aspeed-g5: Add bmc-misc-ctrl nodes to devicetree Andrew Jeffery
2018-07-03  7:04   ` Andrew Jeffery
2018-07-03  7:54   ` Greg KH
2018-07-03  7:54     ` Greg KH
2018-07-04  6:29     ` Andrew Jeffery
2018-07-04  6:29       ` Andrew Jeffery

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180703070413.28756-4-andrew@aj.id.au \
    --to=andrew@aj.id.au \
    --cc=Eugene.Cho@dell.com \
    --cc=a.amelkin@yadro.com \
    --cc=benh@kernel.crashing.org \
    --cc=devicetree@vger.kernel.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=joel@jms.id.au \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=openbmc@lists.ozlabs.org \
    --cc=robh+dt@kernel.org \
    --cc=stewart@linux.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.