On Wed, Jan 23, 2019 at 09:09:51PM +0000, Mike Leach wrote:
[cut]
On Fri, 18 Jan 2019 at 22:17, Mathieu Poirier
in->nr_sigs = 3;in->used_mask = 0x7;in->sig_names = NULL;out->nr_sigs = 3;out->used_mask = 0x7;out->sig_names = NULL;scnprintf(cpu_name_str, 16, "cpu%d", cpuid);cti_add_connection_entry(drvdata, in, out, NULL, cpu_name_str);drvdata->config.trig_out_filter = 0x1; /* filter dbgreq *//* v8 ETM associated config... */cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);if (cs_np) {Does it make sense to have a CTI connected to a CPU but not have an ETM associated with that CPU? Shouldn't this be treated as an error?
No. The ETM is optional, the CTI is not.
Please add a comment - if I triggered on it others will.
Alongside the halting debug signals - which are of limited use in this context - there are two other relevant signals. The CPU has a PMU overflow out signal connected to a trigin on the CTI, and there is a trigout assigned as a generic CTI IRQ - though the connection of this IRQ in any system is implementation defined - recommended attached to GIC or other interrupt controller in the system.
in = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);if (!in)return -ENOMEM;out = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);if (!out)return -ENOMEM;in->nr_sigs = 4;in->used_mask = 0xF0;in->sig_names = NULL;out->nr_sigs = 4;out->used_mask = 0xF0;out->sig_names = NULL;dev_info(drvdata->dev, "v8 finding assoc dev %s\n",cs_np->full_name);csdev = of_coresight_get_cs_device_by_node(cs_np);if (csdev)assoc_name = dev_name(&csdev->dev);elseassoc_name = cs_np->full_name;I'm guessing you're doing the above to deal with device discovery ordering. If I am correct please add a comment that makes it clear.
cti_add_connection_entry(drvdata, in, out, csdev, assoc_name);of_node_put(cs_np);}return 0;Same comment about memory release in case of something goes wrong. I've also taken the habit of adding a comment that mentions where the memory will be be released for each allocation I do. That helps me remember that I have to release the memory and allows other people to better understand how things work.
Good tip.
+}
+static int of_cti_read_trig_group(struct cti_trig_grp **trig_grp_ptr,
struct device_node *np,const char *grp_name,int max_trigs)+{
int items, err = 0;u32 value, pidx;struct cti_trig_grp *grp;grp = kzalloc(sizeof(struct cti_trig_grp), GFP_KERNEL);if (!grp)return -ENOMEM;*trig_grp_ptr = grp;items = of_property_count_elems_of_size(np, grp_name, 4);From the documentation and the DT bindings you've provided it is pretty easy to figure out why you have a 4 there but I will still need a comment to explain it. But have you explained anywhere in the documentation why trigger groups have 4 pins? Is it just that the HW only provides a maximum of 4 in and 4 out for each device? If so, it would be a good thing to write it somewhere.
The 4 here is the size of the individual element in the property - not the number of them. My understanding is that these will be 32 bit values - hence 4 bytes.
/* if the property doesn't exist or has no values, then return* an empty connection group*/if (items < 0)return 0;if (items > max_trigs)return -EINVAL;/* set the number of signals and usage mask */for (pidx = 0; pidx < items; pidx++) {err = of_property_read_u32_index(np, grp_name, pidx, &value);if (err)return err;Memory leak of 'grp'.
Possibly not here - grp is passed out of the function if successfully allocated - and should be cleaned up when the connection is cleaned up. Though this needs a re-check.
grp->nr_sigs++;grp->used_mask |= (0x1 << value);}return 0;+}
+static int of_cti_create_connection(struct device *dev,
struct device_node *np,struct cti_drvdata *drvdata)+{
struct cti_trig_grp *in = 0, *out = 0, *filter = 0;int cpuid = -1, err = 0, trig_max = drvdata->config.nr_trig_max;struct device_node *cs_np;struct coresight_device *csdev = NULL;const char *assoc_name = "unknown";char cpu_name_str[16];/* look for the signals properties. */err = of_cti_read_trig_group(&in, np, CTI_DT_TRIGIN_SIGS, trig_max);if (err)return err;err = of_cti_read_trig_group(&out, np, CTI_DT_TRIGOUT_SIGS, trig_max);if (err)return err;err = of_cti_read_trig_group(&filter, np, CTI_DT_FILTER_OUT_SIGS,trig_max);if (err)return err;Whatch out for memory leaks in the above.
/* read the connection name if set - may be overridden by later */of_property_read_string(np, CTI_DT_CONN_NAME, &assoc_name);/* associated cpu ? */cpuid = of_coresight_ect_get_cpu(np);drvdata->ctidev.cpu = cpuid;if (cpuid >= 0) {scnprintf(cpu_name_str, 16, "cpu%d", cpuid);assoc_name = cpu_name_str;} else {/* associated device ? */cs_np = of_parse_phandle(np, CTI_DT_CSDEV_ASSOC, 0);if (cs_np) {dev_info(dev, "finding assoc dev %s\n",cs_np->full_name);csdev = of_coresight_get_cs_device_by_node(cs_np);if (csdev) /* use device name if csdev found */assoc_name = dev_name(&csdev->dev);else /* otherwise node name for later association */assoc_name = cs_np->full_name;of_node_put(cs_np);}}/* set up a connection */err = cti_add_connection_entry(drvdata, in, out, csdev, assoc_name);/* note any filter info */if (!err)drvdata->config.trig_out_filter |= filter->used_mask;kfree(filter);return err;+}
After reviewing functions of_cti_get_hw_data(), of_cti_create_v8_connections() and of_cti_create_connection() I wonder if they shouldn't be added to a new of-coresight-cti.c file, along with the rest of the OF centric functions needed by the driver.
More to come on Monday.
Mathieu
Thanks for the review
Mike
+/* get the hardware configuration & connection data. */ +static int of_cti_get_hw_data(struct device *dev,
struct device_node *np,struct cti_drvdata *drvdata)+{
int rc = 0;struct cti_device *cti_dev = &drvdata->ctidev;struct device_node *nc = NULL;/* get any CTM ID - defaults to 0 */of_property_read_u32(np, CTI_DT_CTM_ID, &cti_dev->ctm_id);if (of_property_read_bool(np, CTI_DT_V8ARCH)) {rc = of_cti_create_v8_connections(drvdata, np);} else {for_each_child_of_node(np, nc) {if (of_node_cmp(nc->name, CTI_DT_CONNS) != 0)continue;dev_info(dev, "dt found node: %s %s\n",nc->name, nc->full_name);rc = of_cti_create_connection(dev, nc, drvdata);if (rc != 0)return rc;}}/* if no connections, just add a single default based on max IN-OUT */if (cti_dev->nr_trig_con == 0)rc = cti_add_default_connection(drvdata);return rc;+}
+static int +cti_match_con_name(struct cti_device *ctidev, const char *node_name,
const char *csdev_name)+{
int found = 0;struct cti_trig_con *trig_con;list_for_each_entry(trig_con, &ctidev->trig_cons, node) {if (trig_con->con_dev_name) {if (!strcmp(node_name, trig_con->con_dev_name)) {found = 1;/* match: so swap in csdev name */kfree(trig_con->con_dev_name);trig_con->con_dev_name =kstrdup(csdev_name, GFP_KERNEL);goto cti_con_name_match;}}}+cti_con_name_match:
return found;+}
+/* 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
- */
+int cti_add_assoc_to_csdev(struct coresight_device *csdev) +{
struct ect_node *ect_item;struct cti_device *ctidev;const char *node_name = NULL, *csdev_name;/* exit early for no CTIs or self referencing ECT devices.*/if (!cti_count) {dev_info(&csdev->dev, "no CTI to check\n");return 0;}if (!csdev->dev.parent->of_node) {dev_info(&csdev->dev, "No parent of_node pointer\n");return 0;}node_name = csdev->dev.parent->of_node->full_name;if (!node_name) {dev_info(&csdev->dev, "%s - bad node name\n", __func__);return 0;}dev_info(&csdev->dev, "%s - looking for %s\n", __func__, node_name);if (!cti_count || (csdev->type == CORESIGHT_DEV_TYPE_ECT)) {dev_info(&csdev->dev, "no CTI or skip self ref CTI\n");return 0;}csdev_name = dev_name(&csdev->dev);/* for each CTI in list... */mutex_lock(&ect_mutex);list_for_each_entry(ect_item, &ect_net, next) {ctidev = &ect_item->cti_drv->ctidev;if (cti_match_con_name(ctidev, node_name, csdev_name)) {csdev->ect_dev = ect_item->cti_drv->csdev;dev_info(&csdev->dev, "Found!\n");goto cti_found;}}+cti_found:
mutex_unlock(&ect_mutex);return 0;+} +EXPORT_SYMBOL_GPL(cti_add_assoc_to_csdev);
+/* attach/detach channel from trigger - write through if enabled. */ +static 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;dev_info(dev, "chan_trig_op: op %d, dir %d, chan %d, trig %d\n",op, direction, channel_idx, trigger_idx);/* ensure indexes in range */if ((channel_idx >= config->nr_ctm_channels) ||(trigger_idx >= config->nr_trig_max))return -EINVAL;trig_bitmask = 0x1 << 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 = 0x1 << channel_idx;reg_offset = (direction == CTI_TRIG_IN ? CTIINEN(trigger_idx) :CTIOUTEN(trigger_idx));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]);reg_value = (op == CTI_CHAN_ATTACH) ? reg_value | chan_bitmask :reg_value & (~chan_bitmask);/* write local copy */if (direction == CTI_TRIG_IN)config->ctiinen[trigger_idx] = reg_value;elseconfig->ctiouten[trigger_idx] = reg_value;/* write through if enabled */if (CTI_PWR_ENA(config))cti_write_single_reg(drvdata, reg_offset, reg_value);spin_unlock(&drvdata->spinlock);return 0;+}
+static int cti_channel_gate_op(struct device *dev,
enum cti_chan_gate_op op,u32 channel_idx)+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);struct cti_config *config = &drvdata->config;u32 chan_bitmask;u32 reg_value;int err = 0;if (channel_idx >= config->nr_ctm_channels)return -EINVAL;chan_bitmask = 0x1 << channel_idx;spin_lock(&drvdata->spinlock);reg_value = config->ctigate;switch (op) {case CTI_GATE_CHAN_ENABLE:reg_value |= chan_bitmask;break;case CTI_GATE_CHAN_DISABLE:reg_value &= ~chan_bitmask;break;case CTI_GATE_CHAN_ENABLE_ALL:reg_value = (0x1 << config->nr_ctm_channels) - 1;break;case CTI_GATE_CHAN_DISABLE_ALL:reg_value = 0x0;break;default:err = -EINVAL;break;}if (err == 0) {config->ctigate = reg_value;if (CTI_PWR_ENA(config))cti_write_single_reg(drvdata, CTIGATE, reg_value);}spin_unlock(&drvdata->spinlock);return err;+}
+static int cti_channel_setop(struct device *dev,
enum cti_chan_set_op op,u32 channel_idx)+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);struct cti_config *config = &drvdata->config;u32 chan_bitmask;u32 reg_value;u32 reg_offset;int err = 0;if (channel_idx >= config->nr_ctm_channels)return -EINVAL;chan_bitmask = 0x1 << channel_idx;spin_lock(&drvdata->spinlock);reg_value = config->ctiappset;switch (op) {case CTI_CHAN_SET:config->ctiappset |= chan_bitmask;reg_value = config->ctiappset;reg_offset = CTIAPPSET;break;case CTI_CHAN_CLR:config->ctiappset &= ~chan_bitmask;reg_value = chan_bitmask;reg_offset = CTIAPPCLEAR;break;case CTI_CHAN_PULSE:config->ctiappset &= ~chan_bitmask;reg_value = chan_bitmask;reg_offset = CTIAPPPULSE;break;default:err = -EINVAL;break;}if ((err == 0) && CTI_PWR_ENA(config))cti_write_single_reg(drvdata, reg_offset, reg_value);spin_unlock(&drvdata->spinlock);return err;+}
+/** cti ect operations **/ +static int cti_enable(struct coresight_device *csdev, void *__unused) +{
int rc;struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);atomic_inc(&drvdata->config.enable_req_count);rc = cti_enable_hw(drvdata);if (rc)atomic_dec(&drvdata->config.enable_req_count);return rc;+}
+static int cti_disable(struct coresight_device *csdev, void *__unused) +{
int rc = 0;struct cti_drvdata *drvdata = csdev_to_cti_drvdata(csdev);if (!atomic_dec_return(&drvdata->config.enable_req_count)) {rc = cti_disable_hw(drvdata);if (rc)atomic_inc(&drvdata->config.enable_req_count);}return rc;+}
+const struct coresight_ops_ect cti_ops_ect = {
.enable = cti_enable,.disable = cti_disable,+};
+const struct coresight_ops cti_ops = {
.ect_ops = &cti_ops_ect,+};
+/** attributes **/
+#define coresight_cti_reg(name, offset) \
coresight_simple_reg32(struct cti_drvdata, name, offset)+/* show a simple 32 bit value. if pval is NULL then live read,
- otherwise read from supplied pointer only
- */
+static ssize_t cti_reg32_show(struct device *dev, char *buf,
u32 *pval, int reg_offset)+{
unsigned long val = 0;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);struct cti_config *config = &drvdata->config;spin_lock(&drvdata->spinlock);if (pval) {val = (unsigned long)*pval;} else if ((reg_offset >= 0) && CTI_PWR_ENA(config)) {CS_UNLOCK(drvdata->base);val = readl_relaxed(drvdata->base + reg_offset);CS_LOCK(drvdata->base);}spin_unlock(&drvdata->spinlock);return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);+}
+/* store a simple 32 bit value.
- if pval not NULL, then copy to here too,
- if reg_offset >= 0 then write through if enabled.
- */
+static ssize_t cti_reg32_store(struct device *dev, const char *buf,
size_t size, u32 *pval,int reg_offset)+{
unsigned long val;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);struct cti_config *config = &drvdata->config;if (kstrtoul(buf, 16, &val))return -EINVAL;spin_lock(&drvdata->spinlock);/* local store */if (pval)*pval = (u32)val;/* write through of offset and enabled */if ((reg_offset >= 0) && CTI_PWR_ENA(config))cti_write_single_reg(drvdata, reg_offset, val);spin_unlock(&drvdata->spinlock);return size;+}
+/* basic attributes */ +static ssize_t enable_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
int enable_req;bool enabled, powered, cpuid;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);ssize_t size = 0;enable_req = atomic_read(&drvdata->config.enable_req_count);spin_lock(&drvdata->spinlock);powered = drvdata->config.hw_powered;enabled = drvdata->config.hw_enabled;cpuid = drvdata->ctidev.cpu;spin_unlock(&drvdata->spinlock);if (powered) {size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d powered;\n",enabled ? "enabled" : "disabled", cpuid);} else if (cpuid >= 0) {size = scnprintf(buf, PAGE_SIZE, "cti %s; cpu%d unpowered;\n",enable_req ? "enable req" : "disable req", cpuid);} else {size = scnprintf(buf, PAGE_SIZE, "cti %s; no assoc cpu;\n",enabled ? "enabled" : "disabled");}return size;+}
+static ssize_t enable_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
int ret = 0;unsigned long val;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);ret = kstrtoul(buf, 16, &val);if (ret)return ret;if (val)ret = cti_enable(drvdata->csdev, NULL);elseret = cti_disable(drvdata->csdev, NULL);if (ret)return ret;return size;+} +static DEVICE_ATTR_RW(enable);
+static ssize_t ctmid_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);return scnprintf(buf, PAGE_SIZE, "0x%x\n", drvdata->ctidev.ctm_id);+} +static DEVICE_ATTR_RO(ctmid);
+#define LF_ADJUST_BUF_PTRS(n) \
do { buf_pos += n; buf_size -= n; \if (buf_size <= 1) \goto lf_buf_full; \} while (0)+static ssize_t list_features_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
int used = 0, sig_idx, con_idx, nr_trig_max;/* buffer vars */int buf_size = PAGE_SIZE;char *buf_pos = buf;char *con_name;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);struct cti_config *cfg = &drvdata->config;struct cti_device *ctidev = &drvdata->ctidev;u32 sig_mask;struct cti_trig_con *con;/* basic feature info */used = scnprintf(buf_pos, buf_size,"CTI:%s; Channels:%d; Max Trigs:%d\n",dev_name(dev), cfg->nr_ctm_channels, cfg->nr_trig_max);LF_ADJUST_BUF_PTRS(used);used = scnprintf(buf_pos, buf_size, "CTM id = %d; Num Conns = %d\n",ctidev->ctm_id, ctidev->nr_trig_con);LF_ADJUST_BUF_PTRS(used);/* list each connection info */con_idx = 0;nr_trig_max = cfg->nr_trig_max;list_for_each_entry(con, &ctidev->trig_cons, node) {/* connection name */if (con->con_dev_name != NULL)con_name = con->con_dev_name;elsecon_name = "unknown";used = scnprintf(buf_pos, buf_size, "conn(%d:%s)\n",con_idx, con_name);LF_ADJUST_BUF_PTRS(used);con_idx++;/* connection signals */used = scnprintf(buf_pos, buf_size, "Trig IN(%d) [ ",con->con_in->nr_sigs);LF_ADJUST_BUF_PTRS(used);sig_mask = 0x1;for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {if (sig_mask & con->con_in->used_mask) {used = scnprintf(buf_pos, buf_size, "%d ",sig_idx);LF_ADJUST_BUF_PTRS(used);}sig_mask <<= 1;}used = scnprintf(buf_pos, buf_size, "]\n");LF_ADJUST_BUF_PTRS(used);used = scnprintf(buf_pos, buf_size, "Trig OUT(%d) [ ",con->con_out->nr_sigs);LF_ADJUST_BUF_PTRS(used);sig_mask = 0x1;for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {if (sig_mask & con->con_out->used_mask) {used = scnprintf(buf_pos, buf_size, "%d ",sig_idx);LF_ADJUST_BUF_PTRS(used);}sig_mask <<= 1;}used = scnprintf(buf_pos, buf_size, "]\n");LF_ADJUST_BUF_PTRS(used);}if (cfg->trig_out_filter) {used = scnprintf(buf_pos, buf_size, "Trig OUT_FILTERED [ ");LF_ADJUST_BUF_PTRS(used);sig_mask = 0x1;for (sig_idx = 0; sig_idx < nr_trig_max; sig_idx++) {if (sig_mask & cfg->trig_out_filter) {used = scnprintf(buf_pos, buf_size, "%d ",sig_idx);LF_ADJUST_BUF_PTRS(used);}sig_mask <<= 1;}used = scnprintf(buf_pos, buf_size, "]\n");LF_ADJUST_BUF_PTRS(used);}+lf_buf_full:
used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;return used;+} +static DEVICE_ATTR_RO(list_features);
+static struct attribute *coresight_cti_attrs[] = {
&dev_attr_enable.attr,&dev_attr_list_features.attr,&dev_attr_ctmid.attr,NULL,+};
+/* raw register attributes */ +static ssize_t inout_sel_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
u32 val;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);val = (u32)drvdata->config.ctiinout_sel;return scnprintf(buf, PAGE_SIZE, "%#x\n", val);+}
+static ssize_t inout_sel_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
unsigned long val;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);if (kstrtoul(buf, 16, &val))return -EINVAL;if (val > (CTIINOUTEN_MAX-1))return -EINVAL;spin_lock(&drvdata->spinlock);drvdata->config.ctiinout_sel = val;spin_unlock(&drvdata->spinlock);return size;+} +static DEVICE_ATTR_RW(inout_sel);
+static ssize_t inen_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
unsigned long val;int index;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);spin_lock(&drvdata->spinlock);index = drvdata->config.ctiinout_sel;val = drvdata->config.ctiinen[index];spin_unlock(&drvdata->spinlock);return scnprintf(buf, PAGE_SIZE, "INEN%d %#lx\n", index, val);+}
+static ssize_t inen_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
unsigned long val;int index;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);struct cti_config *config = &drvdata->config;if (kstrtoul(buf, 16, &val))return -EINVAL;spin_lock(&drvdata->spinlock);index = config->ctiinout_sel;config->ctiinen[index] = val;/* write through if enabled */if (CTI_PWR_ENA(config))cti_write_single_reg(drvdata, CTIINEN(index), val);spin_unlock(&drvdata->spinlock);return size;+} +static DEVICE_ATTR_RW(inen);
+static ssize_t outen_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
unsigned long val;int index;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);spin_lock(&drvdata->spinlock);index = drvdata->config.ctiinout_sel;val = drvdata->config.ctiouten[index];spin_unlock(&drvdata->spinlock);return scnprintf(buf, PAGE_SIZE, "OUTEN%d %#lx\n", index, val);+}
+static ssize_t outen_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
unsigned long val;int index;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);struct cti_config *config = &drvdata->config;if (kstrtoul(buf, 16, &val))return -EINVAL;spin_lock(&drvdata->spinlock);index = config->ctiinout_sel;config->ctiouten[index] = val;/* write through if enabled */if (CTI_PWR_ENA(config))cti_write_single_reg(drvdata, CTIOUTEN(index), val);spin_unlock(&drvdata->spinlock);return size;+} +static DEVICE_ATTR_RW(outen);
+static ssize_t gate_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);return cti_reg32_show(dev, buf, &drvdata->config.ctigate, -1);+}
+static ssize_t gate_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);return cti_reg32_store(dev, buf, size,&drvdata->config.ctigate, CTIGATE);+} +static DEVICE_ATTR_RW(gate);
+static ssize_t asicctl_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);return cti_reg32_show(dev, buf, &drvdata->config.asicctl, -1);+}
+static ssize_t asicctl_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);return cti_reg32_store(dev, buf, size,&drvdata->config.asicctl, ASICCTL);+} +static DEVICE_ATTR_RW(asicctl);
+static ssize_t intack_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
unsigned long val;if (kstrtoul(buf, 16, &val))return -EINVAL;cti_write_intack(dev, val);return size;+} +static DEVICE_ATTR_WO(intack);
+static ssize_t appset_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);return cti_reg32_show(dev, buf, &drvdata->config.ctiappset, -1);+}
+static ssize_t appset_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);return cti_reg32_store(dev, buf, size,&drvdata->config.ctiappset, CTIAPPSET);+} +static DEVICE_ATTR_RW(appset);
+static ssize_t appclear_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
unsigned long val, mask;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);struct cti_config *config = &drvdata->config;if (kstrtoul(buf, 16, &val))return -EINVAL;spin_lock(&drvdata->spinlock);/* a 1'b1 in appclr clears down the same bit in appset*/mask = ~val;config->ctiappset &= mask;/* write through if enabled */if (CTI_PWR_ENA(config))cti_write_single_reg(drvdata, CTIAPPCLEAR, val);spin_unlock(&drvdata->spinlock);return size;+} +static DEVICE_ATTR_WO(appclear);
+static ssize_t apppulse_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
unsigned long val, mask;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);struct cti_config *config = &drvdata->config;if (kstrtoul(buf, 16, &val))return -EINVAL;spin_lock(&drvdata->spinlock);/* a 1'b1 in apppulse sets then clears the bit,* effectively clears down the same bit in appset*/mask = ~val;config->ctiappset &= mask;/* write through if enabled */if (CTI_PWR_ENA(config))cti_write_single_reg(drvdata, CTIAPPPULSE, val);spin_unlock(&drvdata->spinlock);return size;+} +static DEVICE_ATTR_WO(apppulse);
+static ssize_t itchout_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
return cti_reg32_show(dev, buf, NULL, ITCHOUT);+}
+static ssize_t itchout_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
return cti_reg32_store(dev, buf, size, NULL, ITCHOUT);+} +static DEVICE_ATTR_RW(itchout);
+static ssize_t ittrigout_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
return cti_reg32_show(dev, buf, NULL, ITTRIGOUT);+}
+static ssize_t ittrigout_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
return cti_reg32_store(dev, buf, size, NULL, ITTRIGOUT);+} +static DEVICE_ATTR_RW(ittrigout);
+static ssize_t itchinack_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
return cti_reg32_store(dev, buf, size, NULL, ITCHINACK);+} +static DEVICE_ATTR_WO(itchinack);
+static ssize_t ittriginack_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
return cti_reg32_store(dev, buf, size, NULL, ITTRIGINACK);+} +static DEVICE_ATTR_WO(ittriginack);
+static ssize_t itctrl_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
return cti_reg32_show(dev, buf, NULL, CORESIGHT_ITCTRL);+}
+static ssize_t itctrl_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
return cti_reg32_store(dev, buf, size, NULL, CORESIGHT_ITCTRL);+} +static DEVICE_ATTR_RW(itctrl);
+coresight_cti_reg(triginstatus, CTITRIGINSTATUS); +coresight_cti_reg(trigoutstatus, CTITRIGOUTSTATUS); +coresight_cti_reg(chinstatus, CTICHINSTATUS); +coresight_cti_reg(choutstatus, CTICHOUTSTATUS); +coresight_cti_reg(ittrigin, ITTRIGIN); +coresight_cti_reg(itchin, ITCHIN); +coresight_cti_reg(itchoutack, ITCHOUTACK); +coresight_cti_reg(ittrigoutack, ITTRIGOUTACK);
+static struct attribute *coresight_cti_regs_attrs[] = {
&dev_attr_inout_sel.attr,&dev_attr_inen.attr,&dev_attr_outen.attr,&dev_attr_gate.attr,&dev_attr_asicctl.attr,&dev_attr_intack.attr,&dev_attr_appset.attr,&dev_attr_appclear.attr,&dev_attr_apppulse.attr,&dev_attr_triginstatus.attr,&dev_attr_trigoutstatus.attr,&dev_attr_chinstatus.attr,&dev_attr_choutstatus.attr,&dev_attr_itctrl.attr,&dev_attr_ittrigin.attr,&dev_attr_itchin.attr,&dev_attr_ittrigout.attr,&dev_attr_itchout.attr,&dev_attr_itchoutack.attr,&dev_attr_ittrigoutack.attr,&dev_attr_ittriginack.attr,&dev_attr_itchinack.attr,NULL,+};
+/* channel / trigger api */ +static int +cti_trig_op_parse(struct device *dev, enum cti_chan_op op,
enum cti_trig_dir dir, const char *buf, size_t size)+{
u32 chan_idx;u32 trig_idx;int items, err = size;/* extract chan idx and trigger idx */items = sscanf(buf, "%d %d", &chan_idx, &trig_idx);if (items) {err = cti_channel_trig_op(dev, op, dir, chan_idx, trig_idx);if (!err)err = size;} elseerr = -EINVAL;return err;+}
+static ssize_t trigin_attach_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_IN,buf, size);+} +static DEVICE_ATTR_WO(trigin_attach);
+static ssize_t trigin_detach_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_IN,buf, size);+} +static DEVICE_ATTR_WO(trigin_detach);
+static ssize_t trigout_attach_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
return cti_trig_op_parse(dev, CTI_CHAN_ATTACH, CTI_TRIG_OUT,buf, size);+} +static DEVICE_ATTR_WO(trigout_attach);
+static ssize_t trigout_detach_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
return cti_trig_op_parse(dev, CTI_CHAN_DETACH, CTI_TRIG_OUT,buf, size);+} +static DEVICE_ATTR_WO(trigout_detach);
+static ssize_t gate_enable_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
int err = 0, channel = 0, items;if (strcmp(buf, "all") == 0) {err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE_ALL, 0);} else {items = sscanf(buf, "%d", &channel);if (!items)return -EINVAL;err = cti_channel_gate_op(dev, CTI_GATE_CHAN_ENABLE, 0);}return err ? err : size;+} +static DEVICE_ATTR_WO(gate_enable);
+static ssize_t gate_disable_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
int err = 0, channel = 0, items;if (strcmp(buf, "all") == 0) {err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE_ALL, 0);} else {items = sscanf(buf, "%d", &channel);if (!items)return -EINVAL;err = cti_channel_gate_op(dev, CTI_GATE_CHAN_DISABLE, 0);}return err ? err : size;+} +static DEVICE_ATTR_WO(gate_disable);
+static int +chan_op_parse(struct device *dev, enum cti_chan_set_op op, const char *buf) +{
int err = 0, channel = 0, items;items = sscanf(buf, "%d", &channel);if (!items)return -EINVAL;err = cti_channel_setop(dev, op, channel);return err;+}
+static ssize_t chan_set_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
int err = chan_op_parse(dev, CTI_CHAN_SET, buf);return err ? err : size;+} +static DEVICE_ATTR_WO(chan_set);
+static ssize_t chan_clear_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
int err = chan_op_parse(dev, CTI_CHAN_CLR, buf);return err ? err : size;+} +static DEVICE_ATTR_WO(chan_clear);
+static ssize_t chan_pulse_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
int err = chan_op_parse(dev, CTI_CHAN_PULSE, buf);return err ? err : size;+} +static DEVICE_ATTR_WO(chan_pulse);
+static ssize_t trig_filter_enable_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
unsigned long val;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);spin_lock(&drvdata->spinlock);val = drvdata->config.trig_filter_enable;spin_unlock(&drvdata->spinlock);return scnprintf(buf, PAGE_SIZE, "%ld (%s)\n", val,val ? "enabled" : "disabled");+}
+static ssize_t trig_filter_enable_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
unsigned long val;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);if (kstrtoul(buf, 16, &val))return -EINVAL;spin_lock(&drvdata->spinlock);drvdata->config.trig_filter_enable = val ? 1 : 0;spin_unlock(&drvdata->spinlock);return size;+} +static DEVICE_ATTR_RW(trig_filter_enable);
+/* clear all xtrigger / channel programming */ +static ssize_t reset_xtrigs_store(struct device *dev,
struct device_attribute *attr,const char *buf, size_t size)+{
int i;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);struct cti_config *config = &drvdata->config;spin_lock(&drvdata->spinlock);/* clear the CTI trigger / channel programming registers */for (i = 0; i < config->nr_trig_max; i++) {config->ctiinen[i] = 0;config->ctiouten[i] = 0;}/* clear the other regs */config->ctigate = (0x1 << config->nr_ctm_channels) - 1;config->asicctl = 0;config->ctiappset = 0;config->ctiinout_sel = 0;/* if enabled then write through */if (CTI_PWR_ENA(config))cti_write_all_hw_regs(drvdata);spin_unlock(&drvdata->spinlock);return size;+} +static DEVICE_ATTR_WO(reset_xtrigs);
+/* list attachments by channel */ +#define LX_ADJUST_BUF_PTRS(n) \
do { buf_pos += n; buf_size -= n; \if (buf_size <= 1) \goto lx_buf_full; \} while (0)+static ssize_t list_xtrigs_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
int used = 0, nr_channels, chan_idx, reg_idx;/* buffer vars */int buf_size = PAGE_SIZE;char *buf_pos = buf;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);struct cti_config *cfg = &drvdata->config;u32 chan_mask;nr_channels = cfg->nr_ctm_channels;used = scnprintf(buf_pos, buf_size, "CTI:%s; Channels:%d\n",dev_name(dev), nr_channels);LX_ADJUST_BUF_PTRS(used);/* show the IN and OUT triggers per channel */for (chan_idx = 0; chan_idx < nr_channels; chan_idx++) {chan_mask = 0x1 << chan_idx;used = scnprintf(buf_pos, buf_size, "Chan %d: IN [ ", chan_idx);LX_ADJUST_BUF_PTRS(used);for (reg_idx = 0;reg_idx < drvdata->config.nr_trig_max;reg_idx++) {if (chan_mask & cfg->ctiinen[reg_idx]) {used = scnprintf(buf_pos, buf_size, "%d ",reg_idx);LX_ADJUST_BUF_PTRS(used);}}used = scnprintf(buf_pos, buf_size, "]; OUT [ ");LX_ADJUST_BUF_PTRS(used);for (reg_idx = 0;reg_idx < drvdata->config.nr_trig_max;reg_idx++) {if (chan_mask & cfg->ctiouten[reg_idx]) {used = scnprintf(buf_pos, buf_size, "%d ",reg_idx);LX_ADJUST_BUF_PTRS(used);}}used = scnprintf(buf_pos, buf_size, "];\n");LX_ADJUST_BUF_PTRS(used);}/* show the channels enabled via ctigate */used = scnprintf(buf_pos, buf_size, "Gate Channels Enabled: [ ");LX_ADJUST_BUF_PTRS(used);if (cfg->ctigate == 0) {used = scnprintf(buf_pos, buf_size, "None ]\n");LX_ADJUST_BUF_PTRS(used);} else {for (chan_idx = 0; chan_idx < nr_channels; chan_idx++) {chan_mask = 0x1 << chan_idx;if (chan_mask & cfg->ctigate) {used = scnprintf(buf_pos, buf_size, "%d ",chan_idx);LX_ADJUST_BUF_PTRS(used);}}used = scnprintf(buf_pos, buf_size, "]\n");LX_ADJUST_BUF_PTRS(used);}+lx_buf_full:
used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;return used;+} +static DEVICE_ATTR_RO(list_xtrigs);
+#define LCIU_ADJUST_BUF_PTRS(n) \
do { buf_pos += n; buf_size -= n; \if (buf_size <= 1) \goto lciu_buf_full; \} while (0)+static ssize_t list_chan_inuse_show(struct device *dev,
struct device_attribute *attr,char *buf)+{
int used = 0, nr_channels, i;/* buffer vars */int buf_size = PAGE_SIZE;char *buf_pos = buf;struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);struct cti_config *config = &drvdata->config;u32 inuse_bits = 0, chan_mask, chan_bit_mask;nr_channels = config->nr_ctm_channels;spin_lock(&drvdata->spinlock);for (i = 0; i < config->nr_trig_max; i++) {inuse_bits |= config->ctiinen[i];inuse_bits |= config->ctiouten[i];}spin_unlock(&drvdata->spinlock);/* print in use channels */chan_mask = ((u32)(0x1 << nr_channels)) - 1;if (inuse_bits & chan_mask) {used = scnprintf(buf_pos, buf_size, "Chan in use [ ");LCIU_ADJUST_BUF_PTRS(used);for (i = 0; i < nr_channels; i++) {chan_bit_mask = 0x1 << i;if (chan_bit_mask & inuse_bits) {used = scnprintf(buf_pos, buf_size, "%d ", i);LCIU_ADJUST_BUF_PTRS(used);}}used = scnprintf(buf_pos, buf_size, "]\n");LCIU_ADJUST_BUF_PTRS(used);} else {used = scnprintf(buf_pos, buf_size, "Chan in use [ none ]\n");LCIU_ADJUST_BUF_PTRS(used);}/* print free channels */inuse_bits = ~inuse_bits; /* flip for free channels */if (inuse_bits & chan_mask) {used = scnprintf(buf_pos, buf_size, "Chan free [ ");LCIU_ADJUST_BUF_PTRS(used);for (i = 0; i < nr_channels; i++) {chan_bit_mask = 0x1 << i;if (chan_bit_mask & inuse_bits) {used = scnprintf(buf_pos, buf_size, "%d ", i);LCIU_ADJUST_BUF_PTRS(used);}}used = scnprintf(buf_pos, buf_size, "]\n");LCIU_ADJUST_BUF_PTRS(used);} else {used = scnprintf(buf_pos, buf_size, "Chan free [ none ]\n");LCIU_ADJUST_BUF_PTRS(used);}+lciu_buf_full:
used = buf_size > 0 ? PAGE_SIZE - buf_size : PAGE_SIZE;return used;+} +static DEVICE_ATTR_RO(list_chan_inuse);
+static struct attribute *coresight_cti_channel_attrs[] = {
&dev_attr_trigin_attach.attr,&dev_attr_trigin_detach.attr,&dev_attr_trigout_attach.attr,&dev_attr_trigout_detach.attr,&dev_attr_gate_enable.attr,&dev_attr_gate_disable.attr,&dev_attr_chan_set.attr,&dev_attr_chan_clear.attr,&dev_attr_chan_pulse.attr,&dev_attr_trig_filter_enable.attr,&dev_attr_list_xtrigs.attr,&dev_attr_reset_xtrigs.attr,&dev_attr_list_chan_inuse.attr,NULL,+};
+/* coresight management registers */ +coresight_cti_reg(devaff0, CTIDEVAFF0); +coresight_cti_reg(devaff1, CTIDEVAFF1); +coresight_cti_reg(authstatus, CORESIGHT_AUTHSTATUS); +coresight_cti_reg(devarch, CORESIGHT_DEVARCH); +coresight_cti_reg(devid, CORESIGHT_DEVID); +coresight_cti_reg(devtype, CORESIGHT_DEVTYPE); +coresight_cti_reg(pidr0, CORESIGHT_PERIPHIDR0); +coresight_cti_reg(pidr1, CORESIGHT_PERIPHIDR1); +coresight_cti_reg(pidr2, CORESIGHT_PERIPHIDR2); +coresight_cti_reg(pidr3, CORESIGHT_PERIPHIDR3); +coresight_cti_reg(pidr4, CORESIGHT_PERIPHIDR4);
+static struct attribute *coresight_cti_mgmt_attrs[] = {
&dev_attr_devaff0.attr,&dev_attr_devaff1.attr,&dev_attr_authstatus.attr,&dev_attr_devarch.attr,&dev_attr_devid.attr,&dev_attr_devtype.attr,&dev_attr_pidr0.attr,&dev_attr_pidr1.attr,&dev_attr_pidr2.attr,&dev_attr_pidr3.attr,&dev_attr_pidr4.attr,NULL,+};
+static const struct attribute_group coresight_cti_group = {
.attrs = coresight_cti_attrs,+};
+static const struct attribute_group coresight_cti_regs_group = {
.attrs = coresight_cti_regs_attrs,.name = "regs",+};
+static const struct attribute_group coresight_cti_channels_group = {
.attrs = coresight_cti_channel_attrs,.name = "channels",+};
+static const struct attribute_group coresight_cti_mgmt_group = {
.attrs = coresight_cti_mgmt_attrs,.name = "mgmt",+};
+const struct attribute_group *coresight_cti_groups[] = {
&coresight_cti_group,&coresight_cti_regs_group,&coresight_cti_channels_group,&coresight_cti_mgmt_group,NULL,+};
+static int cti_starting_cpu(unsigned int cpu) +{
struct cti_drvdata *drvdata = cti_cpu_drv[cpu];if (!drvdata)return 0;spin_lock(&drvdata->spinlock);drvdata->config.hw_powered = true;spin_unlock(&drvdata->spinlock);if (atomic_read(&drvdata->config.enable_req_count))cti_enable_hw(drvdata);return 0;+}
+static int cti_stopping_cpu(unsigned int cpu) +{
struct cti_drvdata *drvdata = cti_cpu_drv[cpu];if (!drvdata)return 0;cti_disable_hw(drvdata);spin_lock(&drvdata->spinlock);drvdata->config.hw_powered = false;spin_unlock(&drvdata->spinlock);return 0;+}
+static void cti_smp_set_powered(void *info) +{
struct cti_drvdata *drvdata = info;drvdata->config.hw_powered = true;+}
+/* setup power handling on a cti that has an association with a cpu */ +static int cti_setup_hp_cpu_affinity(struct cti_drvdata *drvdata) +{
int ret = 0;/* CPU affinity - add to quick lookup array* and register cpu hp callbacks first time out*/cpus_read_lock();cti_cpu_drv[drvdata->ctidev.cpu] = drvdata;if (!cti_cpu_count) {cti_hp_online = cpuhp_setup_state_nocalls_cpuslocked(CPUHP_AP_ONLINE_DYN, "arm/coresightcti:online",cti_starting_cpu, cti_stopping_cpu);if (cti_hp_online < 0) {ret = cti_hp_online;cti_cpu_drv[drvdata->ctidev.cpu] = 0;goto cti_hp_cpu_done;}}cti_cpu_count++;/* setup powered flag by running function of assoc cpu */if (smp_call_function_single(drvdata->ctidev.cpu, cti_smp_set_powered,drvdata, 1)) {drvdata->config.hw_powered = false;}+cti_hp_cpu_done:
cpus_read_unlock();return ret;+}
+/* back out hotplug notifications on error */ +static void cti_clear_hp_cpu_affinity(struct cti_drvdata *drvdata) +{
if (!drvdata)return;if (drvdata->ctidev.cpu >= 0) {cti_cpu_drv[drvdata->ctidev.cpu] = 0;cti_cpu_count--;if ((cti_cpu_count == 0) && (cti_hp_online > 0))cpuhp_remove_state_nocalls(cti_hp_online);}+}
+static int cti_probe(struct amba_device *adev, const struct amba_id *id) +{
int ret = 0;u32 devid;void __iomem *base;struct device *dev = &adev->dev;struct cti_drvdata *drvdata = 0;struct coresight_desc cti_desc;struct coresight_platform_data *pdata = NULL;struct resource *res = &adev->res;struct device_node *np = adev->dev.of_node;struct ect_node *ect_nd = 0;/* boilerplate code to set up the basics */dev_info(dev, "%s(ID = %x)\n", __func__, id->id);if (np) {pdata = of_get_coresight_ect_platform_data(dev, np);if (IS_ERR(pdata)) {dev_info(dev, "of_get_coresight_ect_platform err\n");ret = PTR_ERR(pdata);goto err_out;}dev->platform_data = pdata;}/* node to keep track of CTI net */ect_nd = kzalloc(sizeof(struct ect_node), GFP_KERNEL);if (!ect_nd) {ret = -ENOMEM;goto err_out;}/* driver data*/drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);if (!drvdata) {ret = -ENOMEM;dev_info(dev, "%s, mem err\n", __func__);goto err_out;If you go to err_out here ect_nd allocated above is leaked. The same applies to the reste of the 'goto err_out;' below.
}/* links between dev and drvdata*/drvdata->dev = dev;dev_set_drvdata(dev, drvdata);/* default CTI device info */drvdata->ctidev.cpu = -1;drvdata->ctidev.nr_trig_con = 0;drvdata->ctidev.ctm_id = 0;INIT_LIST_HEAD(&drvdata->ctidev.trig_cons);/* Validity for the resource is already checked by the AMBA core */base = devm_ioremap_resource(dev, res);if (IS_ERR(base)) {ret = PTR_ERR(base);dev_info(dev, "%s, remap err\n", __func__);goto err_out;}drvdata->base = base;spin_lock_init(&drvdata->spinlock);/* look at the HW DEVID register for some of the HW settings */devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);drvdata->config.nr_trig_max = (int)((devid & 0xFF00) >> 8);/* no current hardware should exceed this, but protect the driver* in case of fault / out of spec hw*/if (drvdata->config.nr_trig_max > CTIINOUTEN_MAX) {dev_warn_once(dev,"Limiting HW MaxTrig value(%d) to driver max(%d)\n",drvdata->config.nr_trig_max, CTIINOUTEN_MAX);drvdata->config.nr_trig_max = CTIINOUTEN_MAX;}drvdata->config.nr_ctm_channels = (int)((devid & 0xF0000) >> 16);dev_info(dev, "DevID %08x, trigs=%d, chans=%d\n", devid,drvdata->config.nr_trig_max, drvdata->config.nr_ctm_channels);/** additional parse the .dts for connections and signals */of_cti_get_hw_data(dev, np, drvdata);cti_set_default_config(&drvdata->config);/* setup cpu related CTI devices, otherwise assume powered */if (drvdata->ctidev.cpu >= 0) {ret = cti_setup_hp_cpu_affinity(drvdata);if (ret < 0)goto err_out;} elsedrvdata->config.hw_powered = true;/* set up coresight component description */cti_desc.pdata = pdata;cti_desc.type = CORESIGHT_DEV_TYPE_ECT;cti_desc.subtype.ect_subtype = CORESIGHT_DEV_SUBTYPE_ECT_CTI;cti_desc.ops = &cti_ops;cti_desc.groups = coresight_cti_groups;cti_desc.dev = dev;drvdata->csdev = coresight_register(&cti_desc);if (IS_ERR(drvdata->csdev)) {ret = PTR_ERR(drvdata->csdev);pr_err("%s: CS register failed\n", pdata->name);goto err_out;}/* add to list of CTI devices */mutex_lock(&ect_mutex);ect_nd->cti_drv = drvdata;list_add(&ect_nd->next, &ect_net);mutex_unlock(&ect_mutex);/* set any cross references */cti_update_conn_xrefs(drvdata);/* all done - dec pm refcount */pm_runtime_put(&adev->dev);dev_info(dev, "%s: initialized\n", pdata->name);cti_count++;dev_info(dev, "%s - ok\n", __func__);return 0;+err_out:
cti_clear_hp_cpu_affinity(drvdata);dev_info(dev, "%s - err_out\n", __func__);return ret;+}
+/* free up CTI specific resources
- called from coresight_device_release on coresight_unregister.
- */
+void cti_device_release(struct device *dev) +{
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);struct ect_node *ect_item, *ect_tmp;struct cti_trig_con *tc, *tc_tmp;/* free up resources associated with the cti connections */if (drvdata->ctidev.cpu >= 0)cti_cpu_drv[drvdata->ctidev.cpu] = 0;list_for_each_entry_safe(tc, tc_tmp,&drvdata->ctidev.trig_cons, node) {kfree(tc->con_in);kfree(tc->con_out);kfree(tc->con_dev_name);list_del(&tc->node);kfree(tc);}/* remove from the list */mutex_lock(&ect_mutex);list_for_each_entry_safe(ect_item, ect_tmp, &ect_net, next) {if (ect_item->cti_drv == drvdata) {list_del(&ect_item->next);kfree(ect_item);goto ect_list_item_removed;}}+ect_list_item_removed:
mutex_unlock(&ect_mutex);kfree(drvdata);+} +EXPORT_SYMBOL_GPL(cti_device_release);
+#define CTI_AMBA_ID(pid) \
{ \.id = pid, \.mask = 0x000fffff, \}+static struct amba_cs_uci_id uci_id_cti[] = {
{/* CTI UCI data */.devarch = 0x47701a14, /* devarch value for CTI v2 */.devarch_mask = 0xfff0ffff,.devtype = 0x00000014, /* maj(0x4-debug) min(0x1-ECT) */}+};
+#define CTI_AMBA_UCI_ID(pid) \
{ \.id = pid, \.mask = 0x000fffff, \.data = uci_id_cti, \}+static const struct amba_id cti_ids[] = {
CTI_AMBA_ID(0x000bb906), /* Coresight CTI (SoC 400), C-A72, C-A57 */CTI_AMBA_ID(0x000bb922), /* CTI - C-A8 */CTI_AMBA_ID(0x000bb9a8), /* CTI - C-A53 */CTI_AMBA_ID(0x000bb9aa), /* CTI - C-A73 */CTI_AMBA_UCI_ID(0x000bb9da), /* CTI - C-A35 */CTI_AMBA_UCI_ID(0x000bb9ed), /* Coresight CTI (SoC 600) */CTI_AMBA_UCI_ID(0x000bb95d), /* test UCI - ETM PID A53 */{ 0, 0},+};
+static struct amba_driver cti_driver = {
.drv = {.name = "coresight-cti",.owner = THIS_MODULE,.suppress_bind_attrs = true,},.probe = cti_probe,.id_table = cti_ids,+}; +builtin_amba_driver(cti_driver); diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h new file mode 100644 index 000000000000..09cb985ac0ea --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (c) 2018 Linaro Limited, All rights reserved.
- Author: Mike Leach mike.leach@linaro.org
- */
+#ifndef _CORESIGHT_CORESIGHT_CTI_H +#define _CORESIGHT_CORESIGHT_CTI_H
+#include <asm/local.h> +#include <linux/spinlock.h> +#include "coresight-priv.h"
+/*
- Device registers
- 0x000 - 0x144: CTI programming and status
- 0xEDC - 0xEF8: CTI integration test.
- 0xF00 - 0xFFC: Coresight management registers.
- */
+/* CTI programming registers */ +#define CTICONTROL 0x000 +#define CTIINTACK 0x010 +#define CTIAPPSET 0x014 +#define CTIAPPCLEAR 0x018 +#define CTIAPPPULSE 0x01C +#define CTIINEN(n) (0x020 + (4 * n)) +#define CTIOUTEN(n) (0x0A0 + (4 * n)) +#define CTITRIGINSTATUS 0x130 +#define CTITRIGOUTSTATUS 0x134 +#define CTICHINSTATUS 0x138 +#define CTICHOUTSTATUS 0x13C +#define CTIGATE 0x140 +#define ASICCTL 0x144 +/* Integration test registers */ +#define ITCHINACK 0xEDC /* WO CTI CSSoc 400 only*/ +#define ITTRIGINACK 0xEE0 /* WO CTI CSSoc 400 only*/ +#define ITCHOUT 0xEE4 /* WO RW-600 */ +#define ITTRIGOUT 0xEE8 /* WO RW-600 */ +#define ITCHOUTACK 0xEEC /* RO CTI CSSoc 400 only*/ +#define ITTRIGOUTACK 0xEF0 /* RO CTI CSSoc 400 only*/ +#define ITCHIN 0xEF4 /* RO */ +#define ITTRIGIN 0xEF8 /* RO */ +/* management registers */ +#define CTIDEVAFF0 0xFA8 +#define CTIDEVAFF1 0xFAC
+/* CTI CSSoc 600 has a max of 32 trigger signals per direction.
- CTI CSSoc 400 has 8 IO triggers - other CTIs can be impl def.
- Max of in and out defined in the DEVID register.
- pick up actual number used from .dts parameters if present.
- */
+#define CTIINOUTEN_MAX 32
+/**
- Group of related trigger signals
- @nr_sigs: number of signals in the group.
- @used_mask: bitmask representing the signal indexes in the group.
- @sig_names: list of names for the signals.
- */
+struct cti_trig_grp {
int nr_sigs;u32 used_mask;char *sig_names;+};
+/**
- Trigger connection - connection between a CTI and other (coresight) device
- lists input and output trigger signals for the device
- @con_in: connected CTIIN signals for the device.
- @con_out: connected CTIOUT signals for the device.
- @con_dev: coresight device connected to the CTI, NULL if not CS device
- @con_dev_name: name of connected device (CS or CPU)
- @node: entry node in list of connections.
- */
+struct cti_trig_con {
struct cti_trig_grp *con_in;struct cti_trig_grp *con_out;struct coresight_device *con_dev;char *con_dev_name;struct list_head node;+};
+/**
- struct cti_device - description of CTI device properties.
- @nt_trig_con: Number of external devices connected to this device.
- @ctm_id: which CTM this device is connected to (by default it is
assumed there is a single CTM per SoC, ID 0).
- @trig_cons: list of connections to this device.
- @cpu: CPU ID if associated with CPU, -1 otherwise.
- */
+struct cti_device {
int nr_trig_con;u32 ctm_id;struct list_head trig_cons;int cpu;+};
+/**
- struct cti_config - configuration of the CTI device hardware
- hardware description from RO ID regs
- @nr_trig_max: Max number of trigger signals implemented on device.
(max of trig_in or trig_out)
- @nr_ctm_channels: number of available CTM channels
- cti enable control
- @enable_req_count: CTI is enabled alongside >=1 associated devices.
- @hw_enabled: true if hw is currently enabled.
- @hw_powered: true if associated cpu powered on, or no cpu.
- registered triggers and filtering
- @trig_in_use: bitfield of in triggers registered as in use.
- @trig_out_use: bitfield of out triggers registered as in use.
- @trig_out_filter: bitfield of out triggers that are blocked if filter
enabled. Typically this would be dbgreq / restart on a core CTI.
- @trig_filter_enable: 1 if filtering enabled.
- cti software programmable regs:
- @ctiappset: CTI Software application channel set.
- @ctiinout_sel: register selector for INEN and OUTEN regs.
- @ctiinen: enable input trigger to a channel.
- @ctiouten: enable output trigger from a channel.
- @ctigate: gate channel output from CTI to CTM.
- */
+struct cti_config {
/* hardware description */int nr_ctm_channels;int nr_trig_max;/* cti enable control */atomic_t enable_req_count;bool hw_enabled;bool hw_powered;/* registered triggers and filtering */u32 trig_in_use;u32 trig_out_use;u32 trig_out_filter;int trig_filter_enable;/* cti cross trig programmable regs */u32 ctiappset;u8 ctiinout_sel;u32 ctiinen[CTIINOUTEN_MAX];u32 ctiouten[CTIINOUTEN_MAX];u32 ctigate;u32 asicctl;+};
+/**
- struct cti_drvdata - specifics for the CTI device
- @base: Memory mapped base address for this component.
- @dev: The device entity associated to this component.
- @csdev: Standard CoreSight device information.
- @ctidev: Extra information needed by the CTI/CTM framework.
- @spinlock: Control data access to one at a time.
- @config: Configuration data for this CTI device.
- */
+struct cti_drvdata {
void __iomem *base;struct device *dev;struct coresight_device *csdev;struct cti_device ctidev;spinlock_t spinlock;struct cti_config config;+};
+enum cti_chan_op {
CTI_CHAN_ATTACH,CTI_CHAN_DETACH,+};
+enum cti_trig_dir {
CTI_TRIG_IN,CTI_TRIG_OUT,+};
+enum cti_chan_gate_op {
CTI_GATE_CHAN_ENABLE,CTI_GATE_CHAN_DISABLE,CTI_GATE_CHAN_ENABLE_ALL,CTI_GATE_CHAN_DISABLE_ALL,+};
+enum cti_chan_set_op {
CTI_CHAN_SET,CTI_CHAN_CLR,CTI_CHAN_PULSE,+};
+#endif /* _CORESIGHT_CORESIGHT_CTI_H */
2.19.1
CoreSight mailing list CoreSight@lists.linaro.org https://lists.linaro.org/mailman/listinfo/coresight
-- Mike Leach Principal Engineer, ARM Ltd. Manchester Design Centre. UK