in rio.c [1170:1332]
int rio_inb_pwrite_handler(struct rio_mport *mport, union rio_pw_msg *pw_msg)
{
struct rio_dev *rdev;
u32 err_status, em_perrdet, em_ltlerrdet;
int rc, portnum;
struct rio_pwrite *pwrite;
#ifdef DEBUG_PW
{
u32 i;
pr_debug("%s: PW to mport_%d:\n", __func__, mport->id);
for (i = 0; i < RIO_PW_MSG_SIZE / sizeof(u32); i = i + 4) {
pr_debug("0x%02x: %08x %08x %08x %08x\n",
i * 4, pw_msg->raw[i], pw_msg->raw[i + 1],
pw_msg->raw[i + 2], pw_msg->raw[i + 3]);
}
}
#endif
rdev = rio_get_comptag((pw_msg->em.comptag & RIO_CTAG_UDEVID), NULL);
if (rdev) {
pr_debug("RIO: Port-Write message from %s\n", rio_name(rdev));
} else {
pr_debug("RIO: %s No matching device for CTag 0x%08x\n",
__func__, pw_msg->em.comptag);
}
/* Call a device-specific handler (if it is registered for the device).
* This may be the service for endpoints that send device-specific
* port-write messages. End-point messages expected to be handled
* completely by EP specific device driver.
* For switches rc==0 signals that no standard processing required.
*/
if (rdev && rdev->pwcback) {
rc = rdev->pwcback(rdev, pw_msg, 0);
if (rc == 0)
return 0;
}
mutex_lock(&mport->lock);
list_for_each_entry(pwrite, &mport->pwrites, node)
pwrite->pwcback(mport, pwrite->context, pw_msg, 0);
mutex_unlock(&mport->lock);
if (!rdev)
return 0;
/*
* FIXME: The code below stays as it was before for now until we decide
* how to do default PW handling in combination with per-mport callbacks
*/
portnum = pw_msg->em.is_port & 0xFF;
/* Check if device and route to it are functional:
* Sometimes devices may send PW message(s) just before being
* powered down (or link being lost).
*/
if (rio_chk_dev_access(rdev)) {
pr_debug("RIO: device access failed - get link partner\n");
/* Scan route to the device and identify failed link.
* This will replace device and port reported in PW message.
* PW message should not be used after this point.
*/
if (rio_chk_dev_route(rdev, &rdev, &portnum)) {
pr_err("RIO: Route trace for %s failed\n",
rio_name(rdev));
return -EIO;
}
pw_msg = NULL;
}
/* For End-point devices processing stops here */
if (!(rdev->pef & RIO_PEF_SWITCH))
return 0;
if (rdev->phys_efptr == 0) {
pr_err("RIO_PW: Bad switch initialization for %s\n",
rio_name(rdev));
return 0;
}
/*
* Process the port-write notification from switch
*/
if (rdev->rswitch->ops && rdev->rswitch->ops->em_handle)
rdev->rswitch->ops->em_handle(rdev, portnum);
rio_read_config_32(rdev, RIO_DEV_PORT_N_ERR_STS_CSR(rdev, portnum),
&err_status);
pr_debug("RIO_PW: SP%d_ERR_STS_CSR=0x%08x\n", portnum, err_status);
if (err_status & RIO_PORT_N_ERR_STS_PORT_OK) {
if (!(rdev->rswitch->port_ok & (1 << portnum))) {
rdev->rswitch->port_ok |= (1 << portnum);
rio_set_port_lockout(rdev, portnum, 0);
/* Schedule Insertion Service */
pr_debug("RIO_PW: Device Insertion on [%s]-P%d\n",
rio_name(rdev), portnum);
}
/* Clear error-stopped states (if reported).
* Depending on the link partner state, two attempts
* may be needed for successful recovery.
*/
if (err_status & (RIO_PORT_N_ERR_STS_OUT_ES |
RIO_PORT_N_ERR_STS_INP_ES)) {
if (rio_clr_err_stopped(rdev, portnum, err_status))
rio_clr_err_stopped(rdev, portnum, 0);
}
} else { /* if (err_status & RIO_PORT_N_ERR_STS_PORT_UNINIT) */
if (rdev->rswitch->port_ok & (1 << portnum)) {
rdev->rswitch->port_ok &= ~(1 << portnum);
rio_set_port_lockout(rdev, portnum, 1);
if (rdev->phys_rmap == 1) {
rio_write_config_32(rdev,
RIO_DEV_PORT_N_ACK_STS_CSR(rdev, portnum),
RIO_PORT_N_ACK_CLEAR);
} else {
rio_write_config_32(rdev,
RIO_DEV_PORT_N_OB_ACK_CSR(rdev, portnum),
RIO_PORT_N_OB_ACK_CLEAR);
rio_write_config_32(rdev,
RIO_DEV_PORT_N_IB_ACK_CSR(rdev, portnum),
0);
}
/* Schedule Extraction Service */
pr_debug("RIO_PW: Device Extraction on [%s]-P%d\n",
rio_name(rdev), portnum);
}
}
rio_read_config_32(rdev,
rdev->em_efptr + RIO_EM_PN_ERR_DETECT(portnum), &em_perrdet);
if (em_perrdet) {
pr_debug("RIO_PW: RIO_EM_P%d_ERR_DETECT=0x%08x\n",
portnum, em_perrdet);
/* Clear EM Port N Error Detect CSR */
rio_write_config_32(rdev,
rdev->em_efptr + RIO_EM_PN_ERR_DETECT(portnum), 0);
}
rio_read_config_32(rdev,
rdev->em_efptr + RIO_EM_LTL_ERR_DETECT, &em_ltlerrdet);
if (em_ltlerrdet) {
pr_debug("RIO_PW: RIO_EM_LTL_ERR_DETECT=0x%08x\n",
em_ltlerrdet);
/* Clear EM L/T Layer Error Detect CSR */
rio_write_config_32(rdev,
rdev->em_efptr + RIO_EM_LTL_ERR_DETECT, 0);
}
/* Clear remaining error bits and Port-Write Pending bit */
rio_write_config_32(rdev, RIO_DEV_PORT_N_ERR_STS_CSR(rdev, portnum),
err_status);
return 0;
}