From 922ef52cea18567bd6a7e083c6acd776b5676bb6 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 17 Nov 2022 14:29:55 +0100 Subject: [PATCH 01/33] dt-bindings: soc: qcom: Add bindings for Qualcomm Ramp Controller Document the Qualcomm Ramp Controller, found on various legacy Qualcomm SoCs. Signed-off-by: AngeloGioacchino Del Regno Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20221117132956.169432-2-angelogioacchino.delregno@collabora.com --- .../qcom/qcom,msm8976-ramp-controller.yaml | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,msm8976-ramp-controller.yaml diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,msm8976-ramp-controller.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,msm8976-ramp-controller.yaml new file mode 100644 index 0000000000000..aae9cf7b8caf1 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,msm8976-ramp-controller.yaml @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/qcom/qcom,msm8976-ramp-controller.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Ramp Controller + +maintainers: + - AngeloGioacchino Del Regno + +description: + The Ramp Controller is used to program the sequence ID for pulse + swallowing, enable sequences and link Sequence IDs (SIDs) for the + CPU cores on some Qualcomm SoCs. + +properties: + compatible: + enum: + - qcom,msm8976-ramp-controller + + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + cpu-power-controller@b014000 { + compatible = "qcom,msm8976-ramp-controller"; + reg = <0x0b014000 0x68>; + }; From a723c95fa137f7a1e86dc77a150ef6e2affe977d Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 17 Nov 2022 14:29:56 +0100 Subject: [PATCH 02/33] soc: qcom: Add Qualcomm Ramp Controller driver The Ramp Controller is used to program the sequence ID for pulse swallowing, enable sequence and linking sequence IDs for the CPU cores on some Qualcomm SoCs. Signed-off-by: AngeloGioacchino Del Regno Reviewed-by: Dmitry Baryshkov Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20221117132956.169432-3-angelogioacchino.delregno@collabora.com --- drivers/soc/qcom/Kconfig | 9 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/ramp_controller.c | 342 +++++++++++++++++++++++++++++ 3 files changed, 352 insertions(+) create mode 100644 drivers/soc/qcom/ramp_controller.c diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index ae504c43d9e74..df46108e58ce7 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -96,6 +96,15 @@ config QCOM_QMI_HELPERS tristate depends on NET +config QCOM_RAMP_CTRL + tristate "Qualcomm Ramp Controller driver" + depends on ARCH_QCOM || COMPILE_TEST + help + The Ramp Controller is used to program the sequence ID for pulse + swallowing, enable sequence and link sequence IDs for the CPU + cores on some Qualcomm SoCs. + Say y here to enable support for the ramp controller. + config QCOM_RMTFS_MEM tristate "Qualcomm Remote Filesystem memory driver" depends on ARCH_QCOM diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index d66604aff2b06..6e02333c4080e 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_QCOM_OCMEM) += ocmem.o obj-$(CONFIG_QCOM_PDR_HELPERS) += pdr_interface.o obj-$(CONFIG_QCOM_QMI_HELPERS) += qmi_helpers.o qmi_helpers-y += qmi_encdec.o qmi_interface.o +obj-$(CONFIG_QCOM_RAMP_CTRL) += ramp_controller.o obj-$(CONFIG_QCOM_RMTFS_MEM) += rmtfs_mem.o obj-$(CONFIG_QCOM_RPMH) += qcom_rpmh.o qcom_rpmh-y += rpmh-rsc.o diff --git a/drivers/soc/qcom/ramp_controller.c b/drivers/soc/qcom/ramp_controller.c new file mode 100644 index 0000000000000..8f8c514b72882 --- /dev/null +++ b/drivers/soc/qcom/ramp_controller.c @@ -0,0 +1,342 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Qualcomm Ramp Controller driver + * Copyright (c) 2022, AngeloGioacchino Del Regno + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#define RC_UPDATE_EN BIT(0) +#define RC_ROOT_EN BIT(1) + +#define RC_REG_CFG_UPDATE 0x60 +#define RC_CFG_UPDATE_EN BIT(8) +#define RC_CFG_ACK GENMASK(31, 16) + +#define RC_DCVS_CFG_SID 2 +#define RC_LINK_SID 3 +#define RC_LMH_SID 6 +#define RC_DFS_SID 14 + +#define RC_UPDATE_TIMEOUT_US 500 + +/** + * struct qcom_ramp_controller_desc - SoC specific parameters + * @cfg_dfs_sid: Dynamic Frequency Scaling SID configuration + * @cfg_link_sid: Link SID configuration + * @cfg_lmh_sid: Limits Management hardware SID configuration + * @cfg_ramp_en: Ramp Controller enable sequence + * @cfg_ramp_dis: Ramp Controller disable sequence + * @cmd_reg: Command register offset + * @num_dfs_sids: Number of DFS SIDs (max 8) + * @num_link_sids: Number of Link SIDs (max 3) + * @num_lmh_sids: Number of LMh SIDs (max 8) + * @num_ramp_en: Number of entries in enable sequence + * @num_ramp_dis: Number of entries in disable sequence + */ +struct qcom_ramp_controller_desc { + const struct reg_sequence *cfg_dfs_sid; + const struct reg_sequence *cfg_link_sid; + const struct reg_sequence *cfg_lmh_sid; + const struct reg_sequence *cfg_ramp_en; + const struct reg_sequence *cfg_ramp_dis; + u8 cmd_reg; + u8 num_dfs_sids; + u8 num_link_sids; + u8 num_lmh_sids; + u8 num_ramp_en; + u8 num_ramp_dis; +}; + +/** + * struct qcom_ramp_controller - Main driver structure + * @regmap: Regmap handle + * @desc: SoC specific parameters + */ +struct qcom_ramp_controller { + struct regmap *regmap; + const struct qcom_ramp_controller_desc *desc; +}; + +/** + * rc_wait_for_update() - Wait for Ramp Controller root update + * @qrc: Main driver structure + * + * Return: Zero for success or negative number for failure + */ +static int rc_wait_for_update(struct qcom_ramp_controller *qrc) +{ + const struct qcom_ramp_controller_desc *d = qrc->desc; + struct regmap *r = qrc->regmap; + u32 val; + int ret; + + ret = regmap_set_bits(r, d->cmd_reg, RC_ROOT_EN); + if (ret) + return ret; + + return regmap_read_poll_timeout(r, d->cmd_reg, val, !(val & RC_UPDATE_EN), + 1, RC_UPDATE_TIMEOUT_US); +} + +/** + * rc_set_cfg_update() - Ramp Controller configuration update + * @qrc: Main driver structure + * @ce: Configuration entry to update + * + * Return: Zero for success or negative number for failure + */ +static int rc_set_cfg_update(struct qcom_ramp_controller *qrc, u8 ce) +{ + const struct qcom_ramp_controller_desc *d = qrc->desc; + struct regmap *r = qrc->regmap; + u32 ack, val; + int ret; + + /* The ack bit is between bits 16-31 of RC_REG_CFG_UPDATE */ + ack = FIELD_PREP(RC_CFG_ACK, BIT(ce)); + + /* Write the configuration type first... */ + ret = regmap_set_bits(r, d->cmd_reg + RC_REG_CFG_UPDATE, ce); + if (ret) + return ret; + + /* ...and after that, enable the update bit to sync the changes */ + ret = regmap_set_bits(r, d->cmd_reg + RC_REG_CFG_UPDATE, RC_CFG_UPDATE_EN); + if (ret) + return ret; + + /* Wait for the changes to go through */ + ret = regmap_read_poll_timeout(r, d->cmd_reg + RC_REG_CFG_UPDATE, val, + val & ack, 1, RC_UPDATE_TIMEOUT_US); + if (ret) + return ret; + + /* + * Configuration update success! The CFG_UPDATE register will not be + * cleared automatically upon applying the configuration, so we have + * to do that manually in order to leave the ramp controller in a + * predictable and clean state. + */ + ret = regmap_write(r, d->cmd_reg + RC_REG_CFG_UPDATE, 0); + if (ret) + return ret; + + /* Wait for the update bit cleared ack */ + return regmap_read_poll_timeout(r, d->cmd_reg + RC_REG_CFG_UPDATE, + val, !(val & RC_CFG_ACK), 1, + RC_UPDATE_TIMEOUT_US); +} + +/** + * rc_write_cfg - Send configuration sequence + * @qrc: Main driver structure + * @seq: Register sequence to send before asking for update + * @ce: Configuration SID + * @nsids: Total number of SIDs + * + * Returns: Zero for success or negative number for error + */ +static int rc_write_cfg(struct qcom_ramp_controller *qrc, + const struct reg_sequence *seq, + u16 ce, u8 nsids) +{ + int ret; + u8 i; + + /* Check if, and wait until the ramp controller is ready */ + ret = rc_wait_for_update(qrc); + if (ret) + return ret; + + /* Write the sequence */ + ret = regmap_multi_reg_write(qrc->regmap, seq, nsids); + if (ret) + return ret; + + /* Pull the trigger: do config update starting from the last sid */ + for (i = 0; i < nsids; i++) { + ret = rc_set_cfg_update(qrc, (u8)ce - i); + if (ret) + return ret; + } + + return 0; +} + +/** + * rc_ramp_ctrl_enable() - Enable Ramp up/down Control + * @qrc: Main driver structure + * + * Return: Zero for success or negative number for error + */ +static int rc_ramp_ctrl_enable(struct qcom_ramp_controller *qrc) +{ + const struct qcom_ramp_controller_desc *d = qrc->desc; + int i, ret; + + for (i = 0; i < d->num_ramp_en; i++) { + ret = rc_write_cfg(qrc, &d->cfg_ramp_en[i], RC_DCVS_CFG_SID, 1); + if (ret) + return ret; + } + + return 0; +} + +/** + * qcom_ramp_controller_start() - Initialize and start the ramp controller + * @qrc: Main driver structure + * + * The Ramp Controller needs to be initialized by programming the relevant + * registers with SoC-specific configuration: once programming is done, + * the hardware will take care of the rest (no further handling required). + * + * Return: Zero for success or negative number for error + */ +static int qcom_ramp_controller_start(struct qcom_ramp_controller *qrc) +{ + const struct qcom_ramp_controller_desc *d = qrc->desc; + int ret; + + /* Program LMH, DFS, Link SIDs */ + ret = rc_write_cfg(qrc, d->cfg_lmh_sid, RC_LMH_SID, d->num_lmh_sids); + if (ret) + return ret; + + ret = rc_write_cfg(qrc, d->cfg_dfs_sid, RC_DFS_SID, d->num_dfs_sids); + if (ret) + return ret; + + ret = rc_write_cfg(qrc, d->cfg_link_sid, RC_LINK_SID, d->num_link_sids); + if (ret) + return ret; + + /* Everything is ready! Enable the ramp up/down control */ + return rc_ramp_ctrl_enable(qrc); +} + +static const struct regmap_config qrc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x68, + .fast_io = true, +}; + +const struct reg_sequence msm8976_cfg_dfs_sid[] = { + { 0x10, 0xfefebff7 }, + { 0x14, 0xfdff7fef }, + { 0x18, 0xfbffdefb }, + { 0x1c, 0xb69b5555 }, + { 0x20, 0x24929249 }, + { 0x24, 0x49241112 }, + { 0x28, 0x11112111 }, + { 0x2c, 0x8102 } +}; + +const struct reg_sequence msm8976_cfg_link_sid[] = { + { 0x40, 0xfc987 } +}; + +const struct reg_sequence msm8976_cfg_lmh_sid[] = { + { 0x30, 0x77706db }, + { 0x34, 0x5550249 }, + { 0x38, 0x111 } +}; + +const struct reg_sequence msm8976_cfg_ramp_en[] = { + { 0x50, 0x800 }, /* pre_en */ + { 0x50, 0xc00 }, /* en */ + { 0x50, 0x400 } /* post_en */ +}; + +const struct reg_sequence msm8976_cfg_ramp_dis[] = { + { 0x50, 0x0 } +}; + +static const struct qcom_ramp_controller_desc msm8976_rc_cfg = { + .cfg_dfs_sid = msm8976_cfg_dfs_sid, + .num_dfs_sids = ARRAY_SIZE(msm8976_cfg_dfs_sid), + + .cfg_link_sid = msm8976_cfg_link_sid, + .num_link_sids = ARRAY_SIZE(msm8976_cfg_link_sid), + + .cfg_lmh_sid = msm8976_cfg_lmh_sid, + .num_lmh_sids = ARRAY_SIZE(msm8976_cfg_lmh_sid), + + .cfg_ramp_en = msm8976_cfg_ramp_en, + .num_ramp_en = ARRAY_SIZE(msm8976_cfg_ramp_en), + + .cfg_ramp_dis = msm8976_cfg_ramp_dis, + .num_ramp_dis = ARRAY_SIZE(msm8976_cfg_ramp_dis), + + .cmd_reg = 0x0, +}; + +static int qcom_ramp_controller_probe(struct platform_device *pdev) +{ + struct qcom_ramp_controller *qrc; + void __iomem *base; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + qrc = devm_kmalloc(&pdev->dev, sizeof(*qrc), GFP_KERNEL); + if (!qrc) + return -ENOMEM; + + qrc->desc = device_get_match_data(&pdev->dev); + if (!qrc) + return -EINVAL; + + qrc->regmap = devm_regmap_init_mmio(&pdev->dev, base, &qrc_regmap_config); + if (IS_ERR(qrc->regmap)) + return PTR_ERR(qrc->regmap); + + platform_set_drvdata(pdev, qrc); + + return qcom_ramp_controller_start(qrc); +} + +static int qcom_ramp_controller_remove(struct platform_device *pdev) +{ + struct qcom_ramp_controller *qrc = platform_get_drvdata(pdev); + + return rc_write_cfg(qrc, qrc->desc->cfg_ramp_dis, + RC_DCVS_CFG_SID, qrc->desc->num_ramp_dis); +} + +static const struct of_device_id qcom_ramp_controller_match_table[] = { + { .compatible = "qcom,msm8976-ramp-controller", .data = &msm8976_rc_cfg }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, qcom_ramp_controller_match_table); + +static struct platform_driver qcom_ramp_controller_driver = { + .driver = { + .name = "qcom-ramp-controller", + .of_match_table = qcom_ramp_controller_match_table, + .suppress_bind_attrs = true, + }, + .probe = qcom_ramp_controller_probe, + .remove = qcom_ramp_controller_remove, +}; + +static int __init qcom_ramp_controller_init(void) +{ + return platform_driver_register(&qcom_ramp_controller_driver); +} +arch_initcall(qcom_ramp_controller_init); + +MODULE_AUTHOR("AngeloGioacchino Del Regno "); +MODULE_DESCRIPTION("Qualcomm Ramp Controller driver"); +MODULE_LICENSE("GPL"); From 6b42133d2189d9b3770153e84111ede1e1948e0f Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 17 Nov 2022 18:04:59 +0100 Subject: [PATCH 03/33] dt-bindings: soc: qcom: convert non-smd RPM bindings to dt-schema Convert the non-SMD RPM node bindings to dt-schema, the old txt bindings are now removed since all bindings were converted. Signed-off-by: Neil Armstrong Reviewed-by: Rob Herring Acked-by: Lee Jones Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20221005-mdm9615-pinctrl-yaml-v5-2-910019fb8b9b@linaro.org --- .../devicetree/bindings/mfd/qcom-rpm.txt | 283 ------------------ .../bindings/soc/qcom/qcom,rpm.yaml | 101 +++++++ 2 files changed, 101 insertions(+), 283 deletions(-) delete mode 100644 Documentation/devicetree/bindings/mfd/qcom-rpm.txt create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,rpm.yaml diff --git a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt deleted file mode 100644 index b823b86252435..0000000000000 --- a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt +++ /dev/null @@ -1,283 +0,0 @@ -Qualcomm Resource Power Manager (RPM) - -This driver is used to interface with the Resource Power Manager (RPM) found in -various Qualcomm platforms. The RPM allows each component in the system to vote -for state of the system resources, such as clocks, regulators and bus -frequencies. - -- compatible: - Usage: required - Value type: - Definition: must be one of: - "qcom,rpm-apq8064" - "qcom,rpm-msm8660" - "qcom,rpm-msm8960" - "qcom,rpm-ipq8064" - "qcom,rpm-mdm9615" - -- reg: - Usage: required - Value type: - Definition: base address and size of the RPM's message ram - -- interrupts: - Usage: required - Value type: - Definition: three entries specifying the RPM's: - 1. acknowledgement interrupt - 2. error interrupt - 3. wakeup interrupt - -- interrupt-names: - Usage: required - Value type: - Definition: must be the three strings "ack", "err" and "wakeup", in order - -- qcom,ipc: - Usage: required - Value type: - - Definition: three entries specifying the outgoing ipc bit used for - signaling the RPM: - - phandle to a syscon node representing the apcs registers - - u32 representing offset to the register within the syscon - - u32 representing the ipc bit within the register - - -= SUBNODES - -The RPM exposes resources to its subnodes. The below bindings specify the set -of valid subnodes that can operate on these resources. - -== Regulators - -Regulator nodes are identified by their compatible: - -- compatible: - Usage: required - Value type: - Definition: must be one of: - "qcom,rpm-pm8058-regulators" - "qcom,rpm-pm8901-regulators" - "qcom,rpm-pm8921-regulators" - "qcom,rpm-pm8018-regulators" - "qcom,rpm-smb208-regulators" - -- vdd_l0_l1_lvs-supply: -- vdd_l2_l11_l12-supply: -- vdd_l3_l4_l5-supply: -- vdd_l6_l7-supply: -- vdd_l8-supply: -- vdd_l9-supply: -- vdd_l10-supply: -- vdd_l13_l16-supply: -- vdd_l14_l15-supply: -- vdd_l17_l18-supply: -- vdd_l19_l20-supply: -- vdd_l21-supply: -- vdd_l22-supply: -- vdd_l23_l24_l25-supply: -- vdd_ncp-supply: -- vdd_s0-supply: -- vdd_s1-supply: -- vdd_s2-supply: -- vdd_s3-supply: -- vdd_s4-supply: - Usage: optional (pm8058 only) - Value type: - Definition: reference to regulator supplying the input pin, as - described in the data sheet - -- lvs0_in-supply: -- lvs1_in-supply: -- lvs2_in-supply: -- lvs3_in-supply: -- mvs_in-supply: -- vdd_l0-supply: -- vdd_l1-supply: -- vdd_l2-supply: -- vdd_l3-supply: -- vdd_l4-supply: -- vdd_l5-supply: -- vdd_l6-supply: -- vdd_s0-supply: -- vdd_s1-supply: -- vdd_s2-supply: -- vdd_s3-supply: -- vdd_s4-supply: - Usage: optional (pm8901 only) - Value type: - Definition: reference to regulator supplying the input pin, as - described in the data sheet - -- vdd_l1_l2_l12_l18-supply: -- vdd_l3_l15_l17-supply: -- vdd_l4_l14-supply: -- vdd_l5_l8_l16-supply: -- vdd_l6_l7-supply: -- vdd_l9_l11-supply: -- vdd_l10_l22-supply: -- vdd_l21_l23_l29-supply: -- vdd_l24-supply: -- vdd_l25-supply: -- vdd_l26-supply: -- vdd_l27-supply: -- vdd_l28-supply: -- vdd_ncp-supply: -- vdd_s1-supply: -- vdd_s2-supply: -- vdd_s4-supply: -- vdd_s5-supply: -- vdd_s6-supply: -- vdd_s7-supply: -- vdd_s8-supply: -- vin_5vs-supply: -- vin_lvs1_3_6-supply: -- vin_lvs2-supply: -- vin_lvs4_5_7-supply: - Usage: optional (pm8921 only) - Value type: - Definition: reference to regulator supplying the input pin, as - described in the data sheet - -- vin_lvs1-supply: -- vdd_l7-supply: -- vdd_l8-supply: -- vdd_l9_l10_l11_l12-supply: - Usage: optional (pm8018 only) - Value type: - Definition: reference to regulator supplying the input pin, as - described in the data sheet - -The regulator node houses sub-nodes for each regulator within the device. Each -sub-node is identified using the node's name, with valid values listed for each -of the pmics below. - -pm8058: - l0, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, - l16, l17, l18, l19, l20, l21, l22, l23, l24, l25, s0, s1, s2, s3, s4, - lvs0, lvs1, ncp - -pm8901: - l0, l1, l2, l3, l4, l5, l6, s0, s1, s2, s3, s4, lvs0, lvs1, lvs2, lvs3, - mvs - -pm8921: - s1, s2, s3, s4, s7, s8, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, - l12, l14, l15, l16, l17, l18, l21, l22, l23, l24, l25, l26, l27, l28, - l29, lvs1, lvs2, lvs3, lvs4, lvs5, lvs6, lvs7, usb-switch, hdmi-switch, - ncp - -pm8018: - s1, s2, s3, s4, s5, , l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, - l12, l14, lvs1 - -smb208: - s1a, s1b, s2a, s2b - -The content of each sub-node is defined by the standard binding for regulators - -see regulator.txt - with additional custom properties described below: - -=== Switch-mode Power Supply regulator custom properties - -- bias-pull-down: - Usage: optional - Value type: - Definition: enable pull down of the regulator when inactive - -- qcom,switch-mode-frequency: - Usage: required - Value type: - Definition: Frequency (Hz) of the switch-mode power supply; - must be one of: - 19200000, 9600000, 6400000, 4800000, 3840000, 3200000, - 2740000, 2400000, 2130000, 1920000, 1750000, 1600000, - 1480000, 1370000, 1280000, 1200000 - -- qcom,force-mode: - Usage: optional (default if no other qcom,force-mode is specified) - Value type: - Definition: indicates that the regulator should be forced to a - particular mode, valid values are: - QCOM_RPM_FORCE_MODE_NONE - do not force any mode - QCOM_RPM_FORCE_MODE_LPM - force into low power mode - QCOM_RPM_FORCE_MODE_HPM - force into high power mode - QCOM_RPM_FORCE_MODE_AUTO - allow regulator to automatically - select its own mode based on - realtime current draw, only for: - pm8921 smps and ftsmps - -- qcom,power-mode-hysteretic: - Usage: optional - Value type: - Definition: select that the power supply should operate in hysteretic - mode, instead of the default pwm mode - -=== Low-dropout regulator custom properties - -- bias-pull-down: - Usage: optional - Value type: - Definition: enable pull down of the regulator when inactive - -- qcom,force-mode: - Usage: optional - Value type: - Definition: indicates that the regulator should not be forced to any - particular mode, valid values are: - QCOM_RPM_FORCE_MODE_NONE - do not force any mode - QCOM_RPM_FORCE_MODE_LPM - force into low power mode - QCOM_RPM_FORCE_MODE_HPM - force into high power mode - QCOM_RPM_FORCE_MODE_BYPASS - set regulator to use bypass - mode, i.e. to act as a switch - and not regulate, only for: - pm8921 pldo, nldo and nldo1200 - -=== Negative Charge Pump custom properties - -- qcom,switch-mode-frequency: - Usage: required - Value type: - Definition: Frequency (Hz) of the switch mode power supply; - must be one of: - 19200000, 9600000, 6400000, 4800000, 3840000, 3200000, - 2740000, 2400000, 2130000, 1920000, 1750000, 1600000, - 1480000, 1370000, 1280000, 1200000 - -= EXAMPLE - - #include - - rpm@108000 { - compatible = "qcom,rpm-msm8960"; - reg = <0x108000 0x1000>; - qcom,ipc = <&apcs 0x8 2>; - - interrupts = <0 19 0>, <0 21 0>, <0 22 0>; - interrupt-names = "ack", "err", "wakeup"; - - regulators { - compatible = "qcom,rpm-pm8921-regulators"; - vdd_l1_l2_l12_l18-supply = <&pm8921_s4>; - - s1 { - regulator-min-microvolt = <1225000>; - regulator-max-microvolt = <1225000>; - - bias-pull-down; - - qcom,switch-mode-frequency = <3200000>; - }; - - pm8921_s4: s4 { - regulator-min-microvolt = <1800000>; - regulator-max-microvolt = <1800000>; - - qcom,switch-mode-frequency = <1600000>; - bias-pull-down; - - qcom,force-mode = ; - }; - }; - }; - diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,rpm.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,rpm.yaml new file mode 100644 index 0000000000000..b00be9e01206d --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,rpm.yaml @@ -0,0 +1,101 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/qcom/qcom,rpm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Resource Power Manager (RPM) + +description: + This driver is used to interface with the Resource Power Manager (RPM) found + in various Qualcomm platforms. The RPM allows each component in the system + to vote for state of the system resources, such as clocks, regulators and bus + frequencies. + +maintainers: + - Bjorn Andersson + +properties: + compatible: + enum: + - qcom,rpm-apq8064 + - qcom,rpm-msm8660 + - qcom,rpm-msm8960 + - qcom,rpm-ipq8064 + - qcom,rpm-mdm9615 + + reg: + maxItems: 1 + + interrupts: + maxItems: 3 + + interrupt-names: + items: + - const: ack + - const: err + - const: wakeup + + qcom,ipc: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to a syscon node representing the APCS registers + - description: u32 representing offset to the register within the syscon + - description: u32 representing the ipc bit within the register + description: + Three entries specifying the outgoing ipc bit used for signaling the RPM. + +patternProperties: + "^regulators(-[01])?$": + type: object + $ref: /schemas/regulator/qcom,rpm-regulator.yaml# + unevaluatedProperties: false + +required: + - compatible + - reg + - interrupts + - interrupt-names + - qcom,ipc + +additionalProperties: false + +examples: + - | + #include + #include + #include + + rpm@108000 { + compatible = "qcom,rpm-msm8960"; + reg = <0x108000 0x1000>; + qcom,ipc = <&apcs 0x8 2>; + + interrupts = , , ; + interrupt-names = "ack", "err", "wakeup"; + + regulators { + compatible = "qcom,rpm-pm8921-regulators"; + vdd_l1_l2_l12_l18-supply = <&pm8921_s4>; + + s1 { + regulator-min-microvolt = <1225000>; + regulator-max-microvolt = <1225000>; + + bias-pull-down; + + qcom,switch-mode-frequency = <3200000>; + }; + + pm8921_s4: s4 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + + qcom,switch-mode-frequency = <1600000>; + bias-pull-down; + + qcom,force-mode = ; + }; + }; + }; From 417091dc60ba0a991c0e6aa77c1eeb2cfcf0a3d4 Mon Sep 17 00:00:00 2001 From: Souradeep Chowdhury Date: Tue, 27 Dec 2022 20:52:45 +0530 Subject: [PATCH 04/33] dt-bindings: soc: qcom,dcc: Add the dtschema Add the device tree bindings for Data Capture and Compare(DCC). Signed-off-by: Souradeep Chowdhury Reviewed-by: Rob Herring Reviewed-by: Bjorn Andersson Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/eb758cbdc7bd0973b3ca154b11fb8db54cde805f.1672148732.git.quic_schowdhu@quicinc.com --- .../bindings/soc/qcom/qcom,dcc.yaml | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml new file mode 100644 index 0000000000000..ce7e20dd22c97 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/qcom/qcom,dcc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Data Capture and Compare + +maintainers: + - Souradeep Chowdhury + +description: | + DCC (Data Capture and Compare) is a DMA engine which is used to save + configuration data or system memory contents during catastrophic failure + or SW trigger. DCC is used to capture and store data for debugging purpose + +properties: + compatible: + items: + - enum: + - qcom,sm8150-dcc + - qcom,sc7280-dcc + - qcom,sc7180-dcc + - qcom,sdm845-dcc + - const: qcom,dcc + + reg: + items: + - description: DCC base + - description: DCC RAM base + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + dma@10a2000{ + compatible = "qcom,sm8150-dcc", "qcom,dcc"; + reg = <0x010a2000 0x1000>, + <0x010ad000 0x2000>; + }; From 4cbe60cf5ad622f7f45ccc4fa369c9f7a71903b9 Mon Sep 17 00:00:00 2001 From: Souradeep Chowdhury Date: Tue, 27 Dec 2022 20:52:46 +0530 Subject: [PATCH 05/33] soc: qcom: dcc: Add driver support for Data Capture and Compare unit(DCC) The DCC is a DMA Engine designed to capture and store data during system crash or software triggers. The DCC operates based on user inputs via the debugfs interface. The user gives addresses as inputs and these addresses are stored in the dcc sram. In case of a system crash or a manual software trigger by the user through the debugfs interface, the dcc captures and stores the values at these addresses. This patch contains the driver which has all the methods pertaining to the debugfs interface, auxiliary functions to support all the four fundamental operations of dcc namely read, write, read/modify/write and loop. The probe method here instantiates all the resources necessary for dcc to operate mainly the dedicated dcc sram where it stores the values. The DCC driver can be used for debugging purposes without going for a reboot since it can perform software triggers as well based on user inputs. Also add the documentation for debugfs entries which explains the functionalities of each debugfs file that has been created for dcc. The following is the justification of using debugfs interface over the other alternatives like sysfs/ioctls i) As can be seen from the debugfs attribute descriptions, some of the debugfs attribute files here contains multiple arguments which needs to be accepted from the user. This goes against the design style of sysfs. ii) The user input patterns have been made simple and convenient in this case with the use of debugfs interface as user doesn't need to shuffle between different files to execute one instruction as was the case on using other alternatives. Signed-off-by: Souradeep Chowdhury Reviewed-by: Alex Elder [bjorn: Fixed up a few indents and line wraps] Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/644b4f66a358492a8a6738454035c3b120092fe7.1672148732.git.quic_schowdhu@quicinc.com --- Documentation/ABI/testing/debugfs-driver-dcc | 98 ++ drivers/soc/qcom/Kconfig | 8 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/dcc.c | 1299 ++++++++++++++++++ 4 files changed, 1406 insertions(+) create mode 100644 Documentation/ABI/testing/debugfs-driver-dcc create mode 100644 drivers/soc/qcom/dcc.c diff --git a/Documentation/ABI/testing/debugfs-driver-dcc b/Documentation/ABI/testing/debugfs-driver-dcc new file mode 100644 index 0000000000000..a468095020593 --- /dev/null +++ b/Documentation/ABI/testing/debugfs-driver-dcc @@ -0,0 +1,98 @@ +What: /sys/kernel/debug/dcc/.../ready +Date: December 2022 +Contact: Souradeep Chowdhury +Description: + This file is used to check the status of the dcc + hardware if it's ready to take the inputs. A 'Y' + here indicates dcc is in a ready condition. + Example: + cat /sys/kernel/debug/dcc/.../ready + +What: /sys/kernel/debug/dcc/.../trigger +Date: December 2022 +Contact: Souradeep Chowdhury +Description: + This is the debugfs interface for manual software + triggers. The user can simply enter a 1 against + the debugfs file and enable a manual trigger. + Example: + echo 1 > /sys/kernel/debug/dcc/.../trigger + +What: /sys/kernel/debug/dcc/.../config_reset +Date: December 2022 +Contact: Souradeep Chowdhury +Description: + This file is used to reset the configuration of + a dcc driver to the default configuration. This + means that all the previous addresses stored in + the driver gets removed and user needs to enter + the address values from the start. + Example: + echo 1 > /sys/kernel/debug/dcc/../config_reset + +What: /sys/kernel/debug/dcc/.../[list-number]/config +Date: December 2022 +Contact: Souradeep Chowdhury +Description: + This stores the addresses of the registers which + should be read in case of a hardware crash or + manual software triggers. The addresses entered here + are considered under all the 4 types of dcc + instructions Read type, Write type, Read Modify Write + type and Loop type. The lists need to be configured + sequentially and not in a overlapping manner. As an + example user can jump to list x only after list y is + configured and enabled. The format for entering all + types of instructions are explained in examples as + follows. + Example: + i)Read Type Instruction + echo R <1> <2> <3> >/sys/kernel/debug/dcc/../[list-number]/config + 1->Address to be considered for reading the value. + 2->The word count of the addresses, read n words + starting from address <1>. Each word is of 32 bits. + If not entered 1 is considered. + 3->Can be 'apb' or 'ahb' which indicates if it is apb or ahb + bus respectively. If not entered ahb is considered. + ii)Write Type Instruction + echo W <1> <2> <3> > /sys/kernel/debug/dcc/../[list-number]/config + 1->Address to be considered for writing the value. + 2->The value that needs to be written at the location. + 3->Can be a 'apb' or 'ahb' which indicates if it is apb or ahb + but respectively. + iii)Read Modify Write type instruction + echo RW <1> <2> <3> > /sys/kernel/debug/dcc/../[list-number]/config + 1->The address which needs to be considered for read then write. + 2->The value that needs to be written on the address. + 3->The mask of the value to be written. + iv)Loop Type Instruction + echo L <1> <2> <3> > /sys/kernel/debug/dcc/../[list-number]/config + 1->The loop count, the number of times the value of the addresses will be + captured. + 2->The address count, total number of addresses to be entered in this + instruction. + 3->The series of addresses to be entered separated by a space like + ... and so on. + +What: /sys/kernel/debug/dcc/.../[list-number]/enable +Date: December 2022 +Contact: Souradeep Chowdhury +Description: + This debugfs interface is used for enabling the + the dcc hardware. Enable file is kept under the + directory list number for which the user wants + to enable it. For example if the user wants to + enable list 1, then he should go for + echo 1 > /sys/kernel/debug/dcc/.../1/enable. + On enabling the dcc, all the addresses entered + by the user for the corresponding list is written + into dcc sram which is read by the dcc hardware + on manual or crash induced triggers. Lists should + be enabled sequentially.For example after configuring + addresses for list 1 and enabling it, a user can + proceed to enable list 2 or vice versa. + Example: + echo 0 > /sys/kernel/debug/dcc/.../[list-number]/enable + (disable dcc for the corresponding list number) + echo 1 > /sys/kernel/debug/dcc/.../[list-number]/enable + (enable dcc for the corresponding list number) diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index df46108e58ce7..21c4ce2315ba1 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -70,6 +70,14 @@ config QCOM_LLCC SDM845. This provides interfaces to clients that use the LLCC. Say yes here to enable LLCC slice driver. +config QCOM_DCC + tristate "Qualcomm Technologies, Inc. Data Capture and Compare(DCC) engine driver" + depends on ARCH_QCOM || COMPILE_TEST + help + This option enables driver for Data Capture and Compare engine. DCC + driver provides interface to configure DCC block and read back + captured data from DCC's internal SRAM. + config QCOM_KRYO_L2_ACCESSORS bool depends on ARCH_QCOM && ARM64 || COMPILE_TEST diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 6e02333c4080e..3b92c6c8e0ecf 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_QCOM_AOSS_QMP) += qcom_aoss.o obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o obj-$(CONFIG_QCOM_CPR) += cpr.o +obj-$(CONFIG_QCOM_DCC) += dcc.o obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o obj-$(CONFIG_QCOM_OCMEM) += ocmem.o diff --git a/drivers/soc/qcom/dcc.c b/drivers/soc/qcom/dcc.c new file mode 100644 index 0000000000000..d4101f79cb5df --- /dev/null +++ b/drivers/soc/qcom/dcc.c @@ -0,0 +1,1299 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STATUS_READY_TIMEOUT 5000 /* microseconds */ + +#define DCC_SRAM_NODE "dcc_sram" + +/* DCC registers */ +#define DCC_HW_INFO 0x04 +#define DCC_LL_NUM_INFO 0x10 +#define DCC_STATUS(vers) ((vers) == 1 ? 0x0c : 0x1c) +#define DCC_LL_LOCK 0x00 +#define DCC_LL_CFG 0x04 +#define DCC_LL_BASE 0x08 +#define DCC_FD_BASE 0x0c +#define DCC_LL_TIMEOUT 0x10 +#define DCC_LL_INT_ENABLE 0x18 +#define DCC_LL_INT_STATUS 0x1c +#define DCC_LL_SW_TRIGGER 0x2c +#define DCC_LL_BUS_ACCESS_STATUS 0x30 + +/* Default value used if a bit 6 in the HW_INFO register is set. */ +#define DCC_FIX_LOOP_OFFSET 16 + +/* Mask to find version info from HW_Info register */ +#define DCC_VER_INFO_MASK BIT(9) + +#define MAX_DCC_OFFSET GENMASK(9, 2) +#define MAX_DCC_LEN GENMASK(6, 0) +#define MAX_LOOP_CNT GENMASK(7, 0) +#define MAX_LOOP_ADDR 10 + +#define DCC_ADDR_DESCRIPTOR 0x00 +#define DCC_ADDR_LIMIT 27 +#define DCC_WORD_SIZE sizeof(u32) +#define DCC_ADDR_RANGE_MASK GENMASK(31, 4) +#define DCC_LOOP_DESCRIPTOR BIT(30) +#define DCC_RD_MOD_WR_DESCRIPTOR BIT(31) +#define DCC_LINK_DESCRIPTOR GENMASK(31, 30) +#define DCC_STATUS_MASK GENMASK(1, 0) +#define DCC_LOCK_MASK BIT(0) +#define DCC_LOOP_OFFSET_MASK BIT(6) +#define DCC_TRIGGER_MASK BIT(9) + +#define DCC_WRITE_MASK BIT(15) +#define DCC_WRITE_OFF_MASK GENMASK(7, 0) +#define DCC_WRITE_LEN_MASK GENMASK(14, 8) + +#define DCC_READ_IND 0x00 +#define DCC_WRITE_IND (BIT(28)) + +#define DCC_AHB_IND 0x00 +#define DCC_APB_IND BIT(29) + +#define DCC_MAX_LINK_LIST 8 + +#define DCC_VER_MASK2 GENMASK(5, 0) + +#define DCC_SRAM_WORD_LENGTH 4 + +#define DCC_RD_MOD_WR_ADDR 0xc105e + +enum dcc_descriptor_type { + DCC_READ_TYPE, + DCC_LOOP_TYPE, + DCC_READ_WRITE_TYPE, + DCC_WRITE_TYPE +}; + +struct dcc_config_entry { + u32 base; + u32 offset; + u32 len; + u32 loop_cnt; + u32 write_val; + u32 mask; + bool apb_bus; + enum dcc_descriptor_type desc_type; + struct list_head list; +}; + +/** + * struct dcc_drvdata - configuration information related to a dcc device + * @base: Base Address of the dcc device + * @dev: The device attached to the driver data + * @mutex: Lock to protect access and manipulation of dcc_drvdata + * @ram_base: Base address for the SRAM dedicated for the dcc device + * @ram_size: Total size of the SRAM dedicated for the dcc device + * @ram_offset: Offset to the SRAM dedicated for dcc device + * @mem_map_ver: Memory map version of DCC hardware + * @ram_cfg: Used for address limit calculation for dcc + * @ram_start: Starting address of DCC SRAM + * @sram_dev: Miscellaneous device equivalent of dcc SRAM + * @cfg_head: Points to the head of the linked list of addresses + * @dbg_dir: The dcc debugfs directory under which all the debugfs files are placed + * @nr_link_list: Total number of linkedlists supported by the DCC configuration + * @loop_shift: Loop offset bits range for the addresses + * @enable_bitmap: Bitmap to capture the enabled status of each linked list of addresses + */ +struct dcc_drvdata { + void __iomem *base; + void __iomem *ram_base; + struct device *dev; + struct mutex mutex; + size_t ram_size; + size_t ram_offset; + int mem_map_ver; + unsigned int ram_cfg; + unsigned int ram_start; + struct miscdevice sram_dev; + struct list_head *cfg_head; + struct dentry *dbg_dir; + size_t nr_link_list; + u8 loop_shift; + unsigned long *enable_bitmap; +}; + +struct dcc_cfg_attr { + u32 addr; + u32 prev_addr; + u32 prev_off; + u32 link; + u32 sram_offset; +}; + +struct dcc_cfg_loop_attr { + u32 loop_cnt; + u32 loop_len; + u32 loop_off; + bool loop_start; +}; + +static inline u32 dcc_list_offset(int version) +{ + return version == 1 ? 0x1c : version == 2 ? 0x2c : 0x34; +} + +static inline void dcc_list_writel(struct dcc_drvdata *drvdata, + u32 ll, u32 val, u32 off) +{ + u32 offset = dcc_list_offset(drvdata->mem_map_ver) + off; + + writel(val, drvdata->base + ll * 0x80 + offset); +} + +static inline u32 dcc_list_readl(struct dcc_drvdata *drvdata, u32 ll, u32 off) +{ + u32 offset = dcc_list_offset(drvdata->mem_map_ver) + off; + + return readl(drvdata->base + ll * 0x80 + offset); +} + +static void dcc_sram_write_auto(struct dcc_drvdata *drvdata, + u32 val, u32 *off) +{ + /* If the overflow condition is met increment the offset + * and return to indicate that overflow has occurred + */ + if (unlikely(*off > drvdata->ram_size - 4)) { + *off += 4; + return; + } + + writel(val, drvdata->ram_base + *off); + + *off += 4; +} + +static int dcc_sw_trigger(struct dcc_drvdata *drvdata) +{ + void __iomem *addr; + int ret = 0; + int i; + u32 status; + u32 ll_cfg; + u32 tmp_ll_cfg; + u32 val; + + mutex_lock(&drvdata->mutex); + + for (i = 0; i < drvdata->nr_link_list; i++) { + if (!test_bit(i, drvdata->enable_bitmap)) + continue; + ll_cfg = dcc_list_readl(drvdata, i, DCC_LL_CFG); + tmp_ll_cfg = ll_cfg & ~DCC_TRIGGER_MASK; + dcc_list_writel(drvdata, tmp_ll_cfg, i, DCC_LL_CFG); + dcc_list_writel(drvdata, 1, i, DCC_LL_SW_TRIGGER); + dcc_list_writel(drvdata, ll_cfg, i, DCC_LL_CFG); + } + + addr = drvdata->base + DCC_STATUS(drvdata->mem_map_ver); + if (readl_poll_timeout(addr, val, !FIELD_GET(DCC_STATUS_MASK, val), + 1, STATUS_READY_TIMEOUT)) { + dev_err(drvdata->dev, "DCC is busy after receiving sw trigger\n"); + ret = -EBUSY; + goto out_unlock; + } + + for (i = 0; i < drvdata->nr_link_list; i++) { + if (!test_bit(i, drvdata->enable_bitmap)) + continue; + + status = dcc_list_readl(drvdata, i, DCC_LL_BUS_ACCESS_STATUS); + if (!status) + continue; + + dev_err(drvdata->dev, "Read access error for list %d err: 0x%x\n", + i, status); + ll_cfg = dcc_list_readl(drvdata, i, DCC_LL_CFG); + tmp_ll_cfg = ll_cfg & ~DCC_TRIGGER_MASK; + dcc_list_writel(drvdata, tmp_ll_cfg, i, DCC_LL_CFG); + dcc_list_writel(drvdata, DCC_STATUS_MASK, i, DCC_LL_BUS_ACCESS_STATUS); + dcc_list_writel(drvdata, ll_cfg, i, DCC_LL_CFG); + ret = -ENODATA; + break; + } + +out_unlock: + mutex_unlock(&drvdata->mutex); + return ret; +} + +static void dcc_ll_cfg_reset_link(struct dcc_cfg_attr *cfg) +{ + cfg->addr = 0x00; + cfg->link = 0; + cfg->prev_off = 0; + cfg->prev_addr = cfg->addr; +} + +static void dcc_emit_read_write(struct dcc_drvdata *drvdata, + struct dcc_config_entry *entry, + struct dcc_cfg_attr *cfg) +{ + if (cfg->link) { + /* + * write new offset = 1 to continue + * processing the list + */ + + dcc_sram_write_auto(drvdata, cfg->link, &cfg->sram_offset); + + /* Reset link and prev_off */ + dcc_ll_cfg_reset_link(cfg); + } + + cfg->addr = DCC_RD_MOD_WR_DESCRIPTOR; + dcc_sram_write_auto(drvdata, cfg->addr, &cfg->sram_offset); + + dcc_sram_write_auto(drvdata, entry->mask, &cfg->sram_offset); + + dcc_sram_write_auto(drvdata, entry->write_val, &cfg->sram_offset); + + cfg->addr = 0; +} + +static void dcc_emit_loop(struct dcc_drvdata *drvdata, struct dcc_config_entry *entry, + struct dcc_cfg_attr *cfg, + struct dcc_cfg_loop_attr *cfg_loop, + u32 *total_len) +{ + int loop; + + /* Check if we need to write link of prev entry */ + if (cfg->link) + dcc_sram_write_auto(drvdata, cfg->link, &cfg->sram_offset); + + if (cfg_loop->loop_start) { + loop = (cfg->sram_offset - cfg_loop->loop_off) / 4; + loop |= (cfg_loop->loop_cnt << drvdata->loop_shift) & + GENMASK(DCC_ADDR_LIMIT, drvdata->loop_shift); + loop |= DCC_LOOP_DESCRIPTOR; + *total_len += (*total_len - cfg_loop->loop_len) * cfg_loop->loop_cnt; + + dcc_sram_write_auto(drvdata, loop, &cfg->sram_offset); + + cfg_loop->loop_start = false; + cfg_loop->loop_len = 0; + cfg_loop->loop_off = 0; + } else { + cfg_loop->loop_start = true; + cfg_loop->loop_cnt = entry->loop_cnt - 1; + cfg_loop->loop_len = *total_len; + cfg_loop->loop_off = cfg->sram_offset; + } + + /* Reset link and prev_off */ + dcc_ll_cfg_reset_link(cfg); +} + +static void dcc_emit_write(struct dcc_drvdata *drvdata, + struct dcc_config_entry *entry, + struct dcc_cfg_attr *cfg) +{ + u32 off; + + if (cfg->link) { + /* + * write new offset = 1 to continue + * processing the list + */ + dcc_sram_write_auto(drvdata, cfg->link, &cfg->sram_offset); + + /* Reset link and prev_off */ + cfg->addr = 0x00; + cfg->prev_off = 0; + cfg->prev_addr = cfg->addr; + } + + off = entry->offset / 4; + /* write new offset-length pair to correct position */ + cfg->link |= ((off & DCC_WRITE_OFF_MASK) | DCC_WRITE_MASK | + FIELD_PREP(DCC_WRITE_LEN_MASK, entry->len)); + cfg->link |= DCC_LINK_DESCRIPTOR; + + /* Address type */ + cfg->addr = (entry->base >> 4) & GENMASK(DCC_ADDR_LIMIT, 0); + if (entry->apb_bus) + cfg->addr |= DCC_ADDR_DESCRIPTOR | DCC_WRITE_IND | DCC_APB_IND; + else + cfg->addr |= DCC_ADDR_DESCRIPTOR | DCC_WRITE_IND | DCC_AHB_IND; + dcc_sram_write_auto(drvdata, cfg->addr, &cfg->sram_offset); + + dcc_sram_write_auto(drvdata, cfg->link, &cfg->sram_offset); + + dcc_sram_write_auto(drvdata, entry->write_val, &cfg->sram_offset); + + cfg->addr = 0x00; + cfg->link = 0; +} + +static int dcc_emit_read(struct dcc_drvdata *drvdata, + struct dcc_config_entry *entry, + struct dcc_cfg_attr *cfg, + u32 *pos, u32 *total_len) +{ + u32 off; + u32 temp_off; + + cfg->addr = (entry->base >> 4) & GENMASK(27, 0); + + if (entry->apb_bus) + cfg->addr |= DCC_ADDR_DESCRIPTOR | DCC_READ_IND | DCC_APB_IND; + else + cfg->addr |= DCC_ADDR_DESCRIPTOR | DCC_READ_IND | DCC_AHB_IND; + + off = entry->offset / 4; + + *total_len += entry->len * 4; + + if (!cfg->prev_addr || cfg->prev_addr != cfg->addr || cfg->prev_off > off) { + /* Check if we need to write prev link entry */ + if (cfg->link) + dcc_sram_write_auto(drvdata, cfg->link, &cfg->sram_offset); + dev_dbg(drvdata->dev, "DCC: sram address 0x%x\n", cfg->sram_offset); + + /* Write address */ + dcc_sram_write_auto(drvdata, cfg->addr, &cfg->sram_offset); + + /* Reset link and prev_off */ + cfg->link = 0; + cfg->prev_off = 0; + } + + if ((off - cfg->prev_off) > 0xff || entry->len > MAX_DCC_LEN) { + dev_err(drvdata->dev, "DCC: Programming error Base: 0x%x, offset 0x%x\n", + entry->base, entry->offset); + return -EINVAL; + } + + if (cfg->link) { + /* + * link already has one offset-length so new + * offset-length needs to be placed at + * bits [29:15] + */ + *pos = 15; + + /* Clear bits [31:16] */ + cfg->link &= GENMASK(14, 0); + } else { + /* + * link is empty, so new offset-length needs + * to be placed at bits [15:0] + */ + *pos = 0; + cfg->link = 1 << 15; + } + + /* write new offset-length pair to correct position */ + temp_off = (off - cfg->prev_off) & GENMASK(7, 0); + cfg->link |= temp_off | ((entry->len << 8) & GENMASK(14, 8)) << *pos; + + cfg->link |= DCC_LINK_DESCRIPTOR; + + if (*pos) { + dcc_sram_write_auto(drvdata, cfg->link, &cfg->sram_offset); + cfg->link = 0; + } + + cfg->prev_off = off + entry->len - 1; + cfg->prev_addr = cfg->addr; + return 0; +} + +static int dcc_emit_config(struct dcc_drvdata *drvdata, unsigned int curr_list) +{ + int ret; + u32 total_len, pos; + struct dcc_config_entry *entry; + struct dcc_cfg_attr cfg; + struct dcc_cfg_loop_attr cfg_loop; + + memset(&cfg, 0, sizeof(cfg)); + memset(&cfg_loop, 0, sizeof(cfg_loop)); + cfg.sram_offset = drvdata->ram_cfg * 4; + total_len = 0; + + list_for_each_entry(entry, &drvdata->cfg_head[curr_list], list) { + switch (entry->desc_type) { + case DCC_READ_WRITE_TYPE: + dcc_emit_read_write(drvdata, entry, &cfg); + break; + + case DCC_LOOP_TYPE: + dcc_emit_loop(drvdata, entry, &cfg, &cfg_loop, &total_len); + break; + + case DCC_WRITE_TYPE: + dcc_emit_write(drvdata, entry, &cfg); + break; + + case DCC_READ_TYPE: + ret = dcc_emit_read(drvdata, entry, &cfg, &pos, &total_len); + if (ret) + goto err; + break; + } + } + + if (cfg.link) + dcc_sram_write_auto(drvdata, cfg.link, &cfg.sram_offset); + + if (cfg_loop.loop_start) { + dev_err(drvdata->dev, "DCC: Programming error: Loop unterminated\n"); + ret = -EINVAL; + goto err; + } + + /* Handling special case of list ending with a rd_mod_wr */ + if (cfg.addr == DCC_RD_MOD_WR_DESCRIPTOR) { + cfg.addr = (DCC_RD_MOD_WR_ADDR) & GENMASK(27, 0); + cfg.addr |= DCC_ADDR_DESCRIPTOR; + dcc_sram_write_auto(drvdata, cfg.addr, &cfg.sram_offset); + } + + /* Setting zero to indicate end of the list */ + cfg.link = DCC_LINK_DESCRIPTOR; + dcc_sram_write_auto(drvdata, cfg.link, &cfg.sram_offset); + + /* Check if sram offset exceeds the ram size */ + if (cfg.sram_offset > drvdata->ram_size) + goto overstep; + + /* Update ram_cfg and check if the data will overstep */ + drvdata->ram_cfg = (cfg.sram_offset + total_len) / 4; + + if (cfg.sram_offset + total_len > drvdata->ram_size) { + cfg.sram_offset += total_len; + goto overstep; + } + + drvdata->ram_start = cfg.sram_offset / 4; + return 0; +overstep: + ret = -EINVAL; + memset_io(drvdata->ram_base, 0, drvdata->ram_size); + +err: + return ret; +} + +static bool dcc_valid_list(struct dcc_drvdata *drvdata, unsigned int curr_list) +{ + u32 lock_reg; + + if (list_empty(&drvdata->cfg_head[curr_list])) + return false; + + if (test_bit(curr_list, drvdata->enable_bitmap)) { + dev_err(drvdata->dev, "List %d is already enabled\n", curr_list); + return false; + } + + lock_reg = dcc_list_readl(drvdata, curr_list, DCC_LL_LOCK); + if (lock_reg & DCC_LOCK_MASK) { + dev_err(drvdata->dev, "List %d is already locked\n", curr_list); + return false; + } + + return true; +} + +static bool is_dcc_enabled(struct dcc_drvdata *drvdata) +{ + int list; + + for (list = 0; list < drvdata->nr_link_list; list++) + if (test_bit(list, drvdata->enable_bitmap)) + return true; + + return false; +} + +static int dcc_enable(struct dcc_drvdata *drvdata, unsigned int curr_list) +{ + int ret; + u32 ram_cfg_base; + + mutex_lock(&drvdata->mutex); + + if (!dcc_valid_list(drvdata, curr_list)) { + ret = -EINVAL; + goto out_unlock; + } + + /* Fill dcc sram with the poison value. + * This helps in understanding bus + * hang from registers returning a zero + */ + if (!is_dcc_enabled(drvdata)) + memset_io(drvdata->ram_base, 0xde, drvdata->ram_size); + + /* 1. Take ownership of the list */ + dcc_list_writel(drvdata, DCC_LOCK_MASK, curr_list, DCC_LL_LOCK); + + /* 2. Program linked-list in the SRAM */ + ram_cfg_base = drvdata->ram_cfg; + ret = dcc_emit_config(drvdata, curr_list); + if (ret) { + dcc_list_writel(drvdata, 0, curr_list, DCC_LL_LOCK); + goto out_unlock; + } + + /* 3. Program DCC_RAM_CFG reg */ + dcc_list_writel(drvdata, ram_cfg_base + + drvdata->ram_offset / 4, curr_list, DCC_LL_BASE); + dcc_list_writel(drvdata, drvdata->ram_start + + drvdata->ram_offset / 4, curr_list, DCC_FD_BASE); + dcc_list_writel(drvdata, 0xFFF, curr_list, DCC_LL_TIMEOUT); + + /* 4. Clears interrupt status register */ + dcc_list_writel(drvdata, 0, curr_list, DCC_LL_INT_ENABLE); + dcc_list_writel(drvdata, (BIT(0) | BIT(1) | BIT(2)), + curr_list, DCC_LL_INT_STATUS); + + set_bit(curr_list, drvdata->enable_bitmap); + + /* 5. Configure trigger */ + dcc_list_writel(drvdata, DCC_TRIGGER_MASK, + curr_list, DCC_LL_CFG); + +out_unlock: + mutex_unlock(&drvdata->mutex); + return ret; +} + +static void dcc_disable(struct dcc_drvdata *drvdata, int curr_list) +{ + mutex_lock(&drvdata->mutex); + + if (!test_bit(curr_list, drvdata->enable_bitmap)) + goto out_unlock; + dcc_list_writel(drvdata, 0, curr_list, DCC_LL_CFG); + dcc_list_writel(drvdata, 0, curr_list, DCC_LL_BASE); + dcc_list_writel(drvdata, 0, curr_list, DCC_FD_BASE); + dcc_list_writel(drvdata, 0, curr_list, DCC_LL_LOCK); + clear_bit(curr_list, drvdata->enable_bitmap); +out_unlock: + mutex_unlock(&drvdata->mutex); +} + +static u32 dcc_filp_curr_list(const struct file *filp) +{ + struct dentry *dentry = file_dentry(filp); + int curr_list, ret; + + ret = kstrtoint(dentry->d_parent->d_name.name, 0, &curr_list); + if (ret) + return ret; + + return curr_list; +} + +static ssize_t enable_read(struct file *filp, char __user *userbuf, + size_t count, loff_t *ppos) +{ + char *buf; + struct dcc_drvdata *drvdata = filp->private_data; + + mutex_lock(&drvdata->mutex); + + if (is_dcc_enabled(drvdata)) + buf = "Y\n"; + else + buf = "N\n"; + + mutex_unlock(&drvdata->mutex); + + return simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf)); +} + +static ssize_t enable_write(struct file *filp, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + int ret = 0, curr_list; + bool val; + struct dcc_drvdata *drvdata = filp->private_data; + + curr_list = dcc_filp_curr_list(filp); + if (curr_list < 0) + return curr_list; + + ret = kstrtobool_from_user(userbuf, count, &val); + if (ret < 0) + return ret; + + if (val) { + ret = dcc_enable(drvdata, curr_list); + if (ret) + return ret; + } else { + dcc_disable(drvdata, curr_list); + } + + return count; +} + +static const struct file_operations enable_fops = { + .read = enable_read, + .write = enable_write, + .open = simple_open, + .llseek = generic_file_llseek, +}; + +static ssize_t trigger_write(struct file *filp, + const char __user *user_buf, size_t count, + loff_t *ppos) +{ + int ret; + unsigned int val; + struct dcc_drvdata *drvdata = filp->private_data; + + ret = kstrtouint_from_user(user_buf, count, 0, &val); + if (ret < 0) + return ret; + + if (val != 1) + return -EINVAL; + + ret = dcc_sw_trigger(drvdata); + if (ret < 0) + return ret; + + return count; +} + +static const struct file_operations trigger_fops = { + .write = trigger_write, + .open = simple_open, + .llseek = generic_file_llseek, +}; + +static int dcc_config_add(struct dcc_drvdata *drvdata, unsigned int addr, + unsigned int len, bool apb_bus, int curr_list) +{ + int ret = 0; + struct dcc_config_entry *entry, *pentry; + unsigned int base, offset; + + mutex_lock(&drvdata->mutex); + + if (!len || len > drvdata->ram_size / DCC_WORD_SIZE) { + dev_err(drvdata->dev, "DCC: Invalid length\n"); + ret = -EINVAL; + goto out_unlock; + } + + base = addr & DCC_ADDR_RANGE_MASK; + + if (!list_empty(&drvdata->cfg_head[curr_list])) { + pentry = list_last_entry(&drvdata->cfg_head[curr_list], + struct dcc_config_entry, list); + + if (pentry->desc_type == DCC_READ_TYPE && + addr >= (pentry->base + pentry->offset) && + addr <= (pentry->base + pentry->offset + MAX_DCC_OFFSET)) { + /* Re-use base address from last entry */ + base = pentry->base; + + if ((pentry->len * 4 + pentry->base + pentry->offset) + == addr) { + len += pentry->len; + + if (len > MAX_DCC_LEN) + pentry->len = MAX_DCC_LEN; + else + pentry->len = len; + + addr = pentry->base + pentry->offset + + pentry->len * 4; + len -= pentry->len; + } + } + } + + offset = addr - base; + + while (len) { + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + ret = -ENOMEM; + goto out_unlock; + } + + entry->base = base; + entry->offset = offset; + entry->len = min_t(u32, len, MAX_DCC_LEN); + entry->desc_type = DCC_READ_TYPE; + entry->apb_bus = apb_bus; + INIT_LIST_HEAD(&entry->list); + list_add_tail(&entry->list, &drvdata->cfg_head[curr_list]); + + len -= entry->len; + offset += MAX_DCC_LEN * 4; + } + +out_unlock: + mutex_unlock(&drvdata->mutex); + return ret; +} + +static ssize_t dcc_config_add_read(struct dcc_drvdata *drvdata, char *buf, int curr_list) +{ + bool bus; + int len, nval; + unsigned int base; + char apb_bus[4]; + + nval = sscanf(buf, "%x %i %3s", &base, &len, apb_bus); + if (nval <= 0 || nval > 3) + return -EINVAL; + + if (nval == 1) { + len = 1; + bus = false; + } else if (nval == 2) { + bus = false; + } else if (!strcmp("apb", apb_bus)) { + bus = true; + } else if (!strcmp("ahb", apb_bus)) { + bus = false; + } else { + return -EINVAL; + } + + return dcc_config_add(drvdata, base, len, bus, curr_list); +} + +static void dcc_config_reset(struct dcc_drvdata *drvdata) +{ + struct dcc_config_entry *entry, *temp; + int curr_list; + + mutex_lock(&drvdata->mutex); + + for (curr_list = 0; curr_list < drvdata->nr_link_list; curr_list++) { + list_for_each_entry_safe(entry, temp, + &drvdata->cfg_head[curr_list], list) { + list_del(&entry->list); + kfree(entry); + } + } + drvdata->ram_start = 0; + drvdata->ram_cfg = 0; + mutex_unlock(&drvdata->mutex); +} + +static ssize_t config_reset_write(struct file *filp, + const char __user *user_buf, size_t count, + loff_t *ppos) +{ + unsigned int val, ret; + struct dcc_drvdata *drvdata = filp->private_data; + + ret = kstrtouint_from_user(user_buf, count, 0, &val); + if (ret < 0) + return ret; + + if (val) + dcc_config_reset(drvdata); + + return count; +} + +static const struct file_operations config_reset_fops = { + .write = config_reset_write, + .open = simple_open, + .llseek = generic_file_llseek, +}; + +static ssize_t ready_read(struct file *filp, char __user *userbuf, + size_t count, loff_t *ppos) +{ + int ret = 0; + char *buf; + struct dcc_drvdata *drvdata = filp->private_data; + + mutex_lock(&drvdata->mutex); + + if (!is_dcc_enabled(drvdata)) { + ret = -EINVAL; + goto out_unlock; + } + + if (!FIELD_GET(BIT(1), readl(drvdata->base + DCC_STATUS(drvdata->mem_map_ver)))) + buf = "Y\n"; + else + buf = "N\n"; +out_unlock: + mutex_unlock(&drvdata->mutex); + + if (ret < 0) + return -EINVAL; + else + return simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf) + 1); +} + +static const struct file_operations ready_fops = { + .read = ready_read, + .open = simple_open, + .llseek = generic_file_llseek, +}; + +static int dcc_add_loop(struct dcc_drvdata *drvdata, unsigned long loop_cnt, int curr_list) +{ + struct dcc_config_entry *entry; + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->loop_cnt = min_t(u32, loop_cnt, MAX_LOOP_CNT); + entry->desc_type = DCC_LOOP_TYPE; + INIT_LIST_HEAD(&entry->list); + list_add_tail(&entry->list, &drvdata->cfg_head[curr_list]); + + return 0; +} + +static ssize_t dcc_config_add_loop(struct dcc_drvdata *drvdata, char *buf, int curr_list) +{ + int ret, i = 0; + char *token, *input; + char delim[2] = " "; + unsigned int val[MAX_LOOP_ADDR]; + + input = buf; + + while ((token = strsep(&input, delim)) && i < MAX_LOOP_ADDR) { + ret = kstrtoint(token, 0, &val[i++]); + if (ret) + return ret; + } + + if (token) { + dev_err(drvdata->dev, "Max limit %u of loop address exceeded", + MAX_LOOP_ADDR); + return -EINVAL; + } + + if (val[1] < 1 || val[1] > 8) + return -EINVAL; + + ret = dcc_add_loop(drvdata, val[0], curr_list); + if (ret) + return ret; + + for (i = 0; i < val[1]; i++) + dcc_config_add(drvdata, val[i + 2], 1, false, curr_list); + + return dcc_add_loop(drvdata, 1, curr_list); +} + +static int dcc_rd_mod_wr_add(struct dcc_drvdata *drvdata, unsigned int mask, + unsigned int val, int curr_list) +{ + int ret = 0; + struct dcc_config_entry *entry; + + mutex_lock(&drvdata->mutex); + + if (list_empty(&drvdata->cfg_head[curr_list])) { + dev_err(drvdata->dev, "DCC: No read address programmed\n"); + ret = -EPERM; + goto out_unlock; + } + + entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL); + if (!entry) { + ret = -ENOMEM; + goto out_unlock; + } + + entry->desc_type = DCC_READ_WRITE_TYPE; + entry->mask = mask; + entry->write_val = val; + list_add_tail(&entry->list, &drvdata->cfg_head[curr_list]); +out_unlock: + mutex_unlock(&drvdata->mutex); + return ret; +} + +static ssize_t dcc_config_add_read_write(struct dcc_drvdata *drvdata, char *buf, int curr_list) +{ + int ret; + int nval; + unsigned int addr, mask, val; + + nval = sscanf(buf, "%x %x %x", &addr, &mask, &val); + + if (nval <= 1 || nval > 3) + return -EINVAL; + + ret = dcc_config_add(drvdata, addr, 1, false, curr_list); + if (ret) + return ret; + + return dcc_rd_mod_wr_add(drvdata, mask, val, curr_list); +} + +static int dcc_add_write(struct dcc_drvdata *drvdata, unsigned int addr, + unsigned int write_val, int apb_bus, int curr_list) +{ + struct dcc_config_entry *entry; + + entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->desc_type = DCC_WRITE_TYPE; + entry->base = addr & GENMASK(31, 4); + entry->offset = addr - entry->base; + entry->write_val = write_val; + entry->len = 1; + entry->apb_bus = apb_bus; + list_add_tail(&entry->list, &drvdata->cfg_head[curr_list]); + + return 0; +} + +static ssize_t dcc_config_add_write(struct dcc_drvdata *drvdata, char *buf, int curr_list) +{ + bool bus; + int nval; + unsigned int addr, write_val; + char apb_bus[4]; + + nval = sscanf(buf, "%x %x %3s", &addr, &write_val, apb_bus); + + if (nval <= 1 || nval > 3) + return -EINVAL; + + if (nval == 2) + bus = true; + + if (nval == 3) { + if (!strcmp("apb", apb_bus)) + bus = true; + else if (!strcmp("ahb", apb_bus)) + bus = false; + else + return -EINVAL; + } + + return dcc_add_write(drvdata, addr, write_val, bus, curr_list); +} + +static int config_show(struct seq_file *m, void *data) +{ + struct dcc_drvdata *drvdata = m->private; + struct dcc_config_entry *entry; + int index = 0, curr_list; + + curr_list = dcc_filp_curr_list(m->file); + if (curr_list < 0) + return curr_list; + + mutex_lock(&drvdata->mutex); + + list_for_each_entry(entry, &drvdata->cfg_head[curr_list], list) { + index++; + switch (entry->desc_type) { + case DCC_READ_WRITE_TYPE: + seq_printf(m, "RW mask: 0x%x, val: 0x%x\n index: 0x%x\n", + entry->mask, entry->write_val, index); + break; + case DCC_LOOP_TYPE: + seq_printf(m, "L index: 0x%x Loop: %d\n", index, entry->loop_cnt); + break; + case DCC_WRITE_TYPE: + seq_printf(m, "W Base:0x%x, Offset: 0x%x, val: 0x%x, APB: %d\n, Index: 0x%x\n", + entry->base, entry->offset, entry->write_val, entry->apb_bus, + index); + break; + case DCC_READ_TYPE: + seq_printf(m, "R Base:0x%x, Offset: 0x%x, len: 0x%x, APB: %d\n, Index: 0x%x\n", + entry->base, entry->offset, entry->len, entry->apb_bus, index); + } + } + mutex_unlock(&drvdata->mutex); + return 0; +} + +static int config_open(struct inode *inode, struct file *file) +{ + struct dcc_drvdata *drvdata = inode->i_private; + + return single_open(file, config_show, drvdata); +} + +static ssize_t config_write(struct file *filp, + const char __user *user_buf, size_t count, + loff_t *ppos) +{ + int ret, curr_list; + char *token, buf[50]; + char *bufp = buf; + char *delim = " "; + struct dcc_drvdata *drvdata = filp->private_data; + + if (count > sizeof(buf) || count == 0) + return -EINVAL; + + ret = copy_from_user(buf, user_buf, count); + if (ret) + return -EFAULT; + + curr_list = dcc_filp_curr_list(filp); + if (curr_list < 0) + return curr_list; + + if (buf[count - 1] == '\n') + buf[count - 1] = '\0'; + else + return -EINVAL; + + token = strsep(&bufp, delim); + + if (!strcmp("R", token)) { + ret = dcc_config_add_read(drvdata, bufp, curr_list); + } else if (!strcmp("W", token)) { + ret = dcc_config_add_write(drvdata, bufp, curr_list); + } else if (!strcmp("RW", token)) { + ret = dcc_config_add_read_write(drvdata, bufp, curr_list); + } else if (!strcmp("L", token)) { + ret = dcc_config_add_loop(drvdata, bufp, curr_list); + } else { + dev_err(drvdata->dev, "%s is not a correct input\n", token); + return -EINVAL; + } + + if (ret) + return ret; + + return count; +} + +static const struct file_operations config_fops = { + .open = config_open, + .read = seq_read, + .write = config_write, + .llseek = seq_lseek, + .release = single_release, +}; + +static void dcc_delete_debug_dir(struct dcc_drvdata *drvdata) +{ + debugfs_remove_recursive(drvdata->dbg_dir); +}; + +static void dcc_create_debug_dir(struct dcc_drvdata *drvdata) +{ + int i; + char list_num[10]; + struct dentry *list; + struct device *dev = drvdata->dev; + + drvdata->dbg_dir = debugfs_create_dir(dev_name(dev), NULL); + if (IS_ERR(drvdata->dbg_dir)) { + pr_err("can't create debugfs dir\n"); + return; + } + + for (i = 0; i <= drvdata->nr_link_list; i++) { + sprintf(list_num, "%d", i); + list = debugfs_create_dir(list_num, drvdata->dbg_dir); + debugfs_create_file("enable", 0600, list, drvdata, &enable_fops); + debugfs_create_file("config", 0600, list, drvdata, &config_fops); + } + + debugfs_create_file("trigger", 0200, drvdata->dbg_dir, drvdata, &trigger_fops); + debugfs_create_file("ready", 0400, drvdata->dbg_dir, drvdata, &ready_fops); + debugfs_create_file("config_reset", 0200, drvdata->dbg_dir, + drvdata, &config_reset_fops); +} + +static ssize_t dcc_sram_read(struct file *file, char __user *data, + size_t len, loff_t *ppos) +{ + struct dcc_drvdata *drvdata = container_of(file->private_data, struct dcc_drvdata, sram_dev); + unsigned char *buf; + + /* EOF check */ + if (*ppos >= drvdata->ram_size) + return 0; + + if ((*ppos + len) > drvdata->ram_size) + len = (drvdata->ram_size - *ppos); + + buf = kzalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memcpy_fromio(buf, drvdata->ram_base + *ppos, len); + + if (copy_to_user(data, buf, len)) { + kfree(buf); + return -EFAULT; + } + + *ppos += len; + + kfree(buf); + + return len; +} + +static const struct file_operations dcc_sram_fops = { + .owner = THIS_MODULE, + .read = dcc_sram_read, + .llseek = no_llseek, +}; + +static int dcc_sram_dev_init(struct dcc_drvdata *drvdata) +{ + drvdata->sram_dev.minor = MISC_DYNAMIC_MINOR; + drvdata->sram_dev.name = DCC_SRAM_NODE; + drvdata->sram_dev.fops = &dcc_sram_fops; + + return misc_register(&drvdata->sram_dev); +} + +static void dcc_sram_dev_exit(struct dcc_drvdata *drvdata) +{ + misc_deregister(&drvdata->sram_dev); +} + +static int dcc_probe(struct platform_device *pdev) +{ + u32 val; + int ret = 0, i; + struct device *dev = &pdev->dev; + struct dcc_drvdata *drvdata; + struct resource *res; + + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->dev = &pdev->dev; + platform_set_drvdata(pdev, drvdata); + + drvdata->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(drvdata->base)) + return PTR_ERR(drvdata->base); + + drvdata->ram_base = devm_platform_get_and_ioremap_resource(pdev, 1, &res); + if (IS_ERR(drvdata->ram_base)) + return PTR_ERR(drvdata->ram_base); + + drvdata->ram_size = resource_size(res); + + drvdata->ram_offset = (size_t)of_device_get_match_data(&pdev->dev); + + val = readl(drvdata->base + DCC_HW_INFO); + + if (FIELD_GET(DCC_VER_INFO_MASK, val)) { + drvdata->mem_map_ver = 3; + drvdata->nr_link_list = readl(drvdata->base + DCC_LL_NUM_INFO); + if (!drvdata->nr_link_list) + return -EINVAL; + } else if ((val & DCC_VER_MASK2) == DCC_VER_MASK2) { + drvdata->mem_map_ver = 2; + drvdata->nr_link_list = readl(drvdata->base + DCC_LL_NUM_INFO); + if (!drvdata->nr_link_list) + return -EINVAL; + } else { + drvdata->mem_map_ver = 1; + drvdata->nr_link_list = DCC_MAX_LINK_LIST; + } + + /* Either set the fixed loop offset or calculate + * it from the total number of words in dcc_sram. + * Max consecutive addresses dcc can loop is + * equivalent to the words in dcc_sram. + */ + if (val & DCC_LOOP_OFFSET_MASK) + drvdata->loop_shift = DCC_FIX_LOOP_OFFSET; + else + drvdata->loop_shift = get_bitmask_order((drvdata->ram_offset + + drvdata->ram_size) / DCC_SRAM_WORD_LENGTH - 1); + + mutex_init(&drvdata->mutex); + + drvdata->enable_bitmap = devm_kcalloc(dev, BITS_TO_LONGS(drvdata->nr_link_list), + sizeof(*drvdata->enable_bitmap), GFP_KERNEL); + if (!drvdata->enable_bitmap) + return -ENOMEM; + + drvdata->cfg_head = devm_kcalloc(dev, drvdata->nr_link_list, + sizeof(*drvdata->cfg_head), GFP_KERNEL); + if (!drvdata->cfg_head) + return -ENOMEM; + + for (i = 0; i < drvdata->nr_link_list; i++) + INIT_LIST_HEAD(&drvdata->cfg_head[i]); + + ret = dcc_sram_dev_init(drvdata); + if (ret) { + dev_err(drvdata->dev, "DCC: sram node not registered.\n"); + return ret; + } + + dcc_create_debug_dir(drvdata); + + return 0; +} + +static int dcc_remove(struct platform_device *pdev) +{ + struct dcc_drvdata *drvdata = platform_get_drvdata(pdev); + + dcc_delete_debug_dir(drvdata); + dcc_sram_dev_exit(drvdata); + dcc_config_reset(drvdata); + + return 0; +} + +static const struct of_device_id dcc_match_table[] = { + { .compatible = "qcom,sc7180-dcc", .data = (void *)0x6000 }, + { .compatible = "qcom,sc7280-dcc", .data = (void *)0x12000 }, + { .compatible = "qcom,sdm845-dcc", .data = (void *)0x6000 }, + { .compatible = "qcom,sm8150-dcc", .data = (void *)0x5000 }, + { } +}; +MODULE_DEVICE_TABLE(of, dcc_match_table); + +static struct platform_driver dcc_driver = { + .probe = dcc_probe, + .remove = dcc_remove, + .driver = { + .name = "qcom-dcc", + .of_match_table = dcc_match_table, + }, +}; +module_platform_driver(dcc_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Qualcomm Technologies Inc. DCC driver"); From 9732dd8d347851cb9c2fdabd35046ba0c470543e Mon Sep 17 00:00:00 2001 From: Souradeep Chowdhury Date: Tue, 27 Dec 2022 20:52:47 +0530 Subject: [PATCH 06/33] MAINTAINERS: Add the entry for DCC(Data Capture and Compare) driver support Add the entries for all the files added as a part of driver support for DCC(Data Capture and Compare). Signed-off-by: Souradeep Chowdhury Reviewed-by: Bjorn Andersson Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/d94c11b2c219265d150f71ee726637dd0f58c095.1672148732.git.quic_schowdhu@quicinc.com --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index f61eb221415bd..f2c3d471fe8f4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5821,6 +5821,14 @@ W: http://lists.twibble.org/mailman/listinfo/dc395x/ F: Documentation/scsi/dc395x.rst F: drivers/scsi/dc395x.* +DCC QTI DRIVER +M: Souradeep Chowdhury +L: linux-arm-msm@vger.kernel.org +S: Maintained +F: Documentation/ABI/testing/debugfs-driver-dcc +F: Documentation/devicetree/bindings/soc/qcom/qcom,dcc.yaml +F: drivers/soc/qcom/dcc.c + DCCP PROTOCOL L: dccp@vger.kernel.org S: Orphan From 40ebfbec52108be22cb68ecb9dcbd4917583a87c Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Wed, 28 Dec 2022 11:32:58 -0600 Subject: [PATCH 07/33] soc: qcom: ramp_controller: Include linux/bitfield.h Building ramp_controller under x86_64 results in the following build error: error: implicit declaration of function 'FIELD_PREP' is invalid in C99 Include linux/bitfield.h to ensure FIELD_PREP() is declared. Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ramp_controller.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/qcom/ramp_controller.c b/drivers/soc/qcom/ramp_controller.c index 8f8c514b72882..93652b07e812d 100644 --- a/drivers/soc/qcom/ramp_controller.c +++ b/drivers/soc/qcom/ramp_controller.c @@ -5,6 +5,7 @@ * */ +#include #include #include #include From f02a537357a61e7892587c0f3455f8295cc9075c Mon Sep 17 00:00:00 2001 From: Naman Jain Date: Fri, 25 Nov 2022 16:05:33 +0530 Subject: [PATCH 08/33] soc: qcom: socinfo: Add support for new fields in revision 16 Add support for new fields coming with socinfo structure under v16 to get SKU information, product code and name and type of different parts present in the SoC. Also, add debugfs nodes to read feature and product codes to allow user to get SKU and other SoC details. Support for SoC parts name and type parsing will be added separately. Details of fields added: * feature_code: mapped to qcom internal and external SKU IDs * pcode: product code * npartnamemap_offset: parts name map array offset from socinfo base ptr * nnum_partname_mapping: number of part mappings Signed-off-by: Naman Jain Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20221125103533.2960-1-quic_namajain@quicinc.com --- drivers/soc/qcom/socinfo.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index ebcbf9b9c18bc..10efdbcfdf053 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -169,6 +169,11 @@ struct socinfo { __le32 ndefective_parts_array_offset; /* Version 15 */ __le32 nmodem_supported; + /* Version 16 */ + __le32 feature_code; + __le32 pcode; + __le32 npartnamemap_offset; + __le32 nnum_partname_mapping; }; #ifdef CONFIG_DEBUG_FS @@ -189,6 +194,8 @@ struct socinfo_params { u32 num_defective_parts; u32 ndefective_parts_array_offset; u32 nmodem_supported; + u32 feature_code; + u32 pcode; }; struct smem_image_version { @@ -512,6 +519,15 @@ static void socinfo_debugfs_init(struct qcom_socinfo *qcom_socinfo, &qcom_socinfo->info.fmt); switch (qcom_socinfo->info.fmt) { + case SOCINFO_VERSION(0, 16): + qcom_socinfo->info.feature_code = __le32_to_cpu(info->feature_code); + qcom_socinfo->info.pcode = __le32_to_cpu(info->pcode); + + debugfs_create_u32("feature_code", 0444, qcom_socinfo->dbg_root, + &qcom_socinfo->info.feature_code); + debugfs_create_u32("pcode", 0444, qcom_socinfo->dbg_root, + &qcom_socinfo->info.pcode); + fallthrough; case SOCINFO_VERSION(0, 15): qcom_socinfo->info.nmodem_supported = __le32_to_cpu(info->nmodem_supported); From 33ae3d0955943ac5bacfcb6911cf7cb74822bf8c Mon Sep 17 00:00:00 2001 From: Dawei Li Date: Fri, 6 Jan 2023 23:28:03 +0800 Subject: [PATCH 09/33] soc: qcom: apr: make remove callback of apr driver void returned Since commit fc7a6209d571 ("bus: Make remove callback return void") forces bus_type::remove be void-returned, it doesn't make much sense for any bus based driver implementing remove callbalk to return non-void to its caller. As such, change the remove function for apr bus based drivers to return void. Signed-off-by: Dawei Li Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/TYCP286MB23232B7968D34DB8323B0F16CAFB9@TYCP286MB2323.JPNP286.PROD.OUTLOOK.COM --- include/linux/soc/qcom/apr.h | 2 +- sound/soc/qcom/qdsp6/q6core.c | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/include/linux/soc/qcom/apr.h b/include/linux/soc/qcom/apr.h index 23c5b30f35114..be98aebcb3e19 100644 --- a/include/linux/soc/qcom/apr.h +++ b/include/linux/soc/qcom/apr.h @@ -153,7 +153,7 @@ typedef struct apr_device gpr_device_t; struct apr_driver { int (*probe)(struct apr_device *sl); - int (*remove)(struct apr_device *sl); + void (*remove)(struct apr_device *sl); int (*callback)(struct apr_device *a, struct apr_resp_pkt *d); int (*gpr_callback)(struct gpr_resp_pkt *d, void *data, int op); diff --git a/sound/soc/qcom/qdsp6/q6core.c b/sound/soc/qcom/qdsp6/q6core.c index 5358fefd4210b..49cfb32cd2091 100644 --- a/sound/soc/qcom/qdsp6/q6core.c +++ b/sound/soc/qcom/qdsp6/q6core.c @@ -339,7 +339,7 @@ static int q6core_probe(struct apr_device *adev) return 0; } -static int q6core_exit(struct apr_device *adev) +static void q6core_exit(struct apr_device *adev) { struct q6core *core = dev_get_drvdata(&adev->dev); @@ -350,8 +350,6 @@ static int q6core_exit(struct apr_device *adev) g_core = NULL; kfree(core); - - return 0; } #ifdef CONFIG_OF From 26658868354963afbff672ad6f7a85c44c311975 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Thu, 29 Dec 2022 16:16:47 +0100 Subject: [PATCH 10/33] dt-bindings: soc: qcom: apr: Make qcom,protection-domain optional again The protection domain functionality exists only in SoCs starting from MSM8998 [1], while the APR bindings are also used on older platforms. Commit 41288c305836 ("ASoC: dt-bindings: qcom,apr: Split services to shared schema") made the "qcom,protection-domain" required but it should remain optional to avoid dtbs_check warnings on older platforms, e.g.: arch/arm64/boot/dts/qcom/apq8096-db820c.dtb: apr: service@3: 'qcom,protection-domain' is a required property From schema: Documentation/devicetree/bindings/soc/qcom/qcom,apr.yaml [1]: https://lore.kernel.org/all/20200312120842.21991-1-sibis@codeaurora.org/ Fixes: 41288c305836 ("ASoC: dt-bindings: qcom,apr: Split services to shared schema") Signed-off-by: Stephan Gerhold Reviewed-by: Krzysztof Kozlowski Reviewed-by: Bjorn Andersson Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20221229151648.19839-2-stephan@gerhold.net --- .../devicetree/bindings/soc/qcom/qcom,apr-services.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,apr-services.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,apr-services.yaml index 290555426c39c..bdf482db32aac 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,apr-services.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,apr-services.yaml @@ -39,8 +39,8 @@ properties: qcom,protection-domain: $ref: /schemas/types.yaml#/definitions/string-array description: | - Protection domain service name and path for APR service - possible values are:: + Protection domain service name and path for APR service (if supported). + Possible values are:: "avs/audio", "msm/adsp/audio_pd". "kernel/elf_loader", "msm/modem/wlan_pd". "tms/servreg", "msm/adsp/audio_pd". @@ -49,6 +49,5 @@ properties: required: - reg - - qcom,protection-domain additionalProperties: true From 599d41fb8ea8bd2a99ca9525dd69405020e43dda Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Thu, 29 Dec 2022 16:16:48 +0100 Subject: [PATCH 11/33] soc: qcom: apr: Make qcom,protection-domain optional again APR should not fail if the service device tree node does not have the qcom,protection-domain property, since this functionality does not exist on older platforms such as MSM8916 and MSM8996. Ignore -EINVAL (returned when the property does not exist) to fix a regression on 6.2-rc1 that prevents audio from working: qcom,apr remoteproc0:smd-edge.apr_audio_svc.-1.-1: Failed to read second value of qcom,protection-domain qcom,apr remoteproc0:smd-edge.apr_audio_svc.-1.-1: Failed to add apr 3 svc Fixes: 6d7860f5750d ("soc: qcom: apr: Add check for idr_alloc and of_property_read_string_index") Signed-off-by: Stephan Gerhold Reviewed-by: Bjorn Andersson Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20221229151648.19839-3-stephan@gerhold.net --- drivers/soc/qcom/apr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/soc/qcom/apr.c b/drivers/soc/qcom/apr.c index cd44f17dad3d0..d51abb462ae5d 100644 --- a/drivers/soc/qcom/apr.c +++ b/drivers/soc/qcom/apr.c @@ -461,9 +461,10 @@ static int apr_add_device(struct device *dev, struct device_node *np, goto out; } + /* Protection domain is optional, it does not exist on older platforms */ ret = of_property_read_string_index(np, "qcom,protection-domain", 1, &adev->service_path); - if (ret < 0) { + if (ret < 0 && ret != -EINVAL) { dev_err(dev, "Failed to read second value of qcom,protection-domain\n"); goto out; } From d4b2c7484a8edd79c90b9f8acc8a03e5e3235b89 Mon Sep 17 00:00:00 2001 From: Yang Li Date: Fri, 6 Jan 2023 09:17:10 +0800 Subject: [PATCH 12/33] soc: qcom: dcc: Fix unsigned comparison with less than zero The return value from the call to kstrtouint_from_user() is int. However, the return value is being assigned to an unsigned int variable 'ret', so making 'ret' an int. Eliminate the following warning: ./drivers/soc/qcom/dcc.c:815:5-8: WARNING: Unsigned expression compared with zero: ret < 0 Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=3638 Fixes: 4cbe60cf5ad6 ("soc: qcom: dcc: Add driver support for Data Capture and Compare unit(DCC)") Reported-by: Abaci Robot Signed-off-by: Yang Li Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230106011710.2827-1-yang.lee@linux.alibaba.com --- drivers/soc/qcom/dcc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/soc/qcom/dcc.c b/drivers/soc/qcom/dcc.c index d4101f79cb5df..5b50d638771d4 100644 --- a/drivers/soc/qcom/dcc.c +++ b/drivers/soc/qcom/dcc.c @@ -808,7 +808,8 @@ static ssize_t config_reset_write(struct file *filp, const char __user *user_buf, size_t count, loff_t *ppos) { - unsigned int val, ret; + unsigned int val; + int ret; struct dcc_drvdata *drvdata = filp->private_data; ret = kstrtouint_from_user(user_buf, count, 0, &val); From dc2f5a499de420001813562ddbc9d51ece295978 Mon Sep 17 00:00:00 2001 From: Bagas Sanjaya Date: Fri, 30 Dec 2022 20:50:30 +0700 Subject: [PATCH 13/33] soc: qcom: dcc: Fix examples list on /sys/kernel/debug/dcc/.../[list-number]/config documentation kernel test robot reported htmldocs warnings: Documentation/ABI/testing/debugfs-driver-dcc:34: WARNING: Unexpected indentation. Documentation/ABI/testing/debugfs-driver-dcc:34: WARNING: Block quote ends without a blank line; unexpected unindent. Fix these by fixing numbered list syntax on description of /sys/kernel/debug/dcc/.../[list-number]/config, including adding blank line separators as appropriate. Link: https://lore.kernel.org/linux-doc/202212300426.eMLsZsvA-lkp@intel.com/ Fixes: 4cbe60cf5ad622 ("soc: qcom: dcc: Add driver support for Data Capture and Compare unit(DCC)") Reported-by: kernel test robot Signed-off-by: Bagas Sanjaya Acked-by: Souradeep Chowdhury Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20221230135030.17002-2-bagasdotme@gmail.com --- Documentation/ABI/testing/debugfs-driver-dcc | 63 +++++++++++++------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/Documentation/ABI/testing/debugfs-driver-dcc b/Documentation/ABI/testing/debugfs-driver-dcc index a468095020593..a00f4502885b5 100644 --- a/Documentation/ABI/testing/debugfs-driver-dcc +++ b/Documentation/ABI/testing/debugfs-driver-dcc @@ -44,35 +44,54 @@ Description: example user can jump to list x only after list y is configured and enabled. The format for entering all types of instructions are explained in examples as - follows. - Example: - i)Read Type Instruction + follows: + + i) Read Type Instruction + echo R <1> <2> <3> >/sys/kernel/debug/dcc/../[list-number]/config + 1->Address to be considered for reading the value. + 2->The word count of the addresses, read n words starting from address <1>. Each word is of 32 bits. If not entered 1 is considered. + 3->Can be 'apb' or 'ahb' which indicates if it is apb or ahb bus respectively. If not entered ahb is considered. - ii)Write Type Instruction - echo W <1> <2> <3> > /sys/kernel/debug/dcc/../[list-number]/config - 1->Address to be considered for writing the value. - 2->The value that needs to be written at the location. - 3->Can be a 'apb' or 'ahb' which indicates if it is apb or ahb - but respectively. - iii)Read Modify Write type instruction - echo RW <1> <2> <3> > /sys/kernel/debug/dcc/../[list-number]/config - 1->The address which needs to be considered for read then write. - 2->The value that needs to be written on the address. - 3->The mask of the value to be written. - iv)Loop Type Instruction - echo L <1> <2> <3> > /sys/kernel/debug/dcc/../[list-number]/config - 1->The loop count, the number of times the value of the addresses will be - captured. - 2->The address count, total number of addresses to be entered in this - instruction. - 3->The series of addresses to be entered separated by a space like - ... and so on. + + ii) Write Type Instruction + + echo W <1> <2> <3> > /sys/kernel/debug/dcc/../[list-number]/config + + 1->Address to be considered for writing the value. + + 2->The value that needs to be written at the location. + + 3->Can be a 'apb' or 'ahb' which indicates if it is apb or ahb + but respectively. + + iii) Read Modify Write type instruction + + echo RW <1> <2> <3> > /sys/kernel/debug/dcc/../[list-number]/config + + 1->The address which needs to be considered for read then write. + + 2->The value that needs to be written on the address. + + 3->The mask of the value to be written. + + iv) Loop Type Instruction + + echo L <1> <2> <3> > /sys/kernel/debug/dcc/../[list-number]/config + + 1->The loop count, the number of times the value of the addresses will be + captured. + + 2->The address count, total number of addresses to be entered in this + instruction. + + 3->The series of addresses to be entered separated by a space like + ... and so on. What: /sys/kernel/debug/dcc/.../[list-number]/enable Date: December 2022 From 13763fb955a0043bd9b6fb1e237f4fd8fe8b3c75 Mon Sep 17 00:00:00 2001 From: Bagas Sanjaya Date: Fri, 30 Dec 2022 20:50:31 +0700 Subject: [PATCH 14/33] soc: qcom: dcc: rewrite description of dcc sysfs files The description of dcc sysfs files is somewhat confusing and not effective. Rewrite it to be clear. While at it, also use literal code block for config sysfs examples and remove redundant examples that are obvious. Signed-off-by: Bagas Sanjaya Acked-by: Souradeep Chowdhury Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20221230135030.17002-3-bagasdotme@gmail.com --- Documentation/ABI/testing/debugfs-driver-dcc | 142 ++++++++++--------- 1 file changed, 76 insertions(+), 66 deletions(-) diff --git a/Documentation/ABI/testing/debugfs-driver-dcc b/Documentation/ABI/testing/debugfs-driver-dcc index a00f4502885b5..27ed5919d21b9 100644 --- a/Documentation/ABI/testing/debugfs-driver-dcc +++ b/Documentation/ABI/testing/debugfs-driver-dcc @@ -3,115 +3,125 @@ Date: December 2022 Contact: Souradeep Chowdhury Description: This file is used to check the status of the dcc - hardware if it's ready to take the inputs. A 'Y' - here indicates dcc is in a ready condition. - Example: - cat /sys/kernel/debug/dcc/.../ready + hardware if it's ready to receive user configurations. + A 'Y' here indicates dcc is ready. What: /sys/kernel/debug/dcc/.../trigger Date: December 2022 Contact: Souradeep Chowdhury Description: This is the debugfs interface for manual software - triggers. The user can simply enter a 1 against - the debugfs file and enable a manual trigger. - Example: - echo 1 > /sys/kernel/debug/dcc/.../trigger + triggers. The trigger can be invoked by writing '1' + to the file. What: /sys/kernel/debug/dcc/.../config_reset Date: December 2022 Contact: Souradeep Chowdhury Description: This file is used to reset the configuration of - a dcc driver to the default configuration. This - means that all the previous addresses stored in - the driver gets removed and user needs to enter - the address values from the start. - Example: - echo 1 > /sys/kernel/debug/dcc/../config_reset + a dcc driver to the default configuration. When '1' + is written to the file, all the previous addresses + stored in the driver gets removed and users need to + reconfigure addresses again. What: /sys/kernel/debug/dcc/.../[list-number]/config Date: December 2022 Contact: Souradeep Chowdhury Description: This stores the addresses of the registers which - should be read in case of a hardware crash or - manual software triggers. The addresses entered here - are considered under all the 4 types of dcc - instructions Read type, Write type, Read Modify Write - type and Loop type. The lists need to be configured - sequentially and not in a overlapping manner. As an - example user can jump to list x only after list y is - configured and enabled. The format for entering all - types of instructions are explained in examples as - follows: + can be read in case of a hardware crash or manual + software triggers. The input addresses type + can be one of following dcc instructions: read, + write, read-write, and loop type. The lists need to + be configured sequentially and not in a overlapping + manner; e.g. users can jump to list x only after + list y is configured and enabled. The input format for + each type is as follows: - i) Read Type Instruction + i) Read instruction - echo R <1> <2> <3> >/sys/kernel/debug/dcc/../[list-number]/config + :: - 1->Address to be considered for reading the value. + echo R >/sys/kernel/debug/dcc/../[list-number]/config - 2->The word count of the addresses, read n words - starting from address <1>. Each word is of 32 bits. - If not entered 1 is considered. + where: - 3->Can be 'apb' or 'ahb' which indicates if it is apb or ahb - bus respectively. If not entered ahb is considered. + + The address to be read. - ii) Write Type Instruction + + The addresses word count, starting from address <1>. + Each word is 32 bits (4 bytes). If omitted, defaulted + to 1. - echo W <1> <2> <3> > /sys/kernel/debug/dcc/../[list-number]/config + + The bus type, which can be either 'apb' or 'ahb'. + The default is 'ahb' if leaved out. - 1->Address to be considered for writing the value. + ii) Write instruction - 2->The value that needs to be written at the location. + :: - 3->Can be a 'apb' or 'ahb' which indicates if it is apb or ahb - but respectively. + echo W > /sys/kernel/debug/dcc/../[list-number]/config - iii) Read Modify Write type instruction + where: - echo RW <1> <2> <3> > /sys/kernel/debug/dcc/../[list-number]/config + + The address to be written. - 1->The address which needs to be considered for read then write. + + The value to be written at . - 2->The value that needs to be written on the address. + + The bus type, which can be either 'apb' or 'ahb'. - 3->The mask of the value to be written. + iii) Read-write instruction - iv) Loop Type Instruction + :: - echo L <1> <2> <3> > /sys/kernel/debug/dcc/../[list-number]/config + echo RW > /sys/kernel/debug/dcc/../[list-number]/config - 1->The loop count, the number of times the value of the addresses will be - captured. + where: - 2->The address count, total number of addresses to be entered in this - instruction. + + The address to be read and written. - 3->The series of addresses to be entered separated by a space like - ... and so on. + + The value to be written at . + + + The value mask. + + iv) Loop instruction + + :: + + echo L
... > /sys/kernel/debug/dcc/../[list-number]/config + + where: + + + Number of iterations + +
+ total number of addresses to be written + +
+ Space-separated list of addresses. What: /sys/kernel/debug/dcc/.../[list-number]/enable Date: December 2022 Contact: Souradeep Chowdhury Description: This debugfs interface is used for enabling the - the dcc hardware. Enable file is kept under the - directory list number for which the user wants - to enable it. For example if the user wants to - enable list 1, then he should go for - echo 1 > /sys/kernel/debug/dcc/.../1/enable. - On enabling the dcc, all the addresses entered + the dcc hardware. A file named "enable" is in the + directory list number where users can enable/disable + the specific list by writing boolean (1 or 0) to the + file. + + On enabling the dcc, all the addresses specified by the user for the corresponding list is written into dcc sram which is read by the dcc hardware - on manual or crash induced triggers. Lists should - be enabled sequentially.For example after configuring - addresses for list 1 and enabling it, a user can - proceed to enable list 2 or vice versa. - Example: - echo 0 > /sys/kernel/debug/dcc/.../[list-number]/enable - (disable dcc for the corresponding list number) - echo 1 > /sys/kernel/debug/dcc/.../[list-number]/enable - (enable dcc for the corresponding list number) + on manual or crash induced triggers. Lists must + be configured and enabled sequentially, e.g. list + 2 can only be enabled when list 1 have so. From 6049aae52392539e505bfb8ccbcff3c26f1d2f0b Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 17 Dec 2022 17:05:41 +0100 Subject: [PATCH 15/33] PM: AVS: qcom-cpr: Fix an error handling path in cpr_probe() If an error occurs after a successful pm_genpd_init() call, it should be undone by a corresponding pm_genpd_remove(). Add the missing call in the error handling path, as already done in the remove function. Fixes: bf6910abf548 ("power: avs: Add support for CPR (Core Power Reduction)") Signed-off-by: Christophe JAILLET Reviewed-by: Ulf Hansson Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/0f520597dbad89ab99c217c8986912fa53eaf5f9.1671293108.git.christophe.jaillet@wanadoo.fr --- drivers/soc/qcom/cpr.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/soc/qcom/cpr.c b/drivers/soc/qcom/cpr.c index e9b854ed1bdfd..144ea68e0920a 100644 --- a/drivers/soc/qcom/cpr.c +++ b/drivers/soc/qcom/cpr.c @@ -1708,12 +1708,16 @@ static int cpr_probe(struct platform_device *pdev) ret = of_genpd_add_provider_simple(dev->of_node, &drv->pd); if (ret) - return ret; + goto err_remove_genpd; platform_set_drvdata(pdev, drv); cpr_debugfs_init(drv); return 0; + +err_remove_genpd: + pm_genpd_remove(&drv->pd); + return ret; } static int cpr_remove(struct platform_device *pdev) From b4f0370d3ce276397f5c48af99d0b77548825eb1 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 9 Jan 2023 18:45:06 +0100 Subject: [PATCH 16/33] dt-bindings: power: qcom,rpmpd: document sa8775p Add a compatible for sa8775p platforms and relevant defines to the include file. Signed-off-by: Bartosz Golaszewski Acked-by: Krzysztof Kozlowski Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230109174511.1740856-14-brgl@bgdev.pl --- .../devicetree/bindings/power/qcom,rpmpd.yaml | 1 + include/dt-bindings/power/qcom-rpmpd.h | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml index 633d49884019c..1778d98515108 100644 --- a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml +++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml @@ -30,6 +30,7 @@ properties: - qcom,qcs404-rpmpd - qcom,qdu1000-rpmhpd - qcom,sa8540p-rpmhpd + - qcom,sa8775p-rpmhpd - qcom,sdm660-rpmpd - qcom,sc7180-rpmhpd - qcom,sc7280-rpmhpd diff --git a/include/dt-bindings/power/qcom-rpmpd.h b/include/dt-bindings/power/qcom-rpmpd.h index 1e19e258a74de..3117bf7d5ebfc 100644 --- a/include/dt-bindings/power/qcom-rpmpd.h +++ b/include/dt-bindings/power/qcom-rpmpd.h @@ -4,6 +4,25 @@ #ifndef _DT_BINDINGS_POWER_QCOM_RPMPD_H #define _DT_BINDINGS_POWER_QCOM_RPMPD_H +/* SA8775P Power Domain Indexes */ +#define SA8775P_CX 0 +#define SA8775P_CX_AO 1 +#define SA8775P_DDR 2 +#define SA8775P_EBI 3 +#define SA8775P_GFX 4 +#define SA8775P_LCX 5 +#define SA8775P_LMX 6 +#define SA8775P_MMCX 7 +#define SA8775P_MMCX_AO 8 +#define SA8775P_MSS 9 +#define SA8775P_MX 10 +#define SA8775P_MX_AO 11 +#define SA8775P_MXC 12 +#define SA8775P_MXC_AO 13 +#define SA8775P_NSP0 14 +#define SA8775P_NSP1 15 +#define SA8775P_XO 16 + /* SDM670 Power Domain Indexes */ #define SDM670_MX 0 #define SDM670_MX_AO 1 From 91e910adc59a6954e475dd2d6a4534ac56dd8eed Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 9 Jan 2023 18:45:07 +0100 Subject: [PATCH 17/33] soc: qcom: rmphpd: add power domains for sa8775p Add power domain description for sa8775p and a new compatible to match it. Signed-off-by: Bartosz Golaszewski Reviewed-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230109174511.1740856-15-brgl@bgdev.pl --- drivers/soc/qcom/rpmhpd.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c index 4c2d2c296790e..f20e2a49a6695 100644 --- a/drivers/soc/qcom/rpmhpd.c +++ b/drivers/soc/qcom/rpmhpd.c @@ -187,6 +187,16 @@ static struct rpmhpd nsp = { .res_name = "nsp.lvl", }; +static struct rpmhpd nsp0 = { + .pd = { .name = "nsp0", }, + .res_name = "nsp0.lvl", +}; + +static struct rpmhpd nsp1 = { + .pd = { .name = "nsp1", }, + .res_name = "nsp1.lvl", +}; + static struct rpmhpd qphy = { .pd = { .name = "qphy", }, .res_name = "qphy.lvl", @@ -212,6 +222,29 @@ static const struct rpmhpd_desc sa8540p_desc = { .num_pds = ARRAY_SIZE(sa8540p_rpmhpds), }; +/* SA8775P RPMH power domains */ +static struct rpmhpd *sa8775p_rpmhpds[] = { + [SA8775P_CX] = &cx, + [SA8775P_CX_AO] = &cx_ao, + [SA8775P_EBI] = &ebi, + [SA8775P_GFX] = &gfx, + [SA8775P_LCX] = &lcx, + [SA8775P_LMX] = &lmx, + [SA8775P_MMCX] = &mmcx, + [SA8775P_MMCX_AO] = &mmcx_ao, + [SA8775P_MXC] = &mxc, + [SA8775P_MXC_AO] = &mxc_ao, + [SA8775P_MX] = &mx, + [SA8775P_MX_AO] = &mx_ao, + [SA8775P_NSP0] = &nsp0, + [SA8775P_NSP1] = &nsp1, +}; + +static const struct rpmhpd_desc sa8775p_desc = { + .rpmhpds = sa8775p_rpmhpds, + .num_pds = ARRAY_SIZE(sa8775p_rpmhpds), +}; + /* SDM670 RPMH powerdomains */ static struct rpmhpd *sdm670_rpmhpds[] = { [SDM670_CX] = &cx_w_mx_parent, @@ -487,6 +520,7 @@ static const struct rpmhpd_desc sc8280xp_desc = { static const struct of_device_id rpmhpd_match_table[] = { { .compatible = "qcom,qdu1000-rpmhpd", .data = &qdu1000_desc }, { .compatible = "qcom,sa8540p-rpmhpd", .data = &sa8540p_desc }, + { .compatible = "qcom,sa8775p-rpmhpd", .data = &sa8775p_desc }, { .compatible = "qcom,sc7180-rpmhpd", .data = &sc7180_desc }, { .compatible = "qcom,sc7280-rpmhpd", .data = &sc7280_desc }, { .compatible = "qcom,sc8180x-rpmhpd", .data = &sc8180x_desc }, From d45fb976f46600554e2db02ce5d16d1dff84d106 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 9 Jan 2023 20:20:04 -0800 Subject: [PATCH 18/33] soc: qcom: ramp_controller: Make things static The five msm8976_cfg_* objects ought to be static, as reported by LKP and sparse, fix this. drivers/soc/qcom/ramp_controller.c:235:27: sparse: sparse: symbol 'msm8976_cfg_dfs_sid' was not declared. Should it be static? drivers/soc/qcom/ramp_controller.c:246:27: sparse: sparse: symbol 'msm8976_cfg_link_sid' was not declared. Should it be static? drivers/soc/qcom/ramp_controller.c:250:27: sparse: sparse: symbol 'msm8976_cfg_lmh_sid' was not declared. Should it be static? drivers/soc/qcom/ramp_controller.c:256:27: sparse: sparse: symbol 'msm8976_cfg_ramp_en' was not declared. Should it be static? drivers/soc/qcom/ramp_controller.c:262:27: sparse: sparse: symbol 'msm8976_cfg_ramp_dis' was not declared. Should it be static? Fixes: a723c95fa137 ("soc: qcom: Add Qualcomm Ramp Controller driver") Reported-by: kernel test robot Signed-off-by: Bjorn Andersson Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230110042004.2378444-1-quic_bjorande@quicinc.com --- drivers/soc/qcom/ramp_controller.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/soc/qcom/ramp_controller.c b/drivers/soc/qcom/ramp_controller.c index 93652b07e812d..dc74d2a19de2b 100644 --- a/drivers/soc/qcom/ramp_controller.c +++ b/drivers/soc/qcom/ramp_controller.c @@ -232,7 +232,7 @@ static const struct regmap_config qrc_regmap_config = { .fast_io = true, }; -const struct reg_sequence msm8976_cfg_dfs_sid[] = { +static const struct reg_sequence msm8976_cfg_dfs_sid[] = { { 0x10, 0xfefebff7 }, { 0x14, 0xfdff7fef }, { 0x18, 0xfbffdefb }, @@ -243,23 +243,23 @@ const struct reg_sequence msm8976_cfg_dfs_sid[] = { { 0x2c, 0x8102 } }; -const struct reg_sequence msm8976_cfg_link_sid[] = { +static const struct reg_sequence msm8976_cfg_link_sid[] = { { 0x40, 0xfc987 } }; -const struct reg_sequence msm8976_cfg_lmh_sid[] = { +static const struct reg_sequence msm8976_cfg_lmh_sid[] = { { 0x30, 0x77706db }, { 0x34, 0x5550249 }, { 0x38, 0x111 } }; -const struct reg_sequence msm8976_cfg_ramp_en[] = { +static const struct reg_sequence msm8976_cfg_ramp_en[] = { { 0x50, 0x800 }, /* pre_en */ { 0x50, 0xc00 }, /* en */ { 0x50, 0x400 } /* post_en */ }; -const struct reg_sequence msm8976_cfg_ramp_dis[] = { +static const struct reg_sequence msm8976_cfg_ramp_dis[] = { { 0x50, 0x0 } }; From 8f3945a9adce3934943b365bcd5ae2932737f028 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 22 Nov 2022 10:23:44 +0100 Subject: [PATCH 19/33] dt-bindings: firmware: qcom,scm: document MSM8226 clocks Document the clocks (core, bus and iface) used on MSM8226 SCM. Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20221122092345.44369-1-krzysztof.kozlowski@linaro.org --- Documentation/devicetree/bindings/firmware/qcom,scm.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml index 25688571ee7ca..fc986f2f5d304 100644 --- a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml +++ b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml @@ -111,6 +111,7 @@ allOf: enum: - qcom,scm-apq8084 - qcom,scm-mdm9607 + - qcom,scm-msm8226 - qcom,scm-msm8916 - qcom,scm-msm8953 - qcom,scm-msm8974 From c5b8840c895a5b0291ef84e3f13d92c3414033e7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 22 Nov 2022 10:23:45 +0100 Subject: [PATCH 20/33] dt-bindings: firmware: qcom,scm: narrow clocks and interconnects SCM has clock inputs and interconnects only on some of the SoCs, so disallow them inb other cases, which makes the binding stricter. Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20221122092345.44369-2-krzysztof.kozlowski@linaro.org --- .../bindings/firmware/qcom,scm.yaml | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml index fc986f2f5d304..8e6e9ebb343d6 100644 --- a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml +++ b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml @@ -82,6 +82,32 @@ properties: description: TCSR hardware block allOf: + # Clocks + - if: + properties: + compatible: + contains: + enum: + - qcom,scm-apq8064 + - qcom,scm-apq8084 + - qcom,scm-mdm9607 + - qcom,scm-msm8226 + - qcom,scm-msm8660 + - qcom,scm-msm8916 + - qcom,scm-msm8953 + - qcom,scm-msm8960 + - qcom,scm-msm8974 + - qcom,scm-msm8976 + - qcom,scm-sm6375 + then: + required: + - clocks + - clock-names + else: + properties: + clock-names: false + clocks: false + - if: properties: compatible: @@ -100,10 +126,6 @@ allOf: clocks: maxItems: 1 - required: - - clocks - - clock-names - - if: properties: compatible: @@ -128,9 +150,17 @@ allOf: minItems: 3 maxItems: 3 - required: - - clocks - - clock-names + # Interconnects + - if: + not: + properties: + compatible: + contains: + enum: + - qcom,scm-sm8450 + then: + properties: + interconnects: false required: - compatible From 5b8db5b4f756bcb3a3f9b6577f32fc0d3867496f Mon Sep 17 00:00:00 2001 From: Melody Olvera Date: Fri, 16 Dec 2022 15:13:12 -0800 Subject: [PATCH 21/33] dt-bindings: firmware: scm: Add QDU1000/QRU1000 compatible Add compatible for scm driver for QDU1000 and QRU1000 platforms. These drivers only require the compatible field, so update the bindings accordingly for these platforms. Signed-off-by: Melody Olvera Acked-by: Krzysztof Kozlowski Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20221216231312.22574-1-quic_molvera@quicinc.com --- Documentation/devicetree/bindings/firmware/qcom,scm.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml index 8e6e9ebb343d6..9312a0953ea8b 100644 --- a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml +++ b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml @@ -38,6 +38,7 @@ properties: - qcom,scm-msm8994 - qcom,scm-msm8996 - qcom,scm-msm8998 + - qcom,scm-qdu1000 - qcom,scm-sc7180 - qcom,scm-sc7280 - qcom,scm-sc8280xp From da0d37e42f93f7bd6351e7ef67a52ea9a272c761 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Mon, 9 Jan 2023 14:05:21 +0100 Subject: [PATCH 22/33] dt-bindings: reserved-memory: rmtfs: Make qcom,vmid an array Some SoCs mandate that the RMTFS is also assigned to the NAV VM, while others really don't want that. Since it has to be conditional, turn qcom,vmid into an u32 array so that we can handle the NAV case, as well as other similar ones if they pop up in the future. Signed-off-by: Konrad Dybcio Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230109130523.298971-1-konrad.dybcio@linaro.org --- .../devicetree/bindings/reserved-memory/qcom,rmtfs-mem.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/reserved-memory/qcom,rmtfs-mem.yaml b/Documentation/devicetree/bindings/reserved-memory/qcom,rmtfs-mem.yaml index 2998f1c8f0dbc..08eb10c258219 100644 --- a/Documentation/devicetree/bindings/reserved-memory/qcom,rmtfs-mem.yaml +++ b/Documentation/devicetree/bindings/reserved-memory/qcom,rmtfs-mem.yaml @@ -27,9 +27,11 @@ properties: identifier of the client to use this region for buffers qcom,vmid: - $ref: /schemas/types.yaml#/definitions/uint32 + $ref: /schemas/types.yaml#/definitions/uint32-array description: > - vmid of the remote processor, to set up memory protection + Array of vmids of the remote processors, to set up memory protection + minItems: 1 + maxItems: 2 required: - qcom,client-id From e656cd0bcf3d2ba2eceac82b44714bf355428ec4 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Mon, 9 Jan 2023 14:05:22 +0100 Subject: [PATCH 23/33] soc: qcom: rmtfs: Optionally map RMTFS to more VMs Some SoCs require that RMTFS is also mapped to the NAV VM. Trying to power on the modem without that results in the whole platform crashing and forces a hard reboot within about 2 seconds. Add support for mapping the region to additional VMs, such as NAV to open a path towards enabling modem on such platforms. Signed-off-by: Loic Poulain [Konrad: reword, make conditional and flexible, add a define for NAV VMID] Signed-off-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230109130523.298971-2-konrad.dybcio@linaro.org --- drivers/soc/qcom/rmtfs_mem.c | 29 ++++++++++++++++++++++------- include/linux/qcom_scm.h | 1 + 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/drivers/soc/qcom/rmtfs_mem.c b/drivers/soc/qcom/rmtfs_mem.c index 0feaae3578215..9d59ad509a5c7 100644 --- a/drivers/soc/qcom/rmtfs_mem.c +++ b/drivers/soc/qcom/rmtfs_mem.c @@ -17,6 +17,7 @@ #include #define QCOM_RMTFS_MEM_DEV_MAX (MINORMASK + 1) +#define NUM_MAX_VMIDS 2 static dev_t qcom_rmtfs_mem_major; @@ -171,12 +172,12 @@ static void qcom_rmtfs_mem_release_device(struct device *dev) static int qcom_rmtfs_mem_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; - struct qcom_scm_vmperm perms[2]; + struct qcom_scm_vmperm perms[NUM_MAX_VMIDS + 1]; struct reserved_mem *rmem; struct qcom_rmtfs_mem *rmtfs_mem; u32 client_id; - u32 vmid; - int ret; + u32 num_vmids, vmid[NUM_MAX_VMIDS]; + int ret, i; rmem = of_reserved_mem_lookup(node); if (!rmem) { @@ -226,7 +227,18 @@ static int qcom_rmtfs_mem_probe(struct platform_device *pdev) goto put_device; } - ret = of_property_read_u32(node, "qcom,vmid", &vmid); + num_vmids = of_property_count_u32_elems(node, "qcom,vmid"); + if (num_vmids < 0) { + dev_err(&pdev->dev, "failed to count qcom,vmid elements: %d\n", ret); + goto remove_cdev; + } else if (num_vmids > NUM_MAX_VMIDS) { + dev_warn(&pdev->dev, + "too many VMIDs (%d) specified! Only mapping first %d entries\n", + num_vmids, NUM_MAX_VMIDS); + num_vmids = NUM_MAX_VMIDS; + } + + ret = of_property_read_u32_array(node, "qcom,vmid", vmid, num_vmids); if (ret < 0 && ret != -EINVAL) { dev_err(&pdev->dev, "failed to parse qcom,vmid\n"); goto remove_cdev; @@ -238,12 +250,15 @@ static int qcom_rmtfs_mem_probe(struct platform_device *pdev) perms[0].vmid = QCOM_SCM_VMID_HLOS; perms[0].perm = QCOM_SCM_PERM_RW; - perms[1].vmid = vmid; - perms[1].perm = QCOM_SCM_PERM_RW; + + for (i = 0; i < num_vmids; i++) { + perms[i + 1].vmid = vmid[i]; + perms[i + 1].perm = QCOM_SCM_PERM_RW; + } rmtfs_mem->perms = BIT(QCOM_SCM_VMID_HLOS); ret = qcom_scm_assign_mem(rmtfs_mem->addr, rmtfs_mem->size, - &rmtfs_mem->perms, perms, 2); + &rmtfs_mem->perms, perms, num_vmids + 1); if (ret < 0) { dev_err(&pdev->dev, "assign memory failed\n"); goto remove_cdev; diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index f8335644a01ae..150b72edb8797 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -55,6 +55,7 @@ enum qcom_scm_ice_cipher { #define QCOM_SCM_VMID_MSS_MSA 0xF #define QCOM_SCM_VMID_WLAN 0x18 #define QCOM_SCM_VMID_WLAN_CE 0x19 +#define QCOM_SCM_VMID_NAV 0x2B #define QCOM_SCM_PERM_READ 0x4 #define QCOM_SCM_PERM_WRITE 0x2 #define QCOM_SCM_PERM_EXEC 0x1 From 45ca30eb9dfe622b00ce352cf28ee141d243254b Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Mon, 9 Jan 2023 14:05:23 +0100 Subject: [PATCH 24/33] dt-bindings: firmware: qcom: scm: Separate VMIDs from header to bindings The SCM VMIDs represent predefined mappings that come from the irreplaceable and non-omittable firmware that comes with every Qualcomm SoC (unless you steal engineering samples from the factory) and help clarify otherwise totally magic numbers which we are required to pass to the secure world for some parts of the SoC to work at all (with modem being the prime example). On top of that, with changes to the rmtfs binding, secure VMIDs will become useful to have in device trees for readability. Separate them out and add to include/dt-bindings. Signed-off-by: Konrad Dybcio Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230109130523.298971-3-konrad.dybcio@linaro.org --- include/dt-bindings/firmware/qcom,scm.h | 16 ++++++++++++++++ include/linux/qcom_scm.h | 7 ++----- 2 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 include/dt-bindings/firmware/qcom,scm.h diff --git a/include/dt-bindings/firmware/qcom,scm.h b/include/dt-bindings/firmware/qcom,scm.h new file mode 100644 index 0000000000000..1a4e68fa07445 --- /dev/null +++ b/include/dt-bindings/firmware/qcom,scm.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */ +/* + * Copyright (c) 2010-2015, 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (C) 2015 Linaro Ltd. + */ + +#ifndef _DT_BINDINGS_FIRMWARE_QCOM_SCM_H +#define _DT_BINDINGS_FIRMWARE_QCOM_SCM_H + +#define QCOM_SCM_VMID_HLOS 0x3 +#define QCOM_SCM_VMID_MSS_MSA 0xF +#define QCOM_SCM_VMID_WLAN 0x18 +#define QCOM_SCM_VMID_WLAN_CE 0x19 +#define QCOM_SCM_VMID_NAV 0x2B + +#endif diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index 150b72edb8797..1e449a5d7f5c1 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -9,6 +9,8 @@ #include #include +#include + #define QCOM_SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF)) #define QCOM_SCM_CPU_PWR_DOWN_L2_ON 0x0 #define QCOM_SCM_CPU_PWR_DOWN_L2_OFF 0x1 @@ -51,11 +53,6 @@ enum qcom_scm_ice_cipher { QCOM_SCM_ICE_CIPHER_AES_256_CBC = 4, }; -#define QCOM_SCM_VMID_HLOS 0x3 -#define QCOM_SCM_VMID_MSS_MSA 0xF -#define QCOM_SCM_VMID_WLAN 0x18 -#define QCOM_SCM_VMID_WLAN_CE 0x19 -#define QCOM_SCM_VMID_NAV 0x2B #define QCOM_SCM_PERM_READ 0x4 #define QCOM_SCM_PERM_WRITE 0x2 #define QCOM_SCM_PERM_EXEC 0x1 From c525e181fda7f18b694dffb0f22686a4cb407cee Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 17 Jan 2023 02:48:40 +0000 Subject: [PATCH 25/33] dt-bindings: soc: qcom: smd-rpm: Exclude MSM8936 from glink-channels MSM8936/MSM8939 should like MSM8916 not require glink-channels. Reviewed-by: Krzysztof Kozlowski Acked-by: Krzysztof Kozlowski Signed-off-by: Bryan O'Donoghue Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230117024846.1367794-3-bryan.odonoghue@linaro.org --- Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml index 11c0f4dd797ce..16fd67c0bd1fd 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml @@ -80,6 +80,7 @@ if: enum: - qcom,rpm-apq8084 - qcom,rpm-msm8916 + - qcom,rpm-msm8936 - qcom,rpm-msm8974 - qcom,rpm-msm8976 - qcom,rpm-msm8953 From 017a7c11a8a29e266aa40c6d1bf2ba83f880f719 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Wed, 4 Jan 2023 12:53:45 +0100 Subject: [PATCH 26/33] soc: qcom: socinfo: Fix soc_id order The soc_id array is mostly ordered by the numeric "msm-id" defined in qcom,ids.h but some recent entries were added at the wrong place. While it does not make a functional difference it does make it harder to regenerate the entire array after adding a bunch of new IDs. Fixes: de320c07da3d ("soc: qcom: socinfo: Add MSM8956/76 SoC IDs to the soc_id table") Fixes: 147f6534b8ff ("soc: qcom: socinfo: Add SM8550 ID") Signed-off-by: Stephan Gerhold Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230104115348.25046-2-stephan@gerhold.net --- drivers/soc/qcom/socinfo.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 10efdbcfdf053..7258527816b0a 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -257,8 +257,6 @@ static const struct soc_id soc_id[] = { { qcom_board_id(MSM8926) }, { qcom_board_id(MSM8326) }, { qcom_board_id(MSM8916) }, - { qcom_board_id(MSM8956) }, - { qcom_board_id(MSM8976) }, { qcom_board_id(MSM8994) }, { qcom_board_id_named(APQ8074PRO_AA, "APQ8074PRO-AA") }, { qcom_board_id_named(APQ8074PRO_AB, "APQ8074PRO-AB") }, @@ -290,6 +288,8 @@ static const struct soc_id soc_id[] = { { qcom_board_id(MSM8616) }, { qcom_board_id(MSM8992) }, { qcom_board_id(APQ8094) }, + { qcom_board_id(MSM8956) }, + { qcom_board_id(MSM8976) }, { qcom_board_id(MDM9607) }, { qcom_board_id(APQ8096) }, { qcom_board_id(MSM8998) }, @@ -348,7 +348,6 @@ static const struct soc_id soc_id[] = { { qcom_board_id(IPQ6005) }, { qcom_board_id(QRB5165) }, { qcom_board_id(SM8450) }, - { qcom_board_id(SM8550) }, { qcom_board_id(SM7225) }, { qcom_board_id(SA8295P) }, { qcom_board_id(SA8540P) }, @@ -359,6 +358,7 @@ static const struct soc_id soc_id[] = { { qcom_board_id(SC7280) }, { qcom_board_id(SC7180P) }, { qcom_board_id(SM6375) }, + { qcom_board_id(SM8550) }, { qcom_board_id(QRU1000) }, { qcom_board_id(QDU1000) }, { qcom_board_id(QDU1010) }, From 8b949c0e2ce59c376818bc3f80db4b94b06981b7 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Wed, 4 Jan 2023 12:53:46 +0100 Subject: [PATCH 27/33] dt-bindings: arm: qcom,ids: Add QRD board ID QRD (Qualcomm Reference Design) = 0xb = 11 is used on many devices that were originally derived from some reference design provided by Qualcomm. Examples of existing devices in Linux would be: - msm8916-longcheer-l8150/l8910, msm8916-wingtech-wt88047 - msm8953-xiaomi-daisy/tissot/vince - msm8998-fxtec-pro1 - sm4250-oneplus-billie2 Add it to qcom,ids.h so the qcom,board-id properties can be rewritten more clearly using the macros in a future patch set, i.e. qcom,board-id = instead of qcom,board-id = <0x1000b 0x00> Signed-off-by: Stephan Gerhold Acked-by: Rob Herring Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230104115348.25046-3-stephan@gerhold.net --- include/dt-bindings/arm/qcom,ids.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dt-bindings/arm/qcom,ids.h b/include/dt-bindings/arm/qcom,ids.h index f7aef3f310d70..18746bd3e5954 100644 --- a/include/dt-bindings/arm/qcom,ids.h +++ b/include/dt-bindings/arm/qcom,ids.h @@ -165,6 +165,7 @@ #define QCOM_BOARD_ID_MTP 8 #define QCOM_BOARD_ID_DRAGONBOARD 10 +#define QCOM_BOARD_ID_QRD 11 #define QCOM_BOARD_ID_SBC 24 #endif /* _DT_BINDINGS_ARM_QCOM_IDS_H */ From 56abffc793befb19f30ec9d4bec24343529377f1 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Wed, 4 Jan 2023 12:53:47 +0100 Subject: [PATCH 28/33] dt-bindings: arm: qcom,ids: Add a bunch of older SoCs Sync the SoC IDs in qcom,ids.h with relevant entries from Qualcomm's LK bootloader [1] that is used for almost all older Qualcomm SoCs. Several of these are already supported, e.g.: - MSM8960 -> APQ8060, MSM8260, ... - MSM8976 -> APQ8076 - MSM8956 -> APQ8056 Others are currently being worked on, e.g.: - MSM8909(W) -> APQ8009(W), MSM8905, MSM8209, ... - MSM8939 -> MSM8239, ... And even all remaining ones added are close enough to what is already supported so that future support is realistic (if someone steps up to do the work). Add all of them at once to avoid having to add them one by one in the future. This will also benefit other projects making use of the same dt-bindings, e.g. bootloaders where adding support for all these SoCs is a bit easier than on Linux. [1]: https://git.codelinaro.org/clo/la/kernel/lk/-/blob/9d563e4a1d38c5a662bf94215ffc828db57c9e19/platform/msm_shared/smem.h#L286 Signed-off-by: Stephan Gerhold Acked-by: Rob Herring Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230104115348.25046-4-stephan@gerhold.net --- include/dt-bindings/arm/qcom,ids.h | 74 ++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/include/dt-bindings/arm/qcom,ids.h b/include/dt-bindings/arm/qcom,ids.h index 18746bd3e5954..22d7ba17804b1 100644 --- a/include/dt-bindings/arm/qcom,ids.h +++ b/include/dt-bindings/arm/qcom,ids.h @@ -11,36 +11,62 @@ * The MSM chipset and hardware revision used by Qualcomm bootloaders, DTS for * older chipsets (qcom,msm-id) and in socinfo driver: */ +#define QCOM_ID_MSM8260 70 +#define QCOM_ID_MSM8660 71 +#define QCOM_ID_APQ8060 86 #define QCOM_ID_MSM8960 87 #define QCOM_ID_APQ8064 109 +#define QCOM_ID_MSM8930 116 +#define QCOM_ID_MSM8630 117 +#define QCOM_ID_MSM8230 118 +#define QCOM_ID_APQ8030 119 +#define QCOM_ID_MSM8627 120 +#define QCOM_ID_MSM8227 121 #define QCOM_ID_MSM8660A 122 #define QCOM_ID_MSM8260A 123 #define QCOM_ID_APQ8060A 124 #define QCOM_ID_MSM8974 126 +#define QCOM_ID_MSM8225 127 +#define QCOM_ID_MSM8625 129 #define QCOM_ID_MPQ8064 130 #define QCOM_ID_MSM8960AB 138 #define QCOM_ID_APQ8060AB 139 #define QCOM_ID_MSM8260AB 140 #define QCOM_ID_MSM8660AB 141 +#define QCOM_ID_MSM8930AA 142 +#define QCOM_ID_MSM8630AA 143 +#define QCOM_ID_MSM8230AA 144 #define QCOM_ID_MSM8626 145 #define QCOM_ID_MSM8610 147 #define QCOM_ID_APQ8064AB 153 +#define QCOM_ID_MSM8930AB 154 +#define QCOM_ID_MSM8630AB 155 +#define QCOM_ID_MSM8230AB 156 +#define QCOM_ID_APQ8030AB 157 #define QCOM_ID_MSM8226 158 #define QCOM_ID_MSM8526 159 +#define QCOM_ID_APQ8030AA 160 #define QCOM_ID_MSM8110 161 #define QCOM_ID_MSM8210 162 #define QCOM_ID_MSM8810 163 #define QCOM_ID_MSM8212 164 #define QCOM_ID_MSM8612 165 #define QCOM_ID_MSM8112 166 +#define QCOM_ID_MSM8125 167 #define QCOM_ID_MSM8225Q 168 #define QCOM_ID_MSM8625Q 169 #define QCOM_ID_MSM8125Q 170 #define QCOM_ID_APQ8064AA 172 #define QCOM_ID_APQ8084 178 +#define QCOM_ID_MSM8130 179 +#define QCOM_ID_MSM8130AA 180 +#define QCOM_ID_MSM8130AB 181 +#define QCOM_ID_MSM8627AA 182 +#define QCOM_ID_MSM8227AA 183 #define QCOM_ID_APQ8074 184 #define QCOM_ID_MSM8274 185 #define QCOM_ID_MSM8674 186 +#define QCOM_ID_MDM9635 187 #define QCOM_ID_MSM8974PRO_AC 194 #define QCOM_ID_MSM8126 198 #define QCOM_ID_APQ8026 199 @@ -68,34 +94,72 @@ #define QCOM_ID_MSM8510 225 #define QCOM_ID_MSM8512 226 #define QCOM_ID_MSM8936 233 +#define QCOM_ID_MDM9640 234 #define QCOM_ID_MSM8939 239 #define QCOM_ID_APQ8036 240 #define QCOM_ID_APQ8039 241 +#define QCOM_ID_MSM8236 242 +#define QCOM_ID_MSM8636 243 +#define QCOM_ID_MSM8909 245 #define QCOM_ID_MSM8996 246 #define QCOM_ID_APQ8016 247 #define QCOM_ID_MSM8216 248 #define QCOM_ID_MSM8116 249 #define QCOM_ID_MSM8616 250 #define QCOM_ID_MSM8992 251 +#define QCOM_ID_APQ8092 252 #define QCOM_ID_APQ8094 253 +#define QCOM_ID_MSM8209 258 +#define QCOM_ID_MSM8208 259 +#define QCOM_ID_MDM9209 260 +#define QCOM_ID_MDM9309 261 +#define QCOM_ID_MDM9609 262 +#define QCOM_ID_MSM8239 263 +#define QCOM_ID_MSM8952 264 +#define QCOM_ID_APQ8009 265 #define QCOM_ID_MSM8956 266 +#define QCOM_ID_MSM8929 268 +#define QCOM_ID_MSM8629 269 +#define QCOM_ID_MSM8229 270 +#define QCOM_ID_APQ8029 271 +#define QCOM_ID_APQ8056 274 +#define QCOM_ID_MSM8609 275 +#define QCOM_ID_APQ8076 277 #define QCOM_ID_MSM8976 278 +#define QCOM_ID_MDM9650 279 +#define QCOM_ID_MDM9655 283 +#define QCOM_ID_MDM9250 284 +#define QCOM_ID_MDM9255 285 +#define QCOM_ID_MDM9350 286 +#define QCOM_ID_APQ8052 289 #define QCOM_ID_MDM9607 290 #define QCOM_ID_APQ8096 291 #define QCOM_ID_MSM8998 292 #define QCOM_ID_MSM8953 293 +#define QCOM_ID_MSM8937 294 +#define QCOM_ID_APQ8037 295 #define QCOM_ID_MDM8207 296 #define QCOM_ID_MDM9207 297 #define QCOM_ID_MDM9307 298 #define QCOM_ID_MDM9628 299 +#define QCOM_ID_MSM8909W 300 +#define QCOM_ID_APQ8009W 301 +#define QCOM_ID_MSM8996L 302 +#define QCOM_ID_MSM8917 303 #define QCOM_ID_APQ8053 304 #define QCOM_ID_MSM8996SG 305 +#define QCOM_ID_APQ8017 307 +#define QCOM_ID_MSM8217 308 +#define QCOM_ID_MSM8617 309 #define QCOM_ID_MSM8996AU 310 #define QCOM_ID_APQ8096AU 311 #define QCOM_ID_APQ8096SG 312 +#define QCOM_ID_MSM8940 313 +#define QCOM_ID_SDX201 314 #define QCOM_ID_SDM660 317 #define QCOM_ID_SDM630 318 #define QCOM_ID_APQ8098 319 +#define QCOM_ID_MSM8920 320 #define QCOM_ID_SDM845 321 #define QCOM_ID_MDM9206 322 #define QCOM_ID_IPQ8074 323 @@ -103,6 +167,8 @@ #define QCOM_ID_SDM658 325 #define QCOM_ID_SDA658 326 #define QCOM_ID_SDA630 327 +#define QCOM_ID_MSM8905 331 +#define QCOM_ID_SDX202 333 #define QCOM_ID_SDM450 338 #define QCOM_ID_SM8150 339 #define QCOM_ID_SDA845 341 @@ -114,10 +180,15 @@ #define QCOM_ID_SDM632 349 #define QCOM_ID_SDA632 350 #define QCOM_ID_SDA450 351 +#define QCOM_ID_SDM439 353 +#define QCOM_ID_SDM429 354 #define QCOM_ID_SM8250 356 #define QCOM_ID_SA8155 362 +#define QCOM_ID_SDA439 363 +#define QCOM_ID_SDA429 364 #define QCOM_ID_IPQ8070 375 #define QCOM_ID_IPQ8071 376 +#define QCOM_ID_QM215 386 #define QCOM_ID_IPQ8072A 389 #define QCOM_ID_IPQ8074A 390 #define QCOM_ID_IPQ8076A 391 @@ -127,11 +198,14 @@ #define QCOM_ID_IPQ8071A 396 #define QCOM_ID_IPQ6018 402 #define QCOM_ID_IPQ6028 403 +#define QCOM_ID_SDM429W 416 #define QCOM_ID_SM4250 417 #define QCOM_ID_IPQ6000 421 #define QCOM_ID_IPQ6010 422 #define QCOM_ID_SC7180 425 #define QCOM_ID_SM6350 434 +#define QCOM_ID_QCM2150 436 +#define QCOM_ID_SDA429W 437 #define QCOM_ID_SM8350 439 #define QCOM_ID_SM6115 444 #define QCOM_ID_SC8280XP 449 From 40017cebb111eed65bddfa993df7b0636be98de8 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Wed, 4 Jan 2023 12:53:48 +0100 Subject: [PATCH 29/33] soc: qcom: socinfo: Add a bunch of older SoCs Add the new SoCs added in qcom,ids.h to the soc_id array so they show up correctly in the socinfo sysfs. Signed-off-by: Stephan Gerhold Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230104115348.25046-5-stephan@gerhold.net --- drivers/soc/qcom/socinfo.c | 74 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 7258527816b0a..3b970a80f3aaa 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -221,36 +221,62 @@ struct soc_id { }; static const struct soc_id soc_id[] = { + { qcom_board_id(MSM8260) }, + { qcom_board_id(MSM8660) }, + { qcom_board_id(APQ8060) }, { qcom_board_id(MSM8960) }, { qcom_board_id(APQ8064) }, + { qcom_board_id(MSM8930) }, + { qcom_board_id(MSM8630) }, + { qcom_board_id(MSM8230) }, + { qcom_board_id(APQ8030) }, + { qcom_board_id(MSM8627) }, + { qcom_board_id(MSM8227) }, { qcom_board_id(MSM8660A) }, { qcom_board_id(MSM8260A) }, { qcom_board_id(APQ8060A) }, { qcom_board_id(MSM8974) }, + { qcom_board_id(MSM8225) }, + { qcom_board_id(MSM8625) }, { qcom_board_id(MPQ8064) }, { qcom_board_id(MSM8960AB) }, { qcom_board_id(APQ8060AB) }, { qcom_board_id(MSM8260AB) }, { qcom_board_id(MSM8660AB) }, + { qcom_board_id(MSM8930AA) }, + { qcom_board_id(MSM8630AA) }, + { qcom_board_id(MSM8230AA) }, { qcom_board_id(MSM8626) }, { qcom_board_id(MSM8610) }, { qcom_board_id(APQ8064AB) }, + { qcom_board_id(MSM8930AB) }, + { qcom_board_id(MSM8630AB) }, + { qcom_board_id(MSM8230AB) }, + { qcom_board_id(APQ8030AB) }, { qcom_board_id(MSM8226) }, { qcom_board_id(MSM8526) }, + { qcom_board_id(APQ8030AA) }, { qcom_board_id(MSM8110) }, { qcom_board_id(MSM8210) }, { qcom_board_id(MSM8810) }, { qcom_board_id(MSM8212) }, { qcom_board_id(MSM8612) }, { qcom_board_id(MSM8112) }, + { qcom_board_id(MSM8125) }, { qcom_board_id(MSM8225Q) }, { qcom_board_id(MSM8625Q) }, { qcom_board_id(MSM8125Q) }, { qcom_board_id(APQ8064AA) }, { qcom_board_id(APQ8084) }, + { qcom_board_id(MSM8130) }, + { qcom_board_id(MSM8130AA) }, + { qcom_board_id(MSM8130AB) }, + { qcom_board_id(MSM8627AA) }, + { qcom_board_id(MSM8227AA) }, { qcom_board_id(APQ8074) }, { qcom_board_id(MSM8274) }, { qcom_board_id(MSM8674) }, + { qcom_board_id(MDM9635) }, { qcom_board_id_named(MSM8974PRO_AC, "MSM8974PRO-AC") }, { qcom_board_id(MSM8126) }, { qcom_board_id(APQ8026) }, @@ -278,34 +304,72 @@ static const struct soc_id soc_id[] = { { qcom_board_id(MSM8510) }, { qcom_board_id(MSM8512) }, { qcom_board_id(MSM8936) }, + { qcom_board_id(MDM9640) }, { qcom_board_id(MSM8939) }, { qcom_board_id(APQ8036) }, { qcom_board_id(APQ8039) }, + { qcom_board_id(MSM8236) }, + { qcom_board_id(MSM8636) }, + { qcom_board_id(MSM8909) }, { qcom_board_id(MSM8996) }, { qcom_board_id(APQ8016) }, { qcom_board_id(MSM8216) }, { qcom_board_id(MSM8116) }, { qcom_board_id(MSM8616) }, { qcom_board_id(MSM8992) }, + { qcom_board_id(APQ8092) }, { qcom_board_id(APQ8094) }, + { qcom_board_id(MSM8209) }, + { qcom_board_id(MSM8208) }, + { qcom_board_id(MDM9209) }, + { qcom_board_id(MDM9309) }, + { qcom_board_id(MDM9609) }, + { qcom_board_id(MSM8239) }, + { qcom_board_id(MSM8952) }, + { qcom_board_id(APQ8009) }, { qcom_board_id(MSM8956) }, + { qcom_board_id(MSM8929) }, + { qcom_board_id(MSM8629) }, + { qcom_board_id(MSM8229) }, + { qcom_board_id(APQ8029) }, + { qcom_board_id(APQ8056) }, + { qcom_board_id(MSM8609) }, + { qcom_board_id(APQ8076) }, { qcom_board_id(MSM8976) }, + { qcom_board_id(MDM9650) }, + { qcom_board_id(MDM9655) }, + { qcom_board_id(MDM9250) }, + { qcom_board_id(MDM9255) }, + { qcom_board_id(MDM9350) }, + { qcom_board_id(APQ8052) }, { qcom_board_id(MDM9607) }, { qcom_board_id(APQ8096) }, { qcom_board_id(MSM8998) }, { qcom_board_id(MSM8953) }, + { qcom_board_id(MSM8937) }, + { qcom_board_id(APQ8037) }, { qcom_board_id(MDM8207) }, { qcom_board_id(MDM9207) }, { qcom_board_id(MDM9307) }, { qcom_board_id(MDM9628) }, + { qcom_board_id(MSM8909W) }, + { qcom_board_id(APQ8009W) }, + { qcom_board_id(MSM8996L) }, + { qcom_board_id(MSM8917) }, { qcom_board_id(APQ8053) }, { qcom_board_id(MSM8996SG) }, + { qcom_board_id(APQ8017) }, + { qcom_board_id(MSM8217) }, + { qcom_board_id(MSM8617) }, { qcom_board_id(MSM8996AU) }, { qcom_board_id(APQ8096AU) }, { qcom_board_id(APQ8096SG) }, + { qcom_board_id(MSM8940) }, + { qcom_board_id(SDX201) }, { qcom_board_id(SDM660) }, { qcom_board_id(SDM630) }, { qcom_board_id(APQ8098) }, + { qcom_board_id(MSM8920) }, { qcom_board_id(SDM845) }, { qcom_board_id(MDM9206) }, { qcom_board_id(IPQ8074) }, @@ -313,6 +377,8 @@ static const struct soc_id soc_id[] = { { qcom_board_id(SDM658) }, { qcom_board_id(SDA658) }, { qcom_board_id(SDA630) }, + { qcom_board_id(MSM8905) }, + { qcom_board_id(SDX202) }, { qcom_board_id(SDM450) }, { qcom_board_id(SM8150) }, { qcom_board_id(SDA845) }, @@ -324,10 +390,15 @@ static const struct soc_id soc_id[] = { { qcom_board_id(SDM632) }, { qcom_board_id(SDA632) }, { qcom_board_id(SDA450) }, + { qcom_board_id(SDM439) }, + { qcom_board_id(SDM429) }, { qcom_board_id(SM8250) }, { qcom_board_id(SA8155) }, + { qcom_board_id(SDA439) }, + { qcom_board_id(SDA429) }, { qcom_board_id(IPQ8070) }, { qcom_board_id(IPQ8071) }, + { qcom_board_id(QM215) }, { qcom_board_id(IPQ8072A) }, { qcom_board_id(IPQ8074A) }, { qcom_board_id(IPQ8076A) }, @@ -337,11 +408,14 @@ static const struct soc_id soc_id[] = { { qcom_board_id(IPQ8071A) }, { qcom_board_id(IPQ6018) }, { qcom_board_id(IPQ6028) }, + { qcom_board_id(SDM429W) }, { qcom_board_id(SM4250) }, { qcom_board_id(IPQ6000) }, { qcom_board_id(IPQ6010) }, { qcom_board_id(SC7180) }, { qcom_board_id(SM6350) }, + { qcom_board_id(QCM2150) }, + { qcom_board_id(SDA429W) }, { qcom_board_id(SM8350) }, { qcom_board_id(SM6115) }, { qcom_board_id(SC8280XP) }, From a36489778ba8f7eb98c3e9fca2d300090bf1ffcd Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Fri, 13 Jan 2023 16:22:31 +0100 Subject: [PATCH 30/33] Revert "soc: qcom: rpmpd: Add SM4250 support" SM4250 and SM6115 use a shared device tree and the RPMPDs are identical. There's no need for a separate entry, so remove it. This reverts commit 5b617b1b10c1c6a4365d8f956032e95c53b8e388. Signed-off-by: Konrad Dybcio Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230113152232.2624545-1-konrad.dybcio@linaro.org --- drivers/soc/qcom/rpmpd.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/drivers/soc/qcom/rpmpd.c b/drivers/soc/qcom/rpmpd.c index f0db6a10cf4ef..337b1ad1cd3bf 100644 --- a/drivers/soc/qcom/rpmpd.c +++ b/drivers/soc/qcom/rpmpd.c @@ -471,23 +471,6 @@ static const struct rpmpd_desc qcm2290_desc = { .max_state = RPM_SMD_LEVEL_TURBO_NO_CPR, }; -static struct rpmpd *sm4250_rpmpds[] = { - [SM4250_VDDCX] = &sm6115_vddcx, - [SM4250_VDDCX_AO] = &sm6115_vddcx_ao, - [SM4250_VDDCX_VFL] = &sm6115_vddcx_vfl, - [SM4250_VDDMX] = &sm6115_vddmx, - [SM4250_VDDMX_AO] = &sm6115_vddmx_ao, - [SM4250_VDDMX_VFL] = &sm6115_vddmx_vfl, - [SM4250_VDD_LPI_CX] = &sm6115_vdd_lpi_cx, - [SM4250_VDD_LPI_MX] = &sm6115_vdd_lpi_mx, -}; - -static const struct rpmpd_desc sm4250_desc = { - .rpmpds = sm4250_rpmpds, - .num_pds = ARRAY_SIZE(sm4250_rpmpds), - .max_state = RPM_SMD_LEVEL_TURBO_NO_CPR, -}; - static const struct of_device_id rpmpd_match_table[] = { { .compatible = "qcom,mdm9607-rpmpd", .data = &mdm9607_desc }, { .compatible = "qcom,msm8226-rpmpd", .data = &msm8226_desc }, @@ -502,7 +485,6 @@ static const struct of_device_id rpmpd_match_table[] = { { .compatible = "qcom,qcm2290-rpmpd", .data = &qcm2290_desc }, { .compatible = "qcom,qcs404-rpmpd", .data = &qcs404_desc }, { .compatible = "qcom,sdm660-rpmpd", .data = &sdm660_desc }, - { .compatible = "qcom,sm4250-rpmpd", .data = &sm4250_desc }, { .compatible = "qcom,sm6115-rpmpd", .data = &sm6115_desc }, { .compatible = "qcom,sm6125-rpmpd", .data = &sm6125_desc }, { .compatible = "qcom,sm6375-rpmpd", .data = &sm6375_desc }, From dfe5ac7023624617d402ca1c295552fbd271e20c Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Fri, 13 Jan 2023 16:22:32 +0100 Subject: [PATCH 31/33] Revert "dt-bindings: power: rpmpd: Add SM4250 support" SM4250 and SM6115 use a shared device tree and the RPMPDs are identical. There's no need for a separate entry, so remove it. This reverts commit 45ac44ed10e58cf9b510e6552317ed7d2602346f. Signed-off-by: Konrad Dybcio Acked-by: Krzysztof Kozlowski Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230113152232.2624545-2-konrad.dybcio@linaro.org --- .../devicetree/bindings/power/qcom,rpmpd.yaml | 1 - include/dt-bindings/power/qcom-rpmpd.h | 10 ---------- 2 files changed, 11 deletions(-) diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml index 1778d98515108..afad3135ed67c 100644 --- a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml +++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml @@ -40,7 +40,6 @@ properties: - qcom,sdm845-rpmhpd - qcom,sdx55-rpmhpd - qcom,sdx65-rpmhpd - - qcom,sm4250-rpmpd - qcom,sm6115-rpmpd - qcom,sm6125-rpmpd - qcom,sm6350-rpmhpd diff --git a/include/dt-bindings/power/qcom-rpmpd.h b/include/dt-bindings/power/qcom-rpmpd.h index 3117bf7d5ebfc..6df4ee400ef8b 100644 --- a/include/dt-bindings/power/qcom-rpmpd.h +++ b/include/dt-bindings/power/qcom-rpmpd.h @@ -325,16 +325,6 @@ #define SDM660_SSCMX 8 #define SDM660_SSCMX_VFL 9 -/* SM4250 Power Domains */ -#define SM4250_VDDCX 0 -#define SM4250_VDDCX_AO 1 -#define SM4250_VDDCX_VFL 2 -#define SM4250_VDDMX 3 -#define SM4250_VDDMX_AO 4 -#define SM4250_VDDMX_VFL 5 -#define SM4250_VDD_LPI_CX 6 -#define SM4250_VDD_LPI_MX 7 - /* SM6115 Power Domains */ #define SM6115_VDDCX 0 #define SM6115_VDDCX_AO 1 From afb37e2577938bb854f20d428d8f29a01ea8bc93 Mon Sep 17 00:00:00 2001 From: Guru Das Srinagesh Date: Fri, 13 Jan 2023 21:41:13 +0530 Subject: [PATCH 32/33] dt-bindings: firmware: qcom,scm: Add optional interrupt Add an interrupt specification to the bindings to support the wait-queue feature on SM8450 SoCs. Signed-off-by: Guru Das Srinagesh Signed-off-by: Sibi Sankar Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230113161114.22607-2-quic_sibis@quicinc.com --- .../devicetree/bindings/firmware/qcom,scm.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml index 9312a0953ea8b..4193492ba73e0 100644 --- a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml +++ b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml @@ -74,6 +74,12 @@ properties: '#reset-cells': const: 1 + interrupts: + description: + The wait-queue interrupt that firmware raises as part of handshake + protocol to handle sleeping SCM calls. + maxItems: 1 + qcom,dload-mode: $ref: /schemas/types.yaml#/definitions/phandle-array items: @@ -163,6 +169,18 @@ allOf: properties: interconnects: false + # Interrupts + - if: + not: + properties: + compatible: + contains: + enum: + - qcom,scm-sm8450 + then: + properties: + interrupts: false + required: - compatible From 6bf32599223634294cdc6efb359ffaab1d68073c Mon Sep 17 00:00:00 2001 From: Guru Das Srinagesh Date: Fri, 13 Jan 2023 21:41:14 +0530 Subject: [PATCH 33/33] firmware: qcom: scm: Add wait-queue handling logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the firmware (FW) supports multiple requests per VM, multiple requests from the same/different VM can reach the firmware at the same time. Since the firmware currently being used has limited resources, it guards them with a resource lock and puts requests on a wait-queue internally and signals to HLOS that it is doing so. It does this by returning a new return value in addition to success or error: SCM_WAITQ_SLEEP. A sleeping SCM call can be woken up by an interrupt that the FW raises. 1) SCM_WAITQ_SLEEP: When an SCM call receives this return value instead of success or error, FW has placed this call on a wait-queue and has signalled HLOS to put it to non-interruptible sleep. Along with this return value, FW also passes to HLOS `wq_ctx` - a unique number (UID) identifying the wait-queue that it has put the call on, internally. This is to help HLOS with its own bookkeeping to wake this sleeping call later. Additionally, FW also passes to HLOS `smc_call_ctx` - a UID identifying the SCM call thus being put to sleep. This is also for HLOS' bookkeeping to wake this call up later. These two additional values are passed via the a1 and a2 registers. N.B.: The "ctx" in the above UID names = "context". The handshake mechanism that HLOS uses to talk to FW about wait-queue operations involves two new SMC calls. 1) get_wq_ctx(): Arguments: None Returns: wq_ctx, flags, more_pending Get the wait-queue context, and wake up either one or all of the sleeping SCM calls associated with that wait-queue. Additionally, repeat this if there are more wait-queues that are ready to have their requests woken up (`more_pending`). 2) wq_resume(smc_call_ctx): Arguments: smc_call_ctx HLOS needs to issue this in response to receiving an IRQ, passing to FW the same smc_call_ctx that FW receives from HLOS via the get_wq_ctx() call. (The mechanism to wake a SMC call back up is described in detail below) VM_1 VM_2 Firmware │ │ │ │ │ │ │ │ │ │ │ │ │ REQUEST_1 │ │ ├────────────────────────┼─────────────────────────────────┤ │ │ │ │ │ ┌──┼──┐ │ │ │ │ │ │ │ REQUEST_2 │ │ │ │ ├──────────────────────────────┼──┤ │ │ │ │ │ │Resource │ │ │ │ │is busy │ │ {WQ_SLEEP} │ │ │ │ │◄─────────────────────────────┼──┤ │ │ │ wq_ctx, smc_call_ctx │ │ │ │ │ └──┼──┘ │ REQUEST_1 COMPLETE │ │ │◄───────────────────────┼─────────────────────────────────┤ │ │ │ │ │ IRQ │ │ │◄─-------------------------------│ │ │ │ │ │ get_wq_ctx() │ │ ├────────────────────────────────►│ │ │ │ │ │ │ │ │◄────────────────────────────────┤ │ │ wq_ctx, flags, and │ │ │ more_pending │ │ │ │ │ │ │ │ │ wq_resume(smc_call_ctx) │ │ ├────────────────────────────────►│ │ │ │ │ │ │ │ │ REQUEST_2 COMPLETE │ │ │◄────────────────────────────────┤ │ │ │ │ │ │ With the exception of get_wq_ctx(), the other SMC call wq_resume() can return WQ_SLEEP (these nested rounds of WQ_SLEEP are not shown in the above diagram for the sake of simplicity). Therefore, introduce a new do-while loop to handle multiple WQ_SLEEP return values for the same parent SCM call. Request Completion in the above diagram refers to either a success return value (zero) or error (and not SMC_WAITQ_SLEEP) Also add the interrupt handler that wakes up a sleeping SCM call. Signed-off-by: Guru Das Srinagesh Co-developed-by: Sibi Sankar Signed-off-by: Sibi Sankar Reviewed-by: Guru Das Srinagesh Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230113161114.22607-3-quic_sibis@quicinc.com --- drivers/firmware/qcom_scm-smc.c | 86 ++++++++++++++++++++++++++++--- drivers/firmware/qcom_scm.c | 90 ++++++++++++++++++++++++++++++++- drivers/firmware/qcom_scm.h | 8 +++ 3 files changed, 176 insertions(+), 8 deletions(-) diff --git a/drivers/firmware/qcom_scm-smc.c b/drivers/firmware/qcom_scm-smc.c index d111833364ba4..bb3235a64b8f0 100644 --- a/drivers/firmware/qcom_scm-smc.c +++ b/drivers/firmware/qcom_scm-smc.c @@ -52,29 +52,97 @@ static void __scm_smc_do_quirk(const struct arm_smccc_args *smc, } while (res->a0 == QCOM_SCM_INTERRUPTED); } -static void __scm_smc_do(const struct arm_smccc_args *smc, - struct arm_smccc_res *res, bool atomic) +static void fill_wq_resume_args(struct arm_smccc_args *resume, u32 smc_call_ctx) { - int retry_count = 0; + memset(resume->args, 0, sizeof(resume->args[0]) * ARRAY_SIZE(resume->args)); + + resume->args[0] = ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, + ARM_SMCCC_SMC_64, ARM_SMCCC_OWNER_SIP, + SCM_SMC_FNID(QCOM_SCM_SVC_WAITQ, QCOM_SCM_WAITQ_RESUME)); + + resume->args[1] = QCOM_SCM_ARGS(1); + + resume->args[2] = smc_call_ctx; +} + +int scm_get_wq_ctx(u32 *wq_ctx, u32 *flags, u32 *more_pending) +{ + int ret; + struct arm_smccc_res get_wq_res; + struct arm_smccc_args get_wq_ctx = {0}; + + get_wq_ctx.args[0] = ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, + ARM_SMCCC_SMC_64, ARM_SMCCC_OWNER_SIP, + SCM_SMC_FNID(QCOM_SCM_SVC_WAITQ, QCOM_SCM_WAITQ_GET_WQ_CTX)); + + /* Guaranteed to return only success or error, no WAITQ_* */ + __scm_smc_do_quirk(&get_wq_ctx, &get_wq_res); + ret = get_wq_res.a0; + if (ret) + return ret; + + *wq_ctx = get_wq_res.a1; + *flags = get_wq_res.a2; + *more_pending = get_wq_res.a3; + + return 0; +} + +static int __scm_smc_do_quirk_handle_waitq(struct device *dev, struct arm_smccc_args *waitq, + struct arm_smccc_res *res) +{ + int ret; + u32 wq_ctx, smc_call_ctx; + struct arm_smccc_args resume; + struct arm_smccc_args *smc = waitq; + + do { + __scm_smc_do_quirk(smc, res); + + if (res->a0 == QCOM_SCM_WAITQ_SLEEP) { + wq_ctx = res->a1; + smc_call_ctx = res->a2; + + ret = qcom_scm_wait_for_wq_completion(wq_ctx); + if (ret) + return ret; + + fill_wq_resume_args(&resume, smc_call_ctx); + smc = &resume; + } + } while (res->a0 == QCOM_SCM_WAITQ_SLEEP); + + return 0; +} + +static int __scm_smc_do(struct device *dev, struct arm_smccc_args *smc, + struct arm_smccc_res *res, bool atomic) +{ + int ret, retry_count = 0; if (atomic) { __scm_smc_do_quirk(smc, res); - return; + return 0; } do { mutex_lock(&qcom_scm_lock); - __scm_smc_do_quirk(smc, res); + ret = __scm_smc_do_quirk_handle_waitq(dev, smc, res); mutex_unlock(&qcom_scm_lock); + if (ret) + return ret; + if (res->a0 == QCOM_SCM_V2_EBUSY) { if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY) break; msleep(QCOM_SCM_EBUSY_WAIT_MS); } } while (res->a0 == QCOM_SCM_V2_EBUSY); + + return 0; } @@ -83,7 +151,7 @@ int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc, struct qcom_scm_res *res, bool atomic) { int arglen = desc->arginfo & 0xf; - int i; + int i, ret; dma_addr_t args_phys = 0; void *args_virt = NULL; size_t alloc_len; @@ -135,13 +203,17 @@ int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc, smc.args[SCM_SMC_LAST_REG_IDX] = args_phys; } - __scm_smc_do(&smc, &smc_res, atomic); + /* ret error check follows after args_virt cleanup*/ + ret = __scm_smc_do(dev, &smc, &smc_res, atomic); if (args_virt) { dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE); kfree(args_virt); } + if (ret) + return ret; + if (res) { res->result[0] = smc_res.a1; res->result[1] = smc_res.a2; diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index cdbfe54c81467..2000323722bf9 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -4,6 +4,8 @@ */ #include #include +#include +#include #include #include #include @@ -13,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +36,7 @@ struct qcom_scm { struct clk *iface_clk; struct clk *bus_clk; struct icc_path *path; + struct completion waitq_comp; struct reset_controller_dev reset; /* control access to the interconnect path */ @@ -63,6 +67,9 @@ static const u8 qcom_scm_cpu_warm_bits[QCOM_SCM_BOOT_MAX_CPUS] = { BIT(2), BIT(1), BIT(4), BIT(6) }; +#define QCOM_SMC_WAITQ_FLAG_WAKE_ONE BIT(0) +#define QCOM_SMC_WAITQ_FLAG_WAKE_ALL BIT(1) + static const char * const qcom_scm_convention_names[] = { [SMC_CONVENTION_UNKNOWN] = "unknown", [SMC_CONVENTION_ARM_32] = "smc arm 32", @@ -1325,11 +1332,79 @@ bool qcom_scm_is_available(void) } EXPORT_SYMBOL(qcom_scm_is_available); +static int qcom_scm_assert_valid_wq_ctx(u32 wq_ctx) +{ + /* FW currently only supports a single wq_ctx (zero). + * TODO: Update this logic to include dynamic allocation and lookup of + * completion structs when FW supports more wq_ctx values. + */ + if (wq_ctx != 0) { + dev_err(__scm->dev, "Firmware unexpectedly passed non-zero wq_ctx\n"); + return -EINVAL; + } + + return 0; +} + +int qcom_scm_wait_for_wq_completion(u32 wq_ctx) +{ + int ret; + + ret = qcom_scm_assert_valid_wq_ctx(wq_ctx); + if (ret) + return ret; + + wait_for_completion(&__scm->waitq_comp); + + return 0; +} + +static int qcom_scm_waitq_wakeup(struct qcom_scm *scm, unsigned int wq_ctx) +{ + int ret; + + ret = qcom_scm_assert_valid_wq_ctx(wq_ctx); + if (ret) + return ret; + + complete(&__scm->waitq_comp); + + return 0; +} + +static irqreturn_t qcom_scm_irq_handler(int irq, void *data) +{ + int ret; + struct qcom_scm *scm = data; + u32 wq_ctx, flags, more_pending = 0; + + do { + ret = scm_get_wq_ctx(&wq_ctx, &flags, &more_pending); + if (ret) { + dev_err(scm->dev, "GET_WQ_CTX SMC call failed: %d\n", ret); + goto out; + } + + if (flags != QCOM_SMC_WAITQ_FLAG_WAKE_ONE && + flags != QCOM_SMC_WAITQ_FLAG_WAKE_ALL) { + dev_err(scm->dev, "Invalid flags found for wq_ctx: %u\n", flags); + goto out; + } + + ret = qcom_scm_waitq_wakeup(scm, wq_ctx); + if (ret) + goto out; + } while (more_pending); + +out: + return IRQ_HANDLED; +} + static int qcom_scm_probe(struct platform_device *pdev) { struct qcom_scm *scm; unsigned long clks; - int ret; + int irq, ret; scm = devm_kzalloc(&pdev->dev, sizeof(*scm), GFP_KERNEL); if (!scm) @@ -1402,6 +1477,19 @@ static int qcom_scm_probe(struct platform_device *pdev) __scm = scm; __scm->dev = &pdev->dev; + init_completion(&__scm->waitq_comp); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + if (irq != -ENXIO) + return irq; + } else { + ret = devm_request_threaded_irq(__scm->dev, irq, NULL, qcom_scm_irq_handler, + IRQF_ONESHOT, "qcom-scm", __scm); + if (ret < 0) + return dev_err_probe(scm->dev, ret, "Failed to request qcom-scm irq\n"); + } + __get_convention(); /* diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index db3d08a012090..e6e512bd57d1b 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -60,6 +60,9 @@ struct qcom_scm_res { u64 result[MAX_QCOM_SCM_RETS]; }; +int qcom_scm_wait_for_wq_completion(u32 wq_ctx); +int scm_get_wq_ctx(u32 *wq_ctx, u32 *flags, u32 *more_pending); + #define SCM_SMC_FNID(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF)) extern int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc, enum qcom_scm_convention qcom_convention, @@ -129,6 +132,10 @@ extern int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc, #define QCOM_SCM_SMMU_CONFIG_ERRATA1 0x03 #define QCOM_SCM_SMMU_CONFIG_ERRATA1_CLIENT_ALL 0x02 +#define QCOM_SCM_SVC_WAITQ 0x24 +#define QCOM_SCM_WAITQ_RESUME 0x02 +#define QCOM_SCM_WAITQ_GET_WQ_CTX 0x03 + /* common error codes */ #define QCOM_SCM_V2_EBUSY -12 #define QCOM_SCM_ENOMEM -5 @@ -137,6 +144,7 @@ extern int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc, #define QCOM_SCM_EINVAL_ARG -2 #define QCOM_SCM_ERROR -1 #define QCOM_SCM_INTERRUPTED 1 +#define QCOM_SCM_WAITQ_SLEEP 2 static inline int qcom_scm_remap_error(int err) {