pci_enable_device_mem

/**
 * pci_enable_device_mem - Initialize a device for use with Memory space
 * @dev: PCI device to be initialized
 *
 *  Initialize device before it's used by a driver. Ask low-level code
 *  to enable Memory resources. Wake up the device if it was suspended.
 *  Beware, this function can fail.
 */
int pci_enable_device_mem(struct pci_dev *dev)
{
    return pci_enable_device_flags(dev, IORESOURCE_MEM);
}

static int pci_enable_device_flags(struct pci_dev *dev, unsigned long flags)
{
    struct pci_dev *bridge;
    int err;
    int i, bars = 0;

    /*
     * Power state could be unknown at this point, either due to a fresh
     * boot or a device removal call.  So get the current power state
     * so that things like MSI message writing will behave as expected
     * (e.g. if the device really is in D0 at enable time).
     */
    if (dev->pm_cap) {
        u16 pmcsr;
        pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
        dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
    }

    if (atomic_inc_return(&dev->enable_cnt) > 1)
        return 0;        /* already enabled */

    bridge = pci_upstream_bridge(dev);
    if (bridge)
        pci_enable_bridge(bridge);

    /* only skip sriov related */
    for (i = 0; i <= PCI_ROM_RESOURCE; i++)
        if (dev->resource[i].flags & flags)
            bars |= (1 << i);
    for (i = PCI_BRIDGE_RESOURCES; i < DEVICE_COUNT_RESOURCE; i++)
        if (dev->resource[i].flags & flags)
            bars |= (1 << i);

    err = do_pci_enable_device(dev, bars);
    if (err < 0)
        atomic_dec(&dev->enable_cnt);
    return err;
}

static int do_pci_enable_device(struct pci_dev *dev, int bars)
{
    int err;
    struct pci_dev *bridge;
    u16 cmd;
    u8 pin;

    err = pci_set_power_state(dev, PCI_D0);
    if (err < 0 && err != -EIO)
        return err;

    bridge = pci_upstream_bridge(dev);
    if (bridge)
        pcie_aspm_powersave_config_link(bridge);

    err = pcibios_enable_device(dev, bars);
    if (err < 0)
        return err;
    pci_fixup_device(pci_fixup_enable, dev);

    if (dev->msi_enabled || dev->msix_enabled)
        return 0;

    pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
    if (pin) {
        pci_read_config_word(dev, PCI_COMMAND, &cmd);
        if (cmd & PCI_COMMAND_INTX_DISABLE)
            pci_write_config_word(dev, PCI_COMMAND,
                          cmd & ~PCI_COMMAND_INTX_DISABLE);
    }

    return 0;
}

int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
{
    int error;

    /* bound the state we're entering */
    if (state > PCI_D3cold)
        state = PCI_D3cold;
    else if (state < PCI_D0)
        state = PCI_D0;
    else if ((state == PCI_D1 || state == PCI_D2) && pci_no_d1d2(dev))
        /*
         * If the device or the parent bridge do not support PCI PM,
         * ignore the request if we're doing anything other than putting
         * it into D0 (which would only happen on boot).
         */
        return 0;

    /* Check if we're already there */
    if (dev->current_state == state)
        return 0;

    __pci_start_power_transition(dev, state);

    /* This device is quirked not to be put into D3, so
       don't put it in D3 */
    if (state >= PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3))
        return 0;

    /*
     * To put device in D3cold, we put device into D3hot in native
     * way, then put device into D3cold with platform ops
     */
    error = pci_raw_set_power_state(dev, state > PCI_D3hot ?
                    PCI_D3hot : state);

    if (!__pci_complete_power_transition(dev, state))
        error = 0;

    return error;
}

static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
{
    u16 pmcsr;
    bool need_restore = false;

    /* Check if we're already there */
    if (dev->current_state == state)
        return 0;

    if (!dev->pm_cap)
        return -EIO;

    if (state < PCI_D0 || state > PCI_D3hot)
        return -EINVAL;

    /* Validate current state:
     * Can enter D0 from any state, but if we can only go deeper
     * to sleep if we're already in a low power state
     */
    if (state != PCI_D0 && dev->current_state <= PCI_D3cold
        && dev->current_state > state) {
        dev_err(&dev->dev, "invalid power transition (from state %d to %d)\n",
            dev->current_state, state);
        return -EINVAL;
    }

    /* check if this device supports the desired state */
    if ((state == PCI_D1 && !dev->d1_support)
       || (state == PCI_D2 && !dev->d2_support))
        return -EIO;

    pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);

    /* If we're (effectively) in D3, force entire word to 0.
     * This doesn't affect PME_Status, disables PME_En, and
     * sets PowerState to 0.
     */
    switch (dev->current_state) {
    case PCI_D0:
    case PCI_D1:
    case PCI_D2:
        pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
        pmcsr |= state;
        break;
    case PCI_D3hot:
    case PCI_D3cold:
    case PCI_UNKNOWN: /* Boot-up */
        if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot
         && !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET))
            need_restore = true;
        /* Fall-through: force to D0 */
    default:
        pmcsr = 0;
        break;
    }

    /* enter specified state */
    pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr);

    /* Mandatory power management transition delays */
    /* see PCI PM 1.1 5.6.1 table 18 */
    if (state == PCI_D3hot || dev->current_state == PCI_D3hot)
        pci_dev_d3_sleep(dev);
    else if (state == PCI_D2 || dev->current_state == PCI_D2)
        udelay(PCI_PM_D2_DELAY);

    pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
    dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
    if (dev->current_state != state && printk_ratelimit())
        dev_info(&dev->dev, "Refused to change power state, currently in D%d\n",
             dev->current_state);

    /*
     * According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT
     * INTERFACE SPECIFICATION, REV. 1.2", a device transitioning
     * from D3hot to D0 _may_ perform an internal reset, thereby
     * going to "D0 Uninitialized" rather than "D0 Initialized".
     * For example, at least some versions of the 3c905B and the
     * 3c556B exhibit this behaviour.
     *
     * At least some laptop BIOSen (e.g. the Thinkpad T21) leave
     * devices in a D3hot state at boot.  Consequently, we need to
     * restore at least the BARs so that the device will be
     * accessible to its driver.
     */
    if (need_restore)
        pci_restore_bars(dev);

    if (dev->bus->self)
        pcie_aspm_pm_state_change(dev->bus->self);

    return 0;
}

/**
 * pci_restore_bars - restore a device's BAR values (e.g. after wake-up)
 * @dev: PCI device to have its BARs restored
 *
 * Restore the BAR values for a given device, so as to make it
 * accessible by its driver.
 */
static void pci_restore_bars(struct pci_dev *dev)
{
    int i;

    for (i = 0; i < PCI_BRIDGE_RESOURCES; i++)
        pci_update_resource(dev, i);
}

void pci_update_resource(struct pci_dev *dev, int resno)
{
    if (resno <= PCI_ROM_RESOURCE)
        pci_std_update_resource(dev, resno);
#ifdef CONFIG_PCI_IOV
    else if (resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END)
        pci_iov_update_resource(dev, resno);
#endif
}

static void pci_std_update_resource(struct pci_dev *dev, int resno)
{
    struct pci_bus_region region;
    bool disable;
    u16 cmd;
    u32 new, check, mask;
    int reg;
    struct resource *res = dev->resource + resno;

    /* Per SR-IOV spec 3.4.1.11, VF BARs are RO zero */
    if (dev->is_virtfn)
        return;

    /*
     * Ignore resources for unimplemented BARs and unused resource slots
     * for 64 bit BARs.
     */
    if (!res->flags)
        return;

    if (res->flags & IORESOURCE_UNSET)
        return;

    /*
     * Ignore non-moveable resources.  This might be legacy resources for
     * which no functional BAR register exists or another important
     * system resource we shouldn't move around.
     */
    if (res->flags & IORESOURCE_PCI_FIXED)
        return;

    pcibios_resource_to_bus(dev->bus, &region, res);
    new = region.start;

    if (res->flags & IORESOURCE_IO) {
        mask = (u32)PCI_BASE_ADDRESS_IO_MASK;
        new |= res->flags & ~PCI_BASE_ADDRESS_IO_MASK;
    } else if (resno == PCI_ROM_RESOURCE) {
        mask = PCI_ROM_ADDRESS_MASK;
    } else {
        mask = (u32)PCI_BASE_ADDRESS_MEM_MASK;
        new |= res->flags & ~PCI_BASE_ADDRESS_MEM_MASK;
    }

    if (resno < PCI_ROM_RESOURCE) {
        reg = PCI_BASE_ADDRESS_0 + 4 * resno;
    } else if (resno == PCI_ROM_RESOURCE) {

        /*
         * Apparently some Matrox devices have ROM BARs that read
         * as zero when disabled, so don't update ROM BARs unless
         * they're enabled.  See https://lkml.org/lkml/2005/8/30/138.
         */
        if (!(res->flags & IORESOURCE_ROM_ENABLE))
            return;

        reg = dev->rom_base_reg;
        new |= PCI_ROM_ADDRESS_ENABLE;
    } else
        return;

    /*
     * We can't update a 64-bit BAR atomically, so when possible,
     * disable decoding so that a half-updated BAR won't conflict
     * with another device.
     */
    disable = (res->flags & IORESOURCE_MEM_64) && !dev->mmio_always_on;
    if (disable) {
        pci_read_config_word(dev, PCI_COMMAND, &cmd);
        pci_write_config_word(dev, PCI_COMMAND,
                      cmd & ~PCI_COMMAND_MEMORY);
    }

    pci_write_config_dword(dev, reg, new);
    pci_read_config_dword(dev, reg, &check);

    if ((new ^ check) & mask) {
        dev_err(&dev->dev, "BAR %d: error updating (%#08x != %#08x)\n",
            resno, new, check);
    }

    if (res->flags & IORESOURCE_MEM_64) {
        new = region.start >> 16 >> 16;
        pci_write_config_dword(dev, reg + 4, new);
        pci_read_config_dword(dev, reg + 4, &check);
        if (check != new) {
            dev_err(&dev->dev, "BAR %d: error updating (high %#08x != %#08x)\n",
                resno, new, check);
        }
    }

    if (disable)
        pci_write_config_word(dev, PCI_COMMAND, cmd);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值