From b9472f7d8224460499dd7128ec944735ed5345a0 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Mon, 18 Feb 2019 22:43:09 +0100 Subject: firmware: xilinx: fix debugfs write handler - Userspace wants to write a string with `len` bytes, not counting the terminating NULL, so we should allocate `len+1` bytes. It looks like the current code relied on having a nullbyte directly behind `kern_buff`, which happens to work reliably as long as `len` isn't one of the kmalloc size classes. - strncpy_from_user() is completely wrong here; userspace is giving us a (not necessarily null-terminated) buffer and its length. strncpy_from_user() is for cases in which we don't know the length. - Don't let broken userspace allocate arbitrarily big kmalloc allocations. Just use memdup_user_nul(), which is designed precisely for things like this. Signed-off-by: Jann Horn Acked-by: Jolly Shah Signed-off-by: Michal Simek --- drivers/firmware/xilinx/zynqmp-debug.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/xilinx/zynqmp-debug.c b/drivers/firmware/xilinx/zynqmp-debug.c index 2771df6df379..90b66cdbfd58 100644 --- a/drivers/firmware/xilinx/zynqmp-debug.c +++ b/drivers/firmware/xilinx/zynqmp-debug.c @@ -163,21 +163,14 @@ static ssize_t zynqmp_pm_debugfs_api_write(struct file *file, strcpy(debugfs_buf, ""); - if (*off != 0 || len == 0) + if (*off != 0 || len <= 1 || len > PAGE_SIZE - 1) return -EINVAL; - kern_buff = kzalloc(len, GFP_KERNEL); - if (!kern_buff) - return -ENOMEM; - + kern_buff = memdup_user_nul(ptr, len); + if (IS_ERR(kern_buff)) + return PTR_ERR(kern_buff); tmp_buff = kern_buff; - ret = strncpy_from_user(kern_buff, ptr, len); - if (ret < 0) { - ret = -EFAULT; - goto err; - } - /* Read the API name from a user request */ pm_api_req = strsep(&kern_buff, " "); -- cgit v1.2.3 From 3d0313786470acb414b7d5fdd2202f061acffb02 Mon Sep 17 00:00:00 2001 From: Rajan Vaja Date: Mon, 4 Mar 2019 15:18:08 -0800 Subject: drivers: Defer probe if firmware is not ready Driver needs ZynqMP firmware interface to call EEMI APIs. In case firmware is not ready, dependent drivers should wait until the firmware is ready. Signed-off-by: Rajan Vaja Signed-off-by: Jolly Shah Signed-off-by: Michal Simek --- Documentation/xilinx/eemi.txt | 4 ++-- drivers/clk/zynqmp/clkc.c | 4 ++-- drivers/firmware/xilinx/zynqmp-debug.c | 3 --- drivers/firmware/xilinx/zynqmp.c | 11 ++++++++++- drivers/nvmem/zynqmp_nvmem.c | 10 +++++++--- drivers/reset/reset-zynqmp.c | 8 ++++---- drivers/soc/xilinx/zynqmp_pm_domains.c | 18 ++++++++++-------- drivers/soc/xilinx/zynqmp_power.c | 10 ++++++---- drivers/spi/spi-zynqmp-gqspi.c | 5 +++++ include/linux/firmware/xlnx-zynqmp.h | 2 +- 10 files changed, 47 insertions(+), 28 deletions(-) (limited to 'drivers/firmware') diff --git a/Documentation/xilinx/eemi.txt b/Documentation/xilinx/eemi.txt index 0ab686c173be..5f39b4ffdcd4 100644 --- a/Documentation/xilinx/eemi.txt +++ b/Documentation/xilinx/eemi.txt @@ -41,8 +41,8 @@ Example of EEMI ops usage: int ret; eemi_ops = zynqmp_pm_get_eemi_ops(); - if (!eemi_ops) - return -ENXIO; + if (IS_ERR(eemi_ops)) + return PTR_ERR(eemi_ops); ret = eemi_ops->query_data(qdata, ret_payload); diff --git a/drivers/clk/zynqmp/clkc.c b/drivers/clk/zynqmp/clkc.c index b0908ec62f73..2b1d43457f09 100644 --- a/drivers/clk/zynqmp/clkc.c +++ b/drivers/clk/zynqmp/clkc.c @@ -695,8 +695,8 @@ static int zynqmp_clock_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; eemi_ops = zynqmp_pm_get_eemi_ops(); - if (!eemi_ops) - return -ENXIO; + if (IS_ERR(eemi_ops)) + return PTR_ERR(eemi_ops); ret = zynqmp_clk_setup(dev->of_node); diff --git a/drivers/firmware/xilinx/zynqmp-debug.c b/drivers/firmware/xilinx/zynqmp-debug.c index 90b66cdbfd58..c6d0724da4db 100644 --- a/drivers/firmware/xilinx/zynqmp-debug.c +++ b/drivers/firmware/xilinx/zynqmp-debug.c @@ -90,9 +90,6 @@ static int process_api_request(u32 pm_id, u64 *pm_api_arg, u32 *pm_api_ret) int ret; struct zynqmp_pm_query_data qdata = {0}; - if (!eemi_ops) - return -ENXIO; - switch (pm_id) { case PM_GET_API_VERSION: ret = eemi_ops->get_api_version(&pm_api_version); diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 98f936125643..87a1d636c0dc 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -24,6 +24,8 @@ #include #include "zynqmp-debug.h" +static const struct zynqmp_eemi_ops *eemi_ops_tbl; + static const struct mfd_cell firmware_devs[] = { { .name = "zynqmp_power_controller", @@ -649,7 +651,11 @@ static const struct zynqmp_eemi_ops eemi_ops = { */ const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void) { - return &eemi_ops; + if (eemi_ops_tbl) + return eemi_ops_tbl; + else + return ERR_PTR(-EPROBE_DEFER); + } EXPORT_SYMBOL_GPL(zynqmp_pm_get_eemi_ops); @@ -694,6 +700,9 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) pr_info("%s Trustzone version v%d.%d\n", __func__, pm_tz_version >> 16, pm_tz_version & 0xFFFF); + /* Assign eemi_ops_table */ + eemi_ops_tbl = &eemi_ops; + zynqmp_pm_api_debugfs_init(); ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, firmware_devs, diff --git a/drivers/nvmem/zynqmp_nvmem.c b/drivers/nvmem/zynqmp_nvmem.c index 490c8fcaec80..5893543918c8 100644 --- a/drivers/nvmem/zynqmp_nvmem.c +++ b/drivers/nvmem/zynqmp_nvmem.c @@ -16,6 +16,8 @@ struct zynqmp_nvmem_data { struct nvmem_device *nvmem; }; +static const struct zynqmp_eemi_ops *eemi_ops; + static int zynqmp_nvmem_read(void *context, unsigned int offset, void *val, size_t bytes) { @@ -23,9 +25,7 @@ static int zynqmp_nvmem_read(void *context, unsigned int offset, int idcode, version; struct zynqmp_nvmem_data *priv = context; - const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); - - if (!eemi_ops || !eemi_ops->get_chipid) + if (!eemi_ops->get_chipid) return -ENXIO; ret = eemi_ops->get_chipid(&idcode, &version); @@ -61,6 +61,10 @@ static int zynqmp_nvmem_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; + eemi_ops = zynqmp_pm_get_eemi_ops(); + if (IS_ERR(eemi_ops)) + return PTR_ERR(eemi_ops); + priv->dev = dev; econfig.dev = dev; econfig.reg_read = zynqmp_nvmem_read; diff --git a/drivers/reset/reset-zynqmp.c b/drivers/reset/reset-zynqmp.c index 2ef1f13aa47b..99e75d92dada 100644 --- a/drivers/reset/reset-zynqmp.c +++ b/drivers/reset/reset-zynqmp.c @@ -79,11 +79,11 @@ static int zynqmp_reset_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - platform_set_drvdata(pdev, priv); - priv->eemi_ops = zynqmp_pm_get_eemi_ops(); - if (!priv->eemi_ops) - return -ENXIO; + if (IS_ERR(priv->eemi_ops)) + return PTR_ERR(priv->eemi_ops); + + platform_set_drvdata(pdev, priv); priv->rcdev.ops = &zynqmp_reset_ops; priv->rcdev.owner = THIS_MODULE; diff --git a/drivers/soc/xilinx/zynqmp_pm_domains.c b/drivers/soc/xilinx/zynqmp_pm_domains.c index 354d256e6e00..600f57cf0c2e 100644 --- a/drivers/soc/xilinx/zynqmp_pm_domains.c +++ b/drivers/soc/xilinx/zynqmp_pm_domains.c @@ -23,6 +23,8 @@ /* Flag stating if PM nodes mapped to the PM domain has been requested */ #define ZYNQMP_PM_DOMAIN_REQUESTED BIT(0) +static const struct zynqmp_eemi_ops *eemi_ops; + /** * struct zynqmp_pm_domain - Wrapper around struct generic_pm_domain * @gpd: Generic power domain @@ -71,9 +73,8 @@ static int zynqmp_gpd_power_on(struct generic_pm_domain *domain) { int ret; struct zynqmp_pm_domain *pd; - const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); - if (!eemi_ops || !eemi_ops->set_requirement) + if (!eemi_ops->set_requirement) return -ENXIO; pd = container_of(domain, struct zynqmp_pm_domain, gpd); @@ -107,9 +108,8 @@ static int zynqmp_gpd_power_off(struct generic_pm_domain *domain) struct zynqmp_pm_domain *pd; u32 capabilities = 0; bool may_wakeup; - const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); - if (!eemi_ops || !eemi_ops->set_requirement) + if (!eemi_ops->set_requirement) return -ENXIO; pd = container_of(domain, struct zynqmp_pm_domain, gpd); @@ -160,9 +160,8 @@ static int zynqmp_gpd_attach_dev(struct generic_pm_domain *domain, { int ret; struct zynqmp_pm_domain *pd; - const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); - if (!eemi_ops || !eemi_ops->request_node) + if (!eemi_ops->request_node) return -ENXIO; pd = container_of(domain, struct zynqmp_pm_domain, gpd); @@ -197,9 +196,8 @@ static void zynqmp_gpd_detach_dev(struct generic_pm_domain *domain, { int ret; struct zynqmp_pm_domain *pd; - const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); - if (!eemi_ops || !eemi_ops->release_node) + if (!eemi_ops->release_node) return; pd = container_of(domain, struct zynqmp_pm_domain, gpd); @@ -266,6 +264,10 @@ static int zynqmp_gpd_probe(struct platform_device *pdev) struct zynqmp_pm_domain *pd; struct device *dev = &pdev->dev; + eemi_ops = zynqmp_pm_get_eemi_ops(); + if (IS_ERR(eemi_ops)) + return PTR_ERR(eemi_ops); + pd = devm_kcalloc(dev, ZYNQMP_NUM_DOMAINS, sizeof(*pd), GFP_KERNEL); if (!pd) return -ENOMEM; diff --git a/drivers/soc/xilinx/zynqmp_power.c b/drivers/soc/xilinx/zynqmp_power.c index 771cb59b9d22..1b9d14411a15 100644 --- a/drivers/soc/xilinx/zynqmp_power.c +++ b/drivers/soc/xilinx/zynqmp_power.c @@ -31,6 +31,7 @@ static const char *const suspend_modes[] = { }; static enum pm_suspend_mode suspend_mode = PM_SUSPEND_MODE_STD; +static const struct zynqmp_eemi_ops *eemi_ops; enum pm_api_cb_id { PM_INIT_SUSPEND_CB = 30, @@ -92,9 +93,8 @@ static ssize_t suspend_mode_store(struct device *dev, const char *buf, size_t count) { int md, ret = -EINVAL; - const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); - if (!eemi_ops || !eemi_ops->set_suspend_mode) + if (!eemi_ops->set_suspend_mode) return ret; for (md = PM_SUSPEND_MODE_FIRST; md < ARRAY_SIZE(suspend_modes); md++) @@ -120,9 +120,11 @@ static int zynqmp_pm_probe(struct platform_device *pdev) int ret, irq; u32 pm_api_version; - const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); + eemi_ops = zynqmp_pm_get_eemi_ops(); + if (IS_ERR(eemi_ops)) + return PTR_ERR(eemi_ops); - if (!eemi_ops || !eemi_ops->get_api_version || !eemi_ops->init_finalize) + if (!eemi_ops->get_api_version || !eemi_ops->init_finalize) return -ENXIO; eemi_ops->init_finalize(); diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c index 9f83e1b17aa1..d07b6f940f9f 100644 --- a/drivers/spi/spi-zynqmp-gqspi.c +++ b/drivers/spi/spi-zynqmp-gqspi.c @@ -138,6 +138,7 @@ #define SPI_AUTOSUSPEND_TIMEOUT 3000 enum mode_type {GQSPI_MODE_IO, GQSPI_MODE_DMA}; +static const struct zynqmp_eemi_ops *eemi_ops; /** * struct zynqmp_qspi - Defines qspi driver instance @@ -1021,6 +1022,10 @@ static int zynqmp_qspi_probe(struct platform_device *pdev) struct resource *res; struct device *dev = &pdev->dev; + eemi_ops = zynqmp_pm_get_eemi_ops(); + if (IS_ERR(eemi_ops)) + return PTR_ERR(eemi_ops); + master = spi_alloc_master(&pdev->dev, sizeof(*xqspi)); if (!master) return -ENOMEM; diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index 642dab10f65d..3533ee557043 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -293,7 +293,7 @@ const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void); #else static inline struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void) { - return NULL; + return ERR_PTR(-ENODEV); } #endif -- cgit v1.2.3 From 9d616d62faefd573f6eaf687f6c83a872708afcf Mon Sep 17 00:00:00 2001 From: Aisheng Dong Date: Wed, 20 Feb 2019 14:38:27 +0000 Subject: firmware: imx: scu-pd: use bool to set postfix Using bool instead 0/1 to indicate whether adding a postfix for domain names which can improve the code readability and less confusing. Cc: Ulf Hansson Cc: Shawn Guo Cc: Sascha Hauer Cc: "Rafael J. Wysocki" Cc: Kevin Hilman Cc: linux-pm@vger.kernel.org Signed-off-by: Dong Aisheng Signed-off-by: Shawn Guo --- drivers/firmware/imx/scu-pd.c | 102 +++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 51 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c index 39a94c7177fc..e7802ec591c2 100644 --- a/drivers/firmware/imx/scu-pd.c +++ b/drivers/firmware/imx/scu-pd.c @@ -84,71 +84,71 @@ struct imx_sc_pd_soc { static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { /* LSIO SS */ - { "lsio-pwm", IMX_SC_R_PWM_0, 8, 1 }, - { "lsio-gpio", IMX_SC_R_GPIO_0, 8, 1 }, - { "lsio-gpt", IMX_SC_R_GPT_0, 5, 1 }, - { "lsio-kpp", IMX_SC_R_KPP, 1, 0 }, - { "lsio-fspi", IMX_SC_R_FSPI_0, 2, 1 }, - { "lsio-mu", IMX_SC_R_MU_0A, 14, 1 }, + { "lsio-pwm", IMX_SC_R_PWM_0, 8, true }, + { "lsio-gpio", IMX_SC_R_GPIO_0, 8, true }, + { "lsio-gpt", IMX_SC_R_GPT_0, 5, true }, + { "lsio-kpp", IMX_SC_R_KPP, 1, false }, + { "lsio-fspi", IMX_SC_R_FSPI_0, 2, true }, + { "lsio-mu", IMX_SC_R_MU_0A, 14, true }, /* CONN SS */ - { "con-usb", IMX_SC_R_USB_0, 2, 1 }, - { "con-usb0phy", IMX_SC_R_USB_0_PHY, 1, 0 }, - { "con-usb2", IMX_SC_R_USB_2, 1, 0 }, - { "con-usb2phy", IMX_SC_R_USB_2_PHY, 1, 0 }, - { "con-sdhc", IMX_SC_R_SDHC_0, 3, 1 }, - { "con-enet", IMX_SC_R_ENET_0, 2, 1 }, - { "con-nand", IMX_SC_R_NAND, 1, 0 }, - { "con-mlb", IMX_SC_R_MLB_0, 1, 1 }, + { "con-usb", IMX_SC_R_USB_0, 2, true }, + { "con-usb0phy", IMX_SC_R_USB_0_PHY, 1, false }, + { "con-usb2", IMX_SC_R_USB_2, 1, false }, + { "con-usb2phy", IMX_SC_R_USB_2_PHY, 1, false }, + { "con-sdhc", IMX_SC_R_SDHC_0, 3, true }, + { "con-enet", IMX_SC_R_ENET_0, 2, true }, + { "con-nand", IMX_SC_R_NAND, 1, false }, + { "con-mlb", IMX_SC_R_MLB_0, 1, true }, /* Audio DMA SS */ - { "adma-audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, 0 }, - { "adma-audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, 0 }, - { "adma-audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, 0 }, - { "adma-dma0-ch", IMX_SC_R_DMA_0_CH0, 16, 1 }, - { "adma-dma1-ch", IMX_SC_R_DMA_1_CH0, 16, 1 }, - { "adma-dma2-ch", IMX_SC_R_DMA_2_CH0, 5, 1 }, - { "adma-asrc0", IMX_SC_R_ASRC_0, 1, 0 }, - { "adma-asrc1", IMX_SC_R_ASRC_1, 1, 0 }, - { "adma-esai0", IMX_SC_R_ESAI_0, 1, 0 }, - { "adma-spdif0", IMX_SC_R_SPDIF_0, 1, 0 }, - { "adma-sai", IMX_SC_R_SAI_0, 3, 1 }, - { "adma-amix", IMX_SC_R_AMIX, 1, 0 }, - { "adma-mqs0", IMX_SC_R_MQS_0, 1, 0 }, - { "adma-dsp", IMX_SC_R_DSP, 1, 0 }, - { "adma-dsp-ram", IMX_SC_R_DSP_RAM, 1, 0 }, - { "adma-can", IMX_SC_R_CAN_0, 3, 1 }, - { "adma-ftm", IMX_SC_R_FTM_0, 2, 1 }, - { "adma-lpi2c", IMX_SC_R_I2C_0, 4, 1 }, - { "adma-adc", IMX_SC_R_ADC_0, 1, 1 }, - { "adma-lcd", IMX_SC_R_LCD_0, 1, 1 }, - { "adma-lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, 1 }, - { "adma-lpuart", IMX_SC_R_UART_0, 4, 1 }, - { "adma-lpspi", IMX_SC_R_SPI_0, 4, 1 }, + { "adma-audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false }, + { "adma-audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false }, + { "adma-audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false }, + { "adma-dma0-ch", IMX_SC_R_DMA_0_CH0, 16, true }, + { "adma-dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true }, + { "adma-dma2-ch", IMX_SC_R_DMA_2_CH0, 5, true }, + { "adma-asrc0", IMX_SC_R_ASRC_0, 1, false }, + { "adma-asrc1", IMX_SC_R_ASRC_1, 1, false }, + { "adma-esai0", IMX_SC_R_ESAI_0, 1, false }, + { "adma-spdif0", IMX_SC_R_SPDIF_0, 1, false }, + { "adma-sai", IMX_SC_R_SAI_0, 3, true }, + { "adma-amix", IMX_SC_R_AMIX, 1, false }, + { "adma-mqs0", IMX_SC_R_MQS_0, 1, false }, + { "adma-dsp", IMX_SC_R_DSP, 1, false }, + { "adma-dsp-ram", IMX_SC_R_DSP_RAM, 1, false }, + { "adma-can", IMX_SC_R_CAN_0, 3, true }, + { "adma-ftm", IMX_SC_R_FTM_0, 2, true }, + { "adma-lpi2c", IMX_SC_R_I2C_0, 4, true }, + { "adma-adc", IMX_SC_R_ADC_0, 1, true }, + { "adma-lcd", IMX_SC_R_LCD_0, 1, true }, + { "adma-lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true }, + { "adma-lpuart", IMX_SC_R_UART_0, 4, true }, + { "adma-lpspi", IMX_SC_R_SPI_0, 4, true }, /* VPU SS */ - { "vpu", IMX_SC_R_VPU, 1, 0 }, - { "vpu-pid", IMX_SC_R_VPU_PID0, 8, 1 }, - { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, 0 }, - { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, 0 }, + { "vpu", IMX_SC_R_VPU, 1, false }, + { "vpu-pid", IMX_SC_R_VPU_PID0, 8, true }, + { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, false }, + { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, false }, /* GPU SS */ - { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, 1 }, + { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true }, /* HSIO SS */ - { "hsio-pcie-b", IMX_SC_R_PCIE_B, 1, 0 }, - { "hsio-serdes-1", IMX_SC_R_SERDES_1, 1, 0 }, - { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, 0 }, + { "hsio-pcie-b", IMX_SC_R_PCIE_B, 1, false }, + { "hsio-serdes-1", IMX_SC_R_SERDES_1, 1, false }, + { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, false }, /* MIPI/LVDS SS */ - { "mipi0", IMX_SC_R_MIPI_0, 1, 0 }, - { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, 0 }, - { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, 1 }, - { "lvds0", IMX_SC_R_LVDS_0, 1, 0 }, + { "mipi0", IMX_SC_R_MIPI_0, 1, false }, + { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, false }, + { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, true }, + { "lvds0", IMX_SC_R_LVDS_0, 1, false }, /* DC SS */ - { "dc0", IMX_SC_R_DC_0, 1, 0 }, - { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, 1 }, + { "dc0", IMX_SC_R_DC_0, 1, false }, + { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true }, }; static const struct imx_sc_pd_soc imx8qxp_scu_pd = { -- cgit v1.2.3 From ad8cc071c557b075b923bf27aee8a7dae7338f5e Mon Sep 17 00:00:00 2001 From: Aisheng Dong Date: Wed, 20 Feb 2019 14:38:32 +0000 Subject: firmware: imx: scu-pd: add specifying the base of domain name index support As the domain resource id in the same type may not be continuous, so it's hard to describe all such power domains with current struct imx_sc_pd_range. Adding the optional base for domain name index to address this issue. Then we can add the discrete domains easily later. Cc: Ulf Hansson Cc: Shawn Guo Cc: Sascha Hauer Cc: "Rafael J. Wysocki" Cc: Kevin Hilman Cc: linux-pm@vger.kernel.org Signed-off-by: Dong Aisheng Signed-off-by: Shawn Guo --- drivers/firmware/imx/scu-pd.c | 107 ++++++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 52 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c index e7802ec591c2..8a2b6ada58ad 100644 --- a/drivers/firmware/imx/scu-pd.c +++ b/drivers/firmware/imx/scu-pd.c @@ -74,7 +74,10 @@ struct imx_sc_pd_range { char *name; u32 rsrc; u8 num; + + /* add domain index */ bool postfix; + u8 start_from; }; struct imx_sc_pd_soc { @@ -84,71 +87,71 @@ struct imx_sc_pd_soc { static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { /* LSIO SS */ - { "lsio-pwm", IMX_SC_R_PWM_0, 8, true }, - { "lsio-gpio", IMX_SC_R_GPIO_0, 8, true }, - { "lsio-gpt", IMX_SC_R_GPT_0, 5, true }, - { "lsio-kpp", IMX_SC_R_KPP, 1, false }, - { "lsio-fspi", IMX_SC_R_FSPI_0, 2, true }, - { "lsio-mu", IMX_SC_R_MU_0A, 14, true }, + { "lsio-pwm", IMX_SC_R_PWM_0, 8, true, 0 }, + { "lsio-gpio", IMX_SC_R_GPIO_0, 8, true, 0 }, + { "lsio-gpt", IMX_SC_R_GPT_0, 5, true, 0 }, + { "lsio-kpp", IMX_SC_R_KPP, 1, false, 0 }, + { "lsio-fspi", IMX_SC_R_FSPI_0, 2, true, 0 }, + { "lsio-mu", IMX_SC_R_MU_0A, 14, true, 0 }, /* CONN SS */ - { "con-usb", IMX_SC_R_USB_0, 2, true }, - { "con-usb0phy", IMX_SC_R_USB_0_PHY, 1, false }, - { "con-usb2", IMX_SC_R_USB_2, 1, false }, - { "con-usb2phy", IMX_SC_R_USB_2_PHY, 1, false }, - { "con-sdhc", IMX_SC_R_SDHC_0, 3, true }, - { "con-enet", IMX_SC_R_ENET_0, 2, true }, - { "con-nand", IMX_SC_R_NAND, 1, false }, - { "con-mlb", IMX_SC_R_MLB_0, 1, true }, + { "con-usb", IMX_SC_R_USB_0, 2, true, 0 }, + { "con-usb0phy", IMX_SC_R_USB_0_PHY, 1, false, 0 }, + { "con-usb2", IMX_SC_R_USB_2, 1, false, 0 }, + { "con-usb2phy", IMX_SC_R_USB_2_PHY, 1, false, 0 }, + { "con-sdhc", IMX_SC_R_SDHC_0, 3, true, 0 }, + { "con-enet", IMX_SC_R_ENET_0, 2, true, 0 }, + { "con-nand", IMX_SC_R_NAND, 1, false, 0 }, + { "con-mlb", IMX_SC_R_MLB_0, 1, true, 0 }, /* Audio DMA SS */ - { "adma-audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false }, - { "adma-audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false }, - { "adma-audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false }, - { "adma-dma0-ch", IMX_SC_R_DMA_0_CH0, 16, true }, - { "adma-dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true }, - { "adma-dma2-ch", IMX_SC_R_DMA_2_CH0, 5, true }, - { "adma-asrc0", IMX_SC_R_ASRC_0, 1, false }, - { "adma-asrc1", IMX_SC_R_ASRC_1, 1, false }, - { "adma-esai0", IMX_SC_R_ESAI_0, 1, false }, - { "adma-spdif0", IMX_SC_R_SPDIF_0, 1, false }, - { "adma-sai", IMX_SC_R_SAI_0, 3, true }, - { "adma-amix", IMX_SC_R_AMIX, 1, false }, - { "adma-mqs0", IMX_SC_R_MQS_0, 1, false }, - { "adma-dsp", IMX_SC_R_DSP, 1, false }, - { "adma-dsp-ram", IMX_SC_R_DSP_RAM, 1, false }, - { "adma-can", IMX_SC_R_CAN_0, 3, true }, - { "adma-ftm", IMX_SC_R_FTM_0, 2, true }, - { "adma-lpi2c", IMX_SC_R_I2C_0, 4, true }, - { "adma-adc", IMX_SC_R_ADC_0, 1, true }, - { "adma-lcd", IMX_SC_R_LCD_0, 1, true }, - { "adma-lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true }, - { "adma-lpuart", IMX_SC_R_UART_0, 4, true }, - { "adma-lpspi", IMX_SC_R_SPI_0, 4, true }, + { "adma-audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false, 0 }, + { "adma-audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false, 0 }, + { "adma-audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false, 0 }, + { "adma-dma0-ch", IMX_SC_R_DMA_0_CH0, 16, true, 0 }, + { "adma-dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true, 0 }, + { "adma-dma2-ch", IMX_SC_R_DMA_2_CH0, 5, true, 0 }, + { "adma-asrc0", IMX_SC_R_ASRC_0, 1, false, 0 }, + { "adma-asrc1", IMX_SC_R_ASRC_1, 1, false, 0 }, + { "adma-esai0", IMX_SC_R_ESAI_0, 1, false, 0 }, + { "adma-spdif0", IMX_SC_R_SPDIF_0, 1, false, 0 }, + { "adma-sai", IMX_SC_R_SAI_0, 3, true, 0 }, + { "adma-amix", IMX_SC_R_AMIX, 1, false, 0 }, + { "adma-mqs0", IMX_SC_R_MQS_0, 1, false, 0 }, + { "adma-dsp", IMX_SC_R_DSP, 1, false, 0 }, + { "adma-dsp-ram", IMX_SC_R_DSP_RAM, 1, false, 0 }, + { "adma-can", IMX_SC_R_CAN_0, 3, true, 0 }, + { "adma-ftm", IMX_SC_R_FTM_0, 2, true, 0 }, + { "adma-lpi2c", IMX_SC_R_I2C_0, 4, true, 0 }, + { "adma-adc", IMX_SC_R_ADC_0, 1, true, 0 }, + { "adma-lcd", IMX_SC_R_LCD_0, 1, true, 0 }, + { "adma-lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 }, + { "adma-lpuart", IMX_SC_R_UART_0, 4, true, 0 }, + { "adma-lpspi", IMX_SC_R_SPI_0, 4, true, 0 }, /* VPU SS */ - { "vpu", IMX_SC_R_VPU, 1, false }, - { "vpu-pid", IMX_SC_R_VPU_PID0, 8, true }, - { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, false }, - { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, false }, + { "vpu", IMX_SC_R_VPU, 1, false, 0 }, + { "vpu-pid", IMX_SC_R_VPU_PID0, 8, true, 0 }, + { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, false, 0 }, + { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, false, 0 }, /* GPU SS */ - { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true }, + { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true, 0 }, /* HSIO SS */ - { "hsio-pcie-b", IMX_SC_R_PCIE_B, 1, false }, - { "hsio-serdes-1", IMX_SC_R_SERDES_1, 1, false }, - { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, false }, + { "hsio-pcie-b", IMX_SC_R_PCIE_B, 1, false, 0 }, + { "hsio-serdes-1", IMX_SC_R_SERDES_1, 1, false, 0 }, + { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, false, 0 }, /* MIPI/LVDS SS */ - { "mipi0", IMX_SC_R_MIPI_0, 1, false }, - { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, false }, - { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, true }, - { "lvds0", IMX_SC_R_LVDS_0, 1, false }, + { "mipi0", IMX_SC_R_MIPI_0, 1, false, 0 }, + { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, false, 0 }, + { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, true, 0 }, + { "lvds0", IMX_SC_R_LVDS_0, 1, false, 0 }, /* DC SS */ - { "dc0", IMX_SC_R_DC_0, 1, false }, - { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true }, + { "dc0", IMX_SC_R_DC_0, 1, false, 0 }, + { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true, 0 }, }; static const struct imx_sc_pd_soc imx8qxp_scu_pd = { @@ -236,7 +239,7 @@ imx_scu_add_pm_domain(struct device *dev, int idx, if (pd_ranges->postfix) snprintf(sc_pd->name, sizeof(sc_pd->name), - "%s%i", pd_ranges->name, idx); + "%s%i", pd_ranges->name, pd_ranges->start_from + idx); else snprintf(sc_pd->name, sizeof(sc_pd->name), "%s", pd_ranges->name); -- cgit v1.2.3 From 32654dad06e24e2909c64b4fc3d61689f5522975 Mon Sep 17 00:00:00 2001 From: Aisheng Dong Date: Wed, 20 Feb 2019 14:38:36 +0000 Subject: firmware: imx: scu-pd: decouple the SS information from domain names As resource power domain service is provided by SCU firmware, no SS information required. So we can remove the SS indicator from the domain names, then the domains defined can be better shared among different SCU based platforms. Cc: Ulf Hansson Cc: Shawn Guo Cc: Sascha Hauer Cc: "Rafael J. Wysocki" Cc: Kevin Hilman Cc: linux-pm@vger.kernel.org Signed-off-by: Dong Aisheng Signed-off-by: Shawn Guo --- drivers/firmware/imx/scu-pd.c | 92 ++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 44 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c index 8a2b6ada58ad..480cec69e2c9 100644 --- a/drivers/firmware/imx/scu-pd.c +++ b/drivers/firmware/imx/scu-pd.c @@ -87,49 +87,51 @@ struct imx_sc_pd_soc { static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { /* LSIO SS */ - { "lsio-pwm", IMX_SC_R_PWM_0, 8, true, 0 }, - { "lsio-gpio", IMX_SC_R_GPIO_0, 8, true, 0 }, - { "lsio-gpt", IMX_SC_R_GPT_0, 5, true, 0 }, - { "lsio-kpp", IMX_SC_R_KPP, 1, false, 0 }, - { "lsio-fspi", IMX_SC_R_FSPI_0, 2, true, 0 }, - { "lsio-mu", IMX_SC_R_MU_0A, 14, true, 0 }, + { "pwm", IMX_SC_R_PWM_0, 8, true, 0 }, + { "gpio", IMX_SC_R_GPIO_0, 8, true, 0 }, + { "gpt", IMX_SC_R_GPT_0, 5, true, 0 }, + { "kpp", IMX_SC_R_KPP, 1, false, 0 }, + { "fspi", IMX_SC_R_FSPI_0, 2, true, 0 }, + { "mu", IMX_SC_R_MU_0A, 14, true, 0 }, /* CONN SS */ - { "con-usb", IMX_SC_R_USB_0, 2, true, 0 }, - { "con-usb0phy", IMX_SC_R_USB_0_PHY, 1, false, 0 }, - { "con-usb2", IMX_SC_R_USB_2, 1, false, 0 }, - { "con-usb2phy", IMX_SC_R_USB_2_PHY, 1, false, 0 }, - { "con-sdhc", IMX_SC_R_SDHC_0, 3, true, 0 }, - { "con-enet", IMX_SC_R_ENET_0, 2, true, 0 }, - { "con-nand", IMX_SC_R_NAND, 1, false, 0 }, - { "con-mlb", IMX_SC_R_MLB_0, 1, true, 0 }, - - /* Audio DMA SS */ - { "adma-audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false, 0 }, - { "adma-audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false, 0 }, - { "adma-audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false, 0 }, - { "adma-dma0-ch", IMX_SC_R_DMA_0_CH0, 16, true, 0 }, - { "adma-dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true, 0 }, - { "adma-dma2-ch", IMX_SC_R_DMA_2_CH0, 5, true, 0 }, - { "adma-asrc0", IMX_SC_R_ASRC_0, 1, false, 0 }, - { "adma-asrc1", IMX_SC_R_ASRC_1, 1, false, 0 }, - { "adma-esai0", IMX_SC_R_ESAI_0, 1, false, 0 }, - { "adma-spdif0", IMX_SC_R_SPDIF_0, 1, false, 0 }, - { "adma-sai", IMX_SC_R_SAI_0, 3, true, 0 }, - { "adma-amix", IMX_SC_R_AMIX, 1, false, 0 }, - { "adma-mqs0", IMX_SC_R_MQS_0, 1, false, 0 }, - { "adma-dsp", IMX_SC_R_DSP, 1, false, 0 }, - { "adma-dsp-ram", IMX_SC_R_DSP_RAM, 1, false, 0 }, - { "adma-can", IMX_SC_R_CAN_0, 3, true, 0 }, - { "adma-ftm", IMX_SC_R_FTM_0, 2, true, 0 }, - { "adma-lpi2c", IMX_SC_R_I2C_0, 4, true, 0 }, - { "adma-adc", IMX_SC_R_ADC_0, 1, true, 0 }, - { "adma-lcd", IMX_SC_R_LCD_0, 1, true, 0 }, - { "adma-lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 }, - { "adma-lpuart", IMX_SC_R_UART_0, 4, true, 0 }, - { "adma-lpspi", IMX_SC_R_SPI_0, 4, true, 0 }, - - /* VPU SS */ + { "usb", IMX_SC_R_USB_0, 2, true, 0 }, + { "usb0phy", IMX_SC_R_USB_0_PHY, 1, false, 0 }, + { "usb2", IMX_SC_R_USB_2, 1, false, 0 }, + { "usb2phy", IMX_SC_R_USB_2_PHY, 1, false, 0 }, + { "sdhc", IMX_SC_R_SDHC_0, 3, true, 0 }, + { "enet", IMX_SC_R_ENET_0, 2, true, 0 }, + { "nand", IMX_SC_R_NAND, 1, false, 0 }, + { "mlb", IMX_SC_R_MLB_0, 1, true, 0 }, + + /* AUDIO SS */ + { "audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false, 0 }, + { "audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false, 0 }, + { "audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false, 0 }, + { "dma0-ch", IMX_SC_R_DMA_0_CH0, 16, true, 0 }, + { "dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true, 0 }, + { "dma2-ch", IMX_SC_R_DMA_2_CH0, 5, true, 0 }, + { "asrc0", IMX_SC_R_ASRC_0, 1, false, 0 }, + { "asrc1", IMX_SC_R_ASRC_1, 1, false, 0 }, + { "esai0", IMX_SC_R_ESAI_0, 1, false, 0 }, + { "spdif0", IMX_SC_R_SPDIF_0, 1, false, 0 }, + { "sai", IMX_SC_R_SAI_0, 3, true, 0 }, + { "amix", IMX_SC_R_AMIX, 1, false, 0 }, + { "mqs0", IMX_SC_R_MQS_0, 1, false, 0 }, + { "dsp", IMX_SC_R_DSP, 1, false, 0 }, + { "dsp-ram", IMX_SC_R_DSP_RAM, 1, false, 0 }, + + /* DMA SS */ + { "can", IMX_SC_R_CAN_0, 3, true, 0 }, + { "ftm", IMX_SC_R_FTM_0, 2, true, 0 }, + { "lpi2c", IMX_SC_R_I2C_0, 4, true, 0 }, + { "adc", IMX_SC_R_ADC_0, 1, true, 0 }, + { "lcd", IMX_SC_R_LCD_0, 1, true, 0 }, + { "lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 }, + { "lpuart", IMX_SC_R_UART_0, 4, true, 0 }, + { "lpspi", IMX_SC_R_SPI_0, 4, true, 0 }, + + /* VPU SS */ { "vpu", IMX_SC_R_VPU, 1, false, 0 }, { "vpu-pid", IMX_SC_R_VPU_PID0, 8, true, 0 }, { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, false, 0 }, @@ -139,14 +141,16 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true, 0 }, /* HSIO SS */ - { "hsio-pcie-b", IMX_SC_R_PCIE_B, 1, false, 0 }, - { "hsio-serdes-1", IMX_SC_R_SERDES_1, 1, false, 0 }, + { "pcie-b", IMX_SC_R_PCIE_B, 1, false, 0 }, + { "serdes-1", IMX_SC_R_SERDES_1, 1, false, 0 }, { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, false, 0 }, - /* MIPI/LVDS SS */ + /* MIPI SS */ { "mipi0", IMX_SC_R_MIPI_0, 1, false, 0 }, { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, false, 0 }, { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, true, 0 }, + + /* LVDS SS */ { "lvds0", IMX_SC_R_LVDS_0, 1, false, 0 }, /* DC SS */ -- cgit v1.2.3 From 851826c7566e9bb4d03eb050634031ecc802affb Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Tue, 9 Apr 2019 04:59:55 +0000 Subject: firmware: imx: enable imx scu general irq function The System Controller Firmware (SCFW) controls RTC, thermal and WDOG etc., these resources' interrupt function are managed by SCU. When any IRQ pending, SCU will notify Linux via MU general interrupt channel #3, and Linux kernel needs to call SCU APIs to get IRQ status and notify each module to handle the interrupt. Since there is no data transmission for SCU IRQ notification, so doorbell mode is used for this MU channel, and SCU driver will use notifier mechanism to broadcast to every module which registers the SCU block notifier. Signed-off-by: Anson Huang Reviewed-by: Dong Aisheng Signed-off-by: Shawn Guo --- drivers/firmware/imx/Makefile | 2 +- drivers/firmware/imx/imx-scu-irq.c | 168 +++++++++++++++++++++++++++++++++++++ drivers/firmware/imx/imx-scu.c | 6 ++ include/linux/firmware/imx/sci.h | 5 ++ 4 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 drivers/firmware/imx/imx-scu-irq.c (limited to 'drivers/firmware') diff --git a/drivers/firmware/imx/Makefile b/drivers/firmware/imx/Makefile index 1b2e15b3c9ca..802c4ad8e8f9 100644 --- a/drivers/firmware/imx/Makefile +++ b/drivers/firmware/imx/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o +obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o obj-$(CONFIG_IMX_SCU_PD) += scu-pd.o diff --git a/drivers/firmware/imx/imx-scu-irq.c b/drivers/firmware/imx/imx-scu-irq.c new file mode 100644 index 000000000000..043833ad3c1a --- /dev/null +++ b/drivers/firmware/imx/imx-scu-irq.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 NXP + * + * Implementation of the SCU IRQ functions using MU. + * + */ + +#include +#include +#include + +#define IMX_SC_IRQ_FUNC_ENABLE 1 +#define IMX_SC_IRQ_FUNC_STATUS 2 +#define IMX_SC_IRQ_NUM_GROUP 4 + +static u32 mu_resource_id; + +struct imx_sc_msg_irq_get_status { + struct imx_sc_rpc_msg hdr; + union { + struct { + u16 resource; + u8 group; + u8 reserved; + } __packed req; + struct { + u32 status; + } resp; + } data; +}; + +struct imx_sc_msg_irq_enable { + struct imx_sc_rpc_msg hdr; + u32 mask; + u16 resource; + u8 group; + u8 enable; +} __packed; + +static struct imx_sc_ipc *imx_sc_irq_ipc_handle; +static struct work_struct imx_sc_irq_work; +static ATOMIC_NOTIFIER_HEAD(imx_scu_irq_notifier_chain); + +int imx_scu_irq_register_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register( + &imx_scu_irq_notifier_chain, nb); +} +EXPORT_SYMBOL(imx_scu_irq_register_notifier); + +int imx_scu_irq_unregister_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister( + &imx_scu_irq_notifier_chain, nb); +} +EXPORT_SYMBOL(imx_scu_irq_unregister_notifier); + +static int imx_scu_irq_notifier_call_chain(unsigned long status, u8 *group) +{ + return atomic_notifier_call_chain(&imx_scu_irq_notifier_chain, + status, (void *)group); +} + +static void imx_scu_irq_work_handler(struct work_struct *work) +{ + struct imx_sc_msg_irq_get_status msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + u32 irq_status; + int ret; + u8 i; + + for (i = 0; i < IMX_SC_IRQ_NUM_GROUP; i++) { + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_IRQ; + hdr->func = IMX_SC_IRQ_FUNC_STATUS; + hdr->size = 2; + + msg.data.req.resource = mu_resource_id; + msg.data.req.group = i; + + ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true); + if (ret) { + pr_err("get irq group %d status failed, ret %d\n", + i, ret); + return; + } + + irq_status = msg.data.resp.status; + if (!irq_status) + continue; + + imx_scu_irq_notifier_call_chain(irq_status, &i); + } +} + +int imx_scu_irq_group_enable(u8 group, u32 mask, u8 enable) +{ + struct imx_sc_msg_irq_enable msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + int ret; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_IRQ; + hdr->func = IMX_SC_IRQ_FUNC_ENABLE; + hdr->size = 3; + + msg.resource = mu_resource_id; + msg.group = group; + msg.mask = mask; + msg.enable = enable; + + ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true); + if (ret) + pr_err("enable irq failed, group %d, mask %d, ret %d\n", + group, mask, ret); + + return ret; +} +EXPORT_SYMBOL(imx_scu_irq_group_enable); + +static void imx_scu_irq_callback(struct mbox_client *c, void *msg) +{ + schedule_work(&imx_sc_irq_work); +} + +int imx_scu_enable_general_irq_channel(struct device *dev) +{ + struct of_phandle_args spec; + struct mbox_client *cl; + struct mbox_chan *ch; + int ret = 0, i = 0; + + ret = imx_scu_get_handle(&imx_sc_irq_ipc_handle); + if (ret) + return ret; + + cl = devm_kzalloc(dev, sizeof(*cl), GFP_KERNEL); + if (!cl) + return -ENOMEM; + + cl->dev = dev; + cl->rx_callback = imx_scu_irq_callback; + + /* SCU general IRQ uses general interrupt channel 3 */ + ch = mbox_request_channel_byname(cl, "gip3"); + if (IS_ERR(ch)) { + ret = PTR_ERR(ch); + dev_err(dev, "failed to request mbox chan gip3, ret %d\n", ret); + devm_kfree(dev, cl); + return ret; + } + + INIT_WORK(&imx_sc_irq_work, imx_scu_irq_work_handler); + + if (!of_parse_phandle_with_args(dev->of_node, "mboxes", + "#mbox-cells", 0, &spec)) + i = of_alias_get_id(spec.np, "mu"); + + /* use mu1 as general mu irq channel if failed */ + if (i < 0) + i = 1; + + mu_resource_id = IMX_SC_R_MU_0A + i; + + return ret; +} +EXPORT_SYMBOL(imx_scu_enable_general_irq_channel); diff --git a/drivers/firmware/imx/imx-scu.c b/drivers/firmware/imx/imx-scu.c index 2bb1a19c413f..04a24a863d6e 100644 --- a/drivers/firmware/imx/imx-scu.c +++ b/drivers/firmware/imx/imx-scu.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -246,6 +247,11 @@ static int imx_scu_probe(struct platform_device *pdev) imx_sc_ipc_handle = sc_ipc; + ret = imx_scu_enable_general_irq_channel(dev); + if (ret) + dev_warn(dev, + "failed to enable general irq channel: %d\n", ret); + dev_info(dev, "NXP i.MX SCU Initialized\n"); return devm_of_platform_populate(dev); diff --git a/include/linux/firmware/imx/sci.h b/include/linux/firmware/imx/sci.h index ebc55098faee..17ba4e405129 100644 --- a/include/linux/firmware/imx/sci.h +++ b/include/linux/firmware/imx/sci.h @@ -15,4 +15,9 @@ #include #include + +int imx_scu_enable_general_irq_channel(struct device *dev); +int imx_scu_irq_register_notifier(struct notifier_block *nb); +int imx_scu_irq_unregister_notifier(struct notifier_block *nb); +int imx_scu_irq_group_enable(u8 group, u32 mask, u8 enable); #endif /* _SC_SCI_H */ -- cgit v1.2.3 From fa7fe29a645b4da08efe8ff2392898b88f9ded9f Mon Sep 17 00:00:00 2001 From: Steven Price Date: Mon, 25 Mar 2019 17:37:22 +0000 Subject: firmware: arm_scmi: fix of_node leak in scmi_mailbox_check of_parse_phandle_with_args() requires the caller to call of_node_put() on the returned args->np pointer. Otherwise the reference count will remain incremented. However, in this case, since we don't actually use the returned pointer, we can simply pass in NULL. Fixes: aa4f886f3893f ("firmware: arm_scmi: add basic driver infrastructure for SCMI") Signed-off-by: Steven Price Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 8f952f2f1a29..dd967d675c08 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -654,9 +654,7 @@ static int scmi_xfer_info_init(struct scmi_info *sinfo) static int scmi_mailbox_check(struct device_node *np) { - struct of_phandle_args arg; - - return of_parse_phandle_with_args(np, "mboxes", "#mbox-cells", 0, &arg); + return of_parse_phandle_with_args(np, "mboxes", "#mbox-cells", 0, NULL); } static int scmi_mbox_free_channel(int id, void *p, void *data) -- cgit v1.2.3 From d9350f21e5fe2614e1f78ef20c3a3e83c4a36391 Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Fri, 22 Mar 2019 16:55:03 -0500 Subject: firmware: arm_scmi: replace of_match_device->data with of_device_get_match_data() of_match_device can return NULL if no matching device is found though it's highly unlikely to happen in scmi_probe as it's called only if a valid match is found. However we can use of_device_get_match_data() instead of of_match_device()->data to handle NULL pointer checks and return -EINVAL in such a scenario. Reviewed-by: Steven Price Signed-off-by: Aditya Pakki Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index dd967d675c08..b5bc4c7a8fab 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -796,7 +796,9 @@ static int scmi_probe(struct platform_device *pdev) return -EINVAL; } - desc = of_match_device(scmi_of_match, dev)->data; + desc = of_device_get_match_data(dev); + if (!desc) + return -EINVAL; info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); if (!info) -- cgit v1.2.3 From e840decc1954521ff73bbc87b5b3ea36af1704d2 Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Mon, 15 Apr 2019 12:47:46 +0530 Subject: firmware: xilinx: Add fpga API's This Patch Adds fpga API's to support the Bitstream loading by using firmware interface. Signed-off-by: Nava kishore Manne Reviewed-by: Moritz Fischer Signed-off-by: Michal Simek --- drivers/firmware/xilinx/zynqmp.c | 45 ++++++++++++++++++++++++++++++++++++ include/linux/firmware/xlnx-zynqmp.h | 12 ++++++++++ 2 files changed, 57 insertions(+) (limited to 'drivers/firmware') diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 87a1d636c0dc..fd3d83745208 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -539,6 +539,49 @@ static int zynqmp_pm_reset_get_status(const enum zynqmp_pm_reset reset, return ret; } +/** + * zynqmp_pm_fpga_load - Perform the fpga load + * @address: Address to write to + * @size: pl bitstream size + * @flags: Bitstream type + * -XILINX_ZYNQMP_PM_FPGA_FULL: FPGA full reconfiguration + * -XILINX_ZYNQMP_PM_FPGA_PARTIAL: FPGA partial reconfiguration + * + * This function provides access to pmufw. To transfer + * the required bitstream into PL. + * + * Return: Returns status, either success or error+reason + */ +static int zynqmp_pm_fpga_load(const u64 address, const u32 size, + const u32 flags) +{ + return zynqmp_pm_invoke_fn(PM_FPGA_LOAD, lower_32_bits(address), + upper_32_bits(address), size, flags, NULL); +} + +/** + * zynqmp_pm_fpga_get_status - Read value from PCAP status register + * @value: Value to read + * + * This function provides access to the pmufw to get the PCAP + * status + * + * Return: Returns status, either success or error+reason + */ +static int zynqmp_pm_fpga_get_status(u32 *value) +{ + u32 ret_payload[PAYLOAD_ARG_CNT]; + int ret; + + if (!value) + return -EINVAL; + + ret = zynqmp_pm_invoke_fn(PM_FPGA_GET_STATUS, 0, 0, 0, 0, ret_payload); + *value = ret_payload[1]; + + return ret; +} + /** * zynqmp_pm_init_finalize() - PM call to inform firmware that the caller * master has initialized its own power management @@ -642,6 +685,8 @@ static const struct zynqmp_eemi_ops eemi_ops = { .request_node = zynqmp_pm_request_node, .release_node = zynqmp_pm_release_node, .set_requirement = zynqmp_pm_set_requirement, + .fpga_load = zynqmp_pm_fpga_load, + .fpga_get_status = zynqmp_pm_fpga_get_status, }; /** diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index 3533ee557043..1262ea6a1f4b 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -48,6 +48,14 @@ #define ZYNQMP_PM_CAPABILITY_WAKEUP 0x4U #define ZYNQMP_PM_CAPABILITY_POWER 0x8U +/* + * Firmware FPGA Manager flags + * XILINX_ZYNQMP_PM_FPGA_FULL: FPGA full reconfiguration + * XILINX_ZYNQMP_PM_FPGA_PARTIAL: FPGA partial reconfiguration + */ +#define XILINX_ZYNQMP_PM_FPGA_FULL 0x0U +#define XILINX_ZYNQMP_PM_FPGA_PARTIAL BIT(0) + enum pm_api_id { PM_GET_API_VERSION = 1, PM_REQUEST_NODE = 13, @@ -56,6 +64,8 @@ enum pm_api_id { PM_RESET_ASSERT = 17, PM_RESET_GET_STATUS, PM_PM_INIT_FINALIZE = 21, + PM_FPGA_LOAD, + PM_FPGA_GET_STATUS, PM_GET_CHIPID = 24, PM_IOCTL = 34, PM_QUERY_DATA, @@ -258,6 +268,8 @@ struct zynqmp_pm_query_data { struct zynqmp_eemi_ops { int (*get_api_version)(u32 *version); int (*get_chipid)(u32 *idcode, u32 *version); + int (*fpga_load)(const u64 address, const u32 size, const u32 flags); + int (*fpga_get_status)(u32 *value); int (*query_data)(struct zynqmp_pm_query_data qdata, u32 *out); int (*clock_enable)(u32 clock_id); int (*clock_disable)(u32 clock_id); -- cgit v1.2.3