From 52814abf26659b3f3178410fcb0e2222152b4f8a Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 19 Jan 2016 17:14:29 +0200 Subject: [PATCH 001/104] perf: Synchronously free aux pages in case of allocation failure We are currently using asynchronous deallocation in the error path in AUX mmap code, which is unnecessary and also presents a problem for users that wish to probe for the biggest possible buffer size they can get: they'll get -EINVAL on all subsequent attemts to allocate a smaller buffer before the asynchronous deallocation callback frees up the pages from the previous unsuccessful attempt. Currently, gdb does that for allocating AUX buffers for Intel PT traces. More specifically, overwrite mode of AUX pmus that don't support hardware sg (some implementations of Intel PT, for instance) is limited to only one contiguous high order allocation for its buffer and there is no way of knowing its size without trying. This patch changes error path freeing to be synchronous as there won't be any contenders for the AUX pages at that point. Reported-by: Markus Metzger Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Jiri Olsa Cc: Linus Torvalds Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Cc: vince@deater.net Link: http://lkml.kernel.org/r/1453216469-9509-1-git-send-email-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar (cherry picked from commit 45c815f06b80031659c63d7b93e580015d6024dd) --- kernel/events/ring_buffer.c | 40 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index adfdc0536117..1faad2cfdb9e 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -459,6 +459,25 @@ static void rb_free_aux_page(struct ring_buffer *rb, int idx) __free_page(page); } +static void __rb_free_aux(struct ring_buffer *rb) +{ + int pg; + + if (rb->aux_priv) { + rb->free_aux(rb->aux_priv); + rb->free_aux = NULL; + rb->aux_priv = NULL; + } + + if (rb->aux_nr_pages) { + for (pg = 0; pg < rb->aux_nr_pages; pg++) + rb_free_aux_page(rb, pg); + + kfree(rb->aux_pages); + rb->aux_nr_pages = 0; + } +} + int rb_alloc_aux(struct ring_buffer *rb, struct perf_event *event, pgoff_t pgoff, int nr_pages, long watermark, int flags) { @@ -547,30 +566,11 @@ int rb_alloc_aux(struct ring_buffer *rb, struct perf_event *event, if (!ret) rb->aux_pgoff = pgoff; else - rb_free_aux(rb); + __rb_free_aux(rb); return ret; } -static void __rb_free_aux(struct ring_buffer *rb) -{ - int pg; - - if (rb->aux_priv) { - rb->free_aux(rb->aux_priv); - rb->free_aux = NULL; - rb->aux_priv = NULL; - } - - if (rb->aux_nr_pages) { - for (pg = 0; pg < rb->aux_nr_pages; pg++) - rb_free_aux_page(rb, pg); - - kfree(rb->aux_pages); - rb->aux_nr_pages = 0; - } -} - void rb_free_aux(struct ring_buffer *rb) { if (atomic_dec_and_test(&rb->aux_refcount)) From 33bb0224c0a4f7e518dc7462ff7440fbc35ded67 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 22 Dec 2015 17:25:15 +0200 Subject: [PATCH 002/104] stm class: Hide STM-specific options if STM is disabled If STM=n, it doesn't make sense to ask about STM_DUMMY and STM_SOURCE_CONSOLE support, which are not even built when enabled anyway. Hence hide these options if STM=n. Reported-by: Linus Torvalds Signed-off-by: Geert Uytterhoeven Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 4a2e2b19f96acfc037a9773c7729d133ce1e7e3b) --- drivers/hwtracing/stm/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/hwtracing/stm/Kconfig b/drivers/hwtracing/stm/Kconfig index 83e9f591a54b..4c13762f2b4d 100644 --- a/drivers/hwtracing/stm/Kconfig +++ b/drivers/hwtracing/stm/Kconfig @@ -8,6 +8,8 @@ config STM Say Y here to enable System Trace Module device support. +if STM + config STM_DUMMY tristate "Dummy STM driver" help @@ -24,3 +26,5 @@ config STM_SOURCE_CONSOLE If you want to send kernel console messages over STM devices, say Y. + +endif From 51d81f32321e52023849e73a7ea777f89757e6f3 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 22 Dec 2015 17:25:17 +0200 Subject: [PATCH 003/104] stm class: Select CONFIG_SRCU The newly added STM code uses SRCU, but does not ensure that this code is part of the kernel: drivers/built-in.o: In function `stm_source_link_show': include/linux/srcu.h:221: undefined reference to `__srcu_read_lock' include/linux/srcu.h:238: undefined reference to `__srcu_read_unlock' drivers/built-in.o: In function `stm_source_link_drop': include/linux/srcu.h:221: undefined reference to `__srcu_read_lock' include/linux/srcu.h:238: undefined reference to `__srcu_read_unlock' This adds a Kconfig 'select' statement like all the other SRCU using drivers have. Signed-off-by: Arnd Bergmann Fixes: 7bd1d4093c2f ("stm class: Introduce an abstraction for System Trace Module devices") Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 042d4460b5b4379a12f375045ff9065cf6758735) --- drivers/hwtracing/stm/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwtracing/stm/Kconfig b/drivers/hwtracing/stm/Kconfig index 4c13762f2b4d..e0ac75395526 100644 --- a/drivers/hwtracing/stm/Kconfig +++ b/drivers/hwtracing/stm/Kconfig @@ -1,6 +1,7 @@ config STM tristate "System Trace Module devices" select CONFIGFS_FS + select SRCU help A System Trace Module (STM) is a device exporting data in System Trace Protocol (STP) format as defined by MIPI STP standards. From df313b9ca8b14575387fbe53e5609caacde3e243 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 22 Dec 2015 17:25:18 +0200 Subject: [PATCH 004/104] stm class: Fix locking in unbinding policy path Right now, if stm device removal has to unbind from a policy (that is, an stm device that has STP policy, gets removed), it will trigger a nested lock on the stm device's policy mutex. This patch fixes the problem by moving the locking from the policy unbinding to policy removal (configfs path), where it's actually needed; the other caller of the policy unbinding function already takes the mutex around the call. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 4c127fd16e6b33ecb7badc091480c84ea9aebeb6) --- drivers/hwtracing/stm/policy.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index 11ab6d01adf6..94d3abfb737a 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -272,13 +272,17 @@ void stp_policy_unbind(struct stp_policy *policy) { struct stm_device *stm = policy->stm; + /* + * stp_policy_release() will not call here if the policy is already + * unbound; other users should not either, as no link exists between + * this policy and anything else in that case + */ if (WARN_ON_ONCE(!policy->stm)) return; - mutex_lock(&stm->policy_mutex); - stm->policy = NULL; - mutex_unlock(&stm->policy_mutex); + lockdep_assert_held(&stm->policy_mutex); + stm->policy = NULL; policy->stm = NULL; stm_put_device(stm); @@ -287,8 +291,16 @@ void stp_policy_unbind(struct stp_policy *policy) static void stp_policy_release(struct config_item *item) { struct stp_policy *policy = to_stp_policy(item); + struct stm_device *stm = policy->stm; + /* a policy *can* be unbound and still exist in configfs tree */ + if (!stm) + return; + + mutex_lock(&stm->policy_mutex); stp_policy_unbind(policy); + mutex_unlock(&stm->policy_mutex); + kfree(policy); } From 0f049c5ab8a7bc839647702fd29c63177f9e7c6b Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 22 Dec 2015 17:25:19 +0200 Subject: [PATCH 005/104] stm class: Fix link list locking Currently, the list of stm_sources linked to an stm device is protected by a spinlock, which also means that sources' .unlink() method is called under this spinlock. However, this method may (and does) sleep, which means trouble. This patch slightly reworks locking around stm::link_list so that bits that might_sleep() are called with a mutex held instead. Modification of this list requires both mutex and spinlock to be held, while looking at the list can be done under either mutex or spinlock. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit c74f7e8281add80bdfa0ad2998b8df287b13df73) --- drivers/hwtracing/stm/core.c | 38 +++++++++++++++++++++++++++--------- drivers/hwtracing/stm/stm.h | 1 + 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index b6445d9e5453..ddcb606acea6 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -641,6 +641,7 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data, if (err) goto err_device; + mutex_init(&stm->link_mutex); spin_lock_init(&stm->link_lock); INIT_LIST_HEAD(&stm->link_list); @@ -671,11 +672,11 @@ void stm_unregister_device(struct stm_data *stm_data) struct stm_source_device *src, *iter; int i; - spin_lock(&stm->link_lock); + mutex_lock(&stm->link_mutex); list_for_each_entry_safe(src, iter, &stm->link_list, link_entry) { __stm_source_link_drop(src, stm); } - spin_unlock(&stm->link_lock); + mutex_unlock(&stm->link_mutex); synchronize_srcu(&stm_source_srcu); @@ -694,6 +695,17 @@ void stm_unregister_device(struct stm_data *stm_data) } EXPORT_SYMBOL_GPL(stm_unregister_device); +/* + * stm::link_list access serialization uses a spinlock and a mutex; holding + * either of them guarantees that the list is stable; modification requires + * holding both of them. + * + * Lock ordering is as follows: + * stm::link_mutex + * stm::link_lock + * src::link_lock + */ + /** * stm_source_link_add() - connect an stm_source device to an stm device * @src: stm_source device @@ -710,6 +722,7 @@ static int stm_source_link_add(struct stm_source_device *src, char *id; int err; + mutex_lock(&stm->link_mutex); spin_lock(&stm->link_lock); spin_lock(&src->link_lock); @@ -719,6 +732,7 @@ static int stm_source_link_add(struct stm_source_device *src, spin_unlock(&src->link_lock); spin_unlock(&stm->link_lock); + mutex_unlock(&stm->link_mutex); id = kstrdup(src->data->name, GFP_KERNEL); if (id) { @@ -756,6 +770,7 @@ static int stm_source_link_add(struct stm_source_device *src, stm_put_device(stm); fail_detach: + mutex_lock(&stm->link_mutex); spin_lock(&stm->link_lock); spin_lock(&src->link_lock); @@ -764,6 +779,7 @@ static int stm_source_link_add(struct stm_source_device *src, spin_unlock(&src->link_lock); spin_unlock(&stm->link_lock); + mutex_unlock(&stm->link_mutex); return err; } @@ -776,13 +792,20 @@ static int stm_source_link_add(struct stm_source_device *src, * If @stm is @src::link, disconnect them from one another and put the * reference on the @stm device. * - * Caller must hold stm::link_lock. + * Caller must hold stm::link_mutex. */ static void __stm_source_link_drop(struct stm_source_device *src, struct stm_device *stm) { struct stm_device *link; + lockdep_assert_held(&stm->link_mutex); + + if (src->data->unlink) + src->data->unlink(src->data); + + /* for stm::link_list modification, we hold both mutex and spinlock */ + spin_lock(&stm->link_lock); spin_lock(&src->link_lock); link = srcu_dereference_check(src->link, &stm_source_srcu, 1); if (WARN_ON_ONCE(link != stm)) { @@ -791,13 +814,13 @@ static void __stm_source_link_drop(struct stm_source_device *src, } stm_output_free(link, &src->output); - /* caller must hold stm::link_lock */ list_del_init(&src->link_entry); /* matches stm_find_device() from stm_source_link_store() */ stm_put_device(link); rcu_assign_pointer(src->link, NULL); spin_unlock(&src->link_lock); + spin_unlock(&stm->link_lock); } /** @@ -819,12 +842,9 @@ static void stm_source_link_drop(struct stm_source_device *src) stm = srcu_dereference(src->link, &stm_source_srcu); if (stm) { - if (src->data->unlink) - src->data->unlink(src->data); - - spin_lock(&stm->link_lock); + mutex_lock(&stm->link_mutex); __stm_source_link_drop(src, stm); - spin_unlock(&stm->link_lock); + mutex_unlock(&stm->link_mutex); } srcu_read_unlock(&stm_source_srcu, idx); diff --git a/drivers/hwtracing/stm/stm.h b/drivers/hwtracing/stm/stm.h index 95ece0292c99..97ee02241440 100644 --- a/drivers/hwtracing/stm/stm.h +++ b/drivers/hwtracing/stm/stm.h @@ -45,6 +45,7 @@ struct stm_device { int major; unsigned int sw_nmasters; struct stm_data *data; + struct mutex link_mutex; spinlock_t link_lock; struct list_head link_list; /* master allocation */ From 1caf1d3016fcf51a9cb67ac0b750ac90cbc32bbe Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 22 Dec 2015 17:25:21 +0200 Subject: [PATCH 006/104] stm class: Prevent user-controllable allocations Currently, the character device write method allocates a temporary buffer for user's data, but the user's data size is not sanitized and can cause arbitrarily large allocations via kzalloc() or an integer overflow that will then result in overwriting kernel memory. This patch trims the input buffer size to avoid these issues. Reported-by: Sasha Levin Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit f08b18266c7116e2ec6885dd53a928f580060a71) --- drivers/hwtracing/stm/core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index ddcb606acea6..76697ad59f2d 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -406,6 +406,9 @@ static ssize_t stm_char_write(struct file *file, const char __user *buf, char *kbuf; int err; + if (count + 1 > PAGE_SIZE) + count = PAGE_SIZE - 1; + /* * if no m/c have been assigned to this writer up to this * point, use "default" policy entry From a22a7d13aa3875912aa74cc0ba1624fa5fb9990f Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:01 +0200 Subject: [PATCH 007/104] stm class: Use driver's packet callback return value STM drivers provide a callback to generate/send individual STP packets; it also tells the stm core how many bytes of payload it has consumed. However, we would also need to use the negative space of this return value to communicate errors that occur during the packet generation, in which case the stm core will have to take appropriate action. For now, we need to account for the possibility that the stm driver may not support certain combinations of packet type/flags, in which case it is expected to signal an error. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit f8560a9bc76b2cd5c06fa412cb7b5481d70fcf34) --- drivers/hwtracing/stm/core.c | 19 ++++++++++++------- include/linux/stm.h | 7 +++++++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 76697ad59f2d..1f2b7b47692d 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -380,8 +380,8 @@ static int stm_file_assign(struct stm_file *stmf, char *id, unsigned int width) return ret; } -static void stm_write(struct stm_data *data, unsigned int master, - unsigned int channel, const char *buf, size_t count) +static ssize_t stm_write(struct stm_data *data, unsigned int master, + unsigned int channel, const char *buf, size_t count) { unsigned int flags = STP_PACKET_TIMESTAMPED; const unsigned char *p = buf, nil = 0; @@ -393,9 +393,14 @@ static void stm_write(struct stm_data *data, unsigned int master, sz = data->packet(data, master, channel, STP_PACKET_DATA, flags, sz, p); flags = 0; + + if (sz < 0) + break; } data->packet(data, master, channel, STP_PACKET_FLAG, 0, 0, &nil); + + return pos; } static ssize_t stm_char_write(struct file *file, const char __user *buf, @@ -433,8 +438,8 @@ static ssize_t stm_char_write(struct file *file, const char __user *buf, return -EFAULT; } - stm_write(stm->data, stmf->output.master, stmf->output.channel, kbuf, - count); + count = stm_write(stm->data, stmf->output.master, stmf->output.channel, + kbuf, count); kfree(kbuf); @@ -996,9 +1001,9 @@ int stm_source_write(struct stm_source_data *data, unsigned int chan, stm = srcu_dereference(src->link, &stm_source_srcu); if (stm) - stm_write(stm->data, src->output.master, - src->output.channel + chan, - buf, count); + count = stm_write(stm->data, src->output.master, + src->output.channel + chan, + buf, count); else count = -ENODEV; diff --git a/include/linux/stm.h b/include/linux/stm.h index 9d0083d364e6..ab8ceca4f570 100644 --- a/include/linux/stm.h +++ b/include/linux/stm.h @@ -67,6 +67,13 @@ struct stm_device; * description. That is, the lowest master that can be allocated to software * writers is @sw_start and data from this writer will appear is @sw_start * master in the STP stream. + * + * The @packet callback should adhere to the following rules: + * 1) it must return the number of bytes it consumed from the payload; + * 2) therefore, if it sent a packet that does not have payload (like FLAG), + * it must return zero; + * 3) if it does not support the requested packet type/flag combination, + * it must return -ENOTSUPP. */ struct stm_data { const char *name; From 548e3d400155fc6a3f473063368c7028a25d631b Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:02 +0200 Subject: [PATCH 008/104] stm class: Support devices with multiple instances By convention, the name of the stm policy directory in configfs consists of the device name to which it applies and the actual policy name, separated by a dot. Now, some devices already have dots in their names that separate name of the actual device from its instance identifier. Such devices will result in two (or more, who can tell) dots in the policy directory name. Existing policy code, however, will treat the first dot as the one that separates device name from policy name, therefore failing the above case. This patch makes the last dot in the directory name be the separator, thus prohibiting dots from being used in policy names. Suggested-by: Chunyan Zhang Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 59be422e4ce10e3d49d4c9407a80fab8a9b7bc84) --- drivers/hwtracing/stm/policy.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index 94d3abfb737a..1db189657b2b 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -332,10 +332,11 @@ stp_policies_make(struct config_group *group, const char *name) /* * node must look like ., where - * is the name of an existing stm device and - * is an arbitrary string + * is the name of an existing stm device; may + * contain dots; + * is an arbitrary string; may not contain dots */ - p = strchr(devname, '.'); + p = strrchr(devname, '.'); if (!p) { kfree(devname); return ERR_PTR(-EINVAL); From bff93fcc5f62c9318652892c4310eaf551e5c2fa Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:03 +0200 Subject: [PATCH 009/104] stm class: dummy_stm: Create multiple devices STM framework should be able to handle multiple STM devices at a time, each one with its own master allocation policy. This patch changes dummy_stm driver to create multiple STM sinks to help testing the framework. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit bcfdf8afdebe63a2217fa632ae94f8aeecf9126f) --- drivers/hwtracing/stm/dummy_stm.c | 57 ++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/drivers/hwtracing/stm/dummy_stm.c b/drivers/hwtracing/stm/dummy_stm.c index 3709bef0b21f..4ff5961cd36f 100644 --- a/drivers/hwtracing/stm/dummy_stm.c +++ b/drivers/hwtracing/stm/dummy_stm.c @@ -40,22 +40,61 @@ dummy_stm_packet(struct stm_data *stm_data, unsigned int master, return size; } -static struct stm_data dummy_stm = { - .name = "dummy_stm", - .sw_start = 0x0000, - .sw_end = 0xffff, - .sw_nchannels = 0xffff, - .packet = dummy_stm_packet, -}; +#define DUMMY_STM_MAX 32 + +static struct stm_data dummy_stm[DUMMY_STM_MAX]; + +static int nr_dummies = 4; + +module_param(nr_dummies, int, 0600); + +static unsigned int dummy_stm_nr; static int dummy_stm_init(void) { - return stm_register_device(NULL, &dummy_stm, THIS_MODULE); + int i, ret = -ENOMEM, __nr_dummies = ACCESS_ONCE(nr_dummies); + + if (__nr_dummies < 0 || __nr_dummies > DUMMY_STM_MAX) + return -EINVAL; + + for (i = 0; i < __nr_dummies; i++) { + dummy_stm[i].name = kasprintf(GFP_KERNEL, "dummy_stm.%d", i); + if (!dummy_stm[i].name) + goto fail_unregister; + + dummy_stm[i].sw_start = 0x0000; + dummy_stm[i].sw_end = 0xffff; + dummy_stm[i].sw_nchannels = 0xffff; + dummy_stm[i].packet = dummy_stm_packet; + + ret = stm_register_device(NULL, &dummy_stm[i], THIS_MODULE); + if (ret) + goto fail_free; + } + + dummy_stm_nr = __nr_dummies; + + return 0; + +fail_unregister: + for (i--; i >= 0; i--) { + stm_unregister_device(&dummy_stm[i]); +fail_free: + kfree(dummy_stm[i].name); + } + + return ret; + } static void dummy_stm_exit(void) { - stm_unregister_device(&dummy_stm); + int i; + + for (i = 0; i < dummy_stm_nr; i++) { + stm_unregister_device(&dummy_stm[i]); + kfree(dummy_stm[i].name); + } } module_init(dummy_stm_init); From 8ffc98078a5ea1acb750ff53c7a935613e201e72 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:04 +0200 Subject: [PATCH 010/104] stm class: Add heartbeat stm source device Heartbeat stm source may have multiple instances (for connecting to different stm devices). Each instance will send a periodic test message over its stm device when it is linked. This can be used for testing stm class framework, stm device drivers or as a heartbeat over the stm link. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 1192918530381b5cfc0e5da51233fa94f783b221) --- drivers/hwtracing/stm/Kconfig | 11 +++ drivers/hwtracing/stm/Makefile | 2 + drivers/hwtracing/stm/heartbeat.c | 130 ++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 drivers/hwtracing/stm/heartbeat.c diff --git a/drivers/hwtracing/stm/Kconfig b/drivers/hwtracing/stm/Kconfig index e0ac75395526..847a39b35307 100644 --- a/drivers/hwtracing/stm/Kconfig +++ b/drivers/hwtracing/stm/Kconfig @@ -28,4 +28,15 @@ config STM_SOURCE_CONSOLE If you want to send kernel console messages over STM devices, say Y. +config STM_SOURCE_HEARTBEAT + tristate "Heartbeat over STM devices" + help + This is a kernel space trace source that sends periodic + heartbeat messages to trace hosts over STM devices. It is + also useful for testing stm class drivers and the stm class + framework itself. + + If you want to send heartbeat messages over STM devices, + say Y. + endif diff --git a/drivers/hwtracing/stm/Makefile b/drivers/hwtracing/stm/Makefile index f9312c38dd7a..a9ce3d487e57 100644 --- a/drivers/hwtracing/stm/Makefile +++ b/drivers/hwtracing/stm/Makefile @@ -5,5 +5,7 @@ stm_core-y := core.o policy.o obj-$(CONFIG_STM_DUMMY) += dummy_stm.o obj-$(CONFIG_STM_SOURCE_CONSOLE) += stm_console.o +obj-$(CONFIG_STM_SOURCE_HEARTBEAT) += stm_heartbeat.o stm_console-y := console.o +stm_heartbeat-y := heartbeat.o diff --git a/drivers/hwtracing/stm/heartbeat.c b/drivers/hwtracing/stm/heartbeat.c new file mode 100644 index 000000000000..0133571b506f --- /dev/null +++ b/drivers/hwtracing/stm/heartbeat.c @@ -0,0 +1,130 @@ +/* + * Simple heartbeat STM source driver + * Copyright (c) 2016, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * Heartbeat STM source will send repetitive messages over STM devices to a + * trace host. + */ + +#include +#include +#include +#include +#include + +#define STM_HEARTBEAT_MAX 32 + +static int nr_devs = 4; +static int interval_ms = 10; + +module_param(nr_devs, int, 0600); +module_param(interval_ms, int, 0600); + +static struct stm_heartbeat { + struct stm_source_data data; + struct hrtimer hrtimer; + unsigned int active; +} stm_heartbeat[STM_HEARTBEAT_MAX]; + +static unsigned int nr_instances; + +static const char str[] = "heartbeat stm source driver is here to serve you"; + +static enum hrtimer_restart stm_heartbeat_hrtimer_handler(struct hrtimer *hr) +{ + struct stm_heartbeat *heartbeat = container_of(hr, struct stm_heartbeat, + hrtimer); + + stm_source_write(&heartbeat->data, 0, str, sizeof str); + if (heartbeat->active) + hrtimer_forward_now(hr, ms_to_ktime(interval_ms)); + + return heartbeat->active ? HRTIMER_RESTART : HRTIMER_NORESTART; +} + +static int stm_heartbeat_link(struct stm_source_data *data) +{ + struct stm_heartbeat *heartbeat = + container_of(data, struct stm_heartbeat, data); + + heartbeat->active = 1; + hrtimer_start(&heartbeat->hrtimer, ms_to_ktime(interval_ms), + HRTIMER_MODE_ABS); + + return 0; +} + +static void stm_heartbeat_unlink(struct stm_source_data *data) +{ + struct stm_heartbeat *heartbeat = + container_of(data, struct stm_heartbeat, data); + + heartbeat->active = 0; + hrtimer_cancel(&heartbeat->hrtimer); +} + +static int stm_heartbeat_init(void) +{ + int i, ret = -ENOMEM, __nr_instances = ACCESS_ONCE(nr_devs); + + if (__nr_instances < 0 || __nr_instances > STM_HEARTBEAT_MAX) + return -EINVAL; + + for (i = 0; i < __nr_instances; i++) { + stm_heartbeat[i].data.name = + kasprintf(GFP_KERNEL, "heartbeat.%d", i); + if (!stm_heartbeat[i].data.name) + goto fail_unregister; + + stm_heartbeat[i].data.nr_chans = 1; + stm_heartbeat[i].data.link = stm_heartbeat_link; + stm_heartbeat[i].data.unlink = stm_heartbeat_unlink; + hrtimer_init(&stm_heartbeat[i].hrtimer, CLOCK_MONOTONIC, + HRTIMER_MODE_ABS); + stm_heartbeat[i].hrtimer.function = + stm_heartbeat_hrtimer_handler; + + ret = stm_source_register_device(NULL, &stm_heartbeat[i].data); + if (ret) + goto fail_free; + } + + nr_instances = __nr_instances; + + return 0; + +fail_unregister: + for (i--; i >= 0; i--) { + stm_source_unregister_device(&stm_heartbeat[i].data); +fail_free: + kfree(stm_heartbeat[i].data.name); + } + + return ret; +} + +static void stm_heartbeat_exit(void) +{ + int i; + + for (i = 0; i < nr_instances; i++) { + stm_source_unregister_device(&stm_heartbeat[i].data); + kfree(stm_heartbeat[i].data.name); + } +} + +module_init(stm_heartbeat_init); +module_exit(stm_heartbeat_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("stm_heartbeat driver"); +MODULE_AUTHOR("Alexander Shishkin "); From 1f6037f5220b05dc3dda8d917d9c57fc29efa0ea Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:05 +0200 Subject: [PATCH 011/104] stm class: Fix unlocking braino in the error path If an illegal attempt is made to unlink stm source device from an stm device, the stm device's link spinlock mistakenly remains locked. While this really shouldn't happen (there's a warning in place), the locking should remain in order so that we can still recover from this situation if it indeed does happen. This patch unifies the unlocking in the exit path of __stm_source_link_drop() to fix this. Reported-by: Laurent Fert Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 1810f2c44817c74ca3d05d1e3981e3a2e2ceb6f5) --- drivers/hwtracing/stm/core.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 1f2b7b47692d..7aaed081fbf9 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -816,10 +816,8 @@ static void __stm_source_link_drop(struct stm_source_device *src, spin_lock(&stm->link_lock); spin_lock(&src->link_lock); link = srcu_dereference_check(src->link, &stm_source_srcu, 1); - if (WARN_ON_ONCE(link != stm)) { - spin_unlock(&src->link_lock); - return; - } + if (WARN_ON_ONCE(link != stm)) + goto unlock; stm_output_free(link, &src->output); list_del_init(&src->link_entry); @@ -827,6 +825,7 @@ static void __stm_source_link_drop(struct stm_source_device *src, stm_put_device(link); rcu_assign_pointer(src->link, NULL); +unlock: spin_unlock(&src->link_lock); spin_unlock(&stm->link_lock); } From dd5219ad9e3d488a42c341c10a5ee060d0a322dd Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:06 +0200 Subject: [PATCH 012/104] stm class: Guard output assignment against concurrency It is possible to concurrently assign the same output (a character device writer or an stm_source device) to different stm devices, which sets off a strategically placed warning in stm_output_assign(). To avoid this, use a spinlock to serialize (un)assignments between outputs and stm devices. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit cde4ad8368840e414ecf67db258fe1dabaa5fd2e) --- drivers/hwtracing/stm/core.c | 17 +++++++++++++++++ drivers/hwtracing/stm/stm.h | 1 + 2 files changed, 18 insertions(+) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 7aaed081fbf9..41fee197ed31 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -185,6 +185,9 @@ static void stm_output_claim(struct stm_device *stm, struct stm_output *output) { struct stp_master *master = stm_master(stm, output->master); + lockdep_assert_held(&stm->mc_lock); + lockdep_assert_held(&output->lock); + if (WARN_ON_ONCE(master->nr_free < output->nr_chans)) return; @@ -199,6 +202,9 @@ stm_output_disclaim(struct stm_device *stm, struct stm_output *output) { struct stp_master *master = stm_master(stm, output->master); + lockdep_assert_held(&stm->mc_lock); + lockdep_assert_held(&output->lock); + bitmap_release_region(&master->chan_map[0], output->channel, ilog2(output->nr_chans)); @@ -288,6 +294,7 @@ static int stm_output_assign(struct stm_device *stm, unsigned int width, } spin_lock(&stm->mc_lock); + spin_lock(&output->lock); /* output is already assigned -- shouldn't happen */ if (WARN_ON_ONCE(output->nr_chans)) goto unlock; @@ -304,6 +311,7 @@ static int stm_output_assign(struct stm_device *stm, unsigned int width, ret = 0; unlock: + spin_unlock(&output->lock); spin_unlock(&stm->mc_lock); return ret; @@ -312,11 +320,18 @@ static int stm_output_assign(struct stm_device *stm, unsigned int width, static void stm_output_free(struct stm_device *stm, struct stm_output *output) { spin_lock(&stm->mc_lock); + spin_lock(&output->lock); if (output->nr_chans) stm_output_disclaim(stm, output); + spin_unlock(&output->lock); spin_unlock(&stm->mc_lock); } +static void stm_output_init(struct stm_output *output) +{ + spin_lock_init(&output->lock); +} + static int major_match(struct device *dev, const void *data) { unsigned int major = *(unsigned int *)data; @@ -339,6 +354,7 @@ static int stm_char_open(struct inode *inode, struct file *file) if (!stmf) return -ENOMEM; + stm_output_init(&stmf->output); stmf->stm = to_stm_device(dev); if (!try_module_get(stmf->stm->owner)) @@ -952,6 +968,7 @@ int stm_source_register_device(struct device *parent, if (err) goto err; + stm_output_init(&src->output); spin_lock_init(&src->link_lock); INIT_LIST_HEAD(&src->link_entry); src->data = data; diff --git a/drivers/hwtracing/stm/stm.h b/drivers/hwtracing/stm/stm.h index 97ee02241440..4e8c6926260f 100644 --- a/drivers/hwtracing/stm/stm.h +++ b/drivers/hwtracing/stm/stm.h @@ -57,6 +57,7 @@ struct stm_device { container_of((_d), struct stm_device, dev) struct stm_output { + spinlock_t lock; unsigned int master; unsigned int channel; unsigned int nr_chans; From 984a45ed12b5c04728f85e9c258ba1ae9ebac2d3 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:07 +0200 Subject: [PATCH 013/104] stm class: Fix unbalanced module/device refcounting STM code takes references to the stm device and its module for the duration of the character device's existence or the stm_source link. Dropping these references is not well balanced everywhere, which may lead to leaks. This patch balances the acquisition and releasing of these two references and annotates each site so that it's easier to verify correctness by reading the code. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit f7c81c7176c72c7899390754b4b038a64b296e4d) --- drivers/hwtracing/stm/core.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 41fee197ed31..31a03e2fae29 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -113,6 +113,7 @@ struct stm_device *stm_find_device(const char *buf) stm = to_stm_device(dev); if (!try_module_get(stm->owner)) { + /* matches class_find_device() above */ put_device(dev); return NULL; } @@ -125,7 +126,7 @@ struct stm_device *stm_find_device(const char *buf) * @stm: stm device, previously acquired by stm_find_device() * * This drops the module reference and device reference taken by - * stm_find_device(). + * stm_find_device() or stm_char_open(). */ void stm_put_device(struct stm_device *stm) { @@ -365,6 +366,8 @@ static int stm_char_open(struct inode *inode, struct file *file) return nonseekable_open(inode, file); err_free: + /* matches class_find_device() above */ + put_device(dev); kfree(stmf); return err; @@ -375,6 +378,11 @@ static int stm_char_release(struct inode *inode, struct file *file) struct stm_file *stmf = file->private_data; stm_output_free(stmf->stm, &stmf->output); + + /* + * matches the stm_char_open()'s + * class_find_device() + try_module_get() + */ stm_put_device(stmf->stm); kfree(stmf); @@ -539,10 +547,8 @@ static int stm_char_policy_set_ioctl(struct stm_file *stmf, void __user *arg) ret = stm->data->link(stm->data, stmf->output.master, stmf->output.channel); - if (ret) { + if (ret) stm_output_free(stmf->stm, &stmf->output); - stm_put_device(stmf->stm); - } err_free: kfree(id); @@ -679,6 +685,7 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data, return 0; err_device: + /* matches device_initialize() above */ put_device(&stm->dev); err_free: kfree(stm); @@ -791,7 +798,6 @@ static int stm_source_link_add(struct stm_source_device *src, fail_free_output: stm_output_free(stm, &src->output); - stm_put_device(stm); fail_detach: mutex_lock(&stm->link_mutex); @@ -905,8 +911,10 @@ static ssize_t stm_source_link_store(struct device *dev, return -EINVAL; err = stm_source_link_add(src, link); - if (err) + if (err) { + /* matches the stm_find_device() above */ stm_put_device(link); + } return err ? : count; } From fffb71e99d58c8d8a1481821a9d39d90b004dc38 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:08 +0200 Subject: [PATCH 014/104] stm class: Fix a race in unlinking There is a window in stm_source_link_drop(), during which the source's link may change before locks are acquired. When this happens, it throws a warning, since this is not an expected scenario. This patch handles the race in such a way that if the link appears to have changed by the time we took the locks, it will release them and repeat the whole unlinking procedure from the beginning, unless the other contender beat us to it. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit b4ca34aaf78ed0cdfc15956d377064104257a437) --- drivers/hwtracing/stm/core.c | 54 ++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 31a03e2fae29..e072652d9646 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -694,18 +694,26 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data, } EXPORT_SYMBOL_GPL(stm_register_device); -static void __stm_source_link_drop(struct stm_source_device *src, - struct stm_device *stm); +static int __stm_source_link_drop(struct stm_source_device *src, + struct stm_device *stm); void stm_unregister_device(struct stm_data *stm_data) { struct stm_device *stm = stm_data->stm; struct stm_source_device *src, *iter; - int i; + int i, ret; mutex_lock(&stm->link_mutex); list_for_each_entry_safe(src, iter, &stm->link_list, link_entry) { - __stm_source_link_drop(src, stm); + ret = __stm_source_link_drop(src, stm); + /* + * src <-> stm link must not change under the same + * stm::link_mutex, so complain loudly if it has; + * also in this situation ret!=0 means this src is + * not connected to this stm and it should be otherwise + * safe to proceed with the tear-down of stm. + */ + WARN_ON_ONCE(ret); } mutex_unlock(&stm->link_mutex); @@ -824,22 +832,28 @@ static int stm_source_link_add(struct stm_source_device *src, * * Caller must hold stm::link_mutex. */ -static void __stm_source_link_drop(struct stm_source_device *src, - struct stm_device *stm) +static int __stm_source_link_drop(struct stm_source_device *src, + struct stm_device *stm) { struct stm_device *link; + int ret = 0; lockdep_assert_held(&stm->link_mutex); - if (src->data->unlink) - src->data->unlink(src->data); - /* for stm::link_list modification, we hold both mutex and spinlock */ spin_lock(&stm->link_lock); spin_lock(&src->link_lock); link = srcu_dereference_check(src->link, &stm_source_srcu, 1); - if (WARN_ON_ONCE(link != stm)) + + /* + * The linked device may have changed since we last looked, because + * we weren't holding the src::link_lock back then; if this is the + * case, tell the caller to retry. + */ + if (link != stm) { + ret = -EAGAIN; goto unlock; + } stm_output_free(link, &src->output); list_del_init(&src->link_entry); @@ -850,6 +864,11 @@ static void __stm_source_link_drop(struct stm_source_device *src, unlock: spin_unlock(&src->link_lock); spin_unlock(&stm->link_lock); + + if (!ret && src->data->unlink) + src->data->unlink(src->data); + + return ret; } /** @@ -865,18 +884,29 @@ static void __stm_source_link_drop(struct stm_source_device *src, static void stm_source_link_drop(struct stm_source_device *src) { struct stm_device *stm; - int idx; + int idx, ret; +retry: idx = srcu_read_lock(&stm_source_srcu); + /* + * The stm device will be valid for the duration of this + * read section, but the link may change before we grab + * the src::link_lock in __stm_source_link_drop(). + */ stm = srcu_dereference(src->link, &stm_source_srcu); + ret = 0; if (stm) { mutex_lock(&stm->link_mutex); - __stm_source_link_drop(src, stm); + ret = __stm_source_link_drop(src, stm); mutex_unlock(&stm->link_mutex); } srcu_read_unlock(&stm_source_srcu, idx); + + /* if it did change, retry */ + if (ret == -EAGAIN) + goto retry; } static ssize_t stm_source_link_show(struct device *dev, From d3b187f9c7bc7f9f73e4064041890fd71de9f5b3 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:09 +0200 Subject: [PATCH 015/104] stm class: Plug stm device's unlink callback STM device's unlink callback is never actually called from anywhere in the stm class code. This patch adds calls to stm driver's unlink method after the unlinking has succeeded. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit cc8424074e51355e0c6ba717d8edc50d408f2802) --- drivers/hwtracing/stm/core.c | 23 +++++++++++++++++++---- include/linux/stm.h | 3 +++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index e072652d9646..0dd8f4c996f1 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -376,14 +376,19 @@ static int stm_char_open(struct inode *inode, struct file *file) static int stm_char_release(struct inode *inode, struct file *file) { struct stm_file *stmf = file->private_data; + struct stm_device *stm = stmf->stm; - stm_output_free(stmf->stm, &stmf->output); + if (stm->data->unlink) + stm->data->unlink(stm->data, stmf->output.master, + stmf->output.channel); + + stm_output_free(stm, &stmf->output); /* * matches the stm_char_open()'s * class_find_device() + try_module_get() */ - stm_put_device(stmf->stm); + stm_put_device(stm); kfree(stmf); return 0; @@ -865,8 +870,18 @@ static int __stm_source_link_drop(struct stm_source_device *src, spin_unlock(&src->link_lock); spin_unlock(&stm->link_lock); - if (!ret && src->data->unlink) - src->data->unlink(src->data); + /* + * Call the unlink callbacks for both source and stm, when we know + * that we have actually performed the unlinking. + */ + if (!ret) { + if (src->data->unlink) + src->data->unlink(src->data); + + if (stm->data->unlink) + stm->data->unlink(stm->data, src->output.master, + src->output.channel); + } return ret; } diff --git a/include/linux/stm.h b/include/linux/stm.h index ab8ceca4f570..1a79ed8e43da 100644 --- a/include/linux/stm.h +++ b/include/linux/stm.h @@ -74,6 +74,9 @@ struct stm_device; * it must return zero; * 3) if it does not support the requested packet type/flag combination, * it must return -ENOTSUPP. + * + * The @unlink callback is called when there are no more active writers so + * that the master/channel can be quiesced. */ struct stm_data { const char *name; From a2fc3f7248b75b8cdbf54590e41874aebd19303d Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:10 +0200 Subject: [PATCH 016/104] stm class: dummy_stm: Add link callback for fault injection STM device's link callback has the power to abort master/channel assignment by returning a negative error code. Use this in dummy stm device to optionally abort assigning certain channel IDs. This is useful as fault injection into the stm class core, for testing purposes. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit adcde635f5d6b77e9f11087af09c6f62da2bf108) --- drivers/hwtracing/stm/dummy_stm.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/hwtracing/stm/dummy_stm.c b/drivers/hwtracing/stm/dummy_stm.c index 4ff5961cd36f..310adf57e7a1 100644 --- a/drivers/hwtracing/stm/dummy_stm.c +++ b/drivers/hwtracing/stm/dummy_stm.c @@ -50,6 +50,19 @@ module_param(nr_dummies, int, 0600); static unsigned int dummy_stm_nr; +static unsigned int fail_mode; + +module_param(fail_mode, int, 0600); + +static int dummy_stm_link(struct stm_data *data, unsigned int master, + unsigned int channel) +{ + if (fail_mode && (channel & fail_mode)) + return -EINVAL; + + return 0; +} + static int dummy_stm_init(void) { int i, ret = -ENOMEM, __nr_dummies = ACCESS_ONCE(nr_dummies); @@ -66,6 +79,7 @@ static int dummy_stm_init(void) dummy_stm[i].sw_end = 0xffff; dummy_stm[i].sw_nchannels = 0xffff; dummy_stm[i].packet = dummy_stm_packet; + dummy_stm[i].link = dummy_stm_link; ret = stm_register_device(NULL, &dummy_stm[i], THIS_MODULE); if (ret) From 072536f0702fc1587c0e0b80377a9b9ca58fda22 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Wed, 2 Mar 2016 13:24:14 +0200 Subject: [PATCH 017/104] perf/core: Fix perf_sched_count derailment The error path in perf_event_open() is such that asking for a sampling event on a PMU that doesn't generate interrupts will end up in dropping the perf_sched_count even though it hasn't been incremented for this event yet. Given a sufficient amount of these calls, we'll end up disabling scheduler's jump label even though we'd still have active events in the system, thereby facilitating the arrival of the infernal regions upon us. I'm fixing this by moving account_event() inside perf_event_alloc(). Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Cc: vince@deater.net Link: http://lkml.kernel.org/r/1456917854-29427-1-git-send-email-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar (cherry picked from commit 927a5570855836e5d5859a80ce7e91e963545e8f) --- kernel/events/core.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index cfc227ccfceb..c22f4579a2e7 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -7979,6 +7979,9 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, } } + /* symmetric to unaccount_event() in _free_event() */ + account_event(event); + return event; err_per_task: @@ -8342,8 +8345,6 @@ SYSCALL_DEFINE5(perf_event_open, } } - account_event(event); - /* * Special case software events and allow them to be part of * any hardware group. @@ -8626,8 +8627,6 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu, /* Mark owner so we could distinguish it from user events. */ event->owner = EVENT_OWNER_KERNEL; - account_event(event); - ctx = find_get_context(event->pmu, task, event); if (IS_ERR(ctx)) { err = PTR_ERR(ctx); From 75663c46e8076b468ceb1b4d17358cdb65d4ba77 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 21 Mar 2016 10:02:42 +0200 Subject: [PATCH 018/104] perf/core: Don't leak event in the syscall error path In the error path, event_file not being NULL is used to determine whether the event itself still needs to be free'd, so fix it up to avoid leaking. Reported-by: Leon Yu Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Fixes: 130056275ade ("perf: Do not double free") Link: http://lkml.kernel.org/r/87twk06yxp.fsf@ashishki-desk.ger.corp.intel.com Signed-off-by: Ingo Molnar (cherry picked from commit 201c2f85bd0bc13b712d9c0b3d11251b182e06ae) --- kernel/events/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/events/core.c b/kernel/events/core.c index c22f4579a2e7..b296032a2c4e 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -8457,6 +8457,7 @@ SYSCALL_DEFINE5(perf_event_open, f_flags); if (IS_ERR(event_file)) { err = PTR_ERR(event_file); + event_file = NULL; goto err_context; } From e05ed3268032a6c0dc677558fb82afb046dd638f Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 10 May 2016 16:18:33 +0300 Subject: [PATCH 019/104] perf/core: Disable the event on a truncated AUX record When the PMU driver reports a truncated AUX record, it effectively means that there is no more usable room in the event's AUX buffer (even though there may still be some room, so that perf_aux_output_begin() doesn't take action). At this point the consumer still has to be woken up and the event has to be disabled, otherwise the event will just keep spinning between perf_aux_output_begin() and perf_aux_output_end() until its context gets unscheduled. Again, for cpu-wide events this means never, so once in this condition, they will be forever losing data. Fix this by disabling the event and waking up the consumer in case of a truncated AUX record. Reported-by: Markus Metzger Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: Borislav Petkov Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Cc: vince@deater.net Link: http://lkml.kernel.org/r/1462886313-13660-3-git-send-email-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar (cherry picked from commit 9f448cd3cbcec8995935e60b27802ae56aac8cc0) --- kernel/events/ring_buffer.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index 1faad2cfdb9e..287201a5d12f 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -347,6 +347,7 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size, bool truncated) { struct ring_buffer *rb = handle->rb; + bool wakeup = truncated; unsigned long aux_head; u64 flags = 0; @@ -375,9 +376,16 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size, aux_head = rb->user_page->aux_head = local_read(&rb->aux_head); if (aux_head - local_read(&rb->aux_wakeup) >= rb->aux_watermark) { - perf_output_wakeup(handle); + wakeup = true; local_add(rb->aux_watermark, &rb->aux_wakeup); } + + if (wakeup) { + if (truncated) + handle->event->pending_disable = 1; + perf_output_wakeup(handle); + } + handle->event = NULL; local_set(&rb->aux_nest, 0); From b746b9cf2f52aa7a572b8be2b1718966b65f14a1 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Thu, 17 Dec 2015 08:47:02 -0700 Subject: [PATCH 020/104] coresight: checking for NULL string in coresight_name_match() Connection child names associated to ports can sometimes be NULL, which is the case when booting a system on QEMU or when the Coresight power domain isn't switched on. This patch is adding a check to make sure a NULL string isn't fed to strcmp(), something that avoid crashing the system. Cc: # v3.18+ Reported-by: Tyler Baker Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit fadf3a44e974b030e7145218ad1ab25e3ef91738) --- drivers/hwtracing/coresight/coresight.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index e25492137d8b..93738dfbf631 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -548,7 +548,7 @@ static int coresight_name_match(struct device *dev, void *data) to_match = data; i_csdev = to_coresight_device(dev); - if (!strcmp(to_match, dev_name(&i_csdev->dev))) + if (to_match && !strcmp(to_match, dev_name(&i_csdev->dev))) return 1; return 0; From 4b5c6a09cdf7db8c7a30f6b69dc4284bbdd6ed53 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Thu, 17 Dec 2015 08:47:02 -0700 Subject: [PATCH 021/104] coresight: Fix a typo in Kconfig Signed-off-by: Andrew F. Davis Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit b9884d3b79f60846cdf04be262d13bca8996f99a) --- drivers/hwtracing/coresight/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index 6c8921140f02..c85935f3525a 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -8,7 +8,7 @@ menuconfig CORESIGHT This framework provides a kernel interface for the CoreSight debug and trace drivers to register themselves with. It's intended to build a topological view of the CoreSight components based on a DT - specification and configure the right serie of components when a + specification and configure the right series of components when a trace source gets enabled. if CORESIGHT From 0d03bf0e9b50a6310cc0f92c08930111605d8540 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 2 Feb 2016 14:13:55 -0700 Subject: [PATCH 022/104] coresight: fixing indentation problem Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit ff8742279ff163e36ee50bb4e75090af1d7d6e3b) --- drivers/hwtracing/coresight/coresight.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 93738dfbf631..4236a02cdab2 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -536,7 +536,7 @@ static void coresight_fixup_orphan_conns(struct coresight_device *csdev) * are hooked-up with each newly added component. */ bus_for_each_dev(&coresight_bustype, NULL, - csdev, coresight_orphan_match); + csdev, coresight_orphan_match); } From 3e42ab0f61d184a7e6ffa063535afb31538b1595 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 2 Feb 2016 14:13:56 -0700 Subject: [PATCH 023/104] coresight: fixing lockdep error On some platform the following lockdep error occurs when doing simple manipulations: [ 23.197021] [ 23.198608] ====================================================== [ 23.205078] [ INFO: possible circular locking dependency detected ] [ 23.211639] 4.4.0-rc8-00025-gbbf360b #172 Not tainted [ 23.216918] ------------------------------------------------------- [ 23.223480] sh/858 is trying to acquire lock: [ 23.228057] (coresight_mutex){+.+.+.}, at: [] coresight_enable+0x1c/0x1b4 [ 23.236206] [ 23.236206] but task is already holding lock: [ 23.242309] (s_active#52){++++.+}, at: [] kernfs_fop_write+0x5c/0x1c0 [ 23.250122] [ 23.250122] which lock already depends on the new lock. [ 23.250122] [ 23.258697] [ 23.258697] the existing dependency chain (in reverse order) is: [ 23.266510] -> #1 (s_active#52){++++.+}: [ 23.270843] [] __kernfs_remove+0x294/0x35c [ 23.276672] [] kernfs_remove_by_name_ns+0x44/0x8c [ 23.283172] [] remove_files+0x3c/0x84 [ 23.288543] [] sysfs_remove_group+0x48/0x9c [ 23.294494] [] sysfs_remove_groups+0x2c/0x3c [ 23.300506] [] device_remove_attrs+0x5c/0x74 [ 23.306549] [] device_del+0x110/0x218 [ 23.311950] [] device_unregister+0x2c/0x6c [ 23.317779] [] coresight_unregister+0x30/0x40 [ 23.323883] [] etm_probe+0x228/0x2e8 [ 23.329193] [] amba_probe+0xe4/0x160 [ 23.334503] [] driver_probe_device+0x23c/0x480 [ 23.340728] [] __driver_attach+0x9c/0xa0 [ 23.346374] [] bus_for_each_dev+0x70/0xa4 [ 23.352142] [] driver_attach+0x24/0x28 [ 23.357604] [] bus_add_driver+0x1e0/0x278 [ 23.363372] [] driver_register+0x80/0x100 [ 23.369110] [] amba_driver_register+0x58/0x5c [ 23.375244] [] etm_driver_init+0x18/0x1c [ 23.380889] [] do_one_initcall+0xc4/0x20c [ 23.386657] [] kernel_init_freeable+0x160/0x208 [ 23.392974] [] kernel_init+0x18/0xf0 [ 23.398254] [] ret_from_fork+0x14/0x24 [ 23.403747] -> #0 (coresight_mutex){+.+.+.}: [ 23.408447] [] lock_acquire+0xe4/0x210 [ 23.413909] [] mutex_lock_nested+0x74/0x450 [ 23.419860] [] coresight_enable+0x1c/0x1b4 [ 23.425689] [] enable_source_store+0x58/0x68 [ 23.431732] [] dev_attr_store+0x20/0x2c [ 23.437286] [] sysfs_kf_write+0x50/0x54 [ 23.442871] [] kernfs_fop_write+0xc4/0x1c0 [ 23.448699] [] __vfs_write+0x34/0xe4 [ 23.454040] [] vfs_write+0x98/0x174 [ 23.459228] [] SyS_write+0x4c/0xa8 [ 23.464355] [] ret_fast_syscall+0x0/0x1c [ 23.470031] [ 23.470031] other info that might help us debug this: [ 23.470031] [ 23.478393] Possible unsafe locking scenario: [ 23.478393] [ 23.484619] CPU0 CPU1 [ 23.489349] ---- ---- [ 23.494079] lock(s_active#52); [ 23.497497] lock(coresight_mutex); [ 23.503906] lock(s_active#52); [ 23.509918] lock(coresight_mutex); [ 23.513702] [ 23.513702] *** DEADLOCK *** [ 23.513702] [ 23.519897] 3 locks held by sh/858: [ 23.523529] #0: (sb_writers#7){.+.+.+}, at: [] __sb_start_write+0xa8/0xd4 [ 23.531799] #1: (&of->mutex){+.+...}, at: [] kernfs_fop_write+0x54/0x1c0 [ 23.539916] #2: (s_active#52){++++.+}, at: [] kernfs_fop_write+0x5c/0x1c0 [ 23.548156] [ 23.548156] stack backtrace: [ 23.552734] CPU: 0 PID: 858 Comm: sh Not tainted 4.4.0-rc8-00025-gbbf360b #172 [ 23.560302] Hardware name: Generic OMAP4 (Flattened Device Tree) [ 23.566589] Backtrace: [ 23.569152] [] (dump_backtrace) from [] (show_stack+0x18/0x1c) [ 23.577087] r7:ed4b8570 r6:c0936400 r5:c07ae71c r4:00000000 [ 23.583038] [] (show_stack) from [] (dump_stack+0x98/0xc0) [ 23.590606] [] (dump_stack) from [] (print_circular_bug+0x21c/0x33c) [ 23.599090] r5:c0939d60 r4:c0936400 [ 23.602874] [] (print_circular_bug) from [] (__lock_acquire+0x1c98/0x1d88) [ 23.611877] r10:00000003 r9:c0fd7a5c r8:ed4b8550 r7:ed4b8570 r6:ed4b8000 r5:c0ff69e4 [ 23.620117] r4:c0936400 r3:ed4b8550 [ 23.623901] [] (__lock_acquire) from [] (lock_acquire+0xe4/0x210) [ 23.632080] r10:00000000 r9:00000000 r8:60000013 r7:c07cb7b4 r6:00000001 r5:00000000 [ 23.640350] r4:00000000 [ 23.643005] [] (lock_acquire) from [] (mutex_lock_nested+0x74/0x450) [ 23.651458] r10:ecc0bf80 r9:edbe7dcc r8:ed4b8000 r7:c0fd7a5c r6:c0415d40 r5:00000000 [ 23.659729] r4:c07cb780 [ 23.662384] [] (mutex_lock_nested) from [] (coresight_enable+0x1c/0x1b4) [ 23.671234] r10:ecc0bf80 r9:edbe7dcc r8:ed733c00 r7:00000000 r6:ed733c00 r5:00000002 [ 23.679473] r4:ed762140 [ 23.682128] [] (coresight_enable) from [] (enable_source_store+0x58/0x68) [ 23.691070] r7:00000000 r6:ed733c00 r5:00000002 r4:ed762160 [ 23.697052] [] (enable_source_store) from [] (dev_attr_store+0x20/0x2c) [ 23.705780] r5:edbe7dc0 r4:c0415fd8 [ 23.709533] [] (dev_attr_store) from [] (sysfs_kf_write+0x50/0x54) [ 23.717834] r5:edbe7dc0 r4:c030b338 [ 23.721618] [] (sysfs_kf_write) from [] (kernfs_fop_write+0xc4/0x1c0) [ 23.730163] r7:00000000 r6:00000000 r5:00000002 r4:edbe7dc0 [ 23.736145] [] (kernfs_fop_write) from [] (__vfs_write+0x34/0xe4) [ 23.744323] r10:00000000 r9:ecc0a000 r8:c0010964 r7:ecc0bf80 r6:00000002 r5:c01d4ae4 [ 23.752593] r4:ee385a40 [ 23.755249] [] (__vfs_write) from [] (vfs_write+0x98/0x174) [ 23.762908] r9:ecc0a000 r8:c0010964 r7:ecc0bf80 r6:000ab0d8 r5:00000002 r4:ee385a40 [ 23.771057] [] (vfs_write) from [] (SyS_write+0x4c/0xa8) [ 23.778442] r8:c0010964 r7:00000002 r6:000ab0d8 r5:ee385a40 r4:ee385a40 [ 23.785522] [] (SyS_write) from [] (ret_fast_syscall+0x0/0x1c) [ 23.793457] r7:00000004 r6:00000001 r5:000ab0d8 r4:00000002 [ 23.799652] coresight-etb10 54162000.etb: ETB enabled [ 23.805084] coresight-funnel 54164000.funnel: FUNNEL inport 0 enabled [ 23.811859] coresight-replicator 44000000.ocp:replicator: REPLICATOR enabled [ 23.819335] coresight-funnel 54158000.funnel: FUNNEL inport 0 enabled [ 23.826110] coresight-etm3x 5414c000.ptm: ETM tracing enabled The locking in coresight_unregister() is not required as the only customers of the function are drivers themselves when an initialisation failure has been encoutered. Reported-by: Rabin Vincent Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit a9ddc71f5840c2711e530f2e055b278f79948b29) --- drivers/hwtracing/coresight/coresight.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 4236a02cdab2..41b42018b660 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -713,12 +713,8 @@ EXPORT_SYMBOL_GPL(coresight_register); void coresight_unregister(struct coresight_device *csdev) { - mutex_lock(&coresight_mutex); - kfree(csdev->conns); device_unregister(&csdev->dev); - - mutex_unlock(&coresight_mutex); } EXPORT_SYMBOL_GPL(coresight_unregister); From 27aa9ceebe6a14dcc961126bd2a3f462dfba256b Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 2 Feb 2016 14:13:57 -0700 Subject: [PATCH 024/104] coresight: coresight_unregister() function cleanup In its current form the code never frees csdev->refcnt allocated in coresight_register(). There is also a problem with csdev->conns that is freed before device_unregister() rather than in the device release function. This patch addresses both issues by moving kfree(csdev->conns) to coresight_device_release() and freeing csdev->refcnt, also in the same function. Reported-by: Rabin Vincent Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit fae54158792aec705620bdc3938d342879204f0c) --- drivers/hwtracing/coresight/coresight.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 41b42018b660..8872db4410eb 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -481,6 +481,8 @@ static void coresight_device_release(struct device *dev) { struct coresight_device *csdev = to_coresight_device(dev); + kfree(csdev->conns); + kfree(csdev->refcnt); kfree(csdev); } @@ -713,7 +715,6 @@ EXPORT_SYMBOL_GPL(coresight_register); void coresight_unregister(struct coresight_device *csdev) { - kfree(csdev->conns); device_unregister(&csdev->dev); } EXPORT_SYMBOL_GPL(coresight_unregister); From f03467661ca82e16b05e786c893bb4cb89951ca3 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 2 Feb 2016 14:13:58 -0700 Subject: [PATCH 025/104] coresight: release reference taken by 'bus_find_device()' The reference count taken by function bus_find_device() needs to be released if a child device is found, something this patch is adding. Reported-by: Rabin Vincent Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit f2dfab3568fc32afeac8b698481e80e7ab2dc658) --- drivers/hwtracing/coresight/coresight.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 8872db4410eb..a35ca54a76c9 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -570,6 +570,8 @@ static void coresight_fixup_device_conns(struct coresight_device *csdev) if (dev) { conn->child_dev = to_coresight_device(dev); + /* and put reference from 'bus_find_device()' */ + put_device(dev); } else { csdev->orphan = true; conn->child_dev = NULL; From c7d05101bca353aef2e8d16605eca13737fa9b61 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 2 Feb 2016 14:13:59 -0700 Subject: [PATCH 026/104] coresight: remove csdev's link from topology In function 'coresight_unregister()', all references to the csdev that is being taken away need to be removed from the topology. Otherwise building the next coresight path from source to sink may use memory that has been released. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit ad725aee070caf8fa93d84d6fb78321f9642db18) --- drivers/hwtracing/coresight/coresight.c | 46 +++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index a35ca54a76c9..7e6e9ff27dd1 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -579,6 +579,50 @@ static void coresight_fixup_device_conns(struct coresight_device *csdev) } } +static int coresight_remove_match(struct device *dev, void *data) +{ + int i; + struct coresight_device *csdev, *iterator; + struct coresight_connection *conn; + + csdev = data; + iterator = to_coresight_device(dev); + + /* No need to check oneself */ + if (csdev == iterator) + return 0; + + /* + * Circle throuch all the connection of that component. If we find + * a connection whose name matches @csdev, remove it. + */ + for (i = 0; i < iterator->nr_outport; i++) { + conn = &iterator->conns[i]; + + if (conn->child_dev == NULL) + continue; + + if (!strcmp(dev_name(&csdev->dev), conn->child_name)) { + iterator->orphan = true; + conn->child_dev = NULL; + /* No need to continue */ + break; + } + } + + /* + * Returning '0' ensures that all known component on the + * bus will be checked. + */ + return 0; +} + +static void coresight_remove_conns(struct coresight_device *csdev) +{ + bus_for_each_dev(&coresight_bustype, NULL, + csdev, coresight_remove_match); +} + /** * coresight_timeout - loop until a bit has changed to a specific state. * @addr: base address of the area of interest. @@ -717,6 +761,8 @@ EXPORT_SYMBOL_GPL(coresight_register); void coresight_unregister(struct coresight_device *csdev) { + /* Remove references of that device in the topology */ + coresight_remove_conns(csdev); device_unregister(&csdev->dev); } EXPORT_SYMBOL_GPL(coresight_unregister); From b24ef072a99aeb7aeab9ced2a207c6a4af364d52 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 2 Feb 2016 14:14:00 -0700 Subject: [PATCH 027/104] coresight: removing bind/unbind options from sysfs The coresight drivers have absolutely no control over bind and unbind operations triggered from sysfs. The operations simply can't be cancelled or denied event when one or several tracing sessions are under way. Since the memory associated to individual device is invariably freed, the end result is a kernel crash when the path from source to sink is travelled again as demonstrated here[1]. One solution could be to keep track of all the path (i.e tracing session) that get created and iterate through the elements of those path looking for the coresight device that is being removed. This proposition doesn't scale well since there is no upper bound on the amount of concurrent trace session that can be created. With the above in mind, this patch prevent devices from being unbounded from their driver by using the driver->suppress_bind_attr option. That way trace sessions can be managed without fearing to loose devices. Since device can't be removed anymore the xyz_remove() functions found in each driver is also removed. [1]. http://www.spinics.net/lists/arm-kernel/msg474952.html Reported-by: Rabin Vincent Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit b15f0fb657e040401d875d11ae13b269af8a16e0) --- drivers/hwtracing/coresight/coresight-etb10.c | 11 +---------- drivers/hwtracing/coresight/coresight-etm3x.c | 13 +------------ drivers/hwtracing/coresight/coresight-etm4x.c | 13 +------------ drivers/hwtracing/coresight/coresight-funnel.c | 10 +--------- .../coresight/coresight-replicator-qcom.c | 11 +---------- .../hwtracing/coresight/coresight-replicator.c | 16 +--------------- drivers/hwtracing/coresight/coresight-tmc.c | 15 +-------------- drivers/hwtracing/coresight/coresight-tpiu.c | 10 +--------- 8 files changed, 8 insertions(+), 91 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 77d0f9c1118d..92969dae739d 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -489,15 +489,6 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id) return ret; } -static int etb_remove(struct amba_device *adev) -{ - struct etb_drvdata *drvdata = amba_get_drvdata(adev); - - misc_deregister(&drvdata->miscdev); - coresight_unregister(drvdata->csdev); - return 0; -} - #ifdef CONFIG_PM static int etb_runtime_suspend(struct device *dev) { @@ -537,10 +528,10 @@ static struct amba_driver etb_driver = { .name = "coresight-etb10", .owner = THIS_MODULE, .pm = &etb_dev_pm_ops, + .suppress_bind_attrs = true, }, .probe = etb_probe, - .remove = etb_remove, .id_table = etb_ids, }; diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index d630b7ece735..5981fcc69960 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -1877,17 +1877,6 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) return ret; } -static int etm_remove(struct amba_device *adev) -{ - struct etm_drvdata *drvdata = amba_get_drvdata(adev); - - coresight_unregister(drvdata->csdev); - if (--etm_count == 0) - unregister_hotcpu_notifier(&etm_cpu_notifier); - - return 0; -} - #ifdef CONFIG_PM static int etm_runtime_suspend(struct device *dev) { @@ -1948,9 +1937,9 @@ static struct amba_driver etm_driver = { .name = "coresight-etm3x", .owner = THIS_MODULE, .pm = &etm_dev_pm_ops, + .suppress_bind_attrs = true, }, .probe = etm_probe, - .remove = etm_remove, .id_table = etm_ids, }; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index a6707642bb23..167004f9c42b 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -2684,17 +2684,6 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) return ret; } -static int etm4_remove(struct amba_device *adev) -{ - struct etmv4_drvdata *drvdata = amba_get_drvdata(adev); - - coresight_unregister(drvdata->csdev); - if (--etm4_count == 0) - unregister_hotcpu_notifier(&etm4_cpu_notifier); - - return 0; -} - static struct amba_id etm4_ids[] = { { /* ETM 4.0 - Qualcomm */ .id = 0x0003b95d, @@ -2712,9 +2701,9 @@ static struct amba_id etm4_ids[] = { static struct amba_driver etm4x_driver = { .drv = { .name = "coresight-etm4x", + .suppress_bind_attrs = true, }, .probe = etm4_probe, - .remove = etm4_remove, .id_table = etm4_ids, }; diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 2e36bde7fcb4..25e8ea140a09 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -226,14 +226,6 @@ static int funnel_probe(struct amba_device *adev, const struct amba_id *id) return 0; } -static int funnel_remove(struct amba_device *adev) -{ - struct funnel_drvdata *drvdata = amba_get_drvdata(adev); - - coresight_unregister(drvdata->csdev); - return 0; -} - #ifdef CONFIG_PM static int funnel_runtime_suspend(struct device *dev) { @@ -273,9 +265,9 @@ static struct amba_driver funnel_driver = { .name = "coresight-funnel", .owner = THIS_MODULE, .pm = &funnel_dev_pm_ops, + .suppress_bind_attrs = true, }, .probe = funnel_probe, - .remove = funnel_remove, .id_table = funnel_ids, }; diff --git a/drivers/hwtracing/coresight/coresight-replicator-qcom.c b/drivers/hwtracing/coresight/coresight-replicator-qcom.c index 584059e9e866..444815179460 100644 --- a/drivers/hwtracing/coresight/coresight-replicator-qcom.c +++ b/drivers/hwtracing/coresight/coresight-replicator-qcom.c @@ -156,15 +156,6 @@ static int replicator_probe(struct amba_device *adev, const struct amba_id *id) return 0; } -static int replicator_remove(struct amba_device *adev) -{ - struct replicator_state *drvdata = amba_get_drvdata(adev); - - pm_runtime_disable(&adev->dev); - coresight_unregister(drvdata->csdev); - return 0; -} - #ifdef CONFIG_PM static int replicator_runtime_suspend(struct device *dev) { @@ -206,9 +197,9 @@ static struct amba_driver replicator_driver = { .drv = { .name = "coresight-replicator-qcom", .pm = &replicator_dev_pm_ops, + .suppress_bind_attrs = true, }, .probe = replicator_probe, - .remove = replicator_remove, .id_table = replicator_ids, }; diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index 963ac197c253..b77d700a3f0e 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -127,20 +127,6 @@ static int replicator_probe(struct platform_device *pdev) return ret; } -static int replicator_remove(struct platform_device *pdev) -{ - struct replicator_drvdata *drvdata = platform_get_drvdata(pdev); - - coresight_unregister(drvdata->csdev); - pm_runtime_get_sync(&pdev->dev); - if (!IS_ERR(drvdata->atclk)) - clk_disable_unprepare(drvdata->atclk); - pm_runtime_put_noidle(&pdev->dev); - pm_runtime_disable(&pdev->dev); - - return 0; -} - #ifdef CONFIG_PM static int replicator_runtime_suspend(struct device *dev) { @@ -175,11 +161,11 @@ static const struct of_device_id replicator_match[] = { static struct platform_driver replicator_driver = { .probe = replicator_probe, - .remove = replicator_remove, .driver = { .name = "coresight-replicator", .of_match_table = replicator_match, .pm = &replicator_dev_pm_ops, + .suppress_bind_attrs = true, }, }; diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index a57c7ec1661f..570d96815f78 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -770,19 +770,6 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) return ret; } -static int tmc_remove(struct amba_device *adev) -{ - struct tmc_drvdata *drvdata = amba_get_drvdata(adev); - - misc_deregister(&drvdata->miscdev); - coresight_unregister(drvdata->csdev); - if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) - dma_free_coherent(drvdata->dev, drvdata->size, - &drvdata->paddr, GFP_KERNEL); - - return 0; -} - static struct amba_id tmc_ids[] = { { .id = 0x0003b961, @@ -795,9 +782,9 @@ static struct amba_driver tmc_driver = { .drv = { .name = "coresight-tmc", .owner = THIS_MODULE, + .suppress_bind_attrs = true, }, .probe = tmc_probe, - .remove = tmc_remove, .id_table = tmc_ids, }; diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index 7214efd10db5..ca50e73d9df7 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -172,14 +172,6 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id) return 0; } -static int tpiu_remove(struct amba_device *adev) -{ - struct tpiu_drvdata *drvdata = amba_get_drvdata(adev); - - coresight_unregister(drvdata->csdev); - return 0; -} - #ifdef CONFIG_PM static int tpiu_runtime_suspend(struct device *dev) { @@ -223,9 +215,9 @@ static struct amba_driver tpiu_driver = { .name = "coresight-tpiu", .owner = THIS_MODULE, .pm = &tpiu_dev_pm_ops, + .suppress_bind_attrs = true, }, .probe = tpiu_probe, - .remove = tpiu_remove, .id_table = tpiu_ids, }; From 8a403a74c16625044c3bc83ae76791e562952727 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 2 Feb 2016 14:14:01 -0700 Subject: [PATCH 028/104] coresight: implementing 'cpu_id()' API Other than plainly parsing the device tree there is no way to know which CPU a tracer is affined to. As such adding an interface to lookup the CPU field enclosed in the etm_drvdata structure that was initialised at boot time. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 52210c8745e418f82f3f0aeeee01d7bc4858812a) --- drivers/hwtracing/coresight/coresight-etm3x.c | 8 ++++++++ drivers/hwtracing/coresight/coresight-etm4x.c | 8 ++++++++ include/linux/coresight.h | 3 +++ 3 files changed, 19 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 5981fcc69960..aae80e14508d 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -315,6 +315,13 @@ static void etm_enable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu); } +static int etm_cpu_id(struct coresight_device *csdev) +{ + struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + return drvdata->cpu; +} + static int etm_trace_id(struct coresight_device *csdev) { struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -421,6 +428,7 @@ static void etm_disable(struct coresight_device *csdev) } static const struct coresight_ops_source etm_source_ops = { + .cpu_id = etm_cpu_id, .trace_id = etm_trace_id, .enable = etm_enable, .disable = etm_disable, diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 167004f9c42b..b6ae9cb6ff57 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -63,6 +63,13 @@ static bool etm4_arch_supported(u8 arch) return true; } +static int etm4_cpu_id(struct coresight_device *csdev) +{ + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + return drvdata->cpu; +} + static int etm4_trace_id(struct coresight_device *csdev) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -262,6 +269,7 @@ static void etm4_disable(struct coresight_device *csdev) } static const struct coresight_ops_source etm4_source_ops = { + .cpu_id = etm4_cpu_id, .trace_id = etm4_trace_id, .enable = etm4_enable, .disable = etm4_disable, diff --git a/include/linux/coresight.h b/include/linux/coresight.h index a7cabfa23b55..bf62b265bf52 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -205,12 +205,15 @@ struct coresight_ops_link { /** * struct coresight_ops_source - basic operations for a source * Operations available for sources. + * @cpu_id: returns the value of the CPU number this component + * is associated to. * @trace_id: returns the value of the component's trace ID as known to the HW. * @enable: enables tracing for a source. * @disable: disables tracing for a source. */ struct coresight_ops_source { + int (*cpu_id)(struct coresight_device *csdev); int (*trace_id)(struct coresight_device *csdev); int (*enable)(struct coresight_device *csdev); void (*disable)(struct coresight_device *csdev); From fea6fe15e3ac5fae3f220c17f4f56601bbcf4d02 Mon Sep 17 00:00:00 2001 From: Eric Long Date: Wed, 17 Feb 2016 17:51:43 -0700 Subject: [PATCH 029/104] coresight: "DEVICE_ATTR_RO" should defined as static. "DEVICE_ATTR_RO(name)" should be defined as static. And there is an unnecessary space at the front of the code. The sparse tool output logs as the following: coresight-etm4x.c:2224:1: warning: symbol 'dev_attr_trcoslsr' was not declared. Should it be static? coresight-etm4x.c:2225:1: warning: symbol 'dev_attr_trcpdcr' was not declared. Should it be static? coresight-etm4x.c:2226:1: warning: symbol 'dev_attr_trcpdsr' was not declared. Should it be static? And the smatch tool output logs as the following: of_coresight.c:89 of_coresight_alloc_memory() warn: inconsistent indenting Signed-off-by: Eric Long Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit bf16e5b8cdeabc1fe6565af0be475bb2084dc388) --- drivers/hwtracing/coresight/coresight-etm4x.c | 2 +- drivers/hwtracing/coresight/of_coresight.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index b6ae9cb6ff57..d6a92c6d3a66 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -2227,7 +2227,7 @@ static ssize_t name##_show(struct device *_dev, \ return scnprintf(buf, PAGE_SIZE, "0x%x\n", \ readl_relaxed(drvdata->base + offset)); \ } \ -DEVICE_ATTR_RO(name) +static DEVICE_ATTR_RO(name) coresight_simple_func(trcoslsr, TRCOSLSR); coresight_simple_func(trcpdcr, TRCPDCR); diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c index b0973617826f..3cc57c1e3b5d 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c @@ -86,7 +86,7 @@ static int of_coresight_alloc_memory(struct device *dev, return -ENOMEM; /* Children connected to this component via @outports */ - pdata->child_names = devm_kzalloc(dev, pdata->nr_outport * + pdata->child_names = devm_kzalloc(dev, pdata->nr_outport * sizeof(*pdata->child_names), GFP_KERNEL); if (!pdata->child_names) From e1e8321c38b7e47ca9b8095b584dc89bef46504c Mon Sep 17 00:00:00 2001 From: Eric Long Date: Wed, 17 Feb 2016 17:51:44 -0700 Subject: [PATCH 030/104] coresight: etm4x: Check every parameter used by dma_xx_coherent. The dma_alloc_coherent return an "void *" not an "void __iomen *". It uses the wrong parameters when calls dma_free_coherent function. The sparse tool output logs as the following: coresight-tmc.c:199:23: expected void * coresight-tmc.c:199:23: got void [noderef] *vaddr coresight-tmc.c:336:30: warning: incorrect type in assignment (different address spaces) coresight-tmc.c:336:30: expected char *buf coresight-tmc.c:336:30: got void [noderef] * coresight-tmc.c:769:50: warning: incorrect type in argument 4 (different base types) coresight-tmc.c:769:50: expected unsigned long long [unsigned] [usertype] dma_handle coresight-tmc.c:769:50: got restricted gfp_t Signed-off-by: Eric Long Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 61390593f72377c3a8f41ef998462e2d3985adac) --- drivers/hwtracing/coresight/coresight-tmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 570d96815f78..c4fa70ed14ce 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -124,7 +124,7 @@ struct tmc_drvdata { bool reading; char *buf; dma_addr_t paddr; - void __iomem *vaddr; + void *vaddr; u32 size; bool enable; enum tmc_config_type config_type; @@ -766,7 +766,7 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) err_devm_kzalloc: if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) dma_free_coherent(dev, drvdata->size, - &drvdata->paddr, GFP_KERNEL); + drvdata->vaddr, drvdata->paddr); return ret; } From fde79d4de7443a7e3cca1dd05d4a789ebc490a53 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:45 -0700 Subject: [PATCH 031/104] coresight: associating path with session rather than tracer When using the Coresight framework from the sysFS interface a tracer is always handling a single session and as such, a path can be associated with a tracer. But when supporting multiple session per tracer there is no guarantee that sessions will always have the same path from source to sink. This patch is removing the automatic association between path and tracers. The building of a path and enablement of the components in the path are decoupled, allowing for the association of a path with a session rather than a tracer. To keep backward functionality with the current sysFS access methods a per-cpu place holder is used to keep a handle on the path built when tracers are enabled. Lastly APIs to build paths and enable tracers are made public so that other subsystem can interact with the Coresight framework. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit b3e94405941e6916d5e365454d74560c2bea47ca) --- drivers/hwtracing/coresight/coresight-priv.h | 5 + drivers/hwtracing/coresight/coresight.c | 310 +++++++++++++------ include/linux/coresight.h | 2 - 3 files changed, 213 insertions(+), 104 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 62fcd98cc7cf..7b193a34d709 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -52,6 +52,11 @@ static inline void CS_UNLOCK(void __iomem *addr) } while (0); } +void coresight_disable_path(struct list_head *path); +int coresight_enable_path(struct list_head *path); +struct list_head *coresight_build_path(struct coresight_device *csdev); +void coresight_release_path(struct list_head *path); + #ifdef CONFIG_CORESIGHT_SOURCE_ETM3X extern int etm_readl_cp14(u32 off, unsigned int *val); extern int etm_writel_cp14(u32 off, u32 val); diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 7e6e9ff27dd1..f26589effb70 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -29,6 +29,22 @@ static DEFINE_MUTEX(coresight_mutex); +/** + * struct coresight_node - elements of a path, from source to sink + * @csdev: Address of an element. + * @link: hook to the list. + */ +struct coresight_node { + struct coresight_device *csdev; + struct list_head link; +}; + +/* + * When operating Coresight drivers from the sysFS interface, only a single + * path can exist from a tracer (associated to a CPU) to a sink. + */ +static DEFINE_PER_CPU(struct list_head *, sysfs_path); + static int coresight_id_match(struct device *dev, void *data) { int trace_id, i_trace_id; @@ -68,15 +84,12 @@ static int coresight_source_is_unique(struct coresight_device *csdev) csdev, coresight_id_match); } -static int coresight_find_link_inport(struct coresight_device *csdev) +static int coresight_find_link_inport(struct coresight_device *csdev, + struct coresight_device *parent) { int i; - struct coresight_device *parent; struct coresight_connection *conn; - parent = container_of(csdev->path_link.next, - struct coresight_device, path_link); - for (i = 0; i < parent->nr_outport; i++) { conn = &parent->conns[i]; if (conn->child_dev == csdev) @@ -89,15 +102,12 @@ static int coresight_find_link_inport(struct coresight_device *csdev) return 0; } -static int coresight_find_link_outport(struct coresight_device *csdev) +static int coresight_find_link_outport(struct coresight_device *csdev, + struct coresight_device *child) { int i; - struct coresight_device *child; struct coresight_connection *conn; - child = container_of(csdev->path_link.prev, - struct coresight_device, path_link); - for (i = 0; i < csdev->nr_outport; i++) { conn = &csdev->conns[i]; if (conn->child_dev == child) @@ -138,14 +148,19 @@ static void coresight_disable_sink(struct coresight_device *csdev) } } -static int coresight_enable_link(struct coresight_device *csdev) +static int coresight_enable_link(struct coresight_device *csdev, + struct coresight_device *parent, + struct coresight_device *child) { int ret; int link_subtype; int refport, inport, outport; - inport = coresight_find_link_inport(csdev); - outport = coresight_find_link_outport(csdev); + if (!parent || !child) + return -EINVAL; + + inport = coresight_find_link_inport(csdev, parent); + outport = coresight_find_link_outport(csdev, child); link_subtype = csdev->subtype.link_subtype; if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) @@ -168,14 +183,19 @@ static int coresight_enable_link(struct coresight_device *csdev) return 0; } -static void coresight_disable_link(struct coresight_device *csdev) +static void coresight_disable_link(struct coresight_device *csdev, + struct coresight_device *parent, + struct coresight_device *child) { int i, nr_conns; int link_subtype; int refport, inport, outport; - inport = coresight_find_link_inport(csdev); - outport = coresight_find_link_outport(csdev); + if (!parent || !child) + return; + + inport = coresight_find_link_inport(csdev, parent); + outport = coresight_find_link_outport(csdev, child); link_subtype = csdev->subtype.link_subtype; if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) { @@ -235,109 +255,167 @@ static void coresight_disable_source(struct coresight_device *csdev) } } -static int coresight_enable_path(struct list_head *path) +void coresight_disable_path(struct list_head *path) { + struct coresight_node *nd; + struct coresight_device *csdev, *parent, *child; + + list_for_each_entry(nd, path, link) { + csdev = nd->csdev; + + switch (csdev->type) { + case CORESIGHT_DEV_TYPE_SINK: + case CORESIGHT_DEV_TYPE_LINKSINK: + coresight_disable_sink(csdev); + break; + case CORESIGHT_DEV_TYPE_SOURCE: + /* sources are disabled from either sysFS or Perf */ + break; + case CORESIGHT_DEV_TYPE_LINK: + parent = list_prev_entry(nd, link)->csdev; + child = list_next_entry(nd, link)->csdev; + coresight_disable_link(csdev, parent, child); + break; + default: + break; + } + } +} + +int coresight_enable_path(struct list_head *path) +{ + int ret = 0; - struct coresight_device *cd; + struct coresight_node *nd; + struct coresight_device *csdev, *parent, *child; - /* - * At this point we have a full @path, from source to sink. The - * sink is the first entry and the source the last one. Go through - * all the components and enable them one by one. - */ - list_for_each_entry(cd, path, path_link) { - if (cd == list_first_entry(path, struct coresight_device, - path_link)) { - ret = coresight_enable_sink(cd); - } else if (list_is_last(&cd->path_link, path)) { - /* - * Don't enable the source just yet - this needs to - * happen at the very end when all links and sink - * along the path have been configured properly. - */ - ; - } else { - ret = coresight_enable_link(cd); - } - if (ret) + list_for_each_entry_reverse(nd, path, link) { + csdev = nd->csdev; + + switch (csdev->type) { + case CORESIGHT_DEV_TYPE_SINK: + case CORESIGHT_DEV_TYPE_LINKSINK: + ret = coresight_enable_sink(csdev); + if (ret) + goto err; + break; + case CORESIGHT_DEV_TYPE_SOURCE: + /* sources are enabled from either sysFS or Perf */ + break; + case CORESIGHT_DEV_TYPE_LINK: + parent = list_prev_entry(nd, link)->csdev; + child = list_next_entry(nd, link)->csdev; + ret = coresight_enable_link(csdev, parent, child); + if (ret) + goto err; + break; + default: goto err; - } - - return 0; -err: - list_for_each_entry_continue_reverse(cd, path, path_link) { - if (cd == list_first_entry(path, struct coresight_device, - path_link)) { - coresight_disable_sink(cd); - } else if (list_is_last(&cd->path_link, path)) { - ; - } else { - coresight_disable_link(cd); } } +out: return ret; +err: + coresight_disable_path(path); + goto out; } -static int coresight_disable_path(struct list_head *path) +/** + * _coresight_build_path - recursively build a path from a @csdev to a sink. + * @csdev: The device to start from. + * @path: The list to add devices to. + * + * The tree of Coresight device is traversed until an activated sink is + * found. From there the sink is added to the list along with all the + * devices that led to that point - the end result is a list from source + * to sink. In that list the source is the first device and the sink the + * last one. + */ +static int _coresight_build_path(struct coresight_device *csdev, + struct list_head *path) { - struct coresight_device *cd; - - list_for_each_entry_reverse(cd, path, path_link) { - if (cd == list_first_entry(path, struct coresight_device, - path_link)) { - coresight_disable_sink(cd); - } else if (list_is_last(&cd->path_link, path)) { - /* - * The source has already been stopped, no need - * to do it again here. - */ - ; - } else { - coresight_disable_link(cd); - } - } - - return 0; -} - -static int coresight_build_paths(struct coresight_device *csdev, - struct list_head *path, - bool enable) -{ - int i, ret = -EINVAL; + int i; + bool found = false; + struct coresight_node *node; struct coresight_connection *conn; - list_add(&csdev->path_link, path); - + /* An activated sink has been found. Enqueue the element */ if ((csdev->type == CORESIGHT_DEV_TYPE_SINK || - csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) && - csdev->activated) { - if (enable) - ret = coresight_enable_path(path); - else - ret = coresight_disable_path(path); - } else { - for (i = 0; i < csdev->nr_outport; i++) { - conn = &csdev->conns[i]; - if (coresight_build_paths(conn->child_dev, - path, enable) == 0) - ret = 0; + csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) && csdev->activated) + goto out; + + /* Not a sink - recursively explore each port found on this element */ + for (i = 0; i < csdev->nr_outport; i++) { + conn = &csdev->conns[i]; + if (_coresight_build_path(conn->child_dev, path) == 0) { + found = true; + break; } } - if (list_first_entry(path, struct coresight_device, path_link) != csdev) - dev_err(&csdev->dev, "wrong device in %s\n", __func__); + if (!found) + return -ENODEV; - list_del(&csdev->path_link); +out: + /* + * A path from this element to a sink has been found. The elements + * leading to the sink are already enqueued, all that is left to do + * is add a node for this element. + */ + node = kzalloc(sizeof(struct coresight_node), GFP_KERNEL); + if (!node) + return -ENOMEM; - return ret; + node->csdev = csdev; + list_add(&node->link, path); + + return 0; +} + +struct list_head *coresight_build_path(struct coresight_device *csdev) +{ + struct list_head *path; + + path = kzalloc(sizeof(struct list_head), GFP_KERNEL); + if (!path) + return NULL; + + INIT_LIST_HEAD(path); + + if (_coresight_build_path(csdev, path)) { + kfree(path); + path = NULL; + } + + return path; +} + +/** + * coresight_release_path - release a previously built path. + * @path: the path to release. + * + * Go through all the elements of a path and 1) removed it from the list and + * 2) free the memory allocated for each node. + */ +void coresight_release_path(struct list_head *path) +{ + struct coresight_node *nd, *next; + + list_for_each_entry_safe(nd, next, path, link) { + list_del(&nd->link); + kfree(nd); + } + + kfree(path); + path = NULL; } int coresight_enable(struct coresight_device *csdev) { int ret = 0; - LIST_HEAD(path); + int cpu; + struct list_head *path; mutex_lock(&coresight_mutex); if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) { @@ -348,22 +426,47 @@ int coresight_enable(struct coresight_device *csdev) if (csdev->enable) goto out; - if (coresight_build_paths(csdev, &path, true)) { - dev_err(&csdev->dev, "building path(s) failed\n"); + path = coresight_build_path(csdev); + if (!path) { + pr_err("building path(s) failed\n"); goto out; } - if (coresight_enable_source(csdev)) - dev_err(&csdev->dev, "source enable failed\n"); + ret = coresight_enable_path(path); + if (ret) + goto err_path; + + ret = coresight_enable_source(csdev); + if (ret) + goto err_source; + + /* + * When working from sysFS it is important to keep track + * of the paths that were created so that they can be + * undone in 'coresight_disable()'. Since there can only + * be a single session per tracer (when working from sysFS) + * a per-cpu variable will do just fine. + */ + cpu = source_ops(csdev)->cpu_id(csdev); + per_cpu(sysfs_path, cpu) = path; + out: mutex_unlock(&coresight_mutex); return ret; + +err_source: + coresight_disable_path(path); + +err_path: + coresight_release_path(path); + goto out; } EXPORT_SYMBOL_GPL(coresight_enable); void coresight_disable(struct coresight_device *csdev) { - LIST_HEAD(path); + int cpu; + struct list_head *path; mutex_lock(&coresight_mutex); if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) { @@ -373,9 +476,12 @@ void coresight_disable(struct coresight_device *csdev) if (!csdev->enable) goto out; + cpu = source_ops(csdev)->cpu_id(csdev); + path = per_cpu(sysfs_path, cpu); coresight_disable_source(csdev); - if (coresight_build_paths(csdev, &path, false)) - dev_err(&csdev->dev, "releasing path(s) failed\n"); + coresight_disable_path(path); + coresight_release_path(path); + per_cpu(sysfs_path, cpu) = NULL; out: mutex_unlock(&coresight_mutex); diff --git a/include/linux/coresight.h b/include/linux/coresight.h index bf62b265bf52..851ecb22397e 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -152,7 +152,6 @@ struct coresight_connection { by @coresight_ops. * @dev: The device entity associated to this component. * @refcnt: keep track of what is in use. - * @path_link: link of current component into the path being enabled. * @orphan: true if the component has connections that haven't been linked. * @enable: 'true' if component is currently part of an active path. * @activated: 'true' only if a _sink_ has been activated. A sink can be @@ -168,7 +167,6 @@ struct coresight_device { const struct coresight_ops *ops; struct device dev; atomic_t *refcnt; - struct list_head path_link; 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 */ From f72b3bc46dab124c25d84575d3addab8ef7c68cd Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:46 -0700 Subject: [PATCH 032/104] coresight: add API to get sink from path Add an API allowing external code to quickly get a handle on the sink within a path. The sink is always last, but adding an API allows to keep the path's node structure private and remove redundant checks. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit b6404e21f023e4aa208a0ba03d55a9c8a57cb940) --- drivers/hwtracing/coresight/coresight-priv.h | 1 + drivers/hwtracing/coresight/coresight.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 7b193a34d709..14f245a2018d 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -54,6 +54,7 @@ static inline void CS_UNLOCK(void __iomem *addr) void coresight_disable_path(struct list_head *path); int coresight_enable_path(struct list_head *path); +struct coresight_device *coresight_get_sink(struct list_head *path); struct list_head *coresight_build_path(struct coresight_device *csdev); void coresight_release_path(struct list_head *path); diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index f26589effb70..2ed48545df26 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -321,6 +321,21 @@ int coresight_enable_path(struct list_head *path) goto out; } +struct coresight_device *coresight_get_sink(struct list_head *path) +{ + struct coresight_device *csdev; + + if (!path) + return NULL; + + csdev = list_last_entry(path, struct coresight_node, link)->csdev; + if (csdev->type != CORESIGHT_DEV_TYPE_SINK && + csdev->type != CORESIGHT_DEV_TYPE_LINKSINK) + return NULL; + + return csdev; +} + /** * _coresight_build_path - recursively build a path from a @csdev to a sink. * @csdev: The device to start from. From f7e289557fa79b9aa25a3e57717f9538b5a88098 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:47 -0700 Subject: [PATCH 033/104] coresight: moving PM runtime operations to core framework Moving PM runtime operations in Coresight devices enable() and disable() API to the framework core when a path is setup. That way the runtime core doesn't have to be involved everytime a path is enabled. It also avoids calling runtime PM operations in IRQ context. Signed-off-by: Mathieu Poirier Reviewed-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 5da5325fa85658ee793792b5285dd5fdb76ccfb7) --- drivers/hwtracing/coresight/coresight-etb10.c | 4 ---- drivers/hwtracing/coresight/coresight-etm3x.c | 3 --- drivers/hwtracing/coresight/coresight-etm4x.c | 6 ------ drivers/hwtracing/coresight/coresight-funnel.c | 2 -- drivers/hwtracing/coresight/coresight-replicator-qcom.c | 4 ---- drivers/hwtracing/coresight/coresight-replicator.c | 2 -- drivers/hwtracing/coresight/coresight-tmc.c | 5 ----- drivers/hwtracing/coresight/coresight-tpiu.c | 2 -- drivers/hwtracing/coresight/coresight.c | 9 ++++++++- 9 files changed, 8 insertions(+), 29 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 92969dae739d..917562ecf82a 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -137,8 +137,6 @@ static int etb_enable(struct coresight_device *csdev) struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); unsigned long flags; - pm_runtime_get_sync(drvdata->dev); - spin_lock_irqsave(&drvdata->spinlock, flags); etb_enable_hw(drvdata); drvdata->enable = true; @@ -247,8 +245,6 @@ static void etb_disable(struct coresight_device *csdev) drvdata->enable = false; spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); - dev_info(drvdata->dev, "ETB disabled\n"); } diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index aae80e14508d..fe6791e0c66c 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -349,7 +349,6 @@ static int etm_enable(struct coresight_device *csdev) struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); int ret; - pm_runtime_get_sync(csdev->dev.parent); spin_lock(&drvdata->spinlock); /* @@ -373,7 +372,6 @@ static int etm_enable(struct coresight_device *csdev) return 0; err: spin_unlock(&drvdata->spinlock); - pm_runtime_put(csdev->dev.parent); return ret; } @@ -422,7 +420,6 @@ static void etm_disable(struct coresight_device *csdev) spin_unlock(&drvdata->spinlock); put_online_cpus(); - pm_runtime_put(csdev->dev.parent); dev_info(drvdata->dev, "ETM tracing disabled\n"); } diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index d6a92c6d3a66..c2f518fbc9a8 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -79,7 +79,6 @@ static int etm4_trace_id(struct coresight_device *csdev) if (!drvdata->enable) return drvdata->trcid; - pm_runtime_get_sync(drvdata->dev); spin_lock_irqsave(&drvdata->spinlock, flags); CS_UNLOCK(drvdata->base); @@ -88,7 +87,6 @@ static int etm4_trace_id(struct coresight_device *csdev) CS_LOCK(drvdata->base); spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); return trace_id; } @@ -194,7 +192,6 @@ static int etm4_enable(struct coresight_device *csdev) struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); int ret; - pm_runtime_get_sync(drvdata->dev); spin_lock(&drvdata->spinlock); /* @@ -214,7 +211,6 @@ static int etm4_enable(struct coresight_device *csdev) return 0; err: spin_unlock(&drvdata->spinlock); - pm_runtime_put(drvdata->dev); return ret; } @@ -263,8 +259,6 @@ static void etm4_disable(struct coresight_device *csdev) spin_unlock(&drvdata->spinlock); put_online_cpus(); - pm_runtime_put(drvdata->dev); - dev_info(drvdata->dev, "ETM tracing disabled\n"); } diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 25e8ea140a09..fb2c679fbc44 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -69,7 +69,6 @@ static int funnel_enable(struct coresight_device *csdev, int inport, { struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - pm_runtime_get_sync(drvdata->dev); funnel_enable_hw(drvdata, inport); dev_info(drvdata->dev, "FUNNEL inport %d enabled\n", inport); @@ -95,7 +94,6 @@ static void funnel_disable(struct coresight_device *csdev, int inport, struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); funnel_disable_hw(drvdata, inport); - pm_runtime_put(drvdata->dev); dev_info(drvdata->dev, "FUNNEL inport %d disabled\n", inport); } diff --git a/drivers/hwtracing/coresight/coresight-replicator-qcom.c b/drivers/hwtracing/coresight/coresight-replicator-qcom.c index 444815179460..286f90b50989 100644 --- a/drivers/hwtracing/coresight/coresight-replicator-qcom.c +++ b/drivers/hwtracing/coresight/coresight-replicator-qcom.c @@ -48,8 +48,6 @@ static int replicator_enable(struct coresight_device *csdev, int inport, { struct replicator_state *drvdata = dev_get_drvdata(csdev->dev.parent); - pm_runtime_get_sync(drvdata->dev); - CS_UNLOCK(drvdata->base); /* @@ -86,8 +84,6 @@ static void replicator_disable(struct coresight_device *csdev, int inport, CS_LOCK(drvdata->base); - pm_runtime_put(drvdata->dev); - dev_info(drvdata->dev, "REPLICATOR disabled\n"); } diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index b77d700a3f0e..0ce98903992c 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -41,7 +41,6 @@ static int replicator_enable(struct coresight_device *csdev, int inport, { struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - pm_runtime_get_sync(drvdata->dev); dev_info(drvdata->dev, "REPLICATOR enabled\n"); return 0; } @@ -51,7 +50,6 @@ static void replicator_disable(struct coresight_device *csdev, int inport, { struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - pm_runtime_put(drvdata->dev); dev_info(drvdata->dev, "REPLICATOR disabled\n"); } diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index c4fa70ed14ce..b526396d80b6 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -242,12 +242,9 @@ static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode) { unsigned long flags; - pm_runtime_get_sync(drvdata->dev); - spin_lock_irqsave(&drvdata->spinlock, flags); if (drvdata->reading) { spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); return -EBUSY; } @@ -381,8 +378,6 @@ static void tmc_disable(struct tmc_drvdata *drvdata, enum tmc_mode mode) drvdata->enable = false; spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); - dev_info(drvdata->dev, "TMC disabled\n"); } diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index ca50e73d9df7..0d8fce651ff7 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -74,7 +74,6 @@ static int tpiu_enable(struct coresight_device *csdev) { struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - pm_runtime_get_sync(csdev->dev.parent); tpiu_enable_hw(drvdata); dev_info(drvdata->dev, "TPIU enabled\n"); @@ -98,7 +97,6 @@ static void tpiu_disable(struct coresight_device *csdev) struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); tpiu_disable_hw(drvdata); - pm_runtime_put(csdev->dev.parent); dev_info(drvdata->dev, "TPIU disabled\n"); } diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 2ed48545df26..6b44928c1076 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "coresight-priv.h" @@ -376,7 +377,8 @@ static int _coresight_build_path(struct coresight_device *csdev, /* * A path from this element to a sink has been found. The elements * leading to the sink are already enqueued, all that is left to do - * is add a node for this element. + * is tell the PM runtime core we need this element and add a node + * for it. */ node = kzalloc(sizeof(struct coresight_node), GFP_KERNEL); if (!node) @@ -384,6 +386,7 @@ static int _coresight_build_path(struct coresight_device *csdev, node->csdev = csdev; list_add(&node->link, path); + pm_runtime_get_sync(csdev->dev.parent); return 0; } @@ -415,9 +418,13 @@ struct list_head *coresight_build_path(struct coresight_device *csdev) */ void coresight_release_path(struct list_head *path) { + struct coresight_device *csdev; struct coresight_node *nd, *next; list_for_each_entry_safe(nd, next, path, link) { + csdev = nd->csdev; + + pm_runtime_put_sync(csdev->dev.parent); list_del(&nd->link); kfree(nd); } From 46f118189e6870463bbd5bac9f818dcad64922de Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:48 -0700 Subject: [PATCH 034/104] coresight: etm3x: moving etm_readl/writel to header file Moving functions etm_readl/writel to file "coresight-etm.h" so that the main ETM3x driver can be split in more than one file. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit c1f8e57c9e6692f6e8c6c1f9eab7a46264ac4245) --- drivers/hwtracing/coresight/coresight-etm.h | 29 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-etm3x.c | 29 ------------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index b4481eb29304..34f7db881fa7 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -251,4 +251,33 @@ enum etm_addr_type { ETM_ADDR_TYPE_START, ETM_ADDR_TYPE_STOP, }; + +static inline void etm_writel(struct etm_drvdata *drvdata, + u32 val, u32 off) +{ + if (drvdata->use_cp14) { + if (etm_writel_cp14(off, val)) { + dev_err(drvdata->dev, + "invalid CP14 access to ETM reg: %#x", off); + } + } else { + writel_relaxed(val, drvdata->base + off); + } +} + +static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off) +{ + u32 val; + + if (drvdata->use_cp14) { + if (etm_readl_cp14(off, &val)) { + dev_err(drvdata->dev, + "invalid CP14 access to ETM reg: %#x", off); + } + } else { + val = readl_relaxed(drvdata->base + off); + } + + return val; +} #endif diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index fe6791e0c66c..d53440e9af6f 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -42,35 +42,6 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO); static int etm_count; static struct etm_drvdata *etmdrvdata[NR_CPUS]; -static inline void etm_writel(struct etm_drvdata *drvdata, - u32 val, u32 off) -{ - if (drvdata->use_cp14) { - if (etm_writel_cp14(off, val)) { - dev_err(drvdata->dev, - "invalid CP14 access to ETM reg: %#x", off); - } - } else { - writel_relaxed(val, drvdata->base + off); - } -} - -static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off) -{ - u32 val; - - if (drvdata->use_cp14) { - if (etm_readl_cp14(off, &val)) { - dev_err(drvdata->dev, - "invalid CP14 access to ETM reg: %#x", off); - } - } else { - val = readl_relaxed(drvdata->base + off); - } - - return val; -} - /* * Memory mapped writes to clear os lock are not supported on some processors * and OS lock must be unlocked before any memory mapped access on such From dd1e17528c41f41b06287bf7f2b47a078628b87a Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:49 -0700 Subject: [PATCH 035/104] coresight: etm3x: moving sysFS entries to dedicated file SysFS entries are big enough to justify their own file. As such moving all sysFS related declarations to a dedicated location. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit c04148e708c0d8d7eabc447946b712a66b468b47) --- drivers/hwtracing/coresight/Makefile | 3 +- drivers/hwtracing/coresight/coresight-etm.h | 4 + .../coresight/coresight-etm3x-sysfs.c | 1218 ++++++++++++++++ drivers/hwtracing/coresight/coresight-etm3x.c | 1234 +---------------- 4 files changed, 1241 insertions(+), 1218 deletions(-) create mode 100644 drivers/hwtracing/coresight/coresight-etm3x-sysfs.c diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 99f8e5f6256e..233d66cf22d3 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_CORESIGHT_SINK_TPIU) += coresight-tpiu.o obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ coresight-replicator.o -obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o +obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ + coresight-etm3x-sysfs.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index 34f7db881fa7..9a30aa392ed9 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -280,4 +280,8 @@ static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off) return val; } + +extern const struct attribute_group *coresight_etm_groups[]; +int etm_get_trace_id(struct etm_drvdata *drvdata); +void etm_set_default(struct etm_drvdata *drvdata); #endif diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c new file mode 100644 index 000000000000..f409f5a88e95 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -0,0 +1,1218 @@ +/* + * Copyright(C) 2015 Linaro Limited. All rights reserved. + * Author: Mathieu Poirier + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include "coresight-etm.h" + +static ssize_t nr_addr_cmp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->nr_addr_cmp; + return sprintf(buf, "%#lx\n", val); +} +static DEVICE_ATTR_RO(nr_addr_cmp); + +static ssize_t nr_cntr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->nr_cntr; + return sprintf(buf, "%#lx\n", val); +} +static DEVICE_ATTR_RO(nr_cntr); + +static ssize_t nr_ctxid_cmp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->nr_ctxid_cmp; + return sprintf(buf, "%#lx\n", val); +} +static DEVICE_ATTR_RO(nr_ctxid_cmp); + +static ssize_t etmsr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long flags, val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + pm_runtime_get_sync(drvdata->dev); + spin_lock_irqsave(&drvdata->spinlock, flags); + CS_UNLOCK(drvdata->base); + + val = etm_readl(drvdata, ETMSR); + + CS_LOCK(drvdata->base); + spin_unlock_irqrestore(&drvdata->spinlock, flags); + pm_runtime_put(drvdata->dev); + + return sprintf(buf, "%#lx\n", val); +} +static DEVICE_ATTR_RO(etmsr); + +static ssize_t reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int i, ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + if (val) { + spin_lock(&drvdata->spinlock); + drvdata->mode = ETM_MODE_EXCLUDE; + drvdata->ctrl = 0x0; + drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL; + drvdata->startstop_ctrl = 0x0; + drvdata->addr_idx = 0x0; + for (i = 0; i < drvdata->nr_addr_cmp; i++) { + drvdata->addr_val[i] = 0x0; + drvdata->addr_acctype[i] = 0x0; + drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE; + } + drvdata->cntr_idx = 0x0; + + etm_set_default(drvdata); + spin_unlock(&drvdata->spinlock); + } + + return size; +} +static DEVICE_ATTR_WO(reset); + +static ssize_t mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->mode; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + drvdata->mode = val & ETM_MODE_ALL; + + if (drvdata->mode & ETM_MODE_EXCLUDE) + drvdata->enable_ctrl1 |= ETMTECR1_INC_EXC; + else + drvdata->enable_ctrl1 &= ~ETMTECR1_INC_EXC; + + if (drvdata->mode & ETM_MODE_CYCACC) + drvdata->ctrl |= ETMCR_CYC_ACC; + else + drvdata->ctrl &= ~ETMCR_CYC_ACC; + + if (drvdata->mode & ETM_MODE_STALL) { + if (!(drvdata->etmccr & ETMCCR_FIFOFULL)) { + dev_warn(drvdata->dev, "stall mode not supported\n"); + ret = -EINVAL; + goto err_unlock; + } + drvdata->ctrl |= ETMCR_STALL_MODE; + } else + drvdata->ctrl &= ~ETMCR_STALL_MODE; + + if (drvdata->mode & ETM_MODE_TIMESTAMP) { + if (!(drvdata->etmccer & ETMCCER_TIMESTAMP)) { + dev_warn(drvdata->dev, "timestamp not supported\n"); + ret = -EINVAL; + goto err_unlock; + } + drvdata->ctrl |= ETMCR_TIMESTAMP_EN; + } else + drvdata->ctrl &= ~ETMCR_TIMESTAMP_EN; + + if (drvdata->mode & ETM_MODE_CTXID) + drvdata->ctrl |= ETMCR_CTXID_SIZE; + else + drvdata->ctrl &= ~ETMCR_CTXID_SIZE; + spin_unlock(&drvdata->spinlock); + + return size; + +err_unlock: + spin_unlock(&drvdata->spinlock); + return ret; +} +static DEVICE_ATTR_RW(mode); + +static ssize_t trigger_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->trigger_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t trigger_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->trigger_event = val & ETM_EVENT_MASK; + + return size; +} +static DEVICE_ATTR_RW(trigger_event); + +static ssize_t enable_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->enable_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t enable_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->enable_event = val & ETM_EVENT_MASK; + + return size; +} +static DEVICE_ATTR_RW(enable_event); + +static ssize_t fifofull_level_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->fifofull_level; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t fifofull_level_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->fifofull_level = val; + + return size; +} +static DEVICE_ATTR_RW(fifofull_level); + +static ssize_t addr_idx_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->addr_idx; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t addr_idx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + if (val >= drvdata->nr_addr_cmp) + return -EINVAL; + + /* + * Use spinlock to ensure index doesn't change while it gets + * dereferenced multiple times within a spinlock block elsewhere. + */ + spin_lock(&drvdata->spinlock); + drvdata->addr_idx = val; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(addr_idx); + +static ssize_t addr_single_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 idx; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { + spin_unlock(&drvdata->spinlock); + return -EINVAL; + } + + val = drvdata->addr_val[idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t addr_single_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { + spin_unlock(&drvdata->spinlock); + return -EINVAL; + } + + drvdata->addr_val[idx] = val; + drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(addr_single); + +static ssize_t addr_range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 idx; + unsigned long val1, val2; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (idx % 2 != 0) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && + drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || + (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && + drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + val1 = drvdata->addr_val[idx]; + val2 = drvdata->addr_val[idx + 1]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx %#lx\n", val1, val2); +} + +static ssize_t addr_range_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val1, val2; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) + return -EINVAL; + /* Lower address comparator cannot have a higher address value */ + if (val1 > val2) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (idx % 2 != 0) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && + drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || + (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && + drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + drvdata->addr_val[idx] = val1; + drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE; + drvdata->addr_val[idx + 1] = val2; + drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE; + drvdata->enable_ctrl1 |= (1 << (idx/2)); + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(addr_range); + +static ssize_t addr_start_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 idx; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + val = drvdata->addr_val[idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t addr_start_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + drvdata->addr_val[idx] = val; + drvdata->addr_type[idx] = ETM_ADDR_TYPE_START; + drvdata->startstop_ctrl |= (1 << idx); + drvdata->enable_ctrl1 |= BIT(25); + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(addr_start); + +static ssize_t addr_stop_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 idx; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + val = drvdata->addr_val[idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t addr_stop_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + drvdata->addr_val[idx] = val; + drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP; + drvdata->startstop_ctrl |= (1 << (idx + 16)); + drvdata->enable_ctrl1 |= ETMTECR1_START_STOP; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(addr_stop); + +static ssize_t addr_acctype_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + val = drvdata->addr_acctype[drvdata->addr_idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t addr_acctype_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + drvdata->addr_acctype[drvdata->addr_idx] = val; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(addr_acctype); + +static ssize_t cntr_idx_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->cntr_idx; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t cntr_idx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + if (val >= drvdata->nr_cntr) + return -EINVAL; + /* + * Use spinlock to ensure index doesn't change while it gets + * dereferenced multiple times within a spinlock block elsewhere. + */ + spin_lock(&drvdata->spinlock); + drvdata->cntr_idx = val; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(cntr_idx); + +static ssize_t cntr_rld_val_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + val = drvdata->cntr_rld_val[drvdata->cntr_idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t cntr_rld_val_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + drvdata->cntr_rld_val[drvdata->cntr_idx] = val; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(cntr_rld_val); + +static ssize_t cntr_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + val = drvdata->cntr_event[drvdata->cntr_idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t cntr_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + drvdata->cntr_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(cntr_event); + +static ssize_t cntr_rld_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + val = drvdata->cntr_rld_event[drvdata->cntr_idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t cntr_rld_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + drvdata->cntr_rld_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(cntr_rld_event); + +static ssize_t cntr_val_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i, ret = 0; + u32 val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (!drvdata->enable) { + spin_lock(&drvdata->spinlock); + for (i = 0; i < drvdata->nr_cntr; i++) + ret += sprintf(buf, "counter %d: %x\n", + i, drvdata->cntr_val[i]); + spin_unlock(&drvdata->spinlock); + return ret; + } + + for (i = 0; i < drvdata->nr_cntr; i++) { + val = etm_readl(drvdata, ETMCNTVRn(i)); + ret += sprintf(buf, "counter %d: %x\n", i, val); + } + + return ret; +} + +static ssize_t cntr_val_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + drvdata->cntr_val[drvdata->cntr_idx] = val; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(cntr_val); + +static ssize_t seq_12_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_12_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t seq_12_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->seq_12_event = val & ETM_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(seq_12_event); + +static ssize_t seq_21_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_21_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t seq_21_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->seq_21_event = val & ETM_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(seq_21_event); + +static ssize_t seq_23_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_23_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t seq_23_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->seq_23_event = val & ETM_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(seq_23_event); + +static ssize_t seq_31_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_31_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t seq_31_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->seq_31_event = val & ETM_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(seq_31_event); + +static ssize_t seq_32_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_32_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t seq_32_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->seq_32_event = val & ETM_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(seq_32_event); + +static ssize_t seq_13_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_13_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t seq_13_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->seq_13_event = val & ETM_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(seq_13_event); + +static ssize_t seq_curr_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val, flags; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (!drvdata->enable) { + val = drvdata->seq_curr_state; + goto out; + } + + pm_runtime_get_sync(drvdata->dev); + spin_lock_irqsave(&drvdata->spinlock, flags); + + CS_UNLOCK(drvdata->base); + val = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK); + CS_LOCK(drvdata->base); + + spin_unlock_irqrestore(&drvdata->spinlock, flags); + pm_runtime_put(drvdata->dev); +out: + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t seq_curr_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + if (val > ETM_SEQ_STATE_MAX_VAL) + return -EINVAL; + + drvdata->seq_curr_state = val; + + return size; +} +static DEVICE_ATTR_RW(seq_curr_state); + +static ssize_t ctxid_idx_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->ctxid_idx; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t ctxid_idx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + if (val >= drvdata->nr_ctxid_cmp) + return -EINVAL; + + /* + * Use spinlock to ensure index doesn't change while it gets + * dereferenced multiple times within a spinlock block elsewhere. + */ + spin_lock(&drvdata->spinlock); + drvdata->ctxid_idx = val; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(ctxid_idx); + +static ssize_t ctxid_pid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + val = drvdata->ctxid_vpid[drvdata->ctxid_idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t ctxid_pid_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long vpid, pid; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &vpid); + if (ret) + return ret; + + pid = coresight_vpid_to_pid(vpid); + + spin_lock(&drvdata->spinlock); + drvdata->ctxid_pid[drvdata->ctxid_idx] = pid; + drvdata->ctxid_vpid[drvdata->ctxid_idx] = vpid; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(ctxid_pid); + +static ssize_t ctxid_mask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->ctxid_mask; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t ctxid_mask_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->ctxid_mask = val; + return size; +} +static DEVICE_ATTR_RW(ctxid_mask); + +static ssize_t sync_freq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->sync_freq; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t sync_freq_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->sync_freq = val & ETM_SYNC_MASK; + return size; +} +static DEVICE_ATTR_RW(sync_freq); + +static ssize_t timestamp_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->timestamp_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t timestamp_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->timestamp_event = val & ETM_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(timestamp_event); + +static ssize_t cpu_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->cpu; + return scnprintf(buf, PAGE_SIZE, "%d\n", val); + +} +static DEVICE_ATTR_RO(cpu); + +static ssize_t traceid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = etm_get_trace_id(drvdata); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t traceid_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->traceid = val & ETM_TRACEID_MASK; + return size; +} +static DEVICE_ATTR_RW(traceid); + +static struct attribute *coresight_etm_attrs[] = { + &dev_attr_nr_addr_cmp.attr, + &dev_attr_nr_cntr.attr, + &dev_attr_nr_ctxid_cmp.attr, + &dev_attr_etmsr.attr, + &dev_attr_reset.attr, + &dev_attr_mode.attr, + &dev_attr_trigger_event.attr, + &dev_attr_enable_event.attr, + &dev_attr_fifofull_level.attr, + &dev_attr_addr_idx.attr, + &dev_attr_addr_single.attr, + &dev_attr_addr_range.attr, + &dev_attr_addr_start.attr, + &dev_attr_addr_stop.attr, + &dev_attr_addr_acctype.attr, + &dev_attr_cntr_idx.attr, + &dev_attr_cntr_rld_val.attr, + &dev_attr_cntr_event.attr, + &dev_attr_cntr_rld_event.attr, + &dev_attr_cntr_val.attr, + &dev_attr_seq_12_event.attr, + &dev_attr_seq_21_event.attr, + &dev_attr_seq_23_event.attr, + &dev_attr_seq_31_event.attr, + &dev_attr_seq_32_event.attr, + &dev_attr_seq_13_event.attr, + &dev_attr_seq_curr_state.attr, + &dev_attr_ctxid_idx.attr, + &dev_attr_ctxid_pid.attr, + &dev_attr_ctxid_mask.attr, + &dev_attr_sync_freq.attr, + &dev_attr_timestamp_event.attr, + &dev_attr_traceid.attr, + &dev_attr_cpu.attr, + NULL, +}; + +#define coresight_simple_func(name, offset) \ +static ssize_t name##_show(struct device *_dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct etm_drvdata *drvdata = dev_get_drvdata(_dev->parent); \ + return scnprintf(buf, PAGE_SIZE, "0x%x\n", \ + readl_relaxed(drvdata->base + offset)); \ +} \ +DEVICE_ATTR_RO(name) + +coresight_simple_func(etmccr, ETMCCR); +coresight_simple_func(etmccer, ETMCCER); +coresight_simple_func(etmscr, ETMSCR); +coresight_simple_func(etmidr, ETMIDR); +coresight_simple_func(etmcr, ETMCR); +coresight_simple_func(etmtraceidr, ETMTRACEIDR); +coresight_simple_func(etmteevr, ETMTEEVR); +coresight_simple_func(etmtssvr, ETMTSSCR); +coresight_simple_func(etmtecr1, ETMTECR1); +coresight_simple_func(etmtecr2, ETMTECR2); + +static struct attribute *coresight_etm_mgmt_attrs[] = { + &dev_attr_etmccr.attr, + &dev_attr_etmccer.attr, + &dev_attr_etmscr.attr, + &dev_attr_etmidr.attr, + &dev_attr_etmcr.attr, + &dev_attr_etmtraceidr.attr, + &dev_attr_etmteevr.attr, + &dev_attr_etmtssvr.attr, + &dev_attr_etmtecr1.attr, + &dev_attr_etmtecr2.attr, + NULL, +}; + +static const struct attribute_group coresight_etm_group = { + .attrs = coresight_etm_attrs, +}; + +static const struct attribute_group coresight_etm_mgmt_group = { + .attrs = coresight_etm_mgmt_attrs, + .name = "mgmt", +}; + +const struct attribute_group *coresight_etm_groups[] = { + &coresight_etm_group, + &coresight_etm_mgmt_group, + NULL, +}; diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index d53440e9af6f..6ea35b350958 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -186,7 +186,7 @@ static void etm_clr_prog(struct etm_drvdata *drvdata) } } -static void etm_set_default(struct etm_drvdata *drvdata) +void etm_set_default(struct etm_drvdata *drvdata) { int i; @@ -293,15 +293,18 @@ static int etm_cpu_id(struct coresight_device *csdev) return drvdata->cpu; } -static int etm_trace_id(struct coresight_device *csdev) +int etm_get_trace_id(struct etm_drvdata *drvdata) { - struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); unsigned long flags; int trace_id = -1; + if (!drvdata) + goto out; + if (!drvdata->enable) return drvdata->traceid; - pm_runtime_get_sync(csdev->dev.parent); + + pm_runtime_get_sync(drvdata->dev); spin_lock_irqsave(&drvdata->spinlock, flags); @@ -310,9 +313,18 @@ static int etm_trace_id(struct coresight_device *csdev) CS_LOCK(drvdata->base); spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(csdev->dev.parent); + pm_runtime_put(drvdata->dev); +out: return trace_id; + +} + +static int etm_trace_id(struct coresight_device *csdev) +{ + struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + return etm_get_trace_id(drvdata); } static int etm_enable(struct coresight_device *csdev) @@ -406,1218 +418,6 @@ static const struct coresight_ops etm_cs_ops = { .source_ops = &etm_source_ops, }; -static ssize_t nr_addr_cmp_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->nr_addr_cmp; - return sprintf(buf, "%#lx\n", val); -} -static DEVICE_ATTR_RO(nr_addr_cmp); - -static ssize_t nr_cntr_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->nr_cntr; - return sprintf(buf, "%#lx\n", val); -} -static DEVICE_ATTR_RO(nr_cntr); - -static ssize_t nr_ctxid_cmp_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->nr_ctxid_cmp; - return sprintf(buf, "%#lx\n", val); -} -static DEVICE_ATTR_RO(nr_ctxid_cmp); - -static ssize_t etmsr_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long flags, val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - pm_runtime_get_sync(drvdata->dev); - spin_lock_irqsave(&drvdata->spinlock, flags); - CS_UNLOCK(drvdata->base); - - val = etm_readl(drvdata, ETMSR); - - CS_LOCK(drvdata->base); - spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); - - return sprintf(buf, "%#lx\n", val); -} -static DEVICE_ATTR_RO(etmsr); - -static ssize_t reset_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int i, ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - if (val) { - spin_lock(&drvdata->spinlock); - drvdata->mode = ETM_MODE_EXCLUDE; - drvdata->ctrl = 0x0; - drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL; - drvdata->startstop_ctrl = 0x0; - drvdata->addr_idx = 0x0; - for (i = 0; i < drvdata->nr_addr_cmp; i++) { - drvdata->addr_val[i] = 0x0; - drvdata->addr_acctype[i] = 0x0; - drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE; - } - drvdata->cntr_idx = 0x0; - - etm_set_default(drvdata); - spin_unlock(&drvdata->spinlock); - } - - return size; -} -static DEVICE_ATTR_WO(reset); - -static ssize_t mode_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->mode; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t mode_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - drvdata->mode = val & ETM_MODE_ALL; - - if (drvdata->mode & ETM_MODE_EXCLUDE) - drvdata->enable_ctrl1 |= ETMTECR1_INC_EXC; - else - drvdata->enable_ctrl1 &= ~ETMTECR1_INC_EXC; - - if (drvdata->mode & ETM_MODE_CYCACC) - drvdata->ctrl |= ETMCR_CYC_ACC; - else - drvdata->ctrl &= ~ETMCR_CYC_ACC; - - if (drvdata->mode & ETM_MODE_STALL) { - if (!(drvdata->etmccr & ETMCCR_FIFOFULL)) { - dev_warn(drvdata->dev, "stall mode not supported\n"); - ret = -EINVAL; - goto err_unlock; - } - drvdata->ctrl |= ETMCR_STALL_MODE; - } else - drvdata->ctrl &= ~ETMCR_STALL_MODE; - - if (drvdata->mode & ETM_MODE_TIMESTAMP) { - if (!(drvdata->etmccer & ETMCCER_TIMESTAMP)) { - dev_warn(drvdata->dev, "timestamp not supported\n"); - ret = -EINVAL; - goto err_unlock; - } - drvdata->ctrl |= ETMCR_TIMESTAMP_EN; - } else - drvdata->ctrl &= ~ETMCR_TIMESTAMP_EN; - - if (drvdata->mode & ETM_MODE_CTXID) - drvdata->ctrl |= ETMCR_CTXID_SIZE; - else - drvdata->ctrl &= ~ETMCR_CTXID_SIZE; - spin_unlock(&drvdata->spinlock); - - return size; - -err_unlock: - spin_unlock(&drvdata->spinlock); - return ret; -} -static DEVICE_ATTR_RW(mode); - -static ssize_t trigger_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->trigger_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t trigger_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->trigger_event = val & ETM_EVENT_MASK; - - return size; -} -static DEVICE_ATTR_RW(trigger_event); - -static ssize_t enable_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->enable_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t enable_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->enable_event = val & ETM_EVENT_MASK; - - return size; -} -static DEVICE_ATTR_RW(enable_event); - -static ssize_t fifofull_level_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->fifofull_level; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t fifofull_level_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->fifofull_level = val; - - return size; -} -static DEVICE_ATTR_RW(fifofull_level); - -static ssize_t addr_idx_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->addr_idx; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t addr_idx_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - if (val >= drvdata->nr_addr_cmp) - return -EINVAL; - - /* - * Use spinlock to ensure index doesn't change while it gets - * dereferenced multiple times within a spinlock block elsewhere. - */ - spin_lock(&drvdata->spinlock); - drvdata->addr_idx = val; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(addr_idx); - -static ssize_t addr_single_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u8 idx; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { - spin_unlock(&drvdata->spinlock); - return -EINVAL; - } - - val = drvdata->addr_val[idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t addr_single_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { - spin_unlock(&drvdata->spinlock); - return -EINVAL; - } - - drvdata->addr_val[idx] = val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(addr_single); - -static ssize_t addr_range_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u8 idx; - unsigned long val1, val2; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (idx % 2 != 0) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || - (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - val1 = drvdata->addr_val[idx]; - val2 = drvdata->addr_val[idx + 1]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx %#lx\n", val1, val2); -} - -static ssize_t addr_range_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long val1, val2; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) - return -EINVAL; - /* Lower address comparator cannot have a higher address value */ - if (val1 > val2) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (idx % 2 != 0) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || - (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - drvdata->addr_val[idx] = val1; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE; - drvdata->addr_val[idx + 1] = val2; - drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE; - drvdata->enable_ctrl1 |= (1 << (idx/2)); - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(addr_range); - -static ssize_t addr_start_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u8 idx; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - val = drvdata->addr_val[idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t addr_start_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - drvdata->addr_val[idx] = val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_START; - drvdata->startstop_ctrl |= (1 << idx); - drvdata->enable_ctrl1 |= BIT(25); - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(addr_start); - -static ssize_t addr_stop_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u8 idx; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - val = drvdata->addr_val[idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t addr_stop_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - drvdata->addr_val[idx] = val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP; - drvdata->startstop_ctrl |= (1 << (idx + 16)); - drvdata->enable_ctrl1 |= ETMTECR1_START_STOP; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(addr_stop); - -static ssize_t addr_acctype_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - val = drvdata->addr_acctype[drvdata->addr_idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t addr_acctype_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - drvdata->addr_acctype[drvdata->addr_idx] = val; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(addr_acctype); - -static ssize_t cntr_idx_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->cntr_idx; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t cntr_idx_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - if (val >= drvdata->nr_cntr) - return -EINVAL; - /* - * Use spinlock to ensure index doesn't change while it gets - * dereferenced multiple times within a spinlock block elsewhere. - */ - spin_lock(&drvdata->spinlock); - drvdata->cntr_idx = val; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(cntr_idx); - -static ssize_t cntr_rld_val_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - val = drvdata->cntr_rld_val[drvdata->cntr_idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t cntr_rld_val_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - drvdata->cntr_rld_val[drvdata->cntr_idx] = val; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(cntr_rld_val); - -static ssize_t cntr_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - val = drvdata->cntr_event[drvdata->cntr_idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t cntr_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - drvdata->cntr_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(cntr_event); - -static ssize_t cntr_rld_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - val = drvdata->cntr_rld_event[drvdata->cntr_idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t cntr_rld_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - drvdata->cntr_rld_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(cntr_rld_event); - -static ssize_t cntr_val_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int i, ret = 0; - u32 val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (!drvdata->enable) { - spin_lock(&drvdata->spinlock); - for (i = 0; i < drvdata->nr_cntr; i++) - ret += sprintf(buf, "counter %d: %x\n", - i, drvdata->cntr_val[i]); - spin_unlock(&drvdata->spinlock); - return ret; - } - - for (i = 0; i < drvdata->nr_cntr; i++) { - val = etm_readl(drvdata, ETMCNTVRn(i)); - ret += sprintf(buf, "counter %d: %x\n", i, val); - } - - return ret; -} - -static ssize_t cntr_val_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - drvdata->cntr_val[drvdata->cntr_idx] = val; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(cntr_val); - -static ssize_t seq_12_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_12_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t seq_12_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->seq_12_event = val & ETM_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(seq_12_event); - -static ssize_t seq_21_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_21_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t seq_21_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->seq_21_event = val & ETM_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(seq_21_event); - -static ssize_t seq_23_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_23_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t seq_23_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->seq_23_event = val & ETM_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(seq_23_event); - -static ssize_t seq_31_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_31_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t seq_31_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->seq_31_event = val & ETM_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(seq_31_event); - -static ssize_t seq_32_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_32_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t seq_32_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->seq_32_event = val & ETM_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(seq_32_event); - -static ssize_t seq_13_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_13_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t seq_13_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->seq_13_event = val & ETM_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(seq_13_event); - -static ssize_t seq_curr_state_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val, flags; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (!drvdata->enable) { - val = drvdata->seq_curr_state; - goto out; - } - - pm_runtime_get_sync(drvdata->dev); - spin_lock_irqsave(&drvdata->spinlock, flags); - - CS_UNLOCK(drvdata->base); - val = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK); - CS_LOCK(drvdata->base); - - spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); -out: - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t seq_curr_state_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - if (val > ETM_SEQ_STATE_MAX_VAL) - return -EINVAL; - - drvdata->seq_curr_state = val; - - return size; -} -static DEVICE_ATTR_RW(seq_curr_state); - -static ssize_t ctxid_idx_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->ctxid_idx; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t ctxid_idx_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - if (val >= drvdata->nr_ctxid_cmp) - return -EINVAL; - - /* - * Use spinlock to ensure index doesn't change while it gets - * dereferenced multiple times within a spinlock block elsewhere. - */ - spin_lock(&drvdata->spinlock); - drvdata->ctxid_idx = val; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(ctxid_idx); - -static ssize_t ctxid_pid_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - val = drvdata->ctxid_vpid[drvdata->ctxid_idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t ctxid_pid_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long vpid, pid; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &vpid); - if (ret) - return ret; - - pid = coresight_vpid_to_pid(vpid); - - spin_lock(&drvdata->spinlock); - drvdata->ctxid_pid[drvdata->ctxid_idx] = pid; - drvdata->ctxid_vpid[drvdata->ctxid_idx] = vpid; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(ctxid_pid); - -static ssize_t ctxid_mask_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->ctxid_mask; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t ctxid_mask_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->ctxid_mask = val; - return size; -} -static DEVICE_ATTR_RW(ctxid_mask); - -static ssize_t sync_freq_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->sync_freq; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t sync_freq_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->sync_freq = val & ETM_SYNC_MASK; - return size; -} -static DEVICE_ATTR_RW(sync_freq); - -static ssize_t timestamp_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->timestamp_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t timestamp_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->timestamp_event = val & ETM_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(timestamp_event); - -static ssize_t cpu_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->cpu; - return scnprintf(buf, PAGE_SIZE, "%d\n", val); - -} -static DEVICE_ATTR_RO(cpu); - -static ssize_t traceid_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val, flags; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (!drvdata->enable) { - val = drvdata->traceid; - goto out; - } - - pm_runtime_get_sync(drvdata->dev); - spin_lock_irqsave(&drvdata->spinlock, flags); - CS_UNLOCK(drvdata->base); - - val = (etm_readl(drvdata, ETMTRACEIDR) & ETM_TRACEID_MASK); - - CS_LOCK(drvdata->base); - spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); -out: - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t traceid_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->traceid = val & ETM_TRACEID_MASK; - return size; -} -static DEVICE_ATTR_RW(traceid); - -static struct attribute *coresight_etm_attrs[] = { - &dev_attr_nr_addr_cmp.attr, - &dev_attr_nr_cntr.attr, - &dev_attr_nr_ctxid_cmp.attr, - &dev_attr_etmsr.attr, - &dev_attr_reset.attr, - &dev_attr_mode.attr, - &dev_attr_trigger_event.attr, - &dev_attr_enable_event.attr, - &dev_attr_fifofull_level.attr, - &dev_attr_addr_idx.attr, - &dev_attr_addr_single.attr, - &dev_attr_addr_range.attr, - &dev_attr_addr_start.attr, - &dev_attr_addr_stop.attr, - &dev_attr_addr_acctype.attr, - &dev_attr_cntr_idx.attr, - &dev_attr_cntr_rld_val.attr, - &dev_attr_cntr_event.attr, - &dev_attr_cntr_rld_event.attr, - &dev_attr_cntr_val.attr, - &dev_attr_seq_12_event.attr, - &dev_attr_seq_21_event.attr, - &dev_attr_seq_23_event.attr, - &dev_attr_seq_31_event.attr, - &dev_attr_seq_32_event.attr, - &dev_attr_seq_13_event.attr, - &dev_attr_seq_curr_state.attr, - &dev_attr_ctxid_idx.attr, - &dev_attr_ctxid_pid.attr, - &dev_attr_ctxid_mask.attr, - &dev_attr_sync_freq.attr, - &dev_attr_timestamp_event.attr, - &dev_attr_traceid.attr, - &dev_attr_cpu.attr, - NULL, -}; - -#define coresight_simple_func(name, offset) \ -static ssize_t name##_show(struct device *_dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct etm_drvdata *drvdata = dev_get_drvdata(_dev->parent); \ - return scnprintf(buf, PAGE_SIZE, "0x%x\n", \ - readl_relaxed(drvdata->base + offset)); \ -} \ -DEVICE_ATTR_RO(name) - -coresight_simple_func(etmccr, ETMCCR); -coresight_simple_func(etmccer, ETMCCER); -coresight_simple_func(etmscr, ETMSCR); -coresight_simple_func(etmidr, ETMIDR); -coresight_simple_func(etmcr, ETMCR); -coresight_simple_func(etmtraceidr, ETMTRACEIDR); -coresight_simple_func(etmteevr, ETMTEEVR); -coresight_simple_func(etmtssvr, ETMTSSCR); -coresight_simple_func(etmtecr1, ETMTECR1); -coresight_simple_func(etmtecr2, ETMTECR2); - -static struct attribute *coresight_etm_mgmt_attrs[] = { - &dev_attr_etmccr.attr, - &dev_attr_etmccer.attr, - &dev_attr_etmscr.attr, - &dev_attr_etmidr.attr, - &dev_attr_etmcr.attr, - &dev_attr_etmtraceidr.attr, - &dev_attr_etmteevr.attr, - &dev_attr_etmtssvr.attr, - &dev_attr_etmtecr1.attr, - &dev_attr_etmtecr2.attr, - NULL, -}; - -static const struct attribute_group coresight_etm_group = { - .attrs = coresight_etm_attrs, -}; - - -static const struct attribute_group coresight_etm_mgmt_group = { - .attrs = coresight_etm_mgmt_attrs, - .name = "mgmt", -}; - -static const struct attribute_group *coresight_etm_groups[] = { - &coresight_etm_group, - &coresight_etm_mgmt_group, - NULL, -}; - static int etm_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { From fdd3e24c6b7cbea0737008ffb846c76d8b479d97 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:50 -0700 Subject: [PATCH 036/104] coresight: etm3x: unlocking tracers in default arch init Calling function 'smp_call_function_single()' to unlock a tracer and calling it again right after to perform the default initialisation doesn't make sense. Moving 'etm_os_unlock()' just before making the default initialisation results in the same outcome while saving one call to 'smp_call_function_single()'. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit ae69a1da399fccaed406932f5cbee55a6f9d4425) --- drivers/hwtracing/coresight/coresight-etm3x.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 6ea35b350958..f2c74bb56993 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -47,11 +47,11 @@ static struct etm_drvdata *etmdrvdata[NR_CPUS]; * and OS lock must be unlocked before any memory mapped access on such * processors, otherwise memory mapped reads/writes will be invalid. */ -static void etm_os_unlock(void *info) +static void etm_os_unlock(struct etm_drvdata *drvdata) { - struct etm_drvdata *drvdata = (struct etm_drvdata *)info; /* Writing any value to ETMOSLAR unlocks the trace registers */ etm_writel(drvdata, 0x0, ETMOSLAR); + drvdata->os_unlock = true; isb(); } @@ -483,6 +483,9 @@ static void etm_init_arch_data(void *info) u32 etmccr; struct etm_drvdata *drvdata = info; + /* Make sure all registers are accessible */ + etm_os_unlock(drvdata); + CS_UNLOCK(drvdata->base); /* First dummy read */ @@ -607,9 +610,6 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) get_online_cpus(); etmdrvdata[drvdata->cpu] = drvdata; - if (!smp_call_function_single(drvdata->cpu, etm_os_unlock, drvdata, 1)) - drvdata->os_unlock = true; - if (smp_call_function_single(drvdata->cpu, etm_init_arch_data, drvdata, 1)) dev_err(dev, "ETM arch init failed\n"); From e66f4306639b71295995f91374b233efb01227db Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:51 -0700 Subject: [PATCH 037/104] coresight: etm3x: splitting struct etm_drvdata Splitting "etm_drvdata" in two sections, one for the HW specific data and another for user configuration. That way it is easier to manipulate and zero out the configuration data when more than one concurrent tracing session configuration is active. Also taking care of up-lifting all the code affected by this new arrangement. No loss or gain of functionality (other than what is mentioned above) is introduced by this patch. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 1925a470ce69cdfa2b82ac1565d58dfd39cd877d) --- drivers/hwtracing/coresight/coresight-etm.h | 102 +++--- .../coresight/coresight-etm3x-sysfs.c | 290 ++++++++++-------- drivers/hwtracing/coresight/coresight-etm3x.c | 151 ++++----- 3 files changed, 304 insertions(+), 239 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index 9a30aa392ed9..371fb7d2e829 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -136,29 +136,9 @@ #define ETM_DEFAULT_EVENT_VAL (ETM_HARD_WIRE_RES_A | \ ETM_ADD_COMP_0 | \ ETM_EVENT_NOT_A) + /** - * struct etm_drvdata - specifics associated to an ETM component - * @base: memory mapped base address for this component. - * @dev: the device entity associated to this component. - * @atclk: optional clock for the core parts of the ETM. - * @csdev: component vitals needed by the framework. - * @spinlock: only one at a time pls. - * @cpu: the cpu this component is affined to. - * @port_size: port size as reported by ETMCR bit 4-6 and 21. - * @arch: ETM/PTM version number. - * @use_cpu14: true if management registers need to be accessed via CP14. - * @enable: is this ETM/PTM currently tracing. - * @sticky_enable: true if ETM base configuration has been done. - * @boot_enable:true if we should start tracing at boot time. - * @os_unlock: true if access to management registers is allowed. - * @nr_addr_cmp:Number of pairs of address comparators as found in ETMCCR. - * @nr_cntr: Number of counters as found in ETMCCR bit 13-15. - * @nr_ext_inp: Number of external input as found in ETMCCR bit 17-19. - * @nr_ext_out: Number of external output as found in ETMCCR bit 20-22. - * @nr_ctxid_cmp: Number of contextID comparators as found in ETMCCR bit 24-25. - * @etmccr: value of register ETMCCR. - * @etmccer: value of register ETMCCER. - * @traceid: value of the current ID for this component. + * struct etm_config - configuration information related to an ETM * @mode: controls various modes supported by this ETM/PTM. * @ctrl: used in conjunction with @mode. * @trigger_event: setting for register ETMTRIGGER. @@ -189,30 +169,9 @@ * @ctxid_mask: mask applicable to all the context IDs. * @sync_freq: Synchronisation frequency. * @timestamp_event: Defines an event that requests the insertion - of a timestamp into the trace stream. + * of a timestamp into the trace stream. */ -struct etm_drvdata { - void __iomem *base; - struct device *dev; - struct clk *atclk; - struct coresight_device *csdev; - spinlock_t spinlock; - int cpu; - int port_size; - u8 arch; - bool use_cp14; - bool enable; - bool sticky_enable; - bool boot_enable; - bool os_unlock; - u8 nr_addr_cmp; - u8 nr_cntr; - u8 nr_ext_inp; - u8 nr_ext_out; - u8 nr_ctxid_cmp; - u32 etmccr; - u32 etmccer; - u32 traceid; +struct etm_config { u32 mode; u32 ctrl; u32 trigger_event; @@ -244,6 +203,56 @@ struct etm_drvdata { u32 timestamp_event; }; +/** + * struct etm_drvdata - specifics associated to an ETM component + * @base: memory mapped base address for this component. + * @dev: the device entity associated to this component. + * @atclk: optional clock for the core parts of the ETM. + * @csdev: component vitals needed by the framework. + * @spinlock: only one at a time pls. + * @cpu: the cpu this component is affined to. + * @port_size: port size as reported by ETMCR bit 4-6 and 21. + * @arch: ETM/PTM version number. + * @use_cpu14: true if management registers need to be accessed via CP14. + * @enable: is this ETM/PTM currently tracing. + * @sticky_enable: true if ETM base configuration has been done. + * @boot_enable:true if we should start tracing at boot time. + * @os_unlock: true if access to management registers is allowed. + * @nr_addr_cmp:Number of pairs of address comparators as found in ETMCCR. + * @nr_cntr: Number of counters as found in ETMCCR bit 13-15. + * @nr_ext_inp: Number of external input as found in ETMCCR bit 17-19. + * @nr_ext_out: Number of external output as found in ETMCCR bit 20-22. + * @nr_ctxid_cmp: Number of contextID comparators as found in ETMCCR bit 24-25. + * @etmccr: value of register ETMCCR. + * @etmccer: value of register ETMCCER. + * @traceid: value of the current ID for this component. + * @config: structure holding configuration parameters. + */ +struct etm_drvdata { + void __iomem *base; + struct device *dev; + struct clk *atclk; + struct coresight_device *csdev; + spinlock_t spinlock; + int cpu; + int port_size; + u8 arch; + bool use_cp14; + bool enable; + bool sticky_enable; + bool boot_enable; + bool os_unlock; + u8 nr_addr_cmp; + u8 nr_cntr; + u8 nr_ext_inp; + u8 nr_ext_out; + u8 nr_ctxid_cmp; + u32 etmccr; + u32 etmccer; + u32 traceid; + struct etm_config config; +}; + enum etm_addr_type { ETM_ADDR_TYPE_NONE, ETM_ADDR_TYPE_SINGLE, @@ -283,5 +292,6 @@ static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off) extern const struct attribute_group *coresight_etm_groups[]; int etm_get_trace_id(struct etm_drvdata *drvdata); -void etm_set_default(struct etm_drvdata *drvdata); +void etm_set_default(struct etm_config *config); +struct etm_config *get_etm_config(struct etm_drvdata *drvdata); #endif diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index f409f5a88e95..456df2378a6f 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -78,6 +78,7 @@ static ssize_t reset_store(struct device *dev, int i, ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) @@ -85,19 +86,14 @@ static ssize_t reset_store(struct device *dev, if (val) { spin_lock(&drvdata->spinlock); - drvdata->mode = ETM_MODE_EXCLUDE; - drvdata->ctrl = 0x0; - drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL; - drvdata->startstop_ctrl = 0x0; - drvdata->addr_idx = 0x0; + memset(config, 0, sizeof(struct etm_config)); + config->mode = ETM_MODE_EXCLUDE; + config->trigger_event = ETM_DEFAULT_EVENT_VAL; for (i = 0; i < drvdata->nr_addr_cmp; i++) { - drvdata->addr_val[i] = 0x0; - drvdata->addr_acctype[i] = 0x0; - drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE; + config->addr_type[i] = ETM_ADDR_TYPE_NONE; } - drvdata->cntr_idx = 0x0; - etm_set_default(drvdata); + etm_set_default(config); spin_unlock(&drvdata->spinlock); } @@ -110,8 +106,9 @@ static ssize_t mode_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->mode; + val = config->mode; return sprintf(buf, "%#lx\n", val); } @@ -122,48 +119,49 @@ static ssize_t mode_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - drvdata->mode = val & ETM_MODE_ALL; + config->mode = val & ETM_MODE_ALL; - if (drvdata->mode & ETM_MODE_EXCLUDE) - drvdata->enable_ctrl1 |= ETMTECR1_INC_EXC; + if (config->mode & ETM_MODE_EXCLUDE) + config->enable_ctrl1 |= ETMTECR1_INC_EXC; else - drvdata->enable_ctrl1 &= ~ETMTECR1_INC_EXC; + config->enable_ctrl1 &= ~ETMTECR1_INC_EXC; - if (drvdata->mode & ETM_MODE_CYCACC) - drvdata->ctrl |= ETMCR_CYC_ACC; + if (config->mode & ETM_MODE_CYCACC) + config->ctrl |= ETMCR_CYC_ACC; else - drvdata->ctrl &= ~ETMCR_CYC_ACC; + config->ctrl &= ~ETMCR_CYC_ACC; - if (drvdata->mode & ETM_MODE_STALL) { + if (config->mode & ETM_MODE_STALL) { if (!(drvdata->etmccr & ETMCCR_FIFOFULL)) { dev_warn(drvdata->dev, "stall mode not supported\n"); ret = -EINVAL; goto err_unlock; } - drvdata->ctrl |= ETMCR_STALL_MODE; + config->ctrl |= ETMCR_STALL_MODE; } else - drvdata->ctrl &= ~ETMCR_STALL_MODE; + config->ctrl &= ~ETMCR_STALL_MODE; - if (drvdata->mode & ETM_MODE_TIMESTAMP) { + if (config->mode & ETM_MODE_TIMESTAMP) { if (!(drvdata->etmccer & ETMCCER_TIMESTAMP)) { dev_warn(drvdata->dev, "timestamp not supported\n"); ret = -EINVAL; goto err_unlock; } - drvdata->ctrl |= ETMCR_TIMESTAMP_EN; + config->ctrl |= ETMCR_TIMESTAMP_EN; } else - drvdata->ctrl &= ~ETMCR_TIMESTAMP_EN; + config->ctrl &= ~ETMCR_TIMESTAMP_EN; - if (drvdata->mode & ETM_MODE_CTXID) - drvdata->ctrl |= ETMCR_CTXID_SIZE; + if (config->mode & ETM_MODE_CTXID) + config->ctrl |= ETMCR_CTXID_SIZE; else - drvdata->ctrl &= ~ETMCR_CTXID_SIZE; + config->ctrl &= ~ETMCR_CTXID_SIZE; spin_unlock(&drvdata->spinlock); return size; @@ -179,8 +177,9 @@ static ssize_t trigger_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->trigger_event; + val = config->trigger_event; return sprintf(buf, "%#lx\n", val); } @@ -191,12 +190,13 @@ static ssize_t trigger_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->trigger_event = val & ETM_EVENT_MASK; + config->trigger_event = val & ETM_EVENT_MASK; return size; } @@ -207,8 +207,9 @@ static ssize_t enable_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->enable_event; + val = config->enable_event; return sprintf(buf, "%#lx\n", val); } @@ -219,12 +220,13 @@ static ssize_t enable_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->enable_event = val & ETM_EVENT_MASK; + config->enable_event = val & ETM_EVENT_MASK; return size; } @@ -235,8 +237,9 @@ static ssize_t fifofull_level_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->fifofull_level; + val = config->fifofull_level; return sprintf(buf, "%#lx\n", val); } @@ -247,12 +250,13 @@ static ssize_t fifofull_level_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->fifofull_level = val; + config->fifofull_level = val; return size; } @@ -263,8 +267,9 @@ static ssize_t addr_idx_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->addr_idx; + val = config->addr_idx; return sprintf(buf, "%#lx\n", val); } @@ -275,6 +280,7 @@ static ssize_t addr_idx_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) @@ -288,7 +294,7 @@ static ssize_t addr_idx_store(struct device *dev, * dereferenced multiple times within a spinlock block elsewhere. */ spin_lock(&drvdata->spinlock); - drvdata->addr_idx = val; + config->addr_idx = val; spin_unlock(&drvdata->spinlock); return size; @@ -301,16 +307,17 @@ static ssize_t addr_single_show(struct device *dev, u8 idx; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { + idx = config->addr_idx; + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { spin_unlock(&drvdata->spinlock); return -EINVAL; } - val = drvdata->addr_val[idx]; + val = config->addr_val[idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -324,21 +331,22 @@ static ssize_t addr_single_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { + idx = config->addr_idx; + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { spin_unlock(&drvdata->spinlock); return -EINVAL; } - drvdata->addr_val[idx] = val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE; + config->addr_val[idx] = val; + config->addr_type[idx] = ETM_ADDR_TYPE_SINGLE; spin_unlock(&drvdata->spinlock); return size; @@ -351,23 +359,24 @@ static ssize_t addr_range_show(struct device *dev, u8 idx; unsigned long val1, val2; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; if (idx % 2 != 0) { spin_unlock(&drvdata->spinlock); return -EPERM; } - if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || - (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { + if (!((config->addr_type[idx] == ETM_ADDR_TYPE_NONE && + config->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || + (config->addr_type[idx] == ETM_ADDR_TYPE_RANGE && + config->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { spin_unlock(&drvdata->spinlock); return -EPERM; } - val1 = drvdata->addr_val[idx]; - val2 = drvdata->addr_val[idx + 1]; + val1 = config->addr_val[idx]; + val2 = config->addr_val[idx + 1]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx %#lx\n", val1, val2); @@ -380,6 +389,7 @@ static ssize_t addr_range_store(struct device *dev, u8 idx; unsigned long val1, val2; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) return -EINVAL; @@ -388,24 +398,24 @@ static ssize_t addr_range_store(struct device *dev, return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; if (idx % 2 != 0) { spin_unlock(&drvdata->spinlock); return -EPERM; } - if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || - (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { + if (!((config->addr_type[idx] == ETM_ADDR_TYPE_NONE && + config->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || + (config->addr_type[idx] == ETM_ADDR_TYPE_RANGE && + config->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { spin_unlock(&drvdata->spinlock); return -EPERM; } - drvdata->addr_val[idx] = val1; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE; - drvdata->addr_val[idx + 1] = val2; - drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE; - drvdata->enable_ctrl1 |= (1 << (idx/2)); + config->addr_val[idx] = val1; + config->addr_type[idx] = ETM_ADDR_TYPE_RANGE; + config->addr_val[idx + 1] = val2; + config->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE; + config->enable_ctrl1 |= (1 << (idx/2)); spin_unlock(&drvdata->spinlock); return size; @@ -418,16 +428,17 @@ static ssize_t addr_start_show(struct device *dev, u8 idx; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { + idx = config->addr_idx; + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_START)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - val = drvdata->addr_val[idx]; + val = config->addr_val[idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -441,23 +452,24 @@ static ssize_t addr_start_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { + idx = config->addr_idx; + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_START)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - drvdata->addr_val[idx] = val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_START; - drvdata->startstop_ctrl |= (1 << idx); - drvdata->enable_ctrl1 |= BIT(25); + config->addr_val[idx] = val; + config->addr_type[idx] = ETM_ADDR_TYPE_START; + config->startstop_ctrl |= (1 << idx); + config->enable_ctrl1 |= BIT(25); spin_unlock(&drvdata->spinlock); return size; @@ -470,16 +482,17 @@ static ssize_t addr_stop_show(struct device *dev, u8 idx; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { + idx = config->addr_idx; + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - val = drvdata->addr_val[idx]; + val = config->addr_val[idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -493,23 +506,24 @@ static ssize_t addr_stop_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { + idx = config->addr_idx; + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - drvdata->addr_val[idx] = val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP; - drvdata->startstop_ctrl |= (1 << (idx + 16)); - drvdata->enable_ctrl1 |= ETMTECR1_START_STOP; + config->addr_val[idx] = val; + config->addr_type[idx] = ETM_ADDR_TYPE_STOP; + config->startstop_ctrl |= (1 << (idx + 16)); + config->enable_ctrl1 |= ETMTECR1_START_STOP; spin_unlock(&drvdata->spinlock); return size; @@ -521,9 +535,10 @@ static ssize_t addr_acctype_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - val = drvdata->addr_acctype[drvdata->addr_idx]; + val = config->addr_acctype[config->addr_idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -536,13 +551,14 @@ static ssize_t addr_acctype_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - drvdata->addr_acctype[drvdata->addr_idx] = val; + config->addr_acctype[config->addr_idx] = val; spin_unlock(&drvdata->spinlock); return size; @@ -554,8 +570,9 @@ static ssize_t cntr_idx_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->cntr_idx; + val = config->cntr_idx; return sprintf(buf, "%#lx\n", val); } @@ -566,6 +583,7 @@ static ssize_t cntr_idx_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) @@ -578,7 +596,7 @@ static ssize_t cntr_idx_store(struct device *dev, * dereferenced multiple times within a spinlock block elsewhere. */ spin_lock(&drvdata->spinlock); - drvdata->cntr_idx = val; + config->cntr_idx = val; spin_unlock(&drvdata->spinlock); return size; @@ -590,9 +608,10 @@ static ssize_t cntr_rld_val_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - val = drvdata->cntr_rld_val[drvdata->cntr_idx]; + val = config->cntr_rld_val[config->cntr_idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -605,13 +624,14 @@ static ssize_t cntr_rld_val_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - drvdata->cntr_rld_val[drvdata->cntr_idx] = val; + config->cntr_rld_val[config->cntr_idx] = val; spin_unlock(&drvdata->spinlock); return size; @@ -623,9 +643,10 @@ static ssize_t cntr_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - val = drvdata->cntr_event[drvdata->cntr_idx]; + val = config->cntr_event[config->cntr_idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -638,13 +659,14 @@ static ssize_t cntr_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - drvdata->cntr_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK; + config->cntr_event[config->cntr_idx] = val & ETM_EVENT_MASK; spin_unlock(&drvdata->spinlock); return size; @@ -656,9 +678,10 @@ static ssize_t cntr_rld_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - val = drvdata->cntr_rld_event[drvdata->cntr_idx]; + val = config->cntr_rld_event[config->cntr_idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -671,13 +694,14 @@ static ssize_t cntr_rld_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - drvdata->cntr_rld_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK; + config->cntr_rld_event[config->cntr_idx] = val & ETM_EVENT_MASK; spin_unlock(&drvdata->spinlock); return size; @@ -690,12 +714,13 @@ static ssize_t cntr_val_show(struct device *dev, int i, ret = 0; u32 val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; if (!drvdata->enable) { spin_lock(&drvdata->spinlock); for (i = 0; i < drvdata->nr_cntr; i++) ret += sprintf(buf, "counter %d: %x\n", - i, drvdata->cntr_val[i]); + i, config->cntr_val[i]); spin_unlock(&drvdata->spinlock); return ret; } @@ -715,13 +740,14 @@ static ssize_t cntr_val_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - drvdata->cntr_val[drvdata->cntr_idx] = val; + config->cntr_val[config->cntr_idx] = val; spin_unlock(&drvdata->spinlock); return size; @@ -733,8 +759,9 @@ static ssize_t seq_12_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->seq_12_event; + val = config->seq_12_event; return sprintf(buf, "%#lx\n", val); } @@ -745,12 +772,13 @@ static ssize_t seq_12_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->seq_12_event = val & ETM_EVENT_MASK; + config->seq_12_event = val & ETM_EVENT_MASK; return size; } static DEVICE_ATTR_RW(seq_12_event); @@ -760,8 +788,9 @@ static ssize_t seq_21_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->seq_21_event; + val = config->seq_21_event; return sprintf(buf, "%#lx\n", val); } @@ -772,12 +801,13 @@ static ssize_t seq_21_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->seq_21_event = val & ETM_EVENT_MASK; + config->seq_21_event = val & ETM_EVENT_MASK; return size; } static DEVICE_ATTR_RW(seq_21_event); @@ -787,8 +817,9 @@ static ssize_t seq_23_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->seq_23_event; + val = config->seq_23_event; return sprintf(buf, "%#lx\n", val); } @@ -799,12 +830,13 @@ static ssize_t seq_23_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->seq_23_event = val & ETM_EVENT_MASK; + config->seq_23_event = val & ETM_EVENT_MASK; return size; } static DEVICE_ATTR_RW(seq_23_event); @@ -814,8 +846,9 @@ static ssize_t seq_31_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->seq_31_event; + val = config->seq_31_event; return sprintf(buf, "%#lx\n", val); } @@ -826,12 +859,13 @@ static ssize_t seq_31_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->seq_31_event = val & ETM_EVENT_MASK; + config->seq_31_event = val & ETM_EVENT_MASK; return size; } static DEVICE_ATTR_RW(seq_31_event); @@ -841,8 +875,9 @@ static ssize_t seq_32_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->seq_32_event; + val = config->seq_32_event; return sprintf(buf, "%#lx\n", val); } @@ -853,12 +888,13 @@ static ssize_t seq_32_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->seq_32_event = val & ETM_EVENT_MASK; + config->seq_32_event = val & ETM_EVENT_MASK; return size; } static DEVICE_ATTR_RW(seq_32_event); @@ -868,8 +904,9 @@ static ssize_t seq_13_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->seq_13_event; + val = config->seq_13_event; return sprintf(buf, "%#lx\n", val); } @@ -880,12 +917,13 @@ static ssize_t seq_13_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->seq_13_event = val & ETM_EVENT_MASK; + config->seq_13_event = val & ETM_EVENT_MASK; return size; } static DEVICE_ATTR_RW(seq_13_event); @@ -895,9 +933,10 @@ static ssize_t seq_curr_state_show(struct device *dev, { unsigned long val, flags; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; if (!drvdata->enable) { - val = drvdata->seq_curr_state; + val = config->seq_curr_state; goto out; } @@ -921,6 +960,7 @@ static ssize_t seq_curr_state_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) @@ -929,7 +969,7 @@ static ssize_t seq_curr_state_store(struct device *dev, if (val > ETM_SEQ_STATE_MAX_VAL) return -EINVAL; - drvdata->seq_curr_state = val; + config->seq_curr_state = val; return size; } @@ -940,8 +980,9 @@ static ssize_t ctxid_idx_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->ctxid_idx; + val = config->ctxid_idx; return sprintf(buf, "%#lx\n", val); } @@ -952,6 +993,7 @@ static ssize_t ctxid_idx_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) @@ -965,7 +1007,7 @@ static ssize_t ctxid_idx_store(struct device *dev, * dereferenced multiple times within a spinlock block elsewhere. */ spin_lock(&drvdata->spinlock); - drvdata->ctxid_idx = val; + config->ctxid_idx = val; spin_unlock(&drvdata->spinlock); return size; @@ -977,9 +1019,10 @@ static ssize_t ctxid_pid_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - val = drvdata->ctxid_vpid[drvdata->ctxid_idx]; + val = config->ctxid_vpid[config->ctxid_idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -992,6 +1035,7 @@ static ssize_t ctxid_pid_store(struct device *dev, int ret; unsigned long vpid, pid; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &vpid); if (ret) @@ -1000,8 +1044,8 @@ static ssize_t ctxid_pid_store(struct device *dev, pid = coresight_vpid_to_pid(vpid); spin_lock(&drvdata->spinlock); - drvdata->ctxid_pid[drvdata->ctxid_idx] = pid; - drvdata->ctxid_vpid[drvdata->ctxid_idx] = vpid; + config->ctxid_pid[config->ctxid_idx] = pid; + config->ctxid_vpid[config->ctxid_idx] = vpid; spin_unlock(&drvdata->spinlock); return size; @@ -1013,8 +1057,9 @@ static ssize_t ctxid_mask_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->ctxid_mask; + val = config->ctxid_mask; return sprintf(buf, "%#lx\n", val); } @@ -1025,12 +1070,13 @@ static ssize_t ctxid_mask_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->ctxid_mask = val; + config->ctxid_mask = val; return size; } static DEVICE_ATTR_RW(ctxid_mask); @@ -1040,8 +1086,9 @@ static ssize_t sync_freq_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->sync_freq; + val = config->sync_freq; return sprintf(buf, "%#lx\n", val); } @@ -1052,12 +1099,13 @@ static ssize_t sync_freq_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->sync_freq = val & ETM_SYNC_MASK; + config->sync_freq = val & ETM_SYNC_MASK; return size; } static DEVICE_ATTR_RW(sync_freq); @@ -1067,8 +1115,9 @@ static ssize_t timestamp_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->timestamp_event; + val = config->timestamp_event; return sprintf(buf, "%#lx\n", val); } @@ -1079,12 +1128,13 @@ static ssize_t timestamp_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->timestamp_event = val & ETM_EVENT_MASK; + config->timestamp_event = val & ETM_EVENT_MASK; return size; } static DEVICE_ATTR_RW(timestamp_event); diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index f2c74bb56993..1952dc31fee4 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -41,6 +41,7 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO); /* The number of ETM/PTM currently registered */ static int etm_count; static struct etm_drvdata *etmdrvdata[NR_CPUS]; +static void etm_init_default_data(struct etm_config *config); /* * Memory mapped writes to clear os lock are not supported on some processors @@ -186,36 +187,39 @@ static void etm_clr_prog(struct etm_drvdata *drvdata) } } -void etm_set_default(struct etm_drvdata *drvdata) +void etm_set_default(struct etm_config *config) { int i; - drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL; - drvdata->enable_event = ETM_HARD_WIRE_RES_A; + if (WARN_ON_ONCE(!config)) + return; - drvdata->seq_12_event = ETM_DEFAULT_EVENT_VAL; - drvdata->seq_21_event = ETM_DEFAULT_EVENT_VAL; - drvdata->seq_23_event = ETM_DEFAULT_EVENT_VAL; - drvdata->seq_31_event = ETM_DEFAULT_EVENT_VAL; - drvdata->seq_32_event = ETM_DEFAULT_EVENT_VAL; - drvdata->seq_13_event = ETM_DEFAULT_EVENT_VAL; - drvdata->timestamp_event = ETM_DEFAULT_EVENT_VAL; + config->trigger_event = ETM_DEFAULT_EVENT_VAL; + config->enable_event = ETM_HARD_WIRE_RES_A; - for (i = 0; i < drvdata->nr_cntr; i++) { - drvdata->cntr_rld_val[i] = 0x0; - drvdata->cntr_event[i] = ETM_DEFAULT_EVENT_VAL; - drvdata->cntr_rld_event[i] = ETM_DEFAULT_EVENT_VAL; - drvdata->cntr_val[i] = 0x0; + config->seq_12_event = ETM_DEFAULT_EVENT_VAL; + config->seq_21_event = ETM_DEFAULT_EVENT_VAL; + config->seq_23_event = ETM_DEFAULT_EVENT_VAL; + config->seq_31_event = ETM_DEFAULT_EVENT_VAL; + config->seq_32_event = ETM_DEFAULT_EVENT_VAL; + config->seq_13_event = ETM_DEFAULT_EVENT_VAL; + config->timestamp_event = ETM_DEFAULT_EVENT_VAL; + + for (i = 0; i < ETM_MAX_CNTR; i++) { + config->cntr_rld_val[i] = 0x0; + config->cntr_event[i] = ETM_DEFAULT_EVENT_VAL; + config->cntr_rld_event[i] = ETM_DEFAULT_EVENT_VAL; + config->cntr_val[i] = 0x0; } - drvdata->seq_curr_state = 0x0; - drvdata->ctxid_idx = 0x0; - for (i = 0; i < drvdata->nr_ctxid_cmp; i++) { - drvdata->ctxid_pid[i] = 0x0; - drvdata->ctxid_vpid[i] = 0x0; + config->seq_curr_state = 0x0; + config->ctxid_idx = 0x0; + for (i = 0; i < ETM_MAX_CTXID_CMP; i++) { + config->ctxid_pid[i] = 0x0; + config->ctxid_vpid[i] = 0x0; } - drvdata->ctxid_mask = 0x0; + config->ctxid_mask = 0x0; } static void etm_enable_hw(void *info) @@ -223,6 +227,7 @@ static void etm_enable_hw(void *info) int i; u32 etmcr; struct etm_drvdata *drvdata = info; + struct etm_config *config = &drvdata->config; CS_UNLOCK(drvdata->base); @@ -238,39 +243,39 @@ static void etm_enable_hw(void *info) etmcr = etm_readl(drvdata, ETMCR); etmcr &= (ETMCR_PWD_DWN | ETMCR_ETM_PRG); etmcr |= drvdata->port_size; - etm_writel(drvdata, drvdata->ctrl | etmcr, ETMCR); - etm_writel(drvdata, drvdata->trigger_event, ETMTRIGGER); - etm_writel(drvdata, drvdata->startstop_ctrl, ETMTSSCR); - etm_writel(drvdata, drvdata->enable_event, ETMTEEVR); - etm_writel(drvdata, drvdata->enable_ctrl1, ETMTECR1); - etm_writel(drvdata, drvdata->fifofull_level, ETMFFLR); + etm_writel(drvdata, config->ctrl | etmcr, ETMCR); + etm_writel(drvdata, config->trigger_event, ETMTRIGGER); + etm_writel(drvdata, config->startstop_ctrl, ETMTSSCR); + etm_writel(drvdata, config->enable_event, ETMTEEVR); + etm_writel(drvdata, config->enable_ctrl1, ETMTECR1); + etm_writel(drvdata, config->fifofull_level, ETMFFLR); for (i = 0; i < drvdata->nr_addr_cmp; i++) { - etm_writel(drvdata, drvdata->addr_val[i], ETMACVRn(i)); - etm_writel(drvdata, drvdata->addr_acctype[i], ETMACTRn(i)); + etm_writel(drvdata, config->addr_val[i], ETMACVRn(i)); + etm_writel(drvdata, config->addr_acctype[i], ETMACTRn(i)); } for (i = 0; i < drvdata->nr_cntr; i++) { - etm_writel(drvdata, drvdata->cntr_rld_val[i], ETMCNTRLDVRn(i)); - etm_writel(drvdata, drvdata->cntr_event[i], ETMCNTENRn(i)); - etm_writel(drvdata, drvdata->cntr_rld_event[i], + etm_writel(drvdata, config->cntr_rld_val[i], ETMCNTRLDVRn(i)); + etm_writel(drvdata, config->cntr_event[i], ETMCNTENRn(i)); + etm_writel(drvdata, config->cntr_rld_event[i], ETMCNTRLDEVRn(i)); - etm_writel(drvdata, drvdata->cntr_val[i], ETMCNTVRn(i)); + etm_writel(drvdata, config->cntr_val[i], ETMCNTVRn(i)); } - etm_writel(drvdata, drvdata->seq_12_event, ETMSQ12EVR); - etm_writel(drvdata, drvdata->seq_21_event, ETMSQ21EVR); - etm_writel(drvdata, drvdata->seq_23_event, ETMSQ23EVR); - etm_writel(drvdata, drvdata->seq_31_event, ETMSQ31EVR); - etm_writel(drvdata, drvdata->seq_32_event, ETMSQ32EVR); - etm_writel(drvdata, drvdata->seq_13_event, ETMSQ13EVR); - etm_writel(drvdata, drvdata->seq_curr_state, ETMSQR); + etm_writel(drvdata, config->seq_12_event, ETMSQ12EVR); + etm_writel(drvdata, config->seq_21_event, ETMSQ21EVR); + etm_writel(drvdata, config->seq_23_event, ETMSQ23EVR); + etm_writel(drvdata, config->seq_31_event, ETMSQ31EVR); + etm_writel(drvdata, config->seq_32_event, ETMSQ32EVR); + etm_writel(drvdata, config->seq_13_event, ETMSQ13EVR); + etm_writel(drvdata, config->seq_curr_state, ETMSQR); for (i = 0; i < drvdata->nr_ext_out; i++) etm_writel(drvdata, ETM_DEFAULT_EVENT_VAL, ETMEXTOUTEVRn(i)); for (i = 0; i < drvdata->nr_ctxid_cmp; i++) - etm_writel(drvdata, drvdata->ctxid_pid[i], ETMCIDCVRn(i)); - etm_writel(drvdata, drvdata->ctxid_mask, ETMCIDCMR); - etm_writel(drvdata, drvdata->sync_freq, ETMSYNCFR); + etm_writel(drvdata, config->ctxid_pid[i], ETMCIDCVRn(i)); + etm_writel(drvdata, config->ctxid_mask, ETMCIDCMR); + etm_writel(drvdata, config->sync_freq, ETMSYNCFR); /* No external input selected */ etm_writel(drvdata, 0x0, ETMEXTINSELR); - etm_writel(drvdata, drvdata->timestamp_event, ETMTSEVR); + etm_writel(drvdata, config->timestamp_event, ETMTSEVR); /* No auxiliary control selected */ etm_writel(drvdata, 0x0, ETMAUXCR); etm_writel(drvdata, drvdata->traceid, ETMTRACEIDR); @@ -278,7 +283,7 @@ static void etm_enable_hw(void *info) etm_writel(drvdata, 0x0, ETMVMIDCVR); /* Ensures trace output is enabled from this ETM */ - etm_writel(drvdata, drvdata->ctrl | ETMCR_ETM_EN | etmcr, ETMCR); + etm_writel(drvdata, config->ctrl | ETMCR_ETM_EN | etmcr, ETMCR); etm_clr_prog(drvdata); CS_LOCK(drvdata->base); @@ -362,6 +367,7 @@ static void etm_disable_hw(void *info) { int i; struct etm_drvdata *drvdata = info; + struct etm_config *config = &drvdata->config; CS_UNLOCK(drvdata->base); etm_set_prog(drvdata); @@ -370,10 +376,10 @@ static void etm_disable_hw(void *info) etm_writel(drvdata, ETM_HARD_WIRE_RES_A | ETM_EVENT_NOT_A, ETMTEEVR); /* Read back sequencer and counters for post trace analysis */ - drvdata->seq_curr_state = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK); + config->seq_curr_state = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK); for (i = 0; i < drvdata->nr_cntr; i++) - drvdata->cntr_val[i] = etm_readl(drvdata, ETMCNTVRn(i)); + config->cntr_val[i] = etm_readl(drvdata, ETMCNTVRn(i)); etm_set_pwrdwn(drvdata); CS_LOCK(drvdata->base); @@ -522,14 +528,8 @@ static void etm_init_arch_data(void *info) CS_LOCK(drvdata->base); } -static void etm_init_default_data(struct etm_drvdata *drvdata) +static void etm_init_default_data(struct etm_config *config) { - /* - * A trace ID of value 0 is invalid, so let's start at some - * random value that fits in 7 bits and will be just as good. - */ - static int etm3x_traceid = 0x10; - u32 flags = (1 << 0 | /* instruction execute*/ 3 << 3 | /* ARM instruction */ 0 << 5 | /* No data value comparison */ @@ -537,25 +537,28 @@ static void etm_init_default_data(struct etm_drvdata *drvdata) 0 << 8 | /* Ignore context ID */ 0 << 10); /* Security ignored */ - /* - * Initial configuration only - guarantees sources handled by - * this driver have a unique ID at startup time but not between - * all other types of sources. For that we lean on the core - * framework. - */ - drvdata->traceid = etm3x_traceid++; - drvdata->ctrl = (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN); - drvdata->enable_ctrl1 = ETMTECR1_ADDR_COMP_1; - if (drvdata->nr_addr_cmp >= 2) { - drvdata->addr_val[0] = (u32) _stext; - drvdata->addr_val[1] = (u32) _etext; - drvdata->addr_acctype[0] = flags; - drvdata->addr_acctype[1] = flags; - drvdata->addr_type[0] = ETM_ADDR_TYPE_RANGE; - drvdata->addr_type[1] = ETM_ADDR_TYPE_RANGE; - } + if (WARN_ON_ONCE(!config)) + return; - etm_set_default(drvdata); + config->ctrl = (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN); + config->enable_ctrl1 = ETMTECR1_ADDR_COMP_1; + config->addr_val[0] = (u32) _stext; + config->addr_val[1] = (u32) _etext; + config->addr_acctype[0] = flags; + config->addr_acctype[1] = flags; + config->addr_type[0] = ETM_ADDR_TYPE_RANGE; + config->addr_type[1] = ETM_ADDR_TYPE_RANGE; + + etm_set_default(config); +} + +static void etm_init_trace_id(struct etm_drvdata *drvdata) +{ + /* + * A trace ID of value 0 is invalid, so let's start at some + * random value that fits in 7 bits and go from there. + */ + drvdata->traceid = 0x10 + drvdata->cpu; } static int etm_probe(struct amba_device *adev, const struct amba_id *id) @@ -623,7 +626,9 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) ret = -EINVAL; goto err_arch_supported; } - etm_init_default_data(drvdata); + + etm_init_trace_id(drvdata); + etm_init_default_data(&drvdata->config); desc->type = CORESIGHT_DEV_TYPE_SOURCE; desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC; From 877324a46672c3bf72a423fcd9b257642f29a50e Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:52 -0700 Subject: [PATCH 038/104] coresight: etm3x: adding operation mode for etm_enable() Adding a new mode to source API enable() in order to distinguish where the request comes from. That way it is possible to perform different operations based on where the request was issued from. The ETM4x driver is also modified to keep in sync with the new interface. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 22fd532eaa0c24d86e23d8e9e3b7feac4a8cac80) --- drivers/hwtracing/coresight/coresight-etm.h | 5 +- .../coresight/coresight-etm3x-sysfs.c | 4 +- drivers/hwtracing/coresight/coresight-etm3x.c | 68 ++++++++++++++++--- drivers/hwtracing/coresight/coresight-etm4x.c | 2 +- drivers/hwtracing/coresight/coresight-priv.h | 6 ++ drivers/hwtracing/coresight/coresight.c | 6 +- include/linux/coresight.h | 2 +- 7 files changed, 76 insertions(+), 17 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index 371fb7d2e829..5b29d5540fe5 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -13,6 +13,7 @@ #ifndef _CORESIGHT_CORESIGHT_ETM_H #define _CORESIGHT_CORESIGHT_ETM_H +#include #include #include "coresight-priv.h" @@ -214,7 +215,7 @@ struct etm_config { * @port_size: port size as reported by ETMCR bit 4-6 and 21. * @arch: ETM/PTM version number. * @use_cpu14: true if management registers need to be accessed via CP14. - * @enable: is this ETM/PTM currently tracing. + * @mode: this tracer's mode, i.e sysFS, Perf or disabled. * @sticky_enable: true if ETM base configuration has been done. * @boot_enable:true if we should start tracing at boot time. * @os_unlock: true if access to management registers is allowed. @@ -238,7 +239,7 @@ struct etm_drvdata { int port_size; u8 arch; bool use_cp14; - bool enable; + local_t mode; bool sticky_enable; bool boot_enable; bool os_unlock; diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index 456df2378a6f..387c79fd9d5e 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -716,7 +716,7 @@ static ssize_t cntr_val_show(struct device *dev, struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); struct etm_config *config = &drvdata->config; - if (!drvdata->enable) { + if (!local_read(&drvdata->mode)) { spin_lock(&drvdata->spinlock); for (i = 0; i < drvdata->nr_cntr; i++) ret += sprintf(buf, "counter %d: %x\n", @@ -935,7 +935,7 @@ static ssize_t seq_curr_state_show(struct device *dev, struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); struct etm_config *config = &drvdata->config; - if (!drvdata->enable) { + if (!local_read(&drvdata->mode)) { val = config->seq_curr_state; goto out; } diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 1952dc31fee4..e16501b41ed8 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -306,7 +306,7 @@ int etm_get_trace_id(struct etm_drvdata *drvdata) if (!drvdata) goto out; - if (!drvdata->enable) + if (!local_read(&drvdata->mode)) return drvdata->traceid; pm_runtime_get_sync(drvdata->dev); @@ -332,7 +332,7 @@ static int etm_trace_id(struct coresight_device *csdev) return etm_get_trace_id(drvdata); } -static int etm_enable(struct coresight_device *csdev) +static int etm_enable_sysfs(struct coresight_device *csdev) { struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); int ret; @@ -351,18 +351,44 @@ static int etm_enable(struct coresight_device *csdev) goto err; } - drvdata->enable = true; drvdata->sticky_enable = true; - spin_unlock(&drvdata->spinlock); dev_info(drvdata->dev, "ETM tracing enabled\n"); return 0; + err: spin_unlock(&drvdata->spinlock); return ret; } +static int etm_enable(struct coresight_device *csdev, u32 mode) +{ + int ret; + u32 val; + struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode); + + /* Someone is already using the tracer */ + if (val) + return -EBUSY; + + switch (mode) { + case CS_MODE_SYSFS: + ret = etm_enable_sysfs(csdev); + break; + default: + ret = -EINVAL; + } + + /* The tracer didn't start */ + if (ret) + local_set(&drvdata->mode, CS_MODE_DISABLED); + + return ret; +} + static void etm_disable_hw(void *info) { int i; @@ -387,7 +413,7 @@ static void etm_disable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu); } -static void etm_disable(struct coresight_device *csdev) +static void etm_disable_sysfs(struct coresight_device *csdev) { struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -405,7 +431,6 @@ static void etm_disable(struct coresight_device *csdev) * ensures that register writes occur when cpu is powered. */ smp_call_function_single(drvdata->cpu, etm_disable_hw, drvdata, 1); - drvdata->enable = false; spin_unlock(&drvdata->spinlock); put_online_cpus(); @@ -413,6 +438,33 @@ static void etm_disable(struct coresight_device *csdev) dev_info(drvdata->dev, "ETM tracing disabled\n"); } +static void etm_disable(struct coresight_device *csdev) +{ + u32 mode; + struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + /* + * For as long as the tracer isn't disabled another entity can't + * change its status. As such we can read the status here without + * fearing it will change under us. + */ + mode = local_read(&drvdata->mode); + + switch (mode) { + case CS_MODE_DISABLED: + break; + case CS_MODE_SYSFS: + etm_disable_sysfs(csdev); + break; + default: + WARN_ON_ONCE(mode); + return; + } + + if (mode) + local_set(&drvdata->mode, CS_MODE_DISABLED); +} + static const struct coresight_ops_source etm_source_ops = { .cpu_id = etm_cpu_id, .trace_id = etm_trace_id, @@ -440,7 +492,7 @@ static int etm_cpu_callback(struct notifier_block *nfb, unsigned long action, etmdrvdata[cpu]->os_unlock = true; } - if (etmdrvdata[cpu]->enable) + if (local_read(&etmdrvdata[cpu]->mode)) etm_enable_hw(etmdrvdata[cpu]); spin_unlock(&etmdrvdata[cpu]->spinlock); break; @@ -453,7 +505,7 @@ static int etm_cpu_callback(struct notifier_block *nfb, unsigned long action, case CPU_DYING: spin_lock(&etmdrvdata[cpu]->spinlock); - if (etmdrvdata[cpu]->enable) + if (local_read(&etmdrvdata[cpu]->mode)) etm_disable_hw(etmdrvdata[cpu]); spin_unlock(&etmdrvdata[cpu]->spinlock); break; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index c2f518fbc9a8..0026092fec7f 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -187,7 +187,7 @@ static void etm4_enable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu); } -static int etm4_enable(struct coresight_device *csdev) +static int etm4_enable(struct coresight_device *csdev, u32 mode) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); int ret; diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 14f245a2018d..ed116b303e87 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -34,6 +34,12 @@ #define TIMEOUT_US 100 #define BMVAL(val, lsb, msb) ((val & GENMASK(msb, lsb)) >> lsb) +enum cs_mode { + CS_MODE_DISABLED, + CS_MODE_SYSFS, + CS_MODE_PERF, +}; + static inline void CS_LOCK(void __iomem *addr) { do { diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 6b44928c1076..b20afb709141 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -222,7 +222,7 @@ static void coresight_disable_link(struct coresight_device *csdev, csdev->enable = false; } -static int coresight_enable_source(struct coresight_device *csdev) +static int coresight_enable_source(struct coresight_device *csdev, u32 mode) { int ret; @@ -234,7 +234,7 @@ static int coresight_enable_source(struct coresight_device *csdev) if (!csdev->enable) { if (source_ops(csdev)->enable) { - ret = source_ops(csdev)->enable(csdev); + ret = source_ops(csdev)->enable(csdev, mode); if (ret) return ret; } @@ -458,7 +458,7 @@ int coresight_enable(struct coresight_device *csdev) if (ret) goto err_path; - ret = coresight_enable_source(csdev); + ret = coresight_enable_source(csdev, CS_MODE_SYSFS); if (ret) goto err_source; diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 851ecb22397e..61dfb8d511ea 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -213,7 +213,7 @@ struct coresight_ops_link { struct coresight_ops_source { int (*cpu_id)(struct coresight_device *csdev); int (*trace_id)(struct coresight_device *csdev); - int (*enable)(struct coresight_device *csdev); + int (*enable)(struct coresight_device *csdev, u32 mode); void (*disable)(struct coresight_device *csdev); }; From 9e977106258d578d7b9d29251084a43e025a914d Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:53 -0700 Subject: [PATCH 039/104] coresight: etm3x: set progbit to stop trace collection There is no need to use the event enable's "always false" event to stop trace collection. For that purpose setting the programming bit (ETMCR:10) is enough. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 47cd066cd00a65902ee3bd57da5bd395cb83aff9) --- drivers/hwtracing/coresight/coresight-etm3x.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index e16501b41ed8..447459969cb5 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -398,9 +398,6 @@ static void etm_disable_hw(void *info) CS_UNLOCK(drvdata->base); etm_set_prog(drvdata); - /* Program trace enable to low by using always false event */ - etm_writel(drvdata, ETM_HARD_WIRE_RES_A | ETM_EVENT_NOT_A, ETMTEEVR); - /* Read back sequencer and counters for post trace analysis */ config->seq_curr_state = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK); From 9226d25094f893acde0eb8e04ce1564d83a3ccff Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:54 -0700 Subject: [PATCH 040/104] coresight: etm3x: changing default trace configuration Changing default configuration to include the entire address range rather than just the kernel. That way traces are more inclusive and it is easier to narrow down if needed. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit e19217299caf1a54c55081ab6339b3baccec63b0) --- drivers/hwtracing/coresight/coresight-etm.h | 2 ++ drivers/hwtracing/coresight/coresight-etm3x.c | 29 +++++++++---------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index 5b29d5540fe5..44585d4adb26 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -146,6 +146,7 @@ * @startstop_ctrl: setting for register ETMTSSCR. * @enable_event: setting for register ETMTEEVR. * @enable_ctrl1: setting for register ETMTECR1. + * @enable_ctrl2: setting for register ETMTECR2. * @fifofull_level: setting for register ETMFFLR. * @addr_idx: index for the address comparator selection. * @addr_val: value for address comparator register. @@ -179,6 +180,7 @@ struct etm_config { u32 startstop_ctrl; u32 enable_event; u32 enable_ctrl1; + u32 enable_ctrl2; u32 fifofull_level; u8 addr_idx; u32 addr_val[ETM_MAX_ADDR_CMP]; diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 447459969cb5..92139674bea4 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -579,26 +579,23 @@ static void etm_init_arch_data(void *info) static void etm_init_default_data(struct etm_config *config) { - u32 flags = (1 << 0 | /* instruction execute*/ - 3 << 3 | /* ARM instruction */ - 0 << 5 | /* No data value comparison */ - 0 << 7 | /* No exact mach */ - 0 << 8 | /* Ignore context ID */ - 0 << 10); /* Security ignored */ - if (WARN_ON_ONCE(!config)) return; - config->ctrl = (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN); - config->enable_ctrl1 = ETMTECR1_ADDR_COMP_1; - config->addr_val[0] = (u32) _stext; - config->addr_val[1] = (u32) _etext; - config->addr_acctype[0] = flags; - config->addr_acctype[1] = flags; - config->addr_type[0] = ETM_ADDR_TYPE_RANGE; - config->addr_type[1] = ETM_ADDR_TYPE_RANGE; - etm_set_default(config); + + /* + * Taken verbatim from the TRM: + * + * To trace all memory: + * set bit [24] in register 0x009, the ETMTECR1, to 1 + * set all other bits in register 0x009, the ETMTECR1, to 0 + * set all bits in register 0x007, the ETMTECR2, to 0 + * set register 0x008, the ETMTEEVR, to 0x6F (TRUE). + */ + config->enable_ctrl1 = BIT(24); + config->enable_ctrl2 = 0x0; + config->enable_event = ETM_HARD_WIRE_RES_A; } static void etm_init_trace_id(struct etm_drvdata *drvdata) From 003099a9ab219bb985eabfdb64bb29510ff5c0fd Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:55 -0700 Subject: [PATCH 041/104] coresight: etm3x: consolidating initial config There is really no point in having two functions to take care of doing the initial tracer configuration. As such moving everything to 'etm_set_default()'. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit c528a25ac7c4dacba9e4d98d5f06846939c5966f) --- drivers/hwtracing/coresight/coresight-etm3x.c | 37 +++++++------------ 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 92139674bea4..34a69583ccbc 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -41,7 +41,6 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO); /* The number of ETM/PTM currently registered */ static int etm_count; static struct etm_drvdata *etmdrvdata[NR_CPUS]; -static void etm_init_default_data(struct etm_config *config); /* * Memory mapped writes to clear os lock are not supported on some processors @@ -194,6 +193,19 @@ void etm_set_default(struct etm_config *config) if (WARN_ON_ONCE(!config)) return; + /* + * Taken verbatim from the TRM: + * + * To trace all memory: + * set bit [24] in register 0x009, the ETMTECR1, to 1 + * set all other bits in register 0x009, the ETMTECR1, to 0 + * set all bits in register 0x007, the ETMTECR2, to 0 + * set register 0x008, the ETMTEEVR, to 0x6F (TRUE). + */ + config->enable_ctrl1 = BIT(24); + config->enable_ctrl2 = 0x0; + config->enable_event = ETM_HARD_WIRE_RES_A; + config->trigger_event = ETM_DEFAULT_EVENT_VAL; config->enable_event = ETM_HARD_WIRE_RES_A; @@ -577,27 +589,6 @@ static void etm_init_arch_data(void *info) CS_LOCK(drvdata->base); } -static void etm_init_default_data(struct etm_config *config) -{ - if (WARN_ON_ONCE(!config)) - return; - - etm_set_default(config); - - /* - * Taken verbatim from the TRM: - * - * To trace all memory: - * set bit [24] in register 0x009, the ETMTECR1, to 1 - * set all other bits in register 0x009, the ETMTECR1, to 0 - * set all bits in register 0x007, the ETMTECR2, to 0 - * set register 0x008, the ETMTEEVR, to 0x6F (TRUE). - */ - config->enable_ctrl1 = BIT(24); - config->enable_ctrl2 = 0x0; - config->enable_event = ETM_HARD_WIRE_RES_A; -} - static void etm_init_trace_id(struct etm_drvdata *drvdata) { /* @@ -674,7 +665,7 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) } etm_init_trace_id(drvdata); - etm_init_default_data(&drvdata->config); + etm_set_default(&drvdata->config); desc->type = CORESIGHT_DEV_TYPE_SOURCE; desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC; From 749ef4caf345a43c284712b3e4507af76aa7b503 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:56 -0700 Subject: [PATCH 042/104] coresight: etm3x: implementing user/kernel mode tracing Adding new mode to limit tracing to kernel or user space. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 2127154d115d4fe8f18300e5ef6f566581359d56) --- drivers/hwtracing/coresight/coresight-etm.h | 6 +- .../coresight/coresight-etm3x-sysfs.c | 4 ++ drivers/hwtracing/coresight/coresight-etm3x.c | 63 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-priv.h | 3 + 4 files changed, 75 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index 44585d4adb26..51597cb2c08a 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -110,7 +110,10 @@ #define ETM_MODE_STALL BIT(2) #define ETM_MODE_TIMESTAMP BIT(3) #define ETM_MODE_CTXID BIT(4) -#define ETM_MODE_ALL 0x1f +#define ETM_MODE_ALL (ETM_MODE_EXCLUDE | ETM_MODE_CYCACC | \ + ETM_MODE_STALL | ETM_MODE_TIMESTAMP | \ + ETM_MODE_CTXID | ETM_MODE_EXCL_KERN | \ + ETM_MODE_EXCL_USER) #define ETM_SQR_MASK 0x3 #define ETM_TRACEID_MASK 0x3f @@ -296,5 +299,6 @@ static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off) extern const struct attribute_group *coresight_etm_groups[]; int etm_get_trace_id(struct etm_drvdata *drvdata); void etm_set_default(struct etm_config *config); +void etm_config_trace_mode(struct etm_config *config); struct etm_config *get_etm_config(struct etm_drvdata *drvdata); #endif diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index 387c79fd9d5e..cbb4046c1070 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -162,6 +162,10 @@ static ssize_t mode_store(struct device *dev, config->ctrl |= ETMCR_CTXID_SIZE; else config->ctrl &= ~ETMCR_CTXID_SIZE; + + if (config->mode & (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER)) + etm_config_trace_mode(config); + spin_unlock(&drvdata->spinlock); return size; diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 34a69583ccbc..c82d545e68ef 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -234,6 +234,69 @@ void etm_set_default(struct etm_config *config) config->ctxid_mask = 0x0; } +void etm_config_trace_mode(struct etm_config *config) +{ + u32 flags, mode; + + mode = config->mode; + + mode &= (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER); + + /* excluding kernel AND user space doesn't make sense */ + if (mode == (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER)) + return; + + /* nothing to do if neither flags are set */ + if (!(mode & ETM_MODE_EXCL_KERN) && !(mode & ETM_MODE_EXCL_USER)) + return; + + flags = (1 << 0 | /* instruction execute */ + 3 << 3 | /* ARM instruction */ + 0 << 5 | /* No data value comparison */ + 0 << 7 | /* No exact mach */ + 0 << 8); /* Ignore context ID */ + + /* No need to worry about single address comparators. */ + config->enable_ctrl2 = 0x0; + + /* Bit 0 is address range comparator 1 */ + config->enable_ctrl1 = ETMTECR1_ADDR_COMP_1; + + /* + * On ETMv3.5: + * ETMACTRn[13,11] == Non-secure state comparison control + * ETMACTRn[12,10] == Secure state comparison control + * + * b00 == Match in all modes in this state + * b01 == Do not match in any more in this state + * b10 == Match in all modes excepts user mode in this state + * b11 == Match only in user mode in this state + */ + + /* Tracing in secure mode is not supported at this time */ + flags |= (0 << 12 | 1 << 10); + + if (mode & ETM_MODE_EXCL_USER) { + /* exclude user, match all modes except user mode */ + flags |= (1 << 13 | 0 << 11); + } else { + /* exclude kernel, match only in user mode */ + flags |= (1 << 13 | 1 << 11); + } + + /* + * The ETMEEVR register is already set to "hard wire A". As such + * all there is to do is setup an address comparator that spans + * the entire address range and configure the state and mode bits. + */ + config->addr_val[0] = (u32) 0x0; + config->addr_val[1] = (u32) ~0x0; + config->addr_acctype[0] = flags; + config->addr_acctype[1] = flags; + config->addr_type[0] = ETM_ADDR_TYPE_RANGE; + config->addr_type[1] = ETM_ADDR_TYPE_RANGE; +} + static void etm_enable_hw(void *info) { int i; diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index ed116b303e87..932f34a84d96 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -34,6 +34,9 @@ #define TIMEOUT_US 100 #define BMVAL(val, lsb, msb) ((val & GENMASK(msb, lsb)) >> lsb) +#define ETM_MODE_EXCL_KERN BIT(30) +#define ETM_MODE_EXCL_USER BIT(31) + enum cs_mode { CS_MODE_DISABLED, CS_MODE_SYSFS, From 2d25f4c5e5709286d85887bec2d5528dd2cb6eb3 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:57 -0700 Subject: [PATCH 043/104] coresight: etm3x: implementing perf_enable/disable() API That way traces can be enabled and disabled automatically from the Perf subystem using the PMU abstraction. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 882d5e112491c875ab7c8c336b8beaeec54d0509) --- drivers/hwtracing/coresight/Kconfig | 1 + drivers/hwtracing/coresight/coresight-etm3x.c | 95 ++++++++++++++++++- drivers/hwtracing/coresight/coresight-etm4x.c | 4 +- drivers/hwtracing/coresight/coresight.c | 2 +- include/linux/coresight.h | 6 +- 5 files changed, 99 insertions(+), 9 deletions(-) diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index c85935f3525a..db0541031c72 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -4,6 +4,7 @@ menuconfig CORESIGHT bool "CoreSight Tracing Support" select ARM_AMBA + select PERF_EVENTS help This framework provides a kernel interface for the CoreSight debug and trace drivers to register themselves with. It's intended to build diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index c82d545e68ef..a9b820ec16aa 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "coresight-etm.h" @@ -297,6 +298,47 @@ void etm_config_trace_mode(struct etm_config *config) config->addr_type[1] = ETM_ADDR_TYPE_RANGE; } +#define ETM3X_SUPPORTED_OPTIONS (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN) + +static int etm_parse_event_config(struct etm_drvdata *drvdata, + struct perf_event_attr *attr) +{ + struct etm_config *config = &drvdata->config; + + if (!attr) + return -EINVAL; + + /* Clear configuration from previous run */ + memset(config, 0, sizeof(struct etm_config)); + + if (attr->exclude_kernel) + config->mode = ETM_MODE_EXCL_KERN; + + if (attr->exclude_user) + config->mode = ETM_MODE_EXCL_USER; + + /* Always start from the default config */ + etm_set_default(config); + + /* + * By default the tracers are configured to trace the whole address + * range. Narrow the field only if requested by user space. + */ + if (config->mode) + etm_config_trace_mode(config); + + /* + * At this time only cycle accurate and timestamp options are + * available. + */ + if (attr->config & ~ETM3X_SUPPORTED_OPTIONS) + return -EINVAL; + + config->ctrl = attr->config; + + return 0; +} + static void etm_enable_hw(void *info) { int i; @@ -316,8 +358,10 @@ static void etm_enable_hw(void *info) etm_set_prog(drvdata); etmcr = etm_readl(drvdata, ETMCR); - etmcr &= (ETMCR_PWD_DWN | ETMCR_ETM_PRG); + /* Clear setting from a previous run if need be */ + etmcr &= ~ETM3X_SUPPORTED_OPTIONS; etmcr |= drvdata->port_size; + etmcr |= ETMCR_ETM_EN; etm_writel(drvdata, config->ctrl | etmcr, ETMCR); etm_writel(drvdata, config->trigger_event, ETMTRIGGER); etm_writel(drvdata, config->startstop_ctrl, ETMTSSCR); @@ -357,9 +401,6 @@ static void etm_enable_hw(void *info) /* No VMID comparator value selected */ etm_writel(drvdata, 0x0, ETMVMIDCVR); - /* Ensures trace output is enabled from this ETM */ - etm_writel(drvdata, config->ctrl | ETMCR_ETM_EN | etmcr, ETMCR); - etm_clr_prog(drvdata); CS_LOCK(drvdata->base); @@ -407,6 +448,22 @@ static int etm_trace_id(struct coresight_device *csdev) return etm_get_trace_id(drvdata); } +static int etm_enable_perf(struct coresight_device *csdev, + struct perf_event_attr *attr) +{ + struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) + return -EINVAL; + + /* Configure the tracer based on the session's specifics */ + etm_parse_event_config(drvdata, attr); + /* And enable it */ + etm_enable_hw(drvdata); + + return 0; +} + static int etm_enable_sysfs(struct coresight_device *csdev) { struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -437,7 +494,8 @@ static int etm_enable_sysfs(struct coresight_device *csdev) return ret; } -static int etm_enable(struct coresight_device *csdev, u32 mode) +static int etm_enable(struct coresight_device *csdev, + struct perf_event_attr *attr, u32 mode) { int ret; u32 val; @@ -453,6 +511,9 @@ static int etm_enable(struct coresight_device *csdev, u32 mode) case CS_MODE_SYSFS: ret = etm_enable_sysfs(csdev); break; + case CS_MODE_PERF: + ret = etm_enable_perf(csdev, attr); + break; default: ret = -EINVAL; } @@ -485,6 +546,27 @@ static void etm_disable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu); } +static void etm_disable_perf(struct coresight_device *csdev) +{ + struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) + return; + + CS_UNLOCK(drvdata->base); + + /* Setting the prog bit disables tracing immediately */ + etm_set_prog(drvdata); + + /* + * There is no way to know when the tracer will be used again so + * power down the tracer. + */ + etm_set_pwrdwn(drvdata); + + CS_LOCK(drvdata->base); +} + static void etm_disable_sysfs(struct coresight_device *csdev) { struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -528,6 +610,9 @@ static void etm_disable(struct coresight_device *csdev) case CS_MODE_SYSFS: etm_disable_sysfs(csdev); break; + case CS_MODE_PERF: + etm_disable_perf(csdev); + break; default: WARN_ON_ONCE(mode); return; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 0026092fec7f..d0169ba7fbf2 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include "coresight-etm4x.h" @@ -187,7 +188,8 @@ static void etm4_enable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu); } -static int etm4_enable(struct coresight_device *csdev, u32 mode) +static int etm4_enable(struct coresight_device *csdev, + struct perf_event_attr *attr, u32 mode) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); int ret; diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index b20afb709141..95cccb179763 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -234,7 +234,7 @@ static int coresight_enable_source(struct coresight_device *csdev, u32 mode) if (!csdev->enable) { if (source_ops(csdev)->enable) { - ret = source_ops(csdev)->enable(csdev, mode); + ret = source_ops(csdev)->enable(csdev, NULL, mode); if (ret) return ret; } diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 61dfb8d511ea..6801dd64ee5d 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -14,6 +14,7 @@ #define _LINUX_CORESIGHT_H #include +#include #include /* Peripheral id registers (0xFD0-0xFEC) */ @@ -206,14 +207,15 @@ struct coresight_ops_link { * @cpu_id: returns the value of the CPU number this component * is associated to. * @trace_id: returns the value of the component's trace ID as known - to the HW. + * to the HW. * @enable: enables tracing for a source. * @disable: disables tracing for a source. */ struct coresight_ops_source { int (*cpu_id)(struct coresight_device *csdev); int (*trace_id)(struct coresight_device *csdev); - int (*enable)(struct coresight_device *csdev, u32 mode); + int (*enable)(struct coresight_device *csdev, + struct perf_event_attr *attr, u32 mode); void (*disable)(struct coresight_device *csdev); }; From 1aac114220bb712bb693f93a636be4bb9b27876e Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:58 -0700 Subject: [PATCH 044/104] coresight: etb10: moving to local atomic operations Moving to use local atomic operations to take advantage of the lockless implementation, something that will come handy when the ETB is accessed from the Perf subsystem. Also changing the name of the variable to something more meaningful. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 27b10da8fff27d74b755707e61637f6ab488c617) --- drivers/hwtracing/coresight/coresight-etb10.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 917562ecf82a..162c9ccc8c33 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -10,6 +10,7 @@ * GNU General Public License for more details. */ +#include #include #include #include @@ -71,7 +72,7 @@ * @csdev: component vitals needed by the framework. * @miscdev: specifics to handle "/dev/xyz.etb" entry. * @spinlock: only one at a time pls. - * @in_use: synchronise user space access to etb buffer. + * @reading: synchronise user space access to etb buffer. * @buf: area of memory where ETB buffer content gets sent. * @buffer_depth: size of @buf. * @enable: this ETB is being used. @@ -84,7 +85,7 @@ struct etb_drvdata { struct coresight_device *csdev; struct miscdevice miscdev; spinlock_t spinlock; - atomic_t in_use; + local_t reading; u8 *buf; u32 buffer_depth; bool enable; @@ -277,7 +278,7 @@ static int etb_open(struct inode *inode, struct file *file) struct etb_drvdata *drvdata = container_of(file->private_data, struct etb_drvdata, miscdev); - if (atomic_cmpxchg(&drvdata->in_use, 0, 1)) + if (local_cmpxchg(&drvdata->reading, 0, 1)) return -EBUSY; dev_dbg(drvdata->dev, "%s: successfully opened\n", __func__); @@ -313,7 +314,7 @@ static int etb_release(struct inode *inode, struct file *file) { struct etb_drvdata *drvdata = container_of(file->private_data, struct etb_drvdata, miscdev); - atomic_set(&drvdata->in_use, 0); + local_set(&drvdata->reading, 0); dev_dbg(drvdata->dev, "%s: released\n", __func__); return 0; From 5e1df2fa7a6dd8452f56e17c0db9b1214fd8590f Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:59 -0700 Subject: [PATCH 045/104] coresight: etb10: adding operation mode for sink->enable() Adding an operation mode to the sink->enable() API in order to prevent simultaneous access from different callers. TPIU and TMC won't be supplemented with the AUX area API immediately and as such ignore the new mode. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit e827d4550aa3225b8965ce4c266208cfe0297509) --- drivers/hwtracing/coresight/coresight-etb10.c | 30 ++++++++++++++----- drivers/hwtracing/coresight/coresight-priv.h | 2 +- drivers/hwtracing/coresight/coresight-tmc.c | 2 +- drivers/hwtracing/coresight/coresight-tpiu.c | 2 +- drivers/hwtracing/coresight/coresight.c | 10 +++---- include/linux/coresight.h | 2 +- 6 files changed, 32 insertions(+), 16 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 162c9ccc8c33..79099f95ba3f 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -73,9 +73,9 @@ * @miscdev: specifics to handle "/dev/xyz.etb" entry. * @spinlock: only one at a time pls. * @reading: synchronise user space access to etb buffer. + * @mode: this ETB is being used. * @buf: area of memory where ETB buffer content gets sent. * @buffer_depth: size of @buf. - * @enable: this ETB is being used. * @trigger_cntr: amount of words to store after a trigger. */ struct etb_drvdata { @@ -86,9 +86,9 @@ struct etb_drvdata { struct miscdevice miscdev; spinlock_t spinlock; local_t reading; + local_t mode; u8 *buf; u32 buffer_depth; - bool enable; u32 trigger_cntr; }; @@ -133,16 +133,31 @@ static void etb_enable_hw(struct etb_drvdata *drvdata) CS_LOCK(drvdata->base); } -static int etb_enable(struct coresight_device *csdev) +static int etb_enable(struct coresight_device *csdev, u32 mode) { - struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + u32 val; unsigned long flags; + struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + val = local_cmpxchg(&drvdata->mode, + CS_MODE_DISABLED, mode); + /* + * When accessing from Perf, a HW buffer can be handled + * by a single trace entity. In sysFS mode many tracers + * can be logging to the same HW buffer. + */ + if (val == CS_MODE_PERF) + return -EBUSY; + + /* Nothing to do, the tracer is already enabled. */ + if (val == CS_MODE_SYSFS) + goto out; spin_lock_irqsave(&drvdata->spinlock, flags); etb_enable_hw(drvdata); - drvdata->enable = true; spin_unlock_irqrestore(&drvdata->spinlock, flags); +out: dev_info(drvdata->dev, "ETB enabled\n"); return 0; } @@ -243,9 +258,10 @@ static void etb_disable(struct coresight_device *csdev) spin_lock_irqsave(&drvdata->spinlock, flags); etb_disable_hw(drvdata); etb_dump_hw(drvdata); - drvdata->enable = false; spin_unlock_irqrestore(&drvdata->spinlock, flags); + local_set(&drvdata->mode, CS_MODE_DISABLED); + dev_info(drvdata->dev, "ETB disabled\n"); } @@ -263,7 +279,7 @@ static void etb_dump(struct etb_drvdata *drvdata) unsigned long flags; spin_lock_irqsave(&drvdata->spinlock, flags); - if (drvdata->enable) { + if (local_read(&drvdata->mode) == CS_MODE_SYSFS) { etb_disable_hw(drvdata); etb_dump_hw(drvdata); etb_enable_hw(drvdata); diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 932f34a84d96..333eddaed339 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -62,7 +62,7 @@ static inline void CS_UNLOCK(void __iomem *addr) } void coresight_disable_path(struct list_head *path); -int coresight_enable_path(struct list_head *path); +int coresight_enable_path(struct list_head *path, u32 mode); struct coresight_device *coresight_get_sink(struct list_head *path); struct list_head *coresight_build_path(struct coresight_device *csdev); void coresight_release_path(struct list_head *path); diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index b526396d80b6..ac91b0b40ec2 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -265,7 +265,7 @@ static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode) return 0; } -static int tmc_enable_sink(struct coresight_device *csdev) +static int tmc_enable_sink(struct coresight_device *csdev, u32 mode) { struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index 0d8fce651ff7..71582f6dfd4c 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -70,7 +70,7 @@ static void tpiu_enable_hw(struct tpiu_drvdata *drvdata) CS_LOCK(drvdata->base); } -static int tpiu_enable(struct coresight_device *csdev) +static int tpiu_enable(struct coresight_device *csdev, u32 mode) { struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 95cccb179763..6ec2b66af9ee 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -121,13 +121,13 @@ static int coresight_find_link_outport(struct coresight_device *csdev, return 0; } -static int coresight_enable_sink(struct coresight_device *csdev) +static int coresight_enable_sink(struct coresight_device *csdev, u32 mode) { int ret; if (!csdev->enable) { if (sink_ops(csdev)->enable) { - ret = sink_ops(csdev)->enable(csdev); + ret = sink_ops(csdev)->enable(csdev, mode); if (ret) return ret; } @@ -283,7 +283,7 @@ void coresight_disable_path(struct list_head *path) } } -int coresight_enable_path(struct list_head *path) +int coresight_enable_path(struct list_head *path, u32 mode) { int ret = 0; @@ -296,7 +296,7 @@ int coresight_enable_path(struct list_head *path) switch (csdev->type) { case CORESIGHT_DEV_TYPE_SINK: case CORESIGHT_DEV_TYPE_LINKSINK: - ret = coresight_enable_sink(csdev); + ret = coresight_enable_sink(csdev, mode); if (ret) goto err; break; @@ -454,7 +454,7 @@ int coresight_enable(struct coresight_device *csdev) goto out; } - ret = coresight_enable_path(path); + ret = coresight_enable_path(path, CS_MODE_SYSFS); if (ret) goto err_path; diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 6801dd64ee5d..9fa92dcdd2ea 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -186,7 +186,7 @@ struct coresight_device { * @disable: disables the sink. */ struct coresight_ops_sink { - int (*enable)(struct coresight_device *csdev); + int (*enable)(struct coresight_device *csdev, u32 mode); void (*disable)(struct coresight_device *csdev); }; From b18cfc50970dfa87b6f90b544f8b2be520825aa6 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:52:00 -0700 Subject: [PATCH 046/104] coresight: etb10: implementing AUX API Adding an ETB10 specific AUX area operations to be used by the perf framework when events are initialised. Part of this operation involves modeling the mmap'ed area based on the specific ways a sink buffer gathers information. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 2997aa4063d97fdb39450c6078bd81a7b0504f22) --- drivers/hwtracing/coresight/coresight-etb10.c | 234 ++++++++++++++++++ include/linux/coresight.h | 21 +- 2 files changed, 253 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 79099f95ba3f..a2eb6bdeaafa 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -28,6 +28,11 @@ #include #include #include +#include +#include +#include + +#include #include "coresight-priv.h" @@ -64,6 +69,26 @@ #define ETB_FFSR_BIT 1 #define ETB_FRAME_SIZE_WORDS 4 +/** + * struct cs_buffer - keep track of a recording session' specifics + * @cur: index of the current buffer + * @nr_pages: max number of pages granted to us + * @offset: offset within the current buffer + * @data_size: how much we collected in this run + * @lost: other than zero if we had a HW buffer wrap around + * @snapshot: is this run in snapshot mode + * @data_pages: a handle the ring buffer + */ +struct cs_buffers { + unsigned int cur; + unsigned int nr_pages; + unsigned long offset; + local_t data_size; + local_t lost; + bool snapshot; + void **data_pages; +}; + /** * struct etb_drvdata - specifics associated to an ETB component * @base: memory mapped base address for this component. @@ -265,9 +290,218 @@ static void etb_disable(struct coresight_device *csdev) dev_info(drvdata->dev, "ETB disabled\n"); } +static void *etb_alloc_buffer(struct coresight_device *csdev, int cpu, + void **pages, int nr_pages, bool overwrite) +{ + int node; + struct cs_buffers *buf; + + if (cpu == -1) + cpu = smp_processor_id(); + node = cpu_to_node(cpu); + + buf = kzalloc_node(sizeof(struct cs_buffers), GFP_KERNEL, node); + if (!buf) + return NULL; + + buf->snapshot = overwrite; + buf->nr_pages = nr_pages; + buf->data_pages = pages; + + return buf; +} + +static void etb_free_buffer(void *config) +{ + struct cs_buffers *buf = config; + + kfree(buf); +} + +static int etb_set_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config) +{ + int ret = 0; + unsigned long head; + struct cs_buffers *buf = sink_config; + + /* wrap head around to the amount of space we have */ + head = handle->head & ((buf->nr_pages << PAGE_SHIFT) - 1); + + /* find the page to write to */ + buf->cur = head / PAGE_SIZE; + + /* and offset within that page */ + buf->offset = head % PAGE_SIZE; + + local_set(&buf->data_size, 0); + + return ret; +} + +static unsigned long etb_reset_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config, bool *lost) +{ + unsigned long size = 0; + struct cs_buffers *buf = sink_config; + + if (buf) { + /* + * In snapshot mode ->data_size holds the new address of the + * ring buffer's head. The size itself is the whole address + * range since we want the latest information. + */ + if (buf->snapshot) + handle->head = local_xchg(&buf->data_size, + buf->nr_pages << PAGE_SHIFT); + + /* + * Tell the tracer PMU how much we got in this run and if + * something went wrong along the way. Nobody else can use + * this cs_buffers instance until we are done. As such + * resetting parameters here and squaring off with the ring + * buffer API in the tracer PMU is fine. + */ + *lost = !!local_xchg(&buf->lost, 0); + size = local_xchg(&buf->data_size, 0); + } + + return size; +} + +static void etb_update_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config) +{ + int i, cur; + u8 *buf_ptr; + u32 read_ptr, write_ptr, capacity; + u32 status, read_data, to_read; + unsigned long offset; + struct cs_buffers *buf = sink_config; + struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (!buf) + return; + + capacity = drvdata->buffer_depth * ETB_FRAME_SIZE_WORDS; + + CS_UNLOCK(drvdata->base); + etb_disable_hw(drvdata); + + /* unit is in words, not bytes */ + read_ptr = readl_relaxed(drvdata->base + ETB_RAM_READ_POINTER); + write_ptr = readl_relaxed(drvdata->base + ETB_RAM_WRITE_POINTER); + + /* + * Entries should be aligned to the frame size. If they are not + * go back to the last alignement point to give decoding tools a + * chance to fix things. + */ + if (write_ptr % ETB_FRAME_SIZE_WORDS) { + dev_err(drvdata->dev, + "write_ptr: %lu not aligned to formatter frame size\n", + (unsigned long)write_ptr); + + write_ptr &= ~(ETB_FRAME_SIZE_WORDS - 1); + local_inc(&buf->lost); + } + + /* + * Get a hold of the status register and see if a wrap around + * has occurred. If so adjust things accordingly. Otherwise + * start at the beginning and go until the write pointer has + * been reached. + */ + status = readl_relaxed(drvdata->base + ETB_STATUS_REG); + if (status & ETB_STATUS_RAM_FULL) { + local_inc(&buf->lost); + to_read = capacity; + read_ptr = write_ptr; + } else { + to_read = CIRC_CNT(write_ptr, read_ptr, drvdata->buffer_depth); + to_read *= ETB_FRAME_SIZE_WORDS; + } + + /* + * Make sure we don't overwrite data that hasn't been consumed yet. + * It is entirely possible that the HW buffer has more data than the + * ring buffer can currently handle. If so adjust the start address + * to take only the last traces. + * + * In snapshot mode we are looking to get the latest traces only and as + * such, we don't care about not overwriting data that hasn't been + * processed by user space. + */ + if (!buf->snapshot && to_read > handle->size) { + u32 mask = ~(ETB_FRAME_SIZE_WORDS - 1); + + /* The new read pointer must be frame size aligned */ + to_read -= handle->size & mask; + /* + * Move the RAM read pointer up, keeping in mind that + * everything is in frame size units. + */ + read_ptr = (write_ptr + drvdata->buffer_depth) - + to_read / ETB_FRAME_SIZE_WORDS; + /* Wrap around if need be*/ + read_ptr &= ~(drvdata->buffer_depth - 1); + /* let the decoder know we've skipped ahead */ + local_inc(&buf->lost); + } + + /* finally tell HW where we want to start reading from */ + writel_relaxed(read_ptr, drvdata->base + ETB_RAM_READ_POINTER); + + cur = buf->cur; + offset = buf->offset; + for (i = 0; i < to_read; i += 4) { + buf_ptr = buf->data_pages[cur] + offset; + read_data = readl_relaxed(drvdata->base + + ETB_RAM_READ_DATA_REG); + *buf_ptr++ = read_data >> 0; + *buf_ptr++ = read_data >> 8; + *buf_ptr++ = read_data >> 16; + *buf_ptr++ = read_data >> 24; + + offset += 4; + if (offset >= PAGE_SIZE) { + offset = 0; + cur++; + /* wrap around at the end of the buffer */ + cur &= buf->nr_pages - 1; + } + } + + /* reset ETB buffer for next run */ + writel_relaxed(0x0, drvdata->base + ETB_RAM_READ_POINTER); + writel_relaxed(0x0, drvdata->base + ETB_RAM_WRITE_POINTER); + + /* + * In snapshot mode all we have to do is communicate to + * perf_aux_output_end() the address of the current head. In full + * trace mode the same function expects a size to move rb->aux_head + * forward. + */ + if (buf->snapshot) + local_set(&buf->data_size, (cur * PAGE_SIZE) + offset); + else + local_add(to_read, &buf->data_size); + + etb_enable_hw(drvdata); + CS_LOCK(drvdata->base); +} + static const struct coresight_ops_sink etb_sink_ops = { .enable = etb_enable, .disable = etb_disable, + .alloc_buffer = etb_alloc_buffer, + .free_buffer = etb_free_buffer, + .set_buffer = etb_set_buffer, + .reset_buffer = etb_reset_buffer, + .update_buffer = etb_update_buffer, }; static const struct coresight_ops etb_cs_ops = { diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 9fa92dcdd2ea..385d62e64abb 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -182,12 +182,29 @@ struct coresight_device { /** * struct coresight_ops_sink - basic operations for a sink * Operations available for sinks - * @enable: enables the sink. - * @disable: disables the sink. + * @enable: enables the sink. + * @disable: disables the sink. + * @alloc_buffer: initialises perf's ring buffer for trace collection. + * @free_buffer: release memory allocated in @get_config. + * @set_buffer: initialises buffer mechanic before a trace session. + * @reset_buffer: finalises buffer mechanic after a trace session. + * @update_buffer: update buffer pointers after a trace session. */ struct coresight_ops_sink { int (*enable)(struct coresight_device *csdev, u32 mode); void (*disable)(struct coresight_device *csdev); + void *(*alloc_buffer)(struct coresight_device *csdev, int cpu, + void **pages, int nr_pages, bool overwrite); + void (*free_buffer)(void *config); + int (*set_buffer)(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config); + unsigned long (*reset_buffer)(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config, bool *lost); + void (*update_buffer)(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config); }; /** From fbeb6125a14d179f9431524d8502af63ef7afe10 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:52:01 -0700 Subject: [PATCH 047/104] coresight: etm-perf: new PMU driver for ETM tracers Perf is a well known and used tool for performance monitoring and much more. A such it is an ideal candidate for integration with coresight based HW tracing. This patch introduces a PMU that represent a coresight tracer to the Perf core. Cc: Alexander Shishkin Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 0bcbf2e30ff2271b54f54c8697a185f7d86ec6e4) --- drivers/hwtracing/coresight/Makefile | 3 +- .../hwtracing/coresight/coresight-etm-perf.c | 393 ++++++++++++++++++ .../hwtracing/coresight/coresight-etm-perf.h | 32 ++ drivers/hwtracing/coresight/coresight-etm3x.c | 7 + include/linux/coresight-pmu.h | 27 ++ 5 files changed, 461 insertions(+), 1 deletion(-) create mode 100644 drivers/hwtracing/coresight/coresight-etm-perf.c create mode 100644 drivers/hwtracing/coresight/coresight-etm-perf.h create mode 100644 include/linux/coresight-pmu.h diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 233d66cf22d3..cf8c6d689747 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ coresight-replicator.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ - coresight-etm3x-sysfs.o + coresight-etm3x-sysfs.o \ + coresight-etm-perf.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c new file mode 100644 index 000000000000..36153a77e982 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -0,0 +1,393 @@ +/* + * Copyright(C) 2015 Linaro Limited. All rights reserved. + * Author: Mathieu Poirier + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "coresight-priv.h" + +static struct pmu etm_pmu; +static bool etm_perf_up; + +/** + * struct etm_event_data - Coresight specifics associated to an event + * @work: Handle to free allocated memory outside IRQ context. + * @mask: Hold the CPU(s) this event was set for. + * @snk_config: The sink configuration. + * @path: An array of path, each slot for one CPU. + */ +struct etm_event_data { + struct work_struct work; + cpumask_t mask; + void *snk_config; + struct list_head **path; +}; + +static DEFINE_PER_CPU(struct perf_output_handle, ctx_handle); +static DEFINE_PER_CPU(struct coresight_device *, csdev_src); + +/* ETMv3.5/PTM's ETMCR is 'config' */ +PMU_FORMAT_ATTR(cycacc, "config:" __stringify(ETM_OPT_CYCACC)); +PMU_FORMAT_ATTR(timestamp, "config:" __stringify(ETM_OPT_TS)); + +static struct attribute *etm_config_formats_attr[] = { + &format_attr_cycacc.attr, + &format_attr_timestamp.attr, + NULL, +}; + +static struct attribute_group etm_pmu_format_group = { + .name = "format", + .attrs = etm_config_formats_attr, +}; + +static const struct attribute_group *etm_pmu_attr_groups[] = { + &etm_pmu_format_group, + NULL, +}; + +static void etm_event_read(struct perf_event *event) {} + +static int etm_event_init(struct perf_event *event) +{ + if (event->attr.type != etm_pmu.type) + return -ENOENT; + + return 0; +} + +static void free_event_data(struct work_struct *work) +{ + int cpu; + cpumask_t *mask; + struct etm_event_data *event_data; + struct coresight_device *sink; + + event_data = container_of(work, struct etm_event_data, work); + mask = &event_data->mask; + /* + * First deal with the sink configuration. See comment in + * etm_setup_aux() about why we take the first available path. + */ + if (event_data->snk_config) { + cpu = cpumask_first(mask); + sink = coresight_get_sink(event_data->path[cpu]); + if (sink_ops(sink)->free_buffer) + sink_ops(sink)->free_buffer(event_data->snk_config); + } + + for_each_cpu(cpu, mask) { + if (event_data->path[cpu]) + coresight_release_path(event_data->path[cpu]); + } + + kfree(event_data->path); + kfree(event_data); +} + +static void *alloc_event_data(int cpu) +{ + int size; + cpumask_t *mask; + struct etm_event_data *event_data; + + /* First get memory for the session's data */ + event_data = kzalloc(sizeof(struct etm_event_data), GFP_KERNEL); + if (!event_data) + return NULL; + + /* Make sure nothing disappears under us */ + get_online_cpus(); + size = num_online_cpus(); + + mask = &event_data->mask; + if (cpu != -1) + cpumask_set_cpu(cpu, mask); + else + cpumask_copy(mask, cpu_online_mask); + put_online_cpus(); + + /* + * Each CPU has a single path between source and destination. As such + * allocate an array using CPU numbers as indexes. That way a path + * for any CPU can easily be accessed at any given time. We proceed + * the same way for sessions involving a single CPU. The cost of + * unused memory when dealing with single CPU trace scenarios is small + * compared to the cost of searching through an optimized array. + */ + event_data->path = kcalloc(size, + sizeof(struct list_head *), GFP_KERNEL); + if (!event_data->path) { + kfree(event_data); + return NULL; + } + + return event_data; +} + +static void etm_free_aux(void *data) +{ + struct etm_event_data *event_data = data; + + schedule_work(&event_data->work); +} + +static void *etm_setup_aux(int event_cpu, void **pages, + int nr_pages, bool overwrite) +{ + int cpu; + cpumask_t *mask; + struct coresight_device *sink; + struct etm_event_data *event_data = NULL; + + event_data = alloc_event_data(event_cpu); + if (!event_data) + return NULL; + + INIT_WORK(&event_data->work, free_event_data); + + mask = &event_data->mask; + + /* Setup the path for each CPU in a trace session */ + for_each_cpu(cpu, mask) { + struct coresight_device *csdev; + + csdev = per_cpu(csdev_src, cpu); + if (!csdev) + goto err; + + /* + * Building a path doesn't enable it, it simply builds a + * list of devices from source to sink that can be + * referenced later when the path is actually needed. + */ + event_data->path[cpu] = coresight_build_path(csdev); + if (!event_data->path[cpu]) + goto err; + } + + /* + * In theory nothing prevent tracers in a trace session from being + * associated with different sinks, nor having a sink per tracer. But + * until we have HW with this kind of topology and a way to convey + * sink assignement from the perf cmd line we need to assume tracers + * in a trace session are using the same sink. Therefore pick the sink + * found at the end of the first available path. + */ + cpu = cpumask_first(mask); + /* Grab the sink at the end of the path */ + sink = coresight_get_sink(event_data->path[cpu]); + if (!sink) + goto err; + + if (!sink_ops(sink)->alloc_buffer) + goto err; + + /* Get the AUX specific data from the sink buffer */ + event_data->snk_config = + sink_ops(sink)->alloc_buffer(sink, cpu, pages, + nr_pages, overwrite); + if (!event_data->snk_config) + goto err; + +out: + return event_data; + +err: + etm_free_aux(event_data); + event_data = NULL; + goto out; +} + +static void etm_event_start(struct perf_event *event, int flags) +{ + int cpu = smp_processor_id(); + struct etm_event_data *event_data; + struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle); + struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu); + + if (!csdev) + goto fail; + + /* + * Deal with the ring buffer API and get a handle on the + * session's information. + */ + event_data = perf_aux_output_begin(handle, event); + if (!event_data) + goto fail; + + /* We need a sink, no need to continue without one */ + sink = coresight_get_sink(event_data->path[cpu]); + if (WARN_ON_ONCE(!sink || !sink_ops(sink)->set_buffer)) + goto fail_end_stop; + + /* Configure the sink */ + if (sink_ops(sink)->set_buffer(sink, handle, + event_data->snk_config)) + goto fail_end_stop; + + /* Nothing will happen without a path */ + if (coresight_enable_path(event_data->path[cpu], CS_MODE_PERF)) + goto fail_end_stop; + + /* Tell the perf core the event is alive */ + event->hw.state = 0; + + /* Finally enable the tracer */ + if (source_ops(csdev)->enable(csdev, &event->attr, CS_MODE_PERF)) + goto fail_end_stop; + +out: + return; + +fail_end_stop: + perf_aux_output_end(handle, 0, true); +fail: + event->hw.state = PERF_HES_STOPPED; + goto out; +} + +static void etm_event_stop(struct perf_event *event, int mode) +{ + bool lost; + int cpu = smp_processor_id(); + unsigned long size; + struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu); + struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle); + struct etm_event_data *event_data = perf_get_aux(handle); + + if (event->hw.state == PERF_HES_STOPPED) + return; + + if (!csdev) + return; + + sink = coresight_get_sink(event_data->path[cpu]); + if (!sink) + return; + + /* stop tracer */ + source_ops(csdev)->disable(csdev); + + /* tell the core */ + event->hw.state = PERF_HES_STOPPED; + + if (mode & PERF_EF_UPDATE) { + if (WARN_ON_ONCE(handle->event != event)) + return; + + /* update trace information */ + if (!sink_ops(sink)->update_buffer) + return; + + sink_ops(sink)->update_buffer(sink, handle, + event_data->snk_config); + + if (!sink_ops(sink)->reset_buffer) + return; + + size = sink_ops(sink)->reset_buffer(sink, handle, + event_data->snk_config, + &lost); + + perf_aux_output_end(handle, size, lost); + } + + /* Disabling the path make its elements available to other sessions */ + coresight_disable_path(event_data->path[cpu]); +} + +static int etm_event_add(struct perf_event *event, int mode) +{ + int ret = 0; + struct hw_perf_event *hwc = &event->hw; + + if (mode & PERF_EF_START) { + etm_event_start(event, 0); + if (hwc->state & PERF_HES_STOPPED) + ret = -EINVAL; + } else { + hwc->state = PERF_HES_STOPPED; + } + + return ret; +} + +static void etm_event_del(struct perf_event *event, int mode) +{ + etm_event_stop(event, PERF_EF_UPDATE); +} + +int etm_perf_symlink(struct coresight_device *csdev, bool link) +{ + char entry[sizeof("cpu9999999")]; + int ret = 0, cpu = source_ops(csdev)->cpu_id(csdev); + struct device *pmu_dev = etm_pmu.dev; + struct device *cs_dev = &csdev->dev; + + sprintf(entry, "cpu%d", cpu); + + if (!etm_perf_up) + return -EPROBE_DEFER; + + if (link) { + ret = sysfs_create_link(&pmu_dev->kobj, &cs_dev->kobj, entry); + if (ret) + return ret; + per_cpu(csdev_src, cpu) = csdev; + } else { + sysfs_remove_link(&pmu_dev->kobj, entry); + per_cpu(csdev_src, cpu) = NULL; + } + + return 0; +} + +static int __init etm_perf_init(void) +{ + int ret; + + etm_pmu.capabilities = PERF_PMU_CAP_EXCLUSIVE; + + etm_pmu.attr_groups = etm_pmu_attr_groups; + etm_pmu.task_ctx_nr = perf_sw_context; + etm_pmu.read = etm_event_read; + etm_pmu.event_init = etm_event_init; + etm_pmu.setup_aux = etm_setup_aux; + etm_pmu.free_aux = etm_free_aux; + etm_pmu.start = etm_event_start; + etm_pmu.stop = etm_event_stop; + etm_pmu.add = etm_event_add; + etm_pmu.del = etm_event_del; + + ret = perf_pmu_register(&etm_pmu, CORESIGHT_ETM_PMU_NAME, -1); + if (ret == 0) + etm_perf_up = true; + + return ret; +} +module_init(etm_perf_init); diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.h b/drivers/hwtracing/coresight/coresight-etm-perf.h new file mode 100644 index 000000000000..87f5a134eb6f --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm-perf.h @@ -0,0 +1,32 @@ +/* + * Copyright(C) 2015 Linaro Limited. All rights reserved. + * Author: Mathieu Poirier + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef _CORESIGHT_ETM_PERF_H +#define _CORESIGHT_ETM_PERF_H + +struct coresight_device; + +#ifdef CONFIG_CORESIGHT +int etm_perf_symlink(struct coresight_device *csdev, bool link); + +#else +static inline int etm_perf_symlink(struct coresight_device *csdev, bool link) +{ return -EINVAL; } + +#endif /* CONFIG_CORESIGHT */ + +#endif diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index a9b820ec16aa..77b37413803f 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -35,6 +35,7 @@ #include #include "coresight-etm.h" +#include "coresight-etm-perf.h" static int boot_enable; module_param_named(boot_enable, boot_enable, int, S_IRUGO); @@ -827,6 +828,12 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) goto err_arch_supported; } + ret = etm_perf_symlink(drvdata->csdev, true); + if (ret) { + coresight_unregister(drvdata->csdev); + goto err_arch_supported; + } + pm_runtime_put(&adev->dev); dev_info(dev, "%s initialized\n", (char *)id->data); diff --git a/include/linux/coresight-pmu.h b/include/linux/coresight-pmu.h new file mode 100644 index 000000000000..6c5386b23b10 --- /dev/null +++ b/include/linux/coresight-pmu.h @@ -0,0 +1,27 @@ +/* + * Copyright(C) 2015 Linaro Limited. All rights reserved. + * Author: Mathieu Poirier + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef _LINUX_CORESIGHT_PMU_H +#define _LINUX_CORESIGHT_PMU_H + +#define CORESIGHT_ETM_PMU_NAME "cs_etm" + +/* ETMv3.5/PTM's ETMCR config bit */ +#define ETM_OPT_CYCACC 12 +#define ETM_OPT_TS 28 + +#endif From c8ec21bd630e8a56fa811c6542c466b1bdc565ac Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:52:02 -0700 Subject: [PATCH 048/104] coresight: introducing a global trace ID function TraceID values have to be unique for all tracers and consistent between drivers and user space. As such introducing a central function to be used whenever a traceID value is required. The patch also account for data traceIDs, which are usually I(N) + 1. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 17534ceb835a1a96eb921a2a80df168723d6570a) --- drivers/hwtracing/coresight/coresight-etm3x.c | 7 ++----- include/linux/coresight-pmu.h | 12 ++++++++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 77b37413803f..0ba1a3981373 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -740,11 +741,7 @@ static void etm_init_arch_data(void *info) static void etm_init_trace_id(struct etm_drvdata *drvdata) { - /* - * A trace ID of value 0 is invalid, so let's start at some - * random value that fits in 7 bits and go from there. - */ - drvdata->traceid = 0x10 + drvdata->cpu; + drvdata->traceid = coresight_get_trace_id(drvdata->cpu); } static int etm_probe(struct amba_device *adev, const struct amba_id *id) diff --git a/include/linux/coresight-pmu.h b/include/linux/coresight-pmu.h index 6c5386b23b10..7d410260661b 100644 --- a/include/linux/coresight-pmu.h +++ b/include/linux/coresight-pmu.h @@ -19,9 +19,21 @@ #define _LINUX_CORESIGHT_PMU_H #define CORESIGHT_ETM_PMU_NAME "cs_etm" +#define CORESIGHT_ETM_PMU_SEED 0x10 /* ETMv3.5/PTM's ETMCR config bit */ #define ETM_OPT_CYCACC 12 #define ETM_OPT_TS 28 +static inline int coresight_get_trace_id(int cpu) +{ + /* + * A trace ID of value 0 is invalid, so let's start at some + * random value that fits in 7 bits and go from there. Since + * the common convention is to have data trace IDs be I(N) + 1, + * set instruction trace IDs as a function of the CPU number. + */ + return (CORESIGHT_ETM_PMU_SEED + (cpu * 2)); +} + #endif From a70af6f83fda06ff263fb38e79081cc86e3e8fff Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 17 Feb 2016 17:52:03 -0700 Subject: [PATCH 049/104] drivers/hwtracing: make coresight-* explicitly non-modular None of the Kconfig currently controlling compilation of any of the files here are tristate, meaning that none of it currently is being built as a module by anyone. We need not be concerned about .remove functions and blocking the unbind sysfs operations, since that was already done in a recent commit. Lets remove any remaining modular references, so that when reading the drivers there is no doubt they are builtin-only. All drivers get mostly the same changes, so they are handled in batch. Changes are (1) convert to builtin_amba_driver, (2) delete module.h include where unused, and (3) relocate the description into the comments so we don't need MODULE_DESCRIPTION and associated tags. The etm3x and etm4x use module_param_named, and have been adjusted to just include moduleparam.h for that purpose. In commit f309d4443130bf814e991f836e919dca22df37ae ("platform_device: better support builtin boilerplate avoidance") we introduced the builtin_driver macro. Here we use that support and extend it to amba driver registration, so where a driver is clearly non-modular and builtin-only, we can update with the simple mapping of module_amba_driver(...) ---> builtin_amba_driver(...) Since module_amba_driver() uses the same init level priority as builtin_amba_driver() the init ordering remains unchanged with this commit. Cc: Mathieu Poirier Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Paul Gortmaker Signed-off-by: Mathieu Poirier Acked-by: Russell King Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 941943cf519f7cacbbcecee5c4ef4b77b466bd5c) --- drivers/hwtracing/coresight/coresight-etb10.c | 9 +++------ drivers/hwtracing/coresight/coresight-etm3x.c | 14 ++++++++------ drivers/hwtracing/coresight/coresight-etm4x.c | 4 +--- drivers/hwtracing/coresight/coresight-funnel.c | 9 +++------ .../coresight/coresight-replicator-qcom.c | 4 +--- drivers/hwtracing/coresight/coresight-replicator.c | 7 ++----- drivers/hwtracing/coresight/coresight-tmc.c | 9 +++------ drivers/hwtracing/coresight/coresight-tpiu.c | 9 +++------ drivers/hwtracing/coresight/coresight.c | 3 --- drivers/hwtracing/coresight/of_coresight.c | 1 - include/linux/amba/bus.h | 9 +++++++++ 11 files changed, 33 insertions(+), 45 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index a2eb6bdeaafa..acbce79934d6 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -1,4 +1,6 @@ /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * Description: CoreSight Embedded Trace Buffer driver * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -12,7 +14,6 @@ #include #include -#include #include #include #include @@ -781,8 +782,4 @@ static struct amba_driver etb_driver = { .probe = etb_probe, .id_table = etb_ids, }; - -module_amba_driver(etb_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("CoreSight Embedded Trace Buffer driver"); +builtin_amba_driver(etb_driver); diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 0ba1a3981373..d83ab82672e4 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -1,4 +1,6 @@ /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * Description: CoreSight Program Flow Trace driver * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -11,7 +13,7 @@ */ #include -#include +#include #include #include #include @@ -38,6 +40,10 @@ #include "coresight-etm.h" #include "coresight-etm-perf.h" +/* + * Not really modular but using module_param is the easiest way to + * remain consistent with existing use cases for now. + */ static int boot_enable; module_param_named(boot_enable, boot_enable, int, S_IRUGO); @@ -912,8 +918,4 @@ static struct amba_driver etm_driver = { .probe = etm_probe, .id_table = etm_ids, }; - -module_amba_driver(etm_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("CoreSight Program Flow Trace driver"); +builtin_amba_driver(etm_driver); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index d0169ba7fbf2..1c59bd36834c 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -2710,5 +2709,4 @@ static struct amba_driver etm4x_driver = { .probe = etm4_probe, .id_table = etm4_ids, }; - -module_amba_driver(etm4x_driver); +builtin_amba_driver(etm4x_driver); diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index fb2c679fbc44..0600ca30649d 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -1,4 +1,6 @@ /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * Description: CoreSight Funnel driver * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -11,7 +13,6 @@ */ #include -#include #include #include #include @@ -268,8 +269,4 @@ static struct amba_driver funnel_driver = { .probe = funnel_probe, .id_table = funnel_ids, }; - -module_amba_driver(funnel_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("CoreSight Funnel driver"); +builtin_amba_driver(funnel_driver); diff --git a/drivers/hwtracing/coresight/coresight-replicator-qcom.c b/drivers/hwtracing/coresight/coresight-replicator-qcom.c index 286f90b50989..700f710e4bfa 100644 --- a/drivers/hwtracing/coresight/coresight-replicator-qcom.c +++ b/drivers/hwtracing/coresight/coresight-replicator-qcom.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -198,5 +197,4 @@ static struct amba_driver replicator_driver = { .probe = replicator_probe, .id_table = replicator_ids, }; - -module_amba_driver(replicator_driver); +builtin_amba_driver(replicator_driver); diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index 0ce98903992c..4299c0569340 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -1,4 +1,6 @@ /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * Description: CoreSight Replicator driver * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -11,7 +13,6 @@ */ #include -#include #include #include #include @@ -166,8 +167,4 @@ static struct platform_driver replicator_driver = { .suppress_bind_attrs = true, }, }; - builtin_platform_driver(replicator_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("CoreSight Replicator driver"); diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index ac91b0b40ec2..1be191f5d39c 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -1,4 +1,6 @@ /* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * Description: CoreSight Trace Memory Controller driver * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -11,7 +13,6 @@ */ #include -#include #include #include #include @@ -782,8 +783,4 @@ static struct amba_driver tmc_driver = { .probe = tmc_probe, .id_table = tmc_ids, }; - -module_amba_driver(tmc_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("CoreSight Trace Memory Controller driver"); +builtin_amba_driver(tmc_driver); diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index 71582f6dfd4c..8fb09d9237ab 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -1,4 +1,6 @@ /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * Description: CoreSight Trace Port Interface Unit driver * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -11,7 +13,6 @@ */ #include -#include #include #include #include @@ -218,8 +219,4 @@ static struct amba_driver tpiu_driver = { .probe = tpiu_probe, .id_table = tpiu_ids, }; - -module_amba_driver(tpiu_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("CoreSight Trace Port Interface Unit driver"); +builtin_amba_driver(tpiu_driver); diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 6ec2b66af9ee..2ea5961092c1 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -11,7 +11,6 @@ */ #include -#include #include #include #include @@ -894,5 +893,3 @@ void coresight_unregister(struct coresight_device *csdev) device_unregister(&csdev->dev); } EXPORT_SYMBOL_GPL(coresight_unregister); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c index 3cc57c1e3b5d..b68da1888fd5 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c @@ -10,7 +10,6 @@ * GNU General Public License for more details. */ -#include #include #include #include diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h index 9006c4e75cf7..3d8dcdd1aeae 100644 --- a/include/linux/amba/bus.h +++ b/include/linux/amba/bus.h @@ -163,4 +163,13 @@ struct amba_device name##_device = { \ #define module_amba_driver(__amba_drv) \ module_driver(__amba_drv, amba_driver_register, amba_driver_unregister) +/* + * builtin_amba_driver() - Helper macro for drivers that don't do anything + * special in driver initcall. This eliminates a lot of boilerplate. Each + * driver may only use this macro once, and calling it replaces the instance + * device_initcall(). + */ +#define builtin_amba_driver(__amba_drv) \ + builtin_driver(__amba_drv, amba_driver_register) + #endif From 409ce7ce900fdeef6181cd64b4566829c5ae23a4 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sat, 27 Feb 2016 15:21:47 -0500 Subject: [PATCH 050/104] drivers/hwtracing: make coresight-etm-perf.c explicitly non-modular In commit 941943cf519f7cacbbcecee5c4ef4b77b466bd5c ("drivers/hwtracing: make coresight-* explicitly non-modular") we removed all uses of modular functions/macros in favour of their built-in equivlents in this subsystem. However that commit and commit 0bcbf2e30ff2271b54f54c8697a185f7d86ec6e4 ("coresight: etm-perf: new PMU driver for ETM tracers") were in flight at the same time, and hence one new non-modular user of module_init crept back in. Fix it up like we did all the others. Since module_init translates to device_initcall in the non-modular case, the init ordering remains unchanged with this commit. Cc: Alexander Shishkin Signed-off-by: Paul Gortmaker Acked-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit ca48fa22c3ed3b7b062bc6fa7b72493c00571e33) --- drivers/hwtracing/coresight/coresight-etm-perf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 36153a77e982..755125f7917f 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include @@ -390,4 +390,4 @@ static int __init etm_perf_init(void) return ret; } -module_init(etm_perf_init); +device_initcall(etm_perf_init); From b102ff2ac3a5d80863065a9c7749f9646c5ab414 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 26 Jan 2016 15:51:46 -0300 Subject: [PATCH 051/104] perf cpumap: Auto initialize cpu__max_{node,cpu} Since it was always checking if the initialization was done, use that branch to do the initialization if not done already. With this we reduce the number of exported globals from these files. Cc: Adrian Hunter Cc: Borislav Petkov Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Kan Liang Cc: Mathieu Poirier Cc: Namhyung Kim Cc: Stephane Eranian Cc: Wang Nan Link: http://lkml.kernel.org/r/20160125212955.GG22501@kernel.org Signed-off-by: Arnaldo Carvalho de Melo (cherry picked from commit 5ac76283b32b116c58e362e99542182ddcfc8262) --- tools/perf/util/cpumap.c | 30 ++++++++++++++++++++++++++++++ tools/perf/util/cpumap.h | 32 +++----------------------------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 10af1e7524fb..6523e1a8eea5 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -7,6 +7,10 @@ #include #include "asm/bug.h" +static int max_cpu_num; +static int max_node_num; +static int *cpunode_map; + static struct cpu_map *cpu_map__default_new(void) { struct cpu_map *cpus; @@ -435,6 +439,32 @@ static void set_max_node_num(void) pr_err("Failed to read max nodes, using default of %d\n", max_node_num); } +int cpu__max_node(void) +{ + if (unlikely(!max_node_num)) + set_max_node_num(); + + return max_node_num; +} + +int cpu__max_cpu(void) +{ + if (unlikely(!max_cpu_num)) + set_max_cpu_num(); + + return max_cpu_num; +} + +int cpu__get_node(int cpu) +{ + if (unlikely(cpunode_map == NULL)) { + pr_debug("cpu_map not initialized\n"); + return -1; + } + + return cpunode_map[cpu]; +} + static int init_cpunode_map(void) { int i; diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index 85f7772457fa..d6184ba929b6 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h @@ -56,37 +56,11 @@ static inline bool cpu_map__empty(const struct cpu_map *map) return map ? map->map[0] == -1 : true; } -int max_cpu_num; -int max_node_num; -int *cpunode_map; - int cpu__setup_cpunode_map(void); -static inline int cpu__max_node(void) -{ - if (unlikely(!max_node_num)) - pr_debug("cpu_map not initialized\n"); - - return max_node_num; -} - -static inline int cpu__max_cpu(void) -{ - if (unlikely(!max_cpu_num)) - pr_debug("cpu_map not initialized\n"); - - return max_cpu_num; -} - -static inline int cpu__get_node(int cpu) -{ - if (unlikely(cpunode_map == NULL)) { - pr_debug("cpu_map not initialized\n"); - return -1; - } - - return cpunode_map[cpu]; -} +int cpu__max_node(void); +int cpu__max_cpu(void); +int cpu__get_node(int cpu); int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res, int (*f)(struct cpu_map *map, int cpu, void *data), From e8224c4aa1b5cb27809762f59c78034ddda5e23b Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 3 Dec 2015 10:06:41 +0100 Subject: [PATCH 052/104] perf evsel: Introduce disable() method Adding perf_evsel__disable function to have complement for perf_evsel__enable function. Both will be used in following patch to factor perf_evlist__(enable|disable). Signed-off-by: Jiri Olsa Cc: Adrian Hunter Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1449133606-14429-3-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo (cherry picked from commit e98a4cbb01e0ba1110eba5166a425b3eab9b2244) --- tools/perf/util/evsel.c | 10 ++++++++++ tools/perf/util/evsel.h | 1 + 2 files changed, 11 insertions(+) diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 397fb4ed3c97..1eb4d02e3968 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -988,6 +988,16 @@ int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads) 0); } +int perf_evsel__disable(struct perf_evsel *evsel) +{ + int nthreads = thread_map__nr(evsel->threads); + int ncpus = cpu_map__nr(evsel->cpus); + + return perf_evsel__run_ioctl(evsel, ncpus, nthreads, + PERF_EVENT_IOC_DISABLE, + 0); +} + int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) { if (ncpus == 0 || nthreads == 0) diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 0e49bd742c63..eaa4c733c976 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -228,6 +228,7 @@ int perf_evsel__append_filter(struct perf_evsel *evsel, int perf_evsel__apply_filter(struct perf_evsel *evsel, int ncpus, int nthreads, const char *filter); int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads); +int perf_evsel__disable(struct perf_evsel *evsel); int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus); From 14091e83ba1e7dbfaa347b406af4d5fb9f0f81b5 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 7 Jan 2016 10:13:59 +0100 Subject: [PATCH 053/104] perf evlist: Make perf_evlist__open() open evsels with their cpus and threads (like perf record does) 'perf record' uses perf_evsel__open() to open events and passes the evsel->cpus and evsel->threads. Many tests and some tools instead use perf_evlist__open() which passes instead evlist->cpus and evlist->threads. Make perf_evlist__open() follow the 'perf record' behaviour so that a consistent approach is taken. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Namhyung Kim Cc: Noel Grandin Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1452158050-28061-3-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo (cherry picked from commit 23df7f798435796aff07d641456326b81cb34a77) --- tools/perf/util/evlist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index d1392194a9a9..5da08efda32f 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -1486,7 +1486,7 @@ int perf_evlist__open(struct perf_evlist *evlist) perf_evlist__update_id_pos(evlist); evlist__for_each(evlist, evsel) { - err = perf_evsel__open(evsel, evlist->cpus, evlist->threads); + err = perf_evsel__open(evsel, evsel->cpus, evsel->threads); if (err < 0) goto out_err; } From 33dbaa134e0b715b07ba25a8f46027cb2a3a7d96 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 26 Jan 2016 14:05:20 +0200 Subject: [PATCH 054/104] perf tools: tracepoint_error() can receive e=NULL, robustify it Fixes segmentation fault using, for instance: (gdb) run record -I -e intel_pt/tsc=1,noretcomp=1/u /bin/ls Starting program: /home/acme/bin/perf record -I -e intel_pt/tsc=1,noretcomp=1/u /bin/ls Missing separate debuginfos, use: dnf debuginfo-install glibc-2.22-7.fc23.x86_64 [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". Program received signal SIGSEGV, Segmentation fault. 0 x00000000004b9ea5 in tracepoint_error (e=0x0, err=13, sys=0x19b1370 "sched", name=0x19a5d00 "sched_switch") at util/parse-events.c:410 (gdb) bt #0 0x00000000004b9ea5 in tracepoint_error (e=0x0, err=13, sys=0x19b1370 "sched", name=0x19a5d00 "sched_switch") at util/parse-events.c:410 #1 0x00000000004b9fc5 in add_tracepoint (list=0x19a5d20, idx=0x7fffffffb8c0, sys_name=0x19b1370 "sched", evt_name=0x19a5d00 "sched_switch", err=0x0, head_config=0x0) at util/parse-events.c:433 #2 0x00000000004ba334 in add_tracepoint_event (list=0x19a5d20, idx=0x7fffffffb8c0, sys_name=0x19b1370 "sched", evt_name=0x19a5d00 "sched_switch", err=0x0, head_config=0x0) at util/parse-events.c:498 #3 0x00000000004bb699 in parse_events_add_tracepoint (list=0x19a5d20, idx=0x7fffffffb8c0, sys=0x19b1370 "sched", event=0x19a5d00 "sched_switch", err=0x0, head_config=0x0) at util/parse-events.c:936 #4 0x00000000004f6eda in parse_events_parse (_data=0x7fffffffb8b0, scanner=0x19a49d0) at util/parse-events.y:391 #5 0x00000000004bc8e5 in parse_events__scanner (str=0x663ff2 "sched:sched_switch", data=0x7fffffffb8b0, start_token=258) at util/parse-events.c:1361 #6 0x00000000004bca57 in parse_events (evlist=0x19a5220, str=0x663ff2 "sched:sched_switch", err=0x0) at util/parse-events.c:1401 #7 0x0000000000518d5f in perf_evlist__can_select_event (evlist=0x19a3b90, str=0x663ff2 "sched:sched_switch") at util/record.c:253 #8 0x0000000000553c42 in intel_pt_track_switches (evlist=0x19a3b90) at arch/x86/util/intel-pt.c:364 #9 0x00000000005549d1 in intel_pt_recording_options (itr=0x19a2c40, evlist=0x19a3b90, opts=0x8edf68 ) at arch/x86/util/intel-pt.c:664 #10 0x000000000051e076 in auxtrace_record__options (itr=0x19a2c40, evlist=0x19a3b90, opts=0x8edf68 ) at util/auxtrace.c:539 #11 0x0000000000433368 in cmd_record (argc=1, argv=0x7fffffffde60, prefix=0x0) at builtin-record.c:1264 #12 0x000000000049bec2 in run_builtin (p=0x8fa2a8 , argc=5, argv=0x7fffffffde60) at perf.c:390 #13 0x000000000049c12a in handle_internal_command (argc=5, argv=0x7fffffffde60) at perf.c:451 #14 0x000000000049c278 in run_argv (argcp=0x7fffffffdcbc, argv=0x7fffffffdcb0) at perf.c:495 #15 0x000000000049c60a in main (argc=5, argv=0x7fffffffde60) at perf.c:618 (gdb) Intel PT attempts to find the sched:sched_switch tracepoint but that seg faults if tracefs is not readable, because the error reporting structure is null, as errors are not reported when automatically adding tracepoints. Fix by checking before using. Committer note: This doesn't take place in a kernel that supports perf_event_attr.context_switch, that is the default way that will be used for tracking context switches, only in older kernels, like 4.2, in a machine with Intel PT (e.g. Broadwell) for non-priviledged users. Further info from a similar patch by Wang: The error is in tracepoint_error: it assumes the 'e' parameter is valid. However, there are many situation a parse_event() can be called without parse_events_error. See result of $ grep 'parse_events(.*NULL)' ./tools/perf/ -r' Signed-off-by: Adrian Hunter Tested-by: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Josh Poimboeuf Cc: Tong Zhang Cc: Wang Nan Cc: stable@vger.kernel.org # v4.4+ Fixes: 196581717d85 ("perf tools: Enhance parsing events tracepoint error output") Link: http://lkml.kernel.org/r/1453809921-24596-2-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo (cherry picked from commit ec183d22cc284a7a1e17f0341219d8ec8ca070cc) --- tools/perf/util/parse-events.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 6fc8cd753e1a..b48e87693aa5 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -399,6 +399,9 @@ static void tracepoint_error(struct parse_events_error *e, int err, { char help[BUFSIZ]; + if (!e) + return; + /* * We get error directly from syscall errno ( > 0), * or from encoded pointer's error ( < 0). From b020f5b0ad1d29376a56ba181a6c4839a5a448a1 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 7 Mar 2016 16:44:38 -0300 Subject: [PATCH 055/104] perf inject: Hit all DSOs for AUX data in JIT and other cases Currently, when injecting build ids, if there is AUX data then 'perf inject' hits all DSOs because it is not known which DSOs the trace data would hit. That needs to be done for JIT injection also, and in fact there is no reason to distinguish what kind of injection is being done. That is, any time there is AUX data and the HEADER_BUID_ID feature flag is set, and the AUX data is not being processed, then hit all DSOs. This patch does that. Signed-off-by: Adrian Hunter Signed-off-by: Arnaldo Carvalho de Melo Cc: Alexander Shishkin Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/1457005856-6143-2-git-send-email-adrian.hunter@intel.com Signed-off-by: Ingo Molnar (cherry picked from commit 640dad47988ec4b734d71934be103bb6e931279f) --- tools/perf/builtin-inject.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 99d127fe9c35..ac369c494036 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -626,12 +626,16 @@ static int __cmd_inject(struct perf_inject *inject) ret = perf_session__process_events(session); if (!file_out->is_pipe) { - if (inject->build_ids) { + if (inject->build_ids) perf_header__set_feat(&session->header, HEADER_BUILD_ID); - if (inject->have_auxtrace) - dsos__hit_all(session); - } + /* + * Keep all buildids when there is unprocessed AUX data because + * it is not known which ones the AUX trace hits. + */ + if (perf_header__has_feat(&session->header, HEADER_BUILD_ID) && + inject->have_auxtrace && !inject->itrace_synth_opts.set) + dsos__hit_all(session); /* * The AUX areas have been removed and replaced with * synthesized hardware events, so clear the feature flag and From 279b3b64ec953fc6eb20900134f163e831d1eb13 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 7 Mar 2016 16:44:39 -0300 Subject: [PATCH 056/104] perf session: Simplify tool stubs Some of the stubs are identical so just have one function for them. Signed-off-by: Adrian Hunter Signed-off-by: Arnaldo Carvalho de Melo Cc: Alexander Shishkin Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/1457005856-6143-3-git-send-email-adrian.hunter@intel.com Signed-off-by: Ingo Molnar (cherry picked from commit 5fb0ac16c5091f48eecf1a77e461f6957a463d61) --- tools/perf/util/session.c | 40 +++++++-------------------------------- 1 file changed, 7 insertions(+), 33 deletions(-) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index c35ffdd360fe..d8522b84987e 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -224,14 +224,6 @@ static int process_event_stub(struct perf_tool *tool __maybe_unused, return 0; } -static int process_build_id_stub(struct perf_tool *tool __maybe_unused, - union perf_event *event __maybe_unused, - struct perf_session *session __maybe_unused) -{ - dump_printf(": unhandled!\n"); - return 0; -} - static int process_finished_round_stub(struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct ordered_events *oe __maybe_unused) @@ -244,23 +236,6 @@ static int process_finished_round(struct perf_tool *tool, union perf_event *event, struct ordered_events *oe); -static int process_id_index_stub(struct perf_tool *tool __maybe_unused, - union perf_event *event __maybe_unused, - struct perf_session *perf_session - __maybe_unused) -{ - dump_printf(": unhandled!\n"); - return 0; -} - -static int process_event_auxtrace_info_stub(struct perf_tool *tool __maybe_unused, - union perf_event *event __maybe_unused, - struct perf_session *session __maybe_unused) -{ - dump_printf(": unhandled!\n"); - return 0; -} - static int skipn(int fd, off_t n) { char buf[4096]; @@ -287,10 +262,9 @@ static s64 process_event_auxtrace_stub(struct perf_tool *tool __maybe_unused, return event->auxtrace.size; } -static -int process_event_auxtrace_error_stub(struct perf_tool *tool __maybe_unused, - union perf_event *event __maybe_unused, - struct perf_session *session __maybe_unused) +static int process_event_op2_stub(struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct perf_session *session __maybe_unused) { dump_printf(": unhandled!\n"); return 0; @@ -331,7 +305,7 @@ void perf_tool__fill_defaults(struct perf_tool *tool) if (tool->tracing_data == NULL) tool->tracing_data = process_event_synth_tracing_data_stub; if (tool->build_id == NULL) - tool->build_id = process_build_id_stub; + tool->build_id = process_event_op2_stub; if (tool->finished_round == NULL) { if (tool->ordered_events) tool->finished_round = process_finished_round; @@ -339,13 +313,13 @@ void perf_tool__fill_defaults(struct perf_tool *tool) tool->finished_round = process_finished_round_stub; } if (tool->id_index == NULL) - tool->id_index = process_id_index_stub; + tool->id_index = process_event_op2_stub; if (tool->auxtrace_info == NULL) - tool->auxtrace_info = process_event_auxtrace_info_stub; + tool->auxtrace_info = process_event_op2_stub; if (tool->auxtrace == NULL) tool->auxtrace = process_event_auxtrace_stub; if (tool->auxtrace_error == NULL) - tool->auxtrace_error = process_event_auxtrace_error_stub; + tool->auxtrace_error = process_event_op2_stub; } static void swap_sample_id_all(union perf_event *event, void *data) From 49d2bcdaf87549681057bcab36fce0ac47584a5d Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Thu, 14 Jan 2016 14:46:15 -0700 Subject: [PATCH 057/104] perf auxtrace: Add perf_evlist pointer to *info_priv_size() On some architecture the size of the private header may be dependent on the number of tracers used in the session. As such adding a "struct perf_evlist *" parameter, which should contain all the required information. Also adjusting the existing client of the interface to take the new parameter into account. Signed-off-by: Mathieu Poirier Acked-by: Adrian Hunter Cc: Al Grant Cc: Chunyan Zhang Cc: linux-arm-kernel@lists.infradead.org Cc: linux-doc@vger.kernel.org Cc: Mike Leach Cc: Peter Zijlstra Cc: Rabin Vincent Cc: Tor Jeremiassen Link: http://lkml.kernel.org/r/1452807977-8069-22-git-send-email-mathieu.poirier@linaro.org Signed-off-by: Arnaldo Carvalho de Melo (cherry picked from commit 14a05e13a044c1cd6aaa3eb1a5fcdad7b4f6c990) --- tools/perf/arch/x86/util/intel-bts.c | 4 +++- tools/perf/arch/x86/util/intel-pt.c | 4 +++- tools/perf/util/auxtrace.c | 7 ++++--- tools/perf/util/auxtrace.h | 6 ++++-- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c index 9b94ce520917..4685a40777cc 100644 --- a/tools/perf/arch/x86/util/intel-bts.c +++ b/tools/perf/arch/x86/util/intel-bts.c @@ -60,7 +60,9 @@ struct branch { u64 misc; }; -static size_t intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused) +static size_t +intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused, + struct perf_evlist *evlist __maybe_unused) { return INTEL_BTS_AUXTRACE_PRIV_SIZE; } diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c index b02af064f0f9..e5c1f2e21f87 100644 --- a/tools/perf/arch/x86/util/intel-pt.c +++ b/tools/perf/arch/x86/util/intel-pt.c @@ -273,7 +273,9 @@ intel_pt_pmu_default_config(struct perf_pmu *intel_pt_pmu) return attr; } -static size_t intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused) +static size_t +intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused, + struct perf_evlist *evlist __maybe_unused) { return INTEL_PT_AUXTRACE_PRIV_SIZE; } diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index 7f10430af39c..cc1c9ce5cc56 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c @@ -478,10 +478,11 @@ void auxtrace_heap__pop(struct auxtrace_heap *heap) heap_array[last].ordinal); } -size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr) +size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr, + struct perf_evlist *evlist) { if (itr) - return itr->info_priv_size(itr); + return itr->info_priv_size(itr, evlist); return 0; } @@ -852,7 +853,7 @@ int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr, int err; pr_debug2("Synthesizing auxtrace information\n"); - priv_size = auxtrace_record__info_priv_size(itr); + priv_size = auxtrace_record__info_priv_size(itr, session->evlist); ev = zalloc(sizeof(struct auxtrace_info_event) + priv_size); if (!ev) return -ENOMEM; diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h index b86f90db1352..e5a8e2d4f2af 100644 --- a/tools/perf/util/auxtrace.h +++ b/tools/perf/util/auxtrace.h @@ -293,7 +293,8 @@ struct auxtrace_record { int (*recording_options)(struct auxtrace_record *itr, struct perf_evlist *evlist, struct record_opts *opts); - size_t (*info_priv_size)(struct auxtrace_record *itr); + size_t (*info_priv_size)(struct auxtrace_record *itr, + struct perf_evlist *evlist); int (*info_fill)(struct auxtrace_record *itr, struct perf_session *session, struct auxtrace_info_event *auxtrace_info, @@ -429,7 +430,8 @@ int auxtrace_parse_snapshot_options(struct auxtrace_record *itr, int auxtrace_record__options(struct auxtrace_record *itr, struct perf_evlist *evlist, struct record_opts *opts); -size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr); +size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr, + struct perf_evlist *evlist); int auxtrace_record__info_fill(struct auxtrace_record *itr, struct perf_session *session, struct auxtrace_info_event *auxtrace_info, From 5025483e77155a5aa0b62728ebc9ec45355807aa Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 4 Mar 2016 15:42:45 +0200 Subject: [PATCH 058/104] perf/ring_buffer: Refuse to begin AUX transaction after rb->aux_mmap_count drops When ring buffer's AUX area is unmapped and rb->aux_mmap_count drops to zero, new AUX transactions into this buffer can still be started, even though the buffer in en route to deallocation. This patch adds a check to perf_aux_output_begin() for rb->aux_mmap_count being zero, in which case there is no point starting new transactions, in other words, the ring buffers that pass a certain point in perf_mmap_close will not have their events sending new data, which clears path for freeing those buffers' pages right there and then, provided that no active transactions are holding the AUX reference. Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Cc: vince@deater.net Link: http://lkml.kernel.org/r/1457098969-21595-2-git-send-email-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar (cherry picked from commit dcb10a967ce82d5ad20570693091139ae716ff76) --- kernel/events/ring_buffer.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index 287201a5d12f..c5d0b63a148f 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -287,6 +287,13 @@ void *perf_aux_output_begin(struct perf_output_handle *handle, if (!rb_has_aux(rb) || !atomic_inc_not_zero(&rb->aux_refcount)) goto err; + /* + * If rb::aux_mmap_count is zero (and rb_has_aux() above went through), + * the aux buffer is in perf_mmap_close(), about to get freed. + */ + if (!atomic_read(&rb->aux_mmap_count)) + goto err; + /* * Nesting is not supported for AUX area, make sure nested * writers are caught early From c5611c7b5fc21996fc8d81e0d6cbcd75ced77bbb Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Wed, 2 Dec 2015 18:41:11 +0200 Subject: [PATCH 059/104] perf/core: Free AUX pages in unmap path Now that we can ensure that when ring buffer's AUX area is on the way to getting unmapped new transactions won't start, we only need to stop all events that can potentially be writing aux data to our ring buffer. Having done that, we can safely free the AUX pages and corresponding PMU data, as this time it is guaranteed to be the last aux reference holder. This partially reverts: 57ffc5ca679 ("perf: Fix AUX buffer refcounting") ... which was made to defer deallocation that was otherwise possible from an NMI context. Now it is no longer the case; the last call to rb_free_aux() that drops the last AUX reference has to happen in perf_mmap_close() on that AUX area. Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Cc: vince@deater.net Link: http://lkml.kernel.org/r/87d1qtz23d.fsf@ashishki-desk.ger.corp.intel.com Signed-off-by: Ingo Molnar (cherry picked from commit 95ff4ca26c492fc1ed7751f5dd7ab7674b54f4e0) --- kernel/events/core.c | 120 +++++++++++++++++++++++++++++++++++- kernel/events/internal.h | 1 - kernel/events/ring_buffer.c | 37 ++++------- 3 files changed, 129 insertions(+), 29 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index b296032a2c4e..cda4b292135a 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -1884,8 +1884,13 @@ event_sched_in(struct perf_event *event, if (event->state <= PERF_EVENT_STATE_OFF) return 0; - event->state = PERF_EVENT_STATE_ACTIVE; - event->oncpu = smp_processor_id(); + WRITE_ONCE(event->oncpu, smp_processor_id()); + /* + * Order event::oncpu write to happen before the ACTIVE state + * is visible. + */ + smp_wmb(); + WRITE_ONCE(event->state, PERF_EVENT_STATE_ACTIVE); /* * Unthrottle events, since we scheduled we might have missed several @@ -2366,6 +2371,29 @@ void perf_event_enable(struct perf_event *event) } EXPORT_SYMBOL_GPL(perf_event_enable); +static int __perf_event_stop(void *info) +{ + struct perf_event *event = info; + + /* for AUX events, our job is done if the event is already inactive */ + if (READ_ONCE(event->state) != PERF_EVENT_STATE_ACTIVE) + return 0; + + /* matches smp_wmb() in event_sched_in() */ + smp_rmb(); + + /* + * There is a window with interrupts enabled before we get here, + * so we need to check again lest we try to stop another CPU's event. + */ + if (READ_ONCE(event->oncpu) != smp_processor_id()) + return -EAGAIN; + + event->pmu->stop(event, PERF_EF_UPDATE); + + return 0; +} + static int _perf_event_refresh(struct perf_event *event, int refresh) { /* @@ -4625,6 +4653,8 @@ static void perf_mmap_open(struct vm_area_struct *vma) event->pmu->event_mapped(event); } +static void perf_pmu_output_stop(struct perf_event *event); + /* * A buffer can be mmap()ed multiple times; either directly through the same * event, or through other events by use of perf_event_set_output(). @@ -4652,10 +4682,22 @@ static void perf_mmap_close(struct vm_area_struct *vma) */ if (rb_has_aux(rb) && vma->vm_pgoff == rb->aux_pgoff && atomic_dec_and_mutex_lock(&rb->aux_mmap_count, &event->mmap_mutex)) { + /* + * Stop all AUX events that are writing to this buffer, + * so that we can free its AUX pages and corresponding PMU + * data. Note that after rb::aux_mmap_count dropped to zero, + * they won't start any more (see perf_aux_output_begin()). + */ + perf_pmu_output_stop(event); + + /* now it's safe to free the pages */ atomic_long_sub(rb->aux_nr_pages, &mmap_user->locked_vm); vma->vm_mm->pinned_vm -= rb->aux_mmap_locked; + /* this has to be the last one */ rb_free_aux(rb); + WARN_ON_ONCE(atomic_read(&rb->aux_refcount)); + mutex_unlock(&event->mmap_mutex); } @@ -5726,6 +5768,80 @@ perf_event_aux(perf_event_aux_output_cb output, void *data, rcu_read_unlock(); } +struct remote_output { + struct ring_buffer *rb; + int err; +}; + +static void __perf_event_output_stop(struct perf_event *event, void *data) +{ + struct perf_event *parent = event->parent; + struct remote_output *ro = data; + struct ring_buffer *rb = ro->rb; + + if (!has_aux(event)) + return; + + if (!parent) + parent = event; + + /* + * In case of inheritance, it will be the parent that links to the + * ring-buffer, but it will be the child that's actually using it: + */ + if (rcu_dereference(parent->rb) == rb) + ro->err = __perf_event_stop(event); +} + +static int __perf_pmu_output_stop(void *info) +{ + struct perf_event *event = info; + struct pmu *pmu = event->pmu; + struct perf_cpu_context *cpuctx = get_cpu_ptr(pmu->pmu_cpu_context); + struct remote_output ro = { + .rb = event->rb, + }; + + rcu_read_lock(); + perf_event_aux_ctx(&cpuctx->ctx, __perf_event_output_stop, &ro); + if (cpuctx->task_ctx) + perf_event_aux_ctx(cpuctx->task_ctx, __perf_event_output_stop, + &ro); + rcu_read_unlock(); + + return ro.err; +} + +static void perf_pmu_output_stop(struct perf_event *event) +{ + struct perf_event *iter; + int err, cpu; + +restart: + rcu_read_lock(); + list_for_each_entry_rcu(iter, &event->rb->event_list, rb_entry) { + /* + * For per-CPU events, we need to make sure that neither they + * nor their children are running; for cpu==-1 events it's + * sufficient to stop the event itself if it's active, since + * it can't have children. + */ + cpu = iter->cpu; + if (cpu == -1) + cpu = READ_ONCE(iter->oncpu); + + if (cpu == -1) + continue; + + err = cpu_function_call(cpu, __perf_pmu_output_stop, event); + if (err == -EAGAIN) { + rcu_read_unlock(); + goto restart; + } + } + rcu_read_unlock(); +} + /* * task tracking -- fork/exit * diff --git a/kernel/events/internal.h b/kernel/events/internal.h index 2bbad9c1274c..2b229fdcfc09 100644 --- a/kernel/events/internal.h +++ b/kernel/events/internal.h @@ -11,7 +11,6 @@ struct ring_buffer { atomic_t refcount; struct rcu_head rcu_head; - struct irq_work irq_work; #ifdef CONFIG_PERF_USE_VMALLOC struct work_struct work; int page_order; /* allocation order */ diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index c5d0b63a148f..dcd21cae982c 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -221,8 +221,6 @@ void perf_output_end(struct perf_output_handle *handle) rcu_read_unlock(); } -static void rb_irq_work(struct irq_work *work); - static void ring_buffer_init(struct ring_buffer *rb, long watermark, int flags) { @@ -243,16 +241,6 @@ ring_buffer_init(struct ring_buffer *rb, long watermark, int flags) INIT_LIST_HEAD(&rb->event_list); spin_lock_init(&rb->event_lock); - init_irq_work(&rb->irq_work, rb_irq_work); -} - -static void ring_buffer_put_async(struct ring_buffer *rb) -{ - if (!atomic_dec_and_test(&rb->refcount)) - return; - - rb->rcu_head.next = (void *)rb; - irq_work_queue(&rb->irq_work); } /* @@ -292,7 +280,7 @@ void *perf_aux_output_begin(struct perf_output_handle *handle, * the aux buffer is in perf_mmap_close(), about to get freed. */ if (!atomic_read(&rb->aux_mmap_count)) - goto err; + goto err_put; /* * Nesting is not supported for AUX area, make sure nested @@ -338,7 +326,7 @@ void *perf_aux_output_begin(struct perf_output_handle *handle, rb_free_aux(rb); err: - ring_buffer_put_async(rb); + ring_buffer_put(rb); handle->event = NULL; return NULL; @@ -397,7 +385,7 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size, local_set(&rb->aux_nest, 0); rb_free_aux(rb); - ring_buffer_put_async(rb); + ring_buffer_put(rb); } /* @@ -478,6 +466,14 @@ static void __rb_free_aux(struct ring_buffer *rb) { int pg; + /* + * Should never happen, the last reference should be dropped from + * perf_mmap_close() path, which first stops aux transactions (which + * in turn are the atomic holders of aux_refcount) and then does the + * last rb_free_aux(). + */ + WARN_ON_ONCE(in_atomic()); + if (rb->aux_priv) { rb->free_aux(rb->aux_priv); rb->free_aux = NULL; @@ -589,18 +585,7 @@ int rb_alloc_aux(struct ring_buffer *rb, struct perf_event *event, void rb_free_aux(struct ring_buffer *rb) { if (atomic_dec_and_test(&rb->aux_refcount)) - irq_work_queue(&rb->irq_work); -} - -static void rb_irq_work(struct irq_work *work) -{ - struct ring_buffer *rb = container_of(work, struct ring_buffer, irq_work); - - if (!atomic_read(&rb->aux_refcount)) __rb_free_aux(rb); - - if (rb->rcu_head.next == (void *)rb) - call_rcu(&rb->rcu_head, rb_free_rcu); } #ifndef CONFIG_PERF_USE_VMALLOC From 84b84017f4369341bdf54054f86947043fa534ef Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 4 Mar 2016 15:42:47 +0200 Subject: [PATCH 060/104] perf/ring_buffer: Document AUX API usage In order to ensure safe AUX buffer management, we rely on the assumption that pmu::stop() stops its ongoing AUX transaction and not just the hw. This patch documents this requirement for the perf_aux_output_{begin,end}() APIs. Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Mathieu Poirier Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Cc: vince@deater.net Link: http://lkml.kernel.org/r/1457098969-21595-4-git-send-email-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar (cherry picked from commit af5bb4ed1254a378b6028c09e58bdcc1cd9bf5b3) --- kernel/events/ring_buffer.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index dcd21cae982c..084be7d41bcf 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -252,6 +252,10 @@ ring_buffer_init(struct ring_buffer *rb, long watermark, int flags) * The ordering is similar to that of perf_output_{begin,end}, with * the exception of (B), which should be taken care of by the pmu * driver, since ordering rules will differ depending on hardware. + * + * Call this from pmu::start(); see the comment in perf_aux_output_end() + * about its use in pmu callbacks. Both can also be called from the PMI + * handler if needed. */ void *perf_aux_output_begin(struct perf_output_handle *handle, struct perf_event *event) @@ -323,6 +327,7 @@ void *perf_aux_output_begin(struct perf_output_handle *handle, return handle->rb->aux_priv; err_put: + /* can't be last */ rb_free_aux(rb); err: @@ -337,6 +342,10 @@ void *perf_aux_output_begin(struct perf_output_handle *handle, * aux_head and posting a PERF_RECORD_AUX into the perf buffer. It is the * pmu driver's responsibility to observe ordering rules of the hardware, * so that all the data is externally visible before this is called. + * + * Note: this has to be called from pmu::stop() callback, as the assumption + * of the AUX buffer management code is that after pmu::stop(), the AUX + * transaction must be stopped and therefore drop the AUX reference count. */ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size, bool truncated) @@ -384,6 +393,7 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size, handle->event = NULL; local_set(&rb->aux_nest, 0); + /* can't be last */ rb_free_aux(rb); ring_buffer_put(rb); } From de7b545d3abec0b103ec1e7df1b58b7edeecb873 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Thu, 31 Mar 2016 16:30:15 +0300 Subject: [PATCH 061/104] MAINTAINERS: Add a git tree for the stm class So that people know where their patches go. Signed-off-by: Alexander Shishkin Reviewed-by: Laurent Fert (cherry picked from commit e787bc463cc1fe3f51b0cd7bf540236318f69cf1) --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 233f83464814..6b4f6ac66326 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9356,6 +9356,7 @@ F: drivers/mmc/host/dw_mmc* SYSTEM TRACE MODULE CLASS M: Alexander Shishkin S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/ash/stm.git F: Documentation/trace/stm.txt F: drivers/hwtracing/stm/ F: include/linux/stm.h From 5b5ff82fe22e0417001852701b25185e5ac610c6 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 4 Mar 2016 16:22:33 +0200 Subject: [PATCH 062/104] stm class: dummy_stm: Make nr_dummies parameter read-only Changing nr_dummies after the module has been loaded doesn't actually change anything, so just make it read-only. Reported-by: Alan Cox Signed-off-by: Alexander Shishkin Reviewed-by: Laurent Fert (cherry picked from commit 118b4515aa6916ee7751f29c8b2a3af95abc9783) --- drivers/hwtracing/stm/dummy_stm.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/hwtracing/stm/dummy_stm.c b/drivers/hwtracing/stm/dummy_stm.c index 310adf57e7a1..a86612d989f9 100644 --- a/drivers/hwtracing/stm/dummy_stm.c +++ b/drivers/hwtracing/stm/dummy_stm.c @@ -46,9 +46,7 @@ static struct stm_data dummy_stm[DUMMY_STM_MAX]; static int nr_dummies = 4; -module_param(nr_dummies, int, 0600); - -static unsigned int dummy_stm_nr; +module_param(nr_dummies, int, 0400); static unsigned int fail_mode; @@ -65,12 +63,12 @@ static int dummy_stm_link(struct stm_data *data, unsigned int master, static int dummy_stm_init(void) { - int i, ret = -ENOMEM, __nr_dummies = ACCESS_ONCE(nr_dummies); + int i, ret = -ENOMEM; - if (__nr_dummies < 0 || __nr_dummies > DUMMY_STM_MAX) + if (nr_dummies < 0 || nr_dummies > DUMMY_STM_MAX) return -EINVAL; - for (i = 0; i < __nr_dummies; i++) { + for (i = 0; i < nr_dummies; i++) { dummy_stm[i].name = kasprintf(GFP_KERNEL, "dummy_stm.%d", i); if (!dummy_stm[i].name) goto fail_unregister; @@ -86,8 +84,6 @@ static int dummy_stm_init(void) goto fail_free; } - dummy_stm_nr = __nr_dummies; - return 0; fail_unregister: @@ -105,7 +101,7 @@ static void dummy_stm_exit(void) { int i; - for (i = 0; i < dummy_stm_nr; i++) { + for (i = 0; i < nr_dummies; i++) { stm_unregister_device(&dummy_stm[i]); kfree(dummy_stm[i].name); } From 450abebc37405e8a6b0167a7aa60922315e76b4c Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 4 Mar 2016 16:22:33 +0200 Subject: [PATCH 063/104] stm class: stm_heartbeat: Make nr_devs parameter read-only Changing nr_devs after the module has been loaded doesn't actually change anything, so just make it read-only. Reported-by: Alan Cox Signed-off-by: Alexander Shishkin Reviewed-by: Laurent Fert (cherry picked from commit c8be4899449c0b27bc5daf71742cd601b2b3b4e3) --- drivers/hwtracing/stm/heartbeat.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/hwtracing/stm/heartbeat.c b/drivers/hwtracing/stm/heartbeat.c index 0133571b506f..3da7b673aab2 100644 --- a/drivers/hwtracing/stm/heartbeat.c +++ b/drivers/hwtracing/stm/heartbeat.c @@ -26,7 +26,7 @@ static int nr_devs = 4; static int interval_ms = 10; -module_param(nr_devs, int, 0600); +module_param(nr_devs, int, 0400); module_param(interval_ms, int, 0600); static struct stm_heartbeat { @@ -35,8 +35,6 @@ static struct stm_heartbeat { unsigned int active; } stm_heartbeat[STM_HEARTBEAT_MAX]; -static unsigned int nr_instances; - static const char str[] = "heartbeat stm source driver is here to serve you"; static enum hrtimer_restart stm_heartbeat_hrtimer_handler(struct hrtimer *hr) @@ -74,12 +72,12 @@ static void stm_heartbeat_unlink(struct stm_source_data *data) static int stm_heartbeat_init(void) { - int i, ret = -ENOMEM, __nr_instances = ACCESS_ONCE(nr_devs); + int i, ret = -ENOMEM; - if (__nr_instances < 0 || __nr_instances > STM_HEARTBEAT_MAX) + if (nr_devs < 0 || nr_devs > STM_HEARTBEAT_MAX) return -EINVAL; - for (i = 0; i < __nr_instances; i++) { + for (i = 0; i < nr_devs; i++) { stm_heartbeat[i].data.name = kasprintf(GFP_KERNEL, "heartbeat.%d", i); if (!stm_heartbeat[i].data.name) @@ -98,8 +96,6 @@ static int stm_heartbeat_init(void) goto fail_free; } - nr_instances = __nr_instances; - return 0; fail_unregister: @@ -116,7 +112,7 @@ static void stm_heartbeat_exit(void) { int i; - for (i = 0; i < nr_instances; i++) { + for (i = 0; i < nr_devs; i++) { stm_source_unregister_device(&stm_heartbeat[i].data); kfree(stm_heartbeat[i].data.name); } From 5a4e9c9c9c37e7a907eccc9e112e9ecd1970b862 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 4 Mar 2016 16:30:24 +0200 Subject: [PATCH 064/104] stm class: Remove a pointless line No point in explicitly setting something to zero right after we explicitly checked that it is zero. Fix this. Reported-by: Alan Cox Signed-off-by: Alexander Shishkin Reviewed-by: Laurent Fert (cherry picked from commit 8fa11d1c1322f3de40a0e3f3f3e57436a204fcc4) --- drivers/hwtracing/stm/core.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 0dd8f4c996f1..a6c6b2d6a67b 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -546,8 +546,6 @@ static int stm_char_policy_set_ioctl(struct stm_file *stmf, void __user *arg) if (ret) goto err_free; - ret = 0; - if (stm->data->link) ret = stm->data->link(stm->data, stmf->output.master, stmf->output.channel); From 7c678b8584505e7005b99b4f09cca4496812d30c Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 4 Mar 2016 16:36:10 +0200 Subject: [PATCH 065/104] stm class: Do not leak the chrdev in error path Currently, the error path of stm_register_device() forgets to unregister the chrdev. Fix this. Reported-by: Alan Cox Signed-off-by: Alexander Shishkin Reviewed-by: Laurent Fert (cherry picked from commit cbe4a61d1ddc4790d950ca8c33ef79ee68ef5e2b) --- drivers/hwtracing/stm/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index a6c6b2d6a67b..0ffccfdd1017 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -688,6 +688,8 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data, return 0; err_device: + unregister_chrdev(stm->major, stm_data->name); + /* matches device_initialize() above */ put_device(&stm->dev); err_free: From 134bbd594d1c21ffa97ab49d70816536369f1fb1 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 4 Mar 2016 16:48:14 +0200 Subject: [PATCH 066/104] stm class: Fix stm device initialization order Currently, stm_register_device() makes the device visible and then proceeds to initializing spinlocks and other properties, which leaves a window when the device can already be opened but is not yet fully operational. Fix this by reversing the initialization order. Reported-by: Alan Cox Signed-off-by: Alexander Shishkin Reviewed-by: Laurent Fert (cherry picked from commit 389b6699a2aa0b457aa69986e9ddf39f3b4030fd) --- drivers/hwtracing/stm/core.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 0ffccfdd1017..d21eb53ba199 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -666,6 +666,18 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data, stm->dev.parent = parent; stm->dev.release = stm_device_release; + mutex_init(&stm->link_mutex); + spin_lock_init(&stm->link_lock); + INIT_LIST_HEAD(&stm->link_list); + + /* initialize the object before it is accessible via sysfs */ + spin_lock_init(&stm->mc_lock); + mutex_init(&stm->policy_mutex); + stm->sw_nmasters = nmasters; + stm->owner = owner; + stm->data = stm_data; + stm_data->stm = stm; + err = kobject_set_name(&stm->dev.kobj, "%s", stm_data->name); if (err) goto err_device; @@ -674,17 +686,6 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data, if (err) goto err_device; - mutex_init(&stm->link_mutex); - spin_lock_init(&stm->link_lock); - INIT_LIST_HEAD(&stm->link_list); - - spin_lock_init(&stm->mc_lock); - mutex_init(&stm->policy_mutex); - stm->sw_nmasters = nmasters; - stm->owner = owner; - stm->data = stm_data; - stm_data->stm = stm; - return 0; err_device: From 8ebbc16a38eea98885d097b14551dcc378ba0ba5 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 4 Mar 2016 16:55:12 +0200 Subject: [PATCH 067/104] stm class: Remove unnecessary pointer increment Readability: a postfix increment is used on a pointer which is not used anywhere afterwards, which may send the reader looking through the function one extra time. Drop the unnecessary increment. Reported-by: Alan Cox Signed-off-by: Alexander Shishkin Reviewed-by: Laurent Fert (cherry picked from commit fb0801904bbbc7b109d4009520c7fa34bcfb7450) --- drivers/hwtracing/stm/policy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index 1db189657b2b..1c061cb9bff0 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -342,7 +342,7 @@ stp_policies_make(struct config_group *group, const char *name) return ERR_PTR(-EINVAL); } - *p++ = '\0'; + *p = '\0'; stm = stm_find_device(devname); kfree(devname); From 1525472db066e6d9a56b8b0f6ef040b43bdc3cbe Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 3 May 2016 11:33:37 -0600 Subject: [PATCH 068/104] stm class: Support devices that override software assigned masters Some STM devices adjust software assigned master numbers depending on the trace source and its runtime state and whatnot. This patch adds a sysfs attribute to inform the trace-side software that master numbers assigned to software sources will not match those in the STP stream, so that, for example, master/channel allocation policy can be adjusted accordingly. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 8e996a2874bbbed30e8dfe881453825fc6b7654e) --- Documentation/ABI/testing/sysfs-class-stm | 10 ++++++++++ drivers/hwtracing/stm/core.c | 15 +++++++++++++++ include/linux/stm.h | 3 +++ 3 files changed, 28 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-class-stm b/Documentation/ABI/testing/sysfs-class-stm index c9aa4f3fc9a7..77ed3da0f68e 100644 --- a/Documentation/ABI/testing/sysfs-class-stm +++ b/Documentation/ABI/testing/sysfs-class-stm @@ -12,3 +12,13 @@ KernelVersion: 4.3 Contact: Alexander Shishkin Description: Shows the number of channels per master on this STM device. + +What: /sys/class/stm//hw_override +Date: March 2016 +KernelVersion: 4.7 +Contact: Alexander Shishkin +Description: + Reads as 0 if master numbers in the STP stream produced by + this stm device will match the master numbers assigned by + the software or 1 if the stm hardware overrides software + assigned masters. diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index d21eb53ba199..02095410cb33 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -67,9 +67,24 @@ static ssize_t channels_show(struct device *dev, static DEVICE_ATTR_RO(channels); +static ssize_t hw_override_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct stm_device *stm = to_stm_device(dev); + int ret; + + ret = sprintf(buf, "%u\n", stm->data->hw_override); + + return ret; +} + +static DEVICE_ATTR_RO(hw_override); + static struct attribute *stm_attrs[] = { &dev_attr_masters.attr, &dev_attr_channels.attr, + &dev_attr_hw_override.attr, NULL, }; diff --git a/include/linux/stm.h b/include/linux/stm.h index 1a79ed8e43da..8369d8a8cabd 100644 --- a/include/linux/stm.h +++ b/include/linux/stm.h @@ -50,6 +50,8 @@ struct stm_device; * @sw_end: last STP master available to software * @sw_nchannels: number of STP channels per master * @sw_mmiosz: size of one channel's IO space, for mmap, optional + * @hw_override: masters in the STP stream will not match the ones + * assigned by software, but are up to the STM hardware * @packet: callback that sends an STP packet * @mmio_addr: mmap callback, optional * @link: called when a new stm_source gets linked to us, optional @@ -85,6 +87,7 @@ struct stm_data { unsigned int sw_end; unsigned int sw_nchannels; unsigned int sw_mmiosz; + unsigned int hw_override; ssize_t (*packet)(struct stm_data *, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, From f069ef6e0dace0ad0b6f5975672cd5b750628eba Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 5 Apr 2016 11:53:42 -0600 Subject: [PATCH 069/104] coresight: etm4x: moving sysFS entries to a dedicated file As with the etm3x driver, sysFS entries are big enough to justify their own file. As such moving all sysFS related declarations to a dedicated location. No gain/loss of functionality is incurred from this patch. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit a77de2637c9eb4794c6234b40cee2a243c548875) --- drivers/hwtracing/coresight/Makefile | 3 +- .../coresight/coresight-etm4x-sysfs.c | 2048 +++++++++++++++++ drivers/hwtracing/coresight/coresight-etm4x.c | 2029 ---------------- drivers/hwtracing/coresight/coresight-etm4x.h | 2 + 4 files changed, 2052 insertions(+), 2030 deletions(-) create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-sysfs.c diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index cf8c6d689747..637ca978cd05 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -11,5 +11,6 @@ obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ coresight-etm3x-sysfs.o \ coresight-etm-perf.o -obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o +obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \ + coresight-etm4x-sysfs.o obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c new file mode 100644 index 000000000000..5db0de7d4e0e --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -0,0 +1,2048 @@ +/* + * Copyright(C) 2015 Linaro Limited. All rights reserved. + * Author: Mathieu Poirier + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include "coresight-etm4x.h" + +static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude) +{ + u8 idx = drvdata->addr_idx; + + /* + * TRCACATRn.TYPE bit[1:0]: type of comparison + * the trace unit performs + */ + if (BMVAL(drvdata->addr_acc[idx], 0, 1) == ETM_INSTR_ADDR) { + if (idx % 2 != 0) + return -EINVAL; + + /* + * We are performing instruction address comparison. Set the + * relevant bit of ViewInst Include/Exclude Control register + * for corresponding address comparator pair. + */ + if (drvdata->addr_type[idx] != ETM_ADDR_TYPE_RANGE || + drvdata->addr_type[idx + 1] != ETM_ADDR_TYPE_RANGE) + return -EINVAL; + + if (exclude == true) { + /* + * Set exclude bit and unset the include bit + * corresponding to comparator pair + */ + drvdata->viiectlr |= BIT(idx / 2 + 16); + drvdata->viiectlr &= ~BIT(idx / 2); + } else { + /* + * Set include bit and unset exclude bit + * corresponding to comparator pair + */ + drvdata->viiectlr |= BIT(idx / 2); + drvdata->viiectlr &= ~BIT(idx / 2 + 16); + } + } + return 0; +} + +static ssize_t nr_pe_cmp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->nr_pe_cmp; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(nr_pe_cmp); + +static ssize_t nr_addr_cmp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->nr_addr_cmp; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(nr_addr_cmp); + +static ssize_t nr_cntr_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->nr_cntr; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(nr_cntr); + +static ssize_t nr_ext_inp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->nr_ext_inp; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(nr_ext_inp); + +static ssize_t numcidc_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->numcidc; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(numcidc); + +static ssize_t numvmidc_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->numvmidc; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(numvmidc); + +static ssize_t nrseqstate_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->nrseqstate; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(nrseqstate); + +static ssize_t nr_resource_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->nr_resource; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(nr_resource); + +static ssize_t nr_ss_cmp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->nr_ss_cmp; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(nr_ss_cmp); + +static ssize_t reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int i; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + if (val) + drvdata->mode = 0x0; + + /* Disable data tracing: do not trace load and store data transfers */ + drvdata->mode &= ~(ETM_MODE_LOAD | ETM_MODE_STORE); + drvdata->cfg &= ~(BIT(1) | BIT(2)); + + /* Disable data value and data address tracing */ + drvdata->mode &= ~(ETM_MODE_DATA_TRACE_ADDR | + ETM_MODE_DATA_TRACE_VAL); + drvdata->cfg &= ~(BIT(16) | BIT(17)); + + /* Disable all events tracing */ + drvdata->eventctrl0 = 0x0; + drvdata->eventctrl1 = 0x0; + + /* Disable timestamp event */ + drvdata->ts_ctrl = 0x0; + + /* Disable stalling */ + drvdata->stall_ctrl = 0x0; + + /* Reset trace synchronization period to 2^8 = 256 bytes*/ + if (drvdata->syncpr == false) + drvdata->syncfreq = 0x8; + + /* + * Enable ViewInst to trace everything with start-stop logic in + * started state. ARM recommends start-stop logic is set before + * each trace run. + */ + drvdata->vinst_ctrl |= BIT(0); + if (drvdata->nr_addr_cmp == true) { + drvdata->mode |= ETM_MODE_VIEWINST_STARTSTOP; + /* SSSTATUS, bit[9] */ + drvdata->vinst_ctrl |= BIT(9); + } + + /* No address range filtering for ViewInst */ + drvdata->viiectlr = 0x0; + + /* No start-stop filtering for ViewInst */ + drvdata->vissctlr = 0x0; + + /* Disable seq events */ + for (i = 0; i < drvdata->nrseqstate-1; i++) + drvdata->seq_ctrl[i] = 0x0; + drvdata->seq_rst = 0x0; + drvdata->seq_state = 0x0; + + /* Disable external input events */ + drvdata->ext_inp = 0x0; + + drvdata->cntr_idx = 0x0; + for (i = 0; i < drvdata->nr_cntr; i++) { + drvdata->cntrldvr[i] = 0x0; + drvdata->cntr_ctrl[i] = 0x0; + drvdata->cntr_val[i] = 0x0; + } + + drvdata->res_idx = 0x0; + for (i = 0; i < drvdata->nr_resource; i++) + drvdata->res_ctrl[i] = 0x0; + + for (i = 0; i < drvdata->nr_ss_cmp; i++) { + drvdata->ss_ctrl[i] = 0x0; + drvdata->ss_pe_cmp[i] = 0x0; + } + + drvdata->addr_idx = 0x0; + for (i = 0; i < drvdata->nr_addr_cmp * 2; i++) { + drvdata->addr_val[i] = 0x0; + drvdata->addr_acc[i] = 0x0; + drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE; + } + + drvdata->ctxid_idx = 0x0; + for (i = 0; i < drvdata->numcidc; i++) { + drvdata->ctxid_pid[i] = 0x0; + drvdata->ctxid_vpid[i] = 0x0; + } + + drvdata->ctxid_mask0 = 0x0; + drvdata->ctxid_mask1 = 0x0; + + drvdata->vmid_idx = 0x0; + for (i = 0; i < drvdata->numvmidc; i++) + drvdata->vmid_val[i] = 0x0; + drvdata->vmid_mask0 = 0x0; + drvdata->vmid_mask1 = 0x0; + + drvdata->trcid = drvdata->cpu + 1; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_WO(reset); + +static ssize_t mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->mode; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val, mode; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + drvdata->mode = val & ETMv4_MODE_ALL; + + if (drvdata->mode & ETM_MODE_EXCLUDE) + etm4_set_mode_exclude(drvdata, true); + else + etm4_set_mode_exclude(drvdata, false); + + if (drvdata->instrp0 == true) { + /* start by clearing instruction P0 field */ + drvdata->cfg &= ~(BIT(1) | BIT(2)); + if (drvdata->mode & ETM_MODE_LOAD) + /* 0b01 Trace load instructions as P0 instructions */ + drvdata->cfg |= BIT(1); + if (drvdata->mode & ETM_MODE_STORE) + /* 0b10 Trace store instructions as P0 instructions */ + drvdata->cfg |= BIT(2); + if (drvdata->mode & ETM_MODE_LOAD_STORE) + /* + * 0b11 Trace load and store instructions + * as P0 instructions + */ + drvdata->cfg |= BIT(1) | BIT(2); + } + + /* bit[3], Branch broadcast mode */ + if ((drvdata->mode & ETM_MODE_BB) && (drvdata->trcbb == true)) + drvdata->cfg |= BIT(3); + else + drvdata->cfg &= ~BIT(3); + + /* bit[4], Cycle counting instruction trace bit */ + if ((drvdata->mode & ETMv4_MODE_CYCACC) && + (drvdata->trccci == true)) + drvdata->cfg |= BIT(4); + else + drvdata->cfg &= ~BIT(4); + + /* bit[6], Context ID tracing bit */ + if ((drvdata->mode & ETMv4_MODE_CTXID) && (drvdata->ctxid_size)) + drvdata->cfg |= BIT(6); + else + drvdata->cfg &= ~BIT(6); + + if ((drvdata->mode & ETM_MODE_VMID) && (drvdata->vmid_size)) + drvdata->cfg |= BIT(7); + else + drvdata->cfg &= ~BIT(7); + + /* bits[10:8], Conditional instruction tracing bit */ + mode = ETM_MODE_COND(drvdata->mode); + if (drvdata->trccond == true) { + drvdata->cfg &= ~(BIT(8) | BIT(9) | BIT(10)); + drvdata->cfg |= mode << 8; + } + + /* bit[11], Global timestamp tracing bit */ + if ((drvdata->mode & ETMv4_MODE_TIMESTAMP) && (drvdata->ts_size)) + drvdata->cfg |= BIT(11); + else + drvdata->cfg &= ~BIT(11); + + /* bit[12], Return stack enable bit */ + if ((drvdata->mode & ETM_MODE_RETURNSTACK) && + (drvdata->retstack == true)) + drvdata->cfg |= BIT(12); + else + drvdata->cfg &= ~BIT(12); + + /* bits[14:13], Q element enable field */ + mode = ETM_MODE_QELEM(drvdata->mode); + /* start by clearing QE bits */ + drvdata->cfg &= ~(BIT(13) | BIT(14)); + /* if supported, Q elements with instruction counts are enabled */ + if ((mode & BIT(0)) && (drvdata->q_support & BIT(0))) + drvdata->cfg |= BIT(13); + /* + * if supported, Q elements with and without instruction + * counts are enabled + */ + if ((mode & BIT(1)) && (drvdata->q_support & BIT(1))) + drvdata->cfg |= BIT(14); + + /* bit[11], AMBA Trace Bus (ATB) trigger enable bit */ + if ((drvdata->mode & ETM_MODE_ATB_TRIGGER) && + (drvdata->atbtrig == true)) + drvdata->eventctrl1 |= BIT(11); + else + drvdata->eventctrl1 &= ~BIT(11); + + /* bit[12], Low-power state behavior override bit */ + if ((drvdata->mode & ETM_MODE_LPOVERRIDE) && + (drvdata->lpoverride == true)) + drvdata->eventctrl1 |= BIT(12); + else + drvdata->eventctrl1 &= ~BIT(12); + + /* bit[8], Instruction stall bit */ + if (drvdata->mode & ETM_MODE_ISTALL_EN) + drvdata->stall_ctrl |= BIT(8); + else + drvdata->stall_ctrl &= ~BIT(8); + + /* bit[10], Prioritize instruction trace bit */ + if (drvdata->mode & ETM_MODE_INSTPRIO) + drvdata->stall_ctrl |= BIT(10); + else + drvdata->stall_ctrl &= ~BIT(10); + + /* bit[13], Trace overflow prevention bit */ + if ((drvdata->mode & ETM_MODE_NOOVERFLOW) && + (drvdata->nooverflow == true)) + drvdata->stall_ctrl |= BIT(13); + else + drvdata->stall_ctrl &= ~BIT(13); + + /* bit[9] Start/stop logic control bit */ + if (drvdata->mode & ETM_MODE_VIEWINST_STARTSTOP) + drvdata->vinst_ctrl |= BIT(9); + else + drvdata->vinst_ctrl &= ~BIT(9); + + /* bit[10], Whether a trace unit must trace a Reset exception */ + if (drvdata->mode & ETM_MODE_TRACE_RESET) + drvdata->vinst_ctrl |= BIT(10); + else + drvdata->vinst_ctrl &= ~BIT(10); + + /* bit[11], Whether a trace unit must trace a system error exception */ + if ((drvdata->mode & ETM_MODE_TRACE_ERR) && + (drvdata->trc_error == true)) + drvdata->vinst_ctrl |= BIT(11); + else + drvdata->vinst_ctrl &= ~BIT(11); + + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(mode); + +static ssize_t pe_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->pe_sel; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t pe_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + if (val > drvdata->nr_pe) { + spin_unlock(&drvdata->spinlock); + return -EINVAL; + } + + drvdata->pe_sel = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(pe); + +static ssize_t event_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->eventctrl0; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + switch (drvdata->nr_event) { + case 0x0: + /* EVENT0, bits[7:0] */ + drvdata->eventctrl0 = val & 0xFF; + break; + case 0x1: + /* EVENT1, bits[15:8] */ + drvdata->eventctrl0 = val & 0xFFFF; + break; + case 0x2: + /* EVENT2, bits[23:16] */ + drvdata->eventctrl0 = val & 0xFFFFFF; + break; + case 0x3: + /* EVENT3, bits[31:24] */ + drvdata->eventctrl0 = val; + break; + default: + break; + } + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(event); + +static ssize_t event_instren_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = BMVAL(drvdata->eventctrl1, 0, 3); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t event_instren_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + /* start by clearing all instruction event enable bits */ + drvdata->eventctrl1 &= ~(BIT(0) | BIT(1) | BIT(2) | BIT(3)); + switch (drvdata->nr_event) { + case 0x0: + /* generate Event element for event 1 */ + drvdata->eventctrl1 |= val & BIT(1); + break; + case 0x1: + /* generate Event element for event 1 and 2 */ + drvdata->eventctrl1 |= val & (BIT(0) | BIT(1)); + break; + case 0x2: + /* generate Event element for event 1, 2 and 3 */ + drvdata->eventctrl1 |= val & (BIT(0) | BIT(1) | BIT(2)); + break; + case 0x3: + /* generate Event element for all 4 events */ + drvdata->eventctrl1 |= val & 0xF; + break; + default: + break; + } + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(event_instren); + +static ssize_t event_ts_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->ts_ctrl; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t event_ts_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (!drvdata->ts_size) + return -EINVAL; + + drvdata->ts_ctrl = val & ETMv4_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(event_ts); + +static ssize_t syncfreq_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->syncfreq; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t syncfreq_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (drvdata->syncpr == true) + return -EINVAL; + + drvdata->syncfreq = val & ETMv4_SYNC_MASK; + return size; +} +static DEVICE_ATTR_RW(syncfreq); + +static ssize_t cyc_threshold_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->ccctlr; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t cyc_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (val < drvdata->ccitmin) + return -EINVAL; + + drvdata->ccctlr = val & ETM_CYC_THRESHOLD_MASK; + return size; +} +static DEVICE_ATTR_RW(cyc_threshold); + +static ssize_t bb_ctrl_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->bb_ctrl; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t bb_ctrl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (drvdata->trcbb == false) + return -EINVAL; + if (!drvdata->nr_addr_cmp) + return -EINVAL; + /* + * Bit[7:0] selects which address range comparator is used for + * branch broadcast control. + */ + if (BMVAL(val, 0, 7) > drvdata->nr_addr_cmp) + return -EINVAL; + + drvdata->bb_ctrl = val; + return size; +} +static DEVICE_ATTR_RW(bb_ctrl); + +static ssize_t event_vinst_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->vinst_ctrl & ETMv4_EVENT_MASK; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t event_vinst_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + val &= ETMv4_EVENT_MASK; + drvdata->vinst_ctrl &= ~ETMv4_EVENT_MASK; + drvdata->vinst_ctrl |= val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(event_vinst); + +static ssize_t s_exlevel_vinst_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = BMVAL(drvdata->vinst_ctrl, 16, 19); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t s_exlevel_vinst_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + /* clear all EXLEVEL_S bits (bit[18] is never implemented) */ + drvdata->vinst_ctrl &= ~(BIT(16) | BIT(17) | BIT(19)); + /* enable instruction tracing for corresponding exception level */ + val &= drvdata->s_ex_level; + drvdata->vinst_ctrl |= (val << 16); + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(s_exlevel_vinst); + +static ssize_t ns_exlevel_vinst_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + /* EXLEVEL_NS, bits[23:20] */ + val = BMVAL(drvdata->vinst_ctrl, 20, 23); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t ns_exlevel_vinst_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + /* clear EXLEVEL_NS bits (bit[23] is never implemented */ + drvdata->vinst_ctrl &= ~(BIT(20) | BIT(21) | BIT(22)); + /* enable instruction tracing for corresponding exception level */ + val &= drvdata->ns_ex_level; + drvdata->vinst_ctrl |= (val << 20); + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(ns_exlevel_vinst); + +static ssize_t addr_idx_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->addr_idx; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t addr_idx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (val >= drvdata->nr_addr_cmp * 2) + return -EINVAL; + + /* + * Use spinlock to ensure index doesn't change while it gets + * dereferenced multiple times within a spinlock block elsewhere. + */ + spin_lock(&drvdata->spinlock); + drvdata->addr_idx = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(addr_idx); + +static ssize_t addr_instdatatype_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len; + u8 val, idx; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + val = BMVAL(drvdata->addr_acc[idx], 0, 1); + len = scnprintf(buf, PAGE_SIZE, "%s\n", + val == ETM_INSTR_ADDR ? "instr" : + (val == ETM_DATA_LOAD_ADDR ? "data_load" : + (val == ETM_DATA_STORE_ADDR ? "data_store" : + "data_load_store"))); + spin_unlock(&drvdata->spinlock); + return len; +} + +static ssize_t addr_instdatatype_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + char str[20] = ""; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (strlen(buf) >= 20) + return -EINVAL; + if (sscanf(buf, "%s", str) != 1) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!strcmp(str, "instr")) + /* TYPE, bits[1:0] */ + drvdata->addr_acc[idx] &= ~(BIT(0) | BIT(1)); + + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(addr_instdatatype); + +static ssize_t addr_single_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + idx = drvdata->addr_idx; + spin_lock(&drvdata->spinlock); + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + val = (unsigned long)drvdata->addr_val[idx]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t addr_single_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + drvdata->addr_val[idx] = (u64)val; + drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(addr_single); + +static ssize_t addr_range_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 idx; + unsigned long val1, val2; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (idx % 2 != 0) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && + drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || + (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && + drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + val1 = (unsigned long)drvdata->addr_val[idx]; + val2 = (unsigned long)drvdata->addr_val[idx + 1]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2); +} + +static ssize_t addr_range_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val1, val2; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) + return -EINVAL; + /* lower address comparator cannot have a higher address value */ + if (val1 > val2) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (idx % 2 != 0) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && + drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || + (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && + drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + drvdata->addr_val[idx] = (u64)val1; + drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE; + drvdata->addr_val[idx + 1] = (u64)val2; + drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE; + /* + * Program include or exclude control bits for vinst or vdata + * whenever we change addr comparators to ETM_ADDR_TYPE_RANGE + */ + if (drvdata->mode & ETM_MODE_EXCLUDE) + etm4_set_mode_exclude(drvdata, true); + else + etm4_set_mode_exclude(drvdata, false); + + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(addr_range); + +static ssize_t addr_start_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + val = (unsigned long)drvdata->addr_val[idx]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t addr_start_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!drvdata->nr_addr_cmp) { + spin_unlock(&drvdata->spinlock); + return -EINVAL; + } + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + drvdata->addr_val[idx] = (u64)val; + drvdata->addr_type[idx] = ETM_ADDR_TYPE_START; + drvdata->vissctlr |= BIT(idx); + /* SSSTATUS, bit[9] - turn on start/stop logic */ + drvdata->vinst_ctrl |= BIT(9); + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(addr_start); + +static ssize_t addr_stop_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + val = (unsigned long)drvdata->addr_val[idx]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t addr_stop_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!drvdata->nr_addr_cmp) { + spin_unlock(&drvdata->spinlock); + return -EINVAL; + } + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + drvdata->addr_val[idx] = (u64)val; + drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP; + drvdata->vissctlr |= BIT(idx + 16); + /* SSSTATUS, bit[9] - turn on start/stop logic */ + drvdata->vinst_ctrl |= BIT(9); + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(addr_stop); + +static ssize_t addr_ctxtype_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len; + u8 idx, val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + /* CONTEXTTYPE, bits[3:2] */ + val = BMVAL(drvdata->addr_acc[idx], 2, 3); + len = scnprintf(buf, PAGE_SIZE, "%s\n", val == ETM_CTX_NONE ? "none" : + (val == ETM_CTX_CTXID ? "ctxid" : + (val == ETM_CTX_VMID ? "vmid" : "all"))); + spin_unlock(&drvdata->spinlock); + return len; +} + +static ssize_t addr_ctxtype_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + char str[10] = ""; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (strlen(buf) >= 10) + return -EINVAL; + if (sscanf(buf, "%s", str) != 1) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!strcmp(str, "none")) + /* start by clearing context type bits */ + drvdata->addr_acc[idx] &= ~(BIT(2) | BIT(3)); + else if (!strcmp(str, "ctxid")) { + /* 0b01 The trace unit performs a Context ID */ + if (drvdata->numcidc) { + drvdata->addr_acc[idx] |= BIT(2); + drvdata->addr_acc[idx] &= ~BIT(3); + } + } else if (!strcmp(str, "vmid")) { + /* 0b10 The trace unit performs a VMID */ + if (drvdata->numvmidc) { + drvdata->addr_acc[idx] &= ~BIT(2); + drvdata->addr_acc[idx] |= BIT(3); + } + } else if (!strcmp(str, "all")) { + /* + * 0b11 The trace unit performs a Context ID + * comparison and a VMID + */ + if (drvdata->numcidc) + drvdata->addr_acc[idx] |= BIT(2); + if (drvdata->numvmidc) + drvdata->addr_acc[idx] |= BIT(3); + } + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(addr_ctxtype); + +static ssize_t addr_context_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + /* context ID comparator bits[6:4] */ + val = BMVAL(drvdata->addr_acc[idx], 4, 6); + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t addr_context_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if ((drvdata->numcidc <= 1) && (drvdata->numvmidc <= 1)) + return -EINVAL; + if (val >= (drvdata->numcidc >= drvdata->numvmidc ? + drvdata->numcidc : drvdata->numvmidc)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + /* clear context ID comparator bits[6:4] */ + drvdata->addr_acc[idx] &= ~(BIT(4) | BIT(5) | BIT(6)); + drvdata->addr_acc[idx] |= (val << 4); + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(addr_context); + +static ssize_t seq_idx_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_idx; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t seq_idx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (val >= drvdata->nrseqstate - 1) + return -EINVAL; + + /* + * Use spinlock to ensure index doesn't change while it gets + * dereferenced multiple times within a spinlock block elsewhere. + */ + spin_lock(&drvdata->spinlock); + drvdata->seq_idx = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(seq_idx); + +static ssize_t seq_state_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_state; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t seq_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (val >= drvdata->nrseqstate) + return -EINVAL; + + drvdata->seq_state = val; + return size; +} +static DEVICE_ATTR_RW(seq_state); + +static ssize_t seq_event_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->seq_idx; + val = drvdata->seq_ctrl[idx]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t seq_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->seq_idx; + /* RST, bits[7:0] */ + drvdata->seq_ctrl[idx] = val & 0xFF; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(seq_event); + +static ssize_t seq_reset_event_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_rst; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t seq_reset_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (!(drvdata->nrseqstate)) + return -EINVAL; + + drvdata->seq_rst = val & ETMv4_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(seq_reset_event); + +static ssize_t cntr_idx_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->cntr_idx; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t cntr_idx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (val >= drvdata->nr_cntr) + return -EINVAL; + + /* + * Use spinlock to ensure index doesn't change while it gets + * dereferenced multiple times within a spinlock block elsewhere. + */ + spin_lock(&drvdata->spinlock); + drvdata->cntr_idx = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(cntr_idx); + +static ssize_t cntrldvr_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->cntr_idx; + val = drvdata->cntrldvr[idx]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t cntrldvr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (val > ETM_CNTR_MAX_VAL) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->cntr_idx; + drvdata->cntrldvr[idx] = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(cntrldvr); + +static ssize_t cntr_val_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->cntr_idx; + val = drvdata->cntr_val[idx]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t cntr_val_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (val > ETM_CNTR_MAX_VAL) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->cntr_idx; + drvdata->cntr_val[idx] = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(cntr_val); + +static ssize_t cntr_ctrl_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->cntr_idx; + val = drvdata->cntr_ctrl[idx]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t cntr_ctrl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->cntr_idx; + drvdata->cntr_ctrl[idx] = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(cntr_ctrl); + +static ssize_t res_idx_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->res_idx; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t res_idx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + /* Resource selector pair 0 is always implemented and reserved */ + if ((val == 0) || (val >= drvdata->nr_resource)) + return -EINVAL; + + /* + * Use spinlock to ensure index doesn't change while it gets + * dereferenced multiple times within a spinlock block elsewhere. + */ + spin_lock(&drvdata->spinlock); + drvdata->res_idx = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(res_idx); + +static ssize_t res_ctrl_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->res_idx; + val = drvdata->res_ctrl[idx]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t res_ctrl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->res_idx; + /* For odd idx pair inversal bit is RES0 */ + if (idx % 2 != 0) + /* PAIRINV, bit[21] */ + val &= ~BIT(21); + drvdata->res_ctrl[idx] = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(res_ctrl); + +static ssize_t ctxid_idx_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->ctxid_idx; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t ctxid_idx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (val >= drvdata->numcidc) + return -EINVAL; + + /* + * Use spinlock to ensure index doesn't change while it gets + * dereferenced multiple times within a spinlock block elsewhere. + */ + spin_lock(&drvdata->spinlock); + drvdata->ctxid_idx = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(ctxid_idx); + +static ssize_t ctxid_pid_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->ctxid_idx; + val = (unsigned long)drvdata->ctxid_vpid[idx]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t ctxid_pid_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long vpid, pid; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + /* + * only implemented when ctxid tracing is enabled, i.e. at least one + * ctxid comparator is implemented and ctxid is greater than 0 bits + * in length + */ + if (!drvdata->ctxid_size || !drvdata->numcidc) + return -EINVAL; + if (kstrtoul(buf, 16, &vpid)) + return -EINVAL; + + pid = coresight_vpid_to_pid(vpid); + + spin_lock(&drvdata->spinlock); + idx = drvdata->ctxid_idx; + drvdata->ctxid_pid[idx] = (u64)pid; + drvdata->ctxid_vpid[idx] = (u64)vpid; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(ctxid_pid); + +static ssize_t ctxid_masks_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val1, val2; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + val1 = drvdata->ctxid_mask0; + val2 = drvdata->ctxid_mask1; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2); +} + +static ssize_t ctxid_masks_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 i, j, maskbyte; + unsigned long val1, val2, mask; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + /* + * only implemented when ctxid tracing is enabled, i.e. at least one + * ctxid comparator is implemented and ctxid is greater than 0 bits + * in length + */ + if (!drvdata->ctxid_size || !drvdata->numcidc) + return -EINVAL; + if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + /* + * each byte[0..3] controls mask value applied to ctxid + * comparator[0..3] + */ + switch (drvdata->numcidc) { + case 0x1: + /* COMP0, bits[7:0] */ + drvdata->ctxid_mask0 = val1 & 0xFF; + break; + case 0x2: + /* COMP1, bits[15:8] */ + drvdata->ctxid_mask0 = val1 & 0xFFFF; + break; + case 0x3: + /* COMP2, bits[23:16] */ + drvdata->ctxid_mask0 = val1 & 0xFFFFFF; + break; + case 0x4: + /* COMP3, bits[31:24] */ + drvdata->ctxid_mask0 = val1; + break; + case 0x5: + /* COMP4, bits[7:0] */ + drvdata->ctxid_mask0 = val1; + drvdata->ctxid_mask1 = val2 & 0xFF; + break; + case 0x6: + /* COMP5, bits[15:8] */ + drvdata->ctxid_mask0 = val1; + drvdata->ctxid_mask1 = val2 & 0xFFFF; + break; + case 0x7: + /* COMP6, bits[23:16] */ + drvdata->ctxid_mask0 = val1; + drvdata->ctxid_mask1 = val2 & 0xFFFFFF; + break; + case 0x8: + /* COMP7, bits[31:24] */ + drvdata->ctxid_mask0 = val1; + drvdata->ctxid_mask1 = val2; + break; + default: + break; + } + /* + * If software sets a mask bit to 1, it must program relevant byte + * of ctxid comparator value 0x0, otherwise behavior is unpredictable. + * For example, if bit[3] of ctxid_mask0 is 1, we must clear bits[31:24] + * of ctxid comparator0 value (corresponding to byte 0) register. + */ + mask = drvdata->ctxid_mask0; + for (i = 0; i < drvdata->numcidc; i++) { + /* mask value of corresponding ctxid comparator */ + maskbyte = mask & ETMv4_EVENT_MASK; + /* + * each bit corresponds to a byte of respective ctxid comparator + * value register + */ + for (j = 0; j < 8; j++) { + if (maskbyte & 1) + drvdata->ctxid_pid[i] &= ~(0xFF << (j * 8)); + maskbyte >>= 1; + } + /* Select the next ctxid comparator mask value */ + if (i == 3) + /* ctxid comparators[4-7] */ + mask = drvdata->ctxid_mask1; + else + mask >>= 0x8; + } + + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(ctxid_masks); + +static ssize_t vmid_idx_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->vmid_idx; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t vmid_idx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (val >= drvdata->numvmidc) + return -EINVAL; + + /* + * Use spinlock to ensure index doesn't change while it gets + * dereferenced multiple times within a spinlock block elsewhere. + */ + spin_lock(&drvdata->spinlock); + drvdata->vmid_idx = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(vmid_idx); + +static ssize_t vmid_val_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = (unsigned long)drvdata->vmid_val[drvdata->vmid_idx]; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t vmid_val_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + /* + * only implemented when vmid tracing is enabled, i.e. at least one + * vmid comparator is implemented and at least 8 bit vmid size + */ + if (!drvdata->vmid_size || !drvdata->numvmidc) + return -EINVAL; + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + drvdata->vmid_val[drvdata->vmid_idx] = (u64)val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(vmid_val); + +static ssize_t vmid_masks_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val1, val2; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + val1 = drvdata->vmid_mask0; + val2 = drvdata->vmid_mask1; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2); +} + +static ssize_t vmid_masks_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 i, j, maskbyte; + unsigned long val1, val2, mask; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + /* + * only implemented when vmid tracing is enabled, i.e. at least one + * vmid comparator is implemented and at least 8 bit vmid size + */ + if (!drvdata->vmid_size || !drvdata->numvmidc) + return -EINVAL; + if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + + /* + * each byte[0..3] controls mask value applied to vmid + * comparator[0..3] + */ + switch (drvdata->numvmidc) { + case 0x1: + /* COMP0, bits[7:0] */ + drvdata->vmid_mask0 = val1 & 0xFF; + break; + case 0x2: + /* COMP1, bits[15:8] */ + drvdata->vmid_mask0 = val1 & 0xFFFF; + break; + case 0x3: + /* COMP2, bits[23:16] */ + drvdata->vmid_mask0 = val1 & 0xFFFFFF; + break; + case 0x4: + /* COMP3, bits[31:24] */ + drvdata->vmid_mask0 = val1; + break; + case 0x5: + /* COMP4, bits[7:0] */ + drvdata->vmid_mask0 = val1; + drvdata->vmid_mask1 = val2 & 0xFF; + break; + case 0x6: + /* COMP5, bits[15:8] */ + drvdata->vmid_mask0 = val1; + drvdata->vmid_mask1 = val2 & 0xFFFF; + break; + case 0x7: + /* COMP6, bits[23:16] */ + drvdata->vmid_mask0 = val1; + drvdata->vmid_mask1 = val2 & 0xFFFFFF; + break; + case 0x8: + /* COMP7, bits[31:24] */ + drvdata->vmid_mask0 = val1; + drvdata->vmid_mask1 = val2; + break; + default: + break; + } + + /* + * If software sets a mask bit to 1, it must program relevant byte + * of vmid comparator value 0x0, otherwise behavior is unpredictable. + * For example, if bit[3] of vmid_mask0 is 1, we must clear bits[31:24] + * of vmid comparator0 value (corresponding to byte 0) register. + */ + mask = drvdata->vmid_mask0; + for (i = 0; i < drvdata->numvmidc; i++) { + /* mask value of corresponding vmid comparator */ + maskbyte = mask & ETMv4_EVENT_MASK; + /* + * each bit corresponds to a byte of respective vmid comparator + * value register + */ + for (j = 0; j < 8; j++) { + if (maskbyte & 1) + drvdata->vmid_val[i] &= ~(0xFF << (j * 8)); + maskbyte >>= 1; + } + /* Select the next vmid comparator mask value */ + if (i == 3) + /* vmid comparators[4-7] */ + mask = drvdata->vmid_mask1; + else + mask >>= 0x8; + } + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(vmid_masks); + +static ssize_t cpu_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->cpu; + return scnprintf(buf, PAGE_SIZE, "%d\n", val); + +} +static DEVICE_ATTR_RO(cpu); + +static struct attribute *coresight_etmv4_attrs[] = { + &dev_attr_nr_pe_cmp.attr, + &dev_attr_nr_addr_cmp.attr, + &dev_attr_nr_cntr.attr, + &dev_attr_nr_ext_inp.attr, + &dev_attr_numcidc.attr, + &dev_attr_numvmidc.attr, + &dev_attr_nrseqstate.attr, + &dev_attr_nr_resource.attr, + &dev_attr_nr_ss_cmp.attr, + &dev_attr_reset.attr, + &dev_attr_mode.attr, + &dev_attr_pe.attr, + &dev_attr_event.attr, + &dev_attr_event_instren.attr, + &dev_attr_event_ts.attr, + &dev_attr_syncfreq.attr, + &dev_attr_cyc_threshold.attr, + &dev_attr_bb_ctrl.attr, + &dev_attr_event_vinst.attr, + &dev_attr_s_exlevel_vinst.attr, + &dev_attr_ns_exlevel_vinst.attr, + &dev_attr_addr_idx.attr, + &dev_attr_addr_instdatatype.attr, + &dev_attr_addr_single.attr, + &dev_attr_addr_range.attr, + &dev_attr_addr_start.attr, + &dev_attr_addr_stop.attr, + &dev_attr_addr_ctxtype.attr, + &dev_attr_addr_context.attr, + &dev_attr_seq_idx.attr, + &dev_attr_seq_state.attr, + &dev_attr_seq_event.attr, + &dev_attr_seq_reset_event.attr, + &dev_attr_cntr_idx.attr, + &dev_attr_cntrldvr.attr, + &dev_attr_cntr_val.attr, + &dev_attr_cntr_ctrl.attr, + &dev_attr_res_idx.attr, + &dev_attr_res_ctrl.attr, + &dev_attr_ctxid_idx.attr, + &dev_attr_ctxid_pid.attr, + &dev_attr_ctxid_masks.attr, + &dev_attr_vmid_idx.attr, + &dev_attr_vmid_val.attr, + &dev_attr_vmid_masks.attr, + &dev_attr_cpu.attr, + NULL, +}; + +#define coresight_simple_func(name, offset) \ +static ssize_t name##_show(struct device *_dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct etmv4_drvdata *drvdata = dev_get_drvdata(_dev->parent); \ + return scnprintf(buf, PAGE_SIZE, "0x%x\n", \ + readl_relaxed(drvdata->base + offset)); \ +} \ +DEVICE_ATTR_RO(name) + +coresight_simple_func(trcoslsr, TRCOSLSR); +coresight_simple_func(trcpdcr, TRCPDCR); +coresight_simple_func(trcpdsr, TRCPDSR); +coresight_simple_func(trclsr, TRCLSR); +coresight_simple_func(trcauthstatus, TRCAUTHSTATUS); +coresight_simple_func(trcdevid, TRCDEVID); +coresight_simple_func(trcdevtype, TRCDEVTYPE); +coresight_simple_func(trcpidr0, TRCPIDR0); +coresight_simple_func(trcpidr1, TRCPIDR1); +coresight_simple_func(trcpidr2, TRCPIDR2); +coresight_simple_func(trcpidr3, TRCPIDR3); + +static struct attribute *coresight_etmv4_mgmt_attrs[] = { + &dev_attr_trcoslsr.attr, + &dev_attr_trcpdcr.attr, + &dev_attr_trcpdsr.attr, + &dev_attr_trclsr.attr, + &dev_attr_trcauthstatus.attr, + &dev_attr_trcdevid.attr, + &dev_attr_trcdevtype.attr, + &dev_attr_trcpidr0.attr, + &dev_attr_trcpidr1.attr, + &dev_attr_trcpidr2.attr, + &dev_attr_trcpidr3.attr, + NULL, +}; + +coresight_simple_func(trcidr0, TRCIDR0); +coresight_simple_func(trcidr1, TRCIDR1); +coresight_simple_func(trcidr2, TRCIDR2); +coresight_simple_func(trcidr3, TRCIDR3); +coresight_simple_func(trcidr4, TRCIDR4); +coresight_simple_func(trcidr5, TRCIDR5); +/* trcidr[6,7] are reserved */ +coresight_simple_func(trcidr8, TRCIDR8); +coresight_simple_func(trcidr9, TRCIDR9); +coresight_simple_func(trcidr10, TRCIDR10); +coresight_simple_func(trcidr11, TRCIDR11); +coresight_simple_func(trcidr12, TRCIDR12); +coresight_simple_func(trcidr13, TRCIDR13); + +static struct attribute *coresight_etmv4_trcidr_attrs[] = { + &dev_attr_trcidr0.attr, + &dev_attr_trcidr1.attr, + &dev_attr_trcidr2.attr, + &dev_attr_trcidr3.attr, + &dev_attr_trcidr4.attr, + &dev_attr_trcidr5.attr, + /* trcidr[6,7] are reserved */ + &dev_attr_trcidr8.attr, + &dev_attr_trcidr9.attr, + &dev_attr_trcidr10.attr, + &dev_attr_trcidr11.attr, + &dev_attr_trcidr12.attr, + &dev_attr_trcidr13.attr, + NULL, +}; + +static const struct attribute_group coresight_etmv4_group = { + .attrs = coresight_etmv4_attrs, +}; + +static const struct attribute_group coresight_etmv4_mgmt_group = { + .attrs = coresight_etmv4_mgmt_attrs, + .name = "mgmt", +}; + +static const struct attribute_group coresight_etmv4_trcidr_group = { + .attrs = coresight_etmv4_trcidr_attrs, + .name = "trcidr", +}; + +const struct attribute_group *coresight_etmv4_groups[] = { + &coresight_etmv4_group, + &coresight_etmv4_mgmt_group, + &coresight_etmv4_trcidr_group, + NULL, +}; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 1c59bd36834c..5cb919118d70 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -274,2035 +274,6 @@ static const struct coresight_ops etm4_cs_ops = { .source_ops = &etm4_source_ops, }; -static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude) -{ - u8 idx = drvdata->addr_idx; - - /* - * TRCACATRn.TYPE bit[1:0]: type of comparison - * the trace unit performs - */ - if (BMVAL(drvdata->addr_acc[idx], 0, 1) == ETM_INSTR_ADDR) { - if (idx % 2 != 0) - return -EINVAL; - - /* - * We are performing instruction address comparison. Set the - * relevant bit of ViewInst Include/Exclude Control register - * for corresponding address comparator pair. - */ - if (drvdata->addr_type[idx] != ETM_ADDR_TYPE_RANGE || - drvdata->addr_type[idx + 1] != ETM_ADDR_TYPE_RANGE) - return -EINVAL; - - if (exclude == true) { - /* - * Set exclude bit and unset the include bit - * corresponding to comparator pair - */ - drvdata->viiectlr |= BIT(idx / 2 + 16); - drvdata->viiectlr &= ~BIT(idx / 2); - } else { - /* - * Set include bit and unset exclude bit - * corresponding to comparator pair - */ - drvdata->viiectlr |= BIT(idx / 2); - drvdata->viiectlr &= ~BIT(idx / 2 + 16); - } - } - return 0; -} - -static ssize_t nr_pe_cmp_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->nr_pe_cmp; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} -static DEVICE_ATTR_RO(nr_pe_cmp); - -static ssize_t nr_addr_cmp_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->nr_addr_cmp; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} -static DEVICE_ATTR_RO(nr_addr_cmp); - -static ssize_t nr_cntr_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->nr_cntr; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} -static DEVICE_ATTR_RO(nr_cntr); - -static ssize_t nr_ext_inp_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->nr_ext_inp; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} -static DEVICE_ATTR_RO(nr_ext_inp); - -static ssize_t numcidc_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->numcidc; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} -static DEVICE_ATTR_RO(numcidc); - -static ssize_t numvmidc_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->numvmidc; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} -static DEVICE_ATTR_RO(numvmidc); - -static ssize_t nrseqstate_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->nrseqstate; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} -static DEVICE_ATTR_RO(nrseqstate); - -static ssize_t nr_resource_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->nr_resource; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} -static DEVICE_ATTR_RO(nr_resource); - -static ssize_t nr_ss_cmp_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->nr_ss_cmp; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} -static DEVICE_ATTR_RO(nr_ss_cmp); - -static ssize_t reset_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int i; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - if (val) - drvdata->mode = 0x0; - - /* Disable data tracing: do not trace load and store data transfers */ - drvdata->mode &= ~(ETM_MODE_LOAD | ETM_MODE_STORE); - drvdata->cfg &= ~(BIT(1) | BIT(2)); - - /* Disable data value and data address tracing */ - drvdata->mode &= ~(ETM_MODE_DATA_TRACE_ADDR | - ETM_MODE_DATA_TRACE_VAL); - drvdata->cfg &= ~(BIT(16) | BIT(17)); - - /* Disable all events tracing */ - drvdata->eventctrl0 = 0x0; - drvdata->eventctrl1 = 0x0; - - /* Disable timestamp event */ - drvdata->ts_ctrl = 0x0; - - /* Disable stalling */ - drvdata->stall_ctrl = 0x0; - - /* Reset trace synchronization period to 2^8 = 256 bytes*/ - if (drvdata->syncpr == false) - drvdata->syncfreq = 0x8; - - /* - * Enable ViewInst to trace everything with start-stop logic in - * started state. ARM recommends start-stop logic is set before - * each trace run. - */ - drvdata->vinst_ctrl |= BIT(0); - if (drvdata->nr_addr_cmp == true) { - drvdata->mode |= ETM_MODE_VIEWINST_STARTSTOP; - /* SSSTATUS, bit[9] */ - drvdata->vinst_ctrl |= BIT(9); - } - - /* No address range filtering for ViewInst */ - drvdata->viiectlr = 0x0; - - /* No start-stop filtering for ViewInst */ - drvdata->vissctlr = 0x0; - - /* Disable seq events */ - for (i = 0; i < drvdata->nrseqstate-1; i++) - drvdata->seq_ctrl[i] = 0x0; - drvdata->seq_rst = 0x0; - drvdata->seq_state = 0x0; - - /* Disable external input events */ - drvdata->ext_inp = 0x0; - - drvdata->cntr_idx = 0x0; - for (i = 0; i < drvdata->nr_cntr; i++) { - drvdata->cntrldvr[i] = 0x0; - drvdata->cntr_ctrl[i] = 0x0; - drvdata->cntr_val[i] = 0x0; - } - - /* Resource selector pair 0 is always implemented and reserved */ - drvdata->res_idx = 0x2; - for (i = 2; i < drvdata->nr_resource * 2; i++) - drvdata->res_ctrl[i] = 0x0; - - for (i = 0; i < drvdata->nr_ss_cmp; i++) { - drvdata->ss_ctrl[i] = 0x0; - drvdata->ss_pe_cmp[i] = 0x0; - } - - drvdata->addr_idx = 0x0; - for (i = 0; i < drvdata->nr_addr_cmp * 2; i++) { - drvdata->addr_val[i] = 0x0; - drvdata->addr_acc[i] = 0x0; - drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE; - } - - drvdata->ctxid_idx = 0x0; - for (i = 0; i < drvdata->numcidc; i++) { - drvdata->ctxid_pid[i] = 0x0; - drvdata->ctxid_vpid[i] = 0x0; - } - - drvdata->ctxid_mask0 = 0x0; - drvdata->ctxid_mask1 = 0x0; - - drvdata->vmid_idx = 0x0; - for (i = 0; i < drvdata->numvmidc; i++) - drvdata->vmid_val[i] = 0x0; - drvdata->vmid_mask0 = 0x0; - drvdata->vmid_mask1 = 0x0; - - drvdata->trcid = drvdata->cpu + 1; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_WO(reset); - -static ssize_t mode_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->mode; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t mode_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val, mode; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - drvdata->mode = val & ETMv4_MODE_ALL; - - if (drvdata->mode & ETM_MODE_EXCLUDE) - etm4_set_mode_exclude(drvdata, true); - else - etm4_set_mode_exclude(drvdata, false); - - if (drvdata->instrp0 == true) { - /* start by clearing instruction P0 field */ - drvdata->cfg &= ~(BIT(1) | BIT(2)); - if (drvdata->mode & ETM_MODE_LOAD) - /* 0b01 Trace load instructions as P0 instructions */ - drvdata->cfg |= BIT(1); - if (drvdata->mode & ETM_MODE_STORE) - /* 0b10 Trace store instructions as P0 instructions */ - drvdata->cfg |= BIT(2); - if (drvdata->mode & ETM_MODE_LOAD_STORE) - /* - * 0b11 Trace load and store instructions - * as P0 instructions - */ - drvdata->cfg |= BIT(1) | BIT(2); - } - - /* bit[3], Branch broadcast mode */ - if ((drvdata->mode & ETM_MODE_BB) && (drvdata->trcbb == true)) - drvdata->cfg |= BIT(3); - else - drvdata->cfg &= ~BIT(3); - - /* bit[4], Cycle counting instruction trace bit */ - if ((drvdata->mode & ETMv4_MODE_CYCACC) && - (drvdata->trccci == true)) - drvdata->cfg |= BIT(4); - else - drvdata->cfg &= ~BIT(4); - - /* bit[6], Context ID tracing bit */ - if ((drvdata->mode & ETMv4_MODE_CTXID) && (drvdata->ctxid_size)) - drvdata->cfg |= BIT(6); - else - drvdata->cfg &= ~BIT(6); - - if ((drvdata->mode & ETM_MODE_VMID) && (drvdata->vmid_size)) - drvdata->cfg |= BIT(7); - else - drvdata->cfg &= ~BIT(7); - - /* bits[10:8], Conditional instruction tracing bit */ - mode = ETM_MODE_COND(drvdata->mode); - if (drvdata->trccond == true) { - drvdata->cfg &= ~(BIT(8) | BIT(9) | BIT(10)); - drvdata->cfg |= mode << 8; - } - - /* bit[11], Global timestamp tracing bit */ - if ((drvdata->mode & ETMv4_MODE_TIMESTAMP) && (drvdata->ts_size)) - drvdata->cfg |= BIT(11); - else - drvdata->cfg &= ~BIT(11); - - /* bit[12], Return stack enable bit */ - if ((drvdata->mode & ETM_MODE_RETURNSTACK) && - (drvdata->retstack == true)) - drvdata->cfg |= BIT(12); - else - drvdata->cfg &= ~BIT(12); - - /* bits[14:13], Q element enable field */ - mode = ETM_MODE_QELEM(drvdata->mode); - /* start by clearing QE bits */ - drvdata->cfg &= ~(BIT(13) | BIT(14)); - /* if supported, Q elements with instruction counts are enabled */ - if ((mode & BIT(0)) && (drvdata->q_support & BIT(0))) - drvdata->cfg |= BIT(13); - /* - * if supported, Q elements with and without instruction - * counts are enabled - */ - if ((mode & BIT(1)) && (drvdata->q_support & BIT(1))) - drvdata->cfg |= BIT(14); - - /* bit[11], AMBA Trace Bus (ATB) trigger enable bit */ - if ((drvdata->mode & ETM_MODE_ATB_TRIGGER) && - (drvdata->atbtrig == true)) - drvdata->eventctrl1 |= BIT(11); - else - drvdata->eventctrl1 &= ~BIT(11); - - /* bit[12], Low-power state behavior override bit */ - if ((drvdata->mode & ETM_MODE_LPOVERRIDE) && - (drvdata->lpoverride == true)) - drvdata->eventctrl1 |= BIT(12); - else - drvdata->eventctrl1 &= ~BIT(12); - - /* bit[8], Instruction stall bit */ - if (drvdata->mode & ETM_MODE_ISTALL_EN) - drvdata->stall_ctrl |= BIT(8); - else - drvdata->stall_ctrl &= ~BIT(8); - - /* bit[10], Prioritize instruction trace bit */ - if (drvdata->mode & ETM_MODE_INSTPRIO) - drvdata->stall_ctrl |= BIT(10); - else - drvdata->stall_ctrl &= ~BIT(10); - - /* bit[13], Trace overflow prevention bit */ - if ((drvdata->mode & ETM_MODE_NOOVERFLOW) && - (drvdata->nooverflow == true)) - drvdata->stall_ctrl |= BIT(13); - else - drvdata->stall_ctrl &= ~BIT(13); - - /* bit[9] Start/stop logic control bit */ - if (drvdata->mode & ETM_MODE_VIEWINST_STARTSTOP) - drvdata->vinst_ctrl |= BIT(9); - else - drvdata->vinst_ctrl &= ~BIT(9); - - /* bit[10], Whether a trace unit must trace a Reset exception */ - if (drvdata->mode & ETM_MODE_TRACE_RESET) - drvdata->vinst_ctrl |= BIT(10); - else - drvdata->vinst_ctrl &= ~BIT(10); - - /* bit[11], Whether a trace unit must trace a system error exception */ - if ((drvdata->mode & ETM_MODE_TRACE_ERR) && - (drvdata->trc_error == true)) - drvdata->vinst_ctrl |= BIT(11); - else - drvdata->vinst_ctrl &= ~BIT(11); - - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(mode); - -static ssize_t pe_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->pe_sel; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t pe_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - if (val > drvdata->nr_pe) { - spin_unlock(&drvdata->spinlock); - return -EINVAL; - } - - drvdata->pe_sel = val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(pe); - -static ssize_t event_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->eventctrl0; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - switch (drvdata->nr_event) { - case 0x0: - /* EVENT0, bits[7:0] */ - drvdata->eventctrl0 = val & 0xFF; - break; - case 0x1: - /* EVENT1, bits[15:8] */ - drvdata->eventctrl0 = val & 0xFFFF; - break; - case 0x2: - /* EVENT2, bits[23:16] */ - drvdata->eventctrl0 = val & 0xFFFFFF; - break; - case 0x3: - /* EVENT3, bits[31:24] */ - drvdata->eventctrl0 = val; - break; - default: - break; - } - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(event); - -static ssize_t event_instren_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = BMVAL(drvdata->eventctrl1, 0, 3); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t event_instren_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - /* start by clearing all instruction event enable bits */ - drvdata->eventctrl1 &= ~(BIT(0) | BIT(1) | BIT(2) | BIT(3)); - switch (drvdata->nr_event) { - case 0x0: - /* generate Event element for event 1 */ - drvdata->eventctrl1 |= val & BIT(1); - break; - case 0x1: - /* generate Event element for event 1 and 2 */ - drvdata->eventctrl1 |= val & (BIT(0) | BIT(1)); - break; - case 0x2: - /* generate Event element for event 1, 2 and 3 */ - drvdata->eventctrl1 |= val & (BIT(0) | BIT(1) | BIT(2)); - break; - case 0x3: - /* generate Event element for all 4 events */ - drvdata->eventctrl1 |= val & 0xF; - break; - default: - break; - } - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(event_instren); - -static ssize_t event_ts_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->ts_ctrl; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t event_ts_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (!drvdata->ts_size) - return -EINVAL; - - drvdata->ts_ctrl = val & ETMv4_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(event_ts); - -static ssize_t syncfreq_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->syncfreq; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t syncfreq_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (drvdata->syncpr == true) - return -EINVAL; - - drvdata->syncfreq = val & ETMv4_SYNC_MASK; - return size; -} -static DEVICE_ATTR_RW(syncfreq); - -static ssize_t cyc_threshold_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->ccctlr; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t cyc_threshold_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (val < drvdata->ccitmin) - return -EINVAL; - - drvdata->ccctlr = val & ETM_CYC_THRESHOLD_MASK; - return size; -} -static DEVICE_ATTR_RW(cyc_threshold); - -static ssize_t bb_ctrl_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->bb_ctrl; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t bb_ctrl_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (drvdata->trcbb == false) - return -EINVAL; - if (!drvdata->nr_addr_cmp) - return -EINVAL; - /* - * Bit[7:0] selects which address range comparator is used for - * branch broadcast control. - */ - if (BMVAL(val, 0, 7) > drvdata->nr_addr_cmp) - return -EINVAL; - - drvdata->bb_ctrl = val; - return size; -} -static DEVICE_ATTR_RW(bb_ctrl); - -static ssize_t event_vinst_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->vinst_ctrl & ETMv4_EVENT_MASK; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t event_vinst_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - val &= ETMv4_EVENT_MASK; - drvdata->vinst_ctrl &= ~ETMv4_EVENT_MASK; - drvdata->vinst_ctrl |= val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(event_vinst); - -static ssize_t s_exlevel_vinst_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = BMVAL(drvdata->vinst_ctrl, 16, 19); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t s_exlevel_vinst_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - /* clear all EXLEVEL_S bits (bit[18] is never implemented) */ - drvdata->vinst_ctrl &= ~(BIT(16) | BIT(17) | BIT(19)); - /* enable instruction tracing for corresponding exception level */ - val &= drvdata->s_ex_level; - drvdata->vinst_ctrl |= (val << 16); - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(s_exlevel_vinst); - -static ssize_t ns_exlevel_vinst_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - /* EXLEVEL_NS, bits[23:20] */ - val = BMVAL(drvdata->vinst_ctrl, 20, 23); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t ns_exlevel_vinst_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - /* clear EXLEVEL_NS bits (bit[23] is never implemented */ - drvdata->vinst_ctrl &= ~(BIT(20) | BIT(21) | BIT(22)); - /* enable instruction tracing for corresponding exception level */ - val &= drvdata->ns_ex_level; - drvdata->vinst_ctrl |= (val << 20); - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(ns_exlevel_vinst); - -static ssize_t addr_idx_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->addr_idx; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t addr_idx_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (val >= drvdata->nr_addr_cmp * 2) - return -EINVAL; - - /* - * Use spinlock to ensure index doesn't change while it gets - * dereferenced multiple times within a spinlock block elsewhere. - */ - spin_lock(&drvdata->spinlock); - drvdata->addr_idx = val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(addr_idx); - -static ssize_t addr_instdatatype_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - ssize_t len; - u8 val, idx; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - val = BMVAL(drvdata->addr_acc[idx], 0, 1); - len = scnprintf(buf, PAGE_SIZE, "%s\n", - val == ETM_INSTR_ADDR ? "instr" : - (val == ETM_DATA_LOAD_ADDR ? "data_load" : - (val == ETM_DATA_STORE_ADDR ? "data_store" : - "data_load_store"))); - spin_unlock(&drvdata->spinlock); - return len; -} - -static ssize_t addr_instdatatype_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - char str[20] = ""; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (strlen(buf) >= 20) - return -EINVAL; - if (sscanf(buf, "%s", str) != 1) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!strcmp(str, "instr")) - /* TYPE, bits[1:0] */ - drvdata->addr_acc[idx] &= ~(BIT(0) | BIT(1)); - - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(addr_instdatatype); - -static ssize_t addr_single_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - idx = drvdata->addr_idx; - spin_lock(&drvdata->spinlock); - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - val = (unsigned long)drvdata->addr_val[idx]; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t addr_single_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - drvdata->addr_val[idx] = (u64)val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(addr_single); - -static ssize_t addr_range_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 idx; - unsigned long val1, val2; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (idx % 2 != 0) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || - (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - val1 = (unsigned long)drvdata->addr_val[idx]; - val2 = (unsigned long)drvdata->addr_val[idx + 1]; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2); -} - -static ssize_t addr_range_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long val1, val2; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) - return -EINVAL; - /* lower address comparator cannot have a higher address value */ - if (val1 > val2) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (idx % 2 != 0) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || - (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - drvdata->addr_val[idx] = (u64)val1; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE; - drvdata->addr_val[idx + 1] = (u64)val2; - drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE; - /* - * Program include or exclude control bits for vinst or vdata - * whenever we change addr comparators to ETM_ADDR_TYPE_RANGE - */ - if (drvdata->mode & ETM_MODE_EXCLUDE) - etm4_set_mode_exclude(drvdata, true); - else - etm4_set_mode_exclude(drvdata, false); - - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(addr_range); - -static ssize_t addr_start_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - val = (unsigned long)drvdata->addr_val[idx]; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t addr_start_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!drvdata->nr_addr_cmp) { - spin_unlock(&drvdata->spinlock); - return -EINVAL; - } - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - drvdata->addr_val[idx] = (u64)val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_START; - drvdata->vissctlr |= BIT(idx); - /* SSSTATUS, bit[9] - turn on start/stop logic */ - drvdata->vinst_ctrl |= BIT(9); - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(addr_start); - -static ssize_t addr_stop_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - val = (unsigned long)drvdata->addr_val[idx]; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t addr_stop_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!drvdata->nr_addr_cmp) { - spin_unlock(&drvdata->spinlock); - return -EINVAL; - } - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - drvdata->addr_val[idx] = (u64)val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP; - drvdata->vissctlr |= BIT(idx + 16); - /* SSSTATUS, bit[9] - turn on start/stop logic */ - drvdata->vinst_ctrl |= BIT(9); - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(addr_stop); - -static ssize_t addr_ctxtype_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - ssize_t len; - u8 idx, val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - /* CONTEXTTYPE, bits[3:2] */ - val = BMVAL(drvdata->addr_acc[idx], 2, 3); - len = scnprintf(buf, PAGE_SIZE, "%s\n", val == ETM_CTX_NONE ? "none" : - (val == ETM_CTX_CTXID ? "ctxid" : - (val == ETM_CTX_VMID ? "vmid" : "all"))); - spin_unlock(&drvdata->spinlock); - return len; -} - -static ssize_t addr_ctxtype_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - char str[10] = ""; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (strlen(buf) >= 10) - return -EINVAL; - if (sscanf(buf, "%s", str) != 1) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!strcmp(str, "none")) - /* start by clearing context type bits */ - drvdata->addr_acc[idx] &= ~(BIT(2) | BIT(3)); - else if (!strcmp(str, "ctxid")) { - /* 0b01 The trace unit performs a Context ID */ - if (drvdata->numcidc) { - drvdata->addr_acc[idx] |= BIT(2); - drvdata->addr_acc[idx] &= ~BIT(3); - } - } else if (!strcmp(str, "vmid")) { - /* 0b10 The trace unit performs a VMID */ - if (drvdata->numvmidc) { - drvdata->addr_acc[idx] &= ~BIT(2); - drvdata->addr_acc[idx] |= BIT(3); - } - } else if (!strcmp(str, "all")) { - /* - * 0b11 The trace unit performs a Context ID - * comparison and a VMID - */ - if (drvdata->numcidc) - drvdata->addr_acc[idx] |= BIT(2); - if (drvdata->numvmidc) - drvdata->addr_acc[idx] |= BIT(3); - } - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(addr_ctxtype); - -static ssize_t addr_context_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - /* context ID comparator bits[6:4] */ - val = BMVAL(drvdata->addr_acc[idx], 4, 6); - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t addr_context_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if ((drvdata->numcidc <= 1) && (drvdata->numvmidc <= 1)) - return -EINVAL; - if (val >= (drvdata->numcidc >= drvdata->numvmidc ? - drvdata->numcidc : drvdata->numvmidc)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - /* clear context ID comparator bits[6:4] */ - drvdata->addr_acc[idx] &= ~(BIT(4) | BIT(5) | BIT(6)); - drvdata->addr_acc[idx] |= (val << 4); - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(addr_context); - -static ssize_t seq_idx_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_idx; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t seq_idx_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (val >= drvdata->nrseqstate - 1) - return -EINVAL; - - /* - * Use spinlock to ensure index doesn't change while it gets - * dereferenced multiple times within a spinlock block elsewhere. - */ - spin_lock(&drvdata->spinlock); - drvdata->seq_idx = val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(seq_idx); - -static ssize_t seq_state_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_state; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t seq_state_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (val >= drvdata->nrseqstate) - return -EINVAL; - - drvdata->seq_state = val; - return size; -} -static DEVICE_ATTR_RW(seq_state); - -static ssize_t seq_event_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->seq_idx; - val = drvdata->seq_ctrl[idx]; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t seq_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->seq_idx; - /* RST, bits[7:0] */ - drvdata->seq_ctrl[idx] = val & 0xFF; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(seq_event); - -static ssize_t seq_reset_event_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_rst; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t seq_reset_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (!(drvdata->nrseqstate)) - return -EINVAL; - - drvdata->seq_rst = val & ETMv4_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(seq_reset_event); - -static ssize_t cntr_idx_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->cntr_idx; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t cntr_idx_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (val >= drvdata->nr_cntr) - return -EINVAL; - - /* - * Use spinlock to ensure index doesn't change while it gets - * dereferenced multiple times within a spinlock block elsewhere. - */ - spin_lock(&drvdata->spinlock); - drvdata->cntr_idx = val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(cntr_idx); - -static ssize_t cntrldvr_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - val = drvdata->cntrldvr[idx]; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t cntrldvr_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (val > ETM_CNTR_MAX_VAL) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - drvdata->cntrldvr[idx] = val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(cntrldvr); - -static ssize_t cntr_val_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - val = drvdata->cntr_val[idx]; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t cntr_val_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (val > ETM_CNTR_MAX_VAL) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - drvdata->cntr_val[idx] = val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(cntr_val); - -static ssize_t cntr_ctrl_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - val = drvdata->cntr_ctrl[idx]; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t cntr_ctrl_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - drvdata->cntr_ctrl[idx] = val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(cntr_ctrl); - -static ssize_t res_idx_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->res_idx; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t res_idx_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - /* Resource selector pair 0 is always implemented and reserved */ - if (val < 2 || val >= drvdata->nr_resource * 2) - return -EINVAL; - - /* - * Use spinlock to ensure index doesn't change while it gets - * dereferenced multiple times within a spinlock block elsewhere. - */ - spin_lock(&drvdata->spinlock); - drvdata->res_idx = val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(res_idx); - -static ssize_t res_ctrl_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->res_idx; - val = drvdata->res_ctrl[idx]; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t res_ctrl_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->res_idx; - /* For odd idx pair inversal bit is RES0 */ - if (idx % 2 != 0) - /* PAIRINV, bit[21] */ - val &= ~BIT(21); - drvdata->res_ctrl[idx] = val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(res_ctrl); - -static ssize_t ctxid_idx_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->ctxid_idx; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t ctxid_idx_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (val >= drvdata->numcidc) - return -EINVAL; - - /* - * Use spinlock to ensure index doesn't change while it gets - * dereferenced multiple times within a spinlock block elsewhere. - */ - spin_lock(&drvdata->spinlock); - drvdata->ctxid_idx = val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(ctxid_idx); - -static ssize_t ctxid_pid_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->ctxid_idx; - val = (unsigned long)drvdata->ctxid_vpid[idx]; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t ctxid_pid_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long vpid, pid; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - /* - * only implemented when ctxid tracing is enabled, i.e. at least one - * ctxid comparator is implemented and ctxid is greater than 0 bits - * in length - */ - if (!drvdata->ctxid_size || !drvdata->numcidc) - return -EINVAL; - if (kstrtoul(buf, 16, &vpid)) - return -EINVAL; - - pid = coresight_vpid_to_pid(vpid); - - spin_lock(&drvdata->spinlock); - idx = drvdata->ctxid_idx; - drvdata->ctxid_pid[idx] = (u64)pid; - drvdata->ctxid_vpid[idx] = (u64)vpid; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(ctxid_pid); - -static ssize_t ctxid_masks_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val1, val2; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - val1 = drvdata->ctxid_mask0; - val2 = drvdata->ctxid_mask1; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2); -} - -static ssize_t ctxid_masks_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 i, j, maskbyte; - unsigned long val1, val2, mask; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - /* - * only implemented when ctxid tracing is enabled, i.e. at least one - * ctxid comparator is implemented and ctxid is greater than 0 bits - * in length - */ - if (!drvdata->ctxid_size || !drvdata->numcidc) - return -EINVAL; - if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - /* - * each byte[0..3] controls mask value applied to ctxid - * comparator[0..3] - */ - switch (drvdata->numcidc) { - case 0x1: - /* COMP0, bits[7:0] */ - drvdata->ctxid_mask0 = val1 & 0xFF; - break; - case 0x2: - /* COMP1, bits[15:8] */ - drvdata->ctxid_mask0 = val1 & 0xFFFF; - break; - case 0x3: - /* COMP2, bits[23:16] */ - drvdata->ctxid_mask0 = val1 & 0xFFFFFF; - break; - case 0x4: - /* COMP3, bits[31:24] */ - drvdata->ctxid_mask0 = val1; - break; - case 0x5: - /* COMP4, bits[7:0] */ - drvdata->ctxid_mask0 = val1; - drvdata->ctxid_mask1 = val2 & 0xFF; - break; - case 0x6: - /* COMP5, bits[15:8] */ - drvdata->ctxid_mask0 = val1; - drvdata->ctxid_mask1 = val2 & 0xFFFF; - break; - case 0x7: - /* COMP6, bits[23:16] */ - drvdata->ctxid_mask0 = val1; - drvdata->ctxid_mask1 = val2 & 0xFFFFFF; - break; - case 0x8: - /* COMP7, bits[31:24] */ - drvdata->ctxid_mask0 = val1; - drvdata->ctxid_mask1 = val2; - break; - default: - break; - } - /* - * If software sets a mask bit to 1, it must program relevant byte - * of ctxid comparator value 0x0, otherwise behavior is unpredictable. - * For example, if bit[3] of ctxid_mask0 is 1, we must clear bits[31:24] - * of ctxid comparator0 value (corresponding to byte 0) register. - */ - mask = drvdata->ctxid_mask0; - for (i = 0; i < drvdata->numcidc; i++) { - /* mask value of corresponding ctxid comparator */ - maskbyte = mask & ETMv4_EVENT_MASK; - /* - * each bit corresponds to a byte of respective ctxid comparator - * value register - */ - for (j = 0; j < 8; j++) { - if (maskbyte & 1) - drvdata->ctxid_pid[i] &= ~(0xFF << (j * 8)); - maskbyte >>= 1; - } - /* Select the next ctxid comparator mask value */ - if (i == 3) - /* ctxid comparators[4-7] */ - mask = drvdata->ctxid_mask1; - else - mask >>= 0x8; - } - - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(ctxid_masks); - -static ssize_t vmid_idx_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->vmid_idx; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t vmid_idx_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (val >= drvdata->numvmidc) - return -EINVAL; - - /* - * Use spinlock to ensure index doesn't change while it gets - * dereferenced multiple times within a spinlock block elsewhere. - */ - spin_lock(&drvdata->spinlock); - drvdata->vmid_idx = val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(vmid_idx); - -static ssize_t vmid_val_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = (unsigned long)drvdata->vmid_val[drvdata->vmid_idx]; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t vmid_val_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - /* - * only implemented when vmid tracing is enabled, i.e. at least one - * vmid comparator is implemented and at least 8 bit vmid size - */ - if (!drvdata->vmid_size || !drvdata->numvmidc) - return -EINVAL; - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - drvdata->vmid_val[drvdata->vmid_idx] = (u64)val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(vmid_val); - -static ssize_t vmid_masks_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val1, val2; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - val1 = drvdata->vmid_mask0; - val2 = drvdata->vmid_mask1; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2); -} - -static ssize_t vmid_masks_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 i, j, maskbyte; - unsigned long val1, val2, mask; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - /* - * only implemented when vmid tracing is enabled, i.e. at least one - * vmid comparator is implemented and at least 8 bit vmid size - */ - if (!drvdata->vmid_size || !drvdata->numvmidc) - return -EINVAL; - if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - - /* - * each byte[0..3] controls mask value applied to vmid - * comparator[0..3] - */ - switch (drvdata->numvmidc) { - case 0x1: - /* COMP0, bits[7:0] */ - drvdata->vmid_mask0 = val1 & 0xFF; - break; - case 0x2: - /* COMP1, bits[15:8] */ - drvdata->vmid_mask0 = val1 & 0xFFFF; - break; - case 0x3: - /* COMP2, bits[23:16] */ - drvdata->vmid_mask0 = val1 & 0xFFFFFF; - break; - case 0x4: - /* COMP3, bits[31:24] */ - drvdata->vmid_mask0 = val1; - break; - case 0x5: - /* COMP4, bits[7:0] */ - drvdata->vmid_mask0 = val1; - drvdata->vmid_mask1 = val2 & 0xFF; - break; - case 0x6: - /* COMP5, bits[15:8] */ - drvdata->vmid_mask0 = val1; - drvdata->vmid_mask1 = val2 & 0xFFFF; - break; - case 0x7: - /* COMP6, bits[23:16] */ - drvdata->vmid_mask0 = val1; - drvdata->vmid_mask1 = val2 & 0xFFFFFF; - break; - case 0x8: - /* COMP7, bits[31:24] */ - drvdata->vmid_mask0 = val1; - drvdata->vmid_mask1 = val2; - break; - default: - break; - } - - /* - * If software sets a mask bit to 1, it must program relevant byte - * of vmid comparator value 0x0, otherwise behavior is unpredictable. - * For example, if bit[3] of vmid_mask0 is 1, we must clear bits[31:24] - * of vmid comparator0 value (corresponding to byte 0) register. - */ - mask = drvdata->vmid_mask0; - for (i = 0; i < drvdata->numvmidc; i++) { - /* mask value of corresponding vmid comparator */ - maskbyte = mask & ETMv4_EVENT_MASK; - /* - * each bit corresponds to a byte of respective vmid comparator - * value register - */ - for (j = 0; j < 8; j++) { - if (maskbyte & 1) - drvdata->vmid_val[i] &= ~(0xFF << (j * 8)); - maskbyte >>= 1; - } - /* Select the next vmid comparator mask value */ - if (i == 3) - /* vmid comparators[4-7] */ - mask = drvdata->vmid_mask1; - else - mask >>= 0x8; - } - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(vmid_masks); - -static ssize_t cpu_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->cpu; - return scnprintf(buf, PAGE_SIZE, "%d\n", val); - -} -static DEVICE_ATTR_RO(cpu); - -static struct attribute *coresight_etmv4_attrs[] = { - &dev_attr_nr_pe_cmp.attr, - &dev_attr_nr_addr_cmp.attr, - &dev_attr_nr_cntr.attr, - &dev_attr_nr_ext_inp.attr, - &dev_attr_numcidc.attr, - &dev_attr_numvmidc.attr, - &dev_attr_nrseqstate.attr, - &dev_attr_nr_resource.attr, - &dev_attr_nr_ss_cmp.attr, - &dev_attr_reset.attr, - &dev_attr_mode.attr, - &dev_attr_pe.attr, - &dev_attr_event.attr, - &dev_attr_event_instren.attr, - &dev_attr_event_ts.attr, - &dev_attr_syncfreq.attr, - &dev_attr_cyc_threshold.attr, - &dev_attr_bb_ctrl.attr, - &dev_attr_event_vinst.attr, - &dev_attr_s_exlevel_vinst.attr, - &dev_attr_ns_exlevel_vinst.attr, - &dev_attr_addr_idx.attr, - &dev_attr_addr_instdatatype.attr, - &dev_attr_addr_single.attr, - &dev_attr_addr_range.attr, - &dev_attr_addr_start.attr, - &dev_attr_addr_stop.attr, - &dev_attr_addr_ctxtype.attr, - &dev_attr_addr_context.attr, - &dev_attr_seq_idx.attr, - &dev_attr_seq_state.attr, - &dev_attr_seq_event.attr, - &dev_attr_seq_reset_event.attr, - &dev_attr_cntr_idx.attr, - &dev_attr_cntrldvr.attr, - &dev_attr_cntr_val.attr, - &dev_attr_cntr_ctrl.attr, - &dev_attr_res_idx.attr, - &dev_attr_res_ctrl.attr, - &dev_attr_ctxid_idx.attr, - &dev_attr_ctxid_pid.attr, - &dev_attr_ctxid_masks.attr, - &dev_attr_vmid_idx.attr, - &dev_attr_vmid_val.attr, - &dev_attr_vmid_masks.attr, - &dev_attr_cpu.attr, - NULL, -}; - -#define coresight_simple_func(name, offset) \ -static ssize_t name##_show(struct device *_dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct etmv4_drvdata *drvdata = dev_get_drvdata(_dev->parent); \ - return scnprintf(buf, PAGE_SIZE, "0x%x\n", \ - readl_relaxed(drvdata->base + offset)); \ -} \ -static DEVICE_ATTR_RO(name) - -coresight_simple_func(trcoslsr, TRCOSLSR); -coresight_simple_func(trcpdcr, TRCPDCR); -coresight_simple_func(trcpdsr, TRCPDSR); -coresight_simple_func(trclsr, TRCLSR); -coresight_simple_func(trcauthstatus, TRCAUTHSTATUS); -coresight_simple_func(trcdevid, TRCDEVID); -coresight_simple_func(trcdevtype, TRCDEVTYPE); -coresight_simple_func(trcpidr0, TRCPIDR0); -coresight_simple_func(trcpidr1, TRCPIDR1); -coresight_simple_func(trcpidr2, TRCPIDR2); -coresight_simple_func(trcpidr3, TRCPIDR3); - -static struct attribute *coresight_etmv4_mgmt_attrs[] = { - &dev_attr_trcoslsr.attr, - &dev_attr_trcpdcr.attr, - &dev_attr_trcpdsr.attr, - &dev_attr_trclsr.attr, - &dev_attr_trcauthstatus.attr, - &dev_attr_trcdevid.attr, - &dev_attr_trcdevtype.attr, - &dev_attr_trcpidr0.attr, - &dev_attr_trcpidr1.attr, - &dev_attr_trcpidr2.attr, - &dev_attr_trcpidr3.attr, - NULL, -}; - -coresight_simple_func(trcidr0, TRCIDR0); -coresight_simple_func(trcidr1, TRCIDR1); -coresight_simple_func(trcidr2, TRCIDR2); -coresight_simple_func(trcidr3, TRCIDR3); -coresight_simple_func(trcidr4, TRCIDR4); -coresight_simple_func(trcidr5, TRCIDR5); -/* trcidr[6,7] are reserved */ -coresight_simple_func(trcidr8, TRCIDR8); -coresight_simple_func(trcidr9, TRCIDR9); -coresight_simple_func(trcidr10, TRCIDR10); -coresight_simple_func(trcidr11, TRCIDR11); -coresight_simple_func(trcidr12, TRCIDR12); -coresight_simple_func(trcidr13, TRCIDR13); - -static struct attribute *coresight_etmv4_trcidr_attrs[] = { - &dev_attr_trcidr0.attr, - &dev_attr_trcidr1.attr, - &dev_attr_trcidr2.attr, - &dev_attr_trcidr3.attr, - &dev_attr_trcidr4.attr, - &dev_attr_trcidr5.attr, - /* trcidr[6,7] are reserved */ - &dev_attr_trcidr8.attr, - &dev_attr_trcidr9.attr, - &dev_attr_trcidr10.attr, - &dev_attr_trcidr11.attr, - &dev_attr_trcidr12.attr, - &dev_attr_trcidr13.attr, - NULL, -}; - -static const struct attribute_group coresight_etmv4_group = { - .attrs = coresight_etmv4_attrs, -}; - -static const struct attribute_group coresight_etmv4_mgmt_group = { - .attrs = coresight_etmv4_mgmt_attrs, - .name = "mgmt", -}; - -static const struct attribute_group coresight_etmv4_trcidr_group = { - .attrs = coresight_etmv4_trcidr_attrs, - .name = "trcidr", -}; - -static const struct attribute_group *coresight_etmv4_groups[] = { - &coresight_etmv4_group, - &coresight_etmv4_mgmt_group, - &coresight_etmv4_trcidr_group, - NULL, -}; - static void etm4_init_arch_data(void *info) { u32 etmidr0; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index c34100205ca9..e6ff9aae2a7e 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -391,4 +391,6 @@ enum etm_addr_type { ETM_ADDR_TYPE_START, ETM_ADDR_TYPE_STOP, }; + +extern const struct attribute_group *coresight_etmv4_groups[]; #endif From 2b888e472c42df49c54da0ef0ec2a3bd4e158c9b Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 5 Apr 2016 11:53:43 -0600 Subject: [PATCH 070/104] coresight: etm4x: adding config and traceid registers Adding new sysFS management interface to query the configuration and the traceid registers. Both are required to convey information to the perf cmd line tools when using ETMv4 tracers as PMU. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 7c38aa4b03b3fc6ce17e5a00327f8c0be18daf8a) --- .../ABI/testing/sysfs-bus-coresight-devices-etm4x | 13 +++++++++++++ drivers/hwtracing/coresight/coresight-etm4x-sysfs.c | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm4x b/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm4x index 2355ed8ae31f..36258bc1b473 100644 --- a/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm4x +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm4x @@ -359,6 +359,19 @@ Contact: Mathieu Poirier Description: (R) Print the content of the Peripheral ID3 Register (0xFEC). The value is taken directly from the HW. +What: /sys/bus/coresight/devices/.etm/mgmt/trcconfig +Date: February 2016 +KernelVersion: 4.07 +Contact: Mathieu Poirier +Description: (R) Print the content of the trace configuration register + (0x010) as currently set by SW. + +What: /sys/bus/coresight/devices/.etm/mgmt/trctraceid +Date: February 2016 +KernelVersion: 4.07 +Contact: Mathieu Poirier +Description: (R) Print the content of the trace ID register (0x040). + What: /sys/bus/coresight/devices/.etm/trcidr/trcidr0 Date: April 2015 KernelVersion: 4.01 diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index 5db0de7d4e0e..39a8b077e0c2 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -1972,6 +1972,8 @@ coresight_simple_func(trcoslsr, TRCOSLSR); coresight_simple_func(trcpdcr, TRCPDCR); coresight_simple_func(trcpdsr, TRCPDSR); coresight_simple_func(trclsr, TRCLSR); +coresight_simple_func(trcconfig, TRCCONFIGR); +coresight_simple_func(trctraceid, TRCTRACEIDR); coresight_simple_func(trcauthstatus, TRCAUTHSTATUS); coresight_simple_func(trcdevid, TRCDEVID); coresight_simple_func(trcdevtype, TRCDEVTYPE); @@ -1985,6 +1987,8 @@ static struct attribute *coresight_etmv4_mgmt_attrs[] = { &dev_attr_trcpdcr.attr, &dev_attr_trcpdsr.attr, &dev_attr_trclsr.attr, + &dev_attr_trcconfig.attr, + &dev_attr_trctraceid.attr, &dev_attr_trcauthstatus.attr, &dev_attr_trcdevid.attr, &dev_attr_trcdevtype.attr, From 640cc6adfefeeea9a2f66d16bff1bb2f547acbc6 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 5 Apr 2016 11:53:44 -0600 Subject: [PATCH 071/104] coresight: etm4x: splitting struct etmv4_drvdata Similar to what was done on etm3x, splitting driver structure etmv4_drvdata in two. One half is concerned with the HW characteristics that are generally static in nature. The other half deals with user configuration and will change from one trace session to another. No gain/loss of functionality is incurred from this patch. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 54ff892b76c68ea3fa0ba53a0cdc4508b35aee6f) --- .../coresight/coresight-etm4x-sysfs.c | 642 ++++++++++-------- drivers/hwtracing/coresight/coresight-etm4x.c | 134 ++-- drivers/hwtracing/coresight/coresight-etm4x.h | 202 +++--- 3 files changed, 533 insertions(+), 445 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index 39a8b077e0c2..a996db7ef2fc 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -21,13 +21,16 @@ static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude) { - u8 idx = drvdata->addr_idx; + u8 idx; + struct etmv4_config *config = &drvdata->config; + + idx = config->addr_idx; /* * TRCACATRn.TYPE bit[1:0]: type of comparison * the trace unit performs */ - if (BMVAL(drvdata->addr_acc[idx], 0, 1) == ETM_INSTR_ADDR) { + if (BMVAL(config->addr_acc[idx], 0, 1) == ETM_INSTR_ADDR) { if (idx % 2 != 0) return -EINVAL; @@ -36,8 +39,8 @@ static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude) * relevant bit of ViewInst Include/Exclude Control register * for corresponding address comparator pair. */ - if (drvdata->addr_type[idx] != ETM_ADDR_TYPE_RANGE || - drvdata->addr_type[idx + 1] != ETM_ADDR_TYPE_RANGE) + if (config->addr_type[idx] != ETM_ADDR_TYPE_RANGE || + config->addr_type[idx + 1] != ETM_ADDR_TYPE_RANGE) return -EINVAL; if (exclude == true) { @@ -45,15 +48,15 @@ static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude) * Set exclude bit and unset the include bit * corresponding to comparator pair */ - drvdata->viiectlr |= BIT(idx / 2 + 16); - drvdata->viiectlr &= ~BIT(idx / 2); + config->viiectlr |= BIT(idx / 2 + 16); + config->viiectlr &= ~BIT(idx / 2); } else { /* * Set include bit and unset exclude bit * corresponding to comparator pair */ - drvdata->viiectlr |= BIT(idx / 2); - drvdata->viiectlr &= ~BIT(idx / 2 + 16); + config->viiectlr |= BIT(idx / 2); + config->viiectlr &= ~BIT(idx / 2 + 16); } } return 0; @@ -174,104 +177,107 @@ static ssize_t reset_store(struct device *dev, int i; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); if (val) - drvdata->mode = 0x0; + config->mode = 0x0; /* Disable data tracing: do not trace load and store data transfers */ - drvdata->mode &= ~(ETM_MODE_LOAD | ETM_MODE_STORE); - drvdata->cfg &= ~(BIT(1) | BIT(2)); + config->mode &= ~(ETM_MODE_LOAD | ETM_MODE_STORE); + config->cfg &= ~(BIT(1) | BIT(2)); /* Disable data value and data address tracing */ - drvdata->mode &= ~(ETM_MODE_DATA_TRACE_ADDR | + config->mode &= ~(ETM_MODE_DATA_TRACE_ADDR | ETM_MODE_DATA_TRACE_VAL); - drvdata->cfg &= ~(BIT(16) | BIT(17)); + config->cfg &= ~(BIT(16) | BIT(17)); /* Disable all events tracing */ - drvdata->eventctrl0 = 0x0; - drvdata->eventctrl1 = 0x0; + config->eventctrl0 = 0x0; + config->eventctrl1 = 0x0; /* Disable timestamp event */ - drvdata->ts_ctrl = 0x0; + config->ts_ctrl = 0x0; /* Disable stalling */ - drvdata->stall_ctrl = 0x0; + config->stall_ctrl = 0x0; /* Reset trace synchronization period to 2^8 = 256 bytes*/ if (drvdata->syncpr == false) - drvdata->syncfreq = 0x8; + config->syncfreq = 0x8; /* * Enable ViewInst to trace everything with start-stop logic in * started state. ARM recommends start-stop logic is set before * each trace run. */ - drvdata->vinst_ctrl |= BIT(0); + config->vinst_ctrl |= BIT(0); if (drvdata->nr_addr_cmp == true) { - drvdata->mode |= ETM_MODE_VIEWINST_STARTSTOP; + config->mode |= ETM_MODE_VIEWINST_STARTSTOP; /* SSSTATUS, bit[9] */ - drvdata->vinst_ctrl |= BIT(9); + config->vinst_ctrl |= BIT(9); } /* No address range filtering for ViewInst */ - drvdata->viiectlr = 0x0; + config->viiectlr = 0x0; /* No start-stop filtering for ViewInst */ - drvdata->vissctlr = 0x0; + config->vissctlr = 0x0; /* Disable seq events */ for (i = 0; i < drvdata->nrseqstate-1; i++) - drvdata->seq_ctrl[i] = 0x0; - drvdata->seq_rst = 0x0; - drvdata->seq_state = 0x0; + config->seq_ctrl[i] = 0x0; + config->seq_rst = 0x0; + config->seq_state = 0x0; /* Disable external input events */ - drvdata->ext_inp = 0x0; + config->ext_inp = 0x0; - drvdata->cntr_idx = 0x0; + config->cntr_idx = 0x0; for (i = 0; i < drvdata->nr_cntr; i++) { - drvdata->cntrldvr[i] = 0x0; - drvdata->cntr_ctrl[i] = 0x0; - drvdata->cntr_val[i] = 0x0; + config->cntrldvr[i] = 0x0; + config->cntr_ctrl[i] = 0x0; + config->cntr_val[i] = 0x0; } - drvdata->res_idx = 0x0; + config->res_idx = 0x0; for (i = 0; i < drvdata->nr_resource; i++) - drvdata->res_ctrl[i] = 0x0; + config->res_ctrl[i] = 0x0; for (i = 0; i < drvdata->nr_ss_cmp; i++) { - drvdata->ss_ctrl[i] = 0x0; - drvdata->ss_pe_cmp[i] = 0x0; + config->ss_ctrl[i] = 0x0; + config->ss_pe_cmp[i] = 0x0; } - drvdata->addr_idx = 0x0; + config->addr_idx = 0x0; for (i = 0; i < drvdata->nr_addr_cmp * 2; i++) { - drvdata->addr_val[i] = 0x0; - drvdata->addr_acc[i] = 0x0; - drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE; + config->addr_val[i] = 0x0; + config->addr_acc[i] = 0x0; + config->addr_type[i] = ETM_ADDR_TYPE_NONE; } - drvdata->ctxid_idx = 0x0; + config->ctxid_idx = 0x0; for (i = 0; i < drvdata->numcidc; i++) { - drvdata->ctxid_pid[i] = 0x0; - drvdata->ctxid_vpid[i] = 0x0; + config->ctxid_pid[i] = 0x0; + config->ctxid_vpid[i] = 0x0; } - drvdata->ctxid_mask0 = 0x0; - drvdata->ctxid_mask1 = 0x0; + config->ctxid_mask0 = 0x0; + config->ctxid_mask1 = 0x0; - drvdata->vmid_idx = 0x0; + config->vmid_idx = 0x0; for (i = 0; i < drvdata->numvmidc; i++) - drvdata->vmid_val[i] = 0x0; - drvdata->vmid_mask0 = 0x0; - drvdata->vmid_mask1 = 0x0; + config->vmid_val[i] = 0x0; + config->vmid_mask0 = 0x0; + config->vmid_mask1 = 0x0; drvdata->trcid = drvdata->cpu + 1; + spin_unlock(&drvdata->spinlock); + return size; } static DEVICE_ATTR_WO(reset); @@ -282,8 +288,9 @@ static ssize_t mode_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->mode; + val = config->mode; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -293,146 +300,148 @@ static ssize_t mode_store(struct device *dev, { unsigned long val, mode; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); - drvdata->mode = val & ETMv4_MODE_ALL; + config->mode = val & ETMv4_MODE_ALL; - if (drvdata->mode & ETM_MODE_EXCLUDE) + if (config->mode & ETM_MODE_EXCLUDE) etm4_set_mode_exclude(drvdata, true); else etm4_set_mode_exclude(drvdata, false); if (drvdata->instrp0 == true) { /* start by clearing instruction P0 field */ - drvdata->cfg &= ~(BIT(1) | BIT(2)); - if (drvdata->mode & ETM_MODE_LOAD) + config->cfg &= ~(BIT(1) | BIT(2)); + if (config->mode & ETM_MODE_LOAD) /* 0b01 Trace load instructions as P0 instructions */ - drvdata->cfg |= BIT(1); - if (drvdata->mode & ETM_MODE_STORE) + config->cfg |= BIT(1); + if (config->mode & ETM_MODE_STORE) /* 0b10 Trace store instructions as P0 instructions */ - drvdata->cfg |= BIT(2); - if (drvdata->mode & ETM_MODE_LOAD_STORE) + config->cfg |= BIT(2); + if (config->mode & ETM_MODE_LOAD_STORE) /* * 0b11 Trace load and store instructions * as P0 instructions */ - drvdata->cfg |= BIT(1) | BIT(2); + config->cfg |= BIT(1) | BIT(2); } /* bit[3], Branch broadcast mode */ - if ((drvdata->mode & ETM_MODE_BB) && (drvdata->trcbb == true)) - drvdata->cfg |= BIT(3); + if ((config->mode & ETM_MODE_BB) && (drvdata->trcbb == true)) + config->cfg |= BIT(3); else - drvdata->cfg &= ~BIT(3); + config->cfg &= ~BIT(3); /* bit[4], Cycle counting instruction trace bit */ - if ((drvdata->mode & ETMv4_MODE_CYCACC) && + if ((config->mode & ETMv4_MODE_CYCACC) && (drvdata->trccci == true)) - drvdata->cfg |= BIT(4); + config->cfg |= BIT(4); else - drvdata->cfg &= ~BIT(4); + config->cfg &= ~BIT(4); /* bit[6], Context ID tracing bit */ - if ((drvdata->mode & ETMv4_MODE_CTXID) && (drvdata->ctxid_size)) - drvdata->cfg |= BIT(6); + if ((config->mode & ETMv4_MODE_CTXID) && (drvdata->ctxid_size)) + config->cfg |= BIT(6); else - drvdata->cfg &= ~BIT(6); + config->cfg &= ~BIT(6); - if ((drvdata->mode & ETM_MODE_VMID) && (drvdata->vmid_size)) - drvdata->cfg |= BIT(7); + if ((config->mode & ETM_MODE_VMID) && (drvdata->vmid_size)) + config->cfg |= BIT(7); else - drvdata->cfg &= ~BIT(7); + config->cfg &= ~BIT(7); /* bits[10:8], Conditional instruction tracing bit */ - mode = ETM_MODE_COND(drvdata->mode); + mode = ETM_MODE_COND(config->mode); if (drvdata->trccond == true) { - drvdata->cfg &= ~(BIT(8) | BIT(9) | BIT(10)); - drvdata->cfg |= mode << 8; + config->cfg &= ~(BIT(8) | BIT(9) | BIT(10)); + config->cfg |= mode << 8; } /* bit[11], Global timestamp tracing bit */ - if ((drvdata->mode & ETMv4_MODE_TIMESTAMP) && (drvdata->ts_size)) - drvdata->cfg |= BIT(11); + if ((config->mode & ETMv4_MODE_TIMESTAMP) && (drvdata->ts_size)) + config->cfg |= BIT(11); else - drvdata->cfg &= ~BIT(11); + config->cfg &= ~BIT(11); /* bit[12], Return stack enable bit */ - if ((drvdata->mode & ETM_MODE_RETURNSTACK) && - (drvdata->retstack == true)) - drvdata->cfg |= BIT(12); + if ((config->mode & ETM_MODE_RETURNSTACK) && + (drvdata->retstack == true)) + config->cfg |= BIT(12); else - drvdata->cfg &= ~BIT(12); + config->cfg &= ~BIT(12); /* bits[14:13], Q element enable field */ - mode = ETM_MODE_QELEM(drvdata->mode); + mode = ETM_MODE_QELEM(config->mode); /* start by clearing QE bits */ - drvdata->cfg &= ~(BIT(13) | BIT(14)); + config->cfg &= ~(BIT(13) | BIT(14)); /* if supported, Q elements with instruction counts are enabled */ if ((mode & BIT(0)) && (drvdata->q_support & BIT(0))) - drvdata->cfg |= BIT(13); + config->cfg |= BIT(13); /* * if supported, Q elements with and without instruction * counts are enabled */ if ((mode & BIT(1)) && (drvdata->q_support & BIT(1))) - drvdata->cfg |= BIT(14); + config->cfg |= BIT(14); /* bit[11], AMBA Trace Bus (ATB) trigger enable bit */ - if ((drvdata->mode & ETM_MODE_ATB_TRIGGER) && + if ((config->mode & ETM_MODE_ATB_TRIGGER) && (drvdata->atbtrig == true)) - drvdata->eventctrl1 |= BIT(11); + config->eventctrl1 |= BIT(11); else - drvdata->eventctrl1 &= ~BIT(11); + config->eventctrl1 &= ~BIT(11); /* bit[12], Low-power state behavior override bit */ - if ((drvdata->mode & ETM_MODE_LPOVERRIDE) && + if ((config->mode & ETM_MODE_LPOVERRIDE) && (drvdata->lpoverride == true)) - drvdata->eventctrl1 |= BIT(12); + config->eventctrl1 |= BIT(12); else - drvdata->eventctrl1 &= ~BIT(12); + config->eventctrl1 &= ~BIT(12); /* bit[8], Instruction stall bit */ - if (drvdata->mode & ETM_MODE_ISTALL_EN) - drvdata->stall_ctrl |= BIT(8); + if (config->mode & ETM_MODE_ISTALL_EN) + config->stall_ctrl |= BIT(8); else - drvdata->stall_ctrl &= ~BIT(8); + config->stall_ctrl &= ~BIT(8); /* bit[10], Prioritize instruction trace bit */ - if (drvdata->mode & ETM_MODE_INSTPRIO) - drvdata->stall_ctrl |= BIT(10); + if (config->mode & ETM_MODE_INSTPRIO) + config->stall_ctrl |= BIT(10); else - drvdata->stall_ctrl &= ~BIT(10); + config->stall_ctrl &= ~BIT(10); /* bit[13], Trace overflow prevention bit */ - if ((drvdata->mode & ETM_MODE_NOOVERFLOW) && + if ((config->mode & ETM_MODE_NOOVERFLOW) && (drvdata->nooverflow == true)) - drvdata->stall_ctrl |= BIT(13); + config->stall_ctrl |= BIT(13); else - drvdata->stall_ctrl &= ~BIT(13); + config->stall_ctrl &= ~BIT(13); /* bit[9] Start/stop logic control bit */ - if (drvdata->mode & ETM_MODE_VIEWINST_STARTSTOP) - drvdata->vinst_ctrl |= BIT(9); + if (config->mode & ETM_MODE_VIEWINST_STARTSTOP) + config->vinst_ctrl |= BIT(9); else - drvdata->vinst_ctrl &= ~BIT(9); + config->vinst_ctrl &= ~BIT(9); /* bit[10], Whether a trace unit must trace a Reset exception */ - if (drvdata->mode & ETM_MODE_TRACE_RESET) - drvdata->vinst_ctrl |= BIT(10); + if (config->mode & ETM_MODE_TRACE_RESET) + config->vinst_ctrl |= BIT(10); else - drvdata->vinst_ctrl &= ~BIT(10); + config->vinst_ctrl &= ~BIT(10); /* bit[11], Whether a trace unit must trace a system error exception */ - if ((drvdata->mode & ETM_MODE_TRACE_ERR) && + if ((config->mode & ETM_MODE_TRACE_ERR) && (drvdata->trc_error == true)) - drvdata->vinst_ctrl |= BIT(11); + config->vinst_ctrl |= BIT(11); else - drvdata->vinst_ctrl &= ~BIT(11); + config->vinst_ctrl &= ~BIT(11); spin_unlock(&drvdata->spinlock); + return size; } static DEVICE_ATTR_RW(mode); @@ -443,8 +452,9 @@ static ssize_t pe_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->pe_sel; + val = config->pe_sel; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -454,6 +464,7 @@ static ssize_t pe_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -464,7 +475,7 @@ static ssize_t pe_store(struct device *dev, return -EINVAL; } - drvdata->pe_sel = val; + config->pe_sel = val; spin_unlock(&drvdata->spinlock); return size; } @@ -476,8 +487,9 @@ static ssize_t event_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->eventctrl0; + val = config->eventctrl0; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -487,6 +499,7 @@ static ssize_t event_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -495,19 +508,19 @@ static ssize_t event_store(struct device *dev, switch (drvdata->nr_event) { case 0x0: /* EVENT0, bits[7:0] */ - drvdata->eventctrl0 = val & 0xFF; + config->eventctrl0 = val & 0xFF; break; case 0x1: /* EVENT1, bits[15:8] */ - drvdata->eventctrl0 = val & 0xFFFF; + config->eventctrl0 = val & 0xFFFF; break; case 0x2: /* EVENT2, bits[23:16] */ - drvdata->eventctrl0 = val & 0xFFFFFF; + config->eventctrl0 = val & 0xFFFFFF; break; case 0x3: /* EVENT3, bits[31:24] */ - drvdata->eventctrl0 = val; + config->eventctrl0 = val; break; default: break; @@ -523,8 +536,9 @@ static ssize_t event_instren_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = BMVAL(drvdata->eventctrl1, 0, 3); + val = BMVAL(config->eventctrl1, 0, 3); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -534,29 +548,30 @@ static ssize_t event_instren_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); /* start by clearing all instruction event enable bits */ - drvdata->eventctrl1 &= ~(BIT(0) | BIT(1) | BIT(2) | BIT(3)); + config->eventctrl1 &= ~(BIT(0) | BIT(1) | BIT(2) | BIT(3)); switch (drvdata->nr_event) { case 0x0: /* generate Event element for event 1 */ - drvdata->eventctrl1 |= val & BIT(1); + config->eventctrl1 |= val & BIT(1); break; case 0x1: /* generate Event element for event 1 and 2 */ - drvdata->eventctrl1 |= val & (BIT(0) | BIT(1)); + config->eventctrl1 |= val & (BIT(0) | BIT(1)); break; case 0x2: /* generate Event element for event 1, 2 and 3 */ - drvdata->eventctrl1 |= val & (BIT(0) | BIT(1) | BIT(2)); + config->eventctrl1 |= val & (BIT(0) | BIT(1) | BIT(2)); break; case 0x3: /* generate Event element for all 4 events */ - drvdata->eventctrl1 |= val & 0xF; + config->eventctrl1 |= val & 0xF; break; default: break; @@ -572,8 +587,9 @@ static ssize_t event_ts_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->ts_ctrl; + val = config->ts_ctrl; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -583,13 +599,14 @@ static ssize_t event_ts_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; if (!drvdata->ts_size) return -EINVAL; - drvdata->ts_ctrl = val & ETMv4_EVENT_MASK; + config->ts_ctrl = val & ETMv4_EVENT_MASK; return size; } static DEVICE_ATTR_RW(event_ts); @@ -600,8 +617,9 @@ static ssize_t syncfreq_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->syncfreq; + val = config->syncfreq; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -611,13 +629,14 @@ static ssize_t syncfreq_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; if (drvdata->syncpr == true) return -EINVAL; - drvdata->syncfreq = val & ETMv4_SYNC_MASK; + config->syncfreq = val & ETMv4_SYNC_MASK; return size; } static DEVICE_ATTR_RW(syncfreq); @@ -628,8 +647,9 @@ static ssize_t cyc_threshold_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->ccctlr; + val = config->ccctlr; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -639,13 +659,14 @@ static ssize_t cyc_threshold_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; if (val < drvdata->ccitmin) return -EINVAL; - drvdata->ccctlr = val & ETM_CYC_THRESHOLD_MASK; + config->ccctlr = val & ETM_CYC_THRESHOLD_MASK; return size; } static DEVICE_ATTR_RW(cyc_threshold); @@ -656,8 +677,9 @@ static ssize_t bb_ctrl_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->bb_ctrl; + val = config->bb_ctrl; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -667,6 +689,7 @@ static ssize_t bb_ctrl_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -681,7 +704,7 @@ static ssize_t bb_ctrl_store(struct device *dev, if (BMVAL(val, 0, 7) > drvdata->nr_addr_cmp) return -EINVAL; - drvdata->bb_ctrl = val; + config->bb_ctrl = val; return size; } static DEVICE_ATTR_RW(bb_ctrl); @@ -692,8 +715,9 @@ static ssize_t event_vinst_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->vinst_ctrl & ETMv4_EVENT_MASK; + val = config->vinst_ctrl & ETMv4_EVENT_MASK; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -703,14 +727,15 @@ static ssize_t event_vinst_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); val &= ETMv4_EVENT_MASK; - drvdata->vinst_ctrl &= ~ETMv4_EVENT_MASK; - drvdata->vinst_ctrl |= val; + config->vinst_ctrl &= ~ETMv4_EVENT_MASK; + config->vinst_ctrl |= val; spin_unlock(&drvdata->spinlock); return size; } @@ -722,8 +747,9 @@ static ssize_t s_exlevel_vinst_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = BMVAL(drvdata->vinst_ctrl, 16, 19); + val = BMVAL(config->vinst_ctrl, 16, 19); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -733,16 +759,17 @@ static ssize_t s_exlevel_vinst_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); /* clear all EXLEVEL_S bits (bit[18] is never implemented) */ - drvdata->vinst_ctrl &= ~(BIT(16) | BIT(17) | BIT(19)); + config->vinst_ctrl &= ~(BIT(16) | BIT(17) | BIT(19)); /* enable instruction tracing for corresponding exception level */ val &= drvdata->s_ex_level; - drvdata->vinst_ctrl |= (val << 16); + config->vinst_ctrl |= (val << 16); spin_unlock(&drvdata->spinlock); return size; } @@ -754,9 +781,10 @@ static ssize_t ns_exlevel_vinst_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; /* EXLEVEL_NS, bits[23:20] */ - val = BMVAL(drvdata->vinst_ctrl, 20, 23); + val = BMVAL(config->vinst_ctrl, 20, 23); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -766,16 +794,17 @@ static ssize_t ns_exlevel_vinst_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); /* clear EXLEVEL_NS bits (bit[23] is never implemented */ - drvdata->vinst_ctrl &= ~(BIT(20) | BIT(21) | BIT(22)); + config->vinst_ctrl &= ~(BIT(20) | BIT(21) | BIT(22)); /* enable instruction tracing for corresponding exception level */ val &= drvdata->ns_ex_level; - drvdata->vinst_ctrl |= (val << 20); + config->vinst_ctrl |= (val << 20); spin_unlock(&drvdata->spinlock); return size; } @@ -787,8 +816,9 @@ static ssize_t addr_idx_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->addr_idx; + val = config->addr_idx; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -798,6 +828,7 @@ static ssize_t addr_idx_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -809,7 +840,7 @@ static ssize_t addr_idx_store(struct device *dev, * dereferenced multiple times within a spinlock block elsewhere. */ spin_lock(&drvdata->spinlock); - drvdata->addr_idx = val; + config->addr_idx = val; spin_unlock(&drvdata->spinlock); return size; } @@ -822,10 +853,11 @@ static ssize_t addr_instdatatype_show(struct device *dev, ssize_t len; u8 val, idx; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - val = BMVAL(drvdata->addr_acc[idx], 0, 1); + idx = config->addr_idx; + val = BMVAL(config->addr_acc[idx], 0, 1); len = scnprintf(buf, PAGE_SIZE, "%s\n", val == ETM_INSTR_ADDR ? "instr" : (val == ETM_DATA_LOAD_ADDR ? "data_load" : @@ -842,6 +874,7 @@ static ssize_t addr_instdatatype_store(struct device *dev, u8 idx; char str[20] = ""; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (strlen(buf) >= 20) return -EINVAL; @@ -849,10 +882,10 @@ static ssize_t addr_instdatatype_store(struct device *dev, return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; if (!strcmp(str, "instr")) /* TYPE, bits[1:0] */ - drvdata->addr_acc[idx] &= ~(BIT(0) | BIT(1)); + config->addr_acc[idx] &= ~(BIT(0) | BIT(1)); spin_unlock(&drvdata->spinlock); return size; @@ -866,15 +899,16 @@ static ssize_t addr_single_show(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - idx = drvdata->addr_idx; + idx = config->addr_idx; spin_lock(&drvdata->spinlock); - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - val = (unsigned long)drvdata->addr_val[idx]; + val = (unsigned long)config->addr_val[idx]; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -886,20 +920,21 @@ static ssize_t addr_single_store(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { + idx = config->addr_idx; + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - drvdata->addr_val[idx] = (u64)val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE; + config->addr_val[idx] = (u64)val; + config->addr_type[idx] = ETM_ADDR_TYPE_SINGLE; spin_unlock(&drvdata->spinlock); return size; } @@ -912,23 +947,24 @@ static ssize_t addr_range_show(struct device *dev, u8 idx; unsigned long val1, val2; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; if (idx % 2 != 0) { spin_unlock(&drvdata->spinlock); return -EPERM; } - if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || - (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { + if (!((config->addr_type[idx] == ETM_ADDR_TYPE_NONE && + config->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || + (config->addr_type[idx] == ETM_ADDR_TYPE_RANGE && + config->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { spin_unlock(&drvdata->spinlock); return -EPERM; } - val1 = (unsigned long)drvdata->addr_val[idx]; - val2 = (unsigned long)drvdata->addr_val[idx + 1]; + val1 = (unsigned long)config->addr_val[idx]; + val2 = (unsigned long)config->addr_val[idx + 1]; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2); } @@ -940,6 +976,7 @@ static ssize_t addr_range_store(struct device *dev, u8 idx; unsigned long val1, val2; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) return -EINVAL; @@ -948,29 +985,29 @@ static ssize_t addr_range_store(struct device *dev, return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; if (idx % 2 != 0) { spin_unlock(&drvdata->spinlock); return -EPERM; } - if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || - (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { + if (!((config->addr_type[idx] == ETM_ADDR_TYPE_NONE && + config->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || + (config->addr_type[idx] == ETM_ADDR_TYPE_RANGE && + config->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { spin_unlock(&drvdata->spinlock); return -EPERM; } - drvdata->addr_val[idx] = (u64)val1; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE; - drvdata->addr_val[idx + 1] = (u64)val2; - drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE; + config->addr_val[idx] = (u64)val1; + config->addr_type[idx] = ETM_ADDR_TYPE_RANGE; + config->addr_val[idx + 1] = (u64)val2; + config->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE; /* * Program include or exclude control bits for vinst or vdata * whenever we change addr comparators to ETM_ADDR_TYPE_RANGE */ - if (drvdata->mode & ETM_MODE_EXCLUDE) + if (config->mode & ETM_MODE_EXCLUDE) etm4_set_mode_exclude(drvdata, true); else etm4_set_mode_exclude(drvdata, false); @@ -987,17 +1024,18 @@ static ssize_t addr_start_show(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_START)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - val = (unsigned long)drvdata->addr_val[idx]; + val = (unsigned long)config->addr_val[idx]; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1009,27 +1047,28 @@ static ssize_t addr_start_store(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; if (!drvdata->nr_addr_cmp) { spin_unlock(&drvdata->spinlock); return -EINVAL; } - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_START)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - drvdata->addr_val[idx] = (u64)val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_START; - drvdata->vissctlr |= BIT(idx); + config->addr_val[idx] = (u64)val; + config->addr_type[idx] = ETM_ADDR_TYPE_START; + config->vissctlr |= BIT(idx); /* SSSTATUS, bit[9] - turn on start/stop logic */ - drvdata->vinst_ctrl |= BIT(9); + config->vinst_ctrl |= BIT(9); spin_unlock(&drvdata->spinlock); return size; } @@ -1042,17 +1081,18 @@ static ssize_t addr_stop_show(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - val = (unsigned long)drvdata->addr_val[idx]; + val = (unsigned long)config->addr_val[idx]; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1064,27 +1104,28 @@ static ssize_t addr_stop_store(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; if (!drvdata->nr_addr_cmp) { spin_unlock(&drvdata->spinlock); return -EINVAL; } - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - drvdata->addr_val[idx] = (u64)val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP; - drvdata->vissctlr |= BIT(idx + 16); + config->addr_val[idx] = (u64)val; + config->addr_type[idx] = ETM_ADDR_TYPE_STOP; + config->vissctlr |= BIT(idx + 16); /* SSSTATUS, bit[9] - turn on start/stop logic */ - drvdata->vinst_ctrl |= BIT(9); + config->vinst_ctrl |= BIT(9); spin_unlock(&drvdata->spinlock); return size; } @@ -1097,11 +1138,12 @@ static ssize_t addr_ctxtype_show(struct device *dev, ssize_t len; u8 idx, val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; /* CONTEXTTYPE, bits[3:2] */ - val = BMVAL(drvdata->addr_acc[idx], 2, 3); + val = BMVAL(config->addr_acc[idx], 2, 3); len = scnprintf(buf, PAGE_SIZE, "%s\n", val == ETM_CTX_NONE ? "none" : (val == ETM_CTX_CTXID ? "ctxid" : (val == ETM_CTX_VMID ? "vmid" : "all"))); @@ -1116,6 +1158,7 @@ static ssize_t addr_ctxtype_store(struct device *dev, u8 idx; char str[10] = ""; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (strlen(buf) >= 10) return -EINVAL; @@ -1123,21 +1166,21 @@ static ssize_t addr_ctxtype_store(struct device *dev, return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; if (!strcmp(str, "none")) /* start by clearing context type bits */ - drvdata->addr_acc[idx] &= ~(BIT(2) | BIT(3)); + config->addr_acc[idx] &= ~(BIT(2) | BIT(3)); else if (!strcmp(str, "ctxid")) { /* 0b01 The trace unit performs a Context ID */ if (drvdata->numcidc) { - drvdata->addr_acc[idx] |= BIT(2); - drvdata->addr_acc[idx] &= ~BIT(3); + config->addr_acc[idx] |= BIT(2); + config->addr_acc[idx] &= ~BIT(3); } } else if (!strcmp(str, "vmid")) { /* 0b10 The trace unit performs a VMID */ if (drvdata->numvmidc) { - drvdata->addr_acc[idx] &= ~BIT(2); - drvdata->addr_acc[idx] |= BIT(3); + config->addr_acc[idx] &= ~BIT(2); + config->addr_acc[idx] |= BIT(3); } } else if (!strcmp(str, "all")) { /* @@ -1145,9 +1188,9 @@ static ssize_t addr_ctxtype_store(struct device *dev, * comparison and a VMID */ if (drvdata->numcidc) - drvdata->addr_acc[idx] |= BIT(2); + config->addr_acc[idx] |= BIT(2); if (drvdata->numvmidc) - drvdata->addr_acc[idx] |= BIT(3); + config->addr_acc[idx] |= BIT(3); } spin_unlock(&drvdata->spinlock); return size; @@ -1161,11 +1204,12 @@ static ssize_t addr_context_show(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; /* context ID comparator bits[6:4] */ - val = BMVAL(drvdata->addr_acc[idx], 4, 6); + val = BMVAL(config->addr_acc[idx], 4, 6); spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1177,6 +1221,7 @@ static ssize_t addr_context_store(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -1187,10 +1232,10 @@ static ssize_t addr_context_store(struct device *dev, return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; /* clear context ID comparator bits[6:4] */ - drvdata->addr_acc[idx] &= ~(BIT(4) | BIT(5) | BIT(6)); - drvdata->addr_acc[idx] |= (val << 4); + config->addr_acc[idx] &= ~(BIT(4) | BIT(5) | BIT(6)); + config->addr_acc[idx] |= (val << 4); spin_unlock(&drvdata->spinlock); return size; } @@ -1202,8 +1247,9 @@ static ssize_t seq_idx_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->seq_idx; + val = config->seq_idx; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1213,6 +1259,7 @@ static ssize_t seq_idx_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -1224,7 +1271,7 @@ static ssize_t seq_idx_store(struct device *dev, * dereferenced multiple times within a spinlock block elsewhere. */ spin_lock(&drvdata->spinlock); - drvdata->seq_idx = val; + config->seq_idx = val; spin_unlock(&drvdata->spinlock); return size; } @@ -1236,8 +1283,9 @@ static ssize_t seq_state_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->seq_state; + val = config->seq_state; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1247,13 +1295,14 @@ static ssize_t seq_state_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; if (val >= drvdata->nrseqstate) return -EINVAL; - drvdata->seq_state = val; + config->seq_state = val; return size; } static DEVICE_ATTR_RW(seq_state); @@ -1265,10 +1314,11 @@ static ssize_t seq_event_show(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->seq_idx; - val = drvdata->seq_ctrl[idx]; + idx = config->seq_idx; + val = config->seq_ctrl[idx]; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1280,14 +1330,15 @@ static ssize_t seq_event_store(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->seq_idx; + idx = config->seq_idx; /* RST, bits[7:0] */ - drvdata->seq_ctrl[idx] = val & 0xFF; + config->seq_ctrl[idx] = val & 0xFF; spin_unlock(&drvdata->spinlock); return size; } @@ -1299,8 +1350,9 @@ static ssize_t seq_reset_event_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->seq_rst; + val = config->seq_rst; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1310,13 +1362,14 @@ static ssize_t seq_reset_event_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; if (!(drvdata->nrseqstate)) return -EINVAL; - drvdata->seq_rst = val & ETMv4_EVENT_MASK; + config->seq_rst = val & ETMv4_EVENT_MASK; return size; } static DEVICE_ATTR_RW(seq_reset_event); @@ -1327,8 +1380,9 @@ static ssize_t cntr_idx_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->cntr_idx; + val = config->cntr_idx; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1338,6 +1392,7 @@ static ssize_t cntr_idx_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -1349,7 +1404,7 @@ static ssize_t cntr_idx_store(struct device *dev, * dereferenced multiple times within a spinlock block elsewhere. */ spin_lock(&drvdata->spinlock); - drvdata->cntr_idx = val; + config->cntr_idx = val; spin_unlock(&drvdata->spinlock); return size; } @@ -1362,10 +1417,11 @@ static ssize_t cntrldvr_show(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - val = drvdata->cntrldvr[idx]; + idx = config->cntr_idx; + val = config->cntrldvr[idx]; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1377,6 +1433,7 @@ static ssize_t cntrldvr_store(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -1384,8 +1441,8 @@ static ssize_t cntrldvr_store(struct device *dev, return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - drvdata->cntrldvr[idx] = val; + idx = config->cntr_idx; + config->cntrldvr[idx] = val; spin_unlock(&drvdata->spinlock); return size; } @@ -1398,10 +1455,11 @@ static ssize_t cntr_val_show(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - val = drvdata->cntr_val[idx]; + idx = config->cntr_idx; + val = config->cntr_val[idx]; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1413,6 +1471,7 @@ static ssize_t cntr_val_store(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -1420,8 +1479,8 @@ static ssize_t cntr_val_store(struct device *dev, return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - drvdata->cntr_val[idx] = val; + idx = config->cntr_idx; + config->cntr_val[idx] = val; spin_unlock(&drvdata->spinlock); return size; } @@ -1434,10 +1493,11 @@ static ssize_t cntr_ctrl_show(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - val = drvdata->cntr_ctrl[idx]; + idx = config->cntr_idx; + val = config->cntr_ctrl[idx]; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1449,13 +1509,14 @@ static ssize_t cntr_ctrl_store(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - drvdata->cntr_ctrl[idx] = val; + idx = config->cntr_idx; + config->cntr_ctrl[idx] = val; spin_unlock(&drvdata->spinlock); return size; } @@ -1467,8 +1528,9 @@ static ssize_t res_idx_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->res_idx; + val = config->res_idx; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1478,6 +1540,7 @@ static ssize_t res_idx_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -1490,7 +1553,7 @@ static ssize_t res_idx_store(struct device *dev, * dereferenced multiple times within a spinlock block elsewhere. */ spin_lock(&drvdata->spinlock); - drvdata->res_idx = val; + config->res_idx = val; spin_unlock(&drvdata->spinlock); return size; } @@ -1503,10 +1566,11 @@ static ssize_t res_ctrl_show(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->res_idx; - val = drvdata->res_ctrl[idx]; + idx = config->res_idx; + val = config->res_ctrl[idx]; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1518,17 +1582,18 @@ static ssize_t res_ctrl_store(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->res_idx; + idx = config->res_idx; /* For odd idx pair inversal bit is RES0 */ if (idx % 2 != 0) /* PAIRINV, bit[21] */ val &= ~BIT(21); - drvdata->res_ctrl[idx] = val; + config->res_ctrl[idx] = val; spin_unlock(&drvdata->spinlock); return size; } @@ -1540,8 +1605,9 @@ static ssize_t ctxid_idx_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->ctxid_idx; + val = config->ctxid_idx; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1551,6 +1617,7 @@ static ssize_t ctxid_idx_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -1562,7 +1629,7 @@ static ssize_t ctxid_idx_store(struct device *dev, * dereferenced multiple times within a spinlock block elsewhere. */ spin_lock(&drvdata->spinlock); - drvdata->ctxid_idx = val; + config->ctxid_idx = val; spin_unlock(&drvdata->spinlock); return size; } @@ -1575,10 +1642,11 @@ static ssize_t ctxid_pid_show(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->ctxid_idx; - val = (unsigned long)drvdata->ctxid_vpid[idx]; + idx = config->ctxid_idx; + val = (unsigned long)config->ctxid_vpid[idx]; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1590,6 +1658,7 @@ static ssize_t ctxid_pid_store(struct device *dev, u8 idx; unsigned long vpid, pid; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; /* * only implemented when ctxid tracing is enabled, i.e. at least one @@ -1604,9 +1673,9 @@ static ssize_t ctxid_pid_store(struct device *dev, pid = coresight_vpid_to_pid(vpid); spin_lock(&drvdata->spinlock); - idx = drvdata->ctxid_idx; - drvdata->ctxid_pid[idx] = (u64)pid; - drvdata->ctxid_vpid[idx] = (u64)vpid; + idx = config->ctxid_idx; + config->ctxid_pid[idx] = (u64)pid; + config->ctxid_vpid[idx] = (u64)vpid; spin_unlock(&drvdata->spinlock); return size; } @@ -1618,10 +1687,11 @@ static ssize_t ctxid_masks_show(struct device *dev, { unsigned long val1, val2; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - val1 = drvdata->ctxid_mask0; - val2 = drvdata->ctxid_mask1; + val1 = config->ctxid_mask0; + val2 = config->ctxid_mask1; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2); } @@ -1633,6 +1703,7 @@ static ssize_t ctxid_masks_store(struct device *dev, u8 i, j, maskbyte; unsigned long val1, val2, mask; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; /* * only implemented when ctxid tracing is enabled, i.e. at least one @@ -1652,39 +1723,39 @@ static ssize_t ctxid_masks_store(struct device *dev, switch (drvdata->numcidc) { case 0x1: /* COMP0, bits[7:0] */ - drvdata->ctxid_mask0 = val1 & 0xFF; + config->ctxid_mask0 = val1 & 0xFF; break; case 0x2: /* COMP1, bits[15:8] */ - drvdata->ctxid_mask0 = val1 & 0xFFFF; + config->ctxid_mask0 = val1 & 0xFFFF; break; case 0x3: /* COMP2, bits[23:16] */ - drvdata->ctxid_mask0 = val1 & 0xFFFFFF; + config->ctxid_mask0 = val1 & 0xFFFFFF; break; case 0x4: /* COMP3, bits[31:24] */ - drvdata->ctxid_mask0 = val1; + config->ctxid_mask0 = val1; break; case 0x5: /* COMP4, bits[7:0] */ - drvdata->ctxid_mask0 = val1; - drvdata->ctxid_mask1 = val2 & 0xFF; + config->ctxid_mask0 = val1; + config->ctxid_mask1 = val2 & 0xFF; break; case 0x6: /* COMP5, bits[15:8] */ - drvdata->ctxid_mask0 = val1; - drvdata->ctxid_mask1 = val2 & 0xFFFF; + config->ctxid_mask0 = val1; + config->ctxid_mask1 = val2 & 0xFFFF; break; case 0x7: /* COMP6, bits[23:16] */ - drvdata->ctxid_mask0 = val1; - drvdata->ctxid_mask1 = val2 & 0xFFFFFF; + config->ctxid_mask0 = val1; + config->ctxid_mask1 = val2 & 0xFFFFFF; break; case 0x8: /* COMP7, bits[31:24] */ - drvdata->ctxid_mask0 = val1; - drvdata->ctxid_mask1 = val2; + config->ctxid_mask0 = val1; + config->ctxid_mask1 = val2; break; default: break; @@ -1695,7 +1766,7 @@ static ssize_t ctxid_masks_store(struct device *dev, * For example, if bit[3] of ctxid_mask0 is 1, we must clear bits[31:24] * of ctxid comparator0 value (corresponding to byte 0) register. */ - mask = drvdata->ctxid_mask0; + mask = config->ctxid_mask0; for (i = 0; i < drvdata->numcidc; i++) { /* mask value of corresponding ctxid comparator */ maskbyte = mask & ETMv4_EVENT_MASK; @@ -1705,13 +1776,13 @@ static ssize_t ctxid_masks_store(struct device *dev, */ for (j = 0; j < 8; j++) { if (maskbyte & 1) - drvdata->ctxid_pid[i] &= ~(0xFF << (j * 8)); + config->ctxid_pid[i] &= ~(0xFF << (j * 8)); maskbyte >>= 1; } /* Select the next ctxid comparator mask value */ if (i == 3) /* ctxid comparators[4-7] */ - mask = drvdata->ctxid_mask1; + mask = config->ctxid_mask1; else mask >>= 0x8; } @@ -1727,8 +1798,9 @@ static ssize_t vmid_idx_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->vmid_idx; + val = config->vmid_idx; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1738,6 +1810,7 @@ static ssize_t vmid_idx_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -1749,7 +1822,7 @@ static ssize_t vmid_idx_store(struct device *dev, * dereferenced multiple times within a spinlock block elsewhere. */ spin_lock(&drvdata->spinlock); - drvdata->vmid_idx = val; + config->vmid_idx = val; spin_unlock(&drvdata->spinlock); return size; } @@ -1761,8 +1834,9 @@ static ssize_t vmid_val_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = (unsigned long)drvdata->vmid_val[drvdata->vmid_idx]; + val = (unsigned long)config->vmid_val[config->vmid_idx]; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1772,6 +1846,7 @@ static ssize_t vmid_val_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; /* * only implemented when vmid tracing is enabled, i.e. at least one @@ -1783,7 +1858,7 @@ static ssize_t vmid_val_store(struct device *dev, return -EINVAL; spin_lock(&drvdata->spinlock); - drvdata->vmid_val[drvdata->vmid_idx] = (u64)val; + config->vmid_val[config->vmid_idx] = (u64)val; spin_unlock(&drvdata->spinlock); return size; } @@ -1794,10 +1869,11 @@ static ssize_t vmid_masks_show(struct device *dev, { unsigned long val1, val2; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - val1 = drvdata->vmid_mask0; - val2 = drvdata->vmid_mask1; + val1 = config->vmid_mask0; + val2 = config->vmid_mask1; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2); } @@ -1809,6 +1885,8 @@ static ssize_t vmid_masks_store(struct device *dev, u8 i, j, maskbyte; unsigned long val1, val2, mask; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; + /* * only implemented when vmid tracing is enabled, i.e. at least one * vmid comparator is implemented and at least 8 bit vmid size @@ -1827,39 +1905,39 @@ static ssize_t vmid_masks_store(struct device *dev, switch (drvdata->numvmidc) { case 0x1: /* COMP0, bits[7:0] */ - drvdata->vmid_mask0 = val1 & 0xFF; + config->vmid_mask0 = val1 & 0xFF; break; case 0x2: /* COMP1, bits[15:8] */ - drvdata->vmid_mask0 = val1 & 0xFFFF; + config->vmid_mask0 = val1 & 0xFFFF; break; case 0x3: /* COMP2, bits[23:16] */ - drvdata->vmid_mask0 = val1 & 0xFFFFFF; + config->vmid_mask0 = val1 & 0xFFFFFF; break; case 0x4: /* COMP3, bits[31:24] */ - drvdata->vmid_mask0 = val1; + config->vmid_mask0 = val1; break; case 0x5: /* COMP4, bits[7:0] */ - drvdata->vmid_mask0 = val1; - drvdata->vmid_mask1 = val2 & 0xFF; + config->vmid_mask0 = val1; + config->vmid_mask1 = val2 & 0xFF; break; case 0x6: /* COMP5, bits[15:8] */ - drvdata->vmid_mask0 = val1; - drvdata->vmid_mask1 = val2 & 0xFFFF; + config->vmid_mask0 = val1; + config->vmid_mask1 = val2 & 0xFFFF; break; case 0x7: /* COMP6, bits[23:16] */ - drvdata->vmid_mask0 = val1; - drvdata->vmid_mask1 = val2 & 0xFFFFFF; + config->vmid_mask0 = val1; + config->vmid_mask1 = val2 & 0xFFFFFF; break; case 0x8: /* COMP7, bits[31:24] */ - drvdata->vmid_mask0 = val1; - drvdata->vmid_mask1 = val2; + config->vmid_mask0 = val1; + config->vmid_mask1 = val2; break; default: break; @@ -1871,7 +1949,7 @@ static ssize_t vmid_masks_store(struct device *dev, * For example, if bit[3] of vmid_mask0 is 1, we must clear bits[31:24] * of vmid comparator0 value (corresponding to byte 0) register. */ - mask = drvdata->vmid_mask0; + mask = config->vmid_mask0; for (i = 0; i < drvdata->numvmidc; i++) { /* mask value of corresponding vmid comparator */ maskbyte = mask & ETMv4_EVENT_MASK; @@ -1881,13 +1959,13 @@ static ssize_t vmid_masks_store(struct device *dev, */ for (j = 0; j < 8; j++) { if (maskbyte & 1) - drvdata->vmid_val[i] &= ~(0xFF << (j * 8)); + config->vmid_val[i] &= ~(0xFF << (j * 8)); maskbyte >>= 1; } /* Select the next vmid comparator mask value */ if (i == 3) /* vmid comparators[4-7] */ - mask = drvdata->vmid_mask1; + mask = config->vmid_mask1; else mask >>= 0x8; } diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 5cb919118d70..102e9aaf952b 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -95,6 +95,7 @@ static void etm4_enable_hw(void *info) { int i; struct etmv4_drvdata *drvdata = info; + struct etmv4_config *config = &drvdata->config; CS_UNLOCK(drvdata->base); @@ -109,69 +110,69 @@ static void etm4_enable_hw(void *info) "timeout observed when probing at offset %#x\n", TRCSTATR); - writel_relaxed(drvdata->pe_sel, drvdata->base + TRCPROCSELR); - writel_relaxed(drvdata->cfg, drvdata->base + TRCCONFIGR); + writel_relaxed(config->pe_sel, drvdata->base + TRCPROCSELR); + writel_relaxed(config->cfg, drvdata->base + TRCCONFIGR); /* nothing specific implemented */ writel_relaxed(0x0, drvdata->base + TRCAUXCTLR); - writel_relaxed(drvdata->eventctrl0, drvdata->base + TRCEVENTCTL0R); - writel_relaxed(drvdata->eventctrl1, drvdata->base + TRCEVENTCTL1R); - writel_relaxed(drvdata->stall_ctrl, drvdata->base + TRCSTALLCTLR); - writel_relaxed(drvdata->ts_ctrl, drvdata->base + TRCTSCTLR); - writel_relaxed(drvdata->syncfreq, drvdata->base + TRCSYNCPR); - writel_relaxed(drvdata->ccctlr, drvdata->base + TRCCCCTLR); - writel_relaxed(drvdata->bb_ctrl, drvdata->base + TRCBBCTLR); + writel_relaxed(config->eventctrl0, drvdata->base + TRCEVENTCTL0R); + writel_relaxed(config->eventctrl1, drvdata->base + TRCEVENTCTL1R); + writel_relaxed(config->stall_ctrl, drvdata->base + TRCSTALLCTLR); + writel_relaxed(config->ts_ctrl, drvdata->base + TRCTSCTLR); + writel_relaxed(config->syncfreq, drvdata->base + TRCSYNCPR); + writel_relaxed(config->ccctlr, drvdata->base + TRCCCCTLR); + writel_relaxed(config->bb_ctrl, drvdata->base + TRCBBCTLR); writel_relaxed(drvdata->trcid, drvdata->base + TRCTRACEIDR); - writel_relaxed(drvdata->vinst_ctrl, drvdata->base + TRCVICTLR); - writel_relaxed(drvdata->viiectlr, drvdata->base + TRCVIIECTLR); - writel_relaxed(drvdata->vissctlr, + writel_relaxed(config->vinst_ctrl, drvdata->base + TRCVICTLR); + writel_relaxed(config->viiectlr, drvdata->base + TRCVIIECTLR); + writel_relaxed(config->vissctlr, drvdata->base + TRCVISSCTLR); - writel_relaxed(drvdata->vipcssctlr, + writel_relaxed(config->vipcssctlr, drvdata->base + TRCVIPCSSCTLR); for (i = 0; i < drvdata->nrseqstate - 1; i++) - writel_relaxed(drvdata->seq_ctrl[i], + writel_relaxed(config->seq_ctrl[i], drvdata->base + TRCSEQEVRn(i)); - writel_relaxed(drvdata->seq_rst, drvdata->base + TRCSEQRSTEVR); - writel_relaxed(drvdata->seq_state, drvdata->base + TRCSEQSTR); - writel_relaxed(drvdata->ext_inp, drvdata->base + TRCEXTINSELR); + writel_relaxed(config->seq_rst, drvdata->base + TRCSEQRSTEVR); + writel_relaxed(config->seq_state, drvdata->base + TRCSEQSTR); + writel_relaxed(config->ext_inp, drvdata->base + TRCEXTINSELR); for (i = 0; i < drvdata->nr_cntr; i++) { - writel_relaxed(drvdata->cntrldvr[i], + writel_relaxed(config->cntrldvr[i], drvdata->base + TRCCNTRLDVRn(i)); - writel_relaxed(drvdata->cntr_ctrl[i], + writel_relaxed(config->cntr_ctrl[i], drvdata->base + TRCCNTCTLRn(i)); - writel_relaxed(drvdata->cntr_val[i], + writel_relaxed(config->cntr_val[i], drvdata->base + TRCCNTVRn(i)); } /* Resource selector pair 0 is always implemented and reserved */ - for (i = 2; i < drvdata->nr_resource * 2; i++) - writel_relaxed(drvdata->res_ctrl[i], + for (i = 0; i < drvdata->nr_resource * 2; i++) + writel_relaxed(config->res_ctrl[i], drvdata->base + TRCRSCTLRn(i)); for (i = 0; i < drvdata->nr_ss_cmp; i++) { - writel_relaxed(drvdata->ss_ctrl[i], + writel_relaxed(config->ss_ctrl[i], drvdata->base + TRCSSCCRn(i)); - writel_relaxed(drvdata->ss_status[i], + writel_relaxed(config->ss_status[i], drvdata->base + TRCSSCSRn(i)); - writel_relaxed(drvdata->ss_pe_cmp[i], + writel_relaxed(config->ss_pe_cmp[i], drvdata->base + TRCSSPCICRn(i)); } for (i = 0; i < drvdata->nr_addr_cmp; i++) { - writeq_relaxed(drvdata->addr_val[i], + writeq_relaxed(config->addr_val[i], drvdata->base + TRCACVRn(i)); - writeq_relaxed(drvdata->addr_acc[i], + writeq_relaxed(config->addr_acc[i], drvdata->base + TRCACATRn(i)); } for (i = 0; i < drvdata->numcidc; i++) - writeq_relaxed(drvdata->ctxid_pid[i], + writeq_relaxed(config->ctxid_pid[i], drvdata->base + TRCCIDCVRn(i)); - writel_relaxed(drvdata->ctxid_mask0, drvdata->base + TRCCIDCCTLR0); - writel_relaxed(drvdata->ctxid_mask1, drvdata->base + TRCCIDCCTLR1); + writel_relaxed(config->ctxid_mask0, drvdata->base + TRCCIDCCTLR0); + writel_relaxed(config->ctxid_mask1, drvdata->base + TRCCIDCCTLR1); for (i = 0; i < drvdata->numvmidc; i++) - writeq_relaxed(drvdata->vmid_val[i], + writeq_relaxed(config->vmid_val[i], drvdata->base + TRCVMIDCVRn(i)); - writel_relaxed(drvdata->vmid_mask0, drvdata->base + TRCVMIDCCTLR0); - writel_relaxed(drvdata->vmid_mask1, drvdata->base + TRCVMIDCCTLR1); + writel_relaxed(config->vmid_mask0, drvdata->base + TRCVMIDCCTLR0); + writel_relaxed(config->vmid_mask1, drvdata->base + TRCVMIDCCTLR1); /* Enable the trace unit */ writel_relaxed(1, drvdata->base + TRCPRGCTLR); @@ -438,83 +439,84 @@ static void etm4_init_arch_data(void *info) static void etm4_init_default_data(struct etmv4_drvdata *drvdata) { int i; + struct etmv4_config *config = &drvdata->config; - drvdata->pe_sel = 0x0; - drvdata->cfg = (ETMv4_MODE_CTXID | ETM_MODE_VMID | + config->pe_sel = 0x0; + config->cfg = (ETMv4_MODE_CTXID | ETM_MODE_VMID | ETMv4_MODE_TIMESTAMP | ETM_MODE_RETURNSTACK); /* disable all events tracing */ - drvdata->eventctrl0 = 0x0; - drvdata->eventctrl1 = 0x0; + config->eventctrl0 = 0x0; + config->eventctrl1 = 0x0; /* disable stalling */ - drvdata->stall_ctrl = 0x0; + config->stall_ctrl = 0x0; /* disable timestamp event */ - drvdata->ts_ctrl = 0x0; + config->ts_ctrl = 0x0; /* enable trace synchronization every 4096 bytes for trace */ if (drvdata->syncpr == false) - drvdata->syncfreq = 0xC; + config->syncfreq = 0xC; /* * enable viewInst to trace everything with start-stop logic in * started state */ - drvdata->vinst_ctrl |= BIT(0); + config->vinst_ctrl |= BIT(0); /* set initial state of start-stop logic */ if (drvdata->nr_addr_cmp) - drvdata->vinst_ctrl |= BIT(9); + config->vinst_ctrl |= BIT(9); /* no address range filtering for ViewInst */ - drvdata->viiectlr = 0x0; + config->viiectlr = 0x0; /* no start-stop filtering for ViewInst */ - drvdata->vissctlr = 0x0; + config->vissctlr = 0x0; /* disable seq events */ for (i = 0; i < drvdata->nrseqstate-1; i++) - drvdata->seq_ctrl[i] = 0x0; - drvdata->seq_rst = 0x0; - drvdata->seq_state = 0x0; + config->seq_ctrl[i] = 0x0; + config->seq_rst = 0x0; + config->seq_state = 0x0; /* disable external input events */ - drvdata->ext_inp = 0x0; + config->ext_inp = 0x0; for (i = 0; i < drvdata->nr_cntr; i++) { - drvdata->cntrldvr[i] = 0x0; - drvdata->cntr_ctrl[i] = 0x0; - drvdata->cntr_val[i] = 0x0; + config->cntrldvr[i] = 0x0; + config->cntr_ctrl[i] = 0x0; + config->cntr_val[i] = 0x0; } /* Resource selector pair 0 is always implemented and reserved */ - drvdata->res_idx = 0x2; + config->res_idx = 0x2; for (i = 2; i < drvdata->nr_resource * 2; i++) - drvdata->res_ctrl[i] = 0x0; + config->res_ctrl[i] = 0x0; for (i = 0; i < drvdata->nr_ss_cmp; i++) { - drvdata->ss_ctrl[i] = 0x0; - drvdata->ss_pe_cmp[i] = 0x0; + config->ss_ctrl[i] = 0x0; + config->ss_pe_cmp[i] = 0x0; } if (drvdata->nr_addr_cmp >= 1) { - drvdata->addr_val[0] = (unsigned long)_stext; - drvdata->addr_val[1] = (unsigned long)_etext; - drvdata->addr_type[0] = ETM_ADDR_TYPE_RANGE; - drvdata->addr_type[1] = ETM_ADDR_TYPE_RANGE; + config->addr_val[0] = (unsigned long)_stext; + config->addr_val[1] = (unsigned long)_etext; + config->addr_type[0] = ETM_ADDR_TYPE_RANGE; + config->addr_type[1] = ETM_ADDR_TYPE_RANGE; } for (i = 0; i < drvdata->numcidc; i++) { - drvdata->ctxid_pid[i] = 0x0; - drvdata->ctxid_vpid[i] = 0x0; + config->ctxid_pid[i] = 0x0; + config->ctxid_vpid[i] = 0x0; } - drvdata->ctxid_mask0 = 0x0; - drvdata->ctxid_mask1 = 0x0; + config->ctxid_mask0 = 0x0; + config->ctxid_mask1 = 0x0; for (i = 0; i < drvdata->numvmidc; i++) - drvdata->vmid_val[i] = 0x0; - drvdata->vmid_mask0 = 0x0; - drvdata->vmid_mask1 = 0x0; + config->vmid_val[i] = 0x0; + config->vmid_mask0 = 0x0; + config->vmid_mask1 = 0x0; /* * A trace ID value of 0 is invalid, so let's start at some diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index e6ff9aae2a7e..709d96e63910 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -180,66 +180,19 @@ #define TRCSTATR_IDLE_BIT 0 /** - * struct etm4_drvdata - specifics associated to an ETM component - * @base: Memory mapped base address for this component. - * @dev: The device entity associated to this component. - * @csdev: Component vitals needed by the framework. - * @spinlock: Only one at a time pls. - * @cpu: The cpu this component is affined to. - * @arch: ETM version number. - * @enable: Is this ETM currently tracing. - * @sticky_enable: true if ETM base configuration has been done. - * @boot_enable:True if we should start tracing at boot time. - * @os_unlock: True if access to management registers is allowed. - * @nr_pe: The number of processing entity available for tracing. - * @nr_pe_cmp: The number of processing entity comparator inputs that are - * available for tracing. - * @nr_addr_cmp:Number of pairs of address comparators available - * as found in ETMIDR4 0-3. - * @nr_cntr: Number of counters as found in ETMIDR5 bit 28-30. - * @nr_ext_inp: Number of external input. - * @numcidc: Number of contextID comparators. - * @numvmidc: Number of VMID comparators. - * @nrseqstate: The number of sequencer states that are implemented. - * @nr_event: Indicates how many events the trace unit support. - * @nr_resource:The number of resource selection pairs available for tracing. - * @nr_ss_cmp: Number of single-shot comparator controls that are available. + * struct etmv4_config - configuration information related to an ETMv4 * @mode: Controls various modes supported by this ETM. - * @trcid: value of the current ID for this component. - * @trcid_size: Indicates the trace ID width. - * @instrp0: Tracing of load and store instructions - * as P0 elements is supported. - * @trccond: If the trace unit supports conditional - * instruction tracing. - * @retstack: Indicates if the implementation supports a return stack. - * @trc_error: Whether a trace unit can trace a system - * error exception. - * @atbtrig: If the implementation can support ATB triggers - * @lpoverride: If the implementation can support low-power state over. * @pe_sel: Controls which PE to trace. * @cfg: Controls the tracing options. * @eventctrl0: Controls the tracing of arbitrary events. * @eventctrl1: Controls the behavior of the events that @event_ctrl0 selects. * @stallctl: If functionality that prevents trace unit buffer overflows * is available. - * @sysstall: Does the system support stall control of the PE? - * @nooverflow: Indicate if overflow prevention is supported. - * @stall_ctrl: Enables trace unit functionality that prevents trace - * unit buffer overflows. - * @ts_size: Global timestamp size field. * @ts_ctrl: Controls the insertion of global timestamps in the * trace streams. - * @syncpr: Indicates if an implementation has a fixed - * synchronization period. * @syncfreq: Controls how often trace synchronization requests occur. - * @trccci: Indicates if the trace unit supports cycle counting - * for instruction. - * @ccsize: Indicates the size of the cycle counter in bits. - * @ccitmin: minimum value that can be programmed in * the TRCCCCTLR register. * @ccctlr: Sets the threshold value for cycle counting. - * @trcbb: Indicates if the trace unit supports branch broadcast tracing. - * @q_support: Q element support characteristics. * @vinst_ctrl: Controls instruction trace filtering. * @viiectlr: Set or read, the address range comparators. * @vissctlr: Set, or read, the single address comparators that control the @@ -264,73 +217,28 @@ * @addr_acc: Address comparator access type. * @addr_type: Current status of the comparator register. * @ctxid_idx: Context ID index selector. - * @ctxid_size: Size of the context ID field to consider. * @ctxid_pid: Value of the context ID comparator. * @ctxid_vpid: Virtual PID seen by users if PID namespace is enabled, otherwise * the same value of ctxid_pid. * @ctxid_mask0:Context ID comparator mask for comparator 0-3. * @ctxid_mask1:Context ID comparator mask for comparator 4-7. * @vmid_idx: VM ID index selector. - * @vmid_size: Size of the VM ID comparator to consider. * @vmid_val: Value of the VM ID comparator. * @vmid_mask0: VM ID comparator mask for comparator 0-3. * @vmid_mask1: VM ID comparator mask for comparator 4-7. - * @s_ex_level: In secure state, indicates whether instruction tracing is - * supported for the corresponding Exception level. - * @ns_ex_level:In non-secure state, indicates whether instruction tracing is - * supported for the corresponding Exception level. * @ext_inp: External input selection. */ -struct etmv4_drvdata { - void __iomem *base; - struct device *dev; - struct coresight_device *csdev; - spinlock_t spinlock; - int cpu; - u8 arch; - bool enable; - bool sticky_enable; - bool boot_enable; - bool os_unlock; - u8 nr_pe; - u8 nr_pe_cmp; - u8 nr_addr_cmp; - u8 nr_cntr; - u8 nr_ext_inp; - u8 numcidc; - u8 numvmidc; - u8 nrseqstate; - u8 nr_event; - u8 nr_resource; - u8 nr_ss_cmp; +struct etmv4_config { u32 mode; - u8 trcid; - u8 trcid_size; - bool instrp0; - bool trccond; - bool retstack; - bool trc_error; - bool atbtrig; - bool lpoverride; u32 pe_sel; u32 cfg; u32 eventctrl0; u32 eventctrl1; - bool stallctl; - bool sysstall; - bool nooverflow; u32 stall_ctrl; - u8 ts_size; u32 ts_ctrl; - bool syncpr; u32 syncfreq; - bool trccci; - u8 ccsize; - u8 ccitmin; u32 ccctlr; - bool trcbb; u32 bb_ctrl; - bool q_support; u32 vinst_ctrl; u32 viiectlr; u32 vissctlr; @@ -353,19 +261,119 @@ struct etmv4_drvdata { u64 addr_acc[ETM_MAX_SINGLE_ADDR_CMP]; u8 addr_type[ETM_MAX_SINGLE_ADDR_CMP]; u8 ctxid_idx; - u8 ctxid_size; u64 ctxid_pid[ETMv4_MAX_CTXID_CMP]; u64 ctxid_vpid[ETMv4_MAX_CTXID_CMP]; u32 ctxid_mask0; u32 ctxid_mask1; u8 vmid_idx; - u8 vmid_size; u64 vmid_val[ETM_MAX_VMID_CMP]; u32 vmid_mask0; u32 vmid_mask1; + u32 ext_inp; +}; + +/** + * struct etm4_drvdata - specifics associated to an ETM component + * @base: Memory mapped base address for this component. + * @dev: The device entity associated to this component. + * @csdev: Component vitals needed by the framework. + * @spinlock: Only one at a time pls. + * @cpu: The cpu this component is affined to. + * @arch: ETM version number. + * @nr_pe: The number of processing entity available for tracing. + * @nr_pe_cmp: The number of processing entity comparator inputs that are + * available for tracing. + * @nr_addr_cmp:Number of pairs of address comparators available + * as found in ETMIDR4 0-3. + * @nr_cntr: Number of counters as found in ETMIDR5 bit 28-30. + * @nr_ext_inp: Number of external input. + * @numcidc: Number of contextID comparators. + * @numvmidc: Number of VMID comparators. + * @nrseqstate: The number of sequencer states that are implemented. + * @nr_event: Indicates how many events the trace unit support. + * @nr_resource:The number of resource selection pairs available for tracing. + * @nr_ss_cmp: Number of single-shot comparator controls that are available. + * @trcid: value of the current ID for this component. + * @trcid_size: Indicates the trace ID width. + * @ts_size: Global timestamp size field. + * @ctxid_size: Size of the context ID field to consider. + * @vmid_size: Size of the VM ID comparator to consider. + * @ccsize: Indicates the size of the cycle counter in bits. + * @ccitmin: minimum value that can be programmed in + * @s_ex_level: In secure state, indicates whether instruction tracing is + * supported for the corresponding Exception level. + * @ns_ex_level:In non-secure state, indicates whether instruction tracing is + * supported for the corresponding Exception level. + * @enable: Is this ETM currently tracing. + * @sticky_enable: true if ETM base configuration has been done. + * @boot_enable:True if we should start tracing at boot time. + * @os_unlock: True if access to management registers is allowed. + * @instrp0: Tracing of load and store instructions + * as P0 elements is supported. + * @trcbb: Indicates if the trace unit supports branch broadcast tracing. + * @trccond: If the trace unit supports conditional + * instruction tracing. + * @retstack: Indicates if the implementation supports a return stack. + * @trccci: Indicates if the trace unit supports cycle counting + * for instruction. + * @q_support: Q element support characteristics. + * @trc_error: Whether a trace unit can trace a system + * error exception. + * @syncpr: Indicates if an implementation has a fixed + * synchronization period. + * @stall_ctrl: Enables trace unit functionality that prevents trace + * unit buffer overflows. + * @sysstall: Does the system support stall control of the PE? + * @nooverflow: Indicate if overflow prevention is supported. + * @atbtrig: If the implementation can support ATB triggers + * @lpoverride: If the implementation can support low-power state over. + * @config: structure holding configuration parameters. + */ +struct etmv4_drvdata { + void __iomem *base; + struct device *dev; + struct coresight_device *csdev; + spinlock_t spinlock; + int cpu; + u8 arch; + u8 nr_pe; + u8 nr_pe_cmp; + u8 nr_addr_cmp; + u8 nr_cntr; + u8 nr_ext_inp; + u8 numcidc; + u8 numvmidc; + u8 nrseqstate; + u8 nr_event; + u8 nr_resource; + u8 nr_ss_cmp; + u8 trcid; + u8 trcid_size; + u8 ts_size; + u8 ctxid_size; + u8 vmid_size; + u8 ccsize; + u8 ccitmin; u8 s_ex_level; u8 ns_ex_level; - u32 ext_inp; + bool enable; + bool sticky_enable; + bool boot_enable; + bool os_unlock; + bool instrp0; + bool trcbb; + bool trccond; + bool retstack; + bool trccci; + bool q_support; + bool trc_error; + bool syncpr; + bool stallctl; + bool sysstall; + bool nooverflow; + bool atbtrig; + bool lpoverride; + struct etmv4_config config; }; /* Address comparator access types */ From 687e02507830c20c5df9db16e1211194e0b0406d Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 5 Apr 2016 11:53:45 -0600 Subject: [PATCH 072/104] coresight: etm4x: splitting etmv4 default configuration Splitting and updating the default initialisation for each etmv4 configuration so that it can be called at the beginning of each session rather than initialisation time only. Since the trace ID isn't expected to change with every session, moving it with the default tracer initialisation. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit fc208abef39279903887bea955139f64bf0bbb12) --- drivers/hwtracing/coresight/coresight-etm4x.c | 134 +++++++++--------- drivers/hwtracing/coresight/coresight-etm4x.h | 12 ++ 2 files changed, 76 insertions(+), 70 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 102e9aaf952b..2744413dedb2 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -436,14 +437,20 @@ static void etm4_init_arch_data(void *info) CS_LOCK(drvdata->base); } -static void etm4_init_default_data(struct etmv4_drvdata *drvdata) +static void etm4_set_default(struct etmv4_config *config) { - int i; - struct etmv4_config *config = &drvdata->config; + if (WARN_ON_ONCE(!config)) + return; - config->pe_sel = 0x0; - config->cfg = (ETMv4_MODE_CTXID | ETM_MODE_VMID | - ETMv4_MODE_TIMESTAMP | ETM_MODE_RETURNSTACK); + /* + * Make default initialisation trace everything + * + * Select the "always true" resource selector on the + * "Enablign Event" line and configure address range comparator + * '0' to trace all the possible address range. From there + * configure the "include/exclude" engine to include address + * range comparator '0'. + */ /* disable all events tracing */ config->eventctrl0 = 0x0; @@ -452,78 +459,58 @@ static void etm4_init_default_data(struct etmv4_drvdata *drvdata) /* disable stalling */ config->stall_ctrl = 0x0; + /* enable trace synchronization every 4096 bytes, if available */ + config->syncfreq = 0xC; + /* disable timestamp event */ config->ts_ctrl = 0x0; - /* enable trace synchronization every 4096 bytes for trace */ - if (drvdata->syncpr == false) - config->syncfreq = 0xC; + /* TRCVICTLR::EVENT = 0x01, select the always on logic */ + config->vinst_ctrl |= BIT(0); /* - * enable viewInst to trace everything with start-stop logic in - * started state + * TRCVICTLR::SSSTATUS == 1, the start-stop logic is + * in the started state */ - config->vinst_ctrl |= BIT(0); - /* set initial state of start-stop logic */ - if (drvdata->nr_addr_cmp) - config->vinst_ctrl |= BIT(9); + config->vinst_ctrl |= BIT(9); + + /* + * Configure address range comparator '0' to encompass all + * possible addresses. + */ + + /* First half of default address comparator: start at address 0 */ + config->addr_val[ETM_DEFAULT_ADDR_COMP] = 0x0; + /* trace instruction addresses */ + config->addr_acc[ETM_DEFAULT_ADDR_COMP] &= ~(BIT(0) | BIT(1)); + /* EXLEVEL_NS, bits[12:15], only trace application and kernel space */ + config->addr_acc[ETM_DEFAULT_ADDR_COMP] |= ETM_EXLEVEL_NS_HYP; + /* EXLEVEL_S, bits[11:8], don't trace anything in secure state */ + config->addr_acc[ETM_DEFAULT_ADDR_COMP] |= (ETM_EXLEVEL_S_APP | + ETM_EXLEVEL_S_OS | + ETM_EXLEVEL_S_HYP); + config->addr_type[ETM_DEFAULT_ADDR_COMP] = ETM_ADDR_TYPE_RANGE; + + /* + * Second half of default address comparator: go all + * the way to the top. + */ + config->addr_val[ETM_DEFAULT_ADDR_COMP + 1] = ~0x0; + /* trace instruction addresses */ + config->addr_acc[ETM_DEFAULT_ADDR_COMP + 1] &= ~(BIT(0) | BIT(1)); + /* Address comparator type must be equal for both halves */ + config->addr_acc[ETM_DEFAULT_ADDR_COMP + 1] = + config->addr_acc[ETM_DEFAULT_ADDR_COMP]; + config->addr_type[ETM_DEFAULT_ADDR_COMP + 1] = ETM_ADDR_TYPE_RANGE; + + /* + * Configure the ViewInst function to filter on address range + * comparator '0'. + */ + config->viiectlr = BIT(0); - /* no address range filtering for ViewInst */ - config->viiectlr = 0x0; /* no start-stop filtering for ViewInst */ config->vissctlr = 0x0; - - /* disable seq events */ - for (i = 0; i < drvdata->nrseqstate-1; i++) - config->seq_ctrl[i] = 0x0; - config->seq_rst = 0x0; - config->seq_state = 0x0; - - /* disable external input events */ - config->ext_inp = 0x0; - - for (i = 0; i < drvdata->nr_cntr; i++) { - config->cntrldvr[i] = 0x0; - config->cntr_ctrl[i] = 0x0; - config->cntr_val[i] = 0x0; - } - - /* Resource selector pair 0 is always implemented and reserved */ - config->res_idx = 0x2; - for (i = 2; i < drvdata->nr_resource * 2; i++) - config->res_ctrl[i] = 0x0; - - for (i = 0; i < drvdata->nr_ss_cmp; i++) { - config->ss_ctrl[i] = 0x0; - config->ss_pe_cmp[i] = 0x0; - } - - if (drvdata->nr_addr_cmp >= 1) { - config->addr_val[0] = (unsigned long)_stext; - config->addr_val[1] = (unsigned long)_etext; - config->addr_type[0] = ETM_ADDR_TYPE_RANGE; - config->addr_type[1] = ETM_ADDR_TYPE_RANGE; - } - - for (i = 0; i < drvdata->numcidc; i++) { - config->ctxid_pid[i] = 0x0; - config->ctxid_vpid[i] = 0x0; - } - - config->ctxid_mask0 = 0x0; - config->ctxid_mask1 = 0x0; - - for (i = 0; i < drvdata->numvmidc; i++) - config->vmid_val[i] = 0x0; - config->vmid_mask0 = 0x0; - config->vmid_mask1 = 0x0; - - /* - * A trace ID value of 0 is invalid, so let's start at some - * random value that fits in 7 bits. ETMv3.x has 0x10 so let's - * start at 0x20. - */ - drvdata->trcid = 0x20 + drvdata->cpu; } static int etm4_cpu_callback(struct notifier_block *nfb, unsigned long action, @@ -568,6 +555,11 @@ static struct notifier_block etm4_cpu_notifier = { .notifier_call = etm4_cpu_callback, }; +static void etm4_init_trace_id(struct etmv4_drvdata *drvdata) +{ + drvdata->trcid = coresight_get_trace_id(drvdata->cpu); +} + static int etm4_probe(struct amba_device *adev, const struct amba_id *id) { int ret; @@ -627,7 +619,9 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) ret = -EINVAL; goto err_arch_supported; } - etm4_init_default_data(drvdata); + + etm4_init_trace_id(drvdata); + etm4_set_default(&drvdata->config); pm_runtime_put(&adev->dev); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index 709d96e63910..6ff499bfb2f2 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -178,6 +178,18 @@ #define ETMv4_MODE_ALL 0xFFFFFFF #define TRCSTATR_IDLE_BIT 0 +#define ETM_DEFAULT_ADDR_COMP 0 + +/* secure state access levels */ +#define ETM_EXLEVEL_S_APP BIT(8) +#define ETM_EXLEVEL_S_OS BIT(9) +#define ETM_EXLEVEL_S_NA BIT(10) +#define ETM_EXLEVEL_S_HYP BIT(11) +/* non-secure state access levels */ +#define ETM_EXLEVEL_NS_APP BIT(12) +#define ETM_EXLEVEL_NS_OS BIT(13) +#define ETM_EXLEVEL_NS_HYP BIT(14) +#define ETM_EXLEVEL_NS_NA BIT(15) /** * struct etmv4_config - configuration information related to an ETMv4 From 17b04a9f5548d04c62d7ffe3094e8d90d4cc9b9d Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 5 Apr 2016 11:53:46 -0600 Subject: [PATCH 073/104] coresight: etm4x: unlocking tracers in default arch init As with the ETMv3.x driver, calling 'smp_call_function_single()' twice in a row is highly ineffective. As such moving function 'etm4_os_unlock()' before the default initialisation takes place, which results in the same outcome. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 66bbbb77540e846b9aac4c9467aca936128951bf) --- drivers/hwtracing/coresight/coresight-etm4x.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 2744413dedb2..92524fe1aa6c 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -44,12 +44,11 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO); static int etm4_count; static struct etmv4_drvdata *etmdrvdata[NR_CPUS]; -static void etm4_os_unlock(void *info) +static void etm4_os_unlock(struct etmv4_drvdata *drvdata) { - struct etmv4_drvdata *drvdata = (struct etmv4_drvdata *)info; - /* Writing any value to ETMOSLAR unlocks the trace registers */ writel_relaxed(0x0, drvdata->base + TRCOSLAR); + drvdata->os_unlock = true; isb(); } @@ -286,6 +285,9 @@ static void etm4_init_arch_data(void *info) u32 etmidr5; struct etmv4_drvdata *drvdata = info; + /* Make sure all registers are accessible */ + etm4_os_unlock(drvdata); + CS_UNLOCK(drvdata->base); /* find all capabilities of the tracing unit */ @@ -603,9 +605,6 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) get_online_cpus(); etmdrvdata[drvdata->cpu] = drvdata; - if (!smp_call_function_single(drvdata->cpu, etm4_os_unlock, drvdata, 1)) - drvdata->os_unlock = true; - if (smp_call_function_single(drvdata->cpu, etm4_init_arch_data, drvdata, 1)) dev_err(dev, "ETM arch init failed\n"); From 2c25a4e91650464a2bcbedc108654086e9904a68 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 5 Apr 2016 11:53:47 -0600 Subject: [PATCH 074/104] coresight: etm4x: moving etm_drvdata::enable to atomic field Similarly to ETMv3, moving etmv4_drvdata::enable to an atomic type that gives the 'mode' of a tracer and prevents multiple, simultanious access by different subsystems. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit c38a9ec2b2c12c38abca0b7954ed793f26969835) --- drivers/hwtracing/coresight/coresight-etm4x.c | 69 ++++++++++++++++--- drivers/hwtracing/coresight/coresight-etm4x.h | 5 +- 2 files changed, 63 insertions(+), 11 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 92524fe1aa6c..9f51a8f47650 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "coresight-etm4x.h" @@ -76,7 +77,7 @@ static int etm4_trace_id(struct coresight_device *csdev) unsigned long flags; int trace_id = -1; - if (!drvdata->enable) + if (!local_read(&drvdata->mode)) return drvdata->trcid; spin_lock_irqsave(&drvdata->spinlock, flags); @@ -188,8 +189,7 @@ static void etm4_enable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu); } -static int etm4_enable(struct coresight_device *csdev, - struct perf_event_attr *attr, u32 mode) +static int etm4_enable_sysfs(struct coresight_device *csdev) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); int ret; @@ -204,18 +204,46 @@ static int etm4_enable(struct coresight_device *csdev, etm4_enable_hw, drvdata, 1); if (ret) goto err; - drvdata->enable = true; - drvdata->sticky_enable = true; + drvdata->sticky_enable = true; spin_unlock(&drvdata->spinlock); dev_info(drvdata->dev, "ETM tracing enabled\n"); return 0; + err: spin_unlock(&drvdata->spinlock); return ret; } +static int etm4_enable(struct coresight_device *csdev, + struct perf_event_attr *attr, u32 mode) +{ + int ret; + u32 val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode); + + /* Someone is already using the tracer */ + if (val) + return -EBUSY; + + switch (mode) { + case CS_MODE_SYSFS: + ret = etm4_enable_sysfs(csdev); + break; + default: + ret = -EINVAL; + } + + /* The tracer didn't start */ + if (ret) + local_set(&drvdata->mode, CS_MODE_DISABLED); + + return ret; +} + static void etm4_disable_hw(void *info) { u32 control; @@ -238,7 +266,7 @@ static void etm4_disable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu); } -static void etm4_disable(struct coresight_device *csdev) +static void etm4_disable_sysfs(struct coresight_device *csdev) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -256,7 +284,6 @@ static void etm4_disable(struct coresight_device *csdev) * ensures that register writes occur when cpu is powered. */ smp_call_function_single(drvdata->cpu, etm4_disable_hw, drvdata, 1); - drvdata->enable = false; spin_unlock(&drvdata->spinlock); put_online_cpus(); @@ -264,6 +291,30 @@ static void etm4_disable(struct coresight_device *csdev) dev_info(drvdata->dev, "ETM tracing disabled\n"); } +static void etm4_disable(struct coresight_device *csdev) +{ + u32 mode; + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + /* + * For as long as the tracer isn't disabled another entity can't + * change its status. As such we can read the status here without + * fearing it will change under us. + */ + mode = local_read(&drvdata->mode); + + switch (mode) { + case CS_MODE_DISABLED: + break; + case CS_MODE_SYSFS: + etm4_disable_sysfs(csdev); + break; + } + + if (mode) + local_set(&drvdata->mode, CS_MODE_DISABLED); +} + static const struct coresight_ops_source etm4_source_ops = { .cpu_id = etm4_cpu_id, .trace_id = etm4_trace_id, @@ -531,7 +582,7 @@ static int etm4_cpu_callback(struct notifier_block *nfb, unsigned long action, etmdrvdata[cpu]->os_unlock = true; } - if (etmdrvdata[cpu]->enable) + if (local_read(&etmdrvdata[cpu]->mode)) etm4_enable_hw(etmdrvdata[cpu]); spin_unlock(&etmdrvdata[cpu]->spinlock); break; @@ -544,7 +595,7 @@ static int etm4_cpu_callback(struct notifier_block *nfb, unsigned long action, case CPU_DYING: spin_lock(&etmdrvdata[cpu]->spinlock); - if (etmdrvdata[cpu]->enable) + if (local_read(&etmdrvdata[cpu]->mode)) etm4_disable_hw(etmdrvdata[cpu]); spin_unlock(&etmdrvdata[cpu]->spinlock); break; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index 6ff499bfb2f2..f7748ae63451 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -13,6 +13,7 @@ #ifndef _CORESIGHT_CORESIGHT_ETM_H #define _CORESIGHT_CORESIGHT_ETM_H +#include #include #include "coresight-priv.h" @@ -290,6 +291,7 @@ struct etmv4_config { * @dev: The device entity associated to this component. * @csdev: Component vitals needed by the framework. * @spinlock: Only one at a time pls. + * @mode: This tracer's mode, i.e sysFS, Perf or disabled. * @cpu: The cpu this component is affined to. * @arch: ETM version number. * @nr_pe: The number of processing entity available for tracing. @@ -316,7 +318,6 @@ struct etmv4_config { * supported for the corresponding Exception level. * @ns_ex_level:In non-secure state, indicates whether instruction tracing is * supported for the corresponding Exception level. - * @enable: Is this ETM currently tracing. * @sticky_enable: true if ETM base configuration has been done. * @boot_enable:True if we should start tracing at boot time. * @os_unlock: True if access to management registers is allowed. @@ -346,6 +347,7 @@ struct etmv4_drvdata { struct device *dev; struct coresight_device *csdev; spinlock_t spinlock; + local_t mode; int cpu; u8 arch; u8 nr_pe; @@ -368,7 +370,6 @@ struct etmv4_drvdata { u8 ccitmin; u8 s_ex_level; u8 ns_ex_level; - bool enable; bool sticky_enable; bool boot_enable; bool os_unlock; From 7d2266e97a1348a8781d0940e9d84549e1baa7d4 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 5 Apr 2016 11:53:48 -0600 Subject: [PATCH 075/104] coresight: etm4x: implementing user/kernel mode tracing Adding new mode to limit tracing to kernel or user space. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 4f6fce54528e0382281cf199635d098e4b108357) --- .../coresight/coresight-etm4x-sysfs.c | 3 ++ drivers/hwtracing/coresight/coresight-etm4x.c | 35 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-etm4x.h | 5 ++- 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index a996db7ef2fc..0e80ec668402 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -440,6 +440,9 @@ static ssize_t mode_store(struct device *dev, else config->vinst_ctrl &= ~BIT(11); + if (config->mode & (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER)) + etm4_config_trace_mode(config); + spin_unlock(&drvdata->spinlock); return size; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 9f51a8f47650..88b8bc0f6549 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -566,6 +566,41 @@ static void etm4_set_default(struct etmv4_config *config) config->vissctlr = 0x0; } +void etm4_config_trace_mode(struct etmv4_config *config) +{ + u32 addr_acc, mode; + + mode = config->mode; + mode &= (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER); + + /* excluding kernel AND user space doesn't make sense */ + WARN_ON_ONCE(mode == (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER)); + + /* nothing to do if neither flags are set */ + if (!(mode & ETM_MODE_EXCL_KERN) && !(mode & ETM_MODE_EXCL_USER)) + return; + + addr_acc = config->addr_acc[ETM_DEFAULT_ADDR_COMP]; + /* clear default config */ + addr_acc &= ~(ETM_EXLEVEL_NS_APP | ETM_EXLEVEL_NS_OS); + + /* + * EXLEVEL_NS, bits[15:12] + * The Exception levels are: + * Bit[12] Exception level 0 - Application + * Bit[13] Exception level 1 - OS + * Bit[14] Exception level 2 - Hypervisor + * Bit[15] Never implemented + */ + if (mode & ETM_MODE_EXCL_KERN) + addr_acc |= ETM_EXLEVEL_NS_OS; + else + addr_acc |= ETM_EXLEVEL_NS_APP; + + config->addr_acc[ETM_DEFAULT_ADDR_COMP] = addr_acc; + config->addr_acc[ETM_DEFAULT_ADDR_COMP + 1] = addr_acc; +} + static int etm4_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index f7748ae63451..a291d4cc8bd8 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -176,7 +176,9 @@ #define ETM_MODE_TRACE_RESET BIT(25) #define ETM_MODE_TRACE_ERR BIT(26) #define ETM_MODE_VIEWINST_STARTSTOP BIT(27) -#define ETMv4_MODE_ALL 0xFFFFFFF +#define ETMv4_MODE_ALL (GENMASK(27, 0) | \ + ETM_MODE_EXCL_KERN | \ + ETM_MODE_EXCL_USER) #define TRCSTATR_IDLE_BIT 0 #define ETM_DEFAULT_ADDR_COMP 0 @@ -414,4 +416,5 @@ enum etm_addr_type { }; extern const struct attribute_group *coresight_etmv4_groups[]; +void etm4_config_trace_mode(struct etmv4_config *config); #endif From a77728f6795f441d79529d9faf285b127efe8d52 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 5 Apr 2016 11:53:49 -0600 Subject: [PATCH 076/104] coresight: etm4x: implementing the perf PMU API Adding a set of API allowing the Perf core to treat ETMv4 tracers like other PMUs. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 37fbbdbde9ad3722a7a18beab936825a6ff322bf) --- drivers/hwtracing/coresight/Makefile | 5 +- drivers/hwtracing/coresight/coresight-etm4x.c | 85 +++++++++++++++++-- 2 files changed, 82 insertions(+), 8 deletions(-) diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 637ca978cd05..1d0e32c7dbe4 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -1,7 +1,7 @@ # # Makefile for CoreSight drivers. # -obj-$(CONFIG_CORESIGHT) += coresight.o +obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o obj-$(CONFIG_OF) += of_coresight.o obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o obj-$(CONFIG_CORESIGHT_SINK_TPIU) += coresight-tpiu.o @@ -9,8 +9,7 @@ obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ coresight-replicator.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ - coresight-etm3x-sysfs.o \ - coresight-etm-perf.o + coresight-etm3x-sysfs.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \ coresight-etm4x-sysfs.o obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 88b8bc0f6549..6396b28993da 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -31,12 +31,14 @@ #include #include #include +#include #include #include #include #include #include "coresight-etm4x.h" +#include "coresight-etm-perf.h" static int boot_enable; module_param_named(boot_enable, boot_enable, int, S_IRUGO); @@ -44,6 +46,7 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO); /* The number of ETMv4 currently registered */ static int etm4_count; static struct etmv4_drvdata *etmdrvdata[NR_CPUS]; +static void etm4_set_default(struct etmv4_config *config); static void etm4_os_unlock(struct etmv4_drvdata *drvdata) { @@ -189,6 +192,58 @@ static void etm4_enable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu); } +static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, + struct perf_event_attr *attr) +{ + struct etmv4_config *config = &drvdata->config; + + if (!attr) + return -EINVAL; + + /* Clear configuration from previous run */ + memset(config, 0, sizeof(struct etmv4_config)); + + if (attr->exclude_kernel) + config->mode = ETM_MODE_EXCL_KERN; + + if (attr->exclude_user) + config->mode = ETM_MODE_EXCL_USER; + + /* Always start from the default config */ + etm4_set_default(config); + + /* + * By default the tracers are configured to trace the whole address + * range. Narrow the field only if requested by user space. + */ + if (config->mode) + etm4_config_trace_mode(config); + + /* Go from generic option to ETMv4 specifics */ + if (attr->config & BIT(ETM_OPT_CYCACC)) + config->cfg |= ETMv4_MODE_CYCACC; + if (attr->config & BIT(ETM_OPT_TS)) + config->cfg |= ETMv4_MODE_TIMESTAMP; + + return 0; +} + +static int etm4_enable_perf(struct coresight_device *csdev, + struct perf_event_attr *attr) +{ + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) + return -EINVAL; + + /* Configure the tracer based on the session's specifics */ + etm4_parse_event_config(drvdata, attr); + /* And enable it */ + etm4_enable_hw(drvdata); + + return 0; +} + static int etm4_enable_sysfs(struct coresight_device *csdev) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -233,6 +288,9 @@ static int etm4_enable(struct coresight_device *csdev, case CS_MODE_SYSFS: ret = etm4_enable_sysfs(csdev); break; + case CS_MODE_PERF: + ret = etm4_enable_perf(csdev, attr); + break; default: ret = -EINVAL; } @@ -266,6 +324,17 @@ static void etm4_disable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu); } +static int etm4_disable_perf(struct coresight_device *csdev) +{ + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) + return -EINVAL; + + etm4_disable_hw(drvdata); + return 0; +} + static void etm4_disable_sysfs(struct coresight_device *csdev) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -309,6 +378,9 @@ static void etm4_disable(struct coresight_device *csdev) case CS_MODE_SYSFS: etm4_disable_sysfs(csdev); break; + case CS_MODE_PERF: + etm4_disable_perf(csdev); + break; } if (mode) @@ -708,8 +780,6 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) etm4_init_trace_id(drvdata); etm4_set_default(&drvdata->config); - pm_runtime_put(&adev->dev); - desc->type = CORESIGHT_DEV_TYPE_SOURCE; desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC; desc->ops = &etm4_cs_ops; @@ -719,9 +789,16 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) drvdata->csdev = coresight_register(desc); if (IS_ERR(drvdata->csdev)) { ret = PTR_ERR(drvdata->csdev); - goto err_coresight_register; + goto err_arch_supported; } + ret = etm_perf_symlink(drvdata->csdev, true); + if (ret) { + coresight_unregister(drvdata->csdev); + goto err_arch_supported; + } + + pm_runtime_put(&adev->dev); dev_info(dev, "%s initialized\n", (char *)id->data); if (boot_enable) { @@ -732,8 +809,6 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) return 0; err_arch_supported: - pm_runtime_put(&adev->dev); -err_coresight_register: if (--etm4_count == 0) unregister_hotcpu_notifier(&etm4_cpu_notifier); return ret; From 7e388295d098f3a1ca58dcb823929e33027510ae Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 5 Apr 2016 11:53:50 -0600 Subject: [PATCH 077/104] coresight: moving coresight_simple_func() to header file Macro "coresight_simple_func()" can be used by several drivers. As such making the structure type generic and moving to a globally available header file. That way individual drivers can use the functionality by simply specifying the structure they need to work with. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 154f3520fe1cdef9009909dc62828eb2d7635631) --- .../coresight/coresight-etm3x-sysfs.c | 31 ++++------ .../coresight/coresight-etm4x-sysfs.c | 61 ++++++++----------- drivers/hwtracing/coresight/coresight-priv.h | 10 +++ 3 files changed, 49 insertions(+), 53 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index cbb4046c1070..02d4b629891f 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -1221,26 +1221,19 @@ static struct attribute *coresight_etm_attrs[] = { NULL, }; -#define coresight_simple_func(name, offset) \ -static ssize_t name##_show(struct device *_dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct etm_drvdata *drvdata = dev_get_drvdata(_dev->parent); \ - return scnprintf(buf, PAGE_SIZE, "0x%x\n", \ - readl_relaxed(drvdata->base + offset)); \ -} \ -DEVICE_ATTR_RO(name) +#define coresight_etm3x_simple_func(name, offset) \ + coresight_simple_func(struct etm_drvdata, name, offset) -coresight_simple_func(etmccr, ETMCCR); -coresight_simple_func(etmccer, ETMCCER); -coresight_simple_func(etmscr, ETMSCR); -coresight_simple_func(etmidr, ETMIDR); -coresight_simple_func(etmcr, ETMCR); -coresight_simple_func(etmtraceidr, ETMTRACEIDR); -coresight_simple_func(etmteevr, ETMTEEVR); -coresight_simple_func(etmtssvr, ETMTSSCR); -coresight_simple_func(etmtecr1, ETMTECR1); -coresight_simple_func(etmtecr2, ETMTECR2); +coresight_etm3x_simple_func(etmccr, ETMCCR); +coresight_etm3x_simple_func(etmccer, ETMCCER); +coresight_etm3x_simple_func(etmscr, ETMSCR); +coresight_etm3x_simple_func(etmidr, ETMIDR); +coresight_etm3x_simple_func(etmcr, ETMCR); +coresight_etm3x_simple_func(etmtraceidr, ETMTRACEIDR); +coresight_etm3x_simple_func(etmteevr, ETMTEEVR); +coresight_etm3x_simple_func(etmtssvr, ETMTSSCR); +coresight_etm3x_simple_func(etmtecr1, ETMTECR1); +coresight_etm3x_simple_func(etmtecr2, ETMTECR2); static struct attribute *coresight_etm_mgmt_attrs[] = { &dev_attr_etmccr.attr, diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index 0e80ec668402..7c84308c5564 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -2039,29 +2039,22 @@ static struct attribute *coresight_etmv4_attrs[] = { NULL, }; -#define coresight_simple_func(name, offset) \ -static ssize_t name##_show(struct device *_dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct etmv4_drvdata *drvdata = dev_get_drvdata(_dev->parent); \ - return scnprintf(buf, PAGE_SIZE, "0x%x\n", \ - readl_relaxed(drvdata->base + offset)); \ -} \ -DEVICE_ATTR_RO(name) +#define coresight_etm4x_simple_func(name, offset) \ + coresight_simple_func(struct etmv4_drvdata, name, offset) -coresight_simple_func(trcoslsr, TRCOSLSR); -coresight_simple_func(trcpdcr, TRCPDCR); -coresight_simple_func(trcpdsr, TRCPDSR); -coresight_simple_func(trclsr, TRCLSR); -coresight_simple_func(trcconfig, TRCCONFIGR); -coresight_simple_func(trctraceid, TRCTRACEIDR); -coresight_simple_func(trcauthstatus, TRCAUTHSTATUS); -coresight_simple_func(trcdevid, TRCDEVID); -coresight_simple_func(trcdevtype, TRCDEVTYPE); -coresight_simple_func(trcpidr0, TRCPIDR0); -coresight_simple_func(trcpidr1, TRCPIDR1); -coresight_simple_func(trcpidr2, TRCPIDR2); -coresight_simple_func(trcpidr3, TRCPIDR3); +coresight_etm4x_simple_func(trcoslsr, TRCOSLSR); +coresight_etm4x_simple_func(trcpdcr, TRCPDCR); +coresight_etm4x_simple_func(trcpdsr, TRCPDSR); +coresight_etm4x_simple_func(trclsr, TRCLSR); +coresight_etm4x_simple_func(trcconfig, TRCCONFIGR); +coresight_etm4x_simple_func(trctraceid, TRCTRACEIDR); +coresight_etm4x_simple_func(trcauthstatus, TRCAUTHSTATUS); +coresight_etm4x_simple_func(trcdevid, TRCDEVID); +coresight_etm4x_simple_func(trcdevtype, TRCDEVTYPE); +coresight_etm4x_simple_func(trcpidr0, TRCPIDR0); +coresight_etm4x_simple_func(trcpidr1, TRCPIDR1); +coresight_etm4x_simple_func(trcpidr2, TRCPIDR2); +coresight_etm4x_simple_func(trcpidr3, TRCPIDR3); static struct attribute *coresight_etmv4_mgmt_attrs[] = { &dev_attr_trcoslsr.attr, @@ -2080,19 +2073,19 @@ static struct attribute *coresight_etmv4_mgmt_attrs[] = { NULL, }; -coresight_simple_func(trcidr0, TRCIDR0); -coresight_simple_func(trcidr1, TRCIDR1); -coresight_simple_func(trcidr2, TRCIDR2); -coresight_simple_func(trcidr3, TRCIDR3); -coresight_simple_func(trcidr4, TRCIDR4); -coresight_simple_func(trcidr5, TRCIDR5); +coresight_etm4x_simple_func(trcidr0, TRCIDR0); +coresight_etm4x_simple_func(trcidr1, TRCIDR1); +coresight_etm4x_simple_func(trcidr2, TRCIDR2); +coresight_etm4x_simple_func(trcidr3, TRCIDR3); +coresight_etm4x_simple_func(trcidr4, TRCIDR4); +coresight_etm4x_simple_func(trcidr5, TRCIDR5); /* trcidr[6,7] are reserved */ -coresight_simple_func(trcidr8, TRCIDR8); -coresight_simple_func(trcidr9, TRCIDR9); -coresight_simple_func(trcidr10, TRCIDR10); -coresight_simple_func(trcidr11, TRCIDR11); -coresight_simple_func(trcidr12, TRCIDR12); -coresight_simple_func(trcidr13, TRCIDR13); +coresight_etm4x_simple_func(trcidr8, TRCIDR8); +coresight_etm4x_simple_func(trcidr9, TRCIDR9); +coresight_etm4x_simple_func(trcidr10, TRCIDR10); +coresight_etm4x_simple_func(trcidr11, TRCIDR11); +coresight_etm4x_simple_func(trcidr12, TRCIDR12); +coresight_etm4x_simple_func(trcidr13, TRCIDR13); static struct attribute *coresight_etmv4_trcidr_attrs[] = { &dev_attr_trcidr0.attr, diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 333eddaed339..3b5dd95a3588 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -37,6 +37,16 @@ #define ETM_MODE_EXCL_KERN BIT(30) #define ETM_MODE_EXCL_USER BIT(31) +#define coresight_simple_func(type, name, offset) \ +static ssize_t name##_show(struct device *_dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + type *drvdata = dev_get_drvdata(_dev->parent); \ + return scnprintf(buf, PAGE_SIZE, "0x%x\n", \ + readl_relaxed(drvdata->base + offset)); \ +} \ +static DEVICE_ATTR_RO(name) + enum cs_mode { CS_MODE_DISABLED, CS_MODE_SYSFS, From ae1e4d1141cea361cc963da04ce0e1505cd36309 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 5 Apr 2016 11:53:51 -0600 Subject: [PATCH 078/104] coresight: etb10: splitting sysFS "status" entry The sysFS "status" entry conveys a wealth of information about the status of the HW but goes agains the sysFS rule of one topic per file. This patch rectify the situation by adding read-only entries for each of the field formaly displayed by "status". The ABI documentation is kept up to date. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit ad352acbb9d606a5facff31fd96b05d0346726b1) --- .../testing/sysfs-bus-coresight-devices-etb10 | 69 +++++++++++++++-- drivers/hwtracing/coresight/coresight-etb10.c | 77 +++++++++---------- 2 files changed, 98 insertions(+), 48 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-etb10 b/Documentation/ABI/testing/sysfs-bus-coresight-devices-etb10 index 4b8d6ec92e2b..b5f526081711 100644 --- a/Documentation/ABI/testing/sysfs-bus-coresight-devices-etb10 +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-etb10 @@ -6,13 +6,6 @@ Description: (RW) Add/remove a sink from a trace path. There can be multiple source for a single sink. ex: echo 1 > /sys/bus/coresight/devices/20010000.etb/enable_sink -What: /sys/bus/coresight/devices/.etb/status -Date: November 2014 -KernelVersion: 3.19 -Contact: Mathieu Poirier -Description: (R) List various control and status registers. The specific - layout and content is driver specific. - What: /sys/bus/coresight/devices/.etb/trigger_cntr Date: November 2014 KernelVersion: 3.19 @@ -22,3 +15,65 @@ Description: (RW) Disables write access to the Trace RAM by stopping the following the trigger event. The number of 32-bit words written into the Trace RAM following the trigger event is equal to the value stored in this register+1 (from ARM ETB-TRM). + +What: /sys/bus/coresight/devices/.etb/mgmt/rdp +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Defines the depth, in words, of the trace RAM in powers of + 2. The value is read directly from HW register RDP, 0x004. + +What: /sys/bus/coresight/devices/.etb/mgmt/sts +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the ETB status register. The value + is read directly from HW register STS, 0x00C. + +What: /sys/bus/coresight/devices/.etb/mgmt/rrp +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the ETB RAM Read Pointer register + that is used to read entries from the Trace RAM over the APB + interface. The value is read directly from HW register RRP, + 0x014. + +What: /sys/bus/coresight/devices/.etb/mgmt/rwp +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the ETB RAM Write Pointer register + that is used to sets the write pointer to write entries from + the CoreSight bus into the Trace RAM. The value is read directly + from HW register RWP, 0x018. + +What: /sys/bus/coresight/devices/.etb/mgmt/trg +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Similar to "trigger_cntr" above except that this value is + read directly from HW register TRG, 0x01C. + +What: /sys/bus/coresight/devices/.etb/mgmt/ctl +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the ETB Control register. The value + is read directly from HW register CTL, 0x020. + +What: /sys/bus/coresight/devices/.etb/mgmt/ffsr +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the ETB Formatter and Flush Status + register. The value is read directly from HW register FFSR, + 0x300. + +What: /sys/bus/coresight/devices/.etb/mgmt/ffcr +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the ETB Formatter and Flush Control + register. The value is read directly from HW register FFCR, + 0x304. diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index acbce79934d6..8bf4429e1fb3 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -579,47 +579,29 @@ static const struct file_operations etb_fops = { .llseek = no_llseek, }; -static ssize_t status_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long flags; - u32 etb_rdr, etb_sr, etb_rrp, etb_rwp; - u32 etb_trg, etb_cr, etb_ffsr, etb_ffcr; - struct etb_drvdata *drvdata = dev_get_drvdata(dev->parent); +#define coresight_etb10_simple_func(name, offset) \ + coresight_simple_func(struct etb_drvdata, name, offset) - pm_runtime_get_sync(drvdata->dev); - spin_lock_irqsave(&drvdata->spinlock, flags); - CS_UNLOCK(drvdata->base); +coresight_etb10_simple_func(rdp, ETB_RAM_DEPTH_REG); +coresight_etb10_simple_func(sts, ETB_STATUS_REG); +coresight_etb10_simple_func(rrp, ETB_RAM_READ_POINTER); +coresight_etb10_simple_func(rwp, ETB_RAM_WRITE_POINTER); +coresight_etb10_simple_func(trg, ETB_TRG); +coresight_etb10_simple_func(ctl, ETB_CTL_REG); +coresight_etb10_simple_func(ffsr, ETB_FFSR); +coresight_etb10_simple_func(ffcr, ETB_FFCR); - etb_rdr = readl_relaxed(drvdata->base + ETB_RAM_DEPTH_REG); - etb_sr = readl_relaxed(drvdata->base + ETB_STATUS_REG); - etb_rrp = readl_relaxed(drvdata->base + ETB_RAM_READ_POINTER); - etb_rwp = readl_relaxed(drvdata->base + ETB_RAM_WRITE_POINTER); - etb_trg = readl_relaxed(drvdata->base + ETB_TRG); - etb_cr = readl_relaxed(drvdata->base + ETB_CTL_REG); - etb_ffsr = readl_relaxed(drvdata->base + ETB_FFSR); - etb_ffcr = readl_relaxed(drvdata->base + ETB_FFCR); - - CS_LOCK(drvdata->base); - spin_unlock_irqrestore(&drvdata->spinlock, flags); - - pm_runtime_put(drvdata->dev); - - return sprintf(buf, - "Depth:\t\t0x%x\n" - "Status:\t\t0x%x\n" - "RAM read ptr:\t0x%x\n" - "RAM wrt ptr:\t0x%x\n" - "Trigger cnt:\t0x%x\n" - "Control:\t0x%x\n" - "Flush status:\t0x%x\n" - "Flush ctrl:\t0x%x\n", - etb_rdr, etb_sr, etb_rrp, etb_rwp, - etb_trg, etb_cr, etb_ffsr, etb_ffcr); - - return -EINVAL; -} -static DEVICE_ATTR_RO(status); +static struct attribute *coresight_etb_mgmt_attrs[] = { + &dev_attr_rdp.attr, + &dev_attr_sts.attr, + &dev_attr_rrp.attr, + &dev_attr_rwp.attr, + &dev_attr_trg.attr, + &dev_attr_ctl.attr, + &dev_attr_ffsr.attr, + &dev_attr_ffcr.attr, + NULL, +}; static ssize_t trigger_cntr_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -649,10 +631,23 @@ static DEVICE_ATTR_RW(trigger_cntr); static struct attribute *coresight_etb_attrs[] = { &dev_attr_trigger_cntr.attr, - &dev_attr_status.attr, NULL, }; -ATTRIBUTE_GROUPS(coresight_etb); + +static const struct attribute_group coresight_etb_group = { + .attrs = coresight_etb_attrs, +}; + +static const struct attribute_group coresight_etb_mgmt_group = { + .attrs = coresight_etb_mgmt_attrs, + .name = "mgmt", +}; + +const struct attribute_group *coresight_etb_groups[] = { + &coresight_etb_group, + &coresight_etb_mgmt_group, + NULL, +}; static int etb_probe(struct amba_device *adev, const struct amba_id *id) { From 3f4440648d25bbf3d91b0353aad02e8535cf899e Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 5 Apr 2016 11:53:52 -0600 Subject: [PATCH 079/104] coresight: removing gratuitous boot time log messages Removing boot time log for drivers that don't report useful information other than they came up properly. The same information can be found in sysFS once the system has booted and as such doesn't provide any value in the boot log. Reported-by: Mark Brown Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit ef0fd640e3312b8164ec43e1eff24769a7c08b7f) --- drivers/hwtracing/coresight/coresight-etb10.c | 1 - drivers/hwtracing/coresight/coresight-funnel.c | 1 - drivers/hwtracing/coresight/coresight-replicator.c | 1 - drivers/hwtracing/coresight/coresight-tmc.c | 1 - drivers/hwtracing/coresight/coresight-tpiu.c | 1 - 5 files changed, 5 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 8bf4429e1fb3..2461d5c8c157 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -724,7 +724,6 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id) if (ret) goto err_misc_register; - dev_info(dev, "ETB initialized\n"); return 0; err_misc_register: diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 0600ca30649d..05df789056cc 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -221,7 +221,6 @@ static int funnel_probe(struct amba_device *adev, const struct amba_id *id) if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); - dev_info(dev, "FUNNEL initialized\n"); return 0; } diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index 4299c0569340..c6982e312e15 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -114,7 +114,6 @@ static int replicator_probe(struct platform_device *pdev) pm_runtime_put(&pdev->dev); - dev_info(dev, "REPLICATOR initialized\n"); return 0; out_disable_pm: diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 1be191f5d39c..0a4db1ac52ae 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -754,7 +754,6 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) if (ret) goto err_misc_register; - dev_info(dev, "TMC initialized\n"); return 0; err_misc_register: diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index 8fb09d9237ab..4e471e2e9d89 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -167,7 +167,6 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id) if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); - dev_info(dev, "TPIU initialized\n"); return 0; } From 0a1d16cf4f923e224b49f37855a94ee3ae97344d Mon Sep 17 00:00:00 2001 From: Li Pengcheng Date: Tue, 3 May 2016 11:33:35 -0600 Subject: [PATCH 080/104] coresight: no need to do the forced type conversion activated and enable are already unsigned type, no need to change them to unsigned. Signed-off-by: Li Pengcheng Signed-off-by: Li Zhong Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit e8dc27d0ee458f9622b50e2d9476719b3a0e686b) --- drivers/hwtracing/coresight/coresight.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 2ea5961092c1..945bc31f0ec5 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -514,7 +514,7 @@ static ssize_t enable_sink_show(struct device *dev, { struct coresight_device *csdev = to_coresight_device(dev); - return scnprintf(buf, PAGE_SIZE, "%u\n", (unsigned)csdev->activated); + return scnprintf(buf, PAGE_SIZE, "%u\n", csdev->activated); } static ssize_t enable_sink_store(struct device *dev, @@ -544,7 +544,7 @@ static ssize_t enable_source_show(struct device *dev, { struct coresight_device *csdev = to_coresight_device(dev); - return scnprintf(buf, PAGE_SIZE, "%u\n", (unsigned)csdev->enable); + return scnprintf(buf, PAGE_SIZE, "%u\n", csdev->enable); } static ssize_t enable_source_store(struct device *dev, From b1ffdd7ebb35dbd535cb37476b4819ab45c57e4e Mon Sep 17 00:00:00 2001 From: Li Pengcheng Date: Tue, 3 May 2016 11:33:36 -0600 Subject: [PATCH 081/104] coresight: etm4x: modify q_support type Because this operation exceed the range of boolean, so we should modify q_support to unit8 bit. drvdata->q_support = BMVAL(etmidr0, 15, 16) Signed-off-by: Li Pengcheng Signed-off-by: Li Zhong Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 6327a454a8ab0dcab24a647367d216c1b84020c6) --- drivers/hwtracing/coresight/coresight-etm4x.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index a291d4cc8bd8..5359c5197c1d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -372,6 +372,7 @@ struct etmv4_drvdata { u8 ccitmin; u8 s_ex_level; u8 ns_ex_level; + u8 q_support; bool sticky_enable; bool boot_enable; bool os_unlock; @@ -380,7 +381,6 @@ struct etmv4_drvdata { bool trccond; bool retstack; bool trccci; - bool q_support; bool trc_error; bool syncpr; bool stallctl; From 27e5d1a7ac405242327797a69dfc82235d86d513 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:38 -0600 Subject: [PATCH 082/104] coresight: adding path for STM device >From a core framework point of view an STM device is a source that is treated the same way as any other tracers. Unlike tracers though STM devices are not associated with a CPU. As such it doesn't make sense to associate the path from an STM device to its sink with a per-cpu variable as it is done for tracers. This patch simply adds another global variable to keep STM paths and the processing in coresight_enable/disable() is updated to deal with STM devices properly. Signed-off-by: Mathieu Poirier Signed-off-by: Chunyan Zhang Signed-off-by: Greg Kroah-Hartman (cherry picked from commit a685d68328f14579b2e68d6a3a2066089cffbf98) --- drivers/hwtracing/coresight/coresight.c | 106 ++++++++++++++++++------ 1 file changed, 82 insertions(+), 24 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 945bc31f0ec5..0995df8f85bc 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -43,7 +43,15 @@ struct coresight_node { * When operating Coresight drivers from the sysFS interface, only a single * path can exist from a tracer (associated to a CPU) to a sink. */ -static DEFINE_PER_CPU(struct list_head *, sysfs_path); +static DEFINE_PER_CPU(struct list_head *, tracer_path); + +/* + * As of this writing only a single STM can be found in CS topologies. Since + * there is no way to know if we'll ever see more and what kind of + * configuration they will enact, for the time being only define a single path + * for STM. + */ +static struct list_head *stm_path; static int coresight_id_match(struct device *dev, void *data) { @@ -432,18 +440,45 @@ void coresight_release_path(struct list_head *path) path = NULL; } +/** coresight_validate_source - make sure a source has the right credentials + * @csdev: the device structure for a source. + * @function: the function this was called from. + * + * Assumes the coresight_mutex is held. + */ +static int coresight_validate_source(struct coresight_device *csdev, + const char *function) +{ + u32 type, subtype; + + type = csdev->type; + subtype = csdev->subtype.source_subtype; + + if (type != CORESIGHT_DEV_TYPE_SOURCE) { + dev_err(&csdev->dev, "wrong device type in %s\n", function); + return -EINVAL; + } + + if (subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_PROC && + subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE) { + dev_err(&csdev->dev, "wrong device subtype in %s\n", function); + return -EINVAL; + } + + return 0; +} + int coresight_enable(struct coresight_device *csdev) { - int ret = 0; - int cpu; + int cpu, ret = 0; struct list_head *path; mutex_lock(&coresight_mutex); - if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) { - ret = -EINVAL; - dev_err(&csdev->dev, "wrong device type in %s\n", __func__); + + ret = coresight_validate_source(csdev, __func__); + if (ret) goto out; - } + if (csdev->enable) goto out; @@ -461,15 +496,25 @@ int coresight_enable(struct coresight_device *csdev) if (ret) goto err_source; - /* - * When working from sysFS it is important to keep track - * of the paths that were created so that they can be - * undone in 'coresight_disable()'. Since there can only - * be a single session per tracer (when working from sysFS) - * a per-cpu variable will do just fine. - */ - cpu = source_ops(csdev)->cpu_id(csdev); - per_cpu(sysfs_path, cpu) = path; + switch (csdev->subtype.source_subtype) { + case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC: + /* + * When working from sysFS it is important to keep track + * of the paths that were created so that they can be + * undone in 'coresight_disable()'. Since there can only + * be a single session per tracer (when working from sysFS) + * a per-cpu variable will do just fine. + */ + cpu = source_ops(csdev)->cpu_id(csdev); + per_cpu(tracer_path, cpu) = path; + break; + case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE: + stm_path = path; + break; + default: + /* We can't be here */ + break; + } out: mutex_unlock(&coresight_mutex); @@ -486,23 +531,36 @@ EXPORT_SYMBOL_GPL(coresight_enable); void coresight_disable(struct coresight_device *csdev) { - int cpu; - struct list_head *path; + int cpu, ret; + struct list_head *path = NULL; mutex_lock(&coresight_mutex); - if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) { - dev_err(&csdev->dev, "wrong device type in %s\n", __func__); + + ret = coresight_validate_source(csdev, __func__); + if (ret) goto out; - } + if (!csdev->enable) goto out; - cpu = source_ops(csdev)->cpu_id(csdev); - path = per_cpu(sysfs_path, cpu); + switch (csdev->subtype.source_subtype) { + case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC: + cpu = source_ops(csdev)->cpu_id(csdev); + path = per_cpu(tracer_path, cpu); + per_cpu(tracer_path, cpu) = NULL; + break; + case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE: + path = stm_path; + stm_path = NULL; + break; + default: + /* We can't be here */ + break; + } + coresight_disable_source(csdev); coresight_disable_path(path); coresight_release_path(path); - per_cpu(sysfs_path, cpu) = NULL; out: mutex_unlock(&coresight_mutex); From feff85e807c07a11794da2ad4d5a4d8a0368dbfd Mon Sep 17 00:00:00 2001 From: Pratik Patel Date: Tue, 3 May 2016 11:33:40 -0600 Subject: [PATCH 083/104] coresight: stm: adding driver for CoreSight STM component This driver adds support for the STM CoreSight IP block, allowing any system compoment (HW or SW) to log and aggregate messages via a single entity. The CoreSight STM exposes an application defined number of channels called stimulus port. Configuration is done using entries in sysfs and channels made available to userspace via configfs. Signed-off-by: Pratik Patel Signed-off-by: Mathieu Poirier Reviewed-by: Michael Williams Signed-off-by: Chunyan Zhang Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 237483aa5cf43105d148d3f03b29eed47c3e6cf9) --- .../testing/sysfs-bus-coresight-devices-stm | 53 + Documentation/trace/coresight.txt | 37 +- drivers/hwtracing/coresight/Kconfig | 11 + drivers/hwtracing/coresight/Makefile | 1 + drivers/hwtracing/coresight/coresight-stm.c | 920 ++++++++++++++++++ include/linux/coresight-stm.h | 6 + include/uapi/linux/coresight-stm.h | 21 + 7 files changed, 1047 insertions(+), 2 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-coresight-devices-stm create mode 100644 drivers/hwtracing/coresight/coresight-stm.c create mode 100644 include/linux/coresight-stm.h create mode 100644 include/uapi/linux/coresight-stm.h diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-stm b/Documentation/ABI/testing/sysfs-bus-coresight-devices-stm new file mode 100644 index 000000000000..1dffabe7f48d --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-stm @@ -0,0 +1,53 @@ +What: /sys/bus/coresight/devices/.stm/enable_source +Date: April 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (RW) Enable/disable tracing on this specific trace macrocell. + Enabling the trace macrocell implies it has been configured + properly and a sink has been identified for it. The path + of coresight components linking the source to the sink is + configured and managed automatically by the coresight framework. + +What: /sys/bus/coresight/devices/.stm/hwevent_enable +Date: April 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (RW) Provides access to the HW event enable register, used in + conjunction with HW event bank select register. + +What: /sys/bus/coresight/devices/.stm/hwevent_select +Date: April 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (RW) Gives access to the HW event block select register + (STMHEBSR) in order to configure up to 256 channels. Used in + conjunction with "hwevent_enable" register as described above. + +What: /sys/bus/coresight/devices/.stm/port_enable +Date: April 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (RW) Provides access to the stimulus port enable register + (STMSPER). Used in conjunction with "port_select" described + below. + +What: /sys/bus/coresight/devices/.stm/port_select +Date: April 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (RW) Used to determine which bank of stimulus port bit in + register STMSPER (see above) apply to. + +What: /sys/bus/coresight/devices/.stm/status +Date: April 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) List various control and status registers. The specific + layout and content is driver specific. + +What: /sys/bus/coresight/devices/.stm/traceid +Date: April 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (RW) Holds the trace ID that will appear in the trace stream + coming from this trace entity. diff --git a/Documentation/trace/coresight.txt b/Documentation/trace/coresight.txt index 0a5c3290e732..a33c88cd5d1d 100644 --- a/Documentation/trace/coresight.txt +++ b/Documentation/trace/coresight.txt @@ -190,8 +190,8 @@ expected to be accessed and controlled using those entries. Last but not least, "struct module *owner" is expected to be set to reflect the information carried in "THIS_MODULE". -How to use ----------- +How to use the tracer modules +----------------------------- Before trace collection can start, a coresight sink needs to be identify. There is no limit on the amount of sinks (nor sources) that can be enabled at @@ -297,3 +297,36 @@ Info Tracing enabled Instruction 13570831 0x8026B584 E28DD00C false ADD sp,sp,#0xc Instruction 0 0x8026B588 E8BD8000 true LDM sp!,{pc} Timestamp Timestamp: 17107041535 + +How to use the STM module +------------------------- + +Using the System Trace Macrocell module is the same as the tracers - the only +difference is that clients are driving the trace capture rather +than the program flow through the code. + +As with any other CoreSight component, specifics about the STM tracer can be +found in sysfs with more information on each entry being found in [1]: + +root@genericarmv8:~# ls /sys/bus/coresight/devices/20100000.stm +enable_source hwevent_select port_enable subsystem uevent +hwevent_enable mgmt port_select traceid +root@genericarmv8:~# + +Like any other source a sink needs to be identified and the STM enabled before +being used: + +root@genericarmv8:~# echo 1 > /sys/bus/coresight/devices/20010000.etf/enable_sink +root@genericarmv8:~# echo 1 > /sys/bus/coresight/devices/20100000.stm/enable_source + +From there user space applications can request and use channels using the devfs +interface provided for that purpose by the generic STM API: + +root@genericarmv8:~# ls -l /dev/20100000.stm +crw------- 1 root root 10, 61 Jan 3 18:11 /dev/20100000.stm +root@genericarmv8:~# + +Details on how to use the generic STM API can be found here [2]. + +[1]. Documentation/ABI/testing/sysfs-bus-coresight-devices-stm +[2]. Documentation/trace/stm.txt diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index db0541031c72..130cb2114059 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -78,4 +78,15 @@ config CORESIGHT_QCOM_REPLICATOR programmable ATB replicator sends the ATB trace stream from the ETB/ETF to the TPIUi and ETR. +config CORESIGHT_STM + bool "CoreSight System Trace Macrocell driver" + depends on (ARM && !(CPU_32v3 || CPU_32v4 || CPU_32v4T)) || ARM64 + select CORESIGHT_LINKS_AND_SINKS + select STM + help + This driver provides support for hardware assisted software + instrumentation based tracing. This is primarily used for + logging useful software events or data coming from various entities + in the system, possibly running different OSs + endif diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 1d0e32c7dbe4..c6f84b57f52a 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \ coresight-etm4x-sysfs.o obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o +obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c new file mode 100644 index 000000000000..73be58a11e4f --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -0,0 +1,920 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * Description: CoreSight System Trace Macrocell driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Initial implementation by Pratik Patel + * (C) 2014-2015 Pratik Patel + * + * Serious refactoring, code cleanup and upgrading to the Coresight upstream + * framework by Mathieu Poirier + * (C) 2015-2016 Mathieu Poirier + * + * Guaranteed timing and support for various packet type coming from the + * generic STM API by Chunyan Zhang + * (C) 2015-2016 Chunyan Zhang + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "coresight-priv.h" + +#define STMDMASTARTR 0xc04 +#define STMDMASTOPR 0xc08 +#define STMDMASTATR 0xc0c +#define STMDMACTLR 0xc10 +#define STMDMAIDR 0xcfc +#define STMHEER 0xd00 +#define STMHETER 0xd20 +#define STMHEBSR 0xd60 +#define STMHEMCR 0xd64 +#define STMHEMASTR 0xdf4 +#define STMHEFEAT1R 0xdf8 +#define STMHEIDR 0xdfc +#define STMSPER 0xe00 +#define STMSPTER 0xe20 +#define STMPRIVMASKR 0xe40 +#define STMSPSCR 0xe60 +#define STMSPMSCR 0xe64 +#define STMSPOVERRIDER 0xe68 +#define STMSPMOVERRIDER 0xe6c +#define STMSPTRIGCSR 0xe70 +#define STMTCSR 0xe80 +#define STMTSSTIMR 0xe84 +#define STMTSFREQR 0xe8c +#define STMSYNCR 0xe90 +#define STMAUXCR 0xe94 +#define STMSPFEAT1R 0xea0 +#define STMSPFEAT2R 0xea4 +#define STMSPFEAT3R 0xea8 +#define STMITTRIGGER 0xee8 +#define STMITATBDATA0 0xeec +#define STMITATBCTR2 0xef0 +#define STMITATBID 0xef4 +#define STMITATBCTR0 0xef8 + +#define STM_32_CHANNEL 32 +#define BYTES_PER_CHANNEL 256 +#define STM_TRACE_BUF_SIZE 4096 +#define STM_SW_MASTER_END 127 + +/* Register bit definition */ +#define STMTCSR_BUSY_BIT 23 +/* Reserve the first 10 channels for kernel usage */ +#define STM_CHANNEL_OFFSET 0 + +enum stm_pkt_type { + STM_PKT_TYPE_DATA = 0x98, + STM_PKT_TYPE_FLAG = 0xE8, + STM_PKT_TYPE_TRIG = 0xF8, +}; + +#define stm_channel_addr(drvdata, ch) (drvdata->chs.base + \ + (ch * BYTES_PER_CHANNEL)) +#define stm_channel_off(type, opts) (type & ~opts) + +static int boot_nr_channel; + +/* + * Not really modular but using module_param is the easiest way to + * remain consistent with existing use cases for now. + */ +module_param_named( + boot_nr_channel, boot_nr_channel, int, S_IRUGO +); + +/** + * struct channel_space - central management entity for extended ports + * @base: memory mapped base address where channels start. + * @guaraneed: is the channel delivery guaranteed. + */ +struct channel_space { + void __iomem *base; + unsigned long *guaranteed; +}; + +/** + * struct stm_drvdata - specifics associated to an STM component + * @base: memory mapped base address for this component. + * @dev: the device entity associated to this component. + * @atclk: optional clock for the core parts of the STM. + * @csdev: component vitals needed by the framework. + * @spinlock: only one at a time pls. + * @chs: the channels accociated to this STM. + * @stm: structure associated to the generic STM interface. + * @mode: this tracer's mode, i.e sysFS, or disabled. + * @traceid: value of the current ID for this component. + * @write_bytes: Maximus bytes this STM can write at a time. + * @stmsper: settings for register STMSPER. + * @stmspscr: settings for register STMSPSCR. + * @numsp: the total number of stimulus port support by this STM. + * @stmheer: settings for register STMHEER. + * @stmheter: settings for register STMHETER. + * @stmhebsr: settings for register STMHEBSR. + */ +struct stm_drvdata { + void __iomem *base; + struct device *dev; + struct clk *atclk; + struct coresight_device *csdev; + spinlock_t spinlock; + struct channel_space chs; + struct stm_data stm; + local_t mode; + u8 traceid; + u32 write_bytes; + u32 stmsper; + u32 stmspscr; + u32 numsp; + u32 stmheer; + u32 stmheter; + u32 stmhebsr; +}; + +static void stm_hwevent_enable_hw(struct stm_drvdata *drvdata) +{ + CS_UNLOCK(drvdata->base); + + writel_relaxed(drvdata->stmhebsr, drvdata->base + STMHEBSR); + writel_relaxed(drvdata->stmheter, drvdata->base + STMHETER); + writel_relaxed(drvdata->stmheer, drvdata->base + STMHEER); + writel_relaxed(0x01 | /* Enable HW event tracing */ + 0x04, /* Error detection on event tracing */ + drvdata->base + STMHEMCR); + + CS_LOCK(drvdata->base); +} + +static void stm_port_enable_hw(struct stm_drvdata *drvdata) +{ + CS_UNLOCK(drvdata->base); + /* ATB trigger enable on direct writes to TRIG locations */ + writel_relaxed(0x10, + drvdata->base + STMSPTRIGCSR); + writel_relaxed(drvdata->stmspscr, drvdata->base + STMSPSCR); + writel_relaxed(drvdata->stmsper, drvdata->base + STMSPER); + + CS_LOCK(drvdata->base); +} + +static void stm_enable_hw(struct stm_drvdata *drvdata) +{ + if (drvdata->stmheer) + stm_hwevent_enable_hw(drvdata); + + stm_port_enable_hw(drvdata); + + CS_UNLOCK(drvdata->base); + + /* 4096 byte between synchronisation packets */ + writel_relaxed(0xFFF, drvdata->base + STMSYNCR); + writel_relaxed((drvdata->traceid << 16 | /* trace id */ + 0x02 | /* timestamp enable */ + 0x01), /* global STM enable */ + drvdata->base + STMTCSR); + + CS_LOCK(drvdata->base); +} + +static int stm_enable(struct coresight_device *csdev, + struct perf_event_attr *attr, u32 mode) +{ + u32 val; + struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (mode != CS_MODE_SYSFS) + return -EINVAL; + + val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode); + + /* Someone is already using the tracer */ + if (val) + return -EBUSY; + + pm_runtime_get_sync(drvdata->dev); + + spin_lock(&drvdata->spinlock); + stm_enable_hw(drvdata); + spin_unlock(&drvdata->spinlock); + + dev_info(drvdata->dev, "STM tracing enabled\n"); + return 0; +} + +static void stm_hwevent_disable_hw(struct stm_drvdata *drvdata) +{ + CS_UNLOCK(drvdata->base); + + writel_relaxed(0x0, drvdata->base + STMHEMCR); + writel_relaxed(0x0, drvdata->base + STMHEER); + writel_relaxed(0x0, drvdata->base + STMHETER); + + CS_LOCK(drvdata->base); +} + +static void stm_port_disable_hw(struct stm_drvdata *drvdata) +{ + CS_UNLOCK(drvdata->base); + + writel_relaxed(0x0, drvdata->base + STMSPER); + writel_relaxed(0x0, drvdata->base + STMSPTRIGCSR); + + CS_LOCK(drvdata->base); +} + +static void stm_disable_hw(struct stm_drvdata *drvdata) +{ + u32 val; + + CS_UNLOCK(drvdata->base); + + val = readl_relaxed(drvdata->base + STMTCSR); + val &= ~0x1; /* clear global STM enable [0] */ + writel_relaxed(val, drvdata->base + STMTCSR); + + CS_LOCK(drvdata->base); + + stm_port_disable_hw(drvdata); + if (drvdata->stmheer) + stm_hwevent_disable_hw(drvdata); +} + +static void stm_disable(struct coresight_device *csdev) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + /* + * For as long as the tracer isn't disabled another entity can't + * change its status. As such we can read the status here without + * fearing it will change under us. + */ + if (local_read(&drvdata->mode) == CS_MODE_SYSFS) { + spin_lock(&drvdata->spinlock); + stm_disable_hw(drvdata); + spin_unlock(&drvdata->spinlock); + + /* Wait until the engine has completely stopped */ + coresight_timeout(drvdata, STMTCSR, STMTCSR_BUSY_BIT, 0); + + pm_runtime_put(drvdata->dev); + + local_set(&drvdata->mode, CS_MODE_DISABLED); + dev_info(drvdata->dev, "STM tracing disabled\n"); + } +} + +static int stm_trace_id(struct coresight_device *csdev) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + return drvdata->traceid; +} + +static const struct coresight_ops_source stm_source_ops = { + .trace_id = stm_trace_id, + .enable = stm_enable, + .disable = stm_disable, +}; + +static const struct coresight_ops stm_cs_ops = { + .source_ops = &stm_source_ops, +}; + +static inline bool stm_addr_unaligned(const void *addr, u8 write_bytes) +{ + return ((unsigned long)addr & (write_bytes - 1)); +} + +static void stm_send(void *addr, const void *data, u32 size, u8 write_bytes) +{ + u8 paload[8]; + + if (stm_addr_unaligned(data, write_bytes)) { + memcpy(paload, data, size); + data = paload; + } + + /* now we are 64bit/32bit aligned */ + switch (size) { +#ifdef CONFIG_64BIT + case 8: + writeq_relaxed(*(u64 *)data, addr); + break; +#endif + case 4: + writel_relaxed(*(u32 *)data, addr); + break; + case 2: + writew_relaxed(*(u16 *)data, addr); + break; + case 1: + writeb_relaxed(*(u8 *)data, addr); + break; + default: + break; + } +} + +static int stm_generic_link(struct stm_data *stm_data, + unsigned int master, unsigned int channel) +{ + struct stm_drvdata *drvdata = container_of(stm_data, + struct stm_drvdata, stm); + if (!drvdata || !drvdata->csdev) + return -EINVAL; + + return coresight_enable(drvdata->csdev); +} + +static void stm_generic_unlink(struct stm_data *stm_data, + unsigned int master, unsigned int channel) +{ + struct stm_drvdata *drvdata = container_of(stm_data, + struct stm_drvdata, stm); + if (!drvdata || !drvdata->csdev) + return; + + stm_disable(drvdata->csdev); +} + +static long stm_generic_set_options(struct stm_data *stm_data, + unsigned int master, + unsigned int channel, + unsigned int nr_chans, + unsigned long options) +{ + struct stm_drvdata *drvdata = container_of(stm_data, + struct stm_drvdata, stm); + if (!(drvdata && local_read(&drvdata->mode))) + return -EINVAL; + + if (channel >= drvdata->numsp) + return -EINVAL; + + switch (options) { + case STM_OPTION_GUARANTEED: + set_bit(channel, drvdata->chs.guaranteed); + break; + + case STM_OPTION_INVARIANT: + clear_bit(channel, drvdata->chs.guaranteed); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static ssize_t stm_generic_packet(struct stm_data *stm_data, + unsigned int master, + unsigned int channel, + unsigned int packet, + unsigned int flags, + unsigned int size, + const unsigned char *payload) +{ + unsigned long ch_addr; + struct stm_drvdata *drvdata = container_of(stm_data, + struct stm_drvdata, stm); + + if (!(drvdata && local_read(&drvdata->mode))) + return 0; + + if (channel >= drvdata->numsp) + return 0; + + ch_addr = (unsigned long)stm_channel_addr(drvdata, channel); + + flags = (flags == STP_PACKET_TIMESTAMPED) ? STM_FLAG_TIMESTAMPED : 0; + flags |= test_bit(channel, drvdata->chs.guaranteed) ? + STM_FLAG_GUARANTEED : 0; + + if (size > drvdata->write_bytes) + size = drvdata->write_bytes; + else + size = rounddown_pow_of_two(size); + + switch (packet) { + case STP_PACKET_FLAG: + ch_addr |= stm_channel_off(STM_PKT_TYPE_FLAG, flags); + + /* + * The generic STM core sets a size of '0' on flag packets. + * As such send a flag packet of size '1' and tell the + * core we did so. + */ + stm_send((void *)ch_addr, payload, 1, drvdata->write_bytes); + size = 1; + break; + + case STP_PACKET_DATA: + ch_addr |= stm_channel_off(STM_PKT_TYPE_DATA, flags); + stm_send((void *)ch_addr, payload, size, + drvdata->write_bytes); + break; + + default: + return -ENOTSUPP; + } + + return size; +} + +static ssize_t hwevent_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val = drvdata->stmheer; + + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t hwevent_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val; + int ret = 0; + + ret = kstrtoul(buf, 16, &val); + if (ret) + return -EINVAL; + + drvdata->stmheer = val; + /* HW event enable and trigger go hand in hand */ + drvdata->stmheter = val; + + return size; +} +static DEVICE_ATTR_RW(hwevent_enable); + +static ssize_t hwevent_select_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val = drvdata->stmhebsr; + + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t hwevent_select_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val; + int ret = 0; + + ret = kstrtoul(buf, 16, &val); + if (ret) + return -EINVAL; + + drvdata->stmhebsr = val; + + return size; +} +static DEVICE_ATTR_RW(hwevent_select); + +static ssize_t port_select_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val; + + if (!local_read(&drvdata->mode)) { + val = drvdata->stmspscr; + } else { + spin_lock(&drvdata->spinlock); + val = readl_relaxed(drvdata->base + STMSPSCR); + spin_unlock(&drvdata->spinlock); + } + + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t port_select_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val, stmsper; + int ret = 0; + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + drvdata->stmspscr = val; + + if (local_read(&drvdata->mode)) { + CS_UNLOCK(drvdata->base); + /* Process as per ARM's TRM recommendation */ + stmsper = readl_relaxed(drvdata->base + STMSPER); + writel_relaxed(0x0, drvdata->base + STMSPER); + writel_relaxed(drvdata->stmspscr, drvdata->base + STMSPSCR); + writel_relaxed(stmsper, drvdata->base + STMSPER); + CS_LOCK(drvdata->base); + } + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(port_select); + +static ssize_t port_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val; + + if (!local_read(&drvdata->mode)) { + val = drvdata->stmsper; + } else { + spin_lock(&drvdata->spinlock); + val = readl_relaxed(drvdata->base + STMSPER); + spin_unlock(&drvdata->spinlock); + } + + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t port_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val; + int ret = 0; + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + drvdata->stmsper = val; + + if (local_read(&drvdata->mode)) { + CS_UNLOCK(drvdata->base); + writel_relaxed(drvdata->stmsper, drvdata->base + STMSPER); + CS_LOCK(drvdata->base); + } + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(port_enable); + +static ssize_t traceid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->traceid; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t traceid_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + /* traceid field is 7bit wide on STM32 */ + drvdata->traceid = val & 0x7f; + return size; +} +static DEVICE_ATTR_RW(traceid); + +#define coresight_stm_simple_func(name, offset) \ + coresight_simple_func(struct stm_drvdata, name, offset) + +coresight_stm_simple_func(tcsr, STMTCSR); +coresight_stm_simple_func(tsfreqr, STMTSFREQR); +coresight_stm_simple_func(syncr, STMSYNCR); +coresight_stm_simple_func(sper, STMSPER); +coresight_stm_simple_func(spter, STMSPTER); +coresight_stm_simple_func(privmaskr, STMPRIVMASKR); +coresight_stm_simple_func(spscr, STMSPSCR); +coresight_stm_simple_func(spmscr, STMSPMSCR); +coresight_stm_simple_func(spfeat1r, STMSPFEAT1R); +coresight_stm_simple_func(spfeat2r, STMSPFEAT2R); +coresight_stm_simple_func(spfeat3r, STMSPFEAT3R); +coresight_stm_simple_func(devid, CORESIGHT_DEVID); + +static struct attribute *coresight_stm_attrs[] = { + &dev_attr_hwevent_enable.attr, + &dev_attr_hwevent_select.attr, + &dev_attr_port_enable.attr, + &dev_attr_port_select.attr, + &dev_attr_traceid.attr, + NULL, +}; + +static struct attribute *coresight_stm_mgmt_attrs[] = { + &dev_attr_tcsr.attr, + &dev_attr_tsfreqr.attr, + &dev_attr_syncr.attr, + &dev_attr_sper.attr, + &dev_attr_spter.attr, + &dev_attr_privmaskr.attr, + &dev_attr_spscr.attr, + &dev_attr_spmscr.attr, + &dev_attr_spfeat1r.attr, + &dev_attr_spfeat2r.attr, + &dev_attr_spfeat3r.attr, + &dev_attr_devid.attr, + NULL, +}; + +static const struct attribute_group coresight_stm_group = { + .attrs = coresight_stm_attrs, +}; + +static const struct attribute_group coresight_stm_mgmt_group = { + .attrs = coresight_stm_mgmt_attrs, + .name = "mgmt", +}; + +static const struct attribute_group *coresight_stm_groups[] = { + &coresight_stm_group, + &coresight_stm_mgmt_group, + NULL, +}; + +static int stm_get_resource_byname(struct device_node *np, + char *ch_base, struct resource *res) +{ + const char *name = NULL; + int index = 0, found = 0; + + while (!of_property_read_string_index(np, "reg-names", index, &name)) { + if (strcmp(ch_base, name)) { + index++; + continue; + } + + /* We have a match and @index is where it's at */ + found = 1; + break; + } + + if (!found) + return -EINVAL; + + return of_address_to_resource(np, index, res); +} + +static u32 stm_fundamental_data_size(struct stm_drvdata *drvdata) +{ + u32 stmspfeat2r; + + if (!IS_ENABLED(CONFIG_64BIT)) + return 4; + + stmspfeat2r = readl_relaxed(drvdata->base + STMSPFEAT2R); + + /* + * bit[15:12] represents the fundamental data size + * 0 - 32-bit data + * 1 - 64-bit data + */ + return BMVAL(stmspfeat2r, 12, 15) ? 8 : 4; +} + +static u32 stm_num_stimulus_port(struct stm_drvdata *drvdata) +{ + u32 numsp; + + numsp = readl_relaxed(drvdata->base + CORESIGHT_DEVID); + /* + * NUMPS in STMDEVID is 17 bit long and if equal to 0x0, + * 32 stimulus ports are supported. + */ + numsp &= 0x1ffff; + if (!numsp) + numsp = STM_32_CHANNEL; + return numsp; +} + +static void stm_init_default_data(struct stm_drvdata *drvdata) +{ + /* Don't use port selection */ + drvdata->stmspscr = 0x0; + /* + * Enable all channel regardless of their number. When port + * selection isn't used (see above) STMSPER applies to all + * 32 channel group available, hence setting all 32 bits to 1 + */ + drvdata->stmsper = ~0x0; + + /* + * The trace ID value for *ETM* tracers start at CPU_ID * 2 + 0x10 and + * anything equal to or higher than 0x70 is reserved. Since 0x00 is + * also reserved the STM trace ID needs to be higher than 0x00 and + * lowner than 0x10. + */ + drvdata->traceid = 0x1; + + /* Set invariant transaction timing on all channels */ + bitmap_clear(drvdata->chs.guaranteed, 0, drvdata->numsp); +} + +static void stm_init_generic_data(struct stm_drvdata *drvdata) +{ + drvdata->stm.name = dev_name(drvdata->dev); + + /* + * MasterIDs are assigned at HW design phase. As such the core is + * using a single master for interaction with this device. + */ + drvdata->stm.sw_start = 1; + drvdata->stm.sw_end = 1; + drvdata->stm.hw_override = true; + drvdata->stm.sw_nchannels = drvdata->numsp; + drvdata->stm.packet = stm_generic_packet; + drvdata->stm.link = stm_generic_link; + drvdata->stm.unlink = stm_generic_unlink; + drvdata->stm.set_options = stm_generic_set_options; +} + +static int stm_probe(struct amba_device *adev, const struct amba_id *id) +{ + int ret; + void __iomem *base; + unsigned long *guaranteed; + struct device *dev = &adev->dev; + struct coresight_platform_data *pdata = NULL; + struct stm_drvdata *drvdata; + struct resource *res = &adev->res; + struct resource ch_res; + size_t res_size, bitmap_size; + struct coresight_desc *desc; + struct device_node *np = adev->dev.of_node; + + if (np) { + pdata = of_get_coresight_platform_data(dev, np); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + adev->dev.platform_data = pdata; + } + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->dev = &adev->dev; + drvdata->atclk = devm_clk_get(&adev->dev, "atclk"); /* optional */ + if (!IS_ERR(drvdata->atclk)) { + ret = clk_prepare_enable(drvdata->atclk); + if (ret) + return ret; + } + dev_set_drvdata(dev, drvdata); + + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + drvdata->base = base; + + ret = stm_get_resource_byname(np, "stm-stimulus-base", &ch_res); + if (ret) + return ret; + + base = devm_ioremap_resource(dev, &ch_res); + if (IS_ERR(base)) + return PTR_ERR(base); + drvdata->chs.base = base; + + drvdata->write_bytes = stm_fundamental_data_size(drvdata); + + if (boot_nr_channel) { + drvdata->numsp = boot_nr_channel; + res_size = min((resource_size_t)(boot_nr_channel * + BYTES_PER_CHANNEL), resource_size(res)); + } else { + drvdata->numsp = stm_num_stimulus_port(drvdata); + res_size = min((resource_size_t)(drvdata->numsp * + BYTES_PER_CHANNEL), resource_size(res)); + } + bitmap_size = BITS_TO_LONGS(drvdata->numsp) * sizeof(long); + + guaranteed = devm_kzalloc(dev, bitmap_size, GFP_KERNEL); + if (!guaranteed) + return -ENOMEM; + drvdata->chs.guaranteed = guaranteed; + + spin_lock_init(&drvdata->spinlock); + + stm_init_default_data(drvdata); + stm_init_generic_data(drvdata); + + if (stm_register_device(dev, &drvdata->stm, THIS_MODULE)) { + dev_info(dev, + "stm_register_device failed, probing deffered\n"); + return -EPROBE_DEFER; + } + + desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); + if (!desc) { + ret = -ENOMEM; + goto stm_unregister; + } + + desc->type = CORESIGHT_DEV_TYPE_SOURCE; + desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE; + desc->ops = &stm_cs_ops; + desc->pdata = pdata; + desc->dev = dev; + desc->groups = coresight_stm_groups; + drvdata->csdev = coresight_register(desc); + if (IS_ERR(drvdata->csdev)) { + ret = PTR_ERR(drvdata->csdev); + goto stm_unregister; + } + + pm_runtime_put(&adev->dev); + + dev_info(dev, "%s initialized\n", (char *)id->data); + return 0; + +stm_unregister: + stm_unregister_device(&drvdata->stm); + return ret; +} + +#ifdef CONFIG_PM +static int stm_runtime_suspend(struct device *dev) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(dev); + + if (drvdata && !IS_ERR(drvdata->atclk)) + clk_disable_unprepare(drvdata->atclk); + + return 0; +} + +static int stm_runtime_resume(struct device *dev) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(dev); + + if (drvdata && !IS_ERR(drvdata->atclk)) + clk_prepare_enable(drvdata->atclk); + + return 0; +} +#endif + +static const struct dev_pm_ops stm_dev_pm_ops = { + SET_RUNTIME_PM_OPS(stm_runtime_suspend, stm_runtime_resume, NULL) +}; + +static struct amba_id stm_ids[] = { + { + .id = 0x0003b962, + .mask = 0x0003ffff, + .data = "STM32", + }, + { 0, 0}, +}; + +static struct amba_driver stm_driver = { + .drv = { + .name = "coresight-stm", + .owner = THIS_MODULE, + .pm = &stm_dev_pm_ops, + .suppress_bind_attrs = true, + }, + .probe = stm_probe, + .id_table = stm_ids, +}; + +builtin_amba_driver(stm_driver); diff --git a/include/linux/coresight-stm.h b/include/linux/coresight-stm.h new file mode 100644 index 000000000000..a978bb85599a --- /dev/null +++ b/include/linux/coresight-stm.h @@ -0,0 +1,6 @@ +#ifndef __LINUX_CORESIGHT_STM_H_ +#define __LINUX_CORESIGHT_STM_H_ + +#include + +#endif diff --git a/include/uapi/linux/coresight-stm.h b/include/uapi/linux/coresight-stm.h new file mode 100644 index 000000000000..7e4272cf1fb2 --- /dev/null +++ b/include/uapi/linux/coresight-stm.h @@ -0,0 +1,21 @@ +#ifndef __UAPI_CORESIGHT_STM_H_ +#define __UAPI_CORESIGHT_STM_H_ + +#define STM_FLAG_TIMESTAMPED BIT(3) +#define STM_FLAG_GUARANTEED BIT(7) + +/* + * The CoreSight STM supports guaranteed and invariant timing + * transactions. Guaranteed transactions are guaranteed to be + * traced, this might involve stalling the bus or system to + * ensure the transaction is accepted by the STM. While invariant + * timing transactions are not guaranteed to be traced, they + * will take an invariant amount of time regardless of the + * state of the STM. + */ +enum { + STM_OPTION_GUARANTEED = 0, + STM_OPTION_INVARIANT, +}; + +#endif From 143e92c2248c24712e453aef2a43fbe164d37d37 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:41 -0600 Subject: [PATCH 084/104] coresight: etb10: fixing the right amount of words to read This patch rectifies the amount of words to read when the internal buffer is deemed bigger than the amount of space available in the perf ring buffer. The amount to read is set to the amount of space in the perf ring buffer rather than being subtracted by it. Reported-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit b5af0a26da84b75376706a92c7a58036a0bf3541) --- drivers/hwtracing/coresight/coresight-etb10.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 2461d5c8c157..b0d402dbfeae 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -440,7 +440,7 @@ static void etb_update_buffer(struct coresight_device *csdev, u32 mask = ~(ETB_FRAME_SIZE_WORDS - 1); /* The new read pointer must be frame size aligned */ - to_read -= handle->size & mask; + to_read = handle->size & mask; /* * Move the RAM read pointer up, keeping in mind that * everything is in frame size units. From 42d5ddbbf0f8cfadadf6cb41bfa8bcf1b8c421b1 Mon Sep 17 00:00:00 2001 From: Li Pengcheng Date: Tue, 3 May 2016 11:33:42 -0600 Subject: [PATCH 085/104] coresight: etm4x: add tracer ID for A72 Maia processor. This patch adds a cellID for the ETMv4 tracer found on HiSillicon's A72 Maia processor. Signed-off-by: Li Pengcheng Signed-off-by: Li Zhong Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 960e30959988e0f37e0eeb22b6cdb65b94d5d2e7) --- drivers/hwtracing/coresight/coresight-etm4x.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 6396b28993da..462f0dc15757 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -825,6 +825,11 @@ static struct amba_id etm4_ids[] = { .mask = 0x000fffff, .data = "ETM 4.0", }, + { /* ETM 4.0 - A72, Maia, HiSilicon */ + .id = 0x000bb95a, + .mask = 0x000fffff, + .data = "ETM 4.0", + }, { 0, 0}, }; From d8400899814b77b374e69be43f766266caa73c74 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:43 -0600 Subject: [PATCH 086/104] coresight: tmc: adding sysFS management entries Adding management registers that convey implementation specific characteristics. Those are useful for trace configuration and collection along with general trouble shooting. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 7d83d17795efce95def54a13ccd6c3f80de6e8f0) --- .../testing/sysfs-bus-coresight-devices-tmc | 77 +++++++++++++ drivers/hwtracing/coresight/coresight-tmc.c | 103 +++++++----------- 2 files changed, 118 insertions(+), 62 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-tmc b/Documentation/ABI/testing/sysfs-bus-coresight-devices-tmc index f38cded5fa22..4fe677ed1305 100644 --- a/Documentation/ABI/testing/sysfs-bus-coresight-devices-tmc +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-tmc @@ -6,3 +6,80 @@ Description: (RW) Disables write access to the Trace RAM by stopping the formatter after a defined number of words have been stored following the trigger event. Additional interface for this driver are expected to be added as it matures. + +What: /sys/bus/coresight/devices/.tmc/mgmt/rsz +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Defines the size, in 32-bit words, of the local RAM buffer. + The value is read directly from HW register RSZ, 0x004. + +What: /sys/bus/coresight/devices/.tmc/mgmt/sts +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the TMC status register. The value + is read directly from HW register STS, 0x00C. + +What: /sys/bus/coresight/devices/.tmc/mgmt/rrp +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the TMC RAM Read Pointer register + that is used to read entries from the Trace RAM over the APB + interface. The value is read directly from HW register RRP, + 0x014. + +What: /sys/bus/coresight/devices/.tmc/mgmt/rwp +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the TMC RAM Write Pointer register + that is used to sets the write pointer to write entries from + the CoreSight bus into the Trace RAM. The value is read directly + from HW register RWP, 0x018. + +What: /sys/bus/coresight/devices/.tmc/mgmt/trg +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Similar to "trigger_cntr" above except that this value is + read directly from HW register TRG, 0x01C. + +What: /sys/bus/coresight/devices/.tmc/mgmt/ctl +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the TMC Control register. The value + is read directly from HW register CTL, 0x020. + +What: /sys/bus/coresight/devices/.tmc/mgmt/ffsr +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the TMC Formatter and Flush Status + register. The value is read directly from HW register FFSR, + 0x300. + +What: /sys/bus/coresight/devices/.tmc/mgmt/ffcr +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the TMC Formatter and Flush Control + register. The value is read directly from HW register FFCR, + 0x304. + +What: /sys/bus/coresight/devices/.tmc/mgmt/mode +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the TMC Mode register, which + indicate the mode the device has been configured to enact. The + The value is read directly from the MODE register, 0x028. + +What: /sys/bus/coresight/devices/.tmc/mgmt/devid +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Indicates the capabilities of the Coresight TMC. + The value is read directly from the DEVID register, 0xFC8, diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 0a4db1ac52ae..2b42ecbd8831 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -556,56 +556,38 @@ static const struct file_operations tmc_fops = { .llseek = no_llseek, }; -static ssize_t status_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long flags; - u32 tmc_rsz, tmc_sts, tmc_rrp, tmc_rwp, tmc_trg; - u32 tmc_ctl, tmc_ffsr, tmc_ffcr, tmc_mode, tmc_pscr; - u32 devid; - struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); +#define coresight_tmc_simple_func(name, offset) \ + coresight_simple_func(struct tmc_drvdata, name, offset) - pm_runtime_get_sync(drvdata->dev); - spin_lock_irqsave(&drvdata->spinlock, flags); - CS_UNLOCK(drvdata->base); +coresight_tmc_simple_func(rsz, TMC_RSZ); +coresight_tmc_simple_func(sts, TMC_STS); +coresight_tmc_simple_func(rrp, TMC_RRP); +coresight_tmc_simple_func(rwp, TMC_RWP); +coresight_tmc_simple_func(trg, TMC_TRG); +coresight_tmc_simple_func(ctl, TMC_CTL); +coresight_tmc_simple_func(ffsr, TMC_FFSR); +coresight_tmc_simple_func(ffcr, TMC_FFCR); +coresight_tmc_simple_func(mode, TMC_MODE); +coresight_tmc_simple_func(pscr, TMC_PSCR); +coresight_tmc_simple_func(devid, CORESIGHT_DEVID); - tmc_rsz = readl_relaxed(drvdata->base + TMC_RSZ); - tmc_sts = readl_relaxed(drvdata->base + TMC_STS); - tmc_rrp = readl_relaxed(drvdata->base + TMC_RRP); - tmc_rwp = readl_relaxed(drvdata->base + TMC_RWP); - tmc_trg = readl_relaxed(drvdata->base + TMC_TRG); - tmc_ctl = readl_relaxed(drvdata->base + TMC_CTL); - tmc_ffsr = readl_relaxed(drvdata->base + TMC_FFSR); - tmc_ffcr = readl_relaxed(drvdata->base + TMC_FFCR); - tmc_mode = readl_relaxed(drvdata->base + TMC_MODE); - tmc_pscr = readl_relaxed(drvdata->base + TMC_PSCR); - devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID); +static struct attribute *coresight_tmc_mgmt_attrs[] = { + &dev_attr_rsz.attr, + &dev_attr_sts.attr, + &dev_attr_rrp.attr, + &dev_attr_rwp.attr, + &dev_attr_trg.attr, + &dev_attr_ctl.attr, + &dev_attr_ffsr.attr, + &dev_attr_ffcr.attr, + &dev_attr_mode.attr, + &dev_attr_pscr.attr, + &dev_attr_devid.attr, + NULL, +}; - CS_LOCK(drvdata->base); - spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); - - return sprintf(buf, - "Depth:\t\t0x%x\n" - "Status:\t\t0x%x\n" - "RAM read ptr:\t0x%x\n" - "RAM wrt ptr:\t0x%x\n" - "Trigger cnt:\t0x%x\n" - "Control:\t0x%x\n" - "Flush status:\t0x%x\n" - "Flush ctrl:\t0x%x\n" - "Mode:\t\t0x%x\n" - "PSRC:\t\t0x%x\n" - "DEVID:\t\t0x%x\n", - tmc_rsz, tmc_sts, tmc_rrp, tmc_rwp, tmc_trg, - tmc_ctl, tmc_ffsr, tmc_ffcr, tmc_mode, tmc_pscr, devid); - - return -EINVAL; -} -static DEVICE_ATTR_RO(status); - -static ssize_t trigger_cntr_show(struct device *dev, - struct device_attribute *attr, char *buf) +ssize_t trigger_cntr_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); unsigned long val = drvdata->trigger_cntr; @@ -630,26 +612,25 @@ static ssize_t trigger_cntr_store(struct device *dev, } static DEVICE_ATTR_RW(trigger_cntr); -static struct attribute *coresight_etb_attrs[] = { +static struct attribute *coresight_tmc_attrs[] = { &dev_attr_trigger_cntr.attr, - &dev_attr_status.attr, NULL, }; -ATTRIBUTE_GROUPS(coresight_etb); -static struct attribute *coresight_etr_attrs[] = { - &dev_attr_trigger_cntr.attr, - &dev_attr_status.attr, - NULL, +static const struct attribute_group coresight_tmc_group = { + .attrs = coresight_tmc_attrs, }; -ATTRIBUTE_GROUPS(coresight_etr); -static struct attribute *coresight_etf_attrs[] = { - &dev_attr_trigger_cntr.attr, - &dev_attr_status.attr, +static const struct attribute_group coresight_tmc_mgmt_group = { + .attrs = coresight_tmc_mgmt_attrs, + .name = "mgmt", +}; + +const struct attribute_group *coresight_tmc_groups[] = { + &coresight_tmc_group, + &coresight_tmc_mgmt_group, NULL, }; -ATTRIBUTE_GROUPS(coresight_etf); static int tmc_probe(struct amba_device *adev, const struct amba_id *id) { @@ -725,20 +706,18 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) desc->pdata = pdata; desc->dev = dev; desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; + desc->groups = coresight_tmc_groups; if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { desc->type = CORESIGHT_DEV_TYPE_SINK; desc->ops = &tmc_etb_cs_ops; - desc->groups = coresight_etb_groups; } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { desc->type = CORESIGHT_DEV_TYPE_SINK; desc->ops = &tmc_etr_cs_ops; - desc->groups = coresight_etr_groups; } else { desc->type = CORESIGHT_DEV_TYPE_LINKSINK; desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO; desc->ops = &tmc_etf_cs_ops; - desc->groups = coresight_etf_groups; } drvdata->csdev = coresight_register(desc); From 43dc4e87f80f767136de5a6d8f5649f6195b9faa Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:44 -0600 Subject: [PATCH 087/104] coresight: tmc: modifying naming convention According to the TMC architectural state machine, the 'stopped' state is reached when bit 2 (TMCReady) of the TMC Status register turns to '1'. The code is correct but the naming convention isn't. The 'Triggered' bit occupies position '1' of the TMC Status register and has nothing to do with the indication of the TMC entering the stopped state. As such renaming function "tmc_wait_for_triggered()" and changing the #define to reflect what the code is really doing. This patch has no effect other than clarifying the semantic. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 580ff804ecaf5bc59835fec26e17325bcd53fc91) --- drivers/hwtracing/coresight/coresight-tmc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 2b42ecbd8831..3f646e29a99b 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -77,7 +77,7 @@ #define TMC_FFCR_TRIGON_TRIGIN BIT(8) #define TMC_FFCR_STOP_ON_FLUSH BIT(12) -#define TMC_STS_TRIGGERED_BIT 2 +#define TMC_STS_TMCREADY_BIT 2 #define TMC_FFCR_FLUSHMAN_BIT 6 enum tmc_config_type { @@ -132,11 +132,11 @@ struct tmc_drvdata { u32 trigger_cntr; }; -static void tmc_wait_for_ready(struct tmc_drvdata *drvdata) +static void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata) { /* Ensure formatter, unformatter and hardware fifo are empty */ if (coresight_timeout(drvdata->base, - TMC_STS, TMC_STS_TRIGGERED_BIT, 1)) { + TMC_STS, TMC_STS_TMCREADY_BIT, 1)) { dev_err(drvdata->dev, "timeout observed when probing at offset %#x\n", TMC_STS); @@ -160,7 +160,7 @@ static void tmc_flush_and_stop(struct tmc_drvdata *drvdata) TMC_FFCR); } - tmc_wait_for_ready(drvdata); + tmc_wait_for_tmcready(drvdata); } static void tmc_enable_hw(struct tmc_drvdata *drvdata) From 66c6e1177cfae0532c63b465bea65afaef289531 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:45 -0600 Subject: [PATCH 088/104] coresight: tmc: waiting for TMCReady bit before programming According to the TRM before programming the TMC in circular buffer mode (and that for any configuration, ETB, ETR, ETF), the TMCReady bit in the status register has to be set. This patch adds a check to make sure the state machine is in a state where it can be configured, and complains otherwise. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 358f42184e97f9a216b927a5a744597b98e0eee1) --- drivers/hwtracing/coresight/coresight-tmc.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 3f646e29a99b..66fa7736d12f 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -180,6 +180,9 @@ static void tmc_etb_enable_hw(struct tmc_drvdata *drvdata) CS_UNLOCK(drvdata->base); + /* Wait for TMCSReady bit to be set */ + tmc_wait_for_tmcready(drvdata); + writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI | TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT | @@ -201,6 +204,9 @@ static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) CS_UNLOCK(drvdata->base); + /* Wait for TMCSReady bit to be set */ + tmc_wait_for_tmcready(drvdata); + writel_relaxed(drvdata->size / 4, drvdata->base + TMC_RSZ); writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); @@ -230,6 +236,9 @@ static void tmc_etf_enable_hw(struct tmc_drvdata *drvdata) { CS_UNLOCK(drvdata->base); + /* Wait for TMCSReady bit to be set */ + tmc_wait_for_tmcready(drvdata); + writel_relaxed(TMC_MODE_HARDWARE_FIFO, drvdata->base + TMC_MODE); writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI, drvdata->base + TMC_FFCR); From 8356894677ad1f9900a037346514866c35cc4bd3 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:46 -0600 Subject: [PATCH 089/104] coresight: tmc: re-implementing tmc_read_prepare/unprepare() functions In their current implementation the tmc_read_prepare/unprepare() are a lump of if/else that is difficult to read. This patch is alleviating that by using a switch statement. The latter also allows for a better control on the error path. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit b1789b793eb4627928f55a6acea8da7c25e5c6b4) --- drivers/hwtracing/coresight/coresight-tmc.c | 56 +++++++++++++-------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 66fa7736d12f..d211aeec49f8 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -431,7 +431,7 @@ static const struct coresight_ops tmc_etf_cs_ops = { static int tmc_read_prepare(struct tmc_drvdata *drvdata) { - int ret; + int ret = 0; unsigned long flags; enum tmc_mode mode; @@ -439,25 +439,31 @@ static int tmc_read_prepare(struct tmc_drvdata *drvdata) if (!drvdata->enable) goto out; - if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { + switch (drvdata->config_type) { + case TMC_CONFIG_TYPE_ETB: tmc_etb_disable_hw(drvdata); - } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { - tmc_etr_disable_hw(drvdata); - } else { + break; + case TMC_CONFIG_TYPE_ETF: + /* There is no point in reading a TMC in HW FIFO mode */ mode = readl_relaxed(drvdata->base + TMC_MODE); - if (mode == TMC_MODE_CIRCULAR_BUFFER) { - tmc_etb_disable_hw(drvdata); - } else { - ret = -ENODEV; + if (mode != TMC_MODE_CIRCULAR_BUFFER) { + ret = -EINVAL; goto err; } + + tmc_etb_disable_hw(drvdata); + break; + case TMC_CONFIG_TYPE_ETR: + tmc_etr_disable_hw(drvdata); + break; + default: + ret = -EINVAL; + goto err; } + out: drvdata->reading = true; - spin_unlock_irqrestore(&drvdata->spinlock, flags); - dev_info(drvdata->dev, "TMC read start\n"); - return 0; err: spin_unlock_irqrestore(&drvdata->spinlock, flags); return ret; @@ -472,20 +478,30 @@ static void tmc_read_unprepare(struct tmc_drvdata *drvdata) if (!drvdata->enable) goto out; - if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { + switch (drvdata->config_type) { + case TMC_CONFIG_TYPE_ETB: tmc_etb_enable_hw(drvdata); - } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { - tmc_etr_enable_hw(drvdata); - } else { + break; + case TMC_CONFIG_TYPE_ETF: + /* Make sure we don't re-enable a TMC in HW FIFO mode */ mode = readl_relaxed(drvdata->base + TMC_MODE); - if (mode == TMC_MODE_CIRCULAR_BUFFER) - tmc_etb_enable_hw(drvdata); + if (mode != TMC_MODE_CIRCULAR_BUFFER) + goto err; + + tmc_etb_enable_hw(drvdata); + break; + case TMC_CONFIG_TYPE_ETR: + tmc_etr_disable_hw(drvdata); + break; + default: + goto err; } + out: drvdata->reading = false; - spin_unlock_irqrestore(&drvdata->spinlock, flags); - dev_info(drvdata->dev, "TMC read end\n"); +err: + spin_unlock_irqrestore(&drvdata->spinlock, flags); } static int tmc_open(struct inode *inode, struct file *file) From 00bc807d39000d6ff6efabcdc3728c87075c52f8 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:47 -0600 Subject: [PATCH 090/104] coresight: tmc: clearly define number of transfers per burst This patch makes the name of the define reflect the amount of data tranfers per burst, in this case 16. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit ebba56e7b2bd2c9c2bbe02fad8808feef18e1519) --- drivers/hwtracing/coresight/coresight-tmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index d211aeec49f8..8751d53fa078 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -67,7 +67,7 @@ #define TMC_AXICTL_PROT_CTL_B0 BIT(0) #define TMC_AXICTL_PROT_CTL_B1 BIT(1) #define TMC_AXICTL_SCT_GAT_MODE BIT(7) -#define TMC_AXICTL_WR_BURST_LEN 0xF00 +#define TMC_AXICTL_WR_BURST_16 0xF00 /* TMC_FFCR - 0x304 */ #define TMC_FFCR_EN_FMT BIT(0) #define TMC_FFCR_EN_TI BIT(1) @@ -211,7 +211,7 @@ static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); axictl = readl_relaxed(drvdata->base + TMC_AXICTL); - axictl |= TMC_AXICTL_WR_BURST_LEN; + axictl |= TMC_AXICTL_WR_BURST_16; writel_relaxed(axictl, drvdata->base + TMC_AXICTL); axictl &= ~TMC_AXICTL_SCT_GAT_MODE; writel_relaxed(axictl, drvdata->base + TMC_AXICTL); From c3e22ca4e145c472635ae17ecf7b60d531d98924 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:48 -0600 Subject: [PATCH 091/104] coresight: tmc: introducing new header file The amount of #define, enumeration and structure definition is big enough to justify moving them to a new header file. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 4c324b5f0e8a692c8d077da9d18533820c2ab636) --- drivers/hwtracing/coresight/coresight-tmc.c | 102 +--------------- drivers/hwtracing/coresight/coresight-tmc.h | 122 ++++++++++++++++++++ 2 files changed, 123 insertions(+), 101 deletions(-) create mode 100644 drivers/hwtracing/coresight/coresight-tmc.h diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 8751d53fa078..f9a6624ab531 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -30,107 +30,7 @@ #include #include "coresight-priv.h" - -#define TMC_RSZ 0x004 -#define TMC_STS 0x00c -#define TMC_RRD 0x010 -#define TMC_RRP 0x014 -#define TMC_RWP 0x018 -#define TMC_TRG 0x01c -#define TMC_CTL 0x020 -#define TMC_RWD 0x024 -#define TMC_MODE 0x028 -#define TMC_LBUFLEVEL 0x02c -#define TMC_CBUFLEVEL 0x030 -#define TMC_BUFWM 0x034 -#define TMC_RRPHI 0x038 -#define TMC_RWPHI 0x03c -#define TMC_AXICTL 0x110 -#define TMC_DBALO 0x118 -#define TMC_DBAHI 0x11c -#define TMC_FFSR 0x300 -#define TMC_FFCR 0x304 -#define TMC_PSCR 0x308 -#define TMC_ITMISCOP0 0xee0 -#define TMC_ITTRFLIN 0xee8 -#define TMC_ITATBDATA0 0xeec -#define TMC_ITATBCTR2 0xef0 -#define TMC_ITATBCTR1 0xef4 -#define TMC_ITATBCTR0 0xef8 - -/* register description */ -/* TMC_CTL - 0x020 */ -#define TMC_CTL_CAPT_EN BIT(0) -/* TMC_STS - 0x00C */ -#define TMC_STS_TRIGGERED BIT(1) -/* TMC_AXICTL - 0x110 */ -#define TMC_AXICTL_PROT_CTL_B0 BIT(0) -#define TMC_AXICTL_PROT_CTL_B1 BIT(1) -#define TMC_AXICTL_SCT_GAT_MODE BIT(7) -#define TMC_AXICTL_WR_BURST_16 0xF00 -/* TMC_FFCR - 0x304 */ -#define TMC_FFCR_EN_FMT BIT(0) -#define TMC_FFCR_EN_TI BIT(1) -#define TMC_FFCR_FON_FLIN BIT(4) -#define TMC_FFCR_FON_TRIG_EVT BIT(5) -#define TMC_FFCR_FLUSHMAN BIT(6) -#define TMC_FFCR_TRIGON_TRIGIN BIT(8) -#define TMC_FFCR_STOP_ON_FLUSH BIT(12) - -#define TMC_STS_TMCREADY_BIT 2 -#define TMC_FFCR_FLUSHMAN_BIT 6 - -enum tmc_config_type { - TMC_CONFIG_TYPE_ETB, - TMC_CONFIG_TYPE_ETR, - TMC_CONFIG_TYPE_ETF, -}; - -enum tmc_mode { - TMC_MODE_CIRCULAR_BUFFER, - TMC_MODE_SOFTWARE_FIFO, - TMC_MODE_HARDWARE_FIFO, -}; - -enum tmc_mem_intf_width { - TMC_MEM_INTF_WIDTH_32BITS = 0x2, - TMC_MEM_INTF_WIDTH_64BITS = 0x3, - TMC_MEM_INTF_WIDTH_128BITS = 0x4, - TMC_MEM_INTF_WIDTH_256BITS = 0x5, -}; - -/** - * struct tmc_drvdata - specifics associated to an TMC component - * @base: memory mapped base address for this component. - * @dev: the device entity associated to this component. - * @csdev: component vitals needed by the framework. - * @miscdev: specifics to handle "/dev/xyz.tmc" entry. - * @spinlock: only one at a time pls. - * @read_count: manages preparation of buffer for reading. - * @buf: area of memory where trace data get sent. - * @paddr: DMA start location in RAM. - * @vaddr: virtual representation of @paddr. - * @size: @buf size. - * @enable: this TMC is being used. - * @config_type: TMC variant, must be of type @tmc_config_type. - * @trigger_cntr: amount of words to store after a trigger. - */ -struct tmc_drvdata { - void __iomem *base; - struct device *dev; - struct coresight_device *csdev; - struct miscdevice miscdev; - spinlock_t spinlock; - int read_count; - bool reading; - char *buf; - dma_addr_t paddr; - void *vaddr; - u32 size; - bool enable; - enum tmc_config_type config_type; - u32 trigger_cntr; -}; +#include "coresight-tmc.h" static void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata) { diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h new file mode 100644 index 000000000000..49718b4a9788 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -0,0 +1,122 @@ +/* + * Copyright(C) 2015 Linaro Limited. All rights reserved. + * Author: Mathieu Poirier + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef _CORESIGHT_TMC_H +#define _CORESIGHT_TMC_H + +#define TMC_RSZ 0x004 +#define TMC_STS 0x00c +#define TMC_RRD 0x010 +#define TMC_RRP 0x014 +#define TMC_RWP 0x018 +#define TMC_TRG 0x01c +#define TMC_CTL 0x020 +#define TMC_RWD 0x024 +#define TMC_MODE 0x028 +#define TMC_LBUFLEVEL 0x02c +#define TMC_CBUFLEVEL 0x030 +#define TMC_BUFWM 0x034 +#define TMC_RRPHI 0x038 +#define TMC_RWPHI 0x03c +#define TMC_AXICTL 0x110 +#define TMC_DBALO 0x118 +#define TMC_DBAHI 0x11c +#define TMC_FFSR 0x300 +#define TMC_FFCR 0x304 +#define TMC_PSCR 0x308 +#define TMC_ITMISCOP0 0xee0 +#define TMC_ITTRFLIN 0xee8 +#define TMC_ITATBDATA0 0xeec +#define TMC_ITATBCTR2 0xef0 +#define TMC_ITATBCTR1 0xef4 +#define TMC_ITATBCTR0 0xef8 + +/* register description */ +/* TMC_CTL - 0x020 */ +#define TMC_CTL_CAPT_EN BIT(0) +/* TMC_STS - 0x00C */ +#define TMC_STS_TRIGGERED BIT(1) +/* TMC_AXICTL - 0x110 */ +#define TMC_AXICTL_PROT_CTL_B0 BIT(0) +#define TMC_AXICTL_PROT_CTL_B1 BIT(1) +#define TMC_AXICTL_SCT_GAT_MODE BIT(7) +#define TMC_AXICTL_WR_BURST_16 0xF00 +/* TMC_FFCR - 0x304 */ +#define TMC_FFCR_EN_FMT BIT(0) +#define TMC_FFCR_EN_TI BIT(1) +#define TMC_FFCR_FON_FLIN BIT(4) +#define TMC_FFCR_FON_TRIG_EVT BIT(5) +#define TMC_FFCR_FLUSHMAN BIT(6) +#define TMC_FFCR_TRIGON_TRIGIN BIT(8) +#define TMC_FFCR_STOP_ON_FLUSH BIT(12) + +#define TMC_STS_TMCREADY_BIT 2 +#define TMC_FFCR_FLUSHMAN_BIT 6 + +enum tmc_config_type { + TMC_CONFIG_TYPE_ETB, + TMC_CONFIG_TYPE_ETR, + TMC_CONFIG_TYPE_ETF, +}; + +enum tmc_mode { + TMC_MODE_CIRCULAR_BUFFER, + TMC_MODE_SOFTWARE_FIFO, + TMC_MODE_HARDWARE_FIFO, +}; + +enum tmc_mem_intf_width { + TMC_MEM_INTF_WIDTH_32BITS = 0x2, + TMC_MEM_INTF_WIDTH_64BITS = 0x3, + TMC_MEM_INTF_WIDTH_128BITS = 0x4, + TMC_MEM_INTF_WIDTH_256BITS = 0x5, +}; + +/** + * struct tmc_drvdata - specifics associated to an TMC component + * @base: memory mapped base address for this component. + * @dev: the device entity associated to this component. + * @csdev: component vitals needed by the framework. + * @miscdev: specifics to handle "/dev/xyz.tmc" entry. + * @spinlock: only one at a time pls. + * @read_count: manages preparation of buffer for reading. + * @buf: area of memory where trace data get sent. + * @paddr: DMA start location in RAM. + * @vaddr: virtual representation of @paddr. + * @size: @buf size. + * @enable: this TMC is being used. + * @config_type: TMC variant, must be of type @tmc_config_type. + * @trigger_cntr: amount of words to store after a trigger. + */ +struct tmc_drvdata { + void __iomem *base; + struct device *dev; + struct coresight_device *csdev; + struct miscdevice miscdev; + spinlock_t spinlock; + int read_count; + bool reading; + char *buf; + dma_addr_t paddr; + void __iomem *vaddr; + u32 size; + bool enable; + enum tmc_config_type config_type; + u32 trigger_cntr; +}; + +#endif From 5364b7d8eb0e51193f7a6e030fe83481b575f277 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:49 -0600 Subject: [PATCH 092/104] coresight: tmc: cleaning up header file This patch first move the TMC_STS_TMCREADY_BIT and TMC_FFCR_FLUSHMAN_BIT defines to their respective section. It also removes TMC_FFCR_FLUSHMAN, since the same result can easily be obtained using the BIT() macro. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit a8ab4268e0db93c564ee6ccb770bb3d53af24be9) --- drivers/hwtracing/coresight/coresight-tmc.c | 2 +- drivers/hwtracing/coresight/coresight-tmc.h | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index f9a6624ab531..07e2809d832b 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -50,7 +50,7 @@ static void tmc_flush_and_stop(struct tmc_drvdata *drvdata) ffcr = readl_relaxed(drvdata->base + TMC_FFCR); ffcr |= TMC_FFCR_STOP_ON_FLUSH; writel_relaxed(ffcr, drvdata->base + TMC_FFCR); - ffcr |= TMC_FFCR_FLUSHMAN; + ffcr |= BIT(TMC_FFCR_FLUSHMAN_BIT); writel_relaxed(ffcr, drvdata->base + TMC_FFCR); /* Ensure flush completes */ if (coresight_timeout(drvdata->base, diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 49718b4a9788..5a60830c8db5 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -49,6 +49,7 @@ /* TMC_CTL - 0x020 */ #define TMC_CTL_CAPT_EN BIT(0) /* TMC_STS - 0x00C */ +#define TMC_STS_TMCREADY_BIT 2 #define TMC_STS_TRIGGERED BIT(1) /* TMC_AXICTL - 0x110 */ #define TMC_AXICTL_PROT_CTL_B0 BIT(0) @@ -56,16 +57,14 @@ #define TMC_AXICTL_SCT_GAT_MODE BIT(7) #define TMC_AXICTL_WR_BURST_16 0xF00 /* TMC_FFCR - 0x304 */ +#define TMC_FFCR_FLUSHMAN_BIT 6 #define TMC_FFCR_EN_FMT BIT(0) #define TMC_FFCR_EN_TI BIT(1) #define TMC_FFCR_FON_FLIN BIT(4) #define TMC_FFCR_FON_TRIG_EVT BIT(5) -#define TMC_FFCR_FLUSHMAN BIT(6) #define TMC_FFCR_TRIGON_TRIGIN BIT(8) #define TMC_FFCR_STOP_ON_FLUSH BIT(12) -#define TMC_STS_TMCREADY_BIT 2 -#define TMC_FFCR_FLUSHMAN_BIT 6 enum tmc_config_type { TMC_CONFIG_TYPE_ETB, From de03aeaf645691669fbcda2ec364c143ab7d900b Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:50 -0600 Subject: [PATCH 093/104] coresight: tmc: splitting driver in ETB/ETF and ETR components The TMC block can operate in 3 modes (ETB, ETF and ETR) and accessed via two interfaces (sysFS and Perf). That makes 6 mode to cover, which is way too much coupling for a single file. This patch splits the original TMC driver in 2 halves, one for ETB/ETF and another one for ETR mode. A common core is kept for functionality common to all 3 modes. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 6c6ed1e244c0530fb76a8b52024f199f398ef100) --- drivers/hwtracing/coresight/Makefile | 4 +- .../hwtracing/coresight/coresight-tmc-etf.c | 204 ++++++++++++++ .../hwtracing/coresight/coresight-tmc-etr.c | 128 +++++++++ drivers/hwtracing/coresight/coresight-tmc.c | 264 +----------------- drivers/hwtracing/coresight/coresight-tmc.h | 18 ++ 5 files changed, 357 insertions(+), 261 deletions(-) create mode 100644 drivers/hwtracing/coresight/coresight-tmc-etf.c create mode 100644 drivers/hwtracing/coresight/coresight-tmc-etr.c diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index c6f84b57f52a..af480d9c1441 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -3,7 +3,9 @@ # obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o obj-$(CONFIG_OF) += of_coresight.o -obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o +obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o \ + coresight-tmc-etf.o \ + coresight-tmc-etr.o obj-$(CONFIG_CORESIGHT_SINK_TPIU) += coresight-tpiu.o obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c new file mode 100644 index 000000000000..467d19221f7b --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -0,0 +1,204 @@ +/* + * Copyright(C) 2016 Linaro Limited. All rights reserved. + * Author: Mathieu Poirier + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include "coresight-priv.h" +#include "coresight-tmc.h" + +void tmc_etb_enable_hw(struct tmc_drvdata *drvdata) +{ + /* Zero out the memory to help with debug */ + memset(drvdata->buf, 0, drvdata->size); + + CS_UNLOCK(drvdata->base); + + /* Wait for TMCSReady bit to be set */ + tmc_wait_for_tmcready(drvdata); + + writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); + writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI | + TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT | + TMC_FFCR_TRIGON_TRIGIN, + drvdata->base + TMC_FFCR); + + writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG); + tmc_enable_hw(drvdata); + + CS_LOCK(drvdata->base); +} + +static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata) +{ + enum tmc_mem_intf_width memwidth; + u8 memwords; + char *bufp; + u32 read_data; + int i; + + memwidth = BMVAL(readl_relaxed(drvdata->base + CORESIGHT_DEVID), 8, 10); + if (memwidth == TMC_MEM_INTF_WIDTH_32BITS) + memwords = 1; + else if (memwidth == TMC_MEM_INTF_WIDTH_64BITS) + memwords = 2; + else if (memwidth == TMC_MEM_INTF_WIDTH_128BITS) + memwords = 4; + else + memwords = 8; + + bufp = drvdata->buf; + while (1) { + for (i = 0; i < memwords; i++) { + read_data = readl_relaxed(drvdata->base + TMC_RRD); + if (read_data == 0xFFFFFFFF) + return; + memcpy(bufp, &read_data, 4); + bufp += 4; + } + } +} + +void tmc_etb_disable_hw(struct tmc_drvdata *drvdata) +{ + CS_UNLOCK(drvdata->base); + + tmc_flush_and_stop(drvdata); + tmc_etb_dump_hw(drvdata); + tmc_disable_hw(drvdata); + + CS_LOCK(drvdata->base); +} + +static void tmc_etf_enable_hw(struct tmc_drvdata *drvdata) +{ + CS_UNLOCK(drvdata->base); + + /* Wait for TMCSReady bit to be set */ + tmc_wait_for_tmcready(drvdata); + + writel_relaxed(TMC_MODE_HARDWARE_FIFO, drvdata->base + TMC_MODE); + writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI, + drvdata->base + TMC_FFCR); + writel_relaxed(0x0, drvdata->base + TMC_BUFWM); + tmc_enable_hw(drvdata); + + CS_LOCK(drvdata->base); +} + +static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata) +{ + CS_UNLOCK(drvdata->base); + + tmc_flush_and_stop(drvdata); + tmc_disable_hw(drvdata); + + CS_LOCK(drvdata->base); +} + +static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode) +{ + unsigned long flags; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + spin_lock_irqsave(&drvdata->spinlock, flags); + if (drvdata->reading) { + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return -EBUSY; + } + + tmc_etb_enable_hw(drvdata); + drvdata->enable = true; + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + dev_info(drvdata->dev, "TMC-ETB/ETF enabled\n"); + return 0; +} + +static void tmc_disable_etf_sink(struct coresight_device *csdev) +{ + unsigned long flags; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + spin_lock_irqsave(&drvdata->spinlock, flags); + if (drvdata->reading) { + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return; + } + + tmc_etb_disable_hw(drvdata); + drvdata->enable = false; + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + dev_info(drvdata->dev, "TMC-ETB/ETF disabled\n"); +} + +static int tmc_enable_etf_link(struct coresight_device *csdev, + int inport, int outport) +{ + unsigned long flags; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + spin_lock_irqsave(&drvdata->spinlock, flags); + if (drvdata->reading) { + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return -EBUSY; + } + + tmc_etf_enable_hw(drvdata); + drvdata->enable = true; + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + dev_info(drvdata->dev, "TMC-ETF enabled\n"); + return 0; +} + +static void tmc_disable_etf_link(struct coresight_device *csdev, + int inport, int outport) +{ + unsigned long flags; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + spin_lock_irqsave(&drvdata->spinlock, flags); + if (drvdata->reading) { + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return; + } + + tmc_etf_disable_hw(drvdata); + drvdata->enable = false; + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + dev_info(drvdata->dev, "TMC disabled\n"); +} + +static const struct coresight_ops_sink tmc_etf_sink_ops = { + .enable = tmc_enable_etf_sink, + .disable = tmc_disable_etf_sink, +}; + +static const struct coresight_ops_link tmc_etf_link_ops = { + .enable = tmc_enable_etf_link, + .disable = tmc_disable_etf_link, +}; + +const struct coresight_ops tmc_etb_cs_ops = { + .sink_ops = &tmc_etf_sink_ops, +}; + +const struct coresight_ops tmc_etf_cs_ops = { + .sink_ops = &tmc_etf_sink_ops, + .link_ops = &tmc_etf_link_ops, +}; diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c new file mode 100644 index 000000000000..5d9333ec49ae --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -0,0 +1,128 @@ +/* + * Copyright(C) 2016 Linaro Limited. All rights reserved. + * Author: Mathieu Poirier + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include "coresight-priv.h" +#include "coresight-tmc.h" + +void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) +{ + u32 axictl; + + /* Zero out the memory to help with debug */ + memset(drvdata->vaddr, 0, drvdata->size); + + CS_UNLOCK(drvdata->base); + + /* Wait for TMCSReady bit to be set */ + tmc_wait_for_tmcready(drvdata); + + writel_relaxed(drvdata->size / 4, drvdata->base + TMC_RSZ); + writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); + + axictl = readl_relaxed(drvdata->base + TMC_AXICTL); + axictl |= TMC_AXICTL_WR_BURST_16; + writel_relaxed(axictl, drvdata->base + TMC_AXICTL); + axictl &= ~TMC_AXICTL_SCT_GAT_MODE; + writel_relaxed(axictl, drvdata->base + TMC_AXICTL); + axictl = (axictl & + ~(TMC_AXICTL_PROT_CTL_B0 | TMC_AXICTL_PROT_CTL_B1)) | + TMC_AXICTL_PROT_CTL_B1; + writel_relaxed(axictl, drvdata->base + TMC_AXICTL); + + writel_relaxed(drvdata->paddr, drvdata->base + TMC_DBALO); + writel_relaxed(0x0, drvdata->base + TMC_DBAHI); + writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI | + TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT | + TMC_FFCR_TRIGON_TRIGIN, + drvdata->base + TMC_FFCR); + writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG); + tmc_enable_hw(drvdata); + + CS_LOCK(drvdata->base); +} + +static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata) +{ + u32 rwp, val; + + rwp = readl_relaxed(drvdata->base + TMC_RWP); + val = readl_relaxed(drvdata->base + TMC_STS); + + /* How much memory do we still have */ + if (val & BIT(0)) + drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr; + else + drvdata->buf = drvdata->vaddr; +} + +void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) +{ + CS_UNLOCK(drvdata->base); + + tmc_flush_and_stop(drvdata); + tmc_etr_dump_hw(drvdata); + tmc_disable_hw(drvdata); + + CS_LOCK(drvdata->base); +} + +static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode) +{ + unsigned long flags; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + spin_lock_irqsave(&drvdata->spinlock, flags); + if (drvdata->reading) { + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return -EBUSY; + } + + tmc_etr_enable_hw(drvdata); + drvdata->enable = true; + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + dev_info(drvdata->dev, "TMC-ETR enabled\n"); + return 0; +} + +static void tmc_disable_etr_sink(struct coresight_device *csdev) +{ + unsigned long flags; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + spin_lock_irqsave(&drvdata->spinlock, flags); + if (drvdata->reading) { + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return; + } + + tmc_etr_disable_hw(drvdata); + drvdata->enable = false; + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + dev_info(drvdata->dev, "TMC-ETR disabled\n"); +} + +static const struct coresight_ops_sink tmc_etr_sink_ops = { + .enable = tmc_enable_etr_sink, + .disable = tmc_disable_etr_sink, +}; + +const struct coresight_ops tmc_etr_cs_ops = { + .sink_ops = &tmc_etr_sink_ops, +}; diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 07e2809d832b..8d7f6d54c9b0 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -32,7 +32,7 @@ #include "coresight-priv.h" #include "coresight-tmc.h" -static void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata) +void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata) { /* Ensure formatter, unformatter and hardware fifo are empty */ if (coresight_timeout(drvdata->base, @@ -43,7 +43,7 @@ static void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata) } } -static void tmc_flush_and_stop(struct tmc_drvdata *drvdata) +void tmc_flush_and_stop(struct tmc_drvdata *drvdata) { u32 ffcr; @@ -63,272 +63,16 @@ static void tmc_flush_and_stop(struct tmc_drvdata *drvdata) tmc_wait_for_tmcready(drvdata); } -static void tmc_enable_hw(struct tmc_drvdata *drvdata) +void tmc_enable_hw(struct tmc_drvdata *drvdata) { writel_relaxed(TMC_CTL_CAPT_EN, drvdata->base + TMC_CTL); } -static void tmc_disable_hw(struct tmc_drvdata *drvdata) +void tmc_disable_hw(struct tmc_drvdata *drvdata) { writel_relaxed(0x0, drvdata->base + TMC_CTL); } -static void tmc_etb_enable_hw(struct tmc_drvdata *drvdata) -{ - /* Zero out the memory to help with debug */ - memset(drvdata->buf, 0, drvdata->size); - - CS_UNLOCK(drvdata->base); - - /* Wait for TMCSReady bit to be set */ - tmc_wait_for_tmcready(drvdata); - - writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); - writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI | - TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT | - TMC_FFCR_TRIGON_TRIGIN, - drvdata->base + TMC_FFCR); - - writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG); - tmc_enable_hw(drvdata); - - CS_LOCK(drvdata->base); -} - -static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) -{ - u32 axictl; - - /* Zero out the memory to help with debug */ - memset(drvdata->vaddr, 0, drvdata->size); - - CS_UNLOCK(drvdata->base); - - /* Wait for TMCSReady bit to be set */ - tmc_wait_for_tmcready(drvdata); - - writel_relaxed(drvdata->size / 4, drvdata->base + TMC_RSZ); - writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); - - axictl = readl_relaxed(drvdata->base + TMC_AXICTL); - axictl |= TMC_AXICTL_WR_BURST_16; - writel_relaxed(axictl, drvdata->base + TMC_AXICTL); - axictl &= ~TMC_AXICTL_SCT_GAT_MODE; - writel_relaxed(axictl, drvdata->base + TMC_AXICTL); - axictl = (axictl & - ~(TMC_AXICTL_PROT_CTL_B0 | TMC_AXICTL_PROT_CTL_B1)) | - TMC_AXICTL_PROT_CTL_B1; - writel_relaxed(axictl, drvdata->base + TMC_AXICTL); - - writel_relaxed(drvdata->paddr, drvdata->base + TMC_DBALO); - writel_relaxed(0x0, drvdata->base + TMC_DBAHI); - writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI | - TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT | - TMC_FFCR_TRIGON_TRIGIN, - drvdata->base + TMC_FFCR); - writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG); - tmc_enable_hw(drvdata); - - CS_LOCK(drvdata->base); -} - -static void tmc_etf_enable_hw(struct tmc_drvdata *drvdata) -{ - CS_UNLOCK(drvdata->base); - - /* Wait for TMCSReady bit to be set */ - tmc_wait_for_tmcready(drvdata); - - writel_relaxed(TMC_MODE_HARDWARE_FIFO, drvdata->base + TMC_MODE); - writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI, - drvdata->base + TMC_FFCR); - writel_relaxed(0x0, drvdata->base + TMC_BUFWM); - tmc_enable_hw(drvdata); - - CS_LOCK(drvdata->base); -} - -static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode) -{ - unsigned long flags; - - spin_lock_irqsave(&drvdata->spinlock, flags); - if (drvdata->reading) { - spin_unlock_irqrestore(&drvdata->spinlock, flags); - return -EBUSY; - } - - if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { - tmc_etb_enable_hw(drvdata); - } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { - tmc_etr_enable_hw(drvdata); - } else { - if (mode == TMC_MODE_CIRCULAR_BUFFER) - tmc_etb_enable_hw(drvdata); - else - tmc_etf_enable_hw(drvdata); - } - drvdata->enable = true; - spin_unlock_irqrestore(&drvdata->spinlock, flags); - - dev_info(drvdata->dev, "TMC enabled\n"); - return 0; -} - -static int tmc_enable_sink(struct coresight_device *csdev, u32 mode) -{ - struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - - return tmc_enable(drvdata, TMC_MODE_CIRCULAR_BUFFER); -} - -static int tmc_enable_link(struct coresight_device *csdev, int inport, - int outport) -{ - struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - - return tmc_enable(drvdata, TMC_MODE_HARDWARE_FIFO); -} - -static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata) -{ - enum tmc_mem_intf_width memwidth; - u8 memwords; - char *bufp; - u32 read_data; - int i; - - memwidth = BMVAL(readl_relaxed(drvdata->base + CORESIGHT_DEVID), 8, 10); - if (memwidth == TMC_MEM_INTF_WIDTH_32BITS) - memwords = 1; - else if (memwidth == TMC_MEM_INTF_WIDTH_64BITS) - memwords = 2; - else if (memwidth == TMC_MEM_INTF_WIDTH_128BITS) - memwords = 4; - else - memwords = 8; - - bufp = drvdata->buf; - while (1) { - for (i = 0; i < memwords; i++) { - read_data = readl_relaxed(drvdata->base + TMC_RRD); - if (read_data == 0xFFFFFFFF) - return; - memcpy(bufp, &read_data, 4); - bufp += 4; - } - } -} - -static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata) -{ - CS_UNLOCK(drvdata->base); - - tmc_flush_and_stop(drvdata); - tmc_etb_dump_hw(drvdata); - tmc_disable_hw(drvdata); - - CS_LOCK(drvdata->base); -} - -static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata) -{ - u32 rwp, val; - - rwp = readl_relaxed(drvdata->base + TMC_RWP); - val = readl_relaxed(drvdata->base + TMC_STS); - - /* How much memory do we still have */ - if (val & BIT(0)) - drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr; - else - drvdata->buf = drvdata->vaddr; -} - -static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) -{ - CS_UNLOCK(drvdata->base); - - tmc_flush_and_stop(drvdata); - tmc_etr_dump_hw(drvdata); - tmc_disable_hw(drvdata); - - CS_LOCK(drvdata->base); -} - -static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata) -{ - CS_UNLOCK(drvdata->base); - - tmc_flush_and_stop(drvdata); - tmc_disable_hw(drvdata); - - CS_LOCK(drvdata->base); -} - -static void tmc_disable(struct tmc_drvdata *drvdata, enum tmc_mode mode) -{ - unsigned long flags; - - spin_lock_irqsave(&drvdata->spinlock, flags); - if (drvdata->reading) - goto out; - - if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { - tmc_etb_disable_hw(drvdata); - } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { - tmc_etr_disable_hw(drvdata); - } else { - if (mode == TMC_MODE_CIRCULAR_BUFFER) - tmc_etb_disable_hw(drvdata); - else - tmc_etf_disable_hw(drvdata); - } -out: - drvdata->enable = false; - spin_unlock_irqrestore(&drvdata->spinlock, flags); - - dev_info(drvdata->dev, "TMC disabled\n"); -} - -static void tmc_disable_sink(struct coresight_device *csdev) -{ - struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - - tmc_disable(drvdata, TMC_MODE_CIRCULAR_BUFFER); -} - -static void tmc_disable_link(struct coresight_device *csdev, int inport, - int outport) -{ - struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - - tmc_disable(drvdata, TMC_MODE_HARDWARE_FIFO); -} - -static const struct coresight_ops_sink tmc_sink_ops = { - .enable = tmc_enable_sink, - .disable = tmc_disable_sink, -}; - -static const struct coresight_ops_link tmc_link_ops = { - .enable = tmc_enable_link, - .disable = tmc_disable_link, -}; - -static const struct coresight_ops tmc_etb_cs_ops = { - .sink_ops = &tmc_sink_ops, -}; - -static const struct coresight_ops tmc_etr_cs_ops = { - .sink_ops = &tmc_sink_ops, -}; - -static const struct coresight_ops tmc_etf_cs_ops = { - .sink_ops = &tmc_sink_ops, - .link_ops = &tmc_link_ops, -}; - static int tmc_read_prepare(struct tmc_drvdata *drvdata) { int ret = 0; diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 5a60830c8db5..b3017e115c4d 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -18,6 +18,8 @@ #ifndef _CORESIGHT_TMC_H #define _CORESIGHT_TMC_H +#include + #define TMC_RSZ 0x004 #define TMC_STS 0x00c #define TMC_RRD 0x010 @@ -118,4 +120,20 @@ struct tmc_drvdata { u32 trigger_cntr; }; +/* Generic functions */ +void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata); +void tmc_flush_and_stop(struct tmc_drvdata *drvdata); +void tmc_enable_hw(struct tmc_drvdata *drvdata); +void tmc_disable_hw(struct tmc_drvdata *drvdata); + +/* ETB/ETF functions */ +void tmc_etb_enable_hw(struct tmc_drvdata *drvdata); +void tmc_etb_disable_hw(struct tmc_drvdata *drvdata); +extern const struct coresight_ops tmc_etb_cs_ops; +extern const struct coresight_ops tmc_etf_cs_ops; + +/* ETR functions */ +void tmc_etr_enable_hw(struct tmc_drvdata *drvdata); +void tmc_etr_disable_hw(struct tmc_drvdata *drvdata); +extern const struct coresight_ops tmc_etr_cs_ops; #endif From 5e0bd5f02c29e095fbd68b3732e6ee83f1c941ad Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:51 -0600 Subject: [PATCH 094/104] coresight: tmc: making prepare/unprepare functions generic Dealing with HW related matters in tmc_read_prepare/unprepare becomes convoluted when many cases need to be handled distinctively. As such moving processing related to HW setup to individual driver files and keep the core driver generic. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 4525412a5046692abb7a0588589d8ed2c20585e0) --- .../hwtracing/coresight/coresight-tmc-etf.c | 62 ++++++++++++++++++- .../hwtracing/coresight/coresight-tmc-etr.c | 42 ++++++++++++- drivers/hwtracing/coresight/coresight-tmc.c | 55 ++++------------ drivers/hwtracing/coresight/coresight-tmc.h | 8 +-- 4 files changed, 117 insertions(+), 50 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 467d19221f7b..91e43572ce9f 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -71,7 +71,7 @@ static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata) } } -void tmc_etb_disable_hw(struct tmc_drvdata *drvdata) +static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata) { CS_UNLOCK(drvdata->base); @@ -202,3 +202,63 @@ const struct coresight_ops tmc_etf_cs_ops = { .sink_ops = &tmc_etf_sink_ops, .link_ops = &tmc_etf_link_ops, }; + +int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) +{ + enum tmc_mode mode; + int ret = 0; + unsigned long flags; + + /* config types are set a boot time and never change */ + if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETB && + drvdata->config_type != TMC_CONFIG_TYPE_ETF)) + return -EINVAL; + + spin_lock_irqsave(&drvdata->spinlock, flags); + + /* There is no point in reading a TMC in HW FIFO mode */ + mode = readl_relaxed(drvdata->base + TMC_MODE); + if (mode != TMC_MODE_CIRCULAR_BUFFER) { + ret = -EINVAL; + goto out; + } + + /* Disable the TMC if need be */ + if (drvdata->enable) + tmc_etb_disable_hw(drvdata); + + drvdata->reading = true; +out: + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + return ret; +} + +int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata) +{ + enum tmc_mode mode; + unsigned long flags; + + /* config types are set a boot time and never change */ + if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETB && + drvdata->config_type != TMC_CONFIG_TYPE_ETF)) + return -EINVAL; + + spin_lock_irqsave(&drvdata->spinlock, flags); + + /* There is no point in reading a TMC in HW FIFO mode */ + mode = readl_relaxed(drvdata->base + TMC_MODE); + if (mode != TMC_MODE_CIRCULAR_BUFFER) { + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return -EINVAL; + } + + /* Re-enable the TMC if need be */ + if (drvdata->enable) + tmc_etb_enable_hw(drvdata); + + drvdata->reading = false; + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + return 0; +} diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 5d9333ec49ae..3483d139a4ac 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -70,7 +70,7 @@ static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata) drvdata->buf = drvdata->vaddr; } -void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) +static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) { CS_UNLOCK(drvdata->base); @@ -126,3 +126,43 @@ static const struct coresight_ops_sink tmc_etr_sink_ops = { const struct coresight_ops tmc_etr_cs_ops = { .sink_ops = &tmc_etr_sink_ops, }; + +int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) +{ + unsigned long flags; + + /* config types are set a boot time and never change */ + if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR)) + return -EINVAL; + + spin_lock_irqsave(&drvdata->spinlock, flags); + + /* Disable the TMC if need be */ + if (drvdata->enable) + tmc_etr_disable_hw(drvdata); + + drvdata->reading = true; + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + return 0; +} + +int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) +{ + unsigned long flags; + + /* config types are set a boot time and never change */ + if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR)) + return -EINVAL; + + spin_lock_irqsave(&drvdata->spinlock, flags); + + /* RE-enable the TMC if need be */ + if (drvdata->enable) + tmc_etr_enable_hw(drvdata); + + drvdata->reading = false; + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + return 0; +} diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 8d7f6d54c9b0..63f8e55116a6 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -76,76 +76,43 @@ void tmc_disable_hw(struct tmc_drvdata *drvdata) static int tmc_read_prepare(struct tmc_drvdata *drvdata) { int ret = 0; - unsigned long flags; - enum tmc_mode mode; - - spin_lock_irqsave(&drvdata->spinlock, flags); - if (!drvdata->enable) - goto out; switch (drvdata->config_type) { case TMC_CONFIG_TYPE_ETB: - tmc_etb_disable_hw(drvdata); - break; case TMC_CONFIG_TYPE_ETF: - /* There is no point in reading a TMC in HW FIFO mode */ - mode = readl_relaxed(drvdata->base + TMC_MODE); - if (mode != TMC_MODE_CIRCULAR_BUFFER) { - ret = -EINVAL; - goto err; - } - - tmc_etb_disable_hw(drvdata); + ret = tmc_read_prepare_etb(drvdata); break; case TMC_CONFIG_TYPE_ETR: - tmc_etr_disable_hw(drvdata); + ret = tmc_read_prepare_etr(drvdata); break; default: ret = -EINVAL; - goto err; } -out: - drvdata->reading = true; - dev_info(drvdata->dev, "TMC read start\n"); -err: - spin_unlock_irqrestore(&drvdata->spinlock, flags); + if (!ret) + dev_info(drvdata->dev, "TMC read start\n"); + return ret; } static void tmc_read_unprepare(struct tmc_drvdata *drvdata) { - unsigned long flags; - enum tmc_mode mode; - - spin_lock_irqsave(&drvdata->spinlock, flags); - if (!drvdata->enable) - goto out; + int ret = 0; switch (drvdata->config_type) { case TMC_CONFIG_TYPE_ETB: - tmc_etb_enable_hw(drvdata); - break; case TMC_CONFIG_TYPE_ETF: - /* Make sure we don't re-enable a TMC in HW FIFO mode */ - mode = readl_relaxed(drvdata->base + TMC_MODE); - if (mode != TMC_MODE_CIRCULAR_BUFFER) - goto err; - - tmc_etb_enable_hw(drvdata); + ret = tmc_read_unprepare_etb(drvdata); break; case TMC_CONFIG_TYPE_ETR: - tmc_etr_disable_hw(drvdata); + ret = tmc_read_unprepare_etr(drvdata); break; default: - goto err; + ret = -EINVAL; } -out: - drvdata->reading = false; - dev_info(drvdata->dev, "TMC read end\n"); -err: - spin_unlock_irqrestore(&drvdata->spinlock, flags); + if (!ret) + dev_info(drvdata->dev, "TMC read end\n"); } static int tmc_open(struct inode *inode, struct file *file) diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index b3017e115c4d..df661903f83c 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -127,13 +127,13 @@ void tmc_enable_hw(struct tmc_drvdata *drvdata); void tmc_disable_hw(struct tmc_drvdata *drvdata); /* ETB/ETF functions */ -void tmc_etb_enable_hw(struct tmc_drvdata *drvdata); -void tmc_etb_disable_hw(struct tmc_drvdata *drvdata); +int tmc_read_prepare_etb(struct tmc_drvdata *drvdata); +int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata); extern const struct coresight_ops tmc_etb_cs_ops; extern const struct coresight_ops tmc_etf_cs_ops; /* ETR functions */ -void tmc_etr_enable_hw(struct tmc_drvdata *drvdata); -void tmc_etr_disable_hw(struct tmc_drvdata *drvdata); +int tmc_read_prepare_etr(struct tmc_drvdata *drvdata); +int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata); extern const struct coresight_ops tmc_etr_cs_ops; #endif From 17c61c32ab5a2e0ff67e00e4d562ba0a97b999f2 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:52 -0600 Subject: [PATCH 095/104] coresight: tmc: allocating memory when needed In it's current form the TMC probe() function allocates trace buffer memory at boot time, event if coresight isn't used. This is highly inefficient since trace buffers can occupy a lot of memory that could be used otherwised. This patch allocates trace buffers on the fly, when the coresight subsystem is solicited. Allocated buffers are released when traces are read using the device descriptors under /dev. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit de5461970b3e9e19470b821f5feaa3235ceb35f5) --- .../hwtracing/coresight/coresight-tmc-etf.c | 90 ++++++++++++++++-- .../hwtracing/coresight/coresight-tmc-etr.c | 93 ++++++++++++++++++- drivers/hwtracing/coresight/coresight-tmc.c | 14 --- 3 files changed, 170 insertions(+), 27 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 91e43572ce9f..6eb1665cfc9c 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -16,14 +16,12 @@ */ #include +#include #include "coresight-priv.h" #include "coresight-tmc.h" void tmc_etb_enable_hw(struct tmc_drvdata *drvdata) { - /* Zero out the memory to help with debug */ - memset(drvdata->buf, 0, drvdata->size); - CS_UNLOCK(drvdata->base); /* Wait for TMCSReady bit to be set */ @@ -110,21 +108,67 @@ static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata) static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode) { + int ret = 0; + bool used = false; + char *buf = NULL; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + /* This shouldn't be happening */ + if (WARN_ON(mode != CS_MODE_SYSFS)) + return -EINVAL; + + /* + * If we don't have a buffer release the lock and allocate memory. + * Otherwise keep the lock and move along. + */ spin_lock_irqsave(&drvdata->spinlock, flags); - if (drvdata->reading) { + if (!drvdata->buf) { spin_unlock_irqrestore(&drvdata->spinlock, flags); - return -EBUSY; + + /* Allocating the memory here while outside of the spinlock */ + buf = kzalloc(drvdata->size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* Let's try again */ + spin_lock_irqsave(&drvdata->spinlock, flags); + } + + if (drvdata->reading) { + ret = -EBUSY; + goto out; + } + + /* + * If drvdata::buf isn't NULL, memory was allocated for a previous + * trace run but wasn't read. If so simply zero-out the memory. + * Otherwise use the memory allocated above. + * + * The memory is freed when users read the buffer using the + * /dev/xyz.{etf|etb} interface. See tmc_read_unprepare_etf() for + * details. + */ + if (drvdata->buf) { + memset(drvdata->buf, 0, drvdata->size); + } else { + used = true; + drvdata->buf = buf; } tmc_etb_enable_hw(drvdata); drvdata->enable = true; +out: spin_unlock_irqrestore(&drvdata->spinlock, flags); - dev_info(drvdata->dev, "TMC-ETB/ETF enabled\n"); - return 0; + /* Free memory outside the spinlock if need be */ + if (!used && buf) + kfree(buf); + + if (!ret) + dev_info(drvdata->dev, "TMC-ETB/ETF enabled\n"); + + return ret; } static void tmc_disable_etf_sink(struct coresight_device *csdev) @@ -223,6 +267,12 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) goto out; } + /* If drvdata::buf is NULL the trace data has been read already */ + if (drvdata->buf == NULL) { + ret = -EINVAL; + goto out; + } + /* Disable the TMC if need be */ if (drvdata->enable) tmc_etb_disable_hw(drvdata); @@ -236,6 +286,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata) { + char *buf = NULL; enum tmc_mode mode; unsigned long flags; @@ -254,11 +305,34 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata) } /* Re-enable the TMC if need be */ - if (drvdata->enable) + if (drvdata->enable) { + /* + * The trace run will continue with the same allocated trace + * buffer. As such zero-out the buffer so that we don't end + * up with stale data. + * + * Since the tracer is still enabled drvdata::buf + * can't be NULL. + */ + memset(drvdata->buf, 0, drvdata->size); tmc_etb_enable_hw(drvdata); + } else { + /* + * The ETB/ETF is not tracing and the buffer was just read. + * As such prepare to free the trace buffer. + */ + buf = drvdata->buf; + drvdata->buf = NULL; + } drvdata->reading = false; spin_unlock_irqrestore(&drvdata->spinlock, flags); + /* + * Free allocated memory outside of the spinlock. There is no need + * to assert the validity of 'buf' since calling kfree(NULL) is safe. + */ + kfree(buf); + return 0; } diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 3483d139a4ac..ac37bf904fb7 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -16,6 +16,7 @@ */ #include +#include #include "coresight-priv.h" #include "coresight-tmc.h" @@ -83,21 +84,71 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode) { + int ret = 0; + bool used = false; unsigned long flags; + void __iomem *vaddr = NULL; + dma_addr_t paddr; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + /* This shouldn't be happening */ + if (WARN_ON(mode != CS_MODE_SYSFS)) + return -EINVAL; + + /* + * If we don't have a buffer release the lock and allocate memory. + * Otherwise keep the lock and move along. + */ spin_lock_irqsave(&drvdata->spinlock, flags); - if (drvdata->reading) { + if (!drvdata->vaddr) { spin_unlock_irqrestore(&drvdata->spinlock, flags); - return -EBUSY; + + /* + * Contiguous memory can't be allocated while a spinlock is + * held. As such allocate memory here and free it if a buffer + * has already been allocated (from a previous session). + */ + vaddr = dma_alloc_coherent(drvdata->dev, drvdata->size, + &paddr, GFP_KERNEL); + if (!vaddr) + return -ENOMEM; + + /* Let's try again */ + spin_lock_irqsave(&drvdata->spinlock, flags); } + if (drvdata->reading) { + ret = -EBUSY; + goto out; + } + + /* + * If drvdata::buf == NULL, use the memory allocated above. + * Otherwise a buffer still exists from a previous session, so + * simply use that. + */ + if (drvdata->buf == NULL) { + used = true; + drvdata->vaddr = vaddr; + drvdata->paddr = paddr; + drvdata->buf = drvdata->vaddr; + } + + memset(drvdata->vaddr, 0, drvdata->size); + tmc_etr_enable_hw(drvdata); drvdata->enable = true; +out: spin_unlock_irqrestore(&drvdata->spinlock, flags); - dev_info(drvdata->dev, "TMC-ETR enabled\n"); - return 0; + /* Free memory outside the spinlock if need be */ + if (!used && vaddr) + dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr); + + if (!ret) + dev_info(drvdata->dev, "TMC-ETR enabled\n"); + + return ret; } static void tmc_disable_etr_sink(struct coresight_device *csdev) @@ -129,6 +180,7 @@ const struct coresight_ops tmc_etr_cs_ops = { int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) { + int ret = 0; unsigned long flags; /* config types are set a boot time and never change */ @@ -137,11 +189,18 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) spin_lock_irqsave(&drvdata->spinlock, flags); + /* If drvdata::buf is NULL the trace data has been read already */ + if (drvdata->buf == NULL) { + ret = -EINVAL; + goto out; + } + /* Disable the TMC if need be */ if (drvdata->enable) tmc_etr_disable_hw(drvdata); drvdata->reading = true; +out: spin_unlock_irqrestore(&drvdata->spinlock, flags); return 0; @@ -150,6 +209,8 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) { unsigned long flags; + dma_addr_t paddr; + void __iomem *vaddr = NULL; /* config types are set a boot time and never change */ if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR)) @@ -158,11 +219,33 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) spin_lock_irqsave(&drvdata->spinlock, flags); /* RE-enable the TMC if need be */ - if (drvdata->enable) + if (drvdata->enable) { + /* + * The trace run will continue with the same allocated trace + * buffer. As such zero-out the buffer so that we don't end + * up with stale data. + * + * Since the tracer is still enabled drvdata::buf + * can't be NULL. + */ + memset(drvdata->buf, 0, drvdata->size); tmc_etr_enable_hw(drvdata); + } else { + /* + * The ETR is not tracing and the buffer was just read. + * As such prepare to free the trace buffer. + */ + vaddr = drvdata->vaddr; + paddr = drvdata->paddr; + drvdata->buf = NULL; + } drvdata->reading = false; spin_unlock_irqrestore(&drvdata->spinlock, flags); + /* Free allocated memory out side of the spinlock */ + if (vaddr) + dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr); + return 0; } diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 63f8e55116a6..e8e12a9b917a 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -319,20 +319,6 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) pm_runtime_put(&adev->dev); - if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { - drvdata->vaddr = dma_alloc_coherent(dev, drvdata->size, - &drvdata->paddr, GFP_KERNEL); - if (!drvdata->vaddr) - return -ENOMEM; - - memset(drvdata->vaddr, 0, drvdata->size); - drvdata->buf = drvdata->vaddr; - } else { - drvdata->buf = devm_kzalloc(dev, drvdata->size, GFP_KERNEL); - if (!drvdata->buf) - return -ENOMEM; - } - desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); if (!desc) { ret = -ENOMEM; From 09c5cf1702bf498bd5d30e94eda0d855dbb301df Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:53 -0600 Subject: [PATCH 096/104] coresight: tmc: getting rid of multiple read access Allowing multiple readers to access the trace data simultaniously via sysFS provides no shortage of opportunity for race condition, mandates two variable to be maintained (drvdata::read_count and drvdata::reading), makes the code complex and provide little advantages, if any. This patch streamlines the read process by restricting trace data access to a single user. That way drvdata::read_count can be eliminated and race conditions (along with faulty error handling) in function tmc_open() and tmc_release() eliminated. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit f74debbea0885ebb65fb3fa4e598323f40b03f5f) --- .../hwtracing/coresight/coresight-tmc-etf.c | 5 ++++ .../hwtracing/coresight/coresight-tmc-etr.c | 4 ++++ drivers/hwtracing/coresight/coresight-tmc.c | 24 +++++++------------ drivers/hwtracing/coresight/coresight-tmc.h | 2 -- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 6eb1665cfc9c..60edf4d1968f 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -260,6 +260,11 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) spin_lock_irqsave(&drvdata->spinlock, flags); + if (drvdata->reading) { + ret = -EBUSY; + goto out; + } + /* There is no point in reading a TMC in HW FIFO mode */ mode = readl_relaxed(drvdata->base + TMC_MODE); if (mode != TMC_MODE_CIRCULAR_BUFFER) { diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index ac37bf904fb7..d6999b457fb8 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -188,6 +188,10 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) return -EINVAL; spin_lock_irqsave(&drvdata->spinlock, flags); + if (drvdata->reading) { + ret = -EBUSY; + goto out; + } /* If drvdata::buf is NULL the trace data has been read already */ if (drvdata->buf == NULL) { diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index e8e12a9b917a..ae7525a2b94a 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -95,7 +95,7 @@ static int tmc_read_prepare(struct tmc_drvdata *drvdata) return ret; } -static void tmc_read_unprepare(struct tmc_drvdata *drvdata) +static int tmc_read_unprepare(struct tmc_drvdata *drvdata) { int ret = 0; @@ -113,21 +113,20 @@ static void tmc_read_unprepare(struct tmc_drvdata *drvdata) if (!ret) dev_info(drvdata->dev, "TMC read end\n"); + + return ret; } static int tmc_open(struct inode *inode, struct file *file) { + int ret; struct tmc_drvdata *drvdata = container_of(file->private_data, struct tmc_drvdata, miscdev); - int ret = 0; - - if (drvdata->read_count++) - goto out; ret = tmc_read_prepare(drvdata); if (ret) return ret; -out: + nonseekable_open(inode, file); dev_dbg(drvdata->dev, "%s: successfully opened\n", __func__); @@ -167,19 +166,14 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len, static int tmc_release(struct inode *inode, struct file *file) { + int ret; struct tmc_drvdata *drvdata = container_of(file->private_data, struct tmc_drvdata, miscdev); - if (--drvdata->read_count) { - if (drvdata->read_count < 0) { - dev_err(drvdata->dev, "mismatched close\n"); - drvdata->read_count = 0; - } - goto out; - } + ret = tmc_read_unprepare(drvdata); + if (ret) + return ret; - tmc_read_unprepare(drvdata); -out: dev_dbg(drvdata->dev, "%s: released\n", __func__); return 0; } diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index df661903f83c..592eb149fe3a 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -94,7 +94,6 @@ enum tmc_mem_intf_width { * @csdev: component vitals needed by the framework. * @miscdev: specifics to handle "/dev/xyz.tmc" entry. * @spinlock: only one at a time pls. - * @read_count: manages preparation of buffer for reading. * @buf: area of memory where trace data get sent. * @paddr: DMA start location in RAM. * @vaddr: virtual representation of @paddr. @@ -109,7 +108,6 @@ struct tmc_drvdata { struct coresight_device *csdev; struct miscdevice miscdev; spinlock_t spinlock; - int read_count; bool reading; char *buf; dma_addr_t paddr; From cc878b6f43fbac6be550554870f053d20a77da73 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:54 -0600 Subject: [PATCH 097/104] coresight: tmc: adding mode of operation for link/sinks Moving tmc_drvdata::enable to a local_t mode. That way the sink interface is aware of it's orgin and the foundation for mutual exclusion between the sysFS and Perf interface can be laid out. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit f2facc3366d77e78dbc8bf865f1e4a6227a7f0e5) --- .../hwtracing/coresight/coresight-tmc-etf.c | 27 ++++++++++++++----- .../hwtracing/coresight/coresight-tmc-etr.c | 23 ++++++++++++---- drivers/hwtracing/coresight/coresight-tmc.h | 4 +-- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 60edf4d1968f..bc0efc1e5b49 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -111,6 +111,7 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode) int ret = 0; bool used = false; char *buf = NULL; + long val; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -140,6 +141,15 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode) goto out; } + val = local_xchg(&drvdata->mode, mode); + /* + * In sysFS mode we can have multiple writers per sink. Since this + * sink is already enabled no memory is needed and the HW need not be + * touched. + */ + if (val == CS_MODE_SYSFS) + goto out; + /* * If drvdata::buf isn't NULL, memory was allocated for a previous * trace run but wasn't read. If so simply zero-out the memory. @@ -157,7 +167,6 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode) } tmc_etb_enable_hw(drvdata); - drvdata->enable = true; out: spin_unlock_irqrestore(&drvdata->spinlock, flags); @@ -173,6 +182,7 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode) static void tmc_disable_etf_sink(struct coresight_device *csdev) { + long val; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -182,8 +192,11 @@ static void tmc_disable_etf_sink(struct coresight_device *csdev) return; } - tmc_etb_disable_hw(drvdata); - drvdata->enable = false; + val = local_xchg(&drvdata->mode, CS_MODE_DISABLED); + /* Disable the TMC only if it needs to */ + if (val != CS_MODE_DISABLED) + tmc_etb_disable_hw(drvdata); + spin_unlock_irqrestore(&drvdata->spinlock, flags); dev_info(drvdata->dev, "TMC-ETB/ETF disabled\n"); @@ -202,7 +215,7 @@ static int tmc_enable_etf_link(struct coresight_device *csdev, } tmc_etf_enable_hw(drvdata); - drvdata->enable = true; + local_set(&drvdata->mode, CS_MODE_SYSFS); spin_unlock_irqrestore(&drvdata->spinlock, flags); dev_info(drvdata->dev, "TMC-ETF enabled\n"); @@ -222,7 +235,7 @@ static void tmc_disable_etf_link(struct coresight_device *csdev, } tmc_etf_disable_hw(drvdata); - drvdata->enable = false; + local_set(&drvdata->mode, CS_MODE_DISABLED); spin_unlock_irqrestore(&drvdata->spinlock, flags); dev_info(drvdata->dev, "TMC disabled\n"); @@ -279,7 +292,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) } /* Disable the TMC if need be */ - if (drvdata->enable) + if (local_read(&drvdata->mode) == CS_MODE_SYSFS) tmc_etb_disable_hw(drvdata); drvdata->reading = true; @@ -310,7 +323,7 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata) } /* Re-enable the TMC if need be */ - if (drvdata->enable) { + if (local_read(&drvdata->mode) == CS_MODE_SYSFS) { /* * The trace run will continue with the same allocated trace * buffer. As such zero-out the buffer so that we don't end diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index d6999b457fb8..0c107811a232 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -86,6 +86,7 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode) { int ret = 0; bool used = false; + long val; unsigned long flags; void __iomem *vaddr = NULL; dma_addr_t paddr; @@ -122,6 +123,15 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode) goto out; } + val = local_xchg(&drvdata->mode, mode); + /* + * In sysFS mode we can have multiple writers per sink. Since this + * sink is already enabled no memory is needed and the HW need not be + * touched. + */ + if (val == CS_MODE_SYSFS) + goto out; + /* * If drvdata::buf == NULL, use the memory allocated above. * Otherwise a buffer still exists from a previous session, so @@ -137,7 +147,6 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode) memset(drvdata->vaddr, 0, drvdata->size); tmc_etr_enable_hw(drvdata); - drvdata->enable = true; out: spin_unlock_irqrestore(&drvdata->spinlock, flags); @@ -153,6 +162,7 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode) static void tmc_disable_etr_sink(struct coresight_device *csdev) { + long val; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -162,8 +172,11 @@ static void tmc_disable_etr_sink(struct coresight_device *csdev) return; } - tmc_etr_disable_hw(drvdata); - drvdata->enable = false; + val = local_xchg(&drvdata->mode, CS_MODE_DISABLED); + /* Disable the TMC only if it needs to */ + if (val != CS_MODE_DISABLED) + tmc_etr_disable_hw(drvdata); + spin_unlock_irqrestore(&drvdata->spinlock, flags); dev_info(drvdata->dev, "TMC-ETR disabled\n"); @@ -200,7 +213,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) } /* Disable the TMC if need be */ - if (drvdata->enable) + if (local_read(&drvdata->mode) == CS_MODE_SYSFS) tmc_etr_disable_hw(drvdata); drvdata->reading = true; @@ -223,7 +236,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) spin_lock_irqsave(&drvdata->spinlock, flags); /* RE-enable the TMC if need be */ - if (drvdata->enable) { + if (local_read(&drvdata->mode) == CS_MODE_SYSFS) { /* * The trace run will continue with the same allocated trace * buffer. As such zero-out the buffer so that we don't end diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 592eb149fe3a..94bc034d3b98 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -98,7 +98,7 @@ enum tmc_mem_intf_width { * @paddr: DMA start location in RAM. * @vaddr: virtual representation of @paddr. * @size: @buf size. - * @enable: this TMC is being used. + * @mode: how this TMC is being used. * @config_type: TMC variant, must be of type @tmc_config_type. * @trigger_cntr: amount of words to store after a trigger. */ @@ -113,7 +113,7 @@ struct tmc_drvdata { dma_addr_t paddr; void __iomem *vaddr; u32 size; - bool enable; + local_t mode; enum tmc_config_type config_type; u32 trigger_cntr; }; From 29b1d2628eb749a6fe9675f2165b2a53721fde55 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:55 -0600 Subject: [PATCH 098/104] coresight: tmc: dump system memory content only when needed Calling tmc_etf/etr_dump_hw() is required only when operating from sysFS. When working from Perf, the system memory is harvested from the AUX trace API. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit a40318fb01e98e72175bd9891208541148633d42) --- drivers/hwtracing/coresight/coresight-tmc-etf.c | 7 ++++++- drivers/hwtracing/coresight/coresight-tmc-etr.c | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index bc0efc1e5b49..b5e5e6ac67eb 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -74,7 +74,12 @@ static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata) CS_UNLOCK(drvdata->base); tmc_flush_and_stop(drvdata); - tmc_etb_dump_hw(drvdata); + /* + * When operating in sysFS mode the content of the buffer needs to be + * read before the TMC is disabled. + */ + if (local_read(&drvdata->mode) == CS_MODE_SYSFS) + tmc_etb_dump_hw(drvdata); tmc_disable_hw(drvdata); CS_LOCK(drvdata->base); diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 0c107811a232..7208584d0da7 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -76,7 +76,12 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) CS_UNLOCK(drvdata->base); tmc_flush_and_stop(drvdata); - tmc_etr_dump_hw(drvdata); + /* + * When operating in sysFS mode the content of the buffer needs to be + * read before the TMC is disabled. + */ + if (local_read(&drvdata->mode) == CS_MODE_SYSFS) + tmc_etr_dump_hw(drvdata); tmc_disable_hw(drvdata); CS_LOCK(drvdata->base); From 6b21a3edf2e21fb3267efe6d1a67e68f8e73b14e Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:56 -0600 Subject: [PATCH 099/104] coresight: tmc: make sysFS and Perf mode mutually exclusive The sysFS and Perf access methods can't be allowed to interfere with one another. As such introducing guards to access functions that prevents moving forward if a TMC is already being used. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit b217601e9adce4d2dccc95a9e6814bdbf5a4a815) --- .../hwtracing/coresight/coresight-tmc-etf.c | 60 +++++++++++++++++- .../hwtracing/coresight/coresight-tmc-etr.c | 62 ++++++++++++++++++- 2 files changed, 117 insertions(+), 5 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index b5e5e6ac67eb..b11c52be54a9 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -111,7 +111,7 @@ static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata) CS_LOCK(drvdata->base); } -static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode) +static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev, u32 mode) { int ret = 0; bool used = false; @@ -185,6 +185,54 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode) return ret; } +static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, u32 mode) +{ + int ret = 0; + long val; + unsigned long flags; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + /* This shouldn't be happening */ + if (WARN_ON(mode != CS_MODE_PERF)) + return -EINVAL; + + spin_lock_irqsave(&drvdata->spinlock, flags); + if (drvdata->reading) { + ret = -EINVAL; + goto out; + } + + val = local_xchg(&drvdata->mode, mode); + /* + * In Perf mode there can be only one writer per sink. There + * is also no need to continue if the ETB/ETR is already operated + * from sysFS. + */ + if (val != CS_MODE_DISABLED) { + ret = -EINVAL; + goto out; + } + + tmc_etb_enable_hw(drvdata); +out: + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + return ret; +} + +static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode) +{ + switch (mode) { + case CS_MODE_SYSFS: + return tmc_enable_etf_sink_sysfs(csdev, mode); + case CS_MODE_PERF: + return tmc_enable_etf_sink_perf(csdev, mode); + } + + /* We shouldn't be here */ + return -EINVAL; +} + static void tmc_disable_etf_sink(struct coresight_device *csdev) { long val; @@ -267,6 +315,7 @@ const struct coresight_ops tmc_etf_cs_ops = { int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) { + long val; enum tmc_mode mode; int ret = 0; unsigned long flags; @@ -290,6 +339,13 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) goto out; } + val = local_read(&drvdata->mode); + /* Don't interfere if operated from Perf */ + if (val == CS_MODE_PERF) { + ret = -EINVAL; + goto out; + } + /* If drvdata::buf is NULL the trace data has been read already */ if (drvdata->buf == NULL) { ret = -EINVAL; @@ -297,7 +353,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) } /* Disable the TMC if need be */ - if (local_read(&drvdata->mode) == CS_MODE_SYSFS) + if (val == CS_MODE_SYSFS) tmc_etb_disable_hw(drvdata); drvdata->reading = true; diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 7208584d0da7..847d1b5f2c13 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -87,7 +87,7 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) CS_LOCK(drvdata->base); } -static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode) +static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev, u32 mode) { int ret = 0; bool used = false; @@ -165,6 +165,54 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode) return ret; } +static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, u32 mode) +{ + int ret = 0; + long val; + unsigned long flags; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + /* This shouldn't be happening */ + if (WARN_ON(mode != CS_MODE_PERF)) + return -EINVAL; + + spin_lock_irqsave(&drvdata->spinlock, flags); + if (drvdata->reading) { + ret = -EINVAL; + goto out; + } + + val = local_xchg(&drvdata->mode, mode); + /* + * In Perf mode there can be only one writer per sink. There + * is also no need to continue if the ETR is already operated + * from sysFS. + */ + if (val != CS_MODE_DISABLED) { + ret = -EINVAL; + goto out; + } + + tmc_etr_enable_hw(drvdata); +out: + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + return ret; +} + +static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode) +{ + switch (mode) { + case CS_MODE_SYSFS: + return tmc_enable_etr_sink_sysfs(csdev, mode); + case CS_MODE_PERF: + return tmc_enable_etr_sink_perf(csdev, mode); + } + + /* We shouldn't be here */ + return -EINVAL; +} + static void tmc_disable_etr_sink(struct coresight_device *csdev) { long val; @@ -199,6 +247,7 @@ const struct coresight_ops tmc_etr_cs_ops = { int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) { int ret = 0; + long val; unsigned long flags; /* config types are set a boot time and never change */ @@ -211,6 +260,13 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) goto out; } + val = local_read(&drvdata->mode); + /* Don't interfere if operated from Perf */ + if (val == CS_MODE_PERF) { + ret = -EINVAL; + goto out; + } + /* If drvdata::buf is NULL the trace data has been read already */ if (drvdata->buf == NULL) { ret = -EINVAL; @@ -218,14 +274,14 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) } /* Disable the TMC if need be */ - if (local_read(&drvdata->mode) == CS_MODE_SYSFS) + if (val == CS_MODE_SYSFS) tmc_etr_disable_hw(drvdata); drvdata->reading = true; out: spin_unlock_irqrestore(&drvdata->spinlock, flags); - return 0; + return ret; } int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) From 807aa8f320d493a3eb62226d48f1dec6bccce703 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:57 -0600 Subject: [PATCH 100/104] coresight: tmc: keep track of memory width Accessing the HW configuration register each time the memory width is needed simply doesn't make sense. It is much more efficient to read the value once and keep a reference for later use. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 4f1ff3de925d741b0b77c59bc1387cb940ad7c73) --- .../hwtracing/coresight/coresight-tmc-etf.c | 14 +------- drivers/hwtracing/coresight/coresight-tmc.c | 34 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-tmc.h | 10 +++--- 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index b11c52be54a9..ba3384781f71 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -41,25 +41,13 @@ void tmc_etb_enable_hw(struct tmc_drvdata *drvdata) static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata) { - enum tmc_mem_intf_width memwidth; - u8 memwords; char *bufp; u32 read_data; int i; - memwidth = BMVAL(readl_relaxed(drvdata->base + CORESIGHT_DEVID), 8, 10); - if (memwidth == TMC_MEM_INTF_WIDTH_32BITS) - memwords = 1; - else if (memwidth == TMC_MEM_INTF_WIDTH_64BITS) - memwords = 2; - else if (memwidth == TMC_MEM_INTF_WIDTH_128BITS) - memwords = 4; - else - memwords = 8; - bufp = drvdata->buf; while (1) { - for (i = 0; i < memwords; i++) { + for (i = 0; i < drvdata->memwidth; i++) { read_data = readl_relaxed(drvdata->base + TMC_RRD); if (read_data == 0xFFFFFFFF) return; diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index ae7525a2b94a..9e02ac963cd0 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -186,6 +186,39 @@ static const struct file_operations tmc_fops = { .llseek = no_llseek, }; +static enum tmc_mem_intf_width tmc_get_memwidth(u32 devid) +{ + enum tmc_mem_intf_width memwidth; + + /* + * Excerpt from the TRM: + * + * DEVID::MEMWIDTH[10:8] + * 0x2 Memory interface databus is 32 bits wide. + * 0x3 Memory interface databus is 64 bits wide. + * 0x4 Memory interface databus is 128 bits wide. + * 0x5 Memory interface databus is 256 bits wide. + */ + switch (BMVAL(devid, 8, 10)) { + case 0x2: + memwidth = TMC_MEM_INTF_WIDTH_32BITS; + break; + case 0x3: + memwidth = TMC_MEM_INTF_WIDTH_64BITS; + break; + case 0x4: + memwidth = TMC_MEM_INTF_WIDTH_128BITS; + break; + case 0x5: + memwidth = TMC_MEM_INTF_WIDTH_256BITS; + break; + default: + memwidth = 0; + } + + return memwidth; +} + #define coresight_tmc_simple_func(name, offset) \ coresight_simple_func(struct tmc_drvdata, name, offset) @@ -299,6 +332,7 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID); drvdata->config_type = BMVAL(devid, 6, 7); + drvdata->memwidth = tmc_get_memwidth(devid); if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { if (np) diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 94bc034d3b98..c5d06fd57fa8 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -81,10 +81,10 @@ enum tmc_mode { }; enum tmc_mem_intf_width { - TMC_MEM_INTF_WIDTH_32BITS = 0x2, - TMC_MEM_INTF_WIDTH_64BITS = 0x3, - TMC_MEM_INTF_WIDTH_128BITS = 0x4, - TMC_MEM_INTF_WIDTH_256BITS = 0x5, + TMC_MEM_INTF_WIDTH_32BITS = 1, + TMC_MEM_INTF_WIDTH_64BITS = 2, + TMC_MEM_INTF_WIDTH_128BITS = 4, + TMC_MEM_INTF_WIDTH_256BITS = 8, }; /** @@ -100,6 +100,7 @@ enum tmc_mem_intf_width { * @size: @buf size. * @mode: how this TMC is being used. * @config_type: TMC variant, must be of type @tmc_config_type. + * @memwidth: width of the memory interface databus, in bytes. * @trigger_cntr: amount of words to store after a trigger. */ struct tmc_drvdata { @@ -115,6 +116,7 @@ struct tmc_drvdata { u32 size; local_t mode; enum tmc_config_type config_type; + enum tmc_mem_intf_width memwidth; u32 trigger_cntr; }; From 57b6b76d6f9a17797302efff65720b64326ec402 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:58 -0600 Subject: [PATCH 101/104] coresight: moving struct cs_buffers to header file That way we can re-use the structure in other drivers. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit a02e81f7a32b49f3cb70c5ebd2eab5608a088514) --- drivers/hwtracing/coresight/coresight-etb10.c | 20 ------------------- drivers/hwtracing/coresight/coresight-priv.h | 20 +++++++++++++++++++ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index b0d402dbfeae..92f942321e1e 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -70,26 +70,6 @@ #define ETB_FFSR_BIT 1 #define ETB_FRAME_SIZE_WORDS 4 -/** - * struct cs_buffer - keep track of a recording session' specifics - * @cur: index of the current buffer - * @nr_pages: max number of pages granted to us - * @offset: offset within the current buffer - * @data_size: how much we collected in this run - * @lost: other than zero if we had a HW buffer wrap around - * @snapshot: is this run in snapshot mode - * @data_pages: a handle the ring buffer - */ -struct cs_buffers { - unsigned int cur; - unsigned int nr_pages; - unsigned long offset; - local_t data_size; - local_t lost; - bool snapshot; - void **data_pages; -}; - /** * struct etb_drvdata - specifics associated to an ETB component * @base: memory mapped base address for this component. diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 3b5dd95a3588..ad975c58080d 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -53,6 +53,26 @@ enum cs_mode { CS_MODE_PERF, }; +/** + * struct cs_buffer - keep track of a recording session' specifics + * @cur: index of the current buffer + * @nr_pages: max number of pages granted to us + * @offset: offset within the current buffer + * @data_size: how much we collected in this run + * @lost: other than zero if we had a HW buffer wrap around + * @snapshot: is this run in snapshot mode + * @data_pages: a handle the ring buffer + */ +struct cs_buffers { + unsigned int cur; + unsigned int nr_pages; + unsigned long offset; + local_t data_size; + local_t lost; + bool snapshot; + void **data_pages; +}; + static inline void CS_LOCK(void __iomem *addr) { do { From 2686c0577df400e8e78cd97c7f3676d0700dde51 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:59 -0600 Subject: [PATCH 102/104] coresight: tmc: implementing TMC-ETF AUX space API This patch implement the AUX area interfaces required to use the TMC (configured as an ETF) from the Perf sub-system. The heuristic is heavily borrowed from the ETB10 implementation. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 2e499bbc1a929ac87dcb9832d11000fc055f8bc6) --- .../hwtracing/coresight/coresight-tmc-etf.c | 199 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-tmc.h | 1 + 2 files changed, 200 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index ba3384781f71..466af86fd76f 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -15,7 +15,9 @@ * this program. If not, see . */ +#include #include +#include #include #include "coresight-priv.h" #include "coresight-tmc.h" @@ -282,9 +284,206 @@ static void tmc_disable_etf_link(struct coresight_device *csdev, dev_info(drvdata->dev, "TMC disabled\n"); } +static void *tmc_alloc_etf_buffer(struct coresight_device *csdev, int cpu, + void **pages, int nr_pages, bool overwrite) +{ + int node; + struct cs_buffers *buf; + + if (cpu == -1) + cpu = smp_processor_id(); + node = cpu_to_node(cpu); + + /* Allocate memory structure for interaction with Perf */ + buf = kzalloc_node(sizeof(struct cs_buffers), GFP_KERNEL, node); + if (!buf) + return NULL; + + buf->snapshot = overwrite; + buf->nr_pages = nr_pages; + buf->data_pages = pages; + + return buf; +} + +static void tmc_free_etf_buffer(void *config) +{ + struct cs_buffers *buf = config; + + kfree(buf); +} + +static int tmc_set_etf_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config) +{ + int ret = 0; + unsigned long head; + struct cs_buffers *buf = sink_config; + + /* wrap head around to the amount of space we have */ + head = handle->head & ((buf->nr_pages << PAGE_SHIFT) - 1); + + /* find the page to write to */ + buf->cur = head / PAGE_SIZE; + + /* and offset within that page */ + buf->offset = head % PAGE_SIZE; + + local_set(&buf->data_size, 0); + + return ret; +} + +static unsigned long tmc_reset_etf_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config, bool *lost) +{ + long size = 0; + struct cs_buffers *buf = sink_config; + + if (buf) { + /* + * In snapshot mode ->data_size holds the new address of the + * ring buffer's head. The size itself is the whole address + * range since we want the latest information. + */ + if (buf->snapshot) + handle->head = local_xchg(&buf->data_size, + buf->nr_pages << PAGE_SHIFT); + /* + * Tell the tracer PMU how much we got in this run and if + * something went wrong along the way. Nobody else can use + * this cs_buffers instance until we are done. As such + * resetting parameters here and squaring off with the ring + * buffer API in the tracer PMU is fine. + */ + *lost = !!local_xchg(&buf->lost, 0); + size = local_xchg(&buf->data_size, 0); + } + + return size; +} + +static void tmc_update_etf_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config) +{ + int i, cur; + u32 *buf_ptr; + u32 read_ptr, write_ptr; + u32 status, to_read; + unsigned long offset; + struct cs_buffers *buf = sink_config; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (!buf) + return; + + /* This shouldn't happen */ + if (WARN_ON_ONCE(local_read(&drvdata->mode) != CS_MODE_PERF)) + return; + + CS_UNLOCK(drvdata->base); + + tmc_flush_and_stop(drvdata); + + read_ptr = readl_relaxed(drvdata->base + TMC_RRP); + write_ptr = readl_relaxed(drvdata->base + TMC_RWP); + + /* + * Get a hold of the status register and see if a wrap around + * has occurred. If so adjust things accordingly. + */ + status = readl_relaxed(drvdata->base + TMC_STS); + if (status & TMC_STS_FULL) { + local_inc(&buf->lost); + to_read = drvdata->size; + } else { + to_read = CIRC_CNT(write_ptr, read_ptr, drvdata->size); + } + + /* + * The TMC RAM buffer may be bigger than the space available in the + * perf ring buffer (handle->size). If so advance the RRP so that we + * get the latest trace data. + */ + if (to_read > handle->size) { + u32 mask = 0; + + /* + * The value written to RRP must be byte-address aligned to + * the width of the trace memory databus _and_ to a frame + * boundary (16 byte), whichever is the biggest. For example, + * for 32-bit, 64-bit and 128-bit wide trace memory, the four + * LSBs must be 0s. For 256-bit wide trace memory, the five + * LSBs must be 0s. + */ + switch (drvdata->memwidth) { + case TMC_MEM_INTF_WIDTH_32BITS: + case TMC_MEM_INTF_WIDTH_64BITS: + case TMC_MEM_INTF_WIDTH_128BITS: + mask = GENMASK(31, 5); + break; + case TMC_MEM_INTF_WIDTH_256BITS: + mask = GENMASK(31, 6); + break; + } + + /* + * Make sure the new size is aligned in accordance with the + * requirement explained above. + */ + to_read = handle->size & mask; + /* Move the RAM read pointer up */ + read_ptr = (write_ptr + drvdata->size) - to_read; + /* Make sure we are still within our limits */ + if (read_ptr > (drvdata->size - 1)) + read_ptr -= drvdata->size; + /* Tell the HW */ + writel_relaxed(read_ptr, drvdata->base + TMC_RRP); + local_inc(&buf->lost); + } + + cur = buf->cur; + offset = buf->offset; + + /* for every byte to read */ + for (i = 0; i < to_read; i += 4) { + buf_ptr = buf->data_pages[cur] + offset; + *buf_ptr = readl_relaxed(drvdata->base + TMC_RRD); + + offset += 4; + if (offset >= PAGE_SIZE) { + offset = 0; + cur++; + /* wrap around at the end of the buffer */ + cur &= buf->nr_pages - 1; + } + } + + /* + * In snapshot mode all we have to do is communicate to + * perf_aux_output_end() the address of the current head. In full + * trace mode the same function expects a size to move rb->aux_head + * forward. + */ + if (buf->snapshot) + local_set(&buf->data_size, (cur * PAGE_SIZE) + offset); + else + local_add(to_read, &buf->data_size); + + CS_LOCK(drvdata->base); +} + static const struct coresight_ops_sink tmc_etf_sink_ops = { .enable = tmc_enable_etf_sink, .disable = tmc_disable_etf_sink, + .alloc_buffer = tmc_alloc_etf_buffer, + .free_buffer = tmc_free_etf_buffer, + .set_buffer = tmc_set_etf_buffer, + .reset_buffer = tmc_reset_etf_buffer, + .update_buffer = tmc_update_etf_buffer, }; static const struct coresight_ops_link tmc_etf_link_ops = { diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index c5d06fd57fa8..5c5fe2ad2ca7 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -52,6 +52,7 @@ #define TMC_CTL_CAPT_EN BIT(0) /* TMC_STS - 0x00C */ #define TMC_STS_TMCREADY_BIT 2 +#define TMC_STS_FULL BIT(0) #define TMC_STS_TRIGGERED BIT(1) /* TMC_AXICTL - 0x110 */ #define TMC_AXICTL_PROT_CTL_B0 BIT(0) From 6d1fcd0f0e7b17b4c9e4eaf0a61072b467110692 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:34:00 -0600 Subject: [PATCH 103/104] coresight: configuring ETF in FIFO mode when acting as link When part of a path but not identified as a sink, the EFT has to be configured as a link and placed in HW FIFO mode. As such when enabling a path, call the right configuration function based on the role the ETF if playing in this trace run. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit dc2c4ef141c5c14cb8d968ba16c74b4f3c373e2c) --- drivers/hwtracing/coresight/coresight.c | 32 +++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 0995df8f85bc..5443d03a1eec 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -265,15 +265,27 @@ static void coresight_disable_source(struct coresight_device *csdev) void coresight_disable_path(struct list_head *path) { + u32 type; struct coresight_node *nd; struct coresight_device *csdev, *parent, *child; list_for_each_entry(nd, path, link) { csdev = nd->csdev; + type = csdev->type; - switch (csdev->type) { + /* + * ETF devices are tricky... They can be a link or a sink, + * depending on how they are configured. If an ETF has been + * "activated" it will be configured as a sink, otherwise + * go ahead with the link configuration. + */ + if (type == CORESIGHT_DEV_TYPE_LINKSINK) + type = (csdev == coresight_get_sink(path)) ? + CORESIGHT_DEV_TYPE_SINK : + CORESIGHT_DEV_TYPE_LINK; + + switch (type) { case CORESIGHT_DEV_TYPE_SINK: - case CORESIGHT_DEV_TYPE_LINKSINK: coresight_disable_sink(csdev); break; case CORESIGHT_DEV_TYPE_SOURCE: @@ -294,15 +306,27 @@ int coresight_enable_path(struct list_head *path, u32 mode) { int ret = 0; + u32 type; struct coresight_node *nd; struct coresight_device *csdev, *parent, *child; list_for_each_entry_reverse(nd, path, link) { csdev = nd->csdev; + type = csdev->type; - switch (csdev->type) { + /* + * ETF devices are tricky... They can be a link or a sink, + * depending on how they are configured. If an ETF has been + * "activated" it will be configured as a sink, otherwise + * go ahead with the link configuration. + */ + if (type == CORESIGHT_DEV_TYPE_LINKSINK) + type = (csdev == coresight_get_sink(path)) ? + CORESIGHT_DEV_TYPE_SINK : + CORESIGHT_DEV_TYPE_LINK; + + switch (type) { case CORESIGHT_DEV_TYPE_SINK: - case CORESIGHT_DEV_TYPE_LINKSINK: ret = coresight_enable_sink(csdev, mode); if (ret) goto err; From 4a192f60855a84820d361ec58eda0d043faab6f6 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:34:01 -0600 Subject: [PATCH 104/104] coresight: etb10: adjust read pointer only when needed The read pointer (read_ptr) needs to be adjusted only if its value has gone beyond the length of the memory buffer. Reported-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit bedffda8cad46bedb6880bb98c23c51c715216c3) --- drivers/hwtracing/coresight/coresight-etb10.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 92f942321e1e..4d20b0be0c0b 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -428,7 +428,8 @@ static void etb_update_buffer(struct coresight_device *csdev, read_ptr = (write_ptr + drvdata->buffer_depth) - to_read / ETB_FRAME_SIZE_WORDS; /* Wrap around if need be*/ - read_ptr &= ~(drvdata->buffer_depth - 1); + if (read_ptr > (drvdata->buffer_depth - 1)) + read_ptr -= drvdata->buffer_depth; /* let the decoder know we've skipped ahead */ local_inc(&buf->lost); }