2024-04-10 14:46:52 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/amba/bus.h>
|
|
|
|
#include <linux/bitfield.h>
|
|
|
|
#include <linux/coresight.h>
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/io.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/of.h>
|
|
|
|
#include <linux/platform_device.h>
|
|
|
|
|
|
|
|
#include "coresight-priv.h"
|
|
|
|
#include "coresight-tpda.h"
|
|
|
|
#include "coresight-trace-id.h"
|
2024-10-11 14:13:48 +00:00
|
|
|
#include "coresight-tpdm.h"
|
2024-04-10 14:46:52 +00:00
|
|
|
|
|
|
|
DEFINE_CORESIGHT_DEVLIST(tpda_devs, "tpda");
|
|
|
|
|
2024-04-10 14:46:52 +00:00
|
|
|
static bool coresight_device_is_tpdm(struct coresight_device *csdev)
|
|
|
|
{
|
|
|
|
return (csdev->type == CORESIGHT_DEV_TYPE_SOURCE) &&
|
|
|
|
(csdev->subtype.source_subtype ==
|
|
|
|
CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM);
|
|
|
|
}
|
|
|
|
|
2024-10-11 14:13:48 +00:00
|
|
|
static void tpda_clear_element_size(struct coresight_device *csdev)
|
|
|
|
{
|
|
|
|
struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
|
|
|
|
|
|
|
drvdata->dsb_esize = 0;
|
|
|
|
drvdata->cmb_esize = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tpda_set_element_size(struct tpda_drvdata *drvdata, u32 *val)
|
|
|
|
{
|
|
|
|
/* Clear all relevant fields */
|
|
|
|
*val &= ~(TPDA_Pn_CR_DSBSIZE | TPDA_Pn_CR_CMBSIZE);
|
|
|
|
|
|
|
|
if (drvdata->dsb_esize == 64)
|
|
|
|
*val |= TPDA_Pn_CR_DSBSIZE;
|
|
|
|
else if (drvdata->dsb_esize == 32)
|
|
|
|
*val &= ~TPDA_Pn_CR_DSBSIZE;
|
|
|
|
|
|
|
|
if (drvdata->cmb_esize == 64)
|
|
|
|
*val |= FIELD_PREP(TPDA_Pn_CR_CMBSIZE, 0x2);
|
|
|
|
else if (drvdata->cmb_esize == 32)
|
|
|
|
*val |= FIELD_PREP(TPDA_Pn_CR_CMBSIZE, 0x1);
|
|
|
|
else if (drvdata->cmb_esize == 8)
|
|
|
|
*val &= ~TPDA_Pn_CR_CMBSIZE;
|
|
|
|
}
|
|
|
|
|
2024-04-10 14:46:52 +00:00
|
|
|
/*
|
2024-10-11 14:13:48 +00:00
|
|
|
* Read the element size from the TPDM device. One TPDM must have at least one of the
|
|
|
|
* element size property.
|
2024-04-10 14:46:52 +00:00
|
|
|
* Returns
|
2024-10-11 14:13:48 +00:00
|
|
|
* 0 - The element size property is read
|
|
|
|
* Others - Cannot read the property of the element size
|
2024-04-10 14:46:52 +00:00
|
|
|
*/
|
2024-10-11 14:13:48 +00:00
|
|
|
static int tpdm_read_element_size(struct tpda_drvdata *drvdata,
|
|
|
|
struct coresight_device *csdev)
|
2024-04-10 14:46:52 +00:00
|
|
|
{
|
2024-10-11 14:13:48 +00:00
|
|
|
int rc = -EINVAL;
|
|
|
|
struct tpdm_drvdata *tpdm_data = dev_get_drvdata(csdev->dev.parent);
|
|
|
|
|
|
|
|
if (tpdm_has_dsb_dataset(tpdm_data)) {
|
2024-10-11 14:13:48 +00:00
|
|
|
rc = fwnode_property_read_u32(dev_fwnode(csdev->dev.parent),
|
|
|
|
"qcom,dsb-element-bits", &drvdata->dsb_esize);
|
2024-10-11 14:13:48 +00:00
|
|
|
}
|
|
|
|
if (tpdm_has_cmb_dataset(tpdm_data)) {
|
|
|
|
rc = fwnode_property_read_u32(dev_fwnode(csdev->dev.parent),
|
|
|
|
"qcom,cmb-element-bits", &drvdata->cmb_esize);
|
|
|
|
}
|
2024-04-10 14:46:52 +00:00
|
|
|
|
|
|
|
if (rc)
|
|
|
|
dev_warn_once(&csdev->dev,
|
2024-10-11 14:13:48 +00:00
|
|
|
"Failed to read TPDM Element size: %d\n", rc);
|
2024-04-10 14:46:52 +00:00
|
|
|
|
2024-10-11 14:13:48 +00:00
|
|
|
return rc;
|
2024-04-10 14:46:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Search and read element data size from the TPDM node in
|
|
|
|
* the devicetree. Each input port of TPDA is connected to
|
|
|
|
* a TPDM. Different TPDM supports different types of dataset,
|
|
|
|
* and some may support more than one type of dataset.
|
|
|
|
* Parameter "inport" is used to pass in the input port number
|
|
|
|
* of TPDA, and it is set to -1 in the recursize call.
|
|
|
|
*/
|
2024-10-11 14:13:48 +00:00
|
|
|
static int tpda_get_element_size(struct tpda_drvdata *drvdata,
|
|
|
|
struct coresight_device *csdev,
|
2024-04-10 14:46:52 +00:00
|
|
|
int inport)
|
|
|
|
{
|
2024-10-11 14:13:48 +00:00
|
|
|
int rc = 0;
|
|
|
|
int i;
|
2024-04-10 14:46:52 +00:00
|
|
|
struct coresight_device *in;
|
|
|
|
|
|
|
|
for (i = 0; i < csdev->pdata->nr_inconns; i++) {
|
|
|
|
in = csdev->pdata->in_conns[i]->src_dev;
|
|
|
|
if (!in)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Ignore the paths that do not match port */
|
2024-10-11 14:13:48 +00:00
|
|
|
if (inport >= 0 &&
|
2024-04-10 14:46:52 +00:00
|
|
|
csdev->pdata->in_conns[i]->dest_port != inport)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (coresight_device_is_tpdm(in)) {
|
2024-10-11 14:13:48 +00:00
|
|
|
if (drvdata->dsb_esize || drvdata->cmb_esize)
|
|
|
|
return -EEXIST;
|
|
|
|
rc = tpdm_read_element_size(drvdata, in);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
2024-04-10 14:46:52 +00:00
|
|
|
} else {
|
|
|
|
/* Recurse down the path */
|
2024-10-11 14:13:48 +00:00
|
|
|
rc = tpda_get_element_size(drvdata, in, -1);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
2024-04-10 14:46:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-11 14:13:48 +00:00
|
|
|
|
|
|
|
return rc;
|
2024-04-10 14:46:52 +00:00
|
|
|
}
|
|
|
|
|
2024-04-10 14:46:52 +00:00
|
|
|
/* Settings pre enabling port control register */
|
|
|
|
static void tpda_enable_pre_port(struct tpda_drvdata *drvdata)
|
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
val = readl_relaxed(drvdata->base + TPDA_CR);
|
|
|
|
val &= ~TPDA_CR_ATID;
|
|
|
|
val |= FIELD_PREP(TPDA_CR_ATID, drvdata->atid);
|
|
|
|
writel_relaxed(val, drvdata->base + TPDA_CR);
|
|
|
|
}
|
|
|
|
|
2024-04-10 14:46:52 +00:00
|
|
|
static int tpda_enable_port(struct tpda_drvdata *drvdata, int port)
|
2024-04-10 14:46:52 +00:00
|
|
|
{
|
|
|
|
u32 val;
|
2024-10-11 14:13:48 +00:00
|
|
|
int rc;
|
2024-04-10 14:46:52 +00:00
|
|
|
|
|
|
|
val = readl_relaxed(drvdata->base + TPDA_Pn_CR(port));
|
2024-10-11 14:13:48 +00:00
|
|
|
tpda_clear_element_size(drvdata->csdev);
|
|
|
|
rc = tpda_get_element_size(drvdata, drvdata->csdev, port);
|
|
|
|
if (!rc && (drvdata->dsb_esize || drvdata->cmb_esize)) {
|
|
|
|
tpda_set_element_size(drvdata, &val);
|
|
|
|
/* Enable the port */
|
|
|
|
val |= TPDA_Pn_CR_ENA;
|
|
|
|
writel_relaxed(val, drvdata->base + TPDA_Pn_CR(port));
|
|
|
|
} else if (rc == -EEXIST)
|
2024-04-10 14:46:52 +00:00
|
|
|
dev_warn_once(&drvdata->csdev->dev,
|
2024-10-11 14:13:48 +00:00
|
|
|
"Detected multiple TPDMs on port %d", port);
|
|
|
|
else
|
|
|
|
dev_warn_once(&drvdata->csdev->dev,
|
|
|
|
"Didn't find TPDM element size");
|
2024-04-10 14:46:52 +00:00
|
|
|
|
2024-10-11 14:13:48 +00:00
|
|
|
return rc;
|
2024-04-10 14:46:52 +00:00
|
|
|
}
|
|
|
|
|
2024-04-10 14:46:52 +00:00
|
|
|
static int __tpda_enable(struct tpda_drvdata *drvdata, int port)
|
2024-04-10 14:46:52 +00:00
|
|
|
{
|
2024-04-10 14:46:52 +00:00
|
|
|
int ret;
|
|
|
|
|
2024-04-10 14:46:52 +00:00
|
|
|
CS_UNLOCK(drvdata->base);
|
|
|
|
|
coresight: Remove the 'enable' field.
JIRA: https://issues.redhat.com/browse/RHEL-21456
commit d5e83f97eb5669bfdd894ec980083f65517df2fb
Author: James Clark <james.clark@arm.com>
Date: Mon, 29 Jan 2024 15:40:36 +0000
'enable', which probably should have been 'enabled', is only ever read
in the core code in relation to controlling sources, and specifically
only sources in sysfs mode. Confusingly it's not labelled as such and
relying on it can be a source of bugs like the one fixed by
commit 078dbba3f0c9 ("coresight: Fix crash when Perf and sysfs modes are
used concurrently").
Most importantly, it can only be used when the coresight_mutex is held
which is only done when enabling and disabling paths in sysfs mode, and
not Perf mode. So to prevent its usage spreading and leaking out to
other devices, remove it.
It's use is equivalent to checking if the mode is currently sysfs, as
due to the coresight_mutex lock, mode == CS_MODE_SYSFS can only become
true or untrue when that lock is held, and when mode == CS_MODE_SYSFS
the device is both enabled and in sysfs mode.
The one place it was used outside of the core code is in TPDA, but that
pattern is more appropriately represented using refcounts inside the
device's own spinlock.
Signed-off-by: James Clark <james.clark@arm.com>
Link: https://lore.kernel.org/r/20240129154050.569566-6-james.clark@arm.com
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Mark Salter <msalter@redhat.com>
2024-10-11 14:13:48 +00:00
|
|
|
/*
|
|
|
|
* Only do pre-port enable for first port that calls enable when the
|
|
|
|
* device's main refcount is still 0
|
|
|
|
*/
|
2024-10-11 14:13:48 +00:00
|
|
|
lockdep_assert_held(&drvdata->spinlock);
|
|
|
|
if (!drvdata->csdev->refcnt)
|
2024-04-10 14:46:52 +00:00
|
|
|
tpda_enable_pre_port(drvdata);
|
|
|
|
|
2024-04-10 14:46:52 +00:00
|
|
|
ret = tpda_enable_port(drvdata, port);
|
2024-04-10 14:46:52 +00:00
|
|
|
CS_LOCK(drvdata->base);
|
2024-04-10 14:46:52 +00:00
|
|
|
|
|
|
|
return ret;
|
2024-04-10 14:46:52 +00:00
|
|
|
}
|
|
|
|
|
2024-04-10 15:53:54 +00:00
|
|
|
static int tpda_enable(struct coresight_device *csdev,
|
|
|
|
struct coresight_connection *in,
|
|
|
|
struct coresight_connection *out)
|
2024-04-10 14:46:52 +00:00
|
|
|
{
|
|
|
|
struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
2024-04-10 14:46:52 +00:00
|
|
|
int ret = 0;
|
2024-04-10 14:46:52 +00:00
|
|
|
|
|
|
|
spin_lock(&drvdata->spinlock);
|
2024-04-10 14:46:52 +00:00
|
|
|
if (atomic_read(&in->dest_refcnt) == 0) {
|
|
|
|
ret = __tpda_enable(drvdata, in->dest_port);
|
|
|
|
if (!ret) {
|
|
|
|
atomic_inc(&in->dest_refcnt);
|
2024-10-11 14:13:48 +00:00
|
|
|
csdev->refcnt++;
|
2024-04-10 14:46:52 +00:00
|
|
|
dev_dbg(drvdata->dev, "TPDA inport %d enabled.\n", in->dest_port);
|
|
|
|
}
|
|
|
|
}
|
2024-04-10 14:46:52 +00:00
|
|
|
|
|
|
|
spin_unlock(&drvdata->spinlock);
|
2024-04-10 14:46:52 +00:00
|
|
|
return ret;
|
2024-04-10 14:46:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void __tpda_disable(struct tpda_drvdata *drvdata, int port)
|
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
CS_UNLOCK(drvdata->base);
|
|
|
|
|
|
|
|
val = readl_relaxed(drvdata->base + TPDA_Pn_CR(port));
|
|
|
|
val &= ~TPDA_Pn_CR_ENA;
|
|
|
|
writel_relaxed(val, drvdata->base + TPDA_Pn_CR(port));
|
|
|
|
|
|
|
|
CS_LOCK(drvdata->base);
|
|
|
|
}
|
|
|
|
|
2024-04-10 15:53:54 +00:00
|
|
|
static void tpda_disable(struct coresight_device *csdev,
|
|
|
|
struct coresight_connection *in,
|
|
|
|
struct coresight_connection *out)
|
2024-04-10 14:46:52 +00:00
|
|
|
{
|
|
|
|
struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
|
|
|
|
|
|
|
spin_lock(&drvdata->spinlock);
|
coresight: Remove the 'enable' field.
JIRA: https://issues.redhat.com/browse/RHEL-21456
commit d5e83f97eb5669bfdd894ec980083f65517df2fb
Author: James Clark <james.clark@arm.com>
Date: Mon, 29 Jan 2024 15:40:36 +0000
'enable', which probably should have been 'enabled', is only ever read
in the core code in relation to controlling sources, and specifically
only sources in sysfs mode. Confusingly it's not labelled as such and
relying on it can be a source of bugs like the one fixed by
commit 078dbba3f0c9 ("coresight: Fix crash when Perf and sysfs modes are
used concurrently").
Most importantly, it can only be used when the coresight_mutex is held
which is only done when enabling and disabling paths in sysfs mode, and
not Perf mode. So to prevent its usage spreading and leaking out to
other devices, remove it.
It's use is equivalent to checking if the mode is currently sysfs, as
due to the coresight_mutex lock, mode == CS_MODE_SYSFS can only become
true or untrue when that lock is held, and when mode == CS_MODE_SYSFS
the device is both enabled and in sysfs mode.
The one place it was used outside of the core code is in TPDA, but that
pattern is more appropriately represented using refcounts inside the
device's own spinlock.
Signed-off-by: James Clark <james.clark@arm.com>
Link: https://lore.kernel.org/r/20240129154050.569566-6-james.clark@arm.com
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Mark Salter <msalter@redhat.com>
2024-10-11 14:13:48 +00:00
|
|
|
if (atomic_dec_return(&in->dest_refcnt) == 0) {
|
2024-04-10 15:53:54 +00:00
|
|
|
__tpda_disable(drvdata, in->dest_port);
|
2024-10-11 14:13:48 +00:00
|
|
|
csdev->refcnt--;
|
coresight: Remove the 'enable' field.
JIRA: https://issues.redhat.com/browse/RHEL-21456
commit d5e83f97eb5669bfdd894ec980083f65517df2fb
Author: James Clark <james.clark@arm.com>
Date: Mon, 29 Jan 2024 15:40:36 +0000
'enable', which probably should have been 'enabled', is only ever read
in the core code in relation to controlling sources, and specifically
only sources in sysfs mode. Confusingly it's not labelled as such and
relying on it can be a source of bugs like the one fixed by
commit 078dbba3f0c9 ("coresight: Fix crash when Perf and sysfs modes are
used concurrently").
Most importantly, it can only be used when the coresight_mutex is held
which is only done when enabling and disabling paths in sysfs mode, and
not Perf mode. So to prevent its usage spreading and leaking out to
other devices, remove it.
It's use is equivalent to checking if the mode is currently sysfs, as
due to the coresight_mutex lock, mode == CS_MODE_SYSFS can only become
true or untrue when that lock is held, and when mode == CS_MODE_SYSFS
the device is both enabled and in sysfs mode.
The one place it was used outside of the core code is in TPDA, but that
pattern is more appropriately represented using refcounts inside the
device's own spinlock.
Signed-off-by: James Clark <james.clark@arm.com>
Link: https://lore.kernel.org/r/20240129154050.569566-6-james.clark@arm.com
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Mark Salter <msalter@redhat.com>
2024-10-11 14:13:48 +00:00
|
|
|
}
|
2024-04-10 14:46:52 +00:00
|
|
|
spin_unlock(&drvdata->spinlock);
|
|
|
|
|
2024-04-10 15:53:54 +00:00
|
|
|
dev_dbg(drvdata->dev, "TPDA inport %d disabled\n", in->dest_port);
|
2024-04-10 14:46:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct coresight_ops_link tpda_link_ops = {
|
|
|
|
.enable = tpda_enable,
|
|
|
|
.disable = tpda_disable,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct coresight_ops tpda_cs_ops = {
|
|
|
|
.link_ops = &tpda_link_ops,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int tpda_init_default_data(struct tpda_drvdata *drvdata)
|
|
|
|
{
|
|
|
|
int atid;
|
|
|
|
/*
|
|
|
|
* TPDA must has a unique atid. This atid can uniquely
|
|
|
|
* identify the TPDM trace source connected to the TPDA.
|
|
|
|
* The TPDMs which are connected to same TPDA share the
|
|
|
|
* same trace-id. When TPDA does packetization, different
|
|
|
|
* port will have unique channel number for decoding.
|
|
|
|
*/
|
|
|
|
atid = coresight_trace_id_get_system_id();
|
|
|
|
if (atid < 0)
|
|
|
|
return atid;
|
|
|
|
|
|
|
|
drvdata->atid = atid;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tpda_probe(struct amba_device *adev, const struct amba_id *id)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct device *dev = &adev->dev;
|
|
|
|
struct coresight_platform_data *pdata;
|
|
|
|
struct tpda_drvdata *drvdata;
|
|
|
|
struct coresight_desc desc = { 0 };
|
|
|
|
void __iomem *base;
|
|
|
|
|
|
|
|
pdata = coresight_get_platform_data(dev);
|
|
|
|
if (IS_ERR(pdata))
|
|
|
|
return PTR_ERR(pdata);
|
|
|
|
adev->dev.platform_data = pdata;
|
|
|
|
|
|
|
|
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
|
|
|
|
if (!drvdata)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
drvdata->dev = &adev->dev;
|
|
|
|
dev_set_drvdata(dev, drvdata);
|
|
|
|
|
|
|
|
base = devm_ioremap_resource(dev, &adev->res);
|
2024-04-10 14:46:52 +00:00
|
|
|
if (IS_ERR(base))
|
|
|
|
return PTR_ERR(base);
|
2024-04-10 14:46:52 +00:00
|
|
|
drvdata->base = base;
|
|
|
|
|
|
|
|
spin_lock_init(&drvdata->spinlock);
|
|
|
|
|
|
|
|
ret = tpda_init_default_data(drvdata);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
desc.name = coresight_alloc_device_name(&tpda_devs, dev);
|
|
|
|
if (!desc.name)
|
|
|
|
return -ENOMEM;
|
|
|
|
desc.type = CORESIGHT_DEV_TYPE_LINK;
|
|
|
|
desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
|
|
|
|
desc.ops = &tpda_cs_ops;
|
|
|
|
desc.pdata = adev->dev.platform_data;
|
|
|
|
desc.dev = &adev->dev;
|
|
|
|
desc.access = CSDEV_ACCESS_IOMEM(base);
|
|
|
|
drvdata->csdev = coresight_register(&desc);
|
|
|
|
if (IS_ERR(drvdata->csdev))
|
|
|
|
return PTR_ERR(drvdata->csdev);
|
|
|
|
|
|
|
|
pm_runtime_put(&adev->dev);
|
|
|
|
|
|
|
|
dev_dbg(drvdata->dev, "TPDA initialized\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-04-10 14:46:52 +00:00
|
|
|
static void tpda_remove(struct amba_device *adev)
|
2024-04-10 14:46:52 +00:00
|
|
|
{
|
|
|
|
struct tpda_drvdata *drvdata = dev_get_drvdata(&adev->dev);
|
|
|
|
|
|
|
|
coresight_trace_id_put_system_id(drvdata->atid);
|
|
|
|
coresight_unregister(drvdata->csdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Different TPDA has different periph id.
|
|
|
|
* The difference is 0-7 bits' value. So ignore 0-7 bits.
|
|
|
|
*/
|
|
|
|
static struct amba_id tpda_ids[] = {
|
|
|
|
{
|
|
|
|
.id = 0x000f0f00,
|
|
|
|
.mask = 0x000fff00,
|
|
|
|
},
|
2024-10-11 14:13:48 +00:00
|
|
|
{ 0, 0, NULL },
|
2024-04-10 14:46:52 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct amba_driver tpda_driver = {
|
|
|
|
.drv = {
|
|
|
|
.name = "coresight-tpda",
|
|
|
|
.suppress_bind_attrs = true,
|
|
|
|
},
|
|
|
|
.probe = tpda_probe,
|
|
|
|
.remove = tpda_remove,
|
|
|
|
.id_table = tpda_ids,
|
|
|
|
};
|
|
|
|
|
|
|
|
module_amba_driver(tpda_driver);
|
|
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_DESCRIPTION("Trace, Profiling & Diagnostic Aggregator driver");
|