FILE
coresight-tpda.c
File preview with syntax highlighting.
linux-6.19.14/drivers/hwtracing/coresight/coresight-tpda.c
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (c) 2023-2025 Qualcomm Innovation Center, Inc. All rights reserved. 4 */ 5 6#include <linux/amba/bus.h> 7#include <linux/bitfield.h> 8#include <linux/coresight.h> 9#include <linux/device.h> 10#include <linux/err.h> 11#include <linux/fs.h> 12#include <linux/io.h> 13#include <linux/kernel.h> 14#include <linux/module.h> 15#include <linux/of.h> 16#include <linux/platform_device.h> 17 18#include "coresight-priv.h" 19#include "coresight-tpda.h" 20#include "coresight-trace-id.h" 21#include "coresight-tpdm.h" 22 23DEFINE_CORESIGHT_DEVLIST(tpda_devs, "tpda"); 24 25static void tpda_clear_element_size(struct coresight_device *csdev) 26{ 27 struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); 28 29 drvdata->dsb_esize = 0; 30 drvdata->cmb_esize = 0; 31} 32 33static void tpda_set_element_size(struct tpda_drvdata *drvdata, u32 *val) 34{ 35 /* Clear all relevant fields */ 36 *val &= ~(TPDA_Pn_CR_DSBSIZE | TPDA_Pn_CR_CMBSIZE); 37 38 if (drvdata->dsb_esize == 64) 39 *val |= TPDA_Pn_CR_DSBSIZE; 40 else if (drvdata->dsb_esize == 32) 41 *val &= ~TPDA_Pn_CR_DSBSIZE; 42 43 if (drvdata->cmb_esize == 64) 44 *val |= FIELD_PREP(TPDA_Pn_CR_CMBSIZE, 0x2); 45 else if (drvdata->cmb_esize == 32) 46 *val |= FIELD_PREP(TPDA_Pn_CR_CMBSIZE, 0x1); 47 else if (drvdata->cmb_esize == 8) 48 *val &= ~TPDA_Pn_CR_CMBSIZE; 49} 50 51/* 52 * Read the element size from the TPDM device. One TPDM must have at least one of the 53 * element size property. 54 * Returns 55 * 0 - The element size property is read 56 * Others - Cannot read the property of the element size 57 */ 58static int tpdm_read_element_size(struct tpda_drvdata *drvdata, 59 struct coresight_device *csdev) 60{ 61 int rc = -EINVAL; 62 struct tpdm_drvdata *tpdm_data = dev_get_drvdata(csdev->dev.parent); 63 64 if (tpdm_data->dsb) { 65 rc = fwnode_property_read_u32(dev_fwnode(csdev->dev.parent), 66 "qcom,dsb-element-bits", &drvdata->dsb_esize); 67 if (rc) 68 goto out; 69 } 70 71 if (tpdm_data->cmb) { 72 rc = fwnode_property_read_u32(dev_fwnode(csdev->dev.parent), 73 "qcom,cmb-element-bits", &drvdata->cmb_esize); 74 } 75 76out: 77 if (rc) 78 dev_warn_once(&csdev->dev, 79 "Failed to read TPDM Element size: %d\n", rc); 80 81 return rc; 82} 83 84/* 85 * Search and read element data size from the TPDM node in 86 * the devicetree. Each input port of TPDA is connected to 87 * a TPDM. Different TPDM supports different types of dataset, 88 * and some may support more than one type of dataset. 89 * Parameter "inport" is used to pass in the input port number 90 * of TPDA, and it is set to -1 in the recursize call. 91 */ 92static int tpda_get_element_size(struct tpda_drvdata *drvdata, 93 struct coresight_device *csdev, 94 int inport) 95{ 96 int rc = 0; 97 int i; 98 struct coresight_device *in; 99 100 for (i = 0; i < csdev->pdata->nr_inconns; i++) { 101 in = csdev->pdata->in_conns[i]->src_dev; 102 if (!in) 103 continue; 104 105 /* Ignore the paths that do not match port */ 106 if (inport >= 0 && 107 csdev->pdata->in_conns[i]->dest_port != inport) 108 continue; 109 110 /* 111 * If this port has a hardcoded filter, use the source 112 * device directly. 113 */ 114 if (csdev->pdata->in_conns[i]->filter_src_fwnode) { 115 in = csdev->pdata->in_conns[i]->filter_src_dev; 116 if (!in) 117 continue; 118 } 119 120 if (coresight_device_is_tpdm(in)) { 121 if (drvdata->dsb_esize || drvdata->cmb_esize) 122 return -EEXIST; 123 rc = tpdm_read_element_size(drvdata, in); 124 if (rc) 125 return rc; 126 } else { 127 /* Recurse down the path */ 128 rc = tpda_get_element_size(drvdata, in, -1); 129 if (rc) 130 return rc; 131 } 132 } 133 134 return rc; 135} 136 137/* Settings pre enabling port control register */ 138static void tpda_enable_pre_port(struct tpda_drvdata *drvdata) 139{ 140 u32 val; 141 142 val = readl_relaxed(drvdata->base + TPDA_CR); 143 val &= ~TPDA_CR_ATID; 144 val |= FIELD_PREP(TPDA_CR_ATID, drvdata->atid); 145 writel_relaxed(val, drvdata->base + TPDA_CR); 146} 147 148static int tpda_enable_port(struct tpda_drvdata *drvdata, int port) 149{ 150 u32 val; 151 int rc; 152 153 val = readl_relaxed(drvdata->base + TPDA_Pn_CR(port)); 154 tpda_clear_element_size(drvdata->csdev); 155 rc = tpda_get_element_size(drvdata, drvdata->csdev, port); 156 if (!rc && (drvdata->dsb_esize || drvdata->cmb_esize)) { 157 tpda_set_element_size(drvdata, &val); 158 /* Enable the port */ 159 val |= TPDA_Pn_CR_ENA; 160 writel_relaxed(val, drvdata->base + TPDA_Pn_CR(port)); 161 } else if (rc == -EEXIST) 162 dev_warn_once(&drvdata->csdev->dev, 163 "Detected multiple TPDMs on port %d", port); 164 else 165 dev_warn_once(&drvdata->csdev->dev, 166 "Didn't find TPDM element size"); 167 168 return rc; 169} 170 171static int __tpda_enable(struct tpda_drvdata *drvdata, int port) 172{ 173 int ret; 174 175 CS_UNLOCK(drvdata->base); 176 177 /* 178 * Only do pre-port enable for first port that calls enable when the 179 * device's main refcount is still 0 180 */ 181 lockdep_assert_held(&drvdata->spinlock); 182 if (!drvdata->csdev->refcnt) 183 tpda_enable_pre_port(drvdata); 184 185 ret = tpda_enable_port(drvdata, port); 186 CS_LOCK(drvdata->base); 187 188 return ret; 189} 190 191static int tpda_enable(struct coresight_device *csdev, 192 struct coresight_connection *in, 193 struct coresight_connection *out) 194{ 195 struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); 196 int ret = 0; 197 198 spin_lock(&drvdata->spinlock); 199 if (in->dest_refcnt == 0) { 200 ret = __tpda_enable(drvdata, in->dest_port); 201 if (!ret) { 202 in->dest_refcnt++; 203 csdev->refcnt++; 204 dev_dbg(drvdata->dev, "TPDA inport %d enabled.\n", in->dest_port); 205 } 206 } 207 208 spin_unlock(&drvdata->spinlock); 209 return ret; 210} 211 212static void __tpda_disable(struct tpda_drvdata *drvdata, int port) 213{ 214 u32 val; 215 216 CS_UNLOCK(drvdata->base); 217 218 val = readl_relaxed(drvdata->base + TPDA_Pn_CR(port)); 219 val &= ~TPDA_Pn_CR_ENA; 220 writel_relaxed(val, drvdata->base + TPDA_Pn_CR(port)); 221 222 CS_LOCK(drvdata->base); 223} 224 225static void tpda_disable(struct coresight_device *csdev, 226 struct coresight_connection *in, 227 struct coresight_connection *out) 228{ 229 struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); 230 231 spin_lock(&drvdata->spinlock); 232 if (--in->dest_refcnt == 0) { 233 __tpda_disable(drvdata, in->dest_port); 234 csdev->refcnt--; 235 } 236 spin_unlock(&drvdata->spinlock); 237 238 dev_dbg(drvdata->dev, "TPDA inport %d disabled\n", in->dest_port); 239} 240 241static int tpda_trace_id(struct coresight_device *csdev, __maybe_unused enum cs_mode mode, 242 __maybe_unused struct coresight_device *sink) 243{ 244 struct tpda_drvdata *drvdata; 245 246 drvdata = dev_get_drvdata(csdev->dev.parent); 247 248 return drvdata->atid; 249} 250 251static const struct coresight_ops_link tpda_link_ops = { 252 .enable = tpda_enable, 253 .disable = tpda_disable, 254}; 255 256static const struct coresight_ops tpda_cs_ops = { 257 .trace_id = tpda_trace_id, 258 .link_ops = &tpda_link_ops, 259}; 260 261static int tpda_init_default_data(struct tpda_drvdata *drvdata) 262{ 263 int atid; 264 /* 265 * TPDA must has a unique atid. This atid can uniquely 266 * identify the TPDM trace source connected to the TPDA. 267 * The TPDMs which are connected to same TPDA share the 268 * same trace-id. When TPDA does packetization, different 269 * port will have unique channel number for decoding. 270 */ 271 atid = coresight_trace_id_get_system_id(); 272 if (atid < 0) 273 return atid; 274 275 drvdata->atid = atid; 276 return 0; 277} 278 279static int tpda_probe(struct amba_device *adev, const struct amba_id *id) 280{ 281 int ret; 282 struct device *dev = &adev->dev; 283 struct coresight_platform_data *pdata; 284 struct tpda_drvdata *drvdata; 285 struct coresight_desc desc = { 0 }; 286 void __iomem *base; 287 288 pdata = coresight_get_platform_data(dev); 289 if (IS_ERR(pdata)) 290 return PTR_ERR(pdata); 291 adev->dev.platform_data = pdata; 292 293 drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); 294 if (!drvdata) 295 return -ENOMEM; 296 297 drvdata->dev = &adev->dev; 298 dev_set_drvdata(dev, drvdata); 299 300 base = devm_ioremap_resource(dev, &adev->res); 301 if (IS_ERR(base)) 302 return PTR_ERR(base); 303 drvdata->base = base; 304 305 spin_lock_init(&drvdata->spinlock); 306 307 ret = tpda_init_default_data(drvdata); 308 if (ret) 309 return ret; 310 311 desc.name = coresight_alloc_device_name(&tpda_devs, dev); 312 if (!desc.name) 313 return -ENOMEM; 314 desc.type = CORESIGHT_DEV_TYPE_LINK; 315 desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG; 316 desc.ops = &tpda_cs_ops; 317 desc.pdata = adev->dev.platform_data; 318 desc.dev = &adev->dev; 319 desc.access = CSDEV_ACCESS_IOMEM(base); 320 drvdata->csdev = coresight_register(&desc); 321 if (IS_ERR(drvdata->csdev)) 322 return PTR_ERR(drvdata->csdev); 323 324 pm_runtime_put(&adev->dev); 325 326 dev_dbg(drvdata->dev, "TPDA initialized\n"); 327 return 0; 328} 329 330static void tpda_remove(struct amba_device *adev) 331{ 332 struct tpda_drvdata *drvdata = dev_get_drvdata(&adev->dev); 333 334 coresight_trace_id_put_system_id(drvdata->atid); 335 coresight_unregister(drvdata->csdev); 336} 337 338/* 339 * Different TPDA has different periph id. 340 * The difference is 0-7 bits' value. So ignore 0-7 bits. 341 */ 342static const struct amba_id tpda_ids[] = { 343 { 344 .id = 0x000f0f00, 345 .mask = 0x000fff00, 346 }, 347 { 0, 0, NULL }, 348}; 349 350static struct amba_driver tpda_driver = { 351 .drv = { 352 .name = "coresight-tpda", 353 .suppress_bind_attrs = true, 354 }, 355 .probe = tpda_probe, 356 .remove = tpda_remove, 357 .id_table = tpda_ids, 358}; 359 360module_amba_driver(tpda_driver); 361 362MODULE_LICENSE("GPL"); 363MODULE_DESCRIPTION("Trace, Profiling & Diagnostic Aggregator driver");
~/
Read-only file preview.