162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * AMD HSMP Platform Driver
462306a36Sopenharmony_ci * Copyright (c) 2022, AMD.
562306a36Sopenharmony_ci * All Rights Reserved.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * This file provides a device implementation for HSMP interface
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <asm/amd_hsmp.h>
1362306a36Sopenharmony_ci#include <asm/amd_nb.h>
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/io.h>
1662306a36Sopenharmony_ci#include <linux/miscdevice.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/pci.h>
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci#include <linux/semaphore.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define DRIVER_NAME		"amd_hsmp"
2362306a36Sopenharmony_ci#define DRIVER_VERSION		"1.0"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/* HSMP Status / Error codes */
2662306a36Sopenharmony_ci#define HSMP_STATUS_NOT_READY	0x00
2762306a36Sopenharmony_ci#define HSMP_STATUS_OK		0x01
2862306a36Sopenharmony_ci#define HSMP_ERR_INVALID_MSG	0xFE
2962306a36Sopenharmony_ci#define HSMP_ERR_INVALID_INPUT	0xFF
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/* Timeout in millsec */
3262306a36Sopenharmony_ci#define HSMP_MSG_TIMEOUT	100
3362306a36Sopenharmony_ci#define HSMP_SHORT_SLEEP	1
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define HSMP_WR			true
3662306a36Sopenharmony_ci#define HSMP_RD			false
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/*
3962306a36Sopenharmony_ci * To access specific HSMP mailbox register, s/w writes the SMN address of HSMP mailbox
4062306a36Sopenharmony_ci * register into the SMN_INDEX register, and reads/writes the SMN_DATA reg.
4162306a36Sopenharmony_ci * Below are required SMN address for HSMP Mailbox register offsets in SMU address space
4262306a36Sopenharmony_ci */
4362306a36Sopenharmony_ci#define SMN_HSMP_MSG_ID		0x3B10534
4462306a36Sopenharmony_ci#define SMN_HSMP_MSG_RESP	0x3B10980
4562306a36Sopenharmony_ci#define SMN_HSMP_MSG_DATA	0x3B109E0
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define HSMP_INDEX_REG		0xc4
4862306a36Sopenharmony_ci#define HSMP_DATA_REG		0xc8
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic struct semaphore *hsmp_sem;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic struct miscdevice hsmp_device;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic int amd_hsmp_rdwr(struct pci_dev *root, u32 address,
5562306a36Sopenharmony_ci			 u32 *value, bool write)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	int ret;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	ret = pci_write_config_dword(root, HSMP_INDEX_REG, address);
6062306a36Sopenharmony_ci	if (ret)
6162306a36Sopenharmony_ci		return ret;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	ret = (write ? pci_write_config_dword(root, HSMP_DATA_REG, *value)
6462306a36Sopenharmony_ci		     : pci_read_config_dword(root, HSMP_DATA_REG, value));
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return ret;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/*
7062306a36Sopenharmony_ci * Send a message to the HSMP port via PCI-e config space registers.
7162306a36Sopenharmony_ci *
7262306a36Sopenharmony_ci * The caller is expected to zero out any unused arguments.
7362306a36Sopenharmony_ci * If a response is expected, the number of response words should be greater than 0.
7462306a36Sopenharmony_ci *
7562306a36Sopenharmony_ci * Returns 0 for success and populates the requested number of arguments.
7662306a36Sopenharmony_ci * Returns a negative error code for failure.
7762306a36Sopenharmony_ci */
7862306a36Sopenharmony_cistatic int __hsmp_send_message(struct pci_dev *root, struct hsmp_message *msg)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	unsigned long timeout, short_sleep;
8162306a36Sopenharmony_ci	u32 mbox_status;
8262306a36Sopenharmony_ci	u32 index;
8362306a36Sopenharmony_ci	int ret;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	/* Clear the status register */
8662306a36Sopenharmony_ci	mbox_status = HSMP_STATUS_NOT_READY;
8762306a36Sopenharmony_ci	ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_RESP, &mbox_status, HSMP_WR);
8862306a36Sopenharmony_ci	if (ret) {
8962306a36Sopenharmony_ci		pr_err("Error %d clearing mailbox status register\n", ret);
9062306a36Sopenharmony_ci		return ret;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	index = 0;
9462306a36Sopenharmony_ci	/* Write any message arguments */
9562306a36Sopenharmony_ci	while (index < msg->num_args) {
9662306a36Sopenharmony_ci		ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_DATA + (index << 2),
9762306a36Sopenharmony_ci				    &msg->args[index], HSMP_WR);
9862306a36Sopenharmony_ci		if (ret) {
9962306a36Sopenharmony_ci			pr_err("Error %d writing message argument %d\n", ret, index);
10062306a36Sopenharmony_ci			return ret;
10162306a36Sopenharmony_ci		}
10262306a36Sopenharmony_ci		index++;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	/* Write the message ID which starts the operation */
10662306a36Sopenharmony_ci	ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_ID, &msg->msg_id, HSMP_WR);
10762306a36Sopenharmony_ci	if (ret) {
10862306a36Sopenharmony_ci		pr_err("Error %d writing message ID %u\n", ret, msg->msg_id);
10962306a36Sopenharmony_ci		return ret;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	/*
11362306a36Sopenharmony_ci	 * Depending on when the trigger write completes relative to the SMU
11462306a36Sopenharmony_ci	 * firmware 1 ms cycle, the operation may take from tens of us to 1 ms
11562306a36Sopenharmony_ci	 * to complete. Some operations may take more. Therefore we will try
11662306a36Sopenharmony_ci	 * a few short duration sleeps and switch to long sleeps if we don't
11762306a36Sopenharmony_ci	 * succeed quickly.
11862306a36Sopenharmony_ci	 */
11962306a36Sopenharmony_ci	short_sleep = jiffies + msecs_to_jiffies(HSMP_SHORT_SLEEP);
12062306a36Sopenharmony_ci	timeout	= jiffies + msecs_to_jiffies(HSMP_MSG_TIMEOUT);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	while (time_before(jiffies, timeout)) {
12362306a36Sopenharmony_ci		ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_RESP, &mbox_status, HSMP_RD);
12462306a36Sopenharmony_ci		if (ret) {
12562306a36Sopenharmony_ci			pr_err("Error %d reading mailbox status\n", ret);
12662306a36Sopenharmony_ci			return ret;
12762306a36Sopenharmony_ci		}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci		if (mbox_status != HSMP_STATUS_NOT_READY)
13062306a36Sopenharmony_ci			break;
13162306a36Sopenharmony_ci		if (time_before(jiffies, short_sleep))
13262306a36Sopenharmony_ci			usleep_range(50, 100);
13362306a36Sopenharmony_ci		else
13462306a36Sopenharmony_ci			usleep_range(1000, 2000);
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (unlikely(mbox_status == HSMP_STATUS_NOT_READY)) {
13862306a36Sopenharmony_ci		return -ETIMEDOUT;
13962306a36Sopenharmony_ci	} else if (unlikely(mbox_status == HSMP_ERR_INVALID_MSG)) {
14062306a36Sopenharmony_ci		return -ENOMSG;
14162306a36Sopenharmony_ci	} else if (unlikely(mbox_status == HSMP_ERR_INVALID_INPUT)) {
14262306a36Sopenharmony_ci		return -EINVAL;
14362306a36Sopenharmony_ci	} else if (unlikely(mbox_status != HSMP_STATUS_OK)) {
14462306a36Sopenharmony_ci		pr_err("Message ID %u unknown failure (status = 0x%X)\n",
14562306a36Sopenharmony_ci		       msg->msg_id, mbox_status);
14662306a36Sopenharmony_ci		return -EIO;
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/*
15062306a36Sopenharmony_ci	 * SMU has responded OK. Read response data.
15162306a36Sopenharmony_ci	 * SMU reads the input arguments from eight 32 bit registers starting
15262306a36Sopenharmony_ci	 * from SMN_HSMP_MSG_DATA and writes the response data to the same
15362306a36Sopenharmony_ci	 * SMN_HSMP_MSG_DATA address.
15462306a36Sopenharmony_ci	 * We copy the response data if any, back to the args[].
15562306a36Sopenharmony_ci	 */
15662306a36Sopenharmony_ci	index = 0;
15762306a36Sopenharmony_ci	while (index < msg->response_sz) {
15862306a36Sopenharmony_ci		ret = amd_hsmp_rdwr(root, SMN_HSMP_MSG_DATA + (index << 2),
15962306a36Sopenharmony_ci				    &msg->args[index], HSMP_RD);
16062306a36Sopenharmony_ci		if (ret) {
16162306a36Sopenharmony_ci			pr_err("Error %d reading response %u for message ID:%u\n",
16262306a36Sopenharmony_ci			       ret, index, msg->msg_id);
16362306a36Sopenharmony_ci			break;
16462306a36Sopenharmony_ci		}
16562306a36Sopenharmony_ci		index++;
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	return ret;
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic int validate_message(struct hsmp_message *msg)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	/* msg_id against valid range of message IDs */
17462306a36Sopenharmony_ci	if (msg->msg_id < HSMP_TEST || msg->msg_id >= HSMP_MSG_ID_MAX)
17562306a36Sopenharmony_ci		return -ENOMSG;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	/* msg_id is a reserved message ID */
17862306a36Sopenharmony_ci	if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_RSVD)
17962306a36Sopenharmony_ci		return -ENOMSG;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	/* num_args and response_sz against the HSMP spec */
18262306a36Sopenharmony_ci	if (msg->num_args != hsmp_msg_desc_table[msg->msg_id].num_args ||
18362306a36Sopenharmony_ci	    msg->response_sz != hsmp_msg_desc_table[msg->msg_id].response_sz)
18462306a36Sopenharmony_ci		return -EINVAL;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	return 0;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ciint hsmp_send_message(struct hsmp_message *msg)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	struct amd_northbridge *nb;
19262306a36Sopenharmony_ci	int ret;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if (!msg)
19562306a36Sopenharmony_ci		return -EINVAL;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	nb = node_to_amd_nb(msg->sock_ind);
19862306a36Sopenharmony_ci	if (!nb || !nb->root)
19962306a36Sopenharmony_ci		return -ENODEV;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	ret = validate_message(msg);
20262306a36Sopenharmony_ci	if (ret)
20362306a36Sopenharmony_ci		return ret;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/*
20662306a36Sopenharmony_ci	 * The time taken by smu operation to complete is between
20762306a36Sopenharmony_ci	 * 10us to 1ms. Sometime it may take more time.
20862306a36Sopenharmony_ci	 * In SMP system timeout of 100 millisecs should
20962306a36Sopenharmony_ci	 * be enough for the previous thread to finish the operation
21062306a36Sopenharmony_ci	 */
21162306a36Sopenharmony_ci	ret = down_timeout(&hsmp_sem[msg->sock_ind],
21262306a36Sopenharmony_ci			   msecs_to_jiffies(HSMP_MSG_TIMEOUT));
21362306a36Sopenharmony_ci	if (ret < 0)
21462306a36Sopenharmony_ci		return ret;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	ret = __hsmp_send_message(nb->root, msg);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	up(&hsmp_sem[msg->sock_ind]);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	return ret;
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(hsmp_send_message);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic int hsmp_test(u16 sock_ind, u32 value)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct hsmp_message msg = { 0 };
22762306a36Sopenharmony_ci	struct amd_northbridge *nb;
22862306a36Sopenharmony_ci	int ret = -ENODEV;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	nb = node_to_amd_nb(sock_ind);
23162306a36Sopenharmony_ci	if (!nb || !nb->root)
23262306a36Sopenharmony_ci		return ret;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	/*
23562306a36Sopenharmony_ci	 * Test the hsmp port by performing TEST command. The test message
23662306a36Sopenharmony_ci	 * takes one argument and returns the value of that argument + 1.
23762306a36Sopenharmony_ci	 */
23862306a36Sopenharmony_ci	msg.msg_id	= HSMP_TEST;
23962306a36Sopenharmony_ci	msg.num_args	= 1;
24062306a36Sopenharmony_ci	msg.response_sz	= 1;
24162306a36Sopenharmony_ci	msg.args[0]	= value;
24262306a36Sopenharmony_ci	msg.sock_ind	= sock_ind;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	ret = __hsmp_send_message(nb->root, &msg);
24562306a36Sopenharmony_ci	if (ret)
24662306a36Sopenharmony_ci		return ret;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	/* Check the response value */
24962306a36Sopenharmony_ci	if (msg.args[0] != (value + 1)) {
25062306a36Sopenharmony_ci		pr_err("Socket %d test message failed, Expected 0x%08X, received 0x%08X\n",
25162306a36Sopenharmony_ci		       sock_ind, (value + 1), msg.args[0]);
25262306a36Sopenharmony_ci		return -EBADE;
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	return ret;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	int __user *arguser = (int  __user *)arg;
26162306a36Sopenharmony_ci	struct hsmp_message msg = { 0 };
26262306a36Sopenharmony_ci	int ret;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (copy_struct_from_user(&msg, sizeof(msg), arguser, sizeof(struct hsmp_message)))
26562306a36Sopenharmony_ci		return -EFAULT;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	/*
26862306a36Sopenharmony_ci	 * Check msg_id is within the range of supported msg ids
26962306a36Sopenharmony_ci	 * i.e within the array bounds of hsmp_msg_desc_table
27062306a36Sopenharmony_ci	 */
27162306a36Sopenharmony_ci	if (msg.msg_id < HSMP_TEST || msg.msg_id >= HSMP_MSG_ID_MAX)
27262306a36Sopenharmony_ci		return -ENOMSG;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	switch (fp->f_mode & (FMODE_WRITE | FMODE_READ)) {
27562306a36Sopenharmony_ci	case FMODE_WRITE:
27662306a36Sopenharmony_ci		/*
27762306a36Sopenharmony_ci		 * Device is opened in O_WRONLY mode
27862306a36Sopenharmony_ci		 * Execute only set/configure commands
27962306a36Sopenharmony_ci		 */
28062306a36Sopenharmony_ci		if (hsmp_msg_desc_table[msg.msg_id].type != HSMP_SET)
28162306a36Sopenharmony_ci			return -EINVAL;
28262306a36Sopenharmony_ci		break;
28362306a36Sopenharmony_ci	case FMODE_READ:
28462306a36Sopenharmony_ci		/*
28562306a36Sopenharmony_ci		 * Device is opened in O_RDONLY mode
28662306a36Sopenharmony_ci		 * Execute only get/monitor commands
28762306a36Sopenharmony_ci		 */
28862306a36Sopenharmony_ci		if (hsmp_msg_desc_table[msg.msg_id].type != HSMP_GET)
28962306a36Sopenharmony_ci			return -EINVAL;
29062306a36Sopenharmony_ci		break;
29162306a36Sopenharmony_ci	case FMODE_READ | FMODE_WRITE:
29262306a36Sopenharmony_ci		/*
29362306a36Sopenharmony_ci		 * Device is opened in O_RDWR mode
29462306a36Sopenharmony_ci		 * Execute both get/monitor and set/configure commands
29562306a36Sopenharmony_ci		 */
29662306a36Sopenharmony_ci		break;
29762306a36Sopenharmony_ci	default:
29862306a36Sopenharmony_ci		return -EINVAL;
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	ret = hsmp_send_message(&msg);
30262306a36Sopenharmony_ci	if (ret)
30362306a36Sopenharmony_ci		return ret;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (hsmp_msg_desc_table[msg.msg_id].response_sz > 0) {
30662306a36Sopenharmony_ci		/* Copy results back to user for get/monitor commands */
30762306a36Sopenharmony_ci		if (copy_to_user(arguser, &msg, sizeof(struct hsmp_message)))
30862306a36Sopenharmony_ci			return -EFAULT;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	return 0;
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic const struct file_operations hsmp_fops = {
31562306a36Sopenharmony_ci	.owner		= THIS_MODULE,
31662306a36Sopenharmony_ci	.unlocked_ioctl	= hsmp_ioctl,
31762306a36Sopenharmony_ci	.compat_ioctl	= hsmp_ioctl,
31862306a36Sopenharmony_ci};
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic int hsmp_pltdrv_probe(struct platform_device *pdev)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	int i;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	hsmp_sem = devm_kzalloc(&pdev->dev,
32562306a36Sopenharmony_ci				(amd_nb_num() * sizeof(struct semaphore)),
32662306a36Sopenharmony_ci				GFP_KERNEL);
32762306a36Sopenharmony_ci	if (!hsmp_sem)
32862306a36Sopenharmony_ci		return -ENOMEM;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	for (i = 0; i < amd_nb_num(); i++)
33162306a36Sopenharmony_ci		sema_init(&hsmp_sem[i], 1);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	hsmp_device.name	= "hsmp_cdev";
33462306a36Sopenharmony_ci	hsmp_device.minor	= MISC_DYNAMIC_MINOR;
33562306a36Sopenharmony_ci	hsmp_device.fops	= &hsmp_fops;
33662306a36Sopenharmony_ci	hsmp_device.parent	= &pdev->dev;
33762306a36Sopenharmony_ci	hsmp_device.nodename	= "hsmp";
33862306a36Sopenharmony_ci	hsmp_device.mode	= 0644;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	return misc_register(&hsmp_device);
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic void hsmp_pltdrv_remove(struct platform_device *pdev)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	misc_deregister(&hsmp_device);
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic struct platform_driver amd_hsmp_driver = {
34962306a36Sopenharmony_ci	.probe		= hsmp_pltdrv_probe,
35062306a36Sopenharmony_ci	.remove_new	= hsmp_pltdrv_remove,
35162306a36Sopenharmony_ci	.driver		= {
35262306a36Sopenharmony_ci		.name	= DRIVER_NAME,
35362306a36Sopenharmony_ci	},
35462306a36Sopenharmony_ci};
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic struct platform_device *amd_hsmp_platdev;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic int __init hsmp_plt_init(void)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	int ret = -ENODEV;
36162306a36Sopenharmony_ci	u16 num_sockets;
36262306a36Sopenharmony_ci	int i;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD || boot_cpu_data.x86 < 0x19) {
36562306a36Sopenharmony_ci		pr_err("HSMP is not supported on Family:%x model:%x\n",
36662306a36Sopenharmony_ci		       boot_cpu_data.x86, boot_cpu_data.x86_model);
36762306a36Sopenharmony_ci		return ret;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	/*
37162306a36Sopenharmony_ci	 * amd_nb_num() returns number of SMN/DF interfaces present in the system
37262306a36Sopenharmony_ci	 * if we have N SMN/DF interfaces that ideally means N sockets
37362306a36Sopenharmony_ci	 */
37462306a36Sopenharmony_ci	num_sockets = amd_nb_num();
37562306a36Sopenharmony_ci	if (num_sockets == 0)
37662306a36Sopenharmony_ci		return ret;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	/* Test the hsmp interface on each socket */
37962306a36Sopenharmony_ci	for (i = 0; i < num_sockets; i++) {
38062306a36Sopenharmony_ci		ret = hsmp_test(i, 0xDEADBEEF);
38162306a36Sopenharmony_ci		if (ret) {
38262306a36Sopenharmony_ci			pr_err("HSMP is not supported on Fam:%x model:%x\n",
38362306a36Sopenharmony_ci			       boot_cpu_data.x86, boot_cpu_data.x86_model);
38462306a36Sopenharmony_ci			pr_err("Or Is HSMP disabled in BIOS ?\n");
38562306a36Sopenharmony_ci			return -EOPNOTSUPP;
38662306a36Sopenharmony_ci		}
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	ret = platform_driver_register(&amd_hsmp_driver);
39062306a36Sopenharmony_ci	if (ret)
39162306a36Sopenharmony_ci		return ret;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	amd_hsmp_platdev = platform_device_alloc(DRIVER_NAME, PLATFORM_DEVID_NONE);
39462306a36Sopenharmony_ci	if (!amd_hsmp_platdev) {
39562306a36Sopenharmony_ci		ret = -ENOMEM;
39662306a36Sopenharmony_ci		goto drv_unregister;
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	ret = platform_device_add(amd_hsmp_platdev);
40062306a36Sopenharmony_ci	if (ret) {
40162306a36Sopenharmony_ci		platform_device_put(amd_hsmp_platdev);
40262306a36Sopenharmony_ci		goto drv_unregister;
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	return 0;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cidrv_unregister:
40862306a36Sopenharmony_ci	platform_driver_unregister(&amd_hsmp_driver);
40962306a36Sopenharmony_ci	return ret;
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cistatic void __exit hsmp_plt_exit(void)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	platform_device_unregister(amd_hsmp_platdev);
41562306a36Sopenharmony_ci	platform_driver_unregister(&amd_hsmp_driver);
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cidevice_initcall(hsmp_plt_init);
41962306a36Sopenharmony_cimodule_exit(hsmp_plt_exit);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ciMODULE_DESCRIPTION("AMD HSMP Platform Interface Driver");
42262306a36Sopenharmony_ciMODULE_VERSION(DRIVER_VERSION);
42362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
424