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 if (!ret) 160 disable_irq(q6v5->handover_irq); 161 162 return !ret ? -ETIMEDOUT : 0; 163 } 164 EXPORT_SYMBOL_GPL(qcom_q6v5_wait_for_start); 165 166 static irqreturn_t q6v5_handover_interrupt(int irq, void *data) 167 { 168 struct qcom_q6v5 *q6v5 = data; 169 170 if (q6v5->handover) 171 q6v5->handover(q6v5); 172 173 icc_set_bw(q6v5->path, 0, 0); 174 175 q6v5->handover_issued = true; 176 177 return IRQ_HANDLED; 178 } 179 180 static irqreturn_t q6v5_stop_interrupt(int irq, void *data) 181 { 182 struct qcom_q6v5 *q6v5 = data; 183 184 complete(&q6v5->stop_done); 185 186 return IRQ_HANDLED; 187 } 188 189 /** 190 * qcom_q6v5_request_stop() - request the remote processor to stop 191 * @q6v5: reference to qcom_q6v5 context 192 * @sysmon: reference to the remote's sysmon instance, or NULL 193 * 194 * Return: 0 on success, negative errno on failure 195 */ 196 int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5, struct qcom_sysmon *sysmon) 197 { 198 int ret; 199 200 q6v5->running = false; 201 202 /* Don't perform SMP2P dance if remote isn't running */ 203 if (q6v5->rproc->state != RPROC_RUNNING || qcom_sysmon_shutdown_acked(sysmon)) 204 return 0; 205 206 qcom_smem_state_update_bits(q6v5->state, 207 BIT(q6v5->stop_bit), BIT(q6v5->stop_bit)); 208 209 ret = wait_for_completion_timeout(&q6v5->stop_done, 5 * HZ); 210 211 qcom_smem_state_update_bits(q6v5->state, BIT(q6v5->stop_bit), 0); 212 213 return ret == 0 ? -ETIMEDOUT : 0; 214 } 215 EXPORT_SYMBOL_GPL(qcom_q6v5_request_stop); 216 217 /** 218 * qcom_q6v5_panic() - panic handler to invoke a stop on the remote 219 * @q6v5: reference to qcom_q6v5 context 220 * 221 * Set the stop bit and sleep in order to allow the remote processor to flush 222 * its caches etc for post mortem debugging. 223 * 224 * Return: 200ms 225 */ 226 unsigned long qcom_q6v5_panic(struct qcom_q6v5 *q6v5) 227 { 228 qcom_smem_state_update_bits(q6v5->state, 229 BIT(q6v5->stop_bit), BIT(q6v5->stop_bit)); 230 231 return Q6V5_PANIC_DELAY_MS; 232 } 233 EXPORT_SYMBOL_GPL(qcom_q6v5_panic); 234 235 /** 236 * qcom_q6v5_init() - initializer of the q6v5 common struct 237 * @q6v5: handle to be initialized 238 * @pdev: platform_device reference for acquiring resources 239 * @rproc: associated remoteproc instance 240 * @crash_reason: SMEM id for crash reason string, or 0 if none 241 * @load_state: load state resource string 242 * @handover: function to be called when proxy resources should be released 243 * 244 * Return: 0 on success, negative errno on failure 245 */ 246 int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev, 247 struct rproc *rproc, int crash_reason, const char *load_state, 248 void (*handover)(struct qcom_q6v5 *q6v5)) 249 { 250 int ret; 251 252 q6v5->rproc = rproc; 253 q6v5->dev = &pdev->dev; 254 q6v5->crash_reason = crash_reason; 255 q6v5->handover = handover; 256 257 init_completion(&q6v5->start_done); 258 init_completion(&q6v5->stop_done); 259 260 q6v5->wdog_irq = platform_get_irq_byname(pdev, "wdog"); 261 if (q6v5->wdog_irq < 0) 262 return q6v5->wdog_irq; 263 264 ret = devm_request_threaded_irq(&pdev->dev, q6v5->wdog_irq, 265 NULL, q6v5_wdog_interrupt, 266 IRQF_TRIGGER_RISING | IRQF_ONESHOT, 267 "q6v5 wdog", q6v5); 268 if (ret) { 269 dev_err(&pdev->dev, "failed to acquire wdog IRQ\n"); 270 return ret; 271 } 272 273 q6v5->fatal_irq = platform_get_irq_byname(pdev, "fatal"); 274 if (q6v5->fatal_irq < 0) 275 return q6v5->fatal_irq; 276 277 ret = devm_request_threaded_irq(&pdev->dev, q6v5->fatal_irq, 278 NULL, q6v5_fatal_interrupt, 279 IRQF_TRIGGER_RISING | IRQF_ONESHOT, 280 "q6v5 fatal", q6v5); 281 if (ret) { 282 dev_err(&pdev->dev, "failed to acquire fatal IRQ\n"); 283 return ret; 284 } 285 286 q6v5->ready_irq = platform_get_irq_byname(pdev, "ready"); 287 if (q6v5->ready_irq < 0) 288 return q6v5->ready_irq; 289 290 ret = devm_request_threaded_irq(&pdev->dev, q6v5->ready_irq, 291 NULL, q6v5_ready_interrupt, 292 IRQF_TRIGGER_RISING | IRQF_ONESHOT, 293 "q6v5 ready", q6v5); 294 if (ret) { 295 dev_err(&pdev->dev, "failed to acquire ready IRQ\n"); 296 return ret; 297 } 298 299 q6v5->handover_irq = platform_get_irq_byname(pdev, "handover"); 300 if (q6v5->handover_irq < 0) 301 return q6v5->handover_irq; 302 303 ret = devm_request_threaded_irq(&pdev->dev, q6v5->handover_irq, 304 NULL, q6v5_handover_interrupt, 305 IRQF_TRIGGER_RISING | IRQF_ONESHOT, 306 "q6v5 handover", q6v5); 307 if (ret) { 308 dev_err(&pdev->dev, "failed to acquire handover IRQ\n"); 309 return ret; 310 } 311 disable_irq(q6v5->handover_irq); 312 313 q6v5->stop_irq = platform_get_irq_byname(pdev, "stop-ack"); 314 if (q6v5->stop_irq < 0) 315 return q6v5->stop_irq; 316 317 ret = devm_request_threaded_irq(&pdev->dev, q6v5->stop_irq, 318 NULL, q6v5_stop_interrupt, 319 IRQF_TRIGGER_RISING | IRQF_ONESHOT, 320 "q6v5 stop", q6v5); 321 if (ret) { 322 dev_err(&pdev->dev, "failed to acquire stop-ack IRQ\n"); 323 return ret; 324 } 325 326 q6v5->state = devm_qcom_smem_state_get(&pdev->dev, "stop", &q6v5->stop_bit); 327 if (IS_ERR(q6v5->state)) { 328 dev_err(&pdev->dev, "failed to acquire stop state\n"); 329 return PTR_ERR(q6v5->state); 330 } 331 332 q6v5->load_state = devm_kstrdup_const(&pdev->dev, load_state, GFP_KERNEL); 333 q6v5->qmp = qmp_get(&pdev->dev); 334 if (IS_ERR(q6v5->qmp)) { 335 if (PTR_ERR(q6v5->qmp) != -ENODEV) 336 return dev_err_probe(&pdev->dev, PTR_ERR(q6v5->qmp), 337 "failed to acquire load state\n"); 338 q6v5->qmp = NULL; 339 } else if (!q6v5->load_state) { 340 if (!load_state) 341 dev_err(&pdev->dev, "load state resource string empty\n"); 342 343 qmp_put(q6v5->qmp); 344 return load_state ? -ENOMEM : -EINVAL; 345 } 346 347 q6v5->path = devm_of_icc_get(&pdev->dev, NULL); 348 if (IS_ERR(q6v5->path)) 349 return dev_err_probe(&pdev->dev, PTR_ERR(q6v5->path), 350 "failed to acquire interconnect path\n"); 351 352 return 0; 353 } 354 EXPORT_SYMBOL_GPL(qcom_q6v5_init); 355 356 /** 357 * qcom_q6v5_deinit() - deinitialize the q6v5 common struct 358 * @q6v5: reference to qcom_q6v5 context to be deinitialized 359 */ 360 void qcom_q6v5_deinit(struct qcom_q6v5 *q6v5) 361 { 362 qmp_put(q6v5->qmp); 363 } 364 EXPORT_SYMBOL_GPL(qcom_q6v5_deinit); 365 366 MODULE_LICENSE("GPL v2"); 367 MODULE_DESCRIPTION("Qualcomm Peripheral Image Loader for Q6V5"); 368