2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-12-18 18:23:53 +08:00

soc: qcom: smd: Represent smd edges as devices

By representing each edge as its own device the channels are no longer
tied to being parented by the same smd device and as such an edge can
live as children of e.g. remoteproc instances.

Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Signed-off-by: Andy Gross <andy.gross@linaro.org>
This commit is contained in:
Bjorn Andersson 2016-08-15 11:15:57 -07:00 committed by Andy Gross
parent 381a0b4ce4
commit da0573026c
2 changed files with 141 additions and 76 deletions

View File

@ -95,7 +95,7 @@ static const struct {
/** /**
* struct qcom_smd_edge - representing a remote processor * struct qcom_smd_edge - representing a remote processor
* @smd: handle to qcom_smd * @dev: device for this edge
* @of_node: of_node handle for information related to this edge * @of_node: of_node handle for information related to this edge
* @edge_id: identifier of this edge * @edge_id: identifier of this edge
* @remote_pid: identifier of remote processor * @remote_pid: identifier of remote processor
@ -111,7 +111,8 @@ static const struct {
* @state_work: work item for edge state changes * @state_work: work item for edge state changes
*/ */
struct qcom_smd_edge { struct qcom_smd_edge {
struct qcom_smd *smd; struct device dev;
struct device_node *of_node; struct device_node *of_node;
unsigned edge_id; unsigned edge_id;
unsigned remote_pid; unsigned remote_pid;
@ -135,6 +136,8 @@ struct qcom_smd_edge {
struct work_struct state_work; struct work_struct state_work;
}; };
#define to_smd_edge(d) container_of(d, struct qcom_smd_edge, dev)
/* /*
* SMD channel states. * SMD channel states.
*/ */
@ -199,19 +202,6 @@ struct qcom_smd_channel {
struct list_head list; struct list_head list;
}; };
/**
* struct qcom_smd - smd struct
* @dev: device struct
* @num_edges: number of entries in @edges
* @edges: array of edges to be handled
*/
struct qcom_smd {
struct device *dev;
unsigned num_edges;
struct qcom_smd_edge edges[0];
};
/* /*
* Format of the smd_info smem items, for byte aligned channels. * Format of the smd_info smem items, for byte aligned channels.
*/ */
@ -420,7 +410,7 @@ static void qcom_smd_channel_set_state(struct qcom_smd_channel *channel,
if (channel->state == state) if (channel->state == state)
return; return;
dev_dbg(edge->smd->dev, "set_state(%s, %d)\n", channel->name, state); dev_dbg(&edge->dev, "set_state(%s, %d)\n", channel->name, state);
SET_TX_CHANNEL_FLAG(channel, fDSR, is_open); SET_TX_CHANNEL_FLAG(channel, fDSR, is_open);
SET_TX_CHANNEL_FLAG(channel, fCTS, is_open); SET_TX_CHANNEL_FLAG(channel, fCTS, is_open);
@ -964,13 +954,12 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel)
struct qcom_smd_device *qsdev; struct qcom_smd_device *qsdev;
struct qcom_smd_edge *edge = channel->edge; struct qcom_smd_edge *edge = channel->edge;
struct device_node *node; struct device_node *node;
struct qcom_smd *smd = edge->smd;
int ret; int ret;
if (channel->qsdev) if (channel->qsdev)
return -EEXIST; return -EEXIST;
dev_dbg(smd->dev, "registering '%s'\n", channel->name); dev_dbg(&edge->dev, "registering '%s'\n", channel->name);
qsdev = kzalloc(sizeof(*qsdev), GFP_KERNEL); qsdev = kzalloc(sizeof(*qsdev), GFP_KERNEL);
if (!qsdev) if (!qsdev)
@ -981,7 +970,7 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel)
edge->of_node->name, edge->of_node->name,
node ? node->name : channel->name); node ? node->name : channel->name);
qsdev->dev.parent = smd->dev; qsdev->dev.parent = &edge->dev;
qsdev->dev.bus = &qcom_smd_bus; qsdev->dev.bus = &qcom_smd_bus;
qsdev->dev.release = qcom_smd_release_device; qsdev->dev.release = qcom_smd_release_device;
qsdev->dev.of_node = node; qsdev->dev.of_node = node;
@ -992,7 +981,7 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel)
ret = device_register(&qsdev->dev); ret = device_register(&qsdev->dev);
if (ret) { if (ret) {
dev_err(smd->dev, "device_register failed: %d\n", ret); dev_err(&edge->dev, "device_register failed: %d\n", ret);
put_device(&qsdev->dev); put_device(&qsdev->dev);
} }
@ -1138,19 +1127,18 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed
char *name) char *name)
{ {
struct qcom_smd_channel *channel; struct qcom_smd_channel *channel;
struct qcom_smd *smd = edge->smd;
size_t fifo_size; size_t fifo_size;
size_t info_size; size_t info_size;
void *fifo_base; void *fifo_base;
void *info; void *info;
int ret; int ret;
channel = devm_kzalloc(smd->dev, sizeof(*channel), GFP_KERNEL); channel = devm_kzalloc(&edge->dev, sizeof(*channel), GFP_KERNEL);
if (!channel) if (!channel)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
channel->edge = edge; channel->edge = edge;
channel->name = devm_kstrdup(smd->dev, name, GFP_KERNEL); channel->name = devm_kstrdup(&edge->dev, name, GFP_KERNEL);
if (!channel->name) if (!channel->name)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
@ -1173,7 +1161,7 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed
} else if (info_size == 2 * sizeof(struct smd_channel_info)) { } else if (info_size == 2 * sizeof(struct smd_channel_info)) {
channel->info = info; channel->info = info;
} else { } else {
dev_err(smd->dev, dev_err(&edge->dev,
"channel info of size %zu not supported\n", info_size); "channel info of size %zu not supported\n", info_size);
ret = -EINVAL; ret = -EINVAL;
goto free_name_and_channel; goto free_name_and_channel;
@ -1188,7 +1176,7 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed
/* The channel consist of a rx and tx fifo of equal size */ /* The channel consist of a rx and tx fifo of equal size */
fifo_size /= 2; fifo_size /= 2;
dev_dbg(smd->dev, "new channel '%s' info-size: %zu fifo-size: %zu\n", dev_dbg(&edge->dev, "new channel '%s' info-size: %zu fifo-size: %zu\n",
name, info_size, fifo_size); name, info_size, fifo_size);
channel->tx_fifo = fifo_base; channel->tx_fifo = fifo_base;
@ -1200,8 +1188,8 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed
return channel; return channel;
free_name_and_channel: free_name_and_channel:
devm_kfree(smd->dev, channel->name); devm_kfree(&edge->dev, channel->name);
devm_kfree(smd->dev, channel); devm_kfree(&edge->dev, channel);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
@ -1217,7 +1205,6 @@ static void qcom_channel_scan_worker(struct work_struct *work)
struct qcom_smd_alloc_entry *alloc_tbl; struct qcom_smd_alloc_entry *alloc_tbl;
struct qcom_smd_alloc_entry *entry; struct qcom_smd_alloc_entry *entry;
struct qcom_smd_channel *channel; struct qcom_smd_channel *channel;
struct qcom_smd *smd = edge->smd;
unsigned long flags; unsigned long flags;
unsigned fifo_id; unsigned fifo_id;
unsigned info_id; unsigned info_id;
@ -1261,7 +1248,7 @@ static void qcom_channel_scan_worker(struct work_struct *work)
list_add(&channel->list, &edge->channels); list_add(&channel->list, &edge->channels);
spin_unlock_irqrestore(&edge->channels_lock, flags); spin_unlock_irqrestore(&edge->channels_lock, flags);
dev_dbg(smd->dev, "new channel found: '%s'\n", channel->name); dev_dbg(&edge->dev, "new channel found: '%s'\n", channel->name);
set_bit(i, edge->allocated[tbl]); set_bit(i, edge->allocated[tbl]);
wake_up_interruptible(&edge->new_channel_event); wake_up_interruptible(&edge->new_channel_event);
@ -1401,15 +1388,102 @@ static int qcom_smd_parse_edge(struct device *dev,
return 0; return 0;
} }
static int qcom_smd_probe(struct platform_device *pdev) /*
* Release function for an edge.
* Reset the state of each associated channel and free the edge context.
*/
static void qcom_smd_edge_release(struct device *dev)
{
struct qcom_smd_channel *channel;
struct qcom_smd_edge *edge = to_smd_edge(dev);
list_for_each_entry(channel, &edge->channels, list) {
SET_RX_CHANNEL_INFO(channel, state, SMD_CHANNEL_CLOSED);
SET_RX_CHANNEL_INFO(channel, head, 0);
SET_RX_CHANNEL_INFO(channel, tail, 0);
}
kfree(edge);
}
/**
* qcom_smd_register_edge() - register an edge based on an device_node
* @parent: parent device for the edge
* @node: device_node describing the edge
*
* Returns an edge reference, or negative ERR_PTR() on failure.
*/
struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent,
struct device_node *node)
{ {
struct qcom_smd_edge *edge; struct qcom_smd_edge *edge;
struct device_node *node;
struct qcom_smd *smd;
size_t array_size;
int num_edges;
int ret; int ret;
int i = 0;
edge = kzalloc(sizeof(*edge), GFP_KERNEL);
if (!edge)
return ERR_PTR(-ENOMEM);
init_waitqueue_head(&edge->new_channel_event);
edge->dev.parent = parent;
edge->dev.release = qcom_smd_edge_release;
dev_set_name(&edge->dev, "%s:%s", dev_name(parent), node->name);
ret = device_register(&edge->dev);
if (ret) {
pr_err("failed to register smd edge\n");
return ERR_PTR(ret);
}
ret = qcom_smd_parse_edge(&edge->dev, node, edge);
if (ret) {
dev_err(&edge->dev, "failed to parse smd edge\n");
goto unregister_dev;
}
schedule_work(&edge->scan_work);
return edge;
unregister_dev:
put_device(&edge->dev);
return ERR_PTR(ret);
}
EXPORT_SYMBOL(qcom_smd_register_edge);
static int qcom_smd_remove_device(struct device *dev, void *data)
{
device_unregister(dev);
of_node_put(dev->of_node);
put_device(dev);
return 0;
}
/**
* qcom_smd_unregister_edge() - release an edge and its children
* @edge: edge reference acquired from qcom_smd_register_edge
*/
int qcom_smd_unregister_edge(struct qcom_smd_edge *edge)
{
int ret;
disable_irq(edge->irq);
cancel_work_sync(&edge->scan_work);
cancel_work_sync(&edge->state_work);
ret = device_for_each_child(&edge->dev, NULL, qcom_smd_remove_device);
if (ret)
dev_warn(&edge->dev, "can't remove smd device: %d\n", ret);
device_unregister(&edge->dev);
return 0;
}
EXPORT_SYMBOL(qcom_smd_unregister_edge);
static int qcom_smd_probe(struct platform_device *pdev)
{
struct device_node *node;
void *p; void *p;
/* Wait for smem */ /* Wait for smem */
@ -1417,59 +1491,32 @@ static int qcom_smd_probe(struct platform_device *pdev)
if (PTR_ERR(p) == -EPROBE_DEFER) if (PTR_ERR(p) == -EPROBE_DEFER)
return PTR_ERR(p); return PTR_ERR(p);
num_edges = of_get_available_child_count(pdev->dev.of_node); for_each_available_child_of_node(pdev->dev.of_node, node)
array_size = sizeof(*smd) + num_edges * sizeof(struct qcom_smd_edge); qcom_smd_register_edge(&pdev->dev, node);
smd = devm_kzalloc(&pdev->dev, array_size, GFP_KERNEL);
if (!smd)
return -ENOMEM;
smd->dev = &pdev->dev;
smd->num_edges = num_edges;
for_each_available_child_of_node(pdev->dev.of_node, node) {
edge = &smd->edges[i++];
edge->smd = smd;
init_waitqueue_head(&edge->new_channel_event);
ret = qcom_smd_parse_edge(&pdev->dev, node, edge);
if (ret)
continue;
schedule_work(&edge->scan_work);
}
platform_set_drvdata(pdev, smd);
return 0; return 0;
} }
static int qcom_smd_remove_edge(struct device *dev, void *data)
{
struct qcom_smd_edge *edge = to_smd_edge(dev);
return qcom_smd_unregister_edge(edge);
}
/* /*
* Shut down all smd clients by making sure that each edge stops processing * Shut down all smd clients by making sure that each edge stops processing
* events and scanning for new channels, then call destroy on the devices. * events and scanning for new channels, then call destroy on the devices.
*/ */
static int qcom_smd_remove(struct platform_device *pdev) static int qcom_smd_remove(struct platform_device *pdev)
{ {
struct qcom_smd_channel *channel; int ret;
struct qcom_smd_edge *edge;
struct qcom_smd *smd = platform_get_drvdata(pdev);
int i;
for (i = 0; i < smd->num_edges; i++) { ret = device_for_each_child(&pdev->dev, NULL, qcom_smd_remove_edge);
edge = &smd->edges[i]; if (ret)
dev_warn(&pdev->dev, "can't remove smd device: %d\n", ret);
disable_irq(edge->irq); return ret;
cancel_work_sync(&edge->scan_work);
cancel_work_sync(&edge->state_work);
/* No need to lock here, because the writer is gone */
list_for_each_entry(channel, &edge->channels, list) {
if (!channel->qsdev)
continue;
qcom_smd_destroy_device(channel);
}
}
return 0;
} }
static const struct of_device_id qcom_smd_of_match[] = { static const struct of_device_id qcom_smd_of_match[] = {

View File

@ -61,6 +61,10 @@ void qcom_smd_set_drvdata(struct qcom_smd_channel *channel, void *data);
int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len); int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len);
struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent,
struct device_node *node);
int qcom_smd_unregister_edge(struct qcom_smd_edge *edge);
#else #else
static inline int qcom_smd_driver_register(struct qcom_smd_driver *drv) static inline int qcom_smd_driver_register(struct qcom_smd_driver *drv)
@ -111,6 +115,20 @@ static inline int qcom_smd_send(struct qcom_smd_channel *channel,
return -ENXIO; return -ENXIO;
} }
static inline struct qcom_smd_edge *
qcom_smd_register_edge(struct device *parent,
struct device_node *node)
{
return ERR_PTR(-ENXIO);
}
static inline int qcom_smd_unregister_edge(struct qcom_smd_edge *edge)
{
/* This shouldn't be possible */
WARN_ON(1);
return -ENXIO;
}
#endif #endif
#define module_qcom_smd_driver(__smd_driver) \ #define module_qcom_smd_driver(__smd_driver) \