| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815 |
- // SPDX-License-Identifier: GPL-2.0-only
- /* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */
- /* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */
- #include <asm/byteorder.h>
- #include <drm/drm_file.h>
- #include <drm/drm_managed.h>
- #include <linux/devcoredump.h>
- #include <linux/device.h>
- #include <linux/kernel.h>
- #include <linux/mhi.h>
- #include <linux/workqueue.h>
- #include "qaic.h"
- #include "qaic_ssr.h"
- #define SSR_RESP_MSG_SZ 32
- #define SSR_MHI_BUF_SIZE SZ_64K
- #define SSR_MEM_READ_DATA_SIZE ((u64)SSR_MHI_BUF_SIZE - sizeof(struct ssr_crashdump))
- #define SSR_MEM_READ_CHUNK_SIZE ((u64)SSR_MEM_READ_DATA_SIZE - sizeof(struct ssr_memory_read_rsp))
- #define DEBUG_TRANSFER_INFO BIT(0)
- #define DEBUG_TRANSFER_INFO_RSP BIT(1)
- #define MEMORY_READ BIT(2)
- #define MEMORY_READ_RSP BIT(3)
- #define DEBUG_TRANSFER_DONE BIT(4)
- #define DEBUG_TRANSFER_DONE_RSP BIT(5)
- #define SSR_EVENT BIT(8)
- #define SSR_EVENT_RSP BIT(9)
- #define SSR_EVENT_NACK BIT(0)
- #define BEFORE_SHUTDOWN BIT(1)
- #define AFTER_SHUTDOWN BIT(2)
- #define BEFORE_POWER_UP BIT(3)
- #define AFTER_POWER_UP BIT(4)
- struct debug_info_table {
- /* Save preferences. Default is mandatory */
- u64 save_perf;
- /* Base address of the debug region */
- u64 mem_base;
- /* Size of debug region in bytes */
- u64 len;
- /* Description */
- char desc[20];
- /* Filename of debug region */
- char filename[20];
- };
- struct _ssr_hdr {
- __le32 cmd;
- __le32 len;
- __le32 dbc_id;
- };
- struct ssr_hdr {
- u32 cmd;
- u32 len;
- u32 dbc_id;
- };
- struct ssr_debug_transfer_info {
- struct ssr_hdr hdr;
- u32 resv;
- u64 tbl_addr;
- u64 tbl_len;
- } __packed;
- struct ssr_debug_transfer_info_rsp {
- struct _ssr_hdr hdr;
- __le32 ret;
- } __packed;
- struct ssr_memory_read {
- struct _ssr_hdr hdr;
- __le32 resv;
- __le64 addr;
- __le64 len;
- } __packed;
- struct ssr_memory_read_rsp {
- struct _ssr_hdr hdr;
- __le32 resv;
- u8 data[];
- } __packed;
- struct ssr_debug_transfer_done {
- struct _ssr_hdr hdr;
- __le32 resv;
- } __packed;
- struct ssr_debug_transfer_done_rsp {
- struct _ssr_hdr hdr;
- __le32 ret;
- } __packed;
- struct ssr_event {
- struct ssr_hdr hdr;
- u32 event;
- } __packed;
- struct ssr_event_rsp {
- struct _ssr_hdr hdr;
- __le32 event;
- } __packed;
- struct ssr_resp {
- /* Work struct to schedule work coming on QAIC_SSR channel */
- struct work_struct work;
- /* Root struct of device, used to access device resources */
- struct qaic_device *qdev;
- /* Buffer used by MHI for transfer requests */
- u8 data[] __aligned(8);
- };
- /* SSR crashdump book keeping structure */
- struct ssr_dump_info {
- /* DBC associated with this SSR crashdump */
- struct dma_bridge_chan *dbc;
- /*
- * It will be used when we complete the crashdump download and switch
- * to waiting on SSR events
- */
- struct ssr_resp *resp;
- /* MEMORY READ request MHI buffer.*/
- struct ssr_memory_read *read_buf_req;
- /* TRUE: ->read_buf_req is queued for MHI transaction. FALSE: Otherwise */
- bool read_buf_req_queued;
- /* Address of table in host */
- void *tbl_addr;
- /* Total size of table */
- u64 tbl_len;
- /* Offset of table(->tbl_addr) where the new chunk will be dumped */
- u64 tbl_off;
- /* Address of table in device/target */
- u64 tbl_addr_dev;
- /* Ptr to the entire dump */
- void *dump_addr;
- /* Entire crashdump size */
- u64 dump_sz;
- /* Offset of crashdump(->dump_addr) where the new chunk will be dumped */
- u64 dump_off;
- /* Points to the table entry we are currently downloading */
- struct debug_info_table *tbl_ent;
- /* Offset in the current table entry(->tbl_ent) for next chuck */
- u64 tbl_ent_off;
- };
- struct ssr_crashdump {
- /*
- * Points to a book keeping struct maintained by MHI SSR device while
- * downloading a SSR crashdump. It is NULL when crashdump downloading
- * not in progress.
- */
- struct ssr_dump_info *dump_info;
- /* Work struct to schedule work coming on QAIC_SSR channel */
- struct work_struct work;
- /* Root struct of device, used to access device resources */
- struct qaic_device *qdev;
- /* Buffer used by MHI for transfer requests */
- u8 data[];
- };
- #define QAIC_SSR_DUMP_V1_MAGIC 0x1234567890abcdef
- #define QAIC_SSR_DUMP_V1_VER 1
- struct dump_file_meta {
- u64 magic;
- u64 version;
- u64 size; /* Total size of the entire dump */
- u64 tbl_len; /* Length of the table in byte */
- };
- /*
- * Layout of crashdump
- * +------------------------------------------+
- * | Crashdump Meta structure |
- * | type: struct dump_file_meta |
- * +------------------------------------------+
- * | Crashdump Table |
- * | type: array of struct debug_info_table |
- * | |
- * | |
- * | |
- * +------------------------------------------+
- * | Crashdump |
- * | |
- * | |
- * | |
- * | |
- * | |
- * +------------------------------------------+
- */
- static void free_ssr_dump_info(struct ssr_crashdump *ssr_crash)
- {
- struct ssr_dump_info *dump_info = ssr_crash->dump_info;
- ssr_crash->dump_info = NULL;
- if (!dump_info)
- return;
- if (!dump_info->read_buf_req_queued)
- kfree(dump_info->read_buf_req);
- vfree(dump_info->tbl_addr);
- vfree(dump_info->dump_addr);
- kfree(dump_info);
- }
- void qaic_clean_up_ssr(struct qaic_device *qdev)
- {
- struct ssr_crashdump *ssr_crash = qdev->ssr_mhi_buf;
- if (!ssr_crash)
- return;
- qaic_dbc_exit_ssr(qdev);
- free_ssr_dump_info(ssr_crash);
- }
- static int alloc_dump(struct ssr_dump_info *dump_info)
- {
- struct debug_info_table *tbl_ent = dump_info->tbl_addr;
- struct dump_file_meta *dump_meta;
- u64 tbl_sz_lp = 0;
- u64 dump_size = 0;
- while (tbl_sz_lp < dump_info->tbl_len) {
- le64_to_cpus(&tbl_ent->save_perf);
- le64_to_cpus(&tbl_ent->mem_base);
- le64_to_cpus(&tbl_ent->len);
- if (tbl_ent->len == 0)
- return -EINVAL;
- dump_size += tbl_ent->len;
- tbl_ent++;
- tbl_sz_lp += sizeof(*tbl_ent);
- }
- dump_info->dump_sz = dump_size + dump_info->tbl_len + sizeof(*dump_meta);
- dump_info->dump_addr = vzalloc(dump_info->dump_sz);
- if (!dump_info->dump_addr)
- return -ENOMEM;
- /* Copy crashdump meta and table */
- dump_meta = dump_info->dump_addr;
- dump_meta->magic = QAIC_SSR_DUMP_V1_MAGIC;
- dump_meta->version = QAIC_SSR_DUMP_V1_VER;
- dump_meta->size = dump_info->dump_sz;
- dump_meta->tbl_len = dump_info->tbl_len;
- memcpy(dump_info->dump_addr + sizeof(*dump_meta), dump_info->tbl_addr, dump_info->tbl_len);
- /* Offset by crashdump meta and table (copied above) */
- dump_info->dump_off = dump_info->tbl_len + sizeof(*dump_meta);
- return 0;
- }
- static int send_xfer_done(struct qaic_device *qdev, void *resp, u32 dbc_id)
- {
- struct ssr_debug_transfer_done *xfer_done;
- int ret;
- xfer_done = kmalloc_obj(*xfer_done);
- if (!xfer_done) {
- ret = -ENOMEM;
- goto out;
- }
- ret = mhi_queue_buf(qdev->ssr_ch, DMA_FROM_DEVICE, resp, SSR_RESP_MSG_SZ, MHI_EOT);
- if (ret)
- goto free_xfer_done;
- xfer_done->hdr.cmd = cpu_to_le32(DEBUG_TRANSFER_DONE);
- xfer_done->hdr.len = cpu_to_le32(sizeof(*xfer_done));
- xfer_done->hdr.dbc_id = cpu_to_le32(dbc_id);
- ret = mhi_queue_buf(qdev->ssr_ch, DMA_TO_DEVICE, xfer_done, sizeof(*xfer_done), MHI_EOT);
- if (ret)
- goto free_xfer_done;
- return 0;
- free_xfer_done:
- kfree(xfer_done);
- out:
- return ret;
- }
- static int mem_read_req(struct qaic_device *qdev, u64 dest_addr, u64 dest_len)
- {
- struct ssr_crashdump *ssr_crash = qdev->ssr_mhi_buf;
- struct ssr_memory_read *read_buf_req;
- struct ssr_dump_info *dump_info;
- int ret;
- dump_info = ssr_crash->dump_info;
- ret = mhi_queue_buf(qdev->ssr_ch, DMA_FROM_DEVICE, ssr_crash->data, SSR_MEM_READ_DATA_SIZE,
- MHI_EOT);
- if (ret)
- goto out;
- read_buf_req = dump_info->read_buf_req;
- read_buf_req->hdr.cmd = cpu_to_le32(MEMORY_READ);
- read_buf_req->hdr.len = cpu_to_le32(sizeof(*read_buf_req));
- read_buf_req->hdr.dbc_id = cpu_to_le32(qdev->ssr_dbc);
- read_buf_req->addr = cpu_to_le64(dest_addr);
- read_buf_req->len = cpu_to_le64(dest_len);
- ret = mhi_queue_buf(qdev->ssr_ch, DMA_TO_DEVICE, read_buf_req, sizeof(*read_buf_req),
- MHI_EOT);
- if (!ret)
- dump_info->read_buf_req_queued = true;
- out:
- return ret;
- }
- static int ssr_copy_table(struct ssr_dump_info *dump_info, void *data, u64 len)
- {
- if (len > dump_info->tbl_len - dump_info->tbl_off)
- return -EINVAL;
- memcpy(dump_info->tbl_addr + dump_info->tbl_off, data, len);
- dump_info->tbl_off += len;
- /* Entire table has been downloaded, alloc dump memory */
- if (dump_info->tbl_off == dump_info->tbl_len) {
- dump_info->tbl_ent = dump_info->tbl_addr;
- return alloc_dump(dump_info);
- }
- return 0;
- }
- static int ssr_copy_dump(struct ssr_dump_info *dump_info, void *data, u64 len)
- {
- struct debug_info_table *tbl_ent;
- tbl_ent = dump_info->tbl_ent;
- if (len > tbl_ent->len - dump_info->tbl_ent_off)
- return -EINVAL;
- memcpy(dump_info->dump_addr + dump_info->dump_off, data, len);
- dump_info->dump_off += len;
- dump_info->tbl_ent_off += len;
- /*
- * Current segment (a entry in table) of the crashdump is complete,
- * move to next one
- */
- if (tbl_ent->len == dump_info->tbl_ent_off) {
- dump_info->tbl_ent++;
- dump_info->tbl_ent_off = 0;
- }
- return 0;
- }
- static void ssr_dump_worker(struct work_struct *work)
- {
- struct ssr_crashdump *ssr_crash = container_of(work, struct ssr_crashdump, work);
- struct qaic_device *qdev = ssr_crash->qdev;
- struct ssr_memory_read_rsp *mem_rd_resp;
- struct debug_info_table *tbl_ent;
- struct ssr_dump_info *dump_info;
- u64 dest_addr, dest_len;
- struct _ssr_hdr *_hdr;
- struct ssr_hdr hdr;
- u64 data_len;
- int ret;
- mem_rd_resp = (struct ssr_memory_read_rsp *)ssr_crash->data;
- _hdr = &mem_rd_resp->hdr;
- hdr.cmd = le32_to_cpu(_hdr->cmd);
- hdr.len = le32_to_cpu(_hdr->len);
- hdr.dbc_id = le32_to_cpu(_hdr->dbc_id);
- if (hdr.dbc_id != qdev->ssr_dbc)
- goto reset_device;
- dump_info = ssr_crash->dump_info;
- if (!dump_info)
- goto reset_device;
- if (hdr.cmd != MEMORY_READ_RSP)
- goto free_dump_info;
- if (hdr.len > SSR_MEM_READ_DATA_SIZE)
- goto free_dump_info;
- data_len = hdr.len - sizeof(*mem_rd_resp);
- if (dump_info->tbl_off < dump_info->tbl_len) /* Chunk belongs to table */
- ret = ssr_copy_table(dump_info, mem_rd_resp->data, data_len);
- else /* Chunk belongs to crashdump */
- ret = ssr_copy_dump(dump_info, mem_rd_resp->data, data_len);
- if (ret)
- goto free_dump_info;
- if (dump_info->tbl_off < dump_info->tbl_len) {
- /* Continue downloading table */
- dest_addr = dump_info->tbl_addr_dev + dump_info->tbl_off;
- dest_len = min(SSR_MEM_READ_CHUNK_SIZE, dump_info->tbl_len - dump_info->tbl_off);
- ret = mem_read_req(qdev, dest_addr, dest_len);
- } else if (dump_info->dump_off < dump_info->dump_sz) {
- /* Continue downloading crashdump */
- tbl_ent = dump_info->tbl_ent;
- dest_addr = tbl_ent->mem_base + dump_info->tbl_ent_off;
- dest_len = min(SSR_MEM_READ_CHUNK_SIZE, tbl_ent->len - dump_info->tbl_ent_off);
- ret = mem_read_req(qdev, dest_addr, dest_len);
- } else {
- /* Crashdump download complete */
- ret = send_xfer_done(qdev, dump_info->resp->data, hdr.dbc_id);
- }
- /* Most likely a MHI xfer has failed */
- if (ret)
- goto free_dump_info;
- return;
- free_dump_info:
- /* Free the allocated memory */
- free_ssr_dump_info(ssr_crash);
- reset_device:
- /*
- * After subsystem crashes in device crashdump collection begins but
- * something went wrong while collecting crashdump, now instead of
- * handling this error we just reset the device as the best effort has
- * been made
- */
- mhi_soc_reset(qdev->mhi_cntrl);
- }
- static struct ssr_dump_info *alloc_dump_info(struct qaic_device *qdev,
- struct ssr_debug_transfer_info *debug_info)
- {
- struct ssr_dump_info *dump_info;
- int ret;
- le64_to_cpus(&debug_info->tbl_len);
- le64_to_cpus(&debug_info->tbl_addr);
- if (debug_info->tbl_len == 0 ||
- debug_info->tbl_len % sizeof(struct debug_info_table) != 0) {
- ret = -EINVAL;
- goto out;
- }
- /* Allocate SSR crashdump book keeping structure */
- dump_info = kzalloc_obj(*dump_info);
- if (!dump_info) {
- ret = -ENOMEM;
- goto out;
- }
- /* Buffer used to send MEMORY READ request to device via MHI */
- dump_info->read_buf_req = kzalloc_obj(*dump_info->read_buf_req);
- if (!dump_info->read_buf_req) {
- ret = -ENOMEM;
- goto free_dump_info;
- }
- /* Crashdump meta table buffer */
- dump_info->tbl_addr = vzalloc(debug_info->tbl_len);
- if (!dump_info->tbl_addr) {
- ret = -ENOMEM;
- goto free_read_buf_req;
- }
- dump_info->tbl_addr_dev = debug_info->tbl_addr;
- dump_info->tbl_len = debug_info->tbl_len;
- return dump_info;
- free_read_buf_req:
- kfree(dump_info->read_buf_req);
- free_dump_info:
- kfree(dump_info);
- out:
- return ERR_PTR(ret);
- }
- static int dbg_xfer_info_rsp(struct qaic_device *qdev, struct dma_bridge_chan *dbc,
- struct ssr_debug_transfer_info *debug_info)
- {
- struct ssr_debug_transfer_info_rsp *debug_rsp;
- struct ssr_crashdump *ssr_crash = NULL;
- int ret = 0, ret2;
- debug_rsp = kmalloc_obj(*debug_rsp);
- if (!debug_rsp)
- return -ENOMEM;
- if (!qdev->ssr_mhi_buf) {
- ret = -ENOMEM;
- goto send_rsp;
- }
- if (dbc->state != DBC_STATE_BEFORE_POWER_UP) {
- ret = -EINVAL;
- goto send_rsp;
- }
- ssr_crash = qdev->ssr_mhi_buf;
- ssr_crash->dump_info = alloc_dump_info(qdev, debug_info);
- if (IS_ERR(ssr_crash->dump_info)) {
- ret = PTR_ERR(ssr_crash->dump_info);
- ssr_crash->dump_info = NULL;
- }
- send_rsp:
- debug_rsp->hdr.cmd = cpu_to_le32(DEBUG_TRANSFER_INFO_RSP);
- debug_rsp->hdr.len = cpu_to_le32(sizeof(*debug_rsp));
- debug_rsp->hdr.dbc_id = cpu_to_le32(dbc->id);
- /*
- * 0 = Return an ACK confirming the host is ready to download crashdump
- * 1 = Return an NACK confirming the host is not ready to download crashdump
- */
- debug_rsp->ret = cpu_to_le32(ret ? 1 : 0);
- ret2 = mhi_queue_buf(qdev->ssr_ch, DMA_TO_DEVICE, debug_rsp, sizeof(*debug_rsp), MHI_EOT);
- if (ret2) {
- free_ssr_dump_info(ssr_crash);
- kfree(debug_rsp);
- return ret2;
- }
- return ret;
- }
- static void dbg_xfer_done_rsp(struct qaic_device *qdev, struct dma_bridge_chan *dbc,
- struct ssr_debug_transfer_done_rsp *xfer_rsp)
- {
- struct ssr_crashdump *ssr_crash = qdev->ssr_mhi_buf;
- u32 status = le32_to_cpu(xfer_rsp->ret);
- struct device *dev = &qdev->pdev->dev;
- struct ssr_dump_info *dump_info;
- dump_info = ssr_crash->dump_info;
- if (!dump_info)
- return;
- if (status) {
- free_ssr_dump_info(ssr_crash);
- return;
- }
- dev_coredumpv(dev, dump_info->dump_addr, dump_info->dump_sz, GFP_KERNEL);
- /* dev_coredumpv will free dump_info->dump_addr */
- dump_info->dump_addr = NULL;
- free_ssr_dump_info(ssr_crash);
- }
- static void ssr_worker(struct work_struct *work)
- {
- struct ssr_resp *resp = container_of(work, struct ssr_resp, work);
- struct ssr_hdr *hdr = (struct ssr_hdr *)resp->data;
- struct ssr_dump_info *dump_info = NULL;
- struct qaic_device *qdev = resp->qdev;
- struct ssr_crashdump *ssr_crash;
- struct ssr_event_rsp *event_rsp;
- struct dma_bridge_chan *dbc;
- struct ssr_event *event;
- u32 ssr_event_ack;
- int ret;
- le32_to_cpus(&hdr->cmd);
- le32_to_cpus(&hdr->len);
- le32_to_cpus(&hdr->dbc_id);
- if (hdr->len > SSR_RESP_MSG_SZ)
- goto out;
- if (hdr->dbc_id >= qdev->num_dbc)
- goto out;
- dbc = &qdev->dbc[hdr->dbc_id];
- switch (hdr->cmd) {
- case DEBUG_TRANSFER_INFO:
- ret = dbg_xfer_info_rsp(qdev, dbc, (struct ssr_debug_transfer_info *)resp->data);
- if (ret)
- break;
- ssr_crash = qdev->ssr_mhi_buf;
- dump_info = ssr_crash->dump_info;
- dump_info->dbc = dbc;
- dump_info->resp = resp;
- /* Start by downloading debug table */
- ret = mem_read_req(qdev, dump_info->tbl_addr_dev,
- min(dump_info->tbl_len, SSR_MEM_READ_CHUNK_SIZE));
- if (ret) {
- free_ssr_dump_info(ssr_crash);
- break;
- }
- /*
- * Till now everything went fine, which means that we will be
- * collecting crashdump chunk by chunk. Do not queue a response
- * buffer for SSR cmds till the crashdump is complete.
- */
- return;
- case SSR_EVENT:
- event = (struct ssr_event *)hdr;
- le32_to_cpus(&event->event);
- ssr_event_ack = event->event;
- ssr_crash = qdev->ssr_mhi_buf;
- switch (event->event) {
- case BEFORE_SHUTDOWN:
- set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_BEFORE_SHUTDOWN);
- qaic_dbc_enter_ssr(qdev, hdr->dbc_id);
- break;
- case AFTER_SHUTDOWN:
- set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_AFTER_SHUTDOWN);
- break;
- case BEFORE_POWER_UP:
- set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_BEFORE_POWER_UP);
- break;
- case AFTER_POWER_UP:
- /*
- * If dump info is a non NULL value it means that we
- * have received this SSR event while downloading a
- * crashdump for this DBC is still in progress. NACK
- * the SSR event
- */
- if (ssr_crash && ssr_crash->dump_info) {
- free_ssr_dump_info(ssr_crash);
- ssr_event_ack = SSR_EVENT_NACK;
- break;
- }
- set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_AFTER_POWER_UP);
- break;
- default:
- break;
- }
- event_rsp = kmalloc_obj(*event_rsp);
- if (!event_rsp)
- break;
- event_rsp->hdr.cmd = cpu_to_le32(SSR_EVENT_RSP);
- event_rsp->hdr.len = cpu_to_le32(sizeof(*event_rsp));
- event_rsp->hdr.dbc_id = cpu_to_le32(hdr->dbc_id);
- event_rsp->event = cpu_to_le32(ssr_event_ack);
- ret = mhi_queue_buf(qdev->ssr_ch, DMA_TO_DEVICE, event_rsp, sizeof(*event_rsp),
- MHI_EOT);
- if (ret)
- kfree(event_rsp);
- if (event->event == AFTER_POWER_UP && ssr_event_ack != SSR_EVENT_NACK) {
- qaic_dbc_exit_ssr(qdev);
- set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_IDLE);
- }
- break;
- case DEBUG_TRANSFER_DONE_RSP:
- dbg_xfer_done_rsp(qdev, dbc, (struct ssr_debug_transfer_done_rsp *)hdr);
- break;
- default:
- break;
- }
- out:
- ret = mhi_queue_buf(qdev->ssr_ch, DMA_FROM_DEVICE, resp->data, SSR_RESP_MSG_SZ, MHI_EOT);
- if (ret)
- kfree(resp);
- }
- static int qaic_ssr_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 ssr_resp *resp;
- int ret;
- ret = mhi_prepare_for_transfer(mhi_dev);
- if (ret)
- return ret;
- resp = kzalloc(sizeof(*resp) + SSR_RESP_MSG_SZ, GFP_KERNEL);
- if (!resp) {
- mhi_unprepare_from_transfer(mhi_dev);
- return -ENOMEM;
- }
- resp->qdev = qdev;
- INIT_WORK(&resp->work, ssr_worker);
- ret = mhi_queue_buf(mhi_dev, DMA_FROM_DEVICE, resp->data, SSR_RESP_MSG_SZ, MHI_EOT);
- if (ret) {
- kfree(resp);
- mhi_unprepare_from_transfer(mhi_dev);
- return ret;
- }
- dev_set_drvdata(&mhi_dev->dev, qdev);
- qdev->ssr_ch = mhi_dev;
- return 0;
- }
- static void qaic_ssr_mhi_remove(struct mhi_device *mhi_dev)
- {
- struct qaic_device *qdev;
- qdev = dev_get_drvdata(&mhi_dev->dev);
- mhi_unprepare_from_transfer(qdev->ssr_ch);
- qdev->ssr_ch = NULL;
- }
- static void qaic_ssr_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
- {
- struct qaic_device *qdev = dev_get_drvdata(&mhi_dev->dev);
- struct ssr_crashdump *ssr_crash = qdev->ssr_mhi_buf;
- struct _ssr_hdr *hdr = mhi_result->buf_addr;
- struct ssr_dump_info *dump_info;
- if (mhi_result->transaction_status) {
- kfree(mhi_result->buf_addr);
- return;
- }
- /*
- * MEMORY READ is used to download crashdump. And crashdump is
- * downloaded chunk by chunk in a series of MEMORY READ SSR commands.
- * Hence to avoid too many kmalloc() and kfree() of the same MEMORY READ
- * request buffer, we allocate only one such buffer and free it only
- * once.
- */
- if (le32_to_cpu(hdr->cmd) == MEMORY_READ) {
- dump_info = ssr_crash->dump_info;
- if (dump_info) {
- dump_info->read_buf_req_queued = false;
- return;
- }
- }
- kfree(mhi_result->buf_addr);
- }
- static void qaic_ssr_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
- {
- struct ssr_resp *resp = container_of(mhi_result->buf_addr, struct ssr_resp, data);
- struct qaic_device *qdev = dev_get_drvdata(&mhi_dev->dev);
- struct ssr_crashdump *ssr_crash = qdev->ssr_mhi_buf;
- bool memory_read_rsp = false;
- if (ssr_crash && ssr_crash->data == mhi_result->buf_addr)
- memory_read_rsp = true;
- if (mhi_result->transaction_status) {
- /* Do not free SSR crashdump buffer as it allocated via managed APIs */
- if (!memory_read_rsp)
- kfree(resp);
- return;
- }
- if (memory_read_rsp)
- queue_work(qdev->ssr_wq, &ssr_crash->work);
- else
- queue_work(qdev->ssr_wq, &resp->work);
- }
- static const struct mhi_device_id qaic_ssr_mhi_match_table[] = {
- { .chan = "QAIC_SSR", },
- {},
- };
- static struct mhi_driver qaic_ssr_mhi_driver = {
- .id_table = qaic_ssr_mhi_match_table,
- .remove = qaic_ssr_mhi_remove,
- .probe = qaic_ssr_mhi_probe,
- .ul_xfer_cb = qaic_ssr_mhi_ul_xfer_cb,
- .dl_xfer_cb = qaic_ssr_mhi_dl_xfer_cb,
- .driver = {
- .name = "qaic_ssr",
- },
- };
- int qaic_ssr_init(struct qaic_device *qdev, struct drm_device *drm)
- {
- struct ssr_crashdump *ssr_crash;
- qdev->ssr_dbc = QAIC_SSR_DBC_SENTINEL;
- /*
- * Device requests only one SSR at a time. So allocating only one
- * buffer to download crashdump is good enough.
- */
- ssr_crash = drmm_kzalloc(drm, SSR_MHI_BUF_SIZE, GFP_KERNEL);
- if (!ssr_crash)
- return -ENOMEM;
- ssr_crash->qdev = qdev;
- INIT_WORK(&ssr_crash->work, ssr_dump_worker);
- qdev->ssr_mhi_buf = ssr_crash;
- return 0;
- }
- int qaic_ssr_register(void)
- {
- return mhi_driver_register(&qaic_ssr_mhi_driver);
- }
- void qaic_ssr_unregister(void)
- {
- mhi_driver_unregister(&qaic_ssr_mhi_driver);
- }
|