Commit 8f10e1ab authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

Merge tag 'qcom-drivers-for-5.7' of...

Merge tag 'qcom-drivers-for-5.7' of git://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux into arm/drivers

Qualcomm driver updates for v5.7

This adds a new library for subscribing to notifications about
protection domains being stated and stopped and the integration of this
with the APR driver. It also contains fixes and cleanups for AOSS
driver, socinfo and rpmh.

* tag 'qcom-drivers-for-5.7' of git://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux:
  soc: qcom: Fix QCOM_APR dependencies
  soc: qcom: pdr: Avoid uninitialized use of found in pdr_indication_cb
  soc: qcom: apr: Add avs/audio tracking functionality
  dt-bindings: soc: qcom: apr: Add protection domain bindings
  soc: qcom: Introduce Protection Domain Restart helpers
  devicetree: bindings: firmware: add ipq806x to qcom_scm
  soc: qcom: socinfo: Use seq_putc() if possible
  drivers: qcom: rpmh-rsc: Use rcuidle tracepoints for rpmh
  soc: qcom: Do not depend on ARCH_QCOM for QMI helpers
  soc: qcom: aoss: Read back before triggering the IRQ
  soc: qcom: aoss: Use wake_up_all() instead of wake_up_interruptible_all()
  drivers: qcom: rpmh: remove rpmh_flush export
  drivers: qcom: rpmh: fix macro to accept NULL argument

Link: https://lore.kernel.org/r/20200318044236.GD470201@yoga


Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 993330e0 de722e41
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ Required properties:
 * "qcom,scm-apq8064"
 * "qcom,scm-apq8084"
 * "qcom,scm-ipq4019"
 * "qcom,scm-ipq806x"
 * "qcom,scm-msm8660"
 * "qcom,scm-msm8916"
 * "qcom,scm-msm8960"
+50 −0
Original line number Diff line number Diff line
@@ -45,6 +45,18 @@ by the individual bindings for the specific service
			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.
@@ -82,3 +94,41 @@ which uses apr as communication between Apps and QDSP.
			...
		};
	};

= 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>;

		q6core {
			compatible = "qcom,q6core";
			reg = <APR_SVC_ADSP_CORE>;
		};

		q6afe: q6afe {
			compatible = "qcom,q6afe";
			reg = <APR_SVC_AFE>;
			qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd";
			...
		};

		q6asm: q6asm {
			compatible = "qcom,q6asm";
			reg = <APR_SVC_ASM>;
			qcom,protection-domain = "tms/servreg", "msm/slpi/sensor_pd";
			...
		};

		q6adm: q6adm {
			compatible = "qcom,q6adm";
			reg = <APR_SVC_ADM>;
			qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd";
			...
		};
	};
+6 −1
Original line number Diff line number Diff line
@@ -76,6 +76,10 @@ config QCOM_OCMEM
	  requirements. This is typically used by the GPU, camera/video, and
	  audio components on some Snapdragon SoCs.

config QCOM_PDR_HELPERS
	tristate
	select QCOM_QMI_HELPERS

config QCOM_PM
	bool "Qualcomm Power Management"
	depends on ARCH_QCOM && !ARM64
@@ -88,7 +92,6 @@ config QCOM_PM

config QCOM_QMI_HELPERS
	tristate
	depends on ARCH_QCOM || COMPILE_TEST
	depends on NET

config QCOM_RMTFS_MEM
@@ -197,6 +200,8 @@ config QCOM_APR
	tristate "Qualcomm APR Bus (Asynchronous Packet Router)"
	depends on ARCH_QCOM || COMPILE_TEST
	depends on RPMSG
	depends on NET
	select QCOM_PDR_HELPERS
	help
	  Enable APR IPC protocol support between
	  application processor and QDSP6. APR is
+1 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o
obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
obj-$(CONFIG_QCOM_MDT_LOADER)	+= mdt_loader.o
obj-$(CONFIG_QCOM_OCMEM)	+= ocmem.o
obj-$(CONFIG_QCOM_PDR_HELPERS)	+= pdr_interface.o
obj-$(CONFIG_QCOM_PM)	+=	spm.o
obj-$(CONFIG_QCOM_QMI_HELPERS)	+= qmi_helpers.o
qmi_helpers-y	+= qmi_encdec.o qmi_interface.o
+114 −9
Original line number Diff line number Diff line
@@ -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);

	return 0;
}

static int apr_remove_device(struct device *dev, void *null)
{
	struct apr_device *adev = to_apr_device(dev);
	ret = of_apr_add_pd_lookups(dev);
	if (ret)
		goto handle_release;

	device_unregister(&adev->dev);
	of_register_apr_devices(dev, NULL);

	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);
Loading