Skip to content

Commit

Permalink
platform/x86: ISST: Restore state on resume
Browse files Browse the repository at this point in the history
Commands which causes PUNIT writes, store them and restore them on system
resume. The driver stores all such requests in a hash table and stores the
the latest mailbox request parameters. On resume these commands mail box
commands are executed again. There are only 5 such mail box commands which
will trigger such processing so a very low overhead in store and execute
on resume. Also there is no order requirement for mail box commands for
these write/set commands. There is one MSR request for changing turbo
ratio limits, this also stored and get restored on resume and cpu online.

Signed-off-by: Srinivas Pandruvada <[email protected]>
Signed-off-by: Andy Shevchenko <[email protected]>
  • Loading branch information
spandruvada authored and andy-shev committed Jul 2, 2019
1 parent e765f37 commit f607874
Show file tree
Hide file tree
Showing 5 changed files with 258 additions and 1 deletion.
154 changes: 154 additions & 0 deletions drivers/platform/x86/intel_speed_select_if/isst_if_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <linux/cpufeature.h>
#include <linux/cpuhotplug.h>
#include <linux/fs.h>
#include <linux/hashtable.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/pci.h>
Expand Down Expand Up @@ -58,6 +59,151 @@ static const struct isst_cmd_set_req_type isst_cmd_set_reqs[] = {
{0x7F, 0x08, 0x00},
};

struct isst_cmd {
struct hlist_node hnode;
u64 data;
u32 cmd;
int cpu;
int mbox_cmd_type;
u32 param;
};

static DECLARE_HASHTABLE(isst_hash, 8);
static DEFINE_MUTEX(isst_hash_lock);

static int isst_store_new_cmd(int cmd, u32 cpu, int mbox_cmd_type, u32 param,
u32 data)
{
struct isst_cmd *sst_cmd;

sst_cmd = kmalloc(sizeof(*sst_cmd), GFP_KERNEL);
if (!sst_cmd)
return -ENOMEM;

sst_cmd->cpu = cpu;
sst_cmd->cmd = cmd;
sst_cmd->mbox_cmd_type = mbox_cmd_type;
sst_cmd->param = param;
sst_cmd->data = data;

hash_add(isst_hash, &sst_cmd->hnode, sst_cmd->cmd);

return 0;
}

static void isst_delete_hash(void)
{
struct isst_cmd *sst_cmd;
struct hlist_node *tmp;
int i;

hash_for_each_safe(isst_hash, i, tmp, sst_cmd, hnode) {
hash_del(&sst_cmd->hnode);
kfree(sst_cmd);
}
}

/**
* isst_store_cmd() - Store command to a hash table
* @cmd: Mailbox command.
* @sub_cmd: Mailbox sub-command or MSR id.
* @mbox_cmd_type: Mailbox or MSR command.
* @param: Mailbox parameter.
* @data: Mailbox request data or MSR data.
*
* Stores the command to a hash table if there is no such command already
* stored. If already stored update the latest parameter and data for the
* command.
*
* Return: Return result of store to hash table, 0 for success, others for
* failure.
*/
int isst_store_cmd(int cmd, int sub_cmd, u32 cpu, int mbox_cmd_type,
u32 param, u64 data)
{
struct isst_cmd *sst_cmd;
int full_cmd, ret;

full_cmd = (cmd & GENMASK_ULL(15, 0)) << 16;
full_cmd |= (sub_cmd & GENMASK_ULL(15, 0));
mutex_lock(&isst_hash_lock);
hash_for_each_possible(isst_hash, sst_cmd, hnode, full_cmd) {
if (sst_cmd->cmd == full_cmd && sst_cmd->cpu == cpu &&
sst_cmd->mbox_cmd_type == mbox_cmd_type) {
sst_cmd->param = param;
sst_cmd->data = data;
mutex_unlock(&isst_hash_lock);
return 0;
}
}

ret = isst_store_new_cmd(full_cmd, cpu, mbox_cmd_type, param, data);
mutex_unlock(&isst_hash_lock);

return ret;
}
EXPORT_SYMBOL_GPL(isst_store_cmd);

static void isst_mbox_resume_command(struct isst_if_cmd_cb *cb,
struct isst_cmd *sst_cmd)
{
struct isst_if_mbox_cmd mbox_cmd;
int wr_only;

mbox_cmd.command = (sst_cmd->cmd & GENMASK_ULL(31, 16)) >> 16;
mbox_cmd.sub_command = sst_cmd->cmd & GENMASK_ULL(15, 0);
mbox_cmd.parameter = sst_cmd->param;
mbox_cmd.req_data = sst_cmd->data;
mbox_cmd.logical_cpu = sst_cmd->cpu;
(cb->cmd_callback)((u8 *)&mbox_cmd, &wr_only, 1);
}

/**
* isst_resume_common() - Process Resume request
*
* On resume replay all mailbox commands and MSRs.
*
* Return: None.
*/
void isst_resume_common(void)
{
struct isst_cmd *sst_cmd;
int i;

hash_for_each(isst_hash, i, sst_cmd, hnode) {
struct isst_if_cmd_cb *cb;

if (sst_cmd->mbox_cmd_type) {
cb = &punit_callbacks[ISST_IF_DEV_MBOX];
if (cb->registered)
isst_mbox_resume_command(cb, sst_cmd);
} else {
wrmsrl_safe_on_cpu(sst_cmd->cpu, sst_cmd->cmd,
sst_cmd->data);
}
}
}
EXPORT_SYMBOL_GPL(isst_resume_common);

static void isst_restore_msr_local(int cpu)
{
struct isst_cmd *sst_cmd;
int i;

mutex_lock(&isst_hash_lock);
for (i = 0; i < ARRAY_SIZE(punit_msr_white_list); ++i) {
if (!punit_msr_white_list[i])
break;

hash_for_each_possible(isst_hash, sst_cmd, hnode,
punit_msr_white_list[i]) {
if (!sst_cmd->mbox_cmd_type && sst_cmd->cpu == cpu)
wrmsrl_safe(sst_cmd->cmd, sst_cmd->data);
}
}
mutex_unlock(&isst_hash_lock);
}

/**
* isst_if_mbox_cmd_invalid() - Check invalid mailbox commands
* @cmd: Pointer to the command structure to verify.
Expand Down Expand Up @@ -185,6 +331,8 @@ static int isst_if_cpu_online(unsigned int cpu)
}
isst_cpu_info[cpu].punit_cpu_id = data;

isst_restore_msr_local(cpu);

return 0;
}

Expand Down Expand Up @@ -267,6 +415,10 @@ static long isst_if_msr_cmd_req(u8 *cmd_ptr, int *write_only, int resume)
msr_cmd->msr,
msr_cmd->data);
*write_only = 1;
if (!ret && !resume)
ret = isst_store_cmd(0, msr_cmd->msr,
msr_cmd->logical_cpu,
0, 0, msr_cmd->data);
} else {
u64 data;

Expand Down Expand Up @@ -507,6 +659,8 @@ void isst_if_cdev_unregister(int device_type)
mutex_lock(&punit_misc_dev_lock);
misc_usage_count--;
punit_callbacks[device_type].registered = 0;
if (device_type == ISST_IF_DEV_MBOX)
isst_delete_hash();
if (!misc_usage_count && !misc_device_ret) {
misc_deregister(&isst_if_char_driver);
isst_if_cpu_info_exit();
Expand Down
3 changes: 3 additions & 0 deletions drivers/platform/x86/intel_speed_select_if/isst_if_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,7 @@ void isst_if_cdev_unregister(int type);
struct pci_dev *isst_if_get_pci_dev(int cpu, int bus, int dev, int fn);
bool isst_if_mbox_cmd_set_req(struct isst_if_mbox_cmd *mbox_cmd);
bool isst_if_mbox_cmd_invalid(struct isst_if_mbox_cmd *cmd);
int isst_store_cmd(int cmd, int sub_command, u32 cpu, int mbox_cmd,
u32 param, u64 data);
void isst_resume_common(void);
#endif
38 changes: 37 additions & 1 deletion drivers/platform/x86/intel_speed_select_if/isst_if_mbox_msr.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <linux/pci.h>
#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/suspend.h>
#include <linux/topology.h>
#include <linux/uaccess.h>
#include <uapi/linux/isst_if.h>
Expand Down Expand Up @@ -128,11 +129,37 @@ static long isst_if_mbox_proc_cmd(u8 *cmd_ptr, int *write_only, int resume)
if (ret)
return ret;

if (!action.err && !resume && isst_if_mbox_cmd_set_req(action.mbox_cmd))
action.err = isst_store_cmd(action.mbox_cmd->command,
action.mbox_cmd->sub_command,
action.mbox_cmd->logical_cpu, 1,
action.mbox_cmd->parameter,
action.mbox_cmd->req_data);
*write_only = 0;

return action.err;
}


static int isst_pm_notify(struct notifier_block *nb,
unsigned long mode, void *_unused)
{
switch (mode) {
case PM_POST_HIBERNATION:
case PM_POST_RESTORE:
case PM_POST_SUSPEND:
isst_resume_common();
break;
default:
break;
}
return 0;
}

static struct notifier_block isst_pm_nb = {
.notifier_call = isst_pm_notify,
};

#define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, }

static const struct x86_cpu_id isst_if_cpu_ids[] = {
Expand Down Expand Up @@ -166,12 +193,21 @@ static int __init isst_if_mbox_init(void)
cb.offset = offsetof(struct isst_if_mbox_cmds, mbox_cmd);
cb.cmd_callback = isst_if_mbox_proc_cmd;
cb.owner = THIS_MODULE;
return isst_if_cdev_register(ISST_IF_DEV_MBOX, &cb);
ret = isst_if_cdev_register(ISST_IF_DEV_MBOX, &cb);
if (ret)
return ret;

ret = register_pm_notifier(&isst_pm_nb);
if (ret)
isst_if_cdev_unregister(ISST_IF_DEV_MBOX);

return ret;
}
module_init(isst_if_mbox_init)

static void __exit isst_if_mbox_exit(void)
{
unregister_pm_notifier(&isst_pm_nb);
isst_if_cdev_unregister(ISST_IF_DEV_MBOX);
}
module_exit(isst_if_mbox_exit)
Expand Down
15 changes: 15 additions & 0 deletions drivers/platform/x86/intel_speed_select_if/isst_if_mbox_pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ static long isst_if_mbox_proc_cmd(u8 *cmd_ptr, int *write_only, int resume)
*/
mutex_lock(&punit_dev->mutex);
ret = isst_if_mbox_cmd(pdev, mbox_cmd);
if (!ret && !resume && isst_if_mbox_cmd_set_req(mbox_cmd))
ret = isst_store_cmd(mbox_cmd->command,
mbox_cmd->sub_command,
mbox_cmd->logical_cpu, 1,
mbox_cmd->parameter,
mbox_cmd->req_data);
mutex_unlock(&punit_dev->mutex);
if (ret)
return ret;
Expand Down Expand Up @@ -186,11 +192,20 @@ static void isst_if_mbox_remove(struct pci_dev *pdev)
mutex_destroy(&punit_dev->mutex);
}

static int __maybe_unused isst_if_resume(struct device *device)
{
isst_resume_common();
return 0;
}

static SIMPLE_DEV_PM_OPS(isst_if_pm_ops, NULL, isst_if_resume);

static struct pci_driver isst_if_pci_driver = {
.name = "isst_if_mbox_pci",
.id_table = isst_if_mbox_ids,
.probe = isst_if_mbox_probe,
.remove = isst_if_mbox_remove,
.driver.pm = &isst_if_pm_ops,
};

module_pci_driver(isst_if_pci_driver);
Expand Down
49 changes: 49 additions & 0 deletions drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,20 @@

#include "isst_if_common.h"

struct isst_mmio_range {
int beg;
int end;
};

struct isst_mmio_range mmio_range[] = {
{0x04, 0x14},
{0x20, 0xD0},
};

struct isst_if_device {
void __iomem *punit_mmio;
u32 range_0[5];
u32 range_1[45];
struct mutex mutex;
};

Expand Down Expand Up @@ -118,11 +130,48 @@ static void isst_if_remove(struct pci_dev *pdev)
mutex_destroy(&punit_dev->mutex);
}

static int __maybe_unused isst_if_suspend(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct isst_if_device *punit_dev;
int i;

punit_dev = pci_get_drvdata(pdev);
for (i = 0; i < ARRAY_SIZE(punit_dev->range_0); ++i)
punit_dev->range_0[i] = readl(punit_dev->punit_mmio +
mmio_range[0].beg + 4 * i);
for (i = 0; i < ARRAY_SIZE(punit_dev->range_1); ++i)
punit_dev->range_1[i] = readl(punit_dev->punit_mmio +
mmio_range[1].beg + 4 * i);

return 0;
}

static int __maybe_unused isst_if_resume(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct isst_if_device *punit_dev;
int i;

punit_dev = pci_get_drvdata(pdev);
for (i = 0; i < ARRAY_SIZE(punit_dev->range_0); ++i)
writel(punit_dev->range_0[i], punit_dev->punit_mmio +
mmio_range[0].beg + 4 * i);
for (i = 0; i < ARRAY_SIZE(punit_dev->range_1); ++i)
writel(punit_dev->range_1[i], punit_dev->punit_mmio +
mmio_range[1].beg + 4 * i);

return 0;
}

static SIMPLE_DEV_PM_OPS(isst_if_pm_ops, isst_if_suspend, isst_if_resume);

static struct pci_driver isst_if_pci_driver = {
.name = "isst_if_pci",
.id_table = isst_if_ids,
.probe = isst_if_probe,
.remove = isst_if_remove,
.driver.pm = &isst_if_pm_ops,
};

module_pci_driver(isst_if_pci_driver);
Expand Down

0 comments on commit f607874

Please sign in to comment.