aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjorn Andersson <bjorn.andersson@linaro.org>2016-08-15 14:15:57 -0400
committerAndy Gross <andy.gross@linaro.org>2016-08-23 16:46:33 -0400
commitda0573026c2d3d445c39385024bfc3ce6beebe09 (patch)
treee65621f29dd3c430f5005411730ce4fa2b9232c4
parent381a0b4ce45b2ad809b79049e6316a83d5eaa2ea (diff)
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>
-rw-r--r--drivers/soc/qcom/smd.c197
-rw-r--r--include/linux/soc/qcom/smd.h18
2 files changed, 140 insertions, 75 deletions
diff --git a/drivers/soc/qcom/smd.c b/drivers/soc/qcom/smd.c
index 679f7778a4e3..f20816bef1b5 100644
--- a/drivers/soc/qcom/smd.c
+++ b/drivers/soc/qcom/smd.c
@@ -95,7 +95,7 @@ static const struct {
95 95
96/** 96/**
97 * struct qcom_smd_edge - representing a remote processor 97 * struct qcom_smd_edge - representing a remote processor
98 * @smd: handle to qcom_smd 98 * @dev: device for this edge
99 * @of_node: of_node handle for information related to this edge 99 * @of_node: of_node handle for information related to this edge
100 * @edge_id: identifier of this edge 100 * @edge_id: identifier of this edge
101 * @remote_pid: identifier of remote processor 101 * @remote_pid: identifier of remote processor
@@ -111,7 +111,8 @@ static const struct {
111 * @state_work: work item for edge state changes 111 * @state_work: work item for edge state changes
112 */ 112 */
113struct qcom_smd_edge { 113struct qcom_smd_edge {
114 struct qcom_smd *smd; 114 struct device dev;
115
115 struct device_node *of_node; 116 struct device_node *of_node;
116 unsigned edge_id; 117 unsigned edge_id;
117 unsigned remote_pid; 118 unsigned remote_pid;
@@ -135,6 +136,8 @@ struct qcom_smd_edge {
135 struct work_struct state_work; 136 struct work_struct state_work;
136}; 137};
137 138
139#define to_smd_edge(d) container_of(d, struct qcom_smd_edge, dev)
140
138/* 141/*
139 * SMD channel states. 142 * SMD channel states.
140 */ 143 */
@@ -199,19 +202,6 @@ struct qcom_smd_channel {
199 struct list_head list; 202 struct list_head list;
200}; 203};
201 204
202/**
203 * struct qcom_smd - smd struct
204 * @dev: device struct
205 * @num_edges: number of entries in @edges
206 * @edges: array of edges to be handled
207 */
208struct qcom_smd {
209 struct device *dev;
210
211 unsigned num_edges;
212 struct qcom_smd_edge edges[0];
213};
214
215/* 205/*
216 * Format of the smd_info smem items, for byte aligned channels. 206 * Format of the smd_info smem items, for byte aligned channels.
217 */ 207 */
@@ -420,7 +410,7 @@ static void qcom_smd_channel_set_state(struct qcom_smd_channel *channel,
420 if (channel->state == state) 410 if (channel->state == state)
421 return; 411 return;
422 412
423 dev_dbg(edge->smd->dev, "set_state(%s, %d)\n", channel->name, state); 413 dev_dbg(&edge->dev, "set_state(%s, %d)\n", channel->name, state);
424 414
425 SET_TX_CHANNEL_FLAG(channel, fDSR, is_open); 415 SET_TX_CHANNEL_FLAG(channel, fDSR, is_open);
426 SET_TX_CHANNEL_FLAG(channel, fCTS, is_open); 416 SET_TX_CHANNEL_FLAG(channel, fCTS, is_open);
@@ -964,13 +954,12 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel)
964 struct qcom_smd_device *qsdev; 954 struct qcom_smd_device *qsdev;
965 struct qcom_smd_edge *edge = channel->edge; 955 struct qcom_smd_edge *edge = channel->edge;
966 struct device_node *node; 956 struct device_node *node;
967 struct qcom_smd *smd = edge->smd;
968 int ret; 957 int ret;
969 958
970 if (channel->qsdev) 959 if (channel->qsdev)
971 return -EEXIST; 960 return -EEXIST;
972 961
973 dev_dbg(smd->dev, "registering '%s'\n", channel->name); 962 dev_dbg(&edge->dev, "registering '%s'\n", channel->name);
974 963
975 qsdev = kzalloc(sizeof(*qsdev), GFP_KERNEL); 964 qsdev = kzalloc(sizeof(*qsdev), GFP_KERNEL);
976 if (!qsdev) 965 if (!qsdev)
@@ -981,7 +970,7 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel)
981 edge->of_node->name, 970 edge->of_node->name,
982 node ? node->name : channel->name); 971 node ? node->name : channel->name);
983 972
984 qsdev->dev.parent = smd->dev; 973 qsdev->dev.parent = &edge->dev;
985 qsdev->dev.bus = &qcom_smd_bus; 974 qsdev->dev.bus = &qcom_smd_bus;
986 qsdev->dev.release = qcom_smd_release_device; 975 qsdev->dev.release = qcom_smd_release_device;
987 qsdev->dev.of_node = node; 976 qsdev->dev.of_node = node;
@@ -992,7 +981,7 @@ static int qcom_smd_create_device(struct qcom_smd_channel *channel)
992 981
993 ret = device_register(&qsdev->dev); 982 ret = device_register(&qsdev->dev);
994 if (ret) { 983 if (ret) {
995 dev_err(smd->dev, "device_register failed: %d\n", ret); 984 dev_err(&edge->dev, "device_register failed: %d\n", ret);
996 put_device(&qsdev->dev); 985 put_device(&qsdev->dev);
997 } 986 }
998 987
@@ -1138,19 +1127,18 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed
1138 char *name) 1127 char *name)
1139{ 1128{
1140 struct qcom_smd_channel *channel; 1129 struct qcom_smd_channel *channel;
1141 struct qcom_smd *smd = edge->smd;
1142 size_t fifo_size; 1130 size_t fifo_size;
1143 size_t info_size; 1131 size_t info_size;
1144 void *fifo_base; 1132 void *fifo_base;
1145 void *info; 1133 void *info;
1146 int ret; 1134 int ret;
1147 1135
1148 channel = devm_kzalloc(smd->dev, sizeof(*channel), GFP_KERNEL); 1136 channel = devm_kzalloc(&edge->dev, sizeof(*channel), GFP_KERNEL);
1149 if (!channel) 1137 if (!channel)
1150 return ERR_PTR(-ENOMEM); 1138 return ERR_PTR(-ENOMEM);
1151 1139
1152 channel->edge = edge; 1140 channel->edge = edge;
1153 channel->name = devm_kstrdup(smd->dev, name, GFP_KERNEL); 1141 channel->name = devm_kstrdup(&edge->dev, name, GFP_KERNEL);
1154 if (!channel->name) 1142 if (!channel->name)
1155 return ERR_PTR(-ENOMEM); 1143 return ERR_PTR(-ENOMEM);
1156 1144
@@ -1173,7 +1161,7 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed
1173 } else if (info_size == 2 * sizeof(struct smd_channel_info)) { 1161 } else if (info_size == 2 * sizeof(struct smd_channel_info)) {
1174 channel->info = info; 1162 channel->info = info;
1175 } else { 1163 } else {
1176 dev_err(smd->dev, 1164 dev_err(&edge->dev,
1177 "channel info of size %zu not supported\n", info_size); 1165 "channel info of size %zu not supported\n", info_size);
1178 ret = -EINVAL; 1166 ret = -EINVAL;
1179 goto free_name_and_channel; 1167 goto free_name_and_channel;
@@ -1188,7 +1176,7 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed
1188 /* The channel consist of a rx and tx fifo of equal size */ 1176 /* The channel consist of a rx and tx fifo of equal size */
1189 fifo_size /= 2; 1177 fifo_size /= 2;
1190 1178
1191 dev_dbg(smd->dev, "new channel '%s' info-size: %zu fifo-size: %zu\n", 1179 dev_dbg(&edge->dev, "new channel '%s' info-size: %zu fifo-size: %zu\n",
1192 name, info_size, fifo_size); 1180 name, info_size, fifo_size);
1193 1181
1194 channel->tx_fifo = fifo_base; 1182 channel->tx_fifo = fifo_base;
@@ -1200,8 +1188,8 @@ static struct qcom_smd_channel *qcom_smd_create_channel(struct qcom_smd_edge *ed
1200 return channel; 1188 return channel;
1201 1189
1202free_name_and_channel: 1190free_name_and_channel:
1203 devm_kfree(smd->dev, channel->name); 1191 devm_kfree(&edge->dev, channel->name);
1204 devm_kfree(smd->dev, channel); 1192 devm_kfree(&edge->dev, channel);
1205 1193
1206 return ERR_PTR(ret); 1194 return ERR_PTR(ret);
1207} 1195}
@@ -1217,7 +1205,6 @@ static void qcom_channel_scan_worker(struct work_struct *work)
1217 struct qcom_smd_alloc_entry *alloc_tbl; 1205 struct qcom_smd_alloc_entry *alloc_tbl;
1218 struct qcom_smd_alloc_entry *entry; 1206 struct qcom_smd_alloc_entry *entry;
1219 struct qcom_smd_channel *channel; 1207 struct qcom_smd_channel *channel;
1220 struct qcom_smd *smd = edge->smd;
1221 unsigned long flags; 1208 unsigned long flags;
1222 unsigned fifo_id; 1209 unsigned fifo_id;
1223 unsigned info_id; 1210 unsigned info_id;
@@ -1261,7 +1248,7 @@ static void qcom_channel_scan_worker(struct work_struct *work)
1261 list_add(&channel->list, &edge->channels); 1248 list_add(&channel->list, &edge->channels);
1262 spin_unlock_irqrestore(&edge->channels_lock, flags); 1249 spin_unlock_irqrestore(&edge->channels_lock, flags);
1263 1250
1264 dev_dbg(smd->dev, "new channel found: '%s'\n", channel->name); 1251 dev_dbg(&edge->dev, "new channel found: '%s'\n", channel->name);
1265 set_bit(i, edge->allocated[tbl]); 1252 set_bit(i, edge->allocated[tbl]);
1266 1253
1267 wake_up_interruptible(&edge->new_channel_event); 1254 wake_up_interruptible(&edge->new_channel_event);
@@ -1401,15 +1388,102 @@ static int qcom_smd_parse_edge(struct device *dev,
1401 return 0; 1388 return 0;
1402} 1389}
1403 1390
1404static int qcom_smd_probe(struct platform_device *pdev) 1391/*
1392 * Release function for an edge.
1393 * Reset the state of each associated channel and free the edge context.
1394 */
1395static void qcom_smd_edge_release(struct device *dev)
1396{
1397 struct qcom_smd_channel *channel;
1398 struct qcom_smd_edge *edge = to_smd_edge(dev);
1399
1400 list_for_each_entry(channel, &edge->channels, list) {
1401 SET_RX_CHANNEL_INFO(channel, state, SMD_CHANNEL_CLOSED);
1402 SET_RX_CHANNEL_INFO(channel, head, 0);
1403 SET_RX_CHANNEL_INFO(channel, tail, 0);
1404 }
1405
1406 kfree(edge);
1407}
1408
1409/**
1410 * qcom_smd_register_edge() - register an edge based on an device_node
1411 * @parent: parent device for the edge
1412 * @node: device_node describing the edge
1413 *
1414 * Returns an edge reference, or negative ERR_PTR() on failure.
1415 */
1416struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent,
1417 struct device_node *node)
1405{ 1418{
1406 struct qcom_smd_edge *edge; 1419 struct qcom_smd_edge *edge;
1407 struct device_node *node;
1408 struct qcom_smd *smd;
1409 size_t array_size;
1410 int num_edges;
1411 int ret; 1420 int ret;
1412 int i = 0; 1421
1422 edge = kzalloc(sizeof(*edge), GFP_KERNEL);
1423 if (!edge)
1424 return ERR_PTR(-ENOMEM);
1425
1426 init_waitqueue_head(&edge->new_channel_event);
1427
1428 edge->dev.parent = parent;
1429 edge->dev.release = qcom_smd_edge_release;
1430 dev_set_name(&edge->dev, "%s:%s", dev_name(parent), node->name);
1431 ret = device_register(&edge->dev);
1432 if (ret) {
1433 pr_err("failed to register smd edge\n");
1434 return ERR_PTR(ret);
1435 }
1436
1437 ret = qcom_smd_parse_edge(&edge->dev, node, edge);
1438 if (ret) {
1439 dev_err(&edge->dev, "failed to parse smd edge\n");
1440 goto unregister_dev;
1441 }
1442
1443 schedule_work(&edge->scan_work);
1444
1445 return edge;
1446
1447unregister_dev:
1448 put_device(&edge->dev);
1449 return ERR_PTR(ret);
1450}
1451EXPORT_SYMBOL(qcom_smd_register_edge);
1452
1453static int qcom_smd_remove_device(struct device *dev, void *data)
1454{
1455 device_unregister(dev);
1456 of_node_put(dev->of_node);
1457 put_device(dev);
1458
1459 return 0;
1460}
1461
1462/**
1463 * qcom_smd_unregister_edge() - release an edge and its children
1464 * @edge: edge reference acquired from qcom_smd_register_edge
1465 */
1466int qcom_smd_unregister_edge(struct qcom_smd_edge *edge)
1467{
1468 int ret;
1469
1470 disable_irq(edge->irq);
1471 cancel_work_sync(&edge->scan_work);
1472 cancel_work_sync(&edge->state_work);
1473
1474 ret = device_for_each_child(&edge->dev, NULL, qcom_smd_remove_device);
1475 if (ret)
1476 dev_warn(&edge->dev, "can't remove smd device: %d\n", ret);
1477
1478 device_unregister(&edge->dev);
1479
1480 return 0;
1481}
1482EXPORT_SYMBOL(qcom_smd_unregister_edge);
1483
1484static int qcom_smd_probe(struct platform_device *pdev)
1485{
1486 struct device_node *node;
1413 void *p; 1487 void *p;
1414 1488
1415 /* Wait for smem */ 1489 /* Wait for smem */
@@ -1417,29 +1491,17 @@ static int qcom_smd_probe(struct platform_device *pdev)
1417 if (PTR_ERR(p) == -EPROBE_DEFER) 1491 if (PTR_ERR(p) == -EPROBE_DEFER)
1418 return PTR_ERR(p); 1492 return PTR_ERR(p);
1419 1493
1420 num_edges = of_get_available_child_count(pdev->dev.of_node); 1494 for_each_available_child_of_node(pdev->dev.of_node, node)
1421 array_size = sizeof(*smd) + num_edges * sizeof(struct qcom_smd_edge); 1495 qcom_smd_register_edge(&pdev->dev, node);
1422 smd = devm_kzalloc(&pdev->dev, array_size, GFP_KERNEL);
1423 if (!smd)
1424 return -ENOMEM;
1425 smd->dev = &pdev->dev;
1426 1496
1427 smd->num_edges = num_edges; 1497 return 0;
1428 for_each_available_child_of_node(pdev->dev.of_node, node) { 1498}
1429 edge = &smd->edges[i++];
1430 edge->smd = smd;
1431 init_waitqueue_head(&edge->new_channel_event);
1432
1433 ret = qcom_smd_parse_edge(&pdev->dev, node, edge);
1434 if (ret)
1435 continue;
1436
1437 schedule_work(&edge->scan_work);
1438 }
1439 1499
1440 platform_set_drvdata(pdev, smd); 1500static int qcom_smd_remove_edge(struct device *dev, void *data)
1501{
1502 struct qcom_smd_edge *edge = to_smd_edge(dev);
1441 1503
1442 return 0; 1504 return qcom_smd_unregister_edge(edge);
1443} 1505}
1444 1506
1445/* 1507/*
@@ -1448,28 +1510,13 @@ static int qcom_smd_probe(struct platform_device *pdev)
1448 */ 1510 */
1449static int qcom_smd_remove(struct platform_device *pdev) 1511static int qcom_smd_remove(struct platform_device *pdev)
1450{ 1512{
1451 struct qcom_smd_channel *channel; 1513 int ret;
1452 struct qcom_smd_edge *edge;
1453 struct qcom_smd *smd = platform_get_drvdata(pdev);
1454 int i;
1455
1456 for (i = 0; i < smd->num_edges; i++) {
1457 edge = &smd->edges[i];
1458
1459 disable_irq(edge->irq);
1460 cancel_work_sync(&edge->scan_work);
1461 cancel_work_sync(&edge->state_work);
1462
1463 /* No need to lock here, because the writer is gone */
1464 list_for_each_entry(channel, &edge->channels, list) {
1465 if (!channel->qsdev)
1466 continue;
1467 1514
1468 qcom_smd_destroy_device(channel); 1515 ret = device_for_each_child(&pdev->dev, NULL, qcom_smd_remove_edge);
1469 } 1516 if (ret)
1470 } 1517 dev_warn(&pdev->dev, "can't remove smd device: %d\n", ret);
1471 1518
1472 return 0; 1519 return ret;
1473} 1520}
1474 1521
1475static const struct of_device_id qcom_smd_of_match[] = { 1522static const struct of_device_id qcom_smd_of_match[] = {
diff --git a/include/linux/soc/qcom/smd.h b/include/linux/soc/qcom/smd.h
index 324b1decfffb..f148e0ffbec7 100644
--- a/include/linux/soc/qcom/smd.h
+++ b/include/linux/soc/qcom/smd.h
@@ -61,6 +61,10 @@ void qcom_smd_set_drvdata(struct qcom_smd_channel *channel, void *data);
61int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len); 61int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len);
62 62
63 63
64struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent,
65 struct device_node *node);
66int qcom_smd_unregister_edge(struct qcom_smd_edge *edge);
67
64#else 68#else
65 69
66static inline int qcom_smd_driver_register(struct qcom_smd_driver *drv) 70static 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,
111 return -ENXIO; 115 return -ENXIO;
112} 116}
113 117
118static inline struct qcom_smd_edge *
119qcom_smd_register_edge(struct device *parent,
120 struct device_node *node)
121{
122 return ERR_PTR(-ENXIO);
123}
124
125static inline int qcom_smd_unregister_edge(struct qcom_smd_edge *edge)
126{
127 /* This shouldn't be possible */
128 WARN_ON(1);
129 return -ENXIO;
130}
131
114#endif 132#endif
115 133
116#define module_qcom_smd_driver(__smd_driver) \ 134#define module_qcom_smd_driver(__smd_driver) \