aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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) \