diff options
-rw-r--r-- | lwis_device.c | 3 | ||||
-rw-r--r-- | lwis_device.h | 5 | ||||
-rw-r--r-- | lwis_dpm.c | 30 | ||||
-rw-r--r-- | lwis_dpm.h | 8 | ||||
-rw-r--r-- | lwis_dt.c | 9 | ||||
-rw-r--r-- | lwis_platform.h | 12 | ||||
-rw-r--r-- | platform/exynos/lwis_platform_exynos.c | 151 |
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; }; /* @@ -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, @@ -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. */ @@ -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; +} |