e9a05bb4d3
Modify spdx tag for all CAF authored files for Kona. Change-Id: I9308c7189412b1e428a7f67ded0dc076b0e38254 Signed-off-by: Rishabh Bhatnagar <rishabhb@codeaurora.org>
201 lines
5.1 KiB
C
201 lines
5.1 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/esoc_client.h>
|
|
#include <linux/of.h>
|
|
#include <linux/spinlock.h>
|
|
#include "esoc.h"
|
|
|
|
static DEFINE_SPINLOCK(notify_lock);
|
|
static ATOMIC_NOTIFIER_HEAD(client_notify);
|
|
|
|
static void devm_esoc_desc_release(struct device *dev, void *res)
|
|
{
|
|
struct esoc_desc *esoc_desc = res;
|
|
|
|
kfree(esoc_desc->name);
|
|
kfree(esoc_desc->link);
|
|
put_esoc_clink(esoc_desc->priv);
|
|
}
|
|
|
|
static int devm_esoc_desc_match(struct device *dev, void *res, void *data)
|
|
{
|
|
struct esoc_desc *esoc_desc = res;
|
|
return esoc_desc == data;
|
|
}
|
|
|
|
struct esoc_desc *devm_register_esoc_client(struct device *dev,
|
|
const char *name)
|
|
{
|
|
int ret, index;
|
|
const char *client_desc;
|
|
char *esoc_prop;
|
|
const __be32 *parp;
|
|
struct device_node *esoc_node;
|
|
struct device_node *np = dev->of_node;
|
|
struct esoc_clink *esoc_clink;
|
|
struct esoc_desc *desc;
|
|
char *esoc_name, *esoc_link, *esoc_link_info;
|
|
|
|
for (index = 0;; index++) {
|
|
esoc_prop = kasprintf(GFP_KERNEL, "esoc-%d", index);
|
|
if (IS_ERR_OR_NULL(esoc_prop))
|
|
return ERR_PTR(-ENOMEM);
|
|
parp = of_get_property(np, esoc_prop, NULL);
|
|
if (parp == NULL) {
|
|
dev_err(dev, "esoc device not present\n");
|
|
kfree(esoc_prop);
|
|
return NULL;
|
|
}
|
|
ret = of_property_read_string_index(np, "esoc-names", index,
|
|
&client_desc);
|
|
if (ret) {
|
|
dev_err(dev, "cannot find matching string\n");
|
|
kfree(esoc_prop);
|
|
return NULL;
|
|
}
|
|
if (strcmp(client_desc, name)) {
|
|
kfree(esoc_prop);
|
|
continue;
|
|
}
|
|
kfree(esoc_prop);
|
|
esoc_node = of_find_node_by_phandle(be32_to_cpup(parp));
|
|
esoc_clink = get_esoc_clink_by_node(esoc_node);
|
|
if (IS_ERR_OR_NULL(esoc_clink)) {
|
|
dev_err(dev, "matching esoc clink not present\n");
|
|
return ERR_PTR(-EPROBE_DEFER);
|
|
}
|
|
esoc_name = kasprintf(GFP_KERNEL, "esoc%d",
|
|
esoc_clink->id);
|
|
if (IS_ERR_OR_NULL(esoc_name)) {
|
|
dev_err(dev, "unable to allocate esoc name\n");
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
esoc_link = kasprintf(GFP_KERNEL, "%s", esoc_clink->link_name);
|
|
if (IS_ERR_OR_NULL(esoc_link)) {
|
|
dev_err(dev, "unable to allocate esoc link name\n");
|
|
kfree(esoc_name);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
esoc_link_info = kasprintf(GFP_KERNEL, "%s",
|
|
esoc_clink->link_info);
|
|
if (IS_ERR_OR_NULL(esoc_link_info)) {
|
|
dev_err(dev, "unable to alloc link info name\n");
|
|
kfree(esoc_name);
|
|
kfree(esoc_link);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
desc = devres_alloc(devm_esoc_desc_release,
|
|
sizeof(*desc), GFP_KERNEL);
|
|
if (IS_ERR_OR_NULL(desc)) {
|
|
kfree(esoc_name);
|
|
kfree(esoc_link);
|
|
kfree(esoc_link_info);
|
|
dev_err(dev, "unable to allocate esoc descriptor\n");
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
desc->name = esoc_name;
|
|
desc->link = esoc_link;
|
|
desc->link_info = esoc_link_info;
|
|
desc->priv = esoc_clink;
|
|
devres_add(dev, desc);
|
|
return desc;
|
|
}
|
|
return NULL;
|
|
}
|
|
EXPORT_SYMBOL(devm_register_esoc_client);
|
|
|
|
void devm_unregister_esoc_client(struct device *dev,
|
|
struct esoc_desc *esoc_desc)
|
|
{
|
|
int ret;
|
|
|
|
ret = devres_release(dev, devm_esoc_desc_release,
|
|
devm_esoc_desc_match, esoc_desc);
|
|
WARN_ON(ret);
|
|
}
|
|
EXPORT_SYMBOL(devm_unregister_esoc_client);
|
|
|
|
int esoc_register_client_notifier(struct notifier_block *nb)
|
|
{
|
|
return atomic_notifier_chain_register(&client_notify, nb);
|
|
}
|
|
EXPORT_SYMBOL(esoc_register_client_notifier);
|
|
|
|
void notify_esoc_clients(struct esoc_clink *esoc_clink, unsigned long evt)
|
|
{
|
|
unsigned int id;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(¬ify_lock, flags);
|
|
id = esoc_clink->id;
|
|
atomic_notifier_call_chain(&client_notify, evt, &id);
|
|
spin_unlock_irqrestore(¬ify_lock, flags);
|
|
}
|
|
EXPORT_SYMBOL(notify_esoc_clients);
|
|
|
|
int esoc_register_client_hook(struct esoc_desc *desc,
|
|
struct esoc_client_hook *client_hook)
|
|
{
|
|
int i;
|
|
struct esoc_clink *esoc_clink;
|
|
|
|
if (IS_ERR_OR_NULL(desc) || IS_ERR_OR_NULL(client_hook)) {
|
|
pr_debug("%s: Invalid parameters\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
esoc_clink = desc->priv;
|
|
if (IS_ERR_OR_NULL(esoc_clink)) {
|
|
pr_debug("%s: Invalid esoc link\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0; i < ESOC_MAX_HOOKS; i++) {
|
|
if (i == client_hook->prio &&
|
|
esoc_clink->client_hook[i] == NULL) {
|
|
esoc_clink->client_hook[i] = client_hook;
|
|
dev_dbg(&esoc_clink->dev,
|
|
"Client hook registration successful\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
dev_dbg(&esoc_clink->dev, "Client hook registration failed!\n");
|
|
return -EINVAL;
|
|
}
|
|
EXPORT_SYMBOL(esoc_register_client_hook);
|
|
|
|
int esoc_unregister_client_hook(struct esoc_desc *desc,
|
|
struct esoc_client_hook *client_hook)
|
|
{
|
|
int i;
|
|
struct esoc_clink *esoc_clink;
|
|
|
|
if (IS_ERR_OR_NULL(desc) || IS_ERR_OR_NULL(client_hook)) {
|
|
pr_debug("%s: Invalid parameters\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
esoc_clink = desc->priv;
|
|
if (IS_ERR_OR_NULL(esoc_clink)) {
|
|
pr_debug("%s: Invalid esoc link\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0; i < ESOC_MAX_HOOKS; i++) {
|
|
if (i == client_hook->prio &&
|
|
esoc_clink->client_hook[i] != NULL) {
|
|
esoc_clink->client_hook[i] = NULL;
|
|
dev_dbg(&esoc_clink->dev,
|
|
"Client hook unregistration successful\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
dev_dbg(&esoc_clink->dev, "Client hook unregistration failed!\n");
|
|
return -EINVAL;
|
|
}
|
|
EXPORT_SYMBOL(esoc_unregister_client_hook);
|