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:
Hanumant Singh 2013-11-21 18:41:55 -08:00
parent faefe1ea4c
commit 1297c4a630
7 changed files with 256 additions and 0 deletions

View 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>;
..
..
};

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
View 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(&notify_lock, flags);
id = esoc_clink->id;
atomic_notifier_call_chain(&client_notify, evt, &id);
spin_unlock_irqrestore(&notify_lock, flags);
}
EXPORT_SYMBOL(notify_esoc_clients);

View 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