2022-06-19 16:51:51

by Conor Dooley

[permalink] [raw]
Subject: [RFC 0/6] PolarFire SoC Reset controller

From: Conor Dooley <[email protected]>

Hi Stephen (& Philipp),

I gave the aux bus approach to the clock->reset driver combo a go.
Could you take a quick look and lmk if it meets your expectations
for that approach? There weren't too many aux bus drivers to "take
inspiration from" so I implemented this based on drivers/peci/cpu.c
If it all looks sane at first glance, I'll tidy things up a little
and submit.

@Geert the prior "RFC" you said you saw issues with the ethernet?
I implemented the reset stuff for the macs and it looks to be to
be working fine - but I did not do any meaninful testing with
CONFIG_PM=y.

Thanks,
Conor.

(Since it's just the clk -> reset aux bus interface I care about
here, I left the net/dt maintainers off the CC.)*

Conor Dooley (6):
dt-bindings: clk: microchip: mpfs: add reset controller support
dt-bindings: net: cdns,macb: document polarfire soc's macb
clk: microchip: mpfs: add reset controller
reset: add polarfire soc reset support
net: macb: add polarfire soc reset support
riscv: dts: microchip: add mpfs specific macb reset support

.../bindings/clock/microchip,mpfs.yaml | 17 +-
.../devicetree/bindings/net/cdns,macb.yaml | 1 +
arch/riscv/boot/dts/microchip/mpfs.dtsi | 7 +-
drivers/clk/microchip/Kconfig | 1 +
drivers/clk/microchip/clk-mpfs.c | 118 +++++++++++--
drivers/net/ethernet/cadence/macb_main.c | 25 ++-
drivers/reset/Kconfig | 9 +
drivers/reset/Makefile | 2 +-
drivers/reset/reset-mpfs.c | 155 ++++++++++++++++++
include/soc/microchip/mpfs.h | 8 +
10 files changed, 320 insertions(+), 23 deletions(-)
create mode 100644 drivers/reset/reset-mpfs.c


base-commit: b13baccc3850ca8b8cccbf8ed9912dbaa0fdf7f3
--
2.36.1


2022-06-19 16:51:58

by Conor Dooley

[permalink] [raw]
Subject: [RFC 1/6] dt-bindings: clk: microchip: mpfs: add reset controller support

From: Conor Dooley <[email protected]>

The "peripheral" devices on PolarFire SoC can be put into reset, so
update the device tree binding to reflect the presence of a reset
controller.

Signed-off-by: Conor Dooley <[email protected]>
---
.../bindings/clock/microchip,mpfs.yaml | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/clock/microchip,mpfs.yaml b/Documentation/devicetree/bindings/clock/microchip,mpfs.yaml
index 016a4f378b9b..1d0b6a4fda42 100644
--- a/Documentation/devicetree/bindings/clock/microchip,mpfs.yaml
+++ b/Documentation/devicetree/bindings/clock/microchip,mpfs.yaml
@@ -40,8 +40,21 @@ properties:
const: 1
description: |
The clock consumer should specify the desired clock by having the clock
- ID in its "clocks" phandle cell. See include/dt-bindings/clock/microchip,mpfs-clock.h
- for the full list of PolarFire clock IDs.
+ ID in its "clocks" phandle cell.
+ See include/dt-bindings/clock/microchip,mpfs-clock.h for the full list of
+ PolarFire clock IDs.
+
+ resets:
+ maxItems: 1
+
+ '#reset-cells':
+ description:
+ The AHB/AXI peripherals on the PolarFire SoC have reset support, so from
+ CLK_ENVM to CLK_CFM. The reset consumer should specify the desired
+ peripheral via the clock ID in its "resets" phandle cell.
+ See include/dt-bindings/clock/microchip,mpfs-clock.h for the full list of
+ PolarFire clock IDs.
+ const: 1

required:
- compatible
--
2.36.1

2022-06-19 16:52:11

by Conor Dooley

[permalink] [raw]
Subject: [RFC 2/6] dt-bindings: net: cdns,macb: document polarfire soc's macb

From: Conor Dooley <[email protected]>

Until now the PolarFire SoC (MPFS) has been using the generic
"cdns,macb" compatible but has optional reset support. Add a specific
compatible which falls back to the currently used generic binding.

Signed-off-by: Conor Dooley <[email protected]>
---
Documentation/devicetree/bindings/net/cdns,macb.yaml | 1 +
1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/net/cdns,macb.yaml b/Documentation/devicetree/bindings/net/cdns,macb.yaml
index 86fc31c2d91b..9c92156869b2 100644
--- a/Documentation/devicetree/bindings/net/cdns,macb.yaml
+++ b/Documentation/devicetree/bindings/net/cdns,macb.yaml
@@ -28,6 +28,7 @@ properties:
- enum:
- cdns,at91sam9260-macb # Atmel at91sam9 SoCs
- cdns,sam9x60-macb # Microchip sam9x60 SoC
+ - microchip,mpfs-macb # Microchip PolarFire SoC
- const: cdns,macb # Generic

- items:
--
2.36.1

2022-06-19 16:55:00

by Conor Dooley

[permalink] [raw]
Subject: [RFC 6/6] riscv: dts: microchip: add mpfs specific macb reset support

From: Conor Dooley <[email protected]>

The macb on PolarFire SoC has reset support which the generic compatible
does not use. Add the newly introduced MPFS specific compatible as the
primary compatible to avail of this support & wire up the reset to the
clock controllers devicetree entry.

Signed-off-by: Conor Dooley <[email protected]>
---
arch/riscv/boot/dts/microchip/mpfs.dtsi | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/arch/riscv/boot/dts/microchip/mpfs.dtsi b/arch/riscv/boot/dts/microchip/mpfs.dtsi
index 8c3259134194..5a33cbf9467a 100644
--- a/arch/riscv/boot/dts/microchip/mpfs.dtsi
+++ b/arch/riscv/boot/dts/microchip/mpfs.dtsi
@@ -197,6 +197,7 @@ clkcfg: clkcfg@20002000 {
reg = <0x0 0x20002000 0x0 0x1000>, <0x0 0x3E001000 0x0 0x1000>;
clocks = <&refclk>;
#clock-cells = <1>;
+ #reset-cells = <1>;
};

mmuart0: serial@20000000 {
@@ -331,7 +332,7 @@ i2c1: i2c@2010b000 {
};

mac0: ethernet@20110000 {
- compatible = "cdns,macb";
+ compatible = "microchip,mpfs-macb", "cdns,macb";
reg = <0x0 0x20110000 0x0 0x2000>;
#address-cells = <1>;
#size-cells = <0>;
@@ -340,11 +341,12 @@ mac0: ethernet@20110000 {
local-mac-address = [00 00 00 00 00 00];
clocks = <&clkcfg CLK_MAC0>, <&clkcfg CLK_AHB>;
clock-names = "pclk", "hclk";
+ resets = <&clkcfg CLK_MAC0>;
status = "disabled";
};

mac1: ethernet@20112000 {
- compatible = "cdns,macb";
+ compatible = "microchip,mpfs-macb", "cdns,macb";
reg = <0x0 0x20112000 0x0 0x2000>;
#address-cells = <1>;
#size-cells = <0>;
@@ -353,6 +355,7 @@ mac1: ethernet@20112000 {
local-mac-address = [00 00 00 00 00 00];
clocks = <&clkcfg CLK_MAC1>, <&clkcfg CLK_AHB>;
clock-names = "pclk", "hclk";
+ resets = <&clkcfg CLK_MAC1>;
status = "disabled";
};

--
2.36.1

2022-06-19 17:16:15

by Conor Dooley

[permalink] [raw]
Subject: [RFC 4/6] reset: add polarfire soc reset support

From: Conor Dooley <[email protected]>

Add support for the resets on Microchip's PolarFire SoC (MPFS).
Reset control is a single register, wedged in between registers for
clock control. To fit with existed DT etc, the reset controller is
created using the aux device framework & set up in the clock driver.

Signed-off-by: Conor Dooley <[email protected]>
---
drivers/reset/Kconfig | 9 +++
drivers/reset/Makefile | 2 +-
drivers/reset/reset-mpfs.c | 155 +++++++++++++++++++++++++++++++++++++
3 files changed, 165 insertions(+), 1 deletion(-)
create mode 100644 drivers/reset/reset-mpfs.c

diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index 93c8d07ee328..8f7d7cda690d 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -122,6 +122,15 @@ config RESET_MCHP_SPARX5
help
This driver supports switch core reset for the Microchip Sparx5 SoC.

+config RESET_POLARFIRE_SOC
+ bool "Microchip PolarFire SoC (MPFS) Reset Driver"
+ depends on AUXILIARY_BUS
+ default MCHP_CLK_MPFS
+ help
+ This driver supports switch core reset for the Microchip PolarFire SoC
+
+ CONFIG_RESET_MPFS
+
config RESET_MESON
tristate "Meson Reset Driver"
depends on ARCH_MESON || COMPILE_TEST
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index a80a9c4008a7..5fac3a753858 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_RESET_K210) += reset-k210.o
obj-$(CONFIG_RESET_LANTIQ) += reset-lantiq.o
obj-$(CONFIG_RESET_LPC18XX) += reset-lpc18xx.o
obj-$(CONFIG_RESET_MCHP_SPARX5) += reset-microchip-sparx5.o
+obj-$(CONFIG_RESET_POLARFIRE_SOC) += reset-mpfs.o
obj-$(CONFIG_RESET_MESON) += reset-meson.o
obj-$(CONFIG_RESET_MESON_AUDIO_ARB) += reset-meson-audio-arb.o
obj-$(CONFIG_RESET_NPCM) += reset-npcm.o
@@ -38,4 +39,3 @@ obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o
obj-$(CONFIG_RESET_UNIPHIER_GLUE) += reset-uniphier-glue.o
obj-$(CONFIG_RESET_ZYNQ) += reset-zynq.o
obj-$(CONFIG_ARCH_ZYNQMP) += reset-zynqmp.o
-
diff --git a/drivers/reset/reset-mpfs.c b/drivers/reset/reset-mpfs.c
new file mode 100644
index 000000000000..6c9c10cd9077
--- /dev/null
+++ b/drivers/reset/reset-mpfs.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * PolarFire SoC (MPFS) Peripheral Clock Reset Controller
+ *
+ * Author: Conor Dooley <[email protected]>
+ * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries.
+ *
+ */
+#include <linux/auxiliary_bus.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <dt-bindings/clock/microchip,mpfs-clock.h>
+#include <soc/microchip/mpfs.h>
+
+/*
+ * The ENVM reset is the lowest bit in the register & I am using the CLK_FOO
+ * defines in the dt to make things easier to configure - so this is accounting
+ * for the offset of 3 there.
+ */
+#define MPFS_PERIPH_OFFSET CLK_ENVM
+#define MPFS_NUM_RESETS 30u
+
+/*
+ * Peripheral clock resets
+ */
+
+static int mpfs_assert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ u32 reg;
+
+ reg = mpfs_reset_read(rcdev->dev);
+ reg |= (1u << id);
+ mpfs_reset_write(rcdev->dev, reg);
+
+ dev_dbg(rcdev->dev,
+ "Asserting reset for device with REG_SUBBLK_RESET_CR index: %u\n",
+ id);
+ return 0;
+}
+
+static int mpfs_deassert(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ u32 reg, val;
+
+ reg = mpfs_reset_read(rcdev->dev);
+ val = reg & ~(1u << id);
+ mpfs_reset_write(rcdev->dev, val);
+
+ dev_dbg(rcdev->dev,
+ "Deasserting device with REG_SUBBLK_RESET_CR index: %u\n",
+ id);
+
+ return 0;
+}
+
+static int mpfs_status(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ u32 reg = mpfs_reset_read(rcdev->dev);
+
+ return (reg & (1u << id));
+}
+
+static int mpfs_reset(struct reset_controller_dev *rcdev, unsigned long id)
+{
+ dev_dbg(rcdev->dev,
+ "Resetting device with REG_SUBBLK_RESET_CR index: %u\n",
+ id);
+
+ mpfs_assert(rcdev, id);
+
+ /* Value is stolen from the rcar reset driver, will need changing after RFC */
+ udelay(35);
+
+ mpfs_deassert(rcdev, id);
+
+ return 0;
+}
+
+static const struct reset_control_ops mpfs_reset_ops = {
+ .reset = mpfs_reset,
+ .assert = mpfs_assert,
+ .deassert = mpfs_deassert,
+ .status = mpfs_status,
+};
+
+static int mpfs_reset_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+{
+ unsigned int index = reset_spec->args[0];
+
+ /*
+ * CLK_RESERVED does not map to a clock, but it does map to a reset,
+ * so it has to be accounted for here. It is the reset for the fabric,
+ * so if this reset gets called - do not reset it.
+ */
+ if (index == CLK_RESERVED) {
+ dev_err(rcdev->dev, "Resetting the fabric is not supported\n");
+ return -EINVAL;
+ }
+
+ if (index < MPFS_PERIPH_OFFSET || index >= (MPFS_PERIPH_OFFSET + rcdev->nr_resets)) {
+ dev_err(rcdev->dev, "Invalid reset index %u\n", reset_spec->args[0]);
+ return -EINVAL;
+ }
+
+ return index - MPFS_PERIPH_OFFSET;
+}
+
+static int mpfs_reset_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct device *dev = &adev->dev;
+ struct reset_controller_dev *rcdev;
+ int ret;
+
+ rcdev = devm_kzalloc(dev, sizeof(*rcdev), GFP_KERNEL);
+ if (!rcdev)
+ return -ENOMEM;
+
+ rcdev->dev = dev;
+ rcdev->dev->parent = adev->dev.parent;
+ rcdev->ops = &mpfs_reset_ops;
+ rcdev->of_node = adev->dev.parent->of_node;
+ rcdev->of_reset_n_cells = 1;
+ rcdev->of_xlate = mpfs_reset_xlate;
+ rcdev->nr_resets = MPFS_NUM_RESETS;
+
+ ret = devm_reset_controller_register(dev, rcdev);
+ if (!ret)
+ dev_info(dev, "Registered MPFS reset controller\n");
+
+ return ret;
+}
+
+static const struct auxiliary_device_id mpfs_reset_ids[] = {
+ {
+ .name = "clk_mpfs.reset-mpfs",
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(auxiliary, mpfs_reset_ids);
+
+static struct auxiliary_driver mpfs_reset_driver = {
+ .probe = mpfs_reset_probe,
+ .id_table = mpfs_reset_ids,
+};
+
+module_auxiliary_driver(mpfs_reset_driver);
+
+MODULE_DESCRIPTION("Microchip PolarFire SoC Reset Driver");
+MODULE_AUTHOR("Conor Dooley <[email protected]>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(MCHP_CLK_MPFS);
--
2.36.1

2022-06-19 17:17:18

by Conor Dooley

[permalink] [raw]
Subject: [RFC 5/6] net: macb: add polarfire soc reset support

From: Conor Dooley <[email protected]>

To date, the Microchip PolarFire SoC (MPFS) has been using the
cdns,macb compatible, however the generic device does not have reset
support. Add a new compatible & .data for MPFS to hook into the reset
functionality added for zynqmp support (and make the zynqmp init
function generic in the process).

Signed-off-by: Conor Dooley <[email protected]>
---
drivers/net/ethernet/cadence/macb_main.c | 25 +++++++++++++++++-------
1 file changed, 18 insertions(+), 7 deletions(-)

diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
index d89098f4ede8..325f0463fd42 100644
--- a/drivers/net/ethernet/cadence/macb_main.c
+++ b/drivers/net/ethernet/cadence/macb_main.c
@@ -4689,33 +4689,32 @@ static const struct macb_config np4_config = {
.usrio = &macb_default_usrio,
};

-static int zynqmp_init(struct platform_device *pdev)
+static int init_reset_optional(struct platform_device *pdev)
{
struct net_device *dev = platform_get_drvdata(pdev);
struct macb *bp = netdev_priv(dev);
int ret;

if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) {
- /* Ensure PS-GTR PHY device used in SGMII mode is ready */
+ /* Ensure PHY device used in SGMII mode is ready */
bp->sgmii_phy = devm_phy_optional_get(&pdev->dev, NULL);

if (IS_ERR(bp->sgmii_phy)) {
ret = PTR_ERR(bp->sgmii_phy);
dev_err_probe(&pdev->dev, ret,
- "failed to get PS-GTR PHY\n");
+ "failed to get SGMII PHY\n");
return ret;
}

ret = phy_init(bp->sgmii_phy);
if (ret) {
- dev_err(&pdev->dev, "failed to init PS-GTR PHY: %d\n",
+ dev_err(&pdev->dev, "failed to init SGMII PHY: %d\n",
ret);
return ret;
}
}

- /* Fully reset GEM controller at hardware level using zynqmp-reset driver,
- * if mapped in device tree.
+ /* Fully reset controller at hardware level if mapped in device tree
*/
ret = device_reset_optional(&pdev->dev);
if (ret) {
@@ -4737,7 +4736,7 @@ static const struct macb_config zynqmp_config = {
MACB_CAPS_GEM_HAS_PTP | MACB_CAPS_BD_RD_PREFETCH,
.dma_burst_length = 16,
.clk_init = macb_clk_init,
- .init = zynqmp_init,
+ .init = init_reset_optional,
.jumbo_max_len = 10240,
.usrio = &macb_default_usrio,
};
@@ -4751,6 +4750,17 @@ static const struct macb_config zynq_config = {
.usrio = &macb_default_usrio,
};

+static const struct macb_config mpfs_config = {
+ .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE |
+ MACB_CAPS_JUMBO |
+ MACB_CAPS_GEM_HAS_PTP,
+ .dma_burst_length = 16,
+ .clk_init = macb_clk_init,
+ .init = init_reset_optional,
+ .usrio = &macb_default_usrio,
+ .jumbo_max_len = 10240,
+};
+
static const struct macb_config sama7g5_gem_config = {
.caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_CLK_HW_CHG |
MACB_CAPS_MIIONRGMII,
@@ -4787,6 +4797,7 @@ static const struct of_device_id macb_dt_ids[] = {
{ .compatible = "cdns,zynqmp-gem", .data = &zynqmp_config},
{ .compatible = "cdns,zynq-gem", .data = &zynq_config },
{ .compatible = "sifive,fu540-c000-gem", .data = &fu540_c000_config },
+ { .compatible = "microchip,mpfs-macb", .data = &mpfs_config },
{ .compatible = "microchip,sama7g5-gem", .data = &sama7g5_gem_config },
{ .compatible = "microchip,sama7g5-emac", .data = &sama7g5_emac_config },
{ /* sentinel */ }
--
2.36.1

2022-06-19 17:53:17

by Conor Dooley

[permalink] [raw]
Subject: [RFC 3/6] clk: microchip: mpfs: add reset controller

From: Conor Dooley <[email protected]>

Add a reset controller to PolarFire SoC's clock driver. This reset
controller is registered as an aux device and read/write functions
exported to the drivers namespace so that the reset controller can
access the peripheral device reset register.

Signed-off-by: Conor Dooley <[email protected]>
---
drivers/clk/microchip/Kconfig | 1 +
drivers/clk/microchip/clk-mpfs.c | 118 ++++++++++++++++++++++++++++---
include/soc/microchip/mpfs.h | 8 +++
3 files changed, 116 insertions(+), 11 deletions(-)

diff --git a/drivers/clk/microchip/Kconfig b/drivers/clk/microchip/Kconfig
index a5a99873c4f5..b46e864b3bd8 100644
--- a/drivers/clk/microchip/Kconfig
+++ b/drivers/clk/microchip/Kconfig
@@ -6,5 +6,6 @@ config COMMON_CLK_PIC32
config MCHP_CLK_MPFS
bool "Clk driver for PolarFire SoC"
depends on (RISCV && SOC_MICROCHIP_POLARFIRE) || COMPILE_TEST
+ select AUXILIARY_BUS
help
Supports Clock Configuration for PolarFire SoC
diff --git a/drivers/clk/microchip/clk-mpfs.c b/drivers/clk/microchip/clk-mpfs.c
index 070c3b896559..19a9f8cd12ff 100644
--- a/drivers/clk/microchip/clk-mpfs.c
+++ b/drivers/clk/microchip/clk-mpfs.c
@@ -3,12 +3,15 @@
* Daire McNamara,<[email protected]>
* Copyright (C) 2020 Microchip Technology Inc. All rights reserved.
*/
+#include "linux/device.h"
+#include <linux/auxiliary_bus.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <dt-bindings/clock/microchip,mpfs-clock.h>
+#include <soc/microchip/mpfs.h>

/* address offset of control registers */
#define REG_MSSPLL_REF_CR 0x08u
@@ -28,6 +31,7 @@
#define MSSPLL_FIXED_DIV 4u

struct mpfs_clock_data {
+ struct device *dev;
void __iomem *base;
void __iomem *msspll_base;
struct clk_hw_onecell_data hw_data;
@@ -302,10 +306,6 @@ static int mpfs_periph_clk_enable(struct clk_hw *hw)

spin_lock_irqsave(&mpfs_clk_lock, flags);

- reg = readl_relaxed(base_addr + REG_SUBBLK_RESET_CR);
- val = reg & ~(1u << periph->shift);
- writel_relaxed(val, base_addr + REG_SUBBLK_RESET_CR);
-
reg = readl_relaxed(base_addr + REG_SUBBLK_CLOCK_CR);
val = reg | (1u << periph->shift);
writel_relaxed(val, base_addr + REG_SUBBLK_CLOCK_CR);
@@ -339,12 +339,9 @@ static int mpfs_periph_clk_is_enabled(struct clk_hw *hw)
void __iomem *base_addr = periph_hw->sys_base;
u32 reg;

- reg = readl_relaxed(base_addr + REG_SUBBLK_RESET_CR);
- if ((reg & (1u << periph->shift)) == 0u) {
- reg = readl_relaxed(base_addr + REG_SUBBLK_CLOCK_CR);
- if (reg & (1u << periph->shift))
- return 1;
- }
+ reg = readl_relaxed(base_addr + REG_SUBBLK_CLOCK_CR);
+ if (reg & (1u << periph->shift))
+ return 1;

return 0;
}
@@ -438,6 +435,99 @@ static int mpfs_clk_register_periphs(struct device *dev, struct mpfs_periph_hw_c
return 0;
}

+/*
+ * Peripheral clock resets
+ */
+
+#if IS_ENABLED(CONFIG_RESET_CONTROLLER)
+
+u32 mpfs_reset_read(struct device *dev)
+{
+ struct mpfs_clock_data *clock_data = dev_get_drvdata(dev->parent);
+
+ return readl_relaxed(clock_data->base + REG_SUBBLK_RESET_CR);
+}
+EXPORT_SYMBOL_NS_GPL(mpfs_reset_read, MCHP_CLK_MPFS);
+
+void mpfs_reset_write(struct device *dev, u32 val)
+{
+ struct mpfs_clock_data *clock_data = dev_get_drvdata(dev->parent);
+
+ writel_relaxed(val, clock_data->base + REG_SUBBLK_RESET_CR);
+}
+EXPORT_SYMBOL_NS_GPL(mpfs_reset_write, MCHP_CLK_MPFS);
+
+static void mpfs_reset_unregister_adev(void *_adev)
+{
+ struct auxiliary_device *adev = _adev;
+
+ auxiliary_device_delete(adev);
+}
+
+static void mpfs_reset_adev_release(struct device *dev)
+{
+ struct auxiliary_device *adev = to_auxiliary_dev(dev);
+
+ auxiliary_device_uninit(adev);
+
+ kfree(adev->name);
+ kfree(adev);
+}
+
+static struct auxiliary_device *mpfs_reset_adev_alloc(struct mpfs_clock_data *clk_data)
+{
+ struct auxiliary_device *adev;
+ int ret;
+
+ adev = kzalloc(sizeof(*adev), GFP_KERNEL);
+ if (!adev)
+ return ERR_PTR(-ENOMEM);
+
+ adev->name = "reset-mpfs";
+ adev->dev.parent = clk_data->dev;
+ adev->dev.release = mpfs_reset_adev_release;
+ adev->id = 666u;
+
+ ret = auxiliary_device_init(adev);
+ if (ret) {
+ kfree(adev);
+ return ERR_PTR(ret);
+ }
+
+ return adev;
+}
+
+static int mpfs_reset_controller_register(struct mpfs_clock_data *clk_data)
+{
+ struct auxiliary_device *adev;
+ int ret;
+
+ adev = mpfs_reset_adev_alloc(clk_data);
+ if (IS_ERR(adev))
+ return PTR_ERR(adev);
+
+ ret = auxiliary_device_add(adev);
+ if (ret) {
+ auxiliary_device_uninit(adev);
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(clk_data->dev, mpfs_reset_unregister_adev, adev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+#else /* !CONFIG_RESET_CONTROLLER */
+
+static int mpfs_reset_controller_register(struct mpfs_clock_data *clk_data)
+{
+ return 0;
+}
+
+#endif /* !CONFIG_RESET_CONTROLLER */
+
static int mpfs_clk_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -462,6 +552,8 @@ static int mpfs_clk_probe(struct platform_device *pdev)
return PTR_ERR(clk_data->msspll_base);

clk_data->hw_data.num = num_clks;
+ clk_data->dev = dev;
+ dev_set_drvdata(dev, clk_data);

ret = mpfs_clk_register_mssplls(dev, mpfs_msspll_clks, ARRAY_SIZE(mpfs_msspll_clks),
clk_data);
@@ -481,6 +573,10 @@ static int mpfs_clk_probe(struct platform_device *pdev)
if (ret)
return ret;

+ ret = mpfs_reset_controller_register(clk_data);
+ if (ret)
+ return ret;
+
return ret;
}

@@ -488,7 +584,7 @@ static const struct of_device_id mpfs_clk_of_match_table[] = {
{ .compatible = "microchip,mpfs-clkcfg", },
{}
};
-MODULE_DEVICE_TABLE(of, mpfs_clk_match_table);
+MODULE_DEVICE_TABLE(of, mpfs_clk_of_match_table);

static struct platform_driver mpfs_clk_driver = {
.probe = mpfs_clk_probe,
diff --git a/include/soc/microchip/mpfs.h b/include/soc/microchip/mpfs.h
index 6466515262bd..f916dcde457f 100644
--- a/include/soc/microchip/mpfs.h
+++ b/include/soc/microchip/mpfs.h
@@ -40,4 +40,12 @@ struct mpfs_sys_controller *mpfs_sys_controller_get(struct device *dev);

#endif /* if IS_ENABLED(CONFIG_POLARFIRE_SOC_SYS_CTRL) */

+#if IS_ENABLED(CONFIG_MCHP_CLK_MPFS)
+
+u32 mpfs_reset_read(struct device *dev);
+
+void mpfs_reset_write(struct device *dev, u32 val);
+
+#endif /* if IS_ENABLED(CONFIG_MCHP_CLK_MPFS) */
+
#endif /* __SOC_MPFS_H__ */
--
2.36.1

2022-06-19 19:19:45

by Conor Dooley

[permalink] [raw]
Subject: Re: [RFC 4/6] reset: add polarfire soc reset support



On 19/06/2022 17:49, Conor Dooley wrote:
> From: Conor Dooley <[email protected]>
>
> Add support for the resets on Microchip's PolarFire SoC (MPFS).
> Reset control is a single register, wedged in between registers for
> clock control. To fit with existed DT etc, the reset controller is
> created using the aux device framework & set up in the clock driver.
>
> Signed-off-by: Conor Dooley <[email protected]>
> ---
> drivers/reset/Kconfig | 9 +++
> drivers/reset/Makefile | 2 +-
> drivers/reset/reset-mpfs.c | 155 +++++++++++++++++++++++++++++++++++++
> 3 files changed, 165 insertions(+), 1 deletion(-)
> create mode 100644 drivers/reset/reset-mpfs.c
>
> diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
> index 93c8d07ee328..8f7d7cda690d 100644
> --- a/drivers/reset/Kconfig
> +++ b/drivers/reset/Kconfig
> @@ -122,6 +122,15 @@ config RESET_MCHP_SPARX5
> help
> This driver supports switch core reset for the Microchip Sparx5 SoC.
>
> +config RESET_POLARFIRE_SOC
> + bool "Microchip PolarFire SoC (MPFS) Reset Driver"
> + depends on AUXILIARY_BUS

(Correctly) According to lkp I screwed this up, needs to depend on
MCHP_CLK_MPFS.

> + default MCHP_CLK_MPFS
> + help
> + This driver supports switch core reset for the Microchip PolarFire SoC
> +
> + CONFIG_RESET_MPFS
> +
> config RESET_MESON
> tristate "Meson Reset Driver"
> depends on ARCH_MESON || COMPILE_TEST
> diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
> index a80a9c4008a7..5fac3a753858 100644
> --- a/drivers/reset/Makefile
> +++ b/drivers/reset/Makefile
> @@ -17,6 +17,7 @@ obj-$(CONFIG_RESET_K210) += reset-k210.o
> obj-$(CONFIG_RESET_LANTIQ) += reset-lantiq.o
> obj-$(CONFIG_RESET_LPC18XX) += reset-lpc18xx.o
> obj-$(CONFIG_RESET_MCHP_SPARX5) += reset-microchip-sparx5.o
> +obj-$(CONFIG_RESET_POLARFIRE_SOC) += reset-mpfs.o
> obj-$(CONFIG_RESET_MESON) += reset-meson.o
> obj-$(CONFIG_RESET_MESON_AUDIO_ARB) += reset-meson-audio-arb.o
> obj-$(CONFIG_RESET_NPCM) += reset-npcm.o
> @@ -38,4 +39,3 @@ obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o
> obj-$(CONFIG_RESET_UNIPHIER_GLUE) += reset-uniphier-glue.o
> obj-$(CONFIG_RESET_ZYNQ) += reset-zynq.o
> obj-$(CONFIG_ARCH_ZYNQMP) += reset-zynqmp.o
> -
> diff --git a/drivers/reset/reset-mpfs.c b/drivers/reset/reset-mpfs.c
> new file mode 100644
> index 000000000000..6c9c10cd9077
> --- /dev/null
> +++ b/drivers/reset/reset-mpfs.c
> @@ -0,0 +1,155 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * PolarFire SoC (MPFS) Peripheral Clock Reset Controller
> + *
> + * Author: Conor Dooley <[email protected]>
> + * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries.
> + *
> + */
> +#include <linux/auxiliary_bus.h>
> +#include <linux/delay.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset-controller.h>
> +#include <dt-bindings/clock/microchip,mpfs-clock.h>
> +#include <soc/microchip/mpfs.h>
> +
> +/*
> + * The ENVM reset is the lowest bit in the register & I am using the CLK_FOO
> + * defines in the dt to make things easier to configure - so this is accounting
> + * for the offset of 3 there.
> + */
> +#define MPFS_PERIPH_OFFSET CLK_ENVM
> +#define MPFS_NUM_RESETS 30u
> +
> +/*
> + * Peripheral clock resets
> + */
> +
> +static int mpfs_assert(struct reset_controller_dev *rcdev, unsigned long id)
> +{
> + u32 reg;
> +
> + reg = mpfs_reset_read(rcdev->dev);
> + reg |= (1u << id);
> + mpfs_reset_write(rcdev->dev, reg);
> +
> + dev_dbg(rcdev->dev,
> + "Asserting reset for device with REG_SUBBLK_RESET_CR index: %u\n",
> + id);
> + return 0;
> +}
> +
> +static int mpfs_deassert(struct reset_controller_dev *rcdev, unsigned long id)
> +{
> + u32 reg, val;
> +
> + reg = mpfs_reset_read(rcdev->dev);
> + val = reg & ~(1u << id);
> + mpfs_reset_write(rcdev->dev, val);
> +
> + dev_dbg(rcdev->dev,
> + "Deasserting device with REG_SUBBLK_RESET_CR index: %u\n",
> + id);
> +
> + return 0;
> +}
> +
> +static int mpfs_status(struct reset_controller_dev *rcdev, unsigned long id)
> +{
> + u32 reg = mpfs_reset_read(rcdev->dev);
> +
> + return (reg & (1u << id));
> +}
> +
> +static int mpfs_reset(struct reset_controller_dev *rcdev, unsigned long id)
> +{
> + dev_dbg(rcdev->dev,
> + "Resetting device with REG_SUBBLK_RESET_CR index: %u\n",
> + id);
> +
> + mpfs_assert(rcdev, id);
> +
> + /* Value is stolen from the rcar reset driver, will need changing after RFC */
> + udelay(35);
> +
> + mpfs_deassert(rcdev, id);
> +
> + return 0;
> +}
> +
> +static const struct reset_control_ops mpfs_reset_ops = {
> + .reset = mpfs_reset,
> + .assert = mpfs_assert,
> + .deassert = mpfs_deassert,
> + .status = mpfs_status,
> +};
> +
> +static int mpfs_reset_xlate(struct reset_controller_dev *rcdev,
> + const struct of_phandle_args *reset_spec)
> +{
> + unsigned int index = reset_spec->args[0];
> +
> + /*
> + * CLK_RESERVED does not map to a clock, but it does map to a reset,
> + * so it has to be accounted for here. It is the reset for the fabric,
> + * so if this reset gets called - do not reset it.
> + */
> + if (index == CLK_RESERVED) {
> + dev_err(rcdev->dev, "Resetting the fabric is not supported\n");
> + return -EINVAL;
> + }
> +
> + if (index < MPFS_PERIPH_OFFSET || index >= (MPFS_PERIPH_OFFSET + rcdev->nr_resets)) {
> + dev_err(rcdev->dev, "Invalid reset index %u\n", reset_spec->args[0]);
> + return -EINVAL;
> + }
> +
> + return index - MPFS_PERIPH_OFFSET;
> +}
> +
> +static int mpfs_reset_probe(struct auxiliary_device *adev,
> + const struct auxiliary_device_id *id)
> +{
> + struct device *dev = &adev->dev;
> + struct reset_controller_dev *rcdev;
> + int ret;
> +
> + rcdev = devm_kzalloc(dev, sizeof(*rcdev), GFP_KERNEL);
> + if (!rcdev)
> + return -ENOMEM;
> +
> + rcdev->dev = dev;
> + rcdev->dev->parent = adev->dev.parent;
> + rcdev->ops = &mpfs_reset_ops;
> + rcdev->of_node = adev->dev.parent->of_node;
> + rcdev->of_reset_n_cells = 1;
> + rcdev->of_xlate = mpfs_reset_xlate;
> + rcdev->nr_resets = MPFS_NUM_RESETS;
> +
> + ret = devm_reset_controller_register(dev, rcdev);
> + if (!ret)
> + dev_info(dev, "Registered MPFS reset controller\n");
> +
> + return ret;
> +}
> +
> +static const struct auxiliary_device_id mpfs_reset_ids[] = {
> + {
> + .name = "clk_mpfs.reset-mpfs",
> + },
> + { }
> +};
> +MODULE_DEVICE_TABLE(auxiliary, mpfs_reset_ids);
> +
> +static struct auxiliary_driver mpfs_reset_driver = {
> + .probe = mpfs_reset_probe,
> + .id_table = mpfs_reset_ids,
> +};
> +
> +module_auxiliary_driver(mpfs_reset_driver);
> +
> +MODULE_DESCRIPTION("Microchip PolarFire SoC Reset Driver");
> +MODULE_AUTHOR("Conor Dooley <[email protected]>");
> +MODULE_LICENSE("GPL");
> +MODULE_IMPORT_NS(MCHP_CLK_MPFS);