1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. 4 */ 5 6 #include <linux/amba/bus.h> 7 #include <linux/bitmap.h> 8 #include <linux/coresight.h> 9 #include <linux/coresight-pmu.h> 10 #include <linux/device.h> 11 #include <linux/err.h> 12 #include <linux/fs.h> 13 #include <linux/io.h> 14 #include <linux/kernel.h> 15 #include <linux/module.h> 16 #include <linux/of.h> 17 18 #include "coresight-priv.h" 19 #include "coresight-tpdm.h" 20 21 DEFINE_CORESIGHT_DEVLIST(tpdm_devs, "tpdm"); 22 23 static void tpdm_enable_dsb(struct tpdm_drvdata *drvdata) 24 { 25 u32 val; 26 27 /* Set the enable bit of DSB control register to 1 */ 28 val = readl_relaxed(drvdata->base + TPDM_DSB_CR); 29 val |= TPDM_DSB_CR_ENA; 30 writel_relaxed(val, drvdata->base + TPDM_DSB_CR); 31 } 32 33 /* TPDM enable operations */ 34 static void __tpdm_enable(struct tpdm_drvdata *drvdata) 35 { 36 CS_UNLOCK(drvdata->base); 37 38 /* Check if DSB datasets is present for TPDM. */ 39 if (drvdata->datasets & TPDM_PIDR0_DS_DSB) 40 tpdm_enable_dsb(drvdata); 41 42 CS_LOCK(drvdata->base); 43 } 44 45 static int tpdm_enable(struct coresight_device *csdev, struct perf_event *event, 46 enum cs_mode mode) 47 { 48 struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); 49 50 spin_lock(&drvdata->spinlock); 51 if (drvdata->enable) { 52 spin_unlock(&drvdata->spinlock); 53 return -EBUSY; 54 } 55 56 __tpdm_enable(drvdata); 57 drvdata->enable = true; 58 spin_unlock(&drvdata->spinlock); 59 60 dev_dbg(drvdata->dev, "TPDM tracing enabled\n"); 61 return 0; 62 } 63 64 static void tpdm_disable_dsb(struct tpdm_drvdata *drvdata) 65 { 66 u32 val; 67 68 /* Set the enable bit of DSB control register to 0 */ 69 val = readl_relaxed(drvdata->base + TPDM_DSB_CR); 70 val &= ~TPDM_DSB_CR_ENA; 71 writel_relaxed(val, drvdata->base + TPDM_DSB_CR); 72 } 73 74 /* TPDM disable operations */ 75 static void __tpdm_disable(struct tpdm_drvdata *drvdata) 76 { 77 CS_UNLOCK(drvdata->base); 78 79 /* Check if DSB datasets is present for TPDM. */ 80 if (drvdata->datasets & TPDM_PIDR0_DS_DSB) 81 tpdm_disable_dsb(drvdata); 82 83 CS_LOCK(drvdata->base); 84 } 85 86 static void tpdm_disable(struct coresight_device *csdev, 87 struct perf_event *event) 88 { 89 struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); 90 91 spin_lock(&drvdata->spinlock); 92 if (!drvdata->enable) { 93 spin_unlock(&drvdata->spinlock); 94 return; 95 } 96 97 __tpdm_disable(drvdata); 98 drvdata->enable = false; 99 spin_unlock(&drvdata->spinlock); 100 101 dev_dbg(drvdata->dev, "TPDM tracing disabled\n"); 102 } 103 104 static const struct coresight_ops_source tpdm_source_ops = { 105 .enable = tpdm_enable, 106 .disable = tpdm_disable, 107 }; 108 109 static const struct coresight_ops tpdm_cs_ops = { 110 .source_ops = &tpdm_source_ops, 111 }; 112 113 static void tpdm_init_default_data(struct tpdm_drvdata *drvdata) 114 { 115 u32 pidr; 116 117 CS_UNLOCK(drvdata->base); 118 /* Get the datasets present on the TPDM. */ 119 pidr = readl_relaxed(drvdata->base + CORESIGHT_PERIPHIDR0); 120 drvdata->datasets |= pidr & GENMASK(TPDM_DATASETS - 1, 0); 121 CS_LOCK(drvdata->base); 122 } 123 124 /* 125 * value 1: 64 bits test data 126 * value 2: 32 bits test data 127 */ 128 static ssize_t integration_test_store(struct device *dev, 129 struct device_attribute *attr, 130 const char *buf, 131 size_t size) 132 { 133 int i, ret = 0; 134 unsigned long val; 135 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); 136 137 ret = kstrtoul(buf, 10, &val); 138 if (ret) 139 return ret; 140 141 if (val != 1 && val != 2) 142 return -EINVAL; 143 144 if (!drvdata->enable) 145 return -EINVAL; 146 147 if (val == 1) 148 val = ATBCNTRL_VAL_64; 149 else 150 val = ATBCNTRL_VAL_32; 151 CS_UNLOCK(drvdata->base); 152 writel_relaxed(0x1, drvdata->base + TPDM_ITCNTRL); 153 154 for (i = 0; i < INTEGRATION_TEST_CYCLE; i++) 155 writel_relaxed(val, drvdata->base + TPDM_ITATBCNTRL); 156 157 writel_relaxed(0, drvdata->base + TPDM_ITCNTRL); 158 CS_LOCK(drvdata->base); 159 return size; 160 } 161 static DEVICE_ATTR_WO(integration_test); 162 163 static struct attribute *tpdm_attrs[] = { 164 &dev_attr_integration_test.attr, 165 NULL, 166 }; 167 168 static struct attribute_group tpdm_attr_grp = { 169 .attrs = tpdm_attrs, 170 }; 171 172 static const struct attribute_group *tpdm_attr_grps[] = { 173 &tpdm_attr_grp, 174 NULL, 175 }; 176 177 static int tpdm_probe(struct amba_device *adev, const struct amba_id *id) 178 { 179 void __iomem *base; 180 struct device *dev = &adev->dev; 181 struct coresight_platform_data *pdata; 182 struct tpdm_drvdata *drvdata; 183 struct coresight_desc desc = { 0 }; 184 185 pdata = coresight_get_platform_data(dev); 186 if (IS_ERR(pdata)) 187 return PTR_ERR(pdata); 188 adev->dev.platform_data = pdata; 189 190 /* driver data*/ 191 drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); 192 if (!drvdata) 193 return -ENOMEM; 194 drvdata->dev = &adev->dev; 195 dev_set_drvdata(dev, drvdata); 196 197 base = devm_ioremap_resource(dev, &adev->res); 198 if (IS_ERR(base)) 199 return PTR_ERR(base); 200 201 drvdata->base = base; 202 203 /* Set up coresight component description */ 204 desc.name = coresight_alloc_device_name(&tpdm_devs, dev); 205 if (!desc.name) 206 return -ENOMEM; 207 desc.type = CORESIGHT_DEV_TYPE_SOURCE; 208 desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS; 209 desc.ops = &tpdm_cs_ops; 210 desc.pdata = adev->dev.platform_data; 211 desc.dev = &adev->dev; 212 desc.access = CSDEV_ACCESS_IOMEM(base); 213 desc.groups = tpdm_attr_grps; 214 drvdata->csdev = coresight_register(&desc); 215 if (IS_ERR(drvdata->csdev)) 216 return PTR_ERR(drvdata->csdev); 217 218 spin_lock_init(&drvdata->spinlock); 219 tpdm_init_default_data(drvdata); 220 /* Decrease pm refcount when probe is done.*/ 221 pm_runtime_put(&adev->dev); 222 223 return 0; 224 } 225 226 static void tpdm_remove(struct amba_device *adev) 227 { 228 struct tpdm_drvdata *drvdata = dev_get_drvdata(&adev->dev); 229 230 coresight_unregister(drvdata->csdev); 231 } 232 233 /* 234 * Different TPDM has different periph id. 235 * The difference is 0-7 bits' value. So ignore 0-7 bits. 236 */ 237 static struct amba_id tpdm_ids[] = { 238 { 239 .id = 0x000f0e00, 240 .mask = 0x000fff00, 241 }, 242 { 0, 0}, 243 }; 244 245 static struct amba_driver tpdm_driver = { 246 .drv = { 247 .name = "coresight-tpdm", 248 .owner = THIS_MODULE, 249 .suppress_bind_attrs = true, 250 }, 251 .probe = tpdm_probe, 252 .id_table = tpdm_ids, 253 .remove = tpdm_remove, 254 }; 255 256 module_amba_driver(tpdm_driver); 257 258 MODULE_LICENSE("GPL"); 259 MODULE_DESCRIPTION("Trace, Profiling & Diagnostic Monitor driver"); 260