/* * CTI devices can be associated with a PE, or be connected to CoreSight * hardware. We have a list of all CTIs irrespective of CPU bound or * otherwise. * * We assume that the non-CPU CTIs are always powered as we do with sinks etc. * * We leave the client to figure out if all the CTIs are interconnected with * the same CTM, in general this is the case but does not always have to be.
*/
/* net of CTI devices connected via CTM */ static LIST_HEAD(ect_net);
/* protect the list */ static DEFINE_MUTEX(ect_mutex);
/* power management handling */ staticint nr_cti_cpu;
/* quick lookup list for CPU bound CTIs when power handling */ staticstruct cti_drvdata *cti_cpu_drvdata[NR_CPUS];
/* * CTI naming. CTI bound to cores will have the name cti_cpu<N> where * N is the CPU ID. System CTIs will have the name cti_sys<I> where I * is an index allocated by order of discovery. * * CTI device name list - for CTI not bound to cores.
*/
DEFINE_CORESIGHT_DEVLIST(cti_sys_devs, "cti_sys");
/* write set of regs to hardware - call with spinlock claimed */ void cti_write_all_hw_regs(struct cti_drvdata *drvdata)
{ struct cti_config *config = &drvdata->config; int i;
/* cannot enable due to error */
cti_err_not_enabled:
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags); return rc;
}
/* re-enable CTI on CPU when using CPU hotplug */ staticvoid cti_cpuhp_enable_hw(struct cti_drvdata *drvdata)
{ struct cti_config *config = &drvdata->config;
raw_spin_lock(&drvdata->spinlock); /* write if enabled */ if (cti_active(config))
cti_write_single_reg(drvdata, CTIINTACK, ackval);
raw_spin_unlock(&drvdata->spinlock);
}
/* * Look at the HW DEVID register for some of the HW settings. * DEVID[15:8] - max number of in / out triggers.
*/ #define CTI_DEVID_MAXTRIGS(devid_val) ((int) BMVAL(devid_val, 8, 15))
/* DEVID[19:16] - number of CTM channels */ #define CTI_DEVID_CTMCHANNELS(devid_val) ((int) BMVAL(devid_val, 16, 19))
/* * no current hardware should exceed this, but protect the driver * in case of fault / out of spec hw
*/ if (config->nr_trig_max > CTIINOUTEN_MAX) {
dev_warn_once(dev, "Limiting HW MaxTrig value(%d) to driver max(%d)\n",
config->nr_trig_max, CTIINOUTEN_MAX);
config->nr_trig_max = CTIINOUTEN_MAX;
}
/* Most regs default to 0 as zalloc'ed except...*/
config->trig_filter_enable = true;
config->ctigate = GENMASK(config->nr_ctm_channels - 1, 0);
config->enable_req_count = 0;
}
/* * Add a connection entry to the list of connections for this * CTI device.
*/ int cti_add_connection_entry(struct device *dev, struct cti_drvdata *drvdata, struct cti_trig_con *tc, struct coresight_device *csdev, constchar *assoc_dev_name)
{ struct cti_device *cti_dev = &drvdata->ctidev;
tc->con_dev = csdev; /* * Prefer actual associated CS device dev name to supplied value - * which is likely to be node name / other conn name.
*/ if (csdev)
tc->con_dev_name = dev_name(&csdev->dev); elseif (assoc_dev_name != NULL) {
tc->con_dev_name = devm_kstrdup(dev,
assoc_dev_name, GFP_KERNEL); if (!tc->con_dev_name) return -ENOMEM;
}
list_add_tail(&tc->node, &cti_dev->trig_cons);
cti_dev->nr_trig_con++;
/* add connection usage bit info to overall info */
drvdata->config.trig_in_use |= tc->con_in->used_mask;
drvdata->config.trig_out_use |= tc->con_out->used_mask;
return 0;
}
/* create a trigger connection with appropriately sized signal groups */ struct cti_trig_con *cti_allocate_trig_con(struct device *dev, int in_sigs, int out_sigs)
{ struct cti_trig_con *tc = NULL; struct cti_trig_grp *in = NULL, *out = NULL;
tc = devm_kzalloc(dev, sizeof(struct cti_trig_con), GFP_KERNEL); if (!tc) return tc;
in = devm_kzalloc(dev,
offsetof(struct cti_trig_grp, sig_types[in_sigs]),
GFP_KERNEL); if (!in) return NULL;
out = devm_kzalloc(dev,
offsetof(struct cti_trig_grp, sig_types[out_sigs]),
GFP_KERNEL); if (!out) return NULL;
/* * Add a default connection if nothing else is specified. * single connection based on max in/out info, no assoc device
*/ int cti_add_default_connection(struct device *dev, struct cti_drvdata *drvdata)
{ int ret = 0; int n_trigs = drvdata->config.nr_trig_max;
u32 n_trig_mask = GENMASK(n_trigs - 1, 0); struct cti_trig_con *tc = NULL;
/* * Assume max trigs for in and out, * all used, default sig types allocated
*/
tc = cti_allocate_trig_con(dev, n_trigs, n_trigs); if (!tc) return -ENOMEM;
/** cti channel api **/ /* attach/detach channel from trigger - write through if enabled. */ int cti_channel_trig_op(struct device *dev, enum cti_chan_op op, enum cti_trig_dir direction, u32 channel_idx,
u32 trigger_idx)
{ struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); struct cti_config *config = &drvdata->config;
u32 trig_bitmask;
u32 chan_bitmask;
u32 reg_value; int reg_offset;
/* ensure indexes in range */ if ((channel_idx >= config->nr_ctm_channels) ||
(trigger_idx >= config->nr_trig_max)) return -EINVAL;
trig_bitmask = BIT(trigger_idx);
/* ensure registered triggers and not out filtered */ if (direction == CTI_TRIG_IN) { if (!(trig_bitmask & config->trig_in_use)) return -EINVAL;
} else { if (!(trig_bitmask & config->trig_out_use)) return -EINVAL;
if ((config->trig_filter_enable) &&
(config->trig_out_filter & trig_bitmask)) return -EINVAL;
}
/* update the local register values */
chan_bitmask = BIT(channel_idx);
reg_offset = (direction == CTI_TRIG_IN ? CTIINEN(trigger_idx) :
CTIOUTEN(trigger_idx));
raw_spin_lock(&drvdata->spinlock);
/* read - modify write - the trigger / channel enable value */
reg_value = direction == CTI_TRIG_IN ? config->ctiinen[trigger_idx] :
config->ctiouten[trigger_idx]; if (op == CTI_CHAN_ATTACH)
reg_value |= chan_bitmask; else
reg_value &= ~chan_bitmask;
/* write local copy */ if (direction == CTI_TRIG_IN)
config->ctiinen[trigger_idx] = reg_value; else
config->ctiouten[trigger_idx] = reg_value;
/* write through if enabled */ if (cti_active(config))
cti_write_single_reg(drvdata, reg_offset, reg_value);
raw_spin_unlock(&drvdata->spinlock); return 0;
}
link_err = coresight_add_sysfs_link(&link_info); if (link_err)
dev_warn(&drvdata->csdev->dev, "Failed to set CTI sysfs link %s<=>%s\n",
link_info.orig_name, link_info.target_name); return !link_err;
}
/* * Look for a matching connection device name in the list of connections. * If found then swap in the csdev name, set trig con association pointer * and return found.
*/ staticbool
cti_match_fixup_csdev(struct cti_device *ctidev, constchar *node_name, struct coresight_device *csdev)
{ struct cti_trig_con *tc; struct cti_drvdata *drvdata = container_of(ctidev, struct cti_drvdata,
ctidev);
list_for_each_entry(tc, &ctidev->trig_cons, node) { if (tc->con_dev_name) { if (!strcmp(node_name, tc->con_dev_name)) { /* match: so swap in csdev name & dev */
tc->con_dev_name = dev_name(&csdev->dev);
tc->con_dev = csdev; /* try to set sysfs link */ if (cti_add_sysfs_link(drvdata, tc)) returntrue; /* link failed - remove CTI reference */
tc->con_dev = NULL; break;
}
}
} returnfalse;
}
/* * Search the cti list to add an associated CTI into the supplied CS device * This will set the association if CTI declared before the CS device. * (called from coresight_register() without coresight_mutex locked).
*/ staticvoid cti_add_assoc_to_csdev(struct coresight_device *csdev)
{ struct cti_drvdata *ect_item; struct cti_device *ctidev; constchar *node_name = NULL;
/* protect the list */
mutex_lock(&ect_mutex);
/* exit if current is an ECT device.*/ if ((csdev->type == CORESIGHT_DEV_TYPE_HELPER &&
csdev->subtype.helper_subtype ==
CORESIGHT_DEV_SUBTYPE_HELPER_ECT_CTI) ||
list_empty(&ect_net)) goto cti_add_done;
/* if we didn't find the csdev previously we used the fwnode name */
node_name = cti_plat_get_node_name(dev_fwnode(csdev->dev.parent)); if (!node_name) goto cti_add_done;
/* for each CTI in list... */
list_for_each_entry(ect_item, &ect_net, node) {
ctidev = &ect_item->ctidev; if (cti_match_fixup_csdev(ctidev, node_name, csdev)) { /* * if we found a matching csdev then update the ECT * association pointer for the device with this CTI.
*/
coresight_add_helper(csdev, ect_item->csdev); break;
}
}
cti_add_done:
mutex_unlock(&ect_mutex);
}
/* * Operations to add and remove associated CTI. * Register to coresight core driver as call back function.
*/ staticstruct cti_assoc_op cti_assoc_ops = {
.add = cti_add_assoc_to_csdev,
.remove = cti_remove_assoc_from_csdev
};
/* * Update the cross references where the associated device was found * while we were building the connection info. This will occur if the * assoc device was registered before the CTI.
*/ staticvoid cti_update_conn_xrefs(struct cti_drvdata *drvdata)
{ struct cti_trig_con *tc; struct cti_device *ctidev = &drvdata->ctidev;
list_for_each_entry(tc, &ctidev->trig_cons, node) { if (tc->con_dev) { /* if we can set the sysfs link */ if (cti_add_sysfs_link(drvdata, tc)) /* set the CTI/csdev association */
coresight_add_helper(tc->con_dev,
drvdata->csdev); else /* otherwise remove reference from CTI */
tc->con_dev = NULL;
}
}
}
if (WARN_ON_ONCE(drvdata->ctidev.cpu != cpu)) return NOTIFY_BAD;
raw_spin_lock(&drvdata->spinlock);
switch (cmd) { case CPU_PM_ENTER: /* CTI regs all static - we have a copy & nothing to save */
drvdata->config.hw_powered = false; if (drvdata->config.hw_enabled)
coresight_disclaim_device(csdev); break;
case CPU_PM_ENTER_FAILED:
drvdata->config.hw_powered = true; if (drvdata->config.hw_enabled) { if (coresight_claim_device(csdev))
drvdata->config.hw_enabled = false;
} break;
case CPU_PM_EXIT: /* write hardware registers to re-enable. */
drvdata->config.hw_powered = true;
drvdata->config.hw_enabled = false;
/* check enable reference count to enable HW */ if (drvdata->config.enable_req_count) { /* check we can claim the device as we re-power */ if (coresight_claim_device(csdev)) goto cti_notify_exit;
/* * Free up CTI specific resources * called by dev->release, need to call down to underlying csdev release.
*/ staticvoid cti_device_release(struct device *dev)
{ struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); struct cti_drvdata *ect_item, *ect_tmp;
mutex_lock(&ect_mutex);
cti_pm_release(drvdata);
/* remove from the list */
list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, node) { if (ect_item == drvdata) {
list_del(&ect_item->node); break;
}
}
mutex_unlock(&ect_mutex);
/* default to powered - could change on PM notifications */
drvdata->config.hw_powered = true;
/* set up device name - will depend if cpu bound or otherwise */ if (drvdata->ctidev.cpu >= 0)
cti_desc.name = devm_kasprintf(dev, GFP_KERNEL, "cti_cpu%d",
drvdata->ctidev.cpu); else
cti_desc.name = coresight_alloc_device_name(&cti_sys_devs, dev); if (!cti_desc.name) return -ENOMEM;
/* setup CPU power management handling for CPU bound CTI devices. */
ret = cti_pm_setup(drvdata); if (ret) return ret;
/* create dynamic attributes for connections */
ret = cti_create_cons_sysfs(dev, drvdata); if (ret) {
dev_err(dev, "%s: create dynamic sysfs entries failed\n",
cti_desc.name); goto pm_release;
}
coresight_clear_self_claim_tag(&cti_desc.access);
drvdata->csdev = coresight_register(&cti_desc); if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev); goto pm_release;
}
/* add to list of CTI devices */
mutex_lock(&ect_mutex);
list_add(&drvdata->node, &ect_net); /* set any cross references */
cti_update_conn_xrefs(drvdata);
mutex_unlock(&ect_mutex);
/* set up release chain */
drvdata->csdev_release = drvdata->csdev->dev.release;
drvdata->csdev->dev.release = cti_device_release;
/* all done - dec pm refcount */
pm_runtime_put(&adev->dev);
dev_info(&drvdata->csdev->dev, "CTI initialized\n"); return 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.