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

Merge branch 'net-ipa-fix-HOLB-timer-register-use'



Alex Elder says:

====================
net: ipa: fix HOLB timer register use

The function ipa_reg_init_hol_block_timer_val() generates the value
to write into the HOL_BLOCK_TIMER endpoint configuration register,
to represent a given timeout value (in microseconds).  It only
supports a timer value of 0 though, in part because that's
sufficient, but mainly because there was some confusion about
how the register is formatted in newer hardware.

I got clarification about the register format, so this series fixes
ipa_reg_init_hol_block_timer_val() to work for any supported delay
value.

The delay is based on the IPA core clock, so determining the value
to write for a given period requires access to the current core
clock rate.  So the first patch just creates a new function to
provide that.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents cd8700e4 f13a8c31
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -256,6 +256,12 @@ void ipa_clock_put(struct ipa *ipa)
	mutex_unlock(&clock->mutex);
}

/* Return the current IPA core clock rate */
u32 ipa_clock_rate(struct ipa *ipa)
{
	return ipa->clock ? (u32)clk_get_rate(ipa->clock->core) : 0;
}

/* Initialize IPA clocking */
struct ipa_clock *ipa_clock_init(struct device *dev)
{
+8 −0
Original line number Diff line number Diff line
@@ -10,6 +10,14 @@ struct device;

struct ipa;

/**
 * ipa_clock_rate() - Return the current IPA core clock rate
 * @ipa:	IPA structure
 *
 * Return: The current clock rate (in Hz), or 0.
 */
u32 ipa_clock_rate(struct ipa *ipa);

/**
 * ipa_clock_init() - Initialize IPA clocking
 * @dev:	IPA device
+46 −38
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include "ipa_modem.h"
#include "ipa_table.h"
#include "ipa_gsi.h"
#include "ipa_clock.h"

#define atomic_dec_not_zero(v)	atomic_add_unless((v), -1, 0)

@@ -675,36 +676,60 @@ static void ipa_endpoint_init_aggr(struct ipa_endpoint *endpoint)
	iowrite32(val, endpoint->ipa->reg_virt + offset);
}

/* A return value of 0 indicates an error */
/* The head-of-line blocking timer is defined as a tick count, where each
 * tick represents 128 cycles of the IPA core clock.  Return the value
 * that should be written to that register that represents the timeout
 * period provided.
 */
static u32 ipa_reg_init_hol_block_timer_val(struct ipa *ipa, u32 microseconds)
{
	u32 width;
	u32 scale;
	u32 base;
	u64 ticks;
	u64 rate;
	u32 high;
	u32 val;

	if (!microseconds)
		return 0;	/* invalid delay */

	/* Timer is represented in units of clock ticks. */
	if (ipa->version < IPA_VERSION_4_2)
		return microseconds;	/* XXX Needs to be computed */

	/* IPA v4.2 represents the tick count as base * scale */
	scale = 1;			/* XXX Needs to be computed */
	if (scale > field_max(SCALE_FMASK))
		return 0;		/* scale too big */

	base = DIV_ROUND_CLOSEST(microseconds, scale);
	if (base > field_max(BASE_VALUE_FMASK))
		return 0;		/* microseconds too big */
		return 0;	/* Nothing to compute if timer period is 0 */

	/* Use 64 bit arithmetic to avoid overflow... */
	rate = ipa_clock_rate(ipa);
	ticks = DIV_ROUND_CLOSEST(microseconds * rate, 128 * USEC_PER_SEC);
	/* ...but we still need to fit into a 32-bit register */
	WARN_ON(ticks > U32_MAX);

	/* IPA v3.5.1 just records the tick count */
	if (ipa->version == IPA_VERSION_3_5_1)
		return (u32)ticks;

	/* For IPA v4.2, the tick count is represented by base and
	 * scale fields within the 32-bit timer register, where:
	 *     ticks = base << scale;
	 * The best precision is achieved when the base value is as
	 * large as possible.  Find the highest set bit in the tick
	 * count, and extract the number of bits in the base field
	 * such that that high bit is included.
	 */
	high = fls(ticks);		/* 1..32 */
	width = HWEIGHT32(BASE_VALUE_FMASK);
	scale = high > width ? high - width : 0;
	if (scale) {
		/* If we're scaling, round up to get a closer result */
		ticks += 1 << (scale - 1);
		/* High bit was set, so rounding might have affected it */
		if (fls(ticks) != high)
			scale++;
	}

	val = u32_encode_bits(scale, SCALE_FMASK);
	val |= u32_encode_bits(base, BASE_VALUE_FMASK);
	val |= u32_encode_bits(ticks >> scale, BASE_VALUE_FMASK);

	return val;
}

static int ipa_endpoint_init_hol_block_timer(struct ipa_endpoint *endpoint,
/* If microseconds is 0, timeout is immediate */
static void ipa_endpoint_init_hol_block_timer(struct ipa_endpoint *endpoint,
					      u32 microseconds)
{
	u32 endpoint_id = endpoint->endpoint_id;
@@ -712,26 +737,9 @@ static int ipa_endpoint_init_hol_block_timer(struct ipa_endpoint *endpoint,
	u32 offset;
	u32 val;

	/* XXX We'll fix this when the register definition is clear */
	if (microseconds) {
		struct device *dev = &ipa->pdev->dev;

		dev_err(dev, "endpoint %u non-zero HOLB period (ignoring)\n",
			endpoint_id);
		microseconds = 0;
	}

	if (microseconds) {
		val = ipa_reg_init_hol_block_timer_val(ipa, microseconds);
		if (!val)
			return -EINVAL;
	} else {
		val = 0;	/* timeout is immediate */
	}
	offset = IPA_REG_ENDP_INIT_HOL_BLOCK_TIMER_N_OFFSET(endpoint_id);
	val = ipa_reg_init_hol_block_timer_val(ipa, microseconds);
	iowrite32(val, ipa->reg_virt + offset);

	return 0;
}

static void
@@ -756,7 +764,7 @@ void ipa_endpoint_modem_hol_block_clear_all(struct ipa *ipa)
		if (endpoint->toward_ipa || endpoint->ee_id != GSI_EE_MODEM)
			continue;

		(void)ipa_endpoint_init_hol_block_timer(endpoint, 0);
		ipa_endpoint_init_hol_block_timer(endpoint, 0);
		ipa_endpoint_init_hol_block_enable(endpoint, true);
	}
}