root/drivers/remoteproc/qcom_q6v5.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. qcom_q6v5_prepare
  2. qcom_q6v5_unprepare
  3. q6v5_wdog_interrupt
  4. q6v5_fatal_interrupt
  5. q6v5_ready_interrupt
  6. qcom_q6v5_wait_for_start
  7. q6v5_handover_interrupt
  8. q6v5_stop_interrupt
  9. qcom_q6v5_request_stop
  10. qcom_q6v5_init

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

/* [<][>][^][v][top][bottom][index][help] */