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_trace_id_map *id_map) 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_sink_enable(struct coresight_device *csdev, enum cs_mode mode, 45 void *data) 46 { 47 dev_dbg(csdev->dev.parent, "Dummy sink enabled\n"); 48 49 return 0; 50 } 51 52 static int dummy_sink_disable(struct coresight_device *csdev) 53 { 54 dev_dbg(csdev->dev.parent, "Dummy sink disabled\n"); 55 56 return 0; 57 } 58 59 static const struct coresight_ops_source dummy_source_ops = { 60 .enable = dummy_source_enable, 61 .disable = dummy_source_disable, 62 }; 63 64 static const struct coresight_ops dummy_source_cs_ops = { 65 .source_ops = &dummy_source_ops, 66 }; 67 68 static const struct coresight_ops_sink dummy_sink_ops = { 69 .enable = dummy_sink_enable, 70 .disable = dummy_sink_disable, 71 }; 72 73 static const struct coresight_ops dummy_sink_cs_ops = { 74 .sink_ops = &dummy_sink_ops, 75 }; 76 77 /* User can get the trace id of dummy source from this node. */ 78 static ssize_t traceid_show(struct device *dev, 79 struct device_attribute *attr, char *buf) 80 { 81 unsigned long val; 82 struct dummy_drvdata *drvdata = dev_get_drvdata(dev->parent); 83 84 val = drvdata->traceid; 85 return sysfs_emit(buf, "%#lx\n", val); 86 } 87 static DEVICE_ATTR_RO(traceid); 88 89 static struct attribute *coresight_dummy_attrs[] = { 90 &dev_attr_traceid.attr, 91 NULL, 92 }; 93 94 static const struct attribute_group coresight_dummy_group = { 95 .attrs = coresight_dummy_attrs, 96 }; 97 98 static const struct attribute_group *coresight_dummy_groups[] = { 99 &coresight_dummy_group, 100 NULL, 101 }; 102 103 static int dummy_probe(struct platform_device *pdev) 104 { 105 struct device *dev = &pdev->dev; 106 struct device_node *node = dev->of_node; 107 struct coresight_platform_data *pdata; 108 struct dummy_drvdata *drvdata; 109 struct coresight_desc desc = { 0 }; 110 int ret = 0, trace_id = 0; 111 112 drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); 113 if (!drvdata) 114 return -ENOMEM; 115 116 if (of_device_is_compatible(node, "arm,coresight-dummy-source")) { 117 118 desc.name = coresight_alloc_device_name(&source_devs, dev); 119 if (!desc.name) 120 return -ENOMEM; 121 122 desc.type = CORESIGHT_DEV_TYPE_SOURCE; 123 desc.subtype.source_subtype = 124 CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS; 125 desc.ops = &dummy_source_cs_ops; 126 desc.groups = coresight_dummy_groups; 127 128 ret = coresight_get_static_trace_id(dev, &trace_id); 129 if (!ret) { 130 /* Get the static id if id is set in device tree. */ 131 ret = coresight_trace_id_get_static_system_id(trace_id); 132 if (ret < 0) { 133 dev_err(dev, "Fail to get static id.\n"); 134 return ret; 135 } 136 } else { 137 /* Get next available id if id is not set in device tree. */ 138 trace_id = coresight_trace_id_get_system_id(); 139 if (trace_id < 0) { 140 ret = trace_id; 141 return ret; 142 } 143 } 144 drvdata->traceid = (u8)trace_id; 145 146 } else if (of_device_is_compatible(node, "arm,coresight-dummy-sink")) { 147 desc.name = coresight_alloc_device_name(&sink_devs, dev); 148 if (!desc.name) 149 return -ENOMEM; 150 151 desc.type = CORESIGHT_DEV_TYPE_SINK; 152 desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_DUMMY; 153 desc.ops = &dummy_sink_cs_ops; 154 } else { 155 dev_err(dev, "Device type not set\n"); 156 return -EINVAL; 157 } 158 159 pdata = coresight_get_platform_data(dev); 160 if (IS_ERR(pdata)) { 161 ret = PTR_ERR(pdata); 162 goto free_id; 163 } 164 pdev->dev.platform_data = pdata; 165 166 drvdata->dev = &pdev->dev; 167 platform_set_drvdata(pdev, drvdata); 168 169 desc.pdata = pdev->dev.platform_data; 170 desc.dev = &pdev->dev; 171 drvdata->csdev = coresight_register(&desc); 172 if (IS_ERR(drvdata->csdev)) { 173 ret = PTR_ERR(drvdata->csdev); 174 goto free_id; 175 } 176 177 pm_runtime_enable(dev); 178 dev_dbg(dev, "Dummy device initialized\n"); 179 180 ret = 0; 181 goto out; 182 183 free_id: 184 if (IS_VALID_CS_TRACE_ID(drvdata->traceid)) 185 coresight_trace_id_put_system_id(drvdata->traceid); 186 187 out: 188 return ret; 189 } 190 191 static void dummy_remove(struct platform_device *pdev) 192 { 193 struct dummy_drvdata *drvdata = platform_get_drvdata(pdev); 194 struct device *dev = &pdev->dev; 195 196 if (IS_VALID_CS_TRACE_ID(drvdata->traceid)) 197 coresight_trace_id_put_system_id(drvdata->traceid); 198 pm_runtime_disable(dev); 199 coresight_unregister(drvdata->csdev); 200 } 201 202 static const struct of_device_id dummy_match[] = { 203 {.compatible = "arm,coresight-dummy-source"}, 204 {.compatible = "arm,coresight-dummy-sink"}, 205 {}, 206 }; 207 208 static struct platform_driver dummy_driver = { 209 .probe = dummy_probe, 210 .remove = dummy_remove, 211 .driver = { 212 .name = "coresight-dummy", 213 .of_match_table = dummy_match, 214 }, 215 }; 216 217 module_platform_driver(dummy_driver); 218 219 MODULE_LICENSE("GPL"); 220 MODULE_DESCRIPTION("CoreSight dummy driver"); 221