Devicetree
 help / color / mirror / Atom feed
* [PATCH v11 3/4] clk: wpcm450: Add Nuvoton WPCM450 clock/reset controller driver
From: Jonathan Neuschäfer @ 2024-04-01 14:06 UTC (permalink / raw)
  To: openbmc, linux-clk, devicetree
  Cc: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Joel Stanley, Philipp Zabel, linux-kernel,
	Jonathan Neuschäfer
In-Reply-To: <20240401-wpcm-clk-v11-0-379472961244@gmx.net>

This driver implements the following features w.r.t. the clock and reset
controller in the WPCM450 SoC:

- It calculates the rates for all clocks managed by the clock controller
- It leaves the clock tree mostly unchanged, except that it enables/
  disables clock gates based on usage.
- It exposes the reset lines managed by the controller using the
  Generic Reset Controller subsystem

NOTE: If the driver and the corresponding devicetree node are present,
      the driver will disable "unused" clocks. This is problem until
      the clock relations are properly declared in the devicetree (in a
      later patch). Until then, the clk_ignore_unused kernel parameter
      can be used as a workaround.

Signed-off-by: Jonathan Neuschäfer <j.neuschaefer@gmx.net>
Reviewed-by: Joel Stanley <joel@jms.id.au>
---

I have considered converting this driver to a platform driver instead of
using CLK_OF_DECLARE, because platform drivers are generally the way
forward. However, the timer-npcm7xx driver used on the same platform
requires is initialized with TIMER_OF_DECLARE and thus requires the
clocks to be available earlier than a platform driver can provide them.

v11:
- no changes

v10:
- select RESET_{CONTROLLER,SIMPLE} from CLK_WPCM450 instead of messing with the 'default' statement

v9:
- Apply comments made by Stephen Boyd
- Move to drivers/clk/nuvoton/ directory
- Update SPDX license identifier from GPL-2.0 to GPL-2.0-only
- Rename clk_np variable to np
- Use of_clk_hw_register
- Refer to clock parents by .fw_name

v8:
- https://lore.kernel.org/lkml/20230428190226.1304326-3-j.neuschaefer@gmx.net/
- Use %pe format specifier throughout the driver, as suggested by Philipp Zabel
- Add Joel's R-b

v7:
- https://lore.kernel.org/lkml/20230422220240.322572-3-j.neuschaefer@gmx.net/
- Simplify error handling by not deallocating resources

v6:
- Enable RESET_SIMPLE based on ARCH_WPCM450, not ARCH_NPCM, as suggested by Tomer Maimon

v5:
- https://lore.kernel.org/lkml/20221104161850.2889894-6-j.neuschaefer@gmx.net/
- Switch to using clk_parent_data

v4:
- Fix reset controller initialization

v3:
- Change reference clock name from "refclk" to "ref"
- Remove unused variable in return path of wpcm450_clk_register_pll
- Remove unused divisor tables

v2:
- no changes
---
 drivers/clk/Makefile              |   2 +-
 drivers/clk/nuvoton/Kconfig       |  10 +-
 drivers/clk/nuvoton/Makefile      |   1 +
 drivers/clk/nuvoton/clk-wpcm450.c | 372 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 383 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 14fa8d4ecc1fbe..cdeb2ecf3a8e99 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -107,7 +107,7 @@ endif
 obj-y					+= mstar/
 obj-y					+= mvebu/
 obj-$(CONFIG_ARCH_MXS)			+= mxs/
-obj-$(CONFIG_ARCH_MA35)			+= nuvoton/
+obj-y					+= nuvoton/
 obj-$(CONFIG_COMMON_CLK_NXP)		+= nxp/
 obj-$(CONFIG_COMMON_CLK_PISTACHIO)	+= pistachio/
 obj-$(CONFIG_COMMON_CLK_PXA)		+= pxa/
diff --git a/drivers/clk/nuvoton/Kconfig b/drivers/clk/nuvoton/Kconfig
index fe4b7f62f46704..908881654b2e91 100644
--- a/drivers/clk/nuvoton/Kconfig
+++ b/drivers/clk/nuvoton/Kconfig
@@ -3,7 +3,7 @@

 config COMMON_CLK_NUVOTON
 	bool "Nuvoton clock controller common support"
-	depends on ARCH_MA35 || COMPILE_TEST
+	depends on ARCH_MA35 || ARCH_NPCM || COMPILE_TEST
 	default y
 	help
 	  Say y here to enable common clock controller for Nuvoton platforms.
@@ -16,4 +16,12 @@ config CLK_MA35D1
 	help
 	  Build the clock controller driver for MA35D1 SoC.

+config CLK_WPCM450
+	bool "Nuvoton WPCM450 clock/reset controller support"
+	default y
+	select RESET_CONTROLLER
+	select RESET_SIMPLE
+	help
+	  Build the clock and reset controller driver for the WPCM450 SoC.
+
 endif
diff --git a/drivers/clk/nuvoton/Makefile b/drivers/clk/nuvoton/Makefile
index c3c59dd9f2aaab..b130f0d3889ca0 100644
--- a/drivers/clk/nuvoton/Makefile
+++ b/drivers/clk/nuvoton/Makefile
@@ -2,3 +2,4 @@
 obj-$(CONFIG_CLK_MA35D1) += clk-ma35d1.o
 obj-$(CONFIG_CLK_MA35D1) += clk-ma35d1-divider.o
 obj-$(CONFIG_CLK_MA35D1) += clk-ma35d1-pll.o
+obj-$(CONFIG_CLK_WPCM450) += clk-wpcm450.o
diff --git a/drivers/clk/nuvoton/clk-wpcm450.c b/drivers/clk/nuvoton/clk-wpcm450.c
new file mode 100644
index 00000000000000..9100c4b8a56483
--- /dev/null
+++ b/drivers/clk/nuvoton/clk-wpcm450.c
@@ -0,0 +1,372 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Nuvoton WPCM450 clock and reset controller driver.
+ *
+ * Copyright (C) 2022 Jonathan Neuschäfer <j.neuschaefer@gmx.net>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitfield.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/reset-controller.h>
+#include <linux/reset/reset-simple.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/clock/nuvoton,wpcm450-clk.h>
+
+struct wpcm450_clk_pll {
+	struct clk_hw hw;
+	void __iomem *pllcon;
+	u8 flags;
+};
+
+#define to_wpcm450_clk_pll(_hw) container_of(_hw, struct wpcm450_clk_pll, hw)
+
+#define PLLCON_FBDV	GENMASK(24, 16)
+#define PLLCON_PRST	BIT(13)
+#define PLLCON_PWDEN	BIT(12)
+#define PLLCON_OTDV	GENMASK(10, 8)
+#define PLLCON_INDV	GENMASK(5, 0)
+
+static unsigned long wpcm450_clk_pll_recalc_rate(struct clk_hw *hw,
+						 unsigned long parent_rate)
+{
+	struct wpcm450_clk_pll *pll = to_wpcm450_clk_pll(hw);
+	unsigned long fbdv, indv, otdv;
+	u64 rate;
+	u32 pllcon;
+
+	if (parent_rate == 0)
+		return 0;
+
+	pllcon = readl_relaxed(pll->pllcon);
+
+	indv = FIELD_GET(PLLCON_INDV, pllcon) + 1;
+	fbdv = FIELD_GET(PLLCON_FBDV, pllcon) + 1;
+	otdv = FIELD_GET(PLLCON_OTDV, pllcon) + 1;
+
+	rate = (u64)parent_rate * fbdv;
+	do_div(rate, indv * otdv);
+
+	return rate;
+}
+
+static int wpcm450_clk_pll_is_enabled(struct clk_hw *hw)
+{
+	struct wpcm450_clk_pll *pll = to_wpcm450_clk_pll(hw);
+	u32 pllcon;
+
+	pllcon = readl_relaxed(pll->pllcon);
+
+	return !(pllcon & PLLCON_PRST);
+}
+
+static void wpcm450_clk_pll_disable(struct clk_hw *hw)
+{
+	struct wpcm450_clk_pll *pll = to_wpcm450_clk_pll(hw);
+	u32 pllcon;
+
+	pllcon = readl_relaxed(pll->pllcon);
+	pllcon |= PLLCON_PRST | PLLCON_PWDEN;
+	writel(pllcon, pll->pllcon);
+}
+
+static const struct clk_ops wpcm450_clk_pll_ops = {
+	.recalc_rate = wpcm450_clk_pll_recalc_rate,
+	.is_enabled = wpcm450_clk_pll_is_enabled,
+	.disable = wpcm450_clk_pll_disable
+};
+
+static struct clk_hw *
+wpcm450_clk_register_pll(struct device_node *np, void __iomem *pllcon, const char *name,
+			 const struct clk_parent_data *parent, unsigned long flags)
+{
+	struct wpcm450_clk_pll *pll;
+	struct clk_init_data init = {};
+	int ret;
+
+	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+	if (!pll)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &wpcm450_clk_pll_ops;
+	init.parent_data = parent;
+	init.num_parents = 1;
+	init.flags = flags;
+
+	pll->pllcon = pllcon;
+	pll->hw.init = &init;
+
+	ret = of_clk_hw_register(np, &pll->hw);
+	if (ret) {
+		kfree(pll);
+		return ERR_PTR(ret);
+	}
+
+	return &pll->hw;
+}
+
+#define REG_CLKEN	0x00
+#define REG_CLKSEL	0x04
+#define REG_CLKDIV	0x08
+#define REG_PLLCON0	0x0c
+#define REG_PLLCON1	0x10
+#define REG_PMCON	0x14
+#define REG_IRQWAKECON	0x18
+#define REG_IRQWAKEFLAG	0x1c
+#define REG_IPSRST	0x20
+
+struct wpcm450_pll_data {
+	const char *name;
+	struct clk_parent_data parent;
+	unsigned int reg;
+	unsigned long flags;
+};
+
+static const struct wpcm450_pll_data pll_data[] = {
+	{ "pll0", { .fw_name = "ref" }, REG_PLLCON0, 0 },
+	{ "pll1", { .fw_name = "ref" }, REG_PLLCON1, 0 },
+};
+
+struct wpcm450_clksel_data {
+	const char *name;
+	const struct clk_parent_data *parents;
+	unsigned int num_parents;
+	const u32 *table;
+	int shift;
+	int width;
+	int index;
+	unsigned long flags;
+};
+
+static const u32 parent_table[] = { 0, 1, 2 };
+
+static const struct clk_parent_data default_parents[] = {
+	{ .name = "pll0" },
+	{ .name = "pll1" },
+	{ .name = "ref" },
+};
+
+static const struct clk_parent_data huart_parents[] = {
+	{ .fw_name = "ref" },
+	{ .name = "refdiv2" },
+};
+
+static const struct wpcm450_clksel_data clksel_data[] = {
+	{ "cpusel", default_parents, ARRAY_SIZE(default_parents),
+		parent_table, 0, 2, -1, CLK_IS_CRITICAL },
+	{ "clkout", default_parents, ARRAY_SIZE(default_parents),
+		parent_table, 2, 2, -1, 0 },
+	{ "usbphy", default_parents, ARRAY_SIZE(default_parents),
+		parent_table, 6, 2, -1, 0 },
+	{ "uartsel", default_parents, ARRAY_SIZE(default_parents),
+		parent_table, 8, 2, WPCM450_CLK_USBPHY, 0 },
+	{ "huartsel", huart_parents, ARRAY_SIZE(huart_parents),
+		parent_table, 10, 1, -1, 0 },
+};
+
+static const struct clk_div_table div_fixed2[] = {
+	{ .val = 0, .div = 2 },
+	{ }
+};
+
+struct wpcm450_clkdiv_data {
+	const char *name;
+	struct clk_parent_data parent;
+	int div_flags;
+	const struct clk_div_table *table;
+	int shift;
+	int width;
+	unsigned long flags;
+};
+
+static struct wpcm450_clkdiv_data clkdiv_data_early[] = {
+	{ "refdiv2", { .name = "ref" }, 0, div_fixed2, 0, 0 },
+};
+
+static const struct wpcm450_clkdiv_data clkdiv_data[] = {
+	{ "cpu", { .name = "cpusel" }, 0, div_fixed2, 0, 0, CLK_IS_CRITICAL },
+	{ "adcdiv", { .name = "ref" }, CLK_DIVIDER_POWER_OF_TWO, NULL, 28, 2, 0 },
+	{ "apb", { .name = "ahb" }, CLK_DIVIDER_POWER_OF_TWO, NULL, 26, 2, 0 },
+	{ "ahb", { .name = "cpu" }, CLK_DIVIDER_POWER_OF_TWO, NULL, 24, 2, 0 },
+	{ "uart", { .name = "uartsel" }, 0, NULL, 16, 4, 0 },
+	{ "ahb3", { .name = "ahb" }, CLK_DIVIDER_POWER_OF_TWO, NULL, 8, 2, 0 },
+};
+
+struct wpcm450_clken_data {
+	const char *name;
+	struct clk_parent_data parent;
+	int bitnum;
+	unsigned long flags;
+};
+
+static const struct wpcm450_clken_data clken_data[] = {
+	{ "fiu", { .name = "ahb3" }, WPCM450_CLK_FIU, 0 },
+	{ "xbus", { .name = "ahb3" }, WPCM450_CLK_XBUS, 0 },
+	{ "kcs", { .name = "apb" }, WPCM450_CLK_KCS, 0 },
+	{ "shm", { .name = "ahb3" }, WPCM450_CLK_SHM, 0 },
+	{ "usb1", { .name = "ahb" }, WPCM450_CLK_USB1, 0 },
+	{ "emc0", { .name = "ahb" }, WPCM450_CLK_EMC0, 0 },
+	{ "emc1", { .name = "ahb" }, WPCM450_CLK_EMC1, 0 },
+	{ "usb0", { .name = "ahb" }, WPCM450_CLK_USB0, 0 },
+	{ "peci", { .name = "apb" }, WPCM450_CLK_PECI, 0 },
+	{ "aes", { .name = "apb" }, WPCM450_CLK_AES, 0 },
+	{ "uart0", { .name = "uart" }, WPCM450_CLK_UART0, 0 },
+	{ "uart1", { .name = "uart" }, WPCM450_CLK_UART1, 0 },
+	{ "smb2", { .name = "apb" }, WPCM450_CLK_SMB2, 0 },
+	{ "smb3", { .name = "apb" }, WPCM450_CLK_SMB3, 0 },
+	{ "smb4", { .name = "apb" }, WPCM450_CLK_SMB4, 0 },
+	{ "smb5", { .name = "apb" }, WPCM450_CLK_SMB5, 0 },
+	{ "huart", { .name = "huartsel" }, WPCM450_CLK_HUART, 0 },
+	{ "pwm", { .name = "apb" }, WPCM450_CLK_PWM, 0 },
+	{ "timer0", { .name = "refdiv2" }, WPCM450_CLK_TIMER0, 0 },
+	{ "timer1", { .name = "refdiv2" }, WPCM450_CLK_TIMER1, 0 },
+	{ "timer2", { .name = "refdiv2" }, WPCM450_CLK_TIMER2, 0 },
+	{ "timer3", { .name = "refdiv2" }, WPCM450_CLK_TIMER3, 0 },
+	{ "timer4", { .name = "refdiv2" }, WPCM450_CLK_TIMER4, 0 },
+	{ "mft0", { .name = "apb" }, WPCM450_CLK_MFT0, 0 },
+	{ "mft1", { .name = "apb" }, WPCM450_CLK_MFT1, 0 },
+	{ "wdt", { .name = "refdiv2" }, WPCM450_CLK_WDT, 0 },
+	{ "adc", { .name = "adcdiv" }, WPCM450_CLK_ADC, 0 },
+	{ "sdio", { .name = "ahb" }, WPCM450_CLK_SDIO, 0 },
+	{ "sspi", { .name = "apb" }, WPCM450_CLK_SSPI, 0 },
+	{ "smb0", { .name = "apb" }, WPCM450_CLK_SMB0, 0 },
+	{ "smb1", { .name = "apb" }, WPCM450_CLK_SMB1, 0 },
+};
+
+static DEFINE_SPINLOCK(wpcm450_clk_lock);
+
+/*
+ * NOTE: Error handling is very rudimentary here. If the clock driver initial-
+ * ization fails, the system is probably in bigger trouble than what is caused
+ * by a few leaked resources.
+ */
+
+static void __init wpcm450_clk_init(struct device_node *np)
+{
+	struct clk_hw_onecell_data *clk_data;
+	static struct clk_hw **hws;
+	static struct clk_hw *hw;
+	void __iomem *clk_base;
+	int i, ret;
+	struct reset_simple_data *reset;
+
+	clk_base = of_iomap(np, 0);
+	if (!clk_base) {
+		pr_err("%pOFP: failed to map registers\n", np);
+		of_node_put(np);
+		return;
+	}
+	of_node_put(np);
+
+	clk_data = kzalloc(struct_size(clk_data, hws, WPCM450_NUM_CLKS), GFP_KERNEL);
+	if (!clk_data)
+		return;
+
+	clk_data->num = WPCM450_NUM_CLKS;
+	hws = clk_data->hws;
+
+	for (i = 0; i < WPCM450_NUM_CLKS; i++)
+		hws[i] = ERR_PTR(-ENOENT);
+
+	/* PLLs */
+	for (i = 0; i < ARRAY_SIZE(pll_data); i++) {
+		const struct wpcm450_pll_data *data = &pll_data[i];
+
+		hw = wpcm450_clk_register_pll(np, clk_base + data->reg, data->name,
+					      &data->parent, data->flags);
+		if (IS_ERR(hw)) {
+			pr_info("Failed to register PLL: %pe\n", hw);
+			return;
+		}
+	}
+
+	/* Early divisors (REF/2) */
+	for (i = 0; i < ARRAY_SIZE(clkdiv_data_early); i++) {
+		const struct wpcm450_clkdiv_data *data = &clkdiv_data_early[i];
+
+		hw = clk_hw_register_divider_table_parent_data(NULL, data->name, &data->parent,
+							       data->flags, clk_base + REG_CLKDIV,
+							       data->shift, data->width,
+							       data->div_flags, data->table,
+							       &wpcm450_clk_lock);
+		if (IS_ERR(hw)) {
+			pr_err("Failed to register div table: %pe\n", hw);
+			return;
+		}
+	}
+
+	/* Selects/muxes */
+	for (i = 0; i < ARRAY_SIZE(clksel_data); i++) {
+		const struct wpcm450_clksel_data *data = &clksel_data[i];
+
+		hw = clk_hw_register_mux_parent_data(NULL, data->name, data->parents,
+						     data->num_parents, data->flags,
+						     clk_base + REG_CLKSEL, data->shift,
+						     data->width, 0,
+						     &wpcm450_clk_lock);
+		if (IS_ERR(hw)) {
+			pr_err("Failed to register mux: %pe\n", hw);
+			return;
+		}
+		if (data->index >= 0)
+			clk_data->hws[data->index] = hw;
+	}
+
+	/* Divisors */
+	for (i = 0; i < ARRAY_SIZE(clkdiv_data); i++) {
+		const struct wpcm450_clkdiv_data *data = &clkdiv_data[i];
+
+		hw = clk_hw_register_divider_table_parent_data(NULL, data->name, &data->parent,
+							       data->flags, clk_base + REG_CLKDIV,
+							       data->shift, data->width,
+							       data->div_flags, data->table,
+							       &wpcm450_clk_lock);
+		if (IS_ERR(hw)) {
+			pr_err("Failed to register divider: %pe\n", hw);
+			return;
+		}
+	}
+
+	/* Enables/gates */
+	for (i = 0; i < ARRAY_SIZE(clken_data); i++) {
+		const struct wpcm450_clken_data *data = &clken_data[i];
+
+		hw = clk_hw_register_gate_parent_data(NULL, data->name, &data->parent, data->flags,
+						      clk_base + REG_CLKEN, data->bitnum,
+						      data->flags, &wpcm450_clk_lock);
+		if (IS_ERR(hw)) {
+			pr_err("Failed to register gate: %pe\n", hw);
+			return;
+		}
+		clk_data->hws[data->bitnum] = hw;
+	}
+
+	ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
+	if (ret)
+		pr_err("Failed to add DT provider: %pe\n", ERR_PTR(ret));
+
+	/* Reset controller */
+	reset = kzalloc(sizeof(*reset), GFP_KERNEL);
+	if (!reset)
+		return;
+	reset->rcdev.owner = THIS_MODULE;
+	reset->rcdev.nr_resets = WPCM450_NUM_RESETS;
+	reset->rcdev.ops = &reset_simple_ops;
+	reset->rcdev.of_node = np;
+	reset->membase = clk_base + REG_IPSRST;
+	ret = reset_controller_register(&reset->rcdev);
+	if (ret)
+		pr_err("Failed to register reset controller: %pe\n", ERR_PTR(ret));
+
+	of_node_put(np);
+}
+
+CLK_OF_DECLARE(wpcm450_clk_init, "nuvoton,wpcm450-clk", wpcm450_clk_init);

--
2.43.0


^ permalink raw reply related

* [PATCH v11 2/4] ARM: dts: wpcm450: Remove clock-output-names from reference clock node
From: Jonathan Neuschäfer @ 2024-04-01 14:06 UTC (permalink / raw)
  To: openbmc, linux-clk, devicetree
  Cc: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Joel Stanley, Philipp Zabel, linux-kernel,
	Jonathan Neuschäfer
In-Reply-To: <20240401-wpcm-clk-v11-0-379472961244@gmx.net>

A previous version of the as-yet unmerged clk-wpcm450 driver[1] used the
output name, but in the current iteration it doesn't rely on it anymore.
Thus it can be removed from the devicetree.

[1]: Added in "clk: wpcm450: Add Nuvoton WPCM450 clock/reset controller driver"

Fixes: 362e8be2ec04a6 ("ARM: dts: wpcm450: Add clock controller node")
Signed-off-by: Jonathan Neuschäfer <j.neuschaefer@gmx.net>
---

v11:
- Specify since when clock-output-names is unnecessary

v10:
- no changes

v9:
- New patch
---
 arch/arm/boot/dts/nuvoton/nuvoton-wpcm450.dtsi | 1 -
 1 file changed, 1 deletion(-)

diff --git a/arch/arm/boot/dts/nuvoton/nuvoton-wpcm450.dtsi b/arch/arm/boot/dts/nuvoton/nuvoton-wpcm450.dtsi
index 6e1f0f164cb4f5..9dfdd8f67319d3 100644
--- a/arch/arm/boot/dts/nuvoton/nuvoton-wpcm450.dtsi
+++ b/arch/arm/boot/dts/nuvoton/nuvoton-wpcm450.dtsi
@@ -40,7 +40,6 @@ clk24m: clock-24mhz {
 	refclk: clock-48mhz {
 		/* 48 MHz reference oscillator */
 		compatible = "fixed-clock";
-		clock-output-names = "ref";
 		clock-frequency = <48000000>;
 		#clock-cells = <0>;
 	};

--
2.43.0


^ permalink raw reply related

* [PATCH v11 1/4] dt-bindings: clock: Add Nuvoton WPCM450 clock/reset controller
From: Jonathan Neuschäfer @ 2024-04-01 14:06 UTC (permalink / raw)
  To: openbmc, linux-clk, devicetree
  Cc: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Joel Stanley, Philipp Zabel, linux-kernel,
	Jonathan Neuschäfer, Krzysztof Kozlowski
In-Reply-To: <20240401-wpcm-clk-v11-0-379472961244@gmx.net>

The Nuvoton WPCM450 SoC has a combined clock and reset controller.
Add a devicetree binding for it, as well as definitions for the bit
numbers used by it.

Signed-off-by: Jonathan Neuschäfer <j.neuschaefer@gmx.net>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
---
v10-v11:
- no changes

v9:
- Remove clock-output-names in example, because it's now unnecessary due
  to driver improvements

v5-v8:
- no changes

v4:
- https://lore.kernel.org/lkml/20220610072141.347795-4-j.neuschaefer@gmx.net/
- Add R-b tag

v3:
- Change clock-output-names and clock-names from "refclk" to "ref", suggested
  by Krzysztof Kozlowski

v2:
- https://lore.kernel.org/lkml/20220429172030.398011-5-j.neuschaefer@gmx.net/
- Various improvements, suggested by Krzysztof Kozlowski

v1:
- https://lore.kernel.org/lkml/20220422183012.444674-5-j.neuschaefer@gmx.net/
---
 .../bindings/clock/nuvoton,wpcm450-clk.yaml        | 65 +++++++++++++++++++++
 include/dt-bindings/clock/nuvoton,wpcm450-clk.h    | 67 ++++++++++++++++++++++
 2 files changed, 132 insertions(+)

diff --git a/Documentation/devicetree/bindings/clock/nuvoton,wpcm450-clk.yaml b/Documentation/devicetree/bindings/clock/nuvoton,wpcm450-clk.yaml
new file mode 100644
index 00000000000000..93521cf68a040f
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/nuvoton,wpcm450-clk.yaml
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/nuvoton,wpcm450-clk.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nuvoton WPCM450 clock controller
+
+maintainers:
+  - Jonathan Neuschäfer <j.neuschaefer@gmx.net>
+
+description:
+  The clock controller of the Nuvoton WPCM450 SoC supplies clocks and resets to
+  the rest of the chip.
+
+properties:
+  compatible:
+    const: nuvoton,wpcm450-clk
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: Reference clock oscillator (should be 48 MHz)
+
+  clock-names:
+    items:
+      - const: ref
+
+  '#clock-cells':
+    const: 1
+
+  '#reset-cells':
+    const: 1
+
+additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - '#clock-cells'
+
+examples:
+  - |
+    #include <dt-bindings/clock/nuvoton,wpcm450-clk.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    refclk: clock-48mhz {
+      /* 48 MHz reference oscillator */
+      compatible = "fixed-clock";
+      clock-frequency = <48000000>;
+      #clock-cells = <0>;
+    };
+
+    clk: clock-controller@b0000200 {
+      reg = <0xb0000200 0x100>;
+      compatible = "nuvoton,wpcm450-clk";
+      clocks = <&refclk>;
+      clock-names = "ref";
+      #clock-cells = <1>;
+      #reset-cells = <1>;
+    };
diff --git a/include/dt-bindings/clock/nuvoton,wpcm450-clk.h b/include/dt-bindings/clock/nuvoton,wpcm450-clk.h
new file mode 100644
index 00000000000000..86e1c895921b71
--- /dev/null
+++ b/include/dt-bindings/clock/nuvoton,wpcm450-clk.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+
+#ifndef _DT_BINDINGS_CLOCK_NUVOTON_WPCM450_CLK_H
+#define _DT_BINDINGS_CLOCK_NUVOTON_WPCM450_CLK_H
+
+/* Clocks based on CLKEN bits */
+#define WPCM450_CLK_FIU            0
+#define WPCM450_CLK_XBUS           1
+#define WPCM450_CLK_KCS            2
+#define WPCM450_CLK_SHM            4
+#define WPCM450_CLK_USB1           5
+#define WPCM450_CLK_EMC0           6
+#define WPCM450_CLK_EMC1           7
+#define WPCM450_CLK_USB0           8
+#define WPCM450_CLK_PECI           9
+#define WPCM450_CLK_AES           10
+#define WPCM450_CLK_UART0         11
+#define WPCM450_CLK_UART1         12
+#define WPCM450_CLK_SMB2          13
+#define WPCM450_CLK_SMB3          14
+#define WPCM450_CLK_SMB4          15
+#define WPCM450_CLK_SMB5          16
+#define WPCM450_CLK_HUART         17
+#define WPCM450_CLK_PWM           18
+#define WPCM450_CLK_TIMER0        19
+#define WPCM450_CLK_TIMER1        20
+#define WPCM450_CLK_TIMER2        21
+#define WPCM450_CLK_TIMER3        22
+#define WPCM450_CLK_TIMER4        23
+#define WPCM450_CLK_MFT0          24
+#define WPCM450_CLK_MFT1          25
+#define WPCM450_CLK_WDT           26
+#define WPCM450_CLK_ADC           27
+#define WPCM450_CLK_SDIO          28
+#define WPCM450_CLK_SSPI          29
+#define WPCM450_CLK_SMB0          30
+#define WPCM450_CLK_SMB1          31
+
+/* Other clocks */
+#define WPCM450_CLK_USBPHY        32
+
+#define WPCM450_NUM_CLKS          33
+
+/* Resets based on IPSRST bits */
+#define WPCM450_RESET_FIU          0
+#define WPCM450_RESET_EMC0         6
+#define WPCM450_RESET_EMC1         7
+#define WPCM450_RESET_USB0         8
+#define WPCM450_RESET_USB1         9
+#define WPCM450_RESET_AES_PECI    10
+#define WPCM450_RESET_UART        11
+#define WPCM450_RESET_MC          12
+#define WPCM450_RESET_SMB2        13
+#define WPCM450_RESET_SMB3        14
+#define WPCM450_RESET_SMB4        15
+#define WPCM450_RESET_SMB5        16
+#define WPCM450_RESET_PWM         18
+#define WPCM450_RESET_TIMER       19
+#define WPCM450_RESET_ADC         27
+#define WPCM450_RESET_SDIO        28
+#define WPCM450_RESET_SSPI        29
+#define WPCM450_RESET_SMB0        30
+#define WPCM450_RESET_SMB1        31
+
+#define WPCM450_NUM_RESETS        32
+
+#endif /* _DT_BINDINGS_CLOCK_NUVOTON_WPCM450_CLK_H */

--
2.43.0


^ permalink raw reply related

* [PATCH v11 0/4] Nuvoton WPCM450 clock and reset driver
From: Jonathan Neuschäfer @ 2024-04-01 14:06 UTC (permalink / raw)
  To: openbmc, linux-clk, devicetree
  Cc: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Joel Stanley, Philipp Zabel, linux-kernel,
	Jonathan Neuschäfer, Krzysztof Kozlowski

This series adds support for the clock and reset controller in the Nuvoton
WPCM450 SoC. This means that the clock rates for peripherals will be calculated
automatically based on the clock tree as it was preconfigured by the bootloader.
The 24 MHz dummy clock, that is currently in the devicetree, is no longer needed.
Somewhat unfortunately, this also means that there is a breaking change once
the devicetree starts relying on the clock driver, but I find it acceptable in
this case, because WPCM450 is still at a somewhat early stage.

v11:
- Improved description in "ARM: dts: wpcm450: Remove clock-output-names
  from reference clock node"
- some minor format differences due to switching to B4

v10:
- A small tweak (using selected instead of extending an already-long
  default line) in Kconfig, for better robustness

v9:
- Various improvements to the driver
- No longer use global clock names (and the clock-output-names property)
  to refer to the reference clock, but instead rely on a phandle reference

v8:
- https://lore.kernel.org/lkml/20230428190226.1304326-1-j.neuschaefer@gmx.net/
- Use %pe throughout the driver

v7:
- Simplified the error handling, by largely removing resource
  deallocation, which:
  - was already incomplete
  - would only happen in a case when the system is in pretty bad state
    because the clock driver didn't initialize correctly (in other
    words, the clock driver isn't optional enough that complex error
    handling really pays off)

v6:
- Dropped all patches except the clock binding and the clock driver, because
  they have mostly been merged
- Minor correction to how RESET_SIMPLE is selected

v5:
- Dropped patch 2 (watchdog: npcm: Enable clock if provided), which
  was since merged upstream
- Added patch 2 (clocksource: timer-npcm7xx: Enable timer 1 clock before use) again,
  because I wasn't able to find it in linux-next
- Switched the driver to using struct clk_parent_data
- Rebased on 6.1-rc3

v4:
- Leave WDT clock running during after restart handler
- Fix reset controller initialization
- Dropped patch 2/7 (clocksource: timer-npcm7xx: Enable timer 1 clock before use),
  as it was applied by Daniel Lezcano

v3:
- https://lore.kernel.org/lkml/20220508194333.2170161-1-j.neuschaefer@gmx.net/
- Changed "refclk" string to "ref"
- Fixed some dead code in the driver
- Added clk_prepare_enable call to the watchdog restart handler
- Added a few review tags

v2:
- https://lore.kernel.org/lkml/20220429172030.398011-1-j.neuschaefer@gmx.net/
- various small improvements

v1:
- https://lore.kernel.org/lkml/20220422183012.444674-1-j.neuschaefer@gmx.net/

Signed-off-by: Jonathan Neuschäfer <j.neuschaefer@gmx.net>
---
Jonathan Neuschäfer (4):
      dt-bindings: clock: Add Nuvoton WPCM450 clock/reset controller
      ARM: dts: wpcm450: Remove clock-output-names from reference clock node
      clk: wpcm450: Add Nuvoton WPCM450 clock/reset controller driver
      ARM: dts: wpcm450: Switch clocks to clock controller

 .../bindings/clock/nuvoton,wpcm450-clk.yaml        |  65 ++++
 arch/arm/boot/dts/nuvoton/nuvoton-wpcm450.dtsi     |  23 +-
 drivers/clk/Makefile                               |   2 +-
 drivers/clk/nuvoton/Kconfig                        |  10 +-
 drivers/clk/nuvoton/Makefile                       |   1 +
 drivers/clk/nuvoton/clk-wpcm450.c                  | 372 +++++++++++++++++++++
 include/dt-bindings/clock/nuvoton,wpcm450-clk.h    |  67 ++++
 7 files changed, 525 insertions(+), 15 deletions(-)
---
base-commit: 4cece764965020c22cff7665b18a012006359095
change-id: 20240330-wpcm-clk-222a37f59cfb

Best regards,
--
Jonathan Neuschäfer <j.neuschaefer@gmx.net>


^ permalink raw reply

* Re: [PATCH 07/10] dt-bindings: iio: dac: add bindings doc for AD9739A
From: Rob Herring @ 2024-04-01 14:02 UTC (permalink / raw)
  To: Nuno Sa
  Cc: linux-iio, devicetree, Dragos Bogdan, Jonathan Cameron,
	Lars-Peter Clausen, Michael Hennerich, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan
In-Reply-To: <20240328-iio-backend-axi-dac-v1-7-afc808b3fde3@analog.com>

On Thu, Mar 28, 2024 at 02:22:31PM +0100, Nuno Sa wrote:
> This adds the bindings documentation for the 14 bit
> RF Digital-to-Analog converter.

Reword the subject to say 'bindings' only once.

> 
> Signed-off-by: Nuno Sa <nuno.sa@analog.com>
> ---
>  .../devicetree/bindings/iio/dac/adi,ad9739a.yaml   | 88 ++++++++++++++++++++++
>  MAINTAINERS                                        |  8 ++
>  2 files changed, 96 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
> new file mode 100644
> index 000000000000..24bcec763a9b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
> @@ -0,0 +1,88 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/dac/adi,ad9739a.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Analog Devices AD9739A RF DAC
> +
> +maintainers:
> +  - Dragos Bogdan <dragos.bogdan@analog.com>
> +  - Nuno Sa <nuno.sa@analog.com>
> +
> +description: |
> +  The AD9739A is a 14-bit, 2.5 GSPS high performance RF DACs that are capable
> +  of synthesizing wideband signals from dc up to 3 GHz.
> +
> +  https://www.analog.com/media/en/technical-documentation/data-sheets/ad9737a_9739a.pdf
> +
> +properties:
> +  compatible:
> +    enum:
> +      - adi,ad9739a
> +
> +  reg:
> +    maxItems: 1
> +
> +  clocks:
> +    maxItems: 1
> +
> +  reset-gpios:
> +    maxItems: 1
> +
> +  vdd_3_3-supply:

vdd-3-3-supply or vdd-3p3-supply

> +    description: 3.3V Digital input supply.
> +
> +  vdd-supply:
> +    description: 1.8V Digital input supply.
> +
> +  vdda-supply:
> +    description: 3.3V Analog input supply.
> +
> +  vddc-supply:
> +    description: 1.8V Clock input supply.
> +
> +  io-backends:
> +    maxItems: 1
> +
> +  adi,full-scale-microamp:
> +    description: This property represents the DAC full scale current.
> +    minimum: 8700
> +    maximum: 31700
> +
> +required:
> +  - compatible
> +  - reg
> +  - clocks
> +  - io-backends
> +  - vdd_3_3-supply
> +  - vdd-supply
> +  - vdda-supply
> +  - vddc-supply
> +
> +allOf:
> +  - $ref: /schemas/spi/spi-peripheral-props.yaml#
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    spi {
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        dac@0 {
> +            compatible = "adi,ad9739a";
> +            reg = <0>;
> +
> +            clocks = <&dac_clk>;
> +
> +            io-backends = <&iio_backend>;
> +
> +            vdd_3_3-supply = <&vdd_3_3>;
> +            vdd-supply = <&vdd>;
> +            vdda-supply = <&vdd_3_3>;
> +            vddc-supply = <&vdd>;
> +        };
> +    };
> +...
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 2137eb452376..76e872e320d7 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1234,6 +1234,14 @@ W:	https://ez.analog.com/linux-software-drivers
>  F:	Documentation/devicetree/bindings/iio/adc/adi,ad7780.yaml
>  F:	drivers/iio/adc/ad7780.c
>  
> +ANALOG DEVICES INC AD9739a DRIVER
> +M:	Nuno Sa <nuno.sa@analog.com>
> +M:	Dragos Bogdan <dragos.bogdan@analog.com>
> +L:	linux-iio@vger.kernel.org
> +S:	Supported
> +W:	https://ez.analog.com/linux-software-drivers
> +F:	Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
> +
>  ANALOG DEVICES INC ADA4250 DRIVER
>  M:	Antoniu Miclaus <antoniu.miclaus@analog.com>
>  L:	linux-iio@vger.kernel.org
> 
> -- 
> 2.44.0
> 

^ permalink raw reply

* Re: [PATCH 06/10] dt-bindings: iio: dac: add bindings doc for AXI DAC driver
From: Rob Herring @ 2024-04-01 13:59 UTC (permalink / raw)
  To: Nuno Sa
  Cc: linux-iio, devicetree, Dragos Bogdan, Jonathan Cameron,
	Lars-Peter Clausen, Michael Hennerich, Krzysztof Kozlowski,
	Conor Dooley, Olivier Moysan
In-Reply-To: <20240328-iio-backend-axi-dac-v1-6-afc808b3fde3@analog.com>

On Thu, Mar 28, 2024 at 02:22:30PM +0100, Nuno Sa wrote:
> This adds the bindings documentation for the AXI DAC driver.

Bindings are for h/w blocks, not 'drivers'.

Reword the subject to only say 'bindings' once.


> Signed-off-by: Nuno Sa <nuno.sa@analog.com>
> ---
>  .../devicetree/bindings/iio/dac/adi,axi-dac.yaml   | 62 ++++++++++++++++++++++
>  MAINTAINERS                                        |  7 +++
>  2 files changed, 69 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> new file mode 100644
> index 000000000000..1018fd274f04
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> @@ -0,0 +1,62 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/dac/adi,axi-dac.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Analog Devices AXI DAC IP core
> +
> +maintainers:
> +  - Nuno Sa <nuno.sa@analog.com>
> +
> +description: |
> +  Analog Devices Generic AXI DAC IP core for interfacing a DAC device
> +  with a high speed serial (JESD204B/C) or source synchronous parallel
> +  interface (LVDS/CMOS).
> +  Usually, some other interface type (i.e SPI) is used as a control
> +  interface for the actual DAC, while this IP core will interface
> +  to the data-lines of the DAC and handle the streaming of data into
> +  memory via DMA.
> +
> +  https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
> +
> +properties:
> +  compatible:
> +    enum:
> +      - adi,axi-dac-9.1.b
> +
> +  reg:
> +    maxItems: 1
> +
> +  dmas:
> +    maxItems: 1
> +
> +  dma-names:
> +    items:
> +      - const: tx

You don't need *-names if there is only 1 entry.

> +
> +  clocks:
> +    maxItems: 1
> +
> +  '#io-backend-cells':
> +    const: 0
> +
> +required:
> +  - compatible
> +  - dmas
> +  - reg
> +  - clocks
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    dac@44a00000 {
> +        compatible = "adi,axi-dac-9.1.b";
> +        reg = <0x44a00000 0x10000>;
> +        dmas = <&tx_dma 0>;
> +        dma-names = "tx";
> +        #io-backend-cells = <0>;
> +        clocks = <&axi_clk>;
> +    };
> +...
> diff --git a/MAINTAINERS b/MAINTAINERS
> index a7287cf44869..2137eb452376 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1399,6 +1399,13 @@ F:	sound/soc/codecs/adav*
>  F:	sound/soc/codecs/sigmadsp.*
>  F:	sound/soc/codecs/ssm*
>  
> +ANALOG DEVICES INC AXI DAC DRIVER
> +M:	Nuno Sa <nuno.sa@analog.com>
> +L:	linux-iio@vger.kernel.org
> +S:	Supported
> +W:	https://ez.analog.com/linux-software-drivers
> +F:	Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
> +
>  ANALOG DEVICES INC DMA DRIVERS
>  M:	Lars-Peter Clausen <lars@metafoo.de>
>  S:	Supported
> 
> -- 
> 2.44.0
> 

^ permalink raw reply

* Re: [PATCH v6 3/4] dt-bindings: watchdog: aspeed-wdt: Add aspeed,scu
From: Rob Herring @ 2024-04-01 13:56 UTC (permalink / raw)
  To: Peter Yin
  Cc: patrick, Wim Van Sebroeck, Guenter Roeck, Krzysztof Kozlowski,
	Conor Dooley, Joel Stanley, Andrew Jeffery, linux-watchdog,
	devicetree, linux-arm-kernel, linux-aspeed, linux-kernel
In-Reply-To: <20240328022231.3649741-4-peteryin.openbmc@gmail.com>

On Thu, Mar 28, 2024 at 10:22:30AM +0800, Peter Yin wrote:
> To use the SCU register to obtain reset flags for supporting
> bootstatus.
> 
> Signed-off-by: Peter Yin <peteryin.openbmc@gmail.com>
> ---
>  Documentation/devicetree/bindings/watchdog/aspeed-wdt.txt | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/watchdog/aspeed-wdt.txt b/Documentation/devicetree/bindings/watchdog/aspeed-wdt.txt
> index 3208adb3e52e..80a1f58b5a2e 100644
> --- a/Documentation/devicetree/bindings/watchdog/aspeed-wdt.txt
> +++ b/Documentation/devicetree/bindings/watchdog/aspeed-wdt.txt
> @@ -8,6 +8,8 @@ Required properties:
>  
>   - reg: physical base address of the controller and length of memory mapped
>     region
> + - aspeed,scu: a reference to the System Control Unit node of the Aspeed
> +   SOC.

You cannot add new required properties as that is an ABI break.

If there's only 1 SCU instance, you can just fetch its node by 
compatible with no DT change.

What's the plan for converting this binding to schema? This is the 2nd 
new property in 6 months.

Rob

^ permalink raw reply

* Re: [PATCH v7 09/15] media: bcm2835-unicam: Add support for CCP2/CSI2 camera interface
From: Laurent Pinchart @ 2024-04-01 13:52 UTC (permalink / raw)
  To: Tomi Valkeinen
  Cc: linux-media, Dave Stevenson, David Plowman, Jean-Michel Hautbois,
	Hans Verkuil, Naushir Patuck, Sakari Ailus, kernel-list,
	linux-rpi-kernel, Florian Fainelli, Ray Jui, Scott Branden,
	bcm-kernel-feedback-list, Conor Dooley, Krzysztof Kozlowski,
	Rob Herring, devicetree
In-Reply-To: <b4506224-b75a-49d8-8651-a48f39149d52@ideasonboard.com>

On Wed, Mar 27, 2024 at 01:21:09PM +0200, Tomi Valkeinen wrote:
> On 25/03/2024 00:08, Laurent Pinchart wrote:
> > From: Dave Stevenson <dave.stevenson@raspberrypi.com>
> > 
> > Add a driver for the Unicam camera receiver block on BCM283x processors.
> > It is represented as two video device nodes: unicam-image and
> > unicam-embedded which are connected to an internal subdev (named
> > unicam-subdev) in order to manage streams routing.
> 
> Shouldn't this driver call get_frame_desc somewhere to get the VC and DT 
> for the streams?

Generally speaking, yes. In practice, configuring the DT from the frame
descriptor is probably not very useful, as CSI-2 sources that transmit
image data using a DT that doesn't correspond to the media bus code are
not very common and I don't expect this to be needed for unicam.
Configuring the VC, on the other hand, seems to me like a better use
case. I will add get_frame_desc support for the next version.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply

* Re: [PATCH v3 6/6] riscv: dts: starfive: add Milkv Mars board device tree
From: Heinrich Schuchardt @ 2024-04-01 13:28 UTC (permalink / raw)
  To: Jisheng Zhang
  Cc: Rob Herring, Krzysztof Kozlowski, Paul Walmsley, Palmer Dabbelt,
	Albert Ou, Emil Renner Berthing, linux-riscv, devicetree,
	linux-kernel, Conor Dooley, Aurelien Jarno
In-Reply-To: <ZgYn9t4akccWuHyf@xhacker>

On 3/29/24 03:31, Jisheng Zhang wrote:
> On Thu, Mar 28, 2024 at 10:28:28PM +0100, Heinrich Schuchardt wrote:
>> On 2/6/24 20:13, Conor Dooley wrote:
>>> On Wed, Jan 31, 2024 at 09:26:00PM +0800, Jisheng Zhang wrote:
>>>> The Milkv Mars is a development board based on the Starfive JH7110 SoC.
>>>> The board features:
>>>>
>>>> - JH7110 SoC
>>>> - 1/2/4/8 GiB LPDDR4 DRAM
>>>> - AXP15060 PMIC
>>>> - 40 pin GPIO header
>>>> - 3x USB 3.0 host port
>>>> - 1x USB 2.0 host port
>>>> - 1x M.2 E-Key
>>>> - 1x eMMC slot
>>>> - 1x MicroSD slot
>>>> - 1x QSPI Flash
>>>> - 1x 1Gbps Ethernet port
>>>> - 1x HDMI port
>>>> - 1x 2-lane DSI and 1x 4-lane DSI
>>>> - 1x 2-lane CSI
>>>>
>>>> Add the devicetree file describing the currently supported features,
>>>> namely PMIC, UART, I2C, GPIO, SD card, QSPI Flash, eMMC and Ethernet.
>>>>
>>>> Signed-off-by: Jisheng Zhang <jszhang@kernel.org>
>>>
>>> Got a dtbs_check issue in the patchwork CI:
>>>
>>>     +arch/riscv/boot/dts/starfive/jh7110-milkv-mars.dtb: gmac1-rgmii-rxin-clock: 'clock-frequency' is a required property
>>>     +	from schema $id: http://devicetree.org/schemas/clock/fixed-clock.yaml#
>>>     +arch/riscv/boot/dts/starfive/jh7110-milkv-mars.dtb: gmac1-rmii-refin-clock: 'clock-frequency' is a required property
>>>     +	from schema $id: http://devicetree.org/schemas/clock/fixed-clock.yaml#
>>>
>>> Can you fix that please? Also, I applied some patches the other day that
>>> seem to conflict quite a bit with the common board dts patch. Would you
>>> please do a rebase on top of that please?
>>>
>>> Cheers,
>>> Conor.
>>>
>>>> ---
>>>>    arch/riscv/boot/dts/starfive/Makefile         |  1 +
>>>>    .../boot/dts/starfive/jh7110-milkv-mars.dts   | 35 +++++++++++++++++++
>>>>    2 files changed, 36 insertions(+)
>>>>    create mode 100644 arch/riscv/boot/dts/starfive/jh7110-milkv-mars.dts
>>>>
>>>> diff --git a/arch/riscv/boot/dts/starfive/Makefile b/arch/riscv/boot/dts/starfive/Makefile
>>>> index 0141504c0f5c..2fa0cd7f31c3 100644
>>>> --- a/arch/riscv/boot/dts/starfive/Makefile
>>>> +++ b/arch/riscv/boot/dts/starfive/Makefile
>>>> @@ -8,5 +8,6 @@ DTC_FLAGS_jh7110-starfive-visionfive-2-v1.3b := -@
>>>>    dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-beaglev-starlight.dtb
>>>>    dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-starfive-visionfive-v1.dtb
>>>> +dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-milkv-mars.dtb
>>>>    dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.2a.dtb
>>>>    dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb
>>>> diff --git a/arch/riscv/boot/dts/starfive/jh7110-milkv-mars.dts b/arch/riscv/boot/dts/starfive/jh7110-milkv-mars.dts
>>>> new file mode 100644
>>>> index 000000000000..de600e799e7d
>>>> --- /dev/null
>>>> +++ b/arch/riscv/boot/dts/starfive/jh7110-milkv-mars.dts
>>>> @@ -0,0 +1,35 @@
>>>> +// SPDX-License-Identifier: GPL-2.0 OR MIT
>>>> +/*
>>>> + * Copyright (C) 2023 Jisheng Zhang <jszhang@kernel.org>
>>>> + */
>>>> +
>>>> +/dts-v1/;
>>>> +#include "jh7110-visionfive2-mars-common.dtsi"
>>>> +
>>>> +/ {
>>>> +	model = "Milk-V Mars";
>>>> +	compatible = "milkv,mars", "starfive,jh7110";
>>>> +};
>>>> +
>>>> +&gmac0 {
>>>> +	starfive,tx-use-rgmii-clk;
>>>> +	assigned-clocks = <&aoncrg JH7110_AONCLK_GMAC0_TX>;
>>>> +	assigned-clock-parents = <&aoncrg JH7110_AONCLK_GMAC0_RMII_RTX>;
>>>> +};
>>>> +
>>>> +
>>>> +&phy0 {
>>>> +	motorcomm,tx-clk-adj-enabled;
>>>> +	motorcomm,tx-clk-10-inverted;
>>>> +	motorcomm,tx-clk-100-inverted;
>>>> +	motorcomm,tx-clk-1000-inverted;
>>>> +	motorcomm,rx-clk-drv-microamp = <3970>;
>>>> +	motorcomm,rx-data-drv-microamp = <2910>;
>>>> +	rx-internal-delay-ps = <1500>;
>>>> +	tx-internal-delay-ps = <1500>;
>>>> +};
>>>> +
>>>> +&mmc1 {
>>>> +	disable-wp;
>>
>> Due to which difference is 'disable-wp' necessary for the Mars board and not
>> necessary for the VisionFive 2 board?
> 
> Mars doesn't have wp pin, but dunno vf2 case since I don't have a VF2
> board ;)

If the Milk-V Mars does not have a WP GPIO, we should be able to drop 
this property. The VisionFive 2 does not need it either.

>>
>>>> +	cd-gpios = <&sysgpio 41 GPIO_ACTIVE_LOW>;
>>
>> On my VisionFive 2 1.2B, and 1.3A boards GPIO 41 reflects if an SD-card is
>> inserted (as shown in U-Boot by gpio status -a). So shouldn't this value be
>> moved to the common include "jh7110-visionfive2-mars-common.dtsi" and
>> broken-cd removed from the VisionFive2 board?
> 
> I tested the CD pin and can confirm it works on Mars, but I dunno whether
> this works on VF2 since I have no VF2 board.
> Could you please check whether it works or not on VF2?

As mentioned in my prior mail the card detect GPIO is working on the 
VisionFive 2. StarFive acknowledged my U-Boot patch:

https://lore.kernel.org/u-boot/SHXPR01MB086314C47C281B3DDDF7BAE9E63AA@SHXPR01MB0863.CHNPR01.prod.partner.outlook.cn/

Best regards

Heinrich

> 
>>
>> https://doc-en.rvspace.org/VisionFive2/PDF/SCH_RV002_V1.2A_20221216.pdf
>> has a line
>>
>>      GPIO41 | SD_SDIO0_CD_GPIO41 | Micro SD:J10
>>
>> Best regards
>>
>> Heinrich
>>
>>>> +};
>>>> -- 
>>>> 2.43.0
>>


^ permalink raw reply

* Re: [PATCH v7 2/2] dmaengine: Loongson1: Add Loongson-1 APB DMA driver
From: Huacai Chen @ 2024-04-01 13:24 UTC (permalink / raw)
  To: Keguang Zhang
  Cc: Vinod Koul, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	linux-mips, dmaengine, devicetree, linux-kernel
In-Reply-To: <CAJhJPsUW14vMAAhGTobCkSnYytwtUbdsZ5V9p33fzdnr3=L2Ag@mail.gmail.com>

On Mon, Apr 1, 2024 at 7:10 PM Keguang Zhang <keguang.zhang@gmail.com> wrote:
>
> On Mon, Apr 1, 2024 at 5:06 PM Huacai Chen <chenhuacai@kernel.org> wrote:
> >
> > On Mon, Apr 1, 2024 at 10:45 AM Keguang Zhang <keguang.zhang@gmail.com> wrote:
> > >
> > > Hi Huacai,
> > >
> > > On Sat, Mar 30, 2024 at 9:59 PM Huacai Chen <chenhuacai@kernel.org> wrote:
> > > >
> > > > Hi, Keguang,
> > > >
> > > > On Fri, Mar 29, 2024 at 7:28 PM Keguang Zhang via B4 Relay
> > > > <devnull+keguang.zhang.gmail.com@kernel.org> wrote:
> > > > >
> > > > > From: Keguang Zhang <keguang.zhang@gmail.com>
> > > > >
> > > > > This patch adds APB DMA driver for Loongson-1 SoCs.
> > > > >
> > > > > Signed-off-by: Keguang Zhang <keguang.zhang@gmail.com>
> > > > > ---
> > > > > Changes in v7:
> > > > > - Change the comptible to 'loongson,ls1*-apbdma'
> > > > > - Update Kconfig and Makefile accordingly
> > > > > - Rename the file to loongson1-apb-dma.c to keep the consistency
> > > > >
> > > > > Changes in v6:
> > > > > - Implement .device_prep_dma_cyclic for Loongson1 audio driver,
> > > > > - as well as .device_pause and .device_resume.
> > > > > - Set the limitation LS1X_DMA_MAX_DESC and put all descriptors
> > > > > - into one page to save memory
> > > > > - Move dma_pool_zalloc() into ls1x_dma_alloc_desc()
> > > > > - Drop dma_slave_config structure
> > > > > - Use .remove_new instead of .remove
> > > > > - Use KBUILD_MODNAME for the driver name
> > > > > - Improve the debug information
> > > > >
> > > > > Changes in v5:
> > > > > - Add DT support
> > > > > - Use DT data instead of platform data
> > > > > - Use chan_id of struct dma_chan instead of own id
> > > > > - Use of_dma_xlate_by_chan_id() instead of ls1x_dma_filter()
> > > > > - Update the author information to my official name
> > > > >
> > > > > Changes in v4:
> > > > > - Use dma_slave_map to find the proper channel.
> > > > > - Explicitly call devm_request_irq() and tasklet_kill().
> > > > > - Fix namespace issue.
> > > > > - Some minor fixes and cleanups.
> > > > >
> > > > > Changes in v3:
> > > > > - Rename ls1x_dma_filter_fn to ls1x_dma_filter.
> > > > >
> > > > > Changes in v2:
> > > > > - Change the config from 'DMA_LOONGSON1' to 'LOONGSON1_DMA',
> > > > > - and rearrange it in alphabetical order in Kconfig and Makefile.
> > > > > - Fix comment style.
> > > > > ---
> > > > >  drivers/dma/Kconfig             |   9 +
> > > > >  drivers/dma/Makefile            |   1 +
> > > > >  drivers/dma/loongson1-apb-dma.c | 665 ++++++++++++++++++++++++++++++++++++++++
> > > > >  3 files changed, 675 insertions(+)
> > > > >
> > > > > diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
> > > > > index 002a5ec80620..f7b06c4cdf3f 100644
> > > > > --- a/drivers/dma/Kconfig
> > > > > +++ b/drivers/dma/Kconfig
> > > > > @@ -369,6 +369,15 @@ config K3_DMA
> > > > >           Support the DMA engine for Hisilicon K3 platform
> > > > >           devices.
> > > > >
> > > > > +config LOONGSON1_APB_DMA
> > > > > +       tristate "Loongson1 APB DMA support"
> > > > > +       depends on MACH_LOONGSON32 || COMPILE_TEST
> > > > > +       select DMA_ENGINE
> > > > > +       select DMA_VIRTUAL_CHANNELS
> > > > > +       help
> > > > > +         This selects support for the APB DMA controller in Loongson1 SoCs,
> > > > > +         which is required by Loongson1 NAND and audio support.
> > > > Why not rename to LS1X_APB_DMA and put it just before LS2X_APB_DMA
> > > > (and also the driver file name)?
> > > >
> > > So far all Kconfig entries of Loongson-1 drivers are named with the
> > > keyword "LOONGSON1".
> > > The same is true for these file names.
> > > Therefore, I need to keep the consistency.
> > But I see LS1X_IRQ in drivers/irqchip/Kconfig
> >
> Indeed, that's an exception, which was submitted by Jiaxun several years ago.
> Actually, most drivers of Loongson family use the keyword "LOONGSON"
> for Kconfig and "loongson" for filename.
> Thus I take this keywork as the naming convention.
But I think keeping consistency in a same subsystem is better than
keeping consistency in a same SoC (but cross subsystems).

Huacai

>
> > Huacai
> >
> > >
> > >
> > > > Huacai
> > > >
> > > > > +
> > > > >  config LPC18XX_DMAMUX
> > > > >         bool "NXP LPC18xx/43xx DMA MUX for PL080"
> > > > >         depends on ARCH_LPC18XX || COMPILE_TEST
> > > > > diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
> > > > > index dfd40d14e408..b26f6677978a 100644
> > > > > --- a/drivers/dma/Makefile
> > > > > +++ b/drivers/dma/Makefile
> > > > > @@ -47,6 +47,7 @@ obj-$(CONFIG_INTEL_IDMA64) += idma64.o
> > > > >  obj-$(CONFIG_INTEL_IOATDMA) += ioat/
> > > > >  obj-y += idxd/
> > > > >  obj-$(CONFIG_K3_DMA) += k3dma.o
> > > > > +obj-$(CONFIG_LOONGSON1_APB_DMA) += loongson1-apb-dma.o
> > > > >  obj-$(CONFIG_LPC18XX_DMAMUX) += lpc18xx-dmamux.o
> > > > >  obj-$(CONFIG_LS2X_APB_DMA) += ls2x-apb-dma.o
> > > > >  obj-$(CONFIG_MILBEAUT_HDMAC) += milbeaut-hdmac.o
> > > > > diff --git a/drivers/dma/loongson1-apb-dma.c b/drivers/dma/loongson1-apb-dma.c
> > > > > new file mode 100644
> > > > > index 000000000000..d474a2601e6e
> > > > > --- /dev/null
> > > > > +++ b/drivers/dma/loongson1-apb-dma.c
> > > > > @@ -0,0 +1,665 @@
> > > > > +// SPDX-License-Identifier: GPL-2.0-or-later
> > > > > +/*
> > > > > + * Driver for Loongson-1 APB DMA Controller
> > > > > + *
> > > > > + * Copyright (C) 2015-2024 Keguang Zhang <keguang.zhang@gmail.com>
> > > > > + */
> > > > > +
> > > > > +#include <linux/dmapool.h>
> > > > > +#include <linux/dma-mapping.h>
> > > > > +#include <linux/init.h>
> > > > > +#include <linux/interrupt.h>
> > > > > +#include <linux/iopoll.h>
> > > > > +#include <linux/module.h>
> > > > > +#include <linux/of.h>
> > > > > +#include <linux/of_dma.h>
> > > > > +#include <linux/platform_device.h>
> > > > > +#include <linux/slab.h>
> > > > > +
> > > > > +#include "dmaengine.h"
> > > > > +#include "virt-dma.h"
> > > > > +
> > > > > +/* Loongson-1 DMA Control Register */
> > > > > +#define DMA_CTRL                       0x0
> > > > > +
> > > > > +/* DMA Control Register Bits */
> > > > > +#define DMA_STOP                       BIT(4)
> > > > > +#define DMA_START                      BIT(3)
> > > > > +#define DMA_ASK_VALID                  BIT(2)
> > > > > +
> > > > > +#define DMA_ADDR_MASK                  GENMASK(31, 6)
> > > > > +
> > > > > +/* DMA Next Field Bits */
> > > > > +#define DMA_NEXT_VALID                 BIT(0)
> > > > > +
> > > > > +/* DMA Command Field Bits */
> > > > > +#define DMA_RAM2DEV                    BIT(12)
> > > > > +#define DMA_INT                                BIT(1)
> > > > > +#define DMA_INT_MASK                   BIT(0)
> > > > > +
> > > > > +#define LS1X_DMA_MAX_CHANNELS          3
> > > > > +
> > > > > +/* Size of allocations for hardware descriptors */
> > > > > +#define LS1X_DMA_DESCS_SIZE            PAGE_SIZE
> > > > > +#define LS1X_DMA_MAX_DESC              \
> > > > > +       (LS1X_DMA_DESCS_SIZE / sizeof(struct ls1x_dma_hwdesc))
> > > > > +
> > > > > +struct ls1x_dma_hwdesc {
> > > > > +       u32 next;               /* next descriptor address */
> > > > > +       u32 saddr;              /* memory DMA address */
> > > > > +       u32 daddr;              /* device DMA address */
> > > > > +       u32 length;
> > > > > +       u32 stride;
> > > > > +       u32 cycles;
> > > > > +       u32 cmd;
> > > > > +       u32 stats;
> > > > > +};
> > > > > +
> > > > > +struct ls1x_dma_desc {
> > > > > +       struct virt_dma_desc vdesc;
> > > > > +       enum dma_transfer_direction dir;
> > > > > +       enum dma_transaction_type type;
> > > > > +       unsigned int bus_width;
> > > > > +
> > > > > +       unsigned int nr_descs;  /* number of descriptors */
> > > > > +
> > > > > +       struct ls1x_dma_hwdesc *hwdesc;
> > > > > +       dma_addr_t hwdesc_phys;
> > > > > +};
> > > > > +
> > > > > +struct ls1x_dma_chan {
> > > > > +       struct virt_dma_chan vchan;
> > > > > +       struct dma_pool *desc_pool;
> > > > > +       phys_addr_t src_addr;
> > > > > +       phys_addr_t dst_addr;
> > > > > +       enum dma_slave_buswidth src_addr_width;
> > > > > +       enum dma_slave_buswidth dst_addr_width;
> > > > > +
> > > > > +       void __iomem *reg_base;
> > > > > +       int irq;
> > > > > +
> > > > > +       struct ls1x_dma_desc *desc;
> > > > > +
> > > > > +       struct ls1x_dma_hwdesc *curr_hwdesc;
> > > > > +       dma_addr_t curr_hwdesc_phys;
> > > > > +};
> > > > > +
> > > > > +struct ls1x_dma {
> > > > > +       struct dma_device ddev;
> > > > > +       void __iomem *reg_base;
> > > > > +
> > > > > +       unsigned int nr_chans;
> > > > > +       struct ls1x_dma_chan chan[];
> > > > > +};
> > > > > +
> > > > > +#define to_ls1x_dma_chan(dchan)                \
> > > > > +       container_of(dchan, struct ls1x_dma_chan, vchan.chan)
> > > > > +
> > > > > +#define to_ls1x_dma_desc(vd)           \
> > > > > +       container_of(vd, struct ls1x_dma_desc, vdesc)
> > > > > +
> > > > > +/* macros for registers read/write */
> > > > > +#define chan_readl(chan, off)          \
> > > > > +       readl((chan)->reg_base + (off))
> > > > > +
> > > > > +#define chan_writel(chan, off, val)    \
> > > > > +       writel((val), (chan)->reg_base + (off))
> > > > > +
> > > > > +static inline struct device *chan2dev(struct dma_chan *chan)
> > > > > +{
> > > > > +       return &chan->dev->device;
> > > > > +}
> > > > > +
> > > > > +static inline int ls1x_dma_query(struct ls1x_dma_chan *chan,
> > > > > +                                dma_addr_t *hwdesc_phys)
> > > > > +{
> > > > > +       struct dma_chan *dchan = &chan->vchan.chan;
> > > > > +       int val, ret;
> > > > > +
> > > > > +       val = *hwdesc_phys & DMA_ADDR_MASK;
> > > > > +       val |= DMA_ASK_VALID;
> > > > > +       val |= dchan->chan_id;
> > > > > +       chan_writel(chan, DMA_CTRL, val);
> > > > > +       ret = readl_poll_timeout_atomic(chan->reg_base + DMA_CTRL, val,
> > > > > +                                       !(val & DMA_ASK_VALID), 0, 3000);
> > > > > +       if (ret)
> > > > > +               dev_err(chan2dev(dchan), "failed to query DMA\n");
> > > > > +
> > > > > +       return ret;
> > > > > +}
> > > > > +
> > > > > +static inline int ls1x_dma_start(struct ls1x_dma_chan *chan,
> > > > > +                                dma_addr_t *hwdesc_phys)
> > > > > +{
> > > > > +       struct dma_chan *dchan = &chan->vchan.chan;
> > > > > +       int val, ret;
> > > > > +
> > > > > +       dev_dbg(chan2dev(dchan), "cookie=%d, starting hwdesc=%x\n",
> > > > > +               dchan->cookie, *hwdesc_phys);
> > > > > +
> > > > > +       val = *hwdesc_phys & DMA_ADDR_MASK;
> > > > > +       val |= DMA_START;
> > > > > +       val |= dchan->chan_id;
> > > > > +       chan_writel(chan, DMA_CTRL, val);
> > > > > +       ret = readl_poll_timeout(chan->reg_base + DMA_CTRL, val,
> > > > > +                                !(val & DMA_START), 0, 3000);
> > > > > +       if (ret)
> > > > > +               dev_err(chan2dev(dchan), "failed to start DMA\n");
> > > > > +
> > > > > +       return ret;
> > > > > +}
> > > > > +
> > > > > +static inline void ls1x_dma_stop(struct ls1x_dma_chan *chan)
> > > > > +{
> > > > > +       chan_writel(chan, DMA_CTRL, chan_readl(chan, DMA_CTRL) | DMA_STOP);
> > > > > +}
> > > > > +
> > > > > +static void ls1x_dma_free_chan_resources(struct dma_chan *dchan)
> > > > > +{
> > > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > +
> > > > > +       dma_free_coherent(chan2dev(dchan), sizeof(struct ls1x_dma_hwdesc),
> > > > > +                         chan->curr_hwdesc, chan->curr_hwdesc_phys);
> > > > > +       vchan_free_chan_resources(&chan->vchan);
> > > > > +       dma_pool_destroy(chan->desc_pool);
> > > > > +       chan->desc_pool = NULL;
> > > > > +}
> > > > > +
> > > > > +static int ls1x_dma_alloc_chan_resources(struct dma_chan *dchan)
> > > > > +{
> > > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > +
> > > > > +       chan->desc_pool = dma_pool_create(dma_chan_name(dchan),
> > > > > +                                         chan2dev(dchan),
> > > > > +                                         sizeof(struct ls1x_dma_hwdesc),
> > > > > +                                         __alignof__(struct ls1x_dma_hwdesc),
> > > > > +                                         0);
> > > > > +       if (!chan->desc_pool)
> > > > > +               return -ENOMEM;
> > > > > +
> > > > > +       /* allocate memory for querying current HW descriptor */
> > > > > +       dma_set_coherent_mask(chan2dev(dchan), DMA_BIT_MASK(32));
> > > > > +       chan->curr_hwdesc = dma_alloc_coherent(chan2dev(dchan),
> > > > > +                                              sizeof(struct ls1x_dma_hwdesc),
> > > > > +                                              &chan->curr_hwdesc_phys,
> > > > > +                                              GFP_KERNEL);
> > > > > +       if (!chan->curr_hwdesc)
> > > > > +               return -ENOMEM;
> > > > > +
> > > > > +       return 0;
> > > > > +}
> > > > > +
> > > > > +static void ls1x_dma_free_desc(struct virt_dma_desc *vdesc)
> > > > > +{
> > > > > +       struct ls1x_dma_desc *desc = to_ls1x_dma_desc(vdesc);
> > > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(vdesc->tx.chan);
> > > > > +
> > > > > +       dma_pool_free(chan->desc_pool, desc->hwdesc, desc->hwdesc_phys);
> > > > > +       chan->desc = NULL;
> > > > > +       kfree(desc);
> > > > > +}
> > > > > +
> > > > > +static struct ls1x_dma_desc *
> > > > > +ls1x_dma_alloc_desc(struct dma_chan *dchan, int sg_len,
> > > > > +                   enum dma_transfer_direction direction,
> > > > > +                   enum dma_transaction_type type)
> > > > > +{
> > > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > +       struct ls1x_dma_desc *desc;
> > > > > +
> > > > > +       if (sg_len > LS1X_DMA_MAX_DESC) {
> > > > > +               dev_err(chan2dev(dchan), "sg_len %u exceeds limit %lu",
> > > > > +                       sg_len, LS1X_DMA_MAX_DESC);
> > > > > +               return NULL;
> > > > > +       }
> > > > > +
> > > > > +       desc = kzalloc(sizeof(*desc), GFP_NOWAIT);
> > > > > +       if (!desc)
> > > > > +               return NULL;
> > > > > +
> > > > > +       /* allocate HW descriptors */
> > > > > +       desc->hwdesc = dma_pool_zalloc(chan->desc_pool, GFP_NOWAIT,
> > > > > +                                      &desc->hwdesc_phys);
> > > > > +       if (!desc->hwdesc) {
> > > > > +               dev_err(chan2dev(dchan), "failed to alloc HW descriptors\n");
> > > > > +               ls1x_dma_free_desc(&desc->vdesc);
> > > > > +               return NULL;
> > > > > +       }
> > > > > +
> > > > > +       desc->dir = direction;
> > > > > +       desc->type = type;
> > > > > +       desc->nr_descs = sg_len;
> > > > > +
> > > > > +       return desc;
> > > > > +}
> > > > > +
> > > > > +static int ls1x_dma_setup_hwdescs(struct dma_chan *dchan,
> > > > > +                                 struct ls1x_dma_desc *desc,
> > > > > +                                 struct scatterlist *sgl, unsigned int sg_len)
> > > > > +{
> > > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > +       dma_addr_t next_hwdesc_phys = desc->hwdesc_phys;
> > > > > +
> > > > > +       struct scatterlist *sg;
> > > > > +       unsigned int dev_addr, cmd, i;
> > > > > +
> > > > > +       switch (desc->dir) {
> > > > > +       case DMA_MEM_TO_DEV:
> > > > > +               dev_addr = chan->dst_addr;
> > > > > +               desc->bus_width = chan->dst_addr_width;
> > > > > +               cmd = DMA_RAM2DEV | DMA_INT;
> > > > > +               break;
> > > > > +       case DMA_DEV_TO_MEM:
> > > > > +               dev_addr = chan->src_addr;
> > > > > +               desc->bus_width = chan->src_addr_width;
> > > > > +               cmd = DMA_INT;
> > > > > +               break;
> > > > > +       default:
> > > > > +               dev_err(chan2dev(dchan), "unsupported DMA direction: %s\n",
> > > > > +                       dmaengine_get_direction_text(desc->dir));
> > > > > +               return -EINVAL;
> > > > > +       }
> > > > > +
> > > > > +       /* setup HW descriptors */
> > > > > +       for_each_sg(sgl, sg, sg_len, i) {
> > > > > +               dma_addr_t buf_addr = sg_dma_address(sg);
> > > > > +               size_t buf_len = sg_dma_len(sg);
> > > > > +               struct ls1x_dma_hwdesc *hwdesc = &desc->hwdesc[i];
> > > > > +
> > > > > +               if (!is_dma_copy_aligned(dchan->device, buf_addr, 0, buf_len)) {
> > > > > +                       dev_err(chan2dev(dchan), "buffer is not aligned!\n");
> > > > > +                       return -EINVAL;
> > > > > +               }
> > > > > +
> > > > > +               hwdesc->saddr = buf_addr;
> > > > > +               hwdesc->daddr = dev_addr;
> > > > > +               hwdesc->length = buf_len / desc->bus_width;
> > > > > +               hwdesc->stride = 0;
> > > > > +               hwdesc->cycles = 1;
> > > > > +               hwdesc->cmd = cmd;
> > > > > +
> > > > > +               if (i) {
> > > > > +                       next_hwdesc_phys += sizeof(*hwdesc);
> > > > > +                       desc->hwdesc[i - 1].next = next_hwdesc_phys
> > > > > +                           | DMA_NEXT_VALID;
> > > > > +               }
> > > > > +       }
> > > > > +
> > > > > +       if (desc->type == DMA_CYCLIC)
> > > > > +               desc->hwdesc[i - 1].next = desc->hwdesc_phys | DMA_NEXT_VALID;
> > > > > +
> > > > > +       for_each_sg(sgl, sg, sg_len, i) {
> > > > > +               struct ls1x_dma_hwdesc *hwdesc = &desc->hwdesc[i];
> > > > > +
> > > > > +               print_hex_dump_debug("HW DESC: ", DUMP_PREFIX_OFFSET, 16, 4,
> > > > > +                                    hwdesc, sizeof(*hwdesc), false);
> > > > > +       }
> > > > > +
> > > > > +       return 0;
> > > > > +}
> > > > > +
> > > > > +static struct dma_async_tx_descriptor *
> > > > > +ls1x_dma_prep_slave_sg(struct dma_chan *dchan,
> > > > > +                      struct scatterlist *sgl, unsigned int sg_len,
> > > > > +                      enum dma_transfer_direction direction,
> > > > > +                      unsigned long flags, void *context)
> > > > > +{
> > > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > +       struct ls1x_dma_desc *desc;
> > > > > +
> > > > > +       dev_dbg(chan2dev(dchan), "sg_len=%u flags=0x%lx dir=%s\n",
> > > > > +               sg_len, flags, dmaengine_get_direction_text(direction));
> > > > > +
> > > > > +       desc = ls1x_dma_alloc_desc(dchan, sg_len, direction, DMA_SLAVE);
> > > > > +       if (!desc)
> > > > > +               return NULL;
> > > > > +
> > > > > +       if (ls1x_dma_setup_hwdescs(dchan, desc, sgl, sg_len)) {
> > > > > +               ls1x_dma_free_desc(&desc->vdesc);
> > > > > +               return NULL;
> > > > > +       }
> > > > > +
> > > > > +       return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
> > > > > +}
> > > > > +
> > > > > +static struct dma_async_tx_descriptor *
> > > > > +ls1x_dma_prep_dma_cyclic(struct dma_chan *dchan,
> > > > > +                        dma_addr_t buf_addr, size_t buf_len, size_t period_len,
> > > > > +                        enum dma_transfer_direction direction,
> > > > > +                        unsigned long flags)
> > > > > +{
> > > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > +       struct ls1x_dma_desc *desc;
> > > > > +       struct scatterlist *sgl;
> > > > > +       unsigned int sg_len;
> > > > > +       unsigned int i;
> > > > > +
> > > > > +       dev_dbg(chan2dev(dchan),
> > > > > +               "buf_len=%d period_len=%zu flags=0x%lx dir=%s\n", buf_len,
> > > > > +               period_len, flags, dmaengine_get_direction_text(direction));
> > > > > +
> > > > > +       sg_len = buf_len / period_len;
> > > > > +       desc = ls1x_dma_alloc_desc(dchan, sg_len, direction, DMA_CYCLIC);
> > > > > +       if (!desc)
> > > > > +               return NULL;
> > > > > +
> > > > > +       /* allocate the scatterlist */
> > > > > +       sgl = kmalloc_array(sg_len, sizeof(*sgl), GFP_NOWAIT);
> > > > > +       if (!sgl)
> > > > > +               return NULL;
> > > > > +
> > > > > +       sg_init_table(sgl, sg_len);
> > > > > +       for (i = 0; i < sg_len; ++i) {
> > > > > +               sg_set_page(&sgl[i], pfn_to_page(PFN_DOWN(buf_addr)),
> > > > > +                           period_len, offset_in_page(buf_addr));
> > > > > +               sg_dma_address(&sgl[i]) = buf_addr;
> > > > > +               sg_dma_len(&sgl[i]) = period_len;
> > > > > +               buf_addr += period_len;
> > > > > +       }
> > > > > +
> > > > > +       if (ls1x_dma_setup_hwdescs(dchan, desc, sgl, sg_len)) {
> > > > > +               ls1x_dma_free_desc(&desc->vdesc);
> > > > > +               return NULL;
> > > > > +       }
> > > > > +
> > > > > +       kfree(sgl);
> > > > > +
> > > > > +       return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
> > > > > +}
> > > > > +
> > > > > +static int ls1x_dma_slave_config(struct dma_chan *dchan,
> > > > > +                                struct dma_slave_config *config)
> > > > > +{
> > > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > +
> > > > > +       chan->src_addr = config->src_addr;
> > > > > +       chan->src_addr_width = config->src_addr_width;
> > > > > +       chan->dst_addr = config->dst_addr;
> > > > > +       chan->dst_addr_width = config->dst_addr_width;
> > > > > +
> > > > > +       return 0;
> > > > > +}
> > > > > +
> > > > > +static int ls1x_dma_pause(struct dma_chan *dchan)
> > > > > +{
> > > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > +       unsigned long flags;
> > > > > +       int ret;
> > > > > +
> > > > > +       spin_lock_irqsave(&chan->vchan.lock, flags);
> > > > > +       ret = ls1x_dma_query(chan, &chan->curr_hwdesc_phys);
> > > > > +       if (!ret)
> > > > > +               ls1x_dma_stop(chan);
> > > > > +       spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > > > > +
> > > > > +       return ret;
> > > > > +}
> > > > > +
> > > > > +static int ls1x_dma_resume(struct dma_chan *dchan)
> > > > > +{
> > > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > +       unsigned long flags;
> > > > > +       int ret;
> > > > > +
> > > > > +       spin_lock_irqsave(&chan->vchan.lock, flags);
> > > > > +       ret = ls1x_dma_start(chan, &chan->curr_hwdesc_phys);
> > > > > +       spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > > > > +
> > > > > +       return ret;
> > > > > +}
> > > > > +
> > > > > +static int ls1x_dma_terminate_all(struct dma_chan *dchan)
> > > > > +{
> > > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > +       unsigned long flags;
> > > > > +       LIST_HEAD(head);
> > > > > +
> > > > > +       spin_lock_irqsave(&chan->vchan.lock, flags);
> > > > > +       ls1x_dma_stop(chan);
> > > > > +       vchan_get_all_descriptors(&chan->vchan, &head);
> > > > > +       spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > > > > +
> > > > > +       vchan_dma_desc_free_list(&chan->vchan, &head);
> > > > > +
> > > > > +       return 0;
> > > > > +}
> > > > > +
> > > > > +static enum dma_status ls1x_dma_tx_status(struct dma_chan *dchan,
> > > > > +                                         dma_cookie_t cookie,
> > > > > +                                         struct dma_tx_state *state)
> > > > > +{
> > > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > +       struct virt_dma_desc *vdesc;
> > > > > +       enum dma_status status;
> > > > > +       size_t bytes = 0;
> > > > > +       unsigned long flags;
> > > > > +
> > > > > +       status = dma_cookie_status(dchan, cookie, state);
> > > > > +       if (status == DMA_COMPLETE)
> > > > > +               return status;
> > > > > +
> > > > > +       spin_lock_irqsave(&chan->vchan.lock, flags);
> > > > > +       vdesc = vchan_find_desc(&chan->vchan, cookie);
> > > > > +       if (chan->desc && cookie == chan->desc->vdesc.tx.cookie) {
> > > > > +               struct ls1x_dma_desc *desc = chan->desc;
> > > > > +               int i;
> > > > > +
> > > > > +               if (ls1x_dma_query(chan, &chan->curr_hwdesc_phys))
> > > > > +                       return status;
> > > > > +
> > > > > +               /* locate the current HW descriptor */
> > > > > +               for (i = 0; i < desc->nr_descs; i++)
> > > > > +                       if (desc->hwdesc[i].next == chan->curr_hwdesc->next)
> > > > > +                               break;
> > > > > +
> > > > > +               /* count the residues */
> > > > > +               for (; i < desc->nr_descs; i++)
> > > > > +                       bytes += desc->hwdesc[i].length * desc->bus_width;
> > > > > +
> > > > > +               dma_set_residue(state, bytes);
> > > > > +       }
> > > > > +       spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > > > > +
> > > > > +       return status;
> > > > > +}
> > > > > +
> > > > > +static void ls1x_dma_issue_pending(struct dma_chan *dchan)
> > > > > +{
> > > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > +       struct virt_dma_desc *vdesc;
> > > > > +       unsigned long flags;
> > > > > +
> > > > > +       spin_lock_irqsave(&chan->vchan.lock, flags);
> > > > > +       if (vchan_issue_pending(&chan->vchan) && !chan->desc) {
> > > > > +               vdesc = vchan_next_desc(&chan->vchan);
> > > > > +               if (!vdesc) {
> > > > > +                       chan->desc = NULL;
> > > > > +                       return;
> > > > > +               }
> > > > > +               chan->desc = to_ls1x_dma_desc(vdesc);
> > > > > +               ls1x_dma_start(chan, &chan->desc->hwdesc_phys);
> > > > > +       }
> > > > > +       spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > > > > +}
> > > > > +
> > > > > +static irqreturn_t ls1x_dma_irq_handler(int irq, void *data)
> > > > > +{
> > > > > +       struct ls1x_dma_chan *chan = data;
> > > > > +       struct ls1x_dma_desc *desc = chan->desc;
> > > > > +       struct dma_chan *dchan = &chan->vchan.chan;
> > > > > +
> > > > > +       if (!desc) {
> > > > > +               dev_warn(chan2dev(dchan),
> > > > > +                        "IRQ %d with no active descriptor on channel %d\n",
> > > > > +                        irq, dchan->chan_id);
> > > > > +               return IRQ_NONE;
> > > > > +       }
> > > > > +
> > > > > +       dev_dbg(chan2dev(dchan), "DMA IRQ %d on channel %d\n", irq,
> > > > > +               dchan->chan_id);
> > > > > +
> > > > > +       spin_lock(&chan->vchan.lock);
> > > > > +
> > > > > +       if (desc->type == DMA_CYCLIC) {
> > > > > +               vchan_cyclic_callback(&desc->vdesc);
> > > > > +       } else {
> > > > > +               list_del(&desc->vdesc.node);
> > > > > +               vchan_cookie_complete(&desc->vdesc);
> > > > > +               chan->desc = NULL;
> > > > > +       }
> > > > > +
> > > > > +       spin_unlock(&chan->vchan.lock);
> > > > > +       return IRQ_HANDLED;
> > > > > +}
> > > > > +
> > > > > +static int ls1x_dma_chan_probe(struct platform_device *pdev,
> > > > > +                              struct ls1x_dma *dma, int chan_id)
> > > > > +{
> > > > > +       struct device *dev = &pdev->dev;
> > > > > +       struct ls1x_dma_chan *chan = &dma->chan[chan_id];
> > > > > +       char pdev_irqname[4];
> > > > > +       char *irqname;
> > > > > +       int ret;
> > > > > +
> > > > > +       sprintf(pdev_irqname, "ch%u", chan_id);
> > > > > +       chan->irq = platform_get_irq_byname(pdev, pdev_irqname);
> > > > > +       if (chan->irq < 0)
> > > > > +               return -ENODEV;
> > > > > +
> > > > > +       irqname = devm_kasprintf(dev, GFP_KERNEL, "%s:%s",
> > > > > +                                dev_name(dev), pdev_irqname);
> > > > > +       if (!irqname)
> > > > > +               return -ENOMEM;
> > > > > +
> > > > > +       ret = devm_request_irq(dev, chan->irq, ls1x_dma_irq_handler,
> > > > > +                              IRQF_SHARED, irqname, chan);
> > > > > +       if (ret)
> > > > > +               return dev_err_probe(dev, ret,
> > > > > +                                    "failed to request IRQ %u!\n", chan->irq);
> > > > > +
> > > > > +       chan->reg_base = dma->reg_base;
> > > > > +       chan->vchan.desc_free = ls1x_dma_free_desc;
> > > > > +       vchan_init(&chan->vchan, &dma->ddev);
> > > > > +       dev_info(dev, "%s (irq %d) initialized\n", pdev_irqname, chan->irq);
> > > > > +
> > > > > +       return 0;
> > > > > +}
> > > > > +
> > > > > +static void ls1x_dma_chan_remove(struct ls1x_dma *dma, int chan_id)
> > > > > +{
> > > > > +       struct device *dev = dma->ddev.dev;
> > > > > +       struct ls1x_dma_chan *chan = &dma->chan[chan_id];
> > > > > +
> > > > > +       devm_free_irq(dev, chan->irq, chan);
> > > > > +       list_del(&chan->vchan.chan.device_node);
> > > > > +       tasklet_kill(&chan->vchan.task);
> > > > > +}
> > > > > +
> > > > > +static int ls1x_dma_probe(struct platform_device *pdev)
> > > > > +{
> > > > > +       struct device *dev = &pdev->dev;
> > > > > +       struct dma_device *ddev;
> > > > > +       struct ls1x_dma *dma;
> > > > > +       int nr_chans, ret, i;
> > > > > +
> > > > > +       nr_chans = platform_irq_count(pdev);
> > > > > +       if (nr_chans <= 0)
> > > > > +               return nr_chans;
> > > > > +       if (nr_chans > LS1X_DMA_MAX_CHANNELS)
> > > > > +               return dev_err_probe(dev, -EINVAL,
> > > > > +                                    "nr_chans=%d exceeds the maximum\n",
> > > > > +                                    nr_chans);
> > > > > +
> > > > > +       dma = devm_kzalloc(dev, struct_size(dma, chan, nr_chans), GFP_KERNEL);
> > > > > +       if (!dma)
> > > > > +               return -ENOMEM;
> > > > > +
> > > > > +       /* initialize DMA device */
> > > > > +       dma->reg_base = devm_platform_ioremap_resource(pdev, 0);
> > > > > +       if (IS_ERR(dma->reg_base))
> > > > > +               return PTR_ERR(dma->reg_base);
> > > > > +
> > > > > +       ddev = &dma->ddev;
> > > > > +       ddev->dev = dev;
> > > > > +       ddev->copy_align = DMAENGINE_ALIGN_4_BYTES;
> > > > > +       ddev->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> > > > > +           BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
> > > > > +       ddev->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> > > > > +           BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
> > > > > +       ddev->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
> > > > > +       ddev->max_sg_burst = LS1X_DMA_MAX_DESC;
> > > > > +       ddev->residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
> > > > > +       ddev->device_alloc_chan_resources = ls1x_dma_alloc_chan_resources;
> > > > > +       ddev->device_free_chan_resources = ls1x_dma_free_chan_resources;
> > > > > +       ddev->device_prep_slave_sg = ls1x_dma_prep_slave_sg;
> > > > > +       ddev->device_prep_dma_cyclic = ls1x_dma_prep_dma_cyclic;
> > > > > +       ddev->device_config = ls1x_dma_slave_config;
> > > > > +       ddev->device_pause = ls1x_dma_pause;
> > > > > +       ddev->device_resume = ls1x_dma_resume;
> > > > > +       ddev->device_terminate_all = ls1x_dma_terminate_all;
> > > > > +       ddev->device_tx_status = ls1x_dma_tx_status;
> > > > > +       ddev->device_issue_pending = ls1x_dma_issue_pending;
> > > > > +
> > > > > +       dma_cap_set(DMA_SLAVE, ddev->cap_mask);
> > > > > +       INIT_LIST_HEAD(&ddev->channels);
> > > > > +
> > > > > +       /* initialize DMA channels */
> > > > > +       for (i = 0; i < nr_chans; i++) {
> > > > > +               ret = ls1x_dma_chan_probe(pdev, dma, i);
> > > > > +               if (ret)
> > > > > +                       return ret;
> > > > > +       }
> > > > > +       dma->nr_chans = nr_chans;
> > > > > +
> > > > > +       ret = dmaenginem_async_device_register(ddev);
> > > > > +       if (ret) {
> > > > > +               dev_err(dev, "failed to register DMA device! %d\n", ret);
> > > > > +               return ret;
> > > > > +       }
> > > > > +
> > > > > +       ret =
> > > > > +           of_dma_controller_register(dev->of_node, of_dma_xlate_by_chan_id,
> > > > > +                                      ddev);
> > > > > +       if (ret) {
> > > > > +               dev_err(dev, "failed to register DMA controller! %d\n", ret);
> > > > > +               return ret;
> > > > > +       }
> > > > > +
> > > > > +       platform_set_drvdata(pdev, dma);
> > > > > +       dev_info(dev, "Loongson1 DMA driver registered\n");
> > > > > +
> > > > > +       return 0;
> > > > > +}
> > > > > +
> > > > > +static void ls1x_dma_remove(struct platform_device *pdev)
> > > > > +{
> > > > > +       struct ls1x_dma *dma = platform_get_drvdata(pdev);
> > > > > +       int i;
> > > > > +
> > > > > +       of_dma_controller_free(pdev->dev.of_node);
> > > > > +
> > > > > +       for (i = 0; i < dma->nr_chans; i++)
> > > > > +               ls1x_dma_chan_remove(dma, i);
> > > > > +}
> > > > > +
> > > > > +static const struct of_device_id ls1x_dma_match[] = {
> > > > > +       { .compatible = "loongson,ls1b-apbdma" },
> > > > > +       { .compatible = "loongson,ls1c-apbdma" },
> > > > > +       { /* sentinel */ }
> > > > > +};
> > > > > +MODULE_DEVICE_TABLE(of, ls1x_dma_match);
> > > > > +
> > > > > +static struct platform_driver ls1x_dma_driver = {
> > > > > +       .probe = ls1x_dma_probe,
> > > > > +       .remove_new = ls1x_dma_remove,
> > > > > +       .driver = {
> > > > > +               .name = KBUILD_MODNAME,
> > > > > +               .of_match_table = ls1x_dma_match,
> > > > > +       },
> > > > > +};
> > > > > +
> > > > > +module_platform_driver(ls1x_dma_driver);
> > > > > +
> > > > > +MODULE_AUTHOR("Keguang Zhang <keguang.zhang@gmail.com>");
> > > > > +MODULE_DESCRIPTION("Loongson-1 APB DMA Controller driver");
> > > > > +MODULE_LICENSE("GPL");
> > > > >
> > > > > --
> > > > > 2.40.1
> > > > >
> > > > >
> > >
> > >
> > >
> > > --
> > > Best regards,
> > >
> > > Keguang Zhang
> > >
>
>
>
> --
> Best regards,
>
> Keguang Zhang

^ permalink raw reply

* [PATCH v6 4/4] clk: imx: add i.MX95 BLK CTL clk driver
From: Peng Fan (OSS) @ 2024-04-01 13:28 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Shawn Guo, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Abel Vesa
  Cc: linux-clk, devicetree, imx, linux-arm-kernel, linux-kernel,
	Peng Fan
In-Reply-To: <20240401-imx95-blk-ctl-v6-0-84d4eca1e759@nxp.com>

From: Peng Fan <peng.fan@nxp.com>

i.MX95 has BLK CTL modules in various MIXes, the BLK CTL modules
support clock features such as mux/gate/div. This patch
is to add the clock feature of BLK CTL modules

Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 drivers/clk/imx/Kconfig             |   7 +
 drivers/clk/imx/Makefile            |   1 +
 drivers/clk/imx/clk-imx95-blk-ctl.c | 438 ++++++++++++++++++++++++++++++++++++
 3 files changed, 446 insertions(+)

diff --git a/drivers/clk/imx/Kconfig b/drivers/clk/imx/Kconfig
index db3bca5f4ec9..6da0fba68225 100644
--- a/drivers/clk/imx/Kconfig
+++ b/drivers/clk/imx/Kconfig
@@ -114,6 +114,13 @@ config CLK_IMX93
 	help
 	    Build the driver for i.MX93 CCM Clock Driver
 
+config CLK_IMX95_BLK_CTL
+	tristate "IMX95 Clock Driver for BLK CTL"
+	depends on ARCH_MXC || COMPILE_TEST
+	select MXC_CLK
+	help
+	    Build the clock driver for i.MX95 BLK CTL
+
 config CLK_IMXRT1050
 	tristate "IMXRT1050 CCM Clock Driver"
 	depends on SOC_IMXRT || COMPILE_TEST
diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
index d4b8e10b1970..03f2b2a1ab63 100644
--- a/drivers/clk/imx/Makefile
+++ b/drivers/clk/imx/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_CLK_IMX8MP) += clk-imx8mp.o clk-imx8mp-audiomix.o
 obj-$(CONFIG_CLK_IMX8MQ) += clk-imx8mq.o
 
 obj-$(CONFIG_CLK_IMX93) += clk-imx93.o
+obj-$(CONFIG_CLK_IMX95_BLK_CTL) += clk-imx95-blk-ctl.o
 
 obj-$(CONFIG_MXC_CLK_SCU) += clk-imx-scu.o clk-imx-lpcg-scu.o clk-imx-acm.o
 clk-imx-scu-$(CONFIG_CLK_IMX8QXP) += clk-scu.o clk-imx8qxp.o \
diff --git a/drivers/clk/imx/clk-imx95-blk-ctl.c b/drivers/clk/imx/clk-imx95-blk-ctl.c
new file mode 100644
index 000000000000..74f595f9e5e3
--- /dev/null
+++ b/drivers/clk/imx/clk-imx95-blk-ctl.c
@@ -0,0 +1,438 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2024 NXP
+ */
+
+#include <dt-bindings/clock/nxp,imx95-clock.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/pm_runtime.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+enum {
+	CLK_GATE,
+	CLK_DIVIDER,
+	CLK_MUX,
+};
+
+struct imx95_blk_ctl {
+	struct device *dev;
+	spinlock_t lock;
+	struct clk *clk_apb;
+
+	void __iomem *base;
+	/* clock gate register */
+	u32 clk_reg_restore;
+};
+
+struct imx95_blk_ctl_clk_dev_data {
+	const char *name;
+	const char * const *parent_names;
+	u32 num_parents;
+	u32 reg;
+	u32 bit_idx;
+	u32 bit_width;
+	u32 clk_type;
+	u32 flags;
+	u32 flags2;
+	u32 type;
+};
+
+struct imx95_blk_ctl_dev_data {
+	const struct imx95_blk_ctl_clk_dev_data *clk_dev_data;
+	u32 num_clks;
+	bool rpm_enabled;
+	u32 clk_reg_offset;
+};
+
+static const struct imx95_blk_ctl_clk_dev_data vpublk_clk_dev_data[] = {
+	[IMX95_CLK_VPUBLK_WAVE] = {
+		.name = "vpublk_wave_vpu",
+		.parent_names = (const char *[]){ "vpu", },
+		.num_parents = 1,
+		.reg = 8,
+		.bit_idx = 0,
+		.type = CLK_GATE,
+		.flags = CLK_SET_RATE_PARENT,
+		.flags2 = CLK_GATE_SET_TO_DISABLE,
+	},
+	[IMX95_CLK_VPUBLK_JPEG_ENC] = {
+		.name = "vpublk_jpeg_enc",
+		.parent_names = (const char *[]){ "vpujpeg", },
+		.num_parents = 1,
+		.reg = 8,
+		.bit_idx = 1,
+		.type = CLK_GATE,
+		.flags = CLK_SET_RATE_PARENT,
+		.flags2 = CLK_GATE_SET_TO_DISABLE,
+	},
+	[IMX95_CLK_VPUBLK_JPEG_DEC] = {
+		.name = "vpublk_jpeg_dec",
+		.parent_names = (const char *[]){ "vpujpeg", },
+		.num_parents = 1,
+		.reg = 8,
+		.bit_idx = 2,
+		.type = CLK_GATE,
+		.flags = CLK_SET_RATE_PARENT,
+		.flags2 = CLK_GATE_SET_TO_DISABLE,
+	}
+};
+
+static const struct imx95_blk_ctl_dev_data vpublk_dev_data = {
+	.num_clks = ARRAY_SIZE(vpublk_clk_dev_data),
+	.clk_dev_data = vpublk_clk_dev_data,
+	.rpm_enabled = true,
+	.clk_reg_offset = 8,
+};
+
+static const struct imx95_blk_ctl_clk_dev_data camblk_clk_dev_data[] = {
+	[IMX95_CLK_CAMBLK_CSI2_FOR0] = {
+		.name = "camblk_csi2_for0",
+		.parent_names = (const char *[]){ "camisi", },
+		.num_parents = 1,
+		.reg = 0,
+		.bit_idx = 0,
+		.type = CLK_GATE,
+		.flags = CLK_SET_RATE_PARENT,
+		.flags2 = CLK_GATE_SET_TO_DISABLE,
+	},
+	[IMX95_CLK_CAMBLK_CSI2_FOR1] = {
+		.name = "camblk_csi2_for1",
+		.parent_names = (const char *[]){ "camisi", },
+		.num_parents = 1,
+		.reg = 0,
+		.bit_idx = 1,
+		.type = CLK_GATE,
+		.flags = CLK_SET_RATE_PARENT,
+		.flags2 = CLK_GATE_SET_TO_DISABLE,
+	},
+	[IMX95_CLK_CAMBLK_ISP_AXI] = {
+		.name = "camblk_isp_axi",
+		.parent_names = (const char *[]){ "camaxi", },
+		.num_parents = 1,
+		.reg = 0,
+		.bit_idx = 4,
+		.type = CLK_GATE,
+		.flags = CLK_SET_RATE_PARENT,
+		.flags2 = CLK_GATE_SET_TO_DISABLE,
+	},
+	[IMX95_CLK_CAMBLK_ISP_PIXEL] = {
+		.name = "camblk_isp_pixel",
+		.parent_names = (const char *[]){ "camisi", },
+		.num_parents = 1,
+		.reg = 0,
+		.bit_idx = 5,
+		.type = CLK_GATE,
+		.flags = CLK_SET_RATE_PARENT,
+		.flags2 = CLK_GATE_SET_TO_DISABLE,
+	},
+	[IMX95_CLK_CAMBLK_ISP] = {
+		.name = "camblk_isp",
+		.parent_names = (const char *[]){ "camisi", },
+		.num_parents = 1,
+		.reg = 0,
+		.bit_idx = 6,
+		.type = CLK_GATE,
+		.flags = CLK_SET_RATE_PARENT,
+		.flags2 = CLK_GATE_SET_TO_DISABLE,
+	}
+};
+
+static const struct imx95_blk_ctl_dev_data camblk_dev_data = {
+	.num_clks = ARRAY_SIZE(camblk_clk_dev_data),
+	.clk_dev_data = camblk_clk_dev_data,
+	.clk_reg_offset = 0,
+};
+
+static const struct imx95_blk_ctl_clk_dev_data lvds_clk_dev_data[] = {
+	[IMX95_CLK_DISPMIX_LVDS_PHY_DIV] = {
+		.name = "ldb_phy_div",
+		.parent_names = (const char *[]){ "ldbpll", },
+		.num_parents = 1,
+		.reg = 0,
+		.bit_idx = 0,
+		.bit_width = 1,
+		.type = CLK_DIVIDER,
+		.flags2 = CLK_DIVIDER_POWER_OF_TWO,
+	},
+	[IMX95_CLK_DISPMIX_LVDS_CH0_GATE] = {
+		.name = "lvds_ch0_gate",
+		.parent_names = (const char *[]){ "ldb_phy_div", },
+		.num_parents = 1,
+		.reg = 0,
+		.bit_idx = 1,
+		.bit_width = 1,
+		.type = CLK_GATE,
+		.flags = CLK_SET_RATE_PARENT,
+		.flags2 = CLK_GATE_SET_TO_DISABLE,
+	},
+	[IMX95_CLK_DISPMIX_LVDS_CH1_GATE] = {
+		.name = "lvds_ch1_gate",
+		.parent_names = (const char *[]){ "ldb_phy_div", },
+		.num_parents = 1,
+		.reg = 0,
+		.bit_idx = 2,
+		.bit_width = 1,
+		.type = CLK_GATE,
+		.flags = CLK_SET_RATE_PARENT,
+		.flags2 = CLK_GATE_SET_TO_DISABLE,
+	},
+	[IMX95_CLK_DISPMIX_PIX_DI0_GATE] = {
+		.name = "lvds_di0_gate",
+		.parent_names = (const char *[]){ "ldb_pll_div7", },
+		.num_parents = 1,
+		.reg = 0,
+		.bit_idx = 3,
+		.bit_width = 1,
+		.type = CLK_GATE,
+		.flags = CLK_SET_RATE_PARENT,
+		.flags2 = CLK_GATE_SET_TO_DISABLE,
+	},
+	[IMX95_CLK_DISPMIX_PIX_DI1_GATE] = {
+		.name = "lvds_di1_gate",
+		.parent_names = (const char *[]){ "ldb_pll_div7", },
+		.num_parents = 1,
+		.reg = 0,
+		.bit_idx = 4,
+		.bit_width = 1,
+		.type = CLK_GATE,
+		.flags = CLK_SET_RATE_PARENT,
+		.flags2 = CLK_GATE_SET_TO_DISABLE,
+	},
+};
+
+static const struct imx95_blk_ctl_dev_data lvds_csr_dev_data = {
+	.num_clks = ARRAY_SIZE(lvds_clk_dev_data),
+	.clk_dev_data = lvds_clk_dev_data,
+	.clk_reg_offset = 0,
+};
+
+static const struct imx95_blk_ctl_clk_dev_data dispmix_csr_clk_dev_data[] = {
+	[IMX95_CLK_DISPMIX_ENG0_SEL] = {
+		.name = "disp_engine0_sel",
+		.parent_names = (const char *[]){"videopll1", "dsi_pll", "ldb_pll_div7", },
+		.num_parents = 4,
+		.reg = 0,
+		.bit_idx = 0,
+		.bit_width = 2,
+		.type = CLK_MUX,
+		.flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
+	},
+	[IMX95_CLK_DISPMIX_ENG1_SEL] = {
+		.name = "disp_engine1_sel",
+		.parent_names = (const char *[]){"videopll1", "dsi_pll", "ldb_pll_div7", },
+		.num_parents = 4,
+		.reg = 0,
+		.bit_idx = 2,
+		.bit_width = 2,
+		.type = CLK_MUX,
+		.flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
+	}
+};
+
+static const struct imx95_blk_ctl_dev_data dispmix_csr_dev_data = {
+	.num_clks = ARRAY_SIZE(dispmix_csr_clk_dev_data),
+	.clk_dev_data = dispmix_csr_clk_dev_data,
+	.clk_reg_offset = 0,
+};
+
+static int imx95_bc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct imx95_blk_ctl_dev_data *bc_data;
+	struct imx95_blk_ctl *bc;
+	struct clk_hw_onecell_data *clk_hw_data;
+	struct clk_hw **hws;
+	void __iomem *base;
+	int i, ret;
+
+	bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL);
+	if (!bc)
+		return -ENOMEM;
+	bc->dev = dev;
+	dev_set_drvdata(&pdev->dev, bc);
+
+	spin_lock_init(&bc->lock);
+
+	base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	bc->base = base;
+	bc->clk_apb = devm_clk_get(dev, NULL);
+	if (IS_ERR(bc->clk_apb))
+		return dev_err_probe(dev, PTR_ERR(bc->clk_apb), "failed to get APB clock\n");
+
+	ret = clk_prepare_enable(bc->clk_apb);
+	if (ret) {
+		dev_err(dev, "failed to enable apb clock: %d\n", ret);
+		return ret;
+	}
+
+	bc_data = of_device_get_match_data(dev);
+	if (!bc_data)
+		return devm_of_platform_populate(dev);
+
+	clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, bc_data->num_clks),
+				   GFP_KERNEL);
+	if (!clk_hw_data)
+		return -ENOMEM;
+
+	if (bc_data->rpm_enabled)
+		pm_runtime_enable(&pdev->dev);
+
+	clk_hw_data->num = bc_data->num_clks;
+	hws = clk_hw_data->hws;
+
+	for (i = 0; i < bc_data->num_clks; i++) {
+		const struct imx95_blk_ctl_clk_dev_data *data = &bc_data->clk_dev_data[i];
+		void __iomem *reg = base + data->reg;
+
+		if (data->type == CLK_MUX) {
+			hws[i] = clk_hw_register_mux(dev, data->name, data->parent_names,
+						     data->num_parents, data->flags, reg,
+						     data->bit_idx, data->bit_width,
+						     data->flags2, &bc->lock);
+		} else if (data->type == CLK_DIVIDER) {
+			hws[i] = clk_hw_register_divider(dev, data->name, data->parent_names[0],
+							 data->flags, reg, data->bit_idx,
+							 data->bit_width, data->flags2, &bc->lock);
+		} else {
+			hws[i] = clk_hw_register_gate(dev, data->name, data->parent_names[0],
+						      data->flags, reg, data->bit_idx,
+						      data->flags2, &bc->lock);
+		}
+		if (IS_ERR(hws[i])) {
+			ret = PTR_ERR(hws[i]);
+			dev_err(dev, "failed to register: %s:%d\n", data->name, ret);
+			goto cleanup;
+		}
+	}
+
+	ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, clk_hw_data);
+	if (ret)
+		goto cleanup;
+
+	ret = devm_of_platform_populate(dev);
+	if (ret) {
+		of_clk_del_provider(dev->of_node);
+		goto cleanup;
+	}
+
+	if (pm_runtime_enabled(bc->dev))
+		clk_disable_unprepare(bc->clk_apb);
+
+	return 0;
+
+cleanup:
+	for (i = 0; i < bc_data->num_clks; i++) {
+		if (IS_ERR_OR_NULL(hws[i]))
+			continue;
+		clk_hw_unregister(hws[i]);
+	}
+
+	if (bc_data->rpm_enabled)
+		pm_runtime_disable(&pdev->dev);
+
+	return ret;
+}
+
+#ifdef CONFIG_PM
+static int imx95_bc_runtime_suspend(struct device *dev)
+{
+	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(bc->clk_apb);
+	return 0;
+}
+
+static int imx95_bc_runtime_resume(struct device *dev)
+{
+	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
+
+	return clk_prepare_enable(bc->clk_apb);
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int imx95_bc_suspend(struct device *dev)
+{
+	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
+	const struct imx95_blk_ctl_dev_data *bc_data;
+	int ret;
+
+	bc_data = of_device_get_match_data(dev);
+	if (!bc_data)
+		return 0;
+
+	if (bc_data->rpm_enabled) {
+		ret = pm_runtime_get_sync(bc->dev);
+		if (ret < 0) {
+			pm_runtime_put_noidle(bc->dev);
+			return ret;
+		}
+	}
+
+	bc->clk_reg_restore = readl(bc->base + bc_data->clk_reg_offset);
+
+	return 0;
+}
+
+static int imx95_bc_resume(struct device *dev)
+{
+	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);
+	const struct imx95_blk_ctl_dev_data *bc_data;
+
+	bc_data = of_device_get_match_data(dev);
+	if (!bc_data)
+		return 0;
+
+	writel(bc->clk_reg_restore, bc->base + bc_data->clk_reg_offset);
+
+	if (bc_data->rpm_enabled)
+		pm_runtime_put(bc->dev);
+
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops imx95_bc_pm_ops = {
+	SET_RUNTIME_PM_OPS(imx95_bc_runtime_suspend, imx95_bc_runtime_resume, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(imx95_bc_suspend, imx95_bc_resume)
+};
+
+static const struct of_device_id imx95_bc_of_match[] = {
+	{ .compatible = "nxp,imx95-camera-csr", .data = &camblk_dev_data },
+	{ .compatible = "nxp,imx95-display-master-csr", },
+	{ .compatible = "nxp,imx95-lvds-csr", .data = &lvds_csr_dev_data },
+	{ .compatible = "nxp,imx95-display-csr", .data = &dispmix_csr_dev_data },
+	{ .compatible = "nxp,imx95-vpu-csr", .data = &vpublk_dev_data },
+	{ /* Sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, imx95_bc_of_match);
+
+static struct platform_driver imx95_bc_driver = {
+	.probe = imx95_bc_probe,
+	.driver = {
+		.name = "imx95-blk-ctl",
+		.of_match_table = imx95_bc_of_match,
+		.pm = &imx95_bc_pm_ops,
+	},
+};
+module_platform_driver(imx95_bc_driver);
+
+MODULE_DESCRIPTION("NXP i.MX95 blk ctl driver");
+MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
+MODULE_LICENSE("GPL");

-- 
2.37.1


^ permalink raw reply related

* [PATCH v6 3/4] dt-bindings: clock: support i.MX95 Display Master CSR module
From: Peng Fan (OSS) @ 2024-04-01 13:28 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Shawn Guo, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Abel Vesa
  Cc: linux-clk, devicetree, imx, linux-arm-kernel, linux-kernel,
	Peng Fan, Krzysztof Kozlowski
In-Reply-To: <20240401-imx95-blk-ctl-v6-0-84d4eca1e759@nxp.com>

From: Peng Fan <peng.fan@nxp.com>

i.MX95 DISPLAY_MASTER_CSR includes registers to control DSI clock settings,
clock gating, and pixel link select. Add dt-schema for it.

Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 .../clock/nxp,imx95-display-master-csr.yaml        | 64 ++++++++++++++++++++++
 1 file changed, 64 insertions(+)

diff --git a/Documentation/devicetree/bindings/clock/nxp,imx95-display-master-csr.yaml b/Documentation/devicetree/bindings/clock/nxp,imx95-display-master-csr.yaml
new file mode 100644
index 000000000000..07f7412e7658
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/nxp,imx95-display-master-csr.yaml
@@ -0,0 +1,64 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/nxp,imx95-display-master-csr.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP i.MX95 Display Master Block Control
+
+maintainers:
+  - Peng Fan <peng.fan@nxp.com>
+
+properties:
+  compatible:
+    items:
+      - const: nxp,imx95-display-master-csr
+      - const: syscon
+
+  reg:
+    maxItems: 1
+
+  power-domains:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  '#clock-cells':
+    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/nxp,imx95-clock.h
+
+  mux-controller:
+    type: object
+    $ref: /schemas/mux/reg-mux.yaml
+
+required:
+  - compatible
+  - reg
+  - '#clock-cells'
+  - mux-controller
+  - power-domains
+  - clocks
+
+additionalProperties: false
+
+examples:
+  - |
+    syscon@4c410000 {
+      compatible = "nxp,imx95-display-master-csr", "syscon";
+      reg = <0x4c410000 0x10000>;
+      #clock-cells = <1>;
+      clocks = <&scmi_clk 62>;
+      power-domains = <&scmi_devpd 3>;
+
+      mux: mux-controller {
+        compatible = "mmio-mux";
+        #mux-control-cells = <1>;
+        mux-reg-masks = <0x4 0x00000001>; /* Pixel_link_sel */
+        idle-states = <0>;
+      };
+    };
+...

-- 
2.37.1


^ permalink raw reply related

* [PATCH v6 2/4] dt-bindings: clock: support i.MX95 BLK CTL module
From: Peng Fan (OSS) @ 2024-04-01 13:28 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Shawn Guo, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Abel Vesa
  Cc: linux-clk, devicetree, imx, linux-arm-kernel, linux-kernel,
	Peng Fan, Krzysztof Kozlowski
In-Reply-To: <20240401-imx95-blk-ctl-v6-0-84d4eca1e759@nxp.com>

From: Peng Fan <peng.fan@nxp.com>

i.MX95 includes BLK CTL module in several MIXes, such as VPU_CSR in
VPUMIX, CAMERA_CSR in CAMERAMIX and etc.

The BLK CTL module is used for various settings of a specific MIX, such
as clock, QoS and etc.

This patch is to add some BLK CTL modules that has clock features.

Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 .../bindings/clock/nxp,imx95-blk-ctl.yaml          | 56 ++++++++++++++++++++++
 1 file changed, 56 insertions(+)

diff --git a/Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml b/Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml
new file mode 100644
index 000000000000..2dffc02dcd8b
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/nxp,imx95-blk-ctl.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP i.MX95 Block Control
+
+maintainers:
+  - Peng Fan <peng.fan@nxp.com>
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - nxp,imx95-lvds-csr
+          - nxp,imx95-display-csr
+          - nxp,imx95-camera-csr
+          - nxp,imx95-vpu-csr
+      - const: syscon
+
+  reg:
+    maxItems: 1
+
+  power-domains:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  '#clock-cells':
+    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/nxp,imx95-clock.h
+
+required:
+  - compatible
+  - reg
+  - '#clock-cells'
+  - power-domains
+  - clocks
+
+additionalProperties: false
+
+examples:
+  - |
+    syscon@4c410000 {
+      compatible = "nxp,imx95-vpu-csr", "syscon";
+      reg = <0x4c410000 0x10000>;
+      #clock-cells = <1>;
+      clocks = <&scmi_clk 114>;
+      power-domains = <&scmi_devpd 21>;
+    };
+...

-- 
2.37.1


^ permalink raw reply related

* [PATCH v6 1/4] dt-bindings: clock: add i.MX95 clock header
From: Peng Fan (OSS) @ 2024-04-01 13:28 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Shawn Guo, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Abel Vesa
  Cc: linux-clk, devicetree, imx, linux-arm-kernel, linux-kernel,
	Peng Fan
In-Reply-To: <20240401-imx95-blk-ctl-v6-0-84d4eca1e759@nxp.com>

From: Peng Fan <peng.fan@nxp.com>

Add clock header for i.MX95 BLK CTL modules

Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
 include/dt-bindings/clock/nxp,imx95-clock.h | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/include/dt-bindings/clock/nxp,imx95-clock.h b/include/dt-bindings/clock/nxp,imx95-clock.h
new file mode 100644
index 000000000000..782662c3e740
--- /dev/null
+++ b/include/dt-bindings/clock/nxp,imx95-clock.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
+/*
+ * Copyright 2024 NXP
+ */
+
+#ifndef __DT_BINDINGS_CLOCK_IMX95_H
+#define __DT_BINDINGS_CLOCK_IMX95_H
+
+#define IMX95_CLK_VPUBLK_WAVE			0
+#define IMX95_CLK_VPUBLK_JPEG_ENC		1
+#define IMX95_CLK_VPUBLK_JPEG_DEC		2
+
+#define IMX95_CLK_CAMBLK_CSI2_FOR0		0
+#define IMX95_CLK_CAMBLK_CSI2_FOR1		1
+#define IMX95_CLK_CAMBLK_ISP_AXI		2
+#define IMX95_CLK_CAMBLK_ISP_PIXEL		3
+#define IMX95_CLK_CAMBLK_ISP			4
+
+#define IMX95_CLK_DISPMIX_LVDS_PHY_DIV		0
+#define IMX95_CLK_DISPMIX_LVDS_CH0_GATE		1
+#define IMX95_CLK_DISPMIX_LVDS_CH1_GATE		2
+#define IMX95_CLK_DISPMIX_PIX_DI0_GATE		3
+#define IMX95_CLK_DISPMIX_PIX_DI1_GATE		4
+
+#define IMX95_CLK_DISPMIX_ENG0_SEL		0
+#define IMX95_CLK_DISPMIX_ENG1_SEL		1
+
+#endif	/* __DT_BINDINGS_CLOCK_IMX95_H */

-- 
2.37.1


^ permalink raw reply related

* [PATCH v6 0/4] Add support i.MX95 BLK CTL module clock features
From: Peng Fan (OSS) @ 2024-04-01 13:28 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Shawn Guo, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Abel Vesa
  Cc: linux-clk, devicetree, imx, linux-arm-kernel, linux-kernel,
	Peng Fan, Krzysztof Kozlowski

i.MX95's several MIXes has BLK CTL module which could be used for
clk settings, QoS settings, Misc settings for a MIX. This patchset
is to add the clk feature support, including dt-bindings

Signed-off-by: Peng Fan <peng.fan@nxp.com>
---
Changes in v6:
- Put the header as 1st patch, and drop the macro that counting the clks
- Add R-b for patch 2&3
- Drop of_match_ptr in patch 4
- Link to v5: https://lore.kernel.org/r/20240324-imx95-blk-ctl-v5-0-7a706174078a@nxp.com

Changes in v5:
- Merge bindings except the one has mux-controller
- Separate clock ID headers in a separate patch per Rob's comments
- Link to v4: https://lore.kernel.org/r/20240314-imx95-blk-ctl-v4-0-d23de23b6ff2@nxp.com

Changes in v4:
- Separate binding doc for each modules, I still keep the syscon as node
name, because the module is not just for clock
- Pass dt-schema check
- Update node compatibles
- Link to v3: https://lore.kernel.org/r/20240228-imx95-blk-ctl-v3-0-40ceba01a211@nxp.com

Changes in v3:
- Correct example node compatible string
- Pass "make ARCH=arm64 DT_CHECKER_FLAGS=-m -j32 dt_binding_check"
- Link to v2: https://lore.kernel.org/r/20240228-imx95-blk-ctl-v2-0-ffb7eefb6dcd@nxp.com

Changes in v2:
- Correct example node compatible string
- Link to v1: https://lore.kernel.org/r/20240228-imx95-blk-ctl-v1-0-9b5ae3c14d83@nxp.com

---
Peng Fan (4):
      dt-bindings: clock: add i.MX95 clock header
      dt-bindings: clock: support i.MX95 BLK CTL module
      dt-bindings: clock: support i.MX95 Display Master CSR module
      clk: imx: add i.MX95 BLK CTL clk driver

 .../bindings/clock/nxp,imx95-blk-ctl.yaml          |  56 +++
 .../clock/nxp,imx95-display-master-csr.yaml        |  64 +++
 drivers/clk/imx/Kconfig                            |   7 +
 drivers/clk/imx/Makefile                           |   1 +
 drivers/clk/imx/clk-imx95-blk-ctl.c                | 438 +++++++++++++++++++++
 include/dt-bindings/clock/nxp,imx95-clock.h        |  28 ++
 6 files changed, 594 insertions(+)
---
base-commit: c9c32620af65fee2b1ac8390fe1349b33f9d0888
change-id: 20240228-imx95-blk-ctl-9ef8c1fc4c22

Best regards,
-- 
Peng Fan <peng.fan@nxp.com>


^ permalink raw reply

* [PATCH v2 2/2] ASoC: dt-bindings: fsl-asoc-card: convert to YAML
From: Shengjiu Wang @ 2024-04-01 12:54 UTC (permalink / raw)
  To: lgirdwood, broonie, robh+dt, krzysztof.kozlowski+dt, conor+dt,
	shengjiu.wang, linux-sound, devicetree, linux-kernel, shawnguo,
	s.hauer, kernel, festevam, imx, linux-arm-kernel
In-Reply-To: <1711976056-19884-1-git-send-email-shengjiu.wang@nxp.com>

Convert the fsl-asoc-card binding to YAML.

When testing dtbs_check, found below compatible strings
are not listed in document:

fsl,imx-sgtl5000
fsl,imx53-cpuvo-sgtl5000
fsl,imx51-babbage-sgtl5000
fsl,imx53-m53evk-sgtl5000
fsl,imx53-qsb-sgtl5000
fsl,imx53-voipac-sgtl5000
fsl,imx6-armadeus-sgtl5000
fsl,imx6-rex-sgtl5000
fsl,imx6-sabreauto-cs42888
fsl,imx6-wandboard-sgtl5000
fsl,imx6dl-nit6xlite-sgtl5000
fsl,imx6q-ba16-sgtl5000
fsl,imx6q-nitrogen6_max-sgtl5000
fsl,imx6q-nitrogen6_som2-sgtl5000
fsl,imx6q-nitrogen6x-sgtl5000
fsl,imx6q-sabrelite-sgtl5000
fsl,imx6q-sabresd-wm8962
fsl,imx6q-udoo-ac97
fsl,imx6q-ventana-sgtl5000
fsl,imx6sl-evk-wm8962
fsl,imx6sx-sdb-mqs
fsl,imx6sx-sdb-wm8962
fsl,imx7d-evk-wm8960
karo,tx53-audio-sgtl5000
tq,imx53-mba53-sgtl5000

So add them in yaml file to pass the test.

Also correct the 'dai-format' to 'format' in document.

For 'audio-routing', the items are not listed. Because
this fsl-asoc-card is generic driver, which supports several
codecs, if list all the items, there will be a long list.

Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
 .../bindings/sound/fsl-asoc-card.txt          | 117 -----------
 .../bindings/sound/fsl-asoc-card.yaml         | 195 ++++++++++++++++++
 2 files changed, 195 insertions(+), 117 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
 create mode 100644 Documentation/devicetree/bindings/sound/fsl-asoc-card.yaml

diff --git a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt b/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
deleted file mode 100644
index 4e8dbc5abfd1..000000000000
--- a/Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
+++ /dev/null
@@ -1,117 +0,0 @@
-Freescale Generic ASoC Sound Card with ASRC support
-
-The Freescale Generic ASoC Sound Card can be used, ideally, for all Freescale
-SoCs connecting with external CODECs.
-
-The idea of this generic sound card is a bit like ASoC Simple Card. However,
-for Freescale SoCs (especially those released in recent years), most of them
-have ASRC (Documentation/devicetree/bindings/sound/fsl,asrc.txt) inside. And
-this is a specific feature that might be painstakingly controlled and merged
-into the Simple Card.
-
-So having this generic sound card allows all Freescale SoC users to benefit
-from the simplification of a new card support and the capability of the wide
-sample rates support through ASRC.
-
-Note: The card is initially designed for those sound cards who use AC'97, I2S
-      and PCM DAI formats. However, it'll be also possible to support those non
-      AC'97/I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as
-      long as the driver has been properly upgraded.
-
-
-The compatible list for this generic sound card currently:
- "fsl,imx-audio-ac97"
-
- "fsl,imx-audio-cs42888"
-
- "fsl,imx-audio-cs427x"
- (compatible with CS4271 and CS4272)
-
- "fsl,imx-audio-wm8962"
-
- "fsl,imx-audio-sgtl5000"
- (compatible with Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt)
-
- "fsl,imx-audio-wm8960"
-
- "fsl,imx-audio-mqs"
-
- "fsl,imx-audio-wm8524"
-
- "fsl,imx-audio-tlv320aic32x4"
-
- "fsl,imx-audio-tlv320aic31xx"
-
- "fsl,imx-audio-si476x"
-
- "fsl,imx-audio-wm8958"
-
- "fsl,imx-audio-nau8822"
-
-Required properties:
-
-  - compatible		: Contains one of entries in the compatible list.
-
-  - model		: The user-visible name of this sound complex
-
-  - audio-cpu		: The phandle of an CPU DAI controller
-
-  - audio-codec		: The phandle of an audio codec
-
-Optional properties:
-
-  - audio-asrc		: The phandle of ASRC. It can be absent if there's no
-			  need to add ASRC support via DPCM.
-
-  - audio-routing	: A list of the connections between audio components.
-			  Each entry is a pair of strings, the first being the
-			  connection's sink, the second being the connection's
-			  source. There're a few pre-designed board connectors:
-			   * Line Out Jack
-			   * Line In Jack
-			   * Headphone Jack
-			   * Mic Jack
-			   * Ext Spk
-			   * AMIC (stands for Analog Microphone Jack)
-			   * DMIC (stands for Digital Microphone Jack)
-
-			  Note: The "Mic Jack" and "AMIC" are redundant while
-			        coexisting in order to support the old bindings
-				of wm8962 and sgtl5000.
-
-  - hp-det-gpio		: The GPIO that detect headphones are plugged in
-  - mic-det-gpio	: The GPIO that detect microphones are plugged in
-  - bitclock-master	: Indicates dai-link bit clock master; for details see simple-card.yaml.
-  - frame-master	: Indicates dai-link frame master; for details see simple-card.yaml.
-  - dai-format		: audio format, for details see simple-card.yaml.
-  - frame-inversion	: dai-link uses frame clock inversion, for details see simple-card.yaml.
-  - bitclock-inversion	: dai-link uses bit clock inversion, for details see simple-card.yaml.
-  - mclk-id		: main clock id, specific for each card configuration.
-
-Optional unless SSI is selected as a CPU DAI:
-
-  - mux-int-port	: The internal port of the i.MX audio muxer (AUDMUX)
-
-  - mux-ext-port	: The external port of the i.MX audio muxer
-
-Example:
-sound-cs42888 {
-	compatible = "fsl,imx-audio-cs42888";
-	model = "cs42888-audio";
-	audio-cpu = <&esai>;
-	audio-asrc = <&asrc>;
-	audio-codec = <&cs42888>;
-	audio-routing =
-		"Line Out Jack", "AOUT1L",
-		"Line Out Jack", "AOUT1R",
-		"Line Out Jack", "AOUT2L",
-		"Line Out Jack", "AOUT2R",
-		"Line Out Jack", "AOUT3L",
-		"Line Out Jack", "AOUT3R",
-		"Line Out Jack", "AOUT4L",
-		"Line Out Jack", "AOUT4R",
-		"AIN1L", "Line In Jack",
-		"AIN1R", "Line In Jack",
-		"AIN2L", "Line In Jack",
-		"AIN2R", "Line In Jack";
-};
diff --git a/Documentation/devicetree/bindings/sound/fsl-asoc-card.yaml b/Documentation/devicetree/bindings/sound/fsl-asoc-card.yaml
new file mode 100644
index 000000000000..42ca39eebd49
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/fsl-asoc-card.yaml
@@ -0,0 +1,195 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/fsl-asoc-card.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Freescale Generic ASoC Sound Card with ASRC support
+
+description:
+  The Freescale Generic ASoC Sound Card can be used, ideally,
+  for all Freescale SoCs connecting with external CODECs.
+
+  The idea of this generic sound card is a bit like ASoC Simple Card.
+  However, for Freescale SoCs (especially those released in recent years),
+  most of them have ASRC inside. And this is a specific feature that might
+  be painstakingly controlled and merged into the Simple Card.
+
+  So having this generic sound card allows all Freescale SoC users to
+  benefit from the simplification of a new card support and the capability
+  of the wide sample rates support through ASRC.
+
+  Note, The card is initially designed for those sound cards who use AC'97, I2S
+  and PCM DAI formats. However, it'll be also possible to support those non
+  AC'97/I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as
+  long as the driver has been properly upgraded.
+
+maintainers:
+  - Shengjiu Wang <shengjiu.wang@nxp.com>
+
+properties:
+  compatible:
+    oneOf:
+      - items:
+          - enum:
+              - fsl,imx-sgtl5000
+              - fsl,imx53-cpuvo-sgtl5000
+              - fsl,imx51-babbage-sgtl5000
+              - fsl,imx53-m53evk-sgtl5000
+              - fsl,imx53-qsb-sgtl5000
+              - fsl,imx53-voipac-sgtl5000
+              - fsl,imx6-armadeus-sgtl5000
+              - fsl,imx6-rex-sgtl5000
+              - fsl,imx6-sabreauto-cs42888
+              - fsl,imx6-wandboard-sgtl5000
+              - fsl,imx6dl-nit6xlite-sgtl5000
+              - fsl,imx6q-ba16-sgtl5000
+              - fsl,imx6q-nitrogen6_max-sgtl5000
+              - fsl,imx6q-nitrogen6_som2-sgtl5000
+              - fsl,imx6q-nitrogen6x-sgtl5000
+              - fsl,imx6q-sabrelite-sgtl5000
+              - fsl,imx6q-sabresd-wm8962
+              - fsl,imx6q-udoo-ac97
+              - fsl,imx6q-ventana-sgtl5000
+              - fsl,imx6sl-evk-wm8962
+              - fsl,imx6sx-sdb-mqs
+              - fsl,imx6sx-sdb-wm8962
+              - fsl,imx7d-evk-wm8960
+              - karo,tx53-audio-sgtl5000
+              - tq,imx53-mba53-sgtl5000
+          - enum:
+              - fsl,imx-audio-ac97
+              - fsl,imx-audio-cs42888
+              - fsl,imx-audio-mqs
+              - fsl,imx-audio-sgtl5000
+              - fsl,imx-audio-wm8960
+              - fsl,imx-audio-wm8962
+      - items:
+          - enum:
+              - fsl,imx-audio-ac97
+              - fsl,imx-audio-cs42888
+              - fsl,imx-audio-cs427x
+              - fsl,imx-audio-mqs
+              - fsl,imx-audio-nau8822
+              - fsl,imx-audio-sgtl5000
+              - fsl,imx-audio-si476x
+              - fsl,imx-audio-tlv320aic31xx
+              - fsl,imx-audio-tlv320aic32x4
+              - fsl,imx-audio-wm8524
+              - fsl,imx-audio-wm8960
+              - fsl,imx-audio-wm8962
+              - fsl,imx-audio-wm8958
+
+  model:
+    $ref: /schemas/types.yaml#/definitions/string
+    description: The user-visible name of this sound complex
+
+  audio-asrc:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description:
+      The phandle of ASRC. It can be absent if there's no
+      need to add ASRC support via DPCM.
+
+  audio-codec:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: The phandle of an audio codec
+
+  audio-cpu:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: The phandle of an CPU DAI controller
+
+  audio-routing:
+    $ref: /schemas/types.yaml#/definitions/non-unique-string-array
+    description:
+      A list of the connections between audio components. Each entry is a
+      pair of strings, the first being the connection's sink, the second
+      being the connection's source. There're a few pre-designed board
+      connectors. "AMIC" stands for Analog Microphone Jack.
+      "DMIC" stands for Digital Microphone Jack. The "Mic Jack" and "AMIC"
+      are redundant while coexisting in order to support the old bindings
+      of wm8962 and sgtl5000.
+
+  hp-det-gpio:
+    deprecated: true
+    maxItems: 1
+    description: The GPIO that detect headphones are plugged in
+
+  hp-det-gpios:
+    maxItems: 1
+    description: The GPIO that detect headphones are plugged in
+
+  mic-det-gpio:
+    deprecated: true
+    maxItems: 1
+    description: The GPIO that detect microphones are plugged in
+
+  mic-det-gpios:
+    maxItems: 1
+    description: The GPIO that detect microphones are plugged in
+
+  bitclock-master:
+    $ref: simple-card.yaml#/definitions/bitclock-master
+    description: Indicates dai-link bit clock master.
+
+  frame-master:
+    $ref: simple-card.yaml#/definitions/frame-master
+    description: Indicates dai-link frame master.
+
+  format:
+    $ref: simple-card.yaml#/definitions/format
+    description: audio format.
+
+  frame-inversion:
+    $ref: simple-card.yaml#/definitions/frame-inversion
+    description: dai-link uses frame clock inversion.
+
+  bitclock-inversion:
+    $ref: simple-card.yaml#/definitions/bitclock-inversion
+    description: dai-link uses bit clock inversion.
+
+  mclk-id:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: main clock id, specific for each card configuration.
+
+  mux-int-port:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    enum: [1, 2, 7]
+    description: The internal port of the i.MX audio muxer (AUDMUX)
+
+  mux-ext-port:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    enum: [3, 4, 5, 6]
+    description: The external port of the i.MX audio muxer
+
+  ssi-controller:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: The phandle of an CPU DAI controller
+
+required:
+  - compatible
+  - model
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    sound-cs42888 {
+        compatible = "fsl,imx-audio-cs42888";
+        model = "cs42888-audio";
+        audio-cpu = <&esai>;
+        audio-asrc = <&asrc>;
+        audio-codec = <&cs42888>;
+        audio-routing =
+             "Line Out Jack", "AOUT1L",
+             "Line Out Jack", "AOUT1R",
+             "Line Out Jack", "AOUT2L",
+             "Line Out Jack", "AOUT2R",
+             "Line Out Jack", "AOUT3L",
+             "Line Out Jack", "AOUT3R",
+             "Line Out Jack", "AOUT4L",
+             "Line Out Jack", "AOUT4R",
+             "AIN1L", "Line In Jack",
+             "AIN1R", "Line In Jack",
+             "AIN2L", "Line In Jack",
+             "AIN2R", "Line In Jack";
+    };
-- 
2.34.1


^ permalink raw reply related

* [PATCH v2 1/2] ARM: dts: imx6sx-nitrogen6sx: drop incorrect cpu-dai property
From: Shengjiu Wang @ 2024-04-01 12:54 UTC (permalink / raw)
  To: lgirdwood, broonie, robh+dt, krzysztof.kozlowski+dt, conor+dt,
	shengjiu.wang, linux-sound, devicetree, linux-kernel, shawnguo,
	s.hauer, kernel, festevam, imx, linux-arm-kernel
In-Reply-To: <1711976056-19884-1-git-send-email-shengjiu.wang@nxp.com>

drop incorrect cpu-dai property, change it to ssi-controller

Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
 arch/arm/boot/dts/nxp/imx/imx6sx-nitrogen6sx.dts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/nxp/imx/imx6sx-nitrogen6sx.dts b/arch/arm/boot/dts/nxp/imx/imx6sx-nitrogen6sx.dts
index cd9cbc9ccc9e..b82d91a7d76d 100644
--- a/arch/arm/boot/dts/nxp/imx/imx6sx-nitrogen6sx.dts
+++ b/arch/arm/boot/dts/nxp/imx/imx6sx-nitrogen6sx.dts
@@ -83,7 +83,7 @@ reg_wlan: regulator-wlan {
 	sound {
 		compatible = "fsl,imx-audio-sgtl5000";
 		model = "imx6sx-nitrogen6sx-sgtl5000";
-		cpu-dai = <&ssi1>;
+		ssi-controller = <&ssi1>;
 		audio-codec = <&codec>;
 		audio-routing =
 			"MIC_IN", "Mic Jack",
-- 
2.34.1


^ permalink raw reply related

* [PATCH v2 0/2] ASoC: dt-bindings: convert fsl-asoc-card.txt to YAML
From: Shengjiu Wang @ 2024-04-01 12:54 UTC (permalink / raw)
  To: lgirdwood, broonie, robh+dt, krzysztof.kozlowski+dt, conor+dt,
	shengjiu.wang, linux-sound, devicetree, linux-kernel, shawnguo,
	s.hauer, kernel, festevam, imx, linux-arm-kernel

Convert fsl-asoc-card.txt to YAML. In order to pass the checking,
add some used compatible string from devicetree.

change cpu-dai in imx6sx-nitrogen6sx to ssi-controller.

changes in v2:
- update commit message for reason why add compatible strings
  which are not in txt file.
- add deprecated
- add $ref for bitclock-master and others.

Shengjiu Wang (2):
  ARM: dts: imx6sx-nitrogen6sx: drop incorrect cpu-dai property
  ASoC: dt-bindings: fsl-asoc-card: convert to YAML

 .../bindings/sound/fsl-asoc-card.txt          | 117 -----------
 .../bindings/sound/fsl-asoc-card.yaml         | 195 ++++++++++++++++++
 .../boot/dts/nxp/imx/imx6sx-nitrogen6sx.dts   |   2 +-
 3 files changed, 196 insertions(+), 118 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
 create mode 100644 Documentation/devicetree/bindings/sound/fsl-asoc-card.yaml

-- 
2.34.1


^ permalink raw reply

* Re: [PATCH v3 1/2] dt-bindings: arm: qcom: Document the Samsung Galaxy Z Fold5
From: Rob Herring @ 2024-04-01 13:05 UTC (permalink / raw)
  To: Alexandru Marc Serdeliuc
  Cc: devicetree, linux-arm-msm, Bjorn Andersson, linux-kernel,
	Conor Dooley, Krzysztof Kozlowski, Konrad Dybcio
In-Reply-To: <20240331-samsung-galaxy-zfold5-q5q-v3-1-17ae8d0a9fba@yahoo.com>


On Sun, 31 Mar 2024 12:56:39 +0200, Alexandru Marc Serdeliuc wrote:
> This documents Samsung Galaxy Z Fold5 (samsung,q5q)
> which is a foldable phone by Samsung based on the sm8550 SoC.
> 
> Signed-off-by: Alexandru Marc Serdeliuc <serdeliuk@yahoo.com>
> ---
>  Documentation/devicetree/bindings/arm/qcom.yaml | 1 +
>  1 file changed, 1 insertion(+)
> 

Acked-by: Rob Herring <robh@kernel.org>


^ permalink raw reply

* Re: [PATCH v5 1/2] dt-bindings: backlight: Add Texas Instruments LM3509
From: Rob Herring @ 2024-04-01 13:03 UTC (permalink / raw)
  To: Patrick Gansterer
  Cc: dri-devel, linux-leds, devicetree, linux-kernel, linux-fbdev,
	Lee Jones, Daniel Thompson, Jingoo Han, Pavel Machek,
	Krzysztof Kozlowski, Conor Dooley, Helge Deller, Sam Ravnborg,
	Krzysztof Kozlowski
In-Reply-To: <20240330145931.729116-2-paroga@paroga.com>

On Sat, Mar 30, 2024 at 03:59:24PM +0100, Patrick Gansterer wrote:
> Add Device Tree bindings for Texas Instruments LM3509 - a
> High Efficiency Boost for White LED's and/or OLED Displays
> 
> Signed-off-by: Patrick Gansterer <paroga@paroga.com>
> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
> Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org>
> ---
>  .../bindings/leds/backlight/ti,lm3509.yaml    | 139 ++++++++++++++++++
>  1 file changed, 139 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/leds/backlight/ti,lm3509.yaml
> 
> diff --git a/Documentation/devicetree/bindings/leds/backlight/ti,lm3509.yaml b/Documentation/devicetree/bindings/leds/backlight/ti,lm3509.yaml
> new file mode 100644
> index 000000000000..b67f67648852
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/leds/backlight/ti,lm3509.yaml
> @@ -0,0 +1,139 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/leds/backlight/ti,lm3509.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: TI LM3509 High Efficiency Boost for White LED's and/or OLED Displays
> +
> +maintainers:
> +  - Patrick Gansterer <paroga@paroga.com>
> +
> +description:
> +  The LM3509 current mode boost converter offers two separate outputs.
> +  https://www.ti.com/product/LM3509
> +
> +properties:
> +  compatible:
> +    const: ti,lm3509
> +
> +  reg:
> +    maxItems: 1
> +
> +  "#address-cells":
> +    const: 1
> +
> +  "#size-cells":
> +    const: 0
> +
> +  reset-gpios:
> +    maxItems: 1
> +
> +  ti,brightness-rate-of-change-us:
> +    description: Brightness Rate of Change in microseconds.
> +    enum: [51, 13000, 26000, 52000]
> +
> +  ti,oled-mode:
> +    description: Enable OLED mode.
> +    type: boolean
> +
> +patternProperties:
> +  "^led@[01]$":
> +    type: object
> +    description: Properties for a string of connected LEDs.
> +
> +    allOf:

You don't need allOf here.

> +      - $ref: common.yaml#
> +
> +    properties:
> +      reg:
> +        description:
> +          The control register that is used to program the two current sinks.
> +          The LM3509 has two registers (BMAIN and BSUB) and are represented
> +          as 0 or 1 in this property. The two current sinks can be controlled
> +          independently with both registers, or register BMAIN can be
> +          configured to control both sinks with the led-sources property.
> +        minimum: 0
> +        maximum: 1
> +
> +      label: true
> +
> +      led-sources:
> +        allOf:

Or here.

> +          - minItems: 1
> +            maxItems: 2
> +            items:
> +              minimum: 0
> +              maximum: 1

^ permalink raw reply

* [PATCH v2 2/2] arm64: dts: qcom: sm8150: Add video clock controller node
From: Satya Priya Kakitapalli @ 2024-04-01 11:14 UTC (permalink / raw)
  To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Michael Turquette, Stephen Boyd
  Cc: Dmitry Baryshkov, Ajit Pandey, Imran Shaik, Taniya Das,
	Jagadeesh Kona, linux-arm-msm, devicetree, linux-kernel,
	linux-clk, Satya Priya Kakitapalli
In-Reply-To: <20240401-videocc-sm8150-dt-node-v2-0-3b87cd2add96@quicinc.com>

Add device node for video clock controller on Qualcomm
SM8150 platform.

Signed-off-by: Satya Priya Kakitapalli <quic_skakitap@quicinc.com>
---
 arch/arm64/boot/dts/qcom/sa8155p.dtsi |  4 ++++
 arch/arm64/boot/dts/qcom/sm8150.dtsi  | 13 +++++++++++++
 2 files changed, 17 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/sa8155p.dtsi b/arch/arm64/boot/dts/qcom/sa8155p.dtsi
index ffb7ab695213..9e70effc72e1 100644
--- a/arch/arm64/boot/dts/qcom/sa8155p.dtsi
+++ b/arch/arm64/boot/dts/qcom/sa8155p.dtsi
@@ -38,3 +38,7 @@ &rpmhpd {
 	 */
 	compatible = "qcom,sa8155p-rpmhpd";
 };
+
+&videocc {
+	power-domains = <&rpmhpd SA8155P_CX>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sm8150.dtsi b/arch/arm64/boot/dts/qcom/sm8150.dtsi
index a35c0852b5a1..56694efa0c33 100644
--- a/arch/arm64/boot/dts/qcom/sm8150.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8150.dtsi
@@ -14,6 +14,7 @@
 #include <dt-bindings/clock/qcom,dispcc-sm8150.h>
 #include <dt-bindings/clock/qcom,gcc-sm8150.h>
 #include <dt-bindings/clock/qcom,gpucc-sm8150.h>
+#include <dt-bindings/clock/qcom,videocc-sm8150.h>
 #include <dt-bindings/interconnect/qcom,osm-l3.h>
 #include <dt-bindings/interconnect/qcom,sm8150.h>
 #include <dt-bindings/thermal/thermal.h>
@@ -3715,6 +3716,18 @@ usb_2_dwc3: usb@a800000 {
 			};
 		};
 
+		videocc: clock-controller@ab00000 {
+			compatible = "qcom,sm8150-videocc";
+			reg = <0 0x0ab00000 0 0x10000>;
+			clocks = <&gcc GCC_VIDEO_AHB_CLK>,
+				 <&rpmhcc RPMH_CXO_CLK>;
+			clock-names = "iface", "bi_tcxo";
+			power-domains = <&rpmhpd SM8150_MMCX>;
+		        #clock-cells = <1>;
+			#reset-cells = <1>;
+			#power-domain-cells = <1>;
+		};
+
 		camnoc_virt: interconnect@ac00000 {
 			compatible = "qcom,sm8150-camnoc-virt";
 			reg = <0 0x0ac00000 0 0x1000>;

-- 
2.25.1


^ permalink raw reply related

* [PATCH v2 1/2] dt-bindings: clock: qcom: Update SM8150 videocc bindings
From: Satya Priya Kakitapalli @ 2024-04-01 11:14 UTC (permalink / raw)
  To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Michael Turquette, Stephen Boyd
  Cc: Dmitry Baryshkov, Ajit Pandey, Imran Shaik, Taniya Das,
	Jagadeesh Kona, linux-arm-msm, devicetree, linux-kernel,
	linux-clk, Satya Priya Kakitapalli
In-Reply-To: <20240401-videocc-sm8150-dt-node-v2-0-3b87cd2add96@quicinc.com>

Update the clocks list for SM8150 to add both AHB and XO clocks,
as it needs both of them.

Fixes: 35d26e9292e2 ("dt-bindings: clock: Add YAML schemas for the QCOM VIDEOCC clock bindings")
Signed-off-by: Satya Priya Kakitapalli <quic_skakitap@quicinc.com>
---
 .../devicetree/bindings/clock/qcom,videocc.yaml         | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/clock/qcom,videocc.yaml b/Documentation/devicetree/bindings/clock/qcom,videocc.yaml
index 6999e36ace1b..68bac801adb0 100644
--- a/Documentation/devicetree/bindings/clock/qcom,videocc.yaml
+++ b/Documentation/devicetree/bindings/clock/qcom,videocc.yaml
@@ -75,7 +75,6 @@ allOf:
           enum:
             - qcom,sc7180-videocc
             - qcom,sdm845-videocc
-            - qcom,sm8150-videocc
     then:
       properties:
         clocks:
@@ -101,6 +100,22 @@ allOf:
             - const: bi_tcxo
             - const: bi_tcxo_ao
 
+  - if:
+      properties:
+        compatible:
+          enum:
+            - qcom,sm8150-videocc
+    then:
+      properties:
+        clocks:
+          items:
+            - description: AHB
+            - description: Board XO source
+        clock-names:
+          items:
+            - const: iface
+            - const: bi_tcxo
+
   - if:
       properties:
         compatible:

-- 
2.25.1


^ permalink raw reply related

* [PATCH v2 0/2] Add DT support for video clock controller on SM8150
From: Satya Priya Kakitapalli @ 2024-04-01 11:14 UTC (permalink / raw)
  To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Michael Turquette, Stephen Boyd
  Cc: Dmitry Baryshkov, Ajit Pandey, Imran Shaik, Taniya Das,
	Jagadeesh Kona, linux-arm-msm, devicetree, linux-kernel,
	linux-clk, Satya Priya Kakitapalli

Signed-off-by: Satya Priya Kakitapalli <quic_skakitap@quicinc.com>
---
Changes in v2:
- As per Dmitry's comments, there is no need to update to index based
  lookup for already existing drivers, hence keeping clock-names property.
- Updated the videocc bindings to add AHB clock for the sm8150 platform.
- Link to v1: https://lore.kernel.org/r/20240313-videocc-sm8150-dt-node-v1-0-ae8ec3c822c2@quicinc.com

---
Satya Priya Kakitapalli (2):
      dt-bindings: clock: qcom: Update SM8150 videocc bindings
      arm64: dts: qcom: sm8150: Add video clock controller node

 .../devicetree/bindings/clock/qcom,videocc.yaml         | 17 ++++++++++++++++-
 arch/arm64/boot/dts/qcom/sa8155p.dtsi                   |  4 ++++
 arch/arm64/boot/dts/qcom/sm8150.dtsi                    | 13 +++++++++++++
 3 files changed, 33 insertions(+), 1 deletion(-)
---
base-commit: 8ffc8b1bbd505e27e2c8439d326b6059c906c9dd
change-id: 20240308-videocc-sm8150-dt-node-6f163b492f7c

Best regards,
-- 
Satya Priya Kakitapalli <quic_skakitap@quicinc.com>


^ permalink raw reply

* Re: [PATCH v7 2/2] dmaengine: Loongson1: Add Loongson-1 APB DMA driver
From: Keguang Zhang @ 2024-04-01 11:09 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Vinod Koul, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	linux-mips, dmaengine, devicetree, linux-kernel
In-Reply-To: <CAAhV-H63wMMhVng=kn+XOHFL8sTchtGAMae0v50FEN6TO1kAhw@mail.gmail.com>

On Mon, Apr 1, 2024 at 5:06 PM Huacai Chen <chenhuacai@kernel.org> wrote:
>
> On Mon, Apr 1, 2024 at 10:45 AM Keguang Zhang <keguang.zhang@gmail.com> wrote:
> >
> > Hi Huacai,
> >
> > On Sat, Mar 30, 2024 at 9:59 PM Huacai Chen <chenhuacai@kernel.org> wrote:
> > >
> > > Hi, Keguang,
> > >
> > > On Fri, Mar 29, 2024 at 7:28 PM Keguang Zhang via B4 Relay
> > > <devnull+keguang.zhang.gmail.com@kernel.org> wrote:
> > > >
> > > > From: Keguang Zhang <keguang.zhang@gmail.com>
> > > >
> > > > This patch adds APB DMA driver for Loongson-1 SoCs.
> > > >
> > > > Signed-off-by: Keguang Zhang <keguang.zhang@gmail.com>
> > > > ---
> > > > Changes in v7:
> > > > - Change the comptible to 'loongson,ls1*-apbdma'
> > > > - Update Kconfig and Makefile accordingly
> > > > - Rename the file to loongson1-apb-dma.c to keep the consistency
> > > >
> > > > Changes in v6:
> > > > - Implement .device_prep_dma_cyclic for Loongson1 audio driver,
> > > > - as well as .device_pause and .device_resume.
> > > > - Set the limitation LS1X_DMA_MAX_DESC and put all descriptors
> > > > - into one page to save memory
> > > > - Move dma_pool_zalloc() into ls1x_dma_alloc_desc()
> > > > - Drop dma_slave_config structure
> > > > - Use .remove_new instead of .remove
> > > > - Use KBUILD_MODNAME for the driver name
> > > > - Improve the debug information
> > > >
> > > > Changes in v5:
> > > > - Add DT support
> > > > - Use DT data instead of platform data
> > > > - Use chan_id of struct dma_chan instead of own id
> > > > - Use of_dma_xlate_by_chan_id() instead of ls1x_dma_filter()
> > > > - Update the author information to my official name
> > > >
> > > > Changes in v4:
> > > > - Use dma_slave_map to find the proper channel.
> > > > - Explicitly call devm_request_irq() and tasklet_kill().
> > > > - Fix namespace issue.
> > > > - Some minor fixes and cleanups.
> > > >
> > > > Changes in v3:
> > > > - Rename ls1x_dma_filter_fn to ls1x_dma_filter.
> > > >
> > > > Changes in v2:
> > > > - Change the config from 'DMA_LOONGSON1' to 'LOONGSON1_DMA',
> > > > - and rearrange it in alphabetical order in Kconfig and Makefile.
> > > > - Fix comment style.
> > > > ---
> > > >  drivers/dma/Kconfig             |   9 +
> > > >  drivers/dma/Makefile            |   1 +
> > > >  drivers/dma/loongson1-apb-dma.c | 665 ++++++++++++++++++++++++++++++++++++++++
> > > >  3 files changed, 675 insertions(+)
> > > >
> > > > diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
> > > > index 002a5ec80620..f7b06c4cdf3f 100644
> > > > --- a/drivers/dma/Kconfig
> > > > +++ b/drivers/dma/Kconfig
> > > > @@ -369,6 +369,15 @@ config K3_DMA
> > > >           Support the DMA engine for Hisilicon K3 platform
> > > >           devices.
> > > >
> > > > +config LOONGSON1_APB_DMA
> > > > +       tristate "Loongson1 APB DMA support"
> > > > +       depends on MACH_LOONGSON32 || COMPILE_TEST
> > > > +       select DMA_ENGINE
> > > > +       select DMA_VIRTUAL_CHANNELS
> > > > +       help
> > > > +         This selects support for the APB DMA controller in Loongson1 SoCs,
> > > > +         which is required by Loongson1 NAND and audio support.
> > > Why not rename to LS1X_APB_DMA and put it just before LS2X_APB_DMA
> > > (and also the driver file name)?
> > >
> > So far all Kconfig entries of Loongson-1 drivers are named with the
> > keyword "LOONGSON1".
> > The same is true for these file names.
> > Therefore, I need to keep the consistency.
> But I see LS1X_IRQ in drivers/irqchip/Kconfig
>
Indeed, that's an exception, which was submitted by Jiaxun several years ago.
Actually, most drivers of Loongson family use the keyword "LOONGSON"
for Kconfig and "loongson" for filename.
Thus I take this keywork as the naming convention.

> Huacai
>
> >
> >
> > > Huacai
> > >
> > > > +
> > > >  config LPC18XX_DMAMUX
> > > >         bool "NXP LPC18xx/43xx DMA MUX for PL080"
> > > >         depends on ARCH_LPC18XX || COMPILE_TEST
> > > > diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
> > > > index dfd40d14e408..b26f6677978a 100644
> > > > --- a/drivers/dma/Makefile
> > > > +++ b/drivers/dma/Makefile
> > > > @@ -47,6 +47,7 @@ obj-$(CONFIG_INTEL_IDMA64) += idma64.o
> > > >  obj-$(CONFIG_INTEL_IOATDMA) += ioat/
> > > >  obj-y += idxd/
> > > >  obj-$(CONFIG_K3_DMA) += k3dma.o
> > > > +obj-$(CONFIG_LOONGSON1_APB_DMA) += loongson1-apb-dma.o
> > > >  obj-$(CONFIG_LPC18XX_DMAMUX) += lpc18xx-dmamux.o
> > > >  obj-$(CONFIG_LS2X_APB_DMA) += ls2x-apb-dma.o
> > > >  obj-$(CONFIG_MILBEAUT_HDMAC) += milbeaut-hdmac.o
> > > > diff --git a/drivers/dma/loongson1-apb-dma.c b/drivers/dma/loongson1-apb-dma.c
> > > > new file mode 100644
> > > > index 000000000000..d474a2601e6e
> > > > --- /dev/null
> > > > +++ b/drivers/dma/loongson1-apb-dma.c
> > > > @@ -0,0 +1,665 @@
> > > > +// SPDX-License-Identifier: GPL-2.0-or-later
> > > > +/*
> > > > + * Driver for Loongson-1 APB DMA Controller
> > > > + *
> > > > + * Copyright (C) 2015-2024 Keguang Zhang <keguang.zhang@gmail.com>
> > > > + */
> > > > +
> > > > +#include <linux/dmapool.h>
> > > > +#include <linux/dma-mapping.h>
> > > > +#include <linux/init.h>
> > > > +#include <linux/interrupt.h>
> > > > +#include <linux/iopoll.h>
> > > > +#include <linux/module.h>
> > > > +#include <linux/of.h>
> > > > +#include <linux/of_dma.h>
> > > > +#include <linux/platform_device.h>
> > > > +#include <linux/slab.h>
> > > > +
> > > > +#include "dmaengine.h"
> > > > +#include "virt-dma.h"
> > > > +
> > > > +/* Loongson-1 DMA Control Register */
> > > > +#define DMA_CTRL                       0x0
> > > > +
> > > > +/* DMA Control Register Bits */
> > > > +#define DMA_STOP                       BIT(4)
> > > > +#define DMA_START                      BIT(3)
> > > > +#define DMA_ASK_VALID                  BIT(2)
> > > > +
> > > > +#define DMA_ADDR_MASK                  GENMASK(31, 6)
> > > > +
> > > > +/* DMA Next Field Bits */
> > > > +#define DMA_NEXT_VALID                 BIT(0)
> > > > +
> > > > +/* DMA Command Field Bits */
> > > > +#define DMA_RAM2DEV                    BIT(12)
> > > > +#define DMA_INT                                BIT(1)
> > > > +#define DMA_INT_MASK                   BIT(0)
> > > > +
> > > > +#define LS1X_DMA_MAX_CHANNELS          3
> > > > +
> > > > +/* Size of allocations for hardware descriptors */
> > > > +#define LS1X_DMA_DESCS_SIZE            PAGE_SIZE
> > > > +#define LS1X_DMA_MAX_DESC              \
> > > > +       (LS1X_DMA_DESCS_SIZE / sizeof(struct ls1x_dma_hwdesc))
> > > > +
> > > > +struct ls1x_dma_hwdesc {
> > > > +       u32 next;               /* next descriptor address */
> > > > +       u32 saddr;              /* memory DMA address */
> > > > +       u32 daddr;              /* device DMA address */
> > > > +       u32 length;
> > > > +       u32 stride;
> > > > +       u32 cycles;
> > > > +       u32 cmd;
> > > > +       u32 stats;
> > > > +};
> > > > +
> > > > +struct ls1x_dma_desc {
> > > > +       struct virt_dma_desc vdesc;
> > > > +       enum dma_transfer_direction dir;
> > > > +       enum dma_transaction_type type;
> > > > +       unsigned int bus_width;
> > > > +
> > > > +       unsigned int nr_descs;  /* number of descriptors */
> > > > +
> > > > +       struct ls1x_dma_hwdesc *hwdesc;
> > > > +       dma_addr_t hwdesc_phys;
> > > > +};
> > > > +
> > > > +struct ls1x_dma_chan {
> > > > +       struct virt_dma_chan vchan;
> > > > +       struct dma_pool *desc_pool;
> > > > +       phys_addr_t src_addr;
> > > > +       phys_addr_t dst_addr;
> > > > +       enum dma_slave_buswidth src_addr_width;
> > > > +       enum dma_slave_buswidth dst_addr_width;
> > > > +
> > > > +       void __iomem *reg_base;
> > > > +       int irq;
> > > > +
> > > > +       struct ls1x_dma_desc *desc;
> > > > +
> > > > +       struct ls1x_dma_hwdesc *curr_hwdesc;
> > > > +       dma_addr_t curr_hwdesc_phys;
> > > > +};
> > > > +
> > > > +struct ls1x_dma {
> > > > +       struct dma_device ddev;
> > > > +       void __iomem *reg_base;
> > > > +
> > > > +       unsigned int nr_chans;
> > > > +       struct ls1x_dma_chan chan[];
> > > > +};
> > > > +
> > > > +#define to_ls1x_dma_chan(dchan)                \
> > > > +       container_of(dchan, struct ls1x_dma_chan, vchan.chan)
> > > > +
> > > > +#define to_ls1x_dma_desc(vd)           \
> > > > +       container_of(vd, struct ls1x_dma_desc, vdesc)
> > > > +
> > > > +/* macros for registers read/write */
> > > > +#define chan_readl(chan, off)          \
> > > > +       readl((chan)->reg_base + (off))
> > > > +
> > > > +#define chan_writel(chan, off, val)    \
> > > > +       writel((val), (chan)->reg_base + (off))
> > > > +
> > > > +static inline struct device *chan2dev(struct dma_chan *chan)
> > > > +{
> > > > +       return &chan->dev->device;
> > > > +}
> > > > +
> > > > +static inline int ls1x_dma_query(struct ls1x_dma_chan *chan,
> > > > +                                dma_addr_t *hwdesc_phys)
> > > > +{
> > > > +       struct dma_chan *dchan = &chan->vchan.chan;
> > > > +       int val, ret;
> > > > +
> > > > +       val = *hwdesc_phys & DMA_ADDR_MASK;
> > > > +       val |= DMA_ASK_VALID;
> > > > +       val |= dchan->chan_id;
> > > > +       chan_writel(chan, DMA_CTRL, val);
> > > > +       ret = readl_poll_timeout_atomic(chan->reg_base + DMA_CTRL, val,
> > > > +                                       !(val & DMA_ASK_VALID), 0, 3000);
> > > > +       if (ret)
> > > > +               dev_err(chan2dev(dchan), "failed to query DMA\n");
> > > > +
> > > > +       return ret;
> > > > +}
> > > > +
> > > > +static inline int ls1x_dma_start(struct ls1x_dma_chan *chan,
> > > > +                                dma_addr_t *hwdesc_phys)
> > > > +{
> > > > +       struct dma_chan *dchan = &chan->vchan.chan;
> > > > +       int val, ret;
> > > > +
> > > > +       dev_dbg(chan2dev(dchan), "cookie=%d, starting hwdesc=%x\n",
> > > > +               dchan->cookie, *hwdesc_phys);
> > > > +
> > > > +       val = *hwdesc_phys & DMA_ADDR_MASK;
> > > > +       val |= DMA_START;
> > > > +       val |= dchan->chan_id;
> > > > +       chan_writel(chan, DMA_CTRL, val);
> > > > +       ret = readl_poll_timeout(chan->reg_base + DMA_CTRL, val,
> > > > +                                !(val & DMA_START), 0, 3000);
> > > > +       if (ret)
> > > > +               dev_err(chan2dev(dchan), "failed to start DMA\n");
> > > > +
> > > > +       return ret;
> > > > +}
> > > > +
> > > > +static inline void ls1x_dma_stop(struct ls1x_dma_chan *chan)
> > > > +{
> > > > +       chan_writel(chan, DMA_CTRL, chan_readl(chan, DMA_CTRL) | DMA_STOP);
> > > > +}
> > > > +
> > > > +static void ls1x_dma_free_chan_resources(struct dma_chan *dchan)
> > > > +{
> > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > +
> > > > +       dma_free_coherent(chan2dev(dchan), sizeof(struct ls1x_dma_hwdesc),
> > > > +                         chan->curr_hwdesc, chan->curr_hwdesc_phys);
> > > > +       vchan_free_chan_resources(&chan->vchan);
> > > > +       dma_pool_destroy(chan->desc_pool);
> > > > +       chan->desc_pool = NULL;
> > > > +}
> > > > +
> > > > +static int ls1x_dma_alloc_chan_resources(struct dma_chan *dchan)
> > > > +{
> > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > +
> > > > +       chan->desc_pool = dma_pool_create(dma_chan_name(dchan),
> > > > +                                         chan2dev(dchan),
> > > > +                                         sizeof(struct ls1x_dma_hwdesc),
> > > > +                                         __alignof__(struct ls1x_dma_hwdesc),
> > > > +                                         0);
> > > > +       if (!chan->desc_pool)
> > > > +               return -ENOMEM;
> > > > +
> > > > +       /* allocate memory for querying current HW descriptor */
> > > > +       dma_set_coherent_mask(chan2dev(dchan), DMA_BIT_MASK(32));
> > > > +       chan->curr_hwdesc = dma_alloc_coherent(chan2dev(dchan),
> > > > +                                              sizeof(struct ls1x_dma_hwdesc),
> > > > +                                              &chan->curr_hwdesc_phys,
> > > > +                                              GFP_KERNEL);
> > > > +       if (!chan->curr_hwdesc)
> > > > +               return -ENOMEM;
> > > > +
> > > > +       return 0;
> > > > +}
> > > > +
> > > > +static void ls1x_dma_free_desc(struct virt_dma_desc *vdesc)
> > > > +{
> > > > +       struct ls1x_dma_desc *desc = to_ls1x_dma_desc(vdesc);
> > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(vdesc->tx.chan);
> > > > +
> > > > +       dma_pool_free(chan->desc_pool, desc->hwdesc, desc->hwdesc_phys);
> > > > +       chan->desc = NULL;
> > > > +       kfree(desc);
> > > > +}
> > > > +
> > > > +static struct ls1x_dma_desc *
> > > > +ls1x_dma_alloc_desc(struct dma_chan *dchan, int sg_len,
> > > > +                   enum dma_transfer_direction direction,
> > > > +                   enum dma_transaction_type type)
> > > > +{
> > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > +       struct ls1x_dma_desc *desc;
> > > > +
> > > > +       if (sg_len > LS1X_DMA_MAX_DESC) {
> > > > +               dev_err(chan2dev(dchan), "sg_len %u exceeds limit %lu",
> > > > +                       sg_len, LS1X_DMA_MAX_DESC);
> > > > +               return NULL;
> > > > +       }
> > > > +
> > > > +       desc = kzalloc(sizeof(*desc), GFP_NOWAIT);
> > > > +       if (!desc)
> > > > +               return NULL;
> > > > +
> > > > +       /* allocate HW descriptors */
> > > > +       desc->hwdesc = dma_pool_zalloc(chan->desc_pool, GFP_NOWAIT,
> > > > +                                      &desc->hwdesc_phys);
> > > > +       if (!desc->hwdesc) {
> > > > +               dev_err(chan2dev(dchan), "failed to alloc HW descriptors\n");
> > > > +               ls1x_dma_free_desc(&desc->vdesc);
> > > > +               return NULL;
> > > > +       }
> > > > +
> > > > +       desc->dir = direction;
> > > > +       desc->type = type;
> > > > +       desc->nr_descs = sg_len;
> > > > +
> > > > +       return desc;
> > > > +}
> > > > +
> > > > +static int ls1x_dma_setup_hwdescs(struct dma_chan *dchan,
> > > > +                                 struct ls1x_dma_desc *desc,
> > > > +                                 struct scatterlist *sgl, unsigned int sg_len)
> > > > +{
> > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > +       dma_addr_t next_hwdesc_phys = desc->hwdesc_phys;
> > > > +
> > > > +       struct scatterlist *sg;
> > > > +       unsigned int dev_addr, cmd, i;
> > > > +
> > > > +       switch (desc->dir) {
> > > > +       case DMA_MEM_TO_DEV:
> > > > +               dev_addr = chan->dst_addr;
> > > > +               desc->bus_width = chan->dst_addr_width;
> > > > +               cmd = DMA_RAM2DEV | DMA_INT;
> > > > +               break;
> > > > +       case DMA_DEV_TO_MEM:
> > > > +               dev_addr = chan->src_addr;
> > > > +               desc->bus_width = chan->src_addr_width;
> > > > +               cmd = DMA_INT;
> > > > +               break;
> > > > +       default:
> > > > +               dev_err(chan2dev(dchan), "unsupported DMA direction: %s\n",
> > > > +                       dmaengine_get_direction_text(desc->dir));
> > > > +               return -EINVAL;
> > > > +       }
> > > > +
> > > > +       /* setup HW descriptors */
> > > > +       for_each_sg(sgl, sg, sg_len, i) {
> > > > +               dma_addr_t buf_addr = sg_dma_address(sg);
> > > > +               size_t buf_len = sg_dma_len(sg);
> > > > +               struct ls1x_dma_hwdesc *hwdesc = &desc->hwdesc[i];
> > > > +
> > > > +               if (!is_dma_copy_aligned(dchan->device, buf_addr, 0, buf_len)) {
> > > > +                       dev_err(chan2dev(dchan), "buffer is not aligned!\n");
> > > > +                       return -EINVAL;
> > > > +               }
> > > > +
> > > > +               hwdesc->saddr = buf_addr;
> > > > +               hwdesc->daddr = dev_addr;
> > > > +               hwdesc->length = buf_len / desc->bus_width;
> > > > +               hwdesc->stride = 0;
> > > > +               hwdesc->cycles = 1;
> > > > +               hwdesc->cmd = cmd;
> > > > +
> > > > +               if (i) {
> > > > +                       next_hwdesc_phys += sizeof(*hwdesc);
> > > > +                       desc->hwdesc[i - 1].next = next_hwdesc_phys
> > > > +                           | DMA_NEXT_VALID;
> > > > +               }
> > > > +       }
> > > > +
> > > > +       if (desc->type == DMA_CYCLIC)
> > > > +               desc->hwdesc[i - 1].next = desc->hwdesc_phys | DMA_NEXT_VALID;
> > > > +
> > > > +       for_each_sg(sgl, sg, sg_len, i) {
> > > > +               struct ls1x_dma_hwdesc *hwdesc = &desc->hwdesc[i];
> > > > +
> > > > +               print_hex_dump_debug("HW DESC: ", DUMP_PREFIX_OFFSET, 16, 4,
> > > > +                                    hwdesc, sizeof(*hwdesc), false);
> > > > +       }
> > > > +
> > > > +       return 0;
> > > > +}
> > > > +
> > > > +static struct dma_async_tx_descriptor *
> > > > +ls1x_dma_prep_slave_sg(struct dma_chan *dchan,
> > > > +                      struct scatterlist *sgl, unsigned int sg_len,
> > > > +                      enum dma_transfer_direction direction,
> > > > +                      unsigned long flags, void *context)
> > > > +{
> > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > +       struct ls1x_dma_desc *desc;
> > > > +
> > > > +       dev_dbg(chan2dev(dchan), "sg_len=%u flags=0x%lx dir=%s\n",
> > > > +               sg_len, flags, dmaengine_get_direction_text(direction));
> > > > +
> > > > +       desc = ls1x_dma_alloc_desc(dchan, sg_len, direction, DMA_SLAVE);
> > > > +       if (!desc)
> > > > +               return NULL;
> > > > +
> > > > +       if (ls1x_dma_setup_hwdescs(dchan, desc, sgl, sg_len)) {
> > > > +               ls1x_dma_free_desc(&desc->vdesc);
> > > > +               return NULL;
> > > > +       }
> > > > +
> > > > +       return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
> > > > +}
> > > > +
> > > > +static struct dma_async_tx_descriptor *
> > > > +ls1x_dma_prep_dma_cyclic(struct dma_chan *dchan,
> > > > +                        dma_addr_t buf_addr, size_t buf_len, size_t period_len,
> > > > +                        enum dma_transfer_direction direction,
> > > > +                        unsigned long flags)
> > > > +{
> > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > +       struct ls1x_dma_desc *desc;
> > > > +       struct scatterlist *sgl;
> > > > +       unsigned int sg_len;
> > > > +       unsigned int i;
> > > > +
> > > > +       dev_dbg(chan2dev(dchan),
> > > > +               "buf_len=%d period_len=%zu flags=0x%lx dir=%s\n", buf_len,
> > > > +               period_len, flags, dmaengine_get_direction_text(direction));
> > > > +
> > > > +       sg_len = buf_len / period_len;
> > > > +       desc = ls1x_dma_alloc_desc(dchan, sg_len, direction, DMA_CYCLIC);
> > > > +       if (!desc)
> > > > +               return NULL;
> > > > +
> > > > +       /* allocate the scatterlist */
> > > > +       sgl = kmalloc_array(sg_len, sizeof(*sgl), GFP_NOWAIT);
> > > > +       if (!sgl)
> > > > +               return NULL;
> > > > +
> > > > +       sg_init_table(sgl, sg_len);
> > > > +       for (i = 0; i < sg_len; ++i) {
> > > > +               sg_set_page(&sgl[i], pfn_to_page(PFN_DOWN(buf_addr)),
> > > > +                           period_len, offset_in_page(buf_addr));
> > > > +               sg_dma_address(&sgl[i]) = buf_addr;
> > > > +               sg_dma_len(&sgl[i]) = period_len;
> > > > +               buf_addr += period_len;
> > > > +       }
> > > > +
> > > > +       if (ls1x_dma_setup_hwdescs(dchan, desc, sgl, sg_len)) {
> > > > +               ls1x_dma_free_desc(&desc->vdesc);
> > > > +               return NULL;
> > > > +       }
> > > > +
> > > > +       kfree(sgl);
> > > > +
> > > > +       return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
> > > > +}
> > > > +
> > > > +static int ls1x_dma_slave_config(struct dma_chan *dchan,
> > > > +                                struct dma_slave_config *config)
> > > > +{
> > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > +
> > > > +       chan->src_addr = config->src_addr;
> > > > +       chan->src_addr_width = config->src_addr_width;
> > > > +       chan->dst_addr = config->dst_addr;
> > > > +       chan->dst_addr_width = config->dst_addr_width;
> > > > +
> > > > +       return 0;
> > > > +}
> > > > +
> > > > +static int ls1x_dma_pause(struct dma_chan *dchan)
> > > > +{
> > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > +       unsigned long flags;
> > > > +       int ret;
> > > > +
> > > > +       spin_lock_irqsave(&chan->vchan.lock, flags);
> > > > +       ret = ls1x_dma_query(chan, &chan->curr_hwdesc_phys);
> > > > +       if (!ret)
> > > > +               ls1x_dma_stop(chan);
> > > > +       spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > > > +
> > > > +       return ret;
> > > > +}
> > > > +
> > > > +static int ls1x_dma_resume(struct dma_chan *dchan)
> > > > +{
> > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > +       unsigned long flags;
> > > > +       int ret;
> > > > +
> > > > +       spin_lock_irqsave(&chan->vchan.lock, flags);
> > > > +       ret = ls1x_dma_start(chan, &chan->curr_hwdesc_phys);
> > > > +       spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > > > +
> > > > +       return ret;
> > > > +}
> > > > +
> > > > +static int ls1x_dma_terminate_all(struct dma_chan *dchan)
> > > > +{
> > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > +       unsigned long flags;
> > > > +       LIST_HEAD(head);
> > > > +
> > > > +       spin_lock_irqsave(&chan->vchan.lock, flags);
> > > > +       ls1x_dma_stop(chan);
> > > > +       vchan_get_all_descriptors(&chan->vchan, &head);
> > > > +       spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > > > +
> > > > +       vchan_dma_desc_free_list(&chan->vchan, &head);
> > > > +
> > > > +       return 0;
> > > > +}
> > > > +
> > > > +static enum dma_status ls1x_dma_tx_status(struct dma_chan *dchan,
> > > > +                                         dma_cookie_t cookie,
> > > > +                                         struct dma_tx_state *state)
> > > > +{
> > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > +       struct virt_dma_desc *vdesc;
> > > > +       enum dma_status status;
> > > > +       size_t bytes = 0;
> > > > +       unsigned long flags;
> > > > +
> > > > +       status = dma_cookie_status(dchan, cookie, state);
> > > > +       if (status == DMA_COMPLETE)
> > > > +               return status;
> > > > +
> > > > +       spin_lock_irqsave(&chan->vchan.lock, flags);
> > > > +       vdesc = vchan_find_desc(&chan->vchan, cookie);
> > > > +       if (chan->desc && cookie == chan->desc->vdesc.tx.cookie) {
> > > > +               struct ls1x_dma_desc *desc = chan->desc;
> > > > +               int i;
> > > > +
> > > > +               if (ls1x_dma_query(chan, &chan->curr_hwdesc_phys))
> > > > +                       return status;
> > > > +
> > > > +               /* locate the current HW descriptor */
> > > > +               for (i = 0; i < desc->nr_descs; i++)
> > > > +                       if (desc->hwdesc[i].next == chan->curr_hwdesc->next)
> > > > +                               break;
> > > > +
> > > > +               /* count the residues */
> > > > +               for (; i < desc->nr_descs; i++)
> > > > +                       bytes += desc->hwdesc[i].length * desc->bus_width;
> > > > +
> > > > +               dma_set_residue(state, bytes);
> > > > +       }
> > > > +       spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > > > +
> > > > +       return status;
> > > > +}
> > > > +
> > > > +static void ls1x_dma_issue_pending(struct dma_chan *dchan)
> > > > +{
> > > > +       struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > +       struct virt_dma_desc *vdesc;
> > > > +       unsigned long flags;
> > > > +
> > > > +       spin_lock_irqsave(&chan->vchan.lock, flags);
> > > > +       if (vchan_issue_pending(&chan->vchan) && !chan->desc) {
> > > > +               vdesc = vchan_next_desc(&chan->vchan);
> > > > +               if (!vdesc) {
> > > > +                       chan->desc = NULL;
> > > > +                       return;
> > > > +               }
> > > > +               chan->desc = to_ls1x_dma_desc(vdesc);
> > > > +               ls1x_dma_start(chan, &chan->desc->hwdesc_phys);
> > > > +       }
> > > > +       spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > > > +}
> > > > +
> > > > +static irqreturn_t ls1x_dma_irq_handler(int irq, void *data)
> > > > +{
> > > > +       struct ls1x_dma_chan *chan = data;
> > > > +       struct ls1x_dma_desc *desc = chan->desc;
> > > > +       struct dma_chan *dchan = &chan->vchan.chan;
> > > > +
> > > > +       if (!desc) {
> > > > +               dev_warn(chan2dev(dchan),
> > > > +                        "IRQ %d with no active descriptor on channel %d\n",
> > > > +                        irq, dchan->chan_id);
> > > > +               return IRQ_NONE;
> > > > +       }
> > > > +
> > > > +       dev_dbg(chan2dev(dchan), "DMA IRQ %d on channel %d\n", irq,
> > > > +               dchan->chan_id);
> > > > +
> > > > +       spin_lock(&chan->vchan.lock);
> > > > +
> > > > +       if (desc->type == DMA_CYCLIC) {
> > > > +               vchan_cyclic_callback(&desc->vdesc);
> > > > +       } else {
> > > > +               list_del(&desc->vdesc.node);
> > > > +               vchan_cookie_complete(&desc->vdesc);
> > > > +               chan->desc = NULL;
> > > > +       }
> > > > +
> > > > +       spin_unlock(&chan->vchan.lock);
> > > > +       return IRQ_HANDLED;
> > > > +}
> > > > +
> > > > +static int ls1x_dma_chan_probe(struct platform_device *pdev,
> > > > +                              struct ls1x_dma *dma, int chan_id)
> > > > +{
> > > > +       struct device *dev = &pdev->dev;
> > > > +       struct ls1x_dma_chan *chan = &dma->chan[chan_id];
> > > > +       char pdev_irqname[4];
> > > > +       char *irqname;
> > > > +       int ret;
> > > > +
> > > > +       sprintf(pdev_irqname, "ch%u", chan_id);
> > > > +       chan->irq = platform_get_irq_byname(pdev, pdev_irqname);
> > > > +       if (chan->irq < 0)
> > > > +               return -ENODEV;
> > > > +
> > > > +       irqname = devm_kasprintf(dev, GFP_KERNEL, "%s:%s",
> > > > +                                dev_name(dev), pdev_irqname);
> > > > +       if (!irqname)
> > > > +               return -ENOMEM;
> > > > +
> > > > +       ret = devm_request_irq(dev, chan->irq, ls1x_dma_irq_handler,
> > > > +                              IRQF_SHARED, irqname, chan);
> > > > +       if (ret)
> > > > +               return dev_err_probe(dev, ret,
> > > > +                                    "failed to request IRQ %u!\n", chan->irq);
> > > > +
> > > > +       chan->reg_base = dma->reg_base;
> > > > +       chan->vchan.desc_free = ls1x_dma_free_desc;
> > > > +       vchan_init(&chan->vchan, &dma->ddev);
> > > > +       dev_info(dev, "%s (irq %d) initialized\n", pdev_irqname, chan->irq);
> > > > +
> > > > +       return 0;
> > > > +}
> > > > +
> > > > +static void ls1x_dma_chan_remove(struct ls1x_dma *dma, int chan_id)
> > > > +{
> > > > +       struct device *dev = dma->ddev.dev;
> > > > +       struct ls1x_dma_chan *chan = &dma->chan[chan_id];
> > > > +
> > > > +       devm_free_irq(dev, chan->irq, chan);
> > > > +       list_del(&chan->vchan.chan.device_node);
> > > > +       tasklet_kill(&chan->vchan.task);
> > > > +}
> > > > +
> > > > +static int ls1x_dma_probe(struct platform_device *pdev)
> > > > +{
> > > > +       struct device *dev = &pdev->dev;
> > > > +       struct dma_device *ddev;
> > > > +       struct ls1x_dma *dma;
> > > > +       int nr_chans, ret, i;
> > > > +
> > > > +       nr_chans = platform_irq_count(pdev);
> > > > +       if (nr_chans <= 0)
> > > > +               return nr_chans;
> > > > +       if (nr_chans > LS1X_DMA_MAX_CHANNELS)
> > > > +               return dev_err_probe(dev, -EINVAL,
> > > > +                                    "nr_chans=%d exceeds the maximum\n",
> > > > +                                    nr_chans);
> > > > +
> > > > +       dma = devm_kzalloc(dev, struct_size(dma, chan, nr_chans), GFP_KERNEL);
> > > > +       if (!dma)
> > > > +               return -ENOMEM;
> > > > +
> > > > +       /* initialize DMA device */
> > > > +       dma->reg_base = devm_platform_ioremap_resource(pdev, 0);
> > > > +       if (IS_ERR(dma->reg_base))
> > > > +               return PTR_ERR(dma->reg_base);
> > > > +
> > > > +       ddev = &dma->ddev;
> > > > +       ddev->dev = dev;
> > > > +       ddev->copy_align = DMAENGINE_ALIGN_4_BYTES;
> > > > +       ddev->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> > > > +           BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
> > > > +       ddev->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> > > > +           BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
> > > > +       ddev->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
> > > > +       ddev->max_sg_burst = LS1X_DMA_MAX_DESC;
> > > > +       ddev->residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
> > > > +       ddev->device_alloc_chan_resources = ls1x_dma_alloc_chan_resources;
> > > > +       ddev->device_free_chan_resources = ls1x_dma_free_chan_resources;
> > > > +       ddev->device_prep_slave_sg = ls1x_dma_prep_slave_sg;
> > > > +       ddev->device_prep_dma_cyclic = ls1x_dma_prep_dma_cyclic;
> > > > +       ddev->device_config = ls1x_dma_slave_config;
> > > > +       ddev->device_pause = ls1x_dma_pause;
> > > > +       ddev->device_resume = ls1x_dma_resume;
> > > > +       ddev->device_terminate_all = ls1x_dma_terminate_all;
> > > > +       ddev->device_tx_status = ls1x_dma_tx_status;
> > > > +       ddev->device_issue_pending = ls1x_dma_issue_pending;
> > > > +
> > > > +       dma_cap_set(DMA_SLAVE, ddev->cap_mask);
> > > > +       INIT_LIST_HEAD(&ddev->channels);
> > > > +
> > > > +       /* initialize DMA channels */
> > > > +       for (i = 0; i < nr_chans; i++) {
> > > > +               ret = ls1x_dma_chan_probe(pdev, dma, i);
> > > > +               if (ret)
> > > > +                       return ret;
> > > > +       }
> > > > +       dma->nr_chans = nr_chans;
> > > > +
> > > > +       ret = dmaenginem_async_device_register(ddev);
> > > > +       if (ret) {
> > > > +               dev_err(dev, "failed to register DMA device! %d\n", ret);
> > > > +               return ret;
> > > > +       }
> > > > +
> > > > +       ret =
> > > > +           of_dma_controller_register(dev->of_node, of_dma_xlate_by_chan_id,
> > > > +                                      ddev);
> > > > +       if (ret) {
> > > > +               dev_err(dev, "failed to register DMA controller! %d\n", ret);
> > > > +               return ret;
> > > > +       }
> > > > +
> > > > +       platform_set_drvdata(pdev, dma);
> > > > +       dev_info(dev, "Loongson1 DMA driver registered\n");
> > > > +
> > > > +       return 0;
> > > > +}
> > > > +
> > > > +static void ls1x_dma_remove(struct platform_device *pdev)
> > > > +{
> > > > +       struct ls1x_dma *dma = platform_get_drvdata(pdev);
> > > > +       int i;
> > > > +
> > > > +       of_dma_controller_free(pdev->dev.of_node);
> > > > +
> > > > +       for (i = 0; i < dma->nr_chans; i++)
> > > > +               ls1x_dma_chan_remove(dma, i);
> > > > +}
> > > > +
> > > > +static const struct of_device_id ls1x_dma_match[] = {
> > > > +       { .compatible = "loongson,ls1b-apbdma" },
> > > > +       { .compatible = "loongson,ls1c-apbdma" },
> > > > +       { /* sentinel */ }
> > > > +};
> > > > +MODULE_DEVICE_TABLE(of, ls1x_dma_match);
> > > > +
> > > > +static struct platform_driver ls1x_dma_driver = {
> > > > +       .probe = ls1x_dma_probe,
> > > > +       .remove_new = ls1x_dma_remove,
> > > > +       .driver = {
> > > > +               .name = KBUILD_MODNAME,
> > > > +               .of_match_table = ls1x_dma_match,
> > > > +       },
> > > > +};
> > > > +
> > > > +module_platform_driver(ls1x_dma_driver);
> > > > +
> > > > +MODULE_AUTHOR("Keguang Zhang <keguang.zhang@gmail.com>");
> > > > +MODULE_DESCRIPTION("Loongson-1 APB DMA Controller driver");
> > > > +MODULE_LICENSE("GPL");
> > > >
> > > > --
> > > > 2.40.1
> > > >
> > > >
> >
> >
> >
> > --
> > Best regards,
> >
> > Keguang Zhang
> >



-- 
Best regards,

Keguang Zhang

^ permalink raw reply

* [PATCH v3] dt-bindings: PCI: ti,j721e-pci-host: Add device-id for TI's J784S4 SoC
From: Siddharth Vadapalli @ 2024-04-01 11:09 UTC (permalink / raw)
  To: lpieralisi, kw, robh, bhelgaas, krzysztof.kozlowski+dt, conor+dt,
	kishon
  Cc: linux-pci, devicetree, linux-kernel, linux-arm-kernel, srk,
	s-vadapalli

Add the device-id of 0xb012 for the PCIe controller on the J784S4 SoC as
described in the CTRL_MMR_PCI_DEVICE_ID register's PCI_DEVICE_ID_DEVICE_ID
field. The Register descriptions and the Technical Reference Manual for
J784S4 SoC can be found at: https://www.ti.com/lit/zip/spruj52

Signed-off-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Acked-by: Rob Herring <robh@kernel.org>
---

This patch is based on linux-next tagged next-20240328.
v2:
https://patchwork.kernel.org/project/linux-arm-kernel/patch/20240115055236.1840255-1-s-vadapalli@ti.com/
Changes since v2:
- Rebased on next-20240328.

Regards,
Siddharth.

 Documentation/devicetree/bindings/pci/ti,j721e-pci-host.yaml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/pci/ti,j721e-pci-host.yaml b/Documentation/devicetree/bindings/pci/ti,j721e-pci-host.yaml
index b7a534cef24d..0b1f21570ed0 100644
--- a/Documentation/devicetree/bindings/pci/ti,j721e-pci-host.yaml
+++ b/Documentation/devicetree/bindings/pci/ti,j721e-pci-host.yaml
@@ -68,6 +68,7 @@ properties:
       - 0xb00d
       - 0xb00f
       - 0xb010
+      - 0xb012
       - 0xb013
 
   msi-map: true
-- 
2.40.1


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox