coresight-cti-platform.c (13032B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (c) 2019, The Linaro Limited. All rights reserved. 4 */ 5#include <linux/coresight.h> 6#include <linux/device.h> 7#include <linux/err.h> 8#include <linux/of.h> 9#include <linux/property.h> 10#include <linux/slab.h> 11 12#include <dt-bindings/arm/coresight-cti-dt.h> 13 14#include "coresight-cti.h" 15#include "coresight-priv.h" 16 17/* Number of CTI signals in the v8 architecturally defined connection */ 18#define NR_V8PE_IN_SIGS 2 19#define NR_V8PE_OUT_SIGS 3 20#define NR_V8ETM_INOUT_SIGS 4 21 22/* CTI device tree trigger connection node keyword */ 23#define CTI_DT_CONNS "trig-conns" 24 25/* CTI device tree connection property keywords */ 26#define CTI_DT_V8ARCH_COMPAT "arm,coresight-cti-v8-arch" 27#define CTI_DT_CSDEV_ASSOC "arm,cs-dev-assoc" 28#define CTI_DT_TRIGIN_SIGS "arm,trig-in-sigs" 29#define CTI_DT_TRIGOUT_SIGS "arm,trig-out-sigs" 30#define CTI_DT_TRIGIN_TYPES "arm,trig-in-types" 31#define CTI_DT_TRIGOUT_TYPES "arm,trig-out-types" 32#define CTI_DT_FILTER_OUT_SIGS "arm,trig-filters" 33#define CTI_DT_CONN_NAME "arm,trig-conn-name" 34#define CTI_DT_CTM_ID "arm,cti-ctm-id" 35 36#ifdef CONFIG_OF 37/* 38 * CTI can be bound to a CPU, or a system device. 39 * CPU can be declared at the device top level or in a connections node 40 * so need to check relative to node not device. 41 */ 42static int of_cti_get_cpu_at_node(const struct device_node *node) 43{ 44 int cpu; 45 struct device_node *dn; 46 47 if (node == NULL) 48 return -1; 49 50 dn = of_parse_phandle(node, "cpu", 0); 51 /* CTI affinity defaults to no cpu */ 52 if (!dn) 53 return -1; 54 cpu = of_cpu_node_to_id(dn); 55 of_node_put(dn); 56 57 /* No Affinity if no cpu nodes are found */ 58 return (cpu < 0) ? -1 : cpu; 59} 60 61#else 62static int of_cti_get_cpu_at_node(const struct device_node *node) 63{ 64 return -1; 65} 66 67#endif 68 69/* 70 * CTI can be bound to a CPU, or a system device. 71 * CPU can be declared at the device top level or in a connections node 72 * so need to check relative to node not device. 73 */ 74static int cti_plat_get_cpu_at_node(struct fwnode_handle *fwnode) 75{ 76 if (is_of_node(fwnode)) 77 return of_cti_get_cpu_at_node(to_of_node(fwnode)); 78 return -1; 79} 80 81const char *cti_plat_get_node_name(struct fwnode_handle *fwnode) 82{ 83 if (is_of_node(fwnode)) 84 return of_node_full_name(to_of_node(fwnode)); 85 return "unknown"; 86} 87 88/* 89 * Extract a name from the fwnode. 90 * If the device associated with the node is a coresight_device, then return 91 * that name and the coresight_device pointer, otherwise return the node name. 92 */ 93static const char * 94cti_plat_get_csdev_or_node_name(struct fwnode_handle *fwnode, 95 struct coresight_device **csdev) 96{ 97 const char *name = NULL; 98 *csdev = coresight_find_csdev_by_fwnode(fwnode); 99 if (*csdev) 100 name = dev_name(&(*csdev)->dev); 101 else 102 name = cti_plat_get_node_name(fwnode); 103 return name; 104} 105 106static bool cti_plat_node_name_eq(struct fwnode_handle *fwnode, 107 const char *name) 108{ 109 if (is_of_node(fwnode)) 110 return of_node_name_eq(to_of_node(fwnode), name); 111 return false; 112} 113 114static int cti_plat_create_v8_etm_connection(struct device *dev, 115 struct cti_drvdata *drvdata) 116{ 117 int ret = -ENOMEM, i; 118 struct fwnode_handle *root_fwnode, *cs_fwnode; 119 const char *assoc_name = NULL; 120 struct coresight_device *csdev; 121 struct cti_trig_con *tc = NULL; 122 123 root_fwnode = dev_fwnode(dev); 124 if (IS_ERR_OR_NULL(root_fwnode)) 125 return -EINVAL; 126 127 /* Can optionally have an etm node - return if not */ 128 cs_fwnode = fwnode_find_reference(root_fwnode, CTI_DT_CSDEV_ASSOC, 0); 129 if (IS_ERR(cs_fwnode)) 130 return 0; 131 132 /* allocate memory */ 133 tc = cti_allocate_trig_con(dev, NR_V8ETM_INOUT_SIGS, 134 NR_V8ETM_INOUT_SIGS); 135 if (!tc) 136 goto create_v8_etm_out; 137 138 /* build connection data */ 139 tc->con_in->used_mask = 0xF0; /* sigs <4,5,6,7> */ 140 tc->con_out->used_mask = 0xF0; /* sigs <4,5,6,7> */ 141 142 /* 143 * The EXTOUT type signals from the ETM are connected to a set of input 144 * triggers on the CTI, the EXTIN being connected to output triggers. 145 */ 146 for (i = 0; i < NR_V8ETM_INOUT_SIGS; i++) { 147 tc->con_in->sig_types[i] = ETM_EXTOUT; 148 tc->con_out->sig_types[i] = ETM_EXTIN; 149 } 150 151 /* 152 * We look to see if the ETM coresight device associated with this 153 * handle has been registered with the system - i.e. probed before 154 * this CTI. If so csdev will be non NULL and we can use the device 155 * name and pass the csdev to the connection entry function where 156 * the association will be recorded. 157 * If not, then simply record the name in the connection data, the 158 * probing of the ETM will call into the CTI driver API to update the 159 * association then. 160 */ 161 assoc_name = cti_plat_get_csdev_or_node_name(cs_fwnode, &csdev); 162 ret = cti_add_connection_entry(dev, drvdata, tc, csdev, assoc_name); 163 164create_v8_etm_out: 165 fwnode_handle_put(cs_fwnode); 166 return ret; 167} 168 169/* 170 * Create an architecturally defined v8 connection 171 * must have a cpu, can have an ETM. 172 */ 173static int cti_plat_create_v8_connections(struct device *dev, 174 struct cti_drvdata *drvdata) 175{ 176 struct cti_device *cti_dev = &drvdata->ctidev; 177 struct cti_trig_con *tc = NULL; 178 int cpuid = 0; 179 char cpu_name_str[16]; 180 int ret = -ENOMEM; 181 182 /* Must have a cpu node */ 183 cpuid = cti_plat_get_cpu_at_node(dev_fwnode(dev)); 184 if (cpuid < 0) { 185 dev_warn(dev, 186 "ARM v8 architectural CTI connection: missing cpu\n"); 187 return -EINVAL; 188 } 189 cti_dev->cpu = cpuid; 190 191 /* Allocate the v8 cpu connection memory */ 192 tc = cti_allocate_trig_con(dev, NR_V8PE_IN_SIGS, NR_V8PE_OUT_SIGS); 193 if (!tc) 194 goto of_create_v8_out; 195 196 /* Set the v8 PE CTI connection data */ 197 tc->con_in->used_mask = 0x3; /* sigs <0 1> */ 198 tc->con_in->sig_types[0] = PE_DBGTRIGGER; 199 tc->con_in->sig_types[1] = PE_PMUIRQ; 200 tc->con_out->used_mask = 0x7; /* sigs <0 1 2 > */ 201 tc->con_out->sig_types[0] = PE_EDBGREQ; 202 tc->con_out->sig_types[1] = PE_DBGRESTART; 203 tc->con_out->sig_types[2] = PE_CTIIRQ; 204 scnprintf(cpu_name_str, sizeof(cpu_name_str), "cpu%d", cpuid); 205 206 ret = cti_add_connection_entry(dev, drvdata, tc, NULL, cpu_name_str); 207 if (ret) 208 goto of_create_v8_out; 209 210 /* Create the v8 ETM associated connection */ 211 ret = cti_plat_create_v8_etm_connection(dev, drvdata); 212 if (ret) 213 goto of_create_v8_out; 214 215 /* filter pe_edbgreq - PE trigout sig <0> */ 216 drvdata->config.trig_out_filter |= 0x1; 217 218of_create_v8_out: 219 return ret; 220} 221 222static int cti_plat_check_v8_arch_compatible(struct device *dev) 223{ 224 struct fwnode_handle *fwnode = dev_fwnode(dev); 225 226 if (is_of_node(fwnode)) 227 return of_device_is_compatible(to_of_node(fwnode), 228 CTI_DT_V8ARCH_COMPAT); 229 return 0; 230} 231 232static int cti_plat_count_sig_elements(const struct fwnode_handle *fwnode, 233 const char *name) 234{ 235 int nr_elem = fwnode_property_count_u32(fwnode, name); 236 237 return (nr_elem < 0 ? 0 : nr_elem); 238} 239 240static int cti_plat_read_trig_group(struct cti_trig_grp *tgrp, 241 const struct fwnode_handle *fwnode, 242 const char *grp_name) 243{ 244 int idx, err = 0; 245 u32 *values; 246 247 if (!tgrp->nr_sigs) 248 return 0; 249 250 values = kcalloc(tgrp->nr_sigs, sizeof(u32), GFP_KERNEL); 251 if (!values) 252 return -ENOMEM; 253 254 err = fwnode_property_read_u32_array(fwnode, grp_name, 255 values, tgrp->nr_sigs); 256 257 if (!err) { 258 /* set the signal usage mask */ 259 for (idx = 0; idx < tgrp->nr_sigs; idx++) 260 tgrp->used_mask |= BIT(values[idx]); 261 } 262 263 kfree(values); 264 return err; 265} 266 267static int cti_plat_read_trig_types(struct cti_trig_grp *tgrp, 268 const struct fwnode_handle *fwnode, 269 const char *type_name) 270{ 271 int items, err = 0, nr_sigs; 272 u32 *values = NULL, i; 273 274 /* allocate an array according to number of signals in connection */ 275 nr_sigs = tgrp->nr_sigs; 276 if (!nr_sigs) 277 return 0; 278 279 /* see if any types have been included in the device description */ 280 items = cti_plat_count_sig_elements(fwnode, type_name); 281 if (items > nr_sigs) 282 return -EINVAL; 283 284 /* need an array to store the values iff there are any */ 285 if (items) { 286 values = kcalloc(items, sizeof(u32), GFP_KERNEL); 287 if (!values) 288 return -ENOMEM; 289 290 err = fwnode_property_read_u32_array(fwnode, type_name, 291 values, items); 292 if (err) 293 goto read_trig_types_out; 294 } 295 296 /* 297 * Match type id to signal index, 1st type to 1st index etc. 298 * If fewer types than signals default remainder to GEN_IO. 299 */ 300 for (i = 0; i < nr_sigs; i++) { 301 if (i < items) { 302 tgrp->sig_types[i] = 303 values[i] < CTI_TRIG_MAX ? values[i] : GEN_IO; 304 } else { 305 tgrp->sig_types[i] = GEN_IO; 306 } 307 } 308 309read_trig_types_out: 310 kfree(values); 311 return err; 312} 313 314static int cti_plat_process_filter_sigs(struct cti_drvdata *drvdata, 315 const struct fwnode_handle *fwnode) 316{ 317 struct cti_trig_grp *tg = NULL; 318 int err = 0, nr_filter_sigs; 319 320 nr_filter_sigs = cti_plat_count_sig_elements(fwnode, 321 CTI_DT_FILTER_OUT_SIGS); 322 if (nr_filter_sigs == 0) 323 return 0; 324 325 if (nr_filter_sigs > drvdata->config.nr_trig_max) 326 return -EINVAL; 327 328 tg = kzalloc(sizeof(*tg), GFP_KERNEL); 329 if (!tg) 330 return -ENOMEM; 331 332 err = cti_plat_read_trig_group(tg, fwnode, CTI_DT_FILTER_OUT_SIGS); 333 if (!err) 334 drvdata->config.trig_out_filter |= tg->used_mask; 335 336 kfree(tg); 337 return err; 338} 339 340static int cti_plat_create_connection(struct device *dev, 341 struct cti_drvdata *drvdata, 342 struct fwnode_handle *fwnode) 343{ 344 struct cti_trig_con *tc = NULL; 345 int cpuid = -1, err = 0; 346 struct coresight_device *csdev = NULL; 347 const char *assoc_name = "unknown"; 348 char cpu_name_str[16]; 349 int nr_sigs_in, nr_sigs_out; 350 351 /* look to see how many in and out signals we have */ 352 nr_sigs_in = cti_plat_count_sig_elements(fwnode, CTI_DT_TRIGIN_SIGS); 353 nr_sigs_out = cti_plat_count_sig_elements(fwnode, CTI_DT_TRIGOUT_SIGS); 354 355 if ((nr_sigs_in > drvdata->config.nr_trig_max) || 356 (nr_sigs_out > drvdata->config.nr_trig_max)) 357 return -EINVAL; 358 359 tc = cti_allocate_trig_con(dev, nr_sigs_in, nr_sigs_out); 360 if (!tc) 361 return -ENOMEM; 362 363 /* look for the signals properties. */ 364 err = cti_plat_read_trig_group(tc->con_in, fwnode, 365 CTI_DT_TRIGIN_SIGS); 366 if (err) 367 goto create_con_err; 368 369 err = cti_plat_read_trig_types(tc->con_in, fwnode, 370 CTI_DT_TRIGIN_TYPES); 371 if (err) 372 goto create_con_err; 373 374 err = cti_plat_read_trig_group(tc->con_out, fwnode, 375 CTI_DT_TRIGOUT_SIGS); 376 if (err) 377 goto create_con_err; 378 379 err = cti_plat_read_trig_types(tc->con_out, fwnode, 380 CTI_DT_TRIGOUT_TYPES); 381 if (err) 382 goto create_con_err; 383 384 err = cti_plat_process_filter_sigs(drvdata, fwnode); 385 if (err) 386 goto create_con_err; 387 388 /* read the connection name if set - may be overridden by later */ 389 fwnode_property_read_string(fwnode, CTI_DT_CONN_NAME, &assoc_name); 390 391 /* associated cpu ? */ 392 cpuid = cti_plat_get_cpu_at_node(fwnode); 393 if (cpuid >= 0) { 394 drvdata->ctidev.cpu = cpuid; 395 scnprintf(cpu_name_str, sizeof(cpu_name_str), "cpu%d", cpuid); 396 assoc_name = cpu_name_str; 397 } else { 398 /* associated device ? */ 399 struct fwnode_handle *cs_fwnode = fwnode_find_reference(fwnode, 400 CTI_DT_CSDEV_ASSOC, 401 0); 402 if (!IS_ERR(cs_fwnode)) { 403 assoc_name = cti_plat_get_csdev_or_node_name(cs_fwnode, 404 &csdev); 405 fwnode_handle_put(cs_fwnode); 406 } 407 } 408 /* set up a connection */ 409 err = cti_add_connection_entry(dev, drvdata, tc, csdev, assoc_name); 410 411create_con_err: 412 return err; 413} 414 415static int cti_plat_create_impdef_connections(struct device *dev, 416 struct cti_drvdata *drvdata) 417{ 418 int rc = 0; 419 struct fwnode_handle *fwnode = dev_fwnode(dev); 420 struct fwnode_handle *child = NULL; 421 422 if (IS_ERR_OR_NULL(fwnode)) 423 return -EINVAL; 424 425 fwnode_for_each_child_node(fwnode, child) { 426 if (cti_plat_node_name_eq(child, CTI_DT_CONNS)) 427 rc = cti_plat_create_connection(dev, drvdata, 428 child); 429 if (rc != 0) 430 break; 431 } 432 fwnode_handle_put(child); 433 434 return rc; 435} 436 437/* get the hardware configuration & connection data. */ 438static int cti_plat_get_hw_data(struct device *dev, struct cti_drvdata *drvdata) 439{ 440 int rc = 0; 441 struct cti_device *cti_dev = &drvdata->ctidev; 442 443 /* get any CTM ID - defaults to 0 */ 444 device_property_read_u32(dev, CTI_DT_CTM_ID, &cti_dev->ctm_id); 445 446 /* check for a v8 architectural CTI device */ 447 if (cti_plat_check_v8_arch_compatible(dev)) 448 rc = cti_plat_create_v8_connections(dev, drvdata); 449 else 450 rc = cti_plat_create_impdef_connections(dev, drvdata); 451 if (rc) 452 return rc; 453 454 /* if no connections, just add a single default based on max IN-OUT */ 455 if (cti_dev->nr_trig_con == 0) 456 rc = cti_add_default_connection(dev, drvdata); 457 return rc; 458} 459 460struct coresight_platform_data * 461coresight_cti_get_platform_data(struct device *dev) 462{ 463 int ret = -ENOENT; 464 struct coresight_platform_data *pdata = NULL; 465 struct fwnode_handle *fwnode = dev_fwnode(dev); 466 struct cti_drvdata *drvdata = dev_get_drvdata(dev); 467 468 if (IS_ERR_OR_NULL(fwnode)) 469 goto error; 470 471 /* 472 * Alloc platform data but leave it zero init. CTI does not use the 473 * same connection infrastructuree as trace path components but an 474 * empty struct enables us to use the standard coresight component 475 * registration code. 476 */ 477 pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); 478 if (!pdata) { 479 ret = -ENOMEM; 480 goto error; 481 } 482 483 /* get some CTI specifics */ 484 ret = cti_plat_get_hw_data(dev, drvdata); 485 486 if (!ret) 487 return pdata; 488error: 489 return ERR_PTR(ret); 490}