278 lines
8.0 KiB
Diff
278 lines
8.0 KiB
Diff
From 4cc987cced2179b54ebc0a977ff3ef4210acb38c Mon Sep 17 00:00:00 2001
|
|
From: Romuald JEANNE <romuald.jeanne@st.com>
|
|
Date: Mon, 10 Dec 2018 15:36:09 +0100
|
|
Subject: [PATCH 37/52] ARM: stm32mp1-r0-rc3: IRQ Mailbox
|
|
|
|
---
|
|
drivers/irqchip/irq-stm32-exti.c | 122 ++++++++++++++++++++++++++-------------
|
|
1 file changed, 83 insertions(+), 39 deletions(-)
|
|
|
|
diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c
|
|
index 9cc15f1..223ee2e 100644
|
|
--- a/drivers/irqchip/irq-stm32-exti.c
|
|
+++ b/drivers/irqchip/irq-stm32-exti.c
|
|
@@ -6,6 +6,7 @@
|
|
*/
|
|
|
|
#include <linux/bitops.h>
|
|
+#include <linux/delay.h>
|
|
#include <linux/hwspinlock.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/io.h>
|
|
@@ -21,7 +22,8 @@
|
|
|
|
#define IRQS_PER_BANK 32
|
|
|
|
-#define HWSPINLOCK_TIMEOUT 5 /* msec */
|
|
+#define HWSPNLCK_TIMEOUT 1000 /* usec */
|
|
+#define HWSPNLCK_RETRY_DELAY 100 /* usec */
|
|
|
|
struct stm32_exti_bank {
|
|
u32 imr_ofst;
|
|
@@ -35,6 +37,12 @@ struct stm32_exti_bank {
|
|
|
|
#define UNDEF_REG ~0
|
|
|
|
+enum stm32_exti_hwspinlock {
|
|
+ HWSPINLOCK_UNKNOWN,
|
|
+ HWSPINLOCK_NONE,
|
|
+ HWSPINLOCK_READY,
|
|
+};
|
|
+
|
|
struct stm32_desc_irq {
|
|
u32 exti;
|
|
u32 irq_parent;
|
|
@@ -50,7 +58,6 @@ struct stm32_exti_drv_data {
|
|
struct stm32_exti_chip_data {
|
|
struct stm32_exti_host_data *host_data;
|
|
const struct stm32_exti_bank *reg_bank;
|
|
- struct hwspinlock *hwlock;
|
|
struct raw_spinlock rlock;
|
|
u32 wake_active;
|
|
u32 mask_cache;
|
|
@@ -62,6 +69,9 @@ struct stm32_exti_host_data {
|
|
void __iomem *base;
|
|
struct stm32_exti_chip_data *chips_data;
|
|
const struct stm32_exti_drv_data *drv_data;
|
|
+ struct device_node *node;
|
|
+ enum stm32_exti_hwspinlock hwlock_state;
|
|
+ struct hwspinlock *hwlock;
|
|
};
|
|
|
|
static struct stm32_exti_host_data *stm32_host_data;
|
|
@@ -273,20 +283,75 @@ static int stm32_exti_set_type(struct irq_data *d,
|
|
return 0;
|
|
}
|
|
|
|
+static int stm32_exti_hwspin_lock(struct stm32_exti_chip_data *chip_data)
|
|
+{
|
|
+ struct stm32_exti_host_data *host_data = chip_data->host_data;
|
|
+ struct hwspinlock *hwlock;
|
|
+ int id, ret = 0, timeout = 0;
|
|
+
|
|
+ /* first time, check for hwspinlock availability */
|
|
+ if (unlikely(host_data->hwlock_state == HWSPINLOCK_UNKNOWN)) {
|
|
+ id = of_hwspin_lock_get_id(host_data->node, 0);
|
|
+ if (id >= 0) {
|
|
+ hwlock = hwspin_lock_request_specific(id);
|
|
+ if (hwlock) {
|
|
+ /* found valid hwspinlock */
|
|
+ host_data->hwlock_state = HWSPINLOCK_READY;
|
|
+ host_data->hwlock = hwlock;
|
|
+ pr_debug("%s hwspinlock = %d\n", __func__, id);
|
|
+ } else {
|
|
+ host_data->hwlock_state = HWSPINLOCK_NONE;
|
|
+ }
|
|
+ } else if (id != -EPROBE_DEFER) {
|
|
+ host_data->hwlock_state = HWSPINLOCK_NONE;
|
|
+ } else {
|
|
+ /* hwspinlock driver shall be ready at that stage */
|
|
+ ret = -EPROBE_DEFER;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (likely(host_data->hwlock_state == HWSPINLOCK_READY)) {
|
|
+ /*
|
|
+ * Use the x_raw API since we are under spin_lock protection.
|
|
+ * Do not use the x_timeout API because we are under irq_disable
|
|
+ * mode (see __setup_irq())
|
|
+ */
|
|
+ do {
|
|
+ ret = hwspin_trylock_raw(host_data->hwlock);
|
|
+ if (!ret)
|
|
+ return 0;
|
|
+
|
|
+ udelay(HWSPNLCK_RETRY_DELAY);
|
|
+ timeout += HWSPNLCK_RETRY_DELAY;
|
|
+ } while (timeout < HWSPNLCK_TIMEOUT);
|
|
+
|
|
+ if (ret == -EBUSY)
|
|
+ ret = -ETIMEDOUT;
|
|
+ }
|
|
+
|
|
+ if (ret)
|
|
+ pr_err("%s can't get hwspinlock (%d)\n", __func__, ret);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void stm32_exti_hwspin_unlock(struct stm32_exti_chip_data *chip_data)
|
|
+{
|
|
+ if (likely(chip_data->host_data->hwlock_state == HWSPINLOCK_READY))
|
|
+ hwspin_unlock_raw(chip_data->host_data->hwlock);
|
|
+}
|
|
+
|
|
static int stm32_irq_set_type(struct irq_data *d, unsigned int type)
|
|
{
|
|
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
|
|
struct stm32_exti_chip_data *chip_data = gc->private;
|
|
const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank;
|
|
u32 rtsr, ftsr;
|
|
- int err = 0;
|
|
+ int err;
|
|
|
|
irq_gc_lock(gc);
|
|
|
|
- if (chip_data->hwlock)
|
|
- err = hwspin_lock_timeout(chip_data->hwlock,
|
|
- HWSPINLOCK_TIMEOUT);
|
|
-
|
|
+ err = stm32_exti_hwspin_lock(chip_data);
|
|
if (err)
|
|
goto unlock;
|
|
|
|
@@ -301,8 +366,7 @@ static int stm32_irq_set_type(struct irq_data *d, unsigned int type)
|
|
irq_reg_writel(gc, ftsr, stm32_bank->ftsr_ofst);
|
|
|
|
unspinlock:
|
|
- if (chip_data->hwlock)
|
|
- hwspin_unlock(chip_data->hwlock);
|
|
+ stm32_exti_hwspin_unlock(chip_data);
|
|
unlock:
|
|
irq_gc_unlock(gc);
|
|
|
|
@@ -470,14 +534,11 @@ static int stm32_exti_h_set_type(struct irq_data *d, unsigned int type)
|
|
const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank;
|
|
void __iomem *base = chip_data->host_data->base;
|
|
u32 rtsr, ftsr;
|
|
- int err = 0;
|
|
+ int err;
|
|
|
|
raw_spin_lock(&chip_data->rlock);
|
|
|
|
- if (chip_data->hwlock)
|
|
- err = hwspin_lock_timeout(chip_data->hwlock,
|
|
- HWSPINLOCK_TIMEOUT);
|
|
-
|
|
+ err = stm32_exti_hwspin_lock(chip_data);
|
|
if (err)
|
|
goto unlock;
|
|
|
|
@@ -492,15 +553,14 @@ static int stm32_exti_h_set_type(struct irq_data *d, unsigned int type)
|
|
writel_relaxed(ftsr, base + stm32_bank->ftsr_ofst);
|
|
|
|
unspinlock:
|
|
- if (chip_data->hwlock)
|
|
- hwspin_unlock(chip_data->hwlock);
|
|
+ stm32_exti_hwspin_unlock(chip_data);
|
|
unlock:
|
|
raw_spin_unlock(&chip_data->rlock);
|
|
|
|
if (d->parent_data->chip)
|
|
irq_chip_set_type_parent(d, type);
|
|
|
|
- return 0;
|
|
+ return err;
|
|
}
|
|
|
|
static int stm32_exti_h_set_wake(struct irq_data *d, unsigned int on)
|
|
@@ -650,6 +710,8 @@ stm32_exti_host_data *stm32_exti_host_init(const struct stm32_exti_drv_data *dd,
|
|
return NULL;
|
|
|
|
host_data->drv_data = dd;
|
|
+ host_data->node = node;
|
|
+ host_data->hwlock_state = HWSPINLOCK_UNKNOWN;
|
|
host_data->chips_data = kcalloc(dd->bank_nr,
|
|
sizeof(struct stm32_exti_chip_data),
|
|
GFP_KERNEL);
|
|
@@ -676,13 +738,11 @@ stm32_exti_host_data *stm32_exti_host_init(const struct stm32_exti_drv_data *dd,
|
|
|
|
static struct
|
|
stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data,
|
|
- u32 bank_idx,
|
|
- struct device_node *node)
|
|
+ u32 bank_idx)
|
|
{
|
|
const struct stm32_exti_bank *stm32_bank;
|
|
struct stm32_exti_chip_data *chip_data;
|
|
void __iomem *base = h_data->base;
|
|
- u32 irqs_mask;
|
|
|
|
stm32_bank = h_data->drv_data->exti_banks[bank_idx];
|
|
chip_data = &h_data->chips_data[bank_idx];
|
|
@@ -691,10 +751,6 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data,
|
|
|
|
raw_spin_lock_init(&chip_data->rlock);
|
|
|
|
- /* Determine number of irqs supported */
|
|
- writel_relaxed(~0UL, base + stm32_bank->rtsr_ofst);
|
|
- irqs_mask = readl_relaxed(base + stm32_bank->rtsr_ofst);
|
|
-
|
|
/*
|
|
* This IP has no reset, so after hot reboot we should
|
|
* clear registers to avoid residue
|
|
@@ -702,8 +758,7 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data,
|
|
writel_relaxed(0, base + stm32_bank->imr_ofst);
|
|
writel_relaxed(0, base + stm32_bank->emr_ofst);
|
|
|
|
- pr_info("%s: bank%d, External IRQs available:%#x\n",
|
|
- node->full_name, bank_idx, irqs_mask);
|
|
+ pr_info("%pOF: bank%d\n", h_data->node, bank_idx);
|
|
|
|
return chip_data;
|
|
}
|
|
@@ -716,7 +771,6 @@ static int __init stm32_exti_init(const struct stm32_exti_drv_data *drv_data,
|
|
int nr_irqs, ret, i;
|
|
struct irq_chip_generic *gc;
|
|
struct irq_domain *domain;
|
|
- struct hwspinlock *hwlock = NULL;
|
|
|
|
host_data = stm32_exti_host_init(drv_data, node);
|
|
if (!host_data)
|
|
@@ -739,22 +793,12 @@ static int __init stm32_exti_init(const struct stm32_exti_drv_data *drv_data,
|
|
goto out_free_domain;
|
|
}
|
|
|
|
- /* hwspinlock is optional */
|
|
- ret = of_hwspin_lock_get_id(node, 0);
|
|
- if (ret < 0) {
|
|
- if (ret == -EPROBE_DEFER)
|
|
- goto out_free_domain;
|
|
- } else {
|
|
- hwlock = hwspin_lock_request_specific(ret);
|
|
- }
|
|
-
|
|
for (i = 0; i < drv_data->bank_nr; i++) {
|
|
const struct stm32_exti_bank *stm32_bank;
|
|
struct stm32_exti_chip_data *chip_data;
|
|
|
|
stm32_bank = drv_data->exti_banks[i];
|
|
- chip_data = stm32_exti_chip_init(host_data, i, node);
|
|
- chip_data->hwlock = hwlock;
|
|
+ chip_data = stm32_exti_chip_init(host_data, i);
|
|
|
|
gc = irq_get_domain_generic_chip(domain, i * IRQS_PER_BANK);
|
|
|
|
@@ -836,7 +880,7 @@ __init stm32_exti_hierarchy_init(const struct stm32_exti_drv_data *drv_data,
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < drv_data->bank_nr; i++)
|
|
- stm32_exti_chip_init(host_data, i, node);
|
|
+ stm32_exti_chip_init(host_data, i);
|
|
|
|
domain = irq_domain_add_hierarchy(parent_domain, 0,
|
|
drv_data->bank_nr * IRQS_PER_BANK,
|
|
--
|
|
2.7.4
|
|
|