MLK-25473-3 dmaengine: imx-sdma: split request firmware from runtime
authorRobin Gong <yibin.gong@nxp.com>
Thu, 29 Apr 2021 20:09:43 +0000 (04:09 +0800)
committerRobin Gong <yibin.gong@nxp.com>
Fri, 30 Apr 2021 15:09:51 +0000 (23:09 +0800)
split request_firmware from sdma_runtime_resume, because in nfs case,
request_firmware may trigger sleep which cause kernel below complain during
runtime_get_sync in atmoic case like audio record. Actually, only one time
for request_firmware after kernel boot, split it from runtime resume and
only load_scripts instead 'request_firmware + load_script'.

[   63.115924] BUG: scheduling while atomic: arecord/613/0x00000002
[   63.121970] Modules linked in:
[   63.125031] CPU: 3 PID: 613 Comm: arecord Not tainted 5.10.31-104197-ged911ffb5e2f #339
[   63.133033] Hardware name: NXP i.MX8MPlus EVK board (DT)
[   63.138343] Call trace:
[   63.140795]  dump_backtrace+0x0/0x1c8
[   63.144458]  show_stack+0x14/0x60
[   63.147775]  dump_stack+0xd0/0x128
[   63.151178]  __schedule_bug+0x54/0x78
[   63.154843]  __schedule+0x5c8/0x690
[   63.158332]  schedule+0x6c/0x108
[   63.161560]  rpc_wait_bit_killable+0x24/0xa8
[   63.165831]  __wait_on_bit+0xa4/0xe0
[   63.169405]  out_of_line_wait_on_bit+0x8c/0xb0
[   63.173848]  __rpc_execute+0x138/0x338
[   63.177596]  rpc_execute+0x88/0xa8
[   63.180998]  rpc_run_task+0x154/0x1a8
[   63.184661]  rpc_call_sync+0x54/0xa8
[   63.188239]  nfs3_rpc_wrapper+0x50/0xd0
[   63.192074]  nfs3_proc_access+0x7c/0xe0
[   63.195910]  nfs_do_access+0xa0/0x1b8
[   63.199571]  nfs_permission+0xac/0x1b0
[   63.203321]  inode_permission+0xdc/0x170
[   63.207243]  link_path_walk+0x1f4/0x350
[   63.211078]  path_openat+0x80/0xd50
[   63.214565]  do_file_open_root+0xa4/0x150
[   63.218576]  file_open_root+0xf4/0x178
[   63.222326]  kernel_read_file_from_path_initns+0xb0/0x138
[   63.227726]  _request_firmware+0x39c/0x588
[   63.231821]  request_firmware+0x44/0x68
[   63.235659]  sdma_runtime_resume+0x1a0/0x280
[   63.239929]  pm_generic_runtime_resume+0x28/0x40
[   63.244546]  __genpd_runtime_resume+0x2c/0x80
[   63.248902]  genpd_runtime_resume+0x130/0x1e8
[   63.253260]  __rpm_callback+0xd8/0x150
[   63.257008]  rpm_callback+0x24/0x98
[   63.260498]  rpm_resume+0x338/0x4b0
[   63.263986]  __pm_runtime_resume+0x38/0x88
[   63.268082]  sdma_prep_dma_cyclic+0x264/0x290

Signed-off-by: Robin Gong <yibin.gong@nxp.com>
Reviewed-by: Shengjiu Wang <shengjiu.wang@nxp.com>
drivers/dma/imx-sdma.c

index f2c29a6..9598058 100644 (file)
@@ -489,6 +489,7 @@ struct sdma_engine {
        struct gen_pool                 *iram_pool;
        bool                            fw_loaded;
        u32                             fw_fail;
+       u8                              *fw_data;
        unsigned short                  ram_code_start;
 };
 
@@ -496,9 +497,6 @@ static int sdma_config_write(struct dma_chan *chan,
                       struct dma_slave_config *dmaengine_cfg,
                       enum dma_transfer_direction direction);
 
-static int sdma_get_firmware(struct sdma_engine *sdma,
-               const char *fw_name);
-
 static struct sdma_driver_data sdma_imx31 = {
        .chnenbl0 = SDMA_CHNENBL0_IMX31,
        .num_events = 32,
@@ -782,17 +780,54 @@ static int sdma_run_channel0(struct sdma_engine *sdma)
        return ret;
 }
 
-static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size,
-               u32 address)
+#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1        34
+#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V2        38
+#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V3        47
+#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V4        48
+
+static void sdma_add_scripts(struct sdma_engine *sdma,
+               const struct sdma_script_start_addrs *addr)
+{
+       s32 *addr_arr = (u32 *)addr;
+       s32 *saddr_arr = (u32 *)sdma->script_addrs;
+       int i;
+
+       /* use the default firmware in ROM if missing external firmware */
+       if (!sdma->script_number)
+               sdma->script_number = SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1;
+
+       if (sdma->script_number > sizeof(struct sdma_script_start_addrs)
+                                 / sizeof(s32)) {
+               dev_err(sdma->dev,
+                       "SDMA script number %d not match with firmware.\n",
+                       sdma->script_number);
+               return;
+       }
+
+       for (i = 0; i < sdma->script_number; i++)
+               if (addr_arr[i] > 0)
+                       saddr_arr[i] = addr_arr[i];
+}
+
+static int sdma_load_script(struct sdma_engine *sdma)
 {
        struct sdma_buffer_descriptor *bd0 = sdma->bd0;
+       const struct sdma_script_start_addrs *addr;
+       struct sdma_firmware_header *header;
+       unsigned short *ram_code;
        void *buf_virt;
        dma_addr_t buf_phys;
        int ret;
        unsigned long flags;
 
-       buf_virt = dma_alloc_coherent(sdma->dev, size, &buf_phys,
-                                             GFP_KERNEL);
+       header = (struct sdma_firmware_header *)sdma->fw_data;
+
+       addr = (void *)header + header->script_addrs_start;
+       ram_code = (void *)header + header->ram_code_start;
+       sdma->ram_code_start = header->ram_code_start;
+
+       buf_virt = dma_alloc_coherent(sdma->dev, header->ram_code_size,
+                                     &buf_phys, GFP_KERNEL);
        if (!buf_virt)
                return -ENOMEM;
 
@@ -800,18 +835,25 @@ static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size,
 
        bd0->mode.command = C0_SETPM;
        bd0->mode.status = BD_DONE | BD_WRAP | BD_EXTD;
-       bd0->mode.count = size / 2;
+       bd0->mode.count = header->ram_code_size / 2;
        bd0->buffer_addr = buf_phys;
-       bd0->ext_buffer_addr = address;
+       bd0->ext_buffer_addr = addr->ram_code_start_addr;
 
-       memcpy(buf_virt, buf, size);
+       memcpy(buf_virt, ram_code, header->ram_code_size);
 
        ret = sdma_run_channel0(sdma);
 
        spin_unlock_irqrestore(&sdma->channel_0_lock, flags);
 
-       dma_free_coherent(sdma->dev, size, buf_virt, buf_phys);
+       dma_free_coherent(sdma->dev, header->ram_code_size, buf_virt, buf_phys);
+
+       sdma_add_scripts(sdma, addr);
 
+       sdma->fw_loaded = true;
+
+       dev_info_once(sdma->dev, "loaded firmware %d.%d\n",
+                       header->version_major,
+                       header->version_minor);
        return ret;
 }
 
@@ -1586,9 +1628,8 @@ static int sdma_runtime_resume(struct device *dev)
        /* Initializes channel's priorities */
        sdma_set_channel_priority(&sdma->channel[0], 7);
 
-       ret = sdma_get_firmware(sdma, sdma->fw_name);
-       if (ret)
-               dev_warn(sdma->dev, "failed to get firmware.\n");
+       if (sdma_load_script(sdma))
+               dev_warn(sdma->dev, "failed to load script.\n");
 
        sdma->is_on = true;
 
@@ -2101,41 +2142,10 @@ static void sdma_issue_pending(struct dma_chan *chan)
        }
 }
 
-#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1        34
-#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V2        38
-#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V3        47
-#define SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V4        48
-
-static void sdma_add_scripts(struct sdma_engine *sdma,
-               const struct sdma_script_start_addrs *addr)
-{
-       s32 *addr_arr = (u32 *)addr;
-       s32 *saddr_arr = (u32 *)sdma->script_addrs;
-       int i;
-
-       /* use the default firmware in ROM if missing external firmware */
-       if (!sdma->script_number)
-               sdma->script_number = SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1;
-
-       if (sdma->script_number > sizeof(struct sdma_script_start_addrs)
-                                 / sizeof(s32)) {
-               dev_err(sdma->dev,
-                       "SDMA script number %d not match with firmware.\n",
-                       sdma->script_number);
-               return;
-       }
-
-       for (i = 0; i < sdma->script_number; i++)
-               if (addr_arr[i] > 0)
-                       saddr_arr[i] = addr_arr[i];
-}
-
 static void sdma_load_firmware(const struct firmware *fw, void *context)
 {
        struct sdma_engine *sdma = context;
        const struct sdma_firmware_header *header;
-       const struct sdma_script_start_addrs *addr;
-       unsigned short *ram_code;
 
        if (!fw) {
                /* Load firmware once more time if timeout */
@@ -2179,22 +2189,18 @@ static void sdma_load_firmware(const struct firmware *fw, void *context)
                goto err_firmware;
        }
 
-       addr = (void *)header + header->script_addrs_start;
-       ram_code = (void *)header + header->ram_code_start;
-       sdma->ram_code_start = header->ram_code_start;
-
-       /* download the RAM image for SDMA */
-       sdma_load_script(sdma, ram_code,
-                       header->ram_code_size,
-                       addr->ram_code_start_addr);
+       dev_info(sdma->dev, "firmware found.\n");
 
-       sdma_add_scripts(sdma, addr);
+       if (!sdma->fw_data) {
+               sdma->fw_data = kmalloc(fw->size, GFP_KERNEL);
+               if (!sdma->fw_data)
+                       goto err_firmware;
 
-       sdma->fw_loaded = true;
+               memcpy(sdma->fw_data, fw->data, fw->size);
 
-       dev_info_once(sdma->dev, "loaded firmware %d.%d\n",
-                       header->version_major,
-                       header->version_minor);
+               if (!sdma->drvdata->pm_runtime)
+                       pm_runtime_get_sync(sdma->dev);
+       }
 
 err_firmware:
        release_firmware(fw);
@@ -2534,6 +2540,10 @@ static int sdma_probe(struct platform_device *pdev)
                        dev_warn(&pdev->dev, "failed to get firmware name\n");
                else
                        sdma->fw_name = fw_name;
+
+               ret = sdma_get_firmware(sdma, sdma->fw_name);
+               if (ret)
+                       dev_warn(sdma->dev, "failed to get firmware.\n");
        }
 
        /* enable autosuspend for pm_runtime */
@@ -2545,8 +2555,6 @@ static int sdma_probe(struct platform_device *pdev)
        }
 
        pm_runtime_enable(&pdev->dev);
-       if (!sdma->drvdata->pm_runtime)
-               pm_runtime_get_sync(&pdev->dev);
 
        return 0;
 
@@ -2569,6 +2577,7 @@ static int sdma_remove(struct platform_device *pdev)
        devm_free_irq(&pdev->dev, sdma->irq, sdma);
        dma_async_device_unregister(&sdma->dma_device);
        kfree(sdma->script_addrs);
+       kfree(sdma->fw_data);
        clk_unprepare(sdma->clk_ahb);
        clk_unprepare(sdma->clk_ipg);
        /* Kill the tasklet */