Add the reset controller for the Microchip sparx5 / LAN969X / LAN966X
switch SoCs. The reset is asserted once at probe time so downstream
peripherals (SGPIO, MDIO, switch core) come up against a clean IP
state instead of inheriting whatever the previous boot stage left.

Ported from Linux drivers/reset/reset-microchip-sparx5.c at tag
v7.1-rc7

Signed-off-by: Oleksij Rempel <[email protected]>
---
 drivers/reset/Kconfig                  |  10 ++
 drivers/reset/Makefile                 |   1 +
 drivers/reset/reset-microchip-sparx5.c | 170 +++++++++++++++++++++++++
 3 files changed, 181 insertions(+)
 create mode 100644 drivers/reset/reset-microchip-sparx5.c

diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index c3779f087490..1819da73b27f 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -63,4 +63,14 @@ config RESET_SOCFPGA32
        help
          This enables the reset controller driver for 32-bit SoCFPGA platforms.
 
+config RESET_MICROCHIP_SPARX5
+       bool "Microchip Sparx5 / LAN969X / LAN966X switch reset driver"
+       depends on OFDEVICE
+       select MFD_SYSCON
+       help
+         This enables the switch reset controller driver for the Microchip
+         Sparx5 / LAN969X / LAN966X switch SoCs. The reset is asserted once
+         at probe time so downstream peripherals (SGPIO, MDIO, switch core)
+         see a clean IP state.
+
 endif
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index a446cb20d0b7..b5540252bc9a 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_RESET_SOCFPGA32) += reset-socfpga.o
 obj-$(CONFIG_RESET_IMX7) += reset-imx7.o
 obj-$(CONFIG_RESET_STARFIVE) += reset-starfive-vic.o
 obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
+obj-$(CONFIG_RESET_MICROCHIP_SPARX5) += reset-microchip-sparx5.o
diff --git a/drivers/reset/reset-microchip-sparx5.c 
b/drivers/reset/reset-microchip-sparx5.c
new file mode 100644
index 000000000000..8944e3fc1510
--- /dev/null
+++ b/drivers/reset/reset-microchip-sparx5.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Microchip Sparx5 / LAN969X / LAN966X Switch Reset driver
+ *
+ * Copyright (c) 2020 Microchip Technology Inc. and its subsidiaries.
+ *
+ * Ported from Linux drivers/reset/reset-microchip-sparx5.c. Identifiers,
+ * struct layouts and per-SoC props are kept aligned with Linux so future
+ * fixes can be backported with minimal context churn.
+ */
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <io.h>
+#include <of.h>
+#include <linux/err.h>
+#include <linux/iopoll.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+#include <mfd/syscon.h>
+
+struct reset_props {
+       u32 protect_reg;
+       u32 protect_bit;
+       u32 reset_reg;
+       u32 reset_bit;
+};
+
+struct mchp_reset_context {
+       struct regmap *cpu_ctrl;
+       struct regmap *gcb_ctrl;
+       struct reset_controller_dev rcdev;
+       const struct reset_props *props;
+};
+
+static const struct regmap_config sparx5_reset_regmap_config = {
+       .reg_bits       = 32,
+       .val_bits       = 32,
+       .reg_stride     = 4,
+};
+
+static int sparx5_switch_reset(struct mchp_reset_context *ctx)
+{
+       u32 val;
+
+       /* Make sure the core is PROTECTED from reset */
+       regmap_update_bits(ctx->cpu_ctrl, ctx->props->protect_reg,
+                          ctx->props->protect_bit, ctx->props->protect_bit);
+
+       /* Start soft reset */
+       regmap_write(ctx->gcb_ctrl, ctx->props->reset_reg,
+                    ctx->props->reset_bit);
+
+       /* Wait for soft reset done */
+       return regmap_read_poll_timeout(ctx->gcb_ctrl, ctx->props->reset_reg,
+                                       val,
+                                       (val & ctx->props->reset_bit) == 0,
+                                       100);
+}
+
+static int sparx5_reset_noop(struct reset_controller_dev *rcdev,
+                            unsigned long id)
+{
+       return 0;
+}
+
+static const struct reset_control_ops sparx5_reset_ops = {
+       .reset = sparx5_reset_noop,
+};
+
+static int mchp_sparx5_reset_probe(struct device *dev)
+{
+       struct device_node *dn = dev->of_node;
+       struct mchp_reset_context *ctx;
+       struct device_node *syscon_np;
+       struct resource *iores;
+       void __iomem *base;
+       int ret;
+
+       ctx = xzalloc(sizeof(*ctx));
+
+       syscon_np = of_parse_phandle(dn, "cpu-syscon", 0);
+       if (!syscon_np) {
+               dev_err(dev, "missing 'cpu-syscon' phandle\n");
+               ret = -ENODEV;
+               goto err;
+       }
+       ctx->cpu_ctrl = syscon_node_to_regmap(syscon_np);
+       if (IS_ERR(ctx->cpu_ctrl)) {
+               dev_err(dev, "failed to map cpu-syscon: %pe\n", ctx->cpu_ctrl);
+               ret = PTR_ERR(ctx->cpu_ctrl);
+               goto err;
+       }
+
+       iores = dev_request_mem_resource(dev, 0);
+       if (IS_ERR(iores)) {
+               ret = PTR_ERR(iores);
+               goto err;
+       }
+       base = IOMEM(iores->start);
+       ctx->gcb_ctrl = regmap_init_mmio(dev, base, 
&sparx5_reset_regmap_config);
+       if (IS_ERR(ctx->gcb_ctrl)) {
+               ret = PTR_ERR(ctx->gcb_ctrl);
+               goto err;
+       }
+
+       ctx->props = device_get_match_data(dev);
+       if (!ctx->props) {
+               ret = -EINVAL;
+               goto err;
+       }
+
+       ctx->rcdev.nr_resets = 1;
+       ctx->rcdev.ops = &sparx5_reset_ops;
+       ctx->rcdev.of_node = dn;
+
+       /*
+        * Issue the reset very early; reset_control_reset() callers see a
+        * no-op afterwards.
+        */
+       ret = sparx5_switch_reset(ctx);
+       if (ret) {
+               dev_err(dev, "switch reset timed out: %d\n", ret);
+               goto err;
+       }
+
+       ret = reset_controller_register(&ctx->rcdev);
+       if (ret)
+               goto err;
+
+       dev_info(dev, "switch reset complete\n");
+       return 0;
+
+err:
+       free(ctx);
+       return ret;
+}
+
+static const struct reset_props reset_props_sparx5 = {
+       .protect_reg    = 0x84,
+       .protect_bit    = BIT(10),
+       .reset_reg      = 0x0,
+       .reset_bit      = BIT(1),
+};
+
+static const struct reset_props reset_props_lan966x = {
+       .protect_reg    = 0x88,
+       .protect_bit    = BIT(5),
+       .reset_reg      = 0x0,
+       .reset_bit      = BIT(1),
+};
+
+static const struct of_device_id mchp_sparx5_reset_of_match[] = {
+       { .compatible = "microchip,sparx5-switch-reset",
+         .data = &reset_props_sparx5 },
+       { .compatible = "microchip,lan966x-switch-reset",
+         .data = &reset_props_lan966x },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mchp_sparx5_reset_of_match);
+
+static struct driver mchp_sparx5_reset_driver = {
+       .name = "sparx5-switch-reset",
+       .probe = mchp_sparx5_reset_probe,
+       .of_compatible = DRV_OF_COMPAT(mchp_sparx5_reset_of_match),
+};
+/*
+ * Same rationale as Linux's postcore_initcall(): the switch IP must be
+ * reset before downstream consumers (SGPIO, MDIO, switch fabric) probe.
+ */
+coredevice_platform_driver(mchp_sparx5_reset_driver);
-- 
2.47.3


Reply via email to