#include "coresight-cti.h"
 
+/*
+ * Declare the number of static declared attribute groups
+ * Value includes groups + NULL value at end of table.
+ */
+#define CORESIGHT_CTI_STATIC_GROUPS_MAX 5
+
+/*
+ * List of trigger signal type names. Match the constants declared in
+ * include\dt-bindings\arm\coresight-cti-dt.h
+ */
+static const char * const sig_type_names[] = {
+       "genio",        /* GEN_IO */
+       "intreq",       /* GEN_INTREQ */
+       "intack",       /* GEN_INTACK */
+       "haltreq",      /* GEN_HALTREQ */
+       "restartreq",   /* GEN_RESTARTREQ */
+       "pe_edbgreq",   /* PE_EDBGREQ */
+       "pe_dbgrestart",/* PE_DBGRESTART */
+       "pe_ctiirq",    /* PE_CTIIRQ */
+       "pe_pmuirq",    /* PE_PMUIRQ */
+       "pe_dbgtrigger",/* PE_DBGTRIGGER */
+       "etm_extout",   /* ETM_EXTOUT */
+       "etm_extin",    /* ETM_EXTIN */
+       "snk_full",     /* SNK_FULL */
+       "snk_acqcomp",  /* SNK_ACQCOMP */
+       "snk_flushcomp",/* SNK_FLUSHCOMP */
+       "snk_flushin",  /* SNK_FLUSHIN */
+       "snk_trigin",   /* SNK_TRIGIN */
+       "stm_asyncout", /* STM_ASYNCOUT */
+       "stm_tout_spte",/* STM_TOUT_SPTE */
+       "stm_tout_sw",  /* STM_TOUT_SW */
+       "stm_tout_hete",/* STM_TOUT_HETE */
+       "stm_hwevent",  /* STM_HWEVENT */
+       "ela_tstart",   /* ELA_TSTART */
+       "ela_tstop",    /* ELA_TSTOP */
+       "ela_dbgreq",   /* ELA_DBGREQ */
+};
+
+/* Show function pointer used in the connections dynamic declared attributes*/
+typedef ssize_t (*p_show_fn)(struct device *dev, struct device_attribute *attr,
+                            char *buf);
+
+/* Connection attribute types */
+enum cti_conn_attr_type {
+       CTI_CON_ATTR_NAME,
+       CTI_CON_ATTR_TRIGIN_SIG,
+       CTI_CON_ATTR_TRIGOUT_SIG,
+       CTI_CON_ATTR_TRIGIN_TYPES,
+       CTI_CON_ATTR_TRIGOUT_TYPES,
+       CTI_CON_ATTR_MAX,
+};
+
+/* Names for the connection attributes */
+static const char * const con_attr_names[CTI_CON_ATTR_MAX] = {
+       "name",
+       "in_signals",
+       "out_signals",
+       "in_types",
+       "out_types",
+};
+
 /* basic attributes */
 static ssize_t enable_show(struct device *dev,
                           struct device_attribute *attr,
 }
 static DEVICE_ATTR_RO(ctmid);
 
+static ssize_t nr_trigger_cons_show(struct device *dev,
+                                   struct device_attribute *attr,
+                                   char *buf)
+{
+       struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+       return sprintf(buf, "%d\n", drvdata->ctidev.nr_trig_con);
+}
+static DEVICE_ATTR_RO(nr_trigger_cons);
+
 /* attribute and group sysfs tables. */
 static struct attribute *coresight_cti_attrs[] = {
        &dev_attr_enable.attr,
        &dev_attr_powered.attr,
        &dev_attr_ctmid.attr,
+       &dev_attr_nr_trigger_cons.attr,
        NULL,
 };
 
        NULL,
 };
 
-/* sysfs groups */
+/* Create the connections trigger groups and attrs dynamically */
+/*
+ * Each connection has dynamic group triggers<N> + name, trigin/out sigs/types
+ * attributes, + each device has static nr_trigger_cons giving the number
+ * of groups. e.g. in sysfs:-
+ * /cti_<name>/triggers0
+ * /cti_<name>/triggers1
+ * /cti_<name>/nr_trigger_cons
+ * where nr_trigger_cons = 2
+ */
+static ssize_t con_name_show(struct device *dev,
+                            struct device_attribute *attr,
+                            char *buf)
+{
+       struct dev_ext_attribute *ext_attr =
+               container_of(attr, struct dev_ext_attribute, attr);
+       struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
+
+       return sprintf(buf, "%s\n", con->con_dev_name);
+}
+
+static ssize_t trigin_sig_show(struct device *dev,
+                              struct device_attribute *attr,
+                              char *buf)
+{
+       struct dev_ext_attribute *ext_attr =
+               container_of(attr, struct dev_ext_attribute, attr);
+       struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
+       struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
+       struct cti_config *cfg = &drvdata->config;
+       unsigned long mask = con->con_in->used_mask;
+
+       return bitmap_print_to_pagebuf(true, buf, &mask, cfg->nr_trig_max);
+}
+
+static ssize_t trigout_sig_show(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       struct dev_ext_attribute *ext_attr =
+               container_of(attr, struct dev_ext_attribute, attr);
+       struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
+       struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
+       struct cti_config *cfg = &drvdata->config;
+       unsigned long mask = con->con_out->used_mask;
+
+       return bitmap_print_to_pagebuf(true, buf, &mask, cfg->nr_trig_max);
+}
+
+/* convert a sig type id to a name */
+static const char *
+cti_sig_type_name(struct cti_trig_con *con, int used_count, bool in)
+{
+       int idx = 0;
+       struct cti_trig_grp *grp = in ? con->con_in : con->con_out;
+
+       if (used_count < grp->nr_sigs)
+               idx = grp->sig_types[used_count];
+       return sig_type_names[idx];
+}
+
+static ssize_t trigin_type_show(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       struct dev_ext_attribute *ext_attr =
+               container_of(attr, struct dev_ext_attribute, attr);
+       struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
+       int sig_idx, used = 0;
+       const char *name;
+
+       for (sig_idx = 0; sig_idx < con->con_in->nr_sigs; sig_idx++) {
+               name = cti_sig_type_name(con, sig_idx, true);
+               used += sprintf(buf + used, "%s ", name);
+       }
+       used += sprintf(buf + used, "\n");
+       return used;
+}
+
+static ssize_t trigout_type_show(struct device *dev,
+                                struct device_attribute *attr,
+                                char *buf)
+{
+       struct dev_ext_attribute *ext_attr =
+               container_of(attr, struct dev_ext_attribute, attr);
+       struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
+       int sig_idx, used = 0;
+       const char *name;
+
+       for (sig_idx = 0; sig_idx < con->con_out->nr_sigs; sig_idx++) {
+               name = cti_sig_type_name(con, sig_idx, false);
+               used += sprintf(buf + used, "%s ", name);
+       }
+       used += sprintf(buf + used, "\n");
+       return used;
+}
+
+/*
+ * Array of show function names declared above to allow selection
+ * for the connection attributes
+ */
+static p_show_fn show_fns[CTI_CON_ATTR_MAX] = {
+       con_name_show,
+       trigin_sig_show,
+       trigout_sig_show,
+       trigin_type_show,
+       trigout_type_show,
+};
+
+static int cti_create_con_sysfs_attr(struct device *dev,
+                                    struct cti_trig_con *con,
+                                    enum cti_conn_attr_type attr_type,
+                                    int attr_idx)
+{
+       struct dev_ext_attribute *eattr = 0;
+       char *name = 0;
+
+       eattr = devm_kzalloc(dev, sizeof(struct dev_ext_attribute),
+                                   GFP_KERNEL);
+       if (eattr) {
+               name = devm_kstrdup(dev, con_attr_names[attr_type],
+                                   GFP_KERNEL);
+               if (name) {
+                       /* fill out the underlying attribute struct */
+                       eattr->attr.attr.name = name;
+                       eattr->attr.attr.mode = 0444;
+
+                       /* now the device_attribute struct */
+                       eattr->attr.show = show_fns[attr_type];
+               } else {
+                       return -ENOMEM;
+               }
+       } else {
+               return -ENOMEM;
+       }
+       eattr->var = con;
+       con->con_attrs[attr_idx] = &eattr->attr.attr;
+       return 0;
+}
+
+static struct attribute_group *
+cti_create_con_sysfs_group(struct device *dev, struct cti_device *ctidev,
+                          int con_idx, struct cti_trig_con *tc)
+{
+       struct attribute_group *group = NULL;
+       int grp_idx;
+
+       group = devm_kzalloc(dev, sizeof(struct attribute_group), GFP_KERNEL);
+       if (!group)
+               return NULL;
+
+       group->name = devm_kasprintf(dev, GFP_KERNEL, "triggers%d", con_idx);
+       if (!group->name)
+               return NULL;
+
+       grp_idx = con_idx + CORESIGHT_CTI_STATIC_GROUPS_MAX - 1;
+       ctidev->con_groups[grp_idx] = group;
+       tc->attr_group = group;
+       return group;
+}
+
+/* create a triggers connection group and the attributes for that group */
+static int cti_create_con_attr_set(struct device *dev, int con_idx,
+                                  struct cti_device *ctidev,
+                                  struct cti_trig_con *tc)
+{
+       struct attribute_group *attr_group = NULL;
+       int attr_idx = 0;
+       int err = -ENOMEM;
+
+       attr_group = cti_create_con_sysfs_group(dev, ctidev, con_idx, tc);
+       if (!attr_group)
+               return -ENOMEM;
+
+       /* allocate NULL terminated array of attributes */
+       tc->con_attrs = devm_kcalloc(dev, CTI_CON_ATTR_MAX + 1,
+                                    sizeof(struct attribute *), GFP_KERNEL);
+       if (!tc->con_attrs)
+               return -ENOMEM;
+
+       err = cti_create_con_sysfs_attr(dev, tc, CTI_CON_ATTR_NAME,
+                                       attr_idx++);
+       if (err)
+               return err;
+
+       if (tc->con_in->nr_sigs > 0) {
+               err = cti_create_con_sysfs_attr(dev, tc,
+                                               CTI_CON_ATTR_TRIGIN_SIG,
+                                               attr_idx++);
+               if (err)
+                       return err;
+
+               err = cti_create_con_sysfs_attr(dev, tc,
+                                               CTI_CON_ATTR_TRIGIN_TYPES,
+                                               attr_idx++);
+               if (err)
+                       return err;
+       }
+
+       if (tc->con_out->nr_sigs > 0) {
+               err = cti_create_con_sysfs_attr(dev, tc,
+                                               CTI_CON_ATTR_TRIGOUT_SIG,
+                                               attr_idx++);
+               if (err)
+                       return err;
+
+               err = cti_create_con_sysfs_attr(dev, tc,
+                                               CTI_CON_ATTR_TRIGOUT_TYPES,
+                                               attr_idx++);
+               if (err)
+                       return err;
+       }
+       attr_group->attrs = tc->con_attrs;
+       return 0;
+}
+
+/* create the array of group pointers for the CTI sysfs groups */
+int cti_create_cons_groups(struct device *dev, struct cti_device *ctidev)
+{
+       int nr_groups;
+
+       /* nr groups = dynamic + static + NULL terminator */
+       nr_groups = ctidev->nr_trig_con + CORESIGHT_CTI_STATIC_GROUPS_MAX;
+       ctidev->con_groups = devm_kcalloc(dev, nr_groups,
+                                         sizeof(struct attribute_group *),
+                                         GFP_KERNEL);
+       if (!ctidev->con_groups)
+               return -ENOMEM;
+       return 0;
+}
+
+int cti_create_cons_sysfs(struct device *dev, struct cti_drvdata *drvdata)
+{
+       struct cti_device *ctidev = &drvdata->ctidev;
+       int err = 0, con_idx = 0, i;
+       struct cti_trig_con *tc = NULL;
+
+       err = cti_create_cons_groups(dev, ctidev);
+       if (err)
+               return err;
+
+       /* populate first locations with the static set of groups */
+       for (i = 0; i < (CORESIGHT_CTI_STATIC_GROUPS_MAX - 1); i++)
+               ctidev->con_groups[i] = coresight_cti_groups[i];
+
+       /* add dynamic set for each connection */
+       list_for_each_entry(tc, &ctidev->trig_cons, node) {
+               err = cti_create_con_attr_set(dev, con_idx++, ctidev, tc);
+               if (err)
+                       break;
+       }
+       return err;
+}
+
+/* attribute and group sysfs tables. */
 static const struct attribute_group coresight_cti_group = {
        .attrs = coresight_cti_attrs,
 };
        .name = "channels",
 };
 
-const struct attribute_group *coresight_cti_groups[] = {
+const struct attribute_group *
+coresight_cti_groups[CORESIGHT_CTI_STATIC_GROUPS_MAX] = {
        &coresight_cti_group,
        &coresight_cti_mgmt_group,
        &coresight_cti_regs_group,