Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions drivers/remoteproc/qcom_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc,
int ssctl_instance);
void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon);
bool qcom_sysmon_shutdown_acked(struct qcom_sysmon *sysmon);
bool qcom_sysmon_shutdown_irq_state(struct qcom_sysmon *sysmon);
#else
static inline struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc,
const char *name,
Expand All @@ -84,6 +85,11 @@ static inline bool qcom_sysmon_shutdown_acked(struct qcom_sysmon *sysmon)
{
return false;
}

static inline bool qcom_sysmon_shutdown_irq_state(struct qcom_sysmon *sysmon)
{
return false;
}
#endif

#endif
57 changes: 46 additions & 11 deletions drivers/remoteproc/qcom_q6v5.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,40 @@ static int q6v5_load_state_toggle(struct qcom_q6v5 *q6v5, bool enable)
return ret;
}

static void q6v5_handover_irq_enable(struct qcom_q6v5 *q6v5)
{
unsigned long flags;
bool enable = false;

spin_lock_irqsave(&q6v5->handover_lock, flags);
if (!q6v5->handover_irq_enabled) {
q6v5->handover_irq_enabled = true;
enable = true;
}
spin_unlock_irqrestore(&q6v5->handover_lock, flags);

if (enable)
enable_irq(q6v5->handover_irq);
}

static void q6v5_handover_irq_disable(struct qcom_q6v5 *q6v5, bool sync)
{
unsigned long flags;
bool disable = false;

spin_lock_irqsave(&q6v5->handover_lock, flags);
if (q6v5->handover_irq_enabled) {
q6v5->handover_irq_enabled = false;
disable = true;
}
spin_unlock_irqrestore(&q6v5->handover_lock, flags);

if (disable)
disable_irq_nosync(q6v5->handover_irq);
if (sync)
synchronize_irq(q6v5->handover_irq);
}

/**
* qcom_q6v5_prepare() - reinitialize the qcom_q6v5 context before start
* @q6v5: reference to qcom_q6v5 context to be reinitialized
Expand Down Expand Up @@ -65,7 +99,7 @@ int qcom_q6v5_prepare(struct qcom_q6v5 *q6v5)
q6v5->running = true;
q6v5->handover_issued = false;

enable_irq(q6v5->handover_irq);
q6v5_handover_irq_enable(q6v5);

return 0;
}
Expand All @@ -79,7 +113,8 @@ EXPORT_SYMBOL_GPL(qcom_q6v5_prepare);
*/
int qcom_q6v5_unprepare(struct qcom_q6v5 *q6v5)
{
disable_irq(q6v5->handover_irq);
q6v5_handover_irq_disable(q6v5, true);

q6v5_load_state_toggle(q6v5, false);

/* Disable interconnect vote, in case handover never happened */
Expand Down Expand Up @@ -165,18 +200,15 @@ static irqreturn_t q6v5_handover_interrupt(int irq, void *data)
{
struct qcom_q6v5 *q6v5 = data;

if (q6v5->handover_issued) {
dev_err(q6v5->dev, "Handover signaled, but it already happened\n");
return IRQ_HANDLED;
}
q6v5->handover_issued = true;

q6v5_handover_irq_disable(q6v5, false);

if (q6v5->handover)
q6v5->handover(q6v5);

icc_set_bw(q6v5->path, 0, 0);

q6v5->handover_issued = true;

return IRQ_HANDLED;
}

Expand All @@ -203,7 +235,8 @@ int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5, struct qcom_sysmon *sysmon)
q6v5->running = false;

/* Don't perform SMP2P dance if remote isn't running */
if (q6v5->rproc->state != RPROC_RUNNING || qcom_sysmon_shutdown_acked(sysmon))
if ((q6v5->rproc->state != RPROC_RUNNING && q6v5->rproc->state != RPROC_ATTACHED) ||
qcom_sysmon_shutdown_acked(sysmon))
return 0;

qcom_smem_state_update_bits(q6v5->state,
Expand Down Expand Up @@ -325,6 +358,8 @@ int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev,
q6v5->crash_reason = crash_reason;
q6v5->handover = handover;

spin_lock_init(&q6v5->handover_lock);

init_completion(&q6v5->start_done);
init_completion(&q6v5->stop_done);

Expand Down Expand Up @@ -373,13 +408,13 @@ int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev,

ret = devm_request_threaded_irq(&pdev->dev, q6v5->handover_irq,
NULL, q6v5_handover_interrupt,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
IRQF_TRIGGER_RISING | IRQF_ONESHOT |
IRQF_NO_AUTOEN,
"q6v5 handover", q6v5);
if (ret) {
dev_err(&pdev->dev, "failed to acquire handover IRQ\n");
return ret;
}
disable_irq(q6v5->handover_irq);

q6v5->stop_irq = platform_get_irq_byname(pdev, "stop-ack");
if (q6v5->stop_irq < 0)
Expand Down
4 changes: 4 additions & 0 deletions drivers/remoteproc/qcom_q6v5.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <linux/kernel.h>
#include <linux/completion.h>
#include <linux/spinlock.h>
#include <linux/soc/qcom/qcom_aoss.h>

struct icc_path;
Expand Down Expand Up @@ -32,6 +33,9 @@ struct qcom_q6v5 {
int stop_irq;
int pong_irq;

/* Protects handover_irq_enabled against stop/handover races. */
spinlock_t handover_lock;
bool handover_irq_enabled;
bool handover_issued;

struct completion start_done;
Expand Down
36 changes: 19 additions & 17 deletions drivers/remoteproc/qcom_q6v5_pas.c
Original file line number Diff line number Diff line change
Expand Up @@ -561,15 +561,17 @@ static void qcom_pas_coredump(struct rproc *rproc)

static int qcom_pas_attach(struct rproc *rproc)
{
int ret;
struct qcom_pas *pas = rproc->priv;
bool ready_state;
bool crash_state;
bool stop_state;
int ret;

pas->q6v5.handover_issued = true;

pas->q6v5.running = true;
ret = irq_get_irqchip_state(pas->q6v5.fatal_irq,
IRQCHIP_STATE_LINE_LEVEL, &crash_state);

if (ret)
goto disable_running;

Expand All @@ -580,9 +582,18 @@ static int qcom_pas_attach(struct rproc *rproc)
goto disable_running;
}

ret = irq_get_irqchip_state(pas->q6v5.stop_irq,
IRQCHIP_STATE_LINE_LEVEL, &stop_state);
if (ret)
goto disable_running;

if (stop_state || qcom_sysmon_shutdown_irq_state(pas->sysmon)) {
dev_info(pas->dev, "Subsystem found stop state set. Falling back to start.\n");
goto unroll_attach;
}

ret = irq_get_irqchip_state(pas->q6v5.ready_irq,
IRQCHIP_STATE_LINE_LEVEL, &ready_state);

if (ret)
goto disable_running;

Expand All @@ -593,23 +604,14 @@ static int qcom_pas_attach(struct rproc *rproc)
* start the remoteproc.
*/
dev_err(pas->dev, "Failed to get subsystem ready interrupt\n");
pas->rproc->state = RPROC_OFFLINE;
ret = -EINVAL;
goto disable_running;
}

ret = qcom_q6v5_ping_subsystem(&pas->q6v5);

if (ret) {
dev_err(pas->dev, "Failed to ping subsystem, assuming device crashed\n");
rproc_report_crash(rproc, RPROC_FATAL_ERROR);
goto disable_running;
goto unroll_attach;
}

pas->q6v5.handover_issued = true;

return 0;

unroll_attach:
pas->rproc->state = RPROC_OFFLINE;
ret = -EINVAL;
disable_running:
pas->q6v5.running = false;

Expand Down Expand Up @@ -637,6 +639,7 @@ static const struct rproc_ops qcom_pas_minidump_ops = {
.load = qcom_pas_load,
.panic = qcom_pas_panic,
.coredump = qcom_pas_minidump,
.attach = qcom_pas_attach,
};

static int qcom_pas_init_clock(struct qcom_pas *pas)
Expand Down Expand Up @@ -1020,7 +1023,6 @@ static int qcom_pas_probe(struct platform_device *pdev)
pas->rproc->state = RPROC_DETACHED;
}


ret = qcom_pas_setup_tmd(pas);
if (ret)
goto remove_ssr_sysmon;
Expand Down
19 changes: 19 additions & 0 deletions drivers/remoteproc/qcom_sysmon.c
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,25 @@ bool qcom_sysmon_shutdown_acked(struct qcom_sysmon *sysmon)
}
EXPORT_SYMBOL_GPL(qcom_sysmon_shutdown_acked);

bool qcom_sysmon_shutdown_irq_state(struct qcom_sysmon *sysmon)
{
bool shutdown_state;
int ret;

if (!sysmon)
return false;

ret = irq_get_irqchip_state(sysmon->shutdown_irq,
IRQCHIP_STATE_LINE_LEVEL, &shutdown_state);
if (ret) {
dev_warn(sysmon->dev, "failed to get shutdown_state: %d\n", ret);
return false;
}

return shutdown_state;
}
EXPORT_SYMBOL_GPL(qcom_sysmon_shutdown_irq_state);

/**
* sysmon_probe() - probe sys_mon channel
* @rpdev: rpmsg device handle
Expand Down
Loading