xref: /OK3568_Linux_fs/kernel/drivers/remoteproc/qcom_q6v5.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Qualcomm Peripheral Image Loader for Q6V5
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2016-2018 Linaro Ltd.
6*4882a593Smuzhiyun  * Copyright (C) 2014 Sony Mobile Communications AB
7*4882a593Smuzhiyun  * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun #include <linux/kernel.h>
10*4882a593Smuzhiyun #include <linux/platform_device.h>
11*4882a593Smuzhiyun #include <linux/interrupt.h>
12*4882a593Smuzhiyun #include <linux/module.h>
13*4882a593Smuzhiyun #include <linux/soc/qcom/smem.h>
14*4882a593Smuzhiyun #include <linux/soc/qcom/smem_state.h>
15*4882a593Smuzhiyun #include <linux/remoteproc.h>
16*4882a593Smuzhiyun #include "qcom_q6v5.h"
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #define Q6V5_PANIC_DELAY_MS	200
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun /**
21*4882a593Smuzhiyun  * qcom_q6v5_prepare() - reinitialize the qcom_q6v5 context before start
22*4882a593Smuzhiyun  * @q6v5:	reference to qcom_q6v5 context to be reinitialized
23*4882a593Smuzhiyun  *
24*4882a593Smuzhiyun  * Return: 0 on success, negative errno on failure
25*4882a593Smuzhiyun  */
qcom_q6v5_prepare(struct qcom_q6v5 * q6v5)26*4882a593Smuzhiyun int qcom_q6v5_prepare(struct qcom_q6v5 *q6v5)
27*4882a593Smuzhiyun {
28*4882a593Smuzhiyun 	reinit_completion(&q6v5->start_done);
29*4882a593Smuzhiyun 	reinit_completion(&q6v5->stop_done);
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun 	q6v5->running = true;
32*4882a593Smuzhiyun 	q6v5->handover_issued = false;
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun 	enable_irq(q6v5->handover_irq);
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun 	return 0;
37*4882a593Smuzhiyun }
38*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(qcom_q6v5_prepare);
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun /**
41*4882a593Smuzhiyun  * qcom_q6v5_unprepare() - unprepare the qcom_q6v5 context after stop
42*4882a593Smuzhiyun  * @q6v5:	reference to qcom_q6v5 context to be unprepared
43*4882a593Smuzhiyun  *
44*4882a593Smuzhiyun  * Return: 0 on success, 1 if handover hasn't yet been called
45*4882a593Smuzhiyun  */
qcom_q6v5_unprepare(struct qcom_q6v5 * q6v5)46*4882a593Smuzhiyun int qcom_q6v5_unprepare(struct qcom_q6v5 *q6v5)
47*4882a593Smuzhiyun {
48*4882a593Smuzhiyun 	disable_irq(q6v5->handover_irq);
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun 	return !q6v5->handover_issued;
51*4882a593Smuzhiyun }
52*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(qcom_q6v5_unprepare);
53*4882a593Smuzhiyun 
q6v5_wdog_interrupt(int irq,void * data)54*4882a593Smuzhiyun static irqreturn_t q6v5_wdog_interrupt(int irq, void *data)
55*4882a593Smuzhiyun {
56*4882a593Smuzhiyun 	struct qcom_q6v5 *q6v5 = data;
57*4882a593Smuzhiyun 	size_t len;
58*4882a593Smuzhiyun 	char *msg;
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	/* Sometimes the stop triggers a watchdog rather than a stop-ack */
61*4882a593Smuzhiyun 	if (!q6v5->running) {
62*4882a593Smuzhiyun 		complete(&q6v5->stop_done);
63*4882a593Smuzhiyun 		return IRQ_HANDLED;
64*4882a593Smuzhiyun 	}
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, q6v5->crash_reason, &len);
67*4882a593Smuzhiyun 	if (!IS_ERR(msg) && len > 0 && msg[0])
68*4882a593Smuzhiyun 		dev_err(q6v5->dev, "watchdog received: %s\n", msg);
69*4882a593Smuzhiyun 	else
70*4882a593Smuzhiyun 		dev_err(q6v5->dev, "watchdog without message\n");
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun 	rproc_report_crash(q6v5->rproc, RPROC_WATCHDOG);
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 	return IRQ_HANDLED;
75*4882a593Smuzhiyun }
76*4882a593Smuzhiyun 
q6v5_fatal_interrupt(int irq,void * data)77*4882a593Smuzhiyun static irqreturn_t q6v5_fatal_interrupt(int irq, void *data)
78*4882a593Smuzhiyun {
79*4882a593Smuzhiyun 	struct qcom_q6v5 *q6v5 = data;
80*4882a593Smuzhiyun 	size_t len;
81*4882a593Smuzhiyun 	char *msg;
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 	msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, q6v5->crash_reason, &len);
84*4882a593Smuzhiyun 	if (!IS_ERR(msg) && len > 0 && msg[0])
85*4882a593Smuzhiyun 		dev_err(q6v5->dev, "fatal error received: %s\n", msg);
86*4882a593Smuzhiyun 	else
87*4882a593Smuzhiyun 		dev_err(q6v5->dev, "fatal error without message\n");
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	q6v5->running = false;
90*4882a593Smuzhiyun 	rproc_report_crash(q6v5->rproc, RPROC_FATAL_ERROR);
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	return IRQ_HANDLED;
93*4882a593Smuzhiyun }
94*4882a593Smuzhiyun 
q6v5_ready_interrupt(int irq,void * data)95*4882a593Smuzhiyun static irqreturn_t q6v5_ready_interrupt(int irq, void *data)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun 	struct qcom_q6v5 *q6v5 = data;
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	complete(&q6v5->start_done);
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	return IRQ_HANDLED;
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun /**
105*4882a593Smuzhiyun  * qcom_q6v5_wait_for_start() - wait for remote processor start signal
106*4882a593Smuzhiyun  * @q6v5:	reference to qcom_q6v5 context
107*4882a593Smuzhiyun  * @timeout:	timeout to wait for the event, in jiffies
108*4882a593Smuzhiyun  *
109*4882a593Smuzhiyun  * qcom_q6v5_unprepare() should not be called when this function fails.
110*4882a593Smuzhiyun  *
111*4882a593Smuzhiyun  * Return: 0 on success, -ETIMEDOUT on timeout
112*4882a593Smuzhiyun  */
qcom_q6v5_wait_for_start(struct qcom_q6v5 * q6v5,int timeout)113*4882a593Smuzhiyun int qcom_q6v5_wait_for_start(struct qcom_q6v5 *q6v5, int timeout)
114*4882a593Smuzhiyun {
115*4882a593Smuzhiyun 	int ret;
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	ret = wait_for_completion_timeout(&q6v5->start_done, timeout);
118*4882a593Smuzhiyun 	if (!ret)
119*4882a593Smuzhiyun 		disable_irq(q6v5->handover_irq);
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	return !ret ? -ETIMEDOUT : 0;
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(qcom_q6v5_wait_for_start);
124*4882a593Smuzhiyun 
q6v5_handover_interrupt(int irq,void * data)125*4882a593Smuzhiyun static irqreturn_t q6v5_handover_interrupt(int irq, void *data)
126*4882a593Smuzhiyun {
127*4882a593Smuzhiyun 	struct qcom_q6v5 *q6v5 = data;
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 	if (q6v5->handover)
130*4882a593Smuzhiyun 		q6v5->handover(q6v5);
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun 	q6v5->handover_issued = true;
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	return IRQ_HANDLED;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun 
q6v5_stop_interrupt(int irq,void * data)137*4882a593Smuzhiyun static irqreturn_t q6v5_stop_interrupt(int irq, void *data)
138*4882a593Smuzhiyun {
139*4882a593Smuzhiyun 	struct qcom_q6v5 *q6v5 = data;
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	complete(&q6v5->stop_done);
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	return IRQ_HANDLED;
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun /**
147*4882a593Smuzhiyun  * qcom_q6v5_request_stop() - request the remote processor to stop
148*4882a593Smuzhiyun  * @q6v5:	reference to qcom_q6v5 context
149*4882a593Smuzhiyun  *
150*4882a593Smuzhiyun  * Return: 0 on success, negative errno on failure
151*4882a593Smuzhiyun  */
qcom_q6v5_request_stop(struct qcom_q6v5 * q6v5)152*4882a593Smuzhiyun int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun 	int ret;
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	q6v5->running = false;
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	qcom_smem_state_update_bits(q6v5->state,
159*4882a593Smuzhiyun 				    BIT(q6v5->stop_bit), BIT(q6v5->stop_bit));
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun 	ret = wait_for_completion_timeout(&q6v5->stop_done, 5 * HZ);
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	qcom_smem_state_update_bits(q6v5->state, BIT(q6v5->stop_bit), 0);
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 	return ret == 0 ? -ETIMEDOUT : 0;
166*4882a593Smuzhiyun }
167*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(qcom_q6v5_request_stop);
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun /**
170*4882a593Smuzhiyun  * qcom_q6v5_panic() - panic handler to invoke a stop on the remote
171*4882a593Smuzhiyun  * @q6v5:	reference to qcom_q6v5 context
172*4882a593Smuzhiyun  *
173*4882a593Smuzhiyun  * Set the stop bit and sleep in order to allow the remote processor to flush
174*4882a593Smuzhiyun  * its caches etc for post mortem debugging.
175*4882a593Smuzhiyun  *
176*4882a593Smuzhiyun  * Return: 200ms
177*4882a593Smuzhiyun  */
qcom_q6v5_panic(struct qcom_q6v5 * q6v5)178*4882a593Smuzhiyun unsigned long qcom_q6v5_panic(struct qcom_q6v5 *q6v5)
179*4882a593Smuzhiyun {
180*4882a593Smuzhiyun 	qcom_smem_state_update_bits(q6v5->state,
181*4882a593Smuzhiyun 				    BIT(q6v5->stop_bit), BIT(q6v5->stop_bit));
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun 	return Q6V5_PANIC_DELAY_MS;
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(qcom_q6v5_panic);
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun /**
188*4882a593Smuzhiyun  * qcom_q6v5_init() - initializer of the q6v5 common struct
189*4882a593Smuzhiyun  * @q6v5:	handle to be initialized
190*4882a593Smuzhiyun  * @pdev:	platform_device reference for acquiring resources
191*4882a593Smuzhiyun  * @rproc:	associated remoteproc instance
192*4882a593Smuzhiyun  * @crash_reason: SMEM id for crash reason string, or 0 if none
193*4882a593Smuzhiyun  * @handover:	function to be called when proxy resources should be released
194*4882a593Smuzhiyun  *
195*4882a593Smuzhiyun  * Return: 0 on success, negative errno on failure
196*4882a593Smuzhiyun  */
qcom_q6v5_init(struct qcom_q6v5 * q6v5,struct platform_device * pdev,struct rproc * rproc,int crash_reason,void (* handover)(struct qcom_q6v5 * q6v5))197*4882a593Smuzhiyun int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev,
198*4882a593Smuzhiyun 		   struct rproc *rproc, int crash_reason,
199*4882a593Smuzhiyun 		   void (*handover)(struct qcom_q6v5 *q6v5))
200*4882a593Smuzhiyun {
201*4882a593Smuzhiyun 	int ret;
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	q6v5->rproc = rproc;
204*4882a593Smuzhiyun 	q6v5->dev = &pdev->dev;
205*4882a593Smuzhiyun 	q6v5->crash_reason = crash_reason;
206*4882a593Smuzhiyun 	q6v5->handover = handover;
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun 	init_completion(&q6v5->start_done);
209*4882a593Smuzhiyun 	init_completion(&q6v5->stop_done);
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 	q6v5->wdog_irq = platform_get_irq_byname(pdev, "wdog");
212*4882a593Smuzhiyun 	if (q6v5->wdog_irq < 0)
213*4882a593Smuzhiyun 		return q6v5->wdog_irq;
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun 	ret = devm_request_threaded_irq(&pdev->dev, q6v5->wdog_irq,
216*4882a593Smuzhiyun 					NULL, q6v5_wdog_interrupt,
217*4882a593Smuzhiyun 					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
218*4882a593Smuzhiyun 					"q6v5 wdog", q6v5);
219*4882a593Smuzhiyun 	if (ret) {
220*4882a593Smuzhiyun 		dev_err(&pdev->dev, "failed to acquire wdog IRQ\n");
221*4882a593Smuzhiyun 		return ret;
222*4882a593Smuzhiyun 	}
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 	q6v5->fatal_irq = platform_get_irq_byname(pdev, "fatal");
225*4882a593Smuzhiyun 	if (q6v5->fatal_irq < 0)
226*4882a593Smuzhiyun 		return q6v5->fatal_irq;
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	ret = devm_request_threaded_irq(&pdev->dev, q6v5->fatal_irq,
229*4882a593Smuzhiyun 					NULL, q6v5_fatal_interrupt,
230*4882a593Smuzhiyun 					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
231*4882a593Smuzhiyun 					"q6v5 fatal", q6v5);
232*4882a593Smuzhiyun 	if (ret) {
233*4882a593Smuzhiyun 		dev_err(&pdev->dev, "failed to acquire fatal IRQ\n");
234*4882a593Smuzhiyun 		return ret;
235*4882a593Smuzhiyun 	}
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	q6v5->ready_irq = platform_get_irq_byname(pdev, "ready");
238*4882a593Smuzhiyun 	if (q6v5->ready_irq < 0)
239*4882a593Smuzhiyun 		return q6v5->ready_irq;
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun 	ret = devm_request_threaded_irq(&pdev->dev, q6v5->ready_irq,
242*4882a593Smuzhiyun 					NULL, q6v5_ready_interrupt,
243*4882a593Smuzhiyun 					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
244*4882a593Smuzhiyun 					"q6v5 ready", q6v5);
245*4882a593Smuzhiyun 	if (ret) {
246*4882a593Smuzhiyun 		dev_err(&pdev->dev, "failed to acquire ready IRQ\n");
247*4882a593Smuzhiyun 		return ret;
248*4882a593Smuzhiyun 	}
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun 	q6v5->handover_irq = platform_get_irq_byname(pdev, "handover");
251*4882a593Smuzhiyun 	if (q6v5->handover_irq < 0)
252*4882a593Smuzhiyun 		return q6v5->handover_irq;
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 	ret = devm_request_threaded_irq(&pdev->dev, q6v5->handover_irq,
255*4882a593Smuzhiyun 					NULL, q6v5_handover_interrupt,
256*4882a593Smuzhiyun 					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
257*4882a593Smuzhiyun 					"q6v5 handover", q6v5);
258*4882a593Smuzhiyun 	if (ret) {
259*4882a593Smuzhiyun 		dev_err(&pdev->dev, "failed to acquire handover IRQ\n");
260*4882a593Smuzhiyun 		return ret;
261*4882a593Smuzhiyun 	}
262*4882a593Smuzhiyun 	disable_irq(q6v5->handover_irq);
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun 	q6v5->stop_irq = platform_get_irq_byname(pdev, "stop-ack");
265*4882a593Smuzhiyun 	if (q6v5->stop_irq < 0)
266*4882a593Smuzhiyun 		return q6v5->stop_irq;
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 	ret = devm_request_threaded_irq(&pdev->dev, q6v5->stop_irq,
269*4882a593Smuzhiyun 					NULL, q6v5_stop_interrupt,
270*4882a593Smuzhiyun 					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
271*4882a593Smuzhiyun 					"q6v5 stop", q6v5);
272*4882a593Smuzhiyun 	if (ret) {
273*4882a593Smuzhiyun 		dev_err(&pdev->dev, "failed to acquire stop-ack IRQ\n");
274*4882a593Smuzhiyun 		return ret;
275*4882a593Smuzhiyun 	}
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 	q6v5->state = qcom_smem_state_get(&pdev->dev, "stop", &q6v5->stop_bit);
278*4882a593Smuzhiyun 	if (IS_ERR(q6v5->state)) {
279*4882a593Smuzhiyun 		dev_err(&pdev->dev, "failed to acquire stop state\n");
280*4882a593Smuzhiyun 		return PTR_ERR(q6v5->state);
281*4882a593Smuzhiyun 	}
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 	return 0;
284*4882a593Smuzhiyun }
285*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(qcom_q6v5_init);
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
288*4882a593Smuzhiyun MODULE_DESCRIPTION("Qualcomm Peripheral Image Loader for Q6V5");
289