summaryrefslogtreecommitdiff
path: root/drivers/net/ipa/gsi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ipa/gsi.c')
-rw-r--r--drivers/net/ipa/gsi.c255
1 files changed, 185 insertions, 70 deletions
diff --git a/drivers/net/ipa/gsi.c b/drivers/net/ipa/gsi.c
index 12a2001ee1e9..961a11d4fb27 100644
--- a/drivers/net/ipa/gsi.c
+++ b/drivers/net/ipa/gsi.c
@@ -230,21 +230,70 @@ static u32 gsi_channel_id(struct gsi_channel *channel)
return channel - &channel->gsi->channel[0];
}
+/* Update the GSI IRQ type register with the cached value */
+static void gsi_irq_type_update(struct gsi *gsi, u32 val)
+{
+ gsi->type_enabled_bitmap = val;
+ iowrite32(val, gsi->virt + GSI_CNTXT_TYPE_IRQ_MSK_OFFSET);
+}
+
+static void gsi_irq_type_enable(struct gsi *gsi, enum gsi_irq_type_id type_id)
+{
+ gsi_irq_type_update(gsi, gsi->type_enabled_bitmap | BIT(type_id));
+}
+
+static void gsi_irq_type_disable(struct gsi *gsi, enum gsi_irq_type_id type_id)
+{
+ gsi_irq_type_update(gsi, gsi->type_enabled_bitmap & ~BIT(type_id));
+}
+
+/* Turn off all GSI interrupts initially */
+static void gsi_irq_setup(struct gsi *gsi)
+{
+ /* Disable all interrupt types */
+ gsi_irq_type_update(gsi, 0);
+
+ /* Clear all type-specific interrupt masks */
+ iowrite32(0, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_MSK_OFFSET);
+ iowrite32(0, gsi->virt + GSI_CNTXT_SRC_EV_CH_IRQ_MSK_OFFSET);
+ iowrite32(0, gsi->virt + GSI_CNTXT_GLOB_IRQ_EN_OFFSET);
+ iowrite32(0, gsi->virt + GSI_CNTXT_SRC_IEOB_IRQ_MSK_OFFSET);
+ iowrite32(0, gsi->virt + GSI_INTER_EE_SRC_CH_IRQ_OFFSET);
+ iowrite32(0, gsi->virt + GSI_INTER_EE_SRC_EV_CH_IRQ_OFFSET);
+ iowrite32(0, gsi->virt + GSI_CNTXT_GSI_IRQ_EN_OFFSET);
+}
+
+/* Turn off all GSI interrupts when we're all done */
+static void gsi_irq_teardown(struct gsi *gsi)
+{
+ /* Nothing to do */
+}
+
static void gsi_irq_ieob_enable(struct gsi *gsi, u32 evt_ring_id)
{
+ bool enable_ieob = !gsi->ieob_enabled_bitmap;
u32 val;
- gsi->event_enable_bitmap |= BIT(evt_ring_id);
- val = gsi->event_enable_bitmap;
+ gsi->ieob_enabled_bitmap |= BIT(evt_ring_id);
+ val = gsi->ieob_enabled_bitmap;
iowrite32(val, gsi->virt + GSI_CNTXT_SRC_IEOB_IRQ_MSK_OFFSET);
+
+ /* Enable the interrupt type if this is the first channel enabled */
+ if (enable_ieob)
+ gsi_irq_type_enable(gsi, GSI_IEOB);
}
static void gsi_irq_ieob_disable(struct gsi *gsi, u32 evt_ring_id)
{
u32 val;
- gsi->event_enable_bitmap &= ~BIT(evt_ring_id);
- val = gsi->event_enable_bitmap;
+ gsi->ieob_enabled_bitmap &= ~BIT(evt_ring_id);
+
+ /* Disable the interrupt type if this was the last enabled channel */
+ if (!gsi->ieob_enabled_bitmap)
+ gsi_irq_type_disable(gsi, GSI_IEOB);
+
+ val = gsi->ieob_enabled_bitmap;
iowrite32(val, gsi->virt + GSI_CNTXT_SRC_IEOB_IRQ_MSK_OFFSET);
}
@@ -253,38 +302,32 @@ static void gsi_irq_enable(struct gsi *gsi)
{
u32 val;
- /* We don't use inter-EE channel or event interrupts */
- val = GSI_CNTXT_TYPE_IRQ_MSK_ALL;
- val &= ~INTER_EE_CH_CTRL_FMASK;
- val &= ~INTER_EE_EV_CTRL_FMASK;
- iowrite32(val, gsi->virt + GSI_CNTXT_TYPE_IRQ_MSK_OFFSET);
-
- val = GENMASK(gsi->channel_count - 1, 0);
- iowrite32(val, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_MSK_OFFSET);
-
- val = GENMASK(gsi->evt_ring_count - 1, 0);
- iowrite32(val, gsi->virt + GSI_CNTXT_SRC_EV_CH_IRQ_MSK_OFFSET);
-
- /* Each IEOB interrupt is enabled (later) as needed by channels */
- iowrite32(0, gsi->virt + GSI_CNTXT_SRC_IEOB_IRQ_MSK_OFFSET);
-
- val = GSI_CNTXT_GLOB_IRQ_ALL;
- iowrite32(val, gsi->virt + GSI_CNTXT_GLOB_IRQ_EN_OFFSET);
+ /* Global interrupts include hardware error reports. Enable
+ * that so we can at least report the error should it occur.
+ */
+ iowrite32(ERROR_INT_FMASK, gsi->virt + GSI_CNTXT_GLOB_IRQ_EN_OFFSET);
+ gsi_irq_type_update(gsi, gsi->type_enabled_bitmap | BIT(GSI_GLOB_EE));
- /* Never enable GSI_BREAK_POINT */
- val = GSI_CNTXT_GSI_IRQ_ALL & ~BREAK_POINT_FMASK;
+ /* General GSI interrupts are reported to all EEs; if they occur
+ * they are unrecoverable (without reset). A breakpoint interrupt
+ * also exists, but we don't support that. We want to be notified
+ * of errors so we can report them, even if they can't be handled.
+ */
+ val = BUS_ERROR_FMASK;
+ val |= CMD_FIFO_OVRFLOW_FMASK;
+ val |= MCS_STACK_OVRFLOW_FMASK;
iowrite32(val, gsi->virt + GSI_CNTXT_GSI_IRQ_EN_OFFSET);
+ gsi_irq_type_update(gsi, gsi->type_enabled_bitmap | BIT(GSI_GENERAL));
}
-/* Disable all GSI_interrupt types */
+/* Disable all GSI interrupt types */
static void gsi_irq_disable(struct gsi *gsi)
{
+ gsi_irq_type_update(gsi, 0);
+
+ /* Clear the type-specific interrupt masks set by gsi_irq_enable() */
iowrite32(0, gsi->virt + GSI_CNTXT_GSI_IRQ_EN_OFFSET);
iowrite32(0, gsi->virt + GSI_CNTXT_GLOB_IRQ_EN_OFFSET);
- iowrite32(0, gsi->virt + GSI_CNTXT_SRC_IEOB_IRQ_MSK_OFFSET);
- iowrite32(0, gsi->virt + GSI_CNTXT_SRC_EV_CH_IRQ_MSK_OFFSET);
- iowrite32(0, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_MSK_OFFSET);
- iowrite32(0, gsi->virt + GSI_CNTXT_TYPE_IRQ_MSK_OFFSET);
}
/* Return the virtual address associated with a ring index */
@@ -338,13 +381,30 @@ static int evt_ring_command(struct gsi *gsi, u32 evt_ring_id,
struct gsi_evt_ring *evt_ring = &gsi->evt_ring[evt_ring_id];
struct completion *completion = &evt_ring->completion;
struct device *dev = gsi->dev;
+ bool success;
u32 val;
+ /* We only perform one event ring command at a time, and event
+ * control interrupts should only occur when such a command
+ * is issued here. Only permit *this* event ring to trigger
+ * an interrupt, and only enable the event control IRQ type
+ * when we expect it to occur.
+ */
+ val = BIT(evt_ring_id);
+ iowrite32(val, gsi->virt + GSI_CNTXT_SRC_EV_CH_IRQ_MSK_OFFSET);
+ gsi_irq_type_enable(gsi, GSI_EV_CTRL);
+
val = u32_encode_bits(evt_ring_id, EV_CHID_FMASK);
val |= u32_encode_bits(opcode, EV_OPCODE_FMASK);
- if (gsi_command(gsi, GSI_EV_CH_CMD_OFFSET, val, completion))
- return 0; /* Success! */
+ success = gsi_command(gsi, GSI_EV_CH_CMD_OFFSET, val, completion);
+
+ /* Disable the interrupt again */
+ gsi_irq_type_disable(gsi, GSI_EV_CTRL);
+ iowrite32(0, gsi->virt + GSI_CNTXT_SRC_EV_CH_IRQ_MSK_OFFSET);
+
+ if (success)
+ return 0;
dev_err(dev, "GSI command %u for event ring %u timed out, state %u\n",
opcode, evt_ring_id, evt_ring->state);
@@ -434,13 +494,29 @@ gsi_channel_command(struct gsi_channel *channel, enum gsi_ch_cmd_opcode opcode)
u32 channel_id = gsi_channel_id(channel);
struct gsi *gsi = channel->gsi;
struct device *dev = gsi->dev;
+ bool success;
u32 val;
+ /* We only perform one channel command at a time, and channel
+ * control interrupts should only occur when such a command is
+ * issued here. So we only permit *this* channel to trigger
+ * an interrupt and only enable the channel control IRQ type
+ * when we expect it to occur.
+ */
+ val = BIT(channel_id);
+ iowrite32(val, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_MSK_OFFSET);
+ gsi_irq_type_enable(gsi, GSI_CH_CTRL);
+
val = u32_encode_bits(channel_id, CH_CHID_FMASK);
val |= u32_encode_bits(opcode, CH_OPCODE_FMASK);
+ success = gsi_command(gsi, GSI_CH_CMD_OFFSET, val, completion);
- if (gsi_command(gsi, GSI_CH_CMD_OFFSET, val, completion))
- return 0; /* Success! */
+ /* Disable the interrupt again */
+ gsi_irq_type_disable(gsi, GSI_CH_CTRL);
+ iowrite32(0, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_MSK_OFFSET);
+
+ if (success)
+ return 0;
dev_err(dev, "GSI command %u for channel %u timed out, state %u\n",
opcode, channel_id, gsi_channel_state(channel));
@@ -1036,8 +1112,8 @@ static void gsi_isr_glob_err(struct gsi *gsi)
iowrite32(~0, gsi->virt + GSI_ERROR_LOG_CLR_OFFSET);
ee = u32_get_bits(val, ERR_EE_FMASK);
- which = u32_get_bits(val, ERR_VIRT_IDX_FMASK);
type = u32_get_bits(val, ERR_TYPE_FMASK);
+ which = u32_get_bits(val, ERR_VIRT_IDX_FMASK);
code = u32_get_bits(val, ERR_CODE_FMASK);
if (type == GSI_ERR_TYPE_CHAN)
@@ -1112,8 +1188,7 @@ static void gsi_isr_general(struct gsi *gsi)
val = ioread32(gsi->virt + GSI_CNTXT_GSI_IRQ_STTS_OFFSET);
iowrite32(val, gsi->virt + GSI_CNTXT_GSI_IRQ_CLR_OFFSET);
- if (val)
- dev_err(dev, "unexpected general interrupt 0x%08x\n", val);
+ dev_err(dev, "unexpected general interrupt 0x%08x\n", val);
}
/**
@@ -1130,6 +1205,7 @@ static irqreturn_t gsi_isr(int irq, void *dev_id)
u32 intr_mask;
u32 cnt = 0;
+ /* enum gsi_irq_type_id defines GSI interrupt types */
while ((intr_mask = ioread32(gsi->virt + GSI_CNTXT_TYPE_IRQ_OFFSET))) {
/* intr_mask contains bitmask of pending GSI interrupts */
do {
@@ -1138,19 +1214,19 @@ static irqreturn_t gsi_isr(int irq, void *dev_id)
intr_mask ^= gsi_intr;
switch (gsi_intr) {
- case CH_CTRL_FMASK:
+ case BIT(GSI_CH_CTRL):
gsi_isr_chan_ctrl(gsi);
break;
- case EV_CTRL_FMASK:
+ case BIT(GSI_EV_CTRL):
gsi_isr_evt_ctrl(gsi);
break;
- case GLOB_EE_FMASK:
+ case BIT(GSI_GLOB_EE):
gsi_isr_glob_ee(gsi);
break;
- case IEOB_FMASK:
+ case BIT(GSI_IEOB):
gsi_isr_ieob(gsi);
break;
- case GENERAL_FMASK:
+ case BIT(GSI_GENERAL):
gsi_isr_general(gsi);
break;
default:
@@ -1170,6 +1246,34 @@ static irqreturn_t gsi_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static int gsi_irq_init(struct gsi *gsi, struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ unsigned int irq;
+ int ret;
+
+ ret = platform_get_irq_byname(pdev, "gsi");
+ if (ret <= 0) {
+ dev_err(dev, "DT error %d getting \"gsi\" IRQ property\n", ret);
+ return ret ? : -EINVAL;
+ }
+ irq = ret;
+
+ ret = request_irq(irq, gsi_isr, 0, "gsi", gsi);
+ if (ret) {
+ dev_err(dev, "error %d requesting \"gsi\" IRQ\n", ret);
+ return ret;
+ }
+ gsi->irq = irq;
+
+ return 0;
+}
+
+static void gsi_irq_exit(struct gsi *gsi)
+{
+ free_irq(gsi->irq, gsi);
+}
+
/* Return the transaction associated with a transfer completion event */
static struct gsi_trans *gsi_event_trans(struct gsi_channel *channel,
struct gsi_event *event)
@@ -1512,8 +1616,19 @@ static int gsi_generic_command(struct gsi *gsi, u32 channel_id,
enum gsi_generic_cmd_opcode opcode)
{
struct completion *completion = &gsi->completion;
+ bool success;
u32 val;
+ /* The error global interrupt type is always enabled (until we
+ * teardown), so we won't change that. A generic EE command
+ * completes with a GSI global interrupt of type GP_INT1. We
+ * only perform one generic command at a time (to allocate or
+ * halt a modem channel) and only from this function. So we
+ * enable the GP_INT1 IRQ type here while we're expecting it.
+ */
+ val = ERROR_INT_FMASK | GP_INT1_FMASK;
+ iowrite32(val, gsi->virt + GSI_CNTXT_GLOB_IRQ_EN_OFFSET);
+
/* First zero the result code field */
val = ioread32(gsi->virt + GSI_CNTXT_SCRATCH_0_OFFSET);
val &= ~GENERIC_EE_RESULT_FMASK;
@@ -1524,8 +1639,13 @@ static int gsi_generic_command(struct gsi *gsi, u32 channel_id,
val |= u32_encode_bits(channel_id, GENERIC_CHID_FMASK);
val |= u32_encode_bits(GSI_EE_MODEM, GENERIC_EE_FMASK);
- if (gsi_command(gsi, GSI_GENERIC_CMD_OFFSET, val, completion))
- return 0; /* Success! */
+ success = gsi_command(gsi, GSI_GENERIC_CMD_OFFSET, val, completion);
+
+ /* Disable the GP_INT1 IRQ type again */
+ iowrite32(ERROR_INT_FMASK, gsi->virt + GSI_CNTXT_GLOB_IRQ_EN_OFFSET);
+
+ if (success)
+ return 0;
dev_err(gsi->dev, "GSI generic command %u to channel %u timed out\n",
opcode, channel_id);
@@ -1652,6 +1772,7 @@ int gsi_setup(struct gsi *gsi)
{
struct device *dev = gsi->dev;
u32 val;
+ int ret;
/* Here is where we first touch the GSI hardware */
val = ioread32(gsi->virt + GSI_GSI_STATUS_OFFSET);
@@ -1660,6 +1781,8 @@ int gsi_setup(struct gsi *gsi)
return -EIO;
}
+ gsi_irq_setup(gsi);
+
val = ioread32(gsi->virt + GSI_GSI_HW_PARAM_2_OFFSET);
gsi->channel_count = u32_get_bits(val, NUM_CH_PER_EE_FMASK);
@@ -1692,13 +1815,18 @@ int gsi_setup(struct gsi *gsi)
/* Writing 1 indicates IRQ interrupts; 0 would be MSI */
iowrite32(1, gsi->virt + GSI_CNTXT_INTSET_OFFSET);
- return gsi_channel_setup(gsi);
+ ret = gsi_channel_setup(gsi);
+ if (ret)
+ gsi_irq_teardown(gsi);
+
+ return ret;
}
/* Inverse of gsi_setup() */
void gsi_teardown(struct gsi *gsi)
{
gsi_channel_teardown(gsi);
+ gsi_irq_teardown(gsi);
}
/* Initialize a channel's event ring */
@@ -1746,7 +1874,7 @@ static void gsi_evt_ring_init(struct gsi *gsi)
u32 evt_ring_id = 0;
gsi->event_bitmap = gsi_event_bitmap_init(GSI_EVT_RING_COUNT_MAX);
- gsi->event_enable_bitmap = 0;
+ gsi->ieob_enabled_bitmap = 0;
do
init_completion(&gsi->evt_ring[evt_ring_id].completion);
while (++evt_ring_id < GSI_EVT_RING_COUNT_MAX);
@@ -1962,7 +2090,6 @@ int gsi_init(struct gsi *gsi, struct platform_device *pdev,
struct device *dev = &pdev->dev;
struct resource *res;
resource_size_t size;
- unsigned int irq;
int ret;
gsi_validate_build();
@@ -1976,55 +2103,43 @@ int gsi_init(struct gsi *gsi, struct platform_device *pdev,
*/
init_dummy_netdev(&gsi->dummy_dev);
- ret = platform_get_irq_byname(pdev, "gsi");
- if (ret <= 0) {
- dev_err(dev, "DT error %d getting \"gsi\" IRQ property\n", ret);
- return ret ? : -EINVAL;
- }
- irq = ret;
-
- ret = request_irq(irq, gsi_isr, 0, "gsi", gsi);
- if (ret) {
- dev_err(dev, "error %d requesting \"gsi\" IRQ\n", ret);
- return ret;
- }
- gsi->irq = irq;
-
/* Get GSI memory range and map it */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gsi");
if (!res) {
dev_err(dev, "DT error getting \"gsi\" memory property\n");
- ret = -ENODEV;
- goto err_free_irq;
+ return -ENODEV;
}
size = resource_size(res);
if (res->start > U32_MAX || size > U32_MAX - res->start) {
dev_err(dev, "DT memory resource \"gsi\" out of range\n");
- ret = -EINVAL;
- goto err_free_irq;
+ return -EINVAL;
}
gsi->virt = ioremap(res->start, size);
if (!gsi->virt) {
dev_err(dev, "unable to remap \"gsi\" memory\n");
- ret = -ENOMEM;
- goto err_free_irq;
+ return -ENOMEM;
}
- ret = gsi_channel_init(gsi, count, data);
+ init_completion(&gsi->completion);
+
+ ret = gsi_irq_init(gsi, pdev);
if (ret)
goto err_iounmap;
+ ret = gsi_channel_init(gsi, count, data);
+ if (ret)
+ goto err_irq_exit;
+
mutex_init(&gsi->mutex);
- init_completion(&gsi->completion);
return 0;
+err_irq_exit:
+ gsi_irq_exit(gsi);
err_iounmap:
iounmap(gsi->virt);
-err_free_irq:
- free_irq(gsi->irq, gsi);
return ret;
}
@@ -2034,7 +2149,7 @@ void gsi_exit(struct gsi *gsi)
{
mutex_destroy(&gsi->mutex);
gsi_channel_exit(gsi);
- free_irq(gsi->irq, gsi);
+ gsi_irq_exit(gsi);
iounmap(gsi->virt);
}