From 6bc677d3be7dfff71a269ebf3abb49c92a170b26 Mon Sep 17 00:00:00 2001 From: Taniya Das Date: Tue, 2 Jun 2026 20:51:49 +0530 Subject: [PATCH 1/5] FROMLIST: dt-bindings: clock: qcom: Add bindings for PDM GP_MN clock divider Add device tree bindings for the Qualcomm Peripheral Web's PDM GP_MN clock divider. The hardware generates a fractional output frequency from a fixed input clock (typically TCXO4) using the relation Fout = Fin * (M / N), with duty cycle controlled by a separate D register. The clock output is routed over a gpio controlled pin. Link: https://lore.kernel.org/r/20260602-pdm_clk_gp_mnd_v1-v1-1-1522662b6c53@oss.qualcomm.com Signed-off-by: Taniya Das --- .../bindings/clock/qcom,clk-gp-mnd.yaml | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/qcom,clk-gp-mnd.yaml diff --git a/Documentation/devicetree/bindings/clock/qcom,clk-gp-mnd.yaml b/Documentation/devicetree/bindings/clock/qcom,clk-gp-mnd.yaml new file mode 100644 index 0000000000000..c1688bb3d68d8 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,clk-gp-mnd.yaml @@ -0,0 +1,105 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/qcom,clk-gp-mnd.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Peripheral Web's PDM GP_MN Clock Divider + +maintainers: + - Taniya Das + +description: | + The Peripheral Web's PDM GP_MN clock divider receives an input clock + (TCXO4) with frequency Fin and generates an output clock with + frequency Fout = Fin * (M / N) and a duty cycle controlled by D + and routed over a gpio pin. + + The divider is configured using three registers: + + - GP_MN_CLK_MDIV: holds the M value. + - GP_MN_CLK_NDIV: holds the ones complement of (N - M). + - GP_MN_CLK_DUTY: holds the D value. + + For every N input clock cycles the GP_MN produces M output clock + cycles. D is the number of native clock cycles in which the GP_MN + output is low, counted over 2^13 native clock cycles. + + Hardware constraints: + + M <= 511 + N <= 8191 + N > 2 * M + M < D < (N - M) + M and N must be coprime (no common divisor) + +properties: + compatible: + const: qcom,clk-gp-mnd + + reg: + maxItems: 1 + + clocks: + items: + - description: PDM XO4 source clock + - description: PDM AHB bus clock for register access + + clock-names: + items: + - const: pdm_clk + - const: ahb_clk + + '#clock-cells': + const: 0 + + clock-output-names: + maxItems: 1 + + pinctrl-0: + description: Pin configuration for the GP_MN output in the active state. + + pinctrl-names: + items: + - const: active + + assigned-clocks: + maxItems: 1 + description: Parent clock phandle used to set the input frequency. + + assigned-clock-rates: + maxItems: 1 + description: | + Rate for the parent clock in Hz. + Supported rates: 19200000, 9600000, 6400000, 4800000. + +required: + - compatible + - reg + - clocks + - clock-names + - '#clock-cells' + - clock-output-names + - pinctrl-0 + - pinctrl-names + - assigned-clocks + - assigned-clock-rates + +additionalProperties: false + +examples: + - | + #include + gp_mn: clock-controller@88d3000 { + compatible = "qcom,clk-gp-mnd"; + reg = <0x88d3000 0xc>; + clocks = <&gcc GCC_PDM_XO4_CLK>, + <&gcc GCC_PDM_AHB_CLK>; + clock-names = "pdm_clk", "ahb_clk"; + clock-output-names = "gp_mn_clk"; + pinctrl-0 = <&gp_mn_pin_active>; + pinctrl-names = "active"; + assigned-clocks = <&gcc GCC_PDM_XO4_CLK>; + assigned-clock-rates = <4800000>; + #clock-cells = <0>; + }; From e7804943275c9fc148c46225aa2b2c1a5fe142c3 Mon Sep 17 00:00:00 2001 From: Taniya Das Date: Thu, 4 Jun 2026 23:46:51 +0530 Subject: [PATCH 2/5] FROMLIST: clk: qcom: Add a driver for PDM GP_MN fractional clock divider The PDM (Pulse Density Modulation) hardware block on Qualcomm SoCs contains a GP_MN clock divider that produces a fractional output frequency from a fixed input clock (typically TCXO4): Fout = Fin * (M / N) The hardware encodes the period in the NDIV register as the 1's complement of (N - M), and controls the duty cycle via a separate DUTY register that counts the number of low-phase native clock cycles over the period N. Add a standalone platform driver for this block that uses rational_best_approximation() to find the closest M/N pair within the 9-bit M and 13-bit N hardware limits, programs the MDIV, NDIV, and DUTY registers via regmap, and implements the full clk_ops surface including determine_rate, set_rate, recalc_rate, get_duty_cycle, and set_duty_cycle. The PDM AHB bus clock is gated around every register access. Link: https://lore.kernel.org/all/20260602-pdm_clk_gp_mnd_v1-v1-2-1522662b6c53@oss.qualcomm.com Signed-off-by: Taniya Das --- drivers/clk/qcom/Kconfig | 15 ++ drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/clk-gp-mnd.c | 333 ++++++++++++++++++++++++++++++++++ 3 files changed, 349 insertions(+) create mode 100644 drivers/clk/qcom/clk-gp-mnd.c diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 58b53fa42bad6..f442034410da9 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -1635,4 +1635,19 @@ config SM_VIDEOCC_8450 SM8450 or SM8475 devices. Say Y if you want to support video devices and functionality such as video encode/decode. + +config QCOM_CLK_GP_MND + tristate "Qualcomm PDM GP_MN clock divider" + depends on ARM64 || COMPILE_TEST + help + Support for the Qualcomm PDM GP_MN clock divider found in PDM + (Pulse Density Modulation) hardware blocks. + Given an input clock of frequency Fin (TCXO4), the output + frequency is Fout = Fin * (M / N). For every N input cycles + the divider produces M output cycles. D controls the duty + cycle: it is the number of native clock cycles in which the + GP_MN output is low, counted over 8192 native clock cycles. + + Say Y or M if you want to support GP_MN-based frequency and + duty-cycle configuration on Qualcomm SoCs. endif diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 5a277eb53ac9b..ebb29858febc7 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -193,6 +193,7 @@ obj-$(CONFIG_SM_VIDEOCC_8450) += videocc-sm8450.o obj-$(CONFIG_SM_VIDEOCC_8550) += videocc-sm8550.o obj-$(CONFIG_SM_VIDEOCC_MILOS) += videocc-milos.o obj-$(CONFIG_SPMI_PMIC_CLKDIV) += clk-spmi-pmic-div.o +obj-$(CONFIG_QCOM_CLK_GP_MND) += clk-gp-mnd.o obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o obj-$(CONFIG_QCOM_HFPLL) += hfpll.o obj-$(CONFIG_KRAITCC) += krait-cc.o diff --git a/drivers/clk/qcom/clk-gp-mnd.c b/drivers/clk/qcom/clk-gp-mnd.c new file mode 100644 index 0000000000000..826b6b62ddc7b --- /dev/null +++ b/drivers/clk/qcom/clk-gp-mnd.c @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * PDM GP_MND clock divider register offsets. + * + * The hardware computes: + * Fout = Fin * (M / N) + * + * with duty cycle controlled by D, where M < D < (N - M). + * + * Register encoding: + * MDIV = M + * NDIV = ~(N - M) [1's complement of (N - M), masked to N_REG_WIDTH bits] + * DUTY = D + */ +#define GP_MND_MDIV_REG 0x0 +#define GP_MND_NDIV_REG 0x4 +#define GP_MND_DUTY_REG 0x8 + +#define GP_MND_M_WIDTH 9 +#define GP_MND_N_WIDTH 13 + +#define GP_MND_MAX_M GENMASK(GP_MND_M_WIDTH - 1, 0) +#define GP_MND_MAX_N GENMASK(GP_MND_N_WIDTH - 1, 0) + +/** + * struct clk_gp_mnd - GP_MND fractional clock divider + * @pdm_ahb_clk: AHB bus clock required for register access + * @regmap: register map for the PDM block + * @hw: handle between common and hardware-specific interfaces + * @m_val: M value (numerator) + * @n_val: N value (period) + */ +struct clk_gp_mnd { + struct clk *pdm_ahb_clk; + struct regmap *regmap; + struct clk_hw hw; + unsigned int m_val; + unsigned int n_val; +}; + +#define to_clk_gp_mnd(_hw) container_of(_hw, struct clk_gp_mnd, hw) + +static int gp_mnd_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + unsigned long m = 0, n = 0; + + rational_best_approximation(req->rate, req->best_parent_rate, + (unsigned long)GP_MND_MAX_M, + (unsigned long)GP_MND_MAX_N, + &m, &n); + + if (!m || !n) + return -EINVAL; + + /* N = 2M + 1 leaves no valid D satisfying M < D < (N - M) */ + if (n == 2 * m + 1) + return -EINVAL; + + req->rate = DIV_ROUND_CLOSEST_ULL((u64)req->best_parent_rate * m, n); + + return 0; +} + +static int gp_mnd_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_gp_mnd *gp = to_clk_gp_mnd(hw); + unsigned long m = 0, n = 0; + unsigned int d_val, n_val; + int ret; + + rational_best_approximation(rate, parent_rate, + (unsigned long)GP_MND_MAX_M, + (unsigned long)GP_MND_MAX_N, + &m, &n); + + if (!m || !n) + return -EINVAL; + + /* + * When N = 2M + 1 the valid D range [M+1, M] is empty; no duty + * cycle can satisfy M < D < (N - M). Reject before touching hw. + */ + if (n == 2 * m + 1) + return -EINVAL; + + ret = clk_prepare_enable(gp->pdm_ahb_clk); + if (ret) + return ret; + + ret = regmap_write(gp->regmap, GP_MND_MDIV_REG, m); + if (ret) + goto err_unprepare; + + /* N divider holds the 1's complement of (N - M), N_WIDTH bits wide */ + n_val = ~(n - m) & GP_MND_MAX_N; + ret = regmap_write(gp->regmap, GP_MND_NDIV_REG, n_val); + if (ret) + goto err_unprepare; + + /* Program the closest-to-50% duty cycle. */ + d_val = n / 2; + ret = regmap_write(gp->regmap, GP_MND_DUTY_REG, d_val); + if (ret) + goto err_unprepare; + + gp->m_val = m; + gp->n_val = n; + +err_unprepare: + clk_disable_unprepare(gp->pdm_ahb_clk); + + return ret; +} + +static unsigned long gp_mnd_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_gp_mnd *gp = to_clk_gp_mnd(hw); + unsigned int m_val, n_val; + int ret; + + ret = clk_prepare_enable(gp->pdm_ahb_clk); + if (ret) + return 0; + + ret = regmap_read(gp->regmap, GP_MND_MDIV_REG, &m_val); + if (ret) + goto out_unprepare; + + m_val &= GP_MND_MAX_M; + + ret = regmap_read(gp->regmap, GP_MND_NDIV_REG, &n_val); + if (ret) + goto out_unprepare; + + /* Reverse the 1's complement encoding: N = ~NDIV_REG + M */ + n_val = (~n_val & GP_MND_MAX_N) + m_val; + +out_unprepare: + clk_disable_unprepare(gp->pdm_ahb_clk); + + if (ret) + return 0; + + if (!n_val) + return 0; + + gp->m_val = m_val; + gp->n_val = n_val; + + return DIV_ROUND_CLOSEST_ULL((u64)parent_rate * m_val, n_val); +} + +static int gp_mnd_clk_get_duty_cycle(struct clk_hw *hw, struct clk_duty *duty) +{ + struct clk_gp_mnd *gp = to_clk_gp_mnd(hw); + unsigned int d_val; + int ret; + + if (!gp->n_val) { + duty->num = 1; + duty->den = 2; + return 0; + } + + ret = clk_prepare_enable(gp->pdm_ahb_clk); + if (ret) + return ret; + + ret = regmap_read(gp->regmap, GP_MND_DUTY_REG, &d_val); + + clk_disable_unprepare(gp->pdm_ahb_clk); + + if (ret) + return ret; + + duty->num = d_val; + duty->den = gp->n_val; + + return 0; +} + +static int gp_mnd_clk_set_duty_cycle(struct clk_hw *hw, struct clk_duty *duty) +{ + struct clk_gp_mnd *gp = to_clk_gp_mnd(hw); + unsigned int d_val; + int ret; + + if (!gp->n_val || !gp->m_val) + return -EINVAL; + + /* D = (1 - duty) * N, giving the low-phase count */ + d_val = DIV_ROUND_UP((u64)(duty->den - duty->num) * gp->n_val, duty->den); + + /* Hardware constraint: M < D < (N - M) */ + if (d_val <= gp->m_val || d_val >= (gp->n_val - gp->m_val)) + return -EINVAL; + + ret = clk_prepare_enable(gp->pdm_ahb_clk); + if (ret) + return ret; + + ret = regmap_write(gp->regmap, GP_MND_DUTY_REG, d_val); + + clk_disable_unprepare(gp->pdm_ahb_clk); + + return ret; +} + +static const struct clk_ops clk_gp_mnd_ops = { + .determine_rate = gp_mnd_clk_determine_rate, + .set_rate = gp_mnd_clk_set_rate, + .recalc_rate = gp_mnd_clk_recalc_rate, + .get_duty_cycle = gp_mnd_clk_get_duty_cycle, + .set_duty_cycle = gp_mnd_clk_set_duty_cycle, +}; + +static const struct regmap_config gp_mnd_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, +}; + +static int clk_gp_mnd_probe(struct platform_device *pdev) +{ + struct clk_parent_data parent_data = { .index = 0 }; + struct clk_init_data init = { + .ops = &clk_gp_mnd_ops, + .parent_data = &parent_data, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE, + }; + struct device *dev = &pdev->dev; + struct clk_gp_mnd *gp; + struct clk *clk; + struct pinctrl *pin; + struct pinctrl_state *pin_default_state; + void __iomem *base; + int ret; + + gp = devm_kzalloc(dev, sizeof(*gp), GFP_KERNEL); + if (!gp) + return -ENOMEM; + + gp->pdm_ahb_clk = devm_clk_get(dev, "ahb_clk"); + if (IS_ERR(gp->pdm_ahb_clk)) + return dev_err_probe(dev, PTR_ERR(gp->pdm_ahb_clk), + "failed to get ahb_clk\n"); + + clk = devm_clk_get(dev, "pdm_clk"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + /* Set default rate if not already configured */ + if (!clk_get_rate(clk)) { + ret = clk_set_rate(clk, 19200000); + if (ret) + dev_warn(dev, "failed to set default pdm_clk rate\n"); + } + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return dev_err_probe(dev, PTR_ERR(base), + "failed to map PDM registers\n"); + + gp->regmap = devm_regmap_init_mmio(dev, base, &gp_mnd_regmap_config); + if (IS_ERR(gp->regmap)) + return dev_err_probe(dev, PTR_ERR(gp->regmap), + "failed to init regmap\n"); + + ret = of_property_read_string_index(dev->of_node, + "clock-output-names", 0, + &init.name); + if (ret) + return dev_err_probe(dev, ret, "missing clock-output-names\n"); + + gp->hw.init = &init; + + pin = devm_pinctrl_get(dev); + if (IS_ERR(pin)) + return dev_err_probe(dev, PTR_ERR(pin), "missing pinctrl device\n"); + + pin_default_state = pinctrl_lookup_state(pin, "active"); + if (IS_ERR(pin_default_state)) + return dev_err_probe(dev, PTR_ERR(pin_default_state), + "missing pinctrl default state\n"); + + ret = pinctrl_select_state(pin, pin_default_state); + if (ret) + return dev_err_probe(dev, ret, + "failed to select pinctrl default state\n"); + + ret = devm_clk_hw_register(dev, &gp->hw); + if (ret) + return dev_err_probe(dev, ret, + "failed to register gp_mnd clock\n"); + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &gp->hw); +} + +static const struct of_device_id clk_gp_mnd_match_table[] = { + { .compatible = "qcom,clk-gp-mnd" }, + { } +}; +MODULE_DEVICE_TABLE(of, clk_gp_mnd_match_table); + +static struct platform_driver clk_gp_mnd_driver = { + .probe = clk_gp_mnd_probe, + .driver = { + .name = "qcom-clk-gp-mnd", + .of_match_table = clk_gp_mnd_match_table, + }, +}; +module_platform_driver(clk_gp_mnd_driver); + +MODULE_DESCRIPTION("Qualcomm PDM GP_MND clock divider driver"); +MODULE_LICENSE("GPL"); From 513bbc86f81060a05b20ee2654b4ec1c19bd87d8 Mon Sep 17 00:00:00 2001 From: Taniya Das Date: Tue, 2 Jun 2026 20:51:51 +0530 Subject: [PATCH 3/5] FROMLIST: pinctrl: qcom: Add gp_mn mux function for QCS8300, SA8775P and SC7280 Add the gp_mn pin mux function to the TLMM pin controllers for the QCS8300, SA8775P and SC7280 SoCs. This function exposes the GP M/N divider clock output on a dedicated GPIO pin, allowing the clock signal to be routed externally. - QCS8300: gpio32 - SA8775P: gpio35 - SC7280: gpio60 Link: https://lore.kernel.org/r/20260602-pdm_clk_gp_mnd_v1-v1-3-1522662b6c53@oss.qualcomm.com Signed-off-by: Taniya Das --- drivers/pinctrl/qcom/pinctrl-qcs8300.c | 9 ++++++++- drivers/pinctrl/qcom/pinctrl-sa8775p.c | 8 +++++++- drivers/pinctrl/qcom/pinctrl-sc7280.c | 8 +++++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-qcs8300.c b/drivers/pinctrl/qcom/pinctrl-qcs8300.c index f1af1a620684c..3cc08afaa0932 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcs8300.c +++ b/drivers/pinctrl/qcom/pinctrl-qcs8300.c @@ -432,6 +432,7 @@ enum qcs8300_functions { msm_mux_gcc_gp3, msm_mux_gcc_gp4, msm_mux_gcc_gp5, + msm_mux_gp_mn, msm_mux_hs0_mi2s, msm_mux_hs1_mi2s, msm_mux_hs2_mi2s, @@ -659,6 +660,10 @@ static const char *const gcc_gp5_groups[] = { "gpio76", "gpio77", }; +static const char *const gp_mn_groups[] = { + "gpio32", +}; + static const char * const hs0_mi2s_groups[] = { "gpio106", "gpio107", "gpio108", "gpio109", }; @@ -963,6 +968,7 @@ static const struct pinfunction qcs8300_functions[] = { MSM_PIN_FUNCTION(gcc_gp3), MSM_PIN_FUNCTION(gcc_gp4), MSM_PIN_FUNCTION(gcc_gp5), + MSM_PIN_FUNCTION(gp_mn), MSM_PIN_FUNCTION(hs0_mi2s), MSM_PIN_FUNCTION(hs1_mi2s), MSM_PIN_FUNCTION(hs2_mi2s), @@ -1070,7 +1076,8 @@ static const struct msm_pingroup qcs8300_groups[] = { [30] = PINGROUP(30, qup0_se4, cci_i2c_scl, cci_async, emac0_ptp_pps, tgu_ch3, _, _, _, _, _, _), [31] = PINGROUP(31, qup0_se4, cci_i2c_sda, cci_async, emac0_ptp_aux, _, _, _, _, _, _, _), - [32] = PINGROUP(32, qup0_se4, cci_i2c_scl, emac0_ptp_aux, mdp_vsync, _, _, _, _, _, _, _), + [32] = PINGROUP(32, qup0_se4, cci_i2c_scl, emac0_ptp_aux, mdp_vsync, gp_mn, _, _, _, _, + _, _), [33] = PINGROUP(33, qup0_se2, qdss_gpio, _, _, _, _, _, _, _, _, _), [34] = PINGROUP(34, qup0_se2, qdss_gpio, _, _, _, _, _, _, _, _, _), [35] = PINGROUP(35, qup0_se2, gcc_gp1, _, _, _, _, _, _, _, _, _), diff --git a/drivers/pinctrl/qcom/pinctrl-sa8775p.c b/drivers/pinctrl/qcom/pinctrl-sa8775p.c index 53f28b9c49ba2..b240ce830b755 100644 --- a/drivers/pinctrl/qcom/pinctrl-sa8775p.c +++ b/drivers/pinctrl/qcom/pinctrl-sa8775p.c @@ -489,6 +489,7 @@ enum sa8775p_functions { msm_mux_gcc_gp3, msm_mux_gcc_gp4, msm_mux_gcc_gp5, + msm_mux_gp_mn, msm_mux_hs0_mi2s, msm_mux_hs1_mi2s, msm_mux_hs2_mi2s, @@ -837,6 +838,10 @@ static const char * const gcc_gp5_groups[] = { "gpio34", "gpio42", }; +static const char * const gp_mn_groups[] = { + "gpio35", +}; + static const char * const hs0_mi2s_groups[] = { "gpio114", "gpio115", "gpio116", "gpio117", }; @@ -1239,6 +1244,7 @@ static const struct pinfunction sa8775p_functions[] = { MSM_PIN_FUNCTION(gcc_gp3), MSM_PIN_FUNCTION(gcc_gp4), MSM_PIN_FUNCTION(gcc_gp5), + MSM_PIN_FUNCTION(gp_mn), MSM_PIN_FUNCTION(hs0_mi2s), MSM_PIN_FUNCTION(hs1_mi2s), MSM_PIN_FUNCTION(hs2_mi2s), @@ -1370,7 +1376,7 @@ static const struct msm_pingroup sa8775p_groups[] = { [32] = PINGROUP(32, qup0_se4, phase_flag, _, _, _, _, _, _, _), [33] = PINGROUP(33, qup0_se4, gcc_gp4, _, ddr_pxi0, _, _, _, _, _), [34] = PINGROUP(34, qup0_se4, gcc_gp5, _, ddr_pxi0, _, _, _, _, _), - [35] = PINGROUP(35, qup0_se4, phase_flag, _, _, _, _, _, _, _), + [35] = PINGROUP(35, qup0_se4, phase_flag, gp_mn, _, _, _, _, _, _), [36] = PINGROUP(36, qup0_se2, qup0_se5, phase_flag, tgu_ch2, _, _, _, _, _), [37] = PINGROUP(37, qup0_se2, qup0_se5, phase_flag, tgu_ch3, _, _, _, _, _), [38] = PINGROUP(38, qup0_se5, qup0_se2, qdss_cti, phase_flag, tgu_ch4, _, _, _, _), diff --git a/drivers/pinctrl/qcom/pinctrl-sc7280.c b/drivers/pinctrl/qcom/pinctrl-sc7280.c index 44e09608aad07..cb24bef1b8796 100644 --- a/drivers/pinctrl/qcom/pinctrl-sc7280.c +++ b/drivers/pinctrl/qcom/pinctrl-sc7280.c @@ -518,6 +518,7 @@ enum sc7280_functions { msm_mux_gcc_gp1, msm_mux_gcc_gp2, msm_mux_gcc_gp3, + msm_mux_gp_mn, msm_mux_gpio, msm_mux_host2wlan_sol, msm_mux_ibi_i3c, @@ -791,6 +792,10 @@ static const char * const gcc_gp2_groups[] = { static const char * const gcc_gp3_groups[] = { "gpio78", "gpio107", }; + +static const char *const gp_mn_groups[] = { + "gpio60", +}; static const char * const host2wlan_sol_groups[] = { "gpio26", }; @@ -1157,6 +1162,7 @@ static const struct pinfunction sc7280_functions[] = { MSM_PIN_FUNCTION(gcc_gp1), MSM_PIN_FUNCTION(gcc_gp2), MSM_PIN_FUNCTION(gcc_gp3), + MSM_PIN_FUNCTION(gp_mn), MSM_GPIO_PIN_FUNCTION(gpio), MSM_PIN_FUNCTION(host2wlan_sol), MSM_PIN_FUNCTION(ibi_i3c), @@ -1328,7 +1334,7 @@ static const struct msm_pingroup sc7280_groups[] = { [57] = PINGROUP(57, qup16, ddr_bist, phase_flag, _, _, _, _, _, _), [58] = PINGROUP(58, qup16, ddr_bist, phase_flag, qdss, _, _, _, _, _), [59] = PINGROUP(59, qup16, ddr_bist, phase_flag, qdss, _, _, _, _, _), - [60] = PINGROUP(60, qup17, edp_hot, _, phase_flag, _, _, _, _, _), + [60] = PINGROUP(60, qup17, edp_hot, gp_mn, phase_flag, _, _, _, _, _), [61] = PINGROUP(61, qup17, sd_write, phase_flag, tsense_pwm1, tsense_pwm2, _, _, _, _), [62] = PINGROUP(62, qup17, qup16, phase_flag, _, _, _, _, _, _), [63] = PINGROUP(63, qup17, qup16, phase_flag, _, _, _, _, _, _), From 019769a5c8bbc74361e85622d92749c13086f2a4 Mon Sep 17 00:00:00 2001 From: Taniya Das Date: Tue, 2 Jun 2026 20:51:52 +0530 Subject: [PATCH 4/5] FROMLIST: arm64: dts: qcom: Add gp_mn pin state for GP M/N clock output Add pinctrl states for the GP M/N divider clock output pin across multiple Qualcomm SoCs: wire it to the GP M/N clock controller node via pinctrl-0. - sc7280 (sc7280): Add gp_mn_active state on gpio35 (gp_mn function). - lemans (sa8775p): Add gp_mn_active state on gpio35 (gp_mn function). - monaco (qcs8300): Add gp_mn_active state on gpio32 (gp_mn function). Link: https://lore.kernel.org/r/20260602-pdm_clk_gp_mnd_v1-v1-4-1522662b6c53@oss.qualcomm.com Signed-off-by: Taniya Das --- arch/arm64/boot/dts/qcom/lemans.dtsi | 7 +++++++ arch/arm64/boot/dts/qcom/monaco.dtsi | 7 +++++++ arch/arm64/boot/dts/qcom/sc7280.dtsi | 7 +++++++ 3 files changed, 21 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/lemans.dtsi b/arch/arm64/boot/dts/qcom/lemans.dtsi index 5d2b5d8e52ab5..e37f82eb40c7f 100644 --- a/arch/arm64/boot/dts/qcom/lemans.dtsi +++ b/arch/arm64/boot/dts/qcom/lemans.dtsi @@ -5646,6 +5646,13 @@ bias-disable; }; + gp_mn_active: gp_mn_active-state { + pins = "gpio35"; + function = "gp_mn"; + drive-strength = <2>; + bias-disable; + }; + hs0_mi2s_active: hs0-mi2s-active-state { pins = "gpio114", "gpio115", "gpio116", "gpio117"; function = "hs0_mi2s"; diff --git a/arch/arm64/boot/dts/qcom/monaco.dtsi b/arch/arm64/boot/dts/qcom/monaco.dtsi index 3a04458122c9a..07ccf67a3fa08 100644 --- a/arch/arm64/boot/dts/qcom/monaco.dtsi +++ b/arch/arm64/boot/dts/qcom/monaco.dtsi @@ -6269,6 +6269,13 @@ bias-disable; }; + gp_mn_active: gp_mn_active-state { + pins = "gpio32"; + function = "gp_mn"; + drive-strength = <2>; + bias-disable; + }; + hs0_mi2s_active: hs0-mi2s-active-state { pins = "gpio106", "gpio107", "gpio108", "gpio109"; function = "hs0_mi2s"; diff --git a/arch/arm64/boot/dts/qcom/sc7280.dtsi b/arch/arm64/boot/dts/qcom/sc7280.dtsi index e23bc90d0eae7..f99fa223f1c95 100644 --- a/arch/arm64/boot/dts/qcom/sc7280.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7280.dtsi @@ -5852,6 +5852,13 @@ function = "edp_hot"; }; + gp_mn_active: gp_mn_active-state { + pins = "gpio35"; + function = "gp_mn"; + drive-strength = <2>; + bias-disable; + }; + mi2s0_data0: mi2s0-data0-state { pins = "gpio98"; function = "mi2s0_data0"; From cd6b879cf133b64db599d8e73a3d6d3f7b4cbd9b Mon Sep 17 00:00:00 2001 From: Taniya Das Date: Fri, 5 Jun 2026 00:10:48 +0530 Subject: [PATCH 5/5] FROMLIST: arm64: dts: qcom: Add GP M/N clock controller node for SA8775P and QCS8300 Add the GP M/N divider clock controller node at 0x088d3000 to the SA8775P (sc7280, lemans) and QCS8300 (monaco) SoC device trees. The node uses the qcom,clk-gp-mnd compatible, is clocked by the PDM XO4 and AHB clocks from GCC, and exposes a single clock output (gp_mn_clk) on the dedicated gp_mn pin mux function. The XO4 clock is pre-assigned to 4.8 MHz (XO/4). Link: https://lore.kernel.org/r/20260602-pdm_clk_gp_mnd_v1-v1-5-1522662b6c53@oss.qualcomm.com Signed-off-by: Taniya Das --- arch/arm64/boot/dts/qcom/lemans.dtsi | 14 ++++++++++++++ arch/arm64/boot/dts/qcom/monaco.dtsi | 14 ++++++++++++++ arch/arm64/boot/dts/qcom/sc7280.dtsi | 14 ++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/lemans.dtsi b/arch/arm64/boot/dts/qcom/lemans.dtsi index e37f82eb40c7f..5cff6bb90f407 100644 --- a/arch/arm64/boot/dts/qcom/lemans.dtsi +++ b/arch/arm64/boot/dts/qcom/lemans.dtsi @@ -4045,6 +4045,20 @@ }; }; + gp_mn: clock-controller@88d3000 { + compatible = "qcom,clk-gp-mnd"; + reg = <0x0 0x088d3000 0x0 0xc>; + clocks = <&gcc GCC_PDM_XO4_CLK>, + <&gcc GCC_PDM_AHB_CLK>; + clock-names = "pdm_clk", "ahb_clk"; + clock-output-names = "gp_mn_clk"; + #clock-cells = <0>; + pinctrl-names = "active"; + pinctrl-0 = <&gp_mn_active>; + assigned-clocks = <&gcc GCC_PDM_XO4_CLK>; + assigned-clock-rates = <4800000>; + }; + usb_0_hsphy: phy@88e4000 { compatible = "qcom,sa8775p-usb-hs-phy", "qcom,usb-snps-hs-5nm-phy"; diff --git a/arch/arm64/boot/dts/qcom/monaco.dtsi b/arch/arm64/boot/dts/qcom/monaco.dtsi index 07ccf67a3fa08..87b83c3fe2d35 100644 --- a/arch/arm64/boot/dts/qcom/monaco.dtsi +++ b/arch/arm64/boot/dts/qcom/monaco.dtsi @@ -4803,6 +4803,20 @@ }; }; + gp_mn: clock-controller@88d3000 { + compatible = "qcom,clk-gp-mnd"; + reg = <0x0 0x088d3000 0x0 0xc>; + clocks = <&gcc GCC_PDM_XO4_CLK>, + <&gcc GCC_PDM_AHB_CLK>; + clock-names = "pdm_clk", "ahb_clk"; + clock-output-names = "gp_mn_clk"; + #clock-cells = <0>; + pinctrl-names = "active"; + pinctrl-0 = <&gp_mn_active>; + assigned-clocks = <&gcc GCC_PDM_XO4_CLK>; + assigned-clock-rates = <4800000>; + }; + usb_1_hsphy: phy@8904000 { compatible = "qcom,qcs8300-usb-hs-phy", "qcom,usb-snps-hs-7nm-phy"; diff --git a/arch/arm64/boot/dts/qcom/sc7280.dtsi b/arch/arm64/boot/dts/qcom/sc7280.dtsi index f99fa223f1c95..51c87ec26b379 100644 --- a/arch/arm64/boot/dts/qcom/sc7280.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7280.dtsi @@ -4347,6 +4347,20 @@ usb-role-switch; }; + gp_mn: clock-controller@88d3000 { + compatible = "qcom,clk-gp-mnd"; + reg = <0x0 0x088d3000 0x0 0xc>; + clocks = <&gcc GCC_PDM_XO4_CLK>, + <&gcc GCC_PDM_AHB_CLK>; + clock-names = "pdm_clk", "ahb_clk"; + clock-output-names = "gp_mn_clk"; + #clock-cells = <0>; + pinctrl-names = "active"; + pinctrl-0 = <&gp_mn_active>; + assigned-clocks = <&gcc GCC_PDM_XO4_CLK>; + assigned-clock-rates = <4800000>; + }; + qspi: spi@88dc000 { compatible = "qcom,sc7280-qspi", "qcom,qspi-v1"; reg = <0 0x088dc000 0 0x1000>;