diff options
Diffstat (limited to 'drivers/platform/x86/intel')
18 files changed, 873 insertions, 178 deletions
| diff --git a/drivers/platform/x86/intel/bytcrc_pwrsrc.c b/drivers/platform/x86/intel/bytcrc_pwrsrc.c index 8a022b90d12d..418b71af27ff 100644 --- a/drivers/platform/x86/intel/bytcrc_pwrsrc.c +++ b/drivers/platform/x86/intel/bytcrc_pwrsrc.c @@ -158,17 +158,16 @@ static int crc_pwrsrc_probe(struct platform_device *pdev)  	return 0;  } -static int crc_pwrsrc_remove(struct platform_device *pdev) +static void crc_pwrsrc_remove(struct platform_device *pdev)  {  	struct crc_pwrsrc_data *data = platform_get_drvdata(pdev);  	debugfs_remove_recursive(data->debug_dentry); -	return 0;  }  static struct platform_driver crc_pwrsrc_driver = {  	.probe = crc_pwrsrc_probe, -	.remove = crc_pwrsrc_remove, +	.remove_new = crc_pwrsrc_remove,  	.driver = {  		.name = "crystal_cove_pwrsrc",  	}, diff --git a/drivers/platform/x86/intel/ifs/core.c b/drivers/platform/x86/intel/ifs/core.c index 306f886b52d2..7b11198d85a1 100644 --- a/drivers/platform/x86/intel/ifs/core.c +++ b/drivers/platform/x86/intel/ifs/core.c @@ -1,6 +1,7 @@  // SPDX-License-Identifier: GPL-2.0-only  /* Copyright(c) 2022 Intel Corporation. */ +#include <linux/bitfield.h>  #include <linux/module.h>  #include <linux/kdev_t.h>  #include <linux/semaphore.h> @@ -10,13 +11,16 @@  #include "ifs.h" -#define X86_MATCH(model)				\ +#define X86_MATCH(model, array_gen)				\  	X86_MATCH_VENDOR_FAM_MODEL_FEATURE(INTEL, 6,	\ -		INTEL_FAM6_##model, X86_FEATURE_CORE_CAPABILITIES, NULL) +		INTEL_FAM6_##model, X86_FEATURE_CORE_CAPABILITIES, array_gen)  static const struct x86_cpu_id ifs_cpu_ids[] __initconst = { -	X86_MATCH(SAPPHIRERAPIDS_X), -	X86_MATCH(EMERALDRAPIDS_X), +	X86_MATCH(SAPPHIRERAPIDS_X, ARRAY_GEN0), +	X86_MATCH(EMERALDRAPIDS_X, ARRAY_GEN0), +	X86_MATCH(GRANITERAPIDS_X, ARRAY_GEN0), +	X86_MATCH(GRANITERAPIDS_D, ARRAY_GEN0), +	X86_MATCH(ATOM_CRESTMONT_X, ARRAY_GEN1),  	{}  };  MODULE_DEVICE_TABLE(x86cpu, ifs_cpu_ids); @@ -94,6 +98,9 @@ static int __init ifs_init(void)  	for (i = 0; i < IFS_NUMTESTS; i++) {  		if (!(msrval & BIT(ifs_devices[i].test_caps->integrity_cap_bit)))  			continue; +		ifs_devices[i].rw_data.generation = FIELD_GET(MSR_INTEGRITY_CAPS_SAF_GEN_MASK, +							      msrval); +		ifs_devices[i].rw_data.array_gen = (u32)m->driver_data;  		ret = misc_register(&ifs_devices[i].misc);  		if (ret)  			goto err_exit; diff --git a/drivers/platform/x86/intel/ifs/ifs.h b/drivers/platform/x86/intel/ifs/ifs.h index 93191855890f..56b9f3e3cf76 100644 --- a/drivers/platform/x86/intel/ifs/ifs.h +++ b/drivers/platform/x86/intel/ifs/ifs.h @@ -137,6 +137,10 @@  #define MSR_CHUNKS_AUTHENTICATION_STATUS	0x000002c5  #define MSR_ACTIVATE_SCAN			0x000002c6  #define MSR_SCAN_STATUS				0x000002c7 +#define MSR_ARRAY_TRIGGER			0x000002d6 +#define MSR_ARRAY_STATUS			0x000002d7 +#define MSR_SAF_CTRL				0x000004f0 +  #define SCAN_NOT_TESTED				0  #define SCAN_TEST_PASS				1  #define SCAN_TEST_FAIL				2 @@ -144,6 +148,9 @@  #define IFS_TYPE_SAF			0  #define IFS_TYPE_ARRAY_BIST		1 +#define ARRAY_GEN0			0 +#define ARRAY_GEN1			1 +  /* MSR_SCAN_HASHES_STATUS bit fields */  union ifs_scan_hashes_status {  	u64	data; @@ -158,6 +165,19 @@ union ifs_scan_hashes_status {  	};  }; +union ifs_scan_hashes_status_gen2 { +	u64	data; +	struct { +		u16	chunk_size; +		u16	num_chunks; +		u32	error_code		:8; +		u32	chunks_in_stride	:9; +		u32	rsvd			:2; +		u32	max_core_limit		:12; +		u32	valid			:1; +	}; +}; +  /* MSR_CHUNKS_AUTH_STATUS bit fields */  union ifs_chunks_auth_status {  	u64	data; @@ -170,13 +190,31 @@ union ifs_chunks_auth_status {  	};  }; +union ifs_chunks_auth_status_gen2 { +	u64	data; +	struct { +		u16	valid_chunks; +		u16	total_chunks; +		u32	error_code	:8; +		u32	rsvd2		:24; +	}; +}; +  /* MSR_ACTIVATE_SCAN bit fields */  union ifs_scan {  	u64	data;  	struct { -		u32	start	:8; -		u32	stop	:8; -		u32	rsvd	:16; +		union { +			struct { +				u8	start; +				u8	stop; +				u16	rsvd; +			} gen0; +			struct { +				u16	start; +				u16	stop; +			} gen2; +		};  		u32	delay	:31;  		u32	sigmce	:1;  	}; @@ -186,9 +224,17 @@ union ifs_scan {  union ifs_status {  	u64	data;  	struct { -		u32	chunk_num		:8; -		u32	chunk_stop_index	:8; -		u32	rsvd1			:16; +		union { +			struct { +				u8	chunk_num; +				u8	chunk_stop_index; +				u16	rsvd1; +			} gen0; +			struct { +				u16	chunk_num; +				u16	chunk_stop_index; +			} gen2; +		};  		u32	error_code		:8;  		u32	rsvd2			:22;  		u32	control_error		:1; @@ -229,6 +275,9 @@ struct ifs_test_caps {   * @status: it holds simple status pass/fail/untested   * @scan_details: opaque scan status code from h/w   * @cur_batch: number indicating the currently loaded test file + * @generation: IFS test generation enumerated by hardware + * @chunk_size: size of a test chunk + * @array_gen: test generation of array test   */  struct ifs_data {  	int	loaded_version; @@ -238,6 +287,9 @@ struct ifs_data {  	int	status;  	u64	scan_details;  	u32	cur_batch; +	u32	generation; +	u32	chunk_size; +	u32	array_gen;  };  struct ifs_work { diff --git a/drivers/platform/x86/intel/ifs/load.c b/drivers/platform/x86/intel/ifs/load.c index e6ae8265f3a3..a1ee1a74fc3c 100644 --- a/drivers/platform/x86/intel/ifs/load.c +++ b/drivers/platform/x86/intel/ifs/load.c @@ -2,8 +2,9 @@  /* Copyright(c) 2022 Intel Corporation. */  #include <linux/firmware.h> +#include <linux/sizes.h>  #include <asm/cpu.h> -#include <asm/microcode_intel.h> +#include <asm/microcode.h>  #include "ifs.h" @@ -26,6 +27,11 @@ union meta_data {  #define IFS_HEADER_SIZE	(sizeof(struct microcode_header_intel))  #define META_TYPE_IFS	1 +#define INVALIDATE_STRIDE	0x1UL +#define IFS_GEN_STRIDE_AWARE	2 +#define AUTH_INTERRUPTED_ERROR	5 +#define IFS_AUTH_RETRY_CT	10 +  static  struct microcode_header_intel *ifs_header_ptr;	/* pointer to the ifs image header */  static u64 ifs_hash_ptr;			/* Address of ifs metadata (hash) */  static u64 ifs_test_image_ptr;			/* 256B aligned address of test pattern */ @@ -44,7 +50,10 @@ static const char * const scan_hash_status[] = {  static const char * const scan_authentication_status[] = {  	[0] = "No error reported",  	[1] = "Attempt to authenticate a chunk which is already marked as authentic", -	[2] = "Chunk authentication error. The hash of chunk did not match expected value" +	[2] = "Chunk authentication error. The hash of chunk did not match expected value", +	[3] = "Reserved", +	[4] = "Chunk outside the current stride", +	[5] = "Authentication flow interrupted",  };  #define MC_HEADER_META_TYPE_END		(0) @@ -56,12 +65,13 @@ struct metadata_header {  static struct metadata_header *find_meta_data(void *ucode, unsigned int meta_type)  { +	struct microcode_header_intel *hdr = &((struct microcode_intel *)ucode)->hdr;  	struct metadata_header *meta_header;  	unsigned long data_size, total_meta;  	unsigned long meta_size = 0; -	data_size = get_datasize(ucode); -	total_meta = ((struct microcode_intel *)ucode)->hdr.metasize; +	data_size = intel_microcode_get_datasize(hdr); +	total_meta = hdr->metasize;  	if (!total_meta)  		return NULL; @@ -79,6 +89,23 @@ static struct metadata_header *find_meta_data(void *ucode, unsigned int meta_typ  	return NULL;  } +static void hashcopy_err_message(struct device *dev, u32 err_code) +{ +	if (err_code >= ARRAY_SIZE(scan_hash_status)) +		dev_err(dev, "invalid error code 0x%x for hash copy\n", err_code); +	else +		dev_err(dev, "Hash copy error : %s\n", scan_hash_status[err_code]); +} + +static void auth_err_message(struct device *dev, u32 err_code) +{ +	if (err_code >= ARRAY_SIZE(scan_authentication_status)) +		dev_err(dev, "invalid error code 0x%x for authentication\n", err_code); +	else +		dev_err(dev, "Chunk authentication error : %s\n", +			scan_authentication_status[err_code]); +} +  /*   * To copy scan hashes and authenticate test chunks, the initiating cpu must point   * to the EDX:EAX to the test image in linear address. @@ -108,11 +135,7 @@ static void copy_hashes_authenticate_chunks(struct work_struct *work)  	if (!hashes_status.valid) {  		ifsd->loading_error = true; -		if (err_code >= ARRAY_SIZE(scan_hash_status)) { -			dev_err(dev, "invalid error code 0x%x for hash copy\n", err_code); -			goto done; -		} -		dev_err(dev, "Hash copy error : %s", scan_hash_status[err_code]); +		hashcopy_err_message(dev, err_code);  		goto done;  	} @@ -132,13 +155,7 @@ static void copy_hashes_authenticate_chunks(struct work_struct *work)  		if (err_code) {  			ifsd->loading_error = true; -			if (err_code >= ARRAY_SIZE(scan_authentication_status)) { -				dev_err(dev, -					"invalid error code 0x%x for authentication\n", err_code); -				goto done; -			} -			dev_err(dev, "Chunk authentication error %s\n", -				scan_authentication_status[err_code]); +			auth_err_message(dev, err_code);  			goto done;  		}  	} @@ -146,6 +163,102 @@ done:  	complete(&ifs_done);  } +static int get_num_chunks(int gen, union ifs_scan_hashes_status_gen2 status) +{ +	return gen >= IFS_GEN_STRIDE_AWARE ? status.chunks_in_stride : status.num_chunks; +} + +static bool need_copy_scan_hashes(struct ifs_data *ifsd) +{ +	return !ifsd->loaded || +		ifsd->generation < IFS_GEN_STRIDE_AWARE || +		ifsd->loaded_version != ifs_header_ptr->rev; +} + +static int copy_hashes_authenticate_chunks_gen2(struct device *dev) +{ +	union ifs_scan_hashes_status_gen2 hashes_status; +	union ifs_chunks_auth_status_gen2 chunk_status; +	u32 err_code, valid_chunks, total_chunks; +	int i, num_chunks, chunk_size; +	union meta_data *ifs_meta; +	int starting_chunk_nr; +	struct ifs_data *ifsd; +	u64 linear_addr, base; +	u64 chunk_table[2]; +	int retry_count; + +	ifsd = ifs_get_data(dev); + +	if (need_copy_scan_hashes(ifsd)) { +		wrmsrl(MSR_COPY_SCAN_HASHES, ifs_hash_ptr); +		rdmsrl(MSR_SCAN_HASHES_STATUS, hashes_status.data); + +		/* enumerate the scan image information */ +		chunk_size = hashes_status.chunk_size * SZ_1K; +		err_code = hashes_status.error_code; + +		num_chunks = get_num_chunks(ifsd->generation, hashes_status); + +		if (!hashes_status.valid) { +			hashcopy_err_message(dev, err_code); +			return -EIO; +		} +		ifsd->loaded_version = ifs_header_ptr->rev; +		ifsd->chunk_size = chunk_size; +	} else { +		num_chunks = ifsd->valid_chunks; +		chunk_size = ifsd->chunk_size; +	} + +	if (ifsd->generation >= IFS_GEN_STRIDE_AWARE) { +		wrmsrl(MSR_SAF_CTRL, INVALIDATE_STRIDE); +		rdmsrl(MSR_CHUNKS_AUTHENTICATION_STATUS, chunk_status.data); +		if (chunk_status.valid_chunks != 0) { +			dev_err(dev, "Couldn't invalidate installed stride - %d\n", +				chunk_status.valid_chunks); +			return -EIO; +		} +	} + +	base = ifs_test_image_ptr; +	ifs_meta = (union meta_data *)find_meta_data(ifs_header_ptr, META_TYPE_IFS); +	starting_chunk_nr = ifs_meta->starting_chunk; + +	/* scan data authentication and copy chunks to secured memory */ +	for (i = 0; i < num_chunks; i++) { +		retry_count = IFS_AUTH_RETRY_CT; +		linear_addr = base + i * chunk_size; + +		chunk_table[0] = starting_chunk_nr + i; +		chunk_table[1] = linear_addr; +		do { +			wrmsrl(MSR_AUTHENTICATE_AND_COPY_CHUNK, (u64)chunk_table); +			rdmsrl(MSR_CHUNKS_AUTHENTICATION_STATUS, chunk_status.data); +			err_code = chunk_status.error_code; +		} while (err_code == AUTH_INTERRUPTED_ERROR && --retry_count); + +		if (err_code) { +			ifsd->loading_error = true; +			auth_err_message(dev, err_code); +			return -EIO; +		} +	} + +	valid_chunks = chunk_status.valid_chunks; +	total_chunks = chunk_status.total_chunks; + +	if (valid_chunks != total_chunks) { +		ifsd->loading_error = true; +		dev_err(dev, "Couldn't authenticate all the chunks. Authenticated %d total %d.\n", +			valid_chunks, total_chunks); +		return -EIO; +	} +	ifsd->valid_chunks = valid_chunks; + +	return 0; +} +  static int validate_ifs_metadata(struct device *dev)  {  	struct ifs_data *ifsd = ifs_get_data(dev); @@ -178,6 +291,13 @@ static int validate_ifs_metadata(struct device *dev)  		return ret;  	} +	if (ifs_meta->chunks_per_stride && +	    (ifs_meta->starting_chunk % ifs_meta->chunks_per_stride != 0)) { +		dev_warn(dev, "Starting chunk num %u not a multiple of chunks_per_stride %u\n", +			 ifs_meta->starting_chunk, ifs_meta->chunks_per_stride); +		return ret; +	} +  	return 0;  } @@ -198,7 +318,9 @@ static int scan_chunks_sanity_check(struct device *dev)  		return ret;  	ifsd->loading_error = false; -	ifsd->loaded_version = ifs_header_ptr->rev; + +	if (ifsd->generation > 0) +		return copy_hashes_authenticate_chunks_gen2(dev);  	/* copy the scan hash and authenticate per package */  	cpus_read_lock(); @@ -218,6 +340,7 @@ static int scan_chunks_sanity_check(struct device *dev)  		ifs_pkg_auth[curr_pkg] = 1;  	}  	ret = 0; +	ifsd->loaded_version = ifs_header_ptr->rev;  out:  	cpus_read_unlock(); @@ -226,7 +349,7 @@ out:  static int image_sanity_check(struct device *dev, const struct microcode_header_intel *data)  { -	struct ucode_cpu_info uci; +	struct cpu_signature sig;  	/* Provide a specific error message when loading an older/unsupported image */  	if (data->hdrver != MC_HEADER_TYPE_IFS) { @@ -239,11 +362,9 @@ static int image_sanity_check(struct device *dev, const struct microcode_header_  		return -EINVAL;  	} -	intel_cpu_collect_info(&uci); +	intel_collect_cpu_info(&sig); -	if (!intel_find_matching_signature((void *)data, -					   uci.cpu_sig.sig, -					   uci.cpu_sig.pf)) { +	if (!intel_find_matching_signature((void *)data, &sig)) {  		dev_err(dev, "cpu signature, processor flags not matching\n");  		return -EINVAL;  	} @@ -259,6 +380,7 @@ int ifs_load_firmware(struct device *dev)  {  	const struct ifs_test_caps *test = ifs_get_test_caps(dev);  	struct ifs_data *ifsd = ifs_get_data(dev); +	unsigned int expected_size;  	const struct firmware *fw;  	char scan_path[64];  	int ret = -EINVAL; @@ -273,6 +395,13 @@ int ifs_load_firmware(struct device *dev)  		goto done;  	} +	expected_size = ((struct microcode_header_intel *)fw->data)->totalsize; +	if (fw->size != expected_size) { +		dev_err(dev, "File size mismatch (expected %u, actual %zu). Corrupted IFS image.\n", +			expected_size, fw->size); +		return -EINVAL; +	} +  	ret = image_sanity_check(dev, (struct microcode_header_intel *)fw->data);  	if (ret)  		goto release; diff --git a/drivers/platform/x86/intel/ifs/runtest.c b/drivers/platform/x86/intel/ifs/runtest.c index 1061eb7ec399..13ecd55c6668 100644 --- a/drivers/platform/x86/intel/ifs/runtest.c +++ b/drivers/platform/x86/intel/ifs/runtest.c @@ -40,6 +40,8 @@ enum ifs_status_err_code {  	IFS_UNASSIGNED_ERROR_CODE		= 7,  	IFS_EXCEED_NUMBER_OF_THREADS_CONCURRENT	= 8,  	IFS_INTERRUPTED_DURING_EXECUTION	= 9, +	IFS_UNASSIGNED_ERROR_CODE_0xA		= 0xA, +	IFS_CORRUPTED_CHUNK		= 0xB,  };  static const char * const scan_test_status[] = { @@ -55,6 +57,8 @@ static const char * const scan_test_status[] = {  	[IFS_EXCEED_NUMBER_OF_THREADS_CONCURRENT] =  	"Exceeded number of Logical Processors (LP) allowed to run Scan-At-Field concurrently",  	[IFS_INTERRUPTED_DURING_EXECUTION] = "Interrupt occurred prior to SCAN start", +	[IFS_UNASSIGNED_ERROR_CODE_0xA] = "Unassigned error code 0xA", +	[IFS_CORRUPTED_CHUNK] = "Scan operation aborted due to corrupted image. Try reloading",  };  static void message_not_tested(struct device *dev, int cpu, union ifs_status status) @@ -123,6 +127,8 @@ static bool can_restart(union ifs_status status)  	case IFS_MISMATCH_ARGUMENTS_BETWEEN_THREADS:  	case IFS_CORE_NOT_CAPABLE_CURRENTLY:  	case IFS_UNASSIGNED_ERROR_CODE: +	case IFS_UNASSIGNED_ERROR_CODE_0xA: +	case IFS_CORRUPTED_CHUNK:  		break;  	}  	return false; @@ -171,21 +177,31 @@ static void ifs_test_core(int cpu, struct device *dev)  	union ifs_status status;  	unsigned long timeout;  	struct ifs_data *ifsd; +	int to_start, to_stop; +	int status_chunk;  	u64 msrvals[2];  	int retries;  	ifsd = ifs_get_data(dev); -	activate.rsvd = 0; +	activate.gen0.rsvd = 0;  	activate.delay = IFS_THREAD_WAIT;  	activate.sigmce = 0; -	activate.start = 0; -	activate.stop = ifsd->valid_chunks - 1; +	to_start = 0; +	to_stop = ifsd->valid_chunks - 1; + +	if (ifsd->generation) { +		activate.gen2.start = to_start; +		activate.gen2.stop = to_stop; +	} else { +		activate.gen0.start = to_start; +		activate.gen0.stop = to_stop; +	}  	timeout = jiffies + HZ / 2;  	retries = MAX_IFS_RETRIES; -	while (activate.start <= activate.stop) { +	while (to_start <= to_stop) {  		if (time_after(jiffies, timeout)) {  			status.error_code = IFS_SW_TIMEOUT;  			break; @@ -196,13 +212,14 @@ static void ifs_test_core(int cpu, struct device *dev)  		status.data = msrvals[1]; -		trace_ifs_status(cpu, activate, status); +		trace_ifs_status(cpu, to_start, to_stop, status.data);  		/* Some cases can be retried, give up for others */  		if (!can_restart(status))  			break; -		if (status.chunk_num == activate.start) { +		status_chunk = ifsd->generation ? status.gen2.chunk_num : status.gen0.chunk_num; +		if (status_chunk == to_start) {  			/* Check for forward progress */  			if (--retries == 0) {  				if (status.error_code == IFS_NO_ERROR) @@ -211,7 +228,11 @@ static void ifs_test_core(int cpu, struct device *dev)  			}  		} else {  			retries = MAX_IFS_RETRIES; -			activate.start = status.chunk_num; +			if (ifsd->generation) +				activate.gen2.start = status_chunk; +			else +				activate.gen0.start = status_chunk; +			to_start = status_chunk;  		}  	} @@ -308,6 +329,38 @@ static void ifs_array_test_core(int cpu, struct device *dev)  		ifsd->status = SCAN_TEST_PASS;  } +#define ARRAY_GEN1_TEST_ALL_ARRAYS	0x0ULL +#define ARRAY_GEN1_STATUS_FAIL		0x1ULL + +static int do_array_test_gen1(void *status) +{ +	int cpu = smp_processor_id(); +	int first; + +	first = cpumask_first(cpu_smt_mask(cpu)); + +	if (cpu == first) { +		wrmsrl(MSR_ARRAY_TRIGGER, ARRAY_GEN1_TEST_ALL_ARRAYS); +		rdmsrl(MSR_ARRAY_STATUS, *((u64 *)status)); +	} + +	return 0; +} + +static void ifs_array_test_gen1(int cpu, struct device *dev) +{ +	struct ifs_data *ifsd = ifs_get_data(dev); +	u64 status = 0; + +	stop_core_cpuslocked(cpu, do_array_test_gen1, &status); +	ifsd->scan_details = status; + +	if (status & ARRAY_GEN1_STATUS_FAIL) +		ifsd->status = SCAN_TEST_FAIL; +	else +		ifsd->status = SCAN_TEST_PASS; +} +  /*   * Initiate per core test. It wakes up work queue threads on the target cpu and   * its sibling cpu. Once all sibling threads wake up, the scan test gets executed and @@ -331,14 +384,18 @@ int do_core_test(int cpu, struct device *dev)  	switch (test->test_num) {  	case IFS_TYPE_SAF:  		if (!ifsd->loaded) -			return -EPERM; -		ifs_test_core(cpu, dev); +			ret = -EPERM; +		else +			ifs_test_core(cpu, dev);  		break;  	case IFS_TYPE_ARRAY_BIST: -		ifs_array_test_core(cpu, dev); +		if (ifsd->array_gen == ARRAY_GEN0) +			ifs_array_test_core(cpu, dev); +		else +			ifs_array_test_gen1(cpu, dev);  		break;  	default: -		return -EINVAL; +		ret = -EINVAL;  	}  out:  	cpus_read_unlock(); diff --git a/drivers/platform/x86/intel/int3472/clk_and_regulator.c b/drivers/platform/x86/intel/int3472/clk_and_regulator.c index ef4b3141efcd..16e36ac0a7b4 100644 --- a/drivers/platform/x86/intel/int3472/clk_and_regulator.c +++ b/drivers/platform/x86/intel/int3472/clk_and_regulator.c @@ -162,9 +162,8 @@ out_free_init_name:  }  int skl_int3472_register_gpio_clock(struct int3472_discrete_device *int3472, -				    struct acpi_resource_gpio *agpio, u32 polarity) +				    struct gpio_desc *gpio)  { -	char *path = agpio->resource_source.string_ptr;  	struct clk_init_data init = {  		.ops = &skl_int3472_clock_ops,  		.flags = CLK_GET_RATE_NOCACHE, @@ -174,26 +173,12 @@ int skl_int3472_register_gpio_clock(struct int3472_discrete_device *int3472,  	if (int3472->clock.cl)  		return -EBUSY; -	int3472->clock.ena_gpio = acpi_get_and_request_gpiod(path, agpio->pin_table[0], -							     "int3472,clk-enable"); -	if (IS_ERR(int3472->clock.ena_gpio)) { -		ret = PTR_ERR(int3472->clock.ena_gpio); -		int3472->clock.ena_gpio = NULL; -		return dev_err_probe(int3472->dev, ret, "getting clk-enable GPIO\n"); -	} - -	if (polarity == GPIO_ACTIVE_LOW) -		gpiod_toggle_active_low(int3472->clock.ena_gpio); - -	/* Ensure the pin is in output mode and non-active state */ -	gpiod_direction_output(int3472->clock.ena_gpio, 0); +	int3472->clock.ena_gpio = gpio;  	init.name = kasprintf(GFP_KERNEL, "%s-clk",  			      acpi_dev_name(int3472->adev)); -	if (!init.name) { -		ret = -ENOMEM; -		goto out_put_gpio; -	} +	if (!init.name) +		return -ENOMEM;  	int3472->clock.frequency = skl_int3472_get_clk_frequency(int3472); @@ -219,8 +204,6 @@ err_unregister_clk:  	clk_unregister(int3472->clock.clk);  out_free_init_name:  	kfree(init.name); -out_put_gpio: -	gpiod_put(int3472->clock.ena_gpio);  	return ret;  } @@ -232,7 +215,6 @@ void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472)  	clkdev_drop(int3472->clock.cl);  	clk_unregister(int3472->clock.clk); -	gpiod_put(int3472->clock.ena_gpio);  }  /* @@ -273,14 +255,13 @@ static const struct dmi_system_id skl_int3472_regulator_second_sensor[] = {  };  int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, -				   struct acpi_resource_gpio *agpio) +				   struct gpio_desc *gpio)  { -	char *path = agpio->resource_source.string_ptr;  	struct regulator_init_data init_data = { };  	struct regulator_config cfg = { };  	const char *second_sensor = NULL;  	const struct dmi_system_id *id; -	int i, j, ret; +	int i, j;  	id = dmi_first_match(skl_int3472_regulator_second_sensor);  	if (id) @@ -314,16 +295,7 @@ int skl_int3472_register_regulator(struct int3472_discrete_device *int3472,  						int3472->regulator.supply_name,  						&int3472_gpio_regulator_ops); -	int3472->regulator.gpio = acpi_get_and_request_gpiod(path, agpio->pin_table[0], -							     "int3472,regulator"); -	if (IS_ERR(int3472->regulator.gpio)) { -		ret = PTR_ERR(int3472->regulator.gpio); -		int3472->regulator.gpio = NULL; -		return dev_err_probe(int3472->dev, ret, "getting regulator GPIO\n"); -	} - -	/* Ensure the pin is in output mode and non-active state */ -	gpiod_direction_output(int3472->regulator.gpio, 0); +	int3472->regulator.gpio = gpio;  	cfg.dev = &int3472->adev->dev;  	cfg.init_data = &init_data; @@ -332,21 +304,11 @@ int skl_int3472_register_regulator(struct int3472_discrete_device *int3472,  	int3472->regulator.rdev = regulator_register(int3472->dev,  						     &int3472->regulator.rdesc,  						     &cfg); -	if (IS_ERR(int3472->regulator.rdev)) { -		ret = PTR_ERR(int3472->regulator.rdev); -		goto err_free_gpio; -	} -	return 0; - -err_free_gpio: -	gpiod_put(int3472->regulator.gpio); - -	return ret; +	return PTR_ERR_OR_ZERO(int3472->regulator.rdev);  }  void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472)  {  	regulator_unregister(int3472->regulator.rdev); -	gpiod_put(int3472->regulator.gpio);  } diff --git a/drivers/platform/x86/intel/int3472/common.h b/drivers/platform/x86/intel/int3472/common.h index 9f29baa13860..145dec66df64 100644 --- a/drivers/platform/x86/intel/int3472/common.h +++ b/drivers/platform/x86/intel/int3472/common.h @@ -117,16 +117,15 @@ int skl_int3472_get_sensor_adev_and_name(struct device *dev,  					 const char **name_ret);  int skl_int3472_register_gpio_clock(struct int3472_discrete_device *int3472, -				    struct acpi_resource_gpio *agpio, u32 polarity); +				    struct gpio_desc *gpio);  int skl_int3472_register_dsm_clock(struct int3472_discrete_device *int3472);  void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472);  int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, -				   struct acpi_resource_gpio *agpio); +				   struct gpio_desc *gpio);  void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472); -int skl_int3472_register_pled(struct int3472_discrete_device *int3472, -			      struct acpi_resource_gpio *agpio, u32 polarity); +int skl_int3472_register_pled(struct int3472_discrete_device *int3472, struct gpio_desc *gpio);  void skl_int3472_unregister_pled(struct int3472_discrete_device *int3472);  #endif diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index e33c2d75975c..07b302e09340 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -52,21 +52,15 @@ static void skl_int3472_log_sensor_module_name(struct int3472_discrete_device *i  	}  } -static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int3472, -					  struct acpi_resource_gpio *agpio, -					  const char *func, u32 polarity) +static int skl_int3472_fill_gpiod_lookup(struct gpiod_lookup *table_entry, +					 struct acpi_resource_gpio *agpio, +					 const char *func, u32 polarity)  {  	char *path = agpio->resource_source.string_ptr; -	struct gpiod_lookup *table_entry;  	struct acpi_device *adev;  	acpi_handle handle;  	acpi_status status; -	if (int3472->n_sensor_gpios >= INT3472_MAX_SENSOR_GPIOS) { -		dev_warn(int3472->dev, "Too many GPIOs mapped\n"); -		return -EINVAL; -	} -  	status = acpi_get_handle(NULL, path, &handle);  	if (ACPI_FAILURE(status))  		return -EINVAL; @@ -75,18 +69,62 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347  	if (!adev)  		return -ENODEV; -	table_entry = &int3472->gpios.table[int3472->n_sensor_gpios];  	table_entry->key = acpi_dev_name(adev);  	table_entry->chip_hwnum = agpio->pin_table[0];  	table_entry->con_id = func;  	table_entry->idx = 0;  	table_entry->flags = polarity; +	return 0; +} + +static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int3472, +					  struct acpi_resource_gpio *agpio, +					  const char *func, u32 polarity) +{ +	int ret; + +	if (int3472->n_sensor_gpios >= INT3472_MAX_SENSOR_GPIOS) { +		dev_warn(int3472->dev, "Too many GPIOs mapped\n"); +		return -EINVAL; +	} + +	ret = skl_int3472_fill_gpiod_lookup(&int3472->gpios.table[int3472->n_sensor_gpios], +					    agpio, func, polarity); +	if (ret) +		return ret; +  	int3472->n_sensor_gpios++;  	return 0;  } +/* This should *really* only be used when there's no other way... */ +static struct gpio_desc * +skl_int3472_gpiod_get_from_temp_lookup(struct int3472_discrete_device *int3472, +				       struct acpi_resource_gpio *agpio, +				       const char *func, u32 polarity) +{ +	struct gpio_desc *desc; +	int ret; + +	struct gpiod_lookup_table *lookup __free(kfree) = +			kzalloc(struct_size(lookup, table, 2), GFP_KERNEL); +	if (!lookup) +		return ERR_PTR(-ENOMEM); + +	lookup->dev_id = dev_name(int3472->dev); +	ret = skl_int3472_fill_gpiod_lookup(&lookup->table[0], agpio, func, polarity); +	if (ret) +		return ERR_PTR(ret); + +	gpiod_add_lookup_table(lookup); +	desc = devm_gpiod_get(int3472->dev, func, GPIOD_OUT_LOW); +	gpiod_remove_lookup_table(lookup); + +	return desc; +} +  static void int3472_get_func_and_polarity(u8 type, const char **func, u32 *polarity)  {  	switch (type) { @@ -156,6 +194,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,  	struct acpi_resource_gpio *agpio;  	u8 active_value, pin, type;  	union acpi_object *obj; +	struct gpio_desc *gpio;  	const char *err_msg;  	const char *func;  	u32 polarity; @@ -206,22 +245,38 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,  		break;  	case INT3472_GPIO_TYPE_CLK_ENABLE: -		ret = skl_int3472_register_gpio_clock(int3472, agpio, polarity); -		if (ret) -			err_msg = "Failed to register clock\n"; - -		break;  	case INT3472_GPIO_TYPE_PRIVACY_LED: -		ret = skl_int3472_register_pled(int3472, agpio, polarity); -		if (ret) -			err_msg = "Failed to register LED\n"; - -		break;  	case INT3472_GPIO_TYPE_POWER_ENABLE: -		ret = skl_int3472_register_regulator(int3472, agpio); -		if (ret) -			err_msg = "Failed to map regulator to sensor\n"; - +		gpio = skl_int3472_gpiod_get_from_temp_lookup(int3472, agpio, func, polarity); +		if (IS_ERR(gpio)) { +			ret = PTR_ERR(gpio); +			err_msg = "Failed to get GPIO\n"; +			break; +		} + +		switch (type) { +		case INT3472_GPIO_TYPE_CLK_ENABLE: +			ret = skl_int3472_register_gpio_clock(int3472, gpio); +			if (ret) +				err_msg = "Failed to register clock\n"; + +			break; +		case INT3472_GPIO_TYPE_PRIVACY_LED: +			ret = skl_int3472_register_pled(int3472, gpio); +			if (ret) +				err_msg = "Failed to register LED\n"; + +			break; +		case INT3472_GPIO_TYPE_POWER_ENABLE: +			ret = skl_int3472_register_regulator(int3472, gpio); +			if (ret) +				err_msg = "Failed to map regulator to sensor\n"; + +			break; +		default: /* Never reached */ +			ret = -EINVAL; +			break; +		}  		break;  	default:  		dev_warn(int3472->dev, diff --git a/drivers/platform/x86/intel/int3472/led.c b/drivers/platform/x86/intel/int3472/led.c index bca1ce7d0d0c..9cbed694e2ca 100644 --- a/drivers/platform/x86/intel/int3472/led.c +++ b/drivers/platform/x86/intel/int3472/led.c @@ -16,26 +16,15 @@ static int int3472_pled_set(struct led_classdev *led_cdev,  	return 0;  } -int skl_int3472_register_pled(struct int3472_discrete_device *int3472, -			      struct acpi_resource_gpio *agpio, u32 polarity) +int skl_int3472_register_pled(struct int3472_discrete_device *int3472, struct gpio_desc *gpio)  { -	char *p, *path = agpio->resource_source.string_ptr; +	char *p;  	int ret;  	if (int3472->pled.classdev.dev)  		return -EBUSY; -	int3472->pled.gpio = acpi_get_and_request_gpiod(path, agpio->pin_table[0], -							     "int3472,privacy-led"); -	if (IS_ERR(int3472->pled.gpio)) -		return dev_err_probe(int3472->dev, PTR_ERR(int3472->pled.gpio), -				     "getting privacy LED GPIO\n"); - -	if (polarity == GPIO_ACTIVE_LOW) -		gpiod_toggle_active_low(int3472->pled.gpio); - -	/* Ensure the pin is in output mode and non-active state */ -	gpiod_direction_output(int3472->pled.gpio, 0); +	int3472->pled.gpio = gpio;  	/* Generate the name, replacing the ':' in the ACPI devname with '_' */  	snprintf(int3472->pled.name, sizeof(int3472->pled.name), @@ -50,7 +39,7 @@ int skl_int3472_register_pled(struct int3472_discrete_device *int3472,  	ret = led_classdev_register(int3472->dev, &int3472->pled.classdev);  	if (ret) -		goto err_free_gpio; +		return ret;  	int3472->pled.lookup.provider = int3472->pled.name;  	int3472->pled.lookup.dev_id = int3472->sensor_name; @@ -58,10 +47,6 @@ int skl_int3472_register_pled(struct int3472_discrete_device *int3472,  	led_add_lookup(&int3472->pled.lookup);  	return 0; - -err_free_gpio: -	gpiod_put(int3472->pled.gpio); -	return ret;  }  void skl_int3472_unregister_pled(struct int3472_discrete_device *int3472) @@ -71,5 +56,4 @@ void skl_int3472_unregister_pled(struct int3472_discrete_device *int3472)  	led_remove_lookup(&int3472->pled.lookup);  	led_classdev_unregister(&int3472->pled.classdev); -	gpiod_put(int3472->pled.gpio);  } diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 5a36b3f77bc5..e95d3011b999 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -472,7 +472,7 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value)  	 * is based on the contiguous indexes from ltr_show output.  	 * pmc index and ltr index needs to be calculated from it.  	 */ -	for (pmc_index = 0; pmc_index < ARRAY_SIZE(pmcdev->pmcs) && ltr_index > 0; pmc_index++) { +	for (pmc_index = 0; pmc_index < ARRAY_SIZE(pmcdev->pmcs) && ltr_index >= 0; pmc_index++) {  		pmc = pmcdev->pmcs[pmc_index];  		if (!pmc) @@ -1123,7 +1123,7 @@ static const struct x86_cpu_id intel_pmc_core_ids[] = {  	X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_L,	icl_core_init),  	X86_MATCH_INTEL_FAM6_MODEL(ROCKETLAKE,		tgl_core_init),  	X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L,		tgl_core_init), -	X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_N,		tgl_core_init), +	X86_MATCH_INTEL_FAM6_MODEL(ATOM_GRACEMONT,	tgl_core_init),  	X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE,		adl_core_init),  	X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P,        tgl_core_init),  	X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE,		adl_core_init), diff --git a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c index a95004e3d80b..08df9494603c 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c @@ -720,7 +720,7 @@ static struct miscdevice isst_if_char_driver = {  static const struct x86_cpu_id hpm_cpu_ids[] = {  	X86_MATCH_INTEL_FAM6_MODEL(GRANITERAPIDS_X,	NULL), -	X86_MATCH_INTEL_FAM6_MODEL(SIERRAFOREST_X,	NULL), +	X86_MATCH_INTEL_FAM6_MODEL(ATOM_CRESTMONT_X,	NULL),  	{}  }; diff --git a/drivers/platform/x86/intel/speed_select_if/isst_if_mmio.c b/drivers/platform/x86/intel/speed_select_if/isst_if_mmio.c index ff49025ec085..3f4343147dad 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_if_mmio.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_if_mmio.c @@ -18,16 +18,17 @@  struct isst_mmio_range {  	int beg;  	int end; +	int size;  };  static struct isst_mmio_range mmio_range_devid_0[] = { -	{0x04, 0x14}, -	{0x20, 0xD0}, +	{0x04, 0x14, 0x18}, +	{0x20, 0xD0, 0xD4},  };  static struct isst_mmio_range mmio_range_devid_1[] = { -	{0x04, 0x14}, -	{0x20, 0x11C}, +	{0x04, 0x14, 0x18}, +	{0x20, 0x11C, 0x120},  };  struct isst_if_device { @@ -93,6 +94,7 @@ static int isst_if_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  	struct isst_if_device *punit_dev;  	struct isst_if_cmd_cb cb;  	u32 mmio_base, pcu_base; +	struct resource r;  	u64 base_addr;  	int ret; @@ -114,13 +116,16 @@ static int isst_if_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  	pcu_base &= GENMASK(10, 0);  	base_addr = (u64)mmio_base << 23 | (u64) pcu_base << 12; -	punit_dev->punit_mmio = devm_ioremap(&pdev->dev, base_addr, 256); -	if (!punit_dev->punit_mmio) -		return -ENOMEM; + +	punit_dev->mmio_range = (struct isst_mmio_range *) ent->driver_data; + +	r = DEFINE_RES_MEM(base_addr, punit_dev->mmio_range[1].size); +	punit_dev->punit_mmio = devm_ioremap_resource(&pdev->dev, &r); +	if (IS_ERR(punit_dev->punit_mmio)) +		return PTR_ERR(punit_dev->punit_mmio);  	mutex_init(&punit_dev->mutex);  	pci_set_drvdata(pdev, punit_dev); -	punit_dev->mmio_range = (struct isst_mmio_range *) ent->driver_data;  	memset(&cb, 0, sizeof(cb));  	cb.cmd_size = sizeof(struct isst_if_io_reg); diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c index 63faa2ea8327..0b6d2c864437 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -30,7 +30,8 @@  #include "isst_if_common.h"  /* Supported SST hardware version by this driver */ -#define ISST_HEADER_VERSION		1 +#define ISST_MAJOR_VERSION	0 +#define ISST_MINOR_VERSION	1  /*   * Used to indicate if value read from MMIO needs to get multiplied @@ -352,21 +353,25 @@ static int sst_main(struct auxiliary_device *auxdev, struct tpmi_per_power_domai  	pd_info->sst_header.cp_offset *= 8;  	pd_info->sst_header.pp_offset *= 8; -	if (pd_info->sst_header.interface_version != ISST_HEADER_VERSION) { -		dev_err(&auxdev->dev, "SST: Unsupported version:%x\n", -			pd_info->sst_header.interface_version); +	if (pd_info->sst_header.interface_version == TPMI_VERSION_INVALID) +		return -ENODEV; + +	if (TPMI_MAJOR_VERSION(pd_info->sst_header.interface_version) != ISST_MAJOR_VERSION) { +		dev_err(&auxdev->dev, "SST: Unsupported major version:%lx\n", +			TPMI_MAJOR_VERSION(pd_info->sst_header.interface_version));  		return -ENODEV;  	} +	if (TPMI_MINOR_VERSION(pd_info->sst_header.interface_version) != ISST_MINOR_VERSION) +		dev_info(&auxdev->dev, "SST: Ignore: Unsupported minor version:%lx\n", +			 TPMI_MINOR_VERSION(pd_info->sst_header.interface_version)); +  	/* Read SST CP Header */  	*((u64 *)&pd_info->cp_header) = readq(pd_info->sst_base + pd_info->sst_header.cp_offset);  	/* Read PP header */  	*((u64 *)&pd_info->pp_header) = readq(pd_info->sst_base + pd_info->sst_header.pp_offset); -	/* Force level_en_mask level 0 */ -	pd_info->pp_header.level_en_mask |= 0x01; -  	mask = 0x01;  	levels = 0;  	for (i = 0; i < 8; ++i) { @@ -704,7 +709,7 @@ static int isst_if_get_perf_level(void __user *argp)  		return -EINVAL;  	perf_level.max_level = power_domain_info->max_level; -	perf_level.level_mask = power_domain_info->pp_header.allowed_level_mask; +	perf_level.level_mask = power_domain_info->pp_header.level_en_mask;  	perf_level.feature_rev = power_domain_info->pp_header.feature_rev;  	_read_pp_info("current_level", perf_level.current_level, SST_PP_STATUS_OFFSET,  		      SST_PP_LEVEL_START, SST_PP_LEVEL_WIDTH, SST_MUL_FACTOR_NONE) diff --git a/drivers/platform/x86/intel/telemetry/core.c b/drivers/platform/x86/intel/telemetry/core.c index fdf55b5d6948..e4be40f73eeb 100644 --- a/drivers/platform/x86/intel/telemetry/core.c +++ b/drivers/platform/x86/intel/telemetry/core.c @@ -102,7 +102,7 @@ static const struct telemetry_core_ops telm_defpltops = {  /**   * telemetry_update_events() - Update telemetry Configuration   * @pss_evtconfig: PSS related config. No change if num_evts = 0. - * @pss_evtconfig: IOSS related config. No change if num_evts = 0. + * @ioss_evtconfig: IOSS related config. No change if num_evts = 0.   *   * This API updates the IOSS & PSS Telemetry configuration. Old config   * is overwritten. Call telemetry_reset_events when logging is over @@ -176,7 +176,7 @@ EXPORT_SYMBOL_GPL(telemetry_reset_events);  /**   * telemetry_get_eventconfig() - Returns the pss and ioss events enabled   * @pss_evtconfig: Pointer to PSS related configuration. - * @pss_evtconfig: Pointer to IOSS related configuration. + * @ioss_evtconfig: Pointer to IOSS related configuration.   * @pss_len:	   Number of u32 elements allocated for pss_evtconfig array   * @ioss_len:	   Number of u32 elements allocated for ioss_evtconfig array   * diff --git a/drivers/platform/x86/intel/tpmi.c b/drivers/platform/x86/intel/tpmi.c index d1fd6e69401c..311abcac894a 100644 --- a/drivers/platform/x86/intel/tpmi.c +++ b/drivers/platform/x86/intel/tpmi.c @@ -47,10 +47,17 @@   */  #include <linux/auxiliary_bus.h> +#include <linux/bitfield.h> +#include <linux/debugfs.h> +#include <linux/delay.h>  #include <linux/intel_tpmi.h>  #include <linux/io.h> +#include <linux/iopoll.h>  #include <linux/module.h>  #include <linux/pci.h> +#include <linux/security.h> +#include <linux/sizes.h> +#include <linux/string_helpers.h>  #include "vsec.h" @@ -83,12 +90,14 @@ struct intel_tpmi_pfs_entry {   * @vsec_offset: Starting MMIO address for this feature in bytes. Essentially   *		 this offset = "Address" from VSEC header + PFS Capability   *		 offset for this feature entry. + * @vsec_dev:	Pointer to intel_vsec_device structure for this TPMI device   *   * Represents TPMI instance information for one TPMI ID.   */  struct intel_tpmi_pm_feature {  	struct intel_tpmi_pfs_entry pfs_header;  	unsigned int vsec_offset; +	struct intel_vsec_device *vsec_dev;  };  /** @@ -98,6 +107,8 @@ struct intel_tpmi_pm_feature {   * @feature_count:	Number of TPMI of TPMI instances pointed by tpmi_features   * @pfs_start:		Start of PFS offset for the TPMI instances in this device   * @plat_info:		Stores platform info which can be used by the client drivers + * @tpmi_control_mem:	Memory mapped IO for getting control information + * @dbgfs_dir:		debugfs entry pointer   *   * Stores the information for all TPMI devices enumerated from a single PCI device.   */ @@ -107,6 +118,8 @@ struct intel_tpmi_info {  	int feature_count;  	u64 pfs_start;  	struct intel_tpmi_plat_info plat_info; +	void __iomem *tpmi_control_mem; +	struct dentry *dbgfs_dir;  };  /** @@ -130,6 +143,33 @@ struct tpmi_info_header {  	u64 lock:1;  } __packed; +/** + * struct tpmi_feature_state - Structure to read hardware state of a feature + * @enabled:	Enable state of a feature, 1: enabled, 0: disabled + * @reserved_1:	Reserved for future use + * @write_blocked: Writes are blocked means all write operations are ignored + * @read_blocked: Reads are blocked means will read 0xFFs + * @pcs_select:	Interface used by out of band software, not used in OS + * @reserved_2:	Reserved for future use + * @id:		TPMI ID of the feature + * @reserved_3:	Reserved for future use + * @locked:	When set to 1, OS can't change this register. + * + * The structure is used to read hardware state of a TPMI feature. This + * information is used for debug and restricting operations for this feature. + */ +struct tpmi_feature_state { +	u32 enabled:1; +	u32 reserved_1:3; +	u32 write_blocked:1; +	u32 read_blocked:1; +	u32 pcs_select:1; +	u32 reserved_2:1; +	u32 id:8; +	u32 reserved_3:15; +	u32 locked:1; +} __packed; +  /*   * List of supported TMPI IDs.   * Some TMPI IDs are not used by Linux, so the numbers are not consecutive. @@ -139,9 +179,19 @@ enum intel_tpmi_id {  	TPMI_ID_PEM = 1, /* Power and Perf excursion Monitor */  	TPMI_ID_UNCORE = 2, /* Uncore Frequency Scaling */  	TPMI_ID_SST = 5, /* Speed Select Technology */ +	TPMI_CONTROL_ID = 0x80, /* Special ID for getting feature status */  	TPMI_INFO_ID = 0x81, /* Special ID for PCI BDF and Package ID information */  }; +/* + * The size from hardware is in u32 units. This size is from a trusted hardware, + * but better to verify for pre silicon platforms. Set size to 0, when invalid. + */ +#define TPMI_GET_SINGLE_ENTRY_SIZE(pfs)							\ +({											\ +	pfs->pfs_header.entry_size > SZ_1K ? 0 : pfs->pfs_header.entry_size << 2;	\ +}) +  /* Used during auxbus device creation */  static DEFINE_IDA(intel_vsec_tpmi_ida); @@ -175,6 +225,353 @@ struct resource *tpmi_get_resource_at_index(struct auxiliary_device *auxdev, int  }  EXPORT_SYMBOL_NS_GPL(tpmi_get_resource_at_index, INTEL_TPMI); +/* TPMI Control Interface */ + +#define TPMI_CONTROL_STATUS_OFFSET	0x00 +#define TPMI_COMMAND_OFFSET		0x08 +#define TMPI_CONTROL_DATA_VAL_OFFSET	0x0c + +/* + * Spec is calling for max 1 seconds to get ownership at the worst + * case. Read at 10 ms timeouts and repeat up to 1 second. + */ +#define TPMI_CONTROL_TIMEOUT_US		(10 * USEC_PER_MSEC) +#define TPMI_CONTROL_TIMEOUT_MAX_US	(1 * USEC_PER_SEC) + +#define TPMI_RB_TIMEOUT_US		(10 * USEC_PER_MSEC) +#define TPMI_RB_TIMEOUT_MAX_US		USEC_PER_SEC + +/* TPMI Control status register defines */ + +#define TPMI_CONTROL_STATUS_RB		BIT_ULL(0) + +#define TPMI_CONTROL_STATUS_OWNER	GENMASK_ULL(5, 4) +#define TPMI_OWNER_NONE			0 +#define TPMI_OWNER_IN_BAND		1 + +#define TPMI_CONTROL_STATUS_CPL		BIT_ULL(6) +#define TPMI_CONTROL_STATUS_RESULT	GENMASK_ULL(15, 8) +#define TPMI_CONTROL_STATUS_LEN		GENMASK_ULL(31, 16) + +#define TPMI_CMD_PKT_LEN		2 +#define TPMI_CMD_STATUS_SUCCESS		0x40 + +/* TPMI command data registers */ +#define TMPI_CONTROL_DATA_CMD		GENMASK_ULL(7, 0) +#define TPMI_CONTROL_DATA_VAL_FEATURE	GENMASK_ULL(48, 40) + +/* Command to send via control interface */ +#define TPMI_CONTROL_GET_STATE_CMD	0x10 + +#define TPMI_CONTROL_CMD_MASK		GENMASK_ULL(48, 40) + +#define TPMI_CMD_LEN_MASK		GENMASK_ULL(18, 16) + +/* Mutex to complete get feature status without interruption */ +static DEFINE_MUTEX(tpmi_dev_lock); + +static int tpmi_wait_for_owner(struct intel_tpmi_info *tpmi_info, u8 owner) +{ +	u64 control; + +	return readq_poll_timeout(tpmi_info->tpmi_control_mem + TPMI_CONTROL_STATUS_OFFSET, +				  control, owner == FIELD_GET(TPMI_CONTROL_STATUS_OWNER, control), +				  TPMI_CONTROL_TIMEOUT_US, TPMI_CONTROL_TIMEOUT_MAX_US); +} + +static int tpmi_read_feature_status(struct intel_tpmi_info *tpmi_info, int feature_id, +				    struct tpmi_feature_state *feature_state) +{ +	u64 control, data; +	int ret; + +	if (!tpmi_info->tpmi_control_mem) +		return -EFAULT; + +	mutex_lock(&tpmi_dev_lock); + +	/* Wait for owner bit set to 0 (none) */ +	ret = tpmi_wait_for_owner(tpmi_info, TPMI_OWNER_NONE); +	if (ret) +		goto err_unlock; + +	/* set command id to 0x10 for TPMI_GET_STATE */ +	data = FIELD_PREP(TMPI_CONTROL_DATA_CMD, TPMI_CONTROL_GET_STATE_CMD); + +	/* 32 bits for DATA offset and +8 for feature_id field */ +	data |= FIELD_PREP(TPMI_CONTROL_DATA_VAL_FEATURE, feature_id); + +	/* Write at command offset for qword access */ +	writeq(data, tpmi_info->tpmi_control_mem + TPMI_COMMAND_OFFSET); + +	/* Wait for owner bit set to in-band */ +	ret = tpmi_wait_for_owner(tpmi_info, TPMI_OWNER_IN_BAND); +	if (ret) +		goto err_unlock; + +	/* Set Run Busy and packet length of 2 dwords */ +	control = TPMI_CONTROL_STATUS_RB; +	control |= FIELD_PREP(TPMI_CONTROL_STATUS_LEN, TPMI_CMD_PKT_LEN); + +	/* Write at status offset for qword access */ +	writeq(control, tpmi_info->tpmi_control_mem + TPMI_CONTROL_STATUS_OFFSET); + +	/* Wait for Run Busy clear */ +	ret = readq_poll_timeout(tpmi_info->tpmi_control_mem + TPMI_CONTROL_STATUS_OFFSET, +				 control, !(control & TPMI_CONTROL_STATUS_RB), +				 TPMI_RB_TIMEOUT_US, TPMI_RB_TIMEOUT_MAX_US); +	if (ret) +		goto done_proc; + +	control = FIELD_GET(TPMI_CONTROL_STATUS_RESULT, control); +	if (control != TPMI_CMD_STATUS_SUCCESS) { +		ret = -EBUSY; +		goto done_proc; +	} + +	/* Response is ready */ +	memcpy_fromio(feature_state, tpmi_info->tpmi_control_mem + TMPI_CONTROL_DATA_VAL_OFFSET, +		      sizeof(*feature_state)); + +	ret = 0; + +done_proc: +	/* Set CPL "completion" bit */ +	writeq(TPMI_CONTROL_STATUS_CPL, tpmi_info->tpmi_control_mem + TPMI_CONTROL_STATUS_OFFSET); + +err_unlock: +	mutex_unlock(&tpmi_dev_lock); + +	return ret; +} + +int tpmi_get_feature_status(struct auxiliary_device *auxdev, int feature_id, +			    int *locked, int *disabled) +{ +	struct intel_vsec_device *intel_vsec_dev = dev_to_ivdev(auxdev->dev.parent); +	struct intel_tpmi_info *tpmi_info = auxiliary_get_drvdata(&intel_vsec_dev->auxdev); +	struct tpmi_feature_state feature_state; +	int ret; + +	ret = tpmi_read_feature_status(tpmi_info, feature_id, &feature_state); +	if (ret) +		return ret; + +	*locked = feature_state.locked; +	*disabled = !feature_state.enabled; + +	return 0; +} +EXPORT_SYMBOL_NS_GPL(tpmi_get_feature_status, INTEL_TPMI); + +static int tpmi_pfs_dbg_show(struct seq_file *s, void *unused) +{ +	struct intel_tpmi_info *tpmi_info = s->private; +	int locked, disabled, read_blocked, write_blocked; +	struct tpmi_feature_state feature_state; +	struct intel_tpmi_pm_feature *pfs; +	int ret, i; + + +	seq_printf(s, "tpmi PFS start offset 0x:%llx\n", tpmi_info->pfs_start); +	seq_puts(s, "tpmi_id\t\tentries\t\tsize\t\tcap_offset\tattribute\tvsec_offset\tlocked\tdisabled\tread_blocked\twrite_blocked\n"); +	for (i = 0; i < tpmi_info->feature_count; ++i) { +		pfs = &tpmi_info->tpmi_features[i]; +		ret = tpmi_read_feature_status(tpmi_info, pfs->pfs_header.tpmi_id, &feature_state); +		if (ret) { +			locked = 'U'; +			disabled = 'U'; +			read_blocked = 'U'; +			write_blocked = 'U'; +		} else { +			disabled = feature_state.enabled ? 'N' : 'Y'; +			locked = feature_state.locked ? 'Y' : 'N'; +			read_blocked = feature_state.read_blocked ? 'Y' : 'N'; +			write_blocked = feature_state.write_blocked ? 'Y' : 'N'; +		} +		seq_printf(s, "0x%02x\t\t0x%02x\t\t0x%04x\t\t0x%04x\t\t0x%02x\t\t0x%08x\t%c\t%c\t\t%c\t\t%c\n", +			   pfs->pfs_header.tpmi_id, pfs->pfs_header.num_entries, +			   pfs->pfs_header.entry_size, pfs->pfs_header.cap_offset, +			   pfs->pfs_header.attribute, pfs->vsec_offset, locked, disabled, +			   read_blocked, write_blocked); +	} + +	return 0; +} +DEFINE_SHOW_ATTRIBUTE(tpmi_pfs_dbg); + +#define MEM_DUMP_COLUMN_COUNT	8 + +static int tpmi_mem_dump_show(struct seq_file *s, void *unused) +{ +	size_t row_size = MEM_DUMP_COLUMN_COUNT * sizeof(u32); +	struct intel_tpmi_pm_feature *pfs = s->private; +	int count, ret = 0; +	void __iomem *mem; +	u32 off, size; +	u8 *buffer; + +	size = TPMI_GET_SINGLE_ENTRY_SIZE(pfs); +	if (!size) +		return -EIO; + +	buffer = kmalloc(size, GFP_KERNEL); +	if (!buffer) +		return -ENOMEM; + +	off = pfs->vsec_offset; + +	mutex_lock(&tpmi_dev_lock); + +	for (count = 0; count < pfs->pfs_header.num_entries; ++count) { +		seq_printf(s, "TPMI Instance:%d offset:0x%x\n", count, off); + +		mem = ioremap(off, size); +		if (!mem) { +			ret = -ENOMEM; +			break; +		} + +		memcpy_fromio(buffer, mem, size); + +		seq_hex_dump(s, " ", DUMP_PREFIX_OFFSET, row_size, sizeof(u32), buffer, size, +			     false); + +		iounmap(mem); + +		off += size; +	} + +	mutex_unlock(&tpmi_dev_lock); + +	kfree(buffer); + +	return ret; +} +DEFINE_SHOW_ATTRIBUTE(tpmi_mem_dump); + +static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t len, loff_t *ppos) +{ +	struct seq_file *m = file->private_data; +	struct intel_tpmi_pm_feature *pfs = m->private; +	u32 addr, value, punit, size; +	u32 num_elems, *array; +	void __iomem *mem; +	int ret; + +	size = TPMI_GET_SINGLE_ENTRY_SIZE(pfs); +	if (!size) +		return -EIO; + +	ret = parse_int_array_user(userbuf, len, (int **)&array); +	if (ret < 0) +		return ret; + +	num_elems = *array; +	if (num_elems != 3) { +		ret = -EINVAL; +		goto exit_write; +	} + +	punit = array[1]; +	addr = array[2]; +	value = array[3]; + +	if (punit >= pfs->pfs_header.num_entries) { +		ret = -EINVAL; +		goto exit_write; +	} + +	if (addr >= size) { +		ret = -EINVAL; +		goto exit_write; +	} + +	mutex_lock(&tpmi_dev_lock); + +	mem = ioremap(pfs->vsec_offset + punit * size, size); +	if (!mem) { +		ret = -ENOMEM; +		goto unlock_mem_write; +	} + +	writel(value, mem + addr); + +	iounmap(mem); + +	ret = len; + +unlock_mem_write: +	mutex_unlock(&tpmi_dev_lock); + +exit_write: +	kfree(array); + +	return ret; +} + +static int mem_write_show(struct seq_file *s, void *unused) +{ +	return 0; +} + +static int mem_write_open(struct inode *inode, struct file *file) +{ +	return single_open(file, mem_write_show, inode->i_private); +} + +static const struct file_operations mem_write_ops = { +	.open           = mem_write_open, +	.read           = seq_read, +	.write          = mem_write, +	.llseek         = seq_lseek, +	.release        = single_release, +}; + +#define tpmi_to_dev(info)	(&info->vsec_dev->pcidev->dev) + +static void tpmi_dbgfs_register(struct intel_tpmi_info *tpmi_info) +{ +	char name[64]; +	int i; + +	snprintf(name, sizeof(name), "tpmi-%s", dev_name(tpmi_to_dev(tpmi_info))); +	tpmi_info->dbgfs_dir = debugfs_create_dir(name, NULL); + +	debugfs_create_file("pfs_dump", 0444, tpmi_info->dbgfs_dir, tpmi_info, &tpmi_pfs_dbg_fops); + +	for (i = 0; i < tpmi_info->feature_count; ++i) { +		struct intel_tpmi_pm_feature *pfs; +		struct dentry *dir; + +		pfs = &tpmi_info->tpmi_features[i]; +		snprintf(name, sizeof(name), "tpmi-id-%02x", pfs->pfs_header.tpmi_id); +		dir = debugfs_create_dir(name, tpmi_info->dbgfs_dir); + +		debugfs_create_file("mem_dump", 0444, dir, pfs, &tpmi_mem_dump_fops); +		debugfs_create_file("mem_write", 0644, dir, pfs, &mem_write_ops); +	} +} + +static void tpmi_set_control_base(struct auxiliary_device *auxdev, +				  struct intel_tpmi_info *tpmi_info, +				  struct intel_tpmi_pm_feature *pfs) +{ +	void __iomem *mem; +	u32 size; + +	size = TPMI_GET_SINGLE_ENTRY_SIZE(pfs); +	if (!size) +		return; + +	mem = devm_ioremap(&auxdev->dev, pfs->vsec_offset, size); +	if (!mem) +		return; + +	/* mem is pointing to TPMI CONTROL base */ +	tpmi_info->tpmi_control_mem = mem; +} +  static const char *intel_tpmi_name(enum intel_tpmi_id id)  {  	switch (id) { @@ -316,7 +713,7 @@ static int intel_vsec_tpmi_init(struct auxiliary_device *auxdev)  	struct pci_dev *pci_dev = vsec_dev->pcidev;  	struct intel_tpmi_info *tpmi_info;  	u64 pfs_start = 0; -	int i; +	int ret, i;  	tpmi_info = devm_kzalloc(&auxdev->dev, sizeof(*tpmi_info), GFP_KERNEL);  	if (!tpmi_info) @@ -339,6 +736,7 @@ static int intel_vsec_tpmi_init(struct auxiliary_device *auxdev)  		int size, ret;  		pfs = &tpmi_info->tpmi_features[i]; +		pfs->vsec_dev = vsec_dev;  		res = &vsec_dev->resource[i];  		if (!res) @@ -367,13 +765,29 @@ static int intel_vsec_tpmi_init(struct auxiliary_device *auxdev)  		 */  		if (pfs->pfs_header.tpmi_id == TPMI_INFO_ID)  			tpmi_process_info(tpmi_info, pfs); + +		if (pfs->pfs_header.tpmi_id == TPMI_CONTROL_ID) +			tpmi_set_control_base(auxdev, tpmi_info, pfs);  	}  	tpmi_info->pfs_start = pfs_start;  	auxiliary_set_drvdata(auxdev, tpmi_info); -	return tpmi_create_devices(tpmi_info); +	ret = tpmi_create_devices(tpmi_info); +	if (ret) +		return ret; + +	/* +	 * Allow debugfs when security policy allows. Everything this debugfs +	 * interface provides, can also be done via /dev/mem access. If +	 * /dev/mem interface is locked, don't allow debugfs to present any +	 * information. Also check for CAP_SYS_RAWIO as /dev/mem interface. +	 */ +	if (!security_locked_down(LOCKDOWN_DEV_MEM) && capable(CAP_SYS_RAWIO)) +		tpmi_dbgfs_register(tpmi_info); + +	return 0;  }  static int tpmi_probe(struct auxiliary_device *auxdev, @@ -382,11 +796,12 @@ static int tpmi_probe(struct auxiliary_device *auxdev,  	return intel_vsec_tpmi_init(auxdev);  } -/* - * Remove callback is not needed currently as there is no - * cleanup required. All memory allocs are device managed. All - * devices created by this modules are also device managed. - */ +static void tpmi_remove(struct auxiliary_device *auxdev) +{ +	struct intel_tpmi_info *tpmi_info = auxiliary_get_drvdata(auxdev); + +	debugfs_remove_recursive(tpmi_info->dbgfs_dir); +}  static const struct auxiliary_device_id tpmi_id_table[] = {  	{ .name = "intel_vsec.tpmi" }, @@ -397,6 +812,7 @@ MODULE_DEVICE_TABLE(auxiliary, tpmi_id_table);  static struct auxiliary_driver tpmi_aux_driver = {  	.id_table	= tpmi_id_table,  	.probe		= tpmi_probe, +	.remove         = tpmi_remove,  };  module_auxiliary_driver(tpmi_aux_driver); diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c index 1152deaa0078..33ab207493e3 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c @@ -176,7 +176,7 @@ show_uncore_data(initial_max_freq_khz);  static int create_attr_group(struct uncore_data *data, char *name)  { -	int ret, index = 0; +	int ret, freq, index = 0;  	init_attribute_rw(max_freq_khz);  	init_attribute_rw(min_freq_khz); @@ -197,7 +197,11 @@ static int create_attr_group(struct uncore_data *data, char *name)  	data->uncore_attrs[index++] = &data->min_freq_khz_dev_attr.attr;  	data->uncore_attrs[index++] = &data->initial_min_freq_khz_dev_attr.attr;  	data->uncore_attrs[index++] = &data->initial_max_freq_khz_dev_attr.attr; -	data->uncore_attrs[index++] = &data->current_freq_khz_dev_attr.attr; + +	ret = uncore_read_freq(data, &freq); +	if (!ret) +		data->uncore_attrs[index++] = &data->current_freq_khz_dev_attr.attr; +  	data->uncore_attrs[index] = NULL;  	data->uncore_attr_group.name = name; diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c index 7d0a67f8b517..4fb790552c47 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c @@ -28,7 +28,8 @@  #include "uncore-frequency-common.h" -#define	UNCORE_HEADER_VERSION		1 +#define	UNCORE_MAJOR_VERSION		0 +#define	UNCORE_MINOR_VERSION		1  #define UNCORE_HEADER_INDEX		0  #define UNCORE_FABRIC_CLUSTER_OFFSET	8 @@ -302,12 +303,21 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_  		/* Check for version and skip this resource if there is mismatch */  		header = readq(pd_info->uncore_base);  		pd_info->ufs_header_ver = header & UNCORE_VERSION_MASK; -		if (pd_info->ufs_header_ver != UNCORE_HEADER_VERSION) { -			dev_info(&auxdev->dev, "Uncore: Unsupported version:%d\n", -				pd_info->ufs_header_ver); + +		if (pd_info->ufs_header_ver == TPMI_VERSION_INVALID)  			continue; + +		if (TPMI_MAJOR_VERSION(pd_info->ufs_header_ver) != UNCORE_MAJOR_VERSION) { +			dev_err(&auxdev->dev, "Uncore: Unsupported major version:%lx\n", +				TPMI_MAJOR_VERSION(pd_info->ufs_header_ver)); +			ret = -ENODEV; +			goto remove_clusters;  		} +		if (TPMI_MINOR_VERSION(pd_info->ufs_header_ver) != UNCORE_MINOR_VERSION) +			dev_info(&auxdev->dev, "Uncore: Ignore: Unsupported minor version:%lx\n", +				 TPMI_MINOR_VERSION(pd_info->ufs_header_ver)); +  		/* Get Cluster ID Mask */  		cluster_mask = FIELD_GET(UNCORE_LOCAL_FABRIC_CLUSTER_ID_MASK, header);  		if (!cluster_mask) { diff --git a/drivers/platform/x86/intel/vbtn.c b/drivers/platform/x86/intel/vbtn.c index 6fa1735ad7a4..210b0a81b7ec 100644 --- a/drivers/platform/x86/intel/vbtn.c +++ b/drivers/platform/x86/intel/vbtn.c @@ -73,10 +73,10 @@ struct intel_vbtn_priv {  	bool wakeup_mode;  }; -static void detect_tablet_mode(struct platform_device *device) +static void detect_tablet_mode(struct device *dev)  { -	struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev); -	acpi_handle handle = ACPI_HANDLE(&device->dev); +	struct intel_vbtn_priv *priv = dev_get_drvdata(dev); +	acpi_handle handle = ACPI_HANDLE(dev);  	unsigned long long vgbs;  	acpi_status status;  	int m; @@ -89,6 +89,8 @@ static void detect_tablet_mode(struct platform_device *device)  	input_report_switch(priv->switches_dev, SW_TABLET_MODE, m);  	m = (vgbs & VGBS_DOCK_MODE_FLAG) ? 1 : 0;  	input_report_switch(priv->switches_dev, SW_DOCK, m); + +	input_sync(priv->switches_dev);  }  /* @@ -134,7 +136,7 @@ static int intel_vbtn_input_setup(struct platform_device *device)  	priv->switches_dev->id.bustype = BUS_HOST;  	if (priv->has_switches) { -		detect_tablet_mode(device); +		detect_tablet_mode(&device->dev);  		ret = input_register_device(priv->switches_dev);  		if (ret) @@ -198,6 +200,9 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)  	autorelease = val && (!ke_rel || ke_rel->type == KE_IGNORE);  	sparse_keymap_report_event(input_dev, event, val, autorelease); + +	/* Some devices need this to report further events */ +	acpi_evaluate_object(handle, "VBDL", NULL, NULL);  }  /* @@ -352,7 +357,13 @@ static void intel_vbtn_pm_complete(struct device *dev)  static int intel_vbtn_pm_resume(struct device *dev)  { +	struct intel_vbtn_priv *priv = dev_get_drvdata(dev); +  	intel_vbtn_pm_complete(dev); + +	if (priv->has_switches) +		detect_tablet_mode(dev); +  	return 0;  } |