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:
commit
981b467736
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 },
|
||||
};
|
||||
|
|
|
@ -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>");
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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;
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue
Block a user