/* 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 #include #include #include #include #include #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);