ioatdma: channel reset scheme fixup on Intel Atom S1200 platforms

The Intel Atom S1200 family ioatdma changed the channel reset behavior.
It does a reset similar to PCI FLR by resetting all the MSIX
registers. We have to re-init msix interrupts because of this. This
workaround is only specific to this platform and is not expected to carry
over to the later generations.

Signed-off-by: Dave Jiang <dave.jiang@intel.com>
Acked-by: Dan Williams <djbw@fb.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
This commit is contained in:
Dave Jiang 2013-03-26 15:42:47 -07:00 committed by Vinod Koul
parent d92a8d7cbb
commit 8a52b9ff11
3 changed files with 171 additions and 83 deletions

View file

@ -892,7 +892,7 @@ MODULE_PARM_DESC(ioat_interrupt_style,
* ioat_dma_setup_interrupts - setup interrupt handler
* @device: ioat device
*/
static int ioat_dma_setup_interrupts(struct ioatdma_device *device)
int ioat_dma_setup_interrupts(struct ioatdma_device *device)
{
struct ioat_chan_common *chan;
struct pci_dev *pdev = device->pdev;
@ -941,6 +941,7 @@ static int ioat_dma_setup_interrupts(struct ioatdma_device *device)
}
}
intrctrl |= IOAT_INTRCTRL_MSIX_VECTOR_CONTROL;
device->irq_mode = IOAT_MSIX;
goto done;
msix_single_vector:
@ -956,6 +957,7 @@ static int ioat_dma_setup_interrupts(struct ioatdma_device *device)
pci_disable_msix(pdev);
goto msi;
}
device->irq_mode = IOAT_MSIX_SINGLE;
goto done;
msi:
@ -969,6 +971,7 @@ static int ioat_dma_setup_interrupts(struct ioatdma_device *device)
pci_disable_msi(pdev);
goto intx;
}
device->irq_mode = IOAT_MSIX;
goto done;
intx:
@ -977,6 +980,7 @@ static int ioat_dma_setup_interrupts(struct ioatdma_device *device)
if (err)
goto err_no_irq;
device->irq_mode = IOAT_INTX;
done:
if (device->intr_quirk)
device->intr_quirk(device);
@ -987,9 +991,11 @@ static int ioat_dma_setup_interrupts(struct ioatdma_device *device)
err_no_irq:
/* Disable all interrupt generation */
writeb(0, device->reg_base + IOAT_INTRCTRL_OFFSET);
device->irq_mode = IOAT_NOIRQ;
dev_err(dev, "no usable interrupts\n");
return err;
}
EXPORT_SYMBOL(ioat_dma_setup_interrupts);
static void ioat_disable_interrupts(struct ioatdma_device *device)
{

View file

@ -48,6 +48,14 @@
*/
#define NULL_DESC_BUFFER_SIZE 1
enum ioat_irq_mode {
IOAT_NOIRQ = 0,
IOAT_MSIX,
IOAT_MSIX_SINGLE,
IOAT_MSI,
IOAT_INTX
};
/**
* struct ioatdma_device - internal representation of a IOAT device
* @pdev: PCI-Express device
@ -77,6 +85,7 @@ struct ioatdma_device {
struct msix_entry msix_entries[4];
struct ioat_chan_common *idx[4];
struct dca_provider *dca;
enum ioat_irq_mode irq_mode;
void (*intr_quirk)(struct ioatdma_device *device);
int (*enumerate_channels)(struct ioatdma_device *device);
int (*reset_hw)(struct ioat_chan_common *chan);
@ -341,6 +350,7 @@ bool ioat_cleanup_preamble(struct ioat_chan_common *chan,
dma_addr_t *phys_complete);
void ioat_kobject_add(struct ioatdma_device *device, struct kobj_type *type);
void ioat_kobject_del(struct ioatdma_device *device);
int ioat_dma_setup_interrupts(struct ioatdma_device *device);
extern const struct sysfs_ops ioat_sysfs_ops;
extern struct ioat_sysfs_entry ioat_version_attr;
extern struct ioat_sysfs_entry ioat_cap_attr;

View file

@ -111,6 +111,103 @@ static void pq_set_src(struct ioat_raw_descriptor *descs[2],
pq->coef[idx] = coef;
}
static bool is_jf_ioat(struct pci_dev *pdev)
{
switch (pdev->device) {
case PCI_DEVICE_ID_INTEL_IOAT_JSF0:
case PCI_DEVICE_ID_INTEL_IOAT_JSF1:
case PCI_DEVICE_ID_INTEL_IOAT_JSF2:
case PCI_DEVICE_ID_INTEL_IOAT_JSF3:
case PCI_DEVICE_ID_INTEL_IOAT_JSF4:
case PCI_DEVICE_ID_INTEL_IOAT_JSF5:
case PCI_DEVICE_ID_INTEL_IOAT_JSF6:
case PCI_DEVICE_ID_INTEL_IOAT_JSF7:
case PCI_DEVICE_ID_INTEL_IOAT_JSF8:
case PCI_DEVICE_ID_INTEL_IOAT_JSF9:
return true;
default:
return false;
}
}
static bool is_snb_ioat(struct pci_dev *pdev)
{
switch (pdev->device) {
case PCI_DEVICE_ID_INTEL_IOAT_SNB0:
case PCI_DEVICE_ID_INTEL_IOAT_SNB1:
case PCI_DEVICE_ID_INTEL_IOAT_SNB2:
case PCI_DEVICE_ID_INTEL_IOAT_SNB3:
case PCI_DEVICE_ID_INTEL_IOAT_SNB4:
case PCI_DEVICE_ID_INTEL_IOAT_SNB5:
case PCI_DEVICE_ID_INTEL_IOAT_SNB6:
case PCI_DEVICE_ID_INTEL_IOAT_SNB7:
case PCI_DEVICE_ID_INTEL_IOAT_SNB8:
case PCI_DEVICE_ID_INTEL_IOAT_SNB9:
return true;
default:
return false;
}
}
static bool is_ivb_ioat(struct pci_dev *pdev)
{
switch (pdev->device) {
case PCI_DEVICE_ID_INTEL_IOAT_IVB0:
case PCI_DEVICE_ID_INTEL_IOAT_IVB1:
case PCI_DEVICE_ID_INTEL_IOAT_IVB2:
case PCI_DEVICE_ID_INTEL_IOAT_IVB3:
case PCI_DEVICE_ID_INTEL_IOAT_IVB4:
case PCI_DEVICE_ID_INTEL_IOAT_IVB5:
case PCI_DEVICE_ID_INTEL_IOAT_IVB6:
case PCI_DEVICE_ID_INTEL_IOAT_IVB7:
case PCI_DEVICE_ID_INTEL_IOAT_IVB8:
case PCI_DEVICE_ID_INTEL_IOAT_IVB9:
return true;
default:
return false;
}
}
static bool is_hsw_ioat(struct pci_dev *pdev)
{
switch (pdev->device) {
case PCI_DEVICE_ID_INTEL_IOAT_HSW0:
case PCI_DEVICE_ID_INTEL_IOAT_HSW1:
case PCI_DEVICE_ID_INTEL_IOAT_HSW2:
case PCI_DEVICE_ID_INTEL_IOAT_HSW3:
case PCI_DEVICE_ID_INTEL_IOAT_HSW4:
case PCI_DEVICE_ID_INTEL_IOAT_HSW5:
case PCI_DEVICE_ID_INTEL_IOAT_HSW6:
case PCI_DEVICE_ID_INTEL_IOAT_HSW7:
case PCI_DEVICE_ID_INTEL_IOAT_HSW8:
case PCI_DEVICE_ID_INTEL_IOAT_HSW9:
return true;
default:
return false;
}
}
static bool is_xeon_cb32(struct pci_dev *pdev)
{
return is_jf_ioat(pdev) || is_snb_ioat(pdev) || is_ivb_ioat(pdev) ||
is_hsw_ioat(pdev);
}
static bool is_bwd_ioat(struct pci_dev *pdev)
{
switch (pdev->device) {
case PCI_DEVICE_ID_INTEL_IOAT_BWD0:
case PCI_DEVICE_ID_INTEL_IOAT_BWD1:
case PCI_DEVICE_ID_INTEL_IOAT_BWD2:
case PCI_DEVICE_ID_INTEL_IOAT_BWD3:
return true;
default:
return false;
}
}
static void ioat3_dma_unmap(struct ioat2_dma_chan *ioat,
struct ioat_ring_ent *desc, int idx)
{
@ -1168,6 +1265,56 @@ static int ioat3_dma_self_test(struct ioatdma_device *device)
return 0;
}
static int ioat3_irq_reinit(struct ioatdma_device *device)
{
int msixcnt = device->common.chancnt;
struct pci_dev *pdev = device->pdev;
int i;
struct msix_entry *msix;
struct ioat_chan_common *chan;
int err = 0;
switch (device->irq_mode) {
case IOAT_MSIX:
for (i = 0; i < msixcnt; i++) {
msix = &device->msix_entries[i];
chan = ioat_chan_by_index(device, i);
devm_free_irq(&pdev->dev, msix->vector, chan);
}
pci_disable_msix(pdev);
break;
case IOAT_MSIX_SINGLE:
msix = &device->msix_entries[0];
chan = ioat_chan_by_index(device, 0);
devm_free_irq(&pdev->dev, msix->vector, chan);
pci_disable_msix(pdev);
break;
case IOAT_MSI:
chan = ioat_chan_by_index(device, 0);
devm_free_irq(&pdev->dev, pdev->irq, chan);
pci_disable_msi(pdev);
break;
case IOAT_INTX:
chan = ioat_chan_by_index(device, 0);
devm_free_irq(&pdev->dev, pdev->irq, chan);
break;
default:
return 0;
}
device->irq_mode = IOAT_NOIRQ;
err = ioat_dma_setup_interrupts(device);
return err;
}
static int ioat3_reset_hw(struct ioat_chan_common *chan)
{
/* throw away whatever the channel was doing and get it
@ -1199,91 +1346,16 @@ static int ioat3_reset_hw(struct ioat_chan_common *chan)
if (dev_id == PCI_DEVICE_ID_INTEL_IOAT_TBG0)
pci_write_config_dword(pdev, IOAT_PCI_DMAUNCERRSTS_OFFSET, 0x10);
return ioat2_reset_sync(chan, msecs_to_jiffies(200));
}
static bool is_jf_ioat(struct pci_dev *pdev)
{
switch (pdev->device) {
case PCI_DEVICE_ID_INTEL_IOAT_JSF0:
case PCI_DEVICE_ID_INTEL_IOAT_JSF1:
case PCI_DEVICE_ID_INTEL_IOAT_JSF2:
case PCI_DEVICE_ID_INTEL_IOAT_JSF3:
case PCI_DEVICE_ID_INTEL_IOAT_JSF4:
case PCI_DEVICE_ID_INTEL_IOAT_JSF5:
case PCI_DEVICE_ID_INTEL_IOAT_JSF6:
case PCI_DEVICE_ID_INTEL_IOAT_JSF7:
case PCI_DEVICE_ID_INTEL_IOAT_JSF8:
case PCI_DEVICE_ID_INTEL_IOAT_JSF9:
return true;
default:
return false;
}
}
static bool is_snb_ioat(struct pci_dev *pdev)
{
switch (pdev->device) {
case PCI_DEVICE_ID_INTEL_IOAT_SNB0:
case PCI_DEVICE_ID_INTEL_IOAT_SNB1:
case PCI_DEVICE_ID_INTEL_IOAT_SNB2:
case PCI_DEVICE_ID_INTEL_IOAT_SNB3:
case PCI_DEVICE_ID_INTEL_IOAT_SNB4:
case PCI_DEVICE_ID_INTEL_IOAT_SNB5:
case PCI_DEVICE_ID_INTEL_IOAT_SNB6:
case PCI_DEVICE_ID_INTEL_IOAT_SNB7:
case PCI_DEVICE_ID_INTEL_IOAT_SNB8:
case PCI_DEVICE_ID_INTEL_IOAT_SNB9:
return true;
default:
return false;
}
}
static bool is_ivb_ioat(struct pci_dev *pdev)
{
switch (pdev->device) {
case PCI_DEVICE_ID_INTEL_IOAT_IVB0:
case PCI_DEVICE_ID_INTEL_IOAT_IVB1:
case PCI_DEVICE_ID_INTEL_IOAT_IVB2:
case PCI_DEVICE_ID_INTEL_IOAT_IVB3:
case PCI_DEVICE_ID_INTEL_IOAT_IVB4:
case PCI_DEVICE_ID_INTEL_IOAT_IVB5:
case PCI_DEVICE_ID_INTEL_IOAT_IVB6:
case PCI_DEVICE_ID_INTEL_IOAT_IVB7:
case PCI_DEVICE_ID_INTEL_IOAT_IVB8:
case PCI_DEVICE_ID_INTEL_IOAT_IVB9:
return true;
default:
return false;
err = ioat2_reset_sync(chan, msecs_to_jiffies(200));
if (err) {
dev_err(&pdev->dev, "Failed to reset!\n");
return err;
}
}
if (device->irq_mode != IOAT_NOIRQ && is_bwd_ioat(pdev))
err = ioat3_irq_reinit(device);
static bool is_hsw_ioat(struct pci_dev *pdev)
{
switch (pdev->device) {
case PCI_DEVICE_ID_INTEL_IOAT_HSW0:
case PCI_DEVICE_ID_INTEL_IOAT_HSW1:
case PCI_DEVICE_ID_INTEL_IOAT_HSW2:
case PCI_DEVICE_ID_INTEL_IOAT_HSW3:
case PCI_DEVICE_ID_INTEL_IOAT_HSW4:
case PCI_DEVICE_ID_INTEL_IOAT_HSW5:
case PCI_DEVICE_ID_INTEL_IOAT_HSW6:
case PCI_DEVICE_ID_INTEL_IOAT_HSW7:
case PCI_DEVICE_ID_INTEL_IOAT_HSW8:
case PCI_DEVICE_ID_INTEL_IOAT_HSW9:
return true;
default:
return false;
}
}
static bool is_xeon_cb32(struct pci_dev *pdev)
{
return is_jf_ioat(pdev) || is_snb_ioat(pdev) || is_ivb_ioat(pdev) ||
is_hsw_ioat(pdev);
return err;
}
int ioat3_dma_probe(struct ioatdma_device *device, int dca)