Commit dd2b7a66 authored by David S. Miller's avatar David S. Miller

Merge branch 'net-ipa-simplify-endpoint-programming'

Alex Elder says:

====================
net: ipa: simplify endpoint programming

Add tests to functions so they don't update undefined endpoint
registers, rather than requiring the caller to avoid calling them.

Move the call to a workaround function required when suspending
inside the function that puts an endpoint into suspend mode.  This
requires moving a few functions (which are otherwise unchanged).

Then simplify ipa_endpoint_program() to call essentially all
endpoint register update functions unconditionally.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 4e1a6911 fb57c3ea
......@@ -318,41 +318,102 @@ ipa_endpoint_program_delay(struct ipa_endpoint *endpoint, bool enable)
{
/* assert(endpoint->toward_ipa); */
/* Delay mode doesn't work properly for IPA v4.2 */
if (endpoint->ipa->version != IPA_VERSION_4_2)
(void)ipa_endpoint_init_ctrl(endpoint, enable);
}
/* Returns previous suspend state (true means it was enabled) */
static bool ipa_endpoint_aggr_active(struct ipa_endpoint *endpoint)
{
u32 mask = BIT(endpoint->endpoint_id);
struct ipa *ipa = endpoint->ipa;
u32 offset;
u32 val;
/* assert(mask & ipa->available); */
offset = ipa_reg_state_aggr_active_offset(ipa->version);
val = ioread32(ipa->reg_virt + offset);
return !!(val & mask);
}
static void ipa_endpoint_force_close(struct ipa_endpoint *endpoint)
{
u32 mask = BIT(endpoint->endpoint_id);
struct ipa *ipa = endpoint->ipa;
/* assert(mask & ipa->available); */
iowrite32(mask, ipa->reg_virt + IPA_REG_AGGR_FORCE_CLOSE_OFFSET);
}
/**
* ipa_endpoint_suspend_aggr() - Emulate suspend interrupt
* @endpoint_id: Endpoint on which to emulate a suspend
*
* Emulate suspend IPA interrupt to unsuspend an endpoint suspended
* with an open aggregation frame. This is to work around a hardware
* issue in IPA version 3.5.1 where the suspend interrupt will not be
* generated when it should be.
*/
static void ipa_endpoint_suspend_aggr(struct ipa_endpoint *endpoint)
{
struct ipa *ipa = endpoint->ipa;
if (!endpoint->data->aggregation)
return;
/* Nothing to do if the endpoint doesn't have aggregation open */
if (!ipa_endpoint_aggr_active(endpoint))
return;
/* Force close aggregation */
ipa_endpoint_force_close(endpoint);
ipa_interrupt_simulate_suspend(ipa->interrupt);
}
/* Returns previous suspend state (true means suspend was enabled) */
static bool
ipa_endpoint_program_suspend(struct ipa_endpoint *endpoint, bool enable)
{
bool suspended;
if (endpoint->ipa->version != IPA_VERSION_3_5_1)
return enable; /* For IPA v4.0+, no change made */
/* assert(!endpoint->toward_ipa); */
return ipa_endpoint_init_ctrl(endpoint, enable);
suspended = ipa_endpoint_init_ctrl(endpoint, enable);
/* A client suspended with an open aggregation frame will not
* generate a SUSPEND IPA interrupt. If enabling suspend, have
* ipa_endpoint_suspend_aggr() handle this.
*/
if (enable && !suspended)
ipa_endpoint_suspend_aggr(endpoint);
return suspended;
}
/* Enable or disable delay or suspend mode on all modem endpoints */
void ipa_endpoint_modem_pause_all(struct ipa *ipa, bool enable)
{
bool support_suspend;
u32 endpoint_id;
/* DELAY mode doesn't work correctly on IPA v4.2 */
if (ipa->version == IPA_VERSION_4_2)
return;
/* Only IPA v3.5.1 supports SUSPEND mode on RX endpoints */
support_suspend = ipa->version == IPA_VERSION_3_5_1;
for (endpoint_id = 0; endpoint_id < IPA_ENDPOINT_MAX; endpoint_id++) {
struct ipa_endpoint *endpoint = &ipa->endpoint[endpoint_id];
if (endpoint->ee_id != GSI_EE_MODEM)
continue;
/* Set TX delay mode, or for IPA v3.5.1 RX suspend mode */
/* Set TX delay mode or RX suspend mode */
if (endpoint->toward_ipa)
ipa_endpoint_program_delay(endpoint, enable);
else if (support_suspend)
else
(void)ipa_endpoint_program_suspend(endpoint, enable);
}
}
......@@ -527,6 +588,9 @@ static void ipa_endpoint_init_hdr_metadata_mask(struct ipa_endpoint *endpoint)
u32 val = 0;
u32 offset;
if (endpoint->toward_ipa)
return; /* Register not valid for TX endpoints */
offset = IPA_REG_ENDP_INIT_HDR_METADATA_MASK_N_OFFSET(endpoint_id);
/* Note that HDR_ENDIANNESS indicates big endian header fields */
......@@ -541,6 +605,9 @@ static void ipa_endpoint_init_mode(struct ipa_endpoint *endpoint)
u32 offset = IPA_REG_ENDP_INIT_MODE_N_OFFSET(endpoint->endpoint_id);
u32 val;
if (!endpoint->toward_ipa)
return; /* Register not valid for RX endpoints */
if (endpoint->data->dma_mode) {
enum ipa_endpoint_name name = endpoint->data->dma_endpoint;
u32 dma_endpoint_id;
......@@ -699,6 +766,9 @@ static void ipa_endpoint_init_deaggr(struct ipa_endpoint *endpoint)
u32 offset = IPA_REG_ENDP_INIT_DEAGGR_N_OFFSET(endpoint->endpoint_id);
u32 val = 0;
if (!endpoint->toward_ipa)
return; /* Register not valid for RX endpoints */
/* DEAGGR_HDR_LEN is 0 */
/* PACKET_OFFSET_VALID is 0 */
/* PACKET_OFFSET_LOCATION is ignored (not valid) */
......@@ -713,6 +783,9 @@ static void ipa_endpoint_init_seq(struct ipa_endpoint *endpoint)
u32 seq_type = endpoint->seq_type;
u32 val = 0;
if (!endpoint->toward_ipa)
return; /* Register not valid for RX endpoints */
/* Sequencer type is made up of four nibbles */
val |= u32_encode_bits(seq_type & 0xf, HPS_SEQ_TYPE_FMASK);
val |= u32_encode_bits((seq_type >> 4) & 0xf, DPS_SEQ_TYPE_FMASK);
......@@ -1142,29 +1215,6 @@ void ipa_endpoint_default_route_clear(struct ipa *ipa)
ipa_endpoint_default_route_set(ipa, 0);
}
static bool ipa_endpoint_aggr_active(struct ipa_endpoint *endpoint)
{
u32 mask = BIT(endpoint->endpoint_id);
struct ipa *ipa = endpoint->ipa;
u32 offset;
u32 val;
/* assert(mask & ipa->available); */
offset = ipa_reg_state_aggr_active_offset(ipa->version);
val = ioread32(ipa->reg_virt + offset);
return !!(val & mask);
}
static void ipa_endpoint_force_close(struct ipa_endpoint *endpoint)
{
u32 mask = BIT(endpoint->endpoint_id);
struct ipa *ipa = endpoint->ipa;
/* assert(mask & ipa->available); */
iowrite32(mask, ipa->reg_virt + IPA_REG_AGGR_FORCE_CLOSE_OFFSET);
}
/**
* ipa_endpoint_reset_rx_aggr() - Reset RX endpoint with aggregation active
* @endpoint: Endpoint to be reset
......@@ -1209,7 +1259,6 @@ static int ipa_endpoint_reset_rx_aggr(struct ipa_endpoint *endpoint)
gsi_channel_reset(gsi, endpoint->channel_id, false);
/* Make sure the channel isn't suspended */
if (endpoint->ipa->version == IPA_VERSION_3_5_1)
suspended = ipa_endpoint_program_suspend(endpoint, false);
/* Start channel and do a 1 byte read */
......@@ -1293,23 +1342,18 @@ static void ipa_endpoint_reset(struct ipa_endpoint *endpoint)
static void ipa_endpoint_program(struct ipa_endpoint *endpoint)
{
if (endpoint->toward_ipa) {
if (endpoint->ipa->version != IPA_VERSION_4_2)
if (endpoint->toward_ipa)
ipa_endpoint_program_delay(endpoint, false);
else
(void)ipa_endpoint_program_suspend(endpoint, false);
ipa_endpoint_init_cfg(endpoint);
ipa_endpoint_init_hdr(endpoint);
ipa_endpoint_init_hdr_ext(endpoint);
ipa_endpoint_init_hdr_metadata_mask(endpoint);
ipa_endpoint_init_mode(endpoint);
ipa_endpoint_init_aggr(endpoint);
ipa_endpoint_init_deaggr(endpoint);
ipa_endpoint_init_seq(endpoint);
ipa_endpoint_init_mode(endpoint);
} else {
if (endpoint->ipa->version == IPA_VERSION_3_5_1)
(void)ipa_endpoint_program_suspend(endpoint, false);
ipa_endpoint_init_hdr_ext(endpoint);
ipa_endpoint_init_aggr(endpoint);
ipa_endpoint_init_hdr_metadata_mask(endpoint);
}
ipa_endpoint_init_cfg(endpoint);
ipa_endpoint_init_hdr(endpoint);
ipa_endpoint_status(endpoint);
}
......@@ -1365,34 +1409,6 @@ void ipa_endpoint_disable_one(struct ipa_endpoint *endpoint)
endpoint->endpoint_id);
}
/**
* ipa_endpoint_suspend_aggr() - Emulate suspend interrupt
* @endpoint_id: Endpoint on which to emulate a suspend
*
* Emulate suspend IPA interrupt to unsuspend an endpoint suspended
* with an open aggregation frame. This is to work around a hardware
* issue in IPA version 3.5.1 where the suspend interrupt will not be
* generated when it should be.
*/
static void ipa_endpoint_suspend_aggr(struct ipa_endpoint *endpoint)
{
struct ipa *ipa = endpoint->ipa;
/* assert(ipa->version == IPA_VERSION_3_5_1); */
if (!endpoint->data->aggregation)
return;
/* Nothing to do if the endpoint doesn't have aggregation open */
if (!ipa_endpoint_aggr_active(endpoint))
return;
/* Force close aggregation */
ipa_endpoint_force_close(endpoint);
ipa_interrupt_simulate_suspend(ipa->interrupt);
}
void ipa_endpoint_suspend_one(struct ipa_endpoint *endpoint)
{
struct device *dev = &endpoint->ipa->pdev->dev;
......@@ -1406,19 +1422,11 @@ void ipa_endpoint_suspend_one(struct ipa_endpoint *endpoint)
if (!endpoint->toward_ipa)
ipa_endpoint_replenish_disable(endpoint);
/* IPA v3.5.1 doesn't use channel stop for suspend */
stop_channel = endpoint->ipa->version != IPA_VERSION_3_5_1;
if (!endpoint->toward_ipa && !stop_channel) {
/* Due to a hardware bug, a client suspended with an open
* aggregation frame will not generate a SUSPEND IPA
* interrupt. We work around this by force-closing the
* aggregation frame, then simulating the arrival of such
* an interrupt.
*/
if (!endpoint->toward_ipa)
(void)ipa_endpoint_program_suspend(endpoint, true);
ipa_endpoint_suspend_aggr(endpoint);
}
/* IPA v3.5.1 doesn't use channel stop for suspend */
stop_channel = endpoint->ipa->version != IPA_VERSION_3_5_1;
ret = gsi_channel_suspend(gsi, endpoint->channel_id, stop_channel);
if (ret)
dev_err(dev, "error %d suspending channel %u\n", ret,
......@@ -1435,11 +1443,11 @@ void ipa_endpoint_resume_one(struct ipa_endpoint *endpoint)
if (!(endpoint->ipa->enabled & BIT(endpoint->endpoint_id)))
return;
/* IPA v3.5.1 doesn't use channel start for resume */
start_channel = endpoint->ipa->version != IPA_VERSION_3_5_1;
if (!endpoint->toward_ipa && !start_channel)
if (!endpoint->toward_ipa)
(void)ipa_endpoint_program_suspend(endpoint, false);
/* IPA v3.5.1 doesn't use channel start for resume */
start_channel = endpoint->ipa->version != IPA_VERSION_3_5_1;
ret = gsi_channel_resume(gsi, endpoint->channel_id, start_channel);
if (ret)
dev_err(dev, "error %d resuming channel %u\n", ret,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment