[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <CAN37VV7XuiKZY17vFZWJJ5Qx2AtOHNguq=P88Y2PbbTo0LTkfw@mail.gmail.com>
Date: Sat, 6 Dec 2025 23:29:53 +0530
From: Mayuresh Chitale <mchitale@...tanamicro.com>
To: Bo Gan <ganboing@...il.com>
Cc: Anup Patel <apatel@...tanamicro.com>, Rob Herring <robh@...nel.org>,
Krzysztof Kozlowski <krzk+dt@...nel.org>, Conor Dooley <conor+dt@...nel.org>,
Paul Walmsley <paul.walmsley@...ive.com>, Palmer Dabbelt <palmer@...belt.com>,
Greg KH <gregkh@...uxfoundation.org>,
Alexander Shishkin <alexander.shishkin@...ux.intel.com>, Ian Rogers <irogers@...gle.com>,
Mark Rutland <mark.rutland@....com>, devicetree@...r.kernel.org,
Alexandre Ghiti <alex@...ti.fr>, Atish Patra <atish.patra@...ux.dev>,
Peter Zijlstra <peterz@...radead.org>, Anup Patel <anup@...infault.org>,
Adrian Hunter <adrian.hunter@...el.com>, linux-kernel@...r.kernel.org,
Ingo Molnar <mingo@...hat.com>, Jiri Olsa <jolsa@...nel.org>,
Mayuresh Chitale <mchitale@...il.com>, Namhyung Kim <namhyung@...nel.org>,
linux-riscv@...ts.infradead.org, Andrew Jones <ajones@...tanamicro.com>,
Liang Kan <kan.liang@...ux.intel.com>
Subject: Re: [PATCH v2 07/12] rvtrace: Add trace ramsink driver
Hi Bo,
Thanks,
Mayuresh.
On Wed, Dec 3, 2025 at 6:36 AM Bo Gan <ganboing@...il.com> wrote:
>
> Hi Mayuresh,
>
> Thanks for your reply. My comments below.
>
> On 12/2/25 03:47, Mayuresh Chitale wrote:
> > Hi Bo,
> >
> > On Sun, Nov 30, 2025 at 1:17 PM Bo Gan <ganboing@...il.com> wrote:
> >>
> >> Hi Anup,
> >>
> >> My comments below:
> >>
> >> On 11/1/25 08:42, Anup Patel wrote:
> >>> From: Mayuresh Chitale <mchitale@...tanamicro.com>
> >>>
> >>> Add initial implementation of RISC-V trace ramsink driver. The ramsink
> >>> is defined in the RISC-V Trace Control Interface specification.
> >>>
> >>> Co-developed-by: Anup Patel <apatel@...tanamicro.com>
> >>> Signed-off-by: Anup Patel <apatel@...tanamicro.com>
> >>> Signed-off-by: Mayuresh Chitale <mchitale@...tanamicro.com>
> >>> ---
> >>> drivers/hwtracing/rvtrace/Kconfig | 9 +
> >>> drivers/hwtracing/rvtrace/Makefile | 1 +
> >>> drivers/hwtracing/rvtrace/rvtrace-ramsink.c | 262 ++++++++++++++++++++
> >>> 3 files changed, 272 insertions(+)
> >>> create mode 100644 drivers/hwtracing/rvtrace/rvtrace-ramsink.c
> >>>
> >>> diff --git a/drivers/hwtracing/rvtrace/Kconfig b/drivers/hwtracing/rvtrace/Kconfig
> >>> index ba35c05f3f54..0577f9acb858 100644
> >>> --- a/drivers/hwtracing/rvtrace/Kconfig
> >>> +++ b/drivers/hwtracing/rvtrace/Kconfig
> >>> @@ -21,3 +21,12 @@ config RVTRACE_ENCODER
> >>> default y
> >>> help
> >>> This driver provides support for RISC-V Trace Encoder component.
> >>> +
> >>> +config RVTRACE_RAMSINK
> >>> + tristate "RISC-V Trace Ramsink driver"
> >>> + depends on RVTRACE
> >>> + select DMA_SHARED_BUFFER
> >>> + default y
> >>> + help
> >>> + This driver provides support for Risc-V E-Trace Ramsink
> >>> + component.
> >>> diff --git a/drivers/hwtracing/rvtrace/Makefile b/drivers/hwtracing/rvtrace/Makefile
> >>> index f320693a1fc5..122e575da9fb 100644
> >>> --- a/drivers/hwtracing/rvtrace/Makefile
> >>> +++ b/drivers/hwtracing/rvtrace/Makefile
> >>> @@ -3,3 +3,4 @@
> >>> obj-$(CONFIG_RVTRACE) += rvtrace.o
> >>> rvtrace-y := rvtrace-core.o rvtrace-platform.o
> >>> obj-$(CONFIG_RVTRACE_ENCODER) += rvtrace-encoder.o
> >>> +obj-$(CONFIG_RVTRACE_RAMSINK) += rvtrace-ramsink.o
> >>> diff --git a/drivers/hwtracing/rvtrace/rvtrace-ramsink.c b/drivers/hwtracing/rvtrace/rvtrace-ramsink.c
> >>> new file mode 100644
> >>> index 000000000000..676344c9387c
> >>> --- /dev/null
> >>> +++ b/drivers/hwtracing/rvtrace/rvtrace-ramsink.c
> >>> @@ -0,0 +1,262 @@
> >>> +// SPDX-License-Identifier: GPL-2.0
> >>> +/*
> >>> + * Copyright (c) 2025 Ventana Micro Systems Inc.
> >>> + */
> >>> +
> >>> +#include <linux/device.h>
> >>> +#include <linux/platform_device.h>
> >>> +#include <linux/property.h>
> >>> +#include <linux/dma-mapping.h>
> >>> +#include <linux/rvtrace.h>
> >>> +#include <linux/types.h>
> >>> +#include <linux/sizes.h>
> >>> +
> >>> +#define RVTRACE_RAMSINK_STARTLOW_OFF 0x010
> >>> +#define RVTRACE_RAMSINK_STARTHIGH_OFF 0x014
> >>> +#define RVTRACE_RAMSINK_LIMITLOW_OFF 0x018
> >>> +#define RVTRACE_RAMSINK_LIMITHIGH_OFF 0x01c
> >>> +#define RVTRACE_RAMSINK_WPLOW_OFF 0x020
> >>> +#define RVTRACE_RAMSINK_WPLOW_WRAP 0x1
> >>
> >> nit: wrong spacing
> >>
> >>> +#define RVTRACE_RAMSINK_WPHIGH_OFF 0x024
> >>> +#define RVTRACE_RAMSINK_RPLOW_OFF 0x028
> >>> +#define RVTRACE_RAMSINK_RPHIGH_OFF 0x02c
> >>> +
> >>> +struct rvtrace_ramsink_priv {
> >>> + size_t size;
> >>> + void *va;
> >>> + dma_addr_t start;
> >>> + dma_addr_t end;
> >>> + /* WP from prev iteration */
> >>> + dma_addr_t prev_head;
> >>> +};
> >>> +
> >>> +struct trace_buf {
> >>> + void *base;
> >>> + size_t size;
> >>> + long cur;
> >>> + size_t len;
> >>> +};
> >>> +
> >>> +static int rvtrace_ramsink_stop(struct rvtrace_component *comp)
> >>> +{
> >>> + return rvtrace_comp_is_empty(comp);
> >>> +}
> >>> +
> >>
> >> We should first set trRamEnable to 0, poll it, then poll trRamEmpty. It's
> >> essentially the same as encoder, so perhaps move it to core.c. Here we're
> >> just checking whether it's empty, which is wrong. We're also doing similar
> >> things in encoder.c Should fix that as well.
> > This code is called to just stop all components in a path which means:
> > - for encoders the driver clears the trTeInstTracing bit and then
> > polls for empty bit to be cleared
> > - for ramsinks the driver just polls for the empty bit to be cleared
> > More on this below.
>
> Can we rename it to something like rvtrace_comp_poll_empty(...) for clarity?
> This name kind of confused me into thinking it's just a check.
Ok.
>
> >>
> >>> +static void tbuf_to_pbuf_copy(struct trace_buf *src, struct trace_buf *dst)
> >>> +{
> >>> + int bytes_dst, bytes_src, bytes;
> >>> + void *dst_addr, *src_addr;
> >>> +
> >>> + while (src->size) {
> >>> + src_addr = src->base + src->cur;
> >>> + dst_addr = dst->base + dst->cur;
> >>> +
> >>> + if (dst->len - dst->cur < src->size)
> >>> + bytes_dst = dst->len - dst->cur;
> >>> + else
> >>> + bytes_dst = src->size;
> >>> + if (src->len - src->cur < src->size)
> >>> + bytes_src = src->len - src->cur;
> >>> + else
> >>> + bytes_src = src->size;
> >>> + bytes = bytes_dst < bytes_src ? bytes_dst : bytes_src;
> >>> + memcpy(dst_addr, src_addr, bytes);
> >>> + dst->cur = (dst->cur + bytes) % dst->len;
> >>> + src->cur = (src->cur + bytes) % src->len;
> >>> + src->size -= bytes;
> >>> + }
> >>> +}
> >>> +
> >>> +static size_t rvtrace_ramsink_copyto_auxbuf(struct rvtrace_component *comp,
> >>> + struct rvtrace_perf_auxbuf *buf)
> >>> +{
> >>> + struct rvtrace_ramsink_priv *priv = dev_get_drvdata(&comp->dev);
> >>> + struct trace_buf src, dst;
> >>> + u32 wp_low, wp_high;
> >>> + u64 buf_cur_head;
> >>> + size_t size;
> >>> +
> >>> + wp_low = rvtrace_read32(comp->pdata, RVTRACE_RAMSINK_WPLOW_OFF);
> >>> + wp_high = rvtrace_read32(comp->pdata, RVTRACE_RAMSINK_WPHIGH_OFF);
> >>> + buf_cur_head = (u64)(wp_high) << 32 | (wp_low & ~RVTRACE_RAMSINK_WPLOW_WRAP);
> >>> +
> >>> + if (buf_cur_head == priv->prev_head)
> >>> + return 0;
> >>
> >> cur_head == prev_head could mean that we've wrapped around and stopped at
> >> the exact position. Thus, we need to check for the wrapped around bit. If
> >> set, then this case can be merged into buf_cur_head <= priv->prev_head
> >> below.
> > cur_head is the current value in the wp registers whereas prev_head is
> > the value in wp registers from the previous iteration which is saved
> > explicitly by software. So prev_head can't change dynamically.
> >
> >>
> >> For cur_head > prev_head case, we also need to check for wrap-around. If
> >> wrapped-around, we actually filled the entire buffer, not just cur - prev
> >> We also should consider the possibility that the buffer has been wrapped
> >> around multiple times. In all cases, maintaining a prev_head seems to
> >> complicate things, so why don't we just reset the WP to priv->start each
> >> time we start the ramsink component and get rid of prev_head? This way,
> >> we'll always have cur >= start, and depending on wrapped bit, we either
> >> get the whole buffer, or cur - start.
> > Currently the software never clears trRamWrap bit because as per the spec:
> > - It is only set to 0 if trRamWPLow is written
> > - After trace is enabled and active , the trace tool should NOT write
> > any of trRamStart/Limit/WP?? registers.
>
> I think you missed my point. I'm not suggesting that we should change the
> register values when trace is active. I'm saying that there's no need to
> maintain a prev_head. For every trace session, we can just start fresh,
> and reset WP to Start before trace starts. This way we don't need to deal
> with the complex conditions of cur_head > prev_head, because you'll also
> need to deal with wrap around, and the if condition will be complex. The
> trace can wrap around and over-write old traces anyway, so I don't see a
> value of keeping track of prev_head.
>
We can't write the 'Start' value into WP if trRamEnable is set (please
see my previous comment). In any case in v3, I am changing how the
path gets stopped. After that we should be able to reset WP.
> > Let me test this change to disable all the components (instead of just
> > stopping trace) every time we want to copy data to perf buffer. This
> > should let us use the trRamWrap bit as expected. Multiple wraparounds
> > can't be avoided unless StopOnWrap was set. I will update the ramsink
> > driver to set StopOnWrap based on the implementation id.
> >>
> >
> >>> +
> >>> + dst.base = buf->base;
> >>> + dst.len = buf->length;
> >>> + dst.cur = buf->pos;
> >>> +
> >>> + src.base = priv->va;
> >>> + src.len = priv->end - priv->start;
> >>> + if (buf_cur_head > priv->prev_head) {
> >>> + src.size = buf_cur_head - priv->prev_head;
> >>> + } else {
> >>> + src.size = priv->end - priv->prev_head;
> >>> + src.size += buf_cur_head - priv->start;
> >>> + }
> >>> +
> >>> + src.cur = buf_cur_head - priv->start;
> >>> + size = src.size;
> >>> + tbuf_to_pbuf_copy(&src, &dst);
> >>> + buf->pos = dst.cur;
> >>> + priv->prev_head = buf_cur_head;
> >>> +
> >>> + return size;
> >>> +}
> >>> +
> >>> +static int rvtrace_ramsink_setup_buf(struct rvtrace_component *comp)
> >>> +{
> >>> + struct rvtrace_ramsink_priv *priv = dev_get_drvdata(&comp->dev);
> >>> + u64 start_min, limit_max, end;
> >>> + u32 low, high;
> >>> +
> >>> + /* Probe min and max values for start and limit registers */
> >>> + rvtrace_write32(comp->pdata, 0, RVTRACE_RAMSINK_STARTLOW_OFF);
> >>> + rvtrace_write32(comp->pdata, 0, RVTRACE_RAMSINK_STARTHIGH_OFF);
> >>> + low = rvtrace_read32(comp->pdata, RVTRACE_RAMSINK_STARTLOW_OFF);
> >>> + high = rvtrace_read32(comp->pdata, RVTRACE_RAMSINK_STARTHIGH_OFF);
> >>> + start_min = (u64)(high) << 32 | low;
> >>> +
> >>> + rvtrace_write32(comp->pdata, 0xffffffff, RVTRACE_RAMSINK_LIMITLOW_OFF);
> >>> + rvtrace_write32(comp->pdata, 0xffffffff, RVTRACE_RAMSINK_LIMITHIGH_OFF);
> >>> + low = rvtrace_read32(comp->pdata, RVTRACE_RAMSINK_LIMITLOW_OFF);
> >>> + high = rvtrace_read32(comp->pdata, RVTRACE_RAMSINK_LIMITHIGH_OFF);
> >>> + limit_max = (u64)(high) << 32 | low;
> >>> +
> >>> + if (priv->end < start_min) {
> >>
> >> Where was priv->end initialized before this check?
> > I will move it before the check.
> >>
> >>> + dev_err(&comp->dev, "DMA memory not addressable by device\n");
> >>> + return -EINVAL;
> >>> + }
> >>> +
> >>> + /* Setup ram sink start addresses */
> >>> + if (priv->start < start_min) {
> >>
> >> No checks for start_min >= priv->end?
> >>
> >>> + dev_warn(&comp->dev, "Ramsink start address updated from %llx to %llx\n",
> >>> + priv->start, start_min);
> >>> + priv->va += start_min - priv->start;
> >>> + priv->start = start_min;
> >>> + }
> >>> +
> >>> + priv->prev_head = priv->start;
> >>> + priv->end = priv->start + priv->size;
> >>> + rvtrace_write32(comp->pdata, lower_32_bits(priv->start), RVTRACE_RAMSINK_STARTLOW_OFF);
> >>> + rvtrace_write32(comp->pdata, upper_32_bits(priv->start), RVTRACE_RAMSINK_STARTHIGH_OFF);
> >>> + rvtrace_write32(comp->pdata, lower_32_bits(priv->start), RVTRACE_RAMSINK_WPLOW_OFF);
> >>> + rvtrace_write32(comp->pdata, upper_32_bits(priv->start), RVTRACE_RAMSINK_WPHIGH_OFF);
> >>> + /* Setup ram sink limit addresses */
> >>> + if (priv->end > limit_max) {
> >>> + dev_warn(&comp->dev, "Ramsink limit address updated from %llx to %llx\n", priv->end,
> >>> + limit_max);
> >>> + priv->end = limit_max;
> >>> + priv->size = priv->end - priv->start;
> >>> + }
> >>> +
> >>> + /* Limit address needs to be set to end - 4 to avoid overflow */
> >>> + end = priv->end - 4;
> >>
> >> Should not hard-code 4, instead, do write32(0xfffffff, RamLimit), then
> >> ffs(RamLimit) to probe for the the write width, aka, "A" value in Table
> >> 47. Typical Trace RAM Sink Configuration in the Spec.
> > I dont think 'A' can be probed that way because as per section 7.1:
> > "Not every value may be settable in trRamStart/Limit registers. Value
> > written may be trimmed (for example aligned on a particular 2^N
> > boundary)". So writing 0xffffffff to limit register will reveal the
> > boundary alignment but not 'A'. I think 'A' needs to be specified by
> > an implementation with default as 4. I will change it accordingly.
> >
>
> Quoting from spec:
>
> "Value A means alignment which depends on memory access width. If we
> have memory access width of 32-bits, A=4 and value of trRamLimit
> register should be 0x…FC. Some implementations may impose bigger
> alignment of trace data (to allow more efficient transfer rates)
> for SMEM mode. For SRAM mode A must be 4 as access to trace via
> trRamData is always 32-bits wide "
>
> the 4 byte of A value is only for SRAM mode, not SMEM mode. My inter-
> pretation of the spec is that this can be detected by writing -1 to
> Limit, and detect M, A as follows.
No. The text above doesn't talk about probing the value of 'A', just
that 'A' is implementation specific.
>
> 0x11110000011111000
> ^ ^
> | |
> M value A value
>
> A = 1 << (__builtin_ffs(v) - 1)
> M = 1 << __builtin_ctz(v + A - 1)
>
> I'll start a thread on the DTPM mailing list to confirm this.
OK.
BTW,
> my P550 N-trace (pre-ratified) ramsink as A=8.
You can set it by defining a new id for P550 N-trace in the
rvtrace_component_impid enum. Please refer rvtrace.h for more details.
>
> >>
> >>> + rvtrace_write32(comp->pdata, lower_32_bits(end), RVTRACE_RAMSINK_LIMITLOW_OFF);
> >>> + rvtrace_write32(comp->pdata, upper_32_bits(end), RVTRACE_RAMSINK_LIMITHIGH_OFF);
> >>> + low = rvtrace_read32(comp->pdata, RVTRACE_RAMSINK_LIMITLOW_OFF);
> >>> + high = rvtrace_read32(comp->pdata, RVTRACE_RAMSINK_LIMITHIGH_OFF);
> >>> + end = (u64)(high) << 32 | low;
> >>> + if (end != (priv->end - 4)) {
> >>> + dev_warn(&comp->dev, "Ramsink limit address updated from %llx to %llx\n", priv->end,
> >>> + end);
> >>> + priv->end = end;
> >>> + priv->size = priv->end - priv->start;
> >>
> >> Actually the RamLimit read is not the real limit. It's Limit - "A" (the
> >> write width).
> >>
> >>> + }
> >>> +
> >>> + return 0;
> >>> +}
> >>
> >> I think overall we should simplify this setup_buf function. For RamStart
> >> alone, there're alignment + min_addr requirement, which is very unlikely
> >> that the lowest valid address happens to be within the range that dma-
> >> allocated in the caller (when you need to adjust priv->start). Thus, to
> >> do it properly, we first need to detect the min_addr + alignment *before*
> >> dma-alloc, and pass such requirements into dma-alloc, then depending on
> >> what dma-alloc returns, detect the "M" value by interrogating RamLimit.
> >> I'm not sure if dma-alloc can be passed a hint address that whatever
> >> returned must be higher, and whether it can enforce random alignment, so
> >> may be we can just program RamStart/Limit based on what dma-alloc gives
> >> and error out if value is not retained. We do however need to probe for
> >> write width ("A" value). It's usually 4 or 8, but it does vary per-impl,
> >> and remember, the total buffer size is still 1MB, not 1MB - 4 or -8.
> >
> > I think the issue is that the physical memory region returned by
> > dma_alloc* might not be addressable by the hw and so the start / end
> > addresses are already trimmed in the code accordingly but I agree this
> > can be handled differently. For e.g
> > - probe max RamLimit value .
> > - set the dma_mask to the probed value
> > - dma_alloc*
> > - further address checks
> >
> >>
> >>> +
> >>> +static int rvtrace_ramsink_setup(struct rvtrace_component *comp)
> >>> +{
> >>> + struct device *pdev = comp->pdata->dev;
> >>> + struct rvtrace_ramsink_priv *priv;
> >>> +
> >>> + priv = devm_kzalloc(&comp->dev, sizeof(*priv), GFP_KERNEL);
> >>> + if (!priv)
> >>> + return -ENOMEM;
> >>> +
> >>> + dev_set_drvdata(&comp->dev, priv);
> >>> +
> >>> + /* Derive RAM sink memory size based on component implementation ID */
> >>> + switch (comp->pdata->impid) {
> >>> + default:
> >>> + priv->size = SZ_1M;
> >>> + break;
> >>> + }
> >>> +
> >>> + priv->va = dma_alloc_coherent(pdev, priv->size, &priv->start, GFP_KERNEL);
> >>> + if (!priv->va)
> >>> + return -ENOMEM;
> >>> +
> >>> + return rvtrace_ramsink_setup_buf(comp);
> >>> +}
> >>> +
> >>> +static void rvtrace_ramsink_cleanup(struct rvtrace_component *comp)
> >>> +{
> >>> + struct rvtrace_ramsink_priv *priv = dev_get_drvdata(&comp->dev);
> >>> +
> >>> + dma_free_coherent(&comp->dev, priv->size, priv->va, priv->start);
> >>> +}
> >>> +
> >>> +static int rvtrace_ramsink_probe(struct rvtrace_component *comp)
> >>> +{
> >>> + int ret;
> >>> +
> >>> + ret = rvtrace_ramsink_setup(comp);
> >>> + if (ret)
> >>> + return dev_err_probe(&comp->dev, ret, "failed to setup ramsink.\n");
> >>> +
> >>> + ret = rvtrace_enable_component(comp);
> >>> + if (ret)
> >>> + return dev_err_probe(&comp->dev, ret, "failed to enable ramsink.\n");
> >>> +
> >>> + return ret;
> >>> +}
> >>
> >> In probe, we should detect and set it to SMEM mode. The default is SRAM
> >> mode.
> > Actually the default is not set but I agree this needs to be handled
> > differently. We can make this an implementation defined parameter and
> > then cross check with trRamImpl register during probe.
>
> You are right. The reset value of trRamMode is Undef. Thus, we do need to
> test and set it to SMEM mode. (It's a WARL field)
>
> >>
> >>> +
> >>> +static void rvtrace_ramsink_remove(struct rvtrace_component *comp)
> >>> +{
> >>> + int ret;
> >>> +
> >>> + ret = rvtrace_disable_component(comp);
> >>> + if (ret)
> >>> + dev_err(&comp->dev, "failed to disable ramsink.\n");
> >>> +
> >>> + rvtrace_ramsink_cleanup(comp);
> >>> +}
> >>> +
> >>> +static struct rvtrace_component_id rvtrace_ramsink_ids[] = {
> >>> + { .type = RVTRACE_COMPONENT_TYPE_RAMSINK,
> >>> + .version = rvtrace_component_mkversion(1, 0), },
> >>> + {},
> >>> +};
> >>> +
> >>> +static struct rvtrace_driver rvtrace_ramsink_driver = {
> >>> + .id_table = rvtrace_ramsink_ids,
> >>> + .copyto_auxbuf = rvtrace_ramsink_copyto_auxbuf,
> >>
> >> Should have a .start function like the encoder (setting trRamEnable),
> >> and reset the WP to priv->start, as suggested above.
> > The active and enable bits for all components are handled via generic
> > rvtrace_enable_component/rvtrace_disable_component functions. When all
> > components are disabled before copying data to perf then WP would be
> > reset to priv->start as a part of copy_to_auxbuf callback.
>
> Disagree. You can't rely on enable/disable_component for ramsink. It's
> the same as encoder, you need proper start/stop. If the RamEnable bit
> is not cleared, trace may never gets flushed, and you'll never get
> RamEmpty. Also you can't access WP without clearing RamEnable.
>
> I'd also think it's more appropriate to reset WP in start callback. As
> said earlier, new traces can overwrite already captured ones anyway,
> so why not reset WP on each start.
Please see the comment above.
> Future enhancements we can support
> directly reading out the trace buffer when the encoder/sink is inactive,
> so better just leave the WP where it was after stopping.
>
> Bo
>
> >>
> >>> + .stop = rvtrace_ramsink_stop,
> >>> + .probe = rvtrace_ramsink_probe,
> >>> + .remove = rvtrace_ramsink_remove,
> >>> + .driver = {
> >>> + .name = "rvtrace-ramsink",
> >>> + },
> >>> +};
> >>> +
> >>> +static int __init rvtrace_ramsink_init(void)
> >>> +{
> >>> + return rvtrace_register_driver(&rvtrace_ramsink_driver);
> >>> +}
> >>> +
> >>> +static void __exit rvtrace_ramsink_exit(void)
> >>> +{
> >>> + rvtrace_unregister_driver(&rvtrace_ramsink_driver);
> >>> +}
> >>> +
> >>> +module_init(rvtrace_ramsink_init);
> >>> +module_exit(rvtrace_ramsink_exit);
> >>> +
> >>> +/* Module information */
> >>> +MODULE_AUTHOR("Mayuresh Chitale <mchitale@...tanamicro.com>");
> >>> +MODULE_DESCRIPTION("RISC-V Trace Ramsink Driver");
> >>> +MODULE_LICENSE("GPL");
> >>
> >> Bo
> >>
>
Powered by blists - more mailing lists