coresight: Updates for Linux v6.8

Updates for the hwtracing subsystem includes :
  - Support for CoreSight TPDM DSB set
  - Support for tuning Cycle count Threshold for CoreSight ETM via perf
  - Support for TRBE on ACPI based systems
  - Support for choosing buffer mode in ETR for sysfs mode
  - Improvements to HiSilicon PTT driver
  - Cleanups to Ultrasoc SMB driver
  - Cleanup .remove callback for various Coresight platform drivers
  - Remove Leo Yan from Reviewers
 
 Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEuFy0byloRoXZHaWBxcXRZPKyBqEFAmV4SmsACgkQxcXRZPKy
 BqHDHg//UvrXrMpTKXSfuFsb4/UtvT53sBh/pULgdV4hNNF3r3Kp2Ktko/n9ftXu
 OK8LmaKlQvchbc1NLD8mI7lW2xIRoK3Sax4WKyHFU0Wwh2a8cnAnEmip2z5YjuY1
 7UlmY/+SIGTXgtx7P4YUJ0O2FoQwp/7yGAvc0n7xlwFpucuGIpawVsEkviA7OKWo
 fMEnkdYppqWEQE4r4eN9TTV8mVFPj669aWBmi5lTFX0C0eaX24eJ/yj+fNKcoqWZ
 HVxhrPK21KcUd9r9FRuavq5GPZXz1DscdkfwwCvNfUMMbkwf3dPLszN4aBF9cWjN
 mb0YJL2AgjzDawJtPow95tikCpn8jW+Bgd9djEtdw3KwT03K7wjk7clQ79oeobpF
 ry2bY40V+IMHmQoVK6vXPLQu6HPwn1wcOrAWNNQGVPS18I1FdHnt0wpIcl2uui+W
 E9vZ6u9WHxToj5TgL9Uobq8hL6HWChJ3Z8uT7wAN35q0JrpvqwvJaZro3g4knMzE
 YQttPzDKb+VpLh6ZwWNL4p2Y93K4MCITdYouZdGTmYquUKbQgixq7B8WI4qALJML
 RUlWfGfWTy1mqmQrnvSG23QWcfMqpQLaC/a/gDYjKgFvT84BXcKXYYIcpkbb7+/7
 9SigQroz2uq+SgdRjdc2bU1QQ/4LN9XfpgKaLDzdChNLpH6DKO8=
 =7+gH
 -----END PGP SIGNATURE-----

Merge tag 'coresight-next-v6.8' of git://git.kernel.org/pub/scm/linux/kernel/git/coresight/linux into char-misc-next

Suzuki writes:

coresight: Updates for Linux v6.8

Updates for the hwtracing subsystem includes :
 - Support for CoreSight TPDM DSB set
 - Support for tuning Cycle count Threshold for CoreSight ETM via perf
 - Support for TRBE on ACPI based systems
 - Support for choosing buffer mode in ETR for sysfs mode
 - Improvements to HiSilicon PTT driver
 - Cleanups to Ultrasoc SMB driver
 - Cleanup .remove callback for various Coresight platform drivers
 - Remove Leo Yan from Reviewers

Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>

* tag 'coresight-next-v6.8' of git://git.kernel.org/pub/scm/linux/kernel/git/coresight/linux: (32 commits)
  coresight: ultrasoc-smb: Use guards to cleanup
  coresight: ultrasoc-smb: Convert to platform remove callback returning void
  coresight: trbe: Convert to platform remove callback returning void
  coresight: replicator: Convert to platform remove callback returning void
  coresight: funnel: Convert to platform remove callback returning void
  coresight: etm4x: Convert to platform remove callback returning void
  coresight: dummy: Convert to platform remove callback returning void
  coresight: etm4x: Fix width of CCITMIN field
  coresight-tpdm: Correct the property name of MSR number
  hwtracing: hisi_ptt: Optimize the trace data committing
  hwtracing: hisi_ptt: Disable interrupt after trace end
  Documentation: ABI: coresight-tpdm: Fix Bit[3] description indentation
  coresight-tpdm: Add nodes for dsb msr support
  dt-bindings: arm: Add support for DSB MSR register
  coresight-tpdm: Add nodes for timestamp request
  coresight-tpdm: Add nodes to configure pattern match output
  coresight-tpdm: Add nodes for dsb edge control
  coresight-tpdm: Add node to set dsb programming mode
  coresight-tpdm: Add nodes to set trigger timestamp and type
  coresight-tpdm: Add reset node to TPDM node
  ...
This commit is contained in:
Greg Kroah-Hartman 2023-12-12 14:34:15 +01:00
commit e909abe885
26 changed files with 1429 additions and 118 deletions

View File

@ -91,3 +91,19 @@ Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
Description: (RW) Size of the trace buffer for TMC-ETR when used in SYSFS
mode. Writable only for TMC-ETR configurations. The value
should be aligned to the kernel pagesize.
What: /sys/bus/coresight/devices/<memory_map>.tmc/buf_modes_available
Date: August 2023
KernelVersion: 6.7
Contact: Anshuman Khandual <anshuman.khandual@arm.com>
Description: (Read) Shows all supported Coresight TMC-ETR buffer modes available
for the users to configure explicitly. This file is avaialble only
for TMC ETR devices.
What: /sys/bus/coresight/devices/<memory_map>.tmc/buf_mode_preferred
Date: August 2023
KernelVersion: 6.7
Contact: Anshuman Khandual <anshuman.khandual@arm.com>
Description: (RW) Current Coresight TMC-ETR buffer mode selected. But user could
only provide a mode which is supported for a given ETR device. This
file is available only for TMC ETR devices.

View File

@ -11,3 +11,162 @@ Description:
Accepts only one of the 2 values - 1 or 2.
1 : Generate 64 bits data
2 : Generate 32 bits data
What: /sys/bus/coresight/devices/<tpdm-name>/reset_dataset
Date: March 2023
KernelVersion 6.7
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(Write) Reset the dataset of the tpdm.
Accepts only one value - 1.
1 : Reset the dataset of the tpdm
What: /sys/bus/coresight/devices/<tpdm-name>/dsb_trig_type
Date: March 2023
KernelVersion 6.7
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Set/Get the trigger type of the DSB for tpdm.
Accepts only one of the 2 values - 0 or 1.
0 : Set the DSB trigger type to false
1 : Set the DSB trigger type to true
What: /sys/bus/coresight/devices/<tpdm-name>/dsb_trig_ts
Date: March 2023
KernelVersion 6.7
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Set/Get the trigger timestamp of the DSB for tpdm.
Accepts only one of the 2 values - 0 or 1.
0 : Set the DSB trigger type to false
1 : Set the DSB trigger type to true
What: /sys/bus/coresight/devices/<tpdm-name>/dsb_mode
Date: March 2023
KernelVersion 6.7
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Set/Get the programming mode of the DSB for tpdm.
Accepts the value needs to be greater than 0. What data
bits do is listed below.
Bit[0:1] : Test mode control bit for choosing the inputs.
Bit[3] : Set to 0 for low performance mode. Set to 1 for high
performance mode.
Bit[4:8] : Select byte lane for high performance mode.
What: /sys/bus/coresight/devices/<tpdm-name>/dsb_edge/ctrl_idx
Date: March 2023
KernelVersion 6.7
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Set/Get the index number of the edge detection for the DSB
subunit TPDM. Since there are at most 256 edge detections, this
value ranges from 0 to 255.
What: /sys/bus/coresight/devices/<tpdm-name>/dsb_edge/ctrl_val
Date: March 2023
KernelVersion 6.7
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
Write a data to control the edge detection corresponding to
the index number. Before writing data to this sysfs file,
"ctrl_idx" should be written first to configure the index
number of the edge detection which needs to be controlled.
Accepts only one of the following values.
0 - Rising edge detection
1 - Falling edge detection
2 - Rising and falling edge detection (toggle detection)
What: /sys/bus/coresight/devices/<tpdm-name>/dsb_edge/ctrl_mask
Date: March 2023
KernelVersion 6.7
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
Write a data to mask the edge detection corresponding to the index
number. Before writing data to this sysfs file, "ctrl_idx" should
be written first to configure the index number of the edge detection
which needs to be masked.
Accepts only one of the 2 values - 0 or 1.
What: /sys/bus/coresight/devices/<tpdm-name>/dsb_edge/edcr[0:15]
Date: March 2023
KernelVersion 6.7
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
Read a set of the edge control value of the DSB in TPDM.
What: /sys/bus/coresight/devices/<tpdm-name>/dsb_edge/edcmr[0:7]
Date: March 2023
KernelVersion 6.7
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
Read a set of the edge control mask of the DSB in TPDM.
What: /sys/bus/coresight/devices/<tpdm-name>/dsb_trig_patt/xpr[0:7]
Date: March 2023
KernelVersion 6.7
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Set/Get the value of the trigger pattern for the DSB
subunit TPDM.
What: /sys/bus/coresight/devices/<tpdm-name>/dsb_trig_patt/xpmr[0:7]
Date: March 2023
KernelVersion 6.7
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Set/Get the mask of the trigger pattern for the DSB
subunit TPDM.
What: /sys/bus/coresight/devices/<tpdm-name>/dsb_patt/tpr[0:7]
Date: March 2023
KernelVersion 6.7
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Set/Get the value of the pattern for the DSB subunit TPDM.
What: /sys/bus/coresight/devices/<tpdm-name>/dsb_patt/tpmr[0:7]
Date: March 2023
KernelVersion 6.7
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Set/Get the mask of the pattern for the DSB subunit TPDM.
What: /sys/bus/coresight/devices/<tpdm-name>/dsb_patt/enable_ts
Date: March 2023
KernelVersion 6.7
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(Write) Set the pattern timestamp of DSB tpdm. Read
the pattern timestamp of DSB tpdm.
Accepts only one of the 2 values - 0 or 1.
0 : Disable DSB pattern timestamp.
1 : Enable DSB pattern timestamp.
What: /sys/bus/coresight/devices/<tpdm-name>/dsb_patt/set_type
Date: March 2023
KernelVersion 6.7
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(Write) Set the pattern type of DSB tpdm. Read
the pattern type of DSB tpdm.
Accepts only one of the 2 values - 0 or 1.
0 : Set the DSB pattern type to value.
1 : Set the DSB pattern type to toggle.
What: /sys/bus/coresight/devices/<tpdm-name>/dsb_msr/msr[0:31]
Date: March 2023
KernelVersion 6.7
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Set/Get the MSR(mux select register) for the DSB subunit
TPDM.

View File

@ -117,6 +117,10 @@ stable kernels.
+----------------+-----------------+-----------------+-----------------------------+
| ARM | Cortex-A76 | #1463225 | ARM64_ERRATUM_1463225 |
+----------------+-----------------+-----------------+-----------------------------+
| ARM | Cortex-A76 | #1490853 | N/A |
+----------------+-----------------+-----------------+-----------------------------+
| ARM | Cortex-A77 | #1491015 | N/A |
+----------------+-----------------+-----------------+-----------------------------+
| ARM | Cortex-A77 | #1508412 | ARM64_ERRATUM_1508412 |
+----------------+-----------------+-----------------+-----------------------------+
| ARM | Cortex-A710 | #2119858 | ARM64_ERRATUM_2119858 |
@ -127,6 +131,8 @@ stable kernels.
+----------------+-----------------+-----------------+-----------------------------+
| ARM | Cortex-A715 | #2645198 | ARM64_ERRATUM_2645198 |
+----------------+-----------------+-----------------+-----------------------------+
| ARM | Cortex-X1 | #1502854 | N/A |
+----------------+-----------------+-----------------+-----------------------------+
| ARM | Cortex-X2 | #2119858 | ARM64_ERRATUM_2119858 |
+----------------+-----------------+-----------------+-----------------------------+
| ARM | Cortex-X2 | #2224489 | ARM64_ERRATUM_2224489 |
@ -135,6 +141,8 @@ stable kernels.
+----------------+-----------------+-----------------+-----------------------------+
| ARM | Neoverse-N1 | #1349291 | N/A |
+----------------+-----------------+-----------------+-----------------------------+
| ARM | Neoverse-N1 | #1490853 | N/A |
+----------------+-----------------+-----------------+-----------------------------+
| ARM | Neoverse-N1 | #1542419 | ARM64_ERRATUM_1542419 |
+----------------+-----------------+-----------------+-----------------------------+
| ARM | Neoverse-N2 | #2139208 | ARM64_ERRATUM_2139208 |
@ -143,6 +151,8 @@ stable kernels.
+----------------+-----------------+-----------------+-----------------------------+
| ARM | Neoverse-N2 | #2253138 | ARM64_ERRATUM_2253138 |
+----------------+-----------------+-----------------+-----------------------------+
| ARM | Neoverse-V1 | #1619801 | N/A |
+----------------+-----------------+-----------------+-----------------------------+
| ARM | MMU-500 | #841119,826419 | N/A |
+----------------+-----------------+-----------------+-----------------------------+
| ARM | MMU-600 | #1076982,1209401| N/A |

View File

@ -44,6 +44,23 @@ properties:
minItems: 1
maxItems: 2
qcom,dsb-element-size:
description:
Specifies the DSB(Discrete Single Bit) element size supported by
the monitor. The associated aggregator will read this size before it
is enabled. DSB element size currently only supports 32-bit and 64-bit.
$ref: /schemas/types.yaml#/definitions/uint8
enum: [32, 64]
qcom,dsb-msrs-num:
description:
Specifies the number of DSB(Discrete Single Bit) MSR(mux select register)
registers supported by the monitor. If this property is not configured
or set to 0, it means this DSB TPDM doesn't support MSR.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 32
clocks:
maxItems: 1
@ -77,6 +94,9 @@ examples:
compatible = "qcom,coresight-tpdm", "arm,primecell";
reg = <0x0684c000 0x1000>;
qcom,dsb-element-size = /bits/ 8 <32>;
qcom,dsb-msrs-num = <16>;
clocks = <&aoss_qmp>;
clock-names = "apb_pclk";

View File

@ -624,6 +624,10 @@ They are also listed in the folder /sys/bus/event_source/devices/cs_etm/format/
* - timestamp
- Session local version of the system wide setting: :ref:`ETMv4_MODE_TIMESTAMP
<coresight-timestamp>`
* - cc_threshold
- Cycle count threshold value. If nothing is provided here or the provided value is 0, then the
default value i.e 0x100 will be used. If provided value is less than minimum cycles threshold
value, as indicated via TRCIDR3.CCITMIN, then the minimum value will be used instead.
How to use the STM module
-------------------------

View File

@ -2051,7 +2051,6 @@ ARM/CORESIGHT FRAMEWORK AND DRIVERS
M: Suzuki K Poulose <suzuki.poulose@arm.com>
R: Mike Leach <mike.leach@linaro.org>
R: James Clark <james.clark@arm.com>
R: Leo Yan <leo.yan@linaro.org>
L: coresight@lists.linaro.org (moderated for non-subscribers)
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained

View File

@ -1093,6 +1093,7 @@ static int coresight_validate_source(struct coresight_device *csdev,
if (subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_PROC &&
subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE &&
subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM &&
subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS) {
dev_err(&csdev->dev, "wrong device subtype in %s\n", function);
return -EINVAL;
@ -1162,6 +1163,7 @@ int coresight_enable(struct coresight_device *csdev)
per_cpu(tracer_path, cpu) = path;
break;
case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM:
case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS:
/*
* Use the hash of source's device name as ID
@ -1212,6 +1214,7 @@ void coresight_disable(struct coresight_device *csdev)
per_cpu(tracer_path, cpu) = NULL;
break;
case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM:
case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS:
hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev)));
/* Find the path by the hash. */

View File

@ -122,14 +122,13 @@ static int dummy_probe(struct platform_device *pdev)
return 0;
}
static int dummy_remove(struct platform_device *pdev)
static void dummy_remove(struct platform_device *pdev)
{
struct dummy_drvdata *drvdata = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
pm_runtime_disable(dev);
coresight_unregister(drvdata->csdev);
return 0;
}
static const struct of_device_id dummy_match[] = {
@ -140,7 +139,7 @@ static const struct of_device_id dummy_match[] = {
static struct platform_driver dummy_driver = {
.probe = dummy_probe,
.remove = dummy_remove,
.remove_new = dummy_remove,
.driver = {
.name = "coresight-dummy",
.of_match_table = dummy_match,

View File

@ -68,6 +68,7 @@ PMU_FORMAT_ATTR(preset, "config:0-3");
PMU_FORMAT_ATTR(sinkid, "config2:0-31");
/* config ID - set if a system configuration is selected */
PMU_FORMAT_ATTR(configid, "config2:32-63");
PMU_FORMAT_ATTR(cc_threshold, "config3:0-11");
/*
@ -101,6 +102,7 @@ static struct attribute *etm_config_formats_attr[] = {
&format_attr_preset.attr,
&format_attr_configid.attr,
&format_attr_branch_broadcast.attr,
&format_attr_cc_threshold.attr,
NULL,
};

View File

@ -644,7 +644,7 @@ static int etm4_parse_event_config(struct coresight_device *csdev,
struct etmv4_config *config = &drvdata->config;
struct perf_event_attr *attr = &event->attr;
unsigned long cfg_hash;
int preset;
int preset, cc_threshold;
/* Clear configuration from previous run */
memset(config, 0, sizeof(struct etmv4_config));
@ -667,7 +667,12 @@ static int etm4_parse_event_config(struct coresight_device *csdev,
if (attr->config & BIT(ETM_OPT_CYCACC)) {
config->cfg |= TRCCONFIGR_CCI;
/* TRM: Must program this for cycacc to work */
config->ccctlr = ETM_CYC_THRESHOLD_DEFAULT;
cc_threshold = attr->config3 & ETM_CYC_THRESHOLD_MASK;
if (!cc_threshold)
cc_threshold = ETM_CYC_THRESHOLD_DEFAULT;
if (cc_threshold < drvdata->ccitmin)
cc_threshold = drvdata->ccitmin;
config->ccctlr = cc_threshold;
}
if (attr->config & BIT(ETM_OPT_TS)) {
/*
@ -1150,6 +1155,41 @@ static void cpu_detect_trace_filtering(struct etmv4_drvdata *drvdata)
drvdata->trfcr = trfcr;
}
/*
* The following errata on applicable cpu ranges, affect the CCITMIN filed
* in TCRIDR3 register. Software read for the field returns 0x100 limiting
* the cycle threshold granularity, whereas the right value should have
* been 0x4, which is well supported in the hardware.
*/
static struct midr_range etm_wrong_ccitmin_cpus[] = {
/* Erratum #1490853 - Cortex-A76 */
MIDR_RANGE(MIDR_CORTEX_A76, 0, 0, 4, 0),
/* Erratum #1490853 - Neoverse-N1 */
MIDR_RANGE(MIDR_NEOVERSE_N1, 0, 0, 4, 0),
/* Erratum #1491015 - Cortex-A77 */
MIDR_RANGE(MIDR_CORTEX_A77, 0, 0, 1, 0),
/* Erratum #1502854 - Cortex-X1 */
MIDR_REV(MIDR_CORTEX_X1, 0, 0),
/* Erratum #1619801 - Neoverse-V1 */
MIDR_REV(MIDR_NEOVERSE_V1, 0, 0),
{},
};
static void etm4_fixup_wrong_ccitmin(struct etmv4_drvdata *drvdata)
{
/*
* Erratum affected cpus will read 256 as the minimum
* instruction trace cycle counting threshold whereas
* the correct value should be 4 instead. Override the
* recorded value for 'drvdata->ccitmin' to workaround
* this problem.
*/
if (is_midr_in_range_list(read_cpuid_id(), etm_wrong_ccitmin_cpus)) {
if (drvdata->ccitmin == 256)
drvdata->ccitmin = 4;
}
}
static void etm4_init_arch_data(void *info)
{
u32 etmidr0;
@ -1214,6 +1254,8 @@ static void etm4_init_arch_data(void *info)
etmidr3 = etm4x_relaxed_read32(csa, TRCIDR3);
/* CCITMIN, bits[11:0] minimum threshold value that can be programmed */
drvdata->ccitmin = FIELD_GET(TRCIDR3_CCITMIN_MASK, etmidr3);
etm4_fixup_wrong_ccitmin(drvdata);
/* EXLEVEL_S, bits[19:16] Secure state instruction tracing */
drvdata->s_ex_level = FIELD_GET(TRCIDR3_EXLEVEL_S_MASK, etmidr3);
drvdata->config.s_ex_level = drvdata->s_ex_level;
@ -2261,7 +2303,7 @@ static void etm4_remove_amba(struct amba_device *adev)
etm4_remove_dev(drvdata);
}
static int etm4_remove_platform_dev(struct platform_device *pdev)
static void etm4_remove_platform_dev(struct platform_device *pdev)
{
struct etmv4_drvdata *drvdata = dev_get_drvdata(&pdev->dev);
@ -2271,8 +2313,6 @@ static int etm4_remove_platform_dev(struct platform_device *pdev)
if (drvdata && !IS_ERR_OR_NULL(drvdata->pclk))
clk_put(drvdata->pclk);
return 0;
}
static const struct amba_id etm4_ids[] = {
@ -2358,7 +2398,7 @@ MODULE_DEVICE_TABLE(acpi, etm4x_acpi_ids);
static struct platform_driver etm4_platform_driver = {
.probe = etm4_probe_platform_dev,
.remove = etm4_remove_platform_dev,
.remove_new = etm4_remove_platform_dev,
.driver = {
.name = "coresight-etm4x",
.of_match_table = etm4_sysreg_match,

View File

@ -1036,7 +1036,7 @@ struct etmv4_drvdata {
u8 ctxid_size;
u8 vmid_size;
u8 ccsize;
u8 ccitmin;
u16 ccitmin;
u8 s_ex_level;
u8 ns_ex_level;
u8 q_support;

View File

@ -335,11 +335,10 @@ static int static_funnel_probe(struct platform_device *pdev)
return ret;
}
static int static_funnel_remove(struct platform_device *pdev)
static void static_funnel_remove(struct platform_device *pdev)
{
funnel_remove(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return 0;
}
static const struct of_device_id static_funnel_match[] = {
@ -360,7 +359,7 @@ MODULE_DEVICE_TABLE(acpi, static_funnel_ids);
static struct platform_driver static_funnel_driver = {
.probe = static_funnel_probe,
.remove = static_funnel_remove,
.remove_new = static_funnel_remove,
.driver = {
.name = "coresight-static-funnel",
/* THIS_MODULE is taken care of by platform_driver_register() */

View File

@ -320,11 +320,10 @@ static int static_replicator_probe(struct platform_device *pdev)
return ret;
}
static int static_replicator_remove(struct platform_device *pdev)
static void static_replicator_remove(struct platform_device *pdev)
{
replicator_remove(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return 0;
}
#ifdef CONFIG_PM
@ -373,7 +372,7 @@ MODULE_DEVICE_TABLE(acpi, static_replicator_acpi_ids);
static struct platform_driver static_replicator_driver = {
.probe = static_replicator_probe,
.remove = static_replicator_remove,
.remove_new = static_replicator_remove,
.driver = {
.name = "coresight-static-replicator",
/* THIS_MODULE is taken care of by platform_driver_register() */

View File

@ -10,6 +10,7 @@
#include <linux/device.h>
#include <linux/idr.h>
#include <linux/io.h>
#include <linux/iommu.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
@ -344,7 +345,14 @@ static const struct attribute_group coresight_tmc_mgmt_group = {
.name = "mgmt",
};
static const struct attribute_group *coresight_tmc_groups[] = {
static const struct attribute_group *coresight_etf_groups[] = {
&coresight_tmc_group,
&coresight_tmc_mgmt_group,
NULL,
};
static const struct attribute_group *coresight_etr_groups[] = {
&coresight_etr_group,
&coresight_tmc_group,
&coresight_tmc_mgmt_group,
NULL,
@ -465,6 +473,7 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
drvdata->memwidth = tmc_get_memwidth(devid);
/* This device is not associated with a session */
drvdata->pid = -1;
drvdata->etr_mode = ETR_MODE_AUTO;
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
drvdata->size = tmc_etr_get_default_buffer_size(dev);
@ -474,16 +483,17 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
}
desc.dev = dev;
desc.groups = coresight_tmc_groups;
switch (drvdata->config_type) {
case TMC_CONFIG_TYPE_ETB:
desc.groups = coresight_etf_groups;
desc.type = CORESIGHT_DEV_TYPE_SINK;
desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
desc.ops = &tmc_etb_cs_ops;
dev_list = &etb_devs;
break;
case TMC_CONFIG_TYPE_ETR:
desc.groups = coresight_etr_groups;
desc.type = CORESIGHT_DEV_TYPE_SINK;
desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_SYSMEM;
desc.ops = &tmc_etr_cs_ops;
@ -496,6 +506,7 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
dev_list = &etr_devs;
break;
case TMC_CONFIG_TYPE_ETF:
desc.groups = coresight_etf_groups;
desc.type = CORESIGHT_DEV_TYPE_LINKSINK;
desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO;

View File

@ -26,6 +26,12 @@ struct etr_flat_buf {
size_t size;
};
struct etr_buf_hw {
bool has_iommu;
bool has_etr_sg;
bool has_catu;
};
/*
* etr_perf_buffer - Perf buffer used for ETR
* @drvdata - The ETR drvdaga this buffer has been allocated for.
@ -830,6 +836,22 @@ static inline int tmc_etr_mode_alloc_buf(int mode,
}
}
static void get_etr_buf_hw(struct device *dev, struct etr_buf_hw *buf_hw)
{
struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
buf_hw->has_iommu = iommu_get_domain_for_dev(dev->parent);
buf_hw->has_etr_sg = tmc_etr_has_cap(drvdata, TMC_ETR_SG);
buf_hw->has_catu = !!tmc_etr_get_catu_device(drvdata);
}
static bool etr_can_use_flat_mode(struct etr_buf_hw *buf_hw, ssize_t etr_buf_size)
{
bool has_sg = buf_hw->has_catu || buf_hw->has_etr_sg;
return !has_sg || buf_hw->has_iommu || etr_buf_size < SZ_1M;
}
/*
* tmc_alloc_etr_buf: Allocate a buffer use by ETR.
* @drvdata : ETR device details.
@ -843,23 +865,22 @@ static struct etr_buf *tmc_alloc_etr_buf(struct tmc_drvdata *drvdata,
int node, void **pages)
{
int rc = -ENOMEM;
bool has_etr_sg, has_iommu;
bool has_sg, has_catu;
struct etr_buf *etr_buf;
struct etr_buf_hw buf_hw;
struct device *dev = &drvdata->csdev->dev;
has_etr_sg = tmc_etr_has_cap(drvdata, TMC_ETR_SG);
has_iommu = iommu_get_domain_for_dev(dev->parent);
has_catu = !!tmc_etr_get_catu_device(drvdata);
has_sg = has_catu || has_etr_sg;
get_etr_buf_hw(dev, &buf_hw);
etr_buf = kzalloc(sizeof(*etr_buf), GFP_KERNEL);
if (!etr_buf)
return ERR_PTR(-ENOMEM);
etr_buf->size = size;
/* If there is user directive for buffer mode, try that first */
if (drvdata->etr_mode != ETR_MODE_AUTO)
rc = tmc_etr_mode_alloc_buf(drvdata->etr_mode, drvdata,
etr_buf, node, pages);
/*
* If we have to use an existing list of pages, we cannot reliably
* use a contiguous DMA memory (even if we have an IOMMU). Otherwise,
@ -872,14 +893,13 @@ static struct etr_buf *tmc_alloc_etr_buf(struct tmc_drvdata *drvdata,
* Fallback to available mechanisms.
*
*/
if (!pages &&
(!has_sg || has_iommu || size < SZ_1M))
if (rc && !pages && etr_can_use_flat_mode(&buf_hw, size))
rc = tmc_etr_mode_alloc_buf(ETR_MODE_FLAT, drvdata,
etr_buf, node, pages);
if (rc && has_etr_sg)
if (rc && buf_hw.has_etr_sg)
rc = tmc_etr_mode_alloc_buf(ETR_MODE_ETR_SG, drvdata,
etr_buf, node, pages);
if (rc && has_catu)
if (rc && buf_hw.has_catu)
rc = tmc_etr_mode_alloc_buf(ETR_MODE_CATU, drvdata,
etr_buf, node, pages);
if (rc) {
@ -1804,3 +1824,70 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
return 0;
}
static const char *const buf_modes_str[] = {
[ETR_MODE_FLAT] = "flat",
[ETR_MODE_ETR_SG] = "tmc-sg",
[ETR_MODE_CATU] = "catu",
[ETR_MODE_AUTO] = "auto",
};
static ssize_t buf_modes_available_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct etr_buf_hw buf_hw;
ssize_t size = 0;
get_etr_buf_hw(dev, &buf_hw);
size += sysfs_emit(buf, "%s ", buf_modes_str[ETR_MODE_AUTO]);
size += sysfs_emit_at(buf, size, "%s ", buf_modes_str[ETR_MODE_FLAT]);
if (buf_hw.has_etr_sg)
size += sysfs_emit_at(buf, size, "%s ", buf_modes_str[ETR_MODE_ETR_SG]);
if (buf_hw.has_catu)
size += sysfs_emit_at(buf, size, "%s ", buf_modes_str[ETR_MODE_CATU]);
size += sysfs_emit_at(buf, size, "\n");
return size;
}
static DEVICE_ATTR_RO(buf_modes_available);
static ssize_t buf_mode_preferred_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
return sysfs_emit(buf, "%s\n", buf_modes_str[drvdata->etr_mode]);
}
static ssize_t buf_mode_preferred_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etr_buf_hw buf_hw;
get_etr_buf_hw(dev, &buf_hw);
if (sysfs_streq(buf, buf_modes_str[ETR_MODE_FLAT]))
drvdata->etr_mode = ETR_MODE_FLAT;
else if (sysfs_streq(buf, buf_modes_str[ETR_MODE_ETR_SG]) && buf_hw.has_etr_sg)
drvdata->etr_mode = ETR_MODE_ETR_SG;
else if (sysfs_streq(buf, buf_modes_str[ETR_MODE_CATU]) && buf_hw.has_catu)
drvdata->etr_mode = ETR_MODE_CATU;
else if (sysfs_streq(buf, buf_modes_str[ETR_MODE_AUTO]))
drvdata->etr_mode = ETR_MODE_AUTO;
else
return -EINVAL;
return size;
}
static DEVICE_ATTR_RW(buf_mode_preferred);
static struct attribute *coresight_etr_attrs[] = {
&dev_attr_buf_modes_available.attr,
&dev_attr_buf_mode_preferred.attr,
NULL,
};
const struct attribute_group coresight_etr_group = {
.attrs = coresight_etr_attrs,
};

View File

@ -135,6 +135,7 @@ enum etr_mode {
ETR_MODE_FLAT, /* Uses contiguous flat buffer */
ETR_MODE_ETR_SG, /* Uses in-built TMC ETR SG mechanism */
ETR_MODE_CATU, /* Use SG mechanism in CATU */
ETR_MODE_AUTO, /* Use the default mechanism */
};
struct etr_buf_operations;
@ -207,6 +208,7 @@ struct tmc_drvdata {
enum tmc_mem_intf_width memwidth;
u32 trigger_cntr;
u32 etr_caps;
enum etr_mode etr_mode;
struct idr idr;
struct mutex idr_mutex;
struct etr_buf *sysfs_buf;
@ -334,5 +336,6 @@ void tmc_etr_set_catu_ops(const struct etr_buf_operations *catu);
void tmc_etr_remove_catu_ops(void);
struct etr_buf *tmc_etr_get_buffer(struct coresight_device *csdev,
enum cs_mode mode, void *data);
extern const struct attribute_group coresight_etr_group;
#endif

View File

@ -21,6 +21,80 @@
DEFINE_CORESIGHT_DEVLIST(tpda_devs, "tpda");
static bool coresight_device_is_tpdm(struct coresight_device *csdev)
{
return (csdev->type == CORESIGHT_DEV_TYPE_SOURCE) &&
(csdev->subtype.source_subtype ==
CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM);
}
/*
* Read the DSB element size from the TPDM device
* Returns
* The dsb element size read from the devicetree if available.
* 0 - Otherwise, with a warning once.
*/
static int tpdm_read_dsb_element_size(struct coresight_device *csdev)
{
int rc = 0;
u8 size = 0;
rc = fwnode_property_read_u8(dev_fwnode(csdev->dev.parent),
"qcom,dsb-element-size", &size);
if (rc)
dev_warn_once(&csdev->dev,
"Failed to read TPDM DSB Element size: %d\n", rc);
return size;
}
/*
* Search and read element data size from the TPDM node in
* the devicetree. Each input port of TPDA is connected to
* a TPDM. Different TPDM supports different types of dataset,
* and some may support more than one type of dataset.
* Parameter "inport" is used to pass in the input port number
* of TPDA, and it is set to -1 in the recursize call.
*/
static int tpda_get_element_size(struct coresight_device *csdev,
int inport)
{
int dsb_size = -ENOENT;
int i, size;
struct coresight_device *in;
for (i = 0; i < csdev->pdata->nr_inconns; i++) {
in = csdev->pdata->in_conns[i]->src_dev;
if (!in)
continue;
/* Ignore the paths that do not match port */
if (inport > 0 &&
csdev->pdata->in_conns[i]->dest_port != inport)
continue;
if (coresight_device_is_tpdm(in)) {
size = tpdm_read_dsb_element_size(in);
} else {
/* Recurse down the path */
size = tpda_get_element_size(in, -1);
}
if (size < 0)
return size;
if (dsb_size < 0) {
/* Found a size, save it. */
dsb_size = size;
} else {
/* Found duplicate TPDMs */
return -EEXIST;
}
}
return dsb_size;
}
/* Settings pre enabling port control register */
static void tpda_enable_pre_port(struct tpda_drvdata *drvdata)
{
@ -32,26 +106,55 @@ static void tpda_enable_pre_port(struct tpda_drvdata *drvdata)
writel_relaxed(val, drvdata->base + TPDA_CR);
}
static void tpda_enable_port(struct tpda_drvdata *drvdata, int port)
static int tpda_enable_port(struct tpda_drvdata *drvdata, int port)
{
u32 val;
int size;
val = readl_relaxed(drvdata->base + TPDA_Pn_CR(port));
/*
* Configure aggregator port n DSB data set element size
* Set the bit to 0 if the size is 32
* Set the bit to 1 if the size is 64
*/
size = tpda_get_element_size(drvdata->csdev, port);
switch (size) {
case 32:
val &= ~TPDA_Pn_CR_DSBSIZE;
break;
case 64:
val |= TPDA_Pn_CR_DSBSIZE;
break;
case 0:
return -EEXIST;
case -EEXIST:
dev_warn_once(&drvdata->csdev->dev,
"Detected multiple TPDMs on port %d", -EEXIST);
return -EEXIST;
default:
return -EINVAL;
}
/* Enable the port */
val |= TPDA_Pn_CR_ENA;
writel_relaxed(val, drvdata->base + TPDA_Pn_CR(port));
return 0;
}
static void __tpda_enable(struct tpda_drvdata *drvdata, int port)
static int __tpda_enable(struct tpda_drvdata *drvdata, int port)
{
int ret;
CS_UNLOCK(drvdata->base);
if (!drvdata->csdev->enable)
tpda_enable_pre_port(drvdata);
tpda_enable_port(drvdata, port);
ret = tpda_enable_port(drvdata, port);
CS_LOCK(drvdata->base);
return ret;
}
static int tpda_enable(struct coresight_device *csdev,
@ -59,16 +162,19 @@ static int tpda_enable(struct coresight_device *csdev,
struct coresight_connection *out)
{
struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
int ret = 0;
spin_lock(&drvdata->spinlock);
if (atomic_read(&in->dest_refcnt) == 0)
__tpda_enable(drvdata, in->dest_port);
if (atomic_read(&in->dest_refcnt) == 0) {
ret = __tpda_enable(drvdata, in->dest_port);
if (!ret) {
atomic_inc(&in->dest_refcnt);
dev_dbg(drvdata->dev, "TPDA inport %d enabled.\n", in->dest_port);
}
}
atomic_inc(&in->dest_refcnt);
spin_unlock(&drvdata->spinlock);
dev_dbg(drvdata->dev, "TPDA inport %d enabled.\n", in->dest_port);
return 0;
return ret;
}
static void __tpda_disable(struct tpda_drvdata *drvdata, int port)

View File

@ -10,6 +10,8 @@
#define TPDA_Pn_CR(n) (0x004 + (n * 4))
/* Aggregator port enable bit */
#define TPDA_Pn_CR_ENA BIT(0)
/* Aggregator port DSB data set element size bit */
#define TPDA_Pn_CR_DSBSIZE BIT(8)
#define TPDA_MAX_INPORTS 32

View File

@ -4,6 +4,7 @@
*/
#include <linux/amba/bus.h>
#include <linux/bitfield.h>
#include <linux/bitmap.h>
#include <linux/coresight.h>
#include <linux/coresight-pmu.h>
@ -20,23 +21,265 @@
DEFINE_CORESIGHT_DEVLIST(tpdm_devs, "tpdm");
static void tpdm_enable_dsb(struct tpdm_drvdata *drvdata)
/* Read dataset array member with the index number */
static ssize_t tpdm_simple_dataset_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct tpdm_dataset_attribute *tpdm_attr =
container_of(attr, struct tpdm_dataset_attribute, attr);
switch (tpdm_attr->mem) {
case DSB_EDGE_CTRL:
if (tpdm_attr->idx >= TPDM_DSB_MAX_EDCR)
return -EINVAL;
return sysfs_emit(buf, "0x%x\n",
drvdata->dsb->edge_ctrl[tpdm_attr->idx]);
case DSB_EDGE_CTRL_MASK:
if (tpdm_attr->idx >= TPDM_DSB_MAX_EDCMR)
return -EINVAL;
return sysfs_emit(buf, "0x%x\n",
drvdata->dsb->edge_ctrl_mask[tpdm_attr->idx]);
case DSB_TRIG_PATT:
if (tpdm_attr->idx >= TPDM_DSB_MAX_PATT)
return -EINVAL;
return sysfs_emit(buf, "0x%x\n",
drvdata->dsb->trig_patt[tpdm_attr->idx]);
case DSB_TRIG_PATT_MASK:
if (tpdm_attr->idx >= TPDM_DSB_MAX_PATT)
return -EINVAL;
return sysfs_emit(buf, "0x%x\n",
drvdata->dsb->trig_patt_mask[tpdm_attr->idx]);
case DSB_PATT:
if (tpdm_attr->idx >= TPDM_DSB_MAX_PATT)
return -EINVAL;
return sysfs_emit(buf, "0x%x\n",
drvdata->dsb->patt_val[tpdm_attr->idx]);
case DSB_PATT_MASK:
if (tpdm_attr->idx >= TPDM_DSB_MAX_PATT)
return -EINVAL;
return sysfs_emit(buf, "0x%x\n",
drvdata->dsb->patt_mask[tpdm_attr->idx]);
case DSB_MSR:
if (tpdm_attr->idx >= drvdata->dsb_msr_num)
return -EINVAL;
return sysfs_emit(buf, "0x%x\n",
drvdata->dsb->msr[tpdm_attr->idx]);
}
return -EINVAL;
}
/* Write dataset array member with the index number */
static ssize_t tpdm_simple_dataset_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
unsigned long val;
ssize_t ret = size;
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct tpdm_dataset_attribute *tpdm_attr =
container_of(attr, struct tpdm_dataset_attribute, attr);
if (kstrtoul(buf, 0, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
switch (tpdm_attr->mem) {
case DSB_TRIG_PATT:
if (tpdm_attr->idx < TPDM_DSB_MAX_PATT)
drvdata->dsb->trig_patt[tpdm_attr->idx] = val;
else
ret = -EINVAL;
break;
case DSB_TRIG_PATT_MASK:
if (tpdm_attr->idx < TPDM_DSB_MAX_PATT)
drvdata->dsb->trig_patt_mask[tpdm_attr->idx] = val;
else
ret = -EINVAL;
break;
case DSB_PATT:
if (tpdm_attr->idx < TPDM_DSB_MAX_PATT)
drvdata->dsb->patt_val[tpdm_attr->idx] = val;
else
ret = -EINVAL;
break;
case DSB_PATT_MASK:
if (tpdm_attr->idx < TPDM_DSB_MAX_PATT)
drvdata->dsb->patt_mask[tpdm_attr->idx] = val;
else
ret = -EINVAL;
break;
case DSB_MSR:
if (tpdm_attr->idx < drvdata->dsb_msr_num)
drvdata->dsb->msr[tpdm_attr->idx] = val;
else
ret = -EINVAL;
break;
default:
ret = -EINVAL;
}
spin_unlock(&drvdata->spinlock);
return ret;
}
static bool tpdm_has_dsb_dataset(struct tpdm_drvdata *drvdata)
{
return (drvdata->datasets & TPDM_PIDR0_DS_DSB);
}
static umode_t tpdm_dsb_is_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
struct device *dev = kobj_to_dev(kobj);
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
if (drvdata && tpdm_has_dsb_dataset(drvdata))
return attr->mode;
return 0;
}
static umode_t tpdm_dsb_msr_is_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
struct device *dev = kobj_to_dev(kobj);
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct device_attribute *dev_attr =
container_of(attr, struct device_attribute, attr);
struct tpdm_dataset_attribute *tpdm_attr =
container_of(dev_attr, struct tpdm_dataset_attribute, attr);
if (tpdm_attr->idx < drvdata->dsb_msr_num)
return attr->mode;
return 0;
}
static void tpdm_reset_datasets(struct tpdm_drvdata *drvdata)
{
if (tpdm_has_dsb_dataset(drvdata)) {
memset(drvdata->dsb, 0, sizeof(struct dsb_dataset));
drvdata->dsb->trig_ts = true;
drvdata->dsb->trig_type = false;
}
}
static void set_dsb_mode(struct tpdm_drvdata *drvdata, u32 *val)
{
u32 mode;
/* Set the test accurate mode */
mode = TPDM_DSB_MODE_TEST(drvdata->dsb->mode);
*val &= ~TPDM_DSB_CR_TEST_MODE;
*val |= FIELD_PREP(TPDM_DSB_CR_TEST_MODE, mode);
/* Set the byte lane for high-performance mode */
mode = TPDM_DSB_MODE_HPBYTESEL(drvdata->dsb->mode);
*val &= ~TPDM_DSB_CR_HPSEL;
*val |= FIELD_PREP(TPDM_DSB_CR_HPSEL, mode);
/* Set the performance mode */
if (drvdata->dsb->mode & TPDM_DSB_MODE_PERF)
*val |= TPDM_DSB_CR_MODE;
else
*val &= ~TPDM_DSB_CR_MODE;
}
static void set_dsb_tier(struct tpdm_drvdata *drvdata)
{
u32 val;
/* Set the enable bit of DSB control register to 1 */
val = readl_relaxed(drvdata->base + TPDM_DSB_TIER);
/* Clear all relevant fields */
val &= ~(TPDM_DSB_TIER_PATT_TSENAB | TPDM_DSB_TIER_PATT_TYPE |
TPDM_DSB_TIER_XTRIG_TSENAB);
/* Set pattern timestamp type and enablement */
if (drvdata->dsb->patt_ts) {
val |= TPDM_DSB_TIER_PATT_TSENAB;
if (drvdata->dsb->patt_type)
val |= TPDM_DSB_TIER_PATT_TYPE;
else
val &= ~TPDM_DSB_TIER_PATT_TYPE;
} else {
val &= ~TPDM_DSB_TIER_PATT_TSENAB;
}
/* Set trigger timestamp */
if (drvdata->dsb->trig_ts)
val |= TPDM_DSB_TIER_XTRIG_TSENAB;
else
val &= ~TPDM_DSB_TIER_XTRIG_TSENAB;
writel_relaxed(val, drvdata->base + TPDM_DSB_TIER);
}
static void set_dsb_msr(struct tpdm_drvdata *drvdata)
{
int i;
for (i = 0; i < drvdata->dsb_msr_num; i++)
writel_relaxed(drvdata->dsb->msr[i],
drvdata->base + TPDM_DSB_MSR(i));
}
static void tpdm_enable_dsb(struct tpdm_drvdata *drvdata)
{
u32 val, i;
for (i = 0; i < TPDM_DSB_MAX_EDCR; i++)
writel_relaxed(drvdata->dsb->edge_ctrl[i],
drvdata->base + TPDM_DSB_EDCR(i));
for (i = 0; i < TPDM_DSB_MAX_EDCMR; i++)
writel_relaxed(drvdata->dsb->edge_ctrl_mask[i],
drvdata->base + TPDM_DSB_EDCMR(i));
for (i = 0; i < TPDM_DSB_MAX_PATT; i++) {
writel_relaxed(drvdata->dsb->patt_val[i],
drvdata->base + TPDM_DSB_TPR(i));
writel_relaxed(drvdata->dsb->patt_mask[i],
drvdata->base + TPDM_DSB_TPMR(i));
writel_relaxed(drvdata->dsb->trig_patt[i],
drvdata->base + TPDM_DSB_XPR(i));
writel_relaxed(drvdata->dsb->trig_patt_mask[i],
drvdata->base + TPDM_DSB_XPMR(i));
}
set_dsb_tier(drvdata);
set_dsb_msr(drvdata);
val = readl_relaxed(drvdata->base + TPDM_DSB_CR);
/* Set the mode of DSB dataset */
set_dsb_mode(drvdata, &val);
/* Set trigger type */
if (drvdata->dsb->trig_type)
val |= TPDM_DSB_CR_TRIG_TYPE;
else
val &= ~TPDM_DSB_CR_TRIG_TYPE;
/* Set the enable bit of DSB control register to 1 */
val |= TPDM_DSB_CR_ENA;
writel_relaxed(val, drvdata->base + TPDM_DSB_CR);
}
/* TPDM enable operations */
/*
* TPDM enable operations
* The TPDM or Monitor serves as data collection component for various
* dataset types. It covers Basic Counts(BC), Tenure Counts(TC),
* Continuous Multi-Bit(CMB), Multi-lane CMB(MCMB) and Discrete Single
* Bit(DSB). This function will initialize the configuration according
* to the dataset type supported by the TPDM.
*/
static void __tpdm_enable(struct tpdm_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
/* Check if DSB datasets is present for TPDM. */
if (drvdata->datasets & TPDM_PIDR0_DS_DSB)
if (tpdm_has_dsb_dataset(drvdata))
tpdm_enable_dsb(drvdata);
CS_LOCK(drvdata->base);
@ -76,8 +319,7 @@ static void __tpdm_disable(struct tpdm_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
/* Check if DSB datasets is present for TPDM. */
if (drvdata->datasets & TPDM_PIDR0_DS_DSB)
if (tpdm_has_dsb_dataset(drvdata))
tpdm_disable_dsb(drvdata);
CS_LOCK(drvdata->base);
@ -110,17 +352,46 @@ static const struct coresight_ops tpdm_cs_ops = {
.source_ops = &tpdm_source_ops,
};
static void tpdm_init_default_data(struct tpdm_drvdata *drvdata)
static int tpdm_datasets_setup(struct tpdm_drvdata *drvdata)
{
u32 pidr;
CS_UNLOCK(drvdata->base);
/* Get the datasets present on the TPDM. */
pidr = readl_relaxed(drvdata->base + CORESIGHT_PERIPHIDR0);
drvdata->datasets |= pidr & GENMASK(TPDM_DATASETS - 1, 0);
CS_LOCK(drvdata->base);
if (tpdm_has_dsb_dataset(drvdata) && (!drvdata->dsb)) {
drvdata->dsb = devm_kzalloc(drvdata->dev,
sizeof(*drvdata->dsb), GFP_KERNEL);
if (!drvdata->dsb)
return -ENOMEM;
}
tpdm_reset_datasets(drvdata);
return 0;
}
static ssize_t reset_dataset_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
int ret = 0;
unsigned long val;
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
ret = kstrtoul(buf, 0, &val);
if (ret || val != 1)
return -EINVAL;
spin_lock(&drvdata->spinlock);
tpdm_reset_datasets(drvdata);
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_WO(reset_dataset);
/*
* value 1: 64 bits test data
* value 2: 32 bits test data
@ -161,6 +432,7 @@ static ssize_t integration_test_store(struct device *dev,
static DEVICE_ATTR_WO(integration_test);
static struct attribute *tpdm_attrs[] = {
&dev_attr_reset_dataset.attr,
&dev_attr_integration_test.attr,
NULL,
};
@ -169,8 +441,421 @@ static struct attribute_group tpdm_attr_grp = {
.attrs = tpdm_attrs,
};
static ssize_t dsb_mode_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
return sysfs_emit(buf, "%x\n", drvdata->dsb->mode);
}
static ssize_t dsb_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
if ((kstrtoul(buf, 0, &val)) || (val < 0) ||
(val & ~TPDM_DSB_MODE_MASK))
return -EINVAL;
spin_lock(&drvdata->spinlock);
drvdata->dsb->mode = val & TPDM_DSB_MODE_MASK;
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_RW(dsb_mode);
static ssize_t ctrl_idx_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
return sysfs_emit(buf, "%u\n",
(unsigned int)drvdata->dsb->edge_ctrl_idx);
}
/*
* The EDCR registers can include up to 16 32-bit registers, and each
* one can be configured to control up to 16 edge detections(2 bits
* control one edge detection). So a total 256 edge detections can be
* configured. This function provides a way to set the index number of
* the edge detection which needs to be configured.
*/
static ssize_t ctrl_idx_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
if ((kstrtoul(buf, 0, &val)) || (val >= TPDM_DSB_MAX_LINES))
return -EINVAL;
spin_lock(&drvdata->spinlock);
drvdata->dsb->edge_ctrl_idx = val;
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_RW(ctrl_idx);
/*
* This function is used to control the edge detection according
* to the index number that has been set.
* "edge_ctrl" should be one of the following values.
* 0 - Rising edge detection
* 1 - Falling edge detection
* 2 - Rising and falling edge detection (toggle detection)
*/
static ssize_t ctrl_val_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val, edge_ctrl;
int reg;
if ((kstrtoul(buf, 0, &edge_ctrl)) || (edge_ctrl > 0x2))
return -EINVAL;
spin_lock(&drvdata->spinlock);
/*
* There are 2 bit per DSB Edge Control line.
* Thus we have 16 lines in a 32bit word.
*/
reg = EDCR_TO_WORD_IDX(drvdata->dsb->edge_ctrl_idx);
val = drvdata->dsb->edge_ctrl[reg];
val &= ~EDCR_TO_WORD_MASK(drvdata->dsb->edge_ctrl_idx);
val |= EDCR_TO_WORD_VAL(edge_ctrl, drvdata->dsb->edge_ctrl_idx);
drvdata->dsb->edge_ctrl[reg] = val;
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_WO(ctrl_val);
static ssize_t ctrl_mask_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
u32 set;
int reg;
if ((kstrtoul(buf, 0, &val)) || (val & ~1UL))
return -EINVAL;
spin_lock(&drvdata->spinlock);
/*
* There is 1 bit per DSB Edge Control Mark line.
* Thus we have 32 lines in a 32bit word.
*/
reg = EDCMR_TO_WORD_IDX(drvdata->dsb->edge_ctrl_idx);
set = drvdata->dsb->edge_ctrl_mask[reg];
if (val)
set |= BIT(EDCMR_TO_WORD_SHIFT(drvdata->dsb->edge_ctrl_idx));
else
set &= ~BIT(EDCMR_TO_WORD_SHIFT(drvdata->dsb->edge_ctrl_idx));
drvdata->dsb->edge_ctrl_mask[reg] = set;
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_WO(ctrl_mask);
static ssize_t enable_ts_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
return sysfs_emit(buf, "%u\n",
(unsigned int)drvdata->dsb->patt_ts);
}
/*
* value 1: Enable/Disable DSB pattern timestamp
*/
static ssize_t enable_ts_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
if ((kstrtoul(buf, 0, &val)) || (val & ~1UL))
return -EINVAL;
spin_lock(&drvdata->spinlock);
drvdata->dsb->patt_ts = !!val;
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_RW(enable_ts);
static ssize_t set_type_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
return sysfs_emit(buf, "%u\n",
(unsigned int)drvdata->dsb->patt_type);
}
/*
* value 1: Set DSB pattern type
*/
static ssize_t set_type_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
if ((kstrtoul(buf, 0, &val)) || (val & ~1UL))
return -EINVAL;
spin_lock(&drvdata->spinlock);
drvdata->dsb->patt_type = val;
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_RW(set_type);
static ssize_t dsb_trig_type_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
return sysfs_emit(buf, "%u\n",
(unsigned int)drvdata->dsb->trig_type);
}
/*
* Trigger type (boolean):
* false - Disable trigger type.
* true - Enable trigger type.
*/
static ssize_t dsb_trig_type_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
if ((kstrtoul(buf, 0, &val)) || (val & ~1UL))
return -EINVAL;
spin_lock(&drvdata->spinlock);
if (val)
drvdata->dsb->trig_type = true;
else
drvdata->dsb->trig_type = false;
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_RW(dsb_trig_type);
static ssize_t dsb_trig_ts_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
return sysfs_emit(buf, "%u\n",
(unsigned int)drvdata->dsb->trig_ts);
}
/*
* Trigger timestamp (boolean):
* false - Disable trigger timestamp.
* true - Enable trigger timestamp.
*/
static ssize_t dsb_trig_ts_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
if ((kstrtoul(buf, 0, &val)) || (val & ~1UL))
return -EINVAL;
spin_lock(&drvdata->spinlock);
if (val)
drvdata->dsb->trig_ts = true;
else
drvdata->dsb->trig_ts = false;
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_RW(dsb_trig_ts);
static struct attribute *tpdm_dsb_edge_attrs[] = {
&dev_attr_ctrl_idx.attr,
&dev_attr_ctrl_val.attr,
&dev_attr_ctrl_mask.attr,
DSB_EDGE_CTRL_ATTR(0),
DSB_EDGE_CTRL_ATTR(1),
DSB_EDGE_CTRL_ATTR(2),
DSB_EDGE_CTRL_ATTR(3),
DSB_EDGE_CTRL_ATTR(4),
DSB_EDGE_CTRL_ATTR(5),
DSB_EDGE_CTRL_ATTR(6),
DSB_EDGE_CTRL_ATTR(7),
DSB_EDGE_CTRL_ATTR(8),
DSB_EDGE_CTRL_ATTR(9),
DSB_EDGE_CTRL_ATTR(10),
DSB_EDGE_CTRL_ATTR(11),
DSB_EDGE_CTRL_ATTR(12),
DSB_EDGE_CTRL_ATTR(13),
DSB_EDGE_CTRL_ATTR(14),
DSB_EDGE_CTRL_ATTR(15),
DSB_EDGE_CTRL_MASK_ATTR(0),
DSB_EDGE_CTRL_MASK_ATTR(1),
DSB_EDGE_CTRL_MASK_ATTR(2),
DSB_EDGE_CTRL_MASK_ATTR(3),
DSB_EDGE_CTRL_MASK_ATTR(4),
DSB_EDGE_CTRL_MASK_ATTR(5),
DSB_EDGE_CTRL_MASK_ATTR(6),
DSB_EDGE_CTRL_MASK_ATTR(7),
NULL,
};
static struct attribute *tpdm_dsb_trig_patt_attrs[] = {
DSB_TRIG_PATT_ATTR(0),
DSB_TRIG_PATT_ATTR(1),
DSB_TRIG_PATT_ATTR(2),
DSB_TRIG_PATT_ATTR(3),
DSB_TRIG_PATT_ATTR(4),
DSB_TRIG_PATT_ATTR(5),
DSB_TRIG_PATT_ATTR(6),
DSB_TRIG_PATT_ATTR(7),
DSB_TRIG_PATT_MASK_ATTR(0),
DSB_TRIG_PATT_MASK_ATTR(1),
DSB_TRIG_PATT_MASK_ATTR(2),
DSB_TRIG_PATT_MASK_ATTR(3),
DSB_TRIG_PATT_MASK_ATTR(4),
DSB_TRIG_PATT_MASK_ATTR(5),
DSB_TRIG_PATT_MASK_ATTR(6),
DSB_TRIG_PATT_MASK_ATTR(7),
NULL,
};
static struct attribute *tpdm_dsb_patt_attrs[] = {
DSB_PATT_ATTR(0),
DSB_PATT_ATTR(1),
DSB_PATT_ATTR(2),
DSB_PATT_ATTR(3),
DSB_PATT_ATTR(4),
DSB_PATT_ATTR(5),
DSB_PATT_ATTR(6),
DSB_PATT_ATTR(7),
DSB_PATT_MASK_ATTR(0),
DSB_PATT_MASK_ATTR(1),
DSB_PATT_MASK_ATTR(2),
DSB_PATT_MASK_ATTR(3),
DSB_PATT_MASK_ATTR(4),
DSB_PATT_MASK_ATTR(5),
DSB_PATT_MASK_ATTR(6),
DSB_PATT_MASK_ATTR(7),
&dev_attr_enable_ts.attr,
&dev_attr_set_type.attr,
NULL,
};
static struct attribute *tpdm_dsb_msr_attrs[] = {
DSB_MSR_ATTR(0),
DSB_MSR_ATTR(1),
DSB_MSR_ATTR(2),
DSB_MSR_ATTR(3),
DSB_MSR_ATTR(4),
DSB_MSR_ATTR(5),
DSB_MSR_ATTR(6),
DSB_MSR_ATTR(7),
DSB_MSR_ATTR(8),
DSB_MSR_ATTR(9),
DSB_MSR_ATTR(10),
DSB_MSR_ATTR(11),
DSB_MSR_ATTR(12),
DSB_MSR_ATTR(13),
DSB_MSR_ATTR(14),
DSB_MSR_ATTR(15),
DSB_MSR_ATTR(16),
DSB_MSR_ATTR(17),
DSB_MSR_ATTR(18),
DSB_MSR_ATTR(19),
DSB_MSR_ATTR(20),
DSB_MSR_ATTR(21),
DSB_MSR_ATTR(22),
DSB_MSR_ATTR(23),
DSB_MSR_ATTR(24),
DSB_MSR_ATTR(25),
DSB_MSR_ATTR(26),
DSB_MSR_ATTR(27),
DSB_MSR_ATTR(28),
DSB_MSR_ATTR(29),
DSB_MSR_ATTR(30),
DSB_MSR_ATTR(31),
NULL,
};
static struct attribute *tpdm_dsb_attrs[] = {
&dev_attr_dsb_mode.attr,
&dev_attr_dsb_trig_ts.attr,
&dev_attr_dsb_trig_type.attr,
NULL,
};
static struct attribute_group tpdm_dsb_attr_grp = {
.attrs = tpdm_dsb_attrs,
.is_visible = tpdm_dsb_is_visible,
};
static struct attribute_group tpdm_dsb_edge_grp = {
.attrs = tpdm_dsb_edge_attrs,
.is_visible = tpdm_dsb_is_visible,
.name = "dsb_edge",
};
static struct attribute_group tpdm_dsb_trig_patt_grp = {
.attrs = tpdm_dsb_trig_patt_attrs,
.is_visible = tpdm_dsb_is_visible,
.name = "dsb_trig_patt",
};
static struct attribute_group tpdm_dsb_patt_grp = {
.attrs = tpdm_dsb_patt_attrs,
.is_visible = tpdm_dsb_is_visible,
.name = "dsb_patt",
};
static struct attribute_group tpdm_dsb_msr_grp = {
.attrs = tpdm_dsb_msr_attrs,
.is_visible = tpdm_dsb_msr_is_visible,
.name = "dsb_msr",
};
static const struct attribute_group *tpdm_attr_grps[] = {
&tpdm_attr_grp,
&tpdm_dsb_attr_grp,
&tpdm_dsb_edge_grp,
&tpdm_dsb_trig_patt_grp,
&tpdm_dsb_patt_grp,
&tpdm_dsb_msr_grp,
NULL,
};
@ -181,6 +866,7 @@ static int tpdm_probe(struct amba_device *adev, const struct amba_id *id)
struct coresight_platform_data *pdata;
struct tpdm_drvdata *drvdata;
struct coresight_desc desc = { 0 };
int ret;
pdata = coresight_get_platform_data(dev);
if (IS_ERR(pdata))
@ -200,12 +886,20 @@ static int tpdm_probe(struct amba_device *adev, const struct amba_id *id)
drvdata->base = base;
ret = tpdm_datasets_setup(drvdata);
if (ret)
return ret;
if (drvdata && tpdm_has_dsb_dataset(drvdata))
of_property_read_u32(drvdata->dev->of_node,
"qcom,dsb-msrs-num", &drvdata->dsb_msr_num);
/* Set up coresight component description */
desc.name = coresight_alloc_device_name(&tpdm_devs, dev);
if (!desc.name)
return -ENOMEM;
desc.type = CORESIGHT_DEV_TYPE_SOURCE;
desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS;
desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM;
desc.ops = &tpdm_cs_ops;
desc.pdata = adev->dev.platform_data;
desc.dev = &adev->dev;
@ -216,7 +910,7 @@ static int tpdm_probe(struct amba_device *adev, const struct amba_id *id)
return PTR_ERR(drvdata->csdev);
spin_lock_init(&drvdata->spinlock);
tpdm_init_default_data(drvdata);
/* Decrease pm refcount when probe is done.*/
pm_runtime_put(&adev->dev);

View File

@ -11,8 +11,52 @@
/* DSB Subunit Registers */
#define TPDM_DSB_CR (0x780)
#define TPDM_DSB_TIER (0x784)
#define TPDM_DSB_TPR(n) (0x788 + (n * 4))
#define TPDM_DSB_TPMR(n) (0x7A8 + (n * 4))
#define TPDM_DSB_XPR(n) (0x7C8 + (n * 4))
#define TPDM_DSB_XPMR(n) (0x7E8 + (n * 4))
#define TPDM_DSB_EDCR(n) (0x808 + (n * 4))
#define TPDM_DSB_EDCMR(n) (0x848 + (n * 4))
#define TPDM_DSB_MSR(n) (0x980 + (n * 4))
/* Enable bit for DSB subunit */
#define TPDM_DSB_CR_ENA BIT(0)
/* Enable bit for DSB subunit perfmance mode */
#define TPDM_DSB_CR_MODE BIT(1)
/* Enable bit for DSB subunit trigger type */
#define TPDM_DSB_CR_TRIG_TYPE BIT(12)
/* Data bits for DSB high performace mode */
#define TPDM_DSB_CR_HPSEL GENMASK(6, 2)
/* Data bits for DSB test mode */
#define TPDM_DSB_CR_TEST_MODE GENMASK(10, 9)
/* Enable bit for DSB subunit pattern timestamp */
#define TPDM_DSB_TIER_PATT_TSENAB BIT(0)
/* Enable bit for DSB subunit trigger timestamp */
#define TPDM_DSB_TIER_XTRIG_TSENAB BIT(1)
/* Bit for DSB subunit pattern type */
#define TPDM_DSB_TIER_PATT_TYPE BIT(2)
/* DSB programming modes */
/* DSB mode bits mask */
#define TPDM_DSB_MODE_MASK GENMASK(8, 0)
/* Test mode control bit*/
#define TPDM_DSB_MODE_TEST(val) (val & GENMASK(1, 0))
/* Performance mode */
#define TPDM_DSB_MODE_PERF BIT(3)
/* High performance mode */
#define TPDM_DSB_MODE_HPBYTESEL(val) (val & GENMASK(8, 4))
#define EDCRS_PER_WORD 16
#define EDCR_TO_WORD_IDX(r) ((r) / EDCRS_PER_WORD)
#define EDCR_TO_WORD_SHIFT(r) ((r % EDCRS_PER_WORD) * 2)
#define EDCR_TO_WORD_VAL(val, r) (val << EDCR_TO_WORD_SHIFT(r))
#define EDCR_TO_WORD_MASK(r) EDCR_TO_WORD_VAL(0x3, r)
#define EDCMRS_PER_WORD 32
#define EDCMR_TO_WORD_IDX(r) ((r) / EDCMRS_PER_WORD)
#define EDCMR_TO_WORD_SHIFT(r) ((r) % EDCMRS_PER_WORD)
/* TPDM integration test registers */
#define TPDM_ITATBCNTRL (0xEF0)
@ -40,6 +84,95 @@
#define TPDM_PIDR0_DS_IMPDEF BIT(0)
#define TPDM_PIDR0_DS_DSB BIT(1)
#define TPDM_DSB_MAX_LINES 256
/* MAX number of EDCR registers */
#define TPDM_DSB_MAX_EDCR 16
/* MAX number of EDCMR registers */
#define TPDM_DSB_MAX_EDCMR 8
/* MAX number of DSB pattern */
#define TPDM_DSB_MAX_PATT 8
/* MAX number of DSB MSR */
#define TPDM_DSB_MAX_MSR 32
#define tpdm_simple_dataset_ro(name, mem, idx) \
(&((struct tpdm_dataset_attribute[]) { \
{ \
__ATTR(name, 0444, tpdm_simple_dataset_show, NULL), \
mem, \
idx, \
} \
})[0].attr.attr)
#define tpdm_simple_dataset_rw(name, mem, idx) \
(&((struct tpdm_dataset_attribute[]) { \
{ \
__ATTR(name, 0644, tpdm_simple_dataset_show, \
tpdm_simple_dataset_store), \
mem, \
idx, \
} \
})[0].attr.attr)
#define DSB_EDGE_CTRL_ATTR(nr) \
tpdm_simple_dataset_ro(edcr##nr, \
DSB_EDGE_CTRL, nr)
#define DSB_EDGE_CTRL_MASK_ATTR(nr) \
tpdm_simple_dataset_ro(edcmr##nr, \
DSB_EDGE_CTRL_MASK, nr)
#define DSB_TRIG_PATT_ATTR(nr) \
tpdm_simple_dataset_rw(xpr##nr, \
DSB_TRIG_PATT, nr)
#define DSB_TRIG_PATT_MASK_ATTR(nr) \
tpdm_simple_dataset_rw(xpmr##nr, \
DSB_TRIG_PATT_MASK, nr)
#define DSB_PATT_ATTR(nr) \
tpdm_simple_dataset_rw(tpr##nr, \
DSB_PATT, nr)
#define DSB_PATT_MASK_ATTR(nr) \
tpdm_simple_dataset_rw(tpmr##nr, \
DSB_PATT_MASK, nr)
#define DSB_MSR_ATTR(nr) \
tpdm_simple_dataset_rw(msr##nr, \
DSB_MSR, nr)
/**
* struct dsb_dataset - specifics associated to dsb dataset
* @mode: DSB programming mode
* @edge_ctrl_idx Index number of the edge control
* @edge_ctrl: Save value for edge control
* @edge_ctrl_mask: Save value for edge control mask
* @patt_val: Save value for pattern
* @patt_mask: Save value for pattern mask
* @trig_patt: Save value for trigger pattern
* @trig_patt_mask: Save value for trigger pattern mask
* @msr Save value for MSR
* @patt_ts: Enable/Disable pattern timestamp
* @patt_type: Set pattern type
* @trig_ts: Enable/Disable trigger timestamp.
* @trig_type: Enable/Disable trigger type.
*/
struct dsb_dataset {
u32 mode;
u32 edge_ctrl_idx;
u32 edge_ctrl[TPDM_DSB_MAX_EDCR];
u32 edge_ctrl_mask[TPDM_DSB_MAX_EDCMR];
u32 patt_val[TPDM_DSB_MAX_PATT];
u32 patt_mask[TPDM_DSB_MAX_PATT];
u32 trig_patt[TPDM_DSB_MAX_PATT];
u32 trig_patt_mask[TPDM_DSB_MAX_PATT];
u32 msr[TPDM_DSB_MAX_MSR];
bool patt_ts;
bool patt_type;
bool trig_ts;
bool trig_type;
};
/**
* struct tpdm_drvdata - specifics associated to an TPDM component
* @base: memory mapped base address for this component.
@ -48,6 +181,8 @@
* @spinlock: lock for the drvdata value.
* @enable: enable status of the component.
* @datasets: The datasets types present of the TPDM.
* @dsb Specifics associated to TPDM DSB.
* @dsb_msr_num Number of MSR supported by DSB TPDM
*/
struct tpdm_drvdata {
@ -57,6 +192,32 @@ struct tpdm_drvdata {
spinlock_t spinlock;
bool enable;
unsigned long datasets;
struct dsb_dataset *dsb;
u32 dsb_msr_num;
};
/* Enumerate members of various datasets */
enum dataset_mem {
DSB_EDGE_CTRL,
DSB_EDGE_CTRL_MASK,
DSB_TRIG_PATT,
DSB_TRIG_PATT_MASK,
DSB_PATT,
DSB_PATT_MASK,
DSB_MSR,
};
/**
* struct tpdm_dataset_attribute - Record the member variables and
* index number of datasets that need to be operated by sysfs file
* @attr: The device attribute
* @mem: The member in the dataset data structure
* @idx: The index number of the array data
*/
struct tpdm_dataset_attribute {
struct device_attribute attr;
enum dataset_mem mem;
u32 idx;
};
#endif /* _CORESIGHT_CORESIGHT_TPDM_H */

View File

@ -1253,8 +1253,18 @@ static void arm_trbe_register_coresight_cpu(struct trbe_drvdata *drvdata, int cp
desc.name = devm_kasprintf(dev, GFP_KERNEL, "trbe%d", cpu);
if (!desc.name)
goto cpu_clear;
desc.pdata = coresight_get_platform_data(dev);
/*
* TRBE coresight devices do not need regular connections
* information, as the paths get built between all percpu
* source and their respective percpu sink devices. Though
* coresight_register() expect device connections via the
* platform_data, which TRBE devices do not have. As they
* are not real ACPI devices, coresight_get_platform_data()
* ends up failing. Instead let's allocate a dummy zeroed
* coresight_platform_data structure and assign that back
* into the device for that purpose.
*/
desc.pdata = devm_kzalloc(dev, sizeof(*desc.pdata), GFP_KERNEL);
if (IS_ERR(desc.pdata))
goto cpu_clear;
@ -1520,14 +1530,13 @@ probe_failed:
return ret;
}
static int arm_trbe_device_remove(struct platform_device *pdev)
static void arm_trbe_device_remove(struct platform_device *pdev)
{
struct trbe_drvdata *drvdata = platform_get_drvdata(pdev);
arm_trbe_remove_cpuhp(drvdata);
arm_trbe_remove_coresight(drvdata);
arm_trbe_remove_irq(drvdata);
return 0;
}
static const struct of_device_id arm_trbe_of_match[] = {
@ -1536,14 +1545,23 @@ static const struct of_device_id arm_trbe_of_match[] = {
};
MODULE_DEVICE_TABLE(of, arm_trbe_of_match);
#ifdef CONFIG_ACPI
static const struct platform_device_id arm_trbe_acpi_match[] = {
{ ARMV8_TRBE_PDEV_NAME, 0 },
{ }
};
MODULE_DEVICE_TABLE(platform, arm_trbe_acpi_match);
#endif
static struct platform_driver arm_trbe_driver = {
.id_table = ACPI_PTR(arm_trbe_acpi_match),
.driver = {
.name = DRVNAME,
.of_match_table = of_match_ptr(arm_trbe_of_match),
.suppress_bind_attrs = true,
},
.probe = arm_trbe_device_probe,
.remove = arm_trbe_device_remove,
.remove_new = arm_trbe_device_remove,
};
static int __init arm_trbe_init(void)

View File

@ -7,11 +7,13 @@
*
* Author: Anshuman Khandual <anshuman.khandual@arm.com>
*/
#include <linux/acpi.h>
#include <linux/coresight.h>
#include <linux/device.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/perf/arm_pmu.h>
#include <linux/platform_device.h>
#include <linux/smp.h>

View File

@ -97,27 +97,19 @@ static int smb_open(struct inode *inode, struct file *file)
{
struct smb_drv_data *drvdata = container_of(file->private_data,
struct smb_drv_data, miscdev);
int ret = 0;
spin_lock(&drvdata->spinlock);
guard(spinlock)(&drvdata->spinlock);
if (drvdata->reading) {
ret = -EBUSY;
goto out;
}
if (drvdata->reading)
return -EBUSY;
if (atomic_read(&drvdata->csdev->refcnt)) {
ret = -EBUSY;
goto out;
}
if (atomic_read(&drvdata->csdev->refcnt))
return -EBUSY;
smb_update_data_size(drvdata);
drvdata->reading = true;
out:
spin_unlock(&drvdata->spinlock);
return ret;
return 0;
}
static ssize_t smb_read(struct file *file, char __user *data, size_t len,
@ -160,9 +152,8 @@ static int smb_release(struct inode *inode, struct file *file)
struct smb_drv_data *drvdata = container_of(file->private_data,
struct smb_drv_data, miscdev);
spin_lock(&drvdata->spinlock);
guard(spinlock)(&drvdata->spinlock);
drvdata->reading = false;
spin_unlock(&drvdata->spinlock);
return 0;
}
@ -255,19 +246,15 @@ static int smb_enable(struct coresight_device *csdev, enum cs_mode mode,
struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent);
int ret = 0;
spin_lock(&drvdata->spinlock);
guard(spinlock)(&drvdata->spinlock);
/* Do nothing, the trace data is reading by other interface now */
if (drvdata->reading) {
ret = -EBUSY;
goto out;
}
if (drvdata->reading)
return -EBUSY;
/* Do nothing, the SMB is already enabled as other mode */
if (drvdata->mode != CS_MODE_DISABLED && drvdata->mode != mode) {
ret = -EBUSY;
goto out;
}
if (drvdata->mode != CS_MODE_DISABLED && drvdata->mode != mode)
return -EBUSY;
switch (mode) {
case CS_MODE_SYSFS:
@ -281,13 +268,10 @@ static int smb_enable(struct coresight_device *csdev, enum cs_mode mode,
}
if (ret)
goto out;
return ret;
atomic_inc(&csdev->refcnt);
dev_dbg(&csdev->dev, "Ultrasoc SMB enabled\n");
out:
spin_unlock(&drvdata->spinlock);
return ret;
}
@ -295,19 +279,14 @@ out:
static int smb_disable(struct coresight_device *csdev)
{
struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent);
int ret = 0;
spin_lock(&drvdata->spinlock);
guard(spinlock)(&drvdata->spinlock);
if (drvdata->reading) {
ret = -EBUSY;
goto out;
}
if (drvdata->reading)
return -EBUSY;
if (atomic_dec_return(&csdev->refcnt)) {
ret = -EBUSY;
goto out;
}
if (atomic_dec_return(&csdev->refcnt))
return -EBUSY;
/* Complain if we (somehow) got out of sync */
WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED);
@ -317,12 +296,9 @@ static int smb_disable(struct coresight_device *csdev)
/* Dissociate from the target process. */
drvdata->pid = -1;
drvdata->mode = CS_MODE_DISABLED;
dev_dbg(&csdev->dev, "Ultrasoc SMB disabled\n");
out:
spin_unlock(&drvdata->spinlock);
return ret;
return 0;
}
static void *smb_alloc_buffer(struct coresight_device *csdev,
@ -395,17 +371,17 @@ static unsigned long smb_update_buffer(struct coresight_device *csdev,
struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent);
struct smb_data_buffer *sdb = &drvdata->sdb;
struct cs_buffers *buf = sink_config;
unsigned long data_size = 0;
unsigned long data_size;
bool lost = false;
if (!buf)
return 0;
spin_lock(&drvdata->spinlock);
guard(spinlock)(&drvdata->spinlock);
/* Don't do anything if another tracer is using this sink. */
if (atomic_read(&csdev->refcnt) != 1)
goto out;
return 0;
smb_disable_hw(drvdata);
smb_update_data_size(drvdata);
@ -424,8 +400,6 @@ static unsigned long smb_update_buffer(struct coresight_device *csdev,
smb_sync_perf_buffer(drvdata, buf, handle->head);
if (!buf->snapshot && lost)
perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED);
out:
spin_unlock(&drvdata->spinlock);
return data_size;
}
@ -601,15 +575,13 @@ static int smb_probe(struct platform_device *pdev)
return 0;
}
static int smb_remove(struct platform_device *pdev)
static void smb_remove(struct platform_device *pdev)
{
struct smb_drv_data *drvdata = platform_get_drvdata(pdev);
smb_unregister_sink(drvdata);
smb_config_inport(&pdev->dev, false);
return 0;
}
#ifdef CONFIG_ACPI
@ -627,7 +599,7 @@ static struct platform_driver smb_driver = {
.suppress_bind_attrs = true,
},
.probe = smb_probe,
.remove = smb_remove,
.remove_new = smb_remove,
};
module_platform_driver(smb_driver);

View File

@ -183,6 +183,10 @@ static void hisi_ptt_wait_dma_reset_done(struct hisi_ptt *hisi_ptt)
static void hisi_ptt_trace_end(struct hisi_ptt *hisi_ptt)
{
writel(0, hisi_ptt->iobase + HISI_PTT_TRACE_CTRL);
/* Mask the interrupt on the end */
writel(HISI_PTT_TRACE_INT_MASK_ALL, hisi_ptt->iobase + HISI_PTT_TRACE_INT_MASK);
hisi_ptt->trace_ctrl.started = false;
}
@ -270,15 +274,14 @@ static int hisi_ptt_update_aux(struct hisi_ptt *hisi_ptt, int index, bool stop)
buf->pos += size;
/*
* Just commit the traced data if we're going to stop. Otherwise if the
* resident AUX buffer cannot contain the data of next trace buffer,
* apply a new one.
* Always commit the data to the AUX buffer in time to make sure
* userspace got enough time to consume the data.
*
* If we're not going to stop, apply a new one and check whether
* there's enough room for the next trace.
*/
if (stop) {
perf_aux_output_end(handle, buf->pos);
} else if (buf->length - buf->pos < HISI_PTT_TRACE_BUF_SIZE) {
perf_aux_output_end(handle, buf->pos);
perf_aux_output_end(handle, size);
if (!stop) {
buf = perf_aux_output_begin(handle, event);
if (!buf)
return -EINVAL;

View File

@ -47,6 +47,7 @@
#define HISI_PTT_TRACE_INT_STAT 0x0890
#define HISI_PTT_TRACE_INT_STAT_MASK GENMASK(3, 0)
#define HISI_PTT_TRACE_INT_MASK 0x0894
#define HISI_PTT_TRACE_INT_MASK_ALL GENMASK(3, 0)
#define HISI_PTT_TUNING_INT_STAT 0x0898
#define HISI_PTT_TUNING_INT_STAT_MASK BIT(0)
#define HISI_PTT_TRACE_WR_STS 0x08a0

View File

@ -64,6 +64,7 @@ enum coresight_dev_subtype_source {
CORESIGHT_DEV_SUBTYPE_SOURCE_PROC,
CORESIGHT_DEV_SUBTYPE_SOURCE_BUS,
CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE,
CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM,
CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS,
};