[PATCH] Title: Select GPIO command source.

From: peteryin
Date: Wed Aug 23 2023 - 12:10:24 EST


From: peteryin <peter.yin@xxxxxxxxxxxx>

Description:
The capability to choose the GPIO command source
between ARM LPC and Coprocessor CPU is supported.

Test Plan:
Get Bank gpio command source
e.g.
cd /sys/bus/platform/drivers/aspeed-command-source/
cat 1e780000.gpio-command-source/bank_abcd
ARM ARM ARM ARM

Set Bank gpio command source.
e.g.
cd /sys/bus/platform/drivers/aspeed-command-source/

echo "A ARM" > 1e780000.gpio-command-source/bank_abcd
or
echo "A LPC" > 1e780000.gpio-command-source/bank_abcd
or$
echo "A COP" > 1e780000.gpio-command-source/bank_abcd

Signed-off-by: peteryin <peteryin.openbmc@xxxxxxxxx>
---
.../sysfs-driver-aspeed-gpio-command-source | 24 ++
.../soc/aspeed/gpio-command-source.yaml | 58 ++++
drivers/soc/aspeed/Kconfig | 9 +
drivers/soc/aspeed/Makefile | 1 +
drivers/soc/aspeed/aspeed-command-source.c | 266 ++++++++++++++++++
5 files changed, 358 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-driver-aspeed-gpio-command-source
create mode 100644 Documentation/devicetree/bindings/soc/aspeed/gpio-command-source.yaml
create mode 100644 drivers/soc/aspeed/aspeed-command-source.c

diff --git a/Documentation/ABI/testing/sysfs-driver-aspeed-gpio-command-source b/Documentation/ABI/testing/sysfs-driver-aspeed-gpio-command-source
new file mode 100644
index 000000000000..4698f47a1f75
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-driver-aspeed-gpio-command-source
@@ -0,0 +1,24 @@
+What: /sys/bus/platform/drivers/aspeed-command-source/\*command\*/bank\*
+Date: August 2023
+Contact: Peter Yin <peter.yin@xxxxxxxxxxxx>
+Description: Get or set the gpio command source for ARM, LPC or Coprocessor CPU.
+
+ When read, each file shows the list of available options with bank
+ that depends on the selected bank file.
+
+ e.g.
+ get gpio command source
+ cd /sys/bus/platform/drivers/aspeed-command-source/
+ cat 1e780000.gpio-command-source/bank_abcd
+ ARM ARM ARM ARM
+ In this case, gets bank gpio command source.
+
+
+ e.g.
+ set gpio command source
+ cd /sys/bus/platform/drivers/aspeed-command-source/
+ echo "A ARM" > 1e780000.gpio-command-source/bank_abcd
+ or
+ echo "A LPC" > 1e780000.gpio-command-source/bank_abcd
+ or
+ echo "A COP" > 1e780000.gpio-command-source/bank_abcd
diff --git a/Documentation/devicetree/bindings/soc/aspeed/gpio-command-source.yaml b/Documentation/devicetree/bindings/soc/aspeed/gpio-command-source.yaml
new file mode 100644
index 000000000000..034183667501
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/aspeed/gpio-command-source.yaml
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# # Copyright (c) 2023 Quanta Inc.
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/soc/aspeed/gpio-command-source.yaml#";
+$schema: "http://devicetree.org/meta-schemas/core.yaml#";
+
+title: Aspeed UART Routing Controller
+
+maintainers:
+ - Peter Yin <peter.yin@xxxxxxxxxxxx>
+
+description:
+ The Aspeed gpio command source control allow to dynamically write the inputs for
+ the built-in gpio command source.
+
+ This allows, for example, to connect the gpio command source to ARM LPC or Coprocessor CPU.
+ e.g. let LPC port80 to connect the gpio group.
+
+ This driver is for the BMC side. The sysfs files allow the BMC userspace
+ which owns the system configuration policy, to configure gpio command source.
+
+properties:
+ compatible:
+ items:
+ - enum:
+ - aspeed,ast2600-gpio-command-source
+ reg:
+ maxItems: 1
+
+required:
+ - compatible
+
+additionalProperties: false
+
+examples:
+ - |
+ gpio0: gpio@1e780000 {
+ #gpio-cells = <2>;
+ gpio-controller;
+ compatible = "aspeed,ast2600-gpio", "simple-mfd", "syscon";
+ reg = <0x1e780000 0x400>;
+ interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
+ gpio-ranges = <&pinctrl 0 0 208>;
+ ngpios = <208>;
+ clocks = <&syscon ASPEED_CLK_APB2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x0 0x1e780000 0x400>;
+ gpio_command_source: gpio-command-source@0 {
+ compatible = "aspeed,ast2600-gpio-command-source";
+ reg = <0x0 0x400>;
+ status = "disabled";
+ };
+ };
diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig
index bdea4b0a687b..066bea90bd00 100644
--- a/drivers/soc/aspeed/Kconfig
+++ b/drivers/soc/aspeed/Kconfig
@@ -34,6 +34,15 @@ config ASPEED_UART_ROUTING
users to perform runtime configuration of the RX muxes among
the UART controllers and I/O pins.

+config ASPEED_COMMAND_SOURCE
+ tristate "ASPEED gpio command source control"
+ select REGMAP
+ select MFD_SYSCON
+ default ARCH_ASPEED
+ help
+ Provides a driver to control the gpio command source to ARM,
+ LPC or Coprocessor.
+
config ASPEED_P2A_CTRL
tristate "ASPEED P2A (VGA MMIO to BMC) bridge control"
select REGMAP
diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile
index 224127a1dd55..3246f41fe2b2 100644
--- a/drivers/soc/aspeed/Makefile
+++ b/drivers/soc/aspeed/Makefile
@@ -2,6 +2,7 @@
obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o
obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
obj-$(CONFIG_ASPEED_UART_ROUTING) += aspeed-uart-routing.o
+obj-$(CONFIG_ASPEED_COMMAND_SOURCE) += aspeed-command-source.o
obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o
obj-$(CONFIG_ASPEED_SOCINFO) += aspeed-socinfo.o
obj-$(CONFIG_ASPEED_SBC) += aspeed-sbc.o
diff --git a/drivers/soc/aspeed/aspeed-command-source.c b/drivers/soc/aspeed/aspeed-command-source.c
new file mode 100644
index 000000000000..ecf15b56c1a6
--- /dev/null
+++ b/drivers/soc/aspeed/aspeed-command-source.c
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2023 Quanta Inc.
+ */
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+
+/* register offsets */
+#define GPIO60 0x60
+#define GPIO68 0x68
+#define GPIO90 0x90
+#define GPIOE0 0xE0
+#define GPIO110 0x110
+#define GPIO140 0x140
+#define GPIO170 0x170
+
+/* attributes options */
+#define GPIO_COMMAND_SOURCE_ABCD "bank_abcd"
+#define GPIO_COMMAND_SOURCE_EFGH "bank_efgh"
+#define GPIO_COMMAND_SOURCE_IJKL "bank_ijkl"
+#define GPIO_COMMAND_SOURCE_MNOP "bank_mnop"
+#define GPIO_COMMAND_SOURCE_QRST "bank_qrst"
+#define GPIO_COMMAND_SOURCE_UVWX "bank_uvwx"
+#define GPIO_COMMAND_SOURCE_YZ "bank_yz"
+#define GPIO_BANK_SIZE (4)
+
+#define COMMAND_SOURCE1_OFFSET (4)
+#define GPIO_GPIO_GRUOP_OFFSET (8)
+
+struct aspeed_gpio_command_source {
+ struct regmap *map;
+ struct attribute_group const *attr_grp;
+};
+
+struct aspeed_gpio_command_source_selector {
+ struct device_attribute dev_attr;
+ uint32_t reg;
+ const char *const group[];
+};
+
+static const char *const options[] = {
+ "ARM", "LPC", "COP", "NON", NULL
+};
+
+
+#define to_routing_selector(_dev_attr) \
+ container_of(_dev_attr, struct aspeed_gpio_command_source_selector, dev_attr)
+
+static ssize_t aspeed_gpio_command_source_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t aspeed_gpio_command_source_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+#define ROUTING_ATTR(_name) { \
+ .attr = {.name = _name, \
+ .mode = VERIFY_OCTAL_PERMISSIONS(0644) }, \
+ .show = aspeed_gpio_command_source_show, \
+ .store = aspeed_gpio_command_source_store, \
+}
+
+/* routing selector for AST26xx */
+static struct aspeed_gpio_command_source_selector ast2600_bank_abcd = {
+ .dev_attr = ROUTING_ATTR(GPIO_COMMAND_SOURCE_ABCD),
+ .reg = GPIO60,
+ .group = { "A", "B", "C", "D", NULL},
+};
+
+static struct aspeed_gpio_command_source_selector ast2600_bank_efgh = {
+ .dev_attr = ROUTING_ATTR(GPIO_COMMAND_SOURCE_EFGH),
+ .reg = GPIO68,
+ .group = { "E", "F", "G", "H", NULL},
+
+};
+
+static struct aspeed_gpio_command_source_selector ast2600_bank_ijkl = {
+ .dev_attr = ROUTING_ATTR(GPIO_COMMAND_SOURCE_IJKL),
+ .reg = GPIO90,
+ .group = { "I", "J", "L", "L", NULL},
+};
+
+static struct aspeed_gpio_command_source_selector ast2600_bank_mnop = {
+ .dev_attr = ROUTING_ATTR(GPIO_COMMAND_SOURCE_MNOP),
+ .reg = GPIOE0,
+ .group = { "M", "N", "O", "P", NULL},
+};
+
+static struct aspeed_gpio_command_source_selector ast2600_bank_qrst = {
+ .dev_attr = ROUTING_ATTR(GPIO_COMMAND_SOURCE_QRST),
+ .reg = GPIO110,
+ .group = { "Q", "R", "S", "T", NULL},
+};
+
+static struct aspeed_gpio_command_source_selector ast2600_bank_uvwx = {
+ .dev_attr = ROUTING_ATTR(GPIO_COMMAND_SOURCE_UVWX),
+ .reg = GPIO140,
+ .group = { "U", "V", "W", "X", NULL},
+};
+
+static struct aspeed_gpio_command_source_selector ast2600_bank_yz = {
+ .dev_attr = ROUTING_ATTR(GPIO_COMMAND_SOURCE_YZ),
+ .reg = GPIO170,
+ .group = { "Y", "Z", NULL, NULL, NULL},
+};
+
+static struct attribute *ast2600_gpio_command_source_attrs[] = {
+ &ast2600_bank_abcd.dev_attr.attr,
+ &ast2600_bank_efgh.dev_attr.attr,
+ &ast2600_bank_ijkl.dev_attr.attr,
+ &ast2600_bank_mnop.dev_attr.attr,
+ &ast2600_bank_qrst.dev_attr.attr,
+ &ast2600_bank_uvwx.dev_attr.attr,
+ &ast2600_bank_yz.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ast2600_gpio_command_source_attr_group = {
+ .attrs = ast2600_gpio_command_source_attrs,
+};
+
+static ssize_t aspeed_gpio_command_source_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct aspeed_gpio_command_source *gpio_cmd_src = dev_get_drvdata(dev);
+ struct aspeed_gpio_command_source_selector *sel = to_routing_selector(attr);
+ uint8_t cmd_src0, cmd_src1;
+ uint32_t val1 = 0, val2 = 0;
+ int len = 0;
+
+ regmap_read(gpio_cmd_src->map, sel->reg, &val1);
+ regmap_read(gpio_cmd_src->map, sel->reg + GPIO_GPIO_GRUOP_OFFSET, &val2);
+
+ for (int i = 0; i < GPIO_BANK_SIZE; i++) {
+ cmd_src0 = (uint8_t)(val1 >> i*8);
+ cmd_src1 = (uint8_t)(val2 >> i*8);
+
+ if (cmd_src0 == 0 && cmd_src1 == 0)
+ len += sysfs_emit_at(buf, len, "%s ", options[0]);
+ else if (cmd_src0 == 1 && cmd_src1 == 0)
+ len += sysfs_emit_at(buf, len, "%s ", options[1]);
+ else if (cmd_src0 == 0 && cmd_src1 == 1)
+ len += sysfs_emit_at(buf, len, "%s ", options[2]);
+ else if (cmd_src0 == 1 && cmd_src1 == 1)
+ len += sysfs_emit_at(buf, len, "%s ", options[3]);
+ }
+
+ len += sysfs_emit_at(buf, len, "\n");
+ return len;
+}
+
+static ssize_t aspeed_gpio_command_source_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct aspeed_gpio_command_source *gpio_cmd_src = dev_get_drvdata(dev);
+ struct aspeed_gpio_command_source_selector *sel = to_routing_selector(attr);
+
+ char input1[4], input2[4];
+ int idx1 = -1, idx2 = -1;
+ uint8_t cmd_src0 = 0, cmd_src1 = 0;
+
+ if (count >= sizeof(input1) + sizeof(input2))
+ return -EINVAL; // Input is too long
+
+ if (sscanf(buf, "%3s %3s", input1, input2) != 2)
+ return -EINVAL; // Failed to parse input
+
+ idx1 = match_string(sel->group, -1, input1); //match gpio group
+ idx2 = match_string(options, -1, input2); //match action
+ if (idx1 < 0 || idx2 < 0) {
+ dev_err(dev, "invalid value idx1=%d,idx2=%d\n", idx1, idx2);
+ return -EINVAL;
+ }
+
+ if (idx2 == 0) { //ARM
+ cmd_src0 = 0;
+ cmd_src1 = 0;
+ } else if (idx2 == 1) { //LPC
+ cmd_src0 = 1;
+ cmd_src1 = 0;
+ } else if (idx2 == 2) { //Coprocessor CPU
+ cmd_src0 = 0;
+ cmd_src1 = 1;
+ } else if (idx2 == 3) { //Reserve
+ cmd_src0 = 1;
+ cmd_src1 = 1;
+ }
+
+ regmap_update_bits(gpio_cmd_src->map,
+ sel->reg,
+ 1 << (idx1*GPIO_GPIO_GRUOP_OFFSET),
+ cmd_src0 << (idx1 * GPIO_GPIO_GRUOP_OFFSET));
+
+ regmap_update_bits(gpio_cmd_src->map,
+ (sel->reg) + COMMAND_SOURCE1_OFFSET,
+ 1 << (idx1*GPIO_GPIO_GRUOP_OFFSET),
+ cmd_src1 << (idx1 * GPIO_GPIO_GRUOP_OFFSET));
+ return count;
+}
+
+static int aspeed_gpio_command_source_probe(struct platform_device *pdev)
+{
+ int rc;
+ struct device *dev = &pdev->dev;
+ struct aspeed_gpio_command_source *gpio_cmd_src;
+
+ gpio_cmd_src = devm_kzalloc(&pdev->dev, sizeof(*gpio_cmd_src), GFP_KERNEL);
+ if (!gpio_cmd_src)
+ return -ENOMEM;
+
+ gpio_cmd_src->map = syscon_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(gpio_cmd_src->map)) {
+ dev_err(dev, "cannot get regmap\n");
+ return PTR_ERR(gpio_cmd_src->map);
+ }
+
+ gpio_cmd_src->attr_grp = of_device_get_match_data(dev);
+
+ rc = sysfs_create_group(&dev->kobj, gpio_cmd_src->attr_grp);
+ if (rc < 0)
+ return rc;
+
+ dev_set_drvdata(dev, gpio_cmd_src);
+ dev_info(dev, "module probe success\n");
+
+ return 0;
+}
+
+static int aspeed_gpio_command_source_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct aspeed_gpio_command_source *gpio_cmd_src = platform_get_drvdata(pdev);
+
+ sysfs_remove_group(&dev->kobj, gpio_cmd_src->attr_grp);
+
+ return 0;
+}
+
+static const struct of_device_id aspeed_gpio_command_source_table[] = {
+ { .compatible = "aspeed,ast2600-gpio-command-source",
+ .data = &ast2600_gpio_command_source_attr_group },
+ { },
+};
+
+static struct platform_driver aspeed_gpio_command_source_driver = {
+ .driver = {
+ .name = "aspeed-gpio-command-source",
+ .of_match_table = aspeed_gpio_command_source_table,
+ },
+ .probe = aspeed_gpio_command_source_probe,
+ .remove = aspeed_gpio_command_source_remove,
+};
+
+module_platform_driver(aspeed_gpio_command_source_driver);
+
+MODULE_AUTHOR("Peter Yin <peter.yin@xxxxxxxxxxxx>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Driver to configure Aspeed GPIO Command Source");
--
2.25.1