| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612 |
- // SPDX-License-Identifier: GPL-2.0-only
- /* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */
- /* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. */
- #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 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,
- .auto_queue = 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,
- .auto_queue = 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,
- .auto_queue = 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,
- .auto_queue = 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,
- .auto_queue = 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,
- .auto_queue = 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,
- .auto_queue = 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,
- .auto_queue = 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,
- .auto_queue = 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,
- .auto_queue = 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,
- .auto_queue = 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,
- .auto_queue = 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,
- .auto_queue = 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,
- .auto_queue = 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,
- .auto_queue = 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,
- .auto_queue = 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,
- .auto_queue = 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,
- .auto_queue = 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,
- .auto_queue = 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,
- .auto_queue = 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,
- .auto_queue = 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,
- .auto_queue = 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,
- .auto_queue = 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,
- .auto_queue = 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_controller_config aic100_config = {
- .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,
- };
- 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)
- {
- 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 = "qcom/aic100/sbl.bin";
- /* use latest configured timeout */
- aic100_config.timeout_ms = mhi_timeout_ms;
- ret = mhi_register_controller(mhi_cntrl, &aic100_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);
- }
|