Initial commit: Linux kernel 4.19 for Unplugged OS

This commit is contained in:
ziv committed 2025-10-06 13:52:42 +00:00
commit d30235b04c
80519 files changed
+35800681

No files matched your search

+150
View File
@@ -0,0 +1,150 @@
menuconfig PM_DEVFREQ
bool "Generic Dynamic Voltage and Frequency Scaling (DVFS) support"
select SRCU
select PM_OPP
help
A device may have a list of frequencies and voltages available.
devfreq, a generic DVFS framework can be registered for a device
in order to let the governor provided to devfreq choose an
operating frequency based on the device driver's policy.
Each device may have its own governor and policy. Devfreq can
reevaluate the device state periodically and/or based on the
notification to "nb", a notifier block, of devfreq.
Like some CPUs with CPUfreq, a device may have multiple clocks.
However, because the clock frequencies of a single device are
determined by the single device's state, an instance of devfreq
is attached to a single device and returns a "representative"
clock frequency of the device, which is also attached
to a device by 1-to-1. The device registering devfreq takes the
responsibility to "interpret" the representative frequency and
to set its every clock accordingly with the "target" callback
given to devfreq.
When OPP is used with the devfreq device, it is recommended to
register devfreq's nb to the OPP's notifier head. If OPP is
used with the devfreq device, you may use OPP helper
functions defined in devfreq.h.
if PM_DEVFREQ
comment "DEVFREQ Governors"
config DEVFREQ_GOV_SIMPLE_ONDEMAND
tristate "Simple Ondemand"
help
Chooses frequency based on the recent load on the device. Works
similar as ONDEMAND governor of CPUFREQ does. A device with
Simple-Ondemand should be able to provide busy/total counter
values that imply the usage rate. A device may provide tuned
values to the governor with data field at devfreq_add_device().
config DEVFREQ_GOV_PERFORMANCE
tristate "Performance"
help
Sets the frequency at the maximum available frequency.
This governor always returns UINT_MAX as frequency so that
the DEVFREQ framework returns the highest frequency available
at any time.
config DEVFREQ_GOV_POWERSAVE
tristate "Powersave"
help
Sets the frequency at the minimum available frequency.
This governor always returns 0 as frequency so that
the DEVFREQ framework returns the lowest frequency available
at any time.
config DEVFREQ_GOV_USERSPACE
tristate "Userspace"
help
Sets the frequency at the user specified one.
This governor returns the user configured frequency if there
has been an input to /sys/devices/.../power/devfreq_set_freq.
Otherwise, the governor does not change the frequency
given at the initialization.
config DEVFREQ_GOV_PASSIVE
tristate "Passive"
help
Sets the frequency based on the frequency of its parent devfreq
device. This governor does not change the frequency by itself
through sysfs entries. The passive governor recommends that
devfreq device uses the OPP table to get the frequency/voltage.
comment "DEVFREQ Drivers"
config ARM_EXYNOS_BUS_DEVFREQ
tristate "ARM EXYNOS Generic Memory Bus DEVFREQ Driver"
depends on ARCH_EXYNOS || COMPILE_TEST
select DEVFREQ_GOV_SIMPLE_ONDEMAND
select DEVFREQ_GOV_PASSIVE
select DEVFREQ_EVENT_EXYNOS_PPMU
select PM_DEVFREQ_EVENT
select PM_OPP
help
This adds the common DEVFREQ driver for Exynos Memory bus. Exynos
Memory bus has one more group of memory bus (e.g, MIF and INT block).
Each memory bus group could contain many memoby bus block. It reads
PPMU counters of memory controllers by using DEVFREQ-event device
and adjusts the operating frequencies and voltages with OPP support.
This does not yet operate with optimal voltages.
config ARM_TEGRA_DEVFREQ
tristate "Tegra DEVFREQ Driver"
depends on ARCH_TEGRA_124_SOC
select DEVFREQ_GOV_SIMPLE_ONDEMAND
select PM_OPP
help
This adds the DEVFREQ driver for the Tegra family of SoCs.
It reads ACTMON counters of memory controllers and adjusts the
operating frequencies and voltages with OPP support.
config ARM_RK3399_DMC_DEVFREQ
tristate "ARM RK3399 DMC DEVFREQ Driver"
depends on (ARCH_ROCKCHIP && HAVE_ARM_SMCCC) || \
(COMPILE_TEST && HAVE_ARM_SMCCC)
select DEVFREQ_EVENT_ROCKCHIP_DFI
select DEVFREQ_GOV_SIMPLE_ONDEMAND
select PM_DEVFREQ_EVENT
select PM_OPP
help
This adds the DEVFREQ driver for the RK3399 DMC(Dynamic Memory Controller).
It sets the frequency for the memory controller and reads the usage counts
from hardware.
config MTK_QOS_SUPPORT
bool "MTK QOS SUPPORT"
help
This kernel config can be used to enable MTK QOS support.
MTK QOS can support adjust vcore voltage and dram
frequency
If unsure, say N.
config MTK_DVFSRC_MT6833_PRETEST
tristate "MTK_DVFSRC_MT6833_PRETEST"
help
This is test for pre-testing for mt6833 on MT6853 phone
It just for test, and it will remove after mt6833 chip
is ready.
If unsure, say N.
config MTK_DVFSRC_MT6877_PRETEST
tristate "MTK_DVFSRC_MT6877_PRETEST"
help
This is test for pre-testing for mt6877 on MT6853 phone
It just for test, and it will remove after mt6877 chip
is ready.
If unsure, say N.
config MTK_DVFSRC_MT6781_PRETEST
tristate "MTK_DVFSRC_MT6781_PRETEST"
help
This is test for pre-testing for mt6781 on MT6785 phone
It just for test, and it will remove after mt6781 chip
is ready.
If unsure, say N
source "drivers/devfreq/event/Kconfig"
endif # PM_DEVFREQ
+60
View File
@@ -0,0 +1,60 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_PM_DEVFREQ) += devfreq.o
obj-$(CONFIG_PM_DEVFREQ_EVENT) += devfreq-event.o
obj-$(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) += governor_simpleondemand.o
obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE) += governor_performance.o
obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o
obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o
obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o
# DEVFREQ Drivers
obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o
obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o
obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra-devfreq.o
ifneq ($(strip $(CONFIG_MTK_TINYSYS_SSPM_SUPPORT)$(CONFIG_MTK_TINYSYS_SSPM_PLT_SUPPORT)),)
ifneq ($(strip $(CONFIG_MTK_TINYSYS_SSPM_V2)),)
ccflags-y += -I$(srctree)/drivers/misc/mediatek/sspm/v2/
else ifneq ($(CONFIG_MTK_TINYSYS_SSPM_VERSION),"")
ccflags-y += -I$(srctree)/drivers/misc/mediatek/sspm/v1/
else
ccflags-y += -I$(srctree)/drivers/misc/mediatek/sspm/
endif
endif
ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/include/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/spm/$(MTK_PLATFORM)/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/dramc/$(MTK_PLATFORM)/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
ccflags-y += -I$(srctree)/drivers/watchdog/mediatek/include/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/dramc/$(MTK_PLATFORM)/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/sspm/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/sspm/v1/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)/
ccflags-y += -I$(srctree)/drivers/watchdog/mediatek/include/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/emi/$(MTK_PLATFORM)/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/emi/submodule/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/mmdvfs/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/spm/common/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/$(CONFIG_MTK_PLATFORM)\
ifeq ($(CONFIG_MTK_QOS_SUPPORT), y)
obj-$(CONFIG_MACH_MT6771) += helio-dvfsrc.o helio-dvfsrc-sysfs.o
obj-$(CONFIG_MACH_MT6771) += helio-dvfsrc-mt6771.o
obj-$(CONFIG_MACH_MT6771) += helio-dvfsrc-opp-mt6771.o
endif
obj-$(CONFIG_MACH_MT6768) += helio-dvfsrc-ipi.o
obj-$(CONFIG_MACH_MT6768) += helio-dvfsrc-v2/helio-dvfsrc.o helio-dvfsrc-v2/helio-dvfsrc-mt6768.o helio-dvfsrc-v2/helio-dvfsrc-opp-mt6768.o helio-dvfsrc-v2/helio-dvfsrc-opp.o helio-dvfsrc-v2/helio-dvfsrc-sysfs.o
obj-$(CONFIG_MACH_MT6785) += helio-dvfsrc-v3/
obj-$(CONFIG_MACH_MT6885) += helio-dvfsrc-v3/
obj-$(CONFIG_MACH_MT6873) += helio-dvfsrc-v3/
obj-$(CONFIG_MACH_MT6853) += helio-dvfsrc-v3/
obj-$(CONFIG_MACH_MT6893) += helio-dvfsrc-v3/
obj-$(CONFIG_MACH_MT6833) += helio-dvfsrc-v3/
obj-$(CONFIG_MACH_MT6877) += helio-dvfsrc-v3/
obj-$(CONFIG_MACH_MT6781) += helio-dvfsrc-v3/
# DEVFREQ Event Drivers
obj-$(CONFIG_PM_DEVFREQ_EVENT) += event/
+483
View File
@@ -0,0 +1,483 @@
/*
* devfreq-event: a framework to provide raw data and events of devfreq devices
*
* Copyright (C) 2015 Samsung Electronics
* Author: Chanwoo Choi <cw00.choi@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This driver is based on drivers/devfreq/devfreq.c.
*/
#include <linux/devfreq-event.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/of.h>
static struct class *devfreq_event_class;
/* The list of all devfreq event list */
static LIST_HEAD(devfreq_event_list);
static DEFINE_MUTEX(devfreq_event_list_lock);
#define to_devfreq_event(DEV) container_of(DEV, struct devfreq_event_dev, dev)
/**
* devfreq_event_enable_edev() - Enable the devfreq-event dev and increase
* the enable_count of devfreq-event dev.
* @edev : the devfreq-event device
*
* Note that this function increase the enable_count and enable the
* devfreq-event device. The devfreq-event device should be enabled before
* using it by devfreq device.
*/
int devfreq_event_enable_edev(struct devfreq_event_dev *edev)
{
int ret = 0;
if (!edev || !edev->desc)
return -EINVAL;
mutex_lock(&edev->lock);
if (edev->desc->ops && edev->desc->ops->enable
&& edev->enable_count == 0) {
ret = edev->desc->ops->enable(edev);
if (ret < 0)
goto err;
}
edev->enable_count++;
err:
mutex_unlock(&edev->lock);
return ret;
}
EXPORT_SYMBOL_GPL(devfreq_event_enable_edev);
/**
* devfreq_event_disable_edev() - Disable the devfreq-event dev and decrease
* the enable_count of the devfreq-event dev.
* @edev : the devfreq-event device
*
* Note that this function decrease the enable_count and disable the
* devfreq-event device. After the devfreq-event device is disabled,
* devfreq device can't use the devfreq-event device for get/set/reset
* operations.
*/
int devfreq_event_disable_edev(struct devfreq_event_dev *edev)
{
int ret = 0;
if (!edev || !edev->desc)
return -EINVAL;
mutex_lock(&edev->lock);
if (edev->enable_count <= 0) {
dev_warn(&edev->dev, "unbalanced enable_count\n");
ret = -EIO;
goto err;
}
if (edev->desc->ops && edev->desc->ops->disable
&& edev->enable_count == 1) {
ret = edev->desc->ops->disable(edev);
if (ret < 0)
goto err;
}
edev->enable_count--;
err:
mutex_unlock(&edev->lock);
return ret;
}
EXPORT_SYMBOL_GPL(devfreq_event_disable_edev);
/**
* devfreq_event_is_enabled() - Check whether devfreq-event dev is enabled or
* not.
* @edev : the devfreq-event device
*
* Note that this function check whether devfreq-event dev is enabled or not.
* If return true, the devfreq-event dev is enabeld. If return false, the
* devfreq-event dev is disabled.
*/
bool devfreq_event_is_enabled(struct devfreq_event_dev *edev)
{
bool enabled = false;
if (!edev || !edev->desc)
return enabled;
mutex_lock(&edev->lock);
if (edev->enable_count > 0)
enabled = true;
mutex_unlock(&edev->lock);
return enabled;
}
EXPORT_SYMBOL_GPL(devfreq_event_is_enabled);
/**
* devfreq_event_set_event() - Set event to devfreq-event dev to start.
* @edev : the devfreq-event device
*
* Note that this function set the event to the devfreq-event device to start
* for getting the event data which could be various event type.
*/
int devfreq_event_set_event(struct devfreq_event_dev *edev)
{
int ret;
if (!edev || !edev->desc)
return -EINVAL;
if (!edev->desc->ops || !edev->desc->ops->set_event)
return -EINVAL;
if (!devfreq_event_is_enabled(edev))
return -EPERM;
mutex_lock(&edev->lock);
ret = edev->desc->ops->set_event(edev);
mutex_unlock(&edev->lock);
return ret;
}
EXPORT_SYMBOL_GPL(devfreq_event_set_event);
/**
* devfreq_event_get_event() - Get {load|total}_count from devfreq-event dev.
* @edev : the devfreq-event device
* @edata : the calculated data of devfreq-event device
*
* Note that this function get the calculated event data from devfreq-event dev
* after stoping the progress of whole sequence of devfreq-event dev.
*/
int devfreq_event_get_event(struct devfreq_event_dev *edev,
struct devfreq_event_data *edata)
{
int ret;
if (!edev || !edev->desc)
return -EINVAL;
if (!edev->desc->ops || !edev->desc->ops->get_event)
return -EINVAL;
if (!devfreq_event_is_enabled(edev))
return -EINVAL;
edata->total_count = edata->load_count = 0;
mutex_lock(&edev->lock);
ret = edev->desc->ops->get_event(edev, edata);
if (ret < 0)
edata->total_count = edata->load_count = 0;
mutex_unlock(&edev->lock);
return ret;
}
EXPORT_SYMBOL_GPL(devfreq_event_get_event);
/**
* devfreq_event_reset_event() - Reset all opeations of devfreq-event dev.
* @edev : the devfreq-event device
*
* Note that this function stop all operations of devfreq-event dev and reset
* the current event data to make the devfreq-event device into initial state.
*/
int devfreq_event_reset_event(struct devfreq_event_dev *edev)
{
int ret = 0;
if (!edev || !edev->desc)
return -EINVAL;
if (!devfreq_event_is_enabled(edev))
return -EPERM;
mutex_lock(&edev->lock);
if (edev->desc->ops && edev->desc->ops->reset)
ret = edev->desc->ops->reset(edev);
mutex_unlock(&edev->lock);
return ret;
}
EXPORT_SYMBOL_GPL(devfreq_event_reset_event);
/**
* devfreq_event_get_edev_by_phandle() - Get the devfreq-event dev from
* devicetree.
* @dev : the pointer to the given device
* @index : the index into list of devfreq-event device
*
* Note that this function return the pointer of devfreq-event device.
*/
struct devfreq_event_dev *devfreq_event_get_edev_by_phandle(struct device *dev,
int index)
{
struct device_node *node;
struct devfreq_event_dev *edev;
if (!dev->of_node)
return ERR_PTR(-EINVAL);
node = of_parse_phandle(dev->of_node, "devfreq-events", index);
if (!node)
return ERR_PTR(-ENODEV);
mutex_lock(&devfreq_event_list_lock);
list_for_each_entry(edev, &devfreq_event_list, node) {
if (edev->dev.parent && edev->dev.parent->of_node == node)
goto out;
}
list_for_each_entry(edev, &devfreq_event_list, node) {
if (!strcmp(edev->desc->name, node->name))
goto out;
}
edev = NULL;
out:
mutex_unlock(&devfreq_event_list_lock);
if (!edev) {
of_node_put(node);
return ERR_PTR(-ENODEV);
}
of_node_put(node);
return edev;
}
EXPORT_SYMBOL_GPL(devfreq_event_get_edev_by_phandle);
/**
* devfreq_event_get_edev_count() - Get the count of devfreq-event dev
* @dev : the pointer to the given device
*
* Note that this function return the count of devfreq-event devices.
*/
int devfreq_event_get_edev_count(struct device *dev)
{
int count;
if (!dev->of_node) {
dev_err(dev, "device does not have a device node entry\n");
return -EINVAL;
}
count = of_property_count_elems_of_size(dev->of_node, "devfreq-events",
sizeof(u32));
if (count < 0) {
dev_err(dev,
"failed to get the count of devfreq-event in %pOF node\n",
dev->of_node);
return count;
}
return count;
}
EXPORT_SYMBOL_GPL(devfreq_event_get_edev_count);
static void devfreq_event_release_edev(struct device *dev)
{
struct devfreq_event_dev *edev = to_devfreq_event(dev);
kfree(edev);
}
/**
* devfreq_event_add_edev() - Add new devfreq-event device.
* @dev : the device owning the devfreq-event device being created
* @desc : the devfreq-event device's decriptor which include essential
* data for devfreq-event device.
*
* Note that this function add new devfreq-event device to devfreq-event class
* list and register the device of the devfreq-event device.
*/
struct devfreq_event_dev *devfreq_event_add_edev(struct device *dev,
struct devfreq_event_desc *desc)
{
struct devfreq_event_dev *edev;
static atomic_t event_no = ATOMIC_INIT(-1);
int ret;
if (!dev || !desc)
return ERR_PTR(-EINVAL);
if (!desc->name || !desc->ops)
return ERR_PTR(-EINVAL);
if (!desc->ops->set_event || !desc->ops->get_event)
return ERR_PTR(-EINVAL);
edev = kzalloc(sizeof(struct devfreq_event_dev), GFP_KERNEL);
if (!edev)
return ERR_PTR(-ENOMEM);
mutex_init(&edev->lock);
edev->desc = desc;
edev->enable_count = 0;
edev->dev.parent = dev;
edev->dev.class = devfreq_event_class;
edev->dev.release = devfreq_event_release_edev;
dev_set_name(&edev->dev, "event%d", atomic_inc_return(&event_no));
ret = device_register(&edev->dev);
if (ret < 0) {
put_device(&edev->dev);
return ERR_PTR(ret);
}
dev_set_drvdata(&edev->dev, edev);
INIT_LIST_HEAD(&edev->node);
mutex_lock(&devfreq_event_list_lock);
list_add(&edev->node, &devfreq_event_list);
mutex_unlock(&devfreq_event_list_lock);
return edev;
}
EXPORT_SYMBOL_GPL(devfreq_event_add_edev);
/**
* devfreq_event_remove_edev() - Remove the devfreq-event device registered.
* @dev : the devfreq-event device
*
* Note that this function remove the registered devfreq-event device.
*/
int devfreq_event_remove_edev(struct devfreq_event_dev *edev)
{
if (!edev)
return -EINVAL;
WARN_ON(edev->enable_count);
mutex_lock(&devfreq_event_list_lock);
list_del(&edev->node);
mutex_unlock(&devfreq_event_list_lock);
device_unregister(&edev->dev);
return 0;
}
EXPORT_SYMBOL_GPL(devfreq_event_remove_edev);
static int devm_devfreq_event_match(struct device *dev, void *res, void *data)
{
struct devfreq_event_dev **r = res;
if (WARN_ON(!r || !*r))
return 0;
return *r == data;
}
static void devm_devfreq_event_release(struct device *dev, void *res)
{
devfreq_event_remove_edev(*(struct devfreq_event_dev **)res);
}
/**
* devm_devfreq_event_add_edev() - Resource-managed devfreq_event_add_edev()
* @dev : the device owning the devfreq-event device being created
* @desc : the devfreq-event device's decriptor which include essential
* data for devfreq-event device.
*
* Note that this function manages automatically the memory of devfreq-event
* device using device resource management and simplify the free operation
* for memory of devfreq-event device.
*/
struct devfreq_event_dev *devm_devfreq_event_add_edev(struct device *dev,
struct devfreq_event_desc *desc)
{
struct devfreq_event_dev **ptr, *edev;
ptr = devres_alloc(devm_devfreq_event_release, sizeof(*ptr),
GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
edev = devfreq_event_add_edev(dev, desc);
if (IS_ERR(edev)) {
devres_free(ptr);
return ERR_PTR(-ENOMEM);
}
*ptr = edev;
devres_add(dev, ptr);
return edev;
}
EXPORT_SYMBOL_GPL(devm_devfreq_event_add_edev);
/**
* devm_devfreq_event_remove_edev()- Resource-managed devfreq_event_remove_edev()
* @dev : the device owning the devfreq-event device being created
* @edev : the devfreq-event device
*
* Note that this function manages automatically the memory of devfreq-event
* device using device resource management.
*/
void devm_devfreq_event_remove_edev(struct device *dev,
struct devfreq_event_dev *edev)
{
WARN_ON(devres_release(dev, devm_devfreq_event_release,
devm_devfreq_event_match, edev));
}
EXPORT_SYMBOL_GPL(devm_devfreq_event_remove_edev);
/*
* Device attributes for devfreq-event class.
*/
static ssize_t name_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct devfreq_event_dev *edev = to_devfreq_event(dev);
if (!edev || !edev->desc)
return -EINVAL;
return sprintf(buf, "%s\n", edev->desc->name);
}
static DEVICE_ATTR_RO(name);
static ssize_t enable_count_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct devfreq_event_dev *edev = to_devfreq_event(dev);
if (!edev || !edev->desc)
return -EINVAL;
return sprintf(buf, "%d\n", edev->enable_count);
}
static DEVICE_ATTR_RO(enable_count);
static struct attribute *devfreq_event_attrs[] = {
&dev_attr_name.attr,
&dev_attr_enable_count.attr,
NULL,
};
ATTRIBUTE_GROUPS(devfreq_event);
static int __init devfreq_event_init(void)
{
devfreq_event_class = class_create(THIS_MODULE, "devfreq-event");
if (IS_ERR(devfreq_event_class)) {
pr_err("%s: couldn't create class\n", __FILE__);
return PTR_ERR(devfreq_event_class);
}
devfreq_event_class->dev_groups = devfreq_event_groups;
return 0;
}
subsys_initcall(devfreq_event_init);
File diff suppressed because it is too large. Load diff
+41
View File
@@ -0,0 +1,41 @@
menuconfig PM_DEVFREQ_EVENT
bool "DEVFREQ-Event device Support"
help
The devfreq-event device provide the raw data and events which
indicate the current state of devfreq-event device. The provided
data from devfreq-event device is used to monitor the state of
device and determine the suitable size of resource to reduce the
wasted resource.
The devfreq-event device can support the various type of events
(e.g., raw data, utilization, latency, bandwidth). The events
may be used by devfreq governor and other subsystem.
if PM_DEVFREQ_EVENT
config DEVFREQ_EVENT_EXYNOS_NOCP
tristate "EXYNOS NoC (Network On Chip) Probe DEVFREQ event Driver"
depends on ARCH_EXYNOS || COMPILE_TEST
select PM_OPP
select REGMAP_MMIO
help
This add the devfreq-event driver for Exynos SoC. It provides NoC
(Network on Chip) Probe counters to measure the bandwidth of AXI bus.
config DEVFREQ_EVENT_EXYNOS_PPMU
tristate "EXYNOS PPMU (Platform Performance Monitoring Unit) DEVFREQ event Driver"
depends on ARCH_EXYNOS || COMPILE_TEST
select PM_OPP
help
This add the devfreq-event driver for Exynos SoC. It provides PPMU
(Platform Performance Monitoring Unit) counters to estimate the
utilization of each module.
config DEVFREQ_EVENT_ROCKCHIP_DFI
tristate "ROCKCHIP DFI DEVFREQ event Driver"
depends on ARCH_ROCKCHIP || COMPILE_TEST
help
This add the devfreq-event driver for Rockchip SoC. It provides DFI
(DDR Monitor Module) driver to count ddr load.
endif # PM_DEVFREQ_EVENT
+5
View File
@@ -0,0 +1,5 @@
# Exynos DEVFREQ Event Drivers
obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_NOCP) += exynos-nocp.o
obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o
obj-$(CONFIG_DEVFREQ_EVENT_ROCKCHIP_DFI) += rockchip-dfi.o
+303
View File
@@ -0,0 +1,303 @@
/*
* exynos-nocp.c - EXYNOS NoC (Network On Chip) Probe support
*
* Copyright (c) 2016 Samsung Electronics Co., Ltd.
* Author : Chanwoo Choi <cw00.choi@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/devfreq-event.h>
#include <linux/kernel.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include "exynos-nocp.h"
struct exynos_nocp {
struct devfreq_event_dev *edev;
struct devfreq_event_desc desc;
struct device *dev;
struct regmap *regmap;
struct clk *clk;
};
/*
* The devfreq-event ops structure for nocp probe.
*/
static int exynos_nocp_set_event(struct devfreq_event_dev *edev)
{
struct exynos_nocp *nocp = devfreq_event_get_drvdata(edev);
int ret;
/* Disable NoC probe */
ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL,
NOCP_MAIN_CTL_STATEN_MASK, 0);
if (ret < 0) {
dev_err(nocp->dev, "failed to disable the NoC probe device\n");
return ret;
}
/* Set a statistics dump period to 0 */
ret = regmap_write(nocp->regmap, NOCP_STAT_PERIOD, 0x0);
if (ret < 0)
goto out;
/* Set the IntEvent fields of *_SRC */
ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_0_SRC,
NOCP_CNT_SRC_INTEVENT_MASK,
NOCP_CNT_SRC_INTEVENT_BYTE_MASK);
if (ret < 0)
goto out;
ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_1_SRC,
NOCP_CNT_SRC_INTEVENT_MASK,
NOCP_CNT_SRC_INTEVENT_CHAIN_MASK);
if (ret < 0)
goto out;
ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_2_SRC,
NOCP_CNT_SRC_INTEVENT_MASK,
NOCP_CNT_SRC_INTEVENT_CYCLE_MASK);
if (ret < 0)
goto out;
ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_3_SRC,
NOCP_CNT_SRC_INTEVENT_MASK,
NOCP_CNT_SRC_INTEVENT_CHAIN_MASK);
if (ret < 0)
goto out;
/* Set an alarm with a max/min value of 0 to generate StatALARM */
ret = regmap_write(nocp->regmap, NOCP_STAT_ALARM_MIN, 0x0);
if (ret < 0)
goto out;
ret = regmap_write(nocp->regmap, NOCP_STAT_ALARM_MAX, 0x0);
if (ret < 0)
goto out;
/* Set AlarmMode */
ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_0_ALARM_MODE,
NOCP_CNT_ALARM_MODE_MASK,
NOCP_CNT_ALARM_MODE_MIN_MAX_MASK);
if (ret < 0)
goto out;
ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_1_ALARM_MODE,
NOCP_CNT_ALARM_MODE_MASK,
NOCP_CNT_ALARM_MODE_MIN_MAX_MASK);
if (ret < 0)
goto out;
ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_2_ALARM_MODE,
NOCP_CNT_ALARM_MODE_MASK,
NOCP_CNT_ALARM_MODE_MIN_MAX_MASK);
if (ret < 0)
goto out;
ret = regmap_update_bits(nocp->regmap, NOCP_COUNTERS_3_ALARM_MODE,
NOCP_CNT_ALARM_MODE_MASK,
NOCP_CNT_ALARM_MODE_MIN_MAX_MASK);
if (ret < 0)
goto out;
/* Enable the measurements by setting AlarmEn and StatEn */
ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL,
NOCP_MAIN_CTL_STATEN_MASK | NOCP_MAIN_CTL_ALARMEN_MASK,
NOCP_MAIN_CTL_STATEN_MASK | NOCP_MAIN_CTL_ALARMEN_MASK);
if (ret < 0)
goto out;
/* Set GlobalEN */
ret = regmap_update_bits(nocp->regmap, NOCP_CFG_CTL,
NOCP_CFG_CTL_GLOBALEN_MASK,
NOCP_CFG_CTL_GLOBALEN_MASK);
if (ret < 0)
goto out;
/* Enable NoC probe */
ret = regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL,
NOCP_MAIN_CTL_STATEN_MASK,
NOCP_MAIN_CTL_STATEN_MASK);
if (ret < 0)
goto out;
return 0;
out:
/* Reset NoC probe */
if (regmap_update_bits(nocp->regmap, NOCP_MAIN_CTL,
NOCP_MAIN_CTL_STATEN_MASK, 0)) {
dev_err(nocp->dev, "Failed to reset NoC probe device\n");
}
return ret;
}
static int exynos_nocp_get_event(struct devfreq_event_dev *edev,
struct devfreq_event_data *edata)
{
struct exynos_nocp *nocp = devfreq_event_get_drvdata(edev);
unsigned int counter[4];
int ret;
/* Read cycle count */
ret = regmap_read(nocp->regmap, NOCP_COUNTERS_0_VAL, &counter[0]);
if (ret < 0)
goto out;
ret = regmap_read(nocp->regmap, NOCP_COUNTERS_1_VAL, &counter[1]);
if (ret < 0)
goto out;
ret = regmap_read(nocp->regmap, NOCP_COUNTERS_2_VAL, &counter[2]);
if (ret < 0)
goto out;
ret = regmap_read(nocp->regmap, NOCP_COUNTERS_3_VAL, &counter[3]);
if (ret < 0)
goto out;
edata->load_count = ((counter[1] << 16) | counter[0]);
edata->total_count = ((counter[3] << 16) | counter[2]);
dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name,
edata->load_count, edata->total_count);
return 0;
out:
dev_err(nocp->dev, "Failed to read the counter of NoC probe device\n");
return ret;
}
static const struct devfreq_event_ops exynos_nocp_ops = {
.set_event = exynos_nocp_set_event,
.get_event = exynos_nocp_get_event,
};
static const struct of_device_id exynos_nocp_id_match[] = {
{ .compatible = "samsung,exynos5420-nocp", },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, exynos_nocp_id_match);
static struct regmap_config exynos_nocp_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.max_register = NOCP_COUNTERS_3_VAL,
};
static int exynos_nocp_parse_dt(struct platform_device *pdev,
struct exynos_nocp *nocp)
{
struct device *dev = nocp->dev;
struct device_node *np = dev->of_node;
struct resource *res;
void __iomem *base;
if (!np) {
dev_err(dev, "failed to find devicetree node\n");
return -EINVAL;
}
nocp->clk = devm_clk_get(dev, "nocp");
if (IS_ERR(nocp->clk))
nocp->clk = NULL;
/* Maps the memory mapped IO to control nocp register */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
exynos_nocp_regmap_config.max_register = resource_size(res) - 4;
nocp->regmap = devm_regmap_init_mmio(dev, base,
&exynos_nocp_regmap_config);
if (IS_ERR(nocp->regmap)) {
dev_err(dev, "failed to initialize regmap\n");
return PTR_ERR(nocp->regmap);
}
return 0;
}
static int exynos_nocp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct exynos_nocp *nocp;
int ret;
nocp = devm_kzalloc(&pdev->dev, sizeof(*nocp), GFP_KERNEL);
if (!nocp)
return -ENOMEM;
nocp->dev = &pdev->dev;
/* Parse dt data to get resource */
ret = exynos_nocp_parse_dt(pdev, nocp);
if (ret < 0) {
dev_err(&pdev->dev,
"failed to parse devicetree for resource\n");
return ret;
}
/* Add devfreq-event device to measure the bandwidth of NoC */
nocp->desc.ops = &exynos_nocp_ops;
nocp->desc.driver_data = nocp;
nocp->desc.name = np->full_name;
nocp->edev = devm_devfreq_event_add_edev(&pdev->dev, &nocp->desc);
if (IS_ERR(nocp->edev)) {
dev_err(&pdev->dev,
"failed to add devfreq-event device\n");
return PTR_ERR(nocp->edev);
}
platform_set_drvdata(pdev, nocp);
ret = clk_prepare_enable(nocp->clk);
if (ret) {
dev_err(&pdev->dev, "failed to prepare ppmu clock\n");
return ret;
}
pr_info("exynos-nocp: new NoC Probe device registered: %s\n",
dev_name(dev));
return 0;
}
static int exynos_nocp_remove(struct platform_device *pdev)
{
struct exynos_nocp *nocp = platform_get_drvdata(pdev);
clk_disable_unprepare(nocp->clk);
return 0;
}
static struct platform_driver exynos_nocp_driver = {
.probe = exynos_nocp_probe,
.remove = exynos_nocp_remove,
.driver = {
.name = "exynos-nocp",
.of_match_table = exynos_nocp_id_match,
},
};
module_platform_driver(exynos_nocp_driver);
MODULE_DESCRIPTION("Exynos NoC (Network on Chip) Probe driver");
MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
MODULE_LICENSE("GPL");
+78
View File
@@ -0,0 +1,78 @@
/*
* exynos-nocp.h - EXYNOS NoC (Network on Chip) Probe header file
*
* Copyright (c) 2016 Samsung Electronics Co., Ltd.
* Author : Chanwoo Choi <cw00.choi@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __EXYNOS_NOCP_H__
#define __EXYNOS_NOCP_H__
enum nocp_reg {
NOCP_ID_REVISION_ID = 0x04,
NOCP_MAIN_CTL = 0x08,
NOCP_CFG_CTL = 0x0C,
NOCP_STAT_PERIOD = 0x24,
NOCP_STAT_GO = 0x28,
NOCP_STAT_ALARM_MIN = 0x2C,
NOCP_STAT_ALARM_MAX = 0x30,
NOCP_STAT_ALARM_STATUS = 0x34,
NOCP_STAT_ALARM_CLR = 0x38,
NOCP_COUNTERS_0_SRC = 0x138,
NOCP_COUNTERS_0_ALARM_MODE = 0x13C,
NOCP_COUNTERS_0_VAL = 0x140,
NOCP_COUNTERS_1_SRC = 0x14C,
NOCP_COUNTERS_1_ALARM_MODE = 0x150,
NOCP_COUNTERS_1_VAL = 0x154,
NOCP_COUNTERS_2_SRC = 0x160,
NOCP_COUNTERS_2_ALARM_MODE = 0x164,
NOCP_COUNTERS_2_VAL = 0x168,
NOCP_COUNTERS_3_SRC = 0x174,
NOCP_COUNTERS_3_ALARM_MODE = 0x178,
NOCP_COUNTERS_3_VAL = 0x17C,
};
/* NOCP_MAIN_CTL register */
#define NOCP_MAIN_CTL_ERREN_MASK BIT(0)
#define NOCP_MAIN_CTL_TRACEEN_MASK BIT(1)
#define NOCP_MAIN_CTL_PAYLOADEN_MASK BIT(2)
#define NOCP_MAIN_CTL_STATEN_MASK BIT(3)
#define NOCP_MAIN_CTL_ALARMEN_MASK BIT(4)
#define NOCP_MAIN_CTL_STATCONDDUMP_MASK BIT(5)
#define NOCP_MAIN_CTL_INTRUSIVEMODE_MASK BIT(6)
/* NOCP_CFG_CTL register */
#define NOCP_CFG_CTL_GLOBALEN_MASK BIT(0)
#define NOCP_CFG_CTL_ACTIVE_MASK BIT(1)
/* NOCP_COUNTERS_x_SRC register */
#define NOCP_CNT_SRC_INTEVENT_SHIFT 0
#define NOCP_CNT_SRC_INTEVENT_MASK (0x1F << NOCP_CNT_SRC_INTEVENT_SHIFT)
#define NOCP_CNT_SRC_INTEVENT_OFF_MASK (0x0 << NOCP_CNT_SRC_INTEVENT_SHIFT)
#define NOCP_CNT_SRC_INTEVENT_CYCLE_MASK (0x1 << NOCP_CNT_SRC_INTEVENT_SHIFT)
#define NOCP_CNT_SRC_INTEVENT_IDLE_MASK (0x2 << NOCP_CNT_SRC_INTEVENT_SHIFT)
#define NOCP_CNT_SRC_INTEVENT_XFER_MASK (0x3 << NOCP_CNT_SRC_INTEVENT_SHIFT)
#define NOCP_CNT_SRC_INTEVENT_BUSY_MASK (0x4 << NOCP_CNT_SRC_INTEVENT_SHIFT)
#define NOCP_CNT_SRC_INTEVENT_WAIT_MASK (0x5 << NOCP_CNT_SRC_INTEVENT_SHIFT)
#define NOCP_CNT_SRC_INTEVENT_PKT_MASK (0x6 << NOCP_CNT_SRC_INTEVENT_SHIFT)
#define NOCP_CNT_SRC_INTEVENT_BYTE_MASK (0x8 << NOCP_CNT_SRC_INTEVENT_SHIFT)
#define NOCP_CNT_SRC_INTEVENT_CHAIN_MASK (0x10 << NOCP_CNT_SRC_INTEVENT_SHIFT)
/* NOCP_COUNTERS_x_ALARM_MODE register */
#define NOCP_CNT_ALARM_MODE_SHIFT 0
#define NOCP_CNT_ALARM_MODE_MASK (0x3 << NOCP_CNT_ALARM_MODE_SHIFT)
#define NOCP_CNT_ALARM_MODE_OFF_MASK (0x0 << NOCP_CNT_ALARM_MODE_SHIFT)
#define NOCP_CNT_ALARM_MODE_MIN_MASK (0x1 << NOCP_CNT_ALARM_MODE_SHIFT)
#define NOCP_CNT_ALARM_MODE_MAX_MASK (0x2 << NOCP_CNT_ALARM_MODE_SHIFT)
#define NOCP_CNT_ALARM_MODE_MIN_MAX_MASK (0x3 << NOCP_CNT_ALARM_MODE_SHIFT)
#endif /* __EXYNOS_NOCP_H__ */
+679
View File
@@ -0,0 +1,679 @@
/*
* exynos_ppmu.c - EXYNOS PPMU (Platform Performance Monitoring Unit) support
*
* Copyright (c) 2014-2015 Samsung Electronics Co., Ltd.
* Author : Chanwoo Choi <cw00.choi@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This driver is based on drivers/devfreq/exynos/exynos_ppmu.c
*/
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/suspend.h>
#include <linux/devfreq-event.h>
#include "exynos-ppmu.h"
struct exynos_ppmu_data {
struct clk *clk;
};
struct exynos_ppmu {
struct devfreq_event_dev **edev;
struct devfreq_event_desc *desc;
unsigned int num_events;
struct device *dev;
struct regmap *regmap;
struct exynos_ppmu_data ppmu;
};
#define PPMU_EVENT(name) \
{ "ppmu-event0-"#name, PPMU_PMNCNT0 }, \
{ "ppmu-event1-"#name, PPMU_PMNCNT1 }, \
{ "ppmu-event2-"#name, PPMU_PMNCNT2 }, \
{ "ppmu-event3-"#name, PPMU_PMNCNT3 }
static struct __exynos_ppmu_events {
char *name;
int id;
} ppmu_events[] = {
/* For Exynos3250, Exynos4 and Exynos5260 */
PPMU_EVENT(g3d),
PPMU_EVENT(fsys),
/* For Exynos4 SoCs and Exynos3250 */
PPMU_EVENT(dmc0),
PPMU_EVENT(dmc1),
PPMU_EVENT(cpu),
PPMU_EVENT(rightbus),
PPMU_EVENT(leftbus),
PPMU_EVENT(lcd0),
PPMU_EVENT(camif),
/* Only for Exynos3250 and Exynos5260 */
PPMU_EVENT(mfc),
/* Only for Exynos4 SoCs */
PPMU_EVENT(mfc-left),
PPMU_EVENT(mfc-right),
/* Only for Exynos5260 SoCs */
PPMU_EVENT(drex0-s0),
PPMU_EVENT(drex0-s1),
PPMU_EVENT(drex1-s0),
PPMU_EVENT(drex1-s1),
PPMU_EVENT(eagle),
PPMU_EVENT(kfc),
PPMU_EVENT(isp),
PPMU_EVENT(fimc),
PPMU_EVENT(gscl),
PPMU_EVENT(mscl),
PPMU_EVENT(fimd0x),
PPMU_EVENT(fimd1x),
/* Only for Exynos5433 SoCs */
PPMU_EVENT(d0-cpu),
PPMU_EVENT(d0-general),
PPMU_EVENT(d0-rt),
PPMU_EVENT(d1-cpu),
PPMU_EVENT(d1-general),
PPMU_EVENT(d1-rt),
};
static int exynos_ppmu_find_ppmu_id(struct devfreq_event_dev *edev)
{
int i;
for (i = 0; i < ARRAY_SIZE(ppmu_events); i++)
if (!strcmp(edev->desc->name, ppmu_events[i].name))
return ppmu_events[i].id;
return -EINVAL;
}
/*
* The devfreq-event ops structure for PPMU v1.1
*/
static int exynos_ppmu_disable(struct devfreq_event_dev *edev)
{
struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
int ret;
u32 pmnc;
/* Disable all counters */
ret = regmap_write(info->regmap, PPMU_CNTENC,
PPMU_CCNT_MASK |
PPMU_PMCNT0_MASK |
PPMU_PMCNT1_MASK |
PPMU_PMCNT2_MASK |
PPMU_PMCNT3_MASK);
if (ret < 0)
return ret;
/* Disable PPMU */
ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);
if (ret < 0)
return ret;
pmnc &= ~PPMU_PMNC_ENABLE_MASK;
ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);
if (ret < 0)
return ret;
return 0;
}
static int exynos_ppmu_set_event(struct devfreq_event_dev *edev)
{
struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
int id = exynos_ppmu_find_ppmu_id(edev);
int ret;
u32 pmnc, cntens;
if (id < 0)
return id;
/* Enable specific counter */
ret = regmap_read(info->regmap, PPMU_CNTENS, &cntens);
if (ret < 0)
return ret;
cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
ret = regmap_write(info->regmap, PPMU_CNTENS, cntens);
if (ret < 0)
return ret;
/* Set the event of Read/Write data count */
ret = regmap_write(info->regmap, PPMU_BEVTxSEL(id),
PPMU_RO_DATA_CNT | PPMU_WO_DATA_CNT);
if (ret < 0)
return ret;
/* Reset cycle counter/performance counter and enable PPMU */
ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);
if (ret < 0)
return ret;
pmnc &= ~(PPMU_PMNC_ENABLE_MASK
| PPMU_PMNC_COUNTER_RESET_MASK
| PPMU_PMNC_CC_RESET_MASK);
pmnc |= (PPMU_ENABLE << PPMU_PMNC_ENABLE_SHIFT);
pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT);
pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT);
ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);
if (ret < 0)
return ret;
return 0;
}
static int exynos_ppmu_get_event(struct devfreq_event_dev *edev,
struct devfreq_event_data *edata)
{
struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
int id = exynos_ppmu_find_ppmu_id(edev);
unsigned int total_count, load_count;
unsigned int pmcnt3_high, pmcnt3_low;
unsigned int pmnc, cntenc;
int ret;
if (id < 0)
return -EINVAL;
/* Disable PPMU */
ret = regmap_read(info->regmap, PPMU_PMNC, &pmnc);
if (ret < 0)
return ret;
pmnc &= ~PPMU_PMNC_ENABLE_MASK;
ret = regmap_write(info->regmap, PPMU_PMNC, pmnc);
if (ret < 0)
return ret;
/* Read cycle count */
ret = regmap_read(info->regmap, PPMU_CCNT, &total_count);
if (ret < 0)
return ret;
edata->total_count = total_count;
/* Read performance count */
switch (id) {
case PPMU_PMNCNT0:
case PPMU_PMNCNT1:
case PPMU_PMNCNT2:
ret = regmap_read(info->regmap, PPMU_PMNCT(id), &load_count);
if (ret < 0)
return ret;
edata->load_count = load_count;
break;
case PPMU_PMNCNT3:
ret = regmap_read(info->regmap, PPMU_PMCNT3_HIGH, &pmcnt3_high);
if (ret < 0)
return ret;
ret = regmap_read(info->regmap, PPMU_PMCNT3_LOW, &pmcnt3_low);
if (ret < 0)
return ret;
edata->load_count = ((pmcnt3_high << 8) | pmcnt3_low);
break;
default:
return -EINVAL;
}
/* Disable specific counter */
ret = regmap_read(info->regmap, PPMU_CNTENC, &cntenc);
if (ret < 0)
return ret;
cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
ret = regmap_write(info->regmap, PPMU_CNTENC, cntenc);
if (ret < 0)
return ret;
dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name,
edata->load_count, edata->total_count);
return 0;
}
static const struct devfreq_event_ops exynos_ppmu_ops = {
.disable = exynos_ppmu_disable,
.set_event = exynos_ppmu_set_event,
.get_event = exynos_ppmu_get_event,
};
/*
* The devfreq-event ops structure for PPMU v2.0
*/
static int exynos_ppmu_v2_disable(struct devfreq_event_dev *edev)
{
struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
int ret;
u32 pmnc, clear;
/* Disable all counters */
clear = (PPMU_CCNT_MASK | PPMU_PMCNT0_MASK | PPMU_PMCNT1_MASK
| PPMU_PMCNT2_MASK | PPMU_PMCNT3_MASK);
ret = regmap_write(info->regmap, PPMU_V2_FLAG, clear);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_INTENC, clear);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_CNTENC, clear);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_CNT_RESET, clear);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG0, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG1, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_CIG_CFG2, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_CIG_RESULT, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_CNT_AUTO, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_CH_EV0_TYPE, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_CH_EV1_TYPE, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_CH_EV2_TYPE, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_CH_EV3_TYPE, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_SM_ID_V, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_SM_ID_A, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_SM_OTHERS_V, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_SM_OTHERS_A, 0x0);
if (ret < 0)
return ret;
ret = regmap_write(info->regmap, PPMU_V2_INTERRUPT_RESET, 0x0);
if (ret < 0)
return ret;
/* Disable PPMU */
ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);
if (ret < 0)
return ret;
pmnc &= ~PPMU_PMNC_ENABLE_MASK;
ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);
if (ret < 0)
return ret;
return 0;
}
static int exynos_ppmu_v2_set_event(struct devfreq_event_dev *edev)
{
struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
unsigned int pmnc, cntens;
int id = exynos_ppmu_find_ppmu_id(edev);
int ret;
/* Enable all counters */
ret = regmap_read(info->regmap, PPMU_V2_CNTENS, &cntens);
if (ret < 0)
return ret;
cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
ret = regmap_write(info->regmap, PPMU_V2_CNTENS, cntens);
if (ret < 0)
return ret;
/* Set the event of Read/Write data count */
switch (id) {
case PPMU_PMNCNT0:
case PPMU_PMNCNT1:
case PPMU_PMNCNT2:
ret = regmap_write(info->regmap, PPMU_V2_CH_EVx_TYPE(id),
PPMU_V2_RO_DATA_CNT | PPMU_V2_WO_DATA_CNT);
if (ret < 0)
return ret;
break;
case PPMU_PMNCNT3:
ret = regmap_write(info->regmap, PPMU_V2_CH_EVx_TYPE(id),
PPMU_V2_EVT3_RW_DATA_CNT);
if (ret < 0)
return ret;
break;
}
/* Reset cycle counter/performance counter and enable PPMU */
ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);
if (ret < 0)
return ret;
pmnc &= ~(PPMU_PMNC_ENABLE_MASK
| PPMU_PMNC_COUNTER_RESET_MASK
| PPMU_PMNC_CC_RESET_MASK
| PPMU_PMNC_CC_DIVIDER_MASK
| PPMU_V2_PMNC_START_MODE_MASK);
pmnc |= (PPMU_ENABLE << PPMU_PMNC_ENABLE_SHIFT);
pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT);
pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT);
pmnc |= (PPMU_V2_MODE_MANUAL << PPMU_V2_PMNC_START_MODE_SHIFT);
ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);
if (ret < 0)
return ret;
return 0;
}
static int exynos_ppmu_v2_get_event(struct devfreq_event_dev *edev,
struct devfreq_event_data *edata)
{
struct exynos_ppmu *info = devfreq_event_get_drvdata(edev);
int id = exynos_ppmu_find_ppmu_id(edev);
int ret;
unsigned int pmnc, cntenc;
unsigned int pmcnt_high, pmcnt_low;
unsigned int total_count, count;
unsigned long load_count = 0;
/* Disable PPMU */
ret = regmap_read(info->regmap, PPMU_V2_PMNC, &pmnc);
if (ret < 0)
return ret;
pmnc &= ~PPMU_PMNC_ENABLE_MASK;
ret = regmap_write(info->regmap, PPMU_V2_PMNC, pmnc);
if (ret < 0)
return ret;
/* Read cycle count and performance count */
ret = regmap_read(info->regmap, PPMU_V2_CCNT, &total_count);
if (ret < 0)
return ret;
edata->total_count = total_count;
switch (id) {
case PPMU_PMNCNT0:
case PPMU_PMNCNT1:
case PPMU_PMNCNT2:
ret = regmap_read(info->regmap, PPMU_V2_PMNCT(id), &count);
if (ret < 0)
return ret;
load_count = count;
break;
case PPMU_PMNCNT3:
ret = regmap_read(info->regmap, PPMU_V2_PMCNT3_HIGH,
&pmcnt_high);
if (ret < 0)
return ret;
ret = regmap_read(info->regmap, PPMU_V2_PMCNT3_LOW, &pmcnt_low);
if (ret < 0)
return ret;
load_count = ((u64)((pmcnt_high & 0xff)) << 32)+ (u64)pmcnt_low;
break;
}
edata->load_count = load_count;
/* Disable all counters */
ret = regmap_read(info->regmap, PPMU_V2_CNTENC, &cntenc);
if (ret < 0)
return 0;
cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id));
ret = regmap_write(info->regmap, PPMU_V2_CNTENC, cntenc);
if (ret < 0)
return ret;
dev_dbg(&edev->dev, "%25s (load: %ld / %ld)\n", edev->desc->name,
edata->load_count, edata->total_count);
return 0;
}
static const struct devfreq_event_ops exynos_ppmu_v2_ops = {
.disable = exynos_ppmu_v2_disable,
.set_event = exynos_ppmu_v2_set_event,
.get_event = exynos_ppmu_v2_get_event,
};
static const struct of_device_id exynos_ppmu_id_match[] = {
{
.compatible = "samsung,exynos-ppmu",
.data = (void *)&exynos_ppmu_ops,
}, {
.compatible = "samsung,exynos-ppmu-v2",
.data = (void *)&exynos_ppmu_v2_ops,
},
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, exynos_ppmu_id_match);
static struct devfreq_event_ops *exynos_bus_get_ops(struct device_node *np)
{
const struct of_device_id *match;
match = of_match_node(exynos_ppmu_id_match, np);
return (struct devfreq_event_ops *)match->data;
}
static int of_get_devfreq_events(struct device_node *np,
struct exynos_ppmu *info)
{
struct devfreq_event_desc *desc;
struct devfreq_event_ops *event_ops;
struct device *dev = info->dev;
struct device_node *events_np, *node;
int i, j, count;
events_np = of_get_child_by_name(np, "events");
if (!events_np) {
dev_err(dev,
"failed to get child node of devfreq-event devices\n");
return -EINVAL;
}
event_ops = exynos_bus_get_ops(np);
count = of_get_child_count(events_np);
desc = devm_kcalloc(dev, count, sizeof(*desc), GFP_KERNEL);
if (!desc)
return -ENOMEM;
info->num_events = count;
j = 0;
for_each_child_of_node(events_np, node) {
for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) {
if (!ppmu_events[i].name)
continue;
if (!of_node_cmp(node->name, ppmu_events[i].name))
break;
}
if (i == ARRAY_SIZE(ppmu_events)) {
dev_warn(dev,
"don't know how to configure events : %s\n",
node->name);
continue;
}
desc[j].ops = event_ops;
desc[j].driver_data = info;
of_property_read_string(node, "event-name", &desc[j].name);
j++;
}
info->desc = desc;
of_node_put(events_np);
return 0;
}
static struct regmap_config exynos_ppmu_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
static int exynos_ppmu_parse_dt(struct platform_device *pdev,
struct exynos_ppmu *info)
{
struct device *dev = info->dev;
struct device_node *np = dev->of_node;
struct resource *res;
void __iomem *base;
int ret = 0;
if (!np) {
dev_err(dev, "failed to find devicetree node\n");
return -EINVAL;
}
/* Maps the memory mapped IO to control PPMU register */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
exynos_ppmu_regmap_config.max_register = resource_size(res) - 4;
info->regmap = devm_regmap_init_mmio(dev, base,
&exynos_ppmu_regmap_config);
if (IS_ERR(info->regmap)) {
dev_err(dev, "failed to initialize regmap\n");
return PTR_ERR(info->regmap);
}
info->ppmu.clk = devm_clk_get(dev, "ppmu");
if (IS_ERR(info->ppmu.clk)) {
info->ppmu.clk = NULL;
dev_warn(dev, "cannot get PPMU clock\n");
}
ret = of_get_devfreq_events(np, info);
if (ret < 0) {
dev_err(dev, "failed to parse exynos ppmu dt node\n");
return ret;
}
return 0;
}
static int exynos_ppmu_probe(struct platform_device *pdev)
{
struct exynos_ppmu *info;
struct devfreq_event_dev **edev;
struct devfreq_event_desc *desc;
int i, ret = 0, size;
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->dev = &pdev->dev;
/* Parse dt data to get resource */
ret = exynos_ppmu_parse_dt(pdev, info);
if (ret < 0) {
dev_err(&pdev->dev,
"failed to parse devicetree for resource\n");
return ret;
}
desc = info->desc;
size = sizeof(struct devfreq_event_dev *) * info->num_events;
info->edev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
if (!info->edev)
return -ENOMEM;
edev = info->edev;
platform_set_drvdata(pdev, info);
for (i = 0; i < info->num_events; i++) {
edev[i] = devm_devfreq_event_add_edev(&pdev->dev, &desc[i]);
if (IS_ERR(edev[i])) {
ret = PTR_ERR(edev[i]);
dev_err(&pdev->dev,
"failed to add devfreq-event device\n");
return PTR_ERR(edev[i]);
}
pr_info("exynos-ppmu: new PPMU device registered %s (%s)\n",
dev_name(&pdev->dev), desc[i].name);
}
ret = clk_prepare_enable(info->ppmu.clk);
if (ret) {
dev_err(&pdev->dev, "failed to prepare ppmu clock\n");
return ret;
}
return 0;
}
static int exynos_ppmu_remove(struct platform_device *pdev)
{
struct exynos_ppmu *info = platform_get_drvdata(pdev);
clk_disable_unprepare(info->ppmu.clk);
return 0;
}
static struct platform_driver exynos_ppmu_driver = {
.probe = exynos_ppmu_probe,
.remove = exynos_ppmu_remove,
.driver = {
.name = "exynos-ppmu",
.of_match_table = exynos_ppmu_id_match,
},
};
module_platform_driver(exynos_ppmu_driver);
MODULE_DESCRIPTION("Exynos PPMU(Platform Performance Monitoring Unit) driver");
MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
MODULE_LICENSE("GPL");
+163
View File
@@ -0,0 +1,163 @@
/*
* exynos_ppmu.h - EXYNOS PPMU header file
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* Author : Chanwoo Choi <cw00.choi@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __EXYNOS_PPMU_H__
#define __EXYNOS_PPMU_H__
enum ppmu_state {
PPMU_DISABLE = 0,
PPMU_ENABLE,
};
enum ppmu_counter {
PPMU_PMNCNT0 = 0,
PPMU_PMNCNT1,
PPMU_PMNCNT2,
PPMU_PMNCNT3,
PPMU_PMNCNT_MAX,
};
/***
* PPMUv1.1 Definitions
*/
enum ppmu_event_type {
PPMU_RO_BUSY_CYCLE_CNT = 0x0,
PPMU_WO_BUSY_CYCLE_CNT = 0x1,
PPMU_RW_BUSY_CYCLE_CNT = 0x2,
PPMU_RO_REQUEST_CNT = 0x3,
PPMU_WO_REQUEST_CNT = 0x4,
PPMU_RO_DATA_CNT = 0x5,
PPMU_WO_DATA_CNT = 0x6,
PPMU_RO_LATENCY = 0x12,
PPMU_WO_LATENCY = 0x16,
};
enum ppmu_reg {
/* PPC control register */
PPMU_PMNC = 0x00,
PPMU_CNTENS = 0x10,
PPMU_CNTENC = 0x20,
PPMU_INTENS = 0x30,
PPMU_INTENC = 0x40,
PPMU_FLAG = 0x50,
/* Cycle Counter and Performance Event Counter Register */
PPMU_CCNT = 0x100,
PPMU_PMCNT0 = 0x110,
PPMU_PMCNT1 = 0x120,
PPMU_PMCNT2 = 0x130,
PPMU_PMCNT3_HIGH = 0x140,
PPMU_PMCNT3_LOW = 0x150,
/* Bus Event Generator */
PPMU_BEVT0SEL = 0x1000,
PPMU_BEVT1SEL = 0x1100,
PPMU_BEVT2SEL = 0x1200,
PPMU_BEVT3SEL = 0x1300,
PPMU_COUNTER_RESET = 0x1810,
PPMU_READ_OVERFLOW_CNT = 0x1810,
PPMU_READ_UNDERFLOW_CNT = 0x1814,
PPMU_WRITE_OVERFLOW_CNT = 0x1850,
PPMU_WRITE_UNDERFLOW_CNT = 0x1854,
PPMU_READ_PENDING_CNT = 0x1880,
PPMU_WRITE_PENDING_CNT = 0x1884
};
/* PMNC register */
#define PPMU_PMNC_CC_RESET_SHIFT 2
#define PPMU_PMNC_COUNTER_RESET_SHIFT 1
#define PPMU_PMNC_ENABLE_SHIFT 0
#define PPMU_PMNC_START_MODE_MASK BIT(16)
#define PPMU_PMNC_CC_DIVIDER_MASK BIT(3)
#define PPMU_PMNC_CC_RESET_MASK BIT(2)
#define PPMU_PMNC_COUNTER_RESET_MASK BIT(1)
#define PPMU_PMNC_ENABLE_MASK BIT(0)
/* CNTENS/CNTENC/INTENS/INTENC/FLAG register */
#define PPMU_CCNT_MASK BIT(31)
#define PPMU_PMCNT3_MASK BIT(3)
#define PPMU_PMCNT2_MASK BIT(2)
#define PPMU_PMCNT1_MASK BIT(1)
#define PPMU_PMCNT0_MASK BIT(0)
/* PPMU_PMNCTx/PPMU_BETxSEL registers */
#define PPMU_PMNCT(x) (PPMU_PMCNT0 + (0x10 * x))
#define PPMU_BEVTxSEL(x) (PPMU_BEVT0SEL + (0x100 * x))
/***
* PPMU_V2.0 definitions
*/
enum ppmu_v2_mode {
PPMU_V2_MODE_MANUAL = 0,
PPMU_V2_MODE_AUTO = 1,
PPMU_V2_MODE_CIG = 2, /* CIG (Conditional Interrupt Generation) */
};
enum ppmu_v2_event_type {
PPMU_V2_RO_DATA_CNT = 0x4,
PPMU_V2_WO_DATA_CNT = 0x5,
PPMU_V2_EVT3_RW_DATA_CNT = 0x22, /* Only for Event3 */
};
enum ppmu_V2_reg {
/* PPC control register */
PPMU_V2_PMNC = 0x04,
PPMU_V2_CNTENS = 0x08,
PPMU_V2_CNTENC = 0x0c,
PPMU_V2_INTENS = 0x10,
PPMU_V2_INTENC = 0x14,
PPMU_V2_FLAG = 0x18,
/* Cycle Counter and Performance Event Counter Register */
PPMU_V2_CCNT = 0x48,
PPMU_V2_PMCNT0 = 0x34,
PPMU_V2_PMCNT1 = 0x38,
PPMU_V2_PMCNT2 = 0x3c,
PPMU_V2_PMCNT3_LOW = 0x40,
PPMU_V2_PMCNT3_HIGH = 0x44,
/* Bus Event Generator */
PPMU_V2_CIG_CFG0 = 0x1c,
PPMU_V2_CIG_CFG1 = 0x20,
PPMU_V2_CIG_CFG2 = 0x24,
PPMU_V2_CIG_RESULT = 0x28,
PPMU_V2_CNT_RESET = 0x2c,
PPMU_V2_CNT_AUTO = 0x30,
PPMU_V2_CH_EV0_TYPE = 0x200,
PPMU_V2_CH_EV1_TYPE = 0x204,
PPMU_V2_CH_EV2_TYPE = 0x208,
PPMU_V2_CH_EV3_TYPE = 0x20c,
PPMU_V2_SM_ID_V = 0x220,
PPMU_V2_SM_ID_A = 0x224,
PPMU_V2_SM_OTHERS_V = 0x228,
PPMU_V2_SM_OTHERS_A = 0x22c,
PPMU_V2_INTERRUPT_RESET = 0x260,
};
/* PMNC register */
#define PPMU_V2_PMNC_START_MODE_SHIFT 20
#define PPMU_V2_PMNC_START_MODE_MASK (0x3 << PPMU_V2_PMNC_START_MODE_SHIFT)
#define PPMU_PMNC_CC_RESET_SHIFT 2
#define PPMU_PMNC_COUNTER_RESET_SHIFT 1
#define PPMU_PMNC_ENABLE_SHIFT 0
#define PPMU_PMNC_START_MODE_MASK BIT(16)
#define PPMU_PMNC_CC_DIVIDER_MASK BIT(3)
#define PPMU_PMNC_CC_RESET_MASK BIT(2)
#define PPMU_PMNC_COUNTER_RESET_MASK BIT(1)
#define PPMU_PMNC_ENABLE_MASK BIT(0)
#define PPMU_V2_PMNCT(x) (PPMU_V2_PMCNT0 + (0x4 * x))
#define PPMU_V2_CH_EVx_TYPE(x) (PPMU_V2_CH_EV0_TYPE + (0x4 * x))
#endif /* __EXYNOS_PPMU_H__ */
+257
View File
@@ -0,0 +1,257 @@
/*
* Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
* Author: Lin Huang <hl@rock-chips.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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/clk.h>
#include <linux/devfreq-event.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/of.h>
#define RK3399_DMC_NUM_CH 2
/* DDRMON_CTRL */
#define DDRMON_CTRL 0x04
#define CLR_DDRMON_CTRL (0x1f0000 << 0)
#define LPDDR4_EN (0x10001 << 4)
#define HARDWARE_EN (0x10001 << 3)
#define LPDDR3_EN (0x10001 << 2)
#define SOFTWARE_EN (0x10001 << 1)
#define SOFTWARE_DIS (0x10000 << 1)
#define TIME_CNT_EN (0x10001 << 0)
#define DDRMON_CH0_COUNT_NUM 0x28
#define DDRMON_CH0_DFI_ACCESS_NUM 0x2c
#define DDRMON_CH1_COUNT_NUM 0x3c
#define DDRMON_CH1_DFI_ACCESS_NUM 0x40
/* pmu grf */
#define PMUGRF_OS_REG2 0x308
#define DDRTYPE_SHIFT 13
#define DDRTYPE_MASK 7
enum {
DDR3 = 3,
LPDDR3 = 6,
LPDDR4 = 7,
UNUSED = 0xFF
};
struct dmc_usage {
u32 access;
u32 total;
};
/*
* The dfi controller can monitor DDR load. It has an upper and lower threshold
* for the operating points. Whenever the usage leaves these bounds an event is
* generated to indicate the DDR frequency should be changed.
*/
struct rockchip_dfi {
struct devfreq_event_dev *edev;
struct devfreq_event_desc *desc;
struct dmc_usage ch_usage[RK3399_DMC_NUM_CH];
struct device *dev;
void __iomem *regs;
struct regmap *regmap_pmu;
struct clk *clk;
};
static void rockchip_dfi_start_hardware_counter(struct devfreq_event_dev *edev)
{
struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
void __iomem *dfi_regs = info->regs;
u32 val;
u32 ddr_type;
/* get ddr type */
regmap_read(info->regmap_pmu, PMUGRF_OS_REG2, &val);
ddr_type = (val >> DDRTYPE_SHIFT) & DDRTYPE_MASK;
/* clear DDRMON_CTRL setting */
writel_relaxed(CLR_DDRMON_CTRL, dfi_regs + DDRMON_CTRL);
/* set ddr type to dfi */
if (ddr_type == LPDDR3)
writel_relaxed(LPDDR3_EN, dfi_regs + DDRMON_CTRL);
else if (ddr_type == LPDDR4)
writel_relaxed(LPDDR4_EN, dfi_regs + DDRMON_CTRL);
/* enable count, use software mode */
writel_relaxed(SOFTWARE_EN, dfi_regs + DDRMON_CTRL);
}
static void rockchip_dfi_stop_hardware_counter(struct devfreq_event_dev *edev)
{
struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
void __iomem *dfi_regs = info->regs;
writel_relaxed(SOFTWARE_DIS, dfi_regs + DDRMON_CTRL);
}
static int rockchip_dfi_get_busier_ch(struct devfreq_event_dev *edev)
{
struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
u32 tmp, max = 0;
u32 i, busier_ch = 0;
void __iomem *dfi_regs = info->regs;
rockchip_dfi_stop_hardware_counter(edev);
/* Find out which channel is busier */
for (i = 0; i < RK3399_DMC_NUM_CH; i++) {
info->ch_usage[i].access = readl_relaxed(dfi_regs +
DDRMON_CH0_DFI_ACCESS_NUM + i * 20) * 4;
info->ch_usage[i].total = readl_relaxed(dfi_regs +
DDRMON_CH0_COUNT_NUM + i * 20);
tmp = info->ch_usage[i].access;
if (tmp > max) {
busier_ch = i;
max = tmp;
}
}
rockchip_dfi_start_hardware_counter(edev);
return busier_ch;
}
static int rockchip_dfi_disable(struct devfreq_event_dev *edev)
{
struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
rockchip_dfi_stop_hardware_counter(edev);
clk_disable_unprepare(info->clk);
return 0;
}
static int rockchip_dfi_enable(struct devfreq_event_dev *edev)
{
struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
int ret;
ret = clk_prepare_enable(info->clk);
if (ret) {
dev_err(&edev->dev, "failed to enable dfi clk: %d\n", ret);
return ret;
}
rockchip_dfi_start_hardware_counter(edev);
return 0;
}
static int rockchip_dfi_set_event(struct devfreq_event_dev *edev)
{
return 0;
}
static int rockchip_dfi_get_event(struct devfreq_event_dev *edev,
struct devfreq_event_data *edata)
{
struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
int busier_ch;
busier_ch = rockchip_dfi_get_busier_ch(edev);
edata->load_count = info->ch_usage[busier_ch].access;
edata->total_count = info->ch_usage[busier_ch].total;
return 0;
}
static const struct devfreq_event_ops rockchip_dfi_ops = {
.disable = rockchip_dfi_disable,
.enable = rockchip_dfi_enable,
.get_event = rockchip_dfi_get_event,
.set_event = rockchip_dfi_set_event,
};
static const struct of_device_id rockchip_dfi_id_match[] = {
{ .compatible = "rockchip,rk3399-dfi" },
{ },
};
MODULE_DEVICE_TABLE(of, rockchip_dfi_id_match);
static int rockchip_dfi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rockchip_dfi *data;
struct resource *res;
struct devfreq_event_desc *desc;
struct device_node *np = pdev->dev.of_node, *node;
data = devm_kzalloc(dev, sizeof(struct rockchip_dfi), GFP_KERNEL);
if (!data)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
data->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(data->regs))
return PTR_ERR(data->regs);
data->clk = devm_clk_get(dev, "pclk_ddr_mon");
if (IS_ERR(data->clk)) {
dev_err(dev, "Cannot get the clk dmc_clk\n");
return PTR_ERR(data->clk);
};
/* try to find the optional reference to the pmu syscon */
node = of_parse_phandle(np, "rockchip,pmu", 0);
if (node) {
data->regmap_pmu = syscon_node_to_regmap(node);
if (IS_ERR(data->regmap_pmu))
return PTR_ERR(data->regmap_pmu);
}
data->dev = dev;
desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
if (!desc)
return -ENOMEM;
desc->ops = &rockchip_dfi_ops;
desc->driver_data = data;
desc->name = np->name;
data->desc = desc;
data->edev = devm_devfreq_event_add_edev(&pdev->dev, desc);
if (IS_ERR(data->edev)) {
dev_err(&pdev->dev,
"failed to add devfreq-event device\n");
return PTR_ERR(data->edev);
}
platform_set_drvdata(pdev, data);
return 0;
}
static struct platform_driver rockchip_dfi_driver = {
.probe = rockchip_dfi_probe,
.driver = {
.name = "rockchip-dfi",
.of_match_table = rockchip_dfi_id_match,
},
};
module_platform_driver(rockchip_dfi_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Lin Huang <hl@rock-chips.com>");
MODULE_DESCRIPTION("Rockchip DFI driver");
+572
View File
@@ -0,0 +1,572 @@
/*
* Generic Exynos Bus frequency driver with DEVFREQ Framework
*
* Copyright (c) 2016 Samsung Electronics Co., Ltd.
* Author : Chanwoo Choi <cw00.choi@samsung.com>
*
* This driver support Exynos Bus frequency feature by using
* DEVFREQ framework and is based on drivers/devfreq/exynos/exynos4_bus.c.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/devfreq.h>
#include <linux/devfreq-event.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/pm_opp.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#define DEFAULT_SATURATION_RATIO 40
#define DEFAULT_VOLTAGE_TOLERANCE 2
struct exynos_bus {
struct device *dev;
struct devfreq *devfreq;
struct devfreq_event_dev **edev;
unsigned int edev_count;
struct mutex lock;
unsigned long curr_freq;
struct regulator *regulator;
struct clk *clk;
unsigned int voltage_tolerance;
unsigned int ratio;
};
/*
* Control the devfreq-event device to get the current state of bus
*/
#define exynos_bus_ops_edev(ops) \
static int exynos_bus_##ops(struct exynos_bus *bus) \
{ \
int i, ret; \
\
for (i = 0; i < bus->edev_count; i++) { \
if (!bus->edev[i]) \
continue; \
ret = devfreq_event_##ops(bus->edev[i]); \
if (ret < 0) \
return ret; \
} \
\
return 0; \
}
exynos_bus_ops_edev(enable_edev);
exynos_bus_ops_edev(disable_edev);
exynos_bus_ops_edev(set_event);
static int exynos_bus_get_event(struct exynos_bus *bus,
struct devfreq_event_data *edata)
{
struct devfreq_event_data event_data;
unsigned long load_count = 0, total_count = 0;
int i, ret = 0;
for (i = 0; i < bus->edev_count; i++) {
if (!bus->edev[i])
continue;
ret = devfreq_event_get_event(bus->edev[i], &event_data);
if (ret < 0)
return ret;
if (i == 0 || event_data.load_count > load_count) {
load_count = event_data.load_count;
total_count = event_data.total_count;
}
}
edata->load_count = load_count;
edata->total_count = total_count;
return ret;
}
/*
* Must necessary function for devfreq simple-ondemand governor
*/
static int exynos_bus_target(struct device *dev, unsigned long *freq, u32 flags)
{
struct exynos_bus *bus = dev_get_drvdata(dev);
struct dev_pm_opp *new_opp;
unsigned long old_freq, new_freq, new_volt, tol;
int ret = 0;
/* Get new opp-bus instance according to new bus clock */
new_opp = devfreq_recommended_opp(dev, freq, flags);
if (IS_ERR(new_opp)) {
dev_err(dev, "failed to get recommended opp instance\n");
return PTR_ERR(new_opp);
}
new_freq = dev_pm_opp_get_freq(new_opp);
new_volt = dev_pm_opp_get_voltage(new_opp);
dev_pm_opp_put(new_opp);
old_freq = bus->curr_freq;
if (old_freq == new_freq)
return 0;
tol = new_volt * bus->voltage_tolerance / 100;
/* Change voltage and frequency according to new OPP level */
mutex_lock(&bus->lock);
if (old_freq < new_freq) {
ret = regulator_set_voltage_tol(bus->regulator, new_volt, tol);
if (ret < 0) {
dev_err(bus->dev, "failed to set voltage\n");
goto out;
}
}
ret = clk_set_rate(bus->clk, new_freq);
if (ret < 0) {
dev_err(dev, "failed to change clock of bus\n");
clk_set_rate(bus->clk, old_freq);
goto out;
}
if (old_freq > new_freq) {
ret = regulator_set_voltage_tol(bus->regulator, new_volt, tol);
if (ret < 0) {
dev_err(bus->dev, "failed to set voltage\n");
goto out;
}
}
bus->curr_freq = new_freq;
dev_dbg(dev, "Set the frequency of bus (%luHz -> %luHz, %luHz)\n",
old_freq, new_freq, clk_get_rate(bus->clk));
out:
mutex_unlock(&bus->lock);
return ret;
}
static int exynos_bus_get_dev_status(struct device *dev,
struct devfreq_dev_status *stat)
{
struct exynos_bus *bus = dev_get_drvdata(dev);
struct devfreq_event_data edata;
int ret;
stat->current_frequency = bus->curr_freq;
ret = exynos_bus_get_event(bus, &edata);
if (ret < 0) {
stat->total_time = stat->busy_time = 0;
goto err;
}
stat->busy_time = (edata.load_count * 100) / bus->ratio;
stat->total_time = edata.total_count;
dev_dbg(dev, "Usage of devfreq-event : %lu/%lu\n", stat->busy_time,
stat->total_time);
err:
ret = exynos_bus_set_event(bus);
if (ret < 0) {
dev_err(dev, "failed to set event to devfreq-event devices\n");
return ret;
}
return ret;
}
static void exynos_bus_exit(struct device *dev)
{
struct exynos_bus *bus = dev_get_drvdata(dev);
int ret;
ret = exynos_bus_disable_edev(bus);
if (ret < 0)
dev_warn(dev, "failed to disable the devfreq-event devices\n");
dev_pm_opp_of_remove_table(dev);
clk_disable_unprepare(bus->clk);
if (bus->regulator)
regulator_disable(bus->regulator);
}
/*
* Must necessary function for devfreq passive governor
*/
static int exynos_bus_passive_target(struct device *dev, unsigned long *freq,
u32 flags)
{
struct exynos_bus *bus = dev_get_drvdata(dev);
struct dev_pm_opp *new_opp;
unsigned long old_freq, new_freq;
int ret = 0;
/* Get new opp-bus instance according to new bus clock */
new_opp = devfreq_recommended_opp(dev, freq, flags);
if (IS_ERR(new_opp)) {
dev_err(dev, "failed to get recommended opp instance\n");
return PTR_ERR(new_opp);
}
new_freq = dev_pm_opp_get_freq(new_opp);
dev_pm_opp_put(new_opp);
old_freq = bus->curr_freq;
if (old_freq == new_freq)
return 0;
/* Change the frequency according to new OPP level */
mutex_lock(&bus->lock);
ret = clk_set_rate(bus->clk, new_freq);
if (ret < 0) {
dev_err(dev, "failed to set the clock of bus\n");
goto out;
}
*freq = new_freq;
bus->curr_freq = new_freq;
dev_dbg(dev, "Set the frequency of bus (%luHz -> %luHz, %luHz)\n",
old_freq, new_freq, clk_get_rate(bus->clk));
out:
mutex_unlock(&bus->lock);
return ret;
}
static void exynos_bus_passive_exit(struct device *dev)
{
struct exynos_bus *bus = dev_get_drvdata(dev);
dev_pm_opp_of_remove_table(dev);
clk_disable_unprepare(bus->clk);
}
static int exynos_bus_parent_parse_of(struct device_node *np,
struct exynos_bus *bus)
{
struct device *dev = bus->dev;
int i, ret, count, size;
/* Get the regulator to provide each bus with the power */
bus->regulator = devm_regulator_get(dev, "vdd");
if (IS_ERR(bus->regulator)) {
dev_err(dev, "failed to get VDD regulator\n");
return PTR_ERR(bus->regulator);
}
ret = regulator_enable(bus->regulator);
if (ret < 0) {
dev_err(dev, "failed to enable VDD regulator\n");
return ret;
}
/*
* Get the devfreq-event devices to get the current utilization of
* buses. This raw data will be used in devfreq ondemand governor.
*/
count = devfreq_event_get_edev_count(dev);
if (count < 0) {
dev_err(dev, "failed to get the count of devfreq-event dev\n");
ret = count;
goto err_regulator;
}
bus->edev_count = count;
size = sizeof(*bus->edev) * count;
bus->edev = devm_kzalloc(dev, size, GFP_KERNEL);
if (!bus->edev) {
ret = -ENOMEM;
goto err_regulator;
}
for (i = 0; i < count; i++) {
bus->edev[i] = devfreq_event_get_edev_by_phandle(dev, i);
if (IS_ERR(bus->edev[i])) {
ret = -EPROBE_DEFER;
goto err_regulator;
}
}
/*
* Optionally, Get the saturation ratio according to Exynos SoC
* When measuring the utilization of each AXI bus with devfreq-event
* devices, the measured real cycle might be much lower than the
* total cycle of bus during sampling rate. In result, the devfreq
* simple-ondemand governor might not decide to change the current
* frequency due to too utilization (= real cycle/total cycle).
* So, this property is used to adjust the utilization when calculating
* the busy_time in exynos_bus_get_dev_status().
*/
if (of_property_read_u32(np, "exynos,saturation-ratio", &bus->ratio))
bus->ratio = DEFAULT_SATURATION_RATIO;
if (of_property_read_u32(np, "exynos,voltage-tolerance",
&bus->voltage_tolerance))
bus->voltage_tolerance = DEFAULT_VOLTAGE_TOLERANCE;
return 0;
err_regulator:
regulator_disable(bus->regulator);
return ret;
}
static int exynos_bus_parse_of(struct device_node *np,
struct exynos_bus *bus)
{
struct device *dev = bus->dev;
struct dev_pm_opp *opp;
unsigned long rate;
int ret;
/* Get the clock to provide each bus with source clock */
bus->clk = devm_clk_get(dev, "bus");
if (IS_ERR(bus->clk)) {
dev_err(dev, "failed to get bus clock\n");
return PTR_ERR(bus->clk);
}
ret = clk_prepare_enable(bus->clk);
if (ret < 0) {
dev_err(dev, "failed to get enable clock\n");
return ret;
}
/* Get the freq and voltage from OPP table to scale the bus freq */
ret = dev_pm_opp_of_add_table(dev);
if (ret < 0) {
dev_err(dev, "failed to get OPP table\n");
goto err_clk;
}
rate = clk_get_rate(bus->clk);
opp = devfreq_recommended_opp(dev, &rate, 0);
if (IS_ERR(opp)) {
dev_err(dev, "failed to find dev_pm_opp\n");
ret = PTR_ERR(opp);
goto err_opp;
}
bus->curr_freq = dev_pm_opp_get_freq(opp);
dev_pm_opp_put(opp);
return 0;
err_opp:
dev_pm_opp_of_remove_table(dev);
err_clk:
clk_disable_unprepare(bus->clk);
return ret;
}
static int exynos_bus_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node, *node;
struct devfreq_dev_profile *profile;
struct devfreq_simple_ondemand_data *ondemand_data;
struct devfreq_passive_data *passive_data;
struct devfreq *parent_devfreq;
struct exynos_bus *bus;
int ret, max_state;
unsigned long min_freq, max_freq;
bool passive = false;
if (!np) {
dev_err(dev, "failed to find devicetree node\n");
return -EINVAL;
}
bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
if (!bus)
return -ENOMEM;
mutex_init(&bus->lock);
bus->dev = &pdev->dev;
platform_set_drvdata(pdev, bus);
profile = devm_kzalloc(dev, sizeof(*profile), GFP_KERNEL);
if (!profile)
return -ENOMEM;
node = of_parse_phandle(dev->of_node, "devfreq", 0);
if (node) {
of_node_put(node);
passive = true;
} else {
ret = exynos_bus_parent_parse_of(np, bus);
if (ret < 0)
return ret;
}
/* Parse the device-tree to get the resource information */
ret = exynos_bus_parse_of(np, bus);
if (ret < 0)
goto err_reg;
if (passive)
goto passive;
/* Initialize the struct profile and governor data for parent device */
profile->polling_ms = 50;
profile->target = exynos_bus_target;
profile->get_dev_status = exynos_bus_get_dev_status;
profile->exit = exynos_bus_exit;
ondemand_data = devm_kzalloc(dev, sizeof(*ondemand_data), GFP_KERNEL);
if (!ondemand_data) {
ret = -ENOMEM;
goto err;
}
ondemand_data->upthreshold = 40;
ondemand_data->downdifferential = 5;
/* Add devfreq device to monitor and handle the exynos bus */
bus->devfreq = devm_devfreq_add_device(dev, profile,
DEVFREQ_GOV_SIMPLE_ONDEMAND,
ondemand_data);
if (IS_ERR(bus->devfreq)) {
dev_err(dev, "failed to add devfreq device\n");
ret = PTR_ERR(bus->devfreq);
goto err;
}
/* Register opp_notifier to catch the change of OPP */
ret = devm_devfreq_register_opp_notifier(dev, bus->devfreq);
if (ret < 0) {
dev_err(dev, "failed to register opp notifier\n");
goto err;
}
/*
* Enable devfreq-event to get raw data which is used to determine
* current bus load.
*/
ret = exynos_bus_enable_edev(bus);
if (ret < 0) {
dev_err(dev, "failed to enable devfreq-event devices\n");
goto err;
}
ret = exynos_bus_set_event(bus);
if (ret < 0) {
dev_err(dev, "failed to set event to devfreq-event devices\n");
goto err;
}
goto out;
passive:
/* Initialize the struct profile and governor data for passive device */
profile->target = exynos_bus_passive_target;
profile->exit = exynos_bus_passive_exit;
/* Get the instance of parent devfreq device */
parent_devfreq = devfreq_get_devfreq_by_phandle(dev, 0);
if (IS_ERR(parent_devfreq)) {
ret = -EPROBE_DEFER;
goto err;
}
passive_data = devm_kzalloc(dev, sizeof(*passive_data), GFP_KERNEL);
if (!passive_data) {
ret = -ENOMEM;
goto err;
}
passive_data->parent = parent_devfreq;
/* Add devfreq device for exynos bus with passive governor */
bus->devfreq = devm_devfreq_add_device(dev, profile, DEVFREQ_GOV_PASSIVE,
passive_data);
if (IS_ERR(bus->devfreq)) {
dev_err(dev,
"failed to add devfreq dev with passive governor\n");
ret = PTR_ERR(bus->devfreq);
goto err;
}
out:
max_state = bus->devfreq->profile->max_state;
min_freq = (bus->devfreq->profile->freq_table[0] / 1000);
max_freq = (bus->devfreq->profile->freq_table[max_state - 1] / 1000);
pr_info("exynos-bus: new bus device registered: %s (%6ld KHz ~ %6ld KHz)\n",
dev_name(dev), min_freq, max_freq);
return 0;
err:
dev_pm_opp_of_remove_table(dev);
clk_disable_unprepare(bus->clk);
err_reg:
if (!passive)
regulator_disable(bus->regulator);
return ret;
}
#ifdef CONFIG_PM_SLEEP
static int exynos_bus_resume(struct device *dev)
{
struct exynos_bus *bus = dev_get_drvdata(dev);
int ret;
ret = exynos_bus_enable_edev(bus);
if (ret < 0) {
dev_err(dev, "failed to enable the devfreq-event devices\n");
return ret;
}
return 0;
}
static int exynos_bus_suspend(struct device *dev)
{
struct exynos_bus *bus = dev_get_drvdata(dev);
int ret;
ret = exynos_bus_disable_edev(bus);
if (ret < 0) {
dev_err(dev, "failed to disable the devfreq-event devices\n");
return ret;
}
return 0;
}
#endif
static const struct dev_pm_ops exynos_bus_pm = {
SET_SYSTEM_SLEEP_PM_OPS(exynos_bus_suspend, exynos_bus_resume)
};
static const struct of_device_id exynos_bus_of_match[] = {
{ .compatible = "samsung,exynos-bus", },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, exynos_bus_of_match);
static struct platform_driver exynos_bus_platdrv = {
.probe = exynos_bus_probe,
.driver = {
.name = "exynos-bus",
.pm = &exynos_bus_pm,
.of_match_table = of_match_ptr(exynos_bus_of_match),
},
};
module_platform_driver(exynos_bus_platdrv);
MODULE_DESCRIPTION("Generic Exynos Bus frequency driver");
MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
MODULE_LICENSE("GPL v2");
+76
View File
@@ -0,0 +1,76 @@
/*
* governor.h - internal header for devfreq governors.
*
* Copyright (C) 2011 Samsung Electronics
* MyungJoo Ham <myungjoo.ham@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This header is for devfreq governors in drivers/devfreq/
*/
#ifndef _GOVERNOR_H
#define _GOVERNOR_H
#include <linux/devfreq.h>
#define to_devfreq(DEV) container_of((DEV), struct devfreq, dev)
/* Devfreq events */
#define DEVFREQ_GOV_START 0x1
#define DEVFREQ_GOV_STOP 0x2
#define DEVFREQ_GOV_INTERVAL 0x3
#define DEVFREQ_GOV_SUSPEND 0x4
#define DEVFREQ_GOV_RESUME 0x5
/**
* struct devfreq_governor - Devfreq policy governor
* @node: list node - contains registered devfreq governors
* @name: Governor's name
* @immutable: Immutable flag for governor. If the value is 1,
* this govenror is never changeable to other governor.
* @get_target_freq: Returns desired operating frequency for the device.
* Basically, get_target_freq will run
* devfreq_dev_profile.get_dev_status() to get the
* status of the device (load = busy_time / total_time).
* If no_central_polling is set, this callback is called
* only with update_devfreq() notified by OPP.
* @event_handler: Callback for devfreq core framework to notify events
* to governors. Events include per device governor
* init and exit, opp changes out of devfreq, suspend
* and resume of per device devfreq during device idle.
*
* Note that the callbacks are called with devfreq->lock locked by devfreq.
*/
struct devfreq_governor {
struct list_head node;
const char name[DEVFREQ_NAME_LEN];
const unsigned int immutable;
int (*get_target_freq)(struct devfreq *this, unsigned long *freq);
int (*event_handler)(struct devfreq *devfreq,
unsigned int event, void *data);
};
/* Caution: devfreq->lock must be locked before calling update_devfreq */
extern int update_devfreq(struct devfreq *devfreq);
extern void devfreq_monitor_start(struct devfreq *devfreq);
extern void devfreq_monitor_stop(struct devfreq *devfreq);
extern void devfreq_monitor_suspend(struct devfreq *devfreq);
extern void devfreq_monitor_resume(struct devfreq *devfreq);
extern void devfreq_interval_update(struct devfreq *devfreq,
unsigned int *delay);
extern int devfreq_add_governor(struct devfreq_governor *governor);
extern int devfreq_remove_governor(struct devfreq_governor *governor);
extern int devfreq_update_status(struct devfreq *devfreq, unsigned long freq);
static inline int devfreq_update_stats(struct devfreq *df)
{
return df->profile->get_dev_status(df->dev.parent, &df->last_status);
}
#endif /* _GOVERNOR_H */
+210
View File
@@ -0,0 +1,210 @@
/*
* linux/drivers/devfreq/governor_passive.c
*
* Copyright (C) 2016 Samsung Electronics
* Author: Chanwoo Choi <cw00.choi@samsung.com>
* Author: MyungJoo Ham <myungjoo.ham@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/devfreq.h>
#include "governor.h"
static int devfreq_passive_get_target_freq(struct devfreq *devfreq,
unsigned long *freq)
{
struct devfreq_passive_data *p_data
= (struct devfreq_passive_data *)devfreq->data;
struct devfreq *parent_devfreq = (struct devfreq *)p_data->parent;
unsigned long child_freq = ULONG_MAX;
struct dev_pm_opp *opp;
int i, count, ret = 0;
/*
* If the devfreq device with passive governor has the specific method
* to determine the next frequency, should use the get_target_freq()
* of struct devfreq_passive_data.
*/
if (p_data->get_target_freq) {
ret = p_data->get_target_freq(devfreq, freq);
goto out;
}
/*
* If the parent and passive devfreq device uses the OPP table,
* get the next frequency by using the OPP table.
*/
/*
* - parent devfreq device uses the governors except for passive.
* - passive devfreq device uses the passive governor.
*
* Each devfreq has the OPP table. After deciding the new frequency
* from the governor of parent devfreq device, the passive governor
* need to get the index of new frequency on OPP table of parent
* device. And then the index is used for getting the suitable
* new frequency for passive devfreq device.
*/
if (!devfreq->profile || !devfreq->profile->freq_table
|| devfreq->profile->max_state <= 0)
return -EINVAL;
/*
* The passive governor have to get the correct frequency from OPP
* list of parent device. Because in this case, *freq is temporary
* value which is decided by ondemand governor.
*/
opp = devfreq_recommended_opp(parent_devfreq->dev.parent, freq, 0);
if (IS_ERR(opp)) {
ret = PTR_ERR(opp);
goto out;
}
dev_pm_opp_put(opp);
/*
* Get the OPP table's index of decided freqeuncy by governor
* of parent device.
*/
for (i = 0; i < parent_devfreq->profile->max_state; i++)
if (parent_devfreq->profile->freq_table[i] == *freq)
break;
if (i == parent_devfreq->profile->max_state) {
ret = -EINVAL;
goto out;
}
/* Get the suitable frequency by using index of parent device. */
if (i < devfreq->profile->max_state) {
child_freq = devfreq->profile->freq_table[i];
} else {
count = devfreq->profile->max_state;
child_freq = devfreq->profile->freq_table[count - 1];
}
/* Return the suitable frequency for passive device. */
*freq = child_freq;
out:
return ret;
}
static int update_devfreq_passive(struct devfreq *devfreq, unsigned long freq)
{
int ret;
if (!devfreq->governor)
return -EINVAL;
mutex_lock_nested(&devfreq->lock, SINGLE_DEPTH_NESTING);
ret = devfreq->governor->get_target_freq(devfreq, &freq);
if (ret < 0)
goto out;
ret = devfreq->profile->target(devfreq->dev.parent, &freq, 0);
if (ret < 0)
goto out;
if (devfreq->profile->freq_table
&& (devfreq_update_status(devfreq, freq)))
dev_err(&devfreq->dev,
"Couldn't update frequency transition information.\n");
devfreq->previous_freq = freq;
out:
mutex_unlock(&devfreq->lock);
return 0;
}
static int devfreq_passive_notifier_call(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct devfreq_passive_data *data
= container_of(nb, struct devfreq_passive_data, nb);
struct devfreq *devfreq = (struct devfreq *)data->this;
struct devfreq *parent = (struct devfreq *)data->parent;
struct devfreq_freqs *freqs = (struct devfreq_freqs *)ptr;
unsigned long freq = freqs->new;
switch (event) {
case DEVFREQ_PRECHANGE:
if (parent->previous_freq > freq)
update_devfreq_passive(devfreq, freq);
break;
case DEVFREQ_POSTCHANGE:
if (parent->previous_freq < freq)
update_devfreq_passive(devfreq, freq);
break;
}
return NOTIFY_DONE;
}
static int devfreq_passive_event_handler(struct devfreq *devfreq,
unsigned int event, void *data)
{
struct devfreq_passive_data *p_data
= (struct devfreq_passive_data *)devfreq->data;
struct devfreq *parent = (struct devfreq *)p_data->parent;
struct notifier_block *nb = &p_data->nb;
int ret = 0;
if (!parent)
return -EPROBE_DEFER;
switch (event) {
case DEVFREQ_GOV_START:
if (!p_data->this)
p_data->this = devfreq;
nb->notifier_call = devfreq_passive_notifier_call;
ret = devfreq_register_notifier(parent, nb,
DEVFREQ_TRANSITION_NOTIFIER);
break;
case DEVFREQ_GOV_STOP:
WARN_ON(devfreq_unregister_notifier(parent, nb,
DEVFREQ_TRANSITION_NOTIFIER));
break;
default:
break;
}
return ret;
}
static struct devfreq_governor devfreq_passive = {
.name = DEVFREQ_GOV_PASSIVE,
.immutable = 1,
.get_target_freq = devfreq_passive_get_target_freq,
.event_handler = devfreq_passive_event_handler,
};
static int __init devfreq_passive_init(void)
{
return devfreq_add_governor(&devfreq_passive);
}
subsys_initcall(devfreq_passive_init);
static void __exit devfreq_passive_exit(void)
{
int ret;
ret = devfreq_remove_governor(&devfreq_passive);
if (ret)
pr_err("%s: failed remove governor %d\n", __func__, ret);
}
module_exit(devfreq_passive_exit);
MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
MODULE_DESCRIPTION("DEVFREQ Passive governor");
MODULE_LICENSE("GPL v2");
+67
View File
@@ -0,0 +1,67 @@
/*
* linux/drivers/devfreq/governor_performance.c
*
* Copyright (C) 2011 Samsung Electronics
* MyungJoo Ham <myungjoo.ham@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/devfreq.h>
#include <linux/module.h>
#include "governor.h"
static int devfreq_performance_func(struct devfreq *df,
unsigned long *freq)
{
/*
* target callback should be able to get floor value as
* said in devfreq.h
*/
if (!df->max_freq)
*freq = UINT_MAX;
else
*freq = df->max_freq;
return 0;
}
static int devfreq_performance_handler(struct devfreq *devfreq,
unsigned int event, void *data)
{
int ret = 0;
if (event == DEVFREQ_GOV_START) {
mutex_lock(&devfreq->lock);
ret = update_devfreq(devfreq);
mutex_unlock(&devfreq->lock);
}
return ret;
}
static struct devfreq_governor devfreq_performance = {
.name = DEVFREQ_GOV_PERFORMANCE,
.get_target_freq = devfreq_performance_func,
.event_handler = devfreq_performance_handler,
};
static int __init devfreq_performance_init(void)
{
return devfreq_add_governor(&devfreq_performance);
}
subsys_initcall(devfreq_performance_init);
static void __exit devfreq_performance_exit(void)
{
int ret;
ret = devfreq_remove_governor(&devfreq_performance);
if (ret)
pr_err("%s: failed remove governor %d\n", __func__, ret);
return;
}
module_exit(devfreq_performance_exit);
MODULE_LICENSE("GPL");
+64
View File
@@ -0,0 +1,64 @@
/*
* linux/drivers/devfreq/governor_powersave.c
*
* Copyright (C) 2011 Samsung Electronics
* MyungJoo Ham <myungjoo.ham@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/devfreq.h>
#include <linux/module.h>
#include "governor.h"
static int devfreq_powersave_func(struct devfreq *df,
unsigned long *freq)
{
/*
* target callback should be able to get ceiling value as
* said in devfreq.h
*/
*freq = df->min_freq;
return 0;
}
static int devfreq_powersave_handler(struct devfreq *devfreq,
unsigned int event, void *data)
{
int ret = 0;
if (event == DEVFREQ_GOV_START) {
mutex_lock(&devfreq->lock);
ret = update_devfreq(devfreq);
mutex_unlock(&devfreq->lock);
}
return ret;
}
static struct devfreq_governor devfreq_powersave = {
.name = DEVFREQ_GOV_POWERSAVE,
.get_target_freq = devfreq_powersave_func,
.event_handler = devfreq_powersave_handler,
};
static int __init devfreq_powersave_init(void)
{
return devfreq_add_governor(&devfreq_powersave);
}
subsys_initcall(devfreq_powersave_init);
static void __exit devfreq_powersave_exit(void)
{
int ret;
ret = devfreq_remove_governor(&devfreq_powersave);
if (ret)
pr_err("%s: failed remove governor %d\n", __func__, ret);
return;
}
module_exit(devfreq_powersave_exit);
MODULE_LICENSE("GPL");
+163
View File
@@ -0,0 +1,163 @@
/*
* linux/drivers/devfreq/governor_simpleondemand.c
*
* Copyright (C) 2011 Samsung Electronics
* MyungJoo Ham <myungjoo.ham@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/devfreq.h>
#include <linux/math64.h>
#include "governor.h"
/* Default constants for DevFreq-Simple-Ondemand (DFSO) */
#define DFSO_UPTHRESHOLD (90)
#define DFSO_DOWNDIFFERENCTIAL (5)
static int devfreq_simple_ondemand_func(struct devfreq *df,
unsigned long *freq)
{
int err;
struct devfreq_dev_status *stat;
unsigned long long a, b;
unsigned int dfso_upthreshold = DFSO_UPTHRESHOLD;
unsigned int dfso_downdifferential = DFSO_DOWNDIFFERENCTIAL;
struct devfreq_simple_ondemand_data *data = df->data;
unsigned long max = (df->max_freq) ? df->max_freq : UINT_MAX;
unsigned long min = (df->min_freq) ? df->min_freq : 0;
err = devfreq_update_stats(df);
if (err)
return err;
stat = &df->last_status;
if (data) {
if (data->upthreshold)
dfso_upthreshold = data->upthreshold;
if (data->downdifferential)
dfso_downdifferential = data->downdifferential;
}
if (dfso_upthreshold > 100 ||
dfso_upthreshold < dfso_downdifferential)
return -EINVAL;
/* Prevent overflow */
if (stat->busy_time >= (1 << 24) || stat->total_time >= (1 << 24)) {
stat->busy_time >>= 7;
stat->total_time >>= 7;
}
if (data && data->simple_scaling) {
if (stat->busy_time * 100 >
stat->total_time * dfso_upthreshold)
*freq = max;
else if (stat->busy_time * 100 <
stat->total_time * dfso_downdifferential)
*freq = min;
else
*freq = df->previous_freq;
return 0;
}
/* Assume MAX if it is going to be divided by zero */
if (stat->total_time == 0) {
*freq = max;
return 0;
}
/* Set MAX if it's busy enough */
if (stat->busy_time * 100 >
stat->total_time * dfso_upthreshold) {
*freq = max;
return 0;
}
/* Set MAX if we do not know the initial frequency */
if (stat->current_frequency == 0) {
*freq = max;
return 0;
}
/* Keep the current frequency */
if (stat->busy_time * 100 >
stat->total_time * (dfso_upthreshold - dfso_downdifferential)) {
*freq = stat->current_frequency;
return 0;
}
/* Set the desired frequency based on the load */
a = stat->busy_time;
a *= stat->current_frequency;
b = div_u64(a, stat->total_time);
b *= 100;
b = div_u64(b, (dfso_upthreshold - dfso_downdifferential / 2));
*freq = (unsigned long) b;
if (df->min_freq && *freq < df->min_freq)
*freq = df->min_freq;
if (df->max_freq && *freq > df->max_freq)
*freq = df->max_freq;
return 0;
}
static int devfreq_simple_ondemand_handler(struct devfreq *devfreq,
unsigned int event, void *data)
{
switch (event) {
case DEVFREQ_GOV_START:
devfreq_monitor_start(devfreq);
break;
case DEVFREQ_GOV_STOP:
devfreq_monitor_stop(devfreq);
break;
case DEVFREQ_GOV_INTERVAL:
devfreq_interval_update(devfreq, (unsigned int *)data);
break;
case DEVFREQ_GOV_SUSPEND:
devfreq_monitor_suspend(devfreq);
break;
case DEVFREQ_GOV_RESUME:
devfreq_monitor_resume(devfreq);
break;
default:
break;
}
return 0;
}
static struct devfreq_governor devfreq_simple_ondemand = {
.name = DEVFREQ_GOV_SIMPLE_ONDEMAND,
.get_target_freq = devfreq_simple_ondemand_func,
.event_handler = devfreq_simple_ondemand_handler,
};
static int __init devfreq_simple_ondemand_init(void)
{
return devfreq_add_governor(&devfreq_simple_ondemand);
}
subsys_initcall(devfreq_simple_ondemand_init);
static void __exit devfreq_simple_ondemand_exit(void)
{
int ret;
ret = devfreq_remove_governor(&devfreq_simple_ondemand);
if (ret)
pr_err("%s: failed remove governor %d\n", __func__, ret);
return;
}
module_exit(devfreq_simple_ondemand_exit);
MODULE_LICENSE("GPL");
+167
View File
@@ -0,0 +1,167 @@
/*
* linux/drivers/devfreq/governor_userspace.c
*
* Copyright (C) 2011 Samsung Electronics
* MyungJoo Ham <myungjoo.ham@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/devfreq.h>
#include <linux/pm.h>
#include <linux/mutex.h>
#include <linux/module.h>
#include "governor.h"
struct userspace_data {
unsigned long user_frequency;
bool valid;
};
static int devfreq_userspace_func(struct devfreq *df, unsigned long *freq)
{
struct userspace_data *data = df->data;
if (data->valid) {
unsigned long adjusted_freq = data->user_frequency;
if (df->max_freq && adjusted_freq > df->max_freq)
adjusted_freq = df->max_freq;
if (df->min_freq && adjusted_freq < df->min_freq)
adjusted_freq = df->min_freq;
*freq = adjusted_freq;
} else {
*freq = df->previous_freq; /* No user freq specified yet */
}
return 0;
}
static ssize_t store_freq(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct devfreq *devfreq = to_devfreq(dev);
struct userspace_data *data;
unsigned long wanted;
int err = 0;
mutex_lock(&devfreq->lock);
data = devfreq->data;
sscanf(buf, "%lu", &wanted);
data->user_frequency = wanted;
data->valid = true;
err = update_devfreq(devfreq);
if (err == 0)
err = count;
mutex_unlock(&devfreq->lock);
return err;
}
static ssize_t show_freq(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct devfreq *devfreq = to_devfreq(dev);
struct userspace_data *data;
int err = 0;
mutex_lock(&devfreq->lock);
data = devfreq->data;
if (data->valid)
err = sprintf(buf, "%lu\n", data->user_frequency);
else
err = sprintf(buf, "undefined\n");
mutex_unlock(&devfreq->lock);
return err;
}
static DEVICE_ATTR(set_freq, 0644, show_freq, store_freq);
static struct attribute *dev_entries[] = {
&dev_attr_set_freq.attr,
NULL,
};
static const struct attribute_group dev_attr_group = {
.name = DEVFREQ_GOV_USERSPACE,
.attrs = dev_entries,
};
static int userspace_init(struct devfreq *devfreq)
{
int err = 0;
struct userspace_data *data = kzalloc(sizeof(struct userspace_data),
GFP_KERNEL);
if (!data) {
err = -ENOMEM;
goto out;
}
data->valid = false;
devfreq->data = data;
err = sysfs_create_group(&devfreq->dev.kobj, &dev_attr_group);
out:
return err;
}
static void userspace_exit(struct devfreq *devfreq)
{
/*
* Remove the sysfs entry, unless this is being called after
* device_del(), which should have done this already via kobject_del().
*/
if (devfreq->dev.kobj.sd)
sysfs_remove_group(&devfreq->dev.kobj, &dev_attr_group);
kfree(devfreq->data);
devfreq->data = NULL;
}
static int devfreq_userspace_handler(struct devfreq *devfreq,
unsigned int event, void *data)
{
int ret = 0;
switch (event) {
case DEVFREQ_GOV_START:
ret = userspace_init(devfreq);
break;
case DEVFREQ_GOV_STOP:
userspace_exit(devfreq);
break;
default:
break;
}
return ret;
}
static struct devfreq_governor devfreq_userspace = {
.name = "userspace",
.get_target_freq = devfreq_userspace_func,
.event_handler = devfreq_userspace_handler,
};
static int __init devfreq_userspace_init(void)
{
return devfreq_add_governor(&devfreq_userspace);
}
subsys_initcall(devfreq_userspace_init);
static void __exit devfreq_userspace_exit(void)
{
int ret;
ret = devfreq_remove_governor(&devfreq_userspace);
if (ret)
pr_err("%s: failed remove governor %d\n", __func__, ret);
return;
}
module_exit(devfreq_userspace_exit);
MODULE_LICENSE("GPL");
+51
View File
@@ -0,0 +1,51 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kthread.h>
#include <helio-dvfsrc-ipi.h>
#if defined(CONFIG_MTK_TINYSYS_SSPM_SUPPORT)
static int qos_recv_thread(void *arg)
{
struct ipi_action qos_isr;
struct qos_ipi_data qos_d;
unsigned int rdata, ret;
qos_isr.data = &qos_d;
ret = sspm_ipi_recv_registration(IPI_ID_QOS, &qos_isr);
if (ret) {
pr_info("@%s: sspm_ipi_recv_registration failed.\n", __func__);
return 0;
}
while (1) {
rdata = 0;
sspm_ipi_recv_wait(IPI_ID_QOS);
switch (qos_d.cmd) {
case QOS_IPI_ERROR_HANDLER:
sspm_ipi_send_ack(IPI_ID_QOS, &rdata);
break;
}
}
return 0;
}
void helio_dvfsrc_sspm_ipi_init(int dvfsrc_en)
{
struct qos_ipi_data qos_d;
struct task_struct *qos_task;
qos_task = kthread_run(qos_recv_thread, NULL, "qos_recv");
qos_d.cmd = QOS_IPI_QOS_ENABLE;
qos_d.u.qos_init.dvfsrc_en = dvfsrc_en;
qos_ipi_to_sspm_command(&qos_d, 2);
}
#endif
+84
View File
@@ -0,0 +1,84 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_IPI_H
#define __HELIO_DVFSRC_IPI_H
#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
#include <sspm_ipi.h>
#include <sspm_ipi_pin.h>
enum {
QOS_IPI_QOS_ENABLE = 0,
QOS_IPI_OPP_TABLE,
QOS_IPI_VCORE_OPP,
QOS_IPI_DDR_OPP,
QOS_IPI_ERROR_HANDLER,
QOS_IPI_SWPM_INIT,
QOS_IPI_UPOWER_DATA_TRANSFER,
QOS_IPI_UPOWER_DUMP_TABLE,
QOS_IPI_GET_GPU_BW,
QOS_IPI_SWPM_ENABLE,
QOS_IPI_SWPM_SET_UPDATE_CNT,
NR_QOS_IPI,
};
struct qos_ipi_data {
unsigned int cmd;
union {
struct {
unsigned int dvfsrc_en;
} qos_init;
struct {
unsigned int vcore_dvfs_opp;
unsigned int vcore_uv;
unsigned int ddr_khz;
} opp_table;
struct {
unsigned int vcore_dvfs_opp;
unsigned int vcore_opp;
} vcore_opp;
struct {
unsigned int vcore_dvfs_opp;
unsigned int ddr_opp;
} ddr_opp;
struct {
unsigned int master_type;
unsigned int emi_data;
unsigned int predict_data;
} error_handler;
struct {
unsigned int dram_addr;
unsigned int dram_size;
unsigned int dram_ch_num;
} swpm_init;
struct {
unsigned int type;
unsigned int enable;
} swpm_enable;
struct {
unsigned int type;
unsigned int cnt;
} swpm_set_update_cnt;
struct {
unsigned int arg[3];
} upower_data;
} u;
};
static int qos_ipi_to_sspm_command(void *buffer, int slot)
{
int ack_data;
return sspm_ipi_send_sync(IPI_ID_QOS, IPI_OPT_POLLING,
buffer, slot, &ack_data, 1);
}
#endif
#endif
+93
View File
@@ -0,0 +1,93 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_MT6768_H
#define __HELIO_DVFSRC_MT6768_H
#include <mach/upmu_hw.h>
#define PMIC_VCORE_ADDR PMIC_RG_BUCK_VCORE_VOSEL
#define VCORE_BASE_UV 500000
#define VCORE_STEP_UV 6250
/* DVFSRC_BASIC_CONTROL 0x0 */
#define DVFSRC_EN_SHIFT 0
#define DVFSRC_EN_MASK 0x1
#define DVFSRC_OUT_EN_SHIFT 8
#define DVFSRC_OUT_EN_MASK 0x1
#define FORCE_EN_CUR_SHIFT 14
#define FORCE_EN_CUR_MASK 0x1
#define FORCE_EN_TAR_SHIFT 15
#define FORCE_EN_TAR_MASK 0x1
/* DVFSRC_SW_REQ 0x4 */
#define EMI_SW_AP_SHIFT 0
#define EMI_SW_AP_MASK 0x3
#define VCORE_SW_AP_SHIFT 2
#define VCORE_SW_AP_MASK 0x3
/* DVFSRC_SW_REQ2 0x8 */
#define EMI_SW_AP2_SHIFT 0
#define EMI_SW_AP2_MASK 0x3
#define VCORE_SW_AP2_SHIFT 2
#define VCORE_SW_AP2_MASK 0x3
/* DVFSRC_VCORE_REQUEST 0x48 */
#define VCORE_SCP_GEAR_SHIFT 30
#define VCORE_SCP_GEAR_MASK 0x3
/* DVFSRC_VCORE_REQUEST2 0x4C */
#define VCORE_QOS_GEAR0_SHIFT 24
#define VCORE_QOS_GEAR0_MASK 0x3
/* DVFSRC_LEVEL 0xDC */
#define CURRENT_LEVEL_SHIFT 16
#define CURRENT_LEVEL_MASK 0xFFFF
/* DVFSRC_FORCE 0x300 */
#define TARGET_FORCE_SHIFT 0
#define TARGET_FORCE_MASK 0xFFFF
#define CURRENT_FORCE_SHIFT 16
#define CURRENT_FORCE_MASK 0xFFFF
/* met profile table index */
enum met_info_index {
INFO_OPP_IDX = 0,
INFO_FREQ_IDX,
INFO_VCORE_IDX,
INFO_SPM_LEVEL_IDX,
INFO_MAX,
};
enum met_src_index {
SRC_MD2SPM_IDX,
DDR_OPP_IDX,
DDR_SW_REQ1_PMQOS_IDX,
DDR_SW_REQ2_CM_IDX,
DDR_EMI_TOTAL_IDX,
DDR_QOS_BW_IDX,
VCORE_OPP_IDX,
VCORE_SW_REQ1_PMQOS_IDX,
VCORE_SW_REQ2_CM_IDX,
VCORE_SCP_IDX,
SRC_TOTAL_EMI_BW_IDX,
SRC_PMQOS_TATOL_IDX,
SRC_PMQOS_BW0_IDX,
SRC_PMQOS_BW1_IDX,
SRC_PMQOS_BW2_IDX,
SRC_PMQOS_BW3_IDX,
SRC_PMQOS_BW4_IDX,
SRC_MD_REQ_OPP,
BWMON_TOTAL_BW_IDX,
BWMON_CPU_BW_IDX,
BWMON_GPU_BW_IDX,
BWMON_MM_BW_IDX,
SRC_MAX
};
extern void dvfsrc_enable_dvfs_freq_hopping(int gps_on);
extern int dvfsrc_get_dvfs_freq_hopping_status(void);
#endif /* __HELIO_DVFSRC_MT6768_H */
+50
View File
@@ -0,0 +1,50 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_OPP_MT6768_H
#define __HELIO_DVFSRC_OPP_MT6768_H
#include <linux/soc/mediatek/mtk-pm-qos.h>
enum ddr_opp {
DDR_OPP_0 = 0,
DDR_OPP_1,
DDR_OPP_2,
DDR_OPP_NUM,
DDR_OPP_UNREQ = MTK_PM_QOS_DDR_OPP_DEFAULT_VALUE,
};
enum vcore_opp {
VCORE_OPP_0 = 0,
VCORE_OPP_1,
VCORE_OPP_2,
VCORE_OPP_3,
VCORE_OPP_NUM,
VCORE_OPP_UNREQ = MTK_PM_QOS_VCORE_OPP_DEFAULT_VALUE,
};
enum vcore_dvfs_opp {
VCORE_DVFS_OPP_0 = 0,
VCORE_DVFS_OPP_1,
VCORE_DVFS_OPP_2,
VCORE_DVFS_OPP_3,
VCORE_DVFS_OPP_4,
VCORE_DVFS_OPP_5,
VCORE_DVFS_OPP_6,
VCORE_DVFS_OPP_7,
VCORE_DVFS_OPP_8,
VCORE_DVFS_OPP_9,
VCORE_DVFS_OPP_10,
VCORE_DVFS_OPP_11,
VCORE_DVFS_OPP_12,
VCORE_DVFS_OPP_13,
VCORE_DVFS_OPP_14,
VCORE_DVFS_OPP_15,
VCORE_DVFS_OPP_NUM,
VCORE_DVFS_OPP_UNREQ = MTK_PM_QOS_VCORE_DVFS_FORCE_OPP_DEFAULT_VALUE,
};
#endif /* __HELIO_DVFSRC_OPP_MT6768_H */
+58
View File
@@ -0,0 +1,58 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_OPP_MT6785_H
#define __HELIO_DVFSRC_OPP_MT6785_H
#include <linux/soc/mediatek/mtk-pm-qos.h>
int ddr_level_to_step(int opp);
enum ddr_opp {
DDR_OPP_0 = 0,
DDR_OPP_1,
DDR_OPP_2,
DDR_OPP_3,
DDR_OPP_4,
DDR_OPP_5,
DDR_OPP_NUM,
DDR_OPP_UNREQ = MTK_PM_QOS_DDR_OPP_DEFAULT_VALUE,
};
enum vcore_opp {
VCORE_OPP_0 = 0,
VCORE_OPP_1,
VCORE_OPP_2,
VCORE_OPP_NUM,
VCORE_OPP_UNREQ = MTK_PM_QOS_VCORE_OPP_DEFAULT_VALUE,
};
enum emi_opp {
EMI_OPP_0 = 0,
EMI_OPP_1,
EMI_OPP_2,
EMI_OPP_NUM,
EMI_OPP_UNREQ = -1,
};
enum vcore_dvfs_opp {
VCORE_DVFS_OPP_0 = 0,
VCORE_DVFS_OPP_1,
VCORE_DVFS_OPP_2,
VCORE_DVFS_OPP_3,
VCORE_DVFS_OPP_4,
VCORE_DVFS_OPP_5,
VCORE_DVFS_OPP_6,
VCORE_DVFS_OPP_7,
VCORE_DVFS_OPP_8,
VCORE_DVFS_OPP_9,
VCORE_DVFS_OPP_10,
VCORE_DVFS_OPP_11,
VCORE_DVFS_OPP_12,
VCORE_DVFS_OPP_NUM,
VCORE_DVFS_OPP_UNREQ = MTK_PM_QOS_VCORE_DVFS_FORCE_OPP_DEFAULT_VALUE,
};
#endif /* __HELIO_DVFSRC_OPP_MT6785_H */
+75
View File
@@ -0,0 +1,75 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_OPP_MT67XX_H
#define __HELIO_DVFSRC_OPP_MT67XX_H
#include <linux/soc/mediatek/mtk-pm-qos.h>
enum ddr_opp {
DDR_OPP_0 = 0,
DDR_OPP_1,
DDR_OPP_2,
DDR_OPP_3,
DDR_OPP_4,
DDR_OPP_5,
DDR_OPP_6,
DDR_OPP_7,
DDR_OPP_8,
DDR_OPP_9,
DDR_OPP_10,
DDR_OPP_11,
DDR_OPP_12,
DDR_OPP_13,
DDR_OPP_14,
DDR_OPP_15,
DDR_OPP_NUM,
DDR_OPP_UNREQ = MTK_PM_QOS_DDR_OPP_DEFAULT_VALUE,
};
enum vcore_opp {
VCORE_OPP_0 = 0,
VCORE_OPP_1,
VCORE_OPP_2,
VCORE_OPP_3,
VCORE_OPP_4,
VCORE_OPP_5,
VCORE_OPP_6,
VCORE_OPP_7,
VCORE_OPP_8,
VCORE_OPP_9,
VCORE_OPP_10,
VCORE_OPP_11,
VCORE_OPP_12,
VCORE_OPP_13,
VCORE_OPP_14,
VCORE_OPP_15,
VCORE_OPP_NUM,
VCORE_OPP_UNREQ = MTK_PM_QOS_VCORE_OPP_DEFAULT_VALUE,
};
enum vcore_dvfs_opp {
VCORE_DVFS_OPP_0 = 0,
VCORE_DVFS_OPP_1,
VCORE_DVFS_OPP_2,
VCORE_DVFS_OPP_3,
VCORE_DVFS_OPP_4,
VCORE_DVFS_OPP_5,
VCORE_DVFS_OPP_6,
VCORE_DVFS_OPP_7,
VCORE_DVFS_OPP_8,
VCORE_DVFS_OPP_9,
VCORE_DVFS_OPP_10,
VCORE_DVFS_OPP_11,
VCORE_DVFS_OPP_12,
VCORE_DVFS_OPP_13,
VCORE_DVFS_OPP_14,
VCORE_DVFS_OPP_15,
VCORE_DVFS_OPP_NUM,
VCORE_DVFS_OPP_UNREQ = MTK_PM_QOS_VCORE_DVFS_FORCE_OPP_DEFAULT_VALUE,
};
#endif /* __HELIO_DVFSRC_OPP_MT67XX_H */
+64
View File
@@ -0,0 +1,64 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_OPP_MT6833_H
#define __HELIO_DVFSRC_OPP_MT6833_H
#include <linux/soc/mediatek/mtk-pm-qos.h>
int ddr_level_to_step(int opp);
enum ddr_opp {
DDR_OPP_0 = 0,
DDR_OPP_1,
DDR_OPP_2,
DDR_OPP_3,
DDR_OPP_4,
DDR_OPP_5,
DDR_OPP_6,
DDR_OPP_7,
DDR_OPP_NUM,
DDR_OPP_UNREQ = MTK_PM_QOS_DDR_OPP_DEFAULT_VALUE,
};
enum vcore_opp {
VCORE_OPP_0 = 0,
VCORE_OPP_1,
VCORE_OPP_2,
VCORE_OPP_3,
VCORE_OPP_NUM,
VCORE_OPP_UNREQ = MTK_PM_QOS_VCORE_OPP_DEFAULT_VALUE,
};
enum vcore_dvfs_opp {
VCORE_DVFS_OPP_0 = 0,
VCORE_DVFS_OPP_1,
VCORE_DVFS_OPP_2,
VCORE_DVFS_OPP_3,
VCORE_DVFS_OPP_4,
VCORE_DVFS_OPP_5,
VCORE_DVFS_OPP_6,
VCORE_DVFS_OPP_7,
VCORE_DVFS_OPP_8,
VCORE_DVFS_OPP_9,
VCORE_DVFS_OPP_10,
VCORE_DVFS_OPP_11,
VCORE_DVFS_OPP_12,
VCORE_DVFS_OPP_13,
VCORE_DVFS_OPP_14,
VCORE_DVFS_OPP_15,
VCORE_DVFS_OPP_16,
VCORE_DVFS_OPP_17,
VCORE_DVFS_OPP_18,
VCORE_DVFS_OPP_19,
VCORE_DVFS_OPP_20,
VCORE_DVFS_OPP_21,
VCORE_DVFS_OPP_22,
VCORE_DVFS_OPP_23,
VCORE_DVFS_OPP_24,
VCORE_DVFS_OPP_NUM,
VCORE_DVFS_OPP_UNREQ = MTK_PM_QOS_VCORE_DVFS_FORCE_OPP_DEFAULT_VALUE,
};
#endif /* __HELIO_DVFSRC_OPP_MT6833_H */
+60
View File
@@ -0,0 +1,60 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_OPP_MT6853_H
#define __HELIO_DVFSRC_OPP_MT6853_H
#include <linux/soc/mediatek/mtk-pm-qos.h>
int ddr_level_to_step(int opp);
enum ddr_opp {
DDR_OPP_0 = 0,
DDR_OPP_1,
DDR_OPP_2,
DDR_OPP_3,
DDR_OPP_4,
DDR_OPP_5,
DDR_OPP_6,
DDR_OPP_NUM,
DDR_OPP_UNREQ = MTK_PM_QOS_DDR_OPP_DEFAULT_VALUE,
};
enum vcore_opp {
VCORE_OPP_0 = 0,
VCORE_OPP_1,
VCORE_OPP_2,
VCORE_OPP_3,
VCORE_OPP_NUM,
VCORE_OPP_UNREQ = MTK_PM_QOS_VCORE_OPP_DEFAULT_VALUE,
};
enum vcore_dvfs_opp {
VCORE_DVFS_OPP_0 = 0,
VCORE_DVFS_OPP_1,
VCORE_DVFS_OPP_2,
VCORE_DVFS_OPP_3,
VCORE_DVFS_OPP_4,
VCORE_DVFS_OPP_5,
VCORE_DVFS_OPP_6,
VCORE_DVFS_OPP_7,
VCORE_DVFS_OPP_8,
VCORE_DVFS_OPP_9,
VCORE_DVFS_OPP_10,
VCORE_DVFS_OPP_11,
VCORE_DVFS_OPP_12,
VCORE_DVFS_OPP_13,
VCORE_DVFS_OPP_14,
VCORE_DVFS_OPP_15,
VCORE_DVFS_OPP_16,
VCORE_DVFS_OPP_17,
VCORE_DVFS_OPP_18,
VCORE_DVFS_OPP_19,
VCORE_DVFS_OPP_20,
VCORE_DVFS_OPP_NUM,
VCORE_DVFS_OPP_UNREQ = MTK_PM_QOS_VCORE_DVFS_FORCE_OPP_DEFAULT_VALUE,
};
#endif /* __HELIO_DVFSRC_OPP_MT6853_H */
+60
View File
@@ -0,0 +1,60 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_OPP_MT6873_H
#define __HELIO_DVFSRC_OPP_MT6873_H
#include <linux/soc/mediatek/mtk-pm-qos.h>
int ddr_level_to_step(int opp);
enum ddr_opp {
DDR_OPP_0 = 0,
DDR_OPP_1,
DDR_OPP_2,
DDR_OPP_3,
DDR_OPP_4,
DDR_OPP_5,
DDR_OPP_6,
DDR_OPP_NUM,
DDR_OPP_UNREQ = MTK_PM_QOS_DDR_OPP_DEFAULT_VALUE,
};
enum vcore_opp {
VCORE_OPP_0 = 0,
VCORE_OPP_1,
VCORE_OPP_2,
VCORE_OPP_3,
VCORE_OPP_NUM,
VCORE_OPP_UNREQ = MTK_PM_QOS_VCORE_OPP_DEFAULT_VALUE,
};
enum vcore_dvfs_opp {
VCORE_DVFS_OPP_0 = 0,
VCORE_DVFS_OPP_1,
VCORE_DVFS_OPP_2,
VCORE_DVFS_OPP_3,
VCORE_DVFS_OPP_4,
VCORE_DVFS_OPP_5,
VCORE_DVFS_OPP_6,
VCORE_DVFS_OPP_7,
VCORE_DVFS_OPP_8,
VCORE_DVFS_OPP_9,
VCORE_DVFS_OPP_10,
VCORE_DVFS_OPP_11,
VCORE_DVFS_OPP_12,
VCORE_DVFS_OPP_13,
VCORE_DVFS_OPP_14,
VCORE_DVFS_OPP_15,
VCORE_DVFS_OPP_16,
VCORE_DVFS_OPP_17,
VCORE_DVFS_OPP_18,
VCORE_DVFS_OPP_19,
VCORE_DVFS_OPP_20,
VCORE_DVFS_OPP_NUM,
VCORE_DVFS_OPP_UNREQ = MTK_PM_QOS_VCORE_DVFS_FORCE_OPP_DEFAULT_VALUE,
};
#endif /* __HELIO_DVFSRC_OPP_MT6873_H */
+69
View File
@@ -0,0 +1,69 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_OPP_MT6885_H
#define __HELIO_DVFSRC_OPP_MT6885_H
#include <linux/soc/mediatek/mtk-pm-qos.h>
int ddr_level_to_step(int opp);
enum ddr_opp {
DDR_OPP_0 = 0,
DDR_OPP_1,
DDR_OPP_2,
DDR_OPP_3,
DDR_OPP_4,
DDR_OPP_5,
DDR_OPP_6,
DDR_OPP_NUM,
DDR_OPP_UNREQ = MTK_PM_QOS_DDR_OPP_DEFAULT_VALUE,
};
enum vcore_opp {
VCORE_OPP_0 = 0,
VCORE_OPP_1,
VCORE_OPP_2,
VCORE_OPP_3,
VCORE_OPP_NUM,
VCORE_OPP_UNREQ = MTK_PM_QOS_VCORE_OPP_DEFAULT_VALUE,
};
enum emi_opp {
EMI_OPP_0 = 0,
EMI_OPP_1,
EMI_OPP_2,
EMI_OPP_NUM,
EMI_OPP_UNREQ = -1,
};
enum vcore_dvfs_opp {
VCORE_DVFS_OPP_0 = 0,
VCORE_DVFS_OPP_1,
VCORE_DVFS_OPP_2,
VCORE_DVFS_OPP_3,
VCORE_DVFS_OPP_4,
VCORE_DVFS_OPP_5,
VCORE_DVFS_OPP_6,
VCORE_DVFS_OPP_7,
VCORE_DVFS_OPP_8,
VCORE_DVFS_OPP_9,
VCORE_DVFS_OPP_10,
VCORE_DVFS_OPP_11,
VCORE_DVFS_OPP_12,
VCORE_DVFS_OPP_13,
VCORE_DVFS_OPP_14,
VCORE_DVFS_OPP_15,
VCORE_DVFS_OPP_16,
VCORE_DVFS_OPP_17,
VCORE_DVFS_OPP_18,
VCORE_DVFS_OPP_19,
VCORE_DVFS_OPP_20,
VCORE_DVFS_OPP_21,
VCORE_DVFS_OPP_NUM,
VCORE_DVFS_OPP_UNREQ = MTK_PM_QOS_VCORE_DVFS_FORCE_OPP_DEFAULT_VALUE,
};
#endif /* __HELIO_DVFSRC_OPP_MT6885_H */
+73
View File
@@ -0,0 +1,73 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_OPP_MT6893_H
#define __HELIO_DVFSRC_OPP_MT6893_H
#include <linux/soc/mediatek/mtk-pm-qos.h>
int ddr_level_to_step(int opp);
enum ddr_opp {
DDR_OPP_0 = 0,
DDR_OPP_1,
DDR_OPP_2,
DDR_OPP_3,
DDR_OPP_4,
DDR_OPP_5,
DDR_OPP_6,
DDR_OPP_7,
DDR_OPP_NUM,
DDR_OPP_UNREQ = MTK_PM_QOS_DDR_OPP_DEFAULT_VALUE,
};
enum vcore_opp {
VCORE_OPP_0 = 0,
VCORE_OPP_1,
VCORE_OPP_2,
VCORE_OPP_3,
VCORE_OPP_4,
VCORE_OPP_NUM,
VCORE_OPP_UNREQ = MTK_PM_QOS_VCORE_OPP_DEFAULT_VALUE,
};
enum emi_opp {
EMI_OPP_0 = 0,
EMI_OPP_1,
EMI_OPP_2,
EMI_OPP_NUM,
EMI_OPP_UNREQ = -1,
};
enum vcore_dvfs_opp {
VCORE_DVFS_OPP_0 = 0,
VCORE_DVFS_OPP_1,
VCORE_DVFS_OPP_2,
VCORE_DVFS_OPP_3,
VCORE_DVFS_OPP_4,
VCORE_DVFS_OPP_5,
VCORE_DVFS_OPP_6,
VCORE_DVFS_OPP_7,
VCORE_DVFS_OPP_8,
VCORE_DVFS_OPP_9,
VCORE_DVFS_OPP_10,
VCORE_DVFS_OPP_11,
VCORE_DVFS_OPP_12,
VCORE_DVFS_OPP_13,
VCORE_DVFS_OPP_14,
VCORE_DVFS_OPP_15,
VCORE_DVFS_OPP_16,
VCORE_DVFS_OPP_17,
VCORE_DVFS_OPP_18,
VCORE_DVFS_OPP_19,
VCORE_DVFS_OPP_20,
VCORE_DVFS_OPP_21,
VCORE_DVFS_OPP_22,
VCORE_DVFS_OPP_23,
VCORE_DVFS_OPP_NUM,
VCORE_DVFS_OPP_UNREQ = MTK_PM_QOS_VCORE_DVFS_FORCE_OPP_DEFAULT_VALUE,
};
#endif /* __HELIO_DVFSRC_OPP_MT6893_H */
+50
View File
@@ -0,0 +1,50 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* * Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_OPP_V6781_H
#define __HELIO_DVFSRC_OPP_V6781_H
#include <linux/soc/mediatek/mtk-pm-qos.h>
int ddr_level_to_step(int opp);
enum ddr_opp {
DDR_OPP_0 = 0,
DDR_OPP_1,
DDR_OPP_2,
DDR_OPP_3,
DDR_OPP_4,
DDR_OPP_5,
DDR_OPP_NUM,
DDR_OPP_UNREQ = MTK_PM_QOS_DDR_OPP_DEFAULT_VALUE,
};
enum vcore_opp {
VCORE_OPP_0 = 0,
VCORE_OPP_1,
VCORE_OPP_2,
VCORE_OPP_NUM,
VCORE_OPP_UNREQ = MTK_PM_QOS_VCORE_OPP_DEFAULT_VALUE,
};
enum vcore_dvfs_opp {
VCORE_DVFS_OPP_0 = 0,
VCORE_DVFS_OPP_1,
VCORE_DVFS_OPP_2,
VCORE_DVFS_OPP_3,
VCORE_DVFS_OPP_4,
VCORE_DVFS_OPP_5,
VCORE_DVFS_OPP_6,
VCORE_DVFS_OPP_7,
VCORE_DVFS_OPP_8,
VCORE_DVFS_OPP_9,
VCORE_DVFS_OPP_10,
VCORE_DVFS_OPP_11,
VCORE_DVFS_OPP_12,
VCORE_DVFS_OPP_NUM,
VCORE_DVFS_OPP_UNREQ = MTK_PM_QOS_VCORE_DVFS_FORCE_OPP_DEFAULT_VALUE,
};
#endif /* __HELIO_DVFSRC_OPP_V6781_H */
+71
View File
@@ -0,0 +1,71 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_OPP_V6877_H
#define __HELIO_DVFSRC_OPP_V6877_H
#include <linux/soc/mediatek/mtk-pm-qos.h>
int ddr_level_to_step(int opp);
enum ddr_opp {
DDR_OPP_0 = 0,
DDR_OPP_1,
DDR_OPP_2,
DDR_OPP_3,
DDR_OPP_4,
DDR_OPP_5,
DDR_OPP_6,
DDR_OPP_7,
DDR_OPP_NUM,
DDR_OPP_UNREQ = MTK_PM_QOS_DDR_OPP_DEFAULT_VALUE,
};
enum vcore_opp {
VCORE_OPP_0 = 0,
VCORE_OPP_1,
VCORE_OPP_2,
VCORE_OPP_3,
VCORE_OPP_4,
VCORE_OPP_NUM,
VCORE_OPP_UNREQ = MTK_PM_QOS_VCORE_OPP_DEFAULT_VALUE,
};
enum vcore_dvfs_opp {
VCORE_DVFS_OPP_0 = 0,
VCORE_DVFS_OPP_1,
VCORE_DVFS_OPP_2,
VCORE_DVFS_OPP_3,
VCORE_DVFS_OPP_4,
VCORE_DVFS_OPP_5,
VCORE_DVFS_OPP_6,
VCORE_DVFS_OPP_7,
VCORE_DVFS_OPP_8,
VCORE_DVFS_OPP_9,
VCORE_DVFS_OPP_10,
VCORE_DVFS_OPP_11,
VCORE_DVFS_OPP_12,
VCORE_DVFS_OPP_13,
VCORE_DVFS_OPP_14,
VCORE_DVFS_OPP_15,
VCORE_DVFS_OPP_16,
VCORE_DVFS_OPP_17,
VCORE_DVFS_OPP_18,
VCORE_DVFS_OPP_19,
VCORE_DVFS_OPP_20,
VCORE_DVFS_OPP_21,
VCORE_DVFS_OPP_22,
VCORE_DVFS_OPP_23,
VCORE_DVFS_OPP_24,
VCORE_DVFS_OPP_25,
VCORE_DVFS_OPP_26,
VCORE_DVFS_OPP_27,
VCORE_DVFS_OPP_28,
VCORE_DVFS_OPP_29,
VCORE_DVFS_OPP_NUM,
VCORE_DVFS_OPP_UNREQ = MTK_PM_QOS_VCORE_DVFS_FORCE_OPP_DEFAULT_VALUE,
};
#endif /* __HELIO_DVFSRC_OPP_V6877_H */
+83
View File
@@ -0,0 +1,83 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_OPP_H
#define __HELIO_DVFSRC_OPP_H
#if defined(CONFIG_MACH_MT6775)
#include <helio-dvfsrc-opp-mt6775.h>
#elif defined(CONFIG_MACH_MT6771)
#include <helio-dvfsrc-opp-mt6771.h>
#elif defined(CONFIG_MACH_MT6768)
#include <helio-dvfsrc-opp-mt6768.h>
#elif defined(CONFIG_MACH_MT6765)
#include <helio-dvfsrc-opp-mt6765.h>
#elif defined(CONFIG_MACH_MT6785)
#if defined(CONFIG_MTK_DVFSRC_MT6781_PRETEST)
#include <helio-dvfsrc-opp-v6781.h>
#else
#include <helio-dvfsrc-opp-mt6785.h>
#endif
#elif defined(CONFIG_MACH_MT6885)
#include <helio-dvfsrc-opp-mt6885.h>
#elif defined(CONFIG_MACH_MT6873)
#include <helio-dvfsrc-opp-mt6873.h>
#elif defined(CONFIG_MACH_MT6853)
#if defined(CONFIG_MTK_DVFSRC_MT6833_PRETEST)
#include <helio-dvfsrc-opp-mt6833.h>
#elif defined(CONFIG_MTK_DVFSRC_MT6877_PRETEST)
#include <helio-dvfsrc-opp-v6877.h>
#else
#include <helio-dvfsrc-opp-mt6853.h>
#endif
#elif defined(CONFIG_MACH_MT6893)
#include <helio-dvfsrc-opp-mt6893.h>
#elif defined(CONFIG_MACH_MT6833)
#include <helio-dvfsrc-opp-mt6833.h>
#elif defined(CONFIG_MACH_MT6877)
#include <helio-dvfsrc-opp-v6877.h>
#elif defined(CONFIG_MACH_MT6781)
#include <helio-dvfsrc-opp-v6781.h>
#else
#include <helio-dvfsrc-opp-mt67xx.h>
#endif
#if defined(CONFIG_MACH_MT6768) || defined(CONFIG_MACH_MT6785) \
|| defined(CONFIG_MACH_MT6885) || defined(CONFIG_MACH_MT6765) \
|| defined(CONFIG_MACH_MT6873) || defined(CONFIG_MACH_MT6853) \
|| defined(CONFIG_MACH_MT6893) || defined(CONFIG_MACH_MT6833) \
|| defined(CONFIG_MACH_MT6877) || defined(CONFIG_MACH_MT6781)
struct opp_profile {
int vcore_uv;
int ddr_khz;
};
extern int get_cur_vcore_dvfs_opp(void);
extern void set_opp_table(unsigned int vcore_dvfs_opp, int vcore_uv,
int ddr_khz);
extern int get_vcore_opp(unsigned int opp);
extern int get_vcore_uv(unsigned int opp);
extern int get_cur_vcore_opp(void);
extern int get_cur_vcore_uv(void);
extern void set_vcore_opp(unsigned int vcore_dvfs_opp, int vcore_opp);
extern int get_ddr_opp(unsigned int opp);
extern int get_ddr_khz(unsigned int opp);
extern int get_cur_ddr_opp(void);
extern int get_cur_ddr_khz(void);
extern void set_ddr_opp(unsigned int vcore_dvfs_opp, int ddr_opp);
extern void set_vcore_uv_table(unsigned int vcore_opp, int vcore_uv);
extern int get_vcore_uv_table(unsigned int vcore_opp);
extern void set_pwrap_cmd(unsigned int vcore_opp, int pwrap_cmd);
extern int get_pwrap_cmd(unsigned int vcore_opp);
extern int get_opp_ddr_freq(unsigned int ddr_opp);
extern void set_opp_ddr_freq(unsigned int ddr_opp, int ddr_freq);
#endif
#endif /* __HELIO_DVFSRC_OPP_H */
+305
View File
@@ -0,0 +1,305 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2018 MediaTek Inc.
*/
#include <linux/device.h>
#include <linux/sysfs.h>
#include <helio-dvfsrc.h>
#if defined(CONFIG_MTK_DRAMC)
#include <mtk_dramc.h>
#endif
#include <mt-plat/upmu_common.h>
#include "mtk_dvfsrc_reg.h"
#include <linux/pm_qos.h>
#include <helio-dvfsrc-opp.h>
#include <mtk_vcorefs_governor.h>
static struct pm_qos_request dvfsrc_emi_request;
static struct pm_qos_request dvfsrc_vcore_request;
#if !defined(CONFIG_MACH_MT6771)
__weak unsigned int get_dram_data_rate(void) { return 0; }
__weak unsigned int get_vcore_opp_volt(unsigned int seg) { return 0; }
__weak int dram_steps_freq(unsigned int step) { return 0; }
int vcorefs_get_curr_vcore(void)
{
int ret = 0;
#if !defined(CONFIG_FPGA_EARLY_PORTING)
ret = pmic_get_register_value(PMIC_VCORE_ADDR);
ret = vcore_pmic_to_uv(ret);
#endif
return ret;
}
int vcorefs_get_curr_ddr(void)
{
return get_dram_data_rate() * 1000;
}
int dvfsrc_get_vcore_by_steps(u32 opp)
{
return vcore_pmic_to_uv(get_vcore_opp_volt(opp));
}
int dvfsrc_get_ddr_by_steps(u32 opp)
{
int ddr_khz;
ddr_khz = dram_steps_freq(opp) * 1000;
return ddr_khz;
}
#endif
static ssize_t opp_table_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
char *p = buf;
dvfsrc_update_opp_table();
p = dvfsrc_get_opp_table_info(p);
return p - buf;
}
static DEVICE_ATTR_RO(opp_table);
static ssize_t dvfsrc_debug_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct helio_dvfsrc *dvfsrc;
char *p = buf;
char *buff_end = p + PAGE_SIZE;
int uv = vcorefs_get_curr_vcore();
dvfsrc = dev_get_drvdata(dev);
if (!dvfsrc)
return sprintf(buf, "Failed to access dvfsrc\n");
p += snprintf(p, buff_end - p, "[%-12s] uv : %-8u (0x%x)\n",
"vcore", uv, vcore_uv_to_pmic(uv));
p += snprintf(p, buff_end - p, "[%-12s] khz: %-8u\n",
"ddr", vcorefs_get_curr_ddr());
p += snprintf(p, buff_end - p, "[%-12s]: %d\n",
"Enable", dvfsrc->enable);
p += snprintf(p, buff_end - p, "[%-12s]: %d\n",
"skip", dvfsrc->skip);
p += snprintf(p, buff_end - p, "[%-12s]: %d\n",
"log_mask", dvfsrc->log_mask);
p += snprintf(p, buff_end - p, "[vcore_dvs]: %d\n", dvfsrc->vcore_dvs);
p += snprintf(p, buff_end - p, "[ddr_dfs ]: %d\n", dvfsrc->ddr_dfs);
p += snprintf(p, buff_end - p, "[mm_clk ]: %d\n", dvfsrc->mm_clk);
p += snprintf(p, buff_end - p, "%-24s: 0x%x\n",
"DVFSRC_RECORD_COUNT",
dvfsrc_read(dvfsrc, DVFSRC_RECORD_COUNT));
p += snprintf(p, buff_end - p, "%-24s: 0x%x\n",
"DVFSRC_LAST",
dvfsrc_read(dvfsrc, DVFSRC_LAST));
p += snprintf(p, buff_end - p,
"%-24s: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
"DVFSRC_RECORD_0_1~3_1",
dvfsrc_read(dvfsrc, DVFSRC_RECORD_0_1),
dvfsrc_read(dvfsrc, DVFSRC_RECORD_1_1),
dvfsrc_read(dvfsrc, DVFSRC_RECORD_2_1),
dvfsrc_read(dvfsrc, DVFSRC_RECORD_3_1));
p += snprintf(p, buff_end - p,
"%-24s: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
"DVFSRC_RECORD_4_1~7_1",
dvfsrc_read(dvfsrc, DVFSRC_RECORD_4_1),
dvfsrc_read(dvfsrc, DVFSRC_RECORD_5_1),
dvfsrc_read(dvfsrc, DVFSRC_RECORD_6_1),
dvfsrc_read(dvfsrc, DVFSRC_RECORD_7_1));
p += snprintf(p, buff_end - p,
"%-24s: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
"DVFSRC_RECORD_0_0~3_0",
dvfsrc_read(dvfsrc, DVFSRC_RECORD_0_0),
dvfsrc_read(dvfsrc, DVFSRC_RECORD_1_0),
dvfsrc_read(dvfsrc, DVFSRC_RECORD_2_0),
dvfsrc_read(dvfsrc, DVFSRC_RECORD_3_0));
p += snprintf(p, buff_end - p,
"%-24s: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
"DVFSRC_RECORD_4_0~7_0",
dvfsrc_read(dvfsrc, DVFSRC_RECORD_4_0),
dvfsrc_read(dvfsrc, DVFSRC_RECORD_5_0),
dvfsrc_read(dvfsrc, DVFSRC_RECORD_6_0),
dvfsrc_read(dvfsrc, DVFSRC_RECORD_7_0));
p += snprintf(p, buff_end - p,
"%-24s: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
"DVFSRC_RECORD_MD_0~3",
dvfsrc_read(dvfsrc, DVFSRC_RECORD_MD_0),
dvfsrc_read(dvfsrc, DVFSRC_RECORD_MD_1),
dvfsrc_read(dvfsrc, DVFSRC_RECORD_MD_2),
dvfsrc_read(dvfsrc, DVFSRC_RECORD_MD_3));
p += snprintf(p, buff_end - p,
"%-24s: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
"DVFSRC_RECORD_MD_4~7",
dvfsrc_read(dvfsrc, DVFSRC_RECORD_MD_4),
dvfsrc_read(dvfsrc, DVFSRC_RECORD_MD_5),
dvfsrc_read(dvfsrc, DVFSRC_RECORD_MD_6),
dvfsrc_read(dvfsrc, DVFSRC_RECORD_MD_7));
p += snprintf(p, buff_end - p, "%-24s: 0x%x\n",
"DVFSRC_LEVEL",
dvfsrc_read(dvfsrc, DVFSRC_LEVEL));
p += snprintf(p, buff_end - p, "%-24s: 0x%x\n",
"DVFSRC_VCORE_REQUEST",
dvfsrc_read(dvfsrc, DVFSRC_VCORE_REQUEST));
p += snprintf(p, buff_end - p, "%-24s: 0x%x\n",
"DVFSRC_EMI_REQUEST",
dvfsrc_read(dvfsrc, DVFSRC_EMI_REQUEST));
p += snprintf(p, buff_end - p, "%-24s: 0x%x\n",
"DVFSRC_MD_REQUEST",
dvfsrc_read(dvfsrc, DVFSRC_MD_REQUEST));
p += snprintf(p, buff_end - p, "%-24s: 0x%x\n",
"DVFSRC_RSRV_0",
dvfsrc_read(dvfsrc, DVFSRC_RSRV_0));
p += snprintf(p, buff_end - p, "\n");
p += snprintf(p, buff_end - p, "%-24s: %d\n",
"QOS_EMI_OPP",
pm_qos_request(PM_QOS_EMI_OPP));
p += snprintf(p, buff_end - p, "%-24s: %d\n",
"QOS_VCORE_OPP",
pm_qos_request(PM_QOS_VCORE_OPP));
p += snprintf(p, buff_end - p, "%-24s: %3d (100MB/s)\n",
"DVFSRC_EMI_QOS0 THRES",
dvfsrc_read(dvfsrc, DVFSRC_EMI_QOS0));
p += snprintf(p, buff_end - p, "%-24s: %3d (100MB/s)\n",
"DVFSRC_EMI_QOS1 THRES",
dvfsrc_read(dvfsrc, DVFSRC_EMI_QOS1));
p += snprintf(p, buff_end - p, "%-24s: %3d (100MB/s)\n",
"DVFSRC_SW_BW_0 (Test)",
dvfsrc_read(dvfsrc, DVFSRC_SW_BW_0));
p += snprintf(p, buff_end - p, "%-24s: %3d (100MB/s)\n",
"DVFSRC_SW_BW_1 (CPU)",
dvfsrc_read(dvfsrc, DVFSRC_SW_BW_1));
p += snprintf(p, buff_end - p, "%-24s: %3d (100MB/s)\n",
"DVFSRC_SW_BW_2 (GPU)",
dvfsrc_read(dvfsrc, DVFSRC_SW_BW_2));
p += snprintf(p, buff_end - p, "%-24s: %3d (100MB/s)\n",
"DVFSRC_SW_BW_3 (MM)",
dvfsrc_read(dvfsrc, DVFSRC_SW_BW_3));
p += snprintf(p, buff_end - p, "%-24s: %3d (100MB/s)\n",
"DVFSRC_SW_BW_4 (OTHER)",
dvfsrc_read(dvfsrc, DVFSRC_SW_BW_4));
p += snprintf(p, buff_end - p, "\n");
return p - buf;
}
static ssize_t dvfsrc_debug_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int val, val2, r = 0;
char cmd[32];
struct helio_dvfsrc *dvfsrc;
dvfsrc = dev_get_drvdata(dev);
if (!dvfsrc)
return -ENODEV;
if (sscanf(buf, "%31s 0x%x 0x%x", cmd, &val, &val2) == 3 ||
sscanf(buf, "%31s %d %d", cmd, &val, &val2) == 3)
pr_info("dvfsrc_debug cmd: %s, val1: %d(0x%x) val2: %d(0x%x)\n",
cmd, val, val, val2, val2);
else if (sscanf(buf, "%31s 0x%x", cmd, &val) == 2 ||
sscanf(buf, "%31s %d", cmd, &val) == 2)
pr_info("dvfsrc_debug cmd: %s, val: %d(0x%x)\n", cmd, val, val);
if (!strcmp(cmd, "kir_emi"))
pm_qos_update_request(&dvfsrc_emi_request, val);
else if (!strcmp(cmd, "kir_vcore"))
pm_qos_update_request(&dvfsrc_vcore_request, val);
else if (!strcmp(cmd, "skip"))
dvfsrc->skip = val;
else if (!strcmp(cmd, "log_mask"))
dvfsrc->log_mask = val;
else
r = -EPERM;
return count;
}
static DEVICE_ATTR_RW(dvfsrc_debug);
static ssize_t dvfsrc_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct helio_dvfsrc *dvfsrc;
dvfsrc = dev_get_drvdata(dev);
if (!dvfsrc)
return sprintf(buf, "Failed to access dvfsrc\n");
return sprintf(buf, "%d\n", dvfsrc->enable);
}
static ssize_t dvfsrc_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct helio_dvfsrc *dvfsrc;
s32 value;
dvfsrc = dev_get_drvdata(dev);
if (!dvfsrc)
return -ENODEV;
if (kstrtos32(buf, 0, &value))
return -EINVAL;
if (value < 0)
return -EINVAL;
dvfsrc->enable = value;
/* ToDo: If disable, fix highest opp? */
return count;
}
static
DEVICE_ATTR_RW(dvfsrc_enable);
static struct attribute *helio_dvfsrc_attrs[] = {
&dev_attr_dvfsrc_debug.attr,
&dev_attr_dvfsrc_enable.attr,
&dev_attr_opp_table.attr,
NULL,
};
static struct attribute_group helio_dvfsrc_attr_group = {
.name = "helio-dvfsrc",
.attrs = helio_dvfsrc_attrs,
};
int helio_dvfsrc_add_interface(struct device *dev)
{
pm_qos_add_request(&dvfsrc_emi_request,
PM_QOS_EMI_OPP, PM_QOS_EMI_OPP_DEFAULT_VALUE);
pm_qos_add_request(&dvfsrc_vcore_request, PM_QOS_VCORE_OPP,
PM_QOS_VCORE_OPP_DEFAULT_VALUE);
return sysfs_create_group(&dev->kobj, &helio_dvfsrc_attr_group);
}
void helio_dvfsrc_remove_interface(struct device *dev)
{
sysfs_remove_group(&dev->kobj, &helio_dvfsrc_attr_group);
}
@@ -0,0 +1,674 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/fb.h>
#include <linux/notifier.h>
#include <linux/string.h>
#include <mtk_dramc.h>
#include <mt-plat/upmu_common.h>
//#include <ext_wd_drv.h>
#include <mt_emi_api.h>
#include <mt-plat/mtk_devinfo.h>
#include <helio-dvfsrc_v2.h>
#include <helio-dvfsrc-opp.h>
#include <mtk_dvfsrc_reg.h>
#include <mtk_spm_internal.h>
#include <spm/mtk_vcore_dvfs.h>
#include <mtk_gpufreq.h>
#include "mmdvfs_pmqos.h"
__weak unsigned int get_dram_data_rate(void) { return 0; }
static struct reg_config dvfsrc_init_configs[][128] = {
/* SPMFW_LP4X_2CH_3600 */
{
{ DVFSRC_EMI_REQUEST, 0x00240009 },
{ DVFSRC_EMI_REQUEST3, 0x09000000 },
{ DVFSRC_EMI_HRT, 0x003E362C },
{ DVFSRC_EMI_QOS0, 0x00000033 },
{ DVFSRC_EMI_QOS1, 0x0000004C },
{ DVFSRC_EMI_MD2SPM0, 0x0000003F },
{ DVFSRC_EMI_MD2SPM1, 0x00000000 },
{ DVFSRC_EMI_MD2SPM2, 0x000080C0 },
{ DVFSRC_EMI_MD2SPM0_T, 0x00000007 },
{ DVFSRC_EMI_MD2SPM1_T, 0x00000038 },
{ DVFSRC_EMI_MD2SPM2_T, 0x000080C0 },
{ DVFSRC_VCORE_HRT, 0x00000036 },
{ DVFSRC_MD_SW_CONTROL, 0x20000000 },
{ DVFSRC_TIMEOUT_NEXTREQ, 0x00000014 },
{ DVFSRC_INT_EN, 0x00000003 },
{ DVFSRC_LEVEL_LABEL_0_1, 0x00010000 },
{ DVFSRC_LEVEL_LABEL_2_3, 0x00020101 },
{ DVFSRC_LEVEL_LABEL_4_5, 0x01020012 },
{ DVFSRC_LEVEL_LABEL_6_7, 0x02120112 },
{ DVFSRC_LEVEL_LABEL_8_9, 0x00230013 },
{ DVFSRC_LEVEL_LABEL_10_11, 0x01230113 },
{ DVFSRC_LEVEL_LABEL_12_13, 0x02230213 },
{ DVFSRC_LEVEL_LABEL_14_15, 0x03230323 },
{ DVFSRC_FORCE, 0x40000000 },
{ DVFSRC_RSRV_1, 0x0000000C },
{ DVFSRC_QOS_EN, 0x0000407F },
{ DVFSRC_BASIC_CONTROL, 0x0000407B },
{ DVFSRC_BASIC_CONTROL, 0x0000017B },
{ DVFSRC_FORCE, 0x00000000 },
{ -1, 0 },
},
/* SPMFW_LP3_1CH_1866 */
{
{ DVFSRC_EMI_REQUEST, 0x00240009 },
{ DVFSRC_EMI_REQUEST3, 0x09000000 },
{ DVFSRC_EMI_HRT, 0x00000020 },
{ DVFSRC_EMI_QOS0, 0x00000026 },
{ DVFSRC_EMI_QOS1, 0x00000033 },
{ DVFSRC_EMI_MD2SPM0, 0x0000003F },
{ DVFSRC_EMI_MD2SPM1, 0x00000000 },
{ DVFSRC_EMI_MD2SPM2, 0x000080C0 },
{ DVFSRC_EMI_MD2SPM0_T, 0x00000007 },
{ DVFSRC_EMI_MD2SPM1_T, 0x00000038 },
{ DVFSRC_EMI_MD2SPM2_T, 0x000080C0 },
{ DVFSRC_VCORE_HRT, 0x00000020 },
{ DVFSRC_MD_SW_CONTROL, 0x20000000 },
{ DVFSRC_TIMEOUT_NEXTREQ, 0x00000014 },
{ DVFSRC_INT_EN, 0x00000003 },
{ DVFSRC_LEVEL_LABEL_0_1, 0x00010000 },
{ DVFSRC_LEVEL_LABEL_2_3, 0x00020101 },
{ DVFSRC_LEVEL_LABEL_4_5, 0x01020012 },
{ DVFSRC_LEVEL_LABEL_6_7, 0x02120112 },
{ DVFSRC_LEVEL_LABEL_8_9, 0x00230013 },
{ DVFSRC_LEVEL_LABEL_10_11, 0x01230113 },
{ DVFSRC_LEVEL_LABEL_12_13, 0x02230213 },
{ DVFSRC_LEVEL_LABEL_14_15, 0x03230323 },
{ DVFSRC_FORCE, 0x40000000 },
{ DVFSRC_RSRV_1, 0x0000000C },
{ DVFSRC_QOS_EN, 0x0000407F },
{ DVFSRC_BASIC_CONTROL, 0x0000407B },
{ DVFSRC_BASIC_CONTROL, 0x0000017B },
{ DVFSRC_FORCE, 0x00000000 },
{ -1, 0 },
},
/* NULL */
{
{ -1, 0 },
},
};
struct reg_config *dvfsrc_get_init_conf(void)
{
int spmfw_idx = spm_get_spmfw_idx();
if (spmfw_idx < 0)
spmfw_idx = ARRAY_SIZE(dvfsrc_init_configs) - 1;
if (spmfw_idx == SPMFW_LP3_1CH_1866)
spmfw_idx = 1;
else
spmfw_idx = 0;
pr_info("dvfsrc init config index %d\n", spmfw_idx);
return dvfsrc_init_configs[spmfw_idx];
}
void dvfsrc_update_md_scenario(bool blank)
{
switch (spm_get_spmfw_idx()) {
case SPMFW_LP4X_2CH_3600:
case SPMFW_LP4X_2CH_3200:
case SPMFW_LP4_2CH_3200:
if (blank) {
dvfsrc_write(DVFSRC_EMI_MD2SPM0_T, 0x0000003F);
dvfsrc_write(DVFSRC_EMI_MD2SPM1_T, 0x00000000);
} else {
dvfsrc_write(DVFSRC_EMI_MD2SPM0_T, 0x00000007);
dvfsrc_write(DVFSRC_EMI_MD2SPM1_T, 0x00000038);
}
break;
case SPMFW_LP3_1CH_1866:
if (blank) {
dvfsrc_write(DVFSRC_EMI_MD2SPM0_T, 0x0000003F);
dvfsrc_write(DVFSRC_EMI_MD2SPM1_T, 0x00000000);
} else {
dvfsrc_write(DVFSRC_EMI_MD2SPM0_T, 0x00000007);
dvfsrc_write(DVFSRC_EMI_MD2SPM1_T, 0x00000038);
}
break;
default:
break;
}
}
static int dvfsrc_fb_notifier_call(struct notifier_block *self,
unsigned long event, void *data)
{
struct fb_event *evdata = data;
int blank;
if (event != FB_EVENT_BLANK)
return 0;
blank = *(int *)evdata->data;
switch (blank) {
case FB_BLANK_UNBLANK:
dvfsrc_update_md_scenario(false);
break;
case FB_BLANK_POWERDOWN:
dvfsrc_update_md_scenario(true);
break;
default:
break;
}
return 0;
}
static struct notifier_block dvfsrc_fb_notifier = {
.notifier_call = dvfsrc_fb_notifier_call,
};
#if 1
static int is_bypass_flavor(void)
{
int r = 0;
#if defined(CONFIG_ARM64) && \
defined(CONFIG_BUILD_ARM64_DTB_OVERLAY_IMAGE_NAMES)
if (strstr(CONFIG_BUILD_ARM64_DTB_OVERLAY_IMAGE_NAMES,
"k68v1_64_vcore_dvfs_fix") != NULL)
r = 0;
pr_info("flavor check: %s, is_bypass: %d\n",
CONFIG_BUILD_ARM64_DTB_OVERLAY_IMAGE_NAMES, r);
#endif
return r;
}
#endif
static int can_dvfsrc_enable(void)
{
int enable = 0;
#if 1
if (is_bypass_flavor()) {
enable = 0;
pr_info("VCORE DVFS disable for special flavor\n");
} else {
enable = 1;
pr_info("VCORE DVFS enable default\n");
}
#endif
return enable;
}
__weak int mtk_rgu_cfg_dvfsrc(int enable) { return 0; }
__weak int emmc_autok(void)
{
pr_info("NOT SUPPORT EMMC AUTOK\n");
return 0;
}
__weak int sd_autok(void)
{
pr_info("NOT SUPPORT SD AUTOK\n");
return 0;
}
__weak int sdio_autok(void)
{
pr_info("NOT SUPPORT SDIO AUTOK\n");
return 0;
}
void begin_autok_task(void)
{
/* notify MM DVFS for msdc autok start */
mmdvfs_prepare_action(MMDVFS_PREPARE_CALIBRATION_START);
#if 0 /* def CONFIG_MTK_GPU_SUPPORT */
/* notify GPU DVFS for msdc autok start */
mt_gpufreq_disable_by_ptpod();
#endif
}
void finish_autok_task(void)
{
/* check if dvfs force is released */
int force = mtk_pm_qos_request(MTK_PM_QOS_VCORE_DVFS_FORCE_OPP);
/* notify MM DVFS for msdc autok finish */
mmdvfs_prepare_action(MMDVFS_PREPARE_CALIBRATION_END);
#if 0 /* def CONFIG_MTK_GPU_SUPPORT */
/* notify GPU DVFS for msdc autok finish */
mt_gpufreq_enable_by_ptpod();
#endif
if (force >= 0 && force < 16)
pr_info("autok task not release force opp: %d\n", force);
}
void dvfsrc_autok_manager(void)
{
int r = 0;
begin_autok_task();
r = emmc_autok();
pr_info("EMMC autok done: %s\n", (r == 0) ? "Yes" : "No");
r = sd_autok();
pr_info("SD autok done: %s\n", (r == 0) ? "Yes" : "No");
r = sdio_autok();
pr_info("SDIO autok done: %s\n", (r == 0) ? "Yes" : "No");
finish_autok_task();
}
int helio_dvfsrc_platform_init(struct helio_dvfsrc *dvfsrc)
{
mtk_rgu_cfg_dvfsrc(1);
helio_dvfsrc_sram_reg_init();
if (can_dvfsrc_enable())
helio_dvfsrc_enable(1);
else
helio_dvfsrc_enable(0);
dvfsrc->init_config = dvfsrc_get_init_conf();
helio_dvfsrc_reg_config(dvfsrc->init_config);
dvfsrc_autok_manager();
return fb_register_client(&dvfsrc_fb_notifier);
}
void get_opp_info(char *p)
{
#if defined(CONFIG_MTK_PMIC_COMMON)
int pmic_val = pmic_get_register_value(PMIC_VCORE_ADDR);
int vcore_uv = vcore_pmic_to_uv(pmic_val);
#endif
int ddr_khz = get_dram_data_rate() * 1000;
#if defined(CONFIG_MTK_PMIC_COMMON)
p += sprintf(p, "%-24s: %-8u uv (PMIC: 0x%x)\n",
"Vcore", vcore_uv, vcore_uv_to_pmic(vcore_uv));
#endif
p += sprintf(p, "%-24s: %-8u khz\n", "DDR", ddr_khz);
p += sprintf(p, "%-24s: %-8u\n", "SW REQ OPP", get_sw_req_vcore_opp());
}
void get_dvfsrc_reg(char *p)
{
p += sprintf(p, "%-24s: 0x%08x\n",
"DVFSRC_BASIC_CONTROL",
dvfsrc_read(DVFSRC_BASIC_CONTROL));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x\n",
"DVFSRC_SW_REQ(2)",
dvfsrc_read(DVFSRC_SW_REQ),
dvfsrc_read(DVFSRC_SW_REQ2));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x, 0x%08x\n",
"DVFSRC_EMI_QOS0(1)(2)",
dvfsrc_read(DVFSRC_EMI_QOS0),
dvfsrc_read(DVFSRC_EMI_QOS1),
dvfsrc_read(DVFSRC_EMI_QOS2));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x, 0x%08x\n",
"DVFSRC_EMI_REQUEST(2)(3)",
dvfsrc_read(DVFSRC_EMI_REQUEST),
dvfsrc_read(DVFSRC_EMI_REQUEST2),
dvfsrc_read(DVFSRC_EMI_REQUEST3));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x, 0x%08x\n",
"DVFSRC_EMI_MD2SPM0~2",
dvfsrc_read(DVFSRC_EMI_MD2SPM0),
dvfsrc_read(DVFSRC_EMI_MD2SPM1),
dvfsrc_read(DVFSRC_EMI_MD2SPM2));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x, 0x%08x\n",
"DVFSRC_EMI_MD2SPM0~2_T",
dvfsrc_read(DVFSRC_EMI_MD2SPM0_T),
dvfsrc_read(DVFSRC_EMI_MD2SPM1_T),
dvfsrc_read(DVFSRC_EMI_MD2SPM2_T));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x\n",
"DVFSRC_VCORE_REQUEST(2)",
dvfsrc_read(DVFSRC_VCORE_REQUEST),
dvfsrc_read(DVFSRC_VCORE_REQUEST2));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x, 0x%08x\n",
"DVFSRC_VCORE_MD2SPM0~2",
dvfsrc_read(DVFSRC_VCORE_MD2SPM0),
dvfsrc_read(DVFSRC_VCORE_MD2SPM1),
dvfsrc_read(DVFSRC_VCORE_MD2SPM2));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x, 0x%08x\n",
"DVFSRC_VCORE_MD2SPM0~2_T",
dvfsrc_read(DVFSRC_VCORE_MD2SPM0_T),
dvfsrc_read(DVFSRC_VCORE_MD2SPM1_T),
dvfsrc_read(DVFSRC_VCORE_MD2SPM2_T));
p += sprintf(p, "%-24s: 0x%08x\n",
"DVFSRC_MD_REQUEST",
dvfsrc_read(DVFSRC_MD_REQUEST));
p += sprintf(p, "%-24s: 0x%08x\n",
"DVFSRC_INT",
dvfsrc_read(DVFSRC_INT));
p += sprintf(p, "%-24s: 0x%08x\n",
"DVFSRC_INT_EN",
dvfsrc_read(DVFSRC_INT_EN));
p += sprintf(p, "%-24s: 0x%08x\n",
"DVFSRC_LEVEL",
dvfsrc_read(DVFSRC_LEVEL));
p += sprintf(p, "%-24s: %d, %d, %d, %d, %d\n",
"DVFSRC_SW_BW_0~4",
dvfsrc_read(DVFSRC_SW_BW_0),
dvfsrc_read(DVFSRC_SW_BW_1),
dvfsrc_read(DVFSRC_SW_BW_2),
dvfsrc_read(DVFSRC_SW_BW_3),
dvfsrc_read(DVFSRC_SW_BW_4));
}
void get_dvfsrc_record(char *p)
{
p += sprintf(p, "%-24s: 0x%08x\n",
"DVFSRC_FORCE",
dvfsrc_read(DVFSRC_FORCE));
p += sprintf(p, "%-24s: 0x%08x\n",
"DVFSRC_SEC_SW_REQ",
dvfsrc_read(DVFSRC_SEC_SW_REQ));
p += sprintf(p, "%-24s: 0x%08x\n",
"DVFSRC_LAST",
dvfsrc_read(DVFSRC_LAST));
p += sprintf(p, "%-24s: 0x%08x\n",
"DVFSRC_MD_SCENARIO",
dvfsrc_read(DVFSRC_MD_SCENARIO));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x, 0x%08x\n",
"DVFSRC_RECORD_0_0~0_2",
dvfsrc_read(DVFSRC_RECORD_0_0),
dvfsrc_read(DVFSRC_RECORD_0_1),
dvfsrc_read(DVFSRC_RECORD_0_2));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x, 0x%08x\n",
"DVFSRC_RECORD_1_0~1_2",
dvfsrc_read(DVFSRC_RECORD_1_0),
dvfsrc_read(DVFSRC_RECORD_1_1),
dvfsrc_read(DVFSRC_RECORD_1_2));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x, 0x%08x\n",
"DVFSRC_RECORD_2_0~2_2",
dvfsrc_read(DVFSRC_RECORD_2_0),
dvfsrc_read(DVFSRC_RECORD_2_1),
dvfsrc_read(DVFSRC_RECORD_2_2));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x, 0x%08x\n",
"DVFSRC_RECORD_3_0~3_2",
dvfsrc_read(DVFSRC_RECORD_3_0),
dvfsrc_read(DVFSRC_RECORD_3_1),
dvfsrc_read(DVFSRC_RECORD_3_2));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x, 0x%08x\n",
"DVFSRC_RECORD_4_0~4_2",
dvfsrc_read(DVFSRC_RECORD_4_0),
dvfsrc_read(DVFSRC_RECORD_4_1),
dvfsrc_read(DVFSRC_RECORD_4_2));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x, 0x%08x\n",
"DVFSRC_RECORD_5_0~5_2",
dvfsrc_read(DVFSRC_RECORD_5_0),
dvfsrc_read(DVFSRC_RECORD_5_1),
dvfsrc_read(DVFSRC_RECORD_5_2));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x, 0x%08x\n",
"DVFSRC_RECORD_6_0~6_2",
dvfsrc_read(DVFSRC_RECORD_6_0),
dvfsrc_read(DVFSRC_RECORD_6_1),
dvfsrc_read(DVFSRC_RECORD_6_2));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x, 0x%08x\n",
"DVFSRC_RECORD_7_0~7_2",
dvfsrc_read(DVFSRC_RECORD_7_0),
dvfsrc_read(DVFSRC_RECORD_7_1),
dvfsrc_read(DVFSRC_RECORD_7_2));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
"DVFSRC_RECORD_MD_0~3",
dvfsrc_read(DVFSRC_RECORD_MD_0),
dvfsrc_read(DVFSRC_RECORD_MD_1),
dvfsrc_read(DVFSRC_RECORD_MD_2),
dvfsrc_read(DVFSRC_RECORD_MD_3));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
"DVFSRC_RECORD_MD_4~7",
dvfsrc_read(DVFSRC_RECORD_MD_4),
dvfsrc_read(DVFSRC_RECORD_MD_5),
dvfsrc_read(DVFSRC_RECORD_MD_6),
dvfsrc_read(DVFSRC_RECORD_MD_7));
p += sprintf(p, "%-24s: 0x%08x\n",
"DVFSRC_RECORD_COUNT",
dvfsrc_read(DVFSRC_RECORD_COUNT));
p += sprintf(p, "%-24s: 0x%08x\n",
"DVFSRC_RSRV_0",
dvfsrc_read(DVFSRC_RSRV_0));
}
/* met profile table */
unsigned int met_vcorefs_info[INFO_MAX];
unsigned int met_vcorefs_src[SRC_MAX];
char *met_info_name[INFO_MAX] = {
"OPP",
"FREQ",
"VCORE",
"x__SPM_LEVEL",
};
char *met_src_name[SRC_MAX] = {
"MD2SPM",
"SRC_DDR_OPP",
"DDR__SW_REQ1_PMQOS",
"DDR__SW_REQ2_CM",
"DDR__EMI_TOTAL",
"DDR__QOS_BW",
"SRC_VCORE_OPP",
"VCORE__SW_REQ1_PMQOS",
"VCORE__SW_REQ2_CM",
"VCORE__SCP",
"TOTAL_EMI_BW",
"PMQOS_TOTAL",
"PMQOS_BW0",
"PMQOS_BW1",
"PMQOS_BW2",
"PMQOS_BW3",
"PMQOS_BW4",
"MD_REQ_OPP",
"BWMON__TOTAL_BW",
"BWMON__CPU_BW",
"BWMON__GPU_BW",
"BWMON__MM_BW",
};
/* met profile function */
int vcorefs_get_num_opp(void)
{
return VCORE_DVFS_OPP_NUM;
}
EXPORT_SYMBOL(vcorefs_get_num_opp);
int vcorefs_get_opp_info_num(void)
{
return INFO_MAX;
}
EXPORT_SYMBOL(vcorefs_get_opp_info_num);
int vcorefs_get_src_req_num(void)
{
return SRC_MAX;
}
EXPORT_SYMBOL(vcorefs_get_src_req_num);
char **vcorefs_get_opp_info_name(void)
{
return met_info_name;
}
EXPORT_SYMBOL(vcorefs_get_opp_info_name);
char **vcorefs_get_src_req_name(void)
{
return met_src_name;
}
EXPORT_SYMBOL(vcorefs_get_src_req_name);
unsigned int *vcorefs_get_opp_info(void)
{
met_vcorefs_info[INFO_OPP_IDX] = get_cur_vcore_dvfs_opp();
met_vcorefs_info[INFO_FREQ_IDX] = get_cur_ddr_khz();
met_vcorefs_info[INFO_VCORE_IDX] = get_cur_vcore_uv();
met_vcorefs_info[INFO_SPM_LEVEL_IDX] = spm_get_dvfs_level();
return met_vcorefs_info;
}
EXPORT_SYMBOL(vcorefs_get_opp_info);
unsigned int *vcorefs_get_src_req(void)
{
unsigned int qos_total_bw = dvfsrc_read(DVFSRC_SW_BW_0) +
dvfsrc_read(DVFSRC_SW_BW_1) +
dvfsrc_read(DVFSRC_SW_BW_2) +
dvfsrc_read(DVFSRC_SW_BW_3) +
dvfsrc_read(DVFSRC_SW_BW_4);
unsigned int sw_req = dvfsrc_read(DVFSRC_SW_REQ);
unsigned int sw_req2 = dvfsrc_read(DVFSRC_SW_REQ2);
unsigned int total_bw_status = get_emi_bwst(0);
unsigned int total_bw_last = (get_emi_bwvl(0) & 0x7F) * 813;
unsigned int qos0_thres = dvfsrc_read(DVFSRC_EMI_QOS0);
unsigned int qos1_thres = dvfsrc_read(DVFSRC_EMI_QOS1);
met_vcorefs_src[SRC_MD2SPM_IDX] =
spm_vcorefs_get_MD_status();
met_vcorefs_src[DDR_OPP_IDX] = get_cur_ddr_opp();
met_vcorefs_src[DDR_SW_REQ1_PMQOS_IDX] =
(sw_req >> EMI_SW_AP_SHIFT) & EMI_SW_AP_MASK;
met_vcorefs_src[DDR_SW_REQ2_CM_IDX] =
(sw_req2 >> EMI_SW_AP_SHIFT) & EMI_SW_AP_MASK;
if (((total_bw_status >> 1) & 0x1) != 0)
met_vcorefs_src[DDR_EMI_TOTAL_IDX] = 2;
else if ((total_bw_status & 0x1) != 0)
met_vcorefs_src[DDR_EMI_TOTAL_IDX] = 1;
else
met_vcorefs_src[DDR_EMI_TOTAL_IDX] = 0;
if (qos_total_bw > qos1_thres)
met_vcorefs_src[DDR_QOS_BW_IDX] = 2;
else if (qos_total_bw > qos0_thres)
met_vcorefs_src[DDR_QOS_BW_IDX] = 1;
else
met_vcorefs_src[DDR_QOS_BW_IDX] = 0;
met_vcorefs_src[VCORE_OPP_IDX] = get_cur_vcore_opp();
met_vcorefs_src[VCORE_SW_REQ1_PMQOS_IDX] =
(sw_req >> VCORE_SW_AP_SHIFT) & VCORE_SW_AP_MASK;
met_vcorefs_src[VCORE_SW_REQ2_CM_IDX] =
(sw_req2 >> VCORE_SW_AP_SHIFT) & VCORE_SW_AP_MASK;
met_vcorefs_src[VCORE_SCP_IDX] =
(dvfsrc_read(DVFSRC_VCORE_REQUEST) >> VCORE_SCP_GEAR_SHIFT) &
VCORE_SCP_GEAR_MASK;
met_vcorefs_src[SRC_TOTAL_EMI_BW_IDX] = total_bw_last;
met_vcorefs_src[SRC_PMQOS_TATOL_IDX] = qos_total_bw * 100;
met_vcorefs_src[SRC_PMQOS_BW0_IDX] =
dvfsrc_read(DVFSRC_SW_BW_0) * 100;
met_vcorefs_src[SRC_PMQOS_BW1_IDX] =
dvfsrc_read(DVFSRC_SW_BW_1) * 100;
met_vcorefs_src[SRC_PMQOS_BW2_IDX] =
dvfsrc_read(DVFSRC_SW_BW_2) * 100;
met_vcorefs_src[SRC_PMQOS_BW3_IDX] =
dvfsrc_read(DVFSRC_SW_BW_3) * 100;
met_vcorefs_src[SRC_PMQOS_BW4_IDX] =
dvfsrc_read(DVFSRC_SW_BW_4) * 100;
met_vcorefs_src[SRC_MD_REQ_OPP] =
dvfsrc_read(DVFSRC_RSRV_0);
met_vcorefs_src[BWMON_TOTAL_BW_IDX] =
(qos_sram_read(QOS_DEBUG_0) & 0x7FFFFFFF) ^ 12345600;
met_vcorefs_src[BWMON_CPU_BW_IDX] =
(qos_sram_read(QOS_DEBUG_1) & 0x7FFFFFFF) ^ 12345600;
met_vcorefs_src[BWMON_GPU_BW_IDX] =
(qos_sram_read(QOS_DEBUG_2) & 0x7FFFFFFF) ^ 12345600;
met_vcorefs_src[BWMON_MM_BW_IDX] =
(qos_sram_read(QOS_DEBUG_3) & 0x7FFFFFFF) ^ 12345600;
return met_vcorefs_src;
}
EXPORT_SYMBOL(vcorefs_get_src_req);
/* gps workarund function */
static int is_freq_hopping;
void dvfsrc_enable_dvfs_freq_hopping(int gps_on)
{
static struct mtk_pm_qos_request gps_vcore_req;
static struct mtk_pm_qos_request gps_ddr_req;
if (!is_dvfsrc_enabled())
return;
if (spm_get_spmfw_idx() == SPMFW_LP3_1CH_1866)
return;
if (spm_get_spmfw_idx() == SPMFW_LP4X_2CH_3600)
return;
if (!mtk_pm_qos_request_active(&gps_vcore_req))
mtk_pm_qos_add_request(&gps_vcore_req,
MTK_PM_QOS_VCORE_OPP, MTK_PM_QOS_VCORE_OPP_DEFAULT_VALUE);
if (!mtk_pm_qos_request_active(&gps_ddr_req))
mtk_pm_qos_add_request(&gps_ddr_req,
MTK_PM_QOS_DDR_OPP, MTK_PM_QOS_DDR_OPP_DEFAULT_VALUE);
mtk_pm_qos_update_request(&gps_vcore_req, VCORE_OPP_0);
mtk_pm_qos_update_request(&gps_ddr_req, DDR_OPP_0);
#if defined(CONFIG_MTK_PMIC_COMMON)
pr_info("[before]gps_on: %d, vcore: %d ddr: %d dvfsrc_level: 0x%x\n",
gps_on,
vcore_pmic_to_uv(pmic_get_register_value(PMIC_VCORE_ADDR)),
get_dram_data_rate(),
dvfsrc_read(DVFSRC_LEVEL));
#endif
spm_freq_hopping_cmd(!!gps_on);
mtk_pm_qos_update_request(&gps_ddr_req, DDR_OPP_UNREQ);
mtk_pm_qos_update_request(&gps_vcore_req, VCORE_OPP_UNREQ);
is_freq_hopping = !!gps_on;
#if defined(CONFIG_MTK_PMIC_COMMON)
pr_info("[after]gps_on: %d, vcore: %d ddr: %d dvfsrc_level: 0x%x\n",
gps_on,
vcore_pmic_to_uv(pmic_get_register_value(PMIC_VCORE_ADDR)),
get_dram_data_rate(),
dvfsrc_read(DVFSRC_LEVEL));
#endif
}
EXPORT_SYMBOL(dvfsrc_enable_dvfs_freq_hopping);
int dvfsrc_get_dvfs_freq_hopping_status(void)
{
return is_freq_hopping;
}
@@ -0,0 +1,242 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <mt-plat/mtk_devinfo.h>
#include <mtk_spm_internal.h>
#include "helio-dvfsrc-opp.h"
#include <linux/of.h>
#include <linux/of_address.h>
static int get_vb_volt(int vcore_opp)
{
int ret = 0;
int ptpod64 = ((get_devinfo_with_index(64) >> 9) & 0x3);
pr_info("%s: ptpod64: 0x%x\n", __func__, ptpod64);
switch (vcore_opp) {
case VCORE_OPP_0:
case VCORE_OPP_1:
case VCORE_OPP_3:
break;
case VCORE_OPP_2:
if (ptpod64 != 0)
ret = ptpod64 - 1;
break;
default:
break;
}
return ret * 25000;
}
static int is_aging_test(void)
{
int ret = 0;
#if defined(CONFIG_ARM64) && \
defined(CONFIG_BUILD_ARM64_DTB_OVERLAY_IMAGE_NAMES)
pr_info("[VcoreFS] flavor name: %s\n",
CONFIG_BUILD_ARM64_DTB_OVERLAY_IMAGE_NAMES);
if ((strstr(CONFIG_BUILD_ARM64_DTB_OVERLAY_IMAGE_NAMES,
"k68v1_64_aging") != NULL)) {
pr_info("[VcoreFS]: AGING flavor !!!\n");
return 1;
}
#endif
return ret;
}
void dvfsrc_opp_level_mapping(void)
{
int vcore_opp_0_uv, vcore_opp_1_uv, vcore_opp_2_uv, vcore_opp_3_uv;
int is_vcore_ct = 0, is_mini_sqc = 0;
int info2 = 0;
struct device_node *dvfsrc_node = NULL;
int dvfs_v_mode = 0;
int is_vcore_aging = is_aging_test();
if (!strncmp(CONFIG_ARCH_MTK_PROJECT,
"k68v1_64_bsp_ctig", 17))
is_vcore_ct = is_mini_sqc = 1;
pr_info("flavor check: %s, is_vcore_ct: %d, is_mini_sqc: %d\n",
CONFIG_ARCH_MTK_PROJECT,
is_vcore_ct, is_mini_sqc);
set_pwrap_cmd(VCORE_OPP_0, 0);
set_pwrap_cmd(VCORE_OPP_1, 2);
set_pwrap_cmd(VCORE_OPP_2, 3);
set_pwrap_cmd(VCORE_OPP_3, 4);
dvfsrc_node =
of_find_compatible_node(NULL, NULL, "mediatek,dvfsrc");
if (of_property_read_u32(dvfsrc_node, "dvfs_v_mode",
(u32 *) &dvfs_v_mode) == 0)
pr_info("%s: DOE DVFS_V_MODE = %d\n",
__func__, dvfs_v_mode);
vcore_opp_0_uv = 800000;
vcore_opp_2_uv = 700000 - get_vb_volt(VCORE_OPP_2);
vcore_opp_1_uv = 700000;
vcore_opp_3_uv = 650000;
if (dvfs_v_mode == 1) { /* HV */
vcore_opp_0_uv = 843750;
vcore_opp_1_uv = 737500;
vcore_opp_3_uv = 687500;
/* apply MD VB */
vcore_opp_2_uv = 737500;
} else if (dvfs_v_mode == 3) { /* LV */
vcore_opp_0_uv = 756250;
vcore_opp_1_uv = 662500;
vcore_opp_3_uv = 612500;
/* apply MD VB */
vcore_opp_2_uv = 662500;
} else if (is_vcore_aging == 1) {
vcore_opp_0_uv -= 12500;
vcore_opp_1_uv -= 12500;
vcore_opp_2_uv -= 12500;
vcore_opp_3_uv -= 12500;
}
pr_info("flavor check: %s, is_vcore_ct: %d, is_mini_sqc: %d dvfs_v_mode: %d\n",
CONFIG_ARCH_MTK_PROJECT,
is_vcore_ct, is_mini_sqc, dvfs_v_mode);
pr_info("%s: FINAL vcore_opp_uv: %d, %d, %d, %d\n",
__func__,
vcore_opp_0_uv,
vcore_opp_1_uv,
vcore_opp_2_uv,
vcore_opp_3_uv);
set_vcore_uv_table(VCORE_OPP_0, vcore_opp_0_uv);
set_vcore_uv_table(VCORE_OPP_1, vcore_opp_1_uv);
if (info2 & 0x1) {
set_vcore_uv_table(VCORE_OPP_2, vcore_opp_1_uv);
set_vcore_uv_table(VCORE_OPP_3, vcore_opp_1_uv);
} else {
set_vcore_uv_table(VCORE_OPP_2, vcore_opp_2_uv);
set_vcore_uv_table(VCORE_OPP_3, vcore_opp_3_uv);
}
switch (spm_get_spmfw_idx()) {
/* fall through*/
case SPMFW_LP4X_2CH_3600:
case SPMFW_LP4X_2CH_3200:
case SPMFW_LP4_2CH_3200:
set_vcore_opp(VCORE_DVFS_OPP_0, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_1, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_2, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_3, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_4, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_5, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_6, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_7, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_8, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_9, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_10, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_11, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_12, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_13, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_14, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_15, VCORE_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_0, DDR_OPP_0);
set_ddr_opp(VCORE_DVFS_OPP_1, DDR_OPP_0);
set_ddr_opp(VCORE_DVFS_OPP_2, DDR_OPP_0);
set_ddr_opp(VCORE_DVFS_OPP_3, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_4, DDR_OPP_0);
set_ddr_opp(VCORE_DVFS_OPP_5, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_6, DDR_OPP_0);
set_ddr_opp(VCORE_DVFS_OPP_7, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_8, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_9, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_10, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_11, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_12, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_13, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_14, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_15, DDR_OPP_2);
break;
case SPMFW_LP3_1CH_1866:
set_vcore_opp(VCORE_DVFS_OPP_0, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_1, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_2, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_3, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_4, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_5, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_6, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_7, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_8, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_9, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_10, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_11, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_12, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_13, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_14, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_15, VCORE_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_0, DDR_OPP_0);
set_ddr_opp(VCORE_DVFS_OPP_1, DDR_OPP_0);
set_ddr_opp(VCORE_DVFS_OPP_2, DDR_OPP_0);
set_ddr_opp(VCORE_DVFS_OPP_3, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_4, DDR_OPP_0);
set_ddr_opp(VCORE_DVFS_OPP_5, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_6, DDR_OPP_0);
set_ddr_opp(VCORE_DVFS_OPP_7, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_8, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_9, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_10, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_11, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_12, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_13, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_14, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_15, DDR_OPP_2);
break;
default:
set_vcore_opp(VCORE_DVFS_OPP_0, VCORE_OPP_UNREQ);
set_vcore_opp(VCORE_DVFS_OPP_1, VCORE_OPP_UNREQ);
set_vcore_opp(VCORE_DVFS_OPP_2, VCORE_OPP_UNREQ);
set_vcore_opp(VCORE_DVFS_OPP_3, VCORE_OPP_UNREQ);
set_vcore_opp(VCORE_DVFS_OPP_4, VCORE_OPP_UNREQ);
set_vcore_opp(VCORE_DVFS_OPP_5, VCORE_OPP_UNREQ);
set_vcore_opp(VCORE_DVFS_OPP_6, VCORE_OPP_UNREQ);
set_vcore_opp(VCORE_DVFS_OPP_7, VCORE_OPP_UNREQ);
set_vcore_opp(VCORE_DVFS_OPP_8, VCORE_OPP_UNREQ);
set_vcore_opp(VCORE_DVFS_OPP_9, VCORE_OPP_UNREQ);
set_vcore_opp(VCORE_DVFS_OPP_10, VCORE_OPP_UNREQ);
set_vcore_opp(VCORE_DVFS_OPP_11, VCORE_OPP_UNREQ);
set_vcore_opp(VCORE_DVFS_OPP_12, VCORE_OPP_UNREQ);
set_vcore_opp(VCORE_DVFS_OPP_13, VCORE_OPP_UNREQ);
set_vcore_opp(VCORE_DVFS_OPP_14, VCORE_OPP_UNREQ);
set_vcore_opp(VCORE_DVFS_OPP_15, VCORE_OPP_UNREQ);
set_ddr_opp(VCORE_DVFS_OPP_0, DDR_OPP_UNREQ);
set_ddr_opp(VCORE_DVFS_OPP_1, DDR_OPP_UNREQ);
set_ddr_opp(VCORE_DVFS_OPP_2, DDR_OPP_UNREQ);
set_ddr_opp(VCORE_DVFS_OPP_3, DDR_OPP_UNREQ);
set_ddr_opp(VCORE_DVFS_OPP_4, DDR_OPP_UNREQ);
set_ddr_opp(VCORE_DVFS_OPP_5, DDR_OPP_UNREQ);
set_ddr_opp(VCORE_DVFS_OPP_6, DDR_OPP_UNREQ);
set_ddr_opp(VCORE_DVFS_OPP_7, DDR_OPP_UNREQ);
set_ddr_opp(VCORE_DVFS_OPP_8, DDR_OPP_UNREQ);
set_ddr_opp(VCORE_DVFS_OPP_9, DDR_OPP_UNREQ);
set_ddr_opp(VCORE_DVFS_OPP_10, DDR_OPP_UNREQ);
set_ddr_opp(VCORE_DVFS_OPP_11, DDR_OPP_UNREQ);
set_ddr_opp(VCORE_DVFS_OPP_12, DDR_OPP_UNREQ);
set_ddr_opp(VCORE_DVFS_OPP_13, DDR_OPP_UNREQ);
set_ddr_opp(VCORE_DVFS_OPP_14, DDR_OPP_UNREQ);
set_ddr_opp(VCORE_DVFS_OPP_15, DDR_OPP_UNREQ);
break;
}
}
@@ -0,0 +1,221 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <helio-dvfsrc_v2.h>
#include <helio-dvfsrc-opp.h>
#include <helio-dvfsrc-ipi.h>
static struct opp_profile opp_table[VCORE_DVFS_OPP_NUM];
static int vcore_dvfs_to_vcore_opp[VCORE_DVFS_OPP_NUM];
static int vcore_dvfs_to_ddr_opp[VCORE_DVFS_OPP_NUM];
static int vcore_uv_table[VCORE_OPP_NUM];
static int vcore_opp_to_pwrap_cmd[VCORE_OPP_NUM];
static int ddr_table[DDR_OPP_NUM];
/* ToDo: Copy Opp Table to AEE Dump */
int get_cur_vcore_dvfs_opp(void)
{
#if defined(VCOREFS_LEVEL_POSITIVE)
return __builtin_ffs(spm_get_dvfs_level());
#else
return VCORE_DVFS_OPP_NUM - __builtin_ffs(get_dvfs_final_level());
#endif
}
void set_opp_table(unsigned int vcore_dvfs_opp, int vcore_uv, int ddr_khz)
{
#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
struct qos_ipi_data qos_d;
qos_d.cmd = QOS_IPI_OPP_TABLE;
qos_d.u.opp_table.vcore_dvfs_opp = vcore_dvfs_opp;
qos_d.u.opp_table.vcore_uv = vcore_uv;
qos_d.u.opp_table.ddr_khz = ddr_khz;
qos_ipi_to_sspm_command(&qos_d, 4);
#endif
if (vcore_dvfs_opp >= VCORE_DVFS_OPP_NUM)
return;
opp_table[vcore_dvfs_opp].vcore_uv = vcore_uv;
opp_table[vcore_dvfs_opp].ddr_khz = ddr_khz;
}
void set_vcore_opp(unsigned int vcore_dvfs_opp, int vcore_opp)
{
#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
struct qos_ipi_data qos_d;
qos_d.cmd = QOS_IPI_VCORE_OPP;
qos_d.u.vcore_opp.vcore_dvfs_opp = vcore_dvfs_opp;
qos_d.u.vcore_opp.vcore_opp = vcore_opp;
qos_ipi_to_sspm_command(&qos_d, 3);
#endif
if (vcore_dvfs_opp >= VCORE_DVFS_OPP_NUM)
return;
vcore_dvfs_to_vcore_opp[vcore_dvfs_opp] = vcore_opp;
}
int get_vcore_opp(unsigned int opp)
{
if (opp >= VCORE_DVFS_OPP_NUM)
return 0;
return vcore_dvfs_to_vcore_opp[opp];
}
int get_vcore_uv(unsigned int opp)
{
if (opp >= VCORE_DVFS_OPP_NUM)
return 0;
return opp_table[opp].vcore_uv;
}
int get_cur_vcore_opp(void)
{
unsigned int idx;
if (!is_qos_enabled())
return VCORE_OPP_UNREQ;
idx = get_cur_vcore_dvfs_opp();
if (idx >= VCORE_DVFS_OPP_NUM)
return VCORE_OPP_UNREQ;
return vcore_dvfs_to_vcore_opp[idx];
}
int get_cur_vcore_uv(void)
{
unsigned int idx;
if (!is_qos_enabled())
return 0;
idx = get_cur_vcore_dvfs_opp();
if (idx >= VCORE_DVFS_OPP_NUM)
return 0;
return opp_table[idx].vcore_uv;
}
void set_ddr_opp(unsigned int vcore_dvfs_opp, int ddr_opp)
{
#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
struct qos_ipi_data qos_d;
qos_d.cmd = QOS_IPI_DDR_OPP;
qos_d.u.ddr_opp.vcore_dvfs_opp = vcore_dvfs_opp;
qos_d.u.ddr_opp.ddr_opp = ddr_opp;
qos_ipi_to_sspm_command(&qos_d, 3);
#endif
if (vcore_dvfs_opp >= VCORE_DVFS_OPP_NUM)
return;
vcore_dvfs_to_ddr_opp[vcore_dvfs_opp] = ddr_opp;
}
int get_ddr_opp(unsigned int opp)
{
if (opp >= VCORE_DVFS_OPP_NUM)
return 0;
return vcore_dvfs_to_ddr_opp[opp];
}
int get_ddr_khz(unsigned int opp)
{
if (opp >= VCORE_DVFS_OPP_NUM)
return 0;
return opp_table[opp].ddr_khz;
}
int get_cur_ddr_opp(void)
{
unsigned int idx;
if (!is_qos_enabled())
return DDR_OPP_UNREQ;
idx = get_cur_vcore_dvfs_opp();
if (idx >= VCORE_DVFS_OPP_NUM)
return DDR_OPP_UNREQ;
return vcore_dvfs_to_ddr_opp[idx];
}
int get_cur_ddr_khz(void)
{
unsigned int idx;
if (!is_qos_enabled())
return 0;
idx = get_cur_vcore_dvfs_opp();
if (idx >= VCORE_DVFS_OPP_NUM)
return 0;
return opp_table[idx].ddr_khz;
}
void set_vcore_uv_table(unsigned int vcore_opp, int vcore_uv)
{
if (vcore_opp >= VCORE_OPP_NUM)
return;
spm_dvfs_pwrap_cmd(get_pwrap_cmd(vcore_opp),
vcore_uv_to_pmic(vcore_uv));
vcore_uv_table[vcore_opp] = vcore_uv;
}
int get_opp_ddr_freq(unsigned int ddr_opp)
{
if (ddr_opp >= DDR_OPP_NUM)
return 0;
return ddr_table[ddr_opp];
}
void set_opp_ddr_freq(unsigned int ddr_opp, int ddr_freq)
{
if (ddr_opp >= DDR_OPP_NUM)
return;
ddr_table[ddr_opp] = ddr_freq;
}
int get_vcore_uv_table(unsigned int vcore_opp)
{
if (vcore_opp >= VCORE_OPP_NUM)
return 0;
return vcore_uv_table[vcore_opp];
}
void set_pwrap_cmd(unsigned int vcore_opp, int pwrap_cmd)
{
if (vcore_opp >= VCORE_OPP_NUM)
return;
vcore_opp_to_pwrap_cmd[vcore_opp] = pwrap_cmd;
}
int get_pwrap_cmd(unsigned int vcore_opp)
{
if (vcore_opp >= VCORE_OPP_NUM)
return 0;
return vcore_opp_to_pwrap_cmd[vcore_opp];
}
@@ -0,0 +1,310 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/soc/mediatek/mtk-pm-qos.h>
#include <linux/pm_qos.h>
#include <linux/sysfs.h>
#include <linux/mutex.h>
#include <helio-dvfsrc_v2.h>
#include <helio-dvfsrc-opp.h>
__weak void dvfsrc_enable_dvfs_freq_hopping(int gps_on)
{
pr_info("dummy dvfsrc enable freq hopping(%d)\n", gps_on);
}
__weak int dvfsrc_get_dvfs_freq_hopping_status(void)
{
pr_info("dummy dvfsrc get freq hopping status\n");
return 0;
}
static struct mtk_pm_qos_request dvfsrc_memory_bw_req;
static struct mtk_pm_qos_request dvfsrc_ddr_opp_req;
static struct mtk_pm_qos_request dvfsrc_vcore_opp_req;
static struct mtk_pm_qos_request dvfsrc_scp_vcore_req;
static struct mtk_pm_qos_request dvfsrc_power_model_ddr_req;
static struct mtk_pm_qos_request dvfsrc_power_model_vcore_req;
static struct mtk_pm_qos_request dvfsrc_vcore_dvfs_opp_force;
static DEFINE_MUTEX(dump_lock);
static ssize_t dvfsrc_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", is_dvfsrc_enabled());
}
static ssize_t dvfsrc_enable_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int val = 0;
if (kstrtoint(buf, 10, &val))
return -EINVAL;
helio_dvfsrc_enable(val);
return count;
}
static DEVICE_ATTR(dvfsrc_enable, 0644,
dvfsrc_enable_show, dvfsrc_enable_store);
static ssize_t dvfsrc_enable_flag_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%x\n", helio_dvfsrc_flag_get());
}
static ssize_t dvfsrc_enable_flag_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int val = 0;
if (kstrtoint(buf, 16, &val))
return -EINVAL;
helio_dvfsrc_flag_set(val);
return count;
}
static DEVICE_ATTR(dvfsrc_enable_flag, 0644,
dvfsrc_enable_flag_show, dvfsrc_enable_flag_store);
static ssize_t dvfsrc_req_memory_bw_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int val = 0;
if (kstrtoint(buf, 10, &val))
return -EINVAL;
mtk_pm_qos_update_request(&dvfsrc_memory_bw_req, val);
return count;
}
static DEVICE_ATTR(dvfsrc_req_memory_bw, 0200,
NULL, dvfsrc_req_memory_bw_store);
static ssize_t dvfsrc_req_ddr_opp_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int val = 0;
if (kstrtoint(buf, 10, &val))
return -EINVAL;
mtk_pm_qos_update_request(&dvfsrc_ddr_opp_req, val);
return count;
}
static DEVICE_ATTR(dvfsrc_req_ddr_opp, 0200,
NULL, dvfsrc_req_ddr_opp_store);
static ssize_t dvfsrc_req_vcore_opp_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int val = 0;
if (kstrtoint(buf, 10, &val))
return -EINVAL;
mtk_pm_qos_update_request(&dvfsrc_vcore_opp_req, val);
return count;
}
static DEVICE_ATTR(dvfsrc_req_vcore_opp, 0200,
NULL, dvfsrc_req_vcore_opp_store);
static ssize_t dvfsrc_req_scp_vcore_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int val = 0;
if (kstrtoint(buf, 10, &val))
return -EINVAL;
mtk_pm_qos_update_request(&dvfsrc_scp_vcore_req, val);
return count;
}
static DEVICE_ATTR(dvfsrc_req_scp_vcore, 0200,
NULL, dvfsrc_req_scp_vcore_store);
static ssize_t dvfsrc_req_power_model_ddr_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int val = 0;
if (kstrtoint(buf, 10, &val))
return -EINVAL;
mtk_pm_qos_update_request(&dvfsrc_power_model_ddr_req, val);
return count;
}
static DEVICE_ATTR(dvfsrc_req_power_model_ddr, 0200,
NULL, dvfsrc_req_power_model_ddr_store);
static ssize_t dvfsrc_req_power_model_vcore_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int val = 0;
if (kstrtoint(buf, 10, &val))
return -EINVAL;
mtk_pm_qos_update_request(&dvfsrc_power_model_vcore_req, val);
return count;
}
static DEVICE_ATTR(dvfsrc_req_power_model_vcore, 0200,
NULL, dvfsrc_req_power_model_vcore_store);
static ssize_t dvfsrc_set_vcore_uv_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int opp = 0, vcore_uv = 0;
if (sscanf(buf, "%d %d", &opp, &vcore_uv) != 2)
return -EINVAL;
set_vcore_uv_table(opp, vcore_uv);
dvfsrc_opp_table_init();
return count;
}
static DEVICE_ATTR(dvfsrc_set_vcore_uv, 0200,
NULL, dvfsrc_set_vcore_uv_store);
static ssize_t dvfsrc_force_vcore_dvfs_opp_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int val = 0;
if (kstrtoint(buf, 10, &val))
return -EINVAL;
mtk_pm_qos_update_request(&dvfsrc_vcore_dvfs_opp_force, val);
return count;
}
static DEVICE_ATTR(dvfsrc_force_vcore_dvfs_opp, 0200,
NULL, dvfsrc_force_vcore_dvfs_opp_store);
static ssize_t dvfsrc_opp_table_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct helio_dvfsrc *dvfsrc;
char *p = buf;
char *buff_end = p + PAGE_SIZE;
int i;
dvfsrc = dev_get_drvdata(dev);
if (!dvfsrc)
return sprintf(buf, "Failed to access dvfsrc\n");
mutex_lock(&dump_lock);
for (i = VCORE_DVFS_OPP_0; i < VCORE_DVFS_OPP_NUM; i++) {
p += snprintf(p, buff_end - p, "[OPP%-2d]: %-8u uv %-8u khz\n",
i, get_vcore_uv(i), get_ddr_khz(i));
}
p += snprintf(p, buff_end - p, "\n");
mutex_unlock(&dump_lock);
return p - buf;
}
static DEVICE_ATTR(dvfsrc_opp_table, 0444, dvfsrc_opp_table_show, NULL);
static ssize_t dvfsrc_dump_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
char *p = buf;
u32 index;
index = dvfsrc_dump_reg(p, PAGE_SIZE);
return index;
}
static DEVICE_ATTR(dvfsrc_dump, 0444, dvfsrc_dump_show, NULL);
static ssize_t dvfsrc_freq_hopping_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n",
dvfsrc_get_dvfs_freq_hopping_status());
}
static ssize_t dvfsrc_freq_hopping_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int val = 0;
if (kstrtoint(buf, 10, &val))
return -EINVAL;
dvfsrc_enable_dvfs_freq_hopping(val);
return count;
}
static DEVICE_ATTR(dvfsrc_freq_hopping, 0644,
dvfsrc_freq_hopping_show, dvfsrc_freq_hopping_store);
static struct attribute *helio_dvfsrc_attrs[] = {
&dev_attr_dvfsrc_enable.attr,
&dev_attr_dvfsrc_enable_flag.attr,
&dev_attr_dvfsrc_req_memory_bw.attr,
&dev_attr_dvfsrc_req_ddr_opp.attr,
&dev_attr_dvfsrc_req_vcore_opp.attr,
&dev_attr_dvfsrc_req_scp_vcore.attr,
&dev_attr_dvfsrc_req_power_model_ddr.attr,
&dev_attr_dvfsrc_req_power_model_vcore.attr,
&dev_attr_dvfsrc_force_vcore_dvfs_opp.attr,
&dev_attr_dvfsrc_set_vcore_uv.attr,
&dev_attr_dvfsrc_opp_table.attr,
&dev_attr_dvfsrc_dump.attr,
&dev_attr_dvfsrc_freq_hopping.attr,
NULL,
};
static struct attribute_group helio_dvfsrc_attr_group = {
.name = "helio-dvfsrc",
.attrs = helio_dvfsrc_attrs,
};
int helio_dvfsrc_add_interface(struct device *dev)
{
mtk_pm_qos_add_request(&dvfsrc_memory_bw_req, MTK_PM_QOS_MEMORY_BANDWIDTH_TEST,
MTK_PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE);
mtk_pm_qos_add_request(&dvfsrc_ddr_opp_req, MTK_PM_QOS_DDR_OPP,
MTK_PM_QOS_DDR_OPP_DEFAULT_VALUE);
mtk_pm_qos_add_request(&dvfsrc_vcore_opp_req, MTK_PM_QOS_VCORE_OPP,
MTK_PM_QOS_VCORE_OPP_DEFAULT_VALUE);
mtk_pm_qos_add_request(&dvfsrc_scp_vcore_req, MTK_PM_QOS_SCP_VCORE_REQUEST,
MTK_PM_QOS_SCP_VCORE_REQUEST_DEFAULT_VALUE);
mtk_pm_qos_add_request(&dvfsrc_power_model_ddr_req,
MTK_PM_QOS_POWER_MODEL_DDR_REQUEST,
MTK_PM_QOS_POWER_MODEL_DDR_REQUEST_DEFAULT_VALUE);
mtk_pm_qos_add_request(&dvfsrc_power_model_vcore_req,
MTK_PM_QOS_POWER_MODEL_VCORE_REQUEST,
MTK_PM_QOS_POWER_MODEL_VCORE_REQUEST_DEFAULT_VALUE);
mtk_pm_qos_add_request(&dvfsrc_vcore_dvfs_opp_force,
MTK_PM_QOS_VCORE_DVFS_FORCE_OPP,
MTK_PM_QOS_VCORE_DVFS_FORCE_OPP_DEFAULT_VALUE);
return sysfs_create_group(&dev->kobj, &helio_dvfsrc_attr_group);
}
void helio_dvfsrc_remove_interface(struct device *dev)
{
sysfs_remove_group(&dev->kobj, &helio_dvfsrc_attr_group);
}
@@ -0,0 +1,898 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/soc/mediatek/mtk-pm-qos.h>
#include <linux/sched.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/of.h>
#include <linux/sched/clock.h>
#include <mt-plat/aee.h>
#include <spm/mtk_spm.h>
#include <mtk_dramc.h>
#include "../helio-dvfsrc_v2.h"
#include "../mtk_dvfsrc_reg.h"
#include <linux/regulator/consumer.h>
#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
#include <sspm_ipi.h>
#include <sspm_ipi_pin.h>
#endif
static struct regulator *vcore_reg_id;
static struct helio_dvfsrc *dvfsrc;
static DEFINE_MUTEX(sw_req1_mutex);
static DEFINE_SPINLOCK(force_req_lock);
#define DVFSRC_REG(offset) (dvfsrc->regs + offset)
#define DVFSRC_SRAM_REG(offset) (dvfsrc->sram_regs + offset)
int __weak dram_steps_freq(unsigned int step)
{
pr_info("get dram steps_freq fail\n");
return -1;
}
u32 dvfsrc_read(u32 offset)
{
return readl(DVFSRC_REG(offset));
}
void dvfsrc_write(u32 offset, u32 val)
{
writel(val, DVFSRC_REG(offset));
}
#define dvfsrc_rmw(offset, val, mask, shift) \
dvfsrc_write(offset, (dvfsrc_read(offset) & ~(mask << shift)) \
| (val << shift))
u32 dvfsrc_sram_read(u32 offset)
{
if (!is_qos_enabled() || offset >= QOS_SRAM_MAX_SIZE)
return -1;
return readl(DVFSRC_SRAM_REG(offset));
}
void dvfsrc_sram_write(u32 offset, u32 val)
{
if (!is_qos_enabled() || offset >= QOS_SRAM_MAX_SIZE)
return;
writel(val, DVFSRC_SRAM_REG(offset));
}
u32 qos_sram_read(u32 offset)
{
return dvfsrc_sram_read(offset);
}
void qos_sram_write(u32 offset, u32 val)
{
dvfsrc_sram_write(offset, val);
}
static void dvfsrc_set_sw_req(int data, int mask, int shift)
{
dvfsrc_rmw(DVFSRC_SW_REQ, data, mask, shift);
}
static void dvfsrc_set_sw_req2(int data, int mask, int shift)
{
dvfsrc_rmw(DVFSRC_SW_REQ2, data, mask, shift);
}
static void dvfsrc_set_vcore_request(int data, int mask, int shift)
{
dvfsrc_rmw(DVFSRC_VCORE_REQUEST, data, mask, shift);
}
static void dvfsrc_get_timestamp(char *p)
{
int ret = 0;
u64 sec = local_clock();
u64 usec = do_div(sec, 1000000000);
do_div(usec, 1000000);
ret = sprintf(p, "%llu.%llu", sec, usec);
if (ret < 0)
pr_info("sprintf fail\n");
}
static void dvfsrc_set_force_start(int data)
{
dvfsrc->opp_forced = 1;
dvfsrc_get_timestamp(dvfsrc->force_start);
dvfsrc_write(DVFSRC_FORCE, data);
dvfsrc_rmw(DVFSRC_BASIC_CONTROL, 1,
FORCE_EN_TAR_MASK, FORCE_EN_TAR_SHIFT);
}
static void dvfsrc_set_force_end(void)
{
dvfsrc_write(DVFSRC_FORCE, 0);
}
static void dvfsrc_release_force(void)
{
dvfsrc_rmw(DVFSRC_BASIC_CONTROL, 0,
FORCE_EN_TAR_MASK, FORCE_EN_TAR_SHIFT);
dvfsrc_write(DVFSRC_FORCE, 0);
dvfsrc_get_timestamp(dvfsrc->force_end);
dvfsrc->opp_forced = 0;
}
static void dvfsrc_set_sw_bw(int type, int data)
{
switch (type) {
case MTK_PM_QOS_MEMORY_BANDWIDTH_TEST:
dvfsrc_write(DVFSRC_SW_BW_0, data / 100);
break;
case MTK_PM_QOS_CPU_MEMORY_BANDWIDTH:
dvfsrc_write(DVFSRC_SW_BW_1, data / 100);
break;
case MTK_PM_QOS_GPU_MEMORY_BANDWIDTH:
dvfsrc_write(DVFSRC_SW_BW_2, data / 100);
break;
case MTK_PM_QOS_MM_MEMORY_BANDWIDTH:
dvfsrc_write(DVFSRC_SW_BW_3, data / 100);
break;
case MTK_PM_QOS_OTHER_MEMORY_BANDWIDTH:
dvfsrc_write(DVFSRC_SW_BW_4, data / 100);
break;
default:
break;
}
}
int is_dvfsrc_opp_fixed(void)
{
int ret;
unsigned long flags;
if (!is_dvfsrc_enabled())
return 1;
if (!(dvfsrc_read(DVFSRC_BASIC_CONTROL) & 0x100))
return 1;
if (helio_dvfsrc_flag_get() != 0)
return 1;
spin_lock_irqsave(&force_req_lock, flags);
ret = is_opp_forced();
spin_unlock_irqrestore(&force_req_lock, flags);
return ret;
}
static int get_target_level(void)
{
return (dvfsrc_read(DVFSRC_LEVEL) & 0xFFFF);
}
u32 get_dvfs_final_level(void)
{
#if 0
u32 level_register = dvfsrc_read(DVFSRC_LEVEL);
u32 target_level, current_level;
target_level = level_register & 0xFFFF;
current_level = level_register >> CURRENT_LEVEL_SHIFT;
if (target_level != 0)
return min(current_level, target_level);
else
return current_level;
#endif
return dvfsrc_read(DVFSRC_LEVEL) >> CURRENT_LEVEL_SHIFT;
}
int get_sw_req_vcore_opp(void)
{
int opp = -1;
int sw_req = -1;
/* return opp 0, if dvfsrc not enable */
if (!is_dvfsrc_enabled())
return 0;
/* 1st get sw req opp no lock protect is ok*/
if (!is_opp_forced()) {
sw_req = (dvfsrc_read(DVFSRC_SW_REQ) >> VCORE_SW_AP_SHIFT);
sw_req = sw_req & VCORE_SW_AP_MASK;
sw_req = VCORE_OPP_NUM - sw_req - 1;
return sw_req; /* return sw_request, as vcore floor level*/
}
opp = get_cur_vcore_opp();
return opp; /* return opp , as vcore fixed level*/
}
static int commit_data(int type, int data, int check_spmfw)
{
int ret = 0;
int level = 16, opp = 16;
unsigned long flags;
#if defined(CONFIG_MTK_ENG_BUILD)
int opp_uv = 0;
int vcore_uv = 0;
#endif
if (!is_dvfsrc_enabled())
return ret;
if (check_spmfw)
mtk_spmfw_init(1, 0);
switch (type) {
case MTK_PM_QOS_MEMORY_BANDWIDTH_TEST:
case MTK_PM_QOS_CPU_MEMORY_BANDWIDTH:
case MTK_PM_QOS_GPU_MEMORY_BANDWIDTH:
case MTK_PM_QOS_MM_MEMORY_BANDWIDTH:
case MTK_PM_QOS_OTHER_MEMORY_BANDWIDTH:
if (data < 0)
data = 0;
dvfsrc_set_sw_bw(type, data);
break;
case MTK_PM_QOS_DDR_OPP:
mutex_lock(&sw_req1_mutex);
if (data >= DDR_OPP_NUM || data < 0)
data = DDR_OPP_NUM - 1;
opp = data;
level = DDR_OPP_NUM - data - 1;
dvfsrc_set_sw_req(level, EMI_SW_AP_MASK, EMI_SW_AP_SHIFT);
if (!is_opp_forced() && check_spmfw) {
udelay(1);
ret = dvfsrc_wait_for_completion(
get_cur_ddr_opp() <= opp,
DVFSRC_TIMEOUT);
}
mutex_unlock(&sw_req1_mutex);
break;
case MTK_PM_QOS_VCORE_OPP:
mutex_lock(&sw_req1_mutex);
if (data >= VCORE_OPP_NUM || data < 0)
data = VCORE_OPP_NUM - 1;
opp = data;
level = VCORE_OPP_NUM - data - 1;
mb(); /* make sure setting first */
dvfsrc_set_sw_req(level, VCORE_SW_AP_MASK, VCORE_SW_AP_SHIFT);
mb(); /* make sure checking then */
if (!is_opp_forced() && check_spmfw) {
udelay(1);
ret = dvfsrc_wait_for_completion(
(get_target_level() == 0),
DVFSRC_TIMEOUT);
udelay(1);
ret = dvfsrc_wait_for_completion(
get_cur_vcore_opp() <= opp,
DVFSRC_TIMEOUT);
}
#if defined(CONFIG_MTK_ENG_BUILD)
if (!is_opp_forced() && check_spmfw) {
if (vcore_reg_id) {
vcore_uv = regulator_get_voltage(vcore_reg_id);
opp_uv = get_vcore_uv_table(opp);
if (vcore_uv < opp_uv) {
pr_info("DVFS FAIL = %d %d 0x%x\n",
vcore_uv, opp_uv, dvfsrc_read(DVFSRC_LEVEL));
dvfsrc_dump_reg(NULL, 0);
aee_kernel_warning("DVFSRC",
"%s: failed.", __func__);
}
}
}
#endif
mutex_unlock(&sw_req1_mutex);
break;
case MTK_PM_QOS_SCP_VCORE_REQUEST:
if (data >= VCORE_OPP_NUM || data < 0)
data = 0;
opp = VCORE_OPP_NUM - data - 1;
level = data;
dvfsrc_set_vcore_request(level,
VCORE_SCP_GEAR_MASK, VCORE_SCP_GEAR_SHIFT);
if (!is_opp_forced() && check_spmfw) {
udelay(1);
ret = dvfsrc_wait_for_completion(
get_cur_vcore_opp() <= opp,
DVFSRC_TIMEOUT);
}
break;
case MTK_PM_QOS_POWER_MODEL_DDR_REQUEST:
if (data >= DDR_OPP_NUM || data < 0)
data = 0;
opp = DDR_OPP_NUM - data - 1;
level = data;
dvfsrc_set_sw_req2(level,
EMI_SW_AP2_MASK, EMI_SW_AP2_SHIFT);
break;
case MTK_PM_QOS_POWER_MODEL_VCORE_REQUEST:
if (data >= VCORE_OPP_NUM || data < 0)
data = 0;
opp = VCORE_OPP_NUM - data - 1;
level = data;
dvfsrc_set_sw_req2(level,
VCORE_SW_AP2_MASK, VCORE_SW_AP2_SHIFT);
break;
case MTK_PM_QOS_VCORE_DVFS_FORCE_OPP:
spin_lock_irqsave(&force_req_lock, flags);
if (data >= VCORE_DVFS_OPP_NUM || data < 0)
data = VCORE_DVFS_OPP_NUM;
opp = data;
level = VCORE_DVFS_OPP_NUM - data - 1;
if (opp == VCORE_DVFS_OPP_NUM) {
dvfsrc_release_force();
spin_unlock_irqrestore(&force_req_lock, flags);
break;
}
dvfsrc_set_force_start(1 << level);
if (check_spmfw) {
udelay(1);
ret = dvfsrc_wait_for_completion(
get_cur_vcore_dvfs_opp() == opp,
DVFSRC_TIMEOUT);
}
dvfsrc_set_force_end();
spin_unlock_irqrestore(&force_req_lock, flags);
break;
default:
break;
}
if (ret < 0) {
pr_info("%s: type: 0x%x, data: 0x%x, opp: %d, level: %d\n",
__func__, type, data, opp, level);
dvfsrc_dump_reg(NULL, 0);
aee_kernel_warning("DVFSRC", "%s: failed.", __func__);
}
return ret;
}
static void dvfsrc_restore(void)
{
int i;
for (i = MTK_PM_QOS_MEMORY_BANDWIDTH; i < MTK_PM_QOS_NUM_CLASSES; i++)
commit_data(i, mtk_pm_qos_request(i), 0);
}
void helio_dvfsrc_enable(int dvfsrc_en)
{
int ret = 0;
if (dvfsrc_en > 1 || dvfsrc_en < 0)
return;
if (!spm_load_firmware_status())
return;
if (dvfsrc->dvfsrc_enabled == dvfsrc_en && dvfsrc_en == 1) {
pr_info("DVFSRC already enabled\n");
return;
}
#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
helio_dvfsrc_sspm_ipi_init(dvfsrc_en);
#endif
dvfsrc->qos_enabled = 1;
dvfsrc->dvfsrc_enabled = dvfsrc_en;
dvfsrc->opp_forced = 0;
ret = sprintf(dvfsrc->force_start, "0");
if (ret < 0)
pr_info("sprintf fail\n");
ret = sprintf(dvfsrc->force_end, "0");
if (ret < 0)
pr_info("sprintf fail\n");
dvfsrc_restore();
if (dvfsrc_en)
dvfsrc_en |= (dvfsrc->dvfsrc_flag << 1);
mtk_spmfw_init(dvfsrc_en, 1);
}
void helio_dvfsrc_flag_set(int flag)
{
dvfsrc->dvfsrc_flag = flag;
}
int helio_dvfsrc_flag_get(void)
{
return dvfsrc->dvfsrc_flag;
}
void dvfsrc_opp_table_init(void)
{
int i;
int vcore_opp, ddr_opp;
for (i = 0; i < VCORE_DVFS_OPP_NUM; i++) {
vcore_opp = get_vcore_opp(i);
ddr_opp = get_ddr_opp(i);
if (vcore_opp == VCORE_OPP_UNREQ || ddr_opp == DDR_OPP_UNREQ) {
set_opp_table(i, 0, 0);
continue;
}
set_opp_table(i, get_vcore_uv_table(vcore_opp),
dram_steps_freq(ddr_opp) * 1000);
}
}
static int helio_dvfsrc_common_init(void)
{
dvfsrc_opp_level_mapping();
dvfsrc_opp_table_init();
if (!spm_load_firmware_status()) {
pr_info("SPM FIRMWARE IS NOT READY\n");
return -ENODEV;
}
return 0;
}
int dvfsrc_get_emi_bw(int type)
{
int ret = 0;
int i;
if (type == QOS_EMI_BW_TOTAL_AVE) {
for (i = 0; i < QOS_TOTAL_BW_BUF_SIZE; i++)
ret += (dvfsrc_sram_read(QOS_TOTAL_BW_BUF(i)) &
QOS_TOTAL_BW_BUF_BW_MASK);
ret /= QOS_TOTAL_BW_BUF_SIZE;
} else
ret = dvfsrc_sram_read(QOS_TOTAL_BW + 4 * type);
return ret;
}
int get_vcore_dvfs_level(void)
{
return dvfsrc_read(DVFSRC_LEVEL) >> CURRENT_LEVEL_SHIFT;
}
int is_qos_enabled(void)
{
if (dvfsrc)
return dvfsrc->qos_enabled == 1;
return 0;
}
int is_dvfsrc_enabled(void)
{
if (dvfsrc)
return dvfsrc->dvfsrc_enabled == 1;
return 0;
}
int is_opp_forced(void)
{
if (dvfsrc)
return dvfsrc->opp_forced == 1;
return 0;
}
static void get_mtk_pm_qos_info(char *p)
{
char timestamp[20];
dvfsrc_get_timestamp(timestamp);
p += sprintf(p, "%-24s: %d\n",
"MTK_PM_QOS_MEMORY_BW",
mtk_pm_qos_request(MTK_PM_QOS_MEMORY_BANDWIDTH_TEST));
p += sprintf(p, "%-24s: %d\n",
"MTK_PM_QOS_CPU_MEMORY_BW",
mtk_pm_qos_request(MTK_PM_QOS_CPU_MEMORY_BANDWIDTH));
p += sprintf(p, "%-24s: %d\n",
"MTK_PM_QOS_GPU_MEMORY_BW",
mtk_pm_qos_request(MTK_PM_QOS_GPU_MEMORY_BANDWIDTH));
p += sprintf(p, "%-24s: %d\n",
"MTK_PM_QOS_MM_MEMORY_BW",
mtk_pm_qos_request(MTK_PM_QOS_MM_MEMORY_BANDWIDTH));
p += sprintf(p, "%-24s: %d\n",
"MTK_PM_QOS_OTHER_MEMORY_BW",
mtk_pm_qos_request(MTK_PM_QOS_OTHER_MEMORY_BANDWIDTH));
p += sprintf(p, "%-24s: 0x%x\n",
"MTK_PM_QOS_DDR_OPP",
mtk_pm_qos_request(MTK_PM_QOS_DDR_OPP));
p += sprintf(p, "%-24s: 0x%x\n",
"MTK_PM_QOS_VCORE_OPP",
mtk_pm_qos_request(MTK_PM_QOS_VCORE_OPP));
p += sprintf(p, "%-24s: 0x%x\n",
"MTK_PM_QOS_SCP_VCORE_REQ",
mtk_pm_qos_request(MTK_PM_QOS_SCP_VCORE_REQUEST));
p += sprintf(p, "%-24s: 0x%x\n",
"MTK_PM_QOS_PM_DDR_REQ",
mtk_pm_qos_request(MTK_PM_QOS_POWER_MODEL_DDR_REQUEST));
p += sprintf(p, "%-24s: 0x%x\n",
"MTK_PM_QOS_PM_VCORE_REQ",
mtk_pm_qos_request(MTK_PM_QOS_POWER_MODEL_VCORE_REQUEST));
p += sprintf(p, "%-24s: 0x%x\n",
"MTK_PM_QOS_FORCE_OPP",
mtk_pm_qos_request(MTK_PM_QOS_VCORE_DVFS_FORCE_OPP));
p += sprintf(p, "%-24s: %d\n",
"EMI_BW_TOTAL_AVE",
dvfsrc_get_emi_bw(QOS_EMI_BW_TOTAL_AVE));
p += sprintf(p, "%-24s: %d\n",
"EMI_BW_TOTAL", dvfsrc_get_emi_bw(QOS_EMI_BW_TOTAL));
p += sprintf(p, "%-24s: %d\n",
"EMI_BW_TOTAL_W",
dvfsrc_get_emi_bw(QOS_EMI_BW_TOTAL_W));
p += sprintf(p, "%-24s: %d\n",
"EMI_BW_CPU", dvfsrc_get_emi_bw(QOS_EMI_BW_CPU));
p += sprintf(p, "%-24s: %d\n",
"EMI_BW_GPU", dvfsrc_get_emi_bw(QOS_EMI_BW_GPU));
p += sprintf(p, "%-24s: %d\n",
"EMI_BW_MM", dvfsrc_get_emi_bw(QOS_EMI_BW_MM));
p += sprintf(p, "%-24s: %d\n",
"EMI_BW_OTHER", dvfsrc_get_emi_bw(QOS_EMI_BW_OTHER));
p += sprintf(p, "%-24s: %s\n",
"Current Timestamp", timestamp);
p += sprintf(p, "%-24s: %s\n",
"Force Start Timestamp", dvfsrc->force_start);
p += sprintf(p, "%-24s: %s\n",
"Force End Timestamp", dvfsrc->force_end);
}
u32 dvfsrc_dump_reg(char *ptr, u32 count)
{
char buf[1024];
u32 index = 0;
memset(buf, '\0', sizeof(buf));
get_opp_info(buf);
if (ptr) {
index += scnprintf(&ptr[index], (count - index - 1),
"%s\n", buf);
} else
pr_info("%s\n", buf);
memset(buf, '\0', sizeof(buf));
get_dvfsrc_reg(buf);
if (ptr) {
index += scnprintf(&ptr[index], (count - index - 1),
"%s\n", buf);
} else
pr_info("%s\n", buf);
memset(buf, '\0', sizeof(buf));
get_dvfsrc_record(buf);
if (ptr) {
index += scnprintf(&ptr[index], (count - index - 1),
"%s\n", buf);
} else
pr_info("%s\n", buf);
memset(buf, '\0', sizeof(buf));
get_spm_reg(buf);
if (ptr) {
index += scnprintf(&ptr[index], (count - index - 1),
"%s\n", buf);
} else
pr_info("%s\n", buf);
memset(buf, '\0', sizeof(buf));
get_mtk_pm_qos_info(buf);
if (ptr) {
index += scnprintf(&ptr[index], (count - index - 1),
"%s\n", buf);
} else
pr_info("%s\n", buf);
return index;
}
void helio_dvfsrc_reg_config(struct reg_config *config)
{
int idx = 0;
while (config[idx].offset != -1) {
dvfsrc_write(config[idx].offset, config[idx].val);
idx++;
}
}
void helio_dvfsrc_sram_reg_init(void)
{
int i;
for (i = 0; i < 0x80; i += 4)
dvfsrc_sram_write(i, 0);
}
void dvfsrc_set_power_model_ddr_request(unsigned int level)
{
commit_data(MTK_PM_QOS_POWER_MODEL_DDR_REQUEST, level, 1);
}
static int mtk_pm_qos_memory_bw_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(MTK_PM_QOS_MEMORY_BANDWIDTH_TEST, l, 1);
return NOTIFY_OK;
}
static int mtk_pm_qos_cpu_memory_bw_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(MTK_PM_QOS_CPU_MEMORY_BANDWIDTH, l, 1);
return NOTIFY_OK;
}
static int mtk_pm_qos_gpu_memory_bw_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(MTK_PM_QOS_GPU_MEMORY_BANDWIDTH, l, 1);
return NOTIFY_OK;
}
static int mtk_pm_qos_mm_memory_bw_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(MTK_PM_QOS_MM_MEMORY_BANDWIDTH, l, 1);
return NOTIFY_OK;
}
static int mtk_pm_qos_other_memory_bw_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(MTK_PM_QOS_OTHER_MEMORY_BANDWIDTH, l, 1);
return NOTIFY_OK;
}
static int mtk_pm_qos_ddr_opp_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(MTK_PM_QOS_DDR_OPP, l, 1);
return NOTIFY_OK;
}
static int mtk_pm_qos_vcore_opp_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(MTK_PM_QOS_VCORE_OPP, l, 1);
return NOTIFY_OK;
}
static int mtk_pm_qos_scp_vcore_request_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(MTK_PM_QOS_SCP_VCORE_REQUEST, l, 1);
return NOTIFY_OK;
}
static int mtk_pm_qos_power_model_ddr_request_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(MTK_PM_QOS_POWER_MODEL_DDR_REQUEST, l, 1);
return NOTIFY_OK;
}
static int mtk_pm_qos_power_model_vcore_request_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(MTK_PM_QOS_POWER_MODEL_VCORE_REQUEST, l, 1);
return NOTIFY_OK;
}
static int mtk_pm_qos_vcore_dvfs_force_opp_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(MTK_PM_QOS_VCORE_DVFS_FORCE_OPP, l, 1);
return NOTIFY_OK;
}
static void mtk_pm_qos_notifier_register(void)
{
dvfsrc->mtk_pm_qos_memory_bw_nb.notifier_call =
mtk_pm_qos_memory_bw_notify;
dvfsrc->mtk_pm_qos_cpu_memory_bw_nb.notifier_call =
mtk_pm_qos_cpu_memory_bw_notify;
dvfsrc->mtk_pm_qos_gpu_memory_bw_nb.notifier_call =
mtk_pm_qos_gpu_memory_bw_notify;
dvfsrc->mtk_pm_qos_mm_memory_bw_nb.notifier_call =
mtk_pm_qos_mm_memory_bw_notify;
dvfsrc->mtk_pm_qos_other_memory_bw_nb.notifier_call =
mtk_pm_qos_other_memory_bw_notify;
dvfsrc->mtk_pm_qos_ddr_opp_nb.notifier_call =
mtk_pm_qos_ddr_opp_notify;
dvfsrc->mtk_pm_qos_vcore_opp_nb.notifier_call =
mtk_pm_qos_vcore_opp_notify;
dvfsrc->mtk_pm_qos_scp_vcore_request_nb.notifier_call =
mtk_pm_qos_scp_vcore_request_notify;
dvfsrc->mtk_pm_qos_power_model_ddr_request_nb.notifier_call =
mtk_pm_qos_power_model_ddr_request_notify;
dvfsrc->mtk_pm_qos_power_model_vcore_request_nb.notifier_call =
mtk_pm_qos_power_model_vcore_request_notify;
dvfsrc->mtk_pm_qos_vcore_dvfs_force_opp_nb.notifier_call =
mtk_pm_qos_vcore_dvfs_force_opp_notify;
mtk_pm_qos_add_notifier(MTK_PM_QOS_MEMORY_BANDWIDTH_TEST,
&dvfsrc->mtk_pm_qos_memory_bw_nb);
mtk_pm_qos_add_notifier(MTK_PM_QOS_CPU_MEMORY_BANDWIDTH,
&dvfsrc->mtk_pm_qos_cpu_memory_bw_nb);
mtk_pm_qos_add_notifier(MTK_PM_QOS_GPU_MEMORY_BANDWIDTH,
&dvfsrc->mtk_pm_qos_gpu_memory_bw_nb);
mtk_pm_qos_add_notifier(MTK_PM_QOS_MM_MEMORY_BANDWIDTH,
&dvfsrc->mtk_pm_qos_mm_memory_bw_nb);
mtk_pm_qos_add_notifier(MTK_PM_QOS_OTHER_MEMORY_BANDWIDTH,
&dvfsrc->mtk_pm_qos_other_memory_bw_nb);
mtk_pm_qos_add_notifier(MTK_PM_QOS_DDR_OPP,
&dvfsrc->mtk_pm_qos_ddr_opp_nb);
mtk_pm_qos_add_notifier(MTK_PM_QOS_VCORE_OPP,
&dvfsrc->mtk_pm_qos_vcore_opp_nb);
mtk_pm_qos_add_notifier(MTK_PM_QOS_SCP_VCORE_REQUEST,
&dvfsrc->mtk_pm_qos_scp_vcore_request_nb);
mtk_pm_qos_add_notifier(MTK_PM_QOS_POWER_MODEL_DDR_REQUEST,
&dvfsrc->mtk_pm_qos_power_model_ddr_request_nb);
mtk_pm_qos_add_notifier(MTK_PM_QOS_POWER_MODEL_VCORE_REQUEST,
&dvfsrc->mtk_pm_qos_power_model_vcore_request_nb);
mtk_pm_qos_add_notifier(MTK_PM_QOS_VCORE_DVFS_FORCE_OPP,
&dvfsrc->mtk_pm_qos_vcore_dvfs_force_opp_nb);
}
static int helio_dvfsrc_probe(struct platform_device *pdev)
{
int ret = 0;
struct resource *res;
struct device_node *np = pdev->dev.of_node;
dvfsrc = devm_kzalloc(&pdev->dev, sizeof(*dvfsrc), GFP_KERNEL);
if (!dvfsrc)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dvfsrc->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dvfsrc->regs))
return PTR_ERR(dvfsrc->regs);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
dvfsrc->sram_regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dvfsrc->sram_regs))
return PTR_ERR(dvfsrc->sram_regs);
platform_set_drvdata(pdev, dvfsrc);
ret = helio_dvfsrc_add_interface(&pdev->dev);
if (ret)
return ret;
mtk_pm_qos_notifier_register();
helio_dvfsrc_common_init();
vcore_reg_id = regulator_get(&pdev->dev, "vcore");
if (!vcore_reg_id)
pr_info("regulator_get vcore_reg_id failed\n");
if (of_property_read_u32(np, "dvfsrc_flag",
(u32 *) &dvfsrc->dvfsrc_flag))
dvfsrc->dvfsrc_flag = 0;
pr_info("dvfsrc_flag = %d\n", dvfsrc->dvfsrc_flag);
helio_dvfsrc_platform_init(dvfsrc);
pr_info("%s: init done\n", __func__);
return 0;
}
static int helio_dvfsrc_remove(struct platform_device *pdev)
{
helio_dvfsrc_remove_interface(&pdev->dev);
return 0;
}
static const struct of_device_id helio_dvfsrc_of_match[] = {
{ .compatible = "mediatek,dvfsrc" },
{ .compatible = "mediatek,dvfsrc-v2" },
{ },
};
MODULE_DEVICE_TABLE(of, helio_dvfsrc_of_match);
static __maybe_unused int helio_dvfsrc_suspend(struct device *dev)
{
int ret = 0;
if (dvfsrc->suspend) {
ret = dvfsrc->suspend(dvfsrc);
if (ret) {
pr_err("%s suspend failed\n", __func__);
return ret;
}
}
return 0;
}
static __maybe_unused int helio_dvfsrc_resume(struct device *dev)
{
int ret = 0;
if (dvfsrc->resume) {
ret = dvfsrc->resume(dvfsrc);
if (ret) {
pr_err("%s suspend failed\n", __func__);
return ret;
}
}
return ret;
}
static SIMPLE_DEV_PM_OPS(helio_dvfsrc_pm, helio_dvfsrc_suspend,
helio_dvfsrc_resume);
static struct platform_driver helio_dvfsrc_driver = {
.probe = helio_dvfsrc_probe,
.remove = helio_dvfsrc_remove,
.driver = {
.name = "helio-dvfsrc",
.pm = &helio_dvfsrc_pm,
.of_match_table = helio_dvfsrc_of_match,
},
};
static int __init helio_dvfsrc_init(void)
{
int ret = 0;
ret = platform_driver_register(&helio_dvfsrc_driver);
return ret;
}
late_initcall_sync(helio_dvfsrc_init)
static void __exit helio_dvfsrc_exit(void)
{
platform_driver_unregister(&helio_dvfsrc_driver);
}
module_exit(helio_dvfsrc_exit)
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Helio dvfsrc driver");
+84
View File
@@ -0,0 +1,84 @@
#
# Copyright (C) 2019 MediaTek Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License 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.
#
ccflags-y += -Werror -I$(srctree)/drivers/devfreq
ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/spm/$(MTK_PLATFORM)/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/dramc/$(MTK_PLATFORM)/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/include/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
ccflags-y += -I$(srctree)/drivers/watchdog/mediatek/include/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/dramc/$(MTK_PLATFORM)/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/sspm/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/sspm/$(MTK_PLATFORM)/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/emi/$(MTK_PLATFORM)/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/emi/submodule/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/smi/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/$(CONFIG_MTK_PLATFORM)/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/mmp/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/mmdvfs/
ifneq (,$(filter $(CONFIG_MACH_MT6781), y))
ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/qos/mt6781/
else
ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/qos/$(MTK_PLATFORM)/
endif
ccflags-y += -I$(srctree)/drivers/misc/mediatek/dbgtop/
ccflags-y += -I$(srctree)/drivers/watchdog/mediatek/include/
ccflags-y += -I$(srctree)/include/memory/mediatek/
ifneq (,$(filter $(CONFIG_MACH_MT6885), y))
ccflags-y += -DDVFSRC_SMC_CONTROL
endif
ifneq (,$(filter $(CONFIG_MACH_MT6873), y))
ccflags-y += -DDVFSRC_SMC_CONTROL
ccflags-y += -DDVFSRC_SUSPEND_SUPPORT
endif
ifneq (,$(filter $(CONFIG_MACH_MT6853), y))
ccflags-y += -DDVFSRC_SMC_CONTROL
ccflags-y += -DDVFSRC_SUSPEND_SUPPORT
endif
ifneq (,$(filter $(CONFIG_MACH_MT6893), y))
ccflags-y += -DDVFSRC_SMC_CONTROL
ccflags-y += -DDVFSRC_SUSPEND_SUPPORT
endif
ifneq (,$(filter $(CONFIG_MACH_MT6833), y))
ccflags-y += -DDVFSRC_SMC_CONTROL
ccflags-y += -DDVFSRC_SUSPEND_SUPPORT
endif
ifneq (,$(filter $(CONFIG_MACH_MT6877), y))
ccflags-y += -DDVFSRC_SMC_CONTROL
ccflags-y += -DDVFSRC_SUSPEND_SUPPORT
endif
ifneq (,$(filter $(CONFIG_MACH_MT6781), y))
ccflags-y += -DDVFSRC_SMC_CONTROL
ccflags-y += -DDVFSRC_SUSPEND_SUPPORT
endif
obj-y += helio-dvfsrc-qos.o helio-dvfsrc-sysfs.o helio-dvfsrc-opp.o
ifeq ($(CONFIG_MTK_DVFSRC_MT6781_PRETEST),y)
obj-$(CONFIG_MTK_DVFSRC_MT6781_PRETEST) += helio-dvfsrc-opp-v6781.o helio-dvfsrc-v6781.o helio-dvfsrc-ip-v2.o
else
obj-$(CONFIG_MACH_MT6785) += helio-dvfsrc-opp-mt6785.o helio-dvfsrc-mt6785.o helio-dvfsrc-ip-v2.o
endif
obj-$(CONFIG_MACH_MT6885) += helio-dvfsrc-opp-mt6885.o helio-dvfsrc-mt6885.o helio-dvfsrc-ip-v2.o helio-dvfsrc-smc-control.o
obj-$(CONFIG_MACH_MT6873) += helio-dvfsrc-opp-mt6873.o helio-dvfsrc-mt6873.o helio-dvfsrc-ip-v2.o helio-dvfsrc-smc-control.o
ifeq ($(CONFIG_MTK_DVFSRC_MT6833_PRETEST),y)
obj-$(CONFIG_MTK_DVFSRC_MT6833_PRETEST) += helio-dvfsrc-opp-mt6833.o helio-dvfsrc-mt6833.o helio-dvfsrc-ip-v2.o helio-dvfsrc-smc-control.o
else ifeq ($(CONFIG_MTK_DVFSRC_MT6877_PRETEST),y)
obj-$(CONFIG_MTK_DVFSRC_MT6877_PRETEST) += helio-dvfsrc-opp-v6877.o helio-dvfsrc-v6877.o helio-dvfsrc-ip-v2.o helio-dvfsrc-smc-control.o
else
obj-$(CONFIG_MACH_MT6853) += helio-dvfsrc-opp-mt6853.o helio-dvfsrc-mt6853.o helio-dvfsrc-ip-v2.o helio-dvfsrc-smc-control.o
endif
obj-$(CONFIG_MACH_MT6893) += helio-dvfsrc-opp-mt6893.o helio-dvfsrc-mt6893.o helio-dvfsrc-ip-v2.o helio-dvfsrc-smc-control.o
obj-$(CONFIG_MACH_MT6833) += helio-dvfsrc-opp-mt6833.o helio-dvfsrc-mt6833.o helio-dvfsrc-ip-v2.o helio-dvfsrc-smc-control.o
obj-$(CONFIG_MACH_MT6877) += helio-dvfsrc-opp-v6877.o helio-dvfsrc-v6877.o helio-dvfsrc-ip-v2.o helio-dvfsrc-smc-control.o
obj-$(CONFIG_MACH_MT6781) += helio-dvfsrc-opp-v6781.o helio-dvfsrc-v6781.o helio-dvfsrc-ip-v2.o helio-dvfsrc-smc-control.o
@@ -0,0 +1,975 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/fb.h>
#include <linux/notifier.h>
#include <linux/string.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/sched/clock.h>
#include <linux/regulator/consumer.h>
#ifdef CONFIG_MTK_WATCHDOG_COMMON
#include <ext_wd_drv.h>
#endif
#include <helio-dvfsrc-opp.h>
#include <helio-dvfsrc-ip-v2.h>
#include <spm/mtk_vcore_dvfs.h>
#include <linux/mutex.h>
#include <mt-plat/aee.h>
#if defined(CONFIG_MTK_QOS_V2)
#include <mtk_qos_sram.h>
#endif
#if IS_ENABLED(CONFIG_MMPROFILE)
#include <mmprofile.h>
struct dvfsrc_mmp_events_t {
mmp_event dvfs_event;
mmp_event level_change;
};
static struct dvfsrc_mmp_events_t dvfsrc_mmp_events;
#endif
static struct regulator *vcore_reg_id;
#define TIME_STAMP_SIZE 40
static DEFINE_SPINLOCK(force_req_lock);
static char timeout_stamp[TIME_STAMP_SIZE];
static char force_start_stamp[TIME_STAMP_SIZE];
static char force_end_stamp[TIME_STAMP_SIZE];
static char sys_stamp[TIME_STAMP_SIZE];
static char opp_forced;
#define dvfsrc_rmw(offset, val, mask, shift) \
dvfsrc_write(offset, (dvfsrc_read(offset) & ~(mask << shift)) \
| (val << shift))
int __weak mtk_rgu_cfg_dvfsrc(int enable)
{
return 0;
}
struct regulator *__weak dvfsrc_vcore_requlator(struct device *dev)
{
return NULL;
}
static void dvfsrc_set_sw_req(int data, int mask, int shift)
{
dvfsrc_rmw(DVFSRC_SW_REQ3, data, mask, shift);
}
static void dvfsrc_set_sw_req2(int data, int mask, int shift)
{
dvfsrc_rmw(DVFSRC_SW_REQ2, data, mask, shift);
}
static void dvfsrc_set_vcore_request(int data, int mask, int shift)
{
dvfsrc_rmw(DVFSRC_VCORE_REQUEST, data, mask, shift);
}
static void dvfsrc_get_timestamp(char *p)
{
int ret = 0;
u64 sec = local_clock();
u64 usec = do_div(sec, 1000000000);
do_div(usec, 1000);
ret = snprintf(p, TIME_STAMP_SIZE, "%llu.%06llu",
sec, usec);
if (ret < 0)
pr_info("dvfsrc snprintf fail\n");
}
static int is_dvfsrc_forced(void)
{
return opp_forced;
}
int is_dvfsrc_opp_fixed(void)
{
int ret;
unsigned long flags;
if (!is_dvfsrc_enabled())
return 1;
if (!(dvfsrc_read(DVFSRC_BASIC_CONTROL) & 0x100))
return 1;
if (helio_dvfsrc_flag_get() != 0)
return 1;
spin_lock_irqsave(&force_req_lock, flags);
ret = is_dvfsrc_forced();
spin_unlock_irqrestore(&force_req_lock, flags);
return ret;
}
static void dvfsrc_set_force_start(int data)
{
opp_forced = 1;
dvfsrc_get_timestamp(force_start_stamp);
dvfsrc_write(DVFSRC_TARGET_FORCE, data);
dvfsrc_rmw(DVFSRC_BASIC_CONTROL, 1,
FORCE_EN_TAR_MASK, FORCE_EN_TAR_SHIFT);
}
static void dvfsrc_set_force_end(void)
{
dvfsrc_write(DVFSRC_TARGET_FORCE, 0);
}
static void dvfsrc_release_force(void)
{
dvfsrc_rmw(DVFSRC_BASIC_CONTROL, 0,
FORCE_EN_TAR_MASK, FORCE_EN_TAR_SHIFT);
dvfsrc_write(DVFSRC_TARGET_FORCE, 0);
dvfsrc_get_timestamp(force_end_stamp);
opp_forced = 0;
}
static void dvfsrc_set_sw_bw(int type, int data)
{
data = data / 100;
if (data > 0xFF)
data = 0xFF;
switch (type) {
case DVFSRC_QOS_APU_MEMORY_BANDWIDTH:
dvfsrc_write(DVFSRC_SW_BW_0, data);
break;
case DVFSRC_QOS_CPU_MEMORY_BANDWIDTH:
dvfsrc_write(DVFSRC_SW_BW_1, data);
break;
case DVFSRC_QOS_GPU_MEMORY_BANDWIDTH:
dvfsrc_write(DVFSRC_SW_BW_2, data);
break;
case DVFSRC_QOS_MM_MEMORY_BANDWIDTH:
dvfsrc_write(DVFSRC_SW_BW_3, data);
break;
case DVFSRC_QOS_OTHER_MEMORY_BANDWIDTH:
dvfsrc_write(DVFSRC_SW_BW_4, data);
break;
default:
break;
}
}
void dvfsrc_enable_level_intr(int en)
{
if (en == 1)
dvfsrc_write(DVFSRC_INT_EN,
dvfsrc_read(DVFSRC_INT_EN) | (0x4));
else
dvfsrc_write(DVFSRC_INT_EN,
dvfsrc_read(DVFSRC_INT_EN) & ~(0x4));
}
int helio_dvfsrc_level_mask_get(void)
{
return dvfsrc_read(DVFSRC_LEVEL_MASK);
}
int helio_dvfsrc_level_mask_set(bool en, int level)
{
if (en)
dvfsrc_rmw(DVFSRC_LEVEL_MASK, 1, 1, level);
else
dvfsrc_rmw(DVFSRC_LEVEL_MASK, 0, 1, level);
return 0;
}
int get_cur_vcore_dvfs_opp(void)
{
int val = __builtin_ffs(dvfsrc_read(DVFSRC_CURRENT_LEVEL));
if (val == 0)
return VCORE_DVFS_OPP_NUM;
else
return val - 1;
}
int commit_data(int type, int data, int check_spmfw)
{
int ret = 0;
int level = 16, opp = 16;
unsigned long flags;
int opp_uv;
int vcore_uv;
if (!is_dvfsrc_enabled())
return ret;
if (check_spmfw)
mtk_spmfw_init(1, 0);
switch (type) {
case DVFSRC_QOS_APU_MEMORY_BANDWIDTH:
case DVFSRC_QOS_CPU_MEMORY_BANDWIDTH:
case DVFSRC_QOS_GPU_MEMORY_BANDWIDTH:
case DVFSRC_QOS_OTHER_MEMORY_BANDWIDTH:
case DVFSRC_QOS_MM_MEMORY_BANDWIDTH:
if (data < 0)
data = 0;
dvfsrc_set_sw_bw(type, data);
break;
case DVFSRC_QOS_DDR_OPP:
spin_lock_irqsave(&force_req_lock, flags);
if (data >= DDR_OPP_NUM || data < 0)
data = DDR_OPP_NUM - 1;
opp = data;
level = DDR_OPP_NUM - data - 1;
dvfsrc_set_sw_req(level, DDR_SW_AP_MASK, DDR_SW_AP_SHIFT);
if (!is_dvfsrc_forced() && check_spmfw) {
udelay(1);
dvfsrc_wait_for_completion(
dvfsrc_read(DVFSRC_TARGET_LEVEL) == 0,
DVFSRC_TIMEOUT);
udelay(1);
ret = dvfsrc_wait_for_completion(
get_cur_ddr_opp() <= opp,
DVFSRC_TIMEOUT);
}
spin_unlock_irqrestore(&force_req_lock, flags);
break;
case DVFSRC_QOS_VCORE_OPP:
spin_lock_irqsave(&force_req_lock, flags);
if (data >= VCORE_OPP_NUM)
data = VCORE_OPP_NUM - 1;
if (data < 0) {
pr_info("VCORE OPP = %d\n", data);
data = 0;
}
opp = data;
level = VCORE_OPP_NUM - data - 1;
dvfsrc_set_sw_req(level, VCORE_SW_AP_MASK, VCORE_SW_AP_SHIFT);
if (!is_dvfsrc_forced() && check_spmfw) {
udelay(1);
dvfsrc_wait_for_completion(
dvfsrc_read(DVFSRC_TARGET_LEVEL) == 0,
DVFSRC_TIMEOUT);
udelay(1);
ret = dvfsrc_wait_for_completion(
get_cur_vcore_opp() <= opp,
DVFSRC_TIMEOUT);
}
spin_unlock_irqrestore(&force_req_lock, flags);
if (!is_dvfsrc_forced() && check_spmfw) {
if (vcore_reg_id) {
vcore_uv = regulator_get_voltage(vcore_reg_id);
opp_uv = get_vcore_uv_table(opp);
if (vcore_uv < opp_uv) {
pr_info("DVFS FAIL= %d %d 0x%08x 0x%08x %08x\n",
vcore_uv, opp_uv,
dvfsrc_read(DVFSRC_CURRENT_LEVEL),
dvfsrc_read(DVFSRC_TARGET_LEVEL),
get_cur_vcore_dvfs_opp()); /* TODO */
aee_kernel_warning("DVFSRC",
"VCORE failed.",
__func__);
}
}
}
break;
case DVFSRC_QOS_SCP_VCORE_REQUEST:
spin_lock_irqsave(&force_req_lock, flags);
if (data >= VCORE_OPP_NUM || data < 0)
data = 0;
opp = VCORE_OPP_NUM - data - 1;
level = data;
dvfsrc_set_vcore_request(level,
VCORE_SCP_GEAR_MASK, VCORE_SCP_GEAR_SHIFT);
if (!is_dvfsrc_forced() && check_spmfw) {
udelay(1);
dvfsrc_wait_for_completion(
dvfsrc_read(DVFSRC_TARGET_LEVEL) == 0,
DVFSRC_TIMEOUT);
udelay(1);
ret = dvfsrc_wait_for_completion(
get_cur_vcore_opp() <= opp,
DVFSRC_TIMEOUT);
}
spin_unlock_irqrestore(&force_req_lock, flags);
break;
case DVFSRC_QOS_POWER_MODEL_DDR_REQUEST:
if (data >= DDR_OPP_NUM || data < 0)
data = 0;
opp = DDR_OPP_NUM - data - 1;
level = data;
dvfsrc_set_sw_req2(level,
DDR_SW_AP_MASK, DDR_SW_AP_SHIFT);
break;
case DVFSRC_QOS_POWER_MODEL_VCORE_REQUEST:
if (data >= VCORE_OPP_NUM || data < 0)
data = 0;
opp = VCORE_OPP_NUM - data - 1;
level = data;
dvfsrc_set_sw_req2(level,
VCORE_SW_AP_MASK, VCORE_SW_AP_SHIFT);
break;
case DVFSRC_QOS_VCORE_DVFS_FORCE_OPP:
spin_lock_irqsave(&force_req_lock, flags);
if (data >= VCORE_DVFS_OPP_NUM || data < 0)
data = VCORE_DVFS_OPP_NUM;
opp = data;
if (opp == VCORE_DVFS_OPP_NUM) {
dvfsrc_release_force();
spin_unlock_irqrestore(&force_req_lock, flags);
break;
}
level = opp;
dvfsrc_set_force_start(1 << level);
if (check_spmfw) {
ret = dvfsrc_wait_for_completion(
get_cur_vcore_dvfs_opp() == opp,
DVFSRC_TIMEOUT);
}
dvfsrc_set_force_end();
spin_unlock_irqrestore(&force_req_lock, flags);
break;
case DVFSRC_QOS_ISP_HRT_BANDWIDTH:
spin_lock_irqsave(&force_req_lock, flags);
if (data < 0)
data = 0;
dvfsrc_set_isp_hrt_bw(data);
opp = dvfsrc_calc_isp_hrt_opp(data);
if (!is_dvfsrc_forced() && check_spmfw) {
udelay(1);
dvfsrc_wait_for_completion(
dvfsrc_read(DVFSRC_TARGET_LEVEL) == 0,
DVFSRC_TIMEOUT);
ret = dvfsrc_wait_for_completion(
get_cur_ddr_opp() <= opp,
DVFSRC_TIMEOUT);
}
spin_unlock_irqrestore(&force_req_lock, flags);
break;
default:
break;
}
if (!(dvfsrc_read(DVFSRC_BASIC_CONTROL) & 0x100)) {
pr_info("DVFSRC OUT Disable\n");
return ret;
}
if (ret < 0) {
pr_info("%s: type: 0x%x, data: 0x%x, opp: %d, level: %d\n",
__func__, type, data, opp, level);
dvfsrc_dump_reg(NULL, 0);
aee_kernel_warning("DVFSRC", "%s: failed.", __func__);
}
return ret;
}
static void dvfsrc_level_change_dump(void)
{
#if IS_ENABLED(CONFIG_MMPROFILE)
mmprofile_log_ex(dvfsrc_mmp_events.level_change,
MMPROFILE_FLAG_PULSE, dvfsrc_read(DVFSRC_CURRENT_LEVEL),
dvfsrc_read(DVFSRC_TARGET_LEVEL));
#endif
}
static irqreturn_t helio_dvfsrc_interrupt(int irq, void *dev_id)
{
u32 val;
val = dvfsrc_read(DVFSRC_INT);
dvfsrc_write(DVFSRC_INT_CLR, val);
dvfsrc_write(DVFSRC_INT_CLR, 0x0);
if (val & 0x2) {
dvfsrc_write(DVFSRC_INT_EN,
dvfsrc_read(DVFSRC_INT_EN) & ~(0x2));
dvfsrc_get_timestamp(timeout_stamp);
}
if (val & 0x4)
dvfsrc_level_change_dump();
return IRQ_HANDLED;
}
static int dvfsrc_resume(struct helio_dvfsrc *dvfsrc)
{
#ifdef DVFSRC_SUSPEND_SUPPORT
dvfsrc_resume_cb(dvfsrc);
#endif
return 0;
}
static int dvfsrc_suspend(struct helio_dvfsrc *dvfsrc)
{
#ifdef DVFSRC_SUSPEND_SUPPORT
dvfsrc_suspend_cb(dvfsrc);
#endif
return 0;
}
int get_sw_req_vcore_opp(void)
{
int opp = -1;
int sw_req = -1;
int scp_req = VCORE_OPP_NUM - 1;
/* return opp 0, if dvfsrc not enable */
if (!is_dvfsrc_enabled())
return 0;
/* return opp 0, if dvfsrc pmqos not ready */
if (!is_dvfsrc_qos_enabled())
return 0;
/* 1st get sw req opp no lock protect is ok*/
if (!is_dvfsrc_forced()) {
sw_req = (dvfsrc_read(DVFSRC_SW_REQ3) >> VCORE_SW_AP_SHIFT);
sw_req = sw_req & VCORE_SW_AP_MASK;
sw_req = VCORE_OPP_NUM - sw_req - 1;
if (vcorefs_get_scp_req_status()) {
scp_req = ((dvfsrc_read(DVFSRC_VCORE_REQUEST)
>> VCORE_SCP_GEAR_SHIFT) & VCORE_SCP_GEAR_MASK);
scp_req = VCORE_OPP_NUM - scp_req - 1;
}
/* return sw_request, as vcore floor level*/
return (sw_req > scp_req) ? scp_req : sw_req;
}
opp = get_cur_vcore_opp();
return opp; /* return opp , as vcore fixed level*/
}
int helio_dvfsrc_config(struct helio_dvfsrc *dvfsrc)
{
struct platform_device *pdev = to_platform_device(dvfsrc->dev);
int ret;
vcore_reg_id = dvfsrc_vcore_requlator(&pdev->dev);
if (!vcore_reg_id)
pr_info("[DVFSRC] No Vcore regulator\n");
#if defined(CONFIG_MTK_WATCHDOG_COMMON) || defined(CONFIG_MTK_DBGTOP)
dvfsrc_latch_register(1);
#endif
helio_dvfsrc_enable(1);
helio_dvfsrc_platform_init(dvfsrc);
helio_dvfsrc_qos_init_done();
#if IS_ENABLED(CONFIG_MMPROFILE)
mmprofile_enable(1);
if (dvfsrc_mmp_events.dvfs_event == 0) {
dvfsrc_mmp_events.dvfs_event = mmprofile_register_event(
MMP_ROOT_EVENT, "VCORE_DVFS");
dvfsrc_mmp_events.level_change = mmprofile_register_event(
dvfsrc_mmp_events.dvfs_event, "level_change");
mmprofile_enable_event_recursive(
dvfsrc_mmp_events.dvfs_event, 1);
}
mmprofile_start(1);
#endif
dvfsrc->irq = platform_get_irq(pdev, 0);
ret = request_irq(dvfsrc->irq, helio_dvfsrc_interrupt
, IRQF_TRIGGER_NONE, "dvfsrc", dvfsrc);
if (ret)
pr_info("dvfsrc interrupt no use\n");
dvfsrc->resume = dvfsrc_resume;
dvfsrc->suspend = dvfsrc_suspend;
return 0;
}
/* FOR DEBUGGUNG */
u32 vcorefs_get_md_scenario(void)
{
return dvfsrc_read(DVFSRC_DEBUG_STA_0);
}
EXPORT_SYMBOL(vcorefs_get_md_scenario);
u32 vcorefs_get_total_emi_status(void)
{
u32 val;
val = dvfsrc_read(DVFSRC_DEBUG_STA_2);
val = (val >> DEBUG_STA2_EMI_TOTAL_SHIFT) & DEBUG_STA2_EMI_TOTAL_MASK;
return val;
}
u32 vcorefs_get_scp_req_status(void)
{
u32 val;
val = dvfsrc_read(DVFSRC_DEBUG_STA_2);
val = (val >> DEBUG_STA2_SCP_SHIFT) & DEBUG_STA2_SCP_MASK;
return val;
}
u32 vcorefs_get_md_emi_latency_status(void)
{
u32 val;
val = dvfsrc_read(DVFSRC_DEBUG_STA_2);
val = (val >> DEBUG_STA2_MD_EMI_LATENCY_SHIFT)
& DEBUG_STA2_MD_EMI_LATENCY_MASK;
return val;
}
u32 vcorefs_get_hifi_scenario(void)
{
u32 val;
val = dvfsrc_read(DVFSRC_DEBUG_STA_2);
val = (val >> DEBUG_STA2_HIFI_SCENARIO_SHIFT)
& DEBUG_STA2_HIFI_SCENARIO_MASK;
return val;
}
u32 vcorefs_get_hifi_vcore_status(void)
{
u32 hifi_scen;
hifi_scen = __builtin_ffs(vcorefs_get_hifi_scenario());
if (hifi_scen)
return (dvfsrc_read(DVFSRC_VCORE_REQUEST4) >>
((hifi_scen - 1) * 4)) & 0xF;
else
return 0;
}
u32 vcorefs_get_hifi_ddr_status(void)
{
u32 hifi_scen;
hifi_scen = __builtin_ffs(vcorefs_get_hifi_scenario());
if (hifi_scen)
return (dvfsrc_read(DVFSRC_DDR_REQUEST6) >>
((hifi_scen - 1) * 4)) & 0xF;
else
return 0;
}
#if defined(DVFSRC_IP_V2_1) || defined(DVFSRC_IP_V2_2)
u32 dvfsrc_get_md_bw(void)
{
#if defined(DVFSRC_IP_V2_1)
u32 last = dvfsrc_read(DVFSRC_LAST);
#endif
u32 is_turbo, is_urgent, md_scen;
u32 val;
#if defined(DVFSRC_IP_V2_1)
val = dvfsrc_read(DVFSRC_RECORD_0_6 + RECORD_SHIFT * last);
is_turbo =
(val >> MD_TURBO_SWITCH_SHIFT) & MD_TURBO_SWITCH_MASK;
#else
val = dvfsrc_read(DVFSRC_DEBUG_STA_0);
is_turbo =
(val >> DEBUG_MDTURBO_SHIFT) & DEBUG_MDTURBO_MASK;
#endif
val = dvfsrc_read(DVFSRC_DEBUG_STA_0);
is_urgent =
(val >> MD_EMI_URG_DEBUG_SHIFT) & MD_EMI_URG_DEBUG_MASK;
md_scen =
(val >> MD_EMI_VAL_DEBUG_SHIFT) & MD_EMI_VAL_DEBUG_MASK;
if (is_urgent) {
val = dvfsrc_read(DVSFRC_HRT_REQ_MD_URG);
if (is_turbo) {
val = (val >> MD_HRT_BW_URG1_SHIFT)
& MD_HRT_BW_URG1_MASK;
} else {
val = (val >> MD_HRT_BW_URG_SHIFT)
& MD_HRT_BW_URG_MASK;
}
} else {
u32 index, shift;
index = md_scen / 3;
shift = (md_scen % 3) * 10;
if (index > 10)
return 0;
if (index < 8) {
if (is_turbo)
val = dvfsrc_read(DVFSRC_HRT1_REQ_MD_BW_0
+ index * 4);
else
val = dvfsrc_read(DVFSRC_HRT_REQ_MD_BW_0
+ index * 4);
} else {
if (is_turbo)
val = dvfsrc_read(DVFSRC_HRT1_REQ_MD_BW_8
+ (index - 8) * 4);
else
val = dvfsrc_read(DVFSRC_HRT_REQ_MD_BW_8
+ (index - 8) * 4);
}
val = (val >> shift) & MD_HRT_BW_MASK;
}
return val;
}
#else
u32 dvfsrc_get_md_bw(void)
{
u32 val;
val = dvfsrc_read(DVFSRC_DEBUG_STA_3);
val = (val >> DEBUG_STA3_MD_HRT_BW_SHIFT)
& DEBUG_STA3_MD_HRT_BW_MASK;
return val;
}
#endif
#if defined(DVFSRC_IP_V2_1)
u32 vcorefs_get_hrt_bw_ddr(void)
{
u32 last, val;
last = dvfsrc_read(DVFSRC_LAST);
val = dvfsrc_read(DVFSRC_RECORD_0_6 + RECORD_SHIFT * last);
val = (val >> RECORD_HRT_BW_REQ_SHIFT) & RECORD_HRT_BW_REQ_MASK;
return val;
}
#elif defined(DVFSRC_IP_V2_2)
u32 vcorefs_get_hrt_bw_ddr(void)
{
u32 last, val;
last = dvfsrc_read(DVFSRC_LAST);
val = dvfsrc_read(DVFSRC_RECORD_0_5 + RECORD_SHIFT * last);
val = (val >> RECORD_HRT_BW_REQ_SHIFT) & RECORD_HRT_BW_REQ_MASK;
return val;
}
#else
u32 vcorefs_get_hrt_bw_ddr(void)
{
u32 val;
val = dvfsrc_read(DVFSRC_DEBUG_STA_4);
val = (val >> DEBUG_STA4_HRT_BW_REQ_SHIFT)
& DEBUG_STA4_HRT_BW_REQ_MASK;
return val;
}
u32 vcorefs_get_md_imp_ddr(void)
{
u32 val;
val = dvfsrc_read(DVFSRC_DEBUG_STA_4);
val = (val >> MD_EMI_MD_IMP_SHIFT)
& MD_EMI_MD_IMP_MASK;
return val;
}
#endif
#if defined(DVFSRC_IP_V2_1)
u32 vcorefs_get_md_rising_ddr(void)
{
u32 last, val;
last = dvfsrc_read(DVFSRC_LAST);
val = dvfsrc_read(DVFSRC_RECORD_0_6 + RECORD_SHIFT * last);
val = (val >> RECORD_MD_DDR_LATENCY_REQ) &
RECORD_MD_DDR_LATENCY_MASK;
return val;
}
u32 vcorefs_get_hifi_rising_ddr(void)
{
u32 last, val;
last = dvfsrc_read(DVFSRC_LAST);
val = dvfsrc_read(DVFSRC_RECORD_0_6 + RECORD_SHIFT * last);
val = (val >> RECORD_HIFI_DDR_LATENCY_REQ) &
RECORD_HIFI_DDR_LATENCY_MASK;
return val;
}
#else
u32 vcorefs_get_md_rising_ddr(void)
{
u32 val;
val = dvfsrc_read(DVFSRC_DEBUG_STA_0);
val = (val >> DEBUG_MD_RIS_DDR_SHIFT)
& DEBUG_MD_RIS_DDR_MASK;
return val;
}
u32 vcorefs_get_hifi_rising_ddr(void)
{
u32 val;
val = dvfsrc_read(DVFSRC_DEBUG_STA_0);
val = (val >> DEBUG_HIFI_RIS_DDR_SHIFT) &
DEBUG_HIFI_RIS_DDR_MASK;
return val;
}
u32 vcorefs_get_md_scenario_ddr(void)
{
u32 is_turbo, is_urgent, md_scen;
u32 sta0;
u32 val;
is_turbo =
(dvfsrc_read(DVFSRC_MD_TURBO) == 0) ? 1 : 0;
sta0 = dvfsrc_read(DVFSRC_DEBUG_STA_0);
is_urgent =
(sta0 >> MD_EMI_URG_DEBUG_SHIFT) & MD_EMI_URG_DEBUG_MASK;
md_scen =
(sta0 >> MD_EMI_VAL_DEBUG_SHIFT) & MD_EMI_VAL_DEBUG_MASK;
if (is_urgent)
val = is_turbo ? dvfsrc_read(DVFSRC_95MD_SCEN_BW4) : 0;
else {
u32 index, shift;
index = md_scen / 8;
shift = (md_scen % 8) * 4;
if (md_scen > 31)
return 0;
if (is_turbo)
val = dvfsrc_read(DVFSRC_95MD_SCEN_BW0_T
+ index * 4);
else
val = dvfsrc_read(DVFSRC_95MD_SCEN_BW0
+ index * 4);
val = (val >> shift) & DDR_SW_AP_MASK;
}
return val;
}
#endif
void get_dvfsrc_reg(char *p)
{
char timestamp[TIME_STAMP_SIZE];
dvfsrc_get_timestamp(timestamp);
p += sprintf(p, "%-16s: 0x%08x\n",
"BASIC_CONTROL",
dvfsrc_read(DVFSRC_BASIC_CONTROL));
p += sprintf(p,
"%-16s: %08x, %08x, %08x, %08x\n",
"SW_REQ 1~4",
dvfsrc_read(DVFSRC_SW_REQ1),
dvfsrc_read(DVFSRC_SW_REQ2),
dvfsrc_read(DVFSRC_SW_REQ3),
dvfsrc_read(DVFSRC_SW_REQ4));
p += sprintf(p,
"%-16s: %08x, %08x, %08x, %08x\n",
"SW_REQ 5~8",
dvfsrc_read(DVFSRC_SW_REQ5),
dvfsrc_read(DVFSRC_SW_REQ6),
dvfsrc_read(DVFSRC_SW_REQ7),
dvfsrc_read(DVFSRC_SW_REQ8));
p += sprintf(p, "%-16s: %d, %d, %d, %d, %d\n",
"SW_BW_0~4",
dvfsrc_read(DVFSRC_SW_BW_0),
dvfsrc_read(DVFSRC_SW_BW_1),
dvfsrc_read(DVFSRC_SW_BW_2),
dvfsrc_read(DVFSRC_SW_BW_3),
dvfsrc_read(DVFSRC_SW_BW_4));
#if !defined(DVFSRC_IP_V2_1) && !defined(DVFSRC_IP_V2_2)
p += sprintf(p, "%-16s: %d, %d\n",
"SW_BW_5~6",
dvfsrc_read(DVFSRC_SW_BW_5),
dvfsrc_read(DVFSRC_SW_BW_6));
#endif
p += sprintf(p, "%-16s: 0x%08x\n",
"ISP_HRT",
dvfsrc_read(DVFSRC_ISP_HRT));
p += sprintf(p, "%-16s: 0x%08x, 0x%08x, 0x%08x\n",
"DEBUG_STA",
dvfsrc_read(DVFSRC_DEBUG_STA_0),
dvfsrc_read(DVFSRC_DEBUG_STA_1),
dvfsrc_read(DVFSRC_DEBUG_STA_2));
p += sprintf(p, "%-16s: 0x%08x\n",
"DVFSRC_INT",
dvfsrc_read(DVFSRC_INT));
p += sprintf(p, "%-16s: 0x%08x\n",
"DVFSRC_INT_EN",
dvfsrc_read(DVFSRC_INT_EN));
p += sprintf(p, "%-16s: 0x%02x\n",
"TOTAL_EMI_REQ",
vcorefs_get_total_emi_status());
p += sprintf(p, "%-16s: %d\n",
"DDR_QOS_REQ",
dvfsrc_get_ddr_qos());
p += sprintf(p, "%-16s: %d\n",
"HIFI_VCORE_REQ",
vcorefs_get_hifi_vcore_status());
p += sprintf(p, "%-16s: %d\n",
"HIFI_DDR_REQ",
vcorefs_get_hifi_ddr_status());
p += sprintf(p, "%-16s: 0x%08x\n",
"MD_HRT_BW",
dvfsrc_get_md_bw());
p += sprintf(p, "%-16s: %d\n",
"HIFI_RISINGREQ",
vcorefs_get_hifi_rising_ddr());
p += sprintf(p, "%-16s: %d\n",
"MD_RISING_REQ",
vcorefs_get_md_rising_ddr());
#if !defined(DVFSRC_IP_V2_1) && !defined(DVFSRC_IP_V2_2)
p += sprintf(p, "%-16s: %d\n",
"HRT_BW_REQ",
vcorefs_get_hrt_bw_ddr());
#endif
#if !defined(DVFSRC_IP_V2_1)
p += sprintf(p, "%-16s: %d\n",
"MD_SCEN_DDR",
vcorefs_get_md_scenario_ddr());
#endif
p += sprintf(p, "%-16s: %d , 0x%08x\n",
"SCP_VCORE_REQ",
vcorefs_get_scp_req_status(),
dvfsrc_read(DVFSRC_VCORE_REQUEST));
p += sprintf(p, "%-16s: 0x%08x\n",
"CURRENT_LEVEL",
dvfsrc_read(DVFSRC_CURRENT_LEVEL));
p += sprintf(p, "%-16s: 0x%08x\n",
"TARGET_LEVEL",
dvfsrc_read(DVFSRC_TARGET_LEVEL));
p += sprintf(p, "%-16s: %.40s\n",
"Current Tstamp", timestamp);
p += sprintf(p, "%-16s: %.40s\n",
"ForceS Tstamp", force_start_stamp);
p += sprintf(p, "%-16s: %.40s\n",
"ForceE Tstamp", force_end_stamp);
p += sprintf(p, "%-16s: %.40s\n",
"Timeout Tstamp", timeout_stamp);
p += sprintf(p, "%-16s: %.40s\n",
"Sys Tstamp", sys_stamp);
}
void get_dvfsrc_record(char *p)
{
int i, debug_reg;
p += sprintf(p, "%-17s: 0x%08x\n",
"DVFSRC_LAST",
dvfsrc_read(DVFSRC_LAST));
for (i = 0; i < 8; i++) {
debug_reg = DVFSRC_RECORD_0_0 + (i * RECORD_SHIFT);
p += sprintf(p, "[%d]%-14s: %08x,%08x,%08x,%08x\n",
i,
"DVFSRC_REC 0~3",
dvfsrc_read(debug_reg),
dvfsrc_read(debug_reg + 0x4),
dvfsrc_read(debug_reg + 0x8),
dvfsrc_read(debug_reg + 0xC));
#if defined(DVFSRC_IP_V2_1)
p += sprintf(p, "[%d]%-14s: %08x,%08x,%08x\n",
i,
"DVFSRC_REC 4~6",
dvfsrc_read(debug_reg + 0x10),
dvfsrc_read(debug_reg + 0x14),
dvfsrc_read(debug_reg + 0x18));
#else
p += sprintf(p, "[%d]%-14s: %08x,%08x,%08x,%08x\n",
i,
"DVFSRC_REC 4~7",
dvfsrc_read(debug_reg + 0x10),
dvfsrc_read(debug_reg + 0x14),
dvfsrc_read(debug_reg + 0x18),
dvfsrc_read(debug_reg + 0x1C));
#endif
}
}
/* met profile function */
/* met profile table */
static unsigned int met_vcorefs_info[INFO_MAX];
static char *met_info_name[INFO_MAX] = {
"OPP",
"FREQ",
"VCORE",
"x__SPM_LEVEL",
};
int vcorefs_get_num_opp(void)
{
return VCORE_DVFS_OPP_NUM;
}
EXPORT_SYMBOL(vcorefs_get_num_opp);
int vcorefs_get_opp_info_num(void)
{
return INFO_MAX;
}
EXPORT_SYMBOL(vcorefs_get_opp_info_num);
char **vcorefs_get_opp_info_name(void)
{
return met_info_name;
}
EXPORT_SYMBOL(vcorefs_get_opp_info_name);
unsigned int *vcorefs_get_opp_info(void)
{
met_vcorefs_info[INFO_OPP_IDX] = get_cur_vcore_dvfs_opp();
met_vcorefs_info[INFO_FREQ_IDX] = get_cur_ddr_khz();
met_vcorefs_info[INFO_VCORE_IDX] = get_cur_vcore_uv();
met_vcorefs_info[INFO_SPM_LEVEL_IDX] = get_cur_vcore_dvfs_opp();
return met_vcorefs_info;
}
EXPORT_SYMBOL(vcorefs_get_opp_info);
@@ -0,0 +1,41 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_IP_V2_H
#define __HELIO_DVFSRC_IP_V2_H
#include <helio-dvfsrc-qos.h>
#include <mtk_dvfsrc_reg_v2.h>
#if defined(DVFSRC_SMC_CONTROL)
#include <mtk_dvfsrc_smc_reg.h>
#endif
#define DVFSRC_TIMEOUT 1000
extern u32 vcorefs_get_total_emi_status(void);
extern u32 vcorefs_get_scp_req_status(void);
extern u32 vcorefs_get_md_emi_latency_status(void);
extern u32 vcorefs_get_hifi_scenario(void);
extern u32 vcorefs_get_hifi_vcore_status(void);
extern u32 vcorefs_get_hifi_ddr_status(void);
extern u32 dvfsrc_get_md_bw(void);
extern u32 vcorefs_get_md_rising_ddr(void);
extern u32 vcorefs_get_hifi_rising_ddr(void);
extern u32 vcorefs_get_hrt_bw_ddr(void);
extern u32 vcorefs_get_md_scenario(void);
extern u32 vcorefs_get_md_scenario_ddr(void);
extern u32 vcorefs_get_md_imp_ddr(void);
extern u32 dvfsrc_calc_isp_hrt_opp(int data);
extern u32 dvfsrc_get_ddr_qos(void);
extern void dvfsrc_set_isp_hrt_bw(int data);
extern void helio_dvfsrc_platform_init(struct helio_dvfsrc *dvfsrc);
extern struct regulator *dvfsrc_vcore_requlator(struct device *dev);
extern int dvfsrc_latch_register(int enable);
extern void dvfsrc_suspend_cb(struct helio_dvfsrc *dvfsrc);
extern void dvfsrc_resume_cb(struct helio_dvfsrc *dvfsrc);
#endif /* __HELIO_DVFSRC_IP_V2_H */
@@ -0,0 +1,555 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/platform_device.h>
#ifdef CONFIG_MTK_DRAMC
#include <mtk_dramc.h>
#endif
#ifdef CONFIG_MTK_EMI
#include <mt_emi_api.h>
#endif
#include <mt-plat/upmu_common.h>
#include "helio-dvfsrc-ip-v2.h"
#include <helio-dvfsrc-opp.h>
#include <helio-dvfsrc-mt6785.h>
#include <mt-plat/mtk_devinfo.h>
#include <linux/regulator/consumer.h>
#include "mmdvfs_pmqos.h"
#ifdef CONFIG_MTK_WATCHDOG
#include <ext_wd_drv.h>
#endif
#define DVFSRC_1600_FLOOR
#define AUTOK_ENABLE
static struct reg_config dvfsrc_init_configs[][128] = {
{
{ DVFSRC_TIMEOUT_NEXTREQ, 0x0000002B },
{ DVFSRC_INT_EN, 0x00000002 },
{ DVFSRC_QOS_EN, 0x0000407C },
{ DVFSRC_VCORE_REQUEST4, 0x21110000 },
#ifdef DVFSRC_1600_FLOOR
{ DVFSRC_DDR_REQUEST, 0x00004322 },
#else
{ DVFSRC_DDR_REQUEST, 0x00004321 },
#endif
{ DVFSRC_DDR_REQUEST3, 0x00000055 },
#ifdef DVFSRC_1600_FLOOR
{ DVFSRC_DDR_REQUEST5, 0x00322000 },
#else
{ DVFSRC_DDR_REQUEST5, 0x00321000 },
#endif
{ DVFSRC_DDR_REQUEST7, 0x54000000 },
{ DVFSRC_DDR_REQUEST8, 0x55000000 },
{ DVFSRC_DDR_QOS0, 0x00000019 },
{ DVFSRC_DDR_QOS1, 0x00000026 },
{ DVFSRC_DDR_QOS2, 0x00000033 },
{ DVFSRC_DDR_QOS3, 0x0000004C },
{ DVFSRC_DDR_QOS4, 0x00000066 },
{ DVFSRC_DDR_QOS5, 0x00000077 },
{ DVFSRC_DDR_QOS6, 0x00000077 },
{ DVFSRC_95MD_SCEN_BW0_T, 0x22222220 },
{ DVFSRC_95MD_SCEN_BW1_T, 0x22222222 },
{ DVFSRC_95MD_SCEN_BW2_T, 0x22222222 },
{ DVFSRC_95MD_SCEN_BW3_T, 0x52222222 },
{ DVFSRC_95MD_SCEN_BW4, 0x00000005 },
{ DVFSRC_HRT_REQ_UNIT, 0x0000011E },
{ DVSFRC_HRT_REQ_MD_URG, 0x0000D3D3 },
{ DVFSRC_HRT_REQ_MD_BW_0, 0x00200802 },
{ DVFSRC_HRT_REQ_MD_BW_1, 0x00200800 },
{ DVFSRC_HRT_REQ_MD_BW_2, 0x00200002 },
{ DVFSRC_HRT_REQ_MD_BW_3, 0x00200802 },
{ DVFSRC_HRT_REQ_MD_BW_4, 0x00400802 },
{ DVFSRC_HRT_REQ_MD_BW_5, 0x00601404 },
{ DVFSRC_HRT_REQ_MD_BW_6, 0x00902008 },
{ DVFSRC_HRT_REQ_MD_BW_7, 0x00E0380E },
{ DVFSRC_HRT_REQ_MD_BW_8, 0x00000000 },
{ DVFSRC_HRT_REQ_MD_BW_9, 0x00000000 },
{ DVFSRC_HRT_REQ_MD_BW_10, 0x00034C00 },
{ DVFSRC_HRT1_REQ_MD_BW_0, 0x0360D836 },
{ DVFSRC_HRT1_REQ_MD_BW_1, 0x0360D800 },
{ DVFSRC_HRT1_REQ_MD_BW_2, 0x03600036 },
{ DVFSRC_HRT1_REQ_MD_BW_3, 0x0360D836 },
{ DVFSRC_HRT1_REQ_MD_BW_4, 0x0360D836 },
{ DVFSRC_HRT1_REQ_MD_BW_5, 0x0360D836 },
{ DVFSRC_HRT1_REQ_MD_BW_6, 0x0360D836 },
{ DVFSRC_HRT1_REQ_MD_BW_7, 0x0360D836 },
{ DVFSRC_HRT1_REQ_MD_BW_8, 0x00000000 },
{ DVFSRC_HRT1_REQ_MD_BW_9, 0x00000000 },
{ DVFSRC_HRT1_REQ_MD_BW_10, 0x00034C00 },
{ DVFSRC_HRT_HIGH, 0x070804B0 },
{ DVFSRC_HRT_HIGH_1, 0x11830B80 },
{ DVFSRC_HRT_HIGH_2, 0x18A618A6 },
{ DVFSRC_HRT_HIGH_3, 0x000018A6 },
{ DVFSRC_HRT_LOW, 0x070704AF },
{ DVFSRC_HRT_LOW_1, 0x11820B7F },
{ DVFSRC_HRT_LOW_2, 0x18A518A5 },
{ DVFSRC_HRT_LOW_3, 0x000018A5 },
#ifdef DVFSRC_1600_FLOOR
{ DVFSRC_HRT_REQUEST, 0x05554322 },
#else
{ DVFSRC_HRT_REQUEST, 0x05554321 },
#endif
{ DVFSRC_EMI_MON_DEBOUNCE_TIME, 0x4C4C0AB0 },
{ DVFSRC_DDR_ADD_REQUEST, 0x05543210 },
{ DVFSRC_EMI_ADD_REQUEST, 0x03333210 },
{ DVFSRC_LEVEL_LABEL_0_1, 0x40225032 },
{ DVFSRC_LEVEL_LABEL_2_3, 0x20223012 },
{ DVFSRC_LEVEL_LABEL_4_5, 0x40211012 },
{ DVFSRC_LEVEL_LABEL_6_7, 0x20213011 },
{ DVFSRC_LEVEL_LABEL_8_9, 0x30101011 },
{ DVFSRC_LEVEL_LABEL_10_11, 0x10102000 },
{ DVFSRC_LEVEL_LABEL_12_13, 0x00000000 },
{ DVFSRC_LEVEL_LABEL_14_15, 0x00000000 },
{ DVFSRC_CURRENT_FORCE, 0x00000001 },
{ DVFSRC_BASIC_CONTROL, 0x7599404B },
{ DVFSRC_BASIC_CONTROL, 0x7599014B },
{ DVFSRC_CURRENT_FORCE, 0x00000000 },
{ -1, 0 },
},
/* NULL */
{
{ -1, 0 },
},
};
#define dvfsrc_rmw(offset, val, mask, shift) \
dvfsrc_write(offset, (dvfsrc_read(offset) & ~(mask << shift)) \
| (val << shift))
u32 dvfsrc_get_ddr_qos(void)
{
unsigned int qos_total_bw = dvfsrc_read(DVFSRC_SW_BW_0) +
dvfsrc_read(DVFSRC_SW_BW_1) +
dvfsrc_read(DVFSRC_SW_BW_2) +
dvfsrc_read(DVFSRC_SW_BW_3) +
dvfsrc_read(DVFSRC_SW_BW_4);
if (qos_total_bw < 0x19)
return 0;
#ifdef DVFSRC_1600_FLOOR
else if (qos_total_bw < 0x26)
return 2;
#else
else if (qos_total_bw < 0x26)
return 1;
#endif
else if (qos_total_bw < 0x33)
return 2;
else if (qos_total_bw < 0x4c)
return 3;
else if (qos_total_bw < 0x66)
return 4;
else
return 5;
}
static int dvfsrc_get_emi_mon_gear(void)
{
unsigned int total_bw_status;
int i;
total_bw_status = vcorefs_get_total_emi_status() & 0x1F;
for (i = 4; i >= 0 ; i--) {
if ((total_bw_status >> i) > 0) {
#ifdef DVFSRC_1600_FLOOR
if (i == 0)
return i + 2;
else
return i + 1;
#else
return i + 1;
#endif
}
}
return 0;
}
static u32 dvfsrc_calc_hrt_opp(int data)
{
if (data < 0x04B0)
return DDR_OPP_5;
#ifdef DVFSRC_1600_FLOOR
else if (data < 0x0708)
return DDR_OPP_3;
#else
else if (data < 0x0708)
return DDR_OPP_4;
#endif
else if (data < 0x0B80)
return DDR_OPP_3;
else if (data < 0x1183)
return DDR_OPP_2;
else if (data < 0x18A6)
return DDR_OPP_1;
else
return DDR_OPP_0;
}
int dvfsrc_latch_register(int enable)
{
#ifdef CONFIG_MTK_WATCHDOG
return mtk_rgu_cfg_dvfsrc(enable);
#else
return 0;
#endif
}
void dvfsrc_set_isp_hrt_bw(int data)
{
data = (data + 29) / 30;
if (data > 0x3FF)
data = 0x3FF;
dvfsrc_write(DVFSRC_ISP_HRT, data);
}
u32 dvfsrc_calc_isp_hrt_opp(int data)
{
return dvfsrc_calc_hrt_opp(((data + 29) / 30) * 30);
}
struct regulator *dvfsrc_vcore_requlator(struct device *dev)
{
return regulator_get(dev, "vcore");
}
#ifdef AUTOK_ENABLE
__weak int emmc_autok(void)
{
pr_info("NOT SUPPORT EMMC AUTOK\n");
return 0;
}
__weak int sd_autok(void)
{
pr_info("NOT SUPPORT SD AUTOK\n");
return 0;
}
__weak int sdio_autok(void)
{
pr_info("NOT SUPPORT SDIO AUTOK\n");
return 0;
}
void begin_autok_task(void)
{
/* notify MM DVFS for msdc autok start */
mmdvfs_prepare_action(MMDVFS_PREPARE_CALIBRATION_START);
}
void finish_autok_task(void)
{
/* check if dvfs force is released */
int force = mtk_pm_qos_request(MTK_PM_QOS_VCORE_DVFS_FORCE_OPP);
/* notify MM DVFS for msdc autok finish */
mmdvfs_prepare_action(MMDVFS_PREPARE_CALIBRATION_END);
if (force >= 0 && force < 13)
pr_info("autok task not release force opp: %d\n", force);
}
static void dvfsrc_autok_manager(void)
{
int r = 0;
begin_autok_task();
r = emmc_autok();
pr_info("EMMC autok done: %s\n", (r == 0) ? "Yes" : "No");
r = sd_autok();
pr_info("SD autok done: %s\n", (r == 0) ? "Yes" : "No");
r = sdio_autok();
pr_info("SDIO autok done: %s\n", (r == 0) ? "Yes" : "No");
finish_autok_task();
}
#endif
void helio_dvfsrc_platform_pre_init(struct helio_dvfsrc *dvfsrc)
{
}
void helio_dvfsrc_platform_init(struct helio_dvfsrc *dvfsrc)
{
int spmfw_idx = 0;
struct reg_config *config;
int idx = 0;
config = dvfsrc_init_configs[spmfw_idx];
while (config[idx].offset != -1) {
dvfsrc_write(config[idx].offset, config[idx].val);
idx++;
}
#ifdef AUTOK_ENABLE
dvfsrc_autok_manager();
#endif
}
int vcore_pmic_to_uv(int pmic_val)
{
return __vcore_pmic_to_uv(pmic_val);
}
int vcore_uv_to_pmic(int vcore_uv)
{
return __vcore_uv_to_pmic(vcore_uv);
}
void get_opp_info(char *p)
{
#if defined(CONFIG_FPGA_EARLY_PORTING) || !defined(CONFIG_MTK_PMIC_COMMON)
int pmic_val = 0;
int vsram_val = 0;
#else
int pmic_val = pmic_get_register_value(PMIC_VCORE_ADDR);
int vsram_val = pmic_get_register_value(PMIC_VSRAM_OTHERS_ADDR);
#endif
#ifdef CONFIG_MTK_DRAMC
int ddr_khz = get_dram_data_rate() * 1000;
#else
int ddr_khz = 0;
#endif
int vcore_uv = vcore_pmic_to_uv(pmic_val);
int vsram_uv = vsram_pmic_to_uv(vsram_val);
p += sprintf(p, "%-10s: %-8u uv (PMIC: 0x%x)\n",
"Vcore", vcore_uv, vcore_uv_to_pmic(vcore_uv));
p += sprintf(p, "%-10s: %-8u uv (PMIC: 0x%x)\n",
"Vsram", vsram_uv, vsram_uv_to_pmic(vsram_uv));
p += sprintf(p, "%-10s: %-8u khz\n", "DDR", ddr_khz);
}
/* met profile table */
static unsigned int met_vcorefs_src[SRC_MAX];
static char *met_src_name[SRC_MAX] = {
"MD2SPM",
"SRC_DDR_OPP",
"DDR__SW_REQ1_SPM",
"DDR__SW_REQ2_CM",
"DDR__SW_REQ3_PMQOS",
"DDR__SW_REQ4_MD",
"DDR__SW_REQ8_MCUSYS",
"DDR__QOS_BW",
"DDR__EMI_TOTAL",
"DDR__HRT_BW",
"DDR__HIFI",
"DDR__HIFI_LATENCY",
"DDR__MD_LATENCY",
"DDR__MD_DDR",
"SRC_VCORE_OPP",
"VCORE__SW_REQ3_PMQOS",
"VCORE__SCP",
"VCORE__HIFI",
"SCP_REQ",
"PMQOS_TATOL",
"PMQOS_BW0",
"PMQOS_BW1",
"PMQOS_BW2",
"PMQOS_BW3",
"PMQOS_BW4",
"TOTAL_EMI_BW",
"HRT_MD_BW",
"HRT_DISP_BW",
"HRT_ISP_BW",
"MD_SCENARIO",
"HIFI_SCENARIO_IDX",
"MD_EMI_LATENCY",
};
/* met profile function */
int vcorefs_get_src_req_num(void)
{
return SRC_MAX;
}
EXPORT_SYMBOL(vcorefs_get_src_req_num);
char **vcorefs_get_src_req_name(void)
{
return met_src_name;
}
EXPORT_SYMBOL(vcorefs_get_src_req_name);
static void vcorefs_get_src_ddr_req(void)
{
unsigned int sw_req;
met_vcorefs_src[DDR_OPP_IDX] =
get_cur_ddr_opp();
sw_req = dvfsrc_read(DVFSRC_SW_REQ1);
met_vcorefs_src[DDR_SW_REQ1_SPM_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
sw_req = dvfsrc_read(DVFSRC_SW_REQ2);
met_vcorefs_src[DDR_SW_REQ2_CM_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
sw_req = dvfsrc_read(DVFSRC_SW_REQ3);
met_vcorefs_src[DDR_SW_REQ3_PMQOS_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
sw_req = dvfsrc_read(DVFSRC_SW_REQ4);
met_vcorefs_src[DDR_SW_REQ4_MD_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
sw_req = dvfsrc_read(DVFSRC_SW_REQ8);
met_vcorefs_src[DDR_SW_REQ8_MCUSYS_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
met_vcorefs_src[DDR_QOS_BW_IDX] =
dvfsrc_get_ddr_qos();
met_vcorefs_src[DDR_EMI_TOTAL_IDX] =
dvfsrc_get_emi_mon_gear();
met_vcorefs_src[DDR_HRT_BW_IDX] =
vcorefs_get_hrt_bw_ddr();
met_vcorefs_src[DDR_HIFI_IDX] =
vcorefs_get_hifi_ddr_status();
met_vcorefs_src[DDR_HIFI_LATENCY_IDX] =
vcorefs_get_hifi_rising_ddr();
met_vcorefs_src[DDR_MD_LATENCY_IDX] =
vcorefs_get_md_rising_ddr();
met_vcorefs_src[DDR_MD_DDR_IDX] =
vcorefs_get_md_scenario_ddr();
}
static void vcorefs_get_src_vcore_req(void)
{
u32 sw_req;
u32 scp_en;
scp_en = vcorefs_get_scp_req_status();
met_vcorefs_src[VCORE_OPP_IDX] =
get_cur_vcore_opp();
sw_req = dvfsrc_read(DVFSRC_SW_REQ3);
met_vcorefs_src[VCORE_SW_REQ3_PMQOS_IDX] =
(sw_req >> VCORE_SW_AP_SHIFT) & VCORE_SW_AP_MASK;
if (scp_en) {
sw_req = dvfsrc_read(DVFSRC_VCORE_REQUEST);
met_vcorefs_src[VCORE_SCP_IDX] =
(sw_req >> VCORE_SCP_GEAR_SHIFT) & VCORE_SCP_GEAR_MASK;
} else
met_vcorefs_src[VCORE_SCP_IDX] = 0;
met_vcorefs_src[VCORE_HIFI_IDX] =
vcorefs_get_hifi_vcore_status();
}
static void vcorefs_get_src_misc_info(void)
{
#ifdef CONFIG_MTK_EMI
unsigned int total_bw_last = (get_emi_bwvl(0) & 0x7F) * 813;
#endif
u32 qos_bw0, qos_bw1, qos_bw2, qos_bw3, qos_bw4;
qos_bw0 = dvfsrc_read(DVFSRC_SW_BW_0);
qos_bw1 = dvfsrc_read(DVFSRC_SW_BW_1);
qos_bw2 = dvfsrc_read(DVFSRC_SW_BW_2);
qos_bw3 = dvfsrc_read(DVFSRC_SW_BW_3);
qos_bw4 = dvfsrc_read(DVFSRC_SW_BW_4);
met_vcorefs_src[SRC_MD2SPM_IDX] =
vcorefs_get_md_scenario();
met_vcorefs_src[SRC_SCP_REQ_IDX] =
vcorefs_get_scp_req_status();
met_vcorefs_src[SRC_PMQOS_TATOL_IDX] =
qos_bw0 + qos_bw1 + qos_bw2 + qos_bw3 + qos_bw4;
met_vcorefs_src[SRC_PMQOS_BW0_IDX] =
qos_bw0;
met_vcorefs_src[SRC_PMQOS_BW1_IDX] =
qos_bw1;
met_vcorefs_src[SRC_PMQOS_BW2_IDX] =
qos_bw2;
met_vcorefs_src[SRC_PMQOS_BW3_IDX] =
qos_bw3;
met_vcorefs_src[SRC_PMQOS_BW4_IDX] =
qos_bw4;
#ifdef CONFIG_MTK_EMI
met_vcorefs_src[SRC_TOTAL_EMI_BW_IDX] =
total_bw_last;
#endif
met_vcorefs_src[SRC_HRT_MD_BW_IDX] =
dvfsrc_get_md_bw();
met_vcorefs_src[SRC_HRT_ISP_BW_IDX] =
dvfsrc_read(DVFSRC_ISP_HRT);
met_vcorefs_src[SRC_MD_SCENARIO_IDX] =
vcorefs_get_md_scenario();
met_vcorefs_src[SRC_HIFI_SCENARIO_IDX] =
vcorefs_get_hifi_scenario();
met_vcorefs_src[SRC_MD_EMI_LATENCY_IDX] =
vcorefs_get_md_emi_latency_status();
}
unsigned int *vcorefs_get_src_req(void)
{
vcorefs_get_src_ddr_req();
vcorefs_get_src_vcore_req();
vcorefs_get_src_misc_info();
vcorefs_trace_qos();
return met_vcorefs_src;
}
EXPORT_SYMBOL(vcorefs_get_src_req);
int get_cur_ddr_ratio(void)
{
int idx;
if (!is_dvfsrc_enabled())
return 0;
idx = get_cur_vcore_dvfs_opp();
if (idx >= VCORE_DVFS_OPP_NUM)
return 0;
if ((get_ddr_opp(idx) < DDR_OPP_3) || (idx == 10))
return 8;
else
return 4;
}
EXPORT_SYMBOL(get_cur_ddr_ratio);
@@ -0,0 +1,70 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_MT6785_H
#define __HELIO_DVFSRC_MT6785_H
#include <mach/upmu_hw.h>
#define PMIC_VCORE_ADDR PMIC_RG_BUCK_VCORE_VOSEL
#define VCORE_BASE_UV 400000
#define VCORE_STEP_UV 6250
#define PMIC_VSRAM_OTHERS_ADDR PMIC_RG_LDO_VSRAM_OTHERS_VOSEL
#define VSRAM_BASE_UV 500000
#define VSRAM_STEP_UV 6250
/* PMIC */
#define __vcore_pmic_to_uv(pmic) \
(((pmic) * VCORE_STEP_UV) + VCORE_BASE_UV)
#define __vcore_uv_to_pmic(uv) /* pmic >= uv */ \
((((uv) - VCORE_BASE_UV) + (VCORE_STEP_UV - 1)) / VCORE_STEP_UV)
/* VSRAM_OTHERS */
#define vsram_pmic_to_uv(pmic) \
(((pmic) * VSRAM_STEP_UV) + VSRAM_BASE_UV)
#define vsram_uv_to_pmic(uv) /* pmic >= uv */ \
((((uv) - VSRAM_BASE_UV) + (VSRAM_STEP_UV - 1)) / VSRAM_STEP_UV)
enum met_src_index {
SRC_MD2SPM_IDX = 0,
DDR_OPP_IDX,
DDR_SW_REQ1_SPM_IDX,
DDR_SW_REQ2_CM_IDX,
DDR_SW_REQ3_PMQOS_IDX,
DDR_SW_REQ4_MD_IDX,
DDR_SW_REQ8_MCUSYS_IDX,
DDR_QOS_BW_IDX,
DDR_EMI_TOTAL_IDX,
DDR_HRT_BW_IDX,
DDR_HIFI_IDX,
DDR_HIFI_LATENCY_IDX,
DDR_MD_LATENCY_IDX,
DDR_MD_DDR_IDX,
VCORE_OPP_IDX,
VCORE_SW_REQ3_PMQOS_IDX,
VCORE_SCP_IDX,
VCORE_HIFI_IDX,
SRC_SCP_REQ_IDX,
SRC_PMQOS_TATOL_IDX,
SRC_PMQOS_BW0_IDX,
SRC_PMQOS_BW1_IDX,
SRC_PMQOS_BW2_IDX,
SRC_PMQOS_BW3_IDX,
SRC_PMQOS_BW4_IDX,
SRC_TOTAL_EMI_BW_IDX,
SRC_HRT_MD_BW_IDX,
SRC_HRT_DISP_BW_IDX,
SRC_HRT_ISP_BW_IDX,
SRC_MD_SCENARIO_IDX,
SRC_HIFI_SCENARIO_IDX,
SRC_MD_EMI_LATENCY_IDX,
SRC_MAX
};
#endif /* __HELIO_DVFSRC_MT6785_H */
@@ -0,0 +1,781 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/fb.h>
#include <linux/platform_device.h>
#ifdef CONFIG_MEDIATEK_DRAMC
#include <dramc.h>
#endif
#ifdef CONFIG_MTK_EMI
#include <mt_emi_api.h>
#endif
#include <mt-plat/upmu_common.h>
#include "helio-dvfsrc-ip-v2.h"
#include <helio-dvfsrc-opp.h>
#include <helio-dvfsrc-mt6833.h>
#include <mt-plat/mtk_devinfo.h>
#include <linux/regulator/consumer.h>
#include "mmdvfs_pmqos.h"
#include <linux/sysfs.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/sched/clock.h>
#include <dbgtop.h>
#define DVFSRC_FB_MD_TABLE_SWITCH
/* #define AUTOK_ENABLE */
#define dvfsrc_rmw(offset, val, mask, shift) \
dvfsrc_write(offset, (dvfsrc_read(offset) & ~(mask << shift)) \
| (val << shift))
static struct reg_config dvfsrc_init_configs[][128] = {
{
{ DVFSRC_HRT_REQ_UNIT, 0x0000001E },
{ DVFSRC_DEBOUNCE_TIME, 0x00001965 },
{ DVFSRC_TIMEOUT_NEXTREQ, 0x00000015 },
{ DVFSRC_LEVEL_MASK, 0x00EEE000 },
{ DVFSRC_SW_REQ8, 0x00001000 },
{ DVFSRC_DDR_QOS0, 0x0000000c },
{ DVFSRC_DDR_QOS1, 0x00000019 },
{ DVFSRC_DDR_QOS2, 0x00000026 },
{ DVFSRC_DDR_QOS3, 0x00000033 },
{ DVFSRC_DDR_QOS4, 0x0000003B },
{ DVFSRC_DDR_QOS5, 0x0000004C },
{ DVFSRC_DDR_QOS6, 0x00000066 },
{ DVFSRC_LEVEL_LABEL_0_1, 0x60537063 },
{ DVFSRC_LEVEL_LABEL_2_3, 0x50436052 },
{ DVFSRC_LEVEL_LABEL_4_5, 0x50415042 },
{ DVFSRC_LEVEL_LABEL_6_7, 0x40324033 },
{ DVFSRC_LEVEL_LABEL_8_9, 0x30234031 },
{ DVFSRC_LEVEL_LABEL_10_11, 0x30213022 },
{ DVFSRC_LEVEL_LABEL_12_13, 0x20133020 },
{ DVFSRC_LEVEL_LABEL_14_15, 0x20112012 },
{ DVFSRC_LEVEL_LABEL_16_17, 0x10232010 },
{ DVFSRC_LEVEL_LABEL_18_19, 0x10211022 },
{ DVFSRC_LEVEL_LABEL_20_21, 0x00031020 },
{ DVFSRC_LEVEL_LABEL_22_23, 0x00010002 },
{ DVFSRC_LEVEL_LABEL_24_25, 0x00000000 },
{ DVFSRC_MD_LATENCY_IMPROVE, 0x00000050 },
{ DVFSRC_HRT_BW_BASE, 0x00000004 },
{ DVSFRC_HRT_REQ_MD_URG, 0x000D20D2 },
{ DVFSRC_HRT_REQ_MD_BW_0, 0x00200802 },
{ DVFSRC_HRT_REQ_MD_BW_1, 0x00200802 },
{ DVFSRC_HRT_REQ_MD_BW_2, 0x00200800 },
{ DVFSRC_HRT_REQ_MD_BW_3, 0x00400802 },
{ DVFSRC_HRT_REQ_MD_BW_4, 0x00601404 },
{ DVFSRC_HRT_REQ_MD_BW_5, 0x00D02C09 },
{ DVFSRC_HRT_REQ_MD_BW_6, 0x00000012 },
{ DVFSRC_HRT_REQ_MD_BW_7, 0x00000024 },
{ DVFSRC_HRT_REQ_MD_BW_8, 0x00000000 },
{ DVFSRC_HRT_REQ_MD_BW_9, 0x00000000 },
{ DVFSRC_HRT_REQ_MD_BW_10, 0x00034800 },
{ DVFSRC_HRT1_REQ_MD_BW_0, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_1, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_2, 0x04B12C00 },
{ DVFSRC_HRT1_REQ_MD_BW_3, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_4, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_5, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_6, 0x0000004B },
{ DVFSRC_HRT1_REQ_MD_BW_7, 0x0000005C },
{ DVFSRC_HRT1_REQ_MD_BW_8, 0x00000000 },
{ DVFSRC_HRT1_REQ_MD_BW_9, 0x00000000 },
{ DVFSRC_HRT1_REQ_MD_BW_10, 0x00034800 },
{ DVFSRC_95MD_SCEN_BW0_T, 0x30333330 },
{ DVFSRC_95MD_SCEN_BW1_T, 0x33333333 },
{ DVFSRC_95MD_SCEN_BW2_T, 0x00500555 },
{ DVFSRC_95MD_SCEN_BW3_T, 0x70000000 },
{ DVFSRC_95MD_SCEN_BW0, 0x10111110 },
{ DVFSRC_95MD_SCEN_BW1, 0x11111111},
{ DVFSRC_95MD_SCEN_BW2, 0x00300333 },
{ DVFSRC_95MD_SCEN_BW3, 0x70000000 },
{ DVFSRC_95MD_SCEN_BW4, 0x00000007 },
{ DVFSRC_RSRV_5, 0x00000001 },
{ DVFSRC_DDR_REQUEST, 0x00004321 },
{ DVFSRC_DDR_REQUEST3, 0x00000765 },
{ DVFSRC_DDR_ADD_REQUEST, 0x76543210 },
{ DVFSRC_HRT_REQUEST, 0x77654321 },
{ DVFSRC_DDR_REQUEST5, 0x54321000 },
{ DVFSRC_DDR_REQUEST7, 0x76000000 },
{ DVFSRC_EMI_MON_DEBOUNCE_TIME, 0x4C2D0000 },
{ DVFSRC_DDR_REQUEST6, 0x76543210 },
{ DVFSRC_VCORE_USER_REQ, 0x00010A29 },
{ DVFSRC_HRT_HIGH_3, 0x18A618A6 },
{ DVFSRC_HRT_HIGH_2, 0x11830D69 },
{ DVFSRC_HRT_HIGH_1, 0x0B800708 },
{ DVFSRC_HRT_HIGH, 0x04B000C8 },
{ DVFSRC_HRT_LOW_3, 0x18A518A5 },
{ DVFSRC_HRT_LOW_2, 0x11820D68 },
{ DVFSRC_HRT_LOW_1, 0x0B7F0707 },
{ DVFSRC_HRT_LOW, 0x04AF00C7 },
{ DVFSRC_BASIC_CONTROL_3, 0x00000006 },
{ DVFSRC_INT_EN, 0x00000002 },
{ DVFSRC_QOS_EN, 0x0000407C },
{ DVFSRC_CURRENT_FORCE, 0x00000001 },
{ DVFSRC_BASIC_CONTROL, 0x6718444B },
{ DVFSRC_BASIC_CONTROL, 0x6718054B },
{ DVFSRC_CURRENT_FORCE, 0x00000000 },
{ -1, 0 },
},
/* NULL */
{
{ -1, 0 },
},
};
static ssize_t dvfsrc_level_mask_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%x\n", helio_dvfsrc_level_mask_get());
}
static ssize_t dvfsrc_level_mask_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int level = 0, en = 0;
if (sscanf(buf, "%d %d", &level, &en) != 2)
return -EINVAL;
helio_dvfsrc_level_mask_set(en, level);
return count;
}
static DEVICE_ATTR(dvfsrc_level_mask, 0644,
dvfsrc_level_mask_show, dvfsrc_level_mask_store);
static ssize_t dvfsrc_vcore_settle_time_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
/* DE's comment: settle time was hard code in fw (15,30) */
return sprintf(buf, "rising 15 uS, falling 30 uS for mt6833\n");
}
static DEVICE_ATTR(dvfsrc_vcore_settle_time, 0444,
dvfsrc_vcore_settle_time_show, NULL);
static ssize_t dvfsrc_md_imp_gear_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%x\n", dvfsrc_read(DVFSRC_MD_LATENCY_IMPROVE));
}
static ssize_t dvfsrc_md_imp_gear_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int gear = 0;
if (kstrtoint(buf, 10, &gear))
return -EINVAL;
if (gear >= DDR_OPP_NUM || gear < 0)
return -EINVAL;
dvfsrc_rmw(DVFSRC_MD_LATENCY_IMPROVE, gear, 0x7, 4);
return count;
}
static DEVICE_ATTR(dvfsrc_md_imp_gear, 0644,
dvfsrc_md_imp_gear_show, dvfsrc_md_imp_gear_store);
static ssize_t dvfsrc_md_qos_performance_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int len = 0;
len += snprintf(buf + len, PAGE_SIZE - 1 - len,
"%-12s: 0x%08x\n",
"RSRV_5",
dvfsrc_read(DVFSRC_RSRV_5));
len += snprintf(buf + len, PAGE_SIZE - 1 - len,
"%-12s: 0x%08x\n",
"DVFSRC_MD_TURBO",
dvfsrc_read(DVFSRC_MD_TURBO));
len += snprintf(buf + len, PAGE_SIZE - 1 - len,
"%-12s: 0x%08x\n",
"SCEN_URGENT",
dvfsrc_read(DVFSRC_95MD_SCEN_BW4));
len += snprintf(buf + len, PAGE_SIZE - 1 - len,
"%-12s: 0x%08x, %08x, %08x, %08x\n",
"SCEN_BW_T",
dvfsrc_read(DVFSRC_95MD_SCEN_BW0_T),
dvfsrc_read(DVFSRC_95MD_SCEN_BW1_T),
dvfsrc_read(DVFSRC_95MD_SCEN_BW2_T),
dvfsrc_read(DVFSRC_95MD_SCEN_BW3_T));
len += snprintf(buf + len, PAGE_SIZE - 1 - len,
"%-12s: 0x%08x, %08x, %08x, %08x\n",
"SCEN_BW",
dvfsrc_read(DVFSRC_95MD_SCEN_BW0),
dvfsrc_read(DVFSRC_95MD_SCEN_BW1),
dvfsrc_read(DVFSRC_95MD_SCEN_BW2),
dvfsrc_read(DVFSRC_95MD_SCEN_BW3));
return len;
}
static DEVICE_ATTR(dvfsrc_md_qos_performance, 0444,
dvfsrc_md_qos_performance_show, NULL);
static struct attribute *mt6833_helio_dvfsrc_attrs[] = {
&dev_attr_dvfsrc_level_mask.attr,
&dev_attr_dvfsrc_vcore_settle_time.attr,
&dev_attr_dvfsrc_md_imp_gear.attr,
&dev_attr_dvfsrc_md_qos_performance.attr,
NULL,
};
static struct attribute_group mt6833_helio_dvfsrc_attr_group = {
.name = "helio-dvfsrc",
.attrs = mt6833_helio_dvfsrc_attrs,
};
#define dvfsrc_rmw(offset, val, mask, shift) \
dvfsrc_write(offset, (dvfsrc_read(offset) & ~(mask << shift)) \
| (val << shift))
u32 dvfsrc_get_ddr_qos(void)
{
unsigned int qos_total_bw = dvfsrc_read(DVFSRC_SW_BW_0) +
dvfsrc_read(DVFSRC_SW_BW_1) +
dvfsrc_read(DVFSRC_SW_BW_2) +
dvfsrc_read(DVFSRC_SW_BW_3) +
dvfsrc_read(DVFSRC_SW_BW_4);
if (qos_total_bw < 0xC)
return 0;
else if (qos_total_bw < 0x19)
return 1;
else if (qos_total_bw < 0x26)
return 2;
else if (qos_total_bw < 0x33)
return 3;
else if (qos_total_bw < 0x3B)
return 4;
else if (qos_total_bw < 0x4C)
return 5;
else if (qos_total_bw < 0x66)
return 6;
else
return 7;
return 0;
}
static int dvfsrc_get_emi_mon_gear(void)
{
unsigned int total_bw_status;
int i;
total_bw_status = vcorefs_get_total_emi_status() & 0x3F;
for (i = 6; i >= 0 ; i--) {
if ((total_bw_status >> i) > 0)
return i + 1;
}
return 0;
}
static u32 dvfsrc_calc_hrt_opp(int data)
{
if (data < 0x00C8)
return DDR_OPP_7;
else if (data < 0x04B0)
return DDR_OPP_6;
else if (data < 0x0708)
return DDR_OPP_5;
else if (data < 0x0B80)
return DDR_OPP_4;
else if (data < 0x0D69)
return DDR_OPP_3;
else if (data < 0x1183)
return DDR_OPP_2;
else if (data < 0x18A6)
return DDR_OPP_1;
else
return DDR_OPP_0;
}
void dvfsrc_set_isp_hrt_bw(int data)
{
data = (data + 29) / 30;
if (data > 0x3FF)
data = 0x3FF;
dvfsrc_write(DVFSRC_ISP_HRT, data);
}
u32 dvfsrc_calc_isp_hrt_opp(int data)
{
return dvfsrc_calc_hrt_opp(((data + 29) / 30) * 30);
}
struct regulator *dvfsrc_vcore_requlator(struct device *dev)
{
return regulator_get(dev, "vcore");
}
#ifdef AUTOK_ENABLE
__weak int emmc_autok(void)
{
pr_info("NOT SUPPORT EMMC AUTOK\n");
return 0;
}
__weak int sd_autok(void)
{
pr_info("NOT SUPPORT SD AUTOK\n");
return 0;
}
__weak int sdio_autok(void)
{
pr_info("NOT SUPPORT SDIO AUTOK\n");
return 0;
}
void begin_autok_task(void)
{
/* notify MM DVFS for msdc autok start */
mmdvfs_prepare_action(MMDVFS_PREPARE_CALIBRATION_START);
}
void finish_autok_task(void)
{
/* check if dvfs force is released */
int force = mtk_pm_qos_request(MTK_PM_QOS_VCORE_DVFS_FORCE_OPP);
/* notify MM DVFS for msdc autok finish */
mmdvfs_prepare_action(MMDVFS_PREPARE_CALIBRATION_END);
if (force >= 0 && force < VCORE_DVFS_OPP_NUM)
pr_info("autok task not release force opp: %d\n", force);
}
static void dvfsrc_autok_manager(void)
{
int r = 0;
begin_autok_task();
r = emmc_autok();
pr_info("EMMC autok done: %s\n", (r == 0) ? "Yes" : "No");
r = sd_autok();
pr_info("SD autok done: %s\n", (r == 0) ? "Yes" : "No");
r = sdio_autok();
pr_info("SDIO autok done: %s\n", (r == 0) ? "Yes" : "No");
finish_autok_task();
}
#endif
static void dvfsrc_update_md_scenario(bool blank)
{
}
static int dvfsrc_fb_notifier_call(struct notifier_block *self,
unsigned long event, void *data)
{
struct fb_event *evdata = data;
int blank;
if (event != FB_EVENT_BLANK)
return 0;
blank = *(int *)evdata->data;
switch (blank) {
case FB_BLANK_UNBLANK:
dvfsrc_update_md_scenario(false);
break;
case FB_BLANK_POWERDOWN:
dvfsrc_update_md_scenario(true);
break;
default:
break;
}
return 0;
}
static struct notifier_block dvfsrc_fb_notifier = {
.notifier_call = dvfsrc_fb_notifier_call,
};
void helio_dvfsrc_platform_pre_init(struct helio_dvfsrc *dvfsrc)
{
struct platform_device *pdev = to_platform_device(dvfsrc->dev);
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
dvfsrc->spm_regs = devm_ioremap(&pdev->dev,
res->start, resource_size(res));
if (IS_ERR(dvfsrc->spm_regs))
pr_info("not get spm register\n");
}
__weak void mtk_pm_qos_trace_dbg_dump(int mtk_pm_qos_class)
{
}
void dvfsrc_suspend_cb(struct helio_dvfsrc *dvfsrc)
{
int sw_req;
sw_req = dvfsrc_read(DVFSRC_SW_REQ3);
pr_info("[DVFSRC] V:%d, F_OPP:%d, RG:%08x, %08x, %08x, %08x\n",
get_cur_vcore_uv(),
mtk_pm_qos_request(MTK_PM_QOS_VCORE_DVFS_FORCE_OPP),
dvfsrc_read(DVFSRC_CURRENT_LEVEL),
dvfsrc_read(DVFSRC_SW_REQ2),
sw_req,
dvfsrc_read(DVFSRC_DEBUG_STA_0));
if (sw_req & (DDR_SW_AP_MASK << DDR_SW_AP_SHIFT))
mtk_pm_qos_trace_dbg_dump(MTK_PM_QOS_DDR_OPP);
if (sw_req & (VCORE_SW_AP_MASK << VCORE_SW_AP_SHIFT))
mtk_pm_qos_trace_dbg_dump(MTK_PM_QOS_VCORE_OPP);
}
void dvfsrc_resume_cb(struct helio_dvfsrc *dvfsrc)
{
}
void helio_dvfsrc_platform_init(struct helio_dvfsrc *dvfsrc)
{
int spmfw_idx = 0;
struct reg_config *config;
int idx = 0;
sysfs_merge_group(&dvfsrc->dev->kobj, &mt6833_helio_dvfsrc_attr_group);
config = dvfsrc_init_configs[spmfw_idx];
while (config[idx].offset != -1) {
dvfsrc_write(config[idx].offset, config[idx].val);
idx++;
}
#ifdef AUTOK_ENABLE
dvfsrc_autok_manager();
#endif
fb_register_client(&dvfsrc_fb_notifier);
}
int vcore_pmic_to_uv(int pmic_val)
{
return __vcore_pmic_to_uv(pmic_val);
}
int vcore_uv_to_pmic(int vcore_uv)
{
return __vcore_uv_to_pmic(vcore_uv);
}
void get_spm_reg(char *p)
{
p += sprintf(p, "%-24s: 0x%08x\n",
"POWERON_CONFIG_EN",
spm_reg_read(POWERON_CONFIG_EN));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_SW_FLAG_0",
spm_reg_read(SPM_SW_FLAG_0));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_PC_STA",
spm_reg_read(SPM_PC_STA));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVFS_LEVEL",
spm_reg_read(SPM_DVFS_LEVEL));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVS_DFS_LEVEL",
spm_reg_read(SPM_DVS_DFS_LEVEL));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVFS_STA",
spm_reg_read(SPM_DVFS_STA));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVFS_MISC",
spm_reg_read(SPM_DVFS_MISC));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
"SPM_DVFS_CMD0~4",
spm_reg_read(SPM_DVFS_CMD0),
spm_reg_read(SPM_DVFS_CMD1),
spm_reg_read(SPM_DVFS_CMD2),
spm_reg_read(SPM_DVFS_CMD3),
spm_reg_read(SPM_DVFS_CMD4));
}
void get_opp_info(char *p)
{
#if defined(CONFIG_FPGA_EARLY_PORTING) || !defined(CONFIG_MTK_PMIC_COMMON) \
|| !defined(CONFIG_MTK_PMIC_NEW_ARCH)
int pmic_val = 0;
#else
int pmic_val = pmic_get_register_value(PMIC_VCORE_ADDR);
#endif
#ifdef CONFIG_MEDIATEK_DRAMC
int ddr_khz = mtk_dramc_get_data_rate() * 1000;
#else
int ddr_khz = 0;
#endif
int vcore_uv = vcore_pmic_to_uv(pmic_val);
p += sprintf(p, "%-10s: %-8u uv (PMIC: 0x%x)\n",
"Vcore", vcore_uv, vcore_uv_to_pmic(vcore_uv));
p += sprintf(p, "%-10s: %-8u khz\n", "DDR", ddr_khz);
p += sprintf(p, "%-10s: %d\n", "CT_MODE", dvfsrc_ct_mode());
p += sprintf(p, "%-10s: %x\n", "V_MODE", dvfsrc_vcore_mode());
}
/* met profile table */
static unsigned int met_vcorefs_src[SRC_MAX];
static char *met_src_name[SRC_MAX] = {
"MD2SPM",
"SRC_DDR_OPP",
"DDR__SW_REQ1_SPM",
"DDR__SW_REQ2_CM",
"DDR__SW_REQ3_PMQOS",
"DDR__QOS_BW",
"DDR__EMI_TOTAL",
"DDR__HRT_BW",
"DDR__HIFI",
"DDR__HIFI_LATENCY",
"DDR__MD_LATENCY",
"DDR__MD_DDR",
"DDR__MD_LEVEL_MASK",
"SRC_VCORE_OPP",
"VCORE__SW_REQ3_PMQOS",
"VCORE__SCP",
"VCORE__HIFI",
"SCP_REQ",
"PMQOS_TATOL",
"PMQOS_BW0",
"PMQOS_BW1",
"PMQOS_BW2",
"PMQOS_BW3",
"PMQOS_BW4",
"TOTAL_EMI_BW",
"HRT_MD_BW",
"HRT_DISP_BW",
"HRT_ISP_BW",
"MD_SCENARIO",
"HIFI_SCENARIO_IDX",
"MD_EMI_LATENCY",
};
/* met profile function */
int vcorefs_get_src_req_num(void)
{
return SRC_MAX;
}
EXPORT_SYMBOL(vcorefs_get_src_req_num);
char **vcorefs_get_src_req_name(void)
{
return met_src_name;
}
EXPORT_SYMBOL(vcorefs_get_src_req_name);
static u32 vcorefs_get_md_level_mask_ddr(void)
{
int md_srclk, vopp;
if (dvfsrc_read(DVFSRC_BASIC_CONTROL_3) & 0x8) {
md_srclk = dvfsrc_read(DVFSRC_DEBUG_STA_0);
vopp = get_cur_vcore_opp();
md_srclk = (md_srclk >> MD_SRC_CLK_DEBUG_SHIFT)
& MD_SRC_CLK_DEBUG_MASK;
if (vopp != 3 && md_srclk == 1)
return 2;
}
return 0;
}
static void vcorefs_get_src_ddr_req(void)
{
unsigned int sw_req;
met_vcorefs_src[DDR_OPP_IDX] =
get_cur_ddr_opp();
sw_req = dvfsrc_read(DVFSRC_SW_REQ1);
met_vcorefs_src[DDR_SW_REQ1_SPM_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
sw_req = dvfsrc_read(DVFSRC_SW_REQ2);
met_vcorefs_src[DDR_SW_REQ2_CM_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
sw_req = dvfsrc_read(DVFSRC_SW_REQ3);
met_vcorefs_src[DDR_SW_REQ3_PMQOS_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
met_vcorefs_src[DDR_QOS_BW_IDX] =
dvfsrc_get_ddr_qos();
met_vcorefs_src[DDR_EMI_TOTAL_IDX] =
dvfsrc_get_emi_mon_gear();
met_vcorefs_src[DDR_HRT_BW_IDX] =
vcorefs_get_hrt_bw_ddr();
met_vcorefs_src[DDR_HIFI_IDX] =
vcorefs_get_hifi_ddr_status();
met_vcorefs_src[DDR_HIFI_LATENCY_IDX] =
vcorefs_get_hifi_rising_ddr();
met_vcorefs_src[DDR_MD_LATENCY_IDX] =
vcorefs_get_md_imp_ddr();
met_vcorefs_src[DDR_MD_DDR_IDX] =
vcorefs_get_md_scenario_ddr();
met_vcorefs_src[DDR_MD_SRCLK_IDX] =
vcorefs_get_md_level_mask_ddr();
}
static void vcorefs_get_src_vcore_req(void)
{
u32 sw_req;
u32 scp_en;
scp_en = vcorefs_get_scp_req_status();
met_vcorefs_src[VCORE_OPP_IDX] =
get_cur_vcore_opp();
sw_req = dvfsrc_read(DVFSRC_SW_REQ3);
met_vcorefs_src[VCORE_SW_REQ3_PMQOS_IDX] =
(sw_req >> VCORE_SW_AP_SHIFT) & VCORE_SW_AP_MASK;
if (scp_en) {
sw_req = dvfsrc_read(DVFSRC_VCORE_REQUEST);
met_vcorefs_src[VCORE_SCP_IDX] =
(sw_req >> VCORE_SCP_GEAR_SHIFT) & VCORE_SCP_GEAR_MASK;
} else
met_vcorefs_src[VCORE_SCP_IDX] = 0;
met_vcorefs_src[VCORE_HIFI_IDX] =
vcorefs_get_hifi_vcore_status();
}
static void vcorefs_get_src_misc_info(void)
{
u32 qos_bw0, qos_bw1, qos_bw2, qos_bw3, qos_bw4;
qos_bw0 = dvfsrc_read(DVFSRC_SW_BW_0);
qos_bw1 = dvfsrc_read(DVFSRC_SW_BW_1);
qos_bw2 = dvfsrc_read(DVFSRC_SW_BW_2);
qos_bw3 = dvfsrc_read(DVFSRC_SW_BW_3);
qos_bw4 = dvfsrc_read(DVFSRC_SW_BW_4);
met_vcorefs_src[SRC_MD2SPM_IDX] =
vcorefs_get_md_scenario() & 0x1FFFF;
met_vcorefs_src[SRC_SCP_REQ_IDX] =
vcorefs_get_scp_req_status();
met_vcorefs_src[SRC_PMQOS_TATOL_IDX] =
qos_bw0 + qos_bw1 + qos_bw2 + qos_bw3 + qos_bw4;
met_vcorefs_src[SRC_PMQOS_BW0_IDX] =
qos_bw0;
met_vcorefs_src[SRC_PMQOS_BW1_IDX] =
qos_bw1;
met_vcorefs_src[SRC_PMQOS_BW2_IDX] =
qos_bw2;
met_vcorefs_src[SRC_PMQOS_BW3_IDX] =
qos_bw3;
met_vcorefs_src[SRC_PMQOS_BW4_IDX] =
qos_bw4;
met_vcorefs_src[SRC_HRT_MD_BW_IDX] =
dvfsrc_get_md_bw();
met_vcorefs_src[SRC_HRT_ISP_BW_IDX] =
dvfsrc_read(DVFSRC_ISP_HRT);
met_vcorefs_src[SRC_MD_SCENARIO_IDX] =
vcorefs_get_md_scenario();
met_vcorefs_src[SRC_HIFI_SCENARIO_IDX] =
vcorefs_get_hifi_scenario();
met_vcorefs_src[SRC_MD_EMI_LATENCY_IDX] =
vcorefs_get_md_emi_latency_status();
}
int dvfsrc_latch_register(int enable)
{
#ifdef CONFIG_MTK_DBGTOP
return mtk_dbgtop_cfg_dvfsrc(1);
#endif
return 0;
}
unsigned int *vcorefs_get_src_req(void)
{
vcorefs_get_src_ddr_req();
vcorefs_get_src_vcore_req();
vcorefs_get_src_misc_info();
vcorefs_trace_qos();
return met_vcorefs_src;
}
EXPORT_SYMBOL(vcorefs_get_src_req);
int get_cur_ddr_ratio(void)
{
int idx;
if (!is_dvfsrc_enabled())
return 0;
idx = get_cur_vcore_dvfs_opp();
if (idx >= VCORE_DVFS_OPP_NUM)
return 0;
if (get_ddr_opp(idx) < DDR_OPP_6)
return 8;
else
return 4;
}
EXPORT_SYMBOL(get_cur_ddr_ratio);
@@ -0,0 +1,60 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_MT6833_H
#define __HELIO_DVFSRC_MT6833_H
#include <mach/upmu_hw.h>
#define PMIC_VCORE_ADDR PMIC_RG_BUCK_VCORE_VOSEL
#define VCORE_BASE_UV 506250
#define VCORE_STEP_UV 6250
/* PMIC */
#define __vcore_pmic_to_uv(pmic) \
(((pmic) * VCORE_STEP_UV) + VCORE_BASE_UV)
#define __vcore_uv_to_pmic(uv) /* pmic >= uv */ \
((((uv) - VCORE_BASE_UV) + (VCORE_STEP_UV - 1)) / VCORE_STEP_UV)
enum met_src_index {
SRC_MD2SPM_IDX = 0,
DDR_OPP_IDX,
DDR_SW_REQ1_SPM_IDX,
DDR_SW_REQ2_CM_IDX,
DDR_SW_REQ3_PMQOS_IDX,
DDR_QOS_BW_IDX,
DDR_EMI_TOTAL_IDX,
DDR_HRT_BW_IDX,
DDR_HIFI_IDX,
DDR_HIFI_LATENCY_IDX,
DDR_MD_LATENCY_IDX,
DDR_MD_DDR_IDX,
DDR_MD_SRCLK_IDX,
VCORE_OPP_IDX,
VCORE_SW_REQ3_PMQOS_IDX,
VCORE_SCP_IDX,
VCORE_HIFI_IDX,
SRC_SCP_REQ_IDX,
SRC_PMQOS_TATOL_IDX,
SRC_PMQOS_BW0_IDX,
SRC_PMQOS_BW1_IDX,
SRC_PMQOS_BW2_IDX,
SRC_PMQOS_BW3_IDX,
SRC_PMQOS_BW4_IDX,
SRC_TOTAL_EMI_BW_IDX,
SRC_HRT_MD_BW_IDX,
SRC_HRT_DISP_BW_IDX,
SRC_HRT_ISP_BW_IDX,
SRC_MD_SCENARIO_IDX,
SRC_HIFI_SCENARIO_IDX,
SRC_MD_EMI_LATENCY_IDX,
SRC_MAX
};
extern u32 dvfsrc_ct_mode(void);
extern u32 dvfsrc_vcore_mode(void);
#endif /* __HELIO_DVFSRC_MT6833_H */
@@ -0,0 +1,806 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/fb.h>
#include <linux/platform_device.h>
#ifdef CONFIG_MEDIATEK_DRAMC
#include <dramc.h>
#endif
#ifdef CONFIG_MTK_EMI
#include <mt_emi_api.h>
#endif
#include <mt-plat/upmu_common.h>
#include "helio-dvfsrc-ip-v2.h"
#include <helio-dvfsrc-opp.h>
#include <helio-dvfsrc-mt6853.h>
#include <mt-plat/mtk_devinfo.h>
#include <linux/regulator/consumer.h>
#include "mmdvfs_pmqos.h"
#include <linux/sysfs.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/sched/clock.h>
#include <dbgtop.h>
#define DVFSRC_FB_MD_TABLE_SWITCH
/* #define AUTOK_ENABLE */
#define dvfsrc_rmw(offset, val, mask, shift) \
dvfsrc_write(offset, (dvfsrc_read(offset) & ~(mask << shift)) \
| (val << shift))
static struct reg_config dvfsrc_init_configs[][128] = {
{
{ DVFSRC_HRT_REQ_UNIT, 0x0000001E },
{ DVFSRC_DEBOUNCE_TIME, 0x00001965 },
{ DVFSRC_TIMEOUT_NEXTREQ, 0x00000015 },
{ DVFSRC_LEVEL_MASK, 0x000EE000 },
{ DVFSRC_DDR_QOS0, 0x00000019 },
{ DVFSRC_DDR_QOS1, 0x00000026 },
{ DVFSRC_DDR_QOS2, 0x00000033 },
{ DVFSRC_DDR_QOS3, 0x0000003B },
{ DVFSRC_DDR_QOS4, 0x0000004C },
{ DVFSRC_DDR_QOS5, 0x00000066 },
{ DVFSRC_DDR_QOS6, 0x00000066 },
{ DVFSRC_LEVEL_LABEL_0_1, 0x50436053 },
{ DVFSRC_LEVEL_LABEL_2_3, 0x40335042 },
{ DVFSRC_LEVEL_LABEL_4_5, 0x40314032 },
{ DVFSRC_LEVEL_LABEL_6_7, 0x30223023 },
{ DVFSRC_LEVEL_LABEL_8_9, 0x20133021 },
{ DVFSRC_LEVEL_LABEL_10_11, 0x20112012 },
{ DVFSRC_LEVEL_LABEL_12_13, 0x10032010 },
{ DVFSRC_LEVEL_LABEL_14_15, 0x10011002 },
{ DVFSRC_LEVEL_LABEL_16_17, 0x00131000 },
{ DVFSRC_LEVEL_LABEL_18_19, 0x00110012},
{ DVFSRC_LEVEL_LABEL_20_21, 0x00000010 },
{ DVFSRC_MD_LATENCY_IMPROVE, 0x00000040 },
{ DVFSRC_HRT_BW_BASE, 0x00000004 },
{ DVSFRC_HRT_REQ_MD_URG, 0x000D20D2 },
{ DVFSRC_HRT_REQ_MD_BW_0, 0x00200802 },
{ DVFSRC_HRT_REQ_MD_BW_1, 0x00200802 },
{ DVFSRC_HRT_REQ_MD_BW_2, 0x00200800 },
{ DVFSRC_HRT_REQ_MD_BW_3, 0x00400802 },
{ DVFSRC_HRT_REQ_MD_BW_4, 0x00601404 },
{ DVFSRC_HRT_REQ_MD_BW_5, 0x00D02C09 },
{ DVFSRC_HRT_REQ_MD_BW_6, 0x00000012 },
{ DVFSRC_HRT_REQ_MD_BW_7, 0x00000024 },
{ DVFSRC_HRT_REQ_MD_BW_8, 0x00000000 },
{ DVFSRC_HRT_REQ_MD_BW_9, 0x00000000 },
{ DVFSRC_HRT_REQ_MD_BW_10, 0x00034800 },
{ DVFSRC_HRT1_REQ_MD_BW_0, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_1, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_2, 0x04B12C00 },
{ DVFSRC_HRT1_REQ_MD_BW_3, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_4, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_5, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_6, 0x0000004B },
{ DVFSRC_HRT1_REQ_MD_BW_7, 0x0000005C },
{ DVFSRC_HRT1_REQ_MD_BW_8, 0x00000000 },
{ DVFSRC_HRT1_REQ_MD_BW_9, 0x00000000 },
{ DVFSRC_HRT1_REQ_MD_BW_10, 0x00034800 },
#ifdef DVFSRC_FB_MD_TABLE_SWITCH
{ DVFSRC_95MD_SCEN_BW0_T, 0x40444440 },
{ DVFSRC_95MD_SCEN_BW1_T, 0x22244444 },
{ DVFSRC_95MD_SCEN_BW2_T, 0x00400444 },
{ DVFSRC_95MD_SCEN_BW3_T, 0x60000000 },
{ DVFSRC_95MD_SCEN_BW0, 0x20222220 },
{ DVFSRC_95MD_SCEN_BW1, 0x00022222},
{ DVFSRC_95MD_SCEN_BW2, 0x00200222 },
{ DVFSRC_95MD_SCEN_BW3, 0x60000000 },
{ DVFSRC_95MD_SCEN_BW4, 0x00000006 },
{ DVFSRC_RSRV_5, 0x00000001 },
#else
{ DVFSRC_95MD_SCEN_BW0_T, 0x40444440 },
{ DVFSRC_95MD_SCEN_BW1_T, 0x22244444},
{ DVFSRC_95MD_SCEN_BW2_T, 0x00400444 },
{ DVFSRC_95MD_SCEN_BW3_T, 0x60000000 },
{ DVFSRC_95MD_SCEN_BW0, 0x20222220 },
{ DVFSRC_95MD_SCEN_BW1, 0x00022222 },
{ DVFSRC_95MD_SCEN_BW2, 0x00200222 },
{ DVFSRC_95MD_SCEN_BW3, 0x60000000 },
{ DVFSRC_95MD_SCEN_BW4, 0x00000006 },
{ DVFSRC_RSRV_5, 0x00000001 },
#endif
{ DVFSRC_DDR_REQUEST, 0x00004321 },
{ DVFSRC_DDR_REQUEST3, 0x00000065 },
{ DVFSRC_DDR_ADD_REQUEST, 0x66543210 },
{ DVFSRC_HRT_REQUEST, 0x66654321 },
{ DVFSRC_DDR_REQUEST5, 0x54321000 },
{ DVFSRC_DDR_REQUEST7, 0x66000000 },
{ DVFSRC_EMI_MON_DEBOUNCE_TIME, 0x4C2D0000 },
{ DVFSRC_DDR_REQUEST6, 0x66543210 },
{ DVFSRC_VCORE_USER_REQ, 0x00010A29 },
{ DVFSRC_HRT_HIGH_3, 0x18A618A6 },
{ DVFSRC_HRT_HIGH_2, 0x18A61183 },
{ DVFSRC_HRT_HIGH_1, 0x0D690B80 },
{ DVFSRC_HRT_HIGH, 0x070804B0 },
{ DVFSRC_HRT_LOW_3, 0x18A518A5 },
{ DVFSRC_HRT_LOW_2, 0x18A51182 },
{ DVFSRC_HRT_LOW_1, 0x0D680B7F},
{ DVFSRC_HRT_LOW, 0x070704AF },
{ DVFSRC_BASIC_CONTROL_3, 0x00000006 },
{ DVFSRC_INT_EN, 0x00000002 },
{ DVFSRC_QOS_EN, 0x0000407C },
{ DVFSRC_CURRENT_FORCE, 0x00000001 },
{ DVFSRC_BASIC_CONTROL, 0x6698444B },
{ DVFSRC_BASIC_CONTROL, 0x6698054B },
{ DVFSRC_CURRENT_FORCE, 0x00000000 },
{ -1, 0 },
},
/* NULL */
{
{ -1, 0 },
},
};
static ssize_t dvfsrc_level_mask_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%x\n", helio_dvfsrc_level_mask_get());
}
static ssize_t dvfsrc_level_mask_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int level = 0, en = 0;
if (sscanf(buf, "%d %d", &level, &en) != 2)
return -EINVAL;
helio_dvfsrc_level_mask_set(en, level);
return count;
}
static DEVICE_ATTR(dvfsrc_level_mask, 0644,
dvfsrc_level_mask_show, dvfsrc_level_mask_store);
static ssize_t dvfsrc_vcore_settle_time_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
/* DE's comment: settle time was hard code in fw (15,30) */
return sprintf(buf, "rising 15 uS, falling 30 uS for mt6853\n");
}
static DEVICE_ATTR(dvfsrc_vcore_settle_time, 0444,
dvfsrc_vcore_settle_time_show, NULL);
static ssize_t dvfsrc_md_imp_gear_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%x\n", dvfsrc_read(DVFSRC_MD_LATENCY_IMPROVE));
}
static ssize_t dvfsrc_md_imp_gear_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int gear = 0;
if (kstrtoint(buf, 10, &gear))
return -EINVAL;
if (gear >= DDR_OPP_NUM || gear < 0)
return -EINVAL;
dvfsrc_rmw(DVFSRC_MD_LATENCY_IMPROVE, gear, 0x7, 4);
return count;
}
static DEVICE_ATTR(dvfsrc_md_imp_gear, 0644,
dvfsrc_md_imp_gear_show, dvfsrc_md_imp_gear_store);
static ssize_t dvfsrc_md_qos_performance_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int len = 0;
len += snprintf(buf + len, PAGE_SIZE - 1 - len,
"%-12s: 0x%08x\n",
"RSRV_5",
dvfsrc_read(DVFSRC_RSRV_5));
len += snprintf(buf + len, PAGE_SIZE - 1 - len,
"%-12s: 0x%08x\n",
"DVFSRC_MD_TURBO",
dvfsrc_read(DVFSRC_MD_TURBO));
len += snprintf(buf + len, PAGE_SIZE - 1 - len,
"%-12s: 0x%08x\n",
"SCEN_URGENT",
dvfsrc_read(DVFSRC_95MD_SCEN_BW4));
len += snprintf(buf + len, PAGE_SIZE - 1 - len,
"%-12s: 0x%08x, %08x, %08x, %08x\n",
"SCEN_BW_T",
dvfsrc_read(DVFSRC_95MD_SCEN_BW0_T),
dvfsrc_read(DVFSRC_95MD_SCEN_BW1_T),
dvfsrc_read(DVFSRC_95MD_SCEN_BW2_T),
dvfsrc_read(DVFSRC_95MD_SCEN_BW3_T));
len += snprintf(buf + len, PAGE_SIZE - 1 - len,
"%-12s: 0x%08x, %08x, %08x, %08x\n",
"SCEN_BW",
dvfsrc_read(DVFSRC_95MD_SCEN_BW0),
dvfsrc_read(DVFSRC_95MD_SCEN_BW1),
dvfsrc_read(DVFSRC_95MD_SCEN_BW2),
dvfsrc_read(DVFSRC_95MD_SCEN_BW3));
return len;
}
static DEVICE_ATTR(dvfsrc_md_qos_performance, 0444,
dvfsrc_md_qos_performance_show, NULL);
static struct attribute *mt6853_helio_dvfsrc_attrs[] = {
&dev_attr_dvfsrc_level_mask.attr,
&dev_attr_dvfsrc_vcore_settle_time.attr,
&dev_attr_dvfsrc_md_imp_gear.attr,
&dev_attr_dvfsrc_md_qos_performance.attr,
NULL,
};
static struct attribute_group mt6853_helio_dvfsrc_attr_group = {
.name = "helio-dvfsrc",
.attrs = mt6853_helio_dvfsrc_attrs,
};
#define dvfsrc_rmw(offset, val, mask, shift) \
dvfsrc_write(offset, (dvfsrc_read(offset) & ~(mask << shift)) \
| (val << shift))
u32 dvfsrc_get_ddr_qos(void)
{
unsigned int qos_total_bw = dvfsrc_read(DVFSRC_SW_BW_0) +
dvfsrc_read(DVFSRC_SW_BW_1) +
dvfsrc_read(DVFSRC_SW_BW_2) +
dvfsrc_read(DVFSRC_SW_BW_3) +
dvfsrc_read(DVFSRC_SW_BW_4);
if (qos_total_bw < 0x19)
return 0;
else if (qos_total_bw < 0x26)
return 1;
else if (qos_total_bw < 0x33)
return 2;
else if (qos_total_bw < 0x3B)
return 3;
else if (qos_total_bw < 0x4C)
return 4;
else if (qos_total_bw < 0x66)
return 5;
else
return 6;
return 0;
}
static int dvfsrc_get_emi_mon_gear(void)
{
unsigned int total_bw_status;
int i;
total_bw_status = vcorefs_get_total_emi_status() & 0x3F;
for (i = 5; i >= 0 ; i--) {
if ((total_bw_status >> i) > 0)
return i + 1;
}
return 0;
}
static u32 dvfsrc_calc_hrt_opp(int data)
{
if (data < 0x04B0)
return DDR_OPP_6;
else if (data < 0x0708)
return DDR_OPP_5;
else if (data < 0x0B80)
return DDR_OPP_4;
else if (data < 0x0D69)
return DDR_OPP_3;
else if (data < 0x1183)
return DDR_OPP_2;
else if (data < 0x18A6)
return DDR_OPP_1;
else
return DDR_OPP_0;
}
void dvfsrc_set_isp_hrt_bw(int data)
{
data = (data + 29) / 30;
if (data > 0x3FF)
data = 0x3FF;
dvfsrc_write(DVFSRC_ISP_HRT, data);
}
u32 dvfsrc_calc_isp_hrt_opp(int data)
{
return dvfsrc_calc_hrt_opp(((data + 29) / 30) * 30);
}
u32 dvfsrc_get_pcie_vcore_status(void)
{
u32 val, pcie_en;
val = dvfsrc_read(DVFSRC_DEBUG_STA_2);
pcie_en = (val >> DEBUG_STA2_PCIE_SHIFT) & DEBUG_STA2_PCIE_MASK;
if (pcie_en)
return 1;
else
return 0;
}
struct regulator *dvfsrc_vcore_requlator(struct device *dev)
{
return regulator_get(dev, "vcore");
}
#ifdef AUTOK_ENABLE
__weak int emmc_autok(void)
{
pr_info("NOT SUPPORT EMMC AUTOK\n");
return 0;
}
__weak int sd_autok(void)
{
pr_info("NOT SUPPORT SD AUTOK\n");
return 0;
}
__weak int sdio_autok(void)
{
pr_info("NOT SUPPORT SDIO AUTOK\n");
return 0;
}
void begin_autok_task(void)
{
/* notify MM DVFS for msdc autok start */
mmdvfs_prepare_action(MMDVFS_PREPARE_CALIBRATION_START);
}
void finish_autok_task(void)
{
/* check if dvfs force is released */
int force = mtk_pm_qos_request(MTK_PM_QOS_VCORE_DVFS_FORCE_OPP);
/* notify MM DVFS for msdc autok finish */
mmdvfs_prepare_action(MMDVFS_PREPARE_CALIBRATION_END);
if (force >= 0 && force < VCORE_DVFS_OPP_NUM)
pr_info("autok task not release force opp: %d\n", force);
}
static void dvfsrc_autok_manager(void)
{
int r = 0;
begin_autok_task();
r = emmc_autok();
pr_info("EMMC autok done: %s\n", (r == 0) ? "Yes" : "No");
r = sd_autok();
pr_info("SD autok done: %s\n", (r == 0) ? "Yes" : "No");
r = sdio_autok();
pr_info("SDIO autok done: %s\n", (r == 0) ? "Yes" : "No");
finish_autok_task();
}
#endif
static void dvfsrc_update_md_scenario(bool blank)
{
#ifdef DVFSRC_FB_MD_TABLE_SWITCH
if (blank)
dvfsrc_write(DVFSRC_95MD_SCEN_BW1_T, 0x22244444);
else
dvfsrc_write(DVFSRC_95MD_SCEN_BW1_T, 0x22244444);
#endif
}
static int dvfsrc_fb_notifier_call(struct notifier_block *self,
unsigned long event, void *data)
{
struct fb_event *evdata = data;
int blank;
if (event != FB_EVENT_BLANK)
return 0;
blank = *(int *)evdata->data;
switch (blank) {
case FB_BLANK_UNBLANK:
dvfsrc_update_md_scenario(false);
break;
case FB_BLANK_POWERDOWN:
dvfsrc_update_md_scenario(true);
break;
default:
break;
}
return 0;
}
static struct notifier_block dvfsrc_fb_notifier = {
.notifier_call = dvfsrc_fb_notifier_call,
};
void helio_dvfsrc_platform_pre_init(struct helio_dvfsrc *dvfsrc)
{
struct platform_device *pdev = to_platform_device(dvfsrc->dev);
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
dvfsrc->spm_regs = devm_ioremap(&pdev->dev,
res->start, resource_size(res));
if (IS_ERR(dvfsrc->spm_regs))
pr_info("not get spm register\n");
}
__weak void mtk_pm_qos_trace_dbg_dump(int mtk_pm_qos_class)
{
}
void dvfsrc_suspend_cb(struct helio_dvfsrc *dvfsrc)
{
int sw_req;
sw_req = dvfsrc_read(DVFSRC_SW_REQ3);
pr_info("[DVFSRC] V:%d, F_OPP:%d, RG:%08x, %08x, %08x, %08x\n",
get_cur_vcore_uv(),
mtk_pm_qos_request(MTK_PM_QOS_VCORE_DVFS_FORCE_OPP),
dvfsrc_read(DVFSRC_CURRENT_LEVEL),
dvfsrc_read(DVFSRC_SW_REQ2),
sw_req,
dvfsrc_read(DVFSRC_DEBUG_STA_0));
if (sw_req & (DDR_SW_AP_MASK << DDR_SW_AP_SHIFT))
mtk_pm_qos_trace_dbg_dump(MTK_PM_QOS_DDR_OPP);
if (sw_req & (VCORE_SW_AP_MASK << VCORE_SW_AP_SHIFT))
mtk_pm_qos_trace_dbg_dump(MTK_PM_QOS_VCORE_OPP);
}
void dvfsrc_resume_cb(struct helio_dvfsrc *dvfsrc)
{
}
void helio_dvfsrc_platform_init(struct helio_dvfsrc *dvfsrc)
{
int spmfw_idx = 0;
struct reg_config *config;
int idx = 0;
sysfs_merge_group(&dvfsrc->dev->kobj, &mt6853_helio_dvfsrc_attr_group);
config = dvfsrc_init_configs[spmfw_idx];
while (config[idx].offset != -1) {
dvfsrc_write(config[idx].offset, config[idx].val);
idx++;
}
#ifdef AUTOK_ENABLE
dvfsrc_autok_manager();
#endif
fb_register_client(&dvfsrc_fb_notifier);
}
int vcore_pmic_to_uv(int pmic_val)
{
return __vcore_pmic_to_uv(pmic_val);
}
int vcore_uv_to_pmic(int vcore_uv)
{
return __vcore_uv_to_pmic(vcore_uv);
}
void get_spm_reg(char *p)
{
p += sprintf(p, "%-24s: 0x%08x\n",
"POWERON_CONFIG_EN",
spm_reg_read(POWERON_CONFIG_EN));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_SW_FLAG_0",
spm_reg_read(SPM_SW_FLAG_0));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_PC_STA",
spm_reg_read(SPM_PC_STA));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVFS_LEVEL",
spm_reg_read(SPM_DVFS_LEVEL));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVS_DFS_LEVEL",
spm_reg_read(SPM_DVS_DFS_LEVEL));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVFS_STA",
spm_reg_read(SPM_DVFS_STA));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVFS_MISC",
spm_reg_read(SPM_DVFS_MISC));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
"SPM_DVFS_CMD0~4",
spm_reg_read(SPM_DVFS_CMD0),
spm_reg_read(SPM_DVFS_CMD1),
spm_reg_read(SPM_DVFS_CMD2),
spm_reg_read(SPM_DVFS_CMD3),
spm_reg_read(SPM_DVFS_CMD4));
}
void get_opp_info(char *p)
{
#if defined(CONFIG_FPGA_EARLY_PORTING) || !defined(CONFIG_MTK_PMIC_COMMON)
int pmic_val = 0;
#else
int pmic_val = pmic_get_register_value(PMIC_VCORE_ADDR);
#endif
#ifdef CONFIG_MEDIATEK_DRAMC
int ddr_khz = mtk_dramc_get_data_rate() * 1000;
#else
int ddr_khz = 0;
#endif
int vcore_uv = vcore_pmic_to_uv(pmic_val);
p += sprintf(p, "%-10s: %-8u uv (PMIC: 0x%x)\n",
"Vcore", vcore_uv, vcore_uv_to_pmic(vcore_uv));
p += sprintf(p, "%-10s: %-8u khz\n", "DDR", ddr_khz);
p += sprintf(p, "%-10s: %d\n", "CT_MODE", dvfsrc_ct_mode());
p += sprintf(p, "%-10s: %x\n", "V_MODE", dvfsrc_vcore_mode());
}
/* met profile table */
static unsigned int met_vcorefs_src[SRC_MAX];
static char *met_src_name[SRC_MAX] = {
"MD2SPM",
"SRC_DDR_OPP",
"DDR__SW_REQ1_SPM",
"DDR__SW_REQ2_CM",
"DDR__SW_REQ3_PMQOS",
"DDR__QOS_BW",
"DDR__EMI_TOTAL",
"DDR__HRT_BW",
"DDR__HIFI",
"DDR__HIFI_LATENCY",
"DDR__MD_LATENCY",
"DDR__MD_DDR",
"DDR__MD_LEVEL_MASK",
"SRC_VCORE_OPP",
"VCORE__SW_REQ3_PMQOS",
"VCORE__SCP",
"VCORE__HIFI",
"VCORE__PCIE",
"SCP_REQ",
"PMQOS_TATOL",
"PMQOS_BW0",
"PMQOS_BW1",
"PMQOS_BW2",
"PMQOS_BW3",
"PMQOS_BW4",
"TOTAL_EMI_BW",
"HRT_MD_BW",
"HRT_DISP_BW",
"HRT_ISP_BW",
"MD_SCENARIO",
"HIFI_SCENARIO_IDX",
"MD_EMI_LATENCY",
};
/* met profile function */
int vcorefs_get_src_req_num(void)
{
return SRC_MAX;
}
EXPORT_SYMBOL(vcorefs_get_src_req_num);
char **vcorefs_get_src_req_name(void)
{
return met_src_name;
}
EXPORT_SYMBOL(vcorefs_get_src_req_name);
static u32 vcorefs_get_md_level_mask_ddr(void)
{
int md_srclk, vopp;
if (dvfsrc_read(DVFSRC_BASIC_CONTROL_3) & 0x8) {
md_srclk = dvfsrc_read(DVFSRC_DEBUG_STA_0);
vopp = get_cur_vcore_opp();
md_srclk = (md_srclk >> MD_SRC_CLK_DEBUG_SHIFT)
& MD_SRC_CLK_DEBUG_MASK;
if (vopp != 3 && md_srclk == 1)
return 2;
}
return 0;
}
static void vcorefs_get_src_ddr_req(void)
{
unsigned int sw_req;
met_vcorefs_src[DDR_OPP_IDX] =
get_cur_ddr_opp();
sw_req = dvfsrc_read(DVFSRC_SW_REQ1);
met_vcorefs_src[DDR_SW_REQ1_SPM_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
sw_req = dvfsrc_read(DVFSRC_SW_REQ2);
met_vcorefs_src[DDR_SW_REQ2_CM_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
sw_req = dvfsrc_read(DVFSRC_SW_REQ3);
met_vcorefs_src[DDR_SW_REQ3_PMQOS_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
met_vcorefs_src[DDR_QOS_BW_IDX] =
dvfsrc_get_ddr_qos();
met_vcorefs_src[DDR_EMI_TOTAL_IDX] =
dvfsrc_get_emi_mon_gear();
met_vcorefs_src[DDR_HRT_BW_IDX] =
vcorefs_get_hrt_bw_ddr();
met_vcorefs_src[DDR_HIFI_IDX] =
vcorefs_get_hifi_ddr_status();
met_vcorefs_src[DDR_HIFI_LATENCY_IDX] =
vcorefs_get_hifi_rising_ddr();
met_vcorefs_src[DDR_MD_LATENCY_IDX] =
vcorefs_get_md_imp_ddr();
met_vcorefs_src[DDR_MD_DDR_IDX] =
vcorefs_get_md_scenario_ddr();
met_vcorefs_src[DDR_MD_SRCLK_IDX] =
vcorefs_get_md_level_mask_ddr();
}
static void vcorefs_get_src_vcore_req(void)
{
u32 sw_req;
u32 scp_en;
scp_en = vcorefs_get_scp_req_status();
met_vcorefs_src[VCORE_OPP_IDX] =
get_cur_vcore_opp();
sw_req = dvfsrc_read(DVFSRC_SW_REQ3);
met_vcorefs_src[VCORE_SW_REQ3_PMQOS_IDX] =
(sw_req >> VCORE_SW_AP_SHIFT) & VCORE_SW_AP_MASK;
if (scp_en) {
sw_req = dvfsrc_read(DVFSRC_VCORE_REQUEST);
met_vcorefs_src[VCORE_SCP_IDX] =
(sw_req >> VCORE_SCP_GEAR_SHIFT) & VCORE_SCP_GEAR_MASK;
} else
met_vcorefs_src[VCORE_SCP_IDX] = 0;
met_vcorefs_src[VCORE_HIFI_IDX] =
vcorefs_get_hifi_vcore_status();
met_vcorefs_src[VCORE_PCIE_IDX] =
dvfsrc_get_pcie_vcore_status();
}
static void vcorefs_get_src_misc_info(void)
{
u32 qos_bw0, qos_bw1, qos_bw2, qos_bw3, qos_bw4;
qos_bw0 = dvfsrc_read(DVFSRC_SW_BW_0);
qos_bw1 = dvfsrc_read(DVFSRC_SW_BW_1);
qos_bw2 = dvfsrc_read(DVFSRC_SW_BW_2);
qos_bw3 = dvfsrc_read(DVFSRC_SW_BW_3);
qos_bw4 = dvfsrc_read(DVFSRC_SW_BW_4);
met_vcorefs_src[SRC_MD2SPM_IDX] =
vcorefs_get_md_scenario() & 0x1FFFF;
met_vcorefs_src[SRC_SCP_REQ_IDX] =
vcorefs_get_scp_req_status();
met_vcorefs_src[SRC_PMQOS_TATOL_IDX] =
qos_bw0 + qos_bw1 + qos_bw2 + qos_bw3 + qos_bw4;
met_vcorefs_src[SRC_PMQOS_BW0_IDX] =
qos_bw0;
met_vcorefs_src[SRC_PMQOS_BW1_IDX] =
qos_bw1;
met_vcorefs_src[SRC_PMQOS_BW2_IDX] =
qos_bw2;
met_vcorefs_src[SRC_PMQOS_BW3_IDX] =
qos_bw3;
met_vcorefs_src[SRC_PMQOS_BW4_IDX] =
qos_bw4;
met_vcorefs_src[SRC_HRT_MD_BW_IDX] =
dvfsrc_get_md_bw();
met_vcorefs_src[SRC_HRT_ISP_BW_IDX] =
dvfsrc_read(DVFSRC_ISP_HRT);
met_vcorefs_src[SRC_MD_SCENARIO_IDX] =
vcorefs_get_md_scenario();
met_vcorefs_src[SRC_HIFI_SCENARIO_IDX] =
vcorefs_get_hifi_scenario();
met_vcorefs_src[SRC_MD_EMI_LATENCY_IDX] =
vcorefs_get_md_emi_latency_status();
}
int dvfsrc_latch_register(int enable)
{
#ifdef CONFIG_MTK_DBGTOP
return mtk_dbgtop_cfg_dvfsrc(1);
#endif
return 0;
}
unsigned int *vcorefs_get_src_req(void)
{
vcorefs_get_src_ddr_req();
vcorefs_get_src_vcore_req();
vcorefs_get_src_misc_info();
vcorefs_trace_qos();
return met_vcorefs_src;
}
EXPORT_SYMBOL(vcorefs_get_src_req);
int get_cur_ddr_ratio(void)
{
int idx;
if (!is_dvfsrc_enabled())
return 0;
idx = get_cur_vcore_dvfs_opp();
if (idx >= VCORE_DVFS_OPP_NUM)
return 0;
if (get_ddr_opp(idx) < DDR_OPP_6)
return 8;
else
return 4;
}
EXPORT_SYMBOL(get_cur_ddr_ratio);
@@ -0,0 +1,61 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_MT6853_H
#define __HELIO_DVFSRC_MT6853_H
#include <mach/upmu_hw.h>
#define PMIC_VCORE_ADDR PMIC_RG_BUCK_VCORE_VOSEL
#define VCORE_BASE_UV 506250
#define VCORE_STEP_UV 6250
/* PMIC */
#define __vcore_pmic_to_uv(pmic) \
(((pmic) * VCORE_STEP_UV) + VCORE_BASE_UV)
#define __vcore_uv_to_pmic(uv) /* pmic >= uv */ \
((((uv) - VCORE_BASE_UV) + (VCORE_STEP_UV - 1)) / VCORE_STEP_UV)
enum met_src_index {
SRC_MD2SPM_IDX = 0,
DDR_OPP_IDX,
DDR_SW_REQ1_SPM_IDX,
DDR_SW_REQ2_CM_IDX,
DDR_SW_REQ3_PMQOS_IDX,
DDR_QOS_BW_IDX,
DDR_EMI_TOTAL_IDX,
DDR_HRT_BW_IDX,
DDR_HIFI_IDX,
DDR_HIFI_LATENCY_IDX,
DDR_MD_LATENCY_IDX,
DDR_MD_DDR_IDX,
DDR_MD_SRCLK_IDX,
VCORE_OPP_IDX,
VCORE_SW_REQ3_PMQOS_IDX,
VCORE_SCP_IDX,
VCORE_HIFI_IDX,
VCORE_PCIE_IDX,
SRC_SCP_REQ_IDX,
SRC_PMQOS_TATOL_IDX,
SRC_PMQOS_BW0_IDX,
SRC_PMQOS_BW1_IDX,
SRC_PMQOS_BW2_IDX,
SRC_PMQOS_BW3_IDX,
SRC_PMQOS_BW4_IDX,
SRC_TOTAL_EMI_BW_IDX,
SRC_HRT_MD_BW_IDX,
SRC_HRT_DISP_BW_IDX,
SRC_HRT_ISP_BW_IDX,
SRC_MD_SCENARIO_IDX,
SRC_HIFI_SCENARIO_IDX,
SRC_MD_EMI_LATENCY_IDX,
SRC_MAX
};
extern u32 dvfsrc_ct_mode(void);
extern u32 dvfsrc_vcore_mode(void);
#endif /* __HELIO_DVFSRC_MT6853_H */
@@ -0,0 +1,805 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/fb.h>
#include <linux/platform_device.h>
#ifdef CONFIG_MEDIATEK_DRAMC
#include <dramc.h>
#endif
#ifdef CONFIG_MTK_EMI
#include <mt_emi_api.h>
#endif
#include <mt-plat/upmu_common.h>
#include "helio-dvfsrc-ip-v2.h"
#include <helio-dvfsrc-opp.h>
#include <helio-dvfsrc-mt6873.h>
#include <mt-plat/mtk_devinfo.h>
#include <linux/regulator/consumer.h>
#include "mmdvfs_pmqos.h"
#include <linux/sysfs.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/sched/clock.h>
#include <dbgtop.h>
#define DVFSRC_FB_MD_TABLE_SWITCH
#define AUTOK_ENABLE
#define dvfsrc_rmw(offset, val, mask, shift) \
dvfsrc_write(offset, (dvfsrc_read(offset) & ~(mask << shift)) \
| (val << shift))
static struct reg_config dvfsrc_init_configs[][128] = {
{
{ DVFSRC_HRT_REQ_UNIT, 0x0000001E },
{ DVFSRC_DEBOUNCE_TIME, 0x00001965 },
{ DVFSRC_TIMEOUT_NEXTREQ, 0x00000015 },
{ DVFSRC_LEVEL_MASK, 0x000EE000 },
{ DVFSRC_DDR_QOS0, 0x00000019 },
{ DVFSRC_DDR_QOS1, 0x00000026 },
{ DVFSRC_DDR_QOS2, 0x00000033 },
{ DVFSRC_DDR_QOS3, 0x0000003B },
{ DVFSRC_DDR_QOS4, 0x0000004C },
{ DVFSRC_DDR_QOS5, 0x00000066 },
{ DVFSRC_DDR_QOS6, 0x00000066 },
{ DVFSRC_LEVEL_LABEL_0_1, 0x50436053 },
{ DVFSRC_LEVEL_LABEL_2_3, 0x40335042 },
{ DVFSRC_LEVEL_LABEL_4_5, 0x40314032 },
{ DVFSRC_LEVEL_LABEL_6_7, 0x30223023 },
{ DVFSRC_LEVEL_LABEL_8_9, 0x20133021 },
{ DVFSRC_LEVEL_LABEL_10_11, 0x20112012 },
{ DVFSRC_LEVEL_LABEL_12_13, 0x10032010 },
{ DVFSRC_LEVEL_LABEL_14_15, 0x10011002 },
{ DVFSRC_LEVEL_LABEL_16_17, 0x00131000 },
{ DVFSRC_LEVEL_LABEL_18_19, 0x00110012 },
{ DVFSRC_LEVEL_LABEL_20_21, 0x00000010 },
{ DVFSRC_MD_LATENCY_IMPROVE, 0x00000040 },
{ DVFSRC_HRT_BW_BASE, 0x00000004 },
{ DVSFRC_HRT_REQ_MD_URG, 0x000D20D2 },
{ DVFSRC_HRT_REQ_MD_BW_0, 0x00200802 },
{ DVFSRC_HRT_REQ_MD_BW_1, 0x00200802 },
{ DVFSRC_HRT_REQ_MD_BW_2, 0x00200800 },
{ DVFSRC_HRT_REQ_MD_BW_3, 0x00400802 },
{ DVFSRC_HRT_REQ_MD_BW_4, 0x00601404 },
{ DVFSRC_HRT_REQ_MD_BW_5, 0x00D02C09 },
{ DVFSRC_HRT_REQ_MD_BW_6, 0x00000012 },
{ DVFSRC_HRT_REQ_MD_BW_7, 0x00000024 },
{ DVFSRC_HRT_REQ_MD_BW_8, 0x00000000 },
{ DVFSRC_HRT_REQ_MD_BW_9, 0x00000000 },
{ DVFSRC_HRT_REQ_MD_BW_10, 0x00034800 },
{ DVFSRC_HRT1_REQ_MD_BW_0, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_1, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_2, 0x04B12C00 },
{ DVFSRC_HRT1_REQ_MD_BW_3, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_4, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_5, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_6, 0x0000004B },
{ DVFSRC_HRT1_REQ_MD_BW_7, 0x0000005C },
{ DVFSRC_HRT1_REQ_MD_BW_8, 0x00000000 },
{ DVFSRC_HRT1_REQ_MD_BW_9, 0x00000000 },
{ DVFSRC_HRT1_REQ_MD_BW_10, 0x00034800 },
#ifdef DVFSRC_FB_MD_TABLE_SWITCH
{ DVFSRC_95MD_SCEN_BW0_T, 0x40444440 },
{ DVFSRC_95MD_SCEN_BW1_T, 0x44444444 },
{ DVFSRC_95MD_SCEN_BW2_T, 0x00400444 },
{ DVFSRC_95MD_SCEN_BW3_T, 0x60000000 },
{ DVFSRC_95MD_SCEN_BW0, 0x20222220 },
{ DVFSRC_95MD_SCEN_BW1, 0x22222222 },
{ DVFSRC_95MD_SCEN_BW2, 0x00200222 },
{ DVFSRC_95MD_SCEN_BW3, 0x60000000 },
{ DVFSRC_95MD_SCEN_BW4, 0x00000006 },
{ DVFSRC_RSRV_5, 0x00000001 },
#else
{ DVFSRC_95MD_SCEN_BW0_T, 0x40444440 },
{ DVFSRC_95MD_SCEN_BW1_T, 0x44444444 },
{ DVFSRC_95MD_SCEN_BW2_T, 0x00400444 },
{ DVFSRC_95MD_SCEN_BW3_T, 0x60000000 },
{ DVFSRC_95MD_SCEN_BW0, 0x20222220 },
{ DVFSRC_95MD_SCEN_BW1, 0x22222222 },
{ DVFSRC_95MD_SCEN_BW2, 0x00200222 },
{ DVFSRC_95MD_SCEN_BW3, 0x60000000 },
{ DVFSRC_95MD_SCEN_BW4, 0x00000006 },
{ DVFSRC_RSRV_5, 0x00000001 },
#endif
{ DVFSRC_DDR_REQUEST, 0x00004321 },
{ DVFSRC_DDR_REQUEST3, 0x00000065 },
{ DVFSRC_DDR_ADD_REQUEST, 0x66543210 },
{ DVFSRC_HRT_REQUEST, 0x66654321 },
{ DVFSRC_DDR_REQUEST5, 0x54321000 },
{ DVFSRC_DDR_REQUEST7, 0x66000000 },
{ DVFSRC_EMI_MON_DEBOUNCE_TIME, 0x4C2D0000 },
{ DVFSRC_DDR_REQUEST6, 0x66543210 },
{ DVFSRC_VCORE_USER_REQ, 0x00010A29 },
{ DVFSRC_HRT_HIGH_3, 0x18A618A6 },
{ DVFSRC_HRT_HIGH_2, 0x18A61183 },
{ DVFSRC_HRT_HIGH_1, 0x0D690B80 },
{ DVFSRC_HRT_HIGH, 0x070804B0 },
{ DVFSRC_HRT_LOW_3, 0x18A518A5 },
{ DVFSRC_HRT_LOW_2, 0x18A51182 },
{ DVFSRC_HRT_LOW_1, 0x0D680B7F },
{ DVFSRC_HRT_LOW, 0x070704AF },
{ DVFSRC_BASIC_CONTROL_3, 0x00000006 },
{ DVFSRC_INT_EN, 0x00000002 },
{ DVFSRC_QOS_EN, 0x0000407C },
{ DVFSRC_CURRENT_FORCE, 0x00000001 },
{ DVFSRC_BASIC_CONTROL, 0x6698444B },
{ DVFSRC_BASIC_CONTROL, 0x6698054B },
{ DVFSRC_CURRENT_FORCE, 0x00000000 },
{ -1, 0 },
},
/* NULL */
{
{ -1, 0 },
},
};
static ssize_t dvfsrc_level_mask_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%x\n", helio_dvfsrc_level_mask_get());
}
static ssize_t dvfsrc_level_mask_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int level = 0, en = 0;
if (sscanf(buf, "%d %d", &level, &en) != 2)
return -EINVAL;
helio_dvfsrc_level_mask_set(en, level);
return count;
}
static DEVICE_ATTR(dvfsrc_level_mask, 0644,
dvfsrc_level_mask_show, dvfsrc_level_mask_store);
static ssize_t dvfsrc_vcore_settle_time_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
/* DE's comment: settle time was hard code in fw (15,30) */
return sprintf(buf, "rising 15 uS, falling 30 uS for mt6873\n");
}
static DEVICE_ATTR(dvfsrc_vcore_settle_time, 0444,
dvfsrc_vcore_settle_time_show, NULL);
static ssize_t dvfsrc_md_imp_gear_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%x\n", dvfsrc_read(DVFSRC_MD_LATENCY_IMPROVE));
}
static ssize_t dvfsrc_md_imp_gear_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int gear = 0;
if (kstrtoint(buf, 10, &gear))
return -EINVAL;
if (gear >= DDR_OPP_NUM || gear < 0)
return -EINVAL;
dvfsrc_rmw(DVFSRC_MD_LATENCY_IMPROVE, gear, 0x7, 4);
return count;
}
static DEVICE_ATTR(dvfsrc_md_imp_gear, 0644,
dvfsrc_md_imp_gear_show, dvfsrc_md_imp_gear_store);
static ssize_t dvfsrc_md_qos_performance_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int len = 0;
len += snprintf(buf + len, PAGE_SIZE - 1 - len,
"%-12s: 0x%08x\n",
"RSRV_5",
dvfsrc_read(DVFSRC_RSRV_5));
len += snprintf(buf + len, PAGE_SIZE - 1 - len,
"%-12s: 0x%08x\n",
"DVFSRC_MD_TURBO",
dvfsrc_read(DVFSRC_MD_TURBO));
len += snprintf(buf + len, PAGE_SIZE - 1 - len,
"%-12s: 0x%08x\n",
"SCEN_URGENT",
dvfsrc_read(DVFSRC_95MD_SCEN_BW4));
len += snprintf(buf + len, PAGE_SIZE - 1 - len,
"%-12s: 0x%08x, %08x, %08x, %08x\n",
"SCEN_BW_T",
dvfsrc_read(DVFSRC_95MD_SCEN_BW0_T),
dvfsrc_read(DVFSRC_95MD_SCEN_BW1_T),
dvfsrc_read(DVFSRC_95MD_SCEN_BW2_T),
dvfsrc_read(DVFSRC_95MD_SCEN_BW3_T));
len += snprintf(buf + len, PAGE_SIZE - 1 - len,
"%-12s: 0x%08x, %08x, %08x, %08x\n",
"SCEN_BW",
dvfsrc_read(DVFSRC_95MD_SCEN_BW0),
dvfsrc_read(DVFSRC_95MD_SCEN_BW1),
dvfsrc_read(DVFSRC_95MD_SCEN_BW2),
dvfsrc_read(DVFSRC_95MD_SCEN_BW3));
return len;
}
static DEVICE_ATTR(dvfsrc_md_qos_performance, 0444,
dvfsrc_md_qos_performance_show, NULL);
static struct attribute *mt6873_helio_dvfsrc_attrs[] = {
&dev_attr_dvfsrc_level_mask.attr,
&dev_attr_dvfsrc_vcore_settle_time.attr,
&dev_attr_dvfsrc_md_imp_gear.attr,
&dev_attr_dvfsrc_md_qos_performance.attr,
NULL,
};
static struct attribute_group mt6873_helio_dvfsrc_attr_group = {
.name = "helio-dvfsrc",
.attrs = mt6873_helio_dvfsrc_attrs,
};
#define dvfsrc_rmw(offset, val, mask, shift) \
dvfsrc_write(offset, (dvfsrc_read(offset) & ~(mask << shift)) \
| (val << shift))
u32 dvfsrc_get_ddr_qos(void)
{
unsigned int qos_total_bw = dvfsrc_read(DVFSRC_SW_BW_0) +
dvfsrc_read(DVFSRC_SW_BW_1) +
dvfsrc_read(DVFSRC_SW_BW_2) +
dvfsrc_read(DVFSRC_SW_BW_3) +
dvfsrc_read(DVFSRC_SW_BW_4);
if (qos_total_bw < 0x19)
return 0;
else if (qos_total_bw < 0x26)
return 1;
else if (qos_total_bw < 0x33)
return 2;
else if (qos_total_bw < 0x3B)
return 3;
else if (qos_total_bw < 0x4C)
return 4;
else if (qos_total_bw < 0x66)
return 5;
else
return 6;
return 0;
}
static int dvfsrc_get_emi_mon_gear(void)
{
unsigned int total_bw_status;
int i;
total_bw_status = vcorefs_get_total_emi_status() & 0x3F;
for (i = 5; i >= 0 ; i--) {
if ((total_bw_status >> i) > 0)
return i + 1;
}
return 0;
}
static u32 dvfsrc_calc_hrt_opp(int data)
{
if (data < 0x04B0)
return DDR_OPP_6;
else if (data < 0x0708)
return DDR_OPP_5;
else if (data < 0x0B80)
return DDR_OPP_4;
else if (data < 0x0D69)
return DDR_OPP_3;
else if (data < 0x1183)
return DDR_OPP_2;
else if (data < 0x18A6)
return DDR_OPP_1;
else
return DDR_OPP_0;
}
void dvfsrc_set_isp_hrt_bw(int data)
{
data = (data + 29) / 30;
if (data > 0x3FF)
data = 0x3FF;
dvfsrc_write(DVFSRC_ISP_HRT, data);
}
u32 dvfsrc_calc_isp_hrt_opp(int data)
{
return dvfsrc_calc_hrt_opp(((data + 29) / 30) * 30);
}
u32 dvfsrc_get_pcie_vcore_status(void)
{
u32 val, pcie_en;
val = dvfsrc_read(DVFSRC_DEBUG_STA_2);
pcie_en = (val >> DEBUG_STA2_PCIE_SHIFT) & DEBUG_STA2_PCIE_MASK;
if (pcie_en)
return 1;
else
return 0;
}
struct regulator *dvfsrc_vcore_requlator(struct device *dev)
{
return regulator_get(dev, "vgpu11");
}
#ifdef AUTOK_ENABLE
__weak int emmc_autok(void)
{
pr_info("NOT SUPPORT EMMC AUTOK\n");
return 0;
}
__weak int sd_autok(void)
{
pr_info("NOT SUPPORT SD AUTOK\n");
return 0;
}
__weak int sdio_autok(void)
{
pr_info("NOT SUPPORT SDIO AUTOK\n");
return 0;
}
void begin_autok_task(void)
{
/* notify MM DVFS for msdc autok start */
mmdvfs_prepare_action(MMDVFS_PREPARE_CALIBRATION_START);
}
void finish_autok_task(void)
{
/* check if dvfs force is released */
int force = mtk_pm_qos_request(MTK_PM_QOS_VCORE_DVFS_FORCE_OPP);
/* notify MM DVFS for msdc autok finish */
mmdvfs_prepare_action(MMDVFS_PREPARE_CALIBRATION_END);
if (force >= 0 && force < VCORE_DVFS_OPP_NUM)
pr_info("autok task not release force opp: %d\n", force);
}
static void dvfsrc_autok_manager(void)
{
int r = 0;
begin_autok_task();
r = emmc_autok();
pr_info("EMMC autok done: %s\n", (r == 0) ? "Yes" : "No");
r = sd_autok();
pr_info("SD autok done: %s\n", (r == 0) ? "Yes" : "No");
r = sdio_autok();
pr_info("SDIO autok done: %s\n", (r == 0) ? "Yes" : "No");
finish_autok_task();
}
#endif
static void dvfsrc_update_md_scenario(bool blank)
{
#ifdef DVFSRC_FB_MD_TABLE_SWITCH
if (blank)
dvfsrc_write(DVFSRC_95MD_SCEN_BW1_T, 0x22244444);
else
dvfsrc_write(DVFSRC_95MD_SCEN_BW1_T, 0x44444444);
#endif
}
static int dvfsrc_fb_notifier_call(struct notifier_block *self,
unsigned long event, void *data)
{
struct fb_event *evdata = data;
int blank;
if (event != FB_EVENT_BLANK)
return 0;
blank = *(int *)evdata->data;
switch (blank) {
case FB_BLANK_UNBLANK:
dvfsrc_update_md_scenario(false);
break;
case FB_BLANK_POWERDOWN:
dvfsrc_update_md_scenario(true);
break;
default:
break;
}
return 0;
}
static struct notifier_block dvfsrc_fb_notifier = {
.notifier_call = dvfsrc_fb_notifier_call,
};
void helio_dvfsrc_platform_pre_init(struct helio_dvfsrc *dvfsrc)
{
struct platform_device *pdev = to_platform_device(dvfsrc->dev);
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
dvfsrc->spm_regs = devm_ioremap(&pdev->dev,
res->start, resource_size(res));
if (IS_ERR(dvfsrc->spm_regs))
pr_info("not get spm register\n");
}
__weak void mtk_pm_qos_trace_dbg_dump(int mtk_pm_qos_class)
{
}
void dvfsrc_suspend_cb(struct helio_dvfsrc *dvfsrc)
{
int sw_req;
sw_req = dvfsrc_read(DVFSRC_SW_REQ3);
pr_info("[DVFSRC] V:%d, F_OPP:%d, RG:%08x, %08x, %08x, %08x\n",
get_cur_vcore_uv(),
mtk_pm_qos_request(MTK_PM_QOS_VCORE_DVFS_FORCE_OPP),
dvfsrc_read(DVFSRC_CURRENT_LEVEL),
dvfsrc_read(DVFSRC_SW_REQ2),
sw_req,
dvfsrc_read(DVFSRC_DEBUG_STA_0));
if (sw_req & (DDR_SW_AP_MASK << DDR_SW_AP_SHIFT))
mtk_pm_qos_trace_dbg_dump(MTK_PM_QOS_DDR_OPP);
if (sw_req & (VCORE_SW_AP_MASK << VCORE_SW_AP_SHIFT))
mtk_pm_qos_trace_dbg_dump(MTK_PM_QOS_VCORE_OPP);
}
void dvfsrc_resume_cb(struct helio_dvfsrc *dvfsrc)
{
}
void helio_dvfsrc_platform_init(struct helio_dvfsrc *dvfsrc)
{
int spmfw_idx = 0;
struct reg_config *config;
int idx = 0;
sysfs_merge_group(&dvfsrc->dev->kobj, &mt6873_helio_dvfsrc_attr_group);
config = dvfsrc_init_configs[spmfw_idx];
while (config[idx].offset != -1) {
dvfsrc_write(config[idx].offset, config[idx].val);
idx++;
}
#ifdef AUTOK_ENABLE
dvfsrc_autok_manager();
#endif
fb_register_client(&dvfsrc_fb_notifier);
}
int vcore_pmic_to_uv(int pmic_val)
{
return __vcore_pmic_to_uv(pmic_val);
}
int vcore_uv_to_pmic(int vcore_uv)
{
return __vcore_uv_to_pmic(vcore_uv);
}
void get_spm_reg(char *p)
{
p += sprintf(p, "%-24s: 0x%08x\n",
"POWERON_CONFIG_EN",
spm_reg_read(POWERON_CONFIG_EN));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_SW_FLAG_0",
spm_reg_read(SPM_SW_FLAG_0));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_PC_STA",
spm_reg_read(SPM_PC_STA));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVFS_LEVEL",
spm_reg_read(SPM_DVFS_LEVEL));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVS_DFS_LEVEL",
spm_reg_read(SPM_DVS_DFS_LEVEL));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVFS_STA",
spm_reg_read(SPM_DVFS_STA));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVFS_MISC",
spm_reg_read(SPM_DVFS_MISC));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
"SPM_DVFS_CMD0~3",
spm_reg_read(SPM_DVFS_CMD0),
spm_reg_read(SPM_DVFS_CMD1),
spm_reg_read(SPM_DVFS_CMD2),
spm_reg_read(SPM_DVFS_CMD3));
}
void get_opp_info(char *p)
{
#if defined(CONFIG_FPGA_EARLY_PORTING) || !defined(CONFIG_MTK_PMIC_COMMON)
int pmic_val = 0;
#else
int pmic_val = pmic_get_register_value(PMIC_VCORE_ADDR);
#endif
#ifdef CONFIG_MEDIATEK_DRAMC
int ddr_khz = mtk_dramc_get_data_rate() * 1000;
#else
int ddr_khz = 0;
#endif
int vcore_uv = vcore_pmic_to_uv(pmic_val);
p += sprintf(p, "%-10s: %-8u uv (PMIC: 0x%x)\n",
"Vcore", vcore_uv, vcore_uv_to_pmic(vcore_uv));
p += sprintf(p, "%-10s: %-8u khz\n", "DDR", ddr_khz);
p += sprintf(p, "%-10s: %d\n", "CT_MODE", dvfsrc_ct_mode());
p += sprintf(p, "%-10s: %x\n", "V_MODE", dvfsrc_vcore_mode());
}
/* met profile table */
static unsigned int met_vcorefs_src[SRC_MAX];
static char *met_src_name[SRC_MAX] = {
"MD2SPM",
"SRC_DDR_OPP",
"DDR__SW_REQ1_SPM",
"DDR__SW_REQ2_CM",
"DDR__SW_REQ3_PMQOS",
"DDR__QOS_BW",
"DDR__EMI_TOTAL",
"DDR__HRT_BW",
"DDR__HIFI",
"DDR__HIFI_LATENCY",
"DDR__MD_LATENCY",
"DDR__MD_DDR",
"DDR__MD_LEVEL_MASK",
"SRC_VCORE_OPP",
"VCORE__SW_REQ3_PMQOS",
"VCORE__SCP",
"VCORE__HIFI",
"VCORE__PCIE",
"SCP_REQ",
"PMQOS_TATOL",
"PMQOS_BW0",
"PMQOS_BW1",
"PMQOS_BW2",
"PMQOS_BW3",
"PMQOS_BW4",
"TOTAL_EMI_BW",
"HRT_MD_BW",
"HRT_DISP_BW",
"HRT_ISP_BW",
"MD_SCENARIO",
"HIFI_SCENARIO_IDX",
"MD_EMI_LATENCY",
};
/* met profile function */
int vcorefs_get_src_req_num(void)
{
return SRC_MAX;
}
EXPORT_SYMBOL(vcorefs_get_src_req_num);
char **vcorefs_get_src_req_name(void)
{
return met_src_name;
}
EXPORT_SYMBOL(vcorefs_get_src_req_name);
static u32 vcorefs_get_md_level_mask_ddr(void)
{
int md_srclk, vopp;
if (dvfsrc_read(DVFSRC_BASIC_CONTROL_3) & 0x8) {
md_srclk = dvfsrc_read(DVFSRC_DEBUG_STA_0);
vopp = get_cur_vcore_opp();
md_srclk = (md_srclk >> MD_SRC_CLK_DEBUG_SHIFT)
& MD_SRC_CLK_DEBUG_MASK;
if (vopp != 3 && md_srclk == 1)
return 2;
}
return 0;
}
static void vcorefs_get_src_ddr_req(void)
{
unsigned int sw_req;
met_vcorefs_src[DDR_OPP_IDX] =
get_cur_ddr_opp();
sw_req = dvfsrc_read(DVFSRC_SW_REQ1);
met_vcorefs_src[DDR_SW_REQ1_SPM_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
sw_req = dvfsrc_read(DVFSRC_SW_REQ2);
met_vcorefs_src[DDR_SW_REQ2_CM_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
sw_req = dvfsrc_read(DVFSRC_SW_REQ3);
met_vcorefs_src[DDR_SW_REQ3_PMQOS_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
met_vcorefs_src[DDR_QOS_BW_IDX] =
dvfsrc_get_ddr_qos();
met_vcorefs_src[DDR_EMI_TOTAL_IDX] =
dvfsrc_get_emi_mon_gear();
met_vcorefs_src[DDR_HRT_BW_IDX] =
vcorefs_get_hrt_bw_ddr();
met_vcorefs_src[DDR_HIFI_IDX] =
vcorefs_get_hifi_ddr_status();
met_vcorefs_src[DDR_HIFI_LATENCY_IDX] =
vcorefs_get_hifi_rising_ddr();
met_vcorefs_src[DDR_MD_LATENCY_IDX] =
vcorefs_get_md_imp_ddr();
met_vcorefs_src[DDR_MD_DDR_IDX] =
vcorefs_get_md_scenario_ddr();
met_vcorefs_src[DDR_MD_SRCLK_IDX] =
vcorefs_get_md_level_mask_ddr();
}
static void vcorefs_get_src_vcore_req(void)
{
u32 sw_req;
u32 scp_en;
scp_en = vcorefs_get_scp_req_status();
met_vcorefs_src[VCORE_OPP_IDX] =
get_cur_vcore_opp();
sw_req = dvfsrc_read(DVFSRC_SW_REQ3);
met_vcorefs_src[VCORE_SW_REQ3_PMQOS_IDX] =
(sw_req >> VCORE_SW_AP_SHIFT) & VCORE_SW_AP_MASK;
if (scp_en) {
sw_req = dvfsrc_read(DVFSRC_VCORE_REQUEST);
met_vcorefs_src[VCORE_SCP_IDX] =
(sw_req >> VCORE_SCP_GEAR_SHIFT) & VCORE_SCP_GEAR_MASK;
} else
met_vcorefs_src[VCORE_SCP_IDX] = 0;
met_vcorefs_src[VCORE_HIFI_IDX] =
vcorefs_get_hifi_vcore_status();
met_vcorefs_src[VCORE_PCIE_IDX] =
dvfsrc_get_pcie_vcore_status();
}
static void vcorefs_get_src_misc_info(void)
{
u32 qos_bw0, qos_bw1, qos_bw2, qos_bw3, qos_bw4;
qos_bw0 = dvfsrc_read(DVFSRC_SW_BW_0);
qos_bw1 = dvfsrc_read(DVFSRC_SW_BW_1);
qos_bw2 = dvfsrc_read(DVFSRC_SW_BW_2);
qos_bw3 = dvfsrc_read(DVFSRC_SW_BW_3);
qos_bw4 = dvfsrc_read(DVFSRC_SW_BW_4);
met_vcorefs_src[SRC_MD2SPM_IDX] =
vcorefs_get_md_scenario() & 0x1FFFF;
met_vcorefs_src[SRC_SCP_REQ_IDX] =
vcorefs_get_scp_req_status();
met_vcorefs_src[SRC_PMQOS_TATOL_IDX] =
qos_bw0 + qos_bw1 + qos_bw2 + qos_bw3 + qos_bw4;
met_vcorefs_src[SRC_PMQOS_BW0_IDX] =
qos_bw0;
met_vcorefs_src[SRC_PMQOS_BW1_IDX] =
qos_bw1;
met_vcorefs_src[SRC_PMQOS_BW2_IDX] =
qos_bw2;
met_vcorefs_src[SRC_PMQOS_BW3_IDX] =
qos_bw3;
met_vcorefs_src[SRC_PMQOS_BW4_IDX] =
qos_bw4;
met_vcorefs_src[SRC_HRT_MD_BW_IDX] =
dvfsrc_get_md_bw();
met_vcorefs_src[SRC_HRT_ISP_BW_IDX] =
dvfsrc_read(DVFSRC_ISP_HRT);
met_vcorefs_src[SRC_MD_SCENARIO_IDX] =
vcorefs_get_md_scenario();
met_vcorefs_src[SRC_HIFI_SCENARIO_IDX] =
vcorefs_get_hifi_scenario();
met_vcorefs_src[SRC_MD_EMI_LATENCY_IDX] =
vcorefs_get_md_emi_latency_status();
}
int dvfsrc_latch_register(int enable)
{
#ifdef CONFIG_MTK_DBGTOP
return mtk_dbgtop_cfg_dvfsrc(1);
#endif
return 0;
}
unsigned int *vcorefs_get_src_req(void)
{
vcorefs_get_src_ddr_req();
vcorefs_get_src_vcore_req();
vcorefs_get_src_misc_info();
vcorefs_trace_qos();
return met_vcorefs_src;
}
EXPORT_SYMBOL(vcorefs_get_src_req);
int get_cur_ddr_ratio(void)
{
int idx;
if (!is_dvfsrc_enabled())
return 0;
idx = get_cur_vcore_dvfs_opp();
if (idx >= VCORE_DVFS_OPP_NUM)
return 0;
if (get_ddr_opp(idx) < DDR_OPP_6)
return 8;
else
return 4;
}
EXPORT_SYMBOL(get_cur_ddr_ratio);
@@ -0,0 +1,61 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_MT6873_H
#define __HELIO_DVFSRC_MT6873_H
#include <mach/upmu_hw.h>
#define PMIC_VCORE_ADDR PMIC_RG_BUCK_VGPU11_VOSEL
#define VCORE_BASE_UV 400000
#define VCORE_STEP_UV 6250
/* PMIC */
#define __vcore_pmic_to_uv(pmic) \
(((pmic) * VCORE_STEP_UV) + VCORE_BASE_UV)
#define __vcore_uv_to_pmic(uv) /* pmic >= uv */ \
((((uv) - VCORE_BASE_UV) + (VCORE_STEP_UV - 1)) / VCORE_STEP_UV)
enum met_src_index {
SRC_MD2SPM_IDX = 0,
DDR_OPP_IDX,
DDR_SW_REQ1_SPM_IDX,
DDR_SW_REQ2_CM_IDX,
DDR_SW_REQ3_PMQOS_IDX,
DDR_QOS_BW_IDX,
DDR_EMI_TOTAL_IDX,
DDR_HRT_BW_IDX,
DDR_HIFI_IDX,
DDR_HIFI_LATENCY_IDX,
DDR_MD_LATENCY_IDX,
DDR_MD_DDR_IDX,
DDR_MD_SRCLK_IDX,
VCORE_OPP_IDX,
VCORE_SW_REQ3_PMQOS_IDX,
VCORE_SCP_IDX,
VCORE_HIFI_IDX,
VCORE_PCIE_IDX,
SRC_SCP_REQ_IDX,
SRC_PMQOS_TATOL_IDX,
SRC_PMQOS_BW0_IDX,
SRC_PMQOS_BW1_IDX,
SRC_PMQOS_BW2_IDX,
SRC_PMQOS_BW3_IDX,
SRC_PMQOS_BW4_IDX,
SRC_TOTAL_EMI_BW_IDX,
SRC_HRT_MD_BW_IDX,
SRC_HRT_DISP_BW_IDX,
SRC_HRT_ISP_BW_IDX,
SRC_MD_SCENARIO_IDX,
SRC_HIFI_SCENARIO_IDX,
SRC_MD_EMI_LATENCY_IDX,
SRC_MAX
};
extern u32 dvfsrc_ct_mode(void);
extern u32 dvfsrc_vcore_mode(void);
#endif /* __HELIO_DVFSRC_MT6873_H */
@@ -0,0 +1,657 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/platform_device.h>
#ifdef CONFIG_MEDIATEK_DRAMC
#include <dramc.h>
#endif
/*
#ifdef CONFIG_MTK_EMI
#include <mt_emi_api.h>
#endif
*/
#include <mt-plat/upmu_common.h>
#include "helio-dvfsrc-ip-v2.h"
#include <helio-dvfsrc-opp.h>
#include <helio-dvfsrc-mt6885.h>
#include <mt-plat/mtk_devinfo.h>
#include <linux/regulator/consumer.h>
#include "mmdvfs_pmqos.h"
#include <linux/sysfs.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/sched/clock.h>
#ifdef CONFIG_MTK_DBGTOP
#include <dbgtop.h>
#endif
#define DVFSRC_1600_FLOOR
#define AUTOK_ENABLE
static struct reg_config dvfsrc_init_configs[][128] = {
{
{ DVFSRC_HRT_REQ_UNIT, 0x0000001E },
{ DVFSRC_DEBOUNCE_TIME, 0x00001965 },
{ DVFSRC_TIMEOUT_NEXTREQ, 0x00000015 },
{ DVFSRC_LEVEL_MASK, 0x001DC000 },
/* TODO: Check with DE, (freq*8*0.8/100)*/
{ DVFSRC_DDR_QOS0, 0x00000033 },
{ DVFSRC_DDR_QOS1, 0x0000004C },
{ DVFSRC_DDR_QOS2, 0x00000062 },
{ DVFSRC_DDR_QOS3, 0x00000077 },
{ DVFSRC_DDR_QOS4, 0x00000099 },
{ DVFSRC_DDR_QOS5, 0x000000CC },
{ DVFSRC_DDR_QOS6, 0x000000CC },
{ DVFSRC_LEVEL_LABEL_0_1, 0x50436053 },
{ DVFSRC_LEVEL_LABEL_2_3, 0x40335042 },
{ DVFSRC_LEVEL_LABEL_4_5, 0x40314032 },
{ DVFSRC_LEVEL_LABEL_6_7, 0x30223023 },
{ DVFSRC_LEVEL_LABEL_8_9, 0x30203021 },
{ DVFSRC_LEVEL_LABEL_10_11, 0x20122013 },
{ DVFSRC_LEVEL_LABEL_12_13, 0x20102011 },
{ DVFSRC_LEVEL_LABEL_14_15, 0x10021003 },
{ DVFSRC_LEVEL_LABEL_16_17, 0x10001001 },
{ DVFSRC_LEVEL_LABEL_18_19, 0x00120013 },
{ DVFSRC_LEVEL_LABEL_20_21, 0x00100011 },
{ DVFSRC_MD_LATENCY_IMPROVE, 0x00000040 },
{ DVSFRC_HRT_REQ_MD_URG, 0x001AA1AA },
{ DVFSRC_HRT_REQ_MD_BW_0, 0x00200802 },
{ DVFSRC_HRT_REQ_MD_BW_1, 0x00200802 },
{ DVFSRC_HRT_REQ_MD_BW_2, 0x00200800 },
{ DVFSRC_HRT_REQ_MD_BW_3, 0x00400802 },
{ DVFSRC_HRT_REQ_MD_BW_4, 0x00601404 },
{ DVFSRC_HRT_REQ_MD_BW_5, 0x00D02C09 },
{ DVFSRC_HRT_REQ_MD_BW_6, 0x00000012 },
{ DVFSRC_HRT_REQ_MD_BW_7, 0x00000024 },
{ DVFSRC_HRT_REQ_MD_BW_8, 0x00000000 },
{ DVFSRC_HRT_REQ_MD_BW_9, 0x00000000 },
{ DVFSRC_HRT_REQ_MD_BW_10, 0x0006A800 },
{ DVFSRC_HRT1_REQ_MD_BW_0, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_1, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_2, 0x04B12C00 },
{ DVFSRC_HRT1_REQ_MD_BW_3, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_4, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_5, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_6, 0x0000004B },
{ DVFSRC_HRT1_REQ_MD_BW_7, 0x0000005C },
{ DVFSRC_HRT1_REQ_MD_BW_8, 0x00000000 },
{ DVFSRC_HRT1_REQ_MD_BW_9, 0x00000000 },
{ DVFSRC_HRT1_REQ_MD_BW_10, 0x0006A800 },
{ DVFSRC_95MD_SCEN_BW0_T, 0x40444440 },
{ DVFSRC_95MD_SCEN_BW1_T, 0x44444444 },
{ DVFSRC_95MD_SCEN_BW2_T, 0x00400444 },
{ DVFSRC_95MD_SCEN_BW3_T, 0x60000000 },
{ DVFSRC_95MD_SCEN_BW0, 0x20222220 },
{ DVFSRC_95MD_SCEN_BW1, 0x22222222 },
{ DVFSRC_95MD_SCEN_BW2, 0x00200222 },
{ DVFSRC_95MD_SCEN_BW3, 0x60000000 },
{ DVFSRC_95MD_SCEN_BW4, 0x00000006 },
{ DVFSRC_DDR_REQUEST, 0x00004321 },
{ DVFSRC_DDR_REQUEST3, 0x00000065 },
{ DVFSRC_DDR_ADD_REQUEST, 0x66543210 },
{ DVFSRC_EMI_ADD_REQUEST, 0x55543210 },
{ DVFSRC_HRT_REQUEST, 0x66654321 },
{ DVFSRC_DDR_REQUEST5, 0x54321000 },
{ DVFSRC_DDR_REQUEST7, 0x66000000 },
{ DVFSRC_EMI_MON_DEBOUNCE_TIME, 0x4C2D0000 },
{ DVFSRC_VCORE_REQUEST4, 0x21110000 },
{ DVFSRC_HRT_HIGH_3, 0x314C314C },
{ DVFSRC_HRT_HIGH_2, 0x314C2306 },
{ DVFSRC_HRT_HIGH_1, 0x1AD21700 },
{ DVFSRC_HRT_HIGH, 0x0E100960 },
{ DVFSRC_HRT_LOW_3, 0x314B314B },
{ DVFSRC_HRT_LOW_2, 0x314B2305 },
{ DVFSRC_HRT_LOW_1, 0x1AD116FF },
{ DVFSRC_HRT_LOW, 0x0E0F095F },
{ DVFSRC_BASIC_CONTROL_3, 0x00000006 },
{ DVFSRC_INT_EN, 0x00000002 },
{ DVFSRC_QOS_EN, 0x0000407C },
{ DVFSRC_CURRENT_FORCE, 0x00000001 },
{ DVFSRC_BASIC_CONTROL, 0x66B8404B },
{ DVFSRC_BASIC_CONTROL, 0x66B8014B },
{ DVFSRC_CURRENT_FORCE, 0x00000000 },
{ -1, 0 },
},
/* NULL */
{
{ -1, 0 },
},
};
static ssize_t dvfsrc_level_mask_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%x\n", helio_dvfsrc_level_mask_get());
}
static ssize_t dvfsrc_level_mask_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int level = 0, en = 0;
if (sscanf(buf, "%d %d", &level, &en) != 2)
return -EINVAL;
helio_dvfsrc_level_mask_set(en, level);
return count;
}
static DEVICE_ATTR(dvfsrc_level_mask, 0644,
dvfsrc_level_mask_show, dvfsrc_level_mask_store);
static ssize_t dvfsrc_vcore_settle_time_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
/* DE's comment: settle time was hard code in fw (15,30) */
return sprintf(buf, "rising 15 uS, falling 30 uS for mt6885\n");
}
static DEVICE_ATTR(dvfsrc_vcore_settle_time, 0444,
dvfsrc_vcore_settle_time_show, NULL);
static struct attribute *mt6885_helio_dvfsrc_attrs[] = {
&dev_attr_dvfsrc_level_mask.attr,
&dev_attr_dvfsrc_vcore_settle_time.attr,
NULL,
};
static struct attribute_group mt6885_helio_dvfsrc_attr_group = {
.name = "helio-dvfsrc",
.attrs = mt6885_helio_dvfsrc_attrs,
};
#define dvfsrc_rmw(offset, val, mask, shift) \
dvfsrc_write(offset, (dvfsrc_read(offset) & ~(mask << shift)) \
| (val << shift))
u32 dvfsrc_get_ddr_qos(void)
{
unsigned int qos_total_bw = dvfsrc_read(DVFSRC_SW_BW_0) +
dvfsrc_read(DVFSRC_SW_BW_1) +
dvfsrc_read(DVFSRC_SW_BW_2) +
dvfsrc_read(DVFSRC_SW_BW_3) +
dvfsrc_read(DVFSRC_SW_BW_4);
/* TODO: Check with DE, (freq*8*0.8/100)*/
if (qos_total_bw < 0x33)
return 0;
else if (qos_total_bw < 0x4C)
return 1;
else if (qos_total_bw < 0x62)
return 2;
else if (qos_total_bw < 0x77)
return 3;
else if (qos_total_bw < 0x99)
return 4;
else if (qos_total_bw < 0xCC)
return 5;
else
return 6;
return 0;
}
static int dvfsrc_get_emi_mon_gear(void)
{
unsigned int total_bw_status;
int i;
total_bw_status = vcorefs_get_total_emi_status() & 0x3F;
for (i = 5; i >= 0 ; i--) {
if ((total_bw_status >> i) > 0)
return i + 1;
}
return 0;
}
static u32 dvfsrc_calc_hrt_opp(int data)
{
if (data < 0x0960)
return DDR_OPP_6;
else if (data < 0x0E10)
return DDR_OPP_5;
else if (data < 0x1700)
return DDR_OPP_4;
else if (data < 0x1AD2)
return DDR_OPP_3;
else if (data < 0x2306)
return DDR_OPP_2;
else if (data < 0x314C)
return DDR_OPP_1;
else
return DDR_OPP_0;
}
void dvfsrc_set_isp_hrt_bw(int data)
{
data = (data + 29) / 30;
if (data > 0x3FF)
data = 0x3FF;
dvfsrc_write(DVFSRC_ISP_HRT, data);
}
u32 dvfsrc_calc_isp_hrt_opp(int data)
{
return dvfsrc_calc_hrt_opp(((data + 29) / 30) * 30);
}
struct regulator *dvfsrc_vcore_requlator(struct device *dev)
{
return regulator_get(dev, "vgpu11");
}
#ifdef AUTOK_ENABLE
__weak int emmc_autok(void)
{
pr_info("NOT SUPPORT EMMC AUTOK\n");
return 0;
}
__weak int sd_autok(void)
{
pr_info("NOT SUPPORT SD AUTOK\n");
return 0;
}
__weak int sdio_autok(void)
{
pr_info("NOT SUPPORT SDIO AUTOK\n");
return 0;
}
void begin_autok_task(void)
{
/* notify MM DVFS for msdc autok start */
mmdvfs_prepare_action(MMDVFS_PREPARE_CALIBRATION_START);
}
void finish_autok_task(void)
{
/* check if dvfs force is released */
int force = mtk_pm_qos_request(MTK_PM_QOS_VCORE_DVFS_FORCE_OPP);
/* notify MM DVFS for msdc autok finish */
mmdvfs_prepare_action(MMDVFS_PREPARE_CALIBRATION_END);
if (force >= 0 && force < 13)
pr_info("autok task not release force opp: %d\n", force);
}
static void dvfsrc_autok_manager(void)
{
int r = 0;
begin_autok_task();
r = emmc_autok();
pr_info("EMMC autok done: %s\n", (r == 0) ? "Yes" : "No");
r = sd_autok();
pr_info("SD autok done: %s\n", (r == 0) ? "Yes" : "No");
r = sdio_autok();
pr_info("SDIO autok done: %s\n", (r == 0) ? "Yes" : "No");
finish_autok_task();
}
#endif
void helio_dvfsrc_platform_pre_init(struct helio_dvfsrc *dvfsrc)
{
struct platform_device *pdev = to_platform_device(dvfsrc->dev);
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
dvfsrc->spm_regs = devm_ioremap(&pdev->dev,
res->start, resource_size(res));
if (IS_ERR(dvfsrc->spm_regs))
pr_info("not get spm register\n");
}
void helio_dvfsrc_platform_init(struct helio_dvfsrc *dvfsrc)
{
int spmfw_idx = 0;
struct reg_config *config;
int idx = 0;
sysfs_merge_group(&dvfsrc->dev->kobj, &mt6885_helio_dvfsrc_attr_group);
config = dvfsrc_init_configs[spmfw_idx];
while (config[idx].offset != -1) {
dvfsrc_write(config[idx].offset, config[idx].val);
idx++;
}
#ifdef AUTOK_ENABLE
dvfsrc_autok_manager();
#endif
}
int vcore_pmic_to_uv(int pmic_val)
{
return __vcore_pmic_to_uv(pmic_val);
}
int vcore_uv_to_pmic(int vcore_uv)
{
return __vcore_uv_to_pmic(vcore_uv);
}
void get_spm_reg(char *p)
{
p += sprintf(p, "%-24s: 0x%08x\n",
"POWERON_CONFIG_EN",
spm_reg_read(POWERON_CONFIG_EN));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_SW_FLAG_0",
spm_reg_read(SPM_SW_FLAG_0));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_PC_STA",
spm_reg_read(SPM_PC_STA));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVFS_LEVEL",
spm_reg_read(SPM_DVFS_LEVEL));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVS_DFS_LEVEL",
spm_reg_read(SPM_DVS_DFS_LEVEL));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVFS_STA",
spm_reg_read(SPM_DVFS_STA));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVFS_MISC",
spm_reg_read(SPM_DVFS_MISC));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
"SPM_DVFS_CMD0~3",
spm_reg_read(SPM_DVFS_CMD0),
spm_reg_read(SPM_DVFS_CMD1),
spm_reg_read(SPM_DVFS_CMD2),
spm_reg_read(SPM_DVFS_CMD3));
}
void get_opp_info(char *p)
{
#if defined(CONFIG_FPGA_EARLY_PORTING) || !defined(CONFIG_MTK_PMIC_COMMON)
int pmic_val = 0;
#else
int pmic_val = pmic_get_register_value(PMIC_VCORE_ADDR);
#endif
#ifdef CONFIG_MEDIATEK_DRAMC
int ddr_khz = mtk_dramc_get_data_rate() * 1000;
#else
int ddr_khz = 0;
#endif
int vcore_uv = vcore_pmic_to_uv(pmic_val);
p += sprintf(p, "%-10s: %-8u uv (PMIC: 0x%x)\n",
"Vcore", vcore_uv, vcore_uv_to_pmic(vcore_uv));
p += sprintf(p, "%-10s: %-8u khz\n", "DDR", ddr_khz);
}
/* met profile table */
static unsigned int met_vcorefs_src[SRC_MAX];
static char *met_src_name[SRC_MAX] = {
"MD2SPM",
"SRC_DDR_OPP",
"DDR__SW_REQ1_SPM",
"DDR__SW_REQ2_CM",
"DDR__SW_REQ3_PMQOS",
"DDR__SW_REQ4_MD",
"DDR__SW_REQ8_MCUSYS",
"DDR__QOS_BW",
"DDR__EMI_TOTAL",
"DDR__HRT_BW",
"DDR__HIFI",
"DDR__HIFI_LATENCY",
"DDR__MD_LATENCY",
"DDR__MD_DDR",
"DDR__MD_LEVEL_MASK",
"SRC_VCORE_OPP",
"VCORE__SW_REQ3_PMQOS",
"VCORE__SCP",
"VCORE__HIFI",
"SCP_REQ",
"PMQOS_TATOL",
"PMQOS_BW0",
"PMQOS_BW1",
"PMQOS_BW2",
"PMQOS_BW3",
"PMQOS_BW4",
"TOTAL_EMI_BW",
"HRT_MD_BW",
"HRT_DISP_BW",
"HRT_ISP_BW",
"MD_SCENARIO",
"HIFI_SCENARIO_IDX",
"MD_EMI_LATENCY",
};
/* met profile function */
int vcorefs_get_src_req_num(void)
{
return SRC_MAX;
}
EXPORT_SYMBOL(vcorefs_get_src_req_num);
char **vcorefs_get_src_req_name(void)
{
return met_src_name;
}
EXPORT_SYMBOL(vcorefs_get_src_req_name);
static u32 vcorefs_get_md_level_mask_ddr(void)
{
int md_srclk = dvfsrc_read(DVFSRC_DEBUG_STA_0);
int level_mask_en;
int vopp = get_cur_vcore_opp();
level_mask_en = dvfsrc_read(DVFSRC_BASIC_CONTROL_3) & 0x8;
md_srclk = (md_srclk >> MD_SRC_CLK_DEBUG_SHIFT) & MD_SRC_CLK_DEBUG_MASK;
if (vopp != 3 && md_srclk == 1 && level_mask_en == 8)
return 2;
return 0;
}
static void vcorefs_get_src_ddr_req(void)
{
unsigned int sw_req;
met_vcorefs_src[DDR_OPP_IDX] =
get_cur_ddr_opp();
sw_req = dvfsrc_read(DVFSRC_SW_REQ1);
met_vcorefs_src[DDR_SW_REQ1_SPM_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
sw_req = dvfsrc_read(DVFSRC_SW_REQ2);
met_vcorefs_src[DDR_SW_REQ2_CM_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
sw_req = dvfsrc_read(DVFSRC_SW_REQ3);
met_vcorefs_src[DDR_SW_REQ3_PMQOS_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
sw_req = dvfsrc_read(DVFSRC_SW_REQ4);
met_vcorefs_src[DDR_SW_REQ4_MD_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
sw_req = dvfsrc_read(DVFSRC_SW_REQ8);
met_vcorefs_src[DDR_SW_REQ8_MCUSYS_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
met_vcorefs_src[DDR_QOS_BW_IDX] =
dvfsrc_get_ddr_qos();
met_vcorefs_src[DDR_EMI_TOTAL_IDX] =
dvfsrc_get_emi_mon_gear();
met_vcorefs_src[DDR_HRT_BW_IDX] =
vcorefs_get_hrt_bw_ddr();
met_vcorefs_src[DDR_HIFI_IDX] =
vcorefs_get_hifi_ddr_status();
met_vcorefs_src[DDR_HIFI_LATENCY_IDX] =
vcorefs_get_hifi_rising_ddr();
met_vcorefs_src[DDR_MD_LATENCY_IDX] =
vcorefs_get_md_imp_ddr();
met_vcorefs_src[DDR_MD_DDR_IDX] =
vcorefs_get_md_scenario_ddr();
met_vcorefs_src[DDR_MD_SRCLK_IDX] =
vcorefs_get_md_level_mask_ddr();
}
static void vcorefs_get_src_vcore_req(void)
{
u32 sw_req;
u32 scp_en;
scp_en = vcorefs_get_scp_req_status();
met_vcorefs_src[VCORE_OPP_IDX] =
get_cur_vcore_opp();
sw_req = dvfsrc_read(DVFSRC_SW_REQ3);
met_vcorefs_src[VCORE_SW_REQ3_PMQOS_IDX] =
(sw_req >> VCORE_SW_AP_SHIFT) & VCORE_SW_AP_MASK;
if (scp_en) {
sw_req = dvfsrc_read(DVFSRC_VCORE_REQUEST);
met_vcorefs_src[VCORE_SCP_IDX] =
(sw_req >> VCORE_SCP_GEAR_SHIFT) & VCORE_SCP_GEAR_MASK;
} else
met_vcorefs_src[VCORE_SCP_IDX] = 0;
met_vcorefs_src[VCORE_HIFI_IDX] =
vcorefs_get_hifi_vcore_status();
}
static void vcorefs_get_src_misc_info(void)
{
#if 0 //def CONFIG_MTK_EMI
unsigned int total_bw_last = (get_emi_bwvl(0) & 0x7F) * 813;
#endif
u32 qos_bw0, qos_bw1, qos_bw2, qos_bw3, qos_bw4;
qos_bw0 = dvfsrc_read(DVFSRC_SW_BW_0);
qos_bw1 = dvfsrc_read(DVFSRC_SW_BW_1);
qos_bw2 = dvfsrc_read(DVFSRC_SW_BW_2);
qos_bw3 = dvfsrc_read(DVFSRC_SW_BW_3);
qos_bw4 = dvfsrc_read(DVFSRC_SW_BW_4);
met_vcorefs_src[SRC_MD2SPM_IDX] =
vcorefs_get_md_scenario() & 0x1FFFF;
met_vcorefs_src[SRC_SCP_REQ_IDX] =
vcorefs_get_scp_req_status();
met_vcorefs_src[SRC_PMQOS_TATOL_IDX] =
qos_bw0 + qos_bw1 + qos_bw2 + qos_bw3 + qos_bw4;
met_vcorefs_src[SRC_PMQOS_BW0_IDX] =
qos_bw0;
met_vcorefs_src[SRC_PMQOS_BW1_IDX] =
qos_bw1;
met_vcorefs_src[SRC_PMQOS_BW2_IDX] =
qos_bw2;
met_vcorefs_src[SRC_PMQOS_BW3_IDX] =
qos_bw3;
met_vcorefs_src[SRC_PMQOS_BW4_IDX] =
qos_bw4;
#if 0
def CONFIG_MTK_EMI
met_vcorefs_src[SRC_TOTAL_EMI_BW_IDX] =
total_bw_last;
#endif
met_vcorefs_src[SRC_HRT_MD_BW_IDX] =
dvfsrc_get_md_bw();
met_vcorefs_src[SRC_HRT_ISP_BW_IDX] =
dvfsrc_read(DVFSRC_ISP_HRT);
met_vcorefs_src[SRC_MD_SCENARIO_IDX] =
vcorefs_get_md_scenario();
met_vcorefs_src[SRC_HIFI_SCENARIO_IDX] =
vcorefs_get_hifi_scenario();
met_vcorefs_src[SRC_MD_EMI_LATENCY_IDX] =
vcorefs_get_md_emi_latency_status();
}
int dvfsrc_latch_register(int enable)
{
#ifdef CONFIG_MTK_DBGTOP
return mtk_dbgtop_cfg_dvfsrc(1);
#else
return 0;
#endif
}
unsigned int *vcorefs_get_src_req(void)
{
vcorefs_get_src_ddr_req();
vcorefs_get_src_vcore_req();
vcorefs_get_src_misc_info();
vcorefs_trace_qos();
return met_vcorefs_src;
}
EXPORT_SYMBOL(vcorefs_get_src_req);
int get_cur_ddr_ratio(void)
{
int idx;
if (!is_dvfsrc_enabled())
return 0;
idx = get_cur_vcore_dvfs_opp();
if (idx >= VCORE_DVFS_OPP_NUM)
return 0;
if ((get_ddr_opp(idx) < DDR_OPP_3) || (idx == 10))
return 8;
else
return 4;
}
EXPORT_SYMBOL(get_cur_ddr_ratio);
@@ -0,0 +1,71 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_MT6885_H
#define __HELIO_DVFSRC_MT6885_H
#include <mach/upmu_hw.h>
#define PMIC_VCORE_ADDR PMIC_RG_BUCK_VGPU11_VOSEL
#define VCORE_BASE_UV 400000
#define VCORE_STEP_UV 6250
#define PMIC_VSRAM_OTHERS_ADDR PMIC_RG_LDO_VSRAM_OTHERS_VOSEL
#define VSRAM_BASE_UV 500000
#define VSRAM_STEP_UV 6250
/* PMIC */
#define __vcore_pmic_to_uv(pmic) \
(((pmic) * VCORE_STEP_UV) + VCORE_BASE_UV)
#define __vcore_uv_to_pmic(uv) /* pmic >= uv */ \
((((uv) - VCORE_BASE_UV) + (VCORE_STEP_UV - 1)) / VCORE_STEP_UV)
/* VSRAM_OTHERS */
#define vsram_pmic_to_uv(pmic) \
(((pmic) * VSRAM_STEP_UV) + VSRAM_BASE_UV)
#define vsram_uv_to_pmic(uv) /* pmic >= uv */ \
((((uv) - VSRAM_BASE_UV) + (VSRAM_STEP_UV - 1)) / VSRAM_STEP_UV)
enum met_src_index {
SRC_MD2SPM_IDX = 0,
DDR_OPP_IDX,
DDR_SW_REQ1_SPM_IDX,
DDR_SW_REQ2_CM_IDX,
DDR_SW_REQ3_PMQOS_IDX,
DDR_SW_REQ4_MD_IDX,
DDR_SW_REQ8_MCUSYS_IDX,
DDR_QOS_BW_IDX,
DDR_EMI_TOTAL_IDX,
DDR_HRT_BW_IDX,
DDR_HIFI_IDX,
DDR_HIFI_LATENCY_IDX,
DDR_MD_LATENCY_IDX,
DDR_MD_DDR_IDX,
DDR_MD_SRCLK_IDX,
VCORE_OPP_IDX,
VCORE_SW_REQ3_PMQOS_IDX,
VCORE_SCP_IDX,
VCORE_HIFI_IDX,
SRC_SCP_REQ_IDX,
SRC_PMQOS_TATOL_IDX,
SRC_PMQOS_BW0_IDX,
SRC_PMQOS_BW1_IDX,
SRC_PMQOS_BW2_IDX,
SRC_PMQOS_BW3_IDX,
SRC_PMQOS_BW4_IDX,
SRC_TOTAL_EMI_BW_IDX,
SRC_HRT_MD_BW_IDX,
SRC_HRT_DISP_BW_IDX,
SRC_HRT_ISP_BW_IDX,
SRC_MD_SCENARIO_IDX,
SRC_HIFI_SCENARIO_IDX,
SRC_MD_EMI_LATENCY_IDX,
SRC_MAX
};
#endif /* __HELIO_DVFSRC_MT6885_H */
@@ -0,0 +1,689 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/platform_device.h>
#ifdef CONFIG_MEDIATEK_DRAMC
#include <dramc.h>
#endif
/*
#ifdef CONFIG_MTK_EMI
#include <mt_emi_api.h>
#endif
*/
#include <mt-plat/upmu_common.h>
#include "helio-dvfsrc-ip-v2.h"
#include <helio-dvfsrc-opp.h>
#include <helio-dvfsrc-mt6893.h>
#include <mt-plat/mtk_devinfo.h>
#include <linux/regulator/consumer.h>
#include "mmdvfs_pmqos.h"
#include <linux/sysfs.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/sched/clock.h>
#ifdef CONFIG_MTK_DBGTOP
#include <dbgtop.h>
#endif
//#define AUTOK_ENABLE
static struct reg_config dvfsrc_init_configs[][128] = {
{
{ DVFSRC_HRT_REQ_UNIT, 0x0000001E },
{ DVFSRC_DEBOUNCE_TIME, 0x00001965 },
{ DVFSRC_TIMEOUT_NEXTREQ, 0x0000001E },
{ DVFSRC_LEVEL_MASK, 0x001DC000 },
{ DVFSRC_LEVEL_MASK, 0x00CCCC80 },
{ DVFSRC_DDR_QOS0, 0x00000033 },
{ DVFSRC_DDR_QOS1, 0x0000004C },
{ DVFSRC_DDR_QOS2, 0x00000062 },
{ DVFSRC_DDR_QOS3, 0x00000077 },
{ DVFSRC_DDR_QOS4, 0x00000099 },
{ DVFSRC_DDR_QOS5, 0x000000CC },
{ DVFSRC_DDR_QOS6, 0x000000EE },
{ DVFSRC_LEVEL_LABEL_0_1, 0x60547064 },
{ DVFSRC_LEVEL_LABEL_2_3, 0x50436053 },
{ DVFSRC_LEVEL_LABEL_4_5, 0x40335042 },
{ DVFSRC_LEVEL_LABEL_6_7, 0x40314032 },
{ DVFSRC_LEVEL_LABEL_8_9, 0x30223023 },
{ DVFSRC_LEVEL_LABEL_10_11, 0x30203021 },
{ DVFSRC_LEVEL_LABEL_12_13, 0x20122013 },
{ DVFSRC_LEVEL_LABEL_14_15, 0x20102011 },
{ DVFSRC_LEVEL_LABEL_16_17, 0x10021003 },
{ DVFSRC_LEVEL_LABEL_18_19, 0x10001001 },
{ DVFSRC_LEVEL_LABEL_20_21, 0x00120013 },
{ DVFSRC_LEVEL_LABEL_22_23, 0x00100011 },
{ DVFSRC_MD_LATENCY_IMPROVE, 0x00000040 },
{ DVSFRC_HRT_REQ_MD_URG, 0x001AA1AA },
{ DVFSRC_HRT_REQ_MD_BW_0, 0x00200802 },
{ DVFSRC_HRT_REQ_MD_BW_1, 0x00200802 },
{ DVFSRC_HRT_REQ_MD_BW_2, 0x00200800 },
{ DVFSRC_HRT_REQ_MD_BW_3, 0x00400802 },
{ DVFSRC_HRT_REQ_MD_BW_4, 0x00601404 },
{ DVFSRC_HRT_REQ_MD_BW_5, 0x00D02C09 },
{ DVFSRC_HRT_REQ_MD_BW_6, 0x00000012 },
{ DVFSRC_HRT_REQ_MD_BW_7, 0x00000024 },
{ DVFSRC_HRT_REQ_MD_BW_8, 0x00000000 },
{ DVFSRC_HRT_REQ_MD_BW_9, 0x00000000 },
{ DVFSRC_HRT_REQ_MD_BW_10, 0x0006A800 },
{ DVFSRC_HRT1_REQ_MD_BW_0, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_1, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_2, 0x04B12C00 },
{ DVFSRC_HRT1_REQ_MD_BW_3, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_4, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_5, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_6, 0x0000004B },
{ DVFSRC_HRT1_REQ_MD_BW_7, 0x0000005C },
{ DVFSRC_HRT1_REQ_MD_BW_8, 0x00000000 },
{ DVFSRC_HRT1_REQ_MD_BW_9, 0x00000000 },
{ DVFSRC_HRT1_REQ_MD_BW_10, 0x0006A800 },
{ DVFSRC_95MD_SCEN_BW0_T, 0x40444440 },
{ DVFSRC_95MD_SCEN_BW1_T, 0x43344444 },
{ DVFSRC_95MD_SCEN_BW2_T, 0x04300444 },
{ DVFSRC_95MD_SCEN_BW3_T, 0x60000000 },
{ DVFSRC_95MD_SCEN_BW0, 0x20222220 },
{ DVFSRC_95MD_SCEN_BW1, 0x22222222 },
{ DVFSRC_95MD_SCEN_BW2, 0x02200222 },
{ DVFSRC_95MD_SCEN_BW3, 0x60000000 },
{ DVFSRC_95MD_SCEN_BW4, 0x00000006 },
{ DVFSRC_DDR_REQUEST, 0x00004321 },
{ DVFSRC_DDR_REQUEST3, 0x00000765 },
{ DVFSRC_DDR_ADD_REQUEST, 0x76543210 },
{ DVFSRC_EMI_ADD_REQUEST, 0x66543210 },
{ DVFSRC_HRT_REQUEST, 0x77654321 },
{ DVFSRC_DDR_REQUEST5, 0x54321000 },
{ DVFSRC_DDR_REQUEST7, 0x76000000 },
{ DVFSRC_EMI_MON_DEBOUNCE_TIME, 0x4C2D0000 },
{ DVFSRC_VCORE_REQUEST4, 0x21110000 },
{ DVFSRC_HRT_HIGH_3, 0x3A543A54 },
{ DVFSRC_HRT_HIGH_2, 0x314C2306 },
{ DVFSRC_HRT_HIGH_1, 0x1AD21700 },
{ DVFSRC_HRT_HIGH, 0x0E100960 },
{ DVFSRC_HRT_LOW_3, 0x3A533A53 },
{ DVFSRC_HRT_LOW_2, 0x314B2305 },
{ DVFSRC_HRT_LOW_1, 0x1AD116FF },
{ DVFSRC_HRT_LOW, 0x0E0F095F },
/* { DVFSRC_BASIC_CONTROL_3, 0x0000000E },*/
{ DVFSRC_INT_EN, 0x00000002 },
{ DVFSRC_QOS_EN, 0x0000407C },
{ DVFSRC_CURRENT_FORCE, 0x00000004 },
{ DVFSRC_BASIC_CONTROL, 0x66F8404B },
{ DVFSRC_BASIC_CONTROL, 0x66F8014B },
{ DVFSRC_CURRENT_FORCE, 0x00000000 },
{ -1, 0 },
},
/* NULL */
{
{ -1, 0 },
},
};
static ssize_t dvfsrc_level_mask_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%x\n", helio_dvfsrc_level_mask_get());
}
static ssize_t dvfsrc_level_mask_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int level = 0, en = 0;
if (sscanf(buf, "%d %d", &level, &en) != 2)
return -EINVAL;
helio_dvfsrc_level_mask_set(en, level);
return count;
}
static DEVICE_ATTR(dvfsrc_level_mask, 0644,
dvfsrc_level_mask_show, dvfsrc_level_mask_store);
static ssize_t dvfsrc_vcore_settle_time_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
/* DE's comment: settle time was hard code in fw (15,30) */
return sprintf(buf, "rising 15 uS, falling 30 uS for mt6893\n");
}
static DEVICE_ATTR(dvfsrc_vcore_settle_time, 0444,
dvfsrc_vcore_settle_time_show, NULL);
static struct attribute *mt6893_helio_dvfsrc_attrs[] = {
&dev_attr_dvfsrc_level_mask.attr,
&dev_attr_dvfsrc_vcore_settle_time.attr,
NULL,
};
static struct attribute_group mt6893_helio_dvfsrc_attr_group = {
.name = "helio-dvfsrc",
.attrs = mt6893_helio_dvfsrc_attrs,
};
#define dvfsrc_rmw(offset, val, mask, shift) \
dvfsrc_write(offset, (dvfsrc_read(offset) & ~(mask << shift)) \
| (val << shift))
u32 dvfsrc_get_ddr_qos(void)
{
unsigned int qos_total_bw = dvfsrc_read(DVFSRC_SW_BW_0) +
dvfsrc_read(DVFSRC_SW_BW_1) +
dvfsrc_read(DVFSRC_SW_BW_2) +
dvfsrc_read(DVFSRC_SW_BW_3) +
dvfsrc_read(DVFSRC_SW_BW_4);
if (qos_total_bw < 0x33)
return 0;
else if (qos_total_bw < 0x4C)
return 1;
else if (qos_total_bw < 0x62)
return 2;
else if (qos_total_bw < 0x77)
return 3;
else if (qos_total_bw < 0x99)
return 4;
else if (qos_total_bw < 0xCC)
return 5;
else if (qos_total_bw < 0xEE)
return 6;
else
return 7;
return 0;
}
static int dvfsrc_get_emi_mon_gear(void)
{
unsigned int total_bw_status;
int i;
total_bw_status = vcorefs_get_total_emi_status() & 0x7F;
for (i = 6; i >= 0 ; i--) {
if ((total_bw_status >> i) > 0)
return i + 1;
}
return 0;
}
static u32 dvfsrc_calc_hrt_opp(int data)
{
if (data < 0x0960)
return DDR_OPP_7;
else if (data < 0x0E10)
return DDR_OPP_6;
else if (data < 0x1700)
return DDR_OPP_5;
else if (data < 0x1AD2)
return DDR_OPP_4;
else if (data < 0x2306)
return DDR_OPP_3;
else if (data < 0x314C)
return DDR_OPP_2;
else if (data < 0x3A54)
return DDR_OPP_1;
else
return DDR_OPP_0;
}
void dvfsrc_set_isp_hrt_bw(int data)
{
data = (data + 29) / 30;
if (data > 0x3FF)
data = 0x3FF;
dvfsrc_write(DVFSRC_ISP_HRT, data);
}
u32 dvfsrc_calc_isp_hrt_opp(int data)
{
return dvfsrc_calc_hrt_opp(((data + 29) / 30) * 30);
}
struct regulator *dvfsrc_vcore_requlator(struct device *dev)
{
return regulator_get(dev, "vgpu11");
}
#ifdef AUTOK_ENABLE
__weak int emmc_autok(void)
{
pr_info("NOT SUPPORT EMMC AUTOK\n");
return 0;
}
__weak int sd_autok(void)
{
pr_info("NOT SUPPORT SD AUTOK\n");
return 0;
}
__weak int sdio_autok(void)
{
pr_info("NOT SUPPORT SDIO AUTOK\n");
return 0;
}
void begin_autok_task(void)
{
/* notify MM DVFS for msdc autok start */
mmdvfs_prepare_action(MMDVFS_PREPARE_CALIBRATION_START);
}
void finish_autok_task(void)
{
/* check if dvfs force is released */
int force = mtk_pm_qos_request(MTK_PM_QOS_VCORE_DVFS_FORCE_OPP);
/* notify MM DVFS for msdc autok finish */
mmdvfs_prepare_action(MMDVFS_PREPARE_CALIBRATION_END);
if (force >= 0 && force < VCORE_DVFS_OPP_NUM)
pr_info("autok task not release force opp: %d\n", force);
}
static void dvfsrc_autok_manager(void)
{
int r = 0;
begin_autok_task();
r = emmc_autok();
pr_info("EMMC autok done: %s\n", (r == 0) ? "Yes" : "No");
r = sd_autok();
pr_info("SD autok done: %s\n", (r == 0) ? "Yes" : "No");
r = sdio_autok();
pr_info("SDIO autok done: %s\n", (r == 0) ? "Yes" : "No");
finish_autok_task();
}
#endif
void helio_dvfsrc_platform_pre_init(struct helio_dvfsrc *dvfsrc)
{
struct platform_device *pdev = to_platform_device(dvfsrc->dev);
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
dvfsrc->spm_regs = devm_ioremap(&pdev->dev,
res->start, resource_size(res));
if (IS_ERR(dvfsrc->spm_regs))
pr_info("not get spm register\n");
}
__weak void mtk_pm_qos_trace_dbg_dump(int mtk_pm_qos_class)
{
}
void dvfsrc_suspend_cb(struct helio_dvfsrc *dvfsrc)
{
int sw_req;
sw_req = dvfsrc_read(DVFSRC_SW_REQ3);
pr_info("[DVFSRC] V:%d, F_OPP:%d, RG:%08x, %08x, %08x, %08x\n",
get_cur_vcore_uv(),
mtk_pm_qos_request(MTK_PM_QOS_VCORE_DVFS_FORCE_OPP),
dvfsrc_read(DVFSRC_CURRENT_LEVEL),
dvfsrc_read(DVFSRC_SW_REQ2),
sw_req,
dvfsrc_read(DVFSRC_DEBUG_STA_0));
if (sw_req & (DDR_SW_AP_MASK << DDR_SW_AP_SHIFT))
mtk_pm_qos_trace_dbg_dump(MTK_PM_QOS_DDR_OPP);
if (sw_req & (VCORE_SW_AP_MASK << VCORE_SW_AP_SHIFT))
mtk_pm_qos_trace_dbg_dump(MTK_PM_QOS_VCORE_OPP);
}
void dvfsrc_resume_cb(struct helio_dvfsrc *dvfsrc)
{
}
#define DVFSRC_MD_UP_SHIFT 28
void helio_dvfsrc_platform_init(struct helio_dvfsrc *dvfsrc)
{
int spmfw_idx = 0;
struct reg_config *config;
int idx = 0;
int dvfsrc_rsrv;
sysfs_merge_group(&dvfsrc->dev->kobj, &mt6893_helio_dvfsrc_attr_group);
dvfsrc_rsrv = readl(dvfsrc->regs + DVFSRC_RSRV_4);
if (((dvfsrc_rsrv >> DVFSRC_MD_UP_SHIFT) & 0x1))
dvfsrc_write(DVFSRC_BASIC_CONTROL_3, 0xE);
else
dvfsrc_write(DVFSRC_BASIC_CONTROL_3, 0x6);
config = dvfsrc_init_configs[spmfw_idx];
while (config[idx].offset != -1) {
dvfsrc_write(config[idx].offset, config[idx].val);
idx++;
}
#ifdef AUTOK_ENABLE
dvfsrc_autok_manager();
#endif
}
int vcore_pmic_to_uv(int pmic_val)
{
return __vcore_pmic_to_uv(pmic_val);
}
int vcore_uv_to_pmic(int vcore_uv)
{
return __vcore_uv_to_pmic(vcore_uv);
}
void get_spm_reg(char *p)
{
p += sprintf(p, "%-24s: 0x%08x\n",
"POWERON_CONFIG_EN",
spm_reg_read(POWERON_CONFIG_EN));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_SW_FLAG_0",
spm_reg_read(SPM_SW_FLAG_0));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_PC_STA",
spm_reg_read(SPM_PC_STA));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVFS_LEVEL",
spm_reg_read(SPM_DVFS_LEVEL));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVS_DFS_LEVEL",
spm_reg_read(SPM_DVS_DFS_LEVEL));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVFS_STA",
spm_reg_read(SPM_DVFS_STA));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVFS_MISC",
spm_reg_read(SPM_DVFS_MISC));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x, 0x%08x, 0x%08x 0x%08x\n",
"SPM_DVFS_CMD16~20",
spm_reg_read(SPM_DVFS_CMD16),
spm_reg_read(SPM_DVFS_CMD17),
spm_reg_read(SPM_DVFS_CMD18),
spm_reg_read(SPM_DVFS_CMD19),
spm_reg_read(SPM_DVFS_CMD20));
p += sprintf(p, "%-16s: %x , 0x%08x\n",
"MD_MASK",
dvfsrc_read(DVFSRC_BASIC_CONTROL_3),
dvfsrc_read(DVFSRC_LEVEL_MASK));
}
void get_opp_info(char *p)
{
#if defined(CONFIG_FPGA_EARLY_PORTING) || !defined(CONFIG_MTK_PMIC_COMMON)
int pmic_val = 0;
#else
int pmic_val = pmic_get_register_value(PMIC_VCORE_ADDR);
#endif
#ifdef CONFIG_MEDIATEK_DRAMC
int ddr_khz = mtk_dramc_get_data_rate() * 1000;
#else
int ddr_khz = 0;
#endif
int vcore_uv = vcore_pmic_to_uv(pmic_val);
p += sprintf(p, "%-10s: %-8u uv (PMIC: 0x%x)\n",
"Vcore", vcore_uv, vcore_uv_to_pmic(vcore_uv));
p += sprintf(p, "%-10s: %-8u khz\n", "DDR", ddr_khz);
p += sprintf(p, "%6s: 0x%08x\n", "INFO1", get_devinfo_with_index(134));
p += sprintf(p, "%6s: 0x%08x\n", "INFO2", get_devinfo_with_index(140));
p += sprintf(p, "%6s: 0x%08x\n", "INFO3", get_devinfo_with_index(69));
p += sprintf(p, "%6s: 0x%08x\n", "V_MODE", dvfsrc_read(DVFSRC_RSRV_4));
}
/* met profile table */
static unsigned int met_vcorefs_src[SRC_MAX];
static char *met_src_name[SRC_MAX] = {
"MD2SPM",
"SRC_DDR_OPP",
"DDR__SW_REQ1_SPM",
"DDR__SW_REQ2_CM",
"DDR__SW_REQ3_PMQOS",
"DDR__QOS_BW",
"DDR__EMI_TOTAL",
"DDR__HRT_BW",
"DDR__HIFI",
"DDR__HIFI_LATENCY",
"DDR__MD_LATENCY",
"DDR__MD_DDR",
"DDR__MD_LEVEL_MASK",
"SRC_VCORE_OPP",
"VCORE__SW_REQ3_PMQOS",
"VCORE__SCP",
"VCORE__HIFI",
"SCP_REQ",
"PMQOS_TATOL",
"PMQOS_BW0",
"PMQOS_BW1",
"PMQOS_BW2",
"PMQOS_BW3",
"PMQOS_BW4",
"TOTAL_EMI_BW",
"HRT_MD_BW",
"HRT_DISP_BW",
"HRT_ISP_BW",
"MD_SCENARIO",
"HIFI_SCENARIO_IDX",
"MD_EMI_LATENCY",
};
/* met profile function */
int vcorefs_get_src_req_num(void)
{
return SRC_MAX;
}
EXPORT_SYMBOL(vcorefs_get_src_req_num);
char **vcorefs_get_src_req_name(void)
{
return met_src_name;
}
EXPORT_SYMBOL(vcorefs_get_src_req_name);
static u32 vcorefs_get_md_level_mask_ddr(void)
{
int md_srclk, vopp;
if (dvfsrc_read(DVFSRC_BASIC_CONTROL_3) & 0x8) {
md_srclk = dvfsrc_read(DVFSRC_DEBUG_STA_0);
vopp = get_cur_vcore_opp();
md_srclk = (md_srclk >> MD_SRC_CLK_DEBUG_SHIFT)
& MD_SRC_CLK_DEBUG_MASK;
if (vopp != 4 && md_srclk == 1)
return 2;
}
return 0;
}
static void vcorefs_get_src_ddr_req(void)
{
unsigned int sw_req;
met_vcorefs_src[DDR_OPP_IDX] =
get_cur_ddr_opp();
sw_req = dvfsrc_read(DVFSRC_SW_REQ1);
met_vcorefs_src[DDR_SW_REQ1_SPM_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
sw_req = dvfsrc_read(DVFSRC_SW_REQ2);
met_vcorefs_src[DDR_SW_REQ2_CM_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
sw_req = dvfsrc_read(DVFSRC_SW_REQ3);
met_vcorefs_src[DDR_SW_REQ3_PMQOS_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
met_vcorefs_src[DDR_QOS_BW_IDX] =
dvfsrc_get_ddr_qos();
met_vcorefs_src[DDR_EMI_TOTAL_IDX] =
dvfsrc_get_emi_mon_gear();
met_vcorefs_src[DDR_HRT_BW_IDX] =
vcorefs_get_hrt_bw_ddr();
met_vcorefs_src[DDR_HIFI_IDX] =
vcorefs_get_hifi_ddr_status();
met_vcorefs_src[DDR_HIFI_LATENCY_IDX] =
vcorefs_get_hifi_rising_ddr();
met_vcorefs_src[DDR_MD_LATENCY_IDX] =
vcorefs_get_md_imp_ddr();
met_vcorefs_src[DDR_MD_DDR_IDX] =
vcorefs_get_md_scenario_ddr();
met_vcorefs_src[DDR_MD_SRCLK_IDX] =
vcorefs_get_md_level_mask_ddr();
}
static void vcorefs_get_src_vcore_req(void)
{
u32 sw_req;
u32 scp_en;
scp_en = vcorefs_get_scp_req_status();
met_vcorefs_src[VCORE_OPP_IDX] =
get_cur_vcore_opp();
sw_req = dvfsrc_read(DVFSRC_SW_REQ3);
met_vcorefs_src[VCORE_SW_REQ3_PMQOS_IDX] =
(sw_req >> VCORE_SW_AP_SHIFT) & VCORE_SW_AP_MASK;
if (scp_en) {
sw_req = dvfsrc_read(DVFSRC_VCORE_REQUEST);
met_vcorefs_src[VCORE_SCP_IDX] =
(sw_req >> VCORE_SCP_GEAR_SHIFT) & VCORE_SCP_GEAR_MASK;
} else
met_vcorefs_src[VCORE_SCP_IDX] = 0;
met_vcorefs_src[VCORE_HIFI_IDX] =
vcorefs_get_hifi_vcore_status();
}
static void vcorefs_get_src_misc_info(void)
{
u32 qos_bw0, qos_bw1, qos_bw2, qos_bw3, qos_bw4;
qos_bw0 = dvfsrc_read(DVFSRC_SW_BW_0);
qos_bw1 = dvfsrc_read(DVFSRC_SW_BW_1);
qos_bw2 = dvfsrc_read(DVFSRC_SW_BW_2);
qos_bw3 = dvfsrc_read(DVFSRC_SW_BW_3);
qos_bw4 = dvfsrc_read(DVFSRC_SW_BW_4);
met_vcorefs_src[SRC_MD2SPM_IDX] =
vcorefs_get_md_scenario() & 0x1FFFF;
met_vcorefs_src[SRC_SCP_REQ_IDX] =
vcorefs_get_scp_req_status();
met_vcorefs_src[SRC_PMQOS_TATOL_IDX] =
qos_bw0 + qos_bw1 + qos_bw2 + qos_bw3 + qos_bw4;
met_vcorefs_src[SRC_PMQOS_BW0_IDX] =
qos_bw0;
met_vcorefs_src[SRC_PMQOS_BW1_IDX] =
qos_bw1;
met_vcorefs_src[SRC_PMQOS_BW2_IDX] =
qos_bw2;
met_vcorefs_src[SRC_PMQOS_BW3_IDX] =
qos_bw3;
met_vcorefs_src[SRC_PMQOS_BW4_IDX] =
qos_bw4;
met_vcorefs_src[SRC_HRT_MD_BW_IDX] =
dvfsrc_get_md_bw();
met_vcorefs_src[SRC_HRT_ISP_BW_IDX] =
dvfsrc_read(DVFSRC_ISP_HRT);
met_vcorefs_src[SRC_MD_SCENARIO_IDX] =
vcorefs_get_md_scenario();
met_vcorefs_src[SRC_HIFI_SCENARIO_IDX] =
vcorefs_get_hifi_scenario();
met_vcorefs_src[SRC_MD_EMI_LATENCY_IDX] =
vcorefs_get_md_emi_latency_status();
}
int dvfsrc_latch_register(int enable)
{
#ifdef CONFIG_MTK_DBGTOP
return mtk_dbgtop_cfg_dvfsrc(1);
#else
return 0;
#endif
}
unsigned int *vcorefs_get_src_req(void)
{
vcorefs_get_src_ddr_req();
vcorefs_get_src_vcore_req();
vcorefs_get_src_misc_info();
vcorefs_trace_qos();
return met_vcorefs_src;
}
EXPORT_SYMBOL(vcorefs_get_src_req);
int get_cur_ddr_ratio(void)
{
int idx;
if (!is_dvfsrc_enabled())
return 0;
idx = get_cur_vcore_dvfs_opp();
if (idx >= VCORE_DVFS_OPP_NUM)
return 0;
if (get_ddr_opp(idx) < DDR_OPP_7)
return 8;
else
return 4;
}
EXPORT_SYMBOL(get_cur_ddr_ratio);
@@ -0,0 +1,58 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_MT6893_H
#define __HELIO_DVFSRC_MT6893_H
#include <mach/upmu_hw.h>
#define PMIC_VCORE_ADDR PMIC_RG_BUCK_VGPU11_VOSEL
#define VCORE_BASE_UV 400000
#define VCORE_STEP_UV 6250
/* PMIC */
#define __vcore_pmic_to_uv(pmic) \
(((pmic) * VCORE_STEP_UV) + VCORE_BASE_UV)
#define __vcore_uv_to_pmic(uv) /* pmic >= uv */ \
((((uv) - VCORE_BASE_UV) + (VCORE_STEP_UV - 1)) / VCORE_STEP_UV)
enum met_src_index {
SRC_MD2SPM_IDX = 0,
DDR_OPP_IDX,
DDR_SW_REQ1_SPM_IDX,
DDR_SW_REQ2_CM_IDX,
DDR_SW_REQ3_PMQOS_IDX,
DDR_QOS_BW_IDX,
DDR_EMI_TOTAL_IDX,
DDR_HRT_BW_IDX,
DDR_HIFI_IDX,
DDR_HIFI_LATENCY_IDX,
DDR_MD_LATENCY_IDX,
DDR_MD_DDR_IDX,
DDR_MD_SRCLK_IDX,
VCORE_OPP_IDX,
VCORE_SW_REQ3_PMQOS_IDX,
VCORE_SCP_IDX,
VCORE_HIFI_IDX,
SRC_SCP_REQ_IDX,
SRC_PMQOS_TATOL_IDX,
SRC_PMQOS_BW0_IDX,
SRC_PMQOS_BW1_IDX,
SRC_PMQOS_BW2_IDX,
SRC_PMQOS_BW3_IDX,
SRC_PMQOS_BW4_IDX,
SRC_TOTAL_EMI_BW_IDX,
SRC_HRT_MD_BW_IDX,
SRC_HRT_DISP_BW_IDX,
SRC_HRT_ISP_BW_IDX,
SRC_MD_SCENARIO_IDX,
SRC_HIFI_SCENARIO_IDX,
SRC_MD_EMI_LATENCY_IDX,
SRC_MAX
};
#endif /* __HELIO_DVFSRC_MT6893_H */
@@ -0,0 +1,332 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <mt-plat/mtk_devinfo.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <helio-dvfsrc-opp.h>
#include <helio-dvfsrc-qos.h>
#ifdef CONFIG_MTK_DRAMC
#include <mtk_dramc.h>
#endif
#define SOC_CPE_DETECT
#ifndef CONFIG_MTK_DRAMC
static int dram_steps_freq(unsigned int step)
{
pr_info("get dram steps_freq fail\n");
return 4266;
}
#endif
static int get_opp_min_ct(void)
{
int opp_min_ct = 3;
#if defined(CONFIG_ARM64) && \
defined(CONFIG_BUILD_ARM64_DTB_OVERLAY_IMAGE_NAMES)
if ((strstr(CONFIG_BUILD_ARM64_DTB_OVERLAY_IMAGE_NAMES,
"k85v1_64_hqact") != NULL) ||
(strstr(CONFIG_BUILD_ARM64_DTB_OVERLAY_IMAGE_NAMES,
"evb6785_64_hqact") != NULL) ||
(strstr(CONFIG_BUILD_ARM64_DTB_OVERLAY_IMAGE_NAMES,
"k85v1_64_ctighten") != NULL)) {
opp_min_ct = 2;
}
#endif
return opp_min_ct;
}
static int get_vb_volt(int vcore_opp)
{
int ret = 0;
int idx, idx2;
int opp_min_ct = get_opp_min_ct();
int ptpod = get_devinfo_with_index(64);
pr_info("%s: PTPOD: 0x%x\n", __func__, ptpod);
switch (vcore_opp) {
case VCORE_OPP_0:
idx = (ptpod >> 6) & 0x7;
if (idx >= opp_min_ct)
ret = 1;
break;
case VCORE_OPP_1:
idx = (ptpod >> 6) & 0x7;
idx2 = (ptpod >> 3) & 0x7;
if ((idx >= opp_min_ct) && (idx2 >= opp_min_ct))
ret = 1;
break;
case VCORE_OPP_2:
idx = ptpod & 0x7;
if (idx >= opp_min_ct)
ret = 1;
break;
default:
break;
}
return ret * 25000;
}
int ddr_level_to_step(int opp)
{
unsigned int step[] = {0, 1, 3, 5, 7, 9};
return step[opp];
}
void dvfsrc_opp_level_mapping(void)
{
set_vcore_opp(VCORE_DVFS_OPP_0, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_1, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_2, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_3, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_4, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_5, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_6, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_7, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_8, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_9, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_10, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_11, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_12, VCORE_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_0, DDR_OPP_0);
set_ddr_opp(VCORE_DVFS_OPP_1, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_2, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_3, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_4, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_5, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_6, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_7, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_8, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_9, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_10, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_11, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_12, DDR_OPP_5);
}
void dvfsrc_opp_table_init(void)
{
int i;
int vcore_opp, ddr_opp;
for (i = 0; i < VCORE_DVFS_OPP_NUM; i++) {
vcore_opp = get_vcore_opp(i);
ddr_opp = get_ddr_opp(i);
if (vcore_opp == VCORE_OPP_UNREQ || ddr_opp == DDR_OPP_UNREQ) {
set_opp_table(i, 0, 0);
continue;
}
set_opp_table(i, get_vcore_uv_table(vcore_opp),
dram_steps_freq(ddr_level_to_step(ddr_opp)) * 1000);
}
}
static int is_ct_support(void)
{
int ret = 0;
#if defined(CONFIG_ARM64) && \
defined(CONFIG_BUILD_ARM64_DTB_OVERLAY_IMAGE_NAMES)
pr_info("[VcoreFS] flavor name: %s\n",
CONFIG_BUILD_ARM64_DTB_OVERLAY_IMAGE_NAMES);
if ((strstr(CONFIG_BUILD_ARM64_DTB_OVERLAY_IMAGE_NAMES,
"k85v1_64_hqact") != NULL) ||
(strstr(CONFIG_BUILD_ARM64_DTB_OVERLAY_IMAGE_NAMES,
"evb6785_64_hqact") != NULL) ||
(strstr(CONFIG_BUILD_ARM64_DTB_OVERLAY_IMAGE_NAMES,
"k85v1_64_ctighten") != NULL)) {
pr_info("[VcoreFS]: CT flavor !!!\n");
return 1;
}
#endif
return ret;
}
static int is_aging_test(void)
{
int ret = 0;
#if defined(CONFIG_ARM64) && \
defined(CONFIG_BUILD_ARM64_DTB_OVERLAY_IMAGE_NAMES)
pr_info("[VcoreFS] flavor name: %s\n",
CONFIG_BUILD_ARM64_DTB_OVERLAY_IMAGE_NAMES);
if ((strstr(CONFIG_BUILD_ARM64_DTB_OVERLAY_IMAGE_NAMES,
"k85v1_64_aging") != NULL)) {
pr_info("[VcoreFS]: AGING flavor !!!\n");
return 1;
}
#endif
return ret;
}
static int is_qea_test(void)
{
int ret = 0;
return ret;
}
static int __init dvfsrc_opp_init(void)
{
struct device_node *dvfsrc_node = NULL;
int vcore_opp_0_uv, vcore_opp_1_uv, vcore_opp_2_uv;
int i;
int is_vcore_qea = is_qea_test();
int is_vcore_ct = is_ct_support();
int is_vcore_aging = is_aging_test();
#ifdef SOC_CPE_DETECT
void __iomem *dvfsrc_base;
int vcore_arg = 0;
#endif
int doe_ct = 0;
int doe_vcore_mode = 0;
int dvfs_v_mode = 0;
set_pwrap_cmd(VCORE_OPP_0, 3);
set_pwrap_cmd(VCORE_OPP_1, 1);
set_pwrap_cmd(VCORE_OPP_2, 0);
vcore_opp_0_uv = 825000;
vcore_opp_1_uv = 725000;
vcore_opp_2_uv = 650000;
dvfsrc_node =
of_find_compatible_node(NULL, NULL, "mediatek,dvfsrc");
/* For Doe */
if (dvfsrc_node) {
if (of_property_read_u32(dvfsrc_node, "vcore_ct",
(u32 *) &doe_ct) == 0) {
is_vcore_ct = doe_ct;
pr_info("%s: DOE CT = %d\n", __func__, doe_ct);
}
if (of_property_read_u32(dvfsrc_node, "dvfs_v_mode",
(u32 *) &dvfs_v_mode) == 0)
pr_info("%s: DOE DVFS_V_MODE = %d\n",
__func__, dvfs_v_mode);
if (of_property_read_u32(dvfsrc_node, "doe_vcore_mode",
(u32 *) &doe_vcore_mode) == 0) {
pr_info("%s: DOE DRAM_VCORE_MODE = %d\n",
__func__, doe_vcore_mode);
if (doe_vcore_mode == 1) { /*Doe HV */
vcore_opp_0_uv = 868750;
vcore_opp_1_uv = 762500;
vcore_opp_2_uv = 687500;
} else if (doe_vcore_mode == 3) { /*Doe LV */
vcore_opp_0_uv = 781250;
vcore_opp_1_uv = 687500;
vcore_opp_2_uv = 612500;
} else {
doe_vcore_mode = 0;
}
}
}
if (doe_vcore_mode == 0) {
if (is_vcore_ct) {
#ifdef SOC_CPE_DETECT
if (dvfsrc_node) {
dvfsrc_base = of_iomap(dvfsrc_node, 0);
if (dvfsrc_base) {
vcore_arg = readl(dvfsrc_base + 0x94);
iounmap(dvfsrc_base);
}
pr_info("%s: vcore_cpe = %08x\n",
__func__, vcore_arg);
}
if (vcore_arg & 0x8) {
if (vcore_arg & 0x4)
vcore_opp_0_uv -=
get_vb_volt(VCORE_OPP_0);
if ((vcore_arg & 0x4) && (vcore_arg & 0x2))
vcore_opp_1_uv -=
get_vb_volt(VCORE_OPP_1);
} else {
pr_info("%s: CPE not support\n", __func__);
vcore_opp_0_uv -= get_vb_volt(VCORE_OPP_0);
vcore_opp_1_uv -= get_vb_volt(VCORE_OPP_1);
}
#else
vcore_opp_0_uv -= get_vb_volt(VCORE_OPP_0);
vcore_opp_1_uv -= get_vb_volt(VCORE_OPP_1);
#endif
}
if (is_vcore_qea || (dvfs_v_mode == 3)) { /* LV */
if (vcore_opp_0_uv == 800000)
vcore_opp_0_uv = 756250;
else
vcore_opp_0_uv = 781250;
if (vcore_opp_1_uv == 700000)
vcore_opp_1_uv = 662500;
else
vcore_opp_1_uv = 687500;
vcore_opp_2_uv = 612500;
} else if (dvfs_v_mode == 1) { /* HV */
if (vcore_opp_0_uv == 800000)
vcore_opp_0_uv = 843750;
else
vcore_opp_0_uv = 868750;
if (vcore_opp_1_uv == 700000)
vcore_opp_1_uv = 737500;
else
vcore_opp_1_uv = 762500;
vcore_opp_2_uv = 687500;
} else if (is_vcore_aging) {
vcore_opp_0_uv = vcore_opp_0_uv - 12500;
vcore_opp_1_uv = vcore_opp_1_uv - 12500;
vcore_opp_2_uv = vcore_opp_2_uv - 12500;
}
}
pr_info("%s: CT=%d, AGING=%d, QEA=%d, VMODE=%d\n",
__func__,
is_vcore_ct,
is_vcore_aging,
is_vcore_qea,
dvfs_v_mode);
pr_info("%s: FINAL vcore_opp_uv: %d, %d, %d\n",
__func__,
vcore_opp_0_uv,
vcore_opp_1_uv,
vcore_opp_2_uv);
set_vcore_uv_table(VCORE_OPP_0, vcore_opp_0_uv);
set_vcore_uv_table(VCORE_OPP_1, vcore_opp_1_uv);
set_vcore_uv_table(VCORE_OPP_2, vcore_opp_2_uv);
/* meta vcore opp*/
spm_dvfs_pwrap_cmd(2,
vcore_uv_to_pmic((vcore_opp_0_uv + vcore_opp_1_uv) >> 1));
for (i = 0; i < DDR_OPP_NUM; i++) {
set_opp_ddr_freq(i,
dram_steps_freq(ddr_level_to_step(i)) * 1000);
}
return 0;
}
fs_initcall_sync(dvfsrc_opp_init)
@@ -0,0 +1,305 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <mt-plat/mtk_devinfo.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <helio-dvfsrc-opp.h>
#include <helio-dvfsrc-qos.h>
#include <helio-dvfsrc-mt6833.h>
#ifdef CONFIG_MEDIATEK_DRAMC
#include <dramc.h>
#endif
#define SOC_CPE_DETECT
#define AGING_VALUE 12500
#define V_VMODE_SHIFT 0
#define V_AGING_SHIFT 4
#define V_CT_SHIFT 5
#define V_CT_TEST_SHIFT 6
#define V_CT_OPP3_SHIFT 7
static int opp_min_bin_opp0;
static int opp_min_bin_opp3;
static int is_vcore_ct;
static int dvfsrc_rsrv;
#ifndef CONFIG_MEDIATEK_DRAMC
static int mtk_dramc_get_steps_freq(unsigned int step)
{
pr_info("get dram steps_freq fail\n");
return 4266;
}
#endif
u32 dvfsrc_ct_mode(void)
{
return is_vcore_ct;
}
u32 dvfsrc_vcore_mode(void)
{
return dvfsrc_rsrv;
}
void dvfsrc_opp_level_mapping(void)
{
set_vcore_opp(VCORE_DVFS_OPP_0, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_1, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_2, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_3, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_4, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_5, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_6, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_7, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_8, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_9, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_10, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_11, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_12, VCORE_OPP_3);
set_vcore_opp(VCORE_DVFS_OPP_13, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_14, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_15, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_16, VCORE_OPP_3);
set_vcore_opp(VCORE_DVFS_OPP_17, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_18, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_19, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_20, VCORE_OPP_3);
set_vcore_opp(VCORE_DVFS_OPP_21, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_22, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_23, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_24, VCORE_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_0, DDR_OPP_0);
set_ddr_opp(VCORE_DVFS_OPP_1, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_2, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_3, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_4, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_5, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_6, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_7, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_8, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_9, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_10, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_11, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_12, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_13, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_14, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_15, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_16, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_17, DDR_OPP_6);
set_ddr_opp(VCORE_DVFS_OPP_18, DDR_OPP_6);
set_ddr_opp(VCORE_DVFS_OPP_19, DDR_OPP_6);
set_ddr_opp(VCORE_DVFS_OPP_20, DDR_OPP_6);
set_ddr_opp(VCORE_DVFS_OPP_21, DDR_OPP_7);
set_ddr_opp(VCORE_DVFS_OPP_22, DDR_OPP_7);
set_ddr_opp(VCORE_DVFS_OPP_23, DDR_OPP_7);
set_ddr_opp(VCORE_DVFS_OPP_24, DDR_OPP_7);
}
void dvfsrc_opp_table_init(void)
{
int i;
int vcore_opp, ddr_opp;
for (i = 0; i < VCORE_DVFS_OPP_NUM; i++) {
vcore_opp = get_vcore_opp(i);
ddr_opp = get_ddr_opp(i);
if (vcore_opp == VCORE_OPP_UNREQ || ddr_opp == DDR_OPP_UNREQ) {
set_opp_table(i, 0, 0);
continue;
}
set_opp_table(i, get_vcore_uv_table(vcore_opp),
mtk_dramc_get_steps_freq(ddr_opp) * 1000);
}
}
static int get_vb_volt(int vcore_opp)
{
int idx;
int ret = 0;
int ptpod = get_devinfo_with_index(209);
pr_info("%s: PTPOD: 0x%x\n", __func__, ptpod);
switch (vcore_opp) {
case VCORE_OPP_0:
idx = (ptpod >> 4) & 0x7;
if (idx >= opp_min_bin_opp0)
ret = 1;
break;
case VCORE_OPP_3:
idx = ptpod & 0xF;
if (idx >= opp_min_bin_opp3 && idx < 10)
ret = 1;
break;
default:
break;
}
return ret * 25000;
}
static int is_rising_need(void)
{
int idx;
int ptpod = get_devinfo_with_index(209);
pr_info("%s: PTPOD: 0x%x\n", __func__, ptpod);
idx = ptpod & 0xF;
if (idx == 1 || idx == 2)
return idx;
return 0;
}
static int __init dvfsrc_opp_init(void)
{
struct device_node *dvfsrc_node = NULL;
int vcore_opp_0_uv, vcore_opp_1_uv, vcore_opp_2_uv, vcore_opp_3_uv;
int ct_test = 0;
int is_vcore_aging = 0;
int ct_opp3_en = 0;
int dvfs_v_mode = 0;
void __iomem *dvfsrc_base;
set_pwrap_cmd(VCORE_OPP_0, 0);
set_pwrap_cmd(VCORE_OPP_1, 1);
set_pwrap_cmd(VCORE_OPP_2, 2);
set_pwrap_cmd(VCORE_OPP_3, 3);
vcore_opp_0_uv = 725000;
vcore_opp_1_uv = 650000;
vcore_opp_2_uv = 600000;
vcore_opp_3_uv = 550000;
dvfsrc_node =
of_find_compatible_node(NULL, NULL, "mediatek,dvfsrc");
/* For Doe */
if (dvfsrc_node) {
dvfsrc_base = of_iomap(dvfsrc_node, 0);
if (dvfsrc_base) {
dvfsrc_rsrv = readl(dvfsrc_base + 0x610);
iounmap(dvfsrc_base);
}
pr_info("%s: vcore_arg = %08x\n",
__func__, dvfsrc_rsrv);
dvfs_v_mode = (dvfsrc_rsrv >> V_VMODE_SHIFT) & 0x3;
is_vcore_ct = (dvfsrc_rsrv >> V_CT_SHIFT) & 0x1;
ct_test = (dvfsrc_rsrv >> V_CT_TEST_SHIFT) & 0x1;
is_vcore_aging = (dvfsrc_rsrv >> V_AGING_SHIFT) & 0x1;
ct_opp3_en = (dvfsrc_rsrv >> V_CT_OPP3_SHIFT) & 0x1;
}
if (is_vcore_ct) {
if (ct_test) {
opp_min_bin_opp0 = 2;
opp_min_bin_opp3 = 4;
} else {
opp_min_bin_opp0 = 3;
opp_min_bin_opp3 = 5;
}
vcore_opp_0_uv -= get_vb_volt(VCORE_OPP_0);
if (ct_opp3_en)
vcore_opp_3_uv -= get_vb_volt(VCORE_OPP_3);
}
if (is_rising_need() == 2)
vcore_opp_3_uv = 575000;
else if (is_rising_need() == 1)
vcore_opp_3_uv = 600000;
if (dvfs_v_mode == 3) {
/* LV */
if (vcore_opp_0_uv == 700000)
vcore_opp_0_uv = 662500;
else
vcore_opp_0_uv = 687500;
vcore_opp_1_uv = 612500;
vcore_opp_2_uv = 568750;
if (vcore_opp_3_uv == 525000)
vcore_opp_3_uv = 493750;
else if (vcore_opp_3_uv == 575000)
vcore_opp_3_uv = 543750;
else if (vcore_opp_3_uv == 600000)
vcore_opp_3_uv = 568750;
else
vcore_opp_3_uv = 518750;
} else if (dvfs_v_mode == 1) {
/* HV */
if (vcore_opp_0_uv == 700000)
vcore_opp_0_uv = 737500;
else
vcore_opp_0_uv = 762500;
vcore_opp_1_uv = 687500;
vcore_opp_2_uv = 631250;
if (vcore_opp_3_uv == 525000)
vcore_opp_3_uv = 556250;
else if (vcore_opp_3_uv == 575000)
vcore_opp_3_uv = 606250;
else if (vcore_opp_3_uv == 600000)
vcore_opp_3_uv = 631250;
else
vcore_opp_3_uv = 581250;
} else if (is_vcore_aging) {
vcore_opp_0_uv -= AGING_VALUE;
vcore_opp_1_uv -= AGING_VALUE;
vcore_opp_2_uv -= AGING_VALUE;
vcore_opp_3_uv -= AGING_VALUE;
}
if (vcore_opp_3_uv < VCORE_BASE_UV)
vcore_opp_3_uv = VCORE_BASE_UV;
pr_info("%s: CT=%d, VMODE=%d, RSV4=%x\n",
__func__,
is_vcore_ct,
dvfs_v_mode,
dvfsrc_rsrv);
pr_info("%s: FINAL vcore_opp_uv: %d, %d, %d, %d\n",
__func__,
vcore_opp_0_uv,
vcore_opp_1_uv,
vcore_opp_2_uv,
vcore_opp_3_uv);
set_vcore_uv_table(VCORE_OPP_0, vcore_opp_0_uv);
set_vcore_uv_table(VCORE_OPP_1, vcore_opp_1_uv);
set_vcore_uv_table(VCORE_OPP_2, vcore_opp_2_uv);
set_vcore_uv_table(VCORE_OPP_3, vcore_opp_3_uv);
return 0;
}
fs_initcall_sync(dvfsrc_opp_init)
static int __init dvfsrc_dram_opp_init(void)
{
int i;
for (i = 0; i < DDR_OPP_NUM; i++) {
set_opp_ddr_freq(i,
mtk_dramc_get_steps_freq(i) * 1000);
}
return 0;
}
device_initcall_sync(dvfsrc_dram_opp_init)
@@ -0,0 +1,304 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <mt-plat/mtk_devinfo.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <helio-dvfsrc-opp.h>
#include <helio-dvfsrc-qos.h>
#include <helio-dvfsrc-mt6853.h>
#ifdef CONFIG_MEDIATEK_DRAMC
#include <dramc.h>
#endif
#define SOC_CPE_DETECT
#define AGING_VALUE 12500
#define V_VMODE_SHIFT 0
#define V_AGING_SHIFT 4
#define V_CT_SHIFT 5
#define V_CT_TEST_SHIFT 6
#define V_CT_OPP3_SHIFT 7
static int opp_min_bin_opp0;
static int opp_min_bin_opp3;
static int opp_min_bin_opp3_1;
static int is_vcore_ct;
static int dvfsrc_rsrv;
#ifndef CONFIG_MEDIATEK_DRAMC
static int mtk_dramc_get_steps_freq(unsigned int step)
{
pr_info("get dram steps_freq fail\n");
return 4266;
}
#endif
u32 dvfsrc_ct_mode(void)
{
return is_vcore_ct;
}
u32 dvfsrc_vcore_mode(void)
{
return dvfsrc_rsrv;
}
void dvfsrc_opp_level_mapping(void)
{
set_vcore_opp(VCORE_DVFS_OPP_0, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_1, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_2, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_3, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_4, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_5, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_6, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_7, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_8, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_9, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_10, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_11, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_12, VCORE_OPP_3);
set_vcore_opp(VCORE_DVFS_OPP_13, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_14, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_15, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_16, VCORE_OPP_3);
set_vcore_opp(VCORE_DVFS_OPP_17, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_18, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_19, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_20, VCORE_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_0, DDR_OPP_0);
set_ddr_opp(VCORE_DVFS_OPP_1, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_2, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_3, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_4, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_5, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_6, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_7, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_8, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_9, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_10, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_11, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_12, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_13, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_14, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_15, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_16, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_17, DDR_OPP_6);
set_ddr_opp(VCORE_DVFS_OPP_18, DDR_OPP_6);
set_ddr_opp(VCORE_DVFS_OPP_19, DDR_OPP_6);
set_ddr_opp(VCORE_DVFS_OPP_20, DDR_OPP_6);
}
void dvfsrc_opp_table_init(void)
{
int i;
int vcore_opp, ddr_opp;
for (i = 0; i < VCORE_DVFS_OPP_NUM; i++) {
vcore_opp = get_vcore_opp(i);
ddr_opp = get_ddr_opp(i);
if (vcore_opp == VCORE_OPP_UNREQ || ddr_opp == DDR_OPP_UNREQ) {
set_opp_table(i, 0, 0);
continue;
}
set_opp_table(i, get_vcore_uv_table(vcore_opp),
mtk_dramc_get_steps_freq(ddr_opp) * 1000);
}
}
static int get_vb_volt(int vcore_opp)
{
int idx;
int ret = 0;
int ptpod = get_devinfo_with_index(209);
pr_info("%s: PTPOD: 0x%x\n", __func__, ptpod);
switch (vcore_opp) {
case VCORE_OPP_0:
idx = (ptpod >> 4) & 0x7;
if (idx >= opp_min_bin_opp0)
ret = 1;
break;
case VCORE_OPP_3:
idx = ptpod & 0xF;
if (idx >= opp_min_bin_opp3 && idx < opp_min_bin_opp3_1)
ret = 1;
else if (idx >= opp_min_bin_opp3_1 && idx < 9)
ret = 2;
break;
default:
break;
}
return ret * 25000;
}
static int is_rising_need(void)
{
int idx;
int ptpod = get_devinfo_with_index(209);
pr_info("%s: PTPOD: 0x%x\n", __func__, ptpod);
idx = ptpod & 0xF;
if (idx == 1 || idx == 9)
return idx;
return 0;
}
static int __init dvfsrc_opp_init(void)
{
struct device_node *dvfsrc_node = NULL;
int vcore_opp_0_uv, vcore_opp_1_uv, vcore_opp_2_uv, vcore_opp_3_uv;
int ct_test = 0;
int is_vcore_aging = 0;
int ct_opp3_en = 0;
int dvfs_v_mode = 0;
void __iomem *dvfsrc_base;
set_pwrap_cmd(VCORE_OPP_0, 0);
set_pwrap_cmd(VCORE_OPP_1, 1);
set_pwrap_cmd(VCORE_OPP_2, 2);
set_pwrap_cmd(VCORE_OPP_3, 3);
vcore_opp_0_uv = 725000;
vcore_opp_1_uv = 650000;
vcore_opp_2_uv = 600000;
vcore_opp_3_uv = 550000;
dvfsrc_node =
of_find_compatible_node(NULL, NULL, "mediatek,dvfsrc");
/* For Doe */
if (dvfsrc_node) {
dvfsrc_base = of_iomap(dvfsrc_node, 0);
if (dvfsrc_base) {
dvfsrc_rsrv = readl(dvfsrc_base + 0x610);
iounmap(dvfsrc_base);
}
pr_info("%s: vcore_arg = %08x\n",
__func__, dvfsrc_rsrv);
dvfs_v_mode = (dvfsrc_rsrv >> V_VMODE_SHIFT) & 0x3;
is_vcore_ct = (dvfsrc_rsrv >> V_CT_SHIFT) & 0x1;
ct_test = (dvfsrc_rsrv >> V_CT_TEST_SHIFT) & 0x1;
is_vcore_aging = (dvfsrc_rsrv >> V_AGING_SHIFT) & 0x1;
ct_opp3_en = (dvfsrc_rsrv >> V_CT_OPP3_SHIFT) & 0x1;
}
if (is_vcore_ct) {
if (ct_test) {
opp_min_bin_opp0 = 2;
opp_min_bin_opp3 = 3;
opp_min_bin_opp3_1 = 6;
} else {
opp_min_bin_opp0 = 3;
opp_min_bin_opp3 = 4;
opp_min_bin_opp3_1 = 7;
}
vcore_opp_0_uv -= get_vb_volt(VCORE_OPP_0);
if (ct_opp3_en)
vcore_opp_3_uv -= get_vb_volt(VCORE_OPP_3);
}
if (is_rising_need() == 1)
vcore_opp_3_uv = 575000;
else if (is_rising_need() == 9)
vcore_opp_3_uv = 600000;
if (dvfs_v_mode == 3) {
/* LV */
if (vcore_opp_0_uv == 700000)
vcore_opp_0_uv = 662500;
else
vcore_opp_0_uv = 687500;
vcore_opp_1_uv = 612500;
vcore_opp_2_uv = 568750;
if (vcore_opp_3_uv == 525000)
vcore_opp_3_uv = 493750;
else if (vcore_opp_3_uv == 575000)
vcore_opp_3_uv = 543750;
else if (vcore_opp_3_uv == 600000)
vcore_opp_3_uv = 568750;
else if (vcore_opp_3_uv == 500000)
vcore_opp_3_uv = 475000;
else
vcore_opp_3_uv = 518750;
} else if (dvfs_v_mode == 1) {
/* HV */
if (vcore_opp_0_uv == 700000)
vcore_opp_0_uv = 737500;
else
vcore_opp_0_uv = 762500;
vcore_opp_1_uv = 687500;
vcore_opp_2_uv = 631250;
if (vcore_opp_3_uv == 525000)
vcore_opp_3_uv = 556250;
else if (vcore_opp_3_uv == 575000)
vcore_opp_3_uv = 606250;
else if (vcore_opp_3_uv == 600000)
vcore_opp_3_uv = 631250;
else if (vcore_opp_3_uv == 500000)
vcore_opp_3_uv = 525000;
else
vcore_opp_3_uv = 581250;
} else if (is_vcore_aging) {
vcore_opp_0_uv -= AGING_VALUE;
vcore_opp_1_uv -= AGING_VALUE;
vcore_opp_2_uv -= AGING_VALUE;
vcore_opp_3_uv -= AGING_VALUE;
}
if (vcore_opp_3_uv < VCORE_BASE_UV)
vcore_opp_3_uv = VCORE_BASE_UV;
pr_info("%s: CT=%d, VMODE=%d, RSV4=%x\n",
__func__,
is_vcore_ct,
dvfs_v_mode,
dvfsrc_rsrv);
pr_info("%s: FINAL vcore_opp_uv: %d, %d, %d, %d\n",
__func__,
vcore_opp_0_uv,
vcore_opp_1_uv,
vcore_opp_2_uv,
vcore_opp_3_uv);
set_vcore_uv_table(VCORE_OPP_0, vcore_opp_0_uv);
set_vcore_uv_table(VCORE_OPP_1, vcore_opp_1_uv);
set_vcore_uv_table(VCORE_OPP_2, vcore_opp_2_uv);
set_vcore_uv_table(VCORE_OPP_3, vcore_opp_3_uv);
return 0;
}
fs_initcall_sync(dvfsrc_opp_init)
static int __init dvfsrc_dram_opp_init(void)
{
int i;
for (i = 0; i < DDR_OPP_NUM; i++) {
set_opp_ddr_freq(i,
mtk_dramc_get_steps_freq(i) * 1000);
}
return 0;
}
device_initcall_sync(dvfsrc_dram_opp_init)
@@ -0,0 +1,294 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <mt-plat/mtk_devinfo.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <helio-dvfsrc-opp.h>
#include <helio-dvfsrc-qos.h>
#ifdef CONFIG_MEDIATEK_DRAMC
#include <dramc.h>
#endif
#define SOC_CPE_DETECT
#define AGING_VALUE 12500
#define V_VMODE_SHIFT 0
#define V_AGING_SHIFT 4
#define V_CT_SHIFT 5
#define V_CT_TEST_SHIFT 6
#define V_CT_OPP3_SHIFT 7
static int opp_min_bin_opp0;
static int opp_min_bin_opp3;
static int is_vcore_ct;
static int dvfsrc_rsrv;
#ifndef CONFIG_MEDIATEK_DRAMC
static int mtk_dramc_get_steps_freq(unsigned int step)
{
pr_info("get dram steps_freq fail\n");
return 4266;
}
#endif
u32 dvfsrc_ct_mode(void)
{
return is_vcore_ct;
}
u32 dvfsrc_vcore_mode(void)
{
return dvfsrc_rsrv;
}
void dvfsrc_opp_level_mapping(void)
{
set_vcore_opp(VCORE_DVFS_OPP_0, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_1, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_2, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_3, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_4, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_5, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_6, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_7, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_8, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_9, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_10, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_11, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_12, VCORE_OPP_3);
set_vcore_opp(VCORE_DVFS_OPP_13, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_14, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_15, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_16, VCORE_OPP_3);
set_vcore_opp(VCORE_DVFS_OPP_17, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_18, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_19, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_20, VCORE_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_0, DDR_OPP_0);
set_ddr_opp(VCORE_DVFS_OPP_1, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_2, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_3, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_4, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_5, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_6, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_7, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_8, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_9, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_10, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_11, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_12, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_13, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_14, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_15, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_16, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_17, DDR_OPP_6);
set_ddr_opp(VCORE_DVFS_OPP_18, DDR_OPP_6);
set_ddr_opp(VCORE_DVFS_OPP_19, DDR_OPP_6);
set_ddr_opp(VCORE_DVFS_OPP_20, DDR_OPP_6);
}
void dvfsrc_opp_table_init(void)
{
int i;
int vcore_opp, ddr_opp;
for (i = 0; i < VCORE_DVFS_OPP_NUM; i++) {
vcore_opp = get_vcore_opp(i);
ddr_opp = get_ddr_opp(i);
if (vcore_opp == VCORE_OPP_UNREQ || ddr_opp == DDR_OPP_UNREQ) {
set_opp_table(i, 0, 0);
continue;
}
set_opp_table(i, get_vcore_uv_table(vcore_opp),
mtk_dramc_get_steps_freq(ddr_opp) * 1000);
}
}
static int get_vb_volt(int vcore_opp)
{
int idx;
int ret = 0;
int ptpod = get_devinfo_with_index(209);
pr_info("%s: PTPOD: 0x%x\n", __func__, ptpod);
switch (vcore_opp) {
case VCORE_OPP_0:
idx = (ptpod >> 4) & 0x7;
if (idx >= opp_min_bin_opp0)
ret = 1;
break;
case VCORE_OPP_3:
idx = ptpod & 0xF;
if (idx > 10)
ret = 0;
else if (idx >= opp_min_bin_opp3)
ret = 1;
break;
default:
break;
}
return ret * 25000;
}
static int is_rising_need(void)
{
int idx;
int ptpod = get_devinfo_with_index(209);
pr_info("%s: PTPOD: 0x%x\n", __func__, ptpod);
idx = ptpod & 0xF;
if (idx == 11)
return 1;
else
return 0;
}
static int __init dvfsrc_opp_init(void)
{
struct device_node *dvfsrc_node = NULL;
int vcore_opp_0_uv, vcore_opp_1_uv, vcore_opp_2_uv, vcore_opp_3_uv;
int ct_test = 0;
int is_vcore_aging = 0;
int ct_opp3_en = 0;
int dvfs_v_mode = 0;
void __iomem *dvfsrc_base;
set_pwrap_cmd(VCORE_OPP_0, 0);
set_pwrap_cmd(VCORE_OPP_1, 1);
set_pwrap_cmd(VCORE_OPP_2, 2);
set_pwrap_cmd(VCORE_OPP_3, 3);
vcore_opp_0_uv = 725000;
vcore_opp_1_uv = 650000;
vcore_opp_2_uv = 600000;
vcore_opp_3_uv = 575000;
dvfsrc_node =
of_find_compatible_node(NULL, NULL, "mediatek,dvfsrc");
/* For Doe */
if (dvfsrc_node) {
dvfsrc_base = of_iomap(dvfsrc_node, 0);
if (dvfsrc_base) {
dvfsrc_rsrv = readl(dvfsrc_base + 0x610);
iounmap(dvfsrc_base);
}
pr_info("%s: vcore_arg = %08x\n",
__func__, dvfsrc_rsrv);
dvfs_v_mode = (dvfsrc_rsrv >> V_VMODE_SHIFT) & 0x3;
is_vcore_ct = (dvfsrc_rsrv >> V_CT_SHIFT) & 0x1;
ct_test = (dvfsrc_rsrv >> V_CT_TEST_SHIFT) & 0x1;
is_vcore_aging = (dvfsrc_rsrv >> V_AGING_SHIFT) & 0x1;
ct_opp3_en = (dvfsrc_rsrv >> V_CT_OPP3_SHIFT) & 0x1;
}
if (is_vcore_ct) {
if (ct_test) {
opp_min_bin_opp0 = 3;
opp_min_bin_opp3 = 3;
} else {
opp_min_bin_opp0 = 4;
opp_min_bin_opp3 = 4;
}
vcore_opp_0_uv -= get_vb_volt(VCORE_OPP_0);
if (ct_opp3_en)
vcore_opp_3_uv -= get_vb_volt(VCORE_OPP_3);
}
if (is_rising_need()) {
vcore_opp_2_uv = 625000;
vcore_opp_3_uv = 600000;
}
if (dvfs_v_mode == 3) {
/* LV */
if (vcore_opp_0_uv == 700000)
vcore_opp_0_uv = 662500;
else
vcore_opp_0_uv = 687500;
vcore_opp_1_uv = 612500;
if (vcore_opp_2_uv == 625000)
vcore_opp_2_uv = 593750;
else
vcore_opp_2_uv = 568750;
if (vcore_opp_3_uv == 550000)
vcore_opp_3_uv = 518750;
else if (vcore_opp_3_uv == 600000)
vcore_opp_3_uv = 568750;
else
vcore_opp_3_uv = 543750;
} else if (dvfs_v_mode == 1) {
/* HV */
if (vcore_opp_0_uv == 700000)
vcore_opp_0_uv = 737500;
else
vcore_opp_0_uv = 762500;
vcore_opp_1_uv = 687500;
if (vcore_opp_2_uv == 625000)
vcore_opp_2_uv = 656250;
else
vcore_opp_2_uv = 631250;
if (vcore_opp_3_uv == 550000)
vcore_opp_3_uv = 581250;
else if (vcore_opp_3_uv == 600000)
vcore_opp_3_uv = 631250;
else
vcore_opp_3_uv = 606250;
} else if (is_vcore_aging) {
vcore_opp_0_uv -= AGING_VALUE;
vcore_opp_1_uv -= AGING_VALUE;
vcore_opp_2_uv -= AGING_VALUE;
vcore_opp_3_uv -= AGING_VALUE;
}
pr_info("%s: CT=%d, VMODE=%d, RSV4=%x\n",
__func__,
is_vcore_ct,
dvfs_v_mode,
dvfsrc_rsrv);
pr_info("%s: FINAL vcore_opp_uv: %d, %d, %d, %d\n",
__func__,
vcore_opp_0_uv,
vcore_opp_1_uv,
vcore_opp_2_uv,
vcore_opp_3_uv);
set_vcore_uv_table(VCORE_OPP_0, vcore_opp_0_uv);
set_vcore_uv_table(VCORE_OPP_1, vcore_opp_1_uv);
set_vcore_uv_table(VCORE_OPP_2, vcore_opp_2_uv);
set_vcore_uv_table(VCORE_OPP_3, vcore_opp_3_uv);
return 0;
}
fs_initcall_sync(dvfsrc_opp_init)
static int __init dvfsrc_dram_opp_init(void)
{
int i;
for (i = 0; i < DDR_OPP_NUM; i++) {
set_opp_ddr_freq(i,
mtk_dramc_get_steps_freq(i) * 1000);
}
return 0;
}
device_initcall_sync(dvfsrc_dram_opp_init)
@@ -0,0 +1,237 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <mt-plat/mtk_devinfo.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <helio-dvfsrc-opp.h>
#include <helio-dvfsrc-qos.h>
#ifdef CONFIG_MEDIATEK_DRAMC
#include <dramc.h>
#endif
#define SOC_CPE_DETECT
#define AGING_VALUE 12500
#ifndef CONFIG_MEDIATEK_DRAMC
static int mtk_dramc_get_steps_freq(unsigned int step)
{
pr_info("get dram steps_freq fail\n");
return 4266;
}
#endif
int ddr_level_to_step(int opp)
{
/* TODO check later */
unsigned int step[] = {0, 1, 3, 6, 10, 14, 18};
return step[opp];
}
void dvfsrc_opp_level_mapping(void)
{
set_vcore_opp(VCORE_DVFS_OPP_0, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_1, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_2, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_3, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_4, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_5, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_6, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_7, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_8, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_9, VCORE_OPP_3);
set_vcore_opp(VCORE_DVFS_OPP_10, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_11, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_12, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_13, VCORE_OPP_3);
set_vcore_opp(VCORE_DVFS_OPP_14, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_15, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_16, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_17, VCORE_OPP_3);
set_vcore_opp(VCORE_DVFS_OPP_18, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_19, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_20, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_21, VCORE_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_0, DDR_OPP_0);
set_ddr_opp(VCORE_DVFS_OPP_1, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_2, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_3, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_4, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_5, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_6, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_7, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_8, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_9, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_10, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_11, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_12, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_13, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_14, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_15, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_16, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_17, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_18, DDR_OPP_6);
set_ddr_opp(VCORE_DVFS_OPP_19, DDR_OPP_6);
set_ddr_opp(VCORE_DVFS_OPP_20, DDR_OPP_6);
set_ddr_opp(VCORE_DVFS_OPP_21, DDR_OPP_6);
}
void dvfsrc_opp_table_init(void)
{
int i;
int vcore_opp, ddr_opp;
for (i = 0; i < VCORE_DVFS_OPP_NUM; i++) {
vcore_opp = get_vcore_opp(i);
ddr_opp = get_ddr_opp(i);
if (vcore_opp == VCORE_OPP_UNREQ || ddr_opp == DDR_OPP_UNREQ) {
set_opp_table(i, 0, 0);
continue;
}
set_opp_table(i, get_vcore_uv_table(vcore_opp),
mtk_dramc_get_steps_freq(ddr_opp) * 1000);
}
}
static int is_ct_support(void)
{
int ret = 0;
return ret;
}
static int is_aging_test(void)
{
int ret = 0;
#if defined(CONFIG_ARM64) && \
defined(CONFIG_BUILD_ARM64_DTB_OVERLAY_IMAGE_NAMES)
int len = sizeof(CONFIG_BUILD_ARM64_DTB_OVERLAY_IMAGE_NAMES);
if (strncmp(
&CONFIG_BUILD_ARM64_DTB_OVERLAY_IMAGE_NAMES[len - 7],
"_aging", 7) == 0)
return 1;
#endif
return ret;
}
static int is_qea_test(void)
{
int ret = 0;
return ret;
}
static int __init dvfsrc_opp_init(void)
{
struct device_node *dvfsrc_node = NULL;
int vcore_opp_0_uv, vcore_opp_1_uv, vcore_opp_2_uv, vcore_opp_3_uv;
int i;
int is_vcore_qea = is_qea_test();
int is_vcore_ct = is_ct_support();
int is_vcore_aging = is_aging_test();
int doe_ct = 0;
int dvfs_v_mode = 0;
int val = (get_devinfo_with_index(134) & 7);
set_pwrap_cmd(VCORE_OPP_0, 0);
set_pwrap_cmd(VCORE_OPP_1, 1);
set_pwrap_cmd(VCORE_OPP_2, 2);
set_pwrap_cmd(VCORE_OPP_3, 3);
if (val > 2)
val = 2;
val = val * 25000;
vcore_opp_0_uv = 725000;
vcore_opp_1_uv = 650000 + val;
vcore_opp_2_uv = 600000 + val;
vcore_opp_3_uv = 575000 + val;
dvfsrc_node =
of_find_compatible_node(NULL, NULL, "mediatek,dvfsrc");
/* For Doe */
if (dvfsrc_node) {
if (of_property_read_u32(dvfsrc_node, "vcore_ct",
(u32 *) &doe_ct) == 0) {
is_vcore_ct = doe_ct;
pr_info("%s: DOE CT = %d\n", __func__, doe_ct);
}
if (of_property_read_u32(dvfsrc_node, "dvfs_v_mode",
(u32 *) &dvfs_v_mode) == 0)
pr_info("%s: DOE DVFS_V_MODE = %d\n",
__func__, dvfs_v_mode);
}
#if 1 /* TODO: fill when LV/HV setting*/
if (is_vcore_qea || (dvfs_v_mode == 3)) {
/* LV */
vcore_opp_0_uv = 687500;
if (val == 0) {
vcore_opp_1_uv = 612500;
vcore_opp_2_uv = 568750;
vcore_opp_3_uv = 543750;
} else if (val == 25000) {
vcore_opp_1_uv = 637500;
vcore_opp_2_uv = 593750;
vcore_opp_3_uv = 568750;
} else if (val == 50000) {
vcore_opp_1_uv = 662500;
vcore_opp_2_uv = 612500;
vcore_opp_3_uv = 593750;
}
} else if (dvfs_v_mode == 1) {
/* HV */
vcore_opp_0_uv = 761250;
vcore_opp_1_uv = 682500;
vcore_opp_2_uv = 630000;
vcore_opp_3_uv = 603750;
} else if (is_vcore_aging) {
vcore_opp_0_uv -= AGING_VALUE;
vcore_opp_1_uv -= AGING_VALUE;
vcore_opp_2_uv -= AGING_VALUE;
vcore_opp_3_uv -= AGING_VALUE;
}
#endif
pr_info("%s: CT=%d, AGING=%d, QEA=%d, VMODE=%d val=%d\n",
__func__,
is_vcore_ct,
is_vcore_aging,
is_vcore_qea,
dvfs_v_mode,
val);
pr_info("%s: FINAL vcore_opp_uv: %d, %d, %d %d\n",
__func__,
vcore_opp_0_uv,
vcore_opp_1_uv,
vcore_opp_2_uv,
vcore_opp_3_uv);
set_vcore_uv_table(VCORE_OPP_0, vcore_opp_0_uv);
set_vcore_uv_table(VCORE_OPP_1, vcore_opp_1_uv);
set_vcore_uv_table(VCORE_OPP_2, vcore_opp_2_uv);
set_vcore_uv_table(VCORE_OPP_3, vcore_opp_3_uv);
for (i = 0; i < DDR_OPP_NUM; i++) {
set_opp_ddr_freq(i,
mtk_dramc_get_steps_freq(i) * 1000);
}
return 0;
}
device_initcall_sync(dvfsrc_opp_init)
@@ -0,0 +1,240 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <mt-plat/mtk_devinfo.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <helio-dvfsrc-opp.h>
#include <helio-dvfsrc-qos.h>
#ifdef CONFIG_MEDIATEK_DRAMC
#include <dramc.h>
#endif
#define V_VMODE_SHIFT 0
/*#define V_CT_SHIFT 5*/
#define V_CT_TEST_SHIFT 6
#define V_CT_V2_SHIFT 7
static int dvfsrc_rsrv;
#ifndef CONFIG_MEDIATEK_DRAMC
static int mtk_dramc_get_steps_freq(unsigned int step)
{
pr_info("get dram steps_freq fail\n");
return 4266;
}
#endif
void dvfsrc_opp_level_mapping(void)
{
set_vcore_opp(VCORE_DVFS_OPP_0, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_1, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_2, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_3, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_4, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_5, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_6, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_7, VCORE_OPP_3);
set_vcore_opp(VCORE_DVFS_OPP_8, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_9, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_10, VCORE_OPP_3);
set_vcore_opp(VCORE_DVFS_OPP_11, VCORE_OPP_4);
set_vcore_opp(VCORE_DVFS_OPP_12, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_13, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_14, VCORE_OPP_3);
set_vcore_opp(VCORE_DVFS_OPP_15, VCORE_OPP_4);
set_vcore_opp(VCORE_DVFS_OPP_16, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_17, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_18, VCORE_OPP_3);
set_vcore_opp(VCORE_DVFS_OPP_19, VCORE_OPP_4);
set_vcore_opp(VCORE_DVFS_OPP_20, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_21, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_22, VCORE_OPP_3);
set_vcore_opp(VCORE_DVFS_OPP_23, VCORE_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_0, DDR_OPP_0);
set_ddr_opp(VCORE_DVFS_OPP_1, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_2, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_3, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_4, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_5, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_6, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_7, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_8, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_9, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_10, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_11, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_12, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_13, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_14, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_15, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_16, DDR_OPP_6);
set_ddr_opp(VCORE_DVFS_OPP_17, DDR_OPP_6);
set_ddr_opp(VCORE_DVFS_OPP_18, DDR_OPP_6);
set_ddr_opp(VCORE_DVFS_OPP_19, DDR_OPP_6);
set_ddr_opp(VCORE_DVFS_OPP_20, DDR_OPP_7);
set_ddr_opp(VCORE_DVFS_OPP_21, DDR_OPP_7);
set_ddr_opp(VCORE_DVFS_OPP_22, DDR_OPP_7);
set_ddr_opp(VCORE_DVFS_OPP_23, DDR_OPP_7);
}
void dvfsrc_opp_table_init(void)
{
int i;
int vcore_opp, ddr_opp;
for (i = 0; i < VCORE_DVFS_OPP_NUM; i++) {
vcore_opp = get_vcore_opp(i);
ddr_opp = get_ddr_opp(i);
if (vcore_opp == VCORE_OPP_UNREQ || ddr_opp == DDR_OPP_UNREQ) {
set_opp_table(i, 0, 0);
continue;
}
set_opp_table(i, get_vcore_uv_table(vcore_opp),
mtk_dramc_get_steps_freq(ddr_opp) * 1000);
}
}
static int query_rising_idx(void)
{
int idx;
int ptpod = get_devinfo_with_index(134);
pr_info("%s: PTPOD: 0x%x\n", __func__, ptpod);
idx = ptpod & 0x7;
return idx;
}
static int get_vb_volt(int vcore_opp, int info_mode)
{
int ret = 0;
bool ct_test = false;
u32 ptpod = get_devinfo_with_index(69);
pr_info("%s: PTPOD: 0x%x\n", __func__, ptpod);
if (info_mode & (1 << V_CT_TEST_SHIFT))
ct_test = true;
if (vcore_opp == VCORE_OPP_0) {
ptpod = (ptpod >> 8) & 0xF;
ret = (ptpod <= 3) ? ptpod : 3;
if (!ct_test && (ret > 0))
ret = ret - 1;
}
pr_info("%s: OPP = %d %d %d\n", __func__, vcore_opp, ptpod, ret);
return ret * 12500;
}
static int __init dvfsrc_opp_init(void)
{
struct device_node *dvfsrc_node = NULL;
int vcore_opp_0_uv, vcore_opp_1_uv, vcore_opp_2_uv, vcore_opp_3_uv;
int vcore_opp_4_uv;
int is_vcore_ct = 0;
int dvfs_v_mode = 0;
int rising_idx = 0;
void __iomem *dvfsrc_base;
set_pwrap_cmd(VCORE_OPP_0, 16);
set_pwrap_cmd(VCORE_OPP_1, 17);
set_pwrap_cmd(VCORE_OPP_2, 18);
set_pwrap_cmd(VCORE_OPP_3, 19);
set_pwrap_cmd(VCORE_OPP_4, 20);
vcore_opp_0_uv = 750000;
vcore_opp_1_uv = 725000;
vcore_opp_2_uv = 650000;
vcore_opp_3_uv = 600000;
vcore_opp_4_uv = 575000;
dvfsrc_node =
of_find_compatible_node(NULL, NULL, "mediatek,dvfsrc");
/* For Doe */
if (dvfsrc_node) {
dvfsrc_base = of_iomap(dvfsrc_node, 0);
if (dvfsrc_base) {
dvfsrc_rsrv = readl(dvfsrc_base + 0x610);
iounmap(dvfsrc_base);
}
pr_info("%s: vcore_arg = %08x\n",
__func__, dvfsrc_rsrv);
dvfs_v_mode = (dvfsrc_rsrv >> V_VMODE_SHIFT) & 0x3;
is_vcore_ct = (dvfsrc_rsrv >> V_CT_V2_SHIFT) & 0x1;
}
rising_idx = query_rising_idx();
if (rising_idx > 2)
rising_idx = 2;
vcore_opp_2_uv = vcore_opp_2_uv + rising_idx * 25000;
vcore_opp_3_uv = vcore_opp_3_uv + rising_idx * 25000;
vcore_opp_4_uv = vcore_opp_4_uv + rising_idx * 25000;
if (is_vcore_ct && (rising_idx == 0)) {
vcore_opp_0_uv -= get_vb_volt(VCORE_OPP_0, dvfsrc_rsrv);
vcore_opp_1_uv = min(vcore_opp_0_uv, vcore_opp_1_uv);
}
if (dvfs_v_mode == 3) {
/* LV */
vcore_opp_0_uv = rounddown((vcore_opp_0_uv * 95) / 100, 6250);
vcore_opp_1_uv = rounddown((vcore_opp_1_uv * 95) / 100, 6250);
vcore_opp_2_uv = rounddown((vcore_opp_2_uv * 95) / 100, 6250);
vcore_opp_3_uv = rounddown((vcore_opp_3_uv * 95) / 100, 6250);
vcore_opp_4_uv = rounddown((vcore_opp_4_uv * 95) / 100, 6250);
} else if (dvfs_v_mode == 1) {
/* HV */
vcore_opp_0_uv = roundup((vcore_opp_0_uv * 105) / 100, 6250);
vcore_opp_1_uv = roundup((vcore_opp_1_uv * 105) / 100, 6250);
vcore_opp_2_uv = roundup((vcore_opp_2_uv * 105) / 100, 6250);
vcore_opp_3_uv = roundup((vcore_opp_3_uv * 105) / 100, 6250);
vcore_opp_4_uv = roundup((vcore_opp_4_uv * 105) / 100, 6250);
}
pr_info("%s: VMODE=%d, RSV4=%x\n",
__func__,
dvfs_v_mode,
dvfsrc_rsrv);
pr_info("%s: FINAL vcore_opp_uv: %d, %d, %d, %d, %d\n",
__func__,
vcore_opp_0_uv,
vcore_opp_1_uv,
vcore_opp_2_uv,
vcore_opp_3_uv,
vcore_opp_4_uv);
set_vcore_uv_table(VCORE_OPP_0, vcore_opp_0_uv);
set_vcore_uv_table(VCORE_OPP_1, vcore_opp_1_uv);
set_vcore_uv_table(VCORE_OPP_2, vcore_opp_2_uv);
set_vcore_uv_table(VCORE_OPP_3, vcore_opp_3_uv);
set_vcore_uv_table(VCORE_OPP_4, vcore_opp_4_uv);
return 0;
}
fs_initcall_sync(dvfsrc_opp_init)
static int __init dvfsrc_dram_opp_init(void)
{
int i;
for (i = 0; i < DDR_OPP_NUM; i++) {
set_opp_ddr_freq(i,
mtk_dramc_get_steps_freq(i) * 1000);
}
return 0;
}
device_initcall_sync(dvfsrc_dram_opp_init)
@@ -0,0 +1,323 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <mt-plat/mtk_devinfo.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <helio-dvfsrc-opp.h>
#include <helio-dvfsrc-qos.h>
#ifdef CONFIG_MEDIATEK_DRAMC
#include <dramc.h>
#endif
#ifdef CONFIG_MTK_DRAMC
#include <mtk_dramc.h>
#endif
#define V_VMODE_SHIFT 0
#define V_CT_SHIFT 5
#define V_CT_TEST_SHIFT 6
#define V_CT_OPP2_SHIFT 7
static int opp_min_bin_opp0;
static int opp_min_bin_opp2;
static int dvfsrc_rsrv;
#ifndef CONFIG_MTK_DRAMC
static int dram_steps_freq(unsigned int step)
{
pr_info("get dram steps_freq fail\n");
return 4266;
}
#endif
int ddr_level_to_step(int opp)
{
unsigned int step[] = {0, 1, 3, 5, 7, 9};
return step[opp];
}
void dvfsrc_opp_level_mapping(void)
{
set_vcore_opp(VCORE_DVFS_OPP_0, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_1, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_2, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_3, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_4, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_5, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_6, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_7, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_8, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_9, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_10, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_11, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_12, VCORE_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_0, DDR_OPP_0);
set_ddr_opp(VCORE_DVFS_OPP_1, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_2, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_3, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_4, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_5, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_6, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_7, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_8, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_9, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_10, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_11, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_12, DDR_OPP_5);
}
void dvfsrc_opp_table_init(void)
{
int i;
int vcore_opp, ddr_opp;
for (i = 0; i < VCORE_DVFS_OPP_NUM; i++) {
vcore_opp = get_vcore_opp(i);
ddr_opp = get_ddr_opp(i);
if (vcore_opp == VCORE_OPP_UNREQ || ddr_opp == DDR_OPP_UNREQ) {
set_opp_table(i, 0, 0);
continue;
}
set_opp_table(i, get_vcore_uv_table(vcore_opp),
dram_steps_freq(ddr_level_to_step(ddr_opp)) * 1000);
}
}
static int get_vb_volt(int vcore_opp)
{
int idx;
int ret = 0;
int ptpod = get_devinfo_with_index(69);
pr_info("%s: PTPOD: 0x%x\n", __func__, ptpod);
switch (vcore_opp) {
case VCORE_OPP_0:
idx = (ptpod >> 4) & 0xF;
if (idx >= opp_min_bin_opp0)
ret = 1;
break;
case VCORE_OPP_2:
idx = ptpod & 0xF;
if (idx >= opp_min_bin_opp2 && idx < 10)
ret = 1;
break;
default:
break;
}
return ret * 25000;
}
#define DEF_CPUL_LEAKAGE 100
#define DEVINFO_IDX_L 136 /* 07B8 */
#define DEVINFO_OFF_L 8
#define V_OF_FUSE_CPU 950
#define V_RISING_700000 1
#define V_RISING_675000 2
static int devinfo_table[] = {
3539, 492, 1038, 106, 231, 17, 46, 2179,
4, 481, 1014, 103, 225, 17, 45, 2129,
3, 516, 1087, 111, 242, 19, 49, 2282,
4, 504, 1063, 108, 236, 18, 47, 2230,
4, 448, 946, 96, 210, 15, 41, 1986,
2, 438, 924, 93, 205, 14, 40, 1941,
2, 470, 991, 101, 220, 16, 43, 2080,
3, 459, 968, 98, 215, 16, 42, 2033,
3, 594, 1250, 129, 279, 23, 57, 2621,
6, 580, 1221, 126, 273, 22, 56, 2561,
6, 622, 1309, 136, 293, 24, 60, 2745,
7, 608, 1279, 132, 286, 23, 59, 2683,
6, 541, 1139, 117, 254, 20, 51, 2390,
5, 528, 1113, 114, 248, 19, 50, 2335,
4, 566, 1193, 123, 266, 21, 54, 2503,
5, 553, 1166, 120, 260, 21, 53, 2446,
5, 338, 715, 70, 157, 9, 29, 1505,
3153, 330, 699, 69, 153, 9, 28, 1470,
3081, 354, 750, 74, 165, 10, 31, 1576,
3302, 346, 732, 72, 161, 10, 30, 1540,
3227, 307, 652, 63, 142, 8, 26, 1371,
2875, 300, 637, 62, 139, 7, 25, 1340,
2809, 322, 683, 67, 149, 8, 27, 1436,
3011, 315, 667, 65, 146, 8, 26, 1404,
2942, 408, 862, 86, 191, 13, 37, 1811,
1, 398, 842, 84, 186, 12, 36, 1769,
1, 428, 903, 91, 200, 14, 39, 1896,
2, 418, 882, 89, 195, 13, 38, 1853,
2, 371, 785, 78, 173, 11, 33, 1651,
3458, 363, 767, 76, 169, 10, 32, 1613,
3379, 389, 823, 82, 182, 12, 35, 1729,
1, 380, 804, 80, 177, 11, 34, 1689,
};
static int check_power_leakage(void)
{
int devinfo;
unsigned int temp_lkg;
devinfo = (int)get_devinfo_with_index(DEVINFO_IDX_L);
temp_lkg = (devinfo >> DEVINFO_OFF_L) & 0xff;
if (temp_lkg != 0)
temp_lkg = (int)devinfo_table[temp_lkg];
pr_info("cpu efuse leakage : 0x%x\n", temp_lkg);
if (temp_lkg > 0 && temp_lkg <= 40)
return 1;
return 0;
}
static int is_rising_need(void)
{
int idx;
int ptpod = get_devinfo_with_index(69);
int leakage = check_power_leakage();
pr_info("%s: PTPOD: 0x%x\n", __func__, ptpod);
if (leakage) {
idx = ptpod & 0xF;
if (idx == 1)
return V_RISING_700000;
else if (idx > 1 && idx < 10)
return V_RISING_675000;
}
return 0;
}
static int __init dvfsrc_opp_init(void)
{
struct device_node *dvfsrc_node = NULL;
int vcore_opp_0_uv, vcore_opp_1_uv, vcore_opp_2_uv;
int is_vcore_ct = 0;
int dvfs_v_mode = 0;
int ct_test = 0;
int ct_opp2_en = 0;
void __iomem *dvfsrc_base;
#if defined(CONFIG_MTK_DVFSRC_MT6781_PRETEST)
set_pwrap_cmd(VCORE_OPP_0, 3);
set_pwrap_cmd(VCORE_OPP_1, 1);
set_pwrap_cmd(VCORE_OPP_2, 0);
vcore_opp_0_uv = 825000;
vcore_opp_1_uv = 725000;
vcore_opp_2_uv = 650000;
/* meta vcore opp */
spm_dvfs_pwrap_cmd(2,
vcore_uv_to_pmic((vcore_opp_0_uv + vcore_opp_1_uv) >> 1));
#else
set_pwrap_cmd(VCORE_OPP_0, 0);
set_pwrap_cmd(VCORE_OPP_1, 2);
set_pwrap_cmd(VCORE_OPP_2, 3);
vcore_opp_0_uv = 800000;
vcore_opp_1_uv = 700000;
vcore_opp_2_uv = 650000;
#endif
dvfsrc_node =
of_find_compatible_node(NULL, NULL, "mediatek,dvfsrc");
/* For Doe */
if (dvfsrc_node) {
dvfsrc_base = of_iomap(dvfsrc_node, 0);
if (dvfsrc_base) {
dvfsrc_rsrv = readl(dvfsrc_base + 0x610);
iounmap(dvfsrc_base);
}
pr_info("%s: vcore_arg = %08x\n", __func__, dvfsrc_rsrv);
dvfs_v_mode = (dvfsrc_rsrv >> V_VMODE_SHIFT) & 0x3;
is_vcore_ct = (dvfsrc_rsrv >> V_CT_SHIFT) & 0x1;
ct_test = (dvfsrc_rsrv >> V_CT_TEST_SHIFT) & 0x1;
ct_opp2_en = (dvfsrc_rsrv >> V_CT_OPP2_SHIFT) & 0x1;
}
if (is_vcore_ct) {
if (ct_test) {
opp_min_bin_opp0 = 2;
opp_min_bin_opp2 = 4;
} else {
opp_min_bin_opp0 = 3;
opp_min_bin_opp2 = 5;
}
vcore_opp_0_uv -= get_vb_volt(VCORE_OPP_0);
if (ct_opp2_en)
vcore_opp_2_uv -= get_vb_volt(VCORE_OPP_2);
}
if (is_rising_need() == V_RISING_675000)
vcore_opp_2_uv = 675000;
else if (is_rising_need() == V_RISING_700000)
vcore_opp_2_uv = 700000;
if (dvfs_v_mode == 3) {
/* LV */
vcore_opp_0_uv = rounddown((vcore_opp_0_uv * 95) / 100, 6250);
vcore_opp_1_uv = rounddown((vcore_opp_1_uv * 95) / 100, 6250);
vcore_opp_2_uv = rounddown((vcore_opp_2_uv * 95) / 100, 6250);
} else if (dvfs_v_mode == 1) {
/* HV */
vcore_opp_0_uv = roundup((vcore_opp_0_uv * 105) / 100, 6250);
vcore_opp_1_uv = roundup((vcore_opp_1_uv * 105) / 100, 6250);
vcore_opp_2_uv = roundup((vcore_opp_2_uv * 105) / 100, 6250);
}
pr_info("%s: CT=%d, VMODE=%d, RSV4=%x\n",
__func__,
is_vcore_ct,
dvfs_v_mode,
dvfsrc_rsrv);
pr_info("%s: FINAL vcore_opp_uv: %d, %d, %d\n",
__func__,
vcore_opp_0_uv,
vcore_opp_1_uv,
vcore_opp_2_uv);
set_vcore_uv_table(VCORE_OPP_0, vcore_opp_0_uv);
set_vcore_uv_table(VCORE_OPP_1, vcore_opp_1_uv);
set_vcore_uv_table(VCORE_OPP_2, vcore_opp_2_uv);
/* meta vcore opp*/
spm_dvfs_pwrap_cmd(1,
vcore_uv_to_pmic((vcore_opp_0_uv + vcore_opp_1_uv) >> 1));
return 0;
}
fs_initcall_sync(dvfsrc_opp_init)
static int __init dvfsrc_dram_opp_init(void)
{
int i;
for (i = 0; i < DDR_OPP_NUM; i++) {
set_opp_ddr_freq(i,
dram_steps_freq(ddr_level_to_step(i)) * 1000);
}
return 0;
}
device_initcall_sync(dvfsrc_dram_opp_init)
@@ -0,0 +1,303 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <mt-plat/mtk_devinfo.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <helio-dvfsrc-opp.h>
#include <helio-dvfsrc-qos.h>
#ifdef CONFIG_MEDIATEK_DRAMC
#include <dramc.h>
#endif
#define V_VMODE_SHIFT 0
/*#define V_CT_SHIFT 5*/
#define V_CT_TEST_SHIFT 6
#define V_CT_V2_SHIFT 7
#define V_CONSYS_SHIFT 16
#define V_OPP_TYPE_SHIFT 20
static int dvfsrc_rsrv;
static const char vcore_750_bin_tst[14] = {0, 0, 0, 2, 2, 4, 4, 6, 6, 8, 8, 10, 10, 10};
static const char vcore_725_bin_tst[10] = {0, 0, 0, 2, 2, 4, 4, 6, 6, 6};
static const char vcore_550_bin_tst[10] = {0, 0, 0, 0, 0, 2, 2, 4, 4, 4};
static const char vcore_750_bin_mp[14] = {0, 0, 0, 0, 0, 2, 2, 4, 4, 6, 6, 8, 8, 8};
static const char vcore_725_bin_mp[10] = {0, 0, 0, 0, 0, 2, 2, 4, 4, 4};
static const char vcore_550_bin_mp[10] = {0, 0, 0, 0, 0, 0, 2, 2, 4, 4};
#ifndef CONFIG_MEDIATEK_DRAMC
static int mtk_dramc_get_steps_freq(unsigned int step)
{
pr_info("get dram steps_freq fail\n");
return 4266;
}
#endif
void dvfsrc_opp_level_mapping(void)
{
set_vcore_opp(VCORE_DVFS_OPP_0, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_1, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_2, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_3, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_4, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_5, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_6, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_7, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_8, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_9, VCORE_OPP_3);
set_vcore_opp(VCORE_DVFS_OPP_10, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_11, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_12, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_13, VCORE_OPP_3);
set_vcore_opp(VCORE_DVFS_OPP_14, VCORE_OPP_4);
set_vcore_opp(VCORE_DVFS_OPP_15, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_16, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_17, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_18, VCORE_OPP_3);
set_vcore_opp(VCORE_DVFS_OPP_19, VCORE_OPP_4);
set_vcore_opp(VCORE_DVFS_OPP_20, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_21, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_22, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_23, VCORE_OPP_3);
set_vcore_opp(VCORE_DVFS_OPP_24, VCORE_OPP_4);
set_vcore_opp(VCORE_DVFS_OPP_25, VCORE_OPP_0);
set_vcore_opp(VCORE_DVFS_OPP_26, VCORE_OPP_1);
set_vcore_opp(VCORE_DVFS_OPP_27, VCORE_OPP_2);
set_vcore_opp(VCORE_DVFS_OPP_28, VCORE_OPP_3);
set_vcore_opp(VCORE_DVFS_OPP_29, VCORE_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_0, DDR_OPP_0);
set_ddr_opp(VCORE_DVFS_OPP_1, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_2, DDR_OPP_1);
set_ddr_opp(VCORE_DVFS_OPP_3, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_4, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_5, DDR_OPP_2);
set_ddr_opp(VCORE_DVFS_OPP_6, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_7, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_8, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_9, DDR_OPP_3);
set_ddr_opp(VCORE_DVFS_OPP_10, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_11, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_12, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_13, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_14, DDR_OPP_4);
set_ddr_opp(VCORE_DVFS_OPP_15, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_16, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_17, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_18, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_19, DDR_OPP_5);
set_ddr_opp(VCORE_DVFS_OPP_20, DDR_OPP_6);
set_ddr_opp(VCORE_DVFS_OPP_21, DDR_OPP_6);
set_ddr_opp(VCORE_DVFS_OPP_22, DDR_OPP_6);
set_ddr_opp(VCORE_DVFS_OPP_23, DDR_OPP_6);
set_ddr_opp(VCORE_DVFS_OPP_24, DDR_OPP_6);
set_ddr_opp(VCORE_DVFS_OPP_25, DDR_OPP_7);
set_ddr_opp(VCORE_DVFS_OPP_26, DDR_OPP_7);
set_ddr_opp(VCORE_DVFS_OPP_27, DDR_OPP_7);
set_ddr_opp(VCORE_DVFS_OPP_28, DDR_OPP_7);
set_ddr_opp(VCORE_DVFS_OPP_29, DDR_OPP_7);
}
void dvfsrc_opp_table_init(void)
{
int i;
int vcore_opp, ddr_opp;
for (i = 0; i < VCORE_DVFS_OPP_NUM; i++) {
vcore_opp = get_vcore_opp(i);
ddr_opp = get_ddr_opp(i);
if (vcore_opp == VCORE_OPP_UNREQ || ddr_opp == DDR_OPP_UNREQ) {
set_opp_table(i, 0, 0);
continue;
}
set_opp_table(i, get_vcore_uv_table(vcore_opp),
get_opp_ddr_freq(ddr_opp));
}
}
static int get_vb_volt(int vcore_opp, int ct_test)
{
unsigned int idx;
int ret = 0;
int ptpod = get_devinfo_with_index(210);
pr_info("%s: PTPOD: 0x%x\n", __func__, ptpod);
switch (vcore_opp) {
case VCORE_OPP_0:
idx = (ptpod >> 8) & 0xF;
if (idx > 13)
ret = 0;
else if (ct_test)
ret = vcore_750_bin_tst[idx];
else
ret = vcore_750_bin_mp[idx];
pr_info("%s: VCORE_OPP_750 bin: 0x%x, %d\n", __func__, idx, ret);
break;
case VCORE_OPP_1:
idx = (ptpod >> 4) & 0xF;
if (idx > 9)
ret = 0;
else if (ct_test)
ret = vcore_725_bin_tst[idx];
else
ret = vcore_725_bin_mp[idx];
pr_info("%s: VCORE_OPP_725 bin: 0x%x, %d\n", __func__, idx, ret);
break;
case VCORE_OPP_4:
idx = ptpod & 0xF;
if (idx > 9)
ret = 0;
else if (ct_test)
ret = vcore_550_bin_tst[idx];
else
ret = vcore_550_bin_mp[idx];
pr_info("%s: VCORE_OPP_550 bin: 0x%x, %d\n", __func__, idx, ret);
break;
default:
break;
}
return ret * 6250;
}
static int is_rising_need(void)
{
int idx;
int ptpod = get_devinfo_with_index(210);
pr_info("%s: PTPOD: 0x%x\n", __func__, ptpod);
idx = ptpod & 0xF;
if (idx == 1 || idx == 2)
return idx;
return 0;
}
static int __init dvfsrc_opp_init(void)
{
struct device_node *dvfsrc_node = NULL;
int vcore_opp_0_uv, vcore_opp_1_uv, vcore_opp_2_uv, vcore_opp_3_uv;
int vcore_opp_4_uv;
int ct_test = 0;
int is_vcore_ct = 0;
int is_consys = 0;
int dvfs_v_mode = 0;
int opp_type = 0;
void __iomem *dvfsrc_base;
set_pwrap_cmd(VCORE_OPP_0, 0);
set_pwrap_cmd(VCORE_OPP_1, 1);
set_pwrap_cmd(VCORE_OPP_2, 2);
set_pwrap_cmd(VCORE_OPP_3, 3);
set_pwrap_cmd(VCORE_OPP_4, 4);
dvfsrc_node =
of_find_compatible_node(NULL, NULL, "mediatek,dvfsrc");
/* For Doe */
if (dvfsrc_node) {
dvfsrc_base = of_iomap(dvfsrc_node, 0);
if (dvfsrc_base) {
dvfsrc_rsrv = readl(dvfsrc_base + 0x610);
iounmap(dvfsrc_base);
}
pr_info("%s: vcore_arg = %08x\n", __func__, dvfsrc_rsrv);
dvfs_v_mode = (dvfsrc_rsrv >> V_VMODE_SHIFT) & 0x3;
is_vcore_ct = (dvfsrc_rsrv >> V_CT_V2_SHIFT) & 0x1;
ct_test = (dvfsrc_rsrv >> V_CT_TEST_SHIFT) & 0x1;
opp_type = (dvfsrc_rsrv >> V_OPP_TYPE_SHIFT) & 0x3;
is_consys = (dvfsrc_rsrv >> V_CONSYS_SHIFT) & 0x1;
}
if (opp_type == 0)
vcore_opp_0_uv = 725000;
else
vcore_opp_0_uv = 750000;
vcore_opp_1_uv = 725000;
vcore_opp_2_uv = 650000;
vcore_opp_3_uv = 600000;
vcore_opp_4_uv = 550000;
if (is_vcore_ct && (is_rising_need() == 0) && (opp_type == 1)) {
/* disable opp1 volt bin */
/*
if (opp_type == 0)
vcore_opp_0_uv -= get_vb_volt(VCORE_OPP_1, ct_test);
else
vcore_opp_0_uv -= get_vb_volt(VCORE_OPP_0, ct_test);
*/
vcore_opp_1_uv -= get_vb_volt(VCORE_OPP_1, ct_test);
/* disable opp4 volt bin */
/*
vcore_opp_4_uv -= get_vb_volt(VCORE_OPP_4, ct_test);
vcore_opp_0_uv = max(vcore_opp_0_uv, vcore_opp_1_uv);
*/
}
if (is_rising_need() == 2)
vcore_opp_4_uv = 575000;
else if (is_rising_need() == 1)
vcore_opp_4_uv = 600000;
if (is_consys)
vcore_opp_4_uv = 600000;
if (dvfs_v_mode == 3) {
/* LV */
vcore_opp_0_uv = rounddown((vcore_opp_0_uv * 95) / 100, 6250);
vcore_opp_1_uv = rounddown((vcore_opp_1_uv * 95) / 100, 6250);
vcore_opp_2_uv = rounddown((vcore_opp_2_uv * 95) / 100, 6250);
vcore_opp_3_uv = rounddown((vcore_opp_3_uv * 95) / 100, 6250);
vcore_opp_4_uv = rounddown((vcore_opp_4_uv * 95) / 100, 6250);
} else if (dvfs_v_mode == 1) {
/* HV */
vcore_opp_0_uv = roundup((vcore_opp_0_uv * 105) / 100, 6250);
vcore_opp_1_uv = roundup((vcore_opp_1_uv * 105) / 100, 6250);
vcore_opp_2_uv = roundup((vcore_opp_2_uv * 105) / 100, 6250);
vcore_opp_3_uv = roundup((vcore_opp_3_uv * 105) / 100, 6250);
vcore_opp_4_uv = roundup((vcore_opp_4_uv * 105) / 100, 6250);
}
pr_info("%s: VMODE=%d, RSV4=%x\n",
__func__,
dvfs_v_mode,
dvfsrc_rsrv);
pr_info("%s: FINAL vcore_opp_uv: %d, %d, %d, %d, %d\n",
__func__,
vcore_opp_0_uv,
vcore_opp_1_uv,
vcore_opp_2_uv,
vcore_opp_3_uv,
vcore_opp_4_uv);
set_vcore_uv_table(VCORE_OPP_0, vcore_opp_0_uv);
set_vcore_uv_table(VCORE_OPP_1, vcore_opp_1_uv);
set_vcore_uv_table(VCORE_OPP_2, vcore_opp_2_uv);
set_vcore_uv_table(VCORE_OPP_3, vcore_opp_3_uv);
set_vcore_uv_table(VCORE_OPP_4, vcore_opp_4_uv);
return 0;
}
fs_initcall_sync(dvfsrc_opp_init)
static int __init dvfsrc_dram_opp_init(void)
{
int i;
for (i = 0; i < DDR_OPP_NUM; i++)
set_opp_ddr_freq(i, mtk_dramc_get_steps_freq(i) * 1000);
return 0;
}
device_initcall_sync(dvfsrc_dram_opp_init)
@@ -0,0 +1,219 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <helio-dvfsrc-qos.h>
#include <helio-dvfsrc-opp.h>
#if defined(CONFIG_MTK_QOS_V2)
#include <mtk_qos_ipi.h>
#endif
static struct opp_profile opp_table[VCORE_DVFS_OPP_NUM];
static int vcore_dvfs_to_vcore_opp[VCORE_DVFS_OPP_NUM];
static int vcore_dvfs_to_ddr_opp[VCORE_DVFS_OPP_NUM];
static int vcore_uv_table[VCORE_OPP_NUM];
static int vcore_opp_to_pwrap_cmd[VCORE_OPP_NUM];
static int ddr_table[DDR_OPP_NUM];
void set_opp_table(unsigned int vcore_dvfs_opp, int vcore_uv, int ddr_khz)
{
#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
#if defined(CONFIG_MTK_QOS_V2)
struct qos_ipi_data qos_d;
qos_d.cmd = QOS_IPI_OPP_TABLE;
qos_d.u.opp_table.vcore_dvfs_opp = vcore_dvfs_opp;
qos_d.u.opp_table.vcore_uv = vcore_uv;
qos_d.u.opp_table.ddr_khz = ddr_khz;
qos_ipi_to_sspm_command(&qos_d, 4);
#endif
#endif
if (vcore_dvfs_opp >= VCORE_DVFS_OPP_NUM)
return;
opp_table[vcore_dvfs_opp].vcore_uv = vcore_uv;
opp_table[vcore_dvfs_opp].ddr_khz = ddr_khz;
}
void set_vcore_opp(unsigned int vcore_dvfs_opp, int vcore_opp)
{
#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
#if defined(CONFIG_MTK_QOS_V2)
struct qos_ipi_data qos_d;
qos_d.cmd = QOS_IPI_VCORE_OPP;
qos_d.u.vcore_opp.vcore_dvfs_opp = vcore_dvfs_opp;
qos_d.u.vcore_opp.vcore_opp = vcore_opp;
qos_ipi_to_sspm_command(&qos_d, 3);
#endif
#endif
if (vcore_dvfs_opp >= VCORE_DVFS_OPP_NUM)
return;
vcore_dvfs_to_vcore_opp[vcore_dvfs_opp] = vcore_opp;
}
int get_vcore_opp(unsigned int opp)
{
if (opp >= VCORE_DVFS_OPP_NUM)
return 0;
return vcore_dvfs_to_vcore_opp[opp];
}
int get_vcore_uv(unsigned int opp)
{
if (opp >= VCORE_DVFS_OPP_NUM)
return 0;
return opp_table[opp].vcore_uv;
}
int get_cur_vcore_opp(void)
{
unsigned int idx;
if (!is_dvfsrc_enabled())
return VCORE_OPP_UNREQ;
idx = get_cur_vcore_dvfs_opp();
if (idx >= VCORE_DVFS_OPP_NUM)
return VCORE_OPP_UNREQ;
return vcore_dvfs_to_vcore_opp[idx];
}
int get_cur_vcore_uv(void)
{
unsigned int idx;
if (!is_dvfsrc_enabled())
return 0;
idx = get_cur_vcore_dvfs_opp();
if (idx >= VCORE_DVFS_OPP_NUM)
return 0;
return opp_table[idx].vcore_uv;
}
void set_ddr_opp(unsigned int vcore_dvfs_opp, int ddr_opp)
{
#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
#if defined(CONFIG_MTK_QOS_V2)
struct qos_ipi_data qos_d;
qos_d.cmd = QOS_IPI_DDR_OPP;
qos_d.u.ddr_opp.vcore_dvfs_opp = vcore_dvfs_opp;
qos_d.u.ddr_opp.ddr_opp = ddr_opp;
qos_ipi_to_sspm_command(&qos_d, 3);
#endif
#endif
if (vcore_dvfs_opp >= VCORE_DVFS_OPP_NUM)
return;
vcore_dvfs_to_ddr_opp[vcore_dvfs_opp] = ddr_opp;
}
int get_ddr_opp(unsigned int opp)
{
if (opp >= VCORE_DVFS_OPP_NUM)
return 0;
return vcore_dvfs_to_ddr_opp[opp];
}
int get_ddr_khz(unsigned int opp)
{
if (opp >= VCORE_DVFS_OPP_NUM)
return 0;
return opp_table[opp].ddr_khz;
}
int get_cur_ddr_opp(void)
{
unsigned int idx;
if (!is_dvfsrc_enabled())
return DDR_OPP_UNREQ;
idx = get_cur_vcore_dvfs_opp();
if (idx >= VCORE_DVFS_OPP_NUM)
return DDR_OPP_UNREQ;
return vcore_dvfs_to_ddr_opp[idx];
}
int get_cur_ddr_khz(void)
{
unsigned int idx;
if (!is_dvfsrc_enabled())
return 0;
idx = get_cur_vcore_dvfs_opp();
if (idx >= VCORE_DVFS_OPP_NUM)
return 0;
return opp_table[idx].ddr_khz;
}
void set_vcore_uv_table(unsigned int vcore_opp, int vcore_uv)
{
if (vcore_opp >= VCORE_OPP_NUM)
return;
spm_dvfs_pwrap_cmd(get_pwrap_cmd(vcore_opp),
vcore_uv_to_pmic(vcore_uv));
vcore_uv_table[vcore_opp] = vcore_uv;
}
int get_opp_ddr_freq(unsigned int ddr_opp)
{
if (ddr_opp >= DDR_OPP_NUM)
return 0;
return ddr_table[ddr_opp];
}
void set_opp_ddr_freq(unsigned int ddr_opp, int ddr_freq)
{
if (ddr_opp >= DDR_OPP_NUM)
return;
ddr_table[ddr_opp] = ddr_freq;
}
int get_vcore_uv_table(unsigned int vcore_opp)
{
if (vcore_opp >= VCORE_OPP_NUM)
return 0;
return vcore_uv_table[vcore_opp];
}
void set_pwrap_cmd(unsigned int vcore_opp, int pwrap_cmd)
{
if (vcore_opp >= VCORE_OPP_NUM)
return;
vcore_opp_to_pwrap_cmd[vcore_opp] = pwrap_cmd;
}
int get_pwrap_cmd(unsigned int vcore_opp)
{
if (vcore_opp >= VCORE_OPP_NUM)
return 0;
return vcore_opp_to_pwrap_cmd[vcore_opp];
}
@@ -0,0 +1,571 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/soc/mediatek/mtk-pm-qos.h>
#include <linux/sched.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/of.h>
#include <spm/mtk_spm.h>
#if defined(CONFIG_MTK_QOS_V2)
#include <mtk_qos_ipi.h>
#endif
#include <helio-dvfsrc-qos.h>
struct helio_dvfsrc *dvfsrc;
#define DVFSRC_REG(offset) (dvfsrc->regs + offset)
#if defined(DVFSRC_SMC_CONTROL)
#define SPM_REG(offset) (dvfsrc->spm_regs + offset)
u32 spm_reg_read(u32 offset)
{
if (is_dvfsrc_enabled())
return readl(SPM_REG(offset));
else
return 0;
}
#endif
u32 dvfsrc_read(u32 offset)
{
if (is_dvfsrc_enabled())
return readl(DVFSRC_REG(offset));
else
return 0;
}
void dvfsrc_write(u32 offset, u32 val)
{
if (is_dvfsrc_enabled())
writel(val, DVFSRC_REG(offset));
}
static void dvfsrc_restore(void)
{
int i, class_type;
for (i = DVFSRC_QOS_MEMORY_BANDWIDTH; i < DVFSRC_QOS_NUM_CLASSES; i++) {
switch (i) {
case DVFSRC_QOS_MEMORY_BANDWIDTH:
class_type = MTK_PM_QOS_MEMORY_BANDWIDTH_TEST;
break;
case DVFSRC_QOS_CPU_MEMORY_BANDWIDTH:
class_type = MTK_PM_QOS_CPU_MEMORY_BANDWIDTH;
break;
case DVFSRC_QOS_GPU_MEMORY_BANDWIDTH:
class_type = MTK_PM_QOS_GPU_MEMORY_BANDWIDTH;
break;
case DVFSRC_QOS_MM_MEMORY_BANDWIDTH:
class_type = PM_QOS_MM_MEMORY_BANDWIDTH;
break;
case DVFSRC_QOS_OTHER_MEMORY_BANDWIDTH:
class_type = MTK_PM_QOS_OTHER_MEMORY_BANDWIDTH;
break;
case DVFSRC_QOS_DDR_OPP:
class_type = MTK_PM_QOS_DDR_OPP;
break;
case DVFSRC_QOS_VCORE_OPP:
class_type = MTK_PM_QOS_VCORE_OPP;
break;
case DVFSRC_QOS_SCP_VCORE_REQUEST:
class_type = MTK_PM_QOS_SCP_VCORE_REQUEST;
break;
case DVFSRC_QOS_POWER_MODEL_DDR_REQUEST:
class_type = MTK_PM_QOS_POWER_MODEL_DDR_REQUEST;
break;
case DVFSRC_QOS_POWER_MODEL_VCORE_REQUEST:
class_type = MTK_PM_QOS_POWER_MODEL_VCORE_REQUEST;
break;
case DVFSRC_QOS_VCORE_DVFS_FORCE_OPP:
class_type = MTK_PM_QOS_VCORE_DVFS_FORCE_OPP;
break;
case DVFSRC_QOS_ISP_HRT_BANDWIDTH:
class_type = PM_QOS_ISP_HRT_BANDWIDTH;
break;
case DVFSRC_QOS_APU_MEMORY_BANDWIDTH:
class_type = PM_QOS_APU_MEMORY_BANDWIDTH;
break;
default:
continue;
}
commit_data(i, mtk_pm_qos_request(class_type), 0);
}
dvfsrc->qos_enabled = 1;
}
#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
#if defined(CONFIG_MTK_QOS_V2)
static void helio_dvfsrc_sspm_init(int dvfsrc_en)
{
struct qos_ipi_data qos_d;
qos_d.cmd = QOS_IPI_DVFSRC_ENABLE;
qos_d.u.dvfsrc_enable.dvfsrc_en = dvfsrc_en;
qos_ipi_to_sspm_command(&qos_d, 2);
}
#endif
#endif
void helio_dvfsrc_enable(int dvfsrc_en)
{
int ret = 0;
if (dvfsrc_en > 1 || dvfsrc_en < 0)
return;
if (dvfsrc->dvfsrc_enabled == dvfsrc_en && dvfsrc_en == 1) {
pr_info("DVFSRC already enabled\n");
return;
}
#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
#if defined(CONFIG_MTK_QOS_V2)
helio_dvfsrc_sspm_init(dvfsrc_en);
#endif
#endif
dvfsrc->dvfsrc_enabled = dvfsrc_en;
dvfsrc->opp_forced = 0;
ret = sprintf(dvfsrc->force_start, "0");
if (ret < 0)
pr_info("sprintf fail\n");
ret = sprintf(dvfsrc->force_end, "0");
if (ret < 0)
pr_info("sprintf fail\n");
dvfsrc_restore();
if (dvfsrc_en)
dvfsrc_en |= (dvfsrc->dvfsrc_flag << 1);
mtk_spmfw_init(dvfsrc_en, 1);
}
void helio_dvfsrc_flag_set(int flag)
{
dvfsrc->dvfsrc_flag = flag;
}
int helio_dvfsrc_flag_get(void)
{
return dvfsrc->dvfsrc_flag;
}
static int helio_dvfsrc_common_init(void)
{
dvfsrc_opp_level_mapping();
dvfsrc_opp_table_init();
return 0;
}
int is_dvfsrc_enabled(void)
{
if (dvfsrc)
return dvfsrc->dvfsrc_enabled == 1;
return 0;
}
int is_dvfsrc_qos_enabled(void)
{
if (dvfsrc)
return dvfsrc->qos_enabled == 1;
return 0;
}
static void get_mtk_pm_qos_info(char *p)
{
p += sprintf(p, "%-24s: %d\n",
"MTK_PM_QOS_MEMORY_BW",
mtk_pm_qos_request(MTK_PM_QOS_MEMORY_BANDWIDTH_TEST));
p += sprintf(p, "%-24s: %d\n",
"MTK_PM_QOS_CPU_MEMORY_BW",
mtk_pm_qos_request(MTK_PM_QOS_CPU_MEMORY_BANDWIDTH));
p += sprintf(p, "%-24s: %d\n",
"MTK_PM_QOS_GPU_MEMORY_BW",
mtk_pm_qos_request(MTK_PM_QOS_GPU_MEMORY_BANDWIDTH));
p += sprintf(p, "%-24s: %d\n",
"MTK_PM_QOS_MM_MEMORY_BW",
mtk_pm_qos_request(PM_QOS_MM_MEMORY_BANDWIDTH));
p += sprintf(p, "%-24s: %d\n",
"MTK_PM_QOS_OTHER_MEMORY_BW",
mtk_pm_qos_request(MTK_PM_QOS_OTHER_MEMORY_BANDWIDTH));
p += sprintf(p, "%-24s: 0x%x\n",
"MTK_PM_QOS_DDR_OPP",
mtk_pm_qos_request(MTK_PM_QOS_DDR_OPP));
p += sprintf(p, "%-24s: 0x%x\n",
"MTK_PM_QOS_VCORE_OPP",
mtk_pm_qos_request(MTK_PM_QOS_VCORE_OPP));
p += sprintf(p, "%-24s: 0x%x\n",
"MTK_PM_QOS_SCP_VCORE_REQ",
mtk_pm_qos_request(MTK_PM_QOS_SCP_VCORE_REQUEST));
p += sprintf(p, "%-24s: 0x%x\n",
"MTK_PM_QOS_PM_DDR_REQ",
mtk_pm_qos_request(MTK_PM_QOS_POWER_MODEL_DDR_REQUEST));
p += sprintf(p, "%-24s: 0x%x\n",
"MTK_PM_QOS_PM_VCORE_REQ",
mtk_pm_qos_request(MTK_PM_QOS_POWER_MODEL_VCORE_REQUEST));
p += sprintf(p, "%-24s: 0x%x\n",
"MTK_PM_QOS_FORCE_OPP",
mtk_pm_qos_request(MTK_PM_QOS_VCORE_DVFS_FORCE_OPP));
p += sprintf(p, "%-24s: 0x%x\n",
"MTK_PM_QOS_ISP_HRT",
mtk_pm_qos_request(MTK_PM_QOS_ISP_HRT_BANDWIDTH));
}
u32 dvfsrc_dump_reg(char *ptr, u32 count)
{
char buf[1024];
u32 index = 0;
memset(buf, '\0', sizeof(buf));
get_opp_info(buf);
if (ptr) {
index += scnprintf(&ptr[index], (count - index - 1),
"%s\n", buf);
} else
pr_info("%s\n", buf);
memset(buf, '\0', sizeof(buf));
get_dvfsrc_reg(buf);
if (ptr) {
index += scnprintf(&ptr[index], (count - index - 1),
"%s\n", buf);
} else
pr_info("%s\n", buf);
memset(buf, '\0', sizeof(buf));
get_dvfsrc_record(buf);
if (ptr) {
index += scnprintf(&ptr[index], (count - index - 1),
"%s\n", buf);
} else
pr_info("%s\n", buf);
memset(buf, '\0', sizeof(buf));
get_spm_reg(buf);
if (ptr) {
index += scnprintf(&ptr[index], (count - index - 1),
"%s\n", buf);
} else
pr_info("%s\n", buf);
memset(buf, '\0', sizeof(buf));
get_mtk_pm_qos_info(buf);
if (ptr) {
index += scnprintf(&ptr[index], (count - index - 1),
"%s\n", buf);
} else
pr_info("%s\n", buf);
return index;
}
void dvfsrc_set_power_model_ddr_request(unsigned int level)
{
commit_data(DVFSRC_QOS_POWER_MODEL_DDR_REQUEST, level, 1);
}
static int mtk_pm_qos_memory_bw_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(DVFSRC_QOS_MEMORY_BANDWIDTH, l, 1);
return NOTIFY_OK;
}
static int mtk_pm_qos_cpu_memory_bw_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(DVFSRC_QOS_CPU_MEMORY_BANDWIDTH, l, 1);
return NOTIFY_OK;
}
static int mtk_pm_qos_gpu_memory_bw_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(DVFSRC_QOS_GPU_MEMORY_BANDWIDTH, l, 1);
return NOTIFY_OK;
}
static int mtk_pm_qos_mm_memory_bw_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(DVFSRC_QOS_MM_MEMORY_BANDWIDTH, l, 1);
return NOTIFY_OK;
}
static int mtk_pm_qos_other_memory_bw_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(DVFSRC_QOS_OTHER_MEMORY_BANDWIDTH, l, 1);
return NOTIFY_OK;
}
static int mtk_pm_qos_ddr_opp_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(DVFSRC_QOS_DDR_OPP, l, 1);
return NOTIFY_OK;
}
static int mtk_pm_qos_vcore_opp_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(DVFSRC_QOS_VCORE_OPP, l, 1);
return NOTIFY_OK;
}
static int mtk_pm_qos_scp_vcore_request_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(DVFSRC_QOS_SCP_VCORE_REQUEST, l, 1);
return NOTIFY_OK;
}
static int mtk_pm_qos_power_model_ddr_request_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(DVFSRC_QOS_POWER_MODEL_DDR_REQUEST, l, 1);
return NOTIFY_OK;
}
static int mtk_pm_qos_power_model_vcore_request_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(DVFSRC_QOS_POWER_MODEL_VCORE_REQUEST, l, 1);
return NOTIFY_OK;
}
static int mtk_pm_qos_vcore_dvfs_force_opp_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(DVFSRC_QOS_VCORE_DVFS_FORCE_OPP, l, 1);
return NOTIFY_OK;
}
static int pmqos_isp_hrt_memory_bw_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(DVFSRC_QOS_ISP_HRT_BANDWIDTH, l, 1);
return NOTIFY_OK;
}
static int mtk_pm_qos_apu_memory_bw_notify(struct notifier_block *b,
unsigned long l, void *v)
{
commit_data(DVFSRC_QOS_APU_MEMORY_BANDWIDTH, l, 1);
return NOTIFY_OK;
}
static void mtk_pm_qos_notifier_register(void)
{
dvfsrc->mtk_pm_qos_memory_bw_nb.notifier_call =
mtk_pm_qos_memory_bw_notify;
dvfsrc->mtk_pm_qos_cpu_memory_bw_nb.notifier_call =
mtk_pm_qos_cpu_memory_bw_notify;
dvfsrc->mtk_pm_qos_gpu_memory_bw_nb.notifier_call =
mtk_pm_qos_gpu_memory_bw_notify;
dvfsrc->mtk_pm_qos_mm_memory_bw_nb.notifier_call =
mtk_pm_qos_mm_memory_bw_notify;
dvfsrc->mtk_pm_qos_other_memory_bw_nb.notifier_call =
mtk_pm_qos_other_memory_bw_notify;
dvfsrc->mtk_pm_qos_ddr_opp_nb.notifier_call =
mtk_pm_qos_ddr_opp_notify;
dvfsrc->mtk_pm_qos_vcore_opp_nb.notifier_call =
mtk_pm_qos_vcore_opp_notify;
dvfsrc->mtk_pm_qos_scp_vcore_request_nb.notifier_call =
mtk_pm_qos_scp_vcore_request_notify;
dvfsrc->mtk_pm_qos_power_model_ddr_request_nb.notifier_call =
mtk_pm_qos_power_model_ddr_request_notify;
dvfsrc->mtk_pm_qos_power_model_vcore_request_nb.notifier_call =
mtk_pm_qos_power_model_vcore_request_notify;
dvfsrc->mtk_pm_qos_vcore_dvfs_force_opp_nb.notifier_call =
mtk_pm_qos_vcore_dvfs_force_opp_notify;
dvfsrc->mtk_pm_qos_isp_hrt_bw_nb.notifier_call =
pmqos_isp_hrt_memory_bw_notify;
dvfsrc->mtk_pm_qos_apu_memory_bw_nb.notifier_call =
mtk_pm_qos_apu_memory_bw_notify;
mtk_pm_qos_add_notifier(MTK_PM_QOS_MEMORY_BANDWIDTH_TEST,
&dvfsrc->mtk_pm_qos_memory_bw_nb);
mtk_pm_qos_add_notifier(MTK_PM_QOS_CPU_MEMORY_BANDWIDTH,
&dvfsrc->mtk_pm_qos_cpu_memory_bw_nb);
mtk_pm_qos_add_notifier(MTK_PM_QOS_GPU_MEMORY_BANDWIDTH,
&dvfsrc->mtk_pm_qos_gpu_memory_bw_nb);
mtk_pm_qos_add_notifier(PM_QOS_MM_MEMORY_BANDWIDTH,
&dvfsrc->mtk_pm_qos_mm_memory_bw_nb);
mtk_pm_qos_add_notifier(MTK_PM_QOS_OTHER_MEMORY_BANDWIDTH,
&dvfsrc->mtk_pm_qos_other_memory_bw_nb);
mtk_pm_qos_add_notifier(MTK_PM_QOS_DDR_OPP,
&dvfsrc->mtk_pm_qos_ddr_opp_nb);
mtk_pm_qos_add_notifier(MTK_PM_QOS_VCORE_OPP,
&dvfsrc->mtk_pm_qos_vcore_opp_nb);
mtk_pm_qos_add_notifier(MTK_PM_QOS_SCP_VCORE_REQUEST,
&dvfsrc->mtk_pm_qos_scp_vcore_request_nb);
mtk_pm_qos_add_notifier(MTK_PM_QOS_POWER_MODEL_DDR_REQUEST,
&dvfsrc->mtk_pm_qos_power_model_ddr_request_nb);
mtk_pm_qos_add_notifier(MTK_PM_QOS_POWER_MODEL_VCORE_REQUEST,
&dvfsrc->mtk_pm_qos_power_model_vcore_request_nb);
mtk_pm_qos_add_notifier(MTK_PM_QOS_VCORE_DVFS_FORCE_OPP,
&dvfsrc->mtk_pm_qos_vcore_dvfs_force_opp_nb);
mtk_pm_qos_add_notifier(PM_QOS_ISP_HRT_BANDWIDTH,
&dvfsrc->mtk_pm_qos_isp_hrt_bw_nb);
mtk_pm_qos_add_notifier(PM_QOS_APU_MEMORY_BANDWIDTH,
&dvfsrc->mtk_pm_qos_apu_memory_bw_nb);
}
__weak void mtk_pm_qos_trace_dbg_show_request(int mtk_pm_qos_class)
{
}
static DEFINE_RATELIMIT_STATE(tracelimit, 5 * HZ, 1);
void vcorefs_trace_qos(void)
{
if (__ratelimit(&tracelimit)) {
mtk_pm_qos_trace_dbg_show_request(MTK_PM_QOS_DDR_OPP);
mtk_pm_qos_trace_dbg_show_request(MTK_PM_QOS_VCORE_OPP);
}
}
static int helio_dvfsrc_probe(struct platform_device *pdev)
{
int ret = 0;
struct resource *res;
struct device_node *np = pdev->dev.of_node;
if (!spm_load_firmware_status()) {
pr_err("SPM FIRMWARE IS NOT READY\n");
return -ENODEV;
}
dvfsrc = devm_kzalloc(&pdev->dev, sizeof(*dvfsrc), GFP_KERNEL);
if (!dvfsrc)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dvfsrc->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dvfsrc->regs))
return PTR_ERR(dvfsrc->regs);
platform_set_drvdata(pdev, dvfsrc);
dvfsrc->dev = &pdev->dev;
ret = helio_dvfsrc_add_interface(&pdev->dev);
if (ret)
return ret;
mtk_pm_qos_notifier_register();
helio_dvfsrc_common_init();
if (of_property_read_u32(np, "dvfsrc_flag",
(u32 *) &dvfsrc->dvfsrc_flag))
dvfsrc->dvfsrc_flag = 0;
helio_dvfsrc_platform_pre_init(dvfsrc);
helio_dvfsrc_config(dvfsrc);
pr_info("%s: init done\n", __func__);
return 0;
}
static int helio_dvfsrc_remove(struct platform_device *pdev)
{
helio_dvfsrc_remove_interface(&pdev->dev);
return 0;
}
static const struct of_device_id helio_dvfsrc_of_match[] = {
{ .compatible = "mediatek,dvfsrc" },
{ .compatible = "mediatek,mt6785-dvfsrc" },
{ },
};
MODULE_DEVICE_TABLE(of, helio_dvfsrc_of_match);
static __maybe_unused int helio_dvfsrc_suspend(struct device *dev)
{
int ret = 0;
if (dvfsrc->suspend) {
ret = dvfsrc->suspend(dvfsrc);
if (ret)
return ret;
}
return 0;
}
static __maybe_unused int helio_dvfsrc_resume(struct device *dev)
{
int ret = 0;
if (dvfsrc->resume) {
ret = dvfsrc->resume(dvfsrc);
if (ret)
return ret;
}
return ret;
}
static SIMPLE_DEV_PM_OPS(helio_dvfsrc_pm, helio_dvfsrc_suspend,
helio_dvfsrc_resume);
static struct platform_driver helio_dvfsrc_driver = {
.probe = helio_dvfsrc_probe,
.remove = helio_dvfsrc_remove,
.driver = {
.name = "helio-dvfsrc",
.pm = &helio_dvfsrc_pm,
.of_match_table = helio_dvfsrc_of_match,
},
};
static int __init helio_dvfsrc_init(void)
{
int ret = 0;
ret = platform_driver_register(&helio_dvfsrc_driver);
return ret;
}
late_initcall_sync(helio_dvfsrc_init)
static void __exit helio_dvfsrc_exit(void)
{
platform_driver_unregister(&helio_dvfsrc_driver);
}
module_exit(helio_dvfsrc_exit)
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Helio dvfsrc driver");
@@ -0,0 +1,129 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_QOS_H
#define __HELIO_DVFSRC_QOS_H
#include <linux/delay.h>
#include <linux/devfreq.h>
#include <linux/io.h>
#include "helio-dvfsrc-smc-control.h"
#define dvfsrc_wait_for_completion(condition, timeout) \
({ \
int ret = 0; \
if (is_dvfsrc_enabled()) \
ret = 1; \
while (!(condition) && ret > 0) { \
if (ret++ >= timeout) \
ret = -EBUSY; \
udelay(1); \
} \
ret; \
})
enum {
DVFSRC_QOS_RESERVED = 0,
DVFSRC_QOS_MEMORY_BANDWIDTH,
DVFSRC_QOS_CPU_MEMORY_BANDWIDTH,
DVFSRC_QOS_GPU_MEMORY_BANDWIDTH,
DVFSRC_QOS_MM_MEMORY_BANDWIDTH,
DVFSRC_QOS_OTHER_MEMORY_BANDWIDTH,
DVFSRC_QOS_DDR_OPP,
DVFSRC_QOS_VCORE_OPP,
DVFSRC_QOS_SCP_VCORE_REQUEST,
DVFSRC_QOS_POWER_MODEL_DDR_REQUEST,
DVFSRC_QOS_POWER_MODEL_VCORE_REQUEST,
DVFSRC_QOS_VCORE_DVFS_FORCE_OPP,
DVFSRC_QOS_ISP_HRT_BANDWIDTH,
DVFSRC_QOS_APU_MEMORY_BANDWIDTH,
/* insert new ID */
DVFSRC_QOS_NUM_CLASSES,
};
/* met profile table index */
enum met_info_index {
INFO_OPP_IDX = 0,
INFO_FREQ_IDX,
INFO_VCORE_IDX,
INFO_SPM_LEVEL_IDX,
INFO_MAX,
};
struct reg_config {
u32 offset;
u32 val;
};
struct helio_dvfsrc {
int irq;
struct device *dev;
bool qos_enabled;
bool dvfsrc_enabled;
int dvfsrc_flag;
void __iomem *regs;
void __iomem *spm_regs;
void __iomem *sram_regs;
struct notifier_block mtk_pm_qos_memory_bw_nb;
struct notifier_block mtk_pm_qos_cpu_memory_bw_nb;
struct notifier_block mtk_pm_qos_gpu_memory_bw_nb;
struct notifier_block mtk_pm_qos_mm_memory_bw_nb;
struct notifier_block mtk_pm_qos_other_memory_bw_nb;
struct notifier_block mtk_pm_qos_ddr_opp_nb;
struct notifier_block mtk_pm_qos_vcore_opp_nb;
struct notifier_block mtk_pm_qos_scp_vcore_request_nb;
struct notifier_block mtk_pm_qos_power_model_ddr_request_nb;
struct notifier_block mtk_pm_qos_power_model_vcore_request_nb;
struct notifier_block mtk_pm_qos_vcore_dvfs_force_opp_nb;
struct notifier_block mtk_pm_qos_isp_hrt_bw_nb;
struct notifier_block mtk_pm_qos_apu_memory_bw_nb;
struct reg_config *init_config;
bool opp_forced;
char force_start[20];
char force_end[20];
int (*suspend)(struct helio_dvfsrc *dvfsrc_dev);
int (*resume)(struct helio_dvfsrc *dvfsrc_dev);
};
extern int is_dvfsrc_enabled(void);
extern int is_dvfsrc_qos_enabled(void);
extern void helio_dvfsrc_qos_init_done(void);
extern void helio_dvfsrc_enable(int dvfsrc_en);
extern void helio_dvfsrc_flag_set(int flag);
extern int helio_dvfsrc_flag_get(void);
extern u32 dvfsrc_dump_reg(char *ptr, u32 count);
extern u32 dvfsrc_read(u32 offset);
extern u32 spm_reg_read(u32 offset);
extern void dvfsrc_write(u32 offset, u32 val);
extern void dvfsrc_opp_table_init(void);
extern int helio_dvfsrc_add_interface(struct device *dev);
extern void helio_dvfsrc_sspm_ipi_init(int dvfsrc_en);
extern void helio_dvfsrc_remove_interface(struct device *dev);
extern void dvfsrc_opp_level_mapping(void);
extern void get_opp_info(char *p);
extern void get_dvfsrc_reg(char *p);
extern void get_dvfsrc_record(char *p);
extern void vcorefs_trace_qos(void);
extern void helio_dvfsrc_platform_pre_init(struct helio_dvfsrc *dvfsrc);
extern int helio_dvfsrc_config(struct helio_dvfsrc *dvfsrc);
extern int commit_data(int type, int data, int check_spmfw);
extern void dvfsrc_enable_level_intr(int en);
extern void get_spm_reg(char *p);
extern int vcore_pmic_to_uv(int pmic_val);
extern int vcore_uv_to_pmic(int vcore_uv);
extern int helio_dvfsrc_level_mask_get(void);
extern int helio_dvfsrc_level_mask_set(bool en, int level);
extern void mtk_pm_qos_trace_dbg_show_request(int mtk_pm_qos_class);
extern void mtk_pm_qos_trace_dbg_dump(int mtk_pm_qos_class);
#endif /* __HELIO_DVFSRC_QOS_H */
@@ -0,0 +1,49 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include "helio-dvfsrc-smc-control.h"
#define SPM_FLAG_RUN_COMMON_SCENARIO (1U << 10)
#define SPM_FLAG_DIS_VCORE_DVS (1U << 3)
#define SPM_FLAG_DIS_VCORE_DFS (1U << 4)
inline void mtk_spmfw_init(int dvfsrc_en, int skip_check)
{
int dvfsrc_flag = dvfsrc_en >> 1;
unsigned int spm_flags = SPM_FLAG_RUN_COMMON_SCENARIO;
if (dvfsrc_en & 1) {
if (dvfsrc_flag & 0x1)
spm_flags |= SPM_FLAG_DIS_VCORE_DVS;
if (dvfsrc_flag & 0x2)
spm_flags |= SPM_FLAG_DIS_VCORE_DFS;
} else
spm_flags |= (
SPM_FLAG_DIS_VCORE_DVS | SPM_FLAG_DIS_VCORE_DFS);
if (helio_dvfsrc_firmware_status() &&
!skip_check)
return;
helio_dvfsrc_smc(VCOREFS_SMC_CMD_0, 0, 0, 0);
helio_dvfsrc_smc(VCOREFS_SMC_CMD_1, spm_flags, 0, 0);
}
inline void spm_dvfs_pwrap_cmd(int pwrap_cmd, int pwrap_vcore)
{
helio_dvfsrc_smc(VCOREFS_SMC_CMD_3, pwrap_cmd, pwrap_vcore, 0);
}
inline int spm_load_firmware_status(void)
{
return helio_dvfsrc_firmware_status();
}
inline int spm_get_spmfw_idx(void)
{
return helio_dvfsrc_spmfw_type();
}
@@ -0,0 +1,69 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_COMMON_H__
#define __HELIO_DVFSRC_COMMON_H__
#if defined(DVFSRC_SMC_CONTROL)
#include<linux/arm-smccc.h>
#include <mt-plat/mtk_secure_api.h>
enum VCORE_REG_STATUS {
DVFS_LV,
DVFS_FINAL_LV,
DVFS_R9,
DVFS_MD_STATUS,
DVFS_MD_SRCLKEN,
};
enum vcorefs_smc_cmd {
VCOREFS_SMC_CMD_0,
VCOREFS_SMC_CMD_1,
VCOREFS_SMC_CMD_2,
VCOREFS_SMC_CMD_3,
VCOREFS_SMC_CMD_4,
/* check spmfw status */
VCOREFS_SMC_CMD_5,
/* get spmfw type */
VCOREFS_SMC_CMD_6,
/* get spm reg status */
VCOREFS_SMC_CMD_7,
NUM_VCOREFS_SMC_CMD,
};
#define helio_dvfsrc_smc_impl(p1, p2, p3, p4, p5, res) \
arm_smccc_smc(p1, p2, p3, p4\
, p5, 0, 0, 0, &res)
#define helio_dvfsrc_smc(opid, p1, p2, p3) ({\
struct arm_smccc_res res;\
helio_dvfsrc_smc_impl(\
MTK_SIP_KERNEL_SPM_VCOREFS_ARGS,\
opid, p1, p2, p3, res);\
res.a0; })
#define helio_dvfsrc_spmfw_type()\
helio_dvfsrc_smc(VCOREFS_SMC_CMD_6, 0, 0, 0)
#define helio_dvfsrc_firmware_status()\
helio_dvfsrc_smc(VCOREFS_SMC_CMD_5, 0, 0, 0)
#define helio_dvfsrc_reg_status(offset)\
helio_dvfsrc_smc(VCOREFS_SMC_CMD_7, offset, 0, 0)
#endif
extern int spm_load_firmware_status(void);
extern void mtk_spmfw_init(int dvfsrc_en, int skip_check);
extern void spm_dvfs_pwrap_cmd(int pwrap_cmd, int pwrap_vcore);
extern int spm_get_spmfw_idx(void);
#endif
@@ -0,0 +1,366 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/soc/mediatek/mtk-pm-qos.h>
#include <linux/sysfs.h>
#include <linux/mutex.h>
#include <helio-dvfsrc-qos.h>
#include <helio-dvfsrc-opp.h>
#include "helio-dvfsrc-ip-v2.h"
static struct mtk_pm_qos_request dvfsrc_memory_bw_req;
static struct mtk_pm_qos_request dvfsrc_ddr_opp_req;
static struct mtk_pm_qos_request dvfsrc_vcore_opp_req;
static struct mtk_pm_qos_request dvfsrc_scp_vcore_req;
static struct mtk_pm_qos_request dvfsrc_power_model_ddr_req;
static struct mtk_pm_qos_request dvfsrc_power_model_vcore_req;
static struct mtk_pm_qos_request dvfsrc_vcore_dvfs_opp_force;
static struct mtk_pm_qos_request dvfsrc_isphrt_bw_req;
static DEFINE_MUTEX(dump_lock);
static ssize_t dvfsrc_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", is_dvfsrc_enabled());
}
static ssize_t dvfsrc_enable_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int val = 0;
if (kstrtoint(buf, 10, &val))
return -EINVAL;
helio_dvfsrc_enable(val);
return count;
}
static DEVICE_ATTR(dvfsrc_enable, 0644,
dvfsrc_enable_show, dvfsrc_enable_store);
static ssize_t dvfsrc_enable_flag_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%x\n", helio_dvfsrc_flag_get());
}
static ssize_t dvfsrc_enable_flag_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int val = 0;
if (kstrtoint(buf, 16, &val))
return -EINVAL;
helio_dvfsrc_flag_set(val);
return count;
}
static DEVICE_ATTR(dvfsrc_enable_flag, 0644,
dvfsrc_enable_flag_show, dvfsrc_enable_flag_store);
static ssize_t dvfsrc_req_memory_bw_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int val = 0;
if (kstrtoint(buf, 10, &val))
return -EINVAL;
mtk_pm_qos_update_request(&dvfsrc_memory_bw_req, val);
return count;
}
static DEVICE_ATTR(dvfsrc_req_memory_bw, 0200,
NULL, dvfsrc_req_memory_bw_store);
static ssize_t dvfsrc_req_isphrt_bw_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int val = 0;
if (kstrtoint(buf, 10, &val))
return -EINVAL;
mtk_pm_qos_update_request(&dvfsrc_isphrt_bw_req, val);
return count;
}
static DEVICE_ATTR(dvfsrc_req_isphrt_bw, 0200,
NULL, dvfsrc_req_isphrt_bw_store);
static ssize_t dvfsrc_req_ddr_opp_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int val = 0;
if (kstrtoint(buf, 10, &val))
return -EINVAL;
mtk_pm_qos_update_request(&dvfsrc_ddr_opp_req, val);
return count;
}
static DEVICE_ATTR(dvfsrc_req_ddr_opp, 0200,
NULL, dvfsrc_req_ddr_opp_store);
static ssize_t dvfsrc_req_vcore_opp_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int val = 0;
if (kstrtoint(buf, 10, &val))
return -EINVAL;
mtk_pm_qos_update_request(&dvfsrc_vcore_opp_req, val);
return count;
}
static DEVICE_ATTR(dvfsrc_req_vcore_opp, 0200,
NULL, dvfsrc_req_vcore_opp_store);
static ssize_t dvfsrc_req_scp_vcore_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int val = 0;
if (kstrtoint(buf, 10, &val))
return -EINVAL;
mtk_pm_qos_update_request(&dvfsrc_scp_vcore_req, val);
return count;
}
static DEVICE_ATTR(dvfsrc_req_scp_vcore, 0200,
NULL, dvfsrc_req_scp_vcore_store);
static ssize_t dvfsrc_req_power_model_ddr_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int val = 0;
if (kstrtoint(buf, 10, &val))
return -EINVAL;
mtk_pm_qos_update_request(&dvfsrc_power_model_ddr_req, val);
return count;
}
static DEVICE_ATTR(dvfsrc_req_power_model_ddr, 0200,
NULL, dvfsrc_req_power_model_ddr_store);
static ssize_t dvfsrc_req_power_model_vcore_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int val = 0;
if (kstrtoint(buf, 10, &val))
return -EINVAL;
mtk_pm_qos_update_request(&dvfsrc_power_model_vcore_req, val);
return count;
}
static DEVICE_ATTR(dvfsrc_req_power_model_vcore, 0200,
NULL, dvfsrc_req_power_model_vcore_store);
static ssize_t dvfsrc_set_vcore_uv_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int opp = 0, vcore_uv = 0;
if (sscanf(buf, "%d %d", &opp, &vcore_uv) != 2)
return -EINVAL;
set_vcore_uv_table(opp, vcore_uv);
dvfsrc_opp_table_init();
return count;
}
static DEVICE_ATTR(dvfsrc_set_vcore_uv, 0200,
NULL, dvfsrc_set_vcore_uv_store);
static ssize_t dvfsrc_force_vcore_dvfs_opp_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int val = 0;
if (kstrtoint(buf, 10, &val))
return -EINVAL;
mtk_pm_qos_update_request(&dvfsrc_vcore_dvfs_opp_force, val);
return count;
}
static DEVICE_ATTR(dvfsrc_force_vcore_dvfs_opp, 0200,
NULL, dvfsrc_force_vcore_dvfs_opp_store);
static ssize_t dvfsrc_opp_table_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct helio_dvfsrc *dvfsrc;
char *p = buf;
char *buff_end = p + PAGE_SIZE;
int i;
dvfsrc = dev_get_drvdata(dev);
if (!dvfsrc)
return sprintf(buf, "Failed to access dvfsrc\n");
mutex_lock(&dump_lock);
for (i = 0; i < VCORE_DVFS_OPP_NUM; i++) {
p += snprintf(p, buff_end - p, "[OPP%-2d]: %-8u uv %-8u khz\n",
i, get_vcore_uv(i), get_ddr_khz(i));
}
p += snprintf(p, buff_end - p, "\n");
mutex_unlock(&dump_lock);
return p - buf;
}
static DEVICE_ATTR(dvfsrc_opp_table, 0444, dvfsrc_opp_table_show, NULL);
static ssize_t dvfsrc_dump_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
char *p = buf;
u32 index;
index = dvfsrc_dump_reg(p, PAGE_SIZE);
return index;
}
static DEVICE_ATTR(dvfsrc_dump, 0444, dvfsrc_dump_show, NULL);
static ssize_t dvfsrc_level_intr_log_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return 0;
}
static ssize_t dvfsrc_level_intr_log_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int val = 0;
struct helio_dvfsrc *dvfsrc;
dvfsrc = dev_get_drvdata(dev);
if (kstrtoint(buf, 10, &val))
return -EINVAL;
dvfsrc_enable_level_intr(val);
return count;
}
static DEVICE_ATTR(dvfsrc_level_intr_log, 0644,
dvfsrc_level_intr_log_show, dvfsrc_level_intr_log_store);
static ssize_t dvfsrc_num_opps_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", VCORE_DVFS_OPP_NUM);
}
static DEVICE_ATTR(dvfsrc_num_opps, 0444, dvfsrc_num_opps_show, NULL);
static ssize_t dvfsrc_get_dvfs_time_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
u32 last;
u64 time_1, time_2;
u64 dvfs_time_us;
if (!(dvfsrc_read(DVFSRC_BASIC_CONTROL) & (1 << FORCE_EN_TAR_SHIFT)))
return sprintf(buf, "Not in force mode\n");
last = dvfsrc_read(DVFSRC_LAST);
time_1 = dvfsrc_read(DVFSRC_RECORD_0_1 + RECORD_SHIFT * last);
time_1 = time_1 << 32;
time_1 = dvfsrc_read(DVFSRC_RECORD_0_0 + RECORD_SHIFT * last) + time_1;
last = (last + 7) % 8;
time_2 = dvfsrc_read(DVFSRC_RECORD_0_1 + RECORD_SHIFT * last);
time_2 = time_2 << 32;
time_2 = dvfsrc_read(DVFSRC_RECORD_0_0 + RECORD_SHIFT * last) + time_2;
#if BITS_PER_LONG == 32
dvfs_time_us = div_u64((time_1 - time_2), 13);
#else
dvfs_time_us = (time_1 - time_2) / 13;
#endif
return sprintf(buf, "dvfs_time = %llu us\n", dvfs_time_us);
}
static DEVICE_ATTR(dvfsrc_get_dvfs_time, 0444,
dvfsrc_get_dvfs_time_show, NULL);
static struct attribute *helio_dvfsrc_attrs[] = {
&dev_attr_dvfsrc_enable.attr,
&dev_attr_dvfsrc_enable_flag.attr,
&dev_attr_dvfsrc_req_memory_bw.attr,
&dev_attr_dvfsrc_req_ddr_opp.attr,
&dev_attr_dvfsrc_req_vcore_opp.attr,
&dev_attr_dvfsrc_req_scp_vcore.attr,
&dev_attr_dvfsrc_req_power_model_ddr.attr,
&dev_attr_dvfsrc_req_power_model_vcore.attr,
&dev_attr_dvfsrc_force_vcore_dvfs_opp.attr,
&dev_attr_dvfsrc_set_vcore_uv.attr,
&dev_attr_dvfsrc_opp_table.attr,
&dev_attr_dvfsrc_dump.attr,
&dev_attr_dvfsrc_level_intr_log.attr,
&dev_attr_dvfsrc_req_isphrt_bw.attr,
&dev_attr_dvfsrc_num_opps.attr,
&dev_attr_dvfsrc_get_dvfs_time.attr,
NULL,
};
static struct attribute_group helio_dvfsrc_attr_group = {
.name = "helio-dvfsrc",
.attrs = helio_dvfsrc_attrs,
};
int helio_dvfsrc_add_interface(struct device *dev)
{
mtk_pm_qos_add_request(&dvfsrc_memory_bw_req, PM_QOS_APU_MEMORY_BANDWIDTH,
PM_QOS_APU_MEMORY_BANDWIDTH_DEFAULT_VALUE);
mtk_pm_qos_add_request(&dvfsrc_ddr_opp_req, MTK_PM_QOS_DDR_OPP,
0);
mtk_pm_qos_add_request(&dvfsrc_vcore_opp_req, MTK_PM_QOS_VCORE_OPP,
0);
mtk_pm_qos_add_request(&dvfsrc_scp_vcore_req, MTK_PM_QOS_SCP_VCORE_REQUEST,
MTK_PM_QOS_SCP_VCORE_REQUEST_DEFAULT_VALUE);
mtk_pm_qos_add_request(&dvfsrc_power_model_ddr_req,
MTK_PM_QOS_POWER_MODEL_DDR_REQUEST,
MTK_PM_QOS_POWER_MODEL_DDR_REQUEST_DEFAULT_VALUE);
mtk_pm_qos_add_request(&dvfsrc_power_model_vcore_req,
MTK_PM_QOS_POWER_MODEL_VCORE_REQUEST,
MTK_PM_QOS_POWER_MODEL_VCORE_REQUEST_DEFAULT_VALUE);
mtk_pm_qos_add_request(&dvfsrc_vcore_dvfs_opp_force,
MTK_PM_QOS_VCORE_DVFS_FORCE_OPP,
MTK_PM_QOS_VCORE_DVFS_FORCE_OPP_DEFAULT_VALUE);
mtk_pm_qos_add_request(&dvfsrc_isphrt_bw_req,
MTK_PM_QOS_ISP_HRT_BANDWIDTH,
PM_QOS_ISP_HRT_BANDWIDTH_DEFAULT_VALUE);
return sysfs_create_group(&dev->kobj, &helio_dvfsrc_attr_group);
}
void helio_dvfsrc_qos_init_done(void)
{
mtk_pm_qos_update_request(&dvfsrc_vcore_opp_req, MTK_PM_QOS_VCORE_OPP_DEFAULT_VALUE);
mtk_pm_qos_update_request(&dvfsrc_ddr_opp_req, MTK_PM_QOS_DDR_OPP_DEFAULT_VALUE);
}
void helio_dvfsrc_remove_interface(struct device *dev)
{
sysfs_remove_group(&dev->kobj, &helio_dvfsrc_attr_group);
}
@@ -0,0 +1,810 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/platform_device.h>
#ifdef CONFIG_MEDIATEK_DRAMC
#include <dramc.h>
#endif
#ifdef CONFIG_MTK_DRAMC
#include <mtk_dramc.h>
#endif
#ifdef CONFIG_MTK_EMI
#include <mt_emi_api.h>
#endif
#include <mt-plat/upmu_common.h>
#include "helio-dvfsrc-ip-v2.h"
#include <helio-dvfsrc-opp.h>
#include <helio-dvfsrc-v6781.h>
#include <mt-plat/mtk_devinfo.h>
#include <linux/regulator/consumer.h>
#include "mmdvfs_pmqos.h"
#include <linux/sysfs.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/sched/clock.h>
#ifdef CONFIG_MTK_WATCHDOG
#include <ext_wd_drv.h>
#endif
#define dvfsrc_rmw(offset, val, mask, shift) \
dvfsrc_write(offset, (dvfsrc_read(offset) & ~(mask << shift)) \
| (val << shift))
#define DVFSRC_1600_FLOOR
#if defined(CONFIG_MTK_DVFSRC_MT6781_PRETEST)
static struct reg_config dvfsrc_init_configs[][128] = {
{
{ DVFSRC_TIMEOUT_NEXTREQ, 0x0000002B },
{ DVFSRC_INT_EN, 0x00000002 },
{ DVFSRC_QOS_EN, 0x0000407C },
{ DVFSRC_VCORE_REQUEST4, 0x21110000 },
#ifdef DVFSRC_1600_FLOOR
{ DVFSRC_DDR_REQUEST, 0x00004322 },
#else
{ DVFSRC_DDR_REQUEST, 0x00004321 },
#endif
{ DVFSRC_DDR_REQUEST3, 0x00000055 },
#ifdef DVFSRC_1600_FLOOR
{ DVFSRC_DDR_REQUEST5, 0x00322000 },
#else
{ DVFSRC_DDR_REQUEST5, 0x00321000 },
#endif
{ DVFSRC_DDR_REQUEST7, 0x54000000 },
{ DVFSRC_DDR_REQUEST8, 0x55000000 },
{ DVFSRC_DDR_QOS0, 0x00000019 },
{ DVFSRC_DDR_QOS1, 0x00000026 },
{ DVFSRC_DDR_QOS2, 0x00000033 },
{ DVFSRC_DDR_QOS3, 0x0000004C },
{ DVFSRC_DDR_QOS4, 0x00000066 },
{ DVFSRC_DDR_QOS5, 0x00000077 },
{ DVFSRC_DDR_QOS6, 0x00000077 },
{ DVFSRC_95MD_SCEN_BW0_T, 0x22222220 },
{ DVFSRC_95MD_SCEN_BW1_T, 0x22222222 },
{ DVFSRC_95MD_SCEN_BW2_T, 0x22222222 },
{ DVFSRC_95MD_SCEN_BW3_T, 0x52222222 },
{ DVFSRC_95MD_SCEN_BW4, 0x00000005 },
{ DVFSRC_HRT_REQ_UNIT, 0x0000011E },
{ DVSFRC_HRT_REQ_MD_URG, 0x0000D3D3 },
{ DVFSRC_HRT_REQ_MD_BW_0, 0x00200802 },
{ DVFSRC_HRT_REQ_MD_BW_1, 0x00200800 },
{ DVFSRC_HRT_REQ_MD_BW_2, 0x00200002 },
{ DVFSRC_HRT_REQ_MD_BW_3, 0x00200802 },
{ DVFSRC_HRT_REQ_MD_BW_4, 0x00400802 },
{ DVFSRC_HRT_REQ_MD_BW_5, 0x00601404 },
{ DVFSRC_HRT_REQ_MD_BW_6, 0x00902008 },
{ DVFSRC_HRT_REQ_MD_BW_7, 0x00E0380E },
{ DVFSRC_HRT_REQ_MD_BW_8, 0x00000000 },
{ DVFSRC_HRT_REQ_MD_BW_9, 0x00000000 },
{ DVFSRC_HRT_REQ_MD_BW_10, 0x00034C00 },
{ DVFSRC_HRT1_REQ_MD_BW_0, 0x0360D836 },
{ DVFSRC_HRT1_REQ_MD_BW_1, 0x0360D800 },
{ DVFSRC_HRT1_REQ_MD_BW_2, 0x03600036 },
{ DVFSRC_HRT1_REQ_MD_BW_3, 0x0360D836 },
{ DVFSRC_HRT1_REQ_MD_BW_4, 0x0360D836 },
{ DVFSRC_HRT1_REQ_MD_BW_5, 0x0360D836 },
{ DVFSRC_HRT1_REQ_MD_BW_6, 0x0360D836 },
{ DVFSRC_HRT1_REQ_MD_BW_7, 0x0360D836 },
{ DVFSRC_HRT1_REQ_MD_BW_8, 0x00000000 },
{ DVFSRC_HRT1_REQ_MD_BW_9, 0x00000000 },
{ DVFSRC_HRT1_REQ_MD_BW_10, 0x00034C00 },
{ DVFSRC_HRT_HIGH, 0x070804B0 },
{ DVFSRC_HRT_HIGH_1, 0x11830B80 },
{ DVFSRC_HRT_HIGH_2, 0x18A618A6 },
{ DVFSRC_HRT_HIGH_3, 0x000018A6 },
{ DVFSRC_HRT_LOW, 0x070704AF },
{ DVFSRC_HRT_LOW_1, 0x11820B7F },
{ DVFSRC_HRT_LOW_2, 0x18A518A5 },
{ DVFSRC_HRT_LOW_3, 0x000018A5 },
#ifdef DVFSRC_1600_FLOOR
{ DVFSRC_HRT_REQUEST, 0x05554322 },
#else
{ DVFSRC_HRT_REQUEST, 0x05554321 },
#endif
{ DVFSRC_EMI_MON_DEBOUNCE_TIME, 0x4C4C0AB0 },
{ DVFSRC_DDR_ADD_REQUEST, 0x05543210 },
{ DVFSRC_EMI_ADD_REQUEST, 0x03333210 },
{ DVFSRC_LEVEL_LABEL_0_1, 0x40225032 },
{ DVFSRC_LEVEL_LABEL_2_3, 0x20223012 },
{ DVFSRC_LEVEL_LABEL_4_5, 0x40211012 },
{ DVFSRC_LEVEL_LABEL_6_7, 0x20213011 },
{ DVFSRC_LEVEL_LABEL_8_9, 0x30101011 },
{ DVFSRC_LEVEL_LABEL_10_11, 0x10102000 },
{ DVFSRC_LEVEL_LABEL_12_13, 0x00000000 },
{ DVFSRC_LEVEL_LABEL_14_15, 0x00000000 },
{ DVFSRC_CURRENT_FORCE, 0x00000001 },
{ DVFSRC_BASIC_CONTROL, 0x7599404B },
{ DVFSRC_BASIC_CONTROL, 0x7599014B },
{ DVFSRC_CURRENT_FORCE, 0x00000000 },
{ -1, 0 },
},
/* NULL */
{
{ -1, 0 },
},
};
#else
static struct reg_config dvfsrc_init_configs[][128] = {
{
{ DVFSRC_HRT_REQ_UNIT, 0x0000001E },
{ DVFSRC_DEBOUNCE_TIME, 0x00001965 },
{ DVFSRC_TIMEOUT_NEXTREQ, 0x0000002B },
{ DVFSRC_VCORE_REQUEST4, 0x21110000 },
{ DVFSRC_DDR_QOS0, 0x00000019 },
{ DVFSRC_DDR_QOS1, 0x00000026 },
{ DVFSRC_DDR_QOS2, 0x00000033 },
{ DVFSRC_DDR_QOS3, 0x0000004C },
{ DVFSRC_DDR_QOS4, 0x00000066 },
{ DVFSRC_DDR_QOS5, 0x00000077 },
{ DVFSRC_DDR_QOS6, 0x00000077 },
{ DVFSRC_LEVEL_LABEL_0_1, 0x40225032 },
{ DVFSRC_LEVEL_LABEL_2_3, 0x20223012 },
{ DVFSRC_LEVEL_LABEL_4_5, 0x40211012 },
{ DVFSRC_LEVEL_LABEL_6_7, 0x20213011 },
{ DVFSRC_LEVEL_LABEL_8_9, 0x30101011 },
{ DVFSRC_LEVEL_LABEL_10_11, 0x10102000 },
{ DVFSRC_LEVEL_LABEL_12_13, 0x00000000 },
{ DVFSRC_LEVEL_LABEL_14_15, 0x00000000 },
{ DVFSRC_MD_LATENCY_IMPROVE, 0x00000040 },
{ DVFSRC_HRT_BW_BASE, 0x00000004 },
{ DVSFRC_HRT_REQ_MD_URG, 0x0000D3D3 },
{ DVFSRC_HRT_REQ_MD_BW_0, 0x00501405 },
{ DVFSRC_HRT_REQ_MD_BW_1, 0x00501400 },
{ DVFSRC_HRT_REQ_MD_BW_2, 0x00500005 },
{ DVFSRC_HRT_REQ_MD_BW_3, 0x00601405 },
{ DVFSRC_HRT_REQ_MD_BW_4, 0x00B01806 },
{ DVFSRC_HRT_REQ_MD_BW_5, 0x00F0300C },
{ DVFSRC_HRT_REQ_MD_BW_6, 0x00000014 },
{ DVFSRC_HRT_REQ_MD_BW_7, 0x00000000 },
{ DVFSRC_HRT_REQ_MD_BW_8, 0x00000000 },
{ DVFSRC_HRT_REQ_MD_BW_9, 0x00000000 },
{ DVFSRC_HRT_REQ_MD_BW_10, 0x00034C00 },
{ DVFSRC_HRT1_REQ_MD_BW_0, 0x03D0F43D },
{ DVFSRC_HRT1_REQ_MD_BW_1, 0x03D0F400 },
{ DVFSRC_HRT1_REQ_MD_BW_2, 0x03D0003D },
{ DVFSRC_HRT1_REQ_MD_BW_3, 0x03D0F43D },
{ DVFSRC_HRT1_REQ_MD_BW_4, 0x03D0F43D },
{ DVFSRC_HRT1_REQ_MD_BW_5, 0x03D0F43D },
{ DVFSRC_HRT1_REQ_MD_BW_6, 0x0000003D },
{ DVFSRC_HRT1_REQ_MD_BW_7, 0x00000000 },
{ DVFSRC_HRT1_REQ_MD_BW_8, 0x00000000 },
{ DVFSRC_HRT1_REQ_MD_BW_9, 0x00000000 },
{ DVFSRC_HRT1_REQ_MD_BW_10, 0x00034C00 },
{ DVFSRC_95MD_SCEN_BW0_T, 0x22222220 },
{ DVFSRC_95MD_SCEN_BW1_T, 0x22222222 },
{ DVFSRC_95MD_SCEN_BW2_T, 0x22222222 },
{ DVFSRC_95MD_SCEN_BW3_T, 0x52222222 },
{ DVFSRC_95MD_SCEN_BW4, 0x00000005 },
{ DVFSRC_RSRV_5, 0x00000001 },
#ifdef DVFSRC_1600_FLOOR
{ DVFSRC_DDR_REQUEST, 0x00000022 },
#else
{ DVFSRC_DDR_REQUEST, 0x00000021 },
#endif
{ DVFSRC_DDR_REQUEST3, 0x00654300 },
{ DVFSRC_DDR_ADD_REQUEST, 0x66543210 },
#ifdef DVFSRC_1600_FLOOR
{ DVFSRC_DDR_REQUEST5, 0x54322000 },
#else
{ DVFSRC_DDR_REQUEST5, 0x54321000 },
#endif
{ DVFSRC_DDR_REQUEST7, 0x55000000 },
{ DVFSRC_EMI_MON_DEBOUNCE_TIME, 0x4C2D0000 },
{ DVFSRC_VCORE_USER_REQ, 0x00010A29 },
{ DVFSRC_HRT_HIGH, 0x070804B0 },
{ DVFSRC_HRT_HIGH_1, 0x11830B80 },
{ DVFSRC_HRT_HIGH_2, 0x18A618A6 },
{ DVFSRC_HRT_HIGH_3, 0x18A618A6 },
{ DVFSRC_HRT_LOW, 0x070704AF },
{ DVFSRC_HRT_LOW_1, 0x11820B7F },
{ DVFSRC_HRT_LOW_2, 0x18A518A5 },
{ DVFSRC_HRT_LOW_3, 0x18A518A5 },
#ifdef DVFSRC_1600_FLOOR
{ DVFSRC_HRT_REQUEST, 0x55554322 },
#else
{ DVFSRC_HRT_REQUEST, 0x55554321 },
#endif
{ DVFSRC_BASIC_CONTROL_3, 0x00000006 },
{ DVFSRC_INT_EN, 0x00000002 },
{ DVFSRC_QOS_EN, 0x0000407C },
{ DVFSRC_CURRENT_FORCE, 0x00000001 },
{ DVFSRC_BASIC_CONTROL, 0x6598444B },
{ DVFSRC_BASIC_CONTROL, 0x6598054B },
{ DVFSRC_CURRENT_FORCE, 0x00000000 },
{ -1, 0 },
},
/* NULL */
{
{ -1, 0 },
},
};
#endif
static ssize_t dvfsrc_vcore_settle_time_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
/* DE's comment: settle time was hard code in fw (15,30) */
return sprintf(buf, "rising 15 uS, falling 30 uS for mt6781\n");
}
static DEVICE_ATTR(dvfsrc_vcore_settle_time, 0444,
dvfsrc_vcore_settle_time_show, NULL);
static struct attribute *mt6781_helio_dvfsrc_attrs[] = {
&dev_attr_dvfsrc_vcore_settle_time.attr,
NULL,
};
static struct attribute_group mt6781_helio_dvfsrc_attr_group = {
.name = "helio-dvfsrc",
.attrs = mt6781_helio_dvfsrc_attrs,
};
#define dvfsrc_rmw(offset, val, mask, shift) \
dvfsrc_write(offset, (dvfsrc_read(offset) & ~(mask << shift)) \
| (val << shift))
u32 dvfsrc_get_ddr_qos(void)
{
unsigned int qos_total_bw = dvfsrc_read(DVFSRC_SW_BW_0) +
dvfsrc_read(DVFSRC_SW_BW_1) +
dvfsrc_read(DVFSRC_SW_BW_2) +
dvfsrc_read(DVFSRC_SW_BW_3) +
dvfsrc_read(DVFSRC_SW_BW_4);
if (qos_total_bw < 0x19)
return 0;
#ifdef DVFSRC_1600_FLOOR
else if (qos_total_bw < 0x26)
return 2;
#else
else if (qos_total_bw < 0x26)
return 1;
#endif
else if (qos_total_bw < 0x33)
return 2;
else if (qos_total_bw < 0x4c)
return 3;
else if (qos_total_bw < 0x66)
return 4;
else
return 5;
}
static int dvfsrc_get_emi_mon_gear(void)
{
unsigned int val, total_bw_status;
int i;
val = vcorefs_get_total_emi_status();
total_bw_status = (val & 0x3) | ((val & 0x3C0) >> 4);
for (i = 4; i >= 0 ; i--) {
if ((total_bw_status >> i) > 0) {
#ifdef DVFSRC_1600_FLOOR
if (i == 0)
return i + 2;
else
return i + 1;
#else
return i + 1;
#endif
}
}
return 0;
}
static u32 dvfsrc_calc_hrt_opp(int data)
{
if (data < 0x04B0)
return DDR_OPP_5;
#ifdef DVFSRC_1600_FLOOR
else if (data < 0x0708)
return DDR_OPP_3;
#else
else if (data < 0x0708)
return DDR_OPP_4;
#endif
else if (data < 0x0B80)
return DDR_OPP_3;
else if (data < 0x1183)
return DDR_OPP_2;
else if (data < 0x18A6)
return DDR_OPP_1;
else
return DDR_OPP_0;
}
void dvfsrc_set_isp_hrt_bw(int data)
{
data = (data + 29) / 30;
if (data > 0x3FF)
data = 0x3FF;
dvfsrc_write(DVFSRC_ISP_HRT, data);
}
u32 dvfsrc_calc_isp_hrt_opp(int data)
{
return dvfsrc_calc_hrt_opp(((data + 29) / 30) * 30);
}
struct regulator *dvfsrc_vcore_requlator(struct device *dev)
{
return regulator_get(dev, "vcore");
}
#ifdef AUTOK_ENABLE
__weak int emmc_autok(void)
{
pr_info("NOT SUPPORT EMMC AUTOK\n");
return 0;
}
__weak int sd_autok(void)
{
pr_info("NOT SUPPORT SD AUTOK\n");
return 0;
}
__weak int sdio_autok(void)
{
pr_info("NOT SUPPORT SDIO AUTOK\n");
return 0;
}
void begin_autok_task(void)
{
/* notify MM DVFS for msdc autok start */
mmdvfs_prepare_action(MMDVFS_PREPARE_CALIBRATION_START);
}
void finish_autok_task(void)
{
/* check if dvfs force is released */
int force = mtk_pm_qos_request(MTK_PM_QOS_VCORE_DVFS_FORCE_OPP);
/* notify MM DVFS for msdc autok finish */
mmdvfs_prepare_action(MMDVFS_PREPARE_CALIBRATION_END);
if (force >= 0 && force < VCORE_DVFS_OPP_NUM)
pr_info("autok task not release force opp: %d\n", force);
}
static void dvfsrc_autok_manager(void)
{
int r = 0;
begin_autok_task();
r = emmc_autok();
pr_info("EMMC autok done: %s\n", (r == 0) ? "Yes" : "No");
r = sd_autok();
pr_info("SD autok done: %s\n", (r == 0) ? "Yes" : "No");
r = sdio_autok();
pr_info("SDIO autok done: %s\n", (r == 0) ? "Yes" : "No");
finish_autok_task();
}
#endif
void helio_dvfsrc_platform_pre_init(struct helio_dvfsrc *dvfsrc)
{
#if !defined(CONFIG_MTK_DVFSRC_MT6781_PRETEST)
struct platform_device *pdev = to_platform_device(dvfsrc->dev);
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
dvfsrc->spm_regs = devm_ioremap(&pdev->dev,
res->start, resource_size(res));
if (IS_ERR(dvfsrc->spm_regs))
pr_info("not get spm register\n");
#endif
}
__weak void mtk_pm_qos_trace_dbg_dump(int mtk_pm_qos_class)
{
}
void dvfsrc_suspend_cb(struct helio_dvfsrc *dvfsrc)
{
int sw_req;
sw_req = dvfsrc_read(DVFSRC_SW_REQ3);
pr_info("[DVFSRC] V:%d, F_OPP:%d, RG:%08x, %08x, %08x, %08x\n",
get_cur_vcore_uv(),
mtk_pm_qos_request(MTK_PM_QOS_VCORE_DVFS_FORCE_OPP),
dvfsrc_read(DVFSRC_CURRENT_LEVEL),
dvfsrc_read(DVFSRC_SW_REQ2),
sw_req,
dvfsrc_read(DVFSRC_DEBUG_STA_0));
if (sw_req & (DDR_SW_AP_MASK << DDR_SW_AP_SHIFT))
mtk_pm_qos_trace_dbg_dump(MTK_PM_QOS_DDR_OPP);
if (sw_req & (VCORE_SW_AP_MASK << VCORE_SW_AP_SHIFT))
mtk_pm_qos_trace_dbg_dump(MTK_PM_QOS_VCORE_OPP);
}
void dvfsrc_resume_cb(struct helio_dvfsrc *dvfsrc)
{
}
void helio_dvfsrc_platform_init(struct helio_dvfsrc *dvfsrc)
{
int spmfw_idx = 0;
struct reg_config *config;
int idx = 0;
sysfs_merge_group(&dvfsrc->dev->kobj, &mt6781_helio_dvfsrc_attr_group);
config = dvfsrc_init_configs[spmfw_idx];
while (config[idx].offset != -1) {
dvfsrc_write(config[idx].offset, config[idx].val);
idx++;
}
#ifdef AUTOK_ENABLE
dvfsrc_autok_manager();
#endif
}
int vcore_pmic_to_uv(int pmic_val)
{
return __vcore_pmic_to_uv(pmic_val);
}
int vcore_uv_to_pmic(int vcore_uv)
{
return __vcore_uv_to_pmic(vcore_uv);
}
#if !defined(CONFIG_MTK_DVFSRC_MT6781_PRETEST)
void get_spm_reg(char *p)
{
p += sprintf(p, "%-24s: 0x%08x\n",
"POWERON_CONFIG_EN",
spm_reg_read(POWERON_CONFIG_EN));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_SW_FLAG_0",
spm_reg_read(SPM_SW_FLAG_0));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_PC_STA",
spm_reg_read(SPM_PC_STA));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVFS_LEVEL",
spm_reg_read(SPM_DVFS_LEVEL));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVS_DFS_LEVEL",
spm_reg_read(SPM_DVS_DFS_LEVEL));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVFS_STA",
spm_reg_read(SPM_DVFS_STA));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVFS_MISC",
spm_reg_read(SPM_DVFS_MISC));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
"SPM_DVFS_CMD0~4",
spm_reg_read(SPM_DVFS_CMD0),
spm_reg_read(SPM_DVFS_CMD1),
spm_reg_read(SPM_DVFS_CMD2),
spm_reg_read(SPM_DVFS_CMD3),
spm_reg_read(SPM_DVFS_CMD4));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x\n",
"SPM_DVFS_CMD7~8",
spm_reg_read(SPM_DVFS_CMD7),
spm_reg_read(SPM_DVFS_CMD8));
}
#endif
void get_opp_info(char *p)
{
#if defined(CONFIG_FPGA_EARLY_PORTING) || !defined(CONFIG_MTK_PMIC_COMMON) \
|| !defined(CONFIG_MTK_PMIC_NEW_ARCH)
int pmic_val = 0;
int vsram_val = 0;
#else
int pmic_val = pmic_get_register_value(PMIC_VCORE_ADDR);
int vsram_val = pmic_get_register_value(PMIC_VSRAM_OTHERS_ADDR);
#endif
#if defined(CONFIG_MTK_DRAMC)
int ddr_khz = get_dram_data_rate() * 1000;
#elif defined(CONFIG_MEDIATEK_DRAMC)
int ddr_khz = mtk_dramc_get_data_rate() * 1000;
#else
int ddr_khz = 0;
#endif
int vcore_uv = vcore_pmic_to_uv(pmic_val);
int vsram_uv = vsram_pmic_to_uv(vsram_val);
p += sprintf(p, "%-10s: %-8u uv (PMIC: 0x%x)\n",
"Vcore", vcore_uv, vcore_uv_to_pmic(vcore_uv));
p += sprintf(p, "%-10s: %-8u uv (PMIC: 0x%x)\n",
"Vsram", vsram_uv, vsram_uv_to_pmic(vsram_uv));
p += sprintf(p, "%-10s: %-8u khz\n", "DDR", ddr_khz);
}
/* met profile table */
static unsigned int met_vcorefs_src[SRC_MAX];
static char *met_src_name[SRC_MAX] = {
"MD2SPM",
"SRC_DDR_OPP",
"DDR__SW_REQ1_SPM",
"DDR__SW_REQ2_CM",
"DDR__SW_REQ3_PMQOS",
#if defined(CONFIG_MTK_DVFSRC_MT6781_PRETEST)
"DDR__SW_REQ4_MD",
"DDR__SW_REQ8_MCUSYS",
#endif
"DDR__QOS_BW",
"DDR__EMI_TOTAL",
"DDR__HRT_BW",
"DDR__HIFI",
"DDR__HIFI_LATENCY",
"DDR__MD_LATENCY",
"DDR__MD_DDR",
"DDR__MD_LEVEL_MASK",
"SRC_VCORE_OPP",
"VCORE__SW_REQ3_PMQOS",
"VCORE__SCP",
"VCORE__HIFI",
"SCP_REQ",
"PMQOS_TATOL",
"PMQOS_BW0",
"PMQOS_BW1",
"PMQOS_BW2",
"PMQOS_BW3",
"PMQOS_BW4",
"TOTAL_EMI_BW",
"HRT_MD_BW",
"HRT_DISP_BW",
"HRT_ISP_BW",
"MD_SCENARIO",
"HIFI_SCENARIO_IDX",
"MD_EMI_LATENCY",
};
/* met profile function */
int vcorefs_get_src_req_num(void)
{
return SRC_MAX;
}
EXPORT_SYMBOL(vcorefs_get_src_req_num);
char **vcorefs_get_src_req_name(void)
{
return met_src_name;
}
EXPORT_SYMBOL(vcorefs_get_src_req_name);
static u32 vcorefs_get_md_level_mask_ddr(void)
{
#if !defined(CONFIG_MTK_DVFSRC_MT6781_PRETEST)
int md_srclk, vopp;
if (dvfsrc_read(DVFSRC_BASIC_CONTROL_3) & 0x8) {
md_srclk = dvfsrc_read(DVFSRC_DEBUG_STA_0);
vopp = get_cur_vcore_opp();
md_srclk = (md_srclk >> MD_SRC_CLK_DEBUG_SHIFT)
& MD_SRC_CLK_DEBUG_MASK;
if (vopp != 3 && md_srclk == 1)
return 2;
}
#endif
return 0;
}
static void vcorefs_get_src_ddr_req(void)
{
unsigned int sw_req;
met_vcorefs_src[DDR_OPP_IDX] =
get_cur_ddr_opp();
sw_req = dvfsrc_read(DVFSRC_SW_REQ1);
met_vcorefs_src[DDR_SW_REQ1_SPM_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
sw_req = dvfsrc_read(DVFSRC_SW_REQ2);
met_vcorefs_src[DDR_SW_REQ2_CM_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
sw_req = dvfsrc_read(DVFSRC_SW_REQ3);
met_vcorefs_src[DDR_SW_REQ3_PMQOS_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
#if defined(CONFIG_MTK_DVFSRC_MT6781_PRETEST)
sw_req = dvfsrc_read(DVFSRC_SW_REQ4);
met_vcorefs_src[DDR_SW_REQ4_MD_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
sw_req = dvfsrc_read(DVFSRC_SW_REQ8);
met_vcorefs_src[DDR_SW_REQ8_MCUSYS_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
#endif
met_vcorefs_src[DDR_QOS_BW_IDX] =
dvfsrc_get_ddr_qos();
met_vcorefs_src[DDR_EMI_TOTAL_IDX] =
dvfsrc_get_emi_mon_gear();
met_vcorefs_src[DDR_HRT_BW_IDX] =
vcorefs_get_hrt_bw_ddr();
met_vcorefs_src[DDR_HIFI_IDX] =
vcorefs_get_hifi_ddr_status();
met_vcorefs_src[DDR_HIFI_LATENCY_IDX] =
vcorefs_get_hifi_rising_ddr();
#if defined(CONFIG_MTK_DVFSRC_MT6781_PRETEST)
met_vcorefs_src[DDR_MD_LATENCY_IDX] =
vcorefs_get_md_rising_ddr();
#else
met_vcorefs_src[DDR_MD_LATENCY_IDX] =
vcorefs_get_md_imp_ddr();
#endif
met_vcorefs_src[DDR_MD_DDR_IDX] =
vcorefs_get_md_scenario_ddr();
met_vcorefs_src[DDR_MD_SRCLK_IDX] =
vcorefs_get_md_level_mask_ddr();
}
static void vcorefs_get_src_vcore_req(void)
{
u32 sw_req;
u32 scp_en;
scp_en = vcorefs_get_scp_req_status();
met_vcorefs_src[VCORE_OPP_IDX] =
get_cur_vcore_opp();
sw_req = dvfsrc_read(DVFSRC_SW_REQ3);
met_vcorefs_src[VCORE_SW_REQ3_PMQOS_IDX] =
(sw_req >> VCORE_SW_AP_SHIFT) & VCORE_SW_AP_MASK;
if (scp_en) {
sw_req = dvfsrc_read(DVFSRC_VCORE_REQUEST);
met_vcorefs_src[VCORE_SCP_IDX] =
(sw_req >> VCORE_SCP_GEAR_SHIFT) & VCORE_SCP_GEAR_MASK;
} else
met_vcorefs_src[VCORE_SCP_IDX] = 0;
met_vcorefs_src[VCORE_HIFI_IDX] =
vcorefs_get_hifi_vcore_status();
}
static void vcorefs_get_src_misc_info(void)
{
u32 qos_bw0, qos_bw1, qos_bw2, qos_bw3, qos_bw4;
#if defined(CONFIG_MTK_DVFSRC_MT6781_PRETEST)
#ifdef CONFIG_MTK_EMI
unsigned int total_bw_last = (get_emi_bwvl(0) & 0x7F) * 813;
met_vcorefs_src[SRC_TOTAL_EMI_BW_IDX] =
total_bw_last;
met_vcorefs_src[SRC_MD2SPM_IDX] =
vcorefs_get_md_scenario();
#endif
#else
met_vcorefs_src[SRC_MD2SPM_IDX] =
vcorefs_get_md_scenario() & 0x1FFFF;
#endif
qos_bw0 = dvfsrc_read(DVFSRC_SW_BW_0);
qos_bw1 = dvfsrc_read(DVFSRC_SW_BW_1);
qos_bw2 = dvfsrc_read(DVFSRC_SW_BW_2);
qos_bw3 = dvfsrc_read(DVFSRC_SW_BW_3);
qos_bw4 = dvfsrc_read(DVFSRC_SW_BW_4);
met_vcorefs_src[SRC_SCP_REQ_IDX] =
vcorefs_get_scp_req_status();
met_vcorefs_src[SRC_PMQOS_TATOL_IDX] =
qos_bw0 + qos_bw1 + qos_bw2 + qos_bw3 + qos_bw4;
met_vcorefs_src[SRC_PMQOS_BW0_IDX] =
qos_bw0;
met_vcorefs_src[SRC_PMQOS_BW1_IDX] =
qos_bw1;
met_vcorefs_src[SRC_PMQOS_BW2_IDX] =
qos_bw2;
met_vcorefs_src[SRC_PMQOS_BW3_IDX] =
qos_bw3;
met_vcorefs_src[SRC_PMQOS_BW4_IDX] =
qos_bw4;
met_vcorefs_src[SRC_HRT_MD_BW_IDX] =
dvfsrc_get_md_bw();
met_vcorefs_src[SRC_HRT_ISP_BW_IDX] =
dvfsrc_read(DVFSRC_ISP_HRT);
met_vcorefs_src[SRC_MD_SCENARIO_IDX] =
vcorefs_get_md_scenario();
met_vcorefs_src[SRC_HIFI_SCENARIO_IDX] =
vcorefs_get_hifi_scenario();
met_vcorefs_src[SRC_MD_EMI_LATENCY_IDX] =
vcorefs_get_md_emi_latency_status();
}
int dvfsrc_latch_register(int enable)
{
#ifdef CONFIG_MTK_WATCHDOG
return mtk_rgu_cfg_dvfsrc(enable);
#endif
return 0;
}
unsigned int *vcorefs_get_src_req(void)
{
vcorefs_get_src_ddr_req();
vcorefs_get_src_vcore_req();
vcorefs_get_src_misc_info();
vcorefs_trace_qos();
return met_vcorefs_src;
}
EXPORT_SYMBOL(vcorefs_get_src_req);
int get_cur_ddr_ratio(void)
{
int idx;
if (!is_dvfsrc_enabled())
return 0;
idx = get_cur_vcore_dvfs_opp();
if (idx >= VCORE_DVFS_OPP_NUM)
return 0;
if ((get_ddr_opp(idx) < DDR_OPP_3) || (idx == 10))
return 8;
else
return 4;
}
EXPORT_SYMBOL(get_cur_ddr_ratio);
@@ -0,0 +1,81 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_V6781_H
#define __HELIO_DVFSRC_V6781_H
#include <mach/upmu_hw.h>
#if defined(CONFIG_MTK_DVFSRC_MT6781_PRETEST)
#define PMIC_VCORE_ADDR PMIC_RG_BUCK_VCORE_VOSEL
#define VCORE_BASE_UV 400000
#define VCORE_STEP_UV 6250
#else
#define PMIC_VCORE_ADDR PMIC_RG_BUCK_VCORE_VOSEL
#define VCORE_BASE_UV 500000
#define VCORE_STEP_UV 6250
#endif
#define PMIC_VSRAM_OTHERS_ADDR PMIC_RG_LDO_VSRAM_OTHERS_VOSEL
#define VSRAM_BASE_UV 500000
#define VSRAM_STEP_UV 6250
/* PMIC */
#define __vcore_pmic_to_uv(pmic) \
(((pmic) * VCORE_STEP_UV) + VCORE_BASE_UV)
#define __vcore_uv_to_pmic(uv) /* pmic >= uv */ \
((((uv) - VCORE_BASE_UV) + (VCORE_STEP_UV - 1)) / VCORE_STEP_UV)
/* VSRAM_OTHERS */
#define vsram_pmic_to_uv(pmic) \
(((pmic) * VSRAM_STEP_UV) + VSRAM_BASE_UV)
#define vsram_uv_to_pmic(uv) /* pmic >= uv */ \
((((uv) - VSRAM_BASE_UV) + (VSRAM_STEP_UV - 1)) / VSRAM_STEP_UV)
enum met_src_index {
SRC_MD2SPM_IDX = 0,
DDR_OPP_IDX,
DDR_SW_REQ1_SPM_IDX,
DDR_SW_REQ2_CM_IDX,
DDR_SW_REQ3_PMQOS_IDX,
#if defined(CONFIG_MTK_DVFSRC_MT6781_PRETEST)
DDR_SW_REQ4_MD_IDX,
DDR_SW_REQ8_MCUSYS_IDX,
#endif
DDR_QOS_BW_IDX,
DDR_EMI_TOTAL_IDX,
DDR_HRT_BW_IDX,
DDR_HIFI_IDX,
DDR_HIFI_LATENCY_IDX,
DDR_MD_LATENCY_IDX,
DDR_MD_DDR_IDX,
DDR_MD_SRCLK_IDX,
VCORE_OPP_IDX,
VCORE_SW_REQ3_PMQOS_IDX,
VCORE_SCP_IDX,
VCORE_HIFI_IDX,
SRC_SCP_REQ_IDX,
SRC_PMQOS_TATOL_IDX,
SRC_PMQOS_BW0_IDX,
SRC_PMQOS_BW1_IDX,
SRC_PMQOS_BW2_IDX,
SRC_PMQOS_BW3_IDX,
SRC_PMQOS_BW4_IDX,
SRC_TOTAL_EMI_BW_IDX,
SRC_HRT_MD_BW_IDX,
SRC_HRT_DISP_BW_IDX,
SRC_HRT_ISP_BW_IDX,
SRC_MD_SCENARIO_IDX,
SRC_HIFI_SCENARIO_IDX,
SRC_MD_EMI_LATENCY_IDX,
SRC_MAX
};
extern u32 dvfsrc_ct_mode(void);
extern u32 dvfsrc_vcore_mode(void);
#endif /* __HELIO_DVFSRC_V6781_H */
@@ -0,0 +1,859 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/fb.h>
#include <linux/platform_device.h>
#ifdef CONFIG_MEDIATEK_DRAMC
#include <dramc.h>
#endif
#ifdef CONFIG_MTK_EMI
#include <mt_emi_api.h>
#endif
#include <mt-plat/upmu_common.h>
#include "helio-dvfsrc-ip-v2.h"
#include <helio-dvfsrc-opp.h>
#include <helio-dvfsrc-v6877.h>
#include <mt-plat/mtk_devinfo.h>
#include <linux/regulator/consumer.h>
#include "mmdvfs_pmqos.h"
#include <linux/sysfs.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/sched/clock.h>
#include <dbgtop.h>
#define dvfsrc_rmw(offset, val, mask, shift) \
dvfsrc_write(offset, (dvfsrc_read(offset) & ~(mask << shift)) \
| (val << shift))
#define V_OPP_TYPE_SHIFT 20
static int dvfsrc_rsrv;
#ifdef CONFIG_MTK_DVFSRC_MT6877_PRETEST
static struct reg_config dvfsrc_init_configs[][128] = {
{
{ DVFSRC_HRT_REQ_UNIT, 0x0000001E },
{ DVFSRC_DEBOUNCE_TIME, 0x00001965 },
{ DVFSRC_TIMEOUT_NEXTREQ, 0x00000015 },
{ DVFSRC_DDR_QOS0, 0x00000019 },
{ DVFSRC_DDR_QOS1, 0x00000026 },
{ DVFSRC_DDR_QOS2, 0x00000033 },
{ DVFSRC_DDR_QOS3, 0x0000003B },
{ DVFSRC_DDR_QOS4, 0x00000055 },
{ DVFSRC_DDR_QOS5, 0x00000077 },
{ DVFSRC_DDR_QOS6, 0x00000088 },
{ DVFSRC_LEVEL_LABEL_0_1, 0x60647074 },
{ DVFSRC_LEVEL_LABEL_2_3, 0x50546063 },
{ DVFSRC_LEVEL_LABEL_4_5, 0x50525053 },
{ DVFSRC_LEVEL_LABEL_6_7, 0x40434044 },
{ DVFSRC_LEVEL_LABEL_8_9, 0x40414042 },
{ DVFSRC_LEVEL_LABEL_10_11, 0x30333034 },
{ DVFSRC_LEVEL_LABEL_12_13, 0x30313032 },
{ DVFSRC_LEVEL_LABEL_14_15, 0x20243030 },
{ DVFSRC_LEVEL_LABEL_16_17, 0x20222023 },
{ DVFSRC_LEVEL_LABEL_18_19, 0x20202021 },
{ DVFSRC_LEVEL_LABEL_20_21, 0x10131014 },
{ DVFSRC_LEVEL_LABEL_22_23, 0x10111012 },
{ DVFSRC_LEVEL_LABEL_24_25, 0x00041010 },
{ DVFSRC_LEVEL_LABEL_26_27, 0x00020003 },
{ DVFSRC_LEVEL_LABEL_28_29, 0x00000001 },
{ DVFSRC_MD_LATENCY_IMPROVE, 0x00000040 },
{ DVFSRC_HRT_BW_BASE, 0x00000004 },
{ DVSFRC_HRT_REQ_MD_URG, 0x000D20D2 },
{ DVFSRC_HRT_REQ_MD_BW_0, 0x00200802 },
{ DVFSRC_HRT_REQ_MD_BW_1, 0x00200802 },
{ DVFSRC_HRT_REQ_MD_BW_2, 0x00200800 },
{ DVFSRC_HRT_REQ_MD_BW_3, 0x00400802 },
{ DVFSRC_HRT_REQ_MD_BW_4, 0x00601404 },
{ DVFSRC_HRT_REQ_MD_BW_5, 0x00D02C09 },
{ DVFSRC_HRT_REQ_MD_BW_6, 0x00000012 },
{ DVFSRC_HRT_REQ_MD_BW_7, 0x00000024 },
{ DVFSRC_HRT_REQ_MD_BW_8, 0x00000000 },
{ DVFSRC_HRT_REQ_MD_BW_9, 0x00000000 },
{ DVFSRC_HRT_REQ_MD_BW_10, 0x00034800 },
{ DVFSRC_HRT1_REQ_MD_BW_0, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_1, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_2, 0x04B12C00 },
{ DVFSRC_HRT1_REQ_MD_BW_3, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_4, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_5, 0x04B12C4B },
{ DVFSRC_HRT1_REQ_MD_BW_6, 0x0000004B },
{ DVFSRC_HRT1_REQ_MD_BW_7, 0x0000005C },
{ DVFSRC_HRT1_REQ_MD_BW_8, 0x00000000 },
{ DVFSRC_HRT1_REQ_MD_BW_9, 0x00000000 },
{ DVFSRC_HRT1_REQ_MD_BW_10, 0x00034800 },
{ DVFSRC_95MD_SCEN_BW0_T, 0x40444440 },
{ DVFSRC_95MD_SCEN_BW1_T, 0x22244444 },
{ DVFSRC_95MD_SCEN_BW2_T, 0x00400444 },
{ DVFSRC_95MD_SCEN_BW3_T, 0x60000000 },
{ DVFSRC_95MD_SCEN_BW0, 0x20222220 },
{ DVFSRC_95MD_SCEN_BW1, 0x00022222},
{ DVFSRC_95MD_SCEN_BW2, 0x00200222 },
{ DVFSRC_95MD_SCEN_BW3, 0x60000000 },
{ DVFSRC_95MD_SCEN_BW4, 0x00000006 },
{ DVFSRC_RSRV_5, 0x00000001 },
{ DVFSRC_DDR_REQUEST, 0x00004321 },
{ DVFSRC_DDR_REQUEST3, 0x00000765 },
{ DVFSRC_DDR_ADD_REQUEST, 0x76543210 },
{ DVFSRC_HRT_REQUEST, 0x77654321 },
{ DVFSRC_DDR_REQUEST5, 0x54321000 },
{ DVFSRC_DDR_REQUEST7, 0x76000000 },
{ DVFSRC_EMI_MON_DEBOUNCE_TIME, 0x4C2D0000 },
{ DVFSRC_DDR_REQUEST6, 0x76543210 },
{ DVFSRC_VCORE_USER_REQ, 0x00010A29 },
{ DVFSRC_HRT_HIGH_3, 0x20DC20DC },
{ DVFSRC_HRT_HIGH_2, 0x1B3D1328 },
{ DVFSRC_HRT_HIGH_1, 0x0D690B80 },
{ DVFSRC_HRT_HIGH, 0x070804B0 },
{ DVFSRC_HRT_LOW_3, 0x20DB20DB },
{ DVFSRC_HRT_LOW_2, 0x1B3C132A },
{ DVFSRC_HRT_LOW_1, 0x0D680B7F },
{ DVFSRC_HRT_LOW, 0x070704AF },
{ DVFSRC_BASIC_CONTROL_3, 0x00000006 },
{ DVFSRC_INT_EN, 0x00000002 },
{ DVFSRC_QOS_EN, 0x0000407C },
#ifdef CONFIG_MTK_DVFSRC_MT6877_PRETEST
{ DVFSRC_DDR_REQUEST3, 0x00000065 },
{ DVFSRC_DDR_QOS4, 0x0000004C },
{ DVFSRC_DDR_QOS5, 0x00000066 },
{ DVFSRC_HRT_HIGH_2, 0x18A61183 },
{ DVFSRC_HRT_LOW_2, 0x18A51182 },
#endif
{ DVFSRC_CURRENT_FORCE, 0x00000001 },
{ DVFSRC_BASIC_CONTROL, 0x67B8444B },
{ DVFSRC_BASIC_CONTROL, 0x67B8054B },
{ DVFSRC_CURRENT_FORCE, 0x00000000 },
{ -1, 0 },
},
/* NULL */
{
{ -1, 0 },
},
};
#else
static struct reg_config dvfsrc_init_configs[][128] = {
{
{ DVFSRC_HRT_REQ_UNIT, 0x0000001E },
{ DVFSRC_DEBOUNCE_TIME, 0x00001965 },
{ DVFSRC_TIMEOUT_NEXTREQ, 0x0000003C },
{ DVFSRC_DDR_QOS0, 0x00000019 },
{ DVFSRC_DDR_QOS1, 0x00000026 },
{ DVFSRC_DDR_QOS2, 0x00000033 },
{ DVFSRC_DDR_QOS3, 0x0000003B },
{ DVFSRC_DDR_QOS4, 0x00000055 },
{ DVFSRC_DDR_QOS5, 0x00000077 },
{ DVFSRC_DDR_QOS6, 0x00000088 },
{ DVFSRC_LEVEL_LABEL_0_1, 0x60647074 },
{ DVFSRC_LEVEL_LABEL_2_3, 0x50546063 },
{ DVFSRC_LEVEL_LABEL_4_5, 0x50525053 },
{ DVFSRC_LEVEL_LABEL_6_7, 0x40434044 },
{ DVFSRC_LEVEL_LABEL_8_9, 0x40414042 },
{ DVFSRC_LEVEL_LABEL_10_11, 0x30333034 },
{ DVFSRC_LEVEL_LABEL_12_13, 0x30313032 },
{ DVFSRC_LEVEL_LABEL_14_15, 0x20243030 },
{ DVFSRC_LEVEL_LABEL_16_17, 0x20222023 },
{ DVFSRC_LEVEL_LABEL_18_19, 0x20202021 },
{ DVFSRC_LEVEL_LABEL_20_21, 0x10131014 },
{ DVFSRC_LEVEL_LABEL_22_23, 0x10111012 },
{ DVFSRC_LEVEL_LABEL_24_25, 0x00041010 },
{ DVFSRC_LEVEL_LABEL_26_27, 0x00020003 },
{ DVFSRC_LEVEL_LABEL_28_29, 0x00000001 },
{ DVFSRC_MD_LATENCY_IMPROVE, 0x00000040 },
{ DVFSRC_HRT_BW_BASE, 0x00000004 },
{ DVSFRC_HRT_REQ_MD_URG, 0x001A51A5 },
{ DVFSRC_HRT_REQ_MD_BW_0, 0x00300C03 },
{ DVFSRC_HRT_REQ_MD_BW_1, 0x00300C03 },
{ DVFSRC_HRT_REQ_MD_BW_2, 0x00300C00 },
{ DVFSRC_HRT_REQ_MD_BW_3, 0x00601003 },
{ DVFSRC_HRT_REQ_MD_BW_4, 0x00902007 },
{ DVFSRC_HRT_REQ_MD_BW_5, 0x0160440F },
{ DVFSRC_HRT_REQ_MD_BW_6, 0x0000001E },
{ DVFSRC_HRT_REQ_MD_BW_7, 0x0000003A },
{ DVFSRC_HRT_REQ_MD_BW_8, 0x00000000 },
{ DVFSRC_HRT_REQ_MD_BW_9, 0x00000000 },
{ DVFSRC_HRT_REQ_MD_BW_10, 0x00069400 },
{ DVFSRC_HRT1_REQ_MD_BW_0, 0x08020080 },
{ DVFSRC_HRT1_REQ_MD_BW_1, 0x08020080 },
{ DVFSRC_HRT1_REQ_MD_BW_2, 0x08020000 },
{ DVFSRC_HRT1_REQ_MD_BW_3, 0x08020080 },
{ DVFSRC_HRT1_REQ_MD_BW_4, 0x08020080 },
{ DVFSRC_HRT1_REQ_MD_BW_5, 0x08020080 },
{ DVFSRC_HRT1_REQ_MD_BW_6, 0x00000080 },
{ DVFSRC_HRT1_REQ_MD_BW_7, 0x00000098 },
{ DVFSRC_HRT1_REQ_MD_BW_8, 0x00000000 },
{ DVFSRC_HRT1_REQ_MD_BW_9, 0x00000000 },
{ DVFSRC_HRT1_REQ_MD_BW_10, 0x00069400 },
{ DVFSRC_95MD_SCEN_BW0_T, 0x20222220 },
{ DVFSRC_95MD_SCEN_BW1_T, 0x22222222 },
{ DVFSRC_95MD_SCEN_BW2_T, 0x04300444 },
{ DVFSRC_95MD_SCEN_BW3_T, 0x60000000 },
{ DVFSRC_95MD_SCEN_BW0, 0x00000000 },
{ DVFSRC_95MD_SCEN_BW1, 0x00000000 },
{ DVFSRC_95MD_SCEN_BW2, 0x02200222 },
{ DVFSRC_95MD_SCEN_BW3, 0x60000000 },
{ DVFSRC_95MD_SCEN_BW4, 0x00000006 },
{ DVFSRC_RSRV_5, 0x00000001 },
{ DVFSRC_DDR_REQUEST, 0x00004321 },
{ DVFSRC_DDR_REQUEST3, 0x00000765 },
{ DVFSRC_DDR_ADD_REQUEST, 0x76543210 },
{ DVFSRC_HRT_REQUEST, 0x77654321 },
{ DVFSRC_DDR_REQUEST5, 0x54321000 },
{ DVFSRC_DDR_REQUEST7, 0x76000000 },
{ DVFSRC_EMI_MON_DEBOUNCE_TIME, 0x4C2D0000 },
{ DVFSRC_DDR_REQUEST6, 0x76543210 },
{ DVFSRC_VCORE_USER_REQ, 0x00010A29 },
{ DVFSRC_HRT_HIGH_3, 0x31533153 },
{ DVFSRC_HRT_HIGH_2, 0x28D41900 },
{ DVFSRC_HRT_HIGH_1, 0x117E0F00 },
{ DVFSRC_HRT_HIGH, 0x0B400780 },
{ DVFSRC_HRT_LOW_3, 0x31523152 },
{ DVFSRC_HRT_LOW_2, 0x28D318FF },
{ DVFSRC_HRT_LOW_1, 0x117D0EFF },
{ DVFSRC_HRT_LOW, 0x0B3F077F },
{ DVFSRC_BASIC_CONTROL_3, 0x00000006 },
{ DVFSRC_INT_EN, 0x00000002 },
{ DVFSRC_QOS_EN, 0x0000407C },
{ DVFSRC_CURRENT_FORCE, 0x00000001 },
{ DVFSRC_BASIC_CONTROL, 0x67B8444B },
{ DVFSRC_BASIC_CONTROL, 0x67B8054B },
{ DVFSRC_CURRENT_FORCE, 0x00000000 },
{ -1, 0 },
},
/* NULL */
{
{ -1, 0 },
},
};
#endif
static ssize_t dvfsrc_vcore_settle_time_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
/* DE's comment: settle time was hard code in fw (15,30) */
return sprintf(buf, "rising 15 uS, falling 30 uS for mt6877\n");
}
static DEVICE_ATTR(dvfsrc_vcore_settle_time, 0444,
dvfsrc_vcore_settle_time_show, NULL);
static struct attribute *mt6877_helio_dvfsrc_attrs[] = {
&dev_attr_dvfsrc_vcore_settle_time.attr,
NULL,
};
static struct attribute_group mt6877_helio_dvfsrc_attr_group = {
.name = "helio-dvfsrc",
.attrs = mt6877_helio_dvfsrc_attrs,
};
#define dvfsrc_rmw(offset, val, mask, shift) \
dvfsrc_write(offset, (dvfsrc_read(offset) & ~(mask << shift)) \
| (val << shift))
u32 dvfsrc_get_ddr_qos(void)
{
unsigned int qos_total_bw = dvfsrc_read(DVFSRC_SW_BW_0) +
dvfsrc_read(DVFSRC_SW_BW_1) +
dvfsrc_read(DVFSRC_SW_BW_2) +
dvfsrc_read(DVFSRC_SW_BW_3) +
dvfsrc_read(DVFSRC_SW_BW_4);
#ifdef CONFIG_MTK_DVFSRC_MT6877_PRETEST
if (qos_total_bw < 0x19)
return 0;
else if (qos_total_bw < 0x26)
return 1;
else if (qos_total_bw < 0x33)
return 2;
else if (qos_total_bw < 0x3B)
return 3;
else if (qos_total_bw < 0x4C)
return 4;
else if (qos_total_bw < 0x66)
return 5;
else if (qos_total_bw < 0x88)
return 6;
else
return 7;
#else
if (qos_total_bw < 0x19)
return 0;
else if (qos_total_bw < 0x26)
return 1;
else if (qos_total_bw < 0x33)
return 2;
else if (qos_total_bw < 0x3B)
return 3;
else if (qos_total_bw < 0x55)
return 4;
else if (qos_total_bw < 0x77)
return 5;
else if (qos_total_bw < 0x88)
return 6;
else
return 7;
#endif
}
static int dvfsrc_get_emi_mon_gear(void)
{
unsigned int total_bw_status;
int i;
total_bw_status = vcorefs_get_total_emi_status() & 0x7F;
for (i = 6; i >= 0 ; i--) {
if ((total_bw_status >> i) > 0)
return i + 1;
}
return 0;
}
static u32 dvfsrc_calc_hrt_opp(int data)
{
#ifdef CONFIG_MTK_DVFSRC_MT6877_PRETEST
if (data < 0x04B0)
return DDR_OPP_7;
else if (data < 0x0708)
return DDR_OPP_6;
else if (data < 0x0B80)
return DDR_OPP_5;
else if (data < 0x0D69)
return DDR_OPP_4;
else if (data < 0x1183)
return DDR_OPP_3;
else if (data < 0x18A6)
return DDR_OPP_2;
else if (data < 0x20DC)
return DDR_OPP_1;
else
return DDR_OPP_0;
#else
if ((dvfsrc_rsrv >> V_OPP_TYPE_SHIFT) & 0x3) {
if (data < 0x780)
return DDR_OPP_7;
else if (data < 0xB40)
return DDR_OPP_6;
else if (data < 0xF00)
return DDR_OPP_5;
else if (data < 0x117E)
return DDR_OPP_4;
else if (data < 0x1900)
return DDR_OPP_3;
else if (data < 0x24BF)
return DDR_OPP_2;
else if (data < 0x2CA8)
return DDR_OPP_1;
else
return DDR_OPP_0;
} else {
if (data < 0x780)
return DDR_OPP_7;
else if (data < 0xB40)
return DDR_OPP_6;
else if (data < 0xF00)
return DDR_OPP_5;
else if (data < 0x117E)
return DDR_OPP_4;
else if (data < 0x1900)
return DDR_OPP_3;
else if (data < 0x28D4)
return DDR_OPP_2;
else if (data < 0x3153)
return DDR_OPP_1;
else
return DDR_OPP_0;
}
#endif
}
void dvfsrc_set_isp_hrt_bw(int data)
{
data = (data + 29) / 30;
if (data > 0x3FF)
data = 0x3FF;
dvfsrc_write(DVFSRC_ISP_HRT, data);
}
u32 dvfsrc_calc_isp_hrt_opp(int data)
{
return dvfsrc_calc_hrt_opp(((data + 29) / 30) * 30);
}
u32 dvfsrc_get_pcie_vcore_status(void)
{
u32 val, pcie_en;
val = dvfsrc_read(DVFSRC_DEBUG_STA_2);
pcie_en = (val >> DEBUG_STA2_PCIE_SHIFT) & DEBUG_STA2_PCIE_MASK;
if (pcie_en)
return 1;
else
return 0;
}
struct regulator *dvfsrc_vcore_requlator(struct device *dev)
{
#ifdef CONFIG_MTK_DVFSRC_MT6877_PRETEST
return regulator_get(dev, "vcore");
#else
return regulator_get(dev, "vgpu11");
#endif
}
#ifdef AUTOK_ENABLE
__weak int emmc_autok(void)
{
pr_info("NOT SUPPORT EMMC AUTOK\n");
return 0;
}
__weak int sd_autok(void)
{
pr_info("NOT SUPPORT SD AUTOK\n");
return 0;
}
__weak int sdio_autok(void)
{
pr_info("NOT SUPPORT SDIO AUTOK\n");
return 0;
}
void begin_autok_task(void)
{
/* notify MM DVFS for msdc autok start */
mmdvfs_prepare_action(MMDVFS_PREPARE_CALIBRATION_START);
}
void finish_autok_task(void)
{
/* check if dvfs force is released */
int force = mtk_pm_qos_request(MTK_PM_QOS_VCORE_DVFS_FORCE_OPP);
/* notify MM DVFS for msdc autok finish */
mmdvfs_prepare_action(MMDVFS_PREPARE_CALIBRATION_END);
if (force >= 0 && force < VCORE_DVFS_OPP_NUM)
pr_info("autok task not release force opp: %d\n", force);
}
static void dvfsrc_autok_manager(void)
{
int r = 0;
begin_autok_task();
r = emmc_autok();
pr_info("EMMC autok done: %s\n", (r == 0) ? "Yes" : "No");
r = sd_autok();
pr_info("SD autok done: %s\n", (r == 0) ? "Yes" : "No");
r = sdio_autok();
pr_info("SDIO autok done: %s\n", (r == 0) ? "Yes" : "No");
finish_autok_task();
}
#endif
void helio_dvfsrc_platform_pre_init(struct helio_dvfsrc *dvfsrc)
{
struct platform_device *pdev = to_platform_device(dvfsrc->dev);
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
dvfsrc->spm_regs = devm_ioremap(&pdev->dev,
res->start, resource_size(res));
if (IS_ERR(dvfsrc->spm_regs))
pr_info("not get spm register\n");
dvfsrc_rsrv = readl(dvfsrc->regs + DVFSRC_RSRV_4);
#if 0
if (((dvfsrc_rsrv >> V_OPP_TYPE_SHIFT) & 0x3) && (dvfsrc->dvfsrc_flag == 0)) {
dvfsrc->dvfsrc_flag = 0x3;
writel(0x7000, dvfsrc->regs + DVFSRC_SW_REQ6);
}
#endif
}
__weak void mtk_pm_qos_trace_dbg_dump(int mtk_pm_qos_class)
{
}
void dvfsrc_suspend_cb(struct helio_dvfsrc *dvfsrc)
{
int sw_req;
sw_req = dvfsrc_read(DVFSRC_SW_REQ3);
pr_info("[DVFSRC] V:%d, F_OPP:%d, RG:%08x, %08x, %08x, %08x\n",
get_cur_vcore_uv(),
mtk_pm_qos_request(MTK_PM_QOS_VCORE_DVFS_FORCE_OPP),
dvfsrc_read(DVFSRC_CURRENT_LEVEL),
dvfsrc_read(DVFSRC_SW_REQ2),
sw_req,
dvfsrc_read(DVFSRC_DEBUG_STA_0));
if (sw_req & (DDR_SW_AP_MASK << DDR_SW_AP_SHIFT))
mtk_pm_qos_trace_dbg_dump(MTK_PM_QOS_DDR_OPP);
if (sw_req & (VCORE_SW_AP_MASK << VCORE_SW_AP_SHIFT))
mtk_pm_qos_trace_dbg_dump(MTK_PM_QOS_VCORE_OPP);
}
void dvfsrc_resume_cb(struct helio_dvfsrc *dvfsrc)
{
}
void helio_dvfsrc_platform_init(struct helio_dvfsrc *dvfsrc)
{
int spmfw_idx = 0;
struct reg_config *config;
int idx = 0;
sysfs_merge_group(&dvfsrc->dev->kobj, &mt6877_helio_dvfsrc_attr_group);
dvfsrc_rsrv = readl(dvfsrc->regs + DVFSRC_RSRV_4);
if ((dvfsrc_rsrv >> V_OPP_TYPE_SHIFT) & 0x3)
writel(0x7000, dvfsrc->regs + DVFSRC_SW_REQ6);
config = dvfsrc_init_configs[spmfw_idx];
while (config[idx].offset != -1) {
dvfsrc_write(config[idx].offset, config[idx].val);
idx++;
}
if ((dvfsrc_rsrv >> V_OPP_TYPE_SHIFT) & 0x3) {
writel(0x2CA82CA8, dvfsrc->regs + DVFSRC_HRT_HIGH_3);
writel(0x24BF1900, dvfsrc->regs + DVFSRC_HRT_HIGH_2);
writel(0x2CA72CA7, dvfsrc->regs + DVFSRC_HRT_LOW_3);
writel(0x24BE18FF, dvfsrc->regs + DVFSRC_HRT_LOW_2);
writel(0x0000, dvfsrc->regs + DVFSRC_SW_REQ6);
}
#ifdef AUTOK_ENABLE
dvfsrc_autok_manager();
#endif
}
int vcore_pmic_to_uv(int pmic_val)
{
return __vcore_pmic_to_uv(pmic_val);
}
int vcore_uv_to_pmic(int vcore_uv)
{
return __vcore_uv_to_pmic(vcore_uv);
}
void get_spm_reg(char *p)
{
p += sprintf(p, "%-24s: 0x%08x\n",
"POWERON_CONFIG_EN",
spm_reg_read(POWERON_CONFIG_EN));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_SW_FLAG_0",
spm_reg_read(SPM_SW_FLAG_0));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_PC_STA",
spm_reg_read(SPM_PC_STA));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVFS_LEVEL",
spm_reg_read(SPM_DVFS_LEVEL));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVS_DFS_LEVEL",
spm_reg_read(SPM_DVS_DFS_LEVEL));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVFS_STA",
spm_reg_read(SPM_DVFS_STA));
p += sprintf(p, "%-24s: 0x%08x\n",
"SPM_DVFS_MISC",
spm_reg_read(SPM_DVFS_MISC));
p += sprintf(p, "%-24s: 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
"SPM_DVFS_CMD0~4",
spm_reg_read(SPM_DVFS_CMD0),
spm_reg_read(SPM_DVFS_CMD1),
spm_reg_read(SPM_DVFS_CMD2),
spm_reg_read(SPM_DVFS_CMD3),
spm_reg_read(SPM_DVFS_CMD4));
}
void get_opp_info(char *p)
{
#if defined(CONFIG_FPGA_EARLY_PORTING) || !defined(CONFIG_MTK_PMIC_COMMON) \
|| !defined(CONFIG_MTK_PMIC_NEW_ARCH)
int pmic_val = 0;
#else
int pmic_val = pmic_get_register_value(PMIC_VCORE_ADDR);
#endif
#ifdef CONFIG_MEDIATEK_DRAMC
int ddr_khz = mtk_dramc_get_data_rate() * 1000;
#else
int ddr_khz = 0;
#endif
int vcore_uv = vcore_pmic_to_uv(pmic_val);
p += sprintf(p, "%-10s: %-8u uv (PMIC: 0x%x)\n",
"Vcore", vcore_uv, vcore_uv_to_pmic(vcore_uv));
p += sprintf(p, "%-10s: %-8u khz\n", "DDR", ddr_khz);
p += sprintf(p, "%6s: 0x%08x\n", "INFO1", get_devinfo_with_index(210));
p += sprintf(p, "%6s: 0x%08x\n", "V_MODE", dvfsrc_read(DVFSRC_RSRV_4));
}
/* met profile table */
static unsigned int met_vcorefs_src[SRC_MAX];
static char *met_src_name[SRC_MAX] = {
"MD2SPM",
"SRC_DDR_OPP",
"DDR__SW_REQ1_SPM",
"DDR__SW_REQ2_CM",
"DDR__SW_REQ3_PMQOS",
"DDR__QOS_BW",
"DDR__EMI_TOTAL",
"DDR__HRT_BW",
"DDR__HIFI",
"DDR__HIFI_LATENCY",
"DDR__MD_LATENCY",
"DDR__MD_DDR",
"DDR__MD_LEVEL_MASK",
"SRC_VCORE_OPP",
"VCORE__SW_REQ3_PMQOS",
"VCORE__SCP",
"VCORE__HIFI",
"VCORE__PCIE",
"VCORE__DPAMIF",
"SCP_REQ",
"PMQOS_TATOL",
"PMQOS_BW0",
"PMQOS_BW1",
"PMQOS_BW2",
"PMQOS_BW3",
"PMQOS_BW4",
"TOTAL_EMI_BW",
"HRT_MD_BW",
"HRT_DISP_BW",
"HRT_ISP_BW",
"MD_SCENARIO",
"HIFI_SCENARIO_IDX",
"MD_EMI_LATENCY",
};
/* met profile function */
int vcorefs_get_src_req_num(void)
{
return SRC_MAX;
}
EXPORT_SYMBOL(vcorefs_get_src_req_num);
char **vcorefs_get_src_req_name(void)
{
return met_src_name;
}
EXPORT_SYMBOL(vcorefs_get_src_req_name);
static u32 vcorefs_get_md_level_mask_ddr(void)
{
int md_srclk, vopp;
if (dvfsrc_read(DVFSRC_BASIC_CONTROL_3) & 0x8) {
md_srclk = dvfsrc_read(DVFSRC_DEBUG_STA_0);
vopp = get_cur_vcore_opp();
md_srclk = (md_srclk >> MD_SRC_CLK_DEBUG_SHIFT)
& MD_SRC_CLK_DEBUG_MASK;
if (vopp != 3 && md_srclk == 1)
return 2;
}
return 0;
}
static void vcorefs_get_src_ddr_req(void)
{
unsigned int sw_req;
met_vcorefs_src[DDR_OPP_IDX] =
get_cur_ddr_opp();
sw_req = dvfsrc_read(DVFSRC_SW_REQ1);
met_vcorefs_src[DDR_SW_REQ1_SPM_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
sw_req = dvfsrc_read(DVFSRC_SW_REQ2);
met_vcorefs_src[DDR_SW_REQ2_CM_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
sw_req = dvfsrc_read(DVFSRC_SW_REQ3);
met_vcorefs_src[DDR_SW_REQ3_PMQOS_IDX] =
(sw_req >> DDR_SW_AP_SHIFT) & DDR_SW_AP_MASK;
met_vcorefs_src[DDR_QOS_BW_IDX] =
dvfsrc_get_ddr_qos();
met_vcorefs_src[DDR_EMI_TOTAL_IDX] =
dvfsrc_get_emi_mon_gear();
met_vcorefs_src[DDR_HRT_BW_IDX] =
vcorefs_get_hrt_bw_ddr();
met_vcorefs_src[DDR_HIFI_IDX] =
vcorefs_get_hifi_ddr_status();
met_vcorefs_src[DDR_HIFI_LATENCY_IDX] =
vcorefs_get_hifi_rising_ddr();
met_vcorefs_src[DDR_MD_LATENCY_IDX] =
vcorefs_get_md_imp_ddr();
met_vcorefs_src[DDR_MD_DDR_IDX] =
vcorefs_get_md_scenario_ddr();
met_vcorefs_src[DDR_MD_SRCLK_IDX] =
vcorefs_get_md_level_mask_ddr();
}
static void vcorefs_get_src_vcore_req(void)
{
u32 sw_req;
u32 scp_en;
scp_en = vcorefs_get_scp_req_status();
met_vcorefs_src[VCORE_OPP_IDX] =
get_cur_vcore_opp();
sw_req = dvfsrc_read(DVFSRC_SW_REQ3);
met_vcorefs_src[VCORE_SW_REQ3_PMQOS_IDX] =
(sw_req >> VCORE_SW_AP_SHIFT) & VCORE_SW_AP_MASK;
sw_req = dvfsrc_read(DVFSRC_SW_REQ7);
met_vcorefs_src[VCORE_DPAMIF_IDX] =
(sw_req >> VCORE_SW_AP_SHIFT) & VCORE_SW_AP_MASK;
if (scp_en) {
sw_req = dvfsrc_read(DVFSRC_VCORE_REQUEST);
met_vcorefs_src[VCORE_SCP_IDX] =
(sw_req >> VCORE_SCP_GEAR_SHIFT) & VCORE_SCP_GEAR_MASK;
} else
met_vcorefs_src[VCORE_SCP_IDX] = 0;
met_vcorefs_src[VCORE_HIFI_IDX] =
vcorefs_get_hifi_vcore_status();
met_vcorefs_src[VCORE_PCIE_IDX] =
dvfsrc_get_pcie_vcore_status();
}
static void vcorefs_get_src_misc_info(void)
{
u32 qos_bw0, qos_bw1, qos_bw2, qos_bw3, qos_bw4;
qos_bw0 = dvfsrc_read(DVFSRC_SW_BW_0);
qos_bw1 = dvfsrc_read(DVFSRC_SW_BW_1);
qos_bw2 = dvfsrc_read(DVFSRC_SW_BW_2);
qos_bw3 = dvfsrc_read(DVFSRC_SW_BW_3);
qos_bw4 = dvfsrc_read(DVFSRC_SW_BW_4);
met_vcorefs_src[SRC_MD2SPM_IDX] =
vcorefs_get_md_scenario() & 0x1FFFF;
met_vcorefs_src[SRC_SCP_REQ_IDX] =
vcorefs_get_scp_req_status();
met_vcorefs_src[SRC_PMQOS_TATOL_IDX] =
qos_bw0 + qos_bw1 + qos_bw2 + qos_bw3 + qos_bw4;
met_vcorefs_src[SRC_PMQOS_BW0_IDX] =
qos_bw0;
met_vcorefs_src[SRC_PMQOS_BW1_IDX] =
qos_bw1;
met_vcorefs_src[SRC_PMQOS_BW2_IDX] =
qos_bw2;
met_vcorefs_src[SRC_PMQOS_BW3_IDX] =
qos_bw3;
met_vcorefs_src[SRC_PMQOS_BW4_IDX] =
qos_bw4;
met_vcorefs_src[SRC_HRT_MD_BW_IDX] =
dvfsrc_get_md_bw();
met_vcorefs_src[SRC_HRT_ISP_BW_IDX] =
dvfsrc_read(DVFSRC_ISP_HRT);
met_vcorefs_src[SRC_MD_SCENARIO_IDX] =
vcorefs_get_md_scenario();
met_vcorefs_src[SRC_HIFI_SCENARIO_IDX] =
vcorefs_get_hifi_scenario();
met_vcorefs_src[SRC_MD_EMI_LATENCY_IDX] =
vcorefs_get_md_emi_latency_status();
}
int dvfsrc_latch_register(int enable)
{
#ifdef CONFIG_MTK_DBGTOP
return mtk_dbgtop_cfg_dvfsrc(1);
#endif
return 0;
}
unsigned int *vcorefs_get_src_req(void)
{
vcorefs_get_src_ddr_req();
vcorefs_get_src_vcore_req();
vcorefs_get_src_misc_info();
vcorefs_trace_qos();
return met_vcorefs_src;
}
EXPORT_SYMBOL(vcorefs_get_src_req);
int get_cur_ddr_ratio(void)
{
int idx;
if (!is_dvfsrc_enabled())
return 0;
idx = get_cur_vcore_dvfs_opp();
if (idx >= VCORE_DVFS_OPP_NUM)
return 0;
if (get_ddr_opp(idx) < DDR_OPP_7)
return 8;
else
return 4;
}
EXPORT_SYMBOL(get_cur_ddr_ratio);
@@ -0,0 +1,68 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_V6877_H
#define __HELIO_DVFSRC_V6877_H
#include <mach/upmu_hw.h>
#ifdef CONFIG_MTK_DVFSRC_MT6877_PRETEST
#define PMIC_VCORE_ADDR PMIC_RG_BUCK_VCORE_VOSEL
#define VCORE_BASE_UV 506250
#define VCORE_STEP_UV 6250
#else
#define PMIC_VCORE_ADDR PMIC_RG_BUCK_VGPU11_VOSEL
#define VCORE_BASE_UV 400000
#define VCORE_STEP_UV 6250
#endif
/* PMIC */
#define __vcore_pmic_to_uv(pmic) \
(((pmic) * VCORE_STEP_UV) + VCORE_BASE_UV)
#define __vcore_uv_to_pmic(uv) /* pmic >= uv */ \
((((uv) - VCORE_BASE_UV) + (VCORE_STEP_UV - 1)) / VCORE_STEP_UV)
enum met_src_index {
SRC_MD2SPM_IDX = 0,
DDR_OPP_IDX,
DDR_SW_REQ1_SPM_IDX,
DDR_SW_REQ2_CM_IDX,
DDR_SW_REQ3_PMQOS_IDX,
DDR_QOS_BW_IDX,
DDR_EMI_TOTAL_IDX,
DDR_HRT_BW_IDX,
DDR_HIFI_IDX,
DDR_HIFI_LATENCY_IDX,
DDR_MD_LATENCY_IDX,
DDR_MD_DDR_IDX,
DDR_MD_SRCLK_IDX,
VCORE_OPP_IDX,
VCORE_SW_REQ3_PMQOS_IDX,
VCORE_SCP_IDX,
VCORE_HIFI_IDX,
VCORE_PCIE_IDX,
VCORE_DPAMIF_IDX,
SRC_SCP_REQ_IDX,
SRC_PMQOS_TATOL_IDX,
SRC_PMQOS_BW0_IDX,
SRC_PMQOS_BW1_IDX,
SRC_PMQOS_BW2_IDX,
SRC_PMQOS_BW3_IDX,
SRC_PMQOS_BW4_IDX,
SRC_TOTAL_EMI_BW_IDX,
SRC_HRT_MD_BW_IDX,
SRC_HRT_DISP_BW_IDX,
SRC_HRT_ISP_BW_IDX,
SRC_MD_SCENARIO_IDX,
SRC_HIFI_SCENARIO_IDX,
SRC_MD_EMI_LATENCY_IDX,
SRC_MAX
};
extern u32 dvfsrc_ct_mode(void);
extern u32 dvfsrc_vcore_mode(void);
#endif /* __HELIO_DVFSRC_V6877_H */
@@ -0,0 +1,435 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __MTK_DVFSRC_REG_V2_H
#define __MTK_DVFSRC_REG_V2_H
#if defined(CONFIG_MACH_MT6779)
#define DVFSRC_IP_V2_1
#elif defined(CONFIG_MACH_MT6785)
#define DVFSRC_IP_V2_2
#endif
/**************************************
* Define and Declare
**************************************/
#define DVFSRC_BASIC_CONTROL (0x0)
#define DVFSRC_SW_REQ1 (0x4)
#define DVFSRC_SW_REQ2 (0x8)
#define DVFSRC_SW_REQ3 (0xC)
#define DVFSRC_SW_REQ4 (0x10)
#define DVFSRC_SW_REQ5 (0x14)
#define DVFSRC_SW_REQ6 (0x18)
#define DVFSRC_SW_REQ7 (0x1C)
#define DVFSRC_SW_REQ8 (0x20)
#define DVFSRC_EMI_REQUEST (0x24)
#define DVFSRC_EMI_REQUEST2 (0x28)
#define DVFSRC_EMI_REQUEST3 (0x2C)
#define DVFSRC_EMI_REQUEST4 (0x30)
#define DVFSRC_EMI_REQUEST5 (0x34)
#define DVFSRC_EMI_REQUEST6 (0x38)
#define DVFSRC_EMI_HRT (0x3C)
#define DVFSRC_EMI_HRT2 (0x40)
#define DVFSRC_EMI_HRT3 (0x44)
#define DVFSRC_EMI_QOS0 (0x48)
#define DVFSRC_EMI_QOS1 (0x4C)
#define DVFSRC_EMI_QOS2 (0x50)
#define DVFSRC_EMI_MD2SPM0 (0x54)
#define DVFSRC_EMI_MD2SPM1 (0x58)
#define DVFSRC_EMI_MD2SPM2 (0x5C)
#define DVFSRC_EMI_MD2SPM0_T (0x60)
#define DVFSRC_EMI_MD2SPM1_T (0x64)
#define DVFSRC_EMI_MD2SPM2_T (0x68)
#define DVFSRC_VCORE_REQUEST (0x6C)
#define DVFSRC_VCORE_REQUEST2 (0x70)
#define DVFSRC_VCORE_REQUEST3 (0x74)
#define DVFSRC_VCORE_REQUEST4 (0x78)
#define DVFSRC_VCORE_HRT (0x7C)
#define DVFSRC_VCORE_HRT2 (0x80)
#define DVFSRC_VCORE_HRT3 (0x84)
#define DVFSRC_VCORE_QOS0 (0x88)
#define DVFSRC_VCORE_QOS1 (0x8C)
#define DVFSRC_VCORE_QOS2 (0x90)
#define DVFSRC_VCORE_MD2SPM0 (0x94)
#define DVFSRC_VCORE_MD2SPM1 (0x98)
#define DVFSRC_VCORE_MD2SPM2 (0x9C)
#define DVFSRC_VCORE_MD2SPM0_T (0xA0)
#define DVFSRC_VCORE_MD2SPM1_T (0xA4)
#define DVFSRC_VCORE_MD2SPM2_T (0xA8)
#define DVFSRC_MD_VSRAM_REMAP (0xBC)
#define DVFSRC_HALT_SW_CONTROL (0xC0)
#define DVFSRC_INT (0xC4)
#define DVFSRC_INT_EN (0xC8)
#define DVFSRC_INT_CLR (0xCC)
#define DVFSRC_BW_MON_WINDOW (0xD0)
#define DVFSRC_BW_MON_THRES_1 (0xD4)
#define DVFSRC_BW_MON_THRES_2 (0xD8)
#define DVFSRC_MD_TURBO (0xDC)
#define DVFSRC_PCIE_VCORE_REQ (0xE0)
#define DVFSRC_VCORE_USER_REQ (0xE4)
#define DVFSRC_BW_USER_REQ (0xE8)
#define DVFSRC_DEBOUNCE_FOUR (0xF0)
#define DVFSRC_DEBOUNCE_RISE_FALL (0xF4)
#define DVFSRC_TIMEOUT_NEXTREQ (0xF8)
#define DVFSRC_LEVEL_LABEL_0_1 (0x100)
#define DVFSRC_LEVEL_LABEL_2_3 (0x104)
#define DVFSRC_LEVEL_LABEL_4_5 (0x108)
#define DVFSRC_LEVEL_LABEL_6_7 (0x10C)
#define DVFSRC_LEVEL_LABEL_8_9 (0x110)
#define DVFSRC_LEVEL_LABEL_10_11 (0x114)
#define DVFSRC_LEVEL_LABEL_12_13 (0x118)
#define DVFSRC_LEVEL_LABEL_14_15 (0x11C)
#define DVFSRC_MM_BW_0 (0x200)
#define DVFSRC_MM_BW_1 (0x204)
#define DVFSRC_MM_BW_2 (0x208)
#define DVFSRC_MM_BW_3 (0x20C)
#define DVFSRC_MM_BW_4 (0x210)
#define DVFSRC_MM_BW_5 (0x214)
#define DVFSRC_MM_BW_6 (0x218)
#define DVFSRC_MM_BW_7 (0x21C)
#define DVFSRC_MM_BW_8 (0x220)
#define DVFSRC_MM_BW_9 (0x224)
#define DVFSRC_MM_BW_10 (0x228)
#define DVFSRC_MM_BW_11 (0x22C)
#define DVFSRC_MM_BW_12 (0x230)
#define DVFSRC_MM_BW_13 (0x234)
#define DVFSRC_MM_BW_14 (0x238)
#define DVFSRC_MM_BW_15 (0x23C)
#define DVFSRC_MD_BW_0 (0x240)
#define DVFSRC_MD_BW_1 (0x244)
#define DVFSRC_MD_BW_2 (0x248)
#define DVFSRC_MD_BW_3 (0x24C)
#define DVFSRC_MD_BW_4 (0x250)
#define DVFSRC_MD_BW_5 (0x254)
#define DVFSRC_MD_BW_6 (0x258)
#define DVFSRC_MD_BW_7 (0x25C)
#define DVFSRC_SW_BW_0 (0x260)
#define DVFSRC_SW_BW_1 (0x264)
#define DVFSRC_SW_BW_2 (0x268)
#define DVFSRC_SW_BW_3 (0x26C)
#define DVFSRC_SW_BW_4 (0x270)
#define DVFSRC_SW_BW_5 (0x274)
#define DVFSRC_SW_BW_6 (0x278)
#define DVFSRC_QOS_EN (0x280)
#define DVFSRC_MD_BW_URG (0x284)
#define DVFSRC_ISP_HRT (0x290)
#define DVFSRC_HRT_BW_BASE (0x294)
#define DVFSRC_SEC_SW_REQ (0x304)
#define DVFSRC_EMI_MON_DEBOUNCE_TIME (0x308)
#define DVFSRC_MD_LATENCY_IMPROVE (0x30C)
#define DVFSRC_BASIC_CONTROL_3 (0x310)
#define DVFSRC_DEBOUNCE_TIME (0x314)
#define DVFSRC_LEVEL_MASK (0x318)
#define DVFSRC_DEFAULT_OPP (0x31C)
#define DVFSRC_95MD_SCEN_EMI0 (0x500)
#define DVFSRC_95MD_SCEN_EMI1 (0x504)
#define DVFSRC_95MD_SCEN_EMI2 (0x508)
#define DVFSRC_95MD_SCEN_EMI3 (0x50C)
#define DVFSRC_95MD_SCEN_EMI0_T (0x510)
#define DVFSRC_95MD_SCEN_EMI1_T (0x514)
#define DVFSRC_95MD_SCEN_EMI2_T (0x518)
#define DVFSRC_95MD_SCEN_EMI3_T (0x51C)
#define DVFSRC_95MD_SCEN_EMI4 (0x520)
#define DVFSRC_95MD_SCEN_BW0 (0x524)
#define DVFSRC_95MD_SCEN_BW1 (0x528)
#define DVFSRC_95MD_SCEN_BW2 (0x52C)
#define DVFSRC_95MD_SCEN_BW3 (0x530)
#define DVFSRC_95MD_SCEN_BW0_T (0x534)
#define DVFSRC_95MD_SCEN_BW1_T (0x538)
#define DVFSRC_95MD_SCEN_BW2_T (0x53C)
#define DVFSRC_95MD_SCEN_BW3_T (0x540)
#define DVFSRC_95MD_SCEN_BW4 (0x544)
#define DVFSRC_MD_LEVEL_SW_REG (0x548)
#define DVFSRC_RSRV_0 (0x600)
#define DVFSRC_RSRV_1 (0x604)
#define DVFSRC_RSRV_2 (0x608)
#define DVFSRC_RSRV_3 (0x60C)
#define DVFSRC_RSRV_4 (0x610)
#define DVFSRC_RSRV_5 (0x614)
#define DVFSRC_SPM_RESEND (0x630)
#define DVFSRC_DEBUG_STA_0 (0x700)
#define DVFSRC_DEBUG_STA_1 (0x704)
#define DVFSRC_DEBUG_STA_2 (0x708)
#define DVFSRC_DEBUG_STA_3 (0x70C)
#define DVFSRC_DEBUG_STA_4 (0x710)
#define DVFSRC_DEBUG_STA_5 (0x714)
#define DVFSRC_DEBUG_STA_6 (0x718)
#define DVFSRC_EMI_REQUEST7 (0x800)
#define DVFSRC_EMI_HRT_1 (0x804)
#define DVFSRC_EMI_HRT2_1 (0x808)
#define DVFSRC_EMI_HRT3_1 (0x80C)
#define DVFSRC_EMI_QOS3 (0x810)
#define DVFSRC_EMI_QOS4 (0x814)
#define DVFSRC_DDR_REQUEST (0xA00)
#define DVFSRC_DDR_REQUEST2 (0xA04)
#define DVFSRC_DDR_REQUEST3 (0xA08)
#define DVFSRC_DDR_REQUEST4 (0xA0C)
#define DVFSRC_DDR_REQUEST5 (0xA10)
#define DVFSRC_DDR_REQUEST6 (0xA14)
#define DVFSRC_DDR_REQUEST7 (0xA18)
#define DVFSRC_DDR_HRT (0xA1C)
#define DVFSRC_DDR_HRT2 (0xA20)
#define DVFSRC_DDR_HRT3 (0xA24)
#define DVFSRC_DDR_HRT_1 (0xA28)
#define DVFSRC_DDR_HRT2_1 (0xA2C)
#define DVFSRC_DDR_HRT3_1 (0xA30)
#define DVFSRC_DDR_QOS0 (0xA34)
#define DVFSRC_DDR_QOS1 (0xA38)
#define DVFSRC_DDR_QOS2 (0xA3C)
#define DVFSRC_DDR_QOS3 (0xA40)
#define DVFSRC_DDR_QOS4 (0xA44)
#define DVFSRC_DDR_MD2SPM0 (0xA48)
#define DVFSRC_DDR_MD2SPM1 (0xA4C)
#define DVFSRC_DDR_MD2SPM2 (0xA50)
#define DVFSRC_DDR_MD2SPM0_T (0xA54)
#define DVFSRC_DDR_MD2SPM1_T (0xA58)
#define DVFSRC_DDR_MD2SPM2_T (0xA5C)
#if defined(DVFSRC_IP_V2_1)
#define DVFSRC_HRT_REQ_UNIT (0xA84)
#define DVSFRC_HRT_REQ_MD_URG (0xA88)
#define DVFSRC_HRT_REQ_MD_BW_0 (0xA8C)
#define DVFSRC_HRT_REQ_MD_BW_1 (0xA90)
#define DVFSRC_HRT_REQ_MD_BW_2 (0xA94)
#define DVFSRC_HRT_REQ_MD_BW_3 (0xA98)
#define DVFSRC_HRT_REQ_MD_BW_4 (0xA9C)
#define DVFSRC_HRT_REQ_MD_BW_5 (0xAA0)
#define DVFSRC_HRT_REQ_MD_BW_6 (0xAA4)
#define DVFSRC_HRT_REQ_MD_BW_7 (0xAA8)
#define DVFSRC_HRT1_REQ_MD_BW_0 (0xAAC)
#define DVFSRC_HRT1_REQ_MD_BW_1 (0xAB0)
#define DVFSRC_HRT1_REQ_MD_BW_2 (0xAB4)
#define DVFSRC_HRT1_REQ_MD_BW_3 (0xAB8)
#define DVFSRC_HRT1_REQ_MD_BW_4 (0xABC)
#define DVFSRC_HRT1_REQ_MD_BW_5 (0xAC0)
#define DVFSRC_HRT1_REQ_MD_BW_6 (0xAC4)
#define DVFSRC_HRT1_REQ_MD_BW_7 (0xAC8)
#define DVFSRC_HRT_REQ_MD_BW_8 (0xACC)
#define DVFSRC_HRT_REQ_MD_BW_9 (0xAD0)
#define DVFSRC_HRT_REQ_MD_BW_10 (0xAD4)
#define DVFSRC_HRT1_REQ_MD_BW_8 (0xAD8)
#define DVFSRC_HRT1_REQ_MD_BW_9 (0xADC)
#define DVFSRC_HRT1_REQ_MD_BW_10 (0xAE0)
#define DVFSRC_HRT_REQ_BW_SW_REG (0xAE4)
#define DVFSRC_HRT_REQUEST (0xAE8)
#define DVFSRC_HRT_HIGH_2 (0xAEC)
#define DVFSRC_HRT_HIGH_1 (0xAF0)
#define DVFSRC_HRT_HIGH (0xAF4)
#define DVFSRC_HRT_LOW_2 (0xAF8)
#define DVFSRC_HRT_LOW_1 (0xAFC)
#define DVFSRC_HRT_LOW (0xB00)
#define DVFSRC_DDR_ADD_REQUEST (0xB04)
#define DVFSRC_LAST (0xB08)
#define DVFSRC_LAST_L (0xB0C)
#define DVFSRC_MD_SCENARIO (0xB10)
#define DVFSRC_RECORD_0_0 (0xB14)
#define DVFSRC_RECORD_0_1 (0xB18)
#define DVFSRC_RECORD_0_2 (0xB1C)
#define DVFSRC_RECORD_0_3 (0xB20)
#define DVFSRC_RECORD_0_4 (0xB24)
#define DVFSRC_RECORD_0_5 (0xB28)
#define DVFSRC_RECORD_0_6 (0xB2C)
#define DVFSRC_RECORD_0_L_0 (0xBF4)
#define DVFSRC_RECORD_0_L_1 (0xBF8)
#define DVFSRC_RECORD_0_L_2 (0xBFC)
#define DVFSRC_RECORD_0_L_3 (0xC00)
#define DVFSRC_RECORD_0_L_4 (0xC04)
#define DVFSRC_RECORD_0_L_5 (0xC08)
#define DVFSRC_RECORD_0_L_6 (0xC0C)
#else
#define DVFSRC_HRT_REQ_UNIT (0xA60)
#define DVSFRC_HRT_REQ_MD_URG (0xA64)
#define DVFSRC_HRT_REQ_MD_BW_0 (0xA68)
#define DVFSRC_HRT_REQ_MD_BW_1 (0xA6C)
#define DVFSRC_HRT_REQ_MD_BW_2 (0xA70)
#define DVFSRC_HRT_REQ_MD_BW_3 (0xA74)
#define DVFSRC_HRT_REQ_MD_BW_4 (0xA78)
#define DVFSRC_HRT_REQ_MD_BW_5 (0xA7C)
#define DVFSRC_HRT_REQ_MD_BW_6 (0xA80)
#define DVFSRC_HRT_REQ_MD_BW_7 (0xA84)
#define DVFSRC_HRT1_REQ_MD_BW_0 (0xA88)
#define DVFSRC_HRT1_REQ_MD_BW_1 (0xA8C)
#define DVFSRC_HRT1_REQ_MD_BW_2 (0xA90)
#define DVFSRC_HRT1_REQ_MD_BW_3 (0xA94)
#define DVFSRC_HRT1_REQ_MD_BW_4 (0xA98)
#define DVFSRC_HRT1_REQ_MD_BW_5 (0xA9C)
#define DVFSRC_HRT1_REQ_MD_BW_6 (0xAA0)
#define DVFSRC_HRT1_REQ_MD_BW_7 (0xAA4)
#define DVFSRC_HRT_REQ_MD_BW_8 (0xAA8)
#define DVFSRC_HRT_REQ_MD_BW_9 (0xAAC)
#define DVFSRC_HRT_REQ_MD_BW_10 (0xAB0)
#define DVFSRC_HRT1_REQ_MD_BW_8 (0xAB4)
#define DVFSRC_HRT1_REQ_MD_BW_9 (0xAB8)
#define DVFSRC_HRT1_REQ_MD_BW_10 (0xABC)
#define DVFSRC_HRT_REQ_BW_SW_REG (0xAC0)
#define DVFSRC_HRT_REQUEST (0xAC4)
#define DVFSRC_HRT_HIGH_2 (0xAC8)
#define DVFSRC_HRT_HIGH_1 (0xACC)
#define DVFSRC_HRT_HIGH (0xAD0)
#define DVFSRC_HRT_LOW_2 (0xAD4)
#define DVFSRC_HRT_LOW_1 (0xAD8)
#define DVFSRC_HRT_LOW (0xADC)
#define DVFSRC_DDR_ADD_REQUEST (0xAE0)
#define DVFSRC_LAST (0xAE4)
#define DVFSRC_LAST_L (0xAE8)
#define DVFSRC_MD_SCENARIO (0xAEC)
#define DVFSRC_RECORD_0_0 (0xAF0)
#define DVFSRC_RECORD_0_1 (0xAF4)
#define DVFSRC_RECORD_0_2 (0xAF8)
#define DVFSRC_RECORD_0_3 (0xAFC)
#define DVFSRC_RECORD_0_4 (0xB00)
#define DVFSRC_RECORD_0_5 (0xB04)
#define DVFSRC_RECORD_0_6 (0xB08)
#define DVFSRC_RECORD_0_7 (0xB0C)
#define DVFSRC_RECORD_0_L_0 (0xBF0)
#define DVFSRC_RECORD_0_L_1 (0xBF4)
#define DVFSRC_RECORD_0_L_2 (0xBF8)
#define DVFSRC_RECORD_0_L_3 (0xBFC)
#define DVFSRC_RECORD_0_L_4 (0xC00)
#define DVFSRC_RECORD_0_L_5 (0xC04)
#define DVFSRC_RECORD_0_L_6 (0xC08)
#define DVFSRC_RECORD_0_L_7 (0xC0C)
#endif
#define DVFSRC_EMI_REQUEST8 (0xCF0)
#define DVFSRC_DDR_REQUEST8 (0xCF4)
#define DVFSRC_EMI_HRT_2 (0xCF8)
#define DVFSRC_EMI_HRT2_2 (0xCFC)
#define DVFSRC_EMI_HRT3_2 (0xD00)
#define DVFSRC_EMI_QOS5 (0xD04)
#define DVFSRC_EMI_QOS6 (0xD08)
#define DVFSRC_DDR_HRT_2 (0xD0C)
#define DVFSRC_DDR_HRT2_2 (0xD10)
#define DVFSRC_DDR_HRT3_2 (0xD14)
#define DVFSRC_DDR_QOS5 (0xD18)
#define DVFSRC_DDR_QOS6 (0xD1C)
#define DVFSRC_VCORE_REQUEST5 (0xD20)
#define DVFSRC_VCORE_HRT_1 (0xD24)
#define DVFSRC_VCORE_HRT2_1 (0xD28)
#define DVFSRC_VCORE_HRT3_1 (0xD2C)
#define DVFSRC_VCORE_QOS3 (0xD30)
#define DVFSRC_VCORE_QOS4 (0xD34)
#define DVFSRC_HRT_HIGH_3 (0xD38)
#define DVFSRC_HRT_LOW_3 (0xD3C)
#define DVFSRC_BASIC_CONTROL_2 (0xD40)
#define DVFSRC_CURRENT_LEVEL (0xD44)
#define DVFSRC_TARGET_LEVEL (0xD48)
#define DVFSRC_LEVEL_LABEL_16_17 (0xD4C)
#define DVFSRC_LEVEL_LABEL_18_19 (0xD50)
#define DVFSRC_LEVEL_LABEL_20_21 (0xD54)
#define DVFSRC_LEVEL_LABEL_22_23 (0xD58)
#define DVFSRC_LEVEL_LABEL_24_25 (0xD5C)
#define DVFSRC_LEVEL_LABEL_26_27 (0xD60)
#define DVFSRC_LEVEL_LABEL_28_29 (0xD64)
#define DVFSRC_LEVEL_LABEL_30_31 (0xD68)
#define DVFSRC_CURRENT_FORCE (0xD6C)
#define DVFSRC_TARGET_FORCE (0xD70)
#define DVFSRC_EMI_ADD_REQUEST (0xD74)
/* DVFSRC_BASIC_CONTROL 0x0 */
#define DVFSRC_EN_SHIFT 0
#define DVFSRC_EN_MASK 0x1
#define DVFSRC_OUT_EN_SHIFT 8
#define DVFSRC_OUT_EN_MASK 0x1
#define FORCE_EN_CUR_SHIFT 14
#define FORCE_EN_CUR_MASK 0x1
#define FORCE_EN_TAR_SHIFT 15
#define FORCE_EN_TAR_MASK 0x1
/* DVFSRC_SW_REQX */
#define DDR_SW_AP_SHIFT 12
#define DDR_SW_AP_MASK 0x7
#define VCORE_SW_AP_SHIFT 4
#define VCORE_SW_AP_MASK 0x7
#define EMI_SW_AP_SHIFT 0
#define EMI_SW_AP_MASK 0x7
/* DVFSRC_VCORE_REQUEST 0x70 */
#define VCORE_SCP_GEAR_SHIFT 12
#define VCORE_SCP_GEAR_MASK 0x7
/* DVFSRC_LEVEL 0xFC */
#define CURRENT_LEVEL_SHIFT 0
#define CURRENT_LEVEL_MASK 0xFFFFFFFF
/* DVFSRC_FORCE 0x300 */
#define TARGET_FORCE_SHIFT 0
#define TARGET_FORCE_MASK 0xFFFFFFFF
#define CURRENT_FORCE_SHIFT 0
#define CURRENT_FORCE_MASK 0xFFFFFFFF
/* DVFSRC_DEBUG_STA_0 */
#define MD_EMI_URG_DEBUG_SHIFT 16
#define MD_EMI_URG_DEBUG_MASK 0x1
#define MD_SRC_CLK_DEBUG_SHIFT 17
#define MD_SRC_CLK_DEBUG_MASK 0x1
#define MD_EMI_VAL_DEBUG_SHIFT 0
#define MD_EMI_VAL_DEBUG_MASK 0xFFFF
/* DVFSRC_DEBUG_STA_4 */
#define MD_EMI_MD_IMP_SHIFT 19
#define MD_EMI_MD_IMP_MASK 0x7
/* DVSFRC_HRT_REQ_MD_URG */
#define MD_HRT_BW_URG_SHIFT 0
#define MD_HRT_BW_URG_MASK 0xFF
#define MD_HRT_BW_URG1_SHIFT 8
#define MD_HRT_BW_URG1_MASK 0xFF
/* DVFSRC_HRTX_REQ_MD_BW_x */
#define MD_HRT_BW_MASK 0x3FF
#define DEBUG_MDTURBO_SHIFT 18
#define DEBUG_MDTURBO_MASK 0x1
#define DEBUG_MD_RIS_DDR_SHIFT 29
#define DEBUG_MD_RIS_DDR_MASK 0x7
#define DEBUG_HIFI_RIS_DDR_SHIFT 22
#define DEBUG_HIFI_RIS_DDR_MASK 0x7
#define DEBUG_STA2_EMI_TOTAL_SHIFT 0
#define DEBUG_STA2_EMI_TOTAL_MASK 0xFFF
#define DEBUG_STA2_SCP_SHIFT 14
#define DEBUG_STA2_SCP_MASK 0x1
#define DEBUG_STA2_PCIE_SHIFT 27
#define DEBUG_STA2_PCIE_MASK 0x1
#define DEBUG_STA2_MD_EMI_LATENCY_SHIFT 12
#define DEBUG_STA2_MD_EMI_LATENCY_MASK 0x3
#define DEBUG_STA2_HIFI_SCENARIO_SHIFT 16
#define DEBUG_STA2_HIFI_SCENARIO_MASK 0xFF
#define DEBUG_STA4_HRT_BW_REQ_SHIFT 16
#define DEBUG_STA4_HRT_BW_REQ_MASK 0x7
#define DEBUG_STA3_MD_HRT_BW_SHIFT 0
#define DEBUG_STA3_MD_HRT_BW_MASK 0x3FF
#if defined(DVFSRC_IP_V2_1)
#define RECORD_SHIFT 0x1C
#else
#define RECORD_SHIFT 0x20
#endif
#if defined(DVFSRC_IP_V2_1)
#define MD_TURBO_SWITCH_SHIFT 5
#define MD_TURBO_SWITCH_MASK 0x1
#define RECORD_HIFI_DDR_LATENCY_REQ 15
#define RECORD_HIFI_DDR_LATENCY_MASK 0x7
#define RECORD_HRT_BW_REQ_SHIFT 2
#define RECORD_HRT_BW_REQ_MASK 0x7
#define RECORD_MD_DDR_LATENCY_REQ 9
#define RECORD_MD_DDR_LATENCY_MASK 0x7
#elif defined(DVFSRC_IP_V2_2)
#define RECORD_HRT_BW_REQ_SHIFT 21
#define RECORD_HRT_BW_REQ_MASK 0x7
#endif
#endif /* __MTK_DVFSRC_REG_V2_H */
@@ -0,0 +1,55 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __MTK_DVFSRC_SMC_REG_H
#define __MTK_DVFSRC_SMC_REG_H
/**************************************
* Define and Declare
**************************************/
/* SPM REGISTER*/
#if defined(CONFIG_MACH_MT6877)
#define POWERON_CONFIG_EN (0x0000)
#define SPM_SW_FLAG_0 (0x0600)
#define SPM_PC_STA (0x0194)
#define SPM_DVFS_LEVEL (0x038C)
#define SPM_DVS_DFS_LEVEL (0x0390)
#define SPM_DVFS_STA (0x0388)
#define SPM_DVFS_MISC (0x03A0)
#define SPM_DVFS_CMD0 (0x0310)
#define SPM_DVFS_CMD1 (0x0314)
#define SPM_DVFS_CMD2 (0x0318)
#define SPM_DVFS_CMD3 (0x031C)
#define SPM_DVFS_CMD4 (0x0320)
#define SPM_DVFS_CMD16 (0x0350)
#define SPM_DVFS_CMD17 (0x0354)
#define SPM_DVFS_CMD18 (0x0358)
#define SPM_DVFS_CMD19 (0x035C)
#define SPM_DVFS_CMD20 (0x0360)
#else
#define POWERON_CONFIG_EN (0x0000)
#define SPM_SW_FLAG_0 (0x0600)
#define SPM_PC_STA (0x0194)
#define SPM_DVFS_LEVEL (0x04A4)
#define SPM_DVS_DFS_LEVEL (0x04F8)
#define SPM_DVFS_STA (0x01F8)
#define SPM_DVFS_MISC (0x04AC)
#define SPM_DVFS_CMD0 (0x0710)
#define SPM_DVFS_CMD1 (0x0714)
#define SPM_DVFS_CMD2 (0x0718)
#define SPM_DVFS_CMD3 (0x071C)
#define SPM_DVFS_CMD4 (0x0720)
#define SPM_DVFS_CMD7 (0x072C)
#define SPM_DVFS_CMD8 (0x0730)
#define SPM_DVFS_CMD16 (0x0750)
#define SPM_DVFS_CMD17 (0x0754)
#define SPM_DVFS_CMD18 (0x0758)
#define SPM_DVFS_CMD19 (0x075C)
#define SPM_DVFS_CMD20 (0x0760)
#endif
#endif /* __MTK_DVFSRC_REG_V2_H */
+791
View File
@@ -0,0 +1,791 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2018 MediaTek Inc.
*/
#include <linux/devfreq.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include <linux/notifier.h>
#include <linux/pm_qos.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include "governor.h"
#include <helio-dvfsrc.h>
#include <helio-dvfsrc-opp.h>
#include <mtk_dvfsrc_reg.h>
#include <mtk_spm_vcore_dvfs.h>
#include <mtk_spm_vcore_dvfs_ipi.h>
#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
#include <v1/sspm_ipi.h>
#include <sspm_ipi_pin.h>
#endif
#include <mtk_vcorefs_governor.h>
#include <mt-plat/aee.h>
#include <linux/regulator/consumer.h>
static struct regulator *vcore_reg_id;
static DEFINE_MUTEX(sw_req1_mutex);
__weak void helio_dvfsrc_platform_init(struct helio_dvfsrc *dvfsrc) { }
__weak void spm_check_status_before_dvfs(void) { }
__weak int spm_dvfs_flag_init(void) { return 0; }
__weak int vcore_opp_init(void) { return 0; }
__weak int spm_vcorefs_get_dvfs_opp(void) { return 0; }
static struct opp_profile opp_table[VCORE_DVFS_OPP_NUM];
struct helio_dvfsrc *dvfsrc;
int get_cur_vcore_dvfs_opp(void)
{
int dvfsrc_level_bit = dvfsrc_read(dvfsrc, DVFSRC_LEVEL) >> 16;
int dvfsrc_level = 0;
for (dvfsrc_level = 0;
dvfsrc_level < VCORE_DVFS_OPP_NUM - 1; dvfsrc_level++)
if ((dvfsrc_level_bit & (1 << dvfsrc_level)) > 0)
break;
return VCORE_DVFS_OPP_NUM - dvfsrc_level - 1;
}
void dvfsrc_update_opp_table(void)
{
struct opp_profile *opp_ctrl_table = opp_table;
int opp;
mutex_lock(&sw_req1_mutex);
for (opp = 0; opp < VCORE_DVFS_OPP_NUM; opp++)
opp_ctrl_table[opp].vcore_uv = vcorefs_get_vcore_by_steps(opp);
mutex_unlock(&sw_req1_mutex);
}
char *dvfsrc_get_opp_table_info(char *p)
{
struct opp_profile *opp_ctrl_table = opp_table;
int i;
char *buff_end = p + PAGE_SIZE;
for (i = 0; i < VCORE_DVFS_OPP_NUM; i++) {
p += snprintf(p, buff_end - p,
"[OPP%d] vcore_uv: %d (0x%x)\n",
i, opp_ctrl_table[i].vcore_uv,
vcore_uv_to_pmic(opp_ctrl_table[i].vcore_uv));
p += snprintf(p, buff_end - p, "[OPP%d] ddr_khz : %d\n",
i, opp_ctrl_table[i].ddr_khz);
p += snprintf(p, buff_end - p, "\n");
}
for (i = 0; i < VCORE_DVFS_OPP_NUM; i++)
p += snprintf(p, buff_end - p,
"OPP%d : %u\n", i, opp_ctrl_table[i].vcore_uv);
return p;
}
#if !defined(CONFIG_MACH_MT6771) && !defined(CONFIG_MACH_MT6765)
int dvfsrc_get_bw(int type)
{
int ret = 0;
int i;
switch (type) {
case QOS_TOTAL:
ret = dvfsrc_sram_read(dvfsrc, QOS_TOTAL_BW);
break;
case QOS_CPU:
ret = dvfsrc_sram_read(dvfsrc, QOS_CPU_BW);
break;
case QOS_MM:
ret = dvfsrc_sram_read(dvfsrc, QOS_MM_BW);
break;
case QOS_GPU:
ret = dvfsrc_sram_read(dvfsrc, QOS_GPU_BW);
break;
case QOS_MD_PERI:
ret = dvfsrc_sram_read(dvfsrc, QOS_MD_PERI_BW);
break;
case QOS_TOTAL_AVE:
for (i = 0; i < QOS_TOTAL_BW_BUF_SIZE; i++)
ret += dvfsrc_sram_read(dvfsrc, QOS_TOTAL_BW_BUF(i));
ret /= QOS_TOTAL_BW_BUF_SIZE;
break;
default:
break;
}
return ret;
}
#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
void dvfsrc_update_sspm_vcore_opp_table(int opp, unsigned int vcore_uv)
{
struct qos_data qos_d;
qos_d.cmd = QOS_IPI_VCORE_OPP;
qos_d.u.vcore_opp.opp = opp;
qos_d.u.vcore_opp.vcore_uv = vcore_uv;
qos_ipi_to_sspm_command(&qos_d, 3);
}
void dvfsrc_update_sspm_ddr_opp_table(int opp, unsigned int ddr_khz)
{
struct qos_data qos_d;
qos_d.cmd = QOS_IPI_DDR_OPP;
qos_d.u.ddr_opp.opp = opp;
qos_d.u.ddr_opp.ddr_khz = ddr_khz;
qos_ipi_to_sspm_command(&qos_d, 3);
}
#endif
#endif
void dvfsrc_init_opp_table(void)
{
struct opp_profile *opp_ctrl_table = opp_table;
int opp;
mutex_lock(&sw_req1_mutex);
dvfsrc->curr_vcore_uv = vcorefs_get_curr_vcore();
dvfsrc->curr_ddr_khz = vcorefs_get_curr_ddr();
pr_info("curr_vcore_uv: %u, curr_ddr_khz: %u\n",
dvfsrc->curr_vcore_uv,
dvfsrc->curr_ddr_khz);
for (opp = 0; opp < VCORE_DVFS_OPP_NUM; opp++) {
opp_ctrl_table[opp].vcore_uv = vcorefs_get_vcore_by_steps(opp);
opp_ctrl_table[opp].ddr_khz = vcorefs_get_ddr_by_steps(opp);
#ifdef CONFIG_MTK_TINYSYS_SSPM_SUPPORT
dvfsrc_update_sspm_vcore_opp_table(opp,
opp_ctrl_table[opp].vcore_uv);
dvfsrc_update_sspm_ddr_opp_table(opp,
opp_ctrl_table[opp].ddr_khz);
#endif
pr_info("opp %u: vcore_uv: %u, ddr_khz: %u\n", opp,
opp_ctrl_table[opp].vcore_uv,
opp_ctrl_table[opp].ddr_khz);
}
spm_vcorefs_pwarp_cmd();
mutex_unlock(&sw_req1_mutex);
}
int is_qos_can_work(void)
{
if (dvfsrc)
return dvfsrc->enable;
return 0;
}
static struct devfreq_dev_profile helio_devfreq_profile = {
.polling_ms = 0,
};
static int helio_dvfsrc_reg_config(struct helio_dvfsrc *dvfsrc,
struct reg_config *config)
{
return 0;
}
static int helio_governor_event_handler(struct devfreq *devfreq,
unsigned int event, void *data)
{
struct helio_dvfsrc *dvfsrc;
dvfsrc = dev_get_drvdata(devfreq->dev.parent);
switch (event) {
case DEVFREQ_GOV_SUSPEND:
helio_dvfsrc_reg_config(dvfsrc, dvfsrc->suspend_config);
break;
case DEVFREQ_GOV_RESUME:
helio_dvfsrc_reg_config(dvfsrc, dvfsrc->resume_config);
break;
default:
break;
}
return 0;
}
static struct devfreq_governor helio_dvfsrc_governor = {
.name = "helio_dvfsrc",
.event_handler = helio_governor_event_handler,
};
static void helio_dvfsrc_enable(struct helio_dvfsrc *dvfsrc)
{
mutex_lock(&sw_req1_mutex);
#if !defined(CONFIG_MACH_MT6771) && !defined(CONFIG_MACH_MT6765)
dvfsrc_write(dvfsrc, DVFSRC_VCORE_REQUEST,
(dvfsrc_read(dvfsrc, DVFSRC_VCORE_REQUEST) & ~(0x3 << 20)));
dvfsrc_write(dvfsrc, DVFSRC_EMI_REQUEST,
(dvfsrc_read(dvfsrc, DVFSRC_EMI_REQUEST) & ~(0x3 << 20)));
#endif
dvfsrc->enable = 1;
mutex_unlock(&sw_req1_mutex);
}
int is_dvfsrc_opp_fixed(void)
{
if (!is_qos_can_work())
return 1;
if ((spm_dvfs_flag_init()&
(SPM_FLAG_DIS_VCORE_DVS|SPM_FLAG_DIS_VCORE_DFS)) != 0)
return 1;
if (dvfsrc->skip)
return 1;
#if defined(CONFIG_MACH_MT6771)
if (is_force_opp_enable())
return 1;
#endif
return 0;
}
static int commit_data(struct helio_dvfsrc *dvfsrc, int type, int data)
{
int ret = 0;
int level = 0;
int opp = 0;
int last_cnt = 0;
int opp_uv;
int vcore_uv = 0;
mutex_lock(&sw_req1_mutex);
if (!dvfsrc->enable)
goto out;
if (is_force_opp_enable())
goto out;
if (dvfsrc->skip)
goto out;
spm_check_status_before_dvfs();
if (type == PM_QOS_EMI_OPP ||
type == PM_QOS_VCORE_OPP ||
type == PM_QOS_VCORE_DVFS_FIXED_OPP) {
last_cnt = dvfsrc_read(dvfsrc, DVFSRC_LAST);
ret = wait_for_completion
(is_dvfsrc_in_progress(dvfsrc) == 0, DVFSRC_TIMEOUT);
if (ret) {
pr_info("[%s] wait no idle, class: %d, data: 0x%x",
__func__, type, data);
pr_info("rc_level: 0x%x (last: %d -> %d)\n",
dvfsrc_read(dvfsrc, DVFSRC_LEVEL),
last_cnt, dvfsrc_read(dvfsrc, DVFSRC_LAST));
/* aee_kernel_warning(NULL); */
/* goto out; */
}
}
switch (type) {
case PM_QOS_MEMORY_BANDWIDTH:
if (data == PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE)
break;
if (dvfsrc->log_mask & (0x1 << type))
pr_info("[%s] class: %d, data: 0x%x\n",
__func__, type, data);
dvfsrc_write(dvfsrc, DVFSRC_SW_BW_0, data / 100);
break;
case PM_QOS_CPU_MEMORY_BANDWIDTH:
dvfsrc_write(dvfsrc, DVFSRC_SW_BW_1, data / 100);
break;
case PM_QOS_GPU_MEMORY_BANDWIDTH:
dvfsrc_write(dvfsrc, DVFSRC_SW_BW_2, data / 100);
break;
case PM_QOS_MM_MEMORY_BANDWIDTH:
#if defined(CONFIG_MACH_MT6771)
dvfsrc_write(dvfsrc, DVFSRC_SW_BW_3, data / 75); /* x 4/3 */
#else
dvfsrc_write(dvfsrc, DVFSRC_SW_BW_3, data / 100);
#endif
break;
case PM_QOS_MD_PERI_MEMORY_BANDWIDTH:
dvfsrc_write(dvfsrc, DVFSRC_SW_BW_4, data / 100);
break;
case PM_QOS_EMI_OPP:
if (dvfsrc->log_mask & (0x1 << type))
pr_info("[%s] class: %d, data: 0x%x\n",
__func__, type, data);
if (data >= DDR_OPP_NUM)
data = DDR_OPP_NUM - 1;
else if (data < 0)
data = DDR_OPP_NUM - 1;
level = DDR_OPP_NUM - data - 1;
dvfsrc_write(dvfsrc, DVFSRC_SW_REQ,
(dvfsrc_read(dvfsrc, DVFSRC_SW_REQ)
& ~(0x3)) | level);
udelay(1);
ret = wait_for_completion
(is_dvfsrc_in_progress(dvfsrc) == 0, DVFSRC_TIMEOUT);
udelay(1);
#if defined(CONFIG_MACH_MT6771)
opp = get_min_opp_for_ddr(data);
ret = wait_for_completion(spm_vcorefs_get_dvfs_opp() <= opp,
SPM_DVFS_TIMEOUT);
#else
ret = wait_for_completion
(get_dvfsrc_level(dvfsrc) >= emi_to_vcore_dvfs_level[level],
SPM_DVFS_TIMEOUT);
#endif
if (ret < 0) {
pr_info
("[%s] wair not complete, class: %d, data: 0x%x\n",
__func__, type, data);
spm_vcorefs_dump_dvfs_regs(NULL);
aee_kernel_warning("VCOREFS",
"emi_opp cannot be done.");
}
break;
case PM_QOS_VCORE_OPP:
if (dvfsrc->log_mask & (0x1 << type))
pr_info("[%s] class: %d, data: 0x%x\n",
__func__, type, data);
if (data >= VCORE_OPP_NUM)
data = VCORE_OPP_NUM - 1;
else if (data < 0)
data = VCORE_OPP_NUM - 1;
level = ((VCORE_OPP_NUM - data - 1) << 24);
dvfsrc_write(dvfsrc, DVFSRC_VCORE_REQUEST2,
(dvfsrc_read(dvfsrc, DVFSRC_VCORE_REQUEST2)
& ~(0x03000000)) | level);
udelay(1);
ret = wait_for_completion
(is_dvfsrc_in_progress(dvfsrc) == 0, DVFSRC_TIMEOUT);
udelay(1);
#if defined(CONFIG_MACH_MT6771)
opp = get_min_opp_for_vcore(data);
ret = wait_for_completion(spm_vcorefs_get_dvfs_opp() <= opp,
SPM_DVFS_TIMEOUT);
#else
ret = wait_for_completion
(get_dvfsrc_level(dvfsrc) >= vcore_to_vcore_dvfs_level[level],
SPM_DVFS_TIMEOUT);
#endif
if (ret < 0) {
pr_info
("[%s] not complete, class: %d, data: 0x%x\n",
__func__, type, data);
spm_vcorefs_dump_dvfs_regs(NULL);
aee_kernel_warning("VCOREFS",
"vcore_opp cannot be done.");
}
if (vcore_reg_id) {
vcore_uv = regulator_get_voltage(vcore_reg_id);
opp_uv = get_vcore_opp_volt(opp);
if (vcore_uv < opp_uv) {
pr_info("DVFS FAIL= %d %d 0x%08x %08x\n",
vcore_uv, opp_uv,
dvfsrc_read(dvfsrc, DVFSRC_LEVEL),
spm_vcorefs_get_dvfs_opp());
aee_kernel_warning("DVFSRC",
"VCORE failed.",
__func__);
}
}
break;
case PM_QOS_VCORE_DVFS_FIXED_OPP:
if (dvfsrc->log_mask & (0x1 << type))
pr_info("[%s] class: %d, data: 0x%x\n",
__func__, type, data);
if (data >= VCORE_DVFS_OPP_NUM)
data = VCORE_DVFS_OPP_NUM;
if (data == VCORE_DVFS_OPP_NUM) { /* no fix opp*/
dvfsrc_write(dvfsrc, DVFSRC_BASIC_CONTROL,
(dvfsrc_read(dvfsrc,
DVFSRC_BASIC_CONTROL) & ~(1 << 15)));
dvfsrc_write(dvfsrc, DVFSRC_FORCE,
dvfsrc_read(dvfsrc, DVFSRC_FORCE)
& 0xFFFF0000);
} else { /* fix opp */
level = 1 << (VCORE_DVFS_OPP_NUM - data - 1);
dvfsrc_write(dvfsrc, DVFSRC_FORCE, level);
dvfsrc_write(dvfsrc, DVFSRC_BASIC_CONTROL,
(dvfsrc_read(dvfsrc, DVFSRC_BASIC_CONTROL)
| (1 << 15)));
#if defined(CONFIG_MACH_MT6771)
ret = wait_for_completion
(spm_vcorefs_get_dvfs_opp() == data, SPM_DVFS_TIMEOUT);
#else
ret = wait_for_completion
(get_dvfsrc_level(dvfsrc) ==
vcore_dvfs_to_vcore_dvfs_level[level],
SPM_DVFS_TIMEOUT);
#endif
if (ret < 0) {
pr_info
("[%s] not complete, class: %d, data: 0x%x\n",
__func__, type, data);
spm_vcorefs_dump_dvfs_regs(NULL);
aee_kernel_exception("VCOREFS",
"dvfsrc cannot be done.");
}
}
break;
default:
break;
}
out:
mutex_unlock(&sw_req1_mutex);
return ret;
}
#if !defined(CONFIG_MACH_MT6771) && !defined(CONFIG_MACH_MT6765)
void dvfsrc_set_vcore_request(unsigned int mask, unsigned int vcore_level)
{
int r = 0;
unsigned int val = 0;
mutex_lock(&sw_req1_mutex);
/* check DVFS idle */
r = wait_for_completion
(is_dvfsrc_in_progress(dvfsrc) == 0, SPM_DVFS_TIMEOUT);
if (r < 0) {
spm_vcorefs_dump_dvfs_regs(NULL);
aee_kernel_exception("VCOREFS", "dvfsrc cannot be idle.");
goto out;
}
val = (spm_read(DVFSRC_VCORE_REQUEST) & ~mask) | vcore_level;
dvfsrc_write(dvfsrc, DVFSRC_VCORE_REQUEST, val);
r = wait_for_completion(
get_dvfsrc_level(dvfsrc) >= vcore_to_vcore_dvfs_level[vcore_level],
SPM_DVFS_TIMEOUT);
if (r < 0) {
spm_vcorefs_dump_dvfs_regs(NULL);
aee_kernel_exception("VCOREFS", "dvfsrc cannot be done.");
}
out:
mutex_unlock(&sw_req1_mutex);
}
#endif
static int pm_qos_memory_bw_notify(struct notifier_block *b,
unsigned long l, void *v)
{
struct helio_dvfsrc *dvfsrc;
dvfsrc = container_of(b, struct helio_dvfsrc, pm_qos_memory_bw_nb);
commit_data(dvfsrc, PM_QOS_MEMORY_BANDWIDTH, l);
return NOTIFY_OK;
}
static int pm_qos_cpu_memory_bw_notify(struct notifier_block *b,
unsigned long l, void *v)
{
struct helio_dvfsrc *dvfsrc;
dvfsrc = container_of(b, struct helio_dvfsrc, pm_qos_cpu_memory_bw_nb);
commit_data(dvfsrc, PM_QOS_CPU_MEMORY_BANDWIDTH, l);
return NOTIFY_OK;
}
static int pm_qos_gpu_memory_bw_notify(struct notifier_block *b,
unsigned long l, void *v)
{
struct helio_dvfsrc *dvfsrc;
dvfsrc = container_of(b, struct helio_dvfsrc, pm_qos_gpu_memory_bw_nb);
commit_data(dvfsrc, PM_QOS_GPU_MEMORY_BANDWIDTH, l);
return NOTIFY_OK;
}
static int pm_qos_mm_memory_bw_notify(struct notifier_block *b,
unsigned long l, void *v)
{
struct helio_dvfsrc *dvfsrc;
dvfsrc = container_of(b, struct helio_dvfsrc, pm_qos_mm_memory_bw_nb);
commit_data(dvfsrc, PM_QOS_MM_MEMORY_BANDWIDTH, l);
return NOTIFY_OK;
}
static int pm_qos_md_peri_memory_bw_notify(struct notifier_block *b,
unsigned long l, void *v)
{
struct helio_dvfsrc *dvfsrc;
dvfsrc = container_of(b,
struct helio_dvfsrc, pm_qos_md_peri_memory_bw_nb);
commit_data(dvfsrc, PM_QOS_MD_PERI_MEMORY_BANDWIDTH, l);
return NOTIFY_OK;
}
static int pm_qos_emi_opp_notify(struct notifier_block *b,
unsigned long l, void *v)
{
struct helio_dvfsrc *dvfsrc;
dvfsrc = container_of(b, struct helio_dvfsrc, pm_qos_emi_opp_nb);
commit_data(dvfsrc, PM_QOS_EMI_OPP, l);
return NOTIFY_OK;
}
static int pm_qos_vcore_opp_notify(struct notifier_block *b,
unsigned long l, void *v)
{
struct helio_dvfsrc *dvfsrc;
dvfsrc = container_of(b, struct helio_dvfsrc, pm_qos_vcore_opp_nb);
commit_data(dvfsrc, PM_QOS_VCORE_OPP, l);
return NOTIFY_OK;
}
static int pm_qos_vcore_dvfs_fixed_opp_notify(struct notifier_block *b,
unsigned long l, void *v)
{
struct helio_dvfsrc *dvfsrc;
dvfsrc = container_of(b,
struct helio_dvfsrc, pm_qos_vcore_dvfs_fixed_opp_nb);
commit_data(dvfsrc, PM_QOS_VCORE_DVFS_FIXED_OPP, l);
return NOTIFY_OK;
}
static void pm_qos_notifier_register(struct helio_dvfsrc *dvfsrc)
{
dvfsrc->pm_qos_memory_bw_nb.notifier_call =
pm_qos_memory_bw_notify;
dvfsrc->pm_qos_cpu_memory_bw_nb.notifier_call =
pm_qos_cpu_memory_bw_notify;
dvfsrc->pm_qos_gpu_memory_bw_nb.notifier_call =
pm_qos_gpu_memory_bw_notify;
dvfsrc->pm_qos_mm_memory_bw_nb.notifier_call =
pm_qos_mm_memory_bw_notify;
dvfsrc->pm_qos_md_peri_memory_bw_nb.notifier_call =
pm_qos_md_peri_memory_bw_notify;
dvfsrc->pm_qos_emi_opp_nb.notifier_call = pm_qos_emi_opp_notify;
dvfsrc->pm_qos_vcore_opp_nb.notifier_call = pm_qos_vcore_opp_notify;
dvfsrc->pm_qos_vcore_dvfs_fixed_opp_nb.notifier_call =
pm_qos_vcore_dvfs_fixed_opp_notify;
pm_qos_add_notifier(PM_QOS_MEMORY_BANDWIDTH,
&dvfsrc->pm_qos_memory_bw_nb);
pm_qos_add_notifier(PM_QOS_CPU_MEMORY_BANDWIDTH,
&dvfsrc->pm_qos_cpu_memory_bw_nb);
pm_qos_add_notifier(PM_QOS_GPU_MEMORY_BANDWIDTH,
&dvfsrc->pm_qos_gpu_memory_bw_nb);
pm_qos_add_notifier(PM_QOS_MM_MEMORY_BANDWIDTH,
&dvfsrc->pm_qos_mm_memory_bw_nb);
pm_qos_add_notifier(PM_QOS_MD_PERI_MEMORY_BANDWIDTH,
&dvfsrc->pm_qos_md_peri_memory_bw_nb);
pm_qos_add_notifier(PM_QOS_EMI_OPP, &dvfsrc->pm_qos_emi_opp_nb);
pm_qos_add_notifier(PM_QOS_VCORE_OPP, &dvfsrc->pm_qos_vcore_opp_nb);
pm_qos_add_notifier(PM_QOS_VCORE_DVFS_FIXED_OPP,
&dvfsrc->pm_qos_vcore_dvfs_fixed_opp_nb);
}
static int helio_dvfsrc_probe(struct platform_device *pdev)
{
int ret;
struct resource *res;
dvfsrc = devm_kzalloc(&pdev->dev, sizeof(*dvfsrc), GFP_KERNEL);
if (!dvfsrc)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dvfsrc->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dvfsrc->regs))
return PTR_ERR(dvfsrc->regs);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
dvfsrc->sram_regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dvfsrc->sram_regs))
return PTR_ERR(dvfsrc->sram_regs);
ret = helio_dvfsrc_add_interface(&pdev->dev);
if (ret)
return ret;
helio_dvfsrc_platform_init(dvfsrc);
vcore_reg_id = regulator_get(&pdev->dev, "vcore");
if (!vcore_reg_id)
pr_info("regulator_get vcore_reg_id failed\n");
dvfsrc->devfreq = devm_devfreq_add_device(&pdev->dev,
&helio_devfreq_profile,
"helio_dvfsrc",
NULL);
if (dvfsrc->devfreq) {
#if !defined(CONFIG_MACH_MT6771) && !defined(CONFIG_MACH_MT6765)
vcore_opp_init();
dvfsrc_init_opp_table();
spm_check_status_before_dvfs();
ret = helio_dvfsrc_reg_config(dvfsrc, dvfsrc->init_config);
if (ret)
return ret;
#endif
}
pm_qos_notifier_register(dvfsrc);
helio_dvfsrc_enable(dvfsrc);
platform_set_drvdata(pdev, dvfsrc);
return 0;
}
static int helio_dvfsrc_remove(struct platform_device *pdev)
{
helio_dvfsrc_remove_interface(&pdev->dev);
return 0;
}
static const struct of_device_id helio_dvfsrc_of_match[] = {
{ .compatible = "mediatek,dvfsrc_top" ,},
{ .compatible = "mediatek,helio-dvfsrc-v2" ,},
{ },
};
MODULE_DEVICE_TABLE(of, helio_dvfsrc_of_match);
static __maybe_unused int helio_dvfsrc_suspend(struct device *dev)
{
struct helio_dvfsrc *dvfsrc = dev_get_drvdata(dev);
int ret = 0;
#if defined(CONFIG_MACH_MT6771)
if (IS_ERR(dvfsrc->devfreq))
return ret;
#endif
ret = devfreq_suspend_device(dvfsrc->devfreq);
if (ret < 0) {
dev_info(dev, "failed to suspend the devfreq devices\n");
return ret;
}
return 0;
}
static __maybe_unused int helio_dvfsrc_resume(struct device *dev)
{
struct helio_dvfsrc *dvfsrc = dev_get_drvdata(dev);
int ret = 0;
#if defined(CONFIG_MACH_MT6771)
if (IS_ERR(dvfsrc->devfreq))
return ret;
#endif
ret = devfreq_resume_device(dvfsrc->devfreq);
if (ret < 0) {
dev_info(dev, "failed to resume the devfreq devices\n");
return ret;
}
return ret;
}
static SIMPLE_DEV_PM_OPS(helio_dvfsrc_pm, helio_dvfsrc_suspend,
helio_dvfsrc_resume);
static struct platform_driver helio_dvfsrc_driver = {
.probe = helio_dvfsrc_probe,
.remove = helio_dvfsrc_remove,
.driver = {
.name = "helio-dvfsrc",
.pm = &helio_dvfsrc_pm,
.of_match_table = helio_dvfsrc_of_match,
},
};
static int __init helio_dvfsrc_init(void)
{
int ret = 0;
ret = devfreq_add_governor(&helio_dvfsrc_governor);
if (ret) {
pr_info("%s: failed to add governor: %d\n", __func__, ret);
return ret;
}
ret = platform_driver_register(&helio_dvfsrc_driver);
if (ret)
devfreq_remove_governor(&helio_dvfsrc_governor);
return ret;
}
late_initcall_sync(helio_dvfsrc_init)
static void __exit helio_dvfsrc_exit(void)
{
int ret = 0;
platform_driver_unregister(&helio_dvfsrc_driver);
ret = devfreq_remove_governor(&helio_dvfsrc_governor);
if (ret)
pr_info("%s: failed to remove governor: %d\n", __func__, ret);
}
module_exit(helio_dvfsrc_exit)
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Helio dvfsrc driver");
+33
View File
@@ -0,0 +1,33 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2020 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_H
#define __HELIO_DVFSRC_H
#if IS_ENABLED(CONFIG_MTK_DVFSRC)
extern void dvfsrc_enable_dvfs_freq_hopping(int on);
#else
static inline void dvfsrc_enable_dvfs_freq_hopping(int on)
{ }
#if defined(CONFIG_MACH_MT6768)
#include <helio-dvfsrc_v2.h>
#elif defined(CONFIG_MACH_MT6785) || defined(CONFIG_MACH_MT6885)
#include <helio-dvfsrc_v3.h>
#elif defined(CONFIG_MACH_MT6873)
#include <helio-dvfsrc_v3.h>
#elif defined(CONFIG_MACH_MT6853)
#include <helio-dvfsrc_v3.h>
#elif defined(CONFIG_MACH_MT6893)
#include <helio-dvfsrc_v3.h>
#elif defined(CONFIG_MACH_MT6833)
#include <helio-dvfsrc_v3.h>
#elif defined(CONFIG_MACH_MT6877)
#include <helio-dvfsrc_v3.h>
#elif defined(CONFIG_MACH_MT6781)
#include <helio-dvfsrc_v3.h>
#else
#include <helio-dvfsrc_v1.h>
#endif
#endif
#endif
+137
View File
@@ -0,0 +1,137 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2018 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_V1_H
#define __HELIO_DVFSRC_V1_H
#include <linux/delay.h>
#include <linux/devfreq.h>
#include <linux/io.h>
#if defined(CONFIG_MACH_MT6775)
#include <helio-dvfsrc-mt6775.h>
#elif defined(CONFIG_MACH_MT6771)
#include <helio-dvfsrc-mt6771.h>
#include <helio-dvfsrc-opp.h>
#elif defined(CONFIG_MACH_MT6739)
#include <helio-dvfsrc-opp.h>
#endif
struct reg_config {
u32 offset;
u32 val;
};
struct helio_dvfsrc {
struct devfreq *devfreq;
void __iomem *regs;
void __iomem *sram_regs;
struct mutex lock;
int enable;
int skip;
int flag;
int dram_type;
unsigned int log_mask;
int curr_vcore_uv;
int curr_ddr_khz;
bool vcore_dvs;
bool ddr_dfs;
bool mm_clk;
struct notifier_block pm_qos_memory_bw_nb;
struct notifier_block pm_qos_cpu_memory_bw_nb;
struct notifier_block pm_qos_gpu_memory_bw_nb;
struct notifier_block pm_qos_mm_memory_bw_nb;
struct notifier_block pm_qos_md_peri_memory_bw_nb;
struct notifier_block pm_qos_emi_opp_nb;
struct notifier_block pm_qos_vcore_opp_nb;
struct notifier_block pm_qos_vcore_dvfs_fixed_opp_nb;
struct reg_config *init_config;
struct reg_config *suspend_config;
struct reg_config *resume_config;
};
extern spinlock_t helio_dvfsrc_lock;
extern int helio_dvfsrc_add_interface(struct device *dev);
extern void helio_dvfsrc_remove_interface(struct device *dev);
extern void helio_dvfsrc_platform_init(struct helio_dvfsrc *dvfsrc);
extern void spm_check_status_before_dvfs(void);
extern int dvfsrc_transfer_to_dram_level(int data);
extern int dvfsrc_transfer_to_vcore_level(int data);
#if !defined(CONFIG_MACH_MT6771)
extern int vcorefs_get_curr_vcore(void);
extern int vcorefs_get_curr_ddr(void);
extern int dvfsrc_get_vcore_by_steps(u32 opp);
extern int dvfsrc_get_ddr_by_steps(u32 opp);
#endif
extern int is_qos_can_work(void);
extern int spm_dvfs_flag_init(void);
extern void dvfsrc_update_opp_table(void);
extern char *dvfsrc_get_opp_table_info(char *p);
extern u32 vcore_to_vcore_dvfs_level[];
extern u32 emi_to_vcore_dvfs_level[];
extern u32 vcore_dvfs_to_vcore_dvfs_level[];
extern void dvfsrc_update_sspm_vcore_opp_table(int opp, unsigned int vcore_uv);
extern void dvfsrc_update_sspm_ddr_opp_table(int opp, unsigned int ddr_khz);
extern int dvfsrc_get_bw(int type);
extern int get_cur_vcore_dvfs_opp(void);
extern int is_dvfsrc_opp_fixed(void);
#define DVFSRC_REG(dvfsrc, offset) (dvfsrc->regs + offset)
#define DVFSRC_SRAM_REG(dvfsrc, offset) (dvfsrc->sram_regs + offset)
#define DVFSRC_TIMEOUT 1000
#define is_dvfsrc_in_progress(dvfsrc) \
(dvfsrc_read(dvfsrc, DVFSRC_LEVEL) & 0xFFFF)
#define get_dvfsrc_level(dvfsrc) \
(dvfsrc_read(dvfsrc, DVFSRC_LEVEL) >> 16)
/* PMIC */
#define vcore_pmic_to_uv(pmic) \
(((pmic) * VCORE_STEP_UV) + VCORE_BASE_UV)
#define vcore_uv_to_pmic(uv) /* pmic >= uv */ \
((((uv) - VCORE_BASE_UV) + (VCORE_STEP_UV - 1)) / VCORE_STEP_UV)
#define wait_for_completion(condition, timeout) \
({ \
int i = 0; \
while (!(condition)) { \
if (i >= (timeout)) { \
i = -EBUSY; \
break; \
} \
udelay(1); \
i++; \
} \
i; \
})
#define dvfsrc_write(dvfsrc, offset, val) \
writel(val, DVFSRC_REG(dvfsrc, offset))
#define dvfsrc_read(dvfsrc, offset) \
readl(DVFSRC_REG(dvfsrc, offset))
#define dvfsrc_sram_write(dvfsrc, offset, val) \
writel(val, DVFSRC_SRAM_REG(dvfsrc, offset))
#define dvfsrc_sram_read(dvfsrc, offset) \
readl(DVFSRC_SRAM_REG(dvfsrc, offset))
extern struct helio_dvfsrc *dvfsrc;
#endif /* __HELIO_DVFSRC_H */
+168
View File
@@ -0,0 +1,168 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_V2_H
#define __HELIO_DVFSRC_V2_H
#include <linux/delay.h>
#include <linux/devfreq.h>
#include <linux/io.h>
#if defined(CONFIG_MACH_MT6768)
#include <helio-dvfsrc-mt6768.h>
#elif defined(CONFIG_MACH_MT6765)
#include <helio-dvfsrc-mt6765.h>
#else
#include <helio-dvfsrc-mt67xx.h>
#endif
#include "helio-dvfsrc-opp.h"
struct reg_config {
u32 offset;
u32 val;
};
struct helio_dvfsrc {
int irq;
struct device *dev;
bool qos_enabled;
bool dvfsrc_enabled;
int dvfsrc_flag;
void __iomem *regs;
void __iomem *sram_regs;
struct notifier_block mtk_pm_qos_memory_bw_nb;
struct notifier_block mtk_pm_qos_cpu_memory_bw_nb;
struct notifier_block mtk_pm_qos_gpu_memory_bw_nb;
struct notifier_block mtk_pm_qos_mm_memory_bw_nb;
struct notifier_block mtk_pm_qos_other_memory_bw_nb;
struct notifier_block mtk_pm_qos_ddr_opp_nb;
struct notifier_block mtk_pm_qos_vcore_opp_nb;
struct notifier_block mtk_pm_qos_scp_vcore_request_nb;
struct notifier_block mtk_pm_qos_power_model_ddr_request_nb;
struct notifier_block mtk_pm_qos_power_model_vcore_request_nb;
struct notifier_block mtk_pm_qos_vcore_dvfs_force_opp_nb;
struct notifier_block mtk_pm_qos_isp_hrt_bw_nb;
struct notifier_block mtk_pm_qos_apu_memory_bw_nb;
struct reg_config *init_config;
bool opp_forced;
char force_start[20];
char force_end[20];
int (*suspend)(struct helio_dvfsrc *dvfsrc_dev);
int (*resume)(struct helio_dvfsrc *dvfsrc_dev);
};
#define DVFSRC_TIMEOUT 1000
#define QOS_TOTAL_BW_BUF_SIZE 8
#define QOS_TOTAL_BW_BUF(idx) (idx * 4)
#define QOS_TOTAL_BW 0x20
#define QOS_TOTAL_W_BW 0x24
#define QOS_CPU_BW 0x28
#define QOS_GPU_BW 0x2C
#define QOS_MM_BW 0x30
#define QOS_OTHER_BW 0x34
#define QOS_DEBUG_0 0x38
#define QOS_DEBUG_1 0x3C
#define QOS_DEBUG_2 0x40
#define QOS_DEBUG_3 0x44
#define QOS_DEBUG_4 0x48
#define QOS_TOTAL_BW_BUF_LAST 0x4C
#define MM_SMI_CLK 0x50
#define MM_SMI_CLR 0x54
#define MM_SMI_EXE 0x58
#define MM_SMI_DUMP 0x5C
#define CM_STALL_RATIO_OFFSET 0x60
#define QOS_SRAM_MAX_SIZE 0x80
#define QOS_TOTAL_BW_BUF_BW_MASK 0x0000FFFF
#define QOS_TOTAL_BW_BUF_SEQ_MASK 0xFF000000
/* PMIC */
#define vcore_pmic_to_uv(pmic) \
(((pmic) * VCORE_STEP_UV) + VCORE_BASE_UV)
#define vcore_uv_to_pmic(uv) /* pmic >= uv */ \
((((uv) - VCORE_BASE_UV) + (VCORE_STEP_UV - 1)) / VCORE_STEP_UV)
#define dvfsrc_wait_for_completion(condition, timeout) \
({ \
int ret = 0; \
if (is_dvfsrc_enabled()) \
ret = 1; \
while (!(condition) && ret > 0) { \
if (ret++ >= timeout) \
ret = -EBUSY; \
udelay(1); \
} \
ret; \
})
enum {
QOS_EMI_BW_TOTAL = 0,
QOS_EMI_BW_TOTAL_W,
QOS_EMI_BW_CPU,
QOS_EMI_BW_GPU,
QOS_EMI_BW_MM,
QOS_EMI_BW_OTHER,
QOS_EMI_BW_TOTAL_AVE,
QOS_EMI_BW_NUM
};
extern int is_qos_enabled(void);
extern int is_dvfsrc_enabled(void);
extern int is_opp_forced(void);
extern int dvfsrc_get_emi_bw(int type);
extern int get_vcore_dvfs_level(void);
extern void mtk_spmfw_init(int dvfsrc_en, int skip_check);
extern struct reg_config *dvfsrc_get_init_conf(void);
extern void helio_dvfsrc_enable(int dvfsrc_en);
extern void helio_dvfsrc_flag_set(int flag);
extern int helio_dvfsrc_flag_get(void);
extern u32 dvfsrc_dump_reg(char *ptr, u32 count);
extern u32 dvfsrc_read(u32 offset);
extern void dvfsrc_write(u32 offset, u32 val);
extern u32 dvfsrc_sram_read(u32 offset);
extern void dvfsrc_sram_write(u32 offset, u32 val);
extern u32 qos_sram_read(u32 offset);
extern void qos_sram_write(u32 offset, u32 val);
extern void dvfsrc_opp_table_init(void);
extern void helio_dvfsrc_reg_config(struct reg_config *config);
extern void helio_dvfsrc_sram_reg_init(void);
extern int helio_dvfsrc_add_interface(struct device *dev);
extern void helio_dvfsrc_remove_interface(struct device *dev);
extern void dvfsrc_opp_level_mapping(void);
extern void helio_dvfsrc_sspm_ipi_init(int dvfsrc_en);
extern void get_opp_info(char *p);
extern void get_dvfsrc_reg(char *p);
extern void get_dvfsrc_record(char *p);
extern void get_spm_reg(char *p);
extern void spm_dvfs_pwrap_cmd(int pwrap_cmd, int pwrap_vcore);
extern int helio_dvfsrc_platform_init(struct helio_dvfsrc *dvfsrc);
extern u32 spm_get_dvfs_level(void);
extern u32 spm_get_dvfs_final_level(void);
extern u32 spm_get_pcm_reg9_data(void);
extern void dvfsrc_set_power_model_ddr_request(unsigned int level);
/* met profile function */
extern int vcorefs_get_opp_info_num(void);
extern char **vcorefs_get_opp_info_name(void);
extern unsigned int *vcorefs_get_opp_info(void);
extern int vcorefs_get_src_req_num(void);
extern char **vcorefs_get_src_req_name(void);
extern unsigned int *vcorefs_get_src_req(void);
extern u32 vcorefs_get_md_scenario(void);
extern u32 get_dvfs_final_level(void);
extern int get_sw_req_vcore_opp(void);
#endif /* __HELIO_DVFSRC_H */
+26
View File
@@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2020 MediaTek Inc.
*/
#ifndef __HELIO_DVFSRC_V3_H
#define __HELIO_DVFSRC_V3_H
#include "helio-dvfsrc-opp.h"
extern int is_dvfsrc_enabled(void);
extern void dvfsrc_set_power_model_ddr_request(unsigned int level);
/* met profile function */
extern int vcorefs_get_num_opp(void);
extern int vcorefs_get_opp_info_num(void);
extern char **vcorefs_get_opp_info_name(void);
extern unsigned int *vcorefs_get_opp_info(void);
extern int vcorefs_get_src_req_num(void);
extern char **vcorefs_get_src_req_name(void);
extern unsigned int *vcorefs_get_src_req(void);
extern u32 vcorefs_get_md_scenario(void);
extern int get_cur_ddr_ratio(void);
extern int is_dvfsrc_opp_fixed(void);
#endif /* __HELIO_DVFSRC_V3_H */
+28
View File
@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __MTK_DVFSRC_REG_H
#define __MTK_DVFSRC_REG_H
#if defined(CONFIG_MACH_MT6775)
#include "mtk_dvfsrc_reg_mt6775.h"
#elif defined(CONFIG_MACH_MT6765)
#include <mtk_dvfsrc_reg_mt6765.h>
#elif defined(CONFIG_MACH_MT6771)
#include "mtk_dvfsrc_reg_mt6771.h"
#elif defined(CONFIG_MACH_MT6768)
#include <mtk_dvfsrc_reg_mt6768.h>
#elif defined(CONFIG_MACH_MT6765)
#include <mtk_dvfsrc_reg_mt6765.h>
#else
#include <mtk_dvfsrc_reg_mt67xx.h>
#endif
#endif /* __MTK_DVFSRC_REG_H */
+170
View File
@@ -0,0 +1,170 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __MTK_DVFSRC_REG_MT6765_H
#define __MTK_DVFSRC_REG_MT6765_H
/**************************************
* Define and Declare
**************************************/
#define DVFSRC_BASIC_CONTROL (0x0)
#define DVFSRC_SW_REQ (0x4)
#define DVFSRC_SW_REQ2 (0x8)
#define DVFSRC_EMI_REQUEST (0xC)
#define DVFSRC_EMI_REQUEST2 (0x10)
#define DVFSRC_EMI_REQUEST3 (0x14)
#define DVFSRC_EMI_HRT (0x18)
#define DVFSRC_EMI_HRT2 (0x1C)
#define DVFSRC_EMI_HRT3 (0x20)
#define DVFSRC_EMI_QOS0 (0x24)
#define DVFSRC_EMI_QOS1 (0x28)
#define DVFSRC_EMI_QOS2 (0x2C)
#define DVFSRC_EMI_MD2SPM0 (0x30)
#define DVFSRC_EMI_MD2SPM1 (0x34)
#define DVFSRC_EMI_MD2SPM2 (0x38)
#define DVFSRC_EMI_MD2SPM0_T (0x3C)
#define DVFSRC_EMI_MD2SPM1_T (0x40)
#define DVFSRC_EMI_MD2SPM2_T (0x44)
#define DVFSRC_VCORE_REQUEST (0x48)
#define DVFSRC_VCORE_REQUEST2 (0x4C)
#define DVFSRC_VCORE_HRT (0x50)
#define DVFSRC_VCORE_HRT2 (0x54)
#define DVFSRC_VCORE_HRT3 (0x58)
#define DVFSRC_VCORE_QOS0 (0x5C)
#define DVFSRC_VCORE_QOS1 (0x60)
#define DVFSRC_VCORE_QOS2 (0x64)
#define DVFSRC_VCORE_MD2SPM0 (0x68)
#define DVFSRC_VCORE_MD2SPM1 (0x6C)
#define DVFSRC_VCORE_MD2SPM2 (0x70)
#define DVFSRC_VCORE_MD2SPM0_T (0x74)
#define DVFSRC_VCORE_MD2SPM1_T (0x78)
#define DVFSRC_VCORE_MD2SPM2_T (0x7C)
#define DVFSRC_MD_REQUEST (0x80)
#define DVFSRC_MD_SW_CONTROL (0x84)
#define DVFSRC_MD_VMODEM_REMAP (0x88)
#define DVFSRC_MD_VMD_REMAP (0x8C)
#define DVFSRC_MD_VSRAM_REMAP (0x90)
#define DVFSRC_HALT_SW_CONTROL (0x94)
#define DVFSRC_INT (0x98)
#define DVFSRC_INT_EN (0x9C)
#define DVFSRC_INT_CLR (0xA0)
#define DVFSRC_BW_MON_WINDOW (0xA4)
#define DVFSRC_BW_MON_THRES_1 (0xA8)
#define DVFSRC_BW_MON_THRES_2 (0xAC)
#define DVFSRC_MD_TURBO (0xB0)
#define DVFSRC_DEBOUNCE_FOUR (0xD0)
#define DVFSRC_DEBOUNCE_RISE_FALL (0xD4)
#define DVFSRC_TIMEOUT_NEXTREQ (0xD8)
#define DVFSRC_LEVEL (0xDC)
#define DVFSRC_LEVEL_LABEL_0_1 (0xE0)
#define DVFSRC_LEVEL_LABEL_2_3 (0xE4)
#define DVFSRC_LEVEL_LABEL_4_5 (0xE8)
#define DVFSRC_LEVEL_LABEL_6_7 (0xEC)
#define DVFSRC_LEVEL_LABEL_8_9 (0xF0)
#define DVFSRC_LEVEL_LABEL_10_11 (0xF4)
#define DVFSRC_LEVEL_LABEL_12_13 (0xF8)
#define DVFSRC_LEVEL_LABEL_14_15 (0xFC)
#define DVFSRC_MM_BW_0 (0x100)
#define DVFSRC_MM_BW_1 (0x104)
#define DVFSRC_MM_BW_2 (0x108)
#define DVFSRC_MM_BW_3 (0x10C)
#define DVFSRC_MM_BW_4 (0x110)
#define DVFSRC_MM_BW_5 (0x114)
#define DVFSRC_MM_BW_6 (0x118)
#define DVFSRC_MM_BW_7 (0x11C)
#define DVFSRC_MM_BW_8 (0x120)
#define DVFSRC_MM_BW_9 (0x124)
#define DVFSRC_MM_BW_10 (0x128)
#define DVFSRC_MM_BW_11 (0x12C)
#define DVFSRC_MM_BW_12 (0x130)
#define DVFSRC_MM_BW_13 (0x134)
#define DVFSRC_MM_BW_14 (0x138)
#define DVFSRC_MM_BW_15 (0x13C)
#define DVFSRC_MD_BW_0 (0x140)
#define DVFSRC_MD_BW_1 (0x144)
#define DVFSRC_MD_BW_2 (0x148)
#define DVFSRC_MD_BW_3 (0x14C)
#define DVFSRC_MD_BW_4 (0x150)
#define DVFSRC_MD_BW_5 (0x154)
#define DVFSRC_MD_BW_6 (0x158)
#define DVFSRC_MD_BW_7 (0x15C)
#define DVFSRC_SW_BW_0 (0x160)
#define DVFSRC_SW_BW_1 (0x164)
#define DVFSRC_SW_BW_2 (0x168)
#define DVFSRC_SW_BW_3 (0x16C)
#define DVFSRC_SW_BW_4 (0x170)
#define DVFSRC_QOS_EN (0x180)
#define DVFSRC_ISP_HRT (0x190)
#define DVFSRC_FORCE (0x300)
#define DVFSRC_SEC_SW_REQ (0x304)
#define DVFSRC_LAST (0x308)
#define DVFSRC_LAST_L (0x30C)
#define DVFSRC_MD_SCENARIO (0X310)
#define DVFSRC_RECORD_0_0 (0x400)
#define DVFSRC_RECORD_0_1 (0x404)
#define DVFSRC_RECORD_0_2 (0x408)
#define DVFSRC_RECORD_1_0 (0x40C)
#define DVFSRC_RECORD_1_1 (0x410)
#define DVFSRC_RECORD_1_2 (0x414)
#define DVFSRC_RECORD_2_0 (0x418)
#define DVFSRC_RECORD_2_1 (0x41C)
#define DVFSRC_RECORD_2_2 (0x420)
#define DVFSRC_RECORD_3_0 (0x424)
#define DVFSRC_RECORD_3_1 (0x428)
#define DVFSRC_RECORD_3_2 (0x42C)
#define DVFSRC_RECORD_4_0 (0x430)
#define DVFSRC_RECORD_4_1 (0x434)
#define DVFSRC_RECORD_4_2 (0x438)
#define DVFSRC_RECORD_5_0 (0x43C)
#define DVFSRC_RECORD_5_1 (0x440)
#define DVFSRC_RECORD_5_2 (0x444)
#define DVFSRC_RECORD_6_0 (0x448)
#define DVFSRC_RECORD_6_1 (0x44C)
#define DVFSRC_RECORD_6_2 (0x450)
#define DVFSRC_RECORD_7_0 (0x454)
#define DVFSRC_RECORD_7_1 (0x458)
#define DVFSRC_RECORD_7_2 (0x45C)
#define DVFSRC_RECORD_0_L_0 (0x460)
#define DVFSRC_RECORD_0_L_1 (0x464)
#define DVFSRC_RECORD_0_L_2 (0x468)
#define DVFSRC_RECORD_1_L_0 (0x46C)
#define DVFSRC_RECORD_1_L_1 (0x470)
#define DVFSRC_RECORD_1_L_2 (0x474)
#define DVFSRC_RECORD_2_L_0 (0x478)
#define DVFSRC_RECORD_2_L_1 (0x47C)
#define DVFSRC_RECORD_2_L_2 (0x480)
#define DVFSRC_RECORD_3_L_0 (0x484)
#define DVFSRC_RECORD_3_L_1 (0x488)
#define DVFSRC_RECORD_3_L_2 (0x48C)
#define DVFSRC_RECORD_4_L_0 (0x490)
#define DVFSRC_RECORD_4_L_1 (0x494)
#define DVFSRC_RECORD_4_L_2 (0x498)
#define DVFSRC_RECORD_5_L_0 (0x49C)
#define DVFSRC_RECORD_5_L_1 (0x4A0)
#define DVFSRC_RECORD_5_L_2 (0x4A4)
#define DVFSRC_RECORD_6_L_0 (0x4A8)
#define DVFSRC_RECORD_6_L_1 (0x4AC)
#define DVFSRC_RECORD_6_L_2 (0x4B0)
#define DVFSRC_RECORD_7_L_0 (0x4B4)
#define DVFSRC_RECORD_7_L_1 (0x4B8)
#define DVFSRC_RECORD_7_L_2 (0x4BC)
#define DVFSRC_RECORD_MD_0 (0x4C0)
#define DVFSRC_RECORD_MD_1 (0x4C4)
#define DVFSRC_RECORD_MD_2 (0x4C8)
#define DVFSRC_RECORD_MD_3 (0x4CC)
#define DVFSRC_RECORD_MD_4 (0x4D0)
#define DVFSRC_RECORD_MD_5 (0x4D4)
#define DVFSRC_RECORD_MD_6 (0x4D8)
#define DVFSRC_RECORD_MD_7 (0x4DC)
#define DVFSRC_RECORD_COUNT (0x4F0)
#define DVFSRC_RSRV_0 (0x600)
#define DVFSRC_RSRV_1 (0x604)
#define DVFSRC_RSRV_2 (0x608)
#define DVFSRC_RSRV_3 (0x60C)
#define DVFSRC_RSRV_4 (0x610)
#define DVFSRC_RSRV_5 (0x614)
#endif /* __MTK_DVFSRC_REG_MT6765_H */
+174
View File
@@ -0,0 +1,174 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __MTK_DVFSRC_REG_MT6768_H
#define __MTK_DVFSRC_REG_MT6768_H
/**************************************
* Define and Declare
**************************************/
#define DVFSRC_BASIC_CONTROL (0x0)
#define DVFSRC_SW_REQ (0x4)
#define DVFSRC_SW_REQ2 (0x8)
#define DVFSRC_EMI_REQUEST (0xC)
#define DVFSRC_EMI_REQUEST2 (0x10)
#define DVFSRC_EMI_REQUEST3 (0x14)
#define DVFSRC_EMI_HRT (0x18)
#define DVFSRC_EMI_HRT2 (0x1C)
#define DVFSRC_EMI_HRT3 (0x20)
#define DVFSRC_EMI_QOS0 (0x24)
#define DVFSRC_EMI_QOS1 (0x28)
#define DVFSRC_EMI_QOS2 (0x2C)
#define DVFSRC_EMI_MD2SPM0 (0x30)
#define DVFSRC_EMI_MD2SPM1 (0x34)
#define DVFSRC_EMI_MD2SPM2 (0x38)
#define DVFSRC_EMI_MD2SPM0_T (0x3C)
#define DVFSRC_EMI_MD2SPM1_T (0x40)
#define DVFSRC_EMI_MD2SPM2_T (0x44)
#define DVFSRC_VCORE_REQUEST (0x48)
#define DVFSRC_VCORE_REQUEST2 (0x4C)
#define DVFSRC_VCORE_HRT (0x50)
#define DVFSRC_VCORE_HRT2 (0x54)
#define DVFSRC_VCORE_HRT3 (0x58)
#define DVFSRC_VCORE_QOS0 (0x5C)
#define DVFSRC_VCORE_QOS1 (0x60)
#define DVFSRC_VCORE_QOS2 (0x64)
#define DVFSRC_VCORE_MD2SPM0 (0x68)
#define DVFSRC_VCORE_MD2SPM1 (0x6C)
#define DVFSRC_VCORE_MD2SPM2 (0x70)
#define DVFSRC_VCORE_MD2SPM0_T (0x74)
#define DVFSRC_VCORE_MD2SPM1_T (0x78)
#define DVFSRC_VCORE_MD2SPM2_T (0x7C)
#define DVFSRC_MD_REQUEST (0x80)
#define DVFSRC_MD_SW_CONTROL (0x84)
#define DVFSRC_MD_VMODEM_REMAP (0x88)
#define DVFSRC_MD_VMD_REMAP (0x8C)
#define DVFSRC_MD_VSRAM_REMAP (0x90)
#define DVFSRC_HALT_SW_CONTROL (0x94)
#define DVFSRC_INT (0x98)
#define DVFSRC_INT_EN (0x9C)
#define DVFSRC_INT_CLR (0xA0)
#define DVFSRC_BW_MON_WINDOW (0xA4)
#define DVFSRC_BW_MON_THRES_1 (0xA8)
#define DVFSRC_BW_MON_THRES_2 (0xAC)
#define DVFSRC_MD_TURBO (0xB0)
#define DVFSRC_DEBOUNCE_FOUR (0xD0)
#define DVFSRC_DEBOUNCE_RISE_FALL (0xD4)
#define DVFSRC_TIMEOUT_NEXTREQ (0xD8)
#define DVFSRC_LEVEL (0xDC)
#define DVFSRC_LEVEL_LABEL_0_1 (0xE0)
#define DVFSRC_LEVEL_LABEL_2_3 (0xE4)
#define DVFSRC_LEVEL_LABEL_4_5 (0xE8)
#define DVFSRC_LEVEL_LABEL_6_7 (0xEC)
#define DVFSRC_LEVEL_LABEL_8_9 (0xF0)
#define DVFSRC_LEVEL_LABEL_10_11 (0xF4)
#define DVFSRC_LEVEL_LABEL_12_13 (0xF8)
#define DVFSRC_LEVEL_LABEL_14_15 (0xFC)
#define DVFSRC_MM_BW_0 (0x100)
#define DVFSRC_MM_BW_1 (0x104)
#define DVFSRC_MM_BW_2 (0x108)
#define DVFSRC_MM_BW_3 (0x10C)
#define DVFSRC_MM_BW_4 (0x110)
#define DVFSRC_MM_BW_5 (0x114)
#define DVFSRC_MM_BW_6 (0x118)
#define DVFSRC_MM_BW_7 (0x11C)
#define DVFSRC_MM_BW_8 (0x120)
#define DVFSRC_MM_BW_9 (0x124)
#define DVFSRC_MM_BW_10 (0x128)
#define DVFSRC_MM_BW_11 (0x12C)
#define DVFSRC_MM_BW_12 (0x130)
#define DVFSRC_MM_BW_13 (0x134)
#define DVFSRC_MM_BW_14 (0x138)
#define DVFSRC_MM_BW_15 (0x13C)
#define DVFSRC_MD_BW_0 (0x140)
#define DVFSRC_MD_BW_1 (0x144)
#define DVFSRC_MD_BW_2 (0x148)
#define DVFSRC_MD_BW_3 (0x14C)
#define DVFSRC_MD_BW_4 (0x150)
#define DVFSRC_MD_BW_5 (0x154)
#define DVFSRC_MD_BW_6 (0x158)
#define DVFSRC_MD_BW_7 (0x15C)
#define DVFSRC_SW_BW_0 (0x160)
#define DVFSRC_SW_BW_1 (0x164)
#define DVFSRC_SW_BW_2 (0x168)
#define DVFSRC_SW_BW_3 (0x16C)
#define DVFSRC_SW_BW_4 (0x170)
#define DVFSRC_QOS_EN (0x180)
#define DVFSRC_ISP_HRT (0x190)
#define DVFSRC_FORCE (0x300)
#define DVFSRC_SEC_SW_REQ (0x304)
#define DVFSRC_LAST (0x308)
#define DVFSRC_LAST_L (0x30C)
#define DVFSRC_MD_SCENARIO (0X310)
#define DVFSRC_RECORD_0_0 (0x400)
#define DVFSRC_RECORD_0_1 (0x404)
#define DVFSRC_RECORD_0_2 (0x408)
#define DVFSRC_RECORD_1_0 (0x40C)
#define DVFSRC_RECORD_1_1 (0x410)
#define DVFSRC_RECORD_1_2 (0x414)
#define DVFSRC_RECORD_2_0 (0x418)
#define DVFSRC_RECORD_2_1 (0x41C)
#define DVFSRC_RECORD_2_2 (0x420)
#define DVFSRC_RECORD_3_0 (0x424)
#define DVFSRC_RECORD_3_1 (0x428)
#define DVFSRC_RECORD_3_2 (0x42C)
#define DVFSRC_RECORD_4_0 (0x430)
#define DVFSRC_RECORD_4_1 (0x434)
#define DVFSRC_RECORD_4_2 (0x438)
#define DVFSRC_RECORD_5_0 (0x43C)
#define DVFSRC_RECORD_5_1 (0x440)
#define DVFSRC_RECORD_5_2 (0x444)
#define DVFSRC_RECORD_6_0 (0x448)
#define DVFSRC_RECORD_6_1 (0x44C)
#define DVFSRC_RECORD_6_2 (0x450)
#define DVFSRC_RECORD_7_0 (0x454)
#define DVFSRC_RECORD_7_1 (0x458)
#define DVFSRC_RECORD_7_2 (0x45C)
#define DVFSRC_RECORD_0_L_0 (0x460)
#define DVFSRC_RECORD_0_L_1 (0x464)
#define DVFSRC_RECORD_0_L_2 (0x468)
#define DVFSRC_RECORD_1_L_0 (0x46C)
#define DVFSRC_RECORD_1_L_1 (0x470)
#define DVFSRC_RECORD_1_L_2 (0x474)
#define DVFSRC_RECORD_2_L_0 (0x478)
#define DVFSRC_RECORD_2_L_1 (0x47C)
#define DVFSRC_RECORD_2_L_2 (0x480)
#define DVFSRC_RECORD_3_L_0 (0x484)
#define DVFSRC_RECORD_3_L_1 (0x488)
#define DVFSRC_RECORD_3_L_2 (0x48C)
#define DVFSRC_RECORD_4_L_0 (0x490)
#define DVFSRC_RECORD_4_L_1 (0x494)
#define DVFSRC_RECORD_4_L_2 (0x498)
#define DVFSRC_RECORD_5_L_0 (0x49C)
#define DVFSRC_RECORD_5_L_1 (0x4A0)
#define DVFSRC_RECORD_5_L_2 (0x4A4)
#define DVFSRC_RECORD_6_L_0 (0x4A8)
#define DVFSRC_RECORD_6_L_1 (0x4AC)
#define DVFSRC_RECORD_6_L_2 (0x4B0)
#define DVFSRC_RECORD_7_L_0 (0x4B4)
#define DVFSRC_RECORD_7_L_1 (0x4B8)
#define DVFSRC_RECORD_7_L_2 (0x4BC)
#define DVFSRC_RECORD_MD_0 (0x4C0)
#define DVFSRC_RECORD_MD_1 (0x4C4)
#define DVFSRC_RECORD_MD_2 (0x4C8)
#define DVFSRC_RECORD_MD_3 (0x4CC)
#define DVFSRC_RECORD_MD_4 (0x4D0)
#define DVFSRC_RECORD_MD_5 (0x4D4)
#define DVFSRC_RECORD_MD_6 (0x4D8)
#define DVFSRC_RECORD_MD_7 (0x4DC)
#define DVFSRC_RECORD_COUNT (0x4F0)
#define DVFSRC_RSRV_0 (0x600)
#define DVFSRC_RSRV_1 (0x604)
#define DVFSRC_RSRV_2 (0x608)
#define DVFSRC_RSRV_3 (0x60C)
#define DVFSRC_RSRV_4 (0x610)
#define DVFSRC_RSRV_5 (0x614)
#define RECORD_SHIFT 12
#define EMI_BW_MON_SHIFT 16
#define EMI_BW_MON_MASK 3
#endif /* __MTK_DVFSRC_REG_MT6768_H */
+9
View File
@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef __MTK_DVFSRC_REG_MT67XX_H
#define __MTK_DVFSRC_REG_MT67XX_H
#endif /* __MTK_DVFSRC_REG_MT67XX_H */
+442
View File
@@ -0,0 +1,442 @@
/*
* Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd.
* Author: Lin Huang <hl@rock-chips.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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/arm-smccc.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/devfreq.h>
#include <linux/devfreq-event.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/regulator/consumer.h>
#include <linux/rwsem.h>
#include <linux/suspend.h>
#include <soc/rockchip/rockchip_sip.h>
struct dram_timing {
unsigned int ddr3_speed_bin;
unsigned int pd_idle;
unsigned int sr_idle;
unsigned int sr_mc_gate_idle;
unsigned int srpd_lite_idle;
unsigned int standby_idle;
unsigned int auto_pd_dis_freq;
unsigned int dram_dll_dis_freq;
unsigned int phy_dll_dis_freq;
unsigned int ddr3_odt_dis_freq;
unsigned int ddr3_drv;
unsigned int ddr3_odt;
unsigned int phy_ddr3_ca_drv;
unsigned int phy_ddr3_dq_drv;
unsigned int phy_ddr3_odt;
unsigned int lpddr3_odt_dis_freq;
unsigned int lpddr3_drv;
unsigned int lpddr3_odt;
unsigned int phy_lpddr3_ca_drv;
unsigned int phy_lpddr3_dq_drv;
unsigned int phy_lpddr3_odt;
unsigned int lpddr4_odt_dis_freq;
unsigned int lpddr4_drv;
unsigned int lpddr4_dq_odt;
unsigned int lpddr4_ca_odt;
unsigned int phy_lpddr4_ca_drv;
unsigned int phy_lpddr4_ck_cs_drv;
unsigned int phy_lpddr4_dq_drv;
unsigned int phy_lpddr4_odt;
};
struct rk3399_dmcfreq {
struct device *dev;
struct devfreq *devfreq;
struct devfreq_simple_ondemand_data ondemand_data;
struct clk *dmc_clk;
struct devfreq_event_dev *edev;
struct mutex lock;
struct dram_timing timing;
struct regulator *vdd_center;
unsigned long rate, target_rate;
unsigned long volt, target_volt;
};
static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq,
u32 flags)
{
struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(dev);
struct dev_pm_opp *opp;
unsigned long old_clk_rate = dmcfreq->rate;
unsigned long target_volt, target_rate;
int err;
opp = devfreq_recommended_opp(dev, freq, flags);
if (IS_ERR(opp))
return PTR_ERR(opp);
target_rate = dev_pm_opp_get_freq(opp);
target_volt = dev_pm_opp_get_voltage(opp);
dev_pm_opp_put(opp);
if (dmcfreq->rate == target_rate)
return 0;
mutex_lock(&dmcfreq->lock);
/*
* If frequency scaling from low to high, adjust voltage first.
* If frequency scaling from high to low, adjust frequency first.
*/
if (old_clk_rate < target_rate) {
err = regulator_set_voltage(dmcfreq->vdd_center, target_volt,
target_volt);
if (err) {
dev_err(dev, "Cannot set voltage %lu uV\n",
target_volt);
goto out;
}
}
err = clk_set_rate(dmcfreq->dmc_clk, target_rate);
if (err) {
dev_err(dev, "Cannot set frequency %lu (%d)\n", target_rate,
err);
regulator_set_voltage(dmcfreq->vdd_center, dmcfreq->volt,
dmcfreq->volt);
goto out;
}
/*
* Check the dpll rate,
* There only two result we will get,
* 1. Ddr frequency scaling fail, we still get the old rate.
* 2. Ddr frequency scaling sucessful, we get the rate we set.
*/
dmcfreq->rate = clk_get_rate(dmcfreq->dmc_clk);
/* If get the incorrect rate, set voltage to old value. */
if (dmcfreq->rate != target_rate) {
dev_err(dev, "Got wrong frequency, Request %lu, Current %lu\n",
target_rate, dmcfreq->rate);
regulator_set_voltage(dmcfreq->vdd_center, dmcfreq->volt,
dmcfreq->volt);
goto out;
} else if (old_clk_rate > target_rate)
err = regulator_set_voltage(dmcfreq->vdd_center, target_volt,
target_volt);
if (err)
dev_err(dev, "Cannot set voltage %lu uV\n", target_volt);
dmcfreq->rate = target_rate;
dmcfreq->volt = target_volt;
out:
mutex_unlock(&dmcfreq->lock);
return err;
}
static int rk3399_dmcfreq_get_dev_status(struct device *dev,
struct devfreq_dev_status *stat)
{
struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(dev);
struct devfreq_event_data edata;
int ret = 0;
ret = devfreq_event_get_event(dmcfreq->edev, &edata);
if (ret < 0)
return ret;
stat->current_frequency = dmcfreq->rate;
stat->busy_time = edata.load_count;
stat->total_time = edata.total_count;
return ret;
}
static int rk3399_dmcfreq_get_cur_freq(struct device *dev, unsigned long *freq)
{
struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(dev);
*freq = dmcfreq->rate;
return 0;
}
static struct devfreq_dev_profile rk3399_devfreq_dmc_profile = {
.polling_ms = 200,
.target = rk3399_dmcfreq_target,
.get_dev_status = rk3399_dmcfreq_get_dev_status,
.get_cur_freq = rk3399_dmcfreq_get_cur_freq,
};
static __maybe_unused int rk3399_dmcfreq_suspend(struct device *dev)
{
struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(dev);
int ret = 0;
ret = devfreq_event_disable_edev(dmcfreq->edev);
if (ret < 0) {
dev_err(dev, "failed to disable the devfreq-event devices\n");
return ret;
}
ret = devfreq_suspend_device(dmcfreq->devfreq);
if (ret < 0) {
dev_err(dev, "failed to suspend the devfreq devices\n");
return ret;
}
return 0;
}
static __maybe_unused int rk3399_dmcfreq_resume(struct device *dev)
{
struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(dev);
int ret = 0;
ret = devfreq_event_enable_edev(dmcfreq->edev);
if (ret < 0) {
dev_err(dev, "failed to enable the devfreq-event devices\n");
return ret;
}
ret = devfreq_resume_device(dmcfreq->devfreq);
if (ret < 0) {
dev_err(dev, "failed to resume the devfreq devices\n");
return ret;
}
return ret;
}
static SIMPLE_DEV_PM_OPS(rk3399_dmcfreq_pm, rk3399_dmcfreq_suspend,
rk3399_dmcfreq_resume);
static int of_get_ddr_timings(struct dram_timing *timing,
struct device_node *np)
{
int ret = 0;
ret = of_property_read_u32(np, "rockchip,ddr3_speed_bin",
&timing->ddr3_speed_bin);
ret |= of_property_read_u32(np, "rockchip,pd_idle",
&timing->pd_idle);
ret |= of_property_read_u32(np, "rockchip,sr_idle",
&timing->sr_idle);
ret |= of_property_read_u32(np, "rockchip,sr_mc_gate_idle",
&timing->sr_mc_gate_idle);
ret |= of_property_read_u32(np, "rockchip,srpd_lite_idle",
&timing->srpd_lite_idle);
ret |= of_property_read_u32(np, "rockchip,standby_idle",
&timing->standby_idle);
ret |= of_property_read_u32(np, "rockchip,auto_pd_dis_freq",
&timing->auto_pd_dis_freq);
ret |= of_property_read_u32(np, "rockchip,dram_dll_dis_freq",
&timing->dram_dll_dis_freq);
ret |= of_property_read_u32(np, "rockchip,phy_dll_dis_freq",
&timing->phy_dll_dis_freq);
ret |= of_property_read_u32(np, "rockchip,ddr3_odt_dis_freq",
&timing->ddr3_odt_dis_freq);
ret |= of_property_read_u32(np, "rockchip,ddr3_drv",
&timing->ddr3_drv);
ret |= of_property_read_u32(np, "rockchip,ddr3_odt",
&timing->ddr3_odt);
ret |= of_property_read_u32(np, "rockchip,phy_ddr3_ca_drv",
&timing->phy_ddr3_ca_drv);
ret |= of_property_read_u32(np, "rockchip,phy_ddr3_dq_drv",
&timing->phy_ddr3_dq_drv);
ret |= of_property_read_u32(np, "rockchip,phy_ddr3_odt",
&timing->phy_ddr3_odt);
ret |= of_property_read_u32(np, "rockchip,lpddr3_odt_dis_freq",
&timing->lpddr3_odt_dis_freq);
ret |= of_property_read_u32(np, "rockchip,lpddr3_drv",
&timing->lpddr3_drv);
ret |= of_property_read_u32(np, "rockchip,lpddr3_odt",
&timing->lpddr3_odt);
ret |= of_property_read_u32(np, "rockchip,phy_lpddr3_ca_drv",
&timing->phy_lpddr3_ca_drv);
ret |= of_property_read_u32(np, "rockchip,phy_lpddr3_dq_drv",
&timing->phy_lpddr3_dq_drv);
ret |= of_property_read_u32(np, "rockchip,phy_lpddr3_odt",
&timing->phy_lpddr3_odt);
ret |= of_property_read_u32(np, "rockchip,lpddr4_odt_dis_freq",
&timing->lpddr4_odt_dis_freq);
ret |= of_property_read_u32(np, "rockchip,lpddr4_drv",
&timing->lpddr4_drv);
ret |= of_property_read_u32(np, "rockchip,lpddr4_dq_odt",
&timing->lpddr4_dq_odt);
ret |= of_property_read_u32(np, "rockchip,lpddr4_ca_odt",
&timing->lpddr4_ca_odt);
ret |= of_property_read_u32(np, "rockchip,phy_lpddr4_ca_drv",
&timing->phy_lpddr4_ca_drv);
ret |= of_property_read_u32(np, "rockchip,phy_lpddr4_ck_cs_drv",
&timing->phy_lpddr4_ck_cs_drv);
ret |= of_property_read_u32(np, "rockchip,phy_lpddr4_dq_drv",
&timing->phy_lpddr4_dq_drv);
ret |= of_property_read_u32(np, "rockchip,phy_lpddr4_odt",
&timing->phy_lpddr4_odt);
return ret;
}
static int rk3399_dmcfreq_probe(struct platform_device *pdev)
{
struct arm_smccc_res res;
struct device *dev = &pdev->dev;
struct device_node *np = pdev->dev.of_node;
struct rk3399_dmcfreq *data;
int ret, index, size;
uint32_t *timing;
struct dev_pm_opp *opp;
data = devm_kzalloc(dev, sizeof(struct rk3399_dmcfreq), GFP_KERNEL);
if (!data)
return -ENOMEM;
mutex_init(&data->lock);
data->vdd_center = devm_regulator_get(dev, "center");
if (IS_ERR(data->vdd_center)) {
if (PTR_ERR(data->vdd_center) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_err(dev, "Cannot get the regulator \"center\"\n");
return PTR_ERR(data->vdd_center);
}
data->dmc_clk = devm_clk_get(dev, "dmc_clk");
if (IS_ERR(data->dmc_clk)) {
if (PTR_ERR(data->dmc_clk) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_err(dev, "Cannot get the clk dmc_clk\n");
return PTR_ERR(data->dmc_clk);
};
data->edev = devfreq_event_get_edev_by_phandle(dev, 0);
if (IS_ERR(data->edev))
return -EPROBE_DEFER;
ret = devfreq_event_enable_edev(data->edev);
if (ret < 0) {
dev_err(dev, "failed to enable devfreq-event devices\n");
return ret;
}
/*
* Get dram timing and pass it to arm trust firmware,
* the dram drvier in arm trust firmware will get these
* timing and to do dram initial.
*/
if (!of_get_ddr_timings(&data->timing, np)) {
timing = &data->timing.ddr3_speed_bin;
size = sizeof(struct dram_timing) / 4;
for (index = 0; index < size; index++) {
arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, *timing++, index,
ROCKCHIP_SIP_CONFIG_DRAM_SET_PARAM,
0, 0, 0, 0, &res);
if (res.a0) {
dev_err(dev, "Failed to set dram param: %ld\n",
res.a0);
return -EINVAL;
}
}
}
arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0,
ROCKCHIP_SIP_CONFIG_DRAM_INIT,
0, 0, 0, 0, &res);
/*
* We add a devfreq driver to our parent since it has a device tree node
* with operating points.
*/
if (dev_pm_opp_of_add_table(dev)) {
dev_err(dev, "Invalid operating-points in device tree.\n");
return -EINVAL;
}
of_property_read_u32(np, "upthreshold",
&data->ondemand_data.upthreshold);
of_property_read_u32(np, "downdifferential",
&data->ondemand_data.downdifferential);
data->rate = clk_get_rate(data->dmc_clk);
opp = devfreq_recommended_opp(dev, &data->rate, 0);
if (IS_ERR(opp)) {
ret = PTR_ERR(opp);
goto err_free_opp;
}
data->rate = dev_pm_opp_get_freq(opp);
data->volt = dev_pm_opp_get_voltage(opp);
dev_pm_opp_put(opp);
rk3399_devfreq_dmc_profile.initial_freq = data->rate;
data->devfreq = devm_devfreq_add_device(dev,
&rk3399_devfreq_dmc_profile,
DEVFREQ_GOV_SIMPLE_ONDEMAND,
&data->ondemand_data);
if (IS_ERR(data->devfreq)) {
ret = PTR_ERR(data->devfreq);
goto err_free_opp;
}
devm_devfreq_register_opp_notifier(dev, data->devfreq);
data->dev = dev;
platform_set_drvdata(pdev, data);
return 0;
err_free_opp:
dev_pm_opp_of_remove_table(&pdev->dev);
return ret;
}
static int rk3399_dmcfreq_remove(struct platform_device *pdev)
{
struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(&pdev->dev);
/*
* Before remove the opp table we need to unregister the opp notifier.
*/
devm_devfreq_unregister_opp_notifier(dmcfreq->dev, dmcfreq->devfreq);
dev_pm_opp_of_remove_table(dmcfreq->dev);
return 0;
}
static const struct of_device_id rk3399dmc_devfreq_of_match[] = {
{ .compatible = "rockchip,rk3399-dmc" },
{ },
};
MODULE_DEVICE_TABLE(of, rk3399dmc_devfreq_of_match);
static struct platform_driver rk3399_dmcfreq_driver = {
.probe = rk3399_dmcfreq_probe,
.remove = rk3399_dmcfreq_remove,
.driver = {
.name = "rk3399-dmc-freq",
.pm = &rk3399_dmcfreq_pm,
.of_match_table = rk3399dmc_devfreq_of_match,
},
};
module_platform_driver(rk3399_dmcfreq_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Lin Huang <hl@rock-chips.com>");
MODULE_DESCRIPTION("RK3399 dmcfreq driver with devfreq framework");
+788
View File
@@ -0,0 +1,788 @@
/*
* A devfreq driver for NVIDIA Tegra SoCs
*
* Copyright (c) 2014 NVIDIA CORPORATION. All rights reserved.
* Copyright (C) 2014 Google, Inc
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <linux/devfreq.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/reset.h>
#include "governor.h"
#define ACTMON_GLB_STATUS 0x0
#define ACTMON_GLB_PERIOD_CTRL 0x4
#define ACTMON_DEV_CTRL 0x0
#define ACTMON_DEV_CTRL_K_VAL_SHIFT 10
#define ACTMON_DEV_CTRL_ENB_PERIODIC BIT(18)
#define ACTMON_DEV_CTRL_AVG_BELOW_WMARK_EN BIT(20)
#define ACTMON_DEV_CTRL_AVG_ABOVE_WMARK_EN BIT(21)
#define ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_NUM_SHIFT 23
#define ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_NUM_SHIFT 26
#define ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN BIT(29)
#define ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN BIT(30)
#define ACTMON_DEV_CTRL_ENB BIT(31)
#define ACTMON_DEV_UPPER_WMARK 0x4
#define ACTMON_DEV_LOWER_WMARK 0x8
#define ACTMON_DEV_INIT_AVG 0xc
#define ACTMON_DEV_AVG_UPPER_WMARK 0x10
#define ACTMON_DEV_AVG_LOWER_WMARK 0x14
#define ACTMON_DEV_COUNT_WEIGHT 0x18
#define ACTMON_DEV_AVG_COUNT 0x20
#define ACTMON_DEV_INTR_STATUS 0x24
#define ACTMON_INTR_STATUS_CLEAR 0xffffffff
#define ACTMON_DEV_INTR_CONSECUTIVE_UPPER BIT(31)
#define ACTMON_DEV_INTR_CONSECUTIVE_LOWER BIT(30)
#define ACTMON_ABOVE_WMARK_WINDOW 1
#define ACTMON_BELOW_WMARK_WINDOW 3
#define ACTMON_BOOST_FREQ_STEP 16000
/*
* Activity counter is incremented every 256 memory transactions, and each
* transaction takes 4 EMC clocks for Tegra124; So the COUNT_WEIGHT is
* 4 * 256 = 1024.
*/
#define ACTMON_COUNT_WEIGHT 0x400
/*
* ACTMON_AVERAGE_WINDOW_LOG2: default value for @DEV_CTRL_K_VAL, which
* translates to 2 ^ (K_VAL + 1). ex: 2 ^ (6 + 1) = 128
*/
#define ACTMON_AVERAGE_WINDOW_LOG2 6
#define ACTMON_SAMPLING_PERIOD 12 /* ms */
#define ACTMON_DEFAULT_AVG_BAND 6 /* 1/10 of % */
#define KHZ 1000
#define KHZ_MAX (ULONG_MAX / KHZ)
/* Assume that the bus is saturated if the utilization is 25% */
#define BUS_SATURATION_RATIO 25
/**
* struct tegra_devfreq_device_config - configuration specific to an ACTMON
* device
*
* Coefficients and thresholds are percentages unless otherwise noted
*/
struct tegra_devfreq_device_config {
u32 offset;
u32 irq_mask;
/* Factors applied to boost_freq every consecutive watermark breach */
unsigned int boost_up_coeff;
unsigned int boost_down_coeff;
/* Define the watermark bounds when applied to the current avg */
unsigned int boost_up_threshold;
unsigned int boost_down_threshold;
/*
* Threshold of activity (cycles) below which the CPU frequency isn't
* to be taken into account. This is to avoid increasing the EMC
* frequency when the CPU is very busy but not accessing the bus often.
*/
u32 avg_dependency_threshold;
};
enum tegra_actmon_device {
MCALL = 0,
MCCPU,
};
static struct tegra_devfreq_device_config actmon_device_configs[] = {
{
/* MCALL: All memory accesses (including from the CPUs) */
.offset = 0x1c0,
.irq_mask = 1 << 26,
.boost_up_coeff = 200,
.boost_down_coeff = 50,
.boost_up_threshold = 60,
.boost_down_threshold = 40,
},
{
/* MCCPU: memory accesses from the CPUs */
.offset = 0x200,
.irq_mask = 1 << 25,
.boost_up_coeff = 800,
.boost_down_coeff = 90,
.boost_up_threshold = 27,
.boost_down_threshold = 10,
.avg_dependency_threshold = 50000,
},
};
/**
* struct tegra_devfreq_device - state specific to an ACTMON device
*
* Frequencies are in kHz.
*/
struct tegra_devfreq_device {
const struct tegra_devfreq_device_config *config;
void __iomem *regs;
spinlock_t lock;
/* Average event count sampled in the last interrupt */
u32 avg_count;
/*
* Extra frequency to increase the target by due to consecutive
* watermark breaches.
*/
unsigned long boost_freq;
/* Optimal frequency calculated from the stats for this device */
unsigned long target_freq;
};
struct tegra_devfreq {
struct devfreq *devfreq;
struct reset_control *reset;
struct clk *clock;
void __iomem *regs;
struct clk *emc_clock;
unsigned long max_freq;
unsigned long cur_freq;
struct notifier_block rate_change_nb;
struct tegra_devfreq_device devices[ARRAY_SIZE(actmon_device_configs)];
};
struct tegra_actmon_emc_ratio {
unsigned long cpu_freq;
unsigned long emc_freq;
};
static struct tegra_actmon_emc_ratio actmon_emc_ratios[] = {
{ 1400000, KHZ_MAX },
{ 1200000, 750000 },
{ 1100000, 600000 },
{ 1000000, 500000 },
{ 800000, 375000 },
{ 500000, 200000 },
{ 250000, 100000 },
};
static u32 actmon_readl(struct tegra_devfreq *tegra, u32 offset)
{
return readl(tegra->regs + offset);
}
static void actmon_writel(struct tegra_devfreq *tegra, u32 val, u32 offset)
{
writel(val, tegra->regs + offset);
}
static u32 device_readl(struct tegra_devfreq_device *dev, u32 offset)
{
return readl(dev->regs + offset);
}
static void device_writel(struct tegra_devfreq_device *dev, u32 val,
u32 offset)
{
writel(val, dev->regs + offset);
}
static unsigned long do_percent(unsigned long val, unsigned int pct)
{
return val * pct / 100;
}
static void tegra_devfreq_update_avg_wmark(struct tegra_devfreq *tegra,
struct tegra_devfreq_device *dev)
{
u32 avg = dev->avg_count;
u32 avg_band_freq = tegra->max_freq * ACTMON_DEFAULT_AVG_BAND / KHZ;
u32 band = avg_band_freq * ACTMON_SAMPLING_PERIOD;
device_writel(dev, avg + band, ACTMON_DEV_AVG_UPPER_WMARK);
avg = max(dev->avg_count, band);
device_writel(dev, avg - band, ACTMON_DEV_AVG_LOWER_WMARK);
}
static void tegra_devfreq_update_wmark(struct tegra_devfreq *tegra,
struct tegra_devfreq_device *dev)
{
u32 val = tegra->cur_freq * ACTMON_SAMPLING_PERIOD;
device_writel(dev, do_percent(val, dev->config->boost_up_threshold),
ACTMON_DEV_UPPER_WMARK);
device_writel(dev, do_percent(val, dev->config->boost_down_threshold),
ACTMON_DEV_LOWER_WMARK);
}
static void actmon_write_barrier(struct tegra_devfreq *tegra)
{
/* ensure the update has reached the ACTMON */
wmb();
actmon_readl(tegra, ACTMON_GLB_STATUS);
}
static void actmon_isr_device(struct tegra_devfreq *tegra,
struct tegra_devfreq_device *dev)
{
unsigned long flags;
u32 intr_status, dev_ctrl;
spin_lock_irqsave(&dev->lock, flags);
dev->avg_count = device_readl(dev, ACTMON_DEV_AVG_COUNT);
tegra_devfreq_update_avg_wmark(tegra, dev);
intr_status = device_readl(dev, ACTMON_DEV_INTR_STATUS);
dev_ctrl = device_readl(dev, ACTMON_DEV_CTRL);
if (intr_status & ACTMON_DEV_INTR_CONSECUTIVE_UPPER) {
/*
* new_boost = min(old_boost * up_coef + step, max_freq)
*/
dev->boost_freq = do_percent(dev->boost_freq,
dev->config->boost_up_coeff);
dev->boost_freq += ACTMON_BOOST_FREQ_STEP;
dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN;
if (dev->boost_freq >= tegra->max_freq)
dev->boost_freq = tegra->max_freq;
else
dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN;
} else if (intr_status & ACTMON_DEV_INTR_CONSECUTIVE_LOWER) {
/*
* new_boost = old_boost * down_coef
* or 0 if (old_boost * down_coef < step / 2)
*/
dev->boost_freq = do_percent(dev->boost_freq,
dev->config->boost_down_coeff);
dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN;
if (dev->boost_freq < (ACTMON_BOOST_FREQ_STEP >> 1))
dev->boost_freq = 0;
else
dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN;
}
if (dev->config->avg_dependency_threshold) {
if (dev->avg_count >= dev->config->avg_dependency_threshold)
dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN;
else if (dev->boost_freq == 0)
dev_ctrl &= ~ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN;
}
device_writel(dev, dev_ctrl, ACTMON_DEV_CTRL);
device_writel(dev, ACTMON_INTR_STATUS_CLEAR, ACTMON_DEV_INTR_STATUS);
actmon_write_barrier(tegra);
spin_unlock_irqrestore(&dev->lock, flags);
}
static irqreturn_t actmon_isr(int irq, void *data)
{
struct tegra_devfreq *tegra = data;
bool handled = false;
unsigned int i;
u32 val;
val = actmon_readl(tegra, ACTMON_GLB_STATUS);
for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) {
if (val & tegra->devices[i].config->irq_mask) {
actmon_isr_device(tegra, tegra->devices + i);
handled = true;
}
}
return handled ? IRQ_WAKE_THREAD : IRQ_NONE;
}
static unsigned long actmon_cpu_to_emc_rate(struct tegra_devfreq *tegra,
unsigned long cpu_freq)
{
unsigned int i;
struct tegra_actmon_emc_ratio *ratio = actmon_emc_ratios;
for (i = 0; i < ARRAY_SIZE(actmon_emc_ratios); i++, ratio++) {
if (cpu_freq >= ratio->cpu_freq) {
if (ratio->emc_freq >= tegra->max_freq)
return tegra->max_freq;
else
return ratio->emc_freq;
}
}
return 0;
}
static void actmon_update_target(struct tegra_devfreq *tegra,
struct tegra_devfreq_device *dev)
{
unsigned long cpu_freq = 0;
unsigned long static_cpu_emc_freq = 0;
unsigned int avg_sustain_coef;
unsigned long flags;
if (dev->config->avg_dependency_threshold) {
cpu_freq = cpufreq_get(0);
static_cpu_emc_freq = actmon_cpu_to_emc_rate(tegra, cpu_freq);
}
spin_lock_irqsave(&dev->lock, flags);
dev->target_freq = dev->avg_count / ACTMON_SAMPLING_PERIOD;
avg_sustain_coef = 100 * 100 / dev->config->boost_up_threshold;
dev->target_freq = do_percent(dev->target_freq, avg_sustain_coef);
dev->target_freq += dev->boost_freq;
if (dev->avg_count >= dev->config->avg_dependency_threshold)
dev->target_freq = max(dev->target_freq, static_cpu_emc_freq);
spin_unlock_irqrestore(&dev->lock, flags);
}
static irqreturn_t actmon_thread_isr(int irq, void *data)
{
struct tegra_devfreq *tegra = data;
mutex_lock(&tegra->devfreq->lock);
update_devfreq(tegra->devfreq);
mutex_unlock(&tegra->devfreq->lock);
return IRQ_HANDLED;
}
static int tegra_actmon_rate_notify_cb(struct notifier_block *nb,
unsigned long action, void *ptr)
{
struct clk_notifier_data *data = ptr;
struct tegra_devfreq *tegra;
struct tegra_devfreq_device *dev;
unsigned int i;
unsigned long flags;
if (action != POST_RATE_CHANGE)
return NOTIFY_OK;
tegra = container_of(nb, struct tegra_devfreq, rate_change_nb);
tegra->cur_freq = data->new_rate / KHZ;
for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) {
dev = &tegra->devices[i];
spin_lock_irqsave(&dev->lock, flags);
tegra_devfreq_update_wmark(tegra, dev);
spin_unlock_irqrestore(&dev->lock, flags);
}
actmon_write_barrier(tegra);
return NOTIFY_OK;
}
static void tegra_actmon_enable_interrupts(struct tegra_devfreq *tegra)
{
struct tegra_devfreq_device *dev;
u32 val;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) {
dev = &tegra->devices[i];
val = device_readl(dev, ACTMON_DEV_CTRL);
val |= ACTMON_DEV_CTRL_AVG_ABOVE_WMARK_EN;
val |= ACTMON_DEV_CTRL_AVG_BELOW_WMARK_EN;
val |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN;
val |= ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN;
device_writel(dev, val, ACTMON_DEV_CTRL);
}
actmon_write_barrier(tegra);
}
static void tegra_actmon_disable_interrupts(struct tegra_devfreq *tegra)
{
struct tegra_devfreq_device *dev;
u32 val;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) {
dev = &tegra->devices[i];
val = device_readl(dev, ACTMON_DEV_CTRL);
val &= ~ACTMON_DEV_CTRL_AVG_ABOVE_WMARK_EN;
val &= ~ACTMON_DEV_CTRL_AVG_BELOW_WMARK_EN;
val &= ~ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN;
val &= ~ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN;
device_writel(dev, val, ACTMON_DEV_CTRL);
}
actmon_write_barrier(tegra);
}
static void tegra_actmon_configure_device(struct tegra_devfreq *tegra,
struct tegra_devfreq_device *dev)
{
u32 val = 0;
dev->target_freq = tegra->cur_freq;
dev->avg_count = tegra->cur_freq * ACTMON_SAMPLING_PERIOD;
device_writel(dev, dev->avg_count, ACTMON_DEV_INIT_AVG);
tegra_devfreq_update_avg_wmark(tegra, dev);
tegra_devfreq_update_wmark(tegra, dev);
device_writel(dev, ACTMON_COUNT_WEIGHT, ACTMON_DEV_COUNT_WEIGHT);
device_writel(dev, ACTMON_INTR_STATUS_CLEAR, ACTMON_DEV_INTR_STATUS);
val |= ACTMON_DEV_CTRL_ENB_PERIODIC;
val |= (ACTMON_AVERAGE_WINDOW_LOG2 - 1)
<< ACTMON_DEV_CTRL_K_VAL_SHIFT;
val |= (ACTMON_BELOW_WMARK_WINDOW - 1)
<< ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_NUM_SHIFT;
val |= (ACTMON_ABOVE_WMARK_WINDOW - 1)
<< ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_NUM_SHIFT;
val |= ACTMON_DEV_CTRL_ENB;
device_writel(dev, val, ACTMON_DEV_CTRL);
actmon_write_barrier(tegra);
}
static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
u32 flags)
{
struct tegra_devfreq *tegra = dev_get_drvdata(dev);
struct dev_pm_opp *opp;
unsigned long rate;
opp = devfreq_recommended_opp(dev, freq, flags);
if (IS_ERR(opp)) {
dev_err(dev, "Failed to find opp for %lu Hz\n", *freq);
return PTR_ERR(opp);
}
rate = dev_pm_opp_get_freq(opp);
dev_pm_opp_put(opp);
clk_set_min_rate(tegra->emc_clock, rate);
clk_set_rate(tegra->emc_clock, 0);
return 0;
}
static int tegra_devfreq_get_dev_status(struct device *dev,
struct devfreq_dev_status *stat)
{
struct tegra_devfreq *tegra = dev_get_drvdata(dev);
struct tegra_devfreq_device *actmon_dev;
stat->current_frequency = tegra->cur_freq * KHZ;
/* To be used by the tegra governor */
stat->private_data = tegra;
/* The below are to be used by the other governors */
actmon_dev = &tegra->devices[MCALL];
/* Number of cycles spent on memory access */
stat->busy_time = device_readl(actmon_dev, ACTMON_DEV_AVG_COUNT);
/* The bus can be considered to be saturated way before 100% */
stat->busy_time *= 100 / BUS_SATURATION_RATIO;
/* Number of cycles in a sampling period */
stat->total_time = ACTMON_SAMPLING_PERIOD * tegra->cur_freq;
stat->busy_time = min(stat->busy_time, stat->total_time);
return 0;
}
static struct devfreq_dev_profile tegra_devfreq_profile = {
.polling_ms = 0,
.target = tegra_devfreq_target,
.get_dev_status = tegra_devfreq_get_dev_status,
};
static int tegra_governor_get_target(struct devfreq *devfreq,
unsigned long *freq)
{
struct devfreq_dev_status *stat;
struct tegra_devfreq *tegra;
struct tegra_devfreq_device *dev;
unsigned long target_freq = 0;
unsigned int i;
int err;
err = devfreq_update_stats(devfreq);
if (err)
return err;
stat = &devfreq->last_status;
tegra = stat->private_data;
for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) {
dev = &tegra->devices[i];
actmon_update_target(tegra, dev);
target_freq = max(target_freq, dev->target_freq);
}
*freq = target_freq * KHZ;
return 0;
}
static int tegra_governor_event_handler(struct devfreq *devfreq,
unsigned int event, void *data)
{
struct tegra_devfreq *tegra;
int ret = 0;
tegra = dev_get_drvdata(devfreq->dev.parent);
switch (event) {
case DEVFREQ_GOV_START:
devfreq_monitor_start(devfreq);
tegra_actmon_enable_interrupts(tegra);
break;
case DEVFREQ_GOV_STOP:
tegra_actmon_disable_interrupts(tegra);
devfreq_monitor_stop(devfreq);
break;
case DEVFREQ_GOV_SUSPEND:
tegra_actmon_disable_interrupts(tegra);
devfreq_monitor_suspend(devfreq);
break;
case DEVFREQ_GOV_RESUME:
devfreq_monitor_resume(devfreq);
tegra_actmon_enable_interrupts(tegra);
break;
}
return ret;
}
static struct devfreq_governor tegra_devfreq_governor = {
.name = "tegra_actmon",
.get_target_freq = tegra_governor_get_target,
.event_handler = tegra_governor_event_handler,
};
static int tegra_devfreq_probe(struct platform_device *pdev)
{
struct tegra_devfreq *tegra;
struct tegra_devfreq_device *dev;
struct resource *res;
unsigned int i;
unsigned long rate;
int irq;
int err;
tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
if (!tegra)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
tegra->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(tegra->regs))
return PTR_ERR(tegra->regs);
tegra->reset = devm_reset_control_get(&pdev->dev, "actmon");
if (IS_ERR(tegra->reset)) {
dev_err(&pdev->dev, "Failed to get reset\n");
return PTR_ERR(tegra->reset);
}
tegra->clock = devm_clk_get(&pdev->dev, "actmon");
if (IS_ERR(tegra->clock)) {
dev_err(&pdev->dev, "Failed to get actmon clock\n");
return PTR_ERR(tegra->clock);
}
tegra->emc_clock = devm_clk_get(&pdev->dev, "emc");
if (IS_ERR(tegra->emc_clock)) {
dev_err(&pdev->dev, "Failed to get emc clock\n");
return PTR_ERR(tegra->emc_clock);
}
clk_set_rate(tegra->emc_clock, ULONG_MAX);
tegra->rate_change_nb.notifier_call = tegra_actmon_rate_notify_cb;
err = clk_notifier_register(tegra->emc_clock, &tegra->rate_change_nb);
if (err) {
dev_err(&pdev->dev,
"Failed to register rate change notifier\n");
return err;
}
reset_control_assert(tegra->reset);
err = clk_prepare_enable(tegra->clock);
if (err) {
dev_err(&pdev->dev,
"Failed to prepare and enable ACTMON clock\n");
return err;
}
reset_control_deassert(tegra->reset);
tegra->max_freq = clk_round_rate(tegra->emc_clock, ULONG_MAX) / KHZ;
tegra->cur_freq = clk_get_rate(tegra->emc_clock) / KHZ;
actmon_writel(tegra, ACTMON_SAMPLING_PERIOD - 1,
ACTMON_GLB_PERIOD_CTRL);
for (i = 0; i < ARRAY_SIZE(actmon_device_configs); i++) {
dev = tegra->devices + i;
dev->config = actmon_device_configs + i;
dev->regs = tegra->regs + dev->config->offset;
spin_lock_init(&dev->lock);
tegra_actmon_configure_device(tegra, dev);
}
for (rate = 0; rate <= tegra->max_freq * KHZ; rate++) {
rate = clk_round_rate(tegra->emc_clock, rate);
dev_pm_opp_add(&pdev->dev, rate, 0);
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq);
return irq;
}
platform_set_drvdata(pdev, tegra);
err = devm_request_threaded_irq(&pdev->dev, irq, actmon_isr,
actmon_thread_isr, IRQF_SHARED,
"tegra-devfreq", tegra);
if (err) {
dev_err(&pdev->dev, "Interrupt request failed\n");
return err;
}
tegra_devfreq_profile.initial_freq = clk_get_rate(tegra->emc_clock);
tegra->devfreq = devm_devfreq_add_device(&pdev->dev,
&tegra_devfreq_profile,
"tegra_actmon",
NULL);
return 0;
}
static int tegra_devfreq_remove(struct platform_device *pdev)
{
struct tegra_devfreq *tegra = platform_get_drvdata(pdev);
int irq = platform_get_irq(pdev, 0);
u32 val;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(actmon_device_configs); i++) {
val = device_readl(&tegra->devices[i], ACTMON_DEV_CTRL);
val &= ~ACTMON_DEV_CTRL_ENB;
device_writel(&tegra->devices[i], val, ACTMON_DEV_CTRL);
}
actmon_write_barrier(tegra);
devm_free_irq(&pdev->dev, irq, tegra);
clk_notifier_unregister(tegra->emc_clock, &tegra->rate_change_nb);
clk_disable_unprepare(tegra->clock);
return 0;
}
static const struct of_device_id tegra_devfreq_of_match[] = {
{ .compatible = "nvidia,tegra124-actmon" },
{ },
};
MODULE_DEVICE_TABLE(of, tegra_devfreq_of_match);
static struct platform_driver tegra_devfreq_driver = {
.probe = tegra_devfreq_probe,
.remove = tegra_devfreq_remove,
.driver = {
.name = "tegra-devfreq",
.of_match_table = tegra_devfreq_of_match,
},
};
static int __init tegra_devfreq_init(void)
{
int ret = 0;
ret = devfreq_add_governor(&tegra_devfreq_governor);
if (ret) {
pr_err("%s: failed to add governor: %d\n", __func__, ret);
return ret;
}
ret = platform_driver_register(&tegra_devfreq_driver);
if (ret)
devfreq_remove_governor(&tegra_devfreq_governor);
return ret;
}
module_init(tegra_devfreq_init)
static void __exit tegra_devfreq_exit(void)
{
int ret = 0;
platform_driver_unregister(&tegra_devfreq_driver);
ret = devfreq_remove_governor(&tegra_devfreq_governor);
if (ret)
pr_err("%s: failed to remove governor: %d\n", __func__, ret);
}
module_exit(tegra_devfreq_exit)
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Tegra devfreq driver");
MODULE_AUTHOR("Tomeu Vizoso <tomeu.vizoso@collabora.com>");