This patchset adds the first version of the CoreSight CTI hardware driver.
CTIs are defined in the device tree and associated with other CoreSight devices. The core CoreSight code has been modified to enable the registration of the CTI devices on the same bus as the other CoreSight components, but as these are not actually trace generation / capture devices, they are not part of the Coresight path when generating trace.
However, the definition of the standard CoreSight device has been extended to include a reference to an associated CTI device, and the enable / disable trace path operations will auto enable/disable any associated CTI devices at the same time.
Programming is at present via sysfs - a full API is provided to utilise the hardware capabilities. As CTI devices are unprogrammed by default, the auto enable describe above will have no effect until explicit programming takes place.
A set of device tree bindings specific to the CTI topology has been defined.
Documentation has been updated to describe both the CTI hardware, its use and programming in sysfs, and the new dts bindings required.
This patchset is fully functional - but comes with the following caveats:-
1) device tree bindings are incomplete - and in some cases not representative of the final definitions - alternative descriptions are in place to test the differing binding properties. The QCom DB410 has been used as the principle test platform.
2) the main driver file is rather large. It may benefit from having the sysfs specific code split off into a separate file as the etm drivers have done.
3) there is a sig_names field in the CTI connection structure. This is intended to potentially contain the names of the interconnecting triggers, read from the device tree - (e.g. dbgreq, restart, tmcfull) to enable users to determine the purpose of the signals attached to the CTI. This is not yet implemented and comments on the value of implementing this are welcome.
4) there is some debug logging still present. This will be removed.
5) the updates to the AMBA drivers for UCI component ID are included in the set at present for ease of use, but are not intended to be in the final set. (patches 0001 and 0002)
6) tested on a 4.20-rc1 based tree, but applies cleanly to 5.0-rc1
All comments on structure and functionality welcome.
Mike Leach (10): drivers: amba: Updates to component identification for driver matching. coresight: etmv4: Update ID register table to add UCI support drivers: coresight: Add ECT definitions to main CS header. drivers: coresight: Update to Coresight core headers for CTI. drivers: coresight: Updates to CoreSight core for CTI implementation. coresight: of: Update Coresight device tree parser code for CTI. coresight: cti: New CTI driver code. coresight: build: Add in Coresight config to build files coresight: cti: Add in device tree definitions for CTI.dts coresight: docs: Add documentation for CTI component and bindings.
.../testing/sysfs-bus-coresight-devices-cti | 178 ++ .../devicetree/bindings/arm/coresight.txt | 173 ++ Documentation/trace/coresight.txt | 125 +- arch/arm64/boot/dts/arm/juno-base.dtsi | 76 + arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi | 9 + arch/arm64/boot/dts/qcom/msm8916.dtsi | 101 +- drivers/amba/bus.c | 45 +- drivers/hwtracing/coresight/Kconfig | 12 + drivers/hwtracing/coresight/Makefile | 1 + drivers/hwtracing/coresight/coresight-cti.c | 2025 +++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 195 ++ drivers/hwtracing/coresight/coresight-etm4x.c | 18 +- drivers/hwtracing/coresight/coresight-priv.h | 10 + drivers/hwtracing/coresight/coresight.c | 51 +- drivers/hwtracing/coresight/of_coresight.c | 65 + include/linux/amba/bus.h | 32 + include/linux/coresight.h | 42 + 17 files changed, 3144 insertions(+), 14 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-coresight-devices-cti create mode 100644 drivers/hwtracing/coresight/coresight-cti.c create mode 100644 drivers/hwtracing/coresight/coresight-cti.h
The CoreSight specification (ARM IHI 0029E), updates the ID register requirements for components on an AMBA bus, to cover both traditional ARM Primecell type devices, and newer CoreSight and other components.
The Peripheral ID (PID) / Component ID (CID) pair is extended in certain cases to uniquely identify components. CoreSight components related to a single function can share Peripheral ID values, and must be further identified using a Unique Component Identifier (UCI). e.g. the ETM, CTI, PMU and Debug hardware of the A35 all share the same PID.
Bits 15:12 of the CID are defined to be the device class. Class 0xF remains for PrimeCell and legacy components. Class 0x9 defines the component as CoreSight (CORESIGHT_CID above) Class 0x0, 0x1, 0xB, 0xE define components that do not have driver support at present. Class 0x2-0x8,0xA and 0xD-0xD are presently reserved.
The specification futher defines which classes of device use the standard CID/PID pair, and when additional ID registers are required.
The patches provide an update of amba_device and matching code to handle the additional registers required for the Class 0x9 (CoreSight) UCI. The *data pointer in the amba_id is used by the driver to provide extended ID register values for matching.
CoreSight components where PID/CID pair is currently sufficient for unique identification need not provide this additional information.
Signed-off-by: Mike Leach mike.leach@linaro.org --- drivers/amba/bus.c | 45 +++++++++++++++++++++++++++++++++------- include/linux/amba/bus.h | 32 ++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 8 deletions(-)
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index 41b706403ef7..524296a0eba0 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -26,19 +26,36 @@
#define to_amba_driver(d) container_of(d, struct amba_driver, drv)
-static const struct amba_id * -amba_lookup(const struct amba_id *table, struct amba_device *dev) +/* called on periphid match and class 0x9 coresight device. */ +static int +amba_cs_uci_id_match(const struct amba_id *table, struct amba_device *dev) { int ret = 0; + struct amba_cs_uci_id *uci; + + uci = table->data;
+ /* no table data - return match on periphid */ + if (!uci) + return 1; + + /* test against read devtype and masked devarch value */ + ret = (dev->uci.devtype == uci->devtype) && + ((dev->uci.devarch & uci->devarch_mask) == uci->devarch); + return ret; +} + +static const struct amba_id * +amba_lookup(const struct amba_id *table, struct amba_device *dev) +{ while (table->mask) { - ret = (dev->periphid & table->mask) == table->id; - if (ret) - break; + if (((dev->periphid & table->mask) == table->id) && + ((dev->cid != CORESIGHT_CID) || + (amba_cs_uci_id_match(table, dev)))) + return table; table++; } - - return ret ? table : NULL; + return NULL; }
static int amba_match(struct device *dev, struct device_driver *drv) @@ -399,10 +416,22 @@ static int amba_device_try_add(struct amba_device *dev, struct resource *parent) cid |= (readl(tmp + size - 0x10 + 4 * i) & 255) << (i * 8);
+ if (cid == CORESIGHT_CID) { + /* set the base to the start of the last 4k block */ + void __iomem *csbase = tmp + size - 4096; + + dev->uci.devarch = + readl(csbase + UCI_REG_DEVARCH_OFFSET); + dev->uci.devtype = + readl(csbase + UCI_REG_DEVTYPE_OFFSET) & 0xff; + } + amba_put_disable_pclk(dev);
- if (cid == AMBA_CID || cid == CORESIGHT_CID) + if (cid == AMBA_CID || cid == CORESIGHT_CID) { dev->periphid = pid; + dev->cid = cid; + }
if (!dev->periphid) ret = -ENODEV; diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h index d143c13bed26..8c0f392e4da2 100644 --- a/include/linux/amba/bus.h +++ b/include/linux/amba/bus.h @@ -25,6 +25,36 @@ #define AMBA_CID 0xb105f00d #define CORESIGHT_CID 0xb105900d
+/* + * CoreSight Architecture specification updates the ID specification + * for components on the AMBA bus. (ARM IHI 0029E) + * + * Bits 15:12 of the CID are the device class. + * + * Class 0xF remains for PrimeCell and legacy components. (AMBA_CID above) + * Class 0x9 defines the component as CoreSight (CORESIGHT_CID above) + * Class 0x0, 0x1, 0xB, 0xE define components that do not have driver support + * at present. + * Class 0x2-0x8,0xA and 0xD-0xD are presently reserved. + * + * Remaining CID bits stay as 0xb105-00d + */ + +/* + * Class 0x9 components use additional values to form a Unique Component + * Identifier (UCI), where peripheral ID values are identical for different + * components. Passed to the amba bus code from the component driver via + * the amba_id->data pointer. + */ +struct amba_cs_uci_id { + unsigned int devarch; + unsigned int devarch_mask; + unsigned int devtype; +}; + +#define UCI_REG_DEVTYPE_OFFSET 0xFCC +#define UCI_REG_DEVARCH_OFFSET 0xFBC + struct clk;
struct amba_device { @@ -32,6 +62,8 @@ struct amba_device { struct resource res; struct clk *pclk; unsigned int periphid; + unsigned int cid; + struct amba_cs_uci_id uci; unsigned int irq[AMBA_NR_IRQS]; char *driver_override; };
Updates the ID register tables to contain a UCI entry for the A35 ETM device to allow correct matching of driver in the amba bus code.
Signed-off-by: Mike Leach mike.leach@linaro.org --- drivers/hwtracing/coresight/coresight-etm4x.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 53e2fb6e86f6..2fb8054e43ab 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -1073,12 +1073,28 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) .mask = 0x000fffff, \ }
+static struct amba_cs_uci_id uci_id_etm4[] = { + { + /* ETMv4 UCI data */ + .devarch = 0x47704a13, + .devarch_mask = 0xfff0ffff, + .devtype = 0x00000013, + } +}; + +#define ETM4x_AMBA_UCI_ID(pid) \ + { \ + .id = pid, \ + .mask = 0x000fffff, \ + .data = uci_id_etm4, \ + } + static const struct amba_id etm4_ids[] = { ETM4x_AMBA_ID(0x000bb95d), /* Cortex-A53 */ ETM4x_AMBA_ID(0x000bb95e), /* Cortex-A57 */ ETM4x_AMBA_ID(0x000bb95a), /* Cortex-A72 */ ETM4x_AMBA_ID(0x000bb959), /* Cortex-A73 */ - ETM4x_AMBA_ID(0x000bb9da), /* Cortex-A35 */ + ETM4x_AMBA_UCI_ID(0x000bb9da), /* Cortex-A35 */ {}, };
Adds Embedded Cross Trigger (ECT) type and CTI sub-type to CoreSight enums in main coresight headers.
Adds in new global functions for CTI drivers.
Signed-off-by: Mike Leach mike.leach@linaro.org --- include/linux/coresight.h | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+)
diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 46c67a764877..6f85ac0cec55 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -41,6 +41,7 @@ enum coresight_dev_type { CORESIGHT_DEV_TYPE_LINKSINK, CORESIGHT_DEV_TYPE_SOURCE, CORESIGHT_DEV_TYPE_HELPER, + CORESIGHT_DEV_TYPE_ECT, };
enum coresight_dev_subtype_sink { @@ -68,6 +69,12 @@ enum coresight_dev_subtype_helper { CORESIGHT_DEV_SUBTYPE_HELPER_CATU, };
+/* Embedded Cross Trigger (ECT) sub-types */ +enum coresight_dev_subtype_ect { + CORESIGHT_DEV_SUBTYPE_ECT_NONE, + CORESIGHT_DEV_SUBTYPE_ECT_CTI, +}; + /** * union coresight_dev_subtype - further characterisation of a type * @sink_subtype: type of sink this component is, as defined @@ -78,6 +85,8 @@ enum coresight_dev_subtype_helper { * by @coresight_dev_subtype_source. * @helper_subtype: type of helper this component is, as defined * by @coresight_dev_subtype_helper. + * @ect_subtype: type of cross trigger this component is, as + * define by @coresight_dev_subtype_ect */ union coresight_dev_subtype { /* We have some devices which acts as LINK and SINK */ @@ -87,6 +96,7 @@ union coresight_dev_subtype { }; enum coresight_dev_subtype_source source_subtype; enum coresight_dev_subtype_helper helper_subtype; + enum coresight_dev_subtype_ect ect_subtype; };
/** @@ -156,6 +166,8 @@ struct coresight_connection { * @activated: 'true' only if a _sink_ has been activated. A sink can be activated but not yet enabled. Enabling for a _sink_ happens when a source has been selected for that it. + * @ect_dev: Associated cross trigger device. Not part of the trace data + * path or connections. */ struct coresight_device { struct coresight_connection *conns; @@ -169,6 +181,7 @@ struct coresight_device { bool orphan; bool enable; /* true only if configured as part of a path */ bool activated; /* true only if a sink is part of a path */ + struct coresight_device *ect_dev; };
#define to_coresight_device(d) container_of(d, struct coresight_device, dev) @@ -177,6 +190,7 @@ struct coresight_device { #define sink_ops(csdev) csdev->ops->sink_ops #define link_ops(csdev) csdev->ops->link_ops #define helper_ops(csdev) csdev->ops->helper_ops +#define ect_ops(csdev) csdev->ops->ect_ops
/** * struct coresight_ops_sink - basic operations for a sink @@ -242,11 +256,26 @@ struct coresight_ops_helper { int (*disable)(struct coresight_device *csdev, void *data); };
+/** + * struct coresight_ops_ect - Ops for an embedded cross trigger device + * + * All operations could pass in a device specific data, which could + * help the ect device to determine what to do. + * + * @enable : Enable the device + * @disable : Disable the device + */ +struct coresight_ops_ect { + int (*enable)(struct coresight_device *csdev, void *data); + int (*disable)(struct coresight_device *csdev, void *data); +}; + struct coresight_ops { const struct coresight_ops_sink *sink_ops; const struct coresight_ops_link *link_ops; const struct coresight_ops_source *source_ops; const struct coresight_ops_helper *helper_ops; + const struct coresight_ops_ect *ect_ops; };
#ifdef CONFIG_CORESIGHT @@ -290,14 +319,27 @@ static inline void coresight_disclaim_device_unlocked(void __iomem *base) {}
#ifdef CONFIG_OF extern int of_coresight_get_cpu(const struct device_node *node); +extern int of_coresight_ect_get_cpu(const struct device_node *node); extern struct coresight_platform_data * of_get_coresight_platform_data(struct device *dev, const struct device_node *node); +extern struct coresight_platform_data * +of_get_coresight_ect_platform_data(struct device *dev, + const struct device_node *node); +extern struct coresight_device * +of_coresight_get_cs_device_by_node(struct device_node *node); #else static inline int of_coresight_get_cpu(const struct device_node *node) { return 0; } +static inline int of_coresight_ect_get_cpu(const struct device_node *node) +{ return 0; } static inline struct coresight_platform_data *of_get_coresight_platform_data( struct device *dev, const struct device_node *node) { return NULL; } +static inline struct coresight_platform_data * +of_get_coresight_ect_platform_data(struct device *dev, + const struct device_node *node) { return NULL; } +static inline struct coresight_device *of_coresight_get_cs_device_by_node( + struct device_node *node) { return NULL; } #endif
#endif
Hi Mike,
On 09/01/2019 22:54, Mike Leach wrote:
Adds Embedded Cross Trigger (ECT) type and CTI sub-type to CoreSight enums in main coresight headers.
Adds in new global functions for CTI drivers.
Signed-off-by: Mike Leach mike.leach@linaro.org
include/linux/coresight.h | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+)
diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 46c67a764877..6f85ac0cec55 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -41,6 +41,7 @@ enum coresight_dev_type { CORESIGHT_DEV_TYPE_LINKSINK, CORESIGHT_DEV_TYPE_SOURCE, CORESIGHT_DEV_TYPE_HELPER,
- CORESIGHT_DEV_TYPE_ECT, };
enum coresight_dev_subtype_sink { @@ -68,6 +69,12 @@ enum coresight_dev_subtype_helper { CORESIGHT_DEV_SUBTYPE_HELPER_CATU, }; +/* Embedded Cross Trigger (ECT) sub-types */ +enum coresight_dev_subtype_ect {
- CORESIGHT_DEV_SUBTYPE_ECT_NONE,
- CORESIGHT_DEV_SUBTYPE_ECT_CTI,
+};
I haven't looked at the following patches, but I will ask anyway.
Do we really need a CTI type ? Could this be a HELPER_TYPE and a new sub-type of the HELPER ? (Just like we do the CATU) ?
/**
- union coresight_dev_subtype - further characterisation of a type
- @sink_subtype: type of sink this component is, as defined
@@ -78,6 +85,8 @@ enum coresight_dev_subtype_helper {
by @coresight_dev_subtype_source.
- @helper_subtype: type of helper this component is, as defined
by @coresight_dev_subtype_helper.
- @ect_subtype: type of cross trigger this component is, as
*/ union coresight_dev_subtype { /* We have some devices which acts as LINK and SINK */
define by @coresight_dev_subtype_ect
@@ -87,6 +96,7 @@ union coresight_dev_subtype { }; enum coresight_dev_subtype_source source_subtype; enum coresight_dev_subtype_helper helper_subtype;
- enum coresight_dev_subtype_ect ect_subtype; };
/** @@ -156,6 +166,8 @@ struct coresight_connection {
- @activated: 'true' only if a _sink_ has been activated. A sink can be activated but not yet enabled. Enabling for a _sink_ happens when a source has been selected for that it.
- @ect_dev: Associated cross trigger device. Not part of the trace data
*/ struct coresight_device { struct coresight_connection *conns;
path or connections.
@@ -169,6 +181,7 @@ struct coresight_device { bool orphan; bool enable; /* true only if configured as part of a path */ bool activated; /* true only if a sink is part of a path */
- struct coresight_device *ect_dev;
Could we use a "dummy" connection to represent the ECTI, just like we do for CATU ? We could always find the ECT from the conns by matching the type && subtype.
And the bonus is that the ECTI will be enabled automatically when the component is enabled (just like we do for CATU).
Cheers Suzuki
Hi Mike,
I have started to work on your set. I expect the process to take a few days and as such will explicitly let you know when I am done.
On Wed, Jan 09, 2019 at 10:54:34PM +0000, Mike Leach wrote:
Adds Embedded Cross Trigger (ECT) type and CTI sub-type to CoreSight enums in main coresight headers.
Adds in new global functions for CTI drivers.
The goal of a changelog is to mention why you are doing something rather than what you are doing. With this patch it is a little hard to do that but see my comment below about how to introduce functions - proceeding as suggested will help write changelogs that fall within the guideines of the community.
Signed-off-by: Mike Leach mike.leach@linaro.org
include/linux/coresight.h | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+)
diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 46c67a764877..6f85ac0cec55 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -41,6 +41,7 @@ enum coresight_dev_type { CORESIGHT_DEV_TYPE_LINKSINK, CORESIGHT_DEV_TYPE_SOURCE, CORESIGHT_DEV_TYPE_HELPER,
- CORESIGHT_DEV_TYPE_ECT,
}; enum coresight_dev_subtype_sink { @@ -68,6 +69,12 @@ enum coresight_dev_subtype_helper { CORESIGHT_DEV_SUBTYPE_HELPER_CATU, }; +/* Embedded Cross Trigger (ECT) sub-types */ +enum coresight_dev_subtype_ect {
- CORESIGHT_DEV_SUBTYPE_ECT_NONE,
- CORESIGHT_DEV_SUBTYPE_ECT_CTI,
+};
/**
- union coresight_dev_subtype - further characterisation of a type
- @sink_subtype: type of sink this component is, as defined
@@ -78,6 +85,8 @@ enum coresight_dev_subtype_helper {
by @coresight_dev_subtype_source.
- @helper_subtype: type of helper this component is, as defined
by @coresight_dev_subtype_helper.
- @ect_subtype: type of cross trigger this component is, as
This needs to be a tab rather than a series of spaces.
define by @coresight_dev_subtype_ect
s/define/defined
*/ union coresight_dev_subtype { /* We have some devices which acts as LINK and SINK */ @@ -87,6 +96,7 @@ union coresight_dev_subtype { }; enum coresight_dev_subtype_source source_subtype; enum coresight_dev_subtype_helper helper_subtype;
- enum coresight_dev_subtype_ect ect_subtype;
}; /** @@ -156,6 +166,8 @@ struct coresight_connection {
- @activated: 'true' only if a _sink_ has been activated. A sink can be activated but not yet enabled. Enabling for a _sink_ happens when a source has been selected for that it.
- @ect_dev: Associated cross trigger device. Not part of the trace data
*/
path or connections.
struct coresight_device { struct coresight_connection *conns; @@ -169,6 +181,7 @@ struct coresight_device { bool orphan; bool enable; /* true only if configured as part of a path */ bool activated; /* true only if a sink is part of a path */
- struct coresight_device *ect_dev;
}; #define to_coresight_device(d) container_of(d, struct coresight_device, dev) @@ -177,6 +190,7 @@ struct coresight_device { #define sink_ops(csdev) csdev->ops->sink_ops #define link_ops(csdev) csdev->ops->link_ops #define helper_ops(csdev) csdev->ops->helper_ops +#define ect_ops(csdev) csdev->ops->ect_ops /**
- struct coresight_ops_sink - basic operations for a sink
@@ -242,11 +256,26 @@ struct coresight_ops_helper { int (*disable)(struct coresight_device *csdev, void *data); }; +/**
- struct coresight_ops_ect - Ops for an embedded cross trigger device
- All operations could pass in a device specific data, which could
- help the ect device to determine what to do.
- @enable : Enable the device
- @disable : Disable the device
- */
+struct coresight_ops_ect {
- int (*enable)(struct coresight_device *csdev, void *data);
- int (*disable)(struct coresight_device *csdev, void *data);
+};
As pointed out by Suzuki using the existing CATU framework may be a better way but it is much too early in this set to make a final call on that. More to come later.
struct coresight_ops { const struct coresight_ops_sink *sink_ops; const struct coresight_ops_link *link_ops; const struct coresight_ops_source *source_ops; const struct coresight_ops_helper *helper_ops;
- const struct coresight_ops_ect *ect_ops;
}; #ifdef CONFIG_CORESIGHT @@ -290,14 +319,27 @@ static inline void coresight_disclaim_device_unlocked(void __iomem *base) {} #ifdef CONFIG_OF extern int of_coresight_get_cpu(const struct device_node *node); +extern int of_coresight_ect_get_cpu(const struct device_node *node); extern struct coresight_platform_data * of_get_coresight_platform_data(struct device *dev, const struct device_node *node); +extern struct coresight_platform_data * +of_get_coresight_ect_platform_data(struct device *dev,
const struct device_node *node);
+extern struct coresight_device * +of_coresight_get_cs_device_by_node(struct device_node *node); #else static inline int of_coresight_get_cpu(const struct device_node *node) { return 0; } +static inline int of_coresight_ect_get_cpu(const struct device_node *node) +{ return 0; } static inline struct coresight_platform_data *of_get_coresight_platform_data( struct device *dev, const struct device_node *node) { return NULL; } +static inline struct coresight_platform_data * +of_get_coresight_ect_platform_data(struct device *dev,
const struct device_node *node) { return NULL; }
+static inline struct coresight_device *of_coresight_get_cs_device_by_node(
- struct device_node *node) { return NULL; }
#endif
Unfortunately you can't just add function stubs like that. Stubs and their function definition need to be introduced gradually as they are needed. I understand the end result will be the same but you will be called out on the public mailing list so might as well do it now.
#endif
2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Hi Mathieu,
On Tue, 15 Jan 2019 at 17:46, Mathieu Poirier mathieu.poirier@linaro.org wrote:
Hi Mike,
I have started to work on your set. I expect the process to take a few days and as such will explicitly let you know when I am done.
No problem.
On Wed, Jan 09, 2019 at 10:54:34PM +0000, Mike Leach wrote:
Adds Embedded Cross Trigger (ECT) type and CTI sub-type to CoreSight enums in main coresight headers.
Adds in new global functions for CTI drivers.
The goal of a changelog is to mention why you are doing something rather than what you are doing. With this patch it is a little hard to do that but see my comment below about how to introduce functions - proceeding as suggested will help write changelogs that fall within the guideines of the community.
Signed-off-by: Mike Leach mike.leach@linaro.org
include/linux/coresight.h | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+)
diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 46c67a764877..6f85ac0cec55 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -41,6 +41,7 @@ enum coresight_dev_type { CORESIGHT_DEV_TYPE_LINKSINK, CORESIGHT_DEV_TYPE_SOURCE, CORESIGHT_DEV_TYPE_HELPER,
CORESIGHT_DEV_TYPE_ECT,
};
enum coresight_dev_subtype_sink { @@ -68,6 +69,12 @@ enum coresight_dev_subtype_helper { CORESIGHT_DEV_SUBTYPE_HELPER_CATU, };
+/* Embedded Cross Trigger (ECT) sub-types */ +enum coresight_dev_subtype_ect {
CORESIGHT_DEV_SUBTYPE_ECT_NONE,
CORESIGHT_DEV_SUBTYPE_ECT_CTI,
+};
/**
- union coresight_dev_subtype - further characterisation of a type
- @sink_subtype: type of sink this component is, as defined
@@ -78,6 +85,8 @@ enum coresight_dev_subtype_helper {
by @coresight_dev_subtype_source.
- @helper_subtype: type of helper this component is, as defined
by @coresight_dev_subtype_helper.
- @ect_subtype: type of cross trigger this component is, as
This needs to be a tab rather than a series of spaces.
define by @coresight_dev_subtype_ect
s/define/defined
*/ union coresight_dev_subtype { /* We have some devices which acts as LINK and SINK */ @@ -87,6 +96,7 @@ union coresight_dev_subtype { }; enum coresight_dev_subtype_source source_subtype; enum coresight_dev_subtype_helper helper_subtype;
enum coresight_dev_subtype_ect ect_subtype;
};
/** @@ -156,6 +166,8 @@ struct coresight_connection {
- @activated: 'true' only if a _sink_ has been activated. A sink can be activated but not yet enabled. Enabling for a _sink_ happens when a source has been selected for that it.
- @ect_dev: Associated cross trigger device. Not part of the trace data
*/
path or connections.
struct coresight_device { struct coresight_connection *conns; @@ -169,6 +181,7 @@ struct coresight_device { bool orphan; bool enable; /* true only if configured as part of a path */ bool activated; /* true only if a sink is part of a path */
struct coresight_device *ect_dev;
};
#define to_coresight_device(d) container_of(d, struct coresight_device, dev) @@ -177,6 +190,7 @@ struct coresight_device { #define sink_ops(csdev) csdev->ops->sink_ops #define link_ops(csdev) csdev->ops->link_ops #define helper_ops(csdev) csdev->ops->helper_ops +#define ect_ops(csdev) csdev->ops->ect_ops
/**
- struct coresight_ops_sink - basic operations for a sink
@@ -242,11 +256,26 @@ struct coresight_ops_helper { int (*disable)(struct coresight_device *csdev, void *data); };
+/**
- struct coresight_ops_ect - Ops for an embedded cross trigger device
- All operations could pass in a device specific data, which could
- help the ect device to determine what to do.
- @enable : Enable the device
- @disable : Disable the device
- */
+struct coresight_ops_ect {
int (*enable)(struct coresight_device *csdev, void *data);
int (*disable)(struct coresight_device *csdev, void *data);
+};
As pointed out by Suzuki using the existing CATU framework may be a better way but it is much too early in this set to make a final call on that. More to come later.
struct coresight_ops { const struct coresight_ops_sink *sink_ops; const struct coresight_ops_link *link_ops; const struct coresight_ops_source *source_ops; const struct coresight_ops_helper *helper_ops;
const struct coresight_ops_ect *ect_ops;
};
#ifdef CONFIG_CORESIGHT @@ -290,14 +319,27 @@ static inline void coresight_disclaim_device_unlocked(void __iomem *base) {}
#ifdef CONFIG_OF extern int of_coresight_get_cpu(const struct device_node *node); +extern int of_coresight_ect_get_cpu(const struct device_node *node); extern struct coresight_platform_data * of_get_coresight_platform_data(struct device *dev, const struct device_node *node); +extern struct coresight_platform_data * +of_get_coresight_ect_platform_data(struct device *dev,
const struct device_node *node);
+extern struct coresight_device * +of_coresight_get_cs_device_by_node(struct device_node *node); #else static inline int of_coresight_get_cpu(const struct device_node *node) { return 0; } +static inline int of_coresight_ect_get_cpu(const struct device_node *node) +{ return 0; } static inline struct coresight_platform_data *of_get_coresight_platform_data( struct device *dev, const struct device_node *node) { return NULL; } +static inline struct coresight_platform_data * +of_get_coresight_ect_platform_data(struct device *dev,
const struct device_node *node) { return NULL; }
+static inline struct coresight_device *of_coresight_get_cs_device_by_node(
struct device_node *node) { return NULL; }
#endif
Unfortunately you can't just add function stubs like that. Stubs and their function definition need to be introduced gradually as they are needed. I understand the end result will be the same but you will be called out on the public mailing list so might as well do it now.
OK - I was trying to avoid adding too much in one patch - incremental changes to existing code that enable a later block of functionality - but it seems I have gone a little too far,
Most, if not all the changes from patches 0003-0006 introduce code used in the main driver patch 0007.
These can easily be merged together if that is the accepted way to proceed.
Alternatively, if it is only the new functions that are the issue, and the data structure / header changes can be separate that can be done too - I'm just a little unsure here.
Thanks
Mike
#endif
2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
On Wed, 16 Jan 2019 at 04:19, Mike Leach mike.leach@linaro.org wrote:
Hi Mathieu,
On Tue, 15 Jan 2019 at 17:46, Mathieu Poirier mathieu.poirier@linaro.org wrote:
Hi Mike,
I have started to work on your set. I expect the process to take a few days and as such will explicitly let you know when I am done.
No problem.
On Wed, Jan 09, 2019 at 10:54:34PM +0000, Mike Leach wrote:
Adds Embedded Cross Trigger (ECT) type and CTI sub-type to CoreSight enums in main coresight headers.
Adds in new global functions for CTI drivers.
The goal of a changelog is to mention why you are doing something rather than what you are doing. With this patch it is a little hard to do that but see my comment below about how to introduce functions - proceeding as suggested will help write changelogs that fall within the guideines of the community.
Signed-off-by: Mike Leach mike.leach@linaro.org
include/linux/coresight.h | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+)
diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 46c67a764877..6f85ac0cec55 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -41,6 +41,7 @@ enum coresight_dev_type { CORESIGHT_DEV_TYPE_LINKSINK, CORESIGHT_DEV_TYPE_SOURCE, CORESIGHT_DEV_TYPE_HELPER,
CORESIGHT_DEV_TYPE_ECT,
};
enum coresight_dev_subtype_sink { @@ -68,6 +69,12 @@ enum coresight_dev_subtype_helper { CORESIGHT_DEV_SUBTYPE_HELPER_CATU, };
+/* Embedded Cross Trigger (ECT) sub-types */ +enum coresight_dev_subtype_ect {
CORESIGHT_DEV_SUBTYPE_ECT_NONE,
CORESIGHT_DEV_SUBTYPE_ECT_CTI,
+};
/**
- union coresight_dev_subtype - further characterisation of a type
- @sink_subtype: type of sink this component is, as defined
@@ -78,6 +85,8 @@ enum coresight_dev_subtype_helper {
by @coresight_dev_subtype_source.
- @helper_subtype: type of helper this component is, as defined
by @coresight_dev_subtype_helper.
- @ect_subtype: type of cross trigger this component is, as
This needs to be a tab rather than a series of spaces.
define by @coresight_dev_subtype_ect
s/define/defined
*/ union coresight_dev_subtype { /* We have some devices which acts as LINK and SINK */ @@ -87,6 +96,7 @@ union coresight_dev_subtype { }; enum coresight_dev_subtype_source source_subtype; enum coresight_dev_subtype_helper helper_subtype;
enum coresight_dev_subtype_ect ect_subtype;
};
/** @@ -156,6 +166,8 @@ struct coresight_connection {
- @activated: 'true' only if a _sink_ has been activated. A sink can be activated but not yet enabled. Enabling for a _sink_ happens when a source has been selected for that it.
- @ect_dev: Associated cross trigger device. Not part of the trace data
*/
path or connections.
struct coresight_device { struct coresight_connection *conns; @@ -169,6 +181,7 @@ struct coresight_device { bool orphan; bool enable; /* true only if configured as part of a path */ bool activated; /* true only if a sink is part of a path */
struct coresight_device *ect_dev;
};
#define to_coresight_device(d) container_of(d, struct coresight_device, dev) @@ -177,6 +190,7 @@ struct coresight_device { #define sink_ops(csdev) csdev->ops->sink_ops #define link_ops(csdev) csdev->ops->link_ops #define helper_ops(csdev) csdev->ops->helper_ops +#define ect_ops(csdev) csdev->ops->ect_ops
/**
- struct coresight_ops_sink - basic operations for a sink
@@ -242,11 +256,26 @@ struct coresight_ops_helper { int (*disable)(struct coresight_device *csdev, void *data); };
+/**
- struct coresight_ops_ect - Ops for an embedded cross trigger device
- All operations could pass in a device specific data, which could
- help the ect device to determine what to do.
- @enable : Enable the device
- @disable : Disable the device
- */
+struct coresight_ops_ect {
int (*enable)(struct coresight_device *csdev, void *data);
int (*disable)(struct coresight_device *csdev, void *data);
+};
As pointed out by Suzuki using the existing CATU framework may be a better way but it is much too early in this set to make a final call on that. More to come later.
struct coresight_ops { const struct coresight_ops_sink *sink_ops; const struct coresight_ops_link *link_ops; const struct coresight_ops_source *source_ops; const struct coresight_ops_helper *helper_ops;
const struct coresight_ops_ect *ect_ops;
};
#ifdef CONFIG_CORESIGHT @@ -290,14 +319,27 @@ static inline void coresight_disclaim_device_unlocked(void __iomem *base) {}
#ifdef CONFIG_OF extern int of_coresight_get_cpu(const struct device_node *node); +extern int of_coresight_ect_get_cpu(const struct device_node *node); extern struct coresight_platform_data * of_get_coresight_platform_data(struct device *dev, const struct device_node *node); +extern struct coresight_platform_data * +of_get_coresight_ect_platform_data(struct device *dev,
const struct device_node *node);
+extern struct coresight_device * +of_coresight_get_cs_device_by_node(struct device_node *node); #else static inline int of_coresight_get_cpu(const struct device_node *node) { return 0; } +static inline int of_coresight_ect_get_cpu(const struct device_node *node) +{ return 0; } static inline struct coresight_platform_data *of_get_coresight_platform_data( struct device *dev, const struct device_node *node) { return NULL; } +static inline struct coresight_platform_data * +of_get_coresight_ect_platform_data(struct device *dev,
const struct device_node *node) { return NULL; }
+static inline struct coresight_device *of_coresight_get_cs_device_by_node(
struct device_node *node) { return NULL; }
#endif
Unfortunately you can't just add function stubs like that. Stubs and their function definition need to be introduced gradually as they are needed. I understand the end result will be the same but you will be called out on the public mailing list so might as well do it now.
OK - I was trying to avoid adding too much in one patch - incremental changes to existing code that enable a later block of functionality - but it seems I have gone a little too far,
Most, if not all the changes from patches 0003-0006 introduce code used in the main driver patch 0007.
These can easily be merged together if that is the accepted way to proceed.
Alternatively, if it is only the new functions that are the issue, and the data structure / header changes can be separate that can be done too - I'm just a little unsure here.
I have commented more on this in other patches. Ultimately it comes down to grouping code by functionality. So you can introduce a very minimal driver that doesn't much other than being discovered and probed. From there you incrementally add functionality along with the functions that implements it.
Touch base with me on IRC if things are still unclear or if you want more guidance on how to split things. I suggest to just come up with another set and we'll refine further from there.
Mathieu
Thanks
Mike
#endif
2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
Adds in additional common function declarations to internal CoreSight headers to enable additional CTI funtionality.
Signed-off-by: Mike Leach mike.leach@linaro.org --- drivers/hwtracing/coresight/coresight-priv.h | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 579f34943bf1..53fad3f070a7 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -22,6 +22,7 @@ #define CORESIGHT_LAR 0xfb0 #define CORESIGHT_LSR 0xfb4 #define CORESIGHT_AUTHSTATUS 0xfb8 +#define CORESIGHT_DEVARCH 0xfbc #define CORESIGHT_DEVID 0xfc8 #define CORESIGHT_DEVTYPE 0xfcc
@@ -159,4 +160,13 @@ static inline int etm_readl_cp14(u32 off, unsigned int *val) { return 0; } static inline int etm_writel_cp14(u32 off, u32 val) { return 0; } #endif
+#ifdef CONFIG_CORESIGHT_CTI +extern int cti_add_assoc_to_csdev(struct coresight_device *csdev); +extern void cti_device_release(struct device *dev); +#else +static inline int cti_add_assoc_to_csdev(struct coresight_device *csdev) +{ return 0; } +static inline void cti_device_release(struct device *dev) {}; +#endif + #endif
On Wed, Jan 09, 2019 at 10:54:35PM +0000, Mike Leach wrote:
Adds in additional common function declarations to internal CoreSight headers to enable additional CTI funtionality.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-priv.h | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 579f34943bf1..53fad3f070a7 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -22,6 +22,7 @@ #define CORESIGHT_LAR 0xfb0 #define CORESIGHT_LSR 0xfb4 #define CORESIGHT_AUTHSTATUS 0xfb8 +#define CORESIGHT_DEVARCH 0xfbc #define CORESIGHT_DEVID 0xfc8 #define CORESIGHT_DEVTYPE 0xfcc @@ -159,4 +160,13 @@ static inline int etm_readl_cp14(u32 off, unsigned int *val) { return 0; } static inline int etm_writel_cp14(u32 off, u32 val) { return 0; } #endif +#ifdef CONFIG_CORESIGHT_CTI +extern int cti_add_assoc_to_csdev(struct coresight_device *csdev); +extern void cti_device_release(struct device *dev); +#else +static inline int cti_add_assoc_to_csdev(struct coresight_device *csdev) +{ return 0; } +static inline void cti_device_release(struct device *dev) {}; +#endif
Same comment as 03/10 - elements in this patch need to be folded in the patches that need them.
#endif
2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
On Tue, 15 Jan 2019 at 17:57, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, Jan 09, 2019 at 10:54:35PM +0000, Mike Leach wrote:
Adds in additional common function declarations to internal CoreSight headers to enable additional CTI funtionality.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-priv.h | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 579f34943bf1..53fad3f070a7 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -22,6 +22,7 @@ #define CORESIGHT_LAR 0xfb0 #define CORESIGHT_LSR 0xfb4 #define CORESIGHT_AUTHSTATUS 0xfb8 +#define CORESIGHT_DEVARCH 0xfbc #define CORESIGHT_DEVID 0xfc8 #define CORESIGHT_DEVTYPE 0xfcc
@@ -159,4 +160,13 @@ static inline int etm_readl_cp14(u32 off, unsigned int *val) { return 0; } static inline int etm_writel_cp14(u32 off, u32 val) { return 0; } #endif
+#ifdef CONFIG_CORESIGHT_CTI +extern int cti_add_assoc_to_csdev(struct coresight_device *csdev); +extern void cti_device_release(struct device *dev); +#else +static inline int cti_add_assoc_to_csdev(struct coresight_device *csdev) +{ return 0; } +static inline void cti_device_release(struct device *dev) {}; +#endif
Same comment as 03/10 - elements in this patch need to be folded in the patches that need them.
OK - that answers the question from my previous response.
Thanks
Mike
#endif
2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
On Wed, Jan 09, 2019 at 10:54:35PM +0000, Mike Leach wrote:
Adds in additional common function declarations to internal CoreSight headers to enable additional CTI funtionality.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-priv.h | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 579f34943bf1..53fad3f070a7 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -22,6 +22,7 @@ #define CORESIGHT_LAR 0xfb0 #define CORESIGHT_LSR 0xfb4 #define CORESIGHT_AUTHSTATUS 0xfb8 +#define CORESIGHT_DEVARCH 0xfbc #define CORESIGHT_DEVID 0xfc8 #define CORESIGHT_DEVTYPE 0xfcc @@ -159,4 +160,13 @@ static inline int etm_readl_cp14(u32 off, unsigned int *val) { return 0; } static inline int etm_writel_cp14(u32 off, u32 val) { return 0; } #endif +#ifdef CONFIG_CORESIGHT_CTI +extern int cti_add_assoc_to_csdev(struct coresight_device *csdev); +extern void cti_device_release(struct device *dev); +#else +static inline int cti_add_assoc_to_csdev(struct coresight_device *csdev) +{ return 0; }
The above should return a negative error code since the functionatlity is not present. Returning 0 can mislead a user in thinking that everything is fine.
+static inline void cti_device_release(struct device *dev) {}; +#endif
#endif
2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Hi
On Tue, 15 Jan 2019 at 18:08, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, Jan 09, 2019 at 10:54:35PM +0000, Mike Leach wrote:
Adds in additional common function declarations to internal CoreSight headers to enable additional CTI funtionality.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-priv.h | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 579f34943bf1..53fad3f070a7 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -22,6 +22,7 @@ #define CORESIGHT_LAR 0xfb0 #define CORESIGHT_LSR 0xfb4 #define CORESIGHT_AUTHSTATUS 0xfb8 +#define CORESIGHT_DEVARCH 0xfbc #define CORESIGHT_DEVID 0xfc8 #define CORESIGHT_DEVTYPE 0xfcc
@@ -159,4 +160,13 @@ static inline int etm_readl_cp14(u32 off, unsigned int *val) { return 0; } static inline int etm_writel_cp14(u32 off, u32 val) { return 0; } #endif
+#ifdef CONFIG_CORESIGHT_CTI +extern int cti_add_assoc_to_csdev(struct coresight_device *csdev); +extern void cti_device_release(struct device *dev); +#else +static inline int cti_add_assoc_to_csdev(struct coresight_device *csdev) +{ return 0; }
The above should return a negative error code since the functionatlity is not present. Returning 0 can mislead a user in thinking that everything is fine.
This code is guarded by the CONFIG_CORESIGHT_CTI define. Thus if there is no CTI driver configured then this function must return 0 to allow the rest of the CoreSight infrastructure to work. This mirrors the operation of the definitions guarded by the CONFIG_CORESIGHT_ETMV3 define above.
Regards
Mike
+static inline void cti_device_release(struct device *dev) {}; +#endif
#endif
2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Adds in code to the CoreSight core to prepare for the addition of the CTI driver.
Adds functionality to add cross reference association between CTI and other CoreSight devices.
Adds functionality to enable associated CTI when CoreSight devices are enabled by the trace path code.
Signed-off-by: Mike Leach mike.leach@linaro.org --- drivers/hwtracing/coresight/coresight.c | 51 ++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 2b0df1a0a8df..01ad708ca7a7 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -214,6 +214,36 @@ void coresight_disclaim_device(void __iomem *base) CS_LOCK(base); }
+/* enable or disable an associated CTI device of the supplied CS device */ +static void +coresight_control_assoc_ectdev(struct coresight_device *csdev, int enable) +{ + int ect_ret = 0; + struct coresight_device *ect_csdev = csdev->ect_dev; + + if (!ect_csdev) + return; + + if (enable) { + if (ect_ops(ect_csdev)->enable) + ect_ret = ect_ops(ect_csdev)->enable(ect_csdev, NULL); + } else { + if (ect_ops(ect_csdev)->disable) + ect_ret = ect_ops(ect_csdev)->disable(ect_csdev, NULL); + } + + dev_info(&csdev->dev, "%s assoc CTI %s for %s\n", + enable ? "Enabling" : "Disabling", + dev_name(&ect_csdev->dev), dev_name(&csdev->dev)); + + /* even if the control failed - we don't want to fail possible trace + * capture. + */ + if (ect_ret) + dev_info(&csdev->dev, "Associated ECT device (%s) %s failed\n", + dev_name(&ect_csdev->dev), enable ? "enable" : "disable"); +} + static int coresight_enable_sink(struct coresight_device *csdev, u32 mode, void *data) { @@ -227,6 +257,7 @@ static int coresight_enable_sink(struct coresight_device *csdev, ret = sink_ops(csdev)->enable(csdev, mode, data); if (ret) return ret; + coresight_control_assoc_ectdev(csdev, 1); csdev->enable = true; }
@@ -240,6 +271,7 @@ static void coresight_disable_sink(struct coresight_device *csdev) if (atomic_dec_return(csdev->refcnt) == 0) { if (sink_ops(csdev)->disable) { sink_ops(csdev)->disable(csdev); + coresight_control_assoc_ectdev(csdev, 0); csdev->enable = false; } } @@ -277,6 +309,7 @@ static int coresight_enable_link(struct coresight_device *csdev, atomic_dec(&csdev->refcnt[refport]); return ret; } + coresight_control_assoc_ectdev(csdev, 1); } }
@@ -312,8 +345,10 @@ static void coresight_disable_link(struct coresight_device *csdev, }
if (atomic_dec_return(&csdev->refcnt[refport]) == 0) { - if (link_ops(csdev)->disable) + if (link_ops(csdev)->disable) { link_ops(csdev)->disable(csdev, inport, outport); + coresight_control_assoc_ectdev(csdev, 0); + } }
for (i = 0; i < nr_conns; i++) @@ -338,6 +373,7 @@ static int coresight_enable_source(struct coresight_device *csdev, u32 mode) ret = source_ops(csdev)->enable(csdev, NULL, mode); if (ret) return ret; + coresight_control_assoc_ectdev(csdev, 1); } csdev->enable = true; } @@ -360,6 +396,7 @@ static bool coresight_disable_source(struct coresight_device *csdev) if (atomic_dec_return(csdev->refcnt) == 0) { if (source_ops(csdev)->disable) source_ops(csdev)->disable(csdev, NULL); + coresight_control_assoc_ectdev(csdev, 0); csdev->enable = false; } return !csdev->enable; @@ -743,6 +780,7 @@ int coresight_enable(struct coresight_device *csdev) goto out; }
+ dev_info(&csdev->dev, "calling coresight_build_path()\n"); path = coresight_build_path(csdev, sink); if (IS_ERR(path)) { pr_err("building path(s) failed\n"); @@ -750,10 +788,12 @@ int coresight_enable(struct coresight_device *csdev) goto out; }
+ dev_info(&csdev->dev, "calling coresight_enable_path()\n"); ret = coresight_enable_path(path, CS_MODE_SYSFS, NULL); if (ret) goto err_path;
+ dev_info(&csdev->dev, "calling coresight_enable_source()\n"); ret = coresight_enable_source(csdev, CS_MODE_SYSFS); if (ret) goto err_source; @@ -924,12 +964,20 @@ static struct device_type coresight_dev_type[] = { { .name = "helper", }, + { + .name = "ect", + }, };
static void coresight_device_release(struct device *dev) { struct coresight_device *csdev = to_coresight_device(dev);
+ /* additional info to release for CTI */ + if ((csdev->type == CORESIGHT_DEV_TYPE_ECT) && + (csdev->subtype.ect_subtype == CORESIGHT_DEV_SUBTYPE_ECT_CTI)) + cti_device_release(dev); + kfree(csdev->conns); kfree(csdev->refcnt); kfree(csdev); @@ -1171,6 +1219,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
coresight_fixup_device_conns(csdev); coresight_fixup_orphan_conns(csdev); + cti_add_assoc_to_csdev(csdev);
mutex_unlock(&coresight_mutex);
Hi Mike,
On 09/01/2019 22:54, Mike Leach wrote:
Adds in code to the CoreSight core to prepare for the addition of the CTI driver.
Adds functionality to add cross reference association between CTI and other CoreSight devices.
Adds functionality to enable associated CTI when CoreSight devices are enabled by the trace path code.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight.c | 51 ++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 2b0df1a0a8df..01ad708ca7a7 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -214,6 +214,36 @@ void coresight_disclaim_device(void __iomem *base) CS_LOCK(base); } +/* enable or disable an associated CTI device of the supplied CS device */ +static void +coresight_control_assoc_ectdev(struct coresight_device *csdev, int enable) +{
- int ect_ret = 0;
- struct coresight_device *ect_csdev = csdev->ect_dev;
- if (!ect_csdev)
return;
- if (enable) {
if (ect_ops(ect_csdev)->enable)
ect_ret = ect_ops(ect_csdev)->enable(ect_csdev, NULL);
- } else {
if (ect_ops(ect_csdev)->disable)
ect_ret = ect_ops(ect_csdev)->disable(ect_csdev, NULL);
- }
- dev_info(&csdev->dev, "%s assoc CTI %s for %s\n",
enable ? "Enabling" : "Disabling",
dev_name(&ect_csdev->dev), dev_name(&csdev->dev));
- /* even if the control failed - we don't want to fail possible trace
* capture.
*/
- if (ect_ret)
dev_info(&csdev->dev, "Associated ECT device (%s) %s failed\n",
dev_name(&ect_csdev->dev), enable ? "enable" : "disable");
+}
As I mentioned in the previous patch, if you make this a subtype of helper device, this should all be done automatically if it is "connected" to any of the devices that appear in the path, which I think is the way to go.
Thoughts ? Suzuki
Hi Suzuki,
On Fri, 11 Jan 2019 at 13:25, Suzuki K Poulose suzuki.poulose@arm.com wrote:
Hi Mike,
On 09/01/2019 22:54, Mike Leach wrote:
Adds in code to the CoreSight core to prepare for the addition of the CTI driver.
Adds functionality to add cross reference association between CTI and other CoreSight devices.
Adds functionality to enable associated CTI when CoreSight devices are enabled by the trace path code.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight.c | 51 ++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 2b0df1a0a8df..01ad708ca7a7 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -214,6 +214,36 @@ void coresight_disclaim_device(void __iomem *base) CS_LOCK(base); }
+/* enable or disable an associated CTI device of the supplied CS device */ +static void +coresight_control_assoc_ectdev(struct coresight_device *csdev, int enable) +{
int ect_ret = 0;
struct coresight_device *ect_csdev = csdev->ect_dev;
if (!ect_csdev)
return;
if (enable) {
if (ect_ops(ect_csdev)->enable)
ect_ret = ect_ops(ect_csdev)->enable(ect_csdev, NULL);
} else {
if (ect_ops(ect_csdev)->disable)
ect_ret = ect_ops(ect_csdev)->disable(ect_csdev, NULL);
}
dev_info(&csdev->dev, "%s assoc CTI %s for %s\n",
enable ? "Enabling" : "Disabling",
dev_name(&ect_csdev->dev), dev_name(&csdev->dev));
/* even if the control failed - we don't want to fail possible trace
* capture.
*/
if (ect_ret)
dev_info(&csdev->dev, "Associated ECT device (%s) %s failed\n",
dev_name(&ect_csdev->dev), enable ? "enable" : "disable");
+}
As I mentioned in the previous patch, if you make this a subtype of helper device, this should all be done automatically if it is "connected" to any of the devices that appear in the path, which I think is the way to go.
Thoughts ? Suzuki
The CTI connection nodes as I am defining them, declare both an associated device and the set of signals going to and from the device. The signals are declared on a per connection basis as they are implementation dependent (other than v8 core CTIs where the interconnection is architecturally set). The devicetree declarations allow this specific hardware signal setup to be captured by the driver and interrogated via the driver API.
Now, CTIs can connect to multiple other CS devices, a CPU, other hardware, or simply run IO signals off the board to no device at all. The current CoreSight standard associates ETM and CPU with a simple phandle reference. The description of connections along the trace data path from ETM source to sink (including the helper devices) use the in-ports / out-ports / endpoints declarations with matching expected on both connected components. I tried using the helper paradigm with the CTI but eventually went in another direction as:-
1) We would have had to declare endpoints on the CPUs in order to get a valid connection - there is no precedent for this in the current device tree definitions. 2) It is possible and valid to create a CTI device with no associated registered component. this is for IO only or exploration of the system where the full connection information is not known. 3) I was concerned about the massive increase in DTS declarations for a single CS component - in Juno on of the CTIs connects to four separate devices. That would be 8 endpoints on the CTI, (one in and out per device x 4 devices) and 2 ep per device. There are 16 CTIs on the DB410 (though we only have details for 6 of these at present). 4) there is no linear path for CTI connectivity - the star topology via the CTM allows complete inter-connectivity - dependent on device programming. I felt this justified a separate class of device.
Therefore I chose to create a devicetree model that followed the CPU / ETM method of phandle reference for all associations - giving a consistent connection declaration methodology for all the connections, irrespective of the type of associated device (or lack thereof). The CTI could still be a sub-type of helper I guess, but the code is still needed to enable alongside the trace data path.
The goal was to create a set of declarations that was flexible enough to be equally used even where the CTI information was lacking. I also wanted something that was relatively easy to add to the devicetree and easy to maintain.
Regards
Mike
On Tue, 15 Jan 2019 at 08:34, Mike Leach mike.leach@linaro.org wrote:
Hi Suzuki,
On Fri, 11 Jan 2019 at 13:25, Suzuki K Poulose suzuki.poulose@arm.com wrote:
Hi Mike,
On 09/01/2019 22:54, Mike Leach wrote:
Adds in code to the CoreSight core to prepare for the addition of the CTI driver.
Adds functionality to add cross reference association between CTI and other CoreSight devices.
Adds functionality to enable associated CTI when CoreSight devices are enabled by the trace path code.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight.c | 51 ++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 2b0df1a0a8df..01ad708ca7a7 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -214,6 +214,36 @@ void coresight_disclaim_device(void __iomem *base) CS_LOCK(base); }
+/* enable or disable an associated CTI device of the supplied CS device */ +static void +coresight_control_assoc_ectdev(struct coresight_device *csdev, int enable) +{
int ect_ret = 0;
struct coresight_device *ect_csdev = csdev->ect_dev;
if (!ect_csdev)
return;
if (enable) {
if (ect_ops(ect_csdev)->enable)
ect_ret = ect_ops(ect_csdev)->enable(ect_csdev, NULL);
} else {
if (ect_ops(ect_csdev)->disable)
ect_ret = ect_ops(ect_csdev)->disable(ect_csdev, NULL);
}
dev_info(&csdev->dev, "%s assoc CTI %s for %s\n",
enable ? "Enabling" : "Disabling",
dev_name(&ect_csdev->dev), dev_name(&csdev->dev));
/* even if the control failed - we don't want to fail possible trace
* capture.
*/
if (ect_ret)
dev_info(&csdev->dev, "Associated ECT device (%s) %s failed\n",
dev_name(&ect_csdev->dev), enable ? "enable" : "disable");
+}
As I mentioned in the previous patch, if you make this a subtype of helper device, this should all be done automatically if it is "connected" to any of the devices that appear in the path, which I think is the way to go.
Thoughts ? Suzuki
The CTI connection nodes as I am defining them, declare both an associated device and the set of signals going to and from the device. The signals are declared on a per connection basis as they are implementation dependent (other than v8 core CTIs where the interconnection is architecturally set). The devicetree declarations allow this specific hardware signal setup to be captured by the driver and interrogated via the driver API.
Now, CTIs can connect to multiple other CS devices, a CPU, other hardware, or simply run IO signals off the board to no device at all. The current CoreSight standard associates ETM and CPU with a simple phandle reference. The description of connections along the trace data path from ETM source to sink (including the helper devices) use the in-ports / out-ports / endpoints declarations with matching expected on both connected components. I tried using the helper paradigm with the CTI but eventually went in another direction as:-
- We would have had to declare endpoints on the CPUs in order to get
a valid connection - there is no precedent for this in the current device tree definitions. 2) It is possible and valid to create a CTI device with no associated registered component. this is for IO only or exploration of the system where the full connection information is not known. 3) I was concerned about the massive increase in DTS declarations for a single CS component - in Juno on of the CTIs connects to four separate devices. That would be 8 endpoints on the CTI, (one in and out per device x 4 devices) and 2 ep per device. There are 16 CTIs on the DB410 (though we only have details for 6 of these at present). 4) there is no linear path for CTI connectivity - the star topology via the CTM allows complete inter-connectivity - dependent on device programming. I felt this justified a separate class of device.
Therefore I chose to create a devicetree model that followed the CPU / ETM method of phandle reference for all associations - giving a consistent connection declaration methodology for all the connections, irrespective of the type of associated device (or lack thereof). The CTI could still be a sub-type of helper I guess, but the code is still needed to enable alongside the trace data path.
The goal was to create a set of declarations that was flexible enough to be equally used even where the CTI information was lacking. I also wanted something that was relatively easy to add to the devicetree and easy to maintain.
I would also really like to re-use the helper abstraction but Mike is making compelling arguments above. I think we should see another revision of this set where the code is spread out over multiple patches before coming up with a decision on this.
Mathieu
Regards
Mike
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK _______________________________________________ CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
On Wed, Jan 09, 2019 at 10:54:36PM +0000, Mike Leach wrote:
Adds in code to the CoreSight core to prepare for the addition of the CTI driver.
Adds functionality to add cross reference association between CTI and other CoreSight devices.
Adds functionality to enable associated CTI when CoreSight devices are enabled by the trace path code.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight.c | 51 ++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 2b0df1a0a8df..01ad708ca7a7 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -214,6 +214,36 @@ void coresight_disclaim_device(void __iomem *base) CS_LOCK(base); } +/* enable or disable an associated CTI device of the supplied CS device */ +static void +coresight_control_assoc_ectdev(struct coresight_device *csdev, int enable) +{
- int ect_ret = 0;
- struct coresight_device *ect_csdev = csdev->ect_dev;
- if (!ect_csdev)
return;
- if (enable) {
if (ect_ops(ect_csdev)->enable)
ect_ret = ect_ops(ect_csdev)->enable(ect_csdev, NULL);
- } else {
if (ect_ops(ect_csdev)->disable)
ect_ret = ect_ops(ect_csdev)->disable(ect_csdev, NULL);
- }
We can't say if this code is correct since we don't know what the enable and disable functions do. I suggest starting by introducing the driver and then using it.
- dev_info(&csdev->dev, "%s assoc CTI %s for %s\n",
enable ? "Enabling" : "Disabling",
dev_name(&ect_csdev->dev), dev_name(&csdev->dev));
- /* even if the control failed - we don't want to fail possible trace
* capture.
*/
s/even/Even
And the format for multi-line comments isn't correct. It should be:
/* * Even if the control failed - we don't want to fail possible trace * capture. */
Shouldn't we want the trace session to fail if enabling the CTI failed? In my opinion we should.
- if (ect_ret)
dev_info(&csdev->dev, "Associated ECT device (%s) %s failed\n",
dev_name(&ect_csdev->dev), enable ? "enable" : "disable");
+}
static int coresight_enable_sink(struct coresight_device *csdev, u32 mode, void *data) { @@ -227,6 +257,7 @@ static int coresight_enable_sink(struct coresight_device *csdev, ret = sink_ops(csdev)->enable(csdev, mode, data); if (ret) return ret;
csdev->enable = true; }coresight_control_assoc_ectdev(csdev, 1);
@@ -240,6 +271,7 @@ static void coresight_disable_sink(struct coresight_device *csdev) if (atomic_dec_return(csdev->refcnt) == 0) { if (sink_ops(csdev)->disable) { sink_ops(csdev)->disable(csdev);
} }coresight_control_assoc_ectdev(csdev, 0); csdev->enable = false;
@@ -277,6 +309,7 @@ static int coresight_enable_link(struct coresight_device *csdev, atomic_dec(&csdev->refcnt[refport]); return ret; }
} }coresight_control_assoc_ectdev(csdev, 1);
@@ -312,8 +345,10 @@ static void coresight_disable_link(struct coresight_device *csdev, } if (atomic_dec_return(&csdev->refcnt[refport]) == 0) {
if (link_ops(csdev)->disable)
if (link_ops(csdev)->disable) { link_ops(csdev)->disable(csdev, inport, outport);
coresight_control_assoc_ectdev(csdev, 0);
}}
for (i = 0; i < nr_conns; i++) @@ -338,6 +373,7 @@ static int coresight_enable_source(struct coresight_device *csdev, u32 mode) ret = source_ops(csdev)->enable(csdev, NULL, mode); if (ret) return ret;
} csdev->enable = true; }coresight_control_assoc_ectdev(csdev, 1);
@@ -360,6 +396,7 @@ static bool coresight_disable_source(struct coresight_device *csdev) if (atomic_dec_return(csdev->refcnt) == 0) { if (source_ops(csdev)->disable) source_ops(csdev)->disable(csdev, NULL);
csdev->enable = false; } return !csdev->enable;coresight_control_assoc_ectdev(csdev, 0);
@@ -743,6 +780,7 @@ int coresight_enable(struct coresight_device *csdev) goto out; }
- dev_info(&csdev->dev, "calling coresight_build_path()\n");
Please remove.
path = coresight_build_path(csdev, sink); if (IS_ERR(path)) { pr_err("building path(s) failed\n"); @@ -750,10 +788,12 @@ int coresight_enable(struct coresight_device *csdev) goto out; }
- dev_info(&csdev->dev, "calling coresight_enable_path()\n");
Please remove.
ret = coresight_enable_path(path, CS_MODE_SYSFS, NULL); if (ret) goto err_path;
- dev_info(&csdev->dev, "calling coresight_enable_source()\n");
Please remove.
ret = coresight_enable_source(csdev, CS_MODE_SYSFS); if (ret) goto err_source; @@ -924,12 +964,20 @@ static struct device_type coresight_dev_type[] = { { .name = "helper", },
- {
.name = "ect",
- },
}; static void coresight_device_release(struct device *dev) { struct coresight_device *csdev = to_coresight_device(dev);
- /* additional info to release for CTI */
- if ((csdev->type == CORESIGHT_DEV_TYPE_ECT) &&
(csdev->subtype.ect_subtype == CORESIGHT_DEV_SUBTYPE_ECT_CTI))
cti_device_release(dev);
Same comment as above regarding the usage of unimplemented functions. A function needs to be introduced before it is used (or in the same patch).
kfree(csdev->conns); kfree(csdev->refcnt); kfree(csdev); @@ -1171,6 +1219,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) coresight_fixup_device_conns(csdev); coresight_fixup_orphan_conns(csdev);
- cti_add_assoc_to_csdev(csdev);
Same comment as above.
mutex_unlock(&coresight_mutex); -- 2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Adds in functions to allow CTI devices to extract standard Coresight information from device trees.
Adds in function to allow location of a registered Coresight device given the device tree node data.
Signed-off-by: Mike Leach mike.leach@linaro.org --- drivers/hwtracing/coresight/of_coresight.c | 65 ++++++++++++++++++++++ 1 file changed, 65 insertions(+)
diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c index 89092f83567e..1d6adfc0057c 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c @@ -295,3 +295,68 @@ of_get_coresight_platform_data(struct device *dev, return pdata; } EXPORT_SYMBOL_GPL(of_get_coresight_platform_data); + +struct coresight_platform_data * +of_get_coresight_ect_platform_data(struct device *dev, + const struct device_node *node) +{ + struct coresight_platform_data *pdata; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + /* Use device name as sysfs handle */ + pdata->name = dev_name(dev); + + return pdata; +} +EXPORT_SYMBOL_GPL(of_get_coresight_ect_platform_data); + +int of_coresight_ect_get_cpu(const struct device_node *node) +{ + int cpu; + struct device_node *dn; + + dn = of_parse_phandle(node, "cpu", 0); + /* CTI Affinity defaults to no cpu */ + if (!dn) + return -1; + cpu = of_cpu_node_to_id(dn); + of_node_put(dn); + + /* No Affinity if no cpu nodes are found */ + return (cpu < 0) ? -1 : cpu; +} +EXPORT_SYMBOL_GPL(of_coresight_ect_get_cpu); + +/* + * Find a registered coresight device from the device_node. + * The node info is associated with the AMBA parent, so we + * search this first to find a name, and use the name to find + * the device on the coresight bus as they are always named the same. + */ +struct coresight_device * +of_coresight_get_cs_device_by_node(struct device_node *node) +{ + struct device *dev, *adev; + struct coresight_device *csdev = NULL; + const char *amba_dev_name = NULL; + + if (!node) + return NULL; + + adev = bus_find_device(&amba_bustype, NULL, node, of_dev_node_match); + if (adev) { + amba_dev_name = dev_name(adev); + dev = bus_find_device_by_name(&coresight_bustype, NULL, + amba_dev_name); + if (dev) { + csdev = to_coresight_device(dev); + put_device(dev); + } + put_device(adev); + } + return csdev; +} +EXPORT_SYMBOL_GPL(of_coresight_get_cs_device_by_node);
On Wed, Jan 09, 2019 at 10:54:37PM +0000, Mike Leach wrote:
Adds in functions to allow CTI devices to extract standard Coresight information from device trees.
Adds in function to allow location of a registered Coresight device given the device tree node data.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/of_coresight.c | 65 ++++++++++++++++++++++ 1 file changed, 65 insertions(+)
diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c index 89092f83567e..1d6adfc0057c 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c @@ -295,3 +295,68 @@ of_get_coresight_platform_data(struct device *dev, return pdata; } EXPORT_SYMBOL_GPL(of_get_coresight_platform_data);
+struct coresight_platform_data * +of_get_coresight_ect_platform_data(struct device *dev,
const struct device_node *node)
+{
- struct coresight_platform_data *pdata;
- pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
return ERR_PTR(-ENOMEM);
- /* Use device name as sysfs handle */
- pdata->name = dev_name(dev);
The above is already done in of_get_coresight_platform_data() and as such I wonder why it needs to be replicated here.
- return pdata;
+} +EXPORT_SYMBOL_GPL(of_get_coresight_ect_platform_data);
+int of_coresight_ect_get_cpu(const struct device_node *node) +{
- int cpu;
- struct device_node *dn;
- dn = of_parse_phandle(node, "cpu", 0);
- /* CTI Affinity defaults to no cpu */
- if (!dn)
return -1;
- cpu = of_cpu_node_to_id(dn);
- of_node_put(dn);
- /* No Affinity if no cpu nodes are found */
- return (cpu < 0) ? -1 : cpu;
+} +EXPORT_SYMBOL_GPL(of_coresight_ect_get_cpu);
+/*
- Find a registered coresight device from the device_node.
- The node info is associated with the AMBA parent, so we
- search this first to find a name, and use the name to find
- the device on the coresight bus as they are always named the same.
- */
+struct coresight_device * +of_coresight_get_cs_device_by_node(struct device_node *node) +{
- struct device *dev, *adev;
- struct coresight_device *csdev = NULL;
- const char *amba_dev_name = NULL;
- if (!node)
return NULL;
- adev = bus_find_device(&amba_bustype, NULL, node, of_dev_node_match);
- if (adev) {
amba_dev_name = dev_name(adev);
dev = bus_find_device_by_name(&coresight_bustype, NULL,
amba_dev_name);
if (dev) {
csdev = to_coresight_device(dev);
put_device(dev);
}
put_device(adev);
- }
- return csdev;
Humm... This means the @csdev associated with @node has already been scanned and added to the coresight_bus, which may not always be true. For now I can't say more since I don't know how it will be used.
+}
+EXPORT_SYMBOL_GPL(of_coresight_get_cs_device_by_node);
2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
On Tue, 15 Jan 2019 at 18:57, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, Jan 09, 2019 at 10:54:37PM +0000, Mike Leach wrote:
Adds in functions to allow CTI devices to extract standard Coresight information from device trees.
Adds in function to allow location of a registered Coresight device given the device tree node data.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/of_coresight.c | 65 ++++++++++++++++++++++ 1 file changed, 65 insertions(+)
diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c index 89092f83567e..1d6adfc0057c 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c @@ -295,3 +295,68 @@ of_get_coresight_platform_data(struct device *dev, return pdata; } EXPORT_SYMBOL_GPL(of_get_coresight_platform_data);
+struct coresight_platform_data * +of_get_coresight_ect_platform_data(struct device *dev,
const struct device_node *node)
+{
struct coresight_platform_data *pdata;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return ERR_PTR(-ENOMEM);
/* Use device name as sysfs handle */
pdata->name = dev_name(dev);
The above is already done in of_get_coresight_platform_data() and as such I wonder why it needs to be replicated here.
The other function goes on to look for in-ports and out-ports - which throws a warning if they are not there. CTI not using these as it is possible to be connected to no-CS devices or simply external IO pins. (there's a fuller explanation of this in my response to suzuki).
return pdata;
+} +EXPORT_SYMBOL_GPL(of_get_coresight_ect_platform_data);
+int of_coresight_ect_get_cpu(const struct device_node *node) +{
int cpu;
struct device_node *dn;
dn = of_parse_phandle(node, "cpu", 0);
/* CTI Affinity defaults to no cpu */
if (!dn)
return -1;
cpu = of_cpu_node_to_id(dn);
of_node_put(dn);
/* No Affinity if no cpu nodes are found */
return (cpu < 0) ? -1 : cpu;
+} +EXPORT_SYMBOL_GPL(of_coresight_ect_get_cpu);
+/*
- Find a registered coresight device from the device_node.
- The node info is associated with the AMBA parent, so we
- search this first to find a name, and use the name to find
- the device on the coresight bus as they are always named the same.
- */
+struct coresight_device * +of_coresight_get_cs_device_by_node(struct device_node *node) +{
struct device *dev, *adev;
struct coresight_device *csdev = NULL;
const char *amba_dev_name = NULL;
if (!node)
return NULL;
adev = bus_find_device(&amba_bustype, NULL, node, of_dev_node_match);
if (adev) {
amba_dev_name = dev_name(adev);
dev = bus_find_device_by_name(&coresight_bustype, NULL,
amba_dev_name);
if (dev) {
csdev = to_coresight_device(dev);
put_device(dev);
}
put_device(adev);
}
return csdev;
Humm... This means the @csdev associated with @node has already been scanned and added to the coresight_bus, which may not always be true. For now I can't say more since I don't know how it will be used.
This is true - it will fail to find a cs-device if not yet registered, or if the associated device is not in fact a coresight component. As we cannot predict the order of registration, the CTI driver does this search on its own registration, with other CS devices looking for matching CTIs when they are registered - there is a complementary function provided as part of the driver - cti_add_assoc_to_csdev(), called just after the existing functions that fixup orphan connections.
Thanks
Mike.
+}
+EXPORT_SYMBOL_GPL(of_coresight_get_cs_device_by_node);
2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
On Wed, 16 Jan 2019 at 04:01, Mike Leach mike.leach@linaro.org wrote:
On Tue, 15 Jan 2019 at 18:57, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, Jan 09, 2019 at 10:54:37PM +0000, Mike Leach wrote:
Adds in functions to allow CTI devices to extract standard Coresight information from device trees.
Adds in function to allow location of a registered Coresight device given the device tree node data.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/of_coresight.c | 65 ++++++++++++++++++++++ 1 file changed, 65 insertions(+)
diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c index 89092f83567e..1d6adfc0057c 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c @@ -295,3 +295,68 @@ of_get_coresight_platform_data(struct device *dev, return pdata; } EXPORT_SYMBOL_GPL(of_get_coresight_platform_data);
+struct coresight_platform_data * +of_get_coresight_ect_platform_data(struct device *dev,
const struct device_node *node)
+{
struct coresight_platform_data *pdata;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return ERR_PTR(-ENOMEM);
/* Use device name as sysfs handle */
pdata->name = dev_name(dev);
The above is already done in of_get_coresight_platform_data() and as such I wonder why it needs to be replicated here.
The other function goes on to look for in-ports and out-ports - which throws a warning if they are not there. CTI not using these as it is possible to be connected to no-CS devices or simply external IO pins. (there's a fuller explanation of this in my response to suzuki).
Typically what happens in those cases is the current code is extended to deal with new requirements. I haven't read your exchange with Suzuki yet - I will do that when I'm done reviewing your work. That way my review isn't influenced by comments that have already been made.
return pdata;
+} +EXPORT_SYMBOL_GPL(of_get_coresight_ect_platform_data);
+int of_coresight_ect_get_cpu(const struct device_node *node) +{
int cpu;
struct device_node *dn;
dn = of_parse_phandle(node, "cpu", 0);
/* CTI Affinity defaults to no cpu */
if (!dn)
return -1;
cpu = of_cpu_node_to_id(dn);
of_node_put(dn);
/* No Affinity if no cpu nodes are found */
return (cpu < 0) ? -1 : cpu;
+} +EXPORT_SYMBOL_GPL(of_coresight_ect_get_cpu);
+/*
- Find a registered coresight device from the device_node.
- The node info is associated with the AMBA parent, so we
- search this first to find a name, and use the name to find
- the device on the coresight bus as they are always named the same.
- */
+struct coresight_device * +of_coresight_get_cs_device_by_node(struct device_node *node) +{
struct device *dev, *adev;
struct coresight_device *csdev = NULL;
const char *amba_dev_name = NULL;
if (!node)
return NULL;
adev = bus_find_device(&amba_bustype, NULL, node, of_dev_node_match);
if (adev) {
amba_dev_name = dev_name(adev);
dev = bus_find_device_by_name(&coresight_bustype, NULL,
amba_dev_name);
if (dev) {
csdev = to_coresight_device(dev);
put_device(dev);
}
put_device(adev);
}
return csdev;
Humm... This means the @csdev associated with @node has already been scanned and added to the coresight_bus, which may not always be true. For now I can't say more since I don't know how it will be used.
This is true - it will fail to find a cs-device if not yet registered, or if the associated device is not in fact a coresight component. As we cannot predict the order of registration, the CTI driver does this search on its own registration, with other CS devices looking for matching CTIs when they are registered - there is a complementary function provided as part of the driver - cti_add_assoc_to_csdev(), called just after the existing functions that fixup orphan connections.
Right but as of this patch neither the driver or function cti_add_assoc_to_csdev() have been implemented, hence the need to refactor the presentation of the work in this set. I will give more details on that topic when I've reviewed patch 07.
Mathieu
Thanks
Mike.
+}
+EXPORT_SYMBOL_GPL(of_coresight_get_cs_device_by_node);
2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
Initial addition of the files for the Coresight CTI driver.
Signed-off-by: Mike Leach mike.leach@linaro.org --- drivers/hwtracing/coresight/coresight-cti.c | 2025 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 195 ++ 2 files changed, 2220 insertions(+) create mode 100644 drivers/hwtracing/coresight/coresight-cti.c create mode 100644 drivers/hwtracing/coresight/coresight-cti.h
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c new file mode 100644 index 000000000000..d06e19f111e2 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -0,0 +1,2025 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018 Linaro Limited, All rights reserved. + * Author: Mike Leach mike.leach@linaro.org + */ + +#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/smp.h> +#include <linux/sysfs.h> +#include <linux/stat.h> +#include <linux/clk.h> +#include <linux/cpu.h> +#include <linux/coresight.h> +#include <linux/coresight-pmu.h> +#include <linux/pm_wakeup.h> +#include <linux/amba/bus.h> +#include <linux/seq_file.h> +#include <linux/uaccess.h> +#include <linux/perf_event.h> +#include <linux/pm_runtime.h> +#include <linux/of.h> +#include <asm/sections.h> +#include <asm/local.h> + +#include "coresight-cti.h" + +#define csdev_to_cti_drvdata(csdev) \ + dev_get_drvdata(csdev->dev.parent) + +/* CTI device tree trigger connection node keyword */ +#define CTI_DT_CONNS "trig-conns" +/* CTI device tree connection property keywords */ +#define CTI_DT_V8ARCH "arm,cti-v8-arch" +#define CTI_DT_TRIGIN_SIGS "arm,trig-in-sigs" +#define CTI_DT_TRIGOUT_SIGS "arm,trig-out-sigs" +#define CTI_DT_FILTER_OUT_SIGS "arm,trig-filters" +#define CTI_DT_CSDEV_ASSOC "arm,cs-dev-assoc" +#define CTI_DT_CTM_ID "arm,cti-ctm-id" +#define CTI_DT_CONN_NAME "arm,trig-conn-name" + + +/** + * CTI devices can be associated with a PE, or be connected to CoreSight + * hardware. We have a list of all CTIs, and a subset array associated + * with individual PEs for pwr management as they will power up in the + * PE power domain. + * + * At this point we assume that the none CPU CTIs are always powered as + * we do with sinks etc. + * + * We leave the client to figure out if all the CTIs are interconnected with + * the same CTM, in general this is the case but does not always have to be. + */ +struct ect_node { + struct cti_drvdata *cti_drv; + struct list_head next; +}; + +/* net of CTI devices connected via CTM */ +LIST_HEAD(ect_net); + +/* protect the list */ +static DEFINE_MUTEX(ect_mutex); + +/* quick reference for CPU CTIs */ +static struct cti_drvdata *cti_cpu_drv[NR_CPUS]; + +/* number of registered CTI devices */ +static int cti_count; + +/* number of cpu associated CTI devices in use */ +static int cti_cpu_count; + +/* CTI / cpu hotplug notification ID */ +static enum cpuhp_state cti_hp_online; + +/* cpu powered and enabled */ +#define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered) + +/* write set of regs to hardware - call with spinlock claimed */ +static void cti_write_all_hw_regs(struct cti_drvdata *drvdata) +{ + struct cti_config *config = &drvdata->config; + int i; + + CS_UNLOCK(drvdata->base); + + /* disable CTI before writing registers */ + writel_relaxed(0, drvdata->base + CTICONTROL); + + /* write the CTI trigger registers */ + for (i = 0; i < config->nr_trig_max; i++) { + writel_relaxed(config->ctiinen[i], drvdata->base + CTIINEN(i)); + writel_relaxed(config->ctiouten[i], + drvdata->base + CTIOUTEN(i)); + } + + /* other regs */ + writel_relaxed(config->ctigate, drvdata->base + CTIGATE); + writel_relaxed(config->asicctl, drvdata->base + ASICCTL); + writel_relaxed(config->ctiappset, drvdata->base + CTIAPPSET); + + /* re-enable CTI */ + writel_relaxed(1, drvdata->base + CTICONTROL); + + CS_LOCK(drvdata->base); +} + +static void cti_enable_hw_smp_call(void *info) +{ + struct cti_drvdata *drvdata = info; + + cti_write_all_hw_regs(drvdata); +} + +/* write regs to hardware and enable */ +static int cti_enable_hw(void *info) +{ + struct cti_drvdata *drvdata = info; + struct cti_config *config = &drvdata->config; + int rc = 0; + + pm_runtime_get_sync(drvdata->dev); + spin_lock(&drvdata->spinlock); + + /* no need to do anything if enabled or unpowered*/ + if (config->hw_enabled || !config->hw_powered) { + spin_unlock(&drvdata->spinlock); + pm_runtime_put(drvdata->dev); + return 0; + } + + if (drvdata->ctidev.cpu >= 0) { + dev_info(drvdata->dev, "cti enable smp call for cpu %d\n", + drvdata->ctidev.cpu); + rc = smp_call_function_single(drvdata->ctidev.cpu, + cti_enable_hw_smp_call, + drvdata, 1); + } else { + dev_info(drvdata->dev, "cti enable not cpu call\n"); + cti_write_all_hw_regs(drvdata); + } + if (!rc) + config->hw_enabled = true; + spin_unlock(&drvdata->spinlock); + return 0; +} + +/* disable hardware */ +static int cti_disable_hw(void *info) +{ + struct cti_drvdata *drvdata = info; + struct cti_config *config = &drvdata->config; + + spin_lock(&drvdata->spinlock); + /* no need to do anything if disabled or cpu unpowered*/ + if (!config->hw_enabled || !config->hw_powered) { + spin_unlock(&drvdata->spinlock); + return 0; + } + + CS_UNLOCK(drvdata->base); + + /* disable CTI */ + writel_relaxed(0, drvdata->base + CTICONTROL); + config->hw_enabled = false; + + CS_LOCK(drvdata->base); + spin_unlock(&drvdata->spinlock); + pm_runtime_put(drvdata->dev); + return 0; +} + +static void +cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value) +{ + CS_UNLOCK(drvdata->base); + writel_relaxed(value, drvdata->base+offset); + CS_LOCK(drvdata->base); +} + +static void cti_set_default_config(struct cti_config *config) +{ + /* Most regs default to 0 as zalloc'ed except...*/ + config->trig_filter_enable = 1; + config->ctigate = ((u32)0x1 << config->nr_ctm_channels) - 1; + atomic_set(&config->enable_req_count, 0); +} + +static void cti_write_intack(struct device *dev, u32 ackval) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + + spin_lock(&drvdata->spinlock); + /* write if enabled */ + if (CTI_PWR_ENA(config)) + cti_write_single_reg(drvdata, CTIINTACK, ackval); + spin_unlock(&drvdata->spinlock); +} + +/* need to update cross references once CTI csdev has been generated. */ +static void cti_update_conn_xrefs(struct cti_drvdata *drvdata) +{ + struct cti_trig_con *tc; + struct cti_device *ctidev = &drvdata->ctidev; + + list_for_each_entry(tc, &ctidev->trig_cons, node) { + if (tc->con_dev) { + tc->con_dev->ect_dev = drvdata->csdev; + dev_info(drvdata->dev, + "Setting assoc csdev (%px) with (%px)\n", + tc->con_dev, drvdata->csdev); + } + } +} + +/* connection information */ +static int cti_add_connection_entry(struct cti_drvdata *drvdata, + struct cti_trig_grp *in_info, + struct cti_trig_grp *out_info, + struct coresight_device *csdev, + const char *assoc_dev_name) +{ + struct cti_trig_con *tc; + struct cti_device *cti_dev = &drvdata->ctidev; + + tc = kzalloc(sizeof(struct cti_trig_con), GFP_KERNEL); + if (tc == 0) + return -ENOMEM; + + tc->con_dev = csdev; + /* prefer actual associated CS device dev name to supplied value - + * which is likely to be node name / other conn name + */ + if (csdev) + tc->con_dev_name = kstrdup(dev_name(&csdev->dev), GFP_KERNEL); + else if (assoc_dev_name != NULL) + tc->con_dev_name = kstrdup(assoc_dev_name, GFP_KERNEL); + tc->con_in = in_info; + tc->con_out = out_info; + list_add_tail(&tc->node, &cti_dev->trig_cons); + cti_dev->nr_trig_con++; + + /* add connection usage bit info to overall info */ + drvdata->config.trig_in_use |= in_info->used_mask; + drvdata->config.trig_out_use |= out_info->used_mask; + + return 0; +} + +/* add a default connection if nothing else is specified. + * single connection based on max in/out info, no assoc device + */ +static int cti_add_default_connection(struct cti_drvdata *drvdata) +{ + struct cti_trig_grp *in, *out; + int n_trigs = drvdata->config.nr_trig_max; + u32 n_trig_mask = (0x1 << n_trigs) - 1; + + in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL); + if (!in) + return -ENOMEM; + + out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL); + if (!out) + return -ENOMEM; + + dev_info(drvdata->dev, "default connection: trigs %d, trig_mask %08x\n", + n_trigs, n_trig_mask); + + /* assume max trigs for in and out, all used */ + in->nr_sigs = n_trigs; + in->used_mask = n_trig_mask; + in->sig_names = NULL; + out->nr_sigs = n_trigs; + out->used_mask = n_trig_mask; + out->sig_names = NULL; + return cti_add_connection_entry(drvdata, in, out, NULL, "default"); +} + +/* create an architecturally defined v8 connection + * must hava a cpu, can have an ETM + */ +static int of_cti_create_v8_connections(struct cti_drvdata *drvdata, + struct device_node *np) +{ + struct cti_device *cti_dev = &drvdata->ctidev; + struct cti_trig_grp *in, *out; + int cpuid = 0; + struct device_node *cs_np; + struct coresight_device *csdev = NULL; + char cpu_name_str[16]; + const char *assoc_name = NULL; + + cpuid = of_coresight_ect_get_cpu(np); + if (cpuid < 0) { + dev_warn(drvdata->dev, "CTI v8 DT binding no cpu\n"); + return -EINVAL; + } + cti_dev->cpu = cpuid; + + /* do the v8 cpu connection */ + in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL); + if (!in) + return -ENOMEM; + + out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL); + if (!out) + return -ENOMEM; + + /* v8 PE CTI config... */ + in->nr_sigs = 3; + in->used_mask = 0x7; + in->sig_names = NULL; + out->nr_sigs = 3; + out->used_mask = 0x7; + out->sig_names = NULL; + scnprintf(cpu_name_str, 16, "cpu%d", cpuid); + cti_add_connection_entry(drvdata, in, out, NULL, cpu_name_str); + + drvdata->config.trig_out_filter = 0x1; /* filter dbgreq */ + + /* v8 ETM associated config... */ + cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0); + if (cs_np) { + in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL); + if (!in) + return -ENOMEM; + + out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL); + if (!out) + return -ENOMEM; + in->nr_sigs = 4; + in->used_mask = 0xF0; + in->sig_names = NULL; + out->nr_sigs = 4; + out->used_mask = 0xF0; + out->sig_names = NULL; + dev_info(drvdata->dev, "v8 finding assoc dev %s\n", + cs_np->full_name); + csdev = of_coresight_get_cs_device_by_node(cs_np); + if (csdev) + assoc_name = dev_name(&csdev->dev); + else + assoc_name = cs_np->full_name; + cti_add_connection_entry(drvdata, in, out, csdev, assoc_name); + of_node_put(cs_np); + } + return 0; +} + +static int of_cti_read_trig_group(struct cti_trig_grp **trig_grp_ptr, + struct device_node *np, + const char *grp_name, + int max_trigs) +{ + int items, err = 0; + u32 value, pidx; + struct cti_trig_grp *grp; + + grp = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL); + if (!grp) + return -ENOMEM; + *trig_grp_ptr = grp; + + items = of_property_count_elems_of_size(np, grp_name, 4); + /* if the property doesn't exist or has no values, then return + * an empty connection group + */ + if (items < 0) + return 0; + + if (items > max_trigs) + return -EINVAL; + + /* set the number of signals and usage mask */ + for (pidx = 0; pidx < items; pidx++) { + err = of_property_read_u32_index(np, grp_name, pidx, &value); + if (err) + return err; + grp->nr_sigs++; + grp->used_mask |= (0x1 << value); + } + return 0; +} + +static int of_cti_create_connection(struct device *dev, + struct device_node *np, + struct cti_drvdata *drvdata) +{ + struct cti_trig_grp *in = 0, *out = 0, *filter = 0; + int cpuid = -1, err = 0, trig_max = drvdata->config.nr_trig_max; + struct device_node *cs_np; + struct coresight_device *csdev = NULL; + const char *assoc_name = "unknown"; + char cpu_name_str[16]; + + /* look for the signals properties. */ + err = of_cti_read_trig_group(&in, np, CTI_DT_TRIGIN_SIGS, trig_max); + if (err) + return err; + err = of_cti_read_trig_group(&out, np, CTI_DT_TRIGOUT_SIGS, trig_max); + if (err) + return err; + err = of_cti_read_trig_group(&filter, np, CTI_DT_FILTER_OUT_SIGS, + trig_max); + if (err) + return err; + + /* read the connection name if set - may be overridden by later */ + of_property_read_string(np, CTI_DT_CONN_NAME, &assoc_name); + + /* associated cpu ? */ + cpuid = of_coresight_ect_get_cpu(np); + drvdata->ctidev.cpu = cpuid; + if (cpuid >= 0) { + scnprintf(cpu_name_str, 16, "cpu%d", cpuid); + assoc_name = cpu_name_str; + } else { + /* associated device ? */ + cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0); + if (cs_np) { + dev_info(dev, "finding assoc dev %s\n", + cs_np->full_name); + csdev = of_coresight_get_cs_device_by_node(cs_np); + if (csdev) /* use device name if csdev found */ + assoc_name = dev_name(&csdev->dev); + else /* otherwise node name for later association */ + assoc_name = cs_np->full_name; + of_node_put(cs_np); + } + } + /* set up a connection */ + err = cti_add_connection_entry(drvdata, in, out, csdev, assoc_name); + /* note any filter info */ + if (!err) + drvdata->config.trig_out_filter |= filter->used_mask; + kfree(filter); + return err; +} + +/* get the hardware configuration & connection data. */ +static int of_cti_get_hw_data(struct device *dev, + struct device_node *np, + struct cti_drvdata *drvdata) +{ + int rc = 0; + struct cti_device *cti_dev = &drvdata->ctidev; + struct device_node *nc = NULL; + + /* get any CTM ID - defaults to 0 */ + of_property_read_u32(np, CTI_DT_CTM_ID, &cti_dev->ctm_id); + + if (of_property_read_bool(np, CTI_DT_V8ARCH)) { + rc = of_cti_create_v8_connections(drvdata, np); + } else { + for_each_child_of_node(np, nc) { + if (of_node_cmp(nc->name, CTI_DT_CONNS) != 0) + continue; + dev_info(dev, "dt found node: %s %s\n", + nc->name, nc->full_name); + rc = of_cti_create_connection(dev, nc, drvdata); + if (rc != 0) + return rc; + } + } + + /* if no connections, just add a single default based on max IN-OUT */ + if (cti_dev->nr_trig_con == 0) + rc = cti_add_default_connection(drvdata); + return rc; +} + +static int +cti_match_con_name(struct cti_device *ctidev, const char *node_name, + const char *csdev_name) +{ + int found = 0; + struct cti_trig_con *trig_con; + + list_for_each_entry(trig_con, &ctidev->trig_cons, node) { + if (trig_con->con_dev_name) { + if (!strcmp(node_name, trig_con->con_dev_name)) { + found = 1; + /* match: so swap in csdev name */ + kfree(trig_con->con_dev_name); + trig_con->con_dev_name = + kstrdup(csdev_name, GFP_KERNEL); + goto cti_con_name_match; + } + } + } +cti_con_name_match: + return found; +} + +/* search the cti list to add an associated CTI into the supplied CS device + * This will set the association if CTI declared before the CS device + */ +int cti_add_assoc_to_csdev(struct coresight_device *csdev) +{ + struct ect_node *ect_item; + struct cti_device *ctidev; + const char *node_name = NULL, *csdev_name; + + /* exit early for no CTIs or self referencing ECT devices.*/ + if (!cti_count) { + dev_info(&csdev->dev, "no CTI to check\n"); + return 0; + } + + if (!csdev->dev.parent->of_node) { + dev_info(&csdev->dev, "No parent of_node pointer\n"); + return 0; + } + + node_name = csdev->dev.parent->of_node->full_name; + + if (!node_name) { + dev_info(&csdev->dev, "%s - bad node name\n", __func__); + return 0; + } + + dev_info(&csdev->dev, "%s - looking for %s\n", __func__, node_name); + + if (!cti_count || (csdev->type == CORESIGHT_DEV_TYPE_ECT)) { + dev_info(&csdev->dev, "no CTI or skip self ref CTI\n"); + return 0; + } + + csdev_name = dev_name(&csdev->dev); + /* for each CTI in list... */ + mutex_lock(&ect_mutex); + list_for_each_entry(ect_item, &ect_net, next) { + ctidev = &ect_item->cti_drv->ctidev; + if (cti_match_con_name(ctidev, node_name, csdev_name)) { + csdev->ect_dev = ect_item->cti_drv->csdev; + dev_info(&csdev->dev, "Found!\n"); + goto cti_found; + } + } +cti_found: + mutex_unlock(&ect_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(cti_add_assoc_to_csdev); + +/* attach/detach channel from trigger - write through if enabled. */ +static int cti_channel_trig_op(struct device *dev, + enum cti_chan_op op, + enum cti_trig_dir direction, + u32 channel_idx, + u32 trigger_idx) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + u32 trig_bitmask; + u32 chan_bitmask; + u32 reg_value; + int reg_offset; + + dev_info(dev, "chan_trig_op: op %d, dir %d, chan %d, trig %d\n", + op, direction, channel_idx, trigger_idx); + + /* ensure indexes in range */ + if ((channel_idx >= config->nr_ctm_channels) || + (trigger_idx >= config->nr_trig_max)) + return -EINVAL; + + trig_bitmask = 0x1 << trigger_idx; + + /* ensure registered triggers and not out filtered */ + if (direction == CTI_TRIG_IN) { + if (!(trig_bitmask & config->trig_in_use)) + return -EINVAL; + } else { + if (!(trig_bitmask & config->trig_out_use)) + return -EINVAL; + + if ((config->trig_filter_enable) && + (config->trig_out_filter & trig_bitmask)) + return -EINVAL; + } + + /* update the local register values */ + chan_bitmask = 0x1 << channel_idx; + reg_offset = (direction == CTI_TRIG_IN ? CTIINEN(trigger_idx) : + CTIOUTEN(trigger_idx)); + + spin_lock(&drvdata->spinlock); + + /* read - modify write - the trigger / channel enable value */ + reg_value = (direction == CTI_TRIG_IN ? config->ctiinen[trigger_idx] : + config->ctiouten[trigger_idx]); + reg_value = (op == CTI_CHAN_ATTACH) ? reg_value | chan_bitmask : + reg_value & (~chan_bitmask); + + /* write local copy */ + if (direction == CTI_TRIG_IN) + config->ctiinen[trigger_idx] = reg_value; + else + config->ctiouten[trigger_idx] = reg_value; + + /* write through if enabled */ + if (CTI_PWR_ENA(config)) + cti_write_single_reg(drvdata, reg_offset, reg_value); + spin_unlock(&drvdata->spinlock); + return 0; +} + +static int cti_channel_gate_op(struct device *dev, + enum cti_chan_gate_op op, + u32 channel_idx) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + u32 chan_bitmask; + u32 reg_value; + int err = 0; + + if (channel_idx >= config->nr_ctm_channels) + return -EINVAL; + + chan_bitmask = 0x1 << channel_idx; + + spin_lock(&drvdata->spinlock); + reg_value = config->ctigate; + switch (op) { + case CTI_GATE_CHAN_ENABLE: + reg_value |= chan_bitmask; + break; + + case CTI_GATE_CHAN_DISABLE: + reg_value &= ~chan_bitmask; + break; + + case CTI_GATE_CHAN_ENABLE_ALL: + reg_value = (0x1 << config->nr_ctm_channels) - 1; + break; + + case CTI_GATE_CHAN_DISABLE_ALL: + reg_value = 0x0; + break; + + default: + err = -EINVAL; + break; + } + if (err == 0) { + config->ctigate = reg_value; + if (CTI_PWR_ENA(config)) + cti_write_single_reg(drvdata, CTIGATE, reg_value); + } + spin_unlock(&drvdata->spinlock); + return err; +} + +static int cti_channel_setop(struct device *dev, + enum cti_chan_set_op op, + u32 channel_idx) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + u32 chan_bitmask; + u32 reg_value; + u32 reg_offset; + int err = 0; + + if (channel_idx >= config->nr_ctm_channels) + return -EINVAL; + + chan_bitmask = 0x1 << channel_idx; + + spin_lock(&drvdata->spinlock); + reg_value = config->ctiappset; + switch (op) { + case CTI_CHAN_SET: + config->ctiappset |= chan_bitmask; + reg_value = config->ctiappset; + reg_offset = CTIAPPSET; + break; + + case CTI_CHAN_CLR: + config->ctiappset &= ~chan_bitmask; + reg_value = chan_bitmask; + reg_offset = CTIAPPCLEAR; + break; + + case CTI_CHAN_PULSE: + config->ctiappset &= ~chan_bitmask; + reg_value = chan_bitmask; + reg_offset = CTIAPPPULSE; + break; + + default: + err = -EINVAL; + break; + } + + if ((err == 0) && CTI_PWR_ENA(config)) + cti_write_single_reg(drvdata, reg_offset, reg_value); + spin_unlock(&drvdata->spinlock); + + return err; +} + +/** cti ect operations **/ +static int cti_enable(struct coresight_device *csdev, void *__unused) +{ + int rc; + struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev); + + atomic_inc(&drvdata->config.enable_req_count); + rc = cti_enable_hw(drvdata); + if (rc) + atomic_dec(&drvdata->config.enable_req_count); + + return rc; +} + +static int cti_disable(struct coresight_device *csdev, void *__unused) +{ + int rc = 0; + struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev); + + if (!atomic_dec_return(&drvdata->config.enable_req_count)) { + rc = cti_disable_hw(drvdata); + if (rc) + atomic_inc(&drvdata->config.enable_req_count); + } + return rc; +} + +const struct coresight_ops_ect cti_ops_ect = { + .enable = cti_enable, + .disable = cti_disable, +}; + +const struct coresight_ops cti_ops = { + .ect_ops = &cti_ops_ect, +}; + +/** attributes **/ + +#define coresight_cti_reg(name, offset) \ + coresight_simple_reg32(struct cti_drvdata, name, offset) + +/* show a simple 32 bit value. if pval is NULL then live read, + * otherwise read from supplied pointer only + */ +static ssize_t cti_reg32_show(struct device *dev, char *buf, + u32 *pval, int reg_offset) +{ + unsigned long val = 0; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + + spin_lock(&drvdata->spinlock); + if (pval) { + val = (unsigned long)*pval; + } else if ((reg_offset >= 0) && CTI_PWR_ENA(config)) { + CS_UNLOCK(drvdata->base); + val = readl_relaxed(drvdata->base + reg_offset); + CS_LOCK(drvdata->base); + } + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +/* store a simple 32 bit value. + * if pval not NULL, then copy to here too, + * if reg_offset >= 0 then write through if enabled. + */ +static ssize_t cti_reg32_store(struct device *dev, const char *buf, + size_t size, u32 *pval, + int reg_offset) +{ + unsigned long val; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + /* local store */ + if (pval) + *pval = (u32)val; + + /* write through of offset and enabled */ + if ((reg_offset >= 0) && CTI_PWR_ENA(config)) + cti_write_single_reg(drvdata, reg_offset, val); + spin_unlock(&drvdata->spinlock); + return size; +} + +/* basic attributes */ +static ssize_t enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int enable_req; + bool enabled, powered, cpuid; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + ssize_t size = 0; + + enable_req = atomic_read(&drvdata->config.enable_req_count); + spin_lock(&drvdata->spinlock); + powered = drvdata->config.hw_powered; + enabled = drvdata->config.hw_enabled; + cpuid = drvdata->ctidev.cpu; + spin_unlock(&drvdata->spinlock); + + if (powered) { + size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n", + enabled ? "enabled" : "disabled", cpuid); + } else if (cpuid >= 0) { + size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n", + enable_req ? "enable req" : "disable req", cpuid); + } else { + size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n", + enabled ? "enabled" : "disabled"); + } + return size; +} + +static ssize_t enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret = 0; + unsigned long val; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + if (val) + ret = cti_enable(drvdata->csdev, NULL); + else + ret = cti_disable(drvdata->csdev, NULL); + if (ret) + return ret; + return size; +} +static DEVICE_ATTR_RW(enable); + +static ssize_t ctmid_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return scnprintf(buf, PAGE_SIZE, "0x%x\n", drvdata->ctidev.ctm_id); +} +static DEVICE_ATTR_RO(ctmid); + +#define LF_ADJUST_BUF_PTRS(n) \ + do { buf_pos += n; buf_size -= n; \ + if (buf_size <= 1) \ + goto lf_buf_full; \ + } while (0) + +static ssize_t list_features_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int used = 0, sig_idx, con_idx, nr_trig_max; + /* buffer vars */ + int buf_size = PAGE_SIZE; + char *buf_pos = buf; + char *con_name; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *cfg = &drvdata->config; + struct cti_device *ctidev = &drvdata->ctidev; + u32 sig_mask; + struct cti_trig_con *con; + + /* basic feature info */ + used = scnprintf(buf_pos, buf_size, + "CTI:%s; Channels:%d; Max Trigs:%d\n", + dev_name(dev), cfg->nr_ctm_channels, cfg->nr_trig_max); + LF_ADJUST_BUF_PTRS(used); + used = scnprintf(buf_pos, buf_size, "CTM id = %d; Num Conns = %d\n", + ctidev->ctm_id, ctidev->nr_trig_con); + LF_ADJUST_BUF_PTRS(used); + + /* list each connection info */ + con_idx = 0; + nr_trig_max = cfg->nr_trig_max; + list_for_each_entry(con, &ctidev->trig_cons, node) { + /* connection name */ + if (con->con_dev_name != NULL) + con_name = con->con_dev_name; + else + con_name = "unknown"; + used = scnprintf(buf_pos, buf_size, "conn(%d:%s)\n", + con_idx, con_name); + LF_ADJUST_BUF_PTRS(used); + con_idx++; + + /* connection signals */ + used = scnprintf(buf_pos, buf_size, "Trig IN(%d) [ ", + con->con_in->nr_sigs); + LF_ADJUST_BUF_PTRS(used); + sig_mask = 0x1; + for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) { + if (sig_mask & con->con_in->used_mask) { + used = scnprintf(buf_pos, buf_size, "%d ", + sig_idx); + LF_ADJUST_BUF_PTRS(used); + } + sig_mask <<= 1; + } + used = scnprintf(buf_pos, buf_size, "]\n"); + LF_ADJUST_BUF_PTRS(used); + + used = scnprintf(buf_pos, buf_size, "Trig OUT(%d) [ ", + con->con_out->nr_sigs); + LF_ADJUST_BUF_PTRS(used); + sig_mask = 0x1; + for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) { + if (sig_mask & con->con_out->used_mask) { + used = scnprintf(buf_pos, buf_size, "%d ", + sig_idx); + LF_ADJUST_BUF_PTRS(used); + } + sig_mask <<= 1; + } + used = scnprintf(buf_pos, buf_size, "]\n"); + LF_ADJUST_BUF_PTRS(used); + } + + if (cfg->trig_out_filter) { + used = scnprintf(buf_pos, buf_size, "Trig OUT_FILTERED [ "); + LF_ADJUST_BUF_PTRS(used); + sig_mask = 0x1; + for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) { + if (sig_mask & cfg->trig_out_filter) { + used = scnprintf(buf_pos, buf_size, "%d ", + sig_idx); + LF_ADJUST_BUF_PTRS(used); + } + sig_mask <<= 1; + } + used = scnprintf(buf_pos, buf_size, "]\n"); + LF_ADJUST_BUF_PTRS(used); + } + +lf_buf_full: + used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE; + return used; +} +static DEVICE_ATTR_RO(list_features); + + +static struct attribute *coresight_cti_attrs[] = { + &dev_attr_enable.attr, + &dev_attr_list_features.attr, + &dev_attr_ctmid.attr, + NULL, +}; + +/* raw register attributes */ +static ssize_t inout_sel_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u32 val; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = (u32)drvdata->config.ctiinout_sel; + return scnprintf(buf, PAGE_SIZE, "%#x\n", val); +} + +static ssize_t inout_sel_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (val > (CTIINOUTEN_MAX-1)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + drvdata->config.ctiinout_sel = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(inout_sel); + +static ssize_t inen_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + int index; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + index = drvdata->config.ctiinout_sel; + val = drvdata->config.ctiinen[index]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "INEN%d %#lx\n", index, val); +} + +static ssize_t inen_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + int index; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + index = config->ctiinout_sel; + config->ctiinen[index] = val; + + /* write through if enabled */ + if (CTI_PWR_ENA(config)) + cti_write_single_reg(drvdata, CTIINEN(index), val); + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(inen); + +static ssize_t outen_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + int index; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + index = drvdata->config.ctiinout_sel; + val = drvdata->config.ctiouten[index]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "OUTEN%d %#lx\n", index, val); +} + +static ssize_t outen_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + int index; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + index = config->ctiinout_sel; + config->ctiouten[index] = val; + + /* write through if enabled */ + if (CTI_PWR_ENA(config)) + cti_write_single_reg(drvdata, CTIOUTEN(index), val); + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(outen); + + +static ssize_t gate_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return cti_reg32_show(dev, buf, &drvdata->config.ctigate, -1); +} + +static ssize_t gate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return cti_reg32_store(dev, buf, size, + &drvdata->config.ctigate, CTIGATE); +} +static DEVICE_ATTR_RW(gate); + +static ssize_t asicctl_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return cti_reg32_show(dev, buf, &drvdata->config.asicctl, -1); +} + +static ssize_t asicctl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return cti_reg32_store(dev, buf, size, + &drvdata->config.asicctl, ASICCTL); +} +static DEVICE_ATTR_RW(asicctl); + +static ssize_t intack_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + cti_write_intack(dev, val); + return size; +} +static DEVICE_ATTR_WO(intack); + +static ssize_t appset_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return cti_reg32_show(dev, buf, &drvdata->config.ctiappset, -1); +} + +static ssize_t appset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + return cti_reg32_store(dev, buf, size, + &drvdata->config.ctiappset, CTIAPPSET); +} +static DEVICE_ATTR_RW(appset); + +static ssize_t appclear_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val, mask; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + + /* a 1'b1 in appclr clears down the same bit in appset*/ + mask = ~val; + config->ctiappset &= mask; + + /* write through if enabled */ + if (CTI_PWR_ENA(config)) + cti_write_single_reg(drvdata, CTIAPPCLEAR, val); + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_WO(appclear); + +static ssize_t apppulse_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val, mask; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + + /* a 1'b1 in apppulse sets then clears the bit, + * effectively clears down the same bit in appset + */ + mask = ~val; + config->ctiappset &= mask; + + /* write through if enabled */ + if (CTI_PWR_ENA(config)) + cti_write_single_reg(drvdata, CTIAPPPULSE, val); + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_WO(apppulse); + +static ssize_t itchout_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return cti_reg32_show(dev, buf, NULL, ITCHOUT); +} + +static ssize_t itchout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return cti_reg32_store(dev, buf, size, NULL, ITCHOUT); +} +static DEVICE_ATTR_RW(itchout); + +static ssize_t ittrigout_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return cti_reg32_show(dev, buf, NULL, ITTRIGOUT); +} + +static ssize_t ittrigout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return cti_reg32_store(dev, buf, size, NULL, ITTRIGOUT); +} +static DEVICE_ATTR_RW(ittrigout); + +static ssize_t itchinack_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return cti_reg32_store(dev, buf, size, NULL, ITCHINACK); +} +static DEVICE_ATTR_WO(itchinack); + +static ssize_t ittriginack_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return cti_reg32_store(dev, buf, size, NULL, ITTRIGINACK); +} +static DEVICE_ATTR_WO(ittriginack); + +static ssize_t itctrl_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return cti_reg32_show(dev, buf, NULL, CORESIGHT_ITCTRL); +} + +static ssize_t itctrl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return cti_reg32_store(dev, buf, size, NULL, CORESIGHT_ITCTRL); +} +static DEVICE_ATTR_RW(itctrl); + +coresight_cti_reg(triginstatus, CTITRIGINSTATUS); +coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS); +coresight_cti_reg(chinstatus, CTICHINSTATUS); +coresight_cti_reg(choutstatus, CTICHOUTSTATUS); +coresight_cti_reg(ittrigin, ITTRIGIN); +coresight_cti_reg(itchin, ITCHIN); +coresight_cti_reg(itchoutack, ITCHOUTACK); +coresight_cti_reg(ittrigoutack, ITTRIGOUTACK); + +static struct attribute *coresight_cti_regs_attrs[] = { + &dev_attr_inout_sel.attr, + &dev_attr_inen.attr, + &dev_attr_outen.attr, + &dev_attr_gate.attr, + &dev_attr_asicctl.attr, + &dev_attr_intack.attr, + &dev_attr_appset.attr, + &dev_attr_appclear.attr, + &dev_attr_apppulse.attr, + &dev_attr_triginstatus.attr, + &dev_attr_trigoutstatus.attr, + &dev_attr_chinstatus.attr, + &dev_attr_choutstatus.attr, + &dev_attr_itctrl.attr, + &dev_attr_ittrigin.attr, + &dev_attr_itchin.attr, + &dev_attr_ittrigout.attr, + &dev_attr_itchout.attr, + &dev_attr_itchoutack.attr, + &dev_attr_ittrigoutack.attr, + &dev_attr_ittriginack.attr, + &dev_attr_itchinack.attr, + NULL, +}; + +/* channel / trigger api */ +static int +cti_trig_op_parse(struct device *dev, enum cti_chan_op op, + enum cti_trig_dir dir, const char *buf, size_t size) +{ + u32 chan_idx; + u32 trig_idx; + int items, err = size; + + /* extract chan idx and trigger idx */ + items = sscanf(buf, "%d %d", &chan_idx, &trig_idx); + if (items) { + err = cti_channel_trig_op(dev, op, dir, chan_idx, trig_idx); + if (!err) + err = size; + } else + err = -EINVAL; + return err; +} + +static ssize_t trigin_attach_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_IN, + buf, size); +} +static DEVICE_ATTR_WO(trigin_attach); + +static ssize_t trigin_detach_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_IN, + buf, size); +} +static DEVICE_ATTR_WO(trigin_detach); + +static ssize_t trigout_attach_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_OUT, + buf, size); +} +static DEVICE_ATTR_WO(trigout_attach); + +static ssize_t trigout_detach_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_OUT, + buf, size); +} +static DEVICE_ATTR_WO(trigout_detach); + + +static ssize_t gate_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int err = 0, channel = 0, items; + + if (strcmp(buf, "all") == 0) { + err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE_ALL, 0); + } else { + items = sscanf(buf, "%d", &channel); + if (!items) + return -EINVAL; + err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE, 0); + } + return err ? err : size; +} +static DEVICE_ATTR_WO(gate_enable); + +static ssize_t gate_disable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int err = 0, channel = 0, items; + + if (strcmp(buf, "all") == 0) { + err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE_ALL, 0); + } else { + items = sscanf(buf, "%d", &channel); + if (!items) + return -EINVAL; + err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE, 0); + } + return err ? err : size; +} +static DEVICE_ATTR_WO(gate_disable); + +static int +chan_op_parse(struct device *dev, enum cti_chan_set_op op, const char *buf) +{ + int err = 0, channel = 0, items; + + items = sscanf(buf, "%d", &channel); + if (!items) + return -EINVAL; + err = cti_channel_setop(dev, op, channel); + return err; + +} + +static ssize_t chan_set_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int err = chan_op_parse(dev, CTI_CHAN_SET, buf); + + return err ? err : size; +} +static DEVICE_ATTR_WO(chan_set); + +static ssize_t chan_clear_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int err = chan_op_parse(dev, CTI_CHAN_CLR, buf); + + return err ? err : size; +} +static DEVICE_ATTR_WO(chan_clear); + +static ssize_t chan_pulse_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int err = chan_op_parse(dev, CTI_CHAN_PULSE, buf); + + return err ? err : size; +} +static DEVICE_ATTR_WO(chan_pulse); + +static ssize_t trig_filter_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + val = drvdata->config.trig_filter_enable; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%ld (%s)\n", val, + val ? "enabled" : "disabled"); +} + +static ssize_t trig_filter_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + drvdata->config.trig_filter_enable = val ? 1 : 0; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(trig_filter_enable); + +/* clear all xtrigger / channel programming */ +static ssize_t reset_xtrigs_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int i; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + + spin_lock(&drvdata->spinlock); + + /* clear the CTI trigger / channel programming registers */ + for (i = 0; i < config->nr_trig_max; i++) { + config->ctiinen[i] = 0; + config->ctiouten[i] = 0; + } + + /* clear the other regs */ + config->ctigate = (0x1 << config->nr_ctm_channels) - 1; + config->asicctl = 0; + config->ctiappset = 0; + config->ctiinout_sel = 0; + + /* if enabled then write through */ + if (CTI_PWR_ENA(config)) + cti_write_all_hw_regs(drvdata); + + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_WO(reset_xtrigs); + +/* list attachments by channel */ +#define LX_ADJUST_BUF_PTRS(n) \ + do { buf_pos += n; buf_size -= n; \ + if (buf_size <= 1) \ + goto lx_buf_full; \ + } while (0) + +static ssize_t list_xtrigs_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int used = 0, nr_channels, chan_idx, reg_idx; + /* buffer vars */ + int buf_size = PAGE_SIZE; + char *buf_pos = buf; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *cfg = &drvdata->config; + u32 chan_mask; + + nr_channels = cfg->nr_ctm_channels; + + used = scnprintf(buf_pos, buf_size, "CTI:%s; Channels:%d\n", + dev_name(dev), nr_channels); + LX_ADJUST_BUF_PTRS(used); + + /* show the IN and OUT triggers per channel */ + for (chan_idx = 0; chan_idx < nr_channels; chan_idx++) { + chan_mask = 0x1 << chan_idx; + used = scnprintf(buf_pos, buf_size, "Chan %d: IN [ ", chan_idx); + LX_ADJUST_BUF_PTRS(used); + + for (reg_idx = 0; + reg_idx < drvdata->config.nr_trig_max; + reg_idx++) { + if (chan_mask & cfg->ctiinen[reg_idx]) { + used = scnprintf(buf_pos, buf_size, "%d ", + reg_idx); + LX_ADJUST_BUF_PTRS(used); + } + } + used = scnprintf(buf_pos, buf_size, "]; OUT [ "); + LX_ADJUST_BUF_PTRS(used); + + for (reg_idx = 0; + reg_idx < drvdata->config.nr_trig_max; + reg_idx++) { + if (chan_mask & cfg->ctiouten[reg_idx]) { + used = scnprintf(buf_pos, buf_size, "%d ", + reg_idx); + LX_ADJUST_BUF_PTRS(used); + } + } + used = scnprintf(buf_pos, buf_size, "];\n"); + LX_ADJUST_BUF_PTRS(used); + } + + /* show the channels enabled via ctigate */ + used = scnprintf(buf_pos, buf_size, "Gate Channels Enabled: [ "); + LX_ADJUST_BUF_PTRS(used); + + if (cfg->ctigate == 0) { + used = scnprintf(buf_pos, buf_size, "None ]\n"); + LX_ADJUST_BUF_PTRS(used); + } else { + for (chan_idx = 0; chan_idx < nr_channels; chan_idx++) { + chan_mask = 0x1 << chan_idx; + if (chan_mask & cfg->ctigate) { + used = scnprintf(buf_pos, buf_size, "%d ", + chan_idx); + LX_ADJUST_BUF_PTRS(used); + } + } + used = scnprintf(buf_pos, buf_size, "]\n"); + LX_ADJUST_BUF_PTRS(used); + } + +lx_buf_full: + used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE; + return used; +} +static DEVICE_ATTR_RO(list_xtrigs); + +#define LCIU_ADJUST_BUF_PTRS(n) \ + do { buf_pos += n; buf_size -= n; \ + if (buf_size <= 1) \ + goto lciu_buf_full; \ + } while (0) + +static ssize_t list_chan_inuse_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int used = 0, nr_channels, i; + /* buffer vars */ + int buf_size = PAGE_SIZE; + char *buf_pos = buf; + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct cti_config *config = &drvdata->config; + u32 inuse_bits = 0, chan_mask, chan_bit_mask; + + nr_channels = config->nr_ctm_channels; + spin_lock(&drvdata->spinlock); + for (i = 0; i < config->nr_trig_max; i++) { + inuse_bits |= config->ctiinen[i]; + inuse_bits |= config->ctiouten[i]; + } + spin_unlock(&drvdata->spinlock); + + /* print in use channels */ + chan_mask = ((u32)(0x1 << nr_channels)) - 1; + if (inuse_bits & chan_mask) { + used = scnprintf(buf_pos, buf_size, "Chan in use [ "); + LCIU_ADJUST_BUF_PTRS(used); + + for (i = 0; i < nr_channels; i++) { + chan_bit_mask = 0x1 << i; + if (chan_bit_mask & inuse_bits) { + used = scnprintf(buf_pos, buf_size, "%d ", i); + LCIU_ADJUST_BUF_PTRS(used); + } + } + + used = scnprintf(buf_pos, buf_size, "]\n"); + LCIU_ADJUST_BUF_PTRS(used); + + } else { + used = scnprintf(buf_pos, buf_size, "Chan in use [ none ]\n"); + LCIU_ADJUST_BUF_PTRS(used); + } + + /* print free channels */ + inuse_bits = ~inuse_bits; /* flip for free channels */ + if (inuse_bits & chan_mask) { + used = scnprintf(buf_pos, buf_size, "Chan free [ "); + LCIU_ADJUST_BUF_PTRS(used); + + for (i = 0; i < nr_channels; i++) { + chan_bit_mask = 0x1 << i; + if (chan_bit_mask & inuse_bits) { + used = scnprintf(buf_pos, buf_size, "%d ", i); + LCIU_ADJUST_BUF_PTRS(used); + } + } + + used = scnprintf(buf_pos, buf_size, "]\n"); + LCIU_ADJUST_BUF_PTRS(used); + } else { + used = scnprintf(buf_pos, buf_size, "Chan free [ none ]\n"); + LCIU_ADJUST_BUF_PTRS(used); + } + +lciu_buf_full: + used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE; + return used; +} +static DEVICE_ATTR_RO(list_chan_inuse); + +static struct attribute *coresight_cti_channel_attrs[] = { + &dev_attr_trigin_attach.attr, + &dev_attr_trigin_detach.attr, + &dev_attr_trigout_attach.attr, + &dev_attr_trigout_detach.attr, + &dev_attr_gate_enable.attr, + &dev_attr_gate_disable.attr, + &dev_attr_chan_set.attr, + &dev_attr_chan_clear.attr, + &dev_attr_chan_pulse.attr, + &dev_attr_trig_filter_enable.attr, + &dev_attr_list_xtrigs.attr, + &dev_attr_reset_xtrigs.attr, + &dev_attr_list_chan_inuse.attr, + NULL, +}; + +/* coresight management registers */ +coresight_cti_reg(devaff0, CTIDEVAFF0); +coresight_cti_reg(devaff1, CTIDEVAFF1); +coresight_cti_reg(authstatus, CORESIGHT_AUTHSTATUS); +coresight_cti_reg(devarch, CORESIGHT_DEVARCH); +coresight_cti_reg(devid, CORESIGHT_DEVID); +coresight_cti_reg(devtype, CORESIGHT_DEVTYPE); +coresight_cti_reg(pidr0, CORESIGHT_PERIPHIDR0); +coresight_cti_reg(pidr1, CORESIGHT_PERIPHIDR1); +coresight_cti_reg(pidr2, CORESIGHT_PERIPHIDR2); +coresight_cti_reg(pidr3, CORESIGHT_PERIPHIDR3); +coresight_cti_reg(pidr4, CORESIGHT_PERIPHIDR4); + +static struct attribute *coresight_cti_mgmt_attrs[] = { + &dev_attr_devaff0.attr, + &dev_attr_devaff1.attr, + &dev_attr_authstatus.attr, + &dev_attr_devarch.attr, + &dev_attr_devid.attr, + &dev_attr_devtype.attr, + &dev_attr_pidr0.attr, + &dev_attr_pidr1.attr, + &dev_attr_pidr2.attr, + &dev_attr_pidr3.attr, + &dev_attr_pidr4.attr, + NULL, +}; + +static const struct attribute_group coresight_cti_group = { + .attrs = coresight_cti_attrs, +}; + +static const struct attribute_group coresight_cti_regs_group = { + .attrs = coresight_cti_regs_attrs, + .name = "regs", +}; + +static const struct attribute_group coresight_cti_channels_group = { + .attrs = coresight_cti_channel_attrs, + .name = "channels", +}; + +static const struct attribute_group coresight_cti_mgmt_group = { + .attrs = coresight_cti_mgmt_attrs, + .name = "mgmt", +}; + +const struct attribute_group *coresight_cti_groups[] = { + &coresight_cti_group, + &coresight_cti_regs_group, + &coresight_cti_channels_group, + &coresight_cti_mgmt_group, + NULL, +}; + +static int cti_starting_cpu(unsigned int cpu) +{ + struct cti_drvdata *drvdata = cti_cpu_drv[cpu]; + + if (!drvdata) + return 0; + spin_lock(&drvdata->spinlock); + drvdata->config.hw_powered = true; + spin_unlock(&drvdata->spinlock); + if (atomic_read(&drvdata->config.enable_req_count)) + cti_enable_hw(drvdata); + return 0; +} + +static int cti_stopping_cpu(unsigned int cpu) +{ + struct cti_drvdata *drvdata = cti_cpu_drv[cpu]; + + if (!drvdata) + return 0; + cti_disable_hw(drvdata); + spin_lock(&drvdata->spinlock); + drvdata->config.hw_powered = false; + spin_unlock(&drvdata->spinlock); + return 0; +} + +static void cti_smp_set_powered(void *info) +{ + struct cti_drvdata *drvdata = info; + + drvdata->config.hw_powered = true; +} + +/* setup power handling on a cti that has an association with a cpu */ +static int cti_setup_hp_cpu_affinity(struct cti_drvdata *drvdata) +{ + int ret = 0; + + /* CPU affinity - add to quick lookup array + * and register cpu hp callbacks first time out + */ + cpus_read_lock(); + cti_cpu_drv[drvdata->ctidev.cpu] = drvdata; + if (!cti_cpu_count) { + cti_hp_online = cpuhp_setup_state_nocalls_cpuslocked( + CPUHP_AP_ONLINE_DYN, "arm/coresightcti:online", + cti_starting_cpu, cti_stopping_cpu); + + if (cti_hp_online < 0) { + ret = cti_hp_online; + cti_cpu_drv[drvdata->ctidev.cpu] = 0; + goto cti_hp_cpu_done; + } + } + cti_cpu_count++; + + /* setup powered flag by running function of assoc cpu */ + if (smp_call_function_single(drvdata->ctidev.cpu, cti_smp_set_powered, + drvdata, 1)) { + drvdata->config.hw_powered = false; + } + +cti_hp_cpu_done: + cpus_read_unlock(); + return ret; +} + +/* back out hotplug notifications on error */ +static void cti_clear_hp_cpu_affinity(struct cti_drvdata *drvdata) +{ + if (!drvdata) + return; + + if (drvdata->ctidev.cpu >= 0) { + cti_cpu_drv[drvdata->ctidev.cpu] = 0; + cti_cpu_count--; + if ((cti_cpu_count == 0) && (cti_hp_online > 0)) + cpuhp_remove_state_nocalls(cti_hp_online); + } +} + +static int cti_probe(struct amba_device *adev, const struct amba_id *id) +{ + int ret = 0; + u32 devid; + void __iomem *base; + struct device *dev = &adev->dev; + struct cti_drvdata *drvdata = 0; + struct coresight_desc cti_desc; + struct coresight_platform_data *pdata = NULL; + struct resource *res = &adev->res; + struct device_node *np = adev->dev.of_node; + struct ect_node *ect_nd = 0; + + /* boilerplate code to set up the basics */ + dev_info(dev, "%s(ID = %x)\n", __func__, id->id); + if (np) { + pdata = of_get_coresight_ect_platform_data(dev, np); + if (IS_ERR(pdata)) { + dev_info(dev, "of_get_coresight_ect_platform err\n"); + ret = PTR_ERR(pdata); + goto err_out; + } + dev->platform_data = pdata; + } + /* node to keep track of CTI net */ + ect_nd = kzalloc(sizeof(struct ect_node), GFP_KERNEL); + if (!ect_nd) { + ret = -ENOMEM; + goto err_out; + } + + /* driver data*/ + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) { + ret = -ENOMEM; + dev_info(dev, "%s, mem err\n", __func__); + goto err_out; + } + + /* links between dev and drvdata*/ + drvdata->dev = dev; + dev_set_drvdata(dev, drvdata); + + /* default CTI device info */ + drvdata->ctidev.cpu = -1; + drvdata->ctidev.nr_trig_con = 0; + drvdata->ctidev.ctm_id = 0; + INIT_LIST_HEAD(&drvdata->ctidev.trig_cons); + + /* Validity for the resource is already checked by the AMBA core */ + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) { + ret = PTR_ERR(base); + dev_info(dev, "%s, remap err\n", __func__); + goto err_out; + } + + drvdata->base = base; + + spin_lock_init(&drvdata->spinlock); + + /* look at the HW DEVID register for some of the HW settings */ + devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID); + drvdata->config.nr_trig_max = (int)((devid & 0xFF00) >> 8); + /* no current hardware should exceed this, but protect the driver + * in case of fault / out of spec hw + */ + if (drvdata->config.nr_trig_max > CTIINOUTEN_MAX) { + dev_warn_once(dev, + "Limiting HW MaxTrig value(%d) to driver max(%d)\n", + drvdata->config.nr_trig_max, CTIINOUTEN_MAX); + drvdata->config.nr_trig_max = CTIINOUTEN_MAX; + } + + drvdata->config.nr_ctm_channels = (int)((devid & 0xF0000) >> 16); + dev_info(dev, "DevID %08x, trigs=%d, chans=%d\n", devid, + drvdata->config.nr_trig_max, drvdata->config.nr_ctm_channels); + + /** additional parse the .dts for connections and signals */ + of_cti_get_hw_data(dev, np, drvdata); + + cti_set_default_config(&drvdata->config); + + /* setup cpu related CTI devices, otherwise assume powered */ + if (drvdata->ctidev.cpu >= 0) { + ret = cti_setup_hp_cpu_affinity(drvdata); + if (ret < 0) + goto err_out; + } else + drvdata->config.hw_powered = true; + + /* set up coresight component description */ + cti_desc.pdata = pdata; + cti_desc.type = CORESIGHT_DEV_TYPE_ECT; + cti_desc.subtype.ect_subtype = CORESIGHT_DEV_SUBTYPE_ECT_CTI; + cti_desc.ops = &cti_ops; + cti_desc.groups = coresight_cti_groups; + cti_desc.dev = dev; + drvdata->csdev = coresight_register(&cti_desc); + if (IS_ERR(drvdata->csdev)) { + ret = PTR_ERR(drvdata->csdev); + pr_err("%s: CS register failed\n", pdata->name); + goto err_out; + } + + /* add to list of CTI devices */ + mutex_lock(&ect_mutex); + ect_nd->cti_drv = drvdata; + list_add(&ect_nd->next, &ect_net); + mutex_unlock(&ect_mutex); + + /* set any cross references */ + cti_update_conn_xrefs(drvdata); + + /* all done - dec pm refcount */ + pm_runtime_put(&adev->dev); + dev_info(dev, "%s: initialized\n", pdata->name); + cti_count++; + dev_info(dev, "%s - ok\n", __func__); + return 0; + +err_out: + cti_clear_hp_cpu_affinity(drvdata); + dev_info(dev, "%s - err_out\n", __func__); + return ret; +} + +/* free up CTI specific resources + * - called from coresight_device_release on coresight_unregister. + */ +void cti_device_release(struct device *dev) +{ + struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct ect_node *ect_item, *ect_tmp; + struct cti_trig_con *tc, *tc_tmp; + + /* free up resources associated with the cti connections */ + if (drvdata->ctidev.cpu >= 0) + cti_cpu_drv[drvdata->ctidev.cpu] = 0; + + list_for_each_entry_safe(tc, tc_tmp, + &drvdata->ctidev.trig_cons, node) { + kfree(tc->con_in); + kfree(tc->con_out); + kfree(tc->con_dev_name); + list_del(&tc->node); + kfree(tc); + } + + /* remove from the list */ + mutex_lock(&ect_mutex); + list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, next) { + if (ect_item->cti_drv == drvdata) { + list_del(&ect_item->next); + kfree(ect_item); + goto ect_list_item_removed; + } + } +ect_list_item_removed: + mutex_unlock(&ect_mutex); + + kfree(drvdata); +} +EXPORT_SYMBOL_GPL(cti_device_release); + +#define CTI_AMBA_ID(pid) \ + { \ + .id = pid, \ + .mask = 0x000fffff, \ + } + +static struct amba_cs_uci_id uci_id_cti[] = { + { + /* CTI UCI data */ + .devarch = 0x47701a14, /* devarch value for CTI v2 */ + .devarch_mask = 0xfff0ffff, + .devtype = 0x00000014, /* maj(0x4-debug) min(0x1-ECT) */ + } +}; + +#define CTI_AMBA_UCI_ID(pid) \ + { \ + .id = pid, \ + .mask = 0x000fffff, \ + .data = uci_id_cti, \ + } + + +static const struct amba_id cti_ids[] = { + CTI_AMBA_ID(0x000bb906), /* Coresight CTI (SoC 400), C-A72, C-A57 */ + CTI_AMBA_ID(0x000bb922), /* CTI - C-A8 */ + CTI_AMBA_ID(0x000bb9a8), /* CTI - C-A53 */ + CTI_AMBA_ID(0x000bb9aa), /* CTI - C-A73 */ + CTI_AMBA_UCI_ID(0x000bb9da), /* CTI - C-A35 */ + CTI_AMBA_UCI_ID(0x000bb9ed), /* Coresight CTI (SoC 600) */ + CTI_AMBA_UCI_ID(0x000bb95d), /* test UCI - ETM PID A53 */ + { 0, 0}, +}; + +static struct amba_driver cti_driver = { + .drv = { + .name = "coresight-cti", + .owner = THIS_MODULE, + .suppress_bind_attrs = true, + }, + .probe = cti_probe, + .id_table = cti_ids, +}; +builtin_amba_driver(cti_driver); diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h new file mode 100644 index 000000000000..09cb985ac0ea --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018 Linaro Limited, All rights reserved. + * Author: Mike Leach mike.leach@linaro.org + */ + + +#ifndef _CORESIGHT_CORESIGHT_CTI_H +#define _CORESIGHT_CORESIGHT_CTI_H + +#include <asm/local.h> +#include <linux/spinlock.h> +#include "coresight-priv.h" + +/* + * Device registers + * 0x000 - 0x144: CTI programming and status + * 0xEDC - 0xEF8: CTI integration test. + * 0xF00 - 0xFFC: Coresight management registers. + */ +/* CTI programming registers */ +#define CTICONTROL 0x000 +#define CTIINTACK 0x010 +#define CTIAPPSET 0x014 +#define CTIAPPCLEAR 0x018 +#define CTIAPPPULSE 0x01C +#define CTIINEN(n) (0x020 + (4 * n)) +#define CTIOUTEN(n) (0x0A0 + (4 * n)) +#define CTITRIGINSTATUS 0x130 +#define CTITRIGOUTSTATUS 0x134 +#define CTICHINSTATUS 0x138 +#define CTICHOUTSTATUS 0x13C +#define CTIGATE 0x140 +#define ASICCTL 0x144 +/* Integration test registers */ +#define ITCHINACK 0xEDC /* WO CTI CSSoc 400 only*/ +#define ITTRIGINACK 0xEE0 /* WO CTI CSSoc 400 only*/ +#define ITCHOUT 0xEE4 /* WO RW-600 */ +#define ITTRIGOUT 0xEE8 /* WO RW-600 */ +#define ITCHOUTACK 0xEEC /* RO CTI CSSoc 400 only*/ +#define ITTRIGOUTACK 0xEF0 /* RO CTI CSSoc 400 only*/ +#define ITCHIN 0xEF4 /* RO */ +#define ITTRIGIN 0xEF8 /* RO */ +/* management registers */ +#define CTIDEVAFF0 0xFA8 +#define CTIDEVAFF1 0xFAC + + +/* CTI CSSoc 600 has a max of 32 trigger signals per direction. + * CTI CSSoc 400 has 8 IO triggers - other CTIs can be impl def. + * Max of in and out defined in the DEVID register. + * - pick up actual number used from .dts parameters if present. + */ +#define CTIINOUTEN_MAX 32 + +/** + * Group of related trigger signals + * + * @nr_sigs: number of signals in the group. + * @used_mask: bitmask representing the signal indexes in the group. + * @sig_names: list of names for the signals. + */ +struct cti_trig_grp { + int nr_sigs; + u32 used_mask; + char *sig_names; +}; + +/** + * Trigger connection - connection between a CTI and other (coresight) device + * lists input and output trigger signals for the device + * + * @con_in: connected CTIIN signals for the device. + * @con_out: connected CTIOUT signals for the device. + * @con_dev: coresight device connected to the CTI, NULL if not CS device + * @con_dev_name: name of connected device (CS or CPU) + * @node: entry node in list of connections. + */ +struct cti_trig_con { + struct cti_trig_grp *con_in; + struct cti_trig_grp *con_out; + struct coresight_device *con_dev; + char *con_dev_name; + struct list_head node; +}; + +/** + * struct cti_device - description of CTI device properties. + * + * @nt_trig_con: Number of external devices connected to this device. + * @ctm_id: which CTM this device is connected to (by default it is + * assumed there is a single CTM per SoC, ID 0). + * @trig_cons: list of connections to this device. + * @cpu: CPU ID if associated with CPU, -1 otherwise. + * + */ +struct cti_device { + int nr_trig_con; + u32 ctm_id; + struct list_head trig_cons; + int cpu; +}; + +/** + * struct cti_config - configuration of the CTI device hardware + * + * hardware description from RO ID regs + * @nr_trig_max: Max number of trigger signals implemented on device. + * (max of trig_in or trig_out) + * @nr_ctm_channels: number of available CTM channels + * + * cti enable control + * @enable_req_count: CTI is enabled alongside >=1 associated devices. + * @hw_enabled: true if hw is currently enabled. + * @hw_powered: true if associated cpu powered on, or no cpu. + * + * registered triggers and filtering + * @trig_in_use: bitfield of in triggers registered as in use. + * @trig_out_use: bitfield of out triggers registered as in use. + * @trig_out_filter: bitfield of out triggers that are blocked if filter + * enabled. Typically this would be dbgreq / restart on a core CTI. + * @trig_filter_enable: 1 if filtering enabled. + * + * cti software programmable regs: + * @ctiappset: CTI Software application channel set. + * @ctiinout_sel: register selector for INEN and OUTEN regs. + * @ctiinen: enable input trigger to a channel. + * @ctiouten: enable output trigger from a channel. + * @ctigate: gate channel output from CTI to CTM. + */ +struct cti_config { + /* hardware description */ + int nr_ctm_channels; + int nr_trig_max; + /* cti enable control */ + atomic_t enable_req_count; + bool hw_enabled; + bool hw_powered; + /* registered triggers and filtering */ + u32 trig_in_use; + u32 trig_out_use; + u32 trig_out_filter; + int trig_filter_enable; + /* cti cross trig programmable regs */ + u32 ctiappset; + u8 ctiinout_sel; + u32 ctiinen[CTIINOUTEN_MAX]; + u32 ctiouten[CTIINOUTEN_MAX]; + u32 ctigate; + u32 asicctl; +}; + +/** + * struct cti_drvdata - specifics for the CTI device + * @base: Memory mapped base address for this component. + * @dev: The device entity associated to this component. + * @csdev: Standard CoreSight device information. + * @ctidev: Extra information needed by the CTI/CTM framework. + * @spinlock: Control data access to one at a time. + * @config: Configuration data for this CTI device. + * + */ +struct cti_drvdata { + void __iomem *base; + struct device *dev; + struct coresight_device *csdev; + struct cti_device ctidev; + spinlock_t spinlock; + struct cti_config config; +}; + +enum cti_chan_op { + CTI_CHAN_ATTACH, + CTI_CHAN_DETACH, +}; + +enum cti_trig_dir { + CTI_TRIG_IN, + CTI_TRIG_OUT, +}; + +enum cti_chan_gate_op { + CTI_GATE_CHAN_ENABLE, + CTI_GATE_CHAN_DISABLE, + CTI_GATE_CHAN_ENABLE_ALL, + CTI_GATE_CHAN_DISABLE_ALL, +}; + +enum cti_chan_set_op { + CTI_CHAN_SET, + CTI_CHAN_CLR, + CTI_CHAN_PULSE, +}; + +#endif /* _CORESIGHT_CORESIGHT_CTI_H */
On Wed, Jan 09, 2019 at 10:54:38PM +0000, Mike Leach wrote:
Initial addition of the files for the Coresight CTI driver.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-cti.c | 2025 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 195 ++ 2 files changed, 2220 insertions(+) create mode 100644 drivers/hwtracing/coresight/coresight-cti.c create mode 100644 drivers/hwtracing/coresight/coresight-cti.h
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c new file mode 100644 index 000000000000..d06e19f111e2 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -0,0 +1,2025 @@ +/* SPDX-License-Identifier: GPL-2.0 */
https://elixir.bootlin.com/linux/latest/source/drivers/hwtracing/coresight/c...
+/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/smp.h> +#include <linux/sysfs.h> +#include <linux/stat.h> +#include <linux/clk.h> +#include <linux/cpu.h> +#include <linux/coresight.h> +#include <linux/coresight-pmu.h> +#include <linux/pm_wakeup.h> +#include <linux/amba/bus.h> +#include <linux/seq_file.h> +#include <linux/uaccess.h> +#include <linux/perf_event.h> +#include <linux/pm_runtime.h> +#include <linux/of.h> +#include <asm/sections.h> +#include <asm/local.h>
There is a new trend in kernel code now that requires to sort header file declarations in alphabetical order. I know it is annoying but you will be called on it.
+#include "coresight-cti.h"
+#define csdev_to_cti_drvdata(csdev) \
- dev_get_drvdata(csdev->dev.parent)
+/* CTI device tree trigger connection node keyword */ +#define CTI_DT_CONNS "trig-conns" +/* CTI device tree connection property keywords */ +#define CTI_DT_V8ARCH "arm,cti-v8-arch" +#define CTI_DT_TRIGIN_SIGS "arm,trig-in-sigs" +#define CTI_DT_TRIGOUT_SIGS "arm,trig-out-sigs" +#define CTI_DT_FILTER_OUT_SIGS "arm,trig-filters" +#define CTI_DT_CSDEV_ASSOC "arm,cs-dev-assoc" +#define CTI_DT_CTM_ID "arm,cti-ctm-id" +#define CTI_DT_CONN_NAME "arm,trig-conn-name"
+/**
- CTI devices can be associated with a PE, or be connected to CoreSight
- hardware. We have a list of all CTIs, and a subset array associated
- with individual PEs for pwr management as they will power up in the
- PE power domain.
- At this point we assume that the none CPU CTIs are always powered as
- we do with sinks etc.
- We leave the client to figure out if all the CTIs are interconnected with
- the same CTM, in general this is the case but does not always have to be.
- */
+struct ect_node {
- struct cti_drvdata *cti_drv;
- struct list_head next;
+};
+/* net of CTI devices connected via CTM */ +LIST_HEAD(ect_net);
+/* protect the list */ +static DEFINE_MUTEX(ect_mutex);
+/* quick reference for CPU CTIs */ +static struct cti_drvdata *cti_cpu_drv[NR_CPUS];
+/* number of registered CTI devices */ +static int cti_count;
+/* number of cpu associated CTI devices in use */ +static int cti_cpu_count;
+/* CTI / cpu hotplug notification ID */ +static enum cpuhp_state cti_hp_online;
+/* cpu powered and enabled */ +#define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered)
+/* write set of regs to hardware - call with spinlock claimed */ +static void cti_write_all_hw_regs(struct cti_drvdata *drvdata) +{
- struct cti_config *config = &drvdata->config;
- int i;
- CS_UNLOCK(drvdata->base);
- /* disable CTI before writing registers */
- writel_relaxed(0, drvdata->base + CTICONTROL);
- /* write the CTI trigger registers */
- for (i = 0; i < config->nr_trig_max; i++) {
writel_relaxed(config->ctiinen[i], drvdata->base + CTIINEN(i));
writel_relaxed(config->ctiouten[i],
drvdata->base + CTIOUTEN(i));
- }
- /* other regs */
- writel_relaxed(config->ctigate, drvdata->base + CTIGATE);
- writel_relaxed(config->asicctl, drvdata->base + ASICCTL);
- writel_relaxed(config->ctiappset, drvdata->base + CTIAPPSET);
- /* re-enable CTI */
- writel_relaxed(1, drvdata->base + CTICONTROL);
- CS_LOCK(drvdata->base);
+}
+static void cti_enable_hw_smp_call(void *info) +{
- struct cti_drvdata *drvdata = info;
- cti_write_all_hw_regs(drvdata);
+}
+/* write regs to hardware and enable */ +static int cti_enable_hw(void *info) +{
- struct cti_drvdata *drvdata = info;
- struct cti_config *config = &drvdata->config;
- int rc = 0;
- pm_runtime_get_sync(drvdata->dev);
- spin_lock(&drvdata->spinlock);
- /* no need to do anything if enabled or unpowered*/
- if (config->hw_enabled || !config->hw_powered) {
spin_unlock(&drvdata->spinlock);
pm_runtime_put(drvdata->dev);
return 0;
- }
- if (drvdata->ctidev.cpu >= 0) {
dev_info(drvdata->dev, "cti enable smp call for cpu %d\n",
drvdata->ctidev.cpu);
rc = smp_call_function_single(drvdata->ctidev.cpu,
cti_enable_hw_smp_call,
drvdata, 1);
- } else {
dev_info(drvdata->dev, "cti enable not cpu call\n");
cti_write_all_hw_regs(drvdata);
- }
- if (!rc)
config->hw_enabled = true;
- spin_unlock(&drvdata->spinlock);
- return 0;
+}
+/* disable hardware */ +static int cti_disable_hw(void *info) +{
- struct cti_drvdata *drvdata = info;
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- /* no need to do anything if disabled or cpu unpowered*/
- if (!config->hw_enabled || !config->hw_powered) {
spin_unlock(&drvdata->spinlock);
return 0;
- }
- CS_UNLOCK(drvdata->base);
- /* disable CTI */
- writel_relaxed(0, drvdata->base + CTICONTROL);
- config->hw_enabled = false;
- CS_LOCK(drvdata->base);
- spin_unlock(&drvdata->spinlock);
- pm_runtime_put(drvdata->dev);
- return 0;
+}
+static void +cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value) +{
- CS_UNLOCK(drvdata->base);
- writel_relaxed(value, drvdata->base+offset);
- CS_LOCK(drvdata->base);
+}
+static void cti_set_default_config(struct cti_config *config) +{
- /* Most regs default to 0 as zalloc'ed except...*/
- config->trig_filter_enable = 1;
- config->ctigate = ((u32)0x1 << config->nr_ctm_channels) - 1;
- atomic_set(&config->enable_req_count, 0);
+}
+static void cti_write_intack(struct device *dev, u32 ackval) +{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- /* write if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIINTACK, ackval);
- spin_unlock(&drvdata->spinlock);
+}
+/* need to update cross references once CTI csdev has been generated. */ +static void cti_update_conn_xrefs(struct cti_drvdata *drvdata) +{
- struct cti_trig_con *tc;
- struct cti_device *ctidev = &drvdata->ctidev;
- list_for_each_entry(tc, &ctidev->trig_cons, node) {
if (tc->con_dev) {
tc->con_dev->ect_dev = drvdata->csdev;
dev_info(drvdata->dev,
"Setting assoc csdev (%px) with (%px)\n",
tc->con_dev, drvdata->csdev);
}
- }
+}
+/* connection information */ +static int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name)
+{
- struct cti_trig_con *tc;
- struct cti_device *cti_dev = &drvdata->ctidev;
- tc = kzalloc(sizeof(struct cti_trig_con), GFP_KERNEL);
- if (tc == 0)
return -ENOMEM;
- tc->con_dev = csdev;
- /* prefer actual associated CS device dev name to supplied value -
* which is likely to be node name / other conn name
*/
- if (csdev)
tc->con_dev_name = kstrdup(dev_name(&csdev->dev), GFP_KERNEL);
- else if (assoc_dev_name != NULL)
tc->con_dev_name = kstrdup(assoc_dev_name, GFP_KERNEL);
- tc->con_in = in_info;
- tc->con_out = out_info;
- list_add_tail(&tc->node, &cti_dev->trig_cons);
- cti_dev->nr_trig_con++;
- /* add connection usage bit info to overall info */
- drvdata->config.trig_in_use |= in_info->used_mask;
- drvdata->config.trig_out_use |= out_info->used_mask;
- return 0;
+}
+/* add a default connection if nothing else is specified.
- single connection based on max in/out info, no assoc device
- */
+static int cti_add_default_connection(struct cti_drvdata *drvdata) +{
- struct cti_trig_grp *in, *out;
- int n_trigs = drvdata->config.nr_trig_max;
- u32 n_trig_mask = (0x1 << n_trigs) - 1;
- in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!in)
return -ENOMEM;
- out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!out)
return -ENOMEM;
- dev_info(drvdata->dev, "default connection: trigs %d, trig_mask %08x\n",
n_trigs, n_trig_mask);
- /* assume max trigs for in and out, all used */
- in->nr_sigs = n_trigs;
- in->used_mask = n_trig_mask;
- in->sig_names = NULL;
- out->nr_sigs = n_trigs;
- out->used_mask = n_trig_mask;
- out->sig_names = NULL;
- return cti_add_connection_entry(drvdata, in, out, NULL, "default");
+}
+/* create an architecturally defined v8 connection
- must hava a cpu, can have an ETM
- */
+static int of_cti_create_v8_connections(struct cti_drvdata *drvdata,
struct device_node *np)
+{
- struct cti_device *cti_dev = &drvdata->ctidev;
- struct cti_trig_grp *in, *out;
- int cpuid = 0;
- struct device_node *cs_np;
- struct coresight_device *csdev = NULL;
- char cpu_name_str[16];
- const char *assoc_name = NULL;
- cpuid = of_coresight_ect_get_cpu(np);
- if (cpuid < 0) {
dev_warn(drvdata->dev, "CTI v8 DT binding no cpu\n");
return -EINVAL;
- }
- cti_dev->cpu = cpuid;
- /* do the v8 cpu connection */
- in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!in)
return -ENOMEM;
- out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!out)
return -ENOMEM;
- /* v8 PE CTI config... */
- in->nr_sigs = 3;
- in->used_mask = 0x7;
- in->sig_names = NULL;
- out->nr_sigs = 3;
- out->used_mask = 0x7;
- out->sig_names = NULL;
- scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
- cti_add_connection_entry(drvdata, in, out, NULL, cpu_name_str);
- drvdata->config.trig_out_filter = 0x1; /* filter dbgreq */
- /* v8 ETM associated config... */
- cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
- if (cs_np) {
in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!in)
return -ENOMEM;
out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!out)
return -ENOMEM;
in->nr_sigs = 4;
in->used_mask = 0xF0;
in->sig_names = NULL;
out->nr_sigs = 4;
out->used_mask = 0xF0;
out->sig_names = NULL;
dev_info(drvdata->dev, "v8 finding assoc dev %s\n",
cs_np->full_name);
csdev = of_coresight_get_cs_device_by_node(cs_np);
if (csdev)
assoc_name = dev_name(&csdev->dev);
else
assoc_name = cs_np->full_name;
cti_add_connection_entry(drvdata, in, out, csdev, assoc_name);
of_node_put(cs_np);
- }
- return 0;
+}
+static int of_cti_read_trig_group(struct cti_trig_grp **trig_grp_ptr,
struct device_node *np,
const char *grp_name,
int max_trigs)
+{
- int items, err = 0;
- u32 value, pidx;
- struct cti_trig_grp *grp;
- grp = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!grp)
return -ENOMEM;
- *trig_grp_ptr = grp;
- items = of_property_count_elems_of_size(np, grp_name, 4);
- /* if the property doesn't exist or has no values, then return
* an empty connection group
*/
- if (items < 0)
return 0;
- if (items > max_trigs)
return -EINVAL;
- /* set the number of signals and usage mask */
- for (pidx = 0; pidx < items; pidx++) {
err = of_property_read_u32_index(np, grp_name, pidx, &value);
if (err)
return err;
grp->nr_sigs++;
grp->used_mask |= (0x1 << value);
- }
- return 0;
+}
+static int of_cti_create_connection(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
- struct cti_trig_grp *in = 0, *out = 0, *filter = 0;
- int cpuid = -1, err = 0, trig_max = drvdata->config.nr_trig_max;
- struct device_node *cs_np;
- struct coresight_device *csdev = NULL;
- const char *assoc_name = "unknown";
- char cpu_name_str[16];
- /* look for the signals properties. */
- err = of_cti_read_trig_group(&in, np, CTI_DT_TRIGIN_SIGS, trig_max);
- if (err)
return err;
- err = of_cti_read_trig_group(&out, np, CTI_DT_TRIGOUT_SIGS, trig_max);
- if (err)
return err;
- err = of_cti_read_trig_group(&filter, np, CTI_DT_FILTER_OUT_SIGS,
trig_max);
- if (err)
return err;
- /* read the connection name if set - may be overridden by later */
- of_property_read_string(np, CTI_DT_CONN_NAME, &assoc_name);
- /* associated cpu ? */
- cpuid = of_coresight_ect_get_cpu(np);
- drvdata->ctidev.cpu = cpuid;
- if (cpuid >= 0) {
scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
assoc_name = cpu_name_str;
- } else {
/* associated device ? */
cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
if (cs_np) {
dev_info(dev, "finding assoc dev %s\n",
cs_np->full_name);
csdev = of_coresight_get_cs_device_by_node(cs_np);
if (csdev) /* use device name if csdev found */
assoc_name = dev_name(&csdev->dev);
else /* otherwise node name for later association */
assoc_name = cs_np->full_name;
of_node_put(cs_np);
}
- }
- /* set up a connection */
- err = cti_add_connection_entry(drvdata, in, out, csdev, assoc_name);
- /* note any filter info */
- if (!err)
drvdata->config.trig_out_filter |= filter->used_mask;
- kfree(filter);
- return err;
+}
+/* get the hardware configuration & connection data. */ +static int of_cti_get_hw_data(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
- int rc = 0;
- struct cti_device *cti_dev = &drvdata->ctidev;
- struct device_node *nc = NULL;
- /* get any CTM ID - defaults to 0 */
- of_property_read_u32(np, CTI_DT_CTM_ID, &cti_dev->ctm_id);
- if (of_property_read_bool(np, CTI_DT_V8ARCH)) {
rc = of_cti_create_v8_connections(drvdata, np);
- } else {
for_each_child_of_node(np, nc) {
if (of_node_cmp(nc->name, CTI_DT_CONNS) != 0)
continue;
dev_info(dev, "dt found node: %s %s\n",
nc->name, nc->full_name);
rc = of_cti_create_connection(dev, nc, drvdata);
if (rc != 0)
return rc;
}
- }
- /* if no connections, just add a single default based on max IN-OUT */
- if (cti_dev->nr_trig_con == 0)
rc = cti_add_default_connection(drvdata);
- return rc;
+}
+static int +cti_match_con_name(struct cti_device *ctidev, const char *node_name,
const char *csdev_name)
+{
- int found = 0;
- struct cti_trig_con *trig_con;
- list_for_each_entry(trig_con, &ctidev->trig_cons, node) {
if (trig_con->con_dev_name) {
if (!strcmp(node_name, trig_con->con_dev_name)) {
found = 1;
/* match: so swap in csdev name */
kfree(trig_con->con_dev_name);
trig_con->con_dev_name =
kstrdup(csdev_name, GFP_KERNEL);
goto cti_con_name_match;
}
}
- }
+cti_con_name_match:
- return found;
+}
+/* search the cti list to add an associated CTI into the supplied CS device
- This will set the association if CTI declared before the CS device
- */
+int cti_add_assoc_to_csdev(struct coresight_device *csdev) +{
- struct ect_node *ect_item;
- struct cti_device *ctidev;
- const char *node_name = NULL, *csdev_name;
- /* exit early for no CTIs or self referencing ECT devices.*/
- if (!cti_count) {
dev_info(&csdev->dev, "no CTI to check\n");
return 0;
- }
- if (!csdev->dev.parent->of_node) {
dev_info(&csdev->dev, "No parent of_node pointer\n");
return 0;
- }
- node_name = csdev->dev.parent->of_node->full_name;
- if (!node_name) {
dev_info(&csdev->dev, "%s - bad node name\n", __func__);
return 0;
- }
- dev_info(&csdev->dev, "%s - looking for %s\n", __func__, node_name);
- if (!cti_count || (csdev->type == CORESIGHT_DEV_TYPE_ECT)) {
dev_info(&csdev->dev, "no CTI or skip self ref CTI\n");
return 0;
- }
- csdev_name = dev_name(&csdev->dev);
- /* for each CTI in list... */
- mutex_lock(&ect_mutex);
- list_for_each_entry(ect_item, &ect_net, next) {
ctidev = &ect_item->cti_drv->ctidev;
if (cti_match_con_name(ctidev, node_name, csdev_name)) {
csdev->ect_dev = ect_item->cti_drv->csdev;
dev_info(&csdev->dev, "Found!\n");
goto cti_found;
}
- }
+cti_found:
- mutex_unlock(&ect_mutex);
- return 0;
+} +EXPORT_SYMBOL_GPL(cti_add_assoc_to_csdev);
+/* attach/detach channel from trigger - write through if enabled. */ +static int cti_channel_trig_op(struct device *dev,
enum cti_chan_op op,
enum cti_trig_dir direction,
u32 channel_idx,
u32 trigger_idx)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- u32 trig_bitmask;
- u32 chan_bitmask;
- u32 reg_value;
- int reg_offset;
- dev_info(dev, "chan_trig_op: op %d, dir %d, chan %d, trig %d\n",
op, direction, channel_idx, trigger_idx);
- /* ensure indexes in range */
- if ((channel_idx >= config->nr_ctm_channels) ||
(trigger_idx >= config->nr_trig_max))
return -EINVAL;
- trig_bitmask = 0x1 << trigger_idx;
- /* ensure registered triggers and not out filtered */
- if (direction == CTI_TRIG_IN) {
if (!(trig_bitmask & config->trig_in_use))
return -EINVAL;
- } else {
if (!(trig_bitmask & config->trig_out_use))
return -EINVAL;
if ((config->trig_filter_enable) &&
(config->trig_out_filter & trig_bitmask))
return -EINVAL;
- }
- /* update the local register values */
- chan_bitmask = 0x1 << channel_idx;
- reg_offset = (direction == CTI_TRIG_IN ? CTIINEN(trigger_idx) :
CTIOUTEN(trigger_idx));
- spin_lock(&drvdata->spinlock);
- /* read - modify write - the trigger / channel enable value */
- reg_value = (direction == CTI_TRIG_IN ? config->ctiinen[trigger_idx] :
config->ctiouten[trigger_idx]);
- reg_value = (op == CTI_CHAN_ATTACH) ? reg_value | chan_bitmask :
reg_value & (~chan_bitmask);
- /* write local copy */
- if (direction == CTI_TRIG_IN)
config->ctiinen[trigger_idx] = reg_value;
- else
config->ctiouten[trigger_idx] = reg_value;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
- spin_unlock(&drvdata->spinlock);
- return 0;
+}
+static int cti_channel_gate_op(struct device *dev,
enum cti_chan_gate_op op,
u32 channel_idx)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- u32 chan_bitmask;
- u32 reg_value;
- int err = 0;
- if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
- chan_bitmask = 0x1 << channel_idx;
- spin_lock(&drvdata->spinlock);
- reg_value = config->ctigate;
- switch (op) {
- case CTI_GATE_CHAN_ENABLE:
reg_value |= chan_bitmask;
break;
- case CTI_GATE_CHAN_DISABLE:
reg_value &= ~chan_bitmask;
break;
- case CTI_GATE_CHAN_ENABLE_ALL:
reg_value = (0x1 << config->nr_ctm_channels) - 1;
break;
- case CTI_GATE_CHAN_DISABLE_ALL:
reg_value = 0x0;
break;
- default:
err = -EINVAL;
break;
- }
- if (err == 0) {
config->ctigate = reg_value;
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIGATE, reg_value);
- }
- spin_unlock(&drvdata->spinlock);
- return err;
+}
+static int cti_channel_setop(struct device *dev,
enum cti_chan_set_op op,
u32 channel_idx)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- u32 chan_bitmask;
- u32 reg_value;
- u32 reg_offset;
- int err = 0;
- if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
- chan_bitmask = 0x1 << channel_idx;
- spin_lock(&drvdata->spinlock);
- reg_value = config->ctiappset;
- switch (op) {
- case CTI_CHAN_SET:
config->ctiappset |= chan_bitmask;
reg_value = config->ctiappset;
reg_offset = CTIAPPSET;
break;
- case CTI_CHAN_CLR:
config->ctiappset &= ~chan_bitmask;
reg_value = chan_bitmask;
reg_offset = CTIAPPCLEAR;
break;
- case CTI_CHAN_PULSE:
config->ctiappset &= ~chan_bitmask;
reg_value = chan_bitmask;
reg_offset = CTIAPPPULSE;
break;
- default:
err = -EINVAL;
break;
- }
- if ((err == 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
- spin_unlock(&drvdata->spinlock);
- return err;
+}
+/** cti ect operations **/ +static int cti_enable(struct coresight_device *csdev, void *__unused) +{
- int rc;
- struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
- atomic_inc(&drvdata->config.enable_req_count);
- rc = cti_enable_hw(drvdata);
- if (rc)
atomic_dec(&drvdata->config.enable_req_count);
- return rc;
+}
+static int cti_disable(struct coresight_device *csdev, void *__unused) +{
- int rc = 0;
- struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
- if (!atomic_dec_return(&drvdata->config.enable_req_count)) {
rc = cti_disable_hw(drvdata);
if (rc)
atomic_inc(&drvdata->config.enable_req_count);
- }
- return rc;
+}
+const struct coresight_ops_ect cti_ops_ect = {
- .enable = cti_enable,
- .disable = cti_disable,
+};
+const struct coresight_ops cti_ops = {
- .ect_ops = &cti_ops_ect,
+};
+/** attributes **/
+#define coresight_cti_reg(name, offset) \
- coresight_simple_reg32(struct cti_drvdata, name, offset)
+/* show a simple 32 bit value. if pval is NULL then live read,
- otherwise read from supplied pointer only
- */
+static ssize_t cti_reg32_show(struct device *dev, char *buf,
u32 *pval, int reg_offset)
+{
- unsigned long val = 0;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- if (pval) {
val = (unsigned long)*pval;
- } else if ((reg_offset >= 0) && CTI_PWR_ENA(config)) {
CS_UNLOCK(drvdata->base);
val = readl_relaxed(drvdata->base + reg_offset);
CS_LOCK(drvdata->base);
- }
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+/* store a simple 32 bit value.
- if pval not NULL, then copy to here too,
- if reg_offset >= 0 then write through if enabled.
- */
+static ssize_t cti_reg32_store(struct device *dev, const char *buf,
size_t size, u32 *pval,
int reg_offset)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- /* local store */
- if (pval)
*pval = (u32)val;
- /* write through of offset and enabled */
- if ((reg_offset >= 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, val);
- spin_unlock(&drvdata->spinlock);
- return size;
+}
+/* basic attributes */ +static ssize_t enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- int enable_req;
- bool enabled, powered, cpuid;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- ssize_t size = 0;
- enable_req = atomic_read(&drvdata->config.enable_req_count);
- spin_lock(&drvdata->spinlock);
- powered = drvdata->config.hw_powered;
- enabled = drvdata->config.hw_enabled;
- cpuid = drvdata->ctidev.cpu;
- spin_unlock(&drvdata->spinlock);
- if (powered) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled", cpuid);
- } else if (cpuid >= 0) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req", cpuid);
- } else {
size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n",
enabled ? "enabled" : "disabled");
- }
- return size;
+}
+static ssize_t enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int ret = 0;
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- ret = kstrtoul(buf, 16, &val);
- if (ret)
return ret;
- if (val)
ret = cti_enable(drvdata->csdev, NULL);
- else
ret = cti_disable(drvdata->csdev, NULL);
- if (ret)
return ret;
- return size;
+} +static DEVICE_ATTR_RW(enable);
+static ssize_t ctmid_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return scnprintf(buf, PAGE_SIZE, "0x%x\n", drvdata->ctidev.ctm_id);
+} +static DEVICE_ATTR_RO(ctmid);
+#define LF_ADJUST_BUF_PTRS(n) \
- do { buf_pos += n; buf_size -= n; \
- if (buf_size <= 1) \
goto lf_buf_full; \
- } while (0)
+static ssize_t list_features_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- int used = 0, sig_idx, con_idx, nr_trig_max;
- /* buffer vars */
- int buf_size = PAGE_SIZE;
- char *buf_pos = buf;
- char *con_name;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *cfg = &drvdata->config;
- struct cti_device *ctidev = &drvdata->ctidev;
- u32 sig_mask;
- struct cti_trig_con *con;
- /* basic feature info */
- used = scnprintf(buf_pos, buf_size,
"CTI:%s; Channels:%d; Max Trigs:%d\n",
dev_name(dev), cfg->nr_ctm_channels, cfg->nr_trig_max);
- LF_ADJUST_BUF_PTRS(used);
- used = scnprintf(buf_pos, buf_size, "CTM id = %d; Num Conns = %d\n",
ctidev->ctm_id, ctidev->nr_trig_con);
- LF_ADJUST_BUF_PTRS(used);
- /* list each connection info */
- con_idx = 0;
- nr_trig_max = cfg->nr_trig_max;
- list_for_each_entry(con, &ctidev->trig_cons, node) {
/* connection name */
if (con->con_dev_name != NULL)
con_name = con->con_dev_name;
else
con_name = "unknown";
used = scnprintf(buf_pos, buf_size, "conn(%d:%s)\n",
con_idx, con_name);
LF_ADJUST_BUF_PTRS(used);
con_idx++;
/* connection signals */
used = scnprintf(buf_pos, buf_size, "Trig IN(%d) [ ",
con->con_in->nr_sigs);
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & con->con_in->used_mask) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
used = scnprintf(buf_pos, buf_size, "Trig OUT(%d) [ ",
con->con_out->nr_sigs);
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & con->con_out->used_mask) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
- }
- if (cfg->trig_out_filter) {
used = scnprintf(buf_pos, buf_size, "Trig OUT_FILTERED [ ");
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & cfg->trig_out_filter) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
- }
+lf_buf_full:
- used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
- return used;
+} +static DEVICE_ATTR_RO(list_features);
+static struct attribute *coresight_cti_attrs[] = {
- &dev_attr_enable.attr,
- &dev_attr_list_features.attr,
- &dev_attr_ctmid.attr,
- NULL,
+};
+/* raw register attributes */ +static ssize_t inout_sel_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- u32 val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- val = (u32)drvdata->config.ctiinout_sel;
- return scnprintf(buf, PAGE_SIZE, "%#x\n", val);
+}
+static ssize_t inout_sel_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- if (val > (CTIINOUTEN_MAX-1))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- drvdata->config.ctiinout_sel = val;
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(inout_sel);
+static ssize_t inen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- spin_lock(&drvdata->spinlock);
- index = drvdata->config.ctiinout_sel;
- val = drvdata->config.ctiinen[index];
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "INEN%d %#lx\n", index, val);
+}
+static ssize_t inen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- index = config->ctiinout_sel;
- config->ctiinen[index] = val;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIINEN(index), val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(inen);
+static ssize_t outen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- spin_lock(&drvdata->spinlock);
- index = drvdata->config.ctiinout_sel;
- val = drvdata->config.ctiouten[index];
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "OUTEN%d %#lx\n", index, val);
+}
+static ssize_t outen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- index = config->ctiinout_sel;
- config->ctiouten[index] = val;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIOUTEN(index), val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(outen);
+static ssize_t gate_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_show(dev, buf, &drvdata->config.ctigate, -1);
+}
+static ssize_t gate_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_store(dev, buf, size,
&drvdata->config.ctigate, CTIGATE);
+} +static DEVICE_ATTR_RW(gate);
+static ssize_t asicctl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_show(dev, buf, &drvdata->config.asicctl, -1);
+}
+static ssize_t asicctl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_store(dev, buf, size,
&drvdata->config.asicctl, ASICCTL);
+} +static DEVICE_ATTR_RW(asicctl);
+static ssize_t intack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- cti_write_intack(dev, val);
- return size;
+} +static DEVICE_ATTR_WO(intack);
+static ssize_t appset_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_show(dev, buf, &drvdata->config.ctiappset, -1);
+}
+static ssize_t appset_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_store(dev, buf, size,
&drvdata->config.ctiappset, CTIAPPSET);
+} +static DEVICE_ATTR_RW(appset);
+static ssize_t appclear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val, mask;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- /* a 1'b1 in appclr clears down the same bit in appset*/
- mask = ~val;
- config->ctiappset &= mask;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPCLEAR, val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_WO(appclear);
+static ssize_t apppulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val, mask;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- /* a 1'b1 in apppulse sets then clears the bit,
* effectively clears down the same bit in appset
*/
- mask = ~val;
- config->ctiappset &= mask;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPPULSE, val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_WO(apppulse);
+static ssize_t itchout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return cti_reg32_show(dev, buf, NULL, ITCHOUT);
+}
+static ssize_t itchout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITCHOUT);
+} +static DEVICE_ATTR_RW(itchout);
+static ssize_t ittrigout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return cti_reg32_show(dev, buf, NULL, ITTRIGOUT);
+}
+static ssize_t ittrigout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITTRIGOUT);
+} +static DEVICE_ATTR_RW(ittrigout);
+static ssize_t itchinack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITCHINACK);
+} +static DEVICE_ATTR_WO(itchinack);
+static ssize_t ittriginack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITTRIGINACK);
+} +static DEVICE_ATTR_WO(ittriginack);
+static ssize_t itctrl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return cti_reg32_show(dev, buf, NULL, CORESIGHT_ITCTRL);
+}
+static ssize_t itctrl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, CORESIGHT_ITCTRL);
+} +static DEVICE_ATTR_RW(itctrl);
+coresight_cti_reg(triginstatus, CTITRIGINSTATUS); +coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS); +coresight_cti_reg(chinstatus, CTICHINSTATUS); +coresight_cti_reg(choutstatus, CTICHOUTSTATUS); +coresight_cti_reg(ittrigin, ITTRIGIN); +coresight_cti_reg(itchin, ITCHIN); +coresight_cti_reg(itchoutack, ITCHOUTACK); +coresight_cti_reg(ittrigoutack, ITTRIGOUTACK);
+static struct attribute *coresight_cti_regs_attrs[] = {
- &dev_attr_inout_sel.attr,
- &dev_attr_inen.attr,
- &dev_attr_outen.attr,
- &dev_attr_gate.attr,
- &dev_attr_asicctl.attr,
- &dev_attr_intack.attr,
- &dev_attr_appset.attr,
- &dev_attr_appclear.attr,
- &dev_attr_apppulse.attr,
- &dev_attr_triginstatus.attr,
- &dev_attr_trigoutstatus.attr,
- &dev_attr_chinstatus.attr,
- &dev_attr_choutstatus.attr,
- &dev_attr_itctrl.attr,
- &dev_attr_ittrigin.attr,
- &dev_attr_itchin.attr,
- &dev_attr_ittrigout.attr,
- &dev_attr_itchout.attr,
- &dev_attr_itchoutack.attr,
- &dev_attr_ittrigoutack.attr,
- &dev_attr_ittriginack.attr,
- &dev_attr_itchinack.attr,
- NULL,
+};
+/* channel / trigger api */ +static int +cti_trig_op_parse(struct device *dev, enum cti_chan_op op,
enum cti_trig_dir dir, const char *buf, size_t size)
+{
- u32 chan_idx;
- u32 trig_idx;
- int items, err = size;
- /* extract chan idx and trigger idx */
- items = sscanf(buf, "%d %d", &chan_idx, &trig_idx);
- if (items) {
err = cti_channel_trig_op(dev, op, dir, chan_idx, trig_idx);
if (!err)
err = size;
- } else
err = -EINVAL;
- return err;
+}
+static ssize_t trigin_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_attach);
+static ssize_t trigin_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_detach);
+static ssize_t trigout_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_attach);
+static ssize_t trigout_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_detach);
+static ssize_t gate_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = 0, channel = 0, items;
- if (strcmp(buf, "all") == 0) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE_ALL, 0);
- } else {
items = sscanf(buf, "%d", &channel);
if (!items)
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE, 0);
- }
- return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_enable);
+static ssize_t gate_disable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = 0, channel = 0, items;
- if (strcmp(buf, "all") == 0) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE_ALL, 0);
- } else {
items = sscanf(buf, "%d", &channel);
if (!items)
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE, 0);
- }
- return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_disable);
+static int +chan_op_parse(struct device *dev, enum cti_chan_set_op op, const char *buf) +{
- int err = 0, channel = 0, items;
- items = sscanf(buf, "%d", &channel);
- if (!items)
return -EINVAL;
- err = cti_channel_setop(dev, op, channel);
- return err;
+}
+static ssize_t chan_set_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = chan_op_parse(dev, CTI_CHAN_SET, buf);
- return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_set);
+static ssize_t chan_clear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = chan_op_parse(dev, CTI_CHAN_CLR, buf);
- return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_clear);
+static ssize_t chan_pulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = chan_op_parse(dev, CTI_CHAN_PULSE, buf);
- return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_pulse);
+static ssize_t trig_filter_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- spin_lock(&drvdata->spinlock);
- val = drvdata->config.trig_filter_enable;
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "%ld (%s)\n", val,
val ? "enabled" : "disabled");
+}
+static ssize_t trig_filter_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- drvdata->config.trig_filter_enable = val ? 1 : 0;
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(trig_filter_enable);
+/* clear all xtrigger / channel programming */ +static ssize_t reset_xtrigs_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int i;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- /* clear the CTI trigger / channel programming registers */
- for (i = 0; i < config->nr_trig_max; i++) {
config->ctiinen[i] = 0;
config->ctiouten[i] = 0;
- }
- /* clear the other regs */
- config->ctigate = (0x1 << config->nr_ctm_channels) - 1;
- config->asicctl = 0;
- config->ctiappset = 0;
- config->ctiinout_sel = 0;
- /* if enabled then write through */
- if (CTI_PWR_ENA(config))
cti_write_all_hw_regs(drvdata);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_WO(reset_xtrigs);
+/* list attachments by channel */ +#define LX_ADJUST_BUF_PTRS(n) \
- do { buf_pos += n; buf_size -= n; \
- if (buf_size <= 1) \
goto lx_buf_full; \
- } while (0)
+static ssize_t list_xtrigs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- int used = 0, nr_channels, chan_idx, reg_idx;
- /* buffer vars */
- int buf_size = PAGE_SIZE;
- char *buf_pos = buf;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *cfg = &drvdata->config;
- u32 chan_mask;
- nr_channels = cfg->nr_ctm_channels;
- used = scnprintf(buf_pos, buf_size, "CTI:%s; Channels:%d\n",
dev_name(dev), nr_channels);
- LX_ADJUST_BUF_PTRS(used);
- /* show the IN and OUT triggers per channel */
- for (chan_idx = 0; chan_idx < nr_channels; chan_idx++) {
chan_mask = 0x1 << chan_idx;
used = scnprintf(buf_pos, buf_size, "Chan %d: IN [ ", chan_idx);
LX_ADJUST_BUF_PTRS(used);
for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiinen[reg_idx]) {
used = scnprintf(buf_pos, buf_size, "%d ",
reg_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]; OUT [ ");
LX_ADJUST_BUF_PTRS(used);
for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiouten[reg_idx]) {
used = scnprintf(buf_pos, buf_size, "%d ",
reg_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "];\n");
LX_ADJUST_BUF_PTRS(used);
- }
- /* show the channels enabled via ctigate */
- used = scnprintf(buf_pos, buf_size, "Gate Channels Enabled: [ ");
- LX_ADJUST_BUF_PTRS(used);
- if (cfg->ctigate == 0) {
used = scnprintf(buf_pos, buf_size, "None ]\n");
LX_ADJUST_BUF_PTRS(used);
- } else {
for (chan_idx = 0; chan_idx < nr_channels; chan_idx++) {
chan_mask = 0x1 << chan_idx;
if (chan_mask & cfg->ctigate) {
used = scnprintf(buf_pos, buf_size, "%d ",
chan_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LX_ADJUST_BUF_PTRS(used);
- }
+lx_buf_full:
- used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
- return used;
+} +static DEVICE_ATTR_RO(list_xtrigs);
+#define LCIU_ADJUST_BUF_PTRS(n) \
- do { buf_pos += n; buf_size -= n; \
- if (buf_size <= 1) \
goto lciu_buf_full; \
- } while (0)
+static ssize_t list_chan_inuse_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- int used = 0, nr_channels, i;
- /* buffer vars */
- int buf_size = PAGE_SIZE;
- char *buf_pos = buf;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- u32 inuse_bits = 0, chan_mask, chan_bit_mask;
- nr_channels = config->nr_ctm_channels;
- spin_lock(&drvdata->spinlock);
- for (i = 0; i < config->nr_trig_max; i++) {
inuse_bits |= config->ctiinen[i];
inuse_bits |= config->ctiouten[i];
- }
- spin_unlock(&drvdata->spinlock);
- /* print in use channels */
- chan_mask = ((u32)(0x1 << nr_channels)) - 1;
- if (inuse_bits & chan_mask) {
used = scnprintf(buf_pos, buf_size, "Chan in use [ ");
LCIU_ADJUST_BUF_PTRS(used);
for (i = 0; i < nr_channels; i++) {
chan_bit_mask = 0x1 << i;
if (chan_bit_mask & inuse_bits) {
used = scnprintf(buf_pos, buf_size, "%d ", i);
LCIU_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LCIU_ADJUST_BUF_PTRS(used);
- } else {
used = scnprintf(buf_pos, buf_size, "Chan in use [ none ]\n");
LCIU_ADJUST_BUF_PTRS(used);
- }
- /* print free channels */
- inuse_bits = ~inuse_bits; /* flip for free channels */
- if (inuse_bits & chan_mask) {
used = scnprintf(buf_pos, buf_size, "Chan free [ ");
LCIU_ADJUST_BUF_PTRS(used);
for (i = 0; i < nr_channels; i++) {
chan_bit_mask = 0x1 << i;
if (chan_bit_mask & inuse_bits) {
used = scnprintf(buf_pos, buf_size, "%d ", i);
LCIU_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LCIU_ADJUST_BUF_PTRS(used);
- } else {
used = scnprintf(buf_pos, buf_size, "Chan free [ none ]\n");
LCIU_ADJUST_BUF_PTRS(used);
- }
+lciu_buf_full:
- used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
- return used;
+} +static DEVICE_ATTR_RO(list_chan_inuse);
+static struct attribute *coresight_cti_channel_attrs[] = {
- &dev_attr_trigin_attach.attr,
- &dev_attr_trigin_detach.attr,
- &dev_attr_trigout_attach.attr,
- &dev_attr_trigout_detach.attr,
- &dev_attr_gate_enable.attr,
- &dev_attr_gate_disable.attr,
- &dev_attr_chan_set.attr,
- &dev_attr_chan_clear.attr,
- &dev_attr_chan_pulse.attr,
- &dev_attr_trig_filter_enable.attr,
- &dev_attr_list_xtrigs.attr,
- &dev_attr_reset_xtrigs.attr,
- &dev_attr_list_chan_inuse.attr,
- NULL,
+};
+/* coresight management registers */ +coresight_cti_reg(devaff0, CTIDEVAFF0); +coresight_cti_reg(devaff1, CTIDEVAFF1); +coresight_cti_reg(authstatus, CORESIGHT_AUTHSTATUS); +coresight_cti_reg(devarch, CORESIGHT_DEVARCH); +coresight_cti_reg(devid, CORESIGHT_DEVID); +coresight_cti_reg(devtype, CORESIGHT_DEVTYPE); +coresight_cti_reg(pidr0, CORESIGHT_PERIPHIDR0); +coresight_cti_reg(pidr1, CORESIGHT_PERIPHIDR1); +coresight_cti_reg(pidr2, CORESIGHT_PERIPHIDR2); +coresight_cti_reg(pidr3, CORESIGHT_PERIPHIDR3); +coresight_cti_reg(pidr4, CORESIGHT_PERIPHIDR4);
+static struct attribute *coresight_cti_mgmt_attrs[] = {
- &dev_attr_devaff0.attr,
- &dev_attr_devaff1.attr,
- &dev_attr_authstatus.attr,
- &dev_attr_devarch.attr,
- &dev_attr_devid.attr,
- &dev_attr_devtype.attr,
- &dev_attr_pidr0.attr,
- &dev_attr_pidr1.attr,
- &dev_attr_pidr2.attr,
- &dev_attr_pidr3.attr,
- &dev_attr_pidr4.attr,
- NULL,
+};
+static const struct attribute_group coresight_cti_group = {
- .attrs = coresight_cti_attrs,
+};
+static const struct attribute_group coresight_cti_regs_group = {
- .attrs = coresight_cti_regs_attrs,
- .name = "regs",
+};
+static const struct attribute_group coresight_cti_channels_group = {
- .attrs = coresight_cti_channel_attrs,
- .name = "channels",
+};
+static const struct attribute_group coresight_cti_mgmt_group = {
- .attrs = coresight_cti_mgmt_attrs,
- .name = "mgmt",
+};
+const struct attribute_group *coresight_cti_groups[] = {
- &coresight_cti_group,
- &coresight_cti_regs_group,
- &coresight_cti_channels_group,
- &coresight_cti_mgmt_group,
- NULL,
+};
+static int cti_starting_cpu(unsigned int cpu) +{
- struct cti_drvdata *drvdata = cti_cpu_drv[cpu];
- if (!drvdata)
return 0;
- spin_lock(&drvdata->spinlock);
- drvdata->config.hw_powered = true;
- spin_unlock(&drvdata->spinlock);
- if (atomic_read(&drvdata->config.enable_req_count))
cti_enable_hw(drvdata);
- return 0;
+}
+static int cti_stopping_cpu(unsigned int cpu) +{
- struct cti_drvdata *drvdata = cti_cpu_drv[cpu];
- if (!drvdata)
return 0;
- cti_disable_hw(drvdata);
- spin_lock(&drvdata->spinlock);
- drvdata->config.hw_powered = false;
- spin_unlock(&drvdata->spinlock);
- return 0;
+}
+static void cti_smp_set_powered(void *info) +{
- struct cti_drvdata *drvdata = info;
- drvdata->config.hw_powered = true;
+}
+/* setup power handling on a cti that has an association with a cpu */ +static int cti_setup_hp_cpu_affinity(struct cti_drvdata *drvdata) +{
- int ret = 0;
- /* CPU affinity - add to quick lookup array
* and register cpu hp callbacks first time out
*/
- cpus_read_lock();
- cti_cpu_drv[drvdata->ctidev.cpu] = drvdata;
- if (!cti_cpu_count) {
cti_hp_online = cpuhp_setup_state_nocalls_cpuslocked(
CPUHP_AP_ONLINE_DYN, "arm/coresightcti:online",
cti_starting_cpu, cti_stopping_cpu);
if (cti_hp_online < 0) {
ret = cti_hp_online;
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
goto cti_hp_cpu_done;
}
- }
- cti_cpu_count++;
- /* setup powered flag by running function of assoc cpu */
- if (smp_call_function_single(drvdata->ctidev.cpu, cti_smp_set_powered,
drvdata, 1)) {
drvdata->config.hw_powered = false;
- }
+cti_hp_cpu_done:
- cpus_read_unlock();
- return ret;
+}
+/* back out hotplug notifications on error */ +static void cti_clear_hp_cpu_affinity(struct cti_drvdata *drvdata) +{
- if (!drvdata)
return;
- if (drvdata->ctidev.cpu >= 0) {
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
cti_cpu_count--;
if ((cti_cpu_count == 0) && (cti_hp_online > 0))
cpuhp_remove_state_nocalls(cti_hp_online);
- }
+}
+static int cti_probe(struct amba_device *adev, const struct amba_id *id) +{
- int ret = 0;
- u32 devid;
- void __iomem *base;
- struct device *dev = &adev->dev;
- struct cti_drvdata *drvdata = 0;
- struct coresight_desc cti_desc;
- struct coresight_platform_data *pdata = NULL;
- struct resource *res = &adev->res;
- struct device_node *np = adev->dev.of_node;
- struct ect_node *ect_nd = 0;
- /* boilerplate code to set up the basics */
- dev_info(dev, "%s(ID = %x)\n", __func__, id->id);
Please remove all debug code such as the above.
- if (np) {
pdata = of_get_coresight_ect_platform_data(dev, np);
if (IS_ERR(pdata)) {
dev_info(dev, "of_get_coresight_ect_platform err\n");
ret = PTR_ERR(pdata);
goto err_out;
}
dev->platform_data = pdata;
- }
- /* node to keep track of CTI net */
- ect_nd = kzalloc(sizeof(struct ect_node), GFP_KERNEL);
- if (!ect_nd) {
ret = -ENOMEM;
goto err_out;
- }
- /* driver data*/
- drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
- if (!drvdata) {
ret = -ENOMEM;
dev_info(dev, "%s, mem err\n", __func__);
goto err_out;
- }
- /* links between dev and drvdata*/
- drvdata->dev = dev;
- dev_set_drvdata(dev, drvdata);
- /* default CTI device info */
- drvdata->ctidev.cpu = -1;
- drvdata->ctidev.nr_trig_con = 0;
- drvdata->ctidev.ctm_id = 0;
- INIT_LIST_HEAD(&drvdata->ctidev.trig_cons);
- /* Validity for the resource is already checked by the AMBA core */
Start with an upper case or a lower case, the choice is yours but pick one and stick to it.
- base = devm_ioremap_resource(dev, res);
- if (IS_ERR(base)) {
ret = PTR_ERR(base);
dev_info(dev, "%s, remap err\n", __func__);
goto err_out;
- }
- drvdata->base = base;
- spin_lock_init(&drvdata->spinlock);
- /* look at the HW DEVID register for some of the HW settings */
- devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
- drvdata->config.nr_trig_max = (int)((devid & 0xFF00) >> 8);
- /* no current hardware should exceed this, but protect the driver
* in case of fault / out of spec hw
*/
There is an extra space on the second line and please adjust multi-line comment format.
- if (drvdata->config.nr_trig_max > CTIINOUTEN_MAX) {
dev_warn_once(dev,
"Limiting HW MaxTrig value(%d) to driver max(%d)\n",
drvdata->config.nr_trig_max, CTIINOUTEN_MAX);
drvdata->config.nr_trig_max = CTIINOUTEN_MAX;
- }
- drvdata->config.nr_ctm_channels = (int)((devid & 0xF0000) >> 16);
- dev_info(dev, "DevID %08x, trigs=%d, chans=%d\n", devid,
drvdata->config.nr_trig_max, drvdata->config.nr_ctm_channels);
- /** additional parse the .dts for connections and signals */
- of_cti_get_hw_data(dev, np, drvdata);
- cti_set_default_config(&drvdata->config);
- /* setup cpu related CTI devices, otherwise assume powered */
- if (drvdata->ctidev.cpu >= 0) {
ret = cti_setup_hp_cpu_affinity(drvdata);
if (ret < 0)
goto err_out;
- } else
drvdata->config.hw_powered = true;
- /* set up coresight component description */
- cti_desc.pdata = pdata;
- cti_desc.type = CORESIGHT_DEV_TYPE_ECT;
- cti_desc.subtype.ect_subtype = CORESIGHT_DEV_SUBTYPE_ECT_CTI;
- cti_desc.ops = &cti_ops;
- cti_desc.groups = coresight_cti_groups;
- cti_desc.dev = dev;
- drvdata->csdev = coresight_register(&cti_desc);
- if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
pr_err("%s: CS register failed\n", pdata->name);
goto err_out;
- }
- /* add to list of CTI devices */
- mutex_lock(&ect_mutex);
- ect_nd->cti_drv = drvdata;
- list_add(&ect_nd->next, &ect_net);
- mutex_unlock(&ect_mutex);
- /* set any cross references */
- cti_update_conn_xrefs(drvdata);
- /* all done - dec pm refcount */
- pm_runtime_put(&adev->dev);
- dev_info(dev, "%s: initialized\n", pdata->name);
- cti_count++;
- dev_info(dev, "%s - ok\n", __func__);
- return 0;
+err_out:
- cti_clear_hp_cpu_affinity(drvdata);
- dev_info(dev, "%s - err_out\n", __func__);
- return ret;
+}
+/* free up CTI specific resources
- called from coresight_device_release on coresight_unregister.
- */
+void cti_device_release(struct device *dev) +{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct ect_node *ect_item, *ect_tmp;
- struct cti_trig_con *tc, *tc_tmp;
- /* free up resources associated with the cti connections */
- if (drvdata->ctidev.cpu >= 0)
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
- list_for_each_entry_safe(tc, tc_tmp,
&drvdata->ctidev.trig_cons, node) {
kfree(tc->con_in);
kfree(tc->con_out);
kfree(tc->con_dev_name);
list_del(&tc->node);
kfree(tc);
- }
- /* remove from the list */
- mutex_lock(&ect_mutex);
- list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, next) {
if (ect_item->cti_drv == drvdata) {
list_del(&ect_item->next);
kfree(ect_item);
goto ect_list_item_removed;
}
- }
+ect_list_item_removed:
- mutex_unlock(&ect_mutex);
- kfree(drvdata);
+} +EXPORT_SYMBOL_GPL(cti_device_release);
+#define CTI_AMBA_ID(pid) \
- { \
.id = pid, \
.mask = 0x000fffff, \
- }
+static struct amba_cs_uci_id uci_id_cti[] = {
- {
/* CTI UCI data */
.devarch = 0x47701a14, /* devarch value for CTI v2 */
.devarch_mask = 0xfff0ffff,
.devtype = 0x00000014, /* maj(0x4-debug) min(0x1-ECT) */
- }
+};
+#define CTI_AMBA_UCI_ID(pid) \
- { \
.id = pid, \
.mask = 0x000fffff, \
.data = uci_id_cti, \
- }
We are doing the same kind of gymnastic with ETMv4. Please introduce a generic macro in coresight-priv.h and use it everywhere.
+static const struct amba_id cti_ids[] = {
- CTI_AMBA_ID(0x000bb906), /* Coresight CTI (SoC 400), C-A72, C-A57 */
- CTI_AMBA_ID(0x000bb922), /* CTI - C-A8 */
- CTI_AMBA_ID(0x000bb9a8), /* CTI - C-A53 */
- CTI_AMBA_ID(0x000bb9aa), /* CTI - C-A73 */
- CTI_AMBA_UCI_ID(0x000bb9da), /* CTI - C-A35 */
- CTI_AMBA_UCI_ID(0x000bb9ed), /* Coresight CTI (SoC 600) */
- CTI_AMBA_UCI_ID(0x000bb95d), /* test UCI - ETM PID A53 */
- { 0, 0},
+};
+static struct amba_driver cti_driver = {
- .drv = {
.name = "coresight-cti",
.owner = THIS_MODULE,
There is a tab missing between .owner and the '=' sign.
.suppress_bind_attrs = true,
- },
- .probe = cti_probe,
- .id_table = cti_ids,
+}; +builtin_amba_driver(cti_driver); diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h new file mode 100644 index 000000000000..09cb985ac0ea --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
One line too many.
+#ifndef _CORESIGHT_CORESIGHT_CTI_H +#define _CORESIGHT_CORESIGHT_CTI_H
+#include <asm/local.h> +#include <linux/spinlock.h> +#include "coresight-priv.h"
+/*
- Device registers
- 0x000 - 0x144: CTI programming and status
- 0xEDC - 0xEF8: CTI integration test.
- 0xF00 - 0xFFC: Coresight management registers.
- */
+/* CTI programming registers */ +#define CTICONTROL 0x000 +#define CTIINTACK 0x010 +#define CTIAPPSET 0x014 +#define CTIAPPCLEAR 0x018 +#define CTIAPPPULSE 0x01C +#define CTIINEN(n) (0x020 + (4 * n)) +#define CTIOUTEN(n) (0x0A0 + (4 * n)) +#define CTITRIGINSTATUS 0x130 +#define CTITRIGOUTSTATUS 0x134 +#define CTICHINSTATUS 0x138 +#define CTICHOUTSTATUS 0x13C +#define CTIGATE 0x140 +#define ASICCTL 0x144 +/* Integration test registers */ +#define ITCHINACK 0xEDC /* WO CTI CSSoc 400 only*/ +#define ITTRIGINACK 0xEE0 /* WO CTI CSSoc 400 only*/ +#define ITCHOUT 0xEE4 /* WO RW-600 */ +#define ITTRIGOUT 0xEE8 /* WO RW-600 */ +#define ITCHOUTACK 0xEEC /* RO CTI CSSoc 400 only*/ +#define ITTRIGOUTACK 0xEF0 /* RO CTI CSSoc 400 only*/ +#define ITCHIN 0xEF4 /* RO */ +#define ITTRIGIN 0xEF8 /* RO */ +/* management registers */ +#define CTIDEVAFF0 0xFA8 +#define CTIDEVAFF1 0xFAC
+/* CTI CSSoc 600 has a max of 32 trigger signals per direction.
- CTI CSSoc 400 has 8 IO triggers - other CTIs can be impl def.
- Max of in and out defined in the DEVID register.
- pick up actual number used from .dts parameters if present.
- */
+#define CTIINOUTEN_MAX 32
+/**
- Group of related trigger signals
- @nr_sigs: number of signals in the group.
- @used_mask: bitmask representing the signal indexes in the group.
- @sig_names: list of names for the signals.
- */
+struct cti_trig_grp {
- int nr_sigs;
- u32 used_mask;
- char *sig_names;
+};
+/**
- Trigger connection - connection between a CTI and other (coresight) device
- lists input and output trigger signals for the device
- @con_in: connected CTIIN signals for the device.
- @con_out: connected CTIOUT signals for the device.
- @con_dev: coresight device connected to the CTI, NULL if not CS device
- @con_dev_name: name of connected device (CS or CPU)
- @node: entry node in list of connections.
- */
+struct cti_trig_con {
- struct cti_trig_grp *con_in;
- struct cti_trig_grp *con_out;
- struct coresight_device *con_dev;
- char *con_dev_name;
- struct list_head node;
+};
+/**
- struct cti_device - description of CTI device properties.
- @nt_trig_con: Number of external devices connected to this device.
- @ctm_id: which CTM this device is connected to (by default it is
assumed there is a single CTM per SoC, ID 0).
- @trig_cons: list of connections to this device.
- @cpu: CPU ID if associated with CPU, -1 otherwise.
One line too many.
I'm out of time for today - I will start diving more in the code tomorrow.
Mathieu
- */
+struct cti_device {
- int nr_trig_con;
- u32 ctm_id;
- struct list_head trig_cons;
- int cpu;
+};
+/**
- struct cti_config - configuration of the CTI device hardware
- hardware description from RO ID regs
- @nr_trig_max: Max number of trigger signals implemented on device.
(max of trig_in or trig_out)
- @nr_ctm_channels: number of available CTM channels
- cti enable control
- @enable_req_count: CTI is enabled alongside >=1 associated devices.
- @hw_enabled: true if hw is currently enabled.
- @hw_powered: true if associated cpu powered on, or no cpu.
- registered triggers and filtering
- @trig_in_use: bitfield of in triggers registered as in use.
- @trig_out_use: bitfield of out triggers registered as in use.
- @trig_out_filter: bitfield of out triggers that are blocked if filter
enabled. Typically this would be dbgreq / restart on a core CTI.
- @trig_filter_enable: 1 if filtering enabled.
- cti software programmable regs:
- @ctiappset: CTI Software application channel set.
- @ctiinout_sel: register selector for INEN and OUTEN regs.
- @ctiinen: enable input trigger to a channel.
- @ctiouten: enable output trigger from a channel.
- @ctigate: gate channel output from CTI to CTM.
- */
+struct cti_config {
- /* hardware description */
- int nr_ctm_channels;
- int nr_trig_max;
- /* cti enable control */
- atomic_t enable_req_count;
- bool hw_enabled;
- bool hw_powered;
- /* registered triggers and filtering */
- u32 trig_in_use;
- u32 trig_out_use;
- u32 trig_out_filter;
- int trig_filter_enable;
- /* cti cross trig programmable regs */
- u32 ctiappset;
- u8 ctiinout_sel;
- u32 ctiinen[CTIINOUTEN_MAX];
- u32 ctiouten[CTIINOUTEN_MAX];
- u32 ctigate;
- u32 asicctl;
+};
+/**
- struct cti_drvdata - specifics for the CTI device
- @base: Memory mapped base address for this component.
- @dev: The device entity associated to this component.
- @csdev: Standard CoreSight device information.
- @ctidev: Extra information needed by the CTI/CTM framework.
- @spinlock: Control data access to one at a time.
- @config: Configuration data for this CTI device.
- */
+struct cti_drvdata {
- void __iomem *base;
- struct device *dev;
- struct coresight_device *csdev;
- struct cti_device ctidev;
- spinlock_t spinlock;
- struct cti_config config;
+};
+enum cti_chan_op {
- CTI_CHAN_ATTACH,
- CTI_CHAN_DETACH,
+};
+enum cti_trig_dir {
- CTI_TRIG_IN,
- CTI_TRIG_OUT,
+};
+enum cti_chan_gate_op {
- CTI_GATE_CHAN_ENABLE,
- CTI_GATE_CHAN_DISABLE,
- CTI_GATE_CHAN_ENABLE_ALL,
- CTI_GATE_CHAN_DISABLE_ALL,
+};
+enum cti_chan_set_op {
- CTI_CHAN_SET,
- CTI_CHAN_CLR,
- CTI_CHAN_PULSE,
+};
+#endif /* _CORESIGHT_CORESIGHT_CTI_H */
2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Hi,
Will fix formatting issues as requested & create macro.
Thanks
On Thu, 17 Jan 2019 at 22:14, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, Jan 09, 2019 at 10:54:38PM +0000, Mike Leach wrote:
Initial addition of the files for the Coresight CTI driver.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-cti.c | 2025 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 195 ++ 2 files changed, 2220 insertions(+) create mode 100644 drivers/hwtracing/coresight/coresight-cti.c create mode 100644 drivers/hwtracing/coresight/coresight-cti.h
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c new file mode 100644 index 000000000000..d06e19f111e2 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -0,0 +1,2025 @@ +/* SPDX-License-Identifier: GPL-2.0 */
https://elixir.bootlin.com/linux/latest/source/drivers/hwtracing/coresight/c...
+/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/smp.h> +#include <linux/sysfs.h> +#include <linux/stat.h> +#include <linux/clk.h> +#include <linux/cpu.h> +#include <linux/coresight.h> +#include <linux/coresight-pmu.h> +#include <linux/pm_wakeup.h> +#include <linux/amba/bus.h> +#include <linux/seq_file.h> +#include <linux/uaccess.h> +#include <linux/perf_event.h> +#include <linux/pm_runtime.h> +#include <linux/of.h> +#include <asm/sections.h> +#include <asm/local.h>
There is a new trend in kernel code now that requires to sort header file declarations in alphabetical order. I know it is annoying but you will be called on it.
OK
+#include "coresight-cti.h"
+#define csdev_to_cti_drvdata(csdev) \
dev_get_drvdata(csdev->dev.parent)
+/* CTI device tree trigger connection node keyword */ +#define CTI_DT_CONNS "trig-conns" +/* CTI device tree connection property keywords */ +#define CTI_DT_V8ARCH "arm,cti-v8-arch" +#define CTI_DT_TRIGIN_SIGS "arm,trig-in-sigs" +#define CTI_DT_TRIGOUT_SIGS "arm,trig-out-sigs" +#define CTI_DT_FILTER_OUT_SIGS "arm,trig-filters" +#define CTI_DT_CSDEV_ASSOC "arm,cs-dev-assoc" +#define CTI_DT_CTM_ID "arm,cti-ctm-id" +#define CTI_DT_CONN_NAME "arm,trig-conn-name"
+/**
- CTI devices can be associated with a PE, or be connected to CoreSight
- hardware. We have a list of all CTIs, and a subset array associated
- with individual PEs for pwr management as they will power up in the
- PE power domain.
- At this point we assume that the none CPU CTIs are always powered as
- we do with sinks etc.
- We leave the client to figure out if all the CTIs are interconnected with
- the same CTM, in general this is the case but does not always have to be.
- */
+struct ect_node {
struct cti_drvdata *cti_drv;
struct list_head next;
+};
+/* net of CTI devices connected via CTM */ +LIST_HEAD(ect_net);
+/* protect the list */ +static DEFINE_MUTEX(ect_mutex);
+/* quick reference for CPU CTIs */ +static struct cti_drvdata *cti_cpu_drv[NR_CPUS];
+/* number of registered CTI devices */ +static int cti_count;
+/* number of cpu associated CTI devices in use */ +static int cti_cpu_count;
+/* CTI / cpu hotplug notification ID */ +static enum cpuhp_state cti_hp_online;
+/* cpu powered and enabled */ +#define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered)
+/* write set of regs to hardware - call with spinlock claimed */ +static void cti_write_all_hw_regs(struct cti_drvdata *drvdata) +{
struct cti_config *config = &drvdata->config;
int i;
CS_UNLOCK(drvdata->base);
/* disable CTI before writing registers */
writel_relaxed(0, drvdata->base + CTICONTROL);
/* write the CTI trigger registers */
for (i = 0; i < config->nr_trig_max; i++) {
writel_relaxed(config->ctiinen[i], drvdata->base + CTIINEN(i));
writel_relaxed(config->ctiouten[i],
drvdata->base + CTIOUTEN(i));
}
/* other regs */
writel_relaxed(config->ctigate, drvdata->base + CTIGATE);
writel_relaxed(config->asicctl, drvdata->base + ASICCTL);
writel_relaxed(config->ctiappset, drvdata->base + CTIAPPSET);
/* re-enable CTI */
writel_relaxed(1, drvdata->base + CTICONTROL);
CS_LOCK(drvdata->base);
+}
+static void cti_enable_hw_smp_call(void *info) +{
struct cti_drvdata *drvdata = info;
cti_write_all_hw_regs(drvdata);
+}
+/* write regs to hardware and enable */ +static int cti_enable_hw(void *info) +{
struct cti_drvdata *drvdata = info;
struct cti_config *config = &drvdata->config;
int rc = 0;
pm_runtime_get_sync(drvdata->dev);
spin_lock(&drvdata->spinlock);
/* no need to do anything if enabled or unpowered*/
if (config->hw_enabled || !config->hw_powered) {
spin_unlock(&drvdata->spinlock);
pm_runtime_put(drvdata->dev);
return 0;
}
if (drvdata->ctidev.cpu >= 0) {
dev_info(drvdata->dev, "cti enable smp call for cpu %d\n",
drvdata->ctidev.cpu);
rc = smp_call_function_single(drvdata->ctidev.cpu,
cti_enable_hw_smp_call,
drvdata, 1);
} else {
dev_info(drvdata->dev, "cti enable not cpu call\n");
cti_write_all_hw_regs(drvdata);
}
if (!rc)
config->hw_enabled = true;
spin_unlock(&drvdata->spinlock);
return 0;
+}
+/* disable hardware */ +static int cti_disable_hw(void *info) +{
struct cti_drvdata *drvdata = info;
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
/* no need to do anything if disabled or cpu unpowered*/
if (!config->hw_enabled || !config->hw_powered) {
spin_unlock(&drvdata->spinlock);
return 0;
}
CS_UNLOCK(drvdata->base);
/* disable CTI */
writel_relaxed(0, drvdata->base + CTICONTROL);
config->hw_enabled = false;
CS_LOCK(drvdata->base);
spin_unlock(&drvdata->spinlock);
pm_runtime_put(drvdata->dev);
return 0;
+}
+static void +cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value) +{
CS_UNLOCK(drvdata->base);
writel_relaxed(value, drvdata->base+offset);
CS_LOCK(drvdata->base);
+}
+static void cti_set_default_config(struct cti_config *config) +{
/* Most regs default to 0 as zalloc'ed except...*/
config->trig_filter_enable = 1;
config->ctigate = ((u32)0x1 << config->nr_ctm_channels) - 1;
atomic_set(&config->enable_req_count, 0);
+}
+static void cti_write_intack(struct device *dev, u32 ackval) +{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
/* write if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIINTACK, ackval);
spin_unlock(&drvdata->spinlock);
+}
+/* need to update cross references once CTI csdev has been generated. */ +static void cti_update_conn_xrefs(struct cti_drvdata *drvdata) +{
struct cti_trig_con *tc;
struct cti_device *ctidev = &drvdata->ctidev;
list_for_each_entry(tc, &ctidev->trig_cons, node) {
if (tc->con_dev) {
tc->con_dev->ect_dev = drvdata->csdev;
dev_info(drvdata->dev,
"Setting assoc csdev (%px) with (%px)\n",
tc->con_dev, drvdata->csdev);
}
}
+}
+/* connection information */ +static int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name)
+{
struct cti_trig_con *tc;
struct cti_device *cti_dev = &drvdata->ctidev;
tc = kzalloc(sizeof(struct cti_trig_con), GFP_KERNEL);
if (tc == 0)
return -ENOMEM;
tc->con_dev = csdev;
/* prefer actual associated CS device dev name to supplied value -
* which is likely to be node name / other conn name
*/
if (csdev)
tc->con_dev_name = kstrdup(dev_name(&csdev->dev), GFP_KERNEL);
else if (assoc_dev_name != NULL)
tc->con_dev_name = kstrdup(assoc_dev_name, GFP_KERNEL);
tc->con_in = in_info;
tc->con_out = out_info;
list_add_tail(&tc->node, &cti_dev->trig_cons);
cti_dev->nr_trig_con++;
/* add connection usage bit info to overall info */
drvdata->config.trig_in_use |= in_info->used_mask;
drvdata->config.trig_out_use |= out_info->used_mask;
return 0;
+}
+/* add a default connection if nothing else is specified.
- single connection based on max in/out info, no assoc device
- */
+static int cti_add_default_connection(struct cti_drvdata *drvdata) +{
struct cti_trig_grp *in, *out;
int n_trigs = drvdata->config.nr_trig_max;
u32 n_trig_mask = (0x1 << n_trigs) - 1;
in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!in)
return -ENOMEM;
out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!out)
return -ENOMEM;
dev_info(drvdata->dev, "default connection: trigs %d, trig_mask %08x\n",
n_trigs, n_trig_mask);
/* assume max trigs for in and out, all used */
in->nr_sigs = n_trigs;
in->used_mask = n_trig_mask;
in->sig_names = NULL;
out->nr_sigs = n_trigs;
out->used_mask = n_trig_mask;
out->sig_names = NULL;
return cti_add_connection_entry(drvdata, in, out, NULL, "default");
+}
+/* create an architecturally defined v8 connection
- must hava a cpu, can have an ETM
- */
+static int of_cti_create_v8_connections(struct cti_drvdata *drvdata,
struct device_node *np)
+{
struct cti_device *cti_dev = &drvdata->ctidev;
struct cti_trig_grp *in, *out;
int cpuid = 0;
struct device_node *cs_np;
struct coresight_device *csdev = NULL;
char cpu_name_str[16];
const char *assoc_name = NULL;
cpuid = of_coresight_ect_get_cpu(np);
if (cpuid < 0) {
dev_warn(drvdata->dev, "CTI v8 DT binding no cpu\n");
return -EINVAL;
}
cti_dev->cpu = cpuid;
/* do the v8 cpu connection */
in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!in)
return -ENOMEM;
out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!out)
return -ENOMEM;
/* v8 PE CTI config... */
in->nr_sigs = 3;
in->used_mask = 0x7;
in->sig_names = NULL;
out->nr_sigs = 3;
out->used_mask = 0x7;
out->sig_names = NULL;
scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
cti_add_connection_entry(drvdata, in, out, NULL, cpu_name_str);
drvdata->config.trig_out_filter = 0x1; /* filter dbgreq */
/* v8 ETM associated config... */
cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
if (cs_np) {
in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!in)
return -ENOMEM;
out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!out)
return -ENOMEM;
in->nr_sigs = 4;
in->used_mask = 0xF0;
in->sig_names = NULL;
out->nr_sigs = 4;
out->used_mask = 0xF0;
out->sig_names = NULL;
dev_info(drvdata->dev, "v8 finding assoc dev %s\n",
cs_np->full_name);
csdev = of_coresight_get_cs_device_by_node(cs_np);
if (csdev)
assoc_name = dev_name(&csdev->dev);
else
assoc_name = cs_np->full_name;
cti_add_connection_entry(drvdata, in, out, csdev, assoc_name);
of_node_put(cs_np);
}
return 0;
+}
+static int of_cti_read_trig_group(struct cti_trig_grp **trig_grp_ptr,
struct device_node *np,
const char *grp_name,
int max_trigs)
+{
int items, err = 0;
u32 value, pidx;
struct cti_trig_grp *grp;
grp = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!grp)
return -ENOMEM;
*trig_grp_ptr = grp;
items = of_property_count_elems_of_size(np, grp_name, 4);
/* if the property doesn't exist or has no values, then return
* an empty connection group
*/
if (items < 0)
return 0;
if (items > max_trigs)
return -EINVAL;
/* set the number of signals and usage mask */
for (pidx = 0; pidx < items; pidx++) {
err = of_property_read_u32_index(np, grp_name, pidx, &value);
if (err)
return err;
grp->nr_sigs++;
grp->used_mask |= (0x1 << value);
}
return 0;
+}
+static int of_cti_create_connection(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
struct cti_trig_grp *in = 0, *out = 0, *filter = 0;
int cpuid = -1, err = 0, trig_max = drvdata->config.nr_trig_max;
struct device_node *cs_np;
struct coresight_device *csdev = NULL;
const char *assoc_name = "unknown";
char cpu_name_str[16];
/* look for the signals properties. */
err = of_cti_read_trig_group(&in, np, CTI_DT_TRIGIN_SIGS, trig_max);
if (err)
return err;
err = of_cti_read_trig_group(&out, np, CTI_DT_TRIGOUT_SIGS, trig_max);
if (err)
return err;
err = of_cti_read_trig_group(&filter, np, CTI_DT_FILTER_OUT_SIGS,
trig_max);
if (err)
return err;
/* read the connection name if set - may be overridden by later */
of_property_read_string(np, CTI_DT_CONN_NAME, &assoc_name);
/* associated cpu ? */
cpuid = of_coresight_ect_get_cpu(np);
drvdata->ctidev.cpu = cpuid;
if (cpuid >= 0) {
scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
assoc_name = cpu_name_str;
} else {
/* associated device ? */
cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
if (cs_np) {
dev_info(dev, "finding assoc dev %s\n",
cs_np->full_name);
csdev = of_coresight_get_cs_device_by_node(cs_np);
if (csdev) /* use device name if csdev found */
assoc_name = dev_name(&csdev->dev);
else /* otherwise node name for later association */
assoc_name = cs_np->full_name;
of_node_put(cs_np);
}
}
/* set up a connection */
err = cti_add_connection_entry(drvdata, in, out, csdev, assoc_name);
/* note any filter info */
if (!err)
drvdata->config.trig_out_filter |= filter->used_mask;
kfree(filter);
return err;
+}
+/* get the hardware configuration & connection data. */ +static int of_cti_get_hw_data(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
int rc = 0;
struct cti_device *cti_dev = &drvdata->ctidev;
struct device_node *nc = NULL;
/* get any CTM ID - defaults to 0 */
of_property_read_u32(np, CTI_DT_CTM_ID, &cti_dev->ctm_id);
if (of_property_read_bool(np, CTI_DT_V8ARCH)) {
rc = of_cti_create_v8_connections(drvdata, np);
} else {
for_each_child_of_node(np, nc) {
if (of_node_cmp(nc->name, CTI_DT_CONNS) != 0)
continue;
dev_info(dev, "dt found node: %s %s\n",
nc->name, nc->full_name);
rc = of_cti_create_connection(dev, nc, drvdata);
if (rc != 0)
return rc;
}
}
/* if no connections, just add a single default based on max IN-OUT */
if (cti_dev->nr_trig_con == 0)
rc = cti_add_default_connection(drvdata);
return rc;
+}
+static int +cti_match_con_name(struct cti_device *ctidev, const char *node_name,
const char *csdev_name)
+{
int found = 0;
struct cti_trig_con *trig_con;
list_for_each_entry(trig_con, &ctidev->trig_cons, node) {
if (trig_con->con_dev_name) {
if (!strcmp(node_name, trig_con->con_dev_name)) {
found = 1;
/* match: so swap in csdev name */
kfree(trig_con->con_dev_name);
trig_con->con_dev_name =
kstrdup(csdev_name, GFP_KERNEL);
goto cti_con_name_match;
}
}
}
+cti_con_name_match:
return found;
+}
+/* search the cti list to add an associated CTI into the supplied CS device
- This will set the association if CTI declared before the CS device
- */
+int cti_add_assoc_to_csdev(struct coresight_device *csdev) +{
struct ect_node *ect_item;
struct cti_device *ctidev;
const char *node_name = NULL, *csdev_name;
/* exit early for no CTIs or self referencing ECT devices.*/
if (!cti_count) {
dev_info(&csdev->dev, "no CTI to check\n");
return 0;
}
if (!csdev->dev.parent->of_node) {
dev_info(&csdev->dev, "No parent of_node pointer\n");
return 0;
}
node_name = csdev->dev.parent->of_node->full_name;
if (!node_name) {
dev_info(&csdev->dev, "%s - bad node name\n", __func__);
return 0;
}
dev_info(&csdev->dev, "%s - looking for %s\n", __func__, node_name);
if (!cti_count || (csdev->type == CORESIGHT_DEV_TYPE_ECT)) {
dev_info(&csdev->dev, "no CTI or skip self ref CTI\n");
return 0;
}
csdev_name = dev_name(&csdev->dev);
/* for each CTI in list... */
mutex_lock(&ect_mutex);
list_for_each_entry(ect_item, &ect_net, next) {
ctidev = &ect_item->cti_drv->ctidev;
if (cti_match_con_name(ctidev, node_name, csdev_name)) {
csdev->ect_dev = ect_item->cti_drv->csdev;
dev_info(&csdev->dev, "Found!\n");
goto cti_found;
}
}
+cti_found:
mutex_unlock(&ect_mutex);
return 0;
+} +EXPORT_SYMBOL_GPL(cti_add_assoc_to_csdev);
+/* attach/detach channel from trigger - write through if enabled. */ +static int cti_channel_trig_op(struct device *dev,
enum cti_chan_op op,
enum cti_trig_dir direction,
u32 channel_idx,
u32 trigger_idx)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
u32 trig_bitmask;
u32 chan_bitmask;
u32 reg_value;
int reg_offset;
dev_info(dev, "chan_trig_op: op %d, dir %d, chan %d, trig %d\n",
op, direction, channel_idx, trigger_idx);
/* ensure indexes in range */
if ((channel_idx >= config->nr_ctm_channels) ||
(trigger_idx >= config->nr_trig_max))
return -EINVAL;
trig_bitmask = 0x1 << trigger_idx;
/* ensure registered triggers and not out filtered */
if (direction == CTI_TRIG_IN) {
if (!(trig_bitmask & config->trig_in_use))
return -EINVAL;
} else {
if (!(trig_bitmask & config->trig_out_use))
return -EINVAL;
if ((config->trig_filter_enable) &&
(config->trig_out_filter & trig_bitmask))
return -EINVAL;
}
/* update the local register values */
chan_bitmask = 0x1 << channel_idx;
reg_offset = (direction == CTI_TRIG_IN ? CTIINEN(trigger_idx) :
CTIOUTEN(trigger_idx));
spin_lock(&drvdata->spinlock);
/* read - modify write - the trigger / channel enable value */
reg_value = (direction == CTI_TRIG_IN ? config->ctiinen[trigger_idx] :
config->ctiouten[trigger_idx]);
reg_value = (op == CTI_CHAN_ATTACH) ? reg_value | chan_bitmask :
reg_value & (~chan_bitmask);
/* write local copy */
if (direction == CTI_TRIG_IN)
config->ctiinen[trigger_idx] = reg_value;
else
config->ctiouten[trigger_idx] = reg_value;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
spin_unlock(&drvdata->spinlock);
return 0;
+}
+static int cti_channel_gate_op(struct device *dev,
enum cti_chan_gate_op op,
u32 channel_idx)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
u32 chan_bitmask;
u32 reg_value;
int err = 0;
if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
chan_bitmask = 0x1 << channel_idx;
spin_lock(&drvdata->spinlock);
reg_value = config->ctigate;
switch (op) {
case CTI_GATE_CHAN_ENABLE:
reg_value |= chan_bitmask;
break;
case CTI_GATE_CHAN_DISABLE:
reg_value &= ~chan_bitmask;
break;
case CTI_GATE_CHAN_ENABLE_ALL:
reg_value = (0x1 << config->nr_ctm_channels) - 1;
break;
case CTI_GATE_CHAN_DISABLE_ALL:
reg_value = 0x0;
break;
default:
err = -EINVAL;
break;
}
if (err == 0) {
config->ctigate = reg_value;
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIGATE, reg_value);
}
spin_unlock(&drvdata->spinlock);
return err;
+}
+static int cti_channel_setop(struct device *dev,
enum cti_chan_set_op op,
u32 channel_idx)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
u32 chan_bitmask;
u32 reg_value;
u32 reg_offset;
int err = 0;
if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
chan_bitmask = 0x1 << channel_idx;
spin_lock(&drvdata->spinlock);
reg_value = config->ctiappset;
switch (op) {
case CTI_CHAN_SET:
config->ctiappset |= chan_bitmask;
reg_value = config->ctiappset;
reg_offset = CTIAPPSET;
break;
case CTI_CHAN_CLR:
config->ctiappset &= ~chan_bitmask;
reg_value = chan_bitmask;
reg_offset = CTIAPPCLEAR;
break;
case CTI_CHAN_PULSE:
config->ctiappset &= ~chan_bitmask;
reg_value = chan_bitmask;
reg_offset = CTIAPPPULSE;
break;
default:
err = -EINVAL;
break;
}
if ((err == 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
spin_unlock(&drvdata->spinlock);
return err;
+}
+/** cti ect operations **/ +static int cti_enable(struct coresight_device *csdev, void *__unused) +{
int rc;
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
atomic_inc(&drvdata->config.enable_req_count);
rc = cti_enable_hw(drvdata);
if (rc)
atomic_dec(&drvdata->config.enable_req_count);
return rc;
+}
+static int cti_disable(struct coresight_device *csdev, void *__unused) +{
int rc = 0;
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
if (!atomic_dec_return(&drvdata->config.enable_req_count)) {
rc = cti_disable_hw(drvdata);
if (rc)
atomic_inc(&drvdata->config.enable_req_count);
}
return rc;
+}
+const struct coresight_ops_ect cti_ops_ect = {
.enable = cti_enable,
.disable = cti_disable,
+};
+const struct coresight_ops cti_ops = {
.ect_ops = &cti_ops_ect,
+};
+/** attributes **/
+#define coresight_cti_reg(name, offset) \
coresight_simple_reg32(struct cti_drvdata, name, offset)
+/* show a simple 32 bit value. if pval is NULL then live read,
- otherwise read from supplied pointer only
- */
+static ssize_t cti_reg32_show(struct device *dev, char *buf,
u32 *pval, int reg_offset)
+{
unsigned long val = 0;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
if (pval) {
val = (unsigned long)*pval;
} else if ((reg_offset >= 0) && CTI_PWR_ENA(config)) {
CS_UNLOCK(drvdata->base);
val = readl_relaxed(drvdata->base + reg_offset);
CS_LOCK(drvdata->base);
}
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+/* store a simple 32 bit value.
- if pval not NULL, then copy to here too,
- if reg_offset >= 0 then write through if enabled.
- */
+static ssize_t cti_reg32_store(struct device *dev, const char *buf,
size_t size, u32 *pval,
int reg_offset)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
/* local store */
if (pval)
*pval = (u32)val;
/* write through of offset and enabled */
if ((reg_offset >= 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, val);
spin_unlock(&drvdata->spinlock);
return size;
+}
+/* basic attributes */ +static ssize_t enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
int enable_req;
bool enabled, powered, cpuid;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
ssize_t size = 0;
enable_req = atomic_read(&drvdata->config.enable_req_count);
spin_lock(&drvdata->spinlock);
powered = drvdata->config.hw_powered;
enabled = drvdata->config.hw_enabled;
cpuid = drvdata->ctidev.cpu;
spin_unlock(&drvdata->spinlock);
if (powered) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled", cpuid);
} else if (cpuid >= 0) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req", cpuid);
} else {
size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n",
enabled ? "enabled" : "disabled");
}
return size;
+}
+static ssize_t enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int ret = 0;
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
ret = kstrtoul(buf, 16, &val);
if (ret)
return ret;
if (val)
ret = cti_enable(drvdata->csdev, NULL);
else
ret = cti_disable(drvdata->csdev, NULL);
if (ret)
return ret;
return size;
+} +static DEVICE_ATTR_RW(enable);
+static ssize_t ctmid_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return scnprintf(buf, PAGE_SIZE, "0x%x\n", drvdata->ctidev.ctm_id);
+} +static DEVICE_ATTR_RO(ctmid);
+#define LF_ADJUST_BUF_PTRS(n) \
do { buf_pos += n; buf_size -= n; \
if (buf_size <= 1) \
goto lf_buf_full; \
} while (0)
+static ssize_t list_features_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
int used = 0, sig_idx, con_idx, nr_trig_max;
/* buffer vars */
int buf_size = PAGE_SIZE;
char *buf_pos = buf;
char *con_name;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *cfg = &drvdata->config;
struct cti_device *ctidev = &drvdata->ctidev;
u32 sig_mask;
struct cti_trig_con *con;
/* basic feature info */
used = scnprintf(buf_pos, buf_size,
"CTI:%s; Channels:%d; Max Trigs:%d\n",
dev_name(dev), cfg->nr_ctm_channels, cfg->nr_trig_max);
LF_ADJUST_BUF_PTRS(used);
used = scnprintf(buf_pos, buf_size, "CTM id = %d; Num Conns = %d\n",
ctidev->ctm_id, ctidev->nr_trig_con);
LF_ADJUST_BUF_PTRS(used);
/* list each connection info */
con_idx = 0;
nr_trig_max = cfg->nr_trig_max;
list_for_each_entry(con, &ctidev->trig_cons, node) {
/* connection name */
if (con->con_dev_name != NULL)
con_name = con->con_dev_name;
else
con_name = "unknown";
used = scnprintf(buf_pos, buf_size, "conn(%d:%s)\n",
con_idx, con_name);
LF_ADJUST_BUF_PTRS(used);
con_idx++;
/* connection signals */
used = scnprintf(buf_pos, buf_size, "Trig IN(%d) [ ",
con->con_in->nr_sigs);
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & con->con_in->used_mask) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
used = scnprintf(buf_pos, buf_size, "Trig OUT(%d) [ ",
con->con_out->nr_sigs);
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & con->con_out->used_mask) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
}
if (cfg->trig_out_filter) {
used = scnprintf(buf_pos, buf_size, "Trig OUT_FILTERED [ ");
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & cfg->trig_out_filter) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
}
+lf_buf_full:
used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
return used;
+} +static DEVICE_ATTR_RO(list_features);
+static struct attribute *coresight_cti_attrs[] = {
&dev_attr_enable.attr,
&dev_attr_list_features.attr,
&dev_attr_ctmid.attr,
NULL,
+};
+/* raw register attributes */ +static ssize_t inout_sel_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
u32 val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
val = (u32)drvdata->config.ctiinout_sel;
return scnprintf(buf, PAGE_SIZE, "%#x\n", val);
+}
+static ssize_t inout_sel_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
if (kstrtoul(buf, 16, &val))
return -EINVAL;
if (val > (CTIINOUTEN_MAX-1))
return -EINVAL;
spin_lock(&drvdata->spinlock);
drvdata->config.ctiinout_sel = val;
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(inout_sel);
+static ssize_t inen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
spin_lock(&drvdata->spinlock);
index = drvdata->config.ctiinout_sel;
val = drvdata->config.ctiinen[index];
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "INEN%d %#lx\n", index, val);
+}
+static ssize_t inen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
index = config->ctiinout_sel;
config->ctiinen[index] = val;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIINEN(index), val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(inen);
+static ssize_t outen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
spin_lock(&drvdata->spinlock);
index = drvdata->config.ctiinout_sel;
val = drvdata->config.ctiouten[index];
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "OUTEN%d %#lx\n", index, val);
+}
+static ssize_t outen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
index = config->ctiinout_sel;
config->ctiouten[index] = val;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIOUTEN(index), val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(outen);
+static ssize_t gate_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_show(dev, buf, &drvdata->config.ctigate, -1);
+}
+static ssize_t gate_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_store(dev, buf, size,
&drvdata->config.ctigate, CTIGATE);
+} +static DEVICE_ATTR_RW(gate);
+static ssize_t asicctl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_show(dev, buf, &drvdata->config.asicctl, -1);
+}
+static ssize_t asicctl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_store(dev, buf, size,
&drvdata->config.asicctl, ASICCTL);
+} +static DEVICE_ATTR_RW(asicctl);
+static ssize_t intack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
cti_write_intack(dev, val);
return size;
+} +static DEVICE_ATTR_WO(intack);
+static ssize_t appset_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_show(dev, buf, &drvdata->config.ctiappset, -1);
+}
+static ssize_t appset_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_store(dev, buf, size,
&drvdata->config.ctiappset, CTIAPPSET);
+} +static DEVICE_ATTR_RW(appset);
+static ssize_t appclear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val, mask;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
/* a 1'b1 in appclr clears down the same bit in appset*/
mask = ~val;
config->ctiappset &= mask;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPCLEAR, val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_WO(appclear);
+static ssize_t apppulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val, mask;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
/* a 1'b1 in apppulse sets then clears the bit,
* effectively clears down the same bit in appset
*/
mask = ~val;
config->ctiappset &= mask;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPPULSE, val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_WO(apppulse);
+static ssize_t itchout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return cti_reg32_show(dev, buf, NULL, ITCHOUT);
+}
+static ssize_t itchout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITCHOUT);
+} +static DEVICE_ATTR_RW(itchout);
+static ssize_t ittrigout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return cti_reg32_show(dev, buf, NULL, ITTRIGOUT);
+}
+static ssize_t ittrigout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITTRIGOUT);
+} +static DEVICE_ATTR_RW(ittrigout);
+static ssize_t itchinack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITCHINACK);
+} +static DEVICE_ATTR_WO(itchinack);
+static ssize_t ittriginack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITTRIGINACK);
+} +static DEVICE_ATTR_WO(ittriginack);
+static ssize_t itctrl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return cti_reg32_show(dev, buf, NULL, CORESIGHT_ITCTRL);
+}
+static ssize_t itctrl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, CORESIGHT_ITCTRL);
+} +static DEVICE_ATTR_RW(itctrl);
+coresight_cti_reg(triginstatus, CTITRIGINSTATUS); +coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS); +coresight_cti_reg(chinstatus, CTICHINSTATUS); +coresight_cti_reg(choutstatus, CTICHOUTSTATUS); +coresight_cti_reg(ittrigin, ITTRIGIN); +coresight_cti_reg(itchin, ITCHIN); +coresight_cti_reg(itchoutack, ITCHOUTACK); +coresight_cti_reg(ittrigoutack, ITTRIGOUTACK);
+static struct attribute *coresight_cti_regs_attrs[] = {
&dev_attr_inout_sel.attr,
&dev_attr_inen.attr,
&dev_attr_outen.attr,
&dev_attr_gate.attr,
&dev_attr_asicctl.attr,
&dev_attr_intack.attr,
&dev_attr_appset.attr,
&dev_attr_appclear.attr,
&dev_attr_apppulse.attr,
&dev_attr_triginstatus.attr,
&dev_attr_trigoutstatus.attr,
&dev_attr_chinstatus.attr,
&dev_attr_choutstatus.attr,
&dev_attr_itctrl.attr,
&dev_attr_ittrigin.attr,
&dev_attr_itchin.attr,
&dev_attr_ittrigout.attr,
&dev_attr_itchout.attr,
&dev_attr_itchoutack.attr,
&dev_attr_ittrigoutack.attr,
&dev_attr_ittriginack.attr,
&dev_attr_itchinack.attr,
NULL,
+};
+/* channel / trigger api */ +static int +cti_trig_op_parse(struct device *dev, enum cti_chan_op op,
enum cti_trig_dir dir, const char *buf, size_t size)
+{
u32 chan_idx;
u32 trig_idx;
int items, err = size;
/* extract chan idx and trigger idx */
items = sscanf(buf, "%d %d", &chan_idx, &trig_idx);
if (items) {
err = cti_channel_trig_op(dev, op, dir, chan_idx, trig_idx);
if (!err)
err = size;
} else
err = -EINVAL;
return err;
+}
+static ssize_t trigin_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_attach);
+static ssize_t trigin_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_detach);
+static ssize_t trigout_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_attach);
+static ssize_t trigout_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_detach);
+static ssize_t gate_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = 0, channel = 0, items;
if (strcmp(buf, "all") == 0) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE_ALL, 0);
} else {
items = sscanf(buf, "%d", &channel);
if (!items)
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE, 0);
}
return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_enable);
+static ssize_t gate_disable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = 0, channel = 0, items;
if (strcmp(buf, "all") == 0) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE_ALL, 0);
} else {
items = sscanf(buf, "%d", &channel);
if (!items)
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE, 0);
}
return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_disable);
+static int +chan_op_parse(struct device *dev, enum cti_chan_set_op op, const char *buf) +{
int err = 0, channel = 0, items;
items = sscanf(buf, "%d", &channel);
if (!items)
return -EINVAL;
err = cti_channel_setop(dev, op, channel);
return err;
+}
+static ssize_t chan_set_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = chan_op_parse(dev, CTI_CHAN_SET, buf);
return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_set);
+static ssize_t chan_clear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = chan_op_parse(dev, CTI_CHAN_CLR, buf);
return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_clear);
+static ssize_t chan_pulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = chan_op_parse(dev, CTI_CHAN_PULSE, buf);
return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_pulse);
+static ssize_t trig_filter_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
spin_lock(&drvdata->spinlock);
val = drvdata->config.trig_filter_enable;
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "%ld (%s)\n", val,
val ? "enabled" : "disabled");
+}
+static ssize_t trig_filter_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
drvdata->config.trig_filter_enable = val ? 1 : 0;
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(trig_filter_enable);
+/* clear all xtrigger / channel programming */ +static ssize_t reset_xtrigs_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int i;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
/* clear the CTI trigger / channel programming registers */
for (i = 0; i < config->nr_trig_max; i++) {
config->ctiinen[i] = 0;
config->ctiouten[i] = 0;
}
/* clear the other regs */
config->ctigate = (0x1 << config->nr_ctm_channels) - 1;
config->asicctl = 0;
config->ctiappset = 0;
config->ctiinout_sel = 0;
/* if enabled then write through */
if (CTI_PWR_ENA(config))
cti_write_all_hw_regs(drvdata);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_WO(reset_xtrigs);
+/* list attachments by channel */ +#define LX_ADJUST_BUF_PTRS(n) \
do { buf_pos += n; buf_size -= n; \
if (buf_size <= 1) \
goto lx_buf_full; \
} while (0)
+static ssize_t list_xtrigs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
int used = 0, nr_channels, chan_idx, reg_idx;
/* buffer vars */
int buf_size = PAGE_SIZE;
char *buf_pos = buf;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *cfg = &drvdata->config;
u32 chan_mask;
nr_channels = cfg->nr_ctm_channels;
used = scnprintf(buf_pos, buf_size, "CTI:%s; Channels:%d\n",
dev_name(dev), nr_channels);
LX_ADJUST_BUF_PTRS(used);
/* show the IN and OUT triggers per channel */
for (chan_idx = 0; chan_idx < nr_channels; chan_idx++) {
chan_mask = 0x1 << chan_idx;
used = scnprintf(buf_pos, buf_size, "Chan %d: IN [ ", chan_idx);
LX_ADJUST_BUF_PTRS(used);
for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiinen[reg_idx]) {
used = scnprintf(buf_pos, buf_size, "%d ",
reg_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]; OUT [ ");
LX_ADJUST_BUF_PTRS(used);
for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiouten[reg_idx]) {
used = scnprintf(buf_pos, buf_size, "%d ",
reg_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "];\n");
LX_ADJUST_BUF_PTRS(used);
}
/* show the channels enabled via ctigate */
used = scnprintf(buf_pos, buf_size, "Gate Channels Enabled: [ ");
LX_ADJUST_BUF_PTRS(used);
if (cfg->ctigate == 0) {
used = scnprintf(buf_pos, buf_size, "None ]\n");
LX_ADJUST_BUF_PTRS(used);
} else {
for (chan_idx = 0; chan_idx < nr_channels; chan_idx++) {
chan_mask = 0x1 << chan_idx;
if (chan_mask & cfg->ctigate) {
used = scnprintf(buf_pos, buf_size, "%d ",
chan_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LX_ADJUST_BUF_PTRS(used);
}
+lx_buf_full:
used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
return used;
+} +static DEVICE_ATTR_RO(list_xtrigs);
+#define LCIU_ADJUST_BUF_PTRS(n) \
do { buf_pos += n; buf_size -= n; \
if (buf_size <= 1) \
goto lciu_buf_full; \
} while (0)
+static ssize_t list_chan_inuse_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
int used = 0, nr_channels, i;
/* buffer vars */
int buf_size = PAGE_SIZE;
char *buf_pos = buf;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
u32 inuse_bits = 0, chan_mask, chan_bit_mask;
nr_channels = config->nr_ctm_channels;
spin_lock(&drvdata->spinlock);
for (i = 0; i < config->nr_trig_max; i++) {
inuse_bits |= config->ctiinen[i];
inuse_bits |= config->ctiouten[i];
}
spin_unlock(&drvdata->spinlock);
/* print in use channels */
chan_mask = ((u32)(0x1 << nr_channels)) - 1;
if (inuse_bits & chan_mask) {
used = scnprintf(buf_pos, buf_size, "Chan in use [ ");
LCIU_ADJUST_BUF_PTRS(used);
for (i = 0; i < nr_channels; i++) {
chan_bit_mask = 0x1 << i;
if (chan_bit_mask & inuse_bits) {
used = scnprintf(buf_pos, buf_size, "%d ", i);
LCIU_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LCIU_ADJUST_BUF_PTRS(used);
} else {
used = scnprintf(buf_pos, buf_size, "Chan in use [ none ]\n");
LCIU_ADJUST_BUF_PTRS(used);
}
/* print free channels */
inuse_bits = ~inuse_bits; /* flip for free channels */
if (inuse_bits & chan_mask) {
used = scnprintf(buf_pos, buf_size, "Chan free [ ");
LCIU_ADJUST_BUF_PTRS(used);
for (i = 0; i < nr_channels; i++) {
chan_bit_mask = 0x1 << i;
if (chan_bit_mask & inuse_bits) {
used = scnprintf(buf_pos, buf_size, "%d ", i);
LCIU_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LCIU_ADJUST_BUF_PTRS(used);
} else {
used = scnprintf(buf_pos, buf_size, "Chan free [ none ]\n");
LCIU_ADJUST_BUF_PTRS(used);
}
+lciu_buf_full:
used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
return used;
+} +static DEVICE_ATTR_RO(list_chan_inuse);
+static struct attribute *coresight_cti_channel_attrs[] = {
&dev_attr_trigin_attach.attr,
&dev_attr_trigin_detach.attr,
&dev_attr_trigout_attach.attr,
&dev_attr_trigout_detach.attr,
&dev_attr_gate_enable.attr,
&dev_attr_gate_disable.attr,
&dev_attr_chan_set.attr,
&dev_attr_chan_clear.attr,
&dev_attr_chan_pulse.attr,
&dev_attr_trig_filter_enable.attr,
&dev_attr_list_xtrigs.attr,
&dev_attr_reset_xtrigs.attr,
&dev_attr_list_chan_inuse.attr,
NULL,
+};
+/* coresight management registers */ +coresight_cti_reg(devaff0, CTIDEVAFF0); +coresight_cti_reg(devaff1, CTIDEVAFF1); +coresight_cti_reg(authstatus, CORESIGHT_AUTHSTATUS); +coresight_cti_reg(devarch, CORESIGHT_DEVARCH); +coresight_cti_reg(devid, CORESIGHT_DEVID); +coresight_cti_reg(devtype, CORESIGHT_DEVTYPE); +coresight_cti_reg(pidr0, CORESIGHT_PERIPHIDR0); +coresight_cti_reg(pidr1, CORESIGHT_PERIPHIDR1); +coresight_cti_reg(pidr2, CORESIGHT_PERIPHIDR2); +coresight_cti_reg(pidr3, CORESIGHT_PERIPHIDR3); +coresight_cti_reg(pidr4, CORESIGHT_PERIPHIDR4);
+static struct attribute *coresight_cti_mgmt_attrs[] = {
&dev_attr_devaff0.attr,
&dev_attr_devaff1.attr,
&dev_attr_authstatus.attr,
&dev_attr_devarch.attr,
&dev_attr_devid.attr,
&dev_attr_devtype.attr,
&dev_attr_pidr0.attr,
&dev_attr_pidr1.attr,
&dev_attr_pidr2.attr,
&dev_attr_pidr3.attr,
&dev_attr_pidr4.attr,
NULL,
+};
+static const struct attribute_group coresight_cti_group = {
.attrs = coresight_cti_attrs,
+};
+static const struct attribute_group coresight_cti_regs_group = {
.attrs = coresight_cti_regs_attrs,
.name = "regs",
+};
+static const struct attribute_group coresight_cti_channels_group = {
.attrs = coresight_cti_channel_attrs,
.name = "channels",
+};
+static const struct attribute_group coresight_cti_mgmt_group = {
.attrs = coresight_cti_mgmt_attrs,
.name = "mgmt",
+};
+const struct attribute_group *coresight_cti_groups[] = {
&coresight_cti_group,
&coresight_cti_regs_group,
&coresight_cti_channels_group,
&coresight_cti_mgmt_group,
NULL,
+};
+static int cti_starting_cpu(unsigned int cpu) +{
struct cti_drvdata *drvdata = cti_cpu_drv[cpu];
if (!drvdata)
return 0;
spin_lock(&drvdata->spinlock);
drvdata->config.hw_powered = true;
spin_unlock(&drvdata->spinlock);
if (atomic_read(&drvdata->config.enable_req_count))
cti_enable_hw(drvdata);
return 0;
+}
+static int cti_stopping_cpu(unsigned int cpu) +{
struct cti_drvdata *drvdata = cti_cpu_drv[cpu];
if (!drvdata)
return 0;
cti_disable_hw(drvdata);
spin_lock(&drvdata->spinlock);
drvdata->config.hw_powered = false;
spin_unlock(&drvdata->spinlock);
return 0;
+}
+static void cti_smp_set_powered(void *info) +{
struct cti_drvdata *drvdata = info;
drvdata->config.hw_powered = true;
+}
+/* setup power handling on a cti that has an association with a cpu */ +static int cti_setup_hp_cpu_affinity(struct cti_drvdata *drvdata) +{
int ret = 0;
/* CPU affinity - add to quick lookup array
* and register cpu hp callbacks first time out
*/
cpus_read_lock();
cti_cpu_drv[drvdata->ctidev.cpu] = drvdata;
if (!cti_cpu_count) {
cti_hp_online = cpuhp_setup_state_nocalls_cpuslocked(
CPUHP_AP_ONLINE_DYN, "arm/coresightcti:online",
cti_starting_cpu, cti_stopping_cpu);
if (cti_hp_online < 0) {
ret = cti_hp_online;
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
goto cti_hp_cpu_done;
}
}
cti_cpu_count++;
/* setup powered flag by running function of assoc cpu */
if (smp_call_function_single(drvdata->ctidev.cpu, cti_smp_set_powered,
drvdata, 1)) {
drvdata->config.hw_powered = false;
}
+cti_hp_cpu_done:
cpus_read_unlock();
return ret;
+}
+/* back out hotplug notifications on error */ +static void cti_clear_hp_cpu_affinity(struct cti_drvdata *drvdata) +{
if (!drvdata)
return;
if (drvdata->ctidev.cpu >= 0) {
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
cti_cpu_count--;
if ((cti_cpu_count == 0) && (cti_hp_online > 0))
cpuhp_remove_state_nocalls(cti_hp_online);
}
+}
+static int cti_probe(struct amba_device *adev, const struct amba_id *id) +{
int ret = 0;
u32 devid;
void __iomem *base;
struct device *dev = &adev->dev;
struct cti_drvdata *drvdata = 0;
struct coresight_desc cti_desc;
struct coresight_platform_data *pdata = NULL;
struct resource *res = &adev->res;
struct device_node *np = adev->dev.of_node;
struct ect_node *ect_nd = 0;
/* boilerplate code to set up the basics */
dev_info(dev, "%s(ID = %x)\n", __func__, id->id);
Please remove all debug code such as the above.
if (np) {
pdata = of_get_coresight_ect_platform_data(dev, np);
if (IS_ERR(pdata)) {
dev_info(dev, "of_get_coresight_ect_platform err\n");
ret = PTR_ERR(pdata);
goto err_out;
}
dev->platform_data = pdata;
}
/* node to keep track of CTI net */
ect_nd = kzalloc(sizeof(struct ect_node), GFP_KERNEL);
if (!ect_nd) {
ret = -ENOMEM;
goto err_out;
}
/* driver data*/
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata) {
ret = -ENOMEM;
dev_info(dev, "%s, mem err\n", __func__);
goto err_out;
}
/* links between dev and drvdata*/
drvdata->dev = dev;
dev_set_drvdata(dev, drvdata);
/* default CTI device info */
drvdata->ctidev.cpu = -1;
drvdata->ctidev.nr_trig_con = 0;
drvdata->ctidev.ctm_id = 0;
INIT_LIST_HEAD(&drvdata->ctidev.trig_cons);
/* Validity for the resource is already checked by the AMBA core */
Start with an upper case or a lower case, the choice is yours but pick one and stick to it.
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base)) {
ret = PTR_ERR(base);
dev_info(dev, "%s, remap err\n", __func__);
goto err_out;
}
drvdata->base = base;
spin_lock_init(&drvdata->spinlock);
/* look at the HW DEVID register for some of the HW settings */
devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
drvdata->config.nr_trig_max = (int)((devid & 0xFF00) >> 8);
/* no current hardware should exceed this, but protect the driver
* in case of fault / out of spec hw
*/
There is an extra space on the second line and please adjust multi-line comment format.
if (drvdata->config.nr_trig_max > CTIINOUTEN_MAX) {
dev_warn_once(dev,
"Limiting HW MaxTrig value(%d) to driver max(%d)\n",
drvdata->config.nr_trig_max, CTIINOUTEN_MAX);
drvdata->config.nr_trig_max = CTIINOUTEN_MAX;
}
drvdata->config.nr_ctm_channels = (int)((devid & 0xF0000) >> 16);
dev_info(dev, "DevID %08x, trigs=%d, chans=%d\n", devid,
drvdata->config.nr_trig_max, drvdata->config.nr_ctm_channels);
/** additional parse the .dts for connections and signals */
of_cti_get_hw_data(dev, np, drvdata);
cti_set_default_config(&drvdata->config);
/* setup cpu related CTI devices, otherwise assume powered */
if (drvdata->ctidev.cpu >= 0) {
ret = cti_setup_hp_cpu_affinity(drvdata);
if (ret < 0)
goto err_out;
} else
drvdata->config.hw_powered = true;
/* set up coresight component description */
cti_desc.pdata = pdata;
cti_desc.type = CORESIGHT_DEV_TYPE_ECT;
cti_desc.subtype.ect_subtype = CORESIGHT_DEV_SUBTYPE_ECT_CTI;
cti_desc.ops = &cti_ops;
cti_desc.groups = coresight_cti_groups;
cti_desc.dev = dev;
drvdata->csdev = coresight_register(&cti_desc);
if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
pr_err("%s: CS register failed\n", pdata->name);
goto err_out;
}
/* add to list of CTI devices */
mutex_lock(&ect_mutex);
ect_nd->cti_drv = drvdata;
list_add(&ect_nd->next, &ect_net);
mutex_unlock(&ect_mutex);
/* set any cross references */
cti_update_conn_xrefs(drvdata);
/* all done - dec pm refcount */
pm_runtime_put(&adev->dev);
dev_info(dev, "%s: initialized\n", pdata->name);
cti_count++;
dev_info(dev, "%s - ok\n", __func__);
return 0;
+err_out:
cti_clear_hp_cpu_affinity(drvdata);
dev_info(dev, "%s - err_out\n", __func__);
return ret;
+}
+/* free up CTI specific resources
- called from coresight_device_release on coresight_unregister.
- */
+void cti_device_release(struct device *dev) +{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct ect_node *ect_item, *ect_tmp;
struct cti_trig_con *tc, *tc_tmp;
/* free up resources associated with the cti connections */
if (drvdata->ctidev.cpu >= 0)
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
list_for_each_entry_safe(tc, tc_tmp,
&drvdata->ctidev.trig_cons, node) {
kfree(tc->con_in);
kfree(tc->con_out);
kfree(tc->con_dev_name);
list_del(&tc->node);
kfree(tc);
}
/* remove from the list */
mutex_lock(&ect_mutex);
list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, next) {
if (ect_item->cti_drv == drvdata) {
list_del(&ect_item->next);
kfree(ect_item);
goto ect_list_item_removed;
}
}
+ect_list_item_removed:
mutex_unlock(&ect_mutex);
kfree(drvdata);
+} +EXPORT_SYMBOL_GPL(cti_device_release);
+#define CTI_AMBA_ID(pid) \
{ \
.id = pid, \
.mask = 0x000fffff, \
}
+static struct amba_cs_uci_id uci_id_cti[] = {
{
/* CTI UCI data */
.devarch = 0x47701a14, /* devarch value for CTI v2 */
.devarch_mask = 0xfff0ffff,
.devtype = 0x00000014, /* maj(0x4-debug) min(0x1-ECT) */
}
+};
+#define CTI_AMBA_UCI_ID(pid) \
{ \
.id = pid, \
.mask = 0x000fffff, \
.data = uci_id_cti, \
}
We are doing the same kind of gymnastic with ETMv4. Please introduce a generic macro in coresight-priv.h and use it everywhere.
+static const struct amba_id cti_ids[] = {
CTI_AMBA_ID(0x000bb906), /* Coresight CTI (SoC 400), C-A72, C-A57 */
CTI_AMBA_ID(0x000bb922), /* CTI - C-A8 */
CTI_AMBA_ID(0x000bb9a8), /* CTI - C-A53 */
CTI_AMBA_ID(0x000bb9aa), /* CTI - C-A73 */
CTI_AMBA_UCI_ID(0x000bb9da), /* CTI - C-A35 */
CTI_AMBA_UCI_ID(0x000bb9ed), /* Coresight CTI (SoC 600) */
CTI_AMBA_UCI_ID(0x000bb95d), /* test UCI - ETM PID A53 */
{ 0, 0},
+};
+static struct amba_driver cti_driver = {
.drv = {
.name = "coresight-cti",
.owner = THIS_MODULE,
There is a tab missing between .owner and the '=' sign.
.suppress_bind_attrs = true,
},
.probe = cti_probe,
.id_table = cti_ids,
+}; +builtin_amba_driver(cti_driver); diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h new file mode 100644 index 000000000000..09cb985ac0ea --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
One line too many.
+#ifndef _CORESIGHT_CORESIGHT_CTI_H +#define _CORESIGHT_CORESIGHT_CTI_H
+#include <asm/local.h> +#include <linux/spinlock.h> +#include "coresight-priv.h"
+/*
- Device registers
- 0x000 - 0x144: CTI programming and status
- 0xEDC - 0xEF8: CTI integration test.
- 0xF00 - 0xFFC: Coresight management registers.
- */
+/* CTI programming registers */ +#define CTICONTROL 0x000 +#define CTIINTACK 0x010 +#define CTIAPPSET 0x014 +#define CTIAPPCLEAR 0x018 +#define CTIAPPPULSE 0x01C +#define CTIINEN(n) (0x020 + (4 * n)) +#define CTIOUTEN(n) (0x0A0 + (4 * n)) +#define CTITRIGINSTATUS 0x130 +#define CTITRIGOUTSTATUS 0x134 +#define CTICHINSTATUS 0x138 +#define CTICHOUTSTATUS 0x13C +#define CTIGATE 0x140 +#define ASICCTL 0x144 +/* Integration test registers */ +#define ITCHINACK 0xEDC /* WO CTI CSSoc 400 only*/ +#define ITTRIGINACK 0xEE0 /* WO CTI CSSoc 400 only*/ +#define ITCHOUT 0xEE4 /* WO RW-600 */ +#define ITTRIGOUT 0xEE8 /* WO RW-600 */ +#define ITCHOUTACK 0xEEC /* RO CTI CSSoc 400 only*/ +#define ITTRIGOUTACK 0xEF0 /* RO CTI CSSoc 400 only*/ +#define ITCHIN 0xEF4 /* RO */ +#define ITTRIGIN 0xEF8 /* RO */ +/* management registers */ +#define CTIDEVAFF0 0xFA8 +#define CTIDEVAFF1 0xFAC
+/* CTI CSSoc 600 has a max of 32 trigger signals per direction.
- CTI CSSoc 400 has 8 IO triggers - other CTIs can be impl def.
- Max of in and out defined in the DEVID register.
- pick up actual number used from .dts parameters if present.
- */
+#define CTIINOUTEN_MAX 32
+/**
- Group of related trigger signals
- @nr_sigs: number of signals in the group.
- @used_mask: bitmask representing the signal indexes in the group.
- @sig_names: list of names for the signals.
- */
+struct cti_trig_grp {
int nr_sigs;
u32 used_mask;
char *sig_names;
+};
+/**
- Trigger connection - connection between a CTI and other (coresight) device
- lists input and output trigger signals for the device
- @con_in: connected CTIIN signals for the device.
- @con_out: connected CTIOUT signals for the device.
- @con_dev: coresight device connected to the CTI, NULL if not CS device
- @con_dev_name: name of connected device (CS or CPU)
- @node: entry node in list of connections.
- */
+struct cti_trig_con {
struct cti_trig_grp *con_in;
struct cti_trig_grp *con_out;
struct coresight_device *con_dev;
char *con_dev_name;
struct list_head node;
+};
+/**
- struct cti_device - description of CTI device properties.
- @nt_trig_con: Number of external devices connected to this device.
- @ctm_id: which CTM this device is connected to (by default it is
assumed there is a single CTM per SoC, ID 0).
- @trig_cons: list of connections to this device.
- @cpu: CPU ID if associated with CPU, -1 otherwise.
One line too many.
I'm out of time for today - I will start diving more in the code tomorrow.
Mathieu
- */
+struct cti_device {
int nr_trig_con;
u32 ctm_id;
struct list_head trig_cons;
int cpu;
+};
+/**
- struct cti_config - configuration of the CTI device hardware
- hardware description from RO ID regs
- @nr_trig_max: Max number of trigger signals implemented on device.
(max of trig_in or trig_out)
- @nr_ctm_channels: number of available CTM channels
- cti enable control
- @enable_req_count: CTI is enabled alongside >=1 associated devices.
- @hw_enabled: true if hw is currently enabled.
- @hw_powered: true if associated cpu powered on, or no cpu.
- registered triggers and filtering
- @trig_in_use: bitfield of in triggers registered as in use.
- @trig_out_use: bitfield of out triggers registered as in use.
- @trig_out_filter: bitfield of out triggers that are blocked if filter
enabled. Typically this would be dbgreq / restart on a core CTI.
- @trig_filter_enable: 1 if filtering enabled.
- cti software programmable regs:
- @ctiappset: CTI Software application channel set.
- @ctiinout_sel: register selector for INEN and OUTEN regs.
- @ctiinen: enable input trigger to a channel.
- @ctiouten: enable output trigger from a channel.
- @ctigate: gate channel output from CTI to CTM.
- */
+struct cti_config {
/* hardware description */
int nr_ctm_channels;
int nr_trig_max;
/* cti enable control */
atomic_t enable_req_count;
bool hw_enabled;
bool hw_powered;
/* registered triggers and filtering */
u32 trig_in_use;
u32 trig_out_use;
u32 trig_out_filter;
int trig_filter_enable;
/* cti cross trig programmable regs */
u32 ctiappset;
u8 ctiinout_sel;
u32 ctiinen[CTIINOUTEN_MAX];
u32 ctiouten[CTIINOUTEN_MAX];
u32 ctigate;
u32 asicctl;
+};
+/**
- struct cti_drvdata - specifics for the CTI device
- @base: Memory mapped base address for this component.
- @dev: The device entity associated to this component.
- @csdev: Standard CoreSight device information.
- @ctidev: Extra information needed by the CTI/CTM framework.
- @spinlock: Control data access to one at a time.
- @config: Configuration data for this CTI device.
- */
+struct cti_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
struct cti_device ctidev;
spinlock_t spinlock;
struct cti_config config;
+};
+enum cti_chan_op {
CTI_CHAN_ATTACH,
CTI_CHAN_DETACH,
+};
+enum cti_trig_dir {
CTI_TRIG_IN,
CTI_TRIG_OUT,
+};
+enum cti_chan_gate_op {
CTI_GATE_CHAN_ENABLE,
CTI_GATE_CHAN_DISABLE,
CTI_GATE_CHAN_ENABLE_ALL,
CTI_GATE_CHAN_DISABLE_ALL,
+};
+enum cti_chan_set_op {
CTI_CHAN_SET,
CTI_CHAN_CLR,
CTI_CHAN_PULSE,
+};
+#endif /* _CORESIGHT_CORESIGHT_CTI_H */
2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
On Wed, Jan 09, 2019 at 10:54:38PM +0000, Mike Leach wrote:
Initial addition of the files for the Coresight CTI driver.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-cti.c | 2025 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 195 ++ 2 files changed, 2220 insertions(+) create mode 100644 drivers/hwtracing/coresight/coresight-cti.c create mode 100644 drivers/hwtracing/coresight/coresight-cti.h
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c new file mode 100644 index 000000000000..d06e19f111e2 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -0,0 +1,2025 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/smp.h> +#include <linux/sysfs.h> +#include <linux/stat.h> +#include <linux/clk.h> +#include <linux/cpu.h> +#include <linux/coresight.h> +#include <linux/coresight-pmu.h> +#include <linux/pm_wakeup.h> +#include <linux/amba/bus.h> +#include <linux/seq_file.h> +#include <linux/uaccess.h> +#include <linux/perf_event.h> +#include <linux/pm_runtime.h> +#include <linux/of.h> +#include <asm/sections.h> +#include <asm/local.h>
+#include "coresight-cti.h"
+#define csdev_to_cti_drvdata(csdev) \
- dev_get_drvdata(csdev->dev.parent)
+/* CTI device tree trigger connection node keyword */ +#define CTI_DT_CONNS "trig-conns" +/* CTI device tree connection property keywords */ +#define CTI_DT_V8ARCH "arm,cti-v8-arch" +#define CTI_DT_TRIGIN_SIGS "arm,trig-in-sigs" +#define CTI_DT_TRIGOUT_SIGS "arm,trig-out-sigs" +#define CTI_DT_FILTER_OUT_SIGS "arm,trig-filters" +#define CTI_DT_CSDEV_ASSOC "arm,cs-dev-assoc" +#define CTI_DT_CTM_ID "arm,cti-ctm-id" +#define CTI_DT_CONN_NAME "arm,trig-conn-name"
+/**
- CTI devices can be associated with a PE, or be connected to CoreSight
- hardware. We have a list of all CTIs, and a subset array associated
- with individual PEs for pwr management as they will power up in the
- PE power domain.
- At this point we assume that the none CPU CTIs are always powered as
- we do with sinks etc.
- We leave the client to figure out if all the CTIs are interconnected with
- the same CTM, in general this is the case but does not always have to be.
- */
+struct ect_node {
- struct cti_drvdata *cti_drv;
- struct list_head next;
+};
+/* net of CTI devices connected via CTM */ +LIST_HEAD(ect_net);
+/* protect the list */ +static DEFINE_MUTEX(ect_mutex);
+/* quick reference for CPU CTIs */ +static struct cti_drvdata *cti_cpu_drv[NR_CPUS];
+/* number of registered CTI devices */ +static int cti_count;
+/* number of cpu associated CTI devices in use */ +static int cti_cpu_count;
+/* CTI / cpu hotplug notification ID */ +static enum cpuhp_state cti_hp_online;
+/* cpu powered and enabled */ +#define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered)
+/* write set of regs to hardware - call with spinlock claimed */ +static void cti_write_all_hw_regs(struct cti_drvdata *drvdata) +{
- struct cti_config *config = &drvdata->config;
- int i;
- CS_UNLOCK(drvdata->base);
- /* disable CTI before writing registers */
- writel_relaxed(0, drvdata->base + CTICONTROL);
- /* write the CTI trigger registers */
- for (i = 0; i < config->nr_trig_max; i++) {
writel_relaxed(config->ctiinen[i], drvdata->base + CTIINEN(i));
writel_relaxed(config->ctiouten[i],
drvdata->base + CTIOUTEN(i));
- }
- /* other regs */
- writel_relaxed(config->ctigate, drvdata->base + CTIGATE);
- writel_relaxed(config->asicctl, drvdata->base + ASICCTL);
- writel_relaxed(config->ctiappset, drvdata->base + CTIAPPSET);
- /* re-enable CTI */
- writel_relaxed(1, drvdata->base + CTICONTROL);
- CS_LOCK(drvdata->base);
+}
+static void cti_enable_hw_smp_call(void *info) +{
- struct cti_drvdata *drvdata = info;
- cti_write_all_hw_regs(drvdata);
+}
+/* write regs to hardware and enable */ +static int cti_enable_hw(void *info) +{
- struct cti_drvdata *drvdata = info;
- struct cti_config *config = &drvdata->config;
- int rc = 0;
- pm_runtime_get_sync(drvdata->dev);
- spin_lock(&drvdata->spinlock);
- /* no need to do anything if enabled or unpowered*/
- if (config->hw_enabled || !config->hw_powered) {
spin_unlock(&drvdata->spinlock);
pm_runtime_put(drvdata->dev);
return 0;
- }
- if (drvdata->ctidev.cpu >= 0) {
dev_info(drvdata->dev, "cti enable smp call for cpu %d\n",
drvdata->ctidev.cpu);
rc = smp_call_function_single(drvdata->ctidev.cpu,
cti_enable_hw_smp_call,
drvdata, 1);
- } else {
dev_info(drvdata->dev, "cti enable not cpu call\n");
cti_write_all_hw_regs(drvdata);
- }
- if (!rc)
config->hw_enabled = true;
- spin_unlock(&drvdata->spinlock);
- return 0;
+}
+/* disable hardware */ +static int cti_disable_hw(void *info) +{
- struct cti_drvdata *drvdata = info;
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- /* no need to do anything if disabled or cpu unpowered*/
- if (!config->hw_enabled || !config->hw_powered) {
spin_unlock(&drvdata->spinlock);
return 0;
- }
- CS_UNLOCK(drvdata->base);
- /* disable CTI */
- writel_relaxed(0, drvdata->base + CTICONTROL);
- config->hw_enabled = false;
- CS_LOCK(drvdata->base);
- spin_unlock(&drvdata->spinlock);
- pm_runtime_put(drvdata->dev);
- return 0;
+}
+static void +cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value) +{
- CS_UNLOCK(drvdata->base);
- writel_relaxed(value, drvdata->base+offset);
- CS_LOCK(drvdata->base);
+}
+static void cti_set_default_config(struct cti_config *config) +{
- /* Most regs default to 0 as zalloc'ed except...*/
- config->trig_filter_enable = 1;
- config->ctigate = ((u32)0x1 << config->nr_ctm_channels) - 1;
- atomic_set(&config->enable_req_count, 0);
+}
+static void cti_write_intack(struct device *dev, u32 ackval) +{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- /* write if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIINTACK, ackval);
- spin_unlock(&drvdata->spinlock);
+}
+/* need to update cross references once CTI csdev has been generated. */ +static void cti_update_conn_xrefs(struct cti_drvdata *drvdata) +{
- struct cti_trig_con *tc;
- struct cti_device *ctidev = &drvdata->ctidev;
- list_for_each_entry(tc, &ctidev->trig_cons, node) {
if (tc->con_dev) {
tc->con_dev->ect_dev = drvdata->csdev;
dev_info(drvdata->dev,
"Setting assoc csdev (%px) with (%px)\n",
tc->con_dev, drvdata->csdev);
}
- }
+}
+/* connection information */ +static int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name)
+{
- struct cti_trig_con *tc;
- struct cti_device *cti_dev = &drvdata->ctidev;
- tc = kzalloc(sizeof(struct cti_trig_con), GFP_KERNEL);
- if (tc == 0)
return -ENOMEM;
- tc->con_dev = csdev;
- /* prefer actual associated CS device dev name to supplied value -
* which is likely to be node name / other conn name
*/
- if (csdev)
tc->con_dev_name = kstrdup(dev_name(&csdev->dev), GFP_KERNEL);
- else if (assoc_dev_name != NULL)
tc->con_dev_name = kstrdup(assoc_dev_name, GFP_KERNEL);
- tc->con_in = in_info;
- tc->con_out = out_info;
- list_add_tail(&tc->node, &cti_dev->trig_cons);
- cti_dev->nr_trig_con++;
- /* add connection usage bit info to overall info */
- drvdata->config.trig_in_use |= in_info->used_mask;
- drvdata->config.trig_out_use |= out_info->used_mask;
The call graph for this is:
cti_probe() of_cti_get_hw_data() cti_add_default_connection() cti_add_connection_entry()
What happens to the memory allocted here if something goes wrong in cti_probe()? Instead of kzalloc() use devm_kzalloc() with argument drvdata->dev, which ends up being the same as the call done to allocate drvdata in cti_probe. On the flip side it doesn't take care of freeing memory allocated by kstrdup().
- return 0;
+}
+/* add a default connection if nothing else is specified.
- single connection based on max in/out info, no assoc device
- */
+static int cti_add_default_connection(struct cti_drvdata *drvdata) +{
- struct cti_trig_grp *in, *out;
- int n_trigs = drvdata->config.nr_trig_max;
- u32 n_trig_mask = (0x1 << n_trigs) - 1;
- in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!in)
return -ENOMEM;
- out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!out)
return -ENOMEM;
Memory allocated for 'in' is leaked if we have an error.
- dev_info(drvdata->dev, "default connection: trigs %d, trig_mask %08x\n",
n_trigs, n_trig_mask);
- /* assume max trigs for in and out, all used */
- in->nr_sigs = n_trigs;
- in->used_mask = n_trig_mask;
- in->sig_names = NULL;
- out->nr_sigs = n_trigs;
- out->used_mask = n_trig_mask;
- out->sig_names = NULL;
- return cti_add_connection_entry(drvdata, in, out, NULL, "default");
Same as above - memory for 'in' and 'out' is leaked if cti_add_connection_entry() fails.
+}
+/* create an architecturally defined v8 connection
- must hava a cpu, can have an ETM
- */
Multiline format problem.
+static int of_cti_create_v8_connections(struct cti_drvdata *drvdata,
struct device_node *np)
Indentation problems with the second line. Both 's' from the "struct" should be aligned.
+{
- struct cti_device *cti_dev = &drvdata->ctidev;
- struct cti_trig_grp *in, *out;
- int cpuid = 0;
- struct device_node *cs_np;
- struct coresight_device *csdev = NULL;
- char cpu_name_str[16];
- const char *assoc_name = NULL;
- cpuid = of_coresight_ect_get_cpu(np);
- if (cpuid < 0) {
dev_warn(drvdata->dev, "CTI v8 DT binding no cpu\n");
return -EINVAL;
- }
- cti_dev->cpu = cpuid;
- /* do the v8 cpu connection */
- in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!in)
return -ENOMEM;
- out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!out)
return -ENOMEM;
- /* v8 PE CTI config... */
The rule of thumb is to have a comment per hard-code value. Here it would be nice to have the reference and section number of where you got this configuration from - something like:
/* * Setting up CPU in out trigger group in accordance with document XYZ, * section 123. */
- in->nr_sigs = 3;
- in->used_mask = 0x7;
- in->sig_names = NULL;
- out->nr_sigs = 3;
- out->used_mask = 0x7;
- out->sig_names = NULL;
- scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
- cti_add_connection_entry(drvdata, in, out, NULL, cpu_name_str);
- drvdata->config.trig_out_filter = 0x1; /* filter dbgreq */
- /* v8 ETM associated config... */
- cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
- if (cs_np) {
Does it make sense to have a CTI connected to a CPU but not have an ETM associated with that CPU? Shouldn't this be treated as an error?
in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!in)
return -ENOMEM;
out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!out)
return -ENOMEM;
in->nr_sigs = 4;
in->used_mask = 0xF0;
in->sig_names = NULL;
out->nr_sigs = 4;
out->used_mask = 0xF0;
out->sig_names = NULL;
dev_info(drvdata->dev, "v8 finding assoc dev %s\n",
cs_np->full_name);
csdev = of_coresight_get_cs_device_by_node(cs_np);
if (csdev)
assoc_name = dev_name(&csdev->dev);
else
assoc_name = cs_np->full_name;
I'm guessing you're doing the above to deal with device discovery ordering. If I am correct please add a comment that makes it clear.
cti_add_connection_entry(drvdata, in, out, csdev, assoc_name);
of_node_put(cs_np);
- }
- return 0;
Same comment about memory release in case of something goes wrong. I've also taken the habit of adding a comment that mentions where the memory will be be released for each allocation I do. That helps me remember that I have to release the memory and allows other people to better understand how things work.
+}
+static int of_cti_read_trig_group(struct cti_trig_grp **trig_grp_ptr,
struct device_node *np,
const char *grp_name,
int max_trigs)
+{
- int items, err = 0;
- u32 value, pidx;
- struct cti_trig_grp *grp;
- grp = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!grp)
return -ENOMEM;
- *trig_grp_ptr = grp;
- items = of_property_count_elems_of_size(np, grp_name, 4);
From the documentation and the DT bindings you've provided it is pretty easy to
figure out why you have a 4 there but I will still need a comment to explain it. But have you explained anywhere in the documentation why trigger groups have 4 pins? Is it just that the HW only provides a maximum of 4 in and 4 out for each device? If so, it would be a good thing to write it somewhere.
- /* if the property doesn't exist or has no values, then return
* an empty connection group
*/
- if (items < 0)
return 0;
- if (items > max_trigs)
return -EINVAL;
- /* set the number of signals and usage mask */
- for (pidx = 0; pidx < items; pidx++) {
err = of_property_read_u32_index(np, grp_name, pidx, &value);
if (err)
return err;
Memory leak of 'grp'.
grp->nr_sigs++;
grp->used_mask |= (0x1 << value);
- }
- return 0;
+}
+static int of_cti_create_connection(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
- struct cti_trig_grp *in = 0, *out = 0, *filter = 0;
- int cpuid = -1, err = 0, trig_max = drvdata->config.nr_trig_max;
- struct device_node *cs_np;
- struct coresight_device *csdev = NULL;
- const char *assoc_name = "unknown";
- char cpu_name_str[16];
- /* look for the signals properties. */
- err = of_cti_read_trig_group(&in, np, CTI_DT_TRIGIN_SIGS, trig_max);
- if (err)
return err;
- err = of_cti_read_trig_group(&out, np, CTI_DT_TRIGOUT_SIGS, trig_max);
- if (err)
return err;
- err = of_cti_read_trig_group(&filter, np, CTI_DT_FILTER_OUT_SIGS,
trig_max);
- if (err)
return err;
Whatch out for memory leaks in the above.
- /* read the connection name if set - may be overridden by later */
- of_property_read_string(np, CTI_DT_CONN_NAME, &assoc_name);
- /* associated cpu ? */
- cpuid = of_coresight_ect_get_cpu(np);
- drvdata->ctidev.cpu = cpuid;
- if (cpuid >= 0) {
scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
assoc_name = cpu_name_str;
- } else {
/* associated device ? */
cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
if (cs_np) {
dev_info(dev, "finding assoc dev %s\n",
cs_np->full_name);
csdev = of_coresight_get_cs_device_by_node(cs_np);
if (csdev) /* use device name if csdev found */
assoc_name = dev_name(&csdev->dev);
else /* otherwise node name for later association */
assoc_name = cs_np->full_name;
of_node_put(cs_np);
}
- }
- /* set up a connection */
- err = cti_add_connection_entry(drvdata, in, out, csdev, assoc_name);
- /* note any filter info */
- if (!err)
drvdata->config.trig_out_filter |= filter->used_mask;
- kfree(filter);
- return err;
+}
After reviewing functions of_cti_get_hw_data(), of_cti_create_v8_connections() and of_cti_create_connection() I wonder if they shouldn't be added to a new of-coresight-cti.c file, along with the rest of the OF centric functions needed by the driver.
More to come on Monday.
Mathieu
+/* get the hardware configuration & connection data. */ +static int of_cti_get_hw_data(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
- int rc = 0;
- struct cti_device *cti_dev = &drvdata->ctidev;
- struct device_node *nc = NULL;
- /* get any CTM ID - defaults to 0 */
- of_property_read_u32(np, CTI_DT_CTM_ID, &cti_dev->ctm_id);
- if (of_property_read_bool(np, CTI_DT_V8ARCH)) {
rc = of_cti_create_v8_connections(drvdata, np);
- } else {
for_each_child_of_node(np, nc) {
if (of_node_cmp(nc->name, CTI_DT_CONNS) != 0)
continue;
dev_info(dev, "dt found node: %s %s\n",
nc->name, nc->full_name);
rc = of_cti_create_connection(dev, nc, drvdata);
if (rc != 0)
return rc;
}
- }
- /* if no connections, just add a single default based on max IN-OUT */
- if (cti_dev->nr_trig_con == 0)
rc = cti_add_default_connection(drvdata);
- return rc;
+}
+static int +cti_match_con_name(struct cti_device *ctidev, const char *node_name,
const char *csdev_name)
+{
- int found = 0;
- struct cti_trig_con *trig_con;
- list_for_each_entry(trig_con, &ctidev->trig_cons, node) {
if (trig_con->con_dev_name) {
if (!strcmp(node_name, trig_con->con_dev_name)) {
found = 1;
/* match: so swap in csdev name */
kfree(trig_con->con_dev_name);
trig_con->con_dev_name =
kstrdup(csdev_name, GFP_KERNEL);
goto cti_con_name_match;
}
}
- }
+cti_con_name_match:
- return found;
+}
+/* search the cti list to add an associated CTI into the supplied CS device
- This will set the association if CTI declared before the CS device
- */
+int cti_add_assoc_to_csdev(struct coresight_device *csdev) +{
- struct ect_node *ect_item;
- struct cti_device *ctidev;
- const char *node_name = NULL, *csdev_name;
- /* exit early for no CTIs or self referencing ECT devices.*/
- if (!cti_count) {
dev_info(&csdev->dev, "no CTI to check\n");
return 0;
- }
- if (!csdev->dev.parent->of_node) {
dev_info(&csdev->dev, "No parent of_node pointer\n");
return 0;
- }
- node_name = csdev->dev.parent->of_node->full_name;
- if (!node_name) {
dev_info(&csdev->dev, "%s - bad node name\n", __func__);
return 0;
- }
- dev_info(&csdev->dev, "%s - looking for %s\n", __func__, node_name);
- if (!cti_count || (csdev->type == CORESIGHT_DEV_TYPE_ECT)) {
dev_info(&csdev->dev, "no CTI or skip self ref CTI\n");
return 0;
- }
- csdev_name = dev_name(&csdev->dev);
- /* for each CTI in list... */
- mutex_lock(&ect_mutex);
- list_for_each_entry(ect_item, &ect_net, next) {
ctidev = &ect_item->cti_drv->ctidev;
if (cti_match_con_name(ctidev, node_name, csdev_name)) {
csdev->ect_dev = ect_item->cti_drv->csdev;
dev_info(&csdev->dev, "Found!\n");
goto cti_found;
}
- }
+cti_found:
- mutex_unlock(&ect_mutex);
- return 0;
+} +EXPORT_SYMBOL_GPL(cti_add_assoc_to_csdev);
+/* attach/detach channel from trigger - write through if enabled. */ +static int cti_channel_trig_op(struct device *dev,
enum cti_chan_op op,
enum cti_trig_dir direction,
u32 channel_idx,
u32 trigger_idx)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- u32 trig_bitmask;
- u32 chan_bitmask;
- u32 reg_value;
- int reg_offset;
- dev_info(dev, "chan_trig_op: op %d, dir %d, chan %d, trig %d\n",
op, direction, channel_idx, trigger_idx);
- /* ensure indexes in range */
- if ((channel_idx >= config->nr_ctm_channels) ||
(trigger_idx >= config->nr_trig_max))
return -EINVAL;
- trig_bitmask = 0x1 << trigger_idx;
- /* ensure registered triggers and not out filtered */
- if (direction == CTI_TRIG_IN) {
if (!(trig_bitmask & config->trig_in_use))
return -EINVAL;
- } else {
if (!(trig_bitmask & config->trig_out_use))
return -EINVAL;
if ((config->trig_filter_enable) &&
(config->trig_out_filter & trig_bitmask))
return -EINVAL;
- }
- /* update the local register values */
- chan_bitmask = 0x1 << channel_idx;
- reg_offset = (direction == CTI_TRIG_IN ? CTIINEN(trigger_idx) :
CTIOUTEN(trigger_idx));
- spin_lock(&drvdata->spinlock);
- /* read - modify write - the trigger / channel enable value */
- reg_value = (direction == CTI_TRIG_IN ? config->ctiinen[trigger_idx] :
config->ctiouten[trigger_idx]);
- reg_value = (op == CTI_CHAN_ATTACH) ? reg_value | chan_bitmask :
reg_value & (~chan_bitmask);
- /* write local copy */
- if (direction == CTI_TRIG_IN)
config->ctiinen[trigger_idx] = reg_value;
- else
config->ctiouten[trigger_idx] = reg_value;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
- spin_unlock(&drvdata->spinlock);
- return 0;
+}
+static int cti_channel_gate_op(struct device *dev,
enum cti_chan_gate_op op,
u32 channel_idx)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- u32 chan_bitmask;
- u32 reg_value;
- int err = 0;
- if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
- chan_bitmask = 0x1 << channel_idx;
- spin_lock(&drvdata->spinlock);
- reg_value = config->ctigate;
- switch (op) {
- case CTI_GATE_CHAN_ENABLE:
reg_value |= chan_bitmask;
break;
- case CTI_GATE_CHAN_DISABLE:
reg_value &= ~chan_bitmask;
break;
- case CTI_GATE_CHAN_ENABLE_ALL:
reg_value = (0x1 << config->nr_ctm_channels) - 1;
break;
- case CTI_GATE_CHAN_DISABLE_ALL:
reg_value = 0x0;
break;
- default:
err = -EINVAL;
break;
- }
- if (err == 0) {
config->ctigate = reg_value;
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIGATE, reg_value);
- }
- spin_unlock(&drvdata->spinlock);
- return err;
+}
+static int cti_channel_setop(struct device *dev,
enum cti_chan_set_op op,
u32 channel_idx)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- u32 chan_bitmask;
- u32 reg_value;
- u32 reg_offset;
- int err = 0;
- if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
- chan_bitmask = 0x1 << channel_idx;
- spin_lock(&drvdata->spinlock);
- reg_value = config->ctiappset;
- switch (op) {
- case CTI_CHAN_SET:
config->ctiappset |= chan_bitmask;
reg_value = config->ctiappset;
reg_offset = CTIAPPSET;
break;
- case CTI_CHAN_CLR:
config->ctiappset &= ~chan_bitmask;
reg_value = chan_bitmask;
reg_offset = CTIAPPCLEAR;
break;
- case CTI_CHAN_PULSE:
config->ctiappset &= ~chan_bitmask;
reg_value = chan_bitmask;
reg_offset = CTIAPPPULSE;
break;
- default:
err = -EINVAL;
break;
- }
- if ((err == 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
- spin_unlock(&drvdata->spinlock);
- return err;
+}
+/** cti ect operations **/ +static int cti_enable(struct coresight_device *csdev, void *__unused) +{
- int rc;
- struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
- atomic_inc(&drvdata->config.enable_req_count);
- rc = cti_enable_hw(drvdata);
- if (rc)
atomic_dec(&drvdata->config.enable_req_count);
- return rc;
+}
+static int cti_disable(struct coresight_device *csdev, void *__unused) +{
- int rc = 0;
- struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
- if (!atomic_dec_return(&drvdata->config.enable_req_count)) {
rc = cti_disable_hw(drvdata);
if (rc)
atomic_inc(&drvdata->config.enable_req_count);
- }
- return rc;
+}
+const struct coresight_ops_ect cti_ops_ect = {
- .enable = cti_enable,
- .disable = cti_disable,
+};
+const struct coresight_ops cti_ops = {
- .ect_ops = &cti_ops_ect,
+};
+/** attributes **/
+#define coresight_cti_reg(name, offset) \
- coresight_simple_reg32(struct cti_drvdata, name, offset)
+/* show a simple 32 bit value. if pval is NULL then live read,
- otherwise read from supplied pointer only
- */
+static ssize_t cti_reg32_show(struct device *dev, char *buf,
u32 *pval, int reg_offset)
+{
- unsigned long val = 0;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- if (pval) {
val = (unsigned long)*pval;
- } else if ((reg_offset >= 0) && CTI_PWR_ENA(config)) {
CS_UNLOCK(drvdata->base);
val = readl_relaxed(drvdata->base + reg_offset);
CS_LOCK(drvdata->base);
- }
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+/* store a simple 32 bit value.
- if pval not NULL, then copy to here too,
- if reg_offset >= 0 then write through if enabled.
- */
+static ssize_t cti_reg32_store(struct device *dev, const char *buf,
size_t size, u32 *pval,
int reg_offset)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- /* local store */
- if (pval)
*pval = (u32)val;
- /* write through of offset and enabled */
- if ((reg_offset >= 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, val);
- spin_unlock(&drvdata->spinlock);
- return size;
+}
+/* basic attributes */ +static ssize_t enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- int enable_req;
- bool enabled, powered, cpuid;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- ssize_t size = 0;
- enable_req = atomic_read(&drvdata->config.enable_req_count);
- spin_lock(&drvdata->spinlock);
- powered = drvdata->config.hw_powered;
- enabled = drvdata->config.hw_enabled;
- cpuid = drvdata->ctidev.cpu;
- spin_unlock(&drvdata->spinlock);
- if (powered) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled", cpuid);
- } else if (cpuid >= 0) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req", cpuid);
- } else {
size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n",
enabled ? "enabled" : "disabled");
- }
- return size;
+}
+static ssize_t enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int ret = 0;
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- ret = kstrtoul(buf, 16, &val);
- if (ret)
return ret;
- if (val)
ret = cti_enable(drvdata->csdev, NULL);
- else
ret = cti_disable(drvdata->csdev, NULL);
- if (ret)
return ret;
- return size;
+} +static DEVICE_ATTR_RW(enable);
+static ssize_t ctmid_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return scnprintf(buf, PAGE_SIZE, "0x%x\n", drvdata->ctidev.ctm_id);
+} +static DEVICE_ATTR_RO(ctmid);
+#define LF_ADJUST_BUF_PTRS(n) \
- do { buf_pos += n; buf_size -= n; \
- if (buf_size <= 1) \
goto lf_buf_full; \
- } while (0)
+static ssize_t list_features_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- int used = 0, sig_idx, con_idx, nr_trig_max;
- /* buffer vars */
- int buf_size = PAGE_SIZE;
- char *buf_pos = buf;
- char *con_name;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *cfg = &drvdata->config;
- struct cti_device *ctidev = &drvdata->ctidev;
- u32 sig_mask;
- struct cti_trig_con *con;
- /* basic feature info */
- used = scnprintf(buf_pos, buf_size,
"CTI:%s; Channels:%d; Max Trigs:%d\n",
dev_name(dev), cfg->nr_ctm_channels, cfg->nr_trig_max);
- LF_ADJUST_BUF_PTRS(used);
- used = scnprintf(buf_pos, buf_size, "CTM id = %d; Num Conns = %d\n",
ctidev->ctm_id, ctidev->nr_trig_con);
- LF_ADJUST_BUF_PTRS(used);
- /* list each connection info */
- con_idx = 0;
- nr_trig_max = cfg->nr_trig_max;
- list_for_each_entry(con, &ctidev->trig_cons, node) {
/* connection name */
if (con->con_dev_name != NULL)
con_name = con->con_dev_name;
else
con_name = "unknown";
used = scnprintf(buf_pos, buf_size, "conn(%d:%s)\n",
con_idx, con_name);
LF_ADJUST_BUF_PTRS(used);
con_idx++;
/* connection signals */
used = scnprintf(buf_pos, buf_size, "Trig IN(%d) [ ",
con->con_in->nr_sigs);
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & con->con_in->used_mask) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
used = scnprintf(buf_pos, buf_size, "Trig OUT(%d) [ ",
con->con_out->nr_sigs);
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & con->con_out->used_mask) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
- }
- if (cfg->trig_out_filter) {
used = scnprintf(buf_pos, buf_size, "Trig OUT_FILTERED [ ");
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & cfg->trig_out_filter) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
- }
+lf_buf_full:
- used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
- return used;
+} +static DEVICE_ATTR_RO(list_features);
+static struct attribute *coresight_cti_attrs[] = {
- &dev_attr_enable.attr,
- &dev_attr_list_features.attr,
- &dev_attr_ctmid.attr,
- NULL,
+};
+/* raw register attributes */ +static ssize_t inout_sel_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- u32 val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- val = (u32)drvdata->config.ctiinout_sel;
- return scnprintf(buf, PAGE_SIZE, "%#x\n", val);
+}
+static ssize_t inout_sel_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- if (val > (CTIINOUTEN_MAX-1))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- drvdata->config.ctiinout_sel = val;
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(inout_sel);
+static ssize_t inen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- spin_lock(&drvdata->spinlock);
- index = drvdata->config.ctiinout_sel;
- val = drvdata->config.ctiinen[index];
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "INEN%d %#lx\n", index, val);
+}
+static ssize_t inen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- index = config->ctiinout_sel;
- config->ctiinen[index] = val;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIINEN(index), val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(inen);
+static ssize_t outen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- spin_lock(&drvdata->spinlock);
- index = drvdata->config.ctiinout_sel;
- val = drvdata->config.ctiouten[index];
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "OUTEN%d %#lx\n", index, val);
+}
+static ssize_t outen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- index = config->ctiinout_sel;
- config->ctiouten[index] = val;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIOUTEN(index), val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(outen);
+static ssize_t gate_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_show(dev, buf, &drvdata->config.ctigate, -1);
+}
+static ssize_t gate_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_store(dev, buf, size,
&drvdata->config.ctigate, CTIGATE);
+} +static DEVICE_ATTR_RW(gate);
+static ssize_t asicctl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_show(dev, buf, &drvdata->config.asicctl, -1);
+}
+static ssize_t asicctl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_store(dev, buf, size,
&drvdata->config.asicctl, ASICCTL);
+} +static DEVICE_ATTR_RW(asicctl);
+static ssize_t intack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- cti_write_intack(dev, val);
- return size;
+} +static DEVICE_ATTR_WO(intack);
+static ssize_t appset_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_show(dev, buf, &drvdata->config.ctiappset, -1);
+}
+static ssize_t appset_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_store(dev, buf, size,
&drvdata->config.ctiappset, CTIAPPSET);
+} +static DEVICE_ATTR_RW(appset);
+static ssize_t appclear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val, mask;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- /* a 1'b1 in appclr clears down the same bit in appset*/
- mask = ~val;
- config->ctiappset &= mask;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPCLEAR, val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_WO(appclear);
+static ssize_t apppulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val, mask;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- /* a 1'b1 in apppulse sets then clears the bit,
* effectively clears down the same bit in appset
*/
- mask = ~val;
- config->ctiappset &= mask;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPPULSE, val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_WO(apppulse);
+static ssize_t itchout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return cti_reg32_show(dev, buf, NULL, ITCHOUT);
+}
+static ssize_t itchout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITCHOUT);
+} +static DEVICE_ATTR_RW(itchout);
+static ssize_t ittrigout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return cti_reg32_show(dev, buf, NULL, ITTRIGOUT);
+}
+static ssize_t ittrigout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITTRIGOUT);
+} +static DEVICE_ATTR_RW(ittrigout);
+static ssize_t itchinack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITCHINACK);
+} +static DEVICE_ATTR_WO(itchinack);
+static ssize_t ittriginack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITTRIGINACK);
+} +static DEVICE_ATTR_WO(ittriginack);
+static ssize_t itctrl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return cti_reg32_show(dev, buf, NULL, CORESIGHT_ITCTRL);
+}
+static ssize_t itctrl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, CORESIGHT_ITCTRL);
+} +static DEVICE_ATTR_RW(itctrl);
+coresight_cti_reg(triginstatus, CTITRIGINSTATUS); +coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS); +coresight_cti_reg(chinstatus, CTICHINSTATUS); +coresight_cti_reg(choutstatus, CTICHOUTSTATUS); +coresight_cti_reg(ittrigin, ITTRIGIN); +coresight_cti_reg(itchin, ITCHIN); +coresight_cti_reg(itchoutack, ITCHOUTACK); +coresight_cti_reg(ittrigoutack, ITTRIGOUTACK);
+static struct attribute *coresight_cti_regs_attrs[] = {
- &dev_attr_inout_sel.attr,
- &dev_attr_inen.attr,
- &dev_attr_outen.attr,
- &dev_attr_gate.attr,
- &dev_attr_asicctl.attr,
- &dev_attr_intack.attr,
- &dev_attr_appset.attr,
- &dev_attr_appclear.attr,
- &dev_attr_apppulse.attr,
- &dev_attr_triginstatus.attr,
- &dev_attr_trigoutstatus.attr,
- &dev_attr_chinstatus.attr,
- &dev_attr_choutstatus.attr,
- &dev_attr_itctrl.attr,
- &dev_attr_ittrigin.attr,
- &dev_attr_itchin.attr,
- &dev_attr_ittrigout.attr,
- &dev_attr_itchout.attr,
- &dev_attr_itchoutack.attr,
- &dev_attr_ittrigoutack.attr,
- &dev_attr_ittriginack.attr,
- &dev_attr_itchinack.attr,
- NULL,
+};
+/* channel / trigger api */ +static int +cti_trig_op_parse(struct device *dev, enum cti_chan_op op,
enum cti_trig_dir dir, const char *buf, size_t size)
+{
- u32 chan_idx;
- u32 trig_idx;
- int items, err = size;
- /* extract chan idx and trigger idx */
- items = sscanf(buf, "%d %d", &chan_idx, &trig_idx);
- if (items) {
err = cti_channel_trig_op(dev, op, dir, chan_idx, trig_idx);
if (!err)
err = size;
- } else
err = -EINVAL;
- return err;
+}
+static ssize_t trigin_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_attach);
+static ssize_t trigin_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_detach);
+static ssize_t trigout_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_attach);
+static ssize_t trigout_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_detach);
+static ssize_t gate_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = 0, channel = 0, items;
- if (strcmp(buf, "all") == 0) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE_ALL, 0);
- } else {
items = sscanf(buf, "%d", &channel);
if (!items)
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE, 0);
- }
- return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_enable);
+static ssize_t gate_disable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = 0, channel = 0, items;
- if (strcmp(buf, "all") == 0) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE_ALL, 0);
- } else {
items = sscanf(buf, "%d", &channel);
if (!items)
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE, 0);
- }
- return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_disable);
+static int +chan_op_parse(struct device *dev, enum cti_chan_set_op op, const char *buf) +{
- int err = 0, channel = 0, items;
- items = sscanf(buf, "%d", &channel);
- if (!items)
return -EINVAL;
- err = cti_channel_setop(dev, op, channel);
- return err;
+}
+static ssize_t chan_set_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = chan_op_parse(dev, CTI_CHAN_SET, buf);
- return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_set);
+static ssize_t chan_clear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = chan_op_parse(dev, CTI_CHAN_CLR, buf);
- return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_clear);
+static ssize_t chan_pulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = chan_op_parse(dev, CTI_CHAN_PULSE, buf);
- return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_pulse);
+static ssize_t trig_filter_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- spin_lock(&drvdata->spinlock);
- val = drvdata->config.trig_filter_enable;
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "%ld (%s)\n", val,
val ? "enabled" : "disabled");
+}
+static ssize_t trig_filter_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- drvdata->config.trig_filter_enable = val ? 1 : 0;
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(trig_filter_enable);
+/* clear all xtrigger / channel programming */ +static ssize_t reset_xtrigs_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int i;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- /* clear the CTI trigger / channel programming registers */
- for (i = 0; i < config->nr_trig_max; i++) {
config->ctiinen[i] = 0;
config->ctiouten[i] = 0;
- }
- /* clear the other regs */
- config->ctigate = (0x1 << config->nr_ctm_channels) - 1;
- config->asicctl = 0;
- config->ctiappset = 0;
- config->ctiinout_sel = 0;
- /* if enabled then write through */
- if (CTI_PWR_ENA(config))
cti_write_all_hw_regs(drvdata);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_WO(reset_xtrigs);
+/* list attachments by channel */ +#define LX_ADJUST_BUF_PTRS(n) \
- do { buf_pos += n; buf_size -= n; \
- if (buf_size <= 1) \
goto lx_buf_full; \
- } while (0)
+static ssize_t list_xtrigs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- int used = 0, nr_channels, chan_idx, reg_idx;
- /* buffer vars */
- int buf_size = PAGE_SIZE;
- char *buf_pos = buf;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *cfg = &drvdata->config;
- u32 chan_mask;
- nr_channels = cfg->nr_ctm_channels;
- used = scnprintf(buf_pos, buf_size, "CTI:%s; Channels:%d\n",
dev_name(dev), nr_channels);
- LX_ADJUST_BUF_PTRS(used);
- /* show the IN and OUT triggers per channel */
- for (chan_idx = 0; chan_idx < nr_channels; chan_idx++) {
chan_mask = 0x1 << chan_idx;
used = scnprintf(buf_pos, buf_size, "Chan %d: IN [ ", chan_idx);
LX_ADJUST_BUF_PTRS(used);
for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiinen[reg_idx]) {
used = scnprintf(buf_pos, buf_size, "%d ",
reg_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]; OUT [ ");
LX_ADJUST_BUF_PTRS(used);
for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiouten[reg_idx]) {
used = scnprintf(buf_pos, buf_size, "%d ",
reg_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "];\n");
LX_ADJUST_BUF_PTRS(used);
- }
- /* show the channels enabled via ctigate */
- used = scnprintf(buf_pos, buf_size, "Gate Channels Enabled: [ ");
- LX_ADJUST_BUF_PTRS(used);
- if (cfg->ctigate == 0) {
used = scnprintf(buf_pos, buf_size, "None ]\n");
LX_ADJUST_BUF_PTRS(used);
- } else {
for (chan_idx = 0; chan_idx < nr_channels; chan_idx++) {
chan_mask = 0x1 << chan_idx;
if (chan_mask & cfg->ctigate) {
used = scnprintf(buf_pos, buf_size, "%d ",
chan_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LX_ADJUST_BUF_PTRS(used);
- }
+lx_buf_full:
- used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
- return used;
+} +static DEVICE_ATTR_RO(list_xtrigs);
+#define LCIU_ADJUST_BUF_PTRS(n) \
- do { buf_pos += n; buf_size -= n; \
- if (buf_size <= 1) \
goto lciu_buf_full; \
- } while (0)
+static ssize_t list_chan_inuse_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- int used = 0, nr_channels, i;
- /* buffer vars */
- int buf_size = PAGE_SIZE;
- char *buf_pos = buf;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- u32 inuse_bits = 0, chan_mask, chan_bit_mask;
- nr_channels = config->nr_ctm_channels;
- spin_lock(&drvdata->spinlock);
- for (i = 0; i < config->nr_trig_max; i++) {
inuse_bits |= config->ctiinen[i];
inuse_bits |= config->ctiouten[i];
- }
- spin_unlock(&drvdata->spinlock);
- /* print in use channels */
- chan_mask = ((u32)(0x1 << nr_channels)) - 1;
- if (inuse_bits & chan_mask) {
used = scnprintf(buf_pos, buf_size, "Chan in use [ ");
LCIU_ADJUST_BUF_PTRS(used);
for (i = 0; i < nr_channels; i++) {
chan_bit_mask = 0x1 << i;
if (chan_bit_mask & inuse_bits) {
used = scnprintf(buf_pos, buf_size, "%d ", i);
LCIU_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LCIU_ADJUST_BUF_PTRS(used);
- } else {
used = scnprintf(buf_pos, buf_size, "Chan in use [ none ]\n");
LCIU_ADJUST_BUF_PTRS(used);
- }
- /* print free channels */
- inuse_bits = ~inuse_bits; /* flip for free channels */
- if (inuse_bits & chan_mask) {
used = scnprintf(buf_pos, buf_size, "Chan free [ ");
LCIU_ADJUST_BUF_PTRS(used);
for (i = 0; i < nr_channels; i++) {
chan_bit_mask = 0x1 << i;
if (chan_bit_mask & inuse_bits) {
used = scnprintf(buf_pos, buf_size, "%d ", i);
LCIU_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LCIU_ADJUST_BUF_PTRS(used);
- } else {
used = scnprintf(buf_pos, buf_size, "Chan free [ none ]\n");
LCIU_ADJUST_BUF_PTRS(used);
- }
+lciu_buf_full:
- used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
- return used;
+} +static DEVICE_ATTR_RO(list_chan_inuse);
+static struct attribute *coresight_cti_channel_attrs[] = {
- &dev_attr_trigin_attach.attr,
- &dev_attr_trigin_detach.attr,
- &dev_attr_trigout_attach.attr,
- &dev_attr_trigout_detach.attr,
- &dev_attr_gate_enable.attr,
- &dev_attr_gate_disable.attr,
- &dev_attr_chan_set.attr,
- &dev_attr_chan_clear.attr,
- &dev_attr_chan_pulse.attr,
- &dev_attr_trig_filter_enable.attr,
- &dev_attr_list_xtrigs.attr,
- &dev_attr_reset_xtrigs.attr,
- &dev_attr_list_chan_inuse.attr,
- NULL,
+};
+/* coresight management registers */ +coresight_cti_reg(devaff0, CTIDEVAFF0); +coresight_cti_reg(devaff1, CTIDEVAFF1); +coresight_cti_reg(authstatus, CORESIGHT_AUTHSTATUS); +coresight_cti_reg(devarch, CORESIGHT_DEVARCH); +coresight_cti_reg(devid, CORESIGHT_DEVID); +coresight_cti_reg(devtype, CORESIGHT_DEVTYPE); +coresight_cti_reg(pidr0, CORESIGHT_PERIPHIDR0); +coresight_cti_reg(pidr1, CORESIGHT_PERIPHIDR1); +coresight_cti_reg(pidr2, CORESIGHT_PERIPHIDR2); +coresight_cti_reg(pidr3, CORESIGHT_PERIPHIDR3); +coresight_cti_reg(pidr4, CORESIGHT_PERIPHIDR4);
+static struct attribute *coresight_cti_mgmt_attrs[] = {
- &dev_attr_devaff0.attr,
- &dev_attr_devaff1.attr,
- &dev_attr_authstatus.attr,
- &dev_attr_devarch.attr,
- &dev_attr_devid.attr,
- &dev_attr_devtype.attr,
- &dev_attr_pidr0.attr,
- &dev_attr_pidr1.attr,
- &dev_attr_pidr2.attr,
- &dev_attr_pidr3.attr,
- &dev_attr_pidr4.attr,
- NULL,
+};
+static const struct attribute_group coresight_cti_group = {
- .attrs = coresight_cti_attrs,
+};
+static const struct attribute_group coresight_cti_regs_group = {
- .attrs = coresight_cti_regs_attrs,
- .name = "regs",
+};
+static const struct attribute_group coresight_cti_channels_group = {
- .attrs = coresight_cti_channel_attrs,
- .name = "channels",
+};
+static const struct attribute_group coresight_cti_mgmt_group = {
- .attrs = coresight_cti_mgmt_attrs,
- .name = "mgmt",
+};
+const struct attribute_group *coresight_cti_groups[] = {
- &coresight_cti_group,
- &coresight_cti_regs_group,
- &coresight_cti_channels_group,
- &coresight_cti_mgmt_group,
- NULL,
+};
+static int cti_starting_cpu(unsigned int cpu) +{
- struct cti_drvdata *drvdata = cti_cpu_drv[cpu];
- if (!drvdata)
return 0;
- spin_lock(&drvdata->spinlock);
- drvdata->config.hw_powered = true;
- spin_unlock(&drvdata->spinlock);
- if (atomic_read(&drvdata->config.enable_req_count))
cti_enable_hw(drvdata);
- return 0;
+}
+static int cti_stopping_cpu(unsigned int cpu) +{
- struct cti_drvdata *drvdata = cti_cpu_drv[cpu];
- if (!drvdata)
return 0;
- cti_disable_hw(drvdata);
- spin_lock(&drvdata->spinlock);
- drvdata->config.hw_powered = false;
- spin_unlock(&drvdata->spinlock);
- return 0;
+}
+static void cti_smp_set_powered(void *info) +{
- struct cti_drvdata *drvdata = info;
- drvdata->config.hw_powered = true;
+}
+/* setup power handling on a cti that has an association with a cpu */ +static int cti_setup_hp_cpu_affinity(struct cti_drvdata *drvdata) +{
- int ret = 0;
- /* CPU affinity - add to quick lookup array
* and register cpu hp callbacks first time out
*/
- cpus_read_lock();
- cti_cpu_drv[drvdata->ctidev.cpu] = drvdata;
- if (!cti_cpu_count) {
cti_hp_online = cpuhp_setup_state_nocalls_cpuslocked(
CPUHP_AP_ONLINE_DYN, "arm/coresightcti:online",
cti_starting_cpu, cti_stopping_cpu);
if (cti_hp_online < 0) {
ret = cti_hp_online;
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
goto cti_hp_cpu_done;
}
- }
- cti_cpu_count++;
- /* setup powered flag by running function of assoc cpu */
- if (smp_call_function_single(drvdata->ctidev.cpu, cti_smp_set_powered,
drvdata, 1)) {
drvdata->config.hw_powered = false;
- }
+cti_hp_cpu_done:
- cpus_read_unlock();
- return ret;
+}
+/* back out hotplug notifications on error */ +static void cti_clear_hp_cpu_affinity(struct cti_drvdata *drvdata) +{
- if (!drvdata)
return;
- if (drvdata->ctidev.cpu >= 0) {
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
cti_cpu_count--;
if ((cti_cpu_count == 0) && (cti_hp_online > 0))
cpuhp_remove_state_nocalls(cti_hp_online);
- }
+}
+static int cti_probe(struct amba_device *adev, const struct amba_id *id) +{
- int ret = 0;
- u32 devid;
- void __iomem *base;
- struct device *dev = &adev->dev;
- struct cti_drvdata *drvdata = 0;
- struct coresight_desc cti_desc;
- struct coresight_platform_data *pdata = NULL;
- struct resource *res = &adev->res;
- struct device_node *np = adev->dev.of_node;
- struct ect_node *ect_nd = 0;
- /* boilerplate code to set up the basics */
- dev_info(dev, "%s(ID = %x)\n", __func__, id->id);
- if (np) {
pdata = of_get_coresight_ect_platform_data(dev, np);
if (IS_ERR(pdata)) {
dev_info(dev, "of_get_coresight_ect_platform err\n");
ret = PTR_ERR(pdata);
goto err_out;
}
dev->platform_data = pdata;
- }
- /* node to keep track of CTI net */
- ect_nd = kzalloc(sizeof(struct ect_node), GFP_KERNEL);
- if (!ect_nd) {
ret = -ENOMEM;
goto err_out;
- }
- /* driver data*/
- drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
- if (!drvdata) {
ret = -ENOMEM;
dev_info(dev, "%s, mem err\n", __func__);
goto err_out;
If you go to err_out here ect_nd allocated above is leaked. The same applies to the reste of the 'goto err_out;' below.
- }
- /* links between dev and drvdata*/
- drvdata->dev = dev;
- dev_set_drvdata(dev, drvdata);
- /* default CTI device info */
- drvdata->ctidev.cpu = -1;
- drvdata->ctidev.nr_trig_con = 0;
- drvdata->ctidev.ctm_id = 0;
- INIT_LIST_HEAD(&drvdata->ctidev.trig_cons);
- /* Validity for the resource is already checked by the AMBA core */
- base = devm_ioremap_resource(dev, res);
- if (IS_ERR(base)) {
ret = PTR_ERR(base);
dev_info(dev, "%s, remap err\n", __func__);
goto err_out;
- }
- drvdata->base = base;
- spin_lock_init(&drvdata->spinlock);
- /* look at the HW DEVID register for some of the HW settings */
- devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
- drvdata->config.nr_trig_max = (int)((devid & 0xFF00) >> 8);
- /* no current hardware should exceed this, but protect the driver
* in case of fault / out of spec hw
*/
- if (drvdata->config.nr_trig_max > CTIINOUTEN_MAX) {
dev_warn_once(dev,
"Limiting HW MaxTrig value(%d) to driver max(%d)\n",
drvdata->config.nr_trig_max, CTIINOUTEN_MAX);
drvdata->config.nr_trig_max = CTIINOUTEN_MAX;
- }
- drvdata->config.nr_ctm_channels = (int)((devid & 0xF0000) >> 16);
- dev_info(dev, "DevID %08x, trigs=%d, chans=%d\n", devid,
drvdata->config.nr_trig_max, drvdata->config.nr_ctm_channels);
- /** additional parse the .dts for connections and signals */
- of_cti_get_hw_data(dev, np, drvdata);
- cti_set_default_config(&drvdata->config);
- /* setup cpu related CTI devices, otherwise assume powered */
- if (drvdata->ctidev.cpu >= 0) {
ret = cti_setup_hp_cpu_affinity(drvdata);
if (ret < 0)
goto err_out;
- } else
drvdata->config.hw_powered = true;
- /* set up coresight component description */
- cti_desc.pdata = pdata;
- cti_desc.type = CORESIGHT_DEV_TYPE_ECT;
- cti_desc.subtype.ect_subtype = CORESIGHT_DEV_SUBTYPE_ECT_CTI;
- cti_desc.ops = &cti_ops;
- cti_desc.groups = coresight_cti_groups;
- cti_desc.dev = dev;
- drvdata->csdev = coresight_register(&cti_desc);
- if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
pr_err("%s: CS register failed\n", pdata->name);
goto err_out;
- }
- /* add to list of CTI devices */
- mutex_lock(&ect_mutex);
- ect_nd->cti_drv = drvdata;
- list_add(&ect_nd->next, &ect_net);
- mutex_unlock(&ect_mutex);
- /* set any cross references */
- cti_update_conn_xrefs(drvdata);
- /* all done - dec pm refcount */
- pm_runtime_put(&adev->dev);
- dev_info(dev, "%s: initialized\n", pdata->name);
- cti_count++;
- dev_info(dev, "%s - ok\n", __func__);
- return 0;
+err_out:
- cti_clear_hp_cpu_affinity(drvdata);
- dev_info(dev, "%s - err_out\n", __func__);
- return ret;
+}
+/* free up CTI specific resources
- called from coresight_device_release on coresight_unregister.
- */
+void cti_device_release(struct device *dev) +{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct ect_node *ect_item, *ect_tmp;
- struct cti_trig_con *tc, *tc_tmp;
- /* free up resources associated with the cti connections */
- if (drvdata->ctidev.cpu >= 0)
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
- list_for_each_entry_safe(tc, tc_tmp,
&drvdata->ctidev.trig_cons, node) {
kfree(tc->con_in);
kfree(tc->con_out);
kfree(tc->con_dev_name);
list_del(&tc->node);
kfree(tc);
- }
- /* remove from the list */
- mutex_lock(&ect_mutex);
- list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, next) {
if (ect_item->cti_drv == drvdata) {
list_del(&ect_item->next);
kfree(ect_item);
goto ect_list_item_removed;
}
- }
+ect_list_item_removed:
- mutex_unlock(&ect_mutex);
- kfree(drvdata);
+} +EXPORT_SYMBOL_GPL(cti_device_release);
+#define CTI_AMBA_ID(pid) \
- { \
.id = pid, \
.mask = 0x000fffff, \
- }
+static struct amba_cs_uci_id uci_id_cti[] = {
- {
/* CTI UCI data */
.devarch = 0x47701a14, /* devarch value for CTI v2 */
.devarch_mask = 0xfff0ffff,
.devtype = 0x00000014, /* maj(0x4-debug) min(0x1-ECT) */
- }
+};
+#define CTI_AMBA_UCI_ID(pid) \
- { \
.id = pid, \
.mask = 0x000fffff, \
.data = uci_id_cti, \
- }
+static const struct amba_id cti_ids[] = {
- CTI_AMBA_ID(0x000bb906), /* Coresight CTI (SoC 400), C-A72, C-A57 */
- CTI_AMBA_ID(0x000bb922), /* CTI - C-A8 */
- CTI_AMBA_ID(0x000bb9a8), /* CTI - C-A53 */
- CTI_AMBA_ID(0x000bb9aa), /* CTI - C-A73 */
- CTI_AMBA_UCI_ID(0x000bb9da), /* CTI - C-A35 */
- CTI_AMBA_UCI_ID(0x000bb9ed), /* Coresight CTI (SoC 600) */
- CTI_AMBA_UCI_ID(0x000bb95d), /* test UCI - ETM PID A53 */
- { 0, 0},
+};
+static struct amba_driver cti_driver = {
- .drv = {
.name = "coresight-cti",
.owner = THIS_MODULE,
.suppress_bind_attrs = true,
- },
- .probe = cti_probe,
- .id_table = cti_ids,
+}; +builtin_amba_driver(cti_driver); diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h new file mode 100644 index 000000000000..09cb985ac0ea --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#ifndef _CORESIGHT_CORESIGHT_CTI_H +#define _CORESIGHT_CORESIGHT_CTI_H
+#include <asm/local.h> +#include <linux/spinlock.h> +#include "coresight-priv.h"
+/*
- Device registers
- 0x000 - 0x144: CTI programming and status
- 0xEDC - 0xEF8: CTI integration test.
- 0xF00 - 0xFFC: Coresight management registers.
- */
+/* CTI programming registers */ +#define CTICONTROL 0x000 +#define CTIINTACK 0x010 +#define CTIAPPSET 0x014 +#define CTIAPPCLEAR 0x018 +#define CTIAPPPULSE 0x01C +#define CTIINEN(n) (0x020 + (4 * n)) +#define CTIOUTEN(n) (0x0A0 + (4 * n)) +#define CTITRIGINSTATUS 0x130 +#define CTITRIGOUTSTATUS 0x134 +#define CTICHINSTATUS 0x138 +#define CTICHOUTSTATUS 0x13C +#define CTIGATE 0x140 +#define ASICCTL 0x144 +/* Integration test registers */ +#define ITCHINACK 0xEDC /* WO CTI CSSoc 400 only*/ +#define ITTRIGINACK 0xEE0 /* WO CTI CSSoc 400 only*/ +#define ITCHOUT 0xEE4 /* WO RW-600 */ +#define ITTRIGOUT 0xEE8 /* WO RW-600 */ +#define ITCHOUTACK 0xEEC /* RO CTI CSSoc 400 only*/ +#define ITTRIGOUTACK 0xEF0 /* RO CTI CSSoc 400 only*/ +#define ITCHIN 0xEF4 /* RO */ +#define ITTRIGIN 0xEF8 /* RO */ +/* management registers */ +#define CTIDEVAFF0 0xFA8 +#define CTIDEVAFF1 0xFAC
+/* CTI CSSoc 600 has a max of 32 trigger signals per direction.
- CTI CSSoc 400 has 8 IO triggers - other CTIs can be impl def.
- Max of in and out defined in the DEVID register.
- pick up actual number used from .dts parameters if present.
- */
+#define CTIINOUTEN_MAX 32
+/**
- Group of related trigger signals
- @nr_sigs: number of signals in the group.
- @used_mask: bitmask representing the signal indexes in the group.
- @sig_names: list of names for the signals.
- */
+struct cti_trig_grp {
- int nr_sigs;
- u32 used_mask;
- char *sig_names;
+};
+/**
- Trigger connection - connection between a CTI and other (coresight) device
- lists input and output trigger signals for the device
- @con_in: connected CTIIN signals for the device.
- @con_out: connected CTIOUT signals for the device.
- @con_dev: coresight device connected to the CTI, NULL if not CS device
- @con_dev_name: name of connected device (CS or CPU)
- @node: entry node in list of connections.
- */
+struct cti_trig_con {
- struct cti_trig_grp *con_in;
- struct cti_trig_grp *con_out;
- struct coresight_device *con_dev;
- char *con_dev_name;
- struct list_head node;
+};
+/**
- struct cti_device - description of CTI device properties.
- @nt_trig_con: Number of external devices connected to this device.
- @ctm_id: which CTM this device is connected to (by default it is
assumed there is a single CTM per SoC, ID 0).
- @trig_cons: list of connections to this device.
- @cpu: CPU ID if associated with CPU, -1 otherwise.
- */
+struct cti_device {
- int nr_trig_con;
- u32 ctm_id;
- struct list_head trig_cons;
- int cpu;
+};
+/**
- struct cti_config - configuration of the CTI device hardware
- hardware description from RO ID regs
- @nr_trig_max: Max number of trigger signals implemented on device.
(max of trig_in or trig_out)
- @nr_ctm_channels: number of available CTM channels
- cti enable control
- @enable_req_count: CTI is enabled alongside >=1 associated devices.
- @hw_enabled: true if hw is currently enabled.
- @hw_powered: true if associated cpu powered on, or no cpu.
- registered triggers and filtering
- @trig_in_use: bitfield of in triggers registered as in use.
- @trig_out_use: bitfield of out triggers registered as in use.
- @trig_out_filter: bitfield of out triggers that are blocked if filter
enabled. Typically this would be dbgreq / restart on a core CTI.
- @trig_filter_enable: 1 if filtering enabled.
- cti software programmable regs:
- @ctiappset: CTI Software application channel set.
- @ctiinout_sel: register selector for INEN and OUTEN regs.
- @ctiinen: enable input trigger to a channel.
- @ctiouten: enable output trigger from a channel.
- @ctigate: gate channel output from CTI to CTM.
- */
+struct cti_config {
- /* hardware description */
- int nr_ctm_channels;
- int nr_trig_max;
- /* cti enable control */
- atomic_t enable_req_count;
- bool hw_enabled;
- bool hw_powered;
- /* registered triggers and filtering */
- u32 trig_in_use;
- u32 trig_out_use;
- u32 trig_out_filter;
- int trig_filter_enable;
- /* cti cross trig programmable regs */
- u32 ctiappset;
- u8 ctiinout_sel;
- u32 ctiinen[CTIINOUTEN_MAX];
- u32 ctiouten[CTIINOUTEN_MAX];
- u32 ctigate;
- u32 asicctl;
+};
+/**
- struct cti_drvdata - specifics for the CTI device
- @base: Memory mapped base address for this component.
- @dev: The device entity associated to this component.
- @csdev: Standard CoreSight device information.
- @ctidev: Extra information needed by the CTI/CTM framework.
- @spinlock: Control data access to one at a time.
- @config: Configuration data for this CTI device.
- */
+struct cti_drvdata {
- void __iomem *base;
- struct device *dev;
- struct coresight_device *csdev;
- struct cti_device ctidev;
- spinlock_t spinlock;
- struct cti_config config;
+};
+enum cti_chan_op {
- CTI_CHAN_ATTACH,
- CTI_CHAN_DETACH,
+};
+enum cti_trig_dir {
- CTI_TRIG_IN,
- CTI_TRIG_OUT,
+};
+enum cti_chan_gate_op {
- CTI_GATE_CHAN_ENABLE,
- CTI_GATE_CHAN_DISABLE,
- CTI_GATE_CHAN_ENABLE_ALL,
- CTI_GATE_CHAN_DISABLE_ALL,
+};
+enum cti_chan_set_op {
- CTI_CHAN_SET,
- CTI_CHAN_CLR,
- CTI_CHAN_PULSE,
+};
+#endif /* _CORESIGHT_CORESIGHT_CTI_H */
2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Hi,
I'll revisit the memory leaks.
On Fri, 18 Jan 2019 at 22:17, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, Jan 09, 2019 at 10:54:38PM +0000, Mike Leach wrote:
Initial addition of the files for the Coresight CTI driver.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-cti.c | 2025 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 195 ++ 2 files changed, 2220 insertions(+) create mode 100644 drivers/hwtracing/coresight/coresight-cti.c create mode 100644 drivers/hwtracing/coresight/coresight-cti.h
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c new file mode 100644 index 000000000000..d06e19f111e2 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -0,0 +1,2025 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/smp.h> +#include <linux/sysfs.h> +#include <linux/stat.h> +#include <linux/clk.h> +#include <linux/cpu.h> +#include <linux/coresight.h> +#include <linux/coresight-pmu.h> +#include <linux/pm_wakeup.h> +#include <linux/amba/bus.h> +#include <linux/seq_file.h> +#include <linux/uaccess.h> +#include <linux/perf_event.h> +#include <linux/pm_runtime.h> +#include <linux/of.h> +#include <asm/sections.h> +#include <asm/local.h>
+#include "coresight-cti.h"
+#define csdev_to_cti_drvdata(csdev) \
dev_get_drvdata(csdev->dev.parent)
+/* CTI device tree trigger connection node keyword */ +#define CTI_DT_CONNS "trig-conns" +/* CTI device tree connection property keywords */ +#define CTI_DT_V8ARCH "arm,cti-v8-arch" +#define CTI_DT_TRIGIN_SIGS "arm,trig-in-sigs" +#define CTI_DT_TRIGOUT_SIGS "arm,trig-out-sigs" +#define CTI_DT_FILTER_OUT_SIGS "arm,trig-filters" +#define CTI_DT_CSDEV_ASSOC "arm,cs-dev-assoc" +#define CTI_DT_CTM_ID "arm,cti-ctm-id" +#define CTI_DT_CONN_NAME "arm,trig-conn-name"
+/**
- CTI devices can be associated with a PE, or be connected to CoreSight
- hardware. We have a list of all CTIs, and a subset array associated
- with individual PEs for pwr management as they will power up in the
- PE power domain.
- At this point we assume that the none CPU CTIs are always powered as
- we do with sinks etc.
- We leave the client to figure out if all the CTIs are interconnected with
- the same CTM, in general this is the case but does not always have to be.
- */
+struct ect_node {
struct cti_drvdata *cti_drv;
struct list_head next;
+};
+/* net of CTI devices connected via CTM */ +LIST_HEAD(ect_net);
+/* protect the list */ +static DEFINE_MUTEX(ect_mutex);
+/* quick reference for CPU CTIs */ +static struct cti_drvdata *cti_cpu_drv[NR_CPUS];
+/* number of registered CTI devices */ +static int cti_count;
+/* number of cpu associated CTI devices in use */ +static int cti_cpu_count;
+/* CTI / cpu hotplug notification ID */ +static enum cpuhp_state cti_hp_online;
+/* cpu powered and enabled */ +#define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered)
+/* write set of regs to hardware - call with spinlock claimed */ +static void cti_write_all_hw_regs(struct cti_drvdata *drvdata) +{
struct cti_config *config = &drvdata->config;
int i;
CS_UNLOCK(drvdata->base);
/* disable CTI before writing registers */
writel_relaxed(0, drvdata->base + CTICONTROL);
/* write the CTI trigger registers */
for (i = 0; i < config->nr_trig_max; i++) {
writel_relaxed(config->ctiinen[i], drvdata->base + CTIINEN(i));
writel_relaxed(config->ctiouten[i],
drvdata->base + CTIOUTEN(i));
}
/* other regs */
writel_relaxed(config->ctigate, drvdata->base + CTIGATE);
writel_relaxed(config->asicctl, drvdata->base + ASICCTL);
writel_relaxed(config->ctiappset, drvdata->base + CTIAPPSET);
/* re-enable CTI */
writel_relaxed(1, drvdata->base + CTICONTROL);
CS_LOCK(drvdata->base);
+}
+static void cti_enable_hw_smp_call(void *info) +{
struct cti_drvdata *drvdata = info;
cti_write_all_hw_regs(drvdata);
+}
+/* write regs to hardware and enable */ +static int cti_enable_hw(void *info) +{
struct cti_drvdata *drvdata = info;
struct cti_config *config = &drvdata->config;
int rc = 0;
pm_runtime_get_sync(drvdata->dev);
spin_lock(&drvdata->spinlock);
/* no need to do anything if enabled or unpowered*/
if (config->hw_enabled || !config->hw_powered) {
spin_unlock(&drvdata->spinlock);
pm_runtime_put(drvdata->dev);
return 0;
}
if (drvdata->ctidev.cpu >= 0) {
dev_info(drvdata->dev, "cti enable smp call for cpu %d\n",
drvdata->ctidev.cpu);
rc = smp_call_function_single(drvdata->ctidev.cpu,
cti_enable_hw_smp_call,
drvdata, 1);
} else {
dev_info(drvdata->dev, "cti enable not cpu call\n");
cti_write_all_hw_regs(drvdata);
}
if (!rc)
config->hw_enabled = true;
spin_unlock(&drvdata->spinlock);
return 0;
+}
+/* disable hardware */ +static int cti_disable_hw(void *info) +{
struct cti_drvdata *drvdata = info;
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
/* no need to do anything if disabled or cpu unpowered*/
if (!config->hw_enabled || !config->hw_powered) {
spin_unlock(&drvdata->spinlock);
return 0;
}
CS_UNLOCK(drvdata->base);
/* disable CTI */
writel_relaxed(0, drvdata->base + CTICONTROL);
config->hw_enabled = false;
CS_LOCK(drvdata->base);
spin_unlock(&drvdata->spinlock);
pm_runtime_put(drvdata->dev);
return 0;
+}
+static void +cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value) +{
CS_UNLOCK(drvdata->base);
writel_relaxed(value, drvdata->base+offset);
CS_LOCK(drvdata->base);
+}
+static void cti_set_default_config(struct cti_config *config) +{
/* Most regs default to 0 as zalloc'ed except...*/
config->trig_filter_enable = 1;
config->ctigate = ((u32)0x1 << config->nr_ctm_channels) - 1;
atomic_set(&config->enable_req_count, 0);
+}
+static void cti_write_intack(struct device *dev, u32 ackval) +{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
/* write if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIINTACK, ackval);
spin_unlock(&drvdata->spinlock);
+}
+/* need to update cross references once CTI csdev has been generated. */ +static void cti_update_conn_xrefs(struct cti_drvdata *drvdata) +{
struct cti_trig_con *tc;
struct cti_device *ctidev = &drvdata->ctidev;
list_for_each_entry(tc, &ctidev->trig_cons, node) {
if (tc->con_dev) {
tc->con_dev->ect_dev = drvdata->csdev;
dev_info(drvdata->dev,
"Setting assoc csdev (%px) with (%px)\n",
tc->con_dev, drvdata->csdev);
}
}
+}
+/* connection information */ +static int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name)
+{
struct cti_trig_con *tc;
struct cti_device *cti_dev = &drvdata->ctidev;
tc = kzalloc(sizeof(struct cti_trig_con), GFP_KERNEL);
if (tc == 0)
return -ENOMEM;
tc->con_dev = csdev;
/* prefer actual associated CS device dev name to supplied value -
* which is likely to be node name / other conn name
*/
if (csdev)
tc->con_dev_name = kstrdup(dev_name(&csdev->dev), GFP_KERNEL);
else if (assoc_dev_name != NULL)
tc->con_dev_name = kstrdup(assoc_dev_name, GFP_KERNEL);
tc->con_in = in_info;
tc->con_out = out_info;
list_add_tail(&tc->node, &cti_dev->trig_cons);
cti_dev->nr_trig_con++;
/* add connection usage bit info to overall info */
drvdata->config.trig_in_use |= in_info->used_mask;
drvdata->config.trig_out_use |= out_info->used_mask;
The call graph for this is:
cti_probe() of_cti_get_hw_data() cti_add_default_connection() cti_add_connection_entry()
What happens to the memory allocted here if something goes wrong in cti_probe()? Instead of kzalloc() use devm_kzalloc() with argument drvdata->dev, which ends up being the same as the call done to allocate drvdata in cti_probe. On the flip side it doesn't take care of freeing memory allocated by kstrdup().
return 0;
+}
+/* add a default connection if nothing else is specified.
- single connection based on max in/out info, no assoc device
- */
+static int cti_add_default_connection(struct cti_drvdata *drvdata) +{
struct cti_trig_grp *in, *out;
int n_trigs = drvdata->config.nr_trig_max;
u32 n_trig_mask = (0x1 << n_trigs) - 1;
in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!in)
return -ENOMEM;
out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!out)
return -ENOMEM;
Memory allocated for 'in' is leaked if we have an error.
dev_info(drvdata->dev, "default connection: trigs %d, trig_mask %08x\n",
n_trigs, n_trig_mask);
/* assume max trigs for in and out, all used */
in->nr_sigs = n_trigs;
in->used_mask = n_trig_mask;
in->sig_names = NULL;
out->nr_sigs = n_trigs;
out->used_mask = n_trig_mask;
out->sig_names = NULL;
return cti_add_connection_entry(drvdata, in, out, NULL, "default");
Same as above - memory for 'in' and 'out' is leaked if cti_add_connection_entry() fails.
+}
+/* create an architecturally defined v8 connection
- must hava a cpu, can have an ETM
- */
Multiline format problem.
+static int of_cti_create_v8_connections(struct cti_drvdata *drvdata,
struct device_node *np)
Indentation problems with the second line. Both 's' from the "struct" should be aligned.
+{
struct cti_device *cti_dev = &drvdata->ctidev;
struct cti_trig_grp *in, *out;
int cpuid = 0;
struct device_node *cs_np;
struct coresight_device *csdev = NULL;
char cpu_name_str[16];
const char *assoc_name = NULL;
cpuid = of_coresight_ect_get_cpu(np);
if (cpuid < 0) {
dev_warn(drvdata->dev, "CTI v8 DT binding no cpu\n");
return -EINVAL;
}
cti_dev->cpu = cpuid;
/* do the v8 cpu connection */
in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!in)
return -ENOMEM;
out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!out)
return -ENOMEM;
/* v8 PE CTI config... */
The rule of thumb is to have a comment per hard-code value. Here it would be nice to have the reference and section number of where you got this configuration from - something like:
OK
/* * Setting up CPU in out trigger group in accordance with document XYZ, * section 123. */
in->nr_sigs = 3;
in->used_mask = 0x7;
in->sig_names = NULL;
out->nr_sigs = 3;
out->used_mask = 0x7;
out->sig_names = NULL;
scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
cti_add_connection_entry(drvdata, in, out, NULL, cpu_name_str);
drvdata->config.trig_out_filter = 0x1; /* filter dbgreq */
/* v8 ETM associated config... */
cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
if (cs_np) {
Does it make sense to have a CTI connected to a CPU but not have an ETM associated with that CPU? Shouldn't this be treated as an error?
No. The ETM is optional, the CTI is not.
Alongside the halting debug signals - which are of limited use in this context - there are two other relevant signals. The CPU has a PMU overflow out signal connected to a trigin on the CTI, and there is a trigout assigned as a generic CTI IRQ - though the connection of this IRQ in any system is implementation defined - recommended attached to GIC or other interrupt controller in the system.
in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!in)
return -ENOMEM;
out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!out)
return -ENOMEM;
in->nr_sigs = 4;
in->used_mask = 0xF0;
in->sig_names = NULL;
out->nr_sigs = 4;
out->used_mask = 0xF0;
out->sig_names = NULL;
dev_info(drvdata->dev, "v8 finding assoc dev %s\n",
cs_np->full_name);
csdev = of_coresight_get_cs_device_by_node(cs_np);
if (csdev)
assoc_name = dev_name(&csdev->dev);
else
assoc_name = cs_np->full_name;
I'm guessing you're doing the above to deal with device discovery ordering. If I am correct please add a comment that makes it clear.
cti_add_connection_entry(drvdata, in, out, csdev, assoc_name);
of_node_put(cs_np);
}
return 0;
Same comment about memory release in case of something goes wrong. I've also taken the habit of adding a comment that mentions where the memory will be be released for each allocation I do. That helps me remember that I have to release the memory and allows other people to better understand how things work.
Good tip.
+}
+static int of_cti_read_trig_group(struct cti_trig_grp **trig_grp_ptr,
struct device_node *np,
const char *grp_name,
int max_trigs)
+{
int items, err = 0;
u32 value, pidx;
struct cti_trig_grp *grp;
grp = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!grp)
return -ENOMEM;
*trig_grp_ptr = grp;
items = of_property_count_elems_of_size(np, grp_name, 4);
From the documentation and the DT bindings you've provided it is pretty easy to figure out why you have a 4 there but I will still need a comment to explain it. But have you explained anywhere in the documentation why trigger groups have 4 pins? Is it just that the HW only provides a maximum of 4 in and 4 out for each device? If so, it would be a good thing to write it somewhere.
The 4 here is the size of the individual element in the property - not the number of them. My understanding is that these will be 32 bit values - hence 4 bytes.
/* if the property doesn't exist or has no values, then return
* an empty connection group
*/
if (items < 0)
return 0;
if (items > max_trigs)
return -EINVAL;
/* set the number of signals and usage mask */
for (pidx = 0; pidx < items; pidx++) {
err = of_property_read_u32_index(np, grp_name, pidx, &value);
if (err)
return err;
Memory leak of 'grp'.
Possibly not here - grp is passed out of the function if successfully allocated - and should be cleaned up when the connection is cleaned up. Though this needs a re-check.
grp->nr_sigs++;
grp->used_mask |= (0x1 << value);
}
return 0;
+}
+static int of_cti_create_connection(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
struct cti_trig_grp *in = 0, *out = 0, *filter = 0;
int cpuid = -1, err = 0, trig_max = drvdata->config.nr_trig_max;
struct device_node *cs_np;
struct coresight_device *csdev = NULL;
const char *assoc_name = "unknown";
char cpu_name_str[16];
/* look for the signals properties. */
err = of_cti_read_trig_group(&in, np, CTI_DT_TRIGIN_SIGS, trig_max);
if (err)
return err;
err = of_cti_read_trig_group(&out, np, CTI_DT_TRIGOUT_SIGS, trig_max);
if (err)
return err;
err = of_cti_read_trig_group(&filter, np, CTI_DT_FILTER_OUT_SIGS,
trig_max);
if (err)
return err;
Whatch out for memory leaks in the above.
/* read the connection name if set - may be overridden by later */
of_property_read_string(np, CTI_DT_CONN_NAME, &assoc_name);
/* associated cpu ? */
cpuid = of_coresight_ect_get_cpu(np);
drvdata->ctidev.cpu = cpuid;
if (cpuid >= 0) {
scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
assoc_name = cpu_name_str;
} else {
/* associated device ? */
cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
if (cs_np) {
dev_info(dev, "finding assoc dev %s\n",
cs_np->full_name);
csdev = of_coresight_get_cs_device_by_node(cs_np);
if (csdev) /* use device name if csdev found */
assoc_name = dev_name(&csdev->dev);
else /* otherwise node name for later association */
assoc_name = cs_np->full_name;
of_node_put(cs_np);
}
}
/* set up a connection */
err = cti_add_connection_entry(drvdata, in, out, csdev, assoc_name);
/* note any filter info */
if (!err)
drvdata->config.trig_out_filter |= filter->used_mask;
kfree(filter);
return err;
+}
After reviewing functions of_cti_get_hw_data(), of_cti_create_v8_connections() and of_cti_create_connection() I wonder if they shouldn't be added to a new of-coresight-cti.c file, along with the rest of the OF centric functions needed by the driver.
More to come on Monday.
Mathieu
Thanks for the review
Mike
+/* get the hardware configuration & connection data. */ +static int of_cti_get_hw_data(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
int rc = 0;
struct cti_device *cti_dev = &drvdata->ctidev;
struct device_node *nc = NULL;
/* get any CTM ID - defaults to 0 */
of_property_read_u32(np, CTI_DT_CTM_ID, &cti_dev->ctm_id);
if (of_property_read_bool(np, CTI_DT_V8ARCH)) {
rc = of_cti_create_v8_connections(drvdata, np);
} else {
for_each_child_of_node(np, nc) {
if (of_node_cmp(nc->name, CTI_DT_CONNS) != 0)
continue;
dev_info(dev, "dt found node: %s %s\n",
nc->name, nc->full_name);
rc = of_cti_create_connection(dev, nc, drvdata);
if (rc != 0)
return rc;
}
}
/* if no connections, just add a single default based on max IN-OUT */
if (cti_dev->nr_trig_con == 0)
rc = cti_add_default_connection(drvdata);
return rc;
+}
+static int +cti_match_con_name(struct cti_device *ctidev, const char *node_name,
const char *csdev_name)
+{
int found = 0;
struct cti_trig_con *trig_con;
list_for_each_entry(trig_con, &ctidev->trig_cons, node) {
if (trig_con->con_dev_name) {
if (!strcmp(node_name, trig_con->con_dev_name)) {
found = 1;
/* match: so swap in csdev name */
kfree(trig_con->con_dev_name);
trig_con->con_dev_name =
kstrdup(csdev_name, GFP_KERNEL);
goto cti_con_name_match;
}
}
}
+cti_con_name_match:
return found;
+}
+/* search the cti list to add an associated CTI into the supplied CS device
- This will set the association if CTI declared before the CS device
- */
+int cti_add_assoc_to_csdev(struct coresight_device *csdev) +{
struct ect_node *ect_item;
struct cti_device *ctidev;
const char *node_name = NULL, *csdev_name;
/* exit early for no CTIs or self referencing ECT devices.*/
if (!cti_count) {
dev_info(&csdev->dev, "no CTI to check\n");
return 0;
}
if (!csdev->dev.parent->of_node) {
dev_info(&csdev->dev, "No parent of_node pointer\n");
return 0;
}
node_name = csdev->dev.parent->of_node->full_name;
if (!node_name) {
dev_info(&csdev->dev, "%s - bad node name\n", __func__);
return 0;
}
dev_info(&csdev->dev, "%s - looking for %s\n", __func__, node_name);
if (!cti_count || (csdev->type == CORESIGHT_DEV_TYPE_ECT)) {
dev_info(&csdev->dev, "no CTI or skip self ref CTI\n");
return 0;
}
csdev_name = dev_name(&csdev->dev);
/* for each CTI in list... */
mutex_lock(&ect_mutex);
list_for_each_entry(ect_item, &ect_net, next) {
ctidev = &ect_item->cti_drv->ctidev;
if (cti_match_con_name(ctidev, node_name, csdev_name)) {
csdev->ect_dev = ect_item->cti_drv->csdev;
dev_info(&csdev->dev, "Found!\n");
goto cti_found;
}
}
+cti_found:
mutex_unlock(&ect_mutex);
return 0;
+} +EXPORT_SYMBOL_GPL(cti_add_assoc_to_csdev);
+/* attach/detach channel from trigger - write through if enabled. */ +static int cti_channel_trig_op(struct device *dev,
enum cti_chan_op op,
enum cti_trig_dir direction,
u32 channel_idx,
u32 trigger_idx)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
u32 trig_bitmask;
u32 chan_bitmask;
u32 reg_value;
int reg_offset;
dev_info(dev, "chan_trig_op: op %d, dir %d, chan %d, trig %d\n",
op, direction, channel_idx, trigger_idx);
/* ensure indexes in range */
if ((channel_idx >= config->nr_ctm_channels) ||
(trigger_idx >= config->nr_trig_max))
return -EINVAL;
trig_bitmask = 0x1 << trigger_idx;
/* ensure registered triggers and not out filtered */
if (direction == CTI_TRIG_IN) {
if (!(trig_bitmask & config->trig_in_use))
return -EINVAL;
} else {
if (!(trig_bitmask & config->trig_out_use))
return -EINVAL;
if ((config->trig_filter_enable) &&
(config->trig_out_filter & trig_bitmask))
return -EINVAL;
}
/* update the local register values */
chan_bitmask = 0x1 << channel_idx;
reg_offset = (direction == CTI_TRIG_IN ? CTIINEN(trigger_idx) :
CTIOUTEN(trigger_idx));
spin_lock(&drvdata->spinlock);
/* read - modify write - the trigger / channel enable value */
reg_value = (direction == CTI_TRIG_IN ? config->ctiinen[trigger_idx] :
config->ctiouten[trigger_idx]);
reg_value = (op == CTI_CHAN_ATTACH) ? reg_value | chan_bitmask :
reg_value & (~chan_bitmask);
/* write local copy */
if (direction == CTI_TRIG_IN)
config->ctiinen[trigger_idx] = reg_value;
else
config->ctiouten[trigger_idx] = reg_value;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
spin_unlock(&drvdata->spinlock);
return 0;
+}
+static int cti_channel_gate_op(struct device *dev,
enum cti_chan_gate_op op,
u32 channel_idx)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
u32 chan_bitmask;
u32 reg_value;
int err = 0;
if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
chan_bitmask = 0x1 << channel_idx;
spin_lock(&drvdata->spinlock);
reg_value = config->ctigate;
switch (op) {
case CTI_GATE_CHAN_ENABLE:
reg_value |= chan_bitmask;
break;
case CTI_GATE_CHAN_DISABLE:
reg_value &= ~chan_bitmask;
break;
case CTI_GATE_CHAN_ENABLE_ALL:
reg_value = (0x1 << config->nr_ctm_channels) - 1;
break;
case CTI_GATE_CHAN_DISABLE_ALL:
reg_value = 0x0;
break;
default:
err = -EINVAL;
break;
}
if (err == 0) {
config->ctigate = reg_value;
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIGATE, reg_value);
}
spin_unlock(&drvdata->spinlock);
return err;
+}
+static int cti_channel_setop(struct device *dev,
enum cti_chan_set_op op,
u32 channel_idx)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
u32 chan_bitmask;
u32 reg_value;
u32 reg_offset;
int err = 0;
if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
chan_bitmask = 0x1 << channel_idx;
spin_lock(&drvdata->spinlock);
reg_value = config->ctiappset;
switch (op) {
case CTI_CHAN_SET:
config->ctiappset |= chan_bitmask;
reg_value = config->ctiappset;
reg_offset = CTIAPPSET;
break;
case CTI_CHAN_CLR:
config->ctiappset &= ~chan_bitmask;
reg_value = chan_bitmask;
reg_offset = CTIAPPCLEAR;
break;
case CTI_CHAN_PULSE:
config->ctiappset &= ~chan_bitmask;
reg_value = chan_bitmask;
reg_offset = CTIAPPPULSE;
break;
default:
err = -EINVAL;
break;
}
if ((err == 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
spin_unlock(&drvdata->spinlock);
return err;
+}
+/** cti ect operations **/ +static int cti_enable(struct coresight_device *csdev, void *__unused) +{
int rc;
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
atomic_inc(&drvdata->config.enable_req_count);
rc = cti_enable_hw(drvdata);
if (rc)
atomic_dec(&drvdata->config.enable_req_count);
return rc;
+}
+static int cti_disable(struct coresight_device *csdev, void *__unused) +{
int rc = 0;
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
if (!atomic_dec_return(&drvdata->config.enable_req_count)) {
rc = cti_disable_hw(drvdata);
if (rc)
atomic_inc(&drvdata->config.enable_req_count);
}
return rc;
+}
+const struct coresight_ops_ect cti_ops_ect = {
.enable = cti_enable,
.disable = cti_disable,
+};
+const struct coresight_ops cti_ops = {
.ect_ops = &cti_ops_ect,
+};
+/** attributes **/
+#define coresight_cti_reg(name, offset) \
coresight_simple_reg32(struct cti_drvdata, name, offset)
+/* show a simple 32 bit value. if pval is NULL then live read,
- otherwise read from supplied pointer only
- */
+static ssize_t cti_reg32_show(struct device *dev, char *buf,
u32 *pval, int reg_offset)
+{
unsigned long val = 0;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
if (pval) {
val = (unsigned long)*pval;
} else if ((reg_offset >= 0) && CTI_PWR_ENA(config)) {
CS_UNLOCK(drvdata->base);
val = readl_relaxed(drvdata->base + reg_offset);
CS_LOCK(drvdata->base);
}
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+/* store a simple 32 bit value.
- if pval not NULL, then copy to here too,
- if reg_offset >= 0 then write through if enabled.
- */
+static ssize_t cti_reg32_store(struct device *dev, const char *buf,
size_t size, u32 *pval,
int reg_offset)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
/* local store */
if (pval)
*pval = (u32)val;
/* write through of offset and enabled */
if ((reg_offset >= 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, val);
spin_unlock(&drvdata->spinlock);
return size;
+}
+/* basic attributes */ +static ssize_t enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
int enable_req;
bool enabled, powered, cpuid;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
ssize_t size = 0;
enable_req = atomic_read(&drvdata->config.enable_req_count);
spin_lock(&drvdata->spinlock);
powered = drvdata->config.hw_powered;
enabled = drvdata->config.hw_enabled;
cpuid = drvdata->ctidev.cpu;
spin_unlock(&drvdata->spinlock);
if (powered) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled", cpuid);
} else if (cpuid >= 0) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req", cpuid);
} else {
size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n",
enabled ? "enabled" : "disabled");
}
return size;
+}
+static ssize_t enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int ret = 0;
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
ret = kstrtoul(buf, 16, &val);
if (ret)
return ret;
if (val)
ret = cti_enable(drvdata->csdev, NULL);
else
ret = cti_disable(drvdata->csdev, NULL);
if (ret)
return ret;
return size;
+} +static DEVICE_ATTR_RW(enable);
+static ssize_t ctmid_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return scnprintf(buf, PAGE_SIZE, "0x%x\n", drvdata->ctidev.ctm_id);
+} +static DEVICE_ATTR_RO(ctmid);
+#define LF_ADJUST_BUF_PTRS(n) \
do { buf_pos += n; buf_size -= n; \
if (buf_size <= 1) \
goto lf_buf_full; \
} while (0)
+static ssize_t list_features_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
int used = 0, sig_idx, con_idx, nr_trig_max;
/* buffer vars */
int buf_size = PAGE_SIZE;
char *buf_pos = buf;
char *con_name;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *cfg = &drvdata->config;
struct cti_device *ctidev = &drvdata->ctidev;
u32 sig_mask;
struct cti_trig_con *con;
/* basic feature info */
used = scnprintf(buf_pos, buf_size,
"CTI:%s; Channels:%d; Max Trigs:%d\n",
dev_name(dev), cfg->nr_ctm_channels, cfg->nr_trig_max);
LF_ADJUST_BUF_PTRS(used);
used = scnprintf(buf_pos, buf_size, "CTM id = %d; Num Conns = %d\n",
ctidev->ctm_id, ctidev->nr_trig_con);
LF_ADJUST_BUF_PTRS(used);
/* list each connection info */
con_idx = 0;
nr_trig_max = cfg->nr_trig_max;
list_for_each_entry(con, &ctidev->trig_cons, node) {
/* connection name */
if (con->con_dev_name != NULL)
con_name = con->con_dev_name;
else
con_name = "unknown";
used = scnprintf(buf_pos, buf_size, "conn(%d:%s)\n",
con_idx, con_name);
LF_ADJUST_BUF_PTRS(used);
con_idx++;
/* connection signals */
used = scnprintf(buf_pos, buf_size, "Trig IN(%d) [ ",
con->con_in->nr_sigs);
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & con->con_in->used_mask) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
used = scnprintf(buf_pos, buf_size, "Trig OUT(%d) [ ",
con->con_out->nr_sigs);
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & con->con_out->used_mask) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
}
if (cfg->trig_out_filter) {
used = scnprintf(buf_pos, buf_size, "Trig OUT_FILTERED [ ");
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & cfg->trig_out_filter) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
}
+lf_buf_full:
used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
return used;
+} +static DEVICE_ATTR_RO(list_features);
+static struct attribute *coresight_cti_attrs[] = {
&dev_attr_enable.attr,
&dev_attr_list_features.attr,
&dev_attr_ctmid.attr,
NULL,
+};
+/* raw register attributes */ +static ssize_t inout_sel_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
u32 val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
val = (u32)drvdata->config.ctiinout_sel;
return scnprintf(buf, PAGE_SIZE, "%#x\n", val);
+}
+static ssize_t inout_sel_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
if (kstrtoul(buf, 16, &val))
return -EINVAL;
if (val > (CTIINOUTEN_MAX-1))
return -EINVAL;
spin_lock(&drvdata->spinlock);
drvdata->config.ctiinout_sel = val;
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(inout_sel);
+static ssize_t inen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
spin_lock(&drvdata->spinlock);
index = drvdata->config.ctiinout_sel;
val = drvdata->config.ctiinen[index];
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "INEN%d %#lx\n", index, val);
+}
+static ssize_t inen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
index = config->ctiinout_sel;
config->ctiinen[index] = val;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIINEN(index), val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(inen);
+static ssize_t outen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
spin_lock(&drvdata->spinlock);
index = drvdata->config.ctiinout_sel;
val = drvdata->config.ctiouten[index];
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "OUTEN%d %#lx\n", index, val);
+}
+static ssize_t outen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
index = config->ctiinout_sel;
config->ctiouten[index] = val;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIOUTEN(index), val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(outen);
+static ssize_t gate_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_show(dev, buf, &drvdata->config.ctigate, -1);
+}
+static ssize_t gate_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_store(dev, buf, size,
&drvdata->config.ctigate, CTIGATE);
+} +static DEVICE_ATTR_RW(gate);
+static ssize_t asicctl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_show(dev, buf, &drvdata->config.asicctl, -1);
+}
+static ssize_t asicctl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_store(dev, buf, size,
&drvdata->config.asicctl, ASICCTL);
+} +static DEVICE_ATTR_RW(asicctl);
+static ssize_t intack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
cti_write_intack(dev, val);
return size;
+} +static DEVICE_ATTR_WO(intack);
+static ssize_t appset_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_show(dev, buf, &drvdata->config.ctiappset, -1);
+}
+static ssize_t appset_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_store(dev, buf, size,
&drvdata->config.ctiappset, CTIAPPSET);
+} +static DEVICE_ATTR_RW(appset);
+static ssize_t appclear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val, mask;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
/* a 1'b1 in appclr clears down the same bit in appset*/
mask = ~val;
config->ctiappset &= mask;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPCLEAR, val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_WO(appclear);
+static ssize_t apppulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val, mask;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
/* a 1'b1 in apppulse sets then clears the bit,
* effectively clears down the same bit in appset
*/
mask = ~val;
config->ctiappset &= mask;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPPULSE, val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_WO(apppulse);
+static ssize_t itchout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return cti_reg32_show(dev, buf, NULL, ITCHOUT);
+}
+static ssize_t itchout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITCHOUT);
+} +static DEVICE_ATTR_RW(itchout);
+static ssize_t ittrigout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return cti_reg32_show(dev, buf, NULL, ITTRIGOUT);
+}
+static ssize_t ittrigout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITTRIGOUT);
+} +static DEVICE_ATTR_RW(ittrigout);
+static ssize_t itchinack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITCHINACK);
+} +static DEVICE_ATTR_WO(itchinack);
+static ssize_t ittriginack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITTRIGINACK);
+} +static DEVICE_ATTR_WO(ittriginack);
+static ssize_t itctrl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return cti_reg32_show(dev, buf, NULL, CORESIGHT_ITCTRL);
+}
+static ssize_t itctrl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, CORESIGHT_ITCTRL);
+} +static DEVICE_ATTR_RW(itctrl);
+coresight_cti_reg(triginstatus, CTITRIGINSTATUS); +coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS); +coresight_cti_reg(chinstatus, CTICHINSTATUS); +coresight_cti_reg(choutstatus, CTICHOUTSTATUS); +coresight_cti_reg(ittrigin, ITTRIGIN); +coresight_cti_reg(itchin, ITCHIN); +coresight_cti_reg(itchoutack, ITCHOUTACK); +coresight_cti_reg(ittrigoutack, ITTRIGOUTACK);
+static struct attribute *coresight_cti_regs_attrs[] = {
&dev_attr_inout_sel.attr,
&dev_attr_inen.attr,
&dev_attr_outen.attr,
&dev_attr_gate.attr,
&dev_attr_asicctl.attr,
&dev_attr_intack.attr,
&dev_attr_appset.attr,
&dev_attr_appclear.attr,
&dev_attr_apppulse.attr,
&dev_attr_triginstatus.attr,
&dev_attr_trigoutstatus.attr,
&dev_attr_chinstatus.attr,
&dev_attr_choutstatus.attr,
&dev_attr_itctrl.attr,
&dev_attr_ittrigin.attr,
&dev_attr_itchin.attr,
&dev_attr_ittrigout.attr,
&dev_attr_itchout.attr,
&dev_attr_itchoutack.attr,
&dev_attr_ittrigoutack.attr,
&dev_attr_ittriginack.attr,
&dev_attr_itchinack.attr,
NULL,
+};
+/* channel / trigger api */ +static int +cti_trig_op_parse(struct device *dev, enum cti_chan_op op,
enum cti_trig_dir dir, const char *buf, size_t size)
+{
u32 chan_idx;
u32 trig_idx;
int items, err = size;
/* extract chan idx and trigger idx */
items = sscanf(buf, "%d %d", &chan_idx, &trig_idx);
if (items) {
err = cti_channel_trig_op(dev, op, dir, chan_idx, trig_idx);
if (!err)
err = size;
} else
err = -EINVAL;
return err;
+}
+static ssize_t trigin_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_attach);
+static ssize_t trigin_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_detach);
+static ssize_t trigout_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_attach);
+static ssize_t trigout_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_detach);
+static ssize_t gate_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = 0, channel = 0, items;
if (strcmp(buf, "all") == 0) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE_ALL, 0);
} else {
items = sscanf(buf, "%d", &channel);
if (!items)
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE, 0);
}
return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_enable);
+static ssize_t gate_disable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = 0, channel = 0, items;
if (strcmp(buf, "all") == 0) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE_ALL, 0);
} else {
items = sscanf(buf, "%d", &channel);
if (!items)
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE, 0);
}
return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_disable);
+static int +chan_op_parse(struct device *dev, enum cti_chan_set_op op, const char *buf) +{
int err = 0, channel = 0, items;
items = sscanf(buf, "%d", &channel);
if (!items)
return -EINVAL;
err = cti_channel_setop(dev, op, channel);
return err;
+}
+static ssize_t chan_set_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = chan_op_parse(dev, CTI_CHAN_SET, buf);
return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_set);
+static ssize_t chan_clear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = chan_op_parse(dev, CTI_CHAN_CLR, buf);
return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_clear);
+static ssize_t chan_pulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = chan_op_parse(dev, CTI_CHAN_PULSE, buf);
return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_pulse);
+static ssize_t trig_filter_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
spin_lock(&drvdata->spinlock);
val = drvdata->config.trig_filter_enable;
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "%ld (%s)\n", val,
val ? "enabled" : "disabled");
+}
+static ssize_t trig_filter_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
drvdata->config.trig_filter_enable = val ? 1 : 0;
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(trig_filter_enable);
+/* clear all xtrigger / channel programming */ +static ssize_t reset_xtrigs_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int i;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
/* clear the CTI trigger / channel programming registers */
for (i = 0; i < config->nr_trig_max; i++) {
config->ctiinen[i] = 0;
config->ctiouten[i] = 0;
}
/* clear the other regs */
config->ctigate = (0x1 << config->nr_ctm_channels) - 1;
config->asicctl = 0;
config->ctiappset = 0;
config->ctiinout_sel = 0;
/* if enabled then write through */
if (CTI_PWR_ENA(config))
cti_write_all_hw_regs(drvdata);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_WO(reset_xtrigs);
+/* list attachments by channel */ +#define LX_ADJUST_BUF_PTRS(n) \
do { buf_pos += n; buf_size -= n; \
if (buf_size <= 1) \
goto lx_buf_full; \
} while (0)
+static ssize_t list_xtrigs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
int used = 0, nr_channels, chan_idx, reg_idx;
/* buffer vars */
int buf_size = PAGE_SIZE;
char *buf_pos = buf;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *cfg = &drvdata->config;
u32 chan_mask;
nr_channels = cfg->nr_ctm_channels;
used = scnprintf(buf_pos, buf_size, "CTI:%s; Channels:%d\n",
dev_name(dev), nr_channels);
LX_ADJUST_BUF_PTRS(used);
/* show the IN and OUT triggers per channel */
for (chan_idx = 0; chan_idx < nr_channels; chan_idx++) {
chan_mask = 0x1 << chan_idx;
used = scnprintf(buf_pos, buf_size, "Chan %d: IN [ ", chan_idx);
LX_ADJUST_BUF_PTRS(used);
for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiinen[reg_idx]) {
used = scnprintf(buf_pos, buf_size, "%d ",
reg_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]; OUT [ ");
LX_ADJUST_BUF_PTRS(used);
for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiouten[reg_idx]) {
used = scnprintf(buf_pos, buf_size, "%d ",
reg_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "];\n");
LX_ADJUST_BUF_PTRS(used);
}
/* show the channels enabled via ctigate */
used = scnprintf(buf_pos, buf_size, "Gate Channels Enabled: [ ");
LX_ADJUST_BUF_PTRS(used);
if (cfg->ctigate == 0) {
used = scnprintf(buf_pos, buf_size, "None ]\n");
LX_ADJUST_BUF_PTRS(used);
} else {
for (chan_idx = 0; chan_idx < nr_channels; chan_idx++) {
chan_mask = 0x1 << chan_idx;
if (chan_mask & cfg->ctigate) {
used = scnprintf(buf_pos, buf_size, "%d ",
chan_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LX_ADJUST_BUF_PTRS(used);
}
+lx_buf_full:
used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
return used;
+} +static DEVICE_ATTR_RO(list_xtrigs);
+#define LCIU_ADJUST_BUF_PTRS(n) \
do { buf_pos += n; buf_size -= n; \
if (buf_size <= 1) \
goto lciu_buf_full; \
} while (0)
+static ssize_t list_chan_inuse_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
int used = 0, nr_channels, i;
/* buffer vars */
int buf_size = PAGE_SIZE;
char *buf_pos = buf;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
u32 inuse_bits = 0, chan_mask, chan_bit_mask;
nr_channels = config->nr_ctm_channels;
spin_lock(&drvdata->spinlock);
for (i = 0; i < config->nr_trig_max; i++) {
inuse_bits |= config->ctiinen[i];
inuse_bits |= config->ctiouten[i];
}
spin_unlock(&drvdata->spinlock);
/* print in use channels */
chan_mask = ((u32)(0x1 << nr_channels)) - 1;
if (inuse_bits & chan_mask) {
used = scnprintf(buf_pos, buf_size, "Chan in use [ ");
LCIU_ADJUST_BUF_PTRS(used);
for (i = 0; i < nr_channels; i++) {
chan_bit_mask = 0x1 << i;
if (chan_bit_mask & inuse_bits) {
used = scnprintf(buf_pos, buf_size, "%d ", i);
LCIU_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LCIU_ADJUST_BUF_PTRS(used);
} else {
used = scnprintf(buf_pos, buf_size, "Chan in use [ none ]\n");
LCIU_ADJUST_BUF_PTRS(used);
}
/* print free channels */
inuse_bits = ~inuse_bits; /* flip for free channels */
if (inuse_bits & chan_mask) {
used = scnprintf(buf_pos, buf_size, "Chan free [ ");
LCIU_ADJUST_BUF_PTRS(used);
for (i = 0; i < nr_channels; i++) {
chan_bit_mask = 0x1 << i;
if (chan_bit_mask & inuse_bits) {
used = scnprintf(buf_pos, buf_size, "%d ", i);
LCIU_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LCIU_ADJUST_BUF_PTRS(used);
} else {
used = scnprintf(buf_pos, buf_size, "Chan free [ none ]\n");
LCIU_ADJUST_BUF_PTRS(used);
}
+lciu_buf_full:
used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
return used;
+} +static DEVICE_ATTR_RO(list_chan_inuse);
+static struct attribute *coresight_cti_channel_attrs[] = {
&dev_attr_trigin_attach.attr,
&dev_attr_trigin_detach.attr,
&dev_attr_trigout_attach.attr,
&dev_attr_trigout_detach.attr,
&dev_attr_gate_enable.attr,
&dev_attr_gate_disable.attr,
&dev_attr_chan_set.attr,
&dev_attr_chan_clear.attr,
&dev_attr_chan_pulse.attr,
&dev_attr_trig_filter_enable.attr,
&dev_attr_list_xtrigs.attr,
&dev_attr_reset_xtrigs.attr,
&dev_attr_list_chan_inuse.attr,
NULL,
+};
+/* coresight management registers */ +coresight_cti_reg(devaff0, CTIDEVAFF0); +coresight_cti_reg(devaff1, CTIDEVAFF1); +coresight_cti_reg(authstatus, CORESIGHT_AUTHSTATUS); +coresight_cti_reg(devarch, CORESIGHT_DEVARCH); +coresight_cti_reg(devid, CORESIGHT_DEVID); +coresight_cti_reg(devtype, CORESIGHT_DEVTYPE); +coresight_cti_reg(pidr0, CORESIGHT_PERIPHIDR0); +coresight_cti_reg(pidr1, CORESIGHT_PERIPHIDR1); +coresight_cti_reg(pidr2, CORESIGHT_PERIPHIDR2); +coresight_cti_reg(pidr3, CORESIGHT_PERIPHIDR3); +coresight_cti_reg(pidr4, CORESIGHT_PERIPHIDR4);
+static struct attribute *coresight_cti_mgmt_attrs[] = {
&dev_attr_devaff0.attr,
&dev_attr_devaff1.attr,
&dev_attr_authstatus.attr,
&dev_attr_devarch.attr,
&dev_attr_devid.attr,
&dev_attr_devtype.attr,
&dev_attr_pidr0.attr,
&dev_attr_pidr1.attr,
&dev_attr_pidr2.attr,
&dev_attr_pidr3.attr,
&dev_attr_pidr4.attr,
NULL,
+};
+static const struct attribute_group coresight_cti_group = {
.attrs = coresight_cti_attrs,
+};
+static const struct attribute_group coresight_cti_regs_group = {
.attrs = coresight_cti_regs_attrs,
.name = "regs",
+};
+static const struct attribute_group coresight_cti_channels_group = {
.attrs = coresight_cti_channel_attrs,
.name = "channels",
+};
+static const struct attribute_group coresight_cti_mgmt_group = {
.attrs = coresight_cti_mgmt_attrs,
.name = "mgmt",
+};
+const struct attribute_group *coresight_cti_groups[] = {
&coresight_cti_group,
&coresight_cti_regs_group,
&coresight_cti_channels_group,
&coresight_cti_mgmt_group,
NULL,
+};
+static int cti_starting_cpu(unsigned int cpu) +{
struct cti_drvdata *drvdata = cti_cpu_drv[cpu];
if (!drvdata)
return 0;
spin_lock(&drvdata->spinlock);
drvdata->config.hw_powered = true;
spin_unlock(&drvdata->spinlock);
if (atomic_read(&drvdata->config.enable_req_count))
cti_enable_hw(drvdata);
return 0;
+}
+static int cti_stopping_cpu(unsigned int cpu) +{
struct cti_drvdata *drvdata = cti_cpu_drv[cpu];
if (!drvdata)
return 0;
cti_disable_hw(drvdata);
spin_lock(&drvdata->spinlock);
drvdata->config.hw_powered = false;
spin_unlock(&drvdata->spinlock);
return 0;
+}
+static void cti_smp_set_powered(void *info) +{
struct cti_drvdata *drvdata = info;
drvdata->config.hw_powered = true;
+}
+/* setup power handling on a cti that has an association with a cpu */ +static int cti_setup_hp_cpu_affinity(struct cti_drvdata *drvdata) +{
int ret = 0;
/* CPU affinity - add to quick lookup array
* and register cpu hp callbacks first time out
*/
cpus_read_lock();
cti_cpu_drv[drvdata->ctidev.cpu] = drvdata;
if (!cti_cpu_count) {
cti_hp_online = cpuhp_setup_state_nocalls_cpuslocked(
CPUHP_AP_ONLINE_DYN, "arm/coresightcti:online",
cti_starting_cpu, cti_stopping_cpu);
if (cti_hp_online < 0) {
ret = cti_hp_online;
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
goto cti_hp_cpu_done;
}
}
cti_cpu_count++;
/* setup powered flag by running function of assoc cpu */
if (smp_call_function_single(drvdata->ctidev.cpu, cti_smp_set_powered,
drvdata, 1)) {
drvdata->config.hw_powered = false;
}
+cti_hp_cpu_done:
cpus_read_unlock();
return ret;
+}
+/* back out hotplug notifications on error */ +static void cti_clear_hp_cpu_affinity(struct cti_drvdata *drvdata) +{
if (!drvdata)
return;
if (drvdata->ctidev.cpu >= 0) {
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
cti_cpu_count--;
if ((cti_cpu_count == 0) && (cti_hp_online > 0))
cpuhp_remove_state_nocalls(cti_hp_online);
}
+}
+static int cti_probe(struct amba_device *adev, const struct amba_id *id) +{
int ret = 0;
u32 devid;
void __iomem *base;
struct device *dev = &adev->dev;
struct cti_drvdata *drvdata = 0;
struct coresight_desc cti_desc;
struct coresight_platform_data *pdata = NULL;
struct resource *res = &adev->res;
struct device_node *np = adev->dev.of_node;
struct ect_node *ect_nd = 0;
/* boilerplate code to set up the basics */
dev_info(dev, "%s(ID = %x)\n", __func__, id->id);
if (np) {
pdata = of_get_coresight_ect_platform_data(dev, np);
if (IS_ERR(pdata)) {
dev_info(dev, "of_get_coresight_ect_platform err\n");
ret = PTR_ERR(pdata);
goto err_out;
}
dev->platform_data = pdata;
}
/* node to keep track of CTI net */
ect_nd = kzalloc(sizeof(struct ect_node), GFP_KERNEL);
if (!ect_nd) {
ret = -ENOMEM;
goto err_out;
}
/* driver data*/
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata) {
ret = -ENOMEM;
dev_info(dev, "%s, mem err\n", __func__);
goto err_out;
If you go to err_out here ect_nd allocated above is leaked. The same applies to the reste of the 'goto err_out;' below.
}
/* links between dev and drvdata*/
drvdata->dev = dev;
dev_set_drvdata(dev, drvdata);
/* default CTI device info */
drvdata->ctidev.cpu = -1;
drvdata->ctidev.nr_trig_con = 0;
drvdata->ctidev.ctm_id = 0;
INIT_LIST_HEAD(&drvdata->ctidev.trig_cons);
/* Validity for the resource is already checked by the AMBA core */
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base)) {
ret = PTR_ERR(base);
dev_info(dev, "%s, remap err\n", __func__);
goto err_out;
}
drvdata->base = base;
spin_lock_init(&drvdata->spinlock);
/* look at the HW DEVID register for some of the HW settings */
devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
drvdata->config.nr_trig_max = (int)((devid & 0xFF00) >> 8);
/* no current hardware should exceed this, but protect the driver
* in case of fault / out of spec hw
*/
if (drvdata->config.nr_trig_max > CTIINOUTEN_MAX) {
dev_warn_once(dev,
"Limiting HW MaxTrig value(%d) to driver max(%d)\n",
drvdata->config.nr_trig_max, CTIINOUTEN_MAX);
drvdata->config.nr_trig_max = CTIINOUTEN_MAX;
}
drvdata->config.nr_ctm_channels = (int)((devid & 0xF0000) >> 16);
dev_info(dev, "DevID %08x, trigs=%d, chans=%d\n", devid,
drvdata->config.nr_trig_max, drvdata->config.nr_ctm_channels);
/** additional parse the .dts for connections and signals */
of_cti_get_hw_data(dev, np, drvdata);
cti_set_default_config(&drvdata->config);
/* setup cpu related CTI devices, otherwise assume powered */
if (drvdata->ctidev.cpu >= 0) {
ret = cti_setup_hp_cpu_affinity(drvdata);
if (ret < 0)
goto err_out;
} else
drvdata->config.hw_powered = true;
/* set up coresight component description */
cti_desc.pdata = pdata;
cti_desc.type = CORESIGHT_DEV_TYPE_ECT;
cti_desc.subtype.ect_subtype = CORESIGHT_DEV_SUBTYPE_ECT_CTI;
cti_desc.ops = &cti_ops;
cti_desc.groups = coresight_cti_groups;
cti_desc.dev = dev;
drvdata->csdev = coresight_register(&cti_desc);
if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
pr_err("%s: CS register failed\n", pdata->name);
goto err_out;
}
/* add to list of CTI devices */
mutex_lock(&ect_mutex);
ect_nd->cti_drv = drvdata;
list_add(&ect_nd->next, &ect_net);
mutex_unlock(&ect_mutex);
/* set any cross references */
cti_update_conn_xrefs(drvdata);
/* all done - dec pm refcount */
pm_runtime_put(&adev->dev);
dev_info(dev, "%s: initialized\n", pdata->name);
cti_count++;
dev_info(dev, "%s - ok\n", __func__);
return 0;
+err_out:
cti_clear_hp_cpu_affinity(drvdata);
dev_info(dev, "%s - err_out\n", __func__);
return ret;
+}
+/* free up CTI specific resources
- called from coresight_device_release on coresight_unregister.
- */
+void cti_device_release(struct device *dev) +{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct ect_node *ect_item, *ect_tmp;
struct cti_trig_con *tc, *tc_tmp;
/* free up resources associated with the cti connections */
if (drvdata->ctidev.cpu >= 0)
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
list_for_each_entry_safe(tc, tc_tmp,
&drvdata->ctidev.trig_cons, node) {
kfree(tc->con_in);
kfree(tc->con_out);
kfree(tc->con_dev_name);
list_del(&tc->node);
kfree(tc);
}
/* remove from the list */
mutex_lock(&ect_mutex);
list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, next) {
if (ect_item->cti_drv == drvdata) {
list_del(&ect_item->next);
kfree(ect_item);
goto ect_list_item_removed;
}
}
+ect_list_item_removed:
mutex_unlock(&ect_mutex);
kfree(drvdata);
+} +EXPORT_SYMBOL_GPL(cti_device_release);
+#define CTI_AMBA_ID(pid) \
{ \
.id = pid, \
.mask = 0x000fffff, \
}
+static struct amba_cs_uci_id uci_id_cti[] = {
{
/* CTI UCI data */
.devarch = 0x47701a14, /* devarch value for CTI v2 */
.devarch_mask = 0xfff0ffff,
.devtype = 0x00000014, /* maj(0x4-debug) min(0x1-ECT) */
}
+};
+#define CTI_AMBA_UCI_ID(pid) \
{ \
.id = pid, \
.mask = 0x000fffff, \
.data = uci_id_cti, \
}
+static const struct amba_id cti_ids[] = {
CTI_AMBA_ID(0x000bb906), /* Coresight CTI (SoC 400), C-A72, C-A57 */
CTI_AMBA_ID(0x000bb922), /* CTI - C-A8 */
CTI_AMBA_ID(0x000bb9a8), /* CTI - C-A53 */
CTI_AMBA_ID(0x000bb9aa), /* CTI - C-A73 */
CTI_AMBA_UCI_ID(0x000bb9da), /* CTI - C-A35 */
CTI_AMBA_UCI_ID(0x000bb9ed), /* Coresight CTI (SoC 600) */
CTI_AMBA_UCI_ID(0x000bb95d), /* test UCI - ETM PID A53 */
{ 0, 0},
+};
+static struct amba_driver cti_driver = {
.drv = {
.name = "coresight-cti",
.owner = THIS_MODULE,
.suppress_bind_attrs = true,
},
.probe = cti_probe,
.id_table = cti_ids,
+}; +builtin_amba_driver(cti_driver); diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h new file mode 100644 index 000000000000..09cb985ac0ea --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#ifndef _CORESIGHT_CORESIGHT_CTI_H +#define _CORESIGHT_CORESIGHT_CTI_H
+#include <asm/local.h> +#include <linux/spinlock.h> +#include "coresight-priv.h"
+/*
- Device registers
- 0x000 - 0x144: CTI programming and status
- 0xEDC - 0xEF8: CTI integration test.
- 0xF00 - 0xFFC: Coresight management registers.
- */
+/* CTI programming registers */ +#define CTICONTROL 0x000 +#define CTIINTACK 0x010 +#define CTIAPPSET 0x014 +#define CTIAPPCLEAR 0x018 +#define CTIAPPPULSE 0x01C +#define CTIINEN(n) (0x020 + (4 * n)) +#define CTIOUTEN(n) (0x0A0 + (4 * n)) +#define CTITRIGINSTATUS 0x130 +#define CTITRIGOUTSTATUS 0x134 +#define CTICHINSTATUS 0x138 +#define CTICHOUTSTATUS 0x13C +#define CTIGATE 0x140 +#define ASICCTL 0x144 +/* Integration test registers */ +#define ITCHINACK 0xEDC /* WO CTI CSSoc 400 only*/ +#define ITTRIGINACK 0xEE0 /* WO CTI CSSoc 400 only*/ +#define ITCHOUT 0xEE4 /* WO RW-600 */ +#define ITTRIGOUT 0xEE8 /* WO RW-600 */ +#define ITCHOUTACK 0xEEC /* RO CTI CSSoc 400 only*/ +#define ITTRIGOUTACK 0xEF0 /* RO CTI CSSoc 400 only*/ +#define ITCHIN 0xEF4 /* RO */ +#define ITTRIGIN 0xEF8 /* RO */ +/* management registers */ +#define CTIDEVAFF0 0xFA8 +#define CTIDEVAFF1 0xFAC
+/* CTI CSSoc 600 has a max of 32 trigger signals per direction.
- CTI CSSoc 400 has 8 IO triggers - other CTIs can be impl def.
- Max of in and out defined in the DEVID register.
- pick up actual number used from .dts parameters if present.
- */
+#define CTIINOUTEN_MAX 32
+/**
- Group of related trigger signals
- @nr_sigs: number of signals in the group.
- @used_mask: bitmask representing the signal indexes in the group.
- @sig_names: list of names for the signals.
- */
+struct cti_trig_grp {
int nr_sigs;
u32 used_mask;
char *sig_names;
+};
+/**
- Trigger connection - connection between a CTI and other (coresight) device
- lists input and output trigger signals for the device
- @con_in: connected CTIIN signals for the device.
- @con_out: connected CTIOUT signals for the device.
- @con_dev: coresight device connected to the CTI, NULL if not CS device
- @con_dev_name: name of connected device (CS or CPU)
- @node: entry node in list of connections.
- */
+struct cti_trig_con {
struct cti_trig_grp *con_in;
struct cti_trig_grp *con_out;
struct coresight_device *con_dev;
char *con_dev_name;
struct list_head node;
+};
+/**
- struct cti_device - description of CTI device properties.
- @nt_trig_con: Number of external devices connected to this device.
- @ctm_id: which CTM this device is connected to (by default it is
assumed there is a single CTM per SoC, ID 0).
- @trig_cons: list of connections to this device.
- @cpu: CPU ID if associated with CPU, -1 otherwise.
- */
+struct cti_device {
int nr_trig_con;
u32 ctm_id;
struct list_head trig_cons;
int cpu;
+};
+/**
- struct cti_config - configuration of the CTI device hardware
- hardware description from RO ID regs
- @nr_trig_max: Max number of trigger signals implemented on device.
(max of trig_in or trig_out)
- @nr_ctm_channels: number of available CTM channels
- cti enable control
- @enable_req_count: CTI is enabled alongside >=1 associated devices.
- @hw_enabled: true if hw is currently enabled.
- @hw_powered: true if associated cpu powered on, or no cpu.
- registered triggers and filtering
- @trig_in_use: bitfield of in triggers registered as in use.
- @trig_out_use: bitfield of out triggers registered as in use.
- @trig_out_filter: bitfield of out triggers that are blocked if filter
enabled. Typically this would be dbgreq / restart on a core CTI.
- @trig_filter_enable: 1 if filtering enabled.
- cti software programmable regs:
- @ctiappset: CTI Software application channel set.
- @ctiinout_sel: register selector for INEN and OUTEN regs.
- @ctiinen: enable input trigger to a channel.
- @ctiouten: enable output trigger from a channel.
- @ctigate: gate channel output from CTI to CTM.
- */
+struct cti_config {
/* hardware description */
int nr_ctm_channels;
int nr_trig_max;
/* cti enable control */
atomic_t enable_req_count;
bool hw_enabled;
bool hw_powered;
/* registered triggers and filtering */
u32 trig_in_use;
u32 trig_out_use;
u32 trig_out_filter;
int trig_filter_enable;
/* cti cross trig programmable regs */
u32 ctiappset;
u8 ctiinout_sel;
u32 ctiinen[CTIINOUTEN_MAX];
u32 ctiouten[CTIINOUTEN_MAX];
u32 ctigate;
u32 asicctl;
+};
+/**
- struct cti_drvdata - specifics for the CTI device
- @base: Memory mapped base address for this component.
- @dev: The device entity associated to this component.
- @csdev: Standard CoreSight device information.
- @ctidev: Extra information needed by the CTI/CTM framework.
- @spinlock: Control data access to one at a time.
- @config: Configuration data for this CTI device.
- */
+struct cti_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
struct cti_device ctidev;
spinlock_t spinlock;
struct cti_config config;
+};
+enum cti_chan_op {
CTI_CHAN_ATTACH,
CTI_CHAN_DETACH,
+};
+enum cti_trig_dir {
CTI_TRIG_IN,
CTI_TRIG_OUT,
+};
+enum cti_chan_gate_op {
CTI_GATE_CHAN_ENABLE,
CTI_GATE_CHAN_DISABLE,
CTI_GATE_CHAN_ENABLE_ALL,
CTI_GATE_CHAN_DISABLE_ALL,
+};
+enum cti_chan_set_op {
CTI_CHAN_SET,
CTI_CHAN_CLR,
CTI_CHAN_PULSE,
+};
+#endif /* _CORESIGHT_CORESIGHT_CTI_H */
2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
On Wed, Jan 23, 2019 at 09:09:51PM +0000, Mike Leach wrote:
[cut]
On Fri, 18 Jan 2019 at 22:17, Mathieu Poirier
in->nr_sigs = 3;
in->used_mask = 0x7;
in->sig_names = NULL;
out->nr_sigs = 3;
out->used_mask = 0x7;
out->sig_names = NULL;
scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
cti_add_connection_entry(drvdata, in, out, NULL, cpu_name_str);
drvdata->config.trig_out_filter = 0x1; /* filter dbgreq */
/* v8 ETM associated config... */
cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
if (cs_np) {
Does it make sense to have a CTI connected to a CPU but not have an ETM associated with that CPU? Shouldn't this be treated as an error?
No. The ETM is optional, the CTI is not.
Please add a comment - if I triggered on it others will.
Alongside the halting debug signals - which are of limited use in this context - there are two other relevant signals. The CPU has a PMU overflow out signal connected to a trigin on the CTI, and there is a trigout assigned as a generic CTI IRQ - though the connection of this IRQ in any system is implementation defined - recommended attached to GIC or other interrupt controller in the system.
in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!in)
return -ENOMEM;
out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!out)
return -ENOMEM;
in->nr_sigs = 4;
in->used_mask = 0xF0;
in->sig_names = NULL;
out->nr_sigs = 4;
out->used_mask = 0xF0;
out->sig_names = NULL;
dev_info(drvdata->dev, "v8 finding assoc dev %s\n",
cs_np->full_name);
csdev = of_coresight_get_cs_device_by_node(cs_np);
if (csdev)
assoc_name = dev_name(&csdev->dev);
else
assoc_name = cs_np->full_name;
I'm guessing you're doing the above to deal with device discovery ordering. If I am correct please add a comment that makes it clear.
cti_add_connection_entry(drvdata, in, out, csdev, assoc_name);
of_node_put(cs_np);
}
return 0;
Same comment about memory release in case of something goes wrong. I've also taken the habit of adding a comment that mentions where the memory will be be released for each allocation I do. That helps me remember that I have to release the memory and allows other people to better understand how things work.
Good tip.
+}
+static int of_cti_read_trig_group(struct cti_trig_grp **trig_grp_ptr,
struct device_node *np,
const char *grp_name,
int max_trigs)
+{
int items, err = 0;
u32 value, pidx;
struct cti_trig_grp *grp;
grp = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!grp)
return -ENOMEM;
*trig_grp_ptr = grp;
items = of_property_count_elems_of_size(np, grp_name, 4);
From the documentation and the DT bindings you've provided it is pretty easy to figure out why you have a 4 there but I will still need a comment to explain it. But have you explained anywhere in the documentation why trigger groups have 4 pins? Is it just that the HW only provides a maximum of 4 in and 4 out for each device? If so, it would be a good thing to write it somewhere.
The 4 here is the size of the individual element in the property - not the number of them. My understanding is that these will be 32 bit values - hence 4 bytes.
/* if the property doesn't exist or has no values, then return
* an empty connection group
*/
if (items < 0)
return 0;
if (items > max_trigs)
return -EINVAL;
/* set the number of signals and usage mask */
for (pidx = 0; pidx < items; pidx++) {
err = of_property_read_u32_index(np, grp_name, pidx, &value);
if (err)
return err;
Memory leak of 'grp'.
Possibly not here - grp is passed out of the function if successfully allocated - and should be cleaned up when the connection is cleaned up. Though this needs a re-check.
grp->nr_sigs++;
grp->used_mask |= (0x1 << value);
}
return 0;
+}
+static int of_cti_create_connection(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
struct cti_trig_grp *in = 0, *out = 0, *filter = 0;
int cpuid = -1, err = 0, trig_max = drvdata->config.nr_trig_max;
struct device_node *cs_np;
struct coresight_device *csdev = NULL;
const char *assoc_name = "unknown";
char cpu_name_str[16];
/* look for the signals properties. */
err = of_cti_read_trig_group(&in, np, CTI_DT_TRIGIN_SIGS, trig_max);
if (err)
return err;
err = of_cti_read_trig_group(&out, np, CTI_DT_TRIGOUT_SIGS, trig_max);
if (err)
return err;
err = of_cti_read_trig_group(&filter, np, CTI_DT_FILTER_OUT_SIGS,
trig_max);
if (err)
return err;
Whatch out for memory leaks in the above.
/* read the connection name if set - may be overridden by later */
of_property_read_string(np, CTI_DT_CONN_NAME, &assoc_name);
/* associated cpu ? */
cpuid = of_coresight_ect_get_cpu(np);
drvdata->ctidev.cpu = cpuid;
if (cpuid >= 0) {
scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
assoc_name = cpu_name_str;
} else {
/* associated device ? */
cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
if (cs_np) {
dev_info(dev, "finding assoc dev %s\n",
cs_np->full_name);
csdev = of_coresight_get_cs_device_by_node(cs_np);
if (csdev) /* use device name if csdev found */
assoc_name = dev_name(&csdev->dev);
else /* otherwise node name for later association */
assoc_name = cs_np->full_name;
of_node_put(cs_np);
}
}
/* set up a connection */
err = cti_add_connection_entry(drvdata, in, out, csdev, assoc_name);
/* note any filter info */
if (!err)
drvdata->config.trig_out_filter |= filter->used_mask;
kfree(filter);
return err;
+}
After reviewing functions of_cti_get_hw_data(), of_cti_create_v8_connections() and of_cti_create_connection() I wonder if they shouldn't be added to a new of-coresight-cti.c file, along with the rest of the OF centric functions needed by the driver.
More to come on Monday.
Mathieu
Thanks for the review
Mike
+/* get the hardware configuration & connection data. */ +static int of_cti_get_hw_data(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
int rc = 0;
struct cti_device *cti_dev = &drvdata->ctidev;
struct device_node *nc = NULL;
/* get any CTM ID - defaults to 0 */
of_property_read_u32(np, CTI_DT_CTM_ID, &cti_dev->ctm_id);
if (of_property_read_bool(np, CTI_DT_V8ARCH)) {
rc = of_cti_create_v8_connections(drvdata, np);
} else {
for_each_child_of_node(np, nc) {
if (of_node_cmp(nc->name, CTI_DT_CONNS) != 0)
continue;
dev_info(dev, "dt found node: %s %s\n",
nc->name, nc->full_name);
rc = of_cti_create_connection(dev, nc, drvdata);
if (rc != 0)
return rc;
}
}
/* if no connections, just add a single default based on max IN-OUT */
if (cti_dev->nr_trig_con == 0)
rc = cti_add_default_connection(drvdata);
return rc;
+}
+static int +cti_match_con_name(struct cti_device *ctidev, const char *node_name,
const char *csdev_name)
+{
int found = 0;
struct cti_trig_con *trig_con;
list_for_each_entry(trig_con, &ctidev->trig_cons, node) {
if (trig_con->con_dev_name) {
if (!strcmp(node_name, trig_con->con_dev_name)) {
found = 1;
/* match: so swap in csdev name */
kfree(trig_con->con_dev_name);
trig_con->con_dev_name =
kstrdup(csdev_name, GFP_KERNEL);
goto cti_con_name_match;
}
}
}
+cti_con_name_match:
return found;
+}
+/* search the cti list to add an associated CTI into the supplied CS device
- This will set the association if CTI declared before the CS device
- */
+int cti_add_assoc_to_csdev(struct coresight_device *csdev) +{
struct ect_node *ect_item;
struct cti_device *ctidev;
const char *node_name = NULL, *csdev_name;
/* exit early for no CTIs or self referencing ECT devices.*/
if (!cti_count) {
dev_info(&csdev->dev, "no CTI to check\n");
return 0;
}
if (!csdev->dev.parent->of_node) {
dev_info(&csdev->dev, "No parent of_node pointer\n");
return 0;
}
node_name = csdev->dev.parent->of_node->full_name;
if (!node_name) {
dev_info(&csdev->dev, "%s - bad node name\n", __func__);
return 0;
}
dev_info(&csdev->dev, "%s - looking for %s\n", __func__, node_name);
if (!cti_count || (csdev->type == CORESIGHT_DEV_TYPE_ECT)) {
dev_info(&csdev->dev, "no CTI or skip self ref CTI\n");
return 0;
}
csdev_name = dev_name(&csdev->dev);
/* for each CTI in list... */
mutex_lock(&ect_mutex);
list_for_each_entry(ect_item, &ect_net, next) {
ctidev = &ect_item->cti_drv->ctidev;
if (cti_match_con_name(ctidev, node_name, csdev_name)) {
csdev->ect_dev = ect_item->cti_drv->csdev;
dev_info(&csdev->dev, "Found!\n");
goto cti_found;
}
}
+cti_found:
mutex_unlock(&ect_mutex);
return 0;
+} +EXPORT_SYMBOL_GPL(cti_add_assoc_to_csdev);
+/* attach/detach channel from trigger - write through if enabled. */ +static int cti_channel_trig_op(struct device *dev,
enum cti_chan_op op,
enum cti_trig_dir direction,
u32 channel_idx,
u32 trigger_idx)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
u32 trig_bitmask;
u32 chan_bitmask;
u32 reg_value;
int reg_offset;
dev_info(dev, "chan_trig_op: op %d, dir %d, chan %d, trig %d\n",
op, direction, channel_idx, trigger_idx);
/* ensure indexes in range */
if ((channel_idx >= config->nr_ctm_channels) ||
(trigger_idx >= config->nr_trig_max))
return -EINVAL;
trig_bitmask = 0x1 << trigger_idx;
/* ensure registered triggers and not out filtered */
if (direction == CTI_TRIG_IN) {
if (!(trig_bitmask & config->trig_in_use))
return -EINVAL;
} else {
if (!(trig_bitmask & config->trig_out_use))
return -EINVAL;
if ((config->trig_filter_enable) &&
(config->trig_out_filter & trig_bitmask))
return -EINVAL;
}
/* update the local register values */
chan_bitmask = 0x1 << channel_idx;
reg_offset = (direction == CTI_TRIG_IN ? CTIINEN(trigger_idx) :
CTIOUTEN(trigger_idx));
spin_lock(&drvdata->spinlock);
/* read - modify write - the trigger / channel enable value */
reg_value = (direction == CTI_TRIG_IN ? config->ctiinen[trigger_idx] :
config->ctiouten[trigger_idx]);
reg_value = (op == CTI_CHAN_ATTACH) ? reg_value | chan_bitmask :
reg_value & (~chan_bitmask);
/* write local copy */
if (direction == CTI_TRIG_IN)
config->ctiinen[trigger_idx] = reg_value;
else
config->ctiouten[trigger_idx] = reg_value;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
spin_unlock(&drvdata->spinlock);
return 0;
+}
+static int cti_channel_gate_op(struct device *dev,
enum cti_chan_gate_op op,
u32 channel_idx)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
u32 chan_bitmask;
u32 reg_value;
int err = 0;
if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
chan_bitmask = 0x1 << channel_idx;
spin_lock(&drvdata->spinlock);
reg_value = config->ctigate;
switch (op) {
case CTI_GATE_CHAN_ENABLE:
reg_value |= chan_bitmask;
break;
case CTI_GATE_CHAN_DISABLE:
reg_value &= ~chan_bitmask;
break;
case CTI_GATE_CHAN_ENABLE_ALL:
reg_value = (0x1 << config->nr_ctm_channels) - 1;
break;
case CTI_GATE_CHAN_DISABLE_ALL:
reg_value = 0x0;
break;
default:
err = -EINVAL;
break;
}
if (err == 0) {
config->ctigate = reg_value;
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIGATE, reg_value);
}
spin_unlock(&drvdata->spinlock);
return err;
+}
+static int cti_channel_setop(struct device *dev,
enum cti_chan_set_op op,
u32 channel_idx)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
u32 chan_bitmask;
u32 reg_value;
u32 reg_offset;
int err = 0;
if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
chan_bitmask = 0x1 << channel_idx;
spin_lock(&drvdata->spinlock);
reg_value = config->ctiappset;
switch (op) {
case CTI_CHAN_SET:
config->ctiappset |= chan_bitmask;
reg_value = config->ctiappset;
reg_offset = CTIAPPSET;
break;
case CTI_CHAN_CLR:
config->ctiappset &= ~chan_bitmask;
reg_value = chan_bitmask;
reg_offset = CTIAPPCLEAR;
break;
case CTI_CHAN_PULSE:
config->ctiappset &= ~chan_bitmask;
reg_value = chan_bitmask;
reg_offset = CTIAPPPULSE;
break;
default:
err = -EINVAL;
break;
}
if ((err == 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
spin_unlock(&drvdata->spinlock);
return err;
+}
+/** cti ect operations **/ +static int cti_enable(struct coresight_device *csdev, void *__unused) +{
int rc;
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
atomic_inc(&drvdata->config.enable_req_count);
rc = cti_enable_hw(drvdata);
if (rc)
atomic_dec(&drvdata->config.enable_req_count);
return rc;
+}
+static int cti_disable(struct coresight_device *csdev, void *__unused) +{
int rc = 0;
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
if (!atomic_dec_return(&drvdata->config.enable_req_count)) {
rc = cti_disable_hw(drvdata);
if (rc)
atomic_inc(&drvdata->config.enable_req_count);
}
return rc;
+}
+const struct coresight_ops_ect cti_ops_ect = {
.enable = cti_enable,
.disable = cti_disable,
+};
+const struct coresight_ops cti_ops = {
.ect_ops = &cti_ops_ect,
+};
+/** attributes **/
+#define coresight_cti_reg(name, offset) \
coresight_simple_reg32(struct cti_drvdata, name, offset)
+/* show a simple 32 bit value. if pval is NULL then live read,
- otherwise read from supplied pointer only
- */
+static ssize_t cti_reg32_show(struct device *dev, char *buf,
u32 *pval, int reg_offset)
+{
unsigned long val = 0;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
if (pval) {
val = (unsigned long)*pval;
} else if ((reg_offset >= 0) && CTI_PWR_ENA(config)) {
CS_UNLOCK(drvdata->base);
val = readl_relaxed(drvdata->base + reg_offset);
CS_LOCK(drvdata->base);
}
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+/* store a simple 32 bit value.
- if pval not NULL, then copy to here too,
- if reg_offset >= 0 then write through if enabled.
- */
+static ssize_t cti_reg32_store(struct device *dev, const char *buf,
size_t size, u32 *pval,
int reg_offset)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
/* local store */
if (pval)
*pval = (u32)val;
/* write through of offset and enabled */
if ((reg_offset >= 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, val);
spin_unlock(&drvdata->spinlock);
return size;
+}
+/* basic attributes */ +static ssize_t enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
int enable_req;
bool enabled, powered, cpuid;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
ssize_t size = 0;
enable_req = atomic_read(&drvdata->config.enable_req_count);
spin_lock(&drvdata->spinlock);
powered = drvdata->config.hw_powered;
enabled = drvdata->config.hw_enabled;
cpuid = drvdata->ctidev.cpu;
spin_unlock(&drvdata->spinlock);
if (powered) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled", cpuid);
} else if (cpuid >= 0) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req", cpuid);
} else {
size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n",
enabled ? "enabled" : "disabled");
}
return size;
+}
+static ssize_t enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int ret = 0;
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
ret = kstrtoul(buf, 16, &val);
if (ret)
return ret;
if (val)
ret = cti_enable(drvdata->csdev, NULL);
else
ret = cti_disable(drvdata->csdev, NULL);
if (ret)
return ret;
return size;
+} +static DEVICE_ATTR_RW(enable);
+static ssize_t ctmid_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return scnprintf(buf, PAGE_SIZE, "0x%x\n", drvdata->ctidev.ctm_id);
+} +static DEVICE_ATTR_RO(ctmid);
+#define LF_ADJUST_BUF_PTRS(n) \
do { buf_pos += n; buf_size -= n; \
if (buf_size <= 1) \
goto lf_buf_full; \
} while (0)
+static ssize_t list_features_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
int used = 0, sig_idx, con_idx, nr_trig_max;
/* buffer vars */
int buf_size = PAGE_SIZE;
char *buf_pos = buf;
char *con_name;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *cfg = &drvdata->config;
struct cti_device *ctidev = &drvdata->ctidev;
u32 sig_mask;
struct cti_trig_con *con;
/* basic feature info */
used = scnprintf(buf_pos, buf_size,
"CTI:%s; Channels:%d; Max Trigs:%d\n",
dev_name(dev), cfg->nr_ctm_channels, cfg->nr_trig_max);
LF_ADJUST_BUF_PTRS(used);
used = scnprintf(buf_pos, buf_size, "CTM id = %d; Num Conns = %d\n",
ctidev->ctm_id, ctidev->nr_trig_con);
LF_ADJUST_BUF_PTRS(used);
/* list each connection info */
con_idx = 0;
nr_trig_max = cfg->nr_trig_max;
list_for_each_entry(con, &ctidev->trig_cons, node) {
/* connection name */
if (con->con_dev_name != NULL)
con_name = con->con_dev_name;
else
con_name = "unknown";
used = scnprintf(buf_pos, buf_size, "conn(%d:%s)\n",
con_idx, con_name);
LF_ADJUST_BUF_PTRS(used);
con_idx++;
/* connection signals */
used = scnprintf(buf_pos, buf_size, "Trig IN(%d) [ ",
con->con_in->nr_sigs);
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & con->con_in->used_mask) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
used = scnprintf(buf_pos, buf_size, "Trig OUT(%d) [ ",
con->con_out->nr_sigs);
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & con->con_out->used_mask) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
}
if (cfg->trig_out_filter) {
used = scnprintf(buf_pos, buf_size, "Trig OUT_FILTERED [ ");
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & cfg->trig_out_filter) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
}
+lf_buf_full:
used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
return used;
+} +static DEVICE_ATTR_RO(list_features);
+static struct attribute *coresight_cti_attrs[] = {
&dev_attr_enable.attr,
&dev_attr_list_features.attr,
&dev_attr_ctmid.attr,
NULL,
+};
+/* raw register attributes */ +static ssize_t inout_sel_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
u32 val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
val = (u32)drvdata->config.ctiinout_sel;
return scnprintf(buf, PAGE_SIZE, "%#x\n", val);
+}
+static ssize_t inout_sel_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
if (kstrtoul(buf, 16, &val))
return -EINVAL;
if (val > (CTIINOUTEN_MAX-1))
return -EINVAL;
spin_lock(&drvdata->spinlock);
drvdata->config.ctiinout_sel = val;
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(inout_sel);
+static ssize_t inen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
spin_lock(&drvdata->spinlock);
index = drvdata->config.ctiinout_sel;
val = drvdata->config.ctiinen[index];
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "INEN%d %#lx\n", index, val);
+}
+static ssize_t inen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
index = config->ctiinout_sel;
config->ctiinen[index] = val;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIINEN(index), val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(inen);
+static ssize_t outen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
spin_lock(&drvdata->spinlock);
index = drvdata->config.ctiinout_sel;
val = drvdata->config.ctiouten[index];
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "OUTEN%d %#lx\n", index, val);
+}
+static ssize_t outen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
index = config->ctiinout_sel;
config->ctiouten[index] = val;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIOUTEN(index), val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(outen);
+static ssize_t gate_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_show(dev, buf, &drvdata->config.ctigate, -1);
+}
+static ssize_t gate_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_store(dev, buf, size,
&drvdata->config.ctigate, CTIGATE);
+} +static DEVICE_ATTR_RW(gate);
+static ssize_t asicctl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_show(dev, buf, &drvdata->config.asicctl, -1);
+}
+static ssize_t asicctl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_store(dev, buf, size,
&drvdata->config.asicctl, ASICCTL);
+} +static DEVICE_ATTR_RW(asicctl);
+static ssize_t intack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
cti_write_intack(dev, val);
return size;
+} +static DEVICE_ATTR_WO(intack);
+static ssize_t appset_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_show(dev, buf, &drvdata->config.ctiappset, -1);
+}
+static ssize_t appset_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_store(dev, buf, size,
&drvdata->config.ctiappset, CTIAPPSET);
+} +static DEVICE_ATTR_RW(appset);
+static ssize_t appclear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val, mask;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
/* a 1'b1 in appclr clears down the same bit in appset*/
mask = ~val;
config->ctiappset &= mask;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPCLEAR, val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_WO(appclear);
+static ssize_t apppulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val, mask;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
/* a 1'b1 in apppulse sets then clears the bit,
* effectively clears down the same bit in appset
*/
mask = ~val;
config->ctiappset &= mask;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPPULSE, val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_WO(apppulse);
+static ssize_t itchout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return cti_reg32_show(dev, buf, NULL, ITCHOUT);
+}
+static ssize_t itchout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITCHOUT);
+} +static DEVICE_ATTR_RW(itchout);
+static ssize_t ittrigout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return cti_reg32_show(dev, buf, NULL, ITTRIGOUT);
+}
+static ssize_t ittrigout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITTRIGOUT);
+} +static DEVICE_ATTR_RW(ittrigout);
+static ssize_t itchinack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITCHINACK);
+} +static DEVICE_ATTR_WO(itchinack);
+static ssize_t ittriginack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITTRIGINACK);
+} +static DEVICE_ATTR_WO(ittriginack);
+static ssize_t itctrl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return cti_reg32_show(dev, buf, NULL, CORESIGHT_ITCTRL);
+}
+static ssize_t itctrl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, CORESIGHT_ITCTRL);
+} +static DEVICE_ATTR_RW(itctrl);
+coresight_cti_reg(triginstatus, CTITRIGINSTATUS); +coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS); +coresight_cti_reg(chinstatus, CTICHINSTATUS); +coresight_cti_reg(choutstatus, CTICHOUTSTATUS); +coresight_cti_reg(ittrigin, ITTRIGIN); +coresight_cti_reg(itchin, ITCHIN); +coresight_cti_reg(itchoutack, ITCHOUTACK); +coresight_cti_reg(ittrigoutack, ITTRIGOUTACK);
+static struct attribute *coresight_cti_regs_attrs[] = {
&dev_attr_inout_sel.attr,
&dev_attr_inen.attr,
&dev_attr_outen.attr,
&dev_attr_gate.attr,
&dev_attr_asicctl.attr,
&dev_attr_intack.attr,
&dev_attr_appset.attr,
&dev_attr_appclear.attr,
&dev_attr_apppulse.attr,
&dev_attr_triginstatus.attr,
&dev_attr_trigoutstatus.attr,
&dev_attr_chinstatus.attr,
&dev_attr_choutstatus.attr,
&dev_attr_itctrl.attr,
&dev_attr_ittrigin.attr,
&dev_attr_itchin.attr,
&dev_attr_ittrigout.attr,
&dev_attr_itchout.attr,
&dev_attr_itchoutack.attr,
&dev_attr_ittrigoutack.attr,
&dev_attr_ittriginack.attr,
&dev_attr_itchinack.attr,
NULL,
+};
+/* channel / trigger api */ +static int +cti_trig_op_parse(struct device *dev, enum cti_chan_op op,
enum cti_trig_dir dir, const char *buf, size_t size)
+{
u32 chan_idx;
u32 trig_idx;
int items, err = size;
/* extract chan idx and trigger idx */
items = sscanf(buf, "%d %d", &chan_idx, &trig_idx);
if (items) {
err = cti_channel_trig_op(dev, op, dir, chan_idx, trig_idx);
if (!err)
err = size;
} else
err = -EINVAL;
return err;
+}
+static ssize_t trigin_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_attach);
+static ssize_t trigin_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_detach);
+static ssize_t trigout_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_attach);
+static ssize_t trigout_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_detach);
+static ssize_t gate_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = 0, channel = 0, items;
if (strcmp(buf, "all") == 0) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE_ALL, 0);
} else {
items = sscanf(buf, "%d", &channel);
if (!items)
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE, 0);
}
return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_enable);
+static ssize_t gate_disable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = 0, channel = 0, items;
if (strcmp(buf, "all") == 0) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE_ALL, 0);
} else {
items = sscanf(buf, "%d", &channel);
if (!items)
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE, 0);
}
return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_disable);
+static int +chan_op_parse(struct device *dev, enum cti_chan_set_op op, const char *buf) +{
int err = 0, channel = 0, items;
items = sscanf(buf, "%d", &channel);
if (!items)
return -EINVAL;
err = cti_channel_setop(dev, op, channel);
return err;
+}
+static ssize_t chan_set_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = chan_op_parse(dev, CTI_CHAN_SET, buf);
return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_set);
+static ssize_t chan_clear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = chan_op_parse(dev, CTI_CHAN_CLR, buf);
return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_clear);
+static ssize_t chan_pulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = chan_op_parse(dev, CTI_CHAN_PULSE, buf);
return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_pulse);
+static ssize_t trig_filter_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
spin_lock(&drvdata->spinlock);
val = drvdata->config.trig_filter_enable;
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "%ld (%s)\n", val,
val ? "enabled" : "disabled");
+}
+static ssize_t trig_filter_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
drvdata->config.trig_filter_enable = val ? 1 : 0;
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(trig_filter_enable);
+/* clear all xtrigger / channel programming */ +static ssize_t reset_xtrigs_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int i;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
/* clear the CTI trigger / channel programming registers */
for (i = 0; i < config->nr_trig_max; i++) {
config->ctiinen[i] = 0;
config->ctiouten[i] = 0;
}
/* clear the other regs */
config->ctigate = (0x1 << config->nr_ctm_channels) - 1;
config->asicctl = 0;
config->ctiappset = 0;
config->ctiinout_sel = 0;
/* if enabled then write through */
if (CTI_PWR_ENA(config))
cti_write_all_hw_regs(drvdata);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_WO(reset_xtrigs);
+/* list attachments by channel */ +#define LX_ADJUST_BUF_PTRS(n) \
do { buf_pos += n; buf_size -= n; \
if (buf_size <= 1) \
goto lx_buf_full; \
} while (0)
+static ssize_t list_xtrigs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
int used = 0, nr_channels, chan_idx, reg_idx;
/* buffer vars */
int buf_size = PAGE_SIZE;
char *buf_pos = buf;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *cfg = &drvdata->config;
u32 chan_mask;
nr_channels = cfg->nr_ctm_channels;
used = scnprintf(buf_pos, buf_size, "CTI:%s; Channels:%d\n",
dev_name(dev), nr_channels);
LX_ADJUST_BUF_PTRS(used);
/* show the IN and OUT triggers per channel */
for (chan_idx = 0; chan_idx < nr_channels; chan_idx++) {
chan_mask = 0x1 << chan_idx;
used = scnprintf(buf_pos, buf_size, "Chan %d: IN [ ", chan_idx);
LX_ADJUST_BUF_PTRS(used);
for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiinen[reg_idx]) {
used = scnprintf(buf_pos, buf_size, "%d ",
reg_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]; OUT [ ");
LX_ADJUST_BUF_PTRS(used);
for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiouten[reg_idx]) {
used = scnprintf(buf_pos, buf_size, "%d ",
reg_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "];\n");
LX_ADJUST_BUF_PTRS(used);
}
/* show the channels enabled via ctigate */
used = scnprintf(buf_pos, buf_size, "Gate Channels Enabled: [ ");
LX_ADJUST_BUF_PTRS(used);
if (cfg->ctigate == 0) {
used = scnprintf(buf_pos, buf_size, "None ]\n");
LX_ADJUST_BUF_PTRS(used);
} else {
for (chan_idx = 0; chan_idx < nr_channels; chan_idx++) {
chan_mask = 0x1 << chan_idx;
if (chan_mask & cfg->ctigate) {
used = scnprintf(buf_pos, buf_size, "%d ",
chan_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LX_ADJUST_BUF_PTRS(used);
}
+lx_buf_full:
used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
return used;
+} +static DEVICE_ATTR_RO(list_xtrigs);
+#define LCIU_ADJUST_BUF_PTRS(n) \
do { buf_pos += n; buf_size -= n; \
if (buf_size <= 1) \
goto lciu_buf_full; \
} while (0)
+static ssize_t list_chan_inuse_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
int used = 0, nr_channels, i;
/* buffer vars */
int buf_size = PAGE_SIZE;
char *buf_pos = buf;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
u32 inuse_bits = 0, chan_mask, chan_bit_mask;
nr_channels = config->nr_ctm_channels;
spin_lock(&drvdata->spinlock);
for (i = 0; i < config->nr_trig_max; i++) {
inuse_bits |= config->ctiinen[i];
inuse_bits |= config->ctiouten[i];
}
spin_unlock(&drvdata->spinlock);
/* print in use channels */
chan_mask = ((u32)(0x1 << nr_channels)) - 1;
if (inuse_bits & chan_mask) {
used = scnprintf(buf_pos, buf_size, "Chan in use [ ");
LCIU_ADJUST_BUF_PTRS(used);
for (i = 0; i < nr_channels; i++) {
chan_bit_mask = 0x1 << i;
if (chan_bit_mask & inuse_bits) {
used = scnprintf(buf_pos, buf_size, "%d ", i);
LCIU_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LCIU_ADJUST_BUF_PTRS(used);
} else {
used = scnprintf(buf_pos, buf_size, "Chan in use [ none ]\n");
LCIU_ADJUST_BUF_PTRS(used);
}
/* print free channels */
inuse_bits = ~inuse_bits; /* flip for free channels */
if (inuse_bits & chan_mask) {
used = scnprintf(buf_pos, buf_size, "Chan free [ ");
LCIU_ADJUST_BUF_PTRS(used);
for (i = 0; i < nr_channels; i++) {
chan_bit_mask = 0x1 << i;
if (chan_bit_mask & inuse_bits) {
used = scnprintf(buf_pos, buf_size, "%d ", i);
LCIU_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LCIU_ADJUST_BUF_PTRS(used);
} else {
used = scnprintf(buf_pos, buf_size, "Chan free [ none ]\n");
LCIU_ADJUST_BUF_PTRS(used);
}
+lciu_buf_full:
used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
return used;
+} +static DEVICE_ATTR_RO(list_chan_inuse);
+static struct attribute *coresight_cti_channel_attrs[] = {
&dev_attr_trigin_attach.attr,
&dev_attr_trigin_detach.attr,
&dev_attr_trigout_attach.attr,
&dev_attr_trigout_detach.attr,
&dev_attr_gate_enable.attr,
&dev_attr_gate_disable.attr,
&dev_attr_chan_set.attr,
&dev_attr_chan_clear.attr,
&dev_attr_chan_pulse.attr,
&dev_attr_trig_filter_enable.attr,
&dev_attr_list_xtrigs.attr,
&dev_attr_reset_xtrigs.attr,
&dev_attr_list_chan_inuse.attr,
NULL,
+};
+/* coresight management registers */ +coresight_cti_reg(devaff0, CTIDEVAFF0); +coresight_cti_reg(devaff1, CTIDEVAFF1); +coresight_cti_reg(authstatus, CORESIGHT_AUTHSTATUS); +coresight_cti_reg(devarch, CORESIGHT_DEVARCH); +coresight_cti_reg(devid, CORESIGHT_DEVID); +coresight_cti_reg(devtype, CORESIGHT_DEVTYPE); +coresight_cti_reg(pidr0, CORESIGHT_PERIPHIDR0); +coresight_cti_reg(pidr1, CORESIGHT_PERIPHIDR1); +coresight_cti_reg(pidr2, CORESIGHT_PERIPHIDR2); +coresight_cti_reg(pidr3, CORESIGHT_PERIPHIDR3); +coresight_cti_reg(pidr4, CORESIGHT_PERIPHIDR4);
+static struct attribute *coresight_cti_mgmt_attrs[] = {
&dev_attr_devaff0.attr,
&dev_attr_devaff1.attr,
&dev_attr_authstatus.attr,
&dev_attr_devarch.attr,
&dev_attr_devid.attr,
&dev_attr_devtype.attr,
&dev_attr_pidr0.attr,
&dev_attr_pidr1.attr,
&dev_attr_pidr2.attr,
&dev_attr_pidr3.attr,
&dev_attr_pidr4.attr,
NULL,
+};
+static const struct attribute_group coresight_cti_group = {
.attrs = coresight_cti_attrs,
+};
+static const struct attribute_group coresight_cti_regs_group = {
.attrs = coresight_cti_regs_attrs,
.name = "regs",
+};
+static const struct attribute_group coresight_cti_channels_group = {
.attrs = coresight_cti_channel_attrs,
.name = "channels",
+};
+static const struct attribute_group coresight_cti_mgmt_group = {
.attrs = coresight_cti_mgmt_attrs,
.name = "mgmt",
+};
+const struct attribute_group *coresight_cti_groups[] = {
&coresight_cti_group,
&coresight_cti_regs_group,
&coresight_cti_channels_group,
&coresight_cti_mgmt_group,
NULL,
+};
+static int cti_starting_cpu(unsigned int cpu) +{
struct cti_drvdata *drvdata = cti_cpu_drv[cpu];
if (!drvdata)
return 0;
spin_lock(&drvdata->spinlock);
drvdata->config.hw_powered = true;
spin_unlock(&drvdata->spinlock);
if (atomic_read(&drvdata->config.enable_req_count))
cti_enable_hw(drvdata);
return 0;
+}
+static int cti_stopping_cpu(unsigned int cpu) +{
struct cti_drvdata *drvdata = cti_cpu_drv[cpu];
if (!drvdata)
return 0;
cti_disable_hw(drvdata);
spin_lock(&drvdata->spinlock);
drvdata->config.hw_powered = false;
spin_unlock(&drvdata->spinlock);
return 0;
+}
+static void cti_smp_set_powered(void *info) +{
struct cti_drvdata *drvdata = info;
drvdata->config.hw_powered = true;
+}
+/* setup power handling on a cti that has an association with a cpu */ +static int cti_setup_hp_cpu_affinity(struct cti_drvdata *drvdata) +{
int ret = 0;
/* CPU affinity - add to quick lookup array
* and register cpu hp callbacks first time out
*/
cpus_read_lock();
cti_cpu_drv[drvdata->ctidev.cpu] = drvdata;
if (!cti_cpu_count) {
cti_hp_online = cpuhp_setup_state_nocalls_cpuslocked(
CPUHP_AP_ONLINE_DYN, "arm/coresightcti:online",
cti_starting_cpu, cti_stopping_cpu);
if (cti_hp_online < 0) {
ret = cti_hp_online;
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
goto cti_hp_cpu_done;
}
}
cti_cpu_count++;
/* setup powered flag by running function of assoc cpu */
if (smp_call_function_single(drvdata->ctidev.cpu, cti_smp_set_powered,
drvdata, 1)) {
drvdata->config.hw_powered = false;
}
+cti_hp_cpu_done:
cpus_read_unlock();
return ret;
+}
+/* back out hotplug notifications on error */ +static void cti_clear_hp_cpu_affinity(struct cti_drvdata *drvdata) +{
if (!drvdata)
return;
if (drvdata->ctidev.cpu >= 0) {
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
cti_cpu_count--;
if ((cti_cpu_count == 0) && (cti_hp_online > 0))
cpuhp_remove_state_nocalls(cti_hp_online);
}
+}
+static int cti_probe(struct amba_device *adev, const struct amba_id *id) +{
int ret = 0;
u32 devid;
void __iomem *base;
struct device *dev = &adev->dev;
struct cti_drvdata *drvdata = 0;
struct coresight_desc cti_desc;
struct coresight_platform_data *pdata = NULL;
struct resource *res = &adev->res;
struct device_node *np = adev->dev.of_node;
struct ect_node *ect_nd = 0;
/* boilerplate code to set up the basics */
dev_info(dev, "%s(ID = %x)\n", __func__, id->id);
if (np) {
pdata = of_get_coresight_ect_platform_data(dev, np);
if (IS_ERR(pdata)) {
dev_info(dev, "of_get_coresight_ect_platform err\n");
ret = PTR_ERR(pdata);
goto err_out;
}
dev->platform_data = pdata;
}
/* node to keep track of CTI net */
ect_nd = kzalloc(sizeof(struct ect_node), GFP_KERNEL);
if (!ect_nd) {
ret = -ENOMEM;
goto err_out;
}
/* driver data*/
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata) {
ret = -ENOMEM;
dev_info(dev, "%s, mem err\n", __func__);
goto err_out;
If you go to err_out here ect_nd allocated above is leaked. The same applies to the reste of the 'goto err_out;' below.
}
/* links between dev and drvdata*/
drvdata->dev = dev;
dev_set_drvdata(dev, drvdata);
/* default CTI device info */
drvdata->ctidev.cpu = -1;
drvdata->ctidev.nr_trig_con = 0;
drvdata->ctidev.ctm_id = 0;
INIT_LIST_HEAD(&drvdata->ctidev.trig_cons);
/* Validity for the resource is already checked by the AMBA core */
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base)) {
ret = PTR_ERR(base);
dev_info(dev, "%s, remap err\n", __func__);
goto err_out;
}
drvdata->base = base;
spin_lock_init(&drvdata->spinlock);
/* look at the HW DEVID register for some of the HW settings */
devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
drvdata->config.nr_trig_max = (int)((devid & 0xFF00) >> 8);
/* no current hardware should exceed this, but protect the driver
* in case of fault / out of spec hw
*/
if (drvdata->config.nr_trig_max > CTIINOUTEN_MAX) {
dev_warn_once(dev,
"Limiting HW MaxTrig value(%d) to driver max(%d)\n",
drvdata->config.nr_trig_max, CTIINOUTEN_MAX);
drvdata->config.nr_trig_max = CTIINOUTEN_MAX;
}
drvdata->config.nr_ctm_channels = (int)((devid & 0xF0000) >> 16);
dev_info(dev, "DevID %08x, trigs=%d, chans=%d\n", devid,
drvdata->config.nr_trig_max, drvdata->config.nr_ctm_channels);
/** additional parse the .dts for connections and signals */
of_cti_get_hw_data(dev, np, drvdata);
cti_set_default_config(&drvdata->config);
/* setup cpu related CTI devices, otherwise assume powered */
if (drvdata->ctidev.cpu >= 0) {
ret = cti_setup_hp_cpu_affinity(drvdata);
if (ret < 0)
goto err_out;
} else
drvdata->config.hw_powered = true;
/* set up coresight component description */
cti_desc.pdata = pdata;
cti_desc.type = CORESIGHT_DEV_TYPE_ECT;
cti_desc.subtype.ect_subtype = CORESIGHT_DEV_SUBTYPE_ECT_CTI;
cti_desc.ops = &cti_ops;
cti_desc.groups = coresight_cti_groups;
cti_desc.dev = dev;
drvdata->csdev = coresight_register(&cti_desc);
if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
pr_err("%s: CS register failed\n", pdata->name);
goto err_out;
}
/* add to list of CTI devices */
mutex_lock(&ect_mutex);
ect_nd->cti_drv = drvdata;
list_add(&ect_nd->next, &ect_net);
mutex_unlock(&ect_mutex);
/* set any cross references */
cti_update_conn_xrefs(drvdata);
/* all done - dec pm refcount */
pm_runtime_put(&adev->dev);
dev_info(dev, "%s: initialized\n", pdata->name);
cti_count++;
dev_info(dev, "%s - ok\n", __func__);
return 0;
+err_out:
cti_clear_hp_cpu_affinity(drvdata);
dev_info(dev, "%s - err_out\n", __func__);
return ret;
+}
+/* free up CTI specific resources
- called from coresight_device_release on coresight_unregister.
- */
+void cti_device_release(struct device *dev) +{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct ect_node *ect_item, *ect_tmp;
struct cti_trig_con *tc, *tc_tmp;
/* free up resources associated with the cti connections */
if (drvdata->ctidev.cpu >= 0)
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
list_for_each_entry_safe(tc, tc_tmp,
&drvdata->ctidev.trig_cons, node) {
kfree(tc->con_in);
kfree(tc->con_out);
kfree(tc->con_dev_name);
list_del(&tc->node);
kfree(tc);
}
/* remove from the list */
mutex_lock(&ect_mutex);
list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, next) {
if (ect_item->cti_drv == drvdata) {
list_del(&ect_item->next);
kfree(ect_item);
goto ect_list_item_removed;
}
}
+ect_list_item_removed:
mutex_unlock(&ect_mutex);
kfree(drvdata);
+} +EXPORT_SYMBOL_GPL(cti_device_release);
+#define CTI_AMBA_ID(pid) \
{ \
.id = pid, \
.mask = 0x000fffff, \
}
+static struct amba_cs_uci_id uci_id_cti[] = {
{
/* CTI UCI data */
.devarch = 0x47701a14, /* devarch value for CTI v2 */
.devarch_mask = 0xfff0ffff,
.devtype = 0x00000014, /* maj(0x4-debug) min(0x1-ECT) */
}
+};
+#define CTI_AMBA_UCI_ID(pid) \
{ \
.id = pid, \
.mask = 0x000fffff, \
.data = uci_id_cti, \
}
+static const struct amba_id cti_ids[] = {
CTI_AMBA_ID(0x000bb906), /* Coresight CTI (SoC 400), C-A72, C-A57 */
CTI_AMBA_ID(0x000bb922), /* CTI - C-A8 */
CTI_AMBA_ID(0x000bb9a8), /* CTI - C-A53 */
CTI_AMBA_ID(0x000bb9aa), /* CTI - C-A73 */
CTI_AMBA_UCI_ID(0x000bb9da), /* CTI - C-A35 */
CTI_AMBA_UCI_ID(0x000bb9ed), /* Coresight CTI (SoC 600) */
CTI_AMBA_UCI_ID(0x000bb95d), /* test UCI - ETM PID A53 */
{ 0, 0},
+};
+static struct amba_driver cti_driver = {
.drv = {
.name = "coresight-cti",
.owner = THIS_MODULE,
.suppress_bind_attrs = true,
},
.probe = cti_probe,
.id_table = cti_ids,
+}; +builtin_amba_driver(cti_driver); diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h new file mode 100644 index 000000000000..09cb985ac0ea --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#ifndef _CORESIGHT_CORESIGHT_CTI_H +#define _CORESIGHT_CORESIGHT_CTI_H
+#include <asm/local.h> +#include <linux/spinlock.h> +#include "coresight-priv.h"
+/*
- Device registers
- 0x000 - 0x144: CTI programming and status
- 0xEDC - 0xEF8: CTI integration test.
- 0xF00 - 0xFFC: Coresight management registers.
- */
+/* CTI programming registers */ +#define CTICONTROL 0x000 +#define CTIINTACK 0x010 +#define CTIAPPSET 0x014 +#define CTIAPPCLEAR 0x018 +#define CTIAPPPULSE 0x01C +#define CTIINEN(n) (0x020 + (4 * n)) +#define CTIOUTEN(n) (0x0A0 + (4 * n)) +#define CTITRIGINSTATUS 0x130 +#define CTITRIGOUTSTATUS 0x134 +#define CTICHINSTATUS 0x138 +#define CTICHOUTSTATUS 0x13C +#define CTIGATE 0x140 +#define ASICCTL 0x144 +/* Integration test registers */ +#define ITCHINACK 0xEDC /* WO CTI CSSoc 400 only*/ +#define ITTRIGINACK 0xEE0 /* WO CTI CSSoc 400 only*/ +#define ITCHOUT 0xEE4 /* WO RW-600 */ +#define ITTRIGOUT 0xEE8 /* WO RW-600 */ +#define ITCHOUTACK 0xEEC /* RO CTI CSSoc 400 only*/ +#define ITTRIGOUTACK 0xEF0 /* RO CTI CSSoc 400 only*/ +#define ITCHIN 0xEF4 /* RO */ +#define ITTRIGIN 0xEF8 /* RO */ +/* management registers */ +#define CTIDEVAFF0 0xFA8 +#define CTIDEVAFF1 0xFAC
+/* CTI CSSoc 600 has a max of 32 trigger signals per direction.
- CTI CSSoc 400 has 8 IO triggers - other CTIs can be impl def.
- Max of in and out defined in the DEVID register.
- pick up actual number used from .dts parameters if present.
- */
+#define CTIINOUTEN_MAX 32
+/**
- Group of related trigger signals
- @nr_sigs: number of signals in the group.
- @used_mask: bitmask representing the signal indexes in the group.
- @sig_names: list of names for the signals.
- */
+struct cti_trig_grp {
int nr_sigs;
u32 used_mask;
char *sig_names;
+};
+/**
- Trigger connection - connection between a CTI and other (coresight) device
- lists input and output trigger signals for the device
- @con_in: connected CTIIN signals for the device.
- @con_out: connected CTIOUT signals for the device.
- @con_dev: coresight device connected to the CTI, NULL if not CS device
- @con_dev_name: name of connected device (CS or CPU)
- @node: entry node in list of connections.
- */
+struct cti_trig_con {
struct cti_trig_grp *con_in;
struct cti_trig_grp *con_out;
struct coresight_device *con_dev;
char *con_dev_name;
struct list_head node;
+};
+/**
- struct cti_device - description of CTI device properties.
- @nt_trig_con: Number of external devices connected to this device.
- @ctm_id: which CTM this device is connected to (by default it is
assumed there is a single CTM per SoC, ID 0).
- @trig_cons: list of connections to this device.
- @cpu: CPU ID if associated with CPU, -1 otherwise.
- */
+struct cti_device {
int nr_trig_con;
u32 ctm_id;
struct list_head trig_cons;
int cpu;
+};
+/**
- struct cti_config - configuration of the CTI device hardware
- hardware description from RO ID regs
- @nr_trig_max: Max number of trigger signals implemented on device.
(max of trig_in or trig_out)
- @nr_ctm_channels: number of available CTM channels
- cti enable control
- @enable_req_count: CTI is enabled alongside >=1 associated devices.
- @hw_enabled: true if hw is currently enabled.
- @hw_powered: true if associated cpu powered on, or no cpu.
- registered triggers and filtering
- @trig_in_use: bitfield of in triggers registered as in use.
- @trig_out_use: bitfield of out triggers registered as in use.
- @trig_out_filter: bitfield of out triggers that are blocked if filter
enabled. Typically this would be dbgreq / restart on a core CTI.
- @trig_filter_enable: 1 if filtering enabled.
- cti software programmable regs:
- @ctiappset: CTI Software application channel set.
- @ctiinout_sel: register selector for INEN and OUTEN regs.
- @ctiinen: enable input trigger to a channel.
- @ctiouten: enable output trigger from a channel.
- @ctigate: gate channel output from CTI to CTM.
- */
+struct cti_config {
/* hardware description */
int nr_ctm_channels;
int nr_trig_max;
/* cti enable control */
atomic_t enable_req_count;
bool hw_enabled;
bool hw_powered;
/* registered triggers and filtering */
u32 trig_in_use;
u32 trig_out_use;
u32 trig_out_filter;
int trig_filter_enable;
/* cti cross trig programmable regs */
u32 ctiappset;
u8 ctiinout_sel;
u32 ctiinen[CTIINOUTEN_MAX];
u32 ctiouten[CTIINOUTEN_MAX];
u32 ctigate;
u32 asicctl;
+};
+/**
- struct cti_drvdata - specifics for the CTI device
- @base: Memory mapped base address for this component.
- @dev: The device entity associated to this component.
- @csdev: Standard CoreSight device information.
- @ctidev: Extra information needed by the CTI/CTM framework.
- @spinlock: Control data access to one at a time.
- @config: Configuration data for this CTI device.
- */
+struct cti_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
struct cti_device ctidev;
spinlock_t spinlock;
struct cti_config config;
+};
+enum cti_chan_op {
CTI_CHAN_ATTACH,
CTI_CHAN_DETACH,
+};
+enum cti_trig_dir {
CTI_TRIG_IN,
CTI_TRIG_OUT,
+};
+enum cti_chan_gate_op {
CTI_GATE_CHAN_ENABLE,
CTI_GATE_CHAN_DISABLE,
CTI_GATE_CHAN_ENABLE_ALL,
CTI_GATE_CHAN_DISABLE_ALL,
+};
+enum cti_chan_set_op {
CTI_CHAN_SET,
CTI_CHAN_CLR,
CTI_CHAN_PULSE,
+};
+#endif /* _CORESIGHT_CORESIGHT_CTI_H */
2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK
On Wed, Jan 09, 2019 at 10:54:38PM +0000, Mike Leach wrote:
Initial addition of the files for the Coresight CTI driver.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-cti.c | 2025 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 195 ++ 2 files changed, 2220 insertions(+) create mode 100644 drivers/hwtracing/coresight/coresight-cti.c create mode 100644 drivers/hwtracing/coresight/coresight-cti.h
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c new file mode 100644 index 000000000000..d06e19f111e2 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -0,0 +1,2025 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/smp.h> +#include <linux/sysfs.h> +#include <linux/stat.h> +#include <linux/clk.h> +#include <linux/cpu.h> +#include <linux/coresight.h> +#include <linux/coresight-pmu.h> +#include <linux/pm_wakeup.h> +#include <linux/amba/bus.h> +#include <linux/seq_file.h> +#include <linux/uaccess.h> +#include <linux/perf_event.h> +#include <linux/pm_runtime.h> +#include <linux/of.h> +#include <asm/sections.h> +#include <asm/local.h>
+#include "coresight-cti.h"
+#define csdev_to_cti_drvdata(csdev) \
- dev_get_drvdata(csdev->dev.parent)
+/* CTI device tree trigger connection node keyword */ +#define CTI_DT_CONNS "trig-conns" +/* CTI device tree connection property keywords */ +#define CTI_DT_V8ARCH "arm,cti-v8-arch" +#define CTI_DT_TRIGIN_SIGS "arm,trig-in-sigs" +#define CTI_DT_TRIGOUT_SIGS "arm,trig-out-sigs" +#define CTI_DT_FILTER_OUT_SIGS "arm,trig-filters" +#define CTI_DT_CSDEV_ASSOC "arm,cs-dev-assoc" +#define CTI_DT_CTM_ID "arm,cti-ctm-id" +#define CTI_DT_CONN_NAME "arm,trig-conn-name"
+/**
- CTI devices can be associated with a PE, or be connected to CoreSight
- hardware. We have a list of all CTIs, and a subset array associated
- with individual PEs for pwr management as they will power up in the
- PE power domain.
- At this point we assume that the none CPU CTIs are always powered as
- we do with sinks etc.
- We leave the client to figure out if all the CTIs are interconnected with
- the same CTM, in general this is the case but does not always have to be.
- */
+struct ect_node {
- struct cti_drvdata *cti_drv;
- struct list_head next;
+};
+/* net of CTI devices connected via CTM */ +LIST_HEAD(ect_net);
+/* protect the list */ +static DEFINE_MUTEX(ect_mutex);
+/* quick reference for CPU CTIs */ +static struct cti_drvdata *cti_cpu_drv[NR_CPUS];
+/* number of registered CTI devices */ +static int cti_count;
+/* number of cpu associated CTI devices in use */ +static int cti_cpu_count;
+/* CTI / cpu hotplug notification ID */ +static enum cpuhp_state cti_hp_online;
+/* cpu powered and enabled */ +#define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered)
+/* write set of regs to hardware - call with spinlock claimed */ +static void cti_write_all_hw_regs(struct cti_drvdata *drvdata) +{
- struct cti_config *config = &drvdata->config;
- int i;
- CS_UNLOCK(drvdata->base);
- /* disable CTI before writing registers */
- writel_relaxed(0, drvdata->base + CTICONTROL);
- /* write the CTI trigger registers */
- for (i = 0; i < config->nr_trig_max; i++) {
writel_relaxed(config->ctiinen[i], drvdata->base + CTIINEN(i));
writel_relaxed(config->ctiouten[i],
drvdata->base + CTIOUTEN(i));
Indentation problem.
- }
- /* other regs */
- writel_relaxed(config->ctigate, drvdata->base + CTIGATE);
- writel_relaxed(config->asicctl, drvdata->base + ASICCTL);
- writel_relaxed(config->ctiappset, drvdata->base + CTIAPPSET);
- /* re-enable CTI */
- writel_relaxed(1, drvdata->base + CTICONTROL);
- CS_LOCK(drvdata->base);
+}
+static void cti_enable_hw_smp_call(void *info) +{
- struct cti_drvdata *drvdata = info;
- cti_write_all_hw_regs(drvdata);
+}
+/* write regs to hardware and enable */ +static int cti_enable_hw(void *info) +{
- struct cti_drvdata *drvdata = info;
- struct cti_config *config = &drvdata->config;
- int rc = 0;
- pm_runtime_get_sync(drvdata->dev);
- spin_lock(&drvdata->spinlock);
- /* no need to do anything if enabled or unpowered*/
- if (config->hw_enabled || !config->hw_powered) {
spin_unlock(&drvdata->spinlock);
pm_runtime_put(drvdata->dev);
return 0;
- }
- if (drvdata->ctidev.cpu >= 0) {
dev_info(drvdata->dev, "cti enable smp call for cpu %d\n",
drvdata->ctidev.cpu);
rc = smp_call_function_single(drvdata->ctidev.cpu,
cti_enable_hw_smp_call,
drvdata, 1);
- } else {
dev_info(drvdata->dev, "cti enable not cpu call\n");
cti_write_all_hw_regs(drvdata);
- }
- if (!rc)
config->hw_enabled = true;
- spin_unlock(&drvdata->spinlock);
- return 0;
+}
+/* disable hardware */ +static int cti_disable_hw(void *info) +{
- struct cti_drvdata *drvdata = info;
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- /* no need to do anything if disabled or cpu unpowered*/
- if (!config->hw_enabled || !config->hw_powered) {
spin_unlock(&drvdata->spinlock);
return 0;
- }
- CS_UNLOCK(drvdata->base);
- /* disable CTI */
- writel_relaxed(0, drvdata->base + CTICONTROL);
- config->hw_enabled = false;
- CS_LOCK(drvdata->base);
- spin_unlock(&drvdata->spinlock);
- pm_runtime_put(drvdata->dev);
- return 0;
+}
+static void +cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value) +{
- CS_UNLOCK(drvdata->base);
- writel_relaxed(value, drvdata->base+offset);
- CS_LOCK(drvdata->base);
+}
+static void cti_set_default_config(struct cti_config *config) +{
- /* Most regs default to 0 as zalloc'ed except...*/
- config->trig_filter_enable = 1;
- config->ctigate = ((u32)0x1 << config->nr_ctm_channels) - 1;
Please describe what you are doing, i.e configuring all the channels to propagate to its connected CTM by setting the channel bits to '1'.
- atomic_set(&config->enable_req_count, 0);
+}
+static void cti_write_intack(struct device *dev, u32 ackval) +{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- /* write if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIINTACK, ackval);
- spin_unlock(&drvdata->spinlock);
+}
+/* need to update cross references once CTI csdev has been generated. */ +static void cti_update_conn_xrefs(struct cti_drvdata *drvdata) +{
- struct cti_trig_con *tc;
- struct cti_device *ctidev = &drvdata->ctidev;
- list_for_each_entry(tc, &ctidev->trig_cons, node) {
if (tc->con_dev) {
tc->con_dev->ect_dev = drvdata->csdev;
dev_info(drvdata->dev,
"Setting assoc csdev (%px) with (%px)\n",
tc->con_dev, drvdata->csdev);
}
- }
+}
+/* connection information */ +static int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name)
+{
- struct cti_trig_con *tc;
- struct cti_device *cti_dev = &drvdata->ctidev;
- tc = kzalloc(sizeof(struct cti_trig_con), GFP_KERNEL);
- if (tc == 0)
return -ENOMEM;
- tc->con_dev = csdev;
- /* prefer actual associated CS device dev name to supplied value -
* which is likely to be node name / other conn name
*/
- if (csdev)
tc->con_dev_name = kstrdup(dev_name(&csdev->dev), GFP_KERNEL);
- else if (assoc_dev_name != NULL)
tc->con_dev_name = kstrdup(assoc_dev_name, GFP_KERNEL);
- tc->con_in = in_info;
- tc->con_out = out_info;
- list_add_tail(&tc->node, &cti_dev->trig_cons);
- cti_dev->nr_trig_con++;
- /* add connection usage bit info to overall info */
- drvdata->config.trig_in_use |= in_info->used_mask;
- drvdata->config.trig_out_use |= out_info->used_mask;
- return 0;
+}
+/* add a default connection if nothing else is specified.
- single connection based on max in/out info, no assoc device
- */
+static int cti_add_default_connection(struct cti_drvdata *drvdata) +{
- struct cti_trig_grp *in, *out;
- int n_trigs = drvdata->config.nr_trig_max;
- u32 n_trig_mask = (0x1 << n_trigs) - 1;
- in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!in)
return -ENOMEM;
- out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!out)
return -ENOMEM;
- dev_info(drvdata->dev, "default connection: trigs %d, trig_mask %08x\n",
n_trigs, n_trig_mask);
- /* assume max trigs for in and out, all used */
- in->nr_sigs = n_trigs;
- in->used_mask = n_trig_mask;
- in->sig_names = NULL;
- out->nr_sigs = n_trigs;
- out->used_mask = n_trig_mask;
- out->sig_names = NULL;
- return cti_add_connection_entry(drvdata, in, out, NULL, "default");
+}
+/* create an architecturally defined v8 connection
- must hava a cpu, can have an ETM
- */
+static int of_cti_create_v8_connections(struct cti_drvdata *drvdata,
struct device_node *np)
+{
- struct cti_device *cti_dev = &drvdata->ctidev;
- struct cti_trig_grp *in, *out;
- int cpuid = 0;
- struct device_node *cs_np;
- struct coresight_device *csdev = NULL;
- char cpu_name_str[16];
- const char *assoc_name = NULL;
- cpuid = of_coresight_ect_get_cpu(np);
- if (cpuid < 0) {
dev_warn(drvdata->dev, "CTI v8 DT binding no cpu\n");
return -EINVAL;
- }
- cti_dev->cpu = cpuid;
- /* do the v8 cpu connection */
- in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!in)
return -ENOMEM;
- out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!out)
return -ENOMEM;
- /* v8 PE CTI config... */
- in->nr_sigs = 3;
- in->used_mask = 0x7;
- in->sig_names = NULL;
- out->nr_sigs = 3;
- out->used_mask = 0x7;
- out->sig_names = NULL;
- scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
- cti_add_connection_entry(drvdata, in, out, NULL, cpu_name_str);
- drvdata->config.trig_out_filter = 0x1; /* filter dbgreq */
- /* v8 ETM associated config... */
- cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
- if (cs_np) {
in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!in)
return -ENOMEM;
out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!out)
return -ENOMEM;
in->nr_sigs = 4;
in->used_mask = 0xF0;
in->sig_names = NULL;
out->nr_sigs = 4;
out->used_mask = 0xF0;
out->sig_names = NULL;
dev_info(drvdata->dev, "v8 finding assoc dev %s\n",
cs_np->full_name);
csdev = of_coresight_get_cs_device_by_node(cs_np);
if (csdev)
assoc_name = dev_name(&csdev->dev);
else
assoc_name = cs_np->full_name;
cti_add_connection_entry(drvdata, in, out, csdev, assoc_name);
of_node_put(cs_np);
- }
- return 0;
+}
+static int of_cti_read_trig_group(struct cti_trig_grp **trig_grp_ptr,
struct device_node *np,
const char *grp_name,
int max_trigs)
+{
- int items, err = 0;
- u32 value, pidx;
- struct cti_trig_grp *grp;
- grp = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!grp)
return -ENOMEM;
- *trig_grp_ptr = grp;
- items = of_property_count_elems_of_size(np, grp_name, 4);
- /* if the property doesn't exist or has no values, then return
* an empty connection group
*/
- if (items < 0)
return 0;
- if (items > max_trigs)
return -EINVAL;
- /* set the number of signals and usage mask */
- for (pidx = 0; pidx < items; pidx++) {
err = of_property_read_u32_index(np, grp_name, pidx, &value);
if (err)
return err;
grp->nr_sigs++;
grp->used_mask |= (0x1 << value);
- }
- return 0;
+}
+static int of_cti_create_connection(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
- struct cti_trig_grp *in = 0, *out = 0, *filter = 0;
- int cpuid = -1, err = 0, trig_max = drvdata->config.nr_trig_max;
- struct device_node *cs_np;
- struct coresight_device *csdev = NULL;
- const char *assoc_name = "unknown";
- char cpu_name_str[16];
- /* look for the signals properties. */
- err = of_cti_read_trig_group(&in, np, CTI_DT_TRIGIN_SIGS, trig_max);
- if (err)
return err;
- err = of_cti_read_trig_group(&out, np, CTI_DT_TRIGOUT_SIGS, trig_max);
- if (err)
return err;
- err = of_cti_read_trig_group(&filter, np, CTI_DT_FILTER_OUT_SIGS,
trig_max);
- if (err)
return err;
- /* read the connection name if set - may be overridden by later */
- of_property_read_string(np, CTI_DT_CONN_NAME, &assoc_name);
- /* associated cpu ? */
- cpuid = of_coresight_ect_get_cpu(np);
- drvdata->ctidev.cpu = cpuid;
- if (cpuid >= 0) {
scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
assoc_name = cpu_name_str;
- } else {
/* associated device ? */
cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
if (cs_np) {
dev_info(dev, "finding assoc dev %s\n",
cs_np->full_name);
csdev = of_coresight_get_cs_device_by_node(cs_np);
if (csdev) /* use device name if csdev found */
assoc_name = dev_name(&csdev->dev);
else /* otherwise node name for later association */
assoc_name = cs_np->full_name;
of_node_put(cs_np);
}
- }
- /* set up a connection */
- err = cti_add_connection_entry(drvdata, in, out, csdev, assoc_name);
- /* note any filter info */
- if (!err)
drvdata->config.trig_out_filter |= filter->used_mask;
- kfree(filter);
- return err;
+}
+/* get the hardware configuration & connection data. */ +static int of_cti_get_hw_data(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
- int rc = 0;
- struct cti_device *cti_dev = &drvdata->ctidev;
- struct device_node *nc = NULL;
- /* get any CTM ID - defaults to 0 */
- of_property_read_u32(np, CTI_DT_CTM_ID, &cti_dev->ctm_id);
- if (of_property_read_bool(np, CTI_DT_V8ARCH)) {
rc = of_cti_create_v8_connections(drvdata, np);
- } else {
for_each_child_of_node(np, nc) {
if (of_node_cmp(nc->name, CTI_DT_CONNS) != 0)
continue;
dev_info(dev, "dt found node: %s %s\n",
nc->name, nc->full_name);
rc = of_cti_create_connection(dev, nc, drvdata);
if (rc != 0)
return rc;
}
- }
- /* if no connections, just add a single default based on max IN-OUT */
- if (cti_dev->nr_trig_con == 0)
rc = cti_add_default_connection(drvdata);
- return rc;
This function could be done in 3 patch (along with introduction of accompanying code).
1) default connections 2) explicit connections 3) v8 connections
+}
+static int +cti_match_con_name(struct cti_device *ctidev, const char *node_name,
const char *csdev_name)
+{
- int found = 0;
- struct cti_trig_con *trig_con;
- list_for_each_entry(trig_con, &ctidev->trig_cons, node) {
if (trig_con->con_dev_name) {
if (!strcmp(node_name, trig_con->con_dev_name)) {
found = 1;
/* match: so swap in csdev name */
kfree(trig_con->con_dev_name);
trig_con->con_dev_name =
kstrdup(csdev_name, GFP_KERNEL);
goto cti_con_name_match;
}
}
- }
+cti_con_name_match:
- return found;
+}
+/* search the cti list to add an associated CTI into the supplied CS device
- This will set the association if CTI declared before the CS device
- */
+int cti_add_assoc_to_csdev(struct coresight_device *csdev) +{
- struct ect_node *ect_item;
- struct cti_device *ctidev;
- const char *node_name = NULL, *csdev_name;
- /* exit early for no CTIs or self referencing ECT devices.*/
- if (!cti_count) {
dev_info(&csdev->dev, "no CTI to check\n");
return 0;
- }
- if (!csdev->dev.parent->of_node) {
dev_info(&csdev->dev, "No parent of_node pointer\n");
Have you seen this happening? The parent of a csdevice is the amba device, so if we are here parent->of_node must exist...
return 0;
- }
- node_name = csdev->dev.parent->of_node->full_name;
- if (!node_name) {
dev_info(&csdev->dev, "%s - bad node name\n", __func__);
return 0;
- }
I'm not sure that check too is required but you may have seen otherwise. If that is the case add a comment that mentions why it is needed.
- dev_info(&csdev->dev, "%s - looking for %s\n", __func__, node_name);
- if (!cti_count || (csdev->type == CORESIGHT_DEV_TYPE_ECT)) {
!cti_count has already been checked above.
dev_info(&csdev->dev, "no CTI or skip self ref CTI\n");
return 0;
- }
- csdev_name = dev_name(&csdev->dev);
- /* for each CTI in list... */
- mutex_lock(&ect_mutex);
- list_for_each_entry(ect_item, &ect_net, next) {
ctidev = &ect_item->cti_drv->ctidev;
if (cti_match_con_name(ctidev, node_name, csdev_name)) {
csdev->ect_dev = ect_item->cti_drv->csdev;
dev_info(&csdev->dev, "Found!\n");
goto cti_found;
}
- }
+cti_found:
- mutex_unlock(&ect_mutex);
- return 0;
+} +EXPORT_SYMBOL_GPL(cti_add_assoc_to_csdev);
+/* attach/detach channel from trigger - write through if enabled. */ +static int cti_channel_trig_op(struct device *dev,
enum cti_chan_op op,
enum cti_trig_dir direction,
u32 channel_idx,
u32 trigger_idx)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- u32 trig_bitmask;
- u32 chan_bitmask;
- u32 reg_value;
- int reg_offset;
- dev_info(dev, "chan_trig_op: op %d, dir %d, chan %d, trig %d\n",
op, direction, channel_idx, trigger_idx);
- /* ensure indexes in range */
- if ((channel_idx >= config->nr_ctm_channels) ||
(trigger_idx >= config->nr_trig_max))
return -EINVAL;
- trig_bitmask = 0x1 << trigger_idx;
- /* ensure registered triggers and not out filtered */
- if (direction == CTI_TRIG_IN) {
if (!(trig_bitmask & config->trig_in_use))
return -EINVAL;
- } else {
if (!(trig_bitmask & config->trig_out_use))
return -EINVAL;
if ((config->trig_filter_enable) &&
(config->trig_out_filter & trig_bitmask))
return -EINVAL;
- }
- /* update the local register values */
- chan_bitmask = 0x1 << channel_idx;
- reg_offset = (direction == CTI_TRIG_IN ? CTIINEN(trigger_idx) :
CTIOUTEN(trigger_idx));
- spin_lock(&drvdata->spinlock);
- /* read - modify write - the trigger / channel enable value */
- reg_value = (direction == CTI_TRIG_IN ? config->ctiinen[trigger_idx] :
config->ctiouten[trigger_idx]);
- reg_value = (op == CTI_CHAN_ATTACH) ? reg_value | chan_bitmask :
reg_value & (~chan_bitmask);
- /* write local copy */
- if (direction == CTI_TRIG_IN)
config->ctiinen[trigger_idx] = reg_value;
- else
config->ctiouten[trigger_idx] = reg_value;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
- spin_unlock(&drvdata->spinlock);
- return 0;
+}
+static int cti_channel_gate_op(struct device *dev,
enum cti_chan_gate_op op,
u32 channel_idx)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- u32 chan_bitmask;
- u32 reg_value;
- int err = 0;
- if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
- chan_bitmask = 0x1 << channel_idx;
- spin_lock(&drvdata->spinlock);
- reg_value = config->ctigate;
- switch (op) {
- case CTI_GATE_CHAN_ENABLE:
reg_value |= chan_bitmask;
break;
- case CTI_GATE_CHAN_DISABLE:
reg_value &= ~chan_bitmask;
break;
- case CTI_GATE_CHAN_ENABLE_ALL:
reg_value = (0x1 << config->nr_ctm_channels) - 1;
break;
- case CTI_GATE_CHAN_DISABLE_ALL:
reg_value = 0x0;
break;
- default:
err = -EINVAL;
break;
- }
- if (err == 0) {
config->ctigate = reg_value;
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIGATE, reg_value);
- }
- spin_unlock(&drvdata->spinlock);
- return err;
+}
+static int cti_channel_setop(struct device *dev,
enum cti_chan_set_op op,
u32 channel_idx)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- u32 chan_bitmask;
- u32 reg_value;
- u32 reg_offset;
- int err = 0;
- if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
- chan_bitmask = 0x1 << channel_idx;
- spin_lock(&drvdata->spinlock);
- reg_value = config->ctiappset;
- switch (op) {
- case CTI_CHAN_SET:
config->ctiappset |= chan_bitmask;
reg_value = config->ctiappset;
reg_offset = CTIAPPSET;
break;
- case CTI_CHAN_CLR:
config->ctiappset &= ~chan_bitmask;
reg_value = chan_bitmask;
reg_offset = CTIAPPCLEAR;
break;
- case CTI_CHAN_PULSE:
config->ctiappset &= ~chan_bitmask;
reg_value = chan_bitmask;
reg_offset = CTIAPPPULSE;
break;
- default:
err = -EINVAL;
break;
- }
- if ((err == 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
- spin_unlock(&drvdata->spinlock);
- return err;
+}
+/** cti ect operations **/ +static int cti_enable(struct coresight_device *csdev, void *__unused) +{
- int rc;
- struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
- atomic_inc(&drvdata->config.enable_req_count);
- rc = cti_enable_hw(drvdata);
- if (rc)
atomic_dec(&drvdata->config.enable_req_count);
- return rc;
+}
+static int cti_disable(struct coresight_device *csdev, void *__unused) +{
- int rc = 0;
- struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
- if (!atomic_dec_return(&drvdata->config.enable_req_count)) {
rc = cti_disable_hw(drvdata);
if (rc)
atomic_inc(&drvdata->config.enable_req_count);
- }
- return rc;
+}
+const struct coresight_ops_ect cti_ops_ect = {
- .enable = cti_enable,
- .disable = cti_disable,
+};
+const struct coresight_ops cti_ops = {
- .ect_ops = &cti_ops_ect,
+};
+/** attributes **/
+#define coresight_cti_reg(name, offset) \
- coresight_simple_reg32(struct cti_drvdata, name, offset)
+/* show a simple 32 bit value. if pval is NULL then live read,
- otherwise read from supplied pointer only
- */
+static ssize_t cti_reg32_show(struct device *dev, char *buf,
u32 *pval, int reg_offset)
+{
- unsigned long val = 0;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- if (pval) {
val = (unsigned long)*pval;
- } else if ((reg_offset >= 0) && CTI_PWR_ENA(config)) {
CS_UNLOCK(drvdata->base);
val = readl_relaxed(drvdata->base + reg_offset);
CS_LOCK(drvdata->base);
- }
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+/* store a simple 32 bit value.
- if pval not NULL, then copy to here too,
- if reg_offset >= 0 then write through if enabled.
- */
+static ssize_t cti_reg32_store(struct device *dev, const char *buf,
size_t size, u32 *pval,
int reg_offset)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- /* local store */
- if (pval)
*pval = (u32)val;
- /* write through of offset and enabled */
- if ((reg_offset >= 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, val);
- spin_unlock(&drvdata->spinlock);
- return size;
+}
+/* basic attributes */ +static ssize_t enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- int enable_req;
- bool enabled, powered, cpuid;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- ssize_t size = 0;
- enable_req = atomic_read(&drvdata->config.enable_req_count);
- spin_lock(&drvdata->spinlock);
- powered = drvdata->config.hw_powered;
- enabled = drvdata->config.hw_enabled;
- cpuid = drvdata->ctidev.cpu;
- spin_unlock(&drvdata->spinlock);
- if (powered) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled", cpuid);
- } else if (cpuid >= 0) {
CC kernel/module.o CC drivers/hwtracing/coresight/coresight-cti.o /home/mpoirier/work/coresight/kernel-maint/drivers/hwtracing/coresight/coresight-cti.c: In function ‘enable_show’: /home/mpoirier/work/coresight/kernel-maint/drivers/hwtracing/coresight/coresight-cti.c:831:19: warning: comparison of constant ‘0’ with boolean expression is always true [-Wbool-compare] } else if (cpuid >= 0) { ^~ CC drivers/net/ethernet/hisilicon/hns3/hns3_enet.o
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req", cpuid);
- } else {
size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n",
enabled ? "enabled" : "disabled");
- }
- return size;
+}
+static ssize_t enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int ret = 0;
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- ret = kstrtoul(buf, 16, &val);
- if (ret)
return ret;
- if (val)
ret = cti_enable(drvdata->csdev, NULL);
- else
ret = cti_disable(drvdata->csdev, NULL);
- if (ret)
return ret;
- return size;
+} +static DEVICE_ATTR_RW(enable);
+static ssize_t ctmid_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return scnprintf(buf, PAGE_SIZE, "0x%x\n", drvdata->ctidev.ctm_id);
+} +static DEVICE_ATTR_RO(ctmid);
+#define LF_ADJUST_BUF_PTRS(n) \
- do { buf_pos += n; buf_size -= n; \
- if (buf_size <= 1) \
goto lf_buf_full; \
- } while (0)
+static ssize_t list_features_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- int used = 0, sig_idx, con_idx, nr_trig_max;
- /* buffer vars */
- int buf_size = PAGE_SIZE;
- char *buf_pos = buf;
- char *con_name;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *cfg = &drvdata->config;
- struct cti_device *ctidev = &drvdata->ctidev;
- u32 sig_mask;
- struct cti_trig_con *con;
- /* basic feature info */
- used = scnprintf(buf_pos, buf_size,
"CTI:%s; Channels:%d; Max Trigs:%d\n",
dev_name(dev), cfg->nr_ctm_channels, cfg->nr_trig_max);
- LF_ADJUST_BUF_PTRS(used);
- used = scnprintf(buf_pos, buf_size, "CTM id = %d; Num Conns = %d\n",
ctidev->ctm_id, ctidev->nr_trig_con);
- LF_ADJUST_BUF_PTRS(used);
- /* list each connection info */
- con_idx = 0;
- nr_trig_max = cfg->nr_trig_max;
- list_for_each_entry(con, &ctidev->trig_cons, node) {
/* connection name */
if (con->con_dev_name != NULL)
con_name = con->con_dev_name;
else
con_name = "unknown";
used = scnprintf(buf_pos, buf_size, "conn(%d:%s)\n",
con_idx, con_name);
LF_ADJUST_BUF_PTRS(used);
con_idx++;
/* connection signals */
used = scnprintf(buf_pos, buf_size, "Trig IN(%d) [ ",
con->con_in->nr_sigs);
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & con->con_in->used_mask) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
used = scnprintf(buf_pos, buf_size, "Trig OUT(%d) [ ",
con->con_out->nr_sigs);
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & con->con_out->used_mask) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
- }
- if (cfg->trig_out_filter) {
used = scnprintf(buf_pos, buf_size, "Trig OUT_FILTERED [ ");
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & cfg->trig_out_filter) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
- }
+lf_buf_full:
- used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
- return used;
+} +static DEVICE_ATTR_RO(list_features);
+static struct attribute *coresight_cti_attrs[] = {
- &dev_attr_enable.attr,
- &dev_attr_list_features.attr,
- &dev_attr_ctmid.attr,
- NULL,
+};
+/* raw register attributes */ +static ssize_t inout_sel_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- u32 val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- val = (u32)drvdata->config.ctiinout_sel;
- return scnprintf(buf, PAGE_SIZE, "%#x\n", val);
+}
+static ssize_t inout_sel_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- if (val > (CTIINOUTEN_MAX-1))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- drvdata->config.ctiinout_sel = val;
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(inout_sel);
+static ssize_t inen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- spin_lock(&drvdata->spinlock);
- index = drvdata->config.ctiinout_sel;
- val = drvdata->config.ctiinen[index];
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "INEN%d %#lx\n", index, val);
+}
+static ssize_t inen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- index = config->ctiinout_sel;
- config->ctiinen[index] = val;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIINEN(index), val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(inen);
+static ssize_t outen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- spin_lock(&drvdata->spinlock);
- index = drvdata->config.ctiinout_sel;
- val = drvdata->config.ctiouten[index];
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "OUTEN%d %#lx\n", index, val);
+}
+static ssize_t outen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- index = config->ctiinout_sel;
- config->ctiouten[index] = val;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIOUTEN(index), val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(outen);
+static ssize_t gate_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_show(dev, buf, &drvdata->config.ctigate, -1);
+}
+static ssize_t gate_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_store(dev, buf, size,
&drvdata->config.ctigate, CTIGATE);
+} +static DEVICE_ATTR_RW(gate);
+static ssize_t asicctl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_show(dev, buf, &drvdata->config.asicctl, -1);
+}
+static ssize_t asicctl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_store(dev, buf, size,
&drvdata->config.asicctl, ASICCTL);
+} +static DEVICE_ATTR_RW(asicctl);
+static ssize_t intack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- cti_write_intack(dev, val);
- return size;
+} +static DEVICE_ATTR_WO(intack);
+static ssize_t appset_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_show(dev, buf, &drvdata->config.ctiappset, -1);
+}
+static ssize_t appset_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_store(dev, buf, size,
&drvdata->config.ctiappset, CTIAPPSET);
+} +static DEVICE_ATTR_RW(appset);
+static ssize_t appclear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val, mask;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- /* a 1'b1 in appclr clears down the same bit in appset*/
- mask = ~val;
- config->ctiappset &= mask;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPCLEAR, val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_WO(appclear);
+static ssize_t apppulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val, mask;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- /* a 1'b1 in apppulse sets then clears the bit,
* effectively clears down the same bit in appset
*/
- mask = ~val;
- config->ctiappset &= mask;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPPULSE, val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_WO(apppulse);
+static ssize_t itchout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return cti_reg32_show(dev, buf, NULL, ITCHOUT);
+}
+static ssize_t itchout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITCHOUT);
+} +static DEVICE_ATTR_RW(itchout);
+static ssize_t ittrigout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return cti_reg32_show(dev, buf, NULL, ITTRIGOUT);
+}
+static ssize_t ittrigout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITTRIGOUT);
+} +static DEVICE_ATTR_RW(ittrigout);
+static ssize_t itchinack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITCHINACK);
+} +static DEVICE_ATTR_WO(itchinack);
+static ssize_t ittriginack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITTRIGINACK);
+} +static DEVICE_ATTR_WO(ittriginack);
+static ssize_t itctrl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return cti_reg32_show(dev, buf, NULL, CORESIGHT_ITCTRL);
+}
+static ssize_t itctrl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, CORESIGHT_ITCTRL);
+} +static DEVICE_ATTR_RW(itctrl);
+coresight_cti_reg(triginstatus, CTITRIGINSTATUS); +coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS); +coresight_cti_reg(chinstatus, CTICHINSTATUS); +coresight_cti_reg(choutstatus, CTICHOUTSTATUS); +coresight_cti_reg(ittrigin, ITTRIGIN); +coresight_cti_reg(itchin, ITCHIN); +coresight_cti_reg(itchoutack, ITCHOUTACK); +coresight_cti_reg(ittrigoutack, ITTRIGOUTACK);
+static struct attribute *coresight_cti_regs_attrs[] = {
- &dev_attr_inout_sel.attr,
- &dev_attr_inen.attr,
- &dev_attr_outen.attr,
- &dev_attr_gate.attr,
- &dev_attr_asicctl.attr,
- &dev_attr_intack.attr,
- &dev_attr_appset.attr,
- &dev_attr_appclear.attr,
- &dev_attr_apppulse.attr,
- &dev_attr_triginstatus.attr,
- &dev_attr_trigoutstatus.attr,
- &dev_attr_chinstatus.attr,
- &dev_attr_choutstatus.attr,
- &dev_attr_itctrl.attr,
- &dev_attr_ittrigin.attr,
- &dev_attr_itchin.attr,
- &dev_attr_ittrigout.attr,
- &dev_attr_itchout.attr,
- &dev_attr_itchoutack.attr,
- &dev_attr_ittrigoutack.attr,
- &dev_attr_ittriginack.attr,
- &dev_attr_itchinack.attr,
- NULL,
+};
+/* channel / trigger api */ +static int +cti_trig_op_parse(struct device *dev, enum cti_chan_op op,
enum cti_trig_dir dir, const char *buf, size_t size)
+{
- u32 chan_idx;
- u32 trig_idx;
- int items, err = size;
- /* extract chan idx and trigger idx */
- items = sscanf(buf, "%d %d", &chan_idx, &trig_idx);
- if (items) {
err = cti_channel_trig_op(dev, op, dir, chan_idx, trig_idx);
if (!err)
err = size;
- } else
err = -EINVAL;
- return err;
+}
+static ssize_t trigin_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_attach);
+static ssize_t trigin_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_detach);
+static ssize_t trigout_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_attach);
+static ssize_t trigout_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_detach);
+static ssize_t gate_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = 0, channel = 0, items;
- if (strcmp(buf, "all") == 0) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE_ALL, 0);
- } else {
items = sscanf(buf, "%d", &channel);
if (!items)
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE, 0);
- }
- return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_enable);
+static ssize_t gate_disable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = 0, channel = 0, items;
- if (strcmp(buf, "all") == 0) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE_ALL, 0);
- } else {
items = sscanf(buf, "%d", &channel);
if (!items)
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE, 0);
- }
- return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_disable);
+static int +chan_op_parse(struct device *dev, enum cti_chan_set_op op, const char *buf) +{
- int err = 0, channel = 0, items;
- items = sscanf(buf, "%d", &channel);
- if (!items)
return -EINVAL;
- err = cti_channel_setop(dev, op, channel);
- return err;
+}
+static ssize_t chan_set_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = chan_op_parse(dev, CTI_CHAN_SET, buf);
- return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_set);
+static ssize_t chan_clear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = chan_op_parse(dev, CTI_CHAN_CLR, buf);
- return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_clear);
+static ssize_t chan_pulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = chan_op_parse(dev, CTI_CHAN_PULSE, buf);
- return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_pulse);
+static ssize_t trig_filter_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- spin_lock(&drvdata->spinlock);
- val = drvdata->config.trig_filter_enable;
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "%ld (%s)\n", val,
val ? "enabled" : "disabled");
+}
+static ssize_t trig_filter_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- drvdata->config.trig_filter_enable = val ? 1 : 0;
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(trig_filter_enable);
+/* clear all xtrigger / channel programming */ +static ssize_t reset_xtrigs_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int i;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- /* clear the CTI trigger / channel programming registers */
- for (i = 0; i < config->nr_trig_max; i++) {
config->ctiinen[i] = 0;
config->ctiouten[i] = 0;
- }
- /* clear the other regs */
- config->ctigate = (0x1 << config->nr_ctm_channels) - 1;
- config->asicctl = 0;
- config->ctiappset = 0;
- config->ctiinout_sel = 0;
- /* if enabled then write through */
- if (CTI_PWR_ENA(config))
cti_write_all_hw_regs(drvdata);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_WO(reset_xtrigs);
+/* list attachments by channel */ +#define LX_ADJUST_BUF_PTRS(n) \
- do { buf_pos += n; buf_size -= n; \
- if (buf_size <= 1) \
goto lx_buf_full; \
- } while (0)
+static ssize_t list_xtrigs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- int used = 0, nr_channels, chan_idx, reg_idx;
- /* buffer vars */
- int buf_size = PAGE_SIZE;
- char *buf_pos = buf;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *cfg = &drvdata->config;
- u32 chan_mask;
- nr_channels = cfg->nr_ctm_channels;
- used = scnprintf(buf_pos, buf_size, "CTI:%s; Channels:%d\n",
dev_name(dev), nr_channels);
- LX_ADJUST_BUF_PTRS(used);
- /* show the IN and OUT triggers per channel */
- for (chan_idx = 0; chan_idx < nr_channels; chan_idx++) {
chan_mask = 0x1 << chan_idx;
used = scnprintf(buf_pos, buf_size, "Chan %d: IN [ ", chan_idx);
LX_ADJUST_BUF_PTRS(used);
for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiinen[reg_idx]) {
used = scnprintf(buf_pos, buf_size, "%d ",
reg_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]; OUT [ ");
LX_ADJUST_BUF_PTRS(used);
for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiouten[reg_idx]) {
used = scnprintf(buf_pos, buf_size, "%d ",
reg_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "];\n");
LX_ADJUST_BUF_PTRS(used);
- }
- /* show the channels enabled via ctigate */
- used = scnprintf(buf_pos, buf_size, "Gate Channels Enabled: [ ");
- LX_ADJUST_BUF_PTRS(used);
- if (cfg->ctigate == 0) {
used = scnprintf(buf_pos, buf_size, "None ]\n");
LX_ADJUST_BUF_PTRS(used);
- } else {
for (chan_idx = 0; chan_idx < nr_channels; chan_idx++) {
chan_mask = 0x1 << chan_idx;
if (chan_mask & cfg->ctigate) {
used = scnprintf(buf_pos, buf_size, "%d ",
chan_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LX_ADJUST_BUF_PTRS(used);
- }
+lx_buf_full:
- used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
- return used;
+} +static DEVICE_ATTR_RO(list_xtrigs);
+#define LCIU_ADJUST_BUF_PTRS(n) \
- do { buf_pos += n; buf_size -= n; \
- if (buf_size <= 1) \
goto lciu_buf_full; \
- } while (0)
+static ssize_t list_chan_inuse_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- int used = 0, nr_channels, i;
- /* buffer vars */
- int buf_size = PAGE_SIZE;
- char *buf_pos = buf;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- u32 inuse_bits = 0, chan_mask, chan_bit_mask;
- nr_channels = config->nr_ctm_channels;
- spin_lock(&drvdata->spinlock);
- for (i = 0; i < config->nr_trig_max; i++) {
inuse_bits |= config->ctiinen[i];
inuse_bits |= config->ctiouten[i];
- }
- spin_unlock(&drvdata->spinlock);
- /* print in use channels */
- chan_mask = ((u32)(0x1 << nr_channels)) - 1;
- if (inuse_bits & chan_mask) {
used = scnprintf(buf_pos, buf_size, "Chan in use [ ");
LCIU_ADJUST_BUF_PTRS(used);
for (i = 0; i < nr_channels; i++) {
chan_bit_mask = 0x1 << i;
if (chan_bit_mask & inuse_bits) {
used = scnprintf(buf_pos, buf_size, "%d ", i);
LCIU_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LCIU_ADJUST_BUF_PTRS(used);
- } else {
used = scnprintf(buf_pos, buf_size, "Chan in use [ none ]\n");
LCIU_ADJUST_BUF_PTRS(used);
- }
- /* print free channels */
- inuse_bits = ~inuse_bits; /* flip for free channels */
- if (inuse_bits & chan_mask) {
used = scnprintf(buf_pos, buf_size, "Chan free [ ");
LCIU_ADJUST_BUF_PTRS(used);
for (i = 0; i < nr_channels; i++) {
chan_bit_mask = 0x1 << i;
if (chan_bit_mask & inuse_bits) {
used = scnprintf(buf_pos, buf_size, "%d ", i);
LCIU_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LCIU_ADJUST_BUF_PTRS(used);
- } else {
used = scnprintf(buf_pos, buf_size, "Chan free [ none ]\n");
LCIU_ADJUST_BUF_PTRS(used);
- }
+lciu_buf_full:
- used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
- return used;
+} +static DEVICE_ATTR_RO(list_chan_inuse);
+static struct attribute *coresight_cti_channel_attrs[] = {
- &dev_attr_trigin_attach.attr,
- &dev_attr_trigin_detach.attr,
- &dev_attr_trigout_attach.attr,
- &dev_attr_trigout_detach.attr,
- &dev_attr_gate_enable.attr,
- &dev_attr_gate_disable.attr,
- &dev_attr_chan_set.attr,
- &dev_attr_chan_clear.attr,
- &dev_attr_chan_pulse.attr,
- &dev_attr_trig_filter_enable.attr,
- &dev_attr_list_xtrigs.attr,
- &dev_attr_reset_xtrigs.attr,
- &dev_attr_list_chan_inuse.attr,
- NULL,
+};
+/* coresight management registers */ +coresight_cti_reg(devaff0, CTIDEVAFF0); +coresight_cti_reg(devaff1, CTIDEVAFF1); +coresight_cti_reg(authstatus, CORESIGHT_AUTHSTATUS); +coresight_cti_reg(devarch, CORESIGHT_DEVARCH); +coresight_cti_reg(devid, CORESIGHT_DEVID); +coresight_cti_reg(devtype, CORESIGHT_DEVTYPE); +coresight_cti_reg(pidr0, CORESIGHT_PERIPHIDR0); +coresight_cti_reg(pidr1, CORESIGHT_PERIPHIDR1); +coresight_cti_reg(pidr2, CORESIGHT_PERIPHIDR2); +coresight_cti_reg(pidr3, CORESIGHT_PERIPHIDR3); +coresight_cti_reg(pidr4, CORESIGHT_PERIPHIDR4);
+static struct attribute *coresight_cti_mgmt_attrs[] = {
- &dev_attr_devaff0.attr,
- &dev_attr_devaff1.attr,
- &dev_attr_authstatus.attr,
- &dev_attr_devarch.attr,
- &dev_attr_devid.attr,
- &dev_attr_devtype.attr,
- &dev_attr_pidr0.attr,
- &dev_attr_pidr1.attr,
- &dev_attr_pidr2.attr,
- &dev_attr_pidr3.attr,
- &dev_attr_pidr4.attr,
- NULL,
+};
+static const struct attribute_group coresight_cti_group = {
- .attrs = coresight_cti_attrs,
+};
+static const struct attribute_group coresight_cti_regs_group = {
- .attrs = coresight_cti_regs_attrs,
- .name = "regs",
+};
+static const struct attribute_group coresight_cti_channels_group = {
- .attrs = coresight_cti_channel_attrs,
- .name = "channels",
+};
+static const struct attribute_group coresight_cti_mgmt_group = {
- .attrs = coresight_cti_mgmt_attrs,
- .name = "mgmt",
+};
+const struct attribute_group *coresight_cti_groups[] = {
- &coresight_cti_group,
- &coresight_cti_regs_group,
- &coresight_cti_channels_group,
- &coresight_cti_mgmt_group,
- NULL,
+};
+static int cti_starting_cpu(unsigned int cpu) +{
- struct cti_drvdata *drvdata = cti_cpu_drv[cpu];
- if (!drvdata)
return 0;
- spin_lock(&drvdata->spinlock);
- drvdata->config.hw_powered = true;
- spin_unlock(&drvdata->spinlock);
- if (atomic_read(&drvdata->config.enable_req_count))
cti_enable_hw(drvdata);
- return 0;
+}
+static int cti_stopping_cpu(unsigned int cpu) +{
- struct cti_drvdata *drvdata = cti_cpu_drv[cpu];
- if (!drvdata)
return 0;
- cti_disable_hw(drvdata);
- spin_lock(&drvdata->spinlock);
- drvdata->config.hw_powered = false;
- spin_unlock(&drvdata->spinlock);
- return 0;
+}
+static void cti_smp_set_powered(void *info) +{
- struct cti_drvdata *drvdata = info;
- drvdata->config.hw_powered = true;
+}
+/* setup power handling on a cti that has an association with a cpu */ +static int cti_setup_hp_cpu_affinity(struct cti_drvdata *drvdata) +{
- int ret = 0;
- /* CPU affinity - add to quick lookup array
* and register cpu hp callbacks first time out
*/
- cpus_read_lock();
- cti_cpu_drv[drvdata->ctidev.cpu] = drvdata;
- if (!cti_cpu_count) {
cti_hp_online = cpuhp_setup_state_nocalls_cpuslocked(
CPUHP_AP_ONLINE_DYN, "arm/coresightcti:online",
cti_starting_cpu, cti_stopping_cpu);
if (cti_hp_online < 0) {
ret = cti_hp_online;
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
goto cti_hp_cpu_done;
}
- }
- cti_cpu_count++;
- /* setup powered flag by running function of assoc cpu */
- if (smp_call_function_single(drvdata->ctidev.cpu, cti_smp_set_powered,
drvdata, 1)) {
drvdata->config.hw_powered = false;
- }
+cti_hp_cpu_done:
- cpus_read_unlock();
- return ret;
+}
+/* back out hotplug notifications on error */ +static void cti_clear_hp_cpu_affinity(struct cti_drvdata *drvdata) +{
- if (!drvdata)
return;
- if (drvdata->ctidev.cpu >= 0) {
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
cti_cpu_count--;
if ((cti_cpu_count == 0) && (cti_hp_online > 0))
cpuhp_remove_state_nocalls(cti_hp_online);
- }
+}
The CPU hotplug bits can be split in their own file.
+static int cti_probe(struct amba_device *adev, const struct amba_id *id) +{
- int ret = 0;
- u32 devid;
- void __iomem *base;
- struct device *dev = &adev->dev;
- struct cti_drvdata *drvdata = 0;
- struct coresight_desc cti_desc;
- struct coresight_platform_data *pdata = NULL;
- struct resource *res = &adev->res;
- struct device_node *np = adev->dev.of_node;
- struct ect_node *ect_nd = 0;
- /* boilerplate code to set up the basics */
- dev_info(dev, "%s(ID = %x)\n", __func__, id->id);
- if (np) {
pdata = of_get_coresight_ect_platform_data(dev, np);
if (IS_ERR(pdata)) {
dev_info(dev, "of_get_coresight_ect_platform err\n");
ret = PTR_ERR(pdata);
goto err_out;
}
dev->platform_data = pdata;
- }
- /* node to keep track of CTI net */
- ect_nd = kzalloc(sizeof(struct ect_node), GFP_KERNEL);
- if (!ect_nd) {
ret = -ENOMEM;
goto err_out;
- }
- /* driver data*/
- drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
- if (!drvdata) {
ret = -ENOMEM;
dev_info(dev, "%s, mem err\n", __func__);
goto err_out;
- }
- /* links between dev and drvdata*/
- drvdata->dev = dev;
- dev_set_drvdata(dev, drvdata);
- /* default CTI device info */
- drvdata->ctidev.cpu = -1;
- drvdata->ctidev.nr_trig_con = 0;
- drvdata->ctidev.ctm_id = 0;
- INIT_LIST_HEAD(&drvdata->ctidev.trig_cons);
- /* Validity for the resource is already checked by the AMBA core */
- base = devm_ioremap_resource(dev, res);
- if (IS_ERR(base)) {
ret = PTR_ERR(base);
dev_info(dev, "%s, remap err\n", __func__);
goto err_out;
- }
- drvdata->base = base;
- spin_lock_init(&drvdata->spinlock);
- /* look at the HW DEVID register for some of the HW settings */
- devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
- drvdata->config.nr_trig_max = (int)((devid & 0xFF00) >> 8);
- /* no current hardware should exceed this, but protect the driver
* in case of fault / out of spec hw
*/
- if (drvdata->config.nr_trig_max > CTIINOUTEN_MAX) {
dev_warn_once(dev,
"Limiting HW MaxTrig value(%d) to driver max(%d)\n",
drvdata->config.nr_trig_max, CTIINOUTEN_MAX);
drvdata->config.nr_trig_max = CTIINOUTEN_MAX;
- }
- drvdata->config.nr_ctm_channels = (int)((devid & 0xF0000) >> 16);
- dev_info(dev, "DevID %08x, trigs=%d, chans=%d\n", devid,
drvdata->config.nr_trig_max, drvdata->config.nr_ctm_channels);
- /** additional parse the .dts for connections and signals */
- of_cti_get_hw_data(dev, np, drvdata);
- cti_set_default_config(&drvdata->config);
- /* setup cpu related CTI devices, otherwise assume powered */
- if (drvdata->ctidev.cpu >= 0) {
ret = cti_setup_hp_cpu_affinity(drvdata);
if (ret < 0)
goto err_out;
- } else
drvdata->config.hw_powered = true;
- /* set up coresight component description */
- cti_desc.pdata = pdata;
- cti_desc.type = CORESIGHT_DEV_TYPE_ECT;
- cti_desc.subtype.ect_subtype = CORESIGHT_DEV_SUBTYPE_ECT_CTI;
- cti_desc.ops = &cti_ops;
- cti_desc.groups = coresight_cti_groups;
- cti_desc.dev = dev;
- drvdata->csdev = coresight_register(&cti_desc);
- if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
pr_err("%s: CS register failed\n", pdata->name);
goto err_out;
- }
- /* add to list of CTI devices */
- mutex_lock(&ect_mutex);
- ect_nd->cti_drv = drvdata;
- list_add(&ect_nd->next, &ect_net);
- mutex_unlock(&ect_mutex);
- /* set any cross references */
- cti_update_conn_xrefs(drvdata);
- /* all done - dec pm refcount */
- pm_runtime_put(&adev->dev);
- dev_info(dev, "%s: initialized\n", pdata->name);
- cti_count++;
- dev_info(dev, "%s - ok\n", __func__);
- return 0;
+err_out:
- cti_clear_hp_cpu_affinity(drvdata);
- dev_info(dev, "%s - err_out\n", __func__);
- return ret;
+}
+/* free up CTI specific resources
- called from coresight_device_release on coresight_unregister.
- */
Multiline comment format problem.
More to come tomorrow, Mathieu
+void cti_device_release(struct device *dev) +{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct ect_node *ect_item, *ect_tmp;
- struct cti_trig_con *tc, *tc_tmp;
- /* free up resources associated with the cti connections */
- if (drvdata->ctidev.cpu >= 0)
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
- list_for_each_entry_safe(tc, tc_tmp,
&drvdata->ctidev.trig_cons, node) {
kfree(tc->con_in);
kfree(tc->con_out);
kfree(tc->con_dev_name);
list_del(&tc->node);
kfree(tc);
- }
- /* remove from the list */
- mutex_lock(&ect_mutex);
- list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, next) {
if (ect_item->cti_drv == drvdata) {
list_del(&ect_item->next);
kfree(ect_item);
goto ect_list_item_removed;
}
- }
+ect_list_item_removed:
- mutex_unlock(&ect_mutex);
- kfree(drvdata);
+} +EXPORT_SYMBOL_GPL(cti_device_release);
+#define CTI_AMBA_ID(pid) \
- { \
.id = pid, \
.mask = 0x000fffff, \
- }
+static struct amba_cs_uci_id uci_id_cti[] = {
- {
/* CTI UCI data */
.devarch = 0x47701a14, /* devarch value for CTI v2 */
.devarch_mask = 0xfff0ffff,
.devtype = 0x00000014, /* maj(0x4-debug) min(0x1-ECT) */
- }
+};
+#define CTI_AMBA_UCI_ID(pid) \
- { \
.id = pid, \
.mask = 0x000fffff, \
.data = uci_id_cti, \
- }
+static const struct amba_id cti_ids[] = {
- CTI_AMBA_ID(0x000bb906), /* Coresight CTI (SoC 400), C-A72, C-A57 */
- CTI_AMBA_ID(0x000bb922), /* CTI - C-A8 */
- CTI_AMBA_ID(0x000bb9a8), /* CTI - C-A53 */
- CTI_AMBA_ID(0x000bb9aa), /* CTI - C-A73 */
- CTI_AMBA_UCI_ID(0x000bb9da), /* CTI - C-A35 */
- CTI_AMBA_UCI_ID(0x000bb9ed), /* Coresight CTI (SoC 600) */
- CTI_AMBA_UCI_ID(0x000bb95d), /* test UCI - ETM PID A53 */
- { 0, 0},
+};
+static struct amba_driver cti_driver = {
- .drv = {
.name = "coresight-cti",
.owner = THIS_MODULE,
.suppress_bind_attrs = true,
- },
- .probe = cti_probe,
- .id_table = cti_ids,
+}; +builtin_amba_driver(cti_driver); diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h new file mode 100644 index 000000000000..09cb985ac0ea --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#ifndef _CORESIGHT_CORESIGHT_CTI_H +#define _CORESIGHT_CORESIGHT_CTI_H
+#include <asm/local.h> +#include <linux/spinlock.h> +#include "coresight-priv.h"
+/*
- Device registers
- 0x000 - 0x144: CTI programming and status
- 0xEDC - 0xEF8: CTI integration test.
- 0xF00 - 0xFFC: Coresight management registers.
- */
+/* CTI programming registers */ +#define CTICONTROL 0x000 +#define CTIINTACK 0x010 +#define CTIAPPSET 0x014 +#define CTIAPPCLEAR 0x018 +#define CTIAPPPULSE 0x01C +#define CTIINEN(n) (0x020 + (4 * n)) +#define CTIOUTEN(n) (0x0A0 + (4 * n)) +#define CTITRIGINSTATUS 0x130 +#define CTITRIGOUTSTATUS 0x134 +#define CTICHINSTATUS 0x138 +#define CTICHOUTSTATUS 0x13C +#define CTIGATE 0x140 +#define ASICCTL 0x144 +/* Integration test registers */ +#define ITCHINACK 0xEDC /* WO CTI CSSoc 400 only*/ +#define ITTRIGINACK 0xEE0 /* WO CTI CSSoc 400 only*/ +#define ITCHOUT 0xEE4 /* WO RW-600 */ +#define ITTRIGOUT 0xEE8 /* WO RW-600 */ +#define ITCHOUTACK 0xEEC /* RO CTI CSSoc 400 only*/ +#define ITTRIGOUTACK 0xEF0 /* RO CTI CSSoc 400 only*/ +#define ITCHIN 0xEF4 /* RO */ +#define ITTRIGIN 0xEF8 /* RO */ +/* management registers */ +#define CTIDEVAFF0 0xFA8 +#define CTIDEVAFF1 0xFAC
+/* CTI CSSoc 600 has a max of 32 trigger signals per direction.
- CTI CSSoc 400 has 8 IO triggers - other CTIs can be impl def.
- Max of in and out defined in the DEVID register.
- pick up actual number used from .dts parameters if present.
- */
+#define CTIINOUTEN_MAX 32
+/**
- Group of related trigger signals
- @nr_sigs: number of signals in the group.
- @used_mask: bitmask representing the signal indexes in the group.
- @sig_names: list of names for the signals.
- */
+struct cti_trig_grp {
- int nr_sigs;
- u32 used_mask;
- char *sig_names;
+};
+/**
- Trigger connection - connection between a CTI and other (coresight) device
- lists input and output trigger signals for the device
- @con_in: connected CTIIN signals for the device.
- @con_out: connected CTIOUT signals for the device.
- @con_dev: coresight device connected to the CTI, NULL if not CS device
- @con_dev_name: name of connected device (CS or CPU)
- @node: entry node in list of connections.
- */
+struct cti_trig_con {
- struct cti_trig_grp *con_in;
- struct cti_trig_grp *con_out;
- struct coresight_device *con_dev;
- char *con_dev_name;
- struct list_head node;
+};
+/**
- struct cti_device - description of CTI device properties.
- @nt_trig_con: Number of external devices connected to this device.
- @ctm_id: which CTM this device is connected to (by default it is
assumed there is a single CTM per SoC, ID 0).
- @trig_cons: list of connections to this device.
- @cpu: CPU ID if associated with CPU, -1 otherwise.
- */
+struct cti_device {
- int nr_trig_con;
- u32 ctm_id;
- struct list_head trig_cons;
- int cpu;
+};
+/**
- struct cti_config - configuration of the CTI device hardware
- hardware description from RO ID regs
- @nr_trig_max: Max number of trigger signals implemented on device.
(max of trig_in or trig_out)
- @nr_ctm_channels: number of available CTM channels
- cti enable control
- @enable_req_count: CTI is enabled alongside >=1 associated devices.
- @hw_enabled: true if hw is currently enabled.
- @hw_powered: true if associated cpu powered on, or no cpu.
- registered triggers and filtering
- @trig_in_use: bitfield of in triggers registered as in use.
- @trig_out_use: bitfield of out triggers registered as in use.
- @trig_out_filter: bitfield of out triggers that are blocked if filter
enabled. Typically this would be dbgreq / restart on a core CTI.
- @trig_filter_enable: 1 if filtering enabled.
- cti software programmable regs:
- @ctiappset: CTI Software application channel set.
- @ctiinout_sel: register selector for INEN and OUTEN regs.
- @ctiinen: enable input trigger to a channel.
- @ctiouten: enable output trigger from a channel.
- @ctigate: gate channel output from CTI to CTM.
- */
+struct cti_config {
- /* hardware description */
- int nr_ctm_channels;
- int nr_trig_max;
- /* cti enable control */
- atomic_t enable_req_count;
- bool hw_enabled;
- bool hw_powered;
- /* registered triggers and filtering */
- u32 trig_in_use;
- u32 trig_out_use;
- u32 trig_out_filter;
- int trig_filter_enable;
- /* cti cross trig programmable regs */
- u32 ctiappset;
- u8 ctiinout_sel;
- u32 ctiinen[CTIINOUTEN_MAX];
- u32 ctiouten[CTIINOUTEN_MAX];
- u32 ctigate;
- u32 asicctl;
+};
+/**
- struct cti_drvdata - specifics for the CTI device
- @base: Memory mapped base address for this component.
- @dev: The device entity associated to this component.
- @csdev: Standard CoreSight device information.
- @ctidev: Extra information needed by the CTI/CTM framework.
- @spinlock: Control data access to one at a time.
- @config: Configuration data for this CTI device.
- */
+struct cti_drvdata {
- void __iomem *base;
- struct device *dev;
- struct coresight_device *csdev;
- struct cti_device ctidev;
- spinlock_t spinlock;
- struct cti_config config;
+};
+enum cti_chan_op {
- CTI_CHAN_ATTACH,
- CTI_CHAN_DETACH,
+};
+enum cti_trig_dir {
- CTI_TRIG_IN,
- CTI_TRIG_OUT,
+};
+enum cti_chan_gate_op {
- CTI_GATE_CHAN_ENABLE,
- CTI_GATE_CHAN_DISABLE,
- CTI_GATE_CHAN_ENABLE_ALL,
- CTI_GATE_CHAN_DISABLE_ALL,
+};
+enum cti_chan_set_op {
- CTI_CHAN_SET,
- CTI_CHAN_CLR,
- CTI_CHAN_PULSE,
+};
+#endif /* _CORESIGHT_CORESIGHT_CTI_H */
2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Hi,
On Tue, 22 Jan 2019 at 23:09, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, Jan 09, 2019 at 10:54:38PM +0000, Mike Leach wrote:
Initial addition of the files for the Coresight CTI driver.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-cti.c | 2025 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 195 ++ 2 files changed, 2220 insertions(+) create mode 100644 drivers/hwtracing/coresight/coresight-cti.c create mode 100644 drivers/hwtracing/coresight/coresight-cti.h
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c new file mode 100644 index 000000000000..d06e19f111e2 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -0,0 +1,2025 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/smp.h> +#include <linux/sysfs.h> +#include <linux/stat.h> +#include <linux/clk.h> +#include <linux/cpu.h> +#include <linux/coresight.h> +#include <linux/coresight-pmu.h> +#include <linux/pm_wakeup.h> +#include <linux/amba/bus.h> +#include <linux/seq_file.h> +#include <linux/uaccess.h> +#include <linux/perf_event.h> +#include <linux/pm_runtime.h> +#include <linux/of.h> +#include <asm/sections.h> +#include <asm/local.h>
+#include "coresight-cti.h"
+#define csdev_to_cti_drvdata(csdev) \
dev_get_drvdata(csdev->dev.parent)
+/* CTI device tree trigger connection node keyword */ +#define CTI_DT_CONNS "trig-conns" +/* CTI device tree connection property keywords */ +#define CTI_DT_V8ARCH "arm,cti-v8-arch" +#define CTI_DT_TRIGIN_SIGS "arm,trig-in-sigs" +#define CTI_DT_TRIGOUT_SIGS "arm,trig-out-sigs" +#define CTI_DT_FILTER_OUT_SIGS "arm,trig-filters" +#define CTI_DT_CSDEV_ASSOC "arm,cs-dev-assoc" +#define CTI_DT_CTM_ID "arm,cti-ctm-id" +#define CTI_DT_CONN_NAME "arm,trig-conn-name"
+/**
- CTI devices can be associated with a PE, or be connected to CoreSight
- hardware. We have a list of all CTIs, and a subset array associated
- with individual PEs for pwr management as they will power up in the
- PE power domain.
- At this point we assume that the none CPU CTIs are always powered as
- we do with sinks etc.
- We leave the client to figure out if all the CTIs are interconnected with
- the same CTM, in general this is the case but does not always have to be.
- */
+struct ect_node {
struct cti_drvdata *cti_drv;
struct list_head next;
+};
+/* net of CTI devices connected via CTM */ +LIST_HEAD(ect_net);
+/* protect the list */ +static DEFINE_MUTEX(ect_mutex);
+/* quick reference for CPU CTIs */ +static struct cti_drvdata *cti_cpu_drv[NR_CPUS];
+/* number of registered CTI devices */ +static int cti_count;
+/* number of cpu associated CTI devices in use */ +static int cti_cpu_count;
+/* CTI / cpu hotplug notification ID */ +static enum cpuhp_state cti_hp_online;
+/* cpu powered and enabled */ +#define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered)
+/* write set of regs to hardware - call with spinlock claimed */ +static void cti_write_all_hw_regs(struct cti_drvdata *drvdata) +{
struct cti_config *config = &drvdata->config;
int i;
CS_UNLOCK(drvdata->base);
/* disable CTI before writing registers */
writel_relaxed(0, drvdata->base + CTICONTROL);
/* write the CTI trigger registers */
for (i = 0; i < config->nr_trig_max; i++) {
writel_relaxed(config->ctiinen[i], drvdata->base + CTIINEN(i));
writel_relaxed(config->ctiouten[i],
drvdata->base + CTIOUTEN(i));
Indentation problem.
}
/* other regs */
writel_relaxed(config->ctigate, drvdata->base + CTIGATE);
writel_relaxed(config->asicctl, drvdata->base + ASICCTL);
writel_relaxed(config->ctiappset, drvdata->base + CTIAPPSET);
/* re-enable CTI */
writel_relaxed(1, drvdata->base + CTICONTROL);
CS_LOCK(drvdata->base);
+}
+static void cti_enable_hw_smp_call(void *info) +{
struct cti_drvdata *drvdata = info;
cti_write_all_hw_regs(drvdata);
+}
+/* write regs to hardware and enable */ +static int cti_enable_hw(void *info) +{
struct cti_drvdata *drvdata = info;
struct cti_config *config = &drvdata->config;
int rc = 0;
pm_runtime_get_sync(drvdata->dev);
spin_lock(&drvdata->spinlock);
/* no need to do anything if enabled or unpowered*/
if (config->hw_enabled || !config->hw_powered) {
spin_unlock(&drvdata->spinlock);
pm_runtime_put(drvdata->dev);
return 0;
}
if (drvdata->ctidev.cpu >= 0) {
dev_info(drvdata->dev, "cti enable smp call for cpu %d\n",
drvdata->ctidev.cpu);
rc = smp_call_function_single(drvdata->ctidev.cpu,
cti_enable_hw_smp_call,
drvdata, 1);
} else {
dev_info(drvdata->dev, "cti enable not cpu call\n");
cti_write_all_hw_regs(drvdata);
}
if (!rc)
config->hw_enabled = true;
spin_unlock(&drvdata->spinlock);
return 0;
+}
+/* disable hardware */ +static int cti_disable_hw(void *info) +{
struct cti_drvdata *drvdata = info;
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
/* no need to do anything if disabled or cpu unpowered*/
if (!config->hw_enabled || !config->hw_powered) {
spin_unlock(&drvdata->spinlock);
return 0;
}
CS_UNLOCK(drvdata->base);
/* disable CTI */
writel_relaxed(0, drvdata->base + CTICONTROL);
config->hw_enabled = false;
CS_LOCK(drvdata->base);
spin_unlock(&drvdata->spinlock);
pm_runtime_put(drvdata->dev);
return 0;
+}
+static void +cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value) +{
CS_UNLOCK(drvdata->base);
writel_relaxed(value, drvdata->base+offset);
CS_LOCK(drvdata->base);
+}
+static void cti_set_default_config(struct cti_config *config) +{
/* Most regs default to 0 as zalloc'ed except...*/
config->trig_filter_enable = 1;
config->ctigate = ((u32)0x1 << config->nr_ctm_channels) - 1;
Please describe what you are doing, i.e configuring all the channels to propagate to its connected CTM by setting the channel bits to '1'.
atomic_set(&config->enable_req_count, 0);
+}
+static void cti_write_intack(struct device *dev, u32 ackval) +{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
/* write if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIINTACK, ackval);
spin_unlock(&drvdata->spinlock);
+}
+/* need to update cross references once CTI csdev has been generated. */ +static void cti_update_conn_xrefs(struct cti_drvdata *drvdata) +{
struct cti_trig_con *tc;
struct cti_device *ctidev = &drvdata->ctidev;
list_for_each_entry(tc, &ctidev->trig_cons, node) {
if (tc->con_dev) {
tc->con_dev->ect_dev = drvdata->csdev;
dev_info(drvdata->dev,
"Setting assoc csdev (%px) with (%px)\n",
tc->con_dev, drvdata->csdev);
}
}
+}
+/* connection information */ +static int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name)
+{
struct cti_trig_con *tc;
struct cti_device *cti_dev = &drvdata->ctidev;
tc = kzalloc(sizeof(struct cti_trig_con), GFP_KERNEL);
if (tc == 0)
return -ENOMEM;
tc->con_dev = csdev;
/* prefer actual associated CS device dev name to supplied value -
* which is likely to be node name / other conn name
*/
if (csdev)
tc->con_dev_name = kstrdup(dev_name(&csdev->dev), GFP_KERNEL);
else if (assoc_dev_name != NULL)
tc->con_dev_name = kstrdup(assoc_dev_name, GFP_KERNEL);
tc->con_in = in_info;
tc->con_out = out_info;
list_add_tail(&tc->node, &cti_dev->trig_cons);
cti_dev->nr_trig_con++;
/* add connection usage bit info to overall info */
drvdata->config.trig_in_use |= in_info->used_mask;
drvdata->config.trig_out_use |= out_info->used_mask;
return 0;
+}
+/* add a default connection if nothing else is specified.
- single connection based on max in/out info, no assoc device
- */
+static int cti_add_default_connection(struct cti_drvdata *drvdata) +{
struct cti_trig_grp *in, *out;
int n_trigs = drvdata->config.nr_trig_max;
u32 n_trig_mask = (0x1 << n_trigs) - 1;
in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!in)
return -ENOMEM;
out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!out)
return -ENOMEM;
dev_info(drvdata->dev, "default connection: trigs %d, trig_mask %08x\n",
n_trigs, n_trig_mask);
/* assume max trigs for in and out, all used */
in->nr_sigs = n_trigs;
in->used_mask = n_trig_mask;
in->sig_names = NULL;
out->nr_sigs = n_trigs;
out->used_mask = n_trig_mask;
out->sig_names = NULL;
return cti_add_connection_entry(drvdata, in, out, NULL, "default");
+}
+/* create an architecturally defined v8 connection
- must hava a cpu, can have an ETM
- */
+static int of_cti_create_v8_connections(struct cti_drvdata *drvdata,
struct device_node *np)
+{
struct cti_device *cti_dev = &drvdata->ctidev;
struct cti_trig_grp *in, *out;
int cpuid = 0;
struct device_node *cs_np;
struct coresight_device *csdev = NULL;
char cpu_name_str[16];
const char *assoc_name = NULL;
cpuid = of_coresight_ect_get_cpu(np);
if (cpuid < 0) {
dev_warn(drvdata->dev, "CTI v8 DT binding no cpu\n");
return -EINVAL;
}
cti_dev->cpu = cpuid;
/* do the v8 cpu connection */
in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!in)
return -ENOMEM;
out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!out)
return -ENOMEM;
/* v8 PE CTI config... */
in->nr_sigs = 3;
in->used_mask = 0x7;
in->sig_names = NULL;
out->nr_sigs = 3;
out->used_mask = 0x7;
out->sig_names = NULL;
scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
cti_add_connection_entry(drvdata, in, out, NULL, cpu_name_str);
drvdata->config.trig_out_filter = 0x1; /* filter dbgreq */
/* v8 ETM associated config... */
cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
if (cs_np) {
in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!in)
return -ENOMEM;
out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!out)
return -ENOMEM;
in->nr_sigs = 4;
in->used_mask = 0xF0;
in->sig_names = NULL;
out->nr_sigs = 4;
out->used_mask = 0xF0;
out->sig_names = NULL;
dev_info(drvdata->dev, "v8 finding assoc dev %s\n",
cs_np->full_name);
csdev = of_coresight_get_cs_device_by_node(cs_np);
if (csdev)
assoc_name = dev_name(&csdev->dev);
else
assoc_name = cs_np->full_name;
cti_add_connection_entry(drvdata, in, out, csdev, assoc_name);
of_node_put(cs_np);
}
return 0;
+}
+static int of_cti_read_trig_group(struct cti_trig_grp **trig_grp_ptr,
struct device_node *np,
const char *grp_name,
int max_trigs)
+{
int items, err = 0;
u32 value, pidx;
struct cti_trig_grp *grp;
grp = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!grp)
return -ENOMEM;
*trig_grp_ptr = grp;
items = of_property_count_elems_of_size(np, grp_name, 4);
/* if the property doesn't exist or has no values, then return
* an empty connection group
*/
if (items < 0)
return 0;
if (items > max_trigs)
return -EINVAL;
/* set the number of signals and usage mask */
for (pidx = 0; pidx < items; pidx++) {
err = of_property_read_u32_index(np, grp_name, pidx, &value);
if (err)
return err;
grp->nr_sigs++;
grp->used_mask |= (0x1 << value);
}
return 0;
+}
+static int of_cti_create_connection(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
struct cti_trig_grp *in = 0, *out = 0, *filter = 0;
int cpuid = -1, err = 0, trig_max = drvdata->config.nr_trig_max;
struct device_node *cs_np;
struct coresight_device *csdev = NULL;
const char *assoc_name = "unknown";
char cpu_name_str[16];
/* look for the signals properties. */
err = of_cti_read_trig_group(&in, np, CTI_DT_TRIGIN_SIGS, trig_max);
if (err)
return err;
err = of_cti_read_trig_group(&out, np, CTI_DT_TRIGOUT_SIGS, trig_max);
if (err)
return err;
err = of_cti_read_trig_group(&filter, np, CTI_DT_FILTER_OUT_SIGS,
trig_max);
if (err)
return err;
/* read the connection name if set - may be overridden by later */
of_property_read_string(np, CTI_DT_CONN_NAME, &assoc_name);
/* associated cpu ? */
cpuid = of_coresight_ect_get_cpu(np);
drvdata->ctidev.cpu = cpuid;
if (cpuid >= 0) {
scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
assoc_name = cpu_name_str;
} else {
/* associated device ? */
cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
if (cs_np) {
dev_info(dev, "finding assoc dev %s\n",
cs_np->full_name);
csdev = of_coresight_get_cs_device_by_node(cs_np);
if (csdev) /* use device name if csdev found */
assoc_name = dev_name(&csdev->dev);
else /* otherwise node name for later association */
assoc_name = cs_np->full_name;
of_node_put(cs_np);
}
}
/* set up a connection */
err = cti_add_connection_entry(drvdata, in, out, csdev, assoc_name);
/* note any filter info */
if (!err)
drvdata->config.trig_out_filter |= filter->used_mask;
kfree(filter);
return err;
+}
+/* get the hardware configuration & connection data. */ +static int of_cti_get_hw_data(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
int rc = 0;
struct cti_device *cti_dev = &drvdata->ctidev;
struct device_node *nc = NULL;
/* get any CTM ID - defaults to 0 */
of_property_read_u32(np, CTI_DT_CTM_ID, &cti_dev->ctm_id);
if (of_property_read_bool(np, CTI_DT_V8ARCH)) {
rc = of_cti_create_v8_connections(drvdata, np);
} else {
for_each_child_of_node(np, nc) {
if (of_node_cmp(nc->name, CTI_DT_CONNS) != 0)
continue;
dev_info(dev, "dt found node: %s %s\n",
nc->name, nc->full_name);
rc = of_cti_create_connection(dev, nc, drvdata);
if (rc != 0)
return rc;
}
}
/* if no connections, just add a single default based on max IN-OUT */
if (cti_dev->nr_trig_con == 0)
rc = cti_add_default_connection(drvdata);
return rc;
This function could be done in 3 patch (along with introduction of accompanying code).
- default connections
- explicit connections
- v8 connections
OK
+}
+static int +cti_match_con_name(struct cti_device *ctidev, const char *node_name,
const char *csdev_name)
+{
int found = 0;
struct cti_trig_con *trig_con;
list_for_each_entry(trig_con, &ctidev->trig_cons, node) {
if (trig_con->con_dev_name) {
if (!strcmp(node_name, trig_con->con_dev_name)) {
found = 1;
/* match: so swap in csdev name */
kfree(trig_con->con_dev_name);
trig_con->con_dev_name =
kstrdup(csdev_name, GFP_KERNEL);
goto cti_con_name_match;
}
}
}
+cti_con_name_match:
return found;
+}
+/* search the cti list to add an associated CTI into the supplied CS device
- This will set the association if CTI declared before the CS device
- */
+int cti_add_assoc_to_csdev(struct coresight_device *csdev) +{
struct ect_node *ect_item;
struct cti_device *ctidev;
const char *node_name = NULL, *csdev_name;
/* exit early for no CTIs or self referencing ECT devices.*/
if (!cti_count) {
dev_info(&csdev->dev, "no CTI to check\n");
return 0;
}
if (!csdev->dev.parent->of_node) {
dev_info(&csdev->dev, "No parent of_node pointer\n");
Have you seen this happening? The parent of a csdevice is the amba device, so if we are here parent->of_node must exist...
No - this is probably over cautious programming.
return 0;
}
node_name = csdev->dev.parent->of_node->full_name;
if (!node_name) {
dev_info(&csdev->dev, "%s - bad node name\n", __func__);
return 0;
}
I'm not sure that check too is required but you may have seen otherwise. If that is the case add a comment that mentions why it is needed.
Again - cautious programming.
dev_info(&csdev->dev, "%s - looking for %s\n", __func__, node_name);
if (!cti_count || (csdev->type == CORESIGHT_DEV_TYPE_ECT)) {
!cti_count has already been checked above.
agreed - needs re-work.
dev_info(&csdev->dev, "no CTI or skip self ref CTI\n");
return 0;
}
csdev_name = dev_name(&csdev->dev);
/* for each CTI in list... */
mutex_lock(&ect_mutex);
list_for_each_entry(ect_item, &ect_net, next) {
ctidev = &ect_item->cti_drv->ctidev;
if (cti_match_con_name(ctidev, node_name, csdev_name)) {
csdev->ect_dev = ect_item->cti_drv->csdev;
dev_info(&csdev->dev, "Found!\n");
goto cti_found;
}
}
+cti_found:
mutex_unlock(&ect_mutex);
return 0;
+} +EXPORT_SYMBOL_GPL(cti_add_assoc_to_csdev);
+/* attach/detach channel from trigger - write through if enabled. */ +static int cti_channel_trig_op(struct device *dev,
enum cti_chan_op op,
enum cti_trig_dir direction,
u32 channel_idx,
u32 trigger_idx)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
u32 trig_bitmask;
u32 chan_bitmask;
u32 reg_value;
int reg_offset;
dev_info(dev, "chan_trig_op: op %d, dir %d, chan %d, trig %d\n",
op, direction, channel_idx, trigger_idx);
/* ensure indexes in range */
if ((channel_idx >= config->nr_ctm_channels) ||
(trigger_idx >= config->nr_trig_max))
return -EINVAL;
trig_bitmask = 0x1 << trigger_idx;
/* ensure registered triggers and not out filtered */
if (direction == CTI_TRIG_IN) {
if (!(trig_bitmask & config->trig_in_use))
return -EINVAL;
} else {
if (!(trig_bitmask & config->trig_out_use))
return -EINVAL;
if ((config->trig_filter_enable) &&
(config->trig_out_filter & trig_bitmask))
return -EINVAL;
}
/* update the local register values */
chan_bitmask = 0x1 << channel_idx;
reg_offset = (direction == CTI_TRIG_IN ? CTIINEN(trigger_idx) :
CTIOUTEN(trigger_idx));
spin_lock(&drvdata->spinlock);
/* read - modify write - the trigger / channel enable value */
reg_value = (direction == CTI_TRIG_IN ? config->ctiinen[trigger_idx] :
config->ctiouten[trigger_idx]);
reg_value = (op == CTI_CHAN_ATTACH) ? reg_value | chan_bitmask :
reg_value & (~chan_bitmask);
/* write local copy */
if (direction == CTI_TRIG_IN)
config->ctiinen[trigger_idx] = reg_value;
else
config->ctiouten[trigger_idx] = reg_value;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
spin_unlock(&drvdata->spinlock);
return 0;
+}
+static int cti_channel_gate_op(struct device *dev,
enum cti_chan_gate_op op,
u32 channel_idx)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
u32 chan_bitmask;
u32 reg_value;
int err = 0;
if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
chan_bitmask = 0x1 << channel_idx;
spin_lock(&drvdata->spinlock);
reg_value = config->ctigate;
switch (op) {
case CTI_GATE_CHAN_ENABLE:
reg_value |= chan_bitmask;
break;
case CTI_GATE_CHAN_DISABLE:
reg_value &= ~chan_bitmask;
break;
case CTI_GATE_CHAN_ENABLE_ALL:
reg_value = (0x1 << config->nr_ctm_channels) - 1;
break;
case CTI_GATE_CHAN_DISABLE_ALL:
reg_value = 0x0;
break;
default:
err = -EINVAL;
break;
}
if (err == 0) {
config->ctigate = reg_value;
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIGATE, reg_value);
}
spin_unlock(&drvdata->spinlock);
return err;
+}
+static int cti_channel_setop(struct device *dev,
enum cti_chan_set_op op,
u32 channel_idx)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
u32 chan_bitmask;
u32 reg_value;
u32 reg_offset;
int err = 0;
if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
chan_bitmask = 0x1 << channel_idx;
spin_lock(&drvdata->spinlock);
reg_value = config->ctiappset;
switch (op) {
case CTI_CHAN_SET:
config->ctiappset |= chan_bitmask;
reg_value = config->ctiappset;
reg_offset = CTIAPPSET;
break;
case CTI_CHAN_CLR:
config->ctiappset &= ~chan_bitmask;
reg_value = chan_bitmask;
reg_offset = CTIAPPCLEAR;
break;
case CTI_CHAN_PULSE:
config->ctiappset &= ~chan_bitmask;
reg_value = chan_bitmask;
reg_offset = CTIAPPPULSE;
break;
default:
err = -EINVAL;
break;
}
if ((err == 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
spin_unlock(&drvdata->spinlock);
return err;
+}
+/** cti ect operations **/ +static int cti_enable(struct coresight_device *csdev, void *__unused) +{
int rc;
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
atomic_inc(&drvdata->config.enable_req_count);
rc = cti_enable_hw(drvdata);
if (rc)
atomic_dec(&drvdata->config.enable_req_count);
return rc;
+}
+static int cti_disable(struct coresight_device *csdev, void *__unused) +{
int rc = 0;
struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
if (!atomic_dec_return(&drvdata->config.enable_req_count)) {
rc = cti_disable_hw(drvdata);
if (rc)
atomic_inc(&drvdata->config.enable_req_count);
}
return rc;
+}
+const struct coresight_ops_ect cti_ops_ect = {
.enable = cti_enable,
.disable = cti_disable,
+};
+const struct coresight_ops cti_ops = {
.ect_ops = &cti_ops_ect,
+};
+/** attributes **/
+#define coresight_cti_reg(name, offset) \
coresight_simple_reg32(struct cti_drvdata, name, offset)
+/* show a simple 32 bit value. if pval is NULL then live read,
- otherwise read from supplied pointer only
- */
+static ssize_t cti_reg32_show(struct device *dev, char *buf,
u32 *pval, int reg_offset)
+{
unsigned long val = 0;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
if (pval) {
val = (unsigned long)*pval;
} else if ((reg_offset >= 0) && CTI_PWR_ENA(config)) {
CS_UNLOCK(drvdata->base);
val = readl_relaxed(drvdata->base + reg_offset);
CS_LOCK(drvdata->base);
}
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+/* store a simple 32 bit value.
- if pval not NULL, then copy to here too,
- if reg_offset >= 0 then write through if enabled.
- */
+static ssize_t cti_reg32_store(struct device *dev, const char *buf,
size_t size, u32 *pval,
int reg_offset)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
/* local store */
if (pval)
*pval = (u32)val;
/* write through of offset and enabled */
if ((reg_offset >= 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, val);
spin_unlock(&drvdata->spinlock);
return size;
+}
+/* basic attributes */ +static ssize_t enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
int enable_req;
bool enabled, powered, cpuid;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
ssize_t size = 0;
enable_req = atomic_read(&drvdata->config.enable_req_count);
spin_lock(&drvdata->spinlock);
powered = drvdata->config.hw_powered;
enabled = drvdata->config.hw_enabled;
cpuid = drvdata->ctidev.cpu;
spin_unlock(&drvdata->spinlock);
if (powered) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled", cpuid);
} else if (cpuid >= 0) {
CC kernel/module.o CC drivers/hwtracing/coresight/coresight-cti.o /home/mpoirier/work/coresight/kernel-maint/drivers/hwtracing/coresight/coresight-cti.c: In function ‘enable_show’: /home/mpoirier/work/coresight/kernel-maint/drivers/hwtracing/coresight/coresight-cti.c:831:19: warning: comparison of constant ‘0’ with boolean expression is always true [-Wbool-compare] } else if (cpuid >= 0) { ^~ CC drivers/net/ethernet/hisilicon/hns3/hns3_enet.o
Dumb programming error - cpuid should be int. However, I didn't get the same warning from my compiler (arm gcc 5.2. )
Thanks for the review
Mike
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req", cpuid);
} else {
size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n",
enabled ? "enabled" : "disabled");
}
return size;
+}
+static ssize_t enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int ret = 0;
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
ret = kstrtoul(buf, 16, &val);
if (ret)
return ret;
if (val)
ret = cti_enable(drvdata->csdev, NULL);
else
ret = cti_disable(drvdata->csdev, NULL);
if (ret)
return ret;
return size;
+} +static DEVICE_ATTR_RW(enable);
+static ssize_t ctmid_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return scnprintf(buf, PAGE_SIZE, "0x%x\n", drvdata->ctidev.ctm_id);
+} +static DEVICE_ATTR_RO(ctmid);
+#define LF_ADJUST_BUF_PTRS(n) \
do { buf_pos += n; buf_size -= n; \
if (buf_size <= 1) \
goto lf_buf_full; \
} while (0)
+static ssize_t list_features_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
int used = 0, sig_idx, con_idx, nr_trig_max;
/* buffer vars */
int buf_size = PAGE_SIZE;
char *buf_pos = buf;
char *con_name;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *cfg = &drvdata->config;
struct cti_device *ctidev = &drvdata->ctidev;
u32 sig_mask;
struct cti_trig_con *con;
/* basic feature info */
used = scnprintf(buf_pos, buf_size,
"CTI:%s; Channels:%d; Max Trigs:%d\n",
dev_name(dev), cfg->nr_ctm_channels, cfg->nr_trig_max);
LF_ADJUST_BUF_PTRS(used);
used = scnprintf(buf_pos, buf_size, "CTM id = %d; Num Conns = %d\n",
ctidev->ctm_id, ctidev->nr_trig_con);
LF_ADJUST_BUF_PTRS(used);
/* list each connection info */
con_idx = 0;
nr_trig_max = cfg->nr_trig_max;
list_for_each_entry(con, &ctidev->trig_cons, node) {
/* connection name */
if (con->con_dev_name != NULL)
con_name = con->con_dev_name;
else
con_name = "unknown";
used = scnprintf(buf_pos, buf_size, "conn(%d:%s)\n",
con_idx, con_name);
LF_ADJUST_BUF_PTRS(used);
con_idx++;
/* connection signals */
used = scnprintf(buf_pos, buf_size, "Trig IN(%d) [ ",
con->con_in->nr_sigs);
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & con->con_in->used_mask) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
used = scnprintf(buf_pos, buf_size, "Trig OUT(%d) [ ",
con->con_out->nr_sigs);
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & con->con_out->used_mask) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
}
if (cfg->trig_out_filter) {
used = scnprintf(buf_pos, buf_size, "Trig OUT_FILTERED [ ");
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & cfg->trig_out_filter) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
}
+lf_buf_full:
used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
return used;
+} +static DEVICE_ATTR_RO(list_features);
+static struct attribute *coresight_cti_attrs[] = {
&dev_attr_enable.attr,
&dev_attr_list_features.attr,
&dev_attr_ctmid.attr,
NULL,
+};
+/* raw register attributes */ +static ssize_t inout_sel_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
u32 val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
val = (u32)drvdata->config.ctiinout_sel;
return scnprintf(buf, PAGE_SIZE, "%#x\n", val);
+}
+static ssize_t inout_sel_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
if (kstrtoul(buf, 16, &val))
return -EINVAL;
if (val > (CTIINOUTEN_MAX-1))
return -EINVAL;
spin_lock(&drvdata->spinlock);
drvdata->config.ctiinout_sel = val;
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(inout_sel);
+static ssize_t inen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
spin_lock(&drvdata->spinlock);
index = drvdata->config.ctiinout_sel;
val = drvdata->config.ctiinen[index];
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "INEN%d %#lx\n", index, val);
+}
+static ssize_t inen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
index = config->ctiinout_sel;
config->ctiinen[index] = val;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIINEN(index), val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(inen);
+static ssize_t outen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
spin_lock(&drvdata->spinlock);
index = drvdata->config.ctiinout_sel;
val = drvdata->config.ctiouten[index];
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "OUTEN%d %#lx\n", index, val);
+}
+static ssize_t outen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
int index;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
index = config->ctiinout_sel;
config->ctiouten[index] = val;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIOUTEN(index), val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(outen);
+static ssize_t gate_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_show(dev, buf, &drvdata->config.ctigate, -1);
+}
+static ssize_t gate_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_store(dev, buf, size,
&drvdata->config.ctigate, CTIGATE);
+} +static DEVICE_ATTR_RW(gate);
+static ssize_t asicctl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_show(dev, buf, &drvdata->config.asicctl, -1);
+}
+static ssize_t asicctl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_store(dev, buf, size,
&drvdata->config.asicctl, ASICCTL);
+} +static DEVICE_ATTR_RW(asicctl);
+static ssize_t intack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
cti_write_intack(dev, val);
return size;
+} +static DEVICE_ATTR_WO(intack);
+static ssize_t appset_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_show(dev, buf, &drvdata->config.ctiappset, -1);
+}
+static ssize_t appset_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
return cti_reg32_store(dev, buf, size,
&drvdata->config.ctiappset, CTIAPPSET);
+} +static DEVICE_ATTR_RW(appset);
+static ssize_t appclear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val, mask;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
/* a 1'b1 in appclr clears down the same bit in appset*/
mask = ~val;
config->ctiappset &= mask;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPCLEAR, val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_WO(appclear);
+static ssize_t apppulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val, mask;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
/* a 1'b1 in apppulse sets then clears the bit,
* effectively clears down the same bit in appset
*/
mask = ~val;
config->ctiappset &= mask;
/* write through if enabled */
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPPULSE, val);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_WO(apppulse);
+static ssize_t itchout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return cti_reg32_show(dev, buf, NULL, ITCHOUT);
+}
+static ssize_t itchout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITCHOUT);
+} +static DEVICE_ATTR_RW(itchout);
+static ssize_t ittrigout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return cti_reg32_show(dev, buf, NULL, ITTRIGOUT);
+}
+static ssize_t ittrigout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITTRIGOUT);
+} +static DEVICE_ATTR_RW(ittrigout);
+static ssize_t itchinack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITCHINACK);
+} +static DEVICE_ATTR_WO(itchinack);
+static ssize_t ittriginack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, ITTRIGINACK);
+} +static DEVICE_ATTR_WO(ittriginack);
+static ssize_t itctrl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
return cti_reg32_show(dev, buf, NULL, CORESIGHT_ITCTRL);
+}
+static ssize_t itctrl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_reg32_store(dev, buf, size, NULL, CORESIGHT_ITCTRL);
+} +static DEVICE_ATTR_RW(itctrl);
+coresight_cti_reg(triginstatus, CTITRIGINSTATUS); +coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS); +coresight_cti_reg(chinstatus, CTICHINSTATUS); +coresight_cti_reg(choutstatus, CTICHOUTSTATUS); +coresight_cti_reg(ittrigin, ITTRIGIN); +coresight_cti_reg(itchin, ITCHIN); +coresight_cti_reg(itchoutack, ITCHOUTACK); +coresight_cti_reg(ittrigoutack, ITTRIGOUTACK);
+static struct attribute *coresight_cti_regs_attrs[] = {
&dev_attr_inout_sel.attr,
&dev_attr_inen.attr,
&dev_attr_outen.attr,
&dev_attr_gate.attr,
&dev_attr_asicctl.attr,
&dev_attr_intack.attr,
&dev_attr_appset.attr,
&dev_attr_appclear.attr,
&dev_attr_apppulse.attr,
&dev_attr_triginstatus.attr,
&dev_attr_trigoutstatus.attr,
&dev_attr_chinstatus.attr,
&dev_attr_choutstatus.attr,
&dev_attr_itctrl.attr,
&dev_attr_ittrigin.attr,
&dev_attr_itchin.attr,
&dev_attr_ittrigout.attr,
&dev_attr_itchout.attr,
&dev_attr_itchoutack.attr,
&dev_attr_ittrigoutack.attr,
&dev_attr_ittriginack.attr,
&dev_attr_itchinack.attr,
NULL,
+};
+/* channel / trigger api */ +static int +cti_trig_op_parse(struct device *dev, enum cti_chan_op op,
enum cti_trig_dir dir, const char *buf, size_t size)
+{
u32 chan_idx;
u32 trig_idx;
int items, err = size;
/* extract chan idx and trigger idx */
items = sscanf(buf, "%d %d", &chan_idx, &trig_idx);
if (items) {
err = cti_channel_trig_op(dev, op, dir, chan_idx, trig_idx);
if (!err)
err = size;
} else
err = -EINVAL;
return err;
+}
+static ssize_t trigin_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_attach);
+static ssize_t trigin_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_detach);
+static ssize_t trigout_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_attach);
+static ssize_t trigout_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_detach);
+static ssize_t gate_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = 0, channel = 0, items;
if (strcmp(buf, "all") == 0) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE_ALL, 0);
} else {
items = sscanf(buf, "%d", &channel);
if (!items)
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE, 0);
}
return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_enable);
+static ssize_t gate_disable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = 0, channel = 0, items;
if (strcmp(buf, "all") == 0) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE_ALL, 0);
} else {
items = sscanf(buf, "%d", &channel);
if (!items)
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE, 0);
}
return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_disable);
+static int +chan_op_parse(struct device *dev, enum cti_chan_set_op op, const char *buf) +{
int err = 0, channel = 0, items;
items = sscanf(buf, "%d", &channel);
if (!items)
return -EINVAL;
err = cti_channel_setop(dev, op, channel);
return err;
+}
+static ssize_t chan_set_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = chan_op_parse(dev, CTI_CHAN_SET, buf);
return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_set);
+static ssize_t chan_clear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = chan_op_parse(dev, CTI_CHAN_CLR, buf);
return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_clear);
+static ssize_t chan_pulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int err = chan_op_parse(dev, CTI_CHAN_PULSE, buf);
return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_pulse);
+static ssize_t trig_filter_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
spin_lock(&drvdata->spinlock);
val = drvdata->config.trig_filter_enable;
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "%ld (%s)\n", val,
val ? "enabled" : "disabled");
+}
+static ssize_t trig_filter_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
unsigned long val;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
drvdata->config.trig_filter_enable = val ? 1 : 0;
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_RW(trig_filter_enable);
+/* clear all xtrigger / channel programming */ +static ssize_t reset_xtrigs_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
int i;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
spin_lock(&drvdata->spinlock);
/* clear the CTI trigger / channel programming registers */
for (i = 0; i < config->nr_trig_max; i++) {
config->ctiinen[i] = 0;
config->ctiouten[i] = 0;
}
/* clear the other regs */
config->ctigate = (0x1 << config->nr_ctm_channels) - 1;
config->asicctl = 0;
config->ctiappset = 0;
config->ctiinout_sel = 0;
/* if enabled then write through */
if (CTI_PWR_ENA(config))
cti_write_all_hw_regs(drvdata);
spin_unlock(&drvdata->spinlock);
return size;
+} +static DEVICE_ATTR_WO(reset_xtrigs);
+/* list attachments by channel */ +#define LX_ADJUST_BUF_PTRS(n) \
do { buf_pos += n; buf_size -= n; \
if (buf_size <= 1) \
goto lx_buf_full; \
} while (0)
+static ssize_t list_xtrigs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
int used = 0, nr_channels, chan_idx, reg_idx;
/* buffer vars */
int buf_size = PAGE_SIZE;
char *buf_pos = buf;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *cfg = &drvdata->config;
u32 chan_mask;
nr_channels = cfg->nr_ctm_channels;
used = scnprintf(buf_pos, buf_size, "CTI:%s; Channels:%d\n",
dev_name(dev), nr_channels);
LX_ADJUST_BUF_PTRS(used);
/* show the IN and OUT triggers per channel */
for (chan_idx = 0; chan_idx < nr_channels; chan_idx++) {
chan_mask = 0x1 << chan_idx;
used = scnprintf(buf_pos, buf_size, "Chan %d: IN [ ", chan_idx);
LX_ADJUST_BUF_PTRS(used);
for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiinen[reg_idx]) {
used = scnprintf(buf_pos, buf_size, "%d ",
reg_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]; OUT [ ");
LX_ADJUST_BUF_PTRS(used);
for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiouten[reg_idx]) {
used = scnprintf(buf_pos, buf_size, "%d ",
reg_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "];\n");
LX_ADJUST_BUF_PTRS(used);
}
/* show the channels enabled via ctigate */
used = scnprintf(buf_pos, buf_size, "Gate Channels Enabled: [ ");
LX_ADJUST_BUF_PTRS(used);
if (cfg->ctigate == 0) {
used = scnprintf(buf_pos, buf_size, "None ]\n");
LX_ADJUST_BUF_PTRS(used);
} else {
for (chan_idx = 0; chan_idx < nr_channels; chan_idx++) {
chan_mask = 0x1 << chan_idx;
if (chan_mask & cfg->ctigate) {
used = scnprintf(buf_pos, buf_size, "%d ",
chan_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LX_ADJUST_BUF_PTRS(used);
}
+lx_buf_full:
used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
return used;
+} +static DEVICE_ATTR_RO(list_xtrigs);
+#define LCIU_ADJUST_BUF_PTRS(n) \
do { buf_pos += n; buf_size -= n; \
if (buf_size <= 1) \
goto lciu_buf_full; \
} while (0)
+static ssize_t list_chan_inuse_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
int used = 0, nr_channels, i;
/* buffer vars */
int buf_size = PAGE_SIZE;
char *buf_pos = buf;
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct cti_config *config = &drvdata->config;
u32 inuse_bits = 0, chan_mask, chan_bit_mask;
nr_channels = config->nr_ctm_channels;
spin_lock(&drvdata->spinlock);
for (i = 0; i < config->nr_trig_max; i++) {
inuse_bits |= config->ctiinen[i];
inuse_bits |= config->ctiouten[i];
}
spin_unlock(&drvdata->spinlock);
/* print in use channels */
chan_mask = ((u32)(0x1 << nr_channels)) - 1;
if (inuse_bits & chan_mask) {
used = scnprintf(buf_pos, buf_size, "Chan in use [ ");
LCIU_ADJUST_BUF_PTRS(used);
for (i = 0; i < nr_channels; i++) {
chan_bit_mask = 0x1 << i;
if (chan_bit_mask & inuse_bits) {
used = scnprintf(buf_pos, buf_size, "%d ", i);
LCIU_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LCIU_ADJUST_BUF_PTRS(used);
} else {
used = scnprintf(buf_pos, buf_size, "Chan in use [ none ]\n");
LCIU_ADJUST_BUF_PTRS(used);
}
/* print free channels */
inuse_bits = ~inuse_bits; /* flip for free channels */
if (inuse_bits & chan_mask) {
used = scnprintf(buf_pos, buf_size, "Chan free [ ");
LCIU_ADJUST_BUF_PTRS(used);
for (i = 0; i < nr_channels; i++) {
chan_bit_mask = 0x1 << i;
if (chan_bit_mask & inuse_bits) {
used = scnprintf(buf_pos, buf_size, "%d ", i);
LCIU_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LCIU_ADJUST_BUF_PTRS(used);
} else {
used = scnprintf(buf_pos, buf_size, "Chan free [ none ]\n");
LCIU_ADJUST_BUF_PTRS(used);
}
+lciu_buf_full:
used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
return used;
+} +static DEVICE_ATTR_RO(list_chan_inuse);
+static struct attribute *coresight_cti_channel_attrs[] = {
&dev_attr_trigin_attach.attr,
&dev_attr_trigin_detach.attr,
&dev_attr_trigout_attach.attr,
&dev_attr_trigout_detach.attr,
&dev_attr_gate_enable.attr,
&dev_attr_gate_disable.attr,
&dev_attr_chan_set.attr,
&dev_attr_chan_clear.attr,
&dev_attr_chan_pulse.attr,
&dev_attr_trig_filter_enable.attr,
&dev_attr_list_xtrigs.attr,
&dev_attr_reset_xtrigs.attr,
&dev_attr_list_chan_inuse.attr,
NULL,
+};
+/* coresight management registers */ +coresight_cti_reg(devaff0, CTIDEVAFF0); +coresight_cti_reg(devaff1, CTIDEVAFF1); +coresight_cti_reg(authstatus, CORESIGHT_AUTHSTATUS); +coresight_cti_reg(devarch, CORESIGHT_DEVARCH); +coresight_cti_reg(devid, CORESIGHT_DEVID); +coresight_cti_reg(devtype, CORESIGHT_DEVTYPE); +coresight_cti_reg(pidr0, CORESIGHT_PERIPHIDR0); +coresight_cti_reg(pidr1, CORESIGHT_PERIPHIDR1); +coresight_cti_reg(pidr2, CORESIGHT_PERIPHIDR2); +coresight_cti_reg(pidr3, CORESIGHT_PERIPHIDR3); +coresight_cti_reg(pidr4, CORESIGHT_PERIPHIDR4);
+static struct attribute *coresight_cti_mgmt_attrs[] = {
&dev_attr_devaff0.attr,
&dev_attr_devaff1.attr,
&dev_attr_authstatus.attr,
&dev_attr_devarch.attr,
&dev_attr_devid.attr,
&dev_attr_devtype.attr,
&dev_attr_pidr0.attr,
&dev_attr_pidr1.attr,
&dev_attr_pidr2.attr,
&dev_attr_pidr3.attr,
&dev_attr_pidr4.attr,
NULL,
+};
+static const struct attribute_group coresight_cti_group = {
.attrs = coresight_cti_attrs,
+};
+static const struct attribute_group coresight_cti_regs_group = {
.attrs = coresight_cti_regs_attrs,
.name = "regs",
+};
+static const struct attribute_group coresight_cti_channels_group = {
.attrs = coresight_cti_channel_attrs,
.name = "channels",
+};
+static const struct attribute_group coresight_cti_mgmt_group = {
.attrs = coresight_cti_mgmt_attrs,
.name = "mgmt",
+};
+const struct attribute_group *coresight_cti_groups[] = {
&coresight_cti_group,
&coresight_cti_regs_group,
&coresight_cti_channels_group,
&coresight_cti_mgmt_group,
NULL,
+};
+static int cti_starting_cpu(unsigned int cpu) +{
struct cti_drvdata *drvdata = cti_cpu_drv[cpu];
if (!drvdata)
return 0;
spin_lock(&drvdata->spinlock);
drvdata->config.hw_powered = true;
spin_unlock(&drvdata->spinlock);
if (atomic_read(&drvdata->config.enable_req_count))
cti_enable_hw(drvdata);
return 0;
+}
+static int cti_stopping_cpu(unsigned int cpu) +{
struct cti_drvdata *drvdata = cti_cpu_drv[cpu];
if (!drvdata)
return 0;
cti_disable_hw(drvdata);
spin_lock(&drvdata->spinlock);
drvdata->config.hw_powered = false;
spin_unlock(&drvdata->spinlock);
return 0;
+}
+static void cti_smp_set_powered(void *info) +{
struct cti_drvdata *drvdata = info;
drvdata->config.hw_powered = true;
+}
+/* setup power handling on a cti that has an association with a cpu */ +static int cti_setup_hp_cpu_affinity(struct cti_drvdata *drvdata) +{
int ret = 0;
/* CPU affinity - add to quick lookup array
* and register cpu hp callbacks first time out
*/
cpus_read_lock();
cti_cpu_drv[drvdata->ctidev.cpu] = drvdata;
if (!cti_cpu_count) {
cti_hp_online = cpuhp_setup_state_nocalls_cpuslocked(
CPUHP_AP_ONLINE_DYN, "arm/coresightcti:online",
cti_starting_cpu, cti_stopping_cpu);
if (cti_hp_online < 0) {
ret = cti_hp_online;
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
goto cti_hp_cpu_done;
}
}
cti_cpu_count++;
/* setup powered flag by running function of assoc cpu */
if (smp_call_function_single(drvdata->ctidev.cpu, cti_smp_set_powered,
drvdata, 1)) {
drvdata->config.hw_powered = false;
}
+cti_hp_cpu_done:
cpus_read_unlock();
return ret;
+}
+/* back out hotplug notifications on error */ +static void cti_clear_hp_cpu_affinity(struct cti_drvdata *drvdata) +{
if (!drvdata)
return;
if (drvdata->ctidev.cpu >= 0) {
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
cti_cpu_count--;
if ((cti_cpu_count == 0) && (cti_hp_online > 0))
cpuhp_remove_state_nocalls(cti_hp_online);
}
+}
The CPU hotplug bits can be split in their own file.
+static int cti_probe(struct amba_device *adev, const struct amba_id *id) +{
int ret = 0;
u32 devid;
void __iomem *base;
struct device *dev = &adev->dev;
struct cti_drvdata *drvdata = 0;
struct coresight_desc cti_desc;
struct coresight_platform_data *pdata = NULL;
struct resource *res = &adev->res;
struct device_node *np = adev->dev.of_node;
struct ect_node *ect_nd = 0;
/* boilerplate code to set up the basics */
dev_info(dev, "%s(ID = %x)\n", __func__, id->id);
if (np) {
pdata = of_get_coresight_ect_platform_data(dev, np);
if (IS_ERR(pdata)) {
dev_info(dev, "of_get_coresight_ect_platform err\n");
ret = PTR_ERR(pdata);
goto err_out;
}
dev->platform_data = pdata;
}
/* node to keep track of CTI net */
ect_nd = kzalloc(sizeof(struct ect_node), GFP_KERNEL);
if (!ect_nd) {
ret = -ENOMEM;
goto err_out;
}
/* driver data*/
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata) {
ret = -ENOMEM;
dev_info(dev, "%s, mem err\n", __func__);
goto err_out;
}
/* links between dev and drvdata*/
drvdata->dev = dev;
dev_set_drvdata(dev, drvdata);
/* default CTI device info */
drvdata->ctidev.cpu = -1;
drvdata->ctidev.nr_trig_con = 0;
drvdata->ctidev.ctm_id = 0;
INIT_LIST_HEAD(&drvdata->ctidev.trig_cons);
/* Validity for the resource is already checked by the AMBA core */
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base)) {
ret = PTR_ERR(base);
dev_info(dev, "%s, remap err\n", __func__);
goto err_out;
}
drvdata->base = base;
spin_lock_init(&drvdata->spinlock);
/* look at the HW DEVID register for some of the HW settings */
devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
drvdata->config.nr_trig_max = (int)((devid & 0xFF00) >> 8);
/* no current hardware should exceed this, but protect the driver
* in case of fault / out of spec hw
*/
if (drvdata->config.nr_trig_max > CTIINOUTEN_MAX) {
dev_warn_once(dev,
"Limiting HW MaxTrig value(%d) to driver max(%d)\n",
drvdata->config.nr_trig_max, CTIINOUTEN_MAX);
drvdata->config.nr_trig_max = CTIINOUTEN_MAX;
}
drvdata->config.nr_ctm_channels = (int)((devid & 0xF0000) >> 16);
dev_info(dev, "DevID %08x, trigs=%d, chans=%d\n", devid,
drvdata->config.nr_trig_max, drvdata->config.nr_ctm_channels);
/** additional parse the .dts for connections and signals */
of_cti_get_hw_data(dev, np, drvdata);
cti_set_default_config(&drvdata->config);
/* setup cpu related CTI devices, otherwise assume powered */
if (drvdata->ctidev.cpu >= 0) {
ret = cti_setup_hp_cpu_affinity(drvdata);
if (ret < 0)
goto err_out;
} else
drvdata->config.hw_powered = true;
/* set up coresight component description */
cti_desc.pdata = pdata;
cti_desc.type = CORESIGHT_DEV_TYPE_ECT;
cti_desc.subtype.ect_subtype = CORESIGHT_DEV_SUBTYPE_ECT_CTI;
cti_desc.ops = &cti_ops;
cti_desc.groups = coresight_cti_groups;
cti_desc.dev = dev;
drvdata->csdev = coresight_register(&cti_desc);
if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
pr_err("%s: CS register failed\n", pdata->name);
goto err_out;
}
/* add to list of CTI devices */
mutex_lock(&ect_mutex);
ect_nd->cti_drv = drvdata;
list_add(&ect_nd->next, &ect_net);
mutex_unlock(&ect_mutex);
/* set any cross references */
cti_update_conn_xrefs(drvdata);
/* all done - dec pm refcount */
pm_runtime_put(&adev->dev);
dev_info(dev, "%s: initialized\n", pdata->name);
cti_count++;
dev_info(dev, "%s - ok\n", __func__);
return 0;
+err_out:
cti_clear_hp_cpu_affinity(drvdata);
dev_info(dev, "%s - err_out\n", __func__);
return ret;
+}
+/* free up CTI specific resources
- called from coresight_device_release on coresight_unregister.
- */
Multiline comment format problem.
More to come tomorrow, Mathieu
+void cti_device_release(struct device *dev) +{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct ect_node *ect_item, *ect_tmp;
struct cti_trig_con *tc, *tc_tmp;
/* free up resources associated with the cti connections */
if (drvdata->ctidev.cpu >= 0)
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
list_for_each_entry_safe(tc, tc_tmp,
&drvdata->ctidev.trig_cons, node) {
kfree(tc->con_in);
kfree(tc->con_out);
kfree(tc->con_dev_name);
list_del(&tc->node);
kfree(tc);
}
/* remove from the list */
mutex_lock(&ect_mutex);
list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, next) {
if (ect_item->cti_drv == drvdata) {
list_del(&ect_item->next);
kfree(ect_item);
goto ect_list_item_removed;
}
}
+ect_list_item_removed:
mutex_unlock(&ect_mutex);
kfree(drvdata);
+} +EXPORT_SYMBOL_GPL(cti_device_release);
+#define CTI_AMBA_ID(pid) \
{ \
.id = pid, \
.mask = 0x000fffff, \
}
+static struct amba_cs_uci_id uci_id_cti[] = {
{
/* CTI UCI data */
.devarch = 0x47701a14, /* devarch value for CTI v2 */
.devarch_mask = 0xfff0ffff,
.devtype = 0x00000014, /* maj(0x4-debug) min(0x1-ECT) */
}
+};
+#define CTI_AMBA_UCI_ID(pid) \
{ \
.id = pid, \
.mask = 0x000fffff, \
.data = uci_id_cti, \
}
+static const struct amba_id cti_ids[] = {
CTI_AMBA_ID(0x000bb906), /* Coresight CTI (SoC 400), C-A72, C-A57 */
CTI_AMBA_ID(0x000bb922), /* CTI - C-A8 */
CTI_AMBA_ID(0x000bb9a8), /* CTI - C-A53 */
CTI_AMBA_ID(0x000bb9aa), /* CTI - C-A73 */
CTI_AMBA_UCI_ID(0x000bb9da), /* CTI - C-A35 */
CTI_AMBA_UCI_ID(0x000bb9ed), /* Coresight CTI (SoC 600) */
CTI_AMBA_UCI_ID(0x000bb95d), /* test UCI - ETM PID A53 */
{ 0, 0},
+};
+static struct amba_driver cti_driver = {
.drv = {
.name = "coresight-cti",
.owner = THIS_MODULE,
.suppress_bind_attrs = true,
},
.probe = cti_probe,
.id_table = cti_ids,
+}; +builtin_amba_driver(cti_driver); diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h new file mode 100644 index 000000000000..09cb985ac0ea --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#ifndef _CORESIGHT_CORESIGHT_CTI_H +#define _CORESIGHT_CORESIGHT_CTI_H
+#include <asm/local.h> +#include <linux/spinlock.h> +#include "coresight-priv.h"
+/*
- Device registers
- 0x000 - 0x144: CTI programming and status
- 0xEDC - 0xEF8: CTI integration test.
- 0xF00 - 0xFFC: Coresight management registers.
- */
+/* CTI programming registers */ +#define CTICONTROL 0x000 +#define CTIINTACK 0x010 +#define CTIAPPSET 0x014 +#define CTIAPPCLEAR 0x018 +#define CTIAPPPULSE 0x01C +#define CTIINEN(n) (0x020 + (4 * n)) +#define CTIOUTEN(n) (0x0A0 + (4 * n)) +#define CTITRIGINSTATUS 0x130 +#define CTITRIGOUTSTATUS 0x134 +#define CTICHINSTATUS 0x138 +#define CTICHOUTSTATUS 0x13C +#define CTIGATE 0x140 +#define ASICCTL 0x144 +/* Integration test registers */ +#define ITCHINACK 0xEDC /* WO CTI CSSoc 400 only*/ +#define ITTRIGINACK 0xEE0 /* WO CTI CSSoc 400 only*/ +#define ITCHOUT 0xEE4 /* WO RW-600 */ +#define ITTRIGOUT 0xEE8 /* WO RW-600 */ +#define ITCHOUTACK 0xEEC /* RO CTI CSSoc 400 only*/ +#define ITTRIGOUTACK 0xEF0 /* RO CTI CSSoc 400 only*/ +#define ITCHIN 0xEF4 /* RO */ +#define ITTRIGIN 0xEF8 /* RO */ +/* management registers */ +#define CTIDEVAFF0 0xFA8 +#define CTIDEVAFF1 0xFAC
+/* CTI CSSoc 600 has a max of 32 trigger signals per direction.
- CTI CSSoc 400 has 8 IO triggers - other CTIs can be impl def.
- Max of in and out defined in the DEVID register.
- pick up actual number used from .dts parameters if present.
- */
+#define CTIINOUTEN_MAX 32
+/**
- Group of related trigger signals
- @nr_sigs: number of signals in the group.
- @used_mask: bitmask representing the signal indexes in the group.
- @sig_names: list of names for the signals.
- */
+struct cti_trig_grp {
int nr_sigs;
u32 used_mask;
char *sig_names;
+};
+/**
- Trigger connection - connection between a CTI and other (coresight) device
- lists input and output trigger signals for the device
- @con_in: connected CTIIN signals for the device.
- @con_out: connected CTIOUT signals for the device.
- @con_dev: coresight device connected to the CTI, NULL if not CS device
- @con_dev_name: name of connected device (CS or CPU)
- @node: entry node in list of connections.
- */
+struct cti_trig_con {
struct cti_trig_grp *con_in;
struct cti_trig_grp *con_out;
struct coresight_device *con_dev;
char *con_dev_name;
struct list_head node;
+};
+/**
- struct cti_device - description of CTI device properties.
- @nt_trig_con: Number of external devices connected to this device.
- @ctm_id: which CTM this device is connected to (by default it is
assumed there is a single CTM per SoC, ID 0).
- @trig_cons: list of connections to this device.
- @cpu: CPU ID if associated with CPU, -1 otherwise.
- */
+struct cti_device {
int nr_trig_con;
u32 ctm_id;
struct list_head trig_cons;
int cpu;
+};
+/**
- struct cti_config - configuration of the CTI device hardware
- hardware description from RO ID regs
- @nr_trig_max: Max number of trigger signals implemented on device.
(max of trig_in or trig_out)
- @nr_ctm_channels: number of available CTM channels
- cti enable control
- @enable_req_count: CTI is enabled alongside >=1 associated devices.
- @hw_enabled: true if hw is currently enabled.
- @hw_powered: true if associated cpu powered on, or no cpu.
- registered triggers and filtering
- @trig_in_use: bitfield of in triggers registered as in use.
- @trig_out_use: bitfield of out triggers registered as in use.
- @trig_out_filter: bitfield of out triggers that are blocked if filter
enabled. Typically this would be dbgreq / restart on a core CTI.
- @trig_filter_enable: 1 if filtering enabled.
- cti software programmable regs:
- @ctiappset: CTI Software application channel set.
- @ctiinout_sel: register selector for INEN and OUTEN regs.
- @ctiinen: enable input trigger to a channel.
- @ctiouten: enable output trigger from a channel.
- @ctigate: gate channel output from CTI to CTM.
- */
+struct cti_config {
/* hardware description */
int nr_ctm_channels;
int nr_trig_max;
/* cti enable control */
atomic_t enable_req_count;
bool hw_enabled;
bool hw_powered;
/* registered triggers and filtering */
u32 trig_in_use;
u32 trig_out_use;
u32 trig_out_filter;
int trig_filter_enable;
/* cti cross trig programmable regs */
u32 ctiappset;
u8 ctiinout_sel;
u32 ctiinen[CTIINOUTEN_MAX];
u32 ctiouten[CTIINOUTEN_MAX];
u32 ctigate;
u32 asicctl;
+};
+/**
- struct cti_drvdata - specifics for the CTI device
- @base: Memory mapped base address for this component.
- @dev: The device entity associated to this component.
- @csdev: Standard CoreSight device information.
- @ctidev: Extra information needed by the CTI/CTM framework.
- @spinlock: Control data access to one at a time.
- @config: Configuration data for this CTI device.
- */
+struct cti_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
struct cti_device ctidev;
spinlock_t spinlock;
struct cti_config config;
+};
+enum cti_chan_op {
CTI_CHAN_ATTACH,
CTI_CHAN_DETACH,
+};
+enum cti_trig_dir {
CTI_TRIG_IN,
CTI_TRIG_OUT,
+};
+enum cti_chan_gate_op {
CTI_GATE_CHAN_ENABLE,
CTI_GATE_CHAN_DISABLE,
CTI_GATE_CHAN_ENABLE_ALL,
CTI_GATE_CHAN_DISABLE_ALL,
+};
+enum cti_chan_set_op {
CTI_CHAN_SET,
CTI_CHAN_CLR,
CTI_CHAN_PULSE,
+};
+#endif /* _CORESIGHT_CORESIGHT_CTI_H */
2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
On Wed, Jan 09, 2019 at 10:54:38PM +0000, Mike Leach wrote:
Initial addition of the files for the Coresight CTI driver.
Signed-off-by: Mike Leach mike.leach@linaro.org
drivers/hwtracing/coresight/coresight-cti.c | 2025 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-cti.h | 195 ++ 2 files changed, 2220 insertions(+) create mode 100644 drivers/hwtracing/coresight/coresight-cti.c create mode 100644 drivers/hwtracing/coresight/coresight-cti.h
diff --git a/drivers/hwtracing/coresight/coresight-cti.c b/drivers/hwtracing/coresight/coresight-cti.c new file mode 100644 index 000000000000..d06e19f111e2 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.c @@ -0,0 +1,2025 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/smp.h> +#include <linux/sysfs.h> +#include <linux/stat.h> +#include <linux/clk.h> +#include <linux/cpu.h> +#include <linux/coresight.h> +#include <linux/coresight-pmu.h> +#include <linux/pm_wakeup.h> +#include <linux/amba/bus.h> +#include <linux/seq_file.h> +#include <linux/uaccess.h> +#include <linux/perf_event.h> +#include <linux/pm_runtime.h> +#include <linux/of.h> +#include <asm/sections.h> +#include <asm/local.h>
+#include "coresight-cti.h"
+#define csdev_to_cti_drvdata(csdev) \
- dev_get_drvdata(csdev->dev.parent)
+/* CTI device tree trigger connection node keyword */ +#define CTI_DT_CONNS "trig-conns" +/* CTI device tree connection property keywords */ +#define CTI_DT_V8ARCH "arm,cti-v8-arch" +#define CTI_DT_TRIGIN_SIGS "arm,trig-in-sigs" +#define CTI_DT_TRIGOUT_SIGS "arm,trig-out-sigs" +#define CTI_DT_FILTER_OUT_SIGS "arm,trig-filters" +#define CTI_DT_CSDEV_ASSOC "arm,cs-dev-assoc" +#define CTI_DT_CTM_ID "arm,cti-ctm-id" +#define CTI_DT_CONN_NAME "arm,trig-conn-name"
+/**
- CTI devices can be associated with a PE, or be connected to CoreSight
- hardware. We have a list of all CTIs, and a subset array associated
- with individual PEs for pwr management as they will power up in the
- PE power domain.
- At this point we assume that the none CPU CTIs are always powered as
- we do with sinks etc.
- We leave the client to figure out if all the CTIs are interconnected with
- the same CTM, in general this is the case but does not always have to be.
- */
+struct ect_node {
- struct cti_drvdata *cti_drv;
- struct list_head next;
+};
+/* net of CTI devices connected via CTM */ +LIST_HEAD(ect_net);
+/* protect the list */ +static DEFINE_MUTEX(ect_mutex);
+/* quick reference for CPU CTIs */ +static struct cti_drvdata *cti_cpu_drv[NR_CPUS];
+/* number of registered CTI devices */ +static int cti_count;
+/* number of cpu associated CTI devices in use */ +static int cti_cpu_count;
+/* CTI / cpu hotplug notification ID */ +static enum cpuhp_state cti_hp_online;
+/* cpu powered and enabled */ +#define CTI_PWR_ENA(p_cfg) (p_cfg->hw_enabled && p_cfg->hw_powered)
+/* write set of regs to hardware - call with spinlock claimed */ +static void cti_write_all_hw_regs(struct cti_drvdata *drvdata) +{
- struct cti_config *config = &drvdata->config;
- int i;
- CS_UNLOCK(drvdata->base);
- /* disable CTI before writing registers */
- writel_relaxed(0, drvdata->base + CTICONTROL);
- /* write the CTI trigger registers */
- for (i = 0; i < config->nr_trig_max; i++) {
writel_relaxed(config->ctiinen[i], drvdata->base + CTIINEN(i));
writel_relaxed(config->ctiouten[i],
drvdata->base + CTIOUTEN(i));
- }
- /* other regs */
- writel_relaxed(config->ctigate, drvdata->base + CTIGATE);
- writel_relaxed(config->asicctl, drvdata->base + ASICCTL);
- writel_relaxed(config->ctiappset, drvdata->base + CTIAPPSET);
- /* re-enable CTI */
- writel_relaxed(1, drvdata->base + CTICONTROL);
- CS_LOCK(drvdata->base);
+}
+static void cti_enable_hw_smp_call(void *info) +{
- struct cti_drvdata *drvdata = info;
- cti_write_all_hw_regs(drvdata);
+}
+/* write regs to hardware and enable */ +static int cti_enable_hw(void *info) +{
- struct cti_drvdata *drvdata = info;
- struct cti_config *config = &drvdata->config;
- int rc = 0;
- pm_runtime_get_sync(drvdata->dev);
- spin_lock(&drvdata->spinlock);
- /* no need to do anything if enabled or unpowered*/
- if (config->hw_enabled || !config->hw_powered) {
spin_unlock(&drvdata->spinlock);
pm_runtime_put(drvdata->dev);
return 0;
- }
- if (drvdata->ctidev.cpu >= 0) {
dev_info(drvdata->dev, "cti enable smp call for cpu %d\n",
drvdata->ctidev.cpu);
rc = smp_call_function_single(drvdata->ctidev.cpu,
cti_enable_hw_smp_call,
drvdata, 1);
- } else {
dev_info(drvdata->dev, "cti enable not cpu call\n");
cti_write_all_hw_regs(drvdata);
- }
- if (!rc)
config->hw_enabled = true;
- spin_unlock(&drvdata->spinlock);
- return 0;
+}
+/* disable hardware */ +static int cti_disable_hw(void *info) +{
- struct cti_drvdata *drvdata = info;
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- /* no need to do anything if disabled or cpu unpowered*/
- if (!config->hw_enabled || !config->hw_powered) {
spin_unlock(&drvdata->spinlock);
return 0;
- }
- CS_UNLOCK(drvdata->base);
- /* disable CTI */
- writel_relaxed(0, drvdata->base + CTICONTROL);
- config->hw_enabled = false;
- CS_LOCK(drvdata->base);
- spin_unlock(&drvdata->spinlock);
- pm_runtime_put(drvdata->dev);
- return 0;
+}
+static void +cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value) +{
- CS_UNLOCK(drvdata->base);
- writel_relaxed(value, drvdata->base+offset);
- CS_LOCK(drvdata->base);
+}
+static void cti_set_default_config(struct cti_config *config) +{
- /* Most regs default to 0 as zalloc'ed except...*/
- config->trig_filter_enable = 1;
- config->ctigate = ((u32)0x1 << config->nr_ctm_channels) - 1;
- atomic_set(&config->enable_req_count, 0);
+}
+static void cti_write_intack(struct device *dev, u32 ackval) +{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- /* write if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIINTACK, ackval);
- spin_unlock(&drvdata->spinlock);
+}
+/* need to update cross references once CTI csdev has been generated. */ +static void cti_update_conn_xrefs(struct cti_drvdata *drvdata) +{
- struct cti_trig_con *tc;
- struct cti_device *ctidev = &drvdata->ctidev;
- list_for_each_entry(tc, &ctidev->trig_cons, node) {
if (tc->con_dev) {
tc->con_dev->ect_dev = drvdata->csdev;
dev_info(drvdata->dev,
"Setting assoc csdev (%px) with (%px)\n",
tc->con_dev, drvdata->csdev);
}
- }
+}
+/* connection information */ +static int cti_add_connection_entry(struct cti_drvdata *drvdata,
struct cti_trig_grp *in_info,
struct cti_trig_grp *out_info,
struct coresight_device *csdev,
const char *assoc_dev_name)
+{
- struct cti_trig_con *tc;
- struct cti_device *cti_dev = &drvdata->ctidev;
- tc = kzalloc(sizeof(struct cti_trig_con), GFP_KERNEL);
- if (tc == 0)
return -ENOMEM;
- tc->con_dev = csdev;
- /* prefer actual associated CS device dev name to supplied value -
* which is likely to be node name / other conn name
*/
- if (csdev)
tc->con_dev_name = kstrdup(dev_name(&csdev->dev), GFP_KERNEL);
- else if (assoc_dev_name != NULL)
tc->con_dev_name = kstrdup(assoc_dev_name, GFP_KERNEL);
- tc->con_in = in_info;
- tc->con_out = out_info;
- list_add_tail(&tc->node, &cti_dev->trig_cons);
- cti_dev->nr_trig_con++;
- /* add connection usage bit info to overall info */
- drvdata->config.trig_in_use |= in_info->used_mask;
- drvdata->config.trig_out_use |= out_info->used_mask;
- return 0;
+}
+/* add a default connection if nothing else is specified.
- single connection based on max in/out info, no assoc device
- */
+static int cti_add_default_connection(struct cti_drvdata *drvdata) +{
- struct cti_trig_grp *in, *out;
- int n_trigs = drvdata->config.nr_trig_max;
- u32 n_trig_mask = (0x1 << n_trigs) - 1;
- in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!in)
return -ENOMEM;
- out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!out)
return -ENOMEM;
- dev_info(drvdata->dev, "default connection: trigs %d, trig_mask %08x\n",
n_trigs, n_trig_mask);
- /* assume max trigs for in and out, all used */
- in->nr_sigs = n_trigs;
- in->used_mask = n_trig_mask;
- in->sig_names = NULL;
- out->nr_sigs = n_trigs;
- out->used_mask = n_trig_mask;
- out->sig_names = NULL;
- return cti_add_connection_entry(drvdata, in, out, NULL, "default");
+}
+/* create an architecturally defined v8 connection
- must hava a cpu, can have an ETM
- */
+static int of_cti_create_v8_connections(struct cti_drvdata *drvdata,
struct device_node *np)
+{
- struct cti_device *cti_dev = &drvdata->ctidev;
- struct cti_trig_grp *in, *out;
- int cpuid = 0;
- struct device_node *cs_np;
- struct coresight_device *csdev = NULL;
- char cpu_name_str[16];
- const char *assoc_name = NULL;
- cpuid = of_coresight_ect_get_cpu(np);
- if (cpuid < 0) {
dev_warn(drvdata->dev, "CTI v8 DT binding no cpu\n");
return -EINVAL;
- }
- cti_dev->cpu = cpuid;
- /* do the v8 cpu connection */
- in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!in)
return -ENOMEM;
- out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!out)
return -ENOMEM;
- /* v8 PE CTI config... */
- in->nr_sigs = 3;
- in->used_mask = 0x7;
- in->sig_names = NULL;
- out->nr_sigs = 3;
- out->used_mask = 0x7;
- out->sig_names = NULL;
- scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
- cti_add_connection_entry(drvdata, in, out, NULL, cpu_name_str);
- drvdata->config.trig_out_filter = 0x1; /* filter dbgreq */
- /* v8 ETM associated config... */
- cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
- if (cs_np) {
in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!in)
return -ENOMEM;
out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
if (!out)
return -ENOMEM;
in->nr_sigs = 4;
in->used_mask = 0xF0;
in->sig_names = NULL;
out->nr_sigs = 4;
out->used_mask = 0xF0;
out->sig_names = NULL;
dev_info(drvdata->dev, "v8 finding assoc dev %s\n",
cs_np->full_name);
csdev = of_coresight_get_cs_device_by_node(cs_np);
if (csdev)
assoc_name = dev_name(&csdev->dev);
else
assoc_name = cs_np->full_name;
cti_add_connection_entry(drvdata, in, out, csdev, assoc_name);
of_node_put(cs_np);
- }
- return 0;
+}
+static int of_cti_read_trig_group(struct cti_trig_grp **trig_grp_ptr,
struct device_node *np,
const char *grp_name,
int max_trigs)
+{
- int items, err = 0;
- u32 value, pidx;
- struct cti_trig_grp *grp;
- grp = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);
- if (!grp)
return -ENOMEM;
- *trig_grp_ptr = grp;
- items = of_property_count_elems_of_size(np, grp_name, 4);
- /* if the property doesn't exist or has no values, then return
* an empty connection group
*/
- if (items < 0)
return 0;
- if (items > max_trigs)
return -EINVAL;
- /* set the number of signals and usage mask */
- for (pidx = 0; pidx < items; pidx++) {
err = of_property_read_u32_index(np, grp_name, pidx, &value);
if (err)
return err;
grp->nr_sigs++;
grp->used_mask |= (0x1 << value);
- }
- return 0;
+}
+static int of_cti_create_connection(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
- struct cti_trig_grp *in = 0, *out = 0, *filter = 0;
- int cpuid = -1, err = 0, trig_max = drvdata->config.nr_trig_max;
- struct device_node *cs_np;
- struct coresight_device *csdev = NULL;
- const char *assoc_name = "unknown";
- char cpu_name_str[16];
- /* look for the signals properties. */
- err = of_cti_read_trig_group(&in, np, CTI_DT_TRIGIN_SIGS, trig_max);
- if (err)
return err;
- err = of_cti_read_trig_group(&out, np, CTI_DT_TRIGOUT_SIGS, trig_max);
- if (err)
return err;
- err = of_cti_read_trig_group(&filter, np, CTI_DT_FILTER_OUT_SIGS,
trig_max);
- if (err)
return err;
- /* read the connection name if set - may be overridden by later */
- of_property_read_string(np, CTI_DT_CONN_NAME, &assoc_name);
- /* associated cpu ? */
- cpuid = of_coresight_ect_get_cpu(np);
- drvdata->ctidev.cpu = cpuid;
- if (cpuid >= 0) {
scnprintf(cpu_name_str, 16, "cpu%d", cpuid);
assoc_name = cpu_name_str;
- } else {
/* associated device ? */
cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);
if (cs_np) {
dev_info(dev, "finding assoc dev %s\n",
cs_np->full_name);
csdev = of_coresight_get_cs_device_by_node(cs_np);
if (csdev) /* use device name if csdev found */
assoc_name = dev_name(&csdev->dev);
else /* otherwise node name for later association */
assoc_name = cs_np->full_name;
of_node_put(cs_np);
}
- }
- /* set up a connection */
- err = cti_add_connection_entry(drvdata, in, out, csdev, assoc_name);
- /* note any filter info */
- if (!err)
drvdata->config.trig_out_filter |= filter->used_mask;
- kfree(filter);
- return err;
+}
+/* get the hardware configuration & connection data. */ +static int of_cti_get_hw_data(struct device *dev,
struct device_node *np,
struct cti_drvdata *drvdata)
+{
- int rc = 0;
- struct cti_device *cti_dev = &drvdata->ctidev;
- struct device_node *nc = NULL;
- /* get any CTM ID - defaults to 0 */
- of_property_read_u32(np, CTI_DT_CTM_ID, &cti_dev->ctm_id);
- if (of_property_read_bool(np, CTI_DT_V8ARCH)) {
rc = of_cti_create_v8_connections(drvdata, np);
- } else {
for_each_child_of_node(np, nc) {
if (of_node_cmp(nc->name, CTI_DT_CONNS) != 0)
continue;
dev_info(dev, "dt found node: %s %s\n",
nc->name, nc->full_name);
rc = of_cti_create_connection(dev, nc, drvdata);
if (rc != 0)
return rc;
}
- }
- /* if no connections, just add a single default based on max IN-OUT */
- if (cti_dev->nr_trig_con == 0)
rc = cti_add_default_connection(drvdata);
- return rc;
+}
+static int +cti_match_con_name(struct cti_device *ctidev, const char *node_name,
const char *csdev_name)
+{
- int found = 0;
- struct cti_trig_con *trig_con;
- list_for_each_entry(trig_con, &ctidev->trig_cons, node) {
if (trig_con->con_dev_name) {
if (!strcmp(node_name, trig_con->con_dev_name)) {
found = 1;
/* match: so swap in csdev name */
kfree(trig_con->con_dev_name);
trig_con->con_dev_name =
kstrdup(csdev_name, GFP_KERNEL);
goto cti_con_name_match;
}
}
- }
+cti_con_name_match:
- return found;
+}
+/* search the cti list to add an associated CTI into the supplied CS device
- This will set the association if CTI declared before the CS device
- */
+int cti_add_assoc_to_csdev(struct coresight_device *csdev) +{
- struct ect_node *ect_item;
- struct cti_device *ctidev;
- const char *node_name = NULL, *csdev_name;
- /* exit early for no CTIs or self referencing ECT devices.*/
- if (!cti_count) {
dev_info(&csdev->dev, "no CTI to check\n");
return 0;
- }
- if (!csdev->dev.parent->of_node) {
dev_info(&csdev->dev, "No parent of_node pointer\n");
return 0;
- }
- node_name = csdev->dev.parent->of_node->full_name;
- if (!node_name) {
dev_info(&csdev->dev, "%s - bad node name\n", __func__);
return 0;
- }
- dev_info(&csdev->dev, "%s - looking for %s\n", __func__, node_name);
- if (!cti_count || (csdev->type == CORESIGHT_DEV_TYPE_ECT)) {
dev_info(&csdev->dev, "no CTI or skip self ref CTI\n");
return 0;
- }
- csdev_name = dev_name(&csdev->dev);
- /* for each CTI in list... */
- mutex_lock(&ect_mutex);
- list_for_each_entry(ect_item, &ect_net, next) {
ctidev = &ect_item->cti_drv->ctidev;
if (cti_match_con_name(ctidev, node_name, csdev_name)) {
csdev->ect_dev = ect_item->cti_drv->csdev;
dev_info(&csdev->dev, "Found!\n");
goto cti_found;
}
- }
+cti_found:
- mutex_unlock(&ect_mutex);
- return 0;
+} +EXPORT_SYMBOL_GPL(cti_add_assoc_to_csdev);
+/* attach/detach channel from trigger - write through if enabled. */ +static int cti_channel_trig_op(struct device *dev,
enum cti_chan_op op,
enum cti_trig_dir direction,
u32 channel_idx,
u32 trigger_idx)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- u32 trig_bitmask;
- u32 chan_bitmask;
- u32 reg_value;
- int reg_offset;
- dev_info(dev, "chan_trig_op: op %d, dir %d, chan %d, trig %d\n",
op, direction, channel_idx, trigger_idx);
- /* ensure indexes in range */
- if ((channel_idx >= config->nr_ctm_channels) ||
(trigger_idx >= config->nr_trig_max))
return -EINVAL;
- trig_bitmask = 0x1 << trigger_idx;
- /* ensure registered triggers and not out filtered */
- if (direction == CTI_TRIG_IN) {
if (!(trig_bitmask & config->trig_in_use))
return -EINVAL;
- } else {
if (!(trig_bitmask & config->trig_out_use))
return -EINVAL;
if ((config->trig_filter_enable) &&
(config->trig_out_filter & trig_bitmask))
return -EINVAL;
- }
- /* update the local register values */
- chan_bitmask = 0x1 << channel_idx;
- reg_offset = (direction == CTI_TRIG_IN ? CTIINEN(trigger_idx) :
CTIOUTEN(trigger_idx));
- spin_lock(&drvdata->spinlock);
- /* read - modify write - the trigger / channel enable value */
- reg_value = (direction == CTI_TRIG_IN ? config->ctiinen[trigger_idx] :
config->ctiouten[trigger_idx]);
- reg_value = (op == CTI_CHAN_ATTACH) ? reg_value | chan_bitmask :
reg_value & (~chan_bitmask);
- /* write local copy */
- if (direction == CTI_TRIG_IN)
config->ctiinen[trigger_idx] = reg_value;
- else
config->ctiouten[trigger_idx] = reg_value;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
- spin_unlock(&drvdata->spinlock);
- return 0;
+}
+static int cti_channel_gate_op(struct device *dev,
enum cti_chan_gate_op op,
u32 channel_idx)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- u32 chan_bitmask;
- u32 reg_value;
- int err = 0;
- if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
- chan_bitmask = 0x1 << channel_idx;
- spin_lock(&drvdata->spinlock);
- reg_value = config->ctigate;
- switch (op) {
- case CTI_GATE_CHAN_ENABLE:
reg_value |= chan_bitmask;
break;
- case CTI_GATE_CHAN_DISABLE:
reg_value &= ~chan_bitmask;
break;
- case CTI_GATE_CHAN_ENABLE_ALL:
reg_value = (0x1 << config->nr_ctm_channels) - 1;
break;
- case CTI_GATE_CHAN_DISABLE_ALL:
reg_value = 0x0;
break;
- default:
err = -EINVAL;
break;
- }
- if (err == 0) {
config->ctigate = reg_value;
if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIGATE, reg_value);
- }
- spin_unlock(&drvdata->spinlock);
- return err;
+}
+static int cti_channel_setop(struct device *dev,
enum cti_chan_set_op op,
u32 channel_idx)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- u32 chan_bitmask;
- u32 reg_value;
- u32 reg_offset;
- int err = 0;
- if (channel_idx >= config->nr_ctm_channels)
return -EINVAL;
- chan_bitmask = 0x1 << channel_idx;
- spin_lock(&drvdata->spinlock);
- reg_value = config->ctiappset;
- switch (op) {
- case CTI_CHAN_SET:
config->ctiappset |= chan_bitmask;
reg_value = config->ctiappset;
reg_offset = CTIAPPSET;
break;
- case CTI_CHAN_CLR:
config->ctiappset &= ~chan_bitmask;
reg_value = chan_bitmask;
reg_offset = CTIAPPCLEAR;
break;
- case CTI_CHAN_PULSE:
config->ctiappset &= ~chan_bitmask;
reg_value = chan_bitmask;
reg_offset = CTIAPPPULSE;
break;
- default:
err = -EINVAL;
break;
- }
- if ((err == 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
- spin_unlock(&drvdata->spinlock);
- return err;
+}
+/** cti ect operations **/ +static int cti_enable(struct coresight_device *csdev, void *__unused) +{
- int rc;
- struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
- atomic_inc(&drvdata->config.enable_req_count);
- rc = cti_enable_hw(drvdata);
- if (rc)
atomic_dec(&drvdata->config.enable_req_count);
- return rc;
+}
+static int cti_disable(struct coresight_device *csdev, void *__unused) +{
- int rc = 0;
- struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);
- if (!atomic_dec_return(&drvdata->config.enable_req_count)) {
rc = cti_disable_hw(drvdata);
I suggest you do reference counting from within cti_enable/disable_hw() when drvdata->spinlock is taken. For instance with the above, someone could be calling cti_enable() between the time refcount has decremented to '0' and cti_disable_hw() is called.
I also noticed the claimtag API isn't used. As such we can't tell or protect ourselves from a competing agent.
if (rc)
atomic_inc(&drvdata->config.enable_req_count);
- }
- return rc;
+}
+const struct coresight_ops_ect cti_ops_ect = {
- .enable = cti_enable,
- .disable = cti_disable,
+};
+const struct coresight_ops cti_ops = {
- .ect_ops = &cti_ops_ect,
+};
the probe() function along with the operation can be added in a patch. Everything else should be an add-on (by topic) from that stem.
+/** attributes **/
+#define coresight_cti_reg(name, offset) \
- coresight_simple_reg32(struct cti_drvdata, name, offset)
+/* show a simple 32 bit value. if pval is NULL then live read,
- otherwise read from supplied pointer only
- */
+static ssize_t cti_reg32_show(struct device *dev, char *buf,
u32 *pval, int reg_offset)
+{
- unsigned long val = 0;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- if (pval) {
val = (unsigned long)*pval;
- } else if ((reg_offset >= 0) && CTI_PWR_ENA(config)) {
CS_UNLOCK(drvdata->base);
val = readl_relaxed(drvdata->base + reg_offset);
CS_LOCK(drvdata->base);
- }
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+}
+/* store a simple 32 bit value.
- if pval not NULL, then copy to here too,
- if reg_offset >= 0 then write through if enabled.
- */
+static ssize_t cti_reg32_store(struct device *dev, const char *buf,
size_t size, u32 *pval,
int reg_offset)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- /* local store */
- if (pval)
*pval = (u32)val;
- /* write through of offset and enabled */
- if ((reg_offset >= 0) && CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, reg_offset, val);
- spin_unlock(&drvdata->spinlock);
- return size;
+}
+/* basic attributes */ +static ssize_t enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- int enable_req;
- bool enabled, powered, cpuid;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- ssize_t size = 0;
- enable_req = atomic_read(&drvdata->config.enable_req_count);
- spin_lock(&drvdata->spinlock);
- powered = drvdata->config.hw_powered;
- enabled = drvdata->config.hw_enabled;
- cpuid = drvdata->ctidev.cpu;
- spin_unlock(&drvdata->spinlock);
- if (powered) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n",
enabled ? "enabled" : "disabled", cpuid);
- } else if (cpuid >= 0) {
size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n",
enable_req ? "enable req" : "disable req", cpuid);
- } else {
size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n",
enabled ? "enabled" : "disabled");
- }
- return size;
+}
+static ssize_t enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int ret = 0;
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- ret = kstrtoul(buf, 16, &val);
- if (ret)
return ret;
- if (val)
ret = cti_enable(drvdata->csdev, NULL);
- else
ret = cti_disable(drvdata->csdev, NULL);
- if (ret)
return ret;
- return size;
+} +static DEVICE_ATTR_RW(enable);
+static ssize_t ctmid_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return scnprintf(buf, PAGE_SIZE, "0x%x\n", drvdata->ctidev.ctm_id);
+} +static DEVICE_ATTR_RO(ctmid);
+#define LF_ADJUST_BUF_PTRS(n) \
- do { buf_pos += n; buf_size -= n; \
- if (buf_size <= 1) \
goto lf_buf_full; \
- } while (0)
+static ssize_t list_features_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- int used = 0, sig_idx, con_idx, nr_trig_max;
- /* buffer vars */
- int buf_size = PAGE_SIZE;
- char *buf_pos = buf;
- char *con_name;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *cfg = &drvdata->config;
- struct cti_device *ctidev = &drvdata->ctidev;
- u32 sig_mask;
- struct cti_trig_con *con;
- /* basic feature info */
- used = scnprintf(buf_pos, buf_size,
"CTI:%s; Channels:%d; Max Trigs:%d\n",
dev_name(dev), cfg->nr_ctm_channels, cfg->nr_trig_max);
- LF_ADJUST_BUF_PTRS(used);
- used = scnprintf(buf_pos, buf_size, "CTM id = %d; Num Conns = %d\n",
ctidev->ctm_id, ctidev->nr_trig_con);
- LF_ADJUST_BUF_PTRS(used);
- /* list each connection info */
- con_idx = 0;
- nr_trig_max = cfg->nr_trig_max;
- list_for_each_entry(con, &ctidev->trig_cons, node) {
/* connection name */
if (con->con_dev_name != NULL)
con_name = con->con_dev_name;
else
con_name = "unknown";
used = scnprintf(buf_pos, buf_size, "conn(%d:%s)\n",
con_idx, con_name);
LF_ADJUST_BUF_PTRS(used);
con_idx++;
/* connection signals */
used = scnprintf(buf_pos, buf_size, "Trig IN(%d) [ ",
con->con_in->nr_sigs);
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & con->con_in->used_mask) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
used = scnprintf(buf_pos, buf_size, "Trig OUT(%d) [ ",
con->con_out->nr_sigs);
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & con->con_out->used_mask) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
- }
- if (cfg->trig_out_filter) {
used = scnprintf(buf_pos, buf_size, "Trig OUT_FILTERED [ ");
LF_ADJUST_BUF_PTRS(used);
sig_mask = 0x1;
for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {
if (sig_mask & cfg->trig_out_filter) {
used = scnprintf(buf_pos, buf_size, "%d ",
sig_idx);
LF_ADJUST_BUF_PTRS(used);
}
sig_mask <<= 1;
}
used = scnprintf(buf_pos, buf_size, "]\n");
LF_ADJUST_BUF_PTRS(used);
- }
+lf_buf_full:
- used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
- return used;
+} +static DEVICE_ATTR_RO(list_features);
+static struct attribute *coresight_cti_attrs[] = {
- &dev_attr_enable.attr,
- &dev_attr_list_features.attr,
- &dev_attr_ctmid.attr,
- NULL,
+};
+/* raw register attributes */ +static ssize_t inout_sel_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- u32 val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- val = (u32)drvdata->config.ctiinout_sel;
- return scnprintf(buf, PAGE_SIZE, "%#x\n", val);
+}
+static ssize_t inout_sel_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- if (val > (CTIINOUTEN_MAX-1))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- drvdata->config.ctiinout_sel = val;
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(inout_sel);
+static ssize_t inen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- spin_lock(&drvdata->spinlock);
- index = drvdata->config.ctiinout_sel;
- val = drvdata->config.ctiinen[index];
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "INEN%d %#lx\n", index, val);
+}
+static ssize_t inen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- index = config->ctiinout_sel;
- config->ctiinen[index] = val;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIINEN(index), val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(inen);
+static ssize_t outen_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- spin_lock(&drvdata->spinlock);
- index = drvdata->config.ctiinout_sel;
- val = drvdata->config.ctiouten[index];
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "OUTEN%d %#lx\n", index, val);
+}
+static ssize_t outen_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- int index;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- index = config->ctiinout_sel;
- config->ctiouten[index] = val;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIOUTEN(index), val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(outen);
+static ssize_t gate_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_show(dev, buf, &drvdata->config.ctigate, -1);
+}
+static ssize_t gate_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_store(dev, buf, size,
&drvdata->config.ctigate, CTIGATE);
+} +static DEVICE_ATTR_RW(gate);
+static ssize_t asicctl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_show(dev, buf, &drvdata->config.asicctl, -1);
+}
+static ssize_t asicctl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_store(dev, buf, size,
&drvdata->config.asicctl, ASICCTL);
+} +static DEVICE_ATTR_RW(asicctl);
+static ssize_t intack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- cti_write_intack(dev, val);
- return size;
+} +static DEVICE_ATTR_WO(intack);
+static ssize_t appset_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_show(dev, buf, &drvdata->config.ctiappset, -1);
+}
+static ssize_t appset_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- return cti_reg32_store(dev, buf, size,
&drvdata->config.ctiappset, CTIAPPSET);
+} +static DEVICE_ATTR_RW(appset);
+static ssize_t appclear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val, mask;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- /* a 1'b1 in appclr clears down the same bit in appset*/
- mask = ~val;
- config->ctiappset &= mask;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPCLEAR, val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_WO(appclear);
+static ssize_t apppulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val, mask;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- /* a 1'b1 in apppulse sets then clears the bit,
* effectively clears down the same bit in appset
*/
- mask = ~val;
- config->ctiappset &= mask;
- /* write through if enabled */
- if (CTI_PWR_ENA(config))
cti_write_single_reg(drvdata, CTIAPPPULSE, val);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_WO(apppulse);
+static ssize_t itchout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return cti_reg32_show(dev, buf, NULL, ITCHOUT);
+}
+static ssize_t itchout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITCHOUT);
+} +static DEVICE_ATTR_RW(itchout);
+static ssize_t ittrigout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return cti_reg32_show(dev, buf, NULL, ITTRIGOUT);
+}
+static ssize_t ittrigout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITTRIGOUT);
+} +static DEVICE_ATTR_RW(ittrigout);
+static ssize_t itchinack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITCHINACK);
+} +static DEVICE_ATTR_WO(itchinack);
+static ssize_t ittriginack_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, ITTRIGINACK);
+} +static DEVICE_ATTR_WO(ittriginack);
+static ssize_t itctrl_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- return cti_reg32_show(dev, buf, NULL, CORESIGHT_ITCTRL);
+}
+static ssize_t itctrl_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_reg32_store(dev, buf, size, NULL, CORESIGHT_ITCTRL);
+} +static DEVICE_ATTR_RW(itctrl);
+coresight_cti_reg(triginstatus, CTITRIGINSTATUS); +coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS); +coresight_cti_reg(chinstatus, CTICHINSTATUS); +coresight_cti_reg(choutstatus, CTICHOUTSTATUS); +coresight_cti_reg(ittrigin, ITTRIGIN); +coresight_cti_reg(itchin, ITCHIN); +coresight_cti_reg(itchoutack, ITCHOUTACK); +coresight_cti_reg(ittrigoutack, ITTRIGOUTACK);
+static struct attribute *coresight_cti_regs_attrs[] = {
- &dev_attr_inout_sel.attr,
- &dev_attr_inen.attr,
- &dev_attr_outen.attr,
- &dev_attr_gate.attr,
- &dev_attr_asicctl.attr,
- &dev_attr_intack.attr,
- &dev_attr_appset.attr,
- &dev_attr_appclear.attr,
- &dev_attr_apppulse.attr,
- &dev_attr_triginstatus.attr,
- &dev_attr_trigoutstatus.attr,
- &dev_attr_chinstatus.attr,
- &dev_attr_choutstatus.attr,
- &dev_attr_itctrl.attr,
- &dev_attr_ittrigin.attr,
- &dev_attr_itchin.attr,
- &dev_attr_ittrigout.attr,
- &dev_attr_itchout.attr,
- &dev_attr_itchoutack.attr,
- &dev_attr_ittrigoutack.attr,
- &dev_attr_ittriginack.attr,
- &dev_attr_itchinack.attr,
- NULL,
+};
+/* channel / trigger api */ +static int +cti_trig_op_parse(struct device *dev, enum cti_chan_op op,
enum cti_trig_dir dir, const char *buf, size_t size)
+{
- u32 chan_idx;
- u32 trig_idx;
- int items, err = size;
- /* extract chan idx and trigger idx */
- items = sscanf(buf, "%d %d", &chan_idx, &trig_idx);
- if (items) {
err = cti_channel_trig_op(dev, op, dir, chan_idx, trig_idx);
if (!err)
err = size;
- } else
err = -EINVAL;
- return err;
+}
+static ssize_t trigin_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_attach);
+static ssize_t trigin_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_IN,
buf, size);
+} +static DEVICE_ATTR_WO(trigin_detach);
+static ssize_t trigout_attach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_attach);
+static ssize_t trigout_detach_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_OUT,
buf, size);
+} +static DEVICE_ATTR_WO(trigout_detach);
+static ssize_t gate_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = 0, channel = 0, items;
- if (strcmp(buf, "all") == 0) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE_ALL, 0);
- } else {
items = sscanf(buf, "%d", &channel);
if (!items)
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE, 0);
- }
- return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_enable);
+static ssize_t gate_disable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = 0, channel = 0, items;
- if (strcmp(buf, "all") == 0) {
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE_ALL, 0);
- } else {
items = sscanf(buf, "%d", &channel);
if (!items)
return -EINVAL;
err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE, 0);
- }
- return err ? err : size;
+} +static DEVICE_ATTR_WO(gate_disable);
+static int +chan_op_parse(struct device *dev, enum cti_chan_set_op op, const char *buf) +{
- int err = 0, channel = 0, items;
- items = sscanf(buf, "%d", &channel);
- if (!items)
return -EINVAL;
- err = cti_channel_setop(dev, op, channel);
- return err;
+}
+static ssize_t chan_set_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = chan_op_parse(dev, CTI_CHAN_SET, buf);
- return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_set);
+static ssize_t chan_clear_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = chan_op_parse(dev, CTI_CHAN_CLR, buf);
- return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_clear);
+static ssize_t chan_pulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int err = chan_op_parse(dev, CTI_CHAN_PULSE, buf);
- return err ? err : size;
+} +static DEVICE_ATTR_WO(chan_pulse);
+static ssize_t trig_filter_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- spin_lock(&drvdata->spinlock);
- val = drvdata->config.trig_filter_enable;
- spin_unlock(&drvdata->spinlock);
- return scnprintf(buf, PAGE_SIZE, "%ld (%s)\n", val,
val ? "enabled" : "disabled");
+}
+static ssize_t trig_filter_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- unsigned long val;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- if (kstrtoul(buf, 16, &val))
return -EINVAL;
- spin_lock(&drvdata->spinlock);
- drvdata->config.trig_filter_enable = val ? 1 : 0;
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_RW(trig_filter_enable);
+/* clear all xtrigger / channel programming */ +static ssize_t reset_xtrigs_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
+{
- int i;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- spin_lock(&drvdata->spinlock);
- /* clear the CTI trigger / channel programming registers */
- for (i = 0; i < config->nr_trig_max; i++) {
config->ctiinen[i] = 0;
config->ctiouten[i] = 0;
- }
- /* clear the other regs */
- config->ctigate = (0x1 << config->nr_ctm_channels) - 1;
- config->asicctl = 0;
- config->ctiappset = 0;
- config->ctiinout_sel = 0;
- /* if enabled then write through */
- if (CTI_PWR_ENA(config))
cti_write_all_hw_regs(drvdata);
- spin_unlock(&drvdata->spinlock);
- return size;
+} +static DEVICE_ATTR_WO(reset_xtrigs);
+/* list attachments by channel */ +#define LX_ADJUST_BUF_PTRS(n) \
- do { buf_pos += n; buf_size -= n; \
- if (buf_size <= 1) \
goto lx_buf_full; \
- } while (0)
+static ssize_t list_xtrigs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- int used = 0, nr_channels, chan_idx, reg_idx;
- /* buffer vars */
- int buf_size = PAGE_SIZE;
- char *buf_pos = buf;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *cfg = &drvdata->config;
- u32 chan_mask;
- nr_channels = cfg->nr_ctm_channels;
- used = scnprintf(buf_pos, buf_size, "CTI:%s; Channels:%d\n",
dev_name(dev), nr_channels);
- LX_ADJUST_BUF_PTRS(used);
- /* show the IN and OUT triggers per channel */
- for (chan_idx = 0; chan_idx < nr_channels; chan_idx++) {
chan_mask = 0x1 << chan_idx;
used = scnprintf(buf_pos, buf_size, "Chan %d: IN [ ", chan_idx);
LX_ADJUST_BUF_PTRS(used);
for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiinen[reg_idx]) {
used = scnprintf(buf_pos, buf_size, "%d ",
reg_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]; OUT [ ");
LX_ADJUST_BUF_PTRS(used);
for (reg_idx = 0;
reg_idx < drvdata->config.nr_trig_max;
reg_idx++) {
if (chan_mask & cfg->ctiouten[reg_idx]) {
used = scnprintf(buf_pos, buf_size, "%d ",
reg_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "];\n");
LX_ADJUST_BUF_PTRS(used);
- }
- /* show the channels enabled via ctigate */
- used = scnprintf(buf_pos, buf_size, "Gate Channels Enabled: [ ");
- LX_ADJUST_BUF_PTRS(used);
- if (cfg->ctigate == 0) {
used = scnprintf(buf_pos, buf_size, "None ]\n");
LX_ADJUST_BUF_PTRS(used);
- } else {
for (chan_idx = 0; chan_idx < nr_channels; chan_idx++) {
chan_mask = 0x1 << chan_idx;
if (chan_mask & cfg->ctigate) {
used = scnprintf(buf_pos, buf_size, "%d ",
chan_idx);
LX_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LX_ADJUST_BUF_PTRS(used);
- }
+lx_buf_full:
- used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
- return used;
+} +static DEVICE_ATTR_RO(list_xtrigs);
+#define LCIU_ADJUST_BUF_PTRS(n) \
- do { buf_pos += n; buf_size -= n; \
- if (buf_size <= 1) \
goto lciu_buf_full; \
- } while (0)
+static ssize_t list_chan_inuse_show(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- int used = 0, nr_channels, i;
- /* buffer vars */
- int buf_size = PAGE_SIZE;
- char *buf_pos = buf;
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct cti_config *config = &drvdata->config;
- u32 inuse_bits = 0, chan_mask, chan_bit_mask;
- nr_channels = config->nr_ctm_channels;
- spin_lock(&drvdata->spinlock);
- for (i = 0; i < config->nr_trig_max; i++) {
inuse_bits |= config->ctiinen[i];
inuse_bits |= config->ctiouten[i];
- }
- spin_unlock(&drvdata->spinlock);
- /* print in use channels */
- chan_mask = ((u32)(0x1 << nr_channels)) - 1;
- if (inuse_bits & chan_mask) {
used = scnprintf(buf_pos, buf_size, "Chan in use [ ");
LCIU_ADJUST_BUF_PTRS(used);
for (i = 0; i < nr_channels; i++) {
chan_bit_mask = 0x1 << i;
if (chan_bit_mask & inuse_bits) {
used = scnprintf(buf_pos, buf_size, "%d ", i);
LCIU_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LCIU_ADJUST_BUF_PTRS(used);
- } else {
used = scnprintf(buf_pos, buf_size, "Chan in use [ none ]\n");
LCIU_ADJUST_BUF_PTRS(used);
- }
- /* print free channels */
- inuse_bits = ~inuse_bits; /* flip for free channels */
- if (inuse_bits & chan_mask) {
used = scnprintf(buf_pos, buf_size, "Chan free [ ");
LCIU_ADJUST_BUF_PTRS(used);
for (i = 0; i < nr_channels; i++) {
chan_bit_mask = 0x1 << i;
if (chan_bit_mask & inuse_bits) {
used = scnprintf(buf_pos, buf_size, "%d ", i);
LCIU_ADJUST_BUF_PTRS(used);
}
}
used = scnprintf(buf_pos, buf_size, "]\n");
LCIU_ADJUST_BUF_PTRS(used);
- } else {
used = scnprintf(buf_pos, buf_size, "Chan free [ none ]\n");
LCIU_ADJUST_BUF_PTRS(used);
- }
+lciu_buf_full:
- used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;
- return used;
+} +static DEVICE_ATTR_RO(list_chan_inuse);
+static struct attribute *coresight_cti_channel_attrs[] = {
- &dev_attr_trigin_attach.attr,
- &dev_attr_trigin_detach.attr,
- &dev_attr_trigout_attach.attr,
- &dev_attr_trigout_detach.attr,
- &dev_attr_gate_enable.attr,
- &dev_attr_gate_disable.attr,
- &dev_attr_chan_set.attr,
- &dev_attr_chan_clear.attr,
- &dev_attr_chan_pulse.attr,
- &dev_attr_trig_filter_enable.attr,
- &dev_attr_list_xtrigs.attr,
- &dev_attr_reset_xtrigs.attr,
- &dev_attr_list_chan_inuse.attr,
- NULL,
+};
+/* coresight management registers */ +coresight_cti_reg(devaff0, CTIDEVAFF0); +coresight_cti_reg(devaff1, CTIDEVAFF1); +coresight_cti_reg(authstatus, CORESIGHT_AUTHSTATUS); +coresight_cti_reg(devarch, CORESIGHT_DEVARCH); +coresight_cti_reg(devid, CORESIGHT_DEVID); +coresight_cti_reg(devtype, CORESIGHT_DEVTYPE); +coresight_cti_reg(pidr0, CORESIGHT_PERIPHIDR0); +coresight_cti_reg(pidr1, CORESIGHT_PERIPHIDR1); +coresight_cti_reg(pidr2, CORESIGHT_PERIPHIDR2); +coresight_cti_reg(pidr3, CORESIGHT_PERIPHIDR3); +coresight_cti_reg(pidr4, CORESIGHT_PERIPHIDR4);
+static struct attribute *coresight_cti_mgmt_attrs[] = {
- &dev_attr_devaff0.attr,
- &dev_attr_devaff1.attr,
- &dev_attr_authstatus.attr,
- &dev_attr_devarch.attr,
- &dev_attr_devid.attr,
- &dev_attr_devtype.attr,
- &dev_attr_pidr0.attr,
- &dev_attr_pidr1.attr,
- &dev_attr_pidr2.attr,
- &dev_attr_pidr3.attr,
- &dev_attr_pidr4.attr,
- NULL,
+};
+static const struct attribute_group coresight_cti_group = {
- .attrs = coresight_cti_attrs,
+};
+static const struct attribute_group coresight_cti_regs_group = {
- .attrs = coresight_cti_regs_attrs,
- .name = "regs",
+};
+static const struct attribute_group coresight_cti_channels_group = {
- .attrs = coresight_cti_channel_attrs,
- .name = "channels",
+};
+static const struct attribute_group coresight_cti_mgmt_group = {
- .attrs = coresight_cti_mgmt_attrs,
- .name = "mgmt",
+};
+const struct attribute_group *coresight_cti_groups[] = {
- &coresight_cti_group,
- &coresight_cti_regs_group,
- &coresight_cti_channels_group,
- &coresight_cti_mgmt_group,
- NULL,
+};
As you pointed out in the cover letter all the sysfs entries should be forked out to their own file, the same way we did for the ETM drivers. When doing so cti_group and cti_mgmt_group should be part of the same patch (unless you see a reason to split them). After that cti_regs_group and cti_channels_group can come in their own patch.
As I indicated we will need to do something about the multiline output in sysfs. I don't know what yet but I hope it will come as this patchset matures. That is also why I think it should be in its own patch - that way we can fix just that when we figure out what we want to do. For now I suggest to keep the current implementation. In this pass I will not be reviewing sysfs related code - there will be time for that later.
This is all the comments I have for now but more will come with upcoming version. As a whole I think you were successfull at formalizing a HW concept that doesn't currently have an equal in the kernel - we just need to make it shine a little.
Mathieu
+static int cti_starting_cpu(unsigned int cpu) +{
- struct cti_drvdata *drvdata = cti_cpu_drv[cpu];
- if (!drvdata)
return 0;
- spin_lock(&drvdata->spinlock);
- drvdata->config.hw_powered = true;
- spin_unlock(&drvdata->spinlock);
- if (atomic_read(&drvdata->config.enable_req_count))
cti_enable_hw(drvdata);
- return 0;
+}
+static int cti_stopping_cpu(unsigned int cpu) +{
- struct cti_drvdata *drvdata = cti_cpu_drv[cpu];
- if (!drvdata)
return 0;
- cti_disable_hw(drvdata);
- spin_lock(&drvdata->spinlock);
- drvdata->config.hw_powered = false;
- spin_unlock(&drvdata->spinlock);
- return 0;
+}
+static void cti_smp_set_powered(void *info) +{
- struct cti_drvdata *drvdata = info;
- drvdata->config.hw_powered = true;
+}
+/* setup power handling on a cti that has an association with a cpu */ +static int cti_setup_hp_cpu_affinity(struct cti_drvdata *drvdata) +{
- int ret = 0;
- /* CPU affinity - add to quick lookup array
* and register cpu hp callbacks first time out
*/
- cpus_read_lock();
- cti_cpu_drv[drvdata->ctidev.cpu] = drvdata;
- if (!cti_cpu_count) {
cti_hp_online = cpuhp_setup_state_nocalls_cpuslocked(
CPUHP_AP_ONLINE_DYN, "arm/coresightcti:online",
cti_starting_cpu, cti_stopping_cpu);
if (cti_hp_online < 0) {
ret = cti_hp_online;
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
goto cti_hp_cpu_done;
}
- }
- cti_cpu_count++;
- /* setup powered flag by running function of assoc cpu */
- if (smp_call_function_single(drvdata->ctidev.cpu, cti_smp_set_powered,
drvdata, 1)) {
drvdata->config.hw_powered = false;
- }
+cti_hp_cpu_done:
- cpus_read_unlock();
- return ret;
+}
+/* back out hotplug notifications on error */ +static void cti_clear_hp_cpu_affinity(struct cti_drvdata *drvdata) +{
- if (!drvdata)
return;
- if (drvdata->ctidev.cpu >= 0) {
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
cti_cpu_count--;
if ((cti_cpu_count == 0) && (cti_hp_online > 0))
cpuhp_remove_state_nocalls(cti_hp_online);
- }
+}
+static int cti_probe(struct amba_device *adev, const struct amba_id *id) +{
- int ret = 0;
- u32 devid;
- void __iomem *base;
- struct device *dev = &adev->dev;
- struct cti_drvdata *drvdata = 0;
- struct coresight_desc cti_desc;
- struct coresight_platform_data *pdata = NULL;
- struct resource *res = &adev->res;
- struct device_node *np = adev->dev.of_node;
- struct ect_node *ect_nd = 0;
- /* boilerplate code to set up the basics */
- dev_info(dev, "%s(ID = %x)\n", __func__, id->id);
- if (np) {
pdata = of_get_coresight_ect_platform_data(dev, np);
if (IS_ERR(pdata)) {
dev_info(dev, "of_get_coresight_ect_platform err\n");
ret = PTR_ERR(pdata);
goto err_out;
}
dev->platform_data = pdata;
- }
- /* node to keep track of CTI net */
- ect_nd = kzalloc(sizeof(struct ect_node), GFP_KERNEL);
- if (!ect_nd) {
ret = -ENOMEM;
goto err_out;
- }
- /* driver data*/
- drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
- if (!drvdata) {
ret = -ENOMEM;
dev_info(dev, "%s, mem err\n", __func__);
goto err_out;
- }
- /* links between dev and drvdata*/
- drvdata->dev = dev;
- dev_set_drvdata(dev, drvdata);
- /* default CTI device info */
- drvdata->ctidev.cpu = -1;
- drvdata->ctidev.nr_trig_con = 0;
- drvdata->ctidev.ctm_id = 0;
- INIT_LIST_HEAD(&drvdata->ctidev.trig_cons);
- /* Validity for the resource is already checked by the AMBA core */
- base = devm_ioremap_resource(dev, res);
- if (IS_ERR(base)) {
ret = PTR_ERR(base);
dev_info(dev, "%s, remap err\n", __func__);
goto err_out;
- }
- drvdata->base = base;
- spin_lock_init(&drvdata->spinlock);
- /* look at the HW DEVID register for some of the HW settings */
- devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
- drvdata->config.nr_trig_max = (int)((devid & 0xFF00) >> 8);
- /* no current hardware should exceed this, but protect the driver
* in case of fault / out of spec hw
*/
- if (drvdata->config.nr_trig_max > CTIINOUTEN_MAX) {
dev_warn_once(dev,
"Limiting HW MaxTrig value(%d) to driver max(%d)\n",
drvdata->config.nr_trig_max, CTIINOUTEN_MAX);
drvdata->config.nr_trig_max = CTIINOUTEN_MAX;
- }
- drvdata->config.nr_ctm_channels = (int)((devid & 0xF0000) >> 16);
- dev_info(dev, "DevID %08x, trigs=%d, chans=%d\n", devid,
drvdata->config.nr_trig_max, drvdata->config.nr_ctm_channels);
- /** additional parse the .dts for connections and signals */
- of_cti_get_hw_data(dev, np, drvdata);
- cti_set_default_config(&drvdata->config);
- /* setup cpu related CTI devices, otherwise assume powered */
- if (drvdata->ctidev.cpu >= 0) {
ret = cti_setup_hp_cpu_affinity(drvdata);
if (ret < 0)
goto err_out;
- } else
drvdata->config.hw_powered = true;
- /* set up coresight component description */
- cti_desc.pdata = pdata;
- cti_desc.type = CORESIGHT_DEV_TYPE_ECT;
- cti_desc.subtype.ect_subtype = CORESIGHT_DEV_SUBTYPE_ECT_CTI;
- cti_desc.ops = &cti_ops;
- cti_desc.groups = coresight_cti_groups;
- cti_desc.dev = dev;
- drvdata->csdev = coresight_register(&cti_desc);
- if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
pr_err("%s: CS register failed\n", pdata->name);
goto err_out;
- }
- /* add to list of CTI devices */
- mutex_lock(&ect_mutex);
- ect_nd->cti_drv = drvdata;
- list_add(&ect_nd->next, &ect_net);
- mutex_unlock(&ect_mutex);
- /* set any cross references */
- cti_update_conn_xrefs(drvdata);
- /* all done - dec pm refcount */
- pm_runtime_put(&adev->dev);
- dev_info(dev, "%s: initialized\n", pdata->name);
- cti_count++;
- dev_info(dev, "%s - ok\n", __func__);
- return 0;
+err_out:
- cti_clear_hp_cpu_affinity(drvdata);
- dev_info(dev, "%s - err_out\n", __func__);
- return ret;
+}
+/* free up CTI specific resources
- called from coresight_device_release on coresight_unregister.
- */
+void cti_device_release(struct device *dev) +{
- struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
- struct ect_node *ect_item, *ect_tmp;
- struct cti_trig_con *tc, *tc_tmp;
- /* free up resources associated with the cti connections */
- if (drvdata->ctidev.cpu >= 0)
cti_cpu_drv[drvdata->ctidev.cpu] = 0;
- list_for_each_entry_safe(tc, tc_tmp,
&drvdata->ctidev.trig_cons, node) {
kfree(tc->con_in);
kfree(tc->con_out);
kfree(tc->con_dev_name);
list_del(&tc->node);
kfree(tc);
- }
- /* remove from the list */
- mutex_lock(&ect_mutex);
- list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, next) {
if (ect_item->cti_drv == drvdata) {
list_del(&ect_item->next);
kfree(ect_item);
goto ect_list_item_removed;
}
- }
+ect_list_item_removed:
- mutex_unlock(&ect_mutex);
- kfree(drvdata);
+} +EXPORT_SYMBOL_GPL(cti_device_release);
+#define CTI_AMBA_ID(pid) \
- { \
.id = pid, \
.mask = 0x000fffff, \
- }
+static struct amba_cs_uci_id uci_id_cti[] = {
- {
/* CTI UCI data */
.devarch = 0x47701a14, /* devarch value for CTI v2 */
.devarch_mask = 0xfff0ffff,
.devtype = 0x00000014, /* maj(0x4-debug) min(0x1-ECT) */
- }
+};
+#define CTI_AMBA_UCI_ID(pid) \
- { \
.id = pid, \
.mask = 0x000fffff, \
.data = uci_id_cti, \
- }
+static const struct amba_id cti_ids[] = {
- CTI_AMBA_ID(0x000bb906), /* Coresight CTI (SoC 400), C-A72, C-A57 */
- CTI_AMBA_ID(0x000bb922), /* CTI - C-A8 */
- CTI_AMBA_ID(0x000bb9a8), /* CTI - C-A53 */
- CTI_AMBA_ID(0x000bb9aa), /* CTI - C-A73 */
- CTI_AMBA_UCI_ID(0x000bb9da), /* CTI - C-A35 */
- CTI_AMBA_UCI_ID(0x000bb9ed), /* Coresight CTI (SoC 600) */
- CTI_AMBA_UCI_ID(0x000bb95d), /* test UCI - ETM PID A53 */
- { 0, 0},
+};
+static struct amba_driver cti_driver = {
- .drv = {
.name = "coresight-cti",
.owner = THIS_MODULE,
.suppress_bind_attrs = true,
- },
- .probe = cti_probe,
- .id_table = cti_ids,
+}; +builtin_amba_driver(cti_driver); diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h new file mode 100644 index 000000000000..09cb985ac0ea --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#ifndef _CORESIGHT_CORESIGHT_CTI_H +#define _CORESIGHT_CORESIGHT_CTI_H
+#include <asm/local.h> +#include <linux/spinlock.h> +#include "coresight-priv.h"
+/*
- Device registers
- 0x000 - 0x144: CTI programming and status
- 0xEDC - 0xEF8: CTI integration test.
- 0xF00 - 0xFFC: Coresight management registers.
- */
+/* CTI programming registers */ +#define CTICONTROL 0x000 +#define CTIINTACK 0x010 +#define CTIAPPSET 0x014 +#define CTIAPPCLEAR 0x018 +#define CTIAPPPULSE 0x01C +#define CTIINEN(n) (0x020 + (4 * n)) +#define CTIOUTEN(n) (0x0A0 + (4 * n)) +#define CTITRIGINSTATUS 0x130 +#define CTITRIGOUTSTATUS 0x134 +#define CTICHINSTATUS 0x138 +#define CTICHOUTSTATUS 0x13C +#define CTIGATE 0x140 +#define ASICCTL 0x144 +/* Integration test registers */ +#define ITCHINACK 0xEDC /* WO CTI CSSoc 400 only*/ +#define ITTRIGINACK 0xEE0 /* WO CTI CSSoc 400 only*/ +#define ITCHOUT 0xEE4 /* WO RW-600 */ +#define ITTRIGOUT 0xEE8 /* WO RW-600 */ +#define ITCHOUTACK 0xEEC /* RO CTI CSSoc 400 only*/ +#define ITTRIGOUTACK 0xEF0 /* RO CTI CSSoc 400 only*/ +#define ITCHIN 0xEF4 /* RO */ +#define ITTRIGIN 0xEF8 /* RO */ +/* management registers */ +#define CTIDEVAFF0 0xFA8 +#define CTIDEVAFF1 0xFAC
+/* CTI CSSoc 600 has a max of 32 trigger signals per direction.
- CTI CSSoc 400 has 8 IO triggers - other CTIs can be impl def.
- Max of in and out defined in the DEVID register.
- pick up actual number used from .dts parameters if present.
- */
+#define CTIINOUTEN_MAX 32
+/**
- Group of related trigger signals
- @nr_sigs: number of signals in the group.
- @used_mask: bitmask representing the signal indexes in the group.
- @sig_names: list of names for the signals.
- */
+struct cti_trig_grp {
- int nr_sigs;
- u32 used_mask;
- char *sig_names;
+};
+/**
- Trigger connection - connection between a CTI and other (coresight) device
- lists input and output trigger signals for the device
- @con_in: connected CTIIN signals for the device.
- @con_out: connected CTIOUT signals for the device.
- @con_dev: coresight device connected to the CTI, NULL if not CS device
- @con_dev_name: name of connected device (CS or CPU)
- @node: entry node in list of connections.
- */
+struct cti_trig_con {
- struct cti_trig_grp *con_in;
- struct cti_trig_grp *con_out;
- struct coresight_device *con_dev;
- char *con_dev_name;
- struct list_head node;
+};
+/**
- struct cti_device - description of CTI device properties.
- @nt_trig_con: Number of external devices connected to this device.
- @ctm_id: which CTM this device is connected to (by default it is
assumed there is a single CTM per SoC, ID 0).
- @trig_cons: list of connections to this device.
- @cpu: CPU ID if associated with CPU, -1 otherwise.
- */
+struct cti_device {
- int nr_trig_con;
- u32 ctm_id;
- struct list_head trig_cons;
- int cpu;
+};
+/**
- struct cti_config - configuration of the CTI device hardware
- hardware description from RO ID regs
- @nr_trig_max: Max number of trigger signals implemented on device.
(max of trig_in or trig_out)
- @nr_ctm_channels: number of available CTM channels
- cti enable control
- @enable_req_count: CTI is enabled alongside >=1 associated devices.
- @hw_enabled: true if hw is currently enabled.
- @hw_powered: true if associated cpu powered on, or no cpu.
- registered triggers and filtering
- @trig_in_use: bitfield of in triggers registered as in use.
- @trig_out_use: bitfield of out triggers registered as in use.
- @trig_out_filter: bitfield of out triggers that are blocked if filter
enabled. Typically this would be dbgreq / restart on a core CTI.
- @trig_filter_enable: 1 if filtering enabled.
- cti software programmable regs:
- @ctiappset: CTI Software application channel set.
- @ctiinout_sel: register selector for INEN and OUTEN regs.
- @ctiinen: enable input trigger to a channel.
- @ctiouten: enable output trigger from a channel.
- @ctigate: gate channel output from CTI to CTM.
- */
+struct cti_config {
- /* hardware description */
- int nr_ctm_channels;
- int nr_trig_max;
- /* cti enable control */
- atomic_t enable_req_count;
- bool hw_enabled;
- bool hw_powered;
- /* registered triggers and filtering */
- u32 trig_in_use;
- u32 trig_out_use;
- u32 trig_out_filter;
- int trig_filter_enable;
- /* cti cross trig programmable regs */
- u32 ctiappset;
- u8 ctiinout_sel;
- u32 ctiinen[CTIINOUTEN_MAX];
- u32 ctiouten[CTIINOUTEN_MAX];
- u32 ctigate;
- u32 asicctl;
+};
+/**
- struct cti_drvdata - specifics for the CTI device
- @base: Memory mapped base address for this component.
- @dev: The device entity associated to this component.
- @csdev: Standard CoreSight device information.
- @ctidev: Extra information needed by the CTI/CTM framework.
- @spinlock: Control data access to one at a time.
- @config: Configuration data for this CTI device.
- */
+struct cti_drvdata {
- void __iomem *base;
- struct device *dev;
- struct coresight_device *csdev;
- struct cti_device ctidev;
- spinlock_t spinlock;
- struct cti_config config;
+};
+enum cti_chan_op {
- CTI_CHAN_ATTACH,
- CTI_CHAN_DETACH,
+};
+enum cti_trig_dir {
- CTI_TRIG_IN,
- CTI_TRIG_OUT,
+};
+enum cti_chan_gate_op {
- CTI_GATE_CHAN_ENABLE,
- CTI_GATE_CHAN_DISABLE,
- CTI_GATE_CHAN_ENABLE_ALL,
- CTI_GATE_CHAN_DISABLE_ALL,
+};
+enum cti_chan_set_op {
- CTI_CHAN_SET,
- CTI_CHAN_CLR,
- CTI_CHAN_PULSE,
+};
+#endif /* _CORESIGHT_CORESIGHT_CTI_H */
2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Adds CORESIGHT_CTI to coresight configuration options. Adds coresight build to Makefile.
Signed-off-by: Mike Leach mike.leach@linaro.org --- drivers/hwtracing/coresight/Kconfig | 12 ++++++++++++ drivers/hwtracing/coresight/Makefile | 1 + 2 files changed, 13 insertions(+)
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index ad34380cac49..9c4536da95ce 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -114,4 +114,16 @@ config CORESIGHT_CPU_DEBUG properly, please refer Documentation/trace/coresight-cpu-debug.txt for detailed description and the example for usage.
+config CORESIGHT_CTI + bool "CoreSight Cross Trigger Interface (CTI) driver" + depends on ARM || ARM64 + select CORESIGHT_LINKS_AND_SINKS + help + This driver provides support for the CoreSight CTI and CTM components. + These provide hardware triggering events between CoreSight trace + source and sink components to halt trace or inject events into the + trace stream. Also provides a software control to trigger same halt + events - providing fast trace halt compared to disabling sources and + sinks normally in driver software. + endif diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 41870ded51a3..e8c7846533d5 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -19,3 +19,4 @@ obj-$(CONFIG_CORESIGHT_DYNAMIC_REPLICATOR) += coresight-dynamic-replicator.o obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o +obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o
Add in test device tree definitions for qcom DB410 platform, and ARM Juno platform.
Signed-off-by: Mike Leach mike.leach@linaro.org --- arch/arm64/boot/dts/arm/juno-base.dtsi | 76 ++++++++++++++++ arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi | 9 ++ arch/arm64/boot/dts/qcom/msm8916.dtsi | 101 +++++++++++++++++++++- 3 files changed, 183 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/boot/dts/arm/juno-base.dtsi b/arch/arm64/boot/dts/arm/juno-base.dtsi index ed774ee8f659..8c8037cac5bd 100644 --- a/arch/arm64/boot/dts/arm/juno-base.dtsi +++ b/arch/arm64/boot/dts/arm/juno-base.dtsi @@ -132,6 +132,15 @@ }; };
+ cti@20020000 { /* sys_cti_0 */ + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x20020000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + }; + tpiu@20030000 { compatible = "arm,coresight-tpiu", "arm,primecell"; reg = <0 0x20030000 0 0x1000>; @@ -220,6 +229,16 @@ }; };
+ cti@20110000 { /* sys_cti_1 */ + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x20110000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + }; + + cpu_debug0: cpu-debug@22010000 { compatible = "arm,coresight-cpu-debug", "arm,primecell"; reg = <0x0 0x22010000 0x0 0x1000>; @@ -229,6 +248,15 @@ power-domains = <&scpi_devpd 0>; };
+ cti@22020000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x22020000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + }; + etm0: etm@22040000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0 0x22040000 0 0x1000>; @@ -289,6 +317,15 @@ power-domains = <&scpi_devpd 0>; };
+ cti@22120000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x22120000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + }; + etm1: etm@22140000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0 0x22140000 0 0x1000>; @@ -314,6 +351,16 @@ power-domains = <&scpi_devpd 0>; };
+ + cti@23020000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x23020000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + }; + etm2: etm@23040000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0 0x23040000 0 0x1000>; @@ -386,6 +433,15 @@ power-domains = <&scpi_devpd 0>; };
+ cti@23120000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x23120000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + }; + etm3: etm@23140000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0 0x23140000 0 0x1000>; @@ -411,6 +467,16 @@ power-domains = <&scpi_devpd 0>; };
+ cti@23220000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x23220000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + }; + + etm4: etm@23240000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0 0x23240000 0 0x1000>; @@ -436,6 +502,16 @@ power-domains = <&scpi_devpd 0>; };
+ cti@23320000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x23320000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + }; + + etm5: etm@23340000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0 0x23340000 0 0x1000>; diff --git a/arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi b/arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi index cf285152deab..bb62134f612a 100644 --- a/arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi +++ b/arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi @@ -82,4 +82,13 @@
}; }; + + cti@20160000 { /* sys_cti_2 */ + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x20160000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + }; }; diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi index d302d8d639a1..169060609e00 100644 --- a/arch/arm64/boot/dts/qcom/msm8916.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi @@ -18,6 +18,9 @@ #include <dt-bindings/thermal/thermal.h>
/ { + model = "Qualcomm Technologies, Inc. MSM8916"; + compatible = "qcom,msm8916"; + interrupt-parent = <&intc>;
#address-cells = <2>; @@ -1150,7 +1153,6 @@
clocks = <&rpmcc RPM_QDSS_CLK>, <&rpmcc RPM_QDSS_A_CLK>; clock-names = "apb_pclk", "atclk"; - out-ports { #address-cells = <1>; #size-cells = <0>; @@ -1296,7 +1298,7 @@ cpu = <&CPU3>; };
- etm@85c000 { + etm0: etm@85c000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85c000 0x1000>;
@@ -1314,7 +1316,7 @@ }; };
- etm@85d000 { + etm1: etm@85d000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85d000 0x1000>;
@@ -1368,6 +1370,99 @@ }; };
+ /* System CTIs */ + /* CTI 0 - TMC connections */ + + cti@810000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x810000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + /* CTI 1 - TPIU connections */ + cti@811000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x811000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + }; + + /* CTIs 2-11 - no information - not instantiated */ + + /* Core CTIs; CTIs 12-15 */ + /* CTI - CPU-0 */ + cti@858000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x858000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + #address-cells = <1>; + #size-cells = <0>; + + trig-conns@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + arm,trig-in-sigs = <0 1 2 3>; + arm,trig-out-sigs = <4 5 6 7>; + arm,cs-dev-assoc = <&etm0>; + }; + + trig-conns@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + cpu = <&CPU0>; + arm,trig-out-sigs=<0 1 2 3>; + arm,trig-in-sigs=<4 5 6 7>; + }; + + }; + + + /* CTI - CPU-1 */ + cti@859000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x859000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + + arm,cti-v8-arch; + cpu = <&CPU1>; + arm,cs-dev-assoc = <&etm1>; + }; + + /* CTI - CPU-2 */ + cti@85a000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x85a000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + + arm,cti-ctm-id = <1>; + + trig-conns@0 { + arm,trig-out-sigs=<0 1 2 3>; + arm,trig-in-sigs=<4 5 6 7>; + arm,trig-conn-name = "test_conn_name"; + }; + }; + + /* CTI - CPU-3 */ + cti@85b000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x85b000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + }; + venus: video-codec@1d00000 { compatible = "qcom,msm8916-venus"; reg = <0x01d00000 0xff000>;
On Wed, Jan 09, 2019 at 10:54:40PM +0000, Mike Leach wrote:
Add in test device tree definitions for qcom DB410 platform, and ARM Juno platform.
Signed-off-by: Mike Leach mike.leach@linaro.org
arch/arm64/boot/dts/arm/juno-base.dtsi | 76 ++++++++++++++++ arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi | 9 ++ arch/arm64/boot/dts/qcom/msm8916.dtsi | 101 +++++++++++++++++++++- 3 files changed, 183 insertions(+), 3 deletions(-)
This needs to be split in two different patches, one for Juno and another one for MSM since they will be picked up by two different maintainers.
diff --git a/arch/arm64/boot/dts/arm/juno-base.dtsi b/arch/arm64/boot/dts/arm/juno-base.dtsi index ed774ee8f659..8c8037cac5bd 100644 --- a/arch/arm64/boot/dts/arm/juno-base.dtsi +++ b/arch/arm64/boot/dts/arm/juno-base.dtsi @@ -132,6 +132,15 @@ }; };
- cti@20020000 { /* sys_cti_0 */
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0 0x20020000 0 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
- };
- tpiu@20030000 { compatible = "arm,coresight-tpiu", "arm,primecell"; reg = <0 0x20030000 0 0x1000>;
@@ -220,6 +229,16 @@ }; };
- cti@20110000 { /* sys_cti_1 */
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0 0x20110000 0 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
- };
- cpu_debug0: cpu-debug@22010000 { compatible = "arm,coresight-cpu-debug", "arm,primecell"; reg = <0x0 0x22010000 0x0 0x1000>;
@@ -229,6 +248,15 @@ power-domains = <&scpi_devpd 0>; };
- cti@22020000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0 0x22020000 0 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
- };
- etm0: etm@22040000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0 0x22040000 0 0x1000>;
@@ -289,6 +317,15 @@ power-domains = <&scpi_devpd 0>; };
- cti@22120000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0 0x22120000 0 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
- };
- etm1: etm@22140000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0 0x22140000 0 0x1000>;
@@ -314,6 +351,16 @@ power-domains = <&scpi_devpd 0>; };
- cti@23020000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0 0x23020000 0 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
- };
- etm2: etm@23040000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0 0x23040000 0 0x1000>;
@@ -386,6 +433,15 @@ power-domains = <&scpi_devpd 0>; };
- cti@23120000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0 0x23120000 0 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
- };
- etm3: etm@23140000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0 0x23140000 0 0x1000>;
@@ -411,6 +467,16 @@ power-domains = <&scpi_devpd 0>; };
- cti@23220000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0 0x23220000 0 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
- };
- etm4: etm@23240000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0 0x23240000 0 0x1000>;
@@ -436,6 +502,16 @@ power-domains = <&scpi_devpd 0>; };
- cti@23320000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0 0x23320000 0 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
- };
- etm5: etm@23340000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0 0x23340000 0 0x1000>;
diff --git a/arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi b/arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi index cf285152deab..bb62134f612a 100644 --- a/arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi +++ b/arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi @@ -82,4 +82,13 @@ }; };
- cti@20160000 { /* sys_cti_2 */
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0 0x20160000 0 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
- };
}; diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi index d302d8d639a1..169060609e00 100644 --- a/arch/arm64/boot/dts/qcom/msm8916.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi @@ -18,6 +18,9 @@ #include <dt-bindings/thermal/thermal.h> / {
- model = "Qualcomm Technologies, Inc. MSM8916";
- compatible = "qcom,msm8916";
- interrupt-parent = <&intc>;
#address-cells = <2>; @@ -1150,7 +1153,6 @@ clocks = <&rpmcc RPM_QDSS_CLK>, <&rpmcc RPM_QDSS_A_CLK>; clock-names = "apb_pclk", "atclk";
out-ports { #address-cells = <1>; #size-cells = <0>;
@@ -1296,7 +1298,7 @@ cpu = <&CPU3>; };
etm@85c000 {
etm0: etm@85c000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85c000 0x1000>;
@@ -1314,7 +1316,7 @@ }; };
etm@85d000 {
etm1: etm@85d000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85d000 0x1000>;
@@ -1368,6 +1370,99 @@ }; };
/* System CTIs */
/* CTI 0 - TMC connections */
cti@810000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x810000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
};
/* CTI 1 - TPIU connections */
cti@811000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x811000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
};
/* CTIs 2-11 - no information - not instantiated */
/* Core CTIs; CTIs 12-15 */
/* CTI - CPU-0 */
cti@858000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x858000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
#address-cells = <1>;
#size-cells = <0>;
trig-conns@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
arm,trig-in-sigs = <0 1 2 3>;
arm,trig-out-sigs = <4 5 6 7>;
arm,cs-dev-assoc = <&etm0>;
};
trig-conns@1 {
#address-cells = <1>;
#size-cells = <0>;
reg = <1>;
cpu = <&CPU0>;
arm,trig-out-sigs=<0 1 2 3>;
arm,trig-in-sigs=<4 5 6 7>;
};
};
/* CTI - CPU-1 */
cti@859000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x859000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
arm,cti-v8-arch;
cpu = <&CPU1>;
arm,cs-dev-assoc = <&etm1>;
};
/* CTI - CPU-2 */
cti@85a000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x85a000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
arm,cti-ctm-id = <1>;
trig-conns@0 {
arm,trig-out-sigs=<0 1 2 3>;
arm,trig-in-sigs=<4 5 6 7>;
arm,trig-conn-name = "test_conn_name";
};
};
/* CTI - CPU-3 */
cti@85b000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x85b000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
};
- venus: video-codec@1d00000 { compatible = "qcom,msm8916-venus"; reg = <0x01d00000 0xff000>;
-- 2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
On Thu, 17 Jan 2019 at 18:34, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, Jan 09, 2019 at 10:54:40PM +0000, Mike Leach wrote:
Add in test device tree definitions for qcom DB410 platform, and ARM Juno platform.
Signed-off-by: Mike Leach mike.leach@linaro.org
arch/arm64/boot/dts/arm/juno-base.dtsi | 76 ++++++++++++++++ arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi | 9 ++ arch/arm64/boot/dts/qcom/msm8916.dtsi | 101 +++++++++++++++++++++- 3 files changed, 183 insertions(+), 3 deletions(-)
This needs to be split in two different patches, one for Juno and another one for MSM since they will be picked up by two different maintainers.
OK, will do.
Mike
diff --git a/arch/arm64/boot/dts/arm/juno-base.dtsi b/arch/arm64/boot/dts/arm/juno-base.dtsi index ed774ee8f659..8c8037cac5bd 100644 --- a/arch/arm64/boot/dts/arm/juno-base.dtsi +++ b/arch/arm64/boot/dts/arm/juno-base.dtsi @@ -132,6 +132,15 @@ }; };
cti@20020000 { /* sys_cti_0 */
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0 0x20020000 0 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
};
tpiu@20030000 { compatible = "arm,coresight-tpiu", "arm,primecell"; reg = <0 0x20030000 0 0x1000>;
@@ -220,6 +229,16 @@ }; };
cti@20110000 { /* sys_cti_1 */
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0 0x20110000 0 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
};
cpu_debug0: cpu-debug@22010000 { compatible = "arm,coresight-cpu-debug", "arm,primecell"; reg = <0x0 0x22010000 0x0 0x1000>;
@@ -229,6 +248,15 @@ power-domains = <&scpi_devpd 0>; };
cti@22020000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0 0x22020000 0 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
};
etm0: etm@22040000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0 0x22040000 0 0x1000>;
@@ -289,6 +317,15 @@ power-domains = <&scpi_devpd 0>; };
cti@22120000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0 0x22120000 0 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
};
etm1: etm@22140000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0 0x22140000 0 0x1000>;
@@ -314,6 +351,16 @@ power-domains = <&scpi_devpd 0>; };
cti@23020000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0 0x23020000 0 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
};
etm2: etm@23040000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0 0x23040000 0 0x1000>;
@@ -386,6 +433,15 @@ power-domains = <&scpi_devpd 0>; };
cti@23120000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0 0x23120000 0 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
};
etm3: etm@23140000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0 0x23140000 0 0x1000>;
@@ -411,6 +467,16 @@ power-domains = <&scpi_devpd 0>; };
cti@23220000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0 0x23220000 0 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
};
etm4: etm@23240000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0 0x23240000 0 0x1000>;
@@ -436,6 +502,16 @@ power-domains = <&scpi_devpd 0>; };
cti@23320000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0 0x23320000 0 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
};
etm5: etm@23340000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0 0x23340000 0 0x1000>;
diff --git a/arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi b/arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi index cf285152deab..bb62134f612a 100644 --- a/arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi +++ b/arch/arm64/boot/dts/arm/juno-cs-r1r2.dtsi @@ -82,4 +82,13 @@
}; };
cti@20160000 { /* sys_cti_2 */
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0 0x20160000 0 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
};
}; diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi index d302d8d639a1..169060609e00 100644 --- a/arch/arm64/boot/dts/qcom/msm8916.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi @@ -18,6 +18,9 @@ #include <dt-bindings/thermal/thermal.h>
/ {
model = "Qualcomm Technologies, Inc. MSM8916";
compatible = "qcom,msm8916";
interrupt-parent = <&intc>; #address-cells = <2>;
@@ -1150,7 +1153,6 @@
clocks = <&rpmcc RPM_QDSS_CLK>, <&rpmcc RPM_QDSS_A_CLK>; clock-names = "apb_pclk", "atclk";
out-ports { #address-cells = <1>; #size-cells = <0>;
@@ -1296,7 +1298,7 @@ cpu = <&CPU3>; };
etm@85c000 {
etm0: etm@85c000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85c000 0x1000>;
@@ -1314,7 +1316,7 @@ }; };
etm@85d000 {
etm1: etm@85d000 { compatible = "arm,coresight-etm4x", "arm,primecell"; reg = <0x85d000 0x1000>;
@@ -1368,6 +1370,99 @@ }; };
/* System CTIs */
/* CTI 0 - TMC connections */
cti@810000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x810000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
};
/* CTI 1 - TPIU connections */
cti@811000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x811000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
};
/* CTIs 2-11 - no information - not instantiated */
/* Core CTIs; CTIs 12-15 */
/* CTI - CPU-0 */
cti@858000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x858000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
#address-cells = <1>;
#size-cells = <0>;
trig-conns@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
arm,trig-in-sigs = <0 1 2 3>;
arm,trig-out-sigs = <4 5 6 7>;
arm,cs-dev-assoc = <&etm0>;
};
trig-conns@1 {
#address-cells = <1>;
#size-cells = <0>;
reg = <1>;
cpu = <&CPU0>;
arm,trig-out-sigs=<0 1 2 3>;
arm,trig-in-sigs=<4 5 6 7>;
};
};
/* CTI - CPU-1 */
cti@859000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x859000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
arm,cti-v8-arch;
cpu = <&CPU1>;
arm,cs-dev-assoc = <&etm1>;
};
/* CTI - CPU-2 */
cti@85a000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x85a000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
arm,cti-ctm-id = <1>;
trig-conns@0 {
arm,trig-out-sigs=<0 1 2 3>;
arm,trig-in-sigs=<4 5 6 7>;
arm,trig-conn-name = "test_conn_name";
};
};
/* CTI - CPU-3 */
cti@85b000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x85b000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
};
venus: video-codec@1d00000 { compatible = "qcom,msm8916-venus"; reg = <0x01d00000 0xff000>;
-- 2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Adds CTI description to main coresight.txt file. Adds CTI bindings into device tree doc coresight file.
Signed-off-by: Mike Leach mike.leach@linaro.org --- .../testing/sysfs-bus-coresight-devices-cti | 178 ++++++++++++++++++ .../devicetree/bindings/arm/coresight.txt | 173 +++++++++++++++++ Documentation/trace/coresight.txt | 125 +++++++++++- 3 files changed, 475 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-coresight-devices-cti
diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-cti b/Documentation/ABI/testing/sysfs-bus-coresight-devices-cti new file mode 100644 index 000000000000..82289de0d67f --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-cti @@ -0,0 +1,178 @@ +What: /sys/bus/coresight/devices/<memory_map>.cti/enable +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Enable/Disable the CTI hardware. + +What: /sys/bus/coresight/devices/<memory_map>.cti/ctmid +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) Display the associated CTM ID + +What: /sys/bus/coresight/devices/<memory_map>.cti/list_features +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) List the static hardware features of the CTI device. + Lists the connections to other devices, and the available input + and output triggers in these connections. + +What: /sys/bus/coresight/devices/<memory_map>.cti/regs/inout_sel +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Select the index for inen and outen registers. + +What: /sys/bus/coresight/devices/<memory_map>.cti/regs/inen +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Read or write the CTIINEN register selected by inout_sel. + +What: /sys/bus/coresight/devices/<memory_map>.cti/regs/outen +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Read or write the CTIOUTEN register selected by inout_sel. + +What: /sys/bus/coresight/devices/<memory_map>.cti/regs/gate +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Read or write CTIGATE register. + +What: /sys/bus/coresight/devices/<memory_map>.cti/regs/asicctl +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Read or write ASICCTL register. + +What: /sys/bus/coresight/devices/<memory_map>.cti/regs/intack +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Write the INTACK register. + +What: /sys/bus/coresight/devices/<memory_map>.cti/regs/appset +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Set CTIAPPSET register to activate channel. Read back to + determine current value of register. + +What: /sys/bus/coresight/devices/<memory_map>.cti/regs/appclear +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Write APPCLEAR register to deactivate channel. + +What: /sys/bus/coresight/devices/<memory_map>.cti/regs/apppulse +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Write APPPULSE to pulse a channel active for one clock + cycle. + +What: /sys/bus/coresight/devices/<memory_map>.cti/regs/chinstatus +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) Read current status of channel inputs. + +What: /sys/bus/coresight/devices/<memory_map>.cti/regs/choutstatus +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) read current status of channel outputs. + +What: /sys/bus/coresight/devices/<memory_map>.cti/regs/triginstatus +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) read current status of input trigger signals + +What: /sys/bus/coresight/devices/<memory_map>.cti/regs/trigoutstatus +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) read current status of output trigger signals. + +What: /sys/bus/coresight/devices/<memory_map>.cti/channels/trigin_attach +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Attach a CTI input trigger to a CTM channel. + +What: /sys/bus/coresight/devices/<memory_map>.cti/channels/trigin_detach +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Detach a CTI input trigger from a CTM channel. + +What: /sys/bus/coresight/devices/<memory_map>.cti/channels/trigout_attach +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Attach a CTI output trigger to a CTM channel. + +What: /sys/bus/coresight/devices/<memory_map>.cti/channels/trigout_detach +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Detach a CTI output trigger from a CTM channel. + +What: /sys/bus/coresight/devices/<memory_map>.cti/channels/gate_enable +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Enable CTIGATE for single channel or all channels. + +What: /sys/bus/coresight/devices/<memory_map>.cti/channels/gate_disable +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Disable CTIGATE for single channel or all channels. + +What: /sys/bus/coresight/devices/<memory_map>.cti/channels/chan_set +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Activate a single channel. + +What: /sys/bus/coresight/devices/<memory_map>.cti/channels/chan_clear +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Deactivate a single channel. + +What: /sys/bus/coresight/devices/<memory_map>.cti/channels/chan_pulse +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Pulse a single channel - activate for a single clock cycle. + +What: /sys/bus/coresight/devices/<memory_map>.cti/channels/trig_filter_enable +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Enable or disable trigger output signal filtering. + +What: /sys/bus/coresight/devices/<memory_map>.cti/channels/list_xtrigs +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) List the channel / trigger input and output programming. + +What: /sys/bus/coresight/devices/<memory_map>.cti/channels/list_chan_inuse +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) List channels currently in use and those with no attached + triggers. + +What: /sys/bus/coresight/devices/<memory_map>.cti/channels/reset_xtrigs +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Clear all channel / trigger programming. diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt index f8aff65ab921..f19a597aaccb 100644 --- a/Documentation/devicetree/bindings/arm/coresight.txt +++ b/Documentation/devicetree/bindings/arm/coresight.txt @@ -39,9 +39,13 @@ its hardware characteristcs.
- System Trace Macrocell: "arm,coresight-stm", "arm,primecell"; [1] + - Coresight Address Translation Unit (CATU) "arm,coresight-catu", "arm,primecell";
+ - Coresight CTI : + "arm,coresight-cti", "arm,primecell"; + * reg: physical base address and length of the register set(s) of the component.
@@ -55,6 +59,7 @@ its hardware characteristcs. is optional.
* port or ports: see "Graph bindings for Coresight" below. + - Never used by CTI - see "ECT bindings for Coresight" below.
* Additional required properties for System Trace Macrocells (STM): * reg: along with the physical base address and length of the register @@ -94,6 +99,9 @@ its hardware characteristcs. * interrupts : Exactly one SPI may be listed for reporting the address error
+* Optional properties for CTI: + * See ECT bindings for CoreSight below. + Graph bindings for Coresight -------------------------------
@@ -315,3 +323,168 @@ Example:
[1]. There is currently two version of STM: STM32 and STM500. Both have the same HW interface and as such don't need an explicit binding name. + +ECT Bindings for CoreSight +-------------------------- + +The CoreSight Embedded Cross Trigger (ECT) consists of CTI devices connected +to one or more CoreSight components and/or a CPU, with CTIs interconnected in +a star topology via the CTM (which is not programmable). The ECT components +are not part of the trace generation data path and are thus not part of the +CoreSight graph described above. + +The CTI component properties define the connections between the individual CTI +and the components it is directly connected to, consisting of input and output +hardware trigger signals. CTIs can have a maximum number of input and output +hardware trigger signals (8 each for v1 CTI, 32 each for v2 CTI). The number is +defined at design time, the maximum of each defined in the DEVID register. +Note that some hardware trigger signals can be connected to non-CoreSight +components (e.g. UART etc) depending on hardware implementation. + +CTIs are interconnect in a star topology via the CTM, using a number of +programmable channels usually 4, but again implementation defined and described +in the DEVID register. The star topology is not required to be described in the +bindings as the actual connections are software programmable. + +In general the connections between CTI and components via the trigger signals +are implementation defined, other than when v8 core and ETM is present. The v8 +architecture then defines the required signal connections between core and CTI, +and ETM and CTI, if the ETM if present. + +The minimum required binding for a CTI consist of the only the required +properties above. + +e.g. + + /* sys cti 0 - connects to STM, ETF0 (core trace) TPIU and ETR */ + cti0: cti@20020000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0 0x20020000 0 0x1000>; + + clocks = <&soc_smc50mhz>; + clock-names = "apb_pclk"; + power-domains = <&scpi_devpd 0>; + + }; + + +This will result in the driver using the DEVID register to set the +input and output triggers and channels in use. Any user / client +application will require additional information on the connections +between the CTI and other components for correct operation. + +Where information is immediately available for the component connections then +a series of trigger connection nodes can be defined (trig-conns). + +These connections explicitly define the input and output triggers between the +CTI and a connected component. Connections to an cpu use the standard cpu +property, connections to other CoreSight components use the arm,cs-dev-assoc +property. + +Where the signals are connected to a device that is not coresight device then +no association is registered. + +cti@858000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x858000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + + trig-conns@0 { + reg = <0>; + arm,trig-in-sigs = <0 1 2 3>; + arm,trig-out-sigs = <4 5 6 7>; + arm,cs-dev-assoc = <&etm0>; + }; + + trig-conns@1 { + reg = <1>; + cpu = <&CPU0>; + arm,trig-out-sigs=<0 1 2 3>; + arm,trig-in-sigs=<4 5 6 7>; + arm,trig-filters=<0>; + }; + +}; + +Note that where input and output triggers are defined as above, then the driver +will limit the channel connection sysfs API to using only the defined signals. + +The arm,trig-filters property blocks output signals that could cause system +issues is set - such as the dbgreq signal into a CPU. The filtering is +enabled by default, but can be disabled at runtime. + +Finally, a CTI that is an architecturally defined v8 CTI connected to a cpu +and optional ETM may be declared as: + +cti@859000 { + compatible = "arm,coresight-cti", "arm,primecell"; + reg = <0x859000 0x1000>; + + clocks = <&rpmcc RPM_QDSS_CLK>; + clock-names = "apb_pclk"; + + arm,cti-v8-arch; + cpu = <&CPU1>; + arm,cs-dev-assoc = <&etm1>; +}; + +The arm,cti-v8-arch property declares this as a v8 CTI, the cpu property must +be present, and a single arm,cs-dev-assoc may be present to define an attached +ETM. No additional trig-conns nodes are permitted. The driver will build a +connection model according to architectural requirements. This will include +a filter on the CPU debreq signal as described above. + +All the CTI devices are associated with a CTM. On many systems there will be a +single effective CTM (one CTM, or multiple CTMs all interconnected), but it is +possible that systems can have nets of CTIs+CTM that are not interconnected by + a CTM. On these systems a CTM index is declared to associate CTI devices that + are interconnected via the CTM. + + e.g. + arm,ctm-ctm-id=<2> + +CTI devices with the same CTM ID are considered connected by the CTM. If this +parameter is absent then all CTIs are considered interconnected by the same +CTM. + + +*Summary of CTI optional properties: + + * trig-conns : defines a connection node between CTI and a component. + Component may be a CPU, CoreSight device, any other hardware device + or simple external IO lines. + + *arm,trig-out-sigs: List of CTI trigger out signals in use by a + trig-conns node. Only valid as property of trig-conns node. + + *arm,trig-in-sigs: List of CTI trigger in signals in use by a + trig-conns node. Only valid as property of trig-conns node. + + *arm-trig-filters: List of CTI trigger out signals that will be + blocked from becoming active, unless filtering is disabled on the + driver. Only valid as property of trig-conns node. + + *arm,cti-v8-arch: Declares this CTI device as a v8 architecturally + defined device. Use in CTI base node only, no additional trig-conns + nodes permitted if this is declared. + + *cpu : defines a phandle reference to an associated CPU. Use in + trig-conns node, or in CTI base node when arm,cti-v8-arch present. + + *arm,cs-dev-assoc: defines a phandle reference to an associated + CoreSight trace device. When the associated trace device is enabled, + then the respective CTI will be enabled. Use in a trig-conns node, + or in CTI base node when arm,cti-v8-arch present. If the associated + device has not been registered then the node name will be stored as + the connection name for later resolution. If the associated device is + not a CoreSight device or not registered then the node name will + remain the connection name and automatic enabling will not occur. + + *arm,trig-conn-name: defines a connection name that will be displayed, + if not overridden by the name of associated device from + arm,cs-dev-assoc or the CPU. Only valid as property of trig-conns node. + + *arm,ctm-ctm-id: Defines the interconnecting CTM for this device. + Use in CTI base node. diff --git a/Documentation/trace/coresight.txt b/Documentation/trace/coresight.txt index efbc832146e7..dc9708bea7f7 100644 --- a/Documentation/trace/coresight.txt +++ b/Documentation/trace/coresight.txt @@ -122,7 +122,7 @@ Device Tree Bindings
See Documentation/devicetree/bindings/arm/coresight.txt for details.
-As of this writing drivers for ITM, STMs and CTIs are not provided but are +As of this writing drivers for ITM, STMs are not provided but are expected to be added as the solution matures.
@@ -425,6 +425,129 @@ root@genericarmv8:~#
Details on how to use the generic STM API can be found here [2].
+CoreSight Embedded Cross Trigger (CTI & CTM). +--------------------------------------------- + +The CoreSight Cross Trigger Interface (CTI) is a hardware device that takes +individual input and output hardware signals from devices and interconnects +them via the Cross Trigger Matrix (CTM) to other devices, in order to propogate +events between devices. + +e.g. + + 0000000 in_trigs ::::::: + 0 C 0----------->: : + 0 P 0<-----------: : + 0 U 0 out_trigs : : Channels ***** + 0000000 : CTI :<=========>*CTM*<====> (other CTI channel IO) + ####### in_trigs : : (id 0-3) ***** + # ETM #----------->: : + # #<-----------: : + ####### out_trigs ::::::: + +The CTI driver enables the programming of the CTI to attach triggers to +channels. When an input trigger becomes active, the attached channel will +become active. Any output trigger also attached to that channel will also +become active. The active channel is propogated to other CTIs via the CTM +affecting output triggers there, unless filtered by the CTI channel gate. + +It is also possible to activate a channel using system software. + +The CTIs are registered by the system to be associated with CPUs and/or other +CoreSight devices on the trace data path. When these devices are enabled then +the attached CTIs will also be enabled. By default/on power up the CTIs have +no programmed trigger/channel attachments, so will not affect the system +until explicitly programmed. + +The hardware trigger connections between CTIs and devices is implementation +defined, unless the CPU/ETM combination is a v8 architecture, in which case +the connections have an architecturally defined standard layout. + +The hardware trigger signals can also be connected to non-CoreSight devices +(e.g. UART), or be propogated off chip as hardware IO lines. + +All the CTI devices are associated with a CTM. On many systems there will be a +single effective CTM (one CTM, or multiple CTMs all interconnected), but it is +possible that systems can have nets of CTIs+CTM that are not interconnected by + a CTM. On these systems a CTM index is declared to associate CTI devices that + are interconnected via the CTM. + +The CTI devices appear on the existing coresight bus alongside the other +CoreSight devices. + +# ls /sys/bus/coresight/devices/858000.cti +channels ctmid enable list_features mgmt power regs subsystem uevent + +Key items are: +* enable: enables the CTI. +* ctmid : associated CTM - only relevant if system has multiple CTI+CTM clusters +that are not interconnected. +* list_features: displays available hardware defined connections and signals:- + +# cat /sys/bus/coresight/devices/859000.cti/list_features +CTI:859000.cti; Channels:4; Max Trigs:8 +CTM id = 0; Num Conns = 2 +conn(0:cpu1) +Trig IN(3) [ 0 1 2 ] +Trig OUT(3) [ 0 1 2 ] +conn(1:85d000.etm) +Trig IN(4) [ 4 5 6 7 ] +Trig OUT(4) [ 4 5 6 7 ] +Trig OUT_FILTERED [ 0 ] + + +*channels: sub-dir containing the channel API - CTI main programming interface. +*regs: sub-dir giving access to the raw programmable CTI regs. +*mgmt: sub-dir for the standard CoreSight management registers. + +** Channels API : This provides an easy way to attach triggers to channels, +without needing the multiple register operations that are required if +manipluating the 'regs' sub-dir elements directly. + +# ls /sys/bus/coresight/devices/859000.cti/channels/ +chan_clear gate_disable list_xtrigs trigin_attach trigout_detach +chan_pulse gate_enable reset_xtrigs trigin_detach +chan_set list_chan_inuse trig_filter_enable trigout_attach + +Most access to these elements take the form: +echo <chan> [<trigger>] > /<device_path>/<operation> +where the optional <trigger> is only needed for trigXX_at|detach operations. + +e.g. +echo 0 1 > /sys/bus/coresight/devices/859000.cti/channels/trigin_attach +attach trig_in(1) to channel(0). +echo 0 > /sys/bus/coresight/devices/859000.cti/channels/chan_set +activate channel(0) + +*gate_enable, gate_disable operations set the CTI gate to propogate +(enable) to other devices. These operation can take a channel number or 'all' +to operate on all channels. CTI gate is enabled by default at power up. + +*list_xtrigs will output the current channel / trig signal programming for the +CTI, alongside the channels enable through the CTI gate to propogate throughout +the CTM. + +# cat /sys/bus/coresight/devices/859000.cti/channels/list_xtrigs +CTI:859000.cti; Channels:4 +Chan 0: IN [ 1 ]; OUT [ 2 6 ]; +Chan 1: IN [ ]; OUT [ ]; +Chan 2: IN [ ]; OUT [ ]; +Chan 3: IN [ ]; OUT [ ]; +Gate Channels Enabled: [ 0 1 2 3 ] + +*trig_filter_enable: defaults to enabled, disable to allow potentially +dangerous output signals to be set. + +*reset_xtrigs: clear all channel / trigger programming - reset device hardware +to default state. + +*list_chan_inuse: short list of channels that have any signals attached for +this CTI, and those that have none. +# cat /sys/bus/coresight/devices/859000.cti/channels/list_chan_inuse +Chan in use [ 0 ] +Chan free [ 1 2 3 ] + + [1]. Documentation/ABI/testing/sysfs-bus-coresight-devices-stm [2]. Documentation/trace/stm.rst [3]. https://github.com/Linaro/perf-opencsd
On Wed, Jan 09, 2019 at 10:54:41PM +0000, Mike Leach wrote:
Adds CTI description to main coresight.txt file. Adds CTI bindings into device tree doc coresight file.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../testing/sysfs-bus-coresight-devices-cti | 178 ++++++++++++++++++
This goes in a sparate patch.
.../devicetree/bindings/arm/coresight.txt | 173 +++++++++++++++++
This needs to be sent in a patch on its own since the DT binding maintainers need to ACK it before I can pick it up.
Documentation/trace/coresight.txt | 125 +++++++++++-
This would also be in a separate patch since it goes in the Documentation directory, something that is maintained by Jon Corbet. I will do a first pass on it now and may come back to it later. Please see below.
3 files changed, 475 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-coresight-devices-cti
diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-cti b/Documentation/ABI/testing/sysfs-bus-coresight-devices-cti new file mode 100644 index 000000000000..82289de0d67f --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-cti @@ -0,0 +1,178 @@ +What: /sys/bus/coresight/devices/<memory_map>.cti/enable +Date: Jan 2019 +KernelVersion 5.x
Always put the kernel you are targeting. In this case it is 5.0.
+Contact: ?
You, me or both of us - the choice is yours.
+Description: (RW) Enable/Disable the CTI hardware.
+What: /sys/bus/coresight/devices/<memory_map>.cti/ctmid +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) Display the associated CTM ID
+What: /sys/bus/coresight/devices/<memory_map>.cti/list_features +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) List the static hardware features of the CTI device.
Lists the connections to other devices, and the available input
and output triggers in these connections.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/inout_sel +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Select the index for inen and outen registers.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/inen +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Read or write the CTIINEN register selected by inout_sel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/outen +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Read or write the CTIOUTEN register selected by inout_sel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/gate +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Read or write CTIGATE register.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/asicctl +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Read or write ASICCTL register.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/intack +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Write the INTACK register.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/appset +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Set CTIAPPSET register to activate channel. Read back to
determine current value of register.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/appclear +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Write APPCLEAR register to deactivate channel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/apppulse +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Write APPPULSE to pulse a channel active for one clock
cycle.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/chinstatus +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) Read current status of channel inputs.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/choutstatus +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) read current status of channel outputs.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/triginstatus +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) read current status of input trigger signals
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/trigoutstatus +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) read current status of output trigger signals.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/trigin_attach +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Attach a CTI input trigger to a CTM channel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/trigin_detach +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Detach a CTI input trigger from a CTM channel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/trigout_attach +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Attach a CTI output trigger to a CTM channel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/trigout_detach +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Detach a CTI output trigger from a CTM channel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/gate_enable +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Enable CTIGATE for single channel or all channels.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/gate_disable +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Disable CTIGATE for single channel or all channels.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/chan_set +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Activate a single channel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/chan_clear +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Deactivate a single channel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/chan_pulse +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Pulse a single channel - activate for a single clock cycle.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/trig_filter_enable +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Enable or disable trigger output signal filtering.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/list_xtrigs +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) List the channel / trigger input and output programming.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/list_chan_inuse +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) List channels currently in use and those with no attached
triggers.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/reset_xtrigs +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Clear all channel / trigger programming. diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt index f8aff65ab921..f19a597aaccb 100644 --- a/Documentation/devicetree/bindings/arm/coresight.txt +++ b/Documentation/devicetree/bindings/arm/coresight.txt @@ -39,9 +39,13 @@ its hardware characteristcs. - System Trace Macrocell: "arm,coresight-stm", "arm,primecell"; [1]
- Coresight Address Translation Unit (CATU) "arm,coresight-catu", "arm,primecell";
- Coresight CTI :
"arm,coresight-cti", "arm,primecell";
- reg: physical base address and length of the register set(s) of the component.
@@ -55,6 +59,7 @@ its hardware characteristcs. is optional.
- port or ports: see "Graph bindings for Coresight" below.
- Never used by CTI - see "ECT bindings for Coresight" below.
- Additional required properties for System Trace Macrocells (STM):
- reg: along with the physical base address and length of the register
@@ -94,6 +99,9 @@ its hardware characteristcs.
- interrupts : Exactly one SPI may be listed for reporting the address error
+* Optional properties for CTI:
- See ECT bindings for CoreSight below.
Graph bindings for Coresight
@@ -315,3 +323,168 @@ Example: [1]. There is currently two version of STM: STM32 and STM500. Both have the same HW interface and as such don't need an explicit binding name.
+ECT Bindings for CoreSight +--------------------------
+The CoreSight Embedded Cross Trigger (ECT) consists of CTI devices connected +to one or more CoreSight components and/or a CPU, with CTIs interconnected in +a star topology via the CTM (which is not programmable). The ECT components +are not part of the trace generation data path and are thus not part of the +CoreSight graph described above.
I thought ECT was a term coined to represent both CTI and CTM.
+The CTI component properties define the connections between the individual CTI +and the components it is directly connected to, consisting of input and output +hardware trigger signals. CTIs can have a maximum number of input and output +hardware trigger signals (8 each for v1 CTI, 32 each for v2 CTI). The number is +defined at design time, the maximum of each defined in the DEVID register. +Note that some hardware trigger signals can be connected to non-CoreSight +components (e.g. UART etc) depending on hardware implementation.
+CTIs are interconnect in a star topology via the CTM, using a number of +programmable channels usually 4, but again implementation defined and described +in the DEVID register. The star topology is not required to be described in the +bindings as the actual connections are software programmable.
+In general the connections between CTI and components via the trigger signals +are implementation defined, other than when v8 core and ETM is present. The v8 +architecture then defines the required signal connections between core and CTI, +and ETM and CTI, if the ETM if present.
+The minimum required binding for a CTI consist of the only the required +properties above.
+e.g.
- /* sys cti 0 - connects to STM, ETF0 (core trace) TPIU and ETR */
- cti0: cti@20020000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0 0x20020000 0 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
- };
+This will result in the driver using the DEVID register to set the +input and output triggers and channels in use. Any user / client +application will require additional information on the connections +between the CTI and other components for correct operation.
+Where information is immediately available for the component connections then +a series of trigger connection nodes can be defined (trig-conns).
+These connections explicitly define the input and output triggers between the +CTI and a connected component. Connections to an cpu use the standard cpu
s/an cpu/a cpu/
+property, connections to other CoreSight components use the arm,cs-dev-assoc +property.
+Where the signals are connected to a device that is not coresight device then
s/not coresight/not a coresight/
+no association is registered.
+cti@858000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x858000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
trig-conns@0 {
reg = <0>;
arm,trig-in-sigs = <0 1 2 3>;
arm,trig-out-sigs = <4 5 6 7>;
arm,cs-dev-assoc = <&etm0>;
};
trig-conns@1 {
reg = <1>;
cpu = <&CPU0>;
arm,trig-out-sigs=<0 1 2 3>;
arm,trig-in-sigs=<4 5 6 7>;
arm,trig-filters=<0>;
};
+};
+Note that where input and output triggers are defined as above, then the driver +will limit the channel connection sysfs API to using only the defined signals.
+The arm,trig-filters property blocks output signals that could cause system +issues is set - such as the dbgreq signal into a CPU. The filtering is
s/issues is set/issues if set/
+enabled by default, but can be disabled at runtime.
+Finally, a CTI that is an architecturally defined v8 CTI connected to a cpu +and optional ETM may be declared as:
+cti@859000 {
- compatible = "arm,coresight-cti", "arm,primecell";
- reg = <0x859000 0x1000>;
- clocks = <&rpmcc RPM_QDSS_CLK>;
- clock-names = "apb_pclk";
- arm,cti-v8-arch;
- cpu = <&CPU1>;
- arm,cs-dev-assoc = <&etm1>;
+};
+The arm,cti-v8-arch property declares this as a v8 CTI, the cpu property must +be present, and a single arm,cs-dev-assoc may be present to define an attached +ETM. No additional trig-conns nodes are permitted. The driver will build a +connection model according to architectural requirements. This will include +a filter on the CPU debreq signal as described above.
s/debreq/dbgreq/
+All the CTI devices are associated with a CTM. On many systems there will be a +single effective CTM (one CTM, or multiple CTMs all interconnected), but it is +possible that systems can have nets of CTIs+CTM that are not interconnected by
- a CTM. On these systems a CTM index is declared to associate CTI devices that
- are interconnected via the CTM.
- e.g.
- arm,ctm-ctm-id=<2>
+CTI devices with the same CTM ID are considered connected by the CTM. If this +parameter is absent then all CTIs are considered interconnected by the same +CTM.
+*Summary of CTI optional properties:
* trig-conns : defines a connection node between CTI and a component.
s/defines/Defines
Component may be a CPU, CoreSight device, any other hardware device
or simple external IO lines.
*arm,trig-out-sigs: List of CTI trigger out signals in use by a
trig-conns node. Only valid as property of trig-conns node.
*arm,trig-in-sigs: List of CTI trigger in signals in use by a
trig-conns node. Only valid as property of trig-conns node.
*arm-trig-filters: List of CTI trigger out signals that will be
blocked from becoming active, unless filtering is disabled on the
driver. Only valid as property of trig-conns node.
*arm,cti-v8-arch: Declares this CTI device as a v8 architecturally
defined device. Use in CTI base node only, no additional trig-conns
nodes permitted if this is declared.
*cpu : defines a phandle reference to an associated CPU. Use in
s/defines/Defines
trig-conns node, or in CTI base node when arm,cti-v8-arch present.
*arm,cs-dev-assoc: defines a phandle reference to an associated
s/defines/Defines
CoreSight trace device. When the associated trace device is enabled,
then the respective CTI will be enabled. Use in a trig-conns node,
or in CTI base node when arm,cti-v8-arch present. If the associated
device has not been registered then the node name will be stored as
the connection name for later resolution. If the associated device is
not a CoreSight device or not registered then the node name will
remain the connection name and automatic enabling will not occur.
*arm,trig-conn-name: defines a connection name that will be displayed,
s/defines/Defines
And I think it would be worth giving an example of how the binding is used.
if not overridden by the name of associated device from
arm,cs-dev-assoc or the CPU. Only valid as property of trig-conns node.
*arm,ctm-ctm-id: Defines the interconnecting CTM for this device.
Use in CTI base node.
diff --git a/Documentation/trace/coresight.txt b/Documentation/trace/coresight.txt index efbc832146e7..dc9708bea7f7 100644 --- a/Documentation/trace/coresight.txt +++ b/Documentation/trace/coresight.txt @@ -122,7 +122,7 @@ Device Tree Bindings See Documentation/devicetree/bindings/arm/coresight.txt for details. -As of this writing drivers for ITM, STMs and CTIs are not provided but are +As of this writing drivers for ITM, STMs are not provided but are expected to be added as the solution matures. @@ -425,6 +425,129 @@ root@genericarmv8:~# Details on how to use the generic STM API can be found here [2]. +CoreSight Embedded Cross Trigger (CTI & CTM). +---------------------------------------------
+The CoreSight Cross Trigger Interface (CTI) is a hardware device that takes +individual input and output hardware signals from devices and interconnects +them via the Cross Trigger Matrix (CTM) to other devices, in order to propogate
s/propogate/propagate/
+events between devices.
+e.g.
- 0000000 in_trigs :::::::
- 0 C 0----------->: :
- 0 P 0<-----------: :
- 0 U 0 out_trigs : : Channels *****
- 0000000 : CTI :<=========>*CTM*<====> (other CTI channel IO)
- ####### in_trigs : : (id 0-3) *****
- # ETM #----------->: :
- # #<-----------: :
- ####### out_trigs :::::::
+The CTI driver enables the programming of the CTI to attach triggers to +channels. When an input trigger becomes active, the attached channel will +become active. Any output trigger also attached to that channel will also +become active. The active channel is propogated to other CTIs via the CTMa
s/propogated/propagated/
+affecting output triggers there, unless filtered by the CTI channel gate.
+It is also possible to activate a channel using system software.
+The CTIs are registered by the system to be associated with CPUs and/or other +CoreSight devices on the trace data path. When these devices are enabled then +the attached CTIs will also be enabled. By default/on power up the CTIs have +no programmed trigger/channel attachments, so will not affect the system +until explicitly programmed.
+The hardware trigger connections between CTIs and devices is implementation +defined, unless the CPU/ETM combination is a v8 architecture, in which case +the connections have an architecturally defined standard layout.
+The hardware trigger signals can also be connected to non-CoreSight devices +(e.g. UART), or be propogated off chip as hardware IO lines.
s/propogated/propagated/
+All the CTI devices are associated with a CTM. On many systems there will be a +single effective CTM (one CTM, or multiple CTMs all interconnected), but it is +possible that systems can have nets of CTIs+CTM that are not interconnected by
- a CTM. On these systems a CTM index is declared to associate CTI devices that
- are interconnected via the CTM.
+The CTI devices appear on the existing coresight bus alongside the other
s/coresight/CoreSight
+CoreSight devices.
+# ls /sys/bus/coresight/devices/858000.cti +channels ctmid enable list_features mgmt power regs subsystem uevent
+Key items are: +* enable: enables the CTI. +* ctmid : associated CTM - only relevant if system has multiple CTI+CTM clusters +that are not interconnected. +* list_features: displays available hardware defined connections and signals:-
+# cat /sys/bus/coresight/devices/859000.cti/list_features +CTI:859000.cti; Channels:4; Max Trigs:8 +CTM id = 0; Num Conns = 2
"Num Conns" can be misleading - wouldn't node be more appropriate since this is the therminology that is used in the bindings document.
+conn(0:cpu1) +Trig IN(3) [ 0 1 2 ] +Trig OUT(3) [ 0 1 2 ] +conn(1:85d000.etm) +Trig IN(4) [ 4 5 6 7 ] +Trig OUT(4) [ 4 5 6 7 ] +Trig OUT_FILTERED [ 0 ]
That will be a problem - the general rule for sysfs entries is to have one line of output for each.
+*channels: sub-dir containing the channel API - CTI main programming interface. +*regs: sub-dir giving access to the raw programmable CTI regs. +*mgmt: sub-dir for the standard CoreSight management registers.
+** Channels API : This provides an easy way to attach triggers to channels, +without needing the multiple register operations that are required if +manipluating the 'regs' sub-dir elements directly.
s/manipluating/manipulating/
+# ls /sys/bus/coresight/devices/859000.cti/channels/ +chan_clear gate_disable list_xtrigs trigin_attach trigout_detach +chan_pulse gate_enable reset_xtrigs trigin_detach +chan_set list_chan_inuse trig_filter_enable trigout_attach
+Most access to these elements take the form: +echo <chan> [<trigger>] > /<device_path>/<operation> +where the optional <trigger> is only needed for trigXX_at|detach operations.
+e.g. +echo 0 1 > /sys/bus/coresight/devices/859000.cti/channels/trigin_attach +attach trig_in(1) to channel(0). +echo 0 > /sys/bus/coresight/devices/859000.cti/channels/chan_set +activate channel(0)
+*gate_enable, gate_disable operations set the CTI gate to propogate
s/propogate/propagate/
+(enable) to other devices. These operation can take a channel number or 'all' +to operate on all channels. CTI gate is enabled by default at power up.
+*list_xtrigs will output the current channel / trig signal programming for the +CTI, alongside the channels enable through the CTI gate to propogate throughout
s/propogate/propagate/
+the CTM.
+# cat /sys/bus/coresight/devices/859000.cti/channels/list_xtrigs +CTI:859000.cti; Channels:4 +Chan 0: IN [ 1 ]; OUT [ 2 6 ]; +Chan 1: IN [ ]; OUT [ ]; +Chan 2: IN [ ]; OUT [ ]; +Chan 3: IN [ ]; OUT [ ]; +Gate Channels Enabled: [ 0 1 2 3 ]
This too will be a problem.
+*trig_filter_enable: defaults to enabled, disable to allow potentially +dangerous output signals to be set.
+*reset_xtrigs: clear all channel / trigger programming - reset device hardware +to default state.
+*list_chan_inuse: short list of channels that have any signals attached for +this CTI, and those that have none. +# cat /sys/bus/coresight/devices/859000.cti/channels/list_chan_inuse +Chan in use [ 0 ] +Chan free [ 1 2 3 ]
I will likely get back to this document later.
[1]. Documentation/ABI/testing/sysfs-bus-coresight-devices-stm [2]. Documentation/trace/stm.rst [3]. https://github.com/Linaro/perf-opencsd -- 2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
Hi Mathieu,Ack on the spelling issues - more detailed responses below. On Thu, 17 Jan 2019 at 20:36, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, Jan 09, 2019 at 10:54:41PM +0000, Mike Leach wrote:
Adds CTI description to main coresight.txt file. Adds CTI bindings into device tree doc coresight file.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../testing/sysfs-bus-coresight-devices-cti | 178 ++++++++++++++++++
This goes in a sparate patch.
will do.
.../devicetree/bindings/arm/coresight.txt | 173 +++++++++++++++++
This needs to be sent in a patch on its own since the DT binding maintainers need to ACK it before I can pick it up.
Documentation/trace/coresight.txt | 125 +++++++++++-
This would also be in a separate patch since it goes in the Documentation directory, something that is maintained by Jon Corbet. I will do a first pass on it now and may come back to it later. Please see below.
3 files changed, 475 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-coresight-devices-cti
diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-cti b/Documentation/ABI/testing/sysfs-bus-coresight-devices-cti new file mode 100644 index 000000000000..82289de0d67f --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-cti @@ -0,0 +1,178 @@ +What: /sys/bus/coresight/devices/<memory_map>.cti/enable +Date: Jan 2019 +KernelVersion 5.x
Always put the kernel you are targeting. In this case it is 5.0.
OK
+Contact: ?
You, me or both of us - the choice is yours.
Wasn't sure on this. will update.
+Description: (RW) Enable/Disable the CTI hardware.
+What: /sys/bus/coresight/devices/<memory_map>.cti/ctmid +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) Display the associated CTM ID
+What: /sys/bus/coresight/devices/<memory_map>.cti/list_features +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) List the static hardware features of the CTI device.
Lists the connections to other devices, and the available input
and output triggers in these connections.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/inout_sel +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Select the index for inen and outen registers.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/inen +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Read or write the CTIINEN register selected by inout_sel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/outen +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Read or write the CTIOUTEN register selected by inout_sel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/gate +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Read or write CTIGATE register.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/asicctl +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Read or write ASICCTL register.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/intack +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Write the INTACK register.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/appset +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Set CTIAPPSET register to activate channel. Read back to
determine current value of register.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/appclear +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Write APPCLEAR register to deactivate channel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/apppulse +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Write APPPULSE to pulse a channel active for one clock
cycle.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/chinstatus +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) Read current status of channel inputs.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/choutstatus +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) read current status of channel outputs.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/triginstatus +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) read current status of input trigger signals
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/trigoutstatus +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) read current status of output trigger signals.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/trigin_attach +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Attach a CTI input trigger to a CTM channel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/trigin_detach +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Detach a CTI input trigger from a CTM channel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/trigout_attach +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Attach a CTI output trigger to a CTM channel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/trigout_detach +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Detach a CTI output trigger from a CTM channel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/gate_enable +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Enable CTIGATE for single channel or all channels.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/gate_disable +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Disable CTIGATE for single channel or all channels.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/chan_set +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Activate a single channel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/chan_clear +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Deactivate a single channel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/chan_pulse +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Pulse a single channel - activate for a single clock cycle.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/trig_filter_enable +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Enable or disable trigger output signal filtering.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/list_xtrigs +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) List the channel / trigger input and output programming.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/list_chan_inuse +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) List channels currently in use and those with no attached
triggers.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/reset_xtrigs +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Clear all channel / trigger programming. diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt index f8aff65ab921..f19a597aaccb 100644 --- a/Documentation/devicetree/bindings/arm/coresight.txt +++ b/Documentation/devicetree/bindings/arm/coresight.txt @@ -39,9 +39,13 @@ its hardware characteristcs.
- System Trace Macrocell: "arm,coresight-stm", "arm,primecell"; [1]
- Coresight Address Translation Unit (CATU) "arm,coresight-catu", "arm,primecell";
- Coresight CTI :
"arm,coresight-cti", "arm,primecell";
* reg: physical base address and length of the register set(s) of the component.
@@ -55,6 +59,7 @@ its hardware characteristcs. is optional.
* port or ports: see "Graph bindings for Coresight" below.
- Never used by CTI - see "ECT bindings for Coresight" below.
- Additional required properties for System Trace Macrocells (STM):
- reg: along with the physical base address and length of the register
@@ -94,6 +99,9 @@ its hardware characteristcs. * interrupts : Exactly one SPI may be listed for reporting the address error
+* Optional properties for CTI:
* See ECT bindings for CoreSight below.
Graph bindings for Coresight
@@ -315,3 +323,168 @@ Example:
[1]. There is currently two version of STM: STM32 and STM500. Both have the same HW interface and as such don't need an explicit binding name.
+ECT Bindings for CoreSight +--------------------------
+The CoreSight Embedded Cross Trigger (ECT) consists of CTI devices connected +to one or more CoreSight components and/or a CPU, with CTIs interconnected in +a star topology via the CTM (which is not programmable). The ECT components +are not part of the trace generation data path and are thus not part of the +CoreSight graph described above.
I thought ECT was a term coined to represent both CTI and CTM.
Correct - the official name of the hardware technology is ECT - consisting of CTI and CTM components. The bindings primarily relate to CTI -> other device connections, but there is an entry to represent the interconnecting CTM, where more than one independent network of CTIs + CTMs is present.
+The CTI component properties define the connections between the individual CTI +and the components it is directly connected to, consisting of input and output +hardware trigger signals. CTIs can have a maximum number of input and output +hardware trigger signals (8 each for v1 CTI, 32 each for v2 CTI). The number is +defined at design time, the maximum of each defined in the DEVID register. +Note that some hardware trigger signals can be connected to non-CoreSight +components (e.g. UART etc) depending on hardware implementation.
+CTIs are interconnect in a star topology via the CTM, using a number of +programmable channels usually 4, but again implementation defined and described +in the DEVID register. The star topology is not required to be described in the +bindings as the actual connections are software programmable.
+In general the connections between CTI and components via the trigger signals +are implementation defined, other than when v8 core and ETM is present. The v8 +architecture then defines the required signal connections between core and CTI, +and ETM and CTI, if the ETM if present.
+The minimum required binding for a CTI consist of the only the required +properties above.
+e.g.
/* sys cti 0 - connects to STM, ETF0 (core trace) TPIU and ETR */
cti0: cti@20020000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0 0x20020000 0 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
};
+This will result in the driver using the DEVID register to set the +input and output triggers and channels in use. Any user / client +application will require additional information on the connections +between the CTI and other components for correct operation.
+Where information is immediately available for the component connections then +a series of trigger connection nodes can be defined (trig-conns).
+These connections explicitly define the input and output triggers between the +CTI and a connected component. Connections to an cpu use the standard cpu
s/an cpu/a cpu/
+property, connections to other CoreSight components use the arm,cs-dev-assoc +property.
+Where the signals are connected to a device that is not coresight device then
s/not coresight/not a coresight/
+no association is registered.
+cti@858000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x858000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
trig-conns@0 {
reg = <0>;
arm,trig-in-sigs = <0 1 2 3>;
arm,trig-out-sigs = <4 5 6 7>;
arm,cs-dev-assoc = <&etm0>;
};
trig-conns@1 {
reg = <1>;
cpu = <&CPU0>;
arm,trig-out-sigs=<0 1 2 3>;
arm,trig-in-sigs=<4 5 6 7>;
arm,trig-filters=<0>;
};
+};
+Note that where input and output triggers are defined as above, then the driver +will limit the channel connection sysfs API to using only the defined signals.
+The arm,trig-filters property blocks output signals that could cause system +issues is set - such as the dbgreq signal into a CPU. The filtering is
s/issues is set/issues if set/
+enabled by default, but can be disabled at runtime.
+Finally, a CTI that is an architecturally defined v8 CTI connected to a cpu +and optional ETM may be declared as:
+cti@859000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x859000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
arm,cti-v8-arch;
cpu = <&CPU1>;
arm,cs-dev-assoc = <&etm1>;
+};
+The arm,cti-v8-arch property declares this as a v8 CTI, the cpu property must +be present, and a single arm,cs-dev-assoc may be present to define an attached +ETM. No additional trig-conns nodes are permitted. The driver will build a +connection model according to architectural requirements. This will include +a filter on the CPU debreq signal as described above.
s/debreq/dbgreq/
+All the CTI devices are associated with a CTM. On many systems there will be a +single effective CTM (one CTM, or multiple CTMs all interconnected), but it is +possible that systems can have nets of CTIs+CTM that are not interconnected by
- a CTM. On these systems a CTM index is declared to associate CTI devices that
- are interconnected via the CTM.
- e.g.
arm,ctm-ctm-id=<2>
+CTI devices with the same CTM ID are considered connected by the CTM. If this +parameter is absent then all CTIs are considered interconnected by the same +CTM.
+*Summary of CTI optional properties:
* trig-conns : defines a connection node between CTI and a component.
s/defines/Defines
Component may be a CPU, CoreSight device, any other hardware device
or simple external IO lines.
*arm,trig-out-sigs: List of CTI trigger out signals in use by a
trig-conns node. Only valid as property of trig-conns node.
*arm,trig-in-sigs: List of CTI trigger in signals in use by a
trig-conns node. Only valid as property of trig-conns node.
*arm-trig-filters: List of CTI trigger out signals that will be
blocked from becoming active, unless filtering is disabled on the
driver. Only valid as property of trig-conns node.
*arm,cti-v8-arch: Declares this CTI device as a v8 architecturally
defined device. Use in CTI base node only, no additional trig-conns
nodes permitted if this is declared.
*cpu : defines a phandle reference to an associated CPU. Use in
s/defines/Defines
trig-conns node, or in CTI base node when arm,cti-v8-arch present.
*arm,cs-dev-assoc: defines a phandle reference to an associated
s/defines/Defines
CoreSight trace device. When the associated trace device is enabled,
then the respective CTI will be enabled. Use in a trig-conns node,
or in CTI base node when arm,cti-v8-arch present. If the associated
device has not been registered then the node name will be stored as
the connection name for later resolution. If the associated device is
not a CoreSight device or not registered then the node name will
remain the connection name and automatic enabling will not occur.
*arm,trig-conn-name: defines a connection name that will be displayed,
s/defines/Defines
And I think it would be worth giving an example of how the binding is used.
if not overridden by the name of associated device from
arm,cs-dev-assoc or the CPU. Only valid as property of trig-conns node.
*arm,ctm-ctm-id: Defines the interconnecting CTM for this device.
Use in CTI base node.
diff --git a/Documentation/trace/coresight.txt b/Documentation/trace/coresight.txt index efbc832146e7..dc9708bea7f7 100644 --- a/Documentation/trace/coresight.txt +++ b/Documentation/trace/coresight.txt @@ -122,7 +122,7 @@ Device Tree Bindings
See Documentation/devicetree/bindings/arm/coresight.txt for details.
-As of this writing drivers for ITM, STMs and CTIs are not provided but are +As of this writing drivers for ITM, STMs are not provided but are expected to be added as the solution matures.
@@ -425,6 +425,129 @@ root@genericarmv8:~#
Details on how to use the generic STM API can be found here [2].
+CoreSight Embedded Cross Trigger (CTI & CTM). +---------------------------------------------
+The CoreSight Cross Trigger Interface (CTI) is a hardware device that takes +individual input and output hardware signals from devices and interconnects +them via the Cross Trigger Matrix (CTM) to other devices, in order to propogate
s/propogate/propagate/
+events between devices.
+e.g.
- 0000000 in_trigs :::::::
- 0 C 0----------->: :
- 0 P 0<-----------: :
- 0 U 0 out_trigs : : Channels *****
- 0000000 : CTI :<=========>*CTM*<====> (other CTI channel IO)
- ####### in_trigs : : (id 0-3) *****
- # ETM #----------->: :
- # #<-----------: :
- ####### out_trigs :::::::
+The CTI driver enables the programming of the CTI to attach triggers to +channels. When an input trigger becomes active, the attached channel will +become active. Any output trigger also attached to that channel will also +become active. The active channel is propogated to other CTIs via the CTMa
s/propogated/propagated/
+affecting output triggers there, unless filtered by the CTI channel gate.
+It is also possible to activate a channel using system software.
+The CTIs are registered by the system to be associated with CPUs and/or other +CoreSight devices on the trace data path. When these devices are enabled then +the attached CTIs will also be enabled. By default/on power up the CTIs have +no programmed trigger/channel attachments, so will not affect the system +until explicitly programmed.
+The hardware trigger connections between CTIs and devices is implementation +defined, unless the CPU/ETM combination is a v8 architecture, in which case +the connections have an architecturally defined standard layout.
+The hardware trigger signals can also be connected to non-CoreSight devices +(e.g. UART), or be propogated off chip as hardware IO lines.
s/propogated/propagated/
+All the CTI devices are associated with a CTM. On many systems there will be a +single effective CTM (one CTM, or multiple CTMs all interconnected), but it is +possible that systems can have nets of CTIs+CTM that are not interconnected by
- a CTM. On these systems a CTM index is declared to associate CTI devices that
- are interconnected via the CTM.
+The CTI devices appear on the existing coresight bus alongside the other
s/coresight/CoreSight
+CoreSight devices.
+# ls /sys/bus/coresight/devices/858000.cti +channels ctmid enable list_features mgmt power regs subsystem uevent
+Key items are: +* enable: enables the CTI. +* ctmid : associated CTM - only relevant if system has multiple CTI+CTM clusters +that are not interconnected. +* list_features: displays available hardware defined connections and signals:-
+# cat /sys/bus/coresight/devices/859000.cti/list_features +CTI:859000.cti; Channels:4; Max Trigs:8 +CTM id = 0; Num Conns = 2
"Num Conns" can be misleading - wouldn't node be more appropriate since this is the therminology that is used in the bindings document.
+conn(0:cpu1) +Trig IN(3) [ 0 1 2 ] +Trig OUT(3) [ 0 1 2 ] +conn(1:85d000.etm) +Trig IN(4) [ 4 5 6 7 ] +Trig OUT(4) [ 4 5 6 7 ] +Trig OUT_FILTERED [ 0 ]
That will be a problem - the general rule for sysfs entries is to have one line of output for each.
OK - I can split that into more individual sysfs parameters. e.g. list_features -> Channels:4; Max Trigs:8 . CTM id = 0; Num Conns = 2 list_conns -> 0:cpu1 1:85d000.etm
For the trig in / out / filter info I could use a select con / list feature combo - a bit awkward but the only choice- unless generating sysfs attibutes on the fly is possible in which case each conection on the devicecan have its own set of trigin / out features to list.
Would this approach be acceptable?
+*channels: sub-dir containing the channel API - CTI main programming interface. +*regs: sub-dir giving access to the raw programmable CTI regs. +*mgmt: sub-dir for the standard CoreSight management registers.
+** Channels API : This provides an easy way to attach triggers to channels, +without needing the multiple register operations that are required if +manipluating the 'regs' sub-dir elements directly.
s/manipluating/manipulating/
+# ls /sys/bus/coresight/devices/859000.cti/channels/ +chan_clear gate_disable list_xtrigs trigin_attach trigout_detach +chan_pulse gate_enable reset_xtrigs trigin_detach +chan_set list_chan_inuse trig_filter_enable trigout_attach
+Most access to these elements take the form: +echo <chan> [<trigger>] > /<device_path>/<operation> +where the optional <trigger> is only needed for trigXX_at|detach operations.
+e.g. +echo 0 1 > /sys/bus/coresight/devices/859000.cti/channels/trigin_attach +attach trig_in(1) to channel(0). +echo 0 > /sys/bus/coresight/devices/859000.cti/channels/chan_set +activate channel(0)
+*gate_enable, gate_disable operations set the CTI gate to propogate
s/propogate/propagate/
+(enable) to other devices. These operation can take a channel number or 'all' +to operate on all channels. CTI gate is enabled by default at power up.
+*list_xtrigs will output the current channel / trig signal programming for the +CTI, alongside the channels enable through the CTI gate to propogate throughout
s/propogate/propagate/
+the CTM.
+# cat /sys/bus/coresight/devices/859000.cti/channels/list_xtrigs +CTI:859000.cti; Channels:4 +Chan 0: IN [ 1 ]; OUT [ 2 6 ]; +Chan 1: IN [ ]; OUT [ ]; +Chan 2: IN [ ]; OUT [ ]; +Chan 3: IN [ ]; OUT [ ]; +Gate Channels Enabled: [ 0 1 2 3 ]
This too will be a problem.
I would look to implement a similar method here as suggested above.
+*trig_filter_enable: defaults to enabled, disable to allow potentially +dangerous output signals to be set.
+*reset_xtrigs: clear all channel / trigger programming - reset device hardware +to default state.
+*list_chan_inuse: short list of channels that have any signals attached for +this CTI, and those that have none. +# cat /sys/bus/coresight/devices/859000.cti/channels/list_chan_inuse +Chan in use [ 0 ] +Chan free [ 1 2 3 ]
I will likely get back to this document later.
Thanks fo rthe review so far.
Mike
[1]. Documentation/ABI/testing/sysfs-bus-coresight-devices-stm [2]. Documentation/trace/stm.rst [3]. https://github.com/Linaro/perf-opencsd -- 2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
On Wed, Jan 23, 2019 at 06:29:09PM +0000, Mike Leach wrote:
Hi Mathieu,Ack on the spelling issues - more detailed responses below. On Thu, 17 Jan 2019 at 20:36, Mathieu Poirier mathieu.poirier@linaro.org wrote:
On Wed, Jan 09, 2019 at 10:54:41PM +0000, Mike Leach wrote:
Adds CTI description to main coresight.txt file. Adds CTI bindings into device tree doc coresight file.
Signed-off-by: Mike Leach mike.leach@linaro.org
.../testing/sysfs-bus-coresight-devices-cti | 178 ++++++++++++++++++
This goes in a sparate patch.
will do.
.../devicetree/bindings/arm/coresight.txt | 173 +++++++++++++++++
This needs to be sent in a patch on its own since the DT binding maintainers need to ACK it before I can pick it up.
Documentation/trace/coresight.txt | 125 +++++++++++-
This would also be in a separate patch since it goes in the Documentation directory, something that is maintained by Jon Corbet. I will do a first pass on it now and may come back to it later. Please see below.
3 files changed, 475 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-coresight-devices-cti
diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-cti b/Documentation/ABI/testing/sysfs-bus-coresight-devices-cti new file mode 100644 index 000000000000..82289de0d67f --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-cti @@ -0,0 +1,178 @@ +What: /sys/bus/coresight/devices/<memory_map>.cti/enable +Date: Jan 2019 +KernelVersion 5.x
Always put the kernel you are targeting. In this case it is 5.0.
OK
+Contact: ?
You, me or both of us - the choice is yours.
Wasn't sure on this. will update.
+Description: (RW) Enable/Disable the CTI hardware.
+What: /sys/bus/coresight/devices/<memory_map>.cti/ctmid +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) Display the associated CTM ID
+What: /sys/bus/coresight/devices/<memory_map>.cti/list_features +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) List the static hardware features of the CTI device.
Lists the connections to other devices, and the available input
and output triggers in these connections.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/inout_sel +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Select the index for inen and outen registers.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/inen +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Read or write the CTIINEN register selected by inout_sel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/outen +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Read or write the CTIOUTEN register selected by inout_sel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/gate +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Read or write CTIGATE register.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/asicctl +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Read or write ASICCTL register.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/intack +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Write the INTACK register.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/appset +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Set CTIAPPSET register to activate channel. Read back to
determine current value of register.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/appclear +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Write APPCLEAR register to deactivate channel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/apppulse +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Write APPPULSE to pulse a channel active for one clock
cycle.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/chinstatus +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) Read current status of channel inputs.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/choutstatus +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) read current status of channel outputs.
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/triginstatus +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) read current status of input trigger signals
+What: /sys/bus/coresight/devices/<memory_map>.cti/regs/trigoutstatus +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) read current status of output trigger signals.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/trigin_attach +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Attach a CTI input trigger to a CTM channel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/trigin_detach +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Detach a CTI input trigger from a CTM channel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/trigout_attach +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Attach a CTI output trigger to a CTM channel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/trigout_detach +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Detach a CTI output trigger from a CTM channel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/gate_enable +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Enable CTIGATE for single channel or all channels.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/gate_disable +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Disable CTIGATE for single channel or all channels.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/chan_set +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Activate a single channel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/chan_clear +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Deactivate a single channel.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/chan_pulse +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Pulse a single channel - activate for a single clock cycle.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/trig_filter_enable +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (RW) Enable or disable trigger output signal filtering.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/list_xtrigs +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) List the channel / trigger input and output programming.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/list_chan_inuse +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (R) List channels currently in use and those with no attached
triggers.
+What: /sys/bus/coresight/devices/<memory_map>.cti/channels/reset_xtrigs +Date: Jan 2019 +KernelVersion 5.x +Contact: ? +Description: (W) Clear all channel / trigger programming. diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt index f8aff65ab921..f19a597aaccb 100644 --- a/Documentation/devicetree/bindings/arm/coresight.txt +++ b/Documentation/devicetree/bindings/arm/coresight.txt @@ -39,9 +39,13 @@ its hardware characteristcs.
- System Trace Macrocell: "arm,coresight-stm", "arm,primecell"; [1]
- Coresight Address Translation Unit (CATU) "arm,coresight-catu", "arm,primecell";
- Coresight CTI :
"arm,coresight-cti", "arm,primecell";
* reg: physical base address and length of the register set(s) of the component.
@@ -55,6 +59,7 @@ its hardware characteristcs. is optional.
* port or ports: see "Graph bindings for Coresight" below.
- Never used by CTI - see "ECT bindings for Coresight" below.
- Additional required properties for System Trace Macrocells (STM):
- reg: along with the physical base address and length of the register
@@ -94,6 +99,9 @@ its hardware characteristcs. * interrupts : Exactly one SPI may be listed for reporting the address error
+* Optional properties for CTI:
* See ECT bindings for CoreSight below.
Graph bindings for Coresight
@@ -315,3 +323,168 @@ Example:
[1]. There is currently two version of STM: STM32 and STM500. Both have the same HW interface and as such don't need an explicit binding name.
+ECT Bindings for CoreSight +--------------------------
+The CoreSight Embedded Cross Trigger (ECT) consists of CTI devices connected +to one or more CoreSight components and/or a CPU, with CTIs interconnected in +a star topology via the CTM (which is not programmable). The ECT components +are not part of the trace generation data path and are thus not part of the +CoreSight graph described above.
I thought ECT was a term coined to represent both CTI and CTM.
Correct - the official name of the hardware technology is ECT - consisting of CTI and CTM components. The bindings primarily relate to CTI -> other device connections, but there is an entry to represent the interconnecting CTM, where more than one independent network of CTIs + CTMs is present.
The above sentence "The ECT components are not part of the trace generation data path and are thus not part of the CoreSight graph described above." threw me off. Maybe that's a sign confirming that we should have a bindings/arm/coresight-cti.txt file. After all they are very different components
+The CTI component properties define the connections between the individual CTI +and the components it is directly connected to, consisting of input and output +hardware trigger signals. CTIs can have a maximum number of input and output +hardware trigger signals (8 each for v1 CTI, 32 each for v2 CTI). The number is +defined at design time, the maximum of each defined in the DEVID register. +Note that some hardware trigger signals can be connected to non-CoreSight +components (e.g. UART etc) depending on hardware implementation.
+CTIs are interconnect in a star topology via the CTM, using a number of +programmable channels usually 4, but again implementation defined and described +in the DEVID register. The star topology is not required to be described in the +bindings as the actual connections are software programmable.
+In general the connections between CTI and components via the trigger signals +are implementation defined, other than when v8 core and ETM is present. The v8 +architecture then defines the required signal connections between core and CTI, +and ETM and CTI, if the ETM if present.
+The minimum required binding for a CTI consist of the only the required +properties above.
+e.g.
/* sys cti 0 - connects to STM, ETF0 (core trace) TPIU and ETR */
cti0: cti@20020000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0 0x20020000 0 0x1000>;
clocks = <&soc_smc50mhz>;
clock-names = "apb_pclk";
power-domains = <&scpi_devpd 0>;
};
+This will result in the driver using the DEVID register to set the +input and output triggers and channels in use. Any user / client +application will require additional information on the connections +between the CTI and other components for correct operation.
+Where information is immediately available for the component connections then +a series of trigger connection nodes can be defined (trig-conns).
+These connections explicitly define the input and output triggers between the +CTI and a connected component. Connections to an cpu use the standard cpu
s/an cpu/a cpu/
+property, connections to other CoreSight components use the arm,cs-dev-assoc +property.
+Where the signals are connected to a device that is not coresight device then
s/not coresight/not a coresight/
+no association is registered.
+cti@858000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x858000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
trig-conns@0 {
reg = <0>;
arm,trig-in-sigs = <0 1 2 3>;
arm,trig-out-sigs = <4 5 6 7>;
arm,cs-dev-assoc = <&etm0>;
};
trig-conns@1 {
reg = <1>;
cpu = <&CPU0>;
arm,trig-out-sigs=<0 1 2 3>;
arm,trig-in-sigs=<4 5 6 7>;
arm,trig-filters=<0>;
};
+};
+Note that where input and output triggers are defined as above, then the driver +will limit the channel connection sysfs API to using only the defined signals.
+The arm,trig-filters property blocks output signals that could cause system +issues is set - such as the dbgreq signal into a CPU. The filtering is
s/issues is set/issues if set/
+enabled by default, but can be disabled at runtime.
+Finally, a CTI that is an architecturally defined v8 CTI connected to a cpu +and optional ETM may be declared as:
+cti@859000 {
compatible = "arm,coresight-cti", "arm,primecell";
reg = <0x859000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
arm,cti-v8-arch;
cpu = <&CPU1>;
arm,cs-dev-assoc = <&etm1>;
+};
+The arm,cti-v8-arch property declares this as a v8 CTI, the cpu property must +be present, and a single arm,cs-dev-assoc may be present to define an attached +ETM. No additional trig-conns nodes are permitted. The driver will build a +connection model according to architectural requirements. This will include +a filter on the CPU debreq signal as described above.
s/debreq/dbgreq/
+All the CTI devices are associated with a CTM. On many systems there will be a +single effective CTM (one CTM, or multiple CTMs all interconnected), but it is +possible that systems can have nets of CTIs+CTM that are not interconnected by
- a CTM. On these systems a CTM index is declared to associate CTI devices that
- are interconnected via the CTM.
- e.g.
arm,ctm-ctm-id=<2>
+CTI devices with the same CTM ID are considered connected by the CTM. If this +parameter is absent then all CTIs are considered interconnected by the same +CTM.
+*Summary of CTI optional properties:
* trig-conns : defines a connection node between CTI and a component.
s/defines/Defines
Component may be a CPU, CoreSight device, any other hardware device
or simple external IO lines.
*arm,trig-out-sigs: List of CTI trigger out signals in use by a
trig-conns node. Only valid as property of trig-conns node.
*arm,trig-in-sigs: List of CTI trigger in signals in use by a
trig-conns node. Only valid as property of trig-conns node.
*arm-trig-filters: List of CTI trigger out signals that will be
blocked from becoming active, unless filtering is disabled on the
driver. Only valid as property of trig-conns node.
*arm,cti-v8-arch: Declares this CTI device as a v8 architecturally
defined device. Use in CTI base node only, no additional trig-conns
nodes permitted if this is declared.
*cpu : defines a phandle reference to an associated CPU. Use in
s/defines/Defines
trig-conns node, or in CTI base node when arm,cti-v8-arch present.
*arm,cs-dev-assoc: defines a phandle reference to an associated
s/defines/Defines
CoreSight trace device. When the associated trace device is enabled,
then the respective CTI will be enabled. Use in a trig-conns node,
or in CTI base node when arm,cti-v8-arch present. If the associated
device has not been registered then the node name will be stored as
the connection name for later resolution. If the associated device is
not a CoreSight device or not registered then the node name will
remain the connection name and automatic enabling will not occur.
*arm,trig-conn-name: defines a connection name that will be displayed,
s/defines/Defines
And I think it would be worth giving an example of how the binding is used.
if not overridden by the name of associated device from
arm,cs-dev-assoc or the CPU. Only valid as property of trig-conns node.
*arm,ctm-ctm-id: Defines the interconnecting CTM for this device.
Use in CTI base node.
diff --git a/Documentation/trace/coresight.txt b/Documentation/trace/coresight.txt index efbc832146e7..dc9708bea7f7 100644 --- a/Documentation/trace/coresight.txt +++ b/Documentation/trace/coresight.txt @@ -122,7 +122,7 @@ Device Tree Bindings
See Documentation/devicetree/bindings/arm/coresight.txt for details.
-As of this writing drivers for ITM, STMs and CTIs are not provided but are +As of this writing drivers for ITM, STMs are not provided but are expected to be added as the solution matures.
@@ -425,6 +425,129 @@ root@genericarmv8:~#
Details on how to use the generic STM API can be found here [2].
+CoreSight Embedded Cross Trigger (CTI & CTM). +---------------------------------------------
+The CoreSight Cross Trigger Interface (CTI) is a hardware device that takes +individual input and output hardware signals from devices and interconnects +them via the Cross Trigger Matrix (CTM) to other devices, in order to propogate
s/propogate/propagate/
+events between devices.
+e.g.
- 0000000 in_trigs :::::::
- 0 C 0----------->: :
- 0 P 0<-----------: :
- 0 U 0 out_trigs : : Channels *****
- 0000000 : CTI :<=========>*CTM*<====> (other CTI channel IO)
- ####### in_trigs : : (id 0-3) *****
- # ETM #----------->: :
- # #<-----------: :
- ####### out_trigs :::::::
+The CTI driver enables the programming of the CTI to attach triggers to +channels. When an input trigger becomes active, the attached channel will +become active. Any output trigger also attached to that channel will also +become active. The active channel is propogated to other CTIs via the CTMa
s/propogated/propagated/
+affecting output triggers there, unless filtered by the CTI channel gate.
+It is also possible to activate a channel using system software.
+The CTIs are registered by the system to be associated with CPUs and/or other +CoreSight devices on the trace data path. When these devices are enabled then +the attached CTIs will also be enabled. By default/on power up the CTIs have +no programmed trigger/channel attachments, so will not affect the system +until explicitly programmed.
+The hardware trigger connections between CTIs and devices is implementation +defined, unless the CPU/ETM combination is a v8 architecture, in which case +the connections have an architecturally defined standard layout.
+The hardware trigger signals can also be connected to non-CoreSight devices +(e.g. UART), or be propogated off chip as hardware IO lines.
s/propogated/propagated/
+All the CTI devices are associated with a CTM. On many systems there will be a +single effective CTM (one CTM, or multiple CTMs all interconnected), but it is +possible that systems can have nets of CTIs+CTM that are not interconnected by
- a CTM. On these systems a CTM index is declared to associate CTI devices that
- are interconnected via the CTM.
+The CTI devices appear on the existing coresight bus alongside the other
s/coresight/CoreSight
+CoreSight devices.
+# ls /sys/bus/coresight/devices/858000.cti +channels ctmid enable list_features mgmt power regs subsystem uevent
+Key items are: +* enable: enables the CTI. +* ctmid : associated CTM - only relevant if system has multiple CTI+CTM clusters +that are not interconnected. +* list_features: displays available hardware defined connections and signals:-
+# cat /sys/bus/coresight/devices/859000.cti/list_features +CTI:859000.cti; Channels:4; Max Trigs:8 +CTM id = 0; Num Conns = 2
"Num Conns" can be misleading - wouldn't node be more appropriate since this is the therminology that is used in the bindings document.
+conn(0:cpu1) +Trig IN(3) [ 0 1 2 ] +Trig OUT(3) [ 0 1 2 ] +conn(1:85d000.etm) +Trig IN(4) [ 4 5 6 7 ] +Trig OUT(4) [ 4 5 6 7 ] +Trig OUT_FILTERED [ 0 ]
That will be a problem - the general rule for sysfs entries is to have one line of output for each.
OK - I can split that into more individual sysfs parameters. e.g. list_features -> Channels:4; Max Trigs:8 . CTM id = 0; Num Conns = 2 list_conns -> 0:cpu1 1:85d000.etm
That's a start.
For the trig in / out / filter info I could use a select con / list feature combo - a bit awkward but the only choice- unless generating sysfs attibutes on the fly is possible in which case each conection on the devicecan have its own set of trigin / out features to list.
See what works best. Generating attributes on the fly is painful and clunky to manage. This is work in progress and we will likely have to iterate a few times over this.
Would this approach be acceptable?
+*channels: sub-dir containing the channel API - CTI main programming interface. +*regs: sub-dir giving access to the raw programmable CTI regs. +*mgmt: sub-dir for the standard CoreSight management registers.
+** Channels API : This provides an easy way to attach triggers to channels, +without needing the multiple register operations that are required if +manipluating the 'regs' sub-dir elements directly.
s/manipluating/manipulating/
+# ls /sys/bus/coresight/devices/859000.cti/channels/ +chan_clear gate_disable list_xtrigs trigin_attach trigout_detach +chan_pulse gate_enable reset_xtrigs trigin_detach +chan_set list_chan_inuse trig_filter_enable trigout_attach
+Most access to these elements take the form: +echo <chan> [<trigger>] > /<device_path>/<operation> +where the optional <trigger> is only needed for trigXX_at|detach operations.
+e.g. +echo 0 1 > /sys/bus/coresight/devices/859000.cti/channels/trigin_attach +attach trig_in(1) to channel(0). +echo 0 > /sys/bus/coresight/devices/859000.cti/channels/chan_set +activate channel(0)
+*gate_enable, gate_disable operations set the CTI gate to propogate
s/propogate/propagate/
+(enable) to other devices. These operation can take a channel number or 'all' +to operate on all channels. CTI gate is enabled by default at power up.
+*list_xtrigs will output the current channel / trig signal programming for the +CTI, alongside the channels enable through the CTI gate to propogate throughout
s/propogate/propagate/
+the CTM.
+# cat /sys/bus/coresight/devices/859000.cti/channels/list_xtrigs +CTI:859000.cti; Channels:4 +Chan 0: IN [ 1 ]; OUT [ 2 6 ]; +Chan 1: IN [ ]; OUT [ ]; +Chan 2: IN [ ]; OUT [ ]; +Chan 3: IN [ ]; OUT [ ]; +Gate Channels Enabled: [ 0 1 2 3 ]
This too will be a problem.
I would look to implement a similar method here as suggested above.
+*trig_filter_enable: defaults to enabled, disable to allow potentially +dangerous output signals to be set.
+*reset_xtrigs: clear all channel / trigger programming - reset device hardware +to default state.
+*list_chan_inuse: short list of channels that have any signals attached for +this CTI, and those that have none. +# cat /sys/bus/coresight/devices/859000.cti/channels/list_chan_inuse +Chan in use [ 0 ] +Chan free [ 1 2 3 ]
I will likely get back to this document later.
Thanks fo rthe review so far.
Mike
[1]. Documentation/ABI/testing/sysfs-bus-coresight-devices-stm [2]. Documentation/trace/stm.rst [3]. https://github.com/Linaro/perf-opencsd -- 2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK