From 71c488f32b071bfb5cfe9ddf682cd2e0c310c75d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 10 Aug 2017 15:45:10 +0300 Subject: stm: Potential read overflow in stm_char_policy_set_ioctl() The "size" variable comes from the user so we need to verify that it's large enough to hold an stp_policy_id struct. Fixes: 7bd1d4093c2f ("stm class: Introduce an abstraction for System Trace Module devices") Signed-off-by: Dan Carpenter Signed-off-by: Alexander Shishkin --- drivers/hwtracing/stm/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 0e73114..9414900 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -566,7 +566,7 @@ static int stm_char_policy_set_ioctl(struct stm_file *stmf, void __user *arg) if (copy_from_user(&size, arg, sizeof(size))) return -EFAULT; - if (size >= PATH_MAX + sizeof(*id)) + if (size < sizeof(*id) || size >= PATH_MAX + sizeof(*id)) return -EINVAL; /* -- cgit v1.1 From 39fccd2fc33c95de8358451f58c52b9e4b828ddd Mon Sep 17 00:00:00 2001 From: Chunyan Zhang Date: Thu, 23 Mar 2017 14:13:25 +0800 Subject: stm class: Document the stm_ftrace This patch adds a description to the stm_ftrace device source, an interface for collecting Ftrace's function trace information via STM devices. Signed-off-by: Chunyan Zhang Signed-off-by: Alexander Shishkin --- Documentation/trace/stm.txt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Documentation/trace/stm.txt b/Documentation/trace/stm.txt index 11cff47..0376575 100644 --- a/Documentation/trace/stm.txt +++ b/Documentation/trace/stm.txt @@ -83,7 +83,7 @@ by writing the name of the desired stm device there, for example: $ echo dummy_stm.0 > /sys/class/stm_source/console/stm_source_link For examples on how to use stm_source interface in the kernel, refer -to stm_console or stm_heartbeat drivers. +to stm_console, stm_heartbeat or stm_ftrace drivers. Each stm_source device will need to assume a master and a range of channels, depending on how many channels it requires. These are @@ -107,5 +107,16 @@ console in the STP stream, create a "console" policy entry (see the beginning of this text on how to do that). When initialized, it will consume one channel. +stm_ftrace +========== + +This is another "stm_source" device, once the stm_ftrace has been +linked with an stm device, and if "function" tracer is enabled, +function address and parent function address which Ftrace subsystem +would store into ring buffer will be exported via the stm device at +the same time. + +Currently only Ftrace "function" tracer is supported. + [1] https://software.intel.com/sites/default/files/managed/d3/3c/intel-th-developer-manual.pdf [2] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0444b/index.html -- cgit v1.1 From e9b2b3e7933529647a5f284136f5a6bfa9c679ae Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Thu, 10 Aug 2017 11:10:58 +0300 Subject: intel_th: pci: Enable bus mastering The driver forgets to enable bus mastering for the PCI device. Fix this. Signed-off-by: Alexander Shishkin --- drivers/hwtracing/intel_th/pci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c index 590cf90..aac7d66 100644 --- a/drivers/hwtracing/intel_th/pci.c +++ b/drivers/hwtracing/intel_th/pci.c @@ -46,6 +46,8 @@ static int intel_th_pci_probe(struct pci_dev *pdev, if (IS_ERR(th)) return PTR_ERR(th); + pci_set_master(pdev); + return 0; } -- cgit v1.1 From 5376be63ff4ff357d40dbe75c925d7cd5aef19a9 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 18 Nov 2016 14:51:05 +0200 Subject: intel_th: Output devices without ports don't need assigning Output subdevices that rely on other output subdevices (or otherwise don't directly talk to an output port on the switch) don't need to be assigned an output port either. Signed-off-by: Alexander Shishkin --- drivers/hwtracing/intel_th/intel_th.h | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h index 3096e70..c03f28e 100644 --- a/drivers/hwtracing/intel_th/intel_th.h +++ b/drivers/hwtracing/intel_th/intel_th.h @@ -96,6 +96,16 @@ intel_th_device_get_resource(struct intel_th_device *thdev, unsigned int type, return NULL; } +/* + * GTH, output ports configuration + */ +enum { + GTH_NONE = 0, + GTH_MSU, /* memory/usb */ + GTH_CTP, /* Common Trace Port */ + GTH_PTI = 4, /* MIPI-PTI */ +}; + /** * intel_th_output_assigned() - if an output device is assigned to a switch port * @thdev: the output device @@ -106,7 +116,8 @@ static inline bool intel_th_output_assigned(struct intel_th_device *thdev) { return thdev->type == INTEL_TH_OUTPUT && - thdev->output.port >= 0; + (thdev->output.port >= 0 || + thdev->output.type == GTH_NONE); } /** @@ -250,16 +261,6 @@ enum { }; /* - * GTH, output ports configuration - */ -enum { - GTH_NONE = 0, - GTH_MSU, /* memory/usb */ - GTH_CTP, /* Common Trace Port */ - GTH_PTI = 4, /* MIPI-PTI */ -}; - -/* * Scratchpad bits: tell firmware and external debuggers * what we are up to. */ -- cgit v1.1 From 5e06723af998779210dffe9553b36b28955d5860 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 18 Nov 2016 15:05:01 +0200 Subject: intel_th: Streamline the subdevice tree accessors Make to_intel_th*() accessors available from the main header file. Signed-off-by: Alexander Shishkin --- drivers/hwtracing/intel_th/core.c | 15 --------------- drivers/hwtracing/intel_th/intel_th.h | 25 ++++++++++++++++++++++++- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index 8da567a..e6d302b 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -156,21 +156,6 @@ static struct device_type intel_th_source_device_type = { .release = intel_th_device_release, }; -static struct intel_th *to_intel_th(struct intel_th_device *thdev) -{ - /* - * subdevice tree is flat: if this one is not a switch, its - * parent must be - */ - if (thdev->type != INTEL_TH_SWITCH) - thdev = to_intel_th_hub(thdev); - - if (WARN_ON_ONCE(!thdev || thdev->type != INTEL_TH_SWITCH)) - return NULL; - - return dev_get_drvdata(thdev->dev.parent); -} - static char *intel_th_output_devnode(struct device *dev, umode_t *mode, kuid_t *uid, kgid_t *gid) { diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h index c03f28e..496286c 100644 --- a/drivers/hwtracing/intel_th/intel_th.h +++ b/drivers/hwtracing/intel_th/intel_th.h @@ -173,7 +173,7 @@ struct intel_th_driver { ((_d) ? to_intel_th_driver(_d) : NULL) static inline struct intel_th_device * -to_intel_th_hub(struct intel_th_device *thdev) +to_intel_th_parent(struct intel_th_device *thdev) { struct device *parent = thdev->dev.parent; @@ -183,6 +183,29 @@ to_intel_th_hub(struct intel_th_device *thdev) return to_intel_th_device(parent); } +static inline struct intel_th_device * +to_intel_th_hub(struct intel_th_device *thdev) +{ + /* + * subdevice tree is flat: if this one is not a switch, its + * parent must be + */ + if (thdev->type == INTEL_TH_SWITCH) + return thdev; + + return to_intel_th_parent(thdev); +} + +static inline struct intel_th *to_intel_th(struct intel_th_device *thdev) +{ + thdev = to_intel_th_hub(thdev); + + if (WARN_ON_ONCE(!thdev || thdev->type != INTEL_TH_SWITCH)) + return NULL; + + return dev_get_drvdata(thdev->dev.parent); +} + struct intel_th * intel_th_alloc(struct device *dev, struct resource *devres, unsigned int ndevres, int irq); -- cgit v1.1 From 8edc514b01e9cfbc037c708e5260f248cbb4d867 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 18 Nov 2016 15:36:39 +0200 Subject: intel_th: Make SOURCE devices children of the root device The switch (GTH) does not directly interact with SOURCE type devices and may not even be present (in host mode). To reflect this and avoid inconsistencies between target and host mode, make SOURCE devices descendant directly from the root (i.e. PCI) device. Their symlinks will no longer appear under the switch device, but they can still be found under intel_th bus. Signed-off-by: Alexander Shishkin --- drivers/hwtracing/intel_th/core.c | 6 +++--- drivers/hwtracing/intel_th/intel_th.h | 39 +++++++++++++++++++++-------------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index e6d302b..323d3ac 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -311,10 +311,10 @@ intel_th_device_alloc(struct intel_th *th, unsigned int type, const char *name, struct device *parent; struct intel_th_device *thdev; - if (type == INTEL_TH_SWITCH) - parent = th->dev; - else + if (type == INTEL_TH_OUTPUT) parent = &th->hub->dev; + else + parent = th->dev; thdev = kzalloc(sizeof(*thdev) + strlen(name) + 1, GFP_KERNEL); if (!thdev) diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h index 496286c..6243ac1 100644 --- a/drivers/hwtracing/intel_th/intel_th.h +++ b/drivers/hwtracing/intel_th/intel_th.h @@ -172,6 +172,16 @@ struct intel_th_driver { #define to_intel_th_driver_or_null(_d) \ ((_d) ? to_intel_th_driver(_d) : NULL) +/* + * Subdevice tree structure is as follows: + * + struct intel_th device (pci; dev_{get,set}_drvdata() + * + struct intel_th_device INTEL_TH_SWITCH (GTH) + * + struct intel_th_device INTEL_TH_OUTPUT (MSU, PTI) + * + struct intel_th_device INTEL_TH_SOURCE (STH) + * + * In other words, INTEL_TH_OUTPUT devices are children of INTEL_TH_SWITCH; + * INTEL_TH_SWITCH and INTEL_TH_SOURCE are children of the intel_th device. + */ static inline struct intel_th_device * to_intel_th_parent(struct intel_th_device *thdev) { @@ -183,24 +193,12 @@ to_intel_th_parent(struct intel_th_device *thdev) return to_intel_th_device(parent); } -static inline struct intel_th_device * -to_intel_th_hub(struct intel_th_device *thdev) -{ - /* - * subdevice tree is flat: if this one is not a switch, its - * parent must be - */ - if (thdev->type == INTEL_TH_SWITCH) - return thdev; - - return to_intel_th_parent(thdev); -} - static inline struct intel_th *to_intel_th(struct intel_th_device *thdev) { - thdev = to_intel_th_hub(thdev); + if (thdev->type == INTEL_TH_OUTPUT) + thdev = to_intel_th_parent(thdev); - if (WARN_ON_ONCE(!thdev || thdev->type != INTEL_TH_SWITCH)) + if (WARN_ON_ONCE(!thdev || thdev->type == INTEL_TH_OUTPUT)) return NULL; return dev_get_drvdata(thdev->dev.parent); @@ -254,6 +252,17 @@ struct intel_th { #endif }; +static inline struct intel_th_device * +to_intel_th_hub(struct intel_th_device *thdev) +{ + if (thdev->type == INTEL_TH_SWITCH) + return thdev; + else if (thdev->type == INTEL_TH_OUTPUT) + return to_intel_th_parent(thdev); + + return to_intel_th(thdev)->hub; +} + /* * Register windows */ -- cgit v1.1 From a753bfcfdb1f31d74b5ec87faa19f15e8c7b44a2 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Thu, 10 Aug 2017 18:28:38 +0300 Subject: intel_th: Make the switch allocate its subdevices Instead of allocating devices for every possible output subdevice, allow the switch to allocate only the ones that it knows about. Signed-off-by: Alexander Shishkin --- drivers/hwtracing/intel_th/core.c | 288 ++++++++++++++++++++++++---------- drivers/hwtracing/intel_th/gth.c | 17 +- drivers/hwtracing/intel_th/intel_th.h | 13 +- 3 files changed, 230 insertions(+), 88 deletions(-) diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index 323d3ac..4f56959 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -101,17 +101,53 @@ out_pm: return ret; } +static void intel_th_device_remove(struct intel_th_device *thdev); + static int intel_th_remove(struct device *dev) { struct intel_th_driver *thdrv = to_intel_th_driver(dev->driver); struct intel_th_device *thdev = to_intel_th_device(dev); - struct intel_th_device *hub = to_intel_th_device(dev->parent); + struct intel_th_device *hub = to_intel_th_hub(thdev); int err; if (thdev->type == INTEL_TH_SWITCH) { + struct intel_th *th = to_intel_th(hub); + int i, lowest; + + /* disconnect outputs */ err = device_for_each_child(dev, thdev, intel_th_child_remove); if (err) return err; + + /* + * Remove outputs, that is, hub's children: they are created + * at hub's probe time by having the hub call + * intel_th_output_enable() for each of them. + */ + for (i = 0, lowest = -1; i < th->num_thdevs; i++) { + /* + * Move the non-output devices from higher up the + * th->thdev[] array to lower positions to maintain + * a contiguous array. + */ + if (th->thdev[i]->type != INTEL_TH_OUTPUT) { + if (lowest >= 0) { + th->thdev[lowest] = th->thdev[i]; + th->thdev[i] = NULL; + ++lowest; + } + + continue; + } + + if (lowest == -1) + lowest = i; + + intel_th_device_remove(th->thdev[i]); + th->thdev[i] = NULL; + } + + th->num_thdevs = lowest; } if (thdrv->attr_group) @@ -377,7 +413,7 @@ static const struct intel_th_subdevice { unsigned otype; unsigned scrpd; int id; -} intel_th_subdevices[TH_SUBDEVICE_MAX] = { +} intel_th_subdevices[] = { { .nres = 1, .res = { @@ -511,98 +547,181 @@ static inline void intel_th_request_hub_module_flush(struct intel_th *th) } #endif /* CONFIG_MODULES */ -static int intel_th_populate(struct intel_th *th, struct resource *devres, - unsigned int ndevres, int irq) +static struct intel_th_device * +intel_th_subdevice_alloc(struct intel_th *th, + const struct intel_th_subdevice *subdev) { + struct intel_th_device *thdev; struct resource res[3]; unsigned int req = 0; - int src, dst, err; + int r, err; - /* create devices for each intel_th_subdevice */ - for (src = 0, dst = 0; src < ARRAY_SIZE(intel_th_subdevices); src++) { - const struct intel_th_subdevice *subdev = - &intel_th_subdevices[src]; - struct intel_th_device *thdev; - int r; + thdev = intel_th_device_alloc(th, subdev->type, subdev->name, + subdev->id); + if (!thdev) + return ERR_PTR(-ENOMEM); - /* only allow SOURCE and SWITCH devices in host mode */ - if (host_mode && subdev->type == INTEL_TH_OUTPUT) - continue; - thdev = intel_th_device_alloc(th, subdev->type, subdev->name, - subdev->id); - if (!thdev) { - err = -ENOMEM; - goto kill_subdevs; + memcpy(res, subdev->res, + sizeof(struct resource) * subdev->nres); + + for (r = 0; r < subdev->nres; r++) { + struct resource *devres = th->resource; + int bar = TH_MMIO_CONFIG; + + /* + * Take .end == 0 to mean 'take the whole bar', + * .start then tells us which bar it is. Default to + * TH_MMIO_CONFIG. + */ + if (!res[r].end && res[r].flags == IORESOURCE_MEM) { + bar = res[r].start; + res[r].start = 0; + res[r].end = resource_size(&devres[bar]) - 1; } - memcpy(res, subdev->res, - sizeof(struct resource) * subdev->nres); + if (res[r].flags & IORESOURCE_MEM) { + res[r].start += devres[bar].start; + res[r].end += devres[bar].start; - for (r = 0; r < subdev->nres; r++) { - int bar = TH_MMIO_CONFIG; + 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; + } + } - /* - * Take .end == 0 to mean 'take the whole bar', - * .start then tells us which bar it is. Default to - * TH_MMIO_CONFIG. - */ - if (!res[r].end && res[r].flags == IORESOURCE_MEM) { - bar = res[r].start; - res[r].start = 0; - res[r].end = resource_size(&devres[bar]) - 1; - } + err = intel_th_device_add_resources(thdev, res, subdev->nres); + if (err) { + put_device(&thdev->dev); + goto fail_put_device; + } - if (res[r].flags & IORESOURCE_MEM) { - res[r].start += devres[bar].start; - res[r].end += devres[bar].start; + if (subdev->type == INTEL_TH_OUTPUT) { + thdev->dev.devt = MKDEV(th->major, th->num_thdevs); + thdev->output.type = subdev->otype; + thdev->output.port = -1; + thdev->output.scratchpad = subdev->scrpd; + } else if (subdev->type == INTEL_TH_SWITCH) { + thdev->host_mode = host_mode; + th->hub = thdev; + } - dev_dbg(th->dev, "%s:%d @ %pR\n", - subdev->name, r, &res[r]); - } else if (res[r].flags & IORESOURCE_IRQ) { - res[r].start = irq; - } - } + err = device_add(&thdev->dev); + if (err) { + put_device(&thdev->dev); + goto fail_free_res; + } - err = intel_th_device_add_resources(thdev, res, subdev->nres); - if (err) { - put_device(&thdev->dev); - goto kill_subdevs; - } + /* need switch driver to be loaded to enumerate the rest */ + if (subdev->type == INTEL_TH_SWITCH && !req) { + err = intel_th_request_hub_module(th); + if (!err) + req++; + } - if (subdev->type == INTEL_TH_OUTPUT) { - thdev->dev.devt = MKDEV(th->major, dst); - thdev->output.type = subdev->otype; - thdev->output.port = -1; - thdev->output.scratchpad = subdev->scrpd; - } else if (subdev->type == INTEL_TH_SWITCH) { - thdev->host_mode = host_mode; - } + return thdev; + +fail_free_res: + kfree(thdev->resource); + +fail_put_device: + put_device(&thdev->dev); + + return ERR_PTR(err); +} - err = device_add(&thdev->dev); - if (err) { - put_device(&thdev->dev); - goto kill_subdevs; +/** + * intel_th_output_enable() - find and enable a device for a given output type + * @th: Intel TH instance + * @otype: output type + * + * Go through the unallocated output devices, find the first one whos type + * matches @otype and instantiate it. These devices are removed when the hub + * device is removed, see intel_th_remove(). + */ +int intel_th_output_enable(struct intel_th *th, unsigned int otype) +{ + struct intel_th_device *thdev; + int src = 0, dst = 0; + + for (src = 0, dst = 0; dst <= th->num_thdevs; src++, dst++) { + for (; src < ARRAY_SIZE(intel_th_subdevices); src++) { + if (intel_th_subdevices[src].type != INTEL_TH_OUTPUT) + continue; + + if (intel_th_subdevices[src].otype != otype) + continue; + + break; } - /* need switch driver to be loaded to enumerate the rest */ - if (subdev->type == INTEL_TH_SWITCH && !req) { - th->hub = thdev; - err = intel_th_request_hub_module(th); - if (!err) - req++; + /* no unallocated matching subdevices */ + if (src == ARRAY_SIZE(intel_th_subdevices)) + return -ENODEV; + + for (; dst < th->num_thdevs; dst++) { + if (th->thdev[dst]->type != INTEL_TH_OUTPUT) + continue; + + if (th->thdev[dst]->output.type != otype) + continue; + + break; } - th->thdev[dst++] = thdev; + /* + * intel_th_subdevices[src] matches our requirements and is + * not matched in th::thdev[] + */ + if (dst == th->num_thdevs) + goto found; } + return -ENODEV; + +found: + thdev = intel_th_subdevice_alloc(th, &intel_th_subdevices[src]); + if (IS_ERR(thdev)) + return PTR_ERR(thdev); + + th->thdev[th->num_thdevs++] = thdev; + return 0; +} +EXPORT_SYMBOL_GPL(intel_th_output_enable); -kill_subdevs: - for (; dst >= 0; dst--) - intel_th_device_remove(th->thdev[dst]); +static int intel_th_populate(struct intel_th *th) +{ + int src; - return err; + /* create devices for each intel_th_subdevice */ + for (src = 0; src < ARRAY_SIZE(intel_th_subdevices); src++) { + const struct intel_th_subdevice *subdev = + &intel_th_subdevices[src]; + struct intel_th_device *thdev; + + /* only allow SOURCE and SWITCH devices in host mode */ + if (host_mode && subdev->type == INTEL_TH_OUTPUT) + continue; + + /* + * don't enable port OUTPUTs in this path; SWITCH enables them + * via intel_th_output_enable() + */ + if (subdev->type == INTEL_TH_OUTPUT && + subdev->otype != GTH_NONE) + continue; + + thdev = intel_th_subdevice_alloc(th, subdev); + /* note: caller should free subdevices from th::thdev[] */ + if (IS_ERR(thdev)) + return PTR_ERR(thdev); + + th->thdev[th->num_thdevs++] = thdev; + } + + return 0; } static int match_devt(struct device *dev, void *data) @@ -679,24 +798,25 @@ intel_th_alloc(struct device *dev, struct resource *devres, } th->dev = dev; + th->resource = devres; + th->num_resources = ndevres; + th->irq = irq; + dev_set_drvdata(dev, th); pm_runtime_no_callbacks(dev); pm_runtime_put(dev); pm_runtime_allow(dev); - err = intel_th_populate(th, devres, ndevres, irq); - if (err) - goto err_chrdev; + err = intel_th_populate(th); + if (err) { + /* free the subdevices and undo everything */ + intel_th_free(th); + return ERR_PTR(err); + } return th; -err_chrdev: - pm_runtime_forbid(dev); - - __unregister_chrdev(th->major, 0, TH_POSSIBLE_OUTPUTS, - "intel_th/output"); - err_ida: ida_simple_remove(&intel_th_ida, th->id); @@ -712,11 +832,15 @@ void intel_th_free(struct intel_th *th) int i; intel_th_request_hub_module_flush(th); - for (i = 0; i < TH_SUBDEVICE_MAX; i++) - if (th->thdev[i] && th->thdev[i] != th->hub) - intel_th_device_remove(th->thdev[i]); intel_th_device_remove(th->hub); + for (i = 0; i < th->num_thdevs; i++) { + if (th->thdev[i] != th->hub) + intel_th_device_remove(th->thdev[i]); + th->thdev[i] = NULL; + } + + th->num_thdevs = 0; pm_runtime_get_sync(th->dev); pm_runtime_forbid(th->dev); diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index dd32d0b..7d9d667 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -639,6 +639,7 @@ intel_th_gth_set_output(struct intel_th_device *thdev, unsigned int master) static int intel_th_gth_probe(struct intel_th_device *thdev) { struct device *dev = &thdev->dev; + struct intel_th *th = dev_get_drvdata(dev->parent); struct gth_device *gth; struct resource *res; void __iomem *base; @@ -660,6 +661,8 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) gth->base = base; spin_lock_init(>h->gth_lock); + dev_set_drvdata(dev, gth); + /* * Host mode can be signalled via SW means or via SCRPD_DEBUGGER_IN_USE * bit. Either way, don't reset HW in this case, and don't export any @@ -667,7 +670,7 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) * drivers to ports, see intel_th_gth_assign(). */ if (thdev->host_mode) - goto done; + return 0; ret = intel_th_gth_reset(gth); if (ret) { @@ -676,7 +679,7 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) thdev->host_mode = true; - goto done; + return 0; } for (i = 0; i < TH_CONFIGURABLE_MASTERS + 1; i++) @@ -687,6 +690,13 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) gth->output[i].index = i; gth->output[i].port_type = gth_output_parm_get(gth, i, TH_OUTPUT_PARM(port)); + if (gth->output[i].port_type == GTH_NONE) + continue; + + ret = intel_th_output_enable(th, gth->output[i].port_type); + /* -ENODEV is ok, we just won't have that device enumerated */ + if (ret && ret != -ENODEV) + return ret; } if (intel_th_output_attributes(gth) || @@ -698,9 +708,6 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) return -ENOMEM; } -done: - dev_set_drvdata(dev, gth); - return 0; } diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h index 6243ac1..d44da50 100644 --- a/drivers/hwtracing/intel_th/intel_th.h +++ b/drivers/hwtracing/intel_th/intel_th.h @@ -216,6 +216,7 @@ int intel_th_trace_enable(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); +int intel_th_output_enable(struct intel_th *th, unsigned int otype); enum { TH_MMIO_CONFIG = 0, @@ -223,8 +224,9 @@ enum { TH_MMIO_END, }; -#define TH_SUBDEVICE_MAX 6 #define TH_POSSIBLE_OUTPUTS 8 +/* Total number of possible subdevices: outputs + GTH + STH */ +#define TH_SUBDEVICE_MAX (TH_POSSIBLE_OUTPUTS + 2) #define TH_CONFIGURABLE_MASTERS 256 #define TH_MSC_MAX 2 @@ -233,6 +235,10 @@ enum { * @dev: driver core's device * @thdev: subdevices * @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 + * @irq: irq number * @id: this Intel TH controller's device ID in the system * @major: device node major for output devices */ @@ -242,6 +248,11 @@ struct intel_th { struct intel_th_device *thdev[TH_SUBDEVICE_MAX]; struct intel_th_device *hub; + struct resource *resource; + unsigned int num_thdevs; + unsigned int num_resources; + int irq; + int id; int major; #ifdef CONFIG_MODULES -- cgit v1.1 From d5c435df4a890be0ef51f3047080756002d140dd Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 24 Feb 2017 16:05:22 +0200 Subject: intel_th: msu: Use the real device in case of IOMMU domain allocation When allocating DMA buffers for the MSU, use the real device instead of GTH. Signed-off-by: Alexander Shishkin --- drivers/hwtracing/intel_th/msu.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index dbbe31d..dfb57ea 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -709,17 +709,17 @@ static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_blocks) } for (i = 0; i < nr_blocks; i++) { - win->block[i].bdesc = dma_alloc_coherent(msc_dev(msc), size, - &win->block[i].addr, - GFP_KERNEL); + 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; #ifdef CONFIG_X86 /* Set the page as uncached */ set_memory_uc((unsigned long)win->block[i].bdesc, 1); #endif - - if (!win->block[i].bdesc) - goto err_nomem; } win->msc = msc; -- cgit v1.1 From 92758af39ab73f470f765b7213fb4c88c7e5ca03 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 11 Nov 2016 12:09:11 +0200 Subject: intel_th: Enumerate Low Power Path output port type Trace Hub 2.x adds Low Power Path (LPP) output port type, which provides a low power mode trace path from sources to PTI or BSSB. This adds an output subdevice for the LPP port. Signed-off-by: Alexander Shishkin --- drivers/hwtracing/intel_th/core.c | 15 +++++++++++++++ drivers/hwtracing/intel_th/intel_th.h | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index 4f56959..d176000 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -504,6 +504,21 @@ static const struct intel_th_subdevice { .nres = 1, .res = { { + .start = REG_PTI_OFFSET, + .end = REG_PTI_OFFSET + REG_PTI_LENGTH - 1, + .flags = IORESOURCE_MEM, + }, + }, + .id = -1, + .name = "lpp", + .type = INTEL_TH_OUTPUT, + .otype = GTH_LPP, + .scrpd = SCRPD_PTI_IS_PRIM_DEST, + }, + { + .nres = 1, + .res = { + { .start = REG_DCIH_OFFSET, .end = REG_DCIH_OFFSET + REG_DCIH_LENGTH - 1, .flags = IORESOURCE_MEM, diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h index d44da50..3f45584 100644 --- a/drivers/hwtracing/intel_th/intel_th.h +++ b/drivers/hwtracing/intel_th/intel_th.h @@ -103,7 +103,8 @@ enum { GTH_NONE = 0, GTH_MSU, /* memory/usb */ GTH_CTP, /* Common Trace Port */ - GTH_PTI = 4, /* MIPI-PTI */ + GTH_LPP, /* Low Power Path */ + GTH_PTI, /* MIPI-PTI */ }; /** -- cgit v1.1 From f77d22bc1221409b6c0cb6f32c6241161f5c2bc6 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 11 Nov 2016 12:07:25 +0200 Subject: intel_th: pti: Support Low Power Path output port type The Low Power Path (LPP) output port type, looks mostly like PTI to the software, with a few additional bits in the control register. This extends the PTI driver to support LPP ports as well. Signed-off-by: Alexander Shishkin --- drivers/hwtracing/intel_th/pti.c | 115 +++++++++++++++++++++++++++++++++++++-- drivers/hwtracing/intel_th/pti.h | 8 +++ 2 files changed, 118 insertions(+), 5 deletions(-) diff --git a/drivers/hwtracing/intel_th/pti.c b/drivers/hwtracing/intel_th/pti.c index 35738b5..e96a1fc 100644 --- a/drivers/hwtracing/intel_th/pti.c +++ b/drivers/hwtracing/intel_th/pti.c @@ -1,7 +1,7 @@ /* * Intel(R) Trace Hub PTI output driver * - * Copyright (C) 2014-2015 Intel Corporation. + * Copyright (C) 2014-2016 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -34,6 +34,8 @@ struct pti_device { unsigned int freeclk; unsigned int clkdiv; unsigned int patgen; + unsigned int lpp_dest_mask; + unsigned int lpp_dest; }; /* map PTI widths to MODE settings of PTI_CTL register */ @@ -163,6 +165,7 @@ static int intel_th_pti_activate(struct intel_th_device *thdev) ctl |= PTI_FCEN; ctl |= pti->mode << __ffs(PTI_MODE); ctl |= pti->clkdiv << __ffs(PTI_CLKDIV); + ctl |= pti->lpp_dest << __ffs(LPP_DEST); iowrite32(ctl, pti->base + REG_PTI_CTL); @@ -192,6 +195,15 @@ static void read_hw_config(struct pti_device *pti) pti->mode = pti_width_mode(4); if (!pti->clkdiv) pti->clkdiv = 1; + + if (pti->thdev->output.type == GTH_LPP) { + if (ctl & LPP_PTIPRESENT) + pti->lpp_dest_mask |= LPP_DEST_PTI; + if (ctl & LPP_BSSBPRESENT) + pti->lpp_dest_mask |= LPP_DEST_EXI; + if (ctl & LPP_DEST) + pti->lpp_dest = 1; + } } static int intel_th_pti_probe(struct intel_th_device *thdev) @@ -239,10 +251,103 @@ static struct intel_th_driver intel_th_pti_driver = { }, }; -module_driver(intel_th_pti_driver, - intel_th_driver_register, - intel_th_driver_unregister); +static const char * const lpp_dest_str[] = { "pti", "exi" }; + +static ssize_t lpp_dest_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct pti_device *pti = dev_get_drvdata(dev); + ssize_t ret = 0; + int i; + + for (i = ARRAY_SIZE(lpp_dest_str) - 1; i >= 0; i--) { + const char *fmt = pti->lpp_dest == i ? "[%s] " : "%s "; + + if (!(pti->lpp_dest_mask & BIT(i))) + continue; + + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + fmt, lpp_dest_str[i]); + } + + if (ret) + buf[ret - 1] = '\n'; + + return ret; +} + +static ssize_t lpp_dest_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct pti_device *pti = dev_get_drvdata(dev); + ssize_t ret = -EINVAL; + int i; + + for (i = 0; i < ARRAY_SIZE(lpp_dest_str); i++) + if (sysfs_streq(buf, lpp_dest_str[i])) + break; + + if (i < ARRAY_SIZE(lpp_dest_str) && pti->lpp_dest_mask & BIT(i)) { + pti->lpp_dest = i; + ret = size; + } + + return ret; +} + +static DEVICE_ATTR_RW(lpp_dest); + +static struct attribute *lpp_output_attrs[] = { + &dev_attr_mode.attr, + &dev_attr_freerunning_clock.attr, + &dev_attr_clock_divider.attr, + &dev_attr_lpp_dest.attr, + NULL, +}; + +static struct attribute_group lpp_output_group = { + .attrs = lpp_output_attrs, +}; + +static struct intel_th_driver intel_th_lpp_driver = { + .probe = intel_th_pti_probe, + .remove = intel_th_pti_remove, + .activate = intel_th_pti_activate, + .deactivate = intel_th_pti_deactivate, + .attr_group = &lpp_output_group, + .driver = { + .name = "lpp", + .owner = THIS_MODULE, + }, +}; + +static int __init intel_th_pti_lpp_init(void) +{ + int err; + + err = intel_th_driver_register(&intel_th_pti_driver); + if (err) + return err; + + err = intel_th_driver_register(&intel_th_lpp_driver); + if (err) { + intel_th_driver_unregister(&intel_th_pti_driver); + return err; + } + + return 0; +} + +module_init(intel_th_pti_lpp_init); + +static void __exit intel_th_pti_lpp_exit(void) +{ + intel_th_driver_unregister(&intel_th_pti_driver); + intel_th_driver_unregister(&intel_th_lpp_driver); +} + +module_exit(intel_th_pti_lpp_exit); MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Intel(R) Trace Hub PTI output driver"); +MODULE_DESCRIPTION("Intel(R) Trace Hub PTI/LPP output driver"); MODULE_AUTHOR("Alexander Shishkin "); diff --git a/drivers/hwtracing/intel_th/pti.h b/drivers/hwtracing/intel_th/pti.h index 20883f5..30827be 100644 --- a/drivers/hwtracing/intel_th/pti.h +++ b/drivers/hwtracing/intel_th/pti.h @@ -23,7 +23,15 @@ enum { #define PTI_EN BIT(0) #define PTI_FCEN BIT(1) #define PTI_MODE 0xf0 +#define LPP_PTIPRESENT BIT(8) +#define LPP_BSSBPRESENT BIT(9) #define PTI_CLKDIV 0x000f0000 #define PTI_PATGENMODE 0x00f00000 +#define LPP_DEST BIT(25) +#define LPP_BSSBACT BIT(30) +#define LPP_LPPBUSY BIT(31) + +#define LPP_DEST_PTI BIT(0) +#define LPP_DEST_EXI BIT(1) #endif /* __INTEL_TH_STH_H__ */ -- cgit v1.1 From 84331e1390b6378a5129a3678c87a42c6f697d29 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Thu, 30 Jun 2016 16:11:13 +0300 Subject: intel_th: pci: Add Cannon Lake PCH-H support This adds Intel(R) Trace Hub PCI ID for Cannon Lake PCH-H. Signed-off-by: Alexander Shishkin Cc: --- drivers/hwtracing/intel_th/pci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c index aac7d66..507db47 100644 --- a/drivers/hwtracing/intel_th/pci.c +++ b/drivers/hwtracing/intel_th/pci.c @@ -97,6 +97,11 @@ static const struct pci_device_id intel_th_pci_id_table[] = { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x318e), .driver_data = (kernel_ulong_t)0, }, + { + /* Cannon Lake H */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa326), + .driver_data = (kernel_ulong_t)0, + }, { 0 }, }; -- cgit v1.1 From efb3669e14fe17d0ec4ecf57d0365039fe726f59 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Thu, 30 Jun 2016 16:11:31 +0300 Subject: intel_th: pci: Add Cannon Lake PCH-LP support This adds Intel(R) Trace Hub PCI ID for Cannon Lake PCH-LP. Signed-off-by: Alexander Shishkin Cc: --- drivers/hwtracing/intel_th/pci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c index 507db47..5a9a9e8 100644 --- a/drivers/hwtracing/intel_th/pci.c +++ b/drivers/hwtracing/intel_th/pci.c @@ -102,6 +102,11 @@ static const struct pci_device_id intel_th_pci_id_table[] = { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa326), .driver_data = (kernel_ulong_t)0, }, + { + /* Cannon Lake LP */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9da6), + .driver_data = (kernel_ulong_t)0, + }, { 0 }, }; -- cgit v1.1 From 3321371b5d648479058fa6f9441168abbc1467c9 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 18 Aug 2017 17:57:35 +0300 Subject: intel_th: pci: Use drvdata for quirks Allow attaching miscellaneous quirk information to devices as drvdata. Signed-off-by: Alexander Shishkin --- drivers/hwtracing/intel_th/core.c | 6 ++++-- drivers/hwtracing/intel_th/intel_th.h | 27 ++++++++++++++++++++------- drivers/hwtracing/intel_th/pci.c | 3 ++- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index d176000..e915ab2 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -576,6 +576,7 @@ intel_th_subdevice_alloc(struct intel_th *th, if (!thdev) return ERR_PTR(-ENOMEM); + thdev->drvdata = th->drvdata; memcpy(res, subdev->res, sizeof(struct resource) * subdev->nres); @@ -789,8 +790,8 @@ static const struct file_operations intel_th_output_fops = { * @irq: irq number */ struct intel_th * -intel_th_alloc(struct device *dev, struct resource *devres, - unsigned int ndevres, int irq) +intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata, + struct resource *devres, unsigned int ndevres, int irq) { struct intel_th *th; int err; @@ -812,6 +813,7 @@ intel_th_alloc(struct device *dev, struct resource *devres, goto err_ida; } th->dev = dev; + th->drvdata = drvdata; th->resource = devres; th->num_resources = ndevres; diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h index 3f45584..6824460 100644 --- a/drivers/hwtracing/intel_th/intel_th.h +++ b/drivers/hwtracing/intel_th/intel_th.h @@ -48,8 +48,19 @@ struct intel_th_output { }; /** + * struct intel_th_drvdata - describes hardware capabilities and quirks + * @tscu_enable: device needs SW to enable time stamping unit + */ +struct intel_th_drvdata { + unsigned int tscu_enable : 1; +}; + +#define INTEL_TH_CAP(_th, _cap) ((_th)->drvdata ? (_th)->drvdata->_cap : 0) + +/** * struct intel_th_device - device on the intel_th bus * @dev: device + * @drvdata: hardware capabilities/quirks * @resource: array of resources available to this device * @num_resources: number of resources in @resource array * @type: INTEL_TH_{SOURCE,OUTPUT,SWITCH} @@ -59,11 +70,12 @@ struct intel_th_output { * @name: device name to match the driver */ struct intel_th_device { - struct device dev; - struct resource *resource; - unsigned int num_resources; - unsigned int type; - int id; + struct device dev; + struct intel_th_drvdata *drvdata; + struct resource *resource; + unsigned int num_resources; + unsigned int type; + int id; /* INTEL_TH_SWITCH specific */ bool host_mode; @@ -206,8 +218,8 @@ static inline struct intel_th *to_intel_th(struct intel_th_device *thdev) } struct intel_th * -intel_th_alloc(struct device *dev, struct resource *devres, - unsigned int ndevres, int irq); +intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata, + struct resource *devres, unsigned int ndevres, int irq); void intel_th_free(struct intel_th *th); int intel_th_driver_register(struct intel_th_driver *thdrv); @@ -248,6 +260,7 @@ struct intel_th { struct intel_th_device *thdev[TH_SUBDEVICE_MAX]; struct intel_th_device *hub; + struct intel_th_drvdata *drvdata; struct resource *resource; unsigned int num_thdevs; diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c index 5a9a9e8..aed6d59 100644 --- a/drivers/hwtracing/intel_th/pci.c +++ b/drivers/hwtracing/intel_th/pci.c @@ -30,6 +30,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 intel_th *th; int err; @@ -41,7 +42,7 @@ static int intel_th_pci_probe(struct pci_dev *pdev, if (err) return err; - th = intel_th_alloc(&pdev->dev, pdev->resource, + th = intel_th_alloc(&pdev->dev, drvdata, pdev->resource, DEVICE_COUNT_RESOURCE, pdev->irq); if (IS_ERR(th)) return PTR_ERR(th); -- cgit v1.1 From 29e15e83a99cdc13d0d38de558fbea641f8fdda8 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 25 Aug 2017 15:47:22 +0300 Subject: intel_th: Add global activate/deactivate callbacks for the glue layers A glue layer may want to install its own hooks into trace capture start and stop paths to apply workarounds. This adds optional callbacks. Signed-off-by: Alexander Shishkin --- drivers/hwtracing/intel_th/core.c | 26 ++++++++++++++++++++++---- drivers/hwtracing/intel_th/intel_th.h | 2 ++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index e915ab2..998e3e5 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -226,6 +226,7 @@ static int intel_th_output_activate(struct intel_th_device *thdev) { struct intel_th_driver *thdrv = to_intel_th_driver_or_null(thdev->dev.driver); + struct intel_th *th = to_intel_th(thdev); int ret = 0; if (!thdrv) @@ -236,15 +237,28 @@ static int intel_th_output_activate(struct intel_th_device *thdev) pm_runtime_get_sync(&thdev->dev); + if (th->activate) + ret = th->activate(th); + if (ret) + goto fail_put; + if (thdrv->activate) ret = thdrv->activate(thdev); else intel_th_trace_enable(thdev); - if (ret) { - pm_runtime_put(&thdev->dev); - module_put(thdrv->driver.owner); - } + if (ret) + goto fail_deactivate; + + return 0; + +fail_deactivate: + if (th->deactivate) + th->deactivate(th); + +fail_put: + pm_runtime_put(&thdev->dev); + module_put(thdrv->driver.owner); return ret; } @@ -253,6 +267,7 @@ static void intel_th_output_deactivate(struct intel_th_device *thdev) { struct intel_th_driver *thdrv = to_intel_th_driver_or_null(thdev->dev.driver); + struct intel_th *th = to_intel_th(thdev); if (!thdrv) return; @@ -262,6 +277,9 @@ static void intel_th_output_deactivate(struct intel_th_device *thdev) else intel_th_trace_disable(thdev); + if (th->deactivate) + th->deactivate(th); + pm_runtime_put(&thdev->dev); module_put(thdrv->driver.owner); } diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h index 6824460..78a4fb2 100644 --- a/drivers/hwtracing/intel_th/intel_th.h +++ b/drivers/hwtracing/intel_th/intel_th.h @@ -263,6 +263,8 @@ struct intel_th { struct intel_th_drvdata *drvdata; struct resource *resource; + int (*activate)(struct intel_th *); + void (*deactivate)(struct intel_th *); unsigned int num_thdevs; unsigned int num_resources; int irq; -- cgit v1.1 From a0e7df335afd2a8a8a688251ffee375b58b6517c Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 24 Feb 2017 16:09:40 +0200 Subject: intel_th: Perform time resync on capture start On some devices (TH 2.x devices at the moment), the internal time counter is initially not synchronized to the global crystal clock, so the time stamps it produces will not be useful. In this case, the driver needs to force the time counter resync. This applies the workaround to relevant devices. Signed-off-by: Alexander Shishkin --- drivers/hwtracing/intel_th/core.c | 3 +- drivers/hwtracing/intel_th/gth.c | 23 ++++++++++---- drivers/hwtracing/intel_th/gth.h | 5 ++++ drivers/hwtracing/intel_th/intel_th.h | 4 +++ drivers/hwtracing/intel_th/pci.c | 56 +++++++++++++++++++++++++++++++++-- 5 files changed, 82 insertions(+), 9 deletions(-) diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index 998e3e5..1a023e3 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -436,8 +436,9 @@ static const struct intel_th_subdevice { .nres = 1, .res = { { + /* Handle TSCU from GTH driver */ .start = REG_GTH_OFFSET, - .end = REG_GTH_OFFSET + REG_GTH_LENGTH - 1, + .end = REG_TSCU_OFFSET + REG_TSCU_LENGTH - 1, .flags = IORESOURCE_MEM, }, }, diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index 7d9d667..018678e 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -285,16 +285,16 @@ gth_output_parm_get(struct gth_device *gth, int port, unsigned int parm) */ static int intel_th_gth_reset(struct gth_device *gth) { - u32 scratchpad; + u32 reg; int port, i; - scratchpad = ioread32(gth->base + REG_GTH_SCRPD0); - if (scratchpad & SCRPD_DEBUGGER_IN_USE) + reg = ioread32(gth->base + REG_GTH_SCRPD0); + if (reg & SCRPD_DEBUGGER_IN_USE) return -EBUSY; /* Always save/restore STH and TU registers in S0ix entry/exit */ - scratchpad |= SCRPD_STH_IS_ENABLED | SCRPD_TRIGGER_IS_ENABLED; - iowrite32(scratchpad, gth->base + REG_GTH_SCRPD0); + reg |= SCRPD_STH_IS_ENABLED | SCRPD_TRIGGER_IS_ENABLED; + iowrite32(reg, gth->base + REG_GTH_SCRPD0); /* output ports */ for (port = 0; port < 8; port++) { @@ -512,6 +512,15 @@ static void intel_th_gth_disable(struct intel_th_device *thdev, iowrite32(reg, gth->base + REG_GTH_SCRPD0); } +static void gth_tscu_resync(struct gth_device *gth) +{ + u32 reg; + + reg = ioread32(gth->base + REG_TSCU_TSUCTRL); + reg &= ~TSUCTRL_CTCRESYNC; + iowrite32(reg, gth->base + REG_TSCU_TSUCTRL); +} + /** * intel_th_gth_enable() - enable tracing to an output device * @thdev: GTH device @@ -524,6 +533,7 @@ static void intel_th_gth_enable(struct intel_th_device *thdev, struct intel_th_output *output) { struct gth_device *gth = dev_get_drvdata(&thdev->dev); + struct intel_th *th = to_intel_th(thdev); u32 scr = 0xfc0000, scrpd; int master; @@ -539,6 +549,9 @@ static void intel_th_gth_enable(struct intel_th_device *thdev, output->active = true; spin_unlock(>h->gth_lock); + if (INTEL_TH_CAP(th, tscu_enable)) + gth_tscu_resync(gth); + scrpd = ioread32(gth->base + REG_GTH_SCRPD0); scrpd |= output->scratchpad; iowrite32(scrpd, gth->base + REG_GTH_SCRPD0); diff --git a/drivers/hwtracing/intel_th/gth.h b/drivers/hwtracing/intel_th/gth.h index 56f0d26..f3d2342 100644 --- a/drivers/hwtracing/intel_th/gth.h +++ b/drivers/hwtracing/intel_th/gth.h @@ -55,9 +55,14 @@ enum { REG_GTH_SCRPD1 = 0xe4, /* ScratchPad[1] */ REG_GTH_SCRPD2 = 0xe8, /* ScratchPad[2] */ REG_GTH_SCRPD3 = 0xec, /* ScratchPad[3] */ + REG_TSCU_TSUCTRL = 0x2000, /* TSCU control register */ + REG_TSCU_TSCUSTAT = 0x2004, /* TSCU status register */ }; /* waiting for Pipeline Empty bit(s) to assert for GTH */ #define GTH_PLE_WAITLOOP_DEPTH 10000 +#define TSUCTRL_CTCRESYNC BIT(0) +#define TSCUSTAT_CTCSYNCING BIT(1) + #endif /* __INTEL_TH_GTH_H__ */ diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h index 78a4fb2..99ad563 100644 --- a/drivers/hwtracing/intel_th/intel_th.h +++ b/drivers/hwtracing/intel_th/intel_th.h @@ -298,6 +298,10 @@ enum { REG_GTH_OFFSET = 0x0000, REG_GTH_LENGTH = 0x2000, + /* Timestamp counter unit (TSCU) */ + REG_TSCU_OFFSET = 0x2000, + REG_TSCU_LENGTH = 0x1000, + /* Software Trace Hub (STH) [0x4000..0x4fff] */ REG_STH_OFFSET = 0x4000, REG_STH_LENGTH = 0x2000, diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c index aed6d59..bc9cebc 100644 --- a/drivers/hwtracing/intel_th/pci.c +++ b/drivers/hwtracing/intel_th/pci.c @@ -27,6 +27,49 @@ #define BAR_MASK (BIT(TH_MMIO_CONFIG) | BIT(TH_MMIO_SW)) +#define PCI_REG_NPKDSC 0x80 +#define NPKDSC_TSACT BIT(5) + +static int intel_th_pci_activate(struct intel_th *th) +{ + struct pci_dev *pdev = to_pci_dev(th->dev); + u32 npkdsc; + int err; + + if (!INTEL_TH_CAP(th, tscu_enable)) + return 0; + + err = pci_read_config_dword(pdev, PCI_REG_NPKDSC, &npkdsc); + if (!err) { + npkdsc |= NPKDSC_TSACT; + err = pci_write_config_dword(pdev, PCI_REG_NPKDSC, npkdsc); + } + + if (err) + dev_err(&pdev->dev, "failed to read NPKDSC register\n"); + + return err; +} + +static void intel_th_pci_deactivate(struct intel_th *th) +{ + struct pci_dev *pdev = to_pci_dev(th->dev); + u32 npkdsc; + int err; + + if (!INTEL_TH_CAP(th, tscu_enable)) + return; + + err = pci_read_config_dword(pdev, PCI_REG_NPKDSC, &npkdsc); + if (!err) { + npkdsc |= NPKDSC_TSACT; + err = pci_write_config_dword(pdev, PCI_REG_NPKDSC, npkdsc); + } + + if (err) + dev_err(&pdev->dev, "failed to read NPKDSC register\n"); +} + static int intel_th_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -47,6 +90,9 @@ static int intel_th_pci_probe(struct pci_dev *pdev, if (IS_ERR(th)) return PTR_ERR(th); + th->activate = intel_th_pci_activate; + th->deactivate = intel_th_pci_deactivate; + pci_set_master(pdev); return 0; @@ -59,6 +105,10 @@ static void intel_th_pci_remove(struct pci_dev *pdev) intel_th_free(th); } +static const struct intel_th_drvdata intel_th_2x = { + .tscu_enable = 1, +}; + static const struct pci_device_id intel_th_pci_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9d26), @@ -96,17 +146,17 @@ static const struct pci_device_id intel_th_pci_id_table[] = { { /* Gemini Lake */ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x318e), - .driver_data = (kernel_ulong_t)0, + .driver_data = (kernel_ulong_t)&intel_th_2x, }, { /* Cannon Lake H */ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa326), - .driver_data = (kernel_ulong_t)0, + .driver_data = (kernel_ulong_t)&intel_th_2x, }, { /* Cannon Lake LP */ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9da6), - .driver_data = (kernel_ulong_t)0, + .driver_data = (kernel_ulong_t)&intel_th_2x, }, { 0 }, }; -- cgit v1.1