diff options
Diffstat (limited to 'arch/s390/pci/pci_event.c')
| -rw-r--r-- | arch/s390/pci/pci_event.c | 99 | 
1 files changed, 70 insertions, 29 deletions
diff --git a/arch/s390/pci/pci_event.c b/arch/s390/pci/pci_event.c index 0aecaf95484..6d7f5a3016c 100644 --- a/arch/s390/pci/pci_event.c +++ b/arch/s390/pci/pci_event.c @@ -10,6 +10,8 @@  #include <linux/kernel.h>  #include <linux/pci.h> +#include <asm/pci_debug.h> +#include <asm/sclp.h>  /* Content Code Description for PCI Function Error */  struct zpci_ccdf_err { @@ -41,55 +43,94 @@ struct zpci_ccdf_avail {  	u16 pec;			/* PCI event code */  } __packed; -static void zpci_event_log_err(struct zpci_ccdf_err *ccdf) +static void __zpci_event_error(struct zpci_ccdf_err *ccdf)  {  	struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid); -	zpci_err("SEI error CCD:\n"); +	zpci_err("error CCDF:\n");  	zpci_err_hex(ccdf, sizeof(*ccdf)); -	dev_err(&zdev->pdev->dev, "event code: 0x%x\n", ccdf->pec); + +	if (!zdev) +		return; + +	pr_err("%s: Event 0x%x reports an error for PCI function 0x%x\n", +	       pci_name(zdev->pdev), ccdf->pec, ccdf->fid);  } -static void zpci_event_log_avail(struct zpci_ccdf_avail *ccdf) +void zpci_event_error(void *data) +{ +	if (zpci_is_enabled()) +		__zpci_event_error(data); +} + +static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)  {  	struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid); +	struct pci_dev *pdev = zdev ? zdev->pdev : NULL; +	int ret; -	pr_err("%s%s: availability event: fh: 0x%x  fid: 0x%x  event code: 0x%x  reason:", -		(zdev) ? dev_driver_string(&zdev->pdev->dev) : "?", -		(zdev) ? dev_name(&zdev->pdev->dev) : "?", -		ccdf->fh, ccdf->fid, ccdf->pec); -	print_hex_dump(KERN_CONT, "ccdf", DUMP_PREFIX_OFFSET, -		       16, 1, ccdf, sizeof(*ccdf), false); +	pr_info("%s: Event 0x%x reconfigured PCI function 0x%x\n", +		pdev ? pci_name(pdev) : "n/a", ccdf->pec, ccdf->fid); +	zpci_err("avail CCDF:\n"); +	zpci_err_hex(ccdf, sizeof(*ccdf));  	switch (ccdf->pec) { -	case 0x0301: -		zpci_enable_device(zdev); +	case 0x0301: /* Standby -> Configured */ +		if (!zdev || zdev->state != ZPCI_FN_STATE_STANDBY) +			break; +		zdev->state = ZPCI_FN_STATE_CONFIGURED; +		zdev->fh = ccdf->fh; +		ret = zpci_enable_device(zdev); +		if (ret) +			break; +		pci_rescan_bus(zdev->bus); +		break; +	case 0x0302: /* Reserved -> Standby */ +		if (!zdev) +			clp_add_pci_device(ccdf->fid, ccdf->fh, 0); +		break; +	case 0x0303: /* Deconfiguration requested */ +		if (pdev) +			pci_stop_and_remove_bus_device(pdev); + +		ret = zpci_disable_device(zdev); +		if (ret) +			break; + +		ret = sclp_pci_deconfigure(zdev->fid); +		zpci_dbg(3, "deconf fid:%x, rc:%d\n", zdev->fid, ret); +		if (!ret) +			zdev->state = ZPCI_FN_STATE_STANDBY; +  		break; -	case 0x0302: -		clp_add_pci_device(ccdf->fid, ccdf->fh, 0); +	case 0x0304: /* Configured -> Standby */ +		if (pdev) { +			/* Give the driver a hint that the function is +			 * already unusable. */ +			pdev->error_state = pci_channel_io_perm_failure; +			pci_stop_and_remove_bus_device(pdev); +		} + +		zdev->fh = ccdf->fh; +		zpci_disable_device(zdev); +		zdev->state = ZPCI_FN_STATE_STANDBY;  		break; -	case 0x0306: +	case 0x0306: /* 0x308 or 0x302 for multiple devices */  		clp_rescan_pci_devices();  		break; +	case 0x0308: /* Standby -> Reserved */ +		if (!zdev) +			break; +		pci_stop_root_bus(zdev->bus); +		pci_remove_root_bus(zdev->bus); +		break;  	default:  		break;  	}  } -void zpci_event_error(void *data) -{ -	struct zpci_ccdf_err *ccdf = data; -	struct zpci_dev *zdev; - -	zpci_event_log_err(ccdf); -	zdev = get_zdev_by_fid(ccdf->fid); -	if (!zdev) { -		pr_err("Error event for unknown fid: %x", ccdf->fid); -		return; -	} -} -  void zpci_event_availability(void *data)  { -	zpci_event_log_avail(data); +	if (zpci_is_enabled()) +		__zpci_event_availability(data);  }  | 
