diff options
Diffstat (limited to 'drivers/soc/qcom/scm-errata.c')
-rw-r--r-- | drivers/soc/qcom/scm-errata.c | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/drivers/soc/qcom/scm-errata.c b/drivers/soc/qcom/scm-errata.c new file mode 100644 index 000000000000..f68d88829336 --- /dev/null +++ b/drivers/soc/qcom/scm-errata.c @@ -0,0 +1,152 @@ +/* Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/smp.h> +#include <linux/debugfs.h> +#include <linux/cpu.h> +#include <soc/qcom/scm.h> + +#define SCM_KRYO_ERRATA_ID 0x12 +#define ERRATA_WA_DISABLE 0 +#define ERRATA_WA_ENABLE 1 +#define ERRATA_74_75_ID_BIT 0x000 +#define ERRATA_76_ID_BIT 0x100 + +static struct dentry *debugfs_base; +static bool kryo_e74_e75_wa = true; +static bool kryo_e76_wa; + +static void kryo_e74_e75_scm(void *enable) +{ + int ret; + struct scm_desc desc = {0}; + + if (!is_scm_armv8()) + return; + + desc.arginfo = SCM_ARGS(1); + desc.args[0] = enable ? ERRATA_WA_ENABLE : ERRATA_WA_DISABLE; + desc.args[0] |= ERRATA_74_75_ID_BIT; + + ret = scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_BOOT, SCM_KRYO_ERRATA_ID), + &desc); + if (ret) + pr_err("Failed to %s ERRATA_74_75 workaround\n", + enable ? "enable" : "disable"); +} + +static int kryo_e74_e75_set(void *data, u64 val) +{ + if ((val && kryo_e74_e75_wa) || (!val && !kryo_e74_e75_wa)) + return 0; + + kryo_e74_e75_wa = !!val; + + get_cpu(); + on_each_cpu(kryo_e74_e75_scm, (void *)kryo_e74_e75_wa, 1); + put_cpu(); + + return 0; +} + +static int kryo_e74_e75_get(void *data, u64 *val) +{ + *val = kryo_e74_e75_wa; + return 0; +} + +static void kryo_e76_scm(void *enable) +{ + int ret; + struct scm_desc desc = {0}; + + if (!is_scm_armv8()) + return; + + desc.arginfo = SCM_ARGS(1); + desc.args[0] = enable ? ERRATA_WA_ENABLE : ERRATA_WA_DISABLE; + desc.args[0] |= ERRATA_76_ID_BIT; + + ret = scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_BOOT, SCM_KRYO_ERRATA_ID), + &desc); + if (ret) + pr_err("Failed to %s ERRATA_76 workaround\n", + enable ? "enable" : "disable"); +} + +static int kryo_e76_set(void *data, u64 val) +{ + if ((val && kryo_e76_wa) || (!val && !kryo_e76_wa)) + return 0; + + kryo_e76_wa = !!val; + + get_cpu(); + on_each_cpu(kryo_e76_scm, (void *)kryo_e76_wa, 1); + put_cpu(); + + return 0; +} + +static int kryo_e76_get(void *data, u64 *val) +{ + *val = kryo_e76_wa; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(kryo_e74_e75_fops, kryo_e74_e75_get, + kryo_e74_e75_set, "%llu\n"); +DEFINE_SIMPLE_ATTRIBUTE(kryo_e76_fops, kryo_e76_get, + kryo_e76_set, "%llu\n"); + +static int scm_errata_notifier_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_STARTING: + kryo_e74_e75_scm((void *)kryo_e74_e75_wa); + kryo_e76_scm((void *)kryo_e76_wa); + break; + } + return 0; +} + +static struct notifier_block scm_errata_notifier = { + .notifier_call = scm_errata_notifier_callback, +}; + +static int __init scm_errata_init(void) +{ + int ret; + + debugfs_base = debugfs_create_dir("scm_errata", NULL); + if (!debugfs_base) + return -ENOMEM; + + if (!debugfs_create_file("kryo_e74_e75", S_IRUGO | S_IWUSR, + debugfs_base, NULL, &kryo_e74_e75_fops)) + goto err; + if (!debugfs_create_file("kryo_e76", S_IRUGO | S_IWUSR, + debugfs_base, NULL, &kryo_e76_fops)) + goto err; + ret = register_hotcpu_notifier(&scm_errata_notifier); + if (ret) + goto err; + + return 0; +err: + debugfs_remove_recursive(debugfs_base); + return ret; +} +device_initcall(scm_errata_init); |