root/drivers/accel/qaic/qaic_ras.c
// SPDX-License-Identifier: GPL-2.0-only

/* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */
/* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */
/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */

#include <asm/byteorder.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/mhi.h>

#include "qaic.h"
#include "qaic_ras.h"

#define MAGIC           0x55AA
#define VERSION         0x2
#define HDR_SZ          12
#define NUM_TEMP_LVL    3
#define POWER_BREAK     BIT(0)

enum msg_type {
        MSG_PUSH, /* async push from device */
        MSG_REQ,  /* sync request to device */
        MSG_RESP, /* sync response from device */
};

enum err_type {
        CE,     /* correctable error */
        UE,     /* uncorrectable error */
        UE_NF,  /* uncorrectable error that is non-fatal, expect a disruption */
        ERR_TYPE_MAX,
};

static const char * const err_type_str[] = {
        [CE]    = "Correctable",
        [UE]    = "Uncorrectable",
        [UE_NF] = "Uncorrectable Non-Fatal",
};

static const char * const err_class_str[] = {
        [CE]    = "Warning",
        [UE]    = "Fatal",
        [UE_NF] = "Warning",
};

enum err_source {
        SOC_MEM,
        PCIE,
        DDR,
        SYS_BUS1,
        SYS_BUS2,
        NSP_MEM,
        TSENS,
};

static const char * const err_src_str[TSENS + 1] = {
        [SOC_MEM]       = "SoC Memory",
        [PCIE]          = "PCIE",
        [DDR]           = "DDR",
        [SYS_BUS1]      = "System Bus source 1",
        [SYS_BUS2]      = "System Bus source 2",
        [NSP_MEM]       = "NSP Memory",
        [TSENS]         = "Temperature Sensors",
};

struct ras_data {
        /* header start */
        /* Magic number to validate the message */
        u16 magic;
        /* RAS version number */
        u16 ver;
        u32 seq_num;
        /* RAS message type */
        u8  type;
        u8  id;
        /* Size of RAS message without the header in byte */
        u16 len;
        /* header end */
        s32 result;
        /*
         * Error source
         * 0 : SoC Memory
         * 1 : PCIE
         * 2 : DDR
         * 3 : System Bus source 1
         * 4 : System Bus source 2
         * 5 : NSP Memory
         * 6 : Temperature Sensors
         */
        u32 source;
        /*
         * Stores the error type, there are three types of error in RAS
         * 0 : correctable error (CE)
         * 1 : uncorrectable error (UE)
         * 2 : uncorrectable error that is non-fatal (UE_NF)
         */
        u32 err_type;
        u32 err_threshold;
        u32 ce_count;
        u32 ue_count;
        u32 intr_num;
        /* Data specific to error source */
        u8  syndrome[64];
} __packed;

struct soc_mem_syndrome {
        u64 error_address[8];
} __packed;

struct nsp_mem_syndrome {
        u32 error_address[8];
        u8 nsp_id;
} __packed;

struct ddr_syndrome {
        u32 count;
        u32 irq_status;
        u32 data_31_0[2];
        u32 data_63_32[2];
        u32 data_95_64[2];
        u32 data_127_96[2];
        u32 addr_lsb;
        u16 addr_msb;
        u16 parity_bits;
        u16 instance;
        u16 err_type;
} __packed;

struct tsens_syndrome {
        u32 threshold_type;
        s32 temp;
} __packed;

struct sysbus1_syndrome {
        u32 slave;
        u32 err_type;
        u16 addr[8];
        u8  instance;
} __packed;

struct sysbus2_syndrome {
        u32 lsb3;
        u32 msb3;
        u32 lsb2;
        u32 msb2;
        u32 ext_id;
        u16 path;
        u16 op_type;
        u16 len;
        u16 redirect;
        u8  valid;
        u8  word_error;
        u8  non_secure;
        u8  opc;
        u8  error_code;
        u8  trans_type;
        u8  addr_space;
        u8  instance;
} __packed;

struct pcie_syndrome {
        /* CE info */
        u32 bad_tlp;
        u32 bad_dllp;
        u32 replay_rollover;
        u32 replay_timeout;
        u32 rx_err;
        u32 internal_ce_count;
        /* UE_NF info */
        u32 fc_timeout;
        u32 poison_tlp;
        u32 ecrc_err;
        u32 unsupported_req;
        u32 completer_abort;
        u32 completion_timeout;
        /* UE info */
        u32 addr;
        u8  index;
        /*
         * Flag to indicate specific event of PCIe
         * BIT(0): Power break (low power)
         * BIT(1) to BIT(7): Reserved
         */
        u8 flag;
} __packed;

static const char * const threshold_type_str[NUM_TEMP_LVL] = {
        [0] = "lower",
        [1] = "upper",
        [2] = "critical",
};

static void ras_msg_to_cpu(struct ras_data *msg)
{
        struct sysbus1_syndrome *sysbus1_syndrome = (struct sysbus1_syndrome *)&msg->syndrome[0];
        struct sysbus2_syndrome *sysbus2_syndrome = (struct sysbus2_syndrome *)&msg->syndrome[0];
        struct soc_mem_syndrome *soc_syndrome = (struct soc_mem_syndrome *)&msg->syndrome[0];
        struct nsp_mem_syndrome *nsp_syndrome = (struct nsp_mem_syndrome *)&msg->syndrome[0];
        struct tsens_syndrome *tsens_syndrome = (struct tsens_syndrome *)&msg->syndrome[0];
        struct pcie_syndrome *pcie_syndrome = (struct pcie_syndrome *)&msg->syndrome[0];
        struct ddr_syndrome *ddr_syndrome = (struct ddr_syndrome *)&msg->syndrome[0];
        int i;

        le16_to_cpus(&msg->magic);
        le16_to_cpus(&msg->ver);
        le32_to_cpus(&msg->seq_num);
        le16_to_cpus(&msg->len);
        le32_to_cpus(&msg->result);
        le32_to_cpus(&msg->source);
        le32_to_cpus(&msg->err_type);
        le32_to_cpus(&msg->err_threshold);
        le32_to_cpus(&msg->ce_count);
        le32_to_cpus(&msg->ue_count);
        le32_to_cpus(&msg->intr_num);

        switch (msg->source) {
        case SOC_MEM:
                for (i = 0; i < 8; i++)
                        le64_to_cpus(&soc_syndrome->error_address[i]);
                break;
        case PCIE:
                le32_to_cpus(&pcie_syndrome->bad_tlp);
                le32_to_cpus(&pcie_syndrome->bad_dllp);
                le32_to_cpus(&pcie_syndrome->replay_rollover);
                le32_to_cpus(&pcie_syndrome->replay_timeout);
                le32_to_cpus(&pcie_syndrome->rx_err);
                le32_to_cpus(&pcie_syndrome->internal_ce_count);
                le32_to_cpus(&pcie_syndrome->fc_timeout);
                le32_to_cpus(&pcie_syndrome->poison_tlp);
                le32_to_cpus(&pcie_syndrome->ecrc_err);
                le32_to_cpus(&pcie_syndrome->unsupported_req);
                le32_to_cpus(&pcie_syndrome->completer_abort);
                le32_to_cpus(&pcie_syndrome->completion_timeout);
                le32_to_cpus(&pcie_syndrome->addr);
                break;
        case DDR:
                le16_to_cpus(&ddr_syndrome->instance);
                le16_to_cpus(&ddr_syndrome->err_type);
                le32_to_cpus(&ddr_syndrome->count);
                le32_to_cpus(&ddr_syndrome->irq_status);
                le32_to_cpus(&ddr_syndrome->data_31_0[0]);
                le32_to_cpus(&ddr_syndrome->data_31_0[1]);
                le32_to_cpus(&ddr_syndrome->data_63_32[0]);
                le32_to_cpus(&ddr_syndrome->data_63_32[1]);
                le32_to_cpus(&ddr_syndrome->data_95_64[0]);
                le32_to_cpus(&ddr_syndrome->data_95_64[1]);
                le32_to_cpus(&ddr_syndrome->data_127_96[0]);
                le32_to_cpus(&ddr_syndrome->data_127_96[1]);
                le16_to_cpus(&ddr_syndrome->parity_bits);
                le16_to_cpus(&ddr_syndrome->addr_msb);
                le32_to_cpus(&ddr_syndrome->addr_lsb);
                break;
        case SYS_BUS1:
                le32_to_cpus(&sysbus1_syndrome->slave);
                le32_to_cpus(&sysbus1_syndrome->err_type);
                for (i = 0; i < 8; i++)
                        le16_to_cpus(&sysbus1_syndrome->addr[i]);
                break;
        case SYS_BUS2:
                le16_to_cpus(&sysbus2_syndrome->op_type);
                le16_to_cpus(&sysbus2_syndrome->len);
                le16_to_cpus(&sysbus2_syndrome->redirect);
                le16_to_cpus(&sysbus2_syndrome->path);
                le32_to_cpus(&sysbus2_syndrome->ext_id);
                le32_to_cpus(&sysbus2_syndrome->lsb2);
                le32_to_cpus(&sysbus2_syndrome->msb2);
                le32_to_cpus(&sysbus2_syndrome->lsb3);
                le32_to_cpus(&sysbus2_syndrome->msb3);
                break;
        case NSP_MEM:
                for (i = 0; i < 8; i++)
                        le32_to_cpus(&nsp_syndrome->error_address[i]);
                break;
        case TSENS:
                le32_to_cpus(&tsens_syndrome->threshold_type);
                le32_to_cpus(&tsens_syndrome->temp);
                break;
        }
}

static void decode_ras_msg(struct qaic_device *qdev, struct ras_data *msg)
{
        struct sysbus1_syndrome *sysbus1_syndrome = (struct sysbus1_syndrome *)&msg->syndrome[0];
        struct sysbus2_syndrome *sysbus2_syndrome = (struct sysbus2_syndrome *)&msg->syndrome[0];
        struct soc_mem_syndrome *soc_syndrome = (struct soc_mem_syndrome *)&msg->syndrome[0];
        struct nsp_mem_syndrome *nsp_syndrome = (struct nsp_mem_syndrome *)&msg->syndrome[0];
        struct tsens_syndrome *tsens_syndrome = (struct tsens_syndrome *)&msg->syndrome[0];
        struct pcie_syndrome *pcie_syndrome = (struct pcie_syndrome *)&msg->syndrome[0];
        struct ddr_syndrome *ddr_syndrome = (struct ddr_syndrome *)&msg->syndrome[0];
        char *class;
        char *level;

        if (msg->magic != MAGIC) {
                pci_warn(qdev->pdev, "Dropping RAS message with invalid magic %x\n", msg->magic);
                return;
        }

        if (!msg->ver || msg->ver > VERSION) {
                pci_warn(qdev->pdev, "Dropping RAS message with invalid version %d\n", msg->ver);
                return;
        }

        if (msg->type != MSG_PUSH) {
                pci_warn(qdev->pdev, "Dropping non-PUSH RAS message\n");
                return;
        }

        if (msg->len != sizeof(*msg) - HDR_SZ) {
                pci_warn(qdev->pdev, "Dropping RAS message with invalid len %d\n", msg->len);
                return;
        }

        if (msg->err_type >= ERR_TYPE_MAX) {
                pci_warn(qdev->pdev, "Dropping RAS message with err type %d\n", msg->err_type);
                return;
        }

        if (msg->err_type == UE)
                level = KERN_ERR;
        else
                level = KERN_WARNING;

        switch (msg->source) {
        case SOC_MEM:
                dev_printk(level, &qdev->pdev->dev, "RAS event.\nClass:%s\nDescription:%s %s %s\nError Threshold for this report %d\nSyndrome:\n    0x%llx\n    0x%llx\n    0x%llx\n    0x%llx\n    0x%llx\n    0x%llx\n    0x%llx\n    0x%llx\n",
                           err_class_str[msg->err_type],
                           err_type_str[msg->err_type],
                           "error from",
                           err_src_str[msg->source],
                           msg->err_threshold,
                           soc_syndrome->error_address[0],
                           soc_syndrome->error_address[1],
                           soc_syndrome->error_address[2],
                           soc_syndrome->error_address[3],
                           soc_syndrome->error_address[4],
                           soc_syndrome->error_address[5],
                           soc_syndrome->error_address[6],
                           soc_syndrome->error_address[7]);
                break;
        case PCIE:
                dev_printk(level, &qdev->pdev->dev, "RAS event.\nClass:%s\nDescription:%s %s %s\nError Threshold for this report %d\n",
                           err_class_str[msg->err_type],
                           err_type_str[msg->err_type],
                           "error from",
                           err_src_str[msg->source],
                           msg->err_threshold);

                switch (msg->err_type) {
                case CE:
                        /*
                         * Modeled after AER prints. This continues the dev_printk() from a few
                         * lines up. We reduce duplication of code, but also avoid re-printing the
                         * PCI device info so that the end result looks uniform to the log user.
                         */
                        printk(KERN_WARNING pr_fmt("Syndrome:\n    Bad TLP count %d\n    Bad DLLP count %d\n    Replay Rollover count %d\n    Replay Timeout count %d\n    Recv Error count %d\n    Internal CE count %d\n"),
                               pcie_syndrome->bad_tlp,
                               pcie_syndrome->bad_dllp,
                               pcie_syndrome->replay_rollover,
                               pcie_syndrome->replay_timeout,
                               pcie_syndrome->rx_err,
                               pcie_syndrome->internal_ce_count);
                        if (msg->ver > 0x1)
                                pr_warn("    Power break %s\n",
                                        pcie_syndrome->flag & POWER_BREAK ? "ON" : "OFF");
                        break;
                case UE:
                        printk(KERN_ERR pr_fmt("Syndrome:\n    Index %d\n    Address 0x%x\n"),
                               pcie_syndrome->index, pcie_syndrome->addr);
                        break;
                case UE_NF:
                        printk(KERN_WARNING pr_fmt("Syndrome:\n    FC timeout count %d\n    Poisoned TLP count %d\n    ECRC error count %d\n    Unsupported request count %d\n    Completer abort count %d\n    Completion timeout count %d\n"),
                               pcie_syndrome->fc_timeout,
                               pcie_syndrome->poison_tlp,
                               pcie_syndrome->ecrc_err,
                               pcie_syndrome->unsupported_req,
                               pcie_syndrome->completer_abort,
                               pcie_syndrome->completion_timeout);
                        break;
                default:
                        break;
                }
                break;
        case DDR:
                dev_printk(level, &qdev->pdev->dev, "RAS event.\nClass:%s\nDescription:%s %s %s\nError Threshold for this report %d\nSyndrome:\n    Instance %d\n    Count %d\n    Data 31_0 0x%x 0x%x\n    Data 63_32 0x%x 0x%x\n    Data 95_64 0x%x 0x%x\n    Data 127_96 0x%x 0x%x\n    Parity bits 0x%x\n    Address msb 0x%x\n    Address lsb 0x%x\n",
                           err_class_str[msg->err_type],
                           err_type_str[msg->err_type],
                           "error from",
                           err_src_str[msg->source],
                           msg->err_threshold,
                           ddr_syndrome->instance,
                           ddr_syndrome->count,
                           ddr_syndrome->data_31_0[1],
                           ddr_syndrome->data_31_0[0],
                           ddr_syndrome->data_63_32[1],
                           ddr_syndrome->data_63_32[0],
                           ddr_syndrome->data_95_64[1],
                           ddr_syndrome->data_95_64[0],
                           ddr_syndrome->data_127_96[1],
                           ddr_syndrome->data_127_96[0],
                           ddr_syndrome->parity_bits,
                           ddr_syndrome->addr_msb,
                           ddr_syndrome->addr_lsb);
                break;
        case SYS_BUS1:
                dev_printk(level, &qdev->pdev->dev, "RAS event.\nClass:%s\nDescription:%s %s %s\nError Threshold for this report %d\nSyndrome:\n    instance %d\n    %s\n    err_type %d\n    address0 0x%x\n    address1 0x%x\n    address2 0x%x\n    address3 0x%x\n    address4 0x%x\n    address5 0x%x\n    address6 0x%x\n    address7 0x%x\n",
                           err_class_str[msg->err_type],
                           err_type_str[msg->err_type],
                           "error from",
                           err_src_str[msg->source],
                           msg->err_threshold,
                           sysbus1_syndrome->instance,
                           sysbus1_syndrome->slave ? "Slave" : "Master",
                           sysbus1_syndrome->err_type,
                           sysbus1_syndrome->addr[0],
                           sysbus1_syndrome->addr[1],
                           sysbus1_syndrome->addr[2],
                           sysbus1_syndrome->addr[3],
                           sysbus1_syndrome->addr[4],
                           sysbus1_syndrome->addr[5],
                           sysbus1_syndrome->addr[6],
                           sysbus1_syndrome->addr[7]);
                break;
        case SYS_BUS2:
                dev_printk(level, &qdev->pdev->dev, "RAS event.\nClass:%s\nDescription:%s %s %s\nError Threshold for this report %d\nSyndrome:\n    instance %d\n    valid %d\n    word error %d\n    non-secure %d\n    opc %d\n    error code %d\n    transaction type %d\n    address space %d\n    operation type %d\n    len %d\n    redirect %d\n    path %d\n    ext_id %d\n    lsb2 %d\n    msb2 %d\n    lsb3 %d\n    msb3 %d\n",
                           err_class_str[msg->err_type],
                           err_type_str[msg->err_type],
                           "error from",
                           err_src_str[msg->source],
                           msg->err_threshold,
                           sysbus2_syndrome->instance,
                           sysbus2_syndrome->valid,
                           sysbus2_syndrome->word_error,
                           sysbus2_syndrome->non_secure,
                           sysbus2_syndrome->opc,
                           sysbus2_syndrome->error_code,
                           sysbus2_syndrome->trans_type,
                           sysbus2_syndrome->addr_space,
                           sysbus2_syndrome->op_type,
                           sysbus2_syndrome->len,
                           sysbus2_syndrome->redirect,
                           sysbus2_syndrome->path,
                           sysbus2_syndrome->ext_id,
                           sysbus2_syndrome->lsb2,
                           sysbus2_syndrome->msb2,
                           sysbus2_syndrome->lsb3,
                           sysbus2_syndrome->msb3);
                break;
        case NSP_MEM:
                dev_printk(level, &qdev->pdev->dev, "RAS event.\nClass:%s\nDescription:%s %s %s\nError Threshold for this report %d\nSyndrome:\n    NSP ID %d\n    0x%x\n    0x%x\n    0x%x\n    0x%x\n    0x%x\n    0x%x\n    0x%x\n    0x%x\n",
                           err_class_str[msg->err_type],
                           err_type_str[msg->err_type],
                           "error from",
                           err_src_str[msg->source],
                           msg->err_threshold,
                           nsp_syndrome->nsp_id,
                           nsp_syndrome->error_address[0],
                           nsp_syndrome->error_address[1],
                           nsp_syndrome->error_address[2],
                           nsp_syndrome->error_address[3],
                           nsp_syndrome->error_address[4],
                           nsp_syndrome->error_address[5],
                           nsp_syndrome->error_address[6],
                           nsp_syndrome->error_address[7]);
                break;
        case TSENS:
                if (tsens_syndrome->threshold_type >= NUM_TEMP_LVL) {
                        pci_warn(qdev->pdev, "Dropping RAS message with invalid temp threshold %d\n",
                                 tsens_syndrome->threshold_type);
                        break;
                }

                if (msg->err_type)
                        class = "Fatal";
                else if (tsens_syndrome->threshold_type)
                        class = "Critical";
                else
                        class = "Warning";

                dev_printk(level, &qdev->pdev->dev, "RAS event.\nClass:%s\nDescription:%s %s %s\nError Threshold for this report %d\nSyndrome:\n    %s threshold\n    %d deg C\n",
                           class,
                           err_type_str[msg->err_type],
                           "error from",
                           err_src_str[msg->source],
                           msg->err_threshold,
                           threshold_type_str[tsens_syndrome->threshold_type],
                           tsens_syndrome->temp);
                break;
        }

        /* Uncorrectable errors are fatal */
        if (msg->err_type == UE)
                mhi_soc_reset(qdev->mhi_cntrl);

        switch (msg->err_type) {
        case CE:
                if (qdev->ce_count != UINT_MAX)
                        qdev->ce_count++;
                break;
        case UE:
                if (qdev->ce_count != UINT_MAX)
                        qdev->ue_count++;
                break;
        case UE_NF:
                if (qdev->ce_count != UINT_MAX)
                        qdev->ue_nf_count++;
                break;
        default:
                /* not possible */
                break;
        }
}

static ssize_t ce_count_show(struct device *dev, struct device_attribute *attr, char *buf)
{
        struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev));

        return sysfs_emit(buf, "%d\n", qdev->ce_count);
}

static ssize_t ue_count_show(struct device *dev, struct device_attribute *attr, char *buf)
{
        struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev));

        return sysfs_emit(buf, "%d\n", qdev->ue_count);
}

static ssize_t ue_nonfatal_count_show(struct device *dev, struct device_attribute *attr, char *buf)
{
        struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev));

        return sysfs_emit(buf, "%d\n", qdev->ue_nf_count);
}

static DEVICE_ATTR_RO(ce_count);
static DEVICE_ATTR_RO(ue_count);
static DEVICE_ATTR_RO(ue_nonfatal_count);

static struct attribute *ras_attrs[] = {
        &dev_attr_ce_count.attr,
        &dev_attr_ue_count.attr,
        &dev_attr_ue_nonfatal_count.attr,
        NULL,
};

static struct attribute_group ras_group = {
        .attrs = ras_attrs,
};

static int qaic_ras_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
{
        struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev));
        struct ras_data *resp;
        int ret;

        ret = mhi_prepare_for_transfer(mhi_dev);
        if (ret)
                return ret;

        resp = kzalloc_obj(*resp);
        if (!resp) {
                mhi_unprepare_from_transfer(mhi_dev);
                return -ENOMEM;
        }

        ret = mhi_queue_buf(mhi_dev, DMA_FROM_DEVICE, resp, sizeof(*resp), MHI_EOT);
        if (ret) {
                kfree(resp);
                mhi_unprepare_from_transfer(mhi_dev);
                return ret;
        }

        ret = device_add_group(&qdev->pdev->dev, &ras_group);
        if (ret) {
                mhi_unprepare_from_transfer(mhi_dev);
                pci_dbg(qdev->pdev, "ras add sysfs failed %d\n", ret);
                return ret;
        }

        dev_set_drvdata(&mhi_dev->dev, qdev);
        qdev->ras_ch = mhi_dev;

        return ret;
}

static void qaic_ras_mhi_remove(struct mhi_device *mhi_dev)
{
        struct qaic_device *qdev;

        qdev = dev_get_drvdata(&mhi_dev->dev);
        qdev->ras_ch = NULL;
        device_remove_group(&qdev->pdev->dev, &ras_group);
        mhi_unprepare_from_transfer(mhi_dev);
}

static void qaic_ras_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result) {}

static void qaic_ras_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
{
        struct qaic_device *qdev = dev_get_drvdata(&mhi_dev->dev);
        struct ras_data *msg = mhi_result->buf_addr;
        int ret;

        if (mhi_result->transaction_status) {
                kfree(msg);
                return;
        }

        ras_msg_to_cpu(msg);
        decode_ras_msg(qdev, msg);

        ret = mhi_queue_buf(qdev->ras_ch, DMA_FROM_DEVICE, msg, sizeof(*msg), MHI_EOT);
        if (ret) {
                dev_err(&mhi_dev->dev, "Cannot requeue RAS recv buf %d\n", ret);
                kfree(msg);
        }
}

static const struct mhi_device_id qaic_ras_mhi_match_table[] = {
        { .chan = "QAIC_STATUS", },
        {},
};

static struct mhi_driver qaic_ras_mhi_driver = {
        .id_table = qaic_ras_mhi_match_table,
        .remove = qaic_ras_mhi_remove,
        .probe = qaic_ras_mhi_probe,
        .ul_xfer_cb = qaic_ras_mhi_ul_xfer_cb,
        .dl_xfer_cb = qaic_ras_mhi_dl_xfer_cb,
        .driver = {
                .name = "qaic_ras",
        },
};

int qaic_ras_register(void)
{
        return mhi_driver_register(&qaic_ras_mhi_driver);
}

void qaic_ras_unregister(void)
{
        mhi_driver_unregister(&qaic_ras_mhi_driver);
}