Linux 未生成 PCI 设备中断

Linux PCI device interrupt not generated

我目前正在为 AMD 传感器融合中心重构 driver。 可以找到原始驱动程序 here。 当向设备发送命令时,芯片需要一些时间来处理请求。我正在使用 msleep 来等待设备响应。但是我想干净地实现 IRQ 处理。作为模板,我研究了 drivers/i2c/busses/i2c-amd-mp2-pci.c 的实现并提出了以下存根中断处理。

// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
 * AMD Sensor Fusion Hub (SFH) PCIe driver
 *
 * Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
 *          Nehal Bakulchandra Shah <Nehal-bakulchandra.Shah@amd.com>
 *          Richard Neumann <mail@richard-neumann.de>
 */

#include <linux/bitops.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/pm_runtime.h>
#include <linux/types.h>

#include "amd-sfh-pci.h"

/**
 * amd_sfh_get_sensor_mask - Returns the sensors mask.
 * @pci_dev:    The Sensor Fusion Hub PCI device
 *
 * Returns an unsigned integer representing the bitmask to match
 * the sensors connected to the Sensor Fusion Hub.
 */
int amd_sfh_get_sensor_mask(struct pci_dev *pci_dev)
{
    int sensor_mask;
    struct amd_sfh_dev *sfh_dev = pci_get_drvdata(pci_dev);

    sensor_mask = readl(sfh_dev->mmio + AMD_SFH_SENSOR_MASK);
    /* Correct bit shift in firmware register */
    sensor_mask = sensor_mask >> 4;

    if (!sensor_mask)
        dev_err(&pci_dev->dev, "no sensors marked active on device\n");

    return sensor_mask;
}
EXPORT_SYMBOL_GPL(amd_sfh_get_sensor_mask);

/**
 * amd_sfh_start_sensor- Starts the respective sensor.
 * @pci_dev:    The Sensor Fusion Hub PCI device
 * @sensor_idx: The sensor's index
 * @dma_handle: The DMA handle
 */
void amd_sfh_start_sensor(struct pci_dev *pci_dev, enum sensor_idx sensor_idx,
                          dma_addr_t dma_handle)
{
    struct amd_sfh_dev *sfh_dev = pci_get_drvdata(pci_dev);
    union amd_sfh_cmd command;
    union amd_sfh_parm parameter;

    command.ul = 0;
    command.s.cmd_id = enable_sensor;
    command.s.period = PERIOD;
    command.s.sensor_id = sensor_idx;

    parameter.ul = 0;
    parameter.s.buffer_layout = 1;
    parameter.s.buffer_length = 16;

    writeq(dma_handle, sfh_dev->mmio + AMD_SFH_ADDR);
    writel(parameter.ul, sfh_dev->mmio + AMD_SFH_PARM);
    writel(command.ul, sfh_dev->mmio + AMD_SFH_CMD);
}
EXPORT_SYMBOL_GPL(amd_sfh_start_sensor);

/**
 * amd_sfh_stop_sensor- Stops the respective sensor.
 * @pci_dev:    The Sensor Fusion Hub PCI device
 * @sensor_idx: The sensor's index
 */
void amd_sfh_stop_sensor(struct pci_dev *pci_dev, enum sensor_idx sensor_idx)
{
    struct amd_sfh_dev *sfh_dev = pci_get_drvdata(pci_dev);
    union amd_sfh_cmd command;

    command.ul = 0;
    command.s.cmd_id = disable_sensor;
    command.s.period = 0;
    command.s.sensor_id = sensor_idx;

    writeq(0x0, sfh_dev->mmio + AMD_SFH_ADDR);
    writel(command.ul, sfh_dev->mmio + AMD_SFH_CMD);
}
EXPORT_SYMBOL_GPL(amd_sfh_stop_sensor);

/**
 * amd_sfh_stop_all_sensors- Stops all sensors on the SFH.
 * @pci_dev:    The Sensor Fusion Hub PCI device
 */
static void amd_sfh_stop_all_sensors(struct pci_dev *pci_dev)
{
    struct amd_sfh_dev *sfh_dev = pci_get_drvdata(pci_dev);
    union amd_sfh_cmd command;

    command.ul = 0;
    command.s.cmd_id = stop_all_sensors;
    command.s.period = 0;
    command.s.sensor_id = 0;

    writel(command.ul, sfh_dev->mmio + AMD_SFH_CMD);
}

/**
 * amd_sfh_irq_isr - IRQ handler
 * @irq:    The IRQ number received
 * @dev:    The underlying device
 */
static irqreturn_t amd_sfh_irq_isr(int irq, void *dev)
{
    struct amd_sfh_dev *sfh_dev = dev;

    dev_info(&sfh_dev->pci_dev->dev, "got IRQ: %d\n", irq);
    return IRQ_NONE;
}

/**
 * amd_sfh_pci_init - Initializes the PCI device
 * @sfh_dev:    The device data
 * @pci_dev:    The PCI device
 */
static int amd_sfh_pci_init(struct amd_sfh_dev *sfh_dev,
                            struct pci_dev *pci_dev)
{
    int rc;

    pci_set_drvdata(pci_dev, sfh_dev);
    rc = pcim_enable_device(pci_dev);

    if (rc)
        goto err_pci_enable;

    rc = pcim_iomap_regions(pci_dev, BIT(2), pci_name(pci_dev));

    if (rc)
        goto err_pci_enable;

    sfh_dev->pci_dev = pci_dev;
    sfh_dev->mmio = pcim_iomap_table(pci_dev)[2];
    pci_set_master(pci_dev);
    rc = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(64));

    if (rc) {
        rc = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32));

        if (rc)
            goto err_dma_mask;
    }

    /* Set up intx irq */
    writel(0, sfh_dev->mmio + AMD_P2C_MSG_INTEN);
    pci_intx(pci_dev, 1);
    dev_info(&pci_dev->dev, "available interrupt: %d\n", pci_dev->irq);
    rc = devm_request_irq(&pci_dev->dev, pci_dev->irq, amd_sfh_irq_isr,
                  0, dev_name(&pci_dev->dev), sfh_dev);
    if (rc)
        dev_err(&pci_dev->dev, "Failure requesting irq %i: %d\n",
            pci_dev->irq, rc);

    return rc;

err_dma_mask:
    pci_clear_master(pci_dev);
err_pci_enable:
    return rc;
}

static int amd_sfh_pci_probe(struct pci_dev *pci_dev,
                 const struct pci_device_id *id)
{
    int rc;
    struct amd_sfh_dev *sfh_dev;

    sfh_dev = devm_kzalloc(&pci_dev->dev, sizeof(*sfh_dev), GFP_KERNEL);

    if (!sfh_dev)
        return -ENOMEM;

    rc = amd_sfh_pci_init(sfh_dev, pci_dev);

    if (rc)
        return rc;

    pm_runtime_set_autosuspend_delay(&pci_dev->dev, 1000);
    pm_runtime_use_autosuspend(&pci_dev->dev);
    pm_runtime_put_autosuspend(&pci_dev->dev);
    pm_runtime_allow(&pci_dev->dev);

    dev_info(&pci_dev->dev, "SFH device registered.\n");

    return 0;
}

static void amd_sfh_pci_remove(struct pci_dev *pci_dev)
{
    struct amd_sfh_dev *sfh_dev = pci_get_drvdata(pci_dev);

    amd_sfh_stop_all_sensors(sfh_dev->pci_dev);

    pm_runtime_forbid(&pci_dev->dev);
    pm_runtime_get_noresume(&pci_dev->dev);

    pci_intx(pci_dev, 0);
    pci_clear_master(pci_dev);
}

static const struct pci_device_id amd_sfh_pci_tbl[] = {
    {PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_SFH)},
    {0}
};
MODULE_DEVICE_TABLE(pci, amd_sfh_pci_tbl);

static struct pci_driver amd_sfh_pci_driver = {
    .name       = "amd-sfh-pci",
    .id_table   = amd_sfh_pci_tbl,
    .probe      = amd_sfh_pci_probe,
    .remove     = amd_sfh_pci_remove,
};
module_pci_driver(amd_sfh_pci_driver);

/**
 * amd_sfh_find_device - Returns the first best AMD SFH device.
 */
struct amd_sfh_dev *amd_sfh_find_device(void)
{
    struct device *dev;
    struct pci_dev *pci_dev;

    dev = driver_find_next_device(&amd_sfh_pci_driver.driver, NULL);

    if (!dev)
        return NULL;

    pci_dev = to_pci_dev(dev);
    return pci_get_drvdata(pci_dev);
}
EXPORT_SYMBOL_GPL(amd_sfh_find_device);

MODULE_DESCRIPTION("AMD(R) Sensor Fusion Hub PCI driver");
MODULE_AUTHOR("Shyam Sundar S K <Shyam-sundar.S-k@amd.com>");
MODULE_AUTHOR("Nehal Bakulchandra Shah <Nehal-bakulchandra.Shah@amd.com>");
MODULE_AUTHOR("Richard Neumann <mail@richard-neumann.de>");
MODULE_LICENSE("Dual BSD/GPL");

但是 amd_sfh_irq_isr 中的 dev_info 从未被记录,因此我怀疑在写入设备寄存器时从未触发 IRQ。这可能是什么原因,我该怎么做才能正确实施 IRQ 处理?

PS dmesg 输出:

[ 2272.642762] amd-sfh-pci 0000:04:00.7: available interrupt: 56
[ 2272.642840] amd-sfh-pci 0000:04:00.7: SFH device registered.

更新 该设备似乎支持 MSI:

04:00.7 Non-VGA unclassified device [0000]: Advanced Micro Devices, Inc. [AMD] Raven/Raven2/Renoir Sensor Fusion Hub [1022:15e4]
    Subsystem: Hewlett-Packard Company Raven/Raven2/Renoir Sensor Fusion Hub [103c:8496]
    Flags: bus master, fast devsel, latency 0, IRQ 56
    Memory at fc800000 (32-bit, non-prefetchable) [size=1M]
    Memory at fcc8c000 (32-bit, non-prefetchable) [size=8K]
    Capabilities: [48] Vendor Specific Information: Len=08 <?>
    Capabilities: [50] Power Management version 3
    Capabilities: [64] Express Endpoint, MSI 00
    Capabilities: [a0] MSI: Enable- Count=1/2 Maskable- 64bit+
    Capabilities: [c0] MSI-X: Enable- Count=2 Masked-
    Capabilities: [100] Vendor Specific Information: ID=0001 Rev=1 Len=010 <?>
    Kernel modules: amd_sfh_pci

但是仍然没有接收/处理中断:

static void amd_sfh_debug(struct pci_dev *pci_dev)
{
    bool msi;

    msi = pci_dev_msi_enabled(pci_dev);
    pci_info(pci_dev, "MSI: %s\n", msi ? "true" : "false");
    pci_info(pci_dev, "No MSI: %s\n", pci_dev->no_msi ? "true" : "false");
}

/**
 * amd_sfh_handle_irq - Handles IRQs.
 * @irq:    The interrupt request to be handled
 * @dev:    The driver data
 *
 * Returns an appropriate IRQ return type.
 */
static irqreturn_t amd_sfh_handle_irq(int irq, void *dev)
{
    struct amd_sfh_dev *privdata = dev;

    pci_info(privdata->pci_dev, "got IRQ: %d\n", irq);
    return IRQ_NONE;
}

static int amd_sfh_setup_irq(struct amd_sfh_dev *privdata)
{
    int rc, vecs, irq;
    struct pci_dev *pci_dev = privdata->pci_dev;

    vecs = pci_alloc_irq_vectors(pci_dev, 1, 3, PCI_IRQ_ALL_TYPES);
    if (vecs < 0)
        return vecs;

    pci_info(pci_dev, "allocated %d IRQ vectors\n", vecs);

    for (irq = 0; irq < vecs; irq++) {
        pci_info(pci_dev, "requesting IRQ: %d\n", irq);
        rc = devm_request_irq(&pci_dev->dev,
                             pci_irq_vector(pci_dev, irq),
                             amd_sfh_handle_irq, 0, "sfh-irq",
                             privdata);
        if (rc) {
            pci_err(pci_dev, "failed to get IRQ\n");
            goto free_irq_vectors;
        }
    }

    return 0;

free_irq_vectors:
    pci_free_irq_vectors(pci_dev);
    return rc;
}

static int amd_sfh_pci_init(struct amd_sfh_dev *privdata,
                            struct pci_dev *pci_dev)
{
    int rc;

    pci_set_drvdata(pci_dev, privdata);

    rc = pcim_enable_device(pci_dev);
    if (rc)
        return rc;

    rc = pcim_iomap_regions(pci_dev, BIT(2), pci_name(pci_dev));
    if (rc)
        return rc;

    privdata->pci_dev = pci_dev;
    privdata->mmio = pcim_iomap_table(pci_dev)[2];
    pci_set_master(pci_dev);

    rc = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(64));
    if (rc) {
        rc = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32));
        if (rc)
            goto clear_master;
    }

    /* Setup IRQ */
    amd_sfh_debug(pci_dev);

    rc = amd_sfh_setup_irq(privdata);
    if (rc)
        goto clear_master;

    amd_sfh_debug(pci_dev);
    /* End of IRQ setup */

    pci_info(pci_dev, "AMD Sensor Fusion Hub device initialized\n");

    return 0;

clear_master:
    pci_clear_master(pci_dev);
    return rc;
}

dmesg:

[    6.954524] amd-sfh-pci 0000:04:00.7: enabling device (0000 -> 0002)
[    6.954641] amd-sfh-pci 0000:04:00.7: MSI: false
[    6.954642] amd-sfh-pci 0000:04:00.7: No MSI: false
[    6.954791] amd-sfh-pci 0000:04:00.7: allocated 2 IRQ vectors
[    6.954792] amd-sfh-pci 0000:04:00.7: requesting IRQ: 0
[    6.954825] amd-sfh-pci 0000:04:00.7: requesting IRQ: 1
[    6.954860] amd-sfh-pci 0000:04:00.7: MSI: true
[    6.954861] amd-sfh-pci 0000:04:00.7: No MSI: false
[    6.954861] amd-sfh-pci 0000:04:00.7: AMD Sensor Fusion Hub device initialized
[    6.969691] amd-sfh-pci 0000:04:00.7: [Firmware Bug]: No sensors marked active!
[    6.971265] amd-sfh-pci 0000:04:00.7: sensor mask: 0x000001
[    7.548189] hid-generic 0018:03FE:0001.0001: hidraw0: I2C HID v0.01 Device [amd-sfh-accel] on 

AMD 的原始文档提到了中断,但没有具体说明它们是如何生成的。

通过反复试验,我发现我需要将 P2C 寄存器 0x10690 设置为 1 才能在设备上启用中断。有了这个设置,设备就会用中断淹没驱动程序。我仍在弄清楚如何让设备仅在对 C2P 寄存器的实际写入事件时生成中断。

好的,我发现了,如何: https://github.com/conqp/linux/blob/5ba797452a794100d65d103e8eb53f64ae14d1d0/drivers/hid/amd-sfh-hid/amd-sfh-pci.c#L301