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