Merge branch 'net-ipa-gsi-interrupt-updates'
Alex Elder says: ==================== net: ipa: GSI interrupt updates This series implements some updates for the GSI interrupt code, buliding on some bug fixes implemented last month. The first two are simple changes made to improve readability and consistency. The third replaces all msleep() calls with comparable usleep_range() calls. The remainder make some more substantive changes to make the code align with recommendations from Qualcomm. The fourth implements a much shorter timeout for completion GSI commands, and the fifth implements a longer delay between retries of the STOP channel command. Finally, the last implements retries for stopping TX channels (in addition to RX channels). ==================== Link: https://lore.kernel.org/r/20210113171532.19248-1-elder@linaro.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Коммит
3d33c00361
|
@ -89,9 +89,9 @@
|
|||
/* Delay period for interrupt moderation (in 32KHz IPA internal timer ticks) */
|
||||
#define GSI_EVT_RING_INT_MODT (32 * 1) /* 1ms under 32KHz clock */
|
||||
|
||||
#define GSI_CMD_TIMEOUT 5 /* seconds */
|
||||
#define GSI_CMD_TIMEOUT 50 /* milliseconds */
|
||||
|
||||
#define GSI_CHANNEL_STOP_RX_RETRIES 10
|
||||
#define GSI_CHANNEL_STOP_RETRIES 10
|
||||
#define GSI_CHANNEL_MODEM_HALT_RETRIES 10
|
||||
|
||||
#define GSI_MHI_EVENT_ID_START 10 /* 1st reserved event id */
|
||||
|
@ -220,6 +220,58 @@ static void gsi_irq_teardown(struct gsi *gsi)
|
|||
/* Nothing to do */
|
||||
}
|
||||
|
||||
/* Event ring commands are performed one at a time. Their completion
|
||||
* is signaled by the event ring control GSI interrupt type, which is
|
||||
* only enabled when we issue an event ring command. Only the event
|
||||
* ring being operated on has this interrupt enabled.
|
||||
*/
|
||||
static void gsi_irq_ev_ctrl_enable(struct gsi *gsi, u32 evt_ring_id)
|
||||
{
|
||||
u32 val = BIT(evt_ring_id);
|
||||
|
||||
/* There's a small chance that a previous command completed
|
||||
* after the interrupt was disabled, so make sure we have no
|
||||
* pending interrupts before we enable them.
|
||||
*/
|
||||
iowrite32(~0, gsi->virt + GSI_CNTXT_SRC_EV_CH_IRQ_CLR_OFFSET);
|
||||
|
||||
iowrite32(val, gsi->virt + GSI_CNTXT_SRC_EV_CH_IRQ_MSK_OFFSET);
|
||||
gsi_irq_type_enable(gsi, GSI_EV_CTRL);
|
||||
}
|
||||
|
||||
/* Disable event ring control interrupts */
|
||||
static void gsi_irq_ev_ctrl_disable(struct gsi *gsi)
|
||||
{
|
||||
gsi_irq_type_disable(gsi, GSI_EV_CTRL);
|
||||
iowrite32(0, gsi->virt + GSI_CNTXT_SRC_EV_CH_IRQ_MSK_OFFSET);
|
||||
}
|
||||
|
||||
/* Channel commands are performed one at a time. Their completion is
|
||||
* signaled by the channel control GSI interrupt type, which is only
|
||||
* enabled when we issue a channel command. Only the channel being
|
||||
* operated on has this interrupt enabled.
|
||||
*/
|
||||
static void gsi_irq_ch_ctrl_enable(struct gsi *gsi, u32 channel_id)
|
||||
{
|
||||
u32 val = BIT(channel_id);
|
||||
|
||||
/* There's a small chance that a previous command completed
|
||||
* after the interrupt was disabled, so make sure we have no
|
||||
* pending interrupts before we enable them.
|
||||
*/
|
||||
iowrite32(~0, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_CLR_OFFSET);
|
||||
|
||||
iowrite32(val, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_MSK_OFFSET);
|
||||
gsi_irq_type_enable(gsi, GSI_CH_CTRL);
|
||||
}
|
||||
|
||||
/* Disable channel control interrupts */
|
||||
static void gsi_irq_ch_ctrl_disable(struct gsi *gsi)
|
||||
{
|
||||
gsi_irq_type_disable(gsi, GSI_CH_CTRL);
|
||||
iowrite32(0, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_MSK_OFFSET);
|
||||
}
|
||||
|
||||
static void gsi_irq_ieob_enable(struct gsi *gsi, u32 evt_ring_id)
|
||||
{
|
||||
bool enable_ieob = !gsi->ieob_enabled_bitmap;
|
||||
|
@ -307,11 +359,13 @@ static u32 gsi_ring_index(struct gsi_ring *ring, u32 offset)
|
|||
static bool
|
||||
gsi_command(struct gsi *gsi, u32 reg, u32 val, struct completion *completion)
|
||||
{
|
||||
unsigned long timeout = msecs_to_jiffies(GSI_CMD_TIMEOUT);
|
||||
|
||||
reinit_completion(completion);
|
||||
|
||||
iowrite32(val, gsi->virt + reg);
|
||||
|
||||
return !!wait_for_completion_timeout(completion, GSI_CMD_TIMEOUT * HZ);
|
||||
return !!wait_for_completion_timeout(completion, timeout);
|
||||
}
|
||||
|
||||
/* Return the hardware's notion of the current state of an event ring */
|
||||
|
@ -326,41 +380,26 @@ gsi_evt_ring_state(struct gsi *gsi, u32 evt_ring_id)
|
|||
}
|
||||
|
||||
/* Issue an event ring command and wait for it to complete */
|
||||
static void evt_ring_command(struct gsi *gsi, u32 evt_ring_id,
|
||||
enum gsi_evt_cmd_opcode opcode)
|
||||
static void gsi_evt_ring_command(struct gsi *gsi, u32 evt_ring_id,
|
||||
enum gsi_evt_cmd_opcode opcode)
|
||||
{
|
||||
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;
|
||||
bool timeout;
|
||||
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.
|
||||
*
|
||||
* There's a small chance that a previous command completed
|
||||
* after the interrupt was disabled, so make sure we have no
|
||||
* pending interrupts before we enable them.
|
||||
*/
|
||||
iowrite32(~0, gsi->virt + GSI_CNTXT_SRC_EV_CH_IRQ_CLR_OFFSET);
|
||||
|
||||
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);
|
||||
/* Enable the completion interrupt for the command */
|
||||
gsi_irq_ev_ctrl_enable(gsi, evt_ring_id);
|
||||
|
||||
val = u32_encode_bits(evt_ring_id, EV_CHID_FMASK);
|
||||
val |= u32_encode_bits(opcode, EV_OPCODE_FMASK);
|
||||
|
||||
success = gsi_command(gsi, GSI_EV_CH_CMD_OFFSET, val, completion);
|
||||
timeout = !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);
|
||||
gsi_irq_ev_ctrl_disable(gsi);
|
||||
|
||||
if (success)
|
||||
if (!timeout)
|
||||
return;
|
||||
|
||||
dev_err(dev, "GSI command %u for event ring %u timed out, state %u\n",
|
||||
|
@ -380,7 +419,7 @@ static int gsi_evt_ring_alloc_command(struct gsi *gsi, u32 evt_ring_id)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
evt_ring_command(gsi, evt_ring_id, GSI_EVT_ALLOCATE);
|
||||
gsi_evt_ring_command(gsi, evt_ring_id, GSI_EVT_ALLOCATE);
|
||||
|
||||
/* If successful the event ring state will have changed */
|
||||
if (evt_ring->state == GSI_EVT_RING_STATE_ALLOCATED)
|
||||
|
@ -405,7 +444,7 @@ static void gsi_evt_ring_reset_command(struct gsi *gsi, u32 evt_ring_id)
|
|||
return;
|
||||
}
|
||||
|
||||
evt_ring_command(gsi, evt_ring_id, GSI_EVT_RESET);
|
||||
gsi_evt_ring_command(gsi, evt_ring_id, GSI_EVT_RESET);
|
||||
|
||||
/* If successful the event ring state will have changed */
|
||||
if (evt_ring->state == GSI_EVT_RING_STATE_ALLOCATED)
|
||||
|
@ -426,7 +465,7 @@ static void gsi_evt_ring_de_alloc_command(struct gsi *gsi, u32 evt_ring_id)
|
|||
return;
|
||||
}
|
||||
|
||||
evt_ring_command(gsi, evt_ring_id, GSI_EVT_DE_ALLOC);
|
||||
gsi_evt_ring_command(gsi, evt_ring_id, GSI_EVT_DE_ALLOC);
|
||||
|
||||
/* If successful the event ring state will have changed */
|
||||
if (evt_ring->state == GSI_EVT_RING_STATE_NOT_ALLOCATED)
|
||||
|
@ -456,34 +495,19 @@ 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;
|
||||
bool timeout;
|
||||
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.
|
||||
*
|
||||
* There's a small chance that a previous command completed
|
||||
* after the interrupt was disabled, so make sure we have no
|
||||
* pending interrupts before we enable them.
|
||||
*/
|
||||
iowrite32(~0, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_CLR_OFFSET);
|
||||
|
||||
val = BIT(channel_id);
|
||||
iowrite32(val, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_MSK_OFFSET);
|
||||
gsi_irq_type_enable(gsi, GSI_CH_CTRL);
|
||||
/* Enable the completion interrupt for the command */
|
||||
gsi_irq_ch_ctrl_enable(gsi, channel_id);
|
||||
|
||||
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);
|
||||
timeout = !gsi_command(gsi, GSI_CH_CMD_OFFSET, val, completion);
|
||||
|
||||
/* Disable the interrupt again */
|
||||
gsi_irq_type_disable(gsi, GSI_CH_CTRL);
|
||||
iowrite32(0, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_MSK_OFFSET);
|
||||
gsi_irq_ch_ctrl_disable(gsi);
|
||||
|
||||
if (success)
|
||||
if (!timeout)
|
||||
return;
|
||||
|
||||
dev_err(dev, "GSI command %u for channel %u timed out, state %u\n",
|
||||
|
@ -589,7 +613,8 @@ static void gsi_channel_reset_command(struct gsi_channel *channel)
|
|||
struct device *dev = channel->gsi->dev;
|
||||
enum gsi_channel_state state;
|
||||
|
||||
msleep(1); /* A short delay is required before a RESET command */
|
||||
/* A short delay is required before a RESET command */
|
||||
usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC);
|
||||
|
||||
state = gsi_channel_state(channel);
|
||||
if (state != GSI_CHANNEL_STATE_STOPPED &&
|
||||
|
@ -864,21 +889,18 @@ int gsi_channel_start(struct gsi *gsi, u32 channel_id)
|
|||
int gsi_channel_stop(struct gsi *gsi, u32 channel_id)
|
||||
{
|
||||
struct gsi_channel *channel = &gsi->channel[channel_id];
|
||||
u32 retries;
|
||||
u32 retries = GSI_CHANNEL_STOP_RETRIES;
|
||||
int ret;
|
||||
|
||||
gsi_channel_freeze(channel);
|
||||
|
||||
/* RX channels might require a little time to enter STOPPED state */
|
||||
retries = channel->toward_ipa ? 0 : GSI_CHANNEL_STOP_RX_RETRIES;
|
||||
|
||||
mutex_lock(&gsi->mutex);
|
||||
|
||||
do {
|
||||
ret = gsi_channel_stop_command(channel);
|
||||
if (ret != -EAGAIN)
|
||||
break;
|
||||
msleep(1);
|
||||
usleep_range(3 * USEC_PER_MSEC, 5 * USEC_PER_MSEC);
|
||||
} while (retries--);
|
||||
|
||||
mutex_unlock(&gsi->mutex);
|
||||
|
@ -1627,7 +1649,7 @@ static int gsi_generic_command(struct gsi *gsi, u32 channel_id,
|
|||
enum gsi_generic_cmd_opcode opcode)
|
||||
{
|
||||
struct completion *completion = &gsi->completion;
|
||||
bool success;
|
||||
bool timeout;
|
||||
u32 val;
|
||||
|
||||
/* The error global interrupt type is always enabled (until we
|
||||
|
@ -1650,12 +1672,12 @@ 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);
|
||||
|
||||
success = gsi_command(gsi, GSI_GENERIC_CMD_OFFSET, val, completion);
|
||||
timeout = !gsi_command(gsi, GSI_GENERIC_CMD_OFFSET, val, completion);
|
||||
|
||||
/* Disable the GP_INT1 IRQ type again */
|
||||
iowrite32(BIT(ERROR_INT), gsi->virt + GSI_CNTXT_GLOB_IRQ_EN_OFFSET);
|
||||
|
||||
if (success)
|
||||
if (!timeout)
|
||||
return gsi->result;
|
||||
|
||||
dev_err(gsi->dev, "GSI generic command %u to channel %u timed out\n",
|
||||
|
|
|
@ -1378,7 +1378,7 @@ static int ipa_endpoint_reset_rx_aggr(struct ipa_endpoint *endpoint)
|
|||
do {
|
||||
if (!ipa_endpoint_aggr_active(endpoint))
|
||||
break;
|
||||
msleep(1);
|
||||
usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC);
|
||||
} while (retries--);
|
||||
|
||||
/* Check one last time */
|
||||
|
@ -1399,7 +1399,7 @@ static int ipa_endpoint_reset_rx_aggr(struct ipa_endpoint *endpoint)
|
|||
*/
|
||||
gsi_channel_reset(gsi, endpoint->channel_id, true);
|
||||
|
||||
msleep(1);
|
||||
usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC);
|
||||
|
||||
goto out_suspend_again;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче