All of lore.kernel.org
 help / color / mirror / Atom feed
* [GIT PULL 00/22] intel_th: Updates for v5.2
@ 2019-05-03  8:44 Alexander Shishkin
  2019-05-03  8:44 ` [GIT PULL 01/22] intel_th: msu: Fix single mode with IOMMU Alexander Shishkin
                   ` (21 more replies)
  0 siblings, 22 replies; 26+ messages in thread
From: Alexander Shishkin @ 2019-05-03  8:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, Alexander Shishkin

Hi Greg,

Here are the updates that I have for v5.2. The main item on the list is
the support for software trace sinks that I call "buffer drivers". The
purpose is to create a software path for the trace data coming out of
the TH that would allow exporting it via a USB gadget or ethernet or
some such. Most of the patches are reworks in the MSU code to enable
this, including interrupt handling and resource passing between glue
layers and the driver core. An example sink driver is included. Also,
there's a bugfix that's backportable all the way to stable 4.4.

These are all, as usual, tested with aiaiai for bisectability, cppcheck,
sparse, smatch, coccinelle and checkpatch errors.

Signed tag is at the repo below. Individual patches follow. Please
consider pulling or applying. Apologies for late request.

The following changes since commit 9e98c678c2d6ae3a17cb2de55d17f69dddaa231b:

  Linux 5.1-rc1 (2019-03-17 14:22:26 -0700)

are available in the Git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/ash/stm.git tags/intel_th-for-greg-20190503

for you to fetch changes up to c0e8a65abd2e4ad47e5257de6bde1822e9c458b6:

  intel_th: msu: Preserve pre-existing buffer configuration (2019-05-03 11:35:46 +0300)

----------------------------------------------------------------
intel_th: Updates for v5.2

These are:
  * Support for software trace sinks
  * Interrupt handling, MSI support
  * Reworks in resource passing between glue layers and driver core
  * Reworks in buffer management code
  * Various small reworks in the MSU code
  * Support for window switching in MSU "multi" mode
  * A fix for the MSU "single" mode, backportable all the way to v4.4
  * A new "rtit" subdevice

----------------------------------------------------------------
Alexander Shishkin (22):
      intel_th: msu: Fix single mode with IOMMU
      intel_th: SPDX-ify the documentation
      intel_th: Rework resource passing between glue layers and core
      intel_th: Skip subdevices if their MMIO is missing
      intel_th: Add "rtit" source device
      intel_th: Communicate IRQ via resource
      intel_th: pci: Use MSI interrupt signalling
      intel_th: msu: Start handling IRQs
      intel_th: Only report useful IRQs to subdevices
      intel_th: msu: Replace open-coded list_{first,last,next}_entry variants
      intel_th: msu: Switch over to scatterlist
      intel_th: msu: Support multipage blocks
      intel_th: msu: Factor out pipeline draining
      intel_th: gth: Factor out trace start/stop
      intel_th: Add switch triggering support
      intel_th: msu: Correct the block wrap detection
      intel_th: msu: Add a sysfs attribute to trigger window switch
      intel_th: msu: Add current window tracking
      intel_th: msu: Introduce buffer driver interface
      intel_th: msu: Add a sysfs attribute showing possible modes
      intel_th: msu-sink: An example msu buffer driver
      intel_th: msu: Preserve pre-existing buffer configuration

 .../ABI/testing/sysfs-bus-intel_th-devices-msc     |  19 +-
 Documentation/trace/intel_th.rst                   |   2 +
 MAINTAINERS                                        |   1 +
 drivers/hwtracing/intel_th/Makefile                |   3 +
 drivers/hwtracing/intel_th/acpi.c                  |  10 +-
 drivers/hwtracing/intel_th/core.c                  | 139 +++-
 drivers/hwtracing/intel_th/gth.c                   | 125 ++-
 drivers/hwtracing/intel_th/gth.h                   |  19 +
 drivers/hwtracing/intel_th/intel_th.h              |  30 +-
 drivers/hwtracing/intel_th/msu-sink.c              | 137 ++++
 drivers/hwtracing/intel_th/msu.c                   | 864 +++++++++++++++++----
 drivers/hwtracing/intel_th/msu.h                   |  31 +-
 drivers/hwtracing/intel_th/pci.c                   |  32 +-
 include/linux/intel_th.h                           |  67 ++
 14 files changed, 1270 insertions(+), 209 deletions(-)
 create mode 100644 drivers/hwtracing/intel_th/msu-sink.c
 create mode 100644 include/linux/intel_th.h

-- 
2.20.1


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [GIT PULL 01/22] intel_th: msu: Fix single mode with IOMMU
  2019-05-03  8:44 [GIT PULL 00/22] intel_th: Updates for v5.2 Alexander Shishkin
@ 2019-05-03  8:44 ` Alexander Shishkin
  2019-05-03  8:44 ` [GIT PULL 02/22] intel_th: SPDX-ify the documentation Alexander Shishkin
                   ` (20 subsequent siblings)
  21 siblings, 0 replies; 26+ messages in thread
From: Alexander Shishkin @ 2019-05-03  8:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, Alexander Shishkin, stable

Currently, the pages that are allocated for the single mode of MSC are not
mapped into the device's dma space and the code is incorrectly using
*_to_phys() in place of a dma address. This fails with IOMMU enabled and
is otherwise bad practice.

Fix the single mode buffer allocation to map the pages into the device's
DMA space.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Fixes: ba82664c134e ("intel_th: Add Memory Storage Unit driver")
Cc: stable@vger.kernel.org # v4.4+
---
 drivers/hwtracing/intel_th/msu.c | 35 +++++++++++++++++++++++++++++---
 1 file changed, 32 insertions(+), 3 deletions(-)

diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c
index ba7aaf421f36..8ff326c0c406 100644
--- a/drivers/hwtracing/intel_th/msu.c
+++ b/drivers/hwtracing/intel_th/msu.c
@@ -84,6 +84,7 @@ struct msc_iter {
  * @reg_base:		register window base address
  * @thdev:		intel_th_device pointer
  * @win_list:		list of windows in multiblock mode
+ * @single_sgt:		single mode buffer
  * @nr_pages:		total number of pages allocated for this buffer
  * @single_sz:		amount of data in single mode
  * @single_wrap:	single mode wrap occurred
@@ -104,6 +105,7 @@ struct msc {
 	struct intel_th_device	*thdev;
 
 	struct list_head	win_list;
+	struct sg_table		single_sgt;
 	unsigned long		nr_pages;
 	unsigned long		single_sz;
 	unsigned int		single_wrap : 1;
@@ -617,22 +619,45 @@ static void intel_th_msc_deactivate(struct intel_th_device *thdev)
  */
 static int msc_buffer_contig_alloc(struct msc *msc, unsigned long size)
 {
+	unsigned long nr_pages = size >> PAGE_SHIFT;
 	unsigned int order = get_order(size);
 	struct page *page;
+	int ret;
 
 	if (!size)
 		return 0;
 
+	ret = sg_alloc_table(&msc->single_sgt, 1, GFP_KERNEL);
+	if (ret)
+		goto err_out;
+
+	ret = -ENOMEM;
 	page = alloc_pages(GFP_KERNEL | __GFP_ZERO, order);
 	if (!page)
-		return -ENOMEM;
+		goto err_free_sgt;
 
 	split_page(page, order);
-	msc->nr_pages = size >> PAGE_SHIFT;
+	sg_set_buf(msc->single_sgt.sgl, page_address(page), size);
+
+	ret = dma_map_sg(msc_dev(msc)->parent->parent, msc->single_sgt.sgl, 1,
+			 DMA_FROM_DEVICE);
+	if (ret < 0)
+		goto err_free_pages;
+
+	msc->nr_pages = nr_pages;
 	msc->base = page_address(page);
-	msc->base_addr = page_to_phys(page);
+	msc->base_addr = sg_dma_address(msc->single_sgt.sgl);
 
 	return 0;
+
+err_free_pages:
+	__free_pages(page, order);
+
+err_free_sgt:
+	sg_free_table(&msc->single_sgt);
+
+err_out:
+	return ret;
 }
 
 /**
@@ -643,6 +668,10 @@ static void msc_buffer_contig_free(struct msc *msc)
 {
 	unsigned long off;
 
+	dma_unmap_sg(msc_dev(msc)->parent->parent, msc->single_sgt.sgl,
+		     1, DMA_FROM_DEVICE);
+	sg_free_table(&msc->single_sgt);
+
 	for (off = 0; off < msc->nr_pages << PAGE_SHIFT; off += PAGE_SIZE) {
 		struct page *page = virt_to_page(msc->base + off);
 
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [GIT PULL 02/22] intel_th: SPDX-ify the documentation
  2019-05-03  8:44 [GIT PULL 00/22] intel_th: Updates for v5.2 Alexander Shishkin
  2019-05-03  8:44 ` [GIT PULL 01/22] intel_th: msu: Fix single mode with IOMMU Alexander Shishkin
@ 2019-05-03  8:44 ` Alexander Shishkin
  2019-05-03  8:44 ` [GIT PULL 03/22] intel_th: Rework resource passing between glue layers and core Alexander Shishkin
                   ` (19 subsequent siblings)
  21 siblings, 0 replies; 26+ messages in thread
From: Alexander Shishkin @ 2019-05-03  8:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, Alexander Shishkin, Mathieu Poirier

Add the SPDX header to the Intel TH documentation.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Tested-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 Documentation/trace/intel_th.rst | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/trace/intel_th.rst b/Documentation/trace/intel_th.rst
index 19e2d633f3c7..baa12eb09ef4 100644
--- a/Documentation/trace/intel_th.rst
+++ b/Documentation/trace/intel_th.rst
@@ -1,3 +1,5 @@
+.. SPDX-License-Identifier: GPL-2.0
+
 =======================
 Intel(R) Trace Hub (TH)
 =======================
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [GIT PULL 03/22] intel_th: Rework resource passing between glue layers and core
  2019-05-03  8:44 [GIT PULL 00/22] intel_th: Updates for v5.2 Alexander Shishkin
  2019-05-03  8:44 ` [GIT PULL 01/22] intel_th: msu: Fix single mode with IOMMU Alexander Shishkin
  2019-05-03  8:44 ` [GIT PULL 02/22] intel_th: SPDX-ify the documentation Alexander Shishkin
@ 2019-05-03  8:44 ` Alexander Shishkin
  2019-05-03  8:44 ` [GIT PULL 04/22] intel_th: Skip subdevices if their MMIO is missing Alexander Shishkin
                   ` (18 subsequent siblings)
  21 siblings, 0 replies; 26+ messages in thread
From: Alexander Shishkin @ 2019-05-03  8:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, Alexander Shishkin

Currently, MMIO resource numbers in the TH driver core correspond to
PCI BAR numbers, because in the beginning there was only the PCI glue
layer. This created some confusion when the ACPI glue layer was added.

To avoid confusion and remove glue-specific code from the driver core,
split the resource indices between core and glue layers and change the
API so that the driver core receives the MMIO resources in the same
fixed order. At the same time, make the IRQ always be a parameter to
intel_th_alloc() instead of sometimes passing it as a resource.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
 drivers/hwtracing/intel_th/acpi.c     | 12 ++++++++++--
 drivers/hwtracing/intel_th/core.c     | 27 +++++++--------------------
 drivers/hwtracing/intel_th/intel_th.h |  8 ++++----
 drivers/hwtracing/intel_th/pci.c      | 15 ++++++++++++---
 4 files changed, 33 insertions(+), 29 deletions(-)

diff --git a/drivers/hwtracing/intel_th/acpi.c b/drivers/hwtracing/intel_th/acpi.c
index 87bc3744755f..b528e5b113ff 100644
--- a/drivers/hwtracing/intel_th/acpi.c
+++ b/drivers/hwtracing/intel_th/acpi.c
@@ -37,15 +37,23 @@ MODULE_DEVICE_TABLE(acpi, intel_th_acpi_ids);
 static int intel_th_acpi_probe(struct platform_device *pdev)
 {
 	struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
+	struct resource resource[TH_MMIO_END];
 	const struct acpi_device_id *id;
 	struct intel_th *th;
+	int i, r, irq = -1;
 
 	id = acpi_match_device(intel_th_acpi_ids, &pdev->dev);
 	if (!id)
 		return -ENODEV;
 
-	th = intel_th_alloc(&pdev->dev, (void *)id->driver_data,
-			    pdev->resource, pdev->num_resources, -1);
+	for (i = 0, r = 0; i < pdev->num_resources && r < TH_MMIO_END; i++)
+		if (pdev->resource[i].flags & IORESOURCE_IRQ)
+			irq = pdev->resource[i].start;
+		else if (pdev->resource[i].flags & IORESOURCE_MEM)
+			resource[r++] = pdev->resource[i];
+
+	th = intel_th_alloc(&pdev->dev, (void *)id->driver_data, resource, r,
+			    irq);
 	if (IS_ERR(th))
 		return PTR_ERR(th);
 
diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c
index 7c1acc2f801c..c577b94ee606 100644
--- a/drivers/hwtracing/intel_th/core.c
+++ b/drivers/hwtracing/intel_th/core.c
@@ -491,7 +491,7 @@ static const struct intel_th_subdevice {
 				.flags	= IORESOURCE_MEM,
 			},
 			{
-				.start	= 1, /* use resource[1] */
+				.start	= TH_MMIO_SW,
 				.end	= 0,
 				.flags	= IORESOURCE_MEM,
 			},
@@ -584,7 +584,6 @@ intel_th_subdevice_alloc(struct intel_th *th,
 	struct intel_th_device *thdev;
 	struct resource res[3];
 	unsigned int req = 0;
-	bool is64bit = false;
 	int r, err;
 
 	thdev = intel_th_device_alloc(th, subdev->type, subdev->name,
@@ -594,18 +593,12 @@ intel_th_subdevice_alloc(struct intel_th *th,
 
 	thdev->drvdata = th->drvdata;
 
-	for (r = 0; r < th->num_resources; r++)
-		if (th->resource[r].flags & IORESOURCE_MEM_64) {
-			is64bit = true;
-			break;
-		}
-
 	memcpy(res, subdev->res,
 	       sizeof(struct resource) * subdev->nres);
 
 	for (r = 0; r < subdev->nres; r++) {
 		struct resource *devres = th->resource;
-		int bar = 0; /* cut subdevices' MMIO from resource[0] */
+		int bar = TH_MMIO_CONFIG;
 
 		/*
 		 * Take .end == 0 to mean 'take the whole bar',
@@ -614,8 +607,6 @@ intel_th_subdevice_alloc(struct intel_th *th,
 		 */
 		if (!res[r].end && res[r].flags == IORESOURCE_MEM) {
 			bar = res[r].start;
-			if (is64bit)
-				bar *= 2;
 			res[r].start = 0;
 			res[r].end = resource_size(&devres[bar]) - 1;
 		}
@@ -812,8 +803,7 @@ static const struct file_operations intel_th_output_fops = {
 /**
  * intel_th_alloc() - allocate a new Intel TH device and its subdevices
  * @dev:	parent device
- * @devres:	parent's resources
- * @ndevres:	number of resources
+ * @devres:	resources indexed by th_mmio_idx
  * @irq:	irq number
  */
 struct intel_th *
@@ -823,12 +813,8 @@ intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata,
 	struct intel_th *th;
 	int err, r;
 
-	if (irq == -1)
-		for (r = 0; r < ndevres; r++)
-			if (devres[r].flags & IORESOURCE_IRQ) {
-				irq = devres[r].start;
-				break;
-			}
+	if (ndevres < TH_MMIO_END)
+		return ERR_PTR(-EINVAL);
 
 	th = kzalloc(sizeof(*th), GFP_KERNEL);
 	if (!th)
@@ -849,7 +835,8 @@ intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata,
 	th->dev = dev;
 	th->drvdata = drvdata;
 
-	th->resource = devres;
+	for (r = 0; r < ndevres; r++)
+		th->resource[r] = devres[r];
 	th->num_resources = ndevres;
 	th->irq = irq;
 
diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h
index 780206dc9012..8c90c8d01867 100644
--- a/drivers/hwtracing/intel_th/intel_th.h
+++ b/drivers/hwtracing/intel_th/intel_th.h
@@ -225,9 +225,9 @@ int intel_th_set_output(struct intel_th_device *thdev,
 			unsigned int master);
 int intel_th_output_enable(struct intel_th *th, unsigned int otype);
 
-enum {
+enum th_mmio_idx {
 	TH_MMIO_CONFIG = 0,
-	TH_MMIO_SW = 2,
+	TH_MMIO_SW = 1,
 	TH_MMIO_END,
 };
 
@@ -244,7 +244,7 @@ enum {
  * @hub:	"switch" subdevice (GTH)
  * @resource:	resources of the entire controller
  * @num_thdevs:	number of devices in the @thdev array
- * @num_resources:	number or resources in the @resource array
+ * @num_resources:	number of resources in the @resource array
  * @irq:	irq number
  * @id:		this Intel TH controller's device ID in the system
  * @major:	device node major for output devices
@@ -256,7 +256,7 @@ struct intel_th {
 	struct intel_th_device	*hub;
 	struct intel_th_drvdata	*drvdata;
 
-	struct resource		*resource;
+	struct resource		resource[TH_MMIO_END];
 	int			(*activate)(struct intel_th *);
 	void			(*deactivate)(struct intel_th *);
 	unsigned int		num_thdevs;
diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c
index 1cf6290d6435..9dd2d75bd539 100644
--- a/drivers/hwtracing/intel_th/pci.c
+++ b/drivers/hwtracing/intel_th/pci.c
@@ -17,7 +17,12 @@
 
 #define DRIVER_NAME "intel_th_pci"
 
-#define BAR_MASK (BIT(TH_MMIO_CONFIG) | BIT(TH_MMIO_SW))
+enum {
+	TH_PCI_CONFIG_BAR	= 0,
+	TH_PCI_STH_SW_BAR	= 2,
+};
+
+#define BAR_MASK (BIT(TH_PCI_CONFIG_BAR) | BIT(TH_PCI_STH_SW_BAR))
 
 #define PCI_REG_NPKDSC	0x80
 #define NPKDSC_TSACT	BIT(5)
@@ -66,6 +71,10 @@ static int intel_th_pci_probe(struct pci_dev *pdev,
 			      const struct pci_device_id *id)
 {
 	struct intel_th_drvdata *drvdata = (void *)id->driver_data;
+	struct resource resource[TH_MMIO_END] = {
+		[TH_MMIO_CONFIG]	= pdev->resource[TH_PCI_CONFIG_BAR],
+		[TH_MMIO_SW]		= pdev->resource[TH_PCI_STH_SW_BAR],
+	};
 	struct intel_th *th;
 	int err;
 
@@ -77,8 +86,8 @@ static int intel_th_pci_probe(struct pci_dev *pdev,
 	if (err)
 		return err;
 
-	th = intel_th_alloc(&pdev->dev, drvdata, pdev->resource,
-			    DEVICE_COUNT_RESOURCE, pdev->irq);
+	th = intel_th_alloc(&pdev->dev, drvdata, resource, TH_MMIO_END,
+			    pdev->irq);
 	if (IS_ERR(th))
 		return PTR_ERR(th);
 
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [GIT PULL 04/22] intel_th: Skip subdevices if their MMIO is missing
  2019-05-03  8:44 [GIT PULL 00/22] intel_th: Updates for v5.2 Alexander Shishkin
                   ` (2 preceding siblings ...)
  2019-05-03  8:44 ` [GIT PULL 03/22] intel_th: Rework resource passing between glue layers and core Alexander Shishkin
@ 2019-05-03  8:44 ` Alexander Shishkin
  2019-05-03  8:44 ` [GIT PULL 05/22] intel_th: Add "rtit" source device Alexander Shishkin
                   ` (17 subsequent siblings)
  21 siblings, 0 replies; 26+ messages in thread
From: Alexander Shishkin @ 2019-05-03  8:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, Alexander Shishkin

If a subdevice requires an MMIO region that wasn't in the resources passed
down from the glue layer, don't instantiate it, but don't error out. This
means that that particular subdevice doesn't exist for this instance of
Intel TH, which is a perfectly normal situation. This applies, for example,
to the "rtit" source device.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
 drivers/hwtracing/intel_th/core.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c
index c577b94ee606..8c221e1ed12d 100644
--- a/drivers/hwtracing/intel_th/core.c
+++ b/drivers/hwtracing/intel_th/core.c
@@ -607,6 +607,9 @@ intel_th_subdevice_alloc(struct intel_th *th,
 		 */
 		if (!res[r].end && res[r].flags == IORESOURCE_MEM) {
 			bar = res[r].start;
+			err = -ENODEV;
+			if (bar >= th->num_resources)
+				goto fail_put_device;
 			res[r].start = 0;
 			res[r].end = resource_size(&devres[bar]) - 1;
 		}
@@ -749,8 +752,13 @@ static int intel_th_populate(struct intel_th *th)
 
 		thdev = intel_th_subdevice_alloc(th, subdev);
 		/* note: caller should free subdevices from th::thdev[] */
-		if (IS_ERR(thdev))
+		if (IS_ERR(thdev)) {
+			/* ENODEV for individual subdevices is allowed */
+			if (PTR_ERR(thdev) == -ENODEV)
+				continue;
+
 			return PTR_ERR(thdev);
+		}
 
 		th->thdev[th->num_thdevs++] = thdev;
 	}
@@ -813,9 +821,6 @@ intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata,
 	struct intel_th *th;
 	int err, r;
 
-	if (ndevres < TH_MMIO_END)
-		return ERR_PTR(-EINVAL);
-
 	th = kzalloc(sizeof(*th), GFP_KERNEL);
 	if (!th)
 		return ERR_PTR(-ENOMEM);
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [GIT PULL 05/22] intel_th: Add "rtit" source device
  2019-05-03  8:44 [GIT PULL 00/22] intel_th: Updates for v5.2 Alexander Shishkin
                   ` (3 preceding siblings ...)
  2019-05-03  8:44 ` [GIT PULL 04/22] intel_th: Skip subdevices if their MMIO is missing Alexander Shishkin
@ 2019-05-03  8:44 ` Alexander Shishkin
  2019-05-03  8:44 ` [GIT PULL 06/22] intel_th: Communicate IRQ via resource Alexander Shishkin
                   ` (16 subsequent siblings)
  21 siblings, 0 replies; 26+ messages in thread
From: Alexander Shishkin @ 2019-05-03  8:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, Alexander Shishkin

In some versions of Intel TH, the Software Trace Hub (STH) has a second
MMIO BAR dedicated to the input from Intel PT. This calls for a new
subdevice that will be enumerated if the corresponding BAR is present.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
 drivers/hwtracing/intel_th/core.c     | 18 ++++++++++++++++++
 drivers/hwtracing/intel_th/intel_th.h |  1 +
 drivers/hwtracing/intel_th/pci.c      | 11 ++++++++---
 3 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c
index 8c221e1ed12d..a0b8b0182daa 100644
--- a/drivers/hwtracing/intel_th/core.c
+++ b/drivers/hwtracing/intel_th/core.c
@@ -500,6 +500,24 @@ static const struct intel_th_subdevice {
 		.name	= "sth",
 		.type	= INTEL_TH_SOURCE,
 	},
+	{
+		.nres	= 2,
+		.res	= {
+			{
+				.start	= REG_STH_OFFSET,
+				.end	= REG_STH_OFFSET + REG_STH_LENGTH - 1,
+				.flags	= IORESOURCE_MEM,
+			},
+			{
+				.start	= TH_MMIO_RTIT,
+				.end	= 0,
+				.flags	= IORESOURCE_MEM,
+			},
+		},
+		.id	= -1,
+		.name	= "rtit",
+		.type	= INTEL_TH_SOURCE,
+	},
 	{
 		.nres	= 1,
 		.res	= {
diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h
index 8c90c8d01867..3fca86d78fdd 100644
--- a/drivers/hwtracing/intel_th/intel_th.h
+++ b/drivers/hwtracing/intel_th/intel_th.h
@@ -228,6 +228,7 @@ int intel_th_output_enable(struct intel_th *th, unsigned int otype);
 enum th_mmio_idx {
 	TH_MMIO_CONFIG = 0,
 	TH_MMIO_SW = 1,
+	TH_MMIO_RTIT = 2,
 	TH_MMIO_END,
 };
 
diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c
index 9dd2d75bd539..fd8267bbaf2c 100644
--- a/drivers/hwtracing/intel_th/pci.c
+++ b/drivers/hwtracing/intel_th/pci.c
@@ -20,6 +20,7 @@
 enum {
 	TH_PCI_CONFIG_BAR	= 0,
 	TH_PCI_STH_SW_BAR	= 2,
+	TH_PCI_RTIT_BAR		= 4,
 };
 
 #define BAR_MASK (BIT(TH_PCI_CONFIG_BAR) | BIT(TH_PCI_STH_SW_BAR))
@@ -75,8 +76,8 @@ static int intel_th_pci_probe(struct pci_dev *pdev,
 		[TH_MMIO_CONFIG]	= pdev->resource[TH_PCI_CONFIG_BAR],
 		[TH_MMIO_SW]		= pdev->resource[TH_PCI_STH_SW_BAR],
 	};
+	int err, r = TH_MMIO_SW + 1;
 	struct intel_th *th;
-	int err;
 
 	err = pcim_enable_device(pdev);
 	if (err)
@@ -86,8 +87,12 @@ static int intel_th_pci_probe(struct pci_dev *pdev,
 	if (err)
 		return err;
 
-	th = intel_th_alloc(&pdev->dev, drvdata, resource, TH_MMIO_END,
-			    pdev->irq);
+	if (pdev->resource[TH_PCI_RTIT_BAR].start) {
+		resource[TH_MMIO_RTIT] = pdev->resource[TH_PCI_RTIT_BAR];
+		r++;
+	}
+
+	th = intel_th_alloc(&pdev->dev, drvdata, resource, r, pdev->irq);
 	if (IS_ERR(th))
 		return PTR_ERR(th);
 
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [GIT PULL 06/22] intel_th: Communicate IRQ via resource
  2019-05-03  8:44 [GIT PULL 00/22] intel_th: Updates for v5.2 Alexander Shishkin
                   ` (4 preceding siblings ...)
  2019-05-03  8:44 ` [GIT PULL 05/22] intel_th: Add "rtit" source device Alexander Shishkin
@ 2019-05-03  8:44 ` Alexander Shishkin
  2019-05-03  8:44 ` [GIT PULL 07/22] intel_th: pci: Use MSI interrupt signalling Alexander Shishkin
                   ` (15 subsequent siblings)
  21 siblings, 0 replies; 26+ messages in thread
From: Alexander Shishkin @ 2019-05-03  8:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, Alexander Shishkin

Currently, the IRQ is passed between the glue layers and the core as a
separate argument, while the MMIO resources are passed as resources.
This also limits the number of IRQs thus used to one, while the current
versions of Intel TH use a different MSI vector for each interrupt
triggering event, of which there are 7.

Change this to pass IRQ in the resources array.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
 drivers/hwtracing/intel_th/acpi.c     | 10 ++++------
 drivers/hwtracing/intel_th/core.c     | 23 ++++++++++++++++++-----
 drivers/hwtracing/intel_th/intel_th.h |  2 +-
 drivers/hwtracing/intel_th/pci.c      |  9 +++++++--
 4 files changed, 30 insertions(+), 14 deletions(-)

diff --git a/drivers/hwtracing/intel_th/acpi.c b/drivers/hwtracing/intel_th/acpi.c
index b528e5b113ff..87f9024e4bbb 100644
--- a/drivers/hwtracing/intel_th/acpi.c
+++ b/drivers/hwtracing/intel_th/acpi.c
@@ -40,20 +40,18 @@ static int intel_th_acpi_probe(struct platform_device *pdev)
 	struct resource resource[TH_MMIO_END];
 	const struct acpi_device_id *id;
 	struct intel_th *th;
-	int i, r, irq = -1;
+	int i, r;
 
 	id = acpi_match_device(intel_th_acpi_ids, &pdev->dev);
 	if (!id)
 		return -ENODEV;
 
 	for (i = 0, r = 0; i < pdev->num_resources && r < TH_MMIO_END; i++)
-		if (pdev->resource[i].flags & IORESOURCE_IRQ)
-			irq = pdev->resource[i].start;
-		else if (pdev->resource[i].flags & IORESOURCE_MEM)
+		if (pdev->resource[i].flags &
+		    (IORESOURCE_IRQ | IORESOURCE_MEM))
 			resource[r++] = pdev->resource[i];
 
-	th = intel_th_alloc(&pdev->dev, (void *)id->driver_data, resource, r,
-			    irq);
+	th = intel_th_alloc(&pdev->dev, (void *)id->driver_data, resource, r);
 	if (IS_ERR(th))
 		return PTR_ERR(th);
 
diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c
index a0b8b0182daa..0205fca4c606 100644
--- a/drivers/hwtracing/intel_th/core.c
+++ b/drivers/hwtracing/intel_th/core.c
@@ -834,10 +834,10 @@ static const struct file_operations intel_th_output_fops = {
  */
 struct intel_th *
 intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata,
-	       struct resource *devres, unsigned int ndevres, int irq)
+	       struct resource *devres, unsigned int ndevres)
 {
+	int err, r, nr_mmios = 0;
 	struct intel_th *th;
-	int err, r;
 
 	th = kzalloc(sizeof(*th), GFP_KERNEL);
 	if (!th)
@@ -855,13 +855,26 @@ intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata,
 		err = th->major;
 		goto err_ida;
 	}
+	th->irq = -1;
 	th->dev = dev;
 	th->drvdata = drvdata;
 
 	for (r = 0; r < ndevres; r++)
-		th->resource[r] = devres[r];
-	th->num_resources = ndevres;
-	th->irq = irq;
+		switch (devres[r].flags & IORESOURCE_TYPE_BITS) {
+		case IORESOURCE_MEM:
+			th->resource[nr_mmios++] = devres[r];
+			break;
+		case IORESOURCE_IRQ:
+			if (th->irq == -1)
+				th->irq = devres[r].start;
+			break;
+		default:
+			dev_warn(dev, "Unknown resource type %lx\n",
+				 devres[r].flags);
+			break;
+		}
+
+	th->num_resources = nr_mmios;
 
 	dev_set_drvdata(dev, th);
 
diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h
index 3fca86d78fdd..6c6eb87e48a0 100644
--- a/drivers/hwtracing/intel_th/intel_th.h
+++ b/drivers/hwtracing/intel_th/intel_th.h
@@ -213,7 +213,7 @@ static inline struct intel_th *to_intel_th(struct intel_th_device *thdev)
 
 struct intel_th *
 intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata,
-	       struct resource *devres, unsigned int ndevres, int irq);
+	       struct resource *devres, unsigned int ndevres);
 void intel_th_free(struct intel_th *th);
 
 int intel_th_driver_register(struct intel_th_driver *thdrv);
diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c
index fd8267bbaf2c..03d6894cd9c9 100644
--- a/drivers/hwtracing/intel_th/pci.c
+++ b/drivers/hwtracing/intel_th/pci.c
@@ -72,7 +72,7 @@ static int intel_th_pci_probe(struct pci_dev *pdev,
 			      const struct pci_device_id *id)
 {
 	struct intel_th_drvdata *drvdata = (void *)id->driver_data;
-	struct resource resource[TH_MMIO_END] = {
+	struct resource resource[TH_MMIO_END + 1] = {
 		[TH_MMIO_CONFIG]	= pdev->resource[TH_PCI_CONFIG_BAR],
 		[TH_MMIO_SW]		= pdev->resource[TH_PCI_STH_SW_BAR],
 	};
@@ -92,7 +92,12 @@ static int intel_th_pci_probe(struct pci_dev *pdev,
 		r++;
 	}
 
-	th = intel_th_alloc(&pdev->dev, drvdata, resource, r, pdev->irq);
+	if (pdev->irq > 0) {
+		resource[r].flags   = IORESOURCE_IRQ;
+		resource[r++].start = pdev->irq;
+	}
+
+	th = intel_th_alloc(&pdev->dev, drvdata, resource, r);
 	if (IS_ERR(th))
 		return PTR_ERR(th);
 
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [GIT PULL 07/22] intel_th: pci: Use MSI interrupt signalling
  2019-05-03  8:44 [GIT PULL 00/22] intel_th: Updates for v5.2 Alexander Shishkin
                   ` (5 preceding siblings ...)
  2019-05-03  8:44 ` [GIT PULL 06/22] intel_th: Communicate IRQ via resource Alexander Shishkin
@ 2019-05-03  8:44 ` Alexander Shishkin
  2019-05-03  8:44 ` [GIT PULL 08/22] intel_th: msu: Start handling IRQs Alexander Shishkin
                   ` (14 subsequent siblings)
  21 siblings, 0 replies; 26+ messages in thread
From: Alexander Shishkin @ 2019-05-03  8:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, Alexander Shishkin

Since Intel TH is capable of MSI interrupt signalling, make use of it.
The way it works is, each of the 7 interrupt triggering events has its
own vector in this mode, as opposed to interrupt line delivery, where
all events are signalled via the same line. Failing to enable MSI, the
driver falls back to using an interrupt line.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
 drivers/hwtracing/intel_th/intel_th.h |  3 +++
 drivers/hwtracing/intel_th/pci.c      | 16 ++++++++++------
 2 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h
index 6c6eb87e48a0..db3ad8ca1c48 100644
--- a/drivers/hwtracing/intel_th/intel_th.h
+++ b/drivers/hwtracing/intel_th/intel_th.h
@@ -238,6 +238,9 @@ enum th_mmio_idx {
 #define TH_CONFIGURABLE_MASTERS 256
 #define TH_MSC_MAX		2
 
+/* Maximum IRQ vectors */
+#define TH_NVEC_MAX		8
+
 /**
  * struct intel_th - Intel TH controller
  * @dev:	driver core's device
diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c
index 03d6894cd9c9..f5643444481b 100644
--- a/drivers/hwtracing/intel_th/pci.c
+++ b/drivers/hwtracing/intel_th/pci.c
@@ -72,11 +72,11 @@ static int intel_th_pci_probe(struct pci_dev *pdev,
 			      const struct pci_device_id *id)
 {
 	struct intel_th_drvdata *drvdata = (void *)id->driver_data;
-	struct resource resource[TH_MMIO_END + 1] = {
+	struct resource resource[TH_MMIO_END + TH_NVEC_MAX] = {
 		[TH_MMIO_CONFIG]	= pdev->resource[TH_PCI_CONFIG_BAR],
 		[TH_MMIO_SW]		= pdev->resource[TH_PCI_STH_SW_BAR],
 	};
-	int err, r = TH_MMIO_SW + 1;
+	int err, r = TH_MMIO_SW + 1, i;
 	struct intel_th *th;
 
 	err = pcim_enable_device(pdev);
@@ -92,10 +92,12 @@ static int intel_th_pci_probe(struct pci_dev *pdev,
 		r++;
 	}
 
-	if (pdev->irq > 0) {
-		resource[r].flags   = IORESOURCE_IRQ;
-		resource[r++].start = pdev->irq;
-	}
+	err = pci_alloc_irq_vectors(pdev, 1, 8, PCI_IRQ_ALL_TYPES);
+	if (err > 0)
+		for (i = 0; i < err; i++, r++) {
+			resource[r].flags = IORESOURCE_IRQ;
+			resource[r].start = pci_irq_vector(pdev, i);
+		}
 
 	th = intel_th_alloc(&pdev->dev, drvdata, resource, r);
 	if (IS_ERR(th))
@@ -114,6 +116,8 @@ static void intel_th_pci_remove(struct pci_dev *pdev)
 	struct intel_th *th = pci_get_drvdata(pdev);
 
 	intel_th_free(th);
+
+	pci_free_irq_vectors(pdev);
 }
 
 static const struct intel_th_drvdata intel_th_2x = {
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [GIT PULL 08/22] intel_th: msu: Start handling IRQs
  2019-05-03  8:44 [GIT PULL 00/22] intel_th: Updates for v5.2 Alexander Shishkin
                   ` (6 preceding siblings ...)
  2019-05-03  8:44 ` [GIT PULL 07/22] intel_th: pci: Use MSI interrupt signalling Alexander Shishkin
@ 2019-05-03  8:44 ` Alexander Shishkin
  2019-05-03  8:44 ` [GIT PULL 09/22] intel_th: Only report useful IRQs to subdevices Alexander Shishkin
                   ` (13 subsequent siblings)
  21 siblings, 0 replies; 26+ messages in thread
From: Alexander Shishkin @ 2019-05-03  8:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, Alexander Shishkin

We intend to use the interrupt to detect Last Block condition in the MSU
driver, which we can use for double-buffering software-managed data
transfers.

Add an interrupt handler to the MSU driver.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
 drivers/hwtracing/intel_th/core.c     | 32 ++++++++++++++
 drivers/hwtracing/intel_th/intel_th.h |  4 +-
 drivers/hwtracing/intel_th/msu.c      | 64 ++++++++++++++++++++++++++-
 drivers/hwtracing/intel_th/msu.h      |  8 ++++
 4 files changed, 106 insertions(+), 2 deletions(-)

diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c
index 0205fca4c606..750aa9d6f849 100644
--- a/drivers/hwtracing/intel_th/core.c
+++ b/drivers/hwtracing/intel_th/core.c
@@ -826,6 +826,28 @@ static const struct file_operations intel_th_output_fops = {
 	.llseek	= noop_llseek,
 };
 
+static irqreturn_t intel_th_irq(int irq, void *data)
+{
+	struct intel_th *th = data;
+	irqreturn_t ret = IRQ_NONE;
+	struct intel_th_driver *d;
+	int i;
+
+	for (i = 0; i < th->num_thdevs; i++) {
+		if (th->thdev[i]->type != INTEL_TH_OUTPUT)
+			continue;
+
+		d = to_intel_th_driver(th->thdev[i]->dev.driver);
+		if (d && d->irq)
+			ret |= d->irq(th->thdev[i]);
+	}
+
+	if (ret == IRQ_NONE)
+		pr_warn_ratelimited("nobody cared for irq\n");
+
+	return ret;
+}
+
 /**
  * intel_th_alloc() - allocate a new Intel TH device and its subdevices
  * @dev:	parent device
@@ -865,6 +887,12 @@ intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata,
 			th->resource[nr_mmios++] = devres[r];
 			break;
 		case IORESOURCE_IRQ:
+			err = devm_request_irq(dev, devres[r].start,
+					       intel_th_irq, IRQF_SHARED,
+					       dev_name(dev), th);
+			if (err)
+				goto err_chrdev;
+
 			if (th->irq == -1)
 				th->irq = devres[r].start;
 			break;
@@ -891,6 +919,10 @@ intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata,
 
 	return th;
 
+err_chrdev:
+	__unregister_chrdev(th->major, 0, TH_POSSIBLE_OUTPUTS,
+			    "intel_th/output");
+
 err_ida:
 	ida_simple_remove(&intel_th_ida, th->id);
 
diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h
index db3ad8ca1c48..59038215489a 100644
--- a/drivers/hwtracing/intel_th/intel_th.h
+++ b/drivers/hwtracing/intel_th/intel_th.h
@@ -8,6 +8,8 @@
 #ifndef __INTEL_TH_H__
 #define __INTEL_TH_H__
 
+#include <linux/irqreturn.h>
+
 /* intel_th_device device types */
 enum {
 	/* Devices that generate trace data */
@@ -160,7 +162,7 @@ struct intel_th_driver {
 	void			(*disable)(struct intel_th_device *thdev,
 					   struct intel_th_output *output);
 	/* output ops */
-	void			(*irq)(struct intel_th_device *thdev);
+	irqreturn_t		(*irq)(struct intel_th_device *thdev);
 	int			(*activate)(struct intel_th_device *thdev);
 	void			(*deactivate)(struct intel_th_device *thdev);
 	/* file_operations for those who want a device node */
diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c
index 8ff326c0c406..3716d312a4ee 100644
--- a/drivers/hwtracing/intel_th/msu.c
+++ b/drivers/hwtracing/intel_th/msu.c
@@ -102,6 +102,7 @@ struct msc_iter {
  */
 struct msc {
 	void __iomem		*reg_base;
+	void __iomem		*msu_base;
 	struct intel_th_device	*thdev;
 
 	struct list_head	win_list;
@@ -122,7 +123,8 @@ struct msc {
 
 	/* config */
 	unsigned int		enabled : 1,
-				wrap	: 1;
+				wrap	: 1,
+				do_irq	: 1;
 	unsigned int		mode;
 	unsigned int		burst_len;
 	unsigned int		index;
@@ -476,6 +478,40 @@ static void msc_buffer_clear_hw_header(struct msc *msc)
 	}
 }
 
+static int intel_th_msu_init(struct msc *msc)
+{
+	u32 mintctl, msusts;
+
+	if (!msc->do_irq)
+		return 0;
+
+	mintctl = ioread32(msc->msu_base + REG_MSU_MINTCTL);
+	mintctl |= msc->index ? M1BLIE : M0BLIE;
+	iowrite32(mintctl, msc->msu_base + REG_MSU_MINTCTL);
+	if (mintctl != ioread32(msc->msu_base + REG_MSU_MINTCTL)) {
+		dev_info(msc_dev(msc), "MINTCTL ignores writes: no usable interrupts\n");
+		msc->do_irq = 0;
+		return 0;
+	}
+
+	msusts = ioread32(msc->msu_base + REG_MSU_MSUSTS);
+	iowrite32(msusts, msc->msu_base + REG_MSU_MSUSTS);
+
+	return 0;
+}
+
+static void intel_th_msu_deinit(struct msc *msc)
+{
+	u32 mintctl;
+
+	if (!msc->do_irq)
+		return;
+
+	mintctl = ioread32(msc->msu_base + REG_MSU_MINTCTL);
+	mintctl &= msc->index ? ~M1BLIE : ~M0BLIE;
+	iowrite32(mintctl, msc->msu_base + REG_MSU_MINTCTL);
+}
+
 /**
  * msc_configure() - set up MSC hardware
  * @msc:	the MSC device to configure
@@ -1295,6 +1331,21 @@ static int intel_th_msc_init(struct msc *msc)
 	return 0;
 }
 
+static irqreturn_t intel_th_msc_interrupt(struct intel_th_device *thdev)
+{
+	struct msc *msc = dev_get_drvdata(&thdev->dev);
+	u32 msusts = ioread32(msc->msu_base + REG_MSU_MSUSTS);
+	u32 mask = msc->index ? MSUSTS_MSC1BLAST : MSUSTS_MSC0BLAST;
+
+	if (!(msusts & mask)) {
+		if (msc->enabled)
+			return IRQ_HANDLED;
+		return IRQ_NONE;
+	}
+
+	return IRQ_HANDLED;
+}
+
 static const char * const msc_mode[] = {
 	[MSC_MODE_SINGLE]	= "single",
 	[MSC_MODE_MULTI]	= "multi",
@@ -1500,10 +1551,19 @@ static int intel_th_msc_probe(struct intel_th_device *thdev)
 	if (!msc)
 		return -ENOMEM;
 
+	res = intel_th_device_get_resource(thdev, IORESOURCE_IRQ, 1);
+	if (!res)
+		msc->do_irq = 1;
+
 	msc->index = thdev->id;
 
 	msc->thdev = thdev;
 	msc->reg_base = base + msc->index * 0x100;
+	msc->msu_base = base;
+
+	err = intel_th_msu_init(msc);
+	if (err)
+		return err;
 
 	err = intel_th_msc_init(msc);
 	if (err)
@@ -1520,6 +1580,7 @@ static void intel_th_msc_remove(struct intel_th_device *thdev)
 	int ret;
 
 	intel_th_msc_deactivate(thdev);
+	intel_th_msu_deinit(msc);
 
 	/*
 	 * Buffers should not be used at this point except if the
@@ -1533,6 +1594,7 @@ static void intel_th_msc_remove(struct intel_th_device *thdev)
 static struct intel_th_driver intel_th_msc_driver = {
 	.probe	= intel_th_msc_probe,
 	.remove	= intel_th_msc_remove,
+	.irq		= intel_th_msc_interrupt,
 	.activate	= intel_th_msc_activate,
 	.deactivate	= intel_th_msc_deactivate,
 	.fops	= &intel_th_msc_fops,
diff --git a/drivers/hwtracing/intel_th/msu.h b/drivers/hwtracing/intel_th/msu.h
index 9cc8aced6116..e8cb819a3804 100644
--- a/drivers/hwtracing/intel_th/msu.h
+++ b/drivers/hwtracing/intel_th/msu.h
@@ -11,6 +11,7 @@
 enum {
 	REG_MSU_MSUPARAMS	= 0x0000,
 	REG_MSU_MSUSTS		= 0x0008,
+	REG_MSU_MINTCTL		= 0x0004, /* MSU-global interrupt control */
 	REG_MSU_MSC0CTL		= 0x0100, /* MSC0 control */
 	REG_MSU_MSC0STS		= 0x0104, /* MSC0 status */
 	REG_MSU_MSC0BAR		= 0x0108, /* MSC0 output base address */
@@ -28,6 +29,8 @@ enum {
 
 /* MSUSTS bits */
 #define MSUSTS_MSU_INT	BIT(0)
+#define MSUSTS_MSC0BLAST	BIT(16)
+#define MSUSTS_MSC1BLAST	BIT(24)
 
 /* MSCnCTL bits */
 #define MSC_EN		BIT(0)
@@ -36,6 +39,11 @@ enum {
 #define MSC_MODE	(BIT(4) | BIT(5))
 #define MSC_LEN		(BIT(8) | BIT(9) | BIT(10))
 
+/* MINTCTL bits */
+#define MICDE		BIT(0)
+#define M0BLIE		BIT(16)
+#define M1BLIE		BIT(24)
+
 /* MSC operating modes (MSC_MODE) */
 enum {
 	MSC_MODE_SINGLE	= 0,
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [GIT PULL 09/22] intel_th: Only report useful IRQs to subdevices
  2019-05-03  8:44 [GIT PULL 00/22] intel_th: Updates for v5.2 Alexander Shishkin
                   ` (7 preceding siblings ...)
  2019-05-03  8:44 ` [GIT PULL 08/22] intel_th: msu: Start handling IRQs Alexander Shishkin
@ 2019-05-03  8:44 ` Alexander Shishkin
  2019-05-03  8:44 ` [GIT PULL 10/22] intel_th: msu: Replace open-coded list_{first,last,next}_entry variants Alexander Shishkin
                   ` (12 subsequent siblings)
  21 siblings, 0 replies; 26+ messages in thread
From: Alexander Shishkin @ 2019-05-03  8:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, Alexander Shishkin

The only type of IRQ triggering event that is useful to us at the moment
is the "last block" interrupt of the MSU. This interrupt can only be
enabled via "MINTCTL" register that doesn't exist in earlier version of
the Intel TH.

Enumerate the presence of MINTCTL via per-device driver data structure
and only instantiate the IRQ resource for subdevices if this capability
is present.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
 drivers/hwtracing/intel_th/core.c     | 7 ++++++-
 drivers/hwtracing/intel_th/intel_th.h | 2 ++
 drivers/hwtracing/intel_th/pci.c      | 1 +
 3 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c
index 750aa9d6f849..390031df3edf 100644
--- a/drivers/hwtracing/intel_th/core.c
+++ b/drivers/hwtracing/intel_th/core.c
@@ -639,7 +639,12 @@ intel_th_subdevice_alloc(struct intel_th *th,
 			dev_dbg(th->dev, "%s:%d @ %pR\n",
 				subdev->name, r, &res[r]);
 		} else if (res[r].flags & IORESOURCE_IRQ) {
-			res[r].start	= th->irq;
+			/*
+			 * Only pass on the IRQ if we have useful interrupts:
+			 * the ones that can be configured via MINTCTL.
+			 */
+			if (INTEL_TH_CAP(th, has_mintctl) && th->irq != -1)
+				res[r].start = th->irq;
 		}
 	}
 
diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h
index 59038215489a..8b986ba160e4 100644
--- a/drivers/hwtracing/intel_th/intel_th.h
+++ b/drivers/hwtracing/intel_th/intel_th.h
@@ -44,10 +44,12 @@ struct intel_th_output {
 /**
  * struct intel_th_drvdata - describes hardware capabilities and quirks
  * @tscu_enable:	device needs SW to enable time stamping unit
+ * @has_mintctl:	device has interrupt control (MINTCTL) register
  * @host_mode_only:	device can only operate in 'host debugger' mode
  */
 struct intel_th_drvdata {
 	unsigned int	tscu_enable        : 1,
+			has_mintctl        : 1,
 			host_mode_only     : 1;
 };
 
diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c
index f5643444481b..5808dd4c104c 100644
--- a/drivers/hwtracing/intel_th/pci.c
+++ b/drivers/hwtracing/intel_th/pci.c
@@ -122,6 +122,7 @@ static void intel_th_pci_remove(struct pci_dev *pdev)
 
 static const struct intel_th_drvdata intel_th_2x = {
 	.tscu_enable	= 1,
+	.has_mintctl	= 1,
 };
 
 static const struct pci_device_id intel_th_pci_id_table[] = {
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [GIT PULL 10/22] intel_th: msu: Replace open-coded list_{first,last,next}_entry variants
  2019-05-03  8:44 [GIT PULL 00/22] intel_th: Updates for v5.2 Alexander Shishkin
                   ` (8 preceding siblings ...)
  2019-05-03  8:44 ` [GIT PULL 09/22] intel_th: Only report useful IRQs to subdevices Alexander Shishkin
@ 2019-05-03  8:44 ` Alexander Shishkin
  2019-05-03  8:44 ` [GIT PULL 11/22] intel_th: msu: Switch over to scatterlist Alexander Shishkin
                   ` (11 subsequent siblings)
  21 siblings, 0 replies; 26+ messages in thread
From: Alexander Shishkin @ 2019-05-03  8:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, Alexander Shishkin

There are a few places in the code where open-coded versions of list entry
accessors list_first_entry()/list_last_entry()/list_next_entry() are used.

Replace those with the standard macros.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
 drivers/hwtracing/intel_th/msu.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c
index 3716d312a4ee..f5d5cb862a81 100644
--- a/drivers/hwtracing/intel_th/msu.c
+++ b/drivers/hwtracing/intel_th/msu.c
@@ -179,7 +179,7 @@ static struct msc_window *msc_oldest_window(struct msc *msc)
 			return win;
 	}
 
-	return list_entry(msc->win_list.next, struct msc_window, entry);
+	return list_first_entry(&msc->win_list, struct msc_window, entry);
 }
 
 /**
@@ -230,10 +230,10 @@ static inline bool msc_is_last_win(struct msc_window *win)
 static struct msc_window *msc_next_window(struct msc_window *win)
 {
 	if (msc_is_last_win(win))
-		return list_entry(win->msc->win_list.next, struct msc_window,
-				  entry);
+		return list_first_entry(&win->msc->win_list, struct msc_window,
+					entry);
 
-	return list_entry(win->entry.next, struct msc_window, entry);
+	return list_next_entry(win, entry);
 }
 
 static struct msc_block_desc *msc_iter_bdesc(struct msc_iter *iter)
@@ -759,8 +759,9 @@ static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_blocks)
 		return -ENOMEM;
 
 	if (!list_empty(&msc->win_list)) {
-		struct msc_window *prev = list_entry(msc->win_list.prev,
-						     struct msc_window, entry);
+		struct msc_window *prev = list_last_entry(&msc->win_list,
+							  struct msc_window,
+							  entry);
 
 		win->pgoff = prev->pgoff + prev->nr_blocks;
 	}
@@ -863,11 +864,10 @@ static void msc_buffer_relink(struct msc *msc)
 		 */
 		if (msc_is_last_win(win)) {
 			sw_tag |= MSC_SW_TAG_LASTWIN;
-			next_win = list_entry(msc->win_list.next,
-					      struct msc_window, entry);
+			next_win = list_first_entry(&msc->win_list,
+						    struct msc_window, entry);
 		} else {
-			next_win = list_entry(win->entry.next,
-					      struct msc_window, entry);
+			next_win = list_next_entry(win, entry);
 		}
 
 		for (blk = 0; blk < win->nr_blocks; blk++) {
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [GIT PULL 11/22] intel_th: msu: Switch over to scatterlist
  2019-05-03  8:44 [GIT PULL 00/22] intel_th: Updates for v5.2 Alexander Shishkin
                   ` (9 preceding siblings ...)
  2019-05-03  8:44 ` [GIT PULL 10/22] intel_th: msu: Replace open-coded list_{first,last,next}_entry variants Alexander Shishkin
@ 2019-05-03  8:44 ` Alexander Shishkin
  2019-05-03  8:44 ` [GIT PULL 12/22] intel_th: msu: Support multipage blocks Alexander Shishkin
                   ` (10 subsequent siblings)
  21 siblings, 0 replies; 26+ messages in thread
From: Alexander Shishkin @ 2019-05-03  8:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, Alexander Shishkin

Instead of using a home-grown array of pointers to the DMA pages, switch
over to scatterlist data types and accessors, which has all the convenient
accessors, can be used to batch-map DMA memory and is convenient for
passing around between different layers, which will be useful when MSU
buffer management has to cross the boundaries of the MSU driver.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
 drivers/hwtracing/intel_th/msu.c | 163 ++++++++++++++++++++-----------
 1 file changed, 104 insertions(+), 59 deletions(-)

diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c
index f5d5cb862a81..0e7fbef8dee0 100644
--- a/drivers/hwtracing/intel_th/msu.c
+++ b/drivers/hwtracing/intel_th/msu.c
@@ -28,29 +28,19 @@
 
 #define msc_dev(x) (&(x)->thdev->dev)
 
-/**
- * struct msc_block - multiblock mode block descriptor
- * @bdesc:	pointer to hardware descriptor (beginning of the block)
- * @addr:	physical address of the block
- */
-struct msc_block {
-	struct msc_block_desc	*bdesc;
-	dma_addr_t		addr;
-};
-
 /**
  * struct msc_window - multiblock mode window descriptor
  * @entry:	window list linkage (msc::win_list)
  * @pgoff:	page offset into the buffer that this window starts at
  * @nr_blocks:	number of blocks (pages) in this window
- * @block:	array of block descriptors
+ * @sgt:	array of block descriptors
  */
 struct msc_window {
 	struct list_head	entry;
 	unsigned long		pgoff;
 	unsigned int		nr_blocks;
 	struct msc		*msc;
-	struct msc_block	block[0];
+	struct sg_table		sgt;
 };
 
 /**
@@ -143,6 +133,24 @@ static inline bool msc_block_is_empty(struct msc_block_desc *bdesc)
 	return false;
 }
 
+static inline struct msc_block_desc *
+msc_win_block(struct msc_window *win, unsigned int block)
+{
+	return sg_virt(&win->sgt.sgl[block]);
+}
+
+static inline dma_addr_t
+msc_win_baddr(struct msc_window *win, unsigned int block)
+{
+	return sg_dma_address(&win->sgt.sgl[block]);
+}
+
+static inline unsigned long
+msc_win_bpfn(struct msc_window *win, unsigned int block)
+{
+	return msc_win_baddr(win, block) >> PAGE_SHIFT;
+}
+
 /**
  * msc_oldest_window() - locate the window with oldest data
  * @msc:	MSC device
@@ -168,11 +176,11 @@ static struct msc_window *msc_oldest_window(struct msc *msc)
 	 * something like 2, in which case we're good
 	 */
 	list_for_each_entry(win, &msc->win_list, entry) {
-		if (win->block[0].addr == win_addr)
+		if (sg_dma_address(win->sgt.sgl) == win_addr)
 			found++;
 
 		/* skip the empty ones */
-		if (msc_block_is_empty(win->block[0].bdesc))
+		if (msc_block_is_empty(msc_win_block(win, 0)))
 			continue;
 
 		if (found)
@@ -191,7 +199,7 @@ static struct msc_window *msc_oldest_window(struct msc *msc)
 static unsigned int msc_win_oldest_block(struct msc_window *win)
 {
 	unsigned int blk;
-	struct msc_block_desc *bdesc = win->block[0].bdesc;
+	struct msc_block_desc *bdesc = msc_win_block(win, 0);
 
 	/* without wrapping, first block is the oldest */
 	if (!msc_block_wrapped(bdesc))
@@ -202,7 +210,7 @@ static unsigned int msc_win_oldest_block(struct msc_window *win)
 	 * oldest data for this window.
 	 */
 	for (blk = 0; blk < win->nr_blocks; blk++) {
-		bdesc = win->block[blk].bdesc;
+		bdesc = msc_win_block(win, blk);
 
 		if (msc_block_last_written(bdesc))
 			return blk;
@@ -238,7 +246,7 @@ static struct msc_window *msc_next_window(struct msc_window *win)
 
 static struct msc_block_desc *msc_iter_bdesc(struct msc_iter *iter)
 {
-	return iter->win->block[iter->block].bdesc;
+	return msc_win_block(iter->win, iter->block);
 }
 
 static void msc_iter_init(struct msc_iter *iter)
@@ -471,7 +479,7 @@ static void msc_buffer_clear_hw_header(struct msc *msc)
 			offsetof(struct msc_block_desc, hw_tag);
 
 		for (blk = 0; blk < win->nr_blocks; blk++) {
-			struct msc_block_desc *bdesc = win->block[blk].bdesc;
+			struct msc_block_desc *bdesc = msc_win_block(win, blk);
 
 			memset(&bdesc->hw_tag, 0, hw_sz);
 		}
@@ -734,6 +742,40 @@ static struct page *msc_buffer_contig_get_page(struct msc *msc,
 	return virt_to_page(msc->base + (pgoff << PAGE_SHIFT));
 }
 
+static int __msc_buffer_win_alloc(struct msc_window *win,
+				  unsigned int nr_blocks)
+{
+	struct scatterlist *sg_ptr;
+	void *block;
+	int i, ret;
+
+	ret = sg_alloc_table(&win->sgt, nr_blocks, GFP_KERNEL);
+	if (ret)
+		return -ENOMEM;
+
+	for_each_sg(win->sgt.sgl, sg_ptr, nr_blocks, i) {
+		block = dma_alloc_coherent(msc_dev(win->msc)->parent->parent,
+					  PAGE_SIZE, &sg_dma_address(sg_ptr),
+					  GFP_KERNEL);
+		if (!block)
+			goto err_nomem;
+
+		sg_set_buf(sg_ptr, block, PAGE_SIZE);
+	}
+
+	return nr_blocks;
+
+err_nomem:
+	for (i--; i >= 0; i--)
+		dma_free_coherent(msc_dev(win->msc)->parent->parent, PAGE_SIZE,
+				  msc_win_block(win, i),
+				  msc_win_baddr(win, i));
+
+	sg_free_table(&win->sgt);
+
+	return -ENOMEM;
+}
+
 /**
  * msc_buffer_win_alloc() - alloc a window for a multiblock mode
  * @msc:	MSC device
@@ -747,45 +789,48 @@ static struct page *msc_buffer_contig_get_page(struct msc *msc,
 static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_blocks)
 {
 	struct msc_window *win;
-	unsigned long size = PAGE_SIZE;
-	int i, ret = -ENOMEM;
+	int ret = -ENOMEM, i;
 
 	if (!nr_blocks)
 		return 0;
 
-	win = kzalloc(offsetof(struct msc_window, block[nr_blocks]),
-		      GFP_KERNEL);
+	/*
+	 * This limitation hold as long as we need random access to the
+	 * block. When that changes, this can go away.
+	 */
+	if (nr_blocks > SG_MAX_SINGLE_ALLOC)
+		return -EINVAL;
+
+	win = kzalloc(sizeof(*win), GFP_KERNEL);
 	if (!win)
 		return -ENOMEM;
 
+	win->msc = msc;
+
 	if (!list_empty(&msc->win_list)) {
 		struct msc_window *prev = list_last_entry(&msc->win_list,
 							  struct msc_window,
 							  entry);
 
+		/* This works as long as blocks are page-sized */
 		win->pgoff = prev->pgoff + prev->nr_blocks;
 	}
 
-	for (i = 0; i < nr_blocks; i++) {
-		win->block[i].bdesc =
-			dma_alloc_coherent(msc_dev(msc)->parent->parent, size,
-					   &win->block[i].addr, GFP_KERNEL);
-
-		if (!win->block[i].bdesc)
-			goto err_nomem;
+	ret = __msc_buffer_win_alloc(win, nr_blocks);
+	if (ret < 0)
+		goto err_nomem;
 
 #ifdef CONFIG_X86
+	for (i = 0; i < ret; i++)
 		/* Set the page as uncached */
-		set_memory_uc((unsigned long)win->block[i].bdesc, 1);
+		set_memory_uc((unsigned long)msc_win_block(win, i), 1);
 #endif
-	}
 
-	win->msc = msc;
-	win->nr_blocks = nr_blocks;
+	win->nr_blocks = ret;
 
 	if (list_empty(&msc->win_list)) {
-		msc->base = win->block[0].bdesc;
-		msc->base_addr = win->block[0].addr;
+		msc->base = msc_win_block(win, 0);
+		msc->base_addr = msc_win_baddr(win, 0);
 	}
 
 	list_add_tail(&win->entry, &msc->win_list);
@@ -794,19 +839,25 @@ static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_blocks)
 	return 0;
 
 err_nomem:
-	for (i--; i >= 0; i--) {
-#ifdef CONFIG_X86
-		/* Reset the page to write-back before releasing */
-		set_memory_wb((unsigned long)win->block[i].bdesc, 1);
-#endif
-		dma_free_coherent(msc_dev(msc)->parent->parent, size,
-				  win->block[i].bdesc, win->block[i].addr);
-	}
 	kfree(win);
 
 	return ret;
 }
 
+static void __msc_buffer_win_free(struct msc *msc, struct msc_window *win)
+{
+	int i;
+
+	for (i = 0; i < win->nr_blocks; i++) {
+		struct page *page = sg_page(&win->sgt.sgl[i]);
+
+		page->mapping = NULL;
+		dma_free_coherent(msc_dev(win->msc)->parent->parent, PAGE_SIZE,
+				  msc_win_block(win, i), msc_win_baddr(win, i));
+	}
+	sg_free_table(&win->sgt);
+}
+
 /**
  * msc_buffer_win_free() - free a window from MSC's window list
  * @msc:	MSC device
@@ -827,17 +878,13 @@ static void msc_buffer_win_free(struct msc *msc, struct msc_window *win)
 		msc->base_addr = 0;
 	}
 
-	for (i = 0; i < win->nr_blocks; i++) {
-		struct page *page = virt_to_page(win->block[i].bdesc);
-
-		page->mapping = NULL;
 #ifdef CONFIG_X86
-		/* Reset the page to write-back before releasing */
-		set_memory_wb((unsigned long)win->block[i].bdesc, 1);
+	for (i = 0; i < win->nr_blocks; i++)
+		/* Reset the page to write-back */
+		set_memory_wb((unsigned long)msc_win_block(win, i), 1);
 #endif
-		dma_free_coherent(msc_dev(win->msc)->parent->parent, PAGE_SIZE,
-				  win->block[i].bdesc, win->block[i].addr);
-	}
+
+	__msc_buffer_win_free(msc, win);
 
 	kfree(win);
 }
@@ -871,11 +918,11 @@ static void msc_buffer_relink(struct msc *msc)
 		}
 
 		for (blk = 0; blk < win->nr_blocks; blk++) {
-			struct msc_block_desc *bdesc = win->block[blk].bdesc;
+			struct msc_block_desc *bdesc = msc_win_block(win, blk);
 
 			memset(bdesc, 0, sizeof(*bdesc));
 
-			bdesc->next_win = next_win->block[0].addr >> PAGE_SHIFT;
+			bdesc->next_win = msc_win_bpfn(next_win, 0);
 
 			/*
 			 * Similarly to last window, last block should point
@@ -883,11 +930,9 @@ static void msc_buffer_relink(struct msc *msc)
 			 */
 			if (blk == win->nr_blocks - 1) {
 				sw_tag |= MSC_SW_TAG_LASTBLK;
-				bdesc->next_blk =
-					win->block[0].addr >> PAGE_SHIFT;
+				bdesc->next_blk = msc_win_bpfn(win, 0);
 			} else {
-				bdesc->next_blk =
-					win->block[blk + 1].addr >> PAGE_SHIFT;
+				bdesc->next_blk = msc_win_bpfn(win, blk + 1);
 			}
 
 			bdesc->sw_tag = sw_tag;
@@ -1062,7 +1107,7 @@ static struct page *msc_buffer_get_page(struct msc *msc, unsigned long pgoff)
 
 found:
 	pgoff -= win->pgoff;
-	return virt_to_page(win->block[pgoff].bdesc);
+	return sg_page(&win->sgt.sgl[pgoff]);
 }
 
 /**
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [GIT PULL 12/22] intel_th: msu: Support multipage blocks
  2019-05-03  8:44 [GIT PULL 00/22] intel_th: Updates for v5.2 Alexander Shishkin
                   ` (10 preceding siblings ...)
  2019-05-03  8:44 ` [GIT PULL 11/22] intel_th: msu: Switch over to scatterlist Alexander Shishkin
@ 2019-05-03  8:44 ` Alexander Shishkin
  2019-05-03 16:15   ` Greg Kroah-Hartman
  2019-05-03  8:44 ` [GIT PULL 13/22] intel_th: msu: Factor out pipeline draining Alexander Shishkin
                   ` (9 subsequent siblings)
  21 siblings, 1 reply; 26+ messages in thread
From: Alexander Shishkin @ 2019-05-03  8:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, Alexander Shishkin

Now that the MSU is using scatterlist, we can support multipage blocks.
At the moment, the code assumes that all blocks are page-sized, but in
larger buffers it may make sense to chunk together larger blocks of
memory. One place where one-to-many relationship needs to be handled is
the MSU buffer's mmap path.

Get rid of the implicit assumption that all blocks are page-sized.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
 drivers/hwtracing/intel_th/msu.c | 48 ++++++++++++++++++++++----------
 1 file changed, 34 insertions(+), 14 deletions(-)

diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c
index 0e7fbef8dee0..a4f7a5b21d48 100644
--- a/drivers/hwtracing/intel_th/msu.c
+++ b/drivers/hwtracing/intel_th/msu.c
@@ -32,13 +32,15 @@
  * struct msc_window - multiblock mode window descriptor
  * @entry:	window list linkage (msc::win_list)
  * @pgoff:	page offset into the buffer that this window starts at
- * @nr_blocks:	number of blocks (pages) in this window
+ * @nr_blocks:	number of blocks in this window (<= @nr_pages)
+ * @nr_pages:	number of pages in this window
  * @sgt:	array of block descriptors
  */
 struct msc_window {
 	struct list_head	entry;
 	unsigned long		pgoff;
 	unsigned int		nr_blocks;
+	unsigned int		nr_pages;
 	struct msc		*msc;
 	struct sg_table		sgt;
 };
@@ -139,6 +141,12 @@ msc_win_block(struct msc_window *win, unsigned int block)
 	return sg_virt(&win->sgt.sgl[block]);
 }
 
+static inline size_t
+msc_win_actual_bsz(struct msc_window *win, unsigned int block)
+{
+	return win->sgt.sgl[block].length;
+}
+
 static inline dma_addr_t
 msc_win_baddr(struct msc_window *win, unsigned int block)
 {
@@ -779,26 +787,26 @@ static int __msc_buffer_win_alloc(struct msc_window *win,
 /**
  * msc_buffer_win_alloc() - alloc a window for a multiblock mode
  * @msc:	MSC device
- * @nr_blocks:	number of pages in this window
+ * @nr_pages:	number of pages in this window
  *
  * This modifies msc::win_list and msc::base, which requires msc::buf_mutex
  * to serialize, so the caller is expected to hold it.
  *
  * Return:	0 on success, -errno otherwise.
  */
-static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_blocks)
+static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_pages)
 {
 	struct msc_window *win;
 	int ret = -ENOMEM, i;
 
-	if (!nr_blocks)
+	if (!nr_pages)
 		return 0;
 
 	/*
 	 * This limitation hold as long as we need random access to the
 	 * block. When that changes, this can go away.
 	 */
-	if (nr_blocks > SG_MAX_SINGLE_ALLOC)
+	if (nr_pages > SG_MAX_SINGLE_ALLOC)
 		return -EINVAL;
 
 	win = kzalloc(sizeof(*win), GFP_KERNEL);
@@ -812,11 +820,10 @@ static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_blocks)
 							  struct msc_window,
 							  entry);
 
-		/* This works as long as blocks are page-sized */
-		win->pgoff = prev->pgoff + prev->nr_blocks;
+		win->pgoff = prev->pgoff + prev->nr_pages;
 	}
 
-	ret = __msc_buffer_win_alloc(win, nr_blocks);
+	ret = __msc_buffer_win_alloc(win, nr_pages);
 	if (ret < 0)
 		goto err_nomem;
 
@@ -827,6 +834,7 @@ static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_blocks)
 #endif
 
 	win->nr_blocks = ret;
+	win->nr_pages = nr_pages;
 
 	if (list_empty(&msc->win_list)) {
 		msc->base = msc_win_block(win, 0);
@@ -834,7 +842,7 @@ static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_blocks)
 	}
 
 	list_add_tail(&win->entry, &msc->win_list);
-	msc->nr_pages += nr_blocks;
+	msc->nr_pages += nr_pages;
 
 	return 0;
 
@@ -870,7 +878,7 @@ static void msc_buffer_win_free(struct msc *msc, struct msc_window *win)
 {
 	int i;
 
-	msc->nr_pages -= win->nr_blocks;
+	msc->nr_pages -= win->nr_pages;
 
 	list_del(&win->entry);
 	if (list_empty(&msc->win_list)) {
@@ -936,7 +944,7 @@ static void msc_buffer_relink(struct msc *msc)
 			}
 
 			bdesc->sw_tag = sw_tag;
-			bdesc->block_sz = PAGE_SIZE / 64;
+			bdesc->block_sz = msc_win_actual_bsz(win, blk) / 64;
 		}
 	}
 
@@ -1095,19 +1103,31 @@ static int msc_buffer_free_unless_used(struct msc *msc)
 static struct page *msc_buffer_get_page(struct msc *msc, unsigned long pgoff)
 {
 	struct msc_window *win;
+	unsigned int blk;
 
 	if (msc->mode == MSC_MODE_SINGLE)
 		return msc_buffer_contig_get_page(msc, pgoff);
 
 	list_for_each_entry(win, &msc->win_list, entry)
-		if (pgoff >= win->pgoff && pgoff < win->pgoff + win->nr_blocks)
+		if (pgoff >= win->pgoff && pgoff < win->pgoff + win->nr_pages)
 			goto found;
 
 	return NULL;
 
 found:
 	pgoff -= win->pgoff;
-	return sg_page(&win->sgt.sgl[pgoff]);
+
+	for (blk = 0; blk < win->nr_blocks; blk++) {
+		struct page *page = sg_page(&win->sgt.sgl[blk]);
+		size_t pgsz = win->sgt.sgl[blk].length >> PAGE_SHIFT;
+
+		if (pgoff < pgsz)
+			return page + pgoff;
+
+		pgoff -= pgsz;
+	}
+
+	return NULL;
 }
 
 /**
@@ -1481,7 +1501,7 @@ nr_pages_show(struct device *dev, struct device_attribute *attr, char *buf)
 	else if (msc->mode == MSC_MODE_MULTI) {
 		list_for_each_entry(win, &msc->win_list, entry) {
 			count += scnprintf(buf + count, PAGE_SIZE - count,
-					   "%d%c", win->nr_blocks,
+					   "%d%c", win->nr_pages,
 					   msc_is_last_win(win) ? '\n' : ',');
 		}
 	} else {
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [GIT PULL 13/22] intel_th: msu: Factor out pipeline draining
  2019-05-03  8:44 [GIT PULL 00/22] intel_th: Updates for v5.2 Alexander Shishkin
                   ` (11 preceding siblings ...)
  2019-05-03  8:44 ` [GIT PULL 12/22] intel_th: msu: Support multipage blocks Alexander Shishkin
@ 2019-05-03  8:44 ` Alexander Shishkin
  2019-05-03  8:44 ` [GIT PULL 14/22] intel_th: gth: Factor out trace start/stop Alexander Shishkin
                   ` (8 subsequent siblings)
  21 siblings, 0 replies; 26+ messages in thread
From: Alexander Shishkin @ 2019-05-03  8:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, Alexander Shishkin

The code that waits for the pipeline empty condition of the MSU is
currently called in the path that disables the trace. We will also
need this in the window switch trigger sequence. Therefore, factor
out this code and make it accessible to the GTH device.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
 drivers/hwtracing/intel_th/gth.c      |  7 +++++++
 drivers/hwtracing/intel_th/intel_th.h |  4 ++++
 drivers/hwtracing/intel_th/msu.c      | 28 +++++++++++++++++----------
 3 files changed, 29 insertions(+), 10 deletions(-)

diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c
index edc52d75e6bd..7e6b70047125 100644
--- a/drivers/hwtracing/intel_th/gth.c
+++ b/drivers/hwtracing/intel_th/gth.c
@@ -469,6 +469,10 @@ static void intel_th_gth_disable(struct intel_th_device *thdev,
 				 struct intel_th_output *output)
 {
 	struct gth_device *gth = dev_get_drvdata(&thdev->dev);
+	struct intel_th_device *outdev =
+		container_of(output, struct intel_th_device, output);
+	struct intel_th_driver *outdrv =
+		to_intel_th_driver(outdev->dev.driver);
 	unsigned long count;
 	int master;
 	u32 reg;
@@ -492,6 +496,9 @@ static void intel_th_gth_disable(struct intel_th_device *thdev,
 		cpu_relax();
 	}
 
+	if (outdrv->wait_empty)
+		outdrv->wait_empty(outdev);
+
 	/* clear force capture done for next captures */
 	iowrite32(0xfc, gth->base + REG_GTH_SCR2);
 
diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h
index 8b986ba160e4..74a6a4071e7f 100644
--- a/drivers/hwtracing/intel_th/intel_th.h
+++ b/drivers/hwtracing/intel_th/intel_th.h
@@ -20,6 +20,8 @@ enum {
 	INTEL_TH_SWITCH,
 };
 
+struct intel_th_device;
+
 /**
  * struct intel_th_output - descriptor INTEL_TH_OUTPUT type devices
  * @port:	output port number, assigned by the switch
@@ -27,6 +29,7 @@ enum {
  * @scratchpad:	scratchpad bits to flag when this output is enabled
  * @multiblock:	true for multiblock output configuration
  * @active:	true when this output is enabled
+ * @wait_empty:	wait for device pipeline to be empty
  *
  * Output port descriptor, used by switch driver to tell which output
  * port this output device corresponds to. Filled in at output device's
@@ -165,6 +168,7 @@ struct intel_th_driver {
 					   struct intel_th_output *output);
 	/* output ops */
 	irqreturn_t		(*irq)(struct intel_th_device *thdev);
+	void			(*wait_empty)(struct intel_th_device *thdev);
 	int			(*activate)(struct intel_th_device *thdev);
 	void			(*deactivate)(struct intel_th_device *thdev);
 	/* file_operations for those who want a device node */
diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c
index a4f7a5b21d48..01408a0e2458 100644
--- a/drivers/hwtracing/intel_th/msu.c
+++ b/drivers/hwtracing/intel_th/msu.c
@@ -585,23 +585,14 @@ static int msc_configure(struct msc *msc)
  */
 static void msc_disable(struct msc *msc)
 {
-	unsigned long count;
 	u32 reg;
 
 	lockdep_assert_held(&msc->buf_mutex);
 
 	intel_th_trace_disable(msc->thdev);
 
-	for (reg = 0, count = MSC_PLE_WAITLOOP_DEPTH;
-	     count && !(reg & MSCSTS_PLE); count--) {
-		reg = ioread32(msc->reg_base + REG_MSU_MSC0STS);
-		cpu_relax();
-	}
-
-	if (!count)
-		dev_dbg(msc_dev(msc), "timeout waiting for MSC0 PLE\n");
-
 	if (msc->mode == MSC_MODE_SINGLE) {
+		reg = ioread32(msc->reg_base + REG_MSU_MSC0STS);
 		msc->single_wrap = !!(reg & MSCSTS_WRAPSTAT);
 
 		reg = ioread32(msc->reg_base + REG_MSU_MSC0MWP);
@@ -1380,6 +1371,22 @@ static const struct file_operations intel_th_msc_fops = {
 	.owner		= THIS_MODULE,
 };
 
+static void intel_th_msc_wait_empty(struct intel_th_device *thdev)
+{
+	struct msc *msc = dev_get_drvdata(&thdev->dev);
+	unsigned long count;
+	u32 reg;
+
+	for (reg = 0, count = MSC_PLE_WAITLOOP_DEPTH;
+	     count && !(reg & MSCSTS_PLE); count--) {
+		reg = __raw_readl(msc->reg_base + REG_MSU_MSC0STS);
+		cpu_relax();
+	}
+
+	if (!count)
+		dev_dbg(msc_dev(msc), "timeout waiting for MSC0 PLE\n");
+}
+
 static int intel_th_msc_init(struct msc *msc)
 {
 	atomic_set(&msc->user_count, -1);
@@ -1660,6 +1667,7 @@ static struct intel_th_driver intel_th_msc_driver = {
 	.probe	= intel_th_msc_probe,
 	.remove	= intel_th_msc_remove,
 	.irq		= intel_th_msc_interrupt,
+	.wait_empty	= intel_th_msc_wait_empty,
 	.activate	= intel_th_msc_activate,
 	.deactivate	= intel_th_msc_deactivate,
 	.fops	= &intel_th_msc_fops,
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [GIT PULL 14/22] intel_th: gth: Factor out trace start/stop
  2019-05-03  8:44 [GIT PULL 00/22] intel_th: Updates for v5.2 Alexander Shishkin
                   ` (12 preceding siblings ...)
  2019-05-03  8:44 ` [GIT PULL 13/22] intel_th: msu: Factor out pipeline draining Alexander Shishkin
@ 2019-05-03  8:44 ` Alexander Shishkin
  2019-05-03  8:44 ` [GIT PULL 15/22] intel_th: Add switch triggering support Alexander Shishkin
                   ` (7 subsequent siblings)
  21 siblings, 0 replies; 26+ messages in thread
From: Alexander Shishkin @ 2019-05-03  8:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, Alexander Shishkin

The trace enable/disable functions of the GTH include the code that starts
and stops trace flom from the sources. This start/stop functionality will
also be used in the window switch trigger sequence.

Factor out start/stop code from the larger trace enable/disable code in
preparation for the window switch sequence.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
 drivers/hwtracing/intel_th/gth.c | 93 ++++++++++++++++++++++----------
 1 file changed, 64 insertions(+), 29 deletions(-)

diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c
index 7e6b70047125..a879445ef451 100644
--- a/drivers/hwtracing/intel_th/gth.c
+++ b/drivers/hwtracing/intel_th/gth.c
@@ -457,37 +457,28 @@ static int intel_th_output_attributes(struct gth_device *gth)
 }
 
 /**
- * intel_th_gth_disable() - disable tracing to an output device
- * @thdev:	GTH device
- * @output:	output device's descriptor
+ * intel_th_gth_stop() - stop tracing to an output device
+ * @gth:		GTH device
+ * @output:		output device's descriptor
+ * @capture_done:	set when no more traces will be captured
  *
- * This will deconfigure all masters set to output to this device,
- * disable tracing using force storeEn off signal and wait for the
- * "pipeline empty" bit for corresponding output port.
+ * This will stop tracing using force storeEn off signal and wait for the
+ * pipelines to be empty for the corresponding output port.
  */
-static void intel_th_gth_disable(struct intel_th_device *thdev,
-				 struct intel_th_output *output)
+static void intel_th_gth_stop(struct gth_device *gth,
+			      struct intel_th_output *output,
+			      bool capture_done)
 {
-	struct gth_device *gth = dev_get_drvdata(&thdev->dev);
 	struct intel_th_device *outdev =
 		container_of(output, struct intel_th_device, output);
 	struct intel_th_driver *outdrv =
 		to_intel_th_driver(outdev->dev.driver);
 	unsigned long count;
-	int master;
 	u32 reg;
-
-	spin_lock(&gth->gth_lock);
-	output->active = false;
-
-	for_each_set_bit(master, gth->output[output->port].master,
-			 TH_CONFIGURABLE_MASTERS) {
-		gth_master_set(gth, master, -1);
-	}
-	spin_unlock(&gth->gth_lock);
+	u32 scr2 = 0xfc | (capture_done ? 1 : 0);
 
 	iowrite32(0, gth->base + REG_GTH_SCR);
-	iowrite32(0xfd, gth->base + REG_GTH_SCR2);
+	iowrite32(scr2, gth->base + REG_GTH_SCR2);
 
 	/* wait on pipeline empty for the given port */
 	for (reg = 0, count = GTH_PLE_WAITLOOP_DEPTH;
@@ -496,15 +487,63 @@ static void intel_th_gth_disable(struct intel_th_device *thdev,
 		cpu_relax();
 	}
 
+	if (!count)
+		dev_dbg(gth->dev, "timeout waiting for GTH[%d] PLE\n",
+			output->port);
+
+	/* wait on output piepline empty */
 	if (outdrv->wait_empty)
 		outdrv->wait_empty(outdev);
 
 	/* clear force capture done for next captures */
 	iowrite32(0xfc, gth->base + REG_GTH_SCR2);
+}
 
-	if (!count)
-		dev_dbg(&thdev->dev, "timeout waiting for GTH[%d] PLE\n",
-			output->port);
+/**
+ * intel_th_gth_start() - start tracing to an output device
+ * @gth:	GTH device
+ * @output:	output device's descriptor
+ *
+ * This will start tracing using force storeEn signal.
+ */
+static void intel_th_gth_start(struct gth_device *gth,
+			       struct intel_th_output *output)
+{
+	u32 scr = 0xfc0000;
+
+	if (output->multiblock)
+		scr |= 0xff;
+
+	iowrite32(scr, gth->base + REG_GTH_SCR);
+	iowrite32(0, gth->base + REG_GTH_SCR2);
+}
+
+/**
+ * intel_th_gth_disable() - disable tracing to an output device
+ * @thdev:	GTH device
+ * @output:	output device's descriptor
+ *
+ * This will deconfigure all masters set to output to this device,
+ * disable tracing using force storeEn off signal and wait for the
+ * "pipeline empty" bit for corresponding output port.
+ */
+static void intel_th_gth_disable(struct intel_th_device *thdev,
+				 struct intel_th_output *output)
+{
+	struct gth_device *gth = dev_get_drvdata(&thdev->dev);
+	int master;
+	u32 reg;
+
+	spin_lock(&gth->gth_lock);
+	output->active = false;
+
+	for_each_set_bit(master, gth->output[output->port].master,
+			 TH_CONFIGURABLE_MASTERS) {
+		gth_master_set(gth, master, -1);
+	}
+	spin_unlock(&gth->gth_lock);
+
+	intel_th_gth_stop(gth, output, true);
 
 	reg = ioread32(gth->base + REG_GTH_SCRPD0);
 	reg &= ~output->scratchpad;
@@ -533,8 +572,8 @@ static void intel_th_gth_enable(struct intel_th_device *thdev,
 {
 	struct gth_device *gth = dev_get_drvdata(&thdev->dev);
 	struct intel_th *th = to_intel_th(thdev);
-	u32 scr = 0xfc0000, scrpd;
 	int master;
+	u32 scrpd;
 
 	spin_lock(&gth->gth_lock);
 	for_each_set_bit(master, gth->output[output->port].master,
@@ -542,9 +581,6 @@ static void intel_th_gth_enable(struct intel_th_device *thdev,
 		gth_master_set(gth, master, output->port);
 	}
 
-	if (output->multiblock)
-		scr |= 0xff;
-
 	output->active = true;
 	spin_unlock(&gth->gth_lock);
 
@@ -555,8 +591,7 @@ static void intel_th_gth_enable(struct intel_th_device *thdev,
 	scrpd |= output->scratchpad;
 	iowrite32(scrpd, gth->base + REG_GTH_SCRPD0);
 
-	iowrite32(scr, gth->base + REG_GTH_SCR);
-	iowrite32(0, gth->base + REG_GTH_SCR2);
+	intel_th_gth_start(gth, output);
 }
 
 /**
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [GIT PULL 15/22] intel_th: Add switch triggering support
  2019-05-03  8:44 [GIT PULL 00/22] intel_th: Updates for v5.2 Alexander Shishkin
                   ` (13 preceding siblings ...)
  2019-05-03  8:44 ` [GIT PULL 14/22] intel_th: gth: Factor out trace start/stop Alexander Shishkin
@ 2019-05-03  8:44 ` Alexander Shishkin
  2019-05-03  8:44 ` [GIT PULL 16/22] intel_th: msu: Correct the block wrap detection Alexander Shishkin
                   ` (6 subsequent siblings)
  21 siblings, 0 replies; 26+ messages in thread
From: Alexander Shishkin @ 2019-05-03  8:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, Alexander Shishkin

Add support for asserting window switch trigger when tracing to MSU output
ports. This allows for software controlled switching between windows of
the MSU buffer, which can be used for double buffering while exporting the
trace data further from the MSU.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
 drivers/hwtracing/intel_th/core.c     | 25 ++++++++++++++++--
 drivers/hwtracing/intel_th/gth.c      | 37 +++++++++++++++++++++++++++
 drivers/hwtracing/intel_th/gth.h      | 19 ++++++++++++++
 drivers/hwtracing/intel_th/intel_th.h |  6 +++++
 4 files changed, 85 insertions(+), 2 deletions(-)

diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c
index 390031df3edf..033dce563c99 100644
--- a/drivers/hwtracing/intel_th/core.c
+++ b/drivers/hwtracing/intel_th/core.c
@@ -430,9 +430,9 @@ static const struct intel_th_subdevice {
 		.nres	= 1,
 		.res	= {
 			{
-				/* Handle TSCU from GTH driver */
+				/* Handle TSCU and CTS from GTH driver */
 				.start	= REG_GTH_OFFSET,
-				.end	= REG_TSCU_OFFSET + REG_TSCU_LENGTH - 1,
+				.end	= REG_CTS_OFFSET + REG_CTS_LENGTH - 1,
 				.flags	= IORESOURCE_MEM,
 			},
 		},
@@ -987,6 +987,27 @@ int intel_th_trace_enable(struct intel_th_device *thdev)
 }
 EXPORT_SYMBOL_GPL(intel_th_trace_enable);
 
+/**
+ * intel_th_trace_switch() - execute a switch sequence
+ * @thdev:	output device that requests tracing switch
+ */
+int intel_th_trace_switch(struct intel_th_device *thdev)
+{
+	struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent);
+	struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver);
+
+	if (WARN_ON_ONCE(hub->type != INTEL_TH_SWITCH))
+		return -EINVAL;
+
+	if (WARN_ON_ONCE(thdev->type != INTEL_TH_OUTPUT))
+		return -EINVAL;
+
+	hubdrv->trig_switch(hub, &thdev->output);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(intel_th_trace_switch);
+
 /**
  * intel_th_trace_disable() - disable tracing for an output device
  * @thdev:	output device that requests tracing be disabled
diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c
index a879445ef451..fa9d34af87ac 100644
--- a/drivers/hwtracing/intel_th/gth.c
+++ b/drivers/hwtracing/intel_th/gth.c
@@ -308,6 +308,11 @@ static int intel_th_gth_reset(struct gth_device *gth)
 	iowrite32(0, gth->base + REG_GTH_SCR);
 	iowrite32(0xfc, gth->base + REG_GTH_SCR2);
 
+	/* setup CTS for single trigger */
+	iowrite32(CTS_EVENT_ENABLE_IF_ANYTHING, gth->base + REG_CTS_C0S0_EN);
+	iowrite32(CTS_ACTION_CONTROL_SET_STATE(CTS_STATE_IDLE) |
+		  CTS_ACTION_CONTROL_TRIGGER, gth->base + REG_CTS_C0S0_ACT);
+
 	return 0;
 }
 
@@ -594,6 +599,37 @@ static void intel_th_gth_enable(struct intel_th_device *thdev,
 	intel_th_gth_start(gth, output);
 }
 
+/**
+ * intel_th_gth_switch() - execute a switch sequence
+ * @thdev:	GTH device
+ * @output:	output device's descriptor
+ *
+ * This will execute a switch sequence that will trigger a switch window
+ * when tracing to MSC in multi-block mode.
+ */
+static void intel_th_gth_switch(struct intel_th_device *thdev,
+				struct intel_th_output *output)
+{
+	struct gth_device *gth = dev_get_drvdata(&thdev->dev);
+	unsigned long count;
+	u32 reg;
+
+	/* trigger */
+	iowrite32(0, gth->base + REG_CTS_CTL);
+	iowrite32(CTS_CTL_SEQUENCER_ENABLE, gth->base + REG_CTS_CTL);
+	/* wait on trigger status */
+	for (reg = 0, count = CTS_TRIG_WAITLOOP_DEPTH;
+	     count && !(reg & BIT(4)); count--) {
+		reg = ioread32(gth->base + REG_CTS_STAT);
+		cpu_relax();
+	}
+	if (!count)
+		dev_dbg(&thdev->dev, "timeout waiting for CTS Trigger\n");
+
+	intel_th_gth_stop(gth, output, false);
+	intel_th_gth_start(gth, output);
+}
+
 /**
  * intel_th_gth_assign() - assign output device to a GTH output port
  * @thdev:	GTH device
@@ -777,6 +813,7 @@ static struct intel_th_driver intel_th_gth_driver = {
 	.unassign	= intel_th_gth_unassign,
 	.set_output	= intel_th_gth_set_output,
 	.enable		= intel_th_gth_enable,
+	.trig_switch	= intel_th_gth_switch,
 	.disable	= intel_th_gth_disable,
 	.driver	= {
 		.name	= "gth",
diff --git a/drivers/hwtracing/intel_th/gth.h b/drivers/hwtracing/intel_th/gth.h
index 6f2b0b930875..bfcc0fd01177 100644
--- a/drivers/hwtracing/intel_th/gth.h
+++ b/drivers/hwtracing/intel_th/gth.h
@@ -49,6 +49,12 @@ enum {
 	REG_GTH_SCRPD3		= 0xec, /* ScratchPad[3] */
 	REG_TSCU_TSUCTRL	= 0x2000, /* TSCU control register */
 	REG_TSCU_TSCUSTAT	= 0x2004, /* TSCU status register */
+
+	/* Common Capture Sequencer (CTS) registers */
+	REG_CTS_C0S0_EN		= 0x30c0, /* clause_event_enable_c0s0 */
+	REG_CTS_C0S0_ACT	= 0x3180, /* clause_action_control_c0s0 */
+	REG_CTS_STAT		= 0x32a0, /* cts_status */
+	REG_CTS_CTL		= 0x32a4, /* cts_control */
 };
 
 /* waiting for Pipeline Empty bit(s) to assert for GTH */
@@ -57,4 +63,17 @@ enum {
 #define TSUCTRL_CTCRESYNC	BIT(0)
 #define TSCUSTAT_CTCSYNCING	BIT(1)
 
+/* waiting for Trigger status to assert for CTS */
+#define CTS_TRIG_WAITLOOP_DEPTH	10000
+
+#define CTS_EVENT_ENABLE_IF_ANYTHING	BIT(31)
+#define CTS_ACTION_CONTROL_STATE_OFF	27
+#define CTS_ACTION_CONTROL_SET_STATE(x)	\
+	(((x) & 0x1f) << CTS_ACTION_CONTROL_STATE_OFF)
+#define CTS_ACTION_CONTROL_TRIGGER	BIT(4)
+
+#define CTS_STATE_IDLE			0x10u
+
+#define CTS_CTL_SEQUENCER_ENABLE	BIT(0)
+
 #endif /* __INTEL_TH_GTH_H__ */
diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h
index 74a6a4071e7f..0df480072b6c 100644
--- a/drivers/hwtracing/intel_th/intel_th.h
+++ b/drivers/hwtracing/intel_th/intel_th.h
@@ -164,6 +164,8 @@ struct intel_th_driver {
 					    struct intel_th_device *othdev);
 	void			(*enable)(struct intel_th_device *thdev,
 					  struct intel_th_output *output);
+	void			(*trig_switch)(struct intel_th_device *thdev,
+					       struct intel_th_output *output);
 	void			(*disable)(struct intel_th_device *thdev,
 					   struct intel_th_output *output);
 	/* output ops */
@@ -228,6 +230,7 @@ int intel_th_driver_register(struct intel_th_driver *thdrv);
 void intel_th_driver_unregister(struct intel_th_driver *thdrv);
 
 int intel_th_trace_enable(struct intel_th_device *thdev);
+int intel_th_trace_switch(struct intel_th_device *thdev);
 int intel_th_trace_disable(struct intel_th_device *thdev);
 int intel_th_set_output(struct intel_th_device *thdev,
 			unsigned int master);
@@ -308,6 +311,9 @@ enum {
 	REG_TSCU_OFFSET		= 0x2000,
 	REG_TSCU_LENGTH		= 0x1000,
 
+	REG_CTS_OFFSET		= 0x3000,
+	REG_CTS_LENGTH		= 0x1000,
+
 	/* Software Trace Hub (STH) [0x4000..0x4fff] */
 	REG_STH_OFFSET		= 0x4000,
 	REG_STH_LENGTH		= 0x2000,
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [GIT PULL 16/22] intel_th: msu: Correct the block wrap detection
  2019-05-03  8:44 [GIT PULL 00/22] intel_th: Updates for v5.2 Alexander Shishkin
                   ` (14 preceding siblings ...)
  2019-05-03  8:44 ` [GIT PULL 15/22] intel_th: Add switch triggering support Alexander Shishkin
@ 2019-05-03  8:44 ` Alexander Shishkin
  2019-05-03  8:44 ` [GIT PULL 17/22] intel_th: msu: Add a sysfs attribute to trigger window switch Alexander Shishkin
                   ` (5 subsequent siblings)
  21 siblings, 0 replies; 26+ messages in thread
From: Alexander Shishkin @ 2019-05-03  8:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, Alexander Shishkin

In multi window mode the MSU will set "window wrap" bit to indicate block
wrapping as well. Take this into account when checking data blocks.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
 drivers/hwtracing/intel_th/msu.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/hwtracing/intel_th/msu.h b/drivers/hwtracing/intel_th/msu.h
index e8cb819a3804..574c16004cb2 100644
--- a/drivers/hwtracing/intel_th/msu.h
+++ b/drivers/hwtracing/intel_th/msu.h
@@ -95,7 +95,7 @@ static inline unsigned long msc_data_sz(struct msc_block_desc *bdesc)
 
 static inline bool msc_block_wrapped(struct msc_block_desc *bdesc)
 {
-	if (bdesc->hw_tag & MSC_HW_TAG_BLOCKWRAP)
+	if (bdesc->hw_tag & (MSC_HW_TAG_BLOCKWRAP | MSC_HW_TAG_WINWRAP))
 		return true;
 
 	return false;
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [GIT PULL 17/22] intel_th: msu: Add a sysfs attribute to trigger window switch
  2019-05-03  8:44 [GIT PULL 00/22] intel_th: Updates for v5.2 Alexander Shishkin
                   ` (15 preceding siblings ...)
  2019-05-03  8:44 ` [GIT PULL 16/22] intel_th: msu: Correct the block wrap detection Alexander Shishkin
@ 2019-05-03  8:44 ` Alexander Shishkin
  2019-05-03  8:44 ` [GIT PULL 18/22] intel_th: msu: Add current window tracking Alexander Shishkin
                   ` (4 subsequent siblings)
  21 siblings, 0 replies; 26+ messages in thread
From: Alexander Shishkin @ 2019-05-03  8:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, Alexander Shishkin

Now that we have the means to trigger a window switch for the MSU trace
store, add a sysfs file to allow triggering it from userspace.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
 .../testing/sysfs-bus-intel_th-devices-msc    |  8 ++++++
 drivers/hwtracing/intel_th/msu.c              | 28 +++++++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-intel_th-devices-msc b/Documentation/ABI/testing/sysfs-bus-intel_th-devices-msc
index b940c5d91cf7..f54ae244f3f1 100644
--- a/Documentation/ABI/testing/sysfs-bus-intel_th-devices-msc
+++ b/Documentation/ABI/testing/sysfs-bus-intel_th-devices-msc
@@ -30,4 +30,12 @@ Description:	(RW) Configure MSC buffer size for "single" or "multi" modes.
 		there are no active users and tracing is not enabled) and then
 		allocates a new one.
 
+What:		/sys/bus/intel_th/devices/<intel_th_id>-msc<msc-id>/win_switch
+Date:		May 2019
+KernelVersion:	5.2
+Contact:	Alexander Shishkin <alexander.shishkin@linux.intel.com>
+Description:	(RW) Trigger window switch for the MSC's buffer, in
+		multi-window mode. In "multi" mode, accepts writes of "1", thereby
+		triggering a window switch for the buffer. Returns an error in any
+		other operating mode or attempts to write something other than "1".
 
diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c
index 01408a0e2458..089fd1f90a9f 100644
--- a/drivers/hwtracing/intel_th/msu.c
+++ b/drivers/hwtracing/intel_th/msu.c
@@ -1592,10 +1592,38 @@ nr_pages_store(struct device *dev, struct device_attribute *attr,
 
 static DEVICE_ATTR_RW(nr_pages);
 
+static ssize_t
+win_switch_store(struct device *dev, struct device_attribute *attr,
+		 const char *buf, size_t size)
+{
+	struct msc *msc = dev_get_drvdata(dev);
+	unsigned long val;
+	int ret;
+
+	ret = kstrtoul(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	if (val != 1)
+		return -EINVAL;
+
+	mutex_lock(&msc->buf_mutex);
+	if (msc->mode != MSC_MODE_MULTI)
+		ret = -ENOTSUPP;
+	else
+		ret = intel_th_trace_switch(msc->thdev);
+	mutex_unlock(&msc->buf_mutex);
+
+	return ret ? ret : size;
+}
+
+static DEVICE_ATTR_WO(win_switch);
+
 static struct attribute *msc_output_attrs[] = {
 	&dev_attr_wrap.attr,
 	&dev_attr_mode.attr,
 	&dev_attr_nr_pages.attr,
+	&dev_attr_win_switch.attr,
 	NULL,
 };
 
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [GIT PULL 18/22] intel_th: msu: Add current window tracking
  2019-05-03  8:44 [GIT PULL 00/22] intel_th: Updates for v5.2 Alexander Shishkin
                   ` (16 preceding siblings ...)
  2019-05-03  8:44 ` [GIT PULL 17/22] intel_th: msu: Add a sysfs attribute to trigger window switch Alexander Shishkin
@ 2019-05-03  8:44 ` Alexander Shishkin
  2019-05-03  8:44 ` [GIT PULL 19/22] intel_th: msu: Introduce buffer driver interface Alexander Shishkin
                   ` (3 subsequent siblings)
  21 siblings, 0 replies; 26+ messages in thread
From: Alexander Shishkin @ 2019-05-03  8:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, Alexander Shishkin

Now that we have a way to switch between MSC buffer windows, add code to
track the current window. The hardware register NWSA that contains the
address of the next window is unfortunately not always usable, and since
the driver has full control of the window switching, there is no reason
not to keep this on the software side.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
 drivers/hwtracing/intel_th/msu.c | 79 ++++++++++++++++++++------------
 1 file changed, 49 insertions(+), 30 deletions(-)

diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c
index 089fd1f90a9f..2916de5651aa 100644
--- a/drivers/hwtracing/intel_th/msu.c
+++ b/drivers/hwtracing/intel_th/msu.c
@@ -77,6 +77,7 @@ struct msc_iter {
  * @thdev:		intel_th_device pointer
  * @win_list:		list of windows in multiblock mode
  * @single_sgt:		single mode buffer
+ * @cur_win:		current window
  * @nr_pages:		total number of pages allocated for this buffer
  * @single_sz:		amount of data in single mode
  * @single_wrap:	single mode wrap occurred
@@ -99,6 +100,7 @@ struct msc {
 
 	struct list_head	win_list;
 	struct sg_table		single_sgt;
+	struct msc_window	*cur_win;
 	unsigned long		nr_pages;
 	unsigned long		single_sz;
 	unsigned int		single_wrap : 1;
@@ -159,6 +161,31 @@ msc_win_bpfn(struct msc_window *win, unsigned int block)
 	return msc_win_baddr(win, block) >> PAGE_SHIFT;
 }
 
+/**
+ * msc_is_last_win() - check if a window is the last one for a given MSC
+ * @win:	window
+ * Return:	true if @win is the last window in MSC's multiblock buffer
+ */
+static inline bool msc_is_last_win(struct msc_window *win)
+{
+	return win->entry.next == &win->msc->win_list;
+}
+
+/**
+ * msc_next_window() - return next window in the multiblock buffer
+ * @win:	current window
+ *
+ * Return:	window following the current one
+ */
+static struct msc_window *msc_next_window(struct msc_window *win)
+{
+	if (msc_is_last_win(win))
+		return list_first_entry(&win->msc->win_list, struct msc_window,
+					entry);
+
+	return list_next_entry(win, entry);
+}
+
 /**
  * msc_oldest_window() - locate the window with oldest data
  * @msc:	MSC device
@@ -170,9 +197,7 @@ msc_win_bpfn(struct msc_window *win, unsigned int block)
  */
 static struct msc_window *msc_oldest_window(struct msc *msc)
 {
-	struct msc_window *win;
-	u32 reg = ioread32(msc->reg_base + REG_MSU_MSC0NWSA);
-	unsigned long win_addr = (unsigned long)reg << PAGE_SHIFT;
+	struct msc_window *win, *next = msc_next_window(msc->cur_win);
 	unsigned int found = 0;
 
 	if (list_empty(&msc->win_list))
@@ -184,7 +209,7 @@ static struct msc_window *msc_oldest_window(struct msc *msc)
 	 * something like 2, in which case we're good
 	 */
 	list_for_each_entry(win, &msc->win_list, entry) {
-		if (sg_dma_address(win->sgt.sgl) == win_addr)
+		if (win == next)
 			found++;
 
 		/* skip the empty ones */
@@ -227,31 +252,6 @@ static unsigned int msc_win_oldest_block(struct msc_window *win)
 	return 0;
 }
 
-/**
- * msc_is_last_win() - check if a window is the last one for a given MSC
- * @win:	window
- * Return:	true if @win is the last window in MSC's multiblock buffer
- */
-static inline bool msc_is_last_win(struct msc_window *win)
-{
-	return win->entry.next == &win->msc->win_list;
-}
-
-/**
- * msc_next_window() - return next window in the multiblock buffer
- * @win:	current window
- *
- * Return:	window following the current one
- */
-static struct msc_window *msc_next_window(struct msc_window *win)
-{
-	if (msc_is_last_win(win))
-		return list_first_entry(&win->msc->win_list, struct msc_window,
-					entry);
-
-	return list_next_entry(win, entry);
-}
-
 static struct msc_block_desc *msc_iter_bdesc(struct msc_iter *iter)
 {
 	return msc_win_block(iter->win, iter->block);
@@ -830,6 +830,7 @@ static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_pages)
 	if (list_empty(&msc->win_list)) {
 		msc->base = msc_win_block(win, 0);
 		msc->base_addr = msc_win_baddr(win, 0);
+		msc->cur_win = win;
 	}
 
 	list_add_tail(&win->entry, &msc->win_list);
@@ -1403,6 +1404,24 @@ static int intel_th_msc_init(struct msc *msc)
 	return 0;
 }
 
+static void msc_win_switch(struct msc *msc)
+{
+	struct msc_window *last, *first;
+
+	first = list_first_entry(&msc->win_list, struct msc_window, entry);
+	last = list_last_entry(&msc->win_list, struct msc_window, entry);
+
+	if (msc_is_last_win(msc->cur_win))
+		msc->cur_win = first;
+	else
+		msc->cur_win = list_next_entry(msc->cur_win, entry);
+
+	msc->base = msc_win_block(msc->cur_win, 0);
+	msc->base_addr = msc_win_baddr(msc->cur_win, 0);
+
+	intel_th_trace_switch(msc->thdev);
+}
+
 static irqreturn_t intel_th_msc_interrupt(struct intel_th_device *thdev)
 {
 	struct msc *msc = dev_get_drvdata(&thdev->dev);
@@ -1611,7 +1630,7 @@ win_switch_store(struct device *dev, struct device_attribute *attr,
 	if (msc->mode != MSC_MODE_MULTI)
 		ret = -ENOTSUPP;
 	else
-		ret = intel_th_trace_switch(msc->thdev);
+		msc_win_switch(msc);
 	mutex_unlock(&msc->buf_mutex);
 
 	return ret ? ret : size;
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [GIT PULL 19/22] intel_th: msu: Introduce buffer driver interface
  2019-05-03  8:44 [GIT PULL 00/22] intel_th: Updates for v5.2 Alexander Shishkin
                   ` (17 preceding siblings ...)
  2019-05-03  8:44 ` [GIT PULL 18/22] intel_th: msu: Add current window tracking Alexander Shishkin
@ 2019-05-03  8:44 ` Alexander Shishkin
  2019-05-03 16:19   ` Greg Kroah-Hartman
  2019-05-03  8:44 ` [GIT PULL 20/22] intel_th: msu: Add a sysfs attribute showing possible modes Alexander Shishkin
                   ` (2 subsequent siblings)
  21 siblings, 1 reply; 26+ messages in thread
From: Alexander Shishkin @ 2019-05-03  8:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, Alexander Shishkin

Introduces a concept of buffer drivers, which is a mechanism for creating
trace sinks that would receive trace data from MSC buffers and transfer it
elsewhere.

A buffer driver can implement its own window allocation/deallocation if
it has to. It must provide a callback that's used to notify it when a
window fills up, so that it can then start a DMA transaction from that
window 'elsewhere'. This window remains in a 'locked' state and won't be
used for storing new trace data until the buffer driver 'unlocks' it with
a provided API call, at which point the window can be used again for
storing trace data.

This relies on a functional "last block" interrupt, so not all versions of
Trace Hub can use this feature.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
 .../testing/sysfs-bus-intel_th-devices-msc    |   3 +-
 MAINTAINERS                                   |   1 +
 drivers/hwtracing/intel_th/msu.c              | 448 ++++++++++++++++--
 drivers/hwtracing/intel_th/msu.h              |  23 +-
 include/linux/intel_th.h                      |  67 +++
 5 files changed, 485 insertions(+), 57 deletions(-)
 create mode 100644 include/linux/intel_th.h

diff --git a/Documentation/ABI/testing/sysfs-bus-intel_th-devices-msc b/Documentation/ABI/testing/sysfs-bus-intel_th-devices-msc
index f54ae244f3f1..7da00601afdc 100644
--- a/Documentation/ABI/testing/sysfs-bus-intel_th-devices-msc
+++ b/Documentation/ABI/testing/sysfs-bus-intel_th-devices-msc
@@ -12,7 +12,8 @@ Description:	(RW) Configure MSC operating mode:
 		  - "single", for contiguous buffer mode (high-order alloc);
 		  - "multi", for multiblock mode;
 		  - "ExI", for DCI handler mode;
-		  - "debug", for debug mode.
+		  - "debug", for debug mode;
+		  - any of the currently loaded buffer drivers.
 		If operating mode changes, existing buffer is deallocated,
 		provided there are no active users and tracing is not enabled,
 		otherwise the write will fail.
diff --git a/MAINTAINERS b/MAINTAINERS
index e17ebf70b548..69d845f5401b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8027,6 +8027,7 @@ M:	Alexander Shishkin <alexander.shishkin@linux.intel.com>
 S:	Supported
 F:	Documentation/trace/intel_th.rst
 F:	drivers/hwtracing/intel_th/
+F:	include/linux/intel_th.h
 
 INTEL(R) TRUSTED EXECUTION TECHNOLOGY (TXT)
 M:	Ning Sun <ning.sun@intel.com>
diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c
index 2916de5651aa..04bfe4dbf325 100644
--- a/drivers/hwtracing/intel_th/msu.c
+++ b/drivers/hwtracing/intel_th/msu.c
@@ -17,12 +17,14 @@
 #include <linux/mm.h>
 #include <linux/fs.h>
 #include <linux/io.h>
+#include <linux/workqueue.h>
 #include <linux/dma-mapping.h>
 
 #ifdef CONFIG_X86
 #include <asm/set_memory.h>
 #endif
 
+#include <linux/intel_th.h>
 #include "intel_th.h"
 #include "msu.h"
 
@@ -32,17 +34,21 @@
  * struct msc_window - multiblock mode window descriptor
  * @entry:	window list linkage (msc::win_list)
  * @pgoff:	page offset into the buffer that this window starts at
+ * @lockout:	lockout state, see comment below
  * @nr_blocks:	number of blocks in this window (<= @nr_pages)
  * @nr_pages:	number of pages in this window
- * @sgt:	array of block descriptors
+ * @_sgt:	array of block descriptors
+ * @sgt:	pointer to @sgt or whatever bdrv allocates
  */
 struct msc_window {
 	struct list_head	entry;
 	unsigned long		pgoff;
+	atomic_t		lockout;
 	unsigned int		nr_blocks;
 	unsigned int		nr_pages;
 	struct msc		*msc;
-	struct sg_table		sgt;
+	struct sg_table		_sgt;
+	struct sg_table		*sgt;
 };
 
 /**
@@ -98,6 +104,10 @@ struct msc {
 	void __iomem		*msu_base;
 	struct intel_th_device	*thdev;
 
+	const struct msu_buffer_driver	*bdrv;
+	void				*bdrv_priv;
+
+	struct work_struct	work;
 	struct list_head	win_list;
 	struct sg_table		single_sgt;
 	struct msc_window	*cur_win;
@@ -124,6 +134,110 @@ struct msc {
 	unsigned int		index;
 };
 
+/*
+ * Lockout state transitions:
+ *   READY -> INUSE -+-> LOCKED -+-> READY -> etc.
+ *		     \-----------/
+ * WIN_READY:	window can be used by HW
+ * WIN_INUSE:	window is in use
+ * WIN_LOCKED:	window is filled up and is being processed by the buffer driver
+ *
+ * All state transitions happen automatically, except for the LOCKED->READY,
+ * which needs to be signalled by the buffer driver by calling
+ * intel_th_msc_window_unlock().
+ *
+ * When the interrupt handler has to switch to the next window, it checks
+ * whether it's READY, and if it is, it performs the switch and tracing
+ * continues. If it's LOCKED, it stops the trace.
+ */
+enum {
+	WIN_READY = 0,
+	WIN_INUSE,
+	WIN_LOCKED
+};
+
+static LIST_HEAD(msu_buffer_list);
+static struct mutex msu_buffer_mutex;
+
+struct msu_buffer {
+	struct list_head		entry;
+	const struct msu_buffer_driver	*bdrv;
+};
+
+static struct msu_buffer *__msu_buffer_find(const char *name)
+{
+	struct msu_buffer *buf;
+
+	list_for_each_entry(buf, &msu_buffer_list, entry) {
+		if (!strcmp(buf->bdrv->name, name))
+			return buf;
+	}
+
+	return NULL;
+}
+
+static const struct msu_buffer_driver *
+__msu_buffer_driver_find(const char *name)
+{
+	struct msu_buffer *buf = __msu_buffer_find(name);
+
+	return buf ? buf->bdrv : NULL;
+}
+
+static const struct msu_buffer_driver *
+msu_buffer_driver_get(const char *name)
+{
+	const struct msu_buffer_driver *bdrv;
+
+	mutex_lock(&msu_buffer_mutex);
+	bdrv = __msu_buffer_driver_find(name);
+	if (bdrv && !try_module_get(bdrv->owner))
+		bdrv = NULL;
+	mutex_unlock(&msu_buffer_mutex);
+
+	return bdrv ? : NULL;
+}
+
+int intel_th_msu_buffer_register(const struct msu_buffer_driver *bdrv)
+{
+	struct msu_buffer *buf;
+	int ret = -EEXIST;
+
+	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	mutex_lock(&msu_buffer_mutex);
+	if (__msu_buffer_driver_find(bdrv->name))
+		goto out;
+
+	buf->bdrv = bdrv;
+	list_add_tail(&buf->entry, &msu_buffer_list);
+	ret = 0;
+out:
+	mutex_unlock(&msu_buffer_mutex);
+
+	if (ret)
+		kfree(buf);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(intel_th_msu_buffer_register);
+
+void intel_th_msu_buffer_unregister(const struct msu_buffer_driver *bdrv)
+{
+	struct msu_buffer *buf;
+
+	mutex_lock(&msu_buffer_mutex);
+	buf = __msu_buffer_find(bdrv->name);
+	if (buf) {
+		list_del(&buf->entry);
+		kfree(buf);
+	}
+	mutex_unlock(&msu_buffer_mutex);
+}
+EXPORT_SYMBOL_GPL(intel_th_msu_buffer_unregister);
+
 static inline bool msc_block_is_empty(struct msc_block_desc *bdesc)
 {
 	/* header hasn't been written */
@@ -140,19 +254,19 @@ static inline bool msc_block_is_empty(struct msc_block_desc *bdesc)
 static inline struct msc_block_desc *
 msc_win_block(struct msc_window *win, unsigned int block)
 {
-	return sg_virt(&win->sgt.sgl[block]);
+	return sg_virt(&win->sgt->sgl[block]);
 }
 
 static inline size_t
 msc_win_actual_bsz(struct msc_window *win, unsigned int block)
 {
-	return win->sgt.sgl[block].length;
+	return win->sgt->sgl[block].length;
 }
 
 static inline dma_addr_t
 msc_win_baddr(struct msc_window *win, unsigned int block)
 {
-	return sg_dma_address(&win->sgt.sgl[block]);
+	return sg_dma_address(&win->sgt->sgl[block]);
 }
 
 static inline unsigned long
@@ -187,42 +301,105 @@ static struct msc_window *msc_next_window(struct msc_window *win)
 }
 
 /**
- * msc_oldest_window() - locate the window with oldest data
+ * msc_find_window() - find a window matching a given sg_table
  * @msc:	MSC device
- *
- * This should only be used in multiblock mode. Caller should hold the
- * msc::user_count reference.
- *
- * Return:	the oldest window with valid data
+ * @sgt:	SG table of the window
+ * @nonempty:	skip over empty windows
+ * Return:	MSC window structure pointer or NULL if the window
+ *		could not be found.
  */
-static struct msc_window *msc_oldest_window(struct msc *msc)
+static struct msc_window *
+msc_find_window(struct msc *msc, struct sg_table *sgt, bool nonempty)
 {
-	struct msc_window *win, *next = msc_next_window(msc->cur_win);
+	struct msc_window *win;
 	unsigned int found = 0;
 
 	if (list_empty(&msc->win_list))
 		return NULL;
 
+	if (!sgt) {
+		if (!nonempty)
+			return msc->cur_win;
+
+		sgt = msc->cur_win->sgt;
+	}
+
 	/*
 	 * we might need a radix tree for this, depending on how
 	 * many windows a typical user would allocate; ideally it's
 	 * something like 2, in which case we're good
 	 */
 	list_for_each_entry(win, &msc->win_list, entry) {
-		if (win == next)
+		if (win->sgt == sgt)
 			found++;
 
 		/* skip the empty ones */
-		if (msc_block_is_empty(msc_win_block(win, 0)))
+		if (nonempty && msc_block_is_empty(msc_win_block(win, 0)))
 			continue;
 
 		if (found)
 			return win;
 	}
 
+	return NULL;
+}
+
+/**
+ * msc_oldest_window() - locate the window with oldest data
+ * @msc:	MSC device
+ *
+ * This should only be used in multiblock mode. Caller should hold the
+ * msc::user_count reference.
+ *
+ * Return:	the oldest window with valid data
+ */
+static struct msc_window *msc_oldest_window(struct msc *msc)
+{
+	struct msc_window *win;
+
+	if (list_empty(&msc->win_list))
+		return NULL;
+
+	win = msc_find_window(msc, msc_next_window(msc->cur_win)->sgt, true);
+	if (win)
+		return win;
+
 	return list_first_entry(&msc->win_list, struct msc_window, entry);
 }
 
+static size_t msc_win_total_sz(struct msc_window *win)
+{
+	unsigned int blk;
+	size_t size = 0;
+
+	for (blk = 0; blk < win->nr_blocks; blk++) {
+		struct msc_block_desc *bdesc = msc_win_block(win, blk);
+
+		if (msc_block_wrapped(bdesc))
+			return win->nr_pages << PAGE_SHIFT;
+
+		size += msc_total_sz(bdesc);
+		if (msc_block_last_written(bdesc))
+			break;
+	}
+
+	return size;
+}
+
+static unsigned int msc_win_newest_block(struct msc_window *win)
+{
+	unsigned int blk;
+
+	for (blk = 0; blk < win->nr_blocks; blk++) {
+		struct msc_block_desc *bdesc = msc_win_block(win, blk);
+
+		if (msc_block_last_written(bdesc))
+			return blk;
+	}
+
+	return 0;
+}
+
 /**
  * msc_win_oldest_block() - locate the oldest block in a given window
  * @win:	window to look at
@@ -231,7 +408,6 @@ static struct msc_window *msc_oldest_window(struct msc *msc)
  */
 static unsigned int msc_win_oldest_block(struct msc_window *win)
 {
-	unsigned int blk;
 	struct msc_block_desc *bdesc = msc_win_block(win, 0);
 
 	/* without wrapping, first block is the oldest */
@@ -242,14 +418,7 @@ static unsigned int msc_win_oldest_block(struct msc_window *win)
 	 * with wrapping, last written block contains both the newest and the
 	 * oldest data for this window.
 	 */
-	for (blk = 0; blk < win->nr_blocks; blk++) {
-		bdesc = msc_win_block(win, blk);
-
-		if (msc_block_last_written(bdesc))
-			return blk;
-	}
-
-	return 0;
+	return msc_win_newest_block(win);
 }
 
 static struct msc_block_desc *msc_iter_bdesc(struct msc_iter *iter)
@@ -501,6 +670,9 @@ static int intel_th_msu_init(struct msc *msc)
 	if (!msc->do_irq)
 		return 0;
 
+	if (!msc->bdrv)
+		return 0;
+
 	mintctl = ioread32(msc->msu_base + REG_MSU_MINTCTL);
 	mintctl |= msc->index ? M1BLIE : M0BLIE;
 	iowrite32(mintctl, msc->msu_base + REG_MSU_MINTCTL);
@@ -528,6 +700,27 @@ static void intel_th_msu_deinit(struct msc *msc)
 	iowrite32(mintctl, msc->msu_base + REG_MSU_MINTCTL);
 }
 
+static int msc_win_set_lockout(struct msc_window *win, int expect, int new)
+{
+	int old;
+
+	if (!win->msc->bdrv)
+		return 0;
+
+	old = atomic_cmpxchg(&win->lockout, expect, new);
+	if (expect == WIN_READY && old == WIN_LOCKED)
+		return -EBUSY;
+
+	/* from intel_th_msc_window_unlock(), don't warn if not locked */
+	if (expect == WIN_LOCKED && old == new)
+		return 0;
+
+	if (WARN_ONCE(old != expect, "expected lockout state %d, got %d\n",
+		      expect, old))
+		return -EINVAL;
+
+	return 0;
+}
 /**
  * msc_configure() - set up MSC hardware
  * @msc:	the MSC device to configure
@@ -545,8 +738,16 @@ static int msc_configure(struct msc *msc)
 	if (msc->mode > MSC_MODE_MULTI)
 		return -ENOTSUPP;
 
-	if (msc->mode == MSC_MODE_MULTI)
+	if (msc->mode == MSC_MODE_MULTI) {
+		/* Window allocation path makes sure this doesn't happen */
+		if (WARN_ON_ONCE(!msc->cur_win))
+			return -EINVAL;
+
+		if (msc_win_set_lockout(msc->cur_win, WIN_READY, WIN_INUSE))
+			return -EBUSY;
+
 		msc_buffer_clear_hw_header(msc);
+	}
 
 	reg = msc->base_addr >> PAGE_SHIFT;
 	iowrite32(reg, msc->reg_base + REG_MSU_MSC0BAR);
@@ -568,10 +769,14 @@ static int msc_configure(struct msc *msc)
 
 	iowrite32(reg, msc->reg_base + REG_MSU_MSC0CTL);
 
+	intel_th_msu_init(msc);
+
 	msc->thdev->output.multiblock = msc->mode == MSC_MODE_MULTI;
 	intel_th_trace_enable(msc->thdev);
 	msc->enabled = 1;
 
+	if (msc->bdrv && msc->bdrv->activate)
+		msc->bdrv->activate(msc->bdrv_priv);
 
 	return 0;
 }
@@ -585,10 +790,17 @@ static int msc_configure(struct msc *msc)
  */
 static void msc_disable(struct msc *msc)
 {
+	struct msc_window *win = msc->cur_win;
 	u32 reg;
 
 	lockdep_assert_held(&msc->buf_mutex);
 
+	if (msc->mode == MSC_MODE_MULTI)
+		msc_win_set_lockout(win, WIN_INUSE, WIN_LOCKED);
+
+	if (msc->bdrv && msc->bdrv->deactivate)
+		msc->bdrv->deactivate(msc->bdrv_priv);
+	intel_th_msu_deinit(msc);
 	intel_th_trace_disable(msc->thdev);
 
 	if (msc->mode == MSC_MODE_SINGLE) {
@@ -604,6 +816,11 @@ static void msc_disable(struct msc *msc)
 	reg = ioread32(msc->reg_base + REG_MSU_MSC0CTL);
 	reg &= ~MSC_EN;
 	iowrite32(reg, msc->reg_base + REG_MSU_MSC0CTL);
+
+	if (msc->bdrv && msc->bdrv->ready)
+		msc->bdrv->ready(msc->bdrv_priv, win->sgt,
+				 msc_win_total_sz(win));
+
 	msc->enabled = 0;
 
 	iowrite32(0, msc->reg_base + REG_MSU_MSC0BAR);
@@ -614,6 +831,11 @@ static void msc_disable(struct msc *msc)
 
 	reg = ioread32(msc->reg_base + REG_MSU_MSC0STS);
 	dev_dbg(msc_dev(msc), "MSCnSTS: %08x\n", reg);
+
+	reg = ioread32(msc->reg_base + REG_MSU_MSUSTS);
+	reg &= msc->index ? MSUSTS_MSC1BLAST : MSUSTS_MSC0BLAST;
+	iowrite32(reg, msc->reg_base + REG_MSU_MSUSTS);
+	dev_dbg(msc_dev(msc), "MSUSTS: %08x\n", reg);
 }
 
 static int intel_th_msc_activate(struct intel_th_device *thdev)
@@ -748,11 +970,11 @@ static int __msc_buffer_win_alloc(struct msc_window *win,
 	void *block;
 	int i, ret;
 
-	ret = sg_alloc_table(&win->sgt, nr_blocks, GFP_KERNEL);
+	ret = sg_alloc_table(win->sgt, nr_blocks, GFP_KERNEL);
 	if (ret)
 		return -ENOMEM;
 
-	for_each_sg(win->sgt.sgl, sg_ptr, nr_blocks, i) {
+	for_each_sg(win->sgt->sgl, sg_ptr, nr_blocks, i) {
 		block = dma_alloc_coherent(msc_dev(win->msc)->parent->parent,
 					  PAGE_SIZE, &sg_dma_address(sg_ptr),
 					  GFP_KERNEL);
@@ -770,7 +992,7 @@ static int __msc_buffer_win_alloc(struct msc_window *win,
 				  msc_win_block(win, i),
 				  msc_win_baddr(win, i));
 
-	sg_free_table(&win->sgt);
+	sg_free_table(win->sgt);
 
 	return -ENOMEM;
 }
@@ -805,6 +1027,7 @@ static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_pages)
 		return -ENOMEM;
 
 	win->msc = msc;
+	win->sgt = &win->_sgt;
 
 	if (!list_empty(&msc->win_list)) {
 		struct msc_window *prev = list_last_entry(&msc->win_list,
@@ -814,8 +1037,13 @@ static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_pages)
 		win->pgoff = prev->pgoff + prev->nr_pages;
 	}
 
-	ret = __msc_buffer_win_alloc(win, nr_pages);
-	if (ret < 0)
+	if (msc->bdrv && msc->bdrv->alloc_window)
+		ret = msc->bdrv->alloc_window(msc->bdrv_priv, &win->sgt,
+					      nr_pages << PAGE_SHIFT);
+	else
+		ret = __msc_buffer_win_alloc(win, nr_pages);
+
+	if (ret <= 0)
 		goto err_nomem;
 
 #ifdef CONFIG_X86
@@ -826,6 +1054,7 @@ static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_pages)
 
 	win->nr_blocks = ret;
 	win->nr_pages = nr_pages;
+	atomic_set(&win->lockout, WIN_READY);
 
 	if (list_empty(&msc->win_list)) {
 		msc->base = msc_win_block(win, 0);
@@ -849,13 +1078,13 @@ static void __msc_buffer_win_free(struct msc *msc, struct msc_window *win)
 	int i;
 
 	for (i = 0; i < win->nr_blocks; i++) {
-		struct page *page = sg_page(&win->sgt.sgl[i]);
+		struct page *page = sg_page(&win->sgt->sgl[i]);
 
 		page->mapping = NULL;
 		dma_free_coherent(msc_dev(win->msc)->parent->parent, PAGE_SIZE,
 				  msc_win_block(win, i), msc_win_baddr(win, i));
 	}
-	sg_free_table(&win->sgt);
+	sg_free_table(win->sgt);
 }
 
 /**
@@ -884,7 +1113,10 @@ static void msc_buffer_win_free(struct msc *msc, struct msc_window *win)
 		set_memory_wb((unsigned long)msc_win_block(win, i), 1);
 #endif
 
-	__msc_buffer_win_free(msc, win);
+	if (msc->bdrv && msc->bdrv->free_window)
+		msc->bdrv->free_window(msc->bdrv_priv, win->sgt);
+	else
+		__msc_buffer_win_free(msc, win);
 
 	kfree(win);
 }
@@ -1110,8 +1342,8 @@ static struct page *msc_buffer_get_page(struct msc *msc, unsigned long pgoff)
 	pgoff -= win->pgoff;
 
 	for (blk = 0; blk < win->nr_blocks; blk++) {
-		struct page *page = sg_page(&win->sgt.sgl[blk]);
-		size_t pgsz = win->sgt.sgl[blk].length >> PAGE_SHIFT;
+		struct page *page = sg_page(&win->sgt->sgl[blk]);
+		size_t pgsz = win->sgt->sgl[blk].length >> PAGE_SHIFT;
 
 		if (pgoff < pgsz)
 			return page + pgoff;
@@ -1422,18 +1654,77 @@ static void msc_win_switch(struct msc *msc)
 	intel_th_trace_switch(msc->thdev);
 }
 
+/*
+ * @dev:	MSC device to which this relates
+ * @sgt:	window
+ */
+void intel_th_msc_window_unlock(struct device *dev, struct sg_table *sgt)
+{
+	struct msc *msc = dev_get_drvdata(dev);
+	struct msc_window *win = msc_find_window(msc, sgt, false);
+
+	if (!win)
+		return;
+
+	msc_win_set_lockout(win, WIN_LOCKED, WIN_READY);
+}
+EXPORT_SYMBOL_GPL(intel_th_msc_window_unlock);
+
+static void msc_work(struct work_struct *work)
+{
+	struct msc *msc = container_of(work, struct msc, work);
+
+	intel_th_msc_deactivate(msc->thdev);
+}
+
 static irqreturn_t intel_th_msc_interrupt(struct intel_th_device *thdev)
 {
 	struct msc *msc = dev_get_drvdata(&thdev->dev);
 	u32 msusts = ioread32(msc->msu_base + REG_MSU_MSUSTS);
 	u32 mask = msc->index ? MSUSTS_MSC1BLAST : MSUSTS_MSC0BLAST;
+	struct msc_window *win, *next_win;
+
+	if (!msc->do_irq || !msc->bdrv)
+		return IRQ_NONE;
 
 	if (!(msusts & mask)) {
 		if (msc->enabled)
-			return IRQ_HANDLED;
+			goto ack;
 		return IRQ_NONE;
 	}
 
+	msusts &= mask;
+
+	iowrite32(msusts, msc->msu_base + REG_MSU_MSUSTS);
+
+	if (!msc->enabled)
+		return IRQ_NONE;
+
+	/* grab the window before we do the switch */
+	win = msc->cur_win;
+	if (WARN_ON_ONCE(!win))
+		goto ack;
+	next_win = msc_next_window(win);
+	if (WARN_ON_ONCE(!next_win))
+		goto ack;
+
+	/* next window: if READY, proceed, if LOCKED, stop the trace */
+	if (msc_win_set_lockout(next_win, WIN_READY, WIN_INUSE)) {
+		schedule_work(&msc->work);
+		goto ack;
+	}
+
+	/* current window: INUSE -> LOCKED */
+	msc_win_set_lockout(win, WIN_INUSE, WIN_LOCKED);
+
+	msc_win_switch(msc);
+
+	if (msc->bdrv && msc->bdrv->ready)
+		msc->bdrv->ready(msc->bdrv_priv, win->sgt,
+				 msc_win_total_sz(win));
+ack:
+	iowrite32(msusts & mask, msc->msu_base + REG_MSU_MSUSTS);
+
 	return IRQ_HANDLED;
 }
 
@@ -1471,21 +1762,43 @@ wrap_store(struct device *dev, struct device_attribute *attr, const char *buf,
 
 static DEVICE_ATTR_RW(wrap);
 
+static void msc_buffer_unassign(struct msc *msc)
+{
+	lockdep_assert_held(&msc->buf_mutex);
+
+	if (!msc->bdrv)
+		return;
+
+	msc->bdrv->unassign(msc->bdrv_priv);
+	module_put(msc->bdrv->owner);
+	msc->bdrv_priv = NULL;
+	msc->bdrv = NULL;
+}
+
 static ssize_t
 mode_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
 	struct msc *msc = dev_get_drvdata(dev);
+	const char *mode = msc_mode[msc->mode];
+	ssize_t ret;
+
+	mutex_lock(&msc->buf_mutex);
+	if (msc->bdrv)
+		mode = msc->bdrv->name;
+	ret = scnprintf(buf, PAGE_SIZE, "%s\n", mode);
+	mutex_unlock(&msc->buf_mutex);
 
-	return scnprintf(buf, PAGE_SIZE, "%s\n", msc_mode[msc->mode]);
+	return ret;
 }
 
 static ssize_t
 mode_store(struct device *dev, struct device_attribute *attr, const char *buf,
 	   size_t size)
 {
+	const struct msu_buffer_driver *bdrv = NULL;
 	struct msc *msc = dev_get_drvdata(dev);
 	size_t len = size;
-	char *cp;
+	char *cp, *mode;
 	int i, ret;
 
 	if (!capable(CAP_SYS_RAWIO))
@@ -1495,17 +1808,57 @@ mode_store(struct device *dev, struct device_attribute *attr, const char *buf,
 	if (cp)
 		len = cp - buf;
 
+	mode = kstrndup(buf, len, GFP_KERNEL);
 	for (i = 0; i < ARRAY_SIZE(msc_mode); i++)
-		if (!strncmp(msc_mode[i], buf, len))
+		if (!strcmp(msc_mode[i], mode))
 			goto found;
 
+	/* Buffer drivers only work with a usable IRQ */
+	if (!msc->do_irq) {
+		kfree(mode);
+		return -EINVAL;
+	}
+
+	bdrv = msu_buffer_driver_get(mode);
+	kfree(mode);
+	if (bdrv)
+		goto found;
+
 	return -EINVAL;
 
 found:
 	mutex_lock(&msc->buf_mutex);
+	ret = 0;
+	if (bdrv && bdrv == msc->bdrv) {
+		/* matches try_module_get() in  msu_buffer_driver_get() */
+		module_put(bdrv->owner);
+		goto unlock;
+	}
+
 	ret = msc_buffer_unlocked_free_unless_used(msc);
-	if (!ret)
-		msc->mode = i;
+	if (ret)
+		goto unlock;
+
+	if (bdrv) {
+		void *bdrv_priv = bdrv->assign(dev, &i);
+
+		if (!bdrv_priv) {
+			ret = -ENOMEM;
+			goto unlock;
+		}
+
+		msc_buffer_unassign(msc);
+		msc->bdrv_priv = bdrv_priv;
+		msc->bdrv = bdrv;
+	} else {
+		msc_buffer_unassign(msc);
+	}
+
+	msc->mode = i;
+
+unlock:
+	if (ret && bdrv)
+		module_put(bdrv->owner);
 	mutex_unlock(&msc->buf_mutex);
 
 	return ret ? ret : size;
@@ -1627,7 +1980,12 @@ win_switch_store(struct device *dev, struct device_attribute *attr,
 		return -EINVAL;
 
 	mutex_lock(&msc->buf_mutex);
-	if (msc->mode != MSC_MODE_MULTI)
+	/*
+	 * Window switch can only happen in the "multi" mode.
+	 * If a buffer driver is engaged, they have the full
+	 * control over window switching.
+	 */
+	if (msc->mode != MSC_MODE_MULTI || msc->bdrv)
 		ret = -ENOTSUPP;
 	else
 		msc_win_switch(msc);
@@ -1680,10 +2038,7 @@ static int intel_th_msc_probe(struct intel_th_device *thdev)
 	msc->reg_base = base + msc->index * 0x100;
 	msc->msu_base = base;
 
-	err = intel_th_msu_init(msc);
-	if (err)
-		return err;
-
+	INIT_WORK(&msc->work, msc_work);
 	err = intel_th_msc_init(msc);
 	if (err)
 		return err;
@@ -1699,7 +2054,6 @@ static void intel_th_msc_remove(struct intel_th_device *thdev)
 	int ret;
 
 	intel_th_msc_deactivate(thdev);
-	intel_th_msu_deinit(msc);
 
 	/*
 	 * Buffers should not be used at this point except if the
diff --git a/drivers/hwtracing/intel_th/msu.h b/drivers/hwtracing/intel_th/msu.h
index 574c16004cb2..acd808e316f9 100644
--- a/drivers/hwtracing/intel_th/msu.h
+++ b/drivers/hwtracing/intel_th/msu.h
@@ -44,14 +44,6 @@ enum {
 #define M0BLIE		BIT(16)
 #define M1BLIE		BIT(24)
 
-/* MSC operating modes (MSC_MODE) */
-enum {
-	MSC_MODE_SINGLE	= 0,
-	MSC_MODE_MULTI,
-	MSC_MODE_EXI,
-	MSC_MODE_DEBUG,
-};
-
 /* MSCnSTS bits */
 #define MSCSTS_WRAPSTAT	BIT(1)	/* Wrap occurred */
 #define MSCSTS_PLE	BIT(2)	/* Pipeline Empty */
@@ -93,6 +85,19 @@ static inline unsigned long msc_data_sz(struct msc_block_desc *bdesc)
 	return bdesc->valid_dw * 4 - MSC_BDESC;
 }
 
+static inline unsigned long msc_total_sz(struct msc_block_desc *bdesc)
+{
+	if (!bdesc->valid_dw)
+		return 0;
+
+	return bdesc->valid_dw * 4;
+}
+
+static inline unsigned long msc_block_sz(struct msc_block_desc *bdesc)
+{
+	return bdesc->block_sz * 64 - MSC_BDESC;
+}
+
 static inline bool msc_block_wrapped(struct msc_block_desc *bdesc)
 {
 	if (bdesc->hw_tag & (MSC_HW_TAG_BLOCKWRAP | MSC_HW_TAG_WINWRAP))
@@ -104,7 +109,7 @@ static inline bool msc_block_wrapped(struct msc_block_desc *bdesc)
 static inline bool msc_block_last_written(struct msc_block_desc *bdesc)
 {
 	if ((bdesc->hw_tag & MSC_HW_TAG_ENDBIT) ||
-	    (msc_data_sz(bdesc) != DATA_IN_PAGE))
+	    (msc_data_sz(bdesc) != msc_block_sz(bdesc)))
 		return true;
 
 	return false;
diff --git a/include/linux/intel_th.h b/include/linux/intel_th.h
new file mode 100644
index 000000000000..b00b7e1254d9
--- /dev/null
+++ b/include/linux/intel_th.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Intel(R) Trace Hub data structures for implementing buffer drivers.
+ *
+ * Copyright (C) 2019 Intel Corporation.
+ */
+
+#ifndef _INTEL_TH_H_
+#define _INTEL_TH_H_
+
+#include <linux/scatterlist.h>
+
+/* MSC operating modes (MSC_MODE) */
+enum {
+	MSC_MODE_SINGLE	= 0,
+	MSC_MODE_MULTI,
+	MSC_MODE_EXI,
+	MSC_MODE_DEBUG,
+};
+
+struct msu_buffer_driver {
+	const char	*name;
+	struct module	*owner;
+	/*
+	 * ->assign() called when buffer 'mode' is set to this driver
+	 *   (aka mode_store())
+	 * @device:	struct device * of the msc
+	 * @mode:	allows the driver to set HW mode (see the enum above)
+	 * Returns:	a pointer to a private structure associated with this
+	 *		msc or NULL in case of error. This private structure
+	 *		will then be passed into all other callbacks.
+	 */
+	void	*(*assign)(struct device *dev, int *mode);
+	/* ->unassign():	some other mode is selected, clean up */
+	void	(*unassign)(void *priv);
+	/*
+	 * ->alloc_window(): allocate memory for the window of a given
+	 *		size
+	 * @sgt:	pointer to sg_table, can be overridden by the buffer
+	 *		driver, or kept intact
+	 * Returns:	number of sg table entries <= number of pages;
+	 *		0 is treated as an allocation failure.
+	 */
+	int	(*alloc_window)(void *priv, struct sg_table **sgt,
+				size_t size);
+	void	(*free_window)(void *priv, struct sg_table *sgt);
+	/* ->activate():	trace has started */
+	void	(*activate)(void *priv);
+	/* ->deactivate():	trace is about to stop */
+	void	(*deactivate)(void *priv);
+	/*
+	 * ->ready():	window @sgt is filled up to the last block OR
+	 *		tracing is stopped by the user; this window contains
+	 *		@bytes data. The window in question transitions into
+	 *		the "LOCKED" state, indicating that it can't be used
+	 *		by hardware. To clear this state and make the window
+	 *		available to the hardware again, call
+	 *		intel_th_msc_window_unlock().
+	 */
+	int	(*ready)(void *priv, struct sg_table *sgt, size_t bytes);
+};
+
+int intel_th_msu_buffer_register(const struct msu_buffer_driver *bdrv);
+void intel_th_msu_buffer_unregister(const struct msu_buffer_driver *bdrv);
+void intel_th_msc_window_unlock(struct device *dev, struct sg_table *sgt);
+
+#endif /* _INTEL_TH_H_ */
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [GIT PULL 20/22] intel_th: msu: Add a sysfs attribute showing possible modes
  2019-05-03  8:44 [GIT PULL 00/22] intel_th: Updates for v5.2 Alexander Shishkin
                   ` (18 preceding siblings ...)
  2019-05-03  8:44 ` [GIT PULL 19/22] intel_th: msu: Introduce buffer driver interface Alexander Shishkin
@ 2019-05-03  8:44 ` Alexander Shishkin
  2019-05-03 16:13   ` Greg Kroah-Hartman
  2019-05-03  8:44 ` [GIT PULL 21/22] intel_th: msu-sink: An example msu buffer driver Alexander Shishkin
  2019-05-03  8:44 ` [GIT PULL 22/22] intel_th: msu: Preserve pre-existing buffer configuration Alexander Shishkin
  21 siblings, 1 reply; 26+ messages in thread
From: Alexander Shishkin @ 2019-05-03  8:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, Alexander Shishkin

With the addition of dynamically loadable buffer drivers, there needs
to be a way of knowing the currently available ones without having to
scan the list of loaded modules or trial and error.

Add a sysfs file that lists all the currently available "modes", listing
both the MSC hardware operating modes and loaded buffer drivers.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
 .../testing/sysfs-bus-intel_th-devices-msc    |  8 +++++++
 drivers/hwtracing/intel_th/msu.c              | 24 +++++++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-bus-intel_th-devices-msc b/Documentation/ABI/testing/sysfs-bus-intel_th-devices-msc
index 7da00601afdc..a6dec69cf92e 100644
--- a/Documentation/ABI/testing/sysfs-bus-intel_th-devices-msc
+++ b/Documentation/ABI/testing/sysfs-bus-intel_th-devices-msc
@@ -40,3 +40,11 @@ Description:	(RW) Trigger window switch for the MSC's buffer, in
 		triggering a window switch for the buffer. Returns an error in any
 		other operating mode or attempts to write something other than "1".
 
+What:		/sys/bus/intel_th/devices/<intel_th_id>-msc<msc-id>/modes
+Date:		May 2019
+KernelVersion:	5.2
+Contact:	Alexander Shishkin <alexander.shishkin@linux.intel.com>
+Description:	(RO) Lists all possible modes, that is, values that can be
+		written to the "mode" file described above. This includes
+		the hardware operating modes ("single", "multi", etc) and
+		all the buffer drivers that are currently loaded.
diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c
index 04bfe4dbf325..71529cd18d93 100644
--- a/drivers/hwtracing/intel_th/msu.c
+++ b/drivers/hwtracing/intel_th/msu.c
@@ -1866,6 +1866,29 @@ mode_store(struct device *dev, struct device_attribute *attr, const char *buf,
 
 static DEVICE_ATTR_RW(mode);
 
+static ssize_t
+modes_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct msu_buffer *mbuf;
+	ssize_t ret = 0;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(msc_mode); i++)
+		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s\n",
+				 msc_mode[i]);
+
+	mutex_lock(&msu_buffer_mutex);
+	list_for_each_entry(mbuf, &msu_buffer_list, entry) {
+		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s\n",
+				 mbuf->bdrv->name);
+	}
+	mutex_unlock(&msu_buffer_mutex);
+
+	return ret;
+}
+
+static DEVICE_ATTR_RO(modes);
+
 static ssize_t
 nr_pages_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
@@ -1999,6 +2022,7 @@ static DEVICE_ATTR_WO(win_switch);
 static struct attribute *msc_output_attrs[] = {
 	&dev_attr_wrap.attr,
 	&dev_attr_mode.attr,
+	&dev_attr_modes.attr,
 	&dev_attr_nr_pages.attr,
 	&dev_attr_win_switch.attr,
 	NULL,
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [GIT PULL 21/22] intel_th: msu-sink: An example msu buffer driver
  2019-05-03  8:44 [GIT PULL 00/22] intel_th: Updates for v5.2 Alexander Shishkin
                   ` (19 preceding siblings ...)
  2019-05-03  8:44 ` [GIT PULL 20/22] intel_th: msu: Add a sysfs attribute showing possible modes Alexander Shishkin
@ 2019-05-03  8:44 ` Alexander Shishkin
  2019-05-03  8:44 ` [GIT PULL 22/22] intel_th: msu: Preserve pre-existing buffer configuration Alexander Shishkin
  21 siblings, 0 replies; 26+ messages in thread
From: Alexander Shishkin @ 2019-05-03  8:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, Alexander Shishkin

This patch adds an example "sink" MSU buffer driver, which consumes trace
data from MSC buffers.

Functionally, it acts similarly to "multi" mode with automatic window
switching.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
 drivers/hwtracing/intel_th/Makefile   |   3 +
 drivers/hwtracing/intel_th/msu-sink.c | 137 ++++++++++++++++++++++++++
 2 files changed, 140 insertions(+)
 create mode 100644 drivers/hwtracing/intel_th/msu-sink.c

diff --git a/drivers/hwtracing/intel_th/Makefile b/drivers/hwtracing/intel_th/Makefile
index d9252fa8d9ca..b63eb8f309ad 100644
--- a/drivers/hwtracing/intel_th/Makefile
+++ b/drivers/hwtracing/intel_th/Makefile
@@ -20,3 +20,6 @@ intel_th_msu-y			:= msu.o
 
 obj-$(CONFIG_INTEL_TH_PTI)	+= intel_th_pti.o
 intel_th_pti-y			:= pti.o
+
+obj-$(CONFIG_INTEL_TH_MSU)	+= intel_th_msu_sink.o
+intel_th_msu_sink-y		:= msu-sink.o
diff --git a/drivers/hwtracing/intel_th/msu-sink.c b/drivers/hwtracing/intel_th/msu-sink.c
new file mode 100644
index 000000000000..1a7949a282c3
--- /dev/null
+++ b/drivers/hwtracing/intel_th/msu-sink.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * An example software sink buffer driver for Intel TH MSU.
+ *
+ * Copyright (C) 2019 Intel Corporation.
+ */
+
+#include <linux/intel_th.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+
+static unsigned int block_order;
+
+module_param(block_order, int, 0600);
+
+#define MAX_SGTS 16
+
+struct msu_sink_private {
+	struct device	*dev;
+	struct sg_table **sgts;
+	unsigned int	nr_sgts;
+	unsigned int	block_order;
+};
+
+static void *msu_sink_assign(struct device *dev, int *mode)
+{
+	struct msu_sink_private *priv;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return NULL;
+
+	priv->sgts = kzalloc(sizeof(void *) * MAX_SGTS, GFP_KERNEL);
+	if (!priv->sgts) {
+		kfree(priv);
+		return NULL;
+	}
+
+	priv->dev = dev;
+	priv->block_order = block_order;
+	*mode = MSC_MODE_MULTI;
+
+	return priv;
+}
+
+static void msu_sink_unassign(void *data)
+{
+	struct msu_sink_private *priv = data;
+
+	kfree(priv->sgts);
+	kfree(priv);
+}
+
+/* See also: msc.c: __msc_buffer_win_alloc() */
+static int msu_sink_alloc_window(void *data, struct sg_table **sgt, size_t size)
+{
+	struct msu_sink_private *priv = data;
+	unsigned int nents;
+	struct scatterlist *sg_ptr;
+	void *block;
+	int ret, i;
+
+	if (priv->nr_sgts == MAX_SGTS)
+		return -ENOMEM;
+
+	nents = DIV_ROUND_UP(size, PAGE_SIZE << priv->block_order);
+
+	ret = sg_alloc_table(*sgt, nents, GFP_KERNEL);
+	if (ret)
+		return -ENOMEM;
+
+	priv->sgts[priv->nr_sgts++] = *sgt;
+
+	for_each_sg((*sgt)->sgl, sg_ptr, nents, i) {
+		block = dma_alloc_coherent(priv->dev->parent->parent,
+					   PAGE_SIZE << priv->block_order,
+					   &sg_dma_address(sg_ptr),
+					   GFP_KERNEL);
+		sg_set_buf(sg_ptr, block, PAGE_SIZE << priv->block_order);
+		if (priv->block_order)
+			split_page(virt_to_page(block), priv->block_order);
+	}
+
+	return nents;
+}
+
+/* See also: msc.c: __msc_buffer_win_free() */
+static void msu_sink_free_window(void *data, struct sg_table *sgt)
+{
+	struct msu_sink_private *priv = data;
+	struct scatterlist *sg_ptr;
+	int i;
+
+	for_each_sg(sgt->sgl, sg_ptr, sgt->nents, i) {
+		dma_free_coherent(priv->dev->parent->parent,
+				  PAGE_SIZE << priv->block_order,
+				  sg_virt(sg_ptr), sg_dma_address(sg_ptr));
+	}
+
+	sg_free_table(sgt);
+	priv->nr_sgts--;
+}
+
+static int msu_sink_ready(void *data, struct sg_table *sgt, size_t bytes)
+{
+	struct msu_sink_private *priv = data;
+
+	intel_th_msc_window_unlock(priv->dev, sgt);
+
+	return 0;
+}
+
+static const struct msu_buffer_driver sink_bdrv = {
+	.name		= "sink",
+	.owner		= THIS_MODULE,
+	.assign		= msu_sink_assign,
+	.unassign	= msu_sink_unassign,
+	.alloc_window	= msu_sink_alloc_window,
+	.free_window	= msu_sink_free_window,
+	.ready		= msu_sink_ready,
+};
+
+static int msu_sink_init(void)
+{
+	return intel_th_msu_buffer_register(&sink_bdrv);
+}
+module_init(msu_sink_init);
+
+static void msu_sink_exit(void)
+{
+	intel_th_msu_buffer_unregister(&sink_bdrv);
+}
+module_exit(msu_sink_exit);
+
+MODULE_LICENSE("GPL v2");
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [GIT PULL 22/22] intel_th: msu: Preserve pre-existing buffer configuration
  2019-05-03  8:44 [GIT PULL 00/22] intel_th: Updates for v5.2 Alexander Shishkin
                   ` (20 preceding siblings ...)
  2019-05-03  8:44 ` [GIT PULL 21/22] intel_th: msu-sink: An example msu buffer driver Alexander Shishkin
@ 2019-05-03  8:44 ` Alexander Shishkin
  21 siblings, 0 replies; 26+ messages in thread
From: Alexander Shishkin @ 2019-05-03  8:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman; +Cc: linux-kernel, Alexander Shishkin

The MSU configuration registers may contain buffer address/size set by
the BIOS or an external hardware debugger, which may want to take over
tracing from the driver when the driver is not actively tracing.

Preserve these settings when not actively tracing.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
 drivers/hwtracing/intel_th/msu.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c
index 71529cd18d93..f9150e2bd5fc 100644
--- a/drivers/hwtracing/intel_th/msu.c
+++ b/drivers/hwtracing/intel_th/msu.c
@@ -116,6 +116,8 @@ struct msc {
 	unsigned int		single_wrap : 1;
 	void			*base;
 	dma_addr_t		base_addr;
+	u32			orig_addr;
+	u32			orig_sz;
 
 	/* <0: no buffer, 0: no users, >0: active users */
 	atomic_t		user_count;
@@ -749,6 +751,9 @@ static int msc_configure(struct msc *msc)
 		msc_buffer_clear_hw_header(msc);
 	}
 
+	msc->orig_addr = ioread32(msc->reg_base + REG_MSU_MSC0BAR);
+	msc->orig_sz   = ioread32(msc->reg_base + REG_MSU_MSC0SIZE);
+
 	reg = msc->base_addr >> PAGE_SHIFT;
 	iowrite32(reg, msc->reg_base + REG_MSU_MSC0BAR);
 
@@ -823,8 +828,8 @@ static void msc_disable(struct msc *msc)
 
 	msc->enabled = 0;
 
-	iowrite32(0, msc->reg_base + REG_MSU_MSC0BAR);
-	iowrite32(0, msc->reg_base + REG_MSU_MSC0SIZE);
+	iowrite32(msc->orig_addr, msc->reg_base + REG_MSU_MSC0BAR);
+	iowrite32(msc->orig_sz, msc->reg_base + REG_MSU_MSC0SIZE);
 
 	dev_dbg(msc_dev(msc), "MSCnNWSA: %08x\n",
 		ioread32(msc->reg_base + REG_MSU_MSC0NWSA));
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [GIT PULL 20/22] intel_th: msu: Add a sysfs attribute showing possible modes
  2019-05-03  8:44 ` [GIT PULL 20/22] intel_th: msu: Add a sysfs attribute showing possible modes Alexander Shishkin
@ 2019-05-03 16:13   ` Greg Kroah-Hartman
  0 siblings, 0 replies; 26+ messages in thread
From: Greg Kroah-Hartman @ 2019-05-03 16:13 UTC (permalink / raw)
  To: Alexander Shishkin; +Cc: linux-kernel

On Fri, May 03, 2019 at 11:44:53AM +0300, Alexander Shishkin wrote:
> With the addition of dynamically loadable buffer drivers, there needs
> to be a way of knowing the currently available ones without having to
> scan the list of loaded modules or trial and error.
> 
> Add a sysfs file that lists all the currently available "modes", listing
> both the MSC hardware operating modes and loaded buffer drivers.

sysfs files are to be only "one value per file".  This violates that
rule by a lot.

> +static ssize_t
> +modes_show(struct device *dev, struct device_attribute *attr, char *buf)
> +{
> +	struct msu_buffer *mbuf;
> +	ssize_t ret = 0;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(msc_mode); i++)
> +		ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s\n",
> +				 msc_mode[i]);

If you ever have to have a loop in a sysfs show function, you know you
are in trouble.  And here you have two of them.  Please do not do this.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [GIT PULL 12/22] intel_th: msu: Support multipage blocks
  2019-05-03  8:44 ` [GIT PULL 12/22] intel_th: msu: Support multipage blocks Alexander Shishkin
@ 2019-05-03 16:15   ` Greg Kroah-Hartman
  0 siblings, 0 replies; 26+ messages in thread
From: Greg Kroah-Hartman @ 2019-05-03 16:15 UTC (permalink / raw)
  To: Alexander Shishkin; +Cc: linux-kernel

On Fri, May 03, 2019 at 11:44:45AM +0300, Alexander Shishkin wrote:
> @@ -1481,7 +1501,7 @@ nr_pages_show(struct device *dev, struct device_attribute *attr, char *buf)
>  	else if (msc->mode == MSC_MODE_MULTI) {
>  		list_for_each_entry(win, &msc->win_list, entry) {
>  			count += scnprintf(buf + count, PAGE_SIZE - count,
> -					   "%d%c", win->nr_blocks,
> +					   "%d%c", win->nr_pages,
>  					   msc_is_last_win(win) ? '\n' : ',');
>  		}
>  	} else {

Why do you have a sysfs file that has multiple values?  I will not take
a patch that adds to this mess, please remove this file and fix it up
properly.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [GIT PULL 19/22] intel_th: msu: Introduce buffer driver interface
  2019-05-03  8:44 ` [GIT PULL 19/22] intel_th: msu: Introduce buffer driver interface Alexander Shishkin
@ 2019-05-03 16:19   ` Greg Kroah-Hartman
  0 siblings, 0 replies; 26+ messages in thread
From: Greg Kroah-Hartman @ 2019-05-03 16:19 UTC (permalink / raw)
  To: Alexander Shishkin; +Cc: linux-kernel

On Fri, May 03, 2019 at 11:44:52AM +0300, Alexander Shishkin wrote:
> Introduces a concept of buffer drivers, which is a mechanism for creating
> trace sinks that would receive trace data from MSC buffers and transfer it
> elsewhere.
> 
> A buffer driver can implement its own window allocation/deallocation if
> it has to. It must provide a callback that's used to notify it when a
> window fills up, so that it can then start a DMA transaction from that
> window 'elsewhere'. This window remains in a 'locked' state and won't be
> used for storing new trace data until the buffer driver 'unlocks' it with
> a provided API call, at which point the window can be used again for
> storing trace data.
> 
> This relies on a functional "last block" interrupt, so not all versions of
> Trace Hub can use this feature.
> 
> Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
> ---

Why is no one else reviewing any of these patches at all?  Are you
relying on me to do that?

I'll stop here on this patch series, I've applied all but one before
this, but don't have the time to properly review this one, especially so
late before the merge window closes (really, my tree should be closed
already.)

Please fix up the 2 I responded to, and get other people to review these
patches _before_ you ask me to merge them.  Having code with no other
reviewer at all is not good at all.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2019-05-03 16:19 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-05-03  8:44 [GIT PULL 00/22] intel_th: Updates for v5.2 Alexander Shishkin
2019-05-03  8:44 ` [GIT PULL 01/22] intel_th: msu: Fix single mode with IOMMU Alexander Shishkin
2019-05-03  8:44 ` [GIT PULL 02/22] intel_th: SPDX-ify the documentation Alexander Shishkin
2019-05-03  8:44 ` [GIT PULL 03/22] intel_th: Rework resource passing between glue layers and core Alexander Shishkin
2019-05-03  8:44 ` [GIT PULL 04/22] intel_th: Skip subdevices if their MMIO is missing Alexander Shishkin
2019-05-03  8:44 ` [GIT PULL 05/22] intel_th: Add "rtit" source device Alexander Shishkin
2019-05-03  8:44 ` [GIT PULL 06/22] intel_th: Communicate IRQ via resource Alexander Shishkin
2019-05-03  8:44 ` [GIT PULL 07/22] intel_th: pci: Use MSI interrupt signalling Alexander Shishkin
2019-05-03  8:44 ` [GIT PULL 08/22] intel_th: msu: Start handling IRQs Alexander Shishkin
2019-05-03  8:44 ` [GIT PULL 09/22] intel_th: Only report useful IRQs to subdevices Alexander Shishkin
2019-05-03  8:44 ` [GIT PULL 10/22] intel_th: msu: Replace open-coded list_{first,last,next}_entry variants Alexander Shishkin
2019-05-03  8:44 ` [GIT PULL 11/22] intel_th: msu: Switch over to scatterlist Alexander Shishkin
2019-05-03  8:44 ` [GIT PULL 12/22] intel_th: msu: Support multipage blocks Alexander Shishkin
2019-05-03 16:15   ` Greg Kroah-Hartman
2019-05-03  8:44 ` [GIT PULL 13/22] intel_th: msu: Factor out pipeline draining Alexander Shishkin
2019-05-03  8:44 ` [GIT PULL 14/22] intel_th: gth: Factor out trace start/stop Alexander Shishkin
2019-05-03  8:44 ` [GIT PULL 15/22] intel_th: Add switch triggering support Alexander Shishkin
2019-05-03  8:44 ` [GIT PULL 16/22] intel_th: msu: Correct the block wrap detection Alexander Shishkin
2019-05-03  8:44 ` [GIT PULL 17/22] intel_th: msu: Add a sysfs attribute to trigger window switch Alexander Shishkin
2019-05-03  8:44 ` [GIT PULL 18/22] intel_th: msu: Add current window tracking Alexander Shishkin
2019-05-03  8:44 ` [GIT PULL 19/22] intel_th: msu: Introduce buffer driver interface Alexander Shishkin
2019-05-03 16:19   ` Greg Kroah-Hartman
2019-05-03  8:44 ` [GIT PULL 20/22] intel_th: msu: Add a sysfs attribute showing possible modes Alexander Shishkin
2019-05-03 16:13   ` Greg Kroah-Hartman
2019-05-03  8:44 ` [GIT PULL 21/22] intel_th: msu-sink: An example msu buffer driver Alexander Shishkin
2019-05-03  8:44 ` [GIT PULL 22/22] intel_th: msu: Preserve pre-existing buffer configuration Alexander Shishkin

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.