mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-23 01:31:30 +03:00
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 <ellisr@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2225415 Reviewed-by: automaticguardword <automaticguardword@nvidia.com> Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com> Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com> GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
This commit is contained in:
@@ -38,6 +38,19 @@ struct clock_entry {
|
|||||||
struct list_head list;
|
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)
|
static struct device_node *find_clocks_node(const char *name)
|
||||||
{
|
{
|
||||||
const char *node_name = "mods-simple-bus";
|
const char *node_name = "mods-simple-bus";
|
||||||
@@ -92,9 +105,11 @@ err:
|
|||||||
|
|
||||||
void mods_shutdown_clock_api(void)
|
void mods_shutdown_clock_api(void)
|
||||||
{
|
{
|
||||||
struct list_head *head = &mods_clock_handles;
|
struct list_head *head = &mods_clock_handles;
|
||||||
struct list_head *iter;
|
struct list_head *reset_head = &reset_handles;
|
||||||
struct list_head *tmp;
|
struct reset_entry *entry = NULL;
|
||||||
|
struct list_head *iter = NULL;
|
||||||
|
struct list_head *tmp = NULL;
|
||||||
|
|
||||||
spin_lock(&mods_clock_lock);
|
spin_lock(&mods_clock_lock);
|
||||||
|
|
||||||
@@ -105,6 +120,12 @@ void mods_shutdown_clock_api(void)
|
|||||||
kfree(entry);
|
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);
|
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)
|
static struct clk *mods_get_clock(u32 handle)
|
||||||
{
|
{
|
||||||
struct list_head *head = &mods_clock_handles;
|
struct list_head *head = &mods_clock_handles;
|
||||||
struct list_head *iter;
|
struct list_head *iter = NULL;
|
||||||
struct clk *pclk = 0;
|
struct clk *pclk = 0;
|
||||||
|
|
||||||
spin_lock(&mods_clock_lock);
|
spin_lock(&mods_clock_lock);
|
||||||
@@ -164,6 +185,60 @@ static struct clk *mods_get_clock(u32 handle)
|
|||||||
return pclk;
|
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,
|
int esc_mods_get_clock_handle(struct mods_client *client,
|
||||||
struct MODS_GET_CLOCK_HANDLE *p)
|
struct MODS_GET_CLOCK_HANDLE *p)
|
||||||
{
|
{
|
||||||
@@ -202,6 +277,58 @@ err:
|
|||||||
return ret;
|
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,
|
int esc_mods_set_clock_rate(struct mods_client *client,
|
||||||
struct MODS_CLOCK_RATE *p)
|
struct MODS_CLOCK_RATE *p)
|
||||||
{
|
{
|
||||||
@@ -458,6 +585,45 @@ int esc_mods_is_clock_enabled(struct mods_client *client,
|
|||||||
return ret;
|
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,
|
int esc_mods_clock_reset_assert(struct mods_client *client,
|
||||||
struct MODS_CLOCK_HANDLE *p)
|
struct MODS_CLOCK_HANDLE *p)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -600,14 +600,18 @@ int esc_mods_clock_reset_assert(struct mods_client *client,
|
|||||||
struct MODS_CLOCK_HANDLE *p);
|
struct MODS_CLOCK_HANDLE *p);
|
||||||
int esc_mods_clock_reset_deassert(struct mods_client *client,
|
int esc_mods_clock_reset_deassert(struct mods_client *client,
|
||||||
struct MODS_CLOCK_HANDLE *p);
|
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,
|
int esc_mods_flush_cpu_cache_range(struct mods_client *client,
|
||||||
struct MODS_FLUSH_CPU_CACHE_RANGE *p);
|
struct MODS_FLUSH_CPU_CACHE_RANGE *p);
|
||||||
int esc_mods_dma_alloc_coherent(struct mods_client *client,
|
int esc_mods_dma_alloc_coherent(struct mods_client *client,
|
||||||
struct MODS_DMA_COHERENT_MEM_HANDLE *p);
|
struct MODS_DMA_COHERENT_MEM_HANDLE *p);
|
||||||
int esc_mods_dma_free_coherent(struct mods_client *client,
|
int esc_mods_dma_free_coherent(struct mods_client *client,
|
||||||
struct MODS_DMA_COHERENT_MEM_HANDLE *p);
|
struct MODS_DMA_COHERENT_MEM_HANDLE *p);
|
||||||
int esc_mods_dma_copy_to_user(struct mods_client *client,
|
int esc_mods_dma_copy_to_user(struct mods_client *client,
|
||||||
struct MODS_DMA_COPY_TO_USER *p);
|
struct MODS_DMA_COPY_TO_USER *p);
|
||||||
|
|
||||||
#ifdef CONFIG_DMA_ENGINE
|
#ifdef CONFIG_DMA_ENGINE
|
||||||
int esc_mods_dma_request_channel(struct mods_client *client,
|
int esc_mods_dma_request_channel(struct mods_client *client,
|
||||||
|
|||||||
@@ -2179,6 +2179,18 @@ static long mods_krnl_ioctl(struct file *fp,
|
|||||||
esc_mods_clock_reset_deassert,
|
esc_mods_clock_reset_deassert,
|
||||||
MODS_CLOCK_HANDLE);
|
MODS_CLOCK_HANDLE);
|
||||||
break;
|
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
|
#endif
|
||||||
#if defined(CONFIG_ARCH_TEGRA)
|
#if defined(CONFIG_ARCH_TEGRA)
|
||||||
case MODS_ESC_FLUSH_CPU_CACHE_RANGE:
|
case MODS_ESC_FLUSH_CPU_CACHE_RANGE:
|
||||||
|
|||||||
@@ -1235,6 +1235,26 @@ struct MODS_DEVICE_NUMA_INFO {
|
|||||||
__u32 cpu_count;
|
__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
|
/* Used by legacy MODS_ESC_GET_SCREEN_INFO ioctl and as a member of
|
||||||
* MODS_SCREEN_INFO_2.
|
* 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_MAP_MEMORY MODSIO(W, 129, MODS_IOMMU_DMA_MAP_MEMORY)
|
||||||
#define MODS_ESC_IOMMU_DMA_UNMAP_MEMORY MODSIO(W, 130, \
|
#define MODS_ESC_IOMMU_DMA_UNMAP_MEMORY MODSIO(W, 130, \
|
||||||
MODS_IOMMU_DMA_MAP_MEMORY)
|
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_ */
|
#endif /* _UAPI_MODS_H_ */
|
||||||
|
|||||||
Reference in New Issue
Block a user