2006-12-08 18:28:36

by Stephen Hemminger

[permalink] [raw]
Subject: [PATCH 2/6] e1000: use pcix_set_mmrbc

Use new pcix_set_mmrbc interface, this prevents possible data
corruption on broken PCI-X chipsets.

The E1000 PCI hardware wrappers are a nuisance.
Untested on real hardware.

Signed-off-by: Stephen Hemminger <[email protected]>

---
drivers/net/e1000/e1000_hw.c | 23 ++---------------------
drivers/net/e1000/e1000_hw.h | 1 +
drivers/net/e1000/e1000_main.c | 8 ++++++++
3 files changed, 11 insertions(+), 21 deletions(-)

--- pci-x.orig/drivers/net/e1000/e1000_hw.c
+++ pci-x/drivers/net/e1000/e1000_hw.c
@@ -846,10 +846,6 @@ e1000_init_hw(struct e1000_hw *hw)
uint32_t ctrl;
uint32_t i;
int32_t ret_val;
- uint16_t pcix_cmd_word;
- uint16_t pcix_stat_hi_word;
- uint16_t cmd_mmrbc;
- uint16_t stat_mmrbc;
uint32_t mta_size;
uint32_t reg_data;
uint32_t ctrl_ext;
@@ -939,23 +935,8 @@ e1000_init_hw(struct e1000_hw *hw)
break;
default:
/* Workaround for PCI-X problem when BIOS sets MMRBC incorrectly. */
- if (hw->bus_type == e1000_bus_type_pcix) {
- e1000_read_pci_cfg(hw, PCIX_COMMAND_REGISTER, &pcix_cmd_word);
- e1000_read_pci_cfg(hw, PCIX_STATUS_REGISTER_HI,
- &pcix_stat_hi_word);
- cmd_mmrbc = (pcix_cmd_word & PCIX_COMMAND_MMRBC_MASK) >>
- PCIX_COMMAND_MMRBC_SHIFT;
- stat_mmrbc = (pcix_stat_hi_word & PCIX_STATUS_HI_MMRBC_MASK) >>
- PCIX_STATUS_HI_MMRBC_SHIFT;
- if (stat_mmrbc == PCIX_STATUS_HI_MMRBC_4K)
- stat_mmrbc = PCIX_STATUS_HI_MMRBC_2K;
- if (cmd_mmrbc > stat_mmrbc) {
- pcix_cmd_word &= ~PCIX_COMMAND_MMRBC_MASK;
- pcix_cmd_word |= stat_mmrbc << PCIX_COMMAND_MMRBC_SHIFT;
- e1000_write_pci_cfg(hw, PCIX_COMMAND_REGISTER,
- &pcix_cmd_word);
- }
- }
+ if (hw->bus_type == e1000_bus_type_pcix)
+ e1000_pcix_set_mmrbc(hw, 2048);
break;
}

--- pci-x.orig/drivers/net/e1000/e1000_main.c
+++ pci-x/drivers/net/e1000/e1000_main.c
@@ -4770,6 +4770,14 @@ e1000_read_pci_cfg(struct e1000_hw *hw,
}

void
+e1000_pcix_set_mmrbc(struct e1000_hw *hw, int mmrbc)
+{
+ struct e1000_adapter *adapter = hw->back;
+
+ pcix_set_mmrbc(adapter->pdev, mmrbc);
+}
+
+void
e1000_write_pci_cfg(struct e1000_hw *hw, uint32_t reg, uint16_t *value)
{
struct e1000_adapter *adapter = hw->back;
--- pci-x.orig/drivers/net/e1000/e1000_hw.h
+++ pci-x/drivers/net/e1000/e1000_hw.h
@@ -421,6 +421,7 @@ void e1000_tbi_adjust_stats(struct e1000
void e1000_get_bus_info(struct e1000_hw *hw);
void e1000_pci_set_mwi(struct e1000_hw *hw);
void e1000_pci_clear_mwi(struct e1000_hw *hw);
+void e1000_pcix_set_mmrbc(struct e1000_hw *hw, int mmbrc);
void e1000_read_pci_cfg(struct e1000_hw *hw, uint32_t reg, uint16_t * value);
void e1000_write_pci_cfg(struct e1000_hw *hw, uint32_t reg, uint16_t * value);
int32_t e1000_read_pcie_cap_reg(struct e1000_hw *hw, uint32_t reg, uint16_t *value);

--


2006-12-08 21:45:17

by Roland Dreier

[permalink] [raw]
Subject: Re: [PATCH 2/6] e1000: use pcix_set_mmrbc

> - if (hw->bus_type == e1000_bus_type_pcix) {
> - e1000_read_pci_cfg(hw, PCIX_COMMAND_REGISTER, &pcix_cmd_word);
> - e1000_read_pci_cfg(hw, PCIX_STATUS_REGISTER_HI,
> - &pcix_stat_hi_word);
> - cmd_mmrbc = (pcix_cmd_word & PCIX_COMMAND_MMRBC_MASK) >>
> - PCIX_COMMAND_MMRBC_SHIFT;
> - stat_mmrbc = (pcix_stat_hi_word & PCIX_STATUS_HI_MMRBC_MASK) >>
> - PCIX_STATUS_HI_MMRBC_SHIFT;
> - if (stat_mmrbc == PCIX_STATUS_HI_MMRBC_4K)
> - stat_mmrbc = PCIX_STATUS_HI_MMRBC_2K;
> - if (cmd_mmrbc > stat_mmrbc) {
> - pcix_cmd_word &= ~PCIX_COMMAND_MMRBC_MASK;
> - pcix_cmd_word |= stat_mmrbc << PCIX_COMMAND_MMRBC_SHIFT;
> - e1000_write_pci_cfg(hw, PCIX_COMMAND_REGISTER,
> - &pcix_cmd_word);
> - }
> - }
> + if (hw->bus_type == e1000_bus_type_pcix)
> + e1000_pcix_set_mmrbc(hw, 2048);

This changes the behavior of the driver. The existing driver only
sets MMRBC if it's bigger than min(2048, value in the status register).
You're setting MMRBC to 2048 even if it starts out at a smaller value.

- R.

2006-12-08 22:43:45

by Stephen Hemminger

[permalink] [raw]
Subject: Re: [PATCH 2/6] e1000: use pcix_set_mmrbc

On Fri, 08 Dec 2006 13:45:05 -0800
Roland Dreier <[email protected]> wrote:

> > - if (hw->bus_type == e1000_bus_type_pcix) {
> > - e1000_read_pci_cfg(hw, PCIX_COMMAND_REGISTER, &pcix_cmd_word);
> > - e1000_read_pci_cfg(hw, PCIX_STATUS_REGISTER_HI,
> > - &pcix_stat_hi_word);
> > - cmd_mmrbc = (pcix_cmd_word & PCIX_COMMAND_MMRBC_MASK) >>
> > - PCIX_COMMAND_MMRBC_SHIFT;
> > - stat_mmrbc = (pcix_stat_hi_word & PCIX_STATUS_HI_MMRBC_MASK) >>
> > - PCIX_STATUS_HI_MMRBC_SHIFT;
> > - if (stat_mmrbc == PCIX_STATUS_HI_MMRBC_4K)
> > - stat_mmrbc = PCIX_STATUS_HI_MMRBC_2K;
> > - if (cmd_mmrbc > stat_mmrbc) {
> > - pcix_cmd_word &= ~PCIX_COMMAND_MMRBC_MASK;
> > - pcix_cmd_word |= stat_mmrbc << PCIX_COMMAND_MMRBC_SHIFT;
> > - e1000_write_pci_cfg(hw, PCIX_COMMAND_REGISTER,
> > - &pcix_cmd_word);
> > - }
> > - }
> > + if (hw->bus_type == e1000_bus_type_pcix)
> > + e1000_pcix_set_mmrbc(hw, 2048);
>
> This changes the behavior of the driver. The existing driver only
> sets MMRBC if it's bigger than min(2048, value in the status register).
> You're setting MMRBC to 2048 even if it starts out at a smaller value.
>
> - R.

Hmm.. looks like all that code should really be moved off to PCI bus
quirk/setup. None of it is E1000 specific. Something like this (untested):

---
drivers/pci/quirks.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)

--- pci-x.orig/drivers/pci/quirks.c
+++ pci-x/drivers/pci/quirks.c
@@ -1719,6 +1719,36 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NV
#endif /* CONFIG_PCI_MSI */

EXPORT_SYMBOL(pcie_mch_quirk);
+
+/* Check that BIOS has not set PCI-X MMRBC to value bigger than board allows */
+static void __devinit quirk_pcix_mmrbc(struct pci_dev *dev)
+{
+ int cap;
+ u32 stat;
+ u16 cmd, m, c;
+
+ cap = pci_find_capability(dev, PCI_CAP_ID_PCIX);
+ if (cap <= 0)
+ return;
+
+ if (pci_read_config_dword(dev, cap + PCI_X_STATUS, &stat) ||
+ pci_read_config_word(dev, cap + PCI_X_CMD, &cmd))
+ return;
+
+ m = (stat & PCI_X_STATUS_MAX_READ) >> 21; /* max possible MRRBC*/
+ c = (cmd & PCI_X_CMD_MAX_READ) >> 2; /* current MMRBC */
+ if (c > m) {
+ printk(KERN_INFO "PCIX: %s resetting MMRBC to %d\n",
+ pci_name(dev), 512 << m);
+
+ cmd &= ~PCI_X_CMD_MAX_READ;
+ cmd |= m << 2;
+ pci_write_config_dword(dev, cap + PCI_X_CMD, cmd);
+ }
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, quirk_pcix_mmrbc);
+
+
#ifdef CONFIG_HOTPLUG
EXPORT_SYMBOL(pci_fixup_device);
#endif


--
Stephen Hemminger <[email protected]>

2006-12-08 22:58:16

by Kok, Auke

[permalink] [raw]
Subject: Re: [PATCH 2/6] e1000: use pcix_set_mmrbc

Stephen Hemminger wrote:
> On Fri, 08 Dec 2006 13:45:05 -0800
> Roland Dreier <[email protected]> wrote:
>
>> > - if (hw->bus_type == e1000_bus_type_pcix) {
>> > - e1000_read_pci_cfg(hw, PCIX_COMMAND_REGISTER, &pcix_cmd_word);
>> > - e1000_read_pci_cfg(hw, PCIX_STATUS_REGISTER_HI,
>> > - &pcix_stat_hi_word);
>> > - cmd_mmrbc = (pcix_cmd_word & PCIX_COMMAND_MMRBC_MASK) >>
>> > - PCIX_COMMAND_MMRBC_SHIFT;
>> > - stat_mmrbc = (pcix_stat_hi_word & PCIX_STATUS_HI_MMRBC_MASK) >>
>> > - PCIX_STATUS_HI_MMRBC_SHIFT;
>> > - if (stat_mmrbc == PCIX_STATUS_HI_MMRBC_4K)
>> > - stat_mmrbc = PCIX_STATUS_HI_MMRBC_2K;
>> > - if (cmd_mmrbc > stat_mmrbc) {
>> > - pcix_cmd_word &= ~PCIX_COMMAND_MMRBC_MASK;
>> > - pcix_cmd_word |= stat_mmrbc << PCIX_COMMAND_MMRBC_SHIFT;
>> > - e1000_write_pci_cfg(hw, PCIX_COMMAND_REGISTER,
>> > - &pcix_cmd_word);
>> > - }
>> > - }
>> > + if (hw->bus_type == e1000_bus_type_pcix)
>> > + e1000_pcix_set_mmrbc(hw, 2048);
>>
>> This changes the behavior of the driver. The existing driver only
>> sets MMRBC if it's bigger than min(2048, value in the status register).
>> You're setting MMRBC to 2048 even if it starts out at a smaller value.
>>
>> - R.
>
> Hmm.. looks like all that code should really be moved off to PCI bus
> quirk/setup. None of it is E1000 specific. Something like this (untested):

This is not true, and I have to NAK the original patch. Part of the code Stephan is
removing fixes a BUG in one of our *e1000 parts* that has the wrong size.

It would be nice to fix generix pci-x issues qith quirks for platforms but the
adjustment needs to stay for this specific e1000 case.

Perhaps we can accomodate that specific case so that it is apparent from our code, as is
not the case right now.

Auke

PS Thanks to Jeb for fishing this out ;)


>
> ---
> drivers/pci/quirks.c | 30 ++++++++++++++++++++++++++++++
> 1 file changed, 30 insertions(+)
>
> --- pci-x.orig/drivers/pci/quirks.c
> +++ pci-x/drivers/pci/quirks.c
> @@ -1719,6 +1719,36 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NV
> #endif /* CONFIG_PCI_MSI */
>
> EXPORT_SYMBOL(pcie_mch_quirk);
> +
> +/* Check that BIOS has not set PCI-X MMRBC to value bigger than board allows */
> +static void __devinit quirk_pcix_mmrbc(struct pci_dev *dev)
> +{
> + int cap;
> + u32 stat;
> + u16 cmd, m, c;
> +
> + cap = pci_find_capability(dev, PCI_CAP_ID_PCIX);
> + if (cap <= 0)
> + return;
> +
> + if (pci_read_config_dword(dev, cap + PCI_X_STATUS, &stat) ||
> + pci_read_config_word(dev, cap + PCI_X_CMD, &cmd))
> + return;
> +
> + m = (stat & PCI_X_STATUS_MAX_READ) >> 21; /* max possible MRRBC*/
> + c = (cmd & PCI_X_CMD_MAX_READ) >> 2; /* current MMRBC */
> + if (c > m) {
> + printk(KERN_INFO "PCIX: %s resetting MMRBC to %d\n",
> + pci_name(dev), 512 << m);
> +
> + cmd &= ~PCI_X_CMD_MAX_READ;
> + cmd |= m << 2;
> + pci_write_config_dword(dev, cap + PCI_X_CMD, cmd);
> + }
> +}
> +DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, quirk_pcix_mmrbc);
> +
> +
> #ifdef CONFIG_HOTPLUG
> EXPORT_SYMBOL(pci_fixup_device);
> #endif
>
>

2006-12-08 23:38:58

by Jeff Kirsher

[permalink] [raw]
Subject: Re: [PATCH 2/6] e1000: use pcix_set_mmrbc

On 12/8/06, Auke Kok <[email protected]> wrote:
> Stephen Hemminger wrote:
> > On Fri, 08 Dec 2006 13:45:05 -0800
> >
> > Hmm.. looks like all that code should really be moved off to PCI bus
> > quirk/setup. None of it is E1000 specific. Something like this (untested):
>
> This is not true, and I have to NAK the original patch. Part of the code Stephan is
> removing fixes a BUG in one of our *e1000 parts* that has the wrong size.
>
> It would be nice to fix generix pci-x issues qith quirks for platforms but the
> adjustment needs to stay for this specific e1000 case.
>
> Perhaps we can accomodate that specific case so that it is apparent from our code, as is
> not the case right now.
>
> Auke
>
> PS Thanks to Jeb for fishing this out ;)
>

Actually there are two issues that are being resolved with this function:
1- BIOS reports incorrect maximum memory read byte count (mmrbc).
This was seen on some older systems > 5 years ago.

2- EEPROM is reporting an incorrect mmrbc.

This function corrects both of these issues, Stephen second suggestion
of moving the BIOS fix to quirks.c is fine with me. Even with the
code added to quirks.c, we still need this workaround as is to correct
for EEPROM's reporting 4k for a mmrbc. So I am fine with Stephen's
second suggestion NAK the suggested change to e1000_hw.c

--
Cheers,
Jeff