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.
 -----BEGIN PGP SIGNATURE-----
 
 iJkEABEIAEEWIQQSviFCoXpKPDNATbnrxfYkYwVX/wUCWaBJXyMcYWxleGFuZGVy
 LnNoaXNoa2luQGxpbnV4LmludGVsLmNvbQAKCRDrxfYkYwVX/+4vAP9vSdGYkIzJ
 i09a14Oqj1Qv2PWFMTSjYolLHZCgZJ6LqQD/S0CY8eVmHeoYv2U6jHpZFIX7yQsP
 ahlAiqgqneV6GE0=
 =zU1K
 -----END PGP SIGNATURE-----

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.
This commit is contained in:
Greg Kroah-Hartman 2017-08-28 16:58:19 +02:00
commit 981b467736
10 changed files with 579 additions and 158 deletions

View File

@ -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

View File

@ -101,17 +101,53 @@ static int intel_th_probe(struct device *dev)
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,
},
},
@ -479,6 +519,21 @@ static const struct intel_th_subdevice {
.otype = GTH_PTI,
.scrpd = SCRPD_PTI_IS_PRIM_DEST,
},
{
.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 = {
@ -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;
thdev = intel_th_device_alloc(th, subdev->type, subdev->name,
subdev->id);
if (!thdev)
return ERR_PTR(-ENOMEM);
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;
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;
}
}
err = intel_th_device_add_resources(thdev, res, subdev->nres);
if (err) {
put_device(&thdev->dev);
goto fail_put_device;
}
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;
}
err = device_add(&thdev->dev);
if (err) {
put_device(&thdev->dev);
goto fail_free_res;
}
/* 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++;
}
return thdev;
fail_free_res:
kfree(thdev->resource);
fail_put_device:
put_device(&thdev->dev);
return ERR_PTR(err);
}
/**
* 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;
}
/* 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;
}
/*
* 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, dst = 0; src < ARRAY_SIZE(intel_th_subdevices); src++) {
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;
int r;
/* 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;
}
/*
* 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;
memcpy(res, subdev->res,
sizeof(struct resource) * subdev->nres);
thdev = intel_th_subdevice_alloc(th, subdev);
/* note: caller should free subdevices from th::thdev[] */
if (IS_ERR(thdev))
return PTR_ERR(thdev);
for (r = 0; r < subdev->nres; r++) {
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;
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 = intel_th_device_add_resources(thdev, res, subdev->nres);
if (err) {
put_device(&thdev->dev);
goto kill_subdevs;
}
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;
}
err = device_add(&thdev->dev);
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) {
th->hub = thdev;
err = intel_th_request_hub_module(th);
if (!err)
req++;
}
th->thdev[dst++] = thdev;
th->thdev[th->num_thdevs++] = thdev;
}
return 0;
kill_subdevs:
for (; dst >= 0; dst--)
intel_th_device_remove(th->thdev[dst]);
return err;
}
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);

View File

@ -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(&gth->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(&gth->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;
}

View File

@ -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__ */

View File

@ -47,9 +47,20 @@ struct intel_th_output {
bool active;
};
/**
* 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,
@ -249,16 +323,6 @@ enum {
REG_DCIH_LENGTH = REG_MSU_LENGTH,
};
/*
* 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.

View File

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

View File

@ -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 },
};

View File

@ -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>");

View File

@ -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__ */

View File

@ -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;
/*