diff options
-rw-r--r-- | drivers/soc/qcom/smd.c | 197 | ||||
-rw-r--r-- | include/linux/soc/qcom/smd.h | 18 |
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 | */ |
113 | struct qcom_smd_edge { | 113 | struct 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 | */ | ||
208 | struct 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 | ||
1202 | free_name_and_channel: | 1190 | free_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 | ||
1404 | static 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 | */ | ||
1395 | static 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 | */ | ||
1416 | struct 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 | |||
1447 | unregister_dev: | ||
1448 | put_device(&edge->dev); | ||
1449 | return ERR_PTR(ret); | ||
1450 | } | ||
1451 | EXPORT_SYMBOL(qcom_smd_register_edge); | ||
1452 | |||
1453 | static 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 | */ | ||
1466 | int 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 | } | ||
1482 | EXPORT_SYMBOL(qcom_smd_unregister_edge); | ||
1483 | |||
1484 | static 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); | 1500 | static 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 | */ |
1449 | static int qcom_smd_remove(struct platform_device *pdev) | 1511 | static 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 | ||
1475 | static const struct of_device_id qcom_smd_of_match[] = { | 1522 | static 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); | |||
61 | int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len); | 61 | int qcom_smd_send(struct qcom_smd_channel *channel, const void *data, int len); |
62 | 62 | ||
63 | 63 | ||
64 | struct qcom_smd_edge *qcom_smd_register_edge(struct device *parent, | ||
65 | struct device_node *node); | ||
66 | int qcom_smd_unregister_edge(struct qcom_smd_edge *edge); | ||
67 | |||
64 | #else | 68 | #else |
65 | 69 | ||
66 | static inline int qcom_smd_driver_register(struct qcom_smd_driver *drv) | 70 | 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, | |||
111 | return -ENXIO; | 115 | return -ENXIO; |
112 | } | 116 | } |
113 | 117 | ||
118 | static inline struct qcom_smd_edge * | ||
119 | qcom_smd_register_edge(struct device *parent, | ||
120 | struct device_node *node) | ||
121 | { | ||
122 | return ERR_PTR(-ENXIO); | ||
123 | } | ||
124 | |||
125 | static 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) \ |