blob: 7c2b4f6cc4b22251a2cd3c84f01ae68155e0e4b0 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/atomic.h>
#include <linux/slab.h>
#include <linux/export.h>
#include <linux/kthread.h>
#include <linux/semaphore.h>
#include <linux/amlogic/media/clk/gp_pll.h>
static DEFINE_MUTEX(gp_mutex);
static struct list_head gp_pll_user_list = LIST_HEAD_INIT(gp_pll_user_list);
static DEFINE_SEMAPHORE(sem);
static void gp_pll_user_consolidate(void)
{
struct gp_pll_user_handle_s *pos, *grant, *request;
mutex_lock(&gp_mutex);
grant = NULL;
request = NULL;
list_for_each_entry(pos, &gp_pll_user_list, list) {
if (!request &&
(pos->flag & GP_PLL_USER_FLAG_REQUEST) &&
((pos->flag & GP_PLL_USER_FLAG_GRANTED) == 0)) {
request = pos;
}
if (!grant &&
(pos->flag & GP_PLL_USER_FLAG_GRANTED)) {
grant = pos;
}
}
if (request) {
if (!grant) {
request->flag |= GP_PLL_USER_FLAG_GRANTED;
if (request->callback)
request->callback(request,
GP_PLL_USER_EVENT_GRANT);
} else if (grant->priority > request->priority) {
if (grant->callback)
grant->callback(grant,
GP_PLL_USER_EVENT_YIELD);
}
}
mutex_unlock(&gp_mutex);
}
struct gp_pll_user_handle_s *gp_pll_user_register
(const char *name,
u32 priority,
int (*callback)(struct gp_pll_user_handle_s *, int))
{
struct gp_pll_user_handle_s *user, *pos;
user = (struct gp_pll_user_handle_s *)
kmalloc(sizeof(struct gp_pll_user_handle_s), GFP_KERNEL);
if (!user)
return NULL;
user->name = name;
user->priority = priority;
user->callback = callback;
user->flag = 0;
mutex_lock(&gp_mutex);
list_for_each_entry(pos, &gp_pll_user_list, list) {
if (pos->priority > priority)
break;
}
list_add_tail(&user->list, &pos->list);
mutex_unlock(&gp_mutex);
return user;
}
EXPORT_SYMBOL(gp_pll_user_register);
void gp_pll_user_unregister(struct gp_pll_user_handle_s *user)
{
mutex_lock(&gp_mutex);
list_del(&user->list);
mutex_unlock(&gp_mutex);
if (user->flag & GP_PLL_USER_FLAG_GRANTED)
up(&sem);
kfree(user);
}
EXPORT_SYMBOL(gp_pll_user_unregister);
void gp_pll_request(struct gp_pll_user_handle_s *user)
{
if (user->flag & GP_PLL_USER_FLAG_GRANTED)
return;
user->flag = GP_PLL_USER_FLAG_REQUEST;
up(&sem);
}
EXPORT_SYMBOL(gp_pll_request);
void gp_pll_release(struct gp_pll_user_handle_s *user)
{
if (user->flag & GP_PLL_USER_FLAG_GRANTED) {
user->flag = 0;
up(&sem);
}
user->flag = 0;
}
EXPORT_SYMBOL(gp_pll_release);
static int gp_pll_thread(void *data)
{
while (down_interruptible(&sem) == 0)
gp_pll_user_consolidate();
return 0;
}
int __init gp_pll_init(void)
{
kthread_run(gp_pll_thread, NULL, "gp_pll");
return 0;
}