diff options
Diffstat (limited to 'drivers/hwtracing/coresight/coresight.c')
-rw-r--r-- | drivers/hwtracing/coresight/coresight.c | 184 |
1 files changed, 142 insertions, 42 deletions
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 3e07fd335f8c..2b0df1a0a8df 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -128,16 +128,105 @@ static int coresight_find_link_outport(struct coresight_device *csdev, return -ENODEV; } -static int coresight_enable_sink(struct coresight_device *csdev, u32 mode) +static inline u32 coresight_read_claim_tags(void __iomem *base) +{ + return readl_relaxed(base + CORESIGHT_CLAIMCLR); +} + +static inline bool coresight_is_claimed_self_hosted(void __iomem *base) +{ + return coresight_read_claim_tags(base) == CORESIGHT_CLAIM_SELF_HOSTED; +} + +static inline bool coresight_is_claimed_any(void __iomem *base) +{ + return coresight_read_claim_tags(base) != 0; +} + +static inline void coresight_set_claim_tags(void __iomem *base) +{ + writel_relaxed(CORESIGHT_CLAIM_SELF_HOSTED, base + CORESIGHT_CLAIMSET); + isb(); +} + +static inline void coresight_clear_claim_tags(void __iomem *base) +{ + writel_relaxed(CORESIGHT_CLAIM_SELF_HOSTED, base + CORESIGHT_CLAIMCLR); + isb(); +} + +/* + * coresight_claim_device_unlocked : Claim the device for self-hosted usage + * to prevent an external tool from touching this device. As per PSCI + * standards, section "Preserving the execution context" => "Debug and Trace + * save and Restore", DBGCLAIM[1] is reserved for Self-hosted debug/trace and + * DBGCLAIM[0] is reserved for external tools. + * + * Called with CS_UNLOCKed for the component. + * Returns : 0 on success + */ +int coresight_claim_device_unlocked(void __iomem *base) +{ + if (coresight_is_claimed_any(base)) + return -EBUSY; + + coresight_set_claim_tags(base); + if (coresight_is_claimed_self_hosted(base)) + return 0; + /* There was a race setting the tags, clean up and fail */ + coresight_clear_claim_tags(base); + return -EBUSY; +} + +int coresight_claim_device(void __iomem *base) +{ + int rc; + + CS_UNLOCK(base); + rc = coresight_claim_device_unlocked(base); + CS_LOCK(base); + + return rc; +} + +/* + * coresight_disclaim_device_unlocked : Clear the claim tags for the device. + * Called with CS_UNLOCKed for the component. + */ +void coresight_disclaim_device_unlocked(void __iomem *base) +{ + + if (coresight_is_claimed_self_hosted(base)) + coresight_clear_claim_tags(base); + else + /* + * The external agent may have not honoured our claim + * and has manipulated it. Or something else has seriously + * gone wrong in our driver. + */ + WARN_ON_ONCE(1); +} + +void coresight_disclaim_device(void __iomem *base) +{ + CS_UNLOCK(base); + coresight_disclaim_device_unlocked(base); + CS_LOCK(base); +} + +static int coresight_enable_sink(struct coresight_device *csdev, + u32 mode, void *data) { int ret; - if (!csdev->enable) { - if (sink_ops(csdev)->enable) { - ret = sink_ops(csdev)->enable(csdev, mode); - if (ret) - return ret; - } + /* + * We need to make sure the "new" session is compatible with the + * existing "mode" of operation. + */ + if (sink_ops(csdev)->enable) { + ret = sink_ops(csdev)->enable(csdev, mode, data); + if (ret) + return ret; csdev->enable = true; } @@ -184,8 +273,10 @@ static int coresight_enable_link(struct coresight_device *csdev, if (atomic_inc_return(&csdev->refcnt[refport]) == 1) { if (link_ops(csdev)->enable) { ret = link_ops(csdev)->enable(csdev, inport, outport); - if (ret) + if (ret) { + atomic_dec(&csdev->refcnt[refport]); return ret; + } } } @@ -274,13 +365,21 @@ static bool coresight_disable_source(struct coresight_device *csdev) return !csdev->enable; } -void coresight_disable_path(struct list_head *path) +/* + * coresight_disable_path_from : Disable components in the given path beyond + * @nd in the list. If @nd is NULL, all the components, except the SOURCE are + * disabled. + */ +static void coresight_disable_path_from(struct list_head *path, + struct coresight_node *nd) { u32 type; - struct coresight_node *nd; struct coresight_device *csdev, *parent, *child; - list_for_each_entry(nd, path, link) { + if (!nd) + nd = list_first_entry(path, struct coresight_node, link); + + list_for_each_entry_continue(nd, path, link) { csdev = nd->csdev; type = csdev->type; @@ -300,7 +399,12 @@ void coresight_disable_path(struct list_head *path) coresight_disable_sink(csdev); break; case CORESIGHT_DEV_TYPE_SOURCE: - /* sources are disabled from either sysFS or Perf */ + /* + * We skip the first node in the path assuming that it + * is the source. So we don't expect a source device in + * the middle of a path. + */ + WARN_ON(1); break; case CORESIGHT_DEV_TYPE_LINK: parent = list_prev_entry(nd, link)->csdev; @@ -313,7 +417,12 @@ void coresight_disable_path(struct list_head *path) } } -int coresight_enable_path(struct list_head *path, u32 mode) +void coresight_disable_path(struct list_head *path) +{ + coresight_disable_path_from(path, NULL); +} + +int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data) { int ret = 0; @@ -338,9 +447,15 @@ int coresight_enable_path(struct list_head *path, u32 mode) switch (type) { case CORESIGHT_DEV_TYPE_SINK: - ret = coresight_enable_sink(csdev, mode); + ret = coresight_enable_sink(csdev, mode, sink_data); + /* + * Sink is the first component turned on. If we + * failed to enable the sink, there are no components + * that need disabling. Disabling the path here + * would mean we could disrupt an existing session. + */ if (ret) - goto err; + goto out; break; case CORESIGHT_DEV_TYPE_SOURCE: /* sources are enabled from either sysFS or Perf */ @@ -360,7 +475,7 @@ int coresight_enable_path(struct list_head *path, u32 mode) out: return ret; err: - coresight_disable_path(path); + coresight_disable_path_from(path, nd); goto out; } @@ -635,7 +750,7 @@ int coresight_enable(struct coresight_device *csdev) goto out; } - ret = coresight_enable_path(path, CS_MODE_SYSFS); + ret = coresight_enable_path(path, CS_MODE_SYSFS, NULL); if (ret) goto err_path; @@ -995,18 +1110,16 @@ postcore_initcall(coresight_init); struct coresight_device *coresight_register(struct coresight_desc *desc) { - int i; int ret; int link_subtype; int nr_refcnts = 1; atomic_t *refcnts = NULL; struct coresight_device *csdev; - struct coresight_connection *conns = NULL; csdev = kzalloc(sizeof(*csdev), GFP_KERNEL); if (!csdev) { ret = -ENOMEM; - goto err_kzalloc_csdev; + goto err_out; } if (desc->type == CORESIGHT_DEV_TYPE_LINK || @@ -1022,7 +1135,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) refcnts = kcalloc(nr_refcnts, sizeof(*refcnts), GFP_KERNEL); if (!refcnts) { ret = -ENOMEM; - goto err_kzalloc_refcnts; + goto err_free_csdev; } csdev->refcnt = refcnts; @@ -1030,22 +1143,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) csdev->nr_inport = desc->pdata->nr_inport; csdev->nr_outport = desc->pdata->nr_outport; - /* Initialise connections if there is at least one outport */ - if (csdev->nr_outport) { - conns = kcalloc(csdev->nr_outport, sizeof(*conns), GFP_KERNEL); - if (!conns) { - ret = -ENOMEM; - goto err_kzalloc_conns; - } - - for (i = 0; i < csdev->nr_outport; i++) { - conns[i].outport = desc->pdata->outports[i]; - conns[i].child_name = desc->pdata->child_names[i]; - conns[i].child_port = desc->pdata->child_ports[i]; - } - } - - csdev->conns = conns; + csdev->conns = desc->pdata->conns; csdev->type = desc->type; csdev->subtype = desc->subtype; @@ -1062,7 +1160,11 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) ret = device_register(&csdev->dev); if (ret) { put_device(&csdev->dev); - goto err_kzalloc_csdev; + /* + * All resources are free'd explicitly via + * coresight_device_release(), triggered from put_device(). + */ + goto err_out; } mutex_lock(&coresight_mutex); @@ -1074,11 +1176,9 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) return csdev; -err_kzalloc_conns: - kfree(refcnts); -err_kzalloc_refcnts: +err_free_csdev: kfree(csdev); -err_kzalloc_csdev: +err_out: return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(coresight_register); |