diff options
Diffstat (limited to 'drivers/soc')
55 files changed, 1750 insertions, 296 deletions
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index ba8f5b5460..fb2bd31387 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -7,7 +7,7 @@ obj-y += apple/ obj-y += aspeed/ obj-$(CONFIG_ARCH_AT91) += atmel/ obj-y += bcm/ -obj-$(CONFIG_SOC_CANAAN) += canaan/ +obj-$(CONFIG_ARCH_CANAAN) += canaan/ obj-$(CONFIG_ARCH_DOVE) += dove/ obj-$(CONFIG_MACH_DOVE) += dove/ obj-y += fsl/ diff --git a/drivers/soc/canaan/Kconfig b/drivers/soc/canaan/Kconfig index 43ced2bf84..3121d351fe 100644 --- a/drivers/soc/canaan/Kconfig +++ b/drivers/soc/canaan/Kconfig @@ -2,9 +2,9 @@ config SOC_K210_SYSCTL bool "Canaan Kendryte K210 SoC system controller" - depends on RISCV && SOC_CANAAN && OF + depends on RISCV && SOC_CANAAN_K210 && OF depends on COMMON_CLK_K210 - default SOC_CANAAN + default SOC_CANAAN_K210 select PM select MFD_SYSCON help diff --git a/drivers/soc/fsl/qbman/bman_ccsr.c b/drivers/soc/fsl/qbman/bman_ccsr.c index cb24a08be0..b0f26f6f73 100644 --- a/drivers/soc/fsl/qbman/bman_ccsr.c +++ b/drivers/soc/fsl/qbman/bman_ccsr.c @@ -144,17 +144,6 @@ static int bm_set_memory(u64 ba, u32 size) static dma_addr_t fbpr_a; static size_t fbpr_sz; -static int bman_fbpr(struct reserved_mem *rmem) -{ - fbpr_a = rmem->base; - fbpr_sz = rmem->size; - - WARN_ON(!(fbpr_a && fbpr_sz)); - - return 0; -} -RESERVEDMEM_OF_DECLARE(bman_fbpr, "fsl,bman-fbpr", bman_fbpr); - static irqreturn_t bman_isr(int irq, void *ptr) { u32 isr_val, ier_val, ecsr_val, isr_mask, i; @@ -242,17 +231,11 @@ static int fsl_bman_probe(struct platform_device *pdev) return -ENODEV; } - /* - * If FBPR memory wasn't defined using the qbman compatible string - * try using the of_reserved_mem_device method - */ - if (!fbpr_a) { - ret = qbman_init_private_mem(dev, 0, &fbpr_a, &fbpr_sz); - if (ret) { - dev_err(dev, "qbman_init_private_mem() failed 0x%x\n", - ret); - return -ENODEV; - } + ret = qbman_init_private_mem(dev, 0, "fsl,bman-fbpr", &fbpr_a, &fbpr_sz); + if (ret) { + dev_err(dev, "qbman_init_private_mem() failed 0x%x\n", + ret); + return -ENODEV; } dev_dbg(dev, "Allocated FBPR 0x%llx 0x%zx\n", fbpr_a, fbpr_sz); diff --git a/drivers/soc/fsl/qbman/dpaa_sys.c b/drivers/soc/fsl/qbman/dpaa_sys.c index 3375145004..e1d7b79cc4 100644 --- a/drivers/soc/fsl/qbman/dpaa_sys.c +++ b/drivers/soc/fsl/qbman/dpaa_sys.c @@ -34,8 +34,8 @@ /* * Initialize a devices private memory region */ -int qbman_init_private_mem(struct device *dev, int idx, dma_addr_t *addr, - size_t *size) +int qbman_init_private_mem(struct device *dev, int idx, const char *compat, + dma_addr_t *addr, size_t *size) { struct device_node *mem_node; struct reserved_mem *rmem; @@ -44,8 +44,12 @@ int qbman_init_private_mem(struct device *dev, int idx, dma_addr_t *addr, mem_node = of_parse_phandle(dev->of_node, "memory-region", idx); if (!mem_node) { - dev_err(dev, "No memory-region found for index %d\n", idx); - return -ENODEV; + mem_node = of_find_compatible_node(NULL, NULL, compat); + if (!mem_node) { + dev_err(dev, "No memory-region found for index %d or compatible '%s'\n", + idx, compat); + return -ENODEV; + } } rmem = of_reserved_mem_lookup(mem_node); diff --git a/drivers/soc/fsl/qbman/dpaa_sys.h b/drivers/soc/fsl/qbman/dpaa_sys.h index ae8afa552b..16485bde96 100644 --- a/drivers/soc/fsl/qbman/dpaa_sys.h +++ b/drivers/soc/fsl/qbman/dpaa_sys.h @@ -101,8 +101,8 @@ static inline u8 dpaa_cyc_diff(u8 ringsize, u8 first, u8 last) #define DPAA_GENALLOC_OFF 0x80000000 /* Initialize the devices private memory region */ -int qbman_init_private_mem(struct device *dev, int idx, dma_addr_t *addr, - size_t *size); +int qbman_init_private_mem(struct device *dev, int idx, const char *compat, + dma_addr_t *addr, size_t *size); /* memremap() attributes for different platforms */ #ifdef CONFIG_PPC diff --git a/drivers/soc/fsl/qbman/qman_ccsr.c b/drivers/soc/fsl/qbman/qman_ccsr.c index 157659fd03..392e54f14d 100644 --- a/drivers/soc/fsl/qbman/qman_ccsr.c +++ b/drivers/soc/fsl/qbman/qman_ccsr.c @@ -468,28 +468,6 @@ static int zero_priv_mem(phys_addr_t addr, size_t sz) return 0; } - -static int qman_fqd(struct reserved_mem *rmem) -{ - fqd_a = rmem->base; - fqd_sz = rmem->size; - - WARN_ON(!(fqd_a && fqd_sz)); - return 0; -} -RESERVEDMEM_OF_DECLARE(qman_fqd, "fsl,qman-fqd", qman_fqd); - -static int qman_pfdr(struct reserved_mem *rmem) -{ - pfdr_a = rmem->base; - pfdr_sz = rmem->size; - - WARN_ON(!(pfdr_a && pfdr_sz)); - - return 0; -} -RESERVEDMEM_OF_DECLARE(qman_pfdr, "fsl,qman-pfdr", qman_pfdr); - #endif unsigned int qm_get_fqid_maxcnt(void) @@ -796,39 +774,34 @@ static int fsl_qman_probe(struct platform_device *pdev) qm_channel_caam = QMAN_CHANNEL_CAAM_REV3; } - if (fqd_a) { + /* + * Order of memory regions is assumed as FQD followed by PFDR + * in order to ensure allocations from the correct regions the + * driver initializes then allocates each piece in order + */ + ret = qbman_init_private_mem(dev, 0, "fsl,qman-fqd", &fqd_a, &fqd_sz); + if (ret) { + dev_err(dev, "qbman_init_private_mem() for FQD failed 0x%x\n", + ret); + return -ENODEV; + } #ifdef CONFIG_PPC - /* - * For PPC backward DT compatibility - * FQD memory MUST be zero'd by software - */ - zero_priv_mem(fqd_a, fqd_sz); + /* + * For PPC backward DT compatibility + * FQD memory MUST be zero'd by software + */ + zero_priv_mem(fqd_a, fqd_sz); #else - WARN(1, "Unexpected architecture using non shared-dma-mem reservations"); + WARN(1, "Unexpected architecture using non shared-dma-mem reservations"); #endif - } else { - /* - * Order of memory regions is assumed as FQD followed by PFDR - * in order to ensure allocations from the correct regions the - * driver initializes then allocates each piece in order - */ - ret = qbman_init_private_mem(dev, 0, &fqd_a, &fqd_sz); - if (ret) { - dev_err(dev, "qbman_init_private_mem() for FQD failed 0x%x\n", - ret); - return -ENODEV; - } - } dev_dbg(dev, "Allocated FQD 0x%llx 0x%zx\n", fqd_a, fqd_sz); - if (!pfdr_a) { - /* Setup PFDR memory */ - ret = qbman_init_private_mem(dev, 1, &pfdr_a, &pfdr_sz); - if (ret) { - dev_err(dev, "qbman_init_private_mem() for PFDR failed 0x%x\n", - ret); - return -ENODEV; - } + /* Setup PFDR memory */ + ret = qbman_init_private_mem(dev, 1, "fsl,qman-pfdr", &pfdr_a, &pfdr_sz); + if (ret) { + dev_err(dev, "qbman_init_private_mem() for PFDR failed 0x%x\n", + ret); + return -ENODEV; } dev_dbg(dev, "Allocated PFDR 0x%llx 0x%zx\n", pfdr_a, pfdr_sz); diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig index 0ab688af30..4b0a099b28 100644 --- a/drivers/soc/hisilicon/Kconfig +++ b/drivers/soc/hisilicon/Kconfig @@ -6,7 +6,7 @@ menu "Hisilicon SoC drivers" config KUNPENG_HCCS tristate "HCCS driver on Kunpeng SoC" depends on ACPI - depends on MAILBOX + depends on PCC depends on ARM64 || COMPILE_TEST help The Huawei Cache Coherence System (HCCS) is a multi-chip diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c index 9ff70b38e5..e882a61636 100644 --- a/drivers/soc/hisilicon/kunpeng_hccs.c +++ b/drivers/soc/hisilicon/kunpeng_hccs.c @@ -556,6 +556,12 @@ static int hccs_get_all_port_attr(struct hccs_dev *hdev, start_id = rsp_head.next_id; } + if (left_buf_len != 0) { + dev_err(hdev->dev, "failed to get the expected port number(%u) attribute.\n", + size); + return -EINVAL; + } + return 0; } diff --git a/drivers/soc/litex/Kconfig b/drivers/soc/litex/Kconfig index e6ba3573a7..f3f8696395 100644 --- a/drivers/soc/litex/Kconfig +++ b/drivers/soc/litex/Kconfig @@ -7,7 +7,7 @@ config LITEX config LITEX_SOC_CONTROLLER tristate "Enable LiteX SoC Controller driver" - depends on OF || COMPILE_TEST + depends on OF depends on HAS_IOMEM select LITEX help diff --git a/drivers/soc/litex/litex_soc_ctrl.c b/drivers/soc/litex/litex_soc_ctrl.c index 10813299aa..72c44119dd 100644 --- a/drivers/soc/litex/litex_soc_ctrl.c +++ b/drivers/soc/litex/litex_soc_ctrl.c @@ -82,13 +82,11 @@ static int litex_reset_handler(struct notifier_block *this, unsigned long mode, return NOTIFY_DONE; } -#ifdef CONFIG_OF static const struct of_device_id litex_soc_ctrl_of_match[] = { {.compatible = "litex,soc-controller"}, {}, }; MODULE_DEVICE_TABLE(of, litex_soc_ctrl_of_match); -#endif /* CONFIG_OF */ static int litex_soc_ctrl_probe(struct platform_device *pdev) { @@ -130,7 +128,7 @@ static void litex_soc_ctrl_remove(struct platform_device *pdev) static struct platform_driver litex_soc_ctrl_driver = { .driver = { .name = "litex-soc-controller", - .of_match_table = of_match_ptr(litex_soc_ctrl_of_match) + .of_match_table = litex_soc_ctrl_of_match, }, .probe = litex_soc_ctrl_probe, .remove_new = litex_soc_ctrl_remove, diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig index 0810b5b0c6..1b7afb19cc 100644 --- a/drivers/soc/mediatek/Kconfig +++ b/drivers/soc/mediatek/Kconfig @@ -68,4 +68,14 @@ config MTK_SVS chip process corner, temperatures and other factors. Then DVFS driver could apply SVS bank voltage to PMIC/Buck. +config MTK_SOCINFO + tristate "MediaTek SoC Information" + default y + depends on NVMEM_MTK_EFUSE + select SOC_BUS + help + The MediaTek SoC Information (mtk-socinfo) driver provides + information about the SoC to the userspace including the + manufacturer name, marketing name and soc name. + endmenu diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile index 9d3ce7878c..6830512848 100644 --- a/drivers/soc/mediatek/Makefile +++ b/drivers/soc/mediatek/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_MTK_REGULATOR_COUPLER) += mtk-regulator-coupler.o obj-$(CONFIG_MTK_MMSYS) += mtk-mmsys.o obj-$(CONFIG_MTK_MMSYS) += mtk-mutex.o obj-$(CONFIG_MTK_SVS) += mtk-svs.o +obj-$(CONFIG_MTK_SOCINFO) += mtk-socinfo.o diff --git a/drivers/soc/mediatek/mtk-cmdq-helper.c b/drivers/soc/mediatek/mtk-cmdq-helper.c index 0b2e5690da..046522664d 100644 --- a/drivers/soc/mediatek/mtk-cmdq-helper.c +++ b/drivers/soc/mediatek/mtk-cmdq-helper.c @@ -12,6 +12,8 @@ #define CMDQ_WRITE_ENABLE_MASK BIT(0) #define CMDQ_POLL_ENABLE_MASK BIT(0) +/* 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_REG_TYPE 1 #define CMDQ_JUMP_RELATIVE 0 @@ -56,7 +58,7 @@ int cmdq_dev_get_client_reg(struct device *dev, "mediatek,gce-client-reg", 3, idx, &spec); if (err < 0) { - dev_err(dev, + dev_warn(dev, "error %d can't parse gce-client-reg property (%d)", err, idx); @@ -106,22 +108,16 @@ void cmdq_mbox_destroy(struct cmdq_client *client) } EXPORT_SYMBOL(cmdq_mbox_destroy); -struct cmdq_pkt *cmdq_pkt_create(struct cmdq_client *client, size_t size) +int cmdq_pkt_create(struct cmdq_client *client, struct cmdq_pkt *pkt, size_t size) { - struct cmdq_pkt *pkt; struct device *dev; dma_addr_t dma_addr; - pkt = kzalloc(sizeof(*pkt), GFP_KERNEL); - if (!pkt) - return ERR_PTR(-ENOMEM); pkt->va_base = kzalloc(size, GFP_KERNEL); - if (!pkt->va_base) { - kfree(pkt); - return ERR_PTR(-ENOMEM); - } + if (!pkt->va_base) + return -ENOMEM; + pkt->buf_size = size; - pkt->cl = (void *)client; dev = client->chan->mbox->dev; dma_addr = dma_map_single(dev, pkt->va_base, pkt->buf_size, @@ -129,24 +125,20 @@ struct cmdq_pkt *cmdq_pkt_create(struct cmdq_client *client, size_t size) if (dma_mapping_error(dev, dma_addr)) { dev_err(dev, "dma map failed, size=%u\n", (u32)(u64)size); kfree(pkt->va_base); - kfree(pkt); - return ERR_PTR(-ENOMEM); + return -ENOMEM; } pkt->pa_base = dma_addr; - return pkt; + return 0; } EXPORT_SYMBOL(cmdq_pkt_create); -void cmdq_pkt_destroy(struct cmdq_pkt *pkt) +void cmdq_pkt_destroy(struct cmdq_client *client, struct cmdq_pkt *pkt) { - struct cmdq_client *client = (struct cmdq_client *)pkt->cl; - dma_unmap_single(client->chan->mbox->dev, pkt->pa_base, pkt->buf_size, DMA_TO_DEVICE); kfree(pkt->va_base); - kfree(pkt); } EXPORT_SYMBOL(cmdq_pkt_destroy); @@ -300,6 +292,32 @@ int cmdq_pkt_write_s_mask_value(struct cmdq_pkt *pkt, u8 high_addr_reg_idx, } EXPORT_SYMBOL(cmdq_pkt_write_s_mask_value); +int cmdq_pkt_mem_move(struct cmdq_pkt *pkt, dma_addr_t src_addr, dma_addr_t dst_addr) +{ + const u16 high_addr_reg_idx = CMDQ_THR_SPR_IDX0; + const u16 value_reg_idx = CMDQ_THR_SPR_IDX1; + int ret; + + /* read the value of src_addr into high_addr_reg_idx */ + ret = cmdq_pkt_assign(pkt, high_addr_reg_idx, CMDQ_ADDR_HIGH(src_addr)); + if (ret < 0) + return ret; + ret = cmdq_pkt_read_s(pkt, high_addr_reg_idx, CMDQ_ADDR_LOW(src_addr), value_reg_idx); + if (ret < 0) + return ret; + + /* write the value of value_reg_idx into dst_addr */ + ret = cmdq_pkt_assign(pkt, high_addr_reg_idx, CMDQ_ADDR_HIGH(dst_addr)); + if (ret < 0) + return ret; + ret = cmdq_pkt_write_s(pkt, high_addr_reg_idx, CMDQ_ADDR_LOW(dst_addr), value_reg_idx); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL(cmdq_pkt_mem_move); + int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u16 event, bool clear) { struct cmdq_instruction inst = { {0} }; @@ -316,6 +334,21 @@ int cmdq_pkt_wfe(struct cmdq_pkt *pkt, u16 event, bool clear) } EXPORT_SYMBOL(cmdq_pkt_wfe); +int cmdq_pkt_acquire_event(struct cmdq_pkt *pkt, u16 event) +{ + struct cmdq_instruction inst = {}; + + if (event >= CMDQ_MAX_EVENT) + return -EINVAL; + + inst.op = CMDQ_CODE_WFE; + inst.value = CMDQ_WFE_UPDATE | CMDQ_WFE_UPDATE_VALUE | CMDQ_WFE_WAIT; + inst.event = event; + + return cmdq_pkt_append_command(pkt, inst); +} +EXPORT_SYMBOL(cmdq_pkt_acquire_event); + int cmdq_pkt_clear_event(struct cmdq_pkt *pkt, u16 event) { struct cmdq_instruction inst = { {0} }; @@ -381,6 +414,53 @@ int cmdq_pkt_poll_mask(struct cmdq_pkt *pkt, u8 subsys, } EXPORT_SYMBOL(cmdq_pkt_poll_mask); +int cmdq_pkt_poll_addr(struct cmdq_pkt *pkt, dma_addr_t addr, u32 value, u32 mask) +{ + struct cmdq_instruction inst = { {0} }; + u8 use_mask = 0; + int ret; + + /* + * Append an MASK instruction to set the mask for following POLL instruction + * which enables use_mask bit. + */ + if (mask != GENMASK(31, 0)) { + inst.op = CMDQ_CODE_MASK; + inst.mask = ~mask; + ret = cmdq_pkt_append_command(pkt, inst); + if (ret < 0) + return ret; + use_mask = CMDQ_POLL_ENABLE_MASK; + } + + /* + * POLL is an legacy operation in GCE and it does not support SPR and CMDQ_CODE_LOGIC, + * so it can not use cmdq_pkt_assign to keep polling register address to SPR. + * If user wants to poll a register address which doesn't have a subsys id, + * user needs to use GPR and CMDQ_CODE_MASK to move polling register address to GPR. + */ + inst.op = CMDQ_CODE_MASK; + inst.dst_t = CMDQ_REG_TYPE; + inst.sop = CMDQ_POLL_ADDR_GPR; + inst.value = addr; + ret = cmdq_pkt_append_command(pkt, inst); + if (ret < 0) + return ret; + + /* Append POLL instruction to poll the register address assign to GPR previously. */ + inst.op = CMDQ_CODE_POLL; + inst.dst_t = CMDQ_REG_TYPE; + inst.sop = CMDQ_POLL_ADDR_GPR; + inst.offset = use_mask; + inst.value = value; + ret = cmdq_pkt_append_command(pkt, inst); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL(cmdq_pkt_poll_addr); + int cmdq_pkt_assign(struct cmdq_pkt *pkt, u16 reg_idx, u32 value) { struct cmdq_instruction inst = {}; @@ -393,17 +473,36 @@ int cmdq_pkt_assign(struct cmdq_pkt *pkt, u16 reg_idx, u32 value) } EXPORT_SYMBOL(cmdq_pkt_assign); -int cmdq_pkt_jump(struct cmdq_pkt *pkt, dma_addr_t addr) +int cmdq_pkt_jump_abs(struct cmdq_pkt *pkt, dma_addr_t addr, u8 shift_pa) { struct cmdq_instruction inst = {}; inst.op = CMDQ_CODE_JUMP; inst.offset = CMDQ_JUMP_ABSOLUTE; - inst.value = addr >> - cmdq_get_shift_pa(((struct cmdq_client *)pkt->cl)->chan); + inst.value = addr >> shift_pa; + return cmdq_pkt_append_command(pkt, inst); +} +EXPORT_SYMBOL(cmdq_pkt_jump_abs); + +int cmdq_pkt_jump_rel(struct cmdq_pkt *pkt, s32 offset, u8 shift_pa) +{ + struct cmdq_instruction inst = { {0} }; + + inst.op = CMDQ_CODE_JUMP; + inst.value = (u32)offset >> shift_pa; + return cmdq_pkt_append_command(pkt, inst); +} +EXPORT_SYMBOL(cmdq_pkt_jump_rel); + +int cmdq_pkt_eoc(struct cmdq_pkt *pkt) +{ + struct cmdq_instruction inst = { {0} }; + + inst.op = CMDQ_CODE_EOC; + inst.value = CMDQ_EOC_IRQ_EN; return cmdq_pkt_append_command(pkt, inst); } -EXPORT_SYMBOL(cmdq_pkt_jump); +EXPORT_SYMBOL(cmdq_pkt_eoc); int cmdq_pkt_finalize(struct cmdq_pkt *pkt) { @@ -427,19 +526,4 @@ int cmdq_pkt_finalize(struct cmdq_pkt *pkt) } EXPORT_SYMBOL(cmdq_pkt_finalize); -int cmdq_pkt_flush_async(struct cmdq_pkt *pkt) -{ - int err; - struct cmdq_client *client = (struct cmdq_client *)pkt->cl; - - err = mbox_send_message(client->chan, pkt); - if (err < 0) - return err; - /* We can send next packet immediately, so just call txdone. */ - mbox_client_txdone(client->chan, 0); - - return 0; -} -EXPORT_SYMBOL(cmdq_pkt_flush_async); - MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/mediatek/mtk-mutex.c b/drivers/soc/mediatek/mtk-mutex.c index 73c256d395..01b129caf1 100644 --- a/drivers/soc/mediatek/mtk-mutex.c +++ b/drivers/soc/mediatek/mtk-mutex.c @@ -496,6 +496,40 @@ static const unsigned int mt8188_mutex_mod[DDP_COMPONENT_ID_MAX] = { [DDP_COMPONENT_MERGE5] = MT8188_MUTEX_MOD_DISP1_VPP_MERGE4, }; +static const unsigned int mt8188_mdp_mutex_table_mod[MUTEX_MOD_IDX_MAX] = { + [MUTEX_MOD_IDX_MDP_RDMA0] = MT8195_MUTEX_MOD_MDP_RDMA0, + [MUTEX_MOD_IDX_MDP_RDMA2] = MT8195_MUTEX_MOD_MDP_RDMA2, + [MUTEX_MOD_IDX_MDP_RDMA3] = MT8195_MUTEX_MOD_MDP_RDMA3, + [MUTEX_MOD_IDX_MDP_FG0] = MT8195_MUTEX_MOD_MDP_FG0, + [MUTEX_MOD_IDX_MDP_FG2] = MT8195_MUTEX_MOD_MDP_FG2, + [MUTEX_MOD_IDX_MDP_FG3] = MT8195_MUTEX_MOD_MDP_FG3, + [MUTEX_MOD_IDX_MDP_HDR0] = MT8195_MUTEX_MOD_MDP_HDR0, + [MUTEX_MOD_IDX_MDP_HDR2] = MT8195_MUTEX_MOD_MDP_HDR2, + [MUTEX_MOD_IDX_MDP_HDR3] = MT8195_MUTEX_MOD_MDP_HDR3, + [MUTEX_MOD_IDX_MDP_AAL0] = MT8195_MUTEX_MOD_MDP_AAL0, + [MUTEX_MOD_IDX_MDP_AAL2] = MT8195_MUTEX_MOD_MDP_AAL2, + [MUTEX_MOD_IDX_MDP_AAL3] = MT8195_MUTEX_MOD_MDP_AAL3, + [MUTEX_MOD_IDX_MDP_RSZ0] = MT8195_MUTEX_MOD_MDP_RSZ0, + [MUTEX_MOD_IDX_MDP_RSZ2] = MT8195_MUTEX_MOD_MDP_RSZ2, + [MUTEX_MOD_IDX_MDP_RSZ3] = MT8195_MUTEX_MOD_MDP_RSZ3, + [MUTEX_MOD_IDX_MDP_MERGE2] = MT8195_MUTEX_MOD_MDP_MERGE2, + [MUTEX_MOD_IDX_MDP_MERGE3] = MT8195_MUTEX_MOD_MDP_MERGE3, + [MUTEX_MOD_IDX_MDP_TDSHP0] = MT8195_MUTEX_MOD_MDP_TDSHP0, + [MUTEX_MOD_IDX_MDP_TDSHP2] = MT8195_MUTEX_MOD_MDP_TDSHP2, + [MUTEX_MOD_IDX_MDP_TDSHP3] = MT8195_MUTEX_MOD_MDP_TDSHP3, + [MUTEX_MOD_IDX_MDP_COLOR0] = MT8195_MUTEX_MOD_MDP_COLOR0, + [MUTEX_MOD_IDX_MDP_COLOR2] = MT8195_MUTEX_MOD_MDP_COLOR2, + [MUTEX_MOD_IDX_MDP_COLOR3] = MT8195_MUTEX_MOD_MDP_COLOR3, + [MUTEX_MOD_IDX_MDP_OVL0] = MT8195_MUTEX_MOD_MDP_OVL0, + [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, +}; + static const unsigned int mt8192_mutex_mod[DDP_COMPONENT_ID_MAX] = { [DDP_COMPONENT_AAL0] = MT8192_MUTEX_MOD_DISP_AAL0, [DDP_COMPONENT_CCORR] = MT8192_MUTEX_MOD_DISP_CCORR0, @@ -735,6 +769,13 @@ static const struct mtk_mutex_data mt8188_mutex_driver_data = { .mutex_sof_reg = MT8183_MUTEX0_SOF0, }; +static const struct mtk_mutex_data mt8188_vpp_mutex_driver_data = { + .mutex_sof = mt8188_mutex_sof, + .mutex_mod_reg = MT8183_MUTEX0_MOD0, + .mutex_sof_reg = MT8183_MUTEX0_SOF0, + .mutex_table_mod = mt8188_mdp_mutex_table_mod, +}; + static const struct mtk_mutex_data mt8192_mutex_driver_data = { .mutex_mod = mt8192_mutex_mod, .mutex_sof = mt8183_mutex_sof, @@ -1089,6 +1130,7 @@ static const struct of_device_id mutex_driver_dt_match[] = { { .compatible = "mediatek,mt8186-disp-mutex", .data = &mt8186_mutex_driver_data }, { .compatible = "mediatek,mt8186-mdp3-mutex", .data = &mt8186_mdp_mutex_driver_data }, { .compatible = "mediatek,mt8188-disp-mutex", .data = &mt8188_mutex_driver_data }, + { .compatible = "mediatek,mt8188-vpp-mutex", .data = &mt8188_vpp_mutex_driver_data }, { .compatible = "mediatek,mt8192-disp-mutex", .data = &mt8192_mutex_driver_data }, { .compatible = "mediatek,mt8195-disp-mutex", .data = &mt8195_mutex_driver_data }, { .compatible = "mediatek,mt8195-vpp-mutex", .data = &mt8195_vpp_mutex_driver_data }, diff --git a/drivers/soc/mediatek/mtk-socinfo.c b/drivers/soc/mediatek/mtk-socinfo.c new file mode 100644 index 0000000000..74672a9d6d --- /dev/null +++ b/drivers/soc/mediatek/mtk-socinfo.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023 MediaTek Inc. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/pm_runtime.h> +#include <linux/nvmem-consumer.h> +#include <linux/device.h> +#include <linux/device/bus.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/string.h> +#include <linux/sys_soc.h> +#include <linux/slab.h> +#include <linux/platform_device.h> + +#define MTK_SOCINFO_ENTRY(_soc_name, _segment_name, _marketing_name, _cell_data1, _cell_data2) {\ + .soc_name = _soc_name, \ + .segment_name = _segment_name, \ + .marketing_name = _marketing_name, \ + .cell_data = {_cell_data1, _cell_data2} \ +} +#define CELL_NOT_USED (0xFFFFFFFF) +#define MAX_CELLS (2) + +struct mtk_socinfo { + struct device *dev; + struct name_data *name_data; + struct socinfo_data *socinfo_data; + struct soc_device *soc_dev; +}; + +struct socinfo_data { + char *soc_name; + char *segment_name; + char *marketing_name; + u32 cell_data[MAX_CELLS]; +}; + +static const char *cell_names[MAX_CELLS] = {"socinfo-data1", "socinfo-data2"}; + +static struct socinfo_data socinfo_data_table[] = { + MTK_SOCINFO_ENTRY("MT8173", "MT8173V/AC", "MT8173", 0x6CA20004, 0x10000000), + MTK_SOCINFO_ENTRY("MT8183", "MT8183V/AZA", "Kompanio 500", 0x00010043, 0x00000840), + MTK_SOCINFO_ENTRY("MT8183", "MT8183V/AZA", "Kompanio 500", 0x00010043, 0x00000940), + MTK_SOCINFO_ENTRY("MT8186", "MT8186GV/AZA", "Kompanio 520", 0x81861001, CELL_NOT_USED), + MTK_SOCINFO_ENTRY("MT8186T", "MT8186TV/AZA", "Kompanio 528", 0x81862001, CELL_NOT_USED), + MTK_SOCINFO_ENTRY("MT8188", "MT8188GV/AZA", "Kompanio 838", 0x81880000, 0x00000010), + MTK_SOCINFO_ENTRY("MT8188", "MT8188GV/HZA", "Kompanio 838", 0x81880000, 0x00000011), + MTK_SOCINFO_ENTRY("MT8192", "MT8192V/AZA", "Kompanio 820", 0x00001100, 0x00040080), + MTK_SOCINFO_ENTRY("MT8192T", "MT8192V/ATZA", "Kompanio 828", 0x00000100, 0x000400C0), + MTK_SOCINFO_ENTRY("MT8195", "MT8195GV/EZA", "Kompanio 1200", 0x81950300, CELL_NOT_USED), + MTK_SOCINFO_ENTRY("MT8195", "MT8195GV/EHZA", "Kompanio 1200", 0x81950304, CELL_NOT_USED), + MTK_SOCINFO_ENTRY("MT8195", "MT8195TV/EZA", "Kompanio 1380", 0x81950400, CELL_NOT_USED), + MTK_SOCINFO_ENTRY("MT8195", "MT8195TV/EHZA", "Kompanio 1380", 0x81950404, CELL_NOT_USED), + MTK_SOCINFO_ENTRY("MT8395", "MT8395AV/ZA", "Genio 1200", 0x83950100, CELL_NOT_USED), +}; + +static int mtk_socinfo_create_socinfo_node(struct mtk_socinfo *mtk_socinfop) +{ + struct soc_device_attribute *attrs; + static char machine[30] = {0}; + static const char *soc_manufacturer = "MediaTek"; + + attrs = devm_kzalloc(mtk_socinfop->dev, sizeof(*attrs), GFP_KERNEL); + if (!attrs) + return -ENOMEM; + + snprintf(machine, sizeof(machine), "%s (%s)", mtk_socinfop->socinfo_data->marketing_name, + mtk_socinfop->socinfo_data->soc_name); + attrs->family = soc_manufacturer; + attrs->machine = machine; + + mtk_socinfop->soc_dev = soc_device_register(attrs); + if (IS_ERR(mtk_socinfop->soc_dev)) + return PTR_ERR(mtk_socinfop->soc_dev); + + dev_info(mtk_socinfop->dev, "%s %s SoC detected.\n", soc_manufacturer, attrs->machine); + return 0; +} + +static u32 mtk_socinfo_read_cell(struct device *dev, const char *name) +{ + struct nvmem_device *nvmemp; + struct device_node *np, *nvmem_node = dev->parent->of_node; + u32 offset; + u32 cell_val = CELL_NOT_USED; + + /* should never fail since the nvmem driver registers this child */ + nvmemp = nvmem_device_find(nvmem_node, device_match_of_node); + if (IS_ERR(nvmemp)) + goto out; + + np = of_get_child_by_name(nvmem_node, name); + if (!np) + goto put_device; + + if (of_property_read_u32_index(np, "reg", 0, &offset)) + goto put_node; + + nvmem_device_read(nvmemp, offset, sizeof(cell_val), &cell_val); + +put_node: + of_node_put(np); +put_device: + nvmem_device_put(nvmemp); +out: + return cell_val; +} + +static int mtk_socinfo_get_socinfo_data(struct mtk_socinfo *mtk_socinfop) +{ + unsigned int i, j; + unsigned int num_cell_data = 0; + u32 cell_data[MAX_CELLS] = {0}; + bool match_socinfo; + int match_socinfo_index = -1; + + for (i = 0; i < MAX_CELLS; i++) { + cell_data[i] = mtk_socinfo_read_cell(mtk_socinfop->dev, cell_names[i]); + if (cell_data[i] != CELL_NOT_USED) + num_cell_data++; + else + break; + } + + if (!num_cell_data) + return -ENOENT; + + for (i = 0; i < ARRAY_SIZE(socinfo_data_table); i++) { + match_socinfo = true; + for (j = 0; j < num_cell_data; j++) { + if (cell_data[j] != socinfo_data_table[i].cell_data[j]) { + match_socinfo = false; + break; + } + } + if (match_socinfo) { + mtk_socinfop->socinfo_data = &(socinfo_data_table[i]); + match_socinfo_index = i; + break; + } + } + + if (match_socinfo_index < 0) { + dev_warn(mtk_socinfop->dev, + "Unknown MediaTek SoC with ID 0x%08x 0x%08x\n", + cell_data[0], cell_data[1]); + return -ENOENT; + } + + return match_socinfo_index; +} + +static int mtk_socinfo_probe(struct platform_device *pdev) +{ + struct mtk_socinfo *mtk_socinfop; + int ret; + + mtk_socinfop = devm_kzalloc(&pdev->dev, sizeof(*mtk_socinfop), GFP_KERNEL); + if (!mtk_socinfop) + return -ENOMEM; + + mtk_socinfop->dev = &pdev->dev; + + ret = mtk_socinfo_get_socinfo_data(mtk_socinfop); + if (ret < 0) + return dev_err_probe(mtk_socinfop->dev, ret, "Failed to get socinfo data\n"); + + ret = mtk_socinfo_create_socinfo_node(mtk_socinfop); + if (ret) + return dev_err_probe(mtk_socinfop->dev, ret, "Cannot create node\n"); + + platform_set_drvdata(pdev, mtk_socinfop); + return 0; +} + +static void mtk_socinfo_remove(struct platform_device *pdev) +{ + struct mtk_socinfo *mtk_socinfop = platform_get_drvdata(pdev); + + soc_device_unregister(mtk_socinfop->soc_dev); +} + +static struct platform_driver mtk_socinfo = { + .probe = mtk_socinfo_probe, + .remove_new = mtk_socinfo_remove, + .driver = { + .name = "mtk-socinfo", + }, +}; +module_platform_driver(mtk_socinfo); + +MODULE_AUTHOR("William-TW LIN <william-tw.lin@mediatek.com>"); +MODULE_DESCRIPTION("MediaTek socinfo driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/soc/pxa/ssp.c b/drivers/soc/pxa/ssp.c index 7af04e8b81..854d32e045 100644 --- a/drivers/soc/pxa/ssp.c +++ b/drivers/soc/pxa/ssp.c @@ -25,7 +25,7 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/platform_device.h> -#include <linux/spi/pxa2xx_spi.h> +#include <linux/pxa2xx_ssp.h> #include <linux/io.h> #include <linux/of.h> #include <linux/of_device.h> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index c6ca4de425..5af33b0e34 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -268,4 +268,13 @@ config QCOM_INLINE_CRYPTO_ENGINE tristate select QCOM_SCM +config QCOM_PBS + tristate "PBS trigger support for Qualcomm Technologies, Inc. PMICS" + depends on SPMI + help + This driver supports configuring software programmable boot sequencer (PBS) + trigger event through PBS RAM on Qualcomm Technologies, Inc. PMICs. + This module provides the APIs to the client drivers that wants to send the + PBS trigger event to the PBS RAM. + endmenu diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 05b3d54e8d..ca0bece0df 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 CFLAGS_rpmh-rsc.o := -I$(src) +CFLAGS_qcom_aoss.o := -I$(src) obj-$(CONFIG_QCOM_AOSS_QMP) += qcom_aoss.o obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o @@ -34,3 +35,4 @@ obj-$(CONFIG_QCOM_KRYO_L2_ACCESSORS) += kryo-l2-accessors.o obj-$(CONFIG_QCOM_ICC_BWMON) += icc-bwmon.o qcom_ice-objs += ice.o obj-$(CONFIG_QCOM_INLINE_CRYPTO_ENGINE) += qcom_ice.o +obj-$(CONFIG_QCOM_PBS) += qcom-pbs.o diff --git a/drivers/soc/qcom/apr.c b/drivers/soc/qcom/apr.c index 1f8b315576..50749e870e 100644 --- a/drivers/soc/qcom/apr.c +++ b/drivers/soc/qcom/apr.c @@ -399,7 +399,7 @@ static int apr_uevent(const struct device *dev, struct kobj_uevent_env *env) return add_uevent_var(env, "MODALIAS=apr:%s", adev->name); } -struct bus_type aprbus = { +const struct bus_type aprbus = { .name = "aprbus", .match = apr_device_match, .probe = apr_device_probe, diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c index a5fd68411b..d845726620 100644 --- a/drivers/soc/qcom/cmd-db.c +++ b/drivers/soc/qcom/cmd-db.c @@ -1,6 +1,10 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (c) 2016-2018, 2020, The Linux Foundation. All rights reserved. */ +/* + * Copyright (c) 2016-2018, 2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. + */ +#include <linux/bitfield.h> #include <linux/debugfs.h> #include <linux/kernel.h> #include <linux/module.h> @@ -17,6 +21,8 @@ #define MAX_SLV_ID 8 #define SLAVE_ID_MASK 0x7 #define SLAVE_ID_SHIFT 16 +#define SLAVE_ID(addr) FIELD_GET(GENMASK(19, 16), addr) +#define VRM_ADDR(addr) FIELD_GET(GENMASK(19, 4), addr) /** * struct entry_header: header for each entry in cmddb @@ -147,12 +153,7 @@ static int cmd_db_get_header(const char *id, const struct entry_header **eh, if (ret) return ret; - /* - * Pad out query string to same length as in DB. NOTE: the output - * query string is not necessarily '\0' terminated if it bumps up - * against the max size. That's OK and expected. - */ - strncpy(query, id, sizeof(query)); + strtomem_pad(query, id, 0); for (i = 0; i < MAX_SLV_ID; i++) { rsc_hdr = &cmd_db_header->header[i]; @@ -221,6 +222,30 @@ const void *cmd_db_read_aux_data(const char *id, size_t *len) EXPORT_SYMBOL_GPL(cmd_db_read_aux_data); /** + * cmd_db_match_resource_addr() - Compare if both Resource addresses are same + * + * @addr1: Resource address to compare + * @addr2: Resource address to compare + * + * Return: true if two addresses refer to the same resource, false otherwise + */ +bool cmd_db_match_resource_addr(u32 addr1, u32 addr2) +{ + /* + * Each RPMh VRM accelerator resource has 3 or 4 contiguous 4-byte + * aligned addresses associated with it. Ignore the offset to check + * for VRM requests. + */ + if (addr1 == addr2) + return true; + else if (SLAVE_ID(addr1) == CMD_DB_HW_VRM && VRM_ADDR(addr1) == VRM_ADDR(addr2)) + return true; + + return false; +} +EXPORT_SYMBOL_GPL(cmd_db_match_resource_addr); + +/** * cmd_db_read_slave_id - Get the slave ID for a given resource address * * @id: Resource id to query the DB for version @@ -362,7 +387,7 @@ static int __init cmd_db_device_init(void) { return platform_driver_register(&cmd_db_dev_driver); } -arch_initcall(cmd_db_device_init); +core_initcall(cmd_db_device_init); MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Command DB Driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/icc-bwmon.c b/drivers/soc/qcom/icc-bwmon.c index 6567062593..e785197408 100644 --- a/drivers/soc/qcom/icc-bwmon.c +++ b/drivers/soc/qcom/icc-bwmon.c @@ -282,7 +282,7 @@ static const struct regmap_config msm8998_bwmon_regmap_cfg = { * Cache is necessary for using regmap fields with non-readable * registers. */ - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static const struct regmap_config msm8998_bwmon_global_regmap_cfg = { @@ -301,7 +301,7 @@ static const struct regmap_config msm8998_bwmon_global_regmap_cfg = { * Cache is necessary for using regmap fields with non-readable * registers. */ - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static const struct reg_field sdm845_cpu_bwmon_reg_fields[] = { @@ -369,7 +369,7 @@ static const struct regmap_config sdm845_cpu_bwmon_regmap_cfg = { * Cache is necessary for using regmap fields with non-readable * registers. */ - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; /* BWMON v5 */ @@ -446,7 +446,7 @@ static const struct regmap_config sdm845_llcc_bwmon_regmap_cfg = { * Cache is necessary for using regmap fields with non-readable * registers. */ - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static void bwmon_clear_counters(struct icc_bwmon *bwmon, bool clear_all) @@ -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/pdr_interface.c b/drivers/soc/qcom/pdr_interface.c index a1b6a4081d..216166e98f 100644 --- a/drivers/soc/qcom/pdr_interface.c +++ b/drivers/soc/qcom/pdr_interface.c @@ -76,12 +76,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 +104,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 +365,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; @@ -415,7 +417,7 @@ static int pdr_locate_service(struct pdr_handle *pdr, struct pdr_service *pds) if (ret < 0) goto out; - 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)) diff --git a/drivers/soc/qcom/pmic_glink.c b/drivers/soc/qcom/pmic_glink.c index 823fd108fa..9ebc0ba359 100644 --- a/drivers/soc/qcom/pmic_glink.c +++ b/drivers/soc/qcom/pmic_glink.c @@ -348,16 +348,15 @@ static void pmic_glink_remove(struct platform_device *pdev) mutex_unlock(&__pmic_glink_lock); } -static const unsigned long pmic_glink_sc8180x_client_mask = BIT(PMIC_GLINK_CLIENT_BATT) | - BIT(PMIC_GLINK_CLIENT_ALTMODE); +static const unsigned long pmic_glink_sc8280xp_client_mask = BIT(PMIC_GLINK_CLIENT_BATT) | + BIT(PMIC_GLINK_CLIENT_ALTMODE); static const unsigned long pmic_glink_sm8450_client_mask = BIT(PMIC_GLINK_CLIENT_BATT) | BIT(PMIC_GLINK_CLIENT_ALTMODE) | BIT(PMIC_GLINK_CLIENT_UCSI); static const struct of_device_id pmic_glink_of_match[] = { - { .compatible = "qcom,sc8180x-pmic-glink", .data = &pmic_glink_sc8180x_client_mask }, - { .compatible = "qcom,sc8280xp-pmic-glink", .data = &pmic_glink_sc8180x_client_mask }, + { .compatible = "qcom,sc8280xp-pmic-glink", .data = &pmic_glink_sc8280xp_client_mask }, { .compatible = "qcom,pmic-glink", .data = &pmic_glink_sm8450_client_mask }, {} }; @@ -374,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_pdcharger_ulog.c b/drivers/soc/qcom/pmic_pdcharger_ulog.c index 238cd38589..39f412bbf2 100644 --- a/drivers/soc/qcom/pmic_pdcharger_ulog.c +++ b/drivers/soc/qcom/pmic_pdcharger_ulog.c @@ -150,6 +150,10 @@ static const struct rpmsg_device_id pmic_pdcharger_ulog_rpmsg_id_match[] = { { "PMIC_LOGS_ADSP_APPS" }, {} }; +/* + * No MODULE_DEVICE_TABLE intentionally: that's a debugging module, to be + * loaded manually only. + */ static struct rpmsg_driver pmic_pdcharger_ulog_rpmsg_driver = { .probe = pmic_pdcharger_ulog_rpmsg_probe, diff --git a/drivers/soc/qcom/pmic_pdcharger_ulog.h b/drivers/soc/qcom/pmic_pdcharger_ulog.h index 152e3a6b54..1cfa58f0e3 100644 --- a/drivers/soc/qcom/pmic_pdcharger_ulog.h +++ b/drivers/soc/qcom/pmic_pdcharger_ulog.h @@ -18,7 +18,7 @@ TRACE_EVENT(pmic_pdcharger_ulog_msg, __string(msg, msg) ), TP_fast_assign( - __assign_str(msg, msg); + __assign_str(msg); ), TP_printk("%s", __get_str(msg)) ); diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c index bdcf44b85b..2e8f24d5da 100644 --- a/drivers/soc/qcom/qcom-geni-se.c +++ b/drivers/soc/qcom/qcom-geni-se.c @@ -89,7 +89,6 @@ * @base: Base address of this instance of QUP wrapper core * @clks: Handle to the primary & optional secondary AHB clocks * @num_clks: Count of clocks - * @to_core: Core ICC path */ struct geni_wrapper { struct device *dev; diff --git a/drivers/soc/qcom/qcom-pbs.c b/drivers/soc/qcom/qcom-pbs.c new file mode 100644 index 0000000000..6af49b5060 --- /dev/null +++ b/drivers/soc/qcom/qcom-pbs.c @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> +#include <linux/spmi.h> +#include <linux/soc/qcom/qcom-pbs.h> + +#define PBS_CLIENT_TRIG_CTL 0x42 +#define PBS_CLIENT_SW_TRIG_BIT BIT(7) +#define PBS_CLIENT_SCRATCH1 0x50 +#define PBS_CLIENT_SCRATCH2 0x51 +#define PBS_CLIENT_SCRATCH2_ERROR 0xFF + +#define RETRIES 2000 +#define DELAY 1100 + +struct pbs_dev { + struct device *dev; + struct regmap *regmap; + struct mutex lock; + struct device_link *link; + + u32 base; +}; + +static int qcom_pbs_wait_for_ack(struct pbs_dev *pbs, u8 bit_pos) +{ + unsigned int val; + int ret; + + ret = regmap_read_poll_timeout(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, + val, val & BIT(bit_pos), DELAY, DELAY * RETRIES); + + if (ret < 0) { + dev_err(pbs->dev, "Timeout for PBS ACK/NACK for bit %u\n", bit_pos); + return -ETIMEDOUT; + } + + if (val == PBS_CLIENT_SCRATCH2_ERROR) { + ret = regmap_write(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, 0); + dev_err(pbs->dev, "NACK from PBS for bit %u\n", bit_pos); + return -EINVAL; + } + + dev_dbg(pbs->dev, "PBS sequence for bit %u executed!\n", bit_pos); + return 0; +} + +/** + * qcom_pbs_trigger_event() - Trigger the PBS RAM sequence + * @pbs: Pointer to PBS device + * @bitmap: bitmap + * + * This function is used to trigger the PBS RAM sequence to be + * executed by the client driver. + * + * The PBS trigger sequence involves + * 1. setting the PBS sequence bit in PBS_CLIENT_SCRATCH1 + * 2. Initiating the SW PBS trigger + * 3. Checking the equivalent bit in PBS_CLIENT_SCRATCH2 for the + * completion of the sequence. + * 4. If PBS_CLIENT_SCRATCH2 == 0xFF, the PBS sequence failed to execute + * + * Return: 0 on success, < 0 on failure + */ +int qcom_pbs_trigger_event(struct pbs_dev *pbs, u8 bitmap) +{ + unsigned int val; + u16 bit_pos; + int ret; + + if (WARN_ON(!bitmap)) + return -EINVAL; + + if (IS_ERR_OR_NULL(pbs)) + return -EINVAL; + + mutex_lock(&pbs->lock); + ret = regmap_read(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, &val); + if (ret < 0) + goto out; + + if (val == PBS_CLIENT_SCRATCH2_ERROR) { + /* PBS error - clear SCRATCH2 register */ + ret = regmap_write(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, 0); + if (ret < 0) + goto out; + } + + for (bit_pos = 0; bit_pos < 8; bit_pos++) { + if (!(bitmap & BIT(bit_pos))) + continue; + + /* Clear the PBS sequence bit position */ + ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, + BIT(bit_pos), 0); + if (ret < 0) + goto out_clear_scratch1; + + /* Set the PBS sequence bit position */ + ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH1, + BIT(bit_pos), BIT(bit_pos)); + if (ret < 0) + goto out_clear_scratch1; + + /* Initiate the SW trigger */ + ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_TRIG_CTL, + PBS_CLIENT_SW_TRIG_BIT, PBS_CLIENT_SW_TRIG_BIT); + if (ret < 0) + goto out_clear_scratch1; + + ret = qcom_pbs_wait_for_ack(pbs, bit_pos); + if (ret < 0) + goto out_clear_scratch1; + + /* Clear the PBS sequence bit position */ + regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH1, BIT(bit_pos), 0); + regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, BIT(bit_pos), 0); + } + +out_clear_scratch1: + /* Clear all the requested bitmap */ + ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH1, bitmap, 0); + +out: + mutex_unlock(&pbs->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(qcom_pbs_trigger_event); + +/** + * get_pbs_client_device() - Get the PBS device used by client + * @dev: Client device + * + * This function is used to get the PBS device that is being + * used by the client. + * + * Return: pbs_dev on success, ERR_PTR on failure + */ +struct pbs_dev *get_pbs_client_device(struct device *dev) +{ + struct device_node *pbs_dev_node; + struct platform_device *pdev; + struct pbs_dev *pbs; + + pbs_dev_node = of_parse_phandle(dev->of_node, "qcom,pbs", 0); + if (!pbs_dev_node) { + dev_err(dev, "Missing qcom,pbs property\n"); + return ERR_PTR(-ENODEV); + } + + pdev = of_find_device_by_node(pbs_dev_node); + if (!pdev) { + dev_err(dev, "Unable to find PBS dev_node\n"); + pbs = ERR_PTR(-EPROBE_DEFER); + goto out; + } + + pbs = platform_get_drvdata(pdev); + if (!pbs) { + dev_err(dev, "Cannot get pbs instance from %s\n", dev_name(&pdev->dev)); + platform_device_put(pdev); + pbs = ERR_PTR(-EPROBE_DEFER); + goto out; + } + + pbs->link = device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_SUPPLIER); + if (!pbs->link) { + dev_err(&pdev->dev, "Failed to create device link to consumer %s\n", dev_name(dev)); + platform_device_put(pdev); + pbs = ERR_PTR(-EINVAL); + goto out; + } + +out: + of_node_put(pbs_dev_node); + return pbs; +} +EXPORT_SYMBOL_GPL(get_pbs_client_device); + +static int qcom_pbs_probe(struct platform_device *pdev) +{ + struct pbs_dev *pbs; + u32 val; + int ret; + + pbs = devm_kzalloc(&pdev->dev, sizeof(*pbs), GFP_KERNEL); + if (!pbs) + return -ENOMEM; + + pbs->dev = &pdev->dev; + pbs->regmap = dev_get_regmap(pbs->dev->parent, NULL); + if (!pbs->regmap) { + dev_err(pbs->dev, "Couldn't get parent's regmap\n"); + return -EINVAL; + } + + ret = device_property_read_u32(pbs->dev, "reg", &val); + if (ret < 0) { + dev_err(pbs->dev, "Couldn't find reg, ret = %d\n", ret); + return ret; + } + pbs->base = val; + mutex_init(&pbs->lock); + + platform_set_drvdata(pdev, pbs); + + return 0; +} + +static const struct of_device_id qcom_pbs_match_table[] = { + { .compatible = "qcom,pbs" }, + {} +}; +MODULE_DEVICE_TABLE(of, qcom_pbs_match_table); + +static struct platform_driver qcom_pbs_driver = { + .driver = { + .name = "qcom-pbs", + .of_match_table = qcom_pbs_match_table, + }, + .probe = qcom_pbs_probe, +}; +module_platform_driver(qcom_pbs_driver) + +MODULE_DESCRIPTION("QCOM PBS DRIVER"); +MODULE_LICENSE("GPL"); diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c index aff0cfb714..ca2f6b7629 100644 --- a/drivers/soc/qcom/qcom_aoss.c +++ b/drivers/soc/qcom/qcom_aoss.c @@ -3,6 +3,7 @@ * Copyright (c) 2019, Linaro Ltd */ #include <linux/clk-provider.h> +#include <linux/debugfs.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/mailbox_client.h> @@ -13,6 +14,9 @@ #include <linux/slab.h> #include <linux/soc/qcom/qcom_aoss.h> +#define CREATE_TRACE_POINTS +#include "trace-aoss.h" + #define QMP_DESC_MAGIC 0x0 #define QMP_DESC_VERSION 0x4 #define QMP_DESC_FEATURES 0x8 @@ -44,6 +48,8 @@ #define QMP_NUM_COOLING_RESOURCES 2 +#define QMP_DEBUGFS_FILES 4 + static bool qmp_cdev_max_state = 1; struct qmp_cooling_device { @@ -65,6 +71,8 @@ struct qmp_cooling_device { * @tx_lock: provides synchronization between multiple callers of qmp_send() * @qdss_clk: QDSS clock hw struct * @cooling_devs: thermal cooling devices + * @debugfs_root: directory for the developer/tester interface + * @debugfs_files: array of individual debugfs entries under debugfs_root */ struct qmp { void __iomem *msgram; @@ -82,6 +90,8 @@ struct qmp { struct clk_hw qdss_clk; struct qmp_cooling_device *cooling_devs; + struct dentry *debugfs_root; + struct dentry *debugfs_files[QMP_DEBUGFS_FILES]; }; static void qmp_kick(struct qmp *qmp) @@ -214,7 +224,7 @@ static bool qmp_message_empty(struct qmp *qmp) * * Return: 0 on success, negative errno on failure */ -int qmp_send(struct qmp *qmp, const char *fmt, ...) +int __printf(2, 3) qmp_send(struct qmp *qmp, const char *fmt, ...) { char buf[QMP_MSG_LEN]; long time_left; @@ -235,6 +245,8 @@ int qmp_send(struct qmp *qmp, const char *fmt, ...) mutex_lock(&qmp->tx_lock); + trace_aoss_send(buf); + /* The message RAM only implements 32-bit accesses */ __iowrite32_copy(qmp->msgram + qmp->offset + sizeof(u32), buf, sizeof(buf) / sizeof(u32)); @@ -256,6 +268,8 @@ int qmp_send(struct qmp *qmp, const char *fmt, ...) ret = 0; } + trace_aoss_send_done(buf, ret); + mutex_unlock(&qmp->tx_lock); return ret; @@ -475,6 +489,91 @@ void qmp_put(struct qmp *qmp) } EXPORT_SYMBOL_GPL(qmp_put); +struct qmp_debugfs_entry { + const char *name; + const char *fmt; + bool is_bool; + const char *true_val; + const char *false_val; +}; + +static const struct qmp_debugfs_entry qmp_debugfs_entries[QMP_DEBUGFS_FILES] = { + { "ddr_frequency_mhz", "{class: ddr, res: fixed, val: %u}", false }, + { "prevent_aoss_sleep", "{class: aoss_slp, res: sleep: %s}", true, "enable", "disable" }, + { "prevent_cx_collapse", "{class: cx_mol, res: cx, val: %s}", true, "mol", "off" }, + { "prevent_ddr_collapse", "{class: ddr_mol, res: ddr, val: %s}", true, "mol", "off" }, +}; + +static ssize_t qmp_debugfs_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *pos) +{ + const struct qmp_debugfs_entry *entry = NULL; + struct qmp *qmp = file->private_data; + char buf[QMP_MSG_LEN]; + unsigned int uint_val; + const char *str_val; + bool bool_val; + int ret; + int i; + + for (i = 0; i < ARRAY_SIZE(qmp->debugfs_files); i++) { + if (qmp->debugfs_files[i] == file->f_path.dentry) { + entry = &qmp_debugfs_entries[i]; + break; + } + } + if (WARN_ON(!entry)) + return -EFAULT; + + if (entry->is_bool) { + ret = kstrtobool_from_user(user_buf, count, &bool_val); + if (ret) + return ret; + + str_val = bool_val ? entry->true_val : entry->false_val; + + ret = snprintf(buf, sizeof(buf), entry->fmt, str_val); + if (ret >= sizeof(buf)) + return -EINVAL; + } else { + ret = kstrtou32_from_user(user_buf, count, 0, &uint_val); + if (ret) + return ret; + + ret = snprintf(buf, sizeof(buf), entry->fmt, uint_val); + if (ret >= sizeof(buf)) + return -EINVAL; + } + + ret = qmp_send(qmp, buf); + if (ret < 0) + return ret; + + return count; +} + +static const struct file_operations qmp_debugfs_fops = { + .open = simple_open, + .write = qmp_debugfs_write, +}; + +static void qmp_debugfs_create(struct qmp *qmp) +{ + const struct qmp_debugfs_entry *entry; + int i; + + qmp->debugfs_root = debugfs_create_dir("qcom_aoss", NULL); + + for (i = 0; i < ARRAY_SIZE(qmp->debugfs_files); i++) { + entry = &qmp_debugfs_entries[i]; + + qmp->debugfs_files[i] = debugfs_create_file(entry->name, 0200, + qmp->debugfs_root, + qmp, + &qmp_debugfs_fops); + } +} + static int qmp_probe(struct platform_device *pdev) { struct qmp *qmp; @@ -523,6 +622,8 @@ static int qmp_probe(struct platform_device *pdev) platform_set_drvdata(pdev, qmp); + qmp_debugfs_create(qmp); + return 0; err_close_qmp: @@ -537,6 +638,8 @@ static void qmp_remove(struct platform_device *pdev) { struct qmp *qmp = platform_get_drvdata(pdev); + debugfs_remove_recursive(qmp->debugfs_root); + qmp_qdss_clk_remove(qmp); qmp_cooling_devices_remove(qmp); diff --git a/drivers/soc/qcom/qcom_stats.c b/drivers/soc/qcom/qcom_stats.c index 0216fc24f2..c429d5154a 100644 --- a/drivers/soc/qcom/qcom_stats.c +++ b/drivers/soc/qcom/qcom_stats.c @@ -35,11 +35,15 @@ static const struct subsystem_data subsystems[] = { { "wpss", 605, 13 }, { "adsp", 606, 2 }, { "cdsp", 607, 5 }, + { "cdsp1", 607, 12 }, + { "gpdsp0", 607, 17 }, + { "gpdsp1", 607, 18 }, { "slpi", 608, 3 }, { "gpu", 609, 0 }, { "display", 610, 0 }, { "adsp_island", 613, 2 }, { "slpi_island", 613, 3 }, + { "apss", 631, QCOM_SMEM_HOST_ANY }, }; struct stats_config { diff --git a/drivers/soc/qcom/rpm_master_stats.c b/drivers/soc/qcom/rpm_master_stats.c index 9ca13bcf67..086fe4ba67 100644 --- a/drivers/soc/qcom/rpm_master_stats.c +++ b/drivers/soc/qcom/rpm_master_stats.c @@ -148,6 +148,10 @@ static const struct of_device_id rpm_master_table[] = { { .compatible = "qcom,rpm-master-stats" }, { }, }; +/* + * No MODULE_DEVICE_TABLE intentionally: that's a debugging module, to be + * loaded manually only. + */ static struct platform_driver master_stats_driver = { .probe = master_stats_probe, diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index a021dc7180..de86009ecd 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved. */ #define pr_fmt(fmt) "%s " fmt, KBUILD_MODNAME @@ -557,7 +558,7 @@ static int check_for_req_inflight(struct rsc_drv *drv, struct tcs_group *tcs, for_each_set_bit(j, &curr_enabled, MAX_CMDS_PER_TCS) { addr = read_tcs_cmd(drv, drv->regs[RSC_DRV_CMD_ADDR], i, j); for (k = 0; k < msg->num_cmds; k++) { - if (addr == msg->cmds[k].addr) + if (cmd_db_match_resource_addr(msg->cmds[k].addr, addr)) return -EBUSY; } } @@ -645,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, @@ -669,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: @@ -1154,7 +1156,7 @@ static int __init rpmh_driver_init(void) { return platform_driver_register(&rpmh_driver); } -arch_initcall(rpmh_driver_init); +core_initcall(rpmh_driver_init); MODULE_DESCRIPTION("Qualcomm Technologies, Inc. RPMh Driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index 9f26d7f9b9..8903ed9563 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 690afc9a12..7191fa0c08 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -655,8 +655,6 @@ invalid_canary: void *qcom_smem_get(unsigned host, unsigned item, size_t *size) { struct smem_partition *part; - unsigned long flags; - int ret; void *ptr = ERR_PTR(-EPROBE_DEFER); if (!__smem) @@ -665,12 +663,6 @@ void *qcom_smem_get(unsigned host, unsigned item, size_t *size) if (WARN_ON(item >= __smem->item_count)) return ERR_PTR(-EINVAL); - ret = hwspin_lock_timeout_irqsave(__smem->hwlock, - HWSPINLOCK_TIMEOUT, - &flags); - if (ret) - return ERR_PTR(ret); - if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) { part = &__smem->partitions[host]; ptr = qcom_smem_get_private(__smem, part, item, size); @@ -681,10 +673,7 @@ void *qcom_smem_get(unsigned host, unsigned item, size_t *size) ptr = qcom_smem_get_global(__smem, item, size); } - hwspin_unlock_irqrestore(__smem->hwlock, &flags); - return ptr; - } EXPORT_SYMBOL_GPL(qcom_smem_get); diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c index 914b224614..a21241cbee 100644 --- a/drivers/soc/qcom/smp2p.c +++ b/drivers/soc/qcom/smp2p.c @@ -58,8 +58,8 @@ * @valid_entries: number of allocated entries * @flags: * @entries: individual communication entries - * @name: name of the entry - * @value: content of the entry + * @entries.name: name of the entry + * @entries.value: content of the entry */ struct smp2p_smem_item { u32 magic; @@ -275,6 +275,8 @@ static void qcom_smp2p_notify_in(struct qcom_smp2p *smp2p) * * Handle notifications from the remote side to handle newly allocated entries * or any changes to the state bits of existing entries. + * + * Return: %IRQ_HANDLED */ static irqreturn_t qcom_smp2p_intr(int irq, void *data) { diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index a980020ab8..41342c3791 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -133,6 +133,8 @@ static const char *const pmic_models[] = { [72] = "PMR735D", [73] = "PM8550", [74] = "PMK8550", + [82] = "PMC8380", + [83] = "SMB2360", }; struct socinfo_params { @@ -424,9 +426,13 @@ static const struct soc_id soc_id[] = { { qcom_board_id(IPQ9510) }, { qcom_board_id(QRB4210) }, { qcom_board_id(QRB2210) }, + { qcom_board_id(SM8475) }, + { qcom_board_id(SM8475P) }, { qcom_board_id(SA8775P) }, { qcom_board_id(QRU1000) }, + { qcom_board_id(SM8475_2) }, { qcom_board_id(QDU1000) }, + { qcom_board_id(X1E80100) }, { qcom_board_id(SM8650) }, { qcom_board_id(SM4450) }, { qcom_board_id(QDU1010) }, @@ -437,6 +443,8 @@ static const struct soc_id soc_id[] = { { qcom_board_id(IPQ5322) }, { qcom_board_id(IPQ5312) }, { qcom_board_id(IPQ5302) }, + { qcom_board_id(QCS8550) }, + { qcom_board_id(QCM8550) }, { qcom_board_id(IPQ5300) }, }; diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c index 2f0b1bfe76..06e2c4c2a4 100644 --- a/drivers/soc/qcom/spm.c +++ b/drivers/soc/qcom/spm.c @@ -6,20 +6,40 @@ * SAW power controller driver */ -#include <linux/kernel.h> +#include <linux/bitfield.h> +#include <linux/err.h> #include <linux/init.h> #include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/linear_range.h> #include <linux/module.h> -#include <linux/slab.h> #include <linux/of.h> -#include <linux/err.h> #include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/smp.h> + +#include <linux/regulator/driver.h> + #include <soc/qcom/spm.h> +#define FIELD_SET(current, mask, val) \ + (((current) & ~(mask)) | FIELD_PREP((mask), (val))) + #define SPM_CTL_INDEX 0x7f #define SPM_CTL_INDEX_SHIFT 4 #define SPM_CTL_EN BIT(0) +/* These registers might be specific to SPM 1.1 */ +#define SPM_VCTL_VLVL GENMASK(7, 0) +#define SPM_PMIC_DATA_0_VLVL GENMASK(7, 0) +#define SPM_PMIC_DATA_1_MIN_VSEL GENMASK(5, 0) +#define SPM_PMIC_DATA_1_MAX_VSEL GENMASK(21, 16) + +#define SPM_1_1_AVS_CTL_AVS_ENABLED BIT(27) +#define SPM_AVS_CTL_MAX_VLVL GENMASK(22, 17) +#define SPM_AVS_CTL_MIN_VLVL GENMASK(15, 10) + enum spm_reg { SPM_REG_CFG, SPM_REG_SPM_CTL, @@ -29,13 +49,44 @@ enum spm_reg { SPM_REG_PMIC_DATA_1, SPM_REG_VCTL, SPM_REG_SEQ_ENTRY, - SPM_REG_SPM_STS, + SPM_REG_STS0, + SPM_REG_STS1, SPM_REG_PMIC_STS, SPM_REG_AVS_CTL, SPM_REG_AVS_LIMIT, + SPM_REG_RST, SPM_REG_NR, }; +#define MAX_PMIC_DATA 2 +#define MAX_SEQ_DATA 64 + +struct spm_reg_data { + const u16 *reg_offset; + u32 spm_cfg; + u32 spm_dly; + u32 pmic_dly; + u32 pmic_data[MAX_PMIC_DATA]; + u32 avs_ctl; + u32 avs_limit; + u8 seq[MAX_SEQ_DATA]; + u8 start_index[PM_SLEEP_MODE_NR]; + + smp_call_func_t set_vdd; + /* for now we support only a single range */ + struct linear_range *range; + unsigned int ramp_delay; + unsigned int init_uV; +}; + +struct spm_driver_data { + void __iomem *reg_base; + const struct spm_reg_data *reg_data; + struct device *dev; + unsigned int volt_sel; + int reg_cpu; +}; + static const u16 spm_reg_offset_v4_1[SPM_REG_NR] = { [SPM_REG_AVS_CTL] = 0x904, [SPM_REG_AVS_LIMIT] = 0x908, @@ -169,6 +220,10 @@ static const struct spm_reg_data spm_reg_8226_cpu = { static const u16 spm_reg_offset_v1_1[SPM_REG_NR] = { [SPM_REG_CFG] = 0x08, + [SPM_REG_STS0] = 0x0c, + [SPM_REG_STS1] = 0x10, + [SPM_REG_VCTL] = 0x14, + [SPM_REG_AVS_CTL] = 0x18, [SPM_REG_SPM_CTL] = 0x20, [SPM_REG_PMIC_DLY] = 0x24, [SPM_REG_PMIC_DATA_0] = 0x28, @@ -176,7 +231,12 @@ static const u16 spm_reg_offset_v1_1[SPM_REG_NR] = { [SPM_REG_SEQ_ENTRY] = 0x80, }; +static void smp_set_vdd_v1_1(void *data); + /* SPM register data for 8064 */ +static struct linear_range spm_v1_1_regulator_range = + REGULATOR_LINEAR_RANGE(700000, 0, 56, 12500); + static const struct spm_reg_data spm_reg_8064_cpu = { .reg_offset = spm_reg_offset_v1_1, .spm_cfg = 0x1F, @@ -187,6 +247,10 @@ static const struct spm_reg_data spm_reg_8064_cpu = { 0x10, 0x54, 0x30, 0x0C, 0x24, 0x30, 0x0F }, .start_index[PM_SLEEP_MODE_STBY] = 0, .start_index[PM_SLEEP_MODE_SPC] = 2, + .set_vdd = smp_set_vdd_v1_1, + .range = &spm_v1_1_regulator_range, + .init_uV = 1300000, + .ramp_delay = 1250, }; static inline void spm_register_write(struct spm_driver_data *drv, @@ -238,6 +302,178 @@ void spm_set_low_power_mode(struct spm_driver_data *drv, spm_register_write_sync(drv, SPM_REG_SPM_CTL, ctl_val); } +static int spm_set_voltage_sel(struct regulator_dev *rdev, unsigned int selector) +{ + struct spm_driver_data *drv = rdev_get_drvdata(rdev); + + drv->volt_sel = selector; + + /* Always do the SAW register writes on the corresponding CPU */ + return smp_call_function_single(drv->reg_cpu, drv->reg_data->set_vdd, drv, true); +} + +static int spm_get_voltage_sel(struct regulator_dev *rdev) +{ + struct spm_driver_data *drv = rdev_get_drvdata(rdev); + + return drv->volt_sel; +} + +static const struct regulator_ops spm_reg_ops = { + .set_voltage_sel = spm_set_voltage_sel, + .get_voltage_sel = spm_get_voltage_sel, + .list_voltage = regulator_list_voltage_linear_range, + .set_voltage_time_sel = regulator_set_voltage_time_sel, +}; + +static void smp_set_vdd_v1_1(void *data) +{ + struct spm_driver_data *drv = data; + unsigned int vctl, data0, data1, avs_ctl, sts; + unsigned int vlevel, volt_sel; + bool avs_enabled; + + volt_sel = drv->volt_sel; + vlevel = volt_sel | 0x80; /* band */ + + avs_ctl = spm_register_read(drv, SPM_REG_AVS_CTL); + vctl = spm_register_read(drv, SPM_REG_VCTL); + data0 = spm_register_read(drv, SPM_REG_PMIC_DATA_0); + data1 = spm_register_read(drv, SPM_REG_PMIC_DATA_1); + + avs_enabled = avs_ctl & SPM_1_1_AVS_CTL_AVS_ENABLED; + + /* If AVS is enabled, switch it off during the voltage change */ + if (avs_enabled) { + avs_ctl &= ~SPM_1_1_AVS_CTL_AVS_ENABLED; + spm_register_write(drv, SPM_REG_AVS_CTL, avs_ctl); + } + + /* Kick the state machine back to idle */ + spm_register_write(drv, SPM_REG_RST, 1); + + vctl = FIELD_SET(vctl, SPM_VCTL_VLVL, vlevel); + data0 = FIELD_SET(data0, SPM_PMIC_DATA_0_VLVL, vlevel); + data1 = FIELD_SET(data1, SPM_PMIC_DATA_1_MIN_VSEL, volt_sel); + data1 = FIELD_SET(data1, SPM_PMIC_DATA_1_MAX_VSEL, volt_sel); + + spm_register_write(drv, SPM_REG_VCTL, vctl); + spm_register_write(drv, SPM_REG_PMIC_DATA_0, data0); + spm_register_write(drv, SPM_REG_PMIC_DATA_1, data1); + + if (read_poll_timeout_atomic(spm_register_read, + sts, sts == vlevel, + 1, 200, false, + drv, SPM_REG_STS1)) { + dev_err_ratelimited(drv->dev, "timeout setting the voltage (%x %x)!\n", sts, vlevel); + goto enable_avs; + } + + if (avs_enabled) { + unsigned int max_avs = volt_sel; + unsigned int min_avs = max(max_avs, 4U) - 4; + + avs_ctl = FIELD_SET(avs_ctl, SPM_AVS_CTL_MIN_VLVL, min_avs); + avs_ctl = FIELD_SET(avs_ctl, SPM_AVS_CTL_MAX_VLVL, max_avs); + spm_register_write(drv, SPM_REG_AVS_CTL, avs_ctl); + } + +enable_avs: + if (avs_enabled) { + avs_ctl |= SPM_1_1_AVS_CTL_AVS_ENABLED; + spm_register_write(drv, SPM_REG_AVS_CTL, avs_ctl); + } +} + +static int spm_get_cpu(struct device *dev) +{ + int cpu; + bool found; + + for_each_possible_cpu(cpu) { + struct device_node *cpu_node, *saw_node; + + cpu_node = of_cpu_device_node_get(cpu); + if (!cpu_node) + continue; + + saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0); + found = (saw_node == dev->of_node); + of_node_put(saw_node); + of_node_put(cpu_node); + + if (found) + return cpu; + } + + /* L2 SPM is not bound to any CPU, voltage setting is not supported */ + + return -EOPNOTSUPP; +} + +static int spm_register_regulator(struct device *dev, struct spm_driver_data *drv) +{ + struct regulator_config config = { + .dev = dev, + .driver_data = drv, + }; + struct regulator_desc *rdesc; + struct regulator_dev *rdev; + int ret; + bool found; + + if (!drv->reg_data->set_vdd) + return 0; + + rdesc = devm_kzalloc(dev, sizeof(*rdesc), GFP_KERNEL); + if (!rdesc) + return -ENOMEM; + + rdesc->name = "spm"; + rdesc->of_match = of_match_ptr("regulator"); + rdesc->type = REGULATOR_VOLTAGE; + rdesc->owner = THIS_MODULE; + rdesc->ops = &spm_reg_ops; + + rdesc->linear_ranges = drv->reg_data->range; + rdesc->n_linear_ranges = 1; + rdesc->n_voltages = rdesc->linear_ranges[rdesc->n_linear_ranges - 1].max_sel + 1; + rdesc->ramp_delay = drv->reg_data->ramp_delay; + + ret = spm_get_cpu(dev); + if (ret < 0) + return ret; + + drv->reg_cpu = ret; + dev_dbg(dev, "SAW2 bound to CPU %d\n", drv->reg_cpu); + + /* + * Program initial voltage, otherwise registration will also try + * setting the voltage, which might result in undervolting the CPU. + */ + drv->volt_sel = DIV_ROUND_UP(drv->reg_data->init_uV - rdesc->min_uV, + rdesc->uV_step); + ret = linear_range_get_selector_high(drv->reg_data->range, + drv->reg_data->init_uV, + &drv->volt_sel, + &found); + if (ret) { + dev_err(dev, "Initial uV value out of bounds\n"); + return ret; + } + + /* Always do the SAW register writes on the corresponding CPU */ + smp_call_function_single(drv->reg_cpu, drv->reg_data->set_vdd, drv, true); + + rdev = devm_regulator_register(dev, rdesc, &config); + if (IS_ERR(rdev)) { + dev_err(dev, "failed to register regulator\n"); + return PTR_ERR(rdev); + } + + return 0; +} + static const struct of_device_id spm_match_table[] = { { .compatible = "qcom,sdm660-gold-saw2-v4.1-l2", .data = &spm_reg_660_gold_l2 }, @@ -288,6 +524,7 @@ static int spm_dev_probe(struct platform_device *pdev) return -ENODEV; drv->reg_data = match_id->data; + drv->dev = &pdev->dev; platform_set_drvdata(pdev, drv); /* Write the SPM sequences first.. */ @@ -315,6 +552,9 @@ static int spm_dev_probe(struct platform_device *pdev) if (drv->reg_data->reg_offset[SPM_REG_SPM_CTL]) spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY); + if (IS_ENABLED(CONFIG_REGULATOR)) + return spm_register_regulator(&pdev->dev, drv); + return 0; } diff --git a/drivers/soc/qcom/trace-aoss.h b/drivers/soc/qcom/trace-aoss.h new file mode 100644 index 0000000000..fb5b0470c4 --- /dev/null +++ b/drivers/soc/qcom/trace-aoss.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM qcom_aoss + +#if !defined(_TRACE_QCOM_AOSS_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_QCOM_AOSS_H + +#include <linux/tracepoint.h> + +TRACE_EVENT(aoss_send, + TP_PROTO(const char *msg), + TP_ARGS(msg), + TP_STRUCT__entry( + __string(msg, msg) + ), + TP_fast_assign( + __assign_str(msg); + ), + TP_printk("%s", __get_str(msg)) +); + +TRACE_EVENT(aoss_send_done, + TP_PROTO(const char *msg, int ret), + TP_ARGS(msg, ret), + TP_STRUCT__entry( + __string(msg, msg) + __field(int, ret) + ), + TP_fast_assign( + __assign_str(msg); + __entry->ret = ret; + ), + TP_printk("%s: %d", __get_str(msg), __entry->ret) +); + +#endif /* _TRACE_QCOM_AOSS_H */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace-aoss + +#include <trace/define_trace.h> diff --git a/drivers/soc/qcom/trace-rpmh.h b/drivers/soc/qcom/trace-rpmh.h index be6b42ecc1..593ec1d4e0 100644 --- a/drivers/soc/qcom/trace-rpmh.h +++ b/drivers/soc/qcom/trace-rpmh.h @@ -26,7 +26,7 @@ TRACE_EVENT(rpmh_tx_done, ), TP_fast_assign( - __assign_str(name, d->name); + __assign_str(name); __entry->m = m; __entry->addr = r->cmds[0].addr; __entry->data = r->cmds[0].data; @@ -55,7 +55,7 @@ TRACE_EVENT(rpmh_send_msg, ), TP_fast_assign( - __assign_str(name, d->name); + __assign_str(name); __entry->m = m; __entry->state = state; __entry->n = n; diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 0986672f63..5d94c3f314 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -24,6 +24,7 @@ config ARCH_RCAR_GEN2 select RENESAS_IRQC select RST_RCAR select SYS_SUPPORTS_SH_CMT + select SYS_SUPPORTS_SH_TMU config ARCH_RCAR_GEN3 bool @@ -34,6 +35,10 @@ config ARCH_RCAR_GEN3 select SYS_SUPPORTS_SH_CMT select SYS_SUPPORTS_SH_TMU +config ARCH_RCAR_GEN4 + bool + select ARCH_RCAR_GEN3 + config ARCH_RMOBILE bool select PM @@ -240,7 +245,7 @@ config ARCH_R8A77961 config ARCH_R8A779F0 bool "ARM64 Platform support for R-Car S4-8" - select ARCH_RCAR_GEN3 + select ARCH_RCAR_GEN4 select SYSC_R8A779F0 help This enables support for the Renesas R-Car S4-8 SoC. @@ -261,18 +266,25 @@ config ARCH_R8A77970 config ARCH_R8A779A0 bool "ARM64 Platform support for R-Car V3U" - select ARCH_RCAR_GEN3 + select ARCH_RCAR_GEN4 select SYSC_R8A779A0 help This enables support for the Renesas R-Car V3U SoC. config ARCH_R8A779G0 bool "ARM64 Platform support for R-Car V4H" - select ARCH_RCAR_GEN3 + select ARCH_RCAR_GEN4 select SYSC_R8A779G0 help This enables support for the Renesas R-Car V4H SoC. +config ARCH_R8A779H0 + bool "ARM64 Platform support for R-Car V4M" + select ARCH_RCAR_GEN4 + select SYSC_R8A779H0 + help + This enables support for the Renesas R-Car V4M SoC. + config ARCH_R8A774C0 bool "ARM64 Platform support for RZ/G2E" select ARCH_RCAR_GEN3 @@ -333,6 +345,11 @@ config ARCH_R9A09G011 help This enables support for the Renesas RZ/V2M SoC. +config ARCH_R9A09G057 + bool "ARM64 Platform support for RZ/V2H(P)" + help + This enables support for the Renesas RZ/V2H(P) SoC variants. + endif # ARM64 if RISCV diff --git a/drivers/soc/renesas/rcar-rst.c b/drivers/soc/renesas/rcar-rst.c index 98fd97da6c..7ba02f3a4a 100644 --- a/drivers/soc/renesas/rcar-rst.c +++ b/drivers/soc/renesas/rcar-rst.c @@ -117,6 +117,7 @@ static const struct of_device_id rcar_rst_matches[] __initconst = { { .compatible = "renesas,r8a779a0-rst", .data = &rcar_rst_v3u }, { .compatible = "renesas,r8a779f0-rst", .data = &rcar_rst_gen4 }, { .compatible = "renesas,r8a779g0-rst", .data = &rcar_rst_gen4 }, + { .compatible = "renesas,r8a779h0-rst", .data = &rcar_rst_gen4 }, { /* sentinel */ } }; diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index 27eae1a354..172d59e6fb 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -75,6 +75,10 @@ static const struct renesas_family fam_rzg3s __initconst __maybe_unused = { .name = "RZ/G3S", }; +static const struct renesas_family fam_rzv2h __initconst __maybe_unused = { + .name = "RZ/V2H", +}; + static const struct renesas_family fam_rzv2l __initconst __maybe_unused = { .name = "RZ/V2L", }; @@ -177,6 +181,11 @@ static const struct renesas_soc soc_rz_g3s __initconst __maybe_unused = { .id = 0x85e0447, }; +static const struct renesas_soc soc_rz_v2h __initconst __maybe_unused = { + .family = &fam_rzv2h, + .id = 0x847a447, +}; + static const struct renesas_soc soc_rz_v2l __initconst __maybe_unused = { .family = &fam_rzv2l, .id = 0x8447447, @@ -270,6 +279,11 @@ static const struct renesas_soc soc_rcar_v4h __initconst __maybe_unused = { .id = 0x5c, }; +static const struct renesas_soc soc_rcar_v4m __initconst __maybe_unused = { + .family = &fam_rcar_gen4, + .id = 0x5d, +}; + static const struct renesas_soc soc_shmobile_ag5 __initconst __maybe_unused = { .family = &fam_shmobile, .id = 0x37, @@ -380,6 +394,9 @@ static const struct of_device_id renesas_socs[] __initconst __maybe_unused = { #ifdef CONFIG_ARCH_R8A779G0 { .compatible = "renesas,r8a779g0", .data = &soc_rcar_v4h }, #endif +#ifdef CONFIG_ARCH_R8A779H0 + { .compatible = "renesas,r8a779h0", .data = &soc_rcar_v4m }, +#endif #ifdef CONFIG_ARCH_R9A07G043 #ifdef CONFIG_RISCV { .compatible = "renesas,r9a07g043", .data = &soc_rz_five }, @@ -399,6 +416,9 @@ static const struct of_device_id renesas_socs[] __initconst __maybe_unused = { #ifdef CONFIG_ARCH_R9A09G011 { .compatible = "renesas,r9a09g011", .data = &soc_rz_v2m }, #endif +#ifdef CONFIG_ARCH_R9A09G057 + { .compatible = "renesas,r9a09g057", .data = &soc_rz_v2h }, +#endif #ifdef CONFIG_ARCH_SH73A0 { .compatible = "renesas,sh73a0", .data = &soc_shmobile_ag5 }, #endif @@ -424,6 +444,11 @@ static const struct renesas_id id_rzg2l __initconst = { .mask = 0xfffffff, }; +static const struct renesas_id id_rzv2h __initconst = { + .offset = 0x304, + .mask = 0xfffffff, +}; + static const struct renesas_id id_rzv2m __initconst = { .offset = 0x104, .mask = 0xff, @@ -441,6 +466,7 @@ static const struct of_device_id renesas_ids[] __initconst = { { .compatible = "renesas,r9a07g054-sysc", .data = &id_rzg2l }, { .compatible = "renesas,r9a08g045-sysc", .data = &id_rzg2l }, { .compatible = "renesas,r9a09g011-sys", .data = &id_rzv2m }, + { .compatible = "renesas,r9a09g057-sys", .data = &id_rzv2h }, { .compatible = "renesas,prr", .data = &id_prr }, { /* sentinel */ } }; @@ -505,7 +531,7 @@ static int __init renesas_soc_init(void) eslo = product & 0xf; soc_dev_attr->revision = kasprintf(GFP_KERNEL, "ES%u.%u", eshi, eslo); - } else if (id == &id_rzg2l) { + } else if (id == &id_rzg2l || id == &id_rzv2h) { eshi = ((product >> 28) & 0x0f); soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%u", eshi); diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig index 27ec99af77..1a5dfdc978 100644 --- a/drivers/soc/samsung/Kconfig +++ b/drivers/soc/samsung/Kconfig @@ -42,6 +42,7 @@ config EXYNOS_PMU depends on ARCH_EXYNOS || ((ARM || ARM64) && COMPILE_TEST) select EXYNOS_PMU_ARM_DRIVERS if ARM && ARCH_EXYNOS select MFD_CORE + select REGMAP_MMIO # There is no need to enable these drivers for ARMv8 config EXYNOS_PMU_ARM_DRIVERS diff --git a/drivers/soc/samsung/exynos-asv.c b/drivers/soc/samsung/exynos-asv.c index d60af8acc3..97006cc3b9 100644 --- a/drivers/soc/samsung/exynos-asv.c +++ b/drivers/soc/samsung/exynos-asv.c @@ -11,6 +11,7 @@ #include <linux/cpu.h> #include <linux/device.h> +#include <linux/energy_model.h> #include <linux/errno.h> #include <linux/of.h> #include <linux/pm_opp.h> @@ -97,9 +98,16 @@ static int exynos_asv_update_opps(struct exynos_asv *asv) last_opp_table = opp_table; ret = exynos_asv_update_cpu_opps(asv, cpu); - if (ret < 0) + if (!ret) { + /* + * Update EM power values since OPP + * voltage values may have changed. + */ + em_dev_update_chip_binning(cpu); + } else { dev_err(asv->dev, "Couldn't udate OPPs for cpu%d\n", cpuid); + } } dev_pm_opp_put_opp_table(opp_table); diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c index 250537d7cf..fd8b6ac066 100644 --- a/drivers/soc/samsung/exynos-pmu.c +++ b/drivers/soc/samsung/exynos-pmu.c @@ -5,6 +5,7 @@ // // Exynos - CPU PMU(Power Management Unit) support +#include <linux/arm-smccc.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/mfd/core.h> @@ -12,19 +13,134 @@ #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/delay.h> +#include <linux/regmap.h> #include <linux/soc/samsung/exynos-regs-pmu.h> #include <linux/soc/samsung/exynos-pmu.h> #include "exynos-pmu.h" +#define PMUALIVE_MASK GENMASK(13, 0) +#define TENSOR_SET_BITS (BIT(15) | BIT(14)) +#define TENSOR_CLR_BITS BIT(15) +#define TENSOR_SMC_PMU_SEC_REG 0x82000504 +#define TENSOR_PMUREG_READ 0 +#define TENSOR_PMUREG_WRITE 1 +#define TENSOR_PMUREG_RMW 2 + struct exynos_pmu_context { struct device *dev; const struct exynos_pmu_data *pmu_data; + struct regmap *pmureg; }; void __iomem *pmu_base_addr; static struct exynos_pmu_context *pmu_context; +/* forward declaration */ +static struct platform_driver exynos_pmu_driver; + +/* + * Tensor SoCs are configured so that PMU_ALIVE registers can only be written + * from EL3, but are still read accessible. As Linux needs to write some of + * these registers, the following functions are provided and exposed via + * regmap. + * + * Note: This SMC interface is known to be implemented on gs101 and derivative + * SoCs. + */ + +/* Write to a protected PMU register. */ +static int tensor_sec_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct arm_smccc_res res; + unsigned long pmu_base = (unsigned long)context; + + arm_smccc_smc(TENSOR_SMC_PMU_SEC_REG, pmu_base + reg, + TENSOR_PMUREG_WRITE, val, 0, 0, 0, 0, &res); + + /* returns -EINVAL if access isn't allowed or 0 */ + if (res.a0) + pr_warn("%s(): SMC failed: %d\n", __func__, (int)res.a0); + + return (int)res.a0; +} + +/* Read/Modify/Write a protected PMU register. */ +static int tensor_sec_reg_rmw(void *context, unsigned int reg, + unsigned int mask, unsigned int val) +{ + struct arm_smccc_res res; + unsigned long pmu_base = (unsigned long)context; + + arm_smccc_smc(TENSOR_SMC_PMU_SEC_REG, pmu_base + reg, + TENSOR_PMUREG_RMW, mask, val, 0, 0, 0, &res); + + /* returns -EINVAL if access isn't allowed or 0 */ + if (res.a0) + pr_warn("%s(): SMC failed: %d\n", __func__, (int)res.a0); + + return (int)res.a0; +} + +/* + * Read a protected PMU register. All PMU registers can be read by Linux. + * Note: The SMC read register is not used, as only registers that can be + * written are readable via SMC. + */ +static int tensor_sec_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + *val = pmu_raw_readl(reg); + return 0; +} + +/* + * For SoCs that have set/clear bit hardware this function can be used when + * the PMU register will be accessed by multiple masters. + * + * For example, to set bits 13:8 in PMU reg offset 0x3e80 + * tensor_set_bits_atomic(ctx, 0x3e80, 0x3f00, 0x3f00); + * + * Set bit 8, and clear bits 13:9 PMU reg offset 0x3e80 + * tensor_set_bits_atomic(0x3e80, 0x100, 0x3f00); + */ +static int tensor_set_bits_atomic(void *ctx, unsigned int offset, u32 val, + u32 mask) +{ + int ret; + unsigned int i; + + for (i = 0; i < 32; i++) { + if (!(mask & BIT(i))) + continue; + + offset &= ~TENSOR_SET_BITS; + + if (val & BIT(i)) + offset |= TENSOR_SET_BITS; + else + offset |= TENSOR_CLR_BITS; + + ret = tensor_sec_reg_write(ctx, offset, i); + if (ret) + return ret; + } + return ret; +} + +static int tensor_sec_update_bits(void *ctx, unsigned int reg, + unsigned int mask, unsigned int val) +{ + /* + * Use atomic operations for PMU_ALIVE registers (offset 0~0x3FFF) + * as the target registers can be accessed by multiple masters. + */ + if (reg > PMUALIVE_MASK) + return tensor_sec_reg_rmw(ctx, reg, mask, val); + + return tensor_set_bits_atomic(ctx, reg, val, mask); +} void pmu_raw_writel(u32 val, u32 offset) { @@ -75,11 +191,41 @@ void exynos_sys_powerdown_conf(enum sys_powerdown mode) #define exynos_pmu_data_arm_ptr(data) NULL #endif +static const struct regmap_config regmap_smccfg = { + .name = "pmu_regs", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .use_single_read = true, + .use_single_write = true, + .reg_read = tensor_sec_reg_read, + .reg_write = tensor_sec_reg_write, + .reg_update_bits = tensor_sec_update_bits, +}; + +static const struct regmap_config regmap_mmiocfg = { + .name = "pmu_regs", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .use_single_read = true, + .use_single_write = true, +}; + +static const struct exynos_pmu_data gs101_pmu_data = { + .pmu_secure = true +}; + /* * PMU platform driver and devicetree bindings. */ static const struct of_device_id exynos_pmu_of_device_ids[] = { { + .compatible = "google,gs101-pmu", + .data = &gs101_pmu_data, + }, { .compatible = "samsung,exynos3250-pmu", .data = exynos_pmu_data_arm_ptr(exynos3250_pmu_data), }, { @@ -113,19 +259,75 @@ static const struct mfd_cell exynos_pmu_devs[] = { { .name = "exynos-clkout", }, }; +/** + * exynos_get_pmu_regmap() - Obtain pmureg regmap + * + * Find the pmureg regmap previously configured in probe() and return regmap + * pointer. + * + * Return: A pointer to regmap if found or ERR_PTR error value. + */ struct regmap *exynos_get_pmu_regmap(void) { struct device_node *np = of_find_matching_node(NULL, exynos_pmu_of_device_ids); if (np) - return syscon_node_to_regmap(np); + return exynos_get_pmu_regmap_by_phandle(np, NULL); return ERR_PTR(-ENODEV); } EXPORT_SYMBOL_GPL(exynos_get_pmu_regmap); +/** + * exynos_get_pmu_regmap_by_phandle() - Obtain pmureg regmap via phandle + * @np: Device node holding PMU phandle property + * @propname: Name of property holding phandle value + * + * Find the pmureg regmap previously configured in probe() and return regmap + * pointer. + * + * Return: A pointer to regmap if found or ERR_PTR error value. + */ +struct regmap *exynos_get_pmu_regmap_by_phandle(struct device_node *np, + const char *propname) +{ + struct exynos_pmu_context *ctx; + struct device_node *pmu_np; + struct device *dev; + + if (propname) + pmu_np = of_parse_phandle(np, propname, 0); + else + pmu_np = np; + + if (!pmu_np) + return ERR_PTR(-ENODEV); + + /* + * Determine if exynos-pmu device has probed and therefore regmap + * has been created and can be returned to the caller. Otherwise we + * return -EPROBE_DEFER. + */ + dev = driver_find_device_by_of_node(&exynos_pmu_driver.driver, + (void *)pmu_np); + + if (propname) + of_node_put(pmu_np); + + if (!dev) + return ERR_PTR(-EPROBE_DEFER); + + ctx = dev_get_drvdata(dev); + + return ctx->pmureg; +} +EXPORT_SYMBOL_GPL(exynos_get_pmu_regmap_by_phandle); + static int exynos_pmu_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct regmap_config pmu_regmcfg; + struct regmap *regmap; + struct resource *res; int ret; pmu_base_addr = devm_platform_ioremap_resource(pdev, 0); @@ -137,9 +339,38 @@ static int exynos_pmu_probe(struct platform_device *pdev) GFP_KERNEL); if (!pmu_context) return -ENOMEM; - pmu_context->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + pmu_context->pmu_data = of_device_get_match_data(dev); + /* For SoCs that secure PMU register writes use custom regmap */ + if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_secure) { + pmu_regmcfg = regmap_smccfg; + pmu_regmcfg.max_register = resource_size(res) - + pmu_regmcfg.reg_stride; + /* Need physical address for SMC call */ + regmap = devm_regmap_init(dev, NULL, + (void *)(uintptr_t)res->start, + &pmu_regmcfg); + } else { + /* All other SoCs use a MMIO regmap */ + pmu_regmcfg = regmap_mmiocfg; + pmu_regmcfg.max_register = resource_size(res) - + pmu_regmcfg.reg_stride; + regmap = devm_regmap_init_mmio(dev, pmu_base_addr, + &pmu_regmcfg); + } + + if (IS_ERR(regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(regmap), + "regmap init failed\n"); + + pmu_context->pmureg = regmap; + pmu_context->dev = dev; + if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_init) pmu_context->pmu_data->pmu_init(); diff --git a/drivers/soc/samsung/exynos-pmu.h b/drivers/soc/samsung/exynos-pmu.h index 1c652ffd79..0a49a2c9a0 100644 --- a/drivers/soc/samsung/exynos-pmu.h +++ b/drivers/soc/samsung/exynos-pmu.h @@ -21,6 +21,7 @@ struct exynos_pmu_conf { struct exynos_pmu_data { const struct exynos_pmu_conf *pmu_config; const struct exynos_pmu_conf *pmu_config_extra; + bool pmu_secure; void (*pmu_init)(void); void (*powerdown_conf)(enum sys_powerdown); diff --git a/drivers/soc/sunxi/sunxi_sram.c b/drivers/soc/sunxi/sunxi_sram.c index 4458b2e056..6eb6cf0627 100644 --- a/drivers/soc/sunxi/sunxi_sram.c +++ b/drivers/soc/sunxi/sunxi_sram.c @@ -287,6 +287,7 @@ EXPORT_SYMBOL(sunxi_sram_release); struct sunxi_sramc_variant { int num_emac_clocks; bool has_ldo_ctrl; + bool has_ths_offset; }; static const struct sunxi_sramc_variant sun4i_a10_sramc_variant = { @@ -308,8 +309,10 @@ static const struct sunxi_sramc_variant sun50i_a64_sramc_variant = { static const struct sunxi_sramc_variant sun50i_h616_sramc_variant = { .num_emac_clocks = 2, + .has_ths_offset = true, }; +#define SUNXI_SRAM_THS_OFFSET_REG 0x0 #define SUNXI_SRAM_EMAC_CLOCK_REG 0x30 #define SUNXI_SYS_LDO_CTRL_REG 0x150 @@ -318,6 +321,8 @@ static bool sunxi_sram_regmap_accessible_reg(struct device *dev, { const struct sunxi_sramc_variant *variant = dev_get_drvdata(dev); + if (reg == SUNXI_SRAM_THS_OFFSET_REG && variant->has_ths_offset) + return true; if (reg >= SUNXI_SRAM_EMAC_CLOCK_REG && reg < SUNXI_SRAM_EMAC_CLOCK_REG + variant->num_emac_clocks * 4) return true; @@ -327,6 +332,20 @@ static bool sunxi_sram_regmap_accessible_reg(struct device *dev, return false; } +static void sunxi_sram_lock(void *_lock) +{ + spinlock_t *lock = _lock; + + spin_lock(lock); +} + +static void sunxi_sram_unlock(void *_lock) +{ + spinlock_t *lock = _lock; + + spin_unlock(lock); +} + static struct regmap_config sunxi_sram_regmap_config = { .reg_bits = 32, .val_bits = 32, @@ -336,6 +355,9 @@ static struct regmap_config sunxi_sram_regmap_config = { /* other devices have no business accessing other registers */ .readable_reg = sunxi_sram_regmap_accessible_reg, .writeable_reg = sunxi_sram_regmap_accessible_reg, + .lock = sunxi_sram_lock, + .unlock = sunxi_sram_unlock, + .lock_arg = &sram_lock, }; static int __init sunxi_sram_probe(struct platform_device *pdev) diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig index f16beeabaa..33512558af 100644 --- a/drivers/soc/tegra/Kconfig +++ b/drivers/soc/tegra/Kconfig @@ -133,6 +133,11 @@ config ARCH_TEGRA_234_SOC help Enable support for the NVIDIA Tegra234 SoC. +config ARCH_TEGRA_241_SOC + bool "NVIDIA Tegra241 SoC" + help + Enable support for the NVIDIA Tegra241 SoC. + endif endif diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index a2c28f493a..d276672838 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -3,11 +3,13 @@ * Copyright (c) 2013-2023, NVIDIA CORPORATION. All rights reserved. */ +#include <linux/acpi.h> #include <linux/clk.h> #include <linux/device.h> #include <linux/kobject.h> #include <linux/init.h> #include <linux/io.h> +#include <linux/mod_devicetable.h> #include <linux/nvmem-consumer.h> #include <linux/nvmem-provider.h> #include <linux/of.h> @@ -113,6 +115,28 @@ static void tegra_fuse_restore(void *base) fuse->clk = NULL; } +static void tegra_fuse_print_sku_info(struct tegra_sku_info *tegra_sku_info) +{ + pr_info("Tegra Revision: %s SKU: %d CPU Process: %d SoC Process: %d\n", + tegra_revision_name[tegra_sku_info->revision], + tegra_sku_info->sku_id, tegra_sku_info->cpu_process_id, + tegra_sku_info->soc_process_id); + pr_debug("Tegra CPU Speedo ID %d, SoC Speedo ID %d\n", + tegra_sku_info->cpu_speedo_id, tegra_sku_info->soc_speedo_id); +} + +static int tegra_fuse_add_lookups(struct tegra_fuse *fuse) +{ + fuse->lookups = kmemdup_array(fuse->soc->lookups, fuse->soc->num_lookups, + sizeof(*fuse->lookups), GFP_KERNEL); + if (!fuse->lookups) + return -ENOMEM; + + nvmem_add_cell_lookups(fuse->lookups, fuse->soc->num_lookups); + + return 0; +} + static int tegra_fuse_probe(struct platform_device *pdev) { void __iomem *base = fuse->base; @@ -130,15 +154,46 @@ static int tegra_fuse_probe(struct platform_device *pdev) return PTR_ERR(fuse->base); fuse->phys = res->start; - fuse->clk = devm_clk_get(&pdev->dev, "fuse"); - if (IS_ERR(fuse->clk)) { - if (PTR_ERR(fuse->clk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "failed to get FUSE clock: %ld", - PTR_ERR(fuse->clk)); + /* Initialize the soc data and lookups if using ACPI boot. */ + if (is_acpi_node(dev_fwnode(&pdev->dev)) && !fuse->soc) { + u8 chip; - return PTR_ERR(fuse->clk); + tegra_acpi_init_apbmisc(); + + chip = tegra_get_chip_id(); + switch (chip) { +#if defined(CONFIG_ARCH_TEGRA_194_SOC) + case TEGRA194: + fuse->soc = &tegra194_fuse_soc; + break; +#endif +#if defined(CONFIG_ARCH_TEGRA_234_SOC) + case TEGRA234: + fuse->soc = &tegra234_fuse_soc; + break; +#endif +#if defined(CONFIG_ARCH_TEGRA_241_SOC) + case TEGRA241: + fuse->soc = &tegra241_fuse_soc; + break; +#endif + default: + return dev_err_probe(&pdev->dev, -EINVAL, "Unsupported SoC: %02x\n", chip); + } + + fuse->soc->init(fuse); + tegra_fuse_print_sku_info(&tegra_sku_info); + tegra_soc_device_register(); + + err = tegra_fuse_add_lookups(fuse); + if (err) + return dev_err_probe(&pdev->dev, err, "failed to add FUSE lookups\n"); } + fuse->clk = devm_clk_get_optional(&pdev->dev, "fuse"); + if (IS_ERR(fuse->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(fuse->clk), "failed to get FUSE clock\n"); + platform_set_drvdata(pdev, fuse); fuse->dev = &pdev->dev; @@ -179,12 +234,8 @@ static int tegra_fuse_probe(struct platform_device *pdev) } fuse->rst = devm_reset_control_get_optional(&pdev->dev, "fuse"); - if (IS_ERR(fuse->rst)) { - err = PTR_ERR(fuse->rst); - dev_err(&pdev->dev, "failed to get FUSE reset: %pe\n", - fuse->rst); - return err; - } + if (IS_ERR(fuse->rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(fuse->rst), "failed to get FUSE reset\n"); /* * FUSE clock is enabled at a boot time, hence this resume/suspend @@ -262,10 +313,17 @@ static const struct dev_pm_ops tegra_fuse_pm = { SET_SYSTEM_SLEEP_PM_OPS(tegra_fuse_suspend, tegra_fuse_resume) }; +static const struct acpi_device_id tegra_fuse_acpi_match[] = { + { "NVDA200F" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(acpi, tegra_fuse_acpi_match); + static struct platform_driver tegra_fuse_driver = { .driver = { .name = "tegra-fuse", .of_match_table = tegra_fuse_match, + .acpi_match_table = tegra_fuse_acpi_match, .pm = &tegra_fuse_pm, .suppress_bind_attrs = true, }, @@ -287,7 +345,16 @@ u32 __init tegra_fuse_read_early(unsigned int offset) int tegra_fuse_readl(unsigned long offset, u32 *value) { - if (!fuse->read || !fuse->clk) + if (!fuse->dev) + return -EPROBE_DEFER; + + /* + * Wait for fuse->clk to be initialized if device-tree boot is used. + */ + if (is_of_node(dev_fwnode(fuse->dev)) && !fuse->clk) + return -EPROBE_DEFER; + + if (!fuse->read) return -EPROBE_DEFER; if (IS_ERR(fuse->clk)) @@ -343,7 +410,8 @@ const struct attribute_group tegra_soc_attr_group = { }; #if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) || \ - IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) + IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) || \ + IS_ENABLED(CONFIG_ARCH_TEGRA_241_SOC) static ssize_t platform_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -370,7 +438,7 @@ const struct attribute_group tegra194_soc_attr_group = { }; #endif -struct device * __init tegra_soc_device_register(void) +struct device *tegra_soc_device_register(void) { struct soc_device_attribute *attr; struct soc_device *dev; @@ -407,6 +475,7 @@ static int __init tegra_init_fuse(void) const struct of_device_id *match; struct device_node *np; struct resource regs; + int err; tegra_init_apbmisc(); @@ -497,22 +566,13 @@ static int __init tegra_init_fuse(void) fuse->soc->init(fuse); - pr_info("Tegra Revision: %s SKU: %d CPU Process: %d SoC Process: %d\n", - tegra_revision_name[tegra_sku_info.revision], - tegra_sku_info.sku_id, tegra_sku_info.cpu_process_id, - tegra_sku_info.soc_process_id); - pr_debug("Tegra CPU Speedo ID %d, SoC Speedo ID %d\n", - tegra_sku_info.cpu_speedo_id, tegra_sku_info.soc_speedo_id); + tegra_fuse_print_sku_info(&tegra_sku_info); - if (fuse->soc->lookups) { - size_t size = sizeof(*fuse->lookups) * fuse->soc->num_lookups; - - fuse->lookups = kmemdup(fuse->soc->lookups, size, GFP_KERNEL); - if (fuse->lookups) - nvmem_add_cell_lookups(fuse->lookups, fuse->soc->num_lookups); - } + err = tegra_fuse_add_lookups(fuse); + if (err) + pr_err("failed to add FUSE lookups\n"); - return 0; + return err; } early_initcall(tegra_init_fuse); diff --git a/drivers/soc/tegra/fuse/fuse-tegra30.c b/drivers/soc/tegra/fuse/fuse-tegra30.c index e94d46372a..eb14e5ff5a 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra30.c +++ b/drivers/soc/tegra/fuse/fuse-tegra30.c @@ -38,7 +38,8 @@ defined(CONFIG_ARCH_TEGRA_210_SOC) || \ defined(CONFIG_ARCH_TEGRA_186_SOC) || \ defined(CONFIG_ARCH_TEGRA_194_SOC) || \ - defined(CONFIG_ARCH_TEGRA_234_SOC) + defined(CONFIG_ARCH_TEGRA_234_SOC) || \ + defined(CONFIG_ARCH_TEGRA_241_SOC) static u32 tegra30_fuse_read_early(struct tegra_fuse *fuse, unsigned int offset) { if (WARN_ON(!fuse->base)) @@ -678,3 +679,23 @@ const struct tegra_fuse_soc tegra234_fuse_soc = { .clk_suspend_on = false, }; #endif + +#if defined(CONFIG_ARCH_TEGRA_241_SOC) +static const struct tegra_fuse_info tegra241_fuse_info = { + .read = tegra30_fuse_read, + .size = 0x16008, + .spare = 0xcf0, +}; + +static const struct nvmem_keepout tegra241_fuse_keepouts[] = { + { .start = 0xc, .end = 0x1600c } +}; + +const struct tegra_fuse_soc tegra241_fuse_soc = { + .init = tegra30_fuse_init, + .info = &tegra241_fuse_info, + .keepouts = tegra241_fuse_keepouts, + .num_keepouts = ARRAY_SIZE(tegra241_fuse_keepouts), + .soc_attr_group = &tegra194_soc_attr_group, +}; +#endif diff --git a/drivers/soc/tegra/fuse/fuse.h b/drivers/soc/tegra/fuse/fuse.h index 90f23be738..9fee6ad6ad 100644 --- a/drivers/soc/tegra/fuse/fuse.h +++ b/drivers/soc/tegra/fuse/fuse.h @@ -69,6 +69,7 @@ struct tegra_fuse { void tegra_init_revision(void); void tegra_init_apbmisc(void); +void tegra_acpi_init_apbmisc(void); u32 __init tegra_fuse_read_spare(unsigned int spare); u32 __init tegra_fuse_read_early(unsigned int offset); @@ -123,7 +124,8 @@ extern const struct tegra_fuse_soc tegra186_fuse_soc; #endif #if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) || \ - IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) + IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) || \ + IS_ENABLED(CONFIG_ARCH_TEGRA_241_SOC) extern const struct attribute_group tegra194_soc_attr_group; #endif @@ -135,4 +137,8 @@ extern const struct tegra_fuse_soc tegra194_fuse_soc; extern const struct tegra_fuse_soc tegra234_fuse_soc; #endif +#ifdef CONFIG_ARCH_TEGRA_241_SOC +extern const struct tegra_fuse_soc tegra241_fuse_soc; +#endif + #endif diff --git a/drivers/soc/tegra/fuse/tegra-apbmisc.c b/drivers/soc/tegra/fuse/tegra-apbmisc.c index da970f3dbf..e2ca5d55fd 100644 --- a/drivers/soc/tegra/fuse/tegra-apbmisc.c +++ b/drivers/soc/tegra/fuse/tegra-apbmisc.c @@ -3,9 +3,11 @@ * Copyright (c) 2014-2023, NVIDIA CORPORATION. All rights reserved. */ +#include <linux/acpi.h> #include <linux/export.h> #include <linux/io.h> #include <linux/kernel.h> +#include <linux/mod_devicetable.h> #include <linux/of.h> #include <linux/of_address.h> @@ -62,6 +64,7 @@ bool tegra_is_silicon(void) switch (tegra_get_chip_id()) { case TEGRA194: case TEGRA234: + case TEGRA241: case TEGRA264: if (tegra_get_platform() == 0) return true; @@ -160,9 +163,34 @@ void __init tegra_init_revision(void) tegra_sku_info.platform = tegra_get_platform(); } -void __init tegra_init_apbmisc(void) +static void tegra_init_apbmisc_resources(struct resource *apbmisc, + struct resource *straps) { void __iomem *strapping_base; + + apbmisc_base = ioremap(apbmisc->start, resource_size(apbmisc)); + if (apbmisc_base) + chipid = readl_relaxed(apbmisc_base + 4); + else + pr_err("failed to map APBMISC registers\n"); + + strapping_base = ioremap(straps->start, resource_size(straps)); + if (strapping_base) { + strapping = readl_relaxed(strapping_base); + iounmap(strapping_base); + } else { + pr_err("failed to map strapping options registers\n"); + } +} + +/** + * tegra_init_apbmisc - Initializes Tegra APBMISC and Strapping registers. + * + * This is called during early init as some of the old 32-bit ARM code needs + * information from the APBMISC registers very early during boot. + */ +void __init tegra_init_apbmisc(void) +{ struct resource apbmisc, straps; struct device_node *np; @@ -219,23 +247,73 @@ void __init tegra_init_apbmisc(void) } } - apbmisc_base = ioremap(apbmisc.start, resource_size(&apbmisc)); - if (!apbmisc_base) { - pr_err("failed to map APBMISC registers\n"); - } else { - chipid = readl_relaxed(apbmisc_base + 4); + tegra_init_apbmisc_resources(&apbmisc, &straps); + long_ram_code = of_property_read_bool(np, "nvidia,long-ram-code"); + +put: + of_node_put(np); +} + +#ifdef CONFIG_ACPI +static const struct acpi_device_id apbmisc_acpi_match[] = { + { "NVDA2010" }, + { /* sentinel */ } +}; + +void tegra_acpi_init_apbmisc(void) +{ + struct resource *resources[2] = { NULL }; + struct resource_entry *rentry; + struct acpi_device *adev = NULL; + struct list_head resource_list; + int rcount = 0; + int ret; + + adev = acpi_dev_get_first_match_dev(apbmisc_acpi_match[0].id, NULL, -1); + if (!adev) + return; + + INIT_LIST_HEAD(&resource_list); + + ret = acpi_dev_get_memory_resources(adev, &resource_list); + if (ret < 0) { + pr_err("failed to get APBMISC memory resources"); + goto out_put_acpi_dev; } - strapping_base = ioremap(straps.start, resource_size(&straps)); - if (!strapping_base) { - pr_err("failed to map strapping options registers\n"); - } else { - strapping = readl_relaxed(strapping_base); - iounmap(strapping_base); + /* + * Get required memory resources. + * + * resources[0]: apbmisc. + * resources[1]: straps. + */ + resource_list_for_each_entry(rentry, &resource_list) { + if (rcount >= ARRAY_SIZE(resources)) + break; + + resources[rcount++] = rentry->res; } - long_ram_code = of_property_read_bool(np, "nvidia,long-ram-code"); + if (!resources[0]) { + pr_err("failed to get APBMISC registers\n"); + goto out_free_resource_list; + } -put: - of_node_put(np); + if (!resources[1]) { + pr_err("failed to get strapping options registers\n"); + goto out_free_resource_list; + } + + tegra_init_apbmisc_resources(resources[0], resources[1]); + +out_free_resource_list: + acpi_dev_free_resource_list(&resource_list); + +out_put_acpi_dev: + acpi_dev_put(adev); +} +#else +void tegra_acpi_init_apbmisc(void) +{ } +#endif diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index f432aa022a..91d0ad6dde 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -3,7 +3,7 @@ * drivers/soc/tegra/pmc.c * * Copyright (c) 2010 Google, Inc - * Copyright (c) 2018-2023, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved. * * Author: * Colin Cross <ccross@google.com> @@ -384,6 +384,7 @@ struct tegra_pmc_soc { bool has_blink_output; bool has_usb_sleepwalk; bool supports_core_domain; + bool has_single_mmio_aperture; }; /** @@ -1777,30 +1778,6 @@ static int tegra_io_pad_get_voltage(struct tegra_pmc *pmc, enum tegra_io_pad id) return TEGRA_IO_PAD_VOLTAGE_3V3; } -/** - * tegra_io_rail_power_on() - enable power to I/O rail - * @id: Tegra I/O pad ID for which to enable power - * - * See also: tegra_io_pad_power_enable() - */ -int tegra_io_rail_power_on(unsigned int id) -{ - return tegra_io_pad_power_enable(id); -} -EXPORT_SYMBOL(tegra_io_rail_power_on); - -/** - * tegra_io_rail_power_off() - disable power to I/O rail - * @id: Tegra I/O pad ID for which to disable power - * - * See also: tegra_io_pad_power_disable() - */ -int tegra_io_rail_power_off(unsigned int id) -{ - return tegra_io_pad_power_disable(id); -} -EXPORT_SYMBOL(tegra_io_rail_power_off); - #ifdef CONFIG_PM_SLEEP enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void) { @@ -2909,31 +2886,33 @@ static int tegra_pmc_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wake"); - if (res) { + if (pmc->soc->has_single_mmio_aperture) { + pmc->wake = base; + pmc->aotag = base; + pmc->scratch = base; + } else { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "wake"); pmc->wake = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(pmc->wake)) return PTR_ERR(pmc->wake); - } else { - pmc->wake = base; - } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aotag"); - if (res) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "aotag"); pmc->aotag = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(pmc->aotag)) return PTR_ERR(pmc->aotag); - } else { - pmc->aotag = base; - } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "scratch"); - if (res) { - pmc->scratch = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(pmc->scratch)) - return PTR_ERR(pmc->scratch); - } else { - pmc->scratch = base; + /* "scratch" is an optional aperture */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "scratch"); + if (res) { + pmc->scratch = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pmc->scratch)) + return PTR_ERR(pmc->scratch); + } else { + pmc->scratch = NULL; + } } pmc->clk = devm_clk_get_optional(&pdev->dev, "pclk"); @@ -2945,12 +2924,15 @@ static int tegra_pmc_probe(struct platform_device *pdev) * PMC should be last resort for restarting since it soft-resets * CPU without resetting everything else. */ - err = devm_register_reboot_notifier(&pdev->dev, - &tegra_pmc_reboot_notifier); - if (err) { - dev_err(&pdev->dev, "unable to register reboot notifier, %d\n", - err); - return err; + if (pmc->scratch) { + err = devm_register_reboot_notifier(&pdev->dev, + &tegra_pmc_reboot_notifier); + if (err) { + dev_err(&pdev->dev, + "unable to register reboot notifier, %d\n", + err); + return err; + } } err = devm_register_sys_off_handler(&pdev->dev, @@ -3324,6 +3306,7 @@ static const struct tegra_pmc_soc tegra20_pmc_soc = { .num_pmc_clks = 0, .has_blink_output = true, .has_usb_sleepwalk = true, + .has_single_mmio_aperture = true, }; static const char * const tegra30_powergates[] = { @@ -3385,6 +3368,7 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = { .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data), .has_blink_output = true, .has_usb_sleepwalk = true, + .has_single_mmio_aperture = true, }; static const char * const tegra114_powergates[] = { @@ -3442,6 +3426,7 @@ static const struct tegra_pmc_soc tegra114_pmc_soc = { .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data), .has_blink_output = true, .has_usb_sleepwalk = true, + .has_single_mmio_aperture = true, }; static const char * const tegra124_powergates[] = { @@ -3586,6 +3571,7 @@ static const struct tegra_pmc_soc tegra124_pmc_soc = { .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data), .has_blink_output = true, .has_usb_sleepwalk = true, + .has_single_mmio_aperture = true, }; static const char * const tegra210_powergates[] = { @@ -3749,6 +3735,7 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = { .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data), .has_blink_output = true, .has_usb_sleepwalk = true, + .has_single_mmio_aperture = true, }; static const struct tegra_io_pad_soc tegra186_io_pads[] = { @@ -3946,6 +3933,7 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = { .num_pmc_clks = 0, .has_blink_output = false, .has_usb_sleepwalk = false, + .has_single_mmio_aperture = false, }; static const struct tegra_io_pad_soc tegra194_io_pads[] = { @@ -4086,6 +4074,7 @@ static const char * const tegra194_reset_sources[] = { }; static const struct tegra_wake_event tegra194_wake_events[] = { + TEGRA_WAKE_GPIO("eqos", 20, 0, TEGRA194_MAIN_GPIO(G, 4)), TEGRA_WAKE_IRQ("pmu", 24, 209), TEGRA_WAKE_GPIO("power", 29, 1, TEGRA194_AON_GPIO(EE, 4)), TEGRA_WAKE_IRQ("rtc", 73, 10), @@ -4131,6 +4120,7 @@ static const struct tegra_pmc_soc tegra194_pmc_soc = { .num_pmc_clks = 0, .has_blink_output = false, .has_usb_sleepwalk = false, + .has_single_mmio_aperture = false, }; static const struct tegra_io_pad_soc tegra234_io_pads[] = { @@ -4220,6 +4210,8 @@ static const char * const tegra234_reset_sources[] = { }; static const struct tegra_wake_event tegra234_wake_events[] = { + TEGRA_WAKE_GPIO("sd-wake", 8, 0, TEGRA234_MAIN_GPIO(G, 7)), + TEGRA_WAKE_GPIO("eqos", 20, 0, TEGRA234_MAIN_GPIO(G, 4)), TEGRA_WAKE_IRQ("pmu", 24, 209), TEGRA_WAKE_GPIO("power", 29, 1, TEGRA234_AON_GPIO(EE, 4)), TEGRA_WAKE_GPIO("mgbe", 56, 0, TEGRA234_MAIN_GPIO(Y, 3)), @@ -4259,6 +4251,7 @@ static const struct tegra_pmc_soc tegra234_pmc_soc = { .pmc_clks_data = NULL, .num_pmc_clks = 0, .has_blink_output = false, + .has_single_mmio_aperture = false, }; static const struct of_device_id tegra_pmc_match[] = { diff --git a/drivers/soc/ti/wkup_m3_ipc.c b/drivers/soc/ti/wkup_m3_ipc.c index 6a1c6b34c4..88f774db92 100644 --- a/drivers/soc/ti/wkup_m3_ipc.c +++ b/drivers/soc/ti/wkup_m3_ipc.c @@ -16,7 +16,6 @@ #include <linux/irq.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/omap-mailbox.h> #include <linux/platform_device.h> #include <linux/remoteproc.h> #include <linux/suspend.h> @@ -314,7 +313,6 @@ static irqreturn_t wkup_m3_txev_handler(int irq, void *ipc_data) static int wkup_m3_ping(struct wkup_m3_ipc *m3_ipc) { struct device *dev = m3_ipc->dev; - mbox_msg_t dummy_msg = 0; int ret; if (!m3_ipc->mbox) { @@ -330,7 +328,7 @@ static int wkup_m3_ping(struct wkup_m3_ipc *m3_ipc) * the RX callback to avoid multiple interrupts being received * by the CM3. */ - ret = mbox_send_message(m3_ipc->mbox, &dummy_msg); + ret = mbox_send_message(m3_ipc->mbox, NULL); if (ret < 0) { dev_err(dev, "%s: mbox_send_message() failed: %d\n", __func__, ret); @@ -352,7 +350,6 @@ static int wkup_m3_ping(struct wkup_m3_ipc *m3_ipc) static int wkup_m3_ping_noirq(struct wkup_m3_ipc *m3_ipc) { struct device *dev = m3_ipc->dev; - mbox_msg_t dummy_msg = 0; int ret; if (!m3_ipc->mbox) { @@ -361,7 +358,7 @@ static int wkup_m3_ping_noirq(struct wkup_m3_ipc *m3_ipc) return -EIO; } - ret = mbox_send_message(m3_ipc->mbox, &dummy_msg); + ret = mbox_send_message(m3_ipc->mbox, NULL); if (ret < 0) { dev_err(dev, "%s: mbox_send_message() failed: %d\n", __func__, ret); diff --git a/drivers/soc/xilinx/xlnx_event_manager.c b/drivers/soc/xilinx/xlnx_event_manager.c index 253299e421..366018f6a0 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; @@ -570,7 +571,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 +607,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 +624,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 965b114393..b82c01373f 100644 --- a/drivers/soc/xilinx/zynqmp_power.c +++ b/drivers/soc/xilinx/zynqmp_power.c @@ -190,7 +190,9 @@ static int zynqmp_pm_probe(struct platform_device *pdev) u32 pm_api_version; 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) |