+/**
+ * set_controller_speed - set the frequency and/or mode of a specific
+ * controller segment.
+ *
+ * @ctrl: controller to change frequency/mode for.
+ * @adapter_speed: the speed of the adapter we want to match.
+ * @hp_slot: the slot number where the adapter is installed.
+ *
+ * Returns 0 if we successfully change frequency and/or mode to match the
+ * adapter speed.
+ *
+ */
+static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_slot)
+{
+ struct slot *slot;
+ u8 reg;
+ u8 slot_power = readb(ctrl->hpc_reg + SLOT_POWER);
+ u16 reg16;
+ u32 leds = readl(ctrl->hpc_reg + LED_CONTROL);
+
+ if (ctrl->speed == adapter_speed)
+ return 0;
+
+ /* We don't allow freq/mode changes if we find another adapter running
+ * in another slot on this controller */
+ for(slot = ctrl->slot; slot; slot = slot->next) {
+ if (slot->device == (hp_slot + ctrl->slot_device_offset))
+ continue;
+ if (!slot->hotplug_slot && !slot->hotplug_slot->info)
+ continue;
+ if (slot->hotplug_slot->info->adapter_status == 0)
+ continue;
+ /* If another adapter is running on the same segment but at a
+ * lower speed/mode, we allow the new adapter to function at
+ * this rate if supported */
+ if (ctrl->speed < adapter_speed)
+ return 0;
+
+ return 1;
+ }
+
+ /* If the controller doesn't support freq/mode changes and the
+ * controller is running at a higher mode, we bail */
+ if ((ctrl->speed > adapter_speed) && (!ctrl->pcix_speed_capability))
+ return 1;
+
+ /* But we allow the adapter to run at a lower rate if possible */
+ if ((ctrl->speed < adapter_speed) && (!ctrl->pcix_speed_capability))
+ return 0;
+
+ /* We try to set the max speed supported by both the adapter and
+ * controller */
+ if (ctrl->speed_capability < adapter_speed) {
+ if (ctrl->speed == ctrl->speed_capability)
+ return 0;
+ adapter_speed = ctrl->speed_capability;
+ }
+
+ writel(0x0L, ctrl->hpc_reg + LED_CONTROL);
+ writeb(0x00, ctrl->hpc_reg + SLOT_ENABLE);
+
+ set_SOGO(ctrl);
+ wait_for_ctrl_irq(ctrl);
+
+ if (adapter_speed != PCI_SPEED_133MHz_PCIX)
+ reg = 0xF5;
+ else
+ reg = 0xF4;
+ pci_write_config_byte(ctrl->pci_dev, 0x41, reg);
+
+ reg16 = readw(ctrl->hpc_reg + NEXT_CURR_FREQ);
+ reg16 &= ~0x000F;
+ switch(adapter_speed) {
+ case(PCI_SPEED_133MHz_PCIX):
+ reg = 0x75;
+ reg16 |= 0xB;
+ break;
+ case(PCI_SPEED_100MHz_PCIX):
+ reg = 0x74;
+ reg16 |= 0xA;
+ break;
+ case(PCI_SPEED_66MHz_PCIX):
+ reg = 0x73;
+ reg16 |= 0x9;
+ break;
+ case(PCI_SPEED_66MHz):
+ reg = 0x73;
+ reg16 |= 0x1;
+ break;
+ default: /* 33MHz PCI 2.2 */
+ reg = 0x71;
+ break;
+
+ }
+ reg16 |= 0xB << 12;
+ writew(reg16, ctrl->hpc_reg + NEXT_CURR_FREQ);
+
+ mdelay(5);
+
+ /* Reenable interrupts */
+ writel(0, ctrl->hpc_reg + INT_MASK);
+
+ pci_write_config_byte(ctrl->pci_dev, 0x41, reg);
+
+ /* Restart state machine */
+ reg = ~0xF;
+ pci_read_config_byte(ctrl->pci_dev, 0x43, ®);
+ pci_write_config_byte(ctrl->pci_dev, 0x43, reg);
+
+ /* Only if mode change...*/
+ if (((ctrl->speed == PCI_SPEED_66MHz) && (adapter_speed == PCI_SPEED_66MHz_PCIX)) ||
+ ((ctrl->speed == PCI_SPEED_66MHz_PCIX) && (adapter_speed == PCI_SPEED_66MHz)))
+ set_SOGO(ctrl);
+
+ wait_for_ctrl_irq(ctrl);
+ mdelay(1100);
+
+ /* Restore LED/Slot state */
+ writel(leds, ctrl->hpc_reg + LED_CONTROL);
+ writeb(slot_power, ctrl->hpc_reg + SLOT_ENABLE);
+
+ set_SOGO(ctrl);
+ wait_for_ctrl_irq(ctrl);
+
+ ctrl->speed = adapter_speed;
+ slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
+
+ info("Successfully changed frequency/mode for adapter in slot %d\n",
+ slot->number);
+ return 0;
+}
+