diff options
author | Edmond Chung <edmondchung@google.com> | 2020-08-11 14:12:50 -0700 |
---|---|---|
committer | Edmond Chung <edmondchung@google.com> | 2020-08-11 17:14:47 -0700 |
commit | 1fa0e85e78138a8bb4934e103861017cbd48d951 (patch) | |
tree | f9a764b9a11e21b61f2cc99baf2dccb384f86613 /platform | |
parent | 1714a7fec781dc40e446ed829f80fb0225f3344e (diff) | |
download | lwis-1fa0e85e78138a8bb4934e103861017cbd48d951.tar.gz |
Platform: Add GS101 platform-specific functions.
Most of the functions are stubbed out as implementation will be done
later.
- PM QOS related functions has not been migrated to 5.4 yet.
- Exynos IOVMM is deprecated, will need replacements.
Bug: 158222731
Bug: 162961559
Signed-off-by: Edmond Chung <edmondchung@google.com>
Change-Id: I41fe702eeac49728a7f75088761107525e828516
Diffstat (limited to 'platform')
-rw-r--r-- | platform/gs101/lwis_platform_gs101.c | 295 | ||||
-rw-r--r-- | platform/gs101/lwis_platform_gs101.h | 27 | ||||
-rw-r--r-- | platform/gs101/lwis_platform_gs101_dma.c | 79 |
3 files changed, 401 insertions, 0 deletions
diff --git a/platform/gs101/lwis_platform_gs101.c b/platform/gs101/lwis_platform_gs101.c new file mode 100644 index 0000000..a244a77 --- /dev/null +++ b/platform/gs101/lwis_platform_gs101.c @@ -0,0 +1,295 @@ +/* + * Google LWIS GS101 Platform-Specific Functions + * + * Copyright (c) 2020 Google, LLC + * + * 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. + */ + +/* IOVMM is deprecated on 5.4, need replacement for these code blocks */ +#define ENABLE_IOVMM 0 +/* PM QOS is not ported to 5.4 yet */ +#define ENABLE_QOS 0 + +#include "lwis_platform_gs101.h" + +#if ENABLE_IOVMM +#include <linux/exynos_iovmm.h> +#endif +#include <linux/iommu.h> +#include <linux/of.h> +#if ENABLE_QOS +#include <linux/pm_qos.h> +#endif +#include <linux/slab.h> + +#include "lwis_device_dpm.h" +#include "lwis_debug.h" +#include "lwis_platform.h" + +/* Uncomment to let kernel panic when IOMMU hits a page fault. */ +/* TODO: Add error handling to propagate SysMMU errors back to userspace, + * so we don't need to panic here. */ +#define ENABLE_PAGE_FAULT_PANIC + +int lwis_platform_probe(struct lwis_device *lwis_dev) +{ + struct lwis_platform *platform; + + if (!lwis_dev) { + return -ENODEV; + } + + platform = kzalloc(sizeof(struct lwis_platform), GFP_KERNEL); + if (IS_ERR_OR_NULL(platform)) { + return -ENOMEM; + } + lwis_dev->platform = platform; + + /* Enable runtime power management for the platform device */ + pm_runtime_enable(&lwis_dev->plat_dev->dev); + + return 0; +} + +#if ENABLE_IOVMM +static int __attribute__((unused)) +iovmm_fault_handler(struct iommu_domain *domain, struct device *dev, + unsigned long fault_addr, int fault_flag, void *token) +{ + struct lwis_device *lwis_dev = (struct lwis_device *)token; + + pr_err("############ LWIS IOVMM PAGE FAULT ############\n"); + pr_err("\n"); + pr_err("Device: %s IOVMM Page Fault at Address: 0x%pK Flag: 0x%08x\n", + lwis_dev->name, fault_addr, fault_flag); + pr_err("\n"); + lwis_debug_print_transaction_info(lwis_dev); + pr_err("\n"); + lwis_debug_print_event_states_info(lwis_dev); + pr_err("\n"); + lwis_debug_print_buffer_info(lwis_dev); + pr_err("\n"); + pr_err("###############################################\n"); + +#ifdef ENABLE_PAGE_FAULT_PANIC + return NOTIFY_BAD; +#else + return NOTIFY_OK; +#endif /* ENABLE_PAGE_FAULT_PANIC */ +} +#endif /* ENABLE_IOVMM */ + +int lwis_platform_device_enable(struct lwis_device *lwis_dev) +{ + int ret; + struct lwis_platform *platform; + + /* TODO(b/157514330): Refactor */ +#if ENABLE_QOS + const uint32_t int_qos = 465000; + const uint32_t mif_qos = 2093000; + const uint32_t core_clock_qos = 67000; + const uint32_t hpg_qos = 1; +#endif /* ENABLE_QOS */ + + if (!lwis_dev) { + return -ENODEV; + } + + platform = lwis_dev->platform; + if (!platform) { + return -ENODEV; + } + + /* Upref the runtime power management controls for the platform dev */ + ret = pm_runtime_get_sync(&lwis_dev->plat_dev->dev); + if (ret < 0) { + pr_err("Unable to enable platform device\n"); + return ret; + } + +#if ENABLE_IOVMM + if (lwis_dev->has_iommu) { + /* Activate IOMMU/SYSMMU for the platform device */ + ret = iovmm_activate(&lwis_dev->plat_dev->dev); + if (ret < 0) { + pr_err("Failed to enable IOMMU for the device: %d\n", + ret); + return ret; + } + /* Set SYSMMU fault handler */ + iovmm_set_fault_handler(&lwis_dev->plat_dev->dev, + iovmm_fault_handler, lwis_dev); + } +#endif /* ENABLE_IOVMM */ + +#if ENABLE_QOS + /* Set hardcoded DVFS levels */ + if (!pm_qos_request_active(&platform->pm_qos_hpg)) + pm_qos_add_request(&platform->pm_qos_hpg, PM_QOS_CPU_ONLINE_MIN, + hpg_qos); + + ret = lwis_platform_update_qos(lwis_dev, mif_qos, CLOCK_FAMILY_MIF); + if (ret < 0) { + dev_err(lwis_dev->dev, "Failed to enable MIF clock\n"); + return ret; + } + ret = lwis_platform_update_qos(lwis_dev, int_qos, CLOCK_FAMILY_INT); + if (ret < 0) { + dev_err(lwis_dev->dev, "Failed to enable INT clock\n"); + return ret; + } + + if (lwis_dev->clock_family != CLOCK_FAMILY_INVALID && + lwis_dev->clock_family < NUM_CLOCK_FAMILY) { + ret = lwis_platform_update_qos(lwis_dev, core_clock_qos, + lwis_dev->clock_family); + if (ret < 0) { + dev_err(lwis_dev->dev, "Failed to enable core clock\n"); + return ret; + } + } +#endif /* ENABLE_QOS */ + + return 0; +} + +int lwis_platform_device_disable(struct lwis_device *lwis_dev) +{ + struct lwis_platform *platform; + + if (!lwis_dev) { + return -ENODEV; + } + + platform = lwis_dev->platform; + if (!platform) { + return -ENODEV; + } + + /* We can't remove fault handlers, so there's no call corresponding + * to the iovmm_set_fault_handler above */ + + lwis_platform_remove_qos(lwis_dev); + +#if ENABLE_IOVMM + if (lwis_dev->has_iommu) { + /* Deactivate IOMMU/SYSMMU */ + iovmm_deactivate(&lwis_dev->plat_dev->dev); + } +#endif /* ENABLE_IOVMM */ + + /* Disable platform device */ + return pm_runtime_put_sync(&lwis_dev->plat_dev->dev); +} + +int lwis_platform_update_qos(struct lwis_device *lwis_dev, uint32_t value, + enum lwis_clock_family clock_family) +{ +#if ENABLE_QOS + struct lwis_platform *platform; + struct pm_qos_request *qos_req; + int qos_class; + + if (!lwis_dev) { + return -ENODEV; + } + + platform = lwis_dev->platform; + if (!platform) { + return -ENODEV; + } + + if (value == 0) + value = PM_QOS_DEFAULT_VALUE; + + switch (clock_family) { + case CLOCK_FAMILY_INTCAM: + qos_req = &platform->pm_qos_int_cam; + qos_class = PM_QOS_INTCAM_THROUGHPUT; + break; + case CLOCK_FAMILY_CAM: + qos_req = &platform->pm_qos_cam; + qos_class = PM_QOS_CAM_THROUGHPUT; + break; + case CLOCK_FAMILY_TNR: +#if defined(CONFIG_SOC_GS101) + qos_req = &platform->pm_qos_tnr; + qos_class = PM_QOS_TNR_THROUGHPUT; +#endif + break; + case CLOCK_FAMILY_MIF: + qos_req = &platform->pm_qos_mem; + qos_class = PM_QOS_BUS_THROUGHPUT; + break; + case CLOCK_FAMILY_INT: + qos_req = &platform->pm_qos_int; + qos_class = PM_QOS_DEVICE_THROUGHPUT; + break; + default: + dev_err(lwis_dev->dev, "%s clk family %d is invalid\n", + lwis_dev->name, lwis_dev->clock_family); + return -EINVAL; + } + + if (!pm_qos_request_active(qos_req)) + pm_qos_add_request(qos_req, qos_class, value); + else + pm_qos_update_request(qos_req, value); + + dev_info(lwis_dev->dev, + "Updating clock for clock_family %d, freq to %u\n", + clock_family, value); +#endif /* ENABLE_QOS */ + + return 0; +} + +int lwis_platform_remove_qos(struct lwis_device *lwis_dev) +{ +#if ENABLE_QOS + struct lwis_platform *platform; + + if (!lwis_dev) { + return -ENODEV; + } + + 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); + } + break; + case CLOCK_FAMILY_CAM: + if (pm_qos_request_active(&platform->pm_qos_cam)) { + pm_qos_remove_request(&platform->pm_qos_cam); + } + 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); + } +#endif + break; + default: + break; + } +#endif /* ENABLE_QOS */ + return 0; +} diff --git a/platform/gs101/lwis_platform_gs101.h b/platform/gs101/lwis_platform_gs101.h new file mode 100644 index 0000000..7130124 --- /dev/null +++ b/platform/gs101/lwis_platform_gs101.h @@ -0,0 +1,27 @@ +/* + * Google LWIS GS101 Platform-Specific Functions + * + * Copyright (c) 2020 Google, LLC + * + * 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 LWIS_PLATFORM_GS101_H_ +#define LWIS_PLATFORM_GS101_H_ + +#include <linux/pm_qos.h> + +struct lwis_platform { + /* TODO(b/157514330): Refactor this to be dynamically controlled + * from userspace */ + struct pm_qos_request pm_qos_int_cam; + struct pm_qos_request pm_qos_int; + struct pm_qos_request pm_qos_cam; + struct pm_qos_request pm_qos_mem; + struct pm_qos_request pm_qos_hpg; + struct pm_qos_request pm_qos_tnr; +}; + +#endif /* LWIS_PLATFORM_GS101_H_ */
\ No newline at end of file diff --git a/platform/gs101/lwis_platform_gs101_dma.c b/platform/gs101/lwis_platform_gs101_dma.c new file mode 100644 index 0000000..fc1dd8c --- /dev/null +++ b/platform/gs101/lwis_platform_gs101_dma.c @@ -0,0 +1,79 @@ +/* + * Google LWIS GS101 Platform-Specific DMA Functions + * + * Copyright (c) 2020 Google, LLC + * + * 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. + */ + +#define ENABLE_IOVMM 0 + +#include <linux/slab.h> + +#if ENABLE_IOVMM +#include <linux/ion_exynos.h> +#include <linux/exynos_iovmm.h> +#endif + +#include "lwis_commands.h" +#include "lwis_init.h" +#include "lwis_platform.h" +#include "lwis_platform_dma.h" + +#define ION_SYSTEM_HEAP_NAME "ion_system_heap" +#define ION_SYSTEM_CONTIG_HEAP_NAME "ion_system_contig_heap" + +/* + * ION allocation flags, imported from "/drivers/staging/android/uapi/ion.h". + */ +#define ION_FLAG_CACHED 1 +#define ION_FLAG_NOZEROED 8 + +struct dma_buf *lwis_platform_dma_buffer_alloc(size_t len, unsigned int flags) +{ +#if ENABLE_IOVMM + unsigned int ion_flags = 0; + + /* + * "system_contig_heap" does not seemed to be used in the exynos driver, + * that's why system heap is used by default. + */ + const char *ion_heap_name = ION_SYSTEM_HEAP_NAME; + + if (flags & LWIS_DMA_BUFFER_CACHED) { + ion_flags |= ION_FLAG_CACHED; + } + if (flags & LWIS_DMA_BUFFER_UNINITIALIZED) { + ion_flags |= ION_FLAG_NOZEROED; + } + + return ion_alloc_dmabuf(ion_heap_name, len, ion_flags); +#else + return NULL; +#endif +} + +dma_addr_t lwis_platform_dma_buffer_map(struct lwis_device *lwis_dev, + struct dma_buf_attachment *attachment, + off_t offset, size_t size, + enum dma_data_direction direction, + int flags) +{ +#if ENABLE_IOVMM + return ion_iovmm_map(attachment, offset, size, direction, flags); +#else + return 0; +#endif +} + +int lwis_platform_dma_buffer_unmap(struct lwis_device *lwis_dev, + struct dma_buf_attachment *attachment, + dma_addr_t address) +{ +#if ENABLE_IOVMM + ion_iovmm_unmap(attachment, address); +#endif + return 0; +}
\ No newline at end of file |