1026 lines · 27.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * Copyright (C) 2018-2024 Linaro Ltd. */ #include <linux/bug.h>#include <linux/firmware.h>#include <linux/io.h>#include <linux/module.h>#include <linux/of.h>#include <linux/of_reserved_mem.h>#include <linux/platform_device.h>#include <linux/pm_runtime.h>#include <linux/types.h> #include <linux/firmware/qcom/qcom_pas.h>#include <linux/soc/qcom/mdt_loader.h> #include "ipa.h"#include "ipa_cmd.h"#include "ipa_data.h"#include "ipa_endpoint.h"#include "ipa_interrupt.h"#include "ipa_mem.h"#include "ipa_modem.h"#include "ipa_power.h"#include "ipa_reg.h"#include "ipa_resource.h"#include "ipa_smp2p.h"#include "ipa_sysfs.h"#include "ipa_table.h"#include "ipa_uc.h"#include "ipa_version.h" /** * DOC: The IP Accelerator * * This driver supports the Qualcomm IP Accelerator (IPA), which is a * networking component found in many Qualcomm SoCs.  The IPA is connected * to the application processor (AP), but is also connected (and partially * controlled by) other "execution environments" (EEs), such as a modem. * * The IPA is the conduit between the AP and the modem that carries network * traffic.  This driver presents a network interface representing the * connection of the modem to external (e.g. LTE) networks. * * The IPA provides protocol checksum calculation, offloading this work * from the AP.  The IPA offers additional functionality, including routing, * filtering, and NAT support, but that more advanced functionality is not * currently supported.  Despite that, some resources--including routing * tables and filter tables--are defined in this driver because they must * be initialized even when the advanced hardware features are not used. * * There are two distinct layers that implement the IPA hardware, and this * is reflected in the organization of the driver.  The generic software * interface (GSI) is an integral component of the IPA, providing a * well-defined communication layer between the AP subsystem and the IPA * core.  The GSI implements a set of "channels" used for communication * between the AP and the IPA. * * The IPA layer uses GSI channels to implement its "endpoints".  And while * a GSI channel carries data between the AP and the IPA, a pair of IPA * endpoints is used to carry traffic between two EEs.  Specifically, the main * modem network interface is implemented by two pairs of endpoints:  a TX * endpoint on the AP coupled with an RX endpoint on the modem; and another * RX endpoint on the AP receiving data from a TX endpoint on the modem. */ /* The name of the GSI firmware file relative to /lib/firmware */#define IPA_FW_PATH_DEFAULT	"ipa_fws.mdt"#define IPA_PAS_ID		15 /* Shift of 19.2 MHz timestamp to achieve lower resolution timestamps *//* IPA v5.5+ does not specify Qtime timestamp config for DPL */#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 */ /** * enum ipa_firmware_loader: How GSI firmware gets loaded * * @IPA_LOADER_DEFER:		System not ready; try again later * @IPA_LOADER_SELF:		AP loads GSI firmware * @IPA_LOADER_MODEM:		Modem loads GSI firmware, signals when done * @IPA_LOADER_SKIP:		Neither AP nor modem need to load GSI firmware * @IPA_LOADER_INVALID:	GSI firmware loader specification is invalid */enum ipa_firmware_loader {	IPA_LOADER_DEFER,	IPA_LOADER_SELF,	IPA_LOADER_MODEM,	IPA_LOADER_SKIP,	IPA_LOADER_INVALID,}; /** * ipa_setup() - Set up IPA hardware * @ipa:	IPA pointer * * Perform initialization that requires issuing immediate commands on * the command TX endpoint.  If the modem is doing GSI firmware load * and initialization, this function will be called when an SMP2P * interrupt has been signaled by the modem.  Otherwise it will be * called from ipa_probe() after GSI firmware has been successfully * loaded, authenticated, and started by Trust Zone. */int ipa_setup(struct ipa *ipa){	struct ipa_endpoint *exception_endpoint;	struct ipa_endpoint *command_endpoint;	struct device *dev = ipa->dev;	int ret; 	ret = gsi_setup(&ipa->gsi);	if (ret)		return ret; 	ipa_endpoint_setup(ipa); 	/* We need to use the AP command TX endpoint to perform other	 * initialization, so we enable first.	 */	command_endpoint = ipa->name_map[IPA_ENDPOINT_AP_COMMAND_TX];	ret = ipa_endpoint_enable_one(command_endpoint);	if (ret)		goto err_endpoint_teardown; 	ret = ipa_mem_setup(ipa);	/* No matching teardown required */	if (ret)		goto err_command_disable; 	ret = ipa_table_setup(ipa);	/* No matching teardown required */	if (ret)		goto err_command_disable; 	/* Enable the exception handling endpoint, and tell the hardware	 * to use it by default.	 */	exception_endpoint = ipa->name_map[IPA_ENDPOINT_AP_LAN_RX];	ret = ipa_endpoint_enable_one(exception_endpoint);	if (ret)		goto err_command_disable; 	ipa_endpoint_default_route_set(ipa, exception_endpoint->endpoint_id); 	/* We're all set.  Now prepare for communication with the modem */	ret = ipa_qmi_setup(ipa);	if (ret)		goto err_default_route_clear; 	ipa->setup_complete = true; 	dev_info(dev, "IPA driver setup completed successfully\n"); 	return 0; err_default_route_clear:	ipa_endpoint_default_route_clear(ipa);	ipa_endpoint_disable_one(exception_endpoint);err_command_disable:	ipa_endpoint_disable_one(command_endpoint);err_endpoint_teardown:	ipa_endpoint_teardown(ipa);	gsi_teardown(&ipa->gsi); 	return ret;} /** * ipa_teardown() - Inverse of ipa_setup() * @ipa:	IPA pointer */static void ipa_teardown(struct ipa *ipa){	struct ipa_endpoint *exception_endpoint;	struct ipa_endpoint *command_endpoint; 	/* We're going to tear everything down, as if setup never completed */	ipa->setup_complete = false; 	ipa_qmi_teardown(ipa);	ipa_endpoint_default_route_clear(ipa);	exception_endpoint = ipa->name_map[IPA_ENDPOINT_AP_LAN_RX];	ipa_endpoint_disable_one(exception_endpoint);	command_endpoint = ipa->name_map[IPA_ENDPOINT_AP_COMMAND_TX];	ipa_endpoint_disable_one(command_endpoint);	ipa_endpoint_teardown(ipa);	gsi_teardown(&ipa->gsi);} static voidipa_hardware_config_bcr(struct ipa *ipa, const struct ipa_data *data){	const struct reg *reg;	u32 val; 	/* IPA v4.5+ has no backward compatibility register */	if (ipa->version >= IPA_VERSION_4_5)		return; 	reg = ipa_reg(ipa, IPA_BCR);	val = data->backward_compat;	iowrite32(val, ipa->reg_virt + reg_offset(reg));} static void ipa_hardware_config_tx(struct ipa *ipa){	enum ipa_version version = ipa->version;	const struct reg *reg;	u32 offset;	u32 val; 	if (version <= IPA_VERSION_4_0 || version >= IPA_VERSION_4_5)		return; 	/* Disable PA mask to allow HOLB drop */	reg = ipa_reg(ipa, IPA_TX_CFG);	offset = reg_offset(reg); 	val = ioread32(ipa->reg_virt + offset); 	val &= ~reg_bit(reg, PA_MASK_EN); 	iowrite32(val, ipa->reg_virt + offset);} static void ipa_hardware_config_clkon(struct ipa *ipa){	enum ipa_version version = ipa->version;	const struct reg *reg;	u32 val; 	if (version >= IPA_VERSION_4_5)		return; 	if (version < IPA_VERSION_4_0 && version != IPA_VERSION_3_1)		return; 	/* Implement some hardware workarounds */	reg = ipa_reg(ipa, CLKON_CFG);	if (version == IPA_VERSION_3_1) {		/* Disable MISC clock gating */		val = reg_bit(reg, CLKON_MISC);	} else {	/* IPA v4.0+ */		/* Enable open global clocks in the CLKON configuration */		val = reg_bit(reg, CLKON_GLOBAL);		val |= reg_bit(reg, GLOBAL_2X_CLK);	} 	iowrite32(val, ipa->reg_virt + reg_offset(reg));} /* Configure bus access behavior for IPA components */static void ipa_hardware_config_comp(struct ipa *ipa){	const struct reg *reg;	u32 offset;	u32 val; 	/* Nothing to configure prior to IPA v4.0 */	if (ipa->version < IPA_VERSION_4_0)		return; 	reg = ipa_reg(ipa, COMP_CFG);	offset = reg_offset(reg); 	val = ioread32(ipa->reg_virt + offset); 	if (ipa->version == IPA_VERSION_4_0) {		val &= ~reg_bit(reg, IPA_QMB_SELECT_CONS_EN);		val &= ~reg_bit(reg, IPA_QMB_SELECT_PROD_EN);		val &= ~reg_bit(reg, IPA_QMB_SELECT_GLOBAL_EN);	} else if (ipa->version < IPA_VERSION_4_5) {		val |= reg_bit(reg, GSI_MULTI_AXI_MASTERS_DIS);	} else {		/* For IPA v4.5+ FULL_FLUSH_WAIT_RS_CLOSURE_EN is 0 */	} 	val |= reg_bit(reg, GSI_MULTI_INORDER_RD_DIS);	val |= reg_bit(reg, GSI_MULTI_INORDER_WR_DIS); 	iowrite32(val, ipa->reg_virt + offset);} /* Configure DDR and (possibly) PCIe max read/write QSB values */static voidipa_hardware_config_qsb(struct ipa *ipa, const struct ipa_data *data){	const struct ipa_qsb_data *data0;	const struct ipa_qsb_data *data1;	const struct reg *reg;	u32 val; 	/* QMB 0 represents DDR; QMB 1 (if present) represents PCIe */	data0 = &data->qsb_data[IPA_QSB_MASTER_DDR];	if (data->qsb_count > 1)		data1 = &data->qsb_data[IPA_QSB_MASTER_PCIE]; 	/* Max outstanding write accesses for QSB masters */	reg = ipa_reg(ipa, QSB_MAX_WRITES); 	val = reg_encode(reg, GEN_QMB_0_MAX_WRITES, data0->max_writes);	if (data->qsb_count > 1)		val |= reg_encode(reg, GEN_QMB_1_MAX_WRITES, data1->max_writes); 	iowrite32(val, ipa->reg_virt + reg_offset(reg)); 	/* Max outstanding read accesses for QSB masters */	reg = ipa_reg(ipa, QSB_MAX_READS); 	val = reg_encode(reg, GEN_QMB_0_MAX_READS, data0->max_reads);	if (ipa->version >= IPA_VERSION_4_0)		val |= reg_encode(reg, GEN_QMB_0_MAX_READS_BEATS,				  data0->max_reads_beats);	if (data->qsb_count > 1) {		val = reg_encode(reg, GEN_QMB_1_MAX_READS, data1->max_reads);		if (ipa->version >= IPA_VERSION_4_0)			val |= reg_encode(reg, GEN_QMB_1_MAX_READS_BEATS,					  data1->max_reads_beats);	} 	iowrite32(val, ipa->reg_virt + reg_offset(reg));} /* The internal inactivity timer clock is used for the aggregation timer */#define TIMER_FREQUENCY	32000		/* 32 KHz inactivity timer clock */ /* Compute the value to use in the COUNTER_CFG register AGGR_GRANULARITY * field to represent the given number of microseconds.  The value is one * less than the number of timer ticks in the requested period.  0 is not * a valid granularity value (so for example @usec must be at least 16 for * a TIMER_FREQUENCY of 32000). */static __always_inline u32 ipa_aggr_granularity_val(u32 usec){	return DIV_ROUND_CLOSEST(usec * TIMER_FREQUENCY, USEC_PER_SEC) - 1;} /* 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){	const struct reg *reg;	u32 offset;	u32 val = 0; 	/* Timer clock divider must be disabled when we change the rate */	reg = ipa_reg(ipa, TIMERS_XO_CLK_DIV_CFG);	iowrite32(0, ipa->reg_virt + reg_offset(reg)); 	reg = ipa_reg(ipa, QTIME_TIMESTAMP_CFG);	if (ipa->version < IPA_VERSION_5_5) {		/* Set DPL time stamp resolution to use Qtime (not 1 msec) */		val = reg_encode(reg, DPL_TIMESTAMP_LSB, DPL_TIMESTAMP_SHIFT);		val |= reg_bit(reg, DPL_TIMESTAMP_SEL);	}	/* Configure tag and NAT Qtime timestamp resolution as well */	val |= reg_encode(reg, TAG_TIMESTAMP_LSB, TAG_TIMESTAMP_SHIFT);	val |= reg_encode(reg, NAT_TIMESTAMP_LSB, NAT_TIMESTAMP_SHIFT); 	iowrite32(val, ipa->reg_virt + reg_offset(reg)); 	/* Set granularity of pulse generators used for other timers */	reg = ipa_reg(ipa, TIMERS_PULSE_GRAN_CFG);	val = reg_encode(reg, PULSE_GRAN_0, IPA_GRAN_100_US);	val |= reg_encode(reg, PULSE_GRAN_1, IPA_GRAN_1_MS);	if (ipa->version >= IPA_VERSION_5_0) {		val |= reg_encode(reg, PULSE_GRAN_2, IPA_GRAN_10_MS);		val |= reg_encode(reg, PULSE_GRAN_3, IPA_GRAN_10_MS);	} else {		val |= reg_encode(reg, PULSE_GRAN_2, IPA_GRAN_1_MS);	} 	iowrite32(val, ipa->reg_virt + reg_offset(reg)); 	/* Actual divider is 1 more than value supplied here */	reg = ipa_reg(ipa, TIMERS_XO_CLK_DIV_CFG);	offset = reg_offset(reg); 	val = reg_encode(reg, DIV_VALUE, IPA_XO_CLOCK_DIVIDER - 1); 	iowrite32(val, ipa->reg_virt + offset); 	/* Divider value is set; re-enable the common timer clock divider */	val |= reg_bit(reg, DIV_ENABLE); 	iowrite32(val, ipa->reg_virt + offset);} /* Before IPA v4.5 timing is controlled by a counter register */static void ipa_hardware_config_counter(struct ipa *ipa){	u32 granularity = ipa_aggr_granularity_val(IPA_AGGR_GRANULARITY);	const struct reg *reg;	u32 val; 	reg = ipa_reg(ipa, COUNTER_CFG);	/* If defined, EOT_COAL_GRANULARITY is 0 */	val = reg_encode(reg, AGGR_GRANULARITY, granularity);	iowrite32(val, ipa->reg_virt + reg_offset(reg));} static void ipa_hardware_config_timing(struct ipa *ipa){	if (ipa->version < IPA_VERSION_4_5)		ipa_hardware_config_counter(ipa);	else		ipa_qtime_config(ipa);} static void ipa_hardware_config_hashing(struct ipa *ipa){	const struct reg *reg; 	/* Other than IPA v4.2, all versions enable "hashing".  Starting	 * with IPA v5.0, the filter and router tables are implemented	 * differently, but the default configuration enables this feature	 * (now referred to as "cacheing"), so there's nothing to do here.	 */	if (ipa->version != IPA_VERSION_4_2)		return; 	/* IPA v4.2 does not support hashed tables, so disable them */	reg = ipa_reg(ipa, FILT_ROUT_HASH_EN); 	/* IPV6_ROUTER_HASH, IPV6_FILTER_HASH, IPV4_ROUTER_HASH,	 * IPV4_FILTER_HASH are all zero.	 */	iowrite32(0, ipa->reg_virt + reg_offset(reg));} static void ipa_idle_indication_cfg(struct ipa *ipa,				    u32 enter_idle_debounce_thresh,				    bool const_non_idle_enable){	const struct reg *reg;	u32 val; 	if (ipa->version < IPA_VERSION_3_5_1)		return; 	reg = ipa_reg(ipa, IDLE_INDICATION_CFG);	val = reg_encode(reg, ENTER_IDLE_DEBOUNCE_THRESH,			 enter_idle_debounce_thresh);	if (const_non_idle_enable)		val |= reg_bit(reg, CONST_NON_IDLE_ENABLE); 	iowrite32(val, ipa->reg_virt + reg_offset(reg));} /** * ipa_hardware_dcd_config() - Enable dynamic clock division on IPA * @ipa:	IPA pointer * * Configures when the IPA signals it is idle to the global clock * controller, which can respond by scaling down the clock to save * power. */static void ipa_hardware_dcd_config(struct ipa *ipa){	/* Recommended values for IPA 3.5 and later according to IPA HPG */	ipa_idle_indication_cfg(ipa, 256, false);} static void ipa_hardware_dcd_deconfig(struct ipa *ipa){	/* Power-on reset values */	ipa_idle_indication_cfg(ipa, 0, true);} /** * ipa_hardware_config() - Primitive hardware initialization * @ipa:	IPA pointer * @data:	IPA configuration data */static void ipa_hardware_config(struct ipa *ipa, const struct ipa_data *data){	ipa_hardware_config_bcr(ipa, data);	ipa_hardware_config_tx(ipa);	ipa_hardware_config_clkon(ipa);	ipa_hardware_config_comp(ipa);

Lines 1–500 of 1026.