18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Copyright IBM Corp. 2012
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Author(s):
68c2ecf20Sopenharmony_ci *    Jan Glauber <jang@linux.vnet.ibm.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "zpci"
108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/pci.h>
148c2ecf20Sopenharmony_ci#include <asm/pci_debug.h>
158c2ecf20Sopenharmony_ci#include <asm/sclp.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "pci_bus.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/* Content Code Description for PCI Function Error */
208c2ecf20Sopenharmony_cistruct zpci_ccdf_err {
218c2ecf20Sopenharmony_ci	u32 reserved1;
228c2ecf20Sopenharmony_ci	u32 fh;				/* function handle */
238c2ecf20Sopenharmony_ci	u32 fid;			/* function id */
248c2ecf20Sopenharmony_ci	u32 ett		:  4;		/* expected table type */
258c2ecf20Sopenharmony_ci	u32 mvn		: 12;		/* MSI vector number */
268c2ecf20Sopenharmony_ci	u32 dmaas	:  8;		/* DMA address space */
278c2ecf20Sopenharmony_ci	u32		:  6;
288c2ecf20Sopenharmony_ci	u32 q		:  1;		/* event qualifier */
298c2ecf20Sopenharmony_ci	u32 rw		:  1;		/* read/write */
308c2ecf20Sopenharmony_ci	u64 faddr;			/* failing address */
318c2ecf20Sopenharmony_ci	u32 reserved3;
328c2ecf20Sopenharmony_ci	u16 reserved4;
338c2ecf20Sopenharmony_ci	u16 pec;			/* PCI event code */
348c2ecf20Sopenharmony_ci} __packed;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/* Content Code Description for PCI Function Availability */
378c2ecf20Sopenharmony_cistruct zpci_ccdf_avail {
388c2ecf20Sopenharmony_ci	u32 reserved1;
398c2ecf20Sopenharmony_ci	u32 fh;				/* function handle */
408c2ecf20Sopenharmony_ci	u32 fid;			/* function id */
418c2ecf20Sopenharmony_ci	u32 reserved2;
428c2ecf20Sopenharmony_ci	u32 reserved3;
438c2ecf20Sopenharmony_ci	u32 reserved4;
448c2ecf20Sopenharmony_ci	u32 reserved5;
458c2ecf20Sopenharmony_ci	u16 reserved6;
468c2ecf20Sopenharmony_ci	u16 pec;			/* PCI event code */
478c2ecf20Sopenharmony_ci} __packed;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic void __zpci_event_error(struct zpci_ccdf_err *ccdf)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid);
528c2ecf20Sopenharmony_ci	struct pci_dev *pdev = NULL;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	zpci_err("error CCDF:\n");
558c2ecf20Sopenharmony_ci	zpci_err_hex(ccdf, sizeof(*ccdf));
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	if (zdev)
588c2ecf20Sopenharmony_ci		pdev = pci_get_slot(zdev->zbus->bus, zdev->devfn);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	pr_err("%s: Event 0x%x reports an error for PCI function 0x%x\n",
618c2ecf20Sopenharmony_ci	       pdev ? pci_name(pdev) : "n/a", ccdf->pec, ccdf->fid);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	if (!pdev)
648c2ecf20Sopenharmony_ci		goto no_pdev;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	pdev->error_state = pci_channel_io_perm_failure;
678c2ecf20Sopenharmony_ci	pci_dev_put(pdev);
688c2ecf20Sopenharmony_cino_pdev:
698c2ecf20Sopenharmony_ci	zpci_zdev_put(zdev);
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_civoid zpci_event_error(void *data)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	if (zpci_is_enabled())
758c2ecf20Sopenharmony_ci		__zpci_event_error(data);
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid);
818c2ecf20Sopenharmony_ci	bool existing_zdev = !!zdev;
828c2ecf20Sopenharmony_ci	enum zpci_state state;
838c2ecf20Sopenharmony_ci	struct pci_dev *pdev;
848c2ecf20Sopenharmony_ci	int ret;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	zpci_err("avail CCDF:\n");
878c2ecf20Sopenharmony_ci	zpci_err_hex(ccdf, sizeof(*ccdf));
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	switch (ccdf->pec) {
908c2ecf20Sopenharmony_ci	case 0x0301: /* Reserved|Standby -> Configured */
918c2ecf20Sopenharmony_ci		if (!zdev) {
928c2ecf20Sopenharmony_ci			zpci_create_device(ccdf->fid, ccdf->fh, ZPCI_FN_STATE_CONFIGURED);
938c2ecf20Sopenharmony_ci			break;
948c2ecf20Sopenharmony_ci		}
958c2ecf20Sopenharmony_ci		/* the configuration request may be stale */
968c2ecf20Sopenharmony_ci		if (zdev->state != ZPCI_FN_STATE_STANDBY)
978c2ecf20Sopenharmony_ci			break;
988c2ecf20Sopenharmony_ci		zdev->fh = ccdf->fh;
998c2ecf20Sopenharmony_ci		zdev->state = ZPCI_FN_STATE_CONFIGURED;
1008c2ecf20Sopenharmony_ci		ret = zpci_enable_device(zdev);
1018c2ecf20Sopenharmony_ci		if (ret)
1028c2ecf20Sopenharmony_ci			break;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci		/* the PCI function will be scanned once function 0 appears */
1058c2ecf20Sopenharmony_ci		if (!zdev->zbus->bus)
1068c2ecf20Sopenharmony_ci			break;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci		pdev = pci_scan_single_device(zdev->zbus->bus, zdev->devfn);
1098c2ecf20Sopenharmony_ci		if (!pdev)
1108c2ecf20Sopenharmony_ci			break;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci		pci_bus_add_device(pdev);
1138c2ecf20Sopenharmony_ci		pci_lock_rescan_remove();
1148c2ecf20Sopenharmony_ci		pci_bus_add_devices(zdev->zbus->bus);
1158c2ecf20Sopenharmony_ci		pci_unlock_rescan_remove();
1168c2ecf20Sopenharmony_ci		break;
1178c2ecf20Sopenharmony_ci	case 0x0302: /* Reserved -> Standby */
1188c2ecf20Sopenharmony_ci		if (!zdev) {
1198c2ecf20Sopenharmony_ci			zpci_create_device(ccdf->fid, ccdf->fh, ZPCI_FN_STATE_STANDBY);
1208c2ecf20Sopenharmony_ci			break;
1218c2ecf20Sopenharmony_ci		}
1228c2ecf20Sopenharmony_ci		zdev->fh = ccdf->fh;
1238c2ecf20Sopenharmony_ci		break;
1248c2ecf20Sopenharmony_ci	case 0x0303: /* Deconfiguration requested */
1258c2ecf20Sopenharmony_ci		if (!zdev)
1268c2ecf20Sopenharmony_ci			break;
1278c2ecf20Sopenharmony_ci		zpci_remove_device(zdev, false);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci		ret = zpci_disable_device(zdev);
1308c2ecf20Sopenharmony_ci		if (ret)
1318c2ecf20Sopenharmony_ci			break;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci		ret = sclp_pci_deconfigure(zdev->fid);
1348c2ecf20Sopenharmony_ci		zpci_dbg(3, "deconf fid:%x, rc:%d\n", zdev->fid, ret);
1358c2ecf20Sopenharmony_ci		if (!ret)
1368c2ecf20Sopenharmony_ci			zdev->state = ZPCI_FN_STATE_STANDBY;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci		break;
1398c2ecf20Sopenharmony_ci	case 0x0304: /* Configured -> Standby|Reserved */
1408c2ecf20Sopenharmony_ci		if (!zdev)
1418c2ecf20Sopenharmony_ci			break;
1428c2ecf20Sopenharmony_ci		/* Give the driver a hint that the function is
1438c2ecf20Sopenharmony_ci		 * already unusable.
1448c2ecf20Sopenharmony_ci		 */
1458c2ecf20Sopenharmony_ci		zpci_remove_device(zdev, true);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci		zdev->fh = ccdf->fh;
1488c2ecf20Sopenharmony_ci		zpci_disable_device(zdev);
1498c2ecf20Sopenharmony_ci		zdev->state = ZPCI_FN_STATE_STANDBY;
1508c2ecf20Sopenharmony_ci		if (!clp_get_state(ccdf->fid, &state) &&
1518c2ecf20Sopenharmony_ci		    state == ZPCI_FN_STATE_RESERVED) {
1528c2ecf20Sopenharmony_ci			zpci_device_reserved(zdev);
1538c2ecf20Sopenharmony_ci		}
1548c2ecf20Sopenharmony_ci		break;
1558c2ecf20Sopenharmony_ci	case 0x0306: /* 0x308 or 0x302 for multiple devices */
1568c2ecf20Sopenharmony_ci		zpci_remove_reserved_devices();
1578c2ecf20Sopenharmony_ci		clp_scan_pci_devices();
1588c2ecf20Sopenharmony_ci		break;
1598c2ecf20Sopenharmony_ci	case 0x0308: /* Standby -> Reserved */
1608c2ecf20Sopenharmony_ci		if (!zdev)
1618c2ecf20Sopenharmony_ci			break;
1628c2ecf20Sopenharmony_ci		zpci_device_reserved(zdev);
1638c2ecf20Sopenharmony_ci		break;
1648c2ecf20Sopenharmony_ci	default:
1658c2ecf20Sopenharmony_ci		break;
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci	if (existing_zdev)
1688c2ecf20Sopenharmony_ci		zpci_zdev_put(zdev);
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_civoid zpci_event_availability(void *data)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	if (zpci_is_enabled())
1748c2ecf20Sopenharmony_ci		__zpci_event_availability(data);
1758c2ecf20Sopenharmony_ci}
176