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
10 changed files with 579 additions and 158 deletions

View File

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