summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lwis_device.c3
-rw-r--r--lwis_device.h5
-rw-r--r--lwis_dpm.c30
-rw-r--r--lwis_dpm.h8
-rw-r--r--lwis_dt.c9
-rw-r--r--lwis_platform.h12
-rw-r--r--platform/exynos/lwis_platform_exynos.c151
7 files changed, 172 insertions, 46 deletions
diff --git a/lwis_device.c b/lwis_device.c
index 4c8d318..71205ef 100644
--- a/lwis_device.c
+++ b/lwis_device.c
@@ -26,6 +26,7 @@
#include "lwis_commands.h"
#include "lwis_debug.h"
#include "lwis_device.h"
+#include "lwis_dpm.h"
#include "lwis_dt.h"
#include "lwis_event.h"
#include "lwis_gpio.h"
@@ -768,6 +769,8 @@ int lwis_base_probe(struct lwis_device *lwis_dev,
/* Initialize enabled state */
lwis_dev->enabled = 0;
+ lwis_dev->clock_family = CLOCK_FAMILY_INVALID;
+ lwis_dev->last_requested_clock = 0;
/* Initialize client mutex */
mutex_init(&lwis_dev->client_lock);
diff --git a/lwis_device.h b/lwis_device.h
index 5559247..cb8810f 100644
--- a/lwis_device.h
+++ b/lwis_device.h
@@ -200,6 +200,11 @@ struct lwis_device {
#endif
/* Structure to store info to help debugging device data */
struct lwis_device_debug_info debug_info;
+
+ /* clock family this device belongs to */
+ int clock_family;
+ /* last qos requested in khz */
+ uint32_t last_requested_clock;
};
/*
diff --git a/lwis_dpm.c b/lwis_dpm.c
index 7a8941f..0f51d0f 100644
--- a/lwis_dpm.c
+++ b/lwis_dpm.c
@@ -10,10 +10,12 @@
#define pr_fmt(fmt) KBUILD_MODNAME "-dpm: " fmt
+#include "lwis_dpm.h"
+
#include <linux/clk.h>
#include <linux/slab.h>
-#include "lwis_dpm.h"
+#include "lwis_platform.h"
/*
* lwis_dpm_update_clock: update clock settings to lwis device.
@@ -30,6 +32,7 @@ int lwis_dpm_update_clock(struct lwis_device *lwis_dev,
ret = -EINVAL;
goto out;
}
+
for (i = 0; i < num_settings; ++i) {
clk_index = clk_settings[i].clk_index;
old_clk = clk_get_rate(lwis_dev->clocks->clk[clk_index].clk);
@@ -42,14 +45,23 @@ int lwis_dpm_update_clock(struct lwis_device *lwis_dev,
ret = -EINVAL;
goto out;
}
- ret = clk_set_rate(lwis_dev->clocks->clk[clk_index].clk,
- clk_settings[i].frequency);
- if (ret) {
- dev_err(lwis_dev->dev,
- "Error updating clock %s freq: %u\n",
- lwis_dev->clocks->clk[clk_index].name,
- clk_settings[i].frequency);
- goto out;
+
+ if (clk_index == 0 &&
+ lwis_dev->clock_family != CLOCK_FAMILY_INVALID &&
+ lwis_dev->clock_family < CLOCK_FAMILY_MAX) {
+ /* convert value to KHz */
+ lwis_platform_update_qos(
+ lwis_dev, clk_settings[i].frequency / 1000);
+ } else {
+ ret = clk_set_rate(lwis_dev->clocks->clk[clk_index].clk,
+ clk_settings[i].frequency);
+ if (ret) {
+ dev_err(lwis_dev->dev,
+ "Error updating clock %s freq: %u\n",
+ lwis_dev->clocks->clk[clk_index].name,
+ clk_settings[i].frequency);
+ goto out;
+ }
}
dev_info(lwis_dev->dev,
diff --git a/lwis_dpm.h b/lwis_dpm.h
index 944c970..7f5746a 100644
--- a/lwis_dpm.h
+++ b/lwis_dpm.h
@@ -14,6 +14,14 @@
#include "lwis_commands.h"
#include "lwis_device.h"
+enum clock_family {
+ CLOCK_FAMILY_INVALID = -1,
+ CLOCK_FAMILY_CAM,
+ CLOCK_FAMILY_INTCAM,
+ CLOCK_FAMILY_TNR,
+ CLOCK_FAMILY_MAX
+};
+
/*
* lwis_dpm_update_clock: update clock setting to lwis device.
*/
diff --git a/lwis_dt.c b/lwis_dt.c
index 2c7e942..beff6b2 100644
--- a/lwis_dt.c
+++ b/lwis_dt.c
@@ -20,6 +20,7 @@
#include <linux/slab.h>
#include "lwis_clock.h"
+#include "lwis_dpm.h"
#include "lwis_gpio.h"
#include "lwis_i2c.h"
#include "lwis_ioreg.h"
@@ -130,6 +131,7 @@ static int parse_clocks(struct lwis_device *lwis_dev)
struct device_node *dev_node;
const char *name;
u32 rate;
+ int clock_family;
dev = &lwis_dev->plat_dev->dev;
dev_node = dev->of_node;
@@ -159,6 +161,13 @@ static int parse_clocks(struct lwis_device *lwis_dev)
}
}
+ /* It is allowed to omit clock rates for some of the clocks */
+ ret = of_property_read_u32(dev_node, "clock-family", &clock_family);
+ lwis_dev->clock_family =
+ (ret == 0) ? clock_family : CLOCK_FAMILY_INVALID;
+
+ pr_info("%s: clock family %d", lwis_dev->name, lwis_dev->clock_family);
+
lwis_clock_print(lwis_dev->clocks);
return 0;
diff --git a/lwis_platform.h b/lwis_platform.h
index 7e46ad2..cde261b 100644
--- a/lwis_platform.h
+++ b/lwis_platform.h
@@ -31,4 +31,16 @@ int lwis_platform_device_enable(struct lwis_device *lwis_dev);
*/
int lwis_platform_device_disable(struct lwis_device *lwis_dev);
+/*
+ * lwis_platform_update_qos: handles platform-specific parts of
+ * updating qos requirements. "value" is in KHz.
+ */
+int lwis_platform_update_qos(struct lwis_device *lwis_dev, uint32_t value);
+
+/*
+ * lwis_platform_remove_qos: handles platform-specific parts of
+ * removing qos requirements.
+ */
+int lwis_platform_remove_qos(struct lwis_device *lwis_dev);
+
#endif /* LWIS_PLATFORM_H_ */
diff --git a/platform/exynos/lwis_platform_exynos.c b/platform/exynos/lwis_platform_exynos.c
index 64cda92..989eedc 100644
--- a/platform/exynos/lwis_platform_exynos.c
+++ b/platform/exynos/lwis_platform_exynos.c
@@ -8,14 +8,16 @@
* published by the Free Software Foundation.
*/
-#include <linux/slab.h>
-#include <linux/pm_qos.h>
-#include <linux/iommu.h>
+#include "lwis_platform_exynos.h"
+
#include <linux/exynos_iovmm.h>
+#include <linux/iommu.h>
#include <linux/of.h>
+#include <linux/pm_qos.h>
+#include <linux/slab.h>
+#include "lwis_dpm.h"
#include "lwis_platform.h"
-#include "lwis_platform_exynos.h"
int lwis_platform_probe(struct lwis_device *lwis_dev)
{
@@ -48,14 +50,11 @@ int lwis_platform_device_enable(struct lwis_device *lwis_dev)
int ret;
struct lwis_platform *platform;
// TODO: Refactor
- const uint32_t int_cam_qos = 680000;
+
const uint32_t int_qos = 465000;
const uint32_t mif_qos = 2093000;
- const uint32_t cam_qos = 680000;
const uint32_t hpg_qos = 1;
-#if defined(CONFIG_SOC_GS101)
- const uint32_t tnr_qos = 664000;
-#endif
+
BUG_ON(!lwis_dev);
platform = lwis_dev->platform;
if (!platform) {
@@ -80,27 +79,20 @@ int lwis_platform_device_enable(struct lwis_device *lwis_dev)
iovmm_set_fault_handler(&lwis_dev->plat_dev->dev,
iovmm_fault_handler, lwis_dev);
}
+
/* Set hardcoded DVFS levels */
- if (!pm_qos_request_active(&platform->pm_qos_int_cam))
- pm_qos_add_request(&platform->pm_qos_int_cam,
- PM_QOS_INTCAM_THROUGHPUT, int_cam_qos);
- if (!pm_qos_request_active(&platform->pm_qos_int))
- pm_qos_add_request(&platform->pm_qos_int,
- PM_QOS_DEVICE_THROUGHPUT, int_qos);
+
if (!pm_qos_request_active(&platform->pm_qos_mem))
pm_qos_add_request(&platform->pm_qos_mem, PM_QOS_BUS_THROUGHPUT,
mif_qos);
- if (!pm_qos_request_active(&platform->pm_qos_cam))
- pm_qos_add_request(&platform->pm_qos_cam, PM_QOS_CAM_THROUGHPUT,
- cam_qos);
+ if (!pm_qos_request_active(&platform->pm_qos_int))
+ pm_qos_add_request(&platform->pm_qos_int,
+ PM_QOS_DEVICE_THROUGHPUT, int_qos);
if (!pm_qos_request_active(&platform->pm_qos_hpg))
pm_qos_add_request(&platform->pm_qos_hpg, PM_QOS_CPU_ONLINE_MIN,
hpg_qos);
-#if defined(CONFIG_SOC_GS101)
- if (!pm_qos_request_active(&platform->pm_qos_tnr))
- pm_qos_add_request(&platform->pm_qos_tnr, PM_QOS_TNR_THROUGHPUT,
- tnr_qos);
-#endif
+
+ lwis_platform_update_qos(lwis_dev, 680000);
return 0;
}
@@ -118,20 +110,7 @@ int lwis_platform_device_disable(struct lwis_device *lwis_dev)
/* We can't remove fault handlers, so there's no call corresponding
* to the iovmm_set_fault_handler above */
- if (pm_qos_request_active(&platform->pm_qos_int_cam))
- pm_qos_remove_request(&platform->pm_qos_int_cam);
- if (pm_qos_request_active(&platform->pm_qos_int))
- pm_qos_remove_request(&platform->pm_qos_int);
- if (pm_qos_request_active(&platform->pm_qos_mem))
- pm_qos_remove_request(&platform->pm_qos_mem);
- if (pm_qos_request_active(&platform->pm_qos_cam))
- pm_qos_remove_request(&platform->pm_qos_cam);
- if (pm_qos_request_active(&platform->pm_qos_hpg))
- pm_qos_remove_request(&platform->pm_qos_hpg);
-#if defined(CONFIG_SOC_GS101)
- if (pm_qos_request_active(&platform->pm_qos_tnr))
- pm_qos_remove_request(&platform->pm_qos_tnr);
-#endif
+ lwis_platform_remove_qos(lwis_dev);
if (lwis_dev->has_iommu) {
/* Deactivate IOMMU/SYSMMU */
@@ -143,3 +122,101 @@ int lwis_platform_device_disable(struct lwis_device *lwis_dev)
return ret;
}
+
+int lwis_platform_update_qos(struct lwis_device *lwis_dev, uint32_t value)
+{
+ struct lwis_platform *platform;
+ BUG_ON(!lwis_dev);
+ platform = lwis_dev->platform;
+ if (!platform) {
+ return -ENODEV;
+ }
+
+ if (lwis_dev->last_requested_clock == value || value == 0) {
+ return 0;
+ }
+
+ switch (lwis_dev->clock_family) {
+ case CLOCK_FAMILY_INTCAM:
+ if (!pm_qos_request_active(&platform->pm_qos_int_cam)) {
+ pm_qos_add_request(&platform->pm_qos_int_cam,
+ PM_QOS_INTCAM_THROUGHPUT, value);
+ } else {
+ pm_qos_update_request(&platform->pm_qos_int_cam, value);
+ }
+ break;
+ case CLOCK_FAMILY_CAM:
+ if (!pm_qos_request_active(&platform->pm_qos_cam)) {
+ pm_qos_add_request(&platform->pm_qos_cam,
+ PM_QOS_CAM_THROUGHPUT, value);
+ } else {
+ pm_qos_update_request(&platform->pm_qos_cam, value);
+ }
+ break;
+ case CLOCK_FAMILY_TNR:
+#if defined(CONFIG_SOC_GS101)
+ if (!pm_qos_request_active(&platform->pm_qos_tnr)) {
+ pm_qos_add_request(&platform->pm_qos_tnr,
+ PM_QOS_TNR_THROUGHPUT, value);
+ } else {
+ pm_qos_update_request(&platform->pm_qos_tnr, value);
+ }
+#endif
+ break;
+ default:
+ dev_err(lwis_dev->dev, "%s clk family %d is invalid\n",
+ lwis_dev->name, lwis_dev->clock_family);
+ return -EINVAL;
+ break;
+ }
+
+ dev_info(lwis_dev->dev,
+ "Updating clock for clock_family %d, freq from %u to %u\n",
+ lwis_dev->clock_family, lwis_dev->last_requested_clock, value);
+ lwis_dev->last_requested_clock = value;
+
+ return 0;
+}
+
+int lwis_platform_remove_qos(struct lwis_device *lwis_dev)
+{
+ struct lwis_platform *platform;
+ BUG_ON(!lwis_dev);
+ platform = lwis_dev->platform;
+ if (!platform) {
+ return -ENODEV;
+ }
+
+ if (pm_qos_request_active(&platform->pm_qos_int))
+ pm_qos_remove_request(&platform->pm_qos_int);
+ if (pm_qos_request_active(&platform->pm_qos_mem))
+ pm_qos_remove_request(&platform->pm_qos_mem);
+ if (pm_qos_request_active(&platform->pm_qos_hpg))
+ pm_qos_remove_request(&platform->pm_qos_hpg);
+
+ switch (lwis_dev->clock_family) {
+ case CLOCK_FAMILY_INTCAM:
+ if (pm_qos_request_active(&platform->pm_qos_int_cam)) {
+ pm_qos_remove_request(&platform->pm_qos_int_cam);
+ }
+ lwis_dev->last_requested_clock = 0;
+ break;
+ case CLOCK_FAMILY_CAM:
+ if (pm_qos_request_active(&platform->pm_qos_cam)) {
+ pm_qos_remove_request(&platform->pm_qos_cam);
+ }
+ lwis_dev->last_requested_clock = 0;
+ break;
+ case CLOCK_FAMILY_TNR:
+#if defined(CONFIG_SOC_GS101)
+ if (pm_qos_request_active(&platform->pm_qos_tnr)) {
+ pm_qos_remove_request(&platform->pm_qos_tnr);
+ }
+ lwis_dev->last_requested_clock = 0;
+#endif
+ break;
+ default:
+ break;
+ }
+ return 0;
+}