diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c index bcc780ac4ec0..57f2b33a47dd 100644 --- a/drivers/block/nvme.c +++ b/drivers/block/nvme.c @@ -893,6 +893,8 @@ static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev) { int result; u32 aqa; + u64 cap; + unsigned long timeout; struct nvme_queue *nvmeq; dev->dbs = ((void __iomem *)dev->bar) + 4096; @@ -915,10 +917,18 @@ static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev) writeq(nvmeq->cq_dma_addr, &dev->bar->acq); writel(dev->ctrl_config, &dev->bar->cc); + cap = readq(&dev->bar->cap); + timeout = ((NVME_CAP_TIMEOUT(cap) + 1) * HZ / 2) + jiffies; + while (!(readl(&dev->bar->csts) & NVME_CSTS_RDY)) { msleep(100); if (fatal_signal_pending(current)) return -EINTR; + if (time_after(jiffies, timeout)) { + dev_err(&dev->pci_dev->dev, + "Device not ready; aborting initialisation\n"); + return -ENODEV; + } } result = queue_request_irq(dev, nvmeq, "nvme admin"); diff --git a/include/linux/nvme.h b/include/linux/nvme.h index 9d6febb91521..a19304fefa7d 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -35,6 +35,8 @@ struct nvme_bar { __u64 acq; /* Admin CQ Base Address */ }; +#define NVME_CAP_TIMEOUT(cap) (((cap) >> 24) & 0xff) + enum { NVME_CC_ENABLE = 1 << 0, NVME_CC_CSS_NVM = 0 << 4,