1331 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.4 */5 6#include <linux/amba/bus.h>7#include <linux/bitfield.h>8#include <linux/bitmap.h>9#include <linux/coresight.h>10#include <linux/coresight-pmu.h>11#include <linux/device.h>12#include <linux/err.h>13#include <linux/fs.h>14#include <linux/io.h>15#include <linux/kernel.h>16#include <linux/module.h>17#include <linux/of.h>18 19#include "coresight-priv.h"20#include "coresight-tpdm.h"21 22DEFINE_CORESIGHT_DEVLIST(tpdm_devs, "tpdm");23 24/* Read dataset array member with the index number */25static ssize_t tpdm_simple_dataset_show(struct device *dev,26 struct device_attribute *attr,27 char *buf)28{29 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);30 struct tpdm_dataset_attribute *tpdm_attr =31 container_of(attr, struct tpdm_dataset_attribute, attr);32 33 switch (tpdm_attr->mem) {34 case DSB_EDGE_CTRL:35 if (tpdm_attr->idx >= TPDM_DSB_MAX_EDCR)36 return -EINVAL;37 return sysfs_emit(buf, "0x%x\n",38 drvdata->dsb->edge_ctrl[tpdm_attr->idx]);39 case DSB_EDGE_CTRL_MASK:40 if (tpdm_attr->idx >= TPDM_DSB_MAX_EDCMR)41 return -EINVAL;42 return sysfs_emit(buf, "0x%x\n",43 drvdata->dsb->edge_ctrl_mask[tpdm_attr->idx]);44 case DSB_TRIG_PATT:45 if (tpdm_attr->idx >= TPDM_DSB_MAX_PATT)46 return -EINVAL;47 return sysfs_emit(buf, "0x%x\n",48 drvdata->dsb->trig_patt[tpdm_attr->idx]);49 case DSB_TRIG_PATT_MASK:50 if (tpdm_attr->idx >= TPDM_DSB_MAX_PATT)51 return -EINVAL;52 return sysfs_emit(buf, "0x%x\n",53 drvdata->dsb->trig_patt_mask[tpdm_attr->idx]);54 case DSB_PATT:55 if (tpdm_attr->idx >= TPDM_DSB_MAX_PATT)56 return -EINVAL;57 return sysfs_emit(buf, "0x%x\n",58 drvdata->dsb->patt_val[tpdm_attr->idx]);59 case DSB_PATT_MASK:60 if (tpdm_attr->idx >= TPDM_DSB_MAX_PATT)61 return -EINVAL;62 return sysfs_emit(buf, "0x%x\n",63 drvdata->dsb->patt_mask[tpdm_attr->idx]);64 case DSB_MSR:65 if (tpdm_attr->idx >= drvdata->dsb_msr_num)66 return -EINVAL;67 return sysfs_emit(buf, "0x%x\n",68 drvdata->dsb->msr[tpdm_attr->idx]);69 case CMB_TRIG_PATT:70 if (tpdm_attr->idx >= TPDM_CMB_MAX_PATT)71 return -EINVAL;72 return sysfs_emit(buf, "0x%x\n",73 drvdata->cmb->trig_patt[tpdm_attr->idx]);74 case CMB_TRIG_PATT_MASK:75 if (tpdm_attr->idx >= TPDM_CMB_MAX_PATT)76 return -EINVAL;77 return sysfs_emit(buf, "0x%x\n",78 drvdata->cmb->trig_patt_mask[tpdm_attr->idx]);79 case CMB_PATT:80 if (tpdm_attr->idx >= TPDM_CMB_MAX_PATT)81 return -EINVAL;82 return sysfs_emit(buf, "0x%x\n",83 drvdata->cmb->patt_val[tpdm_attr->idx]);84 case CMB_PATT_MASK:85 if (tpdm_attr->idx >= TPDM_CMB_MAX_PATT)86 return -EINVAL;87 return sysfs_emit(buf, "0x%x\n",88 drvdata->cmb->patt_mask[tpdm_attr->idx]);89 case CMB_MSR:90 if (tpdm_attr->idx >= drvdata->cmb_msr_num)91 return -EINVAL;92 return sysfs_emit(buf, "0x%x\n",93 drvdata->cmb->msr[tpdm_attr->idx]);94 }95 return -EINVAL;96}97 98/* Write dataset array member with the index number */99static ssize_t tpdm_simple_dataset_store(struct device *dev,100 struct device_attribute *attr,101 const char *buf,102 size_t size)103{104 unsigned long val;105 ssize_t ret = -EINVAL;106 107 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);108 struct tpdm_dataset_attribute *tpdm_attr =109 container_of(attr, struct tpdm_dataset_attribute, attr);110 111 if (kstrtoul(buf, 0, &val))112 return ret;113 114 guard(spinlock)(&drvdata->spinlock);115 switch (tpdm_attr->mem) {116 case DSB_TRIG_PATT:117 if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) {118 drvdata->dsb->trig_patt[tpdm_attr->idx] = val;119 ret = size;120 }121 break;122 case DSB_TRIG_PATT_MASK:123 if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) {124 drvdata->dsb->trig_patt_mask[tpdm_attr->idx] = val;125 ret = size;126 }127 break;128 case DSB_PATT:129 if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) {130 drvdata->dsb->patt_val[tpdm_attr->idx] = val;131 ret = size;132 }133 break;134 case DSB_PATT_MASK:135 if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) {136 drvdata->dsb->patt_mask[tpdm_attr->idx] = val;137 ret = size;138 }139 break;140 case DSB_MSR:141 if (tpdm_attr->idx < drvdata->dsb_msr_num) {142 drvdata->dsb->msr[tpdm_attr->idx] = val;143 ret = size;144 }145 break;146 case CMB_TRIG_PATT:147 if (tpdm_attr->idx < TPDM_CMB_MAX_PATT) {148 drvdata->cmb->trig_patt[tpdm_attr->idx] = val;149 ret = size;150 }151 break;152 case CMB_TRIG_PATT_MASK:153 if (tpdm_attr->idx < TPDM_CMB_MAX_PATT) {154 drvdata->cmb->trig_patt_mask[tpdm_attr->idx] = val;155 ret = size;156 }157 break;158 case CMB_PATT:159 if (tpdm_attr->idx < TPDM_CMB_MAX_PATT) {160 drvdata->cmb->patt_val[tpdm_attr->idx] = val;161 ret = size;162 }163 break;164 case CMB_PATT_MASK:165 if (tpdm_attr->idx < TPDM_CMB_MAX_PATT) {166 drvdata->cmb->patt_mask[tpdm_attr->idx] = val;167 ret = size;168 }169 break;170 case CMB_MSR:171 if (tpdm_attr->idx < drvdata->cmb_msr_num) {172 drvdata->cmb->msr[tpdm_attr->idx] = val;173 ret = size;174 }175 break;176 default:177 break;178 }179 180 return ret;181}182 183static umode_t tpdm_dsb_is_visible(struct kobject *kobj,184 struct attribute *attr, int n)185{186 struct device *dev = kobj_to_dev(kobj);187 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);188 189 if (drvdata && tpdm_has_dsb_dataset(drvdata))190 return attr->mode;191 192 return 0;193}194 195static umode_t tpdm_cmb_is_visible(struct kobject *kobj,196 struct attribute *attr, int n)197{198 struct device *dev = kobj_to_dev(kobj);199 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);200 201 if (drvdata && tpdm_has_cmb_dataset(drvdata))202 return attr->mode;203 204 return 0;205}206 207static umode_t tpdm_dsb_msr_is_visible(struct kobject *kobj,208 struct attribute *attr, int n)209{210 struct device *dev = kobj_to_dev(kobj);211 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);212 struct device_attribute *dev_attr =213 container_of(attr, struct device_attribute, attr);214 struct tpdm_dataset_attribute *tpdm_attr =215 container_of(dev_attr, struct tpdm_dataset_attribute, attr);216 217 if (tpdm_attr->idx < drvdata->dsb_msr_num)218 return attr->mode;219 220 return 0;221}222 223static umode_t tpdm_cmb_msr_is_visible(struct kobject *kobj,224 struct attribute *attr, int n)225{226 struct device *dev = kobj_to_dev(kobj);227 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);228 229 struct device_attribute *dev_attr =230 container_of(attr, struct device_attribute, attr);231 struct tpdm_dataset_attribute *tpdm_attr =232 container_of(dev_attr, struct tpdm_dataset_attribute, attr);233 234 if (tpdm_attr->idx < drvdata->cmb_msr_num)235 return attr->mode;236 237 return 0;238}239 240static void tpdm_reset_datasets(struct tpdm_drvdata *drvdata)241{242 if (tpdm_has_dsb_dataset(drvdata)) {243 memset(drvdata->dsb, 0, sizeof(struct dsb_dataset));244 245 drvdata->dsb->trig_ts = true;246 drvdata->dsb->trig_type = false;247 }248 249 if (drvdata->cmb)250 memset(drvdata->cmb, 0, sizeof(struct cmb_dataset));251}252 253static void set_dsb_mode(struct tpdm_drvdata *drvdata, u32 *val)254{255 u32 mode;256 257 /* Set the test accurate mode */258 mode = TPDM_DSB_MODE_TEST(drvdata->dsb->mode);259 *val &= ~TPDM_DSB_CR_TEST_MODE;260 *val |= FIELD_PREP(TPDM_DSB_CR_TEST_MODE, mode);261 262 /* Set the byte lane for high-performance mode */263 mode = TPDM_DSB_MODE_HPBYTESEL(drvdata->dsb->mode);264 *val &= ~TPDM_DSB_CR_HPSEL;265 *val |= FIELD_PREP(TPDM_DSB_CR_HPSEL, mode);266 267 /* Set the performance mode */268 if (drvdata->dsb->mode & TPDM_DSB_MODE_PERF)269 *val |= TPDM_DSB_CR_MODE;270 else271 *val &= ~TPDM_DSB_CR_MODE;272}273 274static void set_dsb_tier(struct tpdm_drvdata *drvdata)275{276 u32 val;277 278 val = readl_relaxed(drvdata->base + TPDM_DSB_TIER);279 280 /* Clear all relevant fields */281 val &= ~(TPDM_DSB_TIER_PATT_TSENAB | TPDM_DSB_TIER_PATT_TYPE |282 TPDM_DSB_TIER_XTRIG_TSENAB);283 284 /* Set pattern timestamp type and enablement */285 if (drvdata->dsb->patt_ts) {286 val |= TPDM_DSB_TIER_PATT_TSENAB;287 if (drvdata->dsb->patt_type)288 val |= TPDM_DSB_TIER_PATT_TYPE;289 else290 val &= ~TPDM_DSB_TIER_PATT_TYPE;291 } else {292 val &= ~TPDM_DSB_TIER_PATT_TSENAB;293 }294 295 /* Set trigger timestamp */296 if (drvdata->dsb->trig_ts)297 val |= TPDM_DSB_TIER_XTRIG_TSENAB;298 else299 val &= ~TPDM_DSB_TIER_XTRIG_TSENAB;300 301 writel_relaxed(val, drvdata->base + TPDM_DSB_TIER);302}303 304static void set_dsb_msr(struct tpdm_drvdata *drvdata)305{306 int i;307 308 for (i = 0; i < drvdata->dsb_msr_num; i++)309 writel_relaxed(drvdata->dsb->msr[i],310 drvdata->base + TPDM_DSB_MSR(i));311}312 313static void tpdm_enable_dsb(struct tpdm_drvdata *drvdata)314{315 u32 val, i;316 317 if (!tpdm_has_dsb_dataset(drvdata))318 return;319 320 for (i = 0; i < TPDM_DSB_MAX_EDCR; i++)321 writel_relaxed(drvdata->dsb->edge_ctrl[i],322 drvdata->base + TPDM_DSB_EDCR(i));323 for (i = 0; i < TPDM_DSB_MAX_EDCMR; i++)324 writel_relaxed(drvdata->dsb->edge_ctrl_mask[i],325 drvdata->base + TPDM_DSB_EDCMR(i));326 for (i = 0; i < TPDM_DSB_MAX_PATT; i++) {327 writel_relaxed(drvdata->dsb->patt_val[i],328 drvdata->base + TPDM_DSB_TPR(i));329 writel_relaxed(drvdata->dsb->patt_mask[i],330 drvdata->base + TPDM_DSB_TPMR(i));331 writel_relaxed(drvdata->dsb->trig_patt[i],332 drvdata->base + TPDM_DSB_XPR(i));333 writel_relaxed(drvdata->dsb->trig_patt_mask[i],334 drvdata->base + TPDM_DSB_XPMR(i));335 }336 337 set_dsb_tier(drvdata);338 set_dsb_msr(drvdata);339 340 val = readl_relaxed(drvdata->base + TPDM_DSB_CR);341 /* Set the mode of DSB dataset */342 set_dsb_mode(drvdata, &val);343 /* Set trigger type */344 if (drvdata->dsb->trig_type)345 val |= TPDM_DSB_CR_TRIG_TYPE;346 else347 val &= ~TPDM_DSB_CR_TRIG_TYPE;348 /* Set the enable bit of DSB control register to 1 */349 val |= TPDM_DSB_CR_ENA;350 writel_relaxed(val, drvdata->base + TPDM_DSB_CR);351}352 353static void set_cmb_tier(struct tpdm_drvdata *drvdata)354{355 u32 val;356 357 val = readl_relaxed(drvdata->base + TPDM_CMB_TIER);358 359 /* Clear all relevant fields */360 val &= ~(TPDM_CMB_TIER_PATT_TSENAB | TPDM_CMB_TIER_TS_ALL |361 TPDM_CMB_TIER_XTRIG_TSENAB);362 363 /* Set pattern timestamp type and enablement */364 if (drvdata->cmb->patt_ts)365 val |= TPDM_CMB_TIER_PATT_TSENAB;366 367 /* Set trigger timestamp */368 if (drvdata->cmb->trig_ts)369 val |= TPDM_CMB_TIER_XTRIG_TSENAB;370 371 /* Set all timestamp enablement*/372 if (drvdata->cmb->ts_all)373 val |= TPDM_CMB_TIER_TS_ALL;374 375 writel_relaxed(val, drvdata->base + TPDM_CMB_TIER);376}377 378static void set_cmb_msr(struct tpdm_drvdata *drvdata)379{380 int i;381 382 for (i = 0; i < drvdata->cmb_msr_num; i++)383 writel_relaxed(drvdata->cmb->msr[i],384 drvdata->base + TPDM_CMB_MSR(i));385}386 387static void tpdm_enable_cmb(struct tpdm_drvdata *drvdata)388{389 u32 val, i;390 391 if (!tpdm_has_cmb_dataset(drvdata))392 return;393 394 /* Configure pattern registers */395 for (i = 0; i < TPDM_CMB_MAX_PATT; i++) {396 writel_relaxed(drvdata->cmb->patt_val[i],397 drvdata->base + TPDM_CMB_TPR(i));398 writel_relaxed(drvdata->cmb->patt_mask[i],399 drvdata->base + TPDM_CMB_TPMR(i));400 writel_relaxed(drvdata->cmb->trig_patt[i],401 drvdata->base + TPDM_CMB_XPR(i));402 writel_relaxed(drvdata->cmb->trig_patt_mask[i],403 drvdata->base + TPDM_CMB_XPMR(i));404 }405 406 set_cmb_tier(drvdata);407 set_cmb_msr(drvdata);408 409 val = readl_relaxed(drvdata->base + TPDM_CMB_CR);410 /*411 * Set to 0 for continuous CMB collection mode,412 * 1 for trace-on-change CMB collection mode.413 */414 if (drvdata->cmb->trace_mode)415 val |= TPDM_CMB_CR_MODE;416 else417 val &= ~TPDM_CMB_CR_MODE;418 /* Set the enable bit of CMB control register to 1 */419 val |= TPDM_CMB_CR_ENA;420 writel_relaxed(val, drvdata->base + TPDM_CMB_CR);421}422 423/*424 * TPDM enable operations425 * The TPDM or Monitor serves as data collection component for various426 * dataset types. It covers Basic Counts(BC), Tenure Counts(TC),427 * Continuous Multi-Bit(CMB), Multi-lane CMB(MCMB) and Discrete Single428 * Bit(DSB). This function will initialize the configuration according429 * to the dataset type supported by the TPDM.430 */431static void __tpdm_enable(struct tpdm_drvdata *drvdata)432{433 CS_UNLOCK(drvdata->base);434 435 tpdm_enable_dsb(drvdata);436 tpdm_enable_cmb(drvdata);437 438 CS_LOCK(drvdata->base);439}440 441static int tpdm_enable(struct coresight_device *csdev, struct perf_event *event,442 enum cs_mode mode,443 __maybe_unused struct coresight_trace_id_map *id_map)444{445 struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);446 447 spin_lock(&drvdata->spinlock);448 if (drvdata->enable) {449 spin_unlock(&drvdata->spinlock);450 return -EBUSY;451 }452 453 if (!coresight_take_mode(csdev, mode)) {454 spin_unlock(&drvdata->spinlock);455 return -EBUSY;456 }457 458 __tpdm_enable(drvdata);459 drvdata->enable = true;460 spin_unlock(&drvdata->spinlock);461 462 dev_dbg(drvdata->dev, "TPDM tracing enabled\n");463 return 0;464}465 466static void tpdm_disable_dsb(struct tpdm_drvdata *drvdata)467{468 u32 val;469 470 if (!tpdm_has_dsb_dataset(drvdata))471 return;472 473 /* Set the enable bit of DSB control register to 0 */474 val = readl_relaxed(drvdata->base + TPDM_DSB_CR);475 val &= ~TPDM_DSB_CR_ENA;476 writel_relaxed(val, drvdata->base + TPDM_DSB_CR);477}478 479static void tpdm_disable_cmb(struct tpdm_drvdata *drvdata)480{481 u32 val;482 483 if (!tpdm_has_cmb_dataset(drvdata))484 return;485 486 val = readl_relaxed(drvdata->base + TPDM_CMB_CR);487 /* Set the enable bit of CMB control register to 0 */488 val &= ~TPDM_CMB_CR_ENA;489 writel_relaxed(val, drvdata->base + TPDM_CMB_CR);490}491 492/* TPDM disable operations */493static void __tpdm_disable(struct tpdm_drvdata *drvdata)494{495 CS_UNLOCK(drvdata->base);496 497 tpdm_disable_dsb(drvdata);498 tpdm_disable_cmb(drvdata);499 500 CS_LOCK(drvdata->base);501}502 503static void tpdm_disable(struct coresight_device *csdev,504 struct perf_event *event)505{506 struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);507 508 spin_lock(&drvdata->spinlock);509 if (!drvdata->enable) {510 spin_unlock(&drvdata->spinlock);511 return;512 }513 514 __tpdm_disable(drvdata);515 coresight_set_mode(csdev, CS_MODE_DISABLED);516 drvdata->enable = false;517 spin_unlock(&drvdata->spinlock);518 519 dev_dbg(drvdata->dev, "TPDM tracing disabled\n");520}521 522static const struct coresight_ops_source tpdm_source_ops = {523 .enable = tpdm_enable,524 .disable = tpdm_disable,525};526 527static const struct coresight_ops tpdm_cs_ops = {528 .source_ops = &tpdm_source_ops,529};530 531static int tpdm_datasets_setup(struct tpdm_drvdata *drvdata)532{533 u32 pidr;534 535 /* Get the datasets present on the TPDM. */536 pidr = readl_relaxed(drvdata->base + CORESIGHT_PERIPHIDR0);537 drvdata->datasets |= pidr & GENMASK(TPDM_DATASETS - 1, 0);538 539 if (tpdm_has_dsb_dataset(drvdata) && (!drvdata->dsb)) {540 drvdata->dsb = devm_kzalloc(drvdata->dev,541 sizeof(*drvdata->dsb), GFP_KERNEL);542 if (!drvdata->dsb)543 return -ENOMEM;544 }545 if (tpdm_has_cmb_dataset(drvdata) && (!drvdata->cmb)) {546 drvdata->cmb = devm_kzalloc(drvdata->dev,547 sizeof(*drvdata->cmb), GFP_KERNEL);548 if (!drvdata->cmb)549 return -ENOMEM;550 }551 tpdm_reset_datasets(drvdata);552 553 return 0;554}555 556static ssize_t reset_dataset_store(struct device *dev,557 struct device_attribute *attr,558 const char *buf,559 size_t size)560{561 int ret = 0;562 unsigned long val;563 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);564 565 ret = kstrtoul(buf, 0, &val);566 if (ret || val != 1)567 return -EINVAL;568 569 spin_lock(&drvdata->spinlock);570 tpdm_reset_datasets(drvdata);571 spin_unlock(&drvdata->spinlock);572 573 return size;574}575static DEVICE_ATTR_WO(reset_dataset);576 577/*578 * value 1: 64 bits test data579 * value 2: 32 bits test data580 */581static ssize_t integration_test_store(struct device *dev,582 struct device_attribute *attr,583 const char *buf,584 size_t size)585{586 int i, ret = 0;587 unsigned long val;588 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);589 590 ret = kstrtoul(buf, 10, &val);591 if (ret)592 return ret;593 594 if (val != 1 && val != 2)595 return -EINVAL;596 597 if (!drvdata->enable)598 return -EINVAL;599 600 if (val == 1)601 val = ATBCNTRL_VAL_64;602 else603 val = ATBCNTRL_VAL_32;604 CS_UNLOCK(drvdata->base);605 writel_relaxed(0x1, drvdata->base + TPDM_ITCNTRL);606 607 for (i = 0; i < INTEGRATION_TEST_CYCLE; i++)608 writel_relaxed(val, drvdata->base + TPDM_ITATBCNTRL);609 610 writel_relaxed(0, drvdata->base + TPDM_ITCNTRL);611 CS_LOCK(drvdata->base);612 return size;613}614static DEVICE_ATTR_WO(integration_test);615 616static struct attribute *tpdm_attrs[] = {617 &dev_attr_reset_dataset.attr,618 &dev_attr_integration_test.attr,619 NULL,620};621 622static struct attribute_group tpdm_attr_grp = {623 .attrs = tpdm_attrs,624};625 626static ssize_t dsb_mode_show(struct device *dev,627 struct device_attribute *attr,628 char *buf)629{630 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);631 632 return sysfs_emit(buf, "%x\n", drvdata->dsb->mode);633}634 635static ssize_t dsb_mode_store(struct device *dev,636 struct device_attribute *attr,637 const char *buf,638 size_t size)639{640 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);641 unsigned long val;642 643 if ((kstrtoul(buf, 0, &val)) || (val < 0) ||644 (val & ~TPDM_DSB_MODE_MASK))645 return -EINVAL;646 647 spin_lock(&drvdata->spinlock);648 drvdata->dsb->mode = val & TPDM_DSB_MODE_MASK;649 spin_unlock(&drvdata->spinlock);650 return size;651}652static DEVICE_ATTR_RW(dsb_mode);653 654static ssize_t ctrl_idx_show(struct device *dev,655 struct device_attribute *attr,656 char *buf)657{658 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);659 660 return sysfs_emit(buf, "%u\n",661 (unsigned int)drvdata->dsb->edge_ctrl_idx);662}663 664/*665 * The EDCR registers can include up to 16 32-bit registers, and each666 * one can be configured to control up to 16 edge detections(2 bits667 * control one edge detection). So a total 256 edge detections can be668 * configured. This function provides a way to set the index number of669 * the edge detection which needs to be configured.670 */671static ssize_t ctrl_idx_store(struct device *dev,672 struct device_attribute *attr,673 const char *buf,674 size_t size)675{676 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);677 unsigned long val;678 679 if ((kstrtoul(buf, 0, &val)) || (val >= TPDM_DSB_MAX_LINES))680 return -EINVAL;681 682 spin_lock(&drvdata->spinlock);683 drvdata->dsb->edge_ctrl_idx = val;684 spin_unlock(&drvdata->spinlock);685 686 return size;687}688static DEVICE_ATTR_RW(ctrl_idx);689 690/*691 * This function is used to control the edge detection according692 * to the index number that has been set.693 * "edge_ctrl" should be one of the following values.694 * 0 - Rising edge detection695 * 1 - Falling edge detection696 * 2 - Rising and falling edge detection (toggle detection)697 */698static ssize_t ctrl_val_store(struct device *dev,699 struct device_attribute *attr,700 const char *buf,701 size_t size)702{703 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);704 unsigned long val, edge_ctrl;705 int reg;706 707 if ((kstrtoul(buf, 0, &edge_ctrl)) || (edge_ctrl > 0x2))708 return -EINVAL;709 710 spin_lock(&drvdata->spinlock);711 /*712 * There are 2 bit per DSB Edge Control line.713 * Thus we have 16 lines in a 32bit word.714 */715 reg = EDCR_TO_WORD_IDX(drvdata->dsb->edge_ctrl_idx);716 val = drvdata->dsb->edge_ctrl[reg];717 val &= ~EDCR_TO_WORD_MASK(drvdata->dsb->edge_ctrl_idx);718 val |= EDCR_TO_WORD_VAL(edge_ctrl, drvdata->dsb->edge_ctrl_idx);719 drvdata->dsb->edge_ctrl[reg] = val;720 spin_unlock(&drvdata->spinlock);721 722 return size;723}724static DEVICE_ATTR_WO(ctrl_val);725 726static ssize_t ctrl_mask_store(struct device *dev,727 struct device_attribute *attr,728 const char *buf,729 size_t size)730{731 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);732 unsigned long val;733 u32 set;734 int reg;735 736 if ((kstrtoul(buf, 0, &val)) || (val & ~1UL))737 return -EINVAL;738 739 spin_lock(&drvdata->spinlock);740 /*741 * There is 1 bit per DSB Edge Control Mark line.742 * Thus we have 32 lines in a 32bit word.743 */744 reg = EDCMR_TO_WORD_IDX(drvdata->dsb->edge_ctrl_idx);745 set = drvdata->dsb->edge_ctrl_mask[reg];746 if (val)747 set |= BIT(EDCMR_TO_WORD_SHIFT(drvdata->dsb->edge_ctrl_idx));748 else749 set &= ~BIT(EDCMR_TO_WORD_SHIFT(drvdata->dsb->edge_ctrl_idx));750 drvdata->dsb->edge_ctrl_mask[reg] = set;751 spin_unlock(&drvdata->spinlock);752 753 return size;754}755static DEVICE_ATTR_WO(ctrl_mask);756 757static ssize_t enable_ts_show(struct device *dev,758 struct device_attribute *attr,759 char *buf)760{761 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);762 struct tpdm_dataset_attribute *tpdm_attr =763 container_of(attr, struct tpdm_dataset_attribute, attr);764 ssize_t size = -EINVAL;765 766 if (tpdm_attr->mem == DSB_PATT)767 size = sysfs_emit(buf, "%u\n",768 (unsigned int)drvdata->dsb->patt_ts);769 else if (tpdm_attr->mem == CMB_PATT)770 size = sysfs_emit(buf, "%u\n",771 (unsigned int)drvdata->cmb->patt_ts);772 773 return size;774}775 776/*777 * value 1: Enable/Disable DSB pattern timestamp778 */779static ssize_t enable_ts_store(struct device *dev,780 struct device_attribute *attr,781 const char *buf,782 size_t size)783{784 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);785 struct tpdm_dataset_attribute *tpdm_attr =786 container_of(attr, struct tpdm_dataset_attribute, attr);787 unsigned long val;788 789 if ((kstrtoul(buf, 0, &val)) || (val & ~1UL))790 return -EINVAL;791 792 guard(spinlock)(&drvdata->spinlock);793 if (tpdm_attr->mem == DSB_PATT)794 drvdata->dsb->patt_ts = !!val;795 else if (tpdm_attr->mem == CMB_PATT)796 drvdata->cmb->patt_ts = !!val;797 else798 return -EINVAL;799 800 return size;801}802 803static ssize_t set_type_show(struct device *dev,804 struct device_attribute *attr,805 char *buf)806{807 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);808 809 return sysfs_emit(buf, "%u\n",810 (unsigned int)drvdata->dsb->patt_type);811}812 813/*814 * value 1: Set DSB pattern type815 */816static ssize_t set_type_store(struct device *dev,817 struct device_attribute *attr,818 const char *buf, size_t size)819{820 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);821 unsigned long val;822 823 if ((kstrtoul(buf, 0, &val)) || (val & ~1UL))824 return -EINVAL;825 826 spin_lock(&drvdata->spinlock);827 drvdata->dsb->patt_type = val;828 spin_unlock(&drvdata->spinlock);829 return size;830}831static DEVICE_ATTR_RW(set_type);832 833static ssize_t dsb_trig_type_show(struct device *dev,834 struct device_attribute *attr, char *buf)835{836 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);837 838 return sysfs_emit(buf, "%u\n",839 (unsigned int)drvdata->dsb->trig_type);840}841 842/*843 * Trigger type (boolean):844 * false - Disable trigger type.845 * true - Enable trigger type.846 */847static ssize_t dsb_trig_type_store(struct device *dev,848 struct device_attribute *attr,849 const char *buf,850 size_t size)851{852 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);853 unsigned long val;854 855 if ((kstrtoul(buf, 0, &val)) || (val & ~1UL))856 return -EINVAL;857 858 spin_lock(&drvdata->spinlock);859 if (val)860 drvdata->dsb->trig_type = true;861 else862 drvdata->dsb->trig_type = false;863 spin_unlock(&drvdata->spinlock);864 return size;865}866static DEVICE_ATTR_RW(dsb_trig_type);867 868static ssize_t dsb_trig_ts_show(struct device *dev,869 struct device_attribute *attr,870 char *buf)871{872 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);873 874 return sysfs_emit(buf, "%u\n",875 (unsigned int)drvdata->dsb->trig_ts);876}877 878/*879 * Trigger timestamp (boolean):880 * false - Disable trigger timestamp.881 * true - Enable trigger timestamp.882 */883static ssize_t dsb_trig_ts_store(struct device *dev,884 struct device_attribute *attr,885 const char *buf,886 size_t size)887{888 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);889 unsigned long val;890 891 if ((kstrtoul(buf, 0, &val)) || (val & ~1UL))892 return -EINVAL;893 894 spin_lock(&drvdata->spinlock);895 if (val)896 drvdata->dsb->trig_ts = true;897 else898 drvdata->dsb->trig_ts = false;899 spin_unlock(&drvdata->spinlock);900 return size;901}902static DEVICE_ATTR_RW(dsb_trig_ts);903 904static ssize_t cmb_mode_show(struct device *dev,905 struct device_attribute *attr,906 char *buf)907{908 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);909 910 return sysfs_emit(buf, "%x\n", drvdata->cmb->trace_mode);911 912}913 914static ssize_t cmb_mode_store(struct device *dev,915 struct device_attribute *attr,916 const char *buf,917 size_t size)918{919 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);920 unsigned long trace_mode;921 922 if (kstrtoul(buf, 0, &trace_mode) || (trace_mode & ~1UL))923 return -EINVAL;924 925 spin_lock(&drvdata->spinlock);926 drvdata->cmb->trace_mode = trace_mode;927 spin_unlock(&drvdata->spinlock);928 return size;929}930static DEVICE_ATTR_RW(cmb_mode);931 932static ssize_t cmb_ts_all_show(struct device *dev,933 struct device_attribute *attr,934 char *buf)935{936 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);937 938 return sysfs_emit(buf, "%u\n",939 (unsigned int)drvdata->cmb->ts_all);940}941 942static ssize_t cmb_ts_all_store(struct device *dev,943 struct device_attribute *attr,944 const char *buf,945 size_t size)946{947 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);948 unsigned long val;949 950 if ((kstrtoul(buf, 0, &val)) || (val & ~1UL))951 return -EINVAL;952 953 guard(spinlock)(&drvdata->spinlock);954 if (val)955 drvdata->cmb->ts_all = true;956 else957 drvdata->cmb->ts_all = false;958 959 return size;960}961static DEVICE_ATTR_RW(cmb_ts_all);962 963static ssize_t cmb_trig_ts_show(struct device *dev,964 struct device_attribute *attr,965 char *buf)966{967 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);968 969 return sysfs_emit(buf, "%u\n",970 (unsigned int)drvdata->cmb->trig_ts);971}972 973static ssize_t cmb_trig_ts_store(struct device *dev,974 struct device_attribute *attr,975 const char *buf,976 size_t size)977{978 struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);979 unsigned long val;980 981 if ((kstrtoul(buf, 0, &val)) || (val & ~1UL))982 return -EINVAL;983 984 guard(spinlock)(&drvdata->spinlock);985 if (val)986 drvdata->cmb->trig_ts = true;987 else988 drvdata->cmb->trig_ts = false;989 990 return size;991}992static DEVICE_ATTR_RW(cmb_trig_ts);993 994static struct attribute *tpdm_dsb_edge_attrs[] = {995 &dev_attr_ctrl_idx.attr,996 &dev_attr_ctrl_val.attr,997 &dev_attr_ctrl_mask.attr,998 DSB_EDGE_CTRL_ATTR(0),999 DSB_EDGE_CTRL_ATTR(1),1000 DSB_EDGE_CTRL_ATTR(2),1001 DSB_EDGE_CTRL_ATTR(3),1002 DSB_EDGE_CTRL_ATTR(4),1003 DSB_EDGE_CTRL_ATTR(5),1004 DSB_EDGE_CTRL_ATTR(6),1005 DSB_EDGE_CTRL_ATTR(7),1006 DSB_EDGE_CTRL_ATTR(8),1007 DSB_EDGE_CTRL_ATTR(9),1008 DSB_EDGE_CTRL_ATTR(10),1009 DSB_EDGE_CTRL_ATTR(11),1010 DSB_EDGE_CTRL_ATTR(12),1011 DSB_EDGE_CTRL_ATTR(13),1012 DSB_EDGE_CTRL_ATTR(14),1013 DSB_EDGE_CTRL_ATTR(15),1014 DSB_EDGE_CTRL_MASK_ATTR(0),1015 DSB_EDGE_CTRL_MASK_ATTR(1),1016 DSB_EDGE_CTRL_MASK_ATTR(2),1017 DSB_EDGE_CTRL_MASK_ATTR(3),1018 DSB_EDGE_CTRL_MASK_ATTR(4),1019 DSB_EDGE_CTRL_MASK_ATTR(5),1020 DSB_EDGE_CTRL_MASK_ATTR(6),1021 DSB_EDGE_CTRL_MASK_ATTR(7),1022 NULL,1023};1024 1025static struct attribute *tpdm_dsb_trig_patt_attrs[] = {1026 DSB_TRIG_PATT_ATTR(0),1027 DSB_TRIG_PATT_ATTR(1),1028 DSB_TRIG_PATT_ATTR(2),1029 DSB_TRIG_PATT_ATTR(3),1030 DSB_TRIG_PATT_ATTR(4),1031 DSB_TRIG_PATT_ATTR(5),1032 DSB_TRIG_PATT_ATTR(6),1033 DSB_TRIG_PATT_ATTR(7),1034 DSB_TRIG_PATT_MASK_ATTR(0),1035 DSB_TRIG_PATT_MASK_ATTR(1),1036 DSB_TRIG_PATT_MASK_ATTR(2),1037 DSB_TRIG_PATT_MASK_ATTR(3),1038 DSB_TRIG_PATT_MASK_ATTR(4),1039 DSB_TRIG_PATT_MASK_ATTR(5),1040 DSB_TRIG_PATT_MASK_ATTR(6),1041 DSB_TRIG_PATT_MASK_ATTR(7),1042 NULL,1043};1044 1045static struct attribute *tpdm_dsb_patt_attrs[] = {1046 DSB_PATT_ATTR(0),1047 DSB_PATT_ATTR(1),1048 DSB_PATT_ATTR(2),1049 DSB_PATT_ATTR(3),1050 DSB_PATT_ATTR(4),1051 DSB_PATT_ATTR(5),1052 DSB_PATT_ATTR(6),1053 DSB_PATT_ATTR(7),1054 DSB_PATT_MASK_ATTR(0),1055 DSB_PATT_MASK_ATTR(1),1056 DSB_PATT_MASK_ATTR(2),1057 DSB_PATT_MASK_ATTR(3),1058 DSB_PATT_MASK_ATTR(4),1059 DSB_PATT_MASK_ATTR(5),1060 DSB_PATT_MASK_ATTR(6),1061 DSB_PATT_MASK_ATTR(7),1062 DSB_PATT_ENABLE_TS,1063 &dev_attr_set_type.attr,1064 NULL,1065};1066 1067static struct attribute *tpdm_dsb_msr_attrs[] = {1068 DSB_MSR_ATTR(0),1069 DSB_MSR_ATTR(1),1070 DSB_MSR_ATTR(2),1071 DSB_MSR_ATTR(3),1072 DSB_MSR_ATTR(4),1073 DSB_MSR_ATTR(5),1074 DSB_MSR_ATTR(6),1075 DSB_MSR_ATTR(7),1076 DSB_MSR_ATTR(8),1077 DSB_MSR_ATTR(9),1078 DSB_MSR_ATTR(10),1079 DSB_MSR_ATTR(11),1080 DSB_MSR_ATTR(12),1081 DSB_MSR_ATTR(13),1082 DSB_MSR_ATTR(14),1083 DSB_MSR_ATTR(15),1084 DSB_MSR_ATTR(16),1085 DSB_MSR_ATTR(17),1086 DSB_MSR_ATTR(18),1087 DSB_MSR_ATTR(19),1088 DSB_MSR_ATTR(20),1089 DSB_MSR_ATTR(21),1090 DSB_MSR_ATTR(22),1091 DSB_MSR_ATTR(23),1092 DSB_MSR_ATTR(24),1093 DSB_MSR_ATTR(25),1094 DSB_MSR_ATTR(26),1095 DSB_MSR_ATTR(27),1096 DSB_MSR_ATTR(28),1097 DSB_MSR_ATTR(29),1098 DSB_MSR_ATTR(30),1099 DSB_MSR_ATTR(31),1100 NULL,1101};1102 1103static struct attribute *tpdm_cmb_trig_patt_attrs[] = {1104 CMB_TRIG_PATT_ATTR(0),1105 CMB_TRIG_PATT_ATTR(1),1106 CMB_TRIG_PATT_MASK_ATTR(0),1107 CMB_TRIG_PATT_MASK_ATTR(1),1108 NULL,1109};1110 1111static struct attribute *tpdm_cmb_patt_attrs[] = {1112 CMB_PATT_ATTR(0),1113 CMB_PATT_ATTR(1),1114 CMB_PATT_MASK_ATTR(0),1115 CMB_PATT_MASK_ATTR(1),1116 CMB_PATT_ENABLE_TS,1117 NULL,1118};1119 1120static struct attribute *tpdm_cmb_msr_attrs[] = {1121 CMB_MSR_ATTR(0),1122 CMB_MSR_ATTR(1),1123 CMB_MSR_ATTR(2),1124 CMB_MSR_ATTR(3),1125 CMB_MSR_ATTR(4),1126 CMB_MSR_ATTR(5),1127 CMB_MSR_ATTR(6),1128 CMB_MSR_ATTR(7),1129 CMB_MSR_ATTR(8),1130 CMB_MSR_ATTR(9),1131 CMB_MSR_ATTR(10),1132 CMB_MSR_ATTR(11),1133 CMB_MSR_ATTR(12),1134 CMB_MSR_ATTR(13),1135 CMB_MSR_ATTR(14),1136 CMB_MSR_ATTR(15),1137 CMB_MSR_ATTR(16),1138 CMB_MSR_ATTR(17),1139 CMB_MSR_ATTR(18),1140 CMB_MSR_ATTR(19),1141 CMB_MSR_ATTR(20),1142 CMB_MSR_ATTR(21),1143 CMB_MSR_ATTR(22),1144 CMB_MSR_ATTR(23),1145 CMB_MSR_ATTR(24),1146 CMB_MSR_ATTR(25),1147 CMB_MSR_ATTR(26),1148 CMB_MSR_ATTR(27),1149 CMB_MSR_ATTR(28),1150 CMB_MSR_ATTR(29),1151 CMB_MSR_ATTR(30),1152 CMB_MSR_ATTR(31),1153 NULL,1154};1155 1156static struct attribute *tpdm_dsb_attrs[] = {1157 &dev_attr_dsb_mode.attr,1158 &dev_attr_dsb_trig_ts.attr,1159 &dev_attr_dsb_trig_type.attr,1160 NULL,1161};1162 1163static struct attribute *tpdm_cmb_attrs[] = {1164 &dev_attr_cmb_mode.attr,1165 &dev_attr_cmb_ts_all.attr,1166 &dev_attr_cmb_trig_ts.attr,1167 NULL,1168};1169 1170static struct attribute_group tpdm_dsb_attr_grp = {1171 .attrs = tpdm_dsb_attrs,1172 .is_visible = tpdm_dsb_is_visible,1173};1174 1175static struct attribute_group tpdm_dsb_edge_grp = {1176 .attrs = tpdm_dsb_edge_attrs,1177 .is_visible = tpdm_dsb_is_visible,1178 .name = "dsb_edge",1179};1180 1181static struct attribute_group tpdm_dsb_trig_patt_grp = {1182 .attrs = tpdm_dsb_trig_patt_attrs,1183 .is_visible = tpdm_dsb_is_visible,1184 .name = "dsb_trig_patt",1185};1186 1187static struct attribute_group tpdm_dsb_patt_grp = {1188 .attrs = tpdm_dsb_patt_attrs,1189 .is_visible = tpdm_dsb_is_visible,1190 .name = "dsb_patt",1191};1192 1193static struct attribute_group tpdm_dsb_msr_grp = {1194 .attrs = tpdm_dsb_msr_attrs,1195 .is_visible = tpdm_dsb_msr_is_visible,1196 .name = "dsb_msr",1197};1198 1199static struct attribute_group tpdm_cmb_attr_grp = {1200 .attrs = tpdm_cmb_attrs,1201 .is_visible = tpdm_cmb_is_visible,1202};1203 1204static struct attribute_group tpdm_cmb_trig_patt_grp = {1205 .attrs = tpdm_cmb_trig_patt_attrs,1206 .is_visible = tpdm_cmb_is_visible,1207 .name = "cmb_trig_patt",1208};1209 1210static struct attribute_group tpdm_cmb_patt_grp = {1211 .attrs = tpdm_cmb_patt_attrs,1212 .is_visible = tpdm_cmb_is_visible,1213 .name = "cmb_patt",1214};1215 1216static struct attribute_group tpdm_cmb_msr_grp = {1217 .attrs = tpdm_cmb_msr_attrs,1218 .is_visible = tpdm_cmb_msr_is_visible,1219 .name = "cmb_msr",1220};1221 1222static const struct attribute_group *tpdm_attr_grps[] = {1223 &tpdm_attr_grp,1224 &tpdm_dsb_attr_grp,1225 &tpdm_dsb_edge_grp,1226 &tpdm_dsb_trig_patt_grp,1227 &tpdm_dsb_patt_grp,1228 &tpdm_dsb_msr_grp,1229 &tpdm_cmb_attr_grp,1230 &tpdm_cmb_trig_patt_grp,1231 &tpdm_cmb_patt_grp,1232 &tpdm_cmb_msr_grp,1233 NULL,1234};1235 1236static int tpdm_probe(struct amba_device *adev, const struct amba_id *id)1237{1238 void __iomem *base;1239 struct device *dev = &adev->dev;1240 struct coresight_platform_data *pdata;1241 struct tpdm_drvdata *drvdata;1242 struct coresight_desc desc = { 0 };1243 int ret;1244 1245 pdata = coresight_get_platform_data(dev);1246 if (IS_ERR(pdata))1247 return PTR_ERR(pdata);1248 adev->dev.platform_data = pdata;1249 1250 /* driver data*/1251 drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);1252 if (!drvdata)1253 return -ENOMEM;1254 drvdata->dev = &adev->dev;1255 dev_set_drvdata(dev, drvdata);1256 1257 base = devm_ioremap_resource(dev, &adev->res);1258 if (IS_ERR(base))1259 return PTR_ERR(base);1260 1261 drvdata->base = base;1262 1263 ret = tpdm_datasets_setup(drvdata);1264 if (ret)1265 return ret;1266 1267 if (drvdata && tpdm_has_dsb_dataset(drvdata))1268 of_property_read_u32(drvdata->dev->of_node,1269 "qcom,dsb-msrs-num", &drvdata->dsb_msr_num);1270 1271 if (drvdata && tpdm_has_cmb_dataset(drvdata))1272 of_property_read_u32(drvdata->dev->of_node,1273 "qcom,cmb-msrs-num", &drvdata->cmb_msr_num);1274 1275 /* Set up coresight component description */1276 desc.name = coresight_alloc_device_name(&tpdm_devs, dev);1277 if (!desc.name)1278 return -ENOMEM;1279 desc.type = CORESIGHT_DEV_TYPE_SOURCE;1280 desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM;1281 desc.ops = &tpdm_cs_ops;1282 desc.pdata = adev->dev.platform_data;1283 desc.dev = &adev->dev;1284 desc.access = CSDEV_ACCESS_IOMEM(base);1285 desc.groups = tpdm_attr_grps;1286 drvdata->csdev = coresight_register(&desc);1287 if (IS_ERR(drvdata->csdev))1288 return PTR_ERR(drvdata->csdev);1289 1290 spin_lock_init(&drvdata->spinlock);1291 1292 /* Decrease pm refcount when probe is done.*/1293 pm_runtime_put(&adev->dev);1294 1295 return 0;1296}1297 1298static void tpdm_remove(struct amba_device *adev)1299{1300 struct tpdm_drvdata *drvdata = dev_get_drvdata(&adev->dev);1301 1302 coresight_unregister(drvdata->csdev);1303}1304 1305/*1306 * Different TPDM has different periph id.1307 * The difference is 0-7 bits' value. So ignore 0-7 bits.1308 */1309static struct amba_id tpdm_ids[] = {1310 {1311 .id = 0x000f0e00,1312 .mask = 0x000fff00,1313 },1314 { 0, 0, NULL },1315};1316 1317static struct amba_driver tpdm_driver = {1318 .drv = {1319 .name = "coresight-tpdm",1320 .suppress_bind_attrs = true,1321 },1322 .probe = tpdm_probe,1323 .id_table = tpdm_ids,1324 .remove = tpdm_remove,1325};1326 1327module_amba_driver(tpdm_driver);1328 1329MODULE_LICENSE("GPL");1330MODULE_DESCRIPTION("Trace, Profiling & Diagnostic Monitor driver");1331