Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
# SPDX-License-Identifier: GPL-2.0
#
# MHI bus
#
# Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
#
config MHI_BUS
tristate "Modem Host Interface (MHI) bus"
help
Bus driver for MHI protocol. Modem Host Interface (MHI) is a
communication protocol used by the host processors to control
and communicate with modem devices over a high speed peripheral
bus or shared memory.
config MHI_BUS_DEBUG
bool "Debugfs support for the MHI bus"
depends on MHI_BUS && DEBUG_FS
help
Enable debugfs support for use with the MHI transport. Allows
reading and/or modifying some values within the MHI controller
for debug and test purposes.
config MHI_BUS_PCI_GENERIC
tristate "MHI PCI controller driver"
depends on MHI_BUS
depends on PCI
help
This driver provides MHI PCI controller driver for devices such as
Qualcomm SDX55 based PCIe modems.
@@ -0,0 +1,6 @@
obj-$(CONFIG_MHI_BUS) += mhi.o
mhi-y := init.o main.o pm.o boot.o
mhi-$(CONFIG_MHI_BUS_DEBUG) += debugfs.o
obj-$(CONFIG_MHI_BUS_PCI_GENERIC) += mhi_pci_generic.o
mhi_pci_generic-y += pci_generic.o
+640
View File
@@ -0,0 +1,640 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-direction.h>
#include <linux/dma-mapping.h>
#include <linux/firmware.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/mhi.h>
#include <linux/module.h>
#include <linux/random.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include "internal.h"
/* Setup RDDM vector table for RDDM transfer and program RXVEC */
int mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
struct image_info *img_info)
{
struct mhi_buf *mhi_buf = img_info->mhi_buf;
struct bhi_vec_entry *bhi_vec = img_info->bhi_vec;
void __iomem *base = mhi_cntrl->bhie;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
u32 sequence_id;
unsigned int i;
int ret;
for (i = 0; i < img_info->entries - 1; i++, mhi_buf++, bhi_vec++) {
bhi_vec->dma_addr = cpu_to_le64(mhi_buf->dma_addr);
bhi_vec->size = cpu_to_le64(mhi_buf->len);
}
dev_dbg(dev, "BHIe programming for RDDM\n");
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_HIGH_OFFS,
upper_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_LOW_OFFS,
lower_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHIE_RXVECSIZE_OFFS, mhi_buf->len);
sequence_id = MHI_RANDOM_U32_NONZERO(BHIE_RXVECSTATUS_SEQNUM_BMSK);
ret = mhi_write_reg_field(mhi_cntrl, base, BHIE_RXVECDB_OFFS,
BHIE_RXVECDB_SEQNUM_BMSK, sequence_id);
if (ret) {
dev_err(dev, "Failed to write sequence ID for BHIE_RXVECDB\n");
return ret;
}
dev_dbg(dev, "Address: %p and len: 0x%zx sequence: %u\n",
&mhi_buf->dma_addr, mhi_buf->len, sequence_id);
return 0;
}
/* Collect RDDM buffer during kernel panic */
static int __mhi_download_rddm_in_panic(struct mhi_controller *mhi_cntrl)
{
int ret;
u32 rx_status;
enum mhi_ee_type ee;
const u32 delayus = 2000;
u32 retry = (mhi_cntrl->timeout_ms * 1000) / delayus;
const u32 rddm_timeout_us = 200000;
int rddm_retry = rddm_timeout_us / delayus;
void __iomem *base = mhi_cntrl->bhie;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
dev_dbg(dev, "Entered with pm_state:%s dev_state:%s ee:%s\n",
to_mhi_pm_state_str(mhi_cntrl->pm_state),
mhi_state_str(mhi_cntrl->dev_state),
TO_MHI_EXEC_STR(mhi_cntrl->ee));
/*
* This should only be executing during a kernel panic, we expect all
* other cores to shutdown while we're collecting RDDM buffer. After
* returning from this function, we expect the device to reset.
*
* Normally, we read/write pm_state only after grabbing the
* pm_lock, since we're in a panic, skipping it. Also there is no
* guarantee that this state change would take effect since
* we're setting it w/o grabbing pm_lock
*/
mhi_cntrl->pm_state = MHI_PM_LD_ERR_FATAL_DETECT;
/* update should take the effect immediately */
smp_wmb();
/*
* Make sure device is not already in RDDM. In case the device asserts
* and a kernel panic follows, device will already be in RDDM.
* Do not trigger SYS ERR again and proceed with waiting for
* image download completion.
*/
ee = mhi_get_exec_env(mhi_cntrl);
if (ee == MHI_EE_MAX)
goto error_exit_rddm;
if (ee != MHI_EE_RDDM) {
dev_dbg(dev, "Trigger device into RDDM mode using SYS ERR\n");
mhi_set_mhi_state(mhi_cntrl, MHI_STATE_SYS_ERR);
dev_dbg(dev, "Waiting for device to enter RDDM\n");
while (rddm_retry--) {
ee = mhi_get_exec_env(mhi_cntrl);
if (ee == MHI_EE_RDDM)
break;
udelay(delayus);
}
if (rddm_retry <= 0) {
/* Hardware reset so force device to enter RDDM */
dev_dbg(dev,
"Did not enter RDDM, do a host req reset\n");
mhi_soc_reset(mhi_cntrl);
udelay(delayus);
}
ee = mhi_get_exec_env(mhi_cntrl);
}
dev_dbg(dev,
"Waiting for RDDM image download via BHIe, current EE:%s\n",
TO_MHI_EXEC_STR(ee));
while (retry--) {
ret = mhi_read_reg_field(mhi_cntrl, base, BHIE_RXVECSTATUS_OFFS,
BHIE_RXVECSTATUS_STATUS_BMSK, &rx_status);
if (ret)
return -EIO;
if (rx_status == BHIE_RXVECSTATUS_STATUS_XFER_COMPL)
return 0;
udelay(delayus);
}
ee = mhi_get_exec_env(mhi_cntrl);
ret = mhi_read_reg(mhi_cntrl, base, BHIE_RXVECSTATUS_OFFS, &rx_status);
dev_err(dev, "RXVEC_STATUS: 0x%x\n", rx_status);
error_exit_rddm:
dev_err(dev, "RDDM transfer failed. Current EE: %s\n",
TO_MHI_EXEC_STR(ee));
return -EIO;
}
/* Download RDDM image from device */
int mhi_download_rddm_image(struct mhi_controller *mhi_cntrl, bool in_panic)
{
void __iomem *base = mhi_cntrl->bhie;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
u32 rx_status;
if (in_panic)
return __mhi_download_rddm_in_panic(mhi_cntrl);
dev_dbg(dev, "Waiting for RDDM image download via BHIe\n");
/* Wait for the image download to complete */
wait_event_timeout(mhi_cntrl->state_event,
mhi_read_reg_field(mhi_cntrl, base,
BHIE_RXVECSTATUS_OFFS,
BHIE_RXVECSTATUS_STATUS_BMSK,
&rx_status) || rx_status,
msecs_to_jiffies(mhi_cntrl->timeout_ms));
return (rx_status == BHIE_RXVECSTATUS_STATUS_XFER_COMPL) ? 0 : -EIO;
}
EXPORT_SYMBOL_GPL(mhi_download_rddm_image);
static void mhi_fw_load_error_dump(struct mhi_controller *mhi_cntrl)
{
struct device *dev = &mhi_cntrl->mhi_dev->dev;
rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
void __iomem *base = mhi_cntrl->bhi;
int ret, i;
u32 val;
struct {
char *name;
u32 offset;
} error_reg[] = {
{ "ERROR_CODE", BHI_ERRCODE },
{ "ERROR_DBG1", BHI_ERRDBG1 },
{ "ERROR_DBG2", BHI_ERRDBG2 },
{ "ERROR_DBG3", BHI_ERRDBG3 },
{ NULL },
};
read_lock_bh(pm_lock);
if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
for (i = 0; error_reg[i].name; i++) {
ret = mhi_read_reg(mhi_cntrl, base, error_reg[i].offset, &val);
if (ret)
break;
dev_err(dev, "Reg: %s value: 0x%x\n", error_reg[i].name, val);
}
}
read_unlock_bh(pm_lock);
}
static int mhi_fw_load_bhie(struct mhi_controller *mhi_cntrl,
const struct mhi_buf *mhi_buf)
{
void __iomem *base = mhi_cntrl->bhie;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
u32 tx_status, sequence_id;
int ret;
read_lock_bh(pm_lock);
if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
read_unlock_bh(pm_lock);
return -EIO;
}
sequence_id = MHI_RANDOM_U32_NONZERO(BHIE_TXVECSTATUS_SEQNUM_BMSK);
dev_dbg(dev, "Starting image download via BHIe. Sequence ID: %u\n",
sequence_id);
mhi_write_reg(mhi_cntrl, base, BHIE_TXVECADDR_HIGH_OFFS,
upper_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHIE_TXVECADDR_LOW_OFFS,
lower_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHIE_TXVECSIZE_OFFS, mhi_buf->len);
ret = mhi_write_reg_field(mhi_cntrl, base, BHIE_TXVECDB_OFFS,
BHIE_TXVECDB_SEQNUM_BMSK, sequence_id);
read_unlock_bh(pm_lock);
if (ret)
return ret;
/* Wait for the image download to complete */
ret = wait_event_timeout(mhi_cntrl->state_event,
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
mhi_read_reg_field(mhi_cntrl, base,
BHIE_TXVECSTATUS_OFFS,
BHIE_TXVECSTATUS_STATUS_BMSK,
&tx_status) || tx_status,
msecs_to_jiffies(mhi_cntrl->timeout_ms));
if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
tx_status != BHIE_TXVECSTATUS_STATUS_XFER_COMPL)
return -EIO;
return (!ret) ? -ETIMEDOUT : 0;
}
static int mhi_fw_load_bhi(struct mhi_controller *mhi_cntrl,
const struct mhi_buf *mhi_buf)
{
struct device *dev = &mhi_cntrl->mhi_dev->dev;
rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
void __iomem *base = mhi_cntrl->bhi;
u32 tx_status, session_id;
int ret;
read_lock_bh(pm_lock);
if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
read_unlock_bh(pm_lock);
goto invalid_pm_state;
}
session_id = MHI_RANDOM_U32_NONZERO(BHI_TXDB_SEQNUM_BMSK);
dev_dbg(dev, "Starting image download via BHI. Session ID: %u\n",
session_id);
mhi_write_reg(mhi_cntrl, base, BHI_STATUS, 0);
mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_HIGH, upper_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_LOW, lower_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHI_IMGSIZE, mhi_buf->len);
mhi_write_reg(mhi_cntrl, base, BHI_IMGTXDB, session_id);
read_unlock_bh(pm_lock);
/* Wait for the image download to complete */
ret = wait_event_timeout(mhi_cntrl->state_event,
MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state) ||
mhi_read_reg_field(mhi_cntrl, base, BHI_STATUS,
BHI_STATUS_MASK, &tx_status) || tx_status,
msecs_to_jiffies(mhi_cntrl->timeout_ms));
if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))
goto invalid_pm_state;
if (tx_status == BHI_STATUS_ERROR) {
dev_err(dev, "Image transfer failed\n");
mhi_fw_load_error_dump(mhi_cntrl);
goto invalid_pm_state;
}
return (!ret) ? -ETIMEDOUT : 0;
invalid_pm_state:
return -EIO;
}
static void mhi_free_bhi_buffer(struct mhi_controller *mhi_cntrl,
struct image_info *image_info)
{
struct mhi_buf *mhi_buf = image_info->mhi_buf;
dma_free_coherent(mhi_cntrl->cntrl_dev, mhi_buf->len, mhi_buf->buf, mhi_buf->dma_addr);
kfree(image_info);
}
void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl,
struct image_info *image_info)
{
int i;
struct mhi_buf *mhi_buf = image_info->mhi_buf;
for (i = 0; i < image_info->entries; i++, mhi_buf++)
dma_free_coherent(mhi_cntrl->cntrl_dev, mhi_buf->len,
mhi_buf->buf, mhi_buf->dma_addr);
kfree(image_info);
}
static int mhi_alloc_bhi_buffer(struct mhi_controller *mhi_cntrl,
struct image_info **image_info,
size_t alloc_size)
{
struct image_info *img_info;
struct mhi_buf *mhi_buf;
img_info = kzalloc_flex(*img_info, mhi_buf, 1);
if (!img_info)
return -ENOMEM;
/* Allocate and populate vector table */
mhi_buf = img_info->mhi_buf;
mhi_buf->len = alloc_size;
mhi_buf->buf = dma_alloc_coherent(mhi_cntrl->cntrl_dev, mhi_buf->len,
&mhi_buf->dma_addr, GFP_KERNEL);
if (!mhi_buf->buf)
goto error_alloc_segment;
img_info->bhi_vec = NULL;
img_info->entries = 1;
*image_info = img_info;
return 0;
error_alloc_segment:
kfree(img_info);
return -ENOMEM;
}
int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl,
struct image_info **image_info,
size_t alloc_size)
{
size_t seg_size = mhi_cntrl->seg_len;
int segments = DIV_ROUND_UP(alloc_size, seg_size) + 1;
int i;
struct image_info *img_info;
struct mhi_buf *mhi_buf;
img_info = kzalloc_flex(*img_info, mhi_buf, segments);
if (!img_info)
return -ENOMEM;
img_info->entries = segments;
/* Allocate and populate vector table */
mhi_buf = img_info->mhi_buf;
for (i = 0; i < segments; i++, mhi_buf++) {
size_t vec_size = seg_size;
/* Vector table is the last entry */
if (i == segments - 1)
vec_size = sizeof(struct bhi_vec_entry) * i;
mhi_buf->len = vec_size;
mhi_buf->buf = dma_alloc_coherent(mhi_cntrl->cntrl_dev,
vec_size, &mhi_buf->dma_addr,
GFP_KERNEL);
if (!mhi_buf->buf)
goto error_alloc_segment;
}
img_info->bhi_vec = img_info->mhi_buf[segments - 1].buf;
*image_info = img_info;
return 0;
error_alloc_segment:
for (--i, --mhi_buf; i >= 0; i--, mhi_buf--)
dma_free_coherent(mhi_cntrl->cntrl_dev, mhi_buf->len,
mhi_buf->buf, mhi_buf->dma_addr);
kfree(img_info);
return -ENOMEM;
}
static void mhi_firmware_copy_bhie(struct mhi_controller *mhi_cntrl,
const u8 *buf, size_t remainder,
struct image_info *img_info)
{
size_t to_cpy;
struct mhi_buf *mhi_buf = img_info->mhi_buf;
struct bhi_vec_entry *bhi_vec = img_info->bhi_vec;
while (remainder) {
to_cpy = min(remainder, mhi_buf->len);
memcpy(mhi_buf->buf, buf, to_cpy);
bhi_vec->dma_addr = cpu_to_le64(mhi_buf->dma_addr);
bhi_vec->size = cpu_to_le64(to_cpy);
buf += to_cpy;
remainder -= to_cpy;
bhi_vec++;
mhi_buf++;
}
}
static enum mhi_fw_load_type mhi_fw_load_type_get(const struct mhi_controller *mhi_cntrl)
{
if (mhi_cntrl->fbc_download) {
return MHI_FW_LOAD_FBC;
} else {
if (mhi_cntrl->seg_len)
return MHI_FW_LOAD_BHIE;
else
return MHI_FW_LOAD_BHI;
}
}
static int mhi_load_image_bhi(struct mhi_controller *mhi_cntrl, const u8 *fw_data, size_t size)
{
struct image_info *image;
int ret;
ret = mhi_alloc_bhi_buffer(mhi_cntrl, &image, size);
if (ret)
return ret;
/* Load the firmware into BHI vec table */
memcpy(image->mhi_buf->buf, fw_data, size);
ret = mhi_fw_load_bhi(mhi_cntrl, &image->mhi_buf[image->entries - 1]);
mhi_free_bhi_buffer(mhi_cntrl, image);
return ret;
}
static int mhi_load_image_bhie(struct mhi_controller *mhi_cntrl, const u8 *fw_data, size_t size)
{
struct image_info *image;
int ret;
ret = mhi_alloc_bhie_table(mhi_cntrl, &image, size);
if (ret)
return ret;
mhi_firmware_copy_bhie(mhi_cntrl, fw_data, size, image);
ret = mhi_fw_load_bhie(mhi_cntrl, &image->mhi_buf[image->entries - 1]);
mhi_free_bhie_table(mhi_cntrl, image);
return ret;
}
void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
{
const struct firmware *firmware = NULL;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
enum mhi_fw_load_type fw_load_type;
enum mhi_pm_state new_state;
const char *fw_name;
const u8 *fw_data;
size_t size, fw_sz;
int ret;
if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
dev_err(dev, "Device MHI is not in valid state\n");
return;
}
/* save hardware info from BHI */
ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_SERIALNU,
&mhi_cntrl->serial_number);
if (ret)
dev_err(dev, "Could not capture serial number via BHI\n");
/* wait for ready on pass through or any other execution environment */
if (!MHI_FW_LOAD_CAPABLE(mhi_cntrl->ee))
goto fw_load_ready_state;
fw_name = (mhi_cntrl->ee == MHI_EE_EDL) ?
mhi_cntrl->edl_image : mhi_cntrl->fw_image;
/* check if the driver has already provided the firmware data */
if (!fw_name && mhi_cntrl->fbc_download &&
mhi_cntrl->fw_data && mhi_cntrl->fw_sz) {
if (!mhi_cntrl->sbl_size) {
dev_err(dev, "fw_data provided but no sbl_size\n");
goto error_fw_load;
}
size = mhi_cntrl->sbl_size;
fw_data = mhi_cntrl->fw_data;
fw_sz = mhi_cntrl->fw_sz;
goto skip_req_fw;
}
if (!fw_name || (mhi_cntrl->fbc_download && (!mhi_cntrl->sbl_size ||
!mhi_cntrl->seg_len))) {
dev_err(dev,
"No firmware image defined or !sbl_size || !seg_len\n");
goto error_fw_load;
}
ret = request_firmware(&firmware, fw_name, dev);
if (ret) {
dev_err(dev, "Error loading firmware: %d\n", ret);
goto error_fw_load;
}
size = (mhi_cntrl->fbc_download) ? mhi_cntrl->sbl_size : firmware->size;
/* SBL size provided is maximum size, not necessarily the image size */
if (size > firmware->size)
size = firmware->size;
fw_data = firmware->data;
fw_sz = firmware->size;
skip_req_fw:
fw_load_type = mhi_fw_load_type_get(mhi_cntrl);
if (fw_load_type == MHI_FW_LOAD_BHIE)
ret = mhi_load_image_bhie(mhi_cntrl, fw_data, size);
else
ret = mhi_load_image_bhi(mhi_cntrl, fw_data, size);
/* Error or in EDL mode, we're done */
if (ret) {
dev_err(dev, "MHI did not load image over BHI%s, ret: %d\n",
fw_load_type == MHI_FW_LOAD_BHIE ? "e" : "",
ret);
release_firmware(firmware);
goto error_fw_load;
}
/* Wait for ready since EDL image was loaded */
if (fw_name && fw_name == mhi_cntrl->edl_image) {
release_firmware(firmware);
goto fw_load_ready_state;
}
write_lock_irq(&mhi_cntrl->pm_lock);
mhi_cntrl->dev_state = MHI_STATE_RESET;
write_unlock_irq(&mhi_cntrl->pm_lock);
/*
* If we're doing fbc, populate vector tables while
* device transitioning into MHI READY state
*/
if (fw_load_type == MHI_FW_LOAD_FBC) {
/*
* Some FW combine two separate ELF images (SBL + WLAN FW) in a single
* file. Hence, check for the existence of the second ELF header after
* SBL. If present, load the second image separately.
*/
if (!memcmp(fw_data + mhi_cntrl->sbl_size, ELFMAG, SELFMAG)) {
fw_data += mhi_cntrl->sbl_size;
fw_sz -= mhi_cntrl->sbl_size;
}
ret = mhi_alloc_bhie_table(mhi_cntrl, &mhi_cntrl->fbc_image, fw_sz);
if (ret) {
release_firmware(firmware);
goto error_fw_load;
}
/* Load the firmware into BHIE vec table */
mhi_firmware_copy_bhie(mhi_cntrl, fw_data, fw_sz, mhi_cntrl->fbc_image);
}
release_firmware(firmware);
fw_load_ready_state:
/* Transitioning into MHI RESET->READY state */
ret = mhi_ready_state_transition(mhi_cntrl);
if (ret) {
dev_err(dev, "MHI did not enter READY state\n");
goto error_ready_state;
}
dev_info(dev, "Wait for device to enter SBL or Mission mode\n");
return;
error_ready_state:
if (mhi_cntrl->fbc_image) {
mhi_free_bhie_table(mhi_cntrl, mhi_cntrl->fbc_image);
mhi_cntrl->fbc_image = NULL;
}
error_fw_load:
write_lock_irq(&mhi_cntrl->pm_lock);
new_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_FW_DL_ERR);
write_unlock_irq(&mhi_cntrl->pm_lock);
if (new_state == MHI_PM_FW_DL_ERR)
wake_up_all(&mhi_cntrl->state_event);
}
int mhi_download_amss_image(struct mhi_controller *mhi_cntrl)
{
struct image_info *image_info = mhi_cntrl->fbc_image;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
enum mhi_pm_state new_state;
int ret;
if (!image_info)
return -EIO;
ret = mhi_fw_load_bhie(mhi_cntrl,
/* Vector table is the last entry */
&image_info->mhi_buf[image_info->entries - 1]);
if (ret) {
dev_err(dev, "MHI did not load AMSS, ret:%d\n", ret);
write_lock_irq(&mhi_cntrl->pm_lock);
new_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_FW_DL_ERR);
write_unlock_irq(&mhi_cntrl->pm_lock);
if (new_state == MHI_PM_FW_DL_ERR)
wake_up_all(&mhi_cntrl->state_event);
}
return ret;
}
+414
View File
@@ -0,0 +1,414 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*
*/
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/mhi.h>
#include <linux/module.h>
#include <linux/string_choices.h>
#include "internal.h"
static int mhi_debugfs_states_show(struct seq_file *m, void *d)
{
struct mhi_controller *mhi_cntrl = m->private;
/* states */
seq_printf(m, "PM state: %s Device: %s MHI state: %s EE: %s wake: %s\n",
to_mhi_pm_state_str(mhi_cntrl->pm_state),
mhi_is_active(mhi_cntrl) ? "Active" : "Inactive",
mhi_state_str(mhi_cntrl->dev_state),
TO_MHI_EXEC_STR(mhi_cntrl->ee),
str_true_false(mhi_cntrl->wake_set));
/* counters */
seq_printf(m, "M0: %u M2: %u M3: %u", mhi_cntrl->M0, mhi_cntrl->M2,
mhi_cntrl->M3);
seq_printf(m, " device wake: %u pending packets: %u\n",
atomic_read(&mhi_cntrl->dev_wake),
atomic_read(&mhi_cntrl->pending_pkts));
return 0;
}
static int mhi_debugfs_events_show(struct seq_file *m, void *d)
{
struct mhi_controller *mhi_cntrl = m->private;
struct mhi_event *mhi_event;
struct mhi_event_ctxt *er_ctxt;
int i;
if (!mhi_is_active(mhi_cntrl)) {
seq_puts(m, "Device not ready\n");
return -ENODEV;
}
er_ctxt = mhi_cntrl->mhi_ctxt->er_ctxt;
mhi_event = mhi_cntrl->mhi_event;
for (i = 0; i < mhi_cntrl->total_ev_rings;
i++, er_ctxt++, mhi_event++) {
struct mhi_ring *ring = &mhi_event->ring;
if (mhi_event->offload_ev) {
seq_printf(m, "Index: %d is an offload event ring\n",
i);
continue;
}
seq_printf(m, "Index: %d intmod count: %lu time: %lu",
i, (le32_to_cpu(er_ctxt->intmod) & EV_CTX_INTMODC_MASK) >>
__ffs(EV_CTX_INTMODC_MASK),
(le32_to_cpu(er_ctxt->intmod) & EV_CTX_INTMODT_MASK) >>
__ffs(EV_CTX_INTMODT_MASK));
seq_printf(m, " base: 0x%0llx len: 0x%llx", le64_to_cpu(er_ctxt->rbase),
le64_to_cpu(er_ctxt->rlen));
seq_printf(m, " rp: 0x%llx wp: 0x%llx", le64_to_cpu(er_ctxt->rp),
le64_to_cpu(er_ctxt->wp));
seq_printf(m, " local rp: 0x%pK db: 0x%pad\n", ring->rp,
&mhi_event->db_cfg.db_val);
}
return 0;
}
static int mhi_debugfs_channels_show(struct seq_file *m, void *d)
{
struct mhi_controller *mhi_cntrl = m->private;
struct mhi_chan *mhi_chan;
struct mhi_chan_ctxt *chan_ctxt;
int i;
if (!mhi_is_active(mhi_cntrl)) {
seq_puts(m, "Device not ready\n");
return -ENODEV;
}
mhi_chan = mhi_cntrl->mhi_chan;
chan_ctxt = mhi_cntrl->mhi_ctxt->chan_ctxt;
for (i = 0; i < mhi_cntrl->max_chan; i++, chan_ctxt++, mhi_chan++) {
struct mhi_ring *ring = &mhi_chan->tre_ring;
if (mhi_chan->offload_ch) {
seq_printf(m, "%s(%u) is an offload channel\n",
mhi_chan->name, mhi_chan->chan);
continue;
}
if (!mhi_chan->mhi_dev)
continue;
seq_printf(m,
"%s(%u) state: 0x%lx brstmode: 0x%lx pollcfg: 0x%lx",
mhi_chan->name, mhi_chan->chan, (le32_to_cpu(chan_ctxt->chcfg) &
CHAN_CTX_CHSTATE_MASK) >> __ffs(CHAN_CTX_CHSTATE_MASK),
(le32_to_cpu(chan_ctxt->chcfg) & CHAN_CTX_BRSTMODE_MASK) >>
__ffs(CHAN_CTX_BRSTMODE_MASK), (le32_to_cpu(chan_ctxt->chcfg) &
CHAN_CTX_POLLCFG_MASK) >> __ffs(CHAN_CTX_POLLCFG_MASK));
seq_printf(m, " type: 0x%x event ring: %u", le32_to_cpu(chan_ctxt->chtype),
le32_to_cpu(chan_ctxt->erindex));
seq_printf(m, " base: 0x%llx len: 0x%llx rp: 0x%llx wp: 0x%llx",
le64_to_cpu(chan_ctxt->rbase), le64_to_cpu(chan_ctxt->rlen),
le64_to_cpu(chan_ctxt->rp), le64_to_cpu(chan_ctxt->wp));
seq_printf(m, " local rp: 0x%pK local wp: 0x%pK db: 0x%pad\n",
ring->rp, ring->wp,
&mhi_chan->db_cfg.db_val);
}
return 0;
}
static int mhi_device_info_show(struct device *dev, void *data)
{
struct mhi_device *mhi_dev;
if (dev->bus != &mhi_bus_type)
return 0;
mhi_dev = to_mhi_device(dev);
seq_printf((struct seq_file *)data, "%s: type: %s dev_wake: %u",
mhi_dev->name, mhi_dev->dev_type ? "Controller" : "Transfer",
mhi_dev->dev_wake);
/* for transfer device types only */
if (mhi_dev->dev_type == MHI_DEVICE_XFER)
seq_printf((struct seq_file *)data, " channels: %u(UL)/%u(DL)",
mhi_dev->ul_chan_id, mhi_dev->dl_chan_id);
seq_puts((struct seq_file *)data, "\n");
return 0;
}
static int mhi_debugfs_devices_show(struct seq_file *m, void *d)
{
struct mhi_controller *mhi_cntrl = m->private;
if (!mhi_is_active(mhi_cntrl)) {
seq_puts(m, "Device not ready\n");
return -ENODEV;
}
/* Show controller and client(s) info */
mhi_device_info_show(&mhi_cntrl->mhi_dev->dev, m);
device_for_each_child(&mhi_cntrl->mhi_dev->dev, m, mhi_device_info_show);
return 0;
}
static int mhi_debugfs_regdump_show(struct seq_file *m, void *d)
{
struct mhi_controller *mhi_cntrl = m->private;
enum mhi_state state;
enum mhi_ee_type ee;
int i, ret = -EIO;
u32 val;
void __iomem *mhi_base = mhi_cntrl->regs;
void __iomem *bhi_base = mhi_cntrl->bhi;
void __iomem *bhie_base = mhi_cntrl->bhie;
void __iomem *wake_db = mhi_cntrl->wake_db;
struct {
const char *name;
int offset;
void __iomem *base;
} regs[] = {
{ "MHI_REGLEN", MHIREGLEN, mhi_base},
{ "MHI_VER", MHIVER, mhi_base},
{ "MHI_CFG", MHICFG, mhi_base},
{ "MHI_CTRL", MHICTRL, mhi_base},
{ "MHI_STATUS", MHISTATUS, mhi_base},
{ "MHI_WAKE_DB", 0, wake_db},
{ "BHI_EXECENV", BHI_EXECENV, bhi_base},
{ "BHI_STATUS", BHI_STATUS, bhi_base},
{ "BHI_ERRCODE", BHI_ERRCODE, bhi_base},
{ "BHI_ERRDBG1", BHI_ERRDBG1, bhi_base},
{ "BHI_ERRDBG2", BHI_ERRDBG2, bhi_base},
{ "BHI_ERRDBG3", BHI_ERRDBG3, bhi_base},
{ "BHIE_TXVEC_DB", BHIE_TXVECDB_OFFS, bhie_base},
{ "BHIE_TXVEC_STATUS", BHIE_TXVECSTATUS_OFFS, bhie_base},
{ "BHIE_RXVEC_DB", BHIE_RXVECDB_OFFS, bhie_base},
{ "BHIE_RXVEC_STATUS", BHIE_RXVECSTATUS_OFFS, bhie_base},
{ NULL },
};
if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state))
return ret;
seq_printf(m, "Host PM state: %s Device state: %s EE: %s\n",
to_mhi_pm_state_str(mhi_cntrl->pm_state),
mhi_state_str(mhi_cntrl->dev_state),
TO_MHI_EXEC_STR(mhi_cntrl->ee));
state = mhi_get_mhi_state(mhi_cntrl);
ee = mhi_get_exec_env(mhi_cntrl);
seq_printf(m, "Device EE: %s state: %s\n", TO_MHI_EXEC_STR(ee),
mhi_state_str(state));
for (i = 0; regs[i].name; i++) {
if (!regs[i].base)
continue;
ret = mhi_read_reg(mhi_cntrl, regs[i].base, regs[i].offset,
&val);
if (ret)
continue;
seq_printf(m, "%s: 0x%x\n", regs[i].name, val);
}
return 0;
}
static int mhi_debugfs_device_wake_show(struct seq_file *m, void *d)
{
struct mhi_controller *mhi_cntrl = m->private;
struct mhi_device *mhi_dev = mhi_cntrl->mhi_dev;
if (!mhi_is_active(mhi_cntrl)) {
seq_puts(m, "Device not ready\n");
return -ENODEV;
}
seq_printf(m,
"Wake count: %d\n%s\n", mhi_dev->dev_wake,
"Usage: echo get/put > device_wake to vote/unvote for M0");
return 0;
}
static ssize_t mhi_debugfs_device_wake_write(struct file *file,
const char __user *ubuf,
size_t count, loff_t *ppos)
{
struct seq_file *m = file->private_data;
struct mhi_controller *mhi_cntrl = m->private;
struct mhi_device *mhi_dev = mhi_cntrl->mhi_dev;
char buf[16];
int ret = -EINVAL;
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
if (!strncmp(buf, "get", 3)) {
ret = mhi_device_get_sync(mhi_dev);
} else if (!strncmp(buf, "put", 3)) {
mhi_device_put(mhi_dev);
ret = 0;
}
return ret ? ret : count;
}
static int mhi_debugfs_timeout_ms_show(struct seq_file *m, void *d)
{
struct mhi_controller *mhi_cntrl = m->private;
seq_printf(m, "%u ms\n", mhi_cntrl->timeout_ms);
return 0;
}
static ssize_t mhi_debugfs_timeout_ms_write(struct file *file,
const char __user *ubuf,
size_t count, loff_t *ppos)
{
struct seq_file *m = file->private_data;
struct mhi_controller *mhi_cntrl = m->private;
u32 timeout_ms;
if (kstrtou32_from_user(ubuf, count, 0, &timeout_ms))
return -EINVAL;
mhi_cntrl->timeout_ms = timeout_ms;
return count;
}
static int mhi_debugfs_states_open(struct inode *inode, struct file *fp)
{
return single_open(fp, mhi_debugfs_states_show, inode->i_private);
}
static int mhi_debugfs_events_open(struct inode *inode, struct file *fp)
{
return single_open(fp, mhi_debugfs_events_show, inode->i_private);
}
static int mhi_debugfs_channels_open(struct inode *inode, struct file *fp)
{
return single_open(fp, mhi_debugfs_channels_show, inode->i_private);
}
static int mhi_debugfs_devices_open(struct inode *inode, struct file *fp)
{
return single_open(fp, mhi_debugfs_devices_show, inode->i_private);
}
static int mhi_debugfs_regdump_open(struct inode *inode, struct file *fp)
{
return single_open(fp, mhi_debugfs_regdump_show, inode->i_private);
}
static int mhi_debugfs_device_wake_open(struct inode *inode, struct file *fp)
{
return single_open(fp, mhi_debugfs_device_wake_show, inode->i_private);
}
static int mhi_debugfs_timeout_ms_open(struct inode *inode, struct file *fp)
{
return single_open(fp, mhi_debugfs_timeout_ms_show, inode->i_private);
}
static const struct file_operations debugfs_states_fops = {
.open = mhi_debugfs_states_open,
.release = single_release,
.read = seq_read,
};
static const struct file_operations debugfs_events_fops = {
.open = mhi_debugfs_events_open,
.release = single_release,
.read = seq_read,
};
static const struct file_operations debugfs_channels_fops = {
.open = mhi_debugfs_channels_open,
.release = single_release,
.read = seq_read,
};
static const struct file_operations debugfs_devices_fops = {
.open = mhi_debugfs_devices_open,
.release = single_release,
.read = seq_read,
};
static const struct file_operations debugfs_regdump_fops = {
.open = mhi_debugfs_regdump_open,
.release = single_release,
.read = seq_read,
};
static const struct file_operations debugfs_device_wake_fops = {
.open = mhi_debugfs_device_wake_open,
.write = mhi_debugfs_device_wake_write,
.release = single_release,
.read = seq_read,
};
static const struct file_operations debugfs_timeout_ms_fops = {
.open = mhi_debugfs_timeout_ms_open,
.write = mhi_debugfs_timeout_ms_write,
.release = single_release,
.read = seq_read,
};
static struct dentry *mhi_debugfs_root;
void mhi_create_debugfs(struct mhi_controller *mhi_cntrl)
{
mhi_cntrl->debugfs_dentry =
debugfs_create_dir(dev_name(&mhi_cntrl->mhi_dev->dev),
mhi_debugfs_root);
debugfs_create_file("states", 0444, mhi_cntrl->debugfs_dentry,
mhi_cntrl, &debugfs_states_fops);
debugfs_create_file("events", 0444, mhi_cntrl->debugfs_dentry,
mhi_cntrl, &debugfs_events_fops);
debugfs_create_file("channels", 0444, mhi_cntrl->debugfs_dentry,
mhi_cntrl, &debugfs_channels_fops);
debugfs_create_file("devices", 0444, mhi_cntrl->debugfs_dentry,
mhi_cntrl, &debugfs_devices_fops);
debugfs_create_file("regdump", 0444, mhi_cntrl->debugfs_dentry,
mhi_cntrl, &debugfs_regdump_fops);
debugfs_create_file("device_wake", 0644, mhi_cntrl->debugfs_dentry,
mhi_cntrl, &debugfs_device_wake_fops);
debugfs_create_file("timeout_ms", 0644, mhi_cntrl->debugfs_dentry,
mhi_cntrl, &debugfs_timeout_ms_fops);
}
void mhi_destroy_debugfs(struct mhi_controller *mhi_cntrl)
{
debugfs_remove_recursive(mhi_cntrl->debugfs_dentry);
mhi_cntrl->debugfs_dentry = NULL;
}
void mhi_debugfs_init(void)
{
mhi_debugfs_root = debugfs_create_dir(mhi_bus_type.name, NULL);
}
void mhi_debugfs_exit(void)
{
debugfs_remove_recursive(mhi_debugfs_root);
}
File diff suppressed because it is too large Load Diff
+423
View File
@@ -0,0 +1,423 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
*
*/
#ifndef _MHI_INT_H
#define _MHI_INT_H
#include "../common.h"
extern const struct bus_type mhi_bus_type;
/* Host request register */
#define MHI_SOC_RESET_REQ_OFFSET 0xb0
#define MHI_SOC_RESET_REQ BIT(0)
struct mhi_ctxt {
struct mhi_event_ctxt *er_ctxt;
struct mhi_chan_ctxt *chan_ctxt;
struct mhi_cmd_ctxt *cmd_ctxt;
dma_addr_t er_ctxt_addr;
dma_addr_t chan_ctxt_addr;
dma_addr_t cmd_ctxt_addr;
};
struct bhi_vec_entry {
__le64 dma_addr;
__le64 size;
};
enum mhi_fw_load_type {
MHI_FW_LOAD_BHI, /* BHI only in PBL */
MHI_FW_LOAD_BHIE, /* BHIe only in PBL */
MHI_FW_LOAD_FBC, /* BHI in PBL followed by BHIe in SBL */
MHI_FW_LOAD_MAX,
};
enum mhi_ch_state_type {
MHI_CH_STATE_TYPE_RESET,
MHI_CH_STATE_TYPE_STOP,
MHI_CH_STATE_TYPE_START,
MHI_CH_STATE_TYPE_MAX,
};
#define MHI_CH_STATE_TYPE_LIST \
ch_state_type(RESET, "RESET") \
ch_state_type(STOP, "STOP") \
ch_state_type_end(START, "START")
extern const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX];
#define TO_CH_STATE_TYPE_STR(state) (((state) >= MHI_CH_STATE_TYPE_MAX) ? \
"INVALID_STATE" : \
mhi_ch_state_type_str[(state)])
#define MHI_INVALID_BRSTMODE(mode) (mode != MHI_DB_BRST_DISABLE && \
mode != MHI_DB_BRST_ENABLE)
#define MHI_EE_LIST \
mhi_ee(PBL, "PRIMARY BOOTLOADER") \
mhi_ee(SBL, "SECONDARY BOOTLOADER") \
mhi_ee(AMSS, "MISSION MODE") \
mhi_ee(RDDM, "RAMDUMP DOWNLOAD MODE")\
mhi_ee(WFW, "WLAN FIRMWARE") \
mhi_ee(PTHRU, "PASS THROUGH") \
mhi_ee(EDL, "EMERGENCY DOWNLOAD") \
mhi_ee(FP, "FLASH PROGRAMMER") \
mhi_ee(DISABLE_TRANSITION, "DISABLE") \
mhi_ee_end(NOT_SUPPORTED, "NOT SUPPORTED")
extern const char * const mhi_ee_str[MHI_EE_MAX];
#define TO_MHI_EXEC_STR(ee) (((ee) >= MHI_EE_MAX) ? \
"INVALID_EE" : mhi_ee_str[ee])
#define MHI_IN_PBL(ee) (ee == MHI_EE_PBL || ee == MHI_EE_PTHRU || \
ee == MHI_EE_EDL)
#define MHI_POWER_UP_CAPABLE(ee) (MHI_IN_PBL(ee) || ee == MHI_EE_AMSS)
#define MHI_FW_LOAD_CAPABLE(ee) (ee == MHI_EE_PBL || ee == MHI_EE_EDL)
#define MHI_IN_MISSION_MODE(ee) (ee == MHI_EE_AMSS || ee == MHI_EE_WFW || \
ee == MHI_EE_FP)
enum dev_st_transition {
DEV_ST_TRANSITION_PBL,
DEV_ST_TRANSITION_READY,
DEV_ST_TRANSITION_SBL,
DEV_ST_TRANSITION_MISSION_MODE,
DEV_ST_TRANSITION_FP,
DEV_ST_TRANSITION_SYS_ERR,
DEV_ST_TRANSITION_DISABLE,
DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE,
DEV_ST_TRANSITION_MAX,
};
#define DEV_ST_TRANSITION_LIST \
dev_st_trans(PBL, "PBL") \
dev_st_trans(READY, "READY") \
dev_st_trans(SBL, "SBL") \
dev_st_trans(MISSION_MODE, "MISSION MODE") \
dev_st_trans(FP, "FLASH PROGRAMMER") \
dev_st_trans(SYS_ERR, "SYS ERROR") \
dev_st_trans(DISABLE, "DISABLE") \
dev_st_trans_end(DISABLE_DESTROY_DEVICE, "DISABLE (DESTROY DEVICE)")
extern const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX];
#define TO_DEV_STATE_TRANS_STR(state) (((state) >= DEV_ST_TRANSITION_MAX) ? \
"INVALID_STATE" : dev_state_tran_str[state])
/* internal power states */
enum mhi_pm_state {
MHI_PM_STATE_DISABLE,
MHI_PM_STATE_POR,
MHI_PM_STATE_M0,
MHI_PM_STATE_M2,
MHI_PM_STATE_M3_ENTER,
MHI_PM_STATE_M3,
MHI_PM_STATE_M3_EXIT,
MHI_PM_STATE_FW_DL_ERR,
MHI_PM_STATE_SYS_ERR_DETECT,
MHI_PM_STATE_SYS_ERR_PROCESS,
MHI_PM_STATE_SYS_ERR_FAIL,
MHI_PM_STATE_SHUTDOWN_PROCESS,
MHI_PM_STATE_LD_ERR_FATAL_DETECT,
MHI_PM_STATE_MAX
};
#define MHI_PM_STATE_LIST \
mhi_pm_state(DISABLE, "DISABLE") \
mhi_pm_state(POR, "POWER ON RESET") \
mhi_pm_state(M0, "M0") \
mhi_pm_state(M2, "M2") \
mhi_pm_state(M3_ENTER, "M?->M3") \
mhi_pm_state(M3, "M3") \
mhi_pm_state(M3_EXIT, "M3->M0") \
mhi_pm_state(FW_DL_ERR, "Firmware Download Error") \
mhi_pm_state(SYS_ERR_DETECT, "SYS ERROR Detect") \
mhi_pm_state(SYS_ERR_PROCESS, "SYS ERROR Process") \
mhi_pm_state(SYS_ERR_FAIL, "SYS ERROR Failure") \
mhi_pm_state(SHUTDOWN_PROCESS, "SHUTDOWN Process") \
mhi_pm_state_end(LD_ERR_FATAL_DETECT, "Linkdown or Error Fatal Detect")
#define MHI_PM_DISABLE BIT(0)
#define MHI_PM_POR BIT(1)
#define MHI_PM_M0 BIT(2)
#define MHI_PM_M2 BIT(3)
#define MHI_PM_M3_ENTER BIT(4)
#define MHI_PM_M3 BIT(5)
#define MHI_PM_M3_EXIT BIT(6)
/* firmware download failure state */
#define MHI_PM_FW_DL_ERR BIT(7)
#define MHI_PM_SYS_ERR_DETECT BIT(8)
#define MHI_PM_SYS_ERR_PROCESS BIT(9)
#define MHI_PM_SYS_ERR_FAIL BIT(10)
#define MHI_PM_SHUTDOWN_PROCESS BIT(11)
/* link not accessible */
#define MHI_PM_LD_ERR_FATAL_DETECT BIT(12)
#define MHI_REG_ACCESS_VALID(pm_state) ((pm_state & (MHI_PM_POR | MHI_PM_M0 | \
MHI_PM_M2 | MHI_PM_M3_ENTER | MHI_PM_M3_EXIT | \
MHI_PM_SYS_ERR_DETECT | MHI_PM_SYS_ERR_PROCESS | \
MHI_PM_SYS_ERR_FAIL | MHI_PM_SHUTDOWN_PROCESS | \
MHI_PM_FW_DL_ERR)))
#define MHI_PM_IN_ERROR_STATE(pm_state) (pm_state >= MHI_PM_FW_DL_ERR)
#define MHI_PM_IN_FATAL_STATE(pm_state) (pm_state == MHI_PM_LD_ERR_FATAL_DETECT)
#define MHI_DB_ACCESS_VALID(mhi_cntrl) (mhi_cntrl->pm_state & mhi_cntrl->db_access)
#define MHI_WAKE_DB_CLEAR_VALID(pm_state) (pm_state & (MHI_PM_M0 | \
MHI_PM_M2 | MHI_PM_M3_EXIT))
#define MHI_WAKE_DB_SET_VALID(pm_state) (pm_state & MHI_PM_M2)
#define MHI_WAKE_DB_FORCE_SET_VALID(pm_state) MHI_WAKE_DB_CLEAR_VALID(pm_state)
#define MHI_EVENT_ACCESS_INVALID(pm_state) (pm_state == MHI_PM_DISABLE || \
MHI_PM_IN_ERROR_STATE(pm_state))
#define MHI_PM_IN_SUSPEND_STATE(pm_state) (pm_state & \
(MHI_PM_M3_ENTER | MHI_PM_M3))
#define MHI_PM_FATAL_ERROR(pm_state) ((pm_state == MHI_PM_FW_DL_ERR) || \
(pm_state >= MHI_PM_SYS_ERR_FAIL))
#define NR_OF_CMD_RINGS 1
#define CMD_EL_PER_RING 128
#define PRIMARY_CMD_RING 0
#define MHI_DEV_WAKE_DB 127
#define MHI_MAX_MTU 0xffff
#define MHI_RANDOM_U32_NONZERO(bmsk) (get_random_u32_inclusive(1, bmsk))
enum mhi_er_type {
MHI_ER_TYPE_INVALID = 0x0,
MHI_ER_TYPE_VALID = 0x1,
};
struct db_cfg {
bool reset_req;
bool db_mode;
u32 pollcfg;
enum mhi_db_brst_mode brstmode;
dma_addr_t db_val;
void (*process_db)(struct mhi_controller *mhi_cntrl,
struct db_cfg *db_cfg, void __iomem *io_addr,
dma_addr_t db_val);
};
struct mhi_pm_transitions {
enum mhi_pm_state from_state;
u32 to_states;
};
struct state_transition {
struct list_head node;
enum dev_st_transition state;
};
struct mhi_ring {
dma_addr_t dma_handle;
dma_addr_t iommu_base;
__le64 *ctxt_wp; /* point to ctxt wp */
void *pre_aligned;
void *base;
void *rp;
void *wp;
size_t el_size;
size_t len;
size_t elements;
size_t alloc_size;
void __iomem *db_addr;
};
struct mhi_cmd {
struct mhi_ring ring;
spinlock_t lock;
};
struct mhi_buf_info {
void *v_addr;
void *bb_addr;
void *wp;
void *cb_buf;
dma_addr_t p_addr;
size_t len;
enum dma_data_direction dir;
bool used; /* Indicates whether the buffer is used or not */
bool pre_mapped; /* Already pre-mapped by client */
};
struct mhi_event {
struct mhi_controller *mhi_cntrl;
struct mhi_chan *mhi_chan; /* dedicated to channel */
u32 er_index;
u32 intmod;
u32 irq;
int chan; /* this event ring is dedicated to a channel (optional) */
u32 priority;
enum mhi_er_data_type data_type;
struct mhi_ring ring;
struct db_cfg db_cfg;
struct tasklet_struct task;
spinlock_t lock;
int (*process_event)(struct mhi_controller *mhi_cntrl,
struct mhi_event *mhi_event,
u32 event_quota);
bool hw_ring;
bool cl_manage;
bool offload_ev; /* managed by a device driver */
};
struct mhi_chan {
const char *name;
/*
* Important: When consuming, increment tre_ring first and when
* releasing, decrement buf_ring first. If tre_ring has space, buf_ring
* is guaranteed to have space so we do not need to check both rings.
*/
struct mhi_ring buf_ring;
struct mhi_ring tre_ring;
u32 chan;
u32 er_index;
u32 intmod;
enum mhi_ch_type type;
enum dma_data_direction dir;
struct db_cfg db_cfg;
enum mhi_ch_ee_mask ee_mask;
enum mhi_ch_state ch_state;
enum mhi_ev_ccs ccs;
struct mhi_device *mhi_dev;
void (*xfer_cb)(struct mhi_device *mhi_dev, struct mhi_result *result);
struct mutex mutex;
struct completion completion;
rwlock_t lock;
struct list_head node;
bool lpm_notify;
bool configured;
bool offload_ch;
bool wake_capable;
};
/* Default MHI timeout */
#define MHI_TIMEOUT_MS (1000)
/* debugfs related functions */
#ifdef CONFIG_MHI_BUS_DEBUG
void mhi_create_debugfs(struct mhi_controller *mhi_cntrl);
void mhi_destroy_debugfs(struct mhi_controller *mhi_cntrl);
void mhi_debugfs_init(void);
void mhi_debugfs_exit(void);
#else
static inline void mhi_create_debugfs(struct mhi_controller *mhi_cntrl)
{
}
static inline void mhi_destroy_debugfs(struct mhi_controller *mhi_cntrl)
{
}
static inline void mhi_debugfs_init(void)
{
}
static inline void mhi_debugfs_exit(void)
{
}
#endif
struct mhi_device *mhi_alloc_device(struct mhi_controller *mhi_cntrl);
int mhi_destroy_device(struct device *dev, void *data);
void mhi_create_devices(struct mhi_controller *mhi_cntrl);
int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl,
struct image_info **image_info, size_t alloc_size);
void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl,
struct image_info *image_info);
/* Power management APIs */
enum mhi_pm_state __must_check mhi_tryset_pm_state(
struct mhi_controller *mhi_cntrl,
enum mhi_pm_state state);
const char *to_mhi_pm_state_str(u32 state);
int mhi_queue_state_transition(struct mhi_controller *mhi_cntrl,
enum dev_st_transition state);
void mhi_pm_st_worker(struct work_struct *work);
void mhi_pm_sys_err_handler(struct mhi_controller *mhi_cntrl);
int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl);
int mhi_pm_m0_transition(struct mhi_controller *mhi_cntrl);
void mhi_pm_m1_transition(struct mhi_controller *mhi_cntrl);
int mhi_pm_m3_transition(struct mhi_controller *mhi_cntrl);
int __mhi_device_get_sync(struct mhi_controller *mhi_cntrl);
int mhi_send_cmd(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
enum mhi_cmd_type cmd);
int mhi_download_amss_image(struct mhi_controller *mhi_cntrl);
static inline bool mhi_is_active(struct mhi_controller *mhi_cntrl)
{
return (mhi_cntrl->dev_state >= MHI_STATE_M0 &&
mhi_cntrl->dev_state <= MHI_STATE_M3_FAST);
}
static inline void mhi_trigger_resume(struct mhi_controller *mhi_cntrl)
{
pm_wakeup_event(&mhi_cntrl->mhi_dev->dev, 0);
mhi_cntrl->runtime_get(mhi_cntrl);
mhi_cntrl->runtime_put(mhi_cntrl);
}
/* Register access methods */
void mhi_db_brstmode(struct mhi_controller *mhi_cntrl, struct db_cfg *db_cfg,
void __iomem *db_addr, dma_addr_t db_val);
void mhi_db_brstmode_disable(struct mhi_controller *mhi_cntrl,
struct db_cfg *db_mode, void __iomem *db_addr,
dma_addr_t db_val);
int __must_check mhi_read_reg(struct mhi_controller *mhi_cntrl,
void __iomem *base, u32 offset, u32 *out);
int __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl,
void __iomem *base, u32 offset, u32 mask,
u32 *out);
int __must_check mhi_poll_reg_field(struct mhi_controller *mhi_cntrl,
void __iomem *base, u32 offset, u32 mask,
u32 val, u32 delayus, u32 timeout_ms);
void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base,
u32 offset, u32 val);
int __must_check mhi_write_reg_field(struct mhi_controller *mhi_cntrl,
void __iomem *base, u32 offset, u32 mask,
u32 val);
void mhi_ring_er_db(struct mhi_event *mhi_event);
void mhi_write_db(struct mhi_controller *mhi_cntrl, void __iomem *db_addr,
dma_addr_t db_val);
void mhi_ring_cmd_db(struct mhi_controller *mhi_cntrl, struct mhi_cmd *mhi_cmd);
void mhi_ring_chan_db(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan);
/* Initialization methods */
int mhi_init_mmio(struct mhi_controller *mhi_cntrl);
int mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
struct image_info *img_info);
void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl);
int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan);
void mhi_deinit_chan_ctxt(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan);
void mhi_reset_chan(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan);
/* Event processing methods */
void mhi_ctrl_ev_task(unsigned long data);
void mhi_ev_task(unsigned long data);
int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl,
struct mhi_event *mhi_event, u32 event_quota);
int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
struct mhi_event *mhi_event, u32 event_quota);
void mhi_uevent_notify(struct mhi_controller *mhi_cntrl, enum mhi_ee_type ee);
/* ISR handlers */
irqreturn_t mhi_irq_handler(int irq_number, void *dev);
irqreturn_t mhi_intvec_threaded_handler(int irq_number, void *dev);
irqreturn_t mhi_intvec_handler(int irq_number, void *dev);
int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
struct mhi_buf_info *info, enum mhi_flags flags);
int mhi_map_single_no_bb(struct mhi_controller *mhi_cntrl,
struct mhi_buf_info *buf_info);
int mhi_map_single_use_bb(struct mhi_controller *mhi_cntrl,
struct mhi_buf_info *buf_info);
void mhi_unmap_single_no_bb(struct mhi_controller *mhi_cntrl,
struct mhi_buf_info *buf_info);
void mhi_unmap_single_use_bb(struct mhi_controller *mhi_cntrl,
struct mhi_buf_info *buf_info);
#endif /* _MHI_INT_H */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+283
View File
@@ -0,0 +1,283 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM mhi_host
#if !defined(_TRACE_EVENT_MHI_HOST_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_EVENT_MHI_HOST_H
#include <linux/byteorder/generic.h>
#include <linux/tracepoint.h>
#include <linux/trace_seq.h>
#include "../common.h"
#include "internal.h"
#undef mhi_state
#undef mhi_state_end
#define mhi_state(a, b) TRACE_DEFINE_ENUM(MHI_STATE_##a);
#define mhi_state_end(a, b) TRACE_DEFINE_ENUM(MHI_STATE_##a);
MHI_STATE_LIST
#undef mhi_state
#undef mhi_state_end
#define mhi_state(a, b) { MHI_STATE_##a, b },
#define mhi_state_end(a, b) { MHI_STATE_##a, b }
#undef mhi_pm_state
#undef mhi_pm_state_end
#define mhi_pm_state(a, b) TRACE_DEFINE_ENUM(MHI_PM_STATE_##a);
#define mhi_pm_state_end(a, b) TRACE_DEFINE_ENUM(MHI_PM_STATE_##a);
MHI_PM_STATE_LIST
#undef mhi_pm_state
#undef mhi_pm_state_end
#define mhi_pm_state(a, b) { MHI_PM_STATE_##a, b },
#define mhi_pm_state_end(a, b) { MHI_PM_STATE_##a, b }
#undef mhi_ee
#undef mhi_ee_end
#define mhi_ee(a, b) TRACE_DEFINE_ENUM(MHI_EE_##a);
#define mhi_ee_end(a, b) TRACE_DEFINE_ENUM(MHI_EE_##a);
MHI_EE_LIST
#undef mhi_ee
#undef mhi_ee_end
#define mhi_ee(a, b) { MHI_EE_##a, b },
#define mhi_ee_end(a, b) { MHI_EE_##a, b }
#undef ch_state_type
#undef ch_state_type_end
#define ch_state_type(a, b) TRACE_DEFINE_ENUM(MHI_CH_STATE_TYPE_##a);
#define ch_state_type_end(a, b) TRACE_DEFINE_ENUM(MHI_CH_STATE_TYPE_##a);
MHI_CH_STATE_TYPE_LIST
#undef ch_state_type
#undef ch_state_type_end
#define ch_state_type(a, b) { MHI_CH_STATE_TYPE_##a, b },
#define ch_state_type_end(a, b) { MHI_CH_STATE_TYPE_##a, b }
#undef dev_st_trans
#undef dev_st_trans_end
#define dev_st_trans(a, b) TRACE_DEFINE_ENUM(DEV_ST_TRANSITION_##a);
#define dev_st_trans_end(a, b) TRACE_DEFINE_ENUM(DEV_ST_TRANSITION_##a);
DEV_ST_TRANSITION_LIST
#undef dev_st_trans
#undef dev_st_trans_end
#define dev_st_trans(a, b) { DEV_ST_TRANSITION_##a, b },
#define dev_st_trans_end(a, b) { DEV_ST_TRANSITION_##a, b }
#define TPS(x) tracepoint_string(x)
TRACE_EVENT(mhi_gen_tre,
TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
struct mhi_ring_element *mhi_tre),
TP_ARGS(mhi_cntrl, mhi_chan, mhi_tre),
TP_STRUCT__entry(
__string(name, mhi_cntrl->mhi_dev->name)
__field(int, ch_num)
__field(void *, wp)
__field(uint64_t, tre_ptr)
__field(uint32_t, dword0)
__field(uint32_t, dword1)
),
TP_fast_assign(
__assign_str(name);
__entry->ch_num = mhi_chan->chan;
__entry->wp = mhi_tre;
__entry->tre_ptr = le64_to_cpu(mhi_tre->ptr);
__entry->dword0 = le32_to_cpu(mhi_tre->dword[0]);
__entry->dword1 = le32_to_cpu(mhi_tre->dword[1]);
),
TP_printk("%s: Chan: %d TRE: 0x%p TRE buf: 0x%llx DWORD0: 0x%08x DWORD1: 0x%08x\n",
__get_str(name), __entry->ch_num, __entry->wp, __entry->tre_ptr,
__entry->dword0, __entry->dword1)
);
TRACE_EVENT(mhi_intvec_states,
TP_PROTO(struct mhi_controller *mhi_cntrl, int dev_ee, int dev_state),
TP_ARGS(mhi_cntrl, dev_ee, dev_state),
TP_STRUCT__entry(
__string(name, mhi_cntrl->mhi_dev->name)
__field(int, local_ee)
__field(int, state)
__field(int, dev_ee)
__field(int, dev_state)
),
TP_fast_assign(
__assign_str(name);
__entry->local_ee = mhi_cntrl->ee;
__entry->state = mhi_cntrl->dev_state;
__entry->dev_ee = dev_ee;
__entry->dev_state = dev_state;
),
TP_printk("%s: Local EE: %s State: %s Device EE: %s Dev State: %s\n",
__get_str(name),
__print_symbolic(__entry->local_ee, MHI_EE_LIST),
__print_symbolic(__entry->state, MHI_STATE_LIST),
__print_symbolic(__entry->dev_ee, MHI_EE_LIST),
__print_symbolic(__entry->dev_state, MHI_STATE_LIST))
);
TRACE_EVENT(mhi_tryset_pm_state,
TP_PROTO(struct mhi_controller *mhi_cntrl, int pm_state),
TP_ARGS(mhi_cntrl, pm_state),
TP_STRUCT__entry(
__string(name, mhi_cntrl->mhi_dev->name)
__field(int, pm_state)
),
TP_fast_assign(
__assign_str(name);
if (pm_state)
pm_state = __fls(pm_state);
__entry->pm_state = pm_state;
),
TP_printk("%s: PM state: %s\n", __get_str(name),
__print_symbolic(__entry->pm_state, MHI_PM_STATE_LIST))
);
DECLARE_EVENT_CLASS(mhi_process_event_ring,
TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_ring_element *rp),
TP_ARGS(mhi_cntrl, rp),
TP_STRUCT__entry(
__string(name, mhi_cntrl->mhi_dev->name)
__field(uint32_t, dword0)
__field(uint32_t, dword1)
__field(int, state)
__field(uint64_t, ptr)
__field(void *, rp)
),
TP_fast_assign(
__assign_str(name);
__entry->rp = rp;
__entry->ptr = le64_to_cpu(rp->ptr);
__entry->dword0 = le32_to_cpu(rp->dword[0]);
__entry->dword1 = le32_to_cpu(rp->dword[1]);
__entry->state = MHI_TRE_GET_EV_STATE(rp);
),
TP_printk("%s: TRE: 0x%p TRE buf: 0x%llx DWORD0: 0x%08x DWORD1: 0x%08x State: %s\n",
__get_str(name), __entry->rp, __entry->ptr, __entry->dword0,
__entry->dword1, __print_symbolic(__entry->state, MHI_STATE_LIST))
);
DEFINE_EVENT(mhi_process_event_ring, mhi_data_event,
TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_ring_element *rp),
TP_ARGS(mhi_cntrl, rp)
);
DEFINE_EVENT(mhi_process_event_ring, mhi_ctrl_event,
TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_ring_element *rp),
TP_ARGS(mhi_cntrl, rp)
);
DECLARE_EVENT_CLASS(mhi_update_channel_state,
TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, int state,
const char *reason),
TP_ARGS(mhi_cntrl, mhi_chan, state, reason),
TP_STRUCT__entry(
__string(name, mhi_cntrl->mhi_dev->name)
__field(int, ch_num)
__field(int, state)
__field(const char *, reason)
),
TP_fast_assign(
__assign_str(name);
__entry->ch_num = mhi_chan->chan;
__entry->state = state;
__entry->reason = reason;
),
TP_printk("%s: chan%d: %s state to: %s\n",
__get_str(name), __entry->ch_num, __entry->reason,
__print_symbolic(__entry->state, MHI_CH_STATE_TYPE_LIST))
);
DEFINE_EVENT(mhi_update_channel_state, mhi_channel_command_start,
TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, int state,
const char *reason),
TP_ARGS(mhi_cntrl, mhi_chan, state, reason)
);
DEFINE_EVENT(mhi_update_channel_state, mhi_channel_command_end,
TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, int state,
const char *reason),
TP_ARGS(mhi_cntrl, mhi_chan, state, reason)
);
TRACE_EVENT(mhi_pm_st_transition,
TP_PROTO(struct mhi_controller *mhi_cntrl, int state),
TP_ARGS(mhi_cntrl, state),
TP_STRUCT__entry(
__string(name, mhi_cntrl->mhi_dev->name)
__field(int, state)
),
TP_fast_assign(
__assign_str(name);
__entry->state = state;
),
TP_printk("%s: Handling state transition: %s\n", __get_str(name),
__print_symbolic(__entry->state, DEV_ST_TRANSITION_LIST))
);
#endif
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH ../../drivers/bus/mhi/host
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace
#include <trace/define_trace.h>