[PATCH 4/7] misc: Add driver for DAB IP found on Renesas R-Car devices

Fabrizio Castro fabrizio.castro.jz at renesas.com
Mon Mar 1 16:19:25 GMT 2021


Hi Geert,

Thanks for your feedback!

> From: Geert Uytterhoeven <geert at linux-m68k.org>
> Sent: 26 February 2021 09:34
> Subject: Re: [PATCH 4/7] misc: Add driver for DAB IP found on Renesas R-
> Car devices
> 
> Hi Fabrizio,
> 
> On Thu, Feb 25, 2021 at 11:53 PM Fabrizio Castro
> <fabrizio.castro.jz at renesas.com> wrote:
> > The DAB hardware accelerator found on R-Car E3 and R-Car M3-N devices is
> > a hardware accelerator for software DAB demodulators.
> > It consists of one FFT (Fast Fourier Transform) module and one decoder
> > module, compatible with DAB specification (ETSI EN 300 401 and
> > ETSI TS 102 563).
> > The decoder module can perform FIC decoding and MSC decoding processing
> > from de-puncture to final decoded result.
> >
> > This patch adds a device driver to support the FFT module only.
> >
> > Signed-off-by: Fabrizio Castro <fabrizio.castro.jz at renesas.com>
> 
> Thanks for your patch!
> 
> > --- a/drivers/misc/Kconfig
> > +++ b/drivers/misc/Kconfig
> > @@ -461,4 +461,5 @@ source "drivers/misc/bcm-vk/Kconfig"
> >  source "drivers/misc/cardreader/Kconfig"
> >  source "drivers/misc/habanalabs/Kconfig"
> >  source "drivers/misc/uacce/Kconfig"
> > +source "drivers/misc/rcar_dab/Kconfig"
> 
> rcar-dab?

I will change the directory name

> 
> 
> > --- /dev/null
> > +++ b/drivers/misc/rcar_dab/Kconfig
> > @@ -0,0 +1,11 @@
> > +# SPDX-License-Identifier: GPL-2.0+
> > +#
> > +# R-Car DAB Hardware Accelerator
> > +#
> > +
> > +config RCAR_DAB
> 
> DAB_RCAR?

Agreed

> 
> > +       tristate "DAB accelerator for Renesas R-Car devices"
> > +       depends on ARCH_R8A77990 || ARCH_R8A77965
> 
> || COMPILE_TEST

Will do

> 
> > +       help
> > +         Some R-Car devices come with an IP to accelerate DAB decoding.
> > +         Enable this option to add driver support.
> 
> > --- /dev/null
> > +++ b/drivers/misc/rcar_dab/rcar_dev.c
> > @@ -0,0 +1,176 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * R-Car Gen3 DAB hardware accelerator driver
> > + *
> > + * Copyright (C) 2021 Renesas Electronics Corporation
> > + *
> > + */
> > +#include <linux/clk.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/mutex.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/uaccess.h>
> > +
> > +#include <uapi/linux/rcar_dab.h>
> > +#include "rcar_dev.h"
> > +
> > +irqreturn_t rcar_dab_irq(int irq, void *devid)
> 
> static, as discovered by the robot.

Yep

> 
> > +{
> > +       struct rcar_dab *dab = devid;
> > +       u32 intsr, intcr1;
> > +
> > +       spin_lock(&dab->shared_regs_lock);
> > +
> > +       intcr1 = rcar_dab_read(dab, RCAR_DAB_INTCR1);
> > +       rcar_dab_write(dab, RCAR_DAB_INTCR1, 0x000003FF);
> 
> Magic value (setting undocumented bits?), please use a define.

The documentation (Section 44B.2.21 of the R-Car Gen3 HW User
manual) says to write (reserved) bits 31 to 10 as 0, to write
(reserved) bits 9 to 3 as 1, and the remaining 3 bits (2 to 0),
are interrupt masking bits set to 1 in this case to temporarily
disable interrupts.

I can of course add a define for this.

> 
> > +
> > +       intsr = rcar_dab_read(dab, RCAR_DAB_INTSR);
> > +       if (!intsr) {
> > +               rcar_dab_write(dab, RCAR_DAB_INTCR1, intcr1);
> > +               spin_unlock(&dab->shared_regs_lock);
> > +               return IRQ_NONE;
> > +       }
> > +
> > +       /* Re-enable interrupts that haven't fired */
> > +       rcar_dab_write(dab, RCAR_DAB_INTCR1, 0x3FF & (intsr | intcr1));
> > +       /* Clear interrupts */
> > +       rcar_dab_write(dab, RCAR_DAB_INTSR, 0x7 & ~intsr);
> 
> More magic values.

As per section 44B.2.20 from the R-Car Gen3 HW User Manual,
Bit 31 to 3 are reserved and reads and writes as 0, and a
0 has to be written to bits 2 to 0 to clear the interrupts.

I will use a define for this as well.

> 
> > +
> > +       spin_unlock(&dab->shared_regs_lock);
> > +
> > +       if (intsr & RCAR_DAB_INTSR_FFT_DONE)
> > +               rcar_dab_fft_irq(dab);
> > +
> > +       return IRQ_HANDLED;
> > +}
> > +
> > +static long rcar_dab_unlocked_ioctl(struct file *file, unsigned int
> cmd,
> > +                                   unsigned long arg)
> > +{
> > +       void __user *argp = (void __user *)arg;
> > +       struct rcar_dab *dab;
> > +       int ret;
> > +
> > +       dab = container_of(file->private_data, struct rcar_dab, misc);
> > +
> > +       switch (cmd) {
> > +       case RCAR_DAB_IOC_FFT:
> > +               if (!access_ok(argp, sizeof(struct rcar_dab_fft_req)))
> > +                       return -EFAULT;
> > +               ret = rcar_dab_fft(dab, argp);
> 
> Do you envision ever using the FFT operation from kernel space?
> Might be easier if you handle the copy_{from,to}_user() here.

Buffers are pre-allocated and shared among requests, therefore access to the
buffers has to be protected with a mutex. To keep things tidy (all FFT related
work in the FFT related source file) I prefer to keep this as it is, as FIC
and MSC will add more to the ioctl.

> 
> > +               break;
> > +       default:
> > +               ret = -ENOTTY;
> > +       }
> > +
> > +       return ret;
> > +}
> > +
> > +static const struct file_operations rcar_dab_fops = {
> > +       .owner          = THIS_MODULE,
> > +       .unlocked_ioctl = rcar_dab_unlocked_ioctl,
> 
> Does this need compat_ioctl handling?

Will add

> 
> > +};
> > +
> > +static int rcar_dab_probe(struct platform_device *pdev)
> > +{
> > +       struct device *dev = &pdev->dev;
> > +       struct rcar_dab *dab;
> > +       int ret, irq;
> > +
> > +       dab = devm_kzalloc(dev, sizeof(*dab), GFP_KERNEL);
> > +       if (!dab)
> > +               return -ENOMEM;
> > +       dab->pdev = pdev;
> > +
> > +       dab->base = devm_platform_ioremap_resource(pdev, 0);
> > +       if (IS_ERR(dab->base))
> > +               return PTR_ERR(dab->base);
> > +
> > +       irq = platform_get_irq(pdev, 0);
> > +       if (irq < 0) {
> > +               dev_err(dev, "Can't get the irq.\n");
> > +               return -EINVAL;
> > +       }
> > +
> > +       dab->clk = devm_clk_get(&pdev->dev, "dab");
> > +       if (IS_ERR(dab->clk)) {
> > +               ret = PTR_ERR(dab->clk);
> > +               dev_err(dev, "Can't get the clock (%d).\n", ret);
> > +               return ret;
> > +       }
> > +
> > +       spin_lock_init(&dab->shared_regs_lock);
> > +
> > +       ret = clk_prepare_enable(dab->clk);
> 
> Does the module clock need to be enabled all the time?

No, it doesn't.

> What about using Runtime PM instead of explicit clock handling, so your
> driver will keep on working on future SoCs where the DAB block is part of
> a power domain?

Can do, will switch to using Runtime PM.

> 
> > +       if (ret)
> > +               return ret;
> > +
> > +       ret = rcar_dab_fft_probe(dab);
> > +       if (ret)
> > +               goto error_clock_disable;
> > +
> > +       ret = devm_request_irq(dev, irq, rcar_dab_irq, 0, dev_name(dev),
> dab);
> > +       if (ret) {
> > +               dev_err(dev, "Can't request the irq (%d).\n", ret);
> > +               goto error_remove;
> > +       }
> > +
> > +       platform_set_drvdata(pdev, dab);
> > +
> > +       dab->misc.minor = MISC_DYNAMIC_MINOR;
> > +       dab->misc.name = RCAR_DAB_DRV_NAME;
> > +       dab->misc.fops = &rcar_dab_fops;
> > +       ret = misc_register(&dab->misc);
> > +       if (ret) {
> > +               dev_err(dev, "Can't register misc device (%d).\n", ret);
> > +               goto error_remove;
> > +       }
> > +
> > +       dev_info(dev, "R-Car Gen3 DAB misc driver ready.\n");
> > +
> > +       return 0;
> > +
> > +error_remove:
> > +       rcar_dab_fft_remove(dab);
> > +
> > +error_clock_disable:
> > +       clk_disable_unprepare(dab->clk);
> > +
> > +       return ret;
> > +}
> 
> > --- /dev/null
> > +++ b/drivers/misc/rcar_dab/rcar_dev.h
> 
> > +/* Register DAB_FFTCR */
> > +#define RCAR_DAB_FFTCR_FFT_EN_DISABLED         0
> > +#define RCAR_DAB_FFTCR_FFT_EN_ENABLED          1
> 
> Do you need both?

We don't, I have just thought it made things clearer.

> 
> #define RCAR_DAB_FFTCR_FFT_EN        BIT(0)
> 
> > +
> > +/* Register DAB_FFTREQCR */
> > +#define RCAR_DAB_FFTREQCR_FFT_REQ_INACTIVE     0
> > +#define RCAR_DAB_FFTREQCR_FFT_REQ_ACTIVE       1
> 
> Do you need both?

We don't, I have just thought it made things clearer.

> 
> > +
> > +/* Register DAB_INTSR */
> > +#define RCAR_DAB_INTSR_FFT_DONE                        0x1
> 
> BIT(0) (there are more bits for FIC and MSC)

Will change

> 
> > +
> > +/* Register DAB_INTCR1 */
> > +#define RCAR_DAB_INTCR1_FFT_DONE_MASK          0x1
> 
> BIT(0) (there are more bits for FIC and MSC)

Will change

> 
> > +#define RCAR_DAB_INTCR1_FFT_DONE_INT_ENABLED   0
> > +#define RCAR_DAB_INTCR1_FFT_DONE_INT_DISABLED  1
> 
> Do you need these?
> I'd just retain RCAR_DAB_INTCR1_FFT_DONE.

Agreed

> 
> For enabling interrupts:
> 
>     rcar_dab_update_bits_locked(dab, RCAR_DAB_INTCR1,
>                                 RCAR_DAB_INTCR1_FFT_DONE,
>                                 RCAR_DAB_INTCR1_FFT_DONE);
> 
> and for disabling:
> 
>     rcar_dab_update_bits_locked(dab, RCAR_DAB_INTCR1,
>                                 CAR_DAB_INTCR1_FFT_DONE, 0);

Okay

> 
> > +
> > +struct rcar_dab_fft {
> > +       bool done;                      /*
> > +                                        * Condition for waking up the
> process
> > +                                        * sleeping while FFT is in
> progress.
> > +                                        */
> 
> Please use kerneldoc for documenting structures.

Ok

> 
> > +       wait_queue_head_t wait;         /* Wait queue for FFT. */
> > +       struct mutex lock;              /* Mutex for FFT. */
> > +       dma_addr_t dma_input_buf;       /*
> > +                                        * Input buffer seen by the FFT
> > +                                        * module.
> > +                                        */
> > +       dma_addr_t dma_output_buf;      /*
> > +                                        * Output buffer seen by the FFT
> > +                                        * module.
> > +                                        */
> > +       void *input_buffer;             /* Input buffer seen by the CPU
> */
> > +       void *output_buffer;            /* Output buffer seen by the CPU
> */
> 
> Please use consistent naming (buf vs buffer).

Ok

> 
> > +};
> 
> > --- /dev/null
> > +++ b/drivers/misc/rcar_dab/rcar_fft.c
> 
> > +int rcar_dab_fft(struct rcar_dab *dab, struct rcar_dab_fft_req __user
> *fft_req)
> > +{
> > +       int buffer_size, ret;
> > +
> > +       buffer_size = fft_req->points * 4;
> 
> Missing validation of buffer_size?

Will add

> 
> > +
> > +       mutex_lock(&dab->fft.lock);
> > +
> > +       if (copy_from_user(dab->fft.input_buffer, fft_req-
> >input_address,
> > +                          buffer_size)) {
> > +               mutex_unlock(&dab->fft.lock);
> > +               return -EFAULT;
> > +       }
> > +
> > +       dab->fft.done = false;
> 
> You can init done in rcar_dab_fft_init(), too.

Will move

> 
> > +       ret = rcar_dab_fft_init(dab, fft_req);
> > +       if (ret) {
> > +               mutex_unlock(&dab->fft.lock);
> > +               return ret;
> > +       }
> > +
> > +       rcar_dab_fft_enable(dab);
> > +       wait_event_interruptible_timeout(dab->fft.wait, dab->fft.done,
> HZ);
> > +       if (!dab->fft.done) {
> 
> You can just check the return value of wait_event_interruptible_timeout().

Will change

> 
> > +               rcar_dab_fft_disable(dab);
> > +               ret = -EFAULT;
> 
> -ETIMEOUT?

Yeah, better, thanks

> 
> > +       } else if (copy_to_user(fft_req->output_address, dab-
> >fft.output_buffer,
> > +                               buffer_size)) {
> > +               ret = -EFAULT;
> > +       }
> > +
> > +       mutex_unlock(&dab->fft.lock);
> > +
> > +       return ret;
> > +}
> 
> > --- /dev/null
> > +++ b/include/uapi/linux/rcar_dab.h
> > @@ -0,0 +1,35 @@
> > +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> > +/*
> > + * R-Car Gen3 DAB user space interface data structures
> > + *
> > + * Copyright (C) 2021 Renesas Electronics Corporation
> > + *
> > + */
> > +#ifndef _RCAR_DAB_H_
> > +#define _RCAR_DAB_H_
> > +
> > +struct rcar_dab_fft_req {
> > +       int points;                     /*
> 
> unsigned int

Will change

> 
> > +                                        * The number of points to use.
> > +                                        * Legal values are 256, 512,
> 1024, and
> > +                                        * 2048.
> > +                                        */
> 
> Please use kerneldoc to document struct members.

Ok

> 
> > +       unsigned char ofdm_number;      /*
> > +                                        * Orthogonal Frequency Division
> > +                                        * Multiplexing (OFDM).
> > +                                        * Minimum value is 1, maximum
> value is
> > +                                        * 255.
> > +                                        */
> 
> Please make padding explicit.

Okay

> I'd also sort the members by decreasing size, i.e. pointers first.

Okay

> 
> > +       void __user *input_address;     /*
> 
> input_buf?

Sure

> 
> > +                                        * User space address for the
> input
> > +                                        * buffer.
> > +                                        */
> > +       void __user *output_address;    /*
> 
> output_buf?

Sure

> 
> > +                                        * User space address for the
> output
> > +                                        * buffer.
> > +                                        */
> > +};
> > +
> > +#define        RCAR_DAB_IOC_FFT                _IOWR(0x90, 1, struct
> rcar_dab_fft_req *)
> > +
> > +#endif /* _RCAR_DAB_H_ */

Thanks,
Fab

> 
> Gr{oetje,eeting}s,
> 
>                         Geert
> 
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-
> m68k.org
> 
> In personal conversations with technical people, I call myself a hacker.
> But
> when I'm talking to journalists I just say "programmer" or something like
> that.
>                                 -- Linus Torvalds


More information about the linux-arm-kernel mailing list