Add CoreSight infrastructure for the gtrace framework, providing driver registration helper, clock management, and other core integration APIs.
Signed-off-by: Zane Leung liangzhen@linux.spacemit.com --- drivers/hwtracing/gtrace/Kconfig | 15 + drivers/hwtracing/gtrace/Makefile | 1 + drivers/hwtracing/gtrace/coresight-platform.c | 279 ++++++++++++++++++ drivers/hwtracing/gtrace/coresight.h | 212 +++++++++++++ include/linux/gtrace.h | 7 + 5 files changed, 514 insertions(+) create mode 100644 drivers/hwtracing/gtrace/coresight-platform.c create mode 100644 drivers/hwtracing/gtrace/coresight.h
diff --git a/drivers/hwtracing/gtrace/Kconfig b/drivers/hwtracing/gtrace/Kconfig index 3af47d7c3ce3..5dce4474db7c 100644 --- a/drivers/hwtracing/gtrace/Kconfig +++ b/drivers/hwtracing/gtrace/Kconfig @@ -86,3 +86,18 @@ config RVTRACE_ATBBRIDGE
To compile this driver as a module, choose M here: the module will be called rvtrace-atbbridge. + +config GTRACE_CORESIGHT + tristate "CoreSight Trace Support" + depends on RISCV + depends on GTRACE + select ARM_AMBA + help + This provides the CoreSight backend for the generic hardware trace + (gtrace) framework. It provides the CoreSight claim protocol, + component and driver registration helpers, and clock management + used by the individual CoreSight component drivers (TMC, funnel, + etc.) to register CoreSight hardware with the gtrace core. + + To compile this driver as a module, choose M here: the module + will be called coresight. diff --git a/drivers/hwtracing/gtrace/Makefile b/drivers/hwtracing/gtrace/Makefile index 3f1dcb5664a2..0833a40c397c 100644 --- a/drivers/hwtracing/gtrace/Makefile +++ b/drivers/hwtracing/gtrace/Makefile @@ -11,3 +11,4 @@ rvtrace-encoder-y := rvtrace-encoder-core.o rvtrace-encoder-sysfs.o obj-$(CONFIG_RVTRACE_RAMSINK) += rvtrace-ramsink.o obj-$(CONFIG_RVTRACE_FUNNEL) += rvtrace-funnel.o obj-$(CONFIG_RVTRACE_ATBBRIDGE) += rvtrace-atbbridge.o +obj-$(CONFIG_GTRACE_CORESIGHT) += coresight-platform.o diff --git a/drivers/hwtracing/gtrace/coresight-platform.c b/drivers/hwtracing/gtrace/coresight-platform.c new file mode 100644 index 000000000000..f5a472e86a32 --- /dev/null +++ b/drivers/hwtracing/gtrace/coresight-platform.c @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CoreSight platform backend shared functions for the gtrace framework. + * Derived from drivers/hwtracing/coresight/coresight-core.c. + * + * Copyright (c) 2012, The Linux Foundation. All rights reserved. + */ + +#include <linux/acpi.h> +#include <linux/bitfield.h> +#include <linux/build_bug.h> +#include <linux/cpu_pm.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/slab.h> +#include <linux/stringhash.h> +#include <linux/clk.h> +#include <linux/gtrace.h> +#include <linux/property.h> +#include <linux/delay.h> +#include <linux/pm_runtime.h> +#include <linux/panic_notifier.h> + +#include "coresight.h" + +/* + * When losing synchronisation a new barrier packet needs to be inserted at the + * beginning of the data collected in a buffer. That way the decoder knows that + * it needs to look for another sync sequence. + */ +const u32 coresight_barrier_pkt[4] = {0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff}; +EXPORT_SYMBOL_GPL(coresight_barrier_pkt); + +static u32 coresight_read_claim_tags_unlocked(struct gtrace_component *comp) +{ + return FIELD_GET(CORESIGHT_CLAIM_MASK, + gtrace_relaxed_read32(comp->pdata, CORESIGHT_CLAIMCLR)); +} + +static void coresight_set_self_claim_tag_unlocked(struct gtrace_component *comp) +{ + gtrace_relaxed_write32(comp->pdata, CORESIGHT_CLAIM_SELF_HOSTED, + CORESIGHT_CLAIMSET); + /* Make sure everyone has seen this */ + mb(); +} + +void coresight_clear_self_claim_tag(struct gtrace_platform_data *pdata) +{ + if (pdata->io_mem) + CS_UNLOCK(pdata->base); + coresight_clear_self_claim_tag_unlocked(pdata); + if (pdata->io_mem) + CS_LOCK(pdata->base); +} +EXPORT_SYMBOL_GPL(coresight_clear_self_claim_tag); + +void coresight_clear_self_claim_tag_unlocked(struct gtrace_platform_data *pdata) +{ + gtrace_relaxed_write32(pdata, CORESIGHT_CLAIM_SELF_HOSTED, + CORESIGHT_CLAIMCLR); + /* Make sure everyone has seen this */ + mb(); +} +EXPORT_SYMBOL_GPL(coresight_clear_self_claim_tag_unlocked); + +/* + * coresight_claim_device_unlocked : Claim the device for self-hosted usage + * to prevent an external tool from touching this device. As per PSCI + * standards, section "Preserving the execution context" => "Debug and Trace + * save and Restore", DBGCLAIM[1] is reserved for Self-hosted debug/trace and + * DBGCLAIM[0] is reserved for external tools. + * + * Called with CS_UNLOCKed for the component. + * Returns : 0 on success + */ +int coresight_claim_device_unlocked(struct gtrace_component *comp) +{ + u32 tag; + struct gtrace_platform_data *pdata; + + if (WARN_ON(!comp)) + return -EINVAL; + + pdata = comp->pdata; + tag = coresight_read_claim_tags_unlocked(comp); + + switch (tag) { + case CORESIGHT_CLAIM_FREE: + coresight_set_self_claim_tag_unlocked(comp); + if (coresight_read_claim_tags_unlocked(comp) == CORESIGHT_CLAIM_SELF_HOSTED) + return 0; + + /* There was a race setting the tag, clean up and fail */ + coresight_clear_self_claim_tag_unlocked(pdata); + dev_dbg(&comp->dev, "Busy: Couldn't set self claim tag"); + return -EBUSY; + + case CORESIGHT_CLAIM_EXTERNAL: + /* External debug is an expected state, so log and report BUSY */ + dev_dbg(&comp->dev, "Busy: Claimed by external debugger"); + return -EBUSY; + + default: + case CORESIGHT_CLAIM_SELF_HOSTED: + case CORESIGHT_CLAIM_INVALID: + dev_err_once(&comp->dev, "Invalid claim tag state: %x", tag); + return -EBUSY; + } +} +EXPORT_SYMBOL_GPL(coresight_claim_device_unlocked); + +int coresight_claim_device(struct gtrace_component *comp) +{ + int rc; + + if (WARN_ON(!comp)) + return -EINVAL; + + CS_UNLOCK(comp->pdata->base); + rc = coresight_claim_device_unlocked(comp); + CS_LOCK(comp->pdata->base); + + return rc; +} +EXPORT_SYMBOL_GPL(coresight_claim_device); + +/* + * coresight_disclaim_device_unlocked : Clear the claim tag for the device. + * Called with CS_UNLOCKed for the component. + */ +void coresight_disclaim_device_unlocked(struct gtrace_component *comp) +{ + if (WARN_ON(!comp)) + return; + + if (coresight_read_claim_tags_unlocked(comp) == CORESIGHT_CLAIM_SELF_HOSTED) + coresight_clear_self_claim_tag_unlocked(comp->pdata); + else + /* + * The external agent may have not honoured our claim + * and has manipulated it. Or something else has seriously + * gone wrong in our driver. + */ + dev_WARN_ONCE(&comp->dev, 1, "External agent took claim tag"); +} +EXPORT_SYMBOL_GPL(coresight_disclaim_device_unlocked); + +void coresight_disclaim_device(struct gtrace_component *comp) +{ + if (WARN_ON(!comp)) + return; + + CS_UNLOCK(comp->pdata->base); + coresight_disclaim_device_unlocked(comp); + CS_LOCK(comp->pdata->base); +} +EXPORT_SYMBOL_GPL(coresight_disclaim_device); + +/* + * Attempt to find and enable programming clock (pclk) and trace clock (atclk) + * for the given device. + * + * For ACPI devices, clocks are controlled by firmware, so bail out early in + * this case. Also, skip enabling pclk if the clock is managed by the AMBA + * bus driver instead. + * + * atclk is an optional clock, it will be only enabled when it is existed. + * Otherwise, a NULL pointer will be returned to caller. + * + * Returns: '0' on Success; Error code otherwise. + */ +int coresight_get_enable_clocks(struct device *dev, struct clk **pclk, + struct clk **atclk) +{ + WARN_ON(!pclk); + + if (has_acpi_companion(dev)) + return 0; + + if (!dev_is_amba(dev)) { + /* + * "apb_pclk" is the default clock name for an Arm Primecell + * peripheral, while "apb" is used only by the CTCU driver. + * + * For easier maintenance, CoreSight drivers should use + * "apb_pclk" as the programming clock name. + */ + *pclk = devm_clk_get_optional_enabled(dev, "apb_pclk"); + if (!*pclk) + *pclk = devm_clk_get_optional_enabled(dev, "apb"); + if (IS_ERR(*pclk)) + return PTR_ERR(*pclk); + } + + /* Initialization of atclk is skipped if it is a NULL pointer. */ + if (atclk) { + *atclk = devm_clk_get_optional_enabled(dev, "atclk"); + if (IS_ERR(*atclk)) + return PTR_ERR(*atclk); + } + + return 0; +} +EXPORT_SYMBOL_GPL(coresight_get_enable_clocks); + +struct gtrace_platform_data * +coresight_get_platform_data(struct device *dev) +{ + int ret; + struct gtrace_platform_data *pdata = NULL; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + ret = -ENOMEM; + goto err_out; + } + + pdata->dev = dev; + pdata->control_poll_timeout_usecs = 100; + + ret = gtrace_of_parse_outconns(pdata); + if (ret) { + dev_err_probe(pdata->dev, ret, "failed to parse output connections\n"); + goto err_out; + } + + ret = gtrace_of_parse_inconns(pdata); + if (ret) { + dev_err_probe(pdata->dev, ret, "failed to parse input connections\n"); + goto err_out; + } + + return pdata; +err_out: + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(coresight_get_platform_data); + +int coresight_init_driver_with_owner(const char *drv, struct amba_driver *amba_drv, + struct platform_driver *pdev_drv, struct module *owner, + const char *mod_name) +{ + int ret; + + ret = __amba_driver_register(amba_drv, owner); + if (ret) { + pr_err("%s: error registering AMBA driver\n", drv); + return ret; + } + + ret = __platform_driver_register(pdev_drv, owner, mod_name); + if (!ret) + return 0; + + pr_err("%s: error registering platform driver\n", drv); + amba_driver_unregister(amba_drv); + return ret; +} +EXPORT_SYMBOL_GPL(coresight_init_driver_with_owner); + +void coresight_remove_driver(struct amba_driver *amba_drv, + struct platform_driver *pdev_drv) +{ + platform_driver_unregister(pdev_drv); + amba_driver_unregister(amba_drv); +} +EXPORT_SYMBOL_GPL(coresight_remove_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Pratik Patel pratikp@codeaurora.org"); +MODULE_AUTHOR("Mathieu Poirier mathieu.poirier@linaro.org"); +MODULE_AUTHOR("Zane Leung liangzhen@linux.spacemit.com"); +MODULE_DESCRIPTION("CoreSight backend for the generic trace framework"); diff --git a/drivers/hwtracing/gtrace/coresight.h b/drivers/hwtracing/gtrace/coresight.h new file mode 100644 index 000000000000..f369911daace --- /dev/null +++ b/drivers/hwtracing/gtrace/coresight.h @@ -0,0 +1,212 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CoreSight component register layout and helpers for the gtrace framework. + * Derived from drivers/hwtracing/coresight/coresight-priv.h and + * include/linux/coresight.h. + * + * Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * CoreSight component register layout and helpers for the CoreSight backend + * of the generic trace (gtrace) framework. These definitions are specific to + * the CoreSight architecture and must not be used by the generic core. + */ + +#ifndef __LINUX_CORESIGHT_H__ +#define __LINUX_CORESIGHT_H__ + +#include <linux/gtrace.h> +#include <linux/types.h> +#include <linux/amba/bus.h> + +/* + * Coresight management registers (0xf00-0xfcc) + * 0xfa0 - 0xfa4: Management registers in PFTv1.0 + * Trace registers in PFTv1.1 + */ +#define CORESIGHT_ITCTRL 0xf00 +#define CORESIGHT_CLAIMSET 0xfa0 +#define CORESIGHT_CLAIMCLR 0xfa4 +#define CORESIGHT_LAR 0xfb0 +#define CORESIGHT_LSR 0xfb4 +#define CORESIGHT_DEVARCH 0xfbc +#define CORESIGHT_AUTHSTATUS 0xfb8 +#define CORESIGHT_DEVID 0xfc8 +#define CORESIGHT_DEVTYPE 0xfcc + +/* + * Coresight device CLAIM protocol. + * See PSCI - ARM DEN 0022D, Section: 6.8.1 Debug and Trace save and restore. + */ +#define CORESIGHT_CLAIM_MASK GENMASK(1, 0) +#define CORESIGHT_CLAIM_FREE 0 +#define CORESIGHT_CLAIM_EXTERNAL 1 +#define CORESIGHT_CLAIM_SELF_HOSTED 2 +#define CORESIGHT_CLAIM_INVALID 3 + +/* Peripheral id registers (0xFD0-0xFEC) */ +#define CORESIGHT_PERIPHIDR4 0xfd0 +#define CORESIGHT_PERIPHIDR5 0xfd4 +#define CORESIGHT_PERIPHIDR6 0xfd8 +#define CORESIGHT_PERIPHIDR7 0xfdC +#define CORESIGHT_PERIPHIDR0 0xfe0 +#define CORESIGHT_PERIPHIDR1 0xfe4 +#define CORESIGHT_PERIPHIDR2 0xfe8 +#define CORESIGHT_PERIPHIDR3 0xfeC +/* Component id registers (0xFF0-0xFFC) */ +#define CORESIGHT_COMPIDR0 0xff0 +#define CORESIGHT_COMPIDR1 0xff4 +#define CORESIGHT_COMPIDR2 0xff8 +#define CORESIGHT_COMPIDR3 0xffC + +#define CORESIGHT_UNLOCK 0xc5acce55 + +#define BMVAL(val, lsb, msb) ((val & GENMASK(msb, lsb)) >> lsb) + +extern const u32 coresight_barrier_pkt[4]; +#define CORESIGHT_BARRIER_PKT_SIZE (sizeof(coresight_barrier_pkt)) + +#define CORESIGHT_COMPONENT_ID_VERSION 1 + +static inline void coresight_insert_barrier_packet(void *buf) +{ + if (buf) + memcpy(buf, coresight_barrier_pkt, CORESIGHT_BARRIER_PKT_SIZE); +} + +static inline void CS_LOCK(void __iomem *addr) +{ + do { + /* Wait for things to settle */ + mb(); + writel_relaxed(0x0, addr + CORESIGHT_LAR); + } while (0); +} + +static inline void CS_UNLOCK(void __iomem *addr) +{ + do { + writel_relaxed(CORESIGHT_UNLOCK, addr + CORESIGHT_LAR); + /* Make sure everyone has seen this */ + mb(); + } while (0); +} + +#define CORESIGHT_CIDRn(i) (0xFF0 + ((i) * 4)) + +static inline u32 coresight_get_cid(void __iomem *base) +{ + u32 i, cid = 0; + + for (i = 0; i < 4; i++) + cid |= readl(base + CORESIGHT_CIDRn(i)) << (i * 8); + + return cid; +} + +static inline bool is_coresight_device(void __iomem *base) +{ + u32 cid = coresight_get_cid(base); + + return cid == CORESIGHT_CID; +} + +#define CORESIGHT_PIDRn(i) (0xFE0 + ((i) * 4)) + +static inline u32 coresight_get_pid(struct gtrace_platform_data *pdata) +{ + u32 i, pid = 0; + + for (i = 0; i < 4; i++) + pid |= gtrace_relaxed_read32(pdata, CORESIGHT_PIDRn(i)) << (i * 8); + + return pid; +} + +/* + * Macros and inline functions to handle CoreSight UCI data and driver + * private data in AMBA ID table entries, and extract data values. + */ + +/* coresight AMBA ID, no UCI, no driver data: id table entry */ +#define CS_AMBA_ID(pid) \ + { \ + .id = pid, \ + .mask = 0x000fffff, \ + } + +/* coresight AMBA ID, UCI with driver data only: id table entry. */ +#define CS_AMBA_ID_DATA(pid, dval) \ + { \ + .id = pid, \ + .mask = 0x000fffff, \ + .data = (void *)&(struct amba_cs_uci_id) \ + { \ + .data = (void *)dval, \ + } \ + } + +/* coresight AMBA ID, full UCI structure: id table entry. */ +#define __CS_AMBA_UCI_ID(pid, m, uci_ptr) \ + { \ + .id = pid, \ + .mask = m, \ + .data = (void *)uci_ptr \ + } +#define CS_AMBA_UCI_ID(pid, uci) __CS_AMBA_UCI_ID(pid, 0x000fffff, uci) +/* + * PIDR2[JEDEC], BIT(3) must be 1 (Read As One) to indicate that rest of the + * PIDR1, PIDR2 DES_* fields follow JEDEC encoding for the designer. Use that + * as a match value for blanket matching all devices in the given CoreSight + * device type and architecture. + */ +#define PIDR2_JEDEC BIT(3) +#define PID_PIDR2_JEDEC (PIDR2_JEDEC << 16) +/* + * Match all PIDs in a given CoreSight device type and architecture, defined + * by the uci. + */ +#define CS_AMBA_MATCH_ALL_UCI(uci) \ + __CS_AMBA_UCI_ID(PID_PIDR2_JEDEC, PID_PIDR2_JEDEC, uci) + +/* extract the data value from a UCI structure given amba_id pointer. */ +static inline void *coresight_get_uci_data(const struct amba_id *id) +{ + struct amba_cs_uci_id *uci_id = id->data; + + if (!uci_id) + return NULL; + + return uci_id->data; +} + +static inline void *coresight_get_uci_data_from_amba(const struct amba_id *table, u32 pid) +{ + while (table->mask) { + if ((pid & table->mask) == table->id) + return coresight_get_uci_data(table); + table++; + }; + return NULL; +} + +int coresight_claim_device(struct gtrace_component *comp); +int coresight_claim_device_unlocked(struct gtrace_component *comp); +void coresight_clear_self_claim_tag(struct gtrace_platform_data *pdata); +void coresight_clear_self_claim_tag_unlocked(struct gtrace_platform_data *pdata); +void coresight_disclaim_device(struct gtrace_component *comp); +void coresight_disclaim_device_unlocked(struct gtrace_component *comp); + +struct gtrace_platform_data *coresight_get_platform_data(struct device *dev); +int coresight_get_enable_clocks(struct device *dev, struct clk **pclk, + struct clk **atclk); + +int coresight_init_driver_with_owner(const char *drv, struct amba_driver *amba_drv, + struct platform_driver *pdev_drv, struct module *owner, + const char *mod_name); +#define coresight_init_driver(drv, amba_drv, pdev_drv) \ + coresight_init_driver_with_owner(drv, amba_drv, pdev_drv, THIS_MODULE, KBUILD_MODNAME) + +void coresight_remove_driver(struct amba_driver *amba_drv, + struct platform_driver *pdev_drv); + +#endif /* __LINUX_CORESIGHT_H__ */ diff --git a/include/linux/gtrace.h b/include/linux/gtrace.h index ee60585ef65c..e9210b0a73b5 100644 --- a/include/linux/gtrace.h +++ b/include/linux/gtrace.h @@ -33,6 +33,13 @@ enum gtrace_component_type { GTRACE_RVTRACE_RAMSINK, GTRACE_RVTRACE_PIBSINK, GTRACE_RVTRACE_ATBBRIDGE, + GTRACE_CORESIGHT_FUNNEL, + GTRACE_CORESIGHT_REPLICATOR, + GTRACE_CORESIGHT_TMC_ETB, + GTRACE_CORESIGHT_TMC_ETR, + GTRACE_CORESIGHT_TMC_ETF, + GTRACE_CORESIGHT_ETB, + GTRACE_CORESIGHT_TPIU, GTRACE_COMPONENT_TYPE_MAX };