Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (c) 2019, The Linaro Limited. All rights reserved.
0004  */
0005 #include <linux/coresight.h>
0006 #include <linux/device.h>
0007 #include <linux/err.h>
0008 #include <linux/of.h>
0009 #include <linux/property.h>
0010 #include <linux/slab.h>
0011 
0012 #include <dt-bindings/arm/coresight-cti-dt.h>
0013 
0014 #include "coresight-cti.h"
0015 #include "coresight-priv.h"
0016 
0017 /* Number of CTI signals in the v8 architecturally defined connection */
0018 #define NR_V8PE_IN_SIGS     2
0019 #define NR_V8PE_OUT_SIGS    3
0020 #define NR_V8ETM_INOUT_SIGS 4
0021 
0022 /* CTI device tree trigger connection node keyword */
0023 #define CTI_DT_CONNS        "trig-conns"
0024 
0025 /* CTI device tree connection property keywords */
0026 #define CTI_DT_V8ARCH_COMPAT    "arm,coresight-cti-v8-arch"
0027 #define CTI_DT_CSDEV_ASSOC  "arm,cs-dev-assoc"
0028 #define CTI_DT_TRIGIN_SIGS  "arm,trig-in-sigs"
0029 #define CTI_DT_TRIGOUT_SIGS "arm,trig-out-sigs"
0030 #define CTI_DT_TRIGIN_TYPES "arm,trig-in-types"
0031 #define CTI_DT_TRIGOUT_TYPES    "arm,trig-out-types"
0032 #define CTI_DT_FILTER_OUT_SIGS  "arm,trig-filters"
0033 #define CTI_DT_CONN_NAME    "arm,trig-conn-name"
0034 #define CTI_DT_CTM_ID       "arm,cti-ctm-id"
0035 
0036 #ifdef CONFIG_OF
0037 /*
0038  * CTI can be bound to a CPU, or a system device.
0039  * CPU can be declared at the device top level or in a connections node
0040  * so need to check relative to node not device.
0041  */
0042 static int of_cti_get_cpu_at_node(const struct device_node *node)
0043 {
0044     int cpu;
0045     struct device_node *dn;
0046 
0047     if (node == NULL)
0048         return -1;
0049 
0050     dn = of_parse_phandle(node, "cpu", 0);
0051     /* CTI affinity defaults to no cpu */
0052     if (!dn)
0053         return -1;
0054     cpu = of_cpu_node_to_id(dn);
0055     of_node_put(dn);
0056 
0057     /* No Affinity  if no cpu nodes are found */
0058     return (cpu < 0) ? -1 : cpu;
0059 }
0060 
0061 #else
0062 static int of_cti_get_cpu_at_node(const struct device_node *node)
0063 {
0064     return -1;
0065 }
0066 
0067 #endif
0068 
0069 /*
0070  * CTI can be bound to a CPU, or a system device.
0071  * CPU can be declared at the device top level or in a connections node
0072  * so need to check relative to node not device.
0073  */
0074 static int cti_plat_get_cpu_at_node(struct fwnode_handle *fwnode)
0075 {
0076     if (is_of_node(fwnode))
0077         return of_cti_get_cpu_at_node(to_of_node(fwnode));
0078     return -1;
0079 }
0080 
0081 const char *cti_plat_get_node_name(struct fwnode_handle *fwnode)
0082 {
0083     if (is_of_node(fwnode))
0084         return of_node_full_name(to_of_node(fwnode));
0085     return "unknown";
0086 }
0087 
0088 /*
0089  * Extract a name from the fwnode.
0090  * If the device associated with the node is a coresight_device, then return
0091  * that name and the coresight_device pointer, otherwise return the node name.
0092  */
0093 static const char *
0094 cti_plat_get_csdev_or_node_name(struct fwnode_handle *fwnode,
0095                 struct coresight_device **csdev)
0096 {
0097     const char *name = NULL;
0098     *csdev = coresight_find_csdev_by_fwnode(fwnode);
0099     if (*csdev)
0100         name = dev_name(&(*csdev)->dev);
0101     else
0102         name = cti_plat_get_node_name(fwnode);
0103     return name;
0104 }
0105 
0106 static bool cti_plat_node_name_eq(struct fwnode_handle *fwnode,
0107                   const char *name)
0108 {
0109     if (is_of_node(fwnode))
0110         return of_node_name_eq(to_of_node(fwnode), name);
0111     return false;
0112 }
0113 
0114 static int cti_plat_create_v8_etm_connection(struct device *dev,
0115                          struct cti_drvdata *drvdata)
0116 {
0117     int ret = -ENOMEM, i;
0118     struct fwnode_handle *root_fwnode, *cs_fwnode;
0119     const char *assoc_name = NULL;
0120     struct coresight_device *csdev;
0121     struct cti_trig_con *tc = NULL;
0122 
0123     root_fwnode = dev_fwnode(dev);
0124     if (IS_ERR_OR_NULL(root_fwnode))
0125         return -EINVAL;
0126 
0127     /* Can optionally have an etm node - return if not  */
0128     cs_fwnode = fwnode_find_reference(root_fwnode, CTI_DT_CSDEV_ASSOC, 0);
0129     if (IS_ERR(cs_fwnode))
0130         return 0;
0131 
0132     /* allocate memory */
0133     tc = cti_allocate_trig_con(dev, NR_V8ETM_INOUT_SIGS,
0134                    NR_V8ETM_INOUT_SIGS);
0135     if (!tc)
0136         goto create_v8_etm_out;
0137 
0138     /* build connection data */
0139     tc->con_in->used_mask = 0xF0; /* sigs <4,5,6,7> */
0140     tc->con_out->used_mask = 0xF0; /* sigs <4,5,6,7> */
0141 
0142     /*
0143      * The EXTOUT type signals from the ETM are connected to a set of input
0144      * triggers on the CTI, the EXTIN being connected to output triggers.
0145      */
0146     for (i = 0; i < NR_V8ETM_INOUT_SIGS; i++) {
0147         tc->con_in->sig_types[i] = ETM_EXTOUT;
0148         tc->con_out->sig_types[i] = ETM_EXTIN;
0149     }
0150 
0151     /*
0152      * We look to see if the ETM coresight device associated with this
0153      * handle has been registered with the system - i.e. probed before
0154      * this CTI. If so csdev will be non NULL and we can use the device
0155      * name and pass the csdev to the connection entry function where
0156      * the association will be recorded.
0157      * If not, then simply record the name in the connection data, the
0158      * probing of the ETM will call into the CTI driver API to update the
0159      * association then.
0160      */
0161     assoc_name = cti_plat_get_csdev_or_node_name(cs_fwnode, &csdev);
0162     ret = cti_add_connection_entry(dev, drvdata, tc, csdev, assoc_name);
0163 
0164 create_v8_etm_out:
0165     fwnode_handle_put(cs_fwnode);
0166     return ret;
0167 }
0168 
0169 /*
0170  * Create an architecturally defined v8 connection
0171  * must have a cpu, can have an ETM.
0172  */
0173 static int cti_plat_create_v8_connections(struct device *dev,
0174                       struct cti_drvdata *drvdata)
0175 {
0176     struct cti_device *cti_dev = &drvdata->ctidev;
0177     struct cti_trig_con *tc = NULL;
0178     int cpuid = 0;
0179     char cpu_name_str[16];
0180     int ret = -ENOMEM;
0181 
0182     /* Must have a cpu node */
0183     cpuid = cti_plat_get_cpu_at_node(dev_fwnode(dev));
0184     if (cpuid < 0) {
0185         dev_warn(dev,
0186              "ARM v8 architectural CTI connection: missing cpu\n");
0187         return -EINVAL;
0188     }
0189     cti_dev->cpu = cpuid;
0190 
0191     /* Allocate the v8 cpu connection memory */
0192     tc = cti_allocate_trig_con(dev, NR_V8PE_IN_SIGS, NR_V8PE_OUT_SIGS);
0193     if (!tc)
0194         goto of_create_v8_out;
0195 
0196     /* Set the v8 PE CTI connection data */
0197     tc->con_in->used_mask = 0x3; /* sigs <0 1> */
0198     tc->con_in->sig_types[0] = PE_DBGTRIGGER;
0199     tc->con_in->sig_types[1] = PE_PMUIRQ;
0200     tc->con_out->used_mask = 0x7; /* sigs <0 1 2 > */
0201     tc->con_out->sig_types[0] = PE_EDBGREQ;
0202     tc->con_out->sig_types[1] = PE_DBGRESTART;
0203     tc->con_out->sig_types[2] = PE_CTIIRQ;
0204     scnprintf(cpu_name_str, sizeof(cpu_name_str), "cpu%d", cpuid);
0205 
0206     ret = cti_add_connection_entry(dev, drvdata, tc, NULL, cpu_name_str);
0207     if (ret)
0208         goto of_create_v8_out;
0209 
0210     /* Create the v8 ETM associated connection */
0211     ret = cti_plat_create_v8_etm_connection(dev, drvdata);
0212     if (ret)
0213         goto of_create_v8_out;
0214 
0215     /* filter pe_edbgreq - PE trigout sig <0> */
0216     drvdata->config.trig_out_filter |= 0x1;
0217 
0218 of_create_v8_out:
0219     return ret;
0220 }
0221 
0222 static int cti_plat_check_v8_arch_compatible(struct device *dev)
0223 {
0224     struct fwnode_handle *fwnode = dev_fwnode(dev);
0225 
0226     if (is_of_node(fwnode))
0227         return of_device_is_compatible(to_of_node(fwnode),
0228                            CTI_DT_V8ARCH_COMPAT);
0229     return 0;
0230 }
0231 
0232 static int cti_plat_count_sig_elements(const struct fwnode_handle *fwnode,
0233                        const char *name)
0234 {
0235     int nr_elem = fwnode_property_count_u32(fwnode, name);
0236 
0237     return (nr_elem < 0 ? 0 : nr_elem);
0238 }
0239 
0240 static int cti_plat_read_trig_group(struct cti_trig_grp *tgrp,
0241                     const struct fwnode_handle *fwnode,
0242                     const char *grp_name)
0243 {
0244     int idx, err = 0;
0245     u32 *values;
0246 
0247     if (!tgrp->nr_sigs)
0248         return 0;
0249 
0250     values = kcalloc(tgrp->nr_sigs, sizeof(u32), GFP_KERNEL);
0251     if (!values)
0252         return -ENOMEM;
0253 
0254     err = fwnode_property_read_u32_array(fwnode, grp_name,
0255                          values, tgrp->nr_sigs);
0256 
0257     if (!err) {
0258         /* set the signal usage mask */
0259         for (idx = 0; idx < tgrp->nr_sigs; idx++)
0260             tgrp->used_mask |= BIT(values[idx]);
0261     }
0262 
0263     kfree(values);
0264     return err;
0265 }
0266 
0267 static int cti_plat_read_trig_types(struct cti_trig_grp *tgrp,
0268                     const struct fwnode_handle *fwnode,
0269                     const char *type_name)
0270 {
0271     int items, err = 0, nr_sigs;
0272     u32 *values = NULL, i;
0273 
0274     /* allocate an array according to number of signals in connection */
0275     nr_sigs = tgrp->nr_sigs;
0276     if (!nr_sigs)
0277         return 0;
0278 
0279     /* see if any types have been included in the device description */
0280     items = cti_plat_count_sig_elements(fwnode, type_name);
0281     if (items > nr_sigs)
0282         return -EINVAL;
0283 
0284     /* need an array to store the values iff there are any */
0285     if (items) {
0286         values = kcalloc(items, sizeof(u32), GFP_KERNEL);
0287         if (!values)
0288             return -ENOMEM;
0289 
0290         err = fwnode_property_read_u32_array(fwnode, type_name,
0291                              values, items);
0292         if (err)
0293             goto read_trig_types_out;
0294     }
0295 
0296     /*
0297      * Match type id to signal index, 1st type to 1st index etc.
0298      * If fewer types than signals default remainder to GEN_IO.
0299      */
0300     for (i = 0; i < nr_sigs; i++) {
0301         if (i < items) {
0302             tgrp->sig_types[i] =
0303                 values[i] < CTI_TRIG_MAX ? values[i] : GEN_IO;
0304         } else {
0305             tgrp->sig_types[i] = GEN_IO;
0306         }
0307     }
0308 
0309 read_trig_types_out:
0310     kfree(values);
0311     return err;
0312 }
0313 
0314 static int cti_plat_process_filter_sigs(struct cti_drvdata *drvdata,
0315                     const struct fwnode_handle *fwnode)
0316 {
0317     struct cti_trig_grp *tg = NULL;
0318     int err = 0, nr_filter_sigs;
0319 
0320     nr_filter_sigs = cti_plat_count_sig_elements(fwnode,
0321                              CTI_DT_FILTER_OUT_SIGS);
0322     if (nr_filter_sigs == 0)
0323         return 0;
0324 
0325     if (nr_filter_sigs > drvdata->config.nr_trig_max)
0326         return -EINVAL;
0327 
0328     tg = kzalloc(sizeof(*tg), GFP_KERNEL);
0329     if (!tg)
0330         return -ENOMEM;
0331 
0332     err = cti_plat_read_trig_group(tg, fwnode, CTI_DT_FILTER_OUT_SIGS);
0333     if (!err)
0334         drvdata->config.trig_out_filter |= tg->used_mask;
0335 
0336     kfree(tg);
0337     return err;
0338 }
0339 
0340 static int cti_plat_create_connection(struct device *dev,
0341                       struct cti_drvdata *drvdata,
0342                       struct fwnode_handle *fwnode)
0343 {
0344     struct cti_trig_con *tc = NULL;
0345     int cpuid = -1, err = 0;
0346     struct coresight_device *csdev = NULL;
0347     const char *assoc_name = "unknown";
0348     char cpu_name_str[16];
0349     int nr_sigs_in, nr_sigs_out;
0350 
0351     /* look to see how many in and out signals we have */
0352     nr_sigs_in = cti_plat_count_sig_elements(fwnode, CTI_DT_TRIGIN_SIGS);
0353     nr_sigs_out = cti_plat_count_sig_elements(fwnode, CTI_DT_TRIGOUT_SIGS);
0354 
0355     if ((nr_sigs_in > drvdata->config.nr_trig_max) ||
0356         (nr_sigs_out > drvdata->config.nr_trig_max))
0357         return -EINVAL;
0358 
0359     tc = cti_allocate_trig_con(dev, nr_sigs_in, nr_sigs_out);
0360     if (!tc)
0361         return -ENOMEM;
0362 
0363     /* look for the signals properties. */
0364     err = cti_plat_read_trig_group(tc->con_in, fwnode,
0365                        CTI_DT_TRIGIN_SIGS);
0366     if (err)
0367         goto create_con_err;
0368 
0369     err = cti_plat_read_trig_types(tc->con_in, fwnode,
0370                        CTI_DT_TRIGIN_TYPES);
0371     if (err)
0372         goto create_con_err;
0373 
0374     err = cti_plat_read_trig_group(tc->con_out, fwnode,
0375                        CTI_DT_TRIGOUT_SIGS);
0376     if (err)
0377         goto create_con_err;
0378 
0379     err = cti_plat_read_trig_types(tc->con_out, fwnode,
0380                        CTI_DT_TRIGOUT_TYPES);
0381     if (err)
0382         goto create_con_err;
0383 
0384     err = cti_plat_process_filter_sigs(drvdata, fwnode);
0385     if (err)
0386         goto create_con_err;
0387 
0388     /* read the connection name if set - may be overridden by later */
0389     fwnode_property_read_string(fwnode, CTI_DT_CONN_NAME, &assoc_name);
0390 
0391     /* associated cpu ? */
0392     cpuid = cti_plat_get_cpu_at_node(fwnode);
0393     if (cpuid >= 0) {
0394         drvdata->ctidev.cpu = cpuid;
0395         scnprintf(cpu_name_str, sizeof(cpu_name_str), "cpu%d", cpuid);
0396         assoc_name = cpu_name_str;
0397     } else {
0398         /* associated device ? */
0399         struct fwnode_handle *cs_fwnode = fwnode_find_reference(fwnode,
0400                                     CTI_DT_CSDEV_ASSOC,
0401                                     0);
0402         if (!IS_ERR(cs_fwnode)) {
0403             assoc_name = cti_plat_get_csdev_or_node_name(cs_fwnode,
0404                                      &csdev);
0405             fwnode_handle_put(cs_fwnode);
0406         }
0407     }
0408     /* set up a connection */
0409     err = cti_add_connection_entry(dev, drvdata, tc, csdev, assoc_name);
0410 
0411 create_con_err:
0412     return err;
0413 }
0414 
0415 static int cti_plat_create_impdef_connections(struct device *dev,
0416                           struct cti_drvdata *drvdata)
0417 {
0418     int rc = 0;
0419     struct fwnode_handle *fwnode = dev_fwnode(dev);
0420     struct fwnode_handle *child = NULL;
0421 
0422     if (IS_ERR_OR_NULL(fwnode))
0423         return -EINVAL;
0424 
0425     fwnode_for_each_child_node(fwnode, child) {
0426         if (cti_plat_node_name_eq(child, CTI_DT_CONNS))
0427             rc = cti_plat_create_connection(dev, drvdata,
0428                             child);
0429         if (rc != 0)
0430             break;
0431     }
0432     fwnode_handle_put(child);
0433 
0434     return rc;
0435 }
0436 
0437 /* get the hardware configuration & connection data. */
0438 static int cti_plat_get_hw_data(struct device *dev, struct cti_drvdata *drvdata)
0439 {
0440     int rc = 0;
0441     struct cti_device *cti_dev = &drvdata->ctidev;
0442 
0443     /* get any CTM ID - defaults to 0 */
0444     device_property_read_u32(dev, CTI_DT_CTM_ID, &cti_dev->ctm_id);
0445 
0446     /* check for a v8 architectural CTI device */
0447     if (cti_plat_check_v8_arch_compatible(dev))
0448         rc = cti_plat_create_v8_connections(dev, drvdata);
0449     else
0450         rc = cti_plat_create_impdef_connections(dev, drvdata);
0451     if (rc)
0452         return rc;
0453 
0454     /* if no connections, just add a single default based on max IN-OUT */
0455     if (cti_dev->nr_trig_con == 0)
0456         rc = cti_add_default_connection(dev, drvdata);
0457     return rc;
0458 }
0459 
0460 struct coresight_platform_data *
0461 coresight_cti_get_platform_data(struct device *dev)
0462 {
0463     int ret = -ENOENT;
0464     struct coresight_platform_data *pdata = NULL;
0465     struct fwnode_handle *fwnode = dev_fwnode(dev);
0466     struct cti_drvdata *drvdata = dev_get_drvdata(dev);
0467 
0468     if (IS_ERR_OR_NULL(fwnode))
0469         goto error;
0470 
0471     /*
0472      * Alloc platform data but leave it zero init. CTI does not use the
0473      * same connection infrastructuree as trace path components but an
0474      * empty struct enables us to use the standard coresight component
0475      * registration code.
0476      */
0477     pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
0478     if (!pdata) {
0479         ret = -ENOMEM;
0480         goto error;
0481     }
0482 
0483     /* get some CTI specifics */
0484     ret = cti_plat_get_hw_data(dev, drvdata);
0485 
0486     if (!ret)
0487         return pdata;
0488 error:
0489     return ERR_PTR(ret);
0490 }