Skip to content

Commit

Permalink
Merge branch 'net-ipa-fix-HOLB-timer-register-use'
Browse files Browse the repository at this point in the history
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: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Jul 3, 2020
2 parents cd8700e + f13a8c3 commit a211649
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 38 deletions.
6 changes: 6 additions & 0 deletions drivers/net/ipa/ipa_clock.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
8 changes: 8 additions & 0 deletions drivers/net/ipa/ipa_clock.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
84 changes: 46 additions & 38 deletions drivers/net/ipa/ipa_endpoint.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -675,63 +676,70 @@ 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,
u32 microseconds)
/* 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;
struct ipa *ipa = endpoint->ipa;
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
Expand All @@ -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);
}
}
Expand Down

0 comments on commit a211649

Please sign in to comment.