RE: [PATCH 2/2] misc: Add iop driver for Sunplus SP7021

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Dear Arnd:

> -----Original Message-----
> From: Arnd Bergmann <arnd@xxxxxxxx>
> Sent: Wednesday, November 17, 2021 4:28 PM
> To: Tony Huang <tonyhuang.sunplus@xxxxxxxxx>
> Cc: robh+dt@xxxxxxxxxx; devicetree@xxxxxxxxxxxxxxx;
> linux-kernel@xxxxxxxxxxxxxxx; derek.kiernan@xxxxxxxxxx;
> dragan.cvetic@xxxxxxxxxx; arnd@xxxxxxxx; gregkh@xxxxxxxxxxxxxxxxxxx; Wells
> Lu 呂芳騰 <wells.lu@xxxxxxxxxxx>; Tony Huang 黃懷厚
> <tony.huang@xxxxxxxxxxx>
> Subject: Re: [PATCH 2/2] misc: Add iop driver for Sunplus SP7021
> 
> On Wed, Nov 17, 2021 at 7:48 AM Tony Huang
> <tonyhuang.sunplus@xxxxxxxxx> wrote:
> >
> > Add iop driver for Sunplus SP7021
> >
> > Signed-off-by: Tony Huang <tony.huang@xxxxxxxxxxx>
> 
> A driver like this needs a long description of multiple paragraphs to explain
> what it does, why you need a custom user space interface etc.

I will add long description.

> 
> > +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.
> > diff --git a/drivers/misc/iop/Makefile b/drivers/misc/iop/Makefile new
> > file mode 100644 index 0000000..cb67634
> > --- /dev/null
> > +++ b/drivers/misc/iop/Makefile
> > @@ -0,0 +1,13 @@
> > +#
> > +# Makefile for the IOP module drivers.
> > +#
> > +
> > +       # call from kernel build system
> > +
> > +       obj-$(CONFIG_SUNPLUS_IOP) += sunplus_iop.o
> > +       obj-$(CONFIG_SUNPLUS_IOP) += hal_iop.o
> > +       obj-$(CONFIG_SUNPLUS_IOP) += iopnormal.o
> > +       obj-$(CONFIG_SUNPLUS_IOP) += iopstandby.o
> 
> Remove the extra whitespace here

I will remove it.

> 
> > +clean:
> > +       rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
> > \ No newline at end of file
> 
> Replace the explicit 'rm' command with Kbuild listings for these files.
> Most of the files are already covered by the regular 'clean' target, so you
> should only need special handling for stuff you build separately as well.

I will modify it.

> 
> > diff --git a/drivers/misc/iop/hal_iop.c b/drivers/misc/iop/hal_iop.c
> > new file mode 100644 index 0000000..6a72c8b
> > --- /dev/null
> > +++ b/drivers/misc/iop/hal_iop.c
> > @@ -0,0 +1,495 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +
> > +#include <linux/delay.h>
> > +#include <linux/io.h>
> > +#include "hal_iop.h"
> 
> One fundamental rule for Linux kernel drivers is that we assume that we know
> which OS we are running on, so there is no need for abstracting the operating
> system away from the driver. Please fold your 'hal' into the top-level driver
> accordingly and remove any unneded indirections between the two. I suppose
> you could have everything in one file if after you remove the firmware from
> the driver, and no longer require any headers.

I will modify it.

> 
> > +//#define DEBUG_MESSAGE
> > +//#define early_printk
> > +
> > +#define IOP_KDBG_INFO
> > +#define IOP_FUNC_DEBUG
> > +#define IOP_KDBG_ERR
> > +#ifdef IOP_KDBG_INFO
> > +       #define FUNC_DEBUG()    pr_info("K_IOP: %s(%d)\n", __func__,
> __LINE__)
> > +#else
> > +       #define FUNC_DEBUG()
> > +#endif
> > +
> > +#ifdef IOP_FUNC_DEBUG
> > +#define DBG_INFO(fmt, args ...)        pr_info("K_IOP: " fmt, ## args)
> > +#else
> > +#define DBG_INFO(fmt, args ...)
> > +#endif
> > +
> > +#ifdef IOP_KDBG_ERR
> > +#define DBG_ERR(fmt, args ...) pr_err("K_IOP: " fmt, ## args) #else
> > +#define DBG_ERR(fmt, args ...) #endif
> 
> These should all go away, try using dev_dbg() / dev_info() / dev_err()
> preferably.

I will modify it.

> 
> > +void hal_iop_init(void __iomem *iopbase) {
> > +       struct regs_iop_t *pIopReg = (struct regs_iop_t *)iopbase;
> > +       unsigned long *IOP_base_for_normal = (unsigned long
> *)SP_IOP_RESERVE_BASE;
> > +       unsigned char *IOP_kernel_base;
> > +       unsigned int reg;
> > +
> > +       IOP_kernel_base = (unsigned char *)ioremap((unsigned
> long)IOP_base_for_normal,
> > +               NORMAL_CODE_MAX_SIZE);
> > +
> > +       memset((unsigned char *)IOP_kernel_base, 0,
> NORMAL_CODE_MAX_SIZE);
> > +       memcpy((unsigned char *)IOP_kernel_base, IopNormalCode,
> NORMAL_CODE_MAX_SIZE);
> > +       writel(0x00100010, (void __iomem *)(B_SYSTEM_BASE + 32*4*0 +
> > + 4*1));
> 
> All the type casts should be removed after you use the correct types:
> 'void __iomem *'
> for MMIO tokens, and 'void *' for addressable memory. You can use the
> 'sparse' tool by running 'make C=1' to check for any mismatched __iomem
> annotations.

I will modify it.

> 
> To write to an __iomem area, you can use memcpy_to_io() and memset_io()
> 
> > diff --git a/drivers/misc/iop/hal_iop.h b/drivers/misc/iop/hal_iop.h
> > new file mode 100644 index 0000000..d83f9ea
> > --- /dev/null
> > +++ b/drivers/misc/iop/hal_iop.h
> > @@ -0,0 +1,34 @@
> > +/* SPDX-License-Identifier: GPL-2.0-or-later*/
> > +
> > +#ifndef __IOP_HAL_H__
> > +#define __IOP_HAL_H__
> > +
> > +#include <linux/types.h>
> > +#include <linux/module.h>
> > +#include "reg_iop.h"
> > +
> > +void hal_iop_init(void __iomem *iopbase); void
> > +hal_iop_load_normal_code(void __iomem *iopbase); void
> > +hal_iop_load_standby_code(void __iomem *iopbase); void
> > +hal_iop_normalmode(void __iomem *iopbase); void
> > +hal_iop_standbymode(void __iomem *iopbase); void
> > +hal_iop_get_iop_data(void __iomem *iopbase); void
> > +hal_iop_set_iop_data(void __iomem *iopbase, unsigned int num,
> > +unsigned int value); void hal_gpio_init(void __iomem *iopbase,
> > +unsigned char gpio_number); void hal_iop_suspend(void __iomem
> > +*iopbase, void __iomem *ioppmcbase); void hal_iop_shutdown(void
> > +__iomem *iopbase, void __iomem *ioppmcbase); void
> hal_iop_S1mode(void
> > +__iomem *iopbase); void hal_iop_set_reserve_base(void __iomem
> > +*iopbase); void hal_iop_set_reserve_size(void __iomem *iopbase);
> 
> When you fold those into the actual driver but decide to keep the functions
> separate, try to always pass a pointer to the device instance as the first
> argument. This is the way we do object oriented programming in the kernel.

I will modify it.

> 
> > +#define NORMAL_CODE_MAX_SIZE 0X10000
> > +#define STANDBY_CODE_MAX_SIZE 0x4000
> > +extern const unsigned char IopNormalCode[]; extern const unsigned
> > +char IopStandbyCode[]; extern bool iop_code_mode; extern unsigned
> > +long SP_IOP_RESERVE_BASE; extern unsigned long SP_IOP_RESERVE_SIZE;
> > +extern unsigned int RECEIVE_CODE_SIZE; extern unsigned char
> > +NormalCode[]; extern unsigned char StandbyCode[]; #endif /*
> > +__IOP_HAL_H__ */
> 
> There should generally be no global variables. Instead, these should go into
> per-instance structures.

I will modify it.

> 
> Regarding the naming, avoid CamelCase and use e.g. 'iop_standby_code'
> instead of 'IopStandbyCode'.

I will modify it.

> 
> > diff --git a/drivers/misc/iop/iop_ioctl.h
> > b/drivers/misc/iop/iop_ioctl.h new file mode 100644 index
> > 0000000..195388b
> > --- /dev/null
> > +++ b/drivers/misc/iop/iop_ioctl.h
> > @@ -0,0 +1,24 @@
> > +/* SPDX-License-Identifier: GPL-2.0-or-later*/
> > +/*
> > + *
> > + *iop_ioctl.h
> > + *
> > + */
> > +
> > +#ifndef _IOP_IOCTL_H
> > +#define _IOP_IOCTL_H
> > +
> > +#include <linux/ioctl.h>
> > +
> > +struct ioctl_cmd {
> > +               unsigned int reg;
> > +               unsigned int offset;
> > +               unsigned int val;
> > +};
> > +
> > +#define IOC_MAGIC 'i'
> > +
> > +#define IOCTL_VALSET _IOW(IOC_MAGIC, 1, struct ioctl_cmd) #define
> > +IOCTL_VALGET _IOR(IOC_MAGIC, 2, struct ioctl_cmd)
> > +
> > +#endif
> 
> ioctl interfaces are usually the hardest part of a driver, the best way to do this
> is to completely avoid inventing your own interfaces and using what the kernel
> already has, extending the existing interfaces if possible.
> 
> If you end up having to add your own ioctls, do high-level functions rather than
> low-level register access, and create a separate ioctl command for each
> action.

I will modify it.

> 
> > diff --git a/drivers/misc/iop/iopnormal.c
> > b/drivers/misc/iop/iopnormal.c new file mode 100644 index
> > 0000000..a49a376
> > --- /dev/null
> > +++ b/drivers/misc/iop/iopnormal.c
> > @@ -0,0 +1,4106 @@
> > +const unsigned char IopNormalCode[] = { 0x02, 0x1E, 0x00, 0x02, 0xFF,
> > +0xFF, 0x2E, 0x05, 0x02, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB0, 0x05,
> > +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0xFF, 0xFF,
> > +0xFF, 0x08, 0x75, 0xCD, 0x05, 0x00, 0x09, 0x75, 0x04, 0x02, 0x8F,
> > +0x81, 0x75, 0x52, 0x78, 0x73, 0x04, 0x13, 0xEF, 0xFF, 0xE6,
> 
> As I understand, this is the binary firmware that gets loaded into the iop.
> 
> The way we normally handle firmware loading in the kernel is to use the
> 'request_firmware()' interface from the driver to load the binary from a file in
> the root file system.

I will try to use request_firmware().

> 
> 
> > diff --git a/drivers/misc/iop/reg_iop.h b/drivers/misc/iop/reg_iop.h
> > new file mode 100644 index 0000000..829aa76
> > --- /dev/null
> > +++ b/drivers/misc/iop/reg_iop.h
> > @@ -0,0 +1,93 @@
> > +/* SPDX-License-Identifier: GPL-2.0-or-later*/ #ifndef __REG_IOP_H__
> > +#define __REG_IOP_H__ #ifdef CONFIG_SOC_SP7021 #include
> > +<mach/io_map.h> #endif
> 
> There should not be any conditional on the type of the SOC here, the driver
> object needs to work in a kernel that includes support for multiple SoCs.
> 
> Note that there are no mach/*.h headers for modern SoCs any more, so the
> platform code needs to be updated accordingly. In particular, any MMIO
> register locations need to come from the devicetree, not from a hardcoded
> header.

I will modify it.

> 
> > +struct regs_iop_moon0_t {
> > +       unsigned int stamp;/* 00 */
> > +       unsigned int clken[10];/* 01~10 */
> > +       unsigned int gclken[10];/* 11~20 */
> > +       unsigned int reset[10];/* 21~30 */
> > +       unsigned int sfg_cfg_mode;/* 31 */ };
> 
> Drop the '_t' suffix on the structure names, those are commonly associated
> with names for typedefs, not structures.
> 
> Most driver writers find it easier to define register indexes as macros rather
> than structure definitions, but if all registers are 32-bit wide, then this works as
> well.
> 
> Using 'u32' as the type instead of 'unsigned int' would make this more explicit.

I will modify it.

> 
> > +#ifdef IOP_KDBG_INFO
> > +#define FUNC_DEBUG()   pr_info("K_IOP: %s(%d)\n", __func__, __LINE__)
> > +#else
> > +#define FUNC_DEBUG()
> > +#endif
> > +
> > +#ifdef IOP_FUNC_DEBUG
> > +#define DBG_INFO(fmt, args ...)        pr_info("K_IOP: " fmt, ## args)
> > +#else
> > +#define DBG_INFO(fmt, args ...)
> > +#endif
> > +
> > +#ifdef IOP_KDBG_ERR
> > +#define DBG_ERR(fmt, args ...) pr_err("K_IOP: " fmt, ## args) #else
> > +#define DBG_ERR(fmt, args ...) #endif
> > +/* ----------------------------------------------------------------------------------------------
> */
> > +#define IOP_REG_NAME           "iop"
> > +#define IOP_PMC_REG_NAME          "iop_pmc"
> > +
> > +#define DEVICE_NAME                    "sunplus,sp7021-iop"
> 
> Remove all those and open-code the correct contents in the users.

I will remove it.

> 
> > +static ssize_t setgpio_store(struct device *dev, struct device_attribute *attr,
> > +       const char *buf, size_t count) {
> > +       int ret = count;
> > +       unsigned char num[1];
> > +       unsigned int setnum;
> > +       unsigned long val;
> > +       ssize_t status;
> > +
> > +       DBG_INFO("iop_store_setgpio\n");
> > +       num[0] = buf[0];
> > +       status = kstrtoul(buf, 16, &val);
> > +       if (status)
> > +               return status;
> > +       setnum = val;
> > +       DBG_INFO("set gpio number = %x\n", IOP_GPIO);
> > +       hal_gpio_init(iop->iop_regs, IOP_GPIO);
> > +       return ret;
> > +}
> 
> GPIO access should be handled through the gpio subsystem by creating an
> instance of a gpio_chip, which provides interfaces for both in-kernel and
> user-space users.

I will modify it.

> 
> > +static ssize_t S1mode_show(struct device *dev, struct device_attribute
> *attr,
> > +       char *buf)
> > +{
> > +       ssize_t len = 0;
> > +
> > +       hal_iop_standbymode(iop->iop_regs);
> > +       mdelay(10);//Need time to update iop_data
> > +       hal_iop_S1mode(iop->iop_regs);
> > +       return len;
> > +}
> > +
> > +static ssize_t S1mode_store(struct device *dev, struct device_attribute
> *attr,
> > +       const char *buf, size_t count) {
> > +       ssize_t len = 0;
> > +
> > +       return len;
> > +}
> 
> Each new device attribute needs to be documented in Documentation/ABI/

I will documented for new attribute in Documentation/ABI/.

> 
>        Arnd




[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux