1b3c71626SMao Jinlong // SPDX-License-Identifier: GPL-2.0 2b3c71626SMao Jinlong /* 3b3c71626SMao Jinlong * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. 4b3c71626SMao Jinlong */ 5b3c71626SMao Jinlong 6b3c71626SMao Jinlong #include <linux/amba/bus.h> 7b3c71626SMao Jinlong #include <linux/bitmap.h> 8b3c71626SMao Jinlong #include <linux/coresight.h> 9b3c71626SMao Jinlong #include <linux/coresight-pmu.h> 10b3c71626SMao Jinlong #include <linux/device.h> 11b3c71626SMao Jinlong #include <linux/err.h> 12b3c71626SMao Jinlong #include <linux/fs.h> 13b3c71626SMao Jinlong #include <linux/io.h> 14b3c71626SMao Jinlong #include <linux/kernel.h> 15b3c71626SMao Jinlong #include <linux/module.h> 16b3c71626SMao Jinlong #include <linux/of.h> 17b3c71626SMao Jinlong 18b3c71626SMao Jinlong #include "coresight-priv.h" 19b3c71626SMao Jinlong #include "coresight-tpdm.h" 20b3c71626SMao Jinlong 21b3c71626SMao Jinlong DEFINE_CORESIGHT_DEVLIST(tpdm_devs, "tpdm"); 22b3c71626SMao Jinlong 231f00465dSMao Jinlong static void tpdm_enable_dsb(struct tpdm_drvdata *drvdata) 241f00465dSMao Jinlong { 251f00465dSMao Jinlong u32 val; 261f00465dSMao Jinlong 271f00465dSMao Jinlong /* Set the enable bit of DSB control register to 1 */ 281f00465dSMao Jinlong val = readl_relaxed(drvdata->base + TPDM_DSB_CR); 291f00465dSMao Jinlong val |= TPDM_DSB_CR_ENA; 301f00465dSMao Jinlong writel_relaxed(val, drvdata->base + TPDM_DSB_CR); 311f00465dSMao Jinlong } 321f00465dSMao Jinlong 33b3c71626SMao Jinlong /* TPDM enable operations */ 341f00465dSMao Jinlong static void __tpdm_enable(struct tpdm_drvdata *drvdata) 351f00465dSMao Jinlong { 361f00465dSMao Jinlong CS_UNLOCK(drvdata->base); 371f00465dSMao Jinlong 381f00465dSMao Jinlong /* Check if DSB datasets is present for TPDM. */ 391f00465dSMao Jinlong if (drvdata->datasets & TPDM_PIDR0_DS_DSB) 401f00465dSMao Jinlong tpdm_enable_dsb(drvdata); 411f00465dSMao Jinlong 421f00465dSMao Jinlong CS_LOCK(drvdata->base); 431f00465dSMao Jinlong } 441f00465dSMao Jinlong 45b3c71626SMao Jinlong static int tpdm_enable(struct coresight_device *csdev, 46b3c71626SMao Jinlong struct perf_event *event, u32 mode) 47b3c71626SMao Jinlong { 48b3c71626SMao Jinlong struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); 49b3c71626SMao Jinlong 50b3c71626SMao Jinlong spin_lock(&drvdata->spinlock); 51b3c71626SMao Jinlong if (drvdata->enable) { 52b3c71626SMao Jinlong spin_unlock(&drvdata->spinlock); 53b3c71626SMao Jinlong return -EBUSY; 54b3c71626SMao Jinlong } 55b3c71626SMao Jinlong 561f00465dSMao Jinlong __tpdm_enable(drvdata); 57b3c71626SMao Jinlong drvdata->enable = true; 58b3c71626SMao Jinlong spin_unlock(&drvdata->spinlock); 59b3c71626SMao Jinlong 60b3c71626SMao Jinlong dev_dbg(drvdata->dev, "TPDM tracing enabled\n"); 61b3c71626SMao Jinlong return 0; 62b3c71626SMao Jinlong } 63b3c71626SMao Jinlong 641f00465dSMao Jinlong static void tpdm_disable_dsb(struct tpdm_drvdata *drvdata) 651f00465dSMao Jinlong { 661f00465dSMao Jinlong u32 val; 671f00465dSMao Jinlong 681f00465dSMao Jinlong /* Set the enable bit of DSB control register to 0 */ 691f00465dSMao Jinlong val = readl_relaxed(drvdata->base + TPDM_DSB_CR); 701f00465dSMao Jinlong val &= ~TPDM_DSB_CR_ENA; 711f00465dSMao Jinlong writel_relaxed(val, drvdata->base + TPDM_DSB_CR); 721f00465dSMao Jinlong } 731f00465dSMao Jinlong 74b3c71626SMao Jinlong /* TPDM disable operations */ 751f00465dSMao Jinlong static void __tpdm_disable(struct tpdm_drvdata *drvdata) 761f00465dSMao Jinlong { 771f00465dSMao Jinlong CS_UNLOCK(drvdata->base); 781f00465dSMao Jinlong 791f00465dSMao Jinlong /* Check if DSB datasets is present for TPDM. */ 801f00465dSMao Jinlong if (drvdata->datasets & TPDM_PIDR0_DS_DSB) 811f00465dSMao Jinlong tpdm_disable_dsb(drvdata); 821f00465dSMao Jinlong 831f00465dSMao Jinlong CS_LOCK(drvdata->base); 841f00465dSMao Jinlong } 851f00465dSMao Jinlong 86b3c71626SMao Jinlong static void tpdm_disable(struct coresight_device *csdev, 87b3c71626SMao Jinlong struct perf_event *event) 88b3c71626SMao Jinlong { 89b3c71626SMao Jinlong struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); 90b3c71626SMao Jinlong 91b3c71626SMao Jinlong spin_lock(&drvdata->spinlock); 92b3c71626SMao Jinlong if (!drvdata->enable) { 93b3c71626SMao Jinlong spin_unlock(&drvdata->spinlock); 94b3c71626SMao Jinlong return; 95b3c71626SMao Jinlong } 96b3c71626SMao Jinlong 971f00465dSMao Jinlong __tpdm_disable(drvdata); 98b3c71626SMao Jinlong drvdata->enable = false; 99b3c71626SMao Jinlong spin_unlock(&drvdata->spinlock); 100b3c71626SMao Jinlong 101b3c71626SMao Jinlong dev_dbg(drvdata->dev, "TPDM tracing disabled\n"); 102b3c71626SMao Jinlong } 103b3c71626SMao Jinlong 104b3c71626SMao Jinlong static const struct coresight_ops_source tpdm_source_ops = { 105b3c71626SMao Jinlong .enable = tpdm_enable, 106b3c71626SMao Jinlong .disable = tpdm_disable, 107b3c71626SMao Jinlong }; 108b3c71626SMao Jinlong 109b3c71626SMao Jinlong static const struct coresight_ops tpdm_cs_ops = { 110b3c71626SMao Jinlong .source_ops = &tpdm_source_ops, 111b3c71626SMao Jinlong }; 112b3c71626SMao Jinlong 1131f00465dSMao Jinlong static void tpdm_init_default_data(struct tpdm_drvdata *drvdata) 1141f00465dSMao Jinlong { 1151f00465dSMao Jinlong u32 pidr; 1161f00465dSMao Jinlong 1171f00465dSMao Jinlong CS_UNLOCK(drvdata->base); 1181f00465dSMao Jinlong /* Get the datasets present on the TPDM. */ 1191f00465dSMao Jinlong pidr = readl_relaxed(drvdata->base + CORESIGHT_PERIPHIDR0); 1201f00465dSMao Jinlong drvdata->datasets |= pidr & GENMASK(TPDM_DATASETS - 1, 0); 1211f00465dSMao Jinlong CS_LOCK(drvdata->base); 1221f00465dSMao Jinlong } 1231f00465dSMao Jinlong 124436cca9aSMao Jinlong /* 125436cca9aSMao Jinlong * value 1: 64 bits test data 126436cca9aSMao Jinlong * value 2: 32 bits test data 127436cca9aSMao Jinlong */ 128436cca9aSMao Jinlong static ssize_t integration_test_store(struct device *dev, 129436cca9aSMao Jinlong struct device_attribute *attr, 130436cca9aSMao Jinlong const char *buf, 131436cca9aSMao Jinlong size_t size) 132436cca9aSMao Jinlong { 133436cca9aSMao Jinlong int i, ret = 0; 134436cca9aSMao Jinlong unsigned long val; 135436cca9aSMao Jinlong struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); 136436cca9aSMao Jinlong 137436cca9aSMao Jinlong ret = kstrtoul(buf, 10, &val); 138436cca9aSMao Jinlong if (ret) 139436cca9aSMao Jinlong return ret; 140436cca9aSMao Jinlong 141436cca9aSMao Jinlong if (val != 1 && val != 2) 142436cca9aSMao Jinlong return -EINVAL; 143436cca9aSMao Jinlong 144436cca9aSMao Jinlong if (!drvdata->enable) 145436cca9aSMao Jinlong return -EINVAL; 146436cca9aSMao Jinlong 147436cca9aSMao Jinlong if (val == 1) 148436cca9aSMao Jinlong val = ATBCNTRL_VAL_64; 149436cca9aSMao Jinlong else 150436cca9aSMao Jinlong val = ATBCNTRL_VAL_32; 151436cca9aSMao Jinlong CS_UNLOCK(drvdata->base); 152436cca9aSMao Jinlong writel_relaxed(0x1, drvdata->base + TPDM_ITCNTRL); 153436cca9aSMao Jinlong 154436cca9aSMao Jinlong for (i = 0; i < INTEGRATION_TEST_CYCLE; i++) 155436cca9aSMao Jinlong writel_relaxed(val, drvdata->base + TPDM_ITATBCNTRL); 156436cca9aSMao Jinlong 157436cca9aSMao Jinlong writel_relaxed(0, drvdata->base + TPDM_ITCNTRL); 158436cca9aSMao Jinlong CS_LOCK(drvdata->base); 159436cca9aSMao Jinlong return size; 160436cca9aSMao Jinlong } 161436cca9aSMao Jinlong static DEVICE_ATTR_WO(integration_test); 162436cca9aSMao Jinlong 163436cca9aSMao Jinlong static struct attribute *tpdm_attrs[] = { 164436cca9aSMao Jinlong &dev_attr_integration_test.attr, 165436cca9aSMao Jinlong NULL, 166436cca9aSMao Jinlong }; 167436cca9aSMao Jinlong 168436cca9aSMao Jinlong static struct attribute_group tpdm_attr_grp = { 169436cca9aSMao Jinlong .attrs = tpdm_attrs, 170436cca9aSMao Jinlong }; 171436cca9aSMao Jinlong 172436cca9aSMao Jinlong static const struct attribute_group *tpdm_attr_grps[] = { 173436cca9aSMao Jinlong &tpdm_attr_grp, 174436cca9aSMao Jinlong NULL, 175436cca9aSMao Jinlong }; 176436cca9aSMao Jinlong 177b3c71626SMao Jinlong static int tpdm_probe(struct amba_device *adev, const struct amba_id *id) 178b3c71626SMao Jinlong { 179b3c71626SMao Jinlong void __iomem *base; 180b3c71626SMao Jinlong struct device *dev = &adev->dev; 181b3c71626SMao Jinlong struct coresight_platform_data *pdata; 182b3c71626SMao Jinlong struct tpdm_drvdata *drvdata; 183b3c71626SMao Jinlong struct coresight_desc desc = { 0 }; 184b3c71626SMao Jinlong 185b3c71626SMao Jinlong pdata = coresight_get_platform_data(dev); 186b3c71626SMao Jinlong if (IS_ERR(pdata)) 187b3c71626SMao Jinlong return PTR_ERR(pdata); 188b3c71626SMao Jinlong adev->dev.platform_data = pdata; 189b3c71626SMao Jinlong 190b3c71626SMao Jinlong /* driver data*/ 191b3c71626SMao Jinlong drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); 192b3c71626SMao Jinlong if (!drvdata) 193b3c71626SMao Jinlong return -ENOMEM; 194b3c71626SMao Jinlong drvdata->dev = &adev->dev; 195b3c71626SMao Jinlong dev_set_drvdata(dev, drvdata); 196b3c71626SMao Jinlong 197b3c71626SMao Jinlong base = devm_ioremap_resource(dev, &adev->res); 198b3c71626SMao Jinlong if (IS_ERR(base)) 199b3c71626SMao Jinlong return PTR_ERR(base); 200b3c71626SMao Jinlong 201b3c71626SMao Jinlong drvdata->base = base; 202b3c71626SMao Jinlong 203b3c71626SMao Jinlong /* Set up coresight component description */ 204b3c71626SMao Jinlong desc.name = coresight_alloc_device_name(&tpdm_devs, dev); 205b3c71626SMao Jinlong if (!desc.name) 206b3c71626SMao Jinlong return -ENOMEM; 207b3c71626SMao Jinlong desc.type = CORESIGHT_DEV_TYPE_SOURCE; 208b3c71626SMao Jinlong desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS; 209b3c71626SMao Jinlong desc.ops = &tpdm_cs_ops; 210b3c71626SMao Jinlong desc.pdata = adev->dev.platform_data; 211b3c71626SMao Jinlong desc.dev = &adev->dev; 212b3c71626SMao Jinlong desc.access = CSDEV_ACCESS_IOMEM(base); 213436cca9aSMao Jinlong desc.groups = tpdm_attr_grps; 214b3c71626SMao Jinlong drvdata->csdev = coresight_register(&desc); 215b3c71626SMao Jinlong if (IS_ERR(drvdata->csdev)) 216b3c71626SMao Jinlong return PTR_ERR(drvdata->csdev); 217b3c71626SMao Jinlong 218b3c71626SMao Jinlong spin_lock_init(&drvdata->spinlock); 2191f00465dSMao Jinlong tpdm_init_default_data(drvdata); 220b3c71626SMao Jinlong /* Decrease pm refcount when probe is done.*/ 221b3c71626SMao Jinlong pm_runtime_put(&adev->dev); 222b3c71626SMao Jinlong 223b3c71626SMao Jinlong return 0; 224b3c71626SMao Jinlong } 225b3c71626SMao Jinlong 226*0c1ccc15SArnd Bergmann static void tpdm_remove(struct amba_device *adev) 227b3c71626SMao Jinlong { 228b3c71626SMao Jinlong struct tpdm_drvdata *drvdata = dev_get_drvdata(&adev->dev); 229b3c71626SMao Jinlong 230b3c71626SMao Jinlong coresight_unregister(drvdata->csdev); 231b3c71626SMao Jinlong } 232b3c71626SMao Jinlong 233b3c71626SMao Jinlong /* 234b3c71626SMao Jinlong * Different TPDM has different periph id. 235b3c71626SMao Jinlong * The difference is 0-7 bits' value. So ignore 0-7 bits. 236b3c71626SMao Jinlong */ 237b3c71626SMao Jinlong static struct amba_id tpdm_ids[] = { 238b3c71626SMao Jinlong { 239b3c71626SMao Jinlong .id = 0x000f0e00, 240b3c71626SMao Jinlong .mask = 0x000fff00, 241b3c71626SMao Jinlong }, 242b3c71626SMao Jinlong { 0, 0}, 243b3c71626SMao Jinlong }; 244b3c71626SMao Jinlong 245b3c71626SMao Jinlong static struct amba_driver tpdm_driver = { 246b3c71626SMao Jinlong .drv = { 247b3c71626SMao Jinlong .name = "coresight-tpdm", 248b3c71626SMao Jinlong .owner = THIS_MODULE, 249b3c71626SMao Jinlong .suppress_bind_attrs = true, 250b3c71626SMao Jinlong }, 251b3c71626SMao Jinlong .probe = tpdm_probe, 252b3c71626SMao Jinlong .id_table = tpdm_ids, 253b3c71626SMao Jinlong .remove = tpdm_remove, 254b3c71626SMao Jinlong }; 255b3c71626SMao Jinlong 256b3c71626SMao Jinlong module_amba_driver(tpdm_driver); 257b3c71626SMao Jinlong 258b3c71626SMao Jinlong MODULE_LICENSE("GPL"); 259b3c71626SMao Jinlong MODULE_DESCRIPTION("Trace, Profiling & Diagnostic Monitor driver"); 260