diff options
Diffstat (limited to 'drivers/soc')
38 files changed, 1491 insertions, 416 deletions
diff --git a/drivers/soc/amlogic/meson-clk-measure.c b/drivers/soc/amlogic/meson-clk-measure.c index 3f3039600357..a6453ffeb753 100644 --- a/drivers/soc/amlogic/meson-clk-measure.c +++ b/drivers/soc/amlogic/meson-clk-measure.c @@ -688,4 +688,5 @@ static struct platform_driver meson_msr_driver = { }, }; module_platform_driver(meson_msr_driver); +MODULE_DESCRIPTION("Amlogic Meson SoC Clock Measure driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/amlogic/meson-gx-socinfo.c b/drivers/soc/amlogic/meson-gx-socinfo.c index 6abb730344ab..8809a948201a 100644 --- a/drivers/soc/amlogic/meson-gx-socinfo.c +++ b/drivers/soc/amlogic/meson-gx-socinfo.c @@ -63,7 +63,9 @@ static const struct meson_gx_package_id { { "962X", 0x24, 0x10, 0xf0 }, { "962E", 0x24, 0x20, 0xf0 }, { "A113X", 0x25, 0x37, 0xff }, + { "A113X", 0x25, 0x43, 0xff }, { "A113D", 0x25, 0x22, 0xff }, + { "S905L", 0x26, 0, 0x0 }, { "S905D2", 0x28, 0x10, 0xf0 }, { "S905Y2", 0x28, 0x30, 0xf0 }, { "S905X2", 0x28, 0x40, 0xf0 }, diff --git a/drivers/soc/imx/soc-imx8m.c b/drivers/soc/imx/soc-imx8m.c index ec87d9d878f3..fe111bae38c8 100644 --- a/drivers/soc/imx/soc-imx8m.c +++ b/drivers/soc/imx/soc-imx8m.c @@ -252,4 +252,5 @@ free_soc: return ret; } device_initcall(imx8_soc_init); +MODULE_DESCRIPTION("NXP i.MX8M SoC driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/soc/ixp4xx/ixp4xx-npe.c b/drivers/soc/ixp4xx/ixp4xx-npe.c index 35825ee95dff..34a6f187c220 100644 --- a/drivers/soc/ixp4xx/ixp4xx-npe.c +++ b/drivers/soc/ixp4xx/ixp4xx-npe.c @@ -764,6 +764,7 @@ static struct platform_driver ixp4xx_npe_driver = { module_platform_driver(ixp4xx_npe_driver); MODULE_AUTHOR("Krzysztof Halasa"); +MODULE_DESCRIPTION("Intel IXP4xx Network Processor Engine driver"); MODULE_LICENSE("GPL v2"); MODULE_FIRMWARE(NPE_A_FIRMWARE); MODULE_FIRMWARE(NPE_B_FIRMWARE); diff --git a/drivers/soc/ixp4xx/ixp4xx-qmgr.c b/drivers/soc/ixp4xx/ixp4xx-qmgr.c index 244ad8d7e80b..cb112f3643e9 100644 --- a/drivers/soc/ixp4xx/ixp4xx-qmgr.c +++ b/drivers/soc/ixp4xx/ixp4xx-qmgr.c @@ -465,6 +465,7 @@ static struct platform_driver ixp4xx_qmgr_driver = { }; module_platform_driver(ixp4xx_qmgr_driver); +MODULE_DESCRIPTION("Intel IXP4xx Queue Manager driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Krzysztof Halasa"); diff --git a/drivers/soc/mediatek/mtk-cmdq-helper.c b/drivers/soc/mediatek/mtk-cmdq-helper.c index 046522664dc1..a8fccedba83f 100644 --- a/drivers/soc/mediatek/mtk-cmdq-helper.c +++ b/drivers/soc/mediatek/mtk-cmdq-helper.c @@ -15,6 +15,7 @@ /* dedicate the last GPR_R15 to assign the register address to be poll */ #define CMDQ_POLL_ADDR_GPR (15) #define CMDQ_EOC_IRQ_EN BIT(0) +#define CMDQ_IMMEDIATE_VALUE 0 #define CMDQ_REG_TYPE 1 #define CMDQ_JUMP_RELATIVE 0 #define CMDQ_JUMP_ABSOLUTE 1 @@ -45,6 +46,16 @@ struct cmdq_instruction { u8 op; }; +static inline u8 cmdq_operand_get_type(struct cmdq_operand *op) +{ + return op->reg ? CMDQ_REG_TYPE : CMDQ_IMMEDIATE_VALUE; +} + +static inline u16 cmdq_operand_get_idx_value(struct cmdq_operand *op) +{ + return op->reg ? op->idx : op->value; +} + int cmdq_dev_get_client_reg(struct device *dev, struct cmdq_client_reg *client_reg, int idx) { @@ -461,6 +472,29 @@ int cmdq_pkt_poll_addr(struct cmdq_pkt *pkt, dma_addr_t addr, u32 value, u32 mas } EXPORT_SYMBOL(cmdq_pkt_poll_addr); +int cmdq_pkt_logic_command(struct cmdq_pkt *pkt, u16 result_reg_idx, + struct cmdq_operand *left_operand, + enum cmdq_logic_op s_op, + struct cmdq_operand *right_operand) +{ + struct cmdq_instruction inst = { {0} }; + + if (!left_operand || !right_operand || s_op >= CMDQ_LOGIC_MAX) + return -EINVAL; + + inst.op = CMDQ_CODE_LOGIC; + inst.dst_t = CMDQ_REG_TYPE; + inst.src_t = cmdq_operand_get_type(left_operand); + inst.arg_c_t = cmdq_operand_get_type(right_operand); + inst.sop = s_op; + inst.reg_dst = result_reg_idx; + inst.src_reg = cmdq_operand_get_idx_value(left_operand); + inst.arg_c = cmdq_operand_get_idx_value(right_operand); + + return cmdq_pkt_append_command(pkt, inst); +} +EXPORT_SYMBOL(cmdq_pkt_logic_command); + int cmdq_pkt_assign(struct cmdq_pkt *pkt, u16 reg_idx, u32 value) { struct cmdq_instruction inst = {}; @@ -526,4 +560,5 @@ int cmdq_pkt_finalize(struct cmdq_pkt *pkt) } EXPORT_SYMBOL(cmdq_pkt_finalize); +MODULE_DESCRIPTION("MediaTek Command Queue (CMDQ) driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/mediatek/mtk-mmsys.c b/drivers/soc/mediatek/mtk-mmsys.c index f370f4ec4b88..938240714e54 100644 --- a/drivers/soc/mediatek/mtk-mmsys.c +++ b/drivers/soc/mediatek/mtk-mmsys.c @@ -236,6 +236,7 @@ void mtk_mmsys_mixer_in_config(struct device *dev, int idx, bool alpha_sel, u16 mtk_mmsys_update_bits(mmsys, MT8195_VDO1_MIXER_IN1_ALPHA + (idx - 1) * 4, ~0, alpha << 16 | alpha, cmdq_pkt); + mtk_mmsys_update_bits(mmsys, MT8195_VDO1_HDR_TOP_CFG, BIT(15 + idx), 0, cmdq_pkt); mtk_mmsys_update_bits(mmsys, MT8195_VDO1_HDR_TOP_CFG, BIT(19 + idx), alpha_sel << (19 + idx), cmdq_pkt); mtk_mmsys_update_bits(mmsys, MT8195_VDO1_MIXER_IN1_PAD + (idx - 1) * 4, diff --git a/drivers/soc/mediatek/mtk-mutex.c b/drivers/soc/mediatek/mtk-mutex.c index b5af1fb5847e..01b129caf1eb 100644 --- a/drivers/soc/mediatek/mtk-mutex.c +++ b/drivers/soc/mediatek/mtk-mutex.c @@ -524,6 +524,7 @@ static const unsigned int mt8188_mdp_mutex_table_mod[MUTEX_MOD_IDX_MAX] = { [MUTEX_MOD_IDX_MDP_PAD0] = MT8195_MUTEX_MOD_MDP_PAD0, [MUTEX_MOD_IDX_MDP_PAD2] = MT8195_MUTEX_MOD_MDP_PAD2, [MUTEX_MOD_IDX_MDP_PAD3] = MT8195_MUTEX_MOD_MDP_PAD3, + [MUTEX_MOD_IDX_MDP_TCC0] = MT8195_MUTEX_MOD_MDP_TCC0, [MUTEX_MOD_IDX_MDP_WROT0] = MT8195_MUTEX_MOD_MDP_WROT0, [MUTEX_MOD_IDX_MDP_WROT2] = MT8195_MUTEX_MOD_MDP_WROT2, [MUTEX_MOD_IDX_MDP_WROT3] = MT8195_MUTEX_MOD_MDP_WROT3, diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 5af33b0e3470..7f02f0525933 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -72,11 +72,28 @@ config QCOM_OCMEM requirements. This is typically used by the GPU, camera/video, and audio components on some Snapdragon SoCs. +config QCOM_PD_MAPPER + tristate "Qualcomm Protection Domain Mapper" + select QCOM_QMI_HELPERS + select QCOM_PDR_MSG + select AUXILIARY_BUS + depends on NET && QRTR + default QCOM_RPROC_COMMON + help + The Protection Domain Mapper maps registered services to the domains + and instances handled by the remote DSPs. This is a kernel-space + implementation of the service. It is a simpler alternative to the + userspace daemon. + config QCOM_PDR_HELPERS tristate select QCOM_QMI_HELPERS + select QCOM_PDR_MSG depends on NET +config QCOM_PDR_MSG + tristate + config QCOM_PMIC_PDCHARGER_ULOG tristate "Qualcomm PMIC PDCharger ULOG driver" depends on RPMSG @@ -194,6 +211,7 @@ config QCOM_SMP2P config QCOM_SMSM tristate "Qualcomm Shared Memory State Machine" + depends on MAILBOX depends on QCOM_SMEM select QCOM_SMEM_STATE select IRQ_DOMAIN diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index ca0bece0dfff..d3560f861085 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -7,7 +7,9 @@ obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o obj-$(CONFIG_QCOM_OCMEM) += ocmem.o +obj-$(CONFIG_QCOM_PD_MAPPER) += qcom_pd_mapper.o obj-$(CONFIG_QCOM_PDR_HELPERS) += pdr_interface.o +obj-$(CONFIG_QCOM_PDR_MSG) += qcom_pdr_msg.o obj-$(CONFIG_QCOM_PMIC_GLINK) += pmic_glink.o obj-$(CONFIG_QCOM_PMIC_GLINK) += pmic_glink_altmode.o obj-$(CONFIG_QCOM_PMIC_PDCHARGER_ULOG) += pmic_pdcharger_ulog.o diff --git a/drivers/soc/qcom/icc-bwmon.c b/drivers/soc/qcom/icc-bwmon.c index fb323b3364db..e7851974084b 100644 --- a/drivers/soc/qcom/icc-bwmon.c +++ b/drivers/soc/qcom/icc-bwmon.c @@ -565,7 +565,7 @@ static void bwmon_start(struct icc_bwmon *bwmon) int window; /* No need to check for errors, as this must have succeeded before. */ - dev_pm_opp_find_bw_ceil(bwmon->dev, &bw_low, 0); + dev_pm_opp_put(dev_pm_opp_find_bw_ceil(bwmon->dev, &bw_low, 0)); bwmon_clear_counters(bwmon, true); @@ -772,18 +772,25 @@ static int bwmon_probe(struct platform_device *pdev) opp = dev_pm_opp_find_bw_floor(dev, &bwmon->max_bw_kbps, 0); if (IS_ERR(opp)) return dev_err_probe(dev, PTR_ERR(opp), "failed to find max peak bandwidth\n"); + dev_pm_opp_put(opp); bwmon->min_bw_kbps = 0; opp = dev_pm_opp_find_bw_ceil(dev, &bwmon->min_bw_kbps, 0); if (IS_ERR(opp)) return dev_err_probe(dev, PTR_ERR(opp), "failed to find min peak bandwidth\n"); + dev_pm_opp_put(opp); bwmon->dev = dev; bwmon_disable(bwmon); - ret = devm_request_threaded_irq(dev, bwmon->irq, bwmon_intr, - bwmon_intr_thread, - IRQF_ONESHOT, dev_name(dev), bwmon); + + /* + * SoCs with multiple cpu-bwmon instances can end up using a shared interrupt + * line. Using the devm_ variant might result in the IRQ handler being executed + * after bwmon_disable in bwmon_remove() + */ + ret = request_threaded_irq(bwmon->irq, bwmon_intr, bwmon_intr_thread, + IRQF_ONESHOT | IRQF_SHARED, dev_name(dev), bwmon); if (ret) return dev_err_probe(dev, ret, "failed to request IRQ\n"); @@ -798,6 +805,7 @@ static void bwmon_remove(struct platform_device *pdev) struct icc_bwmon *bwmon = platform_get_drvdata(pdev); bwmon_disable(bwmon); + free_irq(bwmon->irq, bwmon); } static const struct icc_bwmon_data msm8998_bwmon_data = { diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index cbef0dea1d5d..37e11e501728 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -7,6 +7,7 @@ #include <linux/bitfield.h> #include <linux/bitmap.h> #include <linux/bitops.h> +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/io.h> #include <linux/kernel.h> @@ -150,6 +151,25 @@ enum llcc_reg_offset { LLCC_COMMON_STATUS0, }; +static const struct llcc_slice_config sa8775p_data[] = { + {LLCC_CPUSS, 1, 2048, 1, 0, 0x00FF, 0x0, 0, 0, 0, 1, 1, 0, 0}, + {LLCC_VIDSC0, 2, 512, 3, 1, 0x00FF, 0x0, 0, 0, 0, 1, 0, 0, 0}, + {LLCC_CPUSS1, 3, 1024, 1, 1, 0x00FF, 0x0, 0, 0, 0, 1, 0, 0, 0}, + {LLCC_CPUHWT, 5, 512, 1, 1, 0x00FF, 0x0, 0, 0, 0, 1, 0, 0, 0}, + {LLCC_AUDIO, 6, 1024, 1, 1, 0x00FF, 0x0, 0, 0, 0, 0, 0, 0, 0}, + {LLCC_CMPT, 10, 4096, 1, 1, 0x00FF, 0x0, 0, 0, 0, 1, 0, 0, 0}, + {LLCC_GPUHTW, 11, 1024, 1, 1, 0x00FF, 0x0, 0, 0, 0, 1, 0, 0, 0}, + {LLCC_GPU, 12, 1024, 1, 1, 0x00FF, 0x0, 0, 0, 0, 1, 0, 1, 0}, + {LLCC_MMUHWT, 13, 1024, 1, 1, 0x00FF, 0x0, 0, 0, 0, 0, 1, 0, 0}, + {LLCC_CMPTDMA, 15, 1024, 1, 1, 0x00FF, 0x0, 0, 0, 0, 1, 0, 0, 0}, + {LLCC_DISP, 16, 4096, 2, 1, 0x00FF, 0x0, 0, 0, 0, 1, 0, 0, 0}, + {LLCC_VIDFW, 17, 3072, 1, 0, 0x00FF, 0x0, 0, 0, 0, 1, 0, 0, 0}, + {LLCC_AUDHW, 22, 1024, 1, 1, 0x00FF, 0x0, 0, 0, 0, 0, 0, 0, 0}, + {LLCC_CVP, 28, 256, 3, 1, 0x00FF, 0x0, 0, 0, 0, 1, 0, 0, 0}, + {LLCC_APTCM, 30, 1024, 3, 1, 0x0, 0xF0, 1, 0, 0, 1, 0, 0, 0}, + {LLCC_WRCACHE, 31, 512, 1, 1, 0x00FF, 0x0, 0, 0, 0, 0, 1, 0, 0}, +}; + static const struct llcc_slice_config sc7180_data[] = { { LLCC_CPUSS, 1, 256, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 1 }, { LLCC_MDM, 8, 128, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 0 }, @@ -552,6 +572,16 @@ static const struct qcom_llcc_config qdu1000_cfg[] = { }, }; +static const struct qcom_llcc_config sa8775p_cfg[] = { + { + .sct_data = sa8775p_data, + .size = ARRAY_SIZE(sa8775p_data), + .need_llcc_cfg = true, + .reg_offset = llcc_v2_1_reg_offset, + .edac_reg_offset = &llcc_v2_1_edac_reg_offset, + }, +}; + static const struct qcom_llcc_config sc7180_cfg[] = { { .sct_data = sc7180_data, @@ -698,6 +728,11 @@ static const struct qcom_sct_config qdu1000_cfgs = { .num_config = ARRAY_SIZE(qdu1000_cfg), }; +static const struct qcom_sct_config sa8775p_cfgs = { + .llcc_config = sa8775p_cfg, + .num_config = ARRAY_SIZE(sa8775p_cfg), +}; + static const struct qcom_sct_config sc7180_cfgs = { .llcc_config = sc7180_cfg, .num_config = ARRAY_SIZE(sc7180_cfg), @@ -821,6 +856,7 @@ EXPORT_SYMBOL_GPL(llcc_slice_putd); static int llcc_update_act_ctrl(u32 sid, u32 act_ctrl_reg_val, u32 status) { + struct regmap *regmap; u32 act_ctrl_reg; u32 act_clear_reg; u32 status_reg; @@ -849,7 +885,8 @@ static int llcc_update_act_ctrl(u32 sid, return ret; if (drv_data->version >= LLCC_VERSION_4_1_0_0) { - ret = regmap_read_poll_timeout(drv_data->bcast_regmap, status_reg, + regmap = drv_data->bcast_and_regmap ?: drv_data->bcast_regmap; + ret = regmap_read_poll_timeout(regmap, status_reg, slice_status, (slice_status & ACT_COMPLETE), 0, LLCC_STATUS_READ_DELAY); if (ret) @@ -1258,16 +1295,13 @@ static int qcom_llcc_probe(struct platform_device *pdev) /* Initialize rest of LLCC bank regmaps */ for (i = 1; i < num_banks; i++) { - char *base = kasprintf(GFP_KERNEL, "llcc%d_base", i); + char *base __free(kfree) = kasprintf(GFP_KERNEL, "llcc%d_base", i); drv_data->regmaps[i] = qcom_llcc_init_mmio(pdev, i, base); if (IS_ERR(drv_data->regmaps[i])) { ret = PTR_ERR(drv_data->regmaps[i]); - kfree(base); goto err; } - - kfree(base); } drv_data->bcast_regmap = qcom_llcc_init_mmio(pdev, i, "llcc_broadcast_base"); @@ -1284,6 +1318,18 @@ static int qcom_llcc_probe(struct platform_device *pdev) drv_data->version = version; + /* Applicable only when drv_data->version >= 4.1 */ + if (drv_data->version >= LLCC_VERSION_4_1_0_0) { + drv_data->bcast_and_regmap = qcom_llcc_init_mmio(pdev, i + 1, "llcc_broadcast_and_base"); + if (IS_ERR(drv_data->bcast_and_regmap)) { + ret = PTR_ERR(drv_data->bcast_and_regmap); + if (ret == -EINVAL) + drv_data->bcast_and_regmap = NULL; + else + goto err; + } + } + llcc_cfg = cfg->sct_data; sz = cfg->size; @@ -1332,6 +1378,7 @@ err: static const struct of_device_id qcom_llcc_of_match[] = { { .compatible = "qcom,qdu1000-llcc", .data = &qdu1000_cfgs}, + { .compatible = "qcom,sa8775p-llcc", .data = &sa8775p_cfgs }, { .compatible = "qcom,sc7180-llcc", .data = &sc7180_cfgs }, { .compatible = "qcom,sc7280-llcc", .data = &sc7280_cfgs }, { .compatible = "qcom,sc8180x-llcc", .data = &sc8180x_cfgs }, diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c index 6f177e46fa0f..b2c0fb55d4ae 100644 --- a/drivers/soc/qcom/mdt_loader.c +++ b/drivers/soc/qcom/mdt_loader.c @@ -7,6 +7,7 @@ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. */ +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/elf.h> #include <linux/firmware.h> @@ -37,13 +38,12 @@ static ssize_t mdt_load_split_segment(void *ptr, const struct elf32_phdr *phdrs, { const struct elf32_phdr *phdr = &phdrs[segment]; const struct firmware *seg_fw; - char *seg_name; ssize_t ret; if (strlen(fw_name) < 4) return -EINVAL; - seg_name = kstrdup(fw_name, GFP_KERNEL); + char *seg_name __free(kfree) = kstrdup(fw_name, GFP_KERNEL); if (!seg_name) return -ENOMEM; @@ -52,7 +52,6 @@ static ssize_t mdt_load_split_segment(void *ptr, const struct elf32_phdr *phdrs, ptr, phdr->p_filesz); if (ret) { dev_err(dev, "error %zd loading %s\n", ret, seg_name); - kfree(seg_name); return ret; } @@ -64,7 +63,6 @@ static ssize_t mdt_load_split_segment(void *ptr, const struct elf32_phdr *phdrs, } release_firmware(seg_fw); - kfree(seg_name); return ret; } diff --git a/drivers/soc/qcom/ocmem.c b/drivers/soc/qcom/ocmem.c index e8841d247953..6b6dd80cbc0f 100644 --- a/drivers/soc/qcom/ocmem.c +++ b/drivers/soc/qcom/ocmem.c @@ -10,6 +10,7 @@ */ #include <linux/bitfield.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/io.h> #include <linux/kernel.h> @@ -216,7 +217,6 @@ EXPORT_SYMBOL_GPL(of_get_ocmem); struct ocmem_buf *ocmem_allocate(struct ocmem *ocmem, enum ocmem_client client, unsigned long size) { - struct ocmem_buf *buf; int ret; /* TODO: add support for other clients... */ @@ -229,7 +229,7 @@ struct ocmem_buf *ocmem_allocate(struct ocmem *ocmem, enum ocmem_client client, if (test_and_set_bit_lock(BIT(client), &ocmem->active_allocations)) return ERR_PTR(-EBUSY); - buf = kzalloc(sizeof(*buf), GFP_KERNEL); + struct ocmem_buf *buf __free(kfree) = kzalloc(sizeof(*buf), GFP_KERNEL); if (!buf) { ret = -ENOMEM; goto err_unlock; @@ -247,7 +247,7 @@ struct ocmem_buf *ocmem_allocate(struct ocmem *ocmem, enum ocmem_client client, if (ret) { dev_err(ocmem->dev, "could not lock: %d\n", ret); ret = -EINVAL; - goto err_kfree; + goto err_unlock; } } else { ocmem_write(ocmem, OCMEM_REG_GFX_MPU_START, buf->offset); @@ -258,10 +258,8 @@ struct ocmem_buf *ocmem_allocate(struct ocmem *ocmem, enum ocmem_client client, dev_dbg(ocmem->dev, "using %ldK of OCMEM at 0x%08lx for client %d\n", size / 1024, buf->addr, client); - return buf; + return_ptr(buf); -err_kfree: - kfree(buf); err_unlock: clear_bit_unlock(BIT(client), &ocmem->active_allocations); diff --git a/drivers/soc/qcom/pdr_interface.c b/drivers/soc/qcom/pdr_interface.c index a1b6a4081dea..328b6153b2be 100644 --- a/drivers/soc/qcom/pdr_interface.c +++ b/drivers/soc/qcom/pdr_interface.c @@ -3,6 +3,7 @@ * Copyright (C) 2020 The Linux Foundation. All rights reserved. */ +#include <linux/cleanup.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> @@ -76,12 +77,12 @@ static int pdr_locator_new_server(struct qmi_handle *qmi, locator_hdl); struct pdr_service *pds; + mutex_lock(&pdr->lock); /* Create a local client port for QMI communication */ pdr->locator_addr.sq_family = AF_QIPCRTR; pdr->locator_addr.sq_node = svc->node; pdr->locator_addr.sq_port = svc->port; - mutex_lock(&pdr->lock); pdr->locator_init_complete = true; mutex_unlock(&pdr->lock); @@ -104,10 +105,10 @@ static void pdr_locator_del_server(struct qmi_handle *qmi, mutex_lock(&pdr->lock); pdr->locator_init_complete = false; - mutex_unlock(&pdr->lock); pdr->locator_addr.sq_node = 0; pdr->locator_addr.sq_port = 0; + mutex_unlock(&pdr->lock); } static const struct qmi_ops pdr_locator_ops = { @@ -365,12 +366,14 @@ static int pdr_get_domain_list(struct servreg_get_domain_list_req *req, if (ret < 0) return ret; + mutex_lock(&pdr->lock); ret = qmi_send_request(&pdr->locator_hdl, &pdr->locator_addr, &txn, SERVREG_GET_DOMAIN_LIST_REQ, SERVREG_GET_DOMAIN_LIST_REQ_MAX_LEN, servreg_get_domain_list_req_ei, req); + mutex_unlock(&pdr->lock); if (ret < 0) { qmi_txn_cancel(&txn); return ret; @@ -394,13 +397,13 @@ static int pdr_get_domain_list(struct servreg_get_domain_list_req *req, static int pdr_locate_service(struct pdr_handle *pdr, struct pdr_service *pds) { - struct servreg_get_domain_list_resp *resp; struct servreg_get_domain_list_req req; struct servreg_location_entry *entry; int domains_read = 0; int ret, i; - resp = kzalloc(sizeof(*resp), GFP_KERNEL); + struct servreg_get_domain_list_resp *resp __free(kfree) = kzalloc(sizeof(*resp), + GFP_KERNEL); if (!resp) return -ENOMEM; @@ -413,9 +416,9 @@ static int pdr_locate_service(struct pdr_handle *pdr, struct pdr_service *pds) req.domain_offset = domains_read; ret = pdr_get_domain_list(&req, resp, pdr); if (ret < 0) - goto out; + return ret; - for (i = domains_read; i < resp->domain_list_len; i++) { + for (i = 0; i < resp->domain_list_len; i++) { entry = &resp->domain_list[i]; if (strnlen(entry->name, sizeof(entry->name)) == sizeof(entry->name)) @@ -425,7 +428,7 @@ static int pdr_locate_service(struct pdr_handle *pdr, struct pdr_service *pds) pds->service_data_valid = entry->service_data_valid; pds->service_data = entry->service_data; pds->instance = entry->instance; - goto out; + return 0; } } @@ -438,8 +441,7 @@ static int pdr_locate_service(struct pdr_handle *pdr, struct pdr_service *pds) domains_read += resp->domain_list_len; } while (domains_read < resp->total_domains); -out: - kfree(resp); + return ret; } @@ -515,8 +517,7 @@ struct pdr_service *pdr_add_lookup(struct pdr_handle *pdr, const char *service_name, const char *service_path) { - struct pdr_service *pds, *tmp; - int ret; + struct pdr_service *tmp; if (IS_ERR_OR_NULL(pdr)) return ERR_PTR(-EINVAL); @@ -525,7 +526,7 @@ struct pdr_service *pdr_add_lookup(struct pdr_handle *pdr, !service_path || strlen(service_path) > SERVREG_NAME_LENGTH) return ERR_PTR(-EINVAL); - pds = kzalloc(sizeof(*pds), GFP_KERNEL); + struct pdr_service *pds __free(kfree) = kzalloc(sizeof(*pds), GFP_KERNEL); if (!pds) return ERR_PTR(-ENOMEM); @@ -540,8 +541,7 @@ struct pdr_service *pdr_add_lookup(struct pdr_handle *pdr, continue; mutex_unlock(&pdr->list_lock); - ret = -EALREADY; - goto err; + return ERR_PTR(-EALREADY); } list_add(&pds->node, &pdr->lookups); @@ -549,10 +549,7 @@ struct pdr_service *pdr_add_lookup(struct pdr_handle *pdr, schedule_work(&pdr->locator_work); - return pds; -err: - kfree(pds); - return ERR_PTR(ret); + return_ptr(pds); } EXPORT_SYMBOL_GPL(pdr_add_lookup); @@ -649,13 +646,12 @@ struct pdr_handle *pdr_handle_alloc(void (*status)(int state, char *service_path, void *priv), void *priv) { - struct pdr_handle *pdr; int ret; if (!status) return ERR_PTR(-EINVAL); - pdr = kzalloc(sizeof(*pdr), GFP_KERNEL); + struct pdr_handle *pdr __free(kfree) = kzalloc(sizeof(*pdr), GFP_KERNEL); if (!pdr) return ERR_PTR(-ENOMEM); @@ -674,10 +670,8 @@ struct pdr_handle *pdr_handle_alloc(void (*status)(int state, INIT_WORK(&pdr->indack_work, pdr_indack_work); pdr->notifier_wq = create_singlethread_workqueue("pdr_notifier_wq"); - if (!pdr->notifier_wq) { - ret = -ENOMEM; - goto free_pdr_handle; - } + if (!pdr->notifier_wq) + return ERR_PTR(-ENOMEM); pdr->indack_wq = alloc_ordered_workqueue("pdr_indack_wq", WQ_HIGHPRI); if (!pdr->indack_wq) { @@ -702,7 +696,7 @@ struct pdr_handle *pdr_handle_alloc(void (*status)(int state, if (ret < 0) goto release_qmi_handle; - return pdr; + return_ptr(pdr); release_qmi_handle: qmi_handle_release(&pdr->locator_hdl); @@ -710,8 +704,6 @@ destroy_indack: destroy_workqueue(pdr->indack_wq); destroy_notifier: destroy_workqueue(pdr->notifier_wq); -free_pdr_handle: - kfree(pdr); return ERR_PTR(ret); } diff --git a/drivers/soc/qcom/pdr_internal.h b/drivers/soc/qcom/pdr_internal.h index 03c282b7f17e..8d17f7fb79e7 100644 --- a/drivers/soc/qcom/pdr_internal.h +++ b/drivers/soc/qcom/pdr_internal.h @@ -13,6 +13,8 @@ #define SERVREG_SET_ACK_REQ 0x23 #define SERVREG_RESTART_PD_REQ 0x24 +#define SERVREG_LOC_PFR_REQ 0x24 + #define SERVREG_DOMAIN_LIST_LENGTH 32 #define SERVREG_RESTART_PD_REQ_MAX_LEN 67 #define SERVREG_REGISTER_LISTENER_REQ_LEN 71 @@ -20,6 +22,7 @@ #define SERVREG_GET_DOMAIN_LIST_REQ_MAX_LEN 74 #define SERVREG_STATE_UPDATED_IND_MAX_LEN 79 #define SERVREG_GET_DOMAIN_LIST_RESP_MAX_LEN 2389 +#define SERVREG_LOC_PFR_RESP_MAX_LEN 10 struct servreg_location_entry { char name[SERVREG_NAME_LENGTH + 1]; @@ -28,83 +31,12 @@ struct servreg_location_entry { u32 instance; }; -static const struct qmi_elem_info servreg_location_entry_ei[] = { - { - .data_type = QMI_STRING, - .elem_len = SERVREG_NAME_LENGTH + 1, - .elem_size = sizeof(char), - .array_type = NO_ARRAY, - .tlv_type = 0, - .offset = offsetof(struct servreg_location_entry, - name), - }, - { - .data_type = QMI_UNSIGNED_4_BYTE, - .elem_len = 1, - .elem_size = sizeof(u32), - .array_type = NO_ARRAY, - .tlv_type = 0, - .offset = offsetof(struct servreg_location_entry, - instance), - }, - { - .data_type = QMI_UNSIGNED_1_BYTE, - .elem_len = 1, - .elem_size = sizeof(u8), - .array_type = NO_ARRAY, - .tlv_type = 0, - .offset = offsetof(struct servreg_location_entry, - service_data_valid), - }, - { - .data_type = QMI_UNSIGNED_4_BYTE, - .elem_len = 1, - .elem_size = sizeof(u32), - .array_type = NO_ARRAY, - .tlv_type = 0, - .offset = offsetof(struct servreg_location_entry, - service_data), - }, - {} -}; - struct servreg_get_domain_list_req { char service_name[SERVREG_NAME_LENGTH + 1]; u8 domain_offset_valid; u32 domain_offset; }; -static const struct qmi_elem_info servreg_get_domain_list_req_ei[] = { - { - .data_type = QMI_STRING, - .elem_len = SERVREG_NAME_LENGTH + 1, - .elem_size = sizeof(char), - .array_type = NO_ARRAY, - .tlv_type = 0x01, - .offset = offsetof(struct servreg_get_domain_list_req, - service_name), - }, - { - .data_type = QMI_OPT_FLAG, - .elem_len = 1, - .elem_size = sizeof(u8), - .array_type = NO_ARRAY, - .tlv_type = 0x10, - .offset = offsetof(struct servreg_get_domain_list_req, - domain_offset_valid), - }, - { - .data_type = QMI_UNSIGNED_4_BYTE, - .elem_len = 1, - .elem_size = sizeof(u32), - .array_type = NO_ARRAY, - .tlv_type = 0x10, - .offset = offsetof(struct servreg_get_domain_list_req, - domain_offset), - }, - {} -}; - struct servreg_get_domain_list_resp { struct qmi_response_type_v01 resp; u8 total_domains_valid; @@ -116,264 +48,60 @@ struct servreg_get_domain_list_resp { struct servreg_location_entry domain_list[SERVREG_DOMAIN_LIST_LENGTH]; }; -static const struct qmi_elem_info servreg_get_domain_list_resp_ei[] = { - { - .data_type = QMI_STRUCT, - .elem_len = 1, - .elem_size = sizeof(struct qmi_response_type_v01), - .array_type = NO_ARRAY, - .tlv_type = 0x02, - .offset = offsetof(struct servreg_get_domain_list_resp, - resp), - .ei_array = qmi_response_type_v01_ei, - }, - { - .data_type = QMI_OPT_FLAG, - .elem_len = 1, - .elem_size = sizeof(u8), - .array_type = NO_ARRAY, - .tlv_type = 0x10, - .offset = offsetof(struct servreg_get_domain_list_resp, - total_domains_valid), - }, - { - .data_type = QMI_UNSIGNED_2_BYTE, - .elem_len = 1, - .elem_size = sizeof(u16), - .array_type = NO_ARRAY, - .tlv_type = 0x10, - .offset = offsetof(struct servreg_get_domain_list_resp, - total_domains), - }, - { - .data_type = QMI_OPT_FLAG, - .elem_len = 1, - .elem_size = sizeof(u8), - .array_type = NO_ARRAY, - .tlv_type = 0x11, - .offset = offsetof(struct servreg_get_domain_list_resp, - db_rev_count_valid), - }, - { - .data_type = QMI_UNSIGNED_2_BYTE, - .elem_len = 1, - .elem_size = sizeof(u16), - .array_type = NO_ARRAY, - .tlv_type = 0x11, - .offset = offsetof(struct servreg_get_domain_list_resp, - db_rev_count), - }, - { - .data_type = QMI_OPT_FLAG, - .elem_len = 1, - .elem_size = sizeof(u8), - .array_type = NO_ARRAY, - .tlv_type = 0x12, - .offset = offsetof(struct servreg_get_domain_list_resp, - domain_list_valid), - }, - { - .data_type = QMI_DATA_LEN, - .elem_len = 1, - .elem_size = sizeof(u8), - .array_type = NO_ARRAY, - .tlv_type = 0x12, - .offset = offsetof(struct servreg_get_domain_list_resp, - domain_list_len), - }, - { - .data_type = QMI_STRUCT, - .elem_len = SERVREG_DOMAIN_LIST_LENGTH, - .elem_size = sizeof(struct servreg_location_entry), - .array_type = VAR_LEN_ARRAY, - .tlv_type = 0x12, - .offset = offsetof(struct servreg_get_domain_list_resp, - domain_list), - .ei_array = servreg_location_entry_ei, - }, - {} -}; - struct servreg_register_listener_req { u8 enable; char service_path[SERVREG_NAME_LENGTH + 1]; }; -static const struct qmi_elem_info servreg_register_listener_req_ei[] = { - { - .data_type = QMI_UNSIGNED_1_BYTE, - .elem_len = 1, - .elem_size = sizeof(u8), - .array_type = NO_ARRAY, - .tlv_type = 0x01, - .offset = offsetof(struct servreg_register_listener_req, - enable), - }, - { - .data_type = QMI_STRING, - .elem_len = SERVREG_NAME_LENGTH + 1, - .elem_size = sizeof(char), - .array_type = NO_ARRAY, - .tlv_type = 0x02, - .offset = offsetof(struct servreg_register_listener_req, - service_path), - }, - {} -}; - struct servreg_register_listener_resp { struct qmi_response_type_v01 resp; u8 curr_state_valid; enum servreg_service_state curr_state; }; -static const struct qmi_elem_info servreg_register_listener_resp_ei[] = { - { - .data_type = QMI_STRUCT, - .elem_len = 1, - .elem_size = sizeof(struct qmi_response_type_v01), - .array_type = NO_ARRAY, - .tlv_type = 0x02, - .offset = offsetof(struct servreg_register_listener_resp, - resp), - .ei_array = qmi_response_type_v01_ei, - }, - { - .data_type = QMI_OPT_FLAG, - .elem_len = 1, - .elem_size = sizeof(u8), - .array_type = NO_ARRAY, - .tlv_type = 0x10, - .offset = offsetof(struct servreg_register_listener_resp, - curr_state_valid), - }, - { - .data_type = QMI_SIGNED_4_BYTE_ENUM, - .elem_len = 1, - .elem_size = sizeof(enum servreg_service_state), - .array_type = NO_ARRAY, - .tlv_type = 0x10, - .offset = offsetof(struct servreg_register_listener_resp, - curr_state), - }, - {} -}; - struct servreg_restart_pd_req { char service_path[SERVREG_NAME_LENGTH + 1]; }; -static const struct qmi_elem_info servreg_restart_pd_req_ei[] = { - { - .data_type = QMI_STRING, - .elem_len = SERVREG_NAME_LENGTH + 1, - .elem_size = sizeof(char), - .array_type = NO_ARRAY, - .tlv_type = 0x01, - .offset = offsetof(struct servreg_restart_pd_req, - service_path), - }, - {} -}; - struct servreg_restart_pd_resp { struct qmi_response_type_v01 resp; }; -static const struct qmi_elem_info servreg_restart_pd_resp_ei[] = { - { - .data_type = QMI_STRUCT, - .elem_len = 1, - .elem_size = sizeof(struct qmi_response_type_v01), - .array_type = NO_ARRAY, - .tlv_type = 0x02, - .offset = offsetof(struct servreg_restart_pd_resp, - resp), - .ei_array = qmi_response_type_v01_ei, - }, - {} -}; - struct servreg_state_updated_ind { enum servreg_service_state curr_state; char service_path[SERVREG_NAME_LENGTH + 1]; u16 transaction_id; }; -static const struct qmi_elem_info servreg_state_updated_ind_ei[] = { - { - .data_type = QMI_SIGNED_4_BYTE_ENUM, - .elem_len = 1, - .elem_size = sizeof(u32), - .array_type = NO_ARRAY, - .tlv_type = 0x01, - .offset = offsetof(struct servreg_state_updated_ind, - curr_state), - }, - { - .data_type = QMI_STRING, - .elem_len = SERVREG_NAME_LENGTH + 1, - .elem_size = sizeof(char), - .array_type = NO_ARRAY, - .tlv_type = 0x02, - .offset = offsetof(struct servreg_state_updated_ind, - service_path), - }, - { - .data_type = QMI_UNSIGNED_2_BYTE, - .elem_len = 1, - .elem_size = sizeof(u16), - .array_type = NO_ARRAY, - .tlv_type = 0x03, - .offset = offsetof(struct servreg_state_updated_ind, - transaction_id), - }, - {} -}; - struct servreg_set_ack_req { char service_path[SERVREG_NAME_LENGTH + 1]; u16 transaction_id; }; -static const struct qmi_elem_info servreg_set_ack_req_ei[] = { - { - .data_type = QMI_STRING, - .elem_len = SERVREG_NAME_LENGTH + 1, - .elem_size = sizeof(char), - .array_type = NO_ARRAY, - .tlv_type = 0x01, - .offset = offsetof(struct servreg_set_ack_req, - service_path), - }, - { - .data_type = QMI_UNSIGNED_2_BYTE, - .elem_len = 1, - .elem_size = sizeof(u16), - .array_type = NO_ARRAY, - .tlv_type = 0x02, - .offset = offsetof(struct servreg_set_ack_req, - transaction_id), - }, - {} -}; - struct servreg_set_ack_resp { struct qmi_response_type_v01 resp; }; -static const struct qmi_elem_info servreg_set_ack_resp_ei[] = { - { - .data_type = QMI_STRUCT, - .elem_len = 1, - .elem_size = sizeof(struct qmi_response_type_v01), - .array_type = NO_ARRAY, - .tlv_type = 0x02, - .offset = offsetof(struct servreg_set_ack_resp, - resp), - .ei_array = qmi_response_type_v01_ei, - }, - {} +struct servreg_loc_pfr_req { + char service[SERVREG_NAME_LENGTH + 1]; + char reason[257]; }; +struct servreg_loc_pfr_resp { + struct qmi_response_type_v01 rsp; +}; + +extern const struct qmi_elem_info servreg_location_entry_ei[]; +extern const struct qmi_elem_info servreg_get_domain_list_req_ei[]; +extern const struct qmi_elem_info servreg_get_domain_list_resp_ei[]; +extern const struct qmi_elem_info servreg_register_listener_req_ei[]; +extern const struct qmi_elem_info servreg_register_listener_resp_ei[]; +extern const struct qmi_elem_info servreg_restart_pd_req_ei[]; +extern const struct qmi_elem_info servreg_restart_pd_resp_ei[]; +extern const struct qmi_elem_info servreg_state_updated_ind_ei[]; +extern const struct qmi_elem_info servreg_set_ack_req_ei[]; +extern const struct qmi_elem_info servreg_set_ack_resp_ei[]; +extern const struct qmi_elem_info servreg_loc_pfr_req_ei[]; +extern const struct qmi_elem_info servreg_loc_pfr_resp_ei[]; + #endif diff --git a/drivers/soc/qcom/pmic_glink.c b/drivers/soc/qcom/pmic_glink.c index 65279243072c..9ebc0ba35947 100644 --- a/drivers/soc/qcom/pmic_glink.c +++ b/drivers/soc/qcom/pmic_glink.c @@ -373,8 +373,17 @@ static struct platform_driver pmic_glink_driver = { static int pmic_glink_init(void) { - platform_driver_register(&pmic_glink_driver); - register_rpmsg_driver(&pmic_glink_rpmsg_driver); + int ret; + + ret = platform_driver_register(&pmic_glink_driver); + if (ret < 0) + return ret; + + ret = register_rpmsg_driver(&pmic_glink_rpmsg_driver); + if (ret < 0) { + platform_driver_unregister(&pmic_glink_driver); + return ret; + } return 0; } diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c index b3808fc24c69..1e0808b3cb93 100644 --- a/drivers/soc/qcom/pmic_glink_altmode.c +++ b/drivers/soc/qcom/pmic_glink_altmode.c @@ -20,7 +20,7 @@ #include <linux/soc/qcom/pmic_glink.h> -#define PMIC_GLINK_MAX_PORTS 2 +#define PMIC_GLINK_MAX_PORTS 3 #define USBC_SC8180X_NOTIFY_IND 0x13 #define USBC_CMD_WRITE_REQ 0x15 diff --git a/drivers/soc/qcom/qcom_pd_mapper.c b/drivers/soc/qcom/qcom_pd_mapper.c new file mode 100644 index 000000000000..a4c007080665 --- /dev/null +++ b/drivers/soc/qcom/qcom_pd_mapper.c @@ -0,0 +1,677 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Qualcomm Protection Domain mapper + * + * Copyright (c) 2023 Linaro Ltd. + */ + +#include <linux/auxiliary_bus.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/refcount.h> +#include <linux/slab.h> +#include <linux/soc/qcom/qmi.h> + +#include "pdr_internal.h" + +#define SERVREG_QMI_VERSION 0x101 +#define SERVREG_QMI_INSTANCE 0 + +#define TMS_SERVREG_SERVICE "tms/servreg" + +struct qcom_pdm_domain_data { + const char *domain; + u32 instance_id; + /* NULL-terminated array */ + const char * services[]; +}; + +struct qcom_pdm_domain { + struct list_head list; + const char *name; + u32 instance_id; +}; + +struct qcom_pdm_service { + struct list_head list; + struct list_head domains; + const char *name; +}; + +struct qcom_pdm_data { + refcount_t refcnt; + struct qmi_handle handle; + struct list_head services; +}; + +static DEFINE_MUTEX(qcom_pdm_mutex); /* protects __qcom_pdm_data */ +static struct qcom_pdm_data *__qcom_pdm_data; + +static struct qcom_pdm_service *qcom_pdm_find(struct qcom_pdm_data *data, + const char *name) +{ + struct qcom_pdm_service *service; + + list_for_each_entry(service, &data->services, list) { + if (!strcmp(service->name, name)) + return service; + } + + return NULL; +} + +static int qcom_pdm_add_service_domain(struct qcom_pdm_data *data, + const char *service_name, + const char *domain_name, + u32 instance_id) +{ + struct qcom_pdm_service *service; + struct qcom_pdm_domain *domain; + + service = qcom_pdm_find(data, service_name); + if (service) { + list_for_each_entry(domain, &service->domains, list) { + if (!strcmp(domain->name, domain_name)) + return -EBUSY; + } + } else { + service = kzalloc(sizeof(*service), GFP_KERNEL); + if (!service) + return -ENOMEM; + + INIT_LIST_HEAD(&service->domains); + service->name = service_name; + + list_add_tail(&service->list, &data->services); + } + + domain = kzalloc(sizeof(*domain), GFP_KERNEL); + if (!domain) { + if (list_empty(&service->domains)) { + list_del(&service->list); + kfree(service); + } + + return -ENOMEM; + } + + domain->name = domain_name; + domain->instance_id = instance_id; + list_add_tail(&domain->list, &service->domains); + + return 0; +} + +static int qcom_pdm_add_domain(struct qcom_pdm_data *data, + const struct qcom_pdm_domain_data *domain) +{ + int ret; + int i; + + ret = qcom_pdm_add_service_domain(data, + TMS_SERVREG_SERVICE, + domain->domain, + domain->instance_id); + if (ret) + return ret; + + for (i = 0; domain->services[i]; i++) { + ret = qcom_pdm_add_service_domain(data, + domain->services[i], + domain->domain, + domain->instance_id); + if (ret) + return ret; + } + + return 0; + +} + +static void qcom_pdm_free_domains(struct qcom_pdm_data *data) +{ + struct qcom_pdm_service *service, *tservice; + struct qcom_pdm_domain *domain, *tdomain; + + list_for_each_entry_safe(service, tservice, &data->services, list) { + list_for_each_entry_safe(domain, tdomain, &service->domains, list) { + list_del(&domain->list); + kfree(domain); + } + + list_del(&service->list); + kfree(service); + } +} + +static void qcom_pdm_get_domain_list(struct qmi_handle *qmi, + struct sockaddr_qrtr *sq, + struct qmi_txn *txn, + const void *decoded) +{ + struct qcom_pdm_data *data = container_of(qmi, struct qcom_pdm_data, handle); + const struct servreg_get_domain_list_req *req = decoded; + struct servreg_get_domain_list_resp *rsp; + struct qcom_pdm_service *service; + u32 offset; + int ret; + + rsp = kzalloc(sizeof(*rsp), GFP_KERNEL); + if (!rsp) + return; + + offset = req->domain_offset_valid ? req->domain_offset : 0; + + rsp->resp.result = QMI_RESULT_SUCCESS_V01; + rsp->resp.error = QMI_ERR_NONE_V01; + + rsp->db_rev_count_valid = true; + rsp->db_rev_count = 1; + + rsp->total_domains_valid = true; + rsp->total_domains = 0; + + mutex_lock(&qcom_pdm_mutex); + + service = qcom_pdm_find(data, req->service_name); + if (service) { + struct qcom_pdm_domain *domain; + + rsp->domain_list_valid = true; + rsp->domain_list_len = 0; + + list_for_each_entry(domain, &service->domains, list) { + u32 i = rsp->total_domains++; + + if (i >= offset && i < SERVREG_DOMAIN_LIST_LENGTH) { + u32 j = rsp->domain_list_len++; + + strscpy(rsp->domain_list[j].name, domain->name, + sizeof(rsp->domain_list[i].name)); + rsp->domain_list[j].instance = domain->instance_id; + + pr_debug("PDM: found %s / %d\n", domain->name, + domain->instance_id); + } + } + } + + pr_debug("PDM: service '%s' offset %d returning %d domains (of %d)\n", req->service_name, + req->domain_offset_valid ? req->domain_offset : -1, rsp->domain_list_len, rsp->total_domains); + + ret = qmi_send_response(qmi, sq, txn, SERVREG_GET_DOMAIN_LIST_REQ, + SERVREG_GET_DOMAIN_LIST_RESP_MAX_LEN, + servreg_get_domain_list_resp_ei, rsp); + if (ret) + pr_err("Error sending servreg response: %d\n", ret); + + mutex_unlock(&qcom_pdm_mutex); + + kfree(rsp); +} + +static void qcom_pdm_pfr(struct qmi_handle *qmi, + struct sockaddr_qrtr *sq, + struct qmi_txn *txn, + const void *decoded) +{ + const struct servreg_loc_pfr_req *req = decoded; + struct servreg_loc_pfr_resp rsp = {}; + int ret; + + pr_warn_ratelimited("PDM: service '%s' crash: '%s'\n", req->service, req->reason); + + rsp.rsp.result = QMI_RESULT_SUCCESS_V01; + rsp.rsp.error = QMI_ERR_NONE_V01; + + ret = qmi_send_response(qmi, sq, txn, SERVREG_LOC_PFR_REQ, + SERVREG_LOC_PFR_RESP_MAX_LEN, + servreg_loc_pfr_resp_ei, &rsp); + if (ret) + pr_err("Error sending servreg response: %d\n", ret); +} + +static const struct qmi_msg_handler qcom_pdm_msg_handlers[] = { + { + .type = QMI_REQUEST, + .msg_id = SERVREG_GET_DOMAIN_LIST_REQ, + .ei = servreg_get_domain_list_req_ei, + .decoded_size = sizeof(struct servreg_get_domain_list_req), + .fn = qcom_pdm_get_domain_list, + }, + { + .type = QMI_REQUEST, + .msg_id = SERVREG_LOC_PFR_REQ, + .ei = servreg_loc_pfr_req_ei, + .decoded_size = sizeof(struct servreg_loc_pfr_req), + .fn = qcom_pdm_pfr, + }, + { }, +}; + +static const struct qcom_pdm_domain_data adsp_audio_pd = { + .domain = "msm/adsp/audio_pd", + .instance_id = 74, + .services = { + "avs/audio", + NULL, + }, +}; + +static const struct qcom_pdm_domain_data adsp_charger_pd = { + .domain = "msm/adsp/charger_pd", + .instance_id = 74, + .services = { NULL }, +}; + +static const struct qcom_pdm_domain_data adsp_root_pd = { + .domain = "msm/adsp/root_pd", + .instance_id = 74, + .services = { NULL }, +}; + +static const struct qcom_pdm_domain_data adsp_root_pd_pdr = { + .domain = "msm/adsp/root_pd", + .instance_id = 74, + .services = { + "tms/pdr_enabled", + NULL, + }, +}; + +static const struct qcom_pdm_domain_data adsp_sensor_pd = { + .domain = "msm/adsp/sensor_pd", + .instance_id = 74, + .services = { NULL }, +}; + +static const struct qcom_pdm_domain_data msm8996_adsp_audio_pd = { + .domain = "msm/adsp/audio_pd", + .instance_id = 4, + .services = { NULL }, +}; + +static const struct qcom_pdm_domain_data msm8996_adsp_root_pd = { + .domain = "msm/adsp/root_pd", + .instance_id = 4, + .services = { NULL }, +}; + +static const struct qcom_pdm_domain_data cdsp_root_pd = { + .domain = "msm/cdsp/root_pd", + .instance_id = 76, + .services = { NULL }, +}; + +static const struct qcom_pdm_domain_data slpi_root_pd = { + .domain = "msm/slpi/root_pd", + .instance_id = 90, + .services = { NULL }, +}; + +static const struct qcom_pdm_domain_data slpi_sensor_pd = { + .domain = "msm/slpi/sensor_pd", + .instance_id = 90, + .services = { NULL }, +}; + +static const struct qcom_pdm_domain_data mpss_root_pd = { + .domain = "msm/modem/root_pd", + .instance_id = 180, + .services = { + NULL, + }, +}; + +static const struct qcom_pdm_domain_data mpss_root_pd_gps = { + .domain = "msm/modem/root_pd", + .instance_id = 180, + .services = { + "gps/gps_service", + NULL, + }, +}; + +static const struct qcom_pdm_domain_data mpss_root_pd_gps_pdr = { + .domain = "msm/modem/root_pd", + .instance_id = 180, + .services = { + "gps/gps_service", + "tms/pdr_enabled", + NULL, + }, +}; + +static const struct qcom_pdm_domain_data msm8996_mpss_root_pd = { + .domain = "msm/modem/root_pd", + .instance_id = 100, + .services = { NULL }, +}; + +static const struct qcom_pdm_domain_data mpss_wlan_pd = { + .domain = "msm/modem/wlan_pd", + .instance_id = 180, + .services = { + "kernel/elf_loader", + "wlan/fw", + NULL, + }, +}; + +static const struct qcom_pdm_domain_data *msm8996_domains[] = { + &msm8996_adsp_audio_pd, + &msm8996_adsp_root_pd, + &msm8996_mpss_root_pd, + NULL, +}; + +static const struct qcom_pdm_domain_data *msm8998_domains[] = { + &mpss_root_pd, + &mpss_wlan_pd, + NULL, +}; + +static const struct qcom_pdm_domain_data *qcm2290_domains[] = { + &adsp_audio_pd, + &adsp_root_pd, + &adsp_sensor_pd, + &mpss_root_pd_gps, + &mpss_wlan_pd, + NULL, +}; + +static const struct qcom_pdm_domain_data *qcs404_domains[] = { + &adsp_audio_pd, + &adsp_root_pd, + &adsp_sensor_pd, + &cdsp_root_pd, + &mpss_root_pd, + &mpss_wlan_pd, + NULL, +}; + +static const struct qcom_pdm_domain_data *sc7180_domains[] = { + &adsp_audio_pd, + &adsp_root_pd_pdr, + &adsp_sensor_pd, + &mpss_root_pd_gps_pdr, + &mpss_wlan_pd, + NULL, +}; + +static const struct qcom_pdm_domain_data *sc7280_domains[] = { + &adsp_audio_pd, + &adsp_root_pd_pdr, + &adsp_charger_pd, + &adsp_sensor_pd, + &cdsp_root_pd, + &mpss_root_pd_gps_pdr, + NULL, +}; + +static const struct qcom_pdm_domain_data *sc8180x_domains[] = { + &adsp_audio_pd, + &adsp_root_pd, + &adsp_charger_pd, + &cdsp_root_pd, + &mpss_root_pd_gps, + &mpss_wlan_pd, + NULL, +}; + +static const struct qcom_pdm_domain_data *sc8280xp_domains[] = { + &adsp_audio_pd, + &adsp_root_pd_pdr, + &adsp_charger_pd, + &cdsp_root_pd, + NULL, +}; + +static const struct qcom_pdm_domain_data *sdm660_domains[] = { + &adsp_audio_pd, + &adsp_root_pd, + &adsp_sensor_pd, + &cdsp_root_pd, + &mpss_root_pd, + &mpss_wlan_pd, + NULL, +}; + +static const struct qcom_pdm_domain_data *sdm670_domains[] = { + &adsp_audio_pd, + &adsp_root_pd, + &cdsp_root_pd, + &mpss_root_pd, + &mpss_wlan_pd, + NULL, +}; + +static const struct qcom_pdm_domain_data *sdm845_domains[] = { + &adsp_audio_pd, + &adsp_root_pd, + &cdsp_root_pd, + &mpss_root_pd, + &mpss_wlan_pd, + &slpi_root_pd, + &slpi_sensor_pd, + NULL, +}; + +static const struct qcom_pdm_domain_data *sm6115_domains[] = { + &adsp_audio_pd, + &adsp_root_pd, + &adsp_sensor_pd, + &cdsp_root_pd, + &mpss_root_pd_gps, + &mpss_wlan_pd, + NULL, +}; + +static const struct qcom_pdm_domain_data *sm6350_domains[] = { + &adsp_audio_pd, + &adsp_root_pd, + &adsp_sensor_pd, + &cdsp_root_pd, + &mpss_wlan_pd, + NULL, +}; + +static const struct qcom_pdm_domain_data *sm8150_domains[] = { + &adsp_audio_pd, + &adsp_root_pd, + &cdsp_root_pd, + &mpss_root_pd_gps, + &mpss_wlan_pd, + NULL, +}; + +static const struct qcom_pdm_domain_data *sm8250_domains[] = { + &adsp_audio_pd, + &adsp_root_pd, + &cdsp_root_pd, + &slpi_root_pd, + &slpi_sensor_pd, + NULL, +}; + +static const struct qcom_pdm_domain_data *sm8350_domains[] = { + &adsp_audio_pd, + &adsp_root_pd_pdr, + &adsp_charger_pd, + &cdsp_root_pd, + &mpss_root_pd_gps, + &slpi_root_pd, + &slpi_sensor_pd, + NULL, +}; + +static const struct qcom_pdm_domain_data *sm8550_domains[] = { + &adsp_audio_pd, + &adsp_root_pd, + &adsp_charger_pd, + &adsp_sensor_pd, + &cdsp_root_pd, + &mpss_root_pd_gps, + NULL, +}; + +static const struct of_device_id qcom_pdm_domains[] = { + { .compatible = "qcom,apq8064", .data = NULL, }, + { .compatible = "qcom,apq8074", .data = NULL, }, + { .compatible = "qcom,apq8084", .data = NULL, }, + { .compatible = "qcom,apq8096", .data = msm8996_domains, }, + { .compatible = "qcom,msm8226", .data = NULL, }, + { .compatible = "qcom,msm8974", .data = NULL, }, + { .compatible = "qcom,msm8996", .data = msm8996_domains, }, + { .compatible = "qcom,msm8998", .data = msm8998_domains, }, + { .compatible = "qcom,qcm2290", .data = qcm2290_domains, }, + { .compatible = "qcom,qcs404", .data = qcs404_domains, }, + { .compatible = "qcom,sc7180", .data = sc7180_domains, }, + { .compatible = "qcom,sc7280", .data = sc7280_domains, }, + { .compatible = "qcom,sc8180x", .data = sc8180x_domains, }, + { .compatible = "qcom,sc8280xp", .data = sc8280xp_domains, }, + { .compatible = "qcom,sda660", .data = sdm660_domains, }, + { .compatible = "qcom,sdm660", .data = sdm660_domains, }, + { .compatible = "qcom,sdm670", .data = sdm670_domains, }, + { .compatible = "qcom,sdm845", .data = sdm845_domains, }, + { .compatible = "qcom,sm4250", .data = sm6115_domains, }, + { .compatible = "qcom,sm6115", .data = sm6115_domains, }, + { .compatible = "qcom,sm6350", .data = sm6350_domains, }, + { .compatible = "qcom,sm8150", .data = sm8150_domains, }, + { .compatible = "qcom,sm8250", .data = sm8250_domains, }, + { .compatible = "qcom,sm8350", .data = sm8350_domains, }, + { .compatible = "qcom,sm8450", .data = sm8350_domains, }, + { .compatible = "qcom,sm8550", .data = sm8550_domains, }, + { .compatible = "qcom,sm8650", .data = sm8550_domains, }, + {}, +}; + +static void qcom_pdm_stop(struct qcom_pdm_data *data) +{ + qcom_pdm_free_domains(data); + + /* The server is removed automatically */ + qmi_handle_release(&data->handle); + + kfree(data); +} + +static struct qcom_pdm_data *qcom_pdm_start(void) +{ + const struct qcom_pdm_domain_data * const *domains; + const struct of_device_id *match; + struct qcom_pdm_data *data; + struct device_node *root; + int ret, i; + + root = of_find_node_by_path("/"); + if (!root) + return ERR_PTR(-ENODEV); + + match = of_match_node(qcom_pdm_domains, root); + of_node_put(root); + if (!match) { + pr_notice("PDM: no support for the platform, userspace daemon might be required.\n"); + return ERR_PTR(-ENODEV); + } + + domains = match->data; + if (!domains) { + pr_debug("PDM: no domains\n"); + return ERR_PTR(-ENODEV); + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&data->services); + + ret = qmi_handle_init(&data->handle, SERVREG_GET_DOMAIN_LIST_REQ_MAX_LEN, + NULL, qcom_pdm_msg_handlers); + if (ret) { + kfree(data); + return ERR_PTR(ret); + } + + refcount_set(&data->refcnt, 1); + + for (i = 0; domains[i]; i++) { + ret = qcom_pdm_add_domain(data, domains[i]); + if (ret) + goto err_stop; + } + + ret = qmi_add_server(&data->handle, SERVREG_LOCATOR_SERVICE, + SERVREG_QMI_VERSION, SERVREG_QMI_INSTANCE); + if (ret) { + pr_err("PDM: error adding server %d\n", ret); + goto err_stop; + } + + return data; + +err_stop: + qcom_pdm_stop(data); + + return ERR_PTR(ret); +} + +static int qcom_pdm_probe(struct auxiliary_device *auxdev, + const struct auxiliary_device_id *id) + +{ + struct qcom_pdm_data *data; + int ret = 0; + + mutex_lock(&qcom_pdm_mutex); + + if (!__qcom_pdm_data) { + data = qcom_pdm_start(); + + if (IS_ERR(data)) + ret = PTR_ERR(data); + else + __qcom_pdm_data = data; + } + + auxiliary_set_drvdata(auxdev, __qcom_pdm_data); + + mutex_unlock(&qcom_pdm_mutex); + + return ret; +} + +static void qcom_pdm_remove(struct auxiliary_device *auxdev) +{ + struct qcom_pdm_data *data; + + data = auxiliary_get_drvdata(auxdev); + if (!data) + return; + + if (refcount_dec_and_mutex_lock(&data->refcnt, &qcom_pdm_mutex)) { + __qcom_pdm_data = NULL; + qcom_pdm_stop(data); + mutex_unlock(&qcom_pdm_mutex); + } +} + +static const struct auxiliary_device_id qcom_pdm_table[] = { + { .name = "qcom_common.pd-mapper" }, + {}, +}; +MODULE_DEVICE_TABLE(auxiliary, qcom_pdm_table); + +static struct auxiliary_driver qcom_pdm_drv = { + .name = "qcom-pdm-mapper", + .id_table = qcom_pdm_table, + .probe = qcom_pdm_probe, + .remove = qcom_pdm_remove, +}; +module_auxiliary_driver(qcom_pdm_drv); + +MODULE_DESCRIPTION("Qualcomm Protection Domain Mapper"); +MODULE_LICENSE("GPL"); diff --git a/drivers/soc/qcom/qcom_pdr_msg.c b/drivers/soc/qcom/qcom_pdr_msg.c new file mode 100644 index 000000000000..bf3e4a47165e --- /dev/null +++ b/drivers/soc/qcom/qcom_pdr_msg.c @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 The Linux Foundation. All rights reserved. + */ + +#include <linux/module.h> +#include <linux/soc/qcom/qmi.h> + +#include "pdr_internal.h" + +const struct qmi_elem_info servreg_location_entry_ei[] = { + { + .data_type = QMI_STRING, + .elem_len = SERVREG_NAME_LENGTH + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct servreg_location_entry, + name), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct servreg_location_entry, + instance), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct servreg_location_entry, + service_data_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct servreg_location_entry, + service_data), + }, + {} +}; +EXPORT_SYMBOL_GPL(servreg_location_entry_ei); + +const struct qmi_elem_info servreg_get_domain_list_req_ei[] = { + { + .data_type = QMI_STRING, + .elem_len = SERVREG_NAME_LENGTH + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct servreg_get_domain_list_req, + service_name), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct servreg_get_domain_list_req, + domain_offset_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct servreg_get_domain_list_req, + domain_offset), + }, + {} +}; +EXPORT_SYMBOL_GPL(servreg_get_domain_list_req_ei); + +const struct qmi_elem_info servreg_get_domain_list_resp_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct servreg_get_domain_list_resp, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct servreg_get_domain_list_resp, + total_domains_valid), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(u16), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct servreg_get_domain_list_resp, + total_domains), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct servreg_get_domain_list_resp, + db_rev_count_valid), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(u16), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct servreg_get_domain_list_resp, + db_rev_count), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct servreg_get_domain_list_resp, + domain_list_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct servreg_get_domain_list_resp, + domain_list_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = SERVREG_DOMAIN_LIST_LENGTH, + .elem_size = sizeof(struct servreg_location_entry), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct servreg_get_domain_list_resp, + domain_list), + .ei_array = servreg_location_entry_ei, + }, + {} +}; +EXPORT_SYMBOL_GPL(servreg_get_domain_list_resp_ei); + +const struct qmi_elem_info servreg_register_listener_req_ei[] = { + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct servreg_register_listener_req, + enable), + }, + { + .data_type = QMI_STRING, + .elem_len = SERVREG_NAME_LENGTH + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct servreg_register_listener_req, + service_path), + }, + {} +}; +EXPORT_SYMBOL_GPL(servreg_register_listener_req_ei); + +const struct qmi_elem_info servreg_register_listener_resp_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct servreg_register_listener_resp, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct servreg_register_listener_resp, + curr_state_valid), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum servreg_service_state), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct servreg_register_listener_resp, + curr_state), + }, + {} +}; +EXPORT_SYMBOL_GPL(servreg_register_listener_resp_ei); + +const struct qmi_elem_info servreg_restart_pd_req_ei[] = { + { + .data_type = QMI_STRING, + .elem_len = SERVREG_NAME_LENGTH + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct servreg_restart_pd_req, + service_path), + }, + {} +}; +EXPORT_SYMBOL_GPL(servreg_restart_pd_req_ei); + +const struct qmi_elem_info servreg_restart_pd_resp_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct servreg_restart_pd_resp, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + {} +}; +EXPORT_SYMBOL_GPL(servreg_restart_pd_resp_ei); + +const struct qmi_elem_info servreg_state_updated_ind_ei[] = { + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct servreg_state_updated_ind, + curr_state), + }, + { + .data_type = QMI_STRING, + .elem_len = SERVREG_NAME_LENGTH + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct servreg_state_updated_ind, + service_path), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(u16), + .array_type = NO_ARRAY, + .tlv_type = 0x03, + .offset = offsetof(struct servreg_state_updated_ind, + transaction_id), + }, + {} +}; +EXPORT_SYMBOL_GPL(servreg_state_updated_ind_ei); + +const struct qmi_elem_info servreg_set_ack_req_ei[] = { + { + .data_type = QMI_STRING, + .elem_len = SERVREG_NAME_LENGTH + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct servreg_set_ack_req, + service_path), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(u16), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct servreg_set_ack_req, + transaction_id), + }, + {} +}; +EXPORT_SYMBOL_GPL(servreg_set_ack_req_ei); + +const struct qmi_elem_info servreg_set_ack_resp_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct servreg_set_ack_resp, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + {} +}; +EXPORT_SYMBOL_GPL(servreg_set_ack_resp_ei); + +const struct qmi_elem_info servreg_loc_pfr_req_ei[] = { + { + .data_type = QMI_STRING, + .elem_len = SERVREG_NAME_LENGTH + 1, + .elem_size = sizeof(char), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct servreg_loc_pfr_req, service) + }, + { + .data_type = QMI_STRING, + .elem_len = SERVREG_NAME_LENGTH + 1, + .elem_size = sizeof(char), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct servreg_loc_pfr_req, reason) + }, + {} +}; +EXPORT_SYMBOL_GPL(servreg_loc_pfr_req_ei); + +const struct qmi_elem_info servreg_loc_pfr_resp_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof_field(struct servreg_loc_pfr_resp, rsp), + .tlv_type = 0x02, + .offset = offsetof(struct servreg_loc_pfr_resp, rsp), + .ei_array = qmi_response_type_v01_ei, + }, + {} +}; +EXPORT_SYMBOL_GPL(servreg_loc_pfr_resp_ei); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Qualcomm Protection Domain messages data"); diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 561d8037b50a..de86009ecd91 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -646,13 +646,14 @@ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) { struct tcs_group *tcs; int tcs_id; - unsigned long flags; + + might_sleep(); tcs = get_tcs_for_msg(drv, msg); if (IS_ERR(tcs)) return PTR_ERR(tcs); - spin_lock_irqsave(&drv->lock, flags); + spin_lock_irq(&drv->lock); /* Wait forever for a free tcs. It better be there eventually! */ wait_event_lock_irq(drv->tcs_wait, @@ -670,7 +671,7 @@ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) write_tcs_reg_sync(drv, drv->regs[RSC_DRV_CMD_ENABLE], tcs_id, 0); enable_tcs_irq(drv, tcs_id, true); } - spin_unlock_irqrestore(&drv->lock, flags); + spin_unlock_irq(&drv->lock); /* * These two can be done after the lock is released because: diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index 9f26d7f9b9dc..8903ed956312 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -183,7 +183,6 @@ static int __rpmh_write(const struct device *dev, enum rpmh_state state, } if (state == RPMH_ACTIVE_ONLY_STATE) { - WARN_ON(irqs_disabled()); ret = rpmh_rsc_send_data(ctrlr_to_drv(ctrlr), &rpm_msg->msg); } else { /* Clean up our call by spoofing tx_done */ diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index 7191fa0c087f..e40aac281b06 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -795,6 +795,39 @@ int qcom_smem_get_soc_id(u32 *id) } EXPORT_SYMBOL_GPL(qcom_smem_get_soc_id); +/** + * qcom_smem_get_feature_code() - return the feature code + * @code: On success, return the feature code here. + * + * Look up the feature code identifier from SMEM and return it. + * + * Return: 0 on success, negative errno on failure. + */ +int qcom_smem_get_feature_code(u32 *code) +{ + struct socinfo *info; + u32 raw_code; + + info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, NULL); + if (IS_ERR(info)) + return PTR_ERR(info); + + /* This only makes sense for socinfo >= 16 */ + if (__le32_to_cpu(info->fmt) < SOCINFO_VERSION(0, 16)) + return -EOPNOTSUPP; + + raw_code = __le32_to_cpu(info->feature_code); + + /* Ensure the value makes sense */ + if (raw_code > SOCINFO_FC_INT_MAX) + raw_code = SOCINFO_FC_UNKNOWN; + + *code = raw_code; + + return 0; +} +EXPORT_SYMBOL_GPL(qcom_smem_get_feature_code); + static int qcom_smem_get_sbl_version(struct qcom_smem *smem) { struct smem_header *header; diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c index a21241cbeec7..696c2a8387d0 100644 --- a/drivers/soc/qcom/smp2p.c +++ b/drivers/soc/qcom/smp2p.c @@ -16,6 +16,7 @@ #include <linux/platform_device.h> #include <linux/pm_wakeirq.h> #include <linux/regmap.h> +#include <linux/seq_file.h> #include <linux/soc/qcom/smem.h> #include <linux/soc/qcom/smem_state.h> #include <linux/spinlock.h> @@ -353,11 +354,19 @@ static int smp2p_set_irq_type(struct irq_data *irqd, unsigned int type) return 0; } +static void smp2p_irq_print_chip(struct irq_data *irqd, struct seq_file *p) +{ + struct smp2p_entry *entry = irq_data_get_irq_chip_data(irqd); + + seq_printf(p, " %8s", dev_name(entry->smp2p->dev)); +} + static struct irq_chip smp2p_irq_chip = { .name = "smp2p", .irq_mask = smp2p_mask_irq, .irq_unmask = smp2p_unmask_irq, .irq_set_type = smp2p_set_irq_type, + .irq_print_chip = smp2p_irq_print_chip, }; static int smp2p_irq_map(struct irq_domain *d, @@ -617,7 +626,7 @@ static int qcom_smp2p_probe(struct platform_device *pdev) ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, qcom_smp2p_intr, IRQF_ONESHOT, - "smp2p", (void *)smp2p); + NULL, (void *)smp2p); if (ret) { dev_err(&pdev->dev, "failed to request interrupt\n"); goto unwind_interfaces; diff --git a/drivers/soc/qcom/smsm.c b/drivers/soc/qcom/smsm.c index e7c7e9a640a6..ffe78ae34386 100644 --- a/drivers/soc/qcom/smsm.c +++ b/drivers/soc/qcom/smsm.c @@ -5,6 +5,7 @@ */ #include <linux/interrupt.h> +#include <linux/mailbox_client.h> #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of_irq.h> @@ -71,6 +72,7 @@ struct smsm_host; * @lock: spinlock for read-modify-write of the outgoing state * @entries: context for each of the entries * @hosts: context for each of the hosts + * @mbox_client: mailbox client handle */ struct qcom_smsm { struct device *dev; @@ -88,6 +90,8 @@ struct qcom_smsm { struct smsm_entry *entries; struct smsm_host *hosts; + + struct mbox_client mbox_client; }; /** @@ -120,11 +124,14 @@ struct smsm_entry { * @ipc_regmap: regmap for outgoing interrupt * @ipc_offset: offset in @ipc_regmap for outgoing interrupt * @ipc_bit: bit in @ipc_regmap + @ipc_offset for outgoing interrupt + * @mbox_chan: apcs ipc mailbox channel handle */ struct smsm_host { struct regmap *ipc_regmap; int ipc_offset; int ipc_bit; + + struct mbox_chan *mbox_chan; }; /** @@ -172,7 +179,13 @@ static int smsm_update_bits(void *data, u32 mask, u32 value) hostp = &smsm->hosts[host]; val = readl(smsm->subscription + host); - if (val & changes && hostp->ipc_regmap) { + if (!(val & changes)) + continue; + + if (hostp->mbox_chan) { + mbox_send_message(hostp->mbox_chan, NULL); + mbox_client_txdone(hostp->mbox_chan, 0); + } else if (hostp->ipc_regmap) { regmap_write(hostp->ipc_regmap, hostp->ipc_offset, BIT(hostp->ipc_bit)); @@ -353,6 +366,28 @@ static const struct irq_domain_ops smsm_irq_ops = { }; /** + * smsm_parse_mbox() - requests an mbox channel + * @smsm: smsm driver context + * @host_id: index of the remote host to be resolved + * + * Requests the desired channel using the mbox interface which is needed for + * sending the outgoing interrupts to a remove hosts - identified by @host_id. + */ +static int smsm_parse_mbox(struct qcom_smsm *smsm, unsigned int host_id) +{ + struct smsm_host *host = &smsm->hosts[host_id]; + int ret = 0; + + host->mbox_chan = mbox_request_channel(&smsm->mbox_client, host_id); + if (IS_ERR(host->mbox_chan)) { + ret = PTR_ERR(host->mbox_chan); + host->mbox_chan = NULL; + } + + return ret; +} + +/** * smsm_parse_ipc() - parses a qcom,ipc-%d device tree property * @smsm: smsm driver context * @host_id: index of the remote host to be resolved @@ -521,8 +556,16 @@ static int qcom_smsm_probe(struct platform_device *pdev) "qcom,local-host", &smsm->local_host); + smsm->mbox_client.dev = &pdev->dev; + smsm->mbox_client.knows_txdone = true; + /* Parse the host properties */ for (id = 0; id < smsm->num_hosts; id++) { + /* Try using mbox interface first, otherwise fall back to syscon */ + ret = smsm_parse_mbox(smsm, id); + if (!ret) + continue; + ret = smsm_parse_ipc(smsm, id); if (ret < 0) goto out_put; @@ -609,6 +652,9 @@ unwind_interfaces: qcom_smem_state_unregister(smsm->state); out_put: + for (id = 0; id < smsm->num_hosts; id++) + mbox_free_channel(smsm->hosts[id].mbox_chan); + of_node_put(local_node); return ret; } @@ -622,6 +668,9 @@ static void qcom_smsm_remove(struct platform_device *pdev) if (smsm->entries[id].domain) irq_domain_remove(smsm->entries[id].domain); + for (id = 0; id < smsm->num_hosts; id++) + mbox_free_channel(smsm->hosts[id].mbox_chan); + qcom_smem_state_unregister(smsm->state); } diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 277c07a6603d..d7359a235e3c 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -21,14 +21,6 @@ #include <dt-bindings/arm/qcom,ids.h> -/* - * SoC version type with major number in the upper 16 bits and minor - * number in the lower 16 bits. - */ -#define SOCINFO_MAJOR(ver) (((ver) >> 16) & 0xffff) -#define SOCINFO_MINOR(ver) ((ver) & 0xffff) -#define SOCINFO_VERSION(maj, min) ((((maj) & 0xffff) << 16)|((min) & 0xffff)) - /* Helper macros to create soc_id table */ #define qcom_board_id(id) QCOM_ID_ ## id, __stringify(id) #define qcom_board_id_named(id, name) QCOM_ID_ ## id, (name) @@ -124,6 +116,7 @@ static const char *const pmic_models[] = { [50] = "PM8350B", [51] = "PMR735A", [52] = "PMR735B", + [54] = "PM6350", [55] = "PM4125", [58] = "PM8450", [65] = "PM8010", @@ -133,7 +126,8 @@ static const char *const pmic_models[] = { [72] = "PMR735D", [73] = "PM8550", [74] = "PMK8550", - [82] = "SMB2360", + [82] = "PMC8380", + [83] = "SMB2360", }; struct socinfo_params { @@ -348,6 +342,7 @@ static const struct soc_id soc_id[] = { { qcom_board_id(SDA630) }, { qcom_board_id(MSM8905) }, { qcom_board_id(SDX202) }, + { qcom_board_id(SDM670) }, { qcom_board_id(SDM450) }, { qcom_board_id(SM8150) }, { qcom_board_id(SDA845) }, @@ -445,6 +440,7 @@ static const struct soc_id soc_id[] = { { qcom_board_id(QCS8550) }, { qcom_board_id(QCM8550) }, { qcom_board_id(IPQ5300) }, + { qcom_board_id(IPQ5321) }, }; static const char *socinfo_machine(struct device *dev, unsigned int id) diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c index 06e2c4c2a4a8..f75659fff287 100644 --- a/drivers/soc/qcom/spm.c +++ b/drivers/soc/qcom/spm.c @@ -572,4 +572,5 @@ static int __init qcom_spm_init(void) } arch_initcall(qcom_spm_init); +MODULE_DESCRIPTION("Qualcomm Subsystem Power Manager (SPM)"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/wcnss_ctrl.c b/drivers/soc/qcom/wcnss_ctrl.c index 148bcbac332d..62b424e90d90 100644 --- a/drivers/soc/qcom/wcnss_ctrl.c +++ b/drivers/soc/qcom/wcnss_ctrl.c @@ -3,6 +3,7 @@ * Copyright (c) 2016, Linaro Ltd. * Copyright (c) 2015, Sony Mobile Communications Inc. */ +#include <linux/cleanup.h> #include <linux/firmware.h> #include <linux/module.h> #include <linux/slab.h> @@ -198,7 +199,6 @@ static int wcnss_request_version(struct wcnss_ctrl *wcnss) */ static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc) { - struct wcnss_download_nv_req *req; const struct firmware *fw; struct device *dev = wcnss->dev; const char *nvbin = NVBIN_FILE; @@ -206,18 +206,19 @@ static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc) ssize_t left; int ret; - req = kzalloc(sizeof(*req) + NV_FRAGMENT_SIZE, GFP_KERNEL); + struct wcnss_download_nv_req *req __free(kfree) = kzalloc(sizeof(*req) + NV_FRAGMENT_SIZE, + GFP_KERNEL); if (!req) return -ENOMEM; ret = of_property_read_string(dev->of_node, "firmware-name", &nvbin); if (ret < 0 && ret != -EINVAL) - goto free_req; + return ret; ret = request_firmware(&fw, nvbin, dev); if (ret < 0) { dev_err(dev, "Failed to load nv file %s: %d\n", nvbin, ret); - goto free_req; + return ret; } data = fw->data; @@ -263,8 +264,6 @@ static int wcnss_download_nv(struct wcnss_ctrl *wcnss, bool *expect_cbc) release_fw: release_firmware(fw); -free_req: - kfree(req); return ret; } diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c index fd8b6ac06656..a0123070a816 100644 --- a/drivers/soc/samsung/exynos-pmu.c +++ b/drivers/soc/samsung/exynos-pmu.c @@ -129,14 +129,30 @@ static int tensor_set_bits_atomic(void *ctx, unsigned int offset, u32 val, return ret; } -static int tensor_sec_update_bits(void *ctx, unsigned int reg, - unsigned int mask, unsigned int val) +static bool tensor_is_atomic(unsigned int reg) { /* * Use atomic operations for PMU_ALIVE registers (offset 0~0x3FFF) - * as the target registers can be accessed by multiple masters. + * as the target registers can be accessed by multiple masters. SFRs + * that don't support atomic are added to the switch statement below. */ if (reg > PMUALIVE_MASK) + return false; + + switch (reg) { + case GS101_SYSIP_DAT0: + case GS101_SYSTEM_CONFIGURATION: + return false; + default: + return true; + } +} + +static int tensor_sec_update_bits(void *ctx, unsigned int reg, + unsigned int mask, unsigned int val) +{ + + if (!tensor_is_atomic(reg)) return tensor_sec_reg_rmw(ctx, reg, mask, val); return tensor_set_bits_atomic(ctx, reg, val, mask); diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c index 6eb6cf06278e..2781a091a6a6 100644 --- a/drivers/soc/sunxi/sunxi_sram.c +++ b/drivers/soc/sunxi/sunxi_sram.c @@ -33,7 +33,6 @@ struct sunxi_sram_data { u8 offset; u8 width; struct sunxi_sram_func *func; - struct list_head list; }; struct sunxi_sram_desc { @@ -103,7 +102,6 @@ static const struct of_device_id sunxi_sram_dt_ids[] = { }; static struct device *sram_dev; -static LIST_HEAD(claimed_sram); static DEFINE_SPINLOCK(sram_lock); static void __iomem *base; @@ -346,7 +344,7 @@ static void sunxi_sram_unlock(void *_lock) spin_unlock(lock); } -static struct regmap_config sunxi_sram_regmap_config = { +static const struct regmap_config sunxi_sram_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 91d0ad6ddefc..6c37d6eb8b49 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -2891,15 +2891,11 @@ static int tegra_pmc_probe(struct platform_device *pdev) pmc->aotag = base; pmc->scratch = base; } else { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "wake"); - pmc->wake = devm_ioremap_resource(&pdev->dev, res); + pmc->wake = devm_platform_ioremap_resource_byname(pdev, "wake"); if (IS_ERR(pmc->wake)) return PTR_ERR(pmc->wake); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "aotag"); - pmc->aotag = devm_ioremap_resource(&pdev->dev, res); + pmc->aotag = devm_platform_ioremap_resource_byname(pdev, "aotag"); if (IS_ERR(pmc->aotag)) return PTR_ERR(pmc->aotag); diff --git a/drivers/soc/ti/k3-socinfo.c b/drivers/soc/ti/k3-socinfo.c index 59101bf7cf23..4fb0f0a24828 100644 --- a/drivers/soc/ti/k3-socinfo.c +++ b/drivers/soc/ti/k3-socinfo.c @@ -61,7 +61,7 @@ static const struct k3_soc_id { }; static const char * const j721e_rev_string_map[] = { - "1.0", "1.1", + "1.0", "1.1", "2.0", }; static int diff --git a/drivers/soc/ti/knav_qmss.h b/drivers/soc/ti/knav_qmss.h index a01eda720bf6..9325e8ce2e25 100644 --- a/drivers/soc/ti/knav_qmss.h +++ b/drivers/soc/ti/knav_qmss.h @@ -333,7 +333,7 @@ struct knav_range_info { void *queue_base_inst; unsigned flags; struct list_head list; - struct knav_range_ops *ops; + const struct knav_range_ops *ops; struct knav_acc_info acc_info; struct knav_acc_channel *acc; unsigned num_irqs; diff --git a/drivers/soc/ti/knav_qmss_acc.c b/drivers/soc/ti/knav_qmss_acc.c index 3d388646ed43..269b4e75ae40 100644 --- a/drivers/soc/ti/knav_qmss_acc.c +++ b/drivers/soc/ti/knav_qmss_acc.c @@ -450,7 +450,7 @@ static int knav_acc_free_range(struct knav_range_info *range) return 0; } -static struct knav_range_ops knav_acc_range_ops = { +static const struct knav_range_ops knav_acc_range_ops = { .set_notify = knav_acc_set_notify, .init_queue = knav_acc_init_queue, .open_queue = knav_acc_open_queue, diff --git a/drivers/soc/ti/knav_qmss_queue.c b/drivers/soc/ti/knav_qmss_queue.c index 06fb5505c22c..f2055a76f84c 100644 --- a/drivers/soc/ti/knav_qmss_queue.c +++ b/drivers/soc/ti/knav_qmss_queue.c @@ -411,7 +411,7 @@ static int knav_gp_close_queue(struct knav_range_info *range, return 0; } -static struct knav_range_ops knav_gp_range_ops = { +static const struct knav_range_ops knav_gp_range_ops = { .set_notify = knav_gp_set_notify, .open_queue = knav_gp_open_queue, .close_queue = knav_gp_close_queue, diff --git a/drivers/soc/ti/pm33xx.c b/drivers/soc/ti/pm33xx.c index 8e983c3c4e03..3a56bbf3268a 100644 --- a/drivers/soc/ti/pm33xx.c +++ b/drivers/soc/ti/pm33xx.c @@ -450,14 +450,14 @@ static int am33xx_pm_rtc_setup(void) rtc_base_virt = of_iomap(np, 0); if (!rtc_base_virt) { - pr_warn("PM: could not iomap rtc"); + pr_warn("PM: could not iomap rtc\n"); error = -ENODEV; goto err_clk_put; } omap_rtc = rtc_class_open("rtc0"); if (!omap_rtc) { - pr_warn("PM: rtc0 not available"); + pr_warn("PM: rtc0 not available\n"); error = -EPROBE_DEFER; goto err_iounmap; } diff --git a/drivers/soc/xilinx/xlnx_event_manager.c b/drivers/soc/xilinx/xlnx_event_manager.c index 253299e4214d..f529e1346247 100644 --- a/drivers/soc/xilinx/xlnx_event_manager.c +++ b/drivers/soc/xilinx/xlnx_event_manager.c @@ -3,6 +3,7 @@ * Xilinx Event Management Driver * * Copyright (C) 2021 Xilinx, Inc. + * Copyright (C) 2024 Advanced Micro Devices, Inc. * * Abhyuday Godhasara <abhyuday.godhasara@xilinx.com> */ @@ -19,7 +20,7 @@ #include <linux/platform_device.h> #include <linux/slab.h> -static DEFINE_PER_CPU_READ_MOSTLY(int, cpu_number1); +static DEFINE_PER_CPU_READ_MOSTLY(int, dummy_cpu_number); static int virq_sgi; static int event_manager_availability = -EACCES; @@ -35,7 +36,6 @@ static int event_manager_availability = -EACCES; #define MAX_BITS (32U) /* Number of bits available for error mask */ -#define FIRMWARE_VERSION_MASK (0xFFFFU) #define REGISTER_NOTIFIER_FIRMWARE_VERSION (2U) static DEFINE_HASHTABLE(reg_driver_map, REGISTERED_DRIVER_MAX_ORDER); @@ -570,7 +570,6 @@ static void xlnx_disable_percpu_irq(void *data) static int xlnx_event_init_sgi(struct platform_device *pdev) { int ret = 0; - int cpu; /* * IRQ related structures are used for the following: * for each SGI interrupt ensure its mapped by GIC IRQ domain @@ -607,11 +606,8 @@ static int xlnx_event_init_sgi(struct platform_device *pdev) sgi_fwspec.param[0] = sgi_num; virq_sgi = irq_create_fwspec_mapping(&sgi_fwspec); - cpu = get_cpu(); - per_cpu(cpu_number1, cpu) = cpu; ret = request_percpu_irq(virq_sgi, xlnx_event_handler, "xlnx_event_mgmt", - &cpu_number1); - put_cpu(); + &dummy_cpu_number); WARN_ON(ret); if (ret) { @@ -627,16 +623,12 @@ static int xlnx_event_init_sgi(struct platform_device *pdev) static void xlnx_event_cleanup_sgi(struct platform_device *pdev) { - int cpu = smp_processor_id(); - - per_cpu(cpu_number1, cpu) = cpu; - cpuhp_remove_state(CPUHP_AP_ONLINE_DYN); on_each_cpu(xlnx_disable_percpu_irq, NULL, 1); irq_clear_status_flags(virq_sgi, IRQ_PER_CPU); - free_percpu_irq(virq_sgi, &cpu_number1); + free_percpu_irq(virq_sgi, &dummy_cpu_number); irq_dispose_mapping(virq_sgi); } diff --git a/drivers/soc/xilinx/zynqmp_power.c b/drivers/soc/xilinx/zynqmp_power.c index 965b1143936a..411d33f2fb05 100644 --- a/drivers/soc/xilinx/zynqmp_power.c +++ b/drivers/soc/xilinx/zynqmp_power.c @@ -30,9 +30,27 @@ struct zynqmp_pm_work_struct { u32 args[CB_ARG_CNT]; }; -static struct zynqmp_pm_work_struct *zynqmp_pm_init_suspend_work; +/** + * struct zynqmp_pm_event_info - event related information + * @cb_fun: Function pointer to store the callback function. + * @cb_type: Type of callback from pm_api_cb_id, + * PM_NOTIFY_CB - for Error Events, + * PM_INIT_SUSPEND_CB - for suspend callback. + * @node_id: Node-Id related to event. + * @event: Event Mask for the Error Event. + * @wake: Flag specifying whether the subsystem should be woken upon + * event notification. + */ +struct zynqmp_pm_event_info { + event_cb_func_t cb_fun; + enum pm_api_cb_id cb_type; + u32 node_id; + u32 event; + bool wake; +}; + +static struct zynqmp_pm_work_struct *zynqmp_pm_init_suspend_work, *zynqmp_pm_init_restart_work; static struct mbox_chan *rx_chan; -static bool event_registered; enum pm_suspend_mode { PM_SUSPEND_MODE_FIRST = 0, @@ -54,6 +72,19 @@ static void zynqmp_pm_get_callback_data(u32 *buf) zynqmp_pm_invoke_fn(GET_CALLBACK_DATA, buf, 0); } +static void subsystem_restart_event_callback(const u32 *payload, void *data) +{ + /* First element is callback API ID, others are callback arguments */ + if (work_pending(&zynqmp_pm_init_restart_work->callback_work)) + return; + + /* Copy callback arguments into work's structure */ + memcpy(zynqmp_pm_init_restart_work->args, &payload[0], + sizeof(zynqmp_pm_init_restart_work->args)); + + queue_work(system_unbound_wq, &zynqmp_pm_init_restart_work->callback_work); +} + static void suspend_event_callback(const u32 *payload, void *data) { /* First element is callback API ID, others are callback arguments */ @@ -120,6 +151,37 @@ static void ipi_receive_callback(struct mbox_client *cl, void *data) } /** + * zynqmp_pm_subsystem_restart_work_fn - Initiate Subsystem restart + * @work: Pointer to work_struct + * + * Bottom-half of PM callback IRQ handler. + */ +static void zynqmp_pm_subsystem_restart_work_fn(struct work_struct *work) +{ + int ret; + struct zynqmp_pm_work_struct *pm_work = container_of(work, struct zynqmp_pm_work_struct, + callback_work); + + /* First element is callback API ID, others are callback arguments */ + if (pm_work->args[0] == PM_NOTIFY_CB) { + if (pm_work->args[2] == EVENT_SUBSYSTEM_RESTART) { + ret = zynqmp_pm_system_shutdown(ZYNQMP_PM_SHUTDOWN_TYPE_SETSCOPE_ONLY, + ZYNQMP_PM_SHUTDOWN_SUBTYPE_SUBSYSTEM); + if (ret) { + pr_err("unable to set shutdown scope\n"); + return; + } + + kernel_restart(NULL); + } else { + pr_err("%s Unsupported Event - %d\n", __func__, pm_work->args[2]); + } + } else { + pr_err("%s() Unsupported Callback %d\n", __func__, pm_work->args[0]); + } +} + +/** * zynqmp_pm_init_suspend_work_fn - Initialize suspend * @work: Pointer to work_struct * @@ -184,13 +246,51 @@ static ssize_t suspend_mode_store(struct device *dev, static DEVICE_ATTR_RW(suspend_mode); +static void unregister_event(struct device *dev, void *res) +{ + struct zynqmp_pm_event_info *event_info = res; + + xlnx_unregister_event(event_info->cb_type, event_info->node_id, + event_info->event, event_info->cb_fun, NULL); +} + +static int register_event(struct device *dev, const enum pm_api_cb_id cb_type, const u32 node_id, + const u32 event, const bool wake, event_cb_func_t cb_fun) +{ + int ret; + struct zynqmp_pm_event_info *event_info; + + event_info = devres_alloc(unregister_event, sizeof(struct zynqmp_pm_event_info), + GFP_KERNEL); + if (!event_info) + return -ENOMEM; + + event_info->cb_type = cb_type; + event_info->node_id = node_id; + event_info->event = event; + event_info->wake = wake; + event_info->cb_fun = cb_fun; + + ret = xlnx_register_event(event_info->cb_type, event_info->node_id, + event_info->event, event_info->wake, event_info->cb_fun, NULL); + if (ret) { + devres_free(event_info); + return ret; + } + + devres_add(dev, event_info); + return 0; +} + static int zynqmp_pm_probe(struct platform_device *pdev) { int ret, irq; - u32 pm_api_version; + u32 pm_api_version, pm_family_code, pm_sub_family_code, node_id; struct mbox_client *client; - zynqmp_pm_get_api_version(&pm_api_version); + ret = zynqmp_pm_get_api_version(&pm_api_version); + if (ret) + return ret; /* Check PM API version number */ if (pm_api_version < ZYNQMP_PM_VERSION) @@ -203,21 +303,43 @@ static int zynqmp_pm_probe(struct platform_device *pdev) * is not available to use) or -ENODEV(Xilinx Event Manager not compiled), * then use ipi-mailbox or interrupt method. */ - ret = xlnx_register_event(PM_INIT_SUSPEND_CB, 0, 0, false, - suspend_event_callback, NULL); + ret = register_event(&pdev->dev, PM_INIT_SUSPEND_CB, 0, 0, false, + suspend_event_callback); if (!ret) { zynqmp_pm_init_suspend_work = devm_kzalloc(&pdev->dev, sizeof(struct zynqmp_pm_work_struct), GFP_KERNEL); - if (!zynqmp_pm_init_suspend_work) { - xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0, - suspend_event_callback, NULL); + if (!zynqmp_pm_init_suspend_work) return -ENOMEM; - } - event_registered = true; INIT_WORK(&zynqmp_pm_init_suspend_work->callback_work, zynqmp_pm_init_suspend_work_fn); + + ret = zynqmp_pm_get_family_info(&pm_family_code, &pm_sub_family_code); + if (ret < 0) + return ret; + + if (pm_sub_family_code == VERSALNET_SUB_FAMILY_CODE) + node_id = PM_DEV_ACPU_0_0; + else + node_id = PM_DEV_ACPU_0; + + ret = register_event(&pdev->dev, PM_NOTIFY_CB, node_id, EVENT_SUBSYSTEM_RESTART, + false, subsystem_restart_event_callback); + if (ret) { + dev_err(&pdev->dev, "Failed to Register with Xilinx Event manager %d\n", + ret); + return ret; + } + + zynqmp_pm_init_restart_work = devm_kzalloc(&pdev->dev, + sizeof(struct zynqmp_pm_work_struct), + GFP_KERNEL); + if (!zynqmp_pm_init_restart_work) + return -ENOMEM; + + INIT_WORK(&zynqmp_pm_init_restart_work->callback_work, + zynqmp_pm_subsystem_restart_work_fn); } else if (ret != -EACCES && ret != -ENODEV) { dev_err(&pdev->dev, "Failed to Register with Xilinx Event manager %d\n", ret); return ret; @@ -264,15 +386,8 @@ static int zynqmp_pm_probe(struct platform_device *pdev) } ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr); - if (ret) { - if (event_registered) { - xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0, suspend_event_callback, - NULL); - event_registered = false; - } - dev_err(&pdev->dev, "unable to create sysfs interface\n"); + if (ret) return ret; - } return 0; } @@ -280,8 +395,6 @@ static int zynqmp_pm_probe(struct platform_device *pdev) static void zynqmp_pm_remove(struct platform_device *pdev) { sysfs_remove_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr); - if (event_registered) - xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0, suspend_event_callback, NULL); if (!rx_chan) mbox_free_channel(rx_chan); |