1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. 4 */ 5 6 #include <linux/coresight.h> 7 #include <linux/kernel.h> 8 #include <linux/module.h> 9 #include <linux/of.h> 10 #include <linux/platform_device.h> 11 #include <linux/pm_runtime.h> 12 13 #include "coresight-priv.h" 14 #include "coresight-trace-id.h" 15 16 struct dummy_drvdata { 17 struct device *dev; 18 struct coresight_device *csdev; 19 u8 traceid; 20 }; 21 22 DEFINE_CORESIGHT_DEVLIST(source_devs, "dummy_source"); 23 DEFINE_CORESIGHT_DEVLIST(sink_devs, "dummy_sink"); 24 25 static int dummy_source_enable(struct coresight_device *csdev, 26 struct perf_event *event, enum cs_mode mode, 27 __maybe_unused struct coresight_path *path) 28 { 29 if (!coresight_take_mode(csdev, mode)) 30 return -EBUSY; 31 32 dev_dbg(csdev->dev.parent, "Dummy source enabled\n"); 33 34 return 0; 35 } 36 37 static void dummy_source_disable(struct coresight_device *csdev, 38 struct perf_event *event) 39 { 40 coresight_set_mode(csdev, CS_MODE_DISABLED); 41 dev_dbg(csdev->dev.parent, "Dummy source disabled\n"); 42 } 43 44 static int dummy_source_trace_id(struct coresight_device *csdev, __maybe_unused enum cs_mode mode, 45 __maybe_unused struct coresight_device *sink) 46 { 47 struct dummy_drvdata *drvdata; 48 49 drvdata = dev_get_drvdata(csdev->dev.parent); 50 51 return drvdata->traceid; 52 } 53 54 static int dummy_sink_enable(struct coresight_device *csdev, enum cs_mode mode, 55 void *data) 56 { 57 dev_dbg(csdev->dev.parent, "Dummy sink enabled\n"); 58 59 return 0; 60 } 61 62 static int dummy_sink_disable(struct coresight_device *csdev) 63 { 64 dev_dbg(csdev->dev.parent, "Dummy sink disabled\n"); 65 66 return 0; 67 } 68 69 static const struct coresight_ops_source dummy_source_ops = { 70 .enable = dummy_source_enable, 71 .disable = dummy_source_disable, 72 }; 73 74 static const struct coresight_ops dummy_source_cs_ops = { 75 .trace_id = dummy_source_trace_id, 76 .source_ops = &dummy_source_ops, 77 }; 78 79 static const struct coresight_ops_sink dummy_sink_ops = { 80 .enable = dummy_sink_enable, 81 .disable = dummy_sink_disable, 82 }; 83 84 static const struct coresight_ops dummy_sink_cs_ops = { 85 .sink_ops = &dummy_sink_ops, 86 }; 87 88 /* User can get the trace id of dummy source from this node. */ 89 static ssize_t traceid_show(struct device *dev, 90 struct device_attribute *attr, char *buf) 91 { 92 unsigned long val; 93 struct dummy_drvdata *drvdata = dev_get_drvdata(dev->parent); 94 95 val = drvdata->traceid; 96 return sysfs_emit(buf, "%#lx\n", val); 97 } 98 static DEVICE_ATTR_RO(traceid); 99 100 static struct attribute *coresight_dummy_attrs[] = { 101 &dev_attr_traceid.attr, 102 NULL, 103 }; 104 105 static const struct attribute_group coresight_dummy_group = { 106 .attrs = coresight_dummy_attrs, 107 }; 108 109 static const struct attribute_group *coresight_dummy_groups[] = { 110 &coresight_dummy_group, 111 NULL, 112 }; 113 114 static int dummy_probe(struct platform_device *pdev) 115 { 116 struct device *dev = &pdev->dev; 117 struct device_node *node = dev->of_node; 118 struct coresight_platform_data *pdata; 119 struct dummy_drvdata *drvdata; 120 struct coresight_desc desc = { 0 }; 121 int ret = 0, trace_id = 0; 122 123 drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); 124 if (!drvdata) 125 return -ENOMEM; 126 127 if (of_device_is_compatible(node, "arm,coresight-dummy-source")) { 128 129 desc.name = coresight_alloc_device_name(&source_devs, dev); 130 if (!desc.name) 131 return -ENOMEM; 132 133 desc.type = CORESIGHT_DEV_TYPE_SOURCE; 134 desc.subtype.source_subtype = 135 CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS; 136 desc.ops = &dummy_source_cs_ops; 137 desc.groups = coresight_dummy_groups; 138 139 ret = coresight_get_static_trace_id(dev, &trace_id); 140 if (!ret) { 141 /* Get the static id if id is set in device tree. */ 142 ret = coresight_trace_id_get_static_system_id(trace_id); 143 if (ret < 0) { 144 dev_err(dev, "Fail to get static id.\n"); 145 return ret; 146 } 147 } else { 148 /* Get next available id if id is not set in device tree. */ 149 trace_id = coresight_trace_id_get_system_id(); 150 if (trace_id < 0) { 151 ret = trace_id; 152 return ret; 153 } 154 } 155 drvdata->traceid = (u8)trace_id; 156 157 } else if (of_device_is_compatible(node, "arm,coresight-dummy-sink")) { 158 desc.name = coresight_alloc_device_name(&sink_devs, dev); 159 if (!desc.name) 160 return -ENOMEM; 161 162 desc.type = CORESIGHT_DEV_TYPE_SINK; 163 desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_DUMMY; 164 desc.ops = &dummy_sink_cs_ops; 165 } else { 166 dev_err(dev, "Device type not set\n"); 167 return -EINVAL; 168 } 169 170 pdata = coresight_get_platform_data(dev); 171 if (IS_ERR(pdata)) { 172 ret = PTR_ERR(pdata); 173 goto free_id; 174 } 175 pdev->dev.platform_data = pdata; 176 177 drvdata->dev = &pdev->dev; 178 platform_set_drvdata(pdev, drvdata); 179 180 desc.pdata = pdev->dev.platform_data; 181 desc.dev = &pdev->dev; 182 drvdata->csdev = coresight_register(&desc); 183 if (IS_ERR(drvdata->csdev)) { 184 ret = PTR_ERR(drvdata->csdev); 185 goto free_id; 186 } 187 188 pm_runtime_enable(dev); 189 dev_dbg(dev, "Dummy device initialized\n"); 190 191 ret = 0; 192 goto out; 193 194 free_id: 195 if (IS_VALID_CS_TRACE_ID(drvdata->traceid)) 196 coresight_trace_id_put_system_id(drvdata->traceid); 197 198 out: 199 return ret; 200 } 201 202 static void dummy_remove(struct platform_device *pdev) 203 { 204 struct dummy_drvdata *drvdata = platform_get_drvdata(pdev); 205 struct device *dev = &pdev->dev; 206 207 if (IS_VALID_CS_TRACE_ID(drvdata->traceid)) 208 coresight_trace_id_put_system_id(drvdata->traceid); 209 pm_runtime_disable(dev); 210 coresight_unregister(drvdata->csdev); 211 } 212 213 static const struct of_device_id dummy_match[] = { 214 {.compatible = "arm,coresight-dummy-source"}, 215 {.compatible = "arm,coresight-dummy-sink"}, 216 {}, 217 }; 218 219 static struct platform_driver dummy_driver = { 220 .probe = dummy_probe, 221 .remove = dummy_remove, 222 .driver = { 223 .name = "coresight-dummy", 224 .of_match_table = dummy_match, 225 }, 226 }; 227 228 module_platform_driver(dummy_driver); 229 230 MODULE_LICENSE("GPL"); 231 MODULE_DESCRIPTION("CoreSight dummy driver"); 232