soc: qcom: apr: Add avs/audio tracking functionality
Use PDR helper functions to track the protection domains that the apr services are dependent upon on SDM845 SoC, specifically the "avs/audio" service running on ADSP Q6. Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org> Signed-off-by: Sibi Sankar <sibis@codeaurora.org> Link: https://lore.kernel.org/r/20200312120842.21991-4-sibis@codeaurora.org Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
This commit is contained in:
Родитель
a03a5b6313
Коммит
8347356626
|
@ -200,6 +200,7 @@ config QCOM_APR
|
|||
tristate "Qualcomm APR Bus (Asynchronous Packet Router)"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
depends on RPMSG
|
||||
select QCOM_PDR_HELPERS
|
||||
help
|
||||
Enable APR IPC protocol support between
|
||||
application processor and QDSP6. APR is
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/workqueue.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/soc/qcom/apr.h>
|
||||
#include <linux/soc/qcom/pdr.h>
|
||||
#include <linux/rpmsg.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
|
@ -21,6 +22,7 @@ struct apr {
|
|||
spinlock_t rx_lock;
|
||||
struct idr svcs_idr;
|
||||
int dest_domain_id;
|
||||
struct pdr_handle *pdr;
|
||||
struct workqueue_struct *rxwq;
|
||||
struct work_struct rx_work;
|
||||
struct list_head rx_list;
|
||||
|
@ -289,6 +291,9 @@ static int apr_add_device(struct device *dev, struct device_node *np,
|
|||
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));
|
||||
|
||||
ret = device_register(&adev->dev);
|
||||
|
@ -300,14 +305,75 @@ static int apr_add_device(struct device *dev, struct device_node *np,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void of_register_apr_devices(struct device *dev)
|
||||
static int of_apr_add_pd_lookups(struct device *dev)
|
||||
{
|
||||
const char *service_name, *service_path;
|
||||
struct apr *apr = dev_get_drvdata(dev);
|
||||
struct device_node *node;
|
||||
struct pdr_service *pds;
|
||||
int ret;
|
||||
|
||||
for_each_child_of_node(dev->of_node, node) {
|
||||
ret = of_property_read_string_index(node, "qcom,protection-domain",
|
||||
0, &service_name);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
|
||||
ret = of_property_read_string_index(node, "qcom,protection-domain",
|
||||
1, &service_path);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "pdr service path missing: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pds = pdr_add_lookup(apr->pdr, service_name, service_path);
|
||||
if (IS_ERR(pds) && PTR_ERR(pds) != -EALREADY) {
|
||||
dev_err(dev, "pdr add lookup failed: %d\n", ret);
|
||||
return PTR_ERR(pds);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void of_register_apr_devices(struct device *dev, const char *svc_path)
|
||||
{
|
||||
struct apr *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} };
|
||||
|
||||
/*
|
||||
* This function is called with svc_path NULL during
|
||||
* apr_probe(), in which case we register any apr devices
|
||||
* without a qcom,protection-domain specified.
|
||||
*
|
||||
* Then as the protection domains becomes available
|
||||
* (if applicable) this function is again called, but with
|
||||
* svc_path representing the service becoming available. In
|
||||
* this case we register any apr devices with a matching
|
||||
* qcom,protection-domain.
|
||||
*/
|
||||
|
||||
ret = of_property_read_string_index(node, "qcom,protection-domain",
|
||||
1, &service_path);
|
||||
if (svc_path) {
|
||||
/* skip APR services that are PD independent */
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
/* skip APR services whose PD paths don't match */
|
||||
if (strcmp(service_path, svc_path))
|
||||
continue;
|
||||
} else {
|
||||
/* skip APR services whose PD lookups are registered */
|
||||
if (ret == 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(node, "reg", &id.svc_id))
|
||||
continue;
|
||||
|
||||
|
@ -318,6 +384,34 @@ static void of_register_apr_devices(struct device *dev)
|
|||
}
|
||||
}
|
||||
|
||||
static int apr_remove_device(struct device *dev, void *svc_path)
|
||||
{
|
||||
struct apr_device *adev = to_apr_device(dev);
|
||||
|
||||
if (svc_path && adev->service_path) {
|
||||
if (!strcmp(adev->service_path, (char *)svc_path))
|
||||
device_unregister(&adev->dev);
|
||||
} else {
|
||||
device_unregister(&adev->dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void apr_pd_status(int state, char *svc_path, void *priv)
|
||||
{
|
||||
struct apr *apr = (struct apr *)priv;
|
||||
|
||||
switch (state) {
|
||||
case SERVREG_SERVICE_STATE_UP:
|
||||
of_register_apr_devices(apr->dev, svc_path);
|
||||
break;
|
||||
case SERVREG_SERVICE_STATE_DOWN:
|
||||
device_for_each_child(apr->dev, svc_path, apr_remove_device);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int apr_probe(struct rpmsg_device *rpdev)
|
||||
{
|
||||
struct device *dev = &rpdev->dev;
|
||||
|
@ -343,28 +437,39 @@ static int apr_probe(struct rpmsg_device *rpdev)
|
|||
return -ENOMEM;
|
||||
}
|
||||
INIT_WORK(&apr->rx_work, apr_rxwq);
|
||||
|
||||
apr->pdr = pdr_handle_alloc(apr_pd_status, apr);
|
||||
if (IS_ERR(apr->pdr)) {
|
||||
dev_err(dev, "Failed to init PDR handle\n");
|
||||
ret = PTR_ERR(apr->pdr);
|
||||
goto destroy_wq;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&apr->rx_list);
|
||||
spin_lock_init(&apr->rx_lock);
|
||||
spin_lock_init(&apr->svcs_lock);
|
||||
idr_init(&apr->svcs_idr);
|
||||
of_register_apr_devices(dev);
|
||||
|
||||
ret = of_apr_add_pd_lookups(dev);
|
||||
if (ret)
|
||||
goto handle_release;
|
||||
|
||||
of_register_apr_devices(dev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apr_remove_device(struct device *dev, void *null)
|
||||
{
|
||||
struct apr_device *adev = to_apr_device(dev);
|
||||
|
||||
device_unregister(&adev->dev);
|
||||
|
||||
return 0;
|
||||
handle_release:
|
||||
pdr_handle_release(apr->pdr);
|
||||
destroy_wq:
|
||||
destroy_workqueue(apr->rxwq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void apr_remove(struct rpmsg_device *rpdev)
|
||||
{
|
||||
struct apr *apr = dev_get_drvdata(&rpdev->dev);
|
||||
|
||||
pdr_handle_release(apr->pdr);
|
||||
device_for_each_child(&rpdev->dev, NULL, apr_remove_device);
|
||||
flush_workqueue(apr->rxwq);
|
||||
destroy_workqueue(apr->rxwq);
|
||||
|
|
|
@ -85,6 +85,7 @@ struct apr_device {
|
|||
uint16_t domain_id;
|
||||
uint32_t version;
|
||||
char name[APR_NAME_SIZE];
|
||||
const char *service_path;
|
||||
spinlock_t lock;
|
||||
struct list_head node;
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче