v5.15-rc1 + 20210927135559.738-[23456]-srinivas.kandagatla@linaro.org

This immutable branch is based on v5.15-rc1 and contains the following
 patches extending the existig APR driver to also implement GPR:
 20210927135559.738-2-srinivas.kandagatla@linaro.org
 20210927135559.738-3-srinivas.kandagatla@linaro.org
 20210927135559.738-4-srinivas.kandagatla@linaro.org
 20210927135559.738-5-srinivas.kandagatla@linaro.org
 20210927135559.738-6-srinivas.kandagatla@linaro.org
 -----BEGIN PGP SIGNATURE-----
 
 iQJPBAABCAA5FiEEBd4DzF816k8JZtUlCx85Pw2ZrcUFAmFSiE4bHGJqb3JuLmFu
 ZGVyc3NvbkBsaW5hcm8ub3JnAAoJEAsfOT8Nma3FNV0P/1R/t0Dfj+vJg8SarPPg
 u/2LbLBAhFfn0WoVJgdYmpW3lG3kxG+z7I4s5gDdKCrKeyj//jJReRVwkgG4QxNs
 NgSAd24o/afb4/jcNTlmbhIyo+k9gsU5rxw7AynwRkLOyfNG71sytEH/1LvzBj1F
 X5AVQlzEnTsclVlwK3n9ZSm3cKiwgyFnqBLZs+e199FVjJgFGk6WDPplWPo50+hz
 pYImAVPGRbwSGH6cR2M4JSwpU/GJzFAAZf5Y8GrEBQVyhiQeKvYV7UNPjbNt15tv
 qI8Xfa5xbgAFunRC6GsjpaffTmCzHJKYgikchbIKre906IYBLwDHTnxvtXXDgBVe
 Zrd0jFQIrtuHseLQkvaEiVuuzmFibm/atdMUWCyncdunl2fuwTS60hCG+mRd7Pz2
 B85HmcAfS7LTu+dkn4doG4zVHO/JVpYr2eZExb1wxY0Kyz+5KDsESqUCB+7c6wya
 rGqsC+LX8l0MjunxSJbZUcuQLLcaFN9AXJTB6a0gPk+VLZH7EutkG98SonYU07w/
 Wff0bwVQWk3I4QHf0NaS5Ikbcx8KAv1ZaqcUaL2pPYNtIamkiyJdH6f+4C0q+K6n
 aMcLNLzaRcVayHO88SG3XpDLNc+cR2f/rehAkghxXJ8jhdtV5HSb5HLKgu+Lf8q4
 hEBzBQLYeRFJM1TuaZL02aXw
 =r7vf
 -----END PGP SIGNATURE-----

Merge tag '20210927135559.738-6-srinivas.kandagatla@linaro.org' into drivers-for-5.16

v5.15-rc1 + 20210927135559.738-[23456]-srinivas.kandagatla@linaro.org

This immutable branch is based on v5.15-rc1 and contains the following
patches extending the existig APR driver to also implement GPR:
20210927135559.738-2-srinivas.kandagatla@linaro.org
20210927135559.738-3-srinivas.kandagatla@linaro.org
20210927135559.738-4-srinivas.kandagatla@linaro.org
20210927135559.738-5-srinivas.kandagatla@linaro.org
20210927135559.738-6-srinivas.kandagatla@linaro.org
This commit is contained in:
Bjorn Andersson 2021-09-27 22:13:45 -05:00
commit f27591125a
6 changed files with 501 additions and 188 deletions

View File

@ -1,134 +0,0 @@
Qualcomm APR (Asynchronous Packet Router) binding
This binding describes the Qualcomm APR. APR is a IPC protocol for
communication between Application processor and QDSP. APR is mainly
used for audio/voice services on the QDSP.
- compatible:
Usage: required
Value type: <stringlist>
Definition: must be "qcom,apr-v<VERSION-NUMBER>", example "qcom,apr-v2"
- qcom,apr-domain
Usage: required
Value type: <u32>
Definition: Destination processor ID.
Possible values are :
1 - APR simulator
2 - PC
3 - MODEM
4 - ADSP
5 - APPS
6 - MODEM2
7 - APPS2
= APR SERVICES
Each subnode of the APR node represents service tied to this apr. The name
of the nodes are not important. The properties of these nodes are defined
by the individual bindings for the specific service
- All APR services MUST contain the following property:
- reg
Usage: required
Value type: <u32>
Definition: APR Service ID
Possible values are :
3 - DSP Core Service
4 - Audio Front End Service.
5 - Voice Stream Manager Service.
6 - Voice processing manager.
7 - Audio Stream Manager Service.
8 - Audio Device Manager Service.
9 - Multimode voice manager.
10 - Core voice stream.
11 - Core voice processor.
12 - Ultrasound stream manager.
13 - Listen stream manager.
- qcom,protection-domain
Usage: optional
Value type: <stringlist>
Definition: Must list the protection domain service name and path
that the particular apr service has a dependency on.
Possible values are :
"avs/audio", "msm/adsp/audio_pd".
"kernel/elf_loader", "msm/modem/wlan_pd".
"tms/servreg", "msm/adsp/audio_pd".
"tms/servreg", "msm/modem/wlan_pd".
"tms/servreg", "msm/slpi/sensor_pd".
= EXAMPLE
The following example represents a QDSP based sound card on a MSM8996 device
which uses apr as communication between Apps and QDSP.
apr {
compatible = "qcom,apr-v2";
qcom,apr-domain = <APR_DOMAIN_ADSP>;
apr-service@3 {
compatible = "qcom,q6core";
reg = <APR_SVC_ADSP_CORE>;
};
apr-service@4 {
compatible = "qcom,q6afe";
reg = <APR_SVC_AFE>;
dais {
#sound-dai-cells = <1>;
dai@1 {
reg = <HDMI_RX>;
};
};
};
apr-service@7 {
compatible = "qcom,q6asm";
reg = <APR_SVC_ASM>;
...
};
apr-service@8 {
compatible = "qcom,q6adm";
reg = <APR_SVC_ADM>;
...
};
};
= EXAMPLE 2
The following example represents a QDSP based sound card with protection domain
dependencies specified. Here some of the apr services are dependent on services
running on protection domain hosted on ADSP/SLPI remote processors while others
have no such dependency.
apr {
compatible = "qcom,apr-v2";
qcom,glink-channels = "apr_audio_svc";
qcom,apr-domain = <APR_DOMAIN_ADSP>;
apr-service@3 {
compatible = "qcom,q6core";
reg = <APR_SVC_ADSP_CORE>;
};
q6afe: apr-service@4 {
compatible = "qcom,q6afe";
reg = <APR_SVC_AFE>;
qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd";
...
};
q6asm: apr-service@7 {
compatible = "qcom,q6asm";
reg = <APR_SVC_ASM>;
qcom,protection-domain = "tms/servreg", "msm/slpi/sensor_pd";
...
};
q6adm: apr-service@8 {
compatible = "qcom,q6adm";
reg = <APR_SVC_ADM>;
qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd";
...
};
};

View File

@ -0,0 +1,177 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: "http://devicetree.org/schemas/soc/qcom/qcom,apr.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: Qualcomm APR/GPR (Asynchronous/Generic Packet Router) binding
maintainers:
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
description: |
This binding describes the Qualcomm APR/GPR, APR/GPR is a IPC protocol for
communication between Application processor and QDSP. APR/GPR is mainly
used for audio/voice services on the QDSP.
properties:
compatible:
enum:
- qcom,apr-v2
- qcom,gpr
qcom,apr-domain:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [1, 2, 3, 4, 5, 6, 7]
description:
Selects the processor domain for apr
1 = APR simulator
2 = PC Domain
3 = Modem Domain
4 = ADSP Domain
5 = Application processor Domain
6 = Modem2 Domain
7 = Application Processor2 Domain
deprecated: true
qcom,domain:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 1
maximum: 7
description:
Selects the processor domain for apr
1 = APR simulator
2 = PC Domain
3 = Modem Domain
4 = ADSP Domain
5 = Application processor Domain
6 = Modem2 Domain
7 = Application Processor2 Domain
Selects the processor domain for gpr
1 = Modem Domain
2 = Audio DSP Domain
3 = Application Processor Domain
'#address-cells':
const: 1
'#size-cells':
const: 0
#APR/GPR Services
patternProperties:
"^service@[1-9a-d]$":
type: object
description:
APR/GPR node's client devices use subnodes for desired static port services.
properties:
compatible:
enum:
- qcom,q6core
- qcom,q6asm
- qcom,q6afe
- qcom,q6adm
- qcom,q6apm
- qcom,q6prm
reg:
minimum: 1
maximum: 13
description:
APR Service ID
3 = DSP Core Service
4 = Audio Front End Service.
5 = Voice Stream Manager Service.
6 = Voice processing manager.
7 = Audio Stream Manager Service.
8 = Audio Device Manager Service.
9 = Multimode voice manager.
10 = Core voice stream.
11 = Core voice processor.
12 = Ultrasound stream manager.
13 = Listen stream manager.
GPR Service ID
1 = Audio Process Manager Service
2 = Proxy Resource Manager Service.
3 = AMDB Service.
4 = Voice processing manager.
qcom,protection-domain:
$ref: /schemas/types.yaml#/definitions/string-array
description: protection domain service name and path for apr service
possible values are
"avs/audio", "msm/adsp/audio_pd".
"kernel/elf_loader", "msm/modem/wlan_pd".
"tms/servreg", "msm/adsp/audio_pd".
"tms/servreg", "msm/modem/wlan_pd".
"tms/servreg", "msm/slpi/sensor_pd".
'#address-cells':
const: 1
'#size-cells':
const: 0
patternProperties:
"^.*@[0-9a-f]+$":
type: object
description:
Service based devices like clock controllers or digital audio interfaces.
additionalProperties: false
required:
- compatible
- qcom,domain
additionalProperties: false
examples:
- |
#include <dt-bindings/soc/qcom,apr.h>
apr {
compatible = "qcom,apr-v2";
qcom,domain = <APR_DOMAIN_ADSP>;
#address-cells = <1>;
#size-cells = <0>;
q6core: service@3 {
compatible = "qcom,q6core";
reg = <APR_SVC_ADSP_CORE>;
qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd";
};
q6afe: service@4 {
compatible = "qcom,q6afe";
reg = <APR_SVC_AFE>;
qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd";
};
q6asm: service@7 {
compatible = "qcom,q6asm";
reg = <APR_SVC_ASM>;
qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd";
};
q6adm: service@8 {
compatible = "qcom,q6adm";
reg = <APR_SVC_ADM>;
qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd";
};
};
- |
#include <dt-bindings/soc/qcom,gpr.h>
gpr {
compatible = "qcom,gpr";
qcom,domain = <GPR_DOMAIN_ID_ADSP>;
#address-cells = <1>;
#size-cells = <0>;
service@1 {
compatible = "qcom,q6apm";
reg = <GPR_APM_MODULE_IID>;
qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd";
};
};

View File

@ -208,7 +208,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

View File

@ -15,13 +15,23 @@
#include <linux/rpmsg.h>
#include <linux/of.h>
struct apr {
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;
spinlock_t svcs_lock;
spinlock_t rx_lock;
struct idr svcs_idr;
int dest_domain_id;
int type;
struct pdr_handle *pdr;
struct workqueue_struct *rxwq;
struct work_struct rx_work;
@ -44,26 +54,103 @@ struct apr_rx_buf {
*/
int apr_send_pkt(struct apr_device *adev, struct apr_pkt *pkt)
{
struct apr *apr = dev_get_drvdata(adev->dev.parent);
struct packet_router *apr = dev_get_drvdata(adev->dev.parent);
struct apr_hdr *hdr;
unsigned long flags;
int ret;
spin_lock_irqsave(&adev->lock, flags);
spin_lock_irqsave(&adev->svc.lock, flags);
hdr = &pkt->hdr;
hdr->src_domain = APR_DOMAIN_APPS;
hdr->src_svc = adev->svc_id;
hdr->src_svc = adev->svc.id;
hdr->dest_domain = adev->domain_id;
hdr->dest_svc = adev->svc_id;
hdr->dest_svc = adev->svc.id;
ret = rpmsg_trysend(apr->ch, pkt, hdr->pkt_size);
spin_unlock_irqrestore(&adev->lock, flags);
spin_unlock_irqrestore(&adev->svc.lock, flags);
return ret ? ret : hdr->pkt_size;
}
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);
@ -74,7 +161,7 @@ static void apr_dev_release(struct device *dev)
static int apr_callback(struct rpmsg_device *rpdev, void *buf,
int len, void *priv, u32 addr)
{
struct apr *apr = dev_get_drvdata(&rpdev->dev);
struct packet_router *apr = dev_get_drvdata(&rpdev->dev);
struct apr_rx_buf *abuf;
unsigned long flags;
@ -100,11 +187,11 @@ static int apr_callback(struct rpmsg_device *rpdev, void *buf,
return 0;
}
static int apr_do_rx_callback(struct apr *apr, struct apr_rx_buf *abuf)
static int apr_do_rx_callback(struct packet_router *apr, struct apr_rx_buf *abuf)
{
uint16_t hdr_size, msg_type, ver, svc_id;
struct apr_device *svc = NULL;
struct pkt_router_svc *svc;
struct apr_device *adev;
struct apr_driver *adrv = NULL;
struct apr_resp_pkt resp;
struct apr_hdr *hdr;
@ -145,12 +232,15 @@ static int apr_do_rx_callback(struct apr *apr, struct apr_rx_buf *abuf)
svc_id = hdr->dest_svc;
spin_lock_irqsave(&apr->svcs_lock, flags);
svc = idr_find(&apr->svcs_idr, svc_id);
if (svc && svc->dev.driver)
adrv = to_apr_driver(svc->dev.driver);
if (svc && svc->dev->driver) {
adev = svc_to_apr_device(svc);
adrv = to_apr_driver(adev->dev.driver);
}
spin_unlock_irqrestore(&apr->svcs_lock, flags);
if (!adrv) {
dev_err(apr->dev, "APR: service is not registered\n");
if (!adrv || !adev) {
dev_err(apr->dev, "APR: service is not registered (%d)\n",
svc_id);
return -EINVAL;
}
@ -164,20 +254,82 @@ static int apr_do_rx_callback(struct apr *apr, struct apr_rx_buf *abuf)
if (resp.payload_size > 0)
resp.payload = buf + hdr_size;
adrv->callback(svc, &resp);
adrv->callback(adev, &resp);
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 apr *apr = container_of(work, struct apr, rx_work);
struct packet_router *apr = container_of(work, struct packet_router, rx_work);
struct apr_rx_buf *abuf, *b;
unsigned long flags;
if (!list_empty(&apr->rx_list)) {
list_for_each_entry_safe(abuf, b, &apr->rx_list, node) {
apr_do_rx_callback(apr, abuf);
switch (apr->type) {
case PR_TYPE_APR:
apr_do_rx_callback(apr, abuf);
break;
case PR_TYPE_GPR:
gpr_do_rx_callback(apr, abuf);
break;
default:
break;
}
spin_lock_irqsave(&apr->rx_lock, flags);
list_del(&abuf->node);
spin_unlock_irqrestore(&apr->rx_lock, flags);
@ -201,7 +353,7 @@ static int apr_device_match(struct device *dev, struct device_driver *drv)
while (id->domain_id != 0 || id->svc_id != 0) {
if (id->domain_id == adev->domain_id &&
id->svc_id == adev->svc_id)
id->svc_id == adev->svc.id)
return 1;
id++;
}
@ -213,22 +365,27 @@ 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)
{
struct apr_device *adev = to_apr_device(dev);
struct apr_driver *adrv;
struct apr *apr = dev_get_drvdata(adev->dev.parent);
struct packet_router *apr = dev_get_drvdata(adev->dev.parent);
if (dev->driver) {
adrv = to_apr_driver(dev->driver);
if (adrv->remove)
adrv->remove(adev);
spin_lock(&apr->svcs_lock);
idr_remove(&apr->svcs_idr, adev->svc_id);
idr_remove(&apr->svcs_idr, adev->svc.id);
spin_unlock(&apr->svcs_lock);
}
}
@ -255,28 +412,43 @@ struct bus_type aprbus = {
EXPORT_SYMBOL_GPL(aprbus);
static int apr_add_device(struct device *dev, struct device_node *np,
const struct apr_device_id *id)
u32 svc_id, u32 domain_id)
{
struct apr *apr = dev_get_drvdata(dev);
struct packet_router *apr = dev_get_drvdata(dev);
struct apr_device *adev = NULL;
struct pkt_router_svc *svc;
int ret;
adev = kzalloc(sizeof(*adev), GFP_KERNEL);
if (!adev)
return -ENOMEM;
spin_lock_init(&adev->lock);
adev->svc_id = svc_id;
svc = &adev->svc;
svc->id = svc_id;
svc->pr = apr;
svc->priv = adev;
svc->dev = dev;
spin_lock_init(&svc->lock);
adev->domain_id = domain_id;
adev->svc_id = id->svc_id;
adev->domain_id = id->domain_id;
adev->version = id->svc_version;
if (np)
snprintf(adev->name, APR_NAME_SIZE, "%pOFn", np);
else
strscpy(adev->name, id->name, APR_NAME_SIZE);
dev_set_name(&adev->dev, "aprsvc:%s:%x:%x", adev->name,
id->domain_id, id->svc_id);
switch (apr->type) {
case PR_TYPE_APR:
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;
}
adev->dev.bus = &aprbus;
adev->dev.parent = dev;
@ -285,14 +457,13 @@ static int apr_add_device(struct device *dev, struct device_node *np,
adev->dev.driver = NULL;
spin_lock(&apr->svcs_lock);
idr_alloc(&apr->svcs_idr, adev, id->svc_id,
id->svc_id + 1, GFP_ATOMIC);
idr_alloc(&apr->svcs_idr, svc, svc_id, svc_id + 1, GFP_ATOMIC);
spin_unlock(&apr->svcs_lock);
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) {
@ -306,7 +477,7 @@ static int apr_add_device(struct device *dev, struct device_node *np,
static int of_apr_add_pd_lookups(struct device *dev)
{
const char *service_name, *service_path;
struct apr *apr = dev_get_drvdata(dev);
struct packet_router *apr = dev_get_drvdata(dev);
struct device_node *node;
struct pdr_service *pds;
int ret;
@ -336,13 +507,14 @@ static int of_apr_add_pd_lookups(struct device *dev)
static void of_register_apr_devices(struct device *dev, const char *svc_path)
{
struct apr *apr = dev_get_drvdata(dev);
struct packet_router *apr = dev_get_drvdata(dev);
struct device_node *node;
const char *service_path;
int ret;
for_each_child_of_node(dev->of_node, node) {
struct apr_device_id id = { {0} };
u32 svc_id;
u32 domain_id;
/*
* This function is called with svc_path NULL during
@ -372,13 +544,13 @@ static void of_register_apr_devices(struct device *dev, const char *svc_path)
continue;
}
if (of_property_read_u32(node, "reg", &id.svc_id))
if (of_property_read_u32(node, "reg", &svc_id))
continue;
id.domain_id = apr->dest_domain_id;
domain_id = apr->dest_domain_id;
if (apr_add_device(dev, node, &id))
dev_err(dev, "Failed to add apr %d svc\n", id.svc_id);
if (apr_add_device(dev, node, svc_id, domain_id))
dev_err(dev, "Failed to add apr %d svc\n", svc_id);
}
}
@ -398,7 +570,7 @@ static int apr_remove_device(struct device *dev, void *svc_path)
static void apr_pd_status(int state, char *svc_path, void *priv)
{
struct apr *apr = (struct apr *)priv;
struct packet_router *apr = (struct packet_router *)priv;
switch (state) {
case SERVREG_SERVICE_STATE_UP:
@ -413,16 +585,26 @@ static void apr_pd_status(int state, char *svc_path, void *priv)
static int apr_probe(struct rpmsg_device *rpdev)
{
struct device *dev = &rpdev->dev;
struct apr *apr;
struct packet_router *apr;
int ret;
apr = devm_kzalloc(dev, sizeof(*apr), GFP_KERNEL);
if (!apr)
return -ENOMEM;
ret = of_property_read_u32(dev->of_node, "qcom,apr-domain", &apr->dest_domain_id);
ret = of_property_read_u32(dev->of_node, "qcom,domain", &apr->dest_domain_id);
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, "APR Domain ID not specified in DT\n");
dev_err(dev, "Domain ID not specified in DT\n");
return ret;
}
@ -465,7 +647,7 @@ destroy_wq:
static void apr_remove(struct rpmsg_device *rpdev)
{
struct apr *apr = dev_get_drvdata(&rpdev->dev);
struct packet_router *apr = dev_get_drvdata(&rpdev->dev);
pdr_handle_release(apr->pdr);
device_for_each_child(&rpdev->dev, NULL, apr_remove_device);
@ -502,20 +684,21 @@ void apr_driver_unregister(struct apr_driver *drv)
}
EXPORT_SYMBOL_GPL(apr_driver_unregister);
static const struct of_device_id apr_of_match[] = {
static const struct of_device_id pkt_router_of_match[] = {
{ .compatible = "qcom,apr"},
{ .compatible = "qcom,apr-v2"},
{ .compatible = "qcom,gpr"},
{}
};
MODULE_DEVICE_TABLE(of, apr_of_match);
MODULE_DEVICE_TABLE(of, pkt_router_of_match);
static struct rpmsg_driver apr_driver = {
static struct rpmsg_driver packet_router_driver = {
.probe = apr_probe,
.remove = apr_remove,
.callback = apr_callback,
.drv = {
.name = "qcom,apr",
.of_match_table = apr_of_match,
.of_match_table = pkt_router_of_match,
},
};
@ -525,7 +708,7 @@ static int __init apr_init(void)
ret = bus_register(&aprbus);
if (!ret)
ret = register_rpmsg_driver(&apr_driver);
ret = register_rpmsg_driver(&packet_router_driver);
else
bus_unregister(&aprbus);
@ -535,7 +718,7 @@ static int __init apr_init(void)
static void __exit apr_exit(void)
{
bus_unregister(&aprbus);
unregister_rpmsg_driver(&apr_driver);
unregister_rpmsg_driver(&packet_router_driver);
}
subsys_initcall(apr_init);

View File

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
#ifndef __DT_BINDINGS_QCOM_GPR_H
#define __DT_BINDINGS_QCOM_GPR_H
/* DOMAINS */
#define GPR_DOMAIN_ID_MODEM 1
#define GPR_DOMAIN_ID_ADSP 2
#define GPR_DOMAIN_ID_APPS 3
/* Static Services */
#define GPR_APM_MODULE_IID 1
#define GPR_PRM_MODULE_IID 2
#define GPR_AMDB_MODULE_IID 3
#define GPR_VCPM_MODULE_IID 4
#endif /* __DT_BINDINGS_QCOM_GPR_H */

View File

@ -7,6 +7,7 @@
#include <linux/device.h>
#include <linux/mod_devicetable.h>
#include <dt-bindings/soc/qcom,apr.h>
#include <dt-bindings/soc/qcom,gpr.h>
extern struct bus_type aprbus;
@ -75,10 +76,65 @@ struct apr_resp_pkt {
int payload_size;
};
struct gpr_hdr {
uint32_t version:4;
uint32_t hdr_size:4;
uint32_t pkt_size:24;
uint32_t dest_domain:8;
uint32_t src_domain:8;
uint32_t reserved:16;
uint32_t src_port;
uint32_t dest_port;
uint32_t token;
uint32_t opcode;
} __packed;
struct gpr_pkt {
struct gpr_hdr hdr;
uint32_t payload[];
};
struct gpr_resp_pkt {
struct gpr_hdr hdr;
void *payload;
int payload_size;
};
#define GPR_HDR_SIZE sizeof(struct gpr_hdr)
#define GPR_PKT_VER 0x0
#define GPR_PKT_HEADER_WORD_SIZE ((sizeof(struct gpr_pkt) + 3) >> 2)
#define GPR_PKT_HEADER_BYTE_SIZE (GPR_PKT_HEADER_WORD_SIZE << 2)
#define GPR_BASIC_RSP_RESULT 0x02001005
struct gpr_ibasic_rsp_result_t {
uint32_t opcode;
uint32_t status;
};
#define GPR_BASIC_EVT_ACCEPTED 0x02001006
struct gpr_ibasic_rsp_accepted_t {
uint32_t opcode;
};
/* Bits 0 to 15 -- Minor version, Bits 16 to 31 -- Major version */
#define APR_SVC_MAJOR_VERSION(v) ((v >> 16) & 0xFF)
#define APR_SVC_MINOR_VERSION(v) (v & 0xFF)
typedef int (*gpr_port_cb) (struct gpr_resp_pkt *d, void *priv, int op);
struct packet_router;
struct pkt_router_svc {
struct device *dev;
gpr_port_cb callback;
struct packet_router *pr;
spinlock_t lock;
int id;
void *priv;
};
typedef struct pkt_router_svc gpr_port_t;
struct apr_device {
struct device dev;
uint16_t svc_id;
@ -86,21 +142,26 @@ struct apr_device {
uint32_t version;
char name[APR_NAME_SIZE];
const char *service_path;
spinlock_t lock;
struct pkt_router_svc svc;
struct list_head node;
};
typedef struct apr_device gpr_device_t;
#define to_apr_device(d) container_of(d, struct apr_device, dev)
#define svc_to_apr_device(d) container_of(d, struct apr_device, svc)
struct apr_driver {
int (*probe)(struct apr_device *sl);
int (*remove)(struct apr_device *sl);
int (*callback)(struct apr_device *a,
struct apr_resp_pkt *d);
int (*gpr_callback)(struct gpr_resp_pkt *d, void *data, int op);
struct device_driver driver;
const struct apr_device_id *id_table;
};
typedef struct apr_driver gpr_driver_t;
#define to_apr_driver(d) container_of(d, struct apr_driver, driver)
/*
@ -123,7 +184,14 @@ void apr_driver_unregister(struct apr_driver *drv);
#define module_apr_driver(__apr_driver) \
module_driver(__apr_driver, apr_driver_register, \
apr_driver_unregister)
#define module_gpr_driver(__gpr_driver) module_apr_driver(__gpr_driver)
int apr_send_pkt(struct apr_device *adev, struct apr_pkt *pkt);
gpr_port_t *gpr_alloc_port(gpr_device_t *gdev, struct device *dev,
gpr_port_cb cb, void *priv);
void gpr_free_port(gpr_port_t *port);
int gpr_send_port_pkt(gpr_port_t *port, struct gpr_pkt *pkt);
int gpr_send_pkt(gpr_device_t *gdev, struct gpr_pkt *pkt);
#endif /* __QCOM_APR_H_ */