diff options
Diffstat (limited to 'drivers/net/ipa/ipa_main.c')
| -rw-r--r-- | drivers/net/ipa/ipa_main.c | 330 | 
1 files changed, 102 insertions, 228 deletions
| diff --git a/drivers/net/ipa/ipa_main.c b/drivers/net/ipa/ipa_main.c index 97c1b55405cb..9915603ed10b 100644 --- a/drivers/net/ipa/ipa_main.c +++ b/drivers/net/ipa/ipa_main.c @@ -1,7 +1,7 @@  // SPDX-License-Identifier: GPL-2.0  /* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. - * Copyright (C) 2018-2020 Linaro Ltd. + * Copyright (C) 2018-2021 Linaro Ltd.   */  #include <linux/types.h> @@ -22,6 +22,7 @@  #include "ipa_clock.h"  #include "ipa_data.h"  #include "ipa_endpoint.h" +#include "ipa_resource.h"  #include "ipa_cmd.h"  #include "ipa_reg.h"  #include "ipa_mem.h" @@ -66,7 +67,7 @@   */  /* The name of the GSI firmware file relative to /lib/firmware */ -#define IPA_FWS_PATH		"ipa_fws.mdt" +#define IPA_FW_PATH_DEFAULT	"ipa_fws.mdt"  #define IPA_PAS_ID		15  /* Shift of 19.2 MHz timestamp to achieve lower resolution timestamps */ @@ -146,13 +147,13 @@ int ipa_setup(struct ipa *ipa)  	if (ret)  		goto err_endpoint_teardown; -	ret = ipa_mem_setup(ipa); +	ret = ipa_mem_setup(ipa);	/* No matching teardown required */  	if (ret)  		goto err_command_disable; -	ret = ipa_table_setup(ipa); +	ret = ipa_table_setup(ipa);	/* No matching teardown required */  	if (ret) -		goto err_mem_teardown; +		goto err_command_disable;  	/* Enable the exception handling endpoint, and tell the hardware  	 * to use it by default. @@ -160,7 +161,7 @@ int ipa_setup(struct ipa *ipa)  	exception_endpoint = ipa->name_map[IPA_ENDPOINT_AP_LAN_RX];  	ret = ipa_endpoint_enable_one(exception_endpoint);  	if (ret) -		goto err_table_teardown; +		goto err_command_disable;  	ipa_endpoint_default_route_set(ipa, exception_endpoint->endpoint_id); @@ -178,10 +179,6 @@ int ipa_setup(struct ipa *ipa)  err_default_route_clear:  	ipa_endpoint_default_route_clear(ipa);  	ipa_endpoint_disable_one(exception_endpoint); -err_table_teardown: -	ipa_table_teardown(ipa); -err_mem_teardown: -	ipa_mem_teardown(ipa);  err_command_disable:  	ipa_endpoint_disable_one(command_endpoint);  err_endpoint_teardown: @@ -210,8 +207,6 @@ static void ipa_teardown(struct ipa *ipa)  	ipa_endpoint_default_route_clear(ipa);  	exception_endpoint = ipa->name_map[IPA_ENDPOINT_AP_LAN_RX];  	ipa_endpoint_disable_one(exception_endpoint); -	ipa_table_teardown(ipa); -	ipa_mem_teardown(ipa);  	command_endpoint = ipa->name_map[IPA_ENDPOINT_AP_COMMAND_TX];  	ipa_endpoint_disable_one(command_endpoint);  	ipa_endpoint_teardown(ipa); @@ -222,13 +217,13 @@ static void ipa_teardown(struct ipa *ipa)  	gsi_teardown(&ipa->gsi);  } -/* Configure QMB Core Master Port selection */ +/* Configure bus access behavior for IPA components */  static void ipa_hardware_config_comp(struct ipa *ipa)  {  	u32 val; -	/* Nothing to configure for IPA v3.5.1 */ -	if (ipa->version == IPA_VERSION_3_5_1) +	/* Nothing to configure prior to IPA v4.0 */ +	if (ipa->version < IPA_VERSION_4_0)  		return;  	val = ioread32(ipa->reg_virt + IPA_REG_COMP_CFG_OFFSET); @@ -249,56 +244,59 @@ static void ipa_hardware_config_comp(struct ipa *ipa)  	iowrite32(val, ipa->reg_virt + IPA_REG_COMP_CFG_OFFSET);  } -/* Configure DDR and PCIe max read/write QSB values */ -static void ipa_hardware_config_qsb(struct ipa *ipa) +/* Configure DDR and (possibly) PCIe max read/write QSB values */ +static void +ipa_hardware_config_qsb(struct ipa *ipa, const struct ipa_data *data)  { -	enum ipa_version version = ipa->version; -	u32 max0; -	u32 max1; +	const struct ipa_qsb_data *data0; +	const struct ipa_qsb_data *data1;  	u32 val; -	/* QMB_0 represents DDR; QMB_1 represents PCIe */ -	val = u32_encode_bits(8, GEN_QMB_0_MAX_WRITES_FMASK); -	switch (version) { -	case IPA_VERSION_4_2: -		max1 = 0;		/* PCIe not present */ -		break; -	case IPA_VERSION_4_5: -		max1 = 8; -		break; -	default: -		max1 = 4; -		break; -	} -	val |= u32_encode_bits(max1, GEN_QMB_1_MAX_WRITES_FMASK); +	/* assert(data->qsb_count > 0); */ +	/* assert(data->qsb_count < 3); */ + +	/* 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 */ +	val = u32_encode_bits(data0->max_writes, GEN_QMB_0_MAX_WRITES_FMASK); +	if (data->qsb_count > 1) +		val |= u32_encode_bits(data1->max_writes, +				       GEN_QMB_1_MAX_WRITES_FMASK);  	iowrite32(val, ipa->reg_virt + IPA_REG_QSB_MAX_WRITES_OFFSET); -	max1 = 12; -	switch (version) { -	case IPA_VERSION_3_5_1: -		max0 = 8; -		break; -	case IPA_VERSION_4_0: -	case IPA_VERSION_4_1: -		max0 = 12; -		break; -	case IPA_VERSION_4_2: -		max0 = 12; -		max1 = 0;		/* PCIe not present */ -		break; -	case IPA_VERSION_4_5: -		max0 = 0;		/* No limit (hardware maximum) */ -		break; -	} -	val = u32_encode_bits(max0, GEN_QMB_0_MAX_READS_FMASK); -	val |= u32_encode_bits(max1, GEN_QMB_1_MAX_READS_FMASK); -	if (version != IPA_VERSION_3_5_1) { -		/* GEN_QMB_0_MAX_READS_BEATS is 0 */ -		/* GEN_QMB_1_MAX_READS_BEATS is 0 */ +	/* Max outstanding read accesses for QSB masters */ +	val = u32_encode_bits(data0->max_reads, GEN_QMB_0_MAX_READS_FMASK); +	if (ipa->version >= IPA_VERSION_4_0) +		val |= u32_encode_bits(data0->max_reads_beats, +				       GEN_QMB_0_MAX_READS_BEATS_FMASK); +	if (data->qsb_count > 1) { +		val |= u32_encode_bits(data1->max_reads, +				       GEN_QMB_1_MAX_READS_FMASK); +		if (ipa->version >= IPA_VERSION_4_0) +			val |= u32_encode_bits(data1->max_reads_beats, +					       GEN_QMB_1_MAX_READS_BEATS_FMASK);  	}  	iowrite32(val, ipa->reg_virt + IPA_REG_QSB_MAX_READS_OFFSET);  } +/* 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. + */ +static u32 ipa_aggr_granularity_val(u32 usec) +{ +	/* assert(usec != 0); */ + +	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 @@ -385,21 +383,22 @@ static void ipa_hardware_dcd_deconfig(struct ipa *ipa)  /**   * ipa_hardware_config() - Primitive hardware initialization   * @ipa:	IPA pointer + * @data:	IPA configuration data   */ -static void ipa_hardware_config(struct ipa *ipa) +static void ipa_hardware_config(struct ipa *ipa, const struct ipa_data *data)  {  	enum ipa_version version = ipa->version;  	u32 granularity;  	u32 val; -	/* IPA v4.5 has no backward compatibility register */ +	/* IPA v4.5+ has no backward compatibility register */  	if (version < IPA_VERSION_4_5) { -		val = ipa_reg_bcr_val(version); +		val = data->backward_compat;  		iowrite32(val, ipa->reg_virt + IPA_REG_BCR_OFFSET);  	}  	/* Implement some hardware workarounds */ -	if (version != IPA_VERSION_3_5_1 && version < IPA_VERSION_4_5) { +	if (version >= IPA_VERSION_4_0 && version < IPA_VERSION_4_5) {  		/* Enable open global clocks (not needed for IPA v4.5) */  		val = GLOBAL_FMASK;  		val |= GLOBAL_2X_CLK_FMASK; @@ -414,7 +413,7 @@ static void ipa_hardware_config(struct ipa *ipa)  	ipa_hardware_config_comp(ipa);  	/* Configure system bus limits */ -	ipa_hardware_config_qsb(ipa); +	ipa_hardware_config_qsb(ipa, data);  	if (version < IPA_VERSION_4_5) {  		/* Configure aggregation timer granularity */ @@ -448,151 +447,6 @@ static void ipa_hardware_deconfig(struct ipa *ipa)  	ipa_hardware_dcd_deconfig(ipa);  } -#ifdef IPA_VALIDATION - -static bool ipa_resource_limits_valid(struct ipa *ipa, -				      const struct ipa_resource_data *data) -{ -	u32 group_count; -	u32 i; -	u32 j; - -	/* We program at most 6 source or destination resource group limits */ -	BUILD_BUG_ON(IPA_RESOURCE_GROUP_SRC_MAX > 6); - -	group_count = ipa_resource_group_src_count(ipa->version); -	if (!group_count || group_count > IPA_RESOURCE_GROUP_SRC_MAX) -		return false; - -	/* Return an error if a non-zero resource limit is specified -	 * for a resource group not supported by hardware. -	 */ -	for (i = 0; i < data->resource_src_count; i++) { -		const struct ipa_resource_src *resource; - -		resource = &data->resource_src[i]; -		for (j = group_count; j < IPA_RESOURCE_GROUP_SRC_MAX; j++) -			if (resource->limits[j].min || resource->limits[j].max) -				return false; -	} - -	group_count = ipa_resource_group_dst_count(ipa->version); -	if (!group_count || group_count > IPA_RESOURCE_GROUP_DST_MAX) -		return false; - -	for (i = 0; i < data->resource_dst_count; i++) { -		const struct ipa_resource_dst *resource; - -		resource = &data->resource_dst[i]; -		for (j = group_count; j < IPA_RESOURCE_GROUP_DST_MAX; j++) -			if (resource->limits[j].min || resource->limits[j].max) -				return false; -	} - -	return true; -} - -#else /* !IPA_VALIDATION */ - -static bool ipa_resource_limits_valid(struct ipa *ipa, -				      const struct ipa_resource_data *data) -{ -	return true; -} - -#endif /* !IPA_VALIDATION */ - -static void -ipa_resource_config_common(struct ipa *ipa, u32 offset, -			   const struct ipa_resource_limits *xlimits, -			   const struct ipa_resource_limits *ylimits) -{ -	u32 val; - -	val = u32_encode_bits(xlimits->min, X_MIN_LIM_FMASK); -	val |= u32_encode_bits(xlimits->max, X_MAX_LIM_FMASK); -	if (ylimits) { -		val |= u32_encode_bits(ylimits->min, Y_MIN_LIM_FMASK); -		val |= u32_encode_bits(ylimits->max, Y_MAX_LIM_FMASK); -	} - -	iowrite32(val, ipa->reg_virt + offset); -} - -static void ipa_resource_config_src(struct ipa *ipa, -				    const struct ipa_resource_src *resource) -{ -	u32 group_count = ipa_resource_group_src_count(ipa->version); -	const struct ipa_resource_limits *ylimits; -	u32 offset; - -	offset = IPA_REG_SRC_RSRC_GRP_01_RSRC_TYPE_N_OFFSET(resource->type); -	ylimits = group_count == 1 ? NULL : &resource->limits[1]; -	ipa_resource_config_common(ipa, offset, &resource->limits[0], ylimits); - -	if (group_count < 2) -		return; - -	offset = IPA_REG_SRC_RSRC_GRP_23_RSRC_TYPE_N_OFFSET(resource->type); -	ylimits = group_count == 3 ? NULL : &resource->limits[3]; -	ipa_resource_config_common(ipa, offset, &resource->limits[2], ylimits); - -	if (group_count < 4) -		return; - -	offset = IPA_REG_SRC_RSRC_GRP_45_RSRC_TYPE_N_OFFSET(resource->type); -	ylimits = group_count == 5 ? NULL : &resource->limits[5]; -	ipa_resource_config_common(ipa, offset, &resource->limits[4], ylimits); -} - -static void ipa_resource_config_dst(struct ipa *ipa, -				    const struct ipa_resource_dst *resource) -{ -	u32 group_count = ipa_resource_group_dst_count(ipa->version); -	const struct ipa_resource_limits *ylimits; -	u32 offset; - -	offset = IPA_REG_DST_RSRC_GRP_01_RSRC_TYPE_N_OFFSET(resource->type); -	ylimits = group_count == 1 ? NULL : &resource->limits[1]; -	ipa_resource_config_common(ipa, offset, &resource->limits[0], ylimits); - -	if (group_count < 2) -		return; - -	offset = IPA_REG_DST_RSRC_GRP_23_RSRC_TYPE_N_OFFSET(resource->type); -	ylimits = group_count == 3 ? NULL : &resource->limits[3]; -	ipa_resource_config_common(ipa, offset, &resource->limits[2], ylimits); - -	if (group_count < 4) -		return; - -	offset = IPA_REG_DST_RSRC_GRP_45_RSRC_TYPE_N_OFFSET(resource->type); -	ylimits = group_count == 5 ? NULL : &resource->limits[5]; -	ipa_resource_config_common(ipa, offset, &resource->limits[4], ylimits); -} - -static int -ipa_resource_config(struct ipa *ipa, const struct ipa_resource_data *data) -{ -	u32 i; - -	if (!ipa_resource_limits_valid(ipa, data)) -		return -EINVAL; - -	for (i = 0; i < data->resource_src_count; i++) -		ipa_resource_config_src(ipa, &data->resource_src[i]); - -	for (i = 0; i < data->resource_dst_count; i++) -		ipa_resource_config_dst(ipa, &data->resource_dst[i]); - -	return 0; -} - -static void ipa_resource_deconfig(struct ipa *ipa) -{ -	/* Nothing to do */ -} -  /**   * ipa_config() - Configure IPA hardware   * @ipa:	IPA pointer @@ -610,7 +464,7 @@ static int ipa_config(struct ipa *ipa, const struct ipa_data *data)  	 */  	ipa_clock_get(ipa); -	ipa_hardware_config(ipa); +	ipa_hardware_config(ipa, data);  	ret = ipa_endpoint_config(ipa);  	if (ret) @@ -620,23 +474,20 @@ static int ipa_config(struct ipa *ipa, const struct ipa_data *data)  	if (ret)  		goto err_endpoint_deconfig; -	ipa_table_config(ipa); +	ipa_table_config(ipa);		/* No deconfig required */ -	/* Assign resource limitation to each group */ +	/* Assign resource limitation to each group; no deconfig required */  	ret = ipa_resource_config(ipa, data->resource_data);  	if (ret) -		goto err_table_deconfig; +		goto err_mem_deconfig;  	ret = ipa_modem_config(ipa);  	if (ret) -		goto err_resource_deconfig; +		goto err_mem_deconfig;  	return 0; -err_resource_deconfig: -	ipa_resource_deconfig(ipa); -err_table_deconfig: -	ipa_table_deconfig(ipa); +err_mem_deconfig:  	ipa_mem_deconfig(ipa);  err_endpoint_deconfig:  	ipa_endpoint_deconfig(ipa); @@ -654,8 +505,6 @@ err_hardware_deconfig:  static void ipa_deconfig(struct ipa *ipa)  {  	ipa_modem_deconfig(ipa); -	ipa_resource_deconfig(ipa); -	ipa_table_deconfig(ipa);  	ipa_mem_deconfig(ipa);  	ipa_endpoint_deconfig(ipa);  	ipa_hardware_deconfig(ipa); @@ -668,6 +517,7 @@ static int ipa_firmware_load(struct device *dev)  	struct device_node *node;  	struct resource res;  	phys_addr_t phys; +	const char *path;  	ssize_t size;  	void *virt;  	int ret; @@ -685,9 +535,17 @@ static int ipa_firmware_load(struct device *dev)  		return ret;  	} -	ret = request_firmware(&fw, IPA_FWS_PATH, dev); +	/* Use name from DTB if specified; use default for *any* error */ +	ret = of_property_read_string(dev->of_node, "firmware-name", &path);  	if (ret) { -		dev_err(dev, "error %d requesting \"%s\"\n", ret, IPA_FWS_PATH); +		dev_dbg(dev, "error %d getting \"firmware-name\" resource\n", +			ret); +		path = IPA_FW_PATH_DEFAULT; +	} + +	ret = request_firmware(&fw, path, dev); +	if (ret) { +		dev_err(dev, "error %d requesting \"%s\"\n", ret, path);  		return ret;  	} @@ -700,13 +558,11 @@ static int ipa_firmware_load(struct device *dev)  		goto out_release_firmware;  	} -	ret = qcom_mdt_load(dev, fw, IPA_FWS_PATH, IPA_PAS_ID, -			    virt, phys, size, NULL); +	ret = qcom_mdt_load(dev, fw, path, IPA_PAS_ID, virt, phys, size, NULL);  	if (ret) -		dev_err(dev, "error %d loading \"%s\"\n", ret, IPA_FWS_PATH); +		dev_err(dev, "error %d loading \"%s\"\n", ret, path);  	else if ((ret = qcom_scm_pas_auth_and_reset(IPA_PAS_ID))) -		dev_err(dev, "error %d authenticating \"%s\"\n", ret, -			IPA_FWS_PATH); +		dev_err(dev, "error %d authenticating \"%s\"\n", ret, path);  	memunmap(virt);  out_release_firmware: @@ -718,11 +574,23 @@ out_release_firmware:  static const struct of_device_id ipa_match[] = {  	{  		.compatible	= "qcom,sdm845-ipa", -		.data		= &ipa_data_sdm845, +		.data		= &ipa_data_v3_5_1,  	},  	{  		.compatible	= "qcom,sc7180-ipa", -		.data		= &ipa_data_sc7180, +		.data		= &ipa_data_v4_2, +	}, +	{ +		.compatible	= "qcom,sdx55-ipa", +		.data		= &ipa_data_v4_5, +	}, +	{ +		.compatible	= "qcom,sm8350-ipa", +		.data		= &ipa_data_v4_9, +	}, +	{ +		.compatible	= "qcom,sc7280-ipa", +		.data		= &ipa_data_v4_11,  	},  	{ },  }; @@ -735,8 +603,14 @@ MODULE_DEVICE_TABLE(of, ipa_match);  static void ipa_validate_build(void)  {  #ifdef IPA_VALIDATE -	/* We assume we're working on 64-bit hardware */ -	BUILD_BUG_ON(!IS_ENABLED(CONFIG_64BIT)); +	/* At one time we assumed a 64-bit build, allowing some do_div() +	 * calls to be replaced by simple division or modulo operations. +	 * We currently only perform divide and modulo operations on u32, +	 * u16, or size_t objects, and of those only size_t has any chance +	 * of being a 64-bit value.  (It should be guaranteed 32 bits wide +	 * on a 32-bit build, but there is no harm in verifying that.) +	 */ +	BUILD_BUG_ON(!IS_ENABLED(CONFIG_64BIT) && sizeof(size_t) != 4);  	/* Code assumes the EE ID for the AP is 0 (zeroed structure field) */  	BUILD_BUG_ON(GSI_EE_AP != 0); | 
