xref: /linux/drivers/accel/qaic/qaic_ssr.c (revision 9675093acea04c7b51ac3a646c7d0ba376b000d6)
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