From ad434b8d0e873c2e2f84777e7577fba7578e953e Mon Sep 17 00:00:00 2001 From: ellisr Date: Tue, 23 Apr 2019 10:34:43 -0700 Subject: [PATCH] misc: mods: Update reset framework in mods driver Summary: Previously mods stored handles for both clocks and resets in the same list. This meant reset signals that didn't have a corresponding clock signal could not be toggled. This change adds a separate list of structures to handle resets and decouple it from the clock handling. Change-Id: I2a83dd5cb7b7fe412acf7ae1815b18b747ad2cac Signed-off-by: Ellis Roberts Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2225415 Reviewed-by: automaticguardword Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Bharat Nihalani Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- drivers/misc/mods/mods_clock.c | 174 +++++++++++++++++++++++++++++- drivers/misc/mods/mods_internal.h | 12 ++- drivers/misc/mods/mods_krnl.c | 12 +++ include/uapi/misc/mods.h | 22 ++++ 4 files changed, 212 insertions(+), 8 deletions(-) diff --git a/drivers/misc/mods/mods_clock.c b/drivers/misc/mods/mods_clock.c index 698ea5cf..0ab43755 100644 --- a/drivers/misc/mods/mods_clock.c +++ b/drivers/misc/mods/mods_clock.c @@ -38,6 +38,19 @@ struct clock_entry { struct list_head list; }; +static LIST_HEAD(reset_handles); + +struct reset_data { + char name[MAX_DT_SIZE]; + struct reset_control *rst; +}; + +struct reset_entry { + struct list_head list; + struct reset_data rst_data; + u32 handle; +}; + static struct device_node *find_clocks_node(const char *name) { const char *node_name = "mods-simple-bus"; @@ -92,9 +105,11 @@ err: void mods_shutdown_clock_api(void) { - struct list_head *head = &mods_clock_handles; - struct list_head *iter; - struct list_head *tmp; + struct list_head *head = &mods_clock_handles; + struct list_head *reset_head = &reset_handles; + struct reset_entry *entry = NULL; + struct list_head *iter = NULL; + struct list_head *tmp = NULL; spin_lock(&mods_clock_lock); @@ -105,6 +120,12 @@ void mods_shutdown_clock_api(void) kfree(entry); } + list_for_each_safe(iter, tmp, reset_head) { + entry = list_entry(iter, struct reset_entry, list); + list_del(iter); + kfree(entry); + } + spin_unlock(&mods_clock_lock); } @@ -145,7 +166,7 @@ static u32 mods_get_clock_handle(struct clk *pclk) static struct clk *mods_get_clock(u32 handle) { struct list_head *head = &mods_clock_handles; - struct list_head *iter; + struct list_head *iter = NULL; struct clk *pclk = 0; spin_lock(&mods_clock_lock); @@ -164,6 +185,60 @@ static struct clk *mods_get_clock(u32 handle) return pclk; } +static struct reset_data find_reset_data(u32 handle) +{ + struct list_head *entry = NULL; + struct reset_entry *rst_entry = NULL; + struct reset_data reset_data = {"", NULL}; + + spin_lock(&mods_clock_lock); + + list_for_each(entry, &reset_handles) { + rst_entry = list_entry(entry, struct reset_entry, list); + if (handle == rst_entry->handle) { + reset_data = rst_entry->rst_data; + break; + } + } + + spin_unlock(&mods_clock_lock); + + return reset_data; +} + +static int get_reset_handle(struct reset_data reset_data) +{ + int handle = -1; + struct list_head *entry = NULL; + struct reset_entry *rst_entry = NULL; + + spin_lock(&mods_clock_lock); + + /* If entry has no rst structure, we are past last cached entry */ + list_for_each(entry, &reset_handles) { + rst_entry = list_entry(entry, struct reset_entry, list); + handle = rst_entry->handle; + if (strcmp(rst_entry->rst_data.name, + reset_data.name) == 0) { + return handle; + } + } + + /* If reset not already in array, then we must add it */ + rst_entry = kzalloc(sizeof(struct reset_entry), GFP_ATOMIC); + if (unlikely(!rst_entry)) { + spin_unlock(&mods_clock_lock); + return -1; + } + rst_entry->handle = ++handle; + rst_entry->rst_data = reset_data; + INIT_LIST_HEAD(&rst_entry->list); + list_add_tail(&rst_entry->list, &reset_handles); + spin_unlock(&mods_clock_lock); + + return handle; +} + int esc_mods_get_clock_handle(struct mods_client *client, struct MODS_GET_CLOCK_HANDLE *p) { @@ -202,6 +277,58 @@ err: return ret; } +int esc_mods_get_rst_handle(struct mods_client *client, + struct MODS_GET_RESET_HANDLE *p) +{ + struct reset_data reset_data = {{0}, NULL}; + struct reset_control *p_reset_ctrl = NULL; + int ret = -EINVAL; + + struct device_node *mods_np = NULL; + struct property *pp = NULL; + + LOG_ENT(); + + mods_np = find_clocks_node("mods-clocks"); + if (!mods_np || !of_device_is_available(mods_np)) { + cl_error("'mods-clocks' node not found in device tree\n"); + goto err; + } + pp = of_find_property(mods_np, "reset-names", NULL); + if (IS_ERR(pp)) { + cl_error( + "No 'reset-names' prop in 'mods-clocks' node for dev %s\n", + p->reset_name); + goto err; + } + + p_reset_ctrl = of_reset_control_get(mods_np, p->reset_name); + + if (IS_ERR(p_reset_ctrl)) + cl_error("reset (%s) not found\n", p->reset_name); + else { + strncpy(reset_data.name, p->reset_name, + sizeof(reset_data.name) - 1); + if (reset_data.name[sizeof(reset_data.name) - 1] != '\0') { + cl_error( + "reset name %sis too large to store in reset array\n", + reset_data.name); + goto err; + } + reset_data.rst = p_reset_ctrl; + p->reset_handle = get_reset_handle(reset_data); + if (p->reset_handle == -1) { + cl_error("no valid reset handle was acquired"); + goto err; + } + ret = OK; + } +err: + of_node_put(mods_np); + LOG_EXT(); + return ret; +} + int esc_mods_set_clock_rate(struct mods_client *client, struct MODS_CLOCK_RATE *p) { @@ -458,6 +585,45 @@ int esc_mods_is_clock_enabled(struct mods_client *client, return ret; } +int esc_mods_reset_assert(struct mods_client *client, + struct MODS_RESET_HANDLE *p) +{ + int err = -EINVAL; + const struct reset_data reset_data = find_reset_data(p->handle); + struct device_node *mods_np = NULL; + + LOG_ENT(); + mods_np = find_clocks_node("mods-clocks"); + if (!mods_np || !of_device_is_available(mods_np)) { + cl_error("'mods-clocks' node not found in DTB\n"); + goto error; + } + + if (!reset_data.rst) { + cl_error("No reset corresponding to requested handle!\n"); + goto error; + } + + if (p->assert) + err = reset_control_assert(reset_data.rst); + else + err = reset_control_deassert(reset_data.rst); + if (err) { + cl_error("failed to %s reset on '%s'\n", + (p->assert ? "asserted" : "deasserted"), + reset_data.name); + } else { + cl_debug(DEBUG_CLOCK, "%s reset on '%s'", + (p->assert ? "asserted" : "desasserted"), + reset_data.name); + } +error: + of_node_put(mods_np); + + LOG_EXT(); + return err; +} + int esc_mods_clock_reset_assert(struct mods_client *client, struct MODS_CLOCK_HANDLE *p) { diff --git a/drivers/misc/mods/mods_internal.h b/drivers/misc/mods/mods_internal.h index d6c00ed6..a6f726fc 100644 --- a/drivers/misc/mods/mods_internal.h +++ b/drivers/misc/mods/mods_internal.h @@ -600,14 +600,18 @@ int esc_mods_clock_reset_assert(struct mods_client *client, struct MODS_CLOCK_HANDLE *p); int esc_mods_clock_reset_deassert(struct mods_client *client, struct MODS_CLOCK_HANDLE *p); +int esc_mods_reset_assert(struct mods_client *client, + struct MODS_RESET_HANDLE *p); +int esc_mods_get_rst_handle(struct mods_client *client, + struct MODS_GET_RESET_HANDLE *p); int esc_mods_flush_cpu_cache_range(struct mods_client *client, struct MODS_FLUSH_CPU_CACHE_RANGE *p); int esc_mods_dma_alloc_coherent(struct mods_client *client, struct MODS_DMA_COHERENT_MEM_HANDLE *p); -int esc_mods_dma_free_coherent(struct mods_client *client, - struct MODS_DMA_COHERENT_MEM_HANDLE *p); -int esc_mods_dma_copy_to_user(struct mods_client *client, - struct MODS_DMA_COPY_TO_USER *p); +int esc_mods_dma_free_coherent(struct mods_client *client, + struct MODS_DMA_COHERENT_MEM_HANDLE *p); +int esc_mods_dma_copy_to_user(struct mods_client *client, + struct MODS_DMA_COPY_TO_USER *p); #ifdef CONFIG_DMA_ENGINE int esc_mods_dma_request_channel(struct mods_client *client, diff --git a/drivers/misc/mods/mods_krnl.c b/drivers/misc/mods/mods_krnl.c index e1cd6de9..2de22cb7 100644 --- a/drivers/misc/mods/mods_krnl.c +++ b/drivers/misc/mods/mods_krnl.c @@ -2179,6 +2179,18 @@ static long mods_krnl_ioctl(struct file *fp, esc_mods_clock_reset_deassert, MODS_CLOCK_HANDLE); break; + + case MODS_ESC_RESET_ASSERT: + MODS_IOCTL_NORETVAL(MODS_ESC_RESET_ASSERT, + esc_mods_reset_assert, + MODS_RESET_HANDLE); + break; + + case MODS_ESC_GET_RESET_HANDLE: + MODS_IOCTL(MODS_ESC_GET_RESET_HANDLE, + esc_mods_get_rst_handle, + MODS_GET_RESET_HANDLE); + break; #endif #if defined(CONFIG_ARCH_TEGRA) case MODS_ESC_FLUSH_CPU_CACHE_RANGE: diff --git a/include/uapi/misc/mods.h b/include/uapi/misc/mods.h index 9f672d73..e6e6065f 100644 --- a/include/uapi/misc/mods.h +++ b/include/uapi/misc/mods.h @@ -1235,6 +1235,26 @@ struct MODS_DEVICE_NUMA_INFO { __u32 cpu_count; }; +/* Used by MODS_ESC_GET_RESET_HANDLE ioctl. + * Used to get an index in reset array corresponding to a reset name + * so that a client can reference it easily in future calls to toggle + * reset + */ +struct MODS_GET_RESET_HANDLE { + /* OUT */ + __u32 reset_handle; + + /* IN */ + char reset_name[MAX_DT_SIZE]; +}; + +/* Used by MODS_ESC_RESET_ASSERT */ +struct MODS_RESET_HANDLE { + /* IN */ + __u32 handle; + __u8 assert; +}; + /* Used by legacy MODS_ESC_GET_SCREEN_INFO ioctl and as a member of * MODS_SCREEN_INFO_2. */ @@ -1901,5 +1921,7 @@ struct MODS_IOMMU_DMA_MAP_MEMORY { #define MODS_ESC_IOMMU_DMA_MAP_MEMORY MODSIO(W, 129, MODS_IOMMU_DMA_MAP_MEMORY) #define MODS_ESC_IOMMU_DMA_UNMAP_MEMORY MODSIO(W, 130, \ MODS_IOMMU_DMA_MAP_MEMORY) +#define MODS_ESC_RESET_ASSERT MODSIO(W, 131, MODS_RESET_HANDLE) +#define MODS_ESC_GET_RESET_HANDLE MODSIO(WR, 132, MODS_GET_RESET_HANDLE) #endif /* _UAPI_MODS_H_ */