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

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
Loading
Loading
Loading
Loading
+99 −91
Original line number Diff line number Diff line
@@ -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,