1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. 4 */ 5 6 #include <linux/amba/bus.h> 7 #include <linux/coresight.h> 8 #include <linux/device.h> 9 #include <linux/io.h> 10 #include <linux/kernel.h> 11 #include <linux/module.h> 12 #include <linux/of.h> 13 #include <linux/platform_device.h> 14 15 #include "coresight-priv.h" 16 #include "coresight-trace-id.h" 17 18 #define TRACE_NOC_CTRL 0x008 19 #define TRACE_NOC_XLD 0x010 20 #define TRACE_NOC_FREQVAL 0x018 21 #define TRACE_NOC_SYNCR 0x020 22 23 /* Enable generation of output ATB traffic.*/ 24 #define TRACE_NOC_CTRL_PORTEN BIT(0) 25 /* Sets the type of issued ATB FLAG packets.*/ 26 #define TRACE_NOC_CTRL_FLAGTYPE BIT(7) 27 /* Sets the type of issued ATB FREQ packet*/ 28 #define TRACE_NOC_CTRL_FREQTYPE BIT(8) 29 30 #define TRACE_NOC_SYNC_INTERVAL 0xFFFF 31 32 /* 33 * struct trace_noc_drvdata - specifics associated to a trace noc component 34 * @base: memory mapped base address for this component. 35 * @dev: device node for trace_noc_drvdata. 36 * @csdev: component vitals needed by the framework. 37 * @spinlock: serialize enable/disable operation. 38 * @atid: id for the trace packet. 39 */ 40 struct trace_noc_drvdata { 41 void __iomem *base; 42 struct device *dev; 43 struct coresight_device *csdev; 44 spinlock_t spinlock; 45 u32 atid; 46 }; 47 48 DEFINE_CORESIGHT_DEVLIST(trace_noc_devs, "traceNoc"); 49 50 static void trace_noc_enable_hw(struct trace_noc_drvdata *drvdata) 51 { 52 u32 val; 53 54 /* Set ATID */ 55 writel_relaxed(drvdata->atid, drvdata->base + TRACE_NOC_XLD); 56 57 /* Set the data word count between 'SYNC' packets */ 58 writel_relaxed(TRACE_NOC_SYNC_INTERVAL, drvdata->base + TRACE_NOC_SYNCR); 59 60 /* Set the Control register: 61 * - Set the FLAG packets to 'FLAG' packets 62 * - Set the FREQ packets to 'FREQ_TS' packets 63 * - Enable generation of output ATB traffic 64 */ 65 66 val = readl_relaxed(drvdata->base + TRACE_NOC_CTRL); 67 68 val &= ~TRACE_NOC_CTRL_FLAGTYPE; 69 val |= TRACE_NOC_CTRL_FREQTYPE; 70 val |= TRACE_NOC_CTRL_PORTEN; 71 72 writel(val, drvdata->base + TRACE_NOC_CTRL); 73 } 74 75 static int trace_noc_enable(struct coresight_device *csdev, struct coresight_connection *inport, 76 struct coresight_connection *outport) 77 { 78 struct trace_noc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); 79 80 scoped_guard(spinlock, &drvdata->spinlock) { 81 if (csdev->refcnt == 0) 82 trace_noc_enable_hw(drvdata); 83 84 csdev->refcnt++; 85 } 86 87 dev_dbg(drvdata->dev, "Trace NOC is enabled\n"); 88 return 0; 89 } 90 91 static void trace_noc_disable(struct coresight_device *csdev, struct coresight_connection *inport, 92 struct coresight_connection *outport) 93 { 94 struct trace_noc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); 95 96 scoped_guard(spinlock, &drvdata->spinlock) { 97 if (--csdev->refcnt == 0) 98 writel(0x0, drvdata->base + TRACE_NOC_CTRL); 99 } 100 dev_dbg(drvdata->dev, "Trace NOC is disabled\n"); 101 } 102 103 static int trace_noc_id(struct coresight_device *csdev, __maybe_unused enum cs_mode mode, 104 __maybe_unused struct coresight_device *sink) 105 { 106 struct trace_noc_drvdata *drvdata; 107 108 drvdata = dev_get_drvdata(csdev->dev.parent); 109 110 return drvdata->atid; 111 } 112 113 static const struct coresight_ops_link trace_noc_link_ops = { 114 .enable = trace_noc_enable, 115 .disable = trace_noc_disable, 116 }; 117 118 static const struct coresight_ops trace_noc_cs_ops = { 119 .trace_id = trace_noc_id, 120 .link_ops = &trace_noc_link_ops, 121 }; 122 123 static int trace_noc_init_default_data(struct trace_noc_drvdata *drvdata) 124 { 125 int atid; 126 127 atid = coresight_trace_id_get_system_id(); 128 if (atid < 0) 129 return atid; 130 131 drvdata->atid = atid; 132 133 return 0; 134 } 135 136 static ssize_t traceid_show(struct device *dev, 137 struct device_attribute *attr, char *buf) 138 { 139 unsigned long val; 140 struct trace_noc_drvdata *drvdata = dev_get_drvdata(dev->parent); 141 142 val = drvdata->atid; 143 return sprintf(buf, "%#lx\n", val); 144 } 145 static DEVICE_ATTR_RO(traceid); 146 147 static struct attribute *coresight_tnoc_attrs[] = { 148 &dev_attr_traceid.attr, 149 NULL, 150 }; 151 152 static const struct attribute_group coresight_tnoc_group = { 153 .attrs = coresight_tnoc_attrs, 154 }; 155 156 static const struct attribute_group *coresight_tnoc_groups[] = { 157 &coresight_tnoc_group, 158 NULL, 159 }; 160 161 static int trace_noc_probe(struct amba_device *adev, const struct amba_id *id) 162 { 163 struct device *dev = &adev->dev; 164 struct coresight_platform_data *pdata; 165 struct trace_noc_drvdata *drvdata; 166 struct coresight_desc desc = { 0 }; 167 int ret; 168 169 desc.name = coresight_alloc_device_name(&trace_noc_devs, dev); 170 if (!desc.name) 171 return -ENOMEM; 172 173 pdata = coresight_get_platform_data(dev); 174 if (IS_ERR(pdata)) 175 return PTR_ERR(pdata); 176 adev->dev.platform_data = pdata; 177 178 drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); 179 if (!drvdata) 180 return -ENOMEM; 181 182 drvdata->dev = &adev->dev; 183 dev_set_drvdata(dev, drvdata); 184 185 drvdata->base = devm_ioremap_resource(dev, &adev->res); 186 if (IS_ERR(drvdata->base)) 187 return PTR_ERR(drvdata->base); 188 189 spin_lock_init(&drvdata->spinlock); 190 191 ret = trace_noc_init_default_data(drvdata); 192 if (ret) 193 return ret; 194 195 desc.ops = &trace_noc_cs_ops; 196 desc.type = CORESIGHT_DEV_TYPE_LINK; 197 desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG; 198 desc.pdata = adev->dev.platform_data; 199 desc.dev = &adev->dev; 200 desc.access = CSDEV_ACCESS_IOMEM(drvdata->base); 201 desc.groups = coresight_tnoc_groups; 202 drvdata->csdev = coresight_register(&desc); 203 if (IS_ERR(drvdata->csdev)) { 204 coresight_trace_id_put_system_id(drvdata->atid); 205 return PTR_ERR(drvdata->csdev); 206 } 207 pm_runtime_put(&adev->dev); 208 209 return 0; 210 } 211 212 static void trace_noc_remove(struct amba_device *adev) 213 { 214 struct trace_noc_drvdata *drvdata = dev_get_drvdata(&adev->dev); 215 216 coresight_unregister(drvdata->csdev); 217 coresight_trace_id_put_system_id(drvdata->atid); 218 } 219 220 static struct amba_id trace_noc_ids[] = { 221 { 222 .id = 0x000f0c00, 223 .mask = 0x00ffff00, 224 }, 225 { 226 .id = 0x001f0c00, 227 .mask = 0x00ffff00, 228 }, 229 {}, 230 }; 231 MODULE_DEVICE_TABLE(amba, trace_noc_ids); 232 233 static struct amba_driver trace_noc_driver = { 234 .drv = { 235 .name = "coresight-trace-noc", 236 .suppress_bind_attrs = true, 237 }, 238 .probe = trace_noc_probe, 239 .remove = trace_noc_remove, 240 .id_table = trace_noc_ids, 241 }; 242 243 module_amba_driver(trace_noc_driver); 244 245 MODULE_LICENSE("GPL"); 246 MODULE_DESCRIPTION("Trace NOC driver"); 247