// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) STMicroelectronics 2025 - All Rights Reserved * Author: Clément Le Goffic for STMicroelectronics. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "../core.h" #define DRIVER_NAME "stm32_hdp" #define HDP_CTRL_ENABLE 1 #define HDP_CTRL_DISABLE 0 #define HDP_CTRL 0x000 #define HDP_MUX 0x004 #define HDP_VAL 0x010 #define HDP_GPOSET 0x014 #define HDP_GPOCLR 0x018 #define HDP_GPOVAL 0x01c #define HDP_VERR 0x3f4 #define HDP_IPIDR 0x3f8 #define HDP_SIDR 0x3fc #define HDP_MUX_SHIFT(n) ((n) * 4) #define HDP_MUX_MASK(n) (GENMASK(3, 0) << HDP_MUX_SHIFT(n)) #define HDP_MUX_GPOVAL(n) (0xf << HDP_MUX_SHIFT(n)) #define HDP_PIN 8 #define HDP_FUNC 16 #define HDP_FUNC_TOTAL (HDP_PIN * HDP_FUNC) struct stm32_hdp { struct device *dev; void __iomem *base; struct clk *clk; struct pinctrl_dev *pctl_dev; struct gpio_chip gpio_chip; u32 mux_conf; u32 gposet_conf; const char * const *func_name; }; static const struct pinctrl_pin_desc stm32_hdp_pins[] = { PINCTRL_PIN(0, "HDP0"), PINCTRL_PIN(1, "HDP1"), PINCTRL_PIN(2, "HDP2"), PINCTRL_PIN(3, "HDP3"), PINCTRL_PIN(4, "HDP4"), PINCTRL_PIN(5, "HDP5"), PINCTRL_PIN(6, "HDP6"), PINCTRL_PIN(7, "HDP7"), }; static const char * const func_name_mp13[] = { //HDP0 functions: "pwr_pwrwake_sys", "pwr_stop_forbidden", "pwr_stdby_wakeup", "pwr_encomp_vddcore", "bsec_out_sec_niden", "aiec_sys_wakeup", "none", "none", "ddrctrl_lp_req", "pwr_ddr_ret_enable_n", "dts_clk_ptat", "none", "sram3ctrl_tamp_erase_act", "none", "none", "gpoval0", //HDP1 functions: "pwr_sel_vth_vddcpu", "pwr_mpu_ram_lowspeed", "ca7_naxierrirq", "pwr_okin_mr", "bsec_out_sec_dbgen", "aiec_c1_wakeup", "rcc_pwrds_mpu", "none", "ddrctrl_dfi_ctrlupd_req", "ddrctrl_cactive_ddrc_asr", "none", "none", "sram3ctrl_hw_erase_act", "nic400_s0_bready", "none", "gpoval1", //HDP2 functions: "pwr_pwrwake_mpu", "pwr_mpu_clock_disable_ack", "ca7_ndbgreset_i", "none", "bsec_in_rstcore_n", "bsec_out_sec_bsc_dis", "none", "none", "ddrctrl_dfi_init_complete", "ddrctrl_perf_op_is_refresh", "ddrctrl_gskp_dfi_lp_req", "none", "sram3ctrl_sw_erase_act", "nic400_s0_bvalid", "none", "gpoval2", //HDP3 functions: "pwr_sel_vth_vddcore", "pwr_mpu_clock_disable_req", "ca7_npmuirq0", "ca7_nfiqout0", "bsec_out_sec_dftlock", "bsec_out_sec_jtag_dis", "rcc_pwrds_sys", "sram3ctrl_tamp_erase_req", "ddrctrl_stat_ddrc_reg_selfref_type0", "none", "dts_valobus1_0", "dts_valobus2_0", "tamp_potential_tamp_erfcfg", "nic400_s0_wready", "nic400_s0_rready", "gpoval3", //HDP4 functions: "none", "pwr_stop2_active", "ca7_nl2reset_i", "ca7_npreset_varm_i", "bsec_out_sec_dften", "bsec_out_sec_dbgswenable", "eth1_out_pmt_intr_o", "eth2_out_pmt_intr_o", "ddrctrl_stat_ddrc_reg_selfref_type1", "ddrctrl_cactive_0", "dts_valobus1_1", "dts_valobus2_1", "tamp_nreset_sram_ercfg", "nic400_s0_wlast", "nic400_s0_rlast", "gpoval4", //HDP5 functions: "ca7_standbywfil2", "pwr_vth_vddcore_ack", "ca7_ncorereset_i", "ca7_nirqout0", "bsec_in_pwrok", "bsec_out_sec_deviceen", "eth1_out_lpi_intr_o", "eth2_out_lpi_intr_o", "ddrctrl_cactive_ddrc", "ddrctrl_wr_credit_cnt", "dts_valobus1_2", "dts_valobus2_2", "pka_pka_itamp_out", "nic400_s0_wvalid", "nic400_s0_rvalid", "gpoval5", //HDP6 functions: "ca7_standbywfe0", "pwr_vth_vddcpu_ack", "ca7_evento", "none", "bsec_in_tamper_det", "bsec_out_sec_spniden", "eth1_out_mac_speed_o1", "eth2_out_mac_speed_o1", "ddrctrl_csysack_ddrc", "ddrctrl_lpr_credit_cnt", "dts_valobus1_3", "dts_valobus2_3", "saes_tamper_out", "nic400_s0_awready", "nic400_s0_arready", "gpoval6", //HDP7 functions: "ca7_standbywfi0", "pwr_rcc_vcpu_rdy", "ca7_eventi", "ca7_dbgack0", "bsec_out_fuse_ok", "bsec_out_sec_spiden", "eth1_out_mac_speed_o0", "eth2_out_mac_speed_o0", "ddrctrl_csysreq_ddrc", "ddrctrl_hpr_credit_cnt", "dts_valobus1_4", "dts_valobus2_4", "rng_tamper_out", "nic400_s0_awavalid", "nic400_s0_aravalid", "gpoval7", }; static const char * const func_name_mp15[] = { //HDP0 functions: "pwr_pwrwake_sys", "cm4_sleepdeep", "pwr_stdby_wkup", "pwr_encomp_vddcore", "bsec_out_sec_niden", "none", "rcc_cm4_sleepdeep", "gpu_dbg7", "ddrctrl_lp_req", "pwr_ddr_ret_enable_n", "dts_clk_ptat", "none", "none", "none", "none", "gpoval0", //HDP1 functions: "pwr_pwrwake_mcu", "cm4_halted", "ca7_naxierrirq", "pwr_okin_mr", "bsec_out_sec_dbgen", "exti_sys_wakeup", "rcc_pwrds_mpu", "gpu_dbg6", "ddrctrl_dfi_ctrlupd_req", "ddrctrl_cactive_ddrc_asr", "none", "none", "none", "none", "none", "gpoval1", //HDP2 functions: "pwr_pwrwake_mpu", "cm4_rxev", "ca7_npmuirq1", "ca7_nfiqout1", "bsec_in_rstcore_n", "exti_c2_wakeup", "rcc_pwrds_mcu", "gpu_dbg5", "ddrctrl_dfi_init_complete", "ddrctrl_perf_op_is_refresh", "ddrctrl_gskp_dfi_lp_req", "none", "none", "none", "none", "gpoval2", //HDP3 functions: "pwr_sel_vth_vddcore", "cm4_txev", "ca7_npmuirq0", "ca7_nfiqout0", "bsec_out_sec_dftlock", "exti_c1_wakeup", "rcc_pwrds_sys", "gpu_dbg4", "ddrctrl_stat_ddrc_reg_selfref_type0", "ddrctrl_cactive_1", "dts_valobus1_0", "dts_valobus2_0", "none", "none", "none", "gpoval3", //HDP4 functions: "pwr_mpu_pdds_not_cstbydis", "cm4_sleeping", "ca7_nreset1", "ca7_nirqout1", "bsec_out_sec_dften", "bsec_out_sec_dbgswenable", "eth_out_pmt_intr_o", "gpu_dbg3", "ddrctrl_stat_ddrc_reg_selfref_type1", "ddrctrl_cactive_0", "dts_valobus1_1", "dts_valobus2_1", "none", "none", "none", "gpoval4", //HDP5 functions: "ca7_standbywfil2", "pwr_vth_vddcore_ack", "ca7_nreset0", "ca7_nirqout0", "bsec_in_pwrok", "bsec_out_sec_deviceen", "eth_out_lpi_intr_o", "gpu_dbg2", "ddrctrl_cactive_ddrc", "ddrctrl_wr_credit_cnt", "dts_valobus1_2", "dts_valobus2_2", "none", "none", "none", "gpoval5", //HDP6 functions: "ca7_standbywfi1", "ca7_standbywfe1", "ca7_evento", "ca7_dbgack1", "none", "bsec_out_sec_spniden", "eth_out_mac_speed_o1", "gpu_dbg1", "ddrctrl_csysack_ddrc", "ddrctrl_lpr_credit_cnt", "dts_valobus1_3", "dts_valobus2_3", "none", "none", "none", "gpoval6", //HDP7 functions: "ca7_standbywfi0", "ca7_standbywfe0", "none", "ca7_dbgack0", "bsec_out_fuse_ok", "bsec_out_sec_spiden", "eth_out_mac_speed_o0", "gpu_dbg0", "ddrctrl_csysreq_ddrc", "ddrctrl_hpr_credit_cnt", "dts_valobus1_4", "dts_valobus2_4", "none", "none", "none", "gpoval7" }; static const char * const func_name_mp25[] = { //HDP0 functions: "pwr_pwrwake_sys", "cpu2_sleep_deep", "bsec_out_tst_sdr_unlock_or_disable_scan", "bsec_out_nidenm", "bsec_out_nidena", "cpu2_state_0", "rcc_pwrds_sys", "gpu_dbg7", "ddrss_csysreq_ddrc", "ddrss_dfi_phyupd_req", "cpu3_sleep_deep", "d2_gbl_per_clk_bus_req", "pcie_usb_cxpl_debug_info_ei_0", "pcie_usb_cxpl_debug_info_ei_8", "d3_state_0", "gpoval0", //HDP1 functions: "pwr_pwrwake_cpu2", "cpu2_halted", "cpu2_state_1", "bsec_out_dbgenm", "bsec_out_dbgena", "exti1_sys_wakeup", "rcc_pwrds_cpu2", "gpu_dbg6", "ddrss_csysack_ddrc", "ddrss_dfi_phymstr_req", "cpu3_halted", "d2_gbl_per_dma_req", "pcie_usb_cxpl_debug_info_ei_1", "pcie_usb_cxpl_debug_info_ei_9", "d3_state_1", "gpoval1", //HDP2 functions: "pwr_pwrwake_cpu1", "cpu2_rxev", "cpu1_npumirq1", "cpu1_nfiqout1", "bsec_out_shdbgen", "exti1_cpu2_wakeup", "rcc_pwrds_cpu1", "gpu_dbg5", "ddrss_cactive_ddrc", "ddrss_dfi_lp_req", "cpu3_rxev", "hpdma1_clk_bus_req", "pcie_usb_cxpl_debug_info_ei_2", "pcie_usb_cxpl_debug_info_ei_10", "d3_state_2", "gpoval2", //HDP3 functions: "pwr_sel_vth_vddcpu", "cpu2_txev", "cpu1_npumirq0", "cpu1_nfiqout0", "bsec_out_ddbgen", "exti1_cpu1_wakeup", "cpu3_state_0", "gpu_dbg4", "ddrss_mcdcg_en", "ddrss_dfi_freq_0", "cpu3_txev", "hpdma2_clk_bus_req", "pcie_usb_cxpl_debug_info_ei_3", "pcie_usb_cxpl_debug_info_ei_11", "d1_state_0", "gpoval3", //HDP4 functions: "pwr_sel_vth_vddcore", "cpu2_sleeping", "cpu1_evento", "cpu1_nirqout1", "bsec_out_spnidena", "exti2_d3_wakeup", "eth1_out_pmt_intr_o", "gpu_dbg3", "ddrss_dphycg_en", "ddrss_obsp0", "cpu3_sleeping", "hpdma3_clk_bus_req", "pcie_usb_cxpl_debug_info_ei_4", "pcie_usb_cxpl_debug_info_ei_12", "d1_state_1", "gpoval4", //HDP5 functions: "cpu1_standby_wfil2", "none", "none", "cpu1_nirqout0", "bsec_out_spidena", "exti2_cpu3_wakeup", "eth1_out_lpi_intr_o", "gpu_dbg2", "ddrctrl_dfi_init_start", "ddrss_obsp1", "cpu3_state_1", "d3_gbl_per_clk_bus_req", "pcie_usb_cxpl_debug_info_ei_5", "pcie_usb_cxpl_debug_info_ei_13", "d1_state_2", "gpoval5", //HDP6 functions: "cpu1_standby_wfi1", "cpu1_standby_wfe1", "cpu1_halted1", "cpu1_naxierrirq", "bsec_out_spnidenm", "exti2_cpu2_wakeup", "eth2_out_pmt_intr_o", "gpu_dbg1", "ddrss_dfi_init_complete", "ddrss_obsp2", "d2_state_0", "d3_gbl_per_dma_req", "pcie_usb_cxpl_debug_info_ei_6", "pcie_usb_cxpl_debug_info_ei_14", "cpu1_state_0", "gpoval6", //HDP7 functions: "cpu1_standby_wfi0", "cpu1_standby_wfe0", "cpu1_halted0", "none", "bsec_out_spidenm", "exti2_cpu1__wakeup", "eth2_out_lpi_intr_o", "gpu_dbg0", "ddrss_dfi_ctrlupd_req", "ddrss_obsp3", "d2_state_1", "lpdma1_clk_bus_req", "pcie_usb_cxpl_debug_info_ei_7", "pcie_usb_cxpl_debug_info_ei_15", "cpu1_state_1", "gpoval7", }; static const char * const stm32_hdp_pins_group[] = { "HDP0", "HDP1", "HDP2", "HDP3", "HDP4", "HDP5", "HDP6", "HDP7" }; static int stm32_hdp_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) { return GPIO_LINE_DIRECTION_OUT; } static int stm32_hdp_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) { return ARRAY_SIZE(stm32_hdp_pins); } static const char *stm32_hdp_pinctrl_get_group_name(struct pinctrl_dev *pctldev, unsigned int selector) { return stm32_hdp_pins[selector].name; } static int stm32_hdp_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, unsigned int selector, const unsigned int **pins, unsigned int *num_pins) { *pins = &stm32_hdp_pins[selector].number; *num_pins = 1; return 0; } static const struct pinctrl_ops stm32_hdp_pinctrl_ops = { .get_groups_count = stm32_hdp_pinctrl_get_groups_count, .get_group_name = stm32_hdp_pinctrl_get_group_name, .get_group_pins = stm32_hdp_pinctrl_get_group_pins, .dt_node_to_map = pinconf_generic_dt_node_to_map_all, .dt_free_map = pinconf_generic_dt_free_map, }; static int stm32_hdp_pinmux_get_functions_count(struct pinctrl_dev *pctldev) { return HDP_FUNC_TOTAL; } static const char *stm32_hdp_pinmux_get_function_name(struct pinctrl_dev *pctldev, unsigned int selector) { struct stm32_hdp *hdp = pinctrl_dev_get_drvdata(pctldev); return hdp->func_name[selector]; } static int stm32_hdp_pinmux_get_function_groups(struct pinctrl_dev *pctldev, unsigned int selector, const char *const **groups, unsigned int *num_groups) { u32 index = selector / HDP_FUNC; *groups = &stm32_hdp_pins[index].name; *num_groups = 1; return 0; } static int stm32_hdp_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned int func_selector, unsigned int group_selector) { struct stm32_hdp *hdp = pinctrl_dev_get_drvdata(pctldev); unsigned int pin = stm32_hdp_pins[group_selector].number; u32 mux; func_selector %= HDP_FUNC; mux = readl_relaxed(hdp->base + HDP_MUX); mux &= ~HDP_MUX_MASK(pin); mux |= func_selector << HDP_MUX_SHIFT(pin); writel_relaxed(mux, hdp->base + HDP_MUX); hdp->mux_conf = mux; return 0; } static const struct pinmux_ops stm32_hdp_pinmux_ops = { .get_functions_count = stm32_hdp_pinmux_get_functions_count, .get_function_name = stm32_hdp_pinmux_get_function_name, .get_function_groups = stm32_hdp_pinmux_get_function_groups, .set_mux = stm32_hdp_pinmux_set_mux, .gpio_set_direction = NULL, }; static struct pinctrl_desc stm32_hdp_pdesc = { .name = DRIVER_NAME, .pins = stm32_hdp_pins, .npins = ARRAY_SIZE(stm32_hdp_pins), .pctlops = &stm32_hdp_pinctrl_ops, .pmxops = &stm32_hdp_pinmux_ops, .owner = THIS_MODULE, }; static const struct of_device_id stm32_hdp_of_match[] = { { .compatible = "st,stm32mp131-hdp", .data = &func_name_mp13, }, { .compatible = "st,stm32mp151-hdp", .data = &func_name_mp15, }, { .compatible = "st,stm32mp251-hdp", .data = &func_name_mp25, }, {} }; MODULE_DEVICE_TABLE(of, stm32_hdp_of_match); static int stm32_hdp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct stm32_hdp *hdp; u8 version; int err; hdp = devm_kzalloc(dev, sizeof(*hdp), GFP_KERNEL); if (!hdp) return -ENOMEM; hdp->dev = dev; platform_set_drvdata(pdev, hdp); hdp->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(hdp->base)) return PTR_ERR(hdp->base); hdp->func_name = of_device_get_match_data(dev); if (!hdp->func_name) return dev_err_probe(dev, -ENODEV, "No function name provided\n"); hdp->clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(hdp->clk)) return dev_err_probe(dev, PTR_ERR(hdp->clk), "No HDP clock provided\n"); err = devm_pinctrl_register_and_init(dev, &stm32_hdp_pdesc, hdp, &hdp->pctl_dev); if (err) return dev_err_probe(dev, err, "Failed to register pinctrl\n"); err = pinctrl_enable(hdp->pctl_dev); if (err) return dev_err_probe(dev, err, "Failed to enable pinctrl\n"); hdp->gpio_chip.get_direction = stm32_hdp_gpio_get_direction; hdp->gpio_chip.ngpio = ARRAY_SIZE(stm32_hdp_pins); hdp->gpio_chip.can_sleep = true; hdp->gpio_chip.names = stm32_hdp_pins_group; err = bgpio_init(&hdp->gpio_chip, dev, 4, hdp->base + HDP_GPOVAL, hdp->base + HDP_GPOSET, hdp->base + HDP_GPOCLR, NULL, NULL, BGPIOF_NO_INPUT); if (err) return dev_err_probe(dev, err, "Failed to init bgpio\n"); err = devm_gpiochip_add_data(dev, &hdp->gpio_chip, hdp); if (err) return dev_err_probe(dev, err, "Failed to add gpiochip\n"); writel_relaxed(HDP_CTRL_ENABLE, hdp->base + HDP_CTRL); version = readl_relaxed(hdp->base + HDP_VERR); dev_dbg(dev, "STM32 HDP version %u.%u initialized\n", version >> 4, version & 0x0f); return 0; } static void stm32_hdp_remove(struct platform_device *pdev) { struct stm32_hdp *hdp = platform_get_drvdata(pdev); writel_relaxed(HDP_CTRL_DISABLE, hdp->base + HDP_CTRL); } static int stm32_hdp_suspend(struct device *dev) { struct stm32_hdp *hdp = dev_get_drvdata(dev); hdp->gposet_conf = readl_relaxed(hdp->base + HDP_GPOSET); pinctrl_pm_select_sleep_state(dev); clk_disable_unprepare(hdp->clk); return 0; } static int stm32_hdp_resume(struct device *dev) { struct stm32_hdp *hdp = dev_get_drvdata(dev); int err; err = clk_prepare_enable(hdp->clk); if (err) { dev_err(dev, "Failed to prepare_enable clk (%d)\n", err); return err; } writel_relaxed(HDP_CTRL_ENABLE, hdp->base + HDP_CTRL); writel_relaxed(hdp->gposet_conf, hdp->base + HDP_GPOSET); writel_relaxed(hdp->mux_conf, hdp->base + HDP_MUX); pinctrl_pm_select_default_state(dev); return 0; } static DEFINE_SIMPLE_DEV_PM_OPS(stm32_hdp_pm_ops, stm32_hdp_suspend, stm32_hdp_resume); static struct platform_driver stm32_hdp_driver = { .probe = stm32_hdp_probe, .remove = stm32_hdp_remove, .driver = { .name = DRIVER_NAME, .pm = pm_sleep_ptr(&stm32_hdp_pm_ops), .of_match_table = stm32_hdp_of_match, } }; module_platform_driver(stm32_hdp_driver); MODULE_AUTHOR("Clément Le Goffic"); MODULE_DESCRIPTION("STMicroelectronics STM32 Hardware Debug Port driver"); MODULE_LICENSE("GPL");