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 ipa_reg_init_hol_block_timer_val(struct ipa *ipa, u32 microseconds) +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 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 {