Dear greg k-h: > On Mon, Jan 10, 2022 at 02:37:54PM +0800, Tony Huang wrote: > > IOP(8051) embedded inside SP7021 which is used as Processor for I/O > > control, monitor RTC interrupt 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@xxxxxxxxx> > > --- > > Changes in v7: > > - Addressed comments from Greg KH. > > > > Documentation/ABI/testing/sysfs-platform-soc@B | 28 ++ > > MAINTAINERS | 2 + > > drivers/misc/sunplus_iop.c | 455 > +++++++++++++++++++++++++ > > 3 files changed, 485 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..1946a6f > > --- /dev/null > > +++ b/Documentation/ABI/testing/sysfs-platform-soc@B > > @@ -0,0 +1,28 @@ > > +What: /sys/devices/platform/soc@B/9c000400.iop/sp_iop_mailbox > > +Date: January 2022 > > +KernelVersion: 5.16 > > +Contact: Tony Huang <tonyhuang.sunplus@xxxxxxxxx> > > +Description: > > + Show IOP's mailbox0 register data. > > + Format: %x > > + > > +What: /sys/devices/platform/soc@B/9c000400.iop/sp_iop_mode > > +Date: January 2022 > > +KernelVersion: 5.16 > > +Contact: Tony Huang <tonyhuang.sunplus@xxxxxxxxx> > > +Description: > > + Read-Write. > > + > > + Write this file. > > + 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. > > + Writing of other values is invalid. > > + > > + Read this file. > > + Show operation mode of IOP. "0" is normal mode. "1" is standby > > + mode. > > + Format: %x > > + > > + > > diff --git a/MAINTAINERS b/MAINTAINERS index 6f336c9..cbc8dff 100644 > > --- a/MAINTAINERS > > +++ b/MAINTAINERS > > @@ -18245,7 +18245,9 @@ F: drivers/net/ethernet/dlink/sundance.c > > SUNPLUS IOP DRIVER > > M: Tony Huang <tonyhuang.sunplus@xxxxxxxxx> > > 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@xxxxxxxxxxxxxxxxxxxx> > > diff --git a/drivers/misc/sunplus_iop.c b/drivers/misc/sunplus_iop.c > > new file mode 100644 index 0000000..c27875d > > --- /dev/null > > +++ b/drivers/misc/sunplus_iop.c > > @@ -0,0 +1,455 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * The IOP driver for Sunplus SP7021 > > + * > > + * Copyright (C) 2021 Sunplus Technology Inc. > > + * > > + * All Rights Reserved. > > + */ > > +#include <linux/delay.h> > > +#include <linux/dma-mapping.h> > > +#include <linux/firmware.h> > > +#include <linux/iopoll.h> > > +#include <linux/module.h> > > +#include <linux/miscdevice.h> > > +#include <linux/of_platform.h> > > +#include <linux/of_address.h> > > +#include <linux/of_gpio.h> > > + > > +enum IOP_Status_e { > > + IOP_SUCCESS, /* successful */ > > + IOP_ERR_IOP_BUSY, /* IOP is busy */ > > +}; > > + > > +/* moon0 register offset */ > > +#define IOP_CLKEN0 0x04 > > +#define IOP_RESET0 0x54 > > + > > +/* IOP register offset */ > > +#define IOP_CONTROL 0x00 > > +#define IOP_DATA0 0x20 > > +#define IOP_DATA1 0x24 > > +#define IOP_DATA2 0x28 > > +#define IOP_DATA3 0x2c > > +#define IOP_DATA4 0x30 > > +#define IOP_DATA5 0x34 > > +#define IOP_DATA6 0x38 > > +#define IOP_DATA7 0x3c > > +#define IOP_DATA8 0x40 > > +#define IOP_DATA9 0x44 > > +#define IOP_DATA10 0x48 > > +#define IOP_DATA11 0x4c > > +#define IOP_BASE_ADR_L 0x50 > > +#define IOP_BASE_ADR_H 0x54 > > + > > +/* PMC register offset */ > > +#define IOP_PMC_TIMER 0x00 > > +#define IOP_PMC_CTRL 0x04 > > +#define IOP_XTAL27M_PASSWORD_I 0x08 > > +#define IOP_XTAL27M_PASSWORD_II 0x0c > > +#define IOP_XTAL32K_PASSWORD_I 0x10 > > +#define IOP_XTAL32K_PASSWORD_II 0x14 > > +#define IOP_CLK27M_PASSWORD_I 0x18 > > +#define IOP_CLK27M_PASSWORD_II 0x1c > > +#define IOP_PMC_TIMER2 0x20 > > + > > +#define NORMAL_CODE_MAX_SIZE 0X1000 /* Max size of normal.bin > that can be received */ > > +#define STANDBY_CODE_MAX_SIZE 0x4000 /* Max size of standby.bin > that can be received */ > > +struct sp_iop { > > + struct miscdevice dev; > > + struct mutex write_lock; /* avoid parallel access */ > > + void __iomem *iop_regs; > > + void __iomem *pmc_regs; > > + void __iomem *moon0_regs; > > + int irq; > > + int gpio_wakeup; > > + 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; > > + unsigned char bin_code_mode; > > +}; > > + > > +static void sp_iop_normal_mode(struct sp_iop *iop) { > > + 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, iop->moon0_regs + IOP_CLKEN0); > > + > > + reg = readl(iop->iop_regs + IOP_CONTROL); > > + reg |= 0x01; > > + writel(reg, iop->iop_regs + IOP_CONTROL); > > + > > + reg = readl(iop->iop_regs + IOP_CONTROL); > > + reg &= ~(0x8000); > > + writel(reg, iop->iop_regs + IOP_CONTROL); > > + > > + reg = readl(iop->iop_regs + IOP_CONTROL); > > + reg |= 0x0200;// disable watchdog event reset IOP > > + writel(reg, iop->iop_regs + IOP_CONTROL); > > + > > + reg = (iop->iop_mem_start & 0xFFFF); > > + writel(reg, iop->iop_regs + IOP_BASE_ADR_L); > > + reg = (iop->iop_mem_start >> 16); > > + writel(reg, iop->iop_regs + IOP_BASE_ADR_H); > > + > > + reg = readl(iop->iop_regs + IOP_CONTROL); > > + reg &= ~(0x01); > > + writel(reg, iop->iop_regs + IOP_CONTROL); > > + iop->bin_code_mode = 0; > > +} > > + > > +static void sp_iop_standby_mode(struct sp_iop *iop) { > > + 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, iop->moon0_regs + IOP_CLKEN0); > > + > > + reg = readl(iop->iop_regs + IOP_CONTROL); > > + reg |= 0x01; > > + writel(reg, iop->iop_regs + IOP_CONTROL); > > + > > + reg = readl(iop->iop_regs + IOP_CONTROL); > > + reg &= ~(0x8000); > > + writel(reg, iop->iop_regs + IOP_CONTROL); > > + > > + reg = readl(iop->iop_regs + IOP_CONTROL); > > + reg |= 0x0200;// disable watchdog event reset IOP > > + writel(reg, iop->iop_regs + IOP_CONTROL); > > + > > + reg = (iop->iop_mem_start & 0xFFFF); > > + writel(reg, iop->iop_regs + IOP_BASE_ADR_L); > > + reg = (iop->iop_mem_start >> 16); > > + writel(reg, iop->iop_regs + IOP_BASE_ADR_H); > > + > > + reg = readl(iop->iop_regs + IOP_CONTROL); > > + reg &= ~(0x01); > > + writel(reg, iop->iop_regs + IOP_CONTROL); > > + iop->bin_code_mode = 1; > > +} > > + > > +/* 8051 informs linux kerenl. 8051 has been switched to standby.bin code. > */ > > +#define IOP_READY 0x0004 > > +#define RISC_READY 0x0008 > > + > > +/* System linux kernel tells 8051 which gpio pin to wake-up through. */ > > +#define WAKEUP_PIN 0xFE02 > > + > > +/* System linux kernel tells 8051 to execute S1 or S3 mode. */ > > +#define S1 0x5331 > > +#define S3 0x5333 > > + > > +static int sp_iop_s3mode(struct device *dev, struct sp_iop *iop) { > > + unsigned int reg; > > + int ret, value; > > + > > + /* PMC set */ > > + writel(0x00010001, iop->pmc_regs + IOP_PMC_TIMER); > > + reg = readl(iop->pmc_regs + IOP_PMC_CTRL); > > + /* disable system reset PMC, enalbe power down IOP Domain, enable > gating clock 27Mhz */ > > + reg |= 0x23; > > + writel(reg, iop->pmc_regs + IOP_PMC_CTRL); > > + > > + writel(0x55aa00ff, iop->pmc_regs + IOP_XTAL27M_PASSWORD_I); > > + writel(0x00ff55aa, iop->pmc_regs + IOP_XTAL27M_PASSWORD_II); > > + writel(0xaa00ff55, iop->pmc_regs + IOP_XTAL32K_PASSWORD_I); > > + writel(0xff55aa00, iop->pmc_regs + IOP_XTAL32K_PASSWORD_II); > > + writel(0xaaff0055, iop->pmc_regs + IOP_CLK27M_PASSWORD_I); > > + writel(0x5500aaff, iop->pmc_regs + IOP_CLK27M_PASSWORD_II); > > + writel(0x01000100, iop->pmc_regs + IOP_PMC_TIMER2); > > + > > + /* IOP Hardware IP reset */ > > + reg = readl(iop->moon0_regs + IOP_RESET0); > > + reg |= 0x10; > > + writel(reg, (iop->moon0_regs + IOP_RESET0)); > > + reg &= ~(0x10); > > + writel(reg, (iop->moon0_regs + IOP_RESET0)); > > + > > + 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)); > > + > > + writel(WAKEUP_PIN, iop->iop_regs + IOP_DATA0); > > + writel(iop->gpio_wakeup, iop->iop_regs + IOP_DATA1); > > + > > + ret = readl_poll_timeout(iop->iop_regs + IOP_DATA2, value, > > + (value & IOP_READY) == IOP_READY, 1000, 10000); > > + if (ret) { > > + dev_err(dev, "timed out\n"); > > + return ret; > > + } > > + > > + reg = RISC_READY; > > + writel(reg, iop->iop_regs + IOP_DATA2); > > + reg = 0x0000; > > + writel(reg, iop->iop_regs + IOP_DATA5); > > + reg = 0x0060; > > + writel(reg, iop->iop_regs + IOP_DATA6); > > + > > + ret = readl_poll_timeout(iop->iop_regs + IOP_DATA7, value, > > + value == 0xaaaa, 1000, 10000); > > + if (ret) { > > + dev_err(dev, "timed out\n"); > > + return ret; > > + } > > + > > + /* 8051 bin file call Ultra low function. */ > > + writel(0xdd, iop->iop_regs + IOP_DATA1); > > + /* > > + * When the execution is here, the system linux kernel > > + * is about to be powered off > > + * The purpose of mdelay(10): Do not let the system linux > > + * kernel continue to run other programs. > > + */ > > + mdelay(10); > > + return 0; > > +} > > + > > +static int sp_iop_s1mode(struct device *dev, struct sp_iop *iop) { > > + int ret, value; > > + > > + ret = readl_poll_timeout(iop->iop_regs + IOP_DATA2, value, > > + (value & IOP_READY) == IOP_READY, 1000, 10000); > > + if (ret) { > > + dev_err(dev, "timed out\n"); > > + return ret; > > + } > > + > > + writel(RISC_READY, iop->iop_regs + IOP_DATA2); > > + writel(0x0000, iop->iop_regs + IOP_DATA5); > > + writel(0x0060, iop->iop_regs + IOP_DATA6); > > + > > + ret = readl_poll_timeout(iop->iop_regs + IOP_DATA7, value, > > + value == 0xaaaa, 1000, 10000); > > + if (ret) { > > + dev_err(dev, "timed out\n"); > > + return ret; > > + } > > + > > + /* 8051 bin file call S1_mode function. */ > > + writel(0xee, iop->iop_regs + IOP_DATA1); > > + /* > > + * When the execution is here, the system linux kernel > > + * is about to be powered off > > + * The purpose of mdelay(10): Do not let the system linux > > + * kernel continue to run other programs. > > + */ > > + mdelay(10); > > + return 0; > > +} > > + > > +static ssize_t sp_iop_mailbox_show(struct device *dev, struct > > +device_attribute *attr, char *buf) { > > + struct sp_iop *iop = dev_get_drvdata(dev); > > + unsigned int mailbox; > > + > > + mailbox = readl(iop->iop_regs + IOP_DATA0); > > + return sysfs_emit(buf, "%x\n", mailbox); } > > + > > +static ssize_t sp_iop_mode_show(struct device *dev, struct > > +device_attribute *attr, char *buf) { > > + struct sp_iop *iop = dev_get_drvdata(dev); > > + > > + return sysfs_emit(buf, "%x\n", iop->bin_code_mode); } > > + > > +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); > > + > > + if (sysfs_streq(buf, "0")) > > + sp_iop_normal_mode(iop); > > + else if (sysfs_streq(buf, "1")) > > + sp_iop_standby_mode(iop); > > + else > > + return -EINVAL; > > + return count; > > +} > > + > > +static DEVICE_ATTR_RO(sp_iop_mailbox); static > > +DEVICE_ATTR_RW(sp_iop_mode); > > Please do not manually create the sysfs files. You race with userspace and > loose. Use the default_groups pointer of the platform driver and then the > driver core will handle that all for you automatically. OK, I will use the default_groups pointer of the platform driver.