mirror of
https://github.com/team-infusion-developers/android_kernel_samsung_msm8976.git
synced 2024-11-07 04:09:21 +00:00
esoc: Add support for client notifications
Allow clients to register for external soc events. Change-Id: If96ec725ecddfb548d7e680b09bfd68be573c11c Signed-off-by: Hanumant Singh <hanumant@codeaurora.org>
This commit is contained in:
parent
faefe1ea4c
commit
1297c4a630
7 changed files with 256 additions and 0 deletions
58
Documentation/devicetree/bindings/esoc/esoc_client.txt
Normal file
58
Documentation/devicetree/bindings/esoc/esoc_client.txt
Normal file
|
@ -0,0 +1,58 @@
|
|||
== Introduction ==
|
||||
|
||||
In case of multiple external socs each exposing more than one interface to a
|
||||
primary SOC, it is possible for the different interface drivers/clients to
|
||||
expect notifications regarding the state of the external soc that they are
|
||||
interested in. In some cases clients may need notifications of the status of
|
||||
multiple external socs. The bindings referred to below enable a client device
|
||||
to register for notifications from multiple external socs.
|
||||
|
||||
== Esoc client devices ==
|
||||
|
||||
For each esoc client device, every external soc that it is associated with, is
|
||||
assigned an integer id. These ids start at 0 and are contiguous. Each id
|
||||
represents a unique external soc. Each external soc is additionally assigned a
|
||||
name. An additional property is used to spcify the names. A mapping is
|
||||
performed from the name to the id.
|
||||
|
||||
Required properties:
|
||||
esoc-0: A phandle pointing to the esoc node that the esoc client is
|
||||
interested in.
|
||||
|
||||
esoc-names: A list of string names for all the esocs that a given client
|
||||
is interested in. Must have atleast one name corresponding to
|
||||
esoc-0.
|
||||
|
||||
Optional properties:
|
||||
esoc-1: A phandle to the external soc, mapping to the second name in
|
||||
esoc-names.
|
||||
...
|
||||
esoc-n: A phandle to the nth external soc mapping to the nth name in
|
||||
the esoc-names
|
||||
|
||||
== Example ==
|
||||
|
||||
esoc_a {
|
||||
compatible = "esoc_mdm";
|
||||
....
|
||||
...
|
||||
..
|
||||
};
|
||||
|
||||
esoc_b {
|
||||
compatible = "esoc_wlan";
|
||||
..
|
||||
..
|
||||
};
|
||||
|
||||
pcie@0x0001 {
|
||||
compatible = "esoc-pcie-client"
|
||||
....
|
||||
..
|
||||
..
|
||||
esoc-names = "mdm", "wlan";
|
||||
esoc-0 = <&esoc_a>;
|
||||
esoc-1 = <&esoc_b>;
|
||||
..
|
||||
..
|
||||
};
|
|
@ -19,6 +19,15 @@ config ESOC_DEV
|
|||
for the external soc. It can receive event notifications from the
|
||||
control link.
|
||||
|
||||
config ESOC_CLIENT
|
||||
bool "ESOC client interface"
|
||||
depends on OF
|
||||
help
|
||||
Say yes here to enable client interface for external socs.
|
||||
Clients can specify the external soc that they are interested in
|
||||
by using device tree phandles. Based on this, clients can register
|
||||
for notifications from a specific soc.
|
||||
|
||||
config ESOC_DEBUG
|
||||
bool "ESOC debug support"
|
||||
help
|
||||
|
|
|
@ -3,5 +3,6 @@
|
|||
ccflags-$(CONFIG_ESOC_DEBUG) := -DDEBUG
|
||||
obj-$(CONFIG_ESOC) += esoc_bus.o
|
||||
obj-$(CONFIG_ESOC_DEV) += esoc_dev.o
|
||||
obj-$(CONFIG_ESOC_CLIENT) += esoc_client.o
|
||||
obj-$(CONFIG_ESOC_MDM_4x) += esoc-mdm-4x.o
|
||||
obj-$(CONFIG_ESOC_MDM_DRV) += esoc-mdm-drv.o
|
||||
|
|
|
@ -57,6 +57,7 @@ struct esoc_eng {
|
|||
* @compat_data: compat data of esoc driver.
|
||||
* @subsys_desc: descriptor for subsystem restart
|
||||
* @subsys_dev: ssr device handle.
|
||||
* @np: device tree node for esoc_clink.
|
||||
*/
|
||||
struct esoc_clink {
|
||||
const char *name;
|
||||
|
@ -73,6 +74,7 @@ struct esoc_clink {
|
|||
void *compat_data;
|
||||
struct subsys_desc subsys;
|
||||
struct subsys_device *subsys_dev;
|
||||
struct device_node *np;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -125,6 +127,7 @@ int esoc_dev_init(void);
|
|||
void esoc_clink_unregister(struct esoc_clink *esoc_dev);
|
||||
int esoc_clink_register(struct esoc_clink *esoc_dev);
|
||||
struct esoc_clink *get_esoc_clink(int id);
|
||||
struct esoc_clink *get_esoc_clink_by_node(struct device_node *node);
|
||||
void put_esoc_clink(struct esoc_clink *esoc_clink);
|
||||
void *get_esoc_clink_data(struct esoc_clink *esoc);
|
||||
void set_esoc_clink_data(struct esoc_clink *esoc, void *data);
|
||||
|
@ -147,4 +150,14 @@ void *esoc_get_drv_data(struct esoc_clink *esoc_clink);
|
|||
int esoc_clink_register_ssr(struct esoc_clink *esoc_clink);
|
||||
int esoc_clink_request_ssr(struct esoc_clink *esoc_clink);
|
||||
void esoc_clink_unregister_ssr(struct esoc_clink *esoc_clink);
|
||||
/* client notification */
|
||||
#ifdef CONFIG_ESOC_CLIENT
|
||||
void notify_esoc_clients(struct esoc_clink *esoc_clink, unsigned long evt);
|
||||
#else
|
||||
static inline void notify_esoc_clients(struct esoc_clink *esoc_clink,
|
||||
unsigned long evt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -101,6 +101,19 @@ static int esoc_clink_match_id(struct device *dev, void *id)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int esoc_clink_match_node(struct device *dev, void *id)
|
||||
{
|
||||
struct esoc_clink *esoc_clink = to_esoc_clink(dev);
|
||||
struct device_node *node = id;
|
||||
|
||||
if (esoc_clink->np == node) {
|
||||
if (!try_module_get(esoc_clink->owner))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void esoc_for_each_dev(void *data, int (*fn)(struct device *dev, void *))
|
||||
{
|
||||
int ret;
|
||||
|
@ -123,6 +136,19 @@ struct esoc_clink *get_esoc_clink(int id)
|
|||
}
|
||||
EXPORT_SYMBOL(get_esoc_clink);
|
||||
|
||||
struct esoc_clink *get_esoc_clink_by_node(struct device_node *node)
|
||||
{
|
||||
struct esoc_clink *esoc_clink;
|
||||
struct device *dev;
|
||||
|
||||
dev = bus_find_device(&esoc_bus_type, NULL, node,
|
||||
esoc_clink_match_node);
|
||||
if (IS_ERR(dev))
|
||||
return NULL;
|
||||
esoc_clink = to_esoc_clink(dev);
|
||||
return esoc_clink;
|
||||
}
|
||||
|
||||
void put_esoc_clink(struct esoc_clink *esoc_clink)
|
||||
{
|
||||
module_put(esoc_clink->owner);
|
||||
|
@ -176,6 +202,7 @@ void esoc_clink_evt_notify(enum esoc_evt evt, struct esoc_clink *esoc_clink)
|
|||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&esoc_clink->notify_lock, flags);
|
||||
notify_esoc_clients(esoc_clink, evt);
|
||||
if (esoc_clink->req_eng && esoc_clink->req_eng->handle_clink_evt)
|
||||
esoc_clink->req_eng->handle_clink_evt(evt, esoc_clink->req_eng);
|
||||
if (esoc_clink->cmd_eng && esoc_clink->cmd_eng->handle_clink_evt)
|
||||
|
|
115
drivers/esoc/esoc_client.c
Normal file
115
drivers/esoc/esoc_client.c
Normal file
|
@ -0,0 +1,115 @@
|
|||
/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#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);
|
||||
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;
|
||||
|
||||
for (index = 0;; index++) {
|
||||
esoc_prop = kasprintf(GFP_KERNEL, "esoc-%d", index);
|
||||
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 (!esoc_clink) {
|
||||
dev_err(dev, "matching esoc clink not present\n");
|
||||
return NULL;
|
||||
}
|
||||
desc = devres_alloc(devm_esoc_desc_release,
|
||||
sizeof(*desc), GFP_KERNEL);
|
||||
if (!desc)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
esoc_name = kasprintf(GFP_KERNEL, "subsys_esoc%d",
|
||||
esoc_clink->id);
|
||||
desc->name = esoc_name;
|
||||
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);
|
33
include/linux/esoc_client.h
Normal file
33
include/linux/esoc_client.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#ifndef __ESOC_CLIENT_H_
|
||||
#define __ESOC_CLIENT_H_
|
||||
|
||||
#include <linux/esoc_ctrl.h>
|
||||
#include <linux/notifier.h>
|
||||
|
||||
/*
|
||||
* struct esoc_desc: Describes an external soc
|
||||
* @name: external soc name
|
||||
* @priv: private data for external soc
|
||||
*/
|
||||
struct esoc_desc {
|
||||
const char *name;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
struct esoc_desc *devm_register_esoc_client(struct device *dev,
|
||||
const char *name);
|
||||
void devm_unregister_esoc_client(struct device *dev,
|
||||
struct esoc_desc *esoc_desc);
|
||||
int esoc_register_client_notifier(struct notifier_block *nb);
|
||||
#endif
|
Loading…
Reference in a new issue