/* Copyright 2013-2016 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include /* Debugging options */ #define PCIE_SLOT_PREFIX "PCIE-SLOT-%016llx " #define PCIE_SLOT_DBG(s, fmt, a...) \ prlog(PR_DEBUG, PCIE_SLOT_PREFIX fmt, (s)->id, ##a) static int64_t pcie_slot_get_presence_state(struct pci_slot *slot, uint8_t *val) { struct phb *phb = slot->phb; struct pci_device *pd = slot->pd; uint32_t ecap; uint16_t state; /* The presence is always on if it's a switch upstream port */ if (pd->dev_type == PCIE_TYPE_SWITCH_UPPORT) { *val = OPAL_PCI_SLOT_PRESENT; return OPAL_SUCCESS; } /* * The presence is always on if a switch downstream port * doesn't support slot capability according to PCIE spec. */ if (pd->dev_type == PCIE_TYPE_SWITCH_DNPORT && !(slot->pcie_cap & PCICAP_EXP_CAP_SLOT)) { *val = OPAL_PCI_SLOT_PRESENT; return OPAL_SUCCESS; } /* Retrieve presence status */ ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTSTAT, &state); if (state & PCICAP_EXP_SLOTSTAT_PDETECTST) *val = OPAL_PCI_SLOT_PRESENT; else *val = OPAL_PCI_SLOT_EMPTY; return OPAL_SUCCESS; } static int64_t pcie_slot_get_link_state(struct pci_slot *slot, uint8_t *val) { struct phb *phb = slot->phb; struct pci_device *pd = slot->pd; uint32_t ecap; int16_t state; /* * The link behind switch upstream port is always on * since it doesn't have a valid link indicator. */ if (pd->dev_type == PCIE_TYPE_SWITCH_UPPORT) { *val = 1; return OPAL_SUCCESS; } /* Retrieve link width */ ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_LSTAT, &state); if (state & PCICAP_EXP_LSTAT_DLLL_ACT) *val = ((state & PCICAP_EXP_LSTAT_WIDTH) >> 4); else *val = 0; return OPAL_SUCCESS; } static int64_t pcie_slot_get_power_state(struct pci_slot *slot __unused, uint8_t *val) { /* We should return the cached power state that is same to * the PCI slot hotplug state (added/removed). Otherwise, * the OS will see mismatched states, causing the adapter * behind the slot can't be probed successfully on request * of hot add. So we could run into the situation where the * OS sees power-off but it's on in hardware. */ *val = slot->power_state; return OPAL_SUCCESS; } static int64_t pcie_slot_get_attention_state(struct pci_slot *slot, uint8_t *val) { struct phb *phb = slot->phb; struct pci_device *pd = slot->pd; uint32_t ecap; uint16_t state; /* Attention is off if the capability is missing */ if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_ATTNI)) { *val = 0; return OPAL_SUCCESS; } /* Retrieve attention state */ ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL, &state); state = (state & PCICAP_EXP_SLOTCTL_ATTNI) >> 6; switch (state) { case PCIE_INDIC_ON: *val = PCI_SLOT_ATTN_LED_ON; break; case PCIE_INDIC_BLINK: *val = PCI_SLOT_ATTN_LED_BLINK; break; case PCIE_INDIC_OFF: default: *val = PCI_SLOT_ATTN_LED_OFF; } return OPAL_SUCCESS; } static int64_t pcie_slot_get_latch_state(struct pci_slot *slot, uint8_t *val) { struct phb *phb = slot->phb; struct pci_device *pd = slot->pd; uint32_t ecap; uint16_t state; /* Latch is off if MRL sensor doesn't exist */ if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_MRLSENS)) { *val = 0; return OPAL_SUCCESS; } /* Retrieve MRL sensor state */ ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTSTAT, &state); if (state & PCICAP_EXP_SLOTSTAT_MRLSENSST) *val = 1; else *val = 0; return OPAL_SUCCESS; } static int64_t pcie_slot_set_attention_state(struct pci_slot *slot, uint8_t val) { struct phb *phb = slot->phb; struct pci_device *pd = slot->pd; uint32_t ecap; uint16_t state; /* Drop the request if functionality doesn't exist */ if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_ATTNI)) return OPAL_SUCCESS; /* Update with the requested state */ ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL, &state); state &= ~PCICAP_EXP_SLOTCTL_ATTNI; switch (val) { case PCI_SLOT_ATTN_LED_ON: state |= (PCIE_INDIC_ON << 6); break; case PCI_SLOT_ATTN_LED_BLINK: state |= (PCIE_INDIC_BLINK << 6); break; case PCI_SLOT_ATTN_LED_OFF: state |= (PCIE_INDIC_OFF << 6); break; default: prlog(PR_ERR, PCIE_SLOT_PREFIX "Invalid attention state (0x%x)\n", slot->id, val); return OPAL_PARAMETER; } pci_cfg_write16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL, state); return OPAL_SUCCESS; } static int64_t pcie_slot_set_power_state_ext(struct pci_slot *slot, uint8_t val, bool surprise_check) { struct phb *phb = slot->phb; struct pci_device *pd = slot->pd; uint32_t ecap; uint16_t state; if (slot->power_state == val) return OPAL_PARAMETER; /* Update the power state and return immediately if the power * control functionality isn't supported on the PCI slot. */ if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_PWCTRL)) { slot->power_state = val; return OPAL_SUCCESS; } /* The power supply to the slot should be always on when surprise * hotplug is claimed. For this case, update with the requested * power state and bail immediately. * * The PCIe link is likely down if we're powering on the slot upon * the detected presence. Nothing behind the slot will be probed if * we do it immediately even we do have PCI devices connected to the * slot. For this case, we force upper layer to wait for the PCIe * link to be up before probing the PCI devices behind the slot. It's * only concerned in surprise hotplug path. In managed hot-add path, * the PCIe link should have been ready before we power on the slot. * However, it's not harmful to do so in managed hot-add path. * * When flag PCI_SLOT_FLAG_FORCE_POWERON is set for the PCI slot, we * should turn on the slot's power supply on hardware on user's request * because that might have been lost. Otherwise, the PCIe link behind * the slot won't become ready for ever and PCI adapter behind the slot * can't be probed successfully. */ if (surprise_check && slot->surprise_pluggable) { slot->power_state = val; if (val == PCI_SLOT_POWER_OFF) return OPAL_SUCCESS; if (!pci_slot_has_flags(slot, PCI_SLOT_FLAG_FORCE_POWERON)) { pci_slot_set_state(slot, PCI_SLOT_STATE_SPOWER_DONE); return OPAL_ASYNC_COMPLETION; } } pci_slot_set_state(slot, PCI_SLOT_STATE_SPOWER_START); slot->power_state = val; ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL, &state); state &= ~(PCICAP_EXP_SLOTCTL_PWRCTLR | PCICAP_EXP_SLOTCTL_PWRI); switch (val) { case PCI_SLOT_POWER_OFF: state |= (PCICAP_EXP_SLOTCTL_PWRCTLR | (PCIE_INDIC_OFF << 8)); break; case PCI_SLOT_POWER_ON: state |= (PCIE_INDIC_ON << 8); break; default: pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL); prlog(PR_ERR, PCIE_SLOT_PREFIX "Invalid power state (0x%x)\n", slot->id, val); return OPAL_PARAMETER; } pci_cfg_write16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL, state); pci_slot_set_state(slot, PCI_SLOT_STATE_SPOWER_DONE); return OPAL_ASYNC_COMPLETION; } static int64_t pcie_slot_set_power_state(struct pci_slot *slot, uint8_t val) { return pcie_slot_set_power_state_ext(slot, val, true); } static int64_t pcie_slot_sm_poll_link(struct pci_slot *slot) { struct phb *phb = slot->phb; struct pci_device *pd = slot->pd; uint32_t ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); uint16_t val; uint8_t presence = 0; switch (slot->state) { case PCI_SLOT_STATE_LINK_START_POLL: PCIE_SLOT_DBG(slot, "LINK: Start polling\n"); /* Link is down for ever without devices attached */ if (slot->ops.get_presence_state) slot->ops.get_presence_state(slot, &presence); if (!presence) { PCIE_SLOT_DBG(slot, "LINK: No adapter, end polling\n"); pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL); return OPAL_SUCCESS; } /* Enable the link without check */ pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_LCTL, &val); val &= ~PCICAP_EXP_LCTL_LINK_DIS; pci_cfg_write16(phb, pd->bdfn, ecap + PCICAP_EXP_LCTL, val); /* * If the link change report isn't supported, we expect * the link is up and stabilized after one second. */ if (!(slot->link_cap & PCICAP_EXP_LCAP_DL_ACT_REP)) { pci_slot_set_state(slot, PCI_SLOT_STATE_LINK_DELAY_FINALIZED); return pci_slot_set_sm_timeout(slot, secs_to_tb(1)); } /* * Poll the link state if link state change report is * supported on the link. */ pci_slot_set_state(slot, PCI_SLOT_STATE_LINK_POLLING); slot->retries = 250; return pci_slot_set_sm_timeout(slot, msecs_to_tb(20)); case PCI_SLOT_STATE_LINK_DELAY_FINALIZED: PCIE_SLOT_DBG(slot, "LINK: No link report, end polling\n"); if (slot->ops.prepare_link_change) slot->ops.prepare_link_change(slot, true); pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL); return OPAL_SUCCESS; case PCI_SLOT_STATE_LINK_POLLING: pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_LSTAT, &val); if (val & PCICAP_EXP_LSTAT_DLLL_ACT) { PCIE_SLOT_DBG(slot, "LINK: Link is up, end polling\n"); if (slot->ops.prepare_link_change) slot->ops.prepare_link_change(slot, true); pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL); return OPAL_SUCCESS; } /* Check link state again until timeout */ if (slot->retries-- == 0) { prlog(PR_ERR, PCIE_SLOT_PREFIX "LINK: Timeout waiting for up (%04x)\n", slot->id, val); pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL); return OPAL_SUCCESS; } return pci_slot_set_sm_timeout(slot, msecs_to_tb(20)); default: prlog(PR_ERR, PCIE_SLOT_PREFIX "Link: Unexpected slot state %08x\n", slot->id, slot->state); } pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL); return OPAL_HARDWARE; } static void pcie_slot_reset(struct pci_slot *slot, bool assert) { struct phb *phb = slot->phb; struct pci_device *pd = slot->pd; uint16_t ctl; pci_cfg_read16(phb, pd->bdfn, PCI_CFG_BRCTL, &ctl); if (assert) ctl |= PCI_CFG_BRCTL_SECONDARY_RESET; else ctl &= ~PCI_CFG_BRCTL_SECONDARY_RESET; pci_cfg_write16(phb, pd->bdfn, PCI_CFG_BRCTL, ctl); } static int64_t pcie_slot_sm_hreset(struct pci_slot *slot) { switch (slot->state) { case PCI_SLOT_STATE_NORMAL: PCIE_SLOT_DBG(slot, "HRESET: Starts\n"); if (slot->ops.prepare_link_change) { PCIE_SLOT_DBG(slot, "HRESET: Prepare for link down\n"); slot->ops.prepare_link_change(slot, false); } /* fall through */ case PCI_SLOT_STATE_HRESET_START: PCIE_SLOT_DBG(slot, "HRESET: Assert\n"); pcie_slot_reset(slot, true); pci_slot_set_state(slot, PCI_SLOT_STATE_HRESET_HOLD); return pci_slot_set_sm_timeout(slot, msecs_to_tb(250)); case PCI_SLOT_STATE_HRESET_HOLD: PCIE_SLOT_DBG(slot, "HRESET: Deassert\n"); pcie_slot_reset(slot, false); pci_slot_set_state(slot, PCI_SLOT_STATE_LINK_START_POLL); return pci_slot_set_sm_timeout(slot, msecs_to_tb(1800)); default: PCIE_SLOT_DBG(slot, "HRESET: Unexpected slot state %08x\n", slot->state); } pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL); return OPAL_HARDWARE; } /* * Usually, individual platforms need to override the power * management methods for fundamental reset, but the hot * reset method is commonly shared. */ static int64_t pcie_slot_sm_freset(struct pci_slot *slot) { uint8_t power_state = PCI_SLOT_POWER_ON; switch (slot->state) { case PCI_SLOT_STATE_NORMAL: PCIE_SLOT_DBG(slot, "FRESET: Starts\n"); if (slot->ops.prepare_link_change) slot->ops.prepare_link_change(slot, false); /* Retrieve power state */ if (slot->ops.get_power_state) { PCIE_SLOT_DBG(slot, "FRESET: Retrieve power state\n"); slot->ops.get_power_state(slot, &power_state); } /* In power on state, power it off */ if (power_state == PCI_SLOT_POWER_ON) { PCIE_SLOT_DBG(slot, "FRESET: Power is on, turn off\n"); pcie_slot_set_power_state_ext(slot, PCI_SLOT_POWER_OFF, false); pci_slot_set_state(slot, PCI_SLOT_STATE_FRESET_POWER_OFF); return pci_slot_set_sm_timeout(slot, msecs_to_tb(50)); } /* No power state change, */ /* fallthrough */ case PCI_SLOT_STATE_FRESET_POWER_OFF: PCIE_SLOT_DBG(slot, "FRESET: Power is off, turn on\n"); pcie_slot_set_power_state_ext(slot, PCI_SLOT_POWER_ON, false); pci_slot_set_state(slot, PCI_SLOT_STATE_LINK_START_POLL); return pci_slot_set_sm_timeout(slot, msecs_to_tb(50)); default: prlog(PR_ERR, PCIE_SLOT_PREFIX "FRESET: Unexpected slot state %08x\n", slot->id, slot->state); } pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL); return OPAL_HARDWARE; } struct pci_slot *pcie_slot_create(struct phb *phb, struct pci_device *pd) { struct pci_slot *slot; uint32_t ecap; uint16_t slot_ctl; /* Allocate PCI slot */ slot = pci_slot_alloc(phb, pd); if (!slot) return NULL; /* Cache the link and slot capabilities */ ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_CAPABILITY_REG, &slot->pcie_cap); pci_cfg_read32(phb, pd->bdfn, ecap + PCICAP_EXP_LCAP, &slot->link_cap); /* Leave PCI slot capability blank if PCI slot isn't supported */ if (slot->pcie_cap & PCICAP_EXP_CAP_SLOT) pci_cfg_read32(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCAP, &slot->slot_cap); else slot->slot_cap = 0; if (slot->slot_cap & PCICAP_EXP_SLOTCAP_HPLUG_CAP) slot->pluggable = 1; if (slot->slot_cap & PCICAP_EXP_SLOTCAP_PWCTRL) { slot->power_ctl = 1; /* The power is on by default */ slot->power_state = PCI_SLOT_POWER_ON; pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL, &slot_ctl); if (((slot_ctl & PCICAP_EXP_SLOTCTL_PWRI) >> 8) == PCIE_INDIC_OFF) slot->power_state = PCI_SLOT_POWER_OFF; } if (slot->slot_cap & PCICAP_EXP_SLOTCAP_PWRI) slot->power_led_ctl = PCI_SLOT_PWR_LED_CTL_KERNEL; if (slot->slot_cap & PCICAP_EXP_SLOTCAP_ATTNI) slot->attn_led_ctl = PCI_SLOT_ATTN_LED_CTL_KERNEL; slot->wired_lanes = ((slot->link_cap & PCICAP_EXP_LCAP_MAXWDTH) >> 4); /* The surprise hotplug capability is claimed when it's supported * in the slot's capability bits or link state change reporting is * supported in PCIe link capability. It means the surprise hotplug * relies on presence or link state change events. In order for the * link state change event to be properly raised during surprise hot * add/remove, the power supply to the slot should be always on. * * For PCI slots that don't claim surprise hotplug capability explicitly. * Its PDC (Presence Detection Change) isn't reliable. To mark that as * broken on them. */ if (slot->pcie_cap & PCICAP_EXP_CAP_SLOT) { if (slot->slot_cap & PCICAP_EXP_SLOTCAP_HPLUG_SURP) { slot->surprise_pluggable = 1; } else if (slot->link_cap & PCICAP_EXP_LCAP_DL_ACT_REP) { slot->surprise_pluggable = 1; pci_slot_add_flags(slot, PCI_SLOT_FLAG_BROKEN_PDC); } } /* Standard slot operations */ slot->ops.get_presence_state = pcie_slot_get_presence_state; slot->ops.get_link_state = pcie_slot_get_link_state; slot->ops.get_power_state = pcie_slot_get_power_state; slot->ops.get_attention_state = pcie_slot_get_attention_state; slot->ops.get_latch_state = pcie_slot_get_latch_state; slot->ops.set_power_state = pcie_slot_set_power_state; slot->ops.set_attention_state = pcie_slot_set_attention_state; /* * State machine (SM) based reset stuff. The poll function is always * unified for all cases. */ slot->ops.poll_link = pcie_slot_sm_poll_link; slot->ops.hreset = pcie_slot_sm_hreset; slot->ops.freset = pcie_slot_sm_freset; slot->wired_lanes = PCI_SLOT_WIRED_LANES_UNKNOWN; slot->connector_type = PCI_SLOT_CONNECTOR_PCIE_NS; slot->card_desc = PCI_SLOT_DESC_NON_STANDARD; slot->card_mech = PCI_SLOT_MECH_NONE; slot->power_led_ctl = PCI_SLOT_PWR_LED_CTL_NONE; slot->attn_led_ctl = PCI_SLOT_ATTN_LED_CTL_NONE; return slot; } /* FIXME: this is kind of insane */ struct pci_slot *pcie_slot_create_dynamic(struct phb *phb, struct pci_device *pd) { uint32_t ecap, val; struct pci_slot *slot; if (!phb || !pd || pd->slot) return NULL; /* Try to create slot whose details aren't provided by platform. * We only care the downstream ports of PCIe switch that connects * to root port. */ if (pd->dev_type != PCIE_TYPE_SWITCH_DNPORT || !pd->parent || !pd->parent->parent || pd->parent->parent->parent) return NULL; ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); pci_cfg_read32(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCAP, &val); if (!(val & PCICAP_EXP_SLOTCAP_HPLUG_CAP)) return NULL; slot = pcie_slot_create(phb, pd); /* On superMicro's "p8dnu" platform, we create dynamic PCI slots * for all downstream ports of PEX9733 that is connected to PHB * direct slot. The power supply to the PCI slot is lost after * PCI adapter is removed from it. The power supply can't be * turned on when the slot is in empty state. The power supply * isn't turned on automatically when inserting PCI adapter to * the slot at later point. We set a flag to the slot here, to * turn on the power supply in (suprise or managed) hot-add path. * * We have same issue with PEX8718 as above on "p8dnu" platform. */ if (dt_node_is_compatible(dt_root, "supermicro,p8dnu") && slot && slot->pd && (slot->pd->vdid == 0x973310b5 || slot->pd->vdid == 0x871810b5)) pci_slot_add_flags(slot, PCI_SLOT_FLAG_FORCE_POWERON); return slot; }