[PATCH v2 0/8] Add Sunplus SP7021 SoC Support

STALE1770d

Revision v2 of 19 in this series.

58 messages, 6 authors, 2021-11-12 · open the first message on its own page

[PATCH v2 0/8] Add Sunplus SP7021 SoC Support

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-10-29 09:11:47

This patch series add Sunplus SP7021 SoC support.

Sunplus SP7021 is an ARM Cortex A7 (4 cores) based SoC. It integrates many
peripherals (ex: UART, I2C, SPI, SDIO, eMMC, USB, SD card and etc.) into a
single chip. It is designed for industrial control.

SP7021 consists of two chips (dies) in a package. One is called C-chip
(computing chip). It is a 4-core ARM Cortex A7 CPU. It adopts high-level
process (22 nm) for high performance computing. The other is called P-
chip (peripheral chip). It has many peripherals and an ARM A926 added
especially for real-time control. P-chip is made for customers. It adopts
low-level process (ex: 0.11 um) to reduce cost.

Refer to (for documentations):
https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview

Refer to (applications):
https://tibbo.com/store/plus1.html

Refer to (applications):
http://www.sinovoip.com.cn/ecp_view.asp?id=586

Changes in v2:
- sunplus,sp7021-intc.yaml: add descrption for "#interrupt-cells", interrupts
- sunplus,sp7021-intc.yaml: drop "ext0-mask"/"ext1-mask" from DT
- sunplus,sp7021-intc.yaml: fix example.dt too long error
- irq-sp7021-intc.c: major rewrite
- all files with dual license

Qin Jian (8):
  dt-bindings: vendor-prefixes: Add Sunplus
  dt-bindings: arm: sunplus: Add bindings for Sunplus SP7021 SoC boards
  dt-bindings: reset: Add bindings for SP7021 reset driver
  reset: Add Sunplus SP7021 reset driver
  dt-bindings: clock: Add bindings for SP7021 clock driver
  clk: Add Sunplus SP7021 clock driver
  dt-bindings: interrupt-controller: Add bindings for SP7021 interrupt
    controller
  irqchip: Add support for Sunplus SP7021 interrupt controller

 .../bindings/arm/sunplus,sp7021.yaml          |  27 +
 .../bindings/clock/sunplus,sp7021-clkc.yaml   |  38 +
 .../sunplus,sp7021-intc.yaml                  |  59 ++
 .../bindings/reset/sunplus,reset.yaml         |  40 +
 .../devicetree/bindings/vendor-prefixes.yaml  |   2 +
 MAINTAINERS                                   |  16 +
 drivers/clk/Kconfig                           |   8 +
 drivers/clk/Makefile                          |   1 +
 drivers/clk/clk-sp7021.c                      | 770 ++++++++++++++++++
 drivers/irqchip/Kconfig                       |   9 +
 drivers/irqchip/Makefile                      |   1 +
 drivers/irqchip/irq-sp7021-intc.c             | 324 ++++++++
 drivers/reset/Kconfig                         |   9 +
 drivers/reset/Makefile                        |   1 +
 drivers/reset/reset-sunplus.c                 | 159 ++++
 include/dt-bindings/clock/sp-sp7021.h         | 112 +++
 .../interrupt-controller/sp7021-intc.h        |  24 +
 include/dt-bindings/reset/sp-sp7021.h         |  99 +++
 18 files changed, 1699 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
 create mode 100644 Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml
 create mode 100644 Documentation/devicetree/bindings/reset/sunplus,reset.yaml
 create mode 100644 drivers/clk/clk-sp7021.c
 create mode 100644 drivers/irqchip/irq-sp7021-intc.c
 create mode 100644 drivers/reset/reset-sunplus.c
 create mode 100644 include/dt-bindings/clock/sp-sp7021.h
 create mode 100644 include/dt-bindings/interrupt-controller/sp7021-intc.h
 create mode 100644 include/dt-bindings/reset/sp-sp7021.h

--
2.33.1

[PATCH v2 2/8] dt-bindings: arm: sunplus: Add bindings for Sunplus SP7021 SoC boards

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-10-29 09:10:38

This introduces bindings for boards based Sunplus SP7021 SoC.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 .../bindings/arm/sunplus,sp7021.yaml          | 27 +++++++++++++++++++
 MAINTAINERS                                   |  7 +++++
 2 files changed, 34 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
diff --git a/Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml b/Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
new file mode 100644
index 000000000..5b9985b73
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) Sunplus Co., Ltd. 2021
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/arm/sunplus,sp7021.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sunplus SP7021 Boards Device Tree Bindings
+
+maintainers:
+  - qinjian <qinjian@cqplus1.com>
+
+description: |
+  ARM platforms using Sunplus SP7021, an ARM Cortex A7 (4-cores) based SoC.
+  Wiki: https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
+
+properties:
+  $nodename:
+    const: '/'
+  compatible:
+    oneOf:
+      - items:
+          - const: sunplus,sp7021-achip
+
+additionalProperties: true
+
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index e0bca0de0..6a5422f10 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2655,6 +2655,13 @@ F:	drivers/clocksource/armv7m_systick.c
 N:	stm32
 N:	stm
 
+ARM/SUNPLUS SP7021 SOC SUPPORT
+M:	Qin Jian <qinjian@cqplus1.com>
+L:	linux-arm-kernel@lists.infradead.org (moderated for mon-subscribers)
+S:	Maintained
+W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
+F:	Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
+
 ARM/Synaptics SoC support
 M:	Jisheng Zhang <Jisheng.Zhang@synaptics.com>
 M:	Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
-- 
2.33.1

[PATCH v2 8/8] irqchip: Add support for Sunplus SP7021 interrupt controller

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-10-29 09:10:39

Add interrupt driver for Sunplus SP7021 SoC.

This is the interrupt controller in P chip which collects all interrupt
sources in P-chip and routes them to C-chip. C-chip adopts ARM CA7
interrupt controller (compitable = "arm,cortex-a7-gic"). It is a parent
interrupt controller of P-chip interrupt controller.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 MAINTAINERS                       |   1 +
 drivers/irqchip/Kconfig           |   9 +
 drivers/irqchip/Makefile          |   1 +
 drivers/irqchip/irq-sp7021-intc.c | 324 ++++++++++++++++++++++++++++++
 4 files changed, 335 insertions(+)
 create mode 100644 drivers/irqchip/irq-sp7021-intc.c
diff --git a/MAINTAINERS b/MAINTAINERS
index be0334d6a..bfa891d86 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2665,6 +2665,7 @@ F:	Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
 F:	Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml
 F:	Documentation/devicetree/bindings/reset/sunplus,reset.yaml
 F:	drivers/clk/clk-sp7021.c
+F:	drivers/irqchip/irq-sp7021-intc.c
 F:	drivers/reset/reset-sunplus.c
 F:	include/dt-bindings/clock/sp-sp7021.h
 F:	include/dt-bindings/interrupt-controller/sp7021-intc.h
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index aca7b595c..8a58dfb88 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -602,4 +602,13 @@ config APPLE_AIC
 	  Support for the Apple Interrupt Controller found on Apple Silicon SoCs,
 	  such as the M1.
 
+config SUNPLUS_SP7021_INTC
+	bool "Sunplus SP7021 interrupt controller"
+	default SOC_SP7021
+	help
+	  Support for the Sunplus SP7021 Interrupt Controller IP core.
+	  This is used as a primary controller with SP7021 ChipP and can also
+	  be used as a secondary chained controller on SP7021 ChipC.
+	  This is selected automatically by platform config.
+
 endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index f88cbf36a..75411f654 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -116,3 +116,4 @@ obj-$(CONFIG_MACH_REALTEK_RTL)		+= irq-realtek-rtl.o
 obj-$(CONFIG_WPCM450_AIC)		+= irq-wpcm450-aic.o
 obj-$(CONFIG_IRQ_IDT3243X)		+= irq-idt3243x.o
 obj-$(CONFIG_APPLE_AIC)			+= irq-apple-aic.o
+obj-$(CONFIG_SUNPLUS_SP7021_INTC)	+= irq-sp7021-intc.o
diff --git a/drivers/irqchip/irq-sp7021-intc.c b/drivers/irqchip/irq-sp7021-intc.c
new file mode 100644
index 000000000..3431ec746
--- /dev/null
+++ b/drivers/irqchip/irq-sp7021-intc.c
@@ -0,0 +1,324 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/*
+ * Copyright (C) Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/io.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#include <asm/exception.h>
+#include <dt-bindings/interrupt-controller/sp7021-intc.h>
+
+#define SP_INTC_HWIRQ_MIN     0
+#define SP_INTC_HWIRQ_MAX     223
+
+/* Interrupt G0/G1 offset */
+#define INTR_REG_SIZE		(7 * 4)
+
+#define G0_INTR_TYPE		(0)
+#define G0_INTR_POLARITY	(G0_INTR_TYPE + INTR_REG_SIZE)
+#define G0_INTR_PRIORITY	(G0_INTR_POLARITY + INTR_REG_SIZE)
+#define G0_INTR_MASK		(G0_INTR_PRIORITY + INTR_REG_SIZE)
+
+#define G1_INTR_CLR			(0)
+#define G1_MASKED_EXT1		(G1_INTR_CLR + INTR_REG_SIZE)
+#define G1_MASKED_EXT0		(G1_MASKED_EXT1 + INTR_REG_SIZE)
+#define G1_INTR_GROUP		(31 * 4)
+#define G1_INTR_MASK		(0x7F)
+#define G1_EXT1_SHIFT		(0)
+#define G1_EXT0_SHIFT		(8)
+
+static struct sp_intctl {
+	void __iomem *g0;
+	void __iomem *g1;
+	struct irq_domain *domain;
+	struct device_node *node;
+	raw_spinlock_t lock;
+	int virq[2];
+} sp_intc;
+
+/* GPIO INT EDGE BUG WORKAROUND */
+#define GPIO_INT0_HWIRQ			120
+#define GPIO_INT7_HWIRQ			127
+#define GPIO_INT_EDGE_ACTIVE	BIT(31)
+#define IS_GPIO_INT(n)	(((n) >= GPIO_INT0_HWIRQ) && ((n) <= GPIO_INT7_HWIRQ))
+/* array to hold which interrupt needs to workaround the bug
+ * INT_TYPE_NONE: no need
+ * INT_TYPE_EDGE_FALLING/INT_TYPE_EDGE_RISING: need to workaround
+ * GPIO_INT_EDGE_ACTIVING: workaround is on going
+ */
+static u32 edge_trigger[SP_INTC_HWIRQ_MAX];
+
+static struct irq_chip sp_intc_chip;
+
+static void sp_intc_assign_bit(u32 hwirq, void __iomem *base, u32 value)
+{
+	u32 offset, mask;
+	unsigned long flags;
+	void __iomem *reg;
+
+	offset = (hwirq / 32) * 4;
+	reg = base + offset;
+
+	raw_spin_lock_irqsave(&sp_intc.lock, flags);
+	mask = readl_relaxed(reg);
+	if (value)
+		mask |= BIT(hwirq % 32);
+	else
+		mask &= ~BIT(hwirq % 32);
+	writel_relaxed(mask, reg);
+	raw_spin_unlock_irqrestore(&sp_intc.lock, flags);
+}
+
+static void sp_intc_ack_irq(struct irq_data *d)
+{
+	u32 hwirq = d->hwirq;
+
+	if (edge_trigger[hwirq] != IRQ_TYPE_NONE) {
+		sp_intc_assign_bit(hwirq, sp_intc.g0 + G0_INTR_POLARITY,
+			(edge_trigger[hwirq] == IRQ_TYPE_EDGE_RISING));
+		edge_trigger[hwirq] |= GPIO_INT_EDGE_ACTIVE;
+	}
+
+	sp_intc_assign_bit(hwirq, sp_intc.g1 + G1_INTR_CLR, 1);
+}
+
+static void sp_intc_mask_irq(struct irq_data *d)
+{
+	sp_intc_assign_bit(d->hwirq, sp_intc.g0 + G0_INTR_MASK, 0);
+}
+
+static void sp_intc_unmask_irq(struct irq_data *d)
+{
+	sp_intc_assign_bit(d->hwirq, sp_intc.g0 + G0_INTR_MASK, 1);
+}
+
+static void sp_intc_set_priority(u32 hwirq, u32 priority)
+{
+	sp_intc_assign_bit(hwirq, sp_intc.g0 + G0_INTR_PRIORITY, priority);
+}
+
+static int sp_intc_set_type(struct irq_data *d, unsigned int type)
+{
+	u32 intr_type;		/* 0: level			1: edge */
+	u32 intr_polarity;	/* 0: high/rising	1: low/falling */
+	u32 hwirq = d->hwirq;
+
+	/* update the chip/handler */
+	if (type & IRQ_TYPE_LEVEL_MASK)
+		irq_set_chip_handler_name_locked(d, &sp_intc_chip,
+						   handle_level_irq, NULL);
+	else
+		irq_set_chip_handler_name_locked(d, &sp_intc_chip,
+						   handle_edge_irq, NULL);
+
+	edge_trigger[hwirq] = IRQ_TYPE_NONE;
+
+	if (type & IRQ_TYPE_LEVEL_MASK)
+		intr_type = 0;
+	else if (IS_GPIO_INT(hwirq)) {
+		intr_type = 0;
+		edge_trigger[hwirq] = type;
+	} else
+		intr_type = 1;
+
+	intr_polarity = (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_EDGE_FALLING);
+
+	sp_intc_assign_bit(hwirq, sp_intc.g0 + G0_INTR_TYPE, intr_type);
+	sp_intc_assign_bit(hwirq, sp_intc.g0 + G0_INTR_POLARITY, intr_polarity);
+
+	return IRQ_SET_MASK_OK;
+}
+
+static int sp_intc_get_ext_irq(int ext_num)
+{
+	u32 hwirq, mask;
+	u32 i;
+
+	i = readl_relaxed(sp_intc.g1 + G1_INTR_GROUP);
+	if (ext_num)
+		mask = (i >> G1_EXT1_SHIFT) & G1_INTR_MASK;
+	else
+		mask = (i >> G1_EXT0_SHIFT) & G1_INTR_MASK;
+	if (mask) {
+		i = fls(mask) - 1;
+		if (ext_num)
+			mask = readl_relaxed(sp_intc.g1 + G1_MASKED_EXT1 + i * 4);
+		else
+			mask = readl_relaxed(sp_intc.g1 + G1_MASKED_EXT0 + i * 4);
+		if (mask) {
+			hwirq = (i << 5) + fls(mask) - 1;
+			return hwirq;
+		}
+	}
+	return -1; /* No interrupt */
+}
+
+static void sp_intc_handle_ext_cascaded(struct irq_desc *desc)
+{
+	struct irq_chip *host_chip = irq_desc_get_chip(desc);
+	int ext_num = (int)irq_desc_get_handler_data(desc);
+	int hwirq;
+
+	chained_irq_enter(host_chip, desc);
+
+	while ((hwirq = sp_intc_get_ext_irq(ext_num)) >= 0) {
+		if (edge_trigger[hwirq] & GPIO_INT_EDGE_ACTIVE) {
+			edge_trigger[hwirq] &= ~GPIO_INT_EDGE_ACTIVE;
+			sp_intc_assign_bit(hwirq, sp_intc.g0 + G0_INTR_POLARITY,
+				(edge_trigger[hwirq] != IRQ_TYPE_EDGE_RISING));
+		} else
+			generic_handle_domain_irq(sp_intc.domain, hwirq);
+	}
+
+	chained_irq_exit(host_chip, desc);
+}
+
+static void __exception_irq_entry sp_intc_handle_irq(struct pt_regs *regs)
+{
+	int hwirq;
+
+	while ((hwirq = sp_intc_get_ext_irq(0)) >= 0)
+		generic_handle_domain_irq(sp_intc.domain, hwirq);
+}
+
+static void __init sp_intc_chip_init(void __iomem *base0, void __iomem *base1)
+{
+	int i;
+
+	sp_intc.g0 = base0;
+	sp_intc.g1 = base1;
+
+	for (i = 0; i < 7; i++) {
+		/* all mask */
+		writel_relaxed(0, sp_intc.g0 + G0_INTR_MASK + i * 4);
+		/* all edge */
+		writel_relaxed(~0, sp_intc.g0 + G0_INTR_TYPE + i * 4);
+		/* all high-active */
+		writel_relaxed(0, sp_intc.g0 + G0_INTR_POLARITY + i * 4);
+		/* all irq */
+		writel_relaxed(~0, sp_intc.g0 + G0_INTR_PRIORITY + i * 4);
+		/* all clear */
+		writel_relaxed(~0, sp_intc.g1 + G1_INTR_CLR + i * 4);
+	}
+}
+
+int sp_intc_xlate_of(struct irq_domain *d, struct device_node *node,
+			  const u32 *intspec, unsigned int intsize,
+			  irq_hw_number_t *out_hwirq, unsigned int *out_type)
+{
+	int ret;
+
+	ret = irq_domain_xlate_twocell(d, node,
+		intspec, intsize, out_hwirq, out_type);
+	if (!ret) {
+		/* intspec[1]: IRQ_TYPE | SP_INTC_EXT_INT
+		 * SP_INTC_EXT_INT: 0-1,
+		 *   to indicate route to which parent irq: EXT_INT0/EXT_INT1
+		 */
+		u32 ext_int = (intspec[1] & SP_INTC_EXT_INT_MASK) >> SP_INTC_EXT_INT_SHFIT;
+
+		/* priority = 0, route to EXT_INT1
+		 *    otherwise, route to EXT_INT0
+		 */
+		sp_intc_set_priority(*out_hwirq, 1 - ext_int);
+	}
+
+	return ret;
+}
+
+static struct irq_chip sp_intc_chip = {
+	.name = "sp_intc",
+	.irq_ack = sp_intc_ack_irq,
+	.irq_mask = sp_intc_mask_irq,
+	.irq_unmask = sp_intc_unmask_irq,
+	.irq_set_type = sp_intc_set_type,
+};
+
+static int sp_intc_irq_domain_map(struct irq_domain *domain,
+	unsigned int irq, irq_hw_number_t hwirq)
+{
+	irq_set_chip_and_handler(irq, &sp_intc_chip, handle_level_irq);
+	irq_set_chip_data(irq, &sp_intc_chip);
+	irq_set_noprobe(irq);
+
+	return 0;
+}
+
+static const struct irq_domain_ops sp_intc_dm_ops = {
+	.xlate = sp_intc_xlate_of,
+	.map = sp_intc_irq_domain_map,
+};
+
+#ifdef CONFIG_OF
+static int sp_intc_irq_map(struct device_node *node, int i)
+{
+	sp_intc.virq[i] = irq_of_parse_and_map(node, i);
+	if (!sp_intc.virq[i]) {
+		pr_err("%s: missed EXT_INT%d in DT\n", __func__, i);
+		return -ENOENT;
+	}
+
+	pr_info("%s: EXT_INT%d = %d\n", __func__, i, sp_intc.virq[i]);
+	irq_set_chained_handler_and_data(sp_intc.virq[i],
+		sp_intc_handle_ext_cascaded, (void *)i);
+
+	return 0;
+}
+
+int __init sp_intc_init_dt(
+	struct device_node *node, struct device_node *parent)
+{
+	void __iomem *base0, *base1;
+
+	base0 = of_iomap(node, 0);
+	if (!base0) {
+		pr_err("unable to map sp-intc base 0\n");
+		return -EINVAL;
+	}
+
+	base1 = of_iomap(node, 1);
+	if (!base1) {
+		pr_err("unable to map sp-intc base 1\n");
+		return -EINVAL;
+	}
+
+	sp_intc.node = node;
+
+	sp_intc_chip_init(base0, base1);
+
+	sp_intc.domain = irq_domain_add_linear(node,
+			SP_INTC_HWIRQ_MAX - SP_INTC_HWIRQ_MIN,
+			&sp_intc_dm_ops, &sp_intc);
+	if (!sp_intc.domain) {
+		pr_err("%s: unable to create linear domain\n", __func__);
+		return -EINVAL;
+	}
+
+	raw_spin_lock_init(&sp_intc.lock);
+
+	if (parent) {
+		/* secondary chained controller */
+		if (sp_intc_irq_map(node, 0)) // EXT_INT0
+			return -ENOENT;
+
+		if (sp_intc_irq_map(node, 1)) // EXT_INT1
+			return -ENOENT;
+	} else {
+		/* primary controller */
+		set_handle_irq(sp_intc_handle_irq);
+	}
+
+	return 0;
+}
+IRQCHIP_DECLARE(sp_intc, "sunplus,sp7021-intc", sp_intc_init_dt);
+#endif
+
+MODULE_AUTHOR("Qin Jian <qinjian@cqplus1.com>");
+MODULE_DESCRIPTION("Sunplus SP7021 Interrupt Controller Driver");
+MODULE_LICENSE("GPL v2");
-- 
2.33.1

[PATCH v2 4/8] reset: Add Sunplus SP7021 reset driver

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-10-29 09:11:01

Add reset driver for Sunplus SP7021 SoC.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 MAINTAINERS                   |   1 +
 drivers/reset/Kconfig         |   9 ++
 drivers/reset/Makefile        |   1 +
 drivers/reset/reset-sunplus.c | 159 ++++++++++++++++++++++++++++++++++
 4 files changed, 170 insertions(+)
 create mode 100644 drivers/reset/reset-sunplus.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 652f42cab..6caffd6d0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2662,6 +2662,7 @@ S:	Maintained
 W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
 F:	Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
 F:	Documentation/devicetree/bindings/reset/sunplus,reset.yaml
+F:	drivers/reset/reset-sunplus.c
 F:	include/dt-bindings/reset/sp-sp7021.h
 
 ARM/Synaptics SoC support
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index be799a5ab..1aec3c8af 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -249,6 +249,15 @@ config RESET_TI_SYSCON
 	  you wish to use the reset framework for such memory-mapped devices,
 	  say Y here. Otherwise, say N.
 
+config RESET_SUNPLUS
+	bool "Sunplus SoCs Reset Driver"
+	default ARCH_PENTAGRAM
+	help
+	  This enables the reset driver support for Sunplus SP7021 SoC family.
+	  Say Y if you want to control reset signals by the reset controller.
+	  Otherwise, say N.
+	  This driver is selected automatically by platform config.
+
 config RESET_UNIPHIER
 	tristate "Reset controller driver for UniPhier SoCs"
 	depends on ARCH_UNIPHIER || COMPILE_TEST
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 21d46d886..f03403e97 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o
 obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
 obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
 obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
+obj-$(CONFIG_RESET_SUNPLUS) += reset-sunplus.o
 obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
 obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o
 obj-$(CONFIG_RESET_TI_SYSCON) += reset-ti-syscon.o
diff --git a/drivers/reset/reset-sunplus.c b/drivers/reset/reset-sunplus.c
new file mode 100644
index 000000000..696efd75e
--- /dev/null
+++ b/drivers/reset/reset-sunplus.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/*
+ * SP7021 reset driver
+ *
+ * Copyright (C) Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/reboot.h>
+
+#if defined(CONFIG_SOC_SP7021)
+#include <dt-bindings/reset/sp-sp7021.h>
+#elif defined(CONFIG_SOC_Q645)
+#include <dt-bindings/reset/sp-q645.h>
+#endif
+
+#define BITASSERT(id, val)          ((1 << (16 + id)) | (val << id))
+
+
+struct sp_reset_data {
+	struct reset_controller_dev	rcdev;
+	void __iomem			*membase;
+} sp_reset;
+
+
+static inline struct sp_reset_data *
+to_sp_reset_data(struct reset_controller_dev *rcdev)
+{
+	return container_of(rcdev, struct sp_reset_data, rcdev);
+}
+
+static int sp_reset_update(struct reset_controller_dev *rcdev,
+			      unsigned long id, bool assert)
+{
+	struct sp_reset_data *data = to_sp_reset_data(rcdev);
+	int reg_width = sizeof(u32)/2;
+	int bank = id / (reg_width * BITS_PER_BYTE);
+	int offset = id % (reg_width * BITS_PER_BYTE);
+	void __iomem *addr;
+
+	addr = data->membase + (bank * 4);
+
+	if (assert)
+		writel(BITASSERT(offset, 1), addr);
+	else
+		writel(BITASSERT(offset, 0), addr);
+
+	return 0;
+}
+
+static int sp_reset_assert(struct reset_controller_dev *rcdev,
+			      unsigned long id)
+{
+	return sp_reset_update(rcdev, id, true);
+}
+
+
+static int sp_reset_deassert(struct reset_controller_dev *rcdev,
+				unsigned long id)
+{
+	return sp_reset_update(rcdev, id, false);
+}
+
+static int sp_reset_status(struct reset_controller_dev *rcdev,
+			      unsigned long id)
+{
+	struct sp_reset_data *data = to_sp_reset_data(rcdev);
+	int reg_width = sizeof(u32)/2;
+	int bank = id / (reg_width * BITS_PER_BYTE);
+	int offset = id % (reg_width * BITS_PER_BYTE);
+	u32 reg;
+
+	reg = readl(data->membase + (bank * 4));
+
+	return !!(reg & BIT(offset));
+}
+
+static int sp_restart(struct notifier_block *this, unsigned long mode,
+				void *cmd)
+{
+	sp_reset_assert(&sp_reset.rcdev, RST_SYSTEM);
+	sp_reset_deassert(&sp_reset.rcdev, RST_SYSTEM);
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block sp_restart_nb = {
+	.notifier_call = sp_restart,
+	.priority = 192,
+};
+
+static const struct reset_control_ops sp_reset_ops = {
+	.assert		= sp_reset_assert,
+	.deassert	= sp_reset_deassert,
+	.status		= sp_reset_status,
+};
+
+static const struct of_device_id sp_reset_dt_ids[] = {
+	{ .compatible = "sunplus,sp7021-reset", },
+	{ .compatible = "sunplus,q645-reset", },
+	{ /* sentinel */ },
+};
+
+static int sp_reset_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct sp_reset_data *data = &sp_reset;
+	void __iomem *membase;
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	membase = devm_ioremap(dev, res->start, resource_size(res));
+	if (IS_ERR(membase))
+		return PTR_ERR(membase);
+
+	data->membase = membase;
+	data->rcdev.owner = THIS_MODULE;
+	data->rcdev.nr_resets = RST_MAX;
+	data->rcdev.ops = &sp_reset_ops;
+	data->rcdev.of_node = dev->of_node;
+	register_restart_handler(&sp_restart_nb);
+
+	return devm_reset_controller_register(dev, &data->rcdev);
+}
+
+static struct platform_driver sp_reset_driver = {
+	.probe	= sp_reset_probe,
+	.driver = {
+		.name = "sunplus-reset",
+		.of_match_table	= sp_reset_dt_ids,
+	},
+};
+
+static int __init sp_reset_init(void)
+{
+	return platform_driver_register(&sp_reset_driver);
+}
+arch_initcall(sp_reset_init);
+
+MODULE_AUTHOR("Edwin Chiu <edwin.chiu@sunplus.com>");
+MODULE_DESCRIPTION("Sunplus Reset Driver");
+MODULE_LICENSE("GPL");
-- 
2.33.1

[PATCH v2 6/8] clk: Add Sunplus SP7021 clock driver

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-10-29 09:11:07

Add clock driver for Sunplus SP7021 SoC.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 MAINTAINERS              |   1 +
 drivers/clk/Kconfig      |   8 +
 drivers/clk/Makefile     |   1 +
 drivers/clk/clk-sp7021.c | 770 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 780 insertions(+)
 create mode 100644 drivers/clk/clk-sp7021.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 90ebb823f..5069f552f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2663,6 +2663,7 @@ W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
 F:	Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
 F:	Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
 F:	Documentation/devicetree/bindings/reset/sunplus,reset.yaml
+F:	drivers/clk/clk-sp7021.c
 F:	drivers/reset/reset-sunplus.c
 F:	include/dt-bindings/clock/sp-sp7021.h
 F:	include/dt-bindings/reset/sp-sp7021.h
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index c5b3dc973..e2494c2aa 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -334,6 +334,14 @@ config COMMON_CLK_VC5
 	  This driver supports the IDT VersaClock 5 and VersaClock 6
 	  programmable clock generators.
 
+config COMMON_CLK_SP7021
+	def_bool OF && COMMON_CLK && SOC_SP7021
+	help
+	  This driver supports the Sunplus SP7021 SoC clocks.
+	  It implemented SP7021 PLLs/gate.
+	  Not all features of the PLL are currently supported
+	  by the driver.
+
 config COMMON_CLK_STM32MP157
 	def_bool COMMON_CLK && MACH_STM32MP157
 	help
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index e42312121..f15bb5070 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_COMMON_CLK_SI5351)		+= clk-si5351.o
 obj-$(CONFIG_COMMON_CLK_SI514)		+= clk-si514.o
 obj-$(CONFIG_COMMON_CLK_SI544)		+= clk-si544.o
 obj-$(CONFIG_COMMON_CLK_SI570)		+= clk-si570.o
+obj-$(CONFIG_COMMON_CLK_SP7021)		+= clk-sp7021.o
 obj-$(CONFIG_COMMON_CLK_STM32F)		+= clk-stm32f4.o
 obj-$(CONFIG_COMMON_CLK_STM32H7)	+= clk-stm32h7.o
 obj-$(CONFIG_COMMON_CLK_STM32MP157)	+= clk-stm32mp1.o
diff --git a/drivers/clk/clk-sp7021.c b/drivers/clk/clk-sp7021.c
new file mode 100644
index 000000000..39ff84825
--- /dev/null
+++ b/drivers/clk/clk-sp7021.c
@@ -0,0 +1,770 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/*
+ * Copyright (C) Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+//#define DEBUG
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <dt-bindings/clock/sp-sp7021.h>
+
+//#define TRACE	pr_info("### %s:%d (%d)\n", __func__, __LINE__, (clk->reg - REG(4, 0)) / 4)
+#define TRACE
+
+#ifndef clk_readl
+#define clk_readl  readl
+#define clk_writel writel
+#endif
+
+#define MASK_SET(shift, width, value) \
+({ \
+	u32 m = ((1 << (width)) - 1) << (shift); \
+	(m << 16) | (((value) << (shift)) & m); \
+})
+#define MASK_GET(shift, width, value)	(((value) >> (shift)) & ((1 << (width)) - 1))
+
+#define REG(i)	(pll_regs + (i) * 4)
+
+#define PLLA_CTL	REG(7)
+#define PLLE_CTL	REG(12)
+#define PLLF_CTL	REG(13)
+#define PLLTV_CTL	REG(14)
+#define PLLSYS_CTL	REG(26)
+
+#define EXTCLK		"extclk"
+
+/* speical div_width values for PLLTV/PLLA */
+#define DIV_TV	33
+#define DIV_A	34
+
+/* PLLTV parameters */
+enum {
+	SEL_FRA,
+	SDM_MOD,
+	PH_SEL,
+	NFRA,
+	DIVR,
+	DIVN,
+	DIVM,
+	P_MAX
+};
+
+struct sp_pll {
+	struct clk_hw	hw;
+	void __iomem	*reg;
+	spinlock_t	*lock;
+	int		pd_bit;		/* power down bit idx */
+	int		bp_bit;		/* bypass bit idx */
+	unsigned long	brate;		/* base rate, FIXME: replace brate with muldiv */
+	int		div_shift;
+	int		div_width;
+	u32		p[P_MAX];	/* for hold PLLTV/PLLA parameters */
+};
+#define to_sp_pll(_hw)	container_of(_hw, struct sp_pll, hw)
+
+#define clk_regs	(moon_regs + 0x000) /* G0 ~ CLKEN */
+#define pll_regs	(moon_regs + 0x200) /* G4 ~ PLL */
+static void __iomem *moon_regs;
+
+#define P_EXTCLK	(1 << 16)
+static const char * const parents[] = {
+	"pllsys",
+	"extclk",
+};
+
+/* FIXME: parent clk incorrect cause clk_get_rate got error value */
+static const u32 gates[] = {
+	SYSTEM,
+	RTC,
+	IOCTL,
+	IOP,
+	OTPRX,
+	NOC,
+	BR,
+	RBUS_L00,
+	SPIFL,
+	SDCTRL0,
+	PERI0 | P_EXTCLK,
+	A926,
+	UMCTL2,
+	PERI1 | P_EXTCLK,
+
+	DDR_PHY0,
+	ACHIP,
+	STC0,
+	STC_AV0,
+	STC_AV1,
+	STC_AV2,
+	UA0 | P_EXTCLK,
+	UA1 | P_EXTCLK,
+	UA2 | P_EXTCLK,
+	UA3 | P_EXTCLK,
+	UA4 | P_EXTCLK,
+	HWUA | P_EXTCLK,
+	DDC0,
+	UADMA | P_EXTCLK,
+
+	CBDMA0,
+	CBDMA1,
+	SPI_COMBO_0,
+	SPI_COMBO_1,
+	SPI_COMBO_2,
+	SPI_COMBO_3,
+	AUD,
+	USBC0,
+	USBC1,
+	UPHY0,
+	UPHY1,
+
+	I2CM0,
+	I2CM1,
+	I2CM2,
+	I2CM3,
+	PMC,
+	CARD_CTL0,
+	CARD_CTL1,
+
+	CARD_CTL4,
+	BCH,
+	DDFCH,
+	CSIIW0,
+	CSIIW1,
+	MIPICSI0,
+	MIPICSI1,
+
+	HDMI_TX,
+	VPOST,
+
+	TGEN,
+	DMIX,
+	TCON,
+	INTERRUPT,
+
+	RGST,
+	GPIO,
+	RBUS_TOP,
+
+	MAILBOX,
+	SPIND,
+	I2C2CBUS,
+	SEC,
+	GPOST0,
+	DVE,
+
+	OSD0,
+	DISP_PWM,
+	UADBG,
+	DUMMY_MASTER,
+	FIO_CTL,
+	FPGA,
+	L2SW,
+	ICM,
+	AXI_GLOBAL,
+};
+static struct clk *clks[CLK_MAX];
+static struct clk_onecell_data clk_data;
+
+static DEFINE_SPINLOCK(plla_lock);
+static DEFINE_SPINLOCK(plle_lock);
+static DEFINE_SPINLOCK(pllf_lock);
+static DEFINE_SPINLOCK(pllsys_lock);
+static DEFINE_SPINLOCK(plltv_lock);
+
+#define _M		1000000UL
+#define F_27M		(27 * _M)
+
+/******************************************** PLL_TV *******************************************/
+
+//#define PLLTV_STEP_DIR (?) /* Unit: HZ */
+
+/* TODO: set proper FVCO range */
+#define FVCO_MIN	(100 * _M)
+#define FVCO_MAX	(200 * _M)
+
+#define F_MIN		(FVCO_MIN / 8)
+#define F_MAX		(FVCO_MAX)
+
+static long plltv_integer_div(struct sp_pll *clk, unsigned long freq)
+{
+	/* valid m values: 27M must be divisible by m, 0 means end */
+	static const u32 m_table[] = {
+		1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 27, 30, 32, 0
+	};
+	u32 m, n, r;
+#ifdef PLLTV_STEP_DIR
+	u32 step = (PLLTV_STEP_DIR > 0) ? PLLTV_STEP_DIR : -PLLTV_STEP_DIR;
+	int calc_times = 1000000 / step;
+#endif
+	unsigned long fvco, nf;
+
+	TRACE;
+
+	/* check freq */
+	if (freq < F_MIN) {
+		pr_warn("[%s:%d] freq:%lu < F_MIN:%lu, round up\n",
+			__func__, __LINE__, freq, F_MIN);
+		freq = F_MIN;
+	} else if (freq > F_MAX) {
+		pr_warn("[%s:%d] freq:%lu > F_MAX:%lu, round down\n",
+			__func__, __LINE__, freq, F_MAX);
+		freq = F_MAX;
+	}
+
+#ifdef PLLTV_STEP_DIR
+	if ((freq % step) != 0)
+		freq += step - (freq % step) + ((PLLTV_STEP_DIR > 0) ? 0 : PLLTV_STEP_DIR);
+#endif
+
+#ifdef PLLTV_STEP_DIR
+CALC:
+	if (!calc_times) {
+		pr_err("[%s:%d] freq:%lu out of recalc times\n", __func__, __LINE__, freq);
+		return -ETIMEOUT;
+	}
+#endif
+
+	/* DIVR 0~3 */
+	for (r = 0; r <= 3; r++) {
+		fvco = freq << r;
+		if (fvco <= FVCO_MAX)
+			break;
+	}
+
+	/* DIVM */
+	for (m = 0; m_table[m]; m++) {
+		nf = fvco * m_table[m];
+		n = nf / F_27M;
+		if ((n * F_27M) == nf)
+			break;
+	}
+	m = m_table[m];
+
+	if (!m) {
+#ifdef PLLTV_STEP_DIR
+		freq += PLLTV_STEP_DIR;
+		calc_times--;
+		goto CALC;
+#else
+		pr_err("[%s:%d] freq:%lu not found a valid setting\n", __func__, __LINE__, freq);
+		return -EINVAL;
+#endif
+	}
+
+	/* save parameters */
+	clk->p[SEL_FRA] = 0;
+	clk->p[DIVR]    = r;
+	clk->p[DIVN]    = n;
+	clk->p[DIVM]    = m;
+
+	pr_info("[%s:%d]   M:%u N:%u R:%u   CKREF:%lu  FVCO:%lu  FCKOUT:%lu\n",
+		__func__, __LINE__, m, n, r, (fvco / m), fvco, freq);
+
+	return freq;
+}
+
+/* parameters for PLLTV fractional divider */
+/* FIXME: better parameter naming */
+static const u32 pt[][5] = {
+	/* conventional fractional */
+	{
+		1,			// factor
+		5,			// 5 * p0 (nint)
+		1,			// 1 * p0
+		F_27M,			// F_27M / p0
+		1,			// p0 / p2
+	},
+	/* phase rotation */
+	{
+		10,			// factor
+		54,			// 5.4 * p0 (nint)
+		2,			// 0.2 * p0
+		F_27M / 10,		// F_27M / p0
+		5,			// p0 / p2
+	},
+};
+static const u32 mods[] = { 91, 55 }; /* SDM_MOD mod values */
+
+static long plltv_fractional_div(struct sp_pll *clk, unsigned long freq)
+{
+	u32 m, r;
+	u32 nint, nfra;
+	u32 diff_min_quotient = 210000000, diff_min_remainder = 0;
+	u32 diff_min_sign = 0;
+	unsigned long fvco, nf, f, fout = 0;
+	int sdm, ph;
+
+	TRACE;
+	/* check freq */
+	if (freq < F_MIN) {
+		pr_warn("[%s:%d] freq:%lu < F_MIN:%lu, round up\n",
+			__func__, __LINE__, freq, F_MIN);
+		freq = F_MIN;
+	} else if (freq > F_MAX) {
+		pr_warn("[%s:%d] freq:%lu > F_MAX:%lu, round down\n",
+			__func__, __LINE__, freq, F_MAX);
+		freq = F_MAX;
+	}
+
+	/* DIVR 0~3 */
+	for (r = 0; r <= 3; r++) {
+		fvco = freq << r;
+		if (fvco <= FVCO_MAX)
+			break;
+	}
+	pr_info("freq:%lu fvco:%lu R:%u\n", freq, fvco, r);
+	f = F_27M >> r;
+
+	/* PH_SEL 1/0 */
+	for (ph = 1; ph >= 0; ph--) {
+		const u32 *pp = pt[ph];
+		u32 ms = 1;
+
+		/* SDM_MOD 0/1 */
+		for (sdm = 0; sdm <= 1; sdm++) {
+			u32 mod = mods[sdm];
+
+			/* DIVM 1~32 */
+			for (m = ms; m <= 32; m++) {
+				u32 diff_freq;
+				u32 diff_freq_quotient = 0, diff_freq_remainder = 0;
+				u32 diff_freq_sign = 0; /* 0:Positive number, 1:Negative number */
+
+				nf = fvco * m;
+				nint = nf / pp[3];
+
+				if (nint < pp[1])
+					continue;
+				if (nint > pp[1])
+					break;
+
+				nfra = (((nf % pp[3]) * mod * pp[4]) + (F_27M / 2)) / F_27M;
+				if (nfra)
+					diff_freq = (f * (nint + pp[2]) / pp[0]) -
+								(f * (mod - nfra) / mod / pp[4]);
+				else
+					diff_freq = (f * (nint) / pp[0]);
+
+				diff_freq_quotient  = diff_freq / m;
+				diff_freq_remainder = ((diff_freq % m) * 1000) / m;
+
+				pr_info("m = %d N.f = %2d.%03d%03d, nfra = %d/%d  fout = %u\n",
+					m, nint, (nfra * 1000) / mod,
+					(((nfra * 1000) % mod) * 1000) / mod,
+					nfra, mod, diff_freq_quotient);
+
+				if (freq > diff_freq_quotient) {
+					diff_freq_quotient  = freq - diff_freq_quotient - 1;
+					diff_freq_remainder = 1000 - diff_freq_remainder;
+					diff_freq_sign = 1;
+				} else {
+					diff_freq_quotient = diff_freq_quotient - freq;
+					diff_freq_sign = 0;
+				}
+
+				if ((diff_min_quotient > diff_freq_quotient) ||
+					((diff_min_quotient == diff_freq_quotient) &&
+					(diff_min_remainder > diff_freq_remainder))) {
+
+					/* found a closer freq, save parameters */
+					TRACE;
+					clk->p[SEL_FRA] = 1;
+					clk->p[SDM_MOD] = sdm;
+					clk->p[PH_SEL]  = ph;
+					clk->p[NFRA]    = nfra;
+					clk->p[DIVR]    = r;
+					clk->p[DIVM]    = m;
+
+					fout = diff_freq / m;
+					diff_min_quotient = diff_freq_quotient;
+					diff_min_remainder = diff_freq_remainder;
+					diff_min_sign = diff_freq_sign;
+				}
+			}
+		}
+	}
+
+	if (!fout) {
+		pr_err("[%s:%d] freq:%lu not found a valid setting\n", __func__, __LINE__, freq);
+		return -EINVAL;
+	}
+
+	//pr_info("MOD:%u PH_SEL:%u NFRA:%u M:%u R:%u\n",
+	//	mods[clk->p[SDM_MOD]], clk->p[PH_SEL], clk->p[NFRA], clk->p[DIVM], clk->p[DIVR]);
+
+	pr_info("[%s:%d] real out:%lu/%lu Hz(%u, %u, sign %u)\n",
+		__func__, __LINE__, fout, freq,
+		diff_min_quotient, diff_min_remainder, diff_min_sign);
+
+	return fout;
+}
+
+static long plltv_div(struct sp_pll *clk, unsigned long freq)
+{
+	TRACE;
+	if (freq % 100)
+		return plltv_fractional_div(clk, freq);
+	else
+		return plltv_integer_div(clk, freq);
+}
+
+static void plltv_set_rate(struct sp_pll *clk)
+{
+	u32 reg;
+
+	//pr_info("MOD:%u PH_SEL:%u NFRA:%u M:%u R:%u\n",
+	//	mods[clk->p[SDM_MOD]], clk->p[PH_SEL], clk->p[NFRA], clk->p[DIVM], clk->p[DIVR]);
+	reg  = MASK_SET(1, 1, clk->p[SEL_FRA]);
+	reg |= MASK_SET(2, 1, clk->p[SDM_MOD]);
+	reg |= MASK_SET(4, 1, clk->p[PH_SEL]);
+	reg |= MASK_SET(6, 7, clk->p[NFRA]);
+	clk_writel(reg, clk->reg);
+
+	reg  = MASK_SET(7, 2, clk->p[DIVR]);
+	clk_writel(reg, clk->reg + 4);
+
+	reg  = MASK_SET(0, 8, clk->p[DIVN] - 1);
+	reg |= MASK_SET(8, 7, clk->p[DIVM] - 1);
+	clk_writel(reg, clk->reg + 8);
+}
+
+/******************************************** PLL_A ********************************************/
+
+/* from Q628_PLLs_REG_setting.xlsx */
+struct {
+	u32 rate;
+	u32 regs[5];
+} pa[] = {
+	{
+		.rate = 135475200,
+		.regs = {
+			0x4801,
+			0x02df,
+			0x248f,
+			0x0211,
+			0x33e9
+		}
+	},
+	{
+		.rate = 147456000,
+		.regs = {
+			0x4801,
+			0x1adf,
+			0x2490,
+			0x0349,
+			0x33e9
+		}
+	},
+	{
+		.rate = 196608000,
+		.regs = {
+			0x4801,
+			0x42ef,
+			0x2495,
+			0x01c6,
+			0x33e9
+		}
+	},
+};
+
+static void plla_set_rate(struct sp_pll *clk)
+{
+	const u32 *pp = pa[clk->p[0]].regs;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(pa->regs); i++) {
+		clk_writel(0xffff0000 | pp[i], clk->reg + (i * 4));
+		pr_info("%04x\n", pp[i]);
+	}
+}
+
+static long plla_round_rate(struct sp_pll *clk, unsigned long rate)
+{
+	int i = ARRAY_SIZE(pa);
+
+	while (--i) {
+		if (rate >= pa[i].rate)
+			break;
+	}
+	clk->p[0] = i;
+	return pa[i].rate;
+}
+
+/******************************************* SP_PLL ********************************************/
+
+static long sp_pll_calc_div(struct sp_pll *clk, unsigned long rate)
+{
+	u32 fbdiv;
+	u32 max = 1 << clk->div_width;
+
+	fbdiv = DIV_ROUND_CLOSEST(rate, clk->brate);
+	if (fbdiv > max)
+		fbdiv = max;
+	return fbdiv;
+}
+
+static long sp_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+		unsigned long *prate)
+{
+	struct sp_pll *clk = to_sp_pll(hw);
+	long ret;
+
+	TRACE;
+	//pr_info("round_rate: %lu %lu\n", rate, *prate);
+
+	if (rate == *prate)
+		ret = *prate; /* bypass */
+	else if (clk->div_width == DIV_A) {
+		ret = plla_round_rate(clk, rate);
+	} else if (clk->div_width == DIV_TV) {
+		ret = plltv_div(clk, rate);
+		if (ret < 0)
+			ret = *prate;
+	} else
+		ret = sp_pll_calc_div(clk, rate) * clk->brate;
+
+	return ret;
+}
+
+static unsigned long sp_pll_recalc_rate(struct clk_hw *hw,
+		unsigned long prate)
+{
+	struct sp_pll *clk = to_sp_pll(hw);
+	u32 reg = clk_readl(clk->reg);
+	unsigned long ret;
+
+	//TRACE;
+	if (reg & BIT(clk->bp_bit))
+		ret = prate; /* bypass */
+	else if (clk->div_width == DIV_A) {
+		ret = pa[clk->p[0]].rate;
+		//reg = clk_readl(clk->reg + 12); // G4.10 K_SDM_A
+	} else if (clk->div_width == DIV_TV) {
+		u32 m, r, reg2;
+
+		r = MASK_GET(7, 2, clk_readl(clk->reg + 4));
+		reg2 = clk_readl(clk->reg + 8);
+		m = MASK_GET(8, 7, reg2) + 1;
+
+		if (reg & BIT(1)) { /* SEL_FRA */
+			/* fractional divider */
+			u32 sdm  = MASK_GET(2, 1, reg);
+			u32 ph   = MASK_GET(4, 1, reg);
+			u32 nfra = MASK_GET(6, 7, reg);
+			const u32 *pp = pt[ph];
+
+			ret = prate >> r;
+			ret = (ret * (pp[1] + pp[2]) / pp[0]) -
+				  (ret * (mods[sdm] - nfra) / mods[sdm] / pp[4]);
+			ret /= m;
+		} else {
+			/* integer divider */
+			u32 n = MASK_GET(0, 8, reg2) + 1;
+
+			ret = (prate / m * n) >> r;
+		}
+	} else {
+		u32 fbdiv = MASK_GET(clk->div_shift, clk->div_width, reg) + 1;
+
+		ret = clk->brate * fbdiv;
+	}
+	//pr_info("recalc_rate: %lu -> %lu\n", prate, ret);
+
+	return ret;
+}
+
+static int sp_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+		unsigned long prate)
+{
+	struct sp_pll *clk = to_sp_pll(hw);
+	unsigned long flags;
+	u32 reg;
+
+	//TRACE;
+	pr_info("set_rate: %lu -> %lu\n", prate, rate);
+
+	spin_lock_irqsave(clk->lock, flags);
+
+	reg = BIT(clk->bp_bit + 16); /* HIWORD_MASK */
+
+	if (rate == prate)
+		reg |= BIT(clk->bp_bit); /* bypass */
+	else if (clk->div_width == DIV_A)
+		plla_set_rate(clk);
+	else if (clk->div_width == DIV_TV)
+		plltv_set_rate(clk);
+	else if (clk->div_width) {
+		u32 fbdiv = sp_pll_calc_div(clk, rate);
+
+		reg |= MASK_SET(clk->div_shift, clk->div_width, fbdiv - 1);
+	}
+
+	clk_writel(reg, clk->reg);
+
+	spin_unlock_irqrestore(clk->lock, flags);
+
+	return 0;
+}
+
+static int sp_pll_enable(struct clk_hw *hw)
+{
+	struct sp_pll *clk = to_sp_pll(hw);
+	unsigned long flags;
+
+	TRACE;
+	spin_lock_irqsave(clk->lock, flags);
+	clk_writel(BIT(clk->pd_bit + 16) | BIT(clk->pd_bit), clk->reg); /* power up */
+	spin_unlock_irqrestore(clk->lock, flags);
+
+	return 0;
+}
+
+static void sp_pll_disable(struct clk_hw *hw)
+{
+	struct sp_pll *clk = to_sp_pll(hw);
+	unsigned long flags;
+
+	TRACE;
+	spin_lock_irqsave(clk->lock, flags);
+	clk_writel(BIT(clk->pd_bit + 16), clk->reg); /* power down */
+	spin_unlock_irqrestore(clk->lock, flags);
+}
+
+static int sp_pll_is_enabled(struct clk_hw *hw)
+{
+	struct sp_pll *clk = to_sp_pll(hw);
+
+	return clk_readl(clk->reg) & BIT(clk->pd_bit);
+}
+
+static const struct clk_ops sp_pll_ops = {
+	.enable = sp_pll_enable,
+	.disable = sp_pll_disable,
+	.is_enabled = sp_pll_is_enabled,
+	.round_rate = sp_pll_round_rate,
+	.recalc_rate = sp_pll_recalc_rate,
+	.set_rate = sp_pll_set_rate
+};
+
+static const struct clk_ops sp_pll_sub_ops = {
+	.enable = sp_pll_enable,
+	.disable = sp_pll_disable,
+	.is_enabled = sp_pll_is_enabled,
+	.recalc_rate = sp_pll_recalc_rate,
+};
+
+struct clk *clk_register_sp_pll(const char *name, const char *parent,
+		void __iomem *reg, int pd_bit, int bp_bit,
+		unsigned long brate, int shift, int width,
+		spinlock_t *lock)
+{
+	struct sp_pll *pll;
+	struct clk *clk;
+	//unsigned long flags = 0;
+	struct clk_init_data initd = {
+		.name = name,
+		.parent_names = &parent,
+		.ops = (bp_bit >= 0) ? &sp_pll_ops : &sp_pll_sub_ops,
+		.num_parents = 1,
+		.flags = CLK_IGNORE_UNUSED
+	};
+
+	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+	if (!pll)
+		return ERR_PTR(-ENOMEM);
+
+	if (reg == PLLSYS_CTL)
+		initd.flags |= CLK_IS_CRITICAL;
+
+	pll->hw.init = &initd;
+	pll->reg = reg;
+	pll->pd_bit = pd_bit;
+	pll->bp_bit = bp_bit;
+	pll->brate = brate;
+	pll->div_shift = shift;
+	pll->div_width = width;
+	pll->lock = lock;
+
+	clk = clk_register(NULL, &pll->hw);
+	if (WARN_ON(IS_ERR(clk))) {
+		kfree(pll);
+	} else {
+		pr_info("%-20s%lu\n", name, clk_get_rate(clk));
+		clk_register_clkdev(clk, NULL, name);
+	}
+
+	return clk;
+}
+
+static void __init sp_clk_setup(struct device_node *np)
+{
+	int i, j;
+
+	pr_info("sp-clkc init\n");
+
+	moon_regs = of_iomap(np, 0);
+	if (WARN_ON(!moon_regs)) {
+		pr_warn("sp-clkc regs missing.\n");
+		return; // -EIO
+	}
+
+	/* TODO: PLLs initial */
+
+	/* PLL_A */
+	clks[PLL_A] = clk_register_sp_pll("plla", EXTCLK,
+			PLLA_CTL, 11, 12, 27000000, 0, DIV_A, &plla_lock);
+
+	/* PLL_E */
+	clks[PLL_E] = clk_register_sp_pll("plle", EXTCLK,
+			PLLE_CTL, 6, 2, 50000000, 0, 0, &plle_lock);
+	clks[PLL_E_2P5] = clk_register_sp_pll("plle_2p5", "plle",
+			PLLE_CTL, 13, -1, 2500000, 0, 0, &plle_lock);
+	clks[PLL_E_25] = clk_register_sp_pll("plle_25", "plle",
+			PLLE_CTL, 12, -1, 25000000, 0, 0, &plle_lock);
+	clks[PLL_E_112P5] = clk_register_sp_pll("plle_112p5", "plle",
+			PLLE_CTL, 11, -1, 112500000, 0, 0, &plle_lock);
+
+	/* PLL_F */
+	clks[PLL_F] = clk_register_sp_pll("pllf", EXTCLK,
+			PLLF_CTL, 0, 10, 13500000, 1, 4, &pllf_lock);
+
+	/* PLL_TV */
+	clks[PLL_TV] = clk_register_sp_pll("plltv", EXTCLK,
+			PLLTV_CTL, 0, 15, 27000000, 0, DIV_TV, &plltv_lock);
+	clks[PLL_TV_A] = clk_register_divider(NULL, "plltv_a", "plltv", 0,
+			PLLTV_CTL + 4, 5, 1,
+			CLK_DIVIDER_POWER_OF_TWO, &plltv_lock);
+	clk_register_clkdev(clks[PLL_TV_A], NULL, "plltv_a");
+
+	/* PLL_SYS */
+	clks[PLL_SYS] = clk_register_sp_pll("pllsys", EXTCLK,
+			PLLSYS_CTL, 10, 9, 13500000, 0, 4, &pllsys_lock);
+
+	/* gates */
+	for (i = 0; i < ARRAY_SIZE(gates); i++) {
+		char s[10];
+
+		j = gates[i] & 0xffff;
+		sprintf(s, "clken%02x", j);
+		clks[j] = clk_register_gate(NULL, s, parents[gates[i] >> 16], CLK_IGNORE_UNUSED,
+			clk_regs + (j >> 4) * 4, j & 0x0f,
+			CLK_GATE_HIWORD_MASK, NULL);
+	}
+
+	clk_data.clks = clks;
+	clk_data.clk_num = ARRAY_SIZE(clks);
+	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+}
+
+CLK_OF_DECLARE(sp_clkc, "sunplus,sp7021-clkc", sp_clk_setup);
+
+MODULE_AUTHOR("Qin Jian <qinjian@cqplus1.com>");
+MODULE_DESCRIPTION("Sunplus SP7021 Clock Driver");
+MODULE_LICENSE("GPL v2");
-- 
2.33.1

[PATCH v2 3/8] dt-bindings: reset: Add bindings for SP7021 reset driver

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-10-29 09:11:21

Add documentation to describe Sunplus SP7021 reset driver bindings.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 .../bindings/reset/sunplus,reset.yaml         | 40 ++++++++
 MAINTAINERS                                   |  2 +
 include/dt-bindings/reset/sp-sp7021.h         | 99 +++++++++++++++++++
 3 files changed, 141 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/reset/sunplus,reset.yaml
 create mode 100644 include/dt-bindings/reset/sp-sp7021.h
diff --git a/Documentation/devicetree/bindings/reset/sunplus,reset.yaml b/Documentation/devicetree/bindings/reset/sunplus,reset.yaml
new file mode 100644
index 000000000..bf55f4ee2
--- /dev/null
+++ b/Documentation/devicetree/bindings/reset/sunplus,reset.yaml
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) Sunplus Co., Ltd. 2021
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/reset/sunplus,reset.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Sunplus SoC Reset Controller
+
+maintainers:
+  - Qin Jian <qinjian@cqplus1.com>
+
+properties:
+  compatible:
+    enum:
+      - sunplus,sp7021-reset # Reset Controller on SP7021 and compatible SoCs
+      - sunplus,q645-reset # Reset Controller on Q645 and compatible SoCs
+
+  "#reset-cells":
+    const: 1
+
+  reg:
+    maxItems: 1
+
+required:
+  - compatible
+  - "#reset-cells"
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    rstc: reset@9c000054 {
+      compatible = "sunplus,sp7021-reset";
+      #reset-cells = <1>;
+      reg = <0x9c000054 0x28>;
+    };
+
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 6a5422f10..652f42cab 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2661,6 +2661,8 @@ L:	linux-arm-kernel@lists.infradead.org (moderated for mon-subscribers)
 S:	Maintained
 W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
 F:	Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
+F:	Documentation/devicetree/bindings/reset/sunplus,reset.yaml
+F:	include/dt-bindings/reset/sp-sp7021.h
 
 ARM/Synaptics SoC support
 M:	Jisheng Zhang <Jisheng.Zhang@synaptics.com>
diff --git a/include/dt-bindings/reset/sp-sp7021.h b/include/dt-bindings/reset/sp-sp7021.h
new file mode 100644
index 000000000..8823d25ce
--- /dev/null
+++ b/include/dt-bindings/reset/sp-sp7021.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright (C) Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+#ifndef _DT_BINDINGS_RST_SUNPLUS_SP7021_H
+#define _DT_BINDINGS_RST_SUNPLUS_SP7021_H
+
+/* mo_reset0 ~ mo_reset9 */
+#define RST_SYSTEM		0x00
+#define	RST_RTC			0x02
+#define RST_IOCTL		0x03
+#define RST_IOP			0x04
+#define RST_OTPRX		0x05
+#define RST_NOC			0x06
+#define RST_BR			0x07
+#define RST_RBUS_L00		0x08
+#define RST_SPIFL		0x09
+#define RST_SDCTRL0		0x0a
+#define RST_PERI0		0x0b
+#define RST_A926		0x0d
+#define RST_UMCTL2		0x0e
+#define RST_PERI1		0x0f
+
+#define RST_DDR_PHY0		0x10
+#define RST_ACHIP		0x12
+#define RST_STC0		0x14
+#define RST_STC_AV0		0x15
+#define RST_STC_AV1		0x16
+#define RST_STC_AV2		0x17
+#define RST_UA0			0x18
+#define RST_UA1			0x19
+#define RST_UA2			0x1a
+#define RST_UA3			0x1b
+#define RST_UA4			0x1c
+#define RST_HWUA		0x1d
+#define RST_DDC0		0x1e
+#define RST_UADMA		0x1f
+
+#define RST_CBDMA0		0x20
+#define RST_CBDMA1		0x21
+#define RST_SPI_COMBO_0		0x22
+#define RST_SPI_COMBO_1		0x23
+#define RST_SPI_COMBO_2		0x24
+#define RST_SPI_COMBO_3		0x25
+#define RST_AUD			0x26
+#define RST_USBC0		0x2a
+#define RST_USBC1		0x2b
+#define RST_UPHY0		0x2d
+#define RST_UPHY1		0x2e
+
+#define RST_I2CM0		0x30
+#define RST_I2CM1		0x31
+#define RST_I2CM2		0x32
+#define RST_I2CM3		0x33
+#define RST_PMC			0x3d
+#define RST_CARD_CTL0		0x3e
+#define RST_CARD_CTL1		0x3f
+
+#define RST_CARD_CTL4		0x42
+#define RST_BCH			0x44
+#define RST_DDFCH		0x4b
+#define RST_CSIIW0		0x4c
+#define RST_CSIIW1		0x4d
+#define RST_MIPICSI0		0x4e
+#define RST_MIPICSI1		0x4f
+
+#define RST_HDMI_TX		0x50
+#define RST_VPOST		0x55
+
+#define RST_TGEN		0x60
+#define RST_DMIX		0x61
+#define RST_TCON		0x6a
+#define RST_INTERRUPT		0x6f
+
+#define RST_RGST		0x70
+#define RST_GPIO		0x73
+#define RST_RBUS_TOP		0x74
+
+#define RST_MAILBOX		0x86
+#define RST_SPIND		0x8a
+#define RST_I2C2CBUS		0x8b
+#define RST_SEC			0x8d
+#define RST_DVE			0x8e
+#define RST_GPOST0		0x8f
+
+#define RST_OSD0		0x90
+#define RST_DISP_PWM		0x92
+#define RST_UADBG		0x93
+#define RST_DUMMY_MASTER	0x94
+#define RST_FIO_CTL		0x95
+#define RST_FPGA		0x96
+#define RST_L2SW		0x97
+#define RST_ICM			0x98
+#define RST_AXI_GLOBAL		0x99
+
+#define RST_MAX			0xA0
+
+#endif
-- 
2.33.1

[PATCH v2 7/8] dt-bindings: interrupt-controller: Add bindings for SP7021 interrupt controller

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-10-29 09:11:28

Add documentation to describe Sunplus SP7021 interrupt controller bindings.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 .../sunplus,sp7021-intc.yaml                  | 59 +++++++++++++++++++
 MAINTAINERS                                   |  2 +
 .../interrupt-controller/sp7021-intc.h        | 24 ++++++++
 3 files changed, 85 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml
 create mode 100644 include/dt-bindings/interrupt-controller/sp7021-intc.h
diff --git a/Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml b/Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml
new file mode 100644
index 000000000..185c99b5e
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml
@@ -0,0 +1,59 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) Sunplus Co., Ltd. 2021
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/interrupt-controller/sunplus,sp7021-intc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sunplus SP7021 SoC Interrupt Controller Device Tree Bindings
+
+maintainers:
+  - Qin Jian <qinjian@cqplus1.com>
+
+properties:
+  compatible:
+    items:
+      - const: sunplus,sp7021-intc
+
+  interrupt-controller: true
+
+  "#interrupt-cells":
+    const: 2
+    description:
+      The first cell is the IRQ number, the second cell the trigger
+      type as defined in interrupt.txt in this directory.
+      The MSB of second cell also indicate route to which EXT_INT.
+
+  reg:
+    maxItems: 2
+
+  interrupts:
+    maxItems: 2
+    description:
+      EXT_INT0 & EXT_INT1, 2 interrupts references to primary interrupt
+      controller.
+
+required:
+  - compatible
+  - interrupt-controller
+  - "#interrupt-cells"
+  - reg
+  - interrupts
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    intc: interrupt-controller@9c000780 {
+        compatible = "sunplus,sp7021-intc";
+        interrupt-controller;
+        #interrupt-cells = <2>;
+        reg = <0x9c000780 0x80>, <0x9c000a80 0x80>;
+        interrupt-parent = <&gic>;
+        interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>, /* EXT_INT0 */
+                    <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>; /* EXT_INT1 */
+    };
+
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 5069f552f..be0334d6a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2662,10 +2662,12 @@ S:	Maintained
 W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
 F:	Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
 F:	Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
+F:	Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml
 F:	Documentation/devicetree/bindings/reset/sunplus,reset.yaml
 F:	drivers/clk/clk-sp7021.c
 F:	drivers/reset/reset-sunplus.c
 F:	include/dt-bindings/clock/sp-sp7021.h
+F:	include/dt-bindings/interrupt-controller/sp7021-intc.h
 F:	include/dt-bindings/reset/sp-sp7021.h
 
 ARM/Synaptics SoC support
diff --git a/include/dt-bindings/interrupt-controller/sp7021-intc.h b/include/dt-bindings/interrupt-controller/sp7021-intc.h
new file mode 100644
index 000000000..fa2eb8100
--- /dev/null
+++ b/include/dt-bindings/interrupt-controller/sp7021-intc.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright (C) Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ *
+ *
+ * This header provides constants for the SP7021 INTC
+ */
+
+#ifndef _DT_BINDINGS_INTERRUPT_CONTROLLER_SP7021_INTC_H
+#define _DT_BINDINGS_INTERRUPT_CONTROLLER_SP7021_INTC_H
+
+#include <dt-bindings/interrupt-controller/irq.h>
+
+/*
+ * Interrupt specifier cell 1.
+ * The flags in irq.h are valid, plus those below.
+ */
+#define SP_INTC_EXT_INT0		0x00000
+#define SP_INTC_EXT_INT1		0x01000
+#define SP_INTC_EXT_INT_MASK	0xff000
+#define SP_INTC_EXT_INT_SHFIT	12
+
+#endif
-- 
2.33.1

[PATCH v2 5/8] dt-bindings: clock: Add bindings for SP7021 clock driver

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-10-29 09:11:39

Add documentation to describe Sunplus SP7021 clock driver bindings.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 .../bindings/clock/sunplus,sp7021-clkc.yaml   |  38 ++++++
 MAINTAINERS                                   |   2 +
 include/dt-bindings/clock/sp-sp7021.h         | 112 ++++++++++++++++++
 3 files changed, 152 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
 create mode 100644 include/dt-bindings/clock/sp-sp7021.h
diff --git a/Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml b/Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
new file mode 100644
index 000000000..1ce7e41d8
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
@@ -0,0 +1,38 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) Sunplus Co., Ltd. 2021
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/sunplus,sp7021-clkc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sunplus SP7021 SoC Clock Controller Binding
+
+maintainers:
+  - Qin Jian <qinjian@cqplus1.com>
+
+properties:
+  compatible:
+    const: sunplus,sp7021-clkc
+
+  "#clock-cells":
+    const: 1
+
+  reg:
+    maxItems: 1
+
+required:
+  - compatible
+  - "#clock-cells"
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    clkc: clkc@9c000000 {
+      compatible = "sunplus,sp7021-clkc";
+      #clock-cells = <1>;
+      reg = <0x9c000000 0x280>;
+    };
+
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 6caffd6d0..90ebb823f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2661,8 +2661,10 @@ L:	linux-arm-kernel@lists.infradead.org (moderated for mon-subscribers)
 S:	Maintained
 W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
 F:	Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
+F:	Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
 F:	Documentation/devicetree/bindings/reset/sunplus,reset.yaml
 F:	drivers/reset/reset-sunplus.c
+F:	include/dt-bindings/clock/sp-sp7021.h
 F:	include/dt-bindings/reset/sp-sp7021.h
 
 ARM/Synaptics SoC support
diff --git a/include/dt-bindings/clock/sp-sp7021.h b/include/dt-bindings/clock/sp-sp7021.h
new file mode 100644
index 000000000..1ae9c4083
--- /dev/null
+++ b/include/dt-bindings/clock/sp-sp7021.h
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright (C) Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+#ifndef _DT_BINDINGS_CLOCK_SUNPLUS_SP7021_H
+#define _DT_BINDINGS_CLOCK_SUNPLUS_Sp7021_H
+
+#define XTAL		27000000
+
+/* plls */
+#define PLL_A		0
+#define PLL_E		1
+#define PLL_E_2P5	2
+#define PLL_E_25	3
+#define PLL_E_112P5	4
+#define PLL_F		5
+#define PLL_TV		6
+#define PLL_TV_A	7
+#define PLL_SYS		8
+
+/* gates: mo_clken0 ~ mo_clken9 */
+#define SYSTEM		0x10
+#define RTC			0x12
+#define IOCTL		0x13
+#define IOP			0x14
+#define OTPRX		0x15
+#define NOC			0x16
+#define BR			0x17
+#define RBUS_L00	0x18
+#define SPIFL		0x19
+#define SDCTRL0		0x1a
+#define PERI0		0x1b
+#define A926		0x1d
+#define UMCTL2		0x1e
+#define PERI1		0x1f
+
+#define DDR_PHY0	0x20
+#define ACHIP		0x22
+#define STC0		0x24
+#define STC_AV0		0x25
+#define STC_AV1		0x26
+#define STC_AV2		0x27
+#define UA0			0x28
+#define UA1			0x29
+#define UA2			0x2a
+#define UA3			0x2b
+#define UA4			0x2c
+#define HWUA		0x2d
+#define DDC0		0x2e
+#define UADMA		0x2f
+
+#define CBDMA0		0x30
+#define CBDMA1		0x31
+#define SPI_COMBO_0	0x32
+#define SPI_COMBO_1	0x33
+#define SPI_COMBO_2	0x34
+#define SPI_COMBO_3	0x35
+#define AUD			0x36
+#define USBC0		0x3a
+#define USBC1		0x3b
+#define UPHY0		0x3d
+#define UPHY1		0x3e
+
+#define I2CM0		0x40
+#define I2CM1		0x41
+#define I2CM2		0x42
+#define I2CM3		0x43
+#define PMC			0x4d
+#define CARD_CTL0	0x4e
+#define CARD_CTL1	0x4f
+
+#define CARD_CTL4	0x52
+#define BCH			0x54
+#define DDFCH		0x5b
+#define CSIIW0		0x5c
+#define CSIIW1		0x5d
+#define MIPICSI0	0x5e
+#define MIPICSI1	0x5f
+
+#define HDMI_TX		0x60
+#define VPOST		0x65
+
+#define TGEN		0x70
+#define DMIX		0x71
+#define TCON		0x7a
+#define INTERRUPT	0x7f
+
+#define RGST		0x80
+#define GPIO		0x83
+#define RBUS_TOP	0x84
+
+#define MAILBOX		0x96
+#define SPIND		0x9a
+#define I2C2CBUS	0x9b
+#define SEC			0x9d
+#define DVE			0x9e
+#define GPOST0		0x9f
+
+#define OSD0		0xa0
+#define DISP_PWM	0xa2
+#define UADBG		0xa3
+#define DUMMY_MASTER	0xa4
+#define FIO_CTL		0xa5
+#define FPGA		0xa6
+#define L2SW		0xa7
+#define ICM			0xa8
+#define AXI_GLOBAL	0xa9
+
+#define CLK_MAX		0xb0
+
+#endif
-- 
2.33.1

[PATCH v2 1/8] dt-bindings: vendor-prefixes: Add Sunplus

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-10-29 09:11:55

Add vendor prefix for Sunplus Technology Co., Ltd. (http://www.sunplus.com)

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
 1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index a867f7102..50d4ee5ac 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -1131,6 +1131,8 @@ patternProperties:
     description: Summit microelectronics
   "^sunchip,.*":
     description: Shenzhen Sunchip Technology Co., Ltd
+  "^sunplus,.*":
+    description: Sunplus Technology Co., Ltd.
   "^SUNW,.*":
     description: Sun Microsystems, Inc
   "^supermicro,.*":
-- 
2.33.1

Re: [PATCH v2 8/8] irqchip: Add support for Sunplus SP7021 interrupt controller

From: Marc Zyngier <maz@kernel.org>
Date: 2021-10-29 15:25:23

On Fri, 29 Oct 2021 09:44:34 +0100,
Qin Jian [off-list ref] wrote:
Add interrupt driver for Sunplus SP7021 SoC.

This is the interrupt controller in P chip which collects all interrupt
sources in P-chip and routes them to C-chip. C-chip adopts ARM CA7
interrupt controller (compitable = "arm,cortex-a7-gic"). It is a parent
This information isn't relevant.
quoted hunk
interrupt controller of P-chip interrupt controller.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 MAINTAINERS                       |   1 +
 drivers/irqchip/Kconfig           |   9 +
 drivers/irqchip/Makefile          |   1 +
 drivers/irqchip/irq-sp7021-intc.c | 324 ++++++++++++++++++++++++++++++
 4 files changed, 335 insertions(+)
 create mode 100644 drivers/irqchip/irq-sp7021-intc.c
diff --git a/MAINTAINERS b/MAINTAINERS
index be0334d6a..bfa891d86 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2665,6 +2665,7 @@ F:	Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
 F:	Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml
 F:	Documentation/devicetree/bindings/reset/sunplus,reset.yaml
 F:	drivers/clk/clk-sp7021.c
+F:	drivers/irqchip/irq-sp7021-intc.c
 F:	drivers/reset/reset-sunplus.c
 F:	include/dt-bindings/clock/sp-sp7021.h
 F:	include/dt-bindings/interrupt-controller/sp7021-intc.h
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index aca7b595c..8a58dfb88 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -602,4 +602,13 @@ config APPLE_AIC
 	  Support for the Apple Interrupt Controller found on Apple Silicon SoCs,
 	  such as the M1.
 
+config SUNPLUS_SP7021_INTC
+	bool "Sunplus SP7021 interrupt controller"
+	default SOC_SP7021
+	help
+	  Support for the Sunplus SP7021 Interrupt Controller IP core.
+	  This is used as a primary controller with SP7021 ChipP and can also
+	  be used as a secondary chained controller on SP7021 ChipC.
+	  This is selected automatically by platform config.
If it is selected, drop the default.
quoted hunk
+
 endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index f88cbf36a..75411f654 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -116,3 +116,4 @@ obj-$(CONFIG_MACH_REALTEK_RTL)		+= irq-realtek-rtl.o
 obj-$(CONFIG_WPCM450_AIC)		+= irq-wpcm450-aic.o
 obj-$(CONFIG_IRQ_IDT3243X)		+= irq-idt3243x.o
 obj-$(CONFIG_APPLE_AIC)			+= irq-apple-aic.o
+obj-$(CONFIG_SUNPLUS_SP7021_INTC)	+= irq-sp7021-intc.o
diff --git a/drivers/irqchip/irq-sp7021-intc.c b/drivers/irqchip/irq-sp7021-intc.c
new file mode 100644
index 000000000..3431ec746
--- /dev/null
+++ b/drivers/irqchip/irq-sp7021-intc.c
@@ -0,0 +1,324 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/*
+ * Copyright (C) Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/io.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#include <asm/exception.h>
+#include <dt-bindings/interrupt-controller/sp7021-intc.h>
+
+#define SP_INTC_HWIRQ_MIN     0
+#define SP_INTC_HWIRQ_MAX     223
+
+/* Interrupt G0/G1 offset */
+#define INTR_REG_SIZE		(7 * 4)
Why isn't that derived from the number of interrupts?
+
+#define G0_INTR_TYPE		(0)
+#define G0_INTR_POLARITY	(G0_INTR_TYPE + INTR_REG_SIZE)
+#define G0_INTR_PRIORITY	(G0_INTR_POLARITY + INTR_REG_SIZE)
+#define G0_INTR_MASK		(G0_INTR_PRIORITY + INTR_REG_SIZE)
+
+#define G1_INTR_CLR			(0)
+#define G1_MASKED_EXT1		(G1_INTR_CLR + INTR_REG_SIZE)
+#define G1_MASKED_EXT0		(G1_MASKED_EXT1 + INTR_REG_SIZE)
+#define G1_INTR_GROUP		(31 * 4)
+#define G1_INTR_MASK		(0x7F)
+#define G1_EXT1_SHIFT		(0)
+#define G1_EXT0_SHIFT		(8)
+
+static struct sp_intctl {
+	void __iomem *g0;
+	void __iomem *g1;
What is G0? what is G1?
+	struct irq_domain *domain;
+	struct device_node *node;
+	raw_spinlock_t lock;
+	int virq[2];
+} sp_intc;
+
+/* GPIO INT EDGE BUG WORKAROUND */
+#define GPIO_INT0_HWIRQ			120
+#define GPIO_INT7_HWIRQ			127
+#define GPIO_INT_EDGE_ACTIVE	BIT(31)
+#define IS_GPIO_INT(n)	(((n) >= GPIO_INT0_HWIRQ) && ((n) <= GPIO_INT7_HWIRQ))
+/* array to hold which interrupt needs to workaround the bug
+ * INT_TYPE_NONE: no need
+ * INT_TYPE_EDGE_FALLING/INT_TYPE_EDGE_RISING: need to workaround
+ * GPIO_INT_EDGE_ACTIVING: workaround is on going
Please describe the nature of the workaround. s/ACTIVING/ACTIVE/.
+ */
+static u32 edge_trigger[SP_INTC_HWIRQ_MAX];
4 states per interrupt, and yet you use a u32 for each of them...
Also, why isn't this part of your sp_intc data structure? You also
have enough space for 200+ interrupts (ignoring the obvious off-by-one
bug), and yet you are only concerned with 8 of them. Do you spot a
trend here?
+
+static struct irq_chip sp_intc_chip;
+
+static void sp_intc_assign_bit(u32 hwirq, void __iomem *base, u32 value)
If value describes a single bit, why is it a u32?
+{
+	u32 offset, mask;
+	unsigned long flags;
+	void __iomem *reg;
+
+	offset = (hwirq / 32) * 4;
+	reg = base + offset;
+
+	raw_spin_lock_irqsave(&sp_intc.lock, flags);
+	mask = readl_relaxed(reg);
+	if (value)
+		mask |= BIT(hwirq % 32);
+	else
+		mask &= ~BIT(hwirq % 32);
+	writel_relaxed(mask, reg);
+	raw_spin_unlock_irqrestore(&sp_intc.lock, flags);
+}
+
+static void sp_intc_ack_irq(struct irq_data *d)
+{
+	u32 hwirq = d->hwirq;
+
+	if (edge_trigger[hwirq] != IRQ_TYPE_NONE) {
Why don't you just check the irq number instead?
+		sp_intc_assign_bit(hwirq, sp_intc.g0 + G0_INTR_POLARITY,
+			(edge_trigger[hwirq] == IRQ_TYPE_EDGE_RISING));
+		edge_trigger[hwirq] |= GPIO_INT_EDGE_ACTIVE;
The whole thing is terrible. For each of the 8 interrupts, you only
need to know whether:

- it is rising or falling
- it is active or not

That's a grand total of 16 bits instead of almost a 1kB worth of
nothing. Use atomic bitops, and this is done.
+	}
+
+	sp_intc_assign_bit(hwirq, sp_intc.g1 + G1_INTR_CLR, 1);
+}
+
+static void sp_intc_mask_irq(struct irq_data *d)
+{
+	sp_intc_assign_bit(d->hwirq, sp_intc.g0 + G0_INTR_MASK, 0);
+}
+
+static void sp_intc_unmask_irq(struct irq_data *d)
+{
+	sp_intc_assign_bit(d->hwirq, sp_intc.g0 + G0_INTR_MASK, 1);
+}
+
+static void sp_intc_set_priority(u32 hwirq, u32 priority)
+{
+	sp_intc_assign_bit(hwirq, sp_intc.g0 + G0_INTR_PRIORITY, priority);
+}
+
+static int sp_intc_set_type(struct irq_data *d, unsigned int type)
+{
+	u32 intr_type;		/* 0: level			1: edge */
+	u32 intr_polarity;	/* 0: high/rising	1: low/falling */
So how about giving the variables sensible names and types:

	bool is_level, is_high;
+	u32 hwirq = d->hwirq;
+
+	/* update the chip/handler */
+	if (type & IRQ_TYPE_LEVEL_MASK)
+		irq_set_chip_handler_name_locked(d, &sp_intc_chip,
+						   handle_level_irq, NULL);
+	else
+		irq_set_chip_handler_name_locked(d, &sp_intc_chip,
+						   handle_edge_irq, NULL);
+
+	edge_trigger[hwirq] = IRQ_TYPE_NONE;
+
+	if (type & IRQ_TYPE_LEVEL_MASK)
+		intr_type = 0;
+	else if (IS_GPIO_INT(hwirq)) {
+		intr_type = 0;
+		edge_trigger[hwirq] = type;
+	} else
+		intr_type = 1;
Coding style.
+
+	intr_polarity = (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_EDGE_FALLING);
+
+	sp_intc_assign_bit(hwirq, sp_intc.g0 + G0_INTR_TYPE, intr_type);
+	sp_intc_assign_bit(hwirq, sp_intc.g0 + G0_INTR_POLARITY, intr_polarity);
+
+	return IRQ_SET_MASK_OK;
+}
+
+static int sp_intc_get_ext_irq(int ext_num)
+{
+	u32 hwirq, mask;
+	u32 i;
+
+	i = readl_relaxed(sp_intc.g1 + G1_INTR_GROUP);
+	if (ext_num)
+		mask = (i >> G1_EXT1_SHIFT) & G1_INTR_MASK;
+	else
+		mask = (i >> G1_EXT0_SHIFT) & G1_INTR_MASK;
+	if (mask) {
+		i = fls(mask) - 1;
+		if (ext_num)
+			mask = readl_relaxed(sp_intc.g1 + G1_MASKED_EXT1 + i * 4);
+		else
+			mask = readl_relaxed(sp_intc.g1 + G1_MASKED_EXT0 + i * 4);
+		if (mask) {
+			hwirq = (i << 5) + fls(mask) - 1;
+			return hwirq;
+		}
+	}
What a terrible control flow. Also, variable names are cheap, and you
don't need to reuse them when they mean something different.

	if (ext_num) {
		shift = G1_EXT1_SHIFT;
		offset = G1_MASKED_EXT1;
	} else {
		[[ same thing with G0 ]
	}

	status = readl_relaxed();

	pending_summary = (status >> shift) & G1_INTR_MASK;
	if (!pending_summary)
		return -1;

	index = fls(pending_summary) - 1;
	pending = readl_relaxed(g1 + offset + index * 4);
	if (!pending)
		return -1;

	return (index << 5) | (fls(pending) - 1);

Look: no nesting, obvious names, anyone can understand it.
+	return -1; /* No interrupt */
+}
+
+static void sp_intc_handle_ext_cascaded(struct irq_desc *desc)
+{
+	struct irq_chip *host_chip = irq_desc_get_chip(desc);
+	int ext_num = (int)irq_desc_get_handler_data(desc);
+	int hwirq;
+
+	chained_irq_enter(host_chip, desc);
+
+	while ((hwirq = sp_intc_get_ext_irq(ext_num)) >= 0) {
+		if (edge_trigger[hwirq] & GPIO_INT_EDGE_ACTIVE) {
+			edge_trigger[hwirq] &= ~GPIO_INT_EDGE_ACTIVE;
+			sp_intc_assign_bit(hwirq, sp_intc.g0 + G0_INTR_POLARITY,
+				(edge_trigger[hwirq] != IRQ_TYPE_EDGE_RISING));
+		} else
+			generic_handle_domain_irq(sp_intc.domain, hwirq);
Coding style.
+	}
+
+	chained_irq_exit(host_chip, desc);
+}
+
+static void __exception_irq_entry sp_intc_handle_irq(struct pt_regs *regs)
+{
+	int hwirq;
+
+	while ((hwirq = sp_intc_get_ext_irq(0)) >= 0)
+		generic_handle_domain_irq(sp_intc.domain, hwirq);
+}
+
+static void __init sp_intc_chip_init(void __iomem *base0, void __iomem *base1)
+{
+	int i;
+
+	sp_intc.g0 = base0;
+	sp_intc.g1 = base1;
+
+	for (i = 0; i < 7; i++) {
+		/* all mask */
+		writel_relaxed(0, sp_intc.g0 + G0_INTR_MASK + i * 4);
+		/* all edge */
+		writel_relaxed(~0, sp_intc.g0 + G0_INTR_TYPE + i * 4);
+		/* all high-active */
+		writel_relaxed(0, sp_intc.g0 + G0_INTR_POLARITY + i * 4);
+		/* all irq */
+		writel_relaxed(~0, sp_intc.g0 + G0_INTR_PRIORITY + i * 4);
+		/* all clear */
+		writel_relaxed(~0, sp_intc.g1 + G1_INTR_CLR + i * 4);
+	}
+}
+
+int sp_intc_xlate_of(struct irq_domain *d, struct device_node *node,
+			  const u32 *intspec, unsigned int intsize,
+			  irq_hw_number_t *out_hwirq, unsigned int *out_type)
+{
+	int ret;
+
+	ret = irq_domain_xlate_twocell(d, node,
+		intspec, intsize, out_hwirq, out_type);
+	if (!ret) {
+		/* intspec[1]: IRQ_TYPE | SP_INTC_EXT_INT
+		 * SP_INTC_EXT_INT: 0-1,
+		 *   to indicate route to which parent irq: EXT_INT0/EXT_INT1
+		 */
Why is that in the DT? If that's a SW decision, it doesn't belong there.
+		u32 ext_int = (intspec[1] & SP_INTC_EXT_INT_MASK) >> SP_INTC_EXT_INT_SHFIT;
+
+		/* priority = 0, route to EXT_INT1
+		 *    otherwise, route to EXT_INT0
+		 */
+		sp_intc_set_priority(*out_hwirq, 1 - ext_int);
I already said in the initial review that changing the HW didn't
belong in the translate callback, but should be done at map() time.
+	}
+
+	return ret;
+}
+
+static struct irq_chip sp_intc_chip = {
+	.name = "sp_intc",
+	.irq_ack = sp_intc_ack_irq,
+	.irq_mask = sp_intc_mask_irq,
+	.irq_unmask = sp_intc_unmask_irq,
+	.irq_set_type = sp_intc_set_type,
+};
+
+static int sp_intc_irq_domain_map(struct irq_domain *domain,
+	unsigned int irq, irq_hw_number_t hwirq)
+{
+	irq_set_chip_and_handler(irq, &sp_intc_chip, handle_level_irq);
+	irq_set_chip_data(irq, &sp_intc_chip);
+	irq_set_noprobe(irq);
+
+	return 0;
+}
+
+static const struct irq_domain_ops sp_intc_dm_ops = {
+	.xlate = sp_intc_xlate_of,
+	.map = sp_intc_irq_domain_map,
+};
+
+#ifdef CONFIG_OF
Why the #ifdef? This thing doesn't work without OF anyway.
+static int sp_intc_irq_map(struct device_node *node, int i)
+{
+	sp_intc.virq[i] = irq_of_parse_and_map(node, i);
+	if (!sp_intc.virq[i]) {
+		pr_err("%s: missed EXT_INT%d in DT\n", __func__, i);
-ENOENT is enough, no need to shout on the console.
+		return -ENOENT;
+	}
+
+	pr_info("%s: EXT_INT%d = %d\n", __func__, i, sp_intc.virq[i]);
Nobody cares about this. Get rid of it.
+	irq_set_chained_handler_and_data(sp_intc.virq[i],
+		sp_intc_handle_ext_cascaded, (void *)i);
+
+	return 0;
+}
+
+int __init sp_intc_init_dt(
+	struct device_node *node, struct device_node *parent)
Single line.
+{
+	void __iomem *base0, *base1;
+
+	base0 = of_iomap(node, 0);
+	if (!base0) {
+		pr_err("unable to map sp-intc base 0\n");
+		return -EINVAL;
+	}
+
+	base1 = of_iomap(node, 1);
+	if (!base1) {
+		pr_err("unable to map sp-intc base 1\n");
+		return -EINVAL;
+	}
+
+	sp_intc.node = node;
+
+	sp_intc_chip_init(base0, base1);
+
+	sp_intc.domain = irq_domain_add_linear(node,
+			SP_INTC_HWIRQ_MAX - SP_INTC_HWIRQ_MIN,
+			&sp_intc_dm_ops, &sp_intc);
+	if (!sp_intc.domain) {
+		pr_err("%s: unable to create linear domain\n", __func__);
Drop the error message.
+		return -EINVAL;
+	}
+
+	raw_spin_lock_init(&sp_intc.lock);
+
+	if (parent) {
+		/* secondary chained controller */
+		if (sp_intc_irq_map(node, 0)) // EXT_INT0
+			return -ENOENT;
Just return whatever the helper returned. You don't need to
reinterpret it.
+
+		if (sp_intc_irq_map(node, 1)) // EXT_INT1
+			return -ENOENT;
+	} else {
+		/* primary controller */
+		set_handle_irq(sp_intc_handle_irq);
+	}
So what happens if you have *two* of these blocks? One as the root
controller, and another implementing the chained stuff?
+
+	return 0;
+}
+IRQCHIP_DECLARE(sp_intc, "sunplus,sp7021-intc", sp_intc_init_dt);
+#endif
+
+MODULE_AUTHOR("Qin Jian [off-list ref]");
+MODULE_DESCRIPTION("Sunplus SP7021 Interrupt Controller Driver");
+MODULE_LICENSE("GPL v2");
You are using IRQCHIP_DECLARE(), so this isn't a module. Drop this.

Thankfully, it is too late for 5.16, so you have a full cycle to give
this code another major cleanup.

	M.

-- 
Without deviation from the norm, progress is not possible.

Re: [PATCH v2 4/8] reset: Add Sunplus SP7021 reset driver

From: kernel test robot <hidden>
Date: 2021-10-30 04:19:30

Hi Qin,

I love your patch! Yet something to improve:

[auto build test ERROR on robh/for-next]
[also build test ERROR on pza/reset/next clk/clk-next tip/irq/core linus/master v5.15-rc7 next-20211029]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Qin-Jian/Add-Sunplus-SP7021-SoC-Support/20211029-171054
base:   https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
config: arc-allyesconfig (attached as .config)
compiler: arceb-elf-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/45fff7c04fb4d0ccfbd1ed11f73277d1f8742499
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Qin-Jian/Add-Sunplus-SP7021-SoC-Support/20211029-171054
        git checkout 45fff7c04fb4d0ccfbd1ed11f73277d1f8742499
        # save the attached .config to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=arc SHELL=/bin/bash

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <redacted>

All errors (new ones prefixed by >>):

   drivers/reset/reset-sunplus.c: In function 'sp_restart':
quoted
drivers/reset/reset-sunplus.c:98:42: error: 'RST_SYSTEM' undeclared (first use in this function)
      98 |         sp_reset_assert(&sp_reset.rcdev, RST_SYSTEM);
         |                                          ^~~~~~~~~~
   drivers/reset/reset-sunplus.c:98:42: note: each undeclared identifier is reported only once for each function it appears in
   drivers/reset/reset-sunplus.c: In function 'sp_reset_probe':
quoted
drivers/reset/reset-sunplus.c:135:33: error: 'RST_MAX' undeclared (first use in this function); did you mean 'S8_MAX'?
     135 |         data->rcdev.nr_resets = RST_MAX;
         |                                 ^~~~~~~
         |                                 S8_MAX


vim +/RST_SYSTEM +98 drivers/reset/reset-sunplus.c

    94	
    95	static int sp_restart(struct notifier_block *this, unsigned long mode,
    96					void *cmd)
    97	{
  > 98		sp_reset_assert(&sp_reset.rcdev, RST_SYSTEM);
    99		sp_reset_deassert(&sp_reset.rcdev, RST_SYSTEM);
   100	
   101		return NOTIFY_DONE;
   102	}
   103	
   104	static struct notifier_block sp_restart_nb = {
   105		.notifier_call = sp_restart,
   106		.priority = 192,
   107	};
   108	
   109	static const struct reset_control_ops sp_reset_ops = {
   110		.assert		= sp_reset_assert,
   111		.deassert	= sp_reset_deassert,
   112		.status		= sp_reset_status,
   113	};
   114	
   115	static const struct of_device_id sp_reset_dt_ids[] = {
   116		{ .compatible = "sunplus,sp7021-reset", },
   117		{ .compatible = "sunplus,q645-reset", },
   118		{ /* sentinel */ },
   119	};
   120	
   121	static int sp_reset_probe(struct platform_device *pdev)
   122	{
   123		struct device *dev = &pdev->dev;
   124		struct sp_reset_data *data = &sp_reset;
   125		void __iomem *membase;
   126		struct resource *res;
   127	
   128		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
   129		membase = devm_ioremap(dev, res->start, resource_size(res));
   130		if (IS_ERR(membase))
   131			return PTR_ERR(membase);
   132	
   133		data->membase = membase;
   134		data->rcdev.owner = THIS_MODULE;
 > 135		data->rcdev.nr_resets = RST_MAX;
   136		data->rcdev.ops = &sp_reset_ops;
   137		data->rcdev.of_node = dev->of_node;
   138		register_restart_handler(&sp_restart_nb);
   139	
   140		return devm_reset_controller_register(dev, &data->rcdev);
   141	}
   142	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

Re: [PATCH v2 8/8] irqchip: Add support for Sunplus SP7021 interrupt controller

From: kernel test robot <hidden>
Date: 2021-10-30 15:31:23

Hi Qin,

I love your patch! Yet something to improve:

[auto build test ERROR on robh/for-next]
[also build test ERROR on pza/reset/next clk/clk-next linus/master v5.15-rc7]
[cannot apply to tip/irq/core next-20211029]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Qin-Jian/Add-Sunplus-SP7021-SoC-Support/20211029-171054
base:   https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
config: arc-allyesconfig (attached as .config)
compiler: arceb-elf-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/91ab876ddb6d1a596c50c43a79d3e06b9695dee7
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Qin-Jian/Add-Sunplus-SP7021-SoC-Support/20211029-171054
        git checkout 91ab876ddb6d1a596c50c43a79d3e06b9695dee7
        # save the attached .config to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=arc SHELL=/bin/bash

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <redacted>

All errors (new ones prefixed by >>):
quoted
drivers/irqchip/irq-sp7021-intc.c:14:10: fatal error: asm/exception.h: No such file or directory
      14 | #include <asm/exception.h>
         |          ^~~~~~~~~~~~~~~~~
   compilation terminated.


vim +14 drivers/irqchip/irq-sp7021-intc.c

    13	
  > 14	#include <asm/exception.h>
    15	#include <dt-bindings/interrupt-controller/sp7021-intc.h>
    16	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

Re: [PATCH v2 8/8] irqchip: Add support for Sunplus SP7021 interrupt controller

From: kernel test robot <hidden>
Date: 2021-10-30 19:31:16

Hi Qin,

I love your patch! Yet something to improve:

[auto build test ERROR on robh/for-next]
[also build test ERROR on pza/reset/next clk/clk-next linus/master v5.15-rc7]
[cannot apply to tip/irq/core next-20211029]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Qin-Jian/Add-Sunplus-SP7021-SoC-Support/20211029-171054
base:   https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
config: arm-allyesconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/91ab876ddb6d1a596c50c43a79d3e06b9695dee7
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Qin-Jian/Add-Sunplus-SP7021-SoC-Support/20211029-171054
        git checkout 91ab876ddb6d1a596c50c43a79d3e06b9695dee7
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross ARCH=arm 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <redacted>

All errors (new ones prefixed by >>):
quoted
drivers/irqchip/irq-sp7021-intc.c:211:5: error: no previous prototype for 'sp_intc_xlate_of' [-Werror=missing-prototypes]
     211 | int sp_intc_xlate_of(struct irq_domain *d, struct device_node *node,
         |     ^~~~~~~~~~~~~~~~
quoted
drivers/irqchip/irq-sp7021-intc.c:274:12: error: no previous prototype for 'sp_intc_init_dt' [-Werror=missing-prototypes]
     274 | int __init sp_intc_init_dt(
         |            ^~~~~~~~~~~~~~~
   cc1: all warnings being treated as errors


vim +/sp_intc_xlate_of +211 drivers/irqchip/irq-sp7021-intc.c

   210	
 > 211	int sp_intc_xlate_of(struct irq_domain *d, struct device_node *node,
   212				  const u32 *intspec, unsigned int intsize,
   213				  irq_hw_number_t *out_hwirq, unsigned int *out_type)
   214	{
   215		int ret;
   216	
   217		ret = irq_domain_xlate_twocell(d, node,
   218			intspec, intsize, out_hwirq, out_type);
   219		if (!ret) {
   220			/* intspec[1]: IRQ_TYPE | SP_INTC_EXT_INT
   221			 * SP_INTC_EXT_INT: 0-1,
   222			 *   to indicate route to which parent irq: EXT_INT0/EXT_INT1
   223			 */
   224			u32 ext_int = (intspec[1] & SP_INTC_EXT_INT_MASK) >> SP_INTC_EXT_INT_SHFIT;
   225	
   226			/* priority = 0, route to EXT_INT1
   227			 *    otherwise, route to EXT_INT0
   228			 */
   229			sp_intc_set_priority(*out_hwirq, 1 - ext_int);
   230		}
   231	
   232		return ret;
   233	}
   234	
   235	static struct irq_chip sp_intc_chip = {
   236		.name = "sp_intc",
   237		.irq_ack = sp_intc_ack_irq,
   238		.irq_mask = sp_intc_mask_irq,
   239		.irq_unmask = sp_intc_unmask_irq,
   240		.irq_set_type = sp_intc_set_type,
   241	};
   242	
   243	static int sp_intc_irq_domain_map(struct irq_domain *domain,
   244		unsigned int irq, irq_hw_number_t hwirq)
   245	{
   246		irq_set_chip_and_handler(irq, &sp_intc_chip, handle_level_irq);
   247		irq_set_chip_data(irq, &sp_intc_chip);
   248		irq_set_noprobe(irq);
   249	
   250		return 0;
   251	}
   252	
   253	static const struct irq_domain_ops sp_intc_dm_ops = {
   254		.xlate = sp_intc_xlate_of,
   255		.map = sp_intc_irq_domain_map,
   256	};
   257	
   258	#ifdef CONFIG_OF
   259	static int sp_intc_irq_map(struct device_node *node, int i)
   260	{
   261		sp_intc.virq[i] = irq_of_parse_and_map(node, i);
   262		if (!sp_intc.virq[i]) {
   263			pr_err("%s: missed EXT_INT%d in DT\n", __func__, i);
   264			return -ENOENT;
   265		}
   266	
   267		pr_info("%s: EXT_INT%d = %d\n", __func__, i, sp_intc.virq[i]);
   268		irq_set_chained_handler_and_data(sp_intc.virq[i],
   269			sp_intc_handle_ext_cascaded, (void *)i);
   270	
   271		return 0;
   272	}
   273	
 > 274	int __init sp_intc_init_dt(
   275		struct device_node *node, struct device_node *parent)
   276	{
   277		void __iomem *base0, *base1;
   278	
   279		base0 = of_iomap(node, 0);
   280		if (!base0) {
   281			pr_err("unable to map sp-intc base 0\n");
   282			return -EINVAL;
   283		}
   284	
   285		base1 = of_iomap(node, 1);
   286		if (!base1) {
   287			pr_err("unable to map sp-intc base 1\n");
   288			return -EINVAL;
   289		}
   290	
   291		sp_intc.node = node;
   292	
   293		sp_intc_chip_init(base0, base1);
   294	
   295		sp_intc.domain = irq_domain_add_linear(node,
   296				SP_INTC_HWIRQ_MAX - SP_INTC_HWIRQ_MIN,
   297				&sp_intc_dm_ops, &sp_intc);
   298		if (!sp_intc.domain) {
   299			pr_err("%s: unable to create linear domain\n", __func__);
   300			return -EINVAL;
   301		}
   302	
   303		raw_spin_lock_init(&sp_intc.lock);
   304	
   305		if (parent) {
   306			/* secondary chained controller */
   307			if (sp_intc_irq_map(node, 0)) // EXT_INT0
   308				return -ENOENT;
   309	
   310			if (sp_intc_irq_map(node, 1)) // EXT_INT1
   311				return -ENOENT;
   312		} else {
   313			/* primary controller */
   314			set_handle_irq(sp_intc_handle_irq);
   315		}
   316	
   317		return 0;
   318	}
   319	IRQCHIP_DECLARE(sp_intc, "sunplus,sp7021-intc", sp_intc_init_dt);
   320	#endif
   321	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[PATCH v3 4/8] reset: Add Sunplus SP7021 reset driver

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-11-01 05:20:34

Add reset driver for Sunplus SP7021 SoC.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 MAINTAINERS                   |   1 +
 drivers/reset/Kconfig         |   8 ++
 drivers/reset/Makefile        |   1 +
 drivers/reset/reset-sunplus.c | 159 ++++++++++++++++++++++++++++++++++
 4 files changed, 169 insertions(+)
 create mode 100644 drivers/reset/reset-sunplus.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 652f42cab..6caffd6d0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2662,6 +2662,7 @@ S:	Maintained
 W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
 F:	Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
 F:	Documentation/devicetree/bindings/reset/sunplus,reset.yaml
+F:	drivers/reset/reset-sunplus.c
 F:	include/dt-bindings/reset/sp-sp7021.h
 
 ARM/Synaptics SoC support
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index be799a5ab..50695ab47 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -249,6 +249,14 @@ config RESET_TI_SYSCON
 	  you wish to use the reset framework for such memory-mapped devices,
 	  say Y here. Otherwise, say N.
 
+config RESET_SUNPLUS
+	bool "Sunplus SoCs Reset Driver"
+	help
+	  This enables the reset driver support for Sunplus SP7021 SoC family.
+	  Say Y if you want to control reset signals by the reset controller.
+	  Otherwise, say N.
+	  This driver is selected automatically by platform config.
+
 config RESET_UNIPHIER
 	tristate "Reset controller driver for UniPhier SoCs"
 	depends on ARCH_UNIPHIER || COMPILE_TEST
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 21d46d886..f03403e97 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o
 obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
 obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
 obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
+obj-$(CONFIG_RESET_SUNPLUS) += reset-sunplus.o
 obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
 obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o
 obj-$(CONFIG_RESET_TI_SYSCON) += reset-ti-syscon.o
diff --git a/drivers/reset/reset-sunplus.c b/drivers/reset/reset-sunplus.c
new file mode 100644
index 000000000..696efd75e
--- /dev/null
+++ b/drivers/reset/reset-sunplus.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/*
+ * SP7021 reset driver
+ *
+ * Copyright (C) Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/reboot.h>
+
+#if defined(CONFIG_SOC_SP7021)
+#include <dt-bindings/reset/sp-sp7021.h>
+#elif defined(CONFIG_SOC_Q645)
+#include <dt-bindings/reset/sp-q645.h>
+#endif
+
+#define BITASSERT(id, val)          ((1 << (16 + id)) | (val << id))
+
+
+struct sp_reset_data {
+	struct reset_controller_dev	rcdev;
+	void __iomem			*membase;
+} sp_reset;
+
+
+static inline struct sp_reset_data *
+to_sp_reset_data(struct reset_controller_dev *rcdev)
+{
+	return container_of(rcdev, struct sp_reset_data, rcdev);
+}
+
+static int sp_reset_update(struct reset_controller_dev *rcdev,
+			      unsigned long id, bool assert)
+{
+	struct sp_reset_data *data = to_sp_reset_data(rcdev);
+	int reg_width = sizeof(u32)/2;
+	int bank = id / (reg_width * BITS_PER_BYTE);
+	int offset = id % (reg_width * BITS_PER_BYTE);
+	void __iomem *addr;
+
+	addr = data->membase + (bank * 4);
+
+	if (assert)
+		writel(BITASSERT(offset, 1), addr);
+	else
+		writel(BITASSERT(offset, 0), addr);
+
+	return 0;
+}
+
+static int sp_reset_assert(struct reset_controller_dev *rcdev,
+			      unsigned long id)
+{
+	return sp_reset_update(rcdev, id, true);
+}
+
+
+static int sp_reset_deassert(struct reset_controller_dev *rcdev,
+				unsigned long id)
+{
+	return sp_reset_update(rcdev, id, false);
+}
+
+static int sp_reset_status(struct reset_controller_dev *rcdev,
+			      unsigned long id)
+{
+	struct sp_reset_data *data = to_sp_reset_data(rcdev);
+	int reg_width = sizeof(u32)/2;
+	int bank = id / (reg_width * BITS_PER_BYTE);
+	int offset = id % (reg_width * BITS_PER_BYTE);
+	u32 reg;
+
+	reg = readl(data->membase + (bank * 4));
+
+	return !!(reg & BIT(offset));
+}
+
+static int sp_restart(struct notifier_block *this, unsigned long mode,
+				void *cmd)
+{
+	sp_reset_assert(&sp_reset.rcdev, RST_SYSTEM);
+	sp_reset_deassert(&sp_reset.rcdev, RST_SYSTEM);
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block sp_restart_nb = {
+	.notifier_call = sp_restart,
+	.priority = 192,
+};
+
+static const struct reset_control_ops sp_reset_ops = {
+	.assert		= sp_reset_assert,
+	.deassert	= sp_reset_deassert,
+	.status		= sp_reset_status,
+};
+
+static const struct of_device_id sp_reset_dt_ids[] = {
+	{ .compatible = "sunplus,sp7021-reset", },
+	{ .compatible = "sunplus,q645-reset", },
+	{ /* sentinel */ },
+};
+
+static int sp_reset_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct sp_reset_data *data = &sp_reset;
+	void __iomem *membase;
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	membase = devm_ioremap(dev, res->start, resource_size(res));
+	if (IS_ERR(membase))
+		return PTR_ERR(membase);
+
+	data->membase = membase;
+	data->rcdev.owner = THIS_MODULE;
+	data->rcdev.nr_resets = RST_MAX;
+	data->rcdev.ops = &sp_reset_ops;
+	data->rcdev.of_node = dev->of_node;
+	register_restart_handler(&sp_restart_nb);
+
+	return devm_reset_controller_register(dev, &data->rcdev);
+}
+
+static struct platform_driver sp_reset_driver = {
+	.probe	= sp_reset_probe,
+	.driver = {
+		.name = "sunplus-reset",
+		.of_match_table	= sp_reset_dt_ids,
+	},
+};
+
+static int __init sp_reset_init(void)
+{
+	return platform_driver_register(&sp_reset_driver);
+}
+arch_initcall(sp_reset_init);
+
+MODULE_AUTHOR("Edwin Chiu <edwin.chiu@sunplus.com>");
+MODULE_DESCRIPTION("Sunplus Reset Driver");
+MODULE_LICENSE("GPL");
-- 
2.33.1

[PATCH v3 0/8] Add Sunplus SP7021 SoC Support

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-11-01 05:20:36

This patch series add Sunplus SP7021 SoC support.

Sunplus SP7021 is an ARM Cortex A7 (4 cores) based SoC. It integrates many
peripherals (ex: UART, I2C, SPI, SDIO, eMMC, USB, SD card and etc.) into a
single chip. It is designed for industrial control.

SP7021 consists of two chips (dies) in a package. One is called C-chip
(computing chip). It is a 4-core ARM Cortex A7 CPU. It adopts high-level
process (22 nm) for high performance computing. The other is called P-
chip (peripheral chip). It has many peripherals and an ARM A926 added
especially for real-time control. P-chip is made for customers. It adopts
low-level process (ex: 0.11 um) to reduce cost.

Refer to (for documentations):
https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview

Refer to (applications):
https://tibbo.com/store/plus1.html

Refer to (applications):
http://www.sinovoip.com.cn/ecp_view.asp?id=586

Changes in v3:
- sp7021-intc: remove primary controller mode due to P-chip running Linux
  not supported any more.
- sp7021-intc.h: removed, not set ext through the DT but sp_intc_set_ext()
- sunplus,sp7021-intc.yaml: update descriptions for above changes
- irq-sp7021-intc.c: more cleanup based on Marc's review
- all driver's Kconfig removed default, it's selected by platform config

Changes in v2:
- sunplus,sp7021-intc.yaml: add descrption for "#interrupt-cells", interrupts
- sunplus,sp7021-intc.yaml: drop "ext0-mask"/"ext1-mask" from DT
- sunplus,sp7021-intc.yaml: fix example.dt too long error
- irq-sp7021-intc.c: major rewrite
- all files with dual license

Qin Jian (8):
  dt-bindings: vendor-prefixes: Add Sunplus
  dt-bindings: arm: sunplus: Add bindings for Sunplus SP7021 SoC boards
  dt-bindings: reset: Add bindings for SP7021 reset driver
  reset: Add Sunplus SP7021 reset driver
  dt-bindings: clock: Add bindings for SP7021 clock driver
  clk: Add Sunplus SP7021 clock driver
  dt-bindings: interrupt-controller: Add bindings for SP7021 interrupt
    controller
  irqchip: Add Sunplus SP7021 interrupt controller driver

 .../bindings/arm/sunplus,sp7021.yaml          |  27 +
 .../bindings/clock/sunplus,sp7021-clkc.yaml   |  38 +
 .../sunplus,sp7021-intc.yaml                  |  62 ++
 .../bindings/reset/sunplus,reset.yaml         |  40 +
 .../devicetree/bindings/vendor-prefixes.yaml  |   2 +
 MAINTAINERS                                   |  15 +
 drivers/clk/Kconfig                           |   9 +
 drivers/clk/Makefile                          |   1 +
 drivers/clk/clk-sp7021.c                      | 770 ++++++++++++++++++
 drivers/irqchip/Kconfig                       |   9 +
 drivers/irqchip/Makefile                      |   1 +
 drivers/irqchip/irq-sp7021-intc.c             | 279 +++++++
 drivers/reset/Kconfig                         |   8 +
 drivers/reset/Makefile                        |   1 +
 drivers/reset/reset-sunplus.c                 | 159 ++++
 include/dt-bindings/clock/sp-sp7021.h         | 112 +++
 include/dt-bindings/reset/sp-sp7021.h         |  99 +++
 17 files changed, 1632 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
 create mode 100644 Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml
 create mode 100644 Documentation/devicetree/bindings/reset/sunplus,reset.yaml
 create mode 100644 drivers/clk/clk-sp7021.c
 create mode 100644 drivers/irqchip/irq-sp7021-intc.c
 create mode 100644 drivers/reset/reset-sunplus.c
 create mode 100644 include/dt-bindings/clock/sp-sp7021.h
 create mode 100644 include/dt-bindings/reset/sp-sp7021.h

-- 
2.33.1

[PATCH v3 3/8] dt-bindings: reset: Add bindings for SP7021 reset driver

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-11-01 05:20:40

Add documentation to describe Sunplus SP7021 reset driver bindings.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 .../bindings/reset/sunplus,reset.yaml         | 40 ++++++++
 MAINTAINERS                                   |  2 +
 include/dt-bindings/reset/sp-sp7021.h         | 99 +++++++++++++++++++
 3 files changed, 141 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/reset/sunplus,reset.yaml
 create mode 100644 include/dt-bindings/reset/sp-sp7021.h
diff --git a/Documentation/devicetree/bindings/reset/sunplus,reset.yaml b/Documentation/devicetree/bindings/reset/sunplus,reset.yaml
new file mode 100644
index 000000000..bf55f4ee2
--- /dev/null
+++ b/Documentation/devicetree/bindings/reset/sunplus,reset.yaml
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) Sunplus Co., Ltd. 2021
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/reset/sunplus,reset.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Sunplus SoC Reset Controller
+
+maintainers:
+  - Qin Jian <qinjian@cqplus1.com>
+
+properties:
+  compatible:
+    enum:
+      - sunplus,sp7021-reset # Reset Controller on SP7021 and compatible SoCs
+      - sunplus,q645-reset # Reset Controller on Q645 and compatible SoCs
+
+  "#reset-cells":
+    const: 1
+
+  reg:
+    maxItems: 1
+
+required:
+  - compatible
+  - "#reset-cells"
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    rstc: reset@9c000054 {
+      compatible = "sunplus,sp7021-reset";
+      #reset-cells = <1>;
+      reg = <0x9c000054 0x28>;
+    };
+
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 6a5422f10..652f42cab 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2661,6 +2661,8 @@ L:	linux-arm-kernel@lists.infradead.org (moderated for mon-subscribers)
 S:	Maintained
 W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
 F:	Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
+F:	Documentation/devicetree/bindings/reset/sunplus,reset.yaml
+F:	include/dt-bindings/reset/sp-sp7021.h
 
 ARM/Synaptics SoC support
 M:	Jisheng Zhang <Jisheng.Zhang@synaptics.com>
diff --git a/include/dt-bindings/reset/sp-sp7021.h b/include/dt-bindings/reset/sp-sp7021.h
new file mode 100644
index 000000000..8823d25ce
--- /dev/null
+++ b/include/dt-bindings/reset/sp-sp7021.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright (C) Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+#ifndef _DT_BINDINGS_RST_SUNPLUS_SP7021_H
+#define _DT_BINDINGS_RST_SUNPLUS_SP7021_H
+
+/* mo_reset0 ~ mo_reset9 */
+#define RST_SYSTEM		0x00
+#define	RST_RTC			0x02
+#define RST_IOCTL		0x03
+#define RST_IOP			0x04
+#define RST_OTPRX		0x05
+#define RST_NOC			0x06
+#define RST_BR			0x07
+#define RST_RBUS_L00		0x08
+#define RST_SPIFL		0x09
+#define RST_SDCTRL0		0x0a
+#define RST_PERI0		0x0b
+#define RST_A926		0x0d
+#define RST_UMCTL2		0x0e
+#define RST_PERI1		0x0f
+
+#define RST_DDR_PHY0		0x10
+#define RST_ACHIP		0x12
+#define RST_STC0		0x14
+#define RST_STC_AV0		0x15
+#define RST_STC_AV1		0x16
+#define RST_STC_AV2		0x17
+#define RST_UA0			0x18
+#define RST_UA1			0x19
+#define RST_UA2			0x1a
+#define RST_UA3			0x1b
+#define RST_UA4			0x1c
+#define RST_HWUA		0x1d
+#define RST_DDC0		0x1e
+#define RST_UADMA		0x1f
+
+#define RST_CBDMA0		0x20
+#define RST_CBDMA1		0x21
+#define RST_SPI_COMBO_0		0x22
+#define RST_SPI_COMBO_1		0x23
+#define RST_SPI_COMBO_2		0x24
+#define RST_SPI_COMBO_3		0x25
+#define RST_AUD			0x26
+#define RST_USBC0		0x2a
+#define RST_USBC1		0x2b
+#define RST_UPHY0		0x2d
+#define RST_UPHY1		0x2e
+
+#define RST_I2CM0		0x30
+#define RST_I2CM1		0x31
+#define RST_I2CM2		0x32
+#define RST_I2CM3		0x33
+#define RST_PMC			0x3d
+#define RST_CARD_CTL0		0x3e
+#define RST_CARD_CTL1		0x3f
+
+#define RST_CARD_CTL4		0x42
+#define RST_BCH			0x44
+#define RST_DDFCH		0x4b
+#define RST_CSIIW0		0x4c
+#define RST_CSIIW1		0x4d
+#define RST_MIPICSI0		0x4e
+#define RST_MIPICSI1		0x4f
+
+#define RST_HDMI_TX		0x50
+#define RST_VPOST		0x55
+
+#define RST_TGEN		0x60
+#define RST_DMIX		0x61
+#define RST_TCON		0x6a
+#define RST_INTERRUPT		0x6f
+
+#define RST_RGST		0x70
+#define RST_GPIO		0x73
+#define RST_RBUS_TOP		0x74
+
+#define RST_MAILBOX		0x86
+#define RST_SPIND		0x8a
+#define RST_I2C2CBUS		0x8b
+#define RST_SEC			0x8d
+#define RST_DVE			0x8e
+#define RST_GPOST0		0x8f
+
+#define RST_OSD0		0x90
+#define RST_DISP_PWM		0x92
+#define RST_UADBG		0x93
+#define RST_DUMMY_MASTER	0x94
+#define RST_FIO_CTL		0x95
+#define RST_FPGA		0x96
+#define RST_L2SW		0x97
+#define RST_ICM			0x98
+#define RST_AXI_GLOBAL		0x99
+
+#define RST_MAX			0xA0
+
+#endif
-- 
2.33.1

[PATCH v3 6/8] clk: Add Sunplus SP7021 clock driver

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-11-01 05:20:40

Add clock driver for Sunplus SP7021 SoC.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 MAINTAINERS              |   1 +
 drivers/clk/Kconfig      |   9 +
 drivers/clk/Makefile     |   1 +
 drivers/clk/clk-sp7021.c | 770 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 781 insertions(+)
 create mode 100644 drivers/clk/clk-sp7021.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 90ebb823f..5069f552f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2663,6 +2663,7 @@ W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
 F:	Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
 F:	Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
 F:	Documentation/devicetree/bindings/reset/sunplus,reset.yaml
+F:	drivers/clk/clk-sp7021.c
 F:	drivers/reset/reset-sunplus.c
 F:	include/dt-bindings/clock/sp-sp7021.h
 F:	include/dt-bindings/reset/sp-sp7021.h
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index c5b3dc973..1cd7ae7a3 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -334,6 +334,15 @@ config COMMON_CLK_VC5
 	  This driver supports the IDT VersaClock 5 and VersaClock 6
 	  programmable clock generators.
 
+config COMMON_CLK_SP7021
+	bool "Clock driver for Sunplus SP7021 SoC"
+	help
+	  This driver supports the Sunplus SP7021 SoC clocks.
+	  It implemented SP7021 PLLs/gate.
+	  Not all features of the PLL are currently supported
+	  by the driver.
+	  This driver is selected automatically by platform config.
+
 config COMMON_CLK_STM32MP157
 	def_bool COMMON_CLK && MACH_STM32MP157
 	help
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index e42312121..f15bb5070 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_COMMON_CLK_SI5351)		+= clk-si5351.o
 obj-$(CONFIG_COMMON_CLK_SI514)		+= clk-si514.o
 obj-$(CONFIG_COMMON_CLK_SI544)		+= clk-si544.o
 obj-$(CONFIG_COMMON_CLK_SI570)		+= clk-si570.o
+obj-$(CONFIG_COMMON_CLK_SP7021)		+= clk-sp7021.o
 obj-$(CONFIG_COMMON_CLK_STM32F)		+= clk-stm32f4.o
 obj-$(CONFIG_COMMON_CLK_STM32H7)	+= clk-stm32h7.o
 obj-$(CONFIG_COMMON_CLK_STM32MP157)	+= clk-stm32mp1.o
diff --git a/drivers/clk/clk-sp7021.c b/drivers/clk/clk-sp7021.c
new file mode 100644
index 000000000..39ff84825
--- /dev/null
+++ b/drivers/clk/clk-sp7021.c
@@ -0,0 +1,770 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/*
+ * Copyright (C) Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+//#define DEBUG
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <dt-bindings/clock/sp-sp7021.h>
+
+//#define TRACE	pr_info("### %s:%d (%d)\n", __func__, __LINE__, (clk->reg - REG(4, 0)) / 4)
+#define TRACE
+
+#ifndef clk_readl
+#define clk_readl  readl
+#define clk_writel writel
+#endif
+
+#define MASK_SET(shift, width, value) \
+({ \
+	u32 m = ((1 << (width)) - 1) << (shift); \
+	(m << 16) | (((value) << (shift)) & m); \
+})
+#define MASK_GET(shift, width, value)	(((value) >> (shift)) & ((1 << (width)) - 1))
+
+#define REG(i)	(pll_regs + (i) * 4)
+
+#define PLLA_CTL	REG(7)
+#define PLLE_CTL	REG(12)
+#define PLLF_CTL	REG(13)
+#define PLLTV_CTL	REG(14)
+#define PLLSYS_CTL	REG(26)
+
+#define EXTCLK		"extclk"
+
+/* speical div_width values for PLLTV/PLLA */
+#define DIV_TV	33
+#define DIV_A	34
+
+/* PLLTV parameters */
+enum {
+	SEL_FRA,
+	SDM_MOD,
+	PH_SEL,
+	NFRA,
+	DIVR,
+	DIVN,
+	DIVM,
+	P_MAX
+};
+
+struct sp_pll {
+	struct clk_hw	hw;
+	void __iomem	*reg;
+	spinlock_t	*lock;
+	int		pd_bit;		/* power down bit idx */
+	int		bp_bit;		/* bypass bit idx */
+	unsigned long	brate;		/* base rate, FIXME: replace brate with muldiv */
+	int		div_shift;
+	int		div_width;
+	u32		p[P_MAX];	/* for hold PLLTV/PLLA parameters */
+};
+#define to_sp_pll(_hw)	container_of(_hw, struct sp_pll, hw)
+
+#define clk_regs	(moon_regs + 0x000) /* G0 ~ CLKEN */
+#define pll_regs	(moon_regs + 0x200) /* G4 ~ PLL */
+static void __iomem *moon_regs;
+
+#define P_EXTCLK	(1 << 16)
+static const char * const parents[] = {
+	"pllsys",
+	"extclk",
+};
+
+/* FIXME: parent clk incorrect cause clk_get_rate got error value */
+static const u32 gates[] = {
+	SYSTEM,
+	RTC,
+	IOCTL,
+	IOP,
+	OTPRX,
+	NOC,
+	BR,
+	RBUS_L00,
+	SPIFL,
+	SDCTRL0,
+	PERI0 | P_EXTCLK,
+	A926,
+	UMCTL2,
+	PERI1 | P_EXTCLK,
+
+	DDR_PHY0,
+	ACHIP,
+	STC0,
+	STC_AV0,
+	STC_AV1,
+	STC_AV2,
+	UA0 | P_EXTCLK,
+	UA1 | P_EXTCLK,
+	UA2 | P_EXTCLK,
+	UA3 | P_EXTCLK,
+	UA4 | P_EXTCLK,
+	HWUA | P_EXTCLK,
+	DDC0,
+	UADMA | P_EXTCLK,
+
+	CBDMA0,
+	CBDMA1,
+	SPI_COMBO_0,
+	SPI_COMBO_1,
+	SPI_COMBO_2,
+	SPI_COMBO_3,
+	AUD,
+	USBC0,
+	USBC1,
+	UPHY0,
+	UPHY1,
+
+	I2CM0,
+	I2CM1,
+	I2CM2,
+	I2CM3,
+	PMC,
+	CARD_CTL0,
+	CARD_CTL1,
+
+	CARD_CTL4,
+	BCH,
+	DDFCH,
+	CSIIW0,
+	CSIIW1,
+	MIPICSI0,
+	MIPICSI1,
+
+	HDMI_TX,
+	VPOST,
+
+	TGEN,
+	DMIX,
+	TCON,
+	INTERRUPT,
+
+	RGST,
+	GPIO,
+	RBUS_TOP,
+
+	MAILBOX,
+	SPIND,
+	I2C2CBUS,
+	SEC,
+	GPOST0,
+	DVE,
+
+	OSD0,
+	DISP_PWM,
+	UADBG,
+	DUMMY_MASTER,
+	FIO_CTL,
+	FPGA,
+	L2SW,
+	ICM,
+	AXI_GLOBAL,
+};
+static struct clk *clks[CLK_MAX];
+static struct clk_onecell_data clk_data;
+
+static DEFINE_SPINLOCK(plla_lock);
+static DEFINE_SPINLOCK(plle_lock);
+static DEFINE_SPINLOCK(pllf_lock);
+static DEFINE_SPINLOCK(pllsys_lock);
+static DEFINE_SPINLOCK(plltv_lock);
+
+#define _M		1000000UL
+#define F_27M		(27 * _M)
+
+/******************************************** PLL_TV *******************************************/
+
+//#define PLLTV_STEP_DIR (?) /* Unit: HZ */
+
+/* TODO: set proper FVCO range */
+#define FVCO_MIN	(100 * _M)
+#define FVCO_MAX	(200 * _M)
+
+#define F_MIN		(FVCO_MIN / 8)
+#define F_MAX		(FVCO_MAX)
+
+static long plltv_integer_div(struct sp_pll *clk, unsigned long freq)
+{
+	/* valid m values: 27M must be divisible by m, 0 means end */
+	static const u32 m_table[] = {
+		1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 27, 30, 32, 0
+	};
+	u32 m, n, r;
+#ifdef PLLTV_STEP_DIR
+	u32 step = (PLLTV_STEP_DIR > 0) ? PLLTV_STEP_DIR : -PLLTV_STEP_DIR;
+	int calc_times = 1000000 / step;
+#endif
+	unsigned long fvco, nf;
+
+	TRACE;
+
+	/* check freq */
+	if (freq < F_MIN) {
+		pr_warn("[%s:%d] freq:%lu < F_MIN:%lu, round up\n",
+			__func__, __LINE__, freq, F_MIN);
+		freq = F_MIN;
+	} else if (freq > F_MAX) {
+		pr_warn("[%s:%d] freq:%lu > F_MAX:%lu, round down\n",
+			__func__, __LINE__, freq, F_MAX);
+		freq = F_MAX;
+	}
+
+#ifdef PLLTV_STEP_DIR
+	if ((freq % step) != 0)
+		freq += step - (freq % step) + ((PLLTV_STEP_DIR > 0) ? 0 : PLLTV_STEP_DIR);
+#endif
+
+#ifdef PLLTV_STEP_DIR
+CALC:
+	if (!calc_times) {
+		pr_err("[%s:%d] freq:%lu out of recalc times\n", __func__, __LINE__, freq);
+		return -ETIMEOUT;
+	}
+#endif
+
+	/* DIVR 0~3 */
+	for (r = 0; r <= 3; r++) {
+		fvco = freq << r;
+		if (fvco <= FVCO_MAX)
+			break;
+	}
+
+	/* DIVM */
+	for (m = 0; m_table[m]; m++) {
+		nf = fvco * m_table[m];
+		n = nf / F_27M;
+		if ((n * F_27M) == nf)
+			break;
+	}
+	m = m_table[m];
+
+	if (!m) {
+#ifdef PLLTV_STEP_DIR
+		freq += PLLTV_STEP_DIR;
+		calc_times--;
+		goto CALC;
+#else
+		pr_err("[%s:%d] freq:%lu not found a valid setting\n", __func__, __LINE__, freq);
+		return -EINVAL;
+#endif
+	}
+
+	/* save parameters */
+	clk->p[SEL_FRA] = 0;
+	clk->p[DIVR]    = r;
+	clk->p[DIVN]    = n;
+	clk->p[DIVM]    = m;
+
+	pr_info("[%s:%d]   M:%u N:%u R:%u   CKREF:%lu  FVCO:%lu  FCKOUT:%lu\n",
+		__func__, __LINE__, m, n, r, (fvco / m), fvco, freq);
+
+	return freq;
+}
+
+/* parameters for PLLTV fractional divider */
+/* FIXME: better parameter naming */
+static const u32 pt[][5] = {
+	/* conventional fractional */
+	{
+		1,			// factor
+		5,			// 5 * p0 (nint)
+		1,			// 1 * p0
+		F_27M,			// F_27M / p0
+		1,			// p0 / p2
+	},
+	/* phase rotation */
+	{
+		10,			// factor
+		54,			// 5.4 * p0 (nint)
+		2,			// 0.2 * p0
+		F_27M / 10,		// F_27M / p0
+		5,			// p0 / p2
+	},
+};
+static const u32 mods[] = { 91, 55 }; /* SDM_MOD mod values */
+
+static long plltv_fractional_div(struct sp_pll *clk, unsigned long freq)
+{
+	u32 m, r;
+	u32 nint, nfra;
+	u32 diff_min_quotient = 210000000, diff_min_remainder = 0;
+	u32 diff_min_sign = 0;
+	unsigned long fvco, nf, f, fout = 0;
+	int sdm, ph;
+
+	TRACE;
+	/* check freq */
+	if (freq < F_MIN) {
+		pr_warn("[%s:%d] freq:%lu < F_MIN:%lu, round up\n",
+			__func__, __LINE__, freq, F_MIN);
+		freq = F_MIN;
+	} else if (freq > F_MAX) {
+		pr_warn("[%s:%d] freq:%lu > F_MAX:%lu, round down\n",
+			__func__, __LINE__, freq, F_MAX);
+		freq = F_MAX;
+	}
+
+	/* DIVR 0~3 */
+	for (r = 0; r <= 3; r++) {
+		fvco = freq << r;
+		if (fvco <= FVCO_MAX)
+			break;
+	}
+	pr_info("freq:%lu fvco:%lu R:%u\n", freq, fvco, r);
+	f = F_27M >> r;
+
+	/* PH_SEL 1/0 */
+	for (ph = 1; ph >= 0; ph--) {
+		const u32 *pp = pt[ph];
+		u32 ms = 1;
+
+		/* SDM_MOD 0/1 */
+		for (sdm = 0; sdm <= 1; sdm++) {
+			u32 mod = mods[sdm];
+
+			/* DIVM 1~32 */
+			for (m = ms; m <= 32; m++) {
+				u32 diff_freq;
+				u32 diff_freq_quotient = 0, diff_freq_remainder = 0;
+				u32 diff_freq_sign = 0; /* 0:Positive number, 1:Negative number */
+
+				nf = fvco * m;
+				nint = nf / pp[3];
+
+				if (nint < pp[1])
+					continue;
+				if (nint > pp[1])
+					break;
+
+				nfra = (((nf % pp[3]) * mod * pp[4]) + (F_27M / 2)) / F_27M;
+				if (nfra)
+					diff_freq = (f * (nint + pp[2]) / pp[0]) -
+								(f * (mod - nfra) / mod / pp[4]);
+				else
+					diff_freq = (f * (nint) / pp[0]);
+
+				diff_freq_quotient  = diff_freq / m;
+				diff_freq_remainder = ((diff_freq % m) * 1000) / m;
+
+				pr_info("m = %d N.f = %2d.%03d%03d, nfra = %d/%d  fout = %u\n",
+					m, nint, (nfra * 1000) / mod,
+					(((nfra * 1000) % mod) * 1000) / mod,
+					nfra, mod, diff_freq_quotient);
+
+				if (freq > diff_freq_quotient) {
+					diff_freq_quotient  = freq - diff_freq_quotient - 1;
+					diff_freq_remainder = 1000 - diff_freq_remainder;
+					diff_freq_sign = 1;
+				} else {
+					diff_freq_quotient = diff_freq_quotient - freq;
+					diff_freq_sign = 0;
+				}
+
+				if ((diff_min_quotient > diff_freq_quotient) ||
+					((diff_min_quotient == diff_freq_quotient) &&
+					(diff_min_remainder > diff_freq_remainder))) {
+
+					/* found a closer freq, save parameters */
+					TRACE;
+					clk->p[SEL_FRA] = 1;
+					clk->p[SDM_MOD] = sdm;
+					clk->p[PH_SEL]  = ph;
+					clk->p[NFRA]    = nfra;
+					clk->p[DIVR]    = r;
+					clk->p[DIVM]    = m;
+
+					fout = diff_freq / m;
+					diff_min_quotient = diff_freq_quotient;
+					diff_min_remainder = diff_freq_remainder;
+					diff_min_sign = diff_freq_sign;
+				}
+			}
+		}
+	}
+
+	if (!fout) {
+		pr_err("[%s:%d] freq:%lu not found a valid setting\n", __func__, __LINE__, freq);
+		return -EINVAL;
+	}
+
+	//pr_info("MOD:%u PH_SEL:%u NFRA:%u M:%u R:%u\n",
+	//	mods[clk->p[SDM_MOD]], clk->p[PH_SEL], clk->p[NFRA], clk->p[DIVM], clk->p[DIVR]);
+
+	pr_info("[%s:%d] real out:%lu/%lu Hz(%u, %u, sign %u)\n",
+		__func__, __LINE__, fout, freq,
+		diff_min_quotient, diff_min_remainder, diff_min_sign);
+
+	return fout;
+}
+
+static long plltv_div(struct sp_pll *clk, unsigned long freq)
+{
+	TRACE;
+	if (freq % 100)
+		return plltv_fractional_div(clk, freq);
+	else
+		return plltv_integer_div(clk, freq);
+}
+
+static void plltv_set_rate(struct sp_pll *clk)
+{
+	u32 reg;
+
+	//pr_info("MOD:%u PH_SEL:%u NFRA:%u M:%u R:%u\n",
+	//	mods[clk->p[SDM_MOD]], clk->p[PH_SEL], clk->p[NFRA], clk->p[DIVM], clk->p[DIVR]);
+	reg  = MASK_SET(1, 1, clk->p[SEL_FRA]);
+	reg |= MASK_SET(2, 1, clk->p[SDM_MOD]);
+	reg |= MASK_SET(4, 1, clk->p[PH_SEL]);
+	reg |= MASK_SET(6, 7, clk->p[NFRA]);
+	clk_writel(reg, clk->reg);
+
+	reg  = MASK_SET(7, 2, clk->p[DIVR]);
+	clk_writel(reg, clk->reg + 4);
+
+	reg  = MASK_SET(0, 8, clk->p[DIVN] - 1);
+	reg |= MASK_SET(8, 7, clk->p[DIVM] - 1);
+	clk_writel(reg, clk->reg + 8);
+}
+
+/******************************************** PLL_A ********************************************/
+
+/* from Q628_PLLs_REG_setting.xlsx */
+struct {
+	u32 rate;
+	u32 regs[5];
+} pa[] = {
+	{
+		.rate = 135475200,
+		.regs = {
+			0x4801,
+			0x02df,
+			0x248f,
+			0x0211,
+			0x33e9
+		}
+	},
+	{
+		.rate = 147456000,
+		.regs = {
+			0x4801,
+			0x1adf,
+			0x2490,
+			0x0349,
+			0x33e9
+		}
+	},
+	{
+		.rate = 196608000,
+		.regs = {
+			0x4801,
+			0x42ef,
+			0x2495,
+			0x01c6,
+			0x33e9
+		}
+	},
+};
+
+static void plla_set_rate(struct sp_pll *clk)
+{
+	const u32 *pp = pa[clk->p[0]].regs;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(pa->regs); i++) {
+		clk_writel(0xffff0000 | pp[i], clk->reg + (i * 4));
+		pr_info("%04x\n", pp[i]);
+	}
+}
+
+static long plla_round_rate(struct sp_pll *clk, unsigned long rate)
+{
+	int i = ARRAY_SIZE(pa);
+
+	while (--i) {
+		if (rate >= pa[i].rate)
+			break;
+	}
+	clk->p[0] = i;
+	return pa[i].rate;
+}
+
+/******************************************* SP_PLL ********************************************/
+
+static long sp_pll_calc_div(struct sp_pll *clk, unsigned long rate)
+{
+	u32 fbdiv;
+	u32 max = 1 << clk->div_width;
+
+	fbdiv = DIV_ROUND_CLOSEST(rate, clk->brate);
+	if (fbdiv > max)
+		fbdiv = max;
+	return fbdiv;
+}
+
+static long sp_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+		unsigned long *prate)
+{
+	struct sp_pll *clk = to_sp_pll(hw);
+	long ret;
+
+	TRACE;
+	//pr_info("round_rate: %lu %lu\n", rate, *prate);
+
+	if (rate == *prate)
+		ret = *prate; /* bypass */
+	else if (clk->div_width == DIV_A) {
+		ret = plla_round_rate(clk, rate);
+	} else if (clk->div_width == DIV_TV) {
+		ret = plltv_div(clk, rate);
+		if (ret < 0)
+			ret = *prate;
+	} else
+		ret = sp_pll_calc_div(clk, rate) * clk->brate;
+
+	return ret;
+}
+
+static unsigned long sp_pll_recalc_rate(struct clk_hw *hw,
+		unsigned long prate)
+{
+	struct sp_pll *clk = to_sp_pll(hw);
+	u32 reg = clk_readl(clk->reg);
+	unsigned long ret;
+
+	//TRACE;
+	if (reg & BIT(clk->bp_bit))
+		ret = prate; /* bypass */
+	else if (clk->div_width == DIV_A) {
+		ret = pa[clk->p[0]].rate;
+		//reg = clk_readl(clk->reg + 12); // G4.10 K_SDM_A
+	} else if (clk->div_width == DIV_TV) {
+		u32 m, r, reg2;
+
+		r = MASK_GET(7, 2, clk_readl(clk->reg + 4));
+		reg2 = clk_readl(clk->reg + 8);
+		m = MASK_GET(8, 7, reg2) + 1;
+
+		if (reg & BIT(1)) { /* SEL_FRA */
+			/* fractional divider */
+			u32 sdm  = MASK_GET(2, 1, reg);
+			u32 ph   = MASK_GET(4, 1, reg);
+			u32 nfra = MASK_GET(6, 7, reg);
+			const u32 *pp = pt[ph];
+
+			ret = prate >> r;
+			ret = (ret * (pp[1] + pp[2]) / pp[0]) -
+				  (ret * (mods[sdm] - nfra) / mods[sdm] / pp[4]);
+			ret /= m;
+		} else {
+			/* integer divider */
+			u32 n = MASK_GET(0, 8, reg2) + 1;
+
+			ret = (prate / m * n) >> r;
+		}
+	} else {
+		u32 fbdiv = MASK_GET(clk->div_shift, clk->div_width, reg) + 1;
+
+		ret = clk->brate * fbdiv;
+	}
+	//pr_info("recalc_rate: %lu -> %lu\n", prate, ret);
+
+	return ret;
+}
+
+static int sp_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+		unsigned long prate)
+{
+	struct sp_pll *clk = to_sp_pll(hw);
+	unsigned long flags;
+	u32 reg;
+
+	//TRACE;
+	pr_info("set_rate: %lu -> %lu\n", prate, rate);
+
+	spin_lock_irqsave(clk->lock, flags);
+
+	reg = BIT(clk->bp_bit + 16); /* HIWORD_MASK */
+
+	if (rate == prate)
+		reg |= BIT(clk->bp_bit); /* bypass */
+	else if (clk->div_width == DIV_A)
+		plla_set_rate(clk);
+	else if (clk->div_width == DIV_TV)
+		plltv_set_rate(clk);
+	else if (clk->div_width) {
+		u32 fbdiv = sp_pll_calc_div(clk, rate);
+
+		reg |= MASK_SET(clk->div_shift, clk->div_width, fbdiv - 1);
+	}
+
+	clk_writel(reg, clk->reg);
+
+	spin_unlock_irqrestore(clk->lock, flags);
+
+	return 0;
+}
+
+static int sp_pll_enable(struct clk_hw *hw)
+{
+	struct sp_pll *clk = to_sp_pll(hw);
+	unsigned long flags;
+
+	TRACE;
+	spin_lock_irqsave(clk->lock, flags);
+	clk_writel(BIT(clk->pd_bit + 16) | BIT(clk->pd_bit), clk->reg); /* power up */
+	spin_unlock_irqrestore(clk->lock, flags);
+
+	return 0;
+}
+
+static void sp_pll_disable(struct clk_hw *hw)
+{
+	struct sp_pll *clk = to_sp_pll(hw);
+	unsigned long flags;
+
+	TRACE;
+	spin_lock_irqsave(clk->lock, flags);
+	clk_writel(BIT(clk->pd_bit + 16), clk->reg); /* power down */
+	spin_unlock_irqrestore(clk->lock, flags);
+}
+
+static int sp_pll_is_enabled(struct clk_hw *hw)
+{
+	struct sp_pll *clk = to_sp_pll(hw);
+
+	return clk_readl(clk->reg) & BIT(clk->pd_bit);
+}
+
+static const struct clk_ops sp_pll_ops = {
+	.enable = sp_pll_enable,
+	.disable = sp_pll_disable,
+	.is_enabled = sp_pll_is_enabled,
+	.round_rate = sp_pll_round_rate,
+	.recalc_rate = sp_pll_recalc_rate,
+	.set_rate = sp_pll_set_rate
+};
+
+static const struct clk_ops sp_pll_sub_ops = {
+	.enable = sp_pll_enable,
+	.disable = sp_pll_disable,
+	.is_enabled = sp_pll_is_enabled,
+	.recalc_rate = sp_pll_recalc_rate,
+};
+
+struct clk *clk_register_sp_pll(const char *name, const char *parent,
+		void __iomem *reg, int pd_bit, int bp_bit,
+		unsigned long brate, int shift, int width,
+		spinlock_t *lock)
+{
+	struct sp_pll *pll;
+	struct clk *clk;
+	//unsigned long flags = 0;
+	struct clk_init_data initd = {
+		.name = name,
+		.parent_names = &parent,
+		.ops = (bp_bit >= 0) ? &sp_pll_ops : &sp_pll_sub_ops,
+		.num_parents = 1,
+		.flags = CLK_IGNORE_UNUSED
+	};
+
+	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+	if (!pll)
+		return ERR_PTR(-ENOMEM);
+
+	if (reg == PLLSYS_CTL)
+		initd.flags |= CLK_IS_CRITICAL;
+
+	pll->hw.init = &initd;
+	pll->reg = reg;
+	pll->pd_bit = pd_bit;
+	pll->bp_bit = bp_bit;
+	pll->brate = brate;
+	pll->div_shift = shift;
+	pll->div_width = width;
+	pll->lock = lock;
+
+	clk = clk_register(NULL, &pll->hw);
+	if (WARN_ON(IS_ERR(clk))) {
+		kfree(pll);
+	} else {
+		pr_info("%-20s%lu\n", name, clk_get_rate(clk));
+		clk_register_clkdev(clk, NULL, name);
+	}
+
+	return clk;
+}
+
+static void __init sp_clk_setup(struct device_node *np)
+{
+	int i, j;
+
+	pr_info("sp-clkc init\n");
+
+	moon_regs = of_iomap(np, 0);
+	if (WARN_ON(!moon_regs)) {
+		pr_warn("sp-clkc regs missing.\n");
+		return; // -EIO
+	}
+
+	/* TODO: PLLs initial */
+
+	/* PLL_A */
+	clks[PLL_A] = clk_register_sp_pll("plla", EXTCLK,
+			PLLA_CTL, 11, 12, 27000000, 0, DIV_A, &plla_lock);
+
+	/* PLL_E */
+	clks[PLL_E] = clk_register_sp_pll("plle", EXTCLK,
+			PLLE_CTL, 6, 2, 50000000, 0, 0, &plle_lock);
+	clks[PLL_E_2P5] = clk_register_sp_pll("plle_2p5", "plle",
+			PLLE_CTL, 13, -1, 2500000, 0, 0, &plle_lock);
+	clks[PLL_E_25] = clk_register_sp_pll("plle_25", "plle",
+			PLLE_CTL, 12, -1, 25000000, 0, 0, &plle_lock);
+	clks[PLL_E_112P5] = clk_register_sp_pll("plle_112p5", "plle",
+			PLLE_CTL, 11, -1, 112500000, 0, 0, &plle_lock);
+
+	/* PLL_F */
+	clks[PLL_F] = clk_register_sp_pll("pllf", EXTCLK,
+			PLLF_CTL, 0, 10, 13500000, 1, 4, &pllf_lock);
+
+	/* PLL_TV */
+	clks[PLL_TV] = clk_register_sp_pll("plltv", EXTCLK,
+			PLLTV_CTL, 0, 15, 27000000, 0, DIV_TV, &plltv_lock);
+	clks[PLL_TV_A] = clk_register_divider(NULL, "plltv_a", "plltv", 0,
+			PLLTV_CTL + 4, 5, 1,
+			CLK_DIVIDER_POWER_OF_TWO, &plltv_lock);
+	clk_register_clkdev(clks[PLL_TV_A], NULL, "plltv_a");
+
+	/* PLL_SYS */
+	clks[PLL_SYS] = clk_register_sp_pll("pllsys", EXTCLK,
+			PLLSYS_CTL, 10, 9, 13500000, 0, 4, &pllsys_lock);
+
+	/* gates */
+	for (i = 0; i < ARRAY_SIZE(gates); i++) {
+		char s[10];
+
+		j = gates[i] & 0xffff;
+		sprintf(s, "clken%02x", j);
+		clks[j] = clk_register_gate(NULL, s, parents[gates[i] >> 16], CLK_IGNORE_UNUSED,
+			clk_regs + (j >> 4) * 4, j & 0x0f,
+			CLK_GATE_HIWORD_MASK, NULL);
+	}
+
+	clk_data.clks = clks;
+	clk_data.clk_num = ARRAY_SIZE(clks);
+	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+}
+
+CLK_OF_DECLARE(sp_clkc, "sunplus,sp7021-clkc", sp_clk_setup);
+
+MODULE_AUTHOR("Qin Jian <qinjian@cqplus1.com>");
+MODULE_DESCRIPTION("Sunplus SP7021 Clock Driver");
+MODULE_LICENSE("GPL v2");
-- 
2.33.1

[PATCH v3 7/8] dt-bindings: interrupt-controller: Add bindings for SP7021 interrupt controller

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-11-01 05:20:41

Add documentation to describe Sunplus SP7021 interrupt controller bindings.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 .../sunplus,sp7021-intc.yaml                  | 62 +++++++++++++++++++
 MAINTAINERS                                   |  1 +
 2 files changed, 63 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml
diff --git a/Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml b/Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml
new file mode 100644
index 000000000..5daeab63c
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) Sunplus Co., Ltd. 2021
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/interrupt-controller/sunplus,sp7021-intc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sunplus SP7021 SoC Interrupt Controller Device Tree Bindings
+
+maintainers:
+  - Qin Jian <qinjian@cqplus1.com>
+
+properties:
+  compatible:
+    items:
+      - const: sunplus,sp7021-intc
+
+  interrupt-controller: true
+
+  "#interrupt-cells":
+    const: 2
+    description:
+      The first cell is the IRQ number, the second cell is the trigger
+      type as defined in interrupt.txt in this directory.
+
+  reg:
+    maxItems: 2
+    description:
+      Specifies base physical address(s) and size of the controller regs.
+      The 1st region include type/polarity/priority/mask regs.
+      The 2nd region include clear/masked_ext0/masked_ext1/group regs.
+
+  interrupts:
+    maxItems: 2
+    description:
+      EXT_INT0 & EXT_INT1, 2 interrupts references to primary interrupt
+      controller.
+
+required:
+  - compatible
+  - interrupt-controller
+  - "#interrupt-cells"
+  - reg
+  - interrupts
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    intc: interrupt-controller@9c000780 {
+        compatible = "sunplus,sp7021-intc";
+        interrupt-controller;
+        #interrupt-cells = <2>;
+        reg = <0x9c000780 0x80>, <0x9c000a80 0x80>;
+        interrupt-parent = <&gic>;
+        interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>, /* EXT_INT0 */
+                     <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>; /* EXT_INT1 */
+    };
+
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 5069f552f..6b3bbe021 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2662,6 +2662,7 @@ S:	Maintained
 W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
 F:	Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
 F:	Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
+F:	Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml
 F:	Documentation/devicetree/bindings/reset/sunplus,reset.yaml
 F:	drivers/clk/clk-sp7021.c
 F:	drivers/reset/reset-sunplus.c
-- 
2.33.1

[PATCH v3 5/8] dt-bindings: clock: Add bindings for SP7021 clock driver

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-11-01 05:20:47

Add documentation to describe Sunplus SP7021 clock driver bindings.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 .../bindings/clock/sunplus,sp7021-clkc.yaml   |  38 ++++++
 MAINTAINERS                                   |   2 +
 include/dt-bindings/clock/sp-sp7021.h         | 112 ++++++++++++++++++
 3 files changed, 152 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
 create mode 100644 include/dt-bindings/clock/sp-sp7021.h
diff --git a/Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml b/Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
new file mode 100644
index 000000000..1ce7e41d8
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
@@ -0,0 +1,38 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) Sunplus Co., Ltd. 2021
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/sunplus,sp7021-clkc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sunplus SP7021 SoC Clock Controller Binding
+
+maintainers:
+  - Qin Jian <qinjian@cqplus1.com>
+
+properties:
+  compatible:
+    const: sunplus,sp7021-clkc
+
+  "#clock-cells":
+    const: 1
+
+  reg:
+    maxItems: 1
+
+required:
+  - compatible
+  - "#clock-cells"
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    clkc: clkc@9c000000 {
+      compatible = "sunplus,sp7021-clkc";
+      #clock-cells = <1>;
+      reg = <0x9c000000 0x280>;
+    };
+
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 6caffd6d0..90ebb823f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2661,8 +2661,10 @@ L:	linux-arm-kernel@lists.infradead.org (moderated for mon-subscribers)
 S:	Maintained
 W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
 F:	Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
+F:	Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
 F:	Documentation/devicetree/bindings/reset/sunplus,reset.yaml
 F:	drivers/reset/reset-sunplus.c
+F:	include/dt-bindings/clock/sp-sp7021.h
 F:	include/dt-bindings/reset/sp-sp7021.h
 
 ARM/Synaptics SoC support
diff --git a/include/dt-bindings/clock/sp-sp7021.h b/include/dt-bindings/clock/sp-sp7021.h
new file mode 100644
index 000000000..1ae9c4083
--- /dev/null
+++ b/include/dt-bindings/clock/sp-sp7021.h
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright (C) Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+#ifndef _DT_BINDINGS_CLOCK_SUNPLUS_SP7021_H
+#define _DT_BINDINGS_CLOCK_SUNPLUS_Sp7021_H
+
+#define XTAL		27000000
+
+/* plls */
+#define PLL_A		0
+#define PLL_E		1
+#define PLL_E_2P5	2
+#define PLL_E_25	3
+#define PLL_E_112P5	4
+#define PLL_F		5
+#define PLL_TV		6
+#define PLL_TV_A	7
+#define PLL_SYS		8
+
+/* gates: mo_clken0 ~ mo_clken9 */
+#define SYSTEM		0x10
+#define RTC			0x12
+#define IOCTL		0x13
+#define IOP			0x14
+#define OTPRX		0x15
+#define NOC			0x16
+#define BR			0x17
+#define RBUS_L00	0x18
+#define SPIFL		0x19
+#define SDCTRL0		0x1a
+#define PERI0		0x1b
+#define A926		0x1d
+#define UMCTL2		0x1e
+#define PERI1		0x1f
+
+#define DDR_PHY0	0x20
+#define ACHIP		0x22
+#define STC0		0x24
+#define STC_AV0		0x25
+#define STC_AV1		0x26
+#define STC_AV2		0x27
+#define UA0			0x28
+#define UA1			0x29
+#define UA2			0x2a
+#define UA3			0x2b
+#define UA4			0x2c
+#define HWUA		0x2d
+#define DDC0		0x2e
+#define UADMA		0x2f
+
+#define CBDMA0		0x30
+#define CBDMA1		0x31
+#define SPI_COMBO_0	0x32
+#define SPI_COMBO_1	0x33
+#define SPI_COMBO_2	0x34
+#define SPI_COMBO_3	0x35
+#define AUD			0x36
+#define USBC0		0x3a
+#define USBC1		0x3b
+#define UPHY0		0x3d
+#define UPHY1		0x3e
+
+#define I2CM0		0x40
+#define I2CM1		0x41
+#define I2CM2		0x42
+#define I2CM3		0x43
+#define PMC			0x4d
+#define CARD_CTL0	0x4e
+#define CARD_CTL1	0x4f
+
+#define CARD_CTL4	0x52
+#define BCH			0x54
+#define DDFCH		0x5b
+#define CSIIW0		0x5c
+#define CSIIW1		0x5d
+#define MIPICSI0	0x5e
+#define MIPICSI1	0x5f
+
+#define HDMI_TX		0x60
+#define VPOST		0x65
+
+#define TGEN		0x70
+#define DMIX		0x71
+#define TCON		0x7a
+#define INTERRUPT	0x7f
+
+#define RGST		0x80
+#define GPIO		0x83
+#define RBUS_TOP	0x84
+
+#define MAILBOX		0x96
+#define SPIND		0x9a
+#define I2C2CBUS	0x9b
+#define SEC			0x9d
+#define DVE			0x9e
+#define GPOST0		0x9f
+
+#define OSD0		0xa0
+#define DISP_PWM	0xa2
+#define UADBG		0xa3
+#define DUMMY_MASTER	0xa4
+#define FIO_CTL		0xa5
+#define FPGA		0xa6
+#define L2SW		0xa7
+#define ICM			0xa8
+#define AXI_GLOBAL	0xa9
+
+#define CLK_MAX		0xb0
+
+#endif
-- 
2.33.1

[PATCH v3 8/8] irqchip: Add Sunplus SP7021 interrupt controller driver

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-11-01 05:20:56

Add interrupt controller driver for Sunplus SP7021 SoC.
This is the interrupt controller in P-chip which collects all interrupt
sources in P-chip and routes them to parent interrupt controller in C-chip.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 MAINTAINERS                       |   1 +
 drivers/irqchip/Kconfig           |   9 +
 drivers/irqchip/Makefile          |   1 +
 drivers/irqchip/irq-sp7021-intc.c | 279 ++++++++++++++++++++++++++++++
 4 files changed, 290 insertions(+)
 create mode 100644 drivers/irqchip/irq-sp7021-intc.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 6b3bbe021..febbd97bf 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2665,6 +2665,7 @@ F:	Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
 F:	Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml
 F:	Documentation/devicetree/bindings/reset/sunplus,reset.yaml
 F:	drivers/clk/clk-sp7021.c
+F:	drivers/irqchip/irq-sp7021-intc.c
 F:	drivers/reset/reset-sunplus.c
 F:	include/dt-bindings/clock/sp-sp7021.h
 F:	include/dt-bindings/reset/sp-sp7021.h
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index aca7b595c..6f0bc0871 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -602,4 +602,13 @@ config APPLE_AIC
 	  Support for the Apple Interrupt Controller found on Apple Silicon SoCs,
 	  such as the M1.
 
+config SUNPLUS_SP7021_INTC
+	bool "Sunplus SP7021 interrupt controller"
+	help
+	  Support for the Sunplus SP7021 Interrupt Controller IP core.
+	  SP7021 SoC has 2 Chips: C-Chip & P-Chip. This is used as a
+	  chained controller, routed all interrupt source in P-Chip to
+	  the primary controller on C-Chip.
+	  This is selected automatically by platform config.
+
 endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index f88cbf36a..75411f654 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -116,3 +116,4 @@ obj-$(CONFIG_MACH_REALTEK_RTL)		+= irq-realtek-rtl.o
 obj-$(CONFIG_WPCM450_AIC)		+= irq-wpcm450-aic.o
 obj-$(CONFIG_IRQ_IDT3243X)		+= irq-idt3243x.o
 obj-$(CONFIG_APPLE_AIC)			+= irq-apple-aic.o
+obj-$(CONFIG_SUNPLUS_SP7021_INTC)	+= irq-sp7021-intc.o
diff --git a/drivers/irqchip/irq-sp7021-intc.c b/drivers/irqchip/irq-sp7021-intc.c
new file mode 100644
index 000000000..4c995d4e3
--- /dev/null
+++ b/drivers/irqchip/irq-sp7021-intc.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/*
+ * Copyright (C) Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/io.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#define SP_INTC_HWIRQ_MIN	0
+#define SP_INTC_HWIRQ_MAX	223
+
+#define SP_INTC_NR_IRQS		(SP_INTC_HWIRQ_MAX - SP_INTC_HWIRQ_MIN + 1)
+#define SP_INTC_NR_GROUPS	DIV_ROUND_UP(SP_INTC_NR_IRQS, 32)
+#define SP_INTC_REG_SIZE	(SP_INTC_NR_GROUPS * 4)
+
+/* REG_GROUP_0 regs */
+#define REG_INTR_TYPE		(sp_intc.g0)
+#define REG_INTR_POLARITY	(REG_INTR_TYPE     + SP_INTC_REG_SIZE)
+#define REG_INTR_PRIORITY	(REG_INTR_POLARITY + SP_INTC_REG_SIZE)
+#define REG_INTR_MASK		(REG_INTR_PRIORITY + SP_INTC_REG_SIZE)
+
+/* REG_GROUP_1 regs */
+#define REG_INTR_CLEAR		(sp_intc.g1)
+#define REG_MASKED_EXT1		(REG_INTR_CLEAR    + SP_INTC_REG_SIZE)
+#define REG_MASKED_EXT0		(REG_MASKED_EXT1   + SP_INTC_REG_SIZE)
+#define REG_INTR_GROUP		(REG_INTR_CLEAR    + 31 * 4)
+
+#define GROUP_MASK			(BIT(SP_INTC_NR_GROUPS) - 1)
+#define GROUP_SHIFT_EXT1	(0)
+#define GROUP_SHIFT_EXT0	(8)
+
+/*
+ * When GPIO_INT0~7 set to edge trigger, doesn't work properly.
+ * WORKAROUND: change it to level trigger, and toggle the polarity
+ * at ACK/Handler to make the HW work.
+ */
+#define GPIO_INT0_HWIRQ		120
+#define GPIO_INT7_HWIRQ		127
+#define IS_GPIO_INT(irq)	(((irq) >= GPIO_INT0_HWIRQ) && ((irq) <= GPIO_INT7_HWIRQ))
+/* state idx */
+enum {
+	_IS_EDGE = 0,
+	_IS_LOW,
+	_IS_ACTIVE
+};
+#define STATE_BIT(irq, idx)			((irq - GPIO_INT0_HWIRQ) * 3 + idx)
+#define ASSIGN_STATE(irq, idx, v)	assign_bit(STATE_BIT(irq, idx), sp_intc.states, v)
+#define TEST_STATE(irq, idx)		test_bit(STATE_BIT(irq, idx), sp_intc.states)
+
+static struct sp_intctl {
+	/*
+	 * REG_GROUP_0: include type/polarity/priority/mask regs.
+	 * REG_GROUP_1: include clear/masked_ext0/masked_ext1/group regs.
+	 */
+	void __iomem *g0; // REG_GROUP_0 base
+	void __iomem *g1; // REG_GROUP_1 base
+
+	struct irq_domain *domain;
+	raw_spinlock_t lock;
+
+	/*
+	 * store GPIO_INT states
+	 * each interrupt has 3 states: is_edge, is_low, is_active
+	 */
+	DECLARE_BITMAP(states, (GPIO_INT7_HWIRQ - GPIO_INT0_HWIRQ + 1) * 3);
+} sp_intc;
+
+static struct irq_chip sp_intc_chip;
+
+static void sp_intc_assign_bit(u32 hwirq, void __iomem *base, bool value)
+{
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&sp_intc.lock, flags);
+	__assign_bit(hwirq, base, value);
+	raw_spin_unlock_irqrestore(&sp_intc.lock, flags);
+}
+
+static void sp_intc_ack_irq(struct irq_data *d)
+{
+	u32 hwirq = d->hwirq;
+
+	if (unlikely(IS_GPIO_INT(hwirq) && TEST_STATE(hwirq, _IS_EDGE))) { // WORKAROUND
+		sp_intc_assign_bit(hwirq, REG_INTR_POLARITY, !TEST_STATE(hwirq, _IS_LOW));
+		ASSIGN_STATE(hwirq, _IS_ACTIVE, true);
+	}
+
+	sp_intc_assign_bit(hwirq, REG_INTR_CLEAR, 1);
+}
+
+static void sp_intc_mask_irq(struct irq_data *d)
+{
+	sp_intc_assign_bit(d->hwirq, REG_INTR_MASK, 0);
+}
+
+static void sp_intc_unmask_irq(struct irq_data *d)
+{
+	sp_intc_assign_bit(d->hwirq, REG_INTR_MASK, 1);
+}
+
+static int sp_intc_set_type(struct irq_data *d, unsigned int type)
+{
+	u32 hwirq = d->hwirq;
+	bool is_edge = !(type & IRQ_TYPE_LEVEL_MASK);
+	bool is_low = (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_EDGE_FALLING);
+
+	irq_set_handler_locked(d, is_edge ? handle_edge_irq : handle_level_irq);
+
+	if (unlikely(IS_GPIO_INT(hwirq) && is_edge)) { // WORKAROUND
+		/* store states */
+		ASSIGN_STATE(hwirq, _IS_EDGE, is_edge);
+		ASSIGN_STATE(hwirq, _IS_LOW, is_low);
+		ASSIGN_STATE(hwirq, _IS_ACTIVE, false);
+		/* change to level */
+		is_edge = false;
+	}
+
+	sp_intc_assign_bit(hwirq, REG_INTR_TYPE, is_edge);
+	sp_intc_assign_bit(hwirq, REG_INTR_POLARITY, is_low);
+
+	return 0;
+}
+
+static int sp_intc_get_ext_irq(int ext_num)
+{
+	void __iomem *base = ext_num ? REG_MASKED_EXT1 : REG_MASKED_EXT0;
+	u32 shift = ext_num ? GROUP_SHIFT_EXT1 : GROUP_SHIFT_EXT0;
+	u32 groups;
+	u32 pending_group;
+	u32 group;
+	u32 pending_irq;
+
+	groups = readl_relaxed(REG_INTR_GROUP);
+	pending_group = (groups >> shift) & GROUP_MASK;
+	if (!pending_group)
+		return -1;
+
+	group = fls(pending_group) - 1;
+	pending_irq = readl_relaxed(base + group * 4);
+	if (!pending_irq)
+		return -1;
+
+	return (group * 32) + fls(pending_irq) - 1;
+}
+
+static void sp_intc_handle_ext_cascaded(struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	int ext_num = (int)irq_desc_get_handler_data(desc);
+	int hwirq;
+
+	chained_irq_enter(chip, desc);
+
+	while ((hwirq = sp_intc_get_ext_irq(ext_num)) >= 0) {
+		if (unlikely(IS_GPIO_INT(hwirq) && TEST_STATE(hwirq, _IS_ACTIVE))) { // WORKAROUND
+			ASSIGN_STATE(hwirq, _IS_ACTIVE, false);
+			sp_intc_assign_bit(hwirq, REG_INTR_POLARITY, TEST_STATE(hwirq, _IS_LOW));
+		} else {
+			generic_handle_domain_irq(sp_intc.domain, hwirq);
+		}
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+#ifdef CONFIG_SMP
+static int sp_intc_set_affinity(struct irq_data *d, const struct cpumask *mask, bool force)
+{
+	return -EINVAL;
+}
+#endif
+
+static struct irq_chip sp_intc_chip = {
+	.name = "sp_intc",
+	.irq_ack = sp_intc_ack_irq,
+	.irq_mask = sp_intc_mask_irq,
+	.irq_unmask = sp_intc_unmask_irq,
+	.irq_set_type = sp_intc_set_type,
+#ifdef CONFIG_SMP
+	.irq_set_affinity = sp_intc_set_affinity,
+#endif
+};
+
+static int sp_intc_irq_domain_map(struct irq_domain *domain,
+	unsigned int irq, irq_hw_number_t hwirq)
+{
+	irq_set_chip_and_handler(irq, &sp_intc_chip, handle_level_irq);
+	irq_set_chip_data(irq, &sp_intc_chip);
+	irq_set_noprobe(irq);
+
+	return 0;
+}
+
+static const struct irq_domain_ops sp_intc_dm_ops = {
+	.xlate = irq_domain_xlate_twocell,
+	.map = sp_intc_irq_domain_map,
+};
+
+static int sp_intc_irq_map(struct device_node *node, int i)
+{
+	unsigned int irq;
+
+	irq = irq_of_parse_and_map(node, i);
+	if (!irq)
+		return -ENOENT;
+
+	irq_set_chained_handler_and_data(irq, sp_intc_handle_ext_cascaded, (void *)i);
+
+	return 0;
+}
+
+void sp_intc_set_ext(u32 hwirq, int ext_num)
+{
+	sp_intc_assign_bit(hwirq, REG_INTR_PRIORITY, !ext_num);
+}
+EXPORT_SYMBOL_GPL(sp_intc_set_ext);
+
+int __init sp_intc_init_dt(struct device_node *node, struct device_node *parent)
+{
+	int i, ret;
+
+	sp_intc.g0 = of_iomap(node, 0);
+	if (!sp_intc.g0)
+		return -ENXIO;
+
+	sp_intc.g1 = of_iomap(node, 1);
+	if (!sp_intc.g1) {
+		ret = -ENXIO;
+		goto out_unmap0;
+	}
+
+	/* initial regs */
+	for (i = 0; i < SP_INTC_NR_GROUPS; i++) {
+		/* all mask */
+		writel_relaxed(0, REG_INTR_MASK + i * 4);
+		/* all edge */
+		writel_relaxed(~0, REG_INTR_TYPE + i * 4);
+		/* all high-active */
+		writel_relaxed(0, REG_INTR_POLARITY + i * 4);
+		/* all EXT_INT0 */
+		writel_relaxed(~0, REG_INTR_PRIORITY + i * 4);
+		/* all clear */
+		writel_relaxed(~0, REG_INTR_CLEAR + i * 4);
+	}
+
+	sp_intc.domain = irq_domain_add_linear(node, SP_INTC_NR_IRQS,
+			&sp_intc_dm_ops, &sp_intc);
+	if (!sp_intc.domain) {
+		ret = -ENOMEM;
+		goto out_unmap1;
+	}
+
+	raw_spin_lock_init(&sp_intc.lock);
+
+	ret = sp_intc_irq_map(node, 0); // EXT_INT0
+	if (ret)
+		goto out_domain;
+
+	ret = sp_intc_irq_map(node, 1); // EXT_INT1
+	if (ret)
+		goto out_domain;
+
+	return 0;
+
+out_domain:
+	irq_domain_remove(sp_intc.domain);
+out_unmap1:
+	iounmap(sp_intc.g1);
+out_unmap0:
+	iounmap(sp_intc.g0);
+
+	return ret;
+}
+IRQCHIP_DECLARE(sp_intc, "sunplus,sp7021-intc", sp_intc_init_dt);
-- 
2.33.1

[PATCH v3 2/8] dt-bindings: arm: sunplus: Add bindings for Sunplus SP7021 SoC boards

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-11-01 05:20:59

This introduces bindings for boards based Sunplus SP7021 SoC.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 .../bindings/arm/sunplus,sp7021.yaml          | 27 +++++++++++++++++++
 MAINTAINERS                                   |  7 +++++
 2 files changed, 34 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
diff --git a/Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml b/Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
new file mode 100644
index 000000000..5b9985b73
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) Sunplus Co., Ltd. 2021
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/arm/sunplus,sp7021.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sunplus SP7021 Boards Device Tree Bindings
+
+maintainers:
+  - qinjian <qinjian@cqplus1.com>
+
+description: |
+  ARM platforms using Sunplus SP7021, an ARM Cortex A7 (4-cores) based SoC.
+  Wiki: https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
+
+properties:
+  $nodename:
+    const: '/'
+  compatible:
+    oneOf:
+      - items:
+          - const: sunplus,sp7021-achip
+
+additionalProperties: true
+
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index e0bca0de0..6a5422f10 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2655,6 +2655,13 @@ F:	drivers/clocksource/armv7m_systick.c
 N:	stm32
 N:	stm
 
+ARM/SUNPLUS SP7021 SOC SUPPORT
+M:	Qin Jian <qinjian@cqplus1.com>
+L:	linux-arm-kernel@lists.infradead.org (moderated for mon-subscribers)
+S:	Maintained
+W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
+F:	Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
+
 ARM/Synaptics SoC support
 M:	Jisheng Zhang <Jisheng.Zhang@synaptics.com>
 M:	Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
-- 
2.33.1

[PATCH v3 1/8] dt-bindings: vendor-prefixes: Add Sunplus

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-11-01 05:21:01

Add vendor prefix for Sunplus Technology Co., Ltd. (http://www.sunplus.com)

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
 1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index a867f7102..50d4ee5ac 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -1131,6 +1131,8 @@ patternProperties:
     description: Summit microelectronics
   "^sunchip,.*":
     description: Shenzhen Sunchip Technology Co., Ltd
+  "^sunplus,.*":
+    description: Sunplus Technology Co., Ltd.
   "^SUNW,.*":
     description: Sun Microsystems, Inc
   "^supermicro,.*":
-- 
2.33.1

Re: [PATCH v3 8/8] irqchip: Add Sunplus SP7021 interrupt controller driver

From: kernel test robot <hidden>
Date: 2021-11-01 08:28:56

Hi Qin,

I love your patch! Perhaps something to improve:

[auto build test WARNING on pza/reset/next]
[also build test WARNING on robh/for-next clk/clk-next linus/master v5.15]
[cannot apply to tip/irq/core next-20211029]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Qin-Jian/dt-bindings-vendor-prefixes-Add-Sunplus/20211101-140155
base:   https://git.pengutronix.de/git/pza/linux reset/next
config: sparc-randconfig-r011-20211101 (attached as .config)
compiler: sparc64-linux-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/ca53c40693bc90c44e3ec4c95c7758d7342229e2
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Qin-Jian/dt-bindings-vendor-prefixes-Add-Sunplus/20211101-140155
        git checkout ca53c40693bc90c44e3ec4c95c7758d7342229e2
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross ARCH=sparc 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <redacted>

All warnings (new ones prefixed by >>):

   drivers/irqchip/irq-sp7021-intc.c: In function 'sp_intc_handle_ext_cascaded':
quoted
drivers/irqchip/irq-sp7021-intc.c:154:23: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
     154 |         int ext_num = (int)irq_desc_get_handler_data(desc);
         |                       ^
   drivers/irqchip/irq-sp7021-intc.c: In function 'sp_intc_irq_map':
quoted
drivers/irqchip/irq-sp7021-intc.c:212:76: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
     212 |         irq_set_chained_handler_and_data(irq, sp_intc_handle_ext_cascaded, (void *)i);
         |                                                                            ^
   drivers/irqchip/irq-sp7021-intc.c: At top level:
   drivers/irqchip/irq-sp7021-intc.c:217:6: warning: no previous prototype for 'sp_intc_set_ext' [-Wmissing-prototypes]
     217 | void sp_intc_set_ext(u32 hwirq, int ext_num)
         |      ^~~~~~~~~~~~~~~
   drivers/irqchip/irq-sp7021-intc.c:223:12: warning: no previous prototype for 'sp_intc_init_dt' [-Wmissing-prototypes]
     223 | int __init sp_intc_init_dt(struct device_node *node, struct device_node *parent)
         |            ^~~~~~~~~~~~~~~


vim +154 drivers/irqchip/irq-sp7021-intc.c

   150	
   151	static void sp_intc_handle_ext_cascaded(struct irq_desc *desc)
   152	{
   153		struct irq_chip *chip = irq_desc_get_chip(desc);
 > 154		int ext_num = (int)irq_desc_get_handler_data(desc);
   155		int hwirq;
   156	
   157		chained_irq_enter(chip, desc);
   158	
   159		while ((hwirq = sp_intc_get_ext_irq(ext_num)) >= 0) {
   160			if (unlikely(IS_GPIO_INT(hwirq) && TEST_STATE(hwirq, _IS_ACTIVE))) { // WORKAROUND
   161				ASSIGN_STATE(hwirq, _IS_ACTIVE, false);
   162				sp_intc_assign_bit(hwirq, REG_INTR_POLARITY, TEST_STATE(hwirq, _IS_LOW));
   163			} else {
   164				generic_handle_domain_irq(sp_intc.domain, hwirq);
   165			}
   166		}
   167	
   168		chained_irq_exit(chip, desc);
   169	}
   170	
   171	#ifdef CONFIG_SMP
   172	static int sp_intc_set_affinity(struct irq_data *d, const struct cpumask *mask, bool force)
   173	{
   174		return -EINVAL;
   175	}
   176	#endif
   177	
   178	static struct irq_chip sp_intc_chip = {
   179		.name = "sp_intc",
   180		.irq_ack = sp_intc_ack_irq,
   181		.irq_mask = sp_intc_mask_irq,
   182		.irq_unmask = sp_intc_unmask_irq,
   183		.irq_set_type = sp_intc_set_type,
   184	#ifdef CONFIG_SMP
   185		.irq_set_affinity = sp_intc_set_affinity,
   186	#endif
   187	};
   188	
   189	static int sp_intc_irq_domain_map(struct irq_domain *domain,
   190		unsigned int irq, irq_hw_number_t hwirq)
   191	{
   192		irq_set_chip_and_handler(irq, &sp_intc_chip, handle_level_irq);
   193		irq_set_chip_data(irq, &sp_intc_chip);
   194		irq_set_noprobe(irq);
   195	
   196		return 0;
   197	}
   198	
   199	static const struct irq_domain_ops sp_intc_dm_ops = {
   200		.xlate = irq_domain_xlate_twocell,
   201		.map = sp_intc_irq_domain_map,
   202	};
   203	
   204	static int sp_intc_irq_map(struct device_node *node, int i)
   205	{
   206		unsigned int irq;
   207	
   208		irq = irq_of_parse_and_map(node, i);
   209		if (!irq)
   210			return -ENOENT;
   211	
 > 212		irq_set_chained_handler_and_data(irq, sp_intc_handle_ext_cascaded, (void *)i);
   213	
   214		return 0;
   215	}
   216	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

Re: [PATCH v3 6/8] clk: Add Sunplus SP7021 clock driver

From: kernel test robot <hidden>
Date: 2021-11-01 10:16:57

Hi Qin,

I love your patch! Perhaps something to improve:

[auto build test WARNING on pza/reset/next]
[also build test WARNING on robh/for-next clk/clk-next tip/irq/core linus/master v5.15 next-20211029]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Qin-Jian/dt-bindings-vendor-prefixes-Add-Sunplus/20211101-140155
base:   https://git.pengutronix.de/git/pza/linux reset/next
config: openrisc-randconfig-r015-20211101 (attached as .config)
compiler: or1k-linux-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/9767714cf5c22192f99adb0d8e344cb5e38a2e33
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Qin-Jian/dt-bindings-vendor-prefixes-Add-Sunplus/20211101-140155
        git checkout 9767714cf5c22192f99adb0d8e344cb5e38a2e33
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross ARCH=openrisc 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <redacted>

All warnings (new ones prefixed by >>):
quoted
drivers/clk/clk-sp7021.c:663:13: warning: no previous prototype for 'clk_register_sp_pll' [-Wmissing-prototypes]
     663 | struct clk *clk_register_sp_pll(const char *name, const char *parent,
         |             ^~~~~~~~~~~~~~~~~~~


vim +/clk_register_sp_pll +663 drivers/clk/clk-sp7021.c

   662	
 > 663	struct clk *clk_register_sp_pll(const char *name, const char *parent,
   664			void __iomem *reg, int pd_bit, int bp_bit,
   665			unsigned long brate, int shift, int width,
   666			spinlock_t *lock)
   667	{
   668		struct sp_pll *pll;
   669		struct clk *clk;
   670		//unsigned long flags = 0;
   671		struct clk_init_data initd = {
   672			.name = name,
   673			.parent_names = &parent,
   674			.ops = (bp_bit >= 0) ? &sp_pll_ops : &sp_pll_sub_ops,
   675			.num_parents = 1,
   676			.flags = CLK_IGNORE_UNUSED
   677		};
   678	
   679		pll = kzalloc(sizeof(*pll), GFP_KERNEL);
   680		if (!pll)
   681			return ERR_PTR(-ENOMEM);
   682	
   683		if (reg == PLLSYS_CTL)
   684			initd.flags |= CLK_IS_CRITICAL;
   685	
   686		pll->hw.init = &initd;
   687		pll->reg = reg;
   688		pll->pd_bit = pd_bit;
   689		pll->bp_bit = bp_bit;
   690		pll->brate = brate;
   691		pll->div_shift = shift;
   692		pll->div_width = width;
   693		pll->lock = lock;
   694	
   695		clk = clk_register(NULL, &pll->hw);
   696		if (WARN_ON(IS_ERR(clk))) {
   697			kfree(pll);
   698		} else {
   699			pr_info("%-20s%lu\n", name, clk_get_rate(clk));
   700			clk_register_clkdev(clk, NULL, name);
   701		}
   702	
   703		return clk;
   704	}
   705	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

Re: [PATCH v3 8/8] irqchip: Add Sunplus SP7021 interrupt controller driver

From: kernel test robot <hidden>
Date: 2021-11-01 10:26:58

Hi Qin,

I love your patch! Perhaps something to improve:

[auto build test WARNING on pza/reset/next]
[also build test WARNING on robh/for-next clk/clk-next linus/master v5.15]
[cannot apply to tip/irq/core next-20211029]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Qin-Jian/dt-bindings-vendor-prefixes-Add-Sunplus/20211101-140155
base:   https://git.pengutronix.de/git/pza/linux reset/next
config: arc-randconfig-r016-20211101 (attached as .config)
compiler: arceb-elf-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/ca53c40693bc90c44e3ec4c95c7758d7342229e2
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Qin-Jian/dt-bindings-vendor-prefixes-Add-Sunplus/20211101-140155
        git checkout ca53c40693bc90c44e3ec4c95c7758d7342229e2
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross ARCH=arc 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <redacted>

All warnings (new ones prefixed by >>):
quoted
drivers/irqchip/irq-sp7021-intc.c:217:6: warning: no previous prototype for 'sp_intc_set_ext' [-Wmissing-prototypes]
     217 | void sp_intc_set_ext(u32 hwirq, int ext_num)
         |      ^~~~~~~~~~~~~~~
quoted
drivers/irqchip/irq-sp7021-intc.c:223:12: warning: no previous prototype for 'sp_intc_init_dt' [-Wmissing-prototypes]
     223 | int __init sp_intc_init_dt(struct device_node *node, struct device_node *parent)
         |            ^~~~~~~~~~~~~~~


vim +/sp_intc_set_ext +217 drivers/irqchip/irq-sp7021-intc.c

   216	
 > 217	void sp_intc_set_ext(u32 hwirq, int ext_num)
   218	{
   219		sp_intc_assign_bit(hwirq, REG_INTR_PRIORITY, !ext_num);
   220	}
   221	EXPORT_SYMBOL_GPL(sp_intc_set_ext);
   222	
 > 223	int __init sp_intc_init_dt(struct device_node *node, struct device_node *parent)

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

Re: [PATCH v3 2/8] dt-bindings: arm: sunplus: Add bindings for Sunplus SP7021 SoC boards

From: Rob Herring <robh@kernel.org>
Date: 2021-11-01 19:59:05

On Mon, 01 Nov 2021 13:01:52 +0800, Qin Jian wrote:
This introduces bindings for boards based Sunplus SP7021 SoC.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 .../bindings/arm/sunplus,sp7021.yaml          | 27 +++++++++++++++++++
 MAINTAINERS                                   |  7 +++++
 2 files changed, 34 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
Reviewed-by: Rob Herring <robh@kernel.org>

Re: [PATCH v3 5/8] dt-bindings: clock: Add bindings for SP7021 clock driver

From: Rob Herring <robh@kernel.org>
Date: 2021-11-01 19:59:34

On Mon, 01 Nov 2021 13:01:55 +0800, Qin Jian wrote:
Add documentation to describe Sunplus SP7021 clock driver bindings.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 .../bindings/clock/sunplus,sp7021-clkc.yaml   |  38 ++++++
 MAINTAINERS                                   |   2 +
 include/dt-bindings/clock/sp-sp7021.h         | 112 ++++++++++++++++++
 3 files changed, 152 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
 create mode 100644 include/dt-bindings/clock/sp-sp7021.h
Reviewed-by: Rob Herring <robh@kernel.org>

Re: [PATCH v3 3/8] dt-bindings: reset: Add bindings for SP7021 reset driver

From: Philipp Zabel <p.zabel@pengutronix.de>
Date: 2021-11-02 11:51:32

On Mon, 2021-11-01 at 13:01 +0800, Qin Jian wrote:
quoted hunk
Add documentation to describe Sunplus SP7021 reset driver bindings.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 .../bindings/reset/sunplus,reset.yaml         | 40 ++++++++
 MAINTAINERS                                   |  2 +
 include/dt-bindings/reset/sp-sp7021.h         | 99 +++++++++++++++++++
 3 files changed, 141 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/reset/sunplus,reset.yaml
 create mode 100644 include/dt-bindings/reset/sp-sp7021.h
diff --git a/Documentation/devicetree/bindings/reset/sunplus,reset.yaml b/Documentation/devicetree/bindings/reset/sunplus,reset.yaml
new file mode 100644
index 000000000..bf55f4ee2
--- /dev/null
+++ b/Documentation/devicetree/bindings/reset/sunplus,reset.yaml
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) Sunplus Co., Ltd. 2021
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/reset/sunplus,reset.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Sunplus SoC Reset Controller
+
+maintainers:
+  - Qin Jian <qinjian@cqplus1.com>
+
+properties:
+  compatible:
+    enum:
+      - sunplus,sp7021-reset # Reset Controller on SP7021 and compatible SoCs
+      - sunplus,q645-reset # Reset Controller on Q645 and compatible SoCs
The commit message only mentions SP7021. Should Q645 be added later, or
is this patch missing the binding header for Q655?

regards
Philipp

Re: [PATCH v3 4/8] reset: Add Sunplus SP7021 reset driver

From: Philipp Zabel <p.zabel@pengutronix.de>
Date: 2021-11-02 12:23:23

On Mon, 2021-11-01 at 13:01 +0800, Qin Jian wrote:
Add reset driver for Sunplus SP7021 SoC.
You don't mention Q645 here, it appears this driver supports both SoCs?

[...]
quoted hunk
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index be799a5ab..50695ab47 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -249,6 +249,14 @@ config RESET_TI_SYSCON
 	  you wish to use the reset framework for such memory-mapped devices,
 	  say Y here. Otherwise, say N.


+config RESET_SUNPLUS
Please add these entries in alphabetical order.
+	bool "Sunplus SoCs Reset Driver"
Can this be made:

	depends SOC_SP7021 || SOC_Q645 || COMPILE_TEST

?
+	help
+	  This enables the reset driver support for Sunplus SP7021 SoC family.
+	  Say Y if you want to control reset signals by the reset controller.
+	  Otherwise, say N.
+	  This driver is selected automatically by platform config.
Which platform config?


[...]
quoted hunk
diff --git a/drivers/reset/reset-sunplus.c b/drivers/reset/reset-sunplus.c
new file mode 100644
index 000000000..696efd75e
--- /dev/null
+++ b/drivers/reset/reset-sunplus.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/*
+ * SP7021 reset driver
+ *
+ * Copyright (C) Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
Drop this boilerplate, this is not required with the SPDX identifier
above.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/reboot.h>
+
+#if defined(CONFIG_SOC_SP7021)
+#include <dt-bindings/reset/sp-sp7021.h>
+#elif defined(CONFIG_SOC_Q645)
+#include <dt-bindings/reset/sp-q645.h>
+#endif
I'd prefer if you added namespace prefixes to the defines and included
both headers unconditionally.
These are just required for RST_MAX, correct?
+
+#define BITASSERT(id, val)          ((1 << (16 + id)) | (val << id))
+
+
+struct sp_reset_data {
+	struct reset_controller_dev	rcdev;
+	void __iomem			*membase;
+} sp_reset;
Please allocate this with devm_kzalloc in the probe function instead.
+
+
+static inline struct sp_reset_data *
+to_sp_reset_data(struct reset_controller_dev *rcdev)
+{
+	return container_of(rcdev, struct sp_reset_data, rcdev);
+}
+
+static int sp_reset_update(struct reset_controller_dev *rcdev,
+			      unsigned long id, bool assert)
+{
+	struct sp_reset_data *data = to_sp_reset_data(rcdev);
+	int reg_width = sizeof(u32)/2;
+	int bank = id / (reg_width * BITS_PER_BYTE);
+	int offset = id % (reg_width * BITS_PER_BYTE);
+	void __iomem *addr;
+
+	addr = data->membase + (bank * 4);
+
+	if (assert)
+		writel(BITASSERT(offset, 1), addr);
+	else
+		writel(BITASSERT(offset, 0), addr);
Could be

	writel(BITASSERT(offset, assert), addr);
+
+	return 0;
+}
+
+static int sp_reset_assert(struct reset_controller_dev *rcdev,
+			      unsigned long id)
+{
+	return sp_reset_update(rcdev, id, true);
+}
+
+
+static int sp_reset_deassert(struct reset_controller_dev *rcdev,
+				unsigned long id)
+{
+	return sp_reset_update(rcdev, id, false);
+}
+
+static int sp_reset_status(struct reset_controller_dev *rcdev,
+			      unsigned long id)
+{
+	struct sp_reset_data *data = to_sp_reset_data(rcdev);
+	int reg_width = sizeof(u32)/2;
+	int bank = id / (reg_width * BITS_PER_BYTE);
+	int offset = id % (reg_width * BITS_PER_BYTE);
+	u32 reg;
+
+	reg = readl(data->membase + (bank * 4));
+
+	return !!(reg & BIT(offset));
+}
+
+static int sp_restart(struct notifier_block *this, unsigned long mode,
+				void *cmd)
+{
+	sp_reset_assert(&sp_reset.rcdev, RST_SYSTEM);
+	sp_reset_deassert(&sp_reset.rcdev, RST_SYSTEM);
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block sp_restart_nb = {
+	.notifier_call = sp_restart,
+	.priority = 192,
+};
+
+static const struct reset_control_ops sp_reset_ops = {
+	.assert		= sp_reset_assert,
+	.deassert	= sp_reset_deassert,
+	.status		= sp_reset_status,
+};
+
+static const struct of_device_id sp_reset_dt_ids[] = {
+	{ .compatible = "sunplus,sp7021-reset", },
+	{ .compatible = "sunplus,q645-reset", },
+	{ /* sentinel */ },
+};
+
+static int sp_reset_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct sp_reset_data *data = &sp_reset;
+	void __iomem *membase;
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	membase = devm_ioremap(dev, res->start, resource_size(res));
+	if (IS_ERR(membase))
+		return PTR_ERR(membase);
+
+	data->membase = membase;
+	data->rcdev.owner = THIS_MODULE;
+	data->rcdev.nr_resets = RST_MAX;
Use of_device_get_match_data() to determine the correct number of
resets.

regards
Philipp

Re: [PATCH v3 1/8] dt-bindings: vendor-prefixes: Add Sunplus

From: Rob Herring <robh@kernel.org>
Date: 2021-11-02 17:44:13

On Mon, 01 Nov 2021 13:01:51 +0800, Qin Jian wrote:
Add vendor prefix for Sunplus Technology Co., Ltd. (http://www.sunplus.com)

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
 1 file changed, 2 insertions(+)
Acked-by: Rob Herring <robh@kernel.org>

Re: [PATCH v3 7/8] dt-bindings: interrupt-controller: Add bindings for SP7021 interrupt controller

From: Rob Herring <robh@kernel.org>
Date: 2021-11-02 17:45:59

On Mon, 01 Nov 2021 13:01:57 +0800, Qin Jian wrote:
Add documentation to describe Sunplus SP7021 interrupt controller bindings.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 .../sunplus,sp7021-intc.yaml                  | 62 +++++++++++++++++++
 MAINTAINERS                                   |  1 +
 2 files changed, 63 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml
Reviewed-by: Rob Herring <robh@kernel.org>

答复: [PATCH v3 3/8] dt-bindings: reset: Add bindings for SP7021 reset driver

From: qinjian[覃健] <qinjian@cqplus1.com>
Date: 2021-11-03 01:23:57

quoted
+properties:
+  compatible:
+    enum:
+      - sunplus,sp7021-reset # Reset Controller on SP7021 and compatible SoCs
+      - sunplus,q645-reset # Reset Controller on Q645 and compatible SoCs
The commit message only mentions SP7021. Should Q645 be added later, or
is this patch missing the binding header for Q655?
I'll remove the Q645 entry, which will be added later when submitting Q645 support.
Thanks for your review. 

答复: [PATCH v3 4/8] reset: Add Sunplus SP7021 reset driver

From: qinjian[覃健] <qinjian@cqplus1.com>
Date: 2021-11-03 02:45:28

quoted
+	help
+	  This enables the reset driver support for Sunplus SP7021 SoC family.
+	  Say Y if you want to control reset signals by the reset controller.
+	  Otherwise, say N.
+	  This driver is selected automatically by platform config.
Which platform config?
arch/arm/mach-sunplus/Kconfig, which will be submit later.

Thanks for your review, I'll submit the new version based on your advice.

[PATCH v4 02/10] dt-bindings: arm: sunplus: Add bindings for Sunplus SP7021 SoC boards

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-11-04 03:04:23

This introduces bindings for boards based Sunplus SP7021 SoC.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 .../bindings/arm/sunplus,sp7021.yaml          | 27 +++++++++++++++++++
 MAINTAINERS                                   |  7 +++++
 2 files changed, 34 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
diff --git a/Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml b/Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
new file mode 100644
index 000000000..5b9985b73
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) Sunplus Co., Ltd. 2021
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/arm/sunplus,sp7021.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sunplus SP7021 Boards Device Tree Bindings
+
+maintainers:
+  - qinjian <qinjian@cqplus1.com>
+
+description: |
+  ARM platforms using Sunplus SP7021, an ARM Cortex A7 (4-cores) based SoC.
+  Wiki: https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
+
+properties:
+  $nodename:
+    const: '/'
+  compatible:
+    oneOf:
+      - items:
+          - const: sunplus,sp7021-achip
+
+additionalProperties: true
+
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index e0bca0de0..6a5422f10 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2655,6 +2655,13 @@ F:	drivers/clocksource/armv7m_systick.c
 N:	stm32
 N:	stm
 
+ARM/SUNPLUS SP7021 SOC SUPPORT
+M:	Qin Jian <qinjian@cqplus1.com>
+L:	linux-arm-kernel@lists.infradead.org (moderated for mon-subscribers)
+S:	Maintained
+W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
+F:	Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
+
 ARM/Synaptics SoC support
 M:	Jisheng Zhang <Jisheng.Zhang@synaptics.com>
 M:	Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
-- 
2.33.1

[PATCH v4 01/10] dt-bindings: vendor-prefixes: Add Sunplus

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-11-04 03:04:28

Add vendor prefix for Sunplus Technology Co., Ltd. (http://www.sunplus.com)

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
 1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index a867f7102..50d4ee5ac 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -1131,6 +1131,8 @@ patternProperties:
     description: Summit microelectronics
   "^sunchip,.*":
     description: Shenzhen Sunchip Technology Co., Ltd
+  "^sunplus,.*":
+    description: Sunplus Technology Co., Ltd.
   "^SUNW,.*":
     description: Sun Microsystems, Inc
   "^supermicro,.*":
-- 
2.33.1

[PATCH v4 04/10] reset: Add Sunplus SP7021 reset driver

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-11-04 03:04:28

Add reset driver for Sunplus SP7021 SoC.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 MAINTAINERS                   |   1 +
 drivers/reset/Kconfig         |   9 +++
 drivers/reset/Makefile        |   1 +
 drivers/reset/reset-sunplus.c | 133 ++++++++++++++++++++++++++++++++++
 4 files changed, 144 insertions(+)
 create mode 100644 drivers/reset/reset-sunplus.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 652f42cab..6caffd6d0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2662,6 +2662,7 @@ S:	Maintained
 W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
 F:	Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
 F:	Documentation/devicetree/bindings/reset/sunplus,reset.yaml
+F:	drivers/reset/reset-sunplus.c
 F:	include/dt-bindings/reset/sp-sp7021.h
 
 ARM/Synaptics SoC support
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index be799a5ab..5cdd022ad 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -224,6 +224,15 @@ config RESET_SOCFPGA
 	  This enables the reset driver for the SoCFPGA ARMv7 platforms. This
 	  driver gets initialized early during platform init calls.
 
+config RESET_SUNPLUS
+	bool "Sunplus SoCs Reset Driver"
+	depends on ARCH_SUNPLUS || COMPILE_TEST
+	help
+	  This enables the reset driver support for Sunplus SP7021 SoC family.
+	  Say Y if you want to control reset signals by the reset controller.
+	  Otherwise, say N.
+	  This driver is selected automatically by arm/mach-sunplus platform config.
+
 config RESET_SUNXI
 	bool "Allwinner SoCs Reset Driver" if COMPILE_TEST && !ARCH_SUNXI
 	default ARCH_SUNXI
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 21d46d886..f03403e97 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o
 obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
 obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o
 obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o
+obj-$(CONFIG_RESET_SUNPLUS) += reset-sunplus.o
 obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
 obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o
 obj-$(CONFIG_RESET_TI_SYSCON) += reset-ti-syscon.o
diff --git a/drivers/reset/reset-sunplus.c b/drivers/reset/reset-sunplus.c
new file mode 100644
index 000000000..6e29407d4
--- /dev/null
+++ b/drivers/reset/reset-sunplus.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/*
+ * SP7021 reset driver
+ *
+ * Copyright (C) Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset-controller.h>
+#include <linux/reboot.h>
+
+/* HIWORD_MASK */
+#define BITASSERT(id, val)	((1 << (16 + id)) | (val << id))
+
+struct sp_reset_data {
+	struct reset_controller_dev	rcdev;
+	void __iomem *membase;
+} *sp_reset;
+
+static inline struct sp_reset_data *
+to_sp_reset_data(struct reset_controller_dev *rcdev)
+{
+	return container_of(rcdev, struct sp_reset_data, rcdev);
+}
+
+static int sp_reset_update(struct reset_controller_dev *rcdev,
+			      unsigned long id, bool assert)
+{
+	struct sp_reset_data *data = to_sp_reset_data(rcdev);
+	int reg_width = sizeof(u32) / 2;
+	int bank = id / (reg_width * BITS_PER_BYTE);
+	int offset = id % (reg_width * BITS_PER_BYTE);
+	void __iomem *addr = data->membase + (bank * 4);
+
+	writel(BITASSERT(offset, assert), addr);
+
+	return 0;
+}
+
+static int sp_reset_assert(struct reset_controller_dev *rcdev,
+			      unsigned long id)
+{
+	return sp_reset_update(rcdev, id, true);
+}
+
+static int sp_reset_deassert(struct reset_controller_dev *rcdev,
+				unsigned long id)
+{
+	return sp_reset_update(rcdev, id, false);
+}
+
+static int sp_reset_status(struct reset_controller_dev *rcdev,
+			      unsigned long id)
+{
+	struct sp_reset_data *data = to_sp_reset_data(rcdev);
+	int reg_width = sizeof(u32) / 2;
+	int bank = id / (reg_width * BITS_PER_BYTE);
+	int offset = id % (reg_width * BITS_PER_BYTE);
+	u32 reg;
+
+	reg = readl(data->membase + (bank * 4));
+
+	return !!(reg & BIT(offset));
+}
+
+static int sp_restart(struct notifier_block *this, unsigned long mode,
+				void *cmd)
+{
+	sp_reset_assert(&sp_reset->rcdev, 0);
+	sp_reset_deassert(&sp_reset->rcdev, 0);
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block sp_restart_nb = {
+	.notifier_call = sp_restart,
+	.priority = 192,
+};
+
+static const struct reset_control_ops sp_reset_ops = {
+	.assert		= sp_reset_assert,
+	.deassert	= sp_reset_deassert,
+	.status		= sp_reset_status,
+};
+
+static const struct of_device_id sp_reset_dt_ids[] = {
+	{ .compatible = "sunplus,sp7021-reset", },
+	{ /* sentinel */ },
+};
+
+static int sp_reset_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	void __iomem *membase;
+	struct resource *res;
+
+	sp_reset = devm_kzalloc(&pdev->dev, sizeof(*sp_reset), GFP_KERNEL);
+	if (!sp_reset)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	membase = devm_ioremap_resource(dev, res);
+	if (IS_ERR(membase))
+		return PTR_ERR(membase);
+
+	sp_reset->membase = membase;
+	sp_reset->rcdev.owner = THIS_MODULE;
+	sp_reset->rcdev.nr_resets = resource_size(res) / 4 * 16; /* HIWORD_MASK */
+	sp_reset->rcdev.ops = &sp_reset_ops;
+	sp_reset->rcdev.of_node = dev->of_node;
+	register_restart_handler(&sp_restart_nb);
+
+	return devm_reset_controller_register(dev, &sp_reset->rcdev);
+}
+
+static struct platform_driver sp_reset_driver = {
+	.probe	= sp_reset_probe,
+	.driver = {
+		.name = "sunplus-reset",
+		.of_match_table	= sp_reset_dt_ids,
+	},
+};
+module_platform_driver(sp_reset_driver);
+
+MODULE_AUTHOR("Edwin Chiu <edwin.chiu@sunplus.com>");
+MODULE_DESCRIPTION("Sunplus Reset Driver");
+MODULE_LICENSE("GPL v2");
-- 
2.33.1

[PATCH v4 08/10] irqchip: Add Sunplus SP7021 interrupt controller driver

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-11-04 03:04:28

Add interrupt controller driver for Sunplus SP7021 SoC.

This is the interrupt controller in P-chip which collects all interrupt
sources in P-chip and routes them to parent interrupt controller in C-chip.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 MAINTAINERS                       |   1 +
 drivers/irqchip/Kconfig           |   9 +
 drivers/irqchip/Makefile          |   1 +
 drivers/irqchip/irq-sp7021-intc.c | 279 ++++++++++++++++++++++++++++++
 4 files changed, 290 insertions(+)
 create mode 100644 drivers/irqchip/irq-sp7021-intc.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 6b3bbe021..febbd97bf 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2665,6 +2665,7 @@ F:	Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
 F:	Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml
 F:	Documentation/devicetree/bindings/reset/sunplus,reset.yaml
 F:	drivers/clk/clk-sp7021.c
+F:	drivers/irqchip/irq-sp7021-intc.c
 F:	drivers/reset/reset-sunplus.c
 F:	include/dt-bindings/clock/sp-sp7021.h
 F:	include/dt-bindings/reset/sp-sp7021.h
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index aca7b595c..6f0bc0871 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -602,4 +602,13 @@ config APPLE_AIC
 	  Support for the Apple Interrupt Controller found on Apple Silicon SoCs,
 	  such as the M1.
 
+config SUNPLUS_SP7021_INTC
+	bool "Sunplus SP7021 interrupt controller"
+	help
+	  Support for the Sunplus SP7021 Interrupt Controller IP core.
+	  SP7021 SoC has 2 Chips: C-Chip & P-Chip. This is used as a
+	  chained controller, routed all interrupt source in P-Chip to
+	  the primary controller on C-Chip.
+	  This is selected automatically by platform config.
+
 endmenu
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index f88cbf36a..75411f654 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -116,3 +116,4 @@ obj-$(CONFIG_MACH_REALTEK_RTL)		+= irq-realtek-rtl.o
 obj-$(CONFIG_WPCM450_AIC)		+= irq-wpcm450-aic.o
 obj-$(CONFIG_IRQ_IDT3243X)		+= irq-idt3243x.o
 obj-$(CONFIG_APPLE_AIC)			+= irq-apple-aic.o
+obj-$(CONFIG_SUNPLUS_SP7021_INTC)	+= irq-sp7021-intc.o
diff --git a/drivers/irqchip/irq-sp7021-intc.c b/drivers/irqchip/irq-sp7021-intc.c
new file mode 100644
index 000000000..4c995d4e3
--- /dev/null
+++ b/drivers/irqchip/irq-sp7021-intc.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/*
+ * Copyright (C) Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/io.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#define SP_INTC_HWIRQ_MIN	0
+#define SP_INTC_HWIRQ_MAX	223
+
+#define SP_INTC_NR_IRQS		(SP_INTC_HWIRQ_MAX - SP_INTC_HWIRQ_MIN + 1)
+#define SP_INTC_NR_GROUPS	DIV_ROUND_UP(SP_INTC_NR_IRQS, 32)
+#define SP_INTC_REG_SIZE	(SP_INTC_NR_GROUPS * 4)
+
+/* REG_GROUP_0 regs */
+#define REG_INTR_TYPE		(sp_intc.g0)
+#define REG_INTR_POLARITY	(REG_INTR_TYPE     + SP_INTC_REG_SIZE)
+#define REG_INTR_PRIORITY	(REG_INTR_POLARITY + SP_INTC_REG_SIZE)
+#define REG_INTR_MASK		(REG_INTR_PRIORITY + SP_INTC_REG_SIZE)
+
+/* REG_GROUP_1 regs */
+#define REG_INTR_CLEAR		(sp_intc.g1)
+#define REG_MASKED_EXT1		(REG_INTR_CLEAR    + SP_INTC_REG_SIZE)
+#define REG_MASKED_EXT0		(REG_MASKED_EXT1   + SP_INTC_REG_SIZE)
+#define REG_INTR_GROUP		(REG_INTR_CLEAR    + 31 * 4)
+
+#define GROUP_MASK			(BIT(SP_INTC_NR_GROUPS) - 1)
+#define GROUP_SHIFT_EXT1	(0)
+#define GROUP_SHIFT_EXT0	(8)
+
+/*
+ * When GPIO_INT0~7 set to edge trigger, doesn't work properly.
+ * WORKAROUND: change it to level trigger, and toggle the polarity
+ * at ACK/Handler to make the HW work.
+ */
+#define GPIO_INT0_HWIRQ		120
+#define GPIO_INT7_HWIRQ		127
+#define IS_GPIO_INT(irq)	(((irq) >= GPIO_INT0_HWIRQ) && ((irq) <= GPIO_INT7_HWIRQ))
+/* state idx */
+enum {
+	_IS_EDGE = 0,
+	_IS_LOW,
+	_IS_ACTIVE
+};
+#define STATE_BIT(irq, idx)			((irq - GPIO_INT0_HWIRQ) * 3 + idx)
+#define ASSIGN_STATE(irq, idx, v)	assign_bit(STATE_BIT(irq, idx), sp_intc.states, v)
+#define TEST_STATE(irq, idx)		test_bit(STATE_BIT(irq, idx), sp_intc.states)
+
+static struct sp_intctl {
+	/*
+	 * REG_GROUP_0: include type/polarity/priority/mask regs.
+	 * REG_GROUP_1: include clear/masked_ext0/masked_ext1/group regs.
+	 */
+	void __iomem *g0; // REG_GROUP_0 base
+	void __iomem *g1; // REG_GROUP_1 base
+
+	struct irq_domain *domain;
+	raw_spinlock_t lock;
+
+	/*
+	 * store GPIO_INT states
+	 * each interrupt has 3 states: is_edge, is_low, is_active
+	 */
+	DECLARE_BITMAP(states, (GPIO_INT7_HWIRQ - GPIO_INT0_HWIRQ + 1) * 3);
+} sp_intc;
+
+static struct irq_chip sp_intc_chip;
+
+static void sp_intc_assign_bit(u32 hwirq, void __iomem *base, bool value)
+{
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&sp_intc.lock, flags);
+	__assign_bit(hwirq, base, value);
+	raw_spin_unlock_irqrestore(&sp_intc.lock, flags);
+}
+
+static void sp_intc_ack_irq(struct irq_data *d)
+{
+	u32 hwirq = d->hwirq;
+
+	if (unlikely(IS_GPIO_INT(hwirq) && TEST_STATE(hwirq, _IS_EDGE))) { // WORKAROUND
+		sp_intc_assign_bit(hwirq, REG_INTR_POLARITY, !TEST_STATE(hwirq, _IS_LOW));
+		ASSIGN_STATE(hwirq, _IS_ACTIVE, true);
+	}
+
+	sp_intc_assign_bit(hwirq, REG_INTR_CLEAR, 1);
+}
+
+static void sp_intc_mask_irq(struct irq_data *d)
+{
+	sp_intc_assign_bit(d->hwirq, REG_INTR_MASK, 0);
+}
+
+static void sp_intc_unmask_irq(struct irq_data *d)
+{
+	sp_intc_assign_bit(d->hwirq, REG_INTR_MASK, 1);
+}
+
+static int sp_intc_set_type(struct irq_data *d, unsigned int type)
+{
+	u32 hwirq = d->hwirq;
+	bool is_edge = !(type & IRQ_TYPE_LEVEL_MASK);
+	bool is_low = (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_EDGE_FALLING);
+
+	irq_set_handler_locked(d, is_edge ? handle_edge_irq : handle_level_irq);
+
+	if (unlikely(IS_GPIO_INT(hwirq) && is_edge)) { // WORKAROUND
+		/* store states */
+		ASSIGN_STATE(hwirq, _IS_EDGE, is_edge);
+		ASSIGN_STATE(hwirq, _IS_LOW, is_low);
+		ASSIGN_STATE(hwirq, _IS_ACTIVE, false);
+		/* change to level */
+		is_edge = false;
+	}
+
+	sp_intc_assign_bit(hwirq, REG_INTR_TYPE, is_edge);
+	sp_intc_assign_bit(hwirq, REG_INTR_POLARITY, is_low);
+
+	return 0;
+}
+
+static int sp_intc_get_ext_irq(int ext_num)
+{
+	void __iomem *base = ext_num ? REG_MASKED_EXT1 : REG_MASKED_EXT0;
+	u32 shift = ext_num ? GROUP_SHIFT_EXT1 : GROUP_SHIFT_EXT0;
+	u32 groups;
+	u32 pending_group;
+	u32 group;
+	u32 pending_irq;
+
+	groups = readl_relaxed(REG_INTR_GROUP);
+	pending_group = (groups >> shift) & GROUP_MASK;
+	if (!pending_group)
+		return -1;
+
+	group = fls(pending_group) - 1;
+	pending_irq = readl_relaxed(base + group * 4);
+	if (!pending_irq)
+		return -1;
+
+	return (group * 32) + fls(pending_irq) - 1;
+}
+
+static void sp_intc_handle_ext_cascaded(struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	int ext_num = (int)irq_desc_get_handler_data(desc);
+	int hwirq;
+
+	chained_irq_enter(chip, desc);
+
+	while ((hwirq = sp_intc_get_ext_irq(ext_num)) >= 0) {
+		if (unlikely(IS_GPIO_INT(hwirq) && TEST_STATE(hwirq, _IS_ACTIVE))) { // WORKAROUND
+			ASSIGN_STATE(hwirq, _IS_ACTIVE, false);
+			sp_intc_assign_bit(hwirq, REG_INTR_POLARITY, TEST_STATE(hwirq, _IS_LOW));
+		} else {
+			generic_handle_domain_irq(sp_intc.domain, hwirq);
+		}
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+#ifdef CONFIG_SMP
+static int sp_intc_set_affinity(struct irq_data *d, const struct cpumask *mask, bool force)
+{
+	return -EINVAL;
+}
+#endif
+
+static struct irq_chip sp_intc_chip = {
+	.name = "sp_intc",
+	.irq_ack = sp_intc_ack_irq,
+	.irq_mask = sp_intc_mask_irq,
+	.irq_unmask = sp_intc_unmask_irq,
+	.irq_set_type = sp_intc_set_type,
+#ifdef CONFIG_SMP
+	.irq_set_affinity = sp_intc_set_affinity,
+#endif
+};
+
+static int sp_intc_irq_domain_map(struct irq_domain *domain,
+	unsigned int irq, irq_hw_number_t hwirq)
+{
+	irq_set_chip_and_handler(irq, &sp_intc_chip, handle_level_irq);
+	irq_set_chip_data(irq, &sp_intc_chip);
+	irq_set_noprobe(irq);
+
+	return 0;
+}
+
+static const struct irq_domain_ops sp_intc_dm_ops = {
+	.xlate = irq_domain_xlate_twocell,
+	.map = sp_intc_irq_domain_map,
+};
+
+static int sp_intc_irq_map(struct device_node *node, int i)
+{
+	unsigned int irq;
+
+	irq = irq_of_parse_and_map(node, i);
+	if (!irq)
+		return -ENOENT;
+
+	irq_set_chained_handler_and_data(irq, sp_intc_handle_ext_cascaded, (void *)i);
+
+	return 0;
+}
+
+void sp_intc_set_ext(u32 hwirq, int ext_num)
+{
+	sp_intc_assign_bit(hwirq, REG_INTR_PRIORITY, !ext_num);
+}
+EXPORT_SYMBOL_GPL(sp_intc_set_ext);
+
+int __init sp_intc_init_dt(struct device_node *node, struct device_node *parent)
+{
+	int i, ret;
+
+	sp_intc.g0 = of_iomap(node, 0);
+	if (!sp_intc.g0)
+		return -ENXIO;
+
+	sp_intc.g1 = of_iomap(node, 1);
+	if (!sp_intc.g1) {
+		ret = -ENXIO;
+		goto out_unmap0;
+	}
+
+	/* initial regs */
+	for (i = 0; i < SP_INTC_NR_GROUPS; i++) {
+		/* all mask */
+		writel_relaxed(0, REG_INTR_MASK + i * 4);
+		/* all edge */
+		writel_relaxed(~0, REG_INTR_TYPE + i * 4);
+		/* all high-active */
+		writel_relaxed(0, REG_INTR_POLARITY + i * 4);
+		/* all EXT_INT0 */
+		writel_relaxed(~0, REG_INTR_PRIORITY + i * 4);
+		/* all clear */
+		writel_relaxed(~0, REG_INTR_CLEAR + i * 4);
+	}
+
+	sp_intc.domain = irq_domain_add_linear(node, SP_INTC_NR_IRQS,
+			&sp_intc_dm_ops, &sp_intc);
+	if (!sp_intc.domain) {
+		ret = -ENOMEM;
+		goto out_unmap1;
+	}
+
+	raw_spin_lock_init(&sp_intc.lock);
+
+	ret = sp_intc_irq_map(node, 0); // EXT_INT0
+	if (ret)
+		goto out_domain;
+
+	ret = sp_intc_irq_map(node, 1); // EXT_INT1
+	if (ret)
+		goto out_domain;
+
+	return 0;
+
+out_domain:
+	irq_domain_remove(sp_intc.domain);
+out_unmap1:
+	iounmap(sp_intc.g1);
+out_unmap0:
+	iounmap(sp_intc.g0);
+
+	return ret;
+}
+IRQCHIP_DECLARE(sp_intc, "sunplus,sp7021-intc", sp_intc_init_dt);
-- 
2.33.1

[PATCH v4 07/10] dt-bindings: interrupt-controller: Add bindings for SP7021 interrupt controller

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-11-04 03:04:30

Add documentation to describe Sunplus SP7021 interrupt controller bindings.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 .../sunplus,sp7021-intc.yaml                  | 62 +++++++++++++++++++
 MAINTAINERS                                   |  1 +
 2 files changed, 63 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml
diff --git a/Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml b/Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml
new file mode 100644
index 000000000..5daeab63c
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) Sunplus Co., Ltd. 2021
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/interrupt-controller/sunplus,sp7021-intc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sunplus SP7021 SoC Interrupt Controller Device Tree Bindings
+
+maintainers:
+  - Qin Jian <qinjian@cqplus1.com>
+
+properties:
+  compatible:
+    items:
+      - const: sunplus,sp7021-intc
+
+  interrupt-controller: true
+
+  "#interrupt-cells":
+    const: 2
+    description:
+      The first cell is the IRQ number, the second cell is the trigger
+      type as defined in interrupt.txt in this directory.
+
+  reg:
+    maxItems: 2
+    description:
+      Specifies base physical address(s) and size of the controller regs.
+      The 1st region include type/polarity/priority/mask regs.
+      The 2nd region include clear/masked_ext0/masked_ext1/group regs.
+
+  interrupts:
+    maxItems: 2
+    description:
+      EXT_INT0 & EXT_INT1, 2 interrupts references to primary interrupt
+      controller.
+
+required:
+  - compatible
+  - interrupt-controller
+  - "#interrupt-cells"
+  - reg
+  - interrupts
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    intc: interrupt-controller@9c000780 {
+        compatible = "sunplus,sp7021-intc";
+        interrupt-controller;
+        #interrupt-cells = <2>;
+        reg = <0x9c000780 0x80>, <0x9c000a80 0x80>;
+        interrupt-parent = <&gic>;
+        interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>, /* EXT_INT0 */
+                     <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>; /* EXT_INT1 */
+    };
+
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 5069f552f..6b3bbe021 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2662,6 +2662,7 @@ S:	Maintained
 W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
 F:	Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
 F:	Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
+F:	Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml
 F:	Documentation/devicetree/bindings/reset/sunplus,reset.yaml
 F:	drivers/clk/clk-sp7021.c
 F:	drivers/reset/reset-sunplus.c
-- 
2.33.1

[PATCH v4 06/10] clk: Add Sunplus SP7021 clock driver

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-11-04 03:04:32

Add clock driver for Sunplus SP7021 SoC.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 MAINTAINERS              |   1 +
 drivers/clk/Kconfig      |   9 +
 drivers/clk/Makefile     |   1 +
 drivers/clk/clk-sp7021.c | 725 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 736 insertions(+)
 create mode 100644 drivers/clk/clk-sp7021.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 90ebb823f..5069f552f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2663,6 +2663,7 @@ W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
 F:	Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
 F:	Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
 F:	Documentation/devicetree/bindings/reset/sunplus,reset.yaml
+F:	drivers/clk/clk-sp7021.c
 F:	drivers/reset/reset-sunplus.c
 F:	include/dt-bindings/clock/sp-sp7021.h
 F:	include/dt-bindings/reset/sp-sp7021.h
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index c5b3dc973..1cd7ae7a3 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -334,6 +334,15 @@ config COMMON_CLK_VC5
 	  This driver supports the IDT VersaClock 5 and VersaClock 6
 	  programmable clock generators.
 
+config COMMON_CLK_SP7021
+	bool "Clock driver for Sunplus SP7021 SoC"
+	help
+	  This driver supports the Sunplus SP7021 SoC clocks.
+	  It implemented SP7021 PLLs/gate.
+	  Not all features of the PLL are currently supported
+	  by the driver.
+	  This driver is selected automatically by platform config.
+
 config COMMON_CLK_STM32MP157
 	def_bool COMMON_CLK && MACH_STM32MP157
 	help
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index e42312121..f15bb5070 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_COMMON_CLK_SI5351)		+= clk-si5351.o
 obj-$(CONFIG_COMMON_CLK_SI514)		+= clk-si514.o
 obj-$(CONFIG_COMMON_CLK_SI544)		+= clk-si544.o
 obj-$(CONFIG_COMMON_CLK_SI570)		+= clk-si570.o
+obj-$(CONFIG_COMMON_CLK_SP7021)		+= clk-sp7021.o
 obj-$(CONFIG_COMMON_CLK_STM32F)		+= clk-stm32f4.o
 obj-$(CONFIG_COMMON_CLK_STM32H7)	+= clk-stm32h7.o
 obj-$(CONFIG_COMMON_CLK_STM32MP157)	+= clk-stm32mp1.o
diff --git a/drivers/clk/clk-sp7021.c b/drivers/clk/clk-sp7021.c
new file mode 100644
index 000000000..e5e430a66
--- /dev/null
+++ b/drivers/clk/clk-sp7021.c
@@ -0,0 +1,725 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/*
+ * Copyright (C) Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+//#define DEBUG
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <dt-bindings/clock/sp-sp7021.h>
+
+#ifndef clk_readl
+#define clk_readl  readl
+#define clk_writel writel
+#endif
+
+#define MASK_SET(shift, width, value) \
+({ \
+	u32 m = ((1 << (width)) - 1) << (shift); \
+	(m << 16) | (((value) << (shift)) & m); \
+})
+#define MASK_GET(shift, width, value)	(((value) >> (shift)) & ((1 << (width)) - 1))
+
+#define REG(i)		(pll_regs + (i) * 4)
+
+#define PLLA_CTL	REG(7)
+#define PLLE_CTL	REG(12)
+#define PLLF_CTL	REG(13)
+#define PLLTV_CTL	REG(14)
+#define PLLSYS_CTL	REG(26)
+
+#define EXTCLK		"extclk"
+
+/* speical div_width values for PLLTV/PLLA */
+#define DIV_TV		33
+#define DIV_A		34
+
+/* PLLTV parameters */
+enum {
+	SEL_FRA,
+	SDM_MOD,
+	PH_SEL,
+	NFRA,
+	DIVR,
+	DIVN,
+	DIVM,
+	P_MAX
+};
+
+struct sp_pll {
+	struct clk_hw hw;
+	void __iomem *reg;
+	spinlock_t *lock;
+	int div_shift;
+	int div_width;
+	int pd_bit;		/* power down bit idx */
+	int bp_bit;		/* bypass bit idx */
+	unsigned long brate;	/* base rate, TODO: replace brate with muldiv */
+	u32 p[P_MAX];	/* for hold PLLTV/PLLA parameters */
+};
+#define to_sp_pll(_hw)	container_of(_hw, struct sp_pll, hw)
+
+#define clk_regs	(moon_regs + 0x000) /* G0 ~ CLKEN */
+#define pll_regs	(moon_regs + 0x200) /* G4 ~ PLL */
+static void __iomem *moon_regs;
+
+#define P_EXTCLK	(1 << 16)
+static const char * const parents[] = {
+	"pllsys",
+	"extclk",
+};
+
+static const u32 gates[] = {
+	CLK_SYSTEM,
+	CLK_RTC,
+	CLK_IOCTL,
+	CLK_IOP,
+	CLK_OTPRX,
+	CLK_NOC,
+	CLK_BR,
+	CLK_RBUS_L00,
+	CLK_SPIFL,
+	CLK_SDCTRL0,
+	CLK_PERI0 | P_EXTCLK,
+	CLK_A926,
+	CLK_UMCTL2,
+	CLK_PERI1 | P_EXTCLK,
+
+	CLK_DDR_PHY0,
+	CLK_ACHIP,
+	CLK_STC0,
+	CLK_STC_AV0,
+	CLK_STC_AV1,
+	CLK_STC_AV2,
+	CLK_UA0 | P_EXTCLK,
+	CLK_UA1 | P_EXTCLK,
+	CLK_UA2 | P_EXTCLK,
+	CLK_UA3 | P_EXTCLK,
+	CLK_UA4 | P_EXTCLK,
+	CLK_HWUA | P_EXTCLK,
+	CLK_DDC0,
+	CLK_UADMA | P_EXTCLK,
+
+	CLK_CBDMA0,
+	CLK_CBDMA1,
+	CLK_SPI_COMBO_0,
+	CLK_SPI_COMBO_1,
+	CLK_SPI_COMBO_2,
+	CLK_SPI_COMBO_3,
+	CLK_AUD,
+	CLK_USBC0,
+	CLK_USBC1,
+	CLK_UPHY0,
+	CLK_UPHY1,
+
+	CLK_I2CM0,
+	CLK_I2CM1,
+	CLK_I2CM2,
+	CLK_I2CM3,
+	CLK_PMC,
+	CLK_CARD_CTL0,
+	CLK_CARD_CTL1,
+
+	CLK_CARD_CTL4,
+	CLK_BCH,
+	CLK_DDFCH,
+	CLK_CSIIW0,
+	CLK_CSIIW1,
+	CLK_MIPICSI0,
+	CLK_MIPICSI1,
+
+	CLK_HDMI_TX,
+	CLK_VPOST,
+
+	CLK_TGEN,
+	CLK_DMIX,
+	CLK_TCON,
+	CLK_INTERRUPT,
+
+	CLK_RGST,
+	CLK_GPIO,
+	CLK_RBUS_TOP,
+
+	CLK_MAILBOX,
+	CLK_SPIND,
+	CLK_I2C2CBUS,
+	CLK_SEC,
+	CLK_GPOST0,
+	CLK_DVE,
+
+	CLK_OSD0,
+	CLK_DISP_PWM,
+	CLK_UADBG,
+	CLK_DUMMY_MASTER,
+	CLK_FIO_CTL,
+	CLK_FPGA,
+	CLK_L2SW,
+	CLK_ICM,
+	CLK_AXI_GLOBAL,
+};
+static struct clk *clks[CLK_MAX];
+static struct clk_onecell_data clk_data;
+
+static DEFINE_SPINLOCK(plla_lock);
+static DEFINE_SPINLOCK(plle_lock);
+static DEFINE_SPINLOCK(pllf_lock);
+static DEFINE_SPINLOCK(pllsys_lock);
+static DEFINE_SPINLOCK(plltv_lock);
+
+#define _M			1000000UL
+#define F_27M		(27 * _M)
+
+/******************************************** PLL_TV *******************************************/
+
+//#define PLLTV_STEP_DIR (?) /* Unit: HZ */
+
+/* TODO: set proper FVCO range */
+#define FVCO_MIN	(100 * _M)
+#define FVCO_MAX	(200 * _M)
+
+#define F_MIN		(FVCO_MIN / 8)
+#define F_MAX		(FVCO_MAX)
+
+static long plltv_integer_div(struct sp_pll *clk, unsigned long freq)
+{
+	/* valid m values: 27M must be divisible by m, 0 means end */
+	static const u32 m_table[] = {
+		1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 27, 30, 32, 0
+	};
+	u32 m, n, r;
+#ifdef PLLTV_STEP_DIR
+	u32 step = (PLLTV_STEP_DIR > 0) ? PLLTV_STEP_DIR : -PLLTV_STEP_DIR;
+	int calc_times = 1000000 / step;
+#endif
+	unsigned long fvco, nf;
+
+	/* check freq */
+	if (freq < F_MIN) {
+		pr_warn("%s: %s freq:%lu < F_MIN:%lu, round up\n",
+			__func__, clk_hw_get_name(&clk->hw), freq, F_MIN);
+		freq = F_MIN;
+	} else if (freq > F_MAX) {
+		pr_warn("%s: %s freq:%lu > F_MAX:%lu, round down\n",
+			__func__, clk_hw_get_name(&clk->hw), freq, F_MAX);
+		freq = F_MAX;
+	}
+
+#ifdef PLLTV_STEP_DIR
+	if ((freq % step) != 0)
+		freq += step - (freq % step) + ((PLLTV_STEP_DIR > 0) ? 0 : PLLTV_STEP_DIR);
+#endif
+
+#ifdef PLLTV_STEP_DIR
+CALC:
+	if (!calc_times) {
+		pr_err("%s: %s freq:%lu out of recalc times\n",
+			__func__, clk_hw_get_name(&clk->hw), freq);
+		return -ETIMEOUT;
+	}
+#endif
+
+	/* DIVR 0~3 */
+	for (r = 0; r <= 3; r++) {
+		fvco = freq << r;
+		if (fvco <= FVCO_MAX)
+			break;
+	}
+
+	/* DIVM */
+	for (m = 0; m_table[m]; m++) {
+		nf = fvco * m_table[m];
+		n = nf / F_27M;
+		if ((n * F_27M) == nf)
+			break;
+	}
+	m = m_table[m];
+
+	if (!m) {
+#ifdef PLLTV_STEP_DIR
+		freq += PLLTV_STEP_DIR;
+		calc_times--;
+		goto CALC;
+#else
+		pr_err("%s: %s freq:%lu not found a valid setting\n",
+			__func__, clk_hw_get_name(&clk->hw), freq);
+		return -EINVAL;
+#endif
+	}
+
+	/* save parameters */
+	clk->p[SEL_FRA] = 0;
+	clk->p[DIVR]    = r;
+	clk->p[DIVN]    = n;
+	clk->p[DIVM]    = m;
+
+	return freq;
+}
+
+/* parameters for PLLTV fractional divider */
+static const u32 pt[][5] = {
+	/* conventional fractional */
+	{
+		1,			// factor
+		5,			// 5 * p0 (nint)
+		1,			// 1 * p0
+		F_27M,			// F_27M / p0
+		1,			// p0 / p2
+	},
+	/* phase rotation */
+	{
+		10,			// factor
+		54,			// 5.4 * p0 (nint)
+		2,			// 0.2 * p0
+		F_27M / 10,		// F_27M / p0
+		5,			// p0 / p2
+	},
+};
+static const u32 mods[] = { 91, 55 }; /* SDM_MOD mod values */
+
+static long plltv_fractional_div(struct sp_pll *clk, unsigned long freq)
+{
+	u32 m, r;
+	u32 nint, nfra;
+	u32 diff_min_quotient = 210000000, diff_min_remainder = 0;
+	u32 diff_min_sign = 0;
+	unsigned long fvco, nf, f, fout = 0;
+	int sdm, ph;
+
+	/* check freq */
+	if (freq < F_MIN) {
+		pr_warn("%s: %s freq:%lu < F_MIN:%lu, round up\n",
+			__func__, clk_hw_get_name(&clk->hw), freq, F_MIN);
+		freq = F_MIN;
+	} else if (freq > F_MAX) {
+		pr_warn("%s: %s freq:%lu > F_MAX:%lu, round down\n",
+			__func__, clk_hw_get_name(&clk->hw), freq, F_MAX);
+		freq = F_MAX;
+	}
+
+	/* DIVR 0~3 */
+	for (r = 0; r <= 3; r++) {
+		fvco = freq << r;
+		if (fvco <= FVCO_MAX)
+			break;
+	}
+	f = F_27M >> r;
+
+	/* PH_SEL 1/0 */
+	for (ph = 1; ph >= 0; ph--) {
+		const u32 *pp = pt[ph];
+		u32 ms = 1;
+
+		/* SDM_MOD 0/1 */
+		for (sdm = 0; sdm <= 1; sdm++) {
+			u32 mod = mods[sdm];
+
+			/* DIVM 1~32 */
+			for (m = ms; m <= 32; m++) {
+				u32 diff_freq;
+				u32 diff_freq_quotient = 0, diff_freq_remainder = 0;
+				u32 diff_freq_sign = 0; /* 0:Positive number, 1:Negative number */
+
+				nf = fvco * m;
+				nint = nf / pp[3];
+
+				if (nint < pp[1])
+					continue;
+				if (nint > pp[1])
+					break;
+
+				nfra = (((nf % pp[3]) * mod * pp[4]) + (F_27M / 2)) / F_27M;
+				if (nfra)
+					diff_freq = (f * (nint + pp[2]) / pp[0]) -
+								(f * (mod - nfra) / mod / pp[4]);
+				else
+					diff_freq = (f * (nint) / pp[0]);
+
+				diff_freq_quotient  = diff_freq / m;
+				diff_freq_remainder = ((diff_freq % m) * 1000) / m;
+
+				if (freq > diff_freq_quotient) {
+					diff_freq_quotient  = freq - diff_freq_quotient - 1;
+					diff_freq_remainder = 1000 - diff_freq_remainder;
+					diff_freq_sign = 1;
+				} else {
+					diff_freq_quotient = diff_freq_quotient - freq;
+					diff_freq_sign = 0;
+				}
+
+				if ((diff_min_quotient > diff_freq_quotient) ||
+					((diff_min_quotient == diff_freq_quotient) &&
+					(diff_min_remainder > diff_freq_remainder))) {
+
+					/* found a closer freq, save parameters */
+					clk->p[SEL_FRA] = 1;
+					clk->p[SDM_MOD] = sdm;
+					clk->p[PH_SEL]  = ph;
+					clk->p[NFRA]    = nfra;
+					clk->p[DIVR]    = r;
+					clk->p[DIVM]    = m;
+
+					fout = diff_freq / m;
+					diff_min_quotient = diff_freq_quotient;
+					diff_min_remainder = diff_freq_remainder;
+					diff_min_sign = diff_freq_sign;
+				}
+			}
+		}
+	}
+
+	if (!fout) {
+		pr_err("%s: %s freq:%lu not found a valid setting\n",
+			__func__, clk_hw_get_name(&clk->hw), freq);
+		return -EINVAL;
+	}
+
+	return fout;
+}
+
+static long plltv_div(struct sp_pll *clk, unsigned long freq)
+{
+	if (freq % 100)
+		return plltv_fractional_div(clk, freq);
+	else
+		return plltv_integer_div(clk, freq);
+}
+
+static void plltv_set_rate(struct sp_pll *clk)
+{
+	u32 reg;
+
+	reg  = MASK_SET(1, 1, clk->p[SEL_FRA]);
+	reg |= MASK_SET(2, 1, clk->p[SDM_MOD]);
+	reg |= MASK_SET(4, 1, clk->p[PH_SEL]);
+	reg |= MASK_SET(6, 7, clk->p[NFRA]);
+	clk_writel(reg, clk->reg);
+
+	reg  = MASK_SET(7, 2, clk->p[DIVR]);
+	clk_writel(reg, clk->reg + 4);
+
+	reg  = MASK_SET(0, 8, clk->p[DIVN] - 1);
+	reg |= MASK_SET(8, 7, clk->p[DIVM] - 1);
+	clk_writel(reg, clk->reg + 8);
+}
+
+/******************************************** PLL_A ********************************************/
+
+/* from Q628_PLLs_REG_setting.xlsx */
+struct {
+	u32 rate;
+	u32 regs[5];
+} pa[] = {
+	{
+		.rate = 135475200,
+		.regs = {
+			0x4801,
+			0x02df,
+			0x248f,
+			0x0211,
+			0x33e9
+		}
+	},
+	{
+		.rate = 147456000,
+		.regs = {
+			0x4801,
+			0x1adf,
+			0x2490,
+			0x0349,
+			0x33e9
+		}
+	},
+	{
+		.rate = 196608000,
+		.regs = {
+			0x4801,
+			0x42ef,
+			0x2495,
+			0x01c6,
+			0x33e9
+		}
+	},
+};
+
+static void plla_set_rate(struct sp_pll *clk)
+{
+	const u32 *pp = pa[clk->p[0]].regs;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(pa->regs); i++)
+		clk_writel(0xffff0000 | pp[i], clk->reg + (i * 4));
+}
+
+static long plla_round_rate(struct sp_pll *clk, unsigned long rate)
+{
+	int i = ARRAY_SIZE(pa);
+
+	while (--i) {
+		if (rate >= pa[i].rate)
+			break;
+	}
+	clk->p[0] = i;
+	return pa[i].rate;
+}
+
+/******************************************* SP_PLL ********************************************/
+
+static long sp_pll_calc_div(struct sp_pll *clk, unsigned long rate)
+{
+	u32 fbdiv;
+	u32 max = 1 << clk->div_width;
+
+	fbdiv = DIV_ROUND_CLOSEST(rate, clk->brate);
+	if (fbdiv > max)
+		fbdiv = max;
+	return fbdiv;
+}
+
+static long sp_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+		unsigned long *prate)
+{
+	struct sp_pll *clk = to_sp_pll(hw);
+	long ret;
+
+	if (rate == *prate)
+		ret = *prate; /* bypass */
+	else if (clk->div_width == DIV_A) {
+		ret = plla_round_rate(clk, rate);
+	} else if (clk->div_width == DIV_TV) {
+		ret = plltv_div(clk, rate);
+		if (ret < 0)
+			ret = *prate;
+	} else
+		ret = sp_pll_calc_div(clk, rate) * clk->brate;
+
+	return ret;
+}
+
+static unsigned long sp_pll_recalc_rate(struct clk_hw *hw,
+		unsigned long prate)
+{
+	struct sp_pll *clk = to_sp_pll(hw);
+	u32 reg = clk_readl(clk->reg);
+	unsigned long ret;
+
+	if (reg & BIT(clk->bp_bit)) {
+		ret = prate; /* bypass */
+	} else if (clk->div_width == DIV_A) {
+		ret = pa[clk->p[0]].rate;
+	} else if (clk->div_width == DIV_TV) {
+		u32 m, r, reg2;
+
+		r = MASK_GET(7, 2, clk_readl(clk->reg + 4));
+		reg2 = clk_readl(clk->reg + 8);
+		m = MASK_GET(8, 7, reg2) + 1;
+
+		if (reg & BIT(1)) { /* SEL_FRA */
+			/* fractional divider */
+			u32 sdm  = MASK_GET(2, 1, reg);
+			u32 ph   = MASK_GET(4, 1, reg);
+			u32 nfra = MASK_GET(6, 7, reg);
+			const u32 *pp = pt[ph];
+
+			ret = prate >> r;
+			ret = (ret * (pp[1] + pp[2]) / pp[0]) -
+				  (ret * (mods[sdm] - nfra) / mods[sdm] / pp[4]);
+			ret /= m;
+		} else {
+			/* integer divider */
+			u32 n = MASK_GET(0, 8, reg2) + 1;
+
+			ret = (prate / m * n) >> r;
+		}
+	} else {
+		u32 fbdiv = MASK_GET(clk->div_shift, clk->div_width, reg) + 1;
+
+		ret = clk->brate * fbdiv;
+	}
+
+	return ret;
+}
+
+static int sp_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+		unsigned long prate)
+{
+	struct sp_pll *clk = to_sp_pll(hw);
+	unsigned long flags;
+	u32 reg;
+
+	spin_lock_irqsave(clk->lock, flags);
+
+	reg = BIT(clk->bp_bit + 16); /* HIWORD_MASK */
+
+	if (rate == prate)
+		reg |= BIT(clk->bp_bit); /* bypass */
+	else if (clk->div_width == DIV_A)
+		plla_set_rate(clk);
+	else if (clk->div_width == DIV_TV)
+		plltv_set_rate(clk);
+	else if (clk->div_width) {
+		u32 fbdiv = sp_pll_calc_div(clk, rate);
+
+		reg |= MASK_SET(clk->div_shift, clk->div_width, fbdiv - 1);
+	}
+
+	clk_writel(reg, clk->reg);
+
+	spin_unlock_irqrestore(clk->lock, flags);
+
+	return 0;
+}
+
+static int sp_pll_enable(struct clk_hw *hw)
+{
+	struct sp_pll *clk = to_sp_pll(hw);
+	unsigned long flags;
+
+	spin_lock_irqsave(clk->lock, flags);
+	clk_writel(BIT(clk->pd_bit + 16) | BIT(clk->pd_bit), clk->reg); /* power up */
+	spin_unlock_irqrestore(clk->lock, flags);
+
+	return 0;
+}
+
+static void sp_pll_disable(struct clk_hw *hw)
+{
+	struct sp_pll *clk = to_sp_pll(hw);
+	unsigned long flags;
+
+	spin_lock_irqsave(clk->lock, flags);
+	clk_writel(BIT(clk->pd_bit + 16), clk->reg); /* power down */
+	spin_unlock_irqrestore(clk->lock, flags);
+}
+
+static int sp_pll_is_enabled(struct clk_hw *hw)
+{
+	struct sp_pll *clk = to_sp_pll(hw);
+
+	return clk_readl(clk->reg) & BIT(clk->pd_bit);
+}
+
+static const struct clk_ops sp_pll_ops = {
+	.enable = sp_pll_enable,
+	.disable = sp_pll_disable,
+	.is_enabled = sp_pll_is_enabled,
+	.round_rate = sp_pll_round_rate,
+	.recalc_rate = sp_pll_recalc_rate,
+	.set_rate = sp_pll_set_rate
+};
+
+static const struct clk_ops sp_pll_sub_ops = {
+	.enable = sp_pll_enable,
+	.disable = sp_pll_disable,
+	.is_enabled = sp_pll_is_enabled,
+	.recalc_rate = sp_pll_recalc_rate,
+};
+
+struct clk *clk_register_sp_pll(const char *name, const char *parent,
+		void __iomem *reg, int pd_bit, int bp_bit,
+		unsigned long brate, int shift, int width,
+		spinlock_t *lock)
+{
+	struct sp_pll *pll;
+	struct clk *clk;
+	struct clk_init_data initd = {
+		.name = name,
+		.parent_names = &parent,
+		.ops = (bp_bit >= 0) ? &sp_pll_ops : &sp_pll_sub_ops,
+		.num_parents = 1,
+		.flags = CLK_IGNORE_UNUSED
+	};
+
+	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+	if (!pll)
+		return ERR_PTR(-ENOMEM);
+
+	if (reg == PLLSYS_CTL)
+		initd.flags |= CLK_IS_CRITICAL;
+
+	pll->hw.init = &initd;
+	pll->reg = reg;
+	pll->pd_bit = pd_bit;
+	pll->bp_bit = bp_bit;
+	pll->brate = brate;
+	pll->div_shift = shift;
+	pll->div_width = width;
+	pll->lock = lock;
+
+	clk = clk_register(NULL, &pll->hw);
+	if (WARN_ON(IS_ERR(clk))) {
+		kfree(pll);
+	} else {
+		pr_info("%-20s%lu\n", name, clk_get_rate(clk));
+		clk_register_clkdev(clk, NULL, name);
+	}
+
+	return clk;
+}
+
+static void __init sp_clk_setup(struct device_node *np)
+{
+	int i, j;
+
+	moon_regs = of_iomap(np, 0);
+	if (WARN_ON(!moon_regs))
+		return; /* -ENXIO */
+
+	/* PLL_A */
+	clks[PLL_A] = clk_register_sp_pll("plla", EXTCLK,
+			PLLA_CTL, 11, 12, 27000000, 0, DIV_A, &plla_lock);
+
+	/* PLL_E */
+	clks[PLL_E] = clk_register_sp_pll("plle", EXTCLK,
+			PLLE_CTL, 6, 2, 50000000, 0, 0, &plle_lock);
+	clks[PLL_E_2P5] = clk_register_sp_pll("plle_2p5", "plle",
+			PLLE_CTL, 13, -1, 2500000, 0, 0, &plle_lock);
+	clks[PLL_E_25] = clk_register_sp_pll("plle_25", "plle",
+			PLLE_CTL, 12, -1, 25000000, 0, 0, &plle_lock);
+	clks[PLL_E_112P5] = clk_register_sp_pll("plle_112p5", "plle",
+			PLLE_CTL, 11, -1, 112500000, 0, 0, &plle_lock);
+
+	/* PLL_F */
+	clks[PLL_F] = clk_register_sp_pll("pllf", EXTCLK,
+			PLLF_CTL, 0, 10, 13500000, 1, 4, &pllf_lock);
+
+	/* PLL_TV */
+	clks[PLL_TV] = clk_register_sp_pll("plltv", EXTCLK,
+			PLLTV_CTL, 0, 15, 27000000, 0, DIV_TV, &plltv_lock);
+	clks[PLL_TV_A] = clk_register_divider(NULL, "plltv_a", "plltv", 0,
+			PLLTV_CTL + 4, 5, 1,
+			CLK_DIVIDER_POWER_OF_TWO, &plltv_lock);
+	clk_register_clkdev(clks[PLL_TV_A], NULL, "plltv_a");
+
+	/* PLL_SYS */
+	clks[PLL_SYS] = clk_register_sp_pll("pllsys", EXTCLK,
+			PLLSYS_CTL, 10, 9, 13500000, 0, 4, &pllsys_lock);
+
+	/* gates */
+	for (i = 0; i < ARRAY_SIZE(gates); i++) {
+		char s[10];
+
+		j = gates[i] & 0xffff;
+		sprintf(s, "clken%02x", j);
+		clks[j] = clk_register_gate(NULL, s, parents[gates[i] >> 16], CLK_IGNORE_UNUSED,
+			clk_regs + (j >> 4) * 4, j & 0x0f,
+			CLK_GATE_HIWORD_MASK, NULL);
+	}
+
+	clk_data.clks = clks;
+	clk_data.clk_num = ARRAY_SIZE(clks);
+	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+}
+
+CLK_OF_DECLARE(sp_clkc, "sunplus,sp7021-clkc", sp_clk_setup);
+
+MODULE_AUTHOR("Qin Jian <qinjian@cqplus1.com>");
+MODULE_DESCRIPTION("Sunplus SP7021 Clock Driver");
+MODULE_LICENSE("GPL v2");
-- 
2.33.1

[PATCH v4 09/10] ARM: sunplus: Add initial support for Sunplus SP7021 SoC

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-11-04 03:04:35

This patch aims to add an initial support for Sunplus SP7021 SoC.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 MAINTAINERS                         |  1 +
 arch/arm/Kconfig                    | 20 ++++++++++++++++++++
 arch/arm/Makefile                   |  2 ++
 arch/arm/mach-sunplus/Kconfig       | 20 ++++++++++++++++++++
 arch/arm/mach-sunplus/Makefile      |  9 +++++++++
 arch/arm/mach-sunplus/Makefile.boot |  3 +++
 arch/arm/mach-sunplus/sp7021.c      | 16 ++++++++++++++++
 7 files changed, 71 insertions(+)
 create mode 100644 arch/arm/mach-sunplus/Kconfig
 create mode 100644 arch/arm/mach-sunplus/Makefile
 create mode 100644 arch/arm/mach-sunplus/Makefile.boot
 create mode 100644 arch/arm/mach-sunplus/sp7021.c
diff --git a/MAINTAINERS b/MAINTAINERS
index febbd97bf..0ae537a41 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2664,6 +2664,7 @@ F:	Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
 F:	Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
 F:	Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml
 F:	Documentation/devicetree/bindings/reset/sunplus,reset.yaml
+F:	arch/arm/mach-sunplus/
 F:	drivers/clk/clk-sp7021.c
 F:	drivers/irqchip/irq-sp7021-intc.c
 F:	drivers/reset/reset-sunplus.c
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 59baf6c13..feba287eb 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -487,6 +487,24 @@ config ARCH_S3C24XX
 	  (<http://www.simtec.co.uk/products/EB110ITX/>), the IPAQ 1940 or the
 	  Samsung SMDK2410 development board (and derivatives).
 
+config ARCH_SUNPLUS
+	bool "Sunplus SoCs"
+	select CLKSRC_OF
+	select COMMON_CLK
+	select GENERIC_CLOCKEVENTS
+	select GENERIC_IRQ_CHIP
+	select GENERIC_IRQ_MULTI_HANDLER
+	select SERIAL_SUNPLUS
+	select SERIAL_SUNPLUS_CONSOLE
+	select USE_OF
+	select RTC_CLASS
+	select RESET_SUNPLUS
+	help
+	  Support for Sunplus SoC family: SP7021 and succeed SoCs based systems,
+	  such as the Banana Pi BPI-F2S development board ( and derivatives).
+	  (<http://www.sinovoip.com.cn/ecp_view.asp?id=586>)
+	  (<https://tibbo.com/store/plus1.html>)
+
 config ARCH_OMAP1
 	bool "TI OMAP1"
 	depends on MMU
@@ -689,6 +707,8 @@ source "arch/arm/mach-sti/Kconfig"
 
 source "arch/arm/mach-stm32/Kconfig"
 
+source "arch/arm/mach-sunplus/Kconfig"
+
 source "arch/arm/mach-sunxi/Kconfig"
 
 source "arch/arm/mach-tegra/Kconfig"
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 847c31e7c..cac95a950 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -152,6 +152,7 @@ textofs-$(CONFIG_ARCH_MSM8X60) := 0x00208000
 textofs-$(CONFIG_ARCH_MSM8960) := 0x00208000
 textofs-$(CONFIG_ARCH_MESON) := 0x00208000
 textofs-$(CONFIG_ARCH_AXXIA) := 0x00308000
+textofs-$(CONFIG_ARCH_SUNPLUS) := 0x00308000
 
 # Machine directory name.  This list is sorted alphanumerically
 # by CONFIG_* macro name.
@@ -212,6 +213,7 @@ machine-$(CONFIG_ARCH_RENESAS)	 	+= shmobile
 machine-$(CONFIG_ARCH_INTEL_SOCFPGA)	+= socfpga
 machine-$(CONFIG_ARCH_STI)		+= sti
 machine-$(CONFIG_ARCH_STM32)		+= stm32
+machine-$(CONFIG_ARCH_SUNPLUS)		+= sunplus
 machine-$(CONFIG_ARCH_SUNXI)		+= sunxi
 machine-$(CONFIG_ARCH_TEGRA)		+= tegra
 machine-$(CONFIG_ARCH_U8500)		+= ux500
diff --git a/arch/arm/mach-sunplus/Kconfig b/arch/arm/mach-sunplus/Kconfig
new file mode 100644
index 000000000..f7ad76959
--- /dev/null
+++ b/arch/arm/mach-sunplus/Kconfig
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+
+config SOC_SP7021
+	bool "Sunplus SP7021 SoC support"
+	default y
+	select CPU_V7
+	select ARM_GIC
+	select SUNPLUS_SP7021_INTC
+	select HAVE_SMP
+	select ARM_PSCI
+	select COMMON_CLK_SP7021
+	select PINCTRL
+	select PINCTRL_SPPCTL
+	select OF_OVERLAY
+	select GPIOLIB
+	help
+	  Support for Sunplus SP7021 SoC. It is based on ARM 4-core
+	  Cotex-A7 with various peripherals (ex: I2C, SPI, SDIO,
+	  Ethernet and etc.), FPGA interface,  chip-to-chip bus.
+	  It is designed for industrial control.
diff --git a/arch/arm/mach-sunplus/Makefile b/arch/arm/mach-sunplus/Makefile
new file mode 100644
index 000000000..c902580a7
--- /dev/null
+++ b/arch/arm/mach-sunplus/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the linux kernel.
+#
+
+# Object file lists.
+
+obj-$(CONFIG_SOC_SP7021)	+= sp7021.o
+
diff --git a/arch/arm/mach-sunplus/Makefile.boot b/arch/arm/mach-sunplus/Makefile.boot
new file mode 100644
index 000000000..401c30840
--- /dev/null
+++ b/arch/arm/mach-sunplus/Makefile.boot
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+zreladdr-$(CONFIG_ARCH_SUNPLUS) := 0x00308000
diff --git a/arch/arm/mach-sunplus/sp7021.c b/arch/arm/mach-sunplus/sp7021.c
new file mode 100644
index 000000000..774d0a5bd
--- /dev/null
+++ b/arch/arm/mach-sunplus/sp7021.c
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/*
+ * Copyright (C) Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+#include <linux/kernel.h>
+#include <asm/mach/arch.h>
+
+static const char *sp7021_compat[] __initconst = {
+	"sunplus,sp7021",
+	NULL
+};
+
+DT_MACHINE_START(SP7021_DT, "SP7021")
+	.dt_compat	= sp7021_compat,
+MACHINE_END
-- 
2.33.1

[PATCH v4 03/10] dt-bindings: reset: Add bindings for SP7021 reset driver

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-11-04 03:04:40

Add documentation to describe Sunplus SP7021 reset driver bindings.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 .../bindings/reset/sunplus,reset.yaml         | 38 ++++++++
 MAINTAINERS                                   |  2 +
 include/dt-bindings/reset/sp-sp7021.h         | 97 +++++++++++++++++++
 3 files changed, 137 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/reset/sunplus,reset.yaml
 create mode 100644 include/dt-bindings/reset/sp-sp7021.h
diff --git a/Documentation/devicetree/bindings/reset/sunplus,reset.yaml b/Documentation/devicetree/bindings/reset/sunplus,reset.yaml
new file mode 100644
index 000000000..c083c821f
--- /dev/null
+++ b/Documentation/devicetree/bindings/reset/sunplus,reset.yaml
@@ -0,0 +1,38 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) Sunplus Co., Ltd. 2021
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/reset/sunplus,reset.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Sunplus SoC Reset Controller
+
+maintainers:
+  - Qin Jian <qinjian@cqplus1.com>
+
+properties:
+  compatible:
+    const: sunplus,sp7021-reset
+
+  "#reset-cells":
+    const: 1
+
+  reg:
+    maxItems: 1
+
+required:
+  - compatible
+  - "#reset-cells"
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    rstc: reset@9c000054 {
+      compatible = "sunplus,sp7021-reset";
+      #reset-cells = <1>;
+      reg = <0x9c000054 0x28>;
+    };
+
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 6a5422f10..652f42cab 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2661,6 +2661,8 @@ L:	linux-arm-kernel@lists.infradead.org (moderated for mon-subscribers)
 S:	Maintained
 W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
 F:	Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
+F:	Documentation/devicetree/bindings/reset/sunplus,reset.yaml
+F:	include/dt-bindings/reset/sp-sp7021.h
 
 ARM/Synaptics SoC support
 M:	Jisheng Zhang <Jisheng.Zhang@synaptics.com>
diff --git a/include/dt-bindings/reset/sp-sp7021.h b/include/dt-bindings/reset/sp-sp7021.h
new file mode 100644
index 000000000..c0224a960
--- /dev/null
+++ b/include/dt-bindings/reset/sp-sp7021.h
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright (C) Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+#ifndef _DT_BINDINGS_RST_SUNPLUS_SP7021_H
+#define _DT_BINDINGS_RST_SUNPLUS_SP7021_H
+
+/* mo_reset0 ~ mo_reset9 */
+#define RST_SYSTEM		0x00
+#define RST_RTC			0x02
+#define RST_IOCTL		0x03
+#define RST_IOP			0x04
+#define RST_OTPRX		0x05
+#define RST_NOC			0x06
+#define RST_BR			0x07
+#define RST_RBUS_L00	0x08
+#define RST_SPIFL		0x09
+#define RST_SDCTRL0		0x0a
+#define RST_PERI0		0x0b
+#define RST_A926		0x0d
+#define RST_UMCTL2		0x0e
+#define RST_PERI1		0x0f
+
+#define RST_DDR_PHY0	0x10
+#define RST_ACHIP		0x12
+#define RST_STC0		0x14
+#define RST_STC_AV0		0x15
+#define RST_STC_AV1		0x16
+#define RST_STC_AV2		0x17
+#define RST_UA0			0x18
+#define RST_UA1			0x19
+#define RST_UA2			0x1a
+#define RST_UA3			0x1b
+#define RST_UA4			0x1c
+#define RST_HWUA		0x1d
+#define RST_DDC0		0x1e
+#define RST_UADMA		0x1f
+
+#define RST_CBDMA0		0x20
+#define RST_CBDMA1		0x21
+#define RST_SPI_COMBO_0	0x22
+#define RST_SPI_COMBO_1	0x23
+#define RST_SPI_COMBO_2	0x24
+#define RST_SPI_COMBO_3	0x25
+#define RST_AUD			0x26
+#define RST_USBC0		0x2a
+#define RST_USBC1		0x2b
+#define RST_UPHY0		0x2d
+#define RST_UPHY1		0x2e
+
+#define RST_I2CM0		0x30
+#define RST_I2CM1		0x31
+#define RST_I2CM2		0x32
+#define RST_I2CM3		0x33
+#define RST_PMC			0x3d
+#define RST_CARD_CTL0	0x3e
+#define RST_CARD_CTL1	0x3f
+
+#define RST_CARD_CTL4	0x42
+#define RST_BCH			0x44
+#define RST_DDFCH		0x4b
+#define RST_CSIIW0		0x4c
+#define RST_CSIIW1		0x4d
+#define RST_MIPICSI0	0x4e
+#define RST_MIPICSI1	0x4f
+
+#define RST_HDMI_TX		0x50
+#define RST_VPOST		0x55
+
+#define RST_TGEN		0x60
+#define RST_DMIX		0x61
+#define RST_TCON		0x6a
+#define RST_INTERRUPT	0x6f
+
+#define RST_RGST		0x70
+#define RST_GPIO		0x73
+#define RST_RBUS_TOP	0x74
+
+#define RST_MAILBOX		0x86
+#define RST_SPIND		0x8a
+#define RST_I2C2CBUS	0x8b
+#define RST_SEC			0x8d
+#define RST_DVE			0x8e
+#define RST_GPOST0		0x8f
+
+#define RST_OSD0		0x90
+#define RST_DISP_PWM	0x92
+#define RST_UADBG		0x93
+#define RST_DUMMY_MASTER	0x94
+#define RST_FIO_CTL		0x95
+#define RST_FPGA		0x96
+#define RST_L2SW		0x97
+#define RST_ICM			0x98
+#define RST_AXI_GLOBAL	0x99
+
+#endif
-- 
2.33.1

[PATCH v4 05/10] dt-bindings: clock: Add bindings for SP7021 clock driver

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-11-04 03:04:42

Add documentation to describe Sunplus SP7021 clock driver bindings.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 .../bindings/clock/sunplus,sp7021-clkc.yaml   |  38 ++++++
 MAINTAINERS                                   |   2 +
 include/dt-bindings/clock/sp-sp7021.h         | 112 ++++++++++++++++++
 3 files changed, 152 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
 create mode 100644 include/dt-bindings/clock/sp-sp7021.h
diff --git a/Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml b/Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
new file mode 100644
index 000000000..1ce7e41d8
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
@@ -0,0 +1,38 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) Sunplus Co., Ltd. 2021
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/sunplus,sp7021-clkc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sunplus SP7021 SoC Clock Controller Binding
+
+maintainers:
+  - Qin Jian <qinjian@cqplus1.com>
+
+properties:
+  compatible:
+    const: sunplus,sp7021-clkc
+
+  "#clock-cells":
+    const: 1
+
+  reg:
+    maxItems: 1
+
+required:
+  - compatible
+  - "#clock-cells"
+  - reg
+
+additionalProperties: false
+
+examples:
+  - |
+    clkc: clkc@9c000000 {
+      compatible = "sunplus,sp7021-clkc";
+      #clock-cells = <1>;
+      reg = <0x9c000000 0x280>;
+    };
+
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 6caffd6d0..90ebb823f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2661,8 +2661,10 @@ L:	linux-arm-kernel@lists.infradead.org (moderated for mon-subscribers)
 S:	Maintained
 W:	https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
 F:	Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
+F:	Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
 F:	Documentation/devicetree/bindings/reset/sunplus,reset.yaml
 F:	drivers/reset/reset-sunplus.c
+F:	include/dt-bindings/clock/sp-sp7021.h
 F:	include/dt-bindings/reset/sp-sp7021.h
 
 ARM/Synaptics SoC support
diff --git a/include/dt-bindings/clock/sp-sp7021.h b/include/dt-bindings/clock/sp-sp7021.h
new file mode 100644
index 000000000..425e2ec0f
--- /dev/null
+++ b/include/dt-bindings/clock/sp-sp7021.h
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Copyright (C) Sunplus Technology Co., Ltd.
+ *       All rights reserved.
+ */
+#ifndef _DT_BINDINGS_CLOCK_SUNPLUS_SP7021_H
+#define _DT_BINDINGS_CLOCK_SUNPLUS_Sp7021_H
+
+#define XTAL			27000000
+
+/* plls */
+#define PLL_A			0
+#define PLL_E			1
+#define PLL_E_2P5		2
+#define PLL_E_25		3
+#define PLL_E_112P5		4
+#define PLL_F			5
+#define PLL_TV			6
+#define PLL_TV_A		7
+#define PLL_SYS			8
+
+/* gates: mo_clken0 ~ mo_clken9 */
+#define CLK_SYSTEM		0x10
+#define CLK_RTC			0x12
+#define CLK_IOCTL		0x13
+#define CLK_IOP			0x14
+#define CLK_OTPRX		0x15
+#define CLK_NOC			0x16
+#define CLK_BR			0x17
+#define CLK_RBUS_L00	0x18
+#define CLK_SPIFL		0x19
+#define CLK_SDCTRL0		0x1a
+#define CLK_PERI0		0x1b
+#define CLK_A926		0x1d
+#define CLK_UMCTL2		0x1e
+#define CLK_PERI1		0x1f
+
+#define CLK_DDR_PHY0	0x20
+#define CLK_ACHIP		0x22
+#define CLK_STC0		0x24
+#define CLK_STC_AV0		0x25
+#define CLK_STC_AV1		0x26
+#define CLK_STC_AV2		0x27
+#define CLK_UA0			0x28
+#define CLK_UA1			0x29
+#define CLK_UA2			0x2a
+#define CLK_UA3			0x2b
+#define CLK_UA4			0x2c
+#define CLK_HWUA		0x2d
+#define CLK_DDC0		0x2e
+#define CLK_UADMA		0x2f
+
+#define CLK_CBDMA0		0x30
+#define CLK_CBDMA1		0x31
+#define CLK_SPI_COMBO_0	0x32
+#define CLK_SPI_COMBO_1	0x33
+#define CLK_SPI_COMBO_2	0x34
+#define CLK_SPI_COMBO_3	0x35
+#define CLK_AUD			0x36
+#define CLK_USBC0		0x3a
+#define CLK_USBC1		0x3b
+#define CLK_UPHY0		0x3d
+#define CLK_UPHY1		0x3e
+
+#define CLK_I2CM0		0x40
+#define CLK_I2CM1		0x41
+#define CLK_I2CM2		0x42
+#define CLK_I2CM3		0x43
+#define CLK_PMC			0x4d
+#define CLK_CARD_CTL0	0x4e
+#define CLK_CARD_CTL1	0x4f
+
+#define CLK_CARD_CTL4	0x52
+#define CLK_BCH			0x54
+#define CLK_DDFCH		0x5b
+#define CLK_CSIIW0		0x5c
+#define CLK_CSIIW1		0x5d
+#define CLK_MIPICSI0	0x5e
+#define CLK_MIPICSI1	0x5f
+
+#define CLK_HDMI_TX		0x60
+#define CLK_VPOST		0x65
+
+#define CLK_TGEN		0x70
+#define CLK_DMIX		0x71
+#define CLK_TCON		0x7a
+#define CLK_INTERRUPT	0x7f
+
+#define CLK_RGST		0x80
+#define CLK_GPIO		0x83
+#define CLK_RBUS_TOP	0x84
+
+#define CLK_MAILBOX		0x96
+#define CLK_SPIND		0x9a
+#define CLK_I2C2CBUS	0x9b
+#define CLK_SEC			0x9d
+#define CLK_DVE			0x9e
+#define CLK_GPOST0		0x9f
+
+#define CLK_OSD0		0xa0
+#define CLK_DISP_PWM	0xa2
+#define CLK_UADBG		0xa3
+#define CLK_DUMMY_MASTER	0xa4
+#define CLK_FIO_CTL		0xa5
+#define CLK_FPGA		0xa6
+#define CLK_L2SW		0xa7
+#define CLK_ICM			0xa8
+#define CLK_AXI_GLOBAL	0xa9
+
+#define CLK_MAX			0xb0
+
+#endif
-- 
2.33.1

[PATCH v4 10/10] ARM: sp7021_defconfig: Add Sunplus SP7021 defconfig

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-11-04 03:04:46

Add generic Sunplus SP7021 based board defconfig

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 MAINTAINERS                       |   1 +
 arch/arm/configs/sp7021_defconfig | 176 ++++++++++++++++++++++++++++++
 2 files changed, 177 insertions(+)
 create mode 100644 arch/arm/configs/sp7021_defconfig
diff --git a/MAINTAINERS b/MAINTAINERS
index 0ae537a41..9340f8760 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2664,6 +2664,7 @@ F:	Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
 F:	Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
 F:	Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml
 F:	Documentation/devicetree/bindings/reset/sunplus,reset.yaml
+F:	arch/arm/configs/sp7021_*defconfig
 F:	arch/arm/mach-sunplus/
 F:	drivers/clk/clk-sp7021.c
 F:	drivers/irqchip/irq-sp7021-intc.c
diff --git a/arch/arm/configs/sp7021_defconfig b/arch/arm/configs/sp7021_defconfig
new file mode 100644
index 000000000..f337a3304
--- /dev/null
+++ b/arch/arm/configs/sp7021_defconfig
@@ -0,0 +1,176 @@
+CONFIG_DEFAULT_HOSTNAME="SP7021-Ev"
+CONFIG_SYSVIPC=y
+CONFIG_USELIB=y
+CONFIG_NO_HZ_IDLE=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_PREEMPT=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE="../rootfs/initramfs/disk/ ../rootfs/initramfs/initramfs.devnodes"
+CONFIG_INITRAMFS_ROOT_UID=-1
+CONFIG_INITRAMFS_ROOT_GID=-1
+# CONFIG_RD_GZIP is not set
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_LZMA is not set
+# CONFIG_RD_XZ is not set
+# CONFIG_RD_LZO is not set
+# CONFIG_RD_LZ4 is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+# CONFIG_FHANDLE is not set
+CONFIG_KALLSYMS_ALL=y
+CONFIG_EMBEDDED=y
+CONFIG_PERF_EVENTS=y
+CONFIG_SLAB=y
+CONFIG_ARCH_SUNPLUS=y
+# CONFIG_VDSO is not set
+CONFIG_SMP=y
+CONFIG_HAVE_ARM_ARCH_TIMER=y
+CONFIG_THUMB2_KERNEL=y
+CONFIG_HIGHMEM=y
+# CONFIG_HIGHPTE is not set
+CONFIG_FORCE_MAX_ZONEORDER=12
+CONFIG_ZBOOT_ROM_TEXT=0x98307000
+CONFIG_ZBOOT_ROM_BSS=0x03400000
+CONFIG_CMDLINE="root=/dev/ram rw init=/init console=ttyS0,115200 earlyprintk mem=512M@0x0"
+CONFIG_AUTO_ZRELADDR=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_PACKET_DIAG=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_NET_IPIP=m
+CONFIG_BRIDGE=m
+CONFIG_VLAN_8021Q=m
+CONFIG_NETLINK_DIAG=y
+CONFIG_CAN=y
+CONFIG_CFG80211=m
+CONFIG_CFG80211_WEXT=y
+CONFIG_MAC80211=m
+CONFIG_CAIF=y
+CONFIG_CEPH_LIB=y
+CONFIG_CEPH_LIB_USE_DNS_RESOLVER=y
+CONFIG_UEVENT_HELPER=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_NETDEVICES=y
+# CONFIG_NET_VENDOR_ALACRITECH is not set
+# CONFIG_NET_VENDOR_AMAZON is not set
+# CONFIG_NET_VENDOR_AQUANTIA is not set
+# CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_CADENCE is not set
+# CONFIG_NET_VENDOR_CAVIUM is not set
+# CONFIG_NET_VENDOR_CIRRUS is not set
+# CONFIG_NET_VENDOR_CORTINA is not set
+# CONFIG_NET_VENDOR_EZCHIP is not set
+# CONFIG_NET_VENDOR_FARADAY is not set
+# CONFIG_NET_VENDOR_GOOGLE is not set
+# CONFIG_NET_VENDOR_HISILICON is not set
+# CONFIG_NET_VENDOR_HUAWEI is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_MICROCHIP is not set
+# CONFIG_NET_VENDOR_MICROSEMI is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_NETRONOME is not set
+# CONFIG_NET_VENDOR_NI is not set
+# CONFIG_NET_VENDOR_PENSANDO is not set
+# CONFIG_NET_VENDOR_QUALCOMM is not set
+# CONFIG_NET_VENDOR_RENESAS is not set
+# CONFIG_NET_VENDOR_ROCKER is not set
+# CONFIG_NET_VENDOR_SAMSUNG is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SOLARFLARE is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_SOCIONEXT is not set
+# CONFIG_NET_VENDOR_STMICRO is not set
+# CONFIG_NET_VENDOR_SYNOPSYS is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+# CONFIG_NET_VENDOR_XILINX is not set
+CONFIG_NET_VENDOR_SUNPLUS=y
+CONFIG_INPUT_POLLDEV=y
+CONFIG_INPUT_SPARSEKMAP=y
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_HWMON is not set
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_SUPPORT_FILTER=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_PLATFORM_SUPPORT=y
+CONFIG_MEDIA_USB_SUPPORT=y
+CONFIG_USB_VIDEO_CLASS=y
+CONFIG_FB=y
+CONFIG_FB_SP7021=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER=y
+CONFIG_HIDRAW=y
+CONFIG_GEMINI_USB=y
+CONFIG_USB_USE_PLATFORM_RESOURCE=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_HCD_PLATFORM=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_OHCI_HCD_PLATFORM=y
+CONFIG_SUNPLUS_USB_PHY=y
+CONFIG_USB_SUNPLUS_OTG=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_DEVICE_LOSE_PACKET_AFTER_SET_INTERFACE_WORKAROUND=y
+CONFIG_USB_DEVICE_EP11_NOT_AUTO_SWITCH_WORKAROUND=y
+CONFIG_USB_GADGET_SUNPLUS=y
+CONFIG_USB_ZERO=y
+CONFIG_MMC=y
+CONFIG_SP_EMMC=m
+CONFIG_STAGING=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_PWM=y
+CONFIG_PWM_SP7021=y
+CONFIG_RESET_CONTROLLER=y
+CONFIG_EXT2_FS=y
+# CONFIG_DNOTIFY is not set
+CONFIG_FANOTIFY=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_IOCHARSET="utf8"
+CONFIG_EXFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_NFS_FS=y
+CONFIG_NFS_V4=y
+CONFIG_ROOT_NFS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_UTF8=y
+CONFIG_CRC16=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_FS=y
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_SCHEDSTATS=y
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_FTRACE is not set
+CONFIG_DEBUG_USER=y
-- 
2.33.1

[PATCH v4 00/10] Add Sunplus SP7021 SoC Support

From: Qin Jian <qinjian@cqplus1.com>
Date: 2021-11-04 03:04:49

This patch series add Sunplus SP7021 SoC support.

Sunplus SP7021 is an ARM Cortex A7 (4 cores) based SoC. It integrates many
peripherals (ex: UART, I2C, SPI, SDIO, eMMC, USB, SD card and etc.) into a
single chip. It is designed for industrial control.

SP7021 consists of two chips (dies) in a package. One is called C-chip
(computing chip). It is a 4-core ARM Cortex A7 CPU. It adopts high-level
process (22 nm) for high performance computing. The other is called P-
chip (peripheral chip). It has many peripherals and an ARM A926 added
especially for real-time control. P-chip is made for customers. It adopts
low-level process (ex: 0.11 um) to reduce cost.

Refer to (for documentations):
https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview

Refer to (applications):
https://tibbo.com/store/plus1.html

Refer to (applications):
http://www.sinovoip.com.cn/ecp_view.asp?id=586

Changes in v4:
- mach-sunplus: add initial support for SP7021
- sp7021_defconfig: add generic SP7021 defconfig
- reset-sunplus: remove Q645 support
- reset-sunplus.c: refine code based on Philipp's review
- clk-sp7021: clock defines add prefix, more clean up

Changes in v3:
- sp7021-intc: remove primary controller mode due to P-chip running Linux
  not supported any more.
- sp7021-intc.h: removed, not set ext through the DT but sp_intc_set_ext()
- sunplus,sp7021-intc.yaml: update descriptions for above changes
- irq-sp7021-intc.c: more cleanup based on Marc's review
- all driver's Kconfig removed default, it's selected by platform config

Changes in v2:
- sunplus,sp7021-intc.yaml: add descrption for "#interrupt-cells", interrupts
- sunplus,sp7021-intc.yaml: drop "ext0-mask"/"ext1-mask" from DT
- sunplus,sp7021-intc.yaml: fix example.dt too long error
- irq-sp7021-intc.c: major rewrite
- all files with dual license

Qin Jian (10):
  dt-bindings: vendor-prefixes: Add Sunplus
  dt-bindings: arm: sunplus: Add bindings for Sunplus SP7021 SoC boards
  dt-bindings: reset: Add bindings for SP7021 reset driver
  reset: Add Sunplus SP7021 reset driver
  dt-bindings: clock: Add bindings for SP7021 clock driver
  clk: Add Sunplus SP7021 clock driver
  dt-bindings: interrupt-controller: Add bindings for SP7021 interrupt
    controller
  irqchip: Add Sunplus SP7021 interrupt controller driver
  ARM: sunplus: Add initial support for Sunplus SP7021 SoC
  ARM: sp7021_defconfig: Add Sunplus SP7021 defconfig

 .../bindings/arm/sunplus,sp7021.yaml          |  27 +
 .../bindings/clock/sunplus,sp7021-clkc.yaml   |  38 +
 .../sunplus,sp7021-intc.yaml                  |  62 ++
 .../bindings/reset/sunplus,reset.yaml         |  38 +
 .../devicetree/bindings/vendor-prefixes.yaml  |   2 +
 MAINTAINERS                                   |  17 +
 arch/arm/Kconfig                              |  20 +
 arch/arm/Makefile                             |   2 +
 arch/arm/configs/sp7021_defconfig             | 176 +++++
 arch/arm/mach-sunplus/Kconfig                 |  20 +
 arch/arm/mach-sunplus/Makefile                |   9 +
 arch/arm/mach-sunplus/Makefile.boot           |   3 +
 arch/arm/mach-sunplus/sp7021.c                |  16 +
 drivers/clk/Kconfig                           |   9 +
 drivers/clk/Makefile                          |   1 +
 drivers/clk/clk-sp7021.c                      | 725 ++++++++++++++++++
 drivers/irqchip/Kconfig                       |   9 +
 drivers/irqchip/Makefile                      |   1 +
 drivers/irqchip/irq-sp7021-intc.c             | 279 +++++++
 drivers/reset/Kconfig                         |   9 +
 drivers/reset/Makefile                        |   1 +
 drivers/reset/reset-sunplus.c                 | 133 ++++
 include/dt-bindings/clock/sp-sp7021.h         | 112 +++
 include/dt-bindings/reset/sp-sp7021.h         |  97 +++
 24 files changed, 1806 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml
 create mode 100644 Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml
 create mode 100644 Documentation/devicetree/bindings/reset/sunplus,reset.yaml
 create mode 100644 arch/arm/configs/sp7021_defconfig
 create mode 100644 arch/arm/mach-sunplus/Kconfig
 create mode 100644 arch/arm/mach-sunplus/Makefile
 create mode 100644 arch/arm/mach-sunplus/Makefile.boot
 create mode 100644 arch/arm/mach-sunplus/sp7021.c
 create mode 100644 drivers/clk/clk-sp7021.c
 create mode 100644 drivers/irqchip/irq-sp7021-intc.c
 create mode 100644 drivers/reset/reset-sunplus.c
 create mode 100644 include/dt-bindings/clock/sp-sp7021.h
 create mode 100644 include/dt-bindings/reset/sp-sp7021.h

--
2.33.1

Re: [PATCH v4 09/10] ARM: sunplus: Add initial support for Sunplus SP7021 SoC

From: Randy Dunlap <rdunlap@infradead.org>
Date: 2021-11-04 05:09:29

On 11/3/21 7:57 PM, Qin Jian wrote:
quoted hunk
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 59baf6c13..feba287eb 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -487,6 +487,24 @@ config ARCH_S3C24XX
  	  (<http://www.simtec.co.uk/products/EB110ITX/>), the IPAQ 1940 or the
  	  Samsung SMDK2410 development board (and derivatives).
  
+config ARCH_SUNPLUS
+	bool "Sunplus SoCs"
+	select CLKSRC_OF
+	select COMMON_CLK
+	select GENERIC_CLOCKEVENTS
+	select GENERIC_IRQ_CHIP
+	select GENERIC_IRQ_MULTI_HANDLER
+	select SERIAL_SUNPLUS
+	select SERIAL_SUNPLUS_CONSOLE
+	select USE_OF
+	select RTC_CLASS
+	select RESET_SUNPLUS
+	help
+	  Support for Sunplus SoC family: SP7021 and succeed SoCs based systems,
	                                             succeeding or successsor or following
	                                                     SoC-based systems,
+	  such as the Banana Pi BPI-F2S development board ( and derivatives).
	                                                  (and derivatives).
+	  (<http://www.sinovoip.com.cn/ecp_view.asp?id=586>)
+	  (<https://tibbo.com/store/plus1.html>)

-- 
~Randy

Re: [PATCH v4 09/10] ARM: sunplus: Add initial support for Sunplus SP7021 SoC

From: Randy Dunlap <rdunlap@infradead.org>
Date: 2021-11-04 05:09:31

On 11/3/21 7:57 PM, Qin Jian wrote:
quoted hunk
diff --git a/arch/arm/mach-sunplus/Kconfig b/arch/arm/mach-sunplus/Kconfig
new file mode 100644
index 000000000..f7ad76959
--- /dev/null
+++ b/arch/arm/mach-sunplus/Kconfig
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+
+config SOC_SP7021
+	bool "Sunplus SP7021 SoC support"
+	default y
+	select CPU_V7
+	select ARM_GIC
+	select SUNPLUS_SP7021_INTC
+	select HAVE_SMP
+	select ARM_PSCI
+	select COMMON_CLK_SP7021
+	select PINCTRL
+	select PINCTRL_SPPCTL
+	select OF_OVERLAY
+	select GPIOLIB
+	help
+	  Support for Sunplus SP7021 SoC. It is based on ARM 4-core
+	  Cotex-A7 with various peripherals (ex: I2C, SPI, SDIO,
Is that   Cortex-A7
?
+	  Ethernet and etc.), FPGA interface,  chip-to-chip bus.
+	  It is designed for industrial control.

-- 
~Randy

Re: [PATCH v4 08/10] irqchip: Add Sunplus SP7021 interrupt controller driver

From: Randy Dunlap <rdunlap@infradead.org>
Date: 2021-11-04 05:09:40

On 11/3/21 7:57 PM, Qin Jian wrote:
quoted hunk
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index aca7b595c..6f0bc0871 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -602,4 +602,13 @@ config APPLE_AIC
  	  Support for the Apple Interrupt Controller found on Apple Silicon SoCs,
  	  such as the M1.
  
+config SUNPLUS_SP7021_INTC
+	bool "Sunplus SP7021 interrupt controller"
+	help
+	  Support for the Sunplus SP7021 Interrupt Controller IP core.
+	  SP7021 SoC has 2 Chips: C-Chip & P-Chip. This is used as a
+	  chained controller, routed all interrupt source in P-Chip to
	                      routing all interrupt sources
+	  the primary controller on C-Chip.
+	  This is selected automatically by platform config.

-- 
~Randy

Re: [PATCH v4 06/10] clk: Add Sunplus SP7021 clock driver

From: Randy Dunlap <rdunlap@infradead.org>
Date: 2021-11-04 05:09:43

On 11/3/21 7:57 PM, Qin Jian wrote:
quoted hunk
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index c5b3dc973..1cd7ae7a3 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -334,6 +334,15 @@ config COMMON_CLK_VC5
  	  This driver supports the IDT VersaClock 5 and VersaClock 6
  	  programmable clock generators.
  
+config COMMON_CLK_SP7021
+	bool "Clock driver for Sunplus SP7021 SoC"
+	help
+	  This driver supports the Sunplus SP7021 SoC clocks.
+	  It implemented SP7021 PLLs/gate.
	     implements
+	  Not all features of the PLL are currently supported
+	  by the driver.
+	  This driver is selected automatically by platform config.

-- 
~Randy

Re: [PATCH v4 00/10] Add Sunplus SP7021 SoC Support

From: Marc Zyngier <maz@kernel.org>
Date: 2021-11-04 08:22:43

Qin,

On Thu, 04 Nov 2021 02:56:57 +0000,
Qin Jian [off-list ref] wrote:
This patch series add Sunplus SP7021 SoC support.

Sunplus SP7021 is an ARM Cortex A7 (4 cores) based SoC. It integrates many
peripherals (ex: UART, I2C, SPI, SDIO, eMMC, USB, SD card and etc.) into a
single chip. It is designed for industrial control.

SP7021 consists of two chips (dies) in a package. One is called C-chip
(computing chip). It is a 4-core ARM Cortex A7 CPU. It adopts high-level
process (22 nm) for high performance computing. The other is called P-
chip (peripheral chip). It has many peripherals and an ARM A926 added
especially for real-time control. P-chip is made for customers. It adopts
low-level process (ex: 0.11 um) to reduce cost.
That's the 3rd version of this series since Friday, two of them during
the merge window. All you are achieving is to actually *delay* the
review process (at this rate, I'll probably wait until v11 before I
take another look at it).

Documentation/process/submitting-patches.rst states it clearly:

<quote>
Wait for a minimum of one week before resubmitting
</quote>

So please leave people the time to actually do a good review job, and
take the opportunity to review your own patches before posting them
again.

Thanks,

	M.

-- 
Without deviation from the norm, progress is not possible.

答复: [PATCH v4 00/10] Add Sunplus SP7021 SoC Support

From: qinjian[覃健] <qinjian@cqplus1.com>
Date: 2021-11-04 08:36:21

Marc,

Got it. Thank you for reminding me.
I'm sorry for the previous mistake.

-----邮件原件-----
发件人: Marc Zyngier [off-list ref]
发送时间: 2021年11月4日 16:23
收件人: qinjian[覃健] [off-list ref]
抄送: robh+dt@kernel.org; mturquette@baylibre.com; sboyd@kernel.org; p.zabel@pengutronix.de; linux@armlinux.org.uk;
broonie@kernel.org; arnd@arndb.de; linux-arm-kernel@lists.infradead.org; devicetree@vger.kernel.org; linux-kernel@vger.kernel.org;
linux-clk@vger.kernel.org; Wells Lu �畏简v [off-list ref]
主题: Re: [PATCH v4 00/10] Add Sunplus SP7021 SoC Support

Qin,

On Thu, 04 Nov 2021 02:56:57 +0000,
Qin Jian [off-list ref] wrote:
quoted
This patch series add Sunplus SP7021 SoC support.

Sunplus SP7021 is an ARM Cortex A7 (4 cores) based SoC. It integrates many
peripherals (ex: UART, I2C, SPI, SDIO, eMMC, USB, SD card and etc.) into a
single chip. It is designed for industrial control.

SP7021 consists of two chips (dies) in a package. One is called C-chip
(computing chip). It is a 4-core ARM Cortex A7 CPU. It adopts high-level
process (22 nm) for high performance computing. The other is called P-
chip (peripheral chip). It has many peripherals and an ARM A926 added
especially for real-time control. P-chip is made for customers. It adopts
low-level process (ex: 0.11 um) to reduce cost.
That's the 3rd version of this series since Friday, two of them during
the merge window. All you are achieving is to actually *delay* the
review process (at this rate, I'll probably wait until v11 before I
take another look at it).

Documentation/process/submitting-patches.rst states it clearly:

<quote>
Wait for a minimum of one week before resubmitting
</quote>

So please leave people the time to actually do a good review job, and
take the opportunity to review your own patches before posting them
again.

Thanks,

	M.

--
Without deviation from the norm, progress is not possible.

Re: [PATCH v4 06/10] clk: Add Sunplus SP7021 clock driver

From: kernel test robot <hidden>
Date: 2021-11-04 11:32:53

Hi Qin,

I love your patch! Perhaps something to improve:

[auto build test WARNING on pza/reset/next]
[also build test WARNING on robh/for-next clk/clk-next tip/irq/core linus/master v5.15 next-20211104]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Qin-Jian/dt-bindings-vendor-prefixes-Add-Sunplus/20211104-115746
base:   https://git.pengutronix.de/git/pza/linux reset/next
config: xtensa-randconfig-r012-20211104 (attached as .config)
compiler: xtensa-linux-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/604141a9aad536dff720a6337561171d72d63d74
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Qin-Jian/dt-bindings-vendor-prefixes-Add-Sunplus/20211104-115746
        git checkout 604141a9aad536dff720a6337561171d72d63d74
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross ARCH=xtensa 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <redacted>

All warnings (new ones prefixed by >>):

   drivers/clk/clk-sp7021.c: In function 'plltv_fractional_div':
quoted
drivers/clk/clk-sp7021.c:292:13: warning: variable 'diff_min_sign' set but not used [-Wunused-but-set-variable]
     292 |         u32 diff_min_sign = 0;
         |             ^~~~~~~~~~~~~
   drivers/clk/clk-sp7021.c: At top level:
   drivers/clk/clk-sp7021.c:625:13: warning: no previous prototype for 'clk_register_sp_pll' [-Wmissing-prototypes]
     625 | struct clk *clk_register_sp_pll(const char *name, const char *parent,
         |             ^~~~~~~~~~~~~~~~~~~


vim +/diff_min_sign +292 drivers/clk/clk-sp7021.c

   286	
   287	static long plltv_fractional_div(struct sp_pll *clk, unsigned long freq)
   288	{
   289		u32 m, r;
   290		u32 nint, nfra;
   291		u32 diff_min_quotient = 210000000, diff_min_remainder = 0;
 > 292		u32 diff_min_sign = 0;
   293		unsigned long fvco, nf, f, fout = 0;
   294		int sdm, ph;
   295	
   296		/* check freq */
   297		if (freq < F_MIN) {
   298			pr_warn("%s: %s freq:%lu < F_MIN:%lu, round up\n",
   299				__func__, clk_hw_get_name(&clk->hw), freq, F_MIN);
   300			freq = F_MIN;
   301		} else if (freq > F_MAX) {
   302			pr_warn("%s: %s freq:%lu > F_MAX:%lu, round down\n",
   303				__func__, clk_hw_get_name(&clk->hw), freq, F_MAX);
   304			freq = F_MAX;
   305		}
   306	
   307		/* DIVR 0~3 */
   308		for (r = 0; r <= 3; r++) {
   309			fvco = freq << r;
   310			if (fvco <= FVCO_MAX)
   311				break;
   312		}
   313		f = F_27M >> r;
   314	
   315		/* PH_SEL 1/0 */
   316		for (ph = 1; ph >= 0; ph--) {
   317			const u32 *pp = pt[ph];
   318			u32 ms = 1;
   319	
   320			/* SDM_MOD 0/1 */
   321			for (sdm = 0; sdm <= 1; sdm++) {
   322				u32 mod = mods[sdm];
   323	
   324				/* DIVM 1~32 */
   325				for (m = ms; m <= 32; m++) {
   326					u32 diff_freq;
   327					u32 diff_freq_quotient = 0, diff_freq_remainder = 0;
   328					u32 diff_freq_sign = 0; /* 0:Positive number, 1:Negative number */
   329	
   330					nf = fvco * m;
   331					nint = nf / pp[3];
   332	
   333					if (nint < pp[1])
   334						continue;
   335					if (nint > pp[1])
   336						break;
   337	
   338					nfra = (((nf % pp[3]) * mod * pp[4]) + (F_27M / 2)) / F_27M;
   339					if (nfra)
   340						diff_freq = (f * (nint + pp[2]) / pp[0]) -
   341									(f * (mod - nfra) / mod / pp[4]);
   342					else
   343						diff_freq = (f * (nint) / pp[0]);
   344	
   345					diff_freq_quotient  = diff_freq / m;
   346					diff_freq_remainder = ((diff_freq % m) * 1000) / m;
   347	
   348					if (freq > diff_freq_quotient) {
   349						diff_freq_quotient  = freq - diff_freq_quotient - 1;
   350						diff_freq_remainder = 1000 - diff_freq_remainder;
   351						diff_freq_sign = 1;
   352					} else {
   353						diff_freq_quotient = diff_freq_quotient - freq;
   354						diff_freq_sign = 0;
   355					}
   356	
   357					if ((diff_min_quotient > diff_freq_quotient) ||
   358						((diff_min_quotient == diff_freq_quotient) &&
   359						(diff_min_remainder > diff_freq_remainder))) {
   360	
   361						/* found a closer freq, save parameters */
   362						clk->p[SEL_FRA] = 1;
   363						clk->p[SDM_MOD] = sdm;
   364						clk->p[PH_SEL]  = ph;
   365						clk->p[NFRA]    = nfra;
   366						clk->p[DIVR]    = r;
   367						clk->p[DIVM]    = m;
   368	
   369						fout = diff_freq / m;
   370						diff_min_quotient = diff_freq_quotient;
   371						diff_min_remainder = diff_freq_remainder;
   372						diff_min_sign = diff_freq_sign;
   373					}
   374				}
   375			}
   376		}
   377	
   378		if (!fout) {
   379			pr_err("%s: %s freq:%lu not found a valid setting\n",
   380				__func__, clk_hw_get_name(&clk->hw), freq);
   381			return -EINVAL;
   382		}
   383	
   384		return fout;
   385	}
   386	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

Re: [PATCH v4 09/10] ARM: sunplus: Add initial support for Sunplus SP7021 SoC

From: kernel test robot <hidden>
Date: 2021-11-04 15:25:48

Hi Qin,

I love your patch! Yet something to improve:

[auto build test ERROR on pza/reset/next]
[also build test ERROR on robh/for-next clk/clk-next v5.15]
[cannot apply to tip/irq/core linus/master next-20211104]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Qin-Jian/dt-bindings-vendor-prefixes-Add-Sunplus/20211104-115746
base:   https://git.pengutronix.de/git/pza/linux reset/next
config: arm-randconfig-c002-20211104 (attached as .config)
compiler: arm-linux-gnueabi-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/a0450f4dfa75b60af973bceea07b1be864ae81ba
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Qin-Jian/dt-bindings-vendor-prefixes-Add-Sunplus/20211104-115746
        git checkout a0450f4dfa75b60af973bceea07b1be864ae81ba
        # save the attached .config to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=arm SHELL=/bin/bash arch/arm/kernel/ arch/arm/mm/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <redacted>

All errors (new ones prefixed by >>):

   arch/arm/kernel/setup.c: In function 'early_print':
   arch/arm/kernel/setup.c:372:9: warning: function 'early_print' might be a candidate for 'gnu_printf' format attribute [-Wsuggest-attribute=format]
     372 |         vsnprintf(buf, sizeof(buf), str, ap);
         |         ^~~~~~~~~
   In file included from arch/arm/kernel/setup.c:36:
   arch/arm/kernel/setup.c: In function 'cpuid_init_hwcaps':
quoted
arch/arm/include/asm/cputype.h:22:25: error: expected ':' or ')' before numeric constant
      22 | #define CPUID_EXT_ISAR0 0x60
         |                         ^~~~
   arch/arm/include/asm/cputype.h:135:51: note: in definition of macro 'read_cpuid_ext'
     135 |                 asm("mrc        p15, 0, %0, c0, " ext_reg               \
         |                                                   ^~~~~~~
   arch/arm/kernel/setup.c:457:17: note: in expansion of macro 'cpuid_feature_extract'
     457 |         block = cpuid_feature_extract(CPUID_EXT_ISAR0, 24);
         |                 ^~~~~~~~~~~~~~~~~~~~~
   arch/arm/kernel/setup.c:457:39: note: in expansion of macro 'CPUID_EXT_ISAR0'
     457 |         block = cpuid_feature_extract(CPUID_EXT_ISAR0, 24);
         |                                       ^~~~~~~~~~~~~~~
   arch/arm/include/asm/cputype.h:18:25: error: expected ':' or ')' before numeric constant
      18 | #define CPUID_EXT_MMFR0 0x50
         |                         ^~~~
   arch/arm/include/asm/cputype.h:135:51: note: in definition of macro 'read_cpuid_ext'
     135 |                 asm("mrc        p15, 0, %0, c0, " ext_reg               \
         |                                                   ^~~~~~~
   arch/arm/kernel/setup.c:464:17: note: in expansion of macro 'cpuid_feature_extract'
     464 |         block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0);
         |                 ^~~~~~~~~~~~~~~~~~~~~
   arch/arm/kernel/setup.c:464:39: note: in expansion of macro 'CPUID_EXT_MMFR0'
     464 |         block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0);
         |                                       ^~~~~~~~~~~~~~~
   arch/arm/include/asm/cputype.h:27:25: error: expected ':' or ')' before numeric constant
      27 | #define CPUID_EXT_ISAR5 0x74
         |                         ^~~~
   arch/arm/include/asm/cputype.h:135:51: note: in definition of macro 'read_cpuid_ext'
     135 |                 asm("mrc        p15, 0, %0, c0, " ext_reg               \
         |                                                   ^~~~~~~
   arch/arm/kernel/setup.c:469:32: note: in expansion of macro 'CPUID_EXT_ISAR5'
     469 |         isar5 = read_cpuid_ext(CPUID_EXT_ISAR5);
         |                                ^~~~~~~~~~~~~~~
   arch/arm/kernel/setup.c: In function 'elf_hwcap_fixup':
   arch/arm/include/asm/cputype.h:25:25: error: expected ':' or ')' before numeric constant
      25 | #define CPUID_EXT_ISAR3 0x6c
         |                         ^~~~
   arch/arm/include/asm/cputype.h:135:51: note: in definition of macro 'read_cpuid_ext'
     135 |                 asm("mrc        p15, 0, %0, c0, " ext_reg               \
         |                                                   ^~~~~~~
   arch/arm/kernel/setup.c:513:13: note: in expansion of macro 'cpuid_feature_extract'
     513 |         if (cpuid_feature_extract(CPUID_EXT_ISAR3, 12) > 1 ||
         |             ^~~~~~~~~~~~~~~~~~~~~
   arch/arm/kernel/setup.c:513:35: note: in expansion of macro 'CPUID_EXT_ISAR3'
     513 |         if (cpuid_feature_extract(CPUID_EXT_ISAR3, 12) > 1 ||
         |                                   ^~~~~~~~~~~~~~~
   arch/arm/include/asm/cputype.h:25:25: error: expected ':' or ')' before numeric constant
      25 | #define CPUID_EXT_ISAR3 0x6c
         |                         ^~~~
   arch/arm/include/asm/cputype.h:135:51: note: in definition of macro 'read_cpuid_ext'
     135 |                 asm("mrc        p15, 0, %0, c0, " ext_reg               \
         |                                                   ^~~~~~~
   arch/arm/kernel/setup.c:514:14: note: in expansion of macro 'cpuid_feature_extract'
     514 |             (cpuid_feature_extract(CPUID_EXT_ISAR3, 12) == 1 &&
         |              ^~~~~~~~~~~~~~~~~~~~~
   arch/arm/kernel/setup.c:514:36: note: in expansion of macro 'CPUID_EXT_ISAR3'
     514 |             (cpuid_feature_extract(CPUID_EXT_ISAR3, 12) == 1 &&
         |                                    ^~~~~~~~~~~~~~~
   arch/arm/include/asm/cputype.h:26:25: error: expected ':' or ')' before numeric constant
      26 | #define CPUID_EXT_ISAR4 0x70
         |                         ^~~~
   arch/arm/include/asm/cputype.h:135:51: note: in definition of macro 'read_cpuid_ext'
     135 |                 asm("mrc        p15, 0, %0, c0, " ext_reg               \
         |                                                   ^~~~~~~
   arch/arm/kernel/setup.c:515:14: note: in expansion of macro 'cpuid_feature_extract'
     515 |              cpuid_feature_extract(CPUID_EXT_ISAR4, 20) >= 3))
         |              ^~~~~~~~~~~~~~~~~~~~~
   arch/arm/kernel/setup.c:515:36: note: in expansion of macro 'CPUID_EXT_ISAR4'
     515 |              cpuid_feature_extract(CPUID_EXT_ISAR4, 20) >= 3))
         |                                    ^~~~~~~~~~~~~~~
   In file included from arch/arm/include/asm/efi.h:12,
                    from arch/arm/kernel/setup.c:37:
   At top level:
   arch/arm/include/asm/fixmap.h:39:35: warning: '__end_of_fixed_addresses' defined but not used [-Wunused-const-variable=]
      39 | static const enum fixed_addresses __end_of_fixed_addresses =
         |                                   ^~~~~~~~~~~~~~~~~~~~~~~~
--
   arch/arm/mm/pmsa-v8.c: In function 'prlar_read':
quoted
arch/arm/mm/pmsa-v8.c:53:16: error: implicit declaration of function 'readl_relaxed' [-Werror=implicit-function-declaration]
      53 |         return readl_relaxed(BASEADDR_V7M_SCB + PMSAv8_RLAR);
         |                ^~~~~~~~~~~~~
quoted
arch/arm/mm/pmsa-v8.c:53:30: error: 'BASEADDR_V7M_SCB' undeclared (first use in this function)
      53 |         return readl_relaxed(BASEADDR_V7M_SCB + PMSAv8_RLAR);
         |                              ^~~~~~~~~~~~~~~~
   arch/arm/mm/pmsa-v8.c:53:30: note: each undeclared identifier is reported only once for each function it appears in
quoted
arch/arm/mm/pmsa-v8.c:53:49: error: 'PMSAv8_RLAR' undeclared (first use in this function); did you mean 'PMSAv8_MAIR'?
      53 |         return readl_relaxed(BASEADDR_V7M_SCB + PMSAv8_RLAR);
         |                                                 ^~~~~~~~~~~
         |                                                 PMSAv8_MAIR
   arch/arm/mm/pmsa-v8.c: In function 'prbar_read':
   arch/arm/mm/pmsa-v8.c:58:30: error: 'BASEADDR_V7M_SCB' undeclared (first use in this function)
      58 |         return readl_relaxed(BASEADDR_V7M_SCB + PMSAv8_RBAR);
         |                              ^~~~~~~~~~~~~~~~
quoted
arch/arm/mm/pmsa-v8.c:58:49: error: 'PMSAv8_RBAR' undeclared (first use in this function); did you mean 'PMSAv8_MAIR'?
      58 |         return readl_relaxed(BASEADDR_V7M_SCB + PMSAv8_RBAR);
         |                                                 ^~~~~~~~~~~
         |                                                 PMSAv8_MAIR
   arch/arm/mm/pmsa-v8.c: In function 'prsel_write':
quoted
arch/arm/mm/pmsa-v8.c:63:9: error: implicit declaration of function 'writel_relaxed' [-Werror=implicit-function-declaration]
      63 |         writel_relaxed(v, BASEADDR_V7M_SCB + PMSAv8_RNR);
         |         ^~~~~~~~~~~~~~
   arch/arm/mm/pmsa-v8.c:63:27: error: 'BASEADDR_V7M_SCB' undeclared (first use in this function)
      63 |         writel_relaxed(v, BASEADDR_V7M_SCB + PMSAv8_RNR);
         |                           ^~~~~~~~~~~~~~~~
quoted
arch/arm/mm/pmsa-v8.c:63:46: error: 'PMSAv8_RNR' undeclared (first use in this function); did you mean 'PMSAv8_MAIR'?
      63 |         writel_relaxed(v, BASEADDR_V7M_SCB + PMSAv8_RNR);
         |                                              ^~~~~~~~~~
         |                                              PMSAv8_MAIR
   arch/arm/mm/pmsa-v8.c: In function 'prbar_write':
   arch/arm/mm/pmsa-v8.c:68:27: error: 'BASEADDR_V7M_SCB' undeclared (first use in this function)
      68 |         writel_relaxed(v, BASEADDR_V7M_SCB + PMSAv8_RBAR);
         |                           ^~~~~~~~~~~~~~~~
   arch/arm/mm/pmsa-v8.c:68:46: error: 'PMSAv8_RBAR' undeclared (first use in this function); did you mean 'PMSAv8_MAIR'?
      68 |         writel_relaxed(v, BASEADDR_V7M_SCB + PMSAv8_RBAR);
         |                                              ^~~~~~~~~~~
         |                                              PMSAv8_MAIR
   arch/arm/mm/pmsa-v8.c: In function 'prlar_write':
   arch/arm/mm/pmsa-v8.c:73:27: error: 'BASEADDR_V7M_SCB' undeclared (first use in this function)
      73 |         writel_relaxed(v, BASEADDR_V7M_SCB + PMSAv8_RLAR);
         |                           ^~~~~~~~~~~~~~~~
   arch/arm/mm/pmsa-v8.c:73:46: error: 'PMSAv8_RLAR' undeclared (first use in this function); did you mean 'PMSAv8_MAIR'?
      73 |         writel_relaxed(v, BASEADDR_V7M_SCB + PMSAv8_RLAR);
         |                                              ^~~~~~~~~~~
         |                                              PMSAv8_MAIR
   arch/arm/mm/pmsa-v8.c: In function 'prbar_read':
   arch/arm/mm/pmsa-v8.c:59:1: error: control reaches end of non-void function [-Werror=return-type]
      59 | }
         | ^
   arch/arm/mm/pmsa-v8.c: In function 'prlar_read':
   arch/arm/mm/pmsa-v8.c:54:1: error: control reaches end of non-void function [-Werror=return-type]
      54 | }
         | ^
   cc1: some warnings being treated as errors


vim +22 arch/arm/include/asm/cputype.h

0ba8b9b273c45d Russell King     2008-08-10  12  
6fae9cdafc92ae Uwe Kleine-König 2013-05-06  13  #ifdef CONFIG_CPU_V7M
6fae9cdafc92ae Uwe Kleine-König 2013-05-06  14  #define CPUID_EXT_PFR0	0x40
6fae9cdafc92ae Uwe Kleine-König 2013-05-06  15  #define CPUID_EXT_PFR1	0x44
6fae9cdafc92ae Uwe Kleine-König 2013-05-06  16  #define CPUID_EXT_DFR0	0x48
6fae9cdafc92ae Uwe Kleine-König 2013-05-06  17  #define CPUID_EXT_AFR0	0x4c
6fae9cdafc92ae Uwe Kleine-König 2013-05-06  18  #define CPUID_EXT_MMFR0	0x50
6fae9cdafc92ae Uwe Kleine-König 2013-05-06  19  #define CPUID_EXT_MMFR1	0x54
6fae9cdafc92ae Uwe Kleine-König 2013-05-06  20  #define CPUID_EXT_MMFR2	0x58
6fae9cdafc92ae Uwe Kleine-König 2013-05-06  21  #define CPUID_EXT_MMFR3	0x5c
6fae9cdafc92ae Uwe Kleine-König 2013-05-06 @22  #define CPUID_EXT_ISAR0	0x60
6fae9cdafc92ae Uwe Kleine-König 2013-05-06  23  #define CPUID_EXT_ISAR1	0x64
6fae9cdafc92ae Uwe Kleine-König 2013-05-06  24  #define CPUID_EXT_ISAR2	0x68
6fae9cdafc92ae Uwe Kleine-König 2013-05-06  25  #define CPUID_EXT_ISAR3	0x6c
6fae9cdafc92ae Uwe Kleine-König 2013-05-06  26  #define CPUID_EXT_ISAR4	0x70
6fae9cdafc92ae Uwe Kleine-König 2013-05-06  27  #define CPUID_EXT_ISAR5	0x74
6fae9cdafc92ae Uwe Kleine-König 2013-05-06  28  #else
faa7bc51c11d5b Catalin Marinas  2009-05-30  29  #define CPUID_EXT_PFR0	"c1, 0"
faa7bc51c11d5b Catalin Marinas  2009-05-30  30  #define CPUID_EXT_PFR1	"c1, 1"
faa7bc51c11d5b Catalin Marinas  2009-05-30  31  #define CPUID_EXT_DFR0	"c1, 2"
faa7bc51c11d5b Catalin Marinas  2009-05-30  32  #define CPUID_EXT_AFR0	"c1, 3"
faa7bc51c11d5b Catalin Marinas  2009-05-30  33  #define CPUID_EXT_MMFR0	"c1, 4"
faa7bc51c11d5b Catalin Marinas  2009-05-30  34  #define CPUID_EXT_MMFR1	"c1, 5"
faa7bc51c11d5b Catalin Marinas  2009-05-30  35  #define CPUID_EXT_MMFR2	"c1, 6"
faa7bc51c11d5b Catalin Marinas  2009-05-30  36  #define CPUID_EXT_MMFR3	"c1, 7"
faa7bc51c11d5b Catalin Marinas  2009-05-30  37  #define CPUID_EXT_ISAR0	"c2, 0"
faa7bc51c11d5b Catalin Marinas  2009-05-30  38  #define CPUID_EXT_ISAR1	"c2, 1"
faa7bc51c11d5b Catalin Marinas  2009-05-30  39  #define CPUID_EXT_ISAR2	"c2, 2"
faa7bc51c11d5b Catalin Marinas  2009-05-30  40  #define CPUID_EXT_ISAR3	"c2, 3"
faa7bc51c11d5b Catalin Marinas  2009-05-30  41  #define CPUID_EXT_ISAR4	"c2, 4"
faa7bc51c11d5b Catalin Marinas  2009-05-30  42  #define CPUID_EXT_ISAR5	"c2, 5"
6fae9cdafc92ae Uwe Kleine-König 2013-05-06  43  #endif
faa7bc51c11d5b Catalin Marinas  2009-05-30  44  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

Re: [PATCH v4 06/10] clk: Add Sunplus SP7021 clock driver

From: kernel test robot <hidden>
Date: 2021-11-05 04:03:38

Hi Qin,

I love your patch! Yet something to improve:

[auto build test ERROR on pza/reset/next]
[also build test ERROR on robh/for-next clk/clk-next tip/irq/core linus/master v5.15 next-20211104]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Qin-Jian/dt-bindings-vendor-prefixes-Add-Sunplus/20211104-115746
base:   https://git.pengutronix.de/git/pza/linux reset/next
config: arc-allyesconfig (attached as .config)
compiler: arceb-elf-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/604141a9aad536dff720a6337561171d72d63d74
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Qin-Jian/dt-bindings-vendor-prefixes-Add-Sunplus/20211104-115746
        git checkout 604141a9aad536dff720a6337561171d72d63d74
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross ARCH=arc 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <redacted>

All errors (new ones prefixed by >>):

   drivers/clk/clk-sp7021.c: In function 'plltv_fractional_div':
quoted
drivers/clk/clk-sp7021.c:292:13: error: variable 'diff_min_sign' set but not used [-Werror=unused-but-set-variable]
     292 |         u32 diff_min_sign = 0;
         |             ^~~~~~~~~~~~~
   drivers/clk/clk-sp7021.c: At top level:
   drivers/clk/clk-sp7021.c:625:13: error: no previous prototype for 'clk_register_sp_pll' [-Werror=missing-prototypes]
     625 | struct clk *clk_register_sp_pll(const char *name, const char *parent,
         |             ^~~~~~~~~~~~~~~~~~~
   cc1: all warnings being treated as errors


vim +/diff_min_sign +292 drivers/clk/clk-sp7021.c

   286	
   287	static long plltv_fractional_div(struct sp_pll *clk, unsigned long freq)
   288	{
   289		u32 m, r;
   290		u32 nint, nfra;
   291		u32 diff_min_quotient = 210000000, diff_min_remainder = 0;
 > 292		u32 diff_min_sign = 0;
   293		unsigned long fvco, nf, f, fout = 0;
   294		int sdm, ph;
   295	
   296		/* check freq */
   297		if (freq < F_MIN) {
   298			pr_warn("%s: %s freq:%lu < F_MIN:%lu, round up\n",
   299				__func__, clk_hw_get_name(&clk->hw), freq, F_MIN);
   300			freq = F_MIN;
   301		} else if (freq > F_MAX) {
   302			pr_warn("%s: %s freq:%lu > F_MAX:%lu, round down\n",
   303				__func__, clk_hw_get_name(&clk->hw), freq, F_MAX);
   304			freq = F_MAX;
   305		}
   306	
   307		/* DIVR 0~3 */
   308		for (r = 0; r <= 3; r++) {
   309			fvco = freq << r;
   310			if (fvco <= FVCO_MAX)
   311				break;
   312		}
   313		f = F_27M >> r;
   314	
   315		/* PH_SEL 1/0 */
   316		for (ph = 1; ph >= 0; ph--) {
   317			const u32 *pp = pt[ph];
   318			u32 ms = 1;
   319	
   320			/* SDM_MOD 0/1 */
   321			for (sdm = 0; sdm <= 1; sdm++) {
   322				u32 mod = mods[sdm];
   323	
   324				/* DIVM 1~32 */
   325				for (m = ms; m <= 32; m++) {
   326					u32 diff_freq;
   327					u32 diff_freq_quotient = 0, diff_freq_remainder = 0;
   328					u32 diff_freq_sign = 0; /* 0:Positive number, 1:Negative number */
   329	
   330					nf = fvco * m;
   331					nint = nf / pp[3];
   332	
   333					if (nint < pp[1])
   334						continue;
   335					if (nint > pp[1])
   336						break;
   337	
   338					nfra = (((nf % pp[3]) * mod * pp[4]) + (F_27M / 2)) / F_27M;
   339					if (nfra)
   340						diff_freq = (f * (nint + pp[2]) / pp[0]) -
   341									(f * (mod - nfra) / mod / pp[4]);
   342					else
   343						diff_freq = (f * (nint) / pp[0]);
   344	
   345					diff_freq_quotient  = diff_freq / m;
   346					diff_freq_remainder = ((diff_freq % m) * 1000) / m;
   347	
   348					if (freq > diff_freq_quotient) {
   349						diff_freq_quotient  = freq - diff_freq_quotient - 1;
   350						diff_freq_remainder = 1000 - diff_freq_remainder;
   351						diff_freq_sign = 1;
   352					} else {
   353						diff_freq_quotient = diff_freq_quotient - freq;
   354						diff_freq_sign = 0;
   355					}
   356	
   357					if ((diff_min_quotient > diff_freq_quotient) ||
   358						((diff_min_quotient == diff_freq_quotient) &&
   359						(diff_min_remainder > diff_freq_remainder))) {
   360	
   361						/* found a closer freq, save parameters */
   362						clk->p[SEL_FRA] = 1;
   363						clk->p[SDM_MOD] = sdm;
   364						clk->p[PH_SEL]  = ph;
   365						clk->p[NFRA]    = nfra;
   366						clk->p[DIVR]    = r;
   367						clk->p[DIVM]    = m;
   368	
   369						fout = diff_freq / m;
   370						diff_min_quotient = diff_freq_quotient;
   371						diff_min_remainder = diff_freq_remainder;
   372						diff_min_sign = diff_freq_sign;
   373					}
   374				}
   375			}
   376		}
   377	
   378		if (!fout) {
   379			pr_err("%s: %s freq:%lu not found a valid setting\n",
   380				__func__, clk_hw_get_name(&clk->hw), freq);
   381			return -EINVAL;
   382		}
   383	
   384		return fout;
   385	}
   386	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

Re: [PATCH v4 01/10] dt-bindings: vendor-prefixes: Add Sunplus

From: Rob Herring <robh@kernel.org>
Date: 2021-11-08 17:46:01

On Thu, 04 Nov 2021 10:56:58 +0800, Qin Jian wrote:
Add vendor prefix for Sunplus Technology Co., Ltd. (http://www.sunplus.com)

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
 1 file changed, 2 insertions(+)

Please add Acked-by/Reviewed-by tags when posting new versions. However,
there's no need to repost patches *only* to add the tags. The upstream
maintainer will do that for acks received on the version they apply.

If a tag was not added on purpose, please state why and what changed.

Re: [PATCH v4 02/10] dt-bindings: arm: sunplus: Add bindings for Sunplus SP7021 SoC boards

From: Rob Herring <robh@kernel.org>
Date: 2021-11-08 17:46:14

On Thu, 04 Nov 2021 10:56:59 +0800, Qin Jian wrote:
This introduces bindings for boards based Sunplus SP7021 SoC.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 .../bindings/arm/sunplus,sp7021.yaml          | 27 +++++++++++++++++++
 MAINTAINERS                                   |  7 +++++
 2 files changed, 34 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/sunplus,sp7021.yaml

Please add Acked-by/Reviewed-by tags when posting new versions. However,
there's no need to repost patches *only* to add the tags. The upstream
maintainer will do that for acks received on the version they apply.

If a tag was not added on purpose, please state why and what changed.

Re: [PATCH v4 05/10] dt-bindings: clock: Add bindings for SP7021 clock driver

From: Rob Herring <robh@kernel.org>
Date: 2021-11-08 17:46:19

On Thu, 04 Nov 2021 10:57:02 +0800, Qin Jian wrote:
Add documentation to describe Sunplus SP7021 clock driver bindings.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 .../bindings/clock/sunplus,sp7021-clkc.yaml   |  38 ++++++
 MAINTAINERS                                   |   2 +
 include/dt-bindings/clock/sp-sp7021.h         | 112 ++++++++++++++++++
 3 files changed, 152 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/sunplus,sp7021-clkc.yaml
 create mode 100644 include/dt-bindings/clock/sp-sp7021.h

Please add Acked-by/Reviewed-by tags when posting new versions. However,
there's no need to repost patches *only* to add the tags. The upstream
maintainer will do that for acks received on the version they apply.

If a tag was not added on purpose, please state why and what changed.

Re: [PATCH v4 07/10] dt-bindings: interrupt-controller: Add bindings for SP7021 interrupt controller

From: Rob Herring <robh@kernel.org>
Date: 2021-11-08 17:46:53

On Thu, 04 Nov 2021 10:57:04 +0800, Qin Jian wrote:
Add documentation to describe Sunplus SP7021 interrupt controller bindings.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 .../sunplus,sp7021-intc.yaml                  | 62 +++++++++++++++++++
 MAINTAINERS                                   |  1 +
 2 files changed, 63 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/interrupt-controller/sunplus,sp7021-intc.yaml

Please add Acked-by/Reviewed-by tags when posting new versions. However,
there's no need to repost patches *only* to add the tags. The upstream
maintainer will do that for acks received on the version they apply.

If a tag was not added on purpose, please state why and what changed.

Re: [PATCH v4 03/10] dt-bindings: reset: Add bindings for SP7021 reset driver

From: Rob Herring <robh@kernel.org>
Date: 2021-11-12 21:56:35

On Thu, 04 Nov 2021 10:57:00 +0800, Qin Jian wrote:
Add documentation to describe Sunplus SP7021 reset driver bindings.

Signed-off-by: Qin Jian <qinjian@cqplus1.com>
---
 .../bindings/reset/sunplus,reset.yaml         | 38 ++++++++
 MAINTAINERS                                   |  2 +
 include/dt-bindings/reset/sp-sp7021.h         | 97 +++++++++++++++++++
 3 files changed, 137 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/reset/sunplus,reset.yaml
 create mode 100644 include/dt-bindings/reset/sp-sp7021.h
Reviewed-by: Rob Herring <robh@kernel.org>
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help