| // SPDX-License-Identifier: GPL-2.0-only | |
| /* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */ | |
| /* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ | |
| #include <linux/delay.h> | |
| #include <linux/err.h> | |
| #include <linux/memblock.h> | |
| #include <linux/mhi.h> | |
| #include <linux/moduleparam.h> | |
| #include <linux/pci.h> | |
| #include <linux/sizes.h> | |
| #include "mhi_controller.h" | |
| #include "qaic.h" | |
| #define MAX_RESET_TIME_SEC 25 | |
| static unsigned int mhi_timeout_ms = 2000; /* 2 sec default */ | |
| module_param(mhi_timeout_ms, uint, 0600); | |
| MODULE_PARM_DESC(mhi_timeout_ms, "MHI controller timeout value"); | |
| static const char *fw_image_paths[FAMILY_MAX] = { | |
| [FAMILY_AIC100] = "qcom/aic100/sbl.bin", | |
| [FAMILY_AIC200] = "qcom/aic200/sbl.bin", | |
| }; | |
| static const struct mhi_channel_config aic100_channels[] = { | |
| { | |
| .name = "QAIC_LOOPBACK", | |
| .num = 0, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_TO_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_LOOPBACK", | |
| .num = 1, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_FROM_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_SAHARA", | |
| .num = 2, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_TO_DEVICE, | |
| .ee_mask = MHI_CH_EE_SBL, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_SAHARA", | |
| .num = 3, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_FROM_DEVICE, | |
| .ee_mask = MHI_CH_EE_SBL, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_DIAG", | |
| .num = 4, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_TO_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_DIAG", | |
| .num = 5, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_FROM_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_SSR", | |
| .num = 6, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_TO_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_SSR", | |
| .num = 7, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_FROM_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_QDSS", | |
| .num = 8, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_TO_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_QDSS", | |
| .num = 9, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_FROM_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_CONTROL", | |
| .num = 10, | |
| .num_elements = 128, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_TO_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_CONTROL", | |
| .num = 11, | |
| .num_elements = 128, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_FROM_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_LOGGING", | |
| .num = 12, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_TO_DEVICE, | |
| .ee_mask = MHI_CH_EE_SBL, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_LOGGING", | |
| .num = 13, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_FROM_DEVICE, | |
| .ee_mask = MHI_CH_EE_SBL, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_STATUS", | |
| .num = 14, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_TO_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_STATUS", | |
| .num = 15, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_FROM_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_TELEMETRY", | |
| .num = 16, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_TO_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_TELEMETRY", | |
| .num = 17, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_FROM_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_DEBUG", | |
| .num = 18, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_TO_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_DEBUG", | |
| .num = 19, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_FROM_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_TIMESYNC", | |
| .num = 20, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_TO_DEVICE, | |
| .ee_mask = MHI_CH_EE_SBL, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_TIMESYNC", | |
| .num = 21, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_FROM_DEVICE, | |
| .ee_mask = MHI_CH_EE_SBL, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_TIMESYNC_PERIODIC", | |
| .num = 22, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_TO_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_TIMESYNC_PERIODIC", | |
| .num = 23, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_FROM_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "IPCR", | |
| .num = 24, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_TO_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "IPCR", | |
| .num = 25, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_FROM_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| }; | |
| static const struct mhi_channel_config aic200_channels[] = { | |
| { | |
| .name = "QAIC_LOOPBACK", | |
| .num = 0, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_TO_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_LOOPBACK", | |
| .num = 1, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_FROM_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_SAHARA", | |
| .num = 2, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_TO_DEVICE, | |
| .ee_mask = MHI_CH_EE_SBL, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_SAHARA", | |
| .num = 3, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_FROM_DEVICE, | |
| .ee_mask = MHI_CH_EE_SBL, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_SSR", | |
| .num = 6, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_TO_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_SSR", | |
| .num = 7, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_FROM_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_CONTROL", | |
| .num = 10, | |
| .num_elements = 128, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_TO_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_CONTROL", | |
| .num = 11, | |
| .num_elements = 128, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_FROM_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_LOGGING", | |
| .num = 12, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_TO_DEVICE, | |
| .ee_mask = MHI_CH_EE_SBL, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_LOGGING", | |
| .num = 13, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_FROM_DEVICE, | |
| .ee_mask = MHI_CH_EE_SBL, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_STATUS", | |
| .num = 14, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_TO_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_STATUS", | |
| .num = 15, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_FROM_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_TELEMETRY", | |
| .num = 16, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_TO_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_TELEMETRY", | |
| .num = 17, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_FROM_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_TIMESYNC_PERIODIC", | |
| .num = 22, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_TO_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "QAIC_TIMESYNC_PERIODIC", | |
| .num = 23, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_FROM_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "IPCR", | |
| .num = 24, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_TO_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| { | |
| .name = "IPCR", | |
| .num = 25, | |
| .num_elements = 32, | |
| .local_elements = 0, | |
| .event_ring = 0, | |
| .dir = DMA_FROM_DEVICE, | |
| .ee_mask = MHI_CH_EE_AMSS, | |
| .pollcfg = 0, | |
| .doorbell = MHI_DB_BRST_DISABLE, | |
| .lpm_notify = false, | |
| .offload_channel = false, | |
| .doorbell_mode_switch = false, | |
| .wake_capable = false, | |
| }, | |
| }; | |
| static struct mhi_event_config aic100_events[] = { | |
| { | |
| .num_elements = 32, | |
| .irq_moderation_ms = 0, | |
| .irq = 0, | |
| .channel = U32_MAX, | |
| .priority = 1, | |
| .mode = MHI_DB_BRST_DISABLE, | |
| .data_type = MHI_ER_CTRL, | |
| .hardware_event = false, | |
| .client_managed = false, | |
| .offload_channel = false, | |
| }, | |
| }; | |
| static struct mhi_event_config aic200_events[] = { | |
| { | |
| .num_elements = 32, | |
| .irq_moderation_ms = 0, | |
| .irq = 0, | |
| .channel = U32_MAX, | |
| .priority = 1, | |
| .mode = MHI_DB_BRST_DISABLE, | |
| .data_type = MHI_ER_CTRL, | |
| .hardware_event = false, | |
| .client_managed = false, | |
| .offload_channel = false, | |
| }, | |
| }; | |
| static struct mhi_controller_config mhi_cntrl_configs[] = { | |
| [FAMILY_AIC100] = { | |
| .max_channels = 128, | |
| .timeout_ms = 0, /* controlled by mhi_timeout */ | |
| .buf_len = 0, | |
| .num_channels = ARRAY_SIZE(aic100_channels), | |
| .ch_cfg = aic100_channels, | |
| .num_events = ARRAY_SIZE(aic100_events), | |
| .event_cfg = aic100_events, | |
| .use_bounce_buf = false, | |
| .m2_no_db = false, | |
| }, | |
| [FAMILY_AIC200] = { | |
| .max_channels = 128, | |
| .timeout_ms = 0, /* controlled by mhi_timeout */ | |
| .buf_len = 0, | |
| .num_channels = ARRAY_SIZE(aic200_channels), | |
| .ch_cfg = aic200_channels, | |
| .num_events = ARRAY_SIZE(aic200_events), | |
| .event_cfg = aic200_events, | |
| .use_bounce_buf = false, | |
| .m2_no_db = false, | |
| }, | |
| }; | |
| static int mhi_read_reg(struct mhi_controller *mhi_cntrl, void __iomem *addr, u32 *out) | |
| { | |
| u32 tmp; | |
| /* | |
| * SOC_HW_VERSION quirk | |
| * The SOC_HW_VERSION register (offset 0x224) is not reliable and | |
| * may contain uninitialized values, including 0xFFFFFFFF. This could | |
| * cause a false positive link down error. Instead, intercept any | |
| * reads and provide the correct value of the register. | |
| */ | |
| if (addr - mhi_cntrl->regs == 0x224) { | |
| *out = 0x60110200; | |
| return 0; | |
| } | |
| tmp = readl_relaxed(addr); | |
| if (tmp == U32_MAX) | |
| return -EIO; | |
| *out = tmp; | |
| return 0; | |
| } | |
| static void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *addr, u32 val) | |
| { | |
| writel_relaxed(val, addr); | |
| } | |
| static int mhi_runtime_get(struct mhi_controller *mhi_cntrl) | |
| { | |
| return 0; | |
| } | |
| static void mhi_runtime_put(struct mhi_controller *mhi_cntrl) | |
| { | |
| } | |
| static void mhi_status_cb(struct mhi_controller *mhi_cntrl, enum mhi_callback reason) | |
| { | |
| struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_cntrl->cntrl_dev)); | |
| /* this event occurs in atomic context */ | |
| if (reason == MHI_CB_FATAL_ERROR) | |
| pci_err(qdev->pdev, "Fatal error received from device. Attempting to recover\n"); | |
| /* this event occurs in non-atomic context */ | |
| if (reason == MHI_CB_SYS_ERROR) | |
| qaic_dev_reset_clean_local_state(qdev); | |
| } | |
| static int mhi_reset_and_async_power_up(struct mhi_controller *mhi_cntrl) | |
| { | |
| u8 time_sec = 1; | |
| int current_ee; | |
| int ret; | |
| /* Reset the device to bring the device in PBL EE */ | |
| mhi_soc_reset(mhi_cntrl); | |
| /* | |
| * Keep checking the execution environment(EE) after every 1 second | |
| * interval. | |
| */ | |
| do { | |
| msleep(1000); | |
| current_ee = mhi_get_exec_env(mhi_cntrl); | |
| } while (current_ee != MHI_EE_PBL && time_sec++ <= MAX_RESET_TIME_SEC); | |
| /* If the device is in PBL EE retry power up */ | |
| if (current_ee == MHI_EE_PBL) | |
| ret = mhi_async_power_up(mhi_cntrl); | |
| else | |
| ret = -EIO; | |
| return ret; | |
| } | |
| struct mhi_controller *qaic_mhi_register_controller(struct pci_dev *pci_dev, void __iomem *mhi_bar, | |
| int mhi_irq, bool shared_msi, int family) | |
| { | |
| struct mhi_controller_config mhi_config = mhi_cntrl_configs[family]; | |
| struct mhi_controller *mhi_cntrl; | |
| int ret; | |
| mhi_cntrl = devm_kzalloc(&pci_dev->dev, sizeof(*mhi_cntrl), GFP_KERNEL); | |
| if (!mhi_cntrl) | |
| return ERR_PTR(-ENOMEM); | |
| mhi_cntrl->cntrl_dev = &pci_dev->dev; | |
| /* | |
| * Covers the entire possible physical ram region. Remote side is | |
| * going to calculate a size of this range, so subtract 1 to prevent | |
| * rollover. | |
| */ | |
| mhi_cntrl->iova_start = 0; | |
| mhi_cntrl->iova_stop = PHYS_ADDR_MAX - 1; | |
| mhi_cntrl->status_cb = mhi_status_cb; | |
| mhi_cntrl->runtime_get = mhi_runtime_get; | |
| mhi_cntrl->runtime_put = mhi_runtime_put; | |
| mhi_cntrl->read_reg = mhi_read_reg; | |
| mhi_cntrl->write_reg = mhi_write_reg; | |
| mhi_cntrl->regs = mhi_bar; | |
| mhi_cntrl->reg_len = SZ_4K; | |
| mhi_cntrl->nr_irqs = 1; | |
| mhi_cntrl->irq = devm_kmalloc(&pci_dev->dev, sizeof(*mhi_cntrl->irq), GFP_KERNEL); | |
| if (!mhi_cntrl->irq) | |
| return ERR_PTR(-ENOMEM); | |
| mhi_cntrl->irq[0] = mhi_irq; | |
| if (shared_msi) /* MSI shared with data path, no IRQF_NO_SUSPEND */ | |
| mhi_cntrl->irq_flags = IRQF_SHARED; | |
| mhi_cntrl->fw_image = fw_image_paths[family]; | |
| if (family == FAMILY_AIC200) { | |
| mhi_cntrl->name = "AIC200"; | |
| mhi_cntrl->seg_len = SZ_512K; | |
| } else { | |
| mhi_cntrl->name = "AIC100"; | |
| } | |
| /* use latest configured timeout */ | |
| mhi_config.timeout_ms = mhi_timeout_ms; | |
| ret = mhi_register_controller(mhi_cntrl, &mhi_config); | |
| if (ret) { | |
| pci_err(pci_dev, "mhi_register_controller failed %d\n", ret); | |
| return ERR_PTR(ret); | |
| } | |
| ret = mhi_prepare_for_power_up(mhi_cntrl); | |
| if (ret) { | |
| pci_err(pci_dev, "mhi_prepare_for_power_up failed %d\n", ret); | |
| goto prepare_power_up_fail; | |
| } | |
| ret = mhi_async_power_up(mhi_cntrl); | |
| /* | |
| * If EIO is returned it is possible that device is in SBL EE, which is | |
| * undesired. SOC reset the device and try to power up again. | |
| */ | |
| if (ret == -EIO && MHI_EE_SBL == mhi_get_exec_env(mhi_cntrl)) { | |
| pci_err(pci_dev, "Found device in SBL at MHI init. Attempting a reset.\n"); | |
| ret = mhi_reset_and_async_power_up(mhi_cntrl); | |
| } | |
| if (ret) { | |
| pci_err(pci_dev, "mhi_async_power_up failed %d\n", ret); | |
| goto power_up_fail; | |
| } | |
| return mhi_cntrl; | |
| power_up_fail: | |
| mhi_unprepare_after_power_down(mhi_cntrl); | |
| prepare_power_up_fail: | |
| mhi_unregister_controller(mhi_cntrl); | |
| return ERR_PTR(ret); | |
| } | |
| void qaic_mhi_free_controller(struct mhi_controller *mhi_cntrl, bool link_up) | |
| { | |
| mhi_power_down(mhi_cntrl, link_up); | |
| mhi_unprepare_after_power_down(mhi_cntrl); | |
| mhi_unregister_controller(mhi_cntrl); | |
| } | |
| void qaic_mhi_start_reset(struct mhi_controller *mhi_cntrl) | |
| { | |
| mhi_power_down(mhi_cntrl, true); | |
| } | |
| void qaic_mhi_reset_done(struct mhi_controller *mhi_cntrl) | |
| { | |
| struct pci_dev *pci_dev = container_of(mhi_cntrl->cntrl_dev, struct pci_dev, dev); | |
| int ret; | |
| ret = mhi_async_power_up(mhi_cntrl); | |
| if (ret) | |
| pci_err(pci_dev, "mhi_async_power_up failed after reset %d\n", ret); | |
| } |