summaryrefslogtreecommitdiff
path: root/drivers/soc/qcom
diff options
context:
space:
mode:
authorSrinivas Kandagatla <srinivas.kandagatla@linaro.org>2021-09-27 14:55:42 +0100
committerBjorn Andersson <bjorn.andersson@linaro.org>2021-09-27 22:10:07 -0500
commitec1471a898cca38af6b8956a83ebc1297214546f (patch)
tree617534db647f30d33e009b6d9fcdfb6411218cba /drivers/soc/qcom
parent974c6faf7667e551d202712470ca210c14ca249d (diff)
soc: qcom: apr: Add GPR support
Qualcomm Generic Packet router aka GPR is the IPC mechanism found in AudioReach next generation signal processing framework to perform command and response messages between various processors. GPR has concepts of static and dynamic port, all static services like APM (Audio Processing Manager), PRM (Proxy resource manager) have fixed port numbers where as dynamic services like graphs have dynamic port numbers which are allocated at runtime. All GPR packet messages will have source and destination domain and port along with opcode and payload. Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> Link: https://lore.kernel.org/r/20210927135559.738-6-srinivas.kandagatla@linaro.org
Diffstat (limited to 'drivers/soc/qcom')
-rw-r--r--drivers/soc/qcom/Kconfig2
-rw-r--r--drivers/soc/qcom/apr.c166
2 files changed, 161 insertions, 7 deletions
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 79b568f82a1c..bfa2ab5772cf 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -199,7 +199,7 @@ config QCOM_WCNSS_CTRL
firmware to a newly booted WCNSS chip.
config QCOM_APR
- tristate "Qualcomm APR Bus (Asynchronous Packet Router)"
+ tristate "Qualcomm APR/GPR Bus (Asynchronous/Generic Packet Router)"
depends on ARCH_QCOM || COMPILE_TEST
depends on RPMSG
depends on NET
diff --git a/drivers/soc/qcom/apr.c b/drivers/soc/qcom/apr.c
index bfad71e540ad..8a9bfbcd4bb9 100644
--- a/drivers/soc/qcom/apr.c
+++ b/drivers/soc/qcom/apr.c
@@ -17,8 +17,13 @@
enum {
PR_TYPE_APR = 0,
+ PR_TYPE_GPR,
};
+/* Some random values tbh which does not collide with static modules */
+#define GPR_DYNAMIC_PORT_START 0x10000000
+#define GPR_DYNAMIC_PORT_END 0x20000000
+
struct packet_router {
struct rpmsg_endpoint *ch;
struct device *dev;
@@ -69,6 +74,83 @@ int apr_send_pkt(struct apr_device *adev, struct apr_pkt *pkt)
}
EXPORT_SYMBOL_GPL(apr_send_pkt);
+void gpr_free_port(gpr_port_t *port)
+{
+ struct packet_router *gpr = port->pr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpr->svcs_lock, flags);
+ idr_remove(&gpr->svcs_idr, port->id);
+ spin_unlock_irqrestore(&gpr->svcs_lock, flags);
+
+ kfree(port);
+}
+EXPORT_SYMBOL_GPL(gpr_free_port);
+
+gpr_port_t *gpr_alloc_port(struct apr_device *gdev, struct device *dev,
+ gpr_port_cb cb, void *priv)
+{
+ struct packet_router *pr = dev_get_drvdata(gdev->dev.parent);
+ gpr_port_t *port;
+ struct pkt_router_svc *svc;
+ int id;
+
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return ERR_PTR(-ENOMEM);
+
+ svc = port;
+ svc->callback = cb;
+ svc->pr = pr;
+ svc->priv = priv;
+ svc->dev = dev;
+ spin_lock_init(&svc->lock);
+
+ spin_lock(&pr->svcs_lock);
+ id = idr_alloc_cyclic(&pr->svcs_idr, svc, GPR_DYNAMIC_PORT_START,
+ GPR_DYNAMIC_PORT_END, GFP_ATOMIC);
+ if (id < 0) {
+ dev_err(dev, "Unable to allocate dynamic GPR src port\n");
+ kfree(port);
+ spin_unlock(&pr->svcs_lock);
+ return ERR_PTR(id);
+ }
+
+ svc->id = id;
+ spin_unlock(&pr->svcs_lock);
+
+ return port;
+}
+EXPORT_SYMBOL_GPL(gpr_alloc_port);
+
+static int pkt_router_send_svc_pkt(struct pkt_router_svc *svc, struct gpr_pkt *pkt)
+{
+ struct packet_router *pr = svc->pr;
+ struct gpr_hdr *hdr;
+ unsigned long flags;
+ int ret;
+
+ hdr = &pkt->hdr;
+
+ spin_lock_irqsave(&svc->lock, flags);
+ ret = rpmsg_trysend(pr->ch, pkt, hdr->pkt_size);
+ spin_unlock_irqrestore(&svc->lock, flags);
+
+ return ret ? ret : hdr->pkt_size;
+}
+
+int gpr_send_pkt(struct apr_device *gdev, struct gpr_pkt *pkt)
+{
+ return pkt_router_send_svc_pkt(&gdev->svc, pkt);
+}
+EXPORT_SYMBOL_GPL(gpr_send_pkt);
+
+int gpr_send_port_pkt(gpr_port_t *port, struct gpr_pkt *pkt)
+{
+ return pkt_router_send_svc_pkt(port, pkt);
+}
+EXPORT_SYMBOL_GPL(gpr_send_port_pkt);
+
static void apr_dev_release(struct device *dev)
{
struct apr_device *adev = to_apr_device(dev);
@@ -177,6 +259,59 @@ static int apr_do_rx_callback(struct packet_router *apr, struct apr_rx_buf *abuf
return 0;
}
+static int gpr_do_rx_callback(struct packet_router *gpr, struct apr_rx_buf *abuf)
+{
+ uint16_t hdr_size, ver;
+ struct pkt_router_svc *svc = NULL;
+ struct gpr_resp_pkt resp;
+ struct gpr_hdr *hdr;
+ unsigned long flags;
+ void *buf = abuf->buf;
+ int len = abuf->len;
+
+ hdr = buf;
+ ver = hdr->version;
+ if (ver > GPR_PKT_VER + 1)
+ return -EINVAL;
+
+ hdr_size = hdr->hdr_size;
+ if (hdr_size < GPR_PKT_HEADER_WORD_SIZE) {
+ dev_err(gpr->dev, "GPR: Wrong hdr size:%d\n", hdr_size);
+ return -EINVAL;
+ }
+
+ if (hdr->pkt_size < GPR_PKT_HEADER_BYTE_SIZE || hdr->pkt_size != len) {
+ dev_err(gpr->dev, "GPR: Wrong packet size\n");
+ return -EINVAL;
+ }
+
+ resp.hdr = *hdr;
+ resp.payload_size = hdr->pkt_size - (hdr_size * 4);
+
+ /*
+ * NOTE: hdr_size is not same as GPR_HDR_SIZE as remote can include
+ * optional headers in to gpr_hdr which should be ignored
+ */
+ if (resp.payload_size > 0)
+ resp.payload = buf + (hdr_size * 4);
+
+
+ spin_lock_irqsave(&gpr->svcs_lock, flags);
+ svc = idr_find(&gpr->svcs_idr, hdr->dest_port);
+ spin_unlock_irqrestore(&gpr->svcs_lock, flags);
+
+ if (!svc) {
+ dev_err(gpr->dev, "GPR: Port(%x) is not registered\n",
+ hdr->dest_port);
+ return -EINVAL;
+ }
+
+ if (svc->callback)
+ svc->callback(&resp, svc->priv, 0);
+
+ return 0;
+}
+
static void apr_rxwq(struct work_struct *work)
{
struct packet_router *apr = container_of(work, struct packet_router, rx_work);
@@ -189,6 +324,9 @@ static void apr_rxwq(struct work_struct *work)
case PR_TYPE_APR:
apr_do_rx_callback(apr, abuf);
break;
+ case PR_TYPE_GPR:
+ gpr_do_rx_callback(apr, abuf);
+ break;
default:
break;
}
@@ -227,8 +365,13 @@ static int apr_device_probe(struct device *dev)
{
struct apr_device *adev = to_apr_device(dev);
struct apr_driver *adrv = to_apr_driver(dev->driver);
+ int ret;
- return adrv->probe(adev);
+ ret = adrv->probe(adev);
+ if (!ret)
+ adev->svc.callback = adrv->gpr_callback;
+
+ return ret;
}
static void apr_device_remove(struct device *dev)
@@ -299,6 +442,10 @@ static int apr_add_device(struct device *dev, struct device_node *np,
dev_set_name(&adev->dev, "aprsvc:%s:%x:%x", adev->name,
domain_id, svc_id);
break;
+ case PR_TYPE_GPR:
+ dev_set_name(&adev->dev, "gprsvc:%s:%x:%x", adev->name,
+ domain_id, svc_id);
+ break;
default:
break;
}
@@ -316,7 +463,7 @@ static int apr_add_device(struct device *dev, struct device_node *np,
of_property_read_string_index(np, "qcom,protection-domain",
1, &adev->service_path);
- dev_info(dev, "Adding APR dev: %s\n", dev_name(&adev->dev));
+ dev_info(dev, "Adding APR/GPR dev: %s\n", dev_name(&adev->dev));
ret = device_register(&adev->dev);
if (ret) {
@@ -446,10 +593,16 @@ static int apr_probe(struct rpmsg_device *rpdev)
return -ENOMEM;
ret = of_property_read_u32(dev->of_node, "qcom,domain", &apr->dest_domain_id);
- if (ret) /* try deprecated apr-domain property */
- ret = of_property_read_u32(dev->of_node, "qcom,apr-domain",
- &apr->dest_domain_id);
- apr->type = PR_TYPE_APR;
+
+ if (of_device_is_compatible(dev->of_node, "qcom,gpr")) {
+ apr->type = PR_TYPE_GPR;
+ } else {
+ if (ret) /* try deprecated apr-domain property */
+ ret = of_property_read_u32(dev->of_node, "qcom,apr-domain",
+ &apr->dest_domain_id);
+ apr->type = PR_TYPE_APR;
+ }
+
if (ret) {
dev_err(dev, "Domain ID not specified in DT\n");
return ret;
@@ -534,6 +687,7 @@ EXPORT_SYMBOL_GPL(apr_driver_unregister);
static const struct of_device_id pkt_router_of_match[] = {
{ .compatible = "qcom,apr"},
{ .compatible = "qcom,apr-v2"},
+ { .compatible = "qcom,gpr"},
{}
};
MODULE_DEVICE_TABLE(of, pkt_router_of_match);