1
0
mirror of https://github.com/physwizz/a155-U-u1.git synced 2025-02-15 00:18:03 +00:00
physwizz 99537be4e2 first
2024-03-11 06:53:12 +11:00

445 lines
10 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/spinlock.h>
#include <linux/suspend.h>
#include <linux/atomic.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <ssc_module.h>
#include <mt-plat/ssc.h>
//#define SSC_SYSFS_VLOGIC_BOUND_SUPPORT
#if IS_ENABLED(CONFIG_MTK_AEE_FEATURE)
#include <mt-plat/aee.h>
#endif
#if IS_ENABLED(CONFIG_ARM_SCMI_PROTOCOL)
#include <linux/scmi_protocol.h>
#include <tinysys-scmi.h>
#endif
#if IS_ENABLED(CONFIG_GPU_SUPPORT)
#include <gpufreq_v2.h>
static unsigned int gpueb_enable;
#endif
#if IS_ENABLED(CONFIG_MTK_AEE_FEATURE)
#define ssc_aee_print(string, args...) do {\
char ssc_name[100];\
int ret;\
ret = snprintf(ssc_name, 100, "[SSC] "string, ##args); \
if (ret > 0)\
aee_kernel_exception_api(__FILE__, __LINE__, \
DB_OPT_MMPROFILE_BUFFER | DB_OPT_NE_JBT_TRACES, \
ssc_name, "[SSC] error:"string, ##args); \
pr_info("[SSC] error:"string, ##args); \
} while (0)
#else
#define ssc_aee_print(string, args...) \
pr_info("[SSC] error:"string, ##args)
#endif
#define MTK_SSC_DTS_COMPATIBLE "mediatek,ssc"
#define MTK_GPU_DTS_COMPATIBLE "mediatek,gpueb"
#define SSC_VCORE_REGULATOR "dvfsrc-vcore"
#define PLT_SSC_INIT (0x504C5403) /*magic*/
struct kobject *ssc_kobj;
EXPORT_SYMBOL_GPL(ssc_kobj);
static unsigned int ssc_disable;
static BLOCKING_NOTIFIER_HEAD(vlogic_bound_chain);
static struct regulator *ssc_vcore_voter;
static int set_vcore_vlogic_bound(en)
{
int ret;
if (!ssc_vcore_voter) {
pr_info("[SSC] invalid vcore regulator\n");
return -1;
}
if (en)
ret = regulator_set_voltage(ssc_vcore_voter, 600000, INT_MAX);
else
ret = regulator_set_voltage(ssc_vcore_voter, 575000, INT_MAX);
if (ret) {
pr_info("[SSC] vcore vlogic bound %s fail\n", en ? "enable" : "disable");
return -1;
}
return 0;
}
static int set_gpu_vlogic_bound(int en)
{
int ret = 0;
#if IS_ENABLED(CONFIG_GPU_SUPPORT)
/* floor value for GPU = mV * 100 */
if (en)
ret = gpufreq_set_limit(TARGET_DEFAULT, LIMIT_SRAMRC,
GPUPPM_KEEP_IDX, 60000);
else
ret = gpufreq_set_limit(TARGET_DEFAULT, LIMIT_SRAMRC,
GPUPPM_KEEP_IDX, GPUPPM_RESET_IDX);
if (ret)
pr_info("[SSC] gpu vlogic bound %s fail\n", en ? "enable" : "disable");
#endif
return ret;
}
static int ssc_vlogic_bound_event(struct notifier_block *notifier, unsigned long event,
void *data)
{
unsigned int request_id = *((unsigned int*)data);
pr_info("[SSC] request ID = 0x%x, event = 0x%lx\n", request_id, event);
switch(event) {
case SSC_ENABLE_VLOGIC_BOUND:
set_gpu_vlogic_bound(1);
set_vcore_vlogic_bound(1);
return NOTIFY_DONE;
case SSC_DISABLE_VLOGIC_BOUND:
set_vcore_vlogic_bound(0);
set_gpu_vlogic_bound(0);
return NOTIFY_DONE;
case SSC_TIMEOUT:
return NOTIFY_DONE;
default:
return NOTIFY_BAD;
}
return NOTIFY_OK;
}
static struct notifier_block ssc_vlogic_notifier_func = {
.notifier_call = ssc_vlogic_bound_event,
.priority = 0,
};
static int ssc_vlogic_bound_call_chain(unsigned int val, unsigned int request_id)
{
int ret;
ret = blocking_notifier_call_chain(&vlogic_bound_chain, val, &request_id);
if (ret == NOTIFY_DONE || ret == NOTIFY_OK)
return 0;
else
return -1;
}
static atomic_t vlogic_bound_counter;
int ssc_vlogic_bound_register_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&vlogic_bound_chain, nb);
}
EXPORT_SYMBOL_GPL(ssc_vlogic_bound_register_notifier);
int ssc_vlogic_bound_unregister_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_unregister(&vlogic_bound_chain, nb);
}
EXPORT_SYMBOL_GPL(ssc_vlogic_bound_unregister_notifier);
int ssc_enable_vlogic_bound(int request_id)
{
/* check counter value */
if (atomic_read(&vlogic_bound_counter) < 0) {
return -1;
}
/* check request_id */
if (request_id < 0 || request_id >= SSC_REQUEST_NUM) {
return -1;
}
if (atomic_inc_return(&vlogic_bound_counter) == 1) {
return ssc_vlogic_bound_call_chain(SSC_ENABLE_VLOGIC_BOUND, request_id);
}
/* vlogic has been bounded */
return 0;
}
EXPORT_SYMBOL_GPL(ssc_enable_vlogic_bound);
int ssc_disable_vlogic_bound(int request_id)
{
/* check counter value */
if (atomic_read(&vlogic_bound_counter) < 0) {
return -1;
}
/* check request_id */
if (request_id < 0 || request_id >= SSC_REQUEST_NUM) {
return -1;
}
if (atomic_dec_return(&vlogic_bound_counter) == 0) {
return ssc_vlogic_bound_call_chain(SSC_DISABLE_VLOGIC_BOUND, request_id);
}
/* vlogic still bounded */
return 0;
}
EXPORT_SYMBOL_GPL(ssc_disable_vlogic_bound);
#ifdef SSC_SYSFS_VLOGIC_BOUND_SUPPORT
static ssize_t vlogic_bound_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
char mode[10];
if (sscanf(buf, "%9s", mode) != 1)
return -EPERM;
if (strcmp(mode, "enable") == 0)
ssc_enable_vlogic_bound(SSC_SW);
else if (strcmp(mode, "disable") == 0)
ssc_disable_vlogic_bound(SSC_SW);
return count;
}
static ssize_t vlogic_bound_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int len = 0;
len = snprintf(buf, PAGE_SIZE, "[SSC] bound cnt = %d\n",
atomic_read(&vlogic_bound_counter));
return len;
}
DEFINE_ATTR_RW(vlogic_bound);
#endif
static unsigned int safe_vlogic_uV = 0xFFFFFFFF;
unsigned int ssc_get_safe_vlogic_uV(void)
{
return safe_vlogic_uV;
}
#define SSC_TIMEOUT_NUM 6
static const char * const ssc_timeout_name[] = {
"SSC",
"GPU",
"ISP",
"CORE",
"APU",
"SRAM",
};
#if IS_ENABLED(CONFIG_ARM_SCMI_PROTOCOL)
static int ssc_scmi_feature_id;
static int plt_scmi_feature_id;
#define SSC_TIMEOUT_MASK_SHIFT (16)
static void ssc_notification_handler(u32 feature_id, scmi_tinysys_report *report)
{
#define LOG_BUF_SIZE 30
char log_buf[LOG_BUF_SIZE] = { 0 };
int log_size = 0;
int i, timeout;
int timeout_mask_shift = SSC_TIMEOUT_MASK_SHIFT;
if (report->p1 == SSC_STATUS_ERR) {
/* SSC timeout notifiy */
ssc_vlogic_bound_call_chain(SSC_TIMEOUT, SSC_ERR);
timeout = report->p2;
pr_info("[SSC] %s, timeout_sta = 0x%x, sram_sta = 0x%x, misc = 0x%x\n",
__func__, report->p2, report->p3, report->p4);
for (i = 1 ; i < SSC_TIMEOUT_NUM ; i++) {
timeout_mask_shift = (i - 1) * 2 + SSC_TIMEOUT_MASK_SHIFT;
if ((timeout & (0x1U << i)) &&
((timeout & (0x3 << timeout_mask_shift)) == 0x0)) {
log_size += scnprintf(log_buf + log_size,
LOG_BUF_SIZE - log_size, "%s",
ssc_timeout_name[i]);
}
}
pr_info("[SSC] CRDISPATCH_KEY: SSC VIOLATION: %s\n", log_buf);
BUG_ON(1);
}
return;
}
#endif
static DEFINE_SPINLOCK(ssc_locker);
static const struct of_device_id ssc_of_ids[] = {
{.compatible = "mediatek,ssc",},
{}
};
static int mt_ssc_pdrv_probe(struct platform_device *pdev)
{
ssc_vcore_voter = regulator_get(&pdev->dev, SSC_VCORE_REGULATOR);
if (IS_ERR(ssc_vcore_voter)) {
pr_info("[SSC] get ssc vcore regulator fail\n");
ssc_vcore_voter = NULL;
return -1;
}
return 0;
}
static int mt_ssc_pdrv_remove(struct platform_device *pdev)
{
return 0;
}
static struct platform_driver mt_ssc_pdrv = {
.probe = mt_ssc_pdrv_probe,
.remove = mt_ssc_pdrv_remove,
.driver = {
.name = "ssc_dvfs",
.owner = THIS_MODULE,
.of_match_table = ssc_of_ids,
},
};
static int __init ssc_init(void)
{
struct device_node *ssc_node;
int ret;
unsigned long flags;
#if IS_ENABLED(CONFIG_ARM_SCMI_PROTOCOL)
struct scmi_tinysys_info_st *tinfo = NULL;
#endif
pr_info("[SSC] %s\n", __func__);
ret = platform_driver_register(&mt_ssc_pdrv);
if (ret)
pr_info("[SSC] fail to register SSC platform driver\n");
spin_lock_irqsave(&ssc_locker, flags);
ssc_node = of_find_compatible_node(NULL, NULL, MTK_SSC_DTS_COMPATIBLE);
if (ssc_node) {
ret = of_property_read_u32(ssc_node,
MTK_SSC_SAFE_VLOGIC_STRING,
&safe_vlogic_uV);
pr_info("[SSC] safe_vlogic_uV = %d uV", safe_vlogic_uV);
/* This property is not defined*/
if (ret)
safe_vlogic_uV = 0xFFFFFFFF;
ret = of_property_read_u32(ssc_node,
"ssc_disable",
&ssc_disable);
if (!ret && ssc_disable == 1) {
spin_unlock_irqrestore(&ssc_locker, flags);
pr_info("[SSC] disabled\n");
return 0;
}
of_node_put(ssc_node);
}
/* set gpu vlogic bound 0.6V if gpueb not ready */
#if IS_ENABLED(CONFIG_GPU_SUPPORT)
ssc_node = of_find_compatible_node(NULL, NULL, MTK_GPU_DTS_COMPATIBLE);
if (ssc_node) {
ret = of_property_read_u32(ssc_node,
"gpueb-support",
&gpueb_enable);
pr_info("[SSC] gpueb enable = 0x%x\n", gpueb_enable);
if (gpueb_enable == 0)
gpufreq_set_limit(TARGET_DEFAULT, LIMIT_SRAMRC, GPUPPM_KEEP_IDX, 60000);
of_node_put(ssc_node);
}
#endif
spin_unlock_irqrestore(&ssc_locker, flags);
/* scmi interface initialization */
#if IS_ENABLED(CONFIG_ARM_SCMI_PROTOCOL)
tinfo = get_scmi_tinysys_info();
if (!tinfo) {
pr_info("[SSC] get SCMI info fail\n");
goto SKIP_SCMI;
}
ret = of_property_read_u32(tinfo->sdev->dev.of_node, "scmi_ssc", &ssc_scmi_feature_id);
pr_info("[SSC] ssc scmi id = %d\n", ssc_scmi_feature_id);
scmi_tinysys_register_event_notifier(ssc_scmi_feature_id,
(f_handler_t)ssc_notification_handler);
ret = scmi_tinysys_event_notify(ssc_scmi_feature_id, 1);
if (ret < 0)
pr_info("[SSC] SCMI notify register fail\n");
ret = of_property_read_u32(tinfo->sdev->dev.of_node, "scmi_plt", &plt_scmi_feature_id);
ret = scmi_tinysys_common_set(tinfo->ph, plt_scmi_feature_id, PLT_SSC_INIT,
0, 0, 0, 0);
if (ret)
ssc_aee_print("[SSC] SCMI common set fail!\n");
else
pr_info("[SSC] notify done!\n");
SKIP_SCMI:
#endif
ssc_vlogic_bound_register_notifier(&ssc_vlogic_notifier_func);
atomic_set(&vlogic_bound_counter, 0);
/* create sysfs entry for voltage bound */
ssc_kobj = kobject_create_and_add("ssc", kernel_kobj);
#ifdef SSC_SYSFS_VLOGIC_BOUND_SUPPORT
if (ssc_kobj)
ret = sysfs_create_file(ssc_kobj, __ATTR_OF(vlogic_bound));
#endif
return 0;
}
static void __exit ssc_deinit(void)
{
if (ssc_disable == 1)
return;
#ifdef SSC_SYSFS_VLOGIC_BOUND_SUPPORT
sysfs_remove_file(ssc_kobj, __ATTR_OF(vlogic_bound));
#endif
kobject_del(ssc_kobj);
ssc_vlogic_bound_unregister_notifier(&ssc_vlogic_notifier_func);
}
module_init(ssc_init);
module_exit(ssc_deinit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("mtk ssc module");
MODULE_AUTHOR("MediaTek Inc.");