はじめに
AMD XDNA Driver for Linux を覗いてみる (その2)
昨日の続き、今回は、PSP の部分を見ていきます。
amdxdna_psp
psp_load_firmware から。PSPのFirmware をロードする部分
ret = request_firmware(&fw, psp->conf.fw_path, psp->dev);
標準の request_firmware にてデータをロード
psp->fw_buf_sz = ALIGN(fw->size, PSP_FW_ALIGN) + PSP_FW_ALIGN; psp->fw_buffer = kmalloc(psp->fw_buf_sz, GFP_KERNEL);
psp->fw_paddr = virt_to_phys(psp->fw_buffer); offset = ALIGN(psp->fw_paddr, PSP_FW_ALIGN) - psp->fw_paddr; psp->fw_paddr += offset; memcpy(psp->fw_buffer + offset, fw->data, fw->size);
psp_exe
/* Write command and argument registers */ for (i = 0; i < PSP_NUM_IN_REGS; i++) writel(reg_vals[i], PSP_REG(psp, i));
/* clear and set PSP INTR register to kick off */ writel(0, PSP_REG(psp, PSP_INTR_REG)); writel(1, PSP_REG(psp, PSP_INTR_REG));
PSP INTR をクリア後、kick
/* PSP should be busy. Wait for ready, so we know task is done. */ ret = readx_poll_timeout(readl, PSP_REG(psp, PSP_STATUS_REG), ready, FIELD_GET(PSP_STATUS_READY, ready), PSP_POLL_INTERVAL, PSP_POLL_TIMEOUT);
ステータスレジスタをポーリング
resp_code = readl(PSP_REG(psp, PSP_RESP_REG));
ステータス情報を獲得
reg_vals[0] = PSP_VALIDATE; reg_vals[1] = lower_32_bits(psp->fw_paddr); reg_vals[2] = upper_32_bits(psp->fw_paddr); reg_vals[3] = psp->fw_buf_sz; ret = psp_exec(psp, reg_vals); if (ret) { dev_err(psp->dev, "failed to validate fw, ret %d", ret); goto failed_free_fw_buf; }
Firmware の VALIDATE
memset(reg_vals, 0, sizeof(reg_vals)); reg_vals[0] = PSP_START; reg_vals[1] = PSP_START_COPY_FW; ret = psp_exec(psp, reg_vals); if (ret) { dev_err(psp->dev, "failed to start fw, ret %d", ret); goto failed_free_fw_buf; }
Firmwareの起動
psp_regs
psp_regs はどこで設定されているのでしょうか?
ipu_pci.c で下記のように設定されていました。
psp_conf.fw_path = idev->priv->fw_path; for (i = 0; i < PSP_MAX_REGS; i++) psp_conf.psp_regs[i] = tbl[PSP_REG_BAR(idev, i)] + PSP_REG_OFF(idev, i); idev->psp_hdl = amdxdna_psp_create(&pdev->dev, &psp_conf); if (!idev->psp_hdl) { XDNA_ERR(xdna, "failed to create psp"); ret = -EINVAL; goto fini_smu; }
tbl にあるアドレスを設定しています。tbl は、ipu_pci.c で下記のように pcim_iomap_table の戻り値になっています。
tbl = pcim_iomap_table(pdev);
pcim_iomap_table は、linux の lib/devres.c で下記のように定義されています。
/** * pcim_iomap_table - access iomap allocation table * @pdev: PCI device to access iomap table for * * Access iomap allocation table for @dev. If iomap table doesn't * exist and @pdev is managed, it will be allocated. All iomaps * recorded in the iomap table are automatically unmapped on driver * detach. * * This function might sleep when the table is first allocated but can * be safely called without context and guaranteed to succeed once * allocated. */ void __iomem * const *pcim_iomap_table(struct pci_dev *pdev) { struct pcim_iomap_devres *dr, *new_dr; dr = devres_find(&pdev->dev, pcim_iomap_release, NULL, NULL); if (dr) return dr->table; new_dr = devres_alloc_node(pcim_iomap_release, sizeof(*new_dr), GFP_KERNEL, dev_to_node(&pdev->dev)); if (!new_dr) return NULL; dr = devres_get(&pdev->dev, new_dr, NULL, NULL); return dr->table; } EXPORT_SYMBOL(pcim_iomap_table);