Work around devices which return all zeros for reads of existing MSI-X

table VCTRL registers.

Unconditionally program the MSI-X vector control Mask field for MSI-X
table entries without regarud for Mask's previous value. Some devices
return all zeros on reads of the VCTRL registers, which would cause us
to skip disabling interrupts. This fixes the Samsung SM961/PM961 SSDs
which are return zero starting from offset 0x3084 within the memory
region specified by BAR0, even when they are active MSI-X vectors.

The Illumos kernel writes these unconditionally to 0 or 1. However,
section 6.8.2.9 of the PCI Local Bus 3.0 spec (dated Feb 3, 2004)
states for bits 31::01:
	After reset, the state of these bits must be 0. However, for
	potential future use, software must preserve the value of
	these reserved bits when modifying the value of other Vector
	Control bits. If software modifies the value of these reserved
	bits, the result is undefined."
so we always set or clear the Mask bit, but otherwise preserves the
old value.

PR: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=211713
Reviewed By: imp, jhb
Submitted by: Ka Ho Ng
MFC After: 1 week
Differential Revision: https://reviews.freebsd.org/D20873
This commit is contained in:
Warner Losh 2019-07-08 19:38:49 +00:00
parent 74d565fd96
commit e64f3dee49
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=349845

View File

@ -1671,10 +1671,13 @@ pci_mask_msix(device_t dev, u_int index)
KASSERT(msix->msix_msgnum > index, ("bogus index"));
offset = msix->msix_table_offset + index * 16 + 12;
val = bus_read_4(msix->msix_table_res, offset);
if (!(val & PCIM_MSIX_VCTRL_MASK)) {
val |= PCIM_MSIX_VCTRL_MASK;
bus_write_4(msix->msix_table_res, offset, val);
}
val |= PCIM_MSIX_VCTRL_MASK;
/*
* Some devices (e.g. Samsung PM961) do not support reads of this
* register, so always write the new value.
*/
bus_write_4(msix->msix_table_res, offset, val);
}
void
@ -1687,10 +1690,13 @@ pci_unmask_msix(device_t dev, u_int index)
KASSERT(msix->msix_table_len > index, ("bogus index"));
offset = msix->msix_table_offset + index * 16 + 12;
val = bus_read_4(msix->msix_table_res, offset);
if (val & PCIM_MSIX_VCTRL_MASK) {
val &= ~PCIM_MSIX_VCTRL_MASK;
bus_write_4(msix->msix_table_res, offset, val);
}
val &= ~PCIM_MSIX_VCTRL_MASK;
/*
* Some devices (e.g. Samsung PM961) do not support reads of this
* register, so always write the new value.
*/
bus_write_4(msix->msix_table_res, offset, val);
}
int