diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-08-28 16:58:19 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-08-28 16:58:19 +0200 |
commit | 981b4677364dee053763a4c542ed9eb6d94c246c (patch) | |
tree | 444ec28b6ad6df436d16e85c535f9152cbced85a /drivers/hwtracing | |
parent | vmci: fix duplicated code for different branches (diff) | |
parent | intel_th: Perform time resync on capture start (diff) | |
download | linux-981b4677364dee053763a4c542ed9eb6d94c246c.tar.xz linux-981b4677364dee053763a4c542ed9eb6d94c246c.zip |
Merge tag 'stm-for-greg-20170825' of git://git.kernel.org/pub/scm/linux/kernel/git/ash/stm into char-misc-next
Alexander writes:
stm class / intel_th: Updates for 4.14
Intel TH:
* Updated subdevice management code to better fit host mode
* Added support for Low Power Path (LPP) output type
* Fixed memory allocation with IOMMU enabled (DMAR tables)
* Added Cannon Lake PCH PCI IDs
* Added a quirk to force time sync on devices that need it
STM:
* Fixed potential read overflow in ioctl()
* Documented stm_ftrace source.
Diffstat (limited to 'drivers/hwtracing')
-rw-r--r-- | drivers/hwtracing/intel_th/core.c | 359 | ||||
-rw-r--r-- | drivers/hwtracing/intel_th/gth.c | 40 | ||||
-rw-r--r-- | drivers/hwtracing/intel_th/gth.h | 5 | ||||
-rw-r--r-- | drivers/hwtracing/intel_th/intel_th.h | 104 | ||||
-rw-r--r-- | drivers/hwtracing/intel_th/msu.c | 12 | ||||
-rw-r--r-- | drivers/hwtracing/intel_th/pci.c | 67 | ||||
-rw-r--r-- | drivers/hwtracing/intel_th/pti.c | 115 | ||||
-rw-r--r-- | drivers/hwtracing/intel_th/pti.h | 8 | ||||
-rw-r--r-- | drivers/hwtracing/stm/core.c | 2 |
9 files changed, 561 insertions, 151 deletions
diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index 8da567abc0ce..1a023e30488c 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) @@ -156,21 +192,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) { @@ -205,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) @@ -215,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; } @@ -232,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; @@ -241,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); } @@ -326,10 +365,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) @@ -392,13 +431,14 @@ static const struct intel_th_subdevice { unsigned otype; unsigned scrpd; int id; -} intel_th_subdevices[TH_SUBDEVICE_MAX] = { +} intel_th_subdevices[] = { { .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, }, }, @@ -483,6 +523,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, @@ -526,98 +581,182 @@ 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->drvdata = th->drvdata; + + 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; + } + + if (res[r].flags & IORESOURCE_MEM) { + res[r].start += devres[bar].start; + res[r].end += devres[bar].start; - thdev = intel_th_device_alloc(th, subdev->type, subdev->name, - subdev->id); - if (!thdev) { - err = -ENOMEM; - goto kill_subdevs; + 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; } + } - memcpy(res, subdev->res, - sizeof(struct resource) * subdev->nres); + err = intel_th_device_add_resources(thdev, res, subdev->nres); + if (err) { + put_device(&thdev->dev); + goto fail_put_device; + } - for (r = 0; r < subdev->nres; r++) { - int bar = TH_MMIO_CONFIG; + 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; + } - /* - * 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 = device_add(&thdev->dev); + if (err) { + put_device(&thdev->dev); + goto fail_free_res; + } - if (res[r].flags & IORESOURCE_MEM) { - res[r].start += devres[bar].start; - res[r].end += devres[bar].start; + /* 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++; + } - dev_dbg(th->dev, "%s:%d @ %pR\n", - subdev->name, r, &res[r]); - } else if (res[r].flags & IORESOURCE_IRQ) { - res[r].start = irq; - } - } + return thdev; - err = intel_th_device_add_resources(thdev, res, subdev->nres); - if (err) { - put_device(&thdev->dev); - goto kill_subdevs; - } +fail_free_res: + kfree(thdev->resource); - 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; - } +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); + +static int intel_th_populate(struct intel_th *th) +{ + int src; + + /* 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; -kill_subdevs: - for (; dst >= 0; dst--) - intel_th_device_remove(th->thdev[dst]); + /* + * 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 err; + return 0; } static int match_devt(struct device *dev, void *data) @@ -670,8 +809,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; @@ -693,6 +832,11 @@ 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; + th->irq = irq; dev_set_drvdata(dev, th); @@ -700,18 +844,15 @@ intel_th_alloc(struct device *dev, struct resource *devres, 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); @@ -727,11 +868,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 dd32d0bad687..018678ec3c13 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); @@ -639,6 +652,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 +674,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 +683,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 +692,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 +703,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 +721,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/gth.h b/drivers/hwtracing/intel_th/gth.h index 56f0d2620577..f3d234251a12 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 3096e7054f6d..99ad563fc40d 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; @@ -96,6 +108,17 @@ 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_LPP, /* Low Power Path */ + GTH_PTI, /* MIPI-PTI */ +}; + /** * intel_th_output_assigned() - if an output device is assigned to a switch port * @thdev: the output device @@ -106,7 +129,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); } /** @@ -161,8 +185,18 @@ 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_hub(struct intel_th_device *thdev) +to_intel_th_parent(struct intel_th_device *thdev) { struct device *parent = thdev->dev.parent; @@ -172,9 +206,20 @@ to_intel_th_hub(struct intel_th_device *thdev) return to_intel_th_device(parent); } +static inline struct intel_th *to_intel_th(struct intel_th_device *thdev) +{ + if (thdev->type == INTEL_TH_OUTPUT) + thdev = to_intel_th_parent(thdev); + + if (WARN_ON_ONCE(!thdev || thdev->type == INTEL_TH_OUTPUT)) + 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); +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); @@ -184,6 +229,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, @@ -191,8 +237,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 @@ -201,6 +248,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 */ @@ -209,6 +260,14 @@ struct intel_th { struct intel_th_device *thdev[TH_SUBDEVICE_MAX]; struct intel_th_device *hub; + 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; int id; int major; @@ -220,6 +279,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 */ @@ -228,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, @@ -250,16 +324,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. */ diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index dbbe31df74df..dfb57eaa9f22 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; diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c index 590cf90dd21a..bc9cebc30526 100644 --- a/drivers/hwtracing/intel_th/pci.c +++ b/drivers/hwtracing/intel_th/pci.c @@ -27,9 +27,53 @@ #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) { + struct intel_th_drvdata *drvdata = (void *)id->driver_data; struct intel_th *th; int err; @@ -41,11 +85,16 @@ 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); + th->activate = intel_th_pci_activate; + th->deactivate = intel_th_pci_deactivate; + + pci_set_master(pdev); + return 0; } @@ -56,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), @@ -93,7 +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)&intel_th_2x, + }, + { + /* Cannon Lake LP */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9da6), + .driver_data = (kernel_ulong_t)&intel_th_2x, }, { 0 }, }; diff --git a/drivers/hwtracing/intel_th/pti.c b/drivers/hwtracing/intel_th/pti.c index 35738b5bfccd..e96a1fcb57b2 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 <alexander.shishkin@linux.intel.com>"); diff --git a/drivers/hwtracing/intel_th/pti.h b/drivers/hwtracing/intel_th/pti.h index 20883f5628cf..30827be67b4c 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__ */ diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 0e731143f6a4..9414900575d8 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; /* |