提交 c9003783 编写于 作者: J Jakub Kicinski

Merge branch 'net-ipa-add-a-driver-shutdown-callback'

Alex Elder says:

====================
net: ipa: add a driver shutdown callback

The final patch in this series adds a driver shutdown callback for
the IPA driver.  The patches leading up to that address some issues
encountered while ensuring that callback worked as expected:
  - The first just reports a little more information when channels
    or event rings are in unexpected states
  - The second patch recognizes a condition where an as-yet-unused
    channel does not require a reset during teardown
  - The third patch explicitly ignores a certain error condition,
    because it can't be avoided, and is harmless if it occurs
  - The fourth properly handles requests to retry a channel HALT
    request
  - The fifth makes a second attempt to stop modem activity during
    shutdown if it's busy

The shutdown callback is implemented by calling the existing remove
callback function (reporting if that returns an error).
====================

Link: https://lore.kernel.org/r/20201119224929.23819-1-elder@linaro.orgSigned-off-by: NJakub Kicinski <kuba@kernel.org>
......@@ -92,6 +92,7 @@
#define GSI_CMD_TIMEOUT 5 /* seconds */
#define GSI_CHANNEL_STOP_RX_RETRIES 10
#define GSI_CHANNEL_MODEM_HALT_RETRIES 10
#define GSI_MHI_EVENT_ID_START 10 /* 1st reserved event id */
#define GSI_MHI_EVENT_ID_END 16 /* Last reserved event id */
......@@ -365,15 +366,15 @@ static int gsi_evt_ring_alloc_command(struct gsi *gsi, u32 evt_ring_id)
/* Get initial event ring state */
evt_ring->state = gsi_evt_ring_state(gsi, evt_ring_id);
if (evt_ring->state != GSI_EVT_RING_STATE_NOT_ALLOCATED) {
dev_err(gsi->dev, "bad event ring state %u before alloc\n",
evt_ring->state);
dev_err(gsi->dev, "event ring %u bad state %u before alloc\n",
evt_ring_id, evt_ring->state);
return -EINVAL;
}
ret = evt_ring_command(gsi, evt_ring_id, GSI_EVT_ALLOCATE);
if (!ret && evt_ring->state != GSI_EVT_RING_STATE_ALLOCATED) {
dev_err(gsi->dev, "bad event ring state %u after alloc\n",
evt_ring->state);
dev_err(gsi->dev, "event ring %u bad state %u after alloc\n",
evt_ring_id, evt_ring->state);
ret = -EIO;
}
......@@ -389,15 +390,15 @@ static void gsi_evt_ring_reset_command(struct gsi *gsi, u32 evt_ring_id)
if (state != GSI_EVT_RING_STATE_ALLOCATED &&
state != GSI_EVT_RING_STATE_ERROR) {
dev_err(gsi->dev, "bad event ring state %u before reset\n",
evt_ring->state);
dev_err(gsi->dev, "event ring %u bad state %u before reset\n",
evt_ring_id, evt_ring->state);
return;
}
ret = evt_ring_command(gsi, evt_ring_id, GSI_EVT_RESET);
if (!ret && evt_ring->state != GSI_EVT_RING_STATE_ALLOCATED)
dev_err(gsi->dev, "bad event ring state %u after reset\n",
evt_ring->state);
dev_err(gsi->dev, "event ring %u bad state %u after reset\n",
evt_ring_id, evt_ring->state);
}
/* Issue a hardware de-allocation request for an allocated event ring */
......@@ -407,15 +408,15 @@ static void gsi_evt_ring_de_alloc_command(struct gsi *gsi, u32 evt_ring_id)
int ret;
if (evt_ring->state != GSI_EVT_RING_STATE_ALLOCATED) {
dev_err(gsi->dev, "bad event ring state %u before dealloc\n",
evt_ring->state);
dev_err(gsi->dev, "event ring %u state %u before dealloc\n",
evt_ring_id, evt_ring->state);
return;
}
ret = evt_ring_command(gsi, evt_ring_id, GSI_EVT_DE_ALLOC);
if (!ret && evt_ring->state != GSI_EVT_RING_STATE_NOT_ALLOCATED)
dev_err(gsi->dev, "bad event ring state %u after dealloc\n",
evt_ring->state);
dev_err(gsi->dev, "event ring %u bad state %u after dealloc\n",
evt_ring_id, evt_ring->state);
}
/* Fetch the current state of a channel from hardware */
......@@ -479,7 +480,8 @@ static int gsi_channel_alloc_command(struct gsi *gsi, u32 channel_id)
/* Get initial channel state */
state = gsi_channel_state(channel);
if (state != GSI_CHANNEL_STATE_NOT_ALLOCATED) {
dev_err(dev, "bad channel state %u before alloc\n", state);
dev_err(dev, "channel %u bad state %u before alloc\n",
channel_id, state);
return -EINVAL;
}
......@@ -488,7 +490,8 @@ static int gsi_channel_alloc_command(struct gsi *gsi, u32 channel_id)
/* Channel state will normally have been updated */
state = gsi_channel_state(channel);
if (!ret && state != GSI_CHANNEL_STATE_ALLOCATED) {
dev_err(dev, "bad channel state %u after alloc\n", state);
dev_err(dev, "channel %u bad state %u after alloc\n",
channel_id, state);
ret = -EIO;
}
......@@ -505,7 +508,8 @@ static int gsi_channel_start_command(struct gsi_channel *channel)
state = gsi_channel_state(channel);
if (state != GSI_CHANNEL_STATE_ALLOCATED &&
state != GSI_CHANNEL_STATE_STOPPED) {
dev_err(dev, "bad channel state %u before start\n", state);
dev_err(dev, "channel %u bad state %u before start\n",
gsi_channel_id(channel), state);
return -EINVAL;
}
......@@ -514,7 +518,8 @@ static int gsi_channel_start_command(struct gsi_channel *channel)
/* Channel state will normally have been updated */
state = gsi_channel_state(channel);
if (!ret && state != GSI_CHANNEL_STATE_STARTED) {
dev_err(dev, "bad channel state %u after start\n", state);
dev_err(dev, "channel %u bad state %u after start\n",
gsi_channel_id(channel), state);
ret = -EIO;
}
......@@ -538,7 +543,8 @@ static int gsi_channel_stop_command(struct gsi_channel *channel)
if (state != GSI_CHANNEL_STATE_STARTED &&
state != GSI_CHANNEL_STATE_STOP_IN_PROC) {
dev_err(dev, "bad channel state %u before stop\n", state);
dev_err(dev, "channel %u bad state %u before stop\n",
gsi_channel_id(channel), state);
return -EINVAL;
}
......@@ -553,7 +559,8 @@ static int gsi_channel_stop_command(struct gsi_channel *channel)
if (state == GSI_CHANNEL_STATE_STOP_IN_PROC)
return -EAGAIN;
dev_err(dev, "bad channel state %u after stop\n", state);
dev_err(dev, "channel %u bad state %u after stop\n",
gsi_channel_id(channel), state);
return -EIO;
}
......@@ -570,7 +577,10 @@ static void gsi_channel_reset_command(struct gsi_channel *channel)
state = gsi_channel_state(channel);
if (state != GSI_CHANNEL_STATE_STOPPED &&
state != GSI_CHANNEL_STATE_ERROR) {
dev_err(dev, "bad channel state %u before reset\n", state);
/* No need to reset a channel already in ALLOCATED state */
if (state != GSI_CHANNEL_STATE_ALLOCATED)
dev_err(dev, "channel %u bad state %u before reset\n",
gsi_channel_id(channel), state);
return;
}
......@@ -579,7 +589,8 @@ static void gsi_channel_reset_command(struct gsi_channel *channel)
/* Channel state will normally have been updated */
state = gsi_channel_state(channel);
if (!ret && state != GSI_CHANNEL_STATE_ALLOCATED)
dev_err(dev, "bad channel state %u after reset\n", state);
dev_err(dev, "channel %u bad state %u after reset\n",
gsi_channel_id(channel), state);
}
/* Deallocate an ALLOCATED GSI channel */
......@@ -592,7 +603,8 @@ static void gsi_channel_de_alloc_command(struct gsi *gsi, u32 channel_id)
state = gsi_channel_state(channel);
if (state != GSI_CHANNEL_STATE_ALLOCATED) {
dev_err(dev, "bad channel state %u before dealloc\n", state);
dev_err(dev, "channel %u bad state %u before dealloc\n",
channel_id, state);
return;
}
......@@ -601,7 +613,8 @@ static void gsi_channel_de_alloc_command(struct gsi *gsi, u32 channel_id)
/* Channel state will normally have been updated */
state = gsi_channel_state(channel);
if (!ret && state != GSI_CHANNEL_STATE_NOT_ALLOCATED)
dev_err(dev, "bad channel state %u after dealloc\n", state);
dev_err(dev, "channel %u bad state %u after dealloc\n",
channel_id, state);
}
/* Ring an event ring doorbell, reporting the last entry processed by the AP.
......@@ -1075,10 +1088,38 @@ static void gsi_isr_gp_int1(struct gsi *gsi)
u32 result;
u32 val;
/* This interrupt is used to handle completions of the two GENERIC
* GSI commands. We use these to allocate and halt channels on
* the modem's behalf due to a hardware quirk on IPA v4.2. Once
* allocated, the modem "owns" these channels, and as a result we
* have no way of knowing the channel's state at any given time.
*
* It is recommended that we halt the modem channels we allocated
* when shutting down, but it's possible the channel isn't running
* at the time we issue the HALT command. We'll get an error in
* that case, but it's harmless (the channel is already halted).
*
* For this reason, we silently ignore a CHANNEL_NOT_RUNNING error
* if we receive it.
*/
val = ioread32(gsi->virt + GSI_CNTXT_SCRATCH_0_OFFSET);
result = u32_get_bits(val, GENERIC_EE_RESULT_FMASK);
if (result != GENERIC_EE_SUCCESS)
switch (result) {
case GENERIC_EE_SUCCESS:
case GENERIC_EE_CHANNEL_NOT_RUNNING:
gsi->result = 0;
break;
case GENERIC_EE_RETRY:
gsi->result = -EAGAIN;
break;
default:
dev_err(gsi->dev, "global INT1 generic result %u\n", result);
gsi->result = -EIO;
break;
}
complete(&gsi->completion);
}
......@@ -1590,7 +1631,7 @@ static int gsi_generic_command(struct gsi *gsi, u32 channel_id,
iowrite32(BIT(ERROR_INT), gsi->virt + GSI_CNTXT_GLOB_IRQ_EN_OFFSET);
if (success)
return 0;
return gsi->result;
dev_err(gsi->dev, "GSI generic command %u to channel %u timed out\n",
opcode, channel_id);
......@@ -1606,7 +1647,17 @@ static int gsi_modem_channel_alloc(struct gsi *gsi, u32 channel_id)
static void gsi_modem_channel_halt(struct gsi *gsi, u32 channel_id)
{
(void)gsi_generic_command(gsi, channel_id, GSI_GENERIC_HALT_CHANNEL);
u32 retries = GSI_CHANNEL_MODEM_HALT_RETRIES;
int ret;
do
ret = gsi_generic_command(gsi, channel_id,
GSI_GENERIC_HALT_CHANNEL);
while (ret == -EAGAIN && retries--);
if (ret)
dev_err(gsi->dev, "error %d halting modem channel %u\n",
ret, channel_id);
}
/* Setup function for channels */
......
......@@ -161,6 +161,7 @@ struct gsi {
u32 type_enabled_bitmap; /* GSI IRQ types enabled */
u32 ieob_enabled_bitmap; /* IEOB IRQ enabled (event rings) */
struct completion completion; /* for global EE commands */
int result; /* Negative errno (generic commands) */
struct mutex mutex; /* protects commands, programming */
};
......
......@@ -863,6 +863,11 @@ static int ipa_remove(struct platform_device *pdev)
if (ipa->setup_complete) {
ret = ipa_modem_stop(ipa);
/* If starting or stopping is in progress, try once more */
if (ret == -EBUSY) {
usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC);
ret = ipa_modem_stop(ipa);
}
if (ret)
return ret;
......@@ -883,6 +888,15 @@ static int ipa_remove(struct platform_device *pdev)
return 0;
}
static void ipa_shutdown(struct platform_device *pdev)
{
int ret;
ret = ipa_remove(pdev);
if (ret)
dev_err(&pdev->dev, "shutdown: remove returned %d\n", ret);
}
/**
* ipa_suspend() - Power management system suspend callback
* @dev: IPA device structure
......@@ -940,8 +954,9 @@ static const struct dev_pm_ops ipa_pm_ops = {
};
static struct platform_driver ipa_driver = {
.probe = ipa_probe,
.remove = ipa_remove,
.probe = ipa_probe,
.remove = ipa_remove,
.shutdown = ipa_shutdown,
.driver = {
.name = "ipa",
.pm = &ipa_pm_ops,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册