aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--drivers/net/ipa/ipa_endpoint.c174
-rw-r--r--drivers/net/ipa/ipa_main.c67
-rw-r--r--drivers/net/ipa/ipa_reg.h68
3 files changed, 260 insertions, 49 deletions
diff --git a/drivers/net/ipa/ipa_endpoint.c b/drivers/net/ipa/ipa_endpoint.c
index 27f543b6780b..9f4be9812a1f 100644
--- a/drivers/net/ipa/ipa_endpoint.c
+++ b/drivers/net/ipa/ipa_endpoint.c
@@ -37,7 +37,7 @@
#define IPA_ENDPOINT_QMAP_METADATA_MASK 0x000000ff /* host byte order */
#define IPA_ENDPOINT_RESET_AGGR_RETRY_MAX 3
-#define IPA_AGGR_TIME_LIMIT_DEFAULT 500 /* microseconds */
+#define IPA_AGGR_TIME_LIMIT 500 /* microseconds */
/** enum ipa_status_opcode - status element opcode hardware values */
enum ipa_status_opcode {
@@ -74,31 +74,6 @@ struct ipa_status {
#ifdef IPA_VALIDATE
-static void ipa_endpoint_validate_build(void)
-{
- /* The aggregation byte limit defines the point at which an
- * aggregation window will close. It is programmed into the
- * IPA hardware as a number of KB. We don't use "hard byte
- * limit" aggregation, which means that we need to supply
- * enough space in a receive buffer to hold a complete MTU
- * plus normal skb overhead *after* that aggregation byte
- * limit has been crossed.
- *
- * This check just ensures we don't define a receive buffer
- * size that would exceed what we can represent in the field
- * that is used to program its size.
- */
- BUILD_BUG_ON(IPA_RX_BUFFER_SIZE >
- field_max(AGGR_BYTE_LIMIT_FMASK) * SZ_1K +
- IPA_MTU + IPA_RX_BUFFER_OVERHEAD);
-
- /* I honestly don't know where this requirement comes from. But
- * it holds, and if we someday need to loosen the constraint we
- * can try to track it down.
- */
- BUILD_BUG_ON(sizeof(struct ipa_status) % 4);
-}
-
static bool ipa_endpoint_data_valid_one(struct ipa *ipa, u32 count,
const struct ipa_gsi_endpoint_data *all_data,
const struct ipa_gsi_endpoint_data *data)
@@ -180,14 +155,24 @@ static bool ipa_endpoint_data_valid_one(struct ipa *ipa, u32 count,
return true;
}
+static u32 aggr_byte_limit_max(enum ipa_version version)
+{
+ if (version < IPA_VERSION_4_5)
+ return field_max(aggr_byte_limit_fmask(true));
+
+ return field_max(aggr_byte_limit_fmask(false));
+}
+
static bool ipa_endpoint_data_valid(struct ipa *ipa, u32 count,
const struct ipa_gsi_endpoint_data *data)
{
const struct ipa_gsi_endpoint_data *dp = data;
struct device *dev = &ipa->pdev->dev;
enum ipa_endpoint_name name;
+ u32 limit;
- ipa_endpoint_validate_build();
+ /* Not sure where this constraint come from... */
+ BUILD_BUG_ON(sizeof(struct ipa_status) % 4);
if (count > IPA_ENDPOINT_COUNT) {
dev_err(dev, "too many endpoints specified (%u > %u)\n",
@@ -195,6 +180,26 @@ static bool ipa_endpoint_data_valid(struct ipa *ipa, u32 count,
return false;
}
+ /* The aggregation byte limit defines the point at which an
+ * aggregation window will close. It is programmed into the
+ * IPA hardware as a number of KB. We don't use "hard byte
+ * limit" aggregation, which means that we need to supply
+ * enough space in a receive buffer to hold a complete MTU
+ * plus normal skb overhead *after* that aggregation byte
+ * limit has been crossed.
+ *
+ * This check ensures we don't define a receive buffer size
+ * that would exceed what we can represent in the field that
+ * is used to program its size.
+ */
+ limit = aggr_byte_limit_max(ipa->version) * SZ_1K;
+ limit += IPA_MTU + IPA_RX_BUFFER_OVERHEAD;
+ if (limit < IPA_RX_BUFFER_SIZE) {
+ dev_err(dev, "buffer size too big for aggregation (%u > %u)\n",
+ IPA_RX_BUFFER_SIZE, limit);
+ return false;
+ }
+
/* Make sure needed endpoints have defined data */
if (ipa_gsi_endpoint_data_empty(&data[IPA_ENDPOINT_AP_COMMAND_TX])) {
dev_err(dev, "command TX endpoint not defined\n");
@@ -624,29 +629,84 @@ static u32 ipa_aggr_size_kb(u32 rx_buffer_size)
return rx_buffer_size / SZ_1K;
}
+/* Encoded values for AGGR endpoint register fields */
+static u32 aggr_byte_limit_encoded(enum ipa_version version, u32 limit)
+{
+ if (version < IPA_VERSION_4_5)
+ return u32_encode_bits(limit, aggr_byte_limit_fmask(true));
+
+ return u32_encode_bits(limit, aggr_byte_limit_fmask(false));
+}
+
+/* Encode the aggregation timer limit (microseconds) based on IPA version */
+static u32 aggr_time_limit_encoded(enum ipa_version version, u32 limit)
+{
+ u32 gran_sel;
+ u32 fmask;
+ u32 val;
+
+ if (version < IPA_VERSION_4_5) {
+ /* We set aggregation granularity in ipa_hardware_config() */
+ limit = DIV_ROUND_CLOSEST(limit, IPA_AGGR_GRANULARITY);
+
+ return u32_encode_bits(limit, aggr_time_limit_fmask(true));
+ }
+
+ /* IPA v4.5 expresses the time limit using Qtime. The AP has
+ * pulse generators 0 and 1 available, which were configured
+ * in ipa_qtime_config() to have granularity 100 usec and
+ * 1 msec, respectively. Use pulse generator 0 if possible,
+ * otherwise fall back to pulse generator 1.
+ */
+ fmask = aggr_time_limit_fmask(false);
+ val = DIV_ROUND_CLOSEST(limit, 100);
+ if (val > field_max(fmask)) {
+ /* Have to use pulse generator 1 (millisecond granularity) */
+ gran_sel = AGGR_GRAN_SEL_FMASK;
+ val = DIV_ROUND_CLOSEST(limit, 1000);
+ } else {
+ /* We can use pulse generator 0 (100 usec granularity) */
+ gran_sel = 0;
+ }
+
+ return gran_sel | u32_encode_bits(val, fmask);
+}
+
+static u32 aggr_sw_eof_active_encoded(enum ipa_version version, bool enabled)
+{
+ u32 val = enabled ? 1 : 0;
+
+ if (version < IPA_VERSION_4_5)
+ return u32_encode_bits(val, aggr_sw_eof_active_fmask(true));
+
+ return u32_encode_bits(val, aggr_sw_eof_active_fmask(false));
+}
+
static void ipa_endpoint_init_aggr(struct ipa_endpoint *endpoint)
{
u32 offset = IPA_REG_ENDP_INIT_AGGR_N_OFFSET(endpoint->endpoint_id);
+ enum ipa_version version = endpoint->ipa->version;
u32 val = 0;
if (endpoint->data->aggregation) {
if (!endpoint->toward_ipa) {
+ bool close_eof;
u32 limit;
val |= u32_encode_bits(IPA_ENABLE_AGGR, AGGR_EN_FMASK);
val |= u32_encode_bits(IPA_GENERIC, AGGR_TYPE_FMASK);
limit = ipa_aggr_size_kb(IPA_RX_BUFFER_SIZE);
- val |= u32_encode_bits(limit, AGGR_BYTE_LIMIT_FMASK);
+ val |= aggr_byte_limit_encoded(version, limit);
- limit = IPA_AGGR_TIME_LIMIT_DEFAULT;
- limit = DIV_ROUND_CLOSEST(limit, IPA_AGGR_GRANULARITY);
- val |= u32_encode_bits(limit, AGGR_TIME_LIMIT_FMASK);
+ limit = IPA_AGGR_TIME_LIMIT;
+ val |= aggr_time_limit_encoded(version, limit);
/* AGGR_PKT_LIMIT is 0 (unlimited) */
- if (endpoint->data->rx.aggr_close_eof)
- val |= AGGR_SW_EOF_ACTIVE_FMASK;
+ close_eof = endpoint->data->rx.aggr_close_eof;
+ val |= aggr_sw_eof_active_encoded(version, close_eof);
+
/* AGGR_HARD_BYTE_LIMIT_ENABLE is 0 */
} else {
val |= u32_encode_bits(IPA_ENABLE_DEAGGR,
@@ -664,12 +724,45 @@ static void ipa_endpoint_init_aggr(struct ipa_endpoint *endpoint)
iowrite32(val, endpoint->ipa->reg_virt + offset);
}
-/* 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.
+/* Return the Qtime-based head-of-line blocking timer value that
+ * represents the given number of microseconds. The result
+ * includes both the timer value and the selected timer granularity.
+ */
+static u32 hol_block_timer_qtime_val(struct ipa *ipa, u32 microseconds)
+{
+ u32 gran_sel;
+ u32 val;
+
+ /* IPA v4.5 expresses time limits using Qtime. The AP has
+ * pulse generators 0 and 1 available, which were configured
+ * in ipa_qtime_config() to have granularity 100 usec and
+ * 1 msec, respectively. Use pulse generator 0 if possible,
+ * otherwise fall back to pulse generator 1.
+ */
+ val = DIV_ROUND_CLOSEST(microseconds, 100);
+ if (val > field_max(TIME_LIMIT_FMASK)) {
+ /* Have to use pulse generator 1 (millisecond granularity) */
+ gran_sel = GRAN_SEL_FMASK;
+ val = DIV_ROUND_CLOSEST(microseconds, 1000);
+ } else {
+ /* We can use pulse generator 0 (100 usec granularity) */
+ gran_sel = 0;
+ }
+
+ return gran_sel | u32_encode_bits(val, TIME_LIMIT_FMASK);
+}
+
+/* The head-of-line blocking timer is defined as a tick count. For
+ * IPA version 4.5 the tick count is based on the Qtimer, which is
+ * derived from the 19.2 MHz SoC XO clock. For older IPA versions
+ * each tick represents 128 cycles of the IPA core clock.
+ *
+ * Return the encoded value that should be written to that register
+ * that represents the timeout period provided. For IPA v4.2 this
+ * encodes a base and scale value, while for earlier versions the
+ * value is a simple tick count.
*/
-static u32 ipa_reg_init_hol_block_timer_val(struct ipa *ipa, u32 microseconds)
+static u32 hol_block_timer_val(struct ipa *ipa, u32 microseconds)
{
u32 width;
u32 scale;
@@ -681,6 +774,9 @@ static u32 ipa_reg_init_hol_block_timer_val(struct ipa *ipa, u32 microseconds)
if (!microseconds)
return 0; /* Nothing to compute if timer period is 0 */
+ if (ipa->version == IPA_VERSION_4_5)
+ return hol_block_timer_qtime_val(ipa, microseconds);
+
/* Use 64 bit arithmetic to avoid overflow... */
rate = ipa_clock_rate(ipa);
ticks = DIV_ROUND_CLOSEST(microseconds * rate, 128 * USEC_PER_SEC);
@@ -726,7 +822,7 @@ static void ipa_endpoint_init_hol_block_timer(struct ipa_endpoint *endpoint,
u32 val;
offset = IPA_REG_ENDP_INIT_HOL_BLOCK_TIMER_N_OFFSET(endpoint_id);
- val = ipa_reg_init_hol_block_timer_val(ipa, microseconds);
+ val = hol_block_timer_val(ipa, microseconds);
iowrite32(val, ipa->reg_virt + offset);
}
diff --git a/drivers/net/ipa/ipa_main.c b/drivers/net/ipa/ipa_main.c
index f25bcfe51dd4..d0768452c15c 100644
--- a/drivers/net/ipa/ipa_main.c
+++ b/drivers/net/ipa/ipa_main.c
@@ -70,6 +70,14 @@
#define IPA_FWS_PATH "ipa_fws.mdt"
#define IPA_PAS_ID 15
+/* Shift of 19.2 MHz timestamp to achieve lower resolution timestamps */
+#define DPL_TIMESTAMP_SHIFT 14 /* ~1.172 kHz, ~853 usec per tick */
+#define TAG_TIMESTAMP_SHIFT 14
+#define NAT_TIMESTAMP_SHIFT 24 /* ~1.144 Hz, ~874 msec per tick */
+
+/* Divider for 19.2 MHz crystal oscillator clock to get common timer clock */
+#define IPA_XO_CLOCK_DIVIDER 192 /* 1 is subtracted where used */
+
/**
* ipa_suspend_handler() - Handle the suspend IPA interrupt
* @ipa: IPA pointer
@@ -292,6 +300,53 @@ static void ipa_hardware_config_qsb(struct ipa *ipa)
iowrite32(val, ipa->reg_virt + IPA_REG_QSB_MAX_READS_OFFSET);
}
+/* IPA uses unified Qtime starting at IPA v4.5, implementing various
+ * timestamps and timers independent of the IPA core clock rate. The
+ * Qtimer is based on a 56-bit timestamp incremented at each tick of
+ * a 19.2 MHz SoC crystal oscillator (XO clock).
+ *
+ * For IPA timestamps (tag, NAT, data path logging) a lower resolution
+ * timestamp is achieved by shifting the Qtimer timestamp value right
+ * some number of bits to produce the low-order bits of the coarser
+ * granularity timestamp.
+ *
+ * For timers, a common timer clock is derived from the XO clock using
+ * a divider (we use 192, to produce a 100kHz timer clock). From
+ * this common clock, three "pulse generators" are used to produce
+ * timer ticks at a configurable frequency. IPA timers (such as
+ * those used for aggregation or head-of-line block handling) now
+ * define their period based on one of these pulse generators.
+ */
+static void ipa_qtime_config(struct ipa *ipa)
+{
+ u32 val;
+
+ /* Timer clock divider must be disabled when we change the rate */
+ iowrite32(0, ipa->reg_virt + IPA_REG_TIMERS_XO_CLK_DIV_CFG_OFFSET);
+
+ /* Set DPL time stamp resolution to use Qtime (instead of 1 msec) */
+ val = u32_encode_bits(DPL_TIMESTAMP_SHIFT, DPL_TIMESTAMP_LSB_FMASK);
+ val |= u32_encode_bits(1, DPL_TIMESTAMP_SEL_FMASK);
+ /* Configure tag and NAT Qtime timestamp resolution as well */
+ val |= u32_encode_bits(TAG_TIMESTAMP_SHIFT, TAG_TIMESTAMP_LSB_FMASK);
+ val |= u32_encode_bits(NAT_TIMESTAMP_SHIFT, NAT_TIMESTAMP_LSB_FMASK);
+ iowrite32(val, ipa->reg_virt + IPA_REG_QTIME_TIMESTAMP_CFG_OFFSET);
+
+ /* Set granularity of pulse generators used for other timers */
+ val = u32_encode_bits(IPA_GRAN_100_US, GRAN_0_FMASK);
+ val |= u32_encode_bits(IPA_GRAN_1_MS, GRAN_1_FMASK);
+ val |= u32_encode_bits(IPA_GRAN_1_MS, GRAN_2_FMASK);
+ iowrite32(val, ipa->reg_virt + IPA_REG_TIMERS_PULSE_GRAN_CFG_OFFSET);
+
+ /* Actual divider is 1 more than value supplied here */
+ val = u32_encode_bits(IPA_XO_CLOCK_DIVIDER - 1, DIV_VALUE_FMASK);
+ iowrite32(val, ipa->reg_virt + IPA_REG_TIMERS_XO_CLK_DIV_CFG_OFFSET);
+
+ /* Divider value is set; re-enable the common timer clock divider */
+ val |= u32_encode_bits(1, DIV_ENABLE_FMASK);
+ iowrite32(val, ipa->reg_virt + IPA_REG_TIMERS_XO_CLK_DIV_CFG_OFFSET);
+}
+
static void ipa_idle_indication_cfg(struct ipa *ipa,
u32 enter_idle_debounce_thresh,
bool const_non_idle_enable)
@@ -362,10 +417,14 @@ static void ipa_hardware_config(struct ipa *ipa)
/* Configure system bus limits */
ipa_hardware_config_qsb(ipa);
- /* Configure aggregation granularity */
- granularity = ipa_aggr_granularity_val(IPA_AGGR_GRANULARITY);
- val = u32_encode_bits(granularity, AGGR_GRANULARITY_FMASK);
- iowrite32(val, ipa->reg_virt + IPA_REG_COUNTER_CFG_OFFSET);
+ if (version < IPA_VERSION_4_5) {
+ /* Configure aggregation timer granularity */
+ granularity = ipa_aggr_granularity_val(IPA_AGGR_GRANULARITY);
+ val = u32_encode_bits(granularity, AGGR_GRANULARITY_FMASK);
+ iowrite32(val, ipa->reg_virt + IPA_REG_COUNTER_CFG_OFFSET);
+ } else {
+ ipa_qtime_config(ipa);
+ }
/* IPA v4.2 does not support hashed tables, so disable them */
if (version == IPA_VERSION_4_2) {
diff --git a/drivers/net/ipa/ipa_reg.h b/drivers/net/ipa/ipa_reg.h
index 3fabafd7e32c..e6b0827a244e 100644
--- a/drivers/net/ipa/ipa_reg.h
+++ b/drivers/net/ipa/ipa_reg.h
@@ -223,6 +223,7 @@ static inline u32 ipa_reg_bcr_val(enum ipa_version version)
/* ipa->available defines the valid bits in the AGGR_FORCE_CLOSE register */
#define IPA_REG_AGGR_FORCE_CLOSE_OFFSET 0x000001ec
+/* The next register is not present for IPA v4.5 */
#define IPA_REG_COUNTER_CFG_OFFSET 0x000001f0
#define AGGR_GRANULARITY_FMASK GENMASK(8, 4)
@@ -274,6 +275,35 @@ static inline u32 ipa_reg_idle_indication_cfg_offset(enum ipa_version version)
#define ENTER_IDLE_DEBOUNCE_THRESH_FMASK GENMASK(15, 0)
#define CONST_NON_IDLE_ENABLE_FMASK GENMASK(16, 16)
+/* The next register is present for IPA v4.5 */
+#define IPA_REG_QTIME_TIMESTAMP_CFG_OFFSET 0x0000024c
+#define DPL_TIMESTAMP_LSB_FMASK GENMASK(4, 0)
+#define DPL_TIMESTAMP_SEL_FMASK GENMASK(7, 7)
+#define TAG_TIMESTAMP_LSB_FMASK GENMASK(12, 8)
+#define NAT_TIMESTAMP_LSB_FMASK GENMASK(20, 16)
+
+/* The next register is present for IPA v4.5 */
+#define IPA_REG_TIMERS_XO_CLK_DIV_CFG_OFFSET 0x00000250
+#define DIV_VALUE_FMASK GENMASK(8, 0)
+#define DIV_ENABLE_FMASK GENMASK(31, 31)
+
+/* The next register is present for IPA v4.5 */
+#define IPA_REG_TIMERS_PULSE_GRAN_CFG_OFFSET 0x00000254
+#define GRAN_0_FMASK GENMASK(2, 0)
+#define GRAN_1_FMASK GENMASK(5, 3)
+#define GRAN_2_FMASK GENMASK(8, 6)
+/* Values for GRAN_x fields of TIMERS_PULSE_GRAN_CFG */
+enum ipa_pulse_gran {
+ IPA_GRAN_10_US = 0x0,
+ IPA_GRAN_20_US = 0x1,
+ IPA_GRAN_50_US = 0x2,
+ IPA_GRAN_100_US = 0x3,
+ IPA_GRAN_1_MS = 0x4,
+ IPA_GRAN_10_MS = 0x5,
+ IPA_GRAN_100_MS = 0x6,
+ IPA_GRAN_655350_US = 0x7,
+};
+
/* # IPA source resource groups available based on version */
static inline u32 ipa_resource_group_src_count(enum ipa_version version)
{
@@ -450,12 +480,38 @@ enum ipa_mode {
(0x00000824 + 0x0070 * (ep))
#define AGGR_EN_FMASK GENMASK(1, 0)
#define AGGR_TYPE_FMASK GENMASK(4, 2)
-#define AGGR_BYTE_LIMIT_FMASK GENMASK(9, 5)
-#define AGGR_TIME_LIMIT_FMASK GENMASK(14, 10)
-#define AGGR_PKT_LIMIT_FMASK GENMASK(20, 15)
-#define AGGR_SW_EOF_ACTIVE_FMASK GENMASK(21, 21)
-#define AGGR_FORCE_CLOSE_FMASK GENMASK(22, 22)
-#define AGGR_HARD_BYTE_LIMIT_ENABLE_FMASK GENMASK(24, 24)
+static inline u32 aggr_byte_limit_fmask(bool legacy)
+{
+ return legacy ? GENMASK(9, 5) : GENMASK(10, 5);
+}
+
+static inline u32 aggr_time_limit_fmask(bool legacy)
+{
+ return legacy ? GENMASK(14, 10) : GENMASK(16, 12);
+}
+
+static inline u32 aggr_pkt_limit_fmask(bool legacy)
+{
+ return legacy ? GENMASK(20, 15) : GENMASK(22, 17);
+}
+
+static inline u32 aggr_sw_eof_active_fmask(bool legacy)
+{
+ return legacy ? GENMASK(21, 21) : GENMASK(23, 23);
+}
+
+static inline u32 aggr_force_close_fmask(bool legacy)
+{
+ return legacy ? GENMASK(22, 22) : GENMASK(24, 24);
+}
+
+static inline u32 aggr_hard_byte_limit_enable_fmask(bool legacy)
+{
+ return legacy ? GENMASK(24, 24) : GENMASK(26, 26);
+}
+
+/* The next field is present for IPA v4.5 */
+#define AGGR_GRAN_SEL_FMASK GENMASK(27, 27)
/** enum ipa_aggr_en - aggregation enable field in ENDP_INIT_AGGR_N */
enum ipa_aggr_en {