NVIDIA: SAUCE: mailbox: tegra-hsp: add support for multiple shared interrupts

BugLink: https://bugs.launchpad.net/bugs/2072591

Tegra HSP supports up to 8 shared interrupts for shared mailboxes.
Current implementation is limited to use a single shared interrupt for
all shared mailboxes.

Add support for multiple shared interrupts.

http://nvbugs/3939020

Signed-off-by: Kartik <kkartik@nvidia.com>
Tested-by: Abhilash G <abhilashg@nvidia.com>
Reviewed-by: Abhilash G <abhilashg@nvidia.com>
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Acked-by: Jacob Martin <jacob.martin@canonical.com>
Acked-by: Noah Wager <noah.wager@canonical.com>
Signed-off-by: Noah Wager <noah.wager@canonical.com>
This commit is contained in:
Kartik
2023-02-03 13:17:55 +00:00
committed by Noah Wager
parent d83a56ef25
commit dfde7cc74c
+122 -41
View File
@@ -79,8 +79,15 @@ struct tegra_hsp_sm_ops {
void (*recv)(struct tegra_hsp_channel *channel);
};
struct tegra_hsp_shared_interrupt {
unsigned int index;
unsigned int irq;
bool enabled;
};
struct tegra_hsp_mailbox {
struct tegra_hsp_channel channel;
struct tegra_hsp_shared_interrupt *si;
const struct tegra_hsp_sm_ops *ops;
unsigned int index;
bool producer;
@@ -106,8 +113,6 @@ struct tegra_hsp {
struct mbox_controller mbox_sm;
void __iomem *regs;
unsigned int doorbell_irq;
unsigned int *shared_irqs;
unsigned int shared_irq;
unsigned int num_sm;
unsigned int num_as;
unsigned int num_ss;
@@ -119,6 +124,7 @@ struct tegra_hsp {
struct list_head doorbells;
struct tegra_hsp_mailbox *mailboxes;
struct tegra_hsp_shared_interrupt *shared_irqs;
unsigned long mask;
};
@@ -180,6 +186,41 @@ tegra_hsp_doorbell_get(struct tegra_hsp *hsp, unsigned int master)
return db;
}
/*
* Enables or disables interrupt for a mailbox. Should be called with
* hsp->lock held.
*/
static void tegra_hsp_sm_set_irq(struct tegra_hsp_mailbox *mb, bool enable)
{
struct tegra_hsp_shared_interrupt *si = mb->si;
struct tegra_hsp_channel *ch = &mb->channel;
struct tegra_hsp *hsp = mb->channel.hsp;
unsigned int shift;
unsigned int mask;
shift = mb->producer ? HSP_INT_EMPTY_SHIFT : HSP_INT_FULL_SHIFT;
mask = tegra_hsp_readl(hsp, HSP_INT_IE(si->index));
if (enable) {
mask |= BIT(shift + mb->index);
hsp->mask |= BIT(shift + mb->index);
} else {
mask &= ~BIT(shift + mb->index);
hsp->mask &= ~BIT(shift + mb->index);
}
tegra_hsp_writel(hsp, mask, HSP_INT_IE(si->index));
if (hsp->soc->has_per_mb_ie) {
unsigned int reg_ie;
reg_ie = mb->producer ? HSP_SM_SHRD_MBOX_EMPTY_INT_IE :
HSP_SM_SHRD_MBOX_FULL_INT_IE;
tegra_hsp_channel_writel(ch, enable ? 0x1 : 0x0, reg_ie);
}
}
static irqreturn_t tegra_hsp_doorbell_irq(int irq, void *data)
{
struct tegra_hsp *hsp = data;
@@ -243,9 +284,7 @@ static irqreturn_t tegra_hsp_shared_irq(int irq, void *data)
*/
spin_lock(&hsp->lock);
hsp->mask &= ~BIT(HSP_INT_EMPTY_SHIFT + mb->index);
tegra_hsp_writel(hsp, hsp->mask,
HSP_INT_IE(hsp->shared_irq));
tegra_hsp_sm_set_irq(mb, false);
spin_unlock(&hsp->lock);
@@ -465,10 +504,7 @@ static int tegra_hsp_mailbox_send_data(struct mbox_chan *chan, void *data)
/* enable EMPTY interrupt for the shared mailbox */
spin_lock_irqsave(&hsp->lock, flags);
hsp->mask |= BIT(HSP_INT_EMPTY_SHIFT + mb->index);
tegra_hsp_writel(hsp, hsp->mask, HSP_INT_IE(hsp->shared_irq));
tegra_hsp_sm_set_irq(mb, true);
spin_unlock_irqrestore(&hsp->lock, flags);
return 0;
@@ -504,7 +540,6 @@ static int tegra_hsp_mailbox_flush(struct mbox_chan *chan,
static int tegra_hsp_mailbox_startup(struct mbox_chan *chan)
{
struct tegra_hsp_mailbox *mb = chan->con_priv;
struct tegra_hsp_channel *ch = &mb->channel;
struct tegra_hsp *hsp = mb->channel.hsp;
unsigned long flags;
@@ -524,23 +559,12 @@ static int tegra_hsp_mailbox_startup(struct mbox_chan *chan)
spin_lock_irqsave(&hsp->lock, flags);
if (mb->producer)
hsp->mask &= ~BIT(HSP_INT_EMPTY_SHIFT + mb->index);
tegra_hsp_sm_set_irq(mb, false);
else
hsp->mask |= BIT(HSP_INT_FULL_SHIFT + mb->index);
tegra_hsp_writel(hsp, hsp->mask, HSP_INT_IE(hsp->shared_irq));
tegra_hsp_sm_set_irq(mb, true);
spin_unlock_irqrestore(&hsp->lock, flags);
if (hsp->soc->has_per_mb_ie) {
if (mb->producer)
tegra_hsp_channel_writel(ch, 0x0,
HSP_SM_SHRD_MBOX_EMPTY_INT_IE);
else
tegra_hsp_channel_writel(ch, 0x1,
HSP_SM_SHRD_MBOX_FULL_INT_IE);
}
return 0;
}
@@ -561,14 +585,7 @@ static void tegra_hsp_mailbox_shutdown(struct mbox_chan *chan)
}
spin_lock_irqsave(&hsp->lock, flags);
if (mb->producer)
hsp->mask &= ~BIT(HSP_INT_EMPTY_SHIFT + mb->index);
else
hsp->mask &= ~BIT(HSP_INT_FULL_SHIFT + mb->index);
tegra_hsp_writel(hsp, hsp->mask, HSP_INT_IE(hsp->shared_irq));
tegra_hsp_sm_set_irq(mb, false);
spin_unlock_irqrestore(&hsp->lock, flags);
}
@@ -690,13 +707,72 @@ static int tegra_hsp_add_mailboxes(struct tegra_hsp *hsp, struct device *dev)
return 0;
}
static int tegra_hsp_sm_map_shared_interrupts(struct tegra_hsp *hsp,
unsigned int default_si)
{
struct device_node *phandle = hsp->dev->of_node;
unsigned int num_mappings;
unsigned int i;
int err;
/* Assign default shared interrupt. */
for (i = 0; i < hsp->num_sm; i++)
hsp->mailboxes[i].si = &(hsp->shared_irqs[default_si]);
/* Get the configuration from device-tree. */
err = of_property_count_u32_elems(phandle, "nvidia,mailbox-interrupts");
if (err <= 0)
return 0;
num_mappings = err / 2;
for (i = 0; i < num_mappings; i++) {
struct tegra_hsp_shared_interrupt *si;
struct tegra_hsp_mailbox *mb;
unsigned int shared_irq;
unsigned int index;
int err;
err = of_property_read_u32_index(phandle,
"nvidia,mailbox-interrupts",
(i * 2), &index);
if (err || index >= hsp->num_sm)
return -EINVAL;
err = of_property_read_u32_index(phandle,
"nvidia,mailbox-interrupts",
(i * 2) + 1, &shared_irq);
if (err || shared_irq >= hsp->num_si)
return -EINVAL;
si = &hsp->shared_irqs[shared_irq];
mb = &hsp->mailboxes[index];
if (!si->enabled) {
dev_err(hsp->dev,
"shared interrupt: %u is not available\n",
shared_irq);
return -EINVAL;
}
mb->si = si;
dev_dbg(hsp->dev, "shared interrupt: %d mapped to mailbox %d\n",
shared_irq, index);
}
return 0;
}
static int tegra_hsp_request_shared_irq(struct tegra_hsp *hsp)
{
unsigned int i, irq = 0;
unsigned int default_si = hsp->num_si;
unsigned int value;
int err;
for (i = 0; i < hsp->num_si; i++) {
irq = hsp->shared_irqs[i];
irq = hsp->shared_irqs[i].irq;
if (irq <= 0)
continue;
@@ -708,22 +784,26 @@ static int tegra_hsp_request_shared_irq(struct tegra_hsp *hsp)
continue;
}
hsp->shared_irq = i;
hsp->shared_irqs[i].enabled = true;
/* disable all interrupts */
tegra_hsp_writel(hsp, 0, HSP_INT_IE(hsp->shared_irq));
value = tegra_hsp_readl(hsp, HSP_INT_IE(i));
if (value) {
dev_warn(hsp->dev,
"disabling interrupts for si: %d\n", i);
tegra_hsp_writel(hsp, 0, HSP_INT_IE(i));
}
dev_dbg(hsp->dev, "interrupt requested: %u\n", irq);
break;
/* Use first available interrupt as default. */
if (default_si == hsp->num_si)
default_si = i;
}
if (i == hsp->num_si) {
if (default_si == hsp->num_si) {
dev_err(hsp->dev, "failed to find available interrupt\n");
return -ENOENT;
}
return 0;
return tegra_hsp_sm_map_shared_interrupts(hsp, default_si);
}
static int tegra_hsp_probe(struct platform_device *pdev)
@@ -775,7 +855,8 @@ static int tegra_hsp_probe(struct platform_device *pdev)
err = platform_get_irq_byname_optional(pdev, name);
if (err >= 0) {
hsp->shared_irqs[i] = err;
hsp->shared_irqs[i].irq = err;
hsp->shared_irqs[i].index = i;
count++;
}