1*9675093aSJeffrey Hugo // SPDX-License-Identifier: GPL-2.0-only 2*9675093aSJeffrey Hugo 3*9675093aSJeffrey Hugo /* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */ 4*9675093aSJeffrey Hugo /* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ 5*9675093aSJeffrey Hugo 6*9675093aSJeffrey Hugo #include <asm/byteorder.h> 7*9675093aSJeffrey Hugo #include <drm/drm_file.h> 8*9675093aSJeffrey Hugo #include <drm/drm_managed.h> 9*9675093aSJeffrey Hugo #include <linux/device.h> 10*9675093aSJeffrey Hugo #include <linux/kernel.h> 11*9675093aSJeffrey Hugo #include <linux/mhi.h> 12*9675093aSJeffrey Hugo #include <linux/workqueue.h> 13*9675093aSJeffrey Hugo 14*9675093aSJeffrey Hugo #include "qaic.h" 15*9675093aSJeffrey Hugo #include "qaic_ssr.h" 16*9675093aSJeffrey Hugo 17*9675093aSJeffrey Hugo #define SSR_RESP_MSG_SZ 32 18*9675093aSJeffrey Hugo 19*9675093aSJeffrey Hugo #define DEBUG_TRANSFER_INFO BIT(0) 20*9675093aSJeffrey Hugo #define DEBUG_TRANSFER_INFO_RSP BIT(1) 21*9675093aSJeffrey Hugo #define MEMORY_READ BIT(2) 22*9675093aSJeffrey Hugo #define MEMORY_READ_RSP BIT(3) 23*9675093aSJeffrey Hugo #define DEBUG_TRANSFER_DONE BIT(4) 24*9675093aSJeffrey Hugo #define DEBUG_TRANSFER_DONE_RSP BIT(5) 25*9675093aSJeffrey Hugo #define SSR_EVENT BIT(8) 26*9675093aSJeffrey Hugo #define SSR_EVENT_RSP BIT(9) 27*9675093aSJeffrey Hugo 28*9675093aSJeffrey Hugo #define SSR_EVENT_NACK BIT(0) 29*9675093aSJeffrey Hugo #define BEFORE_SHUTDOWN BIT(1) 30*9675093aSJeffrey Hugo #define AFTER_SHUTDOWN BIT(2) 31*9675093aSJeffrey Hugo #define BEFORE_POWER_UP BIT(3) 32*9675093aSJeffrey Hugo #define AFTER_POWER_UP BIT(4) 33*9675093aSJeffrey Hugo 34*9675093aSJeffrey Hugo struct _ssr_hdr { 35*9675093aSJeffrey Hugo __le32 cmd; 36*9675093aSJeffrey Hugo __le32 len; 37*9675093aSJeffrey Hugo __le32 dbc_id; 38*9675093aSJeffrey Hugo }; 39*9675093aSJeffrey Hugo 40*9675093aSJeffrey Hugo struct ssr_hdr { 41*9675093aSJeffrey Hugo u32 cmd; 42*9675093aSJeffrey Hugo u32 len; 43*9675093aSJeffrey Hugo u32 dbc_id; 44*9675093aSJeffrey Hugo }; 45*9675093aSJeffrey Hugo 46*9675093aSJeffrey Hugo struct ssr_debug_transfer_info_rsp { 47*9675093aSJeffrey Hugo struct _ssr_hdr hdr; 48*9675093aSJeffrey Hugo __le32 ret; 49*9675093aSJeffrey Hugo } __packed; 50*9675093aSJeffrey Hugo 51*9675093aSJeffrey Hugo struct ssr_event { 52*9675093aSJeffrey Hugo struct ssr_hdr hdr; 53*9675093aSJeffrey Hugo u32 event; 54*9675093aSJeffrey Hugo } __packed; 55*9675093aSJeffrey Hugo 56*9675093aSJeffrey Hugo struct ssr_event_rsp { 57*9675093aSJeffrey Hugo struct _ssr_hdr hdr; 58*9675093aSJeffrey Hugo __le32 event; 59*9675093aSJeffrey Hugo } __packed; 60*9675093aSJeffrey Hugo 61*9675093aSJeffrey Hugo struct ssr_resp { 62*9675093aSJeffrey Hugo /* Work struct to schedule work coming on QAIC_SSR channel */ 63*9675093aSJeffrey Hugo struct work_struct work; 64*9675093aSJeffrey Hugo /* Root struct of device, used to access device resources */ 65*9675093aSJeffrey Hugo struct qaic_device *qdev; 66*9675093aSJeffrey Hugo /* Buffer used by MHI for transfer requests */ 67*9675093aSJeffrey Hugo u8 data[] __aligned(8); 68*9675093aSJeffrey Hugo }; 69*9675093aSJeffrey Hugo 70*9675093aSJeffrey Hugo void qaic_clean_up_ssr(struct qaic_device *qdev) 71*9675093aSJeffrey Hugo { 72*9675093aSJeffrey Hugo qaic_dbc_exit_ssr(qdev); 73*9675093aSJeffrey Hugo } 74*9675093aSJeffrey Hugo 75*9675093aSJeffrey Hugo static void ssr_worker(struct work_struct *work) 76*9675093aSJeffrey Hugo { 77*9675093aSJeffrey Hugo struct ssr_resp *resp = container_of(work, struct ssr_resp, work); 78*9675093aSJeffrey Hugo struct ssr_hdr *hdr = (struct ssr_hdr *)resp->data; 79*9675093aSJeffrey Hugo struct ssr_debug_transfer_info_rsp *debug_rsp; 80*9675093aSJeffrey Hugo struct qaic_device *qdev = resp->qdev; 81*9675093aSJeffrey Hugo struct ssr_event_rsp *event_rsp; 82*9675093aSJeffrey Hugo struct dma_bridge_chan *dbc; 83*9675093aSJeffrey Hugo struct ssr_event *event; 84*9675093aSJeffrey Hugo u32 ssr_event_ack; 85*9675093aSJeffrey Hugo int ret; 86*9675093aSJeffrey Hugo 87*9675093aSJeffrey Hugo le32_to_cpus(&hdr->cmd); 88*9675093aSJeffrey Hugo le32_to_cpus(&hdr->len); 89*9675093aSJeffrey Hugo le32_to_cpus(&hdr->dbc_id); 90*9675093aSJeffrey Hugo 91*9675093aSJeffrey Hugo if (hdr->len > SSR_RESP_MSG_SZ) 92*9675093aSJeffrey Hugo goto out; 93*9675093aSJeffrey Hugo 94*9675093aSJeffrey Hugo if (hdr->dbc_id >= qdev->num_dbc) 95*9675093aSJeffrey Hugo goto out; 96*9675093aSJeffrey Hugo 97*9675093aSJeffrey Hugo dbc = &qdev->dbc[hdr->dbc_id]; 98*9675093aSJeffrey Hugo 99*9675093aSJeffrey Hugo switch (hdr->cmd) { 100*9675093aSJeffrey Hugo case DEBUG_TRANSFER_INFO: 101*9675093aSJeffrey Hugo /* Decline crash dump request from the device */ 102*9675093aSJeffrey Hugo debug_rsp = kmalloc(sizeof(*debug_rsp), GFP_KERNEL); 103*9675093aSJeffrey Hugo if (!debug_rsp) 104*9675093aSJeffrey Hugo break; 105*9675093aSJeffrey Hugo 106*9675093aSJeffrey Hugo debug_rsp->hdr.cmd = cpu_to_le32(DEBUG_TRANSFER_INFO_RSP); 107*9675093aSJeffrey Hugo debug_rsp->hdr.len = cpu_to_le32(sizeof(*debug_rsp)); 108*9675093aSJeffrey Hugo debug_rsp->hdr.dbc_id = cpu_to_le32(event->hdr.dbc_id); 109*9675093aSJeffrey Hugo debug_rsp->ret = cpu_to_le32(1); 110*9675093aSJeffrey Hugo 111*9675093aSJeffrey Hugo ret = mhi_queue_buf(qdev->ssr_ch, DMA_TO_DEVICE, 112*9675093aSJeffrey Hugo debug_rsp, sizeof(*debug_rsp), MHI_EOT); 113*9675093aSJeffrey Hugo if (ret) { 114*9675093aSJeffrey Hugo pci_warn(qdev->pdev, "Could not send DEBUG_TRANSFER_INFO_RSP %d\n", ret); 115*9675093aSJeffrey Hugo kfree(debug_rsp); 116*9675093aSJeffrey Hugo } 117*9675093aSJeffrey Hugo return; 118*9675093aSJeffrey Hugo case SSR_EVENT: 119*9675093aSJeffrey Hugo event = (struct ssr_event *)hdr; 120*9675093aSJeffrey Hugo le32_to_cpus(&event->event); 121*9675093aSJeffrey Hugo ssr_event_ack = event->event; 122*9675093aSJeffrey Hugo 123*9675093aSJeffrey Hugo switch (event->event) { 124*9675093aSJeffrey Hugo case BEFORE_SHUTDOWN: 125*9675093aSJeffrey Hugo set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_BEFORE_SHUTDOWN); 126*9675093aSJeffrey Hugo qaic_dbc_enter_ssr(qdev, hdr->dbc_id); 127*9675093aSJeffrey Hugo break; 128*9675093aSJeffrey Hugo case AFTER_SHUTDOWN: 129*9675093aSJeffrey Hugo set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_AFTER_SHUTDOWN); 130*9675093aSJeffrey Hugo break; 131*9675093aSJeffrey Hugo case BEFORE_POWER_UP: 132*9675093aSJeffrey Hugo set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_BEFORE_POWER_UP); 133*9675093aSJeffrey Hugo break; 134*9675093aSJeffrey Hugo case AFTER_POWER_UP: 135*9675093aSJeffrey Hugo set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_AFTER_POWER_UP); 136*9675093aSJeffrey Hugo break; 137*9675093aSJeffrey Hugo default: 138*9675093aSJeffrey Hugo break; 139*9675093aSJeffrey Hugo } 140*9675093aSJeffrey Hugo 141*9675093aSJeffrey Hugo event_rsp = kmalloc(sizeof(*event_rsp), GFP_KERNEL); 142*9675093aSJeffrey Hugo if (!event_rsp) 143*9675093aSJeffrey Hugo break; 144*9675093aSJeffrey Hugo 145*9675093aSJeffrey Hugo event_rsp->hdr.cmd = cpu_to_le32(SSR_EVENT_RSP); 146*9675093aSJeffrey Hugo event_rsp->hdr.len = cpu_to_le32(sizeof(*event_rsp)); 147*9675093aSJeffrey Hugo event_rsp->hdr.dbc_id = cpu_to_le32(hdr->dbc_id); 148*9675093aSJeffrey Hugo event_rsp->event = cpu_to_le32(ssr_event_ack); 149*9675093aSJeffrey Hugo 150*9675093aSJeffrey Hugo ret = mhi_queue_buf(qdev->ssr_ch, DMA_TO_DEVICE, event_rsp, sizeof(*event_rsp), 151*9675093aSJeffrey Hugo MHI_EOT); 152*9675093aSJeffrey Hugo if (ret) 153*9675093aSJeffrey Hugo kfree(event_rsp); 154*9675093aSJeffrey Hugo 155*9675093aSJeffrey Hugo if (event->event == AFTER_POWER_UP) { 156*9675093aSJeffrey Hugo qaic_dbc_exit_ssr(qdev); 157*9675093aSJeffrey Hugo set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_IDLE); 158*9675093aSJeffrey Hugo } 159*9675093aSJeffrey Hugo 160*9675093aSJeffrey Hugo break; 161*9675093aSJeffrey Hugo default: 162*9675093aSJeffrey Hugo break; 163*9675093aSJeffrey Hugo } 164*9675093aSJeffrey Hugo 165*9675093aSJeffrey Hugo out: 166*9675093aSJeffrey Hugo ret = mhi_queue_buf(qdev->ssr_ch, DMA_FROM_DEVICE, resp->data, SSR_RESP_MSG_SZ, MHI_EOT); 167*9675093aSJeffrey Hugo if (ret) 168*9675093aSJeffrey Hugo kfree(resp); 169*9675093aSJeffrey Hugo } 170*9675093aSJeffrey Hugo 171*9675093aSJeffrey Hugo static int qaic_ssr_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id) 172*9675093aSJeffrey Hugo { 173*9675093aSJeffrey Hugo struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev)); 174*9675093aSJeffrey Hugo struct ssr_resp *resp; 175*9675093aSJeffrey Hugo int ret; 176*9675093aSJeffrey Hugo 177*9675093aSJeffrey Hugo ret = mhi_prepare_for_transfer(mhi_dev); 178*9675093aSJeffrey Hugo if (ret) 179*9675093aSJeffrey Hugo return ret; 180*9675093aSJeffrey Hugo 181*9675093aSJeffrey Hugo resp = kzalloc(sizeof(*resp) + SSR_RESP_MSG_SZ, GFP_KERNEL); 182*9675093aSJeffrey Hugo if (!resp) { 183*9675093aSJeffrey Hugo mhi_unprepare_from_transfer(mhi_dev); 184*9675093aSJeffrey Hugo return -ENOMEM; 185*9675093aSJeffrey Hugo } 186*9675093aSJeffrey Hugo 187*9675093aSJeffrey Hugo resp->qdev = qdev; 188*9675093aSJeffrey Hugo INIT_WORK(&resp->work, ssr_worker); 189*9675093aSJeffrey Hugo 190*9675093aSJeffrey Hugo ret = mhi_queue_buf(mhi_dev, DMA_FROM_DEVICE, resp->data, SSR_RESP_MSG_SZ, MHI_EOT); 191*9675093aSJeffrey Hugo if (ret) { 192*9675093aSJeffrey Hugo kfree(resp); 193*9675093aSJeffrey Hugo mhi_unprepare_from_transfer(mhi_dev); 194*9675093aSJeffrey Hugo return ret; 195*9675093aSJeffrey Hugo } 196*9675093aSJeffrey Hugo 197*9675093aSJeffrey Hugo dev_set_drvdata(&mhi_dev->dev, qdev); 198*9675093aSJeffrey Hugo qdev->ssr_ch = mhi_dev; 199*9675093aSJeffrey Hugo 200*9675093aSJeffrey Hugo return 0; 201*9675093aSJeffrey Hugo } 202*9675093aSJeffrey Hugo 203*9675093aSJeffrey Hugo static void qaic_ssr_mhi_remove(struct mhi_device *mhi_dev) 204*9675093aSJeffrey Hugo { 205*9675093aSJeffrey Hugo struct qaic_device *qdev; 206*9675093aSJeffrey Hugo 207*9675093aSJeffrey Hugo qdev = dev_get_drvdata(&mhi_dev->dev); 208*9675093aSJeffrey Hugo mhi_unprepare_from_transfer(qdev->ssr_ch); 209*9675093aSJeffrey Hugo qdev->ssr_ch = NULL; 210*9675093aSJeffrey Hugo } 211*9675093aSJeffrey Hugo 212*9675093aSJeffrey Hugo static void qaic_ssr_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result) 213*9675093aSJeffrey Hugo { 214*9675093aSJeffrey Hugo kfree(mhi_result->buf_addr); 215*9675093aSJeffrey Hugo } 216*9675093aSJeffrey Hugo 217*9675093aSJeffrey Hugo static void qaic_ssr_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result) 218*9675093aSJeffrey Hugo { 219*9675093aSJeffrey Hugo struct ssr_resp *resp = container_of(mhi_result->buf_addr, struct ssr_resp, data); 220*9675093aSJeffrey Hugo struct qaic_device *qdev = dev_get_drvdata(&mhi_dev->dev); 221*9675093aSJeffrey Hugo 222*9675093aSJeffrey Hugo if (mhi_result->transaction_status) { 223*9675093aSJeffrey Hugo kfree(resp); 224*9675093aSJeffrey Hugo return; 225*9675093aSJeffrey Hugo } 226*9675093aSJeffrey Hugo queue_work(qdev->ssr_wq, &resp->work); 227*9675093aSJeffrey Hugo } 228*9675093aSJeffrey Hugo 229*9675093aSJeffrey Hugo static const struct mhi_device_id qaic_ssr_mhi_match_table[] = { 230*9675093aSJeffrey Hugo { .chan = "QAIC_SSR", }, 231*9675093aSJeffrey Hugo {}, 232*9675093aSJeffrey Hugo }; 233*9675093aSJeffrey Hugo 234*9675093aSJeffrey Hugo static struct mhi_driver qaic_ssr_mhi_driver = { 235*9675093aSJeffrey Hugo .id_table = qaic_ssr_mhi_match_table, 236*9675093aSJeffrey Hugo .remove = qaic_ssr_mhi_remove, 237*9675093aSJeffrey Hugo .probe = qaic_ssr_mhi_probe, 238*9675093aSJeffrey Hugo .ul_xfer_cb = qaic_ssr_mhi_ul_xfer_cb, 239*9675093aSJeffrey Hugo .dl_xfer_cb = qaic_ssr_mhi_dl_xfer_cb, 240*9675093aSJeffrey Hugo .driver = { 241*9675093aSJeffrey Hugo .name = "qaic_ssr", 242*9675093aSJeffrey Hugo }, 243*9675093aSJeffrey Hugo }; 244*9675093aSJeffrey Hugo 245*9675093aSJeffrey Hugo int qaic_ssr_init(struct qaic_device *qdev) 246*9675093aSJeffrey Hugo { 247*9675093aSJeffrey Hugo qdev->ssr_dbc = QAIC_SSR_DBC_SENTINEL; 248*9675093aSJeffrey Hugo return 0; 249*9675093aSJeffrey Hugo } 250*9675093aSJeffrey Hugo 251*9675093aSJeffrey Hugo int qaic_ssr_register(void) 252*9675093aSJeffrey Hugo { 253*9675093aSJeffrey Hugo return mhi_driver_register(&qaic_ssr_mhi_driver); 254*9675093aSJeffrey Hugo } 255*9675093aSJeffrey Hugo 256*9675093aSJeffrey Hugo void qaic_ssr_unregister(void) 257*9675093aSJeffrey Hugo { 258*9675093aSJeffrey Hugo mhi_driver_unregister(&qaic_ssr_mhi_driver); 259*9675093aSJeffrey Hugo } 260