All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/2] Add iop driver for Sunplus SP7021
@ 2021-12-16  2:34 ` Tony Huang
  0 siblings, 0 replies; 19+ messages in thread
From: Tony Huang @ 2021-12-16  1:38 UTC (permalink / raw)
  To: robh+dt, devicetree, linuxkernel, derek.kiernan, dragan.cvetic,
	arnd, gregkh
  Cc: wells.lu, tonyhuang, Tony Huang

Add iop driver for Sunplus SP7021 SOC

This is a patch series for iop driver for Sunplus SP7021 SOC.

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.

Refer to:
https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
https://tibbo.com/store/plus1.html

Tony Huang (2):
  dt-binding: misc: Add iop yaml file for Sunplus SP7021
  misc: Add iop driver for Sunplus SP7021

 Documentation/ABI/testing/sysfs-platform-soc@B     |  22 +
 .../devicetree/bindings/misc/sunplus-iop.yaml      |  65 +++
 MAINTAINERS                                        |   7 +
 drivers/misc/Kconfig                               |  12 +
 drivers/misc/Makefile                              |   1 +
 drivers/misc/sunplus_iop.c                         | 496 +++++++++++++++++++++
 6 files changed, 603 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-platform-soc@B
 create mode 100644 Documentation/devicetree/bindings/misc/sunplus-iop.yaml
 create mode 100644 drivers/misc/sunplus_iop.c

-- 
2.7.4


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v4 1/2] dt-binding: misc: Add iop yaml file for Sunplus SP7021
  2021-12-16  2:34 ` Tony Huang
@ 2021-12-16  2:34   ` Tony Huang
  -1 siblings, 0 replies; 19+ messages in thread
From: Tony Huang @ 2021-12-16  1:38 UTC (permalink / raw)
  To: robh+dt, devicetree, linuxkernel, derek.kiernan, dragan.cvetic,
	arnd, gregkh
  Cc: wells.lu, tonyhuang, Tony Huang

Add iop yaml file for Sunplus SP7021

Signed-off-by: Tony Huang <tonyhuang.sunplus@gmail.com>
---
Changes in v4:
 - Addressed comments from Rob Herring.

 .../devicetree/bindings/misc/sunplus-iop.yaml      | 65 ++++++++++++++++++++++
 MAINTAINERS                                        |  5 ++
 2 files changed, 70 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/misc/sunplus-iop.yaml

diff --git a/Documentation/devicetree/bindings/misc/sunplus-iop.yaml b/Documentation/devicetree/bindings/misc/sunplus-iop.yaml
new file mode 100644
index 0000000..8510ef8
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/sunplus-iop.yaml
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) Sunplus Ltd. Co. 2021
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/misc/sunplus-iop.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sunplus IOP(8051) controller
+
+maintainers:
+  - Tony Huang <tonyhuang.sunplus@gmail.com>
+
+description: |
+  Processor for I/O control, RTC wake-up procedure management,
+  and cooperation with CPU&PMC in power management.
+
+properties:
+  compatible:
+    enum:
+      - sunplus,sp7021-iop
+
+  reg:
+    items:
+      - description: IOP registers regions
+      - description: PMC registers regions
+      - description: MOON0 registers regions
+
+  reg-names:
+    items:
+      - const: iop
+      - const: iop_pmc
+      - const: moon0
+
+  interrupts:
+    items:
+      - description: IOP_INT0. IOP to system Interrupt 0.
+                     Sent by IOP to system RISC.
+      - description: IOP_INT1. IOP to System Interrupt 1.
+                     Sent by IOP to system RISC.
+
+  memory-region:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - interrupts
+  - memory-region
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    iop: iop@9c000400 {
+        compatible = "sunplus,sp7021-iop";
+        reg = <0x9c000400 0x80>, <0x9c003100 0x80>, <0x9c000000 0x80>;
+        reg-names = "iop", "iop_pmc", "moon0";
+        interrupt-parent = <&intc>;
+        interrupts = <41 IRQ_TYPE_LEVEL_HIGH>, <42 IRQ_TYPE_LEVEL_HIGH>;
+        memory-region = <&iop_reserve>;
+    };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 3b79fd4..071b5e6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17945,6 +17945,11 @@ L:	netdev@vger.kernel.org
 S:	Maintained
 F:	drivers/net/ethernet/dlink/sundance.c
 
+SUNPLUS IOP DRIVER
+M:	Tony Huang <tonyhuang.sunplus@gmail.com>
+S:	Maintained
+F:	Documentation/devicetree/bindings/misc/sunplu-iop.yaml
+
 SUPERH
 M:	Yoshinori Sato <ysato@users.sourceforge.jp>
 M:	Rich Felker <dalias@libc.org>
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v4 2/2] misc: Add iop driver for Sunplus SP7021
  2021-12-16  2:34 ` Tony Huang
@ 2021-12-16  2:35   ` Tony Huang
  -1 siblings, 0 replies; 19+ messages in thread
From: Tony Huang @ 2021-12-16  1:38 UTC (permalink / raw)
  To: robh+dt, devicetree, linuxkernel, derek.kiernan, dragan.cvetic,
	arnd, gregkh
  Cc: wells.lu, tonyhuang, Tony Huang

IOP (IO Processor) embedded inside SP7021 which is used as
Processor for I/O control, RTC wake-up and cooperation with
CPU & PMC in power management purpose.
The IOP core is DQ8051, so also named IOP8051,
it supports dedicated JTAG debug pins which share with SP7021.
In standby mode operation, the power spec reach 400uA.

Signed-off-by: Tony Huang <tonyhuang.sunplus@gmail.com>
---
Changes in v4:
 - Addressed comments from Arnd Bergmann.
 - Addressed comments from Greg KH.

 Documentation/ABI/testing/sysfs-platform-soc@B |  22 ++
 MAINTAINERS                                    |   2 +
 drivers/misc/Kconfig                           |  12 +
 drivers/misc/Makefile                          |   1 +
 drivers/misc/sunplus_iop.c                     | 496 +++++++++++++++++++++++++
 5 files changed, 533 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-platform-soc@B
 create mode 100644 drivers/misc/sunplus_iop.c

diff --git a/Documentation/ABI/testing/sysfs-platform-soc@B b/Documentation/ABI/testing/sysfs-platform-soc@B
new file mode 100644
index 0000000..17d838e
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-platform-soc@B
@@ -0,0 +1,22 @@
+What:		/sys/devices/platform/soc@B/9c000400.iop/sp_iop_state1
+Date:		December 2021
+KernelVersion:	5.15
+Contact:	Tony Huang <tonyhuang.sunplus@gmail.com>
+Description:
+		SP7021 has three power states:S0, S1 and S3.
+		S0:Default domain is on. IOP domain is on. AO domain is on.
+		S1:Default domain is off. IOP domain is on. AO domain is on.
+		S3:Default domain is off. IOP domain is off. AO domain is on.
+		Read sysfs sp_iop_s1mode, system enter S1 mode.
+
+What:		/sys/devices/platform/soc@B/9c000400.iop/sp_iop_mode
+Date:		December 2021
+KernelVersion:	5.15
+Contact:	Tony Huang <tonyhuang.sunplus@gmail.com>
+Description:
+		Operation mode of IOP is switched to standby mode by writing
+		"1" to sysfs.
+		Operation mode of IOP is switched to normal mode by writing
+		"0" to sysfs.
+
+
diff --git a/MAINTAINERS b/MAINTAINERS
index 071b5e6..614b7ff 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17948,7 +17948,9 @@ F:	drivers/net/ethernet/dlink/sundance.c
 SUNPLUS IOP DRIVER
 M:	Tony Huang <tonyhuang.sunplus@gmail.com>
 S:	Maintained
+F:	Documentation/ABI/testing/sysfs-platform-soc@B
 F:	Documentation/devicetree/bindings/misc/sunplu-iop.yaml
+F:	drivers/misc/sunplus_iop.c
 
 SUPERH
 M:	Yoshinori Sato <ysato@users.sourceforge.jp>
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 0f5a49f..f19533b 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -470,6 +470,18 @@ config HISI_HIKEY_USB
 	  switching between the dual-role USB-C port and the USB-A host ports
 	  using only one USB controller.
 
+config SUNPLUS_IOP
+	tristate "Sunplus IOP support"
+	default ARCH_SUNPLUS
+	help
+	  Sunplus I/O processor (8051) driver.
+	  Processor for I/O control, RTC wake-up proceduce management,
+	  and cooperation with CPU&PMC in power management.
+	  Need Install DQ8051, The DQ8051 bin file generated by keil C.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called ad525x_dpot.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index a086197..eafeab6 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_DW_XDATA_PCIE)	+= dw-xdata-pcie.o
 obj-$(CONFIG_PCI_ENDPOINT_TEST)	+= pci_endpoint_test.o
 obj-$(CONFIG_OCXL)		+= ocxl/
 obj-$(CONFIG_BCM_VK)		+= bcm-vk/
+obj-$(CONFIG_SUNPLUS_IOP)	+= sunplus_iop.o
 obj-y				+= cardreader/
 obj-$(CONFIG_PVPANIC)   	+= pvpanic/
 obj-$(CONFIG_HABANA_AI)		+= habanalabs/
diff --git a/drivers/misc/sunplus_iop.c b/drivers/misc/sunplus_iop.c
new file mode 100644
index 0000000..8c4c870
--- /dev/null
+++ b/drivers/misc/sunplus_iop.c
@@ -0,0 +1,496 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * The IOP driver for Sunplus SP7021
+ *
+ * Copyright (C) 2021 Sunplus Technology Inc.
+ *
+ * All Rights Reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/of_platform.h>
+#include <linux/firmware.h>
+#include <linux/dma-mapping.h>
+#include <linux/of_address.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+
+enum IOP_Status_e {
+	IOP_SUCCESS,                /* successful */
+	IOP_ERR_IOP_BUSY,           /* IOP is busy */
+};
+
+struct regs_moon0 {
+	u32 stamp;         /* 00 */
+	u32 clken[10];     /* 01~10 */
+	u32 gclken[10];    /* 11~20 */
+	u32 reset[10];     /* 21~30 */
+	u32 sfg_cfg_mode;  /* 31 */
+};
+
+struct regs_iop {
+	u32 iop_control;/* 00 */
+	u32 iop_reg1;/* 01 */
+	u32 iop_bp;/* 02 */
+	u32 iop_regsel;/* 03 */
+	u32 iop_regout;/* 04 */
+	u32 iop_reg5;/* 05 */
+	u32 iop_resume_pcl;/* 06 */
+	u32 iop_resume_pch;/* 07 */
+	u32 iop_data0;/* 08 */
+	u32 iop_data1;/* 09 */
+	u32 iop_data2;/* 10 */
+	u32 iop_data3;/* 11 */
+	u32 iop_data4;/* 12 */
+	u32 iop_data5;/* 13 */
+	u32 iop_data6;/* 14 */
+	u32 iop_data7;/* 15 */
+	u32 iop_data8;/* 16 */
+	u32 iop_data9;/* 17 */
+	u32 iop_data10;/* 18 */
+	u32 iop_data11;/* 19 */
+	u32 iop_base_adr_l;/* 20 */
+	u32 iop_base_adr_h;/* 21 */
+	u32 memory_bridge_control;/* 22 */
+	u32 iop_regmap_adr_l;/* 23 */
+	u32 iop_regmap_adr_h;/* 24 */
+	u32 iop_direct_adr;/* 25*/
+	u32 reserved[6];/* 26~31 */
+};
+
+struct regs_iop_pmc {
+	u32 PMC_TIMER;/* 00 */
+	u32 PMC_CTRL;/* 01 */
+	u32 XTAL27M_PASSWORD_I;/* 02 */
+	u32 XTAL27M_PASSWORD_II;/* 03 */
+	u32 XTAL32K_PASSWORD_I;/* 04 */
+	u32 XTAL32K_PASSWORD_II;/* 05 */
+	u32 CLK27M_PASSWORD_I;/* 06 */
+	u32 CLK27M_PASSWORD_II;/* 07 */
+	u32 PMC_TIMER2;/* 08 */
+	u32 reserved[23];/* 9~31 */
+};
+
+#define NORMAL_CODE_MAX_SIZE 0X1000
+#define STANDBY_CODE_MAX_SIZE 0x4000
+struct sp_iop {
+	struct miscdevice dev;			// iop device
+	struct mutex write_lock;
+	void __iomem *iop_regs;
+	void __iomem *pmc_regs;
+	void __iomem *moon0_regs;
+	int irq;
+	unsigned char iop_normal_code[NORMAL_CODE_MAX_SIZE];
+	unsigned char iop_standby_code[STANDBY_CODE_MAX_SIZE];
+	resource_size_t iop_mem_start;
+	resource_size_t iop_mem_size;
+	bool mode;
+};
+
+static void sp_iop_normal_mode(struct sp_iop *iop)
+{
+	struct regs_iop *p_iop_reg = (struct regs_iop *)iop->iop_regs;
+	struct regs_moon0 *p_moon0_reg = (struct regs_moon0 *)iop->moon0_regs;
+	void __iomem *iop_kernel_base;
+	unsigned int reg;
+
+	iop_kernel_base = ioremap(iop->iop_mem_start, NORMAL_CODE_MAX_SIZE);
+	memset(iop_kernel_base, 0, NORMAL_CODE_MAX_SIZE);
+	memcpy(iop_kernel_base, iop->iop_normal_code, NORMAL_CODE_MAX_SIZE);
+
+	writel(0x00100010, &p_moon0_reg->clken[0]);
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg |= 0x01;
+	writel(reg, &p_iop_reg->iop_control);
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg &= ~(0x8000);
+	writel(reg, &p_iop_reg->iop_control);
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg |= 0x0200;//disable watchdog event reset IOP
+	writel(reg, &p_iop_reg->iop_control);
+
+	reg = (iop->iop_mem_start & 0xFFFF);
+	writel(reg, &p_iop_reg->iop_base_adr_l);
+	reg	= (iop->iop_mem_start >> 16);
+	writel(reg, &p_iop_reg->iop_base_adr_h);
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg &= ~(0x01);
+	writel(reg, &p_iop_reg->iop_control);
+	iop->mode = 0;
+}
+
+static void sp_iop_standby_mode(struct sp_iop *iop)
+{
+	struct regs_iop *p_iop_reg = (struct regs_iop *)iop->iop_regs;
+	struct regs_moon0 *p_moon0_reg = (struct regs_moon0 *)iop->moon0_regs;
+	void __iomem *iop_kernel_base;
+	unsigned long reg;
+
+	iop_kernel_base = ioremap(iop->iop_mem_start, STANDBY_CODE_MAX_SIZE);
+	memset(iop_kernel_base, 0, STANDBY_CODE_MAX_SIZE);
+	memcpy(iop_kernel_base, iop->iop_standby_code, STANDBY_CODE_MAX_SIZE);
+
+	writel(0x00100010, &p_moon0_reg->clken[0]);
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg |= 0x01;
+	writel(reg, &p_iop_reg->iop_control);
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg &= ~(0x8000);
+	writel(reg, &p_iop_reg->iop_control);
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg |= 0x0200;//disable watchdog event reset IOP
+	writel(reg, &p_iop_reg->iop_control);
+
+	reg = (iop->iop_mem_start & 0xFFFF);
+	writel(reg, &p_iop_reg->iop_base_adr_l);
+	reg = (iop->iop_mem_start >> 16);
+	writel(reg, &p_iop_reg->iop_base_adr_h);
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg &= ~(0x01);
+	writel(reg, &p_iop_reg->iop_control);
+	iop->mode = 1;
+}
+
+#define IOP_READY	0x4
+#define RISC_READY	0x8
+static int sp_iop_shutdown(struct device *dev, struct sp_iop *iop)
+{
+	struct regs_iop *p_iop_reg = (struct regs_iop *)iop->iop_regs;
+	struct regs_moon0 *p_moon0_reg = (struct regs_moon0 *)iop->moon0_regs;
+	struct regs_iop_pmc *p_iop_pmc_reg = (struct regs_iop_pmc *)iop->pmc_regs;
+	unsigned int reg;
+	int ret, value;
+
+	writel(0x00100010, &p_moon0_reg->clken[0]);
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg &= ~(0x8000);
+	writel(reg, &p_iop_reg->iop_control);
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg |= 0x1;
+	writel(reg, &p_iop_reg->iop_control);
+
+	//PMC set
+	writel(0x00010001, &p_iop_pmc_reg->PMC_TIMER);
+	reg = readl(&p_iop_pmc_reg->PMC_CTRL);
+	reg |= 0x23;// disable system reset PMC, enalbe power down 27M, enable gating 27M
+	writel(reg, &p_iop_pmc_reg->PMC_CTRL);
+
+	writel(0x55aa00ff, &p_iop_pmc_reg->XTAL27M_PASSWORD_I);
+	writel(0x00ff55aa, &p_iop_pmc_reg->XTAL27M_PASSWORD_II);
+	writel(0xaa00ff55, &p_iop_pmc_reg->XTAL32K_PASSWORD_I);
+	writel(0xff55aa00, &p_iop_pmc_reg->XTAL32K_PASSWORD_II);
+	writel(0xaaff0055, &p_iop_pmc_reg->CLK27M_PASSWORD_I);
+	writel(0x5500aaff, &p_iop_pmc_reg->CLK27M_PASSWORD_II);
+	writel(0x01000100, &p_iop_pmc_reg->PMC_TIMER2);
+
+	//IOP Hardware IP reset
+	reg = readl(&p_moon0_reg->reset[0]);
+	reg |= 0x10;
+	writel(reg, (&p_moon0_reg->reset[0]));
+	reg &= ~(0x10);
+	writel(reg, (&p_moon0_reg->reset[0]));
+
+	writel(0x00ff0085, (iop->moon0_regs + 32 * 4 * 1 + 4 * 1));
+
+	reg = readl(iop->moon0_regs + 32 * 4 * 1 + 4 * 2);
+	reg |= 0x08000800;
+	writel(reg, (iop->moon0_regs + 32 * 4 * 1 + 4 * 2));
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg |= 0x0200;//disable watchdog event reset IOP
+	writel(reg, &p_iop_reg->iop_control);
+
+	reg = (iop->iop_mem_start & 0xFFFF);
+	writel(reg, &p_iop_reg->iop_base_adr_l);
+	reg = (iop->iop_mem_start >> 16);
+	writel(reg, &p_iop_reg->iop_base_adr_h);
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg &= ~(0x01);
+	writel(reg, &p_iop_reg->iop_control);
+
+	ret = readl_poll_timeout(&p_iop_reg->iop_data2, value,
+				 (value & IOP_READY) == IOP_READY, 1000, 10000);
+	if (ret) {
+		dev_err(dev, "timed out\n");
+		return ret;
+	}
+
+	writel(RISC_READY, &p_iop_reg->iop_data2);
+	writel(0x00, &p_iop_reg->iop_data5);
+	writel(0x60, &p_iop_reg->iop_data6);
+
+	ret = readl_poll_timeout(&p_iop_reg->iop_data7, value,
+				 value == 0xaaaa, 1000, 10000);
+	if (ret) {
+		dev_err(dev, "timed out\n");
+		return ret;
+	}
+
+	writel(0xdd, &p_iop_reg->iop_data1);//8051 bin file call Ultra low function.
+	mdelay(10);
+	return 0;
+}
+
+static int sp_iop_s1mode(struct device *dev, struct sp_iop *iop)
+{
+	struct regs_iop *p_iop_reg = (struct regs_iop *)iop->iop_regs;
+	int ret, value;
+
+	ret = readl_poll_timeout(&p_iop_reg->iop_data2, value,
+				 (value & IOP_READY) == IOP_READY, 1000, 10000);
+	if (ret) {
+		dev_err(dev, "timed out\n");
+		return ret;
+	}
+
+	writel(RISC_READY, &p_iop_reg->iop_data2);
+	writel(0x00, &p_iop_reg->iop_data5);
+	writel(0x60, &p_iop_reg->iop_data6);
+
+	ret = readl_poll_timeout(&p_iop_reg->iop_data7, value,
+				 value == 0xaaaa, 1000, 10000);
+	if (ret) {
+		dev_err(dev, "timed out\n");
+		return ret;
+	}
+
+	writel(0xee, &p_iop_reg->iop_data1);//8051 bin file call S1_mode function.
+	mdelay(10);
+	return 0;
+}
+
+static ssize_t sp_iop_state1_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct sp_iop *iop = dev_get_drvdata(dev);
+	ssize_t len = 0;
+
+	sp_iop_standby_mode(iop);
+	mdelay(10);
+	sp_iop_s1mode(dev, iop);
+	return len;
+}
+
+static ssize_t sp_iop_state1_store(struct device *dev, struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	ssize_t len = 0;
+
+	return len;
+}
+
+static ssize_t sp_iop_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct sp_iop *iop = dev_get_drvdata(dev);
+	ssize_t len = 0;
+
+	if (iop->mode == 0)
+		dev_info(dev, "iop_normal_mode\n");
+	else if (iop->mode == 1)
+		dev_info(dev, "iop_standby_mode\n");
+	return len;
+}
+
+static ssize_t sp_iop_mode_store(struct device *dev, struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+	struct sp_iop *iop = dev_get_drvdata(dev);
+	unsigned char ret = count;
+
+	if (buf[0] == '0') {
+		sp_iop_normal_mode(iop);
+		dev_info(dev, "Switch to normal mode.\n");
+	} else if (buf[0] == '1') {
+		sp_iop_standby_mode(iop);
+		dev_info(dev, "Switch to standby mode.\n");
+	} else {
+		dev_info(dev, "echo 0 or 1 mode\n");
+		dev_info(dev, "0:normal mode\n");
+		dev_info(dev, "1:standby mode\n");
+	}
+	return ret;
+}
+
+static DEVICE_ATTR_RW(sp_iop_state1);
+static DEVICE_ATTR_RW(sp_iop_mode);
+
+static int  sp_iop_get_normal_code(struct device *dev, struct sp_iop *iop)
+{
+	const struct firmware *fw;
+	static const char file[] = "normal.bin";
+	unsigned int err, i;
+
+	err = request_firmware(&fw, file, dev);
+	if (err) {
+		dev_err(dev, "get bin file error\n");
+		return err;
+	}
+
+	for (i = 0; i < NORMAL_CODE_MAX_SIZE; i++) {
+		char temp;
+
+		temp = fw->data[i];
+		iop->iop_normal_code[i] = temp;
+	}
+	release_firmware(fw);
+	return err;
+}
+
+static int  sp_iop_get_standby_code(struct device *dev, struct sp_iop *iop)
+{
+	const struct firmware *fw;
+	static const char file[] = "standby.bin";
+	unsigned int err, i;
+
+	err = request_firmware(&fw, file, dev);
+	if (err) {
+		dev_err(dev, "get bin file error\n");
+		return err;
+	}
+
+	for (i = 0; i < STANDBY_CODE_MAX_SIZE; i++) {
+		char temp;
+
+		temp = fw->data[i];
+		iop->iop_standby_code[i] = temp;
+	}
+	release_firmware(fw);
+	return err;
+}
+
+static int sp_iop_get_resources(struct platform_device *pdev, struct sp_iop *p_sp_iop_info)
+{
+	struct resource *r;
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iop");
+	p_sp_iop_info->iop_regs = devm_ioremap_resource(&pdev->dev, r);
+	if (IS_ERR(p_sp_iop_info->iop_regs)) {
+		dev_err(&pdev->dev, "ioremap fail\n");
+		return PTR_ERR(p_sp_iop_info->iop_regs);
+	}
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iop_pmc");
+	p_sp_iop_info->pmc_regs = devm_ioremap_resource(&pdev->dev, r);
+	if (IS_ERR(p_sp_iop_info->pmc_regs)) {
+		dev_err(&pdev->dev, "ioremap fail\n");
+		return PTR_ERR(p_sp_iop_info->pmc_regs);
+	}
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "moon0");
+	p_sp_iop_info->moon0_regs = devm_ioremap_resource(&pdev->dev, r);
+	if (IS_ERR(p_sp_iop_info->moon0_regs)) {
+		dev_err(&pdev->dev, "ioremap fail\n");
+		return PTR_ERR(p_sp_iop_info->moon0_regs);
+	}
+	return IOP_SUCCESS;
+}
+
+static int sp_iop_platform_driver_probe(struct platform_device *pdev)
+{
+	int ret = -ENXIO;
+	int rc;
+	struct sp_iop *iop;
+	struct device_node *memnp;
+	struct resource mem_res;
+
+	iop = devm_kzalloc(&pdev->dev, sizeof(struct sp_iop), GFP_KERNEL);
+	if (!iop) {
+		ret	= -ENOMEM;
+		goto fail_kmalloc;
+	}
+	/* init */
+	mutex_init(&iop->write_lock);
+	/* register device */
+	iop->dev.name  = "sp_iop";
+	iop->dev.minor = MISC_DYNAMIC_MINOR;
+	ret = misc_register(&iop->dev);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "sp_iop device register fail\n");
+		goto fail_regdev;
+	}
+
+	ret = sp_iop_get_resources(pdev, iop);
+
+	//Get reserve address
+	memnp = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);
+	if (!memnp) {
+		dev_err(&pdev->dev, "no memory-region node\n");
+		return -EINVAL;
+	}
+
+	rc = of_address_to_resource(memnp, 0, &mem_res);
+	of_node_put(memnp);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to translate memory-region to a resource\n");
+		return -EINVAL;
+	}
+
+	iop->iop_mem_start = mem_res.start;
+	iop->iop_mem_size = resource_size(&mem_res);
+
+	ret = sp_iop_get_normal_code(&pdev->dev, iop);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "get normal code err=%d\n", ret);
+		return ret;
+	}
+
+	ret = sp_iop_get_standby_code(&pdev->dev, iop);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "get standby code err=%d\n", ret);
+		return ret;
+	}
+
+	sp_iop_normal_mode(iop);
+	platform_set_drvdata(pdev, iop);
+	device_create_file(&pdev->dev, &dev_attr_sp_iop_state1);
+	device_create_file(&pdev->dev, &dev_attr_sp_iop_mode);
+	return 0;
+
+fail_regdev:
+	mutex_destroy(&iop->write_lock);
+fail_kmalloc:
+	return ret;
+}
+
+static void sp_iop_platform_driver_shutdown(struct platform_device *pdev)
+{
+	struct sp_iop *iop = platform_get_drvdata(pdev);
+
+	sp_iop_standby_mode(iop);
+	mdelay(10);
+	sp_iop_shutdown(&pdev->dev, iop);
+}
+
+static const struct of_device_id sp_iop_of_match[] = {
+	{ .compatible = "sunplus,sp7021-iop" },
+	{ /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, sp_iop_of_match);
+
+static struct platform_driver sp_iop_platform_driver = {
+	.probe		= sp_iop_platform_driver_probe,
+	.shutdown	= sp_iop_platform_driver_shutdown,
+	.driver = {
+		.name	= "sunplus,sp7021-iop",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(sp_iop_of_match),
+	}
+};
+
+module_platform_driver(sp_iop_platform_driver);
+
+MODULE_AUTHOR("Tony Huang <tonyhuang.sunplus@gmail.com>");
+MODULE_DESCRIPTION("Sunplus IOP Driver");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v4 0/2] Add iop driver for Sunplus SP7021
@ 2021-12-16  2:34 ` Tony Huang
  0 siblings, 0 replies; 19+ messages in thread
From: Tony Huang @ 2021-12-16  2:34 UTC (permalink / raw)
  To: linux-kernel; +Cc: Tony Huang

Add iop driver for Sunplus SP7021 SOC

This is a patch series for iop driver for Sunplus SP7021 SOC.

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.

Refer to:
https://sunplus-tibbo.atlassian.net/wiki/spaces/doc/overview
https://tibbo.com/store/plus1.html

Tony Huang (2):
  dt-binding: misc: Add iop yaml file for Sunplus SP7021
  misc: Add iop driver for Sunplus SP7021

 Documentation/ABI/testing/sysfs-platform-soc@B     |  22 +
 .../devicetree/bindings/misc/sunplus-iop.yaml      |  65 +++
 MAINTAINERS                                        |   7 +
 drivers/misc/Kconfig                               |  12 +
 drivers/misc/Makefile                              |   1 +
 drivers/misc/sunplus_iop.c                         | 496 +++++++++++++++++++++
 6 files changed, 603 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-platform-soc@B
 create mode 100644 Documentation/devicetree/bindings/misc/sunplus-iop.yaml
 create mode 100644 drivers/misc/sunplus_iop.c

-- 
2.7.4


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v4 1/2] dt-binding: misc: Add iop yaml file for Sunplus SP7021
@ 2021-12-16  2:34   ` Tony Huang
  0 siblings, 0 replies; 19+ messages in thread
From: Tony Huang @ 2021-12-16  2:34 UTC (permalink / raw)
  To: linux-kernel; +Cc: Tony Huang

Add iop yaml file for Sunplus SP7021

Signed-off-by: Tony Huang <tonyhuang.sunplus@gmail.com>
---
Changes in v4:
 - Addressed comments from Rob Herring.

 .../devicetree/bindings/misc/sunplus-iop.yaml      | 65 ++++++++++++++++++++++
 MAINTAINERS                                        |  5 ++
 2 files changed, 70 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/misc/sunplus-iop.yaml

diff --git a/Documentation/devicetree/bindings/misc/sunplus-iop.yaml b/Documentation/devicetree/bindings/misc/sunplus-iop.yaml
new file mode 100644
index 0000000..8510ef8
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/sunplus-iop.yaml
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) Sunplus Ltd. Co. 2021
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/misc/sunplus-iop.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sunplus IOP(8051) controller
+
+maintainers:
+  - Tony Huang <tonyhuang.sunplus@gmail.com>
+
+description: |
+  Processor for I/O control, RTC wake-up procedure management,
+  and cooperation with CPU&PMC in power management.
+
+properties:
+  compatible:
+    enum:
+      - sunplus,sp7021-iop
+
+  reg:
+    items:
+      - description: IOP registers regions
+      - description: PMC registers regions
+      - description: MOON0 registers regions
+
+  reg-names:
+    items:
+      - const: iop
+      - const: iop_pmc
+      - const: moon0
+
+  interrupts:
+    items:
+      - description: IOP_INT0. IOP to system Interrupt 0.
+                     Sent by IOP to system RISC.
+      - description: IOP_INT1. IOP to System Interrupt 1.
+                     Sent by IOP to system RISC.
+
+  memory-region:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - interrupts
+  - memory-region
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    iop: iop@9c000400 {
+        compatible = "sunplus,sp7021-iop";
+        reg = <0x9c000400 0x80>, <0x9c003100 0x80>, <0x9c000000 0x80>;
+        reg-names = "iop", "iop_pmc", "moon0";
+        interrupt-parent = <&intc>;
+        interrupts = <41 IRQ_TYPE_LEVEL_HIGH>, <42 IRQ_TYPE_LEVEL_HIGH>;
+        memory-region = <&iop_reserve>;
+    };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 3b79fd4..071b5e6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17945,6 +17945,11 @@ L:	netdev@vger.kernel.org
 S:	Maintained
 F:	drivers/net/ethernet/dlink/sundance.c
 
+SUNPLUS IOP DRIVER
+M:	Tony Huang <tonyhuang.sunplus@gmail.com>
+S:	Maintained
+F:	Documentation/devicetree/bindings/misc/sunplu-iop.yaml
+
 SUPERH
 M:	Yoshinori Sato <ysato@users.sourceforge.jp>
 M:	Rich Felker <dalias@libc.org>
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v4 2/2] misc: Add iop driver for Sunplus SP7021
@ 2021-12-16  2:35   ` Tony Huang
  0 siblings, 0 replies; 19+ messages in thread
From: Tony Huang @ 2021-12-16  2:35 UTC (permalink / raw)
  To: linux-kernel; +Cc: Tony Huang

IOP (IO Processor) embedded inside SP7021 which is used as
Processor for I/O control, RTC wake-up and cooperation with
CPU & PMC in power management purpose.
The IOP core is DQ8051, so also named IOP8051,
it supports dedicated JTAG debug pins which share with SP7021.
In standby mode operation, the power spec reach 400uA.

Signed-off-by: Tony Huang <tonyhuang.sunplus@gmail.com>
---
Changes in v4:
 - Addressed comments from Arnd Bergmann.
 - Addressed comments from Greg KH.

 Documentation/ABI/testing/sysfs-platform-soc@B |  22 ++
 MAINTAINERS                                    |   2 +
 drivers/misc/Kconfig                           |  12 +
 drivers/misc/Makefile                          |   1 +
 drivers/misc/sunplus_iop.c                     | 496 +++++++++++++++++++++++++
 5 files changed, 533 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-platform-soc@B
 create mode 100644 drivers/misc/sunplus_iop.c

diff --git a/Documentation/ABI/testing/sysfs-platform-soc@B b/Documentation/ABI/testing/sysfs-platform-soc@B
new file mode 100644
index 0000000..17d838e
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-platform-soc@B
@@ -0,0 +1,22 @@
+What:		/sys/devices/platform/soc@B/9c000400.iop/sp_iop_state1
+Date:		December 2021
+KernelVersion:	5.15
+Contact:	Tony Huang <tonyhuang.sunplus@gmail.com>
+Description:
+		SP7021 has three power states:S0, S1 and S3.
+		S0:Default domain is on. IOP domain is on. AO domain is on.
+		S1:Default domain is off. IOP domain is on. AO domain is on.
+		S3:Default domain is off. IOP domain is off. AO domain is on.
+		Read sysfs sp_iop_s1mode, system enter S1 mode.
+
+What:		/sys/devices/platform/soc@B/9c000400.iop/sp_iop_mode
+Date:		December 2021
+KernelVersion:	5.15
+Contact:	Tony Huang <tonyhuang.sunplus@gmail.com>
+Description:
+		Operation mode of IOP is switched to standby mode by writing
+		"1" to sysfs.
+		Operation mode of IOP is switched to normal mode by writing
+		"0" to sysfs.
+
+
diff --git a/MAINTAINERS b/MAINTAINERS
index 071b5e6..614b7ff 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17948,7 +17948,9 @@ F:	drivers/net/ethernet/dlink/sundance.c
 SUNPLUS IOP DRIVER
 M:	Tony Huang <tonyhuang.sunplus@gmail.com>
 S:	Maintained
+F:	Documentation/ABI/testing/sysfs-platform-soc@B
 F:	Documentation/devicetree/bindings/misc/sunplu-iop.yaml
+F:	drivers/misc/sunplus_iop.c
 
 SUPERH
 M:	Yoshinori Sato <ysato@users.sourceforge.jp>
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 0f5a49f..f19533b 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -470,6 +470,18 @@ config HISI_HIKEY_USB
 	  switching between the dual-role USB-C port and the USB-A host ports
 	  using only one USB controller.
 
+config SUNPLUS_IOP
+	tristate "Sunplus IOP support"
+	default ARCH_SUNPLUS
+	help
+	  Sunplus I/O processor (8051) driver.
+	  Processor for I/O control, RTC wake-up proceduce management,
+	  and cooperation with CPU&PMC in power management.
+	  Need Install DQ8051, The DQ8051 bin file generated by keil C.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called ad525x_dpot.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index a086197..eafeab6 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_DW_XDATA_PCIE)	+= dw-xdata-pcie.o
 obj-$(CONFIG_PCI_ENDPOINT_TEST)	+= pci_endpoint_test.o
 obj-$(CONFIG_OCXL)		+= ocxl/
 obj-$(CONFIG_BCM_VK)		+= bcm-vk/
+obj-$(CONFIG_SUNPLUS_IOP)	+= sunplus_iop.o
 obj-y				+= cardreader/
 obj-$(CONFIG_PVPANIC)   	+= pvpanic/
 obj-$(CONFIG_HABANA_AI)		+= habanalabs/
diff --git a/drivers/misc/sunplus_iop.c b/drivers/misc/sunplus_iop.c
new file mode 100644
index 0000000..8c4c870
--- /dev/null
+++ b/drivers/misc/sunplus_iop.c
@@ -0,0 +1,496 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * The IOP driver for Sunplus SP7021
+ *
+ * Copyright (C) 2021 Sunplus Technology Inc.
+ *
+ * All Rights Reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/of_platform.h>
+#include <linux/firmware.h>
+#include <linux/dma-mapping.h>
+#include <linux/of_address.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+
+enum IOP_Status_e {
+	IOP_SUCCESS,                /* successful */
+	IOP_ERR_IOP_BUSY,           /* IOP is busy */
+};
+
+struct regs_moon0 {
+	u32 stamp;         /* 00 */
+	u32 clken[10];     /* 01~10 */
+	u32 gclken[10];    /* 11~20 */
+	u32 reset[10];     /* 21~30 */
+	u32 sfg_cfg_mode;  /* 31 */
+};
+
+struct regs_iop {
+	u32 iop_control;/* 00 */
+	u32 iop_reg1;/* 01 */
+	u32 iop_bp;/* 02 */
+	u32 iop_regsel;/* 03 */
+	u32 iop_regout;/* 04 */
+	u32 iop_reg5;/* 05 */
+	u32 iop_resume_pcl;/* 06 */
+	u32 iop_resume_pch;/* 07 */
+	u32 iop_data0;/* 08 */
+	u32 iop_data1;/* 09 */
+	u32 iop_data2;/* 10 */
+	u32 iop_data3;/* 11 */
+	u32 iop_data4;/* 12 */
+	u32 iop_data5;/* 13 */
+	u32 iop_data6;/* 14 */
+	u32 iop_data7;/* 15 */
+	u32 iop_data8;/* 16 */
+	u32 iop_data9;/* 17 */
+	u32 iop_data10;/* 18 */
+	u32 iop_data11;/* 19 */
+	u32 iop_base_adr_l;/* 20 */
+	u32 iop_base_adr_h;/* 21 */
+	u32 memory_bridge_control;/* 22 */
+	u32 iop_regmap_adr_l;/* 23 */
+	u32 iop_regmap_adr_h;/* 24 */
+	u32 iop_direct_adr;/* 25*/
+	u32 reserved[6];/* 26~31 */
+};
+
+struct regs_iop_pmc {
+	u32 PMC_TIMER;/* 00 */
+	u32 PMC_CTRL;/* 01 */
+	u32 XTAL27M_PASSWORD_I;/* 02 */
+	u32 XTAL27M_PASSWORD_II;/* 03 */
+	u32 XTAL32K_PASSWORD_I;/* 04 */
+	u32 XTAL32K_PASSWORD_II;/* 05 */
+	u32 CLK27M_PASSWORD_I;/* 06 */
+	u32 CLK27M_PASSWORD_II;/* 07 */
+	u32 PMC_TIMER2;/* 08 */
+	u32 reserved[23];/* 9~31 */
+};
+
+#define NORMAL_CODE_MAX_SIZE 0X1000
+#define STANDBY_CODE_MAX_SIZE 0x4000
+struct sp_iop {
+	struct miscdevice dev;			// iop device
+	struct mutex write_lock;
+	void __iomem *iop_regs;
+	void __iomem *pmc_regs;
+	void __iomem *moon0_regs;
+	int irq;
+	unsigned char iop_normal_code[NORMAL_CODE_MAX_SIZE];
+	unsigned char iop_standby_code[STANDBY_CODE_MAX_SIZE];
+	resource_size_t iop_mem_start;
+	resource_size_t iop_mem_size;
+	bool mode;
+};
+
+static void sp_iop_normal_mode(struct sp_iop *iop)
+{
+	struct regs_iop *p_iop_reg = (struct regs_iop *)iop->iop_regs;
+	struct regs_moon0 *p_moon0_reg = (struct regs_moon0 *)iop->moon0_regs;
+	void __iomem *iop_kernel_base;
+	unsigned int reg;
+
+	iop_kernel_base = ioremap(iop->iop_mem_start, NORMAL_CODE_MAX_SIZE);
+	memset(iop_kernel_base, 0, NORMAL_CODE_MAX_SIZE);
+	memcpy(iop_kernel_base, iop->iop_normal_code, NORMAL_CODE_MAX_SIZE);
+
+	writel(0x00100010, &p_moon0_reg->clken[0]);
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg |= 0x01;
+	writel(reg, &p_iop_reg->iop_control);
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg &= ~(0x8000);
+	writel(reg, &p_iop_reg->iop_control);
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg |= 0x0200;//disable watchdog event reset IOP
+	writel(reg, &p_iop_reg->iop_control);
+
+	reg = (iop->iop_mem_start & 0xFFFF);
+	writel(reg, &p_iop_reg->iop_base_adr_l);
+	reg	= (iop->iop_mem_start >> 16);
+	writel(reg, &p_iop_reg->iop_base_adr_h);
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg &= ~(0x01);
+	writel(reg, &p_iop_reg->iop_control);
+	iop->mode = 0;
+}
+
+static void sp_iop_standby_mode(struct sp_iop *iop)
+{
+	struct regs_iop *p_iop_reg = (struct regs_iop *)iop->iop_regs;
+	struct regs_moon0 *p_moon0_reg = (struct regs_moon0 *)iop->moon0_regs;
+	void __iomem *iop_kernel_base;
+	unsigned long reg;
+
+	iop_kernel_base = ioremap(iop->iop_mem_start, STANDBY_CODE_MAX_SIZE);
+	memset(iop_kernel_base, 0, STANDBY_CODE_MAX_SIZE);
+	memcpy(iop_kernel_base, iop->iop_standby_code, STANDBY_CODE_MAX_SIZE);
+
+	writel(0x00100010, &p_moon0_reg->clken[0]);
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg |= 0x01;
+	writel(reg, &p_iop_reg->iop_control);
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg &= ~(0x8000);
+	writel(reg, &p_iop_reg->iop_control);
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg |= 0x0200;//disable watchdog event reset IOP
+	writel(reg, &p_iop_reg->iop_control);
+
+	reg = (iop->iop_mem_start & 0xFFFF);
+	writel(reg, &p_iop_reg->iop_base_adr_l);
+	reg = (iop->iop_mem_start >> 16);
+	writel(reg, &p_iop_reg->iop_base_adr_h);
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg &= ~(0x01);
+	writel(reg, &p_iop_reg->iop_control);
+	iop->mode = 1;
+}
+
+#define IOP_READY	0x4
+#define RISC_READY	0x8
+static int sp_iop_shutdown(struct device *dev, struct sp_iop *iop)
+{
+	struct regs_iop *p_iop_reg = (struct regs_iop *)iop->iop_regs;
+	struct regs_moon0 *p_moon0_reg = (struct regs_moon0 *)iop->moon0_regs;
+	struct regs_iop_pmc *p_iop_pmc_reg = (struct regs_iop_pmc *)iop->pmc_regs;
+	unsigned int reg;
+	int ret, value;
+
+	writel(0x00100010, &p_moon0_reg->clken[0]);
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg &= ~(0x8000);
+	writel(reg, &p_iop_reg->iop_control);
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg |= 0x1;
+	writel(reg, &p_iop_reg->iop_control);
+
+	//PMC set
+	writel(0x00010001, &p_iop_pmc_reg->PMC_TIMER);
+	reg = readl(&p_iop_pmc_reg->PMC_CTRL);
+	reg |= 0x23;// disable system reset PMC, enalbe power down 27M, enable gating 27M
+	writel(reg, &p_iop_pmc_reg->PMC_CTRL);
+
+	writel(0x55aa00ff, &p_iop_pmc_reg->XTAL27M_PASSWORD_I);
+	writel(0x00ff55aa, &p_iop_pmc_reg->XTAL27M_PASSWORD_II);
+	writel(0xaa00ff55, &p_iop_pmc_reg->XTAL32K_PASSWORD_I);
+	writel(0xff55aa00, &p_iop_pmc_reg->XTAL32K_PASSWORD_II);
+	writel(0xaaff0055, &p_iop_pmc_reg->CLK27M_PASSWORD_I);
+	writel(0x5500aaff, &p_iop_pmc_reg->CLK27M_PASSWORD_II);
+	writel(0x01000100, &p_iop_pmc_reg->PMC_TIMER2);
+
+	//IOP Hardware IP reset
+	reg = readl(&p_moon0_reg->reset[0]);
+	reg |= 0x10;
+	writel(reg, (&p_moon0_reg->reset[0]));
+	reg &= ~(0x10);
+	writel(reg, (&p_moon0_reg->reset[0]));
+
+	writel(0x00ff0085, (iop->moon0_regs + 32 * 4 * 1 + 4 * 1));
+
+	reg = readl(iop->moon0_regs + 32 * 4 * 1 + 4 * 2);
+	reg |= 0x08000800;
+	writel(reg, (iop->moon0_regs + 32 * 4 * 1 + 4 * 2));
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg |= 0x0200;//disable watchdog event reset IOP
+	writel(reg, &p_iop_reg->iop_control);
+
+	reg = (iop->iop_mem_start & 0xFFFF);
+	writel(reg, &p_iop_reg->iop_base_adr_l);
+	reg = (iop->iop_mem_start >> 16);
+	writel(reg, &p_iop_reg->iop_base_adr_h);
+
+	reg = readl(&p_iop_reg->iop_control);
+	reg &= ~(0x01);
+	writel(reg, &p_iop_reg->iop_control);
+
+	ret = readl_poll_timeout(&p_iop_reg->iop_data2, value,
+				 (value & IOP_READY) == IOP_READY, 1000, 10000);
+	if (ret) {
+		dev_err(dev, "timed out\n");
+		return ret;
+	}
+
+	writel(RISC_READY, &p_iop_reg->iop_data2);
+	writel(0x00, &p_iop_reg->iop_data5);
+	writel(0x60, &p_iop_reg->iop_data6);
+
+	ret = readl_poll_timeout(&p_iop_reg->iop_data7, value,
+				 value == 0xaaaa, 1000, 10000);
+	if (ret) {
+		dev_err(dev, "timed out\n");
+		return ret;
+	}
+
+	writel(0xdd, &p_iop_reg->iop_data1);//8051 bin file call Ultra low function.
+	mdelay(10);
+	return 0;
+}
+
+static int sp_iop_s1mode(struct device *dev, struct sp_iop *iop)
+{
+	struct regs_iop *p_iop_reg = (struct regs_iop *)iop->iop_regs;
+	int ret, value;
+
+	ret = readl_poll_timeout(&p_iop_reg->iop_data2, value,
+				 (value & IOP_READY) == IOP_READY, 1000, 10000);
+	if (ret) {
+		dev_err(dev, "timed out\n");
+		return ret;
+	}
+
+	writel(RISC_READY, &p_iop_reg->iop_data2);
+	writel(0x00, &p_iop_reg->iop_data5);
+	writel(0x60, &p_iop_reg->iop_data6);
+
+	ret = readl_poll_timeout(&p_iop_reg->iop_data7, value,
+				 value == 0xaaaa, 1000, 10000);
+	if (ret) {
+		dev_err(dev, "timed out\n");
+		return ret;
+	}
+
+	writel(0xee, &p_iop_reg->iop_data1);//8051 bin file call S1_mode function.
+	mdelay(10);
+	return 0;
+}
+
+static ssize_t sp_iop_state1_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct sp_iop *iop = dev_get_drvdata(dev);
+	ssize_t len = 0;
+
+	sp_iop_standby_mode(iop);
+	mdelay(10);
+	sp_iop_s1mode(dev, iop);
+	return len;
+}
+
+static ssize_t sp_iop_state1_store(struct device *dev, struct device_attribute *attr,
+				   const char *buf, size_t count)
+{
+	ssize_t len = 0;
+
+	return len;
+}
+
+static ssize_t sp_iop_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct sp_iop *iop = dev_get_drvdata(dev);
+	ssize_t len = 0;
+
+	if (iop->mode == 0)
+		dev_info(dev, "iop_normal_mode\n");
+	else if (iop->mode == 1)
+		dev_info(dev, "iop_standby_mode\n");
+	return len;
+}
+
+static ssize_t sp_iop_mode_store(struct device *dev, struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+	struct sp_iop *iop = dev_get_drvdata(dev);
+	unsigned char ret = count;
+
+	if (buf[0] == '0') {
+		sp_iop_normal_mode(iop);
+		dev_info(dev, "Switch to normal mode.\n");
+	} else if (buf[0] == '1') {
+		sp_iop_standby_mode(iop);
+		dev_info(dev, "Switch to standby mode.\n");
+	} else {
+		dev_info(dev, "echo 0 or 1 mode\n");
+		dev_info(dev, "0:normal mode\n");
+		dev_info(dev, "1:standby mode\n");
+	}
+	return ret;
+}
+
+static DEVICE_ATTR_RW(sp_iop_state1);
+static DEVICE_ATTR_RW(sp_iop_mode);
+
+static int  sp_iop_get_normal_code(struct device *dev, struct sp_iop *iop)
+{
+	const struct firmware *fw;
+	static const char file[] = "normal.bin";
+	unsigned int err, i;
+
+	err = request_firmware(&fw, file, dev);
+	if (err) {
+		dev_err(dev, "get bin file error\n");
+		return err;
+	}
+
+	for (i = 0; i < NORMAL_CODE_MAX_SIZE; i++) {
+		char temp;
+
+		temp = fw->data[i];
+		iop->iop_normal_code[i] = temp;
+	}
+	release_firmware(fw);
+	return err;
+}
+
+static int  sp_iop_get_standby_code(struct device *dev, struct sp_iop *iop)
+{
+	const struct firmware *fw;
+	static const char file[] = "standby.bin";
+	unsigned int err, i;
+
+	err = request_firmware(&fw, file, dev);
+	if (err) {
+		dev_err(dev, "get bin file error\n");
+		return err;
+	}
+
+	for (i = 0; i < STANDBY_CODE_MAX_SIZE; i++) {
+		char temp;
+
+		temp = fw->data[i];
+		iop->iop_standby_code[i] = temp;
+	}
+	release_firmware(fw);
+	return err;
+}
+
+static int sp_iop_get_resources(struct platform_device *pdev, struct sp_iop *p_sp_iop_info)
+{
+	struct resource *r;
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iop");
+	p_sp_iop_info->iop_regs = devm_ioremap_resource(&pdev->dev, r);
+	if (IS_ERR(p_sp_iop_info->iop_regs)) {
+		dev_err(&pdev->dev, "ioremap fail\n");
+		return PTR_ERR(p_sp_iop_info->iop_regs);
+	}
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iop_pmc");
+	p_sp_iop_info->pmc_regs = devm_ioremap_resource(&pdev->dev, r);
+	if (IS_ERR(p_sp_iop_info->pmc_regs)) {
+		dev_err(&pdev->dev, "ioremap fail\n");
+		return PTR_ERR(p_sp_iop_info->pmc_regs);
+	}
+
+	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "moon0");
+	p_sp_iop_info->moon0_regs = devm_ioremap_resource(&pdev->dev, r);
+	if (IS_ERR(p_sp_iop_info->moon0_regs)) {
+		dev_err(&pdev->dev, "ioremap fail\n");
+		return PTR_ERR(p_sp_iop_info->moon0_regs);
+	}
+	return IOP_SUCCESS;
+}
+
+static int sp_iop_platform_driver_probe(struct platform_device *pdev)
+{
+	int ret = -ENXIO;
+	int rc;
+	struct sp_iop *iop;
+	struct device_node *memnp;
+	struct resource mem_res;
+
+	iop = devm_kzalloc(&pdev->dev, sizeof(struct sp_iop), GFP_KERNEL);
+	if (!iop) {
+		ret	= -ENOMEM;
+		goto fail_kmalloc;
+	}
+	/* init */
+	mutex_init(&iop->write_lock);
+	/* register device */
+	iop->dev.name  = "sp_iop";
+	iop->dev.minor = MISC_DYNAMIC_MINOR;
+	ret = misc_register(&iop->dev);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "sp_iop device register fail\n");
+		goto fail_regdev;
+	}
+
+	ret = sp_iop_get_resources(pdev, iop);
+
+	//Get reserve address
+	memnp = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);
+	if (!memnp) {
+		dev_err(&pdev->dev, "no memory-region node\n");
+		return -EINVAL;
+	}
+
+	rc = of_address_to_resource(memnp, 0, &mem_res);
+	of_node_put(memnp);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to translate memory-region to a resource\n");
+		return -EINVAL;
+	}
+
+	iop->iop_mem_start = mem_res.start;
+	iop->iop_mem_size = resource_size(&mem_res);
+
+	ret = sp_iop_get_normal_code(&pdev->dev, iop);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "get normal code err=%d\n", ret);
+		return ret;
+	}
+
+	ret = sp_iop_get_standby_code(&pdev->dev, iop);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "get standby code err=%d\n", ret);
+		return ret;
+	}
+
+	sp_iop_normal_mode(iop);
+	platform_set_drvdata(pdev, iop);
+	device_create_file(&pdev->dev, &dev_attr_sp_iop_state1);
+	device_create_file(&pdev->dev, &dev_attr_sp_iop_mode);
+	return 0;
+
+fail_regdev:
+	mutex_destroy(&iop->write_lock);
+fail_kmalloc:
+	return ret;
+}
+
+static void sp_iop_platform_driver_shutdown(struct platform_device *pdev)
+{
+	struct sp_iop *iop = platform_get_drvdata(pdev);
+
+	sp_iop_standby_mode(iop);
+	mdelay(10);
+	sp_iop_shutdown(&pdev->dev, iop);
+}
+
+static const struct of_device_id sp_iop_of_match[] = {
+	{ .compatible = "sunplus,sp7021-iop" },
+	{ /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, sp_iop_of_match);
+
+static struct platform_driver sp_iop_platform_driver = {
+	.probe		= sp_iop_platform_driver_probe,
+	.shutdown	= sp_iop_platform_driver_shutdown,
+	.driver = {
+		.name	= "sunplus,sp7021-iop",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(sp_iop_of_match),
+	}
+};
+
+module_platform_driver(sp_iop_platform_driver);
+
+MODULE_AUTHOR("Tony Huang <tonyhuang.sunplus@gmail.com>");
+MODULE_DESCRIPTION("Sunplus IOP Driver");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH v4 2/2] misc: Add iop driver for Sunplus SP7021
  2021-12-16  2:35   ` Tony Huang
  (?)
@ 2021-12-16  7:36   ` Greg KH
       [not found]     ` <CAHpW4oRTcXq6k2o4cjFeHznZrR737947UPE60nWyPTRLPTR0Gw@mail.gmail.com>
  -1 siblings, 1 reply; 19+ messages in thread
From: Greg KH @ 2021-12-16  7:36 UTC (permalink / raw)
  To: Tony Huang
  Cc: robh+dt, devicetree, linuxkernel, derek.kiernan, dragan.cvetic,
	arnd, wells.lu, tonyhuang

On Thu, Dec 16, 2021 at 09:38:16AM +0800, Tony Huang wrote:
> IOP (IO Processor) embedded inside SP7021 which is used as
> Processor for I/O control, RTC wake-up and cooperation with
> CPU & PMC in power management purpose.
> The IOP core is DQ8051, so also named IOP8051,
> it supports dedicated JTAG debug pins which share with SP7021.
> In standby mode operation, the power spec reach 400uA.
> 
> Signed-off-by: Tony Huang <tonyhuang.sunplus@gmail.com>
> ---
> Changes in v4:
>  - Addressed comments from Arnd Bergmann.
>  - Addressed comments from Greg KH.
> 
>  Documentation/ABI/testing/sysfs-platform-soc@B |  22 ++
>  MAINTAINERS                                    |   2 +
>  drivers/misc/Kconfig                           |  12 +
>  drivers/misc/Makefile                          |   1 +
>  drivers/misc/sunplus_iop.c                     | 496 +++++++++++++++++++++++++
>  5 files changed, 533 insertions(+)
>  create mode 100644 Documentation/ABI/testing/sysfs-platform-soc@B
>  create mode 100644 drivers/misc/sunplus_iop.c
> 
> diff --git a/Documentation/ABI/testing/sysfs-platform-soc@B b/Documentation/ABI/testing/sysfs-platform-soc@B
> new file mode 100644
> index 0000000..17d838e
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-platform-soc@B
> @@ -0,0 +1,22 @@
> +What:		/sys/devices/platform/soc@B/9c000400.iop/sp_iop_state1
> +Date:		December 2021
> +KernelVersion:	5.15
> +Contact:	Tony Huang <tonyhuang.sunplus@gmail.com>
> +Description:
> +		SP7021 has three power states:S0, S1 and S3.
> +		S0:Default domain is on. IOP domain is on. AO domain is on.
> +		S1:Default domain is off. IOP domain is on. AO domain is on.
> +		S3:Default domain is off. IOP domain is off. AO domain is on.
> +		Read sysfs sp_iop_s1mode, system enter S1 mode.
> +
> +What:		/sys/devices/platform/soc@B/9c000400.iop/sp_iop_mode
> +Date:		December 2021
> +KernelVersion:	5.15
> +Contact:	Tony Huang <tonyhuang.sunplus@gmail.com>
> +Description:
> +		Operation mode of IOP is switched to standby mode by writing
> +		"1" to sysfs.
> +		Operation mode of IOP is switched to normal mode by writing
> +		"0" to sysfs.
> +
> +
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 071b5e6..614b7ff 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -17948,7 +17948,9 @@ F:	drivers/net/ethernet/dlink/sundance.c
>  SUNPLUS IOP DRIVER
>  M:	Tony Huang <tonyhuang.sunplus@gmail.com>
>  S:	Maintained
> +F:	Documentation/ABI/testing/sysfs-platform-soc@B
>  F:	Documentation/devicetree/bindings/misc/sunplu-iop.yaml
> +F:	drivers/misc/sunplus_iop.c
>  
>  SUPERH
>  M:	Yoshinori Sato <ysato@users.sourceforge.jp>
> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> index 0f5a49f..f19533b 100644
> --- a/drivers/misc/Kconfig
> +++ b/drivers/misc/Kconfig
> @@ -470,6 +470,18 @@ config HISI_HIKEY_USB
>  	  switching between the dual-role USB-C port and the USB-A host ports
>  	  using only one USB controller.
>  
> +config SUNPLUS_IOP
> +	tristate "Sunplus IOP support"
> +	default ARCH_SUNPLUS
> +	help
> +	  Sunplus I/O processor (8051) driver.
> +	  Processor for I/O control, RTC wake-up proceduce management,
> +	  and cooperation with CPU&PMC in power management.
> +	  Need Install DQ8051, The DQ8051 bin file generated by keil C.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called ad525x_dpot.
> +
>  source "drivers/misc/c2port/Kconfig"
>  source "drivers/misc/eeprom/Kconfig"
>  source "drivers/misc/cb710/Kconfig"
> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> index a086197..eafeab6 100644
> --- a/drivers/misc/Makefile
> +++ b/drivers/misc/Makefile
> @@ -52,6 +52,7 @@ obj-$(CONFIG_DW_XDATA_PCIE)	+= dw-xdata-pcie.o
>  obj-$(CONFIG_PCI_ENDPOINT_TEST)	+= pci_endpoint_test.o
>  obj-$(CONFIG_OCXL)		+= ocxl/
>  obj-$(CONFIG_BCM_VK)		+= bcm-vk/
> +obj-$(CONFIG_SUNPLUS_IOP)	+= sunplus_iop.o
>  obj-y				+= cardreader/
>  obj-$(CONFIG_PVPANIC)   	+= pvpanic/
>  obj-$(CONFIG_HABANA_AI)		+= habanalabs/
> diff --git a/drivers/misc/sunplus_iop.c b/drivers/misc/sunplus_iop.c
> new file mode 100644
> index 0000000..8c4c870
> --- /dev/null
> +++ b/drivers/misc/sunplus_iop.c
> @@ -0,0 +1,496 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * The IOP driver for Sunplus SP7021
> + *
> + * Copyright (C) 2021 Sunplus Technology Inc.
> + *
> + * All Rights Reserved.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/miscdevice.h>
> +#include <linux/of_platform.h>
> +#include <linux/firmware.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/of_address.h>
> +#include <linux/delay.h>
> +#include <linux/iopoll.h>
> +
> +enum IOP_Status_e {
> +	IOP_SUCCESS,                /* successful */
> +	IOP_ERR_IOP_BUSY,           /* IOP is busy */
> +};
> +
> +struct regs_moon0 {
> +	u32 stamp;         /* 00 */
> +	u32 clken[10];     /* 01~10 */
> +	u32 gclken[10];    /* 11~20 */
> +	u32 reset[10];     /* 21~30 */
> +	u32 sfg_cfg_mode;  /* 31 */
> +};
> +
> +struct regs_iop {
> +	u32 iop_control;/* 00 */
> +	u32 iop_reg1;/* 01 */
> +	u32 iop_bp;/* 02 */
> +	u32 iop_regsel;/* 03 */
> +	u32 iop_regout;/* 04 */
> +	u32 iop_reg5;/* 05 */
> +	u32 iop_resume_pcl;/* 06 */
> +	u32 iop_resume_pch;/* 07 */
> +	u32 iop_data0;/* 08 */
> +	u32 iop_data1;/* 09 */
> +	u32 iop_data2;/* 10 */
> +	u32 iop_data3;/* 11 */
> +	u32 iop_data4;/* 12 */
> +	u32 iop_data5;/* 13 */
> +	u32 iop_data6;/* 14 */
> +	u32 iop_data7;/* 15 */
> +	u32 iop_data8;/* 16 */
> +	u32 iop_data9;/* 17 */
> +	u32 iop_data10;/* 18 */
> +	u32 iop_data11;/* 19 */
> +	u32 iop_base_adr_l;/* 20 */
> +	u32 iop_base_adr_h;/* 21 */
> +	u32 memory_bridge_control;/* 22 */
> +	u32 iop_regmap_adr_l;/* 23 */
> +	u32 iop_regmap_adr_h;/* 24 */
> +	u32 iop_direct_adr;/* 25*/
> +	u32 reserved[6];/* 26~31 */
> +};
> +
> +struct regs_iop_pmc {
> +	u32 PMC_TIMER;/* 00 */
> +	u32 PMC_CTRL;/* 01 */
> +	u32 XTAL27M_PASSWORD_I;/* 02 */
> +	u32 XTAL27M_PASSWORD_II;/* 03 */
> +	u32 XTAL32K_PASSWORD_I;/* 04 */
> +	u32 XTAL32K_PASSWORD_II;/* 05 */
> +	u32 CLK27M_PASSWORD_I;/* 06 */
> +	u32 CLK27M_PASSWORD_II;/* 07 */
> +	u32 PMC_TIMER2;/* 08 */
> +	u32 reserved[23];/* 9~31 */
> +};
> +
> +#define NORMAL_CODE_MAX_SIZE 0X1000
> +#define STANDBY_CODE_MAX_SIZE 0x4000
> +struct sp_iop {
> +	struct miscdevice dev;			// iop device
> +	struct mutex write_lock;
> +	void __iomem *iop_regs;
> +	void __iomem *pmc_regs;
> +	void __iomem *moon0_regs;
> +	int irq;
> +	unsigned char iop_normal_code[NORMAL_CODE_MAX_SIZE];
> +	unsigned char iop_standby_code[STANDBY_CODE_MAX_SIZE];
> +	resource_size_t iop_mem_start;
> +	resource_size_t iop_mem_size;
> +	bool mode;
> +};
> +
> +static void sp_iop_normal_mode(struct sp_iop *iop)
> +{
> +	struct regs_iop *p_iop_reg = (struct regs_iop *)iop->iop_regs;
> +	struct regs_moon0 *p_moon0_reg = (struct regs_moon0 *)iop->moon0_regs;
> +	void __iomem *iop_kernel_base;
> +	unsigned int reg;
> +
> +	iop_kernel_base = ioremap(iop->iop_mem_start, NORMAL_CODE_MAX_SIZE);
> +	memset(iop_kernel_base, 0, NORMAL_CODE_MAX_SIZE);
> +	memcpy(iop_kernel_base, iop->iop_normal_code, NORMAL_CODE_MAX_SIZE);
> +
> +	writel(0x00100010, &p_moon0_reg->clken[0]);
> +
> +	reg = readl(&p_iop_reg->iop_control);
> +	reg |= 0x01;
> +	writel(reg, &p_iop_reg->iop_control);
> +
> +	reg = readl(&p_iop_reg->iop_control);
> +	reg &= ~(0x8000);
> +	writel(reg, &p_iop_reg->iop_control);
> +
> +	reg = readl(&p_iop_reg->iop_control);
> +	reg |= 0x0200;//disable watchdog event reset IOP
> +	writel(reg, &p_iop_reg->iop_control);
> +
> +	reg = (iop->iop_mem_start & 0xFFFF);
> +	writel(reg, &p_iop_reg->iop_base_adr_l);
> +	reg	= (iop->iop_mem_start >> 16);
> +	writel(reg, &p_iop_reg->iop_base_adr_h);
> +
> +	reg = readl(&p_iop_reg->iop_control);
> +	reg &= ~(0x01);
> +	writel(reg, &p_iop_reg->iop_control);
> +	iop->mode = 0;
> +}
> +
> +static void sp_iop_standby_mode(struct sp_iop *iop)
> +{
> +	struct regs_iop *p_iop_reg = (struct regs_iop *)iop->iop_regs;
> +	struct regs_moon0 *p_moon0_reg = (struct regs_moon0 *)iop->moon0_regs;
> +	void __iomem *iop_kernel_base;
> +	unsigned long reg;
> +
> +	iop_kernel_base = ioremap(iop->iop_mem_start, STANDBY_CODE_MAX_SIZE);
> +	memset(iop_kernel_base, 0, STANDBY_CODE_MAX_SIZE);
> +	memcpy(iop_kernel_base, iop->iop_standby_code, STANDBY_CODE_MAX_SIZE);
> +
> +	writel(0x00100010, &p_moon0_reg->clken[0]);
> +
> +	reg = readl(&p_iop_reg->iop_control);
> +	reg |= 0x01;
> +	writel(reg, &p_iop_reg->iop_control);
> +
> +	reg = readl(&p_iop_reg->iop_control);
> +	reg &= ~(0x8000);
> +	writel(reg, &p_iop_reg->iop_control);
> +
> +	reg = readl(&p_iop_reg->iop_control);
> +	reg |= 0x0200;//disable watchdog event reset IOP
> +	writel(reg, &p_iop_reg->iop_control);
> +
> +	reg = (iop->iop_mem_start & 0xFFFF);
> +	writel(reg, &p_iop_reg->iop_base_adr_l);
> +	reg = (iop->iop_mem_start >> 16);
> +	writel(reg, &p_iop_reg->iop_base_adr_h);
> +
> +	reg = readl(&p_iop_reg->iop_control);
> +	reg &= ~(0x01);
> +	writel(reg, &p_iop_reg->iop_control);
> +	iop->mode = 1;
> +}
> +
> +#define IOP_READY	0x4
> +#define RISC_READY	0x8
> +static int sp_iop_shutdown(struct device *dev, struct sp_iop *iop)
> +{
> +	struct regs_iop *p_iop_reg = (struct regs_iop *)iop->iop_regs;
> +	struct regs_moon0 *p_moon0_reg = (struct regs_moon0 *)iop->moon0_regs;
> +	struct regs_iop_pmc *p_iop_pmc_reg = (struct regs_iop_pmc *)iop->pmc_regs;
> +	unsigned int reg;
> +	int ret, value;
> +
> +	writel(0x00100010, &p_moon0_reg->clken[0]);
> +
> +	reg = readl(&p_iop_reg->iop_control);
> +	reg &= ~(0x8000);
> +	writel(reg, &p_iop_reg->iop_control);
> +
> +	reg = readl(&p_iop_reg->iop_control);
> +	reg |= 0x1;
> +	writel(reg, &p_iop_reg->iop_control);
> +
> +	//PMC set
> +	writel(0x00010001, &p_iop_pmc_reg->PMC_TIMER);
> +	reg = readl(&p_iop_pmc_reg->PMC_CTRL);
> +	reg |= 0x23;// disable system reset PMC, enalbe power down 27M, enable gating 27M
> +	writel(reg, &p_iop_pmc_reg->PMC_CTRL);
> +
> +	writel(0x55aa00ff, &p_iop_pmc_reg->XTAL27M_PASSWORD_I);
> +	writel(0x00ff55aa, &p_iop_pmc_reg->XTAL27M_PASSWORD_II);
> +	writel(0xaa00ff55, &p_iop_pmc_reg->XTAL32K_PASSWORD_I);
> +	writel(0xff55aa00, &p_iop_pmc_reg->XTAL32K_PASSWORD_II);
> +	writel(0xaaff0055, &p_iop_pmc_reg->CLK27M_PASSWORD_I);
> +	writel(0x5500aaff, &p_iop_pmc_reg->CLK27M_PASSWORD_II);
> +	writel(0x01000100, &p_iop_pmc_reg->PMC_TIMER2);
> +
> +	//IOP Hardware IP reset
> +	reg = readl(&p_moon0_reg->reset[0]);
> +	reg |= 0x10;
> +	writel(reg, (&p_moon0_reg->reset[0]));
> +	reg &= ~(0x10);
> +	writel(reg, (&p_moon0_reg->reset[0]));
> +
> +	writel(0x00ff0085, (iop->moon0_regs + 32 * 4 * 1 + 4 * 1));
> +
> +	reg = readl(iop->moon0_regs + 32 * 4 * 1 + 4 * 2);
> +	reg |= 0x08000800;
> +	writel(reg, (iop->moon0_regs + 32 * 4 * 1 + 4 * 2));
> +
> +	reg = readl(&p_iop_reg->iop_control);
> +	reg |= 0x0200;//disable watchdog event reset IOP
> +	writel(reg, &p_iop_reg->iop_control);
> +
> +	reg = (iop->iop_mem_start & 0xFFFF);
> +	writel(reg, &p_iop_reg->iop_base_adr_l);
> +	reg = (iop->iop_mem_start >> 16);
> +	writel(reg, &p_iop_reg->iop_base_adr_h);
> +
> +	reg = readl(&p_iop_reg->iop_control);
> +	reg &= ~(0x01);
> +	writel(reg, &p_iop_reg->iop_control);
> +
> +	ret = readl_poll_timeout(&p_iop_reg->iop_data2, value,
> +				 (value & IOP_READY) == IOP_READY, 1000, 10000);
> +	if (ret) {
> +		dev_err(dev, "timed out\n");
> +		return ret;
> +	}
> +
> +	writel(RISC_READY, &p_iop_reg->iop_data2);
> +	writel(0x00, &p_iop_reg->iop_data5);
> +	writel(0x60, &p_iop_reg->iop_data6);
> +
> +	ret = readl_poll_timeout(&p_iop_reg->iop_data7, value,
> +				 value == 0xaaaa, 1000, 10000);
> +	if (ret) {
> +		dev_err(dev, "timed out\n");
> +		return ret;
> +	}
> +
> +	writel(0xdd, &p_iop_reg->iop_data1);//8051 bin file call Ultra low function.
> +	mdelay(10);
> +	return 0;
> +}
> +
> +static int sp_iop_s1mode(struct device *dev, struct sp_iop *iop)
> +{
> +	struct regs_iop *p_iop_reg = (struct regs_iop *)iop->iop_regs;
> +	int ret, value;
> +
> +	ret = readl_poll_timeout(&p_iop_reg->iop_data2, value,
> +				 (value & IOP_READY) == IOP_READY, 1000, 10000);
> +	if (ret) {
> +		dev_err(dev, "timed out\n");
> +		return ret;
> +	}
> +
> +	writel(RISC_READY, &p_iop_reg->iop_data2);
> +	writel(0x00, &p_iop_reg->iop_data5);
> +	writel(0x60, &p_iop_reg->iop_data6);
> +
> +	ret = readl_poll_timeout(&p_iop_reg->iop_data7, value,
> +				 value == 0xaaaa, 1000, 10000);
> +	if (ret) {
> +		dev_err(dev, "timed out\n");
> +		return ret;
> +	}
> +
> +	writel(0xee, &p_iop_reg->iop_data1);//8051 bin file call S1_mode function.
> +	mdelay(10);
> +	return 0;
> +}
> +
> +static ssize_t sp_iop_state1_show(struct device *dev, struct device_attribute *attr, char *buf)
> +{
> +	struct sp_iop *iop = dev_get_drvdata(dev);
> +	ssize_t len = 0;
> +
> +	sp_iop_standby_mode(iop);
> +	mdelay(10);
> +	sp_iop_s1mode(dev, iop);
> +	return len;
> +}

This function is not showing anything.

> +
> +static ssize_t sp_iop_state1_store(struct device *dev, struct device_attribute *attr,
> +				   const char *buf, size_t count)
> +{
> +	ssize_t len = 0;
> +
> +	return len;
> +}

Why do you have an empty store function?

> +
> +static ssize_t sp_iop_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
> +{
> +	struct sp_iop *iop = dev_get_drvdata(dev);
> +	ssize_t len = 0;
> +
> +	if (iop->mode == 0)
> +		dev_info(dev, "iop_normal_mode\n");
> +	else if (iop->mode == 1)
> +		dev_info(dev, "iop_standby_mode\n");
> +	return len;
> +}

Again, this is not returning anything in sysfs.

How was this tested???

> +static ssize_t sp_iop_mode_store(struct device *dev, struct device_attribute *attr,
> +				 const char *buf, size_t count)
> +{
> +	struct sp_iop *iop = dev_get_drvdata(dev);
> +	unsigned char ret = count;
> +
> +	if (buf[0] == '0') {
> +		sp_iop_normal_mode(iop);
> +		dev_info(dev, "Switch to normal mode.\n");
> +	} else if (buf[0] == '1') {
> +		sp_iop_standby_mode(iop);
> +		dev_info(dev, "Switch to standby mode.\n");
> +	} else {
> +		dev_info(dev, "echo 0 or 1 mode\n");
> +		dev_info(dev, "0:normal mode\n");
> +		dev_info(dev, "1:standby mode\n");
> +	}
> +	return ret;
> +}

dev_info() is NOT for stuff like this at all.  You do not display a
"help" file or anything else like that in this way.

Please use sysfs correctly.

Also, there is a function to read 1/0 from sysfs, please use that.

> +
> +static DEVICE_ATTR_RW(sp_iop_state1);
> +static DEVICE_ATTR_RW(sp_iop_mode);

These are not really read/write files, please make them the correct
mode.

> +static int  sp_iop_get_normal_code(struct device *dev, struct sp_iop *iop)
> +{
> +	const struct firmware *fw;
> +	static const char file[] = "normal.bin";
> +	unsigned int err, i;
> +
> +	err = request_firmware(&fw, file, dev);
> +	if (err) {
> +		dev_err(dev, "get bin file error\n");
> +		return err;
> +	}
> +
> +	for (i = 0; i < NORMAL_CODE_MAX_SIZE; i++) {
> +		char temp;
> +
> +		temp = fw->data[i];
> +		iop->iop_normal_code[i] = temp;
> +	}
> +	release_firmware(fw);
> +	return err;
> +}
> +
> +static int  sp_iop_get_standby_code(struct device *dev, struct sp_iop *iop)
> +{
> +	const struct firmware *fw;
> +	static const char file[] = "standby.bin";
> +	unsigned int err, i;
> +
> +	err = request_firmware(&fw, file, dev);
> +	if (err) {
> +		dev_err(dev, "get bin file error\n");
> +		return err;
> +	}
> +
> +	for (i = 0; i < STANDBY_CODE_MAX_SIZE; i++) {
> +		char temp;
> +
> +		temp = fw->data[i];
> +		iop->iop_standby_code[i] = temp;
> +	}
> +	release_firmware(fw);
> +	return err;
> +}
> +
> +static int sp_iop_get_resources(struct platform_device *pdev, struct sp_iop *p_sp_iop_info)
> +{
> +	struct resource *r;
> +
> +	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iop");
> +	p_sp_iop_info->iop_regs = devm_ioremap_resource(&pdev->dev, r);
> +	if (IS_ERR(p_sp_iop_info->iop_regs)) {
> +		dev_err(&pdev->dev, "ioremap fail\n");
> +		return PTR_ERR(p_sp_iop_info->iop_regs);
> +	}
> +
> +	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iop_pmc");
> +	p_sp_iop_info->pmc_regs = devm_ioremap_resource(&pdev->dev, r);
> +	if (IS_ERR(p_sp_iop_info->pmc_regs)) {
> +		dev_err(&pdev->dev, "ioremap fail\n");
> +		return PTR_ERR(p_sp_iop_info->pmc_regs);
> +	}
> +
> +	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "moon0");
> +	p_sp_iop_info->moon0_regs = devm_ioremap_resource(&pdev->dev, r);
> +	if (IS_ERR(p_sp_iop_info->moon0_regs)) {
> +		dev_err(&pdev->dev, "ioremap fail\n");
> +		return PTR_ERR(p_sp_iop_info->moon0_regs);
> +	}
> +	return IOP_SUCCESS;
> +}
> +
> +static int sp_iop_platform_driver_probe(struct platform_device *pdev)
> +{
> +	int ret = -ENXIO;
> +	int rc;
> +	struct sp_iop *iop;
> +	struct device_node *memnp;
> +	struct resource mem_res;
> +
> +	iop = devm_kzalloc(&pdev->dev, sizeof(struct sp_iop), GFP_KERNEL);
> +	if (!iop) {
> +		ret	= -ENOMEM;
> +		goto fail_kmalloc;
> +	}
> +	/* init */
> +	mutex_init(&iop->write_lock);
> +	/* register device */
> +	iop->dev.name  = "sp_iop";
> +	iop->dev.minor = MISC_DYNAMIC_MINOR;
> +	ret = misc_register(&iop->dev);

Why do you need a misc device at all?  What are you using it for?  You
do not have any file handling at all.

This driver seems very very odd, are you SURE you are actually using it?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4 2/2] misc: Add iop driver for Sunplus SP7021
  2021-12-16  2:35   ` Tony Huang
  (?)
  (?)
@ 2021-12-16  8:01   ` Arnd Bergmann
       [not found]     ` <CAHpW4oQmtd-gG1HYZT-Dk=QAvXKsKebzcOaVtrEeoujwoL9zSg@mail.gmail.com>
  -1 siblings, 1 reply; 19+ messages in thread
From: Arnd Bergmann @ 2021-12-16  8:01 UTC (permalink / raw)
  To: Tony Huang
  Cc: Rob Herring, DTML, linuxkernel, Derek Kiernan, Dragan Cvetic,
	Arnd Bergmann, gregkh, Wells Lu 呂芳騰,
	tonyhuang

On Thu, Dec 16, 2021 at 2:38 AM Tony Huang <tonyhuang.sunplus@gmail.com> wrote:
>
> IOP (IO Processor) embedded inside SP7021 which is used as
> Processor for I/O control, RTC wake-up and cooperation with
> CPU & PMC in power management purpose.
> The IOP core is DQ8051, so also named IOP8051,
> it supports dedicated JTAG debug pins which share with SP7021.
> In standby mode operation, the power spec reach 400uA.
>
> Signed-off-by: Tony Huang <tonyhuang.sunplus@gmail.com>
> ---
> Changes in v4:
>  - Addressed comments from Arnd Bergmann.

I don't think you did: I asked you specifically to add code to interact with
the existing in-kernel interfaces to use the functionality provided by the
device. Pick any (at least two) subsystems and add support, but leave
out any custom user space interfaces (miscdevice, debugfs, sysfs, ...)
for the moment.

    Arnd

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4 2/2] misc: Add iop driver for Sunplus SP7021
  2021-12-16  2:35   ` Tony Huang
@ 2021-12-16 18:31     ` kernel test robot
  -1 siblings, 0 replies; 19+ messages in thread
From: kernel test robot @ 2021-12-16 18:31 UTC (permalink / raw)
  To: Tony Huang, robh+dt, devicetree, linuxkernel, derek.kiernan,
	dragan.cvetic, arnd, gregkh
  Cc: llvm, kbuild-all, wells.lu, tonyhuang, Tony Huang

Hi Tony,

I love your patch! Perhaps something to improve:

[auto build test WARNING on char-misc/char-misc-testing]
[also build test WARNING on robh/for-next soc/for-next linus/master v5.16-rc5 next-20211215]
[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/Tony-Huang/Add-iop-driver-for-Sunplus-SP7021/20211216-093835
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git af40d16042d674442db8cf5fd654fabcd45fea44
config: hexagon-randconfig-r011-20211216 (https://download.01.org/0day-ci/archive/20211217/202112170208.xrabSwww-lkp@intel.com/config)
compiler: clang version 14.0.0 (https://github.com/llvm/llvm-project dd245bab9fbb364faa1581e4f92ba3119a872fba)
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/916d31a2d28c9f0ad7d7073d7943b9cf656038f5
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Tony-Huang/Add-iop-driver-for-Sunplus-SP7021/20211216-093835
        git checkout 916d31a2d28c9f0ad7d7073d7943b9cf656038f5
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=hexagon SHELL=/bin/bash drivers/misc/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/misc/sunplus_iop.c:475:34: warning: unused variable 'sp_iop_of_match' [-Wunused-const-variable]
   static const struct of_device_id sp_iop_of_match[] = {
                                    ^
   1 warning generated.


vim +/sp_iop_of_match +475 drivers/misc/sunplus_iop.c

   474	
 > 475	static const struct of_device_id sp_iop_of_match[] = {
   476		{ .compatible = "sunplus,sp7021-iop" },
   477		{ /* sentinel */ },
   478	};
   479	

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

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4 2/2] misc: Add iop driver for Sunplus SP7021
@ 2021-12-16 18:31     ` kernel test robot
  0 siblings, 0 replies; 19+ messages in thread
From: kernel test robot @ 2021-12-16 18:31 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 2333 bytes --]

Hi Tony,

I love your patch! Perhaps something to improve:

[auto build test WARNING on char-misc/char-misc-testing]
[also build test WARNING on robh/for-next soc/for-next linus/master v5.16-rc5 next-20211215]
[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/Tony-Huang/Add-iop-driver-for-Sunplus-SP7021/20211216-093835
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git af40d16042d674442db8cf5fd654fabcd45fea44
config: hexagon-randconfig-r011-20211216 (https://download.01.org/0day-ci/archive/20211217/202112170208.xrabSwww-lkp(a)intel.com/config)
compiler: clang version 14.0.0 (https://github.com/llvm/llvm-project dd245bab9fbb364faa1581e4f92ba3119a872fba)
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/916d31a2d28c9f0ad7d7073d7943b9cf656038f5
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Tony-Huang/Add-iop-driver-for-Sunplus-SP7021/20211216-093835
        git checkout 916d31a2d28c9f0ad7d7073d7943b9cf656038f5
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=hexagon SHELL=/bin/bash drivers/misc/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/misc/sunplus_iop.c:475:34: warning: unused variable 'sp_iop_of_match' [-Wunused-const-variable]
   static const struct of_device_id sp_iop_of_match[] = {
                                    ^
   1 warning generated.


vim +/sp_iop_of_match +475 drivers/misc/sunplus_iop.c

   474	
 > 475	static const struct of_device_id sp_iop_of_match[] = {
   476		{ .compatible = "sunplus,sp7021-iop" },
   477		{ /* sentinel */ },
   478	};
   479	

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

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4 1/2] dt-binding: misc: Add iop yaml file for Sunplus SP7021
  2021-12-16  2:34   ` Tony Huang
  (?)
@ 2021-12-16 20:33   ` Rob Herring
  -1 siblings, 0 replies; 19+ messages in thread
From: Rob Herring @ 2021-12-16 20:33 UTC (permalink / raw)
  To: Tony Huang
  Cc: linuxkernel, arnd, devicetree, dragan.cvetic, gregkh, wells.lu,
	robh+dt, tonyhuang, derek.kiernan

On Thu, 16 Dec 2021 09:38:15 +0800, Tony Huang wrote:
> Add iop yaml file for Sunplus SP7021
> 
> Signed-off-by: Tony Huang <tonyhuang.sunplus@gmail.com>
> ---
> Changes in v4:
>  - Addressed comments from Rob Herring.
> 
>  .../devicetree/bindings/misc/sunplus-iop.yaml      | 65 ++++++++++++++++++++++
>  MAINTAINERS                                        |  5 ++
>  2 files changed, 70 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/misc/sunplus-iop.yaml
> 

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

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4 2/2] misc: Add iop driver for Sunplus SP7021
       [not found]       ` <316c16afbff74160b07bd74444f3b8e1@sphcmbx02.sunplus.com.tw>
@ 2021-12-17  6:54         ` gregkh
  2021-12-17  8:12         ` Arnd Bergmann
  1 sibling, 0 replies; 19+ messages in thread
From: gregkh @ 2021-12-17  6:54 UTC (permalink / raw)
  To: Tony Huang 黃懷厚
  Cc: Arnd Bergmann, DTML, Linux Kernel Mailing List, Derek Kiernan,
	Dragan Cvetic, Rob Herring, Wells Lu 呂芳騰,
	黃懷厚

On Fri, Dec 17, 2021 at 02:44:43AM +0000, Tony Huang 黃懷厚 wrote:
> Dear Arnd:
> 
> On Thu, Dec 16, 2021 at 2:38 AM Tony Huang <tonyhuang.sunplus@gmail.com<mailto:tonyhuang.sunplus@gmail.com>> wrote:
> >>
> >> IOP (IO Processor) embedded inside SP7021 which is used as
> >> Processor for I/O control, RTC wake-up and cooperation with
> >> CPU & PMC in power management purpose.
> >> The IOP core is DQ8051, so also named IOP8051,
> >> it supports dedicated JTAG debug pins which share with SP7021.
> >> In standby mode operation, the power spec reach 400uA.
> >>
> >> Signed-off-by: Tony Huang <tonyhuang.sunplus@gmail.com<mailto:tonyhuang.sunplus@gmail.com>>
> >> ---
> >> Changes in v4:
> >>  - Addressed comments from Arnd Bergmann.
> 
> >I don't think you did: I asked you specifically to add code to interact with
> >the existing in-kernel interfaces to use the functionality provided by the
> >device. Pick any (at least two) subsystems and add support, but leave
> >out any custom user space interfaces (miscdevice, debugfs, sysfs, ...)
> >for the moment.
> 
> 1. IOP can run sp_iop_platform_driver_shudown() through the poweroff command and the kernel. Perform system power-off actions.
> 2. Wake up the system by relying on the 8051 internal RTC wake-up mechanism and external GPIO input signals to wake up.
> 3.So you ask me to control IOP(8051) through file_operations, not through DEVICE_ATTR

Your DEVICE_ATTR() calls did not do anything normal, they did not print
out or accept data through sysfs at all.  That is not ok.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 19+ messages in thread

* RE: [PATCH v4 2/2] misc: Add iop driver for Sunplus SP7021
       [not found]     ` <CAHpW4oRTcXq6k2o4cjFeHznZrR737947UPE60nWyPTRLPTR0Gw@mail.gmail.com>
@ 2021-12-17  8:10       ` Tony Huang 黃懷厚
  2021-12-17  8:24         ` gregkh
  0 siblings, 1 reply; 19+ messages in thread
From: Tony Huang 黃懷厚 @ 2021-12-17  8:10 UTC (permalink / raw)
  To: gregkh
  Cc: Wells Lu 呂芳騰,
	Rob Herring, DTML, Linux Kernel Mailing List, Derek Kiernan,
	Dragan Cvetic, Arnd Bergmann, 黃懷厚

Dear Gregkh:

> > +
> > +static ssize_t sp_iop_state1_show(struct device *dev, struct
> > +device_attribute *attr, char *buf) {
> > +     struct sp_iop *iop = dev_get_drvdata(dev);
> > +     ssize_t len = 0;
> > +
> > +     sp_iop_standby_mode(iop);
> > +     mdelay(10);
> > +     sp_iop_s1mode(dev, iop);
> > +     return len;
> > +}
> 
> This function is not showing anything.
> 
							
The purpose of this function:							
SP7021 has three power states:S0, S1 and S3.							
S0:Default domain is on. IOP domain is on. AO domain is on.							
S1:Default domain is off. IOP domain is on. AO domain is on.							
S3:Default domain is off. IOP domain is off. AO domain is on.							
System enter S1 mode when read sysfs sp_iop_state1,							
							
I hope to provide users with the ability to enter S1 mode by themselves.							
So I cannot use DEVICE_ATTR. I should use file_operations method.							

> > +
> > +static ssize_t sp_iop_state1_store(struct device *dev, struct
> device_attribute *attr,
> > +                                const char *buf, size_t count) {
> > +     ssize_t len = 0;
> > +
> > +     return len;
> > +}
> 
> Why do you have an empty store function?
> 
> > +
> > +static ssize_t sp_iop_mode_show(struct device *dev, struct
> > +device_attribute *attr, char *buf) {
> > +     struct sp_iop *iop = dev_get_drvdata(dev);
> > +     ssize_t len = 0;
> > +
> > +     if (iop->mode == 0)
> > +             dev_info(dev, "iop_normal_mode\n");
> > +     else if (iop->mode == 1)
> > +             dev_info(dev, "iop_standby_mode\n");
> > +     return len;
> > +}
> 
> Again, this is not returning anything in sysfs.
> 
> How was this tested???
> 

I type "echo 0 > mode". Device driver will load normal .bin for 8051 in sp_iop_mode_store().								
I type "echo 1 > mode". Device driver will load standby.bin for 8051 in sp_iop_mode_store().								
I type "cat mode". I can know whether the current bin code is normal or standby in 8051.								
								
I hope to provide users with the ability to switch bin files by themselves.								
So I need to use file_operations to achieve my goal.								
Not using DEVICE_ATTR.	Right?								


> > +static ssize_t sp_iop_mode_store(struct device *dev, struct device_attribute
> *attr,
> > +                              const char *buf, size_t count) {
> > +     struct sp_iop *iop = dev_get_drvdata(dev);
> > +     unsigned char ret = count;
> > +
> > +     if (buf[0] == '0') {
> > +             sp_iop_normal_mode(iop);
> > +             dev_info(dev, "Switch to normal mode.\n");
> > +     } else if (buf[0] == '1') {
> > +             sp_iop_standby_mode(iop);
> > +             dev_info(dev, "Switch to standby mode.\n");
> > +     } else {
> > +             dev_info(dev, "echo 0 or 1 mode\n");
> > +             dev_info(dev, "0:normal mode\n");
> > +             dev_info(dev, "1:standby mode\n");
> > +     }
> > +     return ret;
> > +}
> 
> dev_info() is NOT for stuff like this at all.  You do not display a "help" file or
> anything else like that in this way.
> 
> Please use sysfs correctly.
> 
> Also, there is a function to read 1/0 from sysfs, please use that.
> 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4 2/2] misc: Add iop driver for Sunplus SP7021
       [not found]       ` <316c16afbff74160b07bd74444f3b8e1@sphcmbx02.sunplus.com.tw>
  2021-12-17  6:54         ` gregkh
@ 2021-12-17  8:12         ` Arnd Bergmann
  2021-12-17 10:24           ` Tony Huang 黃懷厚
                             ` (2 more replies)
  1 sibling, 3 replies; 19+ messages in thread
From: Arnd Bergmann @ 2021-12-17  8:12 UTC (permalink / raw)
  To: Tony Huang 黃懷厚
  Cc: Arnd Bergmann, DTML, Linux Kernel Mailing List, Derek Kiernan,
	Dragan Cvetic, Rob Herring, gregkh,
	Wells Lu 呂芳騰, 黃懷厚

On Fri, Dec 17, 2021 at 3:44 AM Tony Huang 黃懷厚 <tony.huang@sunplus.com> wrote:
>
> Dear Arnd:
>
>
>
> On Thu, Dec 16, 2021 at 2:38 AM Tony Huang <tonyhuang.sunplus@gmail.com> wrote:
> >>
> >> IOP (IO Processor) embedded inside SP7021 which is used as
> >> Processor for I/O control, RTC wake-up and cooperation with
> >> CPU & PMC in power management purpose.
> >> The IOP core is DQ8051, so also named IOP8051,
> >> it supports dedicated JTAG debug pins which share with SP7021.
> >> In standby mode operation, the power spec reach 400uA.
> >>
> >> Signed-off-by: Tony Huang <tonyhuang.sunplus@gmail.com>
> >> ---
> >> Changes in v4:
> >>  - Addressed comments from Arnd Bergmann.
>
> >I don't think you did: I asked you specifically to add code to interact with
> >the existing in-kernel interfaces to use the functionality provided by the
> >device. Pick any (at least two) subsystems and add support, but leave
> >out any custom user space interfaces (miscdevice, debugfs, sysfs, ...)
> >for the moment.
>
>
>
> 1. IOP can run sp_iop_platform_driver_shudown() through the poweroff command
> and the kernel. Perform system power-off actions.

Do you mean that this method a) cleanly shuts down the iop before the
system is powered down, or b) the driver_shutdown() callback is used to
initiate the powerdown of the system itself?

In case of a) I would not count that as exposing functionality, what you do here
is just part of any driver. If instead you are trying to use b), this
is the wrong
way of doing it, see drivers/power/reset/ for examples of how to do it right.

> 2. Wake up the system by relying on the 8051 internal RTC wake-up mechanism
> and external GPIO input signals to wake up.

I think those should be exposed with drivers/rtc for the RTC and drivers/gpio/
for the GPIO driver, and then you can use the device tree to configure which
GPIO to use as a wakeup and how it's connected to the RTC.

> 3.So you ask me to control IOP(8051) through file_operations, not through DEVICE_ATTR

No, neither of them. Use the appropriate drivers/*/ subsystems for any
functionality that has an existing subsystem. If there is something that the iop
does that does not yet have a subsystem, that requires a more thorough design
discussion for creating a new user interface, ideally in a hardware-independent
way. You should not start with that until all the normal features (rtc, wakeup,
suspend, gpio, ...) are supported.

       Arnd

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4 2/2] misc: Add iop driver for Sunplus SP7021
  2021-12-17  8:10       ` Tony Huang 黃懷厚
@ 2021-12-17  8:24         ` gregkh
  2021-12-20  8:35           ` Tony Huang 黃懷厚
  0 siblings, 1 reply; 19+ messages in thread
From: gregkh @ 2021-12-17  8:24 UTC (permalink / raw)
  To: Tony Huang 黃懷厚
  Cc: Wells Lu 呂芳騰,
	Rob Herring, DTML, Linux Kernel Mailing List, Derek Kiernan,
	Dragan Cvetic, Arnd Bergmann, 黃懷厚

On Fri, Dec 17, 2021 at 08:10:45AM +0000, Tony Huang 黃懷厚 wrote:
> Dear Gregkh:
> 
> > > +
> > > +static ssize_t sp_iop_state1_show(struct device *dev, struct
> > > +device_attribute *attr, char *buf) {
> > > +     struct sp_iop *iop = dev_get_drvdata(dev);
> > > +     ssize_t len = 0;
> > > +
> > > +     sp_iop_standby_mode(iop);
> > > +     mdelay(10);
> > > +     sp_iop_s1mode(dev, iop);
> > > +     return len;
> > > +}
> > 
> > This function is not showing anything.
> > 
> 							
> The purpose of this function:							
> SP7021 has three power states:S0, S1 and S3.							
> S0:Default domain is on. IOP domain is on. AO domain is on.							
> S1:Default domain is off. IOP domain is on. AO domain is on.							
> S3:Default domain is off. IOP domain is off. AO domain is on.							
> System enter S1 mode when read sysfs sp_iop_state1,							

That is not what sysfs is for, sorry.

> I hope to provide users with the ability to enter S1 mode by themselves.							
> So I cannot use DEVICE_ATTR. I should use file_operations method.							

No, please use the normal power management callbacks for your driver
that all other drivers use in the kernel.  There is nothing special
about this one driver to warrant a totally new user/kernel api for it.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 19+ messages in thread

* RE: [PATCH v4 2/2] misc: Add iop driver for Sunplus SP7021
  2021-12-17  8:12         ` Arnd Bergmann
@ 2021-12-17 10:24           ` Tony Huang 黃懷厚
  2021-12-17 11:03           ` Tony Huang 黃懷厚
  2021-12-22  7:20           ` Tony Huang 黃懷厚
  2 siblings, 0 replies; 19+ messages in thread
From: Tony Huang 黃懷厚 @ 2021-12-17 10:24 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: DTML, Linux Kernel Mailing List, Derek Kiernan, Dragan Cvetic,
	Rob Herring, gregkh, Wells Lu 呂芳騰,
	黃懷厚

Dear Arnd:

> > >>
> > >> IOP (IO Processor) embedded inside SP7021 which is used as
> > >> Processor for I/O control, RTC wake-up and cooperation with CPU &
> > >> PMC in power management purpose.
> > >> The IOP core is DQ8051, so also named IOP8051, it supports
> > >> dedicated JTAG debug pins which share with SP7021.
> > >> In standby mode operation, the power spec reach 400uA.
> > >>
> > >> Signed-off-by: Tony Huang <tonyhuang.sunplus@gmail.com>
> > >> ---
> > >> Changes in v4:
> > >>  - Addressed comments from Arnd Bergmann.
> >
> > >I don't think you did: I asked you specifically to add code to
> > >interact with the existing in-kernel interfaces to use the
> > >functionality provided by the device. Pick any (at least two)
> > >subsystems and add support, but leave out any custom user space
> > >interfaces (miscdevice, debugfs, sysfs, ...) for the moment.
> >
> >
> >
> > 1. IOP can run sp_iop_platform_driver_shudown() through the poweroff
> > command and the kernel. Perform system power-off actions.
> 
> Do you mean that this method a) cleanly shuts down the iop before the system
> is powered down, or b) the driver_shutdown() callback is used to initiate the
> powerdown of the system itself?
> 
> In case of a) I would not count that as exposing functionality, what you do here
> is just part of any driver. If instead you are trying to use b), this is the wrong
> way of doing it, see drivers/power/reset/ for examples of how to do it right.
> 
> > 2. Wake up the system by relying on the 8051 internal RTC wake-up
> > mechanism and external GPIO input signals to wake up.
> 
> I think those should be exposed with drivers/rtc for the RTC and drivers/gpio/
> for the GPIO driver, and then you can use the device tree to configure which
> GPIO to use as a wakeup and how it's connected to the RTC.
> 

SP7021:
The system is powered off. But 8051 still has power.
The 8051 has a register to control the power-on and power-off of the system.
8051 has RTC register. When the time is up, 8051 powers on the system.
The 8051 can detect GPIO0~7 pins, and GPIO pin high/low can be used as a power-on judgment mechanism for the system.

> > 3.So you ask me to control IOP(8051) through file_operations, not
> > through DEVICE_ATTR
> 
> No, neither of them. Use the appropriate drivers/*/ subsystems for any
> functionality that has an existing subsystem. If there is something that the iop
> does that does not yet have a subsystem, that requires a more thorough design
> discussion for creating a new user interface, ideally in a hardware-independent
> way. You should not start with that until all the normal features (rtc, wakeup,
> suspend, gpio, ...) are supported.
> 
>        Arnd

^ permalink raw reply	[flat|nested] 19+ messages in thread

* RE: [PATCH v4 2/2] misc: Add iop driver for Sunplus SP7021
  2021-12-17  8:12         ` Arnd Bergmann
  2021-12-17 10:24           ` Tony Huang 黃懷厚
@ 2021-12-17 11:03           ` Tony Huang 黃懷厚
  2021-12-22  7:20           ` Tony Huang 黃懷厚
  2 siblings, 0 replies; 19+ messages in thread
From: Tony Huang 黃懷厚 @ 2021-12-17 11:03 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: DTML, Linux Kernel Mailing List, Derek Kiernan, Dragan Cvetic,
	Rob Herring, gregkh, Wells Lu 呂芳騰,
	黃懷厚

Dear Arnd:
> 
> > 3.So you ask me to control IOP(8051) through file_operations, not
> > through DEVICE_ATTR
> 
> No, neither of them. Use the appropriate drivers/*/ subsystems for any
> functionality that has an existing subsystem. If there is something that the iop
> does that does not yet have a subsystem, that requires a more thorough design
> discussion for creating a new user interface, ideally in a hardware-independent
> way. You should not start with that until all the normal features (rtc, wakeup,
> suspend, gpio, ...) are supported.
> 

IOP(8051) has 12 bytes Mailbox registers, which can be read and written by IOP(8051) and System, used to exchange information with the system, faster than shared memory.

Thanks			

^ permalink raw reply	[flat|nested] 19+ messages in thread

* RE: [PATCH v4 2/2] misc: Add iop driver for Sunplus SP7021
  2021-12-17  8:24         ` gregkh
@ 2021-12-20  8:35           ` Tony Huang 黃懷厚
  0 siblings, 0 replies; 19+ messages in thread
From: Tony Huang 黃懷厚 @ 2021-12-20  8:35 UTC (permalink / raw)
  To: gregkh
  Cc: Wells Lu 呂芳騰,
	Rob Herring, DTML, Linux Kernel Mailing List, Derek Kiernan,
	Dragan Cvetic, Arnd Bergmann, 黃懷厚

Dear grepkh:

> > > > +
> > > > +static ssize_t sp_iop_state1_show(struct device *dev, struct
> > > > +device_attribute *attr, char *buf) {
> > > > +     struct sp_iop *iop = dev_get_drvdata(dev);
> > > > +     ssize_t len = 0;
> > > > +
> > > > +     sp_iop_standby_mode(iop);
> > > > +     mdelay(10);
> > > > +     sp_iop_s1mode(dev, iop);
> > > > +     return len;
> > > > +}
> > >
> > > This function is not showing anything.
> > >
> >
> > The purpose of this function:
> > SP7021 has three power states:S0, S1 and S3.
> > S0:Default domain is on. IOP domain is on. AO domain is on.
> 
> > S1:Default domain is off. IOP domain is on. AO domain is on.
> 
> > S3:Default domain is off. IOP domain is off. AO domain is on.
> 
> > System enter S1 mode when read sysfs sp_iop_state1,
> 
> 
> That is not what sysfs is for, sorry.

I will modify this function.

> 
> > I hope to provide users with the ability to enter S1 mode by themselves.
> 
> > So I cannot use DEVICE_ATTR. I should use file_operations method.
> 
> 
> No, please use the normal power management callbacks for your driver that
> all other drivers use in the kernel.  There is nothing special about this one
> driver to warrant a totally new user/kernel api for it.
> 

OK, I understand.

Thanks


^ permalink raw reply	[flat|nested] 19+ messages in thread

* RE: [PATCH v4 2/2] misc: Add iop driver for Sunplus SP7021
  2021-12-17  8:12         ` Arnd Bergmann
  2021-12-17 10:24           ` Tony Huang 黃懷厚
  2021-12-17 11:03           ` Tony Huang 黃懷厚
@ 2021-12-22  7:20           ` Tony Huang 黃懷厚
  2 siblings, 0 replies; 19+ messages in thread
From: Tony Huang 黃懷厚 @ 2021-12-22  7:20 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: DTML, Linux Kernel Mailing List, Derek Kiernan, Dragan Cvetic,
	Rob Herring, gregkh, Wells Lu 呂芳騰,
	黃懷厚

Dear Arnd:

> > >> IOP (IO Processor) embedded inside SP7021 which is used as
> > >> Processor for I/O control, RTC wake-up and cooperation with CPU &
> > >> PMC in power management purpose.
> > >> The IOP core is DQ8051, so also named IOP8051, it supports
> > >> dedicated JTAG debug pins which share with SP7021.
> > >> In standby mode operation, the power spec reach 400uA.
> > >>
> > >> Signed-off-by: Tony Huang <tonyhuang.sunplus@gmail.com>
> > >> ---
> > >> Changes in v4:
> > >>  - Addressed comments from Arnd Bergmann.
> >
> > >I don't think you did: I asked you specifically to add code to
> > >interact with the existing in-kernel interfaces to use the
> > >functionality provided by the device. Pick any (at least two)
> > >subsystems and add support, but leave out any custom user space
> > >interfaces (miscdevice, debugfs, sysfs, ...) for the moment.
> >
> >
> >
> > 1. IOP can run sp_iop_platform_driver_shudown() through the poweroff
> > command and the kernel. Perform system power-off actions.
> 
> Do you mean that this method a) cleanly shuts down the iop before the system
> is powered down, or b) the driver_shutdown() callback is used to initiate the
> powerdown of the system itself?
> 
> In case of a) I would not count that as exposing functionality, what you do here
> is just part of any driver. If instead you are trying to use b), this is the wrong
> way of doing it, see drivers/power/reset/ for examples of how to do it right.
> 
> > 2. Wake up the system by relying on the 8051 internal RTC wake-up
> > mechanism and external GPIO input signals to wake up.
> 
> I think those should be exposed with drivers/rtc for the RTC and drivers/gpio/
> for the GPIO driver, and then you can use the device tree to configure which
> GPIO to use as a wakeup and how it's connected to the RTC.
> 

I have a question to ask you:	
There are two ways to wake up the Linux kernel system in 8051.								
1)RTC wakeup:								
The SP7021 system mounts the RTC device driver.								
I set the RTC wake-up time by type commands in command line.								
Example: 
echo 0 > /sys/class/rtc/rtc0/wakealarm && nnn=`date '+%s'` && echo $nnn && nnn=`expr $nnn + 10` && echo $nnn > /sys/class/rtc/rtc0/wakealarm								
When the linux kernel system has no power. 8051 can read RTC resgister and receive RTC wakeup interrupt.								
Do I need to add RTC subsystem functiona to the IOP code?								
								
2)GPIO wakeup:								
According to your suggestion								
I use of_get_name_gpio() to get GPIO number from device tree during the IOP probe process.								
Pass it to 8051 for use.								

Thanks


^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2021-12-22  7:20 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-16  1:38 [PATCH v4 0/2] Add iop driver for Sunplus SP7021 Tony Huang
2021-12-16  2:34 ` Tony Huang
2021-12-16  1:38 ` [PATCH v4 1/2] dt-binding: misc: Add iop yaml file " Tony Huang
2021-12-16  2:34   ` Tony Huang
2021-12-16 20:33   ` Rob Herring
2021-12-16  1:38 ` [PATCH v4 2/2] misc: Add iop driver " Tony Huang
2021-12-16  2:35   ` Tony Huang
2021-12-16  7:36   ` Greg KH
     [not found]     ` <CAHpW4oRTcXq6k2o4cjFeHznZrR737947UPE60nWyPTRLPTR0Gw@mail.gmail.com>
2021-12-17  8:10       ` Tony Huang 黃懷厚
2021-12-17  8:24         ` gregkh
2021-12-20  8:35           ` Tony Huang 黃懷厚
2021-12-16  8:01   ` Arnd Bergmann
     [not found]     ` <CAHpW4oQmtd-gG1HYZT-Dk=QAvXKsKebzcOaVtrEeoujwoL9zSg@mail.gmail.com>
     [not found]       ` <316c16afbff74160b07bd74444f3b8e1@sphcmbx02.sunplus.com.tw>
2021-12-17  6:54         ` gregkh
2021-12-17  8:12         ` Arnd Bergmann
2021-12-17 10:24           ` Tony Huang 黃懷厚
2021-12-17 11:03           ` Tony Huang 黃懷厚
2021-12-22  7:20           ` Tony Huang 黃懷厚
2021-12-16 18:31   ` kernel test robot
2021-12-16 18:31     ` kernel test robot

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.