aboutsummaryrefslogtreecommitdiff
path: root/core/pci-opal.c
diff options
context:
space:
mode:
Diffstat (limited to 'core/pci-opal.c')
-rw-r--r--core/pci-opal.c235
1 files changed, 209 insertions, 26 deletions
diff --git a/core/pci-opal.c b/core/pci-opal.c
index 40eda39..c0f399c 100644
--- a/core/pci-opal.c
+++ b/core/pci-opal.c
@@ -18,7 +18,10 @@
#include <opal-api.h>
#include <pci.h>
#include <pci-cfg.h>
+#include <pci-slot.h>
+#include <opal-msg.h>
#include <timebase.h>
+#include <timer.h>
#define OPAL_PCICFG_ACCESS(op, cb, type) \
static int64_t opal_pci_config_##op(uint64_t phb_id, \
@@ -461,16 +464,15 @@ static int64_t opal_pci_map_pe_dma_window_real(uint64_t phb_id,
}
opal_call(OPAL_PCI_MAP_PE_DMA_WINDOW_REAL, opal_pci_map_pe_dma_window_real, 5);
-static int64_t opal_pci_reset(uint64_t phb_id, uint8_t reset_scope,
+static int64_t opal_pci_reset(uint64_t id, uint8_t reset_scope,
uint8_t assert_state)
{
- struct phb *phb = pci_get_phb(phb_id);
+ struct pci_slot *slot = pci_slot_find(id);
+ struct phb *phb = slot ? slot->phb : NULL;
int64_t rc = OPAL_SUCCESS;
- if (!phb)
+ if (!slot || !phb)
return OPAL_PARAMETER;
- if (!phb->ops)
- return OPAL_UNSUPPORTED;
if (assert_state != OPAL_ASSERT_RESET &&
assert_state != OPAL_DEASSERT_RESET)
return OPAL_PARAMETER;
@@ -479,18 +481,22 @@ static int64_t opal_pci_reset(uint64_t phb_id, uint8_t reset_scope,
switch(reset_scope) {
case OPAL_RESET_PHB_COMPLETE:
- if (!phb->ops->complete_reset) {
+ /* Complete reset is applicable to PHB slot only */
+ if (!slot->ops.creset || slot->pd) {
rc = OPAL_UNSUPPORTED;
break;
}
- rc = phb->ops->complete_reset(phb, assert_state);
+ if (assert_state != OPAL_ASSERT_RESET)
+ break;
+
+ rc = slot->ops.creset(slot);
if (rc < 0)
- prerror("PHB#%d: Failure on complete reset, rc=%lld\n",
- phb->opal_id, rc);
+ prlog(PR_ERR, "SLOT-%016llx: Error %lld on complete reset\n",
+ slot->id, rc);
break;
case OPAL_RESET_PCI_FUNDAMENTAL:
- if (!phb->ops->fundamental_reset) {
+ if (!slot->ops.freset) {
rc = OPAL_UNSUPPORTED;
break;
}
@@ -499,13 +505,13 @@ static int64_t opal_pci_reset(uint64_t phb_id, uint8_t reset_scope,
if (assert_state != OPAL_ASSERT_RESET)
break;
- rc = phb->ops->fundamental_reset(phb);
+ rc = slot->ops.freset(slot);
if (rc < 0)
- prerror("PHB#%d: Failure on fundamental reset, rc=%lld\n",
- phb->opal_id, rc);
+ prlog(PR_ERR, "SLOT-%016llx: Error %lld on fundamental reset\n",
+ slot->id, rc);
break;
case OPAL_RESET_PCI_HOT:
- if (!phb->ops->hot_reset) {
+ if (!slot->ops.hreset) {
rc = OPAL_UNSUPPORTED;
break;
}
@@ -514,22 +520,34 @@ static int64_t opal_pci_reset(uint64_t phb_id, uint8_t reset_scope,
if (assert_state != OPAL_ASSERT_RESET)
break;
- rc = phb->ops->hot_reset(phb);
+ rc = slot->ops.hreset(slot);
if (rc < 0)
- prerror("PHB#%d: Failure on hot reset, rc=%lld\n",
- phb->opal_id, rc);
+ prlog(PR_ERR, "SLOT-%016llx: Error %lld on hot reset\n",
+ slot->id, rc);
break;
case OPAL_RESET_PCI_IODA_TABLE:
+ /* It's allowed on PHB slot only */
+ if (slot->pd || !phb->ops || !phb->ops->ioda_reset) {
+ rc = OPAL_UNSUPPORTED;
+ break;
+ }
+
if (assert_state != OPAL_ASSERT_RESET)
break;
- if (phb->ops->ioda_reset)
- phb->ops->ioda_reset(phb, true);
+
+ rc = phb->ops->ioda_reset(phb, true);
break;
case OPAL_RESET_PHB_ERROR:
+ /* It's allowed on PHB slot only */
+ if (slot->pd || !phb->ops || !phb->ops->papr_errinjct_reset) {
+ rc = OPAL_UNSUPPORTED;
+ break;
+ }
+
if (assert_state != OPAL_ASSERT_RESET)
break;
- if (phb->ops->papr_errinjct_reset)
- phb->ops->papr_errinjct_reset(phb);
+
+ rc = phb->ops->papr_errinjct_reset(phb);
break;
default:
rc = OPAL_UNSUPPORTED;
@@ -560,18 +578,19 @@ static int64_t opal_pci_reinit(uint64_t phb_id,
}
opal_call(OPAL_PCI_REINIT, opal_pci_reinit, 3);
-static int64_t opal_pci_poll(uint64_t phb_id)
+static int64_t opal_pci_poll(uint64_t id)
{
- struct phb *phb = pci_get_phb(phb_id);
+ struct pci_slot *slot = pci_slot_find(id);
+ struct phb *phb = slot ? slot->phb : NULL;
int64_t rc;
- if (!phb)
+ if (!slot || !phb)
return OPAL_PARAMETER;
- if (!phb->ops || !phb->ops->poll)
+ if (!slot->ops.poll)
return OPAL_UNSUPPORTED;
phb_lock(phb);
- rc = phb->ops->poll(phb);
+ rc = slot->ops.poll(slot);
phb_unlock(phb);
/* Return milliseconds for caller to sleep: round up */
@@ -585,6 +604,170 @@ static int64_t opal_pci_poll(uint64_t phb_id)
}
opal_call(OPAL_PCI_POLL, opal_pci_poll, 1);
+static int64_t opal_pci_get_presence_state(uint64_t id, uint64_t data)
+{
+ struct pci_slot *slot = pci_slot_find(id);
+ struct phb *phb = slot ? slot->phb : NULL;
+ uint8_t *presence = (uint8_t *)data;
+ int64_t rc;
+
+ if (!slot || !phb)
+ return OPAL_PARAMETER;
+ if (!slot->ops.get_presence_state)
+ return OPAL_UNSUPPORTED;
+
+ phb_lock(phb);
+ rc = slot->ops.get_presence_state(slot, presence);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PCI_GET_PRESENCE_STATE, opal_pci_get_presence_state, 2);
+
+static int64_t opal_pci_get_power_state(uint64_t id, uint64_t data)
+{
+ struct pci_slot *slot = pci_slot_find(id);
+ struct phb *phb = slot ? slot->phb : NULL;
+ uint8_t *power_state = (uint8_t *)data;
+ int64_t rc;
+
+ if (!slot || !phb)
+ return OPAL_PARAMETER;
+ if (!slot->ops.get_power_state)
+ return OPAL_UNSUPPORTED;
+
+ phb_lock(phb);
+ rc = slot->ops.get_power_state(slot, power_state);
+ phb_unlock(phb);
+
+ return rc;
+}
+opal_call(OPAL_PCI_GET_POWER_STATE, opal_pci_get_power_state, 2);
+
+static void set_power_timer(struct timer *t __unused, void *data,
+ uint64_t now __unused)
+{
+ struct pci_slot *slot = data;
+ struct phb *phb = slot->phb;
+ struct pci_device *pd = slot->pd;
+ struct dt_node *dn = pd->dn;
+ uint8_t link;
+
+ switch (slot->state) {
+ case PCI_SLOT_STATE_SPOWER_START:
+ if (slot->retries-- == 0) {
+ pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
+ opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL,
+ slot->async_token, dn->phandle,
+ slot->power_state, OPAL_BUSY);
+ } else {
+ schedule_timer(&slot->timer, msecs_to_tb(10));
+ }
+
+ break;
+ case PCI_SLOT_STATE_SPOWER_DONE:
+ if (slot->power_state == OPAL_PCI_SLOT_POWER_OFF) {
+ pci_remove_bus(phb, &pd->children);
+ pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
+ opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL,
+ slot->async_token, dn->phandle,
+ OPAL_PCI_SLOT_POWER_OFF, OPAL_SUCCESS);
+ break;
+ }
+
+ /* Power on */
+ if (slot->ops.get_link_state(slot, &link) != OPAL_SUCCESS)
+ link = 0;
+ if (link) {
+ slot->ops.prepare_link_change(slot, true);
+ pci_scan_bus(phb, pd->secondary_bus,
+ pd->subordinate_bus,
+ &pd->children, pd, true);
+ pci_add_device_nodes(phb, &pd->children, dn,
+ &phb->lstate, 0);
+ pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
+ opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL,
+ slot->async_token, dn->phandle,
+ OPAL_PCI_SLOT_POWER_ON, OPAL_SUCCESS);
+ } else if (slot->retries-- == 0) {
+ pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL);
+ opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL,
+ slot->async_token, dn->phandle,
+ OPAL_PCI_SLOT_POWER_ON, OPAL_BUSY);
+ } else {
+ schedule_timer(&slot->timer, msecs_to_tb(10));
+ }
+
+ break;
+ default:
+ prlog(PR_ERR, "PCI SLOT %016llx: Unexpected state 0x%08x\n",
+ slot->id, slot->state);
+ }
+}
+
+static int64_t opal_pci_set_power_state(uint64_t async_token,
+ uint64_t id,
+ uint64_t data)
+{
+ struct pci_slot *slot = pci_slot_find(id);
+ struct phb *phb = slot ? slot->phb : NULL;
+ struct pci_device *pd = slot ? slot->pd : NULL;
+ uint8_t *state = (uint8_t *)data;
+ int64_t rc;
+
+ if (!slot || !phb)
+ return OPAL_PARAMETER;
+
+ phb_lock(phb);
+ switch (*state) {
+ case OPAL_PCI_SLOT_POWER_OFF:
+ if (!slot->ops.prepare_link_change ||
+ !slot->ops.set_power_state)
+ return OPAL_UNSUPPORTED;
+
+ slot->async_token = async_token;
+ slot->ops.prepare_link_change(slot, false);
+ rc = slot->ops.set_power_state(slot, PCI_SLOT_POWER_OFF);
+ break;
+ case OPAL_PCI_SLOT_POWER_ON:
+ if (!slot->ops.set_power_state ||
+ !slot->ops.get_link_state)
+ return OPAL_UNSUPPORTED;
+
+ slot->async_token = async_token;
+ rc = slot->ops.set_power_state(slot, PCI_SLOT_POWER_ON);
+ break;
+ case OPAL_PCI_SLOT_OFFLINE:
+ if (!pd)
+ return OPAL_PARAMETER;
+
+ pci_remove_bus(phb, &pd->children);
+ rc = OPAL_SUCCESS;
+ break;
+ case OPAL_PCI_SLOT_ONLINE:
+ if (!pd)
+ return OPAL_PARAMETER;
+ pci_scan_bus(phb, pd->secondary_bus, pd->subordinate_bus,
+ &pd->children, pd, true);
+ pci_add_device_nodes(phb, &pd->children, pd->dn,
+ &phb->lstate, 0);
+ rc = OPAL_SUCCESS;
+ break;
+ default:
+ rc = OPAL_PARAMETER;
+ }
+
+ phb_unlock(phb);
+ if (rc == OPAL_ASYNC_COMPLETION) {
+ slot->retries = 500;
+ init_timer(&slot->timer, set_power_timer, slot);
+ schedule_timer(&slot->timer, msecs_to_tb(10));
+ }
+
+ return rc;
+}
+opal_call(OPAL_PCI_SET_POWER_STATE, opal_pci_set_power_state, 3);
+
static int64_t opal_pci_set_phb_tce_memory(uint64_t phb_id,
uint64_t tce_mem_addr,
uint64_t tce_mem_size)