Vengineerの妄想(準備期間)

人生は短いけど、長いです。人生を楽しみましょう!

AMD XDNA Driver for Linux を覗いてみる (その2)

はじめに

AMD XDNA Driver for Linux を覗いてみる (その2)

昨日の続き、今回は、PSP の部分を見ていきます。

amdxdna_psp

psp_load_firmware から。PSPFirmware をロードする部分

 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);

Firmware のためのカーネルバッファを確保

 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);

カーネルバッファに、Firmware をコピー

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));

ステータス情報を獲得

psp_exec を使っている部分の振り返り。

 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);

おわりに

今回は、PSP関連をみてみました。PSPの実態はどうなっているのでしょうかね。