diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index f112c67d874b..4f277698b484 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -35,6 +35,7 @@ config CORESIGHT_LINK_AND_SINK_TMC tristate "Coresight generic TMC driver" depends on CORESIGHT_LINKS_AND_SINKS + select CORESIGHT_CSR help This enables support for the Trace Memory Controller driver. Depending on its configuration the device can act as a link (embedded @@ -226,6 +227,14 @@ config CORESIGHT_HWEVENT Hardware Event mux control registers to select hardware events based on user input. +config CORESIGHT_CSR + tristate "CoreSight Slave Register driver" + help + This driver provides support for CoreSight Slave Register block + that hosts miscellaneous configuration registers. + Those configuration registers can be used to control, various + coresight configurations. + config CORESIGHT_TRBE tristate "Trace Buffer Extension (TRBE) driver" depends on ARM64 && CORESIGHT_SOURCE_ETM4X diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 416e19d984f6..8533fa2d2f32 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -31,3 +31,4 @@ obj-$(CONFIG_CORESIGHT_DUMMY) += coresight-dummy.o obj-$(CONFIG_CORESIGHT_REMOTE_ETM) += coresight-remote-etm.o obj-$(CONFIG_CORESIGHT_TGU) += coresight-tgu.o obj-$(CONFIG_CORESIGHT_HWEVENT) += coresight-hwevent.o +obj-$(CONFIG_CORESIGHT_CSR) += coresight-csr.o diff --git a/drivers/hwtracing/coresight/coresight-common.h b/drivers/hwtracing/coresight/coresight-common.h new file mode 100644 index 000000000000..76159651667e --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-common.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. + */ + +#ifndef _CORESIGHT_COMMON_H +#define _CORESIGHT_COMMON_H + +#define BM(lsb, msb) ((BIT(msb) - BIT(lsb)) + BIT(msb)) +#define BVAL(val, n) ((val & BIT(n)) >> n) + +struct coresight_csr { + const char *name; + struct list_head link; +}; + +#if IS_ENABLED(CONFIG_CORESIGHT_CSR) +extern void msm_qdss_csr_enable_bam_to_usb(struct coresight_csr *csr); +extern void msm_qdss_csr_enable_flush(struct coresight_csr *csr); +extern void msm_qdss_csr_disable_bam_to_usb(struct coresight_csr *csr); +extern void msm_qdss_csr_disable_flush(struct coresight_csr *csr); +extern void msm_qdss_csr_enable_eth(struct coresight_csr *csr); +extern void msm_qdss_csr_disable_eth(struct coresight_csr *csr); +extern int coresight_csr_hwctrl_set(struct coresight_csr *csr, uint64_t addr, + uint32_t val); +extern void coresight_csr_set_byte_cntr(struct coresight_csr *csr, int irqctrl_offset, + uint32_t count); +extern struct coresight_csr *coresight_csr_get(const char *name); +extern int coresight_csr_set_etr_atid(struct coresight_csr *csr, + uint32_t atid_offset, uint32_t atid, bool enable); +#if IS_ENABLED(CONFIG_OF) +extern int of_get_coresight_csr_name(struct device_node *node, + const char **csr_name); +#else +static inline int of_get_coresight_csr_name(struct device_node *node, + const char **csr_name){ return -EINVAL; } +#endif + +#else +static inline void msm_qdss_csr_enable_bam_to_usb(struct coresight_csr *csr) {} +static inline void msm_qdss_csr_disable_bam_to_usb(struct coresight_csr *csr) {} +static inline void msm_qdss_csr_disable_flush(struct coresight_csr *csr) {} +static inline void msm_qdss_csr_enable_eth(struct coresight_csr *csr) {} +static inline void msm_qdss_csr_disable_eth(struct coresight_csr *csr) {} +static inline int coresight_csr_hwctrl_set(struct coresight_csr *csr, + uint64_t addr, uint32_t val) { return -EINVAL; } +static inline void coresight_csr_set_byte_cntr(struct coresight_csr *csr, int irqctrl_offset, + uint32_t count) {} +static inline struct coresight_csr *coresight_csr_get(const char *name) + { return NULL; } +static inline int coresight_csr_set_etr_atid(struct coresight_csr *csr, + uint32_t atid_offset, uint32_t atid, bool enable) + {return -EINVAL; } +static inline int of_get_coresight_csr_name(struct device_node *node, + const char **csr_name){ return -EINVAL; } +#endif +#if IS_ENABLED(CONFIG_CORESIGHT_CTI) && IS_ENABLED(CONFIG_OF) +extern struct coresight_cti_data *of_get_coresight_cti_data( + struct device *dev, struct device_node *node); +#else +static inline struct coresight_cti_data *of_get_coresight_cti_data( + struct device *dev, struct device_node *node) { return NULL; } +#endif +#endif diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index e41c84cffb81..96a83cf25bad 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -22,8 +22,11 @@ #include "coresight-etm-perf.h" #include "coresight-priv.h" +#include "coresight-common.h" #include "coresight-syscfg.h" +#define MAX_SINK_NAME 20 + static DEFINE_MUTEX(coresight_mutex); static DEFINE_PER_CPU(struct coresight_device *, csdev_sink); @@ -59,6 +62,8 @@ EXPORT_SYMBOL_GPL(coresight_barrier_pkt); static const struct cti_assoc_op *cti_assoc_ops; +static const struct csr_set_atid_op *csr_set_atid_ops; + void coresight_set_cti_ops(const struct cti_assoc_op *cti_op) { cti_assoc_ops = cti_op; @@ -83,6 +88,18 @@ struct coresight_device *coresight_get_percpu_sink(int cpu) } EXPORT_SYMBOL_GPL(coresight_get_percpu_sink); +void coresight_set_csr_ops(const struct csr_set_atid_op *csr_op) +{ + csr_set_atid_ops = csr_op; +} +EXPORT_SYMBOL(coresight_set_csr_ops); + +void coresight_remove_csr_ops(void) +{ + csr_set_atid_ops = NULL; +} +EXPORT_SYMBOL(coresight_remove_csr_ops); + static int coresight_id_match(struct device *dev, void *data) { int trace_id, i_trace_id; @@ -492,6 +509,42 @@ static bool coresight_disable_source(struct coresight_device *csdev) return !csdev->enable; } +static struct coresight_device *coresight_get_source(struct list_head *path) +{ + struct coresight_device *csdev; + + if (!path) + return NULL; + + csdev = list_first_entry(path, struct coresight_node, link)->csdev; + if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) + return NULL; + + return csdev; +} + +static int coresight_set_csr_atid(struct list_head *path, + struct coresight_device *sink_csdev, bool enable) +{ + int atid, ret = 0; + struct coresight_device *src_csdev; + + src_csdev = coresight_get_source(path); + if (!src_csdev) + ret = -EINVAL; + + atid = of_coresight_get_atid(src_csdev); + if (atid < 0) + ret = -EINVAL; + + if (csr_set_atid_ops) + ret = csr_set_atid_ops->set_atid(sink_csdev, atid, enable); + else + ret = -EINVAL; + + return ret; +} + /* * coresight_disable_path_from : Disable components in the given path beyond * @nd in the list. If @nd is NULL, all the components, except the SOURCE are @@ -523,6 +576,9 @@ static void coresight_disable_path_from(struct list_head *path, switch (type) { case CORESIGHT_DEV_TYPE_SINK: + if (csdev->type == CORESIGHT_DEV_TYPE_SINK) + coresight_set_csr_atid(path, csdev, false); + coresight_disable_sink(csdev); break; case CORESIGHT_DEV_TYPE_SOURCE: @@ -584,6 +640,13 @@ int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data) */ if (ret) goto out; + + if (csdev->type == CORESIGHT_DEV_TYPE_SINK) { + ret = coresight_set_csr_atid(path, csdev, true); + if (ret) + dev_dbg(&csdev->dev, "Set csr atid register fail%s\n"); + } + break; case CORESIGHT_DEV_TYPE_SOURCE: /* sources are enabled from either sysFS or Perf */ @@ -678,6 +741,52 @@ coresight_get_enabled_sink(struct coresight_device *source) return coresight_find_enabled_sink(source); } +static int coresight_enabled_sink(struct device *dev, const void *data) +{ + const bool *reset = data; + struct coresight_device *csdev = to_coresight_device(dev); + + if ((csdev->type == CORESIGHT_DEV_TYPE_SINK || + csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) && + csdev->activated) { + /* + * Now that we have a handle on the sink for this session, + * disable the sysFS "enable_sink" flag so that possible + * concurrent perf session that wish to use another sink don't + * trip on it. Doing so has no ramification for the current + * session. + */ + if (*reset) + csdev->activated = false; + + return 1; + } + + return 0; +} + +/** + * coresight_get_enabled_sink_from_bus - returns the first enabled sink found on the bus + * @deactivate: Whether the 'enable_sink' flag should be reset + * + * When operated from perf the deactivate parameter should be set to 'true'. + * That way the "enabled_sink" flag of the sink that was selected can be reset, + * allowing for other concurrent perf sessions to choose a different sink. + * + * When operated from sysFS users have full control and as such the deactivate + * parameter should be set to 'false', hence mandating users to explicitly + * clear the flag. + */ +static struct coresight_device *coresight_get_enabled_sink_from_bus(bool deactivate) +{ + struct device *dev = NULL; + + dev = bus_find_device(&coresight_bustype, NULL, &deactivate, + coresight_enabled_sink); + + return dev ? to_coresight_device(dev) : NULL; +} + static int coresight_sink_by_id(struct device *dev, const void *data) { struct coresight_device *csdev = to_coresight_device(dev); @@ -1157,7 +1266,12 @@ int coresight_enable(struct coresight_device *csdev) goto out; } - sink = coresight_get_enabled_sink(csdev); + if (csdev->def_sink) { + sink = csdev->def_sink; + sink->activated = true; + } else + sink = coresight_get_enabled_sink(csdev); + if (!sink) { ret = -EINVAL; goto out; @@ -1250,15 +1364,22 @@ static ssize_t enable_sink_store(struct device *dev, { int ret; unsigned long val; + struct coresight_device *sink; struct coresight_device *csdev = to_coresight_device(dev); ret = kstrtoul(buf, 10, &val); if (ret) return ret; - if (val) + if (val) { + sink = coresight_get_enabled_sink_from_bus(false); + if (sink && sink->type != csdev->type) { + dev_err(&csdev->dev, + "Another type sink is enabled.\n"); + return -EINVAL; + } csdev->activated = true; - else + } else csdev->activated = false; return size; @@ -1298,6 +1419,57 @@ static ssize_t enable_source_store(struct device *dev, } static DEVICE_ATTR_RW(enable_source); +static ssize_t sink_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct coresight_device *csdev = to_coresight_device(dev); + + if (csdev->def_sink) + return scnprintf(buf, PAGE_SIZE, "%s\n", dev_name(&csdev->def_sink->dev)); + else + return scnprintf(buf, PAGE_SIZE, "\n"); +} + +static ssize_t sink_name_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u32 hash; + char *sink_name; + struct coresight_device *new_sink, *current_sink; + struct coresight_device *csdev = to_coresight_device(dev); + + if (size >= MAX_SINK_NAME) + return -EINVAL; + + if (size == 0) { + csdev->def_sink = NULL; + return size; + } + + sink_name = kstrdup(buf, GFP_KERNEL); + sink_name[size-1] = 0; + + hash = hashlen_hash(hashlen_string(NULL, sink_name)); + new_sink = coresight_get_sink_by_id(hash); + current_sink = coresight_get_enabled_sink_from_bus(false); + + if (!new_sink || (current_sink && + new_sink && current_sink->type != + new_sink->type)) { + dev_err(&csdev->dev, + "Sink name is invalid or another type sink is enabled.\n"); + kfree(sink_name); + return -EINVAL; + } + + csdev->def_sink = new_sink; + kfree(sink_name); + + return size; +} +static DEVICE_ATTR_RW(sink_name); + static struct attribute *coresight_sink_attrs[] = { &dev_attr_enable_sink.attr, NULL, @@ -1306,6 +1478,7 @@ ATTRIBUTE_GROUPS(coresight_sink); static struct attribute *coresight_source_attrs[] = { &dev_attr_enable_source.attr, + &dev_attr_sink_name.attr, NULL, }; ATTRIBUTE_GROUPS(coresight_source); diff --git a/drivers/hwtracing/coresight/coresight-csr.c b/drivers/hwtracing/coresight/coresight-csr.c new file mode 100644 index 000000000000..6175af8fc274 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-csr.c @@ -0,0 +1,817 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2013, 2015-2017, 2019-2021 The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "coresight-priv.h" +#include "coresight-common.h" + +#define csr_writel(drvdata, val, off) __raw_writel((val), drvdata->base + off) +#define csr_readl(drvdata, off) __raw_readl(drvdata->base + off) + +#define CSR_LOCK(drvdata) \ +do { \ + mb(); /* ensure configuration take effect before we lock it */ \ + csr_writel(drvdata, 0x0, CORESIGHT_LAR); \ +} while (0) +#define CSR_UNLOCK(drvdata) \ +do { \ + csr_writel(drvdata, CORESIGHT_UNLOCK, CORESIGHT_LAR); \ + mb(); /* ensure unlock take effect before we configure */ \ +} while (0) + +#define CSR_SWDBGPWRCTRL (0x000) +#define CSR_SWDBGPWRACK (0x004) +#define CSR_SWSPADREG0 (0x008) +#define CSR_SWSPADREG1 (0x00C) +#define CSR_STMTRANSCTRL (0x010) +#define CSR_STMAWIDCTRL (0x014) +#define CSR_STMCHNOFST0 (0x018) +#define CSR_STMCHNOFST1 (0x01C) +#define CSR_STMEXTHWCTRL0 (0x020) +#define CSR_STMEXTHWCTRL1 (0x024) +#define CSR_STMEXTHWCTRL2 (0x028) +#define CSR_STMEXTHWCTRL3 (0x02C) +#define CSR_USBBAMCTRL (0x030) +#define CSR_USBFLSHCTRL (0x034) +#define CSR_TIMESTAMPCTRL (0x038) +#define CSR_AOTIMEVAL0 (0x03C) +#define CSR_AOTIMEVAL1 (0x040) +#define CSR_QDSSTIMEVAL0 (0x044) +#define CSR_QDSSTIMEVAL1 (0x048) +#define CSR_QDSSTIMELOAD0 (0x04C) +#define CSR_QDSSTIMELOAD1 (0x050) +#define CSR_DAPMSAVAL (0x054) +#define CSR_QDSSCLKVOTE (0x058) +#define CSR_QDSSCLKIPI (0x05C) +#define CSR_QDSSPWRREQIGNORE (0x060) +#define CSR_QDSSSPARE (0x064) +#define CSR_IPCAT (0x068) +#define CSR_BYTECNTVAL (0x06C) +#define CSR_ARADDR_EXT (0x130) +#define CSR_AWADDR_EXT (0x134) +#define MSR_NUM ((drvdata->msr_end - drvdata->msr_start + 1) \ + / sizeof(uint32_t)) +#define MSR_MAX_NUM 128 + +#define BLKSIZE_256 0 +#define BLKSIZE_512 1 +#define BLKSIZE_1024 2 +#define BLKSIZE_2048 3 + +#define FLUSHPERIOD_1 0x1 +#define FLUSHPERIOD_2048 0x800 +#define PERFLSHEOT_BIT BIT(18) + +#define CSR_ATID_REG_OFFSET(atid, atid_offset) \ + ((atid / 32) * 4 + atid_offset) + +#define CSR_ATID_REG_BIT(atid) (atid % 32) +#define CSR_MAX_ATID 128 +#define CSR_ATID_REG_SIZE 0xc + +#define CSR_ARADDR_EXT_VAL 0x104 +#define CSR_AWADDR_EXT_VAL 0x104 + +#define CSR_ATID_REG_OFFSET(atid, atid_offset) \ + ((atid / 32) * 4 + atid_offset) + +#define CSR_ATID_REG_BIT(atid) (atid % 32) +#define CSR_MAX_ATID 128 +#define CSR_ATID_REG_SIZE 0xc + +struct csr_drvdata { + void __iomem *base; + phys_addr_t pbase; + phys_addr_t msr_start; + phys_addr_t msr_end; + struct device *dev; + struct coresight_device *csdev; + uint32_t *msr; + atomic_t *msr_refcnt; + uint32_t blksize; + uint32_t flushperiod; + struct coresight_csr csr; + struct clk *clk; + spinlock_t spin_lock; + bool usb_bam_support; + bool perflsheot_set_support; + bool hwctrl_set_support; + bool set_byte_cntr_support; + bool timestamp_support; + bool enable_flush; + bool msr_support; +}; + +DEFINE_CORESIGHT_DEVLIST(csr_devs, "csr"); + +static LIST_HEAD(csr_list); +static DEFINE_MUTEX(csr_lock); + +#define to_csr_drvdata(c) container_of(c, struct csr_drvdata, csr) + +static void msm_qdss_csr_config_flush_period(struct csr_drvdata *drvdata) +{ + uint32_t usbflshctrl; + + CSR_UNLOCK(drvdata); + + usbflshctrl = csr_readl(drvdata, CSR_USBFLSHCTRL); + usbflshctrl = (usbflshctrl & ~0x3FFFC) | (drvdata->flushperiod << 2); + csr_writel(drvdata, usbflshctrl, CSR_USBFLSHCTRL); + + CSR_LOCK(drvdata); +} + +void msm_qdss_csr_enable_bam_to_usb(struct coresight_csr *csr) +{ + struct csr_drvdata *drvdata; + uint32_t usbbamctrl; + unsigned long flags; + + if (csr == NULL) + return; + + drvdata = to_csr_drvdata(csr); + if (IS_ERR_OR_NULL(drvdata) || !drvdata->usb_bam_support) + return; + + spin_lock_irqsave(&drvdata->spin_lock, flags); + CSR_UNLOCK(drvdata); + + usbbamctrl = csr_readl(drvdata, CSR_USBBAMCTRL); + usbbamctrl = (usbbamctrl & ~0x3) | drvdata->blksize; + csr_writel(drvdata, usbbamctrl, CSR_USBBAMCTRL); + + usbbamctrl |= 0x4; + csr_writel(drvdata, usbbamctrl, CSR_USBBAMCTRL); + + CSR_LOCK(drvdata); + spin_unlock_irqrestore(&drvdata->spin_lock, flags); +} +EXPORT_SYMBOL(msm_qdss_csr_enable_bam_to_usb); + +void msm_qdss_csr_enable_flush(struct coresight_csr *csr) +{ + struct csr_drvdata *drvdata; + uint32_t usbflshctrl; + unsigned long flags; + + if (csr == NULL) + return; + + drvdata = to_csr_drvdata(csr); + if (IS_ERR_OR_NULL(drvdata) || !drvdata->usb_bam_support) + return; + + spin_lock_irqsave(&drvdata->spin_lock, flags); + + msm_qdss_csr_config_flush_period(drvdata); + + CSR_UNLOCK(drvdata); + + usbflshctrl = csr_readl(drvdata, CSR_USBFLSHCTRL); + usbflshctrl |= 0x2; + if (drvdata->perflsheot_set_support) + usbflshctrl |= PERFLSHEOT_BIT; + csr_writel(drvdata, usbflshctrl, CSR_USBFLSHCTRL); + + CSR_LOCK(drvdata); + drvdata->enable_flush = true; + spin_unlock_irqrestore(&drvdata->spin_lock, flags); +} +EXPORT_SYMBOL(msm_qdss_csr_enable_flush); + + +void msm_qdss_csr_disable_bam_to_usb(struct coresight_csr *csr) +{ + struct csr_drvdata *drvdata; + uint32_t usbbamctrl; + unsigned long flags; + + if (csr == NULL) + return; + + drvdata = to_csr_drvdata(csr); + if (IS_ERR_OR_NULL(drvdata) || !drvdata->usb_bam_support) + return; + + spin_lock_irqsave(&drvdata->spin_lock, flags); + CSR_UNLOCK(drvdata); + + usbbamctrl = csr_readl(drvdata, CSR_USBBAMCTRL); + usbbamctrl &= (~0x4); + csr_writel(drvdata, usbbamctrl, CSR_USBBAMCTRL); + + CSR_LOCK(drvdata); + spin_unlock_irqrestore(&drvdata->spin_lock, flags); +} +EXPORT_SYMBOL(msm_qdss_csr_disable_bam_to_usb); + +void msm_qdss_csr_disable_flush(struct coresight_csr *csr) +{ + struct csr_drvdata *drvdata; + uint32_t usbflshctrl; + unsigned long flags; + + if (csr == NULL) + return; + + drvdata = to_csr_drvdata(csr); + if (IS_ERR_OR_NULL(drvdata) || !drvdata->usb_bam_support) + return; + + spin_lock_irqsave(&drvdata->spin_lock, flags); + CSR_UNLOCK(drvdata); + + usbflshctrl = csr_readl(drvdata, CSR_USBFLSHCTRL); + usbflshctrl &= ~0x2; + if (drvdata->perflsheot_set_support) + usbflshctrl &= ~PERFLSHEOT_BIT; + csr_writel(drvdata, usbflshctrl, CSR_USBFLSHCTRL); + + CSR_LOCK(drvdata); + drvdata->enable_flush = false; + spin_unlock_irqrestore(&drvdata->spin_lock, flags); +} +EXPORT_SYMBOL(msm_qdss_csr_disable_flush); + +void msm_qdss_csr_enable_eth(struct coresight_csr *csr) +{ + struct csr_drvdata *drvdata; + unsigned long flags; + + if (csr == NULL) + return; + + drvdata = to_csr_drvdata(csr); + if (IS_ERR_OR_NULL(drvdata)) + return; + + spin_lock_irqsave(&drvdata->spin_lock, flags); + CSR_UNLOCK(drvdata); + csr_writel(drvdata, CSR_ARADDR_EXT_VAL, CSR_ARADDR_EXT); + csr_writel(drvdata, CSR_AWADDR_EXT_VAL, CSR_AWADDR_EXT); + CSR_LOCK(drvdata); + spin_unlock_irqrestore(&drvdata->spin_lock, flags); +} +EXPORT_SYMBOL(msm_qdss_csr_enable_eth); + +void msm_qdss_csr_disable_eth(struct coresight_csr *csr) +{ + struct csr_drvdata *drvdata; + unsigned long flags; + + if (csr == NULL) + return; + + drvdata = to_csr_drvdata(csr); + if (IS_ERR_OR_NULL(drvdata)) + return; + + spin_lock_irqsave(&drvdata->spin_lock, flags); + CSR_UNLOCK(drvdata); + csr_writel(drvdata, 0, CSR_ARADDR_EXT); + csr_writel(drvdata, 0, CSR_AWADDR_EXT); + CSR_LOCK(drvdata); + spin_unlock_irqrestore(&drvdata->spin_lock, flags); +} +EXPORT_SYMBOL(msm_qdss_csr_disable_eth); + +int coresight_csr_hwctrl_set(struct coresight_csr *csr, uint64_t addr, + uint32_t val) +{ + struct csr_drvdata *drvdata; + int ret = 0; + unsigned long flags; + + if (csr == NULL) + return -EINVAL; + + drvdata = to_csr_drvdata(csr); + if (IS_ERR_OR_NULL(drvdata) || !drvdata->hwctrl_set_support) + return -EINVAL; + + spin_lock_irqsave(&drvdata->spin_lock, flags); + CSR_UNLOCK(drvdata); + + if (addr == (drvdata->pbase + CSR_STMEXTHWCTRL0)) + csr_writel(drvdata, val, CSR_STMEXTHWCTRL0); + else if (addr == (drvdata->pbase + CSR_STMEXTHWCTRL1)) + csr_writel(drvdata, val, CSR_STMEXTHWCTRL1); + else if (addr == (drvdata->pbase + CSR_STMEXTHWCTRL2)) + csr_writel(drvdata, val, CSR_STMEXTHWCTRL2); + else if (addr == (drvdata->pbase + CSR_STMEXTHWCTRL3)) + csr_writel(drvdata, val, CSR_STMEXTHWCTRL3); + else + ret = -EINVAL; + + CSR_LOCK(drvdata); + spin_unlock_irqrestore(&drvdata->spin_lock, flags); + return ret; +} +EXPORT_SYMBOL(coresight_csr_hwctrl_set); + +void coresight_csr_set_byte_cntr(struct coresight_csr *csr, int irqctrl_offset, uint32_t count) +{ + struct csr_drvdata *drvdata; + unsigned long flags; + + if (csr == NULL) + return; + + drvdata = to_csr_drvdata(csr); + if (IS_ERR_OR_NULL(drvdata) || !drvdata->set_byte_cntr_support) + return; + + spin_lock_irqsave(&drvdata->spin_lock, flags); + CSR_UNLOCK(drvdata); + + csr_writel(drvdata, count, irqctrl_offset); + + /* make sure byte count value is written */ + mb(); + + CSR_LOCK(drvdata); + spin_unlock_irqrestore(&drvdata->spin_lock, flags); +} +EXPORT_SYMBOL(coresight_csr_set_byte_cntr); + +int coresight_csr_set_etr_atid(struct coresight_csr *csr, + uint32_t atid_offset, uint32_t atid, + bool enable) +{ + struct csr_drvdata *drvdata; + unsigned long flags; + uint32_t reg_offset; + int bit; + uint32_t val; + + if (csr == NULL) + return -EINVAL; + + drvdata = to_csr_drvdata(csr); + if (IS_ERR_OR_NULL(drvdata)) + return -EINVAL; + + if (atid < 0 || atid_offset <= 0) + return -EINVAL; + + spin_lock_irqsave(&drvdata->spin_lock, flags); + CSR_UNLOCK(drvdata); + + reg_offset = CSR_ATID_REG_OFFSET(atid, atid_offset); + bit = CSR_ATID_REG_BIT(atid); + if (reg_offset - atid_offset > CSR_ATID_REG_SIZE + || bit >= CSR_MAX_ATID) { + CSR_LOCK(drvdata); + spin_unlock_irqrestore(&drvdata->spin_lock, flags); + return -EINVAL; + } + + val = csr_readl(drvdata, reg_offset); + if (enable) + val = val | BIT(bit); + else + val = val & ~BIT(bit); + csr_writel(drvdata, val, reg_offset); + + CSR_LOCK(drvdata); + spin_unlock_irqrestore(&drvdata->spin_lock, flags); + + return 0; +} +EXPORT_SYMBOL(coresight_csr_set_etr_atid); + +struct coresight_csr *coresight_csr_get(const char *name) +{ + struct coresight_csr *csr; + + mutex_lock(&csr_lock); + list_for_each_entry(csr, &csr_list, link) { + if (!strcmp(csr->name, name)) { + mutex_unlock(&csr_lock); + return csr; + } + } + + mutex_unlock(&csr_lock); + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL(coresight_csr_get); + +int of_get_coresight_csr_name(struct device_node *node, const char **csr_name) +{ + int ret; + struct device_node *csr_node; + + csr_node = of_parse_phandle(node, "coresight-csr", 0); + if (!csr_node) + return -EINVAL; + + ret = of_property_read_string(csr_node, "coresight-name", csr_name); + of_node_put(csr_node); + return ret; +} +EXPORT_SYMBOL(of_get_coresight_csr_name); + +static ssize_t timestamp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t size = 0; + uint64_t time_tick = 0; + uint32_t val, time_val0, time_val1; + int ret; + unsigned long flags; + + struct csr_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (IS_ERR_OR_NULL(drvdata) || !drvdata->timestamp_support) { + dev_err(dev, "Invalid param\n"); + return 0; + } + + ret = clk_prepare_enable(drvdata->clk); + if (ret) + return ret; + + spin_lock_irqsave(&drvdata->spin_lock, flags); + CSR_UNLOCK(drvdata); + + val = csr_readl(drvdata, CSR_TIMESTAMPCTRL); + + val = val & ~BIT(0); + csr_writel(drvdata, val, CSR_TIMESTAMPCTRL); + + val = val | BIT(0); + csr_writel(drvdata, val, CSR_TIMESTAMPCTRL); + + time_val0 = csr_readl(drvdata, CSR_QDSSTIMEVAL0); + time_val1 = csr_readl(drvdata, CSR_QDSSTIMEVAL1); + + CSR_LOCK(drvdata); + spin_unlock_irqrestore(&drvdata->spin_lock, flags); + + clk_disable_unprepare(drvdata->clk); + + time_tick |= (uint64_t)time_val1 << 32; + time_tick |= (uint64_t)time_val0; + size = scnprintf(buf, PAGE_SIZE, "%llu\n", time_tick); + dev_dbg(dev, "timestamp : %s\n", buf); + return size; +} + +static DEVICE_ATTR_RO(timestamp); + +static ssize_t msr_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int i; + ssize_t len = 0; + struct csr_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (IS_ERR_OR_NULL(drvdata) || !drvdata->msr_support || + IS_ERR_OR_NULL(drvdata->msr)) + return -EINVAL; + for (i = 0; i < MSR_NUM; i++) + len += scnprintf(buf + len, PAGE_SIZE - len, "%d 0x%x\n", + i, drvdata->msr[i]); + return len; +} + +static ssize_t msr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t size) +{ + uint32_t offset, val, rval; + int nval, ret; + unsigned long flags; + struct csr_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (IS_ERR_OR_NULL(drvdata) || !drvdata->msr_support || + IS_ERR_OR_NULL(drvdata->msr)) + return -EINVAL; + + nval = sscanf(buf, "%x %x", &offset, &val); + if (nval != 2) + return -EINVAL; + if (offset >= MSR_NUM) + return -EINVAL; + + if (atomic_read(drvdata->msr_refcnt) == 0) { + ret = clk_prepare_enable(drvdata->clk); + if (ret) + return ret; + atomic_inc(drvdata->msr_refcnt); + } + + spin_lock_irqsave(&drvdata->spin_lock, flags); + CSR_UNLOCK(drvdata); + csr_writel(drvdata, val, drvdata->msr_start + offset * 4); + rval = csr_readl(drvdata, drvdata->msr_start + offset * 4); + drvdata->msr[offset] = rval; + CSR_LOCK(drvdata); + spin_unlock_irqrestore(&drvdata->spin_lock, flags); + return size; +} + +static DEVICE_ATTR_RW(msr); + +static ssize_t msr_reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t size) +{ + unsigned long flags, val; + int i; + struct csr_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (IS_ERR_OR_NULL(drvdata) || !drvdata->msr_support || + IS_ERR_OR_NULL(drvdata->msr)) + return -EINVAL; + + if (kstrtoul(buf, 0, &val) || val != 1) + return -EINVAL; + + if (atomic_read(drvdata->msr_refcnt) == 0) + return -EINVAL; + + atomic_set(drvdata->msr_refcnt, 0); + spin_lock_irqsave(&drvdata->spin_lock, flags); + CSR_UNLOCK(drvdata); + for (i = 0; i < MSR_NUM; i++) { + csr_writel(drvdata, 0, drvdata->msr_start + i * 4); + drvdata->msr[i] = 0; + } + CSR_LOCK(drvdata); + spin_unlock_irqrestore(&drvdata->spin_lock, flags); + clk_disable_unprepare(drvdata->clk); + return size; +} + +static DEVICE_ATTR_WO(msr_reset); + +static ssize_t flushperiod_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct csr_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (IS_ERR_OR_NULL(drvdata) || !drvdata->usb_bam_support) { + dev_err(dev, "Invalid param\n"); + return -EINVAL; + } + + return scnprintf(buf, PAGE_SIZE, "%#lx\n", drvdata->flushperiod); +} + +static ssize_t flushperiod_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t size) +{ + unsigned long flags; + unsigned long val; + struct csr_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (IS_ERR_OR_NULL(drvdata) || !drvdata->usb_bam_support) { + dev_err(dev, "Invalid param\n"); + return -EINVAL; + } + + spin_lock_irqsave(&drvdata->spin_lock, flags); + + if (kstrtoul(buf, 0, &val) || val > 0xffff) { + spin_unlock_irqrestore(&drvdata->spin_lock, flags); + return -EINVAL; + } + + if (drvdata->flushperiod == val) + goto out; + + drvdata->flushperiod = val; + + if (drvdata->enable_flush) + msm_qdss_csr_config_flush_period(drvdata); + +out: + spin_unlock_irqrestore(&drvdata->spin_lock, flags); + return size; +} + +static DEVICE_ATTR_RW(flushperiod); + +static struct attribute *swao_csr_attrs[] = { + &dev_attr_timestamp.attr, + &dev_attr_msr.attr, + &dev_attr_msr_reset.attr, + NULL, +}; + +static struct attribute_group swao_csr_attr_grp = { + .attrs = swao_csr_attrs, +}; + +static const struct attribute_group *swao_csr_attr_grps[] = { + &swao_csr_attr_grp, + NULL, +}; + +static struct attribute *csr_attrs[] = { + &dev_attr_flushperiod.attr, + NULL, +}; + +static struct attribute_group csr_attr_grp = { + .attrs = csr_attrs, +}; + +static const struct attribute_group *csr_attr_grps[] = { + &csr_attr_grp, + NULL, +}; + +static int csr_probe(struct platform_device *pdev) +{ + int ret; + struct device *dev = &pdev->dev; + struct coresight_platform_data *pdata; + struct csr_drvdata *drvdata; + struct resource *res, *msr_res; + struct coresight_desc desc = { 0 }; + + desc.name = coresight_alloc_device_name(&csr_devs, dev); + if (!desc.name) + return -ENOMEM; + pdata = coresight_get_platform_data(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + pdev->dev.platform_data = pdata; + + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + drvdata->dev = &pdev->dev; + platform_set_drvdata(pdev, drvdata); + + drvdata->clk = devm_clk_get(dev, "apb_pclk"); + if (IS_ERR(drvdata->clk)) + dev_dbg(dev, "csr not config clk\n"); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr-base"); + if (!res) + return -ENODEV; + drvdata->pbase = res->start; + + drvdata->base = devm_ioremap(dev, res->start, resource_size(res)); + if (!drvdata->base) + return -ENOMEM; + + ret = of_property_read_u32(pdev->dev.of_node, "qcom,blk-size", + &drvdata->blksize); + if (ret) + drvdata->blksize = BLKSIZE_256; + + drvdata->usb_bam_support = of_property_read_bool(pdev->dev.of_node, + "qcom,usb-bam-support"); + if (!drvdata->usb_bam_support) + dev_dbg(dev, "usb_bam support handled by other subsystem\n"); + else + dev_dbg(dev, "usb_bam operation supported\n"); + + drvdata->hwctrl_set_support = of_property_read_bool(pdev->dev.of_node, + "qcom,hwctrl-set-support"); + if (!drvdata->hwctrl_set_support) + dev_dbg(dev, "hwctrl_set_support handled by other subsystem\n"); + else + dev_dbg(dev, "hwctrl_set_support operation supported\n"); + + drvdata->set_byte_cntr_support = of_property_read_bool( + pdev->dev.of_node, "qcom,set-byte-cntr-support"); + if (!drvdata->set_byte_cntr_support) + dev_dbg(dev, "set byte_cntr_support handled by other subsystem\n"); + else + dev_dbg(dev, "set_byte_cntr_support operation supported\n"); + + drvdata->timestamp_support = of_property_read_bool(pdev->dev.of_node, + "qcom,timestamp-support"); + if (!drvdata->timestamp_support) + dev_dbg(dev, "timestamp_support handled by other subsystem\n"); + else + dev_dbg(dev, "timestamp_support operation supported\n"); + + drvdata->perflsheot_set_support = of_property_read_bool( + pdev->dev.of_node, "qcom,perflsheot-set-support"); + if (!drvdata->perflsheot_set_support) + dev_dbg(dev, "perflsheot_set_support handled by other subsystem\n"); + else + dev_dbg(dev, "perflsheot_set_support operation supported\n"); + + if (drvdata->usb_bam_support) + drvdata->flushperiod = FLUSHPERIOD_1; + drvdata->msr_support = of_property_read_bool(pdev->dev.of_node, + "qcom,msr-support"); + if (!drvdata->msr_support) { + dev_dbg(dev, "msr_support handled by other subsystem\n"); + } else { + msr_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "msr-base"); + if (!msr_res || msr_res->start < res->start || msr_res->end + > res->end) + return -ENODEV; + drvdata->msr_start = msr_res->start - res->start; + drvdata->msr_end = msr_res->end - res->start; + drvdata->msr = devm_kzalloc(dev, MSR_NUM * sizeof(uint32_t), + GFP_KERNEL); + if (!drvdata->msr) + return -ENOMEM; + + drvdata->msr_refcnt = devm_kzalloc(dev, sizeof(atomic_t), + GFP_KERNEL); + if (!drvdata->msr_refcnt) + return -ENOMEM; + atomic_set(drvdata->msr_refcnt, 0); + dev_dbg(dev, "msr_support operation supported\n"); + } + + desc.type = CORESIGHT_DEV_TYPE_HELPER; + desc.pdata = pdev->dev.platform_data; + desc.dev = &pdev->dev; + if (drvdata->timestamp_support || drvdata->msr_support) + desc.groups = swao_csr_attr_grps; + else if (drvdata->usb_bam_support) + desc.groups = csr_attr_grps; + + drvdata->csdev = coresight_register(&desc); + if (IS_ERR(drvdata->csdev)) + return PTR_ERR(drvdata->csdev); + + /* Store the driver data pointer for use in exported functions */ + spin_lock_init(&drvdata->spin_lock); + drvdata->csr.name = desc.name; + + mutex_lock(&csr_lock); + list_add_tail(&drvdata->csr.link, &csr_list); + mutex_unlock(&csr_lock); + + dev_info(dev, "CSR initialized: %s\n", drvdata->csr.name); + return 0; +} + +static int csr_remove(struct platform_device *pdev) +{ + struct csr_drvdata *drvdata = platform_get_drvdata(pdev); + + mutex_lock(&csr_lock); + list_del(&drvdata->csr.link); + mutex_unlock(&csr_lock); + + coresight_unregister(drvdata->csdev); + return 0; +} + +static const struct of_device_id csr_match[] = { + {.compatible = "qcom,coresight-csr"}, + {} +}; + +static struct platform_driver csr_driver = { + .probe = csr_probe, + .remove = csr_remove, + .driver = { + .name = "coresight-csr", + .of_match_table = csr_match, + .suppress_bind_attrs = true, + }, +}; + +static int __init csr_init(void) +{ + return platform_driver_register(&csr_driver); +} +module_init(csr_init); + +static void __exit csr_exit(void) +{ + platform_driver_unregister(&csr_driver); +} +module_exit(csr_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CoreSight CSR driver"); diff --git a/drivers/hwtracing/coresight/coresight-platform.c b/drivers/hwtracing/coresight/coresight-platform.c index 475899714104..b24de3445636 100644 --- a/drivers/hwtracing/coresight/coresight-platform.c +++ b/drivers/hwtracing/coresight/coresight-platform.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2012, The Linux Foundation. All rights reserved. + * Copyright (c) 2012, 2021 The Linux Foundation. All rights reserved. */ #include @@ -204,6 +204,16 @@ static int of_coresight_get_cpu(struct device *dev) return cpu; } +int of_coresight_get_atid(struct coresight_device *csdev) +{ + int atid, ret = 0; + + ret = of_property_read_u32(csdev->dev.parent->of_node, "atid", &atid); + if (ret) + return ret; + return atid; +} + /* * of_coresight_parse_endpoint : Parse the given output endpoint @ep * and fill the connection information in @conn diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index ff1dd2092ac5..bc4404078abc 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2012, 2021 The Linux Foundation. All rights reserved. */ #ifndef _CORESIGHT_PRIV_H @@ -183,6 +183,14 @@ struct cti_assoc_op { extern void coresight_set_cti_ops(const struct cti_assoc_op *cti_op); extern void coresight_remove_cti_ops(void); +struct csr_set_atid_op { + int (*set_atid)(struct coresight_device *csdev, u32 atid, bool enable); +}; + +extern void coresight_set_csr_ops(const struct csr_set_atid_op *csr_op); +extern void coresight_remove_csr_ops(void); +int of_coresight_get_atid(struct coresight_device *src_dev); + /* * Macros and inline functions to handle CoreSight UCI data and driver * private data in AMBA ID table entries, and extract data values. diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c index d0276af82494..9cb87a1ca476 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-core.c +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -/* Copyright (c) 2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012, 2021 The Linux Foundation. All rights reserved. * * Description: CoreSight Trace Memory Controller driver */ @@ -26,6 +26,7 @@ #include "coresight-priv.h" #include "coresight-tmc.h" +#include "coresight-common.h" DEFINE_CORESIGHT_DEVLIST(etb_devs, "tmc_etb"); DEFINE_CORESIGHT_DEVLIST(etf_devs, "tmc_etf"); @@ -491,6 +492,17 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) drvdata->size = readl_relaxed(drvdata->base + TMC_RSZ) * 4; } + ret = of_get_coresight_csr_name(adev->dev.of_node, &drvdata->csr_name); + if (ret) + dev_dbg(dev, "No csr data\n"); + else { + drvdata->csr = coresight_csr_get(drvdata->csr_name); + if (IS_ERR(drvdata->csr)) { + dev_dbg(dev, "failed to get csr, defer probe\n"); + return -EPROBE_DEFER; + } + } + desc.dev = dev; desc.groups = coresight_tmc_groups; @@ -512,6 +524,10 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) idr_init(&drvdata->idr); mutex_init(&drvdata->idr_mutex); dev_list = &etr_devs; + + if (!of_property_read_u32(dev->of_node, "csr-atid-offset", + &drvdata->atid_offset)) + coresight_set_csr_ops(&csr_atid_ops); break; case TMC_CONFIG_TYPE_ETF: desc.type = CORESIGHT_DEV_TYPE_LINKSINK; @@ -589,6 +605,8 @@ static void tmc_remove(struct amba_device *adev) * etb fops in this case, device is there until last file * handler to this device is closed. */ + + coresight_remove_csr_ops(); misc_deregister(&drvdata->miscdev); coresight_unregister(drvdata->csdev); } diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 867ad8bb9b0c..90c69e4df4fd 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -18,6 +18,7 @@ #include "coresight-etm-perf.h" #include "coresight-priv.h" #include "coresight-tmc.h" +#include "coresight-common.h" struct etr_flat_buf { struct device *dev; @@ -679,6 +680,18 @@ static ssize_t tmc_etr_get_data_flat_buf(struct etr_buf *etr_buf, return len; } +static int tmc_etr_set_atid(struct coresight_device *csdev, u32 atid, bool enable) +{ + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + return coresight_csr_set_etr_atid(drvdata->csr, drvdata->atid_offset, + atid, enable); +} + +const struct csr_set_atid_op csr_atid_ops = { + .set_atid = tmc_etr_set_atid, +}; + static const struct etr_buf_operations etr_flat_buf_ops = { .alloc = tmc_etr_alloc_flat_buf, .free = tmc_etr_free_flat_buf, diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 6bec20a392b3..fe291513f1b8 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -12,6 +12,7 @@ #include #include #include +#include "coresight-priv.h" #define TMC_RSZ 0x004 #define TMC_STS 0x00c @@ -211,6 +212,9 @@ struct tmc_drvdata { struct mutex idr_mutex; struct etr_buf *sysfs_buf; struct etr_buf *perf_buf; + struct coresight_csr *csr; + const char *csr_name; + u32 atid_offset; }; struct etr_buf_operations { @@ -274,6 +278,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata); int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata); void tmc_etr_disable_hw(struct tmc_drvdata *drvdata); extern const struct coresight_ops tmc_etr_cs_ops; +extern const struct csr_set_atid_op csr_atid_ops; ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata, loff_t pos, size_t len, char **bufpp);