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