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/interconnect.h> 12 #include <linux/interrupt.h> 13 #include <linux/module.h> 14 #include <linux/soc/qcom/qcom_aoss.h> 15 #include <linux/soc/qcom/smem.h> 16 #include <linux/soc/qcom/smem_state.h> 17 #include <linux/remoteproc.h> 18 #include "qcom_common.h" 19 #include "qcom_q6v5.h" 20 21 #define Q6V5_LOAD_STATE_MSG_LEN 64 22 #define Q6V5_PANIC_DELAY_MS 200 23 24 static int q6v5_load_state_toggle(struct qcom_q6v5 *q6v5, bool enable) 25 { 26 int ret; 27 28 if (!q6v5->qmp) 29 return 0; 30 31 ret = qmp_send(q6v5->qmp, "{class: image, res: load_state, name: %s, val: %s}", 32 q6v5->load_state, enable ? "on" : "off"); 33 if (ret) 34 dev_err(q6v5->dev, "failed to toggle load state\n"); 35 36 return ret; 37 } 38 39 static void q6v5_handover_irq_enable(struct qcom_q6v5 *q6v5) 40 { 41 unsigned long flags; 42 bool enable = false; 43 44 spin_lock_irqsave(&q6v5->handover_lock, flags); 45 if (!q6v5->handover_irq_enabled) { 46 q6v5->handover_irq_enabled = true; 47 enable = true; 48 } 49 spin_unlock_irqrestore(&q6v5->handover_lock, flags); 50 51 if (enable) 52 enable_irq(q6v5->handover_irq); 53 } 54 55 static void q6v5_handover_irq_disable(struct qcom_q6v5 *q6v5, bool sync) 56 { 57 unsigned long flags; 58 bool disable = false; 59 60 spin_lock_irqsave(&q6v5->handover_lock, flags); 61 if (q6v5->handover_irq_enabled) { 62 q6v5->handover_irq_enabled = false; 63 disable = true; 64 } 65 spin_unlock_irqrestore(&q6v5->handover_lock, flags); 66 67 if (disable) 68 disable_irq_nosync(q6v5->handover_irq); 69 if (sync) 70 synchronize_irq(q6v5->handover_irq); 71 } 72 73 /** 74 * qcom_q6v5_prepare() - reinitialize the qcom_q6v5 context before start 75 * @q6v5: reference to qcom_q6v5 context to be reinitialized 76 * 77 * Return: 0 on success, negative errno on failure 78 */ 79 int qcom_q6v5_prepare(struct qcom_q6v5 *q6v5) 80 { 81 int ret; 82 83 ret = icc_set_bw(q6v5->path, 0, UINT_MAX); 84 if (ret < 0) { 85 dev_err(q6v5->dev, "failed to set bandwidth request\n"); 86 return ret; 87 } 88 89 ret = q6v5_load_state_toggle(q6v5, true); 90 if (ret) { 91 icc_set_bw(q6v5->path, 0, 0); 92 return ret; 93 } 94 95 reinit_completion(&q6v5->start_done); 96 reinit_completion(&q6v5->stop_done); 97 98 q6v5->running = true; 99 q6v5->handover_issued = false; 100 101 q6v5_handover_irq_enable(q6v5); 102 103 return 0; 104 } 105 EXPORT_SYMBOL_GPL(qcom_q6v5_prepare); 106 107 /** 108 * qcom_q6v5_unprepare() - unprepare the qcom_q6v5 context after stop 109 * @q6v5: reference to qcom_q6v5 context to be unprepared 110 * 111 * Return: 0 on success, 1 if handover hasn't yet been called 112 */ 113 int qcom_q6v5_unprepare(struct qcom_q6v5 *q6v5) 114 { 115 q6v5_handover_irq_disable(q6v5, true); 116 117 q6v5_load_state_toggle(q6v5, false); 118 119 /* Disable interconnect vote, in case handover never happened */ 120 icc_set_bw(q6v5->path, 0, 0); 121 122 return !q6v5->handover_issued; 123 } 124 EXPORT_SYMBOL_GPL(qcom_q6v5_unprepare); 125 126 static irqreturn_t q6v5_wdog_interrupt(int irq, void *data) 127 { 128 struct qcom_q6v5 *q6v5 = data; 129 size_t len; 130 char *msg; 131 132 /* Sometimes the stop triggers a watchdog rather than a stop-ack */ 133 if (!q6v5->running) { 134 complete(&q6v5->stop_done); 135 return IRQ_HANDLED; 136 } 137 138 msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, q6v5->crash_reason, &len); 139 if (!IS_ERR(msg) && len > 0 && msg[0]) 140 dev_err(q6v5->dev, "watchdog received: %s\n", msg); 141 else 142 dev_err(q6v5->dev, "watchdog without message\n"); 143 144 q6v5->running = false; 145 rproc_report_crash(q6v5->rproc, RPROC_WATCHDOG); 146 147 return IRQ_HANDLED; 148 } 149 150 static irqreturn_t q6v5_fatal_interrupt(int irq, void *data) 151 { 152 struct qcom_q6v5 *q6v5 = data; 153 size_t len; 154 char *msg; 155 156 if (!q6v5->running) 157 return IRQ_HANDLED; 158 159 msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, q6v5->crash_reason, &len); 160 if (!IS_ERR(msg) && len > 0 && msg[0]) 161 dev_err(q6v5->dev, "fatal error received: %s\n", msg); 162 else 163 dev_err(q6v5->dev, "fatal error without message\n"); 164 165 q6v5->running = false; 166 rproc_report_crash(q6v5->rproc, RPROC_FATAL_ERROR); 167 168 return IRQ_HANDLED; 169 } 170 171 static irqreturn_t q6v5_ready_interrupt(int irq, void *data) 172 { 173 struct qcom_q6v5 *q6v5 = data; 174 175 complete(&q6v5->start_done); 176 177 return IRQ_HANDLED; 178 } 179 180 /** 181 * qcom_q6v5_wait_for_start() - wait for remote processor start signal 182 * @q6v5: reference to qcom_q6v5 context 183 * @timeout: timeout to wait for the event, in jiffies 184 * 185 * qcom_q6v5_unprepare() should not be called when this function fails. 186 * 187 * Return: 0 on success, -ETIMEDOUT on timeout 188 */ 189 int qcom_q6v5_wait_for_start(struct qcom_q6v5 *q6v5, int timeout) 190 { 191 int ret; 192 193 ret = wait_for_completion_timeout(&q6v5->start_done, timeout); 194 return !ret ? -ETIMEDOUT : 0; 195 } 196 EXPORT_SYMBOL_GPL(qcom_q6v5_wait_for_start); 197 198 static irqreturn_t q6v5_handover_interrupt(int irq, void *data) 199 { 200 struct qcom_q6v5 *q6v5 = data; 201 202 q6v5->handover_issued = true; 203 204 q6v5_handover_irq_disable(q6v5, false); 205 206 if (q6v5->handover) 207 q6v5->handover(q6v5); 208 209 icc_set_bw(q6v5->path, 0, 0); 210 211 return IRQ_HANDLED; 212 } 213 214 static irqreturn_t q6v5_stop_interrupt(int irq, void *data) 215 { 216 struct qcom_q6v5 *q6v5 = data; 217 218 complete(&q6v5->stop_done); 219 220 return IRQ_HANDLED; 221 } 222 223 /** 224 * qcom_q6v5_request_stop() - request the remote processor to stop 225 * @q6v5: reference to qcom_q6v5 context 226 * @sysmon: reference to the remote's sysmon instance, or NULL 227 * 228 * Return: 0 on success, negative errno on failure 229 */ 230 int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5, struct qcom_sysmon *sysmon) 231 { 232 bool was_running = q6v5->running; 233 int ret; 234 235 q6v5->running = false; 236 237 /* A watchdog/fatal IRQ clears running; logical crashes still need a stop. */ 238 if (!was_running || qcom_sysmon_shutdown_acked(sysmon)) 239 return 0; 240 241 qcom_smem_state_update_bits(q6v5->state, 242 BIT(q6v5->stop_bit), BIT(q6v5->stop_bit)); 243 244 ret = wait_for_completion_timeout(&q6v5->stop_done, 5 * HZ); 245 246 qcom_smem_state_update_bits(q6v5->state, BIT(q6v5->stop_bit), 0); 247 248 return ret == 0 ? -ETIMEDOUT : 0; 249 } 250 EXPORT_SYMBOL_GPL(qcom_q6v5_request_stop); 251 252 /** 253 * qcom_q6v5_panic() - panic handler to invoke a stop on the remote 254 * @q6v5: reference to qcom_q6v5 context 255 * 256 * Set the stop bit and sleep in order to allow the remote processor to flush 257 * its caches etc for post mortem debugging. 258 * 259 * Return: 200ms 260 */ 261 unsigned long qcom_q6v5_panic(struct qcom_q6v5 *q6v5) 262 { 263 qcom_smem_state_update_bits(q6v5->state, 264 BIT(q6v5->stop_bit), BIT(q6v5->stop_bit)); 265 266 return Q6V5_PANIC_DELAY_MS; 267 } 268 EXPORT_SYMBOL_GPL(qcom_q6v5_panic); 269 270 /** 271 * qcom_q6v5_init() - initializer of the q6v5 common struct 272 * @q6v5: handle to be initialized 273 * @pdev: platform_device reference for acquiring resources 274 * @rproc: associated remoteproc instance 275 * @crash_reason: SMEM id for crash reason string, or 0 if none 276 * @load_state: load state resource string 277 * @handover: function to be called when proxy resources should be released 278 * 279 * Return: 0 on success, negative errno on failure 280 */ 281 int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev, 282 struct rproc *rproc, int crash_reason, const char *load_state, 283 void (*handover)(struct qcom_q6v5 *q6v5)) 284 { 285 int ret; 286 287 q6v5->rproc = rproc; 288 q6v5->dev = &pdev->dev; 289 q6v5->crash_reason = crash_reason; 290 q6v5->handover = handover; 291 292 spin_lock_init(&q6v5->handover_lock); 293 294 init_completion(&q6v5->start_done); 295 init_completion(&q6v5->stop_done); 296 297 q6v5->wdog_irq = platform_get_irq_byname(pdev, "wdog"); 298 if (q6v5->wdog_irq < 0) 299 return q6v5->wdog_irq; 300 301 ret = devm_request_threaded_irq(&pdev->dev, q6v5->wdog_irq, 302 NULL, q6v5_wdog_interrupt, 303 IRQF_TRIGGER_RISING | IRQF_ONESHOT, 304 "q6v5 wdog", q6v5); 305 if (ret) 306 return ret; 307 308 q6v5->fatal_irq = platform_get_irq_byname(pdev, "fatal"); 309 if (q6v5->fatal_irq < 0) 310 return q6v5->fatal_irq; 311 312 ret = devm_request_threaded_irq(&pdev->dev, q6v5->fatal_irq, 313 NULL, q6v5_fatal_interrupt, 314 IRQF_TRIGGER_RISING | IRQF_ONESHOT, 315 "q6v5 fatal", q6v5); 316 if (ret) 317 return ret; 318 319 q6v5->ready_irq = platform_get_irq_byname(pdev, "ready"); 320 if (q6v5->ready_irq < 0) 321 return q6v5->ready_irq; 322 323 ret = devm_request_threaded_irq(&pdev->dev, q6v5->ready_irq, 324 NULL, q6v5_ready_interrupt, 325 IRQF_TRIGGER_RISING | IRQF_ONESHOT, 326 "q6v5 ready", q6v5); 327 if (ret) 328 return ret; 329 330 q6v5->handover_irq = platform_get_irq_byname(pdev, "handover"); 331 if (q6v5->handover_irq < 0) 332 return q6v5->handover_irq; 333 334 ret = devm_request_threaded_irq(&pdev->dev, q6v5->handover_irq, 335 NULL, q6v5_handover_interrupt, 336 IRQF_TRIGGER_RISING | IRQF_ONESHOT | 337 IRQF_NO_AUTOEN, 338 "q6v5 handover", q6v5); 339 if (ret) 340 return ret; 341 342 q6v5->stop_irq = platform_get_irq_byname(pdev, "stop-ack"); 343 if (q6v5->stop_irq < 0) 344 return q6v5->stop_irq; 345 346 ret = devm_request_threaded_irq(&pdev->dev, q6v5->stop_irq, 347 NULL, q6v5_stop_interrupt, 348 IRQF_TRIGGER_RISING | IRQF_ONESHOT, 349 "q6v5 stop", q6v5); 350 if (ret) 351 return ret; 352 353 q6v5->state = devm_qcom_smem_state_get(&pdev->dev, "stop", &q6v5->stop_bit); 354 if (IS_ERR(q6v5->state)) { 355 dev_err(&pdev->dev, "failed to acquire stop state\n"); 356 return PTR_ERR(q6v5->state); 357 } 358 359 q6v5->load_state = devm_kstrdup_const(&pdev->dev, load_state, GFP_KERNEL); 360 q6v5->qmp = qmp_get(&pdev->dev); 361 if (IS_ERR(q6v5->qmp)) { 362 if (PTR_ERR(q6v5->qmp) != -ENODEV) 363 return dev_err_probe(&pdev->dev, PTR_ERR(q6v5->qmp), 364 "failed to acquire load state\n"); 365 q6v5->qmp = NULL; 366 } else if (!q6v5->load_state) { 367 if (!load_state) 368 dev_err(&pdev->dev, "load state resource string empty\n"); 369 370 qmp_put(q6v5->qmp); 371 return load_state ? -ENOMEM : -EINVAL; 372 } 373 374 q6v5->path = devm_of_icc_get(&pdev->dev, NULL); 375 if (IS_ERR(q6v5->path)) 376 return dev_err_probe(&pdev->dev, PTR_ERR(q6v5->path), 377 "failed to acquire interconnect path\n"); 378 379 return 0; 380 } 381 EXPORT_SYMBOL_GPL(qcom_q6v5_init); 382 383 /** 384 * qcom_q6v5_deinit() - deinitialize the q6v5 common struct 385 * @q6v5: reference to qcom_q6v5 context to be deinitialized 386 */ 387 void qcom_q6v5_deinit(struct qcom_q6v5 *q6v5) 388 { 389 qmp_put(q6v5->qmp); 390 } 391 EXPORT_SYMBOL_GPL(qcom_q6v5_deinit); 392 393 MODULE_LICENSE("GPL v2"); 394 MODULE_DESCRIPTION("Qualcomm Peripheral Image Loader for Q6V5"); 395