diff options
Diffstat (limited to 'drivers/soc/qcom')
-rw-r--r-- | drivers/soc/qcom/Kconfig | 54 | ||||
-rw-r--r-- | drivers/soc/qcom/Makefile | 5 | ||||
-rw-r--r-- | drivers/soc/qcom/llcc-qcom.c (renamed from drivers/soc/qcom/llcc-slice.c) | 130 | ||||
-rw-r--r-- | drivers/soc/qcom/llcc-sdm845.c | 100 | ||||
-rw-r--r-- | drivers/soc/qcom/ocmem.c | 433 | ||||
-rw-r--r-- | drivers/soc/qcom/qcom-geni-se.c | 6 | ||||
-rw-r--r-- | drivers/soc/qcom/qcom_aoss.c | 133 | ||||
-rw-r--r-- | drivers/soc/qcom/qmi_interface.c | 8 | ||||
-rw-r--r-- | drivers/soc/qcom/rpmhpd.c | 56 | ||||
-rw-r--r-- | drivers/soc/qcom/rpmpd.c | 23 | ||||
-rw-r--r-- | drivers/soc/qcom/smd-rpm.c | 18 | ||||
-rw-r--r-- | drivers/soc/qcom/smem.c | 11 | ||||
-rw-r--r-- | drivers/soc/qcom/socinfo.c | 478 |
13 files changed, 1314 insertions, 141 deletions
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index a6d1bfb17279..d0a73e76d563 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -45,35 +45,37 @@ config QCOM_GLINK_SSR neighboring subsystems going up or down. config QCOM_GSBI - tristate "QCOM General Serial Bus Interface" - depends on ARCH_QCOM || COMPILE_TEST - select MFD_SYSCON - help - Say y here to enable GSBI support. The GSBI provides control - functions for connecting the underlying serial UART, SPI, and I2C - devices to the output pins. + tristate "QCOM General Serial Bus Interface" + depends on ARCH_QCOM || COMPILE_TEST + select MFD_SYSCON + help + Say y here to enable GSBI support. The GSBI provides control + functions for connecting the underlying serial UART, SPI, and I2C + devices to the output pins. config QCOM_LLCC tristate "Qualcomm Technologies, Inc. LLCC driver" depends on ARCH_QCOM || COMPILE_TEST help Qualcomm Technologies, Inc. platform specific - Last Level Cache Controller(LLCC) driver. This provides interfaces - to clients that use the LLCC. Say yes here to enable LLCC slice - driver. - -config QCOM_SDM845_LLCC - tristate "Qualcomm Technologies, Inc. SDM845 LLCC driver" - depends on QCOM_LLCC - help - Say yes here to enable the LLCC driver for SDM845. This provides - data required to configure LLCC so that clients can start using the - LLCC slices. + Last Level Cache Controller(LLCC) driver for platforms such as, + SDM845. This provides interfaces to clients that use the LLCC. + Say yes here to enable LLCC slice driver. config QCOM_MDT_LOADER tristate select QCOM_SCM +config QCOM_OCMEM + tristate "Qualcomm On Chip Memory (OCMEM) driver" + depends on ARCH_QCOM + select QCOM_SCM + help + The On Chip Memory (OCMEM) allocator allows various clients to + allocate memory from OCMEM based on performance, latency and power + requirements. This is typically used by the GPU, camera/video, and + audio components on some Snapdragon SoCs. + config QCOM_PM bool "Qualcomm Power Management" depends on ARCH_QCOM && !ARM64 @@ -175,6 +177,14 @@ config QCOM_SMSM Say yes here to support the Qualcomm Shared Memory State Machine. The state machine is represented by bits in shared memory. +config QCOM_SOCINFO + tristate "Qualcomm socinfo driver" + depends on QCOM_SMEM + select SOC_BUS + help + Say yes here to support the Qualcomm socinfo driver, providing + information about the SoC to user space. + config QCOM_WCNSS_CTRL tristate "Qualcomm WCNSS control driver" depends on ARCH_QCOM || COMPILE_TEST @@ -188,8 +198,8 @@ config QCOM_APR depends on ARCH_QCOM || COMPILE_TEST depends on RPMSG help - Enable APR IPC protocol support between - application processor and QDSP6. APR is - used by audio driver to configure QDSP6 - ASM, ADM and AFE modules. + Enable APR IPC protocol support between + application processor and QDSP6. APR is + used by audio driver to configure QDSP6 + ASM, ADM and AFE modules. endmenu diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index eeb088beb15f..9fb35c8a495e 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o +obj-$(CONFIG_QCOM_OCMEM) += ocmem.o obj-$(CONFIG_QCOM_PM) += spm.o obj-$(CONFIG_QCOM_QMI_HELPERS) += qmi_helpers.o qmi_helpers-y += qmi_encdec.o qmi_interface.o @@ -18,9 +19,9 @@ obj-$(CONFIG_QCOM_SMEM) += smem.o obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o obj-$(CONFIG_QCOM_SMP2P) += smp2p.o obj-$(CONFIG_QCOM_SMSM) += smsm.o +obj-$(CONFIG_QCOM_SOCINFO) += socinfo.o obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o obj-$(CONFIG_QCOM_APR) += apr.o -obj-$(CONFIG_QCOM_LLCC) += llcc-slice.o -obj-$(CONFIG_QCOM_SDM845_LLCC) += llcc-sdm845.o +obj-$(CONFIG_QCOM_LLCC) += llcc-qcom.o obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o diff --git a/drivers/soc/qcom/llcc-slice.c b/drivers/soc/qcom/llcc-qcom.c index 9090ea12eaf3..429b5a60a1ba 100644 --- a/drivers/soc/qcom/llcc-slice.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. * */ @@ -11,6 +11,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/of.h> #include <linux/of_device.h> #include <linux/regmap.h> #include <linux/sizes.h> @@ -46,15 +47,90 @@ #define BANK_OFFSET_STRIDE 0x80000 -static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER; +/** + * llcc_slice_config - Data associated with the llcc slice + * @usecase_id: Unique id for the client's use case + * @slice_id: llcc slice id for each client + * @max_cap: The maximum capacity of the cache slice provided in KB + * @priority: Priority of the client used to select victim line for replacement + * @fixed_size: Boolean indicating if the slice has a fixed capacity + * @bonus_ways: Bonus ways are additional ways to be used for any slice, + * if client ends up using more than reserved cache ways. Bonus + * ways are allocated only if they are not reserved for some + * other client. + * @res_ways: Reserved ways for the cache slice, the reserved ways cannot + * be used by any other client than the one its assigned to. + * @cache_mode: Each slice operates as a cache, this controls the mode of the + * slice: normal or TCM(Tightly Coupled Memory) + * @probe_target_ways: Determines what ways to probe for access hit. When + * configured to 1 only bonus and reserved ways are probed. + * When configured to 0 all ways in llcc are probed. + * @dis_cap_alloc: Disable capacity based allocation for a client + * @retain_on_pc: If this bit is set and client has maintained active vote + * then the ways assigned to this client are not flushed on power + * collapse. + * @activate_on_init: Activate the slice immediately after it is programmed + */ +struct llcc_slice_config { + u32 usecase_id; + u32 slice_id; + u32 max_cap; + u32 priority; + bool fixed_size; + u32 bonus_ways; + u32 res_ways; + u32 cache_mode; + u32 probe_target_ways; + bool dis_cap_alloc; + bool retain_on_pc; + bool activate_on_init; +}; + +struct qcom_llcc_config { + const struct llcc_slice_config *sct_data; + int size; +}; + +static const struct llcc_slice_config sc7180_data[] = { + { LLCC_CPUSS, 1, 256, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 1 }, + { LLCC_MDM, 8, 128, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_GPUHTW, 11, 128, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 0 }, + { LLCC_GPU, 12, 128, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 0 }, +}; + +static const struct llcc_slice_config sdm845_data[] = { + { LLCC_CPUSS, 1, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 1 }, + { LLCC_VIDSC0, 2, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0 }, + { LLCC_VIDSC1, 3, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0 }, + { LLCC_ROTATOR, 4, 563, 2, 1, 0x0, 0x00e, 2, 0, 1, 1, 0 }, + { LLCC_VOICE, 5, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_AUDIO, 6, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_MDMHPGRW, 7, 1024, 2, 0, 0xfc, 0xf00, 0, 0, 1, 1, 0 }, + { LLCC_MDM, 8, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_CMPT, 10, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_GPUHTW, 11, 512, 1, 1, 0xc, 0x0, 0, 0, 1, 1, 0 }, + { LLCC_GPU, 12, 2304, 1, 0, 0xff0, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_MMUHWT, 13, 256, 2, 0, 0x0, 0x1, 0, 0, 1, 0, 1 }, + { LLCC_CMPTDMA, 15, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_DISP, 16, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_VIDFW, 17, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 }, + { LLCC_MDMHPFX, 20, 1024, 2, 1, 0x0, 0xf00, 0, 0, 1, 1, 0 }, + { LLCC_MDMPNG, 21, 1024, 0, 1, 0x1e, 0x0, 0, 0, 1, 1, 0 }, + { LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0 }, +}; + +static const struct qcom_llcc_config sc7180_cfg = { + .sct_data = sc7180_data, + .size = ARRAY_SIZE(sc7180_data), +}; -static const struct regmap_config llcc_regmap_config = { - .reg_bits = 32, - .reg_stride = 4, - .val_bits = 32, - .fast_io = true, +static const struct qcom_llcc_config sdm845_cfg = { + .sct_data = sdm845_data, + .size = ARRAY_SIZE(sdm845_data), }; +static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER; + /** * llcc_slice_getd - get llcc slice descriptor * @uid: usecase_id for the client @@ -301,19 +377,24 @@ static int qcom_llcc_cfg_program(struct platform_device *pdev) return ret; } -int qcom_llcc_remove(struct platform_device *pdev) +static int qcom_llcc_remove(struct platform_device *pdev) { /* Set the global pointer to a error code to avoid referencing it */ drv_data = ERR_PTR(-ENODEV); return 0; } -EXPORT_SYMBOL_GPL(qcom_llcc_remove); static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, const char *name) { struct resource *res; void __iomem *base; + struct regmap_config llcc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + }; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); if (!res) @@ -323,16 +404,19 @@ static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev, if (IS_ERR(base)) return ERR_CAST(base); + llcc_regmap_config.name = name; return devm_regmap_init_mmio(&pdev->dev, base, &llcc_regmap_config); } -int qcom_llcc_probe(struct platform_device *pdev, - const struct llcc_slice_config *llcc_cfg, u32 sz) +static int qcom_llcc_probe(struct platform_device *pdev) { u32 num_banks; struct device *dev = &pdev->dev; int ret, i; struct platform_device *llcc_edac; + const struct qcom_llcc_config *cfg; + const struct llcc_slice_config *llcc_cfg; + u32 sz; drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL); if (!drv_data) { @@ -362,6 +446,10 @@ int qcom_llcc_probe(struct platform_device *pdev, num_banks >>= LLCC_LB_CNT_SHIFT; drv_data->num_banks = num_banks; + cfg = of_device_get_match_data(&pdev->dev); + llcc_cfg = cfg->sct_data; + sz = cfg->size; + for (i = 0; i < sz; i++) if (llcc_cfg[i].slice_id > drv_data->max_slices) drv_data->max_slices = llcc_cfg[i].slice_id; @@ -407,6 +495,22 @@ err: drv_data = ERR_PTR(-ENODEV); return ret; } -EXPORT_SYMBOL_GPL(qcom_llcc_probe); -MODULE_LICENSE("GPL v2"); + +static const struct of_device_id qcom_llcc_of_match[] = { + { .compatible = "qcom,sc7180-llcc", .data = &sc7180_cfg }, + { .compatible = "qcom,sdm845-llcc", .data = &sdm845_cfg }, + { } +}; + +static struct platform_driver qcom_llcc_driver = { + .driver = { + .name = "qcom-llcc", + .of_match_table = qcom_llcc_of_match, + }, + .probe = qcom_llcc_probe, + .remove = qcom_llcc_remove, +}; +module_platform_driver(qcom_llcc_driver); + MODULE_DESCRIPTION("Qualcomm Last Level Cache Controller"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/llcc-sdm845.c b/drivers/soc/qcom/llcc-sdm845.c deleted file mode 100644 index 86600d97c36d..000000000000 --- a/drivers/soc/qcom/llcc-sdm845.c +++ /dev/null @@ -1,100 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. - * - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/soc/qcom/llcc-qcom.h> - -/* - * SCT(System Cache Table) entry contains of the following members: - * usecase_id: Unique id for the client's use case - * slice_id: llcc slice id for each client - * max_cap: The maximum capacity of the cache slice provided in KB - * priority: Priority of the client used to select victim line for replacement - * fixed_size: Boolean indicating if the slice has a fixed capacity - * bonus_ways: Bonus ways are additional ways to be used for any slice, - * if client ends up using more than reserved cache ways. Bonus - * ways are allocated only if they are not reserved for some - * other client. - * res_ways: Reserved ways for the cache slice, the reserved ways cannot - * be used by any other client than the one its assigned to. - * cache_mode: Each slice operates as a cache, this controls the mode of the - * slice: normal or TCM(Tightly Coupled Memory) - * probe_target_ways: Determines what ways to probe for access hit. When - * configured to 1 only bonus and reserved ways are probed. - * When configured to 0 all ways in llcc are probed. - * dis_cap_alloc: Disable capacity based allocation for a client - * retain_on_pc: If this bit is set and client has maintained active vote - * then the ways assigned to this client are not flushed on power - * collapse. - * activate_on_init: Activate the slice immediately after the SCT is programmed - */ -#define SCT_ENTRY(uid, sid, mc, p, fs, bway, rway, cmod, ptw, dca, rp, a) \ - { \ - .usecase_id = uid, \ - .slice_id = sid, \ - .max_cap = mc, \ - .priority = p, \ - .fixed_size = fs, \ - .bonus_ways = bway, \ - .res_ways = rway, \ - .cache_mode = cmod, \ - .probe_target_ways = ptw, \ - .dis_cap_alloc = dca, \ - .retain_on_pc = rp, \ - .activate_on_init = a, \ - } - -static struct llcc_slice_config sdm845_data[] = { - SCT_ENTRY(LLCC_CPUSS, 1, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 1), - SCT_ENTRY(LLCC_VIDSC0, 2, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_VIDSC1, 3, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_ROTATOR, 4, 563, 2, 1, 0x0, 0x00e, 2, 0, 1, 1, 0), - SCT_ENTRY(LLCC_VOICE, 5, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_AUDIO, 6, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_MDMHPGRW, 7, 1024, 2, 0, 0xfc, 0xf00, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_MDM, 8, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_CMPT, 10, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_GPUHTW, 11, 512, 1, 1, 0xc, 0x0, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_GPU, 12, 2304, 1, 0, 0xff0, 0x2, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_MMUHWT, 13, 256, 2, 0, 0x0, 0x1, 0, 0, 1, 0, 1), - SCT_ENTRY(LLCC_CMPTDMA, 15, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_DISP, 16, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_VIDFW, 17, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_MDMHPFX, 20, 1024, 2, 1, 0x0, 0xf00, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_MDMPNG, 21, 1024, 0, 1, 0x1e, 0x0, 0, 0, 1, 1, 0), - SCT_ENTRY(LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0), -}; - -static int sdm845_qcom_llcc_remove(struct platform_device *pdev) -{ - return qcom_llcc_remove(pdev); -} - -static int sdm845_qcom_llcc_probe(struct platform_device *pdev) -{ - return qcom_llcc_probe(pdev, sdm845_data, ARRAY_SIZE(sdm845_data)); -} - -static const struct of_device_id sdm845_qcom_llcc_of_match[] = { - { .compatible = "qcom,sdm845-llcc", }, - { } -}; - -static struct platform_driver sdm845_qcom_llcc_driver = { - .driver = { - .name = "sdm845-llcc", - .of_match_table = sdm845_qcom_llcc_of_match, - }, - .probe = sdm845_qcom_llcc_probe, - .remove = sdm845_qcom_llcc_remove, -}; -module_platform_driver(sdm845_qcom_llcc_driver); - -MODULE_DESCRIPTION("QCOM sdm845 LLCC driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/ocmem.c b/drivers/soc/qcom/ocmem.c new file mode 100644 index 000000000000..7f9e9944d1ea --- /dev/null +++ b/drivers/soc/qcom/ocmem.c @@ -0,0 +1,433 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * The On Chip Memory (OCMEM) allocator allows various clients to allocate + * memory from OCMEM based on performance, latency and power requirements. + * This is typically used by the GPU, camera/video, and audio components on + * some Snapdragon SoCs. + * + * Copyright (C) 2019 Brian Masney <masneyb@onstation.org> + * Copyright (C) 2015 Red Hat. Author: Rob Clark <robdclark@gmail.com> + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/qcom_scm.h> +#include <linux/sizes.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <soc/qcom/ocmem.h> + +enum region_mode { + WIDE_MODE = 0x0, + THIN_MODE, + MODE_DEFAULT = WIDE_MODE, +}; + +enum ocmem_macro_state { + PASSTHROUGH = 0, + PERI_ON = 1, + CORE_ON = 2, + CLK_OFF = 4, +}; + +struct ocmem_region { + bool interleaved; + enum region_mode mode; + unsigned int num_macros; + enum ocmem_macro_state macro_state[4]; + unsigned long macro_size; + unsigned long region_size; +}; + +struct ocmem_config { + uint8_t num_regions; + unsigned long macro_size; +}; + +struct ocmem { + struct device *dev; + const struct ocmem_config *config; + struct resource *memory; + void __iomem *mmio; + unsigned int num_ports; + unsigned int num_macros; + bool interleaved; + struct ocmem_region *regions; + unsigned long active_allocations; +}; + +#define OCMEM_MIN_ALIGN SZ_64K +#define OCMEM_MIN_ALLOC SZ_64K + +#define OCMEM_REG_HW_VERSION 0x00000000 +#define OCMEM_REG_HW_PROFILE 0x00000004 + +#define OCMEM_REG_REGION_MODE_CTL 0x00001000 +#define OCMEM_REGION_MODE_CTL_REG0_THIN 0x00000001 +#define OCMEM_REGION_MODE_CTL_REG1_THIN 0x00000002 +#define OCMEM_REGION_MODE_CTL_REG2_THIN 0x00000004 +#define OCMEM_REGION_MODE_CTL_REG3_THIN 0x00000008 + +#define OCMEM_REG_GFX_MPU_START 0x00001004 +#define OCMEM_REG_GFX_MPU_END 0x00001008 + +#define OCMEM_HW_PROFILE_NUM_PORTS(val) FIELD_PREP(0x0000000f, (val)) +#define OCMEM_HW_PROFILE_NUM_MACROS(val) FIELD_PREP(0x00003f00, (val)) + +#define OCMEM_HW_PROFILE_LAST_REGN_HALFSIZE 0x00010000 +#define OCMEM_HW_PROFILE_INTERLEAVING 0x00020000 +#define OCMEM_REG_GEN_STATUS 0x0000000c + +#define OCMEM_REG_PSGSC_STATUS 0x00000038 +#define OCMEM_REG_PSGSC_CTL(i0) (0x0000003c + 0x1*(i0)) + +#define OCMEM_PSGSC_CTL_MACRO0_MODE(val) FIELD_PREP(0x00000007, (val)) +#define OCMEM_PSGSC_CTL_MACRO1_MODE(val) FIELD_PREP(0x00000070, (val)) +#define OCMEM_PSGSC_CTL_MACRO2_MODE(val) FIELD_PREP(0x00000700, (val)) +#define OCMEM_PSGSC_CTL_MACRO3_MODE(val) FIELD_PREP(0x00007000, (val)) + +#define OCMEM_CLK_CORE_IDX 0 +static struct clk_bulk_data ocmem_clks[] = { + { + .id = "core", + }, + { + .id = "iface", + }, +}; + +static inline void ocmem_write(struct ocmem *ocmem, u32 reg, u32 data) +{ + writel(data, ocmem->mmio + reg); +} + +static inline u32 ocmem_read(struct ocmem *ocmem, u32 reg) +{ + return readl(ocmem->mmio + reg); +} + +static void update_ocmem(struct ocmem *ocmem) +{ + uint32_t region_mode_ctrl = 0x0; + int i; + + if (!qcom_scm_ocmem_lock_available()) { + for (i = 0; i < ocmem->config->num_regions; i++) { + struct ocmem_region *region = &ocmem->regions[i]; + + if (region->mode == THIN_MODE) + region_mode_ctrl |= BIT(i); + } + + dev_dbg(ocmem->dev, "ocmem_region_mode_control %x\n", + region_mode_ctrl); + ocmem_write(ocmem, OCMEM_REG_REGION_MODE_CTL, region_mode_ctrl); + } + + for (i = 0; i < ocmem->config->num_regions; i++) { + struct ocmem_region *region = &ocmem->regions[i]; + u32 data; + + data = OCMEM_PSGSC_CTL_MACRO0_MODE(region->macro_state[0]) | + OCMEM_PSGSC_CTL_MACRO1_MODE(region->macro_state[1]) | + OCMEM_PSGSC_CTL_MACRO2_MODE(region->macro_state[2]) | + OCMEM_PSGSC_CTL_MACRO3_MODE(region->macro_state[3]); + + ocmem_write(ocmem, OCMEM_REG_PSGSC_CTL(i), data); + } +} + +static unsigned long phys_to_offset(struct ocmem *ocmem, + unsigned long addr) +{ + if (addr < ocmem->memory->start || addr >= ocmem->memory->end) + return 0; + + return addr - ocmem->memory->start; +} + +static unsigned long device_address(struct ocmem *ocmem, + enum ocmem_client client, + unsigned long addr) +{ + WARN_ON(client != OCMEM_GRAPHICS); + + /* TODO: gpu uses phys_to_offset, but others do not.. */ + return phys_to_offset(ocmem, addr); +} + +static void update_range(struct ocmem *ocmem, struct ocmem_buf *buf, + enum ocmem_macro_state mstate, enum region_mode rmode) +{ + unsigned long offset = 0; + int i, j; + + for (i = 0; i < ocmem->config->num_regions; i++) { + struct ocmem_region *region = &ocmem->regions[i]; + + if (buf->offset <= offset && offset < buf->offset + buf->len) + region->mode = rmode; + + for (j = 0; j < region->num_macros; j++) { + if (buf->offset <= offset && + offset < buf->offset + buf->len) + region->macro_state[j] = mstate; + + offset += region->macro_size; + } + } + + update_ocmem(ocmem); +} + +struct ocmem *of_get_ocmem(struct device *dev) +{ + struct platform_device *pdev; + struct device_node *devnode; + + devnode = of_parse_phandle(dev->of_node, "sram", 0); + if (!devnode || !devnode->parent) { + dev_err(dev, "Cannot look up sram phandle\n"); + return ERR_PTR(-ENODEV); + } + + pdev = of_find_device_by_node(devnode->parent); + if (!pdev) { + dev_err(dev, "Cannot find device node %s\n", devnode->name); + return ERR_PTR(-EPROBE_DEFER); + } + + return platform_get_drvdata(pdev); +} +EXPORT_SYMBOL(of_get_ocmem); + +struct ocmem_buf *ocmem_allocate(struct ocmem *ocmem, enum ocmem_client client, + unsigned long size) +{ + struct ocmem_buf *buf; + int ret; + + /* TODO: add support for other clients... */ + if (WARN_ON(client != OCMEM_GRAPHICS)) + return ERR_PTR(-ENODEV); + + if (size < OCMEM_MIN_ALLOC || !IS_ALIGNED(size, OCMEM_MIN_ALIGN)) + return ERR_PTR(-EINVAL); + + if (test_and_set_bit_lock(BIT(client), &ocmem->active_allocations)) + return ERR_PTR(-EBUSY); + + buf = kzalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto err_unlock; + } + + buf->offset = 0; + buf->addr = device_address(ocmem, client, buf->offset); + buf->len = size; + + update_range(ocmem, buf, CORE_ON, WIDE_MODE); + + if (qcom_scm_ocmem_lock_available()) { + ret = qcom_scm_ocmem_lock(QCOM_SCM_OCMEM_GRAPHICS_ID, + buf->offset, buf->len, WIDE_MODE); + if (ret) { + dev_err(ocmem->dev, "could not lock: %d\n", ret); + ret = -EINVAL; + goto err_kfree; + } + } else { + ocmem_write(ocmem, OCMEM_REG_GFX_MPU_START, buf->offset); + ocmem_write(ocmem, OCMEM_REG_GFX_MPU_END, + buf->offset + buf->len); + } + + dev_dbg(ocmem->dev, "using %ldK of OCMEM at 0x%08lx for client %d\n", + size / 1024, buf->addr, client); + + return buf; + +err_kfree: + kfree(buf); +err_unlock: + clear_bit_unlock(BIT(client), &ocmem->active_allocations); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL(ocmem_allocate); + +void ocmem_free(struct ocmem *ocmem, enum ocmem_client client, + struct ocmem_buf *buf) +{ + /* TODO: add support for other clients... */ + if (WARN_ON(client != OCMEM_GRAPHICS)) + return; + + update_range(ocmem, buf, CLK_OFF, MODE_DEFAULT); + + if (qcom_scm_ocmem_lock_available()) { + int ret; + + ret = qcom_scm_ocmem_unlock(QCOM_SCM_OCMEM_GRAPHICS_ID, + buf->offset, buf->len); + if (ret) + dev_err(ocmem->dev, "could not unlock: %d\n", ret); + } else { + ocmem_write(ocmem, OCMEM_REG_GFX_MPU_START, 0x0); + ocmem_write(ocmem, OCMEM_REG_GFX_MPU_END, 0x0); + } + + kfree(buf); + + clear_bit_unlock(BIT(client), &ocmem->active_allocations); +} +EXPORT_SYMBOL(ocmem_free); + +static int ocmem_dev_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + unsigned long reg, region_size; + int i, j, ret, num_banks; + struct resource *res; + struct ocmem *ocmem; + + if (!qcom_scm_is_available()) + return -EPROBE_DEFER; + + ocmem = devm_kzalloc(dev, sizeof(*ocmem), GFP_KERNEL); + if (!ocmem) + return -ENOMEM; + + ocmem->dev = dev; + ocmem->config = device_get_match_data(dev); + + ret = devm_clk_bulk_get(dev, ARRAY_SIZE(ocmem_clks), ocmem_clks); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "Unable to get clocks\n"); + + return ret; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl"); + ocmem->mmio = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ocmem->mmio)) { + dev_err(&pdev->dev, "Failed to ioremap ocmem_ctrl resource\n"); + return PTR_ERR(ocmem->mmio); + } + + ocmem->memory = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "mem"); + if (!ocmem->memory) { + dev_err(dev, "Could not get mem region\n"); + return -ENXIO; + } + + /* The core clock is synchronous with graphics */ + WARN_ON(clk_set_rate(ocmem_clks[OCMEM_CLK_CORE_IDX].clk, 1000) < 0); + + ret = clk_bulk_prepare_enable(ARRAY_SIZE(ocmem_clks), ocmem_clks); + if (ret) { + dev_info(ocmem->dev, "Failed to enable clocks\n"); + return ret; + } + + if (qcom_scm_restore_sec_cfg_available()) { + dev_dbg(dev, "configuring scm\n"); + ret = qcom_scm_restore_sec_cfg(QCOM_SCM_OCMEM_DEV_ID, 0); + if (ret) { + dev_err(dev, "Could not enable secure configuration\n"); + goto err_clk_disable; + } + } + + reg = ocmem_read(ocmem, OCMEM_REG_HW_PROFILE); + ocmem->num_ports = OCMEM_HW_PROFILE_NUM_PORTS(reg); + ocmem->num_macros = OCMEM_HW_PROFILE_NUM_MACROS(reg); + ocmem->interleaved = !!(reg & OCMEM_HW_PROFILE_INTERLEAVING); + + num_banks = ocmem->num_ports / 2; + region_size = ocmem->config->macro_size * num_banks; + + dev_info(dev, "%u ports, %u regions, %u macros, %sinterleaved\n", + ocmem->num_ports, ocmem->config->num_regions, + ocmem->num_macros, ocmem->interleaved ? "" : "not "); + + ocmem->regions = devm_kcalloc(dev, ocmem->config->num_regions, + sizeof(struct ocmem_region), GFP_KERNEL); + if (!ocmem->regions) { + ret = -ENOMEM; + goto err_clk_disable; + } + + for (i = 0; i < ocmem->config->num_regions; i++) { + struct ocmem_region *region = &ocmem->regions[i]; + + if (WARN_ON(num_banks > ARRAY_SIZE(region->macro_state))) { + ret = -EINVAL; + goto err_clk_disable; + } + + region->mode = MODE_DEFAULT; + region->num_macros = num_banks; + + if (i == (ocmem->config->num_regions - 1) && + reg & OCMEM_HW_PROFILE_LAST_REGN_HALFSIZE) { + region->macro_size = ocmem->config->macro_size / 2; + region->region_size = region_size / 2; + } else { + region->macro_size = ocmem->config->macro_size; + region->region_size = region_size; + } + + for (j = 0; j < ARRAY_SIZE(region->macro_state); j++) + region->macro_state[j] = CLK_OFF; + } + + platform_set_drvdata(pdev, ocmem); + + return 0; + +err_clk_disable: + clk_bulk_disable_unprepare(ARRAY_SIZE(ocmem_clks), ocmem_clks); + return ret; +} + +static int ocmem_dev_remove(struct platform_device *pdev) +{ + clk_bulk_disable_unprepare(ARRAY_SIZE(ocmem_clks), ocmem_clks); + + return 0; +} + +static const struct ocmem_config ocmem_8974_config = { + .num_regions = 3, + .macro_size = SZ_128K, +}; + +static const struct of_device_id ocmem_of_match[] = { + { .compatible = "qcom,msm8974-ocmem", .data = &ocmem_8974_config }, + { } +}; + +MODULE_DEVICE_TABLE(of, ocmem_of_match); + +static struct platform_driver ocmem_driver = { + .probe = ocmem_dev_probe, + .remove = ocmem_dev_remove, + .driver = { + .name = "ocmem", + .of_match_table = ocmem_of_match, + }, +}; + +module_platform_driver(ocmem_driver); + +MODULE_DESCRIPTION("On Chip Memory (OCMEM) allocator for some Snapdragon SoCs"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c index d5cf953b4337..7d622ea1274e 100644 --- a/drivers/soc/qcom/qcom-geni-se.c +++ b/drivers/soc/qcom/qcom-geni-se.c @@ -630,6 +630,9 @@ int geni_se_tx_dma_prep(struct geni_se *se, void *buf, size_t len, struct geni_wrapper *wrapper = se->wrapper; u32 val; + if (!wrapper) + return -EINVAL; + *iova = dma_map_single(wrapper->dev, buf, len, DMA_TO_DEVICE); if (dma_mapping_error(wrapper->dev, *iova)) return -EIO; @@ -663,6 +666,9 @@ int geni_se_rx_dma_prep(struct geni_se *se, void *buf, size_t len, struct geni_wrapper *wrapper = se->wrapper; u32 val; + if (!wrapper) + return -EINVAL; + *iova = dma_map_single(wrapper->dev, buf, len, DMA_FROM_DEVICE); if (dma_mapping_error(wrapper->dev, *iova)) return -EIO; diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c index 5f885196f4d0..006ac40c526a 100644 --- a/drivers/soc/qcom/qcom_aoss.c +++ b/drivers/soc/qcom/qcom_aoss.c @@ -10,6 +10,8 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/pm_domain.h> +#include <linux/thermal.h> +#include <linux/slab.h> #define QMP_DESC_MAGIC 0x0 #define QMP_DESC_VERSION 0x4 @@ -40,6 +42,17 @@ /* 64 bytes is enough to store the requests and provides padding to 4 bytes */ #define QMP_MSG_LEN 64 +#define QMP_NUM_COOLING_RESOURCES 2 + +static bool qmp_cdev_max_state = 1; + +struct qmp_cooling_device { + struct thermal_cooling_device *cdev; + struct qmp *qmp; + char *name; + bool state; +}; + /** * struct qmp - driver state for QMP implementation * @msgram: iomem referencing the message RAM used for communication @@ -69,6 +82,7 @@ struct qmp { struct clk_hw qdss_clk; struct genpd_onecell_data pd_data; + struct qmp_cooling_device *cooling_devs; }; struct qmp_pd { @@ -385,6 +399,118 @@ static void qmp_pd_remove(struct qmp *qmp) pm_genpd_remove(data->domains[i]); } +static int qmp_cdev_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = qmp_cdev_max_state; + return 0; +} + +static int qmp_cdev_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct qmp_cooling_device *qmp_cdev = cdev->devdata; + + *state = qmp_cdev->state; + return 0; +} + +static int qmp_cdev_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + struct qmp_cooling_device *qmp_cdev = cdev->devdata; + char buf[QMP_MSG_LEN] = {}; + bool cdev_state; + int ret; + + /* Normalize state */ + cdev_state = !!state; + + if (qmp_cdev->state == state) + return 0; + + snprintf(buf, sizeof(buf), + "{class: volt_flr, event:zero_temp, res:%s, value:%s}", + qmp_cdev->name, + cdev_state ? "on" : "off"); + + ret = qmp_send(qmp_cdev->qmp, buf, sizeof(buf)); + + if (!ret) + qmp_cdev->state = cdev_state; + + return ret; +} + +static struct thermal_cooling_device_ops qmp_cooling_device_ops = { + .get_max_state = qmp_cdev_get_max_state, + .get_cur_state = qmp_cdev_get_cur_state, + .set_cur_state = qmp_cdev_set_cur_state, +}; + +static int qmp_cooling_device_add(struct qmp *qmp, + struct qmp_cooling_device *qmp_cdev, + struct device_node *node) +{ + char *cdev_name = (char *)node->name; + + qmp_cdev->qmp = qmp; + qmp_cdev->state = !qmp_cdev_max_state; + qmp_cdev->name = cdev_name; + qmp_cdev->cdev = devm_thermal_of_cooling_device_register + (qmp->dev, node, + cdev_name, + qmp_cdev, &qmp_cooling_device_ops); + + if (IS_ERR(qmp_cdev->cdev)) + dev_err(qmp->dev, "unable to register %s cooling device\n", + cdev_name); + + return PTR_ERR_OR_ZERO(qmp_cdev->cdev); +} + +static int qmp_cooling_devices_register(struct qmp *qmp) +{ + struct device_node *np, *child; + int count = QMP_NUM_COOLING_RESOURCES; + int ret; + + np = qmp->dev->of_node; + + qmp->cooling_devs = devm_kcalloc(qmp->dev, count, + sizeof(*qmp->cooling_devs), + GFP_KERNEL); + + if (!qmp->cooling_devs) + return -ENOMEM; + + for_each_available_child_of_node(np, child) { + if (!of_find_property(child, "#cooling-cells", NULL)) + continue; + ret = qmp_cooling_device_add(qmp, &qmp->cooling_devs[count++], + child); + if (ret) + goto unroll; + } + + return 0; + +unroll: + while (--count >= 0) + thermal_cooling_device_unregister + (qmp->cooling_devs[count].cdev); + + return ret; +} + +static void qmp_cooling_devices_remove(struct qmp *qmp) +{ + int i; + + for (i = 0; i < QMP_NUM_COOLING_RESOURCES; i++) + thermal_cooling_device_unregister(qmp->cooling_devs[i].cdev); +} + static int qmp_probe(struct platform_device *pdev) { struct resource *res; @@ -433,6 +559,10 @@ static int qmp_probe(struct platform_device *pdev) if (ret) goto err_remove_qdss_clk; + ret = qmp_cooling_devices_register(qmp); + if (ret) + dev_err(&pdev->dev, "failed to register aoss cooling devices\n"); + platform_set_drvdata(pdev, qmp); return 0; @@ -453,6 +583,7 @@ static int qmp_remove(struct platform_device *pdev) qmp_qdss_clk_remove(qmp); qmp_pd_remove(qmp); + qmp_cooling_devices_remove(qmp); qmp_close(qmp); mbox_free_channel(qmp->mbox_chan); @@ -461,7 +592,9 @@ static int qmp_remove(struct platform_device *pdev) } static const struct of_device_id qmp_dt_match[] = { + { .compatible = "qcom,sc7180-aoss-qmp", }, { .compatible = "qcom,sdm845-aoss-qmp", }, + { .compatible = "qcom,sm8150-aoss-qmp", }, {} }; MODULE_DEVICE_TABLE(of, qmp_dt_match); diff --git a/drivers/soc/qcom/qmi_interface.c b/drivers/soc/qcom/qmi_interface.c index f9e309f0acd3..1a03eaa38c46 100644 --- a/drivers/soc/qcom/qmi_interface.c +++ b/drivers/soc/qcom/qmi_interface.c @@ -655,8 +655,12 @@ int qmi_handle_init(struct qmi_handle *qmi, size_t recv_buf_size, qmi->sock = qmi_sock_create(qmi, &qmi->sq); if (IS_ERR(qmi->sock)) { - pr_err("failed to create QMI socket\n"); - ret = PTR_ERR(qmi->sock); + if (PTR_ERR(qmi->sock) == -EAFNOSUPPORT) { + ret = -EPROBE_DEFER; + } else { + pr_err("failed to create QMI socket\n"); + ret = PTR_ERR(qmi->sock); + } goto err_destroy_wq; } diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c index 5741ec3fa814..4d264d0672c4 100644 --- a/drivers/soc/qcom/rpmhpd.c +++ b/drivers/soc/qcom/rpmhpd.c @@ -93,6 +93,7 @@ static struct rpmhpd sdm845_mx = { static struct rpmhpd sdm845_mx_ao = { .pd = { .name = "mx_ao", }, + .active_only = true, .peer = &sdm845_mx, .res_name = "mx.lvl", }; @@ -107,6 +108,7 @@ static struct rpmhpd sdm845_cx = { static struct rpmhpd sdm845_cx_ao = { .pd = { .name = "cx_ao", }, + .active_only = true, .peer = &sdm845_cx, .parent = &sdm845_mx_ao.pd, .res_name = "cx.lvl", @@ -129,8 +131,62 @@ static const struct rpmhpd_desc sdm845_desc = { .num_pds = ARRAY_SIZE(sdm845_rpmhpds), }; +/* SM8150 RPMH powerdomains */ + +static struct rpmhpd sm8150_mmcx_ao; +static struct rpmhpd sm8150_mmcx = { + .pd = { .name = "mmcx", }, + .peer = &sm8150_mmcx_ao, + .res_name = "mmcx.lvl", +}; + +static struct rpmhpd sm8150_mmcx_ao = { + .pd = { .name = "mmcx_ao", }, + .active_only = true, + .peer = &sm8150_mmcx, + .res_name = "mmcx.lvl", +}; + +static struct rpmhpd *sm8150_rpmhpds[] = { + [SM8150_MSS] = &sdm845_mss, + [SM8150_EBI] = &sdm845_ebi, + [SM8150_LMX] = &sdm845_lmx, + [SM8150_LCX] = &sdm845_lcx, + [SM8150_GFX] = &sdm845_gfx, + [SM8150_MX] = &sdm845_mx, + [SM8150_MX_AO] = &sdm845_mx_ao, + [SM8150_CX] = &sdm845_cx, + [SM8150_CX_AO] = &sdm845_cx_ao, + [SM8150_MMCX] = &sm8150_mmcx, + [SM8150_MMCX_AO] = &sm8150_mmcx_ao, +}; + +static const struct rpmhpd_desc sm8150_desc = { + .rpmhpds = sm8150_rpmhpds, + .num_pds = ARRAY_SIZE(sm8150_rpmhpds), +}; + +/* SC7180 RPMH powerdomains */ +static struct rpmhpd *sc7180_rpmhpds[] = { + [SC7180_CX] = &sdm845_cx, + [SC7180_CX_AO] = &sdm845_cx_ao, + [SC7180_GFX] = &sdm845_gfx, + [SC7180_MX] = &sdm845_mx, + [SC7180_MX_AO] = &sdm845_mx_ao, + [SC7180_LMX] = &sdm845_lmx, + [SC7180_LCX] = &sdm845_lcx, + [SC7180_MSS] = &sdm845_mss, +}; + +static const struct rpmhpd_desc sc7180_desc = { + .rpmhpds = sc7180_rpmhpds, + .num_pds = ARRAY_SIZE(sc7180_rpmhpds), +}; + static const struct of_device_id rpmhpd_match_table[] = { + { .compatible = "qcom,sc7180-rpmhpd", .data = &sc7180_desc }, { .compatible = "qcom,sdm845-rpmhpd", .data = &sdm845_desc }, + { .compatible = "qcom,sm8150-rpmhpd", .data = &sm8150_desc }, { } }; diff --git a/drivers/soc/qcom/rpmpd.c b/drivers/soc/qcom/rpmpd.c index 3c1a55cf25d6..2b1834c5609a 100644 --- a/drivers/soc/qcom/rpmpd.c +++ b/drivers/soc/qcom/rpmpd.c @@ -115,6 +115,28 @@ struct rpmpd_desc { static DEFINE_MUTEX(rpmpd_lock); +/* msm8976 RPM Power Domains */ +DEFINE_RPMPD_PAIR(msm8976, vddcx, vddcx_ao, SMPA, LEVEL, 2); +DEFINE_RPMPD_PAIR(msm8976, vddmx, vddmx_ao, SMPA, LEVEL, 6); + +DEFINE_RPMPD_VFL(msm8976, vddcx_vfl, RWSC, 2); +DEFINE_RPMPD_VFL(msm8976, vddmx_vfl, RWSM, 6); + +static struct rpmpd *msm8976_rpmpds[] = { + [MSM8976_VDDCX] = &msm8976_vddcx, + [MSM8976_VDDCX_AO] = &msm8976_vddcx_ao, + [MSM8976_VDDCX_VFL] = &msm8976_vddcx_vfl, + [MSM8976_VDDMX] = &msm8976_vddmx, + [MSM8976_VDDMX_AO] = &msm8976_vddmx_ao, + [MSM8976_VDDMX_VFL] = &msm8976_vddmx_vfl, +}; + +static const struct rpmpd_desc msm8976_desc = { + .rpmpds = msm8976_rpmpds, + .num_pds = ARRAY_SIZE(msm8976_rpmpds), + .max_state = RPM_SMD_LEVEL_TURBO_HIGH, +}; + /* msm8996 RPM Power domains */ DEFINE_RPMPD_PAIR(msm8996, vddcx, vddcx_ao, SMPA, CORNER, 1); DEFINE_RPMPD_PAIR(msm8996, vddmx, vddmx_ao, SMPA, CORNER, 2); @@ -198,6 +220,7 @@ static const struct rpmpd_desc qcs404_desc = { }; static const struct of_device_id rpmpd_match_table[] = { + { .compatible = "qcom,msm8976-rpmpd", .data = &msm8976_desc }, { .compatible = "qcom,msm8996-rpmpd", .data = &msm8996_desc }, { .compatible = "qcom,msm8998-rpmpd", .data = &msm8998_desc }, { .compatible = "qcom,qcs404-rpmpd", .data = &qcs404_desc }, diff --git a/drivers/soc/qcom/smd-rpm.c b/drivers/soc/qcom/smd-rpm.c index fa9dd12b5e39..005dd30c58fa 100644 --- a/drivers/soc/qcom/smd-rpm.c +++ b/drivers/soc/qcom/smd-rpm.c @@ -19,12 +19,14 @@ /** * struct qcom_smd_rpm - state of the rpm device driver * @rpm_channel: reference to the smd channel + * @icc: interconnect proxy device * @ack: completion for acks * @lock: mutual exclusion around the send/complete pair * @ack_status: result of the rpm request */ struct qcom_smd_rpm { struct rpmsg_endpoint *rpm_channel; + struct platform_device *icc; struct device *dev; struct completion ack; @@ -193,6 +195,7 @@ static int qcom_smd_rpm_callback(struct rpmsg_device *rpdev, static int qcom_smd_rpm_probe(struct rpmsg_device *rpdev) { struct qcom_smd_rpm *rpm; + int ret; rpm = devm_kzalloc(&rpdev->dev, sizeof(*rpm), GFP_KERNEL); if (!rpm) @@ -205,11 +208,23 @@ static int qcom_smd_rpm_probe(struct rpmsg_device *rpdev) rpm->rpm_channel = rpdev->ept; dev_set_drvdata(&rpdev->dev, rpm); - return of_platform_populate(rpdev->dev.of_node, NULL, NULL, &rpdev->dev); + rpm->icc = platform_device_register_data(&rpdev->dev, "icc_smd_rpm", -1, + NULL, 0); + if (IS_ERR(rpm->icc)) + return PTR_ERR(rpm->icc); + + ret = of_platform_populate(rpdev->dev.of_node, NULL, NULL, &rpdev->dev); + if (ret) + platform_device_unregister(rpm->icc); + + return ret; } static void qcom_smd_rpm_remove(struct rpmsg_device *rpdev) { + struct qcom_smd_rpm *rpm = dev_get_drvdata(&rpdev->dev); + + platform_device_unregister(rpm->icc); of_platform_depopulate(&rpdev->dev); } @@ -217,6 +232,7 @@ static const struct of_device_id qcom_smd_rpm_of_match[] = { { .compatible = "qcom,rpm-apq8084" }, { .compatible = "qcom,rpm-msm8916" }, { .compatible = "qcom,rpm-msm8974" }, + { .compatible = "qcom,rpm-msm8976" }, { .compatible = "qcom,rpm-msm8996" }, { .compatible = "qcom,rpm-msm8998" }, { .compatible = "qcom,rpm-sdm660" }, diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index f27c00d82ae4..28c19bcb2f20 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -84,7 +84,7 @@ #define SMEM_GLOBAL_HOST 0xfffe /* Max number of processors/hosts in a system */ -#define SMEM_HOST_COUNT 10 +#define SMEM_HOST_COUNT 11 /** * struct smem_proc_comm - proc_comm communication struct (legacy) @@ -268,6 +268,7 @@ struct qcom_smem { struct smem_partition_header *partitions[SMEM_HOST_COUNT]; size_t cacheline[SMEM_HOST_COUNT]; u32 item_count; + struct platform_device *socinfo; unsigned num_regions; struct smem_region regions[]; @@ -963,11 +964,19 @@ static int qcom_smem_probe(struct platform_device *pdev) __smem = smem; + smem->socinfo = platform_device_register_data(&pdev->dev, "qcom-socinfo", + PLATFORM_DEVID_NONE, NULL, + 0); + if (IS_ERR(smem->socinfo)) + dev_dbg(&pdev->dev, "failed to register socinfo device\n"); + return 0; } static int qcom_smem_remove(struct platform_device *pdev) { + platform_device_unregister(__smem->socinfo); + hwspin_lock_free(__smem->hwlock); __smem = NULL; diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c new file mode 100644 index 000000000000..7864b75ce569 --- /dev/null +++ b/drivers/soc/qcom/socinfo.c @@ -0,0 +1,478 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2009-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2019, Linaro Ltd. + */ + +#include <linux/debugfs.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/random.h> +#include <linux/slab.h> +#include <linux/soc/qcom/smem.h> +#include <linux/string.h> +#include <linux/sys_soc.h> +#include <linux/types.h> + +/* + * SoC version type with major number in the upper 16 bits and minor + * number in the lower 16 bits. + */ +#define SOCINFO_MAJOR(ver) (((ver) >> 16) & 0xffff) +#define SOCINFO_MINOR(ver) ((ver) & 0xffff) +#define SOCINFO_VERSION(maj, min) ((((maj) & 0xffff) << 16)|((min) & 0xffff)) + +#define SMEM_SOCINFO_BUILD_ID_LENGTH 32 + +/* + * SMEM item id, used to acquire handles to respective + * SMEM region. + */ +#define SMEM_HW_SW_BUILD_ID 137 + +#ifdef CONFIG_DEBUG_FS +#define SMEM_IMAGE_VERSION_BLOCKS_COUNT 32 +#define SMEM_IMAGE_VERSION_SIZE 4096 +#define SMEM_IMAGE_VERSION_NAME_SIZE 75 +#define SMEM_IMAGE_VERSION_VARIANT_SIZE 20 +#define SMEM_IMAGE_VERSION_OEM_SIZE 32 + +/* + * SMEM Image table indices + */ +#define SMEM_IMAGE_TABLE_BOOT_INDEX 0 +#define SMEM_IMAGE_TABLE_TZ_INDEX 1 +#define SMEM_IMAGE_TABLE_RPM_INDEX 3 +#define SMEM_IMAGE_TABLE_APPS_INDEX 10 +#define SMEM_IMAGE_TABLE_MPSS_INDEX 11 +#define SMEM_IMAGE_TABLE_ADSP_INDEX 12 +#define SMEM_IMAGE_TABLE_CNSS_INDEX 13 +#define SMEM_IMAGE_TABLE_VIDEO_INDEX 14 +#define SMEM_IMAGE_VERSION_TABLE 469 + +/* + * SMEM Image table names + */ +static const char *const socinfo_image_names[] = { + [SMEM_IMAGE_TABLE_ADSP_INDEX] = "adsp", + [SMEM_IMAGE_TABLE_APPS_INDEX] = "apps", + [SMEM_IMAGE_TABLE_BOOT_INDEX] = "boot", + [SMEM_IMAGE_TABLE_CNSS_INDEX] = "cnss", + [SMEM_IMAGE_TABLE_MPSS_INDEX] = "mpss", + [SMEM_IMAGE_TABLE_RPM_INDEX] = "rpm", + [SMEM_IMAGE_TABLE_TZ_INDEX] = "tz", + [SMEM_IMAGE_TABLE_VIDEO_INDEX] = "video", +}; + +static const char *const pmic_models[] = { + [0] = "Unknown PMIC model", + [9] = "PM8994", + [11] = "PM8916", + [13] = "PM8058", + [14] = "PM8028", + [15] = "PM8901", + [16] = "PM8027", + [17] = "ISL9519", + [18] = "PM8921", + [19] = "PM8018", + [20] = "PM8015", + [21] = "PM8014", + [22] = "PM8821", + [23] = "PM8038", + [24] = "PM8922", + [25] = "PM8917", +}; +#endif /* CONFIG_DEBUG_FS */ + +/* Socinfo SMEM item structure */ +struct socinfo { + __le32 fmt; + __le32 id; + __le32 ver; + char build_id[SMEM_SOCINFO_BUILD_ID_LENGTH]; + /* Version 2 */ + __le32 raw_id; + __le32 raw_ver; + /* Version 3 */ + __le32 hw_plat; + /* Version 4 */ + __le32 plat_ver; + /* Version 5 */ + __le32 accessory_chip; + /* Version 6 */ + __le32 hw_plat_subtype; + /* Version 7 */ + __le32 pmic_model; + __le32 pmic_die_rev; + /* Version 8 */ + __le32 pmic_model_1; + __le32 pmic_die_rev_1; + __le32 pmic_model_2; + __le32 pmic_die_rev_2; + /* Version 9 */ + __le32 foundry_id; + /* Version 10 */ + __le32 serial_num; + /* Version 11 */ + __le32 num_pmics; + __le32 pmic_array_offset; + /* Version 12 */ + __le32 chip_family; + __le32 raw_device_family; + __le32 raw_device_num; +}; + +#ifdef CONFIG_DEBUG_FS +struct socinfo_params { + u32 raw_device_family; + u32 hw_plat_subtype; + u32 accessory_chip; + u32 raw_device_num; + u32 chip_family; + u32 foundry_id; + u32 plat_ver; + u32 raw_ver; + u32 hw_plat; + u32 fmt; +}; + +struct smem_image_version { + char name[SMEM_IMAGE_VERSION_NAME_SIZE]; + char variant[SMEM_IMAGE_VERSION_VARIANT_SIZE]; + char pad; + char oem[SMEM_IMAGE_VERSION_OEM_SIZE]; +}; +#endif /* CONFIG_DEBUG_FS */ + +struct qcom_socinfo { + struct soc_device *soc_dev; + struct soc_device_attribute attr; +#ifdef CONFIG_DEBUG_FS + struct dentry *dbg_root; + struct socinfo_params info; +#endif /* CONFIG_DEBUG_FS */ +}; + +struct soc_id { + unsigned int id; + const char *name; +}; + +static const struct soc_id soc_id[] = { + { 87, "MSM8960" }, + { 109, "APQ8064" }, + { 122, "MSM8660A" }, + { 123, "MSM8260A" }, + { 124, "APQ8060A" }, + { 126, "MSM8974" }, + { 130, "MPQ8064" }, + { 138, "MSM8960AB" }, + { 139, "APQ8060AB" }, + { 140, "MSM8260AB" }, + { 141, "MSM8660AB" }, + { 178, "APQ8084" }, + { 184, "APQ8074" }, + { 185, "MSM8274" }, + { 186, "MSM8674" }, + { 194, "MSM8974PRO" }, + { 206, "MSM8916" }, + { 208, "APQ8074-AA" }, + { 209, "APQ8074-AB" }, + { 210, "APQ8074PRO" }, + { 211, "MSM8274-AA" }, + { 212, "MSM8274-AB" }, + { 213, "MSM8274PRO" }, + { 214, "MSM8674-AA" }, + { 215, "MSM8674-AB" }, + { 216, "MSM8674PRO" }, + { 217, "MSM8974-AA" }, + { 218, "MSM8974-AB" }, + { 246, "MSM8996" }, + { 247, "APQ8016" }, + { 248, "MSM8216" }, + { 249, "MSM8116" }, + { 250, "MSM8616" }, + { 291, "APQ8096" }, + { 305, "MSM8996SG" }, + { 310, "MSM8996AU" }, + { 311, "APQ8096AU" }, + { 312, "APQ8096SG" }, + { 321, "SDM845" }, + { 341, "SDA845" }, +}; + +static const char *socinfo_machine(struct device *dev, unsigned int id) +{ + int idx; + + for (idx = 0; idx < ARRAY_SIZE(soc_id); idx++) { + if (soc_id[idx].id == id) + return soc_id[idx].name; + } + + return NULL; +} + +#ifdef CONFIG_DEBUG_FS + +#define QCOM_OPEN(name, _func) \ +static int qcom_open_##name(struct inode *inode, struct file *file) \ +{ \ + return single_open(file, _func, inode->i_private); \ +} \ + \ +static const struct file_operations qcom_ ##name## _ops = { \ + .open = qcom_open_##name, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ +} + +#define DEBUGFS_ADD(info, name) \ + debugfs_create_file(__stringify(name), 0400, \ + qcom_socinfo->dbg_root, \ + info, &qcom_ ##name## _ops) + + +static int qcom_show_build_id(struct seq_file *seq, void *p) +{ + struct socinfo *socinfo = seq->private; + + seq_printf(seq, "%s\n", socinfo->build_id); + + return 0; +} + +static int qcom_show_pmic_model(struct seq_file *seq, void *p) +{ + struct socinfo *socinfo = seq->private; + int model = SOCINFO_MINOR(le32_to_cpu(socinfo->pmic_model)); + + if (model < 0) + return -EINVAL; + + seq_printf(seq, "%s\n", pmic_models[model]); + + return 0; +} + +static int qcom_show_pmic_die_revision(struct seq_file *seq, void *p) +{ + struct socinfo *socinfo = seq->private; + + seq_printf(seq, "%u.%u\n", + SOCINFO_MAJOR(le32_to_cpu(socinfo->pmic_die_rev)), + SOCINFO_MINOR(le32_to_cpu(socinfo->pmic_die_rev))); + + return 0; +} + +QCOM_OPEN(build_id, qcom_show_build_id); +QCOM_OPEN(pmic_model, qcom_show_pmic_model); +QCOM_OPEN(pmic_die_rev, qcom_show_pmic_die_revision); + +#define DEFINE_IMAGE_OPS(type) \ +static int show_image_##type(struct seq_file *seq, void *p) \ +{ \ + struct smem_image_version *image_version = seq->private; \ + seq_puts(seq, image_version->type); \ + seq_puts(seq, "\n"); \ + return 0; \ +} \ +static int open_image_##type(struct inode *inode, struct file *file) \ +{ \ + return single_open(file, show_image_##type, inode->i_private); \ +} \ + \ +static const struct file_operations qcom_image_##type##_ops = { \ + .open = open_image_##type, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release, \ +} + +DEFINE_IMAGE_OPS(name); +DEFINE_IMAGE_OPS(variant); +DEFINE_IMAGE_OPS(oem); + +static void socinfo_debugfs_init(struct qcom_socinfo *qcom_socinfo, + struct socinfo *info) +{ + struct smem_image_version *versions; + struct dentry *dentry; + size_t size; + int i; + + qcom_socinfo->dbg_root = debugfs_create_dir("qcom_socinfo", NULL); + + qcom_socinfo->info.fmt = __le32_to_cpu(info->fmt); + + switch (qcom_socinfo->info.fmt) { + case SOCINFO_VERSION(0, 12): + qcom_socinfo->info.chip_family = + __le32_to_cpu(info->chip_family); + qcom_socinfo->info.raw_device_family = + __le32_to_cpu(info->raw_device_family); + qcom_socinfo->info.raw_device_num = + __le32_to_cpu(info->raw_device_num); + + debugfs_create_x32("chip_family", 0400, qcom_socinfo->dbg_root, + &qcom_socinfo->info.chip_family); + debugfs_create_x32("raw_device_family", 0400, + qcom_socinfo->dbg_root, + &qcom_socinfo->info.raw_device_family); + debugfs_create_x32("raw_device_number", 0400, + qcom_socinfo->dbg_root, + &qcom_socinfo->info.raw_device_num); + /* Fall through */ + case SOCINFO_VERSION(0, 11): + case SOCINFO_VERSION(0, 10): + case SOCINFO_VERSION(0, 9): + qcom_socinfo->info.foundry_id = __le32_to_cpu(info->foundry_id); + + debugfs_create_u32("foundry_id", 0400, qcom_socinfo->dbg_root, + &qcom_socinfo->info.foundry_id); + /* Fall through */ + case SOCINFO_VERSION(0, 8): + case SOCINFO_VERSION(0, 7): + DEBUGFS_ADD(info, pmic_model); + DEBUGFS_ADD(info, pmic_die_rev); + /* Fall through */ + case SOCINFO_VERSION(0, 6): + qcom_socinfo->info.hw_plat_subtype = + __le32_to_cpu(info->hw_plat_subtype); + + debugfs_create_u32("hardware_platform_subtype", 0400, + qcom_socinfo->dbg_root, + &qcom_socinfo->info.hw_plat_subtype); + /* Fall through */ + case SOCINFO_VERSION(0, 5): + qcom_socinfo->info.accessory_chip = + __le32_to_cpu(info->accessory_chip); + + debugfs_create_u32("accessory_chip", 0400, + qcom_socinfo->dbg_root, + &qcom_socinfo->info.accessory_chip); + /* Fall through */ + case SOCINFO_VERSION(0, 4): + qcom_socinfo->info.plat_ver = __le32_to_cpu(info->plat_ver); + + debugfs_create_u32("platform_version", 0400, + qcom_socinfo->dbg_root, + &qcom_socinfo->info.plat_ver); + /* Fall through */ + case SOCINFO_VERSION(0, 3): + qcom_socinfo->info.hw_plat = __le32_to_cpu(info->hw_plat); + + debugfs_create_u32("hardware_platform", 0400, + qcom_socinfo->dbg_root, + &qcom_socinfo->info.hw_plat); + /* Fall through */ + case SOCINFO_VERSION(0, 2): + qcom_socinfo->info.raw_ver = __le32_to_cpu(info->raw_ver); + + debugfs_create_u32("raw_version", 0400, qcom_socinfo->dbg_root, + &qcom_socinfo->info.raw_ver); + /* Fall through */ + case SOCINFO_VERSION(0, 1): + DEBUGFS_ADD(info, build_id); + break; + } + + versions = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_IMAGE_VERSION_TABLE, + &size); + + for (i = 0; i < ARRAY_SIZE(socinfo_image_names); i++) { + if (!socinfo_image_names[i]) + continue; + + dentry = debugfs_create_dir(socinfo_image_names[i], + qcom_socinfo->dbg_root); + debugfs_create_file("name", 0400, dentry, &versions[i], + &qcom_image_name_ops); + debugfs_create_file("variant", 0400, dentry, &versions[i], + &qcom_image_variant_ops); + debugfs_create_file("oem", 0400, dentry, &versions[i], + &qcom_image_oem_ops); + } +} + +static void socinfo_debugfs_exit(struct qcom_socinfo *qcom_socinfo) +{ + debugfs_remove_recursive(qcom_socinfo->dbg_root); +} +#else +static void socinfo_debugfs_init(struct qcom_socinfo *qcom_socinfo, + struct socinfo *info) +{ +} +static void socinfo_debugfs_exit(struct qcom_socinfo *qcom_socinfo) { } +#endif /* CONFIG_DEBUG_FS */ + +static int qcom_socinfo_probe(struct platform_device *pdev) +{ + struct qcom_socinfo *qs; + struct socinfo *info; + size_t item_size; + + info = qcom_smem_get(QCOM_SMEM_HOST_ANY, SMEM_HW_SW_BUILD_ID, + &item_size); + if (IS_ERR(info)) { + dev_err(&pdev->dev, "Couldn't find socinfo\n"); + return PTR_ERR(info); + } + + qs = devm_kzalloc(&pdev->dev, sizeof(*qs), GFP_KERNEL); + if (!qs) + return -ENOMEM; + + qs->attr.family = "Snapdragon"; + qs->attr.machine = socinfo_machine(&pdev->dev, + le32_to_cpu(info->id)); + qs->attr.revision = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%u.%u", + SOCINFO_MAJOR(le32_to_cpu(info->ver)), + SOCINFO_MINOR(le32_to_cpu(info->ver))); + if (offsetof(struct socinfo, serial_num) <= item_size) + qs->attr.serial_number = devm_kasprintf(&pdev->dev, GFP_KERNEL, + "%u", + le32_to_cpu(info->serial_num)); + + qs->soc_dev = soc_device_register(&qs->attr); + if (IS_ERR(qs->soc_dev)) + return PTR_ERR(qs->soc_dev); + + socinfo_debugfs_init(qs, info); + + /* Feed the soc specific unique data into entropy pool */ + add_device_randomness(info, item_size); + + platform_set_drvdata(pdev, qs->soc_dev); + + return 0; +} + +static int qcom_socinfo_remove(struct platform_device *pdev) +{ + struct qcom_socinfo *qs = platform_get_drvdata(pdev); + + soc_device_unregister(qs->soc_dev); + + socinfo_debugfs_exit(qs); + + return 0; +} + +static struct platform_driver qcom_socinfo_driver = { + .probe = qcom_socinfo_probe, + .remove = qcom_socinfo_remove, + .driver = { + .name = "qcom-socinfo", + }, +}; + +module_platform_driver(qcom_socinfo_driver); + +MODULE_DESCRIPTION("Qualcomm SoCinfo driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:qcom-socinfo"); |