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