123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Driver for handling the PCIe controller errors on
- * HiSilicon HIP SoCs.
- *
- * Copyright (c) 2020 HiSilicon Limited.
- */
- #include <linux/acpi.h>
- #include <acpi/ghes.h>
- #include <linux/bitops.h>
- #include <linux/delay.h>
- #include <linux/pci.h>
- #include <linux/platform_device.h>
- #include <linux/kfifo.h>
- #include <linux/spinlock.h>
- /* HISI PCIe controller error definitions */
- #define HISI_PCIE_ERR_MISC_REGS 33
- #define HISI_PCIE_LOCAL_VALID_VERSION BIT(0)
- #define HISI_PCIE_LOCAL_VALID_SOC_ID BIT(1)
- #define HISI_PCIE_LOCAL_VALID_SOCKET_ID BIT(2)
- #define HISI_PCIE_LOCAL_VALID_NIMBUS_ID BIT(3)
- #define HISI_PCIE_LOCAL_VALID_SUB_MODULE_ID BIT(4)
- #define HISI_PCIE_LOCAL_VALID_CORE_ID BIT(5)
- #define HISI_PCIE_LOCAL_VALID_PORT_ID BIT(6)
- #define HISI_PCIE_LOCAL_VALID_ERR_TYPE BIT(7)
- #define HISI_PCIE_LOCAL_VALID_ERR_SEVERITY BIT(8)
- #define HISI_PCIE_LOCAL_VALID_ERR_MISC 9
- static guid_t hisi_pcie_sec_guid =
- GUID_INIT(0xB2889FC9, 0xE7D7, 0x4F9D,
- 0xA8, 0x67, 0xAF, 0x42, 0xE9, 0x8B, 0xE7, 0x72);
- /*
- * Firmware reports the socket port ID where the error occurred. These
- * macros convert that to the core ID and core port ID required by the
- * ACPI reset method.
- */
- #define HISI_PCIE_PORT_ID(core, v) (((v) >> 1) + ((core) << 3))
- #define HISI_PCIE_CORE_ID(v) ((v) >> 3)
- #define HISI_PCIE_CORE_PORT_ID(v) (((v) & 7) << 1)
- struct hisi_pcie_error_data {
- u64 val_bits;
- u8 version;
- u8 soc_id;
- u8 socket_id;
- u8 nimbus_id;
- u8 sub_module_id;
- u8 core_id;
- u8 port_id;
- u8 err_severity;
- u16 err_type;
- u8 reserv[2];
- u32 err_misc[HISI_PCIE_ERR_MISC_REGS];
- };
- struct hisi_pcie_error_private {
- struct notifier_block nb;
- struct device *dev;
- };
- enum hisi_pcie_submodule_id {
- HISI_PCIE_SUB_MODULE_ID_AP,
- HISI_PCIE_SUB_MODULE_ID_TL,
- HISI_PCIE_SUB_MODULE_ID_MAC,
- HISI_PCIE_SUB_MODULE_ID_DL,
- HISI_PCIE_SUB_MODULE_ID_SDI,
- };
- static const char * const hisi_pcie_sub_module[] = {
- [HISI_PCIE_SUB_MODULE_ID_AP] = "AP Layer",
- [HISI_PCIE_SUB_MODULE_ID_TL] = "TL Layer",
- [HISI_PCIE_SUB_MODULE_ID_MAC] = "MAC Layer",
- [HISI_PCIE_SUB_MODULE_ID_DL] = "DL Layer",
- [HISI_PCIE_SUB_MODULE_ID_SDI] = "SDI Layer",
- };
- enum hisi_pcie_err_severity {
- HISI_PCIE_ERR_SEV_RECOVERABLE,
- HISI_PCIE_ERR_SEV_FATAL,
- HISI_PCIE_ERR_SEV_CORRECTED,
- HISI_PCIE_ERR_SEV_NONE,
- };
- static const char * const hisi_pcie_error_sev[] = {
- [HISI_PCIE_ERR_SEV_RECOVERABLE] = "recoverable",
- [HISI_PCIE_ERR_SEV_FATAL] = "fatal",
- [HISI_PCIE_ERR_SEV_CORRECTED] = "corrected",
- [HISI_PCIE_ERR_SEV_NONE] = "none",
- };
- static const char *hisi_pcie_get_string(const char * const *array,
- size_t n, u32 id)
- {
- u32 index;
- for (index = 0; index < n; index++) {
- if (index == id && array[index])
- return array[index];
- }
- return "unknown";
- }
- static int hisi_pcie_port_reset(struct platform_device *pdev,
- u32 chip_id, u32 port_id)
- {
- struct device *dev = &pdev->dev;
- acpi_handle handle = ACPI_HANDLE(dev);
- union acpi_object arg[3];
- struct acpi_object_list arg_list;
- acpi_status s;
- unsigned long long data = 0;
- arg[0].type = ACPI_TYPE_INTEGER;
- arg[0].integer.value = chip_id;
- arg[1].type = ACPI_TYPE_INTEGER;
- arg[1].integer.value = HISI_PCIE_CORE_ID(port_id);
- arg[2].type = ACPI_TYPE_INTEGER;
- arg[2].integer.value = HISI_PCIE_CORE_PORT_ID(port_id);
- arg_list.count = 3;
- arg_list.pointer = arg;
- s = acpi_evaluate_integer(handle, "RST", &arg_list, &data);
- if (ACPI_FAILURE(s)) {
- dev_err(dev, "No RST method\n");
- return -EIO;
- }
- if (data) {
- dev_err(dev, "Failed to Reset\n");
- return -EIO;
- }
- return 0;
- }
- static int hisi_pcie_port_do_recovery(struct platform_device *dev,
- u32 chip_id, u32 port_id)
- {
- acpi_status s;
- struct device *device = &dev->dev;
- acpi_handle root_handle = ACPI_HANDLE(device);
- struct acpi_pci_root *pci_root;
- struct pci_bus *root_bus;
- struct pci_dev *pdev;
- u32 domain, busnr, devfn;
- s = acpi_get_parent(root_handle, &root_handle);
- if (ACPI_FAILURE(s))
- return -ENODEV;
- pci_root = acpi_pci_find_root(root_handle);
- if (!pci_root)
- return -ENODEV;
- root_bus = pci_root->bus;
- domain = pci_root->segment;
- busnr = root_bus->number;
- devfn = PCI_DEVFN(port_id, 0);
- pdev = pci_get_domain_bus_and_slot(domain, busnr, devfn);
- if (!pdev) {
- dev_info(device, "Fail to get root port %04x:%02x:%02x.%d device\n",
- domain, busnr, PCI_SLOT(devfn), PCI_FUNC(devfn));
- return -ENODEV;
- }
- pci_stop_and_remove_bus_device_locked(pdev);
- pci_dev_put(pdev);
- if (hisi_pcie_port_reset(dev, chip_id, port_id))
- return -EIO;
- /*
- * The initialization time of subordinate devices after
- * hot reset is no more than 1s, which is required by
- * the PCI spec v5.0 sec 6.6.1. The time will shorten
- * if Readiness Notifications mechanisms are used. But
- * wait 1s here to adapt any conditions.
- */
- ssleep(1UL);
- /* add root port and downstream devices */
- pci_lock_rescan_remove();
- pci_rescan_bus(root_bus);
- pci_unlock_rescan_remove();
- return 0;
- }
- static void hisi_pcie_handle_error(struct platform_device *pdev,
- const struct hisi_pcie_error_data *edata)
- {
- struct device *dev = &pdev->dev;
- int idx, rc;
- const unsigned long valid_bits[] = {BITMAP_FROM_U64(edata->val_bits)};
- if (edata->val_bits == 0) {
- dev_warn(dev, "%s: no valid error information\n", __func__);
- return;
- }
- dev_info(dev, "\nHISI : HIP : PCIe controller error\n");
- if (edata->val_bits & HISI_PCIE_LOCAL_VALID_SOC_ID)
- dev_info(dev, "Table version = %d\n", edata->version);
- if (edata->val_bits & HISI_PCIE_LOCAL_VALID_SOCKET_ID)
- dev_info(dev, "Socket ID = %d\n", edata->socket_id);
- if (edata->val_bits & HISI_PCIE_LOCAL_VALID_NIMBUS_ID)
- dev_info(dev, "Nimbus ID = %d\n", edata->nimbus_id);
- if (edata->val_bits & HISI_PCIE_LOCAL_VALID_SUB_MODULE_ID)
- dev_info(dev, "Sub Module = %s\n",
- hisi_pcie_get_string(hisi_pcie_sub_module,
- ARRAY_SIZE(hisi_pcie_sub_module),
- edata->sub_module_id));
- if (edata->val_bits & HISI_PCIE_LOCAL_VALID_CORE_ID)
- dev_info(dev, "Core ID = core%d\n", edata->core_id);
- if (edata->val_bits & HISI_PCIE_LOCAL_VALID_PORT_ID)
- dev_info(dev, "Port ID = port%d\n", edata->port_id);
- if (edata->val_bits & HISI_PCIE_LOCAL_VALID_ERR_SEVERITY)
- dev_info(dev, "Error severity = %s\n",
- hisi_pcie_get_string(hisi_pcie_error_sev,
- ARRAY_SIZE(hisi_pcie_error_sev),
- edata->err_severity));
- if (edata->val_bits & HISI_PCIE_LOCAL_VALID_ERR_TYPE)
- dev_info(dev, "Error type = 0x%x\n", edata->err_type);
- dev_info(dev, "Reg Dump:\n");
- idx = HISI_PCIE_LOCAL_VALID_ERR_MISC;
- for_each_set_bit_from(idx, valid_bits,
- HISI_PCIE_LOCAL_VALID_ERR_MISC + HISI_PCIE_ERR_MISC_REGS)
- dev_info(dev, "ERR_MISC_%d = 0x%x\n", idx - HISI_PCIE_LOCAL_VALID_ERR_MISC,
- edata->err_misc[idx - HISI_PCIE_LOCAL_VALID_ERR_MISC]);
- if (edata->err_severity != HISI_PCIE_ERR_SEV_RECOVERABLE)
- return;
- /* Recovery for the PCIe controller errors, try reset
- * PCI port for the error recovery
- */
- rc = hisi_pcie_port_do_recovery(pdev, edata->socket_id,
- HISI_PCIE_PORT_ID(edata->core_id, edata->port_id));
- if (rc)
- dev_info(dev, "fail to do hisi pcie port reset\n");
- }
- static int hisi_pcie_notify_error(struct notifier_block *nb,
- unsigned long event, void *data)
- {
- struct acpi_hest_generic_data *gdata = data;
- const struct hisi_pcie_error_data *error_data = acpi_hest_get_payload(gdata);
- struct hisi_pcie_error_private *priv;
- struct device *dev;
- struct platform_device *pdev;
- guid_t err_sec_guid;
- u8 socket;
- import_guid(&err_sec_guid, gdata->section_type);
- if (!guid_equal(&err_sec_guid, &hisi_pcie_sec_guid))
- return NOTIFY_DONE;
- priv = container_of(nb, struct hisi_pcie_error_private, nb);
- dev = priv->dev;
- if (device_property_read_u8(dev, "socket", &socket))
- return NOTIFY_DONE;
- if (error_data->socket_id != socket)
- return NOTIFY_DONE;
- pdev = container_of(dev, struct platform_device, dev);
- hisi_pcie_handle_error(pdev, error_data);
- return NOTIFY_OK;
- }
- static int hisi_pcie_error_handler_probe(struct platform_device *pdev)
- {
- struct hisi_pcie_error_private *priv;
- int ret;
- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
- priv->nb.notifier_call = hisi_pcie_notify_error;
- priv->dev = &pdev->dev;
- ret = ghes_register_vendor_record_notifier(&priv->nb);
- if (ret) {
- dev_err(&pdev->dev,
- "Failed to register hisi pcie controller error handler with apei\n");
- return ret;
- }
- platform_set_drvdata(pdev, priv);
- return 0;
- }
- static int hisi_pcie_error_handler_remove(struct platform_device *pdev)
- {
- struct hisi_pcie_error_private *priv = platform_get_drvdata(pdev);
- ghes_unregister_vendor_record_notifier(&priv->nb);
- return 0;
- }
- static const struct acpi_device_id hisi_pcie_acpi_match[] = {
- { "HISI0361", 0 },
- { }
- };
- static struct platform_driver hisi_pcie_error_handler_driver = {
- .driver = {
- .name = "hisi-pcie-error-handler",
- .acpi_match_table = hisi_pcie_acpi_match,
- },
- .probe = hisi_pcie_error_handler_probe,
- .remove = hisi_pcie_error_handler_remove,
- };
- module_platform_driver(hisi_pcie_error_handler_driver);
- MODULE_DESCRIPTION("HiSilicon HIP PCIe controller error handling driver");
- MODULE_LICENSE("GPL v2");
|