On Mon, Dec 09, 2019 at 07:53:24PM +0530, shubhrajyoti.datta@xxxxxxxxx wrote: > From: Shubhrajyoti Datta <shubhrajyoti.datta@xxxxxxxxxx> > > Added driver for Xilinx AXI performance monitor IP. > > Signed-off-by: Subbaraya Sundeep Bhatta <sbhatta@xxxxxxxxxx> > Signed-off-by: Shubhrajyoti Datta <shubhrajyoti.datta@xxxxxxxxxx> > --- > v2: > Updated the header > > drivers/uio/Kconfig | 12 ++ > drivers/uio/Makefile | 1 + > drivers/uio/uio_xilinx_apm.c | 358 +++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 371 insertions(+) > create mode 100644 drivers/uio/uio_xilinx_apm.c > > diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig > index 202ee81..de30312 100644 > --- a/drivers/uio/Kconfig > +++ b/drivers/uio/Kconfig > @@ -165,4 +165,16 @@ config UIO_HV_GENERIC > to network and storage devices from userspace. > > If you compile this as a module, it will be called uio_hv_generic. > + > +config UIO_XILINX_APM > + tristate "Xilinx AXI Performance Monitor driver" > + depends on MICROBLAZE || ARCH_ZYNQ || ARCH_ZYNQMP > + help > + This driver is developed for AXI Performance Monitor IP, designed to > + monitor AXI4 traffic for performance analysis of AXI bus in the > + system. Driver maps HW registers and parameters to userspace. > + > + To compile this driver as a module, choose M here; the module > + will be called uio_xilinx_apm. > + > endif > diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile > index c285dd2..b3464d8 100644 > --- a/drivers/uio/Makefile > +++ b/drivers/uio/Makefile > @@ -11,3 +11,4 @@ obj-$(CONFIG_UIO_PRUSS) += uio_pruss.o > obj-$(CONFIG_UIO_MF624) += uio_mf624.o > obj-$(CONFIG_UIO_FSL_ELBC_GPCM) += uio_fsl_elbc_gpcm.o > obj-$(CONFIG_UIO_HV_GENERIC) += uio_hv_generic.o > +obj-$(CONFIG_UIO_XILINX_APM) += uio_xilinx_apm.o > diff --git a/drivers/uio/uio_xilinx_apm.c b/drivers/uio/uio_xilinx_apm.c > new file mode 100644 > index 0000000..3f69922 > --- /dev/null > +++ b/drivers/uio/uio_xilinx_apm.c > @@ -0,0 +1,358 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Xilinx AXI Performance Monitor > + * > + * Description: > + * This driver is developed for AXI Performance Monitor IP, > + * designed to monitor AXI4 traffic for performance analysis > + * of AXI bus in the system. Driver maps HW registers and parameters > + * to userspace. Userspace need not clear the interrupt of IP since > + * driver clears the interrupt. > + * > + * Copyright (c) 2019 Xilinx Inc. > + */ > + > +#include <linux/clk.h> > +#include <linux/io.h> > +#include <linux/module.h> > +#include <linux/of.h> > +#include <linux/platform_device.h> > +#include <linux/pm_runtime.h> > +#include <linux/slab.h> > +#include <linux/uio_driver.h> > + > +#define XAPM_IS_OFFSET 0x0038 /* Interrupt Status Register */ > +#define DRV_NAME "xilinxapm_uio" > +#define DRV_VERSION "1.0" No need for a version, the code is in the kernel tree, use the version of the kernel instead please. > +#define UIO_DUMMY_MEMSIZE 4096 > +#define XAPM_MODE_ADVANCED 1 > +#define XAPM_MODE_PROFILE 2 > +#define XAPM_MODE_TRACE 3 > + > +/** > + * struct xapm_param - HW parameters structure > + * @mode: Mode in which APM is working > + * @maxslots: Maximum number of Slots in APM > + * @eventcnt: Event counting enabled in APM > + * @eventlog: Event logging enabled in APM > + * @sampledcnt: Sampled metric counters enabled in APM > + * @numcounters: Number of counters in APM > + * @metricwidth: Metric Counter width (32/64) > + * @sampledwidth: Sampled metric counter width > + * @globalcntwidth: Global Clock counter width > + * @scalefactor: Scaling factor > + * @isr: Interrupts info shared to userspace > + * @is_32bit_filter: Flags for 32bit filter > + * @clk: Clock handle > + */ > +struct xapm_param { > + u32 mode; > + u32 maxslots; > + u32 eventcnt; > + u32 eventlog; > + u32 sampledcnt; > + u32 numcounters; > + u32 metricwidth; > + u32 sampledwidth; > + u32 globalcntwidth; > + u32 scalefactor; > + u32 isr; > + bool is_32bit_filter; > + struct clk *clk; You seem to copy this structure to hardware? How? This is not a definition of a proper hardware interface. Or are you copying it to userspace? If so, even worse! This is not how to properly define such a structure at all. > +}; > + > +/** > + * struct xapm_dev - Global driver structure > + * @info: uio_info structure > + * @param: xapm_param structure > + * @regs: IOmapped base address > + */ > +struct xapm_dev { > + struct uio_info info; > + struct xapm_param param; > + void __iomem *regs; > +}; > + > +/** > + * xapm_handler - Interrupt handler for APM > + * @irq: IRQ number > + * @info: Pointer to uio_info structure > + * > + * Return: Always returns IRQ_HANDLED > + */ > +static irqreturn_t xapm_handler(int irq, struct uio_info *info) > +{ > + struct xapm_dev *xapm = (struct xapm_dev *)info->priv; DO you need to cast? > + void *ptr; > + > + ptr = (unsigned long *)xapm->info.mem[1].addr; No need to cast. And ptr is a void, not unsigned long *. > + /* Clear the interrupt and copy the ISR value to userspace */ > + xapm->param.isr = readl(xapm->regs + XAPM_IS_OFFSET); > + writel(xapm->param.isr, xapm->regs + XAPM_IS_OFFSET); > + memcpy(ptr, &xapm->param, sizeof(struct xapm_param)); Where did you just copy this memory to? > + > + return IRQ_HANDLED; > +} > + > +/** > + * xapm_getprop - Retrieves dts properties to param structure > + * @pdev: Pointer to platform device > + * @param: Pointer to param structure > + * > + * Returns: '0' on success and failure value on error > + */ > +static int xapm_getprop(struct platform_device *pdev, struct xapm_param *param) > +{ > + u32 mode = 0; > + int ret; > + struct device_node *node; > + > + node = pdev->dev.of_node; > + > + /* Retrieve required dts properties and fill param structure */ > + ret = of_property_read_u32(node, "xlnx,enable-profile", &mode); > + if (ret < 0) > + dev_info(&pdev->dev, "no property xlnx,enable-profile\n"); If it's an error, make it dev_err(). No need to be noisy otherwise. Same for all of these. > + else if (mode) > + param->mode = XAPM_MODE_PROFILE; > + > + ret = of_property_read_u32(node, "xlnx,enable-trace", &mode); > + if (ret < 0) > + dev_info(&pdev->dev, "no property xlnx,enable-trace\n"); > + else if (mode) > + param->mode = XAPM_MODE_TRACE; > + > + ret = of_property_read_u32(node, "xlnx,num-monitor-slots", > + ¶m->maxslots); > + if (ret < 0) { > + dev_err(&pdev->dev, "no property xlnx,num-monitor-slots"); Doesn't of_property_read_u32() print an error if it can not be found? Don't duplicate the message please. Same for all of these. > + return ret; > + } > + > + ret = of_property_read_u32(node, "xlnx,enable-event-count", > + ¶m->eventcnt); > + if (ret < 0) { > + dev_err(&pdev->dev, "no property xlnx,enable-event-count"); > + return ret; > + } > + > + ret = of_property_read_u32(node, "xlnx,enable-event-log", > + ¶m->eventlog); > + if (ret < 0) { > + dev_err(&pdev->dev, "no property xlnx,enable-event-log"); > + return ret; > + } > + > + ret = of_property_read_u32(node, "xlnx,have-sampled-metric-cnt", > + ¶m->sampledcnt); > + if (ret < 0) { > + dev_err(&pdev->dev, "no property xlnx,have-sampled-metric-cnt"); > + return ret; > + } > + > + ret = of_property_read_u32(node, "xlnx,num-of-counters", > + ¶m->numcounters); > + if (ret < 0) { > + dev_err(&pdev->dev, "no property xlnx,num-of-counters"); > + return ret; > + } > + > + ret = of_property_read_u32(node, "xlnx,metric-count-width", > + ¶m->metricwidth); > + if (ret < 0) { > + dev_err(&pdev->dev, "no property xlnx,metric-count-width"); > + return ret; > + } > + > + ret = of_property_read_u32(node, "xlnx,metrics-sample-count-width", > + ¶m->sampledwidth); > + if (ret < 0) { > + dev_err(&pdev->dev, "no property metrics-sample-count-width"); > + return ret; > + } > + > + ret = of_property_read_u32(node, "xlnx,global-count-width", > + ¶m->globalcntwidth); > + if (ret < 0) { > + dev_err(&pdev->dev, "no property xlnx,global-count-width"); > + return ret; > + } > + > + ret = of_property_read_u32(node, "xlnx,metric-count-scale", > + ¶m->scalefactor); > + if (ret < 0) { > + dev_err(&pdev->dev, "no property xlnx,metric-count-scale"); > + return ret; > + } > + > + param->is_32bit_filter = of_property_read_bool(node, > + "xlnx,id-filter-32bit"); > + > + return 0; > +} > + > +/** > + * xapm_probe - Driver probe function > + * @pdev: Pointer to the platform_device structure > + * > + * Returns: '0' on success and failure value on error > + */ > + Why the blank line? Why the kernel doc for static functions? > +static int xapm_probe(struct platform_device *pdev) > +{ > + struct xapm_dev *xapm; > + struct resource *res; > + int irq; > + int ret; > + void *ptr; > + > + xapm = devm_kzalloc(&pdev->dev, (sizeof(struct xapm_dev)), GFP_KERNEL); > + if (!xapm) > + return -ENOMEM; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + xapm->regs = devm_ioremap_resource(&pdev->dev, res); > + if (IS_ERR(xapm->regs)) { > + dev_err(&pdev->dev, "unable to iomap registers\n"); > + return PTR_ERR(xapm->regs); > + } > + > + xapm->param.clk = devm_clk_get(&pdev->dev, NULL); > + if (IS_ERR(xapm->param.clk)) { > + if (PTR_ERR(xapm->param.clk) != -EPROBE_DEFER) > + dev_err(&pdev->dev, "axi clock error\n"); > + return PTR_ERR(xapm->param.clk); > + } > + > + ret = clk_prepare_enable(xapm->param.clk); > + if (ret) { > + dev_err(&pdev->dev, "Unable to enable clock.\n"); > + return ret; > + } > + pm_runtime_get_noresume(&pdev->dev); > + pm_runtime_set_active(&pdev->dev); > + pm_runtime_enable(&pdev->dev); > + /* Initialize mode as Advanced so that if no mode in dts, default > + * is Advanced > + */ Odd commenting style, this isn't the network stack :) > + xapm->param.mode = XAPM_MODE_ADVANCED; > + ret = xapm_getprop(pdev, &xapm->param); > + if (ret < 0) > + goto err_clk_dis; > + > + xapm->info.mem[0].name = "xilinx_apm"; Not driver name? Why have that #define then? > + xapm->info.mem[0].addr = res->start; > + xapm->info.mem[0].size = resource_size(res); > + xapm->info.mem[0].memtype = UIO_MEM_PHYS; > + > + xapm->info.mem[1].addr = (unsigned long)kzalloc(UIO_DUMMY_MEMSIZE, > + GFP_KERNEL); > + ptr = (unsigned long *)xapm->info.mem[1].addr; > + xapm->info.mem[1].size = UIO_DUMMY_MEMSIZE; > + xapm->info.mem[1].memtype = UIO_MEM_LOGICAL; > + > + xapm->info.name = "axi-pmon"; > + xapm->info.version = DRV_VERSION; > + > + irq = platform_get_irq(pdev, 0); > + if (irq < 0) { > + dev_err(&pdev->dev, "unable to get irq\n"); > + ret = irq; > + goto err_clk_dis; > + } > + > + xapm->info.irq = irq; > + xapm->info.handler = xapm_handler; > + xapm->info.priv = xapm; > + xapm->info.irq_flags = IRQF_SHARED; > + > + memcpy(ptr, &xapm->param, sizeof(struct xapm_param)); Where did you just copy this to? > + > + ret = uio_register_device(&pdev->dev, &xapm->info); > + if (ret < 0) { > + dev_err(&pdev->dev, "unable to register to UIO\n"); > + goto err_clk_dis; > + } > + > + platform_set_drvdata(pdev, xapm); > + > + dev_info(&pdev->dev, "Probed Xilinx APM\n"); Again, this should be quiet if all goes well, didn't I say that before? Finally, why do you need a UIO driver at all here? Why can't the current ones work properly for you? thanks, greg k-h